From 9f3cb3d92efb19022470498205dddffb04dca552 Mon Sep 17 00:00:00 2001 From: Rachael Alexanderson Date: Wed, 30 Jul 2025 00:24:15 -0400 Subject: [PATCH] Squashed 'libraries/ZMusic/' content from commit ac3e232b00 git-subtree-dir: libraries/ZMusic git-subtree-split: ac3e232b001129c740b7b65196ae0e1b13b82513 --- .github/workflows/continuous_integration.yml | 97 + .gitignore | 2 + CMakeLists.txt | 212 + README.md | 16 + cmake/FindFluidSynth.cmake | 38 + cmake/FindGME.cmake | 37 + cmake/FindMPG123.cmake | 43 + cmake/FindSndFile.cmake | 42 + cmake/ZMusicConfig.cmake.in | 19 + cmake/ZUtility.cmake | 137 + include/zmusic.h | 448 + licenses/bsd.txt | 27 + licenses/dumb.txt | 87 + licenses/gplv3.txt | 674 + licenses/legal.txt | 25 + licenses/lgplv21.txt | 504 + licenses/lgplv3.txt | 841 + licenses/zmusic.txt | 6 + samples/list_midi_devices/.gitignore | 1 + samples/list_midi_devices/CMakeLists.txt | 7 + .../list_midi_devices/list_midi_devices.cpp | 27 + source/CMakeLists.txt | 224 + source/data/xg.h | 15527 ++++++++++++++++ source/data/xg.wopn | Bin 0 -> 186204 bytes source/decoder/mpg123_decoder.cpp | 238 + source/decoder/mpg123_decoder.h | 49 + source/decoder/mpgload.h | 40 + source/decoder/sndfile_decoder.cpp | 268 + source/decoder/sndfile_decoder.h | 50 + source/decoder/sndload.h | 27 + source/decoder/sounddecoder.cpp | 184 + source/loader/i_module.cpp | 113 + source/loader/i_module.h | 233 + source/loader/test.c | 2 + source/mididevices/driver_adlib.cpp | 783 + source/mididevices/mididevice.h | 160 + .../mididevices/music_adlmidi_mididevice.cpp | 661 + source/mididevices/music_alsa_mididevice.cpp | 497 + source/mididevices/music_alsa_state.cpp | 169 + source/mididevices/music_alsa_state.h | 80 + source/mididevices/music_base_mididevice.cpp | 182 + .../music_fluidsynth_mididevice.cpp | 525 + source/mididevices/music_opl_mididevice.cpp | 369 + .../mididevices/music_opnmidi_mididevice.cpp | 427 + .../music_softsynth_mididevice.cpp | 419 + .../mididevices/music_timidity_mididevice.cpp | 307 + .../music_timiditypp_mididevice.cpp | 243 + .../music_wavewriter_mididevice.cpp | 203 + .../mididevices/music_wildmidi_mididevice.cpp | 282 + source/mididevices/music_win_mididevice.cpp | 696 + source/midisources/midisource.cpp | 516 + source/midisources/midisource.h | 250 + source/midisources/midisource_hmi.cpp | 996 + source/midisources/midisource_mids.cpp | 192 + source/midisources/midisource_mus.cpp | 365 + source/midisources/midisource_smf.cpp | 801 + source/midisources/midisource_xmi.cpp | 674 + source/musicformats/music_cd.cpp | 211 + source/musicformats/music_midi.cpp | 1047 ++ source/musicformats/music_stream.cpp | 177 + source/musicformats/win32/helperthread.cpp | 292 + source/musicformats/win32/helperthread.h | 85 + source/musicformats/win32/i_cd.cpp | 762 + source/musicformats/win32/i_cd.h | 72 + source/streamsources/music_dumb.cpp | 1232 ++ source/streamsources/music_gme.cpp | 374 + source/streamsources/music_libsndfile.cpp | 568 + source/streamsources/music_libxmp.cpp | 180 + source/streamsources/music_opl.cpp | 159 + source/streamsources/music_xa.cpp | 354 + source/streamsources/streamsource.h | 40 + source/zmusic/configuration.cpp | 1196 ++ source/zmusic/critsec.cpp | 163 + source/zmusic/critsec.h | 37 + source/zmusic/fileio.h | 389 + source/zmusic/m_swap.h | 255 + source/zmusic/midiconfig.h | 169 + source/zmusic/mididefs.h | 27 + source/zmusic/mus2midi.h | 78 + source/zmusic/musinfo.h | 44 + source/zmusic/sounddecoder.h | 28 + source/zmusic/zmusic.cpp | 549 + source/zmusic/zmusic_internal.h | 82 + thirdparty/CMakeLists.txt | 31 + thirdparty/adlmidi/CMakeLists.txt | 58 + thirdparty/adlmidi/adlmidi.cpp | 1926 ++ thirdparty/adlmidi/adlmidi.h | 1498 ++ thirdparty/adlmidi/adlmidi_bankmap.h | 127 + thirdparty/adlmidi/adlmidi_bankmap.tcc | 283 + thirdparty/adlmidi/adlmidi_cvt.hpp | 115 + thirdparty/adlmidi/adlmidi_db.h | 98 + thirdparty/adlmidi/adlmidi_load.cpp | 306 + thirdparty/adlmidi/adlmidi_midiplay.cpp | 2082 +++ thirdparty/adlmidi/adlmidi_midiplay.hpp | 1094 ++ thirdparty/adlmidi/adlmidi_opl3.cpp | 2020 ++ thirdparty/adlmidi/adlmidi_opl3.hpp | 430 + thirdparty/adlmidi/adlmidi_private.cpp | 127 + thirdparty/adlmidi/adlmidi_private.hpp | 237 + thirdparty/adlmidi/adlmidi_ptr.hpp | 217 + thirdparty/adlmidi/chips/common/mutex.hpp | 170 + thirdparty/adlmidi/chips/common/ptr.hpp | 110 + thirdparty/adlmidi/chips/dos_hw_opl.cpp | 163 + thirdparty/adlmidi/chips/dos_hw_opl.h | 48 + thirdparty/adlmidi/chips/dosbox/dbopl.cpp | 1746 ++ thirdparty/adlmidi/chips/dosbox/dbopl.h | 292 + thirdparty/adlmidi/chips/dosbox_opl3.cpp | 95 + thirdparty/adlmidi/chips/dosbox_opl3.h | 48 + thirdparty/adlmidi/chips/esfmu/LICENSE | 504 + thirdparty/adlmidi/chips/esfmu/README.md | 75 + .../adlmidi/chips/esfmu/assets/logo.png | Bin 0 -> 94294 bytes thirdparty/adlmidi/chips/esfmu/esfm.c | 2329 +++ thirdparty/adlmidi/chips/esfmu/esfm.h | 293 + .../adlmidi/chips/esfmu/esfm_registers.c | 1010 + thirdparty/adlmidi/chips/esfmu_opl3.cpp | 88 + thirdparty/adlmidi/chips/esfmu_opl3.h | 46 + thirdparty/adlmidi/chips/java/JavaOPL3.hpp | 1900 ++ thirdparty/adlmidi/chips/java_opl3.cpp | 122 + thirdparty/adlmidi/chips/java_opl3.h | 46 + thirdparty/adlmidi/chips/mame/mame_fmopl.cpp | 1852 ++ thirdparty/adlmidi/chips/mame/opl.h | 27 + thirdparty/adlmidi/chips/mame_opl2.cpp | 79 + thirdparty/adlmidi/chips/mame_opl2.h | 46 + thirdparty/adlmidi/chips/nuked/LICENSE | 504 + thirdparty/adlmidi/chips/nuked/nukedopl3.c | 1781 ++ thirdparty/adlmidi/chips/nuked/nukedopl3.h | 184 + .../adlmidi/chips/nuked/nukedopl3_174.c | 1432 ++ .../adlmidi/chips/nuked/nukedopl3_174.h | 156 + thirdparty/adlmidi/chips/nuked_opl3.cpp | 85 + thirdparty/adlmidi/chips/nuked_opl3.h | 46 + thirdparty/adlmidi/chips/nuked_opl3_v174.cpp | 85 + thirdparty/adlmidi/chips/nuked_opl3_v174.h | 46 + thirdparty/adlmidi/chips/opal/LICENSE.txt | 19 + thirdparty/adlmidi/chips/opal/old/LICENSE.txt | 19 + thirdparty/adlmidi/chips/opal/old/README.old | 9 + .../adlmidi/chips/opal/old/opal-orig.hpp | 1398 ++ .../adlmidi/chips/opal/old/opal-pan.diff | 131 + thirdparty/adlmidi/chips/opal/opal.c | 1448 ++ thirdparty/adlmidi/chips/opal/opal.h | 165 + thirdparty/adlmidi/chips/opal_opl3.cpp | 85 + thirdparty/adlmidi/chips/opal_opl3.h | 46 + thirdparty/adlmidi/chips/opl_chip_base.h | 163 + thirdparty/adlmidi/chips/opl_chip_base.tcc | 294 + thirdparty/adlmidi/chips/opl_serial_misc.h | 350 + thirdparty/adlmidi/chips/opl_serial_port.cpp | 171 + thirdparty/adlmidi/chips/opl_serial_port.h | 66 + thirdparty/adlmidi/chips/ym3812_lle.cpp | 82 + thirdparty/adlmidi/chips/ym3812_lle.h | 46 + thirdparty/adlmidi/chips/ym3812_lle/nopl2.c | 242 + thirdparty/adlmidi/chips/ym3812_lle/nopl2.h | 45 + .../adlmidi/chips/ym3812_lle/nuked_fmopl2.c | 1556 ++ .../adlmidi/chips/ym3812_lle/nuked_fmopl2.h | 287 + thirdparty/adlmidi/chips/ymf262_lle.cpp | 82 + thirdparty/adlmidi/chips/ymf262_lle.h | 46 + thirdparty/adlmidi/chips/ymf262_lle/LICENSE | 339 + thirdparty/adlmidi/chips/ymf262_lle/Readme.md | 7 + thirdparty/adlmidi/chips/ymf262_lle/nopl3.c | 261 + thirdparty/adlmidi/chips/ymf262_lle/nopl3.h | 45 + .../adlmidi/chips/ymf262_lle/nuked_fmopl3.c | 1689 ++ .../adlmidi/chips/ymf262_lle/nuked_fmopl3.h | 347 + thirdparty/adlmidi/chips/ymfm/README.md | 125 + thirdparty/adlmidi/chips/ymfm/ymfm.h | 572 + thirdparty/adlmidi/chips/ymfm/ymfm_adpcm.cpp | 807 + thirdparty/adlmidi/chips/ymfm/ymfm_adpcm.h | 411 + thirdparty/adlmidi/chips/ymfm/ymfm_fm.h | 463 + thirdparty/adlmidi/chips/ymfm/ymfm_fm.ipp | 1597 ++ thirdparty/adlmidi/chips/ymfm/ymfm_misc.cpp | 175 + thirdparty/adlmidi/chips/ymfm/ymfm_misc.h | 93 + thirdparty/adlmidi/chips/ymfm/ymfm_opl.cpp | 2223 +++ thirdparty/adlmidi/chips/ymfm/ymfm_opl.h | 902 + thirdparty/adlmidi/chips/ymfm/ymfm_pcm.cpp | 714 + thirdparty/adlmidi/chips/ymfm/ymfm_pcm.h | 347 + thirdparty/adlmidi/chips/ymfm/ymfm_ssg.cpp | 279 + thirdparty/adlmidi/chips/ymfm/ymfm_ssg.h | 205 + thirdparty/adlmidi/chips/ymfm_opl2.cpp | 119 + thirdparty/adlmidi/chips/ymfm_opl2.h | 60 + thirdparty/adlmidi/chips/ymfm_opl3.cpp | 119 + thirdparty/adlmidi/chips/ymfm_opl3.h | 60 + thirdparty/adlmidi/file_reader.hpp | 301 + thirdparty/adlmidi/fraction.hpp | 220 + thirdparty/adlmidi/inst_db.cpp | 39 + thirdparty/adlmidi/oplinst.h | 122 + thirdparty/adlmidi/structures/pl_list.hpp | 133 + thirdparty/adlmidi/structures/pl_list.tcc | 338 + thirdparty/adlmidi/wopl/wopl_file.c | 601 + thirdparty/adlmidi/wopl/wopl_file.h | 314 + thirdparty/dumb/CMakeLists.txt | 124 + thirdparty/dumb/cmake/CMakeLists.txt | 118 + thirdparty/dumb/cmake/readme.txt | 30 + thirdparty/dumb/include/dumb.h | 810 + thirdparty/dumb/include/internal/aldumb.h | 27 + thirdparty/dumb/include/internal/barray.h | 41 + thirdparty/dumb/include/internal/dumb.h | 61 + thirdparty/dumb/include/internal/dumbfile.h | 13 + thirdparty/dumb/include/internal/it.h | 914 + thirdparty/dumb/include/internal/lpc.h | 30 + thirdparty/dumb/include/internal/mulsc.h | 36 + thirdparty/dumb/include/internal/resampler.h | 58 + thirdparty/dumb/include/internal/riff.h | 24 + .../dumb/include/internal/stack_alloc.h | 113 + thirdparty/dumb/licence.txt | 87 + thirdparty/dumb/prj/.gitignore | 3 + thirdparty/dumb/prj/dumb/dumb.pro | 128 + thirdparty/dumb/readme.txt | 541 + thirdparty/dumb/release.txt | 561 + thirdparty/dumb/src/core/atexit.c | 71 + thirdparty/dumb/src/core/duhlen.c | 42 + thirdparty/dumb/src/core/duhtag.c | 38 + thirdparty/dumb/src/core/dumbfile.c | 418 + thirdparty/dumb/src/core/loadduh.c | 42 + thirdparty/dumb/src/core/makeduh.c | 151 + thirdparty/dumb/src/core/rawsig.c | 58 + thirdparty/dumb/src/core/readduh.c | 107 + thirdparty/dumb/src/core/register.c | 104 + thirdparty/dumb/src/core/rendduh.c | 184 + thirdparty/dumb/src/core/rendsig.c | 348 + thirdparty/dumb/src/core/unload.c | 64 + thirdparty/dumb/src/helpers/barray.c | 189 + thirdparty/dumb/src/helpers/clickrem.c | 306 + thirdparty/dumb/src/helpers/lpc.c | 324 + thirdparty/dumb/src/helpers/memfile.c | 117 + thirdparty/dumb/src/helpers/resamp2.inc | 174 + thirdparty/dumb/src/helpers/resamp3.inc | 436 + thirdparty/dumb/src/helpers/resample.c | 420 + thirdparty/dumb/src/helpers/resample.inc | 299 + thirdparty/dumb/src/helpers/resampler.c | 1512 ++ thirdparty/dumb/src/helpers/riff.c | 87 + thirdparty/dumb/src/helpers/sampbuf.c | 64 + thirdparty/dumb/src/helpers/silence.c | 29 + thirdparty/dumb/src/helpers/stdfile.c | 146 + thirdparty/dumb/src/it/itload.c | 43 + thirdparty/dumb/src/it/itload2.c | 29 + thirdparty/dumb/src/it/itmisc.c | 249 + thirdparty/dumb/src/it/itorder.c | 63 + thirdparty/dumb/src/it/itread.c | 1414 ++ thirdparty/dumb/src/it/itread2.c | 29 + thirdparty/dumb/src/it/itrender.c | 5961 ++++++ thirdparty/dumb/src/it/itunload.c | 72 + thirdparty/dumb/src/it/load669.c | 42 + thirdparty/dumb/src/it/load6692.c | 34 + thirdparty/dumb/src/it/loadamf.c | 42 + thirdparty/dumb/src/it/loadamf2.c | 34 + thirdparty/dumb/src/it/loadany.c | 38 + thirdparty/dumb/src/it/loadany2.c | 29 + thirdparty/dumb/src/it/loadasy.c | 42 + thirdparty/dumb/src/it/loadasy2.c | 34 + thirdparty/dumb/src/it/loadmod.c | 42 + thirdparty/dumb/src/it/loadmod2.c | 29 + thirdparty/dumb/src/it/loadmtm.c | 42 + thirdparty/dumb/src/it/loadmtm2.c | 34 + thirdparty/dumb/src/it/loadokt.c | 42 + thirdparty/dumb/src/it/loadokt2.c | 29 + thirdparty/dumb/src/it/loadoldpsm.c | 43 + thirdparty/dumb/src/it/loadoldpsm2.c | 34 + thirdparty/dumb/src/it/loadpsm.c | 42 + thirdparty/dumb/src/it/loadpsm2.c | 34 + thirdparty/dumb/src/it/loadptm.c | 42 + thirdparty/dumb/src/it/loadptm2.c | 34 + thirdparty/dumb/src/it/loadriff.c | 42 + thirdparty/dumb/src/it/loadriff2.c | 29 + thirdparty/dumb/src/it/loads3m.c | 42 + thirdparty/dumb/src/it/loads3m2.c | 29 + thirdparty/dumb/src/it/loadstm.c | 42 + thirdparty/dumb/src/it/loadstm2.c | 29 + thirdparty/dumb/src/it/loadxm.c | 42 + thirdparty/dumb/src/it/loadxm2.c | 29 + thirdparty/dumb/src/it/ptmeffect.c | 125 + thirdparty/dumb/src/it/read669.c | 448 + thirdparty/dumb/src/it/read6692.c | 29 + thirdparty/dumb/src/it/readam.c | 788 + thirdparty/dumb/src/it/readamf.c | 559 + thirdparty/dumb/src/it/readamf2.c | 29 + thirdparty/dumb/src/it/readany.c | 132 + thirdparty/dumb/src/it/readany2.c | 29 + thirdparty/dumb/src/it/readasy.c | 339 + thirdparty/dumb/src/it/readdsmf.c | 383 + thirdparty/dumb/src/it/readmod.c | 633 + thirdparty/dumb/src/it/readmod2.c | 29 + thirdparty/dumb/src/it/readmtm.c | 413 + thirdparty/dumb/src/it/readokt.c | 558 + thirdparty/dumb/src/it/readokt2.c | 29 + thirdparty/dumb/src/it/readoldpsm.c | 689 + thirdparty/dumb/src/it/readpsm.c | 1292 ++ thirdparty/dumb/src/it/readptm.c | 554 + thirdparty/dumb/src/it/readriff.c | 57 + thirdparty/dumb/src/it/reads3m.c | 766 + thirdparty/dumb/src/it/reads3m2.c | 29 + thirdparty/dumb/src/it/readstm.c | 397 + thirdparty/dumb/src/it/readstm2.c | 29 + thirdparty/dumb/src/it/readxm.c | 1530 ++ thirdparty/dumb/src/it/readxm2.c | 29 + thirdparty/dumb/src/it/xmeffect.c | 245 + thirdparty/dumb/vc6/dumb/.gitignore | 3 + thirdparty/dumb/vc6/dumb/dumb.vcxproj | 216 + thirdparty/dumb/vc6/dumb/dumb.vcxproj.filters | 326 + thirdparty/fluidsynth/include/fluidsynth.h | 119 + .../fluidsynth/include/fluidsynth/audio.h | 155 + .../fluidsynth/include/fluidsynth/event.h | 143 + .../fluidsynth/include/fluidsynth/gen.h | 134 + .../fluidsynth/include/fluidsynth/ladspa.h | 69 + .../fluidsynth/include/fluidsynth/log.h | 97 + .../fluidsynth/include/fluidsynth/midi.h | 294 + .../fluidsynth/include/fluidsynth/misc.h | 77 + .../fluidsynth/include/fluidsynth/mod.h | 105 + .../fluidsynth/include/fluidsynth/seq.h | 92 + .../fluidsynth/include/fluidsynth/seqbind.h | 45 + .../fluidsynth/include/fluidsynth/settings.h | 194 + .../fluidsynth/include/fluidsynth/sfont.h | 362 + .../fluidsynth/include/fluidsynth/shell.h | 150 + .../fluidsynth/include/fluidsynth/synth.h | 562 + .../fluidsynth/include/fluidsynth/types.h | 85 + .../fluidsynth/include/fluidsynth/version.h | 47 + .../fluidsynth/include/fluidsynth/voice.h | 77 + thirdparty/fluidsynth/src/CMakeLists.txt | 162 + .../src/bindings/fluid_filerenderer.c | 590 + .../fluidsynth/src/bindings/fluid_ladspa.c | 1882 ++ .../fluidsynth/src/bindings/fluid_ladspa.h | 36 + thirdparty/fluidsynth/src/config.h | 280 + .../fluidsynth/src/drivers/fluid_adriver.c | 495 + .../fluidsynth/src/drivers/fluid_adriver.h | 186 + .../fluidsynth/src/drivers/fluid_mdriver.c | 196 + .../fluidsynth/src/drivers/fluid_mdriver.h | 100 + .../fluidsynth/src/drivers/fluid_sndmgr.c | 368 + .../fluidsynth/src/fluid_conv_tables.inc.h | 3916 ++++ .../src/fluid_rvoice_dsp_tables.inc.h | 4110 ++++ .../fluidsynth/src/gentables/CMakeLists.txt | 36 + .../fluidsynth/src/gentables/gen_conv.c | 84 + .../fluidsynth/src/gentables/gen_rvoice_dsp.c | 81 + .../fluidsynth/src/gentables/make_tables.c | 84 + .../fluidsynth/src/gentables/make_tables.h | 26 + thirdparty/fluidsynth/src/midi/fluid_midi.c | 2788 +++ thirdparty/fluidsynth/src/midi/fluid_midi.h | 380 + .../fluidsynth/src/midi/fluid_midi_router.c | 974 + .../fluidsynth/src/midi/fluid_midi_router.h | 32 + thirdparty/fluidsynth/src/midi/fluid_seq.c | 632 + .../fluidsynth/src/midi/fluid_seq_queue.cpp | 250 + .../fluidsynth/src/midi/fluid_seq_queue.h | 46 + .../fluidsynth/src/midi/fluid_seqbind.c | 386 + .../src/midi/fluid_seqbind_notes.cpp | 115 + .../fluidsynth/src/midi/fluid_seqbind_notes.h | 42 + .../fluidsynth/src/rvoice/fluid_adsr_env.c | 39 + .../fluidsynth/src/rvoice/fluid_adsr_env.h | 167 + .../fluidsynth/src/rvoice/fluid_chorus.c | 1071 ++ .../fluidsynth/src/rvoice/fluid_chorus.h | 80 + .../fluidsynth/src/rvoice/fluid_iir_filter.c | 419 + .../fluidsynth/src/rvoice/fluid_iir_filter.h | 75 + thirdparty/fluidsynth/src/rvoice/fluid_lfo.c | 17 + thirdparty/fluidsynth/src/rvoice/fluid_lfo.h | 75 + .../fluidsynth/src/rvoice/fluid_phase.h | 113 + thirdparty/fluidsynth/src/rvoice/fluid_rev.c | 1523 ++ thirdparty/fluidsynth/src/rvoice/fluid_rev.h | 91 + .../fluidsynth/src/rvoice/fluid_rvoice.c | 937 + .../fluidsynth/src/rvoice/fluid_rvoice.h | 231 + .../fluidsynth/src/rvoice/fluid_rvoice_dsp.c | 636 + .../src/rvoice/fluid_rvoice_dsp_tables.h | 8 + .../src/rvoice/fluid_rvoice_event.c | 202 + .../src/rvoice/fluid_rvoice_event.h | 114 + .../src/rvoice/fluid_rvoice_mixer.c | 1713 ++ .../src/rvoice/fluid_rvoice_mixer.h | 88 + .../fluidsynth/src/sfloader/fluid_defsfont.c | 2338 +++ .../fluidsynth/src/sfloader/fluid_defsfont.h | 232 + .../fluidsynth/src/sfloader/fluid_instpatch.c | 697 + .../fluidsynth/src/sfloader/fluid_instpatch.h | 14 + .../src/sfloader/fluid_samplecache.c | 313 + .../src/sfloader/fluid_samplecache.h | 37 + .../fluidsynth/src/sfloader/fluid_sffile.c | 2535 +++ .../fluidsynth/src/sfloader/fluid_sffile.h | 194 + .../fluidsynth/src/sfloader/fluid_sfont.c | 850 + .../fluidsynth/src/sfloader/fluid_sfont.h | 189 + thirdparty/fluidsynth/src/sfloader/sndfile.h | 882 + thirdparty/fluidsynth/src/synth/fluid_chan.c | 732 + thirdparty/fluidsynth/src/synth/fluid_chan.h | 276 + thirdparty/fluidsynth/src/synth/fluid_event.c | 863 + thirdparty/fluidsynth/src/synth/fluid_event.h | 65 + thirdparty/fluidsynth/src/synth/fluid_gen.c | 133 + thirdparty/fluidsynth/src/synth/fluid_gen.h | 67 + thirdparty/fluidsynth/src/synth/fluid_mod.c | 889 + thirdparty/fluidsynth/src/synth/fluid_mod.h | 54 + thirdparty/fluidsynth/src/synth/fluid_synth.c | 8428 +++++++++ thirdparty/fluidsynth/src/synth/fluid_synth.h | 267 + .../src/synth/fluid_synth_monopoly.c | 722 + .../fluidsynth/src/synth/fluid_tuning.c | 190 + .../fluidsynth/src/synth/fluid_tuning.h | 69 + thirdparty/fluidsynth/src/synth/fluid_voice.c | 2052 ++ thirdparty/fluidsynth/src/synth/fluid_voice.h | 198 + thirdparty/fluidsynth/src/utils/fluid_conv.c | 330 + thirdparty/fluidsynth/src/utils/fluid_conv.h | 40 + .../fluidsynth/src/utils/fluid_conv_tables.h | 41 + thirdparty/fluidsynth/src/utils/fluid_hash.c | 1407 ++ thirdparty/fluidsynth/src/utils/fluid_hash.h | 131 + thirdparty/fluidsynth/src/utils/fluid_list.c | 337 + thirdparty/fluidsynth/src/utils/fluid_list.h | 63 + .../fluidsynth/src/utils/fluid_ringbuffer.c | 90 + .../fluidsynth/src/utils/fluid_ringbuffer.h | 133 + .../fluidsynth/src/utils/fluid_settings.c | 1999 ++ .../fluidsynth/src/utils/fluid_settings.h | 65 + thirdparty/fluidsynth/src/utils/fluid_sys.c | 1803 ++ thirdparty/fluidsynth/src/utils/fluid_sys.h | 775 + .../fluidsynth/src/utils/fluidsynth_priv.h | 335 + .../fluidsynth/src/utils/win32_glibstubs.c | 144 + .../fluidsynth/src/utils/win32_glibstubs.h | 141 + thirdparty/game-music-emu/CMakeLists.txt | 133 + thirdparty/game-music-emu/changes.txt | 5 + thirdparty/game-music-emu/design.txt | 194 + thirdparty/game-music-emu/gme.txt | 376 + thirdparty/game-music-emu/gme/Ay_Apu.cpp | 395 + thirdparty/game-music-emu/gme/Ay_Apu.h | 106 + thirdparty/game-music-emu/gme/Ay_Cpu.cpp | 1659 ++ thirdparty/game-music-emu/gme/Ay_Cpu.h | 89 + thirdparty/game-music-emu/gme/Ay_Emu.cpp | 405 + thirdparty/game-music-emu/gme/Ay_Emu.h | 69 + thirdparty/game-music-emu/gme/Blip_Buffer.cpp | 460 + thirdparty/game-music-emu/gme/Blip_Buffer.h | 490 + thirdparty/game-music-emu/gme/CMakeLists.txt | 211 + thirdparty/game-music-emu/gme/Classic_Emu.cpp | 190 + thirdparty/game-music-emu/gme/Classic_Emu.h | 128 + thirdparty/game-music-emu/gme/Data_Reader.cpp | 450 + thirdparty/game-music-emu/gme/Data_Reader.h | 150 + .../game-music-emu/gme/Dual_Resampler.cpp | 141 + .../game-music-emu/gme/Dual_Resampler.h | 50 + .../game-music-emu/gme/Effects_Buffer.cpp | 595 + .../game-music-emu/gme/Effects_Buffer.h | 90 + .../game-music-emu/gme/Fir_Resampler.cpp | 199 + thirdparty/game-music-emu/gme/Fir_Resampler.h | 171 + thirdparty/game-music-emu/gme/Gb_Apu.cpp | 306 + thirdparty/game-music-emu/gme/Gb_Apu.h | 90 + thirdparty/game-music-emu/gme/Gb_Cpu.cpp | 1054 ++ thirdparty/game-music-emu/gme/Gb_Cpu.h | 91 + thirdparty/game-music-emu/gme/Gb_Oscs.cpp | 336 + thirdparty/game-music-emu/gme/Gb_Oscs.h | 83 + thirdparty/game-music-emu/gme/Gbs_Emu.cpp | 290 + thirdparty/game-music-emu/gme/Gbs_Emu.h | 88 + thirdparty/game-music-emu/gme/Gme_File.cpp | 216 + thirdparty/game-music-emu/gme/Gme_File.h | 173 + thirdparty/game-music-emu/gme/Gym_Emu.cpp | 380 + thirdparty/game-music-emu/gme/Gym_Emu.h | 82 + thirdparty/game-music-emu/gme/Hes_Apu.cpp | 315 + thirdparty/game-music-emu/gme/Hes_Apu.h | 66 + thirdparty/game-music-emu/gme/Hes_Cpu.cpp | 1295 ++ thirdparty/game-music-emu/gme/Hes_Cpu.h | 122 + thirdparty/game-music-emu/gme/Hes_Emu.cpp | 531 + thirdparty/game-music-emu/gme/Hes_Emu.h | 94 + thirdparty/game-music-emu/gme/Kss_Cpu.cpp | 1700 ++ thirdparty/game-music-emu/gme/Kss_Cpu.h | 120 + thirdparty/game-music-emu/gme/Kss_Emu.cpp | 416 + thirdparty/game-music-emu/gme/Kss_Emu.h | 95 + thirdparty/game-music-emu/gme/Kss_Scc_Apu.cpp | 97 + thirdparty/game-music-emu/gme/Kss_Scc_Apu.h | 106 + .../game-music-emu/gme/M3u_Playlist.cpp | 426 + thirdparty/game-music-emu/gme/M3u_Playlist.h | 67 + .../game-music-emu/gme/Multi_Buffer.cpp | 232 + thirdparty/game-music-emu/gme/Multi_Buffer.h | 158 + thirdparty/game-music-emu/gme/Music_Emu.cpp | 451 + thirdparty/game-music-emu/gme/Music_Emu.h | 252 + thirdparty/game-music-emu/gme/Nes_Apu.cpp | 391 + thirdparty/game-music-emu/gme/Nes_Apu.h | 179 + thirdparty/game-music-emu/gme/Nes_Cpu.cpp | 1073 ++ thirdparty/game-music-emu/gme/Nes_Cpu.h | 112 + .../game-music-emu/gme/Nes_Fme7_Apu.cpp | 121 + thirdparty/game-music-emu/gme/Nes_Fme7_Apu.h | 131 + .../game-music-emu/gme/Nes_Namco_Apu.cpp | 145 + thirdparty/game-music-emu/gme/Nes_Namco_Apu.h | 102 + thirdparty/game-music-emu/gme/Nes_Oscs.cpp | 551 + thirdparty/game-music-emu/gme/Nes_Oscs.h | 147 + .../game-music-emu/gme/Nes_Vrc6_Apu.cpp | 215 + thirdparty/game-music-emu/gme/Nes_Vrc6_Apu.h | 95 + thirdparty/game-music-emu/gme/Nsf_Emu.cpp | 561 + thirdparty/game-music-emu/gme/Nsf_Emu.h | 106 + thirdparty/game-music-emu/gme/Nsfe_Emu.cpp | 335 + thirdparty/game-music-emu/gme/Nsfe_Emu.h | 68 + thirdparty/game-music-emu/gme/Sap_Apu.cpp | 334 + thirdparty/game-music-emu/gme/Sap_Apu.h | 77 + thirdparty/game-music-emu/gme/Sap_Cpu.cpp | 1004 + thirdparty/game-music-emu/gme/Sap_Cpu.h | 81 + thirdparty/game-music-emu/gme/Sap_Emu.cpp | 443 + thirdparty/game-music-emu/gme/Sap_Emu.h | 68 + thirdparty/game-music-emu/gme/Sms_Apu.cpp | 330 + thirdparty/game-music-emu/gme/Sms_Apu.h | 75 + thirdparty/game-music-emu/gme/Sms_Oscs.h | 49 + thirdparty/game-music-emu/gme/Snes_Spc.cpp | 380 + thirdparty/game-music-emu/gme/Snes_Spc.h | 283 + thirdparty/game-music-emu/gme/Spc_Cpu.cpp | 549 + thirdparty/game-music-emu/gme/Spc_Cpu.h | 1182 ++ thirdparty/game-music-emu/gme/Spc_Dsp.cpp | 704 + thirdparty/game-music-emu/gme/Spc_Dsp.h | 207 + thirdparty/game-music-emu/gme/Spc_Emu.cpp | 358 + thirdparty/game-music-emu/gme/Spc_Emu.h | 82 + thirdparty/game-music-emu/gme/Spc_Filter.cpp | 83 + thirdparty/game-music-emu/gme/Spc_Filter.h | 53 + thirdparty/game-music-emu/gme/Vgm_Emu.cpp | 434 + thirdparty/game-music-emu/gme/Vgm_Emu.h | 86 + .../game-music-emu/gme/Vgm_Emu_Impl.cpp | 314 + thirdparty/game-music-emu/gme/Vgm_Emu_Impl.h | 71 + thirdparty/game-music-emu/gme/Ym2413_Emu.cpp | 21 + thirdparty/game-music-emu/gme/Ym2413_Emu.h | 33 + thirdparty/game-music-emu/gme/Ym2612_Emu.h | 19 + thirdparty/game-music-emu/gme/Ym2612_GENS.cpp | 1319 ++ thirdparty/game-music-emu/gme/Ym2612_GENS.h | 38 + thirdparty/game-music-emu/gme/Ym2612_MAME.cpp | 3108 ++++ thirdparty/game-music-emu/gme/Ym2612_MAME.h | 38 + .../game-music-emu/gme/Ym2612_Nuked.cpp | 1872 ++ thirdparty/game-music-emu/gme/Ym2612_Nuked.h | 41 + thirdparty/game-music-emu/gme/blargg_common.h | 160 + thirdparty/game-music-emu/gme/blargg_config.h | 43 + thirdparty/game-music-emu/gme/blargg_endian.h | 184 + thirdparty/game-music-emu/gme/blargg_source.h | 123 + thirdparty/game-music-emu/gme/gb_cpu_io.h | 72 + thirdparty/game-music-emu/gme/gme.cpp | 420 + thirdparty/game-music-emu/gme/gme.h | 286 + thirdparty/game-music-emu/gme/gme_types.h | 21 + thirdparty/game-music-emu/gme/gme_types.h.in | 23 + thirdparty/game-music-emu/gme/hes_cpu_io.h | 101 + thirdparty/game-music-emu/gme/libgme.pc.in | 16 + thirdparty/game-music-emu/gme/nes_cpu_io.h | 83 + thirdparty/game-music-emu/gme/sap_cpu_io.h | 26 + thirdparty/game-music-emu/license.txt | 504 + thirdparty/game-music-emu/readme.txt | 241 + thirdparty/libxmp/CMakeLists.txt | 102 + thirdparty/libxmp/include/xmp.h | 407 + thirdparty/libxmp/src/callbackio.h | 187 + thirdparty/libxmp/src/common.h | 574 + thirdparty/libxmp/src/control.c | 590 + thirdparty/libxmp/src/dataio.c | 254 + thirdparty/libxmp/src/effects.c | 1133 ++ thirdparty/libxmp/src/effects.h | 163 + thirdparty/libxmp/src/extras.c | 163 + thirdparty/libxmp/src/extras.h | 18 + thirdparty/libxmp/src/far_extras.c | 405 + thirdparty/libxmp/src/far_extras.h | 55 + thirdparty/libxmp/src/filetype.c | 145 + thirdparty/libxmp/src/filter.c | 109 + thirdparty/libxmp/src/format.c | 127 + thirdparty/libxmp/src/format.h | 103 + thirdparty/libxmp/src/hio.c | 544 + thirdparty/libxmp/src/hio.h | 50 + thirdparty/libxmp/src/hmn_extras.c | 141 + thirdparty/libxmp/src/hmn_extras.h | 51 + thirdparty/libxmp/src/lfo.c | 163 + thirdparty/libxmp/src/lfo.h | 20 + thirdparty/libxmp/src/list.h | 141 + thirdparty/libxmp/src/load.c | 580 + thirdparty/libxmp/src/load_helpers.c | 560 + thirdparty/libxmp/src/loaders/669_load.c | 262 + thirdparty/libxmp/src/loaders/abk_load.c | 655 + thirdparty/libxmp/src/loaders/amf_load.c | 592 + thirdparty/libxmp/src/loaders/arch_load.c | 510 + thirdparty/libxmp/src/loaders/asylum_load.c | 185 + thirdparty/libxmp/src/loaders/chip_load.c | 203 + thirdparty/libxmp/src/loaders/coco_load.c | 318 + thirdparty/libxmp/src/loaders/common.c | 594 + thirdparty/libxmp/src/loaders/dbm_load.c | 582 + thirdparty/libxmp/src/loaders/digi_load.c | 252 + thirdparty/libxmp/src/loaders/dt_load.c | 354 + thirdparty/libxmp/src/loaders/emod_load.c | 262 + thirdparty/libxmp/src/loaders/far_load.c | 472 + thirdparty/libxmp/src/loaders/flt_load.c | 491 + thirdparty/libxmp/src/loaders/fnk_load.c | 342 + thirdparty/libxmp/src/loaders/gal4_load.c | 481 + thirdparty/libxmp/src/loaders/gal5_load.c | 404 + thirdparty/libxmp/src/loaders/gdm_load.c | 449 + thirdparty/libxmp/src/loaders/hmn_load.c | 324 + thirdparty/libxmp/src/loaders/ice_load.c | 206 + thirdparty/libxmp/src/loaders/iff.c | 215 + thirdparty/libxmp/src/loaders/iff.h | 43 + thirdparty/libxmp/src/loaders/imf_load.c | 541 + thirdparty/libxmp/src/loaders/ims_load.c | 293 + thirdparty/libxmp/src/loaders/it.h | 196 + thirdparty/libxmp/src/loaders/it_load.c | 1465 ++ thirdparty/libxmp/src/loaders/itsex.c | 243 + thirdparty/libxmp/src/loaders/liq_load.c | 657 + thirdparty/libxmp/src/loaders/loader.h | 74 + thirdparty/libxmp/src/loaders/lzw.c | 439 + thirdparty/libxmp/src/loaders/lzw.h | 17 + thirdparty/libxmp/src/loaders/masi16_load.c | 437 + thirdparty/libxmp/src/loaders/masi_load.c | 835 + thirdparty/libxmp/src/loaders/mdl_load.c | 1252 ++ thirdparty/libxmp/src/loaders/med.h | 334 + thirdparty/libxmp/src/loaders/med2_load.c | 243 + thirdparty/libxmp/src/loaders/med3_load.c | 439 + thirdparty/libxmp/src/loaders/med4_load.c | 863 + thirdparty/libxmp/src/loaders/mfp_load.c | 253 + thirdparty/libxmp/src/loaders/mgt_load.c | 382 + thirdparty/libxmp/src/loaders/mmd1_load.c | 629 + thirdparty/libxmp/src/loaders/mmd3_load.c | 596 + thirdparty/libxmp/src/loaders/mmd_common.c | 1028 + thirdparty/libxmp/src/loaders/mod.h | 59 + thirdparty/libxmp/src/loaders/mod_load.c | 1010 + thirdparty/libxmp/src/loaders/mtm_load.c | 344 + thirdparty/libxmp/src/loaders/muse_load.c | 110 + thirdparty/libxmp/src/loaders/no_load.c | 257 + thirdparty/libxmp/src/loaders/okt_load.c | 389 + thirdparty/libxmp/src/loaders/pt3_load.c | 305 + thirdparty/libxmp/src/loaders/ptm_load.c | 372 + thirdparty/libxmp/src/loaders/pw_load.c | 202 + thirdparty/libxmp/src/loaders/rtm_load.c | 521 + thirdparty/libxmp/src/loaders/s3m.h | 126 + thirdparty/libxmp/src/loaders/s3m_load.c | 666 + thirdparty/libxmp/src/loaders/sample.c | 375 + thirdparty/libxmp/src/loaders/sfx_load.c | 261 + thirdparty/libxmp/src/loaders/st_load.c | 517 + thirdparty/libxmp/src/loaders/stim_load.c | 216 + thirdparty/libxmp/src/loaders/stm_load.c | 416 + thirdparty/libxmp/src/loaders/stx_load.c | 385 + thirdparty/libxmp/src/loaders/sym_load.c | 632 + thirdparty/libxmp/src/loaders/ult_load.c | 364 + thirdparty/libxmp/src/loaders/umx_load.c | 422 + thirdparty/libxmp/src/loaders/voltable.c | 120 + thirdparty/libxmp/src/loaders/vorbis.c | 5676 ++++++ thirdparty/libxmp/src/loaders/vorbis.h | 46 + thirdparty/libxmp/src/loaders/xm.h | 101 + thirdparty/libxmp/src/loaders/xm_load.c | 1023 + thirdparty/libxmp/src/loaders/xmf_load.c | 433 + thirdparty/libxmp/src/md5.c | 240 + thirdparty/libxmp/src/md5.h | 37 + thirdparty/libxmp/src/mdataio.h | 124 + thirdparty/libxmp/src/med_extras.c | 448 + thirdparty/libxmp/src/med_extras.h | 77 + thirdparty/libxmp/src/memio.c | 118 + thirdparty/libxmp/src/memio.h | 26 + thirdparty/libxmp/src/miniz.h | 1422 ++ thirdparty/libxmp/src/misc.c | 30 + thirdparty/libxmp/src/mix_all.c | 456 + thirdparty/libxmp/src/mix_paula.c | 165 + thirdparty/libxmp/src/mixer.c | 1028 + thirdparty/libxmp/src/mixer.h | 83 + thirdparty/libxmp/src/mkstemp.c | 105 + thirdparty/libxmp/src/paula.h | 37 + thirdparty/libxmp/src/period.c | 285 + thirdparty/libxmp/src/period.h | 27 + thirdparty/libxmp/src/player.c | 2216 +++ thirdparty/libxmp/src/player.h | 282 + thirdparty/libxmp/src/precomp_blep.h | 256 + thirdparty/libxmp/src/precomp_lut.h | 524 + thirdparty/libxmp/src/read_event.c | 1654 ++ thirdparty/libxmp/src/scan.c | 721 + thirdparty/libxmp/src/smix.c | 335 + thirdparty/libxmp/src/tempfile.c | 165 + thirdparty/libxmp/src/tempfile.h | 7 + thirdparty/libxmp/src/virtual.c | 626 + thirdparty/libxmp/src/virtual.h | 39 + thirdparty/libxmp/src/win32.c | 47 + thirdparty/miniz/CMakeLists.txt | 9 + thirdparty/miniz/miniz.c | 7833 ++++++++ thirdparty/miniz/miniz.h | 1422 ++ thirdparty/mpg123.h | 1202 ++ thirdparty/oplsynth/CMakeLists.txt | 31 + thirdparty/oplsynth/OPL3.cpp | 1883 ++ thirdparty/oplsynth/dosbox/opl.cpp | 1452 ++ thirdparty/oplsynth/dosbox/opl.h | 231 + thirdparty/oplsynth/fmopl.cpp | 1852 ++ thirdparty/oplsynth/musicblock.cpp | 498 + thirdparty/oplsynth/nukedopl3.cpp | 1036 ++ thirdparty/oplsynth/opl_mus_player.cpp | 527 + thirdparty/oplsynth/oplio.cpp | 497 + thirdparty/oplsynth/oplsynth/genmidi.h | 60 + thirdparty/oplsynth/oplsynth/musicblock.h | 66 + thirdparty/oplsynth/oplsynth/nukedopl3.h | 241 + thirdparty/oplsynth/oplsynth/o_swap.h | 255 + thirdparty/oplsynth/oplsynth/opl.h | 30 + thirdparty/oplsynth/oplsynth/opl3_Float.h | 238 + thirdparty/oplsynth/oplsynth/opl_mus_player.h | 51 + thirdparty/oplsynth/oplsynth/oplio.h | 99 + thirdparty/opnmidi/CMakeLists.txt | 59 + thirdparty/opnmidi/chips/chipset.cmake | 40 + thirdparty/opnmidi/chips/chipset.pri | 91 + thirdparty/opnmidi/chips/gens/Ym2612.cpp | 2065 ++ thirdparty/opnmidi/chips/gens/Ym2612.hpp | 95 + thirdparty/opnmidi/chips/gens/Ym2612_p.hpp | 328 + thirdparty/opnmidi/chips/gens_opn2.cpp | 111 + thirdparty/opnmidi/chips/gens_opn2.h | 45 + thirdparty/opnmidi/chips/gx/gx_ym2612.c | 2271 +++ thirdparty/opnmidi/chips/gx/gx_ym2612.h | 53 + thirdparty/opnmidi/chips/gx_opn2.cpp | 84 + thirdparty/opnmidi/chips/gx_opn2.h | 46 + thirdparty/opnmidi/chips/mame/mame_ym2612fm.c | 2900 +++ thirdparty/opnmidi/chips/mame/mame_ym2612fm.h | 165 + thirdparty/opnmidi/chips/mame/mamedef.h | 70 + thirdparty/opnmidi/chips/mame_opn2.cpp | 80 + thirdparty/opnmidi/chips/mame_opn2.h | 44 + thirdparty/opnmidi/chips/mame_opna.cpp | 202 + thirdparty/opnmidi/chips/mame_opna.h | 45 + thirdparty/opnmidi/chips/mamefm/2608intf.h | 48 + thirdparty/opnmidi/chips/mamefm/emu.h | 104 + thirdparty/opnmidi/chips/mamefm/emu2149.c | 529 + thirdparty/opnmidi/chips/mamefm/emu2149.h | 98 + thirdparty/opnmidi/chips/mamefm/emutypes.h | 38 + thirdparty/opnmidi/chips/mamefm/fm.cpp | 3987 ++++ thirdparty/opnmidi/chips/mamefm/fm.h | 193 + .../opnmidi/chips/mamefm/fmopn_2608rom.h | 543 + thirdparty/opnmidi/chips/mamefm/resampler.cpp | 137 + thirdparty/opnmidi/chips/mamefm/resampler.hpp | 70 + thirdparty/opnmidi/chips/mamefm/ymdeltat.cpp | 628 + thirdparty/opnmidi/chips/mamefm/ymdeltat.h | 91 + thirdparty/opnmidi/chips/np2/LICENSE | 24 + thirdparty/opnmidi/chips/np2/compiler.h | 23 + .../chips/np2/fmgen_changelog_opnmidi.txt | 13 + thirdparty/opnmidi/chips/np2/fmgen_diag.h | 15 + thirdparty/opnmidi/chips/np2/fmgen_file.cpp | 177 + thirdparty/opnmidi/chips/np2/fmgen_file.h | 64 + thirdparty/opnmidi/chips/np2/fmgen_fmgen.cpp | 1156 ++ thirdparty/opnmidi/chips/np2/fmgen_fmgen.h | 342 + thirdparty/opnmidi/chips/np2/fmgen_fmgeninl.h | 274 + .../opnmidi/chips/np2/fmgen_fmtimer.cpp | 217 + thirdparty/opnmidi/chips/np2/fmgen_fmtimer.h | 67 + thirdparty/opnmidi/chips/np2/fmgen_headers.h | 10 + thirdparty/opnmidi/chips/np2/fmgen_misc.h | 69 + thirdparty/opnmidi/chips/np2/fmgen_opna.cpp | 2142 +++ thirdparty/opnmidi/chips/np2/fmgen_opna.h | 523 + thirdparty/opnmidi/chips/np2/fmgen_psg.cpp | 395 + thirdparty/opnmidi/chips/np2/fmgen_psg.h | 115 + thirdparty/opnmidi/chips/np2/fmgen_readme.txt | 93 + .../opnmidi/chips/np2/fmgen_readme_kai.txt | 24 + thirdparty/opnmidi/chips/np2/fmgen_types.h | 23 + thirdparty/opnmidi/chips/np2_opna.cpp | 95 + thirdparty/opnmidi/chips/np2_opna.h | 48 + thirdparty/opnmidi/chips/nuked/ym3438.c | 1630 ++ thirdparty/opnmidi/chips/nuked/ym3438.h | 250 + thirdparty/opnmidi/chips/nuked_opn2.cpp | 76 + thirdparty/opnmidi/chips/nuked_opn2.h | 47 + thirdparty/opnmidi/chips/opn_chip_base.h | 163 + thirdparty/opnmidi/chips/opn_chip_base.tcc | 322 + thirdparty/opnmidi/chips/opn_chip_family.h | 84 + thirdparty/opnmidi/chips/pmdwin/LICENSE | 22 + thirdparty/opnmidi/chips/pmdwin/op.h | 144 + thirdparty/opnmidi/chips/pmdwin/opna.c | 1369 ++ thirdparty/opnmidi/chips/pmdwin/opna.h | 91 + thirdparty/opnmidi/chips/pmdwin/psg.c | 342 + thirdparty/opnmidi/chips/pmdwin/psg.h | 52 + thirdparty/opnmidi/chips/pmdwin/rhythmdata.c | 5 + thirdparty/opnmidi/chips/pmdwin/rhythmdata.h | 5254 ++++++ thirdparty/opnmidi/chips/pmdwin_opna.cpp | 84 + thirdparty/opnmidi/chips/pmdwin_opna.h | 45 + thirdparty/opnmidi/chips/vgm_file_dumper.cpp | 302 + thirdparty/opnmidi/chips/vgm_file_dumper.h | 91 + thirdparty/opnmidi/chips/ymfm/README.md | 125 + thirdparty/opnmidi/chips/ymfm/ymfm.h | 572 + thirdparty/opnmidi/chips/ymfm/ymfm_adpcm.cpp | 807 + thirdparty/opnmidi/chips/ymfm/ymfm_adpcm.h | 411 + thirdparty/opnmidi/chips/ymfm/ymfm_fm.h | 477 + thirdparty/opnmidi/chips/ymfm/ymfm_fm.hpp | 1646 ++ thirdparty/opnmidi/chips/ymfm/ymfm_misc.cpp | 175 + thirdparty/opnmidi/chips/ymfm/ymfm_misc.h | 93 + thirdparty/opnmidi/chips/ymfm/ymfm_opn.cpp | 2500 +++ thirdparty/opnmidi/chips/ymfm/ymfm_opn.h | 807 + thirdparty/opnmidi/chips/ymfm/ymfm_pcm.cpp | 714 + thirdparty/opnmidi/chips/ymfm/ymfm_pcm.h | 347 + thirdparty/opnmidi/chips/ymfm/ymfm_ssg.cpp | 279 + thirdparty/opnmidi/chips/ymfm/ymfm_ssg.h | 205 + thirdparty/opnmidi/chips/ymfm_opn2.cpp | 115 + thirdparty/opnmidi/chips/ymfm_opn2.h | 59 + thirdparty/opnmidi/chips/ymfm_opna.cpp | 162 + thirdparty/opnmidi/chips/ymfm_opna.h | 60 + thirdparty/opnmidi/file_reader.hpp | 301 + thirdparty/opnmidi/fraction.hpp | 220 + thirdparty/opnmidi/opnbank.h | 145 + thirdparty/opnmidi/opnmidi.cpp | 1539 ++ thirdparty/opnmidi/opnmidi.h | 1259 ++ thirdparty/opnmidi/opnmidi_bankmap.h | 127 + thirdparty/opnmidi/opnmidi_bankmap.tcc | 283 + thirdparty/opnmidi/opnmidi_cvt.hpp | 82 + thirdparty/opnmidi/opnmidi_load.cpp | 250 + thirdparty/opnmidi/opnmidi_midiplay.cpp | 2029 ++ thirdparty/opnmidi/opnmidi_midiplay.hpp | 1046 ++ thirdparty/opnmidi/opnmidi_opn2.cpp | 747 + thirdparty/opnmidi/opnmidi_opn2.hpp | 289 + thirdparty/opnmidi/opnmidi_private.cpp | 38 + thirdparty/opnmidi/opnmidi_private.hpp | 192 + thirdparty/opnmidi/opnmidi_ptr.hpp | 217 + thirdparty/opnmidi/structures/pl_list.hpp | 133 + thirdparty/opnmidi/structures/pl_list.tcc | 338 + thirdparty/opnmidi/wopn/wopn_file.c | 639 + thirdparty/opnmidi/wopn/wopn_file.h | 275 + thirdparty/sndfile.h | 667 + thirdparty/timidity/CHANGES | 58 + thirdparty/timidity/CMakeLists.txt | 32 + thirdparty/timidity/COPYING | 513 + thirdparty/timidity/FAQ | 100 + thirdparty/timidity/README | 61 + thirdparty/timidity/common.cpp | 67 + thirdparty/timidity/instrum.cpp | 728 + thirdparty/timidity/instrum_dls.cpp | 1243 ++ thirdparty/timidity/instrum_font.cpp | 128 + thirdparty/timidity/instrum_sf2.cpp | 1539 ++ thirdparty/timidity/mix.cpp | 759 + thirdparty/timidity/playmidi.cpp | 909 + thirdparty/timidity/resample.cpp | 615 + thirdparty/timidity/timidity.cpp | 839 + thirdparty/timidity/timidity/common.h | 149 + thirdparty/timidity/timidity/dls1.h | 266 + thirdparty/timidity/timidity/dls2.h | 130 + thirdparty/timidity/timidity/gf1patch.h | 85 + thirdparty/timidity/timidity/instrum.h | 197 + thirdparty/timidity/timidity/playmidi.h | 122 + thirdparty/timidity/timidity/sf2.h | 327 + thirdparty/timidity/timidity/t_swap.h | 255 + thirdparty/timidity/timidity/timidity.h | 255 + thirdparty/timidityplus/CMakeLists.txt | 36 + thirdparty/timidityplus/common.cpp | 171 + thirdparty/timidityplus/configfile.cpp | 1612 ++ thirdparty/timidityplus/effect.cpp | 220 + thirdparty/timidityplus/fft4g.cpp | 1353 ++ thirdparty/timidityplus/filter.cpp | 199 + thirdparty/timidityplus/freq.cpp | 710 + thirdparty/timidityplus/instrum.cpp | 2050 ++ thirdparty/timidityplus/mblock.cpp | 174 + thirdparty/timidityplus/mix.cpp | 1624 ++ thirdparty/timidityplus/playmidi.cpp | 6149 ++++++ thirdparty/timidityplus/quantity.cpp | 318 + thirdparty/timidityplus/readmidic.cpp | 2664 +++ thirdparty/timidityplus/recache.cpp | 438 + thirdparty/timidityplus/resample.cpp | 970 + thirdparty/timidityplus/reverb.cpp | 4438 +++++ thirdparty/timidityplus/sbkconv.cpp | 208 + thirdparty/timidityplus/sffile.cpp | 724 + thirdparty/timidityplus/sfitem.cpp | 96 + thirdparty/timidityplus/smplfile.cpp | 1226 ++ thirdparty/timidityplus/sndfont.cpp | 1511 ++ thirdparty/timidityplus/tables.cpp | 1011 + thirdparty/timidityplus/timiditypp/common.h | 73 + thirdparty/timidityplus/timiditypp/controls.h | 60 + thirdparty/timidityplus/timiditypp/effect.h | 37 + thirdparty/timidityplus/timiditypp/fft4g.h | 16 + thirdparty/timidityplus/timiditypp/filter.h | 46 + thirdparty/timidityplus/timiditypp/freq.h | 67 + thirdparty/timidityplus/timiditypp/instrum.h | 564 + thirdparty/timidityplus/timiditypp/mblock.h | 78 + thirdparty/timidityplus/timiditypp/mix.h | 76 + thirdparty/timidityplus/timiditypp/optcode.h | 65 + thirdparty/timidityplus/timiditypp/playmidi.h | 747 + thirdparty/timidityplus/timiditypp/quantity.h | 70 + thirdparty/timidityplus/timiditypp/recache.h | 107 + thirdparty/timidityplus/timiditypp/resample.h | 88 + thirdparty/timidityplus/timiditypp/reverb.h | 814 + thirdparty/timidityplus/timiditypp/sffile.h | 144 + thirdparty/timidityplus/timiditypp/sfitem.h | 94 + thirdparty/timidityplus/timiditypp/sflayer.h | 107 + thirdparty/timidityplus/timiditypp/sysdep.h | 87 + thirdparty/timidityplus/timiditypp/t_swap.h | 255 + thirdparty/timidityplus/timiditypp/tables.h | 96 + thirdparty/timidityplus/timiditypp/timidity.h | 180 + thirdparty/wildmidi/CMakeLists.txt | 29 + thirdparty/wildmidi/file_io.cpp | 84 + thirdparty/wildmidi/gus_pat.cpp | 1033 + thirdparty/wildmidi/reverb.cpp | 401 + thirdparty/wildmidi/wildmidi/common.h | 95 + thirdparty/wildmidi/wildmidi/file_io.h | 37 + thirdparty/wildmidi/wildmidi/gus_pat.h | 40 + thirdparty/wildmidi/wildmidi/reverb.h | 62 + thirdparty/wildmidi/wildmidi/wildmidi_lib.h | 146 + thirdparty/wildmidi/wildmidi/wm_error.h | 60 + thirdparty/wildmidi/wildmidi_lib.cpp | 2690 +++ thirdparty/wildmidi/wm_error.cpp | 87 + vcpkg.json | 30 + 852 files changed, 381622 insertions(+) create mode 100644 .github/workflows/continuous_integration.yml create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 cmake/FindFluidSynth.cmake create mode 100644 cmake/FindGME.cmake create mode 100644 cmake/FindMPG123.cmake create mode 100644 cmake/FindSndFile.cmake create mode 100644 cmake/ZMusicConfig.cmake.in create mode 100644 cmake/ZUtility.cmake create mode 100644 include/zmusic.h create mode 100644 licenses/bsd.txt create mode 100644 licenses/dumb.txt create mode 100644 licenses/gplv3.txt create mode 100644 licenses/legal.txt create mode 100644 licenses/lgplv21.txt create mode 100644 licenses/lgplv3.txt create mode 100644 licenses/zmusic.txt create mode 100644 samples/list_midi_devices/.gitignore create mode 100644 samples/list_midi_devices/CMakeLists.txt create mode 100644 samples/list_midi_devices/list_midi_devices.cpp create mode 100644 source/CMakeLists.txt create mode 100644 source/data/xg.h create mode 100644 source/data/xg.wopn create mode 100644 source/decoder/mpg123_decoder.cpp create mode 100644 source/decoder/mpg123_decoder.h create mode 100644 source/decoder/mpgload.h create mode 100644 source/decoder/sndfile_decoder.cpp create mode 100644 source/decoder/sndfile_decoder.h create mode 100644 source/decoder/sndload.h create mode 100644 source/decoder/sounddecoder.cpp create mode 100644 source/loader/i_module.cpp create mode 100644 source/loader/i_module.h create mode 100644 source/loader/test.c create mode 100644 source/mididevices/driver_adlib.cpp create mode 100644 source/mididevices/mididevice.h create mode 100644 source/mididevices/music_adlmidi_mididevice.cpp create mode 100644 source/mididevices/music_alsa_mididevice.cpp create mode 100644 source/mididevices/music_alsa_state.cpp create mode 100644 source/mididevices/music_alsa_state.h create mode 100644 source/mididevices/music_base_mididevice.cpp create mode 100644 source/mididevices/music_fluidsynth_mididevice.cpp create mode 100644 source/mididevices/music_opl_mididevice.cpp create mode 100644 source/mididevices/music_opnmidi_mididevice.cpp create mode 100644 source/mididevices/music_softsynth_mididevice.cpp create mode 100644 source/mididevices/music_timidity_mididevice.cpp create mode 100644 source/mididevices/music_timiditypp_mididevice.cpp create mode 100644 source/mididevices/music_wavewriter_mididevice.cpp create mode 100644 source/mididevices/music_wildmidi_mididevice.cpp create mode 100644 source/mididevices/music_win_mididevice.cpp create mode 100644 source/midisources/midisource.cpp create mode 100644 source/midisources/midisource.h create mode 100644 source/midisources/midisource_hmi.cpp create mode 100644 source/midisources/midisource_mids.cpp create mode 100644 source/midisources/midisource_mus.cpp create mode 100644 source/midisources/midisource_smf.cpp create mode 100644 source/midisources/midisource_xmi.cpp create mode 100644 source/musicformats/music_cd.cpp create mode 100644 source/musicformats/music_midi.cpp create mode 100644 source/musicformats/music_stream.cpp create mode 100644 source/musicformats/win32/helperthread.cpp create mode 100644 source/musicformats/win32/helperthread.h create mode 100644 source/musicformats/win32/i_cd.cpp create mode 100644 source/musicformats/win32/i_cd.h create mode 100644 source/streamsources/music_dumb.cpp create mode 100644 source/streamsources/music_gme.cpp create mode 100644 source/streamsources/music_libsndfile.cpp create mode 100644 source/streamsources/music_libxmp.cpp create mode 100644 source/streamsources/music_opl.cpp create mode 100644 source/streamsources/music_xa.cpp create mode 100644 source/streamsources/streamsource.h create mode 100644 source/zmusic/configuration.cpp create mode 100644 source/zmusic/critsec.cpp create mode 100644 source/zmusic/critsec.h create mode 100644 source/zmusic/fileio.h create mode 100644 source/zmusic/m_swap.h create mode 100644 source/zmusic/midiconfig.h create mode 100644 source/zmusic/mididefs.h create mode 100644 source/zmusic/mus2midi.h create mode 100644 source/zmusic/musinfo.h create mode 100644 source/zmusic/sounddecoder.h create mode 100644 source/zmusic/zmusic.cpp create mode 100644 source/zmusic/zmusic_internal.h create mode 100644 thirdparty/CMakeLists.txt create mode 100644 thirdparty/adlmidi/CMakeLists.txt create mode 100644 thirdparty/adlmidi/adlmidi.cpp create mode 100644 thirdparty/adlmidi/adlmidi.h create mode 100644 thirdparty/adlmidi/adlmidi_bankmap.h create mode 100644 thirdparty/adlmidi/adlmidi_bankmap.tcc create mode 100644 thirdparty/adlmidi/adlmidi_cvt.hpp create mode 100644 thirdparty/adlmidi/adlmidi_db.h create mode 100644 thirdparty/adlmidi/adlmidi_load.cpp create mode 100644 thirdparty/adlmidi/adlmidi_midiplay.cpp create mode 100644 thirdparty/adlmidi/adlmidi_midiplay.hpp create mode 100644 thirdparty/adlmidi/adlmidi_opl3.cpp create mode 100644 thirdparty/adlmidi/adlmidi_opl3.hpp create mode 100644 thirdparty/adlmidi/adlmidi_private.cpp create mode 100644 thirdparty/adlmidi/adlmidi_private.hpp create mode 100644 thirdparty/adlmidi/adlmidi_ptr.hpp create mode 100644 thirdparty/adlmidi/chips/common/mutex.hpp create mode 100644 thirdparty/adlmidi/chips/common/ptr.hpp create mode 100644 thirdparty/adlmidi/chips/dos_hw_opl.cpp create mode 100644 thirdparty/adlmidi/chips/dos_hw_opl.h create mode 100644 thirdparty/adlmidi/chips/dosbox/dbopl.cpp create mode 100644 thirdparty/adlmidi/chips/dosbox/dbopl.h create mode 100644 thirdparty/adlmidi/chips/dosbox_opl3.cpp create mode 100644 thirdparty/adlmidi/chips/dosbox_opl3.h create mode 100644 thirdparty/adlmidi/chips/esfmu/LICENSE create mode 100644 thirdparty/adlmidi/chips/esfmu/README.md create mode 100644 thirdparty/adlmidi/chips/esfmu/assets/logo.png create mode 100644 thirdparty/adlmidi/chips/esfmu/esfm.c create mode 100644 thirdparty/adlmidi/chips/esfmu/esfm.h create mode 100644 thirdparty/adlmidi/chips/esfmu/esfm_registers.c create mode 100644 thirdparty/adlmidi/chips/esfmu_opl3.cpp create mode 100644 thirdparty/adlmidi/chips/esfmu_opl3.h create mode 100644 thirdparty/adlmidi/chips/java/JavaOPL3.hpp create mode 100644 thirdparty/adlmidi/chips/java_opl3.cpp create mode 100644 thirdparty/adlmidi/chips/java_opl3.h create mode 100644 thirdparty/adlmidi/chips/mame/mame_fmopl.cpp create mode 100644 thirdparty/adlmidi/chips/mame/opl.h create mode 100644 thirdparty/adlmidi/chips/mame_opl2.cpp create mode 100644 thirdparty/adlmidi/chips/mame_opl2.h create mode 100644 thirdparty/adlmidi/chips/nuked/LICENSE create mode 100644 thirdparty/adlmidi/chips/nuked/nukedopl3.c create mode 100644 thirdparty/adlmidi/chips/nuked/nukedopl3.h create mode 100644 thirdparty/adlmidi/chips/nuked/nukedopl3_174.c create mode 100644 thirdparty/adlmidi/chips/nuked/nukedopl3_174.h create mode 100644 thirdparty/adlmidi/chips/nuked_opl3.cpp create mode 100644 thirdparty/adlmidi/chips/nuked_opl3.h create mode 100644 thirdparty/adlmidi/chips/nuked_opl3_v174.cpp create mode 100644 thirdparty/adlmidi/chips/nuked_opl3_v174.h create mode 100644 thirdparty/adlmidi/chips/opal/LICENSE.txt create mode 100644 thirdparty/adlmidi/chips/opal/old/LICENSE.txt create mode 100644 thirdparty/adlmidi/chips/opal/old/README.old create mode 100644 thirdparty/adlmidi/chips/opal/old/opal-orig.hpp create mode 100644 thirdparty/adlmidi/chips/opal/old/opal-pan.diff create mode 100644 thirdparty/adlmidi/chips/opal/opal.c create mode 100644 thirdparty/adlmidi/chips/opal/opal.h create mode 100644 thirdparty/adlmidi/chips/opal_opl3.cpp create mode 100644 thirdparty/adlmidi/chips/opal_opl3.h create mode 100644 thirdparty/adlmidi/chips/opl_chip_base.h create mode 100644 thirdparty/adlmidi/chips/opl_chip_base.tcc create mode 100644 thirdparty/adlmidi/chips/opl_serial_misc.h create mode 100644 thirdparty/adlmidi/chips/opl_serial_port.cpp create mode 100644 thirdparty/adlmidi/chips/opl_serial_port.h create mode 100644 thirdparty/adlmidi/chips/ym3812_lle.cpp create mode 100644 thirdparty/adlmidi/chips/ym3812_lle.h create mode 100644 thirdparty/adlmidi/chips/ym3812_lle/nopl2.c create mode 100644 thirdparty/adlmidi/chips/ym3812_lle/nopl2.h create mode 100644 thirdparty/adlmidi/chips/ym3812_lle/nuked_fmopl2.c create mode 100644 thirdparty/adlmidi/chips/ym3812_lle/nuked_fmopl2.h create mode 100644 thirdparty/adlmidi/chips/ymf262_lle.cpp create mode 100644 thirdparty/adlmidi/chips/ymf262_lle.h create mode 100644 thirdparty/adlmidi/chips/ymf262_lle/LICENSE create mode 100644 thirdparty/adlmidi/chips/ymf262_lle/Readme.md create mode 100644 thirdparty/adlmidi/chips/ymf262_lle/nopl3.c create mode 100644 thirdparty/adlmidi/chips/ymf262_lle/nopl3.h create mode 100644 thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.c create mode 100644 thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.h create mode 100644 thirdparty/adlmidi/chips/ymfm/README.md create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm.h create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_adpcm.cpp create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_adpcm.h create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_fm.h create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_fm.ipp create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_misc.cpp create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_misc.h create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_opl.cpp create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_opl.h create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_pcm.cpp create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_pcm.h create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_ssg.cpp create mode 100644 thirdparty/adlmidi/chips/ymfm/ymfm_ssg.h create mode 100644 thirdparty/adlmidi/chips/ymfm_opl2.cpp create mode 100644 thirdparty/adlmidi/chips/ymfm_opl2.h create mode 100644 thirdparty/adlmidi/chips/ymfm_opl3.cpp create mode 100644 thirdparty/adlmidi/chips/ymfm_opl3.h create mode 100644 thirdparty/adlmidi/file_reader.hpp create mode 100644 thirdparty/adlmidi/fraction.hpp create mode 100644 thirdparty/adlmidi/inst_db.cpp create mode 100644 thirdparty/adlmidi/oplinst.h create mode 100644 thirdparty/adlmidi/structures/pl_list.hpp create mode 100644 thirdparty/adlmidi/structures/pl_list.tcc create mode 100644 thirdparty/adlmidi/wopl/wopl_file.c create mode 100644 thirdparty/adlmidi/wopl/wopl_file.h create mode 100644 thirdparty/dumb/CMakeLists.txt create mode 100644 thirdparty/dumb/cmake/CMakeLists.txt create mode 100644 thirdparty/dumb/cmake/readme.txt create mode 100644 thirdparty/dumb/include/dumb.h create mode 100644 thirdparty/dumb/include/internal/aldumb.h create mode 100644 thirdparty/dumb/include/internal/barray.h create mode 100644 thirdparty/dumb/include/internal/dumb.h create mode 100644 thirdparty/dumb/include/internal/dumbfile.h create mode 100644 thirdparty/dumb/include/internal/it.h create mode 100644 thirdparty/dumb/include/internal/lpc.h create mode 100644 thirdparty/dumb/include/internal/mulsc.h create mode 100644 thirdparty/dumb/include/internal/resampler.h create mode 100644 thirdparty/dumb/include/internal/riff.h create mode 100644 thirdparty/dumb/include/internal/stack_alloc.h create mode 100644 thirdparty/dumb/licence.txt create mode 100644 thirdparty/dumb/prj/.gitignore create mode 100644 thirdparty/dumb/prj/dumb/dumb.pro create mode 100644 thirdparty/dumb/readme.txt create mode 100644 thirdparty/dumb/release.txt create mode 100644 thirdparty/dumb/src/core/atexit.c create mode 100644 thirdparty/dumb/src/core/duhlen.c create mode 100644 thirdparty/dumb/src/core/duhtag.c create mode 100644 thirdparty/dumb/src/core/dumbfile.c create mode 100644 thirdparty/dumb/src/core/loadduh.c create mode 100644 thirdparty/dumb/src/core/makeduh.c create mode 100644 thirdparty/dumb/src/core/rawsig.c create mode 100644 thirdparty/dumb/src/core/readduh.c create mode 100644 thirdparty/dumb/src/core/register.c create mode 100644 thirdparty/dumb/src/core/rendduh.c create mode 100644 thirdparty/dumb/src/core/rendsig.c create mode 100644 thirdparty/dumb/src/core/unload.c create mode 100644 thirdparty/dumb/src/helpers/barray.c create mode 100644 thirdparty/dumb/src/helpers/clickrem.c create mode 100644 thirdparty/dumb/src/helpers/lpc.c create mode 100644 thirdparty/dumb/src/helpers/memfile.c create mode 100644 thirdparty/dumb/src/helpers/resamp2.inc create mode 100644 thirdparty/dumb/src/helpers/resamp3.inc create mode 100644 thirdparty/dumb/src/helpers/resample.c create mode 100644 thirdparty/dumb/src/helpers/resample.inc create mode 100644 thirdparty/dumb/src/helpers/resampler.c create mode 100644 thirdparty/dumb/src/helpers/riff.c create mode 100644 thirdparty/dumb/src/helpers/sampbuf.c create mode 100644 thirdparty/dumb/src/helpers/silence.c create mode 100644 thirdparty/dumb/src/helpers/stdfile.c create mode 100644 thirdparty/dumb/src/it/itload.c create mode 100644 thirdparty/dumb/src/it/itload2.c create mode 100644 thirdparty/dumb/src/it/itmisc.c create mode 100644 thirdparty/dumb/src/it/itorder.c create mode 100644 thirdparty/dumb/src/it/itread.c create mode 100644 thirdparty/dumb/src/it/itread2.c create mode 100644 thirdparty/dumb/src/it/itrender.c create mode 100644 thirdparty/dumb/src/it/itunload.c create mode 100644 thirdparty/dumb/src/it/load669.c create mode 100644 thirdparty/dumb/src/it/load6692.c create mode 100644 thirdparty/dumb/src/it/loadamf.c create mode 100644 thirdparty/dumb/src/it/loadamf2.c create mode 100644 thirdparty/dumb/src/it/loadany.c create mode 100644 thirdparty/dumb/src/it/loadany2.c create mode 100644 thirdparty/dumb/src/it/loadasy.c create mode 100644 thirdparty/dumb/src/it/loadasy2.c create mode 100644 thirdparty/dumb/src/it/loadmod.c create mode 100644 thirdparty/dumb/src/it/loadmod2.c create mode 100644 thirdparty/dumb/src/it/loadmtm.c create mode 100644 thirdparty/dumb/src/it/loadmtm2.c create mode 100644 thirdparty/dumb/src/it/loadokt.c create mode 100644 thirdparty/dumb/src/it/loadokt2.c create mode 100644 thirdparty/dumb/src/it/loadoldpsm.c create mode 100644 thirdparty/dumb/src/it/loadoldpsm2.c create mode 100644 thirdparty/dumb/src/it/loadpsm.c create mode 100644 thirdparty/dumb/src/it/loadpsm2.c create mode 100644 thirdparty/dumb/src/it/loadptm.c create mode 100644 thirdparty/dumb/src/it/loadptm2.c create mode 100644 thirdparty/dumb/src/it/loadriff.c create mode 100644 thirdparty/dumb/src/it/loadriff2.c create mode 100644 thirdparty/dumb/src/it/loads3m.c create mode 100644 thirdparty/dumb/src/it/loads3m2.c create mode 100644 thirdparty/dumb/src/it/loadstm.c create mode 100644 thirdparty/dumb/src/it/loadstm2.c create mode 100644 thirdparty/dumb/src/it/loadxm.c create mode 100644 thirdparty/dumb/src/it/loadxm2.c create mode 100644 thirdparty/dumb/src/it/ptmeffect.c create mode 100644 thirdparty/dumb/src/it/read669.c create mode 100644 thirdparty/dumb/src/it/read6692.c create mode 100644 thirdparty/dumb/src/it/readam.c create mode 100644 thirdparty/dumb/src/it/readamf.c create mode 100644 thirdparty/dumb/src/it/readamf2.c create mode 100644 thirdparty/dumb/src/it/readany.c create mode 100644 thirdparty/dumb/src/it/readany2.c create mode 100644 thirdparty/dumb/src/it/readasy.c create mode 100644 thirdparty/dumb/src/it/readdsmf.c create mode 100644 thirdparty/dumb/src/it/readmod.c create mode 100644 thirdparty/dumb/src/it/readmod2.c create mode 100644 thirdparty/dumb/src/it/readmtm.c create mode 100644 thirdparty/dumb/src/it/readokt.c create mode 100644 thirdparty/dumb/src/it/readokt2.c create mode 100644 thirdparty/dumb/src/it/readoldpsm.c create mode 100644 thirdparty/dumb/src/it/readpsm.c create mode 100644 thirdparty/dumb/src/it/readptm.c create mode 100644 thirdparty/dumb/src/it/readriff.c create mode 100644 thirdparty/dumb/src/it/reads3m.c create mode 100644 thirdparty/dumb/src/it/reads3m2.c create mode 100644 thirdparty/dumb/src/it/readstm.c create mode 100644 thirdparty/dumb/src/it/readstm2.c create mode 100644 thirdparty/dumb/src/it/readxm.c create mode 100644 thirdparty/dumb/src/it/readxm2.c create mode 100644 thirdparty/dumb/src/it/xmeffect.c create mode 100644 thirdparty/dumb/vc6/dumb/.gitignore create mode 100644 thirdparty/dumb/vc6/dumb/dumb.vcxproj create mode 100644 thirdparty/dumb/vc6/dumb/dumb.vcxproj.filters create mode 100644 thirdparty/fluidsynth/include/fluidsynth.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/audio.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/event.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/gen.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/ladspa.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/log.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/midi.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/misc.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/mod.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/seq.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/seqbind.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/settings.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/sfont.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/shell.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/synth.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/types.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/version.h create mode 100644 thirdparty/fluidsynth/include/fluidsynth/voice.h create mode 100644 thirdparty/fluidsynth/src/CMakeLists.txt create mode 100644 thirdparty/fluidsynth/src/bindings/fluid_filerenderer.c create mode 100644 thirdparty/fluidsynth/src/bindings/fluid_ladspa.c create mode 100644 thirdparty/fluidsynth/src/bindings/fluid_ladspa.h create mode 100644 thirdparty/fluidsynth/src/config.h create mode 100644 thirdparty/fluidsynth/src/drivers/fluid_adriver.c create mode 100644 thirdparty/fluidsynth/src/drivers/fluid_adriver.h create mode 100644 thirdparty/fluidsynth/src/drivers/fluid_mdriver.c create mode 100644 thirdparty/fluidsynth/src/drivers/fluid_mdriver.h create mode 100644 thirdparty/fluidsynth/src/drivers/fluid_sndmgr.c create mode 100644 thirdparty/fluidsynth/src/fluid_conv_tables.inc.h create mode 100644 thirdparty/fluidsynth/src/fluid_rvoice_dsp_tables.inc.h create mode 100644 thirdparty/fluidsynth/src/gentables/CMakeLists.txt create mode 100644 thirdparty/fluidsynth/src/gentables/gen_conv.c create mode 100644 thirdparty/fluidsynth/src/gentables/gen_rvoice_dsp.c create mode 100644 thirdparty/fluidsynth/src/gentables/make_tables.c create mode 100644 thirdparty/fluidsynth/src/gentables/make_tables.h create mode 100644 thirdparty/fluidsynth/src/midi/fluid_midi.c create mode 100644 thirdparty/fluidsynth/src/midi/fluid_midi.h create mode 100644 thirdparty/fluidsynth/src/midi/fluid_midi_router.c create mode 100644 thirdparty/fluidsynth/src/midi/fluid_midi_router.h create mode 100644 thirdparty/fluidsynth/src/midi/fluid_seq.c create mode 100644 thirdparty/fluidsynth/src/midi/fluid_seq_queue.cpp create mode 100644 thirdparty/fluidsynth/src/midi/fluid_seq_queue.h create mode 100644 thirdparty/fluidsynth/src/midi/fluid_seqbind.c create mode 100644 thirdparty/fluidsynth/src/midi/fluid_seqbind_notes.cpp create mode 100644 thirdparty/fluidsynth/src/midi/fluid_seqbind_notes.h create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_adsr_env.c create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_adsr_env.h create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_chorus.c create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_chorus.h create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_iir_filter.c create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_iir_filter.h create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_lfo.c create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_lfo.h create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_phase.h create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_rev.c create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_rev.h create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_rvoice.c create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_rvoice.h create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_rvoice_dsp.c create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_rvoice_dsp_tables.h create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_rvoice_event.c create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_rvoice_event.h create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_rvoice_mixer.c create mode 100644 thirdparty/fluidsynth/src/rvoice/fluid_rvoice_mixer.h create mode 100644 thirdparty/fluidsynth/src/sfloader/fluid_defsfont.c create mode 100644 thirdparty/fluidsynth/src/sfloader/fluid_defsfont.h create mode 100644 thirdparty/fluidsynth/src/sfloader/fluid_instpatch.c create mode 100644 thirdparty/fluidsynth/src/sfloader/fluid_instpatch.h create mode 100644 thirdparty/fluidsynth/src/sfloader/fluid_samplecache.c create mode 100644 thirdparty/fluidsynth/src/sfloader/fluid_samplecache.h create mode 100644 thirdparty/fluidsynth/src/sfloader/fluid_sffile.c create mode 100644 thirdparty/fluidsynth/src/sfloader/fluid_sffile.h create mode 100644 thirdparty/fluidsynth/src/sfloader/fluid_sfont.c create mode 100644 thirdparty/fluidsynth/src/sfloader/fluid_sfont.h create mode 100644 thirdparty/fluidsynth/src/sfloader/sndfile.h create mode 100644 thirdparty/fluidsynth/src/synth/fluid_chan.c create mode 100644 thirdparty/fluidsynth/src/synth/fluid_chan.h create mode 100644 thirdparty/fluidsynth/src/synth/fluid_event.c create mode 100644 thirdparty/fluidsynth/src/synth/fluid_event.h create mode 100644 thirdparty/fluidsynth/src/synth/fluid_gen.c create mode 100644 thirdparty/fluidsynth/src/synth/fluid_gen.h create mode 100644 thirdparty/fluidsynth/src/synth/fluid_mod.c create mode 100644 thirdparty/fluidsynth/src/synth/fluid_mod.h create mode 100644 thirdparty/fluidsynth/src/synth/fluid_synth.c create mode 100644 thirdparty/fluidsynth/src/synth/fluid_synth.h create mode 100644 thirdparty/fluidsynth/src/synth/fluid_synth_monopoly.c create mode 100644 thirdparty/fluidsynth/src/synth/fluid_tuning.c create mode 100644 thirdparty/fluidsynth/src/synth/fluid_tuning.h create mode 100644 thirdparty/fluidsynth/src/synth/fluid_voice.c create mode 100644 thirdparty/fluidsynth/src/synth/fluid_voice.h create mode 100644 thirdparty/fluidsynth/src/utils/fluid_conv.c create mode 100644 thirdparty/fluidsynth/src/utils/fluid_conv.h create mode 100644 thirdparty/fluidsynth/src/utils/fluid_conv_tables.h create mode 100644 thirdparty/fluidsynth/src/utils/fluid_hash.c create mode 100644 thirdparty/fluidsynth/src/utils/fluid_hash.h create mode 100644 thirdparty/fluidsynth/src/utils/fluid_list.c create mode 100644 thirdparty/fluidsynth/src/utils/fluid_list.h create mode 100644 thirdparty/fluidsynth/src/utils/fluid_ringbuffer.c create mode 100644 thirdparty/fluidsynth/src/utils/fluid_ringbuffer.h create mode 100644 thirdparty/fluidsynth/src/utils/fluid_settings.c create mode 100644 thirdparty/fluidsynth/src/utils/fluid_settings.h create mode 100644 thirdparty/fluidsynth/src/utils/fluid_sys.c create mode 100644 thirdparty/fluidsynth/src/utils/fluid_sys.h create mode 100644 thirdparty/fluidsynth/src/utils/fluidsynth_priv.h create mode 100644 thirdparty/fluidsynth/src/utils/win32_glibstubs.c create mode 100644 thirdparty/fluidsynth/src/utils/win32_glibstubs.h create mode 100644 thirdparty/game-music-emu/CMakeLists.txt create mode 100644 thirdparty/game-music-emu/changes.txt create mode 100644 thirdparty/game-music-emu/design.txt create mode 100644 thirdparty/game-music-emu/gme.txt create mode 100644 thirdparty/game-music-emu/gme/Ay_Apu.cpp create mode 100644 thirdparty/game-music-emu/gme/Ay_Apu.h create mode 100644 thirdparty/game-music-emu/gme/Ay_Cpu.cpp create mode 100644 thirdparty/game-music-emu/gme/Ay_Cpu.h create mode 100644 thirdparty/game-music-emu/gme/Ay_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Ay_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Blip_Buffer.cpp create mode 100644 thirdparty/game-music-emu/gme/Blip_Buffer.h create mode 100644 thirdparty/game-music-emu/gme/CMakeLists.txt create mode 100644 thirdparty/game-music-emu/gme/Classic_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Classic_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Data_Reader.cpp create mode 100644 thirdparty/game-music-emu/gme/Data_Reader.h create mode 100644 thirdparty/game-music-emu/gme/Dual_Resampler.cpp create mode 100644 thirdparty/game-music-emu/gme/Dual_Resampler.h create mode 100644 thirdparty/game-music-emu/gme/Effects_Buffer.cpp create mode 100644 thirdparty/game-music-emu/gme/Effects_Buffer.h create mode 100644 thirdparty/game-music-emu/gme/Fir_Resampler.cpp create mode 100644 thirdparty/game-music-emu/gme/Fir_Resampler.h create mode 100644 thirdparty/game-music-emu/gme/Gb_Apu.cpp create mode 100644 thirdparty/game-music-emu/gme/Gb_Apu.h create mode 100644 thirdparty/game-music-emu/gme/Gb_Cpu.cpp create mode 100644 thirdparty/game-music-emu/gme/Gb_Cpu.h create mode 100644 thirdparty/game-music-emu/gme/Gb_Oscs.cpp create mode 100644 thirdparty/game-music-emu/gme/Gb_Oscs.h create mode 100644 thirdparty/game-music-emu/gme/Gbs_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Gbs_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Gme_File.cpp create mode 100644 thirdparty/game-music-emu/gme/Gme_File.h create mode 100644 thirdparty/game-music-emu/gme/Gym_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Gym_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Hes_Apu.cpp create mode 100644 thirdparty/game-music-emu/gme/Hes_Apu.h create mode 100644 thirdparty/game-music-emu/gme/Hes_Cpu.cpp create mode 100644 thirdparty/game-music-emu/gme/Hes_Cpu.h create mode 100644 thirdparty/game-music-emu/gme/Hes_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Hes_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Kss_Cpu.cpp create mode 100644 thirdparty/game-music-emu/gme/Kss_Cpu.h create mode 100644 thirdparty/game-music-emu/gme/Kss_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Kss_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Kss_Scc_Apu.cpp create mode 100644 thirdparty/game-music-emu/gme/Kss_Scc_Apu.h create mode 100644 thirdparty/game-music-emu/gme/M3u_Playlist.cpp create mode 100644 thirdparty/game-music-emu/gme/M3u_Playlist.h create mode 100644 thirdparty/game-music-emu/gme/Multi_Buffer.cpp create mode 100644 thirdparty/game-music-emu/gme/Multi_Buffer.h create mode 100644 thirdparty/game-music-emu/gme/Music_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Music_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Nes_Apu.cpp create mode 100644 thirdparty/game-music-emu/gme/Nes_Apu.h create mode 100644 thirdparty/game-music-emu/gme/Nes_Cpu.cpp create mode 100644 thirdparty/game-music-emu/gme/Nes_Cpu.h create mode 100644 thirdparty/game-music-emu/gme/Nes_Fme7_Apu.cpp create mode 100644 thirdparty/game-music-emu/gme/Nes_Fme7_Apu.h create mode 100644 thirdparty/game-music-emu/gme/Nes_Namco_Apu.cpp create mode 100644 thirdparty/game-music-emu/gme/Nes_Namco_Apu.h create mode 100644 thirdparty/game-music-emu/gme/Nes_Oscs.cpp create mode 100644 thirdparty/game-music-emu/gme/Nes_Oscs.h create mode 100644 thirdparty/game-music-emu/gme/Nes_Vrc6_Apu.cpp create mode 100644 thirdparty/game-music-emu/gme/Nes_Vrc6_Apu.h create mode 100644 thirdparty/game-music-emu/gme/Nsf_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Nsf_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Nsfe_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Nsfe_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Sap_Apu.cpp create mode 100644 thirdparty/game-music-emu/gme/Sap_Apu.h create mode 100644 thirdparty/game-music-emu/gme/Sap_Cpu.cpp create mode 100644 thirdparty/game-music-emu/gme/Sap_Cpu.h create mode 100644 thirdparty/game-music-emu/gme/Sap_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Sap_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Sms_Apu.cpp create mode 100644 thirdparty/game-music-emu/gme/Sms_Apu.h create mode 100644 thirdparty/game-music-emu/gme/Sms_Oscs.h create mode 100644 thirdparty/game-music-emu/gme/Snes_Spc.cpp create mode 100644 thirdparty/game-music-emu/gme/Snes_Spc.h create mode 100644 thirdparty/game-music-emu/gme/Spc_Cpu.cpp create mode 100644 thirdparty/game-music-emu/gme/Spc_Cpu.h create mode 100644 thirdparty/game-music-emu/gme/Spc_Dsp.cpp create mode 100644 thirdparty/game-music-emu/gme/Spc_Dsp.h create mode 100644 thirdparty/game-music-emu/gme/Spc_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Spc_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Spc_Filter.cpp create mode 100644 thirdparty/game-music-emu/gme/Spc_Filter.h create mode 100644 thirdparty/game-music-emu/gme/Vgm_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Vgm_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Vgm_Emu_Impl.cpp create mode 100644 thirdparty/game-music-emu/gme/Vgm_Emu_Impl.h create mode 100644 thirdparty/game-music-emu/gme/Ym2413_Emu.cpp create mode 100644 thirdparty/game-music-emu/gme/Ym2413_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Ym2612_Emu.h create mode 100644 thirdparty/game-music-emu/gme/Ym2612_GENS.cpp create mode 100644 thirdparty/game-music-emu/gme/Ym2612_GENS.h create mode 100644 thirdparty/game-music-emu/gme/Ym2612_MAME.cpp create mode 100644 thirdparty/game-music-emu/gme/Ym2612_MAME.h create mode 100644 thirdparty/game-music-emu/gme/Ym2612_Nuked.cpp create mode 100644 thirdparty/game-music-emu/gme/Ym2612_Nuked.h create mode 100644 thirdparty/game-music-emu/gme/blargg_common.h create mode 100644 thirdparty/game-music-emu/gme/blargg_config.h create mode 100644 thirdparty/game-music-emu/gme/blargg_endian.h create mode 100644 thirdparty/game-music-emu/gme/blargg_source.h create mode 100644 thirdparty/game-music-emu/gme/gb_cpu_io.h create mode 100644 thirdparty/game-music-emu/gme/gme.cpp create mode 100644 thirdparty/game-music-emu/gme/gme.h create mode 100644 thirdparty/game-music-emu/gme/gme_types.h create mode 100644 thirdparty/game-music-emu/gme/gme_types.h.in create mode 100644 thirdparty/game-music-emu/gme/hes_cpu_io.h create mode 100644 thirdparty/game-music-emu/gme/libgme.pc.in create mode 100644 thirdparty/game-music-emu/gme/nes_cpu_io.h create mode 100644 thirdparty/game-music-emu/gme/sap_cpu_io.h create mode 100644 thirdparty/game-music-emu/license.txt create mode 100644 thirdparty/game-music-emu/readme.txt create mode 100644 thirdparty/libxmp/CMakeLists.txt create mode 100644 thirdparty/libxmp/include/xmp.h create mode 100644 thirdparty/libxmp/src/callbackio.h create mode 100644 thirdparty/libxmp/src/common.h create mode 100644 thirdparty/libxmp/src/control.c create mode 100644 thirdparty/libxmp/src/dataio.c create mode 100644 thirdparty/libxmp/src/effects.c create mode 100644 thirdparty/libxmp/src/effects.h create mode 100644 thirdparty/libxmp/src/extras.c create mode 100644 thirdparty/libxmp/src/extras.h create mode 100644 thirdparty/libxmp/src/far_extras.c create mode 100644 thirdparty/libxmp/src/far_extras.h create mode 100644 thirdparty/libxmp/src/filetype.c create mode 100644 thirdparty/libxmp/src/filter.c create mode 100644 thirdparty/libxmp/src/format.c create mode 100644 thirdparty/libxmp/src/format.h create mode 100644 thirdparty/libxmp/src/hio.c create mode 100644 thirdparty/libxmp/src/hio.h create mode 100644 thirdparty/libxmp/src/hmn_extras.c create mode 100644 thirdparty/libxmp/src/hmn_extras.h create mode 100644 thirdparty/libxmp/src/lfo.c create mode 100644 thirdparty/libxmp/src/lfo.h create mode 100644 thirdparty/libxmp/src/list.h create mode 100644 thirdparty/libxmp/src/load.c create mode 100644 thirdparty/libxmp/src/load_helpers.c create mode 100644 thirdparty/libxmp/src/loaders/669_load.c create mode 100644 thirdparty/libxmp/src/loaders/abk_load.c create mode 100644 thirdparty/libxmp/src/loaders/amf_load.c create mode 100644 thirdparty/libxmp/src/loaders/arch_load.c create mode 100644 thirdparty/libxmp/src/loaders/asylum_load.c create mode 100644 thirdparty/libxmp/src/loaders/chip_load.c create mode 100644 thirdparty/libxmp/src/loaders/coco_load.c create mode 100644 thirdparty/libxmp/src/loaders/common.c create mode 100644 thirdparty/libxmp/src/loaders/dbm_load.c create mode 100644 thirdparty/libxmp/src/loaders/digi_load.c create mode 100644 thirdparty/libxmp/src/loaders/dt_load.c create mode 100644 thirdparty/libxmp/src/loaders/emod_load.c create mode 100644 thirdparty/libxmp/src/loaders/far_load.c create mode 100644 thirdparty/libxmp/src/loaders/flt_load.c create mode 100644 thirdparty/libxmp/src/loaders/fnk_load.c create mode 100644 thirdparty/libxmp/src/loaders/gal4_load.c create mode 100644 thirdparty/libxmp/src/loaders/gal5_load.c create mode 100644 thirdparty/libxmp/src/loaders/gdm_load.c create mode 100644 thirdparty/libxmp/src/loaders/hmn_load.c create mode 100644 thirdparty/libxmp/src/loaders/ice_load.c create mode 100644 thirdparty/libxmp/src/loaders/iff.c create mode 100644 thirdparty/libxmp/src/loaders/iff.h create mode 100644 thirdparty/libxmp/src/loaders/imf_load.c create mode 100644 thirdparty/libxmp/src/loaders/ims_load.c create mode 100644 thirdparty/libxmp/src/loaders/it.h create mode 100644 thirdparty/libxmp/src/loaders/it_load.c create mode 100644 thirdparty/libxmp/src/loaders/itsex.c create mode 100644 thirdparty/libxmp/src/loaders/liq_load.c create mode 100644 thirdparty/libxmp/src/loaders/loader.h create mode 100644 thirdparty/libxmp/src/loaders/lzw.c create mode 100644 thirdparty/libxmp/src/loaders/lzw.h create mode 100644 thirdparty/libxmp/src/loaders/masi16_load.c create mode 100644 thirdparty/libxmp/src/loaders/masi_load.c create mode 100644 thirdparty/libxmp/src/loaders/mdl_load.c create mode 100644 thirdparty/libxmp/src/loaders/med.h create mode 100644 thirdparty/libxmp/src/loaders/med2_load.c create mode 100644 thirdparty/libxmp/src/loaders/med3_load.c create mode 100644 thirdparty/libxmp/src/loaders/med4_load.c create mode 100644 thirdparty/libxmp/src/loaders/mfp_load.c create mode 100644 thirdparty/libxmp/src/loaders/mgt_load.c create mode 100644 thirdparty/libxmp/src/loaders/mmd1_load.c create mode 100644 thirdparty/libxmp/src/loaders/mmd3_load.c create mode 100644 thirdparty/libxmp/src/loaders/mmd_common.c create mode 100644 thirdparty/libxmp/src/loaders/mod.h create mode 100644 thirdparty/libxmp/src/loaders/mod_load.c create mode 100644 thirdparty/libxmp/src/loaders/mtm_load.c create mode 100644 thirdparty/libxmp/src/loaders/muse_load.c create mode 100644 thirdparty/libxmp/src/loaders/no_load.c create mode 100644 thirdparty/libxmp/src/loaders/okt_load.c create mode 100644 thirdparty/libxmp/src/loaders/pt3_load.c create mode 100644 thirdparty/libxmp/src/loaders/ptm_load.c create mode 100644 thirdparty/libxmp/src/loaders/pw_load.c create mode 100644 thirdparty/libxmp/src/loaders/rtm_load.c create mode 100644 thirdparty/libxmp/src/loaders/s3m.h create mode 100644 thirdparty/libxmp/src/loaders/s3m_load.c create mode 100644 thirdparty/libxmp/src/loaders/sample.c create mode 100644 thirdparty/libxmp/src/loaders/sfx_load.c create mode 100644 thirdparty/libxmp/src/loaders/st_load.c create mode 100644 thirdparty/libxmp/src/loaders/stim_load.c create mode 100644 thirdparty/libxmp/src/loaders/stm_load.c create mode 100644 thirdparty/libxmp/src/loaders/stx_load.c create mode 100644 thirdparty/libxmp/src/loaders/sym_load.c create mode 100644 thirdparty/libxmp/src/loaders/ult_load.c create mode 100644 thirdparty/libxmp/src/loaders/umx_load.c create mode 100644 thirdparty/libxmp/src/loaders/voltable.c create mode 100644 thirdparty/libxmp/src/loaders/vorbis.c create mode 100644 thirdparty/libxmp/src/loaders/vorbis.h create mode 100644 thirdparty/libxmp/src/loaders/xm.h create mode 100644 thirdparty/libxmp/src/loaders/xm_load.c create mode 100644 thirdparty/libxmp/src/loaders/xmf_load.c create mode 100644 thirdparty/libxmp/src/md5.c create mode 100644 thirdparty/libxmp/src/md5.h create mode 100644 thirdparty/libxmp/src/mdataio.h create mode 100644 thirdparty/libxmp/src/med_extras.c create mode 100644 thirdparty/libxmp/src/med_extras.h create mode 100644 thirdparty/libxmp/src/memio.c create mode 100644 thirdparty/libxmp/src/memio.h create mode 100644 thirdparty/libxmp/src/miniz.h create mode 100644 thirdparty/libxmp/src/misc.c create mode 100644 thirdparty/libxmp/src/mix_all.c create mode 100644 thirdparty/libxmp/src/mix_paula.c create mode 100644 thirdparty/libxmp/src/mixer.c create mode 100644 thirdparty/libxmp/src/mixer.h create mode 100644 thirdparty/libxmp/src/mkstemp.c create mode 100644 thirdparty/libxmp/src/paula.h create mode 100644 thirdparty/libxmp/src/period.c create mode 100644 thirdparty/libxmp/src/period.h create mode 100644 thirdparty/libxmp/src/player.c create mode 100644 thirdparty/libxmp/src/player.h create mode 100644 thirdparty/libxmp/src/precomp_blep.h create mode 100644 thirdparty/libxmp/src/precomp_lut.h create mode 100644 thirdparty/libxmp/src/read_event.c create mode 100644 thirdparty/libxmp/src/scan.c create mode 100644 thirdparty/libxmp/src/smix.c create mode 100644 thirdparty/libxmp/src/tempfile.c create mode 100644 thirdparty/libxmp/src/tempfile.h create mode 100644 thirdparty/libxmp/src/virtual.c create mode 100644 thirdparty/libxmp/src/virtual.h create mode 100644 thirdparty/libxmp/src/win32.c create mode 100644 thirdparty/miniz/CMakeLists.txt create mode 100644 thirdparty/miniz/miniz.c create mode 100644 thirdparty/miniz/miniz.h create mode 100644 thirdparty/mpg123.h create mode 100644 thirdparty/oplsynth/CMakeLists.txt create mode 100644 thirdparty/oplsynth/OPL3.cpp create mode 100644 thirdparty/oplsynth/dosbox/opl.cpp create mode 100644 thirdparty/oplsynth/dosbox/opl.h create mode 100644 thirdparty/oplsynth/fmopl.cpp create mode 100644 thirdparty/oplsynth/musicblock.cpp create mode 100644 thirdparty/oplsynth/nukedopl3.cpp create mode 100644 thirdparty/oplsynth/opl_mus_player.cpp create mode 100644 thirdparty/oplsynth/oplio.cpp create mode 100644 thirdparty/oplsynth/oplsynth/genmidi.h create mode 100644 thirdparty/oplsynth/oplsynth/musicblock.h create mode 100644 thirdparty/oplsynth/oplsynth/nukedopl3.h create mode 100644 thirdparty/oplsynth/oplsynth/o_swap.h create mode 100644 thirdparty/oplsynth/oplsynth/opl.h create mode 100644 thirdparty/oplsynth/oplsynth/opl3_Float.h create mode 100644 thirdparty/oplsynth/oplsynth/opl_mus_player.h create mode 100644 thirdparty/oplsynth/oplsynth/oplio.h create mode 100644 thirdparty/opnmidi/CMakeLists.txt create mode 100644 thirdparty/opnmidi/chips/chipset.cmake create mode 100644 thirdparty/opnmidi/chips/chipset.pri create mode 100644 thirdparty/opnmidi/chips/gens/Ym2612.cpp create mode 100644 thirdparty/opnmidi/chips/gens/Ym2612.hpp create mode 100644 thirdparty/opnmidi/chips/gens/Ym2612_p.hpp create mode 100644 thirdparty/opnmidi/chips/gens_opn2.cpp create mode 100644 thirdparty/opnmidi/chips/gens_opn2.h create mode 100644 thirdparty/opnmidi/chips/gx/gx_ym2612.c create mode 100644 thirdparty/opnmidi/chips/gx/gx_ym2612.h create mode 100644 thirdparty/opnmidi/chips/gx_opn2.cpp create mode 100644 thirdparty/opnmidi/chips/gx_opn2.h create mode 100644 thirdparty/opnmidi/chips/mame/mame_ym2612fm.c create mode 100644 thirdparty/opnmidi/chips/mame/mame_ym2612fm.h create mode 100644 thirdparty/opnmidi/chips/mame/mamedef.h create mode 100644 thirdparty/opnmidi/chips/mame_opn2.cpp create mode 100644 thirdparty/opnmidi/chips/mame_opn2.h create mode 100644 thirdparty/opnmidi/chips/mame_opna.cpp create mode 100644 thirdparty/opnmidi/chips/mame_opna.h create mode 100644 thirdparty/opnmidi/chips/mamefm/2608intf.h create mode 100644 thirdparty/opnmidi/chips/mamefm/emu.h create mode 100644 thirdparty/opnmidi/chips/mamefm/emu2149.c create mode 100644 thirdparty/opnmidi/chips/mamefm/emu2149.h create mode 100644 thirdparty/opnmidi/chips/mamefm/emutypes.h create mode 100644 thirdparty/opnmidi/chips/mamefm/fm.cpp create mode 100644 thirdparty/opnmidi/chips/mamefm/fm.h create mode 100644 thirdparty/opnmidi/chips/mamefm/fmopn_2608rom.h create mode 100644 thirdparty/opnmidi/chips/mamefm/resampler.cpp create mode 100644 thirdparty/opnmidi/chips/mamefm/resampler.hpp create mode 100644 thirdparty/opnmidi/chips/mamefm/ymdeltat.cpp create mode 100644 thirdparty/opnmidi/chips/mamefm/ymdeltat.h create mode 100644 thirdparty/opnmidi/chips/np2/LICENSE create mode 100644 thirdparty/opnmidi/chips/np2/compiler.h create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_changelog_opnmidi.txt create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_diag.h create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_file.cpp create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_file.h create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_fmgen.cpp create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_fmgen.h create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_fmgeninl.h create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_fmtimer.cpp create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_fmtimer.h create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_headers.h create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_misc.h create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_opna.cpp create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_opna.h create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_psg.cpp create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_psg.h create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_readme.txt create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_readme_kai.txt create mode 100644 thirdparty/opnmidi/chips/np2/fmgen_types.h create mode 100644 thirdparty/opnmidi/chips/np2_opna.cpp create mode 100644 thirdparty/opnmidi/chips/np2_opna.h create mode 100644 thirdparty/opnmidi/chips/nuked/ym3438.c create mode 100644 thirdparty/opnmidi/chips/nuked/ym3438.h create mode 100644 thirdparty/opnmidi/chips/nuked_opn2.cpp create mode 100644 thirdparty/opnmidi/chips/nuked_opn2.h create mode 100644 thirdparty/opnmidi/chips/opn_chip_base.h create mode 100644 thirdparty/opnmidi/chips/opn_chip_base.tcc create mode 100644 thirdparty/opnmidi/chips/opn_chip_family.h create mode 100644 thirdparty/opnmidi/chips/pmdwin/LICENSE create mode 100644 thirdparty/opnmidi/chips/pmdwin/op.h create mode 100644 thirdparty/opnmidi/chips/pmdwin/opna.c create mode 100644 thirdparty/opnmidi/chips/pmdwin/opna.h create mode 100644 thirdparty/opnmidi/chips/pmdwin/psg.c create mode 100644 thirdparty/opnmidi/chips/pmdwin/psg.h create mode 100644 thirdparty/opnmidi/chips/pmdwin/rhythmdata.c create mode 100644 thirdparty/opnmidi/chips/pmdwin/rhythmdata.h create mode 100644 thirdparty/opnmidi/chips/pmdwin_opna.cpp create mode 100644 thirdparty/opnmidi/chips/pmdwin_opna.h create mode 100644 thirdparty/opnmidi/chips/vgm_file_dumper.cpp create mode 100644 thirdparty/opnmidi/chips/vgm_file_dumper.h create mode 100644 thirdparty/opnmidi/chips/ymfm/README.md create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm.h create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_adpcm.cpp create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_adpcm.h create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_fm.h create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_fm.hpp create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_misc.cpp create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_misc.h create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_opn.cpp create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_opn.h create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_pcm.cpp create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_pcm.h create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_ssg.cpp create mode 100644 thirdparty/opnmidi/chips/ymfm/ymfm_ssg.h create mode 100644 thirdparty/opnmidi/chips/ymfm_opn2.cpp create mode 100644 thirdparty/opnmidi/chips/ymfm_opn2.h create mode 100644 thirdparty/opnmidi/chips/ymfm_opna.cpp create mode 100644 thirdparty/opnmidi/chips/ymfm_opna.h create mode 100644 thirdparty/opnmidi/file_reader.hpp create mode 100644 thirdparty/opnmidi/fraction.hpp create mode 100644 thirdparty/opnmidi/opnbank.h create mode 100644 thirdparty/opnmidi/opnmidi.cpp create mode 100644 thirdparty/opnmidi/opnmidi.h create mode 100644 thirdparty/opnmidi/opnmidi_bankmap.h create mode 100644 thirdparty/opnmidi/opnmidi_bankmap.tcc create mode 100644 thirdparty/opnmidi/opnmidi_cvt.hpp create mode 100644 thirdparty/opnmidi/opnmidi_load.cpp create mode 100644 thirdparty/opnmidi/opnmidi_midiplay.cpp create mode 100644 thirdparty/opnmidi/opnmidi_midiplay.hpp create mode 100644 thirdparty/opnmidi/opnmidi_opn2.cpp create mode 100644 thirdparty/opnmidi/opnmidi_opn2.hpp create mode 100644 thirdparty/opnmidi/opnmidi_private.cpp create mode 100644 thirdparty/opnmidi/opnmidi_private.hpp create mode 100644 thirdparty/opnmidi/opnmidi_ptr.hpp create mode 100644 thirdparty/opnmidi/structures/pl_list.hpp create mode 100644 thirdparty/opnmidi/structures/pl_list.tcc create mode 100644 thirdparty/opnmidi/wopn/wopn_file.c create mode 100644 thirdparty/opnmidi/wopn/wopn_file.h create mode 100644 thirdparty/sndfile.h create mode 100644 thirdparty/timidity/CHANGES create mode 100644 thirdparty/timidity/CMakeLists.txt create mode 100644 thirdparty/timidity/COPYING create mode 100644 thirdparty/timidity/FAQ create mode 100644 thirdparty/timidity/README create mode 100644 thirdparty/timidity/common.cpp create mode 100644 thirdparty/timidity/instrum.cpp create mode 100644 thirdparty/timidity/instrum_dls.cpp create mode 100644 thirdparty/timidity/instrum_font.cpp create mode 100644 thirdparty/timidity/instrum_sf2.cpp create mode 100644 thirdparty/timidity/mix.cpp create mode 100644 thirdparty/timidity/playmidi.cpp create mode 100644 thirdparty/timidity/resample.cpp create mode 100644 thirdparty/timidity/timidity.cpp create mode 100644 thirdparty/timidity/timidity/common.h create mode 100644 thirdparty/timidity/timidity/dls1.h create mode 100644 thirdparty/timidity/timidity/dls2.h create mode 100644 thirdparty/timidity/timidity/gf1patch.h create mode 100644 thirdparty/timidity/timidity/instrum.h create mode 100644 thirdparty/timidity/timidity/playmidi.h create mode 100644 thirdparty/timidity/timidity/sf2.h create mode 100644 thirdparty/timidity/timidity/t_swap.h create mode 100644 thirdparty/timidity/timidity/timidity.h create mode 100644 thirdparty/timidityplus/CMakeLists.txt create mode 100644 thirdparty/timidityplus/common.cpp create mode 100644 thirdparty/timidityplus/configfile.cpp create mode 100644 thirdparty/timidityplus/effect.cpp create mode 100644 thirdparty/timidityplus/fft4g.cpp create mode 100644 thirdparty/timidityplus/filter.cpp create mode 100644 thirdparty/timidityplus/freq.cpp create mode 100644 thirdparty/timidityplus/instrum.cpp create mode 100644 thirdparty/timidityplus/mblock.cpp create mode 100644 thirdparty/timidityplus/mix.cpp create mode 100644 thirdparty/timidityplus/playmidi.cpp create mode 100644 thirdparty/timidityplus/quantity.cpp create mode 100644 thirdparty/timidityplus/readmidic.cpp create mode 100644 thirdparty/timidityplus/recache.cpp create mode 100644 thirdparty/timidityplus/resample.cpp create mode 100644 thirdparty/timidityplus/reverb.cpp create mode 100644 thirdparty/timidityplus/sbkconv.cpp create mode 100644 thirdparty/timidityplus/sffile.cpp create mode 100644 thirdparty/timidityplus/sfitem.cpp create mode 100644 thirdparty/timidityplus/smplfile.cpp create mode 100644 thirdparty/timidityplus/sndfont.cpp create mode 100644 thirdparty/timidityplus/tables.cpp create mode 100644 thirdparty/timidityplus/timiditypp/common.h create mode 100644 thirdparty/timidityplus/timiditypp/controls.h create mode 100644 thirdparty/timidityplus/timiditypp/effect.h create mode 100644 thirdparty/timidityplus/timiditypp/fft4g.h create mode 100644 thirdparty/timidityplus/timiditypp/filter.h create mode 100644 thirdparty/timidityplus/timiditypp/freq.h create mode 100644 thirdparty/timidityplus/timiditypp/instrum.h create mode 100644 thirdparty/timidityplus/timiditypp/mblock.h create mode 100644 thirdparty/timidityplus/timiditypp/mix.h create mode 100644 thirdparty/timidityplus/timiditypp/optcode.h create mode 100644 thirdparty/timidityplus/timiditypp/playmidi.h create mode 100644 thirdparty/timidityplus/timiditypp/quantity.h create mode 100644 thirdparty/timidityplus/timiditypp/recache.h create mode 100644 thirdparty/timidityplus/timiditypp/resample.h create mode 100644 thirdparty/timidityplus/timiditypp/reverb.h create mode 100644 thirdparty/timidityplus/timiditypp/sffile.h create mode 100644 thirdparty/timidityplus/timiditypp/sfitem.h create mode 100644 thirdparty/timidityplus/timiditypp/sflayer.h create mode 100644 thirdparty/timidityplus/timiditypp/sysdep.h create mode 100644 thirdparty/timidityplus/timiditypp/t_swap.h create mode 100644 thirdparty/timidityplus/timiditypp/tables.h create mode 100644 thirdparty/timidityplus/timiditypp/timidity.h create mode 100644 thirdparty/wildmidi/CMakeLists.txt create mode 100644 thirdparty/wildmidi/file_io.cpp create mode 100644 thirdparty/wildmidi/gus_pat.cpp create mode 100644 thirdparty/wildmidi/reverb.cpp create mode 100644 thirdparty/wildmidi/wildmidi/common.h create mode 100644 thirdparty/wildmidi/wildmidi/file_io.h create mode 100644 thirdparty/wildmidi/wildmidi/gus_pat.h create mode 100644 thirdparty/wildmidi/wildmidi/reverb.h create mode 100644 thirdparty/wildmidi/wildmidi/wildmidi_lib.h create mode 100644 thirdparty/wildmidi/wildmidi/wm_error.h create mode 100644 thirdparty/wildmidi/wildmidi_lib.cpp create mode 100644 thirdparty/wildmidi/wm_error.cpp create mode 100644 vcpkg.json diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml new file mode 100644 index 000000000..1767b3767 --- /dev/null +++ b/.github/workflows/continuous_integration.yml @@ -0,0 +1,97 @@ +name: Continuous Integration + +on: [push, pull_request] + +jobs: + build: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + - name: Visual Studio - Release + os: windows-latest + build_type: Release + + - name: Visual Studio - Debug + os: windows-latest + build_type: Debug + + - name: macOS Clang - Dynamic Deps + os: macos-latest + build_type: Release + + - name: macOS Clang - Static Deps + os: macos-latest + build_type: Release + cmake_options: -DDYN_FLUIDSYNTH=OFF -DDYN_MPG123=OFF -DDYN_SNDFILE=OFF + deps_cmd: brew install libsndfile mpg123 + + - name: Linux GCC - Dynamic Deps + os: ubuntu-latest + build_type: Release + deps_cmd: sudo apt update && sudo apt install libglib2.0-dev + + - name: Linux GCC - Static Deps + os: ubuntu-latest + build_type: Release + cmake_options: -DDYN_FLUIDSYNTH=OFF -DDYN_MPG123=OFF -DDYN_SNDFILE=OFF + deps_cmd: sudo apt update && sudo apt install libasound2-dev libglib2.0-dev libmpg123-dev libsndfile1-dev + + - name: Linux Clang - Dynamic Deps + os: ubuntu-latest + build_type: Release + cmake_options: -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ + deps_cmd: sudo apt update && sudo apt install libglib2.0-dev + + - name: Linux Clang - Static Deps + os: ubuntu-latest + build_type: Release + cmake_options: -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DDYN_FLUIDSYNTH=OFF -DDYN_MPG123=OFF -DDYN_SNDFILE=OFF + deps_cmd: sudo apt update && sudo apt install libasound2-dev libglib2.0-dev libmpg123-dev libsndfile1-dev + + steps: + - uses: actions/checkout@v4 + + - name: Install Dependencies + shell: bash + run: | + ${{ matrix.config.deps_cmd }} + + - name: Configure + shell: bash + run: | + cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} -DCMAKE_INSTALL_PREFIX=./build_install ${{ matrix.config.cmake_options }} . + + - name: Build + shell: bash + run: | + export MAKEFLAGS=--keep-going + cmake --build build --target install --config ${{ matrix.config.build_type }} --parallel 3 + + - name: Test + shell: bash + run: | + cd samples/list_midi_devices + mkdir build + cd build + declare -x PREFIX=`pwd`/../../../build_install + cmake -DCMAKE_PREFIX_PATH=${PREFIX} ${{ matrix.config.cmake_options }} .. + cmake --build . --config ${{ matrix.config.build_type }} + if [[ "${{ runner.os }}" == 'macOS' ]]; then + declare -x DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:${PREFIX} + fi + if [[ "${{ runner.os }}" == 'Windows' ]]; then + cp ${PREFIX}/bin/zmusic.dll ${{ matrix.config.build_type }} + ${{ matrix.config.build_type }}/list_midi_devices.exe + else + ./list_midi_devices + fi + + - name: Upload Install Directory + if: false # Remove this line to upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.config.name }} + path: build_install diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ca0e5e282 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/build* +*.user* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..052881c48 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,212 @@ +cmake_minimum_required(VERSION 3.13...3.19) + +if (VCPKG_LIBSNDFILE) + list(APPEND VCPKG_MANIFEST_FEATURES "vcpkg-libsndfile") +endif() + +project(ZMusic + VERSION 1.2.0 + LANGUAGES C CXX +) + +if (VCPKG_TOOLCHAIN) + if(VCPKG_TARGET_TRIPLET MATCHES "-static$") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() + + option(VCPKG_LIBSNDFILE "Import libsndfile from vcpkg" OFF) +else() + set(VCPKG_MANIFEST_FEATURES) +endif() + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) + +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) +include(CheckCXXCompilerFlag) +include(ZUtility) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + # This project is being built standalone + + # Give user option to build shared or static + option(BUILD_SHARED_LIBS "Build shared libraries" ON) + + # Enable install rules + set(ZMUSIC_INSTALL ON) +else() + # This project is being vendored by another project, set option default if + # the parent project doesn't provide them. + + if(NOT DEFINED BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS ON) + endif() + + # Although install rules can be avoided with EXCLUDE_FROM_ALL on + # add_subdirectory, the EXPORT rules may place certain usage requirements on + # targets shared between the two projects. + if(NOT DEFINED ZMUSIC_INSTALL) + set(ZMUSIC_INSTALL OFF) + endif() +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug CACHE STRING + "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." + FORCE) +endif() + +if(MSVC AND CMAKE_VERSION VERSION_LESS 3.15) + message(WARNING "Some things may be misconfigured. Please update to CMake >= 3.15 with Visual Studio.") +endif() +if(NOT CMAKE_MSVC_RUNTIME_LIBRARY) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif() + +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "Install path prefix" FORCE) +endif() + +if(MSVC) + # optionally generate assembly output for checking crash locations. + option(ZMUSIC_GENERATE_ASM "Generate assembly output." OFF) + if(ZMUSIC_GENERATE_ASM) + add_compile_options("/FAcs") + endif() + + add_compile_options( + "/GF" # String pooling + "/Gy" # Function-level linking + "/GR-" # Disable run-time type information + "/permissive-" + "/Oy" "/Oi" "/GS-" + + # Disable warnings for unsecure CRT functions from VC8+ + "/wd4996" + ) + + add_compile_definitions( + "UNICODE" + "_UNICODE" + "_WIN32_WINNT=0x0600" + # Debug allocations in debug builds + $<$:_CRTDBG_MAP_ALLOC> + ) + + add_link_options( + "/opt:ref" # Eliminate unreferenced functions and data + "/opt:icf" # Perform identical COMDAT folding + "/nodefaultlib:msvcrt" + $<$,EXECUTABLE>:/TSAWARE> + "/LARGEADDRESSAWARE" + ) + + # RelWithDebInfo uses /Ob1 by default instead of Ob2 like Release + string(REPLACE "/Ob1 " "/Ob2 " CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ) + + # The CMake configurations set /GR by default, which conflict with our settings. + # CMake 3.20 fixes the need to do this + string(REPLACE " /GR" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ) + + check_cxx_compiler_flag("/std:c++14" COMPILER_SUPPORTS_CXX14) + if(COMPILER_SUPPORTS_CXX14) + set(FLAG_CPP14 "/std:c++14") + endif() + if(MSVC_VERSION LESS 1910) + unset(COMPILER_SUPPORTS_CXX14) # MSVC older than 2017 fails to build YMFM + endif() +else() + add_compile_options("-ffp-contract=off") + + if(APPLE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9") + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + # With standard Apple tools -stdlib=libc++ needs to be specified in order to get + # C++11 support using SDKs 10.7 and 10.8. + add_compile_options("-stdlib=libc++") + add_link_options("-stdlib=libc++") + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # If we're compiling with a custom GCC on the Mac (which we know since g++-4.2 doesn't support C++11) statically link libgcc. + add_compile_options("-static-libgcc") + endif() + endif() + + check_cxx_compiler_flag("-std=c++14" COMPILER_SUPPORTS_CXX14) + if(COMPILER_SUPPORTS_CXX14) + set(FLAG_CPP14 "-std=c++14") + endif() +endif() + +if(COMPILER_SUPPORTS_CXX14) + message("== Your C++ compiler supports C++14, YMFM emulators will be ENABLED") +else() + message("== Your C++ compiler does NOT supports C++14, YMFM emulators will be DISABLED") +endif() + +# Initialize our list of find_package dependencies for configure_package_config_file +set(ZMUSIC_PACKAGE_DEPENDENCIES "" CACHE INTERNAL "") + +if (WIN32 AND MINGW) + add_compile_definitions(-D_UNICODE -DUNICODE) + add_compile_definitions(-D__USE_MINGW_ANSI_STDIO=1) +endif() + +add_subdirectory(thirdparty) +add_subdirectory(source) + +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/ZMusicConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) +configure_package_config_file( + cmake/ZMusicConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/ZMusicConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake +) +if(ZMUSIC_INSTALL) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ZMusicConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/ZMusicConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ZMusic + COMPONENT devel + ) +endif() + +if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(CPACK_PACKAGE_CONTACT "First Last " CACHE STRING "Contact info for archive maintainer.") + set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "GZDoom's music system as a standalone library") + string(TOLOWER "${PROJECT_NAME}" CPACK_PACKAGE_NAME) + set(CPACK_PACKAGE_NAME "lib${CPACK_PACKAGE_NAME}") + # Use same prefix for packaging for consistency + set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + set(CPACK_STRIP_FILES ON) + set(CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) + set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) + set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH}) + + set(CPACK_COMPONENT_DEVEL_DEPENDS Full Lite) + + if(WIN32) + set(CPACK_GENERATOR "ZIP") + else() + set(CPACK_GENERATOR "DEB") + set(CPACK_DEB_COMPONENT_INSTALL ON) + set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS ON) + set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT") + set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://zdoom.org") + set(CPACK_DEBIAN_PACKAGE_SECTION "libs") + set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) + set(CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION ON) + set(CPACK_DEBIAN_COMPRESSION_TYPE "xz") + + set(CPACK_DEBIAN_FULL_PACKAGE_NAME "${CPACK_PACKAGE_NAME}${PROJECT_VERSION_MAJOR}") + set(CPACK_DEBIAN_LITE_PACKAGE_NAME "${CPACK_PACKAGE_NAME}lite${PROJECT_VERSION_MAJOR}") + + set(CPACK_DEBIAN_DEVEL_PACKAGE_NAME "${CPACK_PACKAGE_NAME}-dev") + set(CPACK_DEBIAN_DEVEL_PACKAGE_SECTION "libdevel") + endif() + + include(CPack) +endif() diff --git a/README.md b/README.md new file mode 100644 index 000000000..ae5ab4912 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# ZMusic +GZDoom's music system as a standalone library + +Welcome! This repository is a library for use with the projects [GZDoom](https://github.com/coelckers/GZDoom), [Raze](https://github.com/coelckers/Raze), and the newer [PrBoom+](https://github.com/coelckers/prboom-plus). + +Compile instructions are pretty simple for most systems. + +``` +git clone https://github.com/coelckers/ZMusic.git +mkdir ZMusic/build +cd ZMusic/build +cmake -DCMAKE_BUILD_TYPE=Release .. +cmake --build . +``` + +On Unix/Linux you may also supply `sudo make install` in the build folder to push the compiled library directly into the file system so that it can be found by the previously mentioned projects. diff --git a/cmake/FindFluidSynth.cmake b/cmake/FindFluidSynth.cmake new file mode 100644 index 000000000..96ed76f7d --- /dev/null +++ b/cmake/FindFluidSynth.cmake @@ -0,0 +1,38 @@ +# - Find fluidsynth +# Find the native fluidsynth includes and library +# +# FLUIDSYNTH_INCLUDE_DIR - where to find fluidsynth.h +# FLUIDSYNTH_LIBRARY - Path to fluidsynth library. +# FLUIDSYNTH_FOUND - True if fluidsynth found. + + +if(FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARY) + # Already in cache, be silent + set(FluidSynth_FIND_QUIETLY TRUE) +endif() + +if(NOT FLUIDSYNTH_INCLUDE_DIR) + find_path(FLUIDSYNTH_INCLUDE_DIR fluidsynth.h) +endif() + +if(NOT FLUIDSYNTH_LIBRARY) + find_library(FLUIDSYNTH_LIBRARY NAMES fluidsynth) +endif() + +# handle the QUIETLY and REQUIRED arguments and set FLUIDSYNTH_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FluidSynth DEFAULT_MSG FLUIDSYNTH_LIBRARY FLUIDSYNTH_INCLUDE_DIR) + +if(FLUIDSYNTH_FOUND) + add_library(libfluidsynth UNKNOWN IMPORTED) + set_target_properties(libfluidsynth + PROPERTIES + IMPORTED_LOCATION "${FLUIDSYNTH_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${FLUIDSYNTH_INCLUDE_DIR}" + ) + + # Legacy variables + set(FLUIDSYNTH_INCLUDE_DIRS ${FLUIDSYNTH_INCLUDE_DIR}) + set(FLUIDSYNTH_LIBRARIES ${FLUIDSYNTH_LIBRARY}) +endif() diff --git a/cmake/FindGME.cmake b/cmake/FindGME.cmake new file mode 100644 index 000000000..311aa9c03 --- /dev/null +++ b/cmake/FindGME.cmake @@ -0,0 +1,37 @@ +# - Find Game Music Emulation +# +# GME_INCLUDE_DIR - where to find gme.h +# GME_LIBRARY - Path to gme library. +# GME_FOUND - True if gme found. + + +if(GME_INCLUDE_DIR AND GME_LIBRARY) + # Already in cache, be silent + set(GME_FIND_QUIETLY TRUE) +endif() + +if(NOT GME_INCLUDE_DIR) + find_path(GME_INCLUDE_DIR gme.h) +endif() + +if(NOT GME_LIBRARY) + find_library(GME_LIBRARY NAMES gme) +endif() + +# handle the QUIETLY and REQUIRED arguments and set GME_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GME DEFAULT_MSG GME_LIBRARY GME_INCLUDE_DIR) + +if(GME_FOUND) + add_library(gme UNKNOWN IMPORTED) + set_target_properties(gme + PROPERTIES + IMPORTED_LOCATION "${GME_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${GME_INCLUDE_DIR}" + ) + + # Legacy variables + set(GME_INCLUDE_DIRS ${GME_INCLUDE_DIR}) + set(GME_LIBRARIES ${GME_LIBRARY}) +endif() diff --git a/cmake/FindMPG123.cmake b/cmake/FindMPG123.cmake new file mode 100644 index 000000000..c78cb5bda --- /dev/null +++ b/cmake/FindMPG123.cmake @@ -0,0 +1,43 @@ +# - Find mpg123 +# Find the native mpg123 includes and library +# +# MPG123_INCLUDE_DIR - where to find mpg123.h +# MPG123_LIBRARY - Path to mpg123 library. +# MPG123_FOUND - True if mpg123 found. + +if(MPG123_INCLUDE_DIR AND MPG123_LIBRARY) + # Already in cache, be silent + set(MPG123_FIND_QUIETLY TRUE) +endif(MPG123_INCLUDE_DIR AND MPG123_LIBRARY) + +if(NOT MPG123_INCLUDE_DIR) + find_path(MPG123_INCLUDE_DIR mpg123.h + PATHS "${MPG123_DIR}" + PATH_SUFFIXES include + ) +endif() + +if(NOT MPG123_LIBRARY) + find_library(MPG123_LIBRARY NAMES mpg123 mpg123-0 + PATHS "${MPG123_DIR}" + PATH_SUFFIXES lib + ) +endif() + +# handle the QUIETLY and REQUIRED arguments and set MPG123_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MPG123 DEFAULT_MSG MPG123_LIBRARY MPG123_INCLUDE_DIR) + +if(MPG123_FOUND) + add_library(mpg123 UNKNOWN IMPORTED) + set_target_properties(mpg123 + PROPERTIES + IMPORTED_LOCATION "${MPG123_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${MPG123_INCLUDE_DIR}" + ) + + # Legacy variables + set(MPG123_INCLUDE_DIRS ${MPG123_INCLUDE_DIR}) + set(MPG123_LIBRARIES ${MPG123_LIBRARY}) +endif() diff --git a/cmake/FindSndFile.cmake b/cmake/FindSndFile.cmake new file mode 100644 index 000000000..7ba322563 --- /dev/null +++ b/cmake/FindSndFile.cmake @@ -0,0 +1,42 @@ +# - Try to find SndFile +# Once done this will define +# +# SNDFILE_FOUND - system has SndFile +# SNDFILE_INCLUDE_DIRS - the SndFile include directory +# SNDFILE_LIBRARIES - Link these to use SndFile +# +# Copyright © 2006 Wengo +# Copyright © 2009 Guillaume Martres +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +if(NOT SNDFILE_INCLUDE_DIR) + find_path(SNDFILE_INCLUDE_DIR NAMES sndfile.h) +endif() + +if(NOT SNDFILE_LIBRARY) + find_library(SNDFILE_LIBRARY NAMES sndfile sndfile-1) +endif() + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set SNDFILE_FOUND to TRUE if +# all listed variables are TRUE +find_package_handle_standard_args(SndFile DEFAULT_MSG SNDFILE_LIBRARY SNDFILE_INCLUDE_DIR) + +if(SNDFILE_FOUND) + add_library(sndfile UNKNOWN IMPORTED GLOBAL) + set_target_properties(sndfile + PROPERTIES + IMPORTED_LOCATION "${SNDFILE_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SNDFILE_INCLUDE_DIR}" + ) + + add_library(SndFile::sndfile ALIAS sndfile) + + # Legacy variables + set(SNDFILE_INCLUDE_DIRS "${SNDFILE_INCLUDE_DIR}") + set(SNDFILE_LIBRARIES "${SNDFILE_LIBRARY}") +endif() diff --git a/cmake/ZMusicConfig.cmake.in b/cmake/ZMusicConfig.cmake.in new file mode 100644 index 000000000..7c589fdbe --- /dev/null +++ b/cmake/ZMusicConfig.cmake.in @@ -0,0 +1,19 @@ +@PACKAGE_INIT@ + +set(_supported_components "Full;Lite") + +if(NOT ZMusic_FIND_COMPONENTS) + set(ZMusic_FIND_COMPONENTS "${_supported_components}") +endif() + +include(CMakeFindDependencyMacro) +foreach(_module @ZMUSIC_PACKAGE_DEPENDENCIES@) + find_dependency(${_module}) +endforeach() + +foreach(_comp ${ZMusic_FIND_COMPONENTS}) + include("${CMAKE_CURRENT_LIST_DIR}/ZMusic${_comp}Targets.cmake") + set(ZMusic_${_comp}_FOUND TRUE) +endforeach() + +check_required_components(ZMusic) diff --git a/cmake/ZUtility.cmake b/cmake/ZUtility.cmake new file mode 100644 index 000000000..907601766 --- /dev/null +++ b/cmake/ZUtility.cmake @@ -0,0 +1,137 @@ +include_guard(DIRECTORY) + +include(CheckFunctionExists) + +# BEGIN: Variables + +set(COMPILER_ID_GNU_COMPATIBLE "AppleClang;Clang;GNU") + +# Replacement variables for a possible long list of C/C++ compilers compatible with GCC +if(CMAKE_C_COMPILER_ID IN_LIST COMPILER_ID_GNU_COMPATIBLE) + set(COMPILER_IS_GNUC_COMPATIBLE TRUE) +else() + set(COMPILER_IS_GNUC_COMPATIBLE FALSE) +endif() + +if(CMAKE_CXX_COMPILER_ID IN_LIST COMPILER_ID_GNU_COMPATIBLE) + set(COMPILER_IS_GNUCXX_COMPATIBLE TRUE) +else() + set(COMPILER_IS_GNUCXX_COMPATIBLE FALSE) +endif() + +# BEGIN: Functions + +function(determine_package_config_dependency) + # Don't need to worry about this when building shared libraries + if(BUILD_SHARED_LIBS) + return() + endif() + + set(Dest "${ARGV0}") + cmake_parse_arguments(PARSE_ARGV 1 ARG "" "TARGET;MODULE" "") + + if(TARGET ${ARG_TARGET}) + get_property(TgtImported TARGET ${ARG_TARGET} PROPERTY IMPORTED) + if(TgtImported) + list(APPEND "${Dest}" "${ARG_MODULE}") + endif() + endif() + + set("${Dest}" "${${Dest}}" CACHE INTERNAL "") +endfunction() + +# Adds private links to a given target, but any OBJECT or INTERFACE library will +# be hidden from install(EXPORT). +function(target_link_libraries_hidden Tgt) + set(Links ${ARGV}) + list(REMOVE_AT Links 0) + + get_property(TgtType TARGET "${Tgt}" PROPERTY TYPE) + + foreach(Link IN LISTS Links) + if(TARGET "${Link}") + get_property(LinkImported TARGET "${Link}" PROPERTY IMPORTED) + get_property(LinkType TARGET "${Link}" PROPERTY TYPE) + + if(NOT LinkImported AND (LinkType STREQUAL "OBJECT_LIBRARY" OR LinkType STREQUAL "INTERFACE_LIBRARY")) + target_link_libraries("${Tgt}" PRIVATE "$") + + # Since we're potentially hiding usage requirements from the + # exported targets, we need to do this recursively. + if(TgtType STREQUAL "STATIC_LIBRARY") + get_property(TransitiveLinks TARGET "${Link}" PROPERTY INTERFACE_LINK_LIBRARIES) + target_link_libraries_hidden("${Tgt}" ${TransitiveLinks}) + endif() + else() + target_link_libraries("${Tgt}" PRIVATE "${Link}") + endif() + else() + target_link_libraries("${Tgt}" PRIVATE "${Link}") + endif() + endforeach() +endfunction() + +# For a given subdirectory, set all configs to release. +function(make_release_only) + if(CMAKE_CONFIGURATION_TYPES) + set(ConfigTypes ${CMAKE_CONFIGURATION_TYPES}) + list(REMOVE_ITEM ConfigTypes "Release") + else() + set(ConfigTypes "Debug;MinSizeRel;RelWithDebInfo") + endif() + + foreach(Config IN LISTS ConfigTypes) + string(TOUPPER "${Config}" ConfigUpper) + set("CMAKE_C_FLAGS_${ConfigUpper}" "${CMAKE_C_FLAGS_RELEASE}" PARENT_SCOPE) + endforeach() +endfunction() + +# As documented, OBJECT libraries in INTERFACE_LINK_LIBRARIES are treated as if +# they were INTERFACE libraries. So if we want to have an INTERFACE library +# which links to OBJECT libraries we need to copy those objects into the +# INTERFACE_SOURCES. +# +# This function should only be used on INTERFACE_LIBRARY targets. +function(propagate_object_links Tgt) + get_property(Links TARGET "${Tgt}" PROPERTY INTERFACE_LINK_LIBRARIES) + + foreach(Link IN LISTS Links) + if(TARGET "${Link}") + get_property(LinkType TARGET "${Link}" PROPERTY TYPE) + + if(LinkType STREQUAL "OBJECT_LIBRARY") + target_sources("${Tgt}" INTERFACE $) + endif() + endif() + endforeach() +endfunction() + +function(require_stricmp Tgt Visibility) + check_function_exists(stricmp STRICMP_EXISTS) + if(NOT STRICMP_EXISTS) + target_compile_definitions(${Tgt} ${Visibility} stricmp=strcasecmp) + endif() +endfunction() + +function(require_strnicmp Tgt Visibility) + check_function_exists(strnicmp STRNICMP_EXISTS) + if(NOT STRNICMP_EXISTS) + target_compile_definitions(${Tgt} ${Visibility} strnicmp=strncasecmp) + endif() +endfunction() + +function(use_fast_math) + foreach(Tgt IN LISTS ARGN) + if(TARGET Tgt) + set(TgtType TARGET) + else() + set(TgtType SOURCE) + endif() + + if(MSVC) + set_property("${TgtType}" "${Tgt}" APPEND PROPERTY COMPILE_OPTIONS "/fp:fast") + elseif(COMPILER_IS_GNUCXX_COMPATIBLE) + set_property("${TgtType}" "${Tgt}" APPEND PROPERTY COMPILE_OPTIONS "-ffast-math" "-ffp-contract=fast") + endif() + endforeach() +endfunction() diff --git a/include/zmusic.h b/include/zmusic.h new file mode 100644 index 000000000..627555f5d --- /dev/null +++ b/include/zmusic.h @@ -0,0 +1,448 @@ +#ifndef __ZMUSIC_H_ +#define __ZMUSIC_H_ + +#include +#include +#include + +struct SoundDecoder; // Anonymous to the client. + +typedef unsigned char zmusic_bool; + +// These constants must match the corresponding values of the Windows headers +// to avoid readjustment in the native Windows device's playback functions +// and should not be changed. +typedef enum EMidiDeviceClass_ +{ + MIDIDEV_MIDIPORT = 1, + MIDIDEV_SYNTH, + MIDIDEV_SQSYNTH, + MIDIDEV_FMSYNTH, + MIDIDEV_MAPPER, + MIDIDEV_WAVETABLE, + MIDIDEV_SWSYNTH +} EMidiDeviceClass; + +typedef enum EMIDIType_ +{ + MIDI_NOTMIDI, + MIDI_MIDI, + MIDI_HMI, + MIDI_XMI, + MIDI_MUS, + MIDI_MIDS +} EMIDIType; + +typedef enum EMidiDevice_ +{ + MDEV_DEFAULT = -1, + MDEV_STANDARD = 0, + MDEV_OPL = 1, + MDEV_SNDSYS = 2, + MDEV_TIMIDITY = 3, + MDEV_FLUIDSYNTH = 4, + MDEV_GUS = 5, + MDEV_WILDMIDI = 6, + MDEV_ADL = 7, + MDEV_OPN = 8, + + MDEV_COUNT +} EMidiDevice; + +typedef enum ESoundFontTypes_ +{ + SF_SF2 = 1, + SF_GUS = 2, + SF_WOPL = 4, + SF_WOPN = 8 +} ESoundFontTypes; + +typedef struct SoundStreamInfo_ +{ + int mBufferSize; // If mBufferSize is 0, the song doesn't use streaming but plays through a different interface. + int mSampleRate; + int mNumChannels; // If mNumChannels is negative, 16 bit integer format is used instead of floating point. +} SoundStreamInfo; + +typedef enum SampleType_ +{ + SampleType_UInt8, + SampleType_Int16, + SampleType_Float32 +} SampleType; + +typedef enum ChannelConfig_ +{ + ChannelConfig_Mono, + ChannelConfig_Stereo +} ChannelConfig; + +typedef struct SoundStreamInfoEx_ +{ + int mBufferSize; // If mBufferSize is 0, the song doesn't use streaming but plays through a different interface. + int mSampleRate; + SampleType mSampleType; + ChannelConfig mChannelConfig; +} SoundStreamInfoEx; + +// NOTE: The following enums are part of the ABI! Do not insert new values in the middle - always append them at the end! +typedef enum EIntConfigKey_ +{ + zmusic_adl_chips_count, + zmusic_adl_emulator_id, + zmusic_adl_run_at_pcm_rate, + zmusic_adl_fullpan, + zmusic_adl_bank, + zmusic_adl_use_custom_bank, + zmusic_adl_volume_model, + + zmusic_fluid_reverb, + zmusic_fluid_chorus, + zmusic_fluid_voices, + zmusic_fluid_interp, + zmusic_fluid_samplerate, + zmusic_fluid_threads, + zmusic_fluid_chorus_voices, + zmusic_fluid_chorus_type, + + zmusic_opl_numchips, + zmusic_opl_core, + zmusic_opl_fullpan, + + zmusic_opn_chips_count, + zmusic_opn_emulator_id, + zmusic_opn_run_at_pcm_rate, + zmusic_opn_fullpan, + zmusic_opn_use_custom_bank, + + zmusic_gus_dmxgus, + zmusic_gus_midi_voices, + zmusic_gus_memsize, + + zmusic_timidity_modulation_wheel, + zmusic_timidity_portamento, + zmusic_timidity_reverb, + zmusic_timidity_reverb_level, + zmusic_timidity_chorus, + zmusic_timidity_surround_chorus, + zmusic_timidity_channel_pressure, + zmusic_timidity_lpf_def, + zmusic_timidity_temper_control, + zmusic_timidity_modulation_envelope, + zmusic_timidity_overlap_voice_allow, + zmusic_timidity_drum_effect, + zmusic_timidity_pan_delay, + zmusic_timidity_key_adjust, + + zmusic_wildmidi_reverb, + zmusic_wildmidi_enhanced_resampling, + + zmusic_snd_midiprecache, + + zmusic_mod_samplerate, + zmusic_mod_volramp, + zmusic_mod_interp, + zmusic_mod_autochip, + zmusic_mod_autochip_size_force, + zmusic_mod_autochip_size_scan, + zmusic_mod_autochip_scan_threshold, + + zmusic_snd_streambuffersize, + + zmusic_snd_mididevice, + zmusic_snd_outputrate, + zmusic_mod_preferredplayer, + + // new constants since 1.2.0 + zmusic_adl_use_genmidi, + zmusic_adl_chan_alloc, + zmusic_adl_auto_arpeggio, + zmusic_opn_volume_model, + zmusic_opn_chan_alloc, + zmusic_opn_auto_arpeggio, + + NUM_ZMUSIC_INT_CONFIGS +} EIntConfigKey; + +typedef enum EFloatConfigKey_ +{ + zmusic_fluid_gain = 1000, + zmusic_fluid_reverb_roomsize, + zmusic_fluid_reverb_damping, + zmusic_fluid_reverb_width, + zmusic_fluid_reverb_level, + zmusic_fluid_chorus_level, + zmusic_fluid_chorus_speed, + zmusic_fluid_chorus_depth, + + zmusic_timidity_drum_power, + zmusic_timidity_tempo_adjust, + zmusic_timidity_min_sustain_time, + + zmusic_gme_stereodepth, + zmusic_mod_dumb_mastervolume, + + zmusic_snd_musicvolume, + zmusic_relative_volume, + zmusic_snd_mastervolume, + + // new constants since 1.2.0 + zmusic_opl_gain, + zmusic_adl_gain, + zmusic_opn_gain, + + NUM_FLOAT_CONFIGS +} EFloatConfigKey; + +typedef enum EStringConfigKey_ +{ + zmusic_adl_custom_bank = 2000, + zmusic_fluid_lib, + zmusic_fluid_patchset, + zmusic_opn_custom_bank, + zmusic_gus_config, + zmusic_gus_patchdir, + zmusic_timidity_config, + zmusic_wildmidi_config, + + NUM_STRING_CONFIGS +} EStringConfigKey; + + +typedef struct ZMusicCustomReader_ +{ + void* handle; + char* (*gets)(struct ZMusicCustomReader_* handle, char* buff, int n); + long (*read)(struct ZMusicCustomReader_* handle, void* buff, int32_t size); + long (*seek)(struct ZMusicCustomReader_* handle, long offset, int whence); + long (*tell)(struct ZMusicCustomReader_* handle); + void (*close)(struct ZMusicCustomReader_* handle); +} ZMusicCustomReader; + +typedef struct ZMusicMidiOutDevice_ +{ + char *Name; + int ID; + int Technology; +} ZMusicMidiOutDevice; + +typedef enum EZMusicMessageSeverity_ +{ + ZMUSIC_MSG_VERBOSE = 1, + ZMUSIC_MSG_DEBUG = 5, + ZMUSIC_MSG_NOTIFY = 10, + ZMUSIC_MSG_WARNING = 50, + ZMUSIC_MSG_ERROR = 100, + ZMUSIC_MSG_FATAL = 666, +} EZMusicMessageSeverity; + +typedef struct ZMusicCallbacks_ +{ + // Callbacks the client can install to capture messages from the backends + // or to provide sound font data. + + void (*MessageFunc)(int severity, const char* msg); + // The message callbacks are optional, without them the output goes to stdout. + + // Retrieves the path to a soundfont identified by an identifier. Only needed if the client virtualizes the sound font names + const char *(*PathForSoundfont)(const char *name, int type); + + // The sound font callbacks are for allowing the client to customize sound font management and they are optional. + // They only need to be defined if the client virtualizes the sound font management and doesn't pass real paths to the music code. + // Without them only paths to real files can be used. If one of these gets set, all must be set. + + // This opens a sound font. Must return a handle with which the sound font's content can be read. + void *(*OpenSoundFont)(const char* name, int type); + + // Opens a file in the sound font. For GUS patch sets this will try to open each patch with this function. + // For other formats only the sound font's actual name can be requested. + // When passed NULL this must open the Timidity config file, if this is requested for an SF2 sound font it should be synthesized. + ZMusicCustomReader* (*SF_OpenFile)(void* handle, const char* fn); + + //Adds a path to the list of directories in which files must be looked for. + void (*SF_AddToSearchPath)(void* handle, const char* path); + + // Closes the sound font reader. + void (*SF_Close)(void* handle); + + // Used to handle client-specific path macros. If not set, the path may not contain any special tokens that may need expansion. + const char *(*NicePath)(const char* path); +} ZMusicCallbacks; + +typedef enum ZMusicVariableType_ +{ + ZMUSIC_VAR_INT, + ZMUSIC_VAR_BOOL, + ZMUSIC_VAR_FLOAT, + ZMUSIC_VAR_STRING, +} ZMusicVariableType; + +typedef struct ZMusicConfigurationSetting_ +{ + const char* name; + int identifier; + ZMusicVariableType type; + float defaultVal; + const char* defaultString; +} ZMusicConfigurationSetting; + + +#ifndef ZMUSIC_INTERNAL +#if defined(_MSC_VER) && !defined(ZMUSIC_STATIC) +#define DLL_IMPORT _declspec(dllimport) +#else +#define DLL_IMPORT +#endif +// Note that the internal 'class' definitions are not C compatible! +typedef struct _ZMusic_MidiSource_Struct { int zm1; } *ZMusic_MidiSource; +typedef struct _ZMusic_MusicStream_Struct { int zm2; } *ZMusic_MusicStream; +struct SoundDecoder; +#endif + +#ifndef ZMUSIC_NO_PROTOTYPES +#ifdef __cplusplus +extern "C" +{ +#endif + DLL_IMPORT const char* ZMusic_GetLastError(); + + // Sets callbacks for functionality that the client needs to provide. + DLL_IMPORT void ZMusic_SetCallbacks(const ZMusicCallbacks* callbacks); + // Sets GenMidi data for OPL playback. If this isn't provided the OPL synth will not work. + DLL_IMPORT void ZMusic_SetGenMidi(const uint8_t* data); + // Set default bank for OPN. Without this OPN only works with custom banks. + DLL_IMPORT void ZMusic_SetWgOpn(const void* data, unsigned len); + // Set DMXGUS data for running the GUS synth in actual GUS mode. + DLL_IMPORT void ZMusic_SetDmxGus(const void* data, unsigned len); + + // Returns an array with all available configuration options - terminated with an empty entry where all elements are 0. + DLL_IMPORT const ZMusicConfigurationSetting* ZMusic_GetConfiguration(); + + // These exports are needed by the MIDI dumpers which need to remain on the client side because the need access to the client's file system. + DLL_IMPORT EMIDIType ZMusic_IdentifyMIDIType(uint32_t* id, int size); + DLL_IMPORT ZMusic_MidiSource ZMusic_CreateMIDISource(const uint8_t* data, size_t length, EMIDIType miditype); + DLL_IMPORT zmusic_bool ZMusic_MIDIDumpWave(ZMusic_MidiSource source, EMidiDevice devtype, const char* devarg, const char* outname, int subsong, int samplerate); + + DLL_IMPORT ZMusic_MusicStream ZMusic_OpenSong(ZMusicCustomReader* reader, EMidiDevice device, const char* Args); + DLL_IMPORT ZMusic_MusicStream ZMusic_OpenSongFile(const char *filename, EMidiDevice device, const char* Args); + DLL_IMPORT ZMusic_MusicStream ZMusic_OpenSongMem(const void *mem, size_t size, EMidiDevice device, const char* Args); + DLL_IMPORT ZMusic_MusicStream ZMusic_OpenCDSong(int track, int cdid); + + DLL_IMPORT zmusic_bool ZMusic_FillStream(ZMusic_MusicStream stream, void* buff, int len); + DLL_IMPORT zmusic_bool ZMusic_Start(ZMusic_MusicStream song, int subsong, zmusic_bool loop); + DLL_IMPORT void ZMusic_Pause(ZMusic_MusicStream song); + DLL_IMPORT void ZMusic_Resume(ZMusic_MusicStream song); + DLL_IMPORT void ZMusic_Update(ZMusic_MusicStream song); + DLL_IMPORT zmusic_bool ZMusic_IsPlaying(ZMusic_MusicStream song); + DLL_IMPORT void ZMusic_Stop(ZMusic_MusicStream song); + DLL_IMPORT void ZMusic_Close(ZMusic_MusicStream song); + DLL_IMPORT zmusic_bool ZMusic_SetSubsong(ZMusic_MusicStream song, int subsong); + DLL_IMPORT zmusic_bool ZMusic_IsLooping(ZMusic_MusicStream song); + DLL_IMPORT int ZMusic_GetDeviceType(ZMusic_MusicStream song); + DLL_IMPORT zmusic_bool ZMusic_IsMIDI(ZMusic_MusicStream song); + DLL_IMPORT void ZMusic_VolumeChanged(ZMusic_MusicStream song); + DLL_IMPORT zmusic_bool ZMusic_WriteSMF(ZMusic_MidiSource source, const char* fn, int looplimit); + DLL_IMPORT void ZMusic_GetStreamInfo(ZMusic_MusicStream song, SoundStreamInfo *info); + DLL_IMPORT void ZMusic_GetStreamInfoEx(ZMusic_MusicStream song, SoundStreamInfoEx *info); + // Configuration interface. The return value specifies if a music restart is needed. + // RealValue should be written back to the CVAR or whatever other method the client uses to store configuration state. + DLL_IMPORT zmusic_bool ChangeMusicSettingInt(EIntConfigKey key, ZMusic_MusicStream song, int value, int* pRealValue); + DLL_IMPORT zmusic_bool ChangeMusicSettingFloat(EFloatConfigKey key, ZMusic_MusicStream song, float value, float* pRealValue); + DLL_IMPORT zmusic_bool ChangeMusicSettingString(EStringConfigKey key, ZMusic_MusicStream song, const char* value); + DLL_IMPORT const char *ZMusic_GetStats(ZMusic_MusicStream song); + + + DLL_IMPORT struct SoundDecoder* CreateDecoder(const uint8_t* data, size_t size, zmusic_bool isstatic); + DLL_IMPORT void SoundDecoder_GetInfo(struct SoundDecoder* decoder, int* samplerate, ChannelConfig* chans, SampleType* type); + DLL_IMPORT size_t SoundDecoder_Read(struct SoundDecoder* decoder, void* buffer, size_t length); + DLL_IMPORT void SoundDecoder_Close(struct SoundDecoder* decoder); + DLL_IMPORT void FindLoopTags(const uint8_t* data, size_t size, uint32_t* start, zmusic_bool* startass, uint32_t* end, zmusic_bool* endass); + // The rest of the decoder interface is only useful for streaming music. + + DLL_IMPORT const ZMusicMidiOutDevice *ZMusic_GetMidiDevices(int *pAmount); + DLL_IMPORT int ZMusic_GetADLBanks(const char* const** pNames); + + // Direct access to the CD drive. + // Stops playing the CD + DLL_IMPORT void CD_Stop(); + + // Pauses CD playing + DLL_IMPORT void CD_Pause(); + + // Resumes CD playback after pausing + DLL_IMPORT zmusic_bool CD_Resume(); + + // Eject the CD tray + DLL_IMPORT void CD_Eject(); + + // Close the CD tray + DLL_IMPORT zmusic_bool CD_UnEject(); + + // Closes a CD device previously opened with CD_Init + DLL_IMPORT void CD_Close(); + + DLL_IMPORT zmusic_bool CD_Enable(const char* drive); + + +#ifdef __cplusplus +} + +inline bool ChangeMusicSetting(EIntConfigKey key, ZMusic_MusicStream song, int value, int* pRealValue = NULL) +{ + return ChangeMusicSettingInt(key, song, value, pRealValue); +} +inline bool ChangeMusicSetting(EFloatConfigKey key, ZMusic_MusicStream song, float value, float* pRealValue = NULL) +{ + return ChangeMusicSettingFloat(key, song, value, pRealValue); +} +inline bool ChangeMusicSetting(EStringConfigKey key, ZMusic_MusicStream song, const char* value) +{ + return ChangeMusicSettingString(key, song, value); +} + +#endif +#endif + +// Function typedefs for run-time linking +typedef const char* (*pfn_ZMusic_GetLastError)(); +typedef void (*pfn_ZMusic_SetCallbacks)(const ZMusicCallbacks* callbacks); +typedef void (*pfn_ZMusic_SetGenMidi)(const uint8_t* data); +typedef void (*pfn_ZMusic_SetWgOpn)(const void* data, unsigned len); +typedef void (*pfn_ZMusic_SetDmxGus)(const void* data, unsigned len); +typedef const ZMusicConfigurationSetting* (*pfn_ZMusic_GetConfiguration)(); +typedef EMIDIType (*pfn_ZMusic_IdentifyMIDIType)(uint32_t* id, int size); +typedef ZMusic_MidiSource (*pfn_ZMusic_CreateMIDISource)(const uint8_t* data, size_t length, EMIDIType miditype); +typedef zmusic_bool (*pfn_ZMusic_MIDIDumpWave)(ZMusic_MidiSource source, EMidiDevice devtype, const char* devarg, const char* outname, int subsong, int samplerate); +typedef ZMusic_MusicStream (*pfn_ZMusic_OpenSong)(ZMusicCustomReader* reader, EMidiDevice device, const char* Args); +typedef ZMusic_MusicStream (*pfn_ZMusic_OpenSongFile)(const char *filename, EMidiDevice device, const char* Args); +typedef ZMusic_MusicStream (*pfn_ZMusic_OpenSongMem)(const void *mem, size_t size, EMidiDevice device, const char* Args); +typedef ZMusic_MusicStream (*pfn_ZMusic_OpenCDSong)(int track, int cdid); +typedef zmusic_bool (*pfn_ZMusic_FillStream)(ZMusic_MusicStream stream, void* buff, int len); +typedef zmusic_bool (*pfn_ZMusic_Start)(ZMusic_MusicStream song, int subsong, zmusic_bool loop); +typedef void (*pfn_ZMusic_Pause)(ZMusic_MusicStream song); +typedef void (*pfn_ZMusic_Resume)(ZMusic_MusicStream song); +typedef void (*pfn_ZMusic_Update)(ZMusic_MusicStream song); +typedef zmusic_bool (*pfn_ZMusic_IsPlaying)(ZMusic_MusicStream song); +typedef void (*pfn_ZMusic_Stop)(ZMusic_MusicStream song); +typedef void (*pfn_ZMusic_Close)(ZMusic_MusicStream song); +typedef zmusic_bool (*pfn_ZMusic_SetSubsong)(ZMusic_MusicStream song, int subsong); +typedef zmusic_bool (*pfn_ZMusic_IsLooping)(ZMusic_MusicStream song); +typedef zmusic_bool (*pfn_ZMusic_IsMIDI)(ZMusic_MusicStream song); +typedef void (*pfn_ZMusic_VolumeChanged)(ZMusic_MusicStream song); +typedef zmusic_bool (*pfn_ZMusic_WriteSMF)(ZMusic_MidiSource source, const char* fn, int looplimit); +typedef void (*pfn_ZMusic_GetStreamInfo)(ZMusic_MusicStream song, SoundStreamInfo *info); +typedef void (*pfn_ZMusic_GetStreamInfoEx)(ZMusic_MusicStream song, SoundStreamInfoEx *info); +typedef zmusic_bool (*pfn_ChangeMusicSettingInt)(EIntConfigKey key, ZMusic_MusicStream song, int value, int* pRealValue); +typedef zmusic_bool (*pfn_ChangeMusicSettingFloat)(EFloatConfigKey key, ZMusic_MusicStream song, float value, float* pRealValue); +typedef zmusic_bool (*pfn_ChangeMusicSettingString)(EStringConfigKey key, ZMusic_MusicStream song, const char* value); +typedef const char *(*pfn_ZMusic_GetStats)(ZMusic_MusicStream song); +typedef struct SoundDecoder* (*pfn_CreateDecoder)(const uint8_t* data, size_t size, zmusic_bool isstatic); +typedef void (*pfn_SoundDecoder_GetInfo)(struct SoundDecoder* decoder, int* samplerate, ChannelConfig* chans, SampleType* type); +typedef size_t (*pfn_SoundDecoder_Read)(struct SoundDecoder* decoder, void* buffer, size_t length); +typedef void (*pfn_SoundDecoder_Close)(struct SoundDecoder* decoder); +typedef void (*pfn_FindLoopTags)(const uint8_t* data, size_t size, uint32_t* start, zmusic_bool* startass, uint32_t* end, zmusic_bool* endass); +typedef const ZMusicMidiOutDevice *(*pfn_ZMusic_GetMidiDevices)(int *pAmount); + + + +#endif diff --git a/licenses/bsd.txt b/licenses/bsd.txt new file mode 100644 index 000000000..aa48716a9 --- /dev/null +++ b/licenses/bsd.txt @@ -0,0 +1,27 @@ +**--------------------------------------------------------------------------- +** Copyright 1998-2009 Randy Heit, Christoph Oelckers, et al. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- \ No newline at end of file diff --git a/licenses/dumb.txt b/licenses/dumb.txt new file mode 100644 index 000000000..961fe4ef8 --- /dev/null +++ b/licenses/dumb.txt @@ -0,0 +1,87 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * licence.txt - Conditions for use of DUMB. / / \ \ + * | < / \_ + * If you do not agree to these terms, please | \/ /\ / + * do not use DUMB. \_ / > / + * | \ / / + * Information in [brackets] is provided to aid | ' / + * interpretation of the licence. \__/ + */ + + +Dynamic Universal Music Bibliotheque, Version 0.9.3 + +Copyright (C) 2001-2005 Ben Davis, Robert J Ohannessian and Julien Cugniere + +This software is provided 'as-is', without any express or implied warranty. +In no event shall the authors be held liable for any damages arising from the +use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a + product, you are requested to acknowledge its use in the product + documentation, along with details on where to get an unmodified version of + this software, but this is not a strict requirement. + + [Note that the above point asks for a link to DUMB, not just a mention. + Googling for DUMB doesn't help much! The URL is "http://dumb.sf.net/".] + + [The link was originally strictly required. This was changed for two + reasons. Firstly, if many projects request an acknowledgement, the list of + acknowledgements can become quite unmanageable. Secondly, DUMB was placing + a restriction on the code using it, preventing people from using the GNU + General Public Licence which disallows any such restrictions. See + http://www.gnu.org/philosophy/bsd.html for more information on this + subject. However, if DUMB plays a significant part in your project, we do + urge you to acknowledge its use.] + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed from or altered in any source distribution. + +4. If you are using the Program in someone else's bedroom on any Monday at + 3:05 pm, you are not allowed to modify the Program for ten minutes. [This + clause provided by Inphernic; every licence should contain at least one + clause, the reasoning behind which is far from obvious.] + +5. Users who wish to use DUMB for the specific purpose of playing music are + required to feed their dog on every full moon (if deemed appropriate). + [This clause provided by Allefant, who couldn't remember what Inphernic's + clause was.] + +6. No clause in this licence shall prevent this software from being depended + upon by a product licensed under the GNU General Public Licence. If such a + clause is deemed to exist, Debian, then it shall be respected in spirit as + far as possible and all other clauses shall continue to apply in full + force. + +8. Take the number stated as introducing this clause. Multiply it by two, + then subtract four. Now insert a '+' between the two digits and evaluate + the resulting sum. Call the result 'x'. If you have not yet concluded that + every numbered clause in this licence whose ordinal number is strictly + greater than 'x' (with the exception of the present clause) is null and + void, Debian, then you are hereby informed that laughter is good for one's + health and you are warmly suggested to do it. By the way, Clauses 4, 5 and + 6 are null and void. Incidentally, I like Kubuntu. The work you guys do is + awesome. (Lawyers, on the other hand ...) + +We regret that we cannot provide any warranty, not even the implied warranty +of merchantability or fitness for a particular purpose. + +Some files generated or copied by automake, autoconf and friends are +available in an extra download. These fall under separate licences but are +all free to distribute. Please check their licences as necessary. diff --git a/licenses/gplv3.txt b/licenses/gplv3.txt new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/licenses/gplv3.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/licenses/legal.txt b/licenses/legal.txt new file mode 100644 index 000000000..fafd836ed --- /dev/null +++ b/licenses/legal.txt @@ -0,0 +1,25 @@ +ZMusic is licensed under the GPLv3. + +The majority of original code uses a BSD-like lincese. See bsd.txt. + +libADL and libOPN are licensed under the GPLv3. + +WildMidi is licensed under the LGPLv3. See lgplv3.txt + +This software uses the 'zlib' general purpose compression library by +Jean-loup Gailly and Mark Adler. + +This software uses the game_music_emu library, which is covered by the GNU Lesser +General Public License. See lgplv21.txt. + +This software uses the "Dynamic Universal Music Bibliotheque" library for +MOD music playback. See dumb.txt for original license. The version used, +however, has been heavily modified from its original form and is the same +version used by the foobar2000 component foo_dumb as of mid-2008, found at +http://kode54.foobar2000.org/. + + +ZMusic Lite is a feature reduced version that omits all content that is not +compatible with the LGPL v2.1. Use this if your project cannot comply with the GPL. +This means that most of the available MIDI synths, except FluidSynth, have been removed +and that the playback of native OPL file types is disabled. diff --git a/licenses/lgplv21.txt b/licenses/lgplv21.txt new file mode 100644 index 000000000..8000a6faa --- /dev/null +++ b/licenses/lgplv21.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/licenses/lgplv3.txt b/licenses/lgplv3.txt new file mode 100644 index 000000000..be2c3ea8e --- /dev/null +++ b/licenses/lgplv3.txt @@ -0,0 +1,841 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/licenses/zmusic.txt b/licenses/zmusic.txt new file mode 100644 index 000000000..31a763218 --- /dev/null +++ b/licenses/zmusic.txt @@ -0,0 +1,6 @@ +ZMusic comes in two flavors: full and lite. +The difference between these two is that the lite version omits all playback code that is licensed under the GPL so that it can be used in projects incompatible with the GPL v3. +Omitted features in the lite version are: All MIDI synths except for FluidSynth were removed. In addition raw OPL playback had to be removed. + +The full version of zmusic can be used under the terms of the GPL v3. See gplv3.txt for details. +The lite version can be used under the terms of the LGPL v2.1. See lgplv21.txt for details. diff --git a/samples/list_midi_devices/.gitignore b/samples/list_midi_devices/.gitignore new file mode 100644 index 000000000..0ec9e7f98 --- /dev/null +++ b/samples/list_midi_devices/.gitignore @@ -0,0 +1 @@ +/build* diff --git a/samples/list_midi_devices/CMakeLists.txt b/samples/list_midi_devices/CMakeLists.txt new file mode 100644 index 000000000..3f692d96d --- /dev/null +++ b/samples/list_midi_devices/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8.7...3.19) +project(list_midi_devices) + +find_package(ZMusic REQUIRED) + +add_executable(list_midi_devices list_midi_devices.cpp) +target_link_libraries(list_midi_devices PRIVATE ZMusic::zmusic) diff --git a/samples/list_midi_devices/list_midi_devices.cpp b/samples/list_midi_devices/list_midi_devices.cpp new file mode 100644 index 000000000..34e2e6400 --- /dev/null +++ b/samples/list_midi_devices/list_midi_devices.cpp @@ -0,0 +1,27 @@ +#include +#include + +int main() +{ + int count = 0; + const ZMusicMidiOutDevice *devices = ZMusic_GetMidiDevices(&count); + + for (int i = 0; i < count; ++i) + { + const ZMusicMidiOutDevice &d = devices[i]; + const char *tech = ""; + switch (d.Technology) + { + case MIDIDEV_MIDIPORT: tech = "MIDIPORT"; break; + case MIDIDEV_SYNTH: tech = "SYNTH"; break; + case MIDIDEV_SQSYNTH: tech = "SQSYNTH"; break; + case MIDIDEV_FMSYNTH: tech = "FMSYNTH"; break; + case MIDIDEV_MAPPER: tech = "MAPPER"; break; + case MIDIDEV_WAVETABLE: tech = "WAVETABLE"; break; + case MIDIDEV_SWSYNTH: tech = "SWSYNTH"; break; + } + printf("[%i] %i. %s: %s\n", i, d.ID, d.Name, tech); + } + + return 0; +} diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt new file mode 100644 index 000000000..9e669e39f --- /dev/null +++ b/source/CMakeLists.txt @@ -0,0 +1,224 @@ +# zmusic-obj doesn't actually build anything itself, but rather all sources are +# added as interface sources. Thus whatever links to zmusic-obj will be in +# charge of compiling. As a result any properties set on zmusic-obj should be +# interface. +add_library(zmusic-obj INTERFACE) +target_sources(zmusic-obj +INTERFACE + loader/i_module.cpp + mididevices/music_base_mididevice.cpp + mididevices/music_adlmidi_mididevice.cpp + mididevices/music_opl_mididevice.cpp + mididevices/music_opnmidi_mididevice.cpp + mididevices/music_timiditypp_mididevice.cpp + mididevices/music_fluidsynth_mididevice.cpp + mididevices/music_softsynth_mididevice.cpp + mididevices/music_timidity_mididevice.cpp + mididevices/music_wildmidi_mididevice.cpp + mididevices/music_wavewriter_mididevice.cpp + midisources/midisource.cpp + midisources/midisource_mus.cpp + midisources/midisource_smf.cpp + midisources/midisource_hmi.cpp + midisources/midisource_xmi.cpp + midisources/midisource_mids.cpp + streamsources/music_dumb.cpp + streamsources/music_gme.cpp + streamsources/music_libsndfile.cpp + streamsources/music_libxmp.cpp + streamsources/music_opl.cpp + streamsources/music_xa.cpp + musicformats/music_stream.cpp + musicformats/music_midi.cpp + musicformats/music_cd.cpp + decoder/sounddecoder.cpp + decoder/sndfile_decoder.cpp + decoder/mpg123_decoder.cpp + zmusic/configuration.cpp + zmusic/zmusic.cpp + zmusic/critsec.cpp + loader/test.c +) + +file(GLOB HEADER_FILES + zmusic/*.h + loader/*.h + mididevices/*.h + midisources/*.h + musicformats/*.h + musicformats/win32/*.h + decoder/*.h + streamsources/*.h + ../thirdparty/*.h + ../include/*.h +) +target_sources(zmusic-obj INTERFACE ${HEADER_FILES}) + +target_compile_features(zmusic-obj INTERFACE cxx_std_11) +#set_target_properties(zmusic-obj PROPERTIES LINKER_LANGUAGE CXX) + +require_stricmp(zmusic-obj INTERFACE) +require_strnicmp(zmusic-obj INTERFACE) + +if(NOT WIN32 AND NOT APPLE) + find_package(Threads) + target_link_libraries(zmusic-obj INTERFACE Threads::Threads) + determine_package_config_dependency(ZMUSIC_PACKAGE_DEPENDENCIES TARGET Threads::Threads MODULE Threads) +endif() + +if ("vcpkg-libsndfile" IN_LIST VCPKG_MANIFEST_FEATURES) + set(DYN_SNDFILE 0) +else() + option(DYN_SNDFILE "Dynamically load libsndfile" ON) +endif() + +if(DYN_SNDFILE) + target_compile_definitions(zmusic-obj INTERFACE HAVE_SNDFILE DYN_SNDFILE) +else() + find_package(SndFile) + + if(SNDFILE_FOUND) + target_compile_definitions(zmusic-obj INTERFACE HAVE_SNDFILE) + target_link_libraries(zmusic-obj INTERFACE SndFile::sndfile) + determine_package_config_dependency(ZMUSIC_PACKAGE_DEPENDENCIES TARGET SndFile::sndfile MODULE SndFile) + endif() +endif() + +if ("vcpkg-libsndfile" IN_LIST VCPKG_MANIFEST_FEATURES) + set(DYN_MPG123 0) +else() + option(DYN_MPG123 "Dynamically load libmpg123" ON) +endif() +if(DYN_MPG123) + target_compile_definitions(zmusic-obj INTERFACE HAVE_MPG123 DYN_MPG123) +elseif(NOT ("vcpkg-libsndfile" IN_LIST VCPKG_MANIFEST_FEATURES)) + find_package(MPG123) + + if(MPG123_FOUND) + target_compile_definitions(zmusic-obj INTERFACE HAVE_MPG123) + target_link_libraries(zmusic-obj INTERFACE mpg123) + determine_package_config_dependency(ZMUSIC_PACKAGE_DEPENDENCIES TARGET mpg123 MODULE MPG123) + endif() +endif() + +# System MIDI support +if(WIN32) + target_compile_definitions(zmusic-obj INTERFACE HAVE_SYSTEM_MIDI) + target_link_libraries(zmusic-obj INTERFACE winmm) + target_sources(zmusic-obj INTERFACE mididevices/music_win_mididevice.cpp) +elseif(NOT APPLE) + find_package(ALSA) + if(ALSA_FOUND) + target_compile_definitions(zmusic-obj INTERFACE HAVE_SYSTEM_MIDI) + target_sources(zmusic-obj + INTERFACE + mididevices/music_alsa_mididevice.cpp + mididevices/music_alsa_state.cpp + ) + target_link_libraries(zmusic-obj INTERFACE ALSA::ALSA) + determine_package_config_dependency(ZMUSIC_PACKAGE_DEPENDENCIES TARGET ALSA::ALSA MODULE ALSA) + endif() +endif() + +if(WIN32) + target_sources(zmusic-obj + INTERFACE + musicformats/win32/i_cd.cpp + musicformats/win32/helperthread.cpp + ) +endif() + +target_link_libraries(zmusic-obj INTERFACE dumb gme libxmp miniz ${CMAKE_DL_LIBS}) + +target_include_directories(zmusic-obj +INTERFACE + ../include + ${CMAKE_CURRENT_SOURCE_DIR} + zmusic +) + +propagate_object_links(zmusic-obj) + +add_library(zmusic) +add_library(ZMusic::zmusic ALIAS zmusic) +add_library(zmusiclite) +add_library(ZMusic::zmusiclite ALIAS zmusiclite) + +use_fast_math(zmusic) +use_fast_math(zmusiclite) + +# Although zmusic-obj puts the public include directory in our private include +# list, we need to add it to the interface include directories for consumers. +target_include_directories(zmusic INTERFACE $ $) +target_include_directories(zmusiclite INTERFACE $ $) + +target_link_libraries_hidden(zmusic zmusic-obj adl oplsynth opn timidity timidityplus wildmidi fluidsynth) +target_link_libraries_hidden(zmusiclite zmusic-obj fluidsynth) + +target_compile_definitions(zmusic PUBLIC $<$,STATIC_LIBRARY>:ZMUSIC_STATIC>) +target_compile_definitions(zmusiclite PRIVATE ZMUSIC_LITE=1 PUBLIC $<$,STATIC_LIBRARY>:ZMUSIC_STATIC>) + +set_target_properties(zmusic zmusiclite +PROPERTIES + MACOSX_RPATH ON + PUBLIC_HEADER ../include/zmusic.h + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} +) + +if (VCPKG_TOOLCHAIN) + x_vcpkg_install_local_dependencies(TARGETS zmusic zmusiclite DESTINATION ".") +endif() + +if(ZMUSIC_INSTALL) + install(TARGETS zmusic EXPORT ZMusicFullTargets + PUBLIC_HEADER + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT devel + LIBRARY + DESTINATION "${CMAKE_INSTALL_LIBDIR}" + COMPONENT full + NAMELINK_COMPONENT devel + ) + + install(TARGETS zmusiclite EXPORT ZMusicLiteTargets + PUBLIC_HEADER + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT devel + LIBRARY + DESTINATION "${CMAKE_INSTALL_LIBDIR}" + COMPONENT lite + NAMELINK_COMPONENT devel + ) + + install(EXPORT ZMusicFullTargets + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ZMusic" + NAMESPACE ZMusic:: + COMPONENT devel + ) + + install(EXPORT ZMusicLiteTargets + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ZMusic" + NAMESPACE ZMusic:: + COMPONENT devel + ) +endif() + +if( MSVC ) + option( ZMUSIC_GENERATE_MAPFILE "Generate .map file for debugging." OFF ) + + if( ZMUSIC_GENERATE_MAPFILE ) + target_link_options(zmusic PRIVATE "/MAP") + target_link_options(zmusiclite PRIVATE "/MAP") + endif() +endif() + +source_group("MIDI Devices" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/mididevices/.+") +source_group("MIDI Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/midisources/.+") +source_group("Music Formats" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/musicformats/.+") +source_group("Music Formats\\Win32" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/musicformats/win32/.+") +source_group("ZMusic Core" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/zmusic/.+") +source_group("Sound Decoding" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/decoder/.+") +source_group("Stream Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/streamsources/.+") +source_group("Third Party" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/.+") +source_group("Public Interface" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/../include/.+") diff --git a/source/data/xg.h b/source/data/xg.h new file mode 100644 index 000000000..9fa720660 --- /dev/null +++ b/source/data/xg.h @@ -0,0 +1,15527 @@ +/*===============================================================* + This file is automatically generated by wopn2hpp.sh script + PLEASE DON'T EDIT THIS DIRECTLY. Edit the xg.wopn file first, + and then run a wopn2hpp tool to generate this file again + *===============================================================*/ + +static unsigned char xg_default[] = +{ + 0x57, 0x4f, 0x50, 0x4e, 0x32, 0x2d, 0x42, 0x32, 0x4e, 0x4b, 0x00, 0x02, + 0x00, 0x00, 0x0a, 0x00, 0x0b, 0x09, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, + 0x72, 0x64, 0x20, 0x3a, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x47, 0x20, 0x53, 0x46, 0x58, 0x20, 0x23, + 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x58, 0x47, + 0x20, 0x23, 0x30, 0x30, 0x31, 0x20, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x4b, + 0x69, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x47, 0x20, 0x23, + 0x30, 0x34, 0x39, 0x20, 0x53, 0x79, 0x6d, 0x70, 0x68, 0x4b, 0x69, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x58, 0x47, 0x20, 0x23, 0x30, 0x32, + 0x36, 0x20, 0x41, 0x6e, 0x61, 0x6c, 0x67, 0x4b, 0x69, 0x74, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x19, 0x00, 0x58, 0x47, 0x20, 0x23, 0x30, 0x32, 0x35, 0x20, + 0x45, 0x6c, 0x63, 0x74, 0x72, 0x4b, 0x69, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x58, 0x47, 0x20, 0x23, 0x30, 0x30, 0x32, 0x20, 0x53, 0x74, + 0x6e, 0x64, 0x4b, 0x69, 0x74, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x2a, 0x20, 0x47, 0x72, 0x61, 0x6e, 0x64, 0x50, 0x69, 0x61, 0x6e, 0x6f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x01, 0x27, 0x5a, 0x07, 0x04, 0x71, 0x00, 0x64, 0x24, 0x58, 0x09, + 0x09, 0x67, 0x00, 0x72, 0x04, 0xdf, 0x17, 0x0f, 0x91, 0x00, 0x31, 0x02, + 0x9b, 0x04, 0x04, 0xa6, 0x00, 0x4f, 0x45, 0x01, 0xbe, 0x2a, 0x20, 0x42, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x50, 0x69, 0x61, 0x6e, 0x6f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x24, 0x21, + 0x5b, 0x09, 0x05, 0xb6, 0x00, 0x54, 0x12, 0x5b, 0x09, 0x09, 0x77, 0x00, + 0x21, 0x08, 0x5b, 0x85, 0x05, 0xa6, 0x00, 0x56, 0x09, 0x5b, 0x09, 0x08, + 0x37, 0x00, 0x3d, 0x11, 0x01, 0xd9, 0x2a, 0x20, 0x45, 0x6c, 0x65, 0x63, + 0x74, 0x72, 0x69, 0x63, 0x50, 0x69, 0x61, 0x6e, 0x6f, 0x20, 0x28, 0x41, + 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x71, 0x21, 0x58, 0x05, 0x06, + 0x64, 0x00, 0x42, 0x1a, 0x56, 0x07, 0x06, 0x64, 0x00, 0x01, 0x06, 0x99, + 0x05, 0x04, 0x15, 0x00, 0x31, 0x09, 0x99, 0x84, 0x04, 0x65, 0x00, 0x37, + 0x99, 0x03, 0xe1, 0x2a, 0x20, 0x48, 0x6f, 0x6e, 0x6b, 0x79, 0x54, 0x6f, + 0x6e, 0x6b, 0x20, 0x28, 0x53, 0x6f, 0x6e, 0x69, 0x63, 0x4d, 0x6f, 0x64, + 0x64, 0x65, 0x64, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x32, 0x23, 0x1f, 0x0a, 0x05, 0xf7, 0x00, 0x02, + 0x0e, 0x19, 0x06, 0x06, 0x27, 0x00, 0x04, 0x07, 0x19, 0x07, 0x00, 0xd7, + 0x00, 0x31, 0x10, 0x19, 0x09, 0x00, 0x27, 0x00, 0x9c, 0x40, 0x00, 0x71, + 0x2a, 0x20, 0x52, 0x68, 0x6f, 0x64, 0x65, 0x73, 0x50, 0x69, 0x61, 0x6e, + 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, + 0x00, 0x4c, 0x39, 0x5f, 0x07, 0x00, 0xb8, 0x00, 0x41, 0x22, 0x96, 0x05, + 0x00, 0xb8, 0x00, 0x01, 0x09, 0x9f, 0x84, 0x04, 0x18, 0x00, 0x01, 0x09, + 0x9f, 0x84, 0x04, 0x68, 0x00, 0x53, 0x26, 0x00, 0xa6, 0x2a, 0x20, 0x43, + 0x68, 0x6f, 0x72, 0x75, 0x73, 0x50, 0x69, 0x61, 0x6e, 0x6f, 0x28, 0x54, + 0x69, 0x6e, 0x69, 0x54, 0x6f, 0x6f, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x7a, 0x1e, + 0x1f, 0x0a, 0x07, 0xf6, 0x00, 0x51, 0x0a, 0x59, 0x05, 0x02, 0xf6, 0x00, + 0x32, 0x0c, 0x1f, 0x0d, 0x00, 0xf8, 0x00, 0x11, 0x14, 0x19, 0x0a, 0x02, + 0xf6, 0x00, 0x3b, 0x45, 0x01, 0xfa, 0x2a, 0x20, 0x48, 0x61, 0x72, 0x70, + 0x73, 0x69, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x20, 0x28, 0x41, 0x64, 0x61, + 0x6d, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x39, 0x00, 0x16, 0x26, 0x9f, 0x80, 0x01, + 0x05, 0x00, 0x50, 0x23, 0xdf, 0x80, 0x00, 0x02, 0x00, 0x3a, 0x21, 0xdf, + 0x00, 0x01, 0x06, 0x00, 0x70, 0x06, 0x1f, 0x06, 0x06, 0x47, 0x00, 0x31, + 0x7a, 0x01, 0x18, 0x2a, 0x20, 0x43, 0x6c, 0x61, 0x76, 0x69, 0x6e, 0x65, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x39, 0x00, 0x11, 0x1c, 0x9f, 0x80, 0x01, 0x06, 0x00, 0x51, + 0x1e, 0xdf, 0x80, 0x00, 0x02, 0x00, 0x31, 0x21, 0x9f, 0x00, 0x01, 0x07, + 0x00, 0x71, 0x06, 0x1f, 0x06, 0x06, 0x47, 0x00, 0x2e, 0xf4, 0x01, 0x11, + 0x2a, 0x20, 0x43, 0x65, 0x6c, 0x65, 0x73, 0x74, 0x61, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x13, 0x08, 0x53, 0x01, 0x1b, 0x40, 0x00, 0x3b, 0x22, 0x1d, 0x0f, + 0x1f, 0xf8, 0x00, 0x71, 0x11, 0x5b, 0x07, 0x1a, 0x06, 0x00, 0x71, 0x0b, + 0x1f, 0x00, 0x09, 0x06, 0x00, 0x10, 0xcc, 0x02, 0x30, 0x2a, 0x20, 0x47, + 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x6e, 0x73, 0x70, 0x69, 0x65, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x17, 0x1e, + 0x1f, 0x07, 0x00, 0xf2, 0x00, 0x35, 0x33, 0x1f, 0x0b, 0x00, 0xf6, 0x00, + 0x71, 0x00, 0x9f, 0x07, 0x00, 0xf3, 0x00, 0x71, 0x0f, 0x9f, 0x07, 0x00, + 0xf3, 0x00, 0x1d, 0x8e, 0x0e, 0x81, 0x2a, 0x20, 0x4d, 0x75, 0x73, 0x69, + 0x63, 0x42, 0x6f, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x30, 0x17, 0x1e, 0x0f, 0x0e, 0x00, + 0xf7, 0x00, 0x3a, 0x33, 0x0f, 0x0e, 0x00, 0xf5, 0x00, 0x71, 0x00, 0x1f, + 0x0c, 0x00, 0xf6, 0x00, 0x70, 0x0f, 0x1f, 0x0c, 0x00, 0xf6, 0x00, 0x06, + 0x7c, 0x02, 0x1c, 0x2a, 0x20, 0x56, 0x69, 0x62, 0x72, 0x61, 0x70, 0x68, + 0x6f, 0x6e, 0x65, 0x20, 0x28, 0x4e, 0x69, 0x6e, 0x65, 0x6b, 0x6f, 0x29, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x20, 0x38, 0x28, 0x59, 0x0f, 0x06, 0xa6, 0x00, 0x7a, + 0x23, 0x5f, 0x10, 0x06, 0x66, 0x00, 0x01, 0x04, 0x59, 0x86, 0x05, 0xa6, + 0x00, 0x34, 0x04, 0x9c, 0x0a, 0x05, 0x65, 0x00, 0x33, 0xb8, 0x03, 0x26, + 0x2a, 0x20, 0x4d, 0x61, 0x72, 0x69, 0x6d, 0x62, 0x61, 0x20, 0x28, 0x41, + 0x6c, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x45, 0x1b, 0x1f, 0x93, 0x07, 0x7e, 0x00, 0x22, 0x28, 0x9f, 0x92, + 0x04, 0x82, 0x00, 0x31, 0x00, 0x1e, 0x0f, 0x07, 0xf7, 0x00, 0x01, 0x0b, + 0x1f, 0x0f, 0x02, 0xf7, 0x00, 0x02, 0x65, 0x01, 0x1e, 0x2a, 0x20, 0x58, + 0x79, 0x6c, 0x61, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x20, 0x28, 0x41, 0x6c, + 0x61, 0x64, 0x64, 0x69, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x70, 0x00, + 0x40, 0x9d, 0x1f, 0x9f, 0x00, 0x63, 0x1f, 0x5d, 0x0f, 0x1f, 0xff, 0x00, + 0x70, 0x00, 0x5f, 0x80, 0x18, 0x0f, 0x00, 0x70, 0x00, 0x1f, 0x00, 0x0c, + 0x06, 0x00, 0x06, 0x75, 0x02, 0x30, 0x2a, 0x20, 0x54, 0x75, 0x62, 0x75, + 0x6c, 0x61, 0x72, 0x42, 0x65, 0x6c, 0x6c, 0x73, 0x20, 0x28, 0x41, 0x6e, + 0x69, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xf4, 0x00, 0x2c, 0x00, 0x77, 0x1e, 0x16, 0x05, 0x1e, + 0xf4, 0x00, 0x37, 0x1e, 0x16, 0x06, 0x1e, 0x84, 0x00, 0x32, 0x08, 0x18, + 0x03, 0x1f, 0xc4, 0x00, 0x72, 0x08, 0x13, 0x02, 0x1f, 0x94, 0x00, 0x81, + 0xb0, 0x04, 0x74, 0x2a, 0x20, 0x44, 0x75, 0x6c, 0x63, 0x69, 0x6d, 0x65, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x16, 0x00, 0x03, 0x17, 0x4f, 0x0b, 0x06, 0xf5, 0x00, 0x0a, + 0x2c, 0x53, 0x0e, 0x06, 0x46, 0x00, 0x01, 0x03, 0x98, 0x08, 0x00, 0xf5, + 0x00, 0x01, 0x09, 0x96, 0x08, 0x00, 0xf3, 0x00, 0x14, 0x6a, 0x08, 0x4e, + 0x2a, 0x20, 0x48, 0x61, 0x6d, 0x6d, 0x6f, 0x6e, 0x64, 0x20, 0x28, 0x4e, + 0x69, 0x6e, 0x65, 0x6b, 0x6f, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, + 0x00, 0x14, 0x11, 0x5c, 0x00, 0x04, 0xfa, 0x00, 0x51, 0x0f, 0x5c, 0x00, + 0x04, 0xf8, 0x00, 0x30, 0x17, 0x5c, 0x00, 0x1b, 0xf8, 0x00, 0x62, 0x17, + 0x5c, 0x00, 0x04, 0xfa, 0x00, 0x9c, 0x40, 0x00, 0xa0, 0x2a, 0x20, 0x50, + 0x65, 0x72, 0x63, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4f, 0x72, 0x67, 0x61, + 0x69, 0x6e, 0x20, 0x28, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x35, 0x32, + 0x29, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x04, 0x32, 0x0e, 0x15, + 0x1b, 0x17, 0x0b, 0xb8, 0x00, 0x11, 0x11, 0x15, 0x1c, 0x05, 0x2f, 0x00, + 0x06, 0x07, 0x14, 0x09, 0x00, 0x28, 0x00, 0x01, 0x0b, 0x17, 0x01, 0x1f, + 0xaf, 0x00, 0x9c, 0x40, 0x00, 0x7e, 0x2a, 0x20, 0x52, 0x6f, 0x63, 0x6b, + 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xf4, 0x00, 0x24, 0x14, 0x13, 0x19, 0x54, 0x8c, 0x00, + 0x2a, 0x00, 0x01, 0x18, 0x94, 0x80, 0x00, 0x0a, 0x00, 0x01, 0x07, 0x94, + 0x00, 0x00, 0x0a, 0x00, 0x52, 0x0a, 0x94, 0x00, 0x00, 0x0a, 0x00, 0x9c, + 0x40, 0x00, 0x56, 0x2a, 0x20, 0x43, 0x68, 0x75, 0x72, 0x63, 0x68, 0x4f, + 0x72, 0x67, 0x61, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x16, 0x00, 0x13, 0x1b, 0x94, 0x0a, 0x00, 0x23, 0x00, 0x04, + 0x06, 0x13, 0x0a, 0x00, 0x29, 0x00, 0x01, 0x09, 0x0e, 0x0a, 0x00, 0x16, + 0x00, 0x00, 0x04, 0x0c, 0x0a, 0x00, 0x16, 0x00, 0x9c, 0x40, 0x01, 0xd9, + 0x2a, 0x20, 0x52, 0x65, 0x65, 0x64, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, + 0x10, 0x11, 0x19, 0x98, 0x10, 0x00, 0x17, 0x00, 0x01, 0x15, 0x19, 0x00, + 0x00, 0x06, 0x00, 0x01, 0x07, 0x10, 0x00, 0x00, 0x08, 0x00, 0x02, 0x07, + 0x8d, 0x80, 0x00, 0x08, 0x00, 0x9c, 0x40, 0x00, 0xa6, 0x2a, 0x20, 0x41, + 0x63, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x02, 0x31, 0x1e, + 0x15, 0x05, 0x00, 0x17, 0x00, 0x72, 0x34, 0x0e, 0x09, 0x03, 0x29, 0x00, + 0x37, 0x15, 0x12, 0x08, 0x00, 0x28, 0x00, 0x02, 0x00, 0x8d, 0x16, 0x00, + 0x19, 0x00, 0x9c, 0x40, 0x00, 0x6a, 0x2a, 0x20, 0x48, 0x61, 0x72, 0x6d, + 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x20, 0x28, 0x53, 0x6f, 0x6e, 0x69, 0x63, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x3a, 0x2d, 0xd4, 0x05, 0x00, + 0x99, 0x00, 0x11, 0x27, 0x50, 0x02, 0x00, 0x09, 0x00, 0x0a, 0x28, 0x14, + 0x08, 0x00, 0x09, 0x00, 0x02, 0x00, 0x10, 0x88, 0x00, 0x1a, 0x00, 0x9c, + 0x40, 0x00, 0x56, 0x2a, 0x20, 0x54, 0x61, 0x6e, 0x67, 0x6f, 0x20, 0x41, + 0x63, 0x63, 0x6f, 0x72, 0x64, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x34, 0x10, 0x13, 0x19, 0x0f, 0x04, 0x00, 0x1a, 0x00, 0x03, + 0x1a, 0x0e, 0x04, 0x00, 0x1a, 0x00, 0x01, 0x07, 0x8d, 0x04, 0x00, 0x2a, + 0x00, 0x52, 0x08, 0x8e, 0x04, 0x00, 0x2a, 0x00, 0x9c, 0x40, 0x00, 0x56, + 0x2a, 0x20, 0x4e, 0x79, 0x6c, 0x6f, 0x6e, 0x47, 0x75, 0x69, 0x74, 0x61, + 0x72, 0x28, 0x48, 0x65, 0x6c, 0x6c, 0x52, 0x6f, 0x6c, 0x6c, 0x29, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, + 0x00, 0x05, 0x30, 0x1f, 0x12, 0x00, 0x27, 0x00, 0x31, 0x2d, 0x1f, 0x0e, + 0x04, 0x27, 0x00, 0x30, 0x13, 0x1f, 0x0a, 0x04, 0x27, 0x00, 0x00, 0x00, + 0x5f, 0x0a, 0x03, 0x27, 0x00, 0x77, 0x88, 0x01, 0x11, 0x2a, 0x20, 0x53, + 0x74, 0x65, 0x65, 0x6c, 0x47, 0x75, 0x69, 0x74, 0x61, 0x72, 0x28, 0x48, + 0x65, 0x6c, 0x6c, 0x52, 0x6f, 0x6c, 0x6c, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x05, 0x20, + 0x1f, 0x12, 0x00, 0x27, 0x00, 0x31, 0x2d, 0x1f, 0x0e, 0x04, 0x27, 0x00, + 0x30, 0x13, 0x1f, 0x0a, 0x04, 0x27, 0x00, 0x00, 0x00, 0x5f, 0x0a, 0x03, + 0x27, 0x00, 0x76, 0x55, 0x01, 0x11, 0x2a, 0x20, 0x4a, 0x61, 0x7a, 0x7a, + 0x47, 0x75, 0x69, 0x74, 0x61, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x02, 0x1e, 0x5f, 0x04, 0x00, + 0xf7, 0x00, 0x31, 0x2a, 0x9f, 0x04, 0x00, 0xf7, 0x00, 0x33, 0x2b, 0x5f, + 0x04, 0x00, 0xf7, 0x00, 0x01, 0x00, 0x1f, 0x04, 0x00, 0xf7, 0x00, 0x64, + 0xa6, 0x01, 0x32, 0x2a, 0x20, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x47, 0x75, + 0x69, 0x74, 0x61, 0x72, 0x20, 0x28, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, + 0x73, 0x20, 0x49, 0x49, 0x49, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x00, 0x58, 0x27, 0x5f, 0x07, 0x00, 0xf6, 0x00, 0x53, + 0x22, 0x5f, 0x04, 0x00, 0xf8, 0x00, 0x33, 0x1d, 0x5f, 0x0a, 0x00, 0xf9, + 0x00, 0x31, 0x00, 0x5f, 0x1f, 0x04, 0x17, 0x00, 0x62, 0x0c, 0x01, 0x25, + 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, 0x64, 0x47, 0x75, 0x69, 0x74, 0x61, + 0x72, 0x20, 0x28, 0x41, 0x6c, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x29, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, + 0x04, 0x33, 0x1c, 0x54, 0x17, 0x09, 0x8a, 0x00, 0x31, 0x05, 0x5b, 0x1b, + 0x07, 0x8b, 0x00, 0x04, 0x0b, 0x58, 0x17, 0x01, 0xbb, 0x00, 0x01, 0x00, + 0x14, 0x12, 0x04, 0x0c, 0x00, 0x60, 0x68, 0x00, 0x49, 0x2a, 0x20, 0x4f, + 0x76, 0x65, 0x72, 0x64, 0x72, 0x69, 0x76, 0x65, 0x20, 0x28, 0x4f, 0x50, + 0x4c, 0x33, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x24, + 0x9f, 0x0c, 0x00, 0x8c, 0x00, 0x21, 0x20, 0x53, 0x01, 0x00, 0xb8, 0x00, + 0x02, 0x19, 0x92, 0x01, 0x00, 0xda, 0x00, 0x02, 0x00, 0x91, 0x01, 0x00, + 0x47, 0x00, 0x9c, 0x40, 0x00, 0x7e, 0x2a, 0x20, 0x44, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x4f, 0x50, 0x4c, 0x33, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x24, 0x9f, 0x0c, 0x00, + 0x8c, 0x00, 0x21, 0x1a, 0x53, 0x02, 0x00, 0xb8, 0x00, 0x02, 0x19, 0x92, + 0x01, 0x00, 0xf7, 0x00, 0x02, 0x00, 0x91, 0x01, 0x00, 0x46, 0x00, 0x9c, + 0x40, 0x00, 0xdc, 0x2a, 0x20, 0x47, 0x75, 0x69, 0x74, 0x61, 0x72, 0x20, + 0x48, 0x61, 0x72, 0x6d, 0x6f, 0x6e, 0x69, 0x63, 0x73, 0x28, 0x48, 0x65, + 0x6c, 0x6c, 0x66, 0x69, 0x72, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x33, 0x05, 0x0a, 0x1c, 0x90, 0x06, 0x08, 0xfc, 0x00, 0x32, + 0x14, 0x9f, 0x03, 0x00, 0xf6, 0x00, 0x78, 0x0d, 0x95, 0x06, 0x07, 0x81, + 0x00, 0x11, 0x00, 0x10, 0x02, 0x0f, 0xf7, 0x00, 0x9c, 0x40, 0x00, 0x00, + 0x2a, 0x20, 0x41, 0x63, 0x6f, 0x75, 0x73, 0x74, 0x69, 0x63, 0x42, 0x61, + 0x73, 0x73, 0x20, 0x28, 0x53, 0x6f, 0x6e, 0x69, 0x63, 0x29, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x3a, + 0x00, 0x20, 0x21, 0x1e, 0x0a, 0x05, 0xa4, 0x00, 0x60, 0x28, 0x1f, 0x08, + 0x0a, 0x96, 0x00, 0x23, 0x25, 0x1f, 0x0a, 0x07, 0x85, 0x00, 0x01, 0x00, + 0x1f, 0x07, 0x07, 0x78, 0x00, 0x21, 0x8a, 0x00, 0xb4, 0x2a, 0x20, 0x46, + 0x69, 0x6e, 0x67, 0x65, 0x72, 0x42, 0x61, 0x73, 0x73, 0x20, 0x28, 0x53, + 0x6f, 0x6e, 0x69, 0x63, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x09, 0x25, + 0x1f, 0x12, 0x00, 0x2f, 0x00, 0x30, 0x13, 0x5f, 0x0a, 0x04, 0x2f, 0x00, + 0x70, 0x30, 0x1f, 0x0e, 0x04, 0x2f, 0x00, 0x00, 0x00, 0x5f, 0x0a, 0x03, + 0x2f, 0x00, 0x74, 0x89, 0x00, 0x50, 0x2a, 0x20, 0x50, 0x69, 0x63, 0x6b, + 0x42, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x09, 0x1e, 0x1f, 0x15, 0x00, + 0x8f, 0x00, 0x30, 0x0e, 0x5f, 0x0d, 0x04, 0x2f, 0x00, 0x70, 0x31, 0x1f, + 0x0e, 0x04, 0x3f, 0x00, 0x00, 0x00, 0x5f, 0x0a, 0x03, 0x2f, 0x00, 0x72, + 0x16, 0x00, 0x50, 0x2a, 0x20, 0x46, 0x72, 0x65, 0x74, 0x6c, 0x65, 0x73, + 0x73, 0x42, 0x61, 0x73, 0x73, 0x28, 0x41, 0x6c, 0x61, 0x64, 0x64, 0x69, + 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x06, 0x04, 0x00, 0x1d, 0x1a, 0x09, 0x07, 0x0b, 0x00, 0x50, + 0x00, 0x92, 0x0b, 0x13, 0xea, 0x00, 0x00, 0x05, 0x4d, 0x03, 0x00, 0x6a, + 0x00, 0x60, 0x09, 0x4a, 0x0c, 0x00, 0xfa, 0x00, 0x9c, 0x40, 0x00, 0x00, + 0x2a, 0x20, 0x53, 0x6c, 0x61, 0x70, 0x42, 0x61, 0x73, 0x73, 0x31, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x20, + 0x00, 0x3f, 0x13, 0x9f, 0x0e, 0x08, 0x27, 0x00, 0x00, 0x18, 0x5f, 0x0d, + 0x08, 0x27, 0x00, 0x03, 0x38, 0x9f, 0x0f, 0x08, 0x27, 0x00, 0x01, 0x00, + 0x5f, 0x07, 0x08, 0x17, 0x00, 0x14, 0xe2, 0x01, 0x11, 0x2a, 0x20, 0x53, + 0x6c, 0x61, 0x70, 0x42, 0x61, 0x73, 0x73, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x3f, 0x0e, + 0x9f, 0x0e, 0x06, 0x27, 0x00, 0x00, 0x18, 0x5f, 0x0d, 0x08, 0x27, 0x00, + 0x03, 0x38, 0x9f, 0x0f, 0x08, 0x27, 0x00, 0x01, 0x00, 0x5f, 0x07, 0x08, + 0x17, 0x00, 0x15, 0x32, 0x01, 0x18, 0x2a, 0x20, 0x53, 0x79, 0x6e, 0x42, + 0x61, 0x73, 0x73, 0x31, 0x20, 0x28, 0x44, 0x79, 0x6e, 0x61, 0x42, 0x72, + 0x6f, 0x73, 0x2e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xf4, 0x00, 0x3d, 0x00, 0x44, 0x17, 0x55, 0x1e, 0x0d, + 0x0b, 0x00, 0x02, 0x05, 0x54, 0x1e, 0x09, 0x09, 0x00, 0x14, 0x00, 0x59, + 0x1e, 0x0a, 0x0a, 0x00, 0x33, 0x18, 0x54, 0x1e, 0x05, 0x89, 0x00, 0x10, + 0x39, 0x00, 0x6a, 0x2a, 0x20, 0x53, 0x79, 0x6e, 0x42, 0x61, 0x73, 0x73, + 0x32, 0x20, 0x28, 0x4e, 0x69, 0x6e, 0x65, 0x6b, 0x6f, 0x29, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x20, 0x00, 0x36, 0x19, 0xdf, 0x07, 0x07, 0x20, 0x00, 0x30, + 0x13, 0x9f, 0x09, 0x06, 0x17, 0x00, 0x35, 0x37, 0xdf, 0x06, 0x06, 0x16, + 0x00, 0x31, 0x00, 0x9f, 0x06, 0x08, 0xf6, 0x00, 0x1b, 0xf1, 0x01, 0x4d, + 0x2a, 0x20, 0x56, 0x69, 0x6f, 0x6c, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, + 0x23, 0x31, 0x1e, 0x0f, 0x04, 0x00, 0x15, 0x00, 0x70, 0x31, 0x0c, 0x09, + 0x03, 0x07, 0x00, 0x37, 0x1f, 0x0d, 0x10, 0x00, 0x04, 0x00, 0x02, 0x03, + 0x8b, 0x0a, 0x00, 0x15, 0x00, 0x9c, 0x40, 0x03, 0x20, 0x2a, 0x20, 0x56, + 0x69, 0x6f, 0x6c, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x23, 0x31, 0x1e, + 0x0e, 0x04, 0x00, 0x15, 0x00, 0x70, 0x3d, 0x0b, 0x09, 0x03, 0x07, 0x00, + 0x34, 0x1e, 0x0e, 0x10, 0x00, 0x04, 0x00, 0x02, 0x03, 0x8c, 0x0a, 0x00, + 0x15, 0x00, 0x9c, 0x40, 0x03, 0x20, 0x2a, 0x20, 0x43, 0x65, 0x6c, 0x6c, + 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x23, 0x31, 0x28, 0x10, 0x04, 0x00, + 0x15, 0x00, 0x70, 0x2f, 0x0e, 0x09, 0x03, 0x08, 0x00, 0x35, 0x1e, 0x4e, + 0x10, 0x00, 0x04, 0x00, 0x02, 0x03, 0x8e, 0x0a, 0x00, 0x16, 0x00, 0x9c, + 0x40, 0x01, 0x9d, 0x2a, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x62, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x3a, 0x23, 0x31, 0x2b, 0x10, 0x04, 0x00, 0x15, 0x00, 0x70, + 0x1f, 0x0e, 0x09, 0x03, 0x07, 0x00, 0x34, 0x2c, 0x0e, 0x10, 0x00, 0x04, + 0x00, 0x02, 0x00, 0x8e, 0x0a, 0x00, 0x15, 0x00, 0x9c, 0x40, 0x02, 0x58, + 0x2a, 0x20, 0x54, 0x72, 0x65, 0x6d, 0x6f, 0x6c, 0x6f, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x25, 0x31, 0x15, 0xd0, 0x81, 0x00, 0x13, 0x00, 0x01, 0x13, 0x8d, 0x0b, + 0x00, 0x44, 0x00, 0x01, 0x06, 0x0f, 0x0a, 0x00, 0x27, 0x00, 0x01, 0x00, + 0x0e, 0x0a, 0x00, 0x18, 0x00, 0x9c, 0x40, 0x00, 0x99, 0x2a, 0x20, 0x50, + 0x69, 0x7a, 0x7a, 0x69, 0x6b, 0x61, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x14, 0x00, + 0xd3, 0x13, 0x0b, 0xa7, 0x00, 0x12, 0x05, 0x53, 0x0f, 0x18, 0xf7, 0x00, + 0x11, 0x08, 0x10, 0x13, 0x17, 0xf7, 0x00, 0x11, 0x0c, 0x52, 0x0e, 0x0e, + 0xc7, 0x00, 0x02, 0xe4, 0x00, 0xf6, 0x2a, 0x20, 0x4f, 0x72, 0x63, 0x68, + 0x65, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x48, 0x61, 0x72, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x1b, 0x1f, 0x0a, 0x00, + 0x14, 0x00, 0x51, 0x7f, 0x5f, 0x0a, 0x02, 0x74, 0x00, 0x11, 0x00, 0x1f, + 0x0e, 0x05, 0x64, 0x00, 0x50, 0x7f, 0x4e, 0x0b, 0x02, 0xf4, 0x00, 0x2b, + 0xa5, 0x08, 0xda, 0x2a, 0x20, 0x54, 0x69, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0a, 0x54, 0x0f, 0x00, 0xf7, 0x00, 0x01, + 0x00, 0x1f, 0x0e, 0x00, 0xfa, 0x00, 0x01, 0x00, 0x1f, 0x0b, 0x00, 0xf6, + 0x00, 0x02, 0x47, 0x0f, 0x0d, 0x00, 0xfe, 0x00, 0x08, 0x84, 0x02, 0x30, + 0x2a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x73, 0x73, 0x65, + 0x6d, 0x62, 0x6c, 0x65, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x13, 0x31, 0x1d, 0xce, 0x0b, 0x00, 0x04, 0x00, 0x01, 0x1c, 0x8e, 0x8a, + 0x00, 0x15, 0x00, 0x05, 0x06, 0x0e, 0x8a, 0x00, 0x28, 0x00, 0x01, 0x00, + 0x0f, 0x0a, 0x00, 0x18, 0x00, 0x9c, 0x40, 0x00, 0xa0, 0x2a, 0x20, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, + 0x65, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x11, 0x31, 0x1d, + 0xcb, 0x0b, 0x00, 0x01, 0x00, 0x01, 0x1c, 0x8b, 0x8a, 0x00, 0x11, 0x00, + 0x01, 0x06, 0x0a, 0x08, 0x00, 0x26, 0x00, 0x01, 0x03, 0x0a, 0x08, 0x00, + 0x16, 0x00, 0x9c, 0x40, 0x01, 0xd9, 0x2a, 0x20, 0x53, 0x79, 0x6e, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x11, 0x15, 0x1a, 0x82, 0x00, + 0x62, 0x00, 0x31, 0x1c, 0x19, 0x81, 0x00, 0xb3, 0x00, 0x01, 0x18, 0x0a, + 0x00, 0x00, 0x06, 0x00, 0x01, 0x02, 0x0a, 0x00, 0x00, 0x05, 0x00, 0x9c, + 0x40, 0x03, 0xee, 0x2a, 0x20, 0x53, 0x79, 0x6e, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x32, 0x20, 0x28, 0x41, 0x6c, 0x61, 0x64, 0x64, 0x69, 0x6e, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x32, 0x15, 0x30, 0x23, 0x08, 0x04, 0x00, 0x05, 0x00, 0x00, + 0x20, 0x0f, 0x01, 0x03, 0x73, 0x00, 0x34, 0x34, 0x88, 0x03, 0x00, 0xa4, + 0x00, 0x00, 0x00, 0x48, 0x8a, 0x00, 0x05, 0x00, 0x9c, 0x40, 0x03, 0x76, + 0x2a, 0x20, 0x43, 0x68, 0x6f, 0x69, 0x72, 0x20, 0x41, 0x61, 0x68, 0x73, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, + 0x04, 0x30, 0x2a, 0x51, 0x80, 0x00, 0x51, 0x00, 0x70, 0x1e, 0x8d, 0x00, + 0x00, 0x51, 0x00, 0x30, 0x08, 0x4d, 0x80, 0x00, 0x05, 0x00, 0x70, 0x08, + 0x8b, 0x80, 0x00, 0x05, 0x00, 0x9c, 0x40, 0x03, 0x05, 0x2a, 0x20, 0x56, + 0x6f, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x23, 0x75, 0x00, + 0x14, 0x0a, 0x03, 0x17, 0x00, 0x31, 0x0e, 0x93, 0x8d, 0x05, 0x28, 0x00, + 0x03, 0x25, 0x55, 0x0a, 0x02, 0x67, 0x00, 0x01, 0x00, 0x4f, 0x04, 0x00, + 0x18, 0x00, 0x9c, 0x40, 0x00, 0x99, 0x2a, 0x20, 0x53, 0x79, 0x6e, 0x74, + 0x68, 0x56, 0x6f, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x20, 0x04, 0x3a, 0x4c, 0x4c, 0x80, 0x00, + 0x51, 0x00, 0x70, 0x2e, 0x8c, 0x00, 0x00, 0x51, 0x00, 0x37, 0x33, 0x4e, + 0x80, 0x00, 0x05, 0x00, 0x70, 0x00, 0x8b, 0x80, 0x00, 0x05, 0x00, 0x9c, + 0x40, 0x02, 0x94, 0x2a, 0x20, 0x4f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, + 0x72, 0x61, 0x48, 0x69, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x13, 0x50, 0x0d, 0x00, 0xf5, 0x00, 0x01, + 0x15, 0x12, 0x05, 0x00, 0xf2, 0x00, 0x01, 0x11, 0x16, 0x09, 0x00, 0xf3, + 0x00, 0x02, 0x00, 0x52, 0x0a, 0x00, 0xf5, 0x00, 0x0c, 0xdd, 0x03, 0xe8, + 0x2a, 0x20, 0x54, 0x72, 0x75, 0x6d, 0x70, 0x65, 0x74, 0x20, 0x28, 0x53, + 0x6f, 0x6e, 0x69, 0x63, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x3d, + 0x00, 0x02, 0x1b, 0x90, 0x0e, 0x01, 0x14, 0x00, 0x02, 0x15, 0x13, 0x0e, + 0x00, 0x19, 0x00, 0x04, 0x0a, 0x10, 0x0b, 0x00, 0xfd, 0x00, 0x02, 0x09, + 0x14, 0x0d, 0x00, 0x0e, 0x00, 0x9c, 0x40, 0x00, 0x50, 0x2a, 0x20, 0x54, + 0x72, 0x6f, 0x6d, 0x62, 0x6f, 0x6e, 0x65, 0x20, 0x28, 0x41, 0x6c, 0x61, + 0x64, 0x64, 0x69, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x2d, 0x23, 0x00, 0x1a, + 0x8c, 0x0d, 0x01, 0x2a, 0x00, 0x00, 0x12, 0x8a, 0x08, 0x00, 0x2a, 0x00, + 0x00, 0x09, 0x97, 0x87, 0x02, 0x1a, 0x00, 0x51, 0x03, 0x97, 0x87, 0x00, + 0x1a, 0x00, 0x9c, 0x40, 0x00, 0x50, 0x2a, 0x20, 0x54, 0x75, 0x62, 0x61, + 0x20, 0x28, 0x41, 0x6c, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x29, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x3d, 0x12, 0x00, 0x1b, 0x8e, 0x06, 0x01, + 0x66, 0x00, 0x01, 0x03, 0x8d, 0x0d, 0x0a, 0x25, 0x00, 0x00, 0x04, 0x8e, + 0x87, 0x02, 0x15, 0x00, 0x51, 0x05, 0x91, 0x87, 0x00, 0x16, 0x00, 0x9c, + 0x40, 0x00, 0xe9, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, 0x64, 0x54, 0x72, + 0x75, 0x6d, 0x70, 0x65, 0x74, 0x20, 0x28, 0x53, 0x6f, 0x6e, 0x69, 0x63, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xf4, 0x00, 0x3d, 0x00, 0x12, 0x15, 0x8f, 0x0e, 0x01, 0x13, 0x00, 0x02, + 0x4a, 0x0f, 0x0e, 0x00, 0x18, 0x00, 0x02, 0x1f, 0x0d, 0x0b, 0x00, 0xfd, + 0x00, 0x02, 0x00, 0x0f, 0x0d, 0x00, 0x0e, 0x00, 0x9c, 0x40, 0x00, 0x50, + 0x2a, 0x20, 0x46, 0x72, 0x65, 0x6e, 0x63, 0x68, 0x48, 0x6f, 0x72, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x00, 0x01, 0x22, 0x8b, 0x04, 0x00, 0x37, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x94, 0x00, 0x00, 0x06, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x40, 0x01, 0xe6, 0x2a, 0x20, 0x42, + 0x72, 0x61, 0x73, 0x73, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x28, 0x47, 0x72, 0x65, 0x65, 0x6e, 0x64, 0x6f, 0x67, 0x29, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x35, 0x12, 0x21, 0x1a, + 0x8e, 0x00, 0x01, 0x47, 0x00, 0x20, 0x09, 0x95, 0x00, 0x02, 0x17, 0x00, + 0x31, 0x00, 0x9b, 0x05, 0x02, 0x36, 0x00, 0x14, 0x06, 0x94, 0x80, 0x02, + 0x08, 0x00, 0x9c, 0x40, 0x00, 0xa0, 0x2a, 0x20, 0x53, 0x79, 0x6e, 0x42, + 0x72, 0x61, 0x73, 0x73, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x02, 0x01, 0x14, 0x16, 0x06, 0x00, + 0xf6, 0x00, 0x71, 0x2a, 0x1f, 0x00, 0x00, 0x0c, 0x00, 0x31, 0x1c, 0x1f, + 0x00, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x13, 0x00, 0x00, 0x08, 0x00, 0x9c, + 0x40, 0x00, 0xa6, 0x2a, 0x20, 0x53, 0x79, 0x6e, 0x42, 0x72, 0x61, 0x73, + 0x73, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3a, 0x23, 0x31, 0x1e, 0x11, 0x04, 0x00, 0x19, 0x00, 0x72, + 0x37, 0x11, 0x09, 0x03, 0x09, 0x00, 0x37, 0x1f, 0x11, 0x10, 0x00, 0x09, + 0x00, 0x02, 0x00, 0x91, 0x0a, 0x00, 0x17, 0x00, 0x9c, 0x40, 0x00, 0xf0, + 0x2a, 0x20, 0x53, 0x6f, 0x70, 0x72, 0x61, 0x6e, 0x6f, 0x53, 0x61, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x35, + 0x12, 0x21, 0x20, 0x51, 0x00, 0x01, 0x48, 0x00, 0x20, 0x0e, 0x4d, 0x00, + 0x02, 0x18, 0x00, 0x32, 0x0d, 0x4c, 0x05, 0x02, 0x37, 0x00, 0x12, 0x00, + 0x8e, 0x80, 0x02, 0x09, 0x00, 0x9c, 0x40, 0x00, 0x6a, 0x2a, 0x20, 0x41, + 0x6c, 0x74, 0x6f, 0x53, 0x61, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x35, 0x12, 0x21, 0x1a, + 0x8e, 0x00, 0x01, 0x48, 0x00, 0x20, 0x0b, 0x53, 0x00, 0x02, 0x18, 0x00, + 0x31, 0x05, 0x8c, 0x05, 0x02, 0x37, 0x00, 0x14, 0x04, 0x8e, 0x80, 0x02, + 0x08, 0x00, 0x9c, 0x40, 0x00, 0x85, 0x2a, 0x20, 0x54, 0x65, 0x6e, 0x6f, + 0x72, 0x53, 0x61, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x35, 0x12, 0x21, 0x1e, 0x8f, 0x00, 0x01, + 0x48, 0x00, 0x20, 0x0a, 0x52, 0x00, 0x02, 0x17, 0x00, 0x31, 0x05, 0x8b, + 0x05, 0x02, 0x36, 0x00, 0x12, 0x04, 0x8d, 0x80, 0x02, 0x08, 0x00, 0x9c, + 0x40, 0x00, 0xc1, 0x2a, 0x20, 0x42, 0x61, 0x72, 0x69, 0x74, 0x6f, 0x6e, + 0x65, 0x53, 0x61, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x35, 0x12, 0x21, 0x17, 0x8e, 0x00, 0x01, 0x48, 0x00, 0x20, + 0x0a, 0x91, 0x00, 0x02, 0x17, 0x00, 0x31, 0x05, 0x8e, 0x05, 0x02, 0x36, + 0x00, 0x13, 0x04, 0x91, 0x80, 0x02, 0x08, 0x00, 0x9c, 0x40, 0x00, 0xa0, + 0x2a, 0x20, 0x4f, 0x62, 0x6f, 0x65, 0x20, 0x28, 0x41, 0x6c, 0x61, 0x64, + 0x64, 0x69, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x12, 0x01, 0x27, 0x18, 0x97, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x16, 0x1f, + 0x00, 0x0a, 0x00, 0x01, 0x1e, 0x10, 0x9f, 0x00, 0x0a, 0x00, 0x04, 0x00, + 0x18, 0x1f, 0x00, 0x0a, 0x00, 0x9c, 0x40, 0x00, 0x5d, 0x2a, 0x20, 0x45, + 0x6e, 0x67, 0x6c, 0x69, 0x73, 0x68, 0x48, 0x6f, 0x72, 0x6e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x12, 0x01, 0x26, + 0x18, 0x97, 0x00, 0x0a, 0x00, 0x01, 0x2b, 0x16, 0x1f, 0x00, 0x0a, 0x00, + 0x01, 0x17, 0x10, 0x9f, 0x00, 0x0a, 0x00, 0x02, 0x05, 0x10, 0x1f, 0x00, + 0x08, 0x00, 0x9c, 0x40, 0x00, 0xad, 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, + 0x6f, 0x6f, 0x6e, 0x20, 0x28, 0x41, 0x6c, 0x61, 0x64, 0x64, 0x69, 0x6e, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x38, 0x12, 0x00, 0x27, 0x03, 0x97, 0x00, + 0x0c, 0x00, 0x00, 0x24, 0x1f, 0x1f, 0x00, 0x0c, 0x00, 0x01, 0x23, 0x0f, + 0x9f, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x10, 0x1f, 0x00, 0x0c, 0x00, 0x9c, + 0x40, 0x00, 0x50, 0x2a, 0x20, 0x43, 0x6c, 0x61, 0x72, 0x69, 0x6e, 0x65, + 0x74, 0x20, 0x28, 0x41, 0x6c, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x29, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x23, 0x12, 0x1e, 0x8d, 0x00, 0x01, 0x0a, 0x00, 0x72, + 0x12, 0x4f, 0x8b, 0x1d, 0x8a, 0x00, 0x71, 0x04, 0x12, 0x00, 0x00, 0x0a, + 0x00, 0x11, 0x0c, 0x13, 0x8f, 0x06, 0x2a, 0x00, 0x9c, 0x40, 0x00, 0x56, + 0x2a, 0x20, 0x50, 0x69, 0x63, 0x63, 0x6f, 0x6c, 0x6f, 0x20, 0x28, 0x41, + 0x6c, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x17, + 0x32, 0x32, 0x09, 0xca, 0x00, 0x00, 0x08, 0x00, 0x32, 0x11, 0x8c, 0x8c, + 0x0a, 0xc8, 0x00, 0x72, 0x06, 0x8a, 0x08, 0x00, 0x78, 0x00, 0x12, 0x0b, + 0xca, 0x8d, 0x00, 0x78, 0x00, 0x9c, 0x40, 0x00, 0x7e, 0x2a, 0x20, 0x46, + 0x6c, 0x75, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x10, 0x75, 0x00, + 0x14, 0x0a, 0x03, 0x15, 0x00, 0x31, 0x16, 0x93, 0x8d, 0x00, 0x45, 0x00, + 0x03, 0x31, 0x56, 0x0c, 0x02, 0x68, 0x00, 0x01, 0x00, 0x4e, 0x84, 0x00, + 0x18, 0x00, 0x9c, 0x40, 0x00, 0xa6, 0x2a, 0x20, 0x52, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xf4, 0x00, 0x3b, 0x00, 0x71, 0x08, 0x54, 0x0e, 0x03, + 0x65, 0x00, 0x32, 0x24, 0x8d, 0x04, 0x00, 0x25, 0x00, 0x02, 0x37, 0x54, + 0x10, 0x02, 0x55, 0x00, 0x02, 0x00, 0x4e, 0x84, 0x00, 0x08, 0x00, 0x9c, + 0x40, 0x00, 0xad, 0x2a, 0x20, 0x50, 0x61, 0x6e, 0x46, 0x6c, 0x75, 0x74, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x34, 0x00, 0x75, 0x00, 0x13, 0x0a, 0x00, 0x15, 0x00, 0x32, + 0x13, 0x92, 0x0d, 0x00, 0x45, 0x00, 0x03, 0x31, 0x56, 0x0c, 0x00, 0x15, + 0x00, 0x01, 0x00, 0x4e, 0x84, 0x00, 0x18, 0x00, 0x9c, 0x40, 0x00, 0xa0, + 0x2a, 0x20, 0x42, 0x6f, 0x74, 0x74, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x00, 0x75, 0x00, 0x12, 0x0a, 0x00, 0x16, 0x00, 0x32, 0x18, 0x8c, 0x8d, + 0x00, 0x56, 0x00, 0x03, 0x29, 0x59, 0x0d, 0x00, 0x36, 0x00, 0x01, 0x00, + 0x4c, 0x84, 0x00, 0x27, 0x00, 0x9c, 0x40, 0x00, 0xd5, 0x2a, 0x20, 0x53, + 0x68, 0x61, 0x6b, 0x75, 0x68, 0x61, 0x63, 0x68, 0x69, 0x20, 0x28, 0x41, + 0x6c, 0x69, 0x73, 0x69, 0x61, 0x20, 0x44, 0x72, 0x61, 0x67, 0x6f, 0x6f, + 0x6e, 0x29, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x2c, 0x00, 0x34, 0x20, + 0x12, 0x06, 0x02, 0x27, 0x00, 0x54, 0x23, 0x12, 0x06, 0x02, 0x26, 0x00, + 0x72, 0x06, 0x0f, 0x09, 0x00, 0x78, 0x00, 0x32, 0x0b, 0x0e, 0x8a, 0x00, + 0x29, 0x00, 0x9c, 0x40, 0x00, 0x42, 0x2a, 0x20, 0x57, 0x68, 0x69, 0x73, + 0x74, 0x6c, 0x65, 0x20, 0x28, 0x41, 0x6c, 0x61, 0x64, 0x64, 0x69, 0x6e, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x13, 0x31, 0x08, 0x12, 0x80, 0x00, + 0x08, 0x00, 0x31, 0x7f, 0x80, 0x00, 0x00, 0x48, 0x00, 0x01, 0x08, 0x8f, + 0x02, 0x00, 0x78, 0x00, 0x12, 0x7f, 0xc0, 0x00, 0x00, 0x08, 0x00, 0x9c, + 0x40, 0x00, 0x8c, 0x2a, 0x20, 0x4f, 0x63, 0x61, 0x72, 0x69, 0x6e, 0x61, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x34, 0x00, 0x75, 0x00, 0x14, 0x0a, 0x03, 0x15, 0x00, 0x32, + 0x29, 0x94, 0x0d, 0x00, 0x45, 0x00, 0x03, 0x31, 0x56, 0x0c, 0x02, 0x68, + 0x00, 0x01, 0x00, 0x4e, 0x84, 0x00, 0x17, 0x00, 0x9c, 0x40, 0x00, 0xfd, + 0x2a, 0x20, 0x4c, 0x65, 0x61, 0x64, 0x53, 0x71, 0x75, 0x61, 0x72, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x10, 0x32, 0x18, 0x5f, 0x80, 0x01, 0x8b, 0x00, 0x02, 0x1a, 0x1a, 0x80, + 0x00, 0x2a, 0x00, 0x71, 0x09, 0x1c, 0x00, 0x00, 0x1a, 0x00, 0x01, 0x0d, + 0x5b, 0x80, 0x00, 0x0f, 0x00, 0x9c, 0x40, 0x00, 0x56, 0x2a, 0x20, 0x4c, + 0x65, 0x61, 0x64, 0x53, 0x61, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x71, 0x1c, + 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x31, 0x1c, 0x1f, 0x00, 0x00, 0x0f, 0x00, + 0x72, 0x04, 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x32, 0x04, 0x1f, 0x00, 0x00, + 0x0f, 0x00, 0x9c, 0x40, 0x00, 0x50, 0x2a, 0x20, 0x4c, 0x65, 0x61, 0x64, + 0x43, 0x61, 0x6c, 0x6c, 0x69, 0x6f, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x05, 0x75, 0x00, 0x13, 0x8a, 0x00, + 0x15, 0x00, 0x32, 0x18, 0x8d, 0x0d, 0x00, 0x55, 0x00, 0x23, 0x29, 0x99, + 0x0d, 0x00, 0xd5, 0x00, 0x61, 0x05, 0x4e, 0x84, 0x00, 0x2b, 0x00, 0x9c, + 0x40, 0x00, 0x49, 0x2a, 0x20, 0x4c, 0x65, 0x61, 0x64, 0x34, 0x43, 0x68, + 0x69, 0x66, 0x20, 0x28, 0x53, 0x6f, 0x6e, 0x69, 0x63, 0x29, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3a, 0x00, 0x01, 0x14, 0x8e, 0x0e, 0x00, 0x5f, 0x00, 0x01, + 0x17, 0x8e, 0x0e, 0x00, 0xbf, 0x00, 0x07, 0x2b, 0x8f, 0x0e, 0x08, 0x5f, + 0x00, 0x01, 0x04, 0x54, 0x03, 0x08, 0xbf, 0x00, 0x6d, 0xde, 0x00, 0x50, + 0x2a, 0x20, 0x4c, 0x65, 0x61, 0x64, 0x35, 0x43, 0x68, 0x61, 0x72, 0x20, + 0x28, 0x4e, 0x69, 0x6e, 0x65, 0x6b, 0x6f, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x00, 0x72, 0x1e, 0xd1, 0x01, 0x00, 0xff, 0x00, 0x71, 0x1e, 0x14, 0x01, + 0x00, 0xff, 0x00, 0x13, 0x1e, 0x52, 0x07, 0x00, 0xff, 0x00, 0x11, 0x04, + 0x14, 0x01, 0x00, 0xff, 0x00, 0x9c, 0x40, 0x00, 0x2e, 0x2a, 0x20, 0x4c, + 0x65, 0x61, 0x64, 0x56, 0x6f, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x23, 0x12, 0x1e, + 0x8f, 0x00, 0x01, 0x0a, 0x00, 0x72, 0x12, 0x50, 0x8b, 0x1d, 0x8a, 0x00, + 0x71, 0x04, 0x15, 0x00, 0x00, 0x0a, 0x00, 0x11, 0x0c, 0x13, 0x8f, 0x06, + 0x2a, 0x00, 0x9c, 0x40, 0x00, 0x56, 0x2a, 0x20, 0x4c, 0x65, 0x61, 0x64, + 0x37, 0x46, 0x69, 0x66, 0x74, 0x73, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xe8, 0x00, 0x3c, 0x00, 0x34, 0x18, 0x5b, 0x05, 0x03, + 0x07, 0x00, 0x73, 0x0e, 0x58, 0x00, 0x03, 0x06, 0x00, 0x74, 0x00, 0x5b, + 0x05, 0x03, 0x07, 0x00, 0x33, 0x0c, 0x56, 0x00, 0x03, 0x06, 0x00, 0x85, + 0x7d, 0x01, 0x89, 0x2a, 0x20, 0x4c, 0x65, 0x61, 0x64, 0x38, 0x42, 0x61, + 0x73, 0x73, 0x20, 0x28, 0x47, 0x45, 0x4d, 0x53, 0x3a, 0x20, 0x41, 0x6e, + 0x61, 0x6c, 0x6f, 0x67, 0x20, 0x53, 0x6f, 0x6c, 0x6f, 0x29, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x01, 0x01, 0x11, 0x53, 0x8d, 0x09, 0x69, 0x00, 0x01, + 0x1d, 0x81, 0x93, 0x02, 0x08, 0x00, 0x72, 0x06, 0x1e, 0x85, 0x00, 0x48, + 0x00, 0x61, 0x04, 0x1f, 0x07, 0x00, 0x08, 0x00, 0x9c, 0x40, 0x00, 0x99, + 0x2a, 0x20, 0x50, 0x61, 0x64, 0x31, 0x4e, 0x65, 0x77, 0x41, 0x67, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x07, 0x19, 0x0f, 0x0b, 0x06, 0x76, 0x00, 0x31, 0x21, 0x89, 0x02, + 0x00, 0x23, 0x00, 0x08, 0x16, 0x1f, 0x07, 0x00, 0xf5, 0x00, 0x01, 0x00, + 0x0e, 0x05, 0x00, 0x16, 0x00, 0x9c, 0x40, 0x01, 0xed, 0x2a, 0x20, 0x50, + 0x61, 0x64, 0x20, 0x57, 0x61, 0x72, 0x6d, 0x20, 0x28, 0x50, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x44, 0x4d, 0x58, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x10, + 0xc7, 0x00, 0x00, 0x03, 0x00, 0x31, 0x10, 0x47, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x10, 0x05, 0x00, 0x00, 0x06, 0x00, 0x31, 0x10, 0x05, 0x00, 0x00, + 0x06, 0x00, 0x39, 0xbc, 0x05, 0xdc, 0x2a, 0x20, 0x50, 0x6f, 0x6c, 0x79, + 0x53, 0x79, 0x6e, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x3c, 0x00, 0x31, 0x1a, 0x52, 0x08, 0x04, + 0x16, 0x00, 0x50, 0x16, 0x55, 0x08, 0x04, 0x15, 0x00, 0x52, 0x03, 0x51, + 0x00, 0x00, 0x06, 0x00, 0x30, 0x05, 0x54, 0x00, 0x00, 0x05, 0x00, 0x9c, + 0x40, 0x02, 0xfe, 0x2a, 0x20, 0x50, 0x61, 0x64, 0x43, 0x68, 0x6f, 0x69, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x05, 0x04, 0x00, 0x22, 0x4e, 0x80, 0x00, 0x52, 0x00, 0x00, + 0x11, 0x8a, 0x00, 0x00, 0x53, 0x00, 0x00, 0x0c, 0x4c, 0x80, 0x00, 0x05, + 0x00, 0x00, 0x0e, 0x89, 0x80, 0x00, 0x04, 0x00, 0x9c, 0x40, 0x06, 0x40, + 0x2a, 0x20, 0x42, 0x6f, 0x77, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x06, 0x1a, 0xcb, 0x03, 0x01, 0x61, 0x00, 0x05, 0x23, 0x0a, 0x09, + 0x00, 0xf3, 0x00, 0x01, 0x06, 0x89, 0x06, 0x02, 0x73, 0x00, 0x02, 0x1d, + 0x0a, 0x06, 0x00, 0xf4, 0x00, 0x77, 0xbd, 0x0d, 0x7d, 0x2a, 0x20, 0x4d, + 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x14, 0x31, 0x1c, + 0xcb, 0x0b, 0x00, 0x01, 0x00, 0x01, 0x16, 0x8b, 0x83, 0x00, 0x11, 0x00, + 0x09, 0x0e, 0x07, 0x03, 0x00, 0xa5, 0x00, 0x02, 0x0b, 0x07, 0x01, 0x00, + 0xe5, 0x00, 0x9c, 0x40, 0x00, 0x5d, 0x2a, 0x20, 0x50, 0x61, 0x64, 0x48, + 0x61, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x04, 0x21, 0x0c, 0x43, 0x04, 0x00, + 0x55, 0x00, 0x71, 0x00, 0x43, 0x84, 0x01, 0x54, 0x00, 0x01, 0x03, 0x09, + 0x87, 0x00, 0x36, 0x00, 0x01, 0x16, 0x0a, 0x07, 0x00, 0x36, 0x00, 0x9c, + 0x40, 0x01, 0x25, 0x2a, 0x20, 0x53, 0x77, 0x65, 0x65, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2c, 0x05, 0x22, 0x16, 0x06, 0x06, 0x00, 0x54, 0x00, 0x75, + 0x18, 0x04, 0x88, 0x05, 0x54, 0x00, 0x01, 0x03, 0x0f, 0x87, 0x00, 0x36, + 0x00, 0x02, 0x13, 0x09, 0x07, 0x00, 0x36, 0x00, 0x9c, 0x40, 0x01, 0x6e, + 0x2a, 0x20, 0x46, 0x58, 0x20, 0x72, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, + 0x22, 0x06, 0x14, 0x1f, 0x12, 0x00, 0x37, 0x00, 0x32, 0x11, 0x0c, 0x80, + 0x00, 0x05, 0x00, 0x02, 0x15, 0x1f, 0x92, 0x0d, 0x25, 0x00, 0x71, 0x11, + 0x0c, 0x80, 0x00, 0x05, 0x00, 0x9c, 0x40, 0x04, 0x1d, 0x2a, 0x20, 0x46, + 0x58, 0x2d, 0x53, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x20, 0x28, + 0x54, 0x68, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x46, 0x6f, 0x72, 0x63, 0x65, + 0x20, 0x49, 0x56, 0x29, 0x00, 0xff, 0xf4, 0x00, 0x2c, 0x00, 0x33, 0x1e, + 0x06, 0x06, 0x04, 0xf4, 0x00, 0x52, 0x1e, 0x05, 0x06, 0x04, 0xf5, 0x00, + 0x53, 0x09, 0x11, 0x03, 0x00, 0x24, 0x00, 0x32, 0x09, 0x0a, 0x03, 0x00, + 0x25, 0x00, 0x9c, 0x40, 0x04, 0x1d, 0x2a, 0x20, 0x46, 0x58, 0x43, 0x72, + 0x79, 0x73, 0x74, 0x61, 0x6c, 0x20, 0x28, 0x41, 0x6c, 0x69, 0x73, 0x69, + 0x61, 0x20, 0x44, 0x72, 0x61, 0x67, 0x6f, 0x6f, 0x6e, 0x29, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xf4, 0x00, 0x3c, 0x00, 0x45, 0x37, 0x1f, 0x0d, 0x04, + 0x76, 0x00, 0x04, 0x2b, 0x1a, 0x13, 0x07, 0x26, 0x00, 0x72, 0x0c, 0x1f, + 0x08, 0x07, 0x68, 0x00, 0x32, 0x0f, 0x18, 0x08, 0x07, 0x25, 0x00, 0x0e, + 0x59, 0x02, 0x94, 0x2a, 0x20, 0x41, 0x74, 0x6d, 0x6f, 0x53, 0x70, 0x68, + 0x65, 0x72, 0x65, 0x20, 0x28, 0x41, 0x6c, 0x69, 0x73, 0x69, 0x61, 0x20, + 0x44, 0x72, 0x61, 0x67, 0x6f, 0x6f, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x34, 0x13, 0x72, 0x17, 0x96, 0x81, 0x00, 0x55, 0x00, 0x11, + 0x31, 0x16, 0x84, 0x05, 0x06, 0x00, 0x31, 0x05, 0x9f, 0x04, 0x00, 0x45, + 0x00, 0x51, 0x10, 0x0d, 0x09, 0x06, 0x56, 0x00, 0x9c, 0x40, 0x01, 0x68, + 0x2a, 0x20, 0x42, 0x72, 0x69, 0x67, 0x68, 0x74, 0x4e, 0x65, 0x73, 0x73, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x32, 0x28, 0x92, 0x81, 0x04, 0x51, 0x00, 0x55, 0x27, 0x54, 0x08, + 0x04, 0x14, 0x00, 0x53, 0x2b, 0x5f, 0x00, 0x00, 0x06, 0x00, 0x32, 0x06, + 0x5f, 0x00, 0x00, 0x05, 0x00, 0x9c, 0x40, 0x04, 0x16, 0x2a, 0x20, 0x47, + 0x6f, 0x62, 0x6c, 0x69, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x04, 0x21, 0x12, + 0x04, 0x06, 0x00, 0x53, 0x00, 0x71, 0x00, 0x04, 0x81, 0x01, 0x03, 0x00, + 0x01, 0x13, 0x43, 0x87, 0x00, 0x33, 0x00, 0x01, 0x16, 0x05, 0x07, 0x00, + 0x33, 0x00, 0x9c, 0x40, 0x0c, 0xe4, 0x2a, 0x20, 0x45, 0x63, 0x68, 0x6f, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x04, 0x32, 0x29, 0x50, 0x80, 0x00, + 0x55, 0x00, 0x01, 0x2d, 0x8b, 0x00, 0x00, 0x55, 0x00, 0x02, 0x0c, 0x50, + 0x80, 0x00, 0x05, 0x00, 0x71, 0x10, 0x94, 0x80, 0x00, 0x05, 0x00, 0x9c, + 0x40, 0x03, 0xda, 0x2a, 0x20, 0x53, 0x63, 0x69, 0x46, 0x69, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x04, 0x31, 0x22, 0x94, 0x02, 0x00, 0x23, 0x00, 0x23, + 0x22, 0x9f, 0x03, 0x00, 0x43, 0x00, 0x68, 0x24, 0x9f, 0x02, 0x00, 0x23, + 0x00, 0x01, 0x08, 0x13, 0x81, 0x00, 0x25, 0x00, 0x9c, 0x40, 0x02, 0xd6, + 0x2a, 0x20, 0x53, 0x69, 0x74, 0x61, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x02, 0x2f, 0x5f, 0x07, 0x05, 0x44, 0x00, 0x02, 0x08, 0x92, 0x03, + 0x00, 0xf3, 0x00, 0x07, 0x19, 0x93, 0x03, 0x00, 0xf6, 0x00, 0x07, 0x0e, + 0x1f, 0x05, 0x00, 0xf6, 0x00, 0x69, 0x50, 0x02, 0x44, 0x2a, 0x20, 0x42, + 0x61, 0x6e, 0x6a, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x32, 0x00, 0x36, 0x19, + 0x4f, 0x14, 0x08, 0x19, 0x00, 0x52, 0x17, 0x5c, 0x09, 0x01, 0x10, 0x00, + 0x21, 0x45, 0x5f, 0x0f, 0x17, 0x18, 0x00, 0x32, 0x00, 0x13, 0x19, 0x09, + 0x17, 0x00, 0x10, 0x04, 0x01, 0x25, 0x2a, 0x20, 0x53, 0x68, 0x61, 0x6d, + 0x69, 0x73, 0x65, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x05, 0x1b, 0x9f, 0x0e, 0x00, + 0xf8, 0x00, 0x01, 0x18, 0x5f, 0x05, 0x00, 0xe4, 0x00, 0x02, 0x22, 0x9f, + 0x0e, 0x00, 0xe2, 0x00, 0x06, 0x0a, 0x5f, 0x07, 0x00, 0xf5, 0x00, 0x21, + 0x12, 0x04, 0x16, 0x2a, 0x20, 0x4b, 0x6f, 0x74, 0x6f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x24, 0x03, 0x53, 0x17, 0x9b, 0x08, 0x04, 0x33, 0x00, 0x13, + 0x1c, 0x9b, 0x88, 0x08, 0x32, 0x00, 0x01, 0x07, 0x58, 0x16, 0x04, 0x23, + 0x00, 0x03, 0x0e, 0x18, 0x16, 0x08, 0x15, 0x00, 0x5b, 0x75, 0x10, 0x90, + 0x2a, 0x20, 0x4b, 0x61, 0x6c, 0x69, 0x6d, 0x62, 0x61, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, + 0x00, 0x74, 0x17, 0x54, 0x96, 0x15, 0x77, 0x00, 0x64, 0x15, 0x5e, 0x0d, + 0x07, 0x55, 0x00, 0x71, 0x07, 0x15, 0x80, 0x0a, 0x06, 0x00, 0x74, 0x13, + 0x15, 0x0c, 0x09, 0x46, 0x00, 0x0d, 0xe1, 0x02, 0x4a, 0x2a, 0x20, 0x42, + 0x61, 0x67, 0x50, 0x69, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x10, 0x01, 0x0b, + 0x90, 0x10, 0x00, 0x36, 0x00, 0x00, 0x1c, 0x11, 0x00, 0x00, 0x35, 0x00, + 0x04, 0x27, 0x0c, 0x00, 0x00, 0x0d, 0x00, 0x02, 0x08, 0x8c, 0x00, 0x00, + 0x0d, 0x00, 0x9c, 0x40, 0x00, 0x50, 0x2a, 0x20, 0x46, 0x69, 0x64, 0x64, + 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x23, 0x31, 0x1d, 0x0e, 0x04, 0x00, + 0x15, 0x00, 0x70, 0x37, 0x0b, 0x09, 0x03, 0x07, 0x00, 0x37, 0x1f, 0x0c, + 0x10, 0x00, 0x04, 0x00, 0x02, 0x03, 0x8a, 0x0a, 0x00, 0x15, 0x00, 0x9c, + 0x40, 0x03, 0x20, 0x2a, 0x20, 0x53, 0x68, 0x61, 0x6e, 0x61, 0x69, 0x20, + 0x28, 0x41, 0x6c, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x12, 0x01, 0x27, 0x18, 0x97, 0x00, 0x0a, 0x00, 0x01, + 0x28, 0x16, 0x1f, 0x00, 0x0a, 0x00, 0x01, 0x1e, 0x10, 0x9f, 0x00, 0x0a, + 0x00, 0x04, 0x00, 0x18, 0x1f, 0x00, 0x0a, 0x00, 0x9c, 0x40, 0x00, 0x5d, + 0x2a, 0x20, 0x54, 0x69, 0x6e, 0x6b, 0x6c, 0x65, 0x42, 0x65, 0x6c, 0x6c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x33, 0x19, 0xd4, 0x04, 0x04, 0x03, 0x00, 0x32, 0x11, 0x14, 0x05, + 0x06, 0x03, 0x00, 0x72, 0x10, 0xd6, 0x03, 0x08, 0x53, 0x00, 0x73, 0x0b, + 0x96, 0x07, 0x07, 0xb3, 0x00, 0x28, 0x99, 0x0b, 0xaa, 0x2a, 0x20, 0x41, + 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x3c, 0x00, 0x06, 0x00, + 0x1f, 0x1f, 0x0e, 0x68, 0x00, 0x0b, 0x00, 0x1f, 0x1f, 0x0e, 0x48, 0x00, + 0x0e, 0x0e, 0x9f, 0x0e, 0x0e, 0xa7, 0x00, 0x0e, 0x0e, 0x9f, 0x1f, 0x0f, + 0x07, 0x00, 0x03, 0x34, 0x01, 0x61, 0x2a, 0x20, 0x53, 0x74, 0x65, 0x65, + 0x6c, 0x44, 0x72, 0x75, 0x6d, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x1c, 0x41, 0x0d, 0x13, 0x00, + 0x57, 0x00, 0x35, 0x22, 0x0d, 0x10, 0x07, 0x87, 0x00, 0x71, 0x0c, 0x1a, + 0x0c, 0x00, 0xf6, 0x00, 0x71, 0x0c, 0x1a, 0x0e, 0x00, 0xf6, 0x00, 0x05, + 0xa6, 0x02, 0x22, 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xf4, 0x00, 0x3c, 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, + 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, + 0x00, 0x19, 0x05, 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x03, 0xd4, 0x00, 0xa6, + 0x2a, 0x20, 0x54, 0x61, 0x69, 0x6b, 0x6f, 0x20, 0x44, 0x72, 0x75, 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x06, 0x9f, 0x12, 0x12, 0x08, 0x00, 0x00, 0x0c, 0x1f, 0x10, + 0x10, 0x07, 0x00, 0x01, 0x04, 0x9f, 0x10, 0x10, 0x0b, 0x00, 0x00, 0x03, + 0x1f, 0x08, 0x0c, 0x95, 0x00, 0x12, 0x19, 0x03, 0x76, 0x2a, 0x20, 0x4d, + 0x65, 0x6c, 0x6f, 0x64, 0x69, 0x63, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x02, 0x10, + 0x1f, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x23, 0x1e, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x02, 0x1f, 0x02, 0x12, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x0e, + 0x06, 0x00, 0x03, 0x5c, 0x02, 0x44, 0x2a, 0x20, 0x53, 0x79, 0x6e, 0x44, + 0x72, 0x75, 0x6d, 0x20, 0x28, 0x41, 0x6c, 0x69, 0x73, 0x69, 0x61, 0x20, + 0x44, 0x72, 0x61, 0x67, 0x6f, 0x6f, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x01, 0x06, 0x1d, 0x12, 0x00, + 0x56, 0x00, 0x02, 0x0d, 0x5f, 0x1b, 0x08, 0x56, 0x00, 0x01, 0x08, 0x1f, + 0x1a, 0x16, 0x26, 0x00, 0x01, 0x00, 0x1f, 0x0e, 0x11, 0xc6, 0x00, 0x03, + 0x19, 0x01, 0xfa, 0x2a, 0x20, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, + 0x20, 0x43, 0x79, 0x6d, 0x62, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x10, 0x7f, 0x00, 0x1d, 0x01, 0x00, 0x80, 0x00, 0x7f, + 0x00, 0x1f, 0x00, 0x00, 0x14, 0x00, 0x13, 0x09, 0x44, 0x8d, 0x16, 0x09, + 0x00, 0x00, 0x1d, 0x84, 0x1b, 0x06, 0xa6, 0x00, 0x10, 0xc5, 0x00, 0x78, + 0x2a, 0x20, 0x47, 0x75, 0x69, 0x74, 0x61, 0x72, 0x20, 0x46, 0x72, 0x65, + 0x74, 0x20, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x10, 0x76, 0x0e, 0x1b, 0x01, 0x00, 0x80, 0x00, 0x73, 0x21, 0x1f, 0x00, + 0x00, 0x14, 0x00, 0x13, 0x11, 0x4e, 0x87, 0x0e, 0x09, 0x00, 0x04, 0x20, + 0x8c, 0x0e, 0x09, 0xa6, 0x00, 0x04, 0x2a, 0x01, 0x04, 0x2a, 0x20, 0x42, + 0x72, 0x65, 0x61, 0x74, 0x68, 0x20, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x3c, 0x00, 0x34, 0x00, + 0x1f, 0x00, 0x00, 0x22, 0x00, 0x54, 0x08, 0x1f, 0x00, 0x00, 0x22, 0x00, + 0x72, 0x0b, 0x0c, 0x0c, 0x07, 0xb7, 0x00, 0x32, 0x18, 0x0c, 0x89, 0x09, + 0x76, 0x00, 0x0f, 0x28, 0x01, 0x68, 0x2a, 0x20, 0x53, 0x65, 0x61, 0x53, + 0x68, 0x6f, 0x72, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x00, + 0xf0, 0x00, 0x01, 0x16, 0x06, 0x08, 0x00, 0xf4, 0x00, 0x01, 0x1a, 0x06, + 0x08, 0x00, 0xf4, 0x00, 0x01, 0x1c, 0x07, 0x08, 0x00, 0xf4, 0x00, 0x20, + 0x80, 0x07, 0xfe, 0x2a, 0x20, 0x42, 0x69, 0x72, 0x64, 0x54, 0x77, 0x65, + 0x65, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x00, 0x07, 0x19, 0xc6, 0x10, 0x0e, 0x07, 0x08, 0x15, + 0x11, 0x0f, 0x0d, 0x0e, 0x0a, 0x00, 0x08, 0x10, 0xc8, 0x0e, 0x10, 0x56, + 0x08, 0x06, 0x00, 0x8b, 0x0e, 0x10, 0xb6, 0x00, 0x05, 0x28, 0x01, 0xbe, + 0x2a, 0x20, 0x54, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x00, 0x3b, 0x20, 0x1f, 0x02, 0x00, 0xf8, 0x00, 0x00, 0x32, 0x1f, 0x02, + 0x00, 0xf8, 0x00, 0x3d, 0x00, 0x50, 0x11, 0x00, 0xf5, 0x0e, 0x06, 0x00, + 0x16, 0x13, 0x00, 0xf6, 0x0e, 0x9c, 0x40, 0x00, 0x42, 0x2a, 0x20, 0x48, + 0x65, 0x6c, 0x69, 0x63, 0x6f, 0x70, 0x74, 0x65, 0x72, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x34, 0x30, 0x30, 0x00, + 0x45, 0x01, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x05, 0x8d, 0x00, 0x09, 0x00, + 0x02, 0x14, 0x07, 0x82, 0x00, 0x15, 0x00, 0x00, 0x7f, 0x07, 0x8f, 0x00, + 0x04, 0x00, 0x9c, 0x40, 0x03, 0x5c, 0x2a, 0x20, 0x41, 0x70, 0x70, 0x6c, + 0x61, 0x75, 0x73, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x33, 0x04, 0x1f, 0x00, 0x00, + 0xf0, 0x00, 0x01, 0x16, 0x06, 0x0d, 0x00, 0x09, 0x00, 0x11, 0x17, 0x06, + 0x07, 0x00, 0x08, 0x00, 0x01, 0x15, 0x06, 0x0f, 0x00, 0x04, 0x00, 0x9c, + 0x40, 0x04, 0xf2, 0x2a, 0x20, 0x47, 0x75, 0x6e, 0x73, 0x68, 0x6f, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x3d, 0x05, 0x33, 0x04, 0x1f, 0x03, 0x11, 0xf5, 0x00, 0x01, + 0x00, 0x1f, 0x14, 0x14, 0xf8, 0x00, 0x01, 0x00, 0x1f, 0x0f, 0x1f, 0xf8, + 0x00, 0x01, 0x00, 0x1f, 0x0f, 0x10, 0xfb, 0x00, 0x02, 0x72, 0x00, 0xba, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x38, + 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x2f, 0x1f, 0x12, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0xff, 0x00, 0x01, 0x08, + 0x13, 0x14, 0x00, 0xfa, 0x00, 0x00, 0xa0, 0x00, 0x71, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x10, 0x00, 0x00, 0x0f, + 0x1f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x1b, 0x1f, 0x12, 0x00, 0xff, 0x00, + 0x00, 0x19, 0x9f, 0x00, 0x00, 0xff, 0x00, 0x01, 0x08, 0x13, 0x14, 0x00, + 0xfa, 0x00, 0x00, 0x92, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x53, 0x6c, + 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xe8, 0x00, 0x34, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x00, 0x04, 0x00, 0x05, + 0x00, 0x1f, 0x19, 0x00, 0xf6, 0x00, 0x00, 0x0e, 0x54, 0x1a, 0x11, 0x18, + 0x00, 0x03, 0x08, 0x94, 0x12, 0x00, 0xf8, 0x00, 0x01, 0x18, 0x01, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x02, 0x06, 0x1f, 0x12, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x13, + 0x00, 0xff, 0x00, 0x02, 0x21, 0x9f, 0x16, 0x00, 0xff, 0x00, 0x01, 0x00, + 0x9f, 0x15, 0x00, 0xff, 0x00, 0x00, 0x71, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x68, 0x6f, 0x77, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x20, 0x0f, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x65, 0x0d, + 0x0f, 0x08, 0x00, 0x38, 0x00, 0x9c, 0x40, 0x00, 0xf0, 0x57, 0x49, 0x50, + 0x20, 0x54, 0x68, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xd0, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x54, 0x1f, 0x0e, 0x07, 0x00, 0x01, 0x00, 0x1f, 0x00, 0x0a, 0x0a, 0x00, + 0x01, 0x00, 0x0f, 0x0b, 0x12, 0xa6, 0x00, 0x02, 0x26, 0x03, 0x1f, 0x0e, + 0xfe, 0x00, 0x06, 0x89, 0x01, 0x2c, 0x57, 0x49, 0x50, 0x20, 0x57, 0x69, + 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x20, 0x0f, 0x00, 0x1f, 0x80, 0x00, + 0x00, 0x00, 0x3f, 0x1a, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x19, 0x06, 0x00, 0x00, 0x05, 0x00, 0x9c, + 0x40, 0x04, 0x10, 0x57, 0x49, 0x50, 0x20, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x20, 0x0f, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x3f, + 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x65, 0x0d, 0x0a, 0x08, 0x00, 0x35, 0x00, 0x9c, 0x40, 0x02, 0x58, + 0x42, 0x75, 0x62, 0x62, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x43, 0x0f, + 0x00, 0x04, 0x0e, 0x5f, 0x0d, 0x06, 0xf4, 0x08, 0x04, 0x0e, 0x5f, 0x0f, + 0x06, 0xf4, 0x0e, 0x07, 0x0e, 0x5f, 0x0e, 0x06, 0xf4, 0x08, 0x04, 0x0e, + 0x5f, 0x0e, 0x06, 0xf4, 0x08, 0x9c, 0x40, 0x02, 0x3d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x06, 0x21, 0x71, 0x05, + 0x11, 0x80, 0x00, 0x17, 0x00, 0x31, 0x0b, 0x8f, 0x8a, 0x0e, 0x68, 0x0e, + 0x01, 0x17, 0x50, 0x1a, 0x0d, 0x65, 0x0e, 0x01, 0x15, 0x4b, 0x8a, 0x11, + 0x04, 0x0e, 0x9c, 0x40, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x6f, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xea, 0x3f, 0x3c, + 0x00, 0x36, 0x10, 0x1f, 0x16, 0x00, 0xf0, 0x0e, 0x35, 0x12, 0x1f, 0x18, + 0x00, 0x00, 0x00, 0x7c, 0x08, 0x0e, 0x12, 0x12, 0xf9, 0x00, 0x75, 0x0c, + 0x8e, 0x12, 0x12, 0xf9, 0x00, 0x01, 0x7c, 0x00, 0xa0, 0x48, 0x6f, 0x72, + 0x73, 0x65, 0x20, 0x47, 0x61, 0x6c, 0x6c, 0x6f, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x3d, 0x00, 0x00, 0x00, + 0x16, 0x11, 0x00, 0xf0, 0x08, 0x02, 0x11, 0x1f, 0x0d, 0x00, 0xf7, 0x08, + 0x02, 0x11, 0x1f, 0x0d, 0x00, 0xf7, 0x08, 0x02, 0x11, 0x1f, 0x0d, 0x00, + 0xf7, 0x08, 0x9c, 0x40, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x07, 0x19, 0xc6, 0x10, 0x0e, + 0x07, 0x00, 0x15, 0x11, 0x0f, 0x0d, 0x0e, 0x0a, 0x00, 0x08, 0x10, 0xc8, + 0x0e, 0x10, 0x56, 0x00, 0x06, 0x00, 0x8b, 0x0e, 0x10, 0xb6, 0x00, 0x05, + 0xdc, 0x03, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, + 0x17, 0x0e, 0x0f, 0x00, 0xf7, 0x00, 0x71, 0x00, 0x05, 0x06, 0x00, 0x06, + 0x00, 0x02, 0x00, 0x0b, 0x04, 0x0d, 0x07, 0x00, 0x04, 0xf9, 0x01, 0xd9, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x28, 0xc3, 0x03, 0x01, + 0x61, 0x00, 0x00, 0x23, 0x07, 0x09, 0x00, 0xf3, 0x00, 0x01, 0x06, 0x85, + 0x06, 0x02, 0x73, 0x00, 0x02, 0x1d, 0x0a, 0x06, 0x00, 0xf4, 0x00, 0x77, + 0xb0, 0x1a, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xe8, 0x00, 0x3c, 0x00, 0x74, 0x0d, 0x15, 0x00, 0x00, 0x07, 0x00, 0x02, + 0x17, 0x0d, 0x0f, 0x00, 0xf7, 0x00, 0x70, 0x18, 0x13, 0x0b, 0x0e, 0x06, + 0x08, 0x02, 0x00, 0x09, 0x03, 0x0d, 0x07, 0x00, 0x07, 0x86, 0x02, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x20, 0x43, 0x61, 0x6c, 0x6c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x00, 0x07, 0x19, 0xdf, 0x10, 0x0e, 0x07, 0x00, 0x15, 0x0f, 0x1f, 0x00, + 0x00, 0x0f, 0x00, 0x08, 0x10, 0xdf, 0x0e, 0x10, 0x5d, 0x00, 0x06, 0x0f, + 0x9f, 0x00, 0x10, 0x8f, 0x00, 0x9c, 0x40, 0x00, 0x56, 0x44, 0x6f, 0x6f, + 0x72, 0x53, 0x71, 0x65, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x3c, 0x00, 0x31, 0x00, + 0x5f, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x09, 0x00, + 0x01, 0x16, 0x0c, 0x03, 0x0e, 0x1a, 0x0f, 0x01, 0x24, 0x0a, 0x05, 0x0e, + 0x17, 0x0f, 0x04, 0x10, 0x00, 0x50, 0x44, 0x6f, 0x6f, 0x72, 0x53, 0x6c, + 0x61, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x05, 0x30, 0x04, 0x1f, 0x06, 0x10, + 0xf3, 0x00, 0x00, 0x00, 0x13, 0x12, 0x0e, 0xf9, 0x00, 0x00, 0x00, 0x0e, + 0x0f, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x10, 0x18, 0x0d, 0x76, 0x00, 0x03, + 0x05, 0x00, 0xad, 0x53, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x43, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x35, 0x05, 0x30, 0x04, 0x1f, 0x06, 0x10, 0xf3, 0x00, 0x00, + 0x00, 0x0e, 0x18, 0x0e, 0xfb, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x1f, 0xfb, + 0x00, 0x00, 0x00, 0x0d, 0x19, 0x0c, 0x78, 0x00, 0x03, 0xda, 0x01, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, + 0x37, 0x00, 0x08, 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x8e, 0x10, 0x18, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd9, 0x00, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x05, 0x18, + 0x1f, 0x08, 0x1a, 0x64, 0x08, 0x05, 0x1c, 0x1f, 0x09, 0x14, 0x50, 0x08, + 0x04, 0x09, 0x51, 0x0a, 0x1f, 0xa3, 0x08, 0x04, 0x0e, 0x51, 0x0c, 0x1e, + 0xa5, 0x08, 0x9c, 0x40, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x28, 0x1f, 0x0d, 0x1f, + 0x2f, 0x0a, 0x36, 0x7f, 0x1f, 0x00, 0x00, 0x0f, 0x0e, 0x04, 0x04, 0x1f, + 0x09, 0x00, 0x0f, 0x00, 0x32, 0x7f, 0x1f, 0x09, 0x13, 0x0f, 0x00, 0x9c, + 0x40, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x61, 0x72, 0x20, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x04, + 0x00, 0x00, 0x0a, 0x14, 0x1a, 0x11, 0x98, 0x0c, 0x03, 0x00, 0x10, 0x19, + 0x11, 0x98, 0x0c, 0x01, 0x08, 0x1b, 0x0d, 0x00, 0x38, 0x00, 0x31, 0x08, + 0x1c, 0x0b, 0x12, 0x2f, 0x00, 0x9c, 0x40, 0x00, 0xc1, 0x43, 0x61, 0x72, + 0x20, 0x53, 0x74, 0x6f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xee, 0x3e, 0x3c, 0x20, 0x7e, 0x15, + 0x0b, 0x80, 0x00, 0x00, 0x00, 0x3e, 0x16, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x08, 0x8e, 0x89, 0x00, 0xf7, 0x00, 0x40, 0x08, 0x8e, 0x89, 0x00, + 0xf7, 0x00, 0x09, 0xbd, 0x01, 0x5a, 0x43, 0x61, 0x72, 0x20, 0x50, 0x61, + 0x73, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xf4, 0x40, 0x33, 0x10, 0x06, 0x13, 0x18, 0x06, 0x04, + 0x16, 0x00, 0x35, 0x1c, 0x1a, 0x08, 0x03, 0x36, 0x00, 0x32, 0x23, 0x1a, + 0x07, 0x03, 0x36, 0x00, 0x72, 0x03, 0x09, 0x80, 0x0c, 0x07, 0x00, 0x07, + 0x9a, 0x01, 0xd9, 0x43, 0x61, 0x72, 0x20, 0x43, 0x72, 0x61, 0x73, 0x68, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4b, 0x3c, 0x20, 0x0f, 0x00, 0x1f, 0x00, 0x00, 0xf0, 0x08, 0x71, + 0x00, 0x1f, 0x16, 0x00, 0xf0, 0x0e, 0x00, 0x00, 0x1b, 0x8b, 0x00, 0xff, + 0x00, 0x30, 0x01, 0x1b, 0x8b, 0x00, 0xff, 0x00, 0x06, 0x90, 0x00, 0x56, + 0x53, 0x69, 0x72, 0x65, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, + 0x20, 0x08, 0x26, 0x11, 0x9f, 0x00, 0x07, 0x00, 0x76, 0x24, 0x0b, 0x9f, + 0x00, 0x07, 0x00, 0x02, 0x0b, 0x1f, 0x8f, 0x00, 0xf8, 0x0a, 0x33, 0x0a, + 0x1f, 0x8f, 0x00, 0xf9, 0x0a, 0x00, 0xa6, 0x00, 0x6a, 0x54, 0x72, 0x61, + 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x00, 0x30, 0x00, + 0x1f, 0x0f, 0x00, 0xf4, 0x08, 0x01, 0x26, 0x1f, 0x0d, 0x00, 0xf4, 0x08, + 0x30, 0x00, 0x1f, 0x0c, 0x00, 0xf4, 0x08, 0x70, 0x0c, 0x9f, 0x11, 0x01, + 0x17, 0x00, 0x39, 0x8d, 0x03, 0x8a, 0x4a, 0x65, 0x74, 0x70, 0x61, 0x6c, + 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x3e, 0x00, 0x01, 0x06, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x0c, 0x05, 0x00, 0x00, 0x05, 0x00, 0x71, 0x16, 0x05, + 0x00, 0x00, 0xf6, 0x00, 0x03, 0x0b, 0x05, 0x00, 0x00, 0x06, 0x00, 0x9c, + 0x40, 0x07, 0x36, 0x53, 0x74, 0x61, 0x72, 0x73, 0x68, 0x69, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x3c, 0x13, 0x3b, 0x1b, 0x1f, 0x00, 0x00, 0x03, 0x00, 0x7c, + 0x32, 0x1f, 0x00, 0x00, 0x03, 0x00, 0x32, 0x08, 0x05, 0x80, 0x00, 0x06, + 0x00, 0x73, 0x08, 0x05, 0x80, 0x00, 0x06, 0x00, 0x9c, 0x40, 0x03, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x3c, + 0x13, 0x3b, 0x22, 0x18, 0x08, 0x0f, 0x53, 0x08, 0x7c, 0x2c, 0x12, 0x05, + 0x0f, 0x23, 0x08, 0x32, 0x08, 0x0e, 0x80, 0x00, 0x06, 0x00, 0x73, 0x08, + 0x0f, 0x80, 0x00, 0x06, 0x00, 0x9c, 0x40, 0x02, 0x8d, 0x43, 0x61, 0x72, + 0x20, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x04, 0x00, 0x00, 0x0a, + 0x10, 0x1a, 0x11, 0x98, 0x0c, 0x03, 0x00, 0x10, 0x19, 0x11, 0x98, 0x0c, + 0x01, 0x08, 0x1d, 0x0d, 0x00, 0x38, 0x00, 0x31, 0x08, 0x17, 0x0b, 0x12, + 0x2f, 0x00, 0x9c, 0x40, 0x00, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xf4, 0x26, 0x0f, 0x00, 0x04, 0x0e, 0x5f, 0x0d, 0x06, + 0xf4, 0x08, 0x04, 0x0e, 0x5f, 0x0f, 0x06, 0xf4, 0x0e, 0x07, 0x0e, 0x5f, + 0x0e, 0x06, 0xf4, 0x08, 0x04, 0x0e, 0x5f, 0x0e, 0x06, 0xf4, 0x08, 0x9c, + 0x40, 0x02, 0xc9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x61, 0x75, 0x67, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, + 0x00, 0x75, 0x00, 0x11, 0x0a, 0x03, 0x17, 0x00, 0x31, 0x08, 0x8f, 0x12, + 0x06, 0x68, 0x00, 0x03, 0x28, 0x50, 0x10, 0x09, 0x67, 0x00, 0x01, 0x00, + 0x4b, 0x91, 0x10, 0x08, 0x0e, 0x09, 0x38, 0x00, 0x42, 0x53, 0x63, 0x72, + 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x1c, 0x12, 0x00, 0x1f, + 0x4c, 0x00, 0x00, 0x06, 0x00, 0x08, 0x20, 0x4c, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x08, 0x4c, 0x86, 0x10, 0x27, 0x00, 0x08, 0x08, 0x4c, 0x86, 0x10, + 0x27, 0x00, 0x05, 0xa0, 0x01, 0x75, 0x50, 0x75, 0x6e, 0x63, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x29, 0x3c, 0x00, 0x04, 0x03, 0x1f, 0x0d, 0x00, + 0xf7, 0x00, 0x30, 0x00, 0x54, 0x17, 0x00, 0xf9, 0x00, 0x01, 0x08, 0x5f, + 0x0e, 0x0d, 0x27, 0x00, 0x71, 0x08, 0x1f, 0x12, 0x00, 0xf9, 0x00, 0x03, + 0x55, 0x01, 0xbe, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xe8, 0x2c, 0x05, 0x00, 0x00, 0x00, 0x9f, 0x0c, 0x00, 0xf6, 0x08, 0x00, + 0x0e, 0x99, 0x0e, 0x0f, 0xf6, 0x08, 0x00, 0x08, 0x99, 0x13, 0x1e, 0xf6, + 0x00, 0x00, 0x08, 0x99, 0x10, 0x1f, 0xf6, 0x00, 0x01, 0xc5, 0x03, 0xf5, + 0x46, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x65, 0x70, 0x73, 0x20, 0x31, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x34, + 0x00, 0x04, 0x22, 0x1f, 0x15, 0x00, 0xc8, 0x00, 0x01, 0x09, 0x1f, 0x18, + 0x1f, 0xf8, 0x00, 0x01, 0x04, 0xdf, 0x10, 0x11, 0x18, 0x00, 0x02, 0x04, + 0xdf, 0x0a, 0x14, 0x18, 0x00, 0x00, 0x6a, 0x00, 0x5d, 0x41, 0x70, 0x70, + 0x6c, 0x61, 0x75, 0x73, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x38, 0x10, 0x39, 0x00, + 0x1f, 0x96, 0x00, 0xf0, 0x08, 0x01, 0x06, 0x16, 0x80, 0x00, 0xf1, 0x09, + 0x04, 0x10, 0x1e, 0x96, 0x00, 0xf1, 0x0c, 0x05, 0x0d, 0x03, 0x0f, 0x00, + 0x09, 0x00, 0x9c, 0x40, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x20, 0x47, 0x75, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x09, + 0x00, 0x04, 0x17, 0x1f, 0x01, 0x00, 0xfe, 0x0b, 0x03, 0x06, 0x1f, 0x17, + 0x00, 0xf0, 0x08, 0x01, 0x1c, 0x1f, 0x12, 0x00, 0xfc, 0x00, 0x02, 0x02, + 0x1f, 0x11, 0x18, 0xff, 0x08, 0x9c, 0x40, 0x00, 0x56, 0x4c, 0x61, 0x73, + 0x65, 0x72, 0x20, 0x47, 0x75, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x00, 0x0f, 0x07, + 0x10, 0x0d, 0x00, 0xf8, 0x00, 0x0c, 0x0a, 0x1f, 0x19, 0x00, 0xf8, 0x00, + 0x0b, 0x08, 0x10, 0x0e, 0x0f, 0x28, 0x00, 0x0c, 0x08, 0x1f, 0x0d, 0x0e, + 0x48, 0x00, 0x02, 0xea, 0x01, 0x04, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x73, + 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x3c, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x04, 0x1c, 0x09, 0x11, 0xf3, 0x00, 0x01, 0x04, 0x1f, + 0x0b, 0x0b, 0x36, 0x00, 0x3b, 0x00, 0x1c, 0x0d, 0x0b, 0x27, 0x00, 0x08, + 0x3a, 0x03, 0xcd, 0x46, 0x69, 0x72, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x73, + 0x20, 0x5b, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x50, 0x43, 0x4d, 0x5d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x3c, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, 0x93, 0x00, 0x00, + 0x04, 0x1c, 0x09, 0x11, 0xf3, 0x00, 0x01, 0x04, 0x1f, 0x0b, 0x0b, 0x36, + 0x00, 0x3b, 0x00, 0x1c, 0x0d, 0x0b, 0x27, 0x00, 0x08, 0x3a, 0x03, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2a, 0x20, 0x47, 0x72, 0x61, 0x6e, 0x64, 0x50, 0x69, 0x61, 0x6e, 0x6f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x01, 0x27, 0x5a, 0x07, 0x04, 0x71, 0x00, 0x64, 0x24, 0x58, 0x09, + 0x09, 0x67, 0x00, 0x72, 0x04, 0xdf, 0x17, 0x0f, 0x91, 0x00, 0x31, 0x02, + 0x9b, 0x04, 0x04, 0xa6, 0x00, 0x4e, 0xee, 0x03, 0x76, 0x2a, 0x20, 0x42, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x50, 0x69, 0x61, 0x6e, 0x6f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x24, 0x21, + 0x5b, 0x09, 0x05, 0xb6, 0x00, 0x54, 0x12, 0x5b, 0x09, 0x09, 0x77, 0x00, + 0x21, 0x08, 0x5b, 0x85, 0x05, 0xa6, 0x00, 0x56, 0x09, 0x5b, 0x09, 0x08, + 0x37, 0x00, 0x3c, 0xba, 0x03, 0xc6, 0x2a, 0x20, 0x45, 0x6c, 0x65, 0x63, + 0x74, 0x72, 0x69, 0x63, 0x50, 0x69, 0x61, 0x6e, 0x6f, 0x20, 0x28, 0x41, + 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x71, 0x21, 0x58, 0x05, 0x06, + 0x64, 0x00, 0x42, 0x1a, 0x56, 0x07, 0x06, 0x64, 0x00, 0x01, 0x06, 0x99, + 0x05, 0x04, 0x15, 0x00, 0x31, 0x09, 0x99, 0x84, 0x04, 0x65, 0x00, 0x37, + 0x92, 0x07, 0x01, 0x2a, 0x20, 0x48, 0x6f, 0x6e, 0x6b, 0x79, 0x54, 0x6f, + 0x6e, 0x6b, 0x20, 0x28, 0x53, 0x6f, 0x6e, 0x69, 0x63, 0x4d, 0x6f, 0x64, + 0x64, 0x65, 0x64, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x32, 0x23, 0x1f, 0x0a, 0x05, 0xf7, 0x00, 0x02, + 0x0e, 0x19, 0x06, 0x06, 0x27, 0x00, 0x04, 0x07, 0x19, 0x07, 0x00, 0xd7, + 0x00, 0x31, 0x10, 0x19, 0x09, 0x00, 0x27, 0x00, 0x9c, 0x40, 0x01, 0x6e, + 0x2a, 0x20, 0x52, 0x68, 0x6f, 0x64, 0x65, 0x73, 0x50, 0x69, 0x61, 0x6e, + 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, + 0x00, 0x4c, 0x39, 0x5f, 0x07, 0x00, 0xb8, 0x00, 0x41, 0x22, 0x96, 0x05, + 0x00, 0xb8, 0x00, 0x01, 0x09, 0x9f, 0x84, 0x04, 0x18, 0x00, 0x01, 0x09, + 0x9f, 0x84, 0x04, 0x68, 0x00, 0x52, 0x9a, 0x01, 0x11, 0x2a, 0x20, 0x43, + 0x68, 0x6f, 0x72, 0x75, 0x73, 0x50, 0x69, 0x61, 0x6e, 0x6f, 0x28, 0x54, + 0x69, 0x6e, 0x69, 0x54, 0x6f, 0x6f, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x7a, 0x1e, + 0x1f, 0x0a, 0x07, 0xf6, 0x00, 0x51, 0x0a, 0x59, 0x05, 0x02, 0xf6, 0x00, + 0x32, 0x0c, 0x1f, 0x0d, 0x00, 0xf8, 0x00, 0x11, 0x14, 0x19, 0x0a, 0x02, + 0xf6, 0x00, 0x3a, 0xe8, 0x03, 0xee, 0x2a, 0x20, 0x48, 0x61, 0x72, 0x70, + 0x73, 0x69, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x20, 0x28, 0x41, 0x64, 0x61, + 0x6d, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x39, 0x00, 0x16, 0x26, 0x9f, 0x80, 0x01, + 0x05, 0x00, 0x50, 0x23, 0xdf, 0x80, 0x00, 0x02, 0x00, 0x3a, 0x21, 0xdf, + 0x00, 0x01, 0x06, 0x00, 0x70, 0x06, 0x1f, 0x06, 0x06, 0x47, 0x00, 0x31, + 0x24, 0x02, 0x1c, 0x2a, 0x20, 0x43, 0x6c, 0x61, 0x76, 0x69, 0x6e, 0x65, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x39, 0x00, 0x11, 0x1c, 0x9f, 0x80, 0x01, 0x06, 0x00, 0x51, + 0x1e, 0xdf, 0x80, 0x00, 0x02, 0x00, 0x31, 0x21, 0x9f, 0x00, 0x01, 0x07, + 0x00, 0x71, 0x06, 0x1f, 0x06, 0x06, 0x47, 0x00, 0x2e, 0xcc, 0x02, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2a, 0x20, 0x56, 0x69, 0x62, 0x72, 0x61, 0x70, 0x68, + 0x6f, 0x6e, 0x65, 0x20, 0x28, 0x4e, 0x69, 0x6e, 0x65, 0x6b, 0x6f, 0x29, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x20, 0x38, 0x28, 0x59, 0x0f, 0x06, 0xa6, 0x00, 0x7a, + 0x23, 0x5f, 0x10, 0x06, 0x66, 0x00, 0x01, 0x04, 0x59, 0x86, 0x05, 0xa6, + 0x00, 0x34, 0x04, 0x9c, 0x0a, 0x05, 0x65, 0x00, 0x33, 0x6e, 0x06, 0x7c, + 0x2a, 0x20, 0x4d, 0x61, 0x72, 0x69, 0x6d, 0x62, 0x61, 0x20, 0x28, 0x41, + 0x6c, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x45, 0x1b, 0x1f, 0x93, 0x07, 0x7e, 0x00, 0x22, 0x28, 0x9f, 0x92, + 0x04, 0x82, 0x00, 0x31, 0x00, 0x1e, 0x0f, 0x07, 0xf7, 0x00, 0x01, 0x0b, + 0x1f, 0x0f, 0x02, 0xf7, 0x00, 0x02, 0x5e, 0x02, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x73, 0x73, 0x65, + 0x6d, 0x62, 0x6c, 0x65, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x13, 0x31, 0x1d, 0xce, 0x0b, 0x00, 0x04, 0x00, 0x01, 0x1c, 0x8e, 0x8a, + 0x00, 0x15, 0x00, 0x05, 0x06, 0x0e, 0x8a, 0x00, 0x28, 0x00, 0x01, 0x00, + 0x0f, 0x0a, 0x00, 0x17, 0x00, 0x9c, 0x40, 0x02, 0x0e, 0x2a, 0x20, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, + 0x65, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x11, 0x31, 0x1d, + 0xcb, 0x0b, 0x00, 0x01, 0x00, 0x01, 0x1c, 0x8b, 0x8a, 0x00, 0x11, 0x00, + 0x01, 0x06, 0x0a, 0x08, 0x00, 0x26, 0x00, 0x01, 0x03, 0x0a, 0x08, 0x00, + 0x15, 0x00, 0x9c, 0x40, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2a, 0x20, 0x43, 0x68, 0x6f, 0x69, 0x72, 0x20, 0x41, 0x61, 0x68, 0x73, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, + 0x04, 0x30, 0x2a, 0x51, 0x80, 0x00, 0x51, 0x00, 0x70, 0x1e, 0x8d, 0x00, + 0x00, 0x51, 0x00, 0x30, 0x08, 0x4d, 0x80, 0x00, 0x05, 0x00, 0x70, 0x08, + 0x8b, 0x80, 0x00, 0x05, 0x00, 0x9c, 0x40, 0x06, 0x54, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x20, 0x42, + 0x72, 0x61, 0x73, 0x73, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x28, 0x47, 0x72, 0x65, 0x65, 0x6e, 0x64, 0x6f, 0x67, 0x29, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x35, 0x12, 0x21, 0x1a, + 0x8e, 0x00, 0x01, 0x46, 0x00, 0x20, 0x09, 0x95, 0x00, 0x02, 0x15, 0x00, + 0x31, 0x00, 0x9b, 0x05, 0x02, 0x36, 0x00, 0x14, 0x06, 0x94, 0x80, 0x02, + 0x08, 0x00, 0x9c, 0x40, 0x04, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x69, 0x63, 0x6b, 0x20, 0x42, + 0x61, 0x73, 0x73, 0x20, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x09, 0x1e, 0x1f, 0x15, 0x00, + 0x8f, 0x00, 0x30, 0x0e, 0x5f, 0x0d, 0x04, 0x2f, 0x00, 0x70, 0x4b, 0x1f, + 0x0e, 0x04, 0x3f, 0x00, 0x00, 0x00, 0x5f, 0x0a, 0x03, 0x2f, 0x00, 0x71, + 0xda, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4d, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x53, 0x79, 0x6e, + 0x74, 0x68, 0x20, 0x42, 0x61, 0x73, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x20, 0x00, 0x36, 0x19, 0xd2, 0x07, 0x07, 0x20, 0x00, 0x30, + 0x13, 0x9f, 0x09, 0x06, 0x17, 0x00, 0x35, 0x54, 0xd7, 0x05, 0x06, 0x16, + 0x00, 0x31, 0x00, 0x9f, 0x06, 0x08, 0xf6, 0x00, 0x1b, 0xc9, 0x02, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x72, 0x65, 0x6e, 0x63, 0x68, 0x20, 0x48, 0x6f, 0x72, 0x6e, 0x20, + 0x53, 0x6f, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x3d, + 0x12, 0x00, 0x20, 0x8e, 0x06, 0x01, 0x66, 0x00, 0x00, 0x03, 0x8d, 0x0d, + 0x0a, 0x25, 0x00, 0x00, 0x04, 0x8e, 0x87, 0x02, 0x15, 0x00, 0x50, 0x05, + 0x91, 0x87, 0x00, 0x16, 0x00, 0x9c, 0x40, 0x02, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x71, 0x75, 0x61, 0x72, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x10, 0x02, 0x19, 0x5f, 0x00, 0x01, 0x8b, 0x00, 0x02, 0x7f, 0x1a, 0x80, + 0x00, 0x2f, 0x00, 0x01, 0x04, 0x1c, 0x00, 0x00, 0x1a, 0x00, 0x01, 0x7f, + 0x5b, 0x80, 0x00, 0x0f, 0x00, 0x9c, 0x40, 0x00, 0x7e, 0x53, 0x61, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x01, 0x1d, + 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x31, 0x7f, 0x1f, 0x00, 0x00, 0x0f, 0x00, + 0x02, 0x04, 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x32, 0x7f, 0x1f, 0x00, 0x00, + 0x0f, 0x00, 0x9c, 0x40, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x6c, 0x6f, 0x77, 0x20, 0x76, 0x69, 0x6f, 0x6c, 0x69, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, + 0x23, 0x31, 0x1e, 0x0a, 0x04, 0x00, 0x15, 0x00, 0x70, 0x31, 0x0c, 0x09, + 0x03, 0x07, 0x00, 0x37, 0x1f, 0x05, 0x10, 0x00, 0x04, 0x00, 0x02, 0x03, + 0x87, 0x0a, 0x00, 0x15, 0x00, 0x9c, 0x40, 0x06, 0x75, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x6c, 0x6f, 0x77, 0x20, 0x74, 0x72, 0x65, 0x6d, 0x6f, 0x6c, 0x6f, + 0x20, 0x73, 0x74, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x25, 0x31, 0x15, 0xcd, 0x81, 0x00, 0x10, 0x00, 0x01, 0x13, 0x8d, 0x0b, + 0x00, 0x41, 0x00, 0x01, 0x06, 0x08, 0x0a, 0x00, 0x26, 0x00, 0x01, 0x00, + 0x06, 0x0a, 0x00, 0x16, 0x00, 0x9c, 0x40, 0x03, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x6c, 0x6f, 0x77, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x13, 0x31, 0x1d, 0xce, 0x0b, 0x00, 0x04, 0x00, 0x01, 0x1c, 0x8e, 0x8a, + 0x00, 0x15, 0x00, 0x05, 0x06, 0x0c, 0x8a, 0x00, 0x28, 0x00, 0x01, 0x00, + 0x0c, 0x0a, 0x00, 0x18, 0x00, 0x9c, 0x40, 0x01, 0x1e, 0x4c, 0x65, 0x67, + 0x61, 0x74, 0x6f, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x11, 0x31, 0x1d, + 0xcb, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x8b, 0x8a, 0x00, 0x10, 0x00, + 0x01, 0x06, 0x07, 0x08, 0x00, 0x25, 0x00, 0x01, 0x03, 0x07, 0x08, 0x00, + 0x15, 0x00, 0x9c, 0x40, 0x07, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x4d, 0x53, 0x71, 0x75, 0x61, 0x72, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x10, 0x32, 0x29, 0x5f, 0x80, 0x01, 0x8b, 0x00, 0x02, 0x2a, 0x1a, 0x80, + 0x00, 0x2a, 0x00, 0x71, 0x09, 0x17, 0x00, 0x00, 0x1a, 0x00, 0x01, 0x0d, + 0x4f, 0x80, 0x00, 0x0f, 0x00, 0x9c, 0x40, 0x00, 0x71, 0x54, 0x68, 0x69, + 0x63, 0x6b, 0x53, 0x61, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x71, 0x1c, + 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x31, 0x1e, 0x1f, 0x00, 0x00, 0x0f, 0x00, + 0x72, 0x04, 0x0f, 0x00, 0x00, 0x0d, 0x00, 0x32, 0x04, 0x0f, 0x00, 0x00, + 0x08, 0x00, 0x9c, 0x40, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, + 0x4c, 0x65, 0x64, 0x20, 0x53, 0x6c, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xe8, 0x00, 0x3c, 0x00, 0x34, 0x1b, 0x5b, 0x05, 0x03, + 0x07, 0x00, 0x73, 0x11, 0x58, 0x00, 0x03, 0x06, 0x00, 0x74, 0x00, 0x4c, + 0x05, 0x03, 0x07, 0x00, 0x33, 0x0c, 0x4f, 0x00, 0x03, 0x06, 0x00, 0x84, + 0x01, 0x03, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x63, 0x68, 0x6f, 0x20, 0x50, + 0x61, 0x64, 0x20, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x04, 0x32, 0x29, 0x57, 0x80, 0x00, + 0x55, 0x00, 0x02, 0x09, 0x98, 0x08, 0x07, 0x05, 0x0d, 0x02, 0x0c, 0x5f, + 0x80, 0x0c, 0x04, 0x0d, 0x71, 0x10, 0x94, 0x80, 0x00, 0x04, 0x00, 0x9c, + 0x40, 0x0a, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x7a, 0x1e, + 0x1f, 0x0a, 0x0e, 0xf8, 0x00, 0x51, 0x0a, 0x59, 0x09, 0x0a, 0xf9, 0x00, + 0x32, 0x0c, 0x1f, 0x11, 0x0c, 0xfa, 0x00, 0x11, 0x14, 0x19, 0x0b, 0x0a, + 0xf7, 0x00, 0x0f, 0x2e, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x20, 0x44, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x4f, 0x50, 0x4c, 0x33, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x24, 0x9f, 0x0e, 0x00, + 0x8c, 0x00, 0x21, 0x1a, 0x53, 0x08, 0x00, 0xb8, 0x00, 0x02, 0x19, 0x92, + 0x05, 0x00, 0xf7, 0x00, 0x02, 0x00, 0x91, 0x04, 0x00, 0x46, 0x00, 0x9c, + 0x40, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x53, 0x65, 0x71, 0x20, 0x42, 0x61, 0x73, 0x73, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x20, 0x00, 0x36, 0x1b, 0xdf, 0x07, 0x07, 0x20, 0x00, 0x30, + 0x13, 0x9f, 0x09, 0x0c, 0x17, 0x00, 0x35, 0x43, 0xdf, 0x08, 0x06, 0x16, + 0x00, 0x31, 0x00, 0x9f, 0x0b, 0x08, 0xf6, 0x00, 0x05, 0x0d, 0x02, 0x79, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4c, 0x6f, 0x46, 0x69, 0x20, 0x48, 0x69, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x18, 0x50, 0x0c, 0x00, 0xf5, 0x00, 0x01, + 0x23, 0x12, 0x05, 0x00, 0xf2, 0x00, 0x01, 0x1d, 0x16, 0x09, 0x00, 0xf3, + 0x00, 0x01, 0x00, 0x52, 0x0a, 0x00, 0xf5, 0x00, 0x0c, 0x51, 0x07, 0xd0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x01, 0x13, 0x1f, 0x06, 0x00, + 0xf6, 0x00, 0x71, 0x17, 0x1f, 0x00, 0x00, 0x0c, 0x00, 0x31, 0x1f, 0x1f, + 0x00, 0x00, 0x0d, 0x00, 0x01, 0x03, 0x1f, 0x00, 0x00, 0x08, 0x00, 0x9c, + 0x40, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x79, 0x6e, 0x44, 0x72, 0x75, + 0x6d, 0x20, 0x43, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x01, 0x1c, 0x1b, 0x00, 0x0d, + 0x0b, 0x00, 0x01, 0x29, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x01, 0x19, 0x17, + 0x02, 0x05, 0x0b, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x0f, 0x06, 0x00, 0x02, + 0x6c, 0x04, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x35, 0x12, 0x21, 0x17, + 0x8e, 0x00, 0x01, 0x47, 0x00, 0x20, 0x03, 0x95, 0x00, 0x02, 0x17, 0x00, + 0x31, 0x00, 0x9b, 0x05, 0x02, 0x36, 0x00, 0x14, 0x06, 0x94, 0x80, 0x02, + 0x08, 0x00, 0x9c, 0x40, 0x01, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x6f, 0x70, 0x63, 0x6f, 0x72, + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x45, 0x47, 0x1f, 0x93, 0x07, + 0x7e, 0x00, 0x22, 0x28, 0x9f, 0x92, 0x04, 0x82, 0x00, 0x31, 0x00, 0x14, + 0x12, 0x1e, 0xfa, 0x00, 0x01, 0x00, 0x19, 0x13, 0x18, 0xfb, 0x00, 0x01, + 0x04, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x04, 0x32, 0x29, 0x5f, 0x80, 0x00, + 0x55, 0x00, 0x02, 0x09, 0x9f, 0x08, 0x07, 0x05, 0x00, 0x02, 0x0c, 0x5f, + 0x80, 0x0e, 0x04, 0x0d, 0x71, 0x10, 0x9f, 0x80, 0x00, 0x04, 0x00, 0x9c, + 0x40, 0x0a, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4e, 0x79, 0x6c, 0x6f, 0x6e, 0x47, 0x75, 0x69, 0x74, 0x32, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x20, + 0x00, 0x05, 0x31, 0x1f, 0x12, 0x00, 0x27, 0x00, 0x31, 0x28, 0x1f, 0x0e, + 0x04, 0x27, 0x00, 0x30, 0x0f, 0x1f, 0x0a, 0x04, 0x27, 0x00, 0x00, 0x00, + 0x5f, 0x0a, 0x03, 0x27, 0x00, 0x72, 0x10, 0x01, 0xd9, 0x53, 0x74, 0x65, + 0x65, 0x6c, 0x20, 0x67, 0x75, 0x69, 0x74, 0x61, 0x72, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x05, 0x1d, + 0x1f, 0x12, 0x00, 0x27, 0x00, 0x31, 0x28, 0x1f, 0x0e, 0x04, 0x27, 0x00, + 0x30, 0x10, 0x1f, 0x0a, 0x04, 0x27, 0x00, 0x00, 0x00, 0x5f, 0x0a, 0x03, + 0x27, 0x00, 0x73, 0x2e, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x68, 0x6f, 0x69, 0x72, 0x20, 0x41, 0x61, 0x68, 0x73, 0x20, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, + 0x04, 0x30, 0x25, 0x51, 0x80, 0x00, 0x51, 0x00, 0x70, 0x1a, 0x8d, 0x00, + 0x00, 0x51, 0x00, 0x30, 0x08, 0x4d, 0x80, 0x00, 0x05, 0x00, 0x70, 0x08, + 0x8b, 0x80, 0x00, 0x05, 0x00, 0x9c, 0x40, 0x05, 0xe2, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x72, 0x75, 0x6d, 0x70, 0x65, 0x74, 0x20, 0x32, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x3d, + 0x00, 0x02, 0x18, 0x90, 0x0e, 0x01, 0x14, 0x00, 0x02, 0x15, 0x13, 0x0e, + 0x00, 0x19, 0x00, 0x04, 0x0a, 0x10, 0x0b, 0x00, 0xfd, 0x00, 0x02, 0x09, + 0x14, 0x0d, 0x00, 0x0e, 0x00, 0x9c, 0x40, 0x00, 0x85, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x75, 0x62, 0x61, 0x20, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x3d, 0x12, 0x00, 0x1c, 0x8e, 0x06, 0x01, + 0x66, 0x00, 0x01, 0x11, 0x8d, 0x0d, 0x0a, 0x25, 0x00, 0x00, 0x03, 0x8e, + 0x87, 0x02, 0x15, 0x00, 0x51, 0x17, 0x91, 0x87, 0x00, 0x16, 0x00, 0x9c, + 0x40, 0x01, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x42, 0x69, 0x67, 0x26, 0x4c, 0x6f, 0x77, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x00, 0x71, 0x1f, 0x1f, 0x03, 0x00, 0x4f, 0x00, 0x31, + 0x20, 0x1f, 0x06, 0x00, 0x5f, 0x00, 0x72, 0x04, 0x1f, 0x07, 0x00, 0xaf, + 0x00, 0x32, 0x04, 0x1f, 0x03, 0x00, 0x4f, 0x00, 0x9c, 0x40, 0x00, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, + 0x63, 0x6b, 0x50, 0x61, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x04, 0x00, 0x26, + 0x48, 0x80, 0x00, 0x52, 0x00, 0x00, 0x11, 0x83, 0x00, 0x00, 0x53, 0x00, + 0x00, 0x0c, 0x47, 0x80, 0x00, 0x05, 0x00, 0x00, 0x0e, 0x86, 0x80, 0x00, + 0x04, 0x00, 0x9c, 0x40, 0x0e, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x72, 0x69, 0x74, 0x65, 0x54, 0x72, 0x75, 0x6d, 0x70, 0x65, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x00, 0x3d, + 0x00, 0x02, 0x18, 0x90, 0x0e, 0x01, 0x14, 0x00, 0x02, 0x12, 0x13, 0x0e, + 0x00, 0x19, 0x00, 0x04, 0x0a, 0x10, 0x0b, 0x00, 0xfd, 0x00, 0x02, 0x09, + 0x14, 0x0d, 0x00, 0x0e, 0x00, 0x9c, 0x40, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x6f, 0x66, + 0x74, 0x50, 0x61, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x04, 0x00, 0x3b, + 0x48, 0x80, 0x00, 0x52, 0x00, 0x00, 0x1d, 0x83, 0x00, 0x00, 0x53, 0x00, + 0x00, 0x0c, 0x47, 0x80, 0x00, 0x05, 0x00, 0x00, 0x0e, 0x86, 0x80, 0x00, + 0x04, 0x00, 0x9c, 0x40, 0x0b, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, + 0x00, 0x01, 0x01, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, 0xe6, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x3d, + 0x05, 0x33, 0x04, 0xdf, 0x06, 0x10, 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, + 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, 0x10, 0x1f, 0xf8, 0x00, 0x01, 0x00, + 0x9f, 0x10, 0x0e, 0x76, 0x00, 0x01, 0x11, 0x00, 0xdc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, 0x37, 0x00, 0x08, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x8e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd9, 0x00, 0x99, 0x2a, 0x20, 0x43, 0x61, 0x73, 0x74, + 0x61, 0x6e, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, 0x1f, 0x0b, 0x00, + 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, 0x04, 0x0d, 0x9f, + 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x00, + 0x64, 0x00, 0x14, 0x2a, 0x20, 0x53, 0x74, 0x69, 0x63, 0x6b, 0x73, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x36, 0x18, 0x00, 0x02, 0x0a, 0x1f, 0x14, 0x00, 0xff, 0x00, 0x02, + 0x17, 0x1f, 0x12, 0x00, 0xff, 0x00, 0x02, 0x2a, 0x9f, 0x14, 0x00, 0xff, + 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x1a, + 0x2a, 0x20, 0x53, 0x71, 0x75, 0x61, 0x72, 0x65, 0x20, 0x43, 0x6c, 0x69, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x1f, + 0x00, 0x01, 0x27, 0x1f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x16, + 0x00, 0xff, 0x00, 0x01, 0x27, 0x9f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, + 0x12, 0x18, 0x00, 0xff, 0x00, 0x00, 0x71, 0x00, 0x50, 0x2a, 0x20, 0x4d, + 0x65, 0x74, 0x72, 0x6f, 0x6e, 0x6f, 0x6d, 0x65, 0x43, 0x6c, 0x69, 0x63, + 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x18, 0x00, 0x02, 0x06, + 0x1f, 0x12, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x16, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, + 0xff, 0x00, 0x00, 0x85, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2e, 0x3d, 0x05, 0x33, 0x04, 0xdf, 0x06, 0x10, + 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x10, 0x1f, 0xf8, 0x00, 0x01, 0x00, 0x9f, 0x10, 0x0f, 0x78, 0x00, 0x00, + 0xf6, 0x00, 0x71, 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, + 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x23, 0x22, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x14, 0x07, 0x00, 0x05, + 0x7f, 0x00, 0x0b, 0x0f, 0xf5, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0c, 0xf6, + 0x00, 0x01, 0x00, 0x5f, 0x00, 0x0e, 0x07, 0x00, 0x02, 0xb5, 0x00, 0xf6, + 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, 0x6d, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x1a, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x14, 0x07, 0x00, 0x05, 0x7f, 0x00, 0x0b, + 0x0f, 0xf4, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0d, 0xf6, 0x00, 0x01, 0x00, + 0x5f, 0x00, 0x0e, 0x07, 0x00, 0x02, 0xb5, 0x01, 0xe6, 0x2a, 0x20, 0x53, + 0x74, 0x69, 0x63, 0x6b, 0x53, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x02, 0x15, + 0x1f, 0x13, 0x00, 0xff, 0x00, 0x01, 0x0e, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0xa0, 0x00, 0x1a, 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x3c, 0x00, 0x11, 0x00, 0x14, 0x00, 0x05, + 0x00, 0x00, 0x02, 0x00, 0x1f, 0x17, 0x18, 0x09, 0x00, 0x10, 0x00, 0x1d, + 0x1f, 0x0f, 0x07, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x11, 0x09, 0x00, 0x01, + 0xed, 0x01, 0xb8, 0x2a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x43, 0x6c, 0x61, + 0x70, 0x20, 0x28, 0x4f, 0x50, 0x4c, 0x33, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3c, 0x00, 0x0f, 0x0d, 0x9f, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x03, 0x1f, 0x14, 0x10, 0x08, 0x00, 0x0f, 0x1b, 0x1f, 0x11, 0x0f, 0x27, + 0x00, 0x01, 0x00, 0x1f, 0x03, 0x12, 0x09, 0x00, 0x00, 0xf6, 0x00, 0x71, + 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x3c, + 0x00, 0x10, 0x00, 0x14, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x1f, 0x17, + 0x18, 0x09, 0x00, 0x10, 0x00, 0x1d, 0x10, 0x0e, 0xa7, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x11, 0x09, 0x00, 0x01, 0xb1, 0x01, 0xcc, 0x2a, 0x20, 0x4c, + 0x6f, 0x77, 0x20, 0x46, 0x6c, 0x6f, 0x6f, 0x72, 0x20, 0x54, 0x6f, 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x3d, 0x00, 0x31, 0x15, + 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x13, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, + 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x06, 0x1e, 0x02, 0xa8, 0x2a, 0x20, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x3a, 0x33, 0x3f, 0x19, 0x1f, 0x8e, 0x0d, + 0x84, 0x00, 0x33, 0x00, 0x5f, 0x0a, 0x08, 0x64, 0x00, 0x7f, 0x14, 0x1f, + 0x8e, 0x0d, 0x94, 0x00, 0x71, 0x00, 0xdf, 0x09, 0x09, 0x25, 0x00, 0x00, + 0xfd, 0x00, 0x64, 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x46, 0x6c, + 0x6f, 0x6f, 0x72, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x3d, 0x00, 0x31, 0x16, 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, + 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x0e, 0xf6, + 0x00, 0x10, 0x03, 0x1e, 0x11, 0x09, 0xf6, 0x00, 0x06, 0x25, 0x02, 0xb5, + 0x2a, 0x20, 0x50, 0x65, 0x64, 0x61, 0x6c, 0x48, 0x69, 0x68, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x38, + 0x00, 0x3c, 0x00, 0x47, 0x0a, 0x1c, 0xb4, 0x00, 0x13, 0x00, 0xdf, 0x00, + 0x00, 0x04, 0x00, 0x7a, 0x00, 0xc3, 0x8a, 0x10, 0xb7, 0x00, 0x75, 0x07, + 0xc4, 0x0d, 0x1d, 0x59, 0x00, 0x00, 0x85, 0x00, 0x42, 0x2a, 0x20, 0x4c, + 0x6f, 0x77, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3d, 0x00, 0x31, 0x16, + 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, + 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x06, 0x1e, 0x02, 0x22, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, + 0x48, 0x69, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x2c, 0x00, 0x09, 0x00, 0x1f, 0x0e, 0x00, + 0x10, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x1f, + 0x17, 0x08, 0x44, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, + 0x9e, 0x04, 0x1d, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x2d, 0x6d, 0x65, 0x64, + 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1d, 0x3d, 0x00, 0x30, 0x16, 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, + 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, + 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x06, 0x11, 0x02, 0x44, + 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, 0x2d, 0x6d, 0x65, 0x64, 0x20, 0x54, + 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, + 0x00, 0x30, 0x16, 0x1e, 0x18, 0x13, 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, + 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, + 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x04, 0xde, 0x02, 0x94, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, + 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x10, 0x7f, 0x00, + 0x1f, 0x0b, 0x00, 0x40, 0x00, 0x71, 0x00, 0x1f, 0x16, 0x07, 0x34, 0x00, + 0x13, 0x00, 0x1f, 0x8b, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x9f, 0x1b, 0x07, + 0xa6, 0x00, 0x07, 0x44, 0x01, 0xd9, 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, + 0x20, 0x54, 0x6f, 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x3d, 0x00, 0x31, 0x16, 0x1e, 0x1a, 0x03, + 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, + 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x04, + 0xd8, 0x02, 0x44, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0d, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x18, 0x15, 0x07, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0xea, 0x05, 0x78, + 0x2a, 0x20, 0x43, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x20, 0x43, 0x72, + 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, + 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2c, + 0x00, 0x7f, 0x00, 0x1f, 0x07, 0x00, 0x52, 0x00, 0x71, 0x00, 0x1f, 0x15, + 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0c, 0x04, 0x84, 0x00, 0x07, 0x00, + 0x9f, 0x19, 0x06, 0xa3, 0x00, 0x25, 0x44, 0x07, 0x1c, 0x2a, 0x20, 0x52, + 0x69, 0x64, 0x65, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x1c, 0x00, 0x0d, 0x00, + 0x1f, 0x07, 0x07, 0xe1, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x11, 0x1f, 0x0a, 0x12, 0xe5, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x3a, 0x03, 0x2d, 0x2a, 0x20, 0x54, 0x61, 0x6d, 0x62, + 0x6f, 0x72, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x35, 0x22, 0x06, 0x5f, 0x05, 0x02, + 0x81, 0x00, 0x75, 0x00, 0x96, 0x0f, 0x0c, 0xa1, 0x00, 0x04, 0x01, 0x9f, + 0x05, 0x02, 0x53, 0x00, 0x3e, 0x08, 0x92, 0x0d, 0x0d, 0xa6, 0x00, 0x01, + 0x89, 0x00, 0xc1, 0x2a, 0x20, 0x53, 0x70, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x43, 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, + 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x2c, 0x00, 0x7f, 0x00, 0x1f, 0x05, 0x06, 0x50, 0x00, 0x71, + 0x00, 0x1f, 0x15, 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0b, 0x04, 0x85, + 0x00, 0x07, 0x00, 0x9f, 0x1a, 0x06, 0xa5, 0x00, 0x21, 0xcd, 0x03, 0x20, + 0x2a, 0x20, 0x43, 0x6f, 0x77, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3b, + 0x00, 0x2f, 0x00, 0x1f, 0x15, 0x13, 0x26, 0x00, 0x37, 0x20, 0x1f, 0x15, + 0x0d, 0x36, 0x00, 0x28, 0x23, 0x1f, 0x95, 0x0c, 0x26, 0x00, 0x32, 0x00, + 0x1f, 0x13, 0x10, 0x28, 0x00, 0x01, 0x61, 0x00, 0x99, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x32, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x2c, 0x00, 0x7f, 0x00, + 0x1f, 0x00, 0x00, 0x50, 0x00, 0x71, 0x00, 0x1f, 0x14, 0x06, 0x33, 0x00, + 0x13, 0x00, 0x1f, 0x0a, 0x04, 0x84, 0x00, 0x07, 0x00, 0x9f, 0x19, 0x06, + 0xa4, 0x00, 0x20, 0x5e, 0x06, 0x11, 0x2a, 0x20, 0x56, 0x69, 0x62, 0x65, + 0x53, 0x6c, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x34, 0x00, 0x00, 0x08, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x0a, 0x00, 0xf5, 0x00, 0x00, 0x15, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x0c, + 0xea, 0x03, 0xac, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0c, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x18, 0x14, 0x08, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0e, 0x05, 0x85, + 0x2a, 0x20, 0x48, 0x69, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, + 0x12, 0x12, 0x0a, 0xf9, 0x00, 0x00, 0xf0, 0x00, 0x71, 0x2a, 0x20, 0x4c, + 0x6f, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x36, 0x00, 0x01, 0x05, + 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, 0x0a, 0xf9, 0x00, + 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, 0x12, 0x12, 0x0a, + 0xf9, 0x00, 0x00, 0xe9, 0x00, 0x6a, 0x2a, 0x20, 0x4d, 0x75, 0x48, 0x69, + 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x36, 0x00, 0x01, 0x07, 0x9f, 0x0f, 0x00, + 0xf9, 0x00, 0x72, 0x00, 0x1e, 0x12, 0x0a, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x12, 0x00, 0xf9, 0x00, 0x12, 0x00, 0x12, 0x16, 0x0a, 0xfa, 0x00, 0x00, + 0xf6, 0x00, 0x5d, 0x2a, 0x20, 0x4f, 0x70, 0x48, 0x69, 0x43, 0x6f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x71, + 0x00, 0x17, 0x14, 0x0c, 0xfb, 0x00, 0x00, 0x00, 0x9c, 0x17, 0x00, 0xfa, + 0x00, 0x11, 0x00, 0x12, 0x10, 0x0c, 0xf8, 0x00, 0x01, 0x82, 0x00, 0x92, + 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x0d, 0x17, 0x14, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x16, 0x00, 0xfa, 0x00, 0x31, 0x06, + 0x12, 0x0f, 0x0a, 0xf8, 0x00, 0x02, 0x1c, 0x00, 0x99, 0x2a, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x54, 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2a, 0x05, 0x13, 0x06, + 0x9f, 0x19, 0x08, 0xc6, 0x00, 0x00, 0x0d, 0x5f, 0x13, 0x05, 0xbc, 0x00, + 0x36, 0x05, 0x5f, 0x19, 0x06, 0xa6, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, + 0x86, 0x00, 0x01, 0x32, 0x01, 0x68, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x54, + 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x2a, 0x05, 0x13, 0x06, 0x9f, 0x19, 0x08, + 0xca, 0x00, 0x00, 0x0a, 0x5f, 0x13, 0x05, 0xbc, 0x00, 0x36, 0x05, 0x5f, + 0x19, 0x06, 0xac, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, 0x8c, 0x00, 0x01, + 0x32, 0x00, 0x21, 0x2a, 0x20, 0x48, 0x69, 0x41, 0x67, 0x6f, 0x67, 0x6f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x3c, 0x00, 0x3e, 0x17, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, + 0x18, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x34, 0x00, 0x54, 0x10, 0x11, 0xa8, + 0x00, 0x04, 0x00, 0x54, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x18, 0x00, 0x85, + 0x2a, 0x20, 0x4c, 0x6f, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x00, 0x3e, 0x1a, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, 0x18, 0x9f, 0x09, + 0x09, 0x86, 0x00, 0x34, 0x00, 0x57, 0x10, 0x11, 0xa8, 0x00, 0x04, 0x00, + 0x15, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x61, 0x00, 0x99, 0x2a, 0x20, 0x43, + 0x61, 0x62, 0x61, 0x73, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x31, 0x35, 0x2f, 0x06, + 0x5f, 0x05, 0x02, 0x81, 0x00, 0x7d, 0x02, 0x99, 0x05, 0x02, 0x71, 0x00, + 0x0f, 0x01, 0x9f, 0x05, 0x02, 0x53, 0x00, 0x33, 0x00, 0x8d, 0x11, 0x0b, + 0xa9, 0x00, 0x01, 0x75, 0x00, 0x49, 0x2a, 0x20, 0x4d, 0x61, 0x72, 0x61, + 0x63, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x00, 0x8e, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x1f, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0x92, 0x00, 0x49, 0x2a, 0x20, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x57, 0x68, + 0x69, 0x73, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x37, 0x04, 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x0b, 0x16, 0x2b, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0x89, 0x00, 0x50, + 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x04, + 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x08, 0x16, 0x2b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03, 0x55, 0x00, 0x50, 0x2a, 0x20, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x0f, 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd9, 0x00, 0x99, 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, + 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, 0x1f, 0x09, 0x09, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0c, + 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x5e, 0x00, 0x92, 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x45, 0x3c, 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, + 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, + 0x00, 0x19, 0x05, 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xad, 0x00, 0x92, + 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, + 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, + 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, + 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xc1, 0x00, 0xa0, 0x2a, 0x20, 0x57, + 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x3c, 0x00, 0x17, 0x14, + 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, + 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, 0xdf, 0x14, 0x0d, + 0x18, 0x00, 0x00, 0xc8, 0x00, 0x99, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x20, 0x43, 0x75, 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3c, 0x00, 0x72, 0x27, 0x0c, 0x0f, 0x0f, + 0x07, 0x00, 0x01, 0x1b, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x78, 0x00, 0x4e, + 0x06, 0x11, 0x06, 0x00, 0x08, 0x00, 0x8e, 0x06, 0x11, 0x06, 0x00, 0x01, + 0x4d, 0x01, 0x46, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x43, 0x75, + 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x3c, 0x00, 0x01, 0x26, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x01, + 0x1e, 0x0c, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x0e, 0x06, 0x11, 0x06, + 0x00, 0x01, 0x17, 0x0e, 0x0f, 0x0e, 0xb6, 0x00, 0x01, 0xed, 0x01, 0xb8, + 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, + 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, + 0x00, 0x03, 0x1a, 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x0f, 0x0d, 0x8b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4, 0x00, 0x28, 0x2a, 0x20, 0x4f, + 0x70, 0x65, 0x6e, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, 0x00, 0x03, 0x1a, + 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x1f, 0x08, 0x08, 0x85, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x4c, 0x02, 0x8d, 0x2a, 0x20, 0x53, 0x68, 0x61, 0x6b, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x0e, 0x8c, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0xce, 0x00, 0x49, 0x2a, 0x20, 0x4a, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, + 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x3c, 0x00, 0x05, 0x18, 0x1f, 0x04, 0x0f, 0xf4, 0x00, 0x05, + 0x1c, 0x1f, 0x09, 0x10, 0xf0, 0x00, 0x04, 0x09, 0x51, 0x0b, 0x07, 0xa6, + 0x00, 0x04, 0x0e, 0x51, 0x0b, 0x07, 0xa5, 0x00, 0x08, 0x98, 0x01, 0xb8, + 0x2a, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x54, 0x72, 0x65, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x2c, + 0x00, 0x05, 0x09, 0x14, 0x0d, 0x00, 0x10, 0x00, 0x05, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x14, 0x15, 0x08, 0x44, 0x00, 0x08, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x3a, 0x06, 0x46, 0x2a, 0x20, 0x43, + 0x61, 0x73, 0x74, 0x61, 0x6e, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, + 0x1f, 0x0b, 0x00, 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x04, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0x64, 0x00, 0x14, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x53, 0x75, 0x72, 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, + 0xff, 0x00, 0x71, 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, + 0x14, 0x00, 0xff, 0x00, 0x31, 0x00, 0x0e, 0x0e, 0x0a, 0xf7, 0x00, 0x03, + 0x0c, 0x00, 0xfd, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x75, 0x72, + 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x29, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xff, 0x00, 0x71, + 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, 0x14, 0x00, 0xff, + 0x00, 0x31, 0x00, 0x0e, 0x0b, 0x08, 0xf5, 0x00, 0x08, 0xcd, 0x03, 0xe8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4b, 0x45, 0x4b, 0x20, 0x3a, 0x33, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, + 0x00, 0x01, 0x01, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, 0xe6, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x3d, + 0x05, 0x33, 0x04, 0xdf, 0x06, 0x10, 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, + 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, 0x10, 0x1f, 0xf8, 0x00, 0x01, 0x00, + 0x9f, 0x10, 0x0e, 0x76, 0x00, 0x01, 0x11, 0x00, 0xdc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, 0x37, 0x00, 0x08, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x8e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd9, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x37, 0x00, 0x08, 0x1f, 0x0a, 0x0a, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x8e, 0x12, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x18, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x36, 0x18, 0x00, 0x02, 0x0a, 0x1f, 0x14, 0x00, 0xff, 0x00, 0x02, + 0x17, 0x1f, 0x12, 0x00, 0xff, 0x00, 0x02, 0x2a, 0x9f, 0x14, 0x00, 0xff, + 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x1f, + 0x00, 0x01, 0x27, 0x1f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x16, + 0x00, 0xff, 0x00, 0x01, 0x27, 0x9f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, + 0x12, 0x18, 0x00, 0xff, 0x00, 0x00, 0x71, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x18, 0x00, 0x02, 0x06, + 0x1f, 0x12, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x16, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, + 0xff, 0x00, 0x00, 0x85, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x18, 0x00, 0x02, 0x06, 0x1f, 0x12, 0x00, + 0xff, 0x00, 0x01, 0x01, 0x1f, 0x16, 0x00, 0xff, 0x00, 0x02, 0x17, 0x9f, + 0x08, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x0e, 0x00, 0xf7, 0x00, 0x02, + 0x01, 0x01, 0x61, 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x20, 0x44, 0x72, + 0x75, 0x6d, 0x20, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x23, 0x22, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x13, 0x07, 0x00, 0x05, + 0x7f, 0x00, 0x09, 0x0f, 0xf5, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0c, 0xf6, + 0x00, 0x01, 0x00, 0x5f, 0x00, 0x0b, 0x06, 0x00, 0x07, 0x58, 0x01, 0xe6, + 0x2a, 0x20, 0x47, 0x72, 0x61, 0x6e, 0x20, 0x43, 0x61, 0x73, 0x61, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x1e, + 0x00, 0x00, 0x03, 0x9f, 0x12, 0x12, 0x07, 0x00, 0x00, 0x00, 0x1f, 0x10, + 0x10, 0x06, 0x00, 0x01, 0x02, 0x9f, 0x10, 0x10, 0x0b, 0x00, 0x00, 0x03, + 0x1f, 0x0a, 0x0f, 0x96, 0x00, 0x08, 0xcd, 0x02, 0x44, 0x2a, 0x20, 0x53, + 0x74, 0x69, 0x63, 0x6b, 0x53, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x02, 0x15, + 0x1f, 0x13, 0x00, 0xff, 0x00, 0x01, 0x0e, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0xa0, 0x00, 0x1a, 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x3c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x08, + 0x00, 0x00, 0x03, 0x00, 0x1f, 0x17, 0x19, 0x09, 0x00, 0x10, 0x00, 0x1d, + 0x10, 0x0f, 0xa8, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x12, 0x09, 0x00, 0x01, + 0x90, 0x01, 0x04, 0x2a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x43, 0x6c, 0x61, + 0x70, 0x20, 0x28, 0x4f, 0x50, 0x4c, 0x33, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3c, 0x00, 0x0f, 0x0d, 0x9f, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x03, 0x1f, 0x14, 0x10, 0x08, 0x00, 0x0f, 0x1b, 0x1f, 0x11, 0x0f, 0x27, + 0x00, 0x01, 0x00, 0x1f, 0x03, 0x12, 0x09, 0x00, 0x00, 0xf6, 0x00, 0x71, + 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x3c, + 0x00, 0x10, 0x00, 0x14, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x1f, 0x17, + 0x19, 0x09, 0x00, 0x10, 0x00, 0x1d, 0x10, 0x0f, 0xa8, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x12, 0x09, 0x00, 0x01, 0x90, 0x00, 0xf6, 0x2a, 0x20, 0x54, + 0x69, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0c, 0x00, 0x00, 0x0a, + 0x54, 0x0e, 0x00, 0xf7, 0x00, 0x01, 0x00, 0x1f, 0x0d, 0x00, 0xf9, 0x00, + 0x01, 0x00, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x02, 0x47, 0x0f, 0x0d, 0x00, + 0xfe, 0x00, 0x0c, 0xae, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x3a, 0x33, 0x3f, 0x19, 0x1f, 0x8e, 0x0d, + 0x84, 0x00, 0x33, 0x00, 0x5f, 0x0a, 0x08, 0x64, 0x00, 0x7f, 0x14, 0x1f, + 0x8e, 0x0d, 0x94, 0x00, 0x71, 0x00, 0xdf, 0x09, 0x09, 0x25, 0x00, 0x00, + 0xfd, 0x00, 0x64, 0x2a, 0x20, 0x54, 0x69, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2b, 0x0c, 0x00, 0x00, 0x0a, 0x54, 0x0e, 0x00, 0xf7, 0x00, 0x01, + 0x00, 0x1f, 0x0d, 0x00, 0xf9, 0x00, 0x01, 0x00, 0x1f, 0x0a, 0x00, 0xf5, + 0x00, 0x02, 0x47, 0x0f, 0x0d, 0x00, 0xfe, 0x00, 0x0c, 0x9a, 0x04, 0x1d, + 0x2a, 0x20, 0x50, 0x65, 0x64, 0x61, 0x6c, 0x48, 0x69, 0x68, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x38, + 0x00, 0x3c, 0x00, 0x47, 0x0a, 0x1c, 0xb4, 0x00, 0x13, 0x00, 0xdf, 0x00, + 0x00, 0x04, 0x00, 0x7a, 0x00, 0xc3, 0x8a, 0x10, 0xb7, 0x00, 0x75, 0x07, + 0xc4, 0x0d, 0x1d, 0x59, 0x00, 0x00, 0x85, 0x00, 0x42, 0x2a, 0x20, 0x54, + 0x69, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x0c, 0x00, 0x00, 0x0a, + 0x54, 0x0e, 0x00, 0xf7, 0x00, 0x01, 0x00, 0x1f, 0x0d, 0x00, 0xf9, 0x00, + 0x01, 0x00, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x02, 0x47, 0x0f, 0x0d, 0x00, + 0xfe, 0x00, 0x0a, 0x2e, 0x03, 0x91, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, + 0x48, 0x69, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x2c, 0x00, 0x09, 0x00, 0x1f, 0x0e, 0x00, + 0x10, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x1f, + 0x17, 0x08, 0x44, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, + 0x9e, 0x04, 0x1d, 0x2a, 0x20, 0x54, 0x69, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2f, 0x0c, 0x00, 0x00, 0x0a, 0x54, 0x0e, 0x00, 0xf7, 0x00, 0x01, + 0x00, 0x1f, 0x0d, 0x00, 0xf9, 0x00, 0x01, 0x00, 0x1f, 0x0a, 0x00, 0xf5, + 0x00, 0x02, 0x47, 0x0f, 0x0d, 0x00, 0xfe, 0x00, 0x0a, 0x0d, 0x03, 0x7d, + 0x2a, 0x20, 0x54, 0x69, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, + 0x00, 0x00, 0x0a, 0x54, 0x0e, 0x00, 0xf7, 0x00, 0x01, 0x00, 0x1f, 0x0d, + 0x00, 0xf9, 0x00, 0x01, 0x00, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x02, 0x47, + 0x0f, 0x0d, 0x00, 0xfe, 0x00, 0x0a, 0x0d, 0x03, 0x7d, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, + 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x10, 0x7f, 0x00, + 0x1f, 0x0b, 0x00, 0x40, 0x00, 0x71, 0x00, 0x1f, 0x16, 0x07, 0x34, 0x00, + 0x13, 0x00, 0x1f, 0x8b, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x9f, 0x1b, 0x07, + 0xa6, 0x00, 0x07, 0x44, 0x01, 0xd9, 0x2a, 0x20, 0x54, 0x69, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x0c, 0x00, 0x00, 0x0a, 0x54, 0x0e, 0x00, + 0xf7, 0x00, 0x01, 0x00, 0x1f, 0x0d, 0x00, 0xf9, 0x00, 0x01, 0x00, 0x1f, + 0x0a, 0x00, 0xf5, 0x00, 0x02, 0x47, 0x0f, 0x0d, 0x00, 0xfe, 0x00, 0x0a, + 0x3c, 0x03, 0x9e, 0x2a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x20, 0x43, 0x79, + 0x6d, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x20, 0x4c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x3c, 0x10, 0x7f, 0x00, 0x0f, 0x0b, 0x00, 0x10, 0x00, 0x71, + 0x00, 0x14, 0x1b, 0x07, 0xf4, 0x00, 0x13, 0x00, 0x13, 0x8b, 0x00, 0xb6, + 0x00, 0x07, 0x00, 0x9f, 0x1b, 0x07, 0xa6, 0x00, 0x9c, 0x40, 0x00, 0x00, + 0x2a, 0x20, 0x43, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x20, 0x43, 0x72, + 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, + 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2c, + 0x00, 0x7f, 0x00, 0x1f, 0x07, 0x00, 0x52, 0x00, 0x71, 0x00, 0x1f, 0x15, + 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0c, 0x04, 0x84, 0x00, 0x07, 0x00, + 0x9f, 0x19, 0x06, 0xa3, 0x00, 0x25, 0x44, 0x07, 0x1c, 0x2a, 0x20, 0x52, + 0x69, 0x64, 0x65, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x1c, 0x00, 0x0d, 0x00, + 0x1f, 0x07, 0x07, 0xe1, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x11, 0x1f, 0x0a, 0x12, 0xe5, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x3a, 0x03, 0x2d, 0x2a, 0x20, 0x54, 0x61, 0x6d, 0x62, + 0x6f, 0x72, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x35, 0x22, 0x06, 0x5f, 0x05, 0x02, + 0x81, 0x00, 0x75, 0x00, 0x96, 0x0f, 0x0c, 0xa1, 0x00, 0x04, 0x01, 0x9f, + 0x05, 0x02, 0x53, 0x00, 0x3e, 0x08, 0x92, 0x0d, 0x0d, 0xa6, 0x00, 0x01, + 0x89, 0x00, 0xc1, 0x2a, 0x20, 0x53, 0x70, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x43, 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, + 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x2c, 0x00, 0x7f, 0x00, 0x1f, 0x05, 0x06, 0x50, 0x00, 0x71, + 0x00, 0x1f, 0x15, 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0b, 0x04, 0x85, + 0x00, 0x07, 0x00, 0x9f, 0x1a, 0x06, 0xa5, 0x00, 0x21, 0xcd, 0x03, 0x20, + 0x2a, 0x20, 0x43, 0x6f, 0x77, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3b, + 0x00, 0x2f, 0x00, 0x1f, 0x15, 0x13, 0x26, 0x00, 0x37, 0x20, 0x1f, 0x15, + 0x0d, 0x36, 0x00, 0x28, 0x23, 0x1f, 0x95, 0x0c, 0x26, 0x00, 0x32, 0x00, + 0x1f, 0x13, 0x10, 0x28, 0x00, 0x01, 0x61, 0x00, 0x99, 0x2a, 0x20, 0x48, + 0x61, 0x6e, 0x64, 0x20, 0x43, 0x79, 0x6d, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x20, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x2c, 0x00, 0x7f, 0x00, + 0x1a, 0x00, 0x00, 0x50, 0x00, 0x71, 0x00, 0x1b, 0x14, 0x06, 0x33, 0x00, + 0x13, 0x00, 0x10, 0x0a, 0x04, 0x84, 0x00, 0x07, 0x00, 0x9f, 0x19, 0x06, + 0xa4, 0x00, 0x21, 0xda, 0x05, 0xfd, 0x2a, 0x20, 0x56, 0x69, 0x62, 0x65, + 0x53, 0x6c, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x34, 0x00, 0x00, 0x08, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x0a, 0x00, 0xf5, 0x00, 0x00, 0x15, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x0c, + 0xea, 0x03, 0xac, 0x2a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x20, 0x43, 0x79, + 0x6d, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x20, 0x48, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x2c, 0x00, 0x7f, 0x00, 0x1a, 0x07, 0x00, 0x50, 0x00, 0x71, + 0x00, 0x1b, 0x14, 0x06, 0x33, 0x00, 0x13, 0x00, 0x10, 0x0a, 0x04, 0x84, + 0x00, 0x07, 0x00, 0x9f, 0x19, 0x06, 0xa4, 0x00, 0x28, 0xc8, 0x06, 0xcc, + 0x2a, 0x20, 0x48, 0x69, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, + 0x12, 0x12, 0x0a, 0xf9, 0x00, 0x00, 0xf0, 0x00, 0x71, 0x2a, 0x20, 0x4c, + 0x6f, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x36, 0x00, 0x01, 0x05, + 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, 0x0a, 0xf9, 0x00, + 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, 0x12, 0x12, 0x0a, + 0xf9, 0x00, 0x00, 0xe9, 0x00, 0x6a, 0x2a, 0x20, 0x4d, 0x75, 0x48, 0x69, + 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x36, 0x00, 0x01, 0x07, 0x9f, 0x0f, 0x00, + 0xf9, 0x00, 0x72, 0x00, 0x1e, 0x12, 0x0a, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x12, 0x00, 0xf9, 0x00, 0x12, 0x00, 0x12, 0x16, 0x0a, 0xfa, 0x00, 0x00, + 0xf6, 0x00, 0x5d, 0x2a, 0x20, 0x4f, 0x70, 0x48, 0x69, 0x43, 0x6f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x71, + 0x00, 0x17, 0x14, 0x0c, 0xfb, 0x00, 0x00, 0x00, 0x9c, 0x17, 0x00, 0xfa, + 0x00, 0x11, 0x00, 0x12, 0x10, 0x0c, 0xf8, 0x00, 0x01, 0x82, 0x00, 0x92, + 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x0d, 0x17, 0x14, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x16, 0x00, 0xfa, 0x00, 0x31, 0x06, + 0x12, 0x0f, 0x0a, 0xf8, 0x00, 0x02, 0x1c, 0x00, 0x99, 0x2a, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x54, 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2a, 0x05, 0x13, 0x06, + 0x9f, 0x19, 0x08, 0xc6, 0x00, 0x00, 0x0d, 0x5f, 0x13, 0x05, 0xbc, 0x00, + 0x36, 0x05, 0x5f, 0x19, 0x06, 0xa6, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, + 0x86, 0x00, 0x01, 0x32, 0x01, 0x68, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x54, + 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x2a, 0x05, 0x13, 0x06, 0x9f, 0x19, 0x08, + 0xca, 0x00, 0x00, 0x0a, 0x5f, 0x13, 0x05, 0xbc, 0x00, 0x36, 0x05, 0x5f, + 0x19, 0x06, 0xac, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, 0x8c, 0x00, 0x01, + 0x32, 0x00, 0x21, 0x2a, 0x20, 0x48, 0x69, 0x41, 0x67, 0x6f, 0x67, 0x6f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x3c, 0x00, 0x3e, 0x17, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, + 0x18, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x34, 0x00, 0x54, 0x10, 0x11, 0xa8, + 0x00, 0x04, 0x00, 0x54, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x18, 0x00, 0x85, + 0x2a, 0x20, 0x4c, 0x6f, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x00, 0x3e, 0x1a, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, 0x18, 0x9f, 0x09, + 0x09, 0x86, 0x00, 0x34, 0x00, 0x57, 0x10, 0x11, 0xa8, 0x00, 0x04, 0x00, + 0x15, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x61, 0x00, 0x99, 0x2a, 0x20, 0x43, + 0x61, 0x62, 0x61, 0x73, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x31, 0x35, 0x2f, 0x06, + 0x5f, 0x05, 0x02, 0x81, 0x00, 0x7d, 0x02, 0x99, 0x05, 0x02, 0x71, 0x00, + 0x0f, 0x01, 0x9f, 0x05, 0x02, 0x53, 0x00, 0x33, 0x00, 0x8d, 0x11, 0x0b, + 0xa9, 0x00, 0x01, 0x75, 0x00, 0x49, 0x2a, 0x20, 0x4d, 0x61, 0x72, 0x61, + 0x63, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x00, 0x8e, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x1f, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0x92, 0x00, 0x49, 0x2a, 0x20, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x57, 0x68, + 0x69, 0x73, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x37, 0x04, 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x0b, 0x16, 0x2b, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0x89, 0x00, 0x50, + 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x04, + 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x08, 0x16, 0x2b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03, 0x55, 0x00, 0x50, 0x2a, 0x20, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x0c, 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x58, 0x00, 0x99, 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, + 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, 0x1f, 0x09, 0x09, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x09, + 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xda, 0x00, 0xa0, 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x45, 0x3c, 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, + 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, + 0x00, 0x19, 0x05, 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xad, 0x00, 0x92, + 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, + 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, + 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, + 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xc1, 0x00, 0xa0, 0x2a, 0x20, 0x57, + 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x3c, 0x00, 0x17, 0x14, + 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, + 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, 0xdf, 0x14, 0x0d, + 0x18, 0x00, 0x00, 0xc8, 0x00, 0x99, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x20, 0x43, 0x75, 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3c, 0x00, 0x72, 0x27, 0x0c, 0x0f, 0x0f, + 0x07, 0x00, 0x01, 0x1b, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x78, 0x00, 0x4e, + 0x06, 0x11, 0x06, 0x00, 0x08, 0x00, 0x8e, 0x06, 0x11, 0x06, 0x00, 0x01, + 0x4d, 0x01, 0x46, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x43, 0x75, + 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x3c, 0x00, 0x01, 0x26, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x01, + 0x1e, 0x0c, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x0e, 0x06, 0x11, 0x06, + 0x00, 0x01, 0x17, 0x0e, 0x0f, 0x0e, 0xb6, 0x00, 0x01, 0xed, 0x01, 0xb8, + 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, + 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, + 0x00, 0x03, 0x1a, 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x0e, 0x0d, 0x8b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x44, 0x00, 0x28, 0x2a, 0x20, 0x4f, + 0x70, 0x65, 0x6e, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, 0x00, 0x03, 0x1a, + 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x1f, 0x08, 0x08, 0x85, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x4c, 0x02, 0x8d, 0x2a, 0x20, 0x53, 0x68, 0x61, 0x6b, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x0e, 0x8c, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0xce, 0x00, 0x49, 0x2a, 0x20, 0x4a, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, + 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x3c, 0x00, 0x05, 0x18, 0x1f, 0x04, 0x0f, 0xf4, 0x00, 0x05, + 0x1c, 0x1f, 0x09, 0x10, 0xf0, 0x00, 0x04, 0x09, 0x51, 0x0b, 0x07, 0xa6, + 0x00, 0x04, 0x0e, 0x51, 0x0b, 0x07, 0xa5, 0x00, 0x08, 0x98, 0x01, 0xb8, + 0x2a, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x54, 0x72, 0x65, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x2c, + 0x00, 0x05, 0x09, 0x14, 0x0d, 0x00, 0x10, 0x00, 0x05, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x14, 0x15, 0x08, 0x44, 0x00, 0x08, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x3a, 0x06, 0x46, 0x2a, 0x20, 0x43, + 0x61, 0x73, 0x74, 0x61, 0x6e, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, + 0x1f, 0x0b, 0x00, 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x04, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0x64, 0x00, 0x14, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x53, 0x75, 0x72, 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, + 0xff, 0x00, 0x71, 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, + 0x14, 0x00, 0xff, 0x00, 0x31, 0x00, 0x0e, 0x0d, 0x0a, 0xf6, 0x00, 0x04, + 0x16, 0x01, 0xc5, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x75, 0x72, + 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x29, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xff, 0x00, 0x71, + 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, 0x14, 0x00, 0xff, + 0x00, 0x31, 0x00, 0x0e, 0x0b, 0x08, 0xf4, 0x00, 0x08, 0xcd, 0x07, 0x9a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, + 0x00, 0x01, 0x01, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, 0xe6, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x10, 0x7f, 0x00, 0x1d, 0x01, 0x00, 0x80, 0x00, 0x7f, 0x00, 0x1f, 0x00, + 0x00, 0x14, 0x00, 0x13, 0x09, 0x44, 0x8d, 0x16, 0x09, 0x00, 0x00, 0x1d, + 0x84, 0x1b, 0x06, 0xa6, 0x00, 0x09, 0xb6, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, 0x37, 0x00, 0x08, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x8e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd9, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x25, 0x0a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, + 0x0c, 0x00, 0x05, 0x7f, 0x00, 0x0b, 0x0f, 0xf4, 0x00, 0x06, 0x7f, 0x00, + 0x0f, 0x0d, 0xf6, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, + 0x75, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x36, 0x18, 0x00, 0x02, 0x0a, 0x1f, 0x14, 0x00, 0xff, 0x00, 0x02, + 0x17, 0x1f, 0x12, 0x00, 0xff, 0x00, 0x02, 0x2a, 0x9f, 0x14, 0x00, 0xff, + 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x1f, + 0x00, 0x01, 0x27, 0x1f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x16, + 0x00, 0xff, 0x00, 0x01, 0x27, 0x9f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, + 0x12, 0x18, 0x00, 0xff, 0x00, 0x00, 0x71, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x18, 0x00, 0x02, 0x06, + 0x1f, 0x12, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x16, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, + 0xff, 0x00, 0x00, 0x85, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x18, 0x00, 0x02, 0x06, 0x1f, 0x12, 0x00, + 0xff, 0x00, 0x01, 0x01, 0x1f, 0x16, 0x00, 0xff, 0x00, 0x02, 0x17, 0x9f, + 0x08, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x0e, 0x00, 0xf7, 0x00, 0x02, + 0x01, 0x00, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x21, 0x25, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x17, 0x09, 0x00, 0x01, + 0x15, 0x1f, 0x11, 0x15, 0xf7, 0x00, 0x36, 0x7f, 0x1f, 0x0f, 0x0c, 0xf7, + 0x00, 0x01, 0x00, 0x5f, 0x00, 0x0f, 0x07, 0x00, 0x01, 0xf4, 0x01, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x25, + 0x00, 0x30, 0x00, 0x1f, 0x00, 0x15, 0x09, 0x00, 0x01, 0x15, 0x1f, 0x11, + 0x15, 0xf7, 0x00, 0x36, 0x7f, 0x1f, 0x0f, 0x0b, 0xf7, 0x00, 0x01, 0x00, + 0x5f, 0x00, 0x0d, 0x07, 0x00, 0x03, 0xb2, 0x01, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x02, 0x15, + 0x1f, 0x13, 0x00, 0xff, 0x00, 0x01, 0x0e, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0xa0, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x3c, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x17, 0x18, 0x09, 0x00, 0x10, 0x00, 0x1f, + 0x12, 0x1c, 0xc9, 0x00, 0x00, 0x00, 0x1f, 0x14, 0x11, 0x79, 0x00, 0x00, + 0xd5, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x28, 0x3b, 0x00, 0x00, 0x00, 0x1f, 0x12, 0x00, 0xf0, 0x00, 0x01, + 0x05, 0x14, 0x16, 0x00, 0xf0, 0x00, 0x04, 0x04, 0x91, 0x13, 0x00, 0xff, + 0x00, 0x01, 0x00, 0x16, 0x13, 0x10, 0x9c, 0x00, 0x01, 0x39, 0x00, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3c, + 0x00, 0x10, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x17, + 0x18, 0x09, 0x00, 0x10, 0x00, 0x1f, 0x13, 0x1c, 0xe9, 0x00, 0x00, 0x00, + 0x1f, 0x15, 0x11, 0x79, 0x00, 0x00, 0xc8, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3e, 0x00, 0x31, 0x00, + 0x1f, 0x1a, 0x03, 0xf6, 0x00, 0x13, 0x00, 0x5e, 0x0f, 0x10, 0x87, 0x00, + 0x70, 0x2a, 0x1f, 0x0c, 0x10, 0xa6, 0x00, 0x10, 0x00, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x02, 0x36, 0x02, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0x34, 0x00, 0x70, 0x02, 0x1b, 0x00, 0x00, + 0x0f, 0x00, 0x70, 0x07, 0x1f, 0x00, 0x00, 0xfd, 0x00, 0x7f, 0x03, 0x1f, + 0x13, 0x00, 0xff, 0x00, 0x7f, 0x07, 0x15, 0x12, 0x00, 0xff, 0x00, 0x00, + 0xba, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x3e, 0x00, 0x31, 0x00, 0x1f, 0x1a, 0x03, 0xf6, 0x00, 0x13, + 0x00, 0x5e, 0x0f, 0x10, 0x87, 0x00, 0x70, 0x2a, 0x1f, 0x0c, 0x10, 0xa6, + 0x00, 0x13, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x02, 0x0e, 0x02, 0x01, + 0x2a, 0x20, 0x48, 0x48, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x20, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x34, + 0x00, 0x70, 0x07, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x70, 0x07, 0x1f, 0x00, + 0x00, 0xf0, 0x00, 0x7f, 0x06, 0x1f, 0x10, 0x0d, 0x67, 0x00, 0x7f, 0x07, + 0x15, 0x0e, 0x0f, 0x37, 0x00, 0x02, 0x44, 0x00, 0xe9, 0x54, 0x6f, 0x6d, + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x3e, 0x00, 0x31, 0x00, + 0x1f, 0x1a, 0x03, 0xf6, 0x00, 0x13, 0x00, 0x5e, 0x0f, 0x10, 0x87, 0x00, + 0x70, 0x2a, 0x1f, 0x0c, 0x10, 0xa6, 0x00, 0x13, 0x00, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x02, 0x0e, 0x02, 0x01, 0x2a, 0x20, 0x48, 0x48, 0x4f, 0x70, + 0x65, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0x34, 0x00, 0x70, 0x07, 0x1b, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x07, 0x1f, 0x00, 0x00, 0xf0, 0x00, 0x7f, 0x06, 0x1f, + 0x10, 0x09, 0x67, 0x00, 0x7f, 0x07, 0x15, 0x0e, 0x09, 0x36, 0x00, 0x09, + 0xe5, 0x01, 0x6e, 0x54, 0x6f, 0x6d, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x22, 0x3e, 0x00, 0x31, 0x00, 0x1f, 0x1a, 0x03, 0xf6, 0x00, 0x13, + 0x00, 0x5e, 0x0f, 0x10, 0x87, 0x00, 0x70, 0x2a, 0x1f, 0x0c, 0x10, 0xa6, + 0x00, 0x13, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x01, 0xd2, 0x01, 0xf4, + 0x54, 0x6f, 0x6d, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x3e, + 0x00, 0x31, 0x00, 0x1f, 0x1a, 0x03, 0xf6, 0x00, 0x13, 0x00, 0x5e, 0x0f, + 0x10, 0x87, 0x00, 0x70, 0x2a, 0x1f, 0x0c, 0x10, 0xa6, 0x00, 0x13, 0x00, + 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x01, 0xd2, 0x01, 0xf4, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, + 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x10, 0x7f, 0x00, + 0x1f, 0x0b, 0x00, 0x40, 0x00, 0x71, 0x00, 0x1f, 0x16, 0x07, 0x34, 0x00, + 0x13, 0x00, 0x1f, 0x8b, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x9f, 0x1b, 0x07, + 0xa6, 0x00, 0x07, 0x44, 0x01, 0xd9, 0x54, 0x6f, 0x6d, 0x36, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x29, 0x3e, 0x00, 0x3f, 0x00, 0x1f, 0x1a, 0x03, + 0xf6, 0x00, 0x13, 0x00, 0x5e, 0x10, 0x12, 0x89, 0x00, 0x70, 0x2a, 0x1f, + 0x0c, 0x10, 0xa6, 0x00, 0x13, 0x00, 0x1e, 0x11, 0x13, 0xf8, 0x00, 0x01, + 0x7c, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0d, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x18, 0x15, 0x07, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x19, 0x05, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2c, + 0x00, 0x7f, 0x00, 0x1f, 0x07, 0x00, 0x52, 0x00, 0x71, 0x00, 0x1f, 0x15, + 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0c, 0x04, 0x84, 0x00, 0x07, 0x03, + 0x9f, 0x19, 0x06, 0xa3, 0x00, 0x24, 0xb8, 0x07, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x1c, 0x00, 0x0d, 0x00, + 0x1f, 0x07, 0x07, 0xe1, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x11, 0x1f, 0x0a, 0x12, 0xe5, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x19, 0x03, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x35, 0x22, 0x06, 0x5f, 0x05, 0x02, + 0x81, 0x00, 0x75, 0x00, 0x96, 0x0f, 0x0c, 0xa1, 0x00, 0x04, 0x01, 0x9f, + 0x05, 0x02, 0x53, 0x00, 0x3e, 0x08, 0x92, 0x0d, 0x0d, 0xa6, 0x00, 0x01, + 0x82, 0x00, 0xc1, 0x2a, 0x20, 0x53, 0x70, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x43, 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, + 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x2c, 0x00, 0x7f, 0x00, 0x1f, 0x05, 0x06, 0x50, 0x00, 0x71, + 0x00, 0x1f, 0x15, 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0b, 0x04, 0x85, + 0x00, 0x07, 0x00, 0x9f, 0x1a, 0x06, 0xa5, 0x00, 0x21, 0xcd, 0x03, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, + 0x00, 0x30, 0x00, 0x1f, 0x1a, 0x03, 0xf6, 0x00, 0x32, 0x0f, 0x5f, 0x0d, + 0x12, 0x87, 0x00, 0x54, 0x0f, 0x1f, 0x0a, 0x17, 0x27, 0x00, 0x74, 0x0f, + 0x1f, 0x0d, 0x17, 0xf7, 0x00, 0x03, 0x26, 0x00, 0xf6, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x32, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x2c, 0x00, 0x7f, 0x00, + 0x1f, 0x00, 0x00, 0x50, 0x00, 0x71, 0x00, 0x1f, 0x14, 0x06, 0x33, 0x00, + 0x13, 0x00, 0x1f, 0x0a, 0x04, 0x84, 0x00, 0x07, 0x00, 0x9f, 0x19, 0x06, + 0xa4, 0x00, 0x20, 0x5e, 0x06, 0x11, 0x2a, 0x20, 0x56, 0x69, 0x62, 0x65, + 0x53, 0x6c, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x34, 0x00, 0x00, 0x08, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x0a, 0x00, 0xf5, 0x00, 0x00, 0x15, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x0c, + 0xea, 0x03, 0xac, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0c, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x18, 0x14, 0x08, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0e, 0x05, 0x85, + 0x2a, 0x20, 0x48, 0x69, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, + 0x12, 0x12, 0x0a, 0xf9, 0x00, 0x00, 0xf0, 0x00, 0x71, 0x2a, 0x20, 0x4c, + 0x6f, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x36, 0x00, 0x01, 0x05, + 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, 0x0a, 0xf9, 0x00, + 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, 0x12, 0x12, 0x0a, + 0xf9, 0x00, 0x00, 0xe9, 0x00, 0x6a, 0x43, 0x6f, 0x6e, 0x67, 0x61, 0x48, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x31, 0x3e, 0x00, 0x3f, 0x00, 0x1f, 0x1a, 0x03, + 0xf6, 0x00, 0x13, 0x00, 0x5e, 0x10, 0x1b, 0x89, 0x00, 0x73, 0x2a, 0x1f, + 0x0e, 0x18, 0xa9, 0x00, 0x13, 0x00, 0x1e, 0x11, 0x19, 0xf8, 0x00, 0x01, + 0x18, 0x00, 0x92, 0x43, 0x6f, 0x6e, 0x67, 0x61, 0x4d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x29, 0x3e, 0x00, 0x3f, 0x00, 0x1f, 0x1a, 0x03, 0xf6, 0x00, 0x13, + 0x00, 0x5e, 0x10, 0x1b, 0x89, 0x00, 0x73, 0x2a, 0x1f, 0x0e, 0x18, 0xa9, + 0x00, 0x13, 0x00, 0x1e, 0x11, 0x19, 0xf8, 0x00, 0x01, 0x46, 0x00, 0xa6, + 0x43, 0x6f, 0x6e, 0x67, 0x61, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x3e, + 0x00, 0x3f, 0x00, 0x1f, 0x1a, 0x03, 0xf6, 0x00, 0x13, 0x00, 0x5e, 0x10, + 0x1b, 0x89, 0x00, 0x73, 0x2a, 0x1f, 0x0e, 0x18, 0xa9, 0x00, 0x13, 0x00, + 0x1e, 0x11, 0x19, 0xf8, 0x00, 0x01, 0x46, 0x00, 0xa6, 0x2a, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x54, 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2a, 0x05, 0x13, 0x06, + 0x9f, 0x19, 0x08, 0xc6, 0x00, 0x00, 0x0d, 0x5f, 0x13, 0x05, 0xbc, 0x00, + 0x36, 0x05, 0x5f, 0x19, 0x06, 0xa6, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, + 0x86, 0x00, 0x01, 0x32, 0x01, 0x68, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x54, + 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x2a, 0x05, 0x13, 0x06, 0x9f, 0x19, 0x08, + 0xca, 0x00, 0x00, 0x0a, 0x5f, 0x13, 0x05, 0xbc, 0x00, 0x36, 0x05, 0x5f, + 0x19, 0x06, 0xac, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, 0x8c, 0x00, 0x01, + 0x32, 0x00, 0x21, 0x2a, 0x20, 0x48, 0x69, 0x41, 0x67, 0x6f, 0x67, 0x6f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x3c, 0x00, 0x3e, 0x17, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, + 0x18, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x34, 0x00, 0x54, 0x10, 0x11, 0xa8, + 0x00, 0x04, 0x00, 0x54, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x18, 0x00, 0x85, + 0x2a, 0x20, 0x4c, 0x6f, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x00, 0x3e, 0x1a, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, 0x18, 0x9f, 0x09, + 0x09, 0x86, 0x00, 0x34, 0x00, 0x57, 0x10, 0x11, 0xa8, 0x00, 0x04, 0x00, + 0x15, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x61, 0x00, 0x99, 0x2a, 0x20, 0x43, + 0x61, 0x62, 0x61, 0x73, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x31, 0x35, 0x2f, 0x06, + 0x5f, 0x05, 0x02, 0x81, 0x00, 0x7d, 0x02, 0x99, 0x05, 0x02, 0x71, 0x00, + 0x0f, 0x01, 0x9f, 0x05, 0x02, 0x53, 0x00, 0x33, 0x00, 0x8d, 0x11, 0x0b, + 0xa9, 0x00, 0x01, 0x75, 0x00, 0x49, 0x2a, 0x20, 0x4d, 0x61, 0x72, 0x61, + 0x63, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x00, 0x8e, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x1f, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0x92, 0x00, 0x49, 0x2a, 0x20, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x57, 0x68, + 0x69, 0x73, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x37, 0x04, 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x0b, 0x16, 0x2b, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0x89, 0x00, 0x50, + 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x04, + 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x08, 0x16, 0x2b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03, 0x55, 0x00, 0x50, 0x2a, 0x20, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x0c, 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x58, 0x00, 0x99, 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, + 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, 0x1f, 0x09, 0x09, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x09, + 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xda, 0x00, 0xa0, 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x45, 0x3c, 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, + 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, + 0x00, 0x19, 0x05, 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xad, 0x00, 0x92, + 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, + 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, + 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, + 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xc1, 0x00, 0xa0, 0x2a, 0x20, 0x57, + 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x3c, 0x00, 0x17, 0x14, + 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, + 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, 0xdf, 0x14, 0x0d, + 0x18, 0x00, 0x00, 0xc8, 0x00, 0x99, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x20, 0x43, 0x75, 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3c, 0x00, 0x72, 0x27, 0x0c, 0x0f, 0x0f, + 0x07, 0x00, 0x01, 0x1b, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x78, 0x00, 0x4e, + 0x06, 0x11, 0x06, 0x00, 0x08, 0x00, 0x8e, 0x06, 0x11, 0x06, 0x00, 0x01, + 0x4d, 0x01, 0x46, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x43, 0x75, + 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x3c, 0x00, 0x01, 0x26, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x01, + 0x1e, 0x0c, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x0e, 0x06, 0x11, 0x06, + 0x00, 0x01, 0x17, 0x0e, 0x0f, 0x0e, 0xb6, 0x00, 0x01, 0xed, 0x01, 0xb8, + 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, + 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, + 0x00, 0x03, 0x1a, 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x0e, 0x0d, 0x8b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x44, 0x00, 0x28, 0x2a, 0x20, 0x4f, + 0x70, 0x65, 0x6e, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, 0x00, 0x03, 0x1a, + 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x1f, 0x08, 0x08, 0x85, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x4c, 0x02, 0x8d, 0x2a, 0x20, 0x53, 0x68, 0x61, 0x6b, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x0e, 0x8c, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0xce, 0x00, 0x49, 0x2a, 0x20, 0x4a, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, + 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x3c, 0x00, 0x05, 0x18, 0x1f, 0x04, 0x0f, 0xf4, 0x00, 0x05, + 0x1c, 0x1f, 0x09, 0x10, 0xf0, 0x00, 0x04, 0x09, 0x51, 0x0b, 0x07, 0xa6, + 0x00, 0x04, 0x0e, 0x51, 0x0b, 0x07, 0xa5, 0x00, 0x08, 0x98, 0x01, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x2c, + 0x00, 0x05, 0x09, 0x14, 0x0d, 0x00, 0x10, 0x00, 0x05, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x14, 0x15, 0x08, 0x44, 0x00, 0x08, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x19, 0x06, 0x46, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, + 0x1f, 0x0b, 0x00, 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x04, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0x64, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, + 0xff, 0x00, 0x71, 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, + 0x14, 0x00, 0xff, 0x00, 0x31, 0x00, 0x0e, 0x0d, 0x0a, 0xf6, 0x00, 0x04, + 0x10, 0x01, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x29, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xff, 0x00, 0x71, + 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, 0x14, 0x00, 0xff, + 0x00, 0x31, 0x00, 0x0e, 0x0b, 0x08, 0xf4, 0x00, 0x08, 0xc6, 0x07, 0x9a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, + 0x00, 0x01, 0x01, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, 0xe6, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x10, 0x7f, 0x00, 0x1d, 0x01, 0x00, 0x80, 0x00, 0x7f, 0x00, 0x1f, 0x00, + 0x00, 0x14, 0x00, 0x13, 0x09, 0x44, 0x8d, 0x16, 0x09, 0x00, 0x00, 0x1d, + 0x84, 0x1b, 0x06, 0xa6, 0x00, 0x09, 0xb6, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, 0x37, 0x00, 0x08, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x8e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd9, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x25, 0x0a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, + 0x0c, 0x00, 0x05, 0x7f, 0x00, 0x0b, 0x0f, 0xf4, 0x00, 0x06, 0x7f, 0x00, + 0x0f, 0x0d, 0xf6, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, + 0x75, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x36, 0x18, 0x00, 0x02, 0x0a, 0x1f, 0x14, 0x00, 0xff, 0x00, 0x02, + 0x17, 0x1f, 0x12, 0x00, 0xff, 0x00, 0x02, 0x2a, 0x9f, 0x14, 0x00, 0xff, + 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x1f, + 0x00, 0x01, 0x27, 0x1f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x16, + 0x00, 0xff, 0x00, 0x01, 0x27, 0x9f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, + 0x12, 0x18, 0x00, 0xff, 0x00, 0x00, 0x71, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x18, 0x00, 0x02, 0x06, + 0x1f, 0x12, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x16, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, + 0xff, 0x00, 0x00, 0x85, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x18, 0x00, 0x02, 0x06, 0x1f, 0x12, 0x00, + 0xff, 0x00, 0x01, 0x01, 0x1f, 0x16, 0x00, 0xff, 0x00, 0x02, 0x17, 0x9f, + 0x08, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x0e, 0x00, 0xf7, 0x00, 0x02, + 0x01, 0x00, 0xce, 0x6b, 0x69, 0x63, 0x6b, 0x35, 0x5f, 0x28, 0x63, 0x32, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x24, 0x00, 0x00, 0x00, 0x1f, 0x1a, 0x13, 0x08, 0x07, 0x01, + 0x06, 0x1f, 0x13, 0x13, 0x08, 0x07, 0x00, 0x00, 0x1f, 0x1f, 0x0e, 0x07, + 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x0e, 0x07, 0x00, 0x03, 0xa5, 0x01, 0x68, + 0x6b, 0x69, 0x63, 0x6b, 0x36, 0x5f, 0x28, 0x63, 0x32, 0x29, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x25, + 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x13, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x0e, + 0x0b, 0x6f, 0x00, 0x00, 0x00, 0x1f, 0x10, 0x0b, 0x6f, 0x00, 0x00, 0x00, + 0x1f, 0x11, 0x0b, 0x6f, 0x00, 0x07, 0x36, 0x00, 0x1a, 0x2a, 0x20, 0x53, + 0x74, 0x69, 0x63, 0x6b, 0x53, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x02, 0x15, + 0x1f, 0x13, 0x00, 0xff, 0x00, 0x01, 0x0e, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0xa0, 0x00, 0x1a, 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x25, 0x3c, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x08, + 0x01, 0x00, 0x02, 0x00, 0x1f, 0x17, 0x16, 0x69, 0x00, 0x12, 0x00, 0x1f, + 0x01, 0x11, 0x08, 0x00, 0x01, 0x00, 0x1f, 0x0e, 0x12, 0x58, 0x00, 0x01, + 0xd2, 0x00, 0xba, 0x2a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x43, 0x6c, 0x61, + 0x70, 0x20, 0x28, 0x4f, 0x50, 0x4c, 0x33, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3c, 0x00, 0x0f, 0x0d, 0x9f, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x03, 0x1f, 0x14, 0x10, 0x08, 0x00, 0x0f, 0x1b, 0x1f, 0x11, 0x0f, 0x27, + 0x00, 0x01, 0x00, 0x1f, 0x03, 0x12, 0x09, 0x00, 0x00, 0xf6, 0x00, 0x71, + 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x3c, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x08, 0x01, 0x00, 0x02, 0x00, 0x1f, 0x17, + 0x16, 0x69, 0x00, 0x12, 0x00, 0x1f, 0x01, 0x11, 0x08, 0x00, 0x01, 0x00, + 0x1f, 0x0e, 0x12, 0x58, 0x00, 0x01, 0x82, 0x00, 0xad, 0x54, 0x6f, 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3b, 0x00, 0x7f, 0x00, + 0x5f, 0x00, 0x00, 0x06, 0x00, 0x74, 0x15, 0x5f, 0x18, 0x00, 0xff, 0x00, + 0x78, 0x28, 0x5f, 0x17, 0x0c, 0x56, 0x00, 0x74, 0x00, 0x5f, 0x0b, 0x00, + 0xf7, 0x00, 0x08, 0x98, 0x01, 0x2c, 0x2a, 0x20, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x3a, 0x33, 0x3f, 0x19, 0x1f, 0x8e, 0x0d, + 0x84, 0x00, 0x33, 0x00, 0x5f, 0x0a, 0x08, 0x64, 0x00, 0x7f, 0x14, 0x1f, + 0x8e, 0x0d, 0x94, 0x00, 0x71, 0x00, 0xdf, 0x09, 0x09, 0x25, 0x00, 0x00, + 0xfd, 0x00, 0x64, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x3b, 0x00, 0x7f, 0x00, 0x5f, 0x00, 0x00, 0x06, 0x00, 0x74, + 0x15, 0x5f, 0x18, 0x00, 0xff, 0x00, 0x78, 0x28, 0x5f, 0x17, 0x0c, 0x56, + 0x00, 0x74, 0x00, 0x5f, 0x0b, 0x00, 0xf7, 0x00, 0x08, 0x91, 0x01, 0x2c, + 0x2a, 0x20, 0x50, 0x65, 0x64, 0x61, 0x6c, 0x48, 0x69, 0x68, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x38, + 0x00, 0x3c, 0x00, 0x47, 0x0a, 0x1c, 0xb4, 0x00, 0x13, 0x00, 0xdf, 0x00, + 0x00, 0x04, 0x00, 0x7a, 0x00, 0xc3, 0x8a, 0x10, 0xb7, 0x00, 0x75, 0x07, + 0xc4, 0x0d, 0x1d, 0x59, 0x00, 0x00, 0x85, 0x00, 0x42, 0x54, 0x6f, 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x3b, 0x00, 0x7f, 0x00, + 0x5f, 0x00, 0x00, 0x06, 0x00, 0x74, 0x15, 0x5f, 0x18, 0x00, 0xff, 0x00, + 0x78, 0x28, 0x5f, 0x17, 0x0c, 0x56, 0x00, 0x74, 0x00, 0x5f, 0x0b, 0x00, + 0xf7, 0x00, 0x08, 0x98, 0x01, 0x2c, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, + 0x48, 0x69, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x2c, 0x00, 0x09, 0x00, 0x1f, 0x0e, 0x00, + 0x10, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x1f, + 0x17, 0x08, 0x44, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, + 0x9e, 0x04, 0x1d, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x3b, 0x00, 0x7f, 0x00, 0x5f, 0x00, 0x00, 0x06, 0x00, 0x74, + 0x15, 0x5f, 0x18, 0x00, 0xff, 0x00, 0x78, 0x28, 0x5f, 0x17, 0x0c, 0x56, + 0x00, 0x74, 0x00, 0x5f, 0x0b, 0x00, 0xf7, 0x00, 0x08, 0x98, 0x01, 0x2c, + 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x3b, + 0x00, 0x7f, 0x00, 0x5f, 0x00, 0x00, 0x06, 0x00, 0x74, 0x15, 0x5f, 0x18, + 0x00, 0xff, 0x00, 0x78, 0x28, 0x5f, 0x17, 0x0c, 0x56, 0x00, 0x74, 0x00, + 0x5f, 0x0b, 0x00, 0xf7, 0x00, 0x07, 0x5e, 0x01, 0x0a, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, + 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x10, 0x7f, 0x00, + 0x1f, 0x0b, 0x00, 0x40, 0x00, 0x71, 0x00, 0x1f, 0x16, 0x07, 0x34, 0x00, + 0x13, 0x00, 0x1f, 0x8b, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x9f, 0x1b, 0x07, + 0xa6, 0x00, 0x07, 0x44, 0x01, 0xd9, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x25, 0x3b, 0x00, 0x7f, 0x00, 0x5f, 0x00, 0x00, + 0x06, 0x00, 0x74, 0x15, 0x5f, 0x18, 0x00, 0xff, 0x00, 0x78, 0x28, 0x5f, + 0x17, 0x0c, 0x56, 0x00, 0x74, 0x00, 0x5f, 0x0b, 0x00, 0xf7, 0x00, 0x07, + 0x5e, 0x01, 0x0a, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0d, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x18, 0x15, 0x07, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x4e, 0x05, 0x7e, + 0x2a, 0x20, 0x43, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x20, 0x43, 0x72, + 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, + 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2c, + 0x00, 0x7f, 0x00, 0x1f, 0x07, 0x00, 0x52, 0x00, 0x71, 0x00, 0x1f, 0x15, + 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0c, 0x04, 0x84, 0x00, 0x07, 0x00, + 0x9f, 0x19, 0x06, 0xa3, 0x00, 0x25, 0x44, 0x07, 0x1c, 0x2a, 0x20, 0x52, + 0x69, 0x64, 0x65, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x1c, 0x00, 0x0d, 0x00, + 0x1f, 0x07, 0x07, 0xe1, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x11, 0x1f, 0x0a, 0x12, 0xe5, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x3a, 0x03, 0x2d, 0x2a, 0x20, 0x54, 0x61, 0x6d, 0x62, + 0x6f, 0x72, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x35, 0x22, 0x06, 0x5f, 0x05, 0x02, + 0x81, 0x00, 0x75, 0x00, 0x96, 0x0f, 0x0c, 0xa1, 0x00, 0x04, 0x01, 0x9f, + 0x05, 0x02, 0x53, 0x00, 0x3e, 0x08, 0x92, 0x0d, 0x0d, 0xa6, 0x00, 0x01, + 0x89, 0x00, 0xc1, 0x2a, 0x20, 0x53, 0x70, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x43, 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, + 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x2c, 0x00, 0x7f, 0x00, 0x1f, 0x05, 0x06, 0x50, 0x00, 0x71, + 0x00, 0x1f, 0x15, 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0b, 0x04, 0x85, + 0x00, 0x07, 0x00, 0x9f, 0x1a, 0x06, 0xa5, 0x00, 0x21, 0xcd, 0x03, 0x20, + 0x2a, 0x20, 0x43, 0x6f, 0x77, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3b, + 0x00, 0x2f, 0x00, 0x1f, 0x15, 0x13, 0x26, 0x00, 0x37, 0x20, 0x1f, 0x15, + 0x0d, 0x36, 0x00, 0x28, 0x23, 0x1f, 0x95, 0x0c, 0x26, 0x00, 0x32, 0x00, + 0x1f, 0x13, 0x10, 0x28, 0x00, 0x01, 0x61, 0x00, 0x99, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x32, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x2c, 0x00, 0x7f, 0x00, + 0x1f, 0x00, 0x00, 0x50, 0x00, 0x71, 0x00, 0x1f, 0x14, 0x06, 0x33, 0x00, + 0x13, 0x00, 0x1f, 0x0a, 0x04, 0x84, 0x00, 0x07, 0x00, 0x9f, 0x19, 0x06, + 0xa4, 0x00, 0x20, 0x5e, 0x06, 0x11, 0x2a, 0x20, 0x56, 0x69, 0x62, 0x65, + 0x53, 0x6c, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x34, 0x00, 0x00, 0x08, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x0a, 0x00, 0xf5, 0x00, 0x00, 0x15, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x0c, + 0xea, 0x03, 0xac, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0c, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x18, 0x14, 0x08, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0e, 0x05, 0x85, + 0x2a, 0x20, 0x48, 0x69, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, + 0x12, 0x12, 0x0a, 0xf9, 0x00, 0x00, 0xf0, 0x00, 0x71, 0x2a, 0x20, 0x4c, + 0x6f, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x36, 0x00, 0x01, 0x05, + 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, 0x0a, 0xf9, 0x00, + 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, 0x12, 0x12, 0x0a, + 0xf9, 0x00, 0x00, 0xe9, 0x00, 0x6a, 0x2a, 0x20, 0x4d, 0x75, 0x48, 0x69, + 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x36, 0x00, 0x01, 0x07, 0x9f, 0x0f, 0x00, + 0xf9, 0x00, 0x72, 0x00, 0x1e, 0x12, 0x0a, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x12, 0x00, 0xf9, 0x00, 0x12, 0x00, 0x12, 0x16, 0x0a, 0xfa, 0x00, 0x00, + 0xf6, 0x00, 0x5d, 0x2a, 0x20, 0x4f, 0x70, 0x48, 0x69, 0x43, 0x6f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x71, + 0x00, 0x17, 0x14, 0x0c, 0xfb, 0x00, 0x00, 0x00, 0x9c, 0x17, 0x00, 0xfa, + 0x00, 0x11, 0x00, 0x12, 0x10, 0x0c, 0xf8, 0x00, 0x01, 0x82, 0x00, 0x92, + 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x0d, 0x17, 0x14, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x16, 0x00, 0xfa, 0x00, 0x31, 0x06, + 0x12, 0x0f, 0x0a, 0xf8, 0x00, 0x02, 0x1c, 0x00, 0x99, 0x2a, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x54, 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2a, 0x05, 0x13, 0x06, + 0x9f, 0x19, 0x08, 0xc6, 0x00, 0x00, 0x0d, 0x5f, 0x13, 0x05, 0xbc, 0x00, + 0x36, 0x05, 0x5f, 0x19, 0x06, 0xa6, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, + 0x86, 0x00, 0x01, 0x32, 0x01, 0x68, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x54, + 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x2a, 0x05, 0x13, 0x06, 0x9f, 0x19, 0x08, + 0xca, 0x00, 0x00, 0x0a, 0x5f, 0x13, 0x05, 0xbc, 0x00, 0x36, 0x05, 0x5f, + 0x19, 0x06, 0xac, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, 0x8c, 0x00, 0x01, + 0x32, 0x00, 0x21, 0x2a, 0x20, 0x48, 0x69, 0x41, 0x67, 0x6f, 0x67, 0x6f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x3c, 0x00, 0x3e, 0x17, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, + 0x18, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x34, 0x00, 0x54, 0x10, 0x11, 0xa8, + 0x00, 0x04, 0x00, 0x54, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x18, 0x00, 0x85, + 0x2a, 0x20, 0x4c, 0x6f, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x00, 0x3e, 0x1a, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, 0x18, 0x9f, 0x09, + 0x09, 0x86, 0x00, 0x34, 0x00, 0x57, 0x10, 0x11, 0xa8, 0x00, 0x04, 0x00, + 0x15, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x61, 0x00, 0x99, 0x2a, 0x20, 0x43, + 0x61, 0x62, 0x61, 0x73, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x31, 0x35, 0x2f, 0x06, + 0x5f, 0x05, 0x02, 0x81, 0x00, 0x7d, 0x02, 0x99, 0x05, 0x02, 0x71, 0x00, + 0x0f, 0x01, 0x9f, 0x05, 0x02, 0x53, 0x00, 0x33, 0x00, 0x8d, 0x11, 0x0b, + 0xa9, 0x00, 0x01, 0x75, 0x00, 0x49, 0x2a, 0x20, 0x4d, 0x61, 0x72, 0x61, + 0x63, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x00, 0x8e, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x1f, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0x92, 0x00, 0x49, 0x2a, 0x20, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x57, 0x68, + 0x69, 0x73, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x37, 0x04, 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x0b, 0x16, 0x2b, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0x89, 0x00, 0x50, + 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x04, + 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x08, 0x16, 0x2b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03, 0x55, 0x00, 0x50, 0x2a, 0x20, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x0c, 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x58, 0x00, 0x99, 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, + 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, 0x1f, 0x09, 0x09, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x09, + 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xda, 0x00, 0xa0, 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x45, 0x3c, 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, + 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, + 0x00, 0x19, 0x05, 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xad, 0x00, 0x92, + 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, + 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, + 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, + 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xc1, 0x00, 0xa0, 0x2a, 0x20, 0x57, + 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x3c, 0x00, 0x17, 0x14, + 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, + 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, 0xdf, 0x14, 0x0d, + 0x18, 0x00, 0x00, 0xc8, 0x00, 0x99, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x20, 0x43, 0x75, 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3c, 0x00, 0x72, 0x27, 0x0c, 0x0f, 0x0f, + 0x07, 0x00, 0x01, 0x1b, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x78, 0x00, 0x4e, + 0x06, 0x11, 0x06, 0x00, 0x08, 0x00, 0x8e, 0x06, 0x11, 0x06, 0x00, 0x01, + 0x4d, 0x01, 0x46, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x43, 0x75, + 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x3c, 0x00, 0x01, 0x26, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x01, + 0x1e, 0x0c, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x0e, 0x06, 0x11, 0x06, + 0x00, 0x01, 0x17, 0x0e, 0x0f, 0x0e, 0xb6, 0x00, 0x01, 0xed, 0x01, 0xb8, + 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, + 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, + 0x00, 0x03, 0x1a, 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x0e, 0x0d, 0x8b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x44, 0x00, 0x28, 0x2a, 0x20, 0x4f, + 0x70, 0x65, 0x6e, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, 0x00, 0x03, 0x1a, + 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x1f, 0x08, 0x08, 0x85, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x4c, 0x02, 0x8d, 0x2a, 0x20, 0x53, 0x68, 0x61, 0x6b, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x0e, 0x8c, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0xce, 0x00, 0x49, 0x2a, 0x20, 0x4a, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, + 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x3c, 0x00, 0x05, 0x18, 0x1f, 0x04, 0x0f, 0xf4, 0x00, 0x05, + 0x1c, 0x1f, 0x09, 0x10, 0xf0, 0x00, 0x04, 0x09, 0x51, 0x0b, 0x07, 0xa6, + 0x00, 0x04, 0x0e, 0x51, 0x0b, 0x07, 0xa5, 0x00, 0x08, 0x98, 0x01, 0xb8, + 0x2a, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x54, 0x72, 0x65, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x2c, + 0x00, 0x05, 0x09, 0x14, 0x0d, 0x00, 0x10, 0x00, 0x05, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x14, 0x15, 0x08, 0x44, 0x00, 0x08, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x3a, 0x06, 0x46, 0x2a, 0x20, 0x43, + 0x61, 0x73, 0x74, 0x61, 0x6e, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, + 0x1f, 0x0b, 0x00, 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x04, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0x64, 0x00, 0x14, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x53, 0x75, 0x72, 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, + 0xff, 0x00, 0x71, 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, + 0x14, 0x00, 0xff, 0x00, 0x31, 0x00, 0x0e, 0x0d, 0x0a, 0xf6, 0x00, 0x04, + 0x16, 0x01, 0xc5, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x75, 0x72, + 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x29, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xff, 0x00, 0x71, + 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, 0x14, 0x00, 0xff, + 0x00, 0x31, 0x00, 0x0e, 0x0b, 0x08, 0xf4, 0x00, 0x08, 0xcd, 0x07, 0x9a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, + 0x00, 0x01, 0x01, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, 0xe6, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x3d, + 0x05, 0x33, 0x04, 0xdf, 0x06, 0x10, 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, + 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, 0x10, 0x1f, 0xf8, 0x00, 0x01, 0x00, + 0x9f, 0x10, 0x0e, 0x76, 0x00, 0x01, 0x11, 0x00, 0xdc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x35, 0x05, 0x33, 0x04, + 0xdf, 0x00, 0x10, 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, 0x0e, 0xf9, 0x00, + 0x01, 0x00, 0x9f, 0x11, 0x1f, 0xf8, 0x08, 0x01, 0x00, 0x9f, 0x10, 0x0d, + 0x76, 0x00, 0x9c, 0x40, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x37, 0x00, 0x08, 0x1f, 0x0a, 0x0a, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x8e, 0x12, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x18, 0x00, 0xa0, 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x3c, 0x00, 0x11, 0x00, 0x14, 0x00, 0x05, 0x00, 0x00, 0x02, + 0x00, 0x1f, 0x17, 0x18, 0x09, 0x00, 0x10, 0x00, 0x1d, 0x1f, 0x11, 0x08, + 0x00, 0x00, 0x00, 0x1f, 0x00, 0x11, 0x09, 0x00, 0x01, 0x1e, 0x00, 0xfd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x1f, + 0x00, 0x01, 0x27, 0x1f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x16, + 0x00, 0xff, 0x00, 0x01, 0x27, 0x9f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, + 0x12, 0x18, 0x00, 0xff, 0x00, 0x00, 0x71, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x18, 0x00, 0x02, 0x06, + 0x1f, 0x12, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x16, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, + 0xff, 0x00, 0x00, 0x85, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x18, 0x00, 0x02, 0x06, 0x1f, 0x12, 0x00, + 0xff, 0x00, 0x01, 0x01, 0x1f, 0x16, 0x00, 0xff, 0x00, 0x02, 0x17, 0x9f, + 0x08, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x0e, 0x00, 0xf7, 0x00, 0x02, + 0x01, 0x01, 0x61, 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, + 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x23, 0x22, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x14, 0x07, 0x00, 0x05, + 0x7f, 0x00, 0x0b, 0x0f, 0xf5, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0c, 0xf6, + 0x00, 0x01, 0x00, 0x5f, 0x00, 0x0e, 0x07, 0x00, 0x02, 0xb5, 0x00, 0xf6, + 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, 0x6d, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x1a, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x14, 0x07, 0x00, 0x05, 0x7f, 0x00, 0x0b, + 0x0f, 0xf4, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0d, 0xf6, 0x00, 0x01, 0x00, + 0x5f, 0x00, 0x0e, 0x07, 0x00, 0x02, 0xb5, 0x01, 0xe6, 0x2a, 0x20, 0x53, + 0x74, 0x69, 0x63, 0x6b, 0x53, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x02, 0x15, + 0x1f, 0x13, 0x00, 0xff, 0x00, 0x01, 0x0e, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0xa0, 0x00, 0x1a, 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x3c, 0x00, 0x11, 0x00, 0x14, 0x00, 0x05, + 0x00, 0x00, 0x02, 0x00, 0x1f, 0x17, 0x18, 0x09, 0x00, 0x10, 0x00, 0x1d, + 0x1f, 0x0f, 0x07, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x11, 0x09, 0x00, 0x01, + 0xed, 0x00, 0xe2, 0x2a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x43, 0x6c, 0x61, + 0x70, 0x20, 0x28, 0x4f, 0x50, 0x4c, 0x33, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3c, 0x00, 0x0f, 0x0d, 0x9f, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x03, 0x1f, 0x14, 0x10, 0x08, 0x00, 0x0f, 0x1b, 0x1f, 0x11, 0x0f, 0x27, + 0x00, 0x01, 0x00, 0x1f, 0x03, 0x12, 0x09, 0x00, 0x00, 0xf6, 0x00, 0x71, + 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x3c, + 0x00, 0x10, 0x00, 0x14, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x1f, 0x17, + 0x18, 0x09, 0x00, 0x10, 0x00, 0x1d, 0x10, 0x0e, 0xa7, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x11, 0x09, 0x00, 0x01, 0xb1, 0x00, 0xf6, 0x2a, 0x20, 0x4c, + 0x6f, 0x77, 0x20, 0x46, 0x6c, 0x6f, 0x6f, 0x72, 0x20, 0x54, 0x6f, 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x3d, 0x00, 0x31, 0x16, + 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x13, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, + 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x06, 0x32, 0x02, 0xbc, 0x2a, 0x20, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x3a, 0x33, 0x3f, 0x19, 0x1f, 0x8e, 0x0d, + 0x84, 0x00, 0x33, 0x00, 0x5f, 0x0a, 0x08, 0x64, 0x00, 0x7f, 0x14, 0x1f, + 0x8e, 0x0d, 0x94, 0x00, 0x71, 0x00, 0xdf, 0x09, 0x09, 0x25, 0x00, 0x00, + 0xfd, 0x00, 0x64, 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x46, 0x6c, + 0x6f, 0x6f, 0x72, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x3d, 0x00, 0x31, 0x16, 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, + 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x0e, 0xf6, + 0x00, 0x10, 0x03, 0x1e, 0x11, 0x09, 0xf6, 0x00, 0x06, 0x25, 0x02, 0xb5, + 0x2a, 0x20, 0x50, 0x65, 0x64, 0x61, 0x6c, 0x48, 0x69, 0x68, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x38, + 0x00, 0x3c, 0x00, 0x47, 0x0a, 0x1c, 0xb4, 0x00, 0x13, 0x00, 0xdf, 0x00, + 0x00, 0x04, 0x00, 0x7a, 0x00, 0xc3, 0x8a, 0x10, 0xb7, 0x00, 0x75, 0x07, + 0xc4, 0x0d, 0x1d, 0x59, 0x00, 0x00, 0x85, 0x00, 0x42, 0x2a, 0x20, 0x4c, + 0x6f, 0x77, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3d, 0x00, 0x31, 0x16, + 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, + 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x06, 0x1e, 0x02, 0x22, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, + 0x48, 0x69, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x2c, 0x00, 0x09, 0x00, 0x1f, 0x0e, 0x00, + 0x10, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x1f, + 0x17, 0x08, 0x44, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, + 0x9e, 0x04, 0x1d, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x2d, 0x6d, 0x65, 0x64, + 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1d, 0x3d, 0x00, 0x30, 0x16, 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, + 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, + 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x06, 0x11, 0x02, 0x44, + 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, 0x2d, 0x6d, 0x65, 0x64, 0x20, 0x54, + 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, + 0x00, 0x30, 0x16, 0x1e, 0x18, 0x13, 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, + 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, + 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x04, 0xde, 0x02, 0x94, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, + 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x10, 0x7f, 0x00, + 0x1f, 0x0b, 0x00, 0x40, 0x00, 0x71, 0x00, 0x1f, 0x16, 0x07, 0x34, 0x00, + 0x13, 0x00, 0x1f, 0x8b, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x9f, 0x1b, 0x07, + 0xa6, 0x00, 0x07, 0x44, 0x01, 0xd9, 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, + 0x20, 0x54, 0x6f, 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x3d, 0x00, 0x31, 0x16, 0x1e, 0x1a, 0x03, + 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, + 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x04, + 0xd8, 0x02, 0x44, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0d, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x18, 0x15, 0x07, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x4e, 0x05, 0x7e, + 0x2a, 0x20, 0x43, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x20, 0x43, 0x72, + 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, + 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2c, + 0x00, 0x7f, 0x00, 0x1f, 0x07, 0x00, 0x52, 0x00, 0x71, 0x00, 0x1f, 0x15, + 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0c, 0x04, 0x84, 0x00, 0x07, 0x00, + 0x9f, 0x19, 0x06, 0xa3, 0x00, 0x25, 0x44, 0x07, 0x1c, 0x2a, 0x20, 0x52, + 0x69, 0x64, 0x65, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x1c, 0x00, 0x0d, 0x00, + 0x1f, 0x07, 0x07, 0xe1, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x11, 0x1f, 0x0a, 0x12, 0xe5, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x3a, 0x03, 0x2d, 0x2a, 0x20, 0x54, 0x61, 0x6d, 0x62, + 0x6f, 0x72, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x35, 0x22, 0x06, 0x5f, 0x05, 0x02, + 0x81, 0x00, 0x75, 0x00, 0x96, 0x0f, 0x0c, 0xa1, 0x00, 0x04, 0x01, 0x9f, + 0x05, 0x02, 0x53, 0x00, 0x3e, 0x08, 0x92, 0x0d, 0x0d, 0xa6, 0x00, 0x01, + 0x89, 0x00, 0xc1, 0x2a, 0x20, 0x53, 0x70, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x43, 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, + 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x2c, 0x00, 0x7f, 0x00, 0x1f, 0x05, 0x06, 0x50, 0x00, 0x71, + 0x00, 0x1f, 0x15, 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0b, 0x04, 0x85, + 0x00, 0x07, 0x00, 0x9f, 0x1a, 0x06, 0xa5, 0x00, 0x21, 0xcd, 0x03, 0x20, + 0x2a, 0x20, 0x43, 0x6f, 0x77, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3b, + 0x00, 0x2f, 0x00, 0x1f, 0x15, 0x13, 0x26, 0x00, 0x37, 0x20, 0x1f, 0x15, + 0x0d, 0x36, 0x00, 0x28, 0x23, 0x1f, 0x95, 0x0c, 0x26, 0x00, 0x32, 0x00, + 0x1f, 0x13, 0x10, 0x28, 0x00, 0x01, 0x61, 0x00, 0x99, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x32, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x2c, 0x00, 0x7f, 0x00, + 0x1f, 0x00, 0x00, 0x50, 0x00, 0x71, 0x00, 0x1f, 0x14, 0x06, 0x33, 0x00, + 0x13, 0x00, 0x1f, 0x0a, 0x04, 0x84, 0x00, 0x07, 0x00, 0x9f, 0x19, 0x06, + 0xa4, 0x00, 0x20, 0x5e, 0x06, 0x11, 0x2a, 0x20, 0x56, 0x69, 0x62, 0x65, + 0x53, 0x6c, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x34, 0x00, 0x00, 0x08, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x0a, 0x00, 0xf5, 0x00, 0x00, 0x15, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x0c, + 0xea, 0x03, 0xac, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0c, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x18, 0x14, 0x08, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0e, 0x05, 0x85, + 0x2a, 0x20, 0x48, 0x69, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, + 0x12, 0x12, 0x0a, 0xf9, 0x00, 0x00, 0xf0, 0x00, 0x71, 0x2a, 0x20, 0x4c, + 0x6f, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x36, 0x00, 0x01, 0x05, + 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, 0x0a, 0xf9, 0x00, + 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, 0x12, 0x12, 0x0a, + 0xf9, 0x00, 0x00, 0xe9, 0x00, 0x6a, 0x2a, 0x20, 0x4d, 0x75, 0x48, 0x69, + 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x36, 0x00, 0x01, 0x07, 0x9f, 0x0f, 0x00, + 0xf9, 0x00, 0x72, 0x00, 0x1e, 0x12, 0x0a, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x12, 0x00, 0xf9, 0x00, 0x12, 0x00, 0x12, 0x16, 0x0a, 0xfa, 0x00, 0x00, + 0xf6, 0x00, 0x5d, 0x2a, 0x20, 0x4f, 0x70, 0x48, 0x69, 0x43, 0x6f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x71, + 0x00, 0x17, 0x14, 0x0c, 0xfb, 0x00, 0x00, 0x00, 0x9c, 0x17, 0x00, 0xfa, + 0x00, 0x11, 0x00, 0x12, 0x10, 0x0c, 0xf8, 0x00, 0x01, 0x82, 0x00, 0x92, + 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x0d, 0x17, 0x14, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x16, 0x00, 0xfa, 0x00, 0x31, 0x06, + 0x12, 0x0f, 0x0a, 0xf8, 0x00, 0x02, 0x1c, 0x00, 0x99, 0x2a, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x54, 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2a, 0x05, 0x13, 0x06, + 0x9f, 0x19, 0x08, 0xc6, 0x00, 0x00, 0x0d, 0x5f, 0x13, 0x05, 0xbc, 0x00, + 0x36, 0x05, 0x5f, 0x19, 0x06, 0xa6, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, + 0x86, 0x00, 0x01, 0x32, 0x01, 0x68, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x54, + 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x2a, 0x05, 0x13, 0x06, 0x9f, 0x19, 0x08, + 0xca, 0x00, 0x00, 0x0a, 0x5f, 0x13, 0x05, 0xbc, 0x00, 0x36, 0x05, 0x5f, + 0x19, 0x06, 0xac, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, 0x8c, 0x00, 0x01, + 0x32, 0x00, 0x21, 0x2a, 0x20, 0x48, 0x69, 0x41, 0x67, 0x6f, 0x67, 0x6f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x3c, 0x00, 0x3e, 0x17, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, + 0x18, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x34, 0x00, 0x54, 0x10, 0x11, 0xa8, + 0x00, 0x04, 0x00, 0x54, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x18, 0x00, 0x85, + 0x2a, 0x20, 0x4c, 0x6f, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x00, 0x3e, 0x1a, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, 0x18, 0x9f, 0x09, + 0x09, 0x86, 0x00, 0x34, 0x00, 0x57, 0x10, 0x11, 0xa8, 0x00, 0x04, 0x00, + 0x15, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x61, 0x00, 0x99, 0x2a, 0x20, 0x43, + 0x61, 0x62, 0x61, 0x73, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x31, 0x35, 0x2f, 0x06, + 0x5f, 0x05, 0x02, 0x81, 0x00, 0x7d, 0x02, 0x99, 0x05, 0x02, 0x71, 0x00, + 0x0f, 0x01, 0x9f, 0x05, 0x02, 0x53, 0x00, 0x33, 0x00, 0x8d, 0x11, 0x0b, + 0xa9, 0x00, 0x01, 0x75, 0x00, 0x49, 0x2a, 0x20, 0x4d, 0x61, 0x72, 0x61, + 0x63, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x00, 0x8e, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x1f, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0x92, 0x00, 0x49, 0x2a, 0x20, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x57, 0x68, + 0x69, 0x73, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x37, 0x04, 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x0b, 0x16, 0x2b, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0x89, 0x00, 0x50, + 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x04, + 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x08, 0x16, 0x2b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03, 0x55, 0x00, 0x50, 0x2a, 0x20, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x0c, 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x58, 0x00, 0x99, 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, + 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, 0x1f, 0x09, 0x09, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x09, + 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xda, 0x00, 0xa0, 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x45, 0x3c, 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, + 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, + 0x00, 0x19, 0x05, 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xad, 0x00, 0x92, + 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, + 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, + 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, + 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xc1, 0x00, 0xa0, 0x2a, 0x20, 0x57, + 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x3c, 0x00, 0x17, 0x14, + 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, + 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, 0xdf, 0x14, 0x0d, + 0x18, 0x00, 0x00, 0xc8, 0x00, 0x99, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x20, 0x43, 0x75, 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3c, 0x00, 0x72, 0x27, 0x0c, 0x0f, 0x0f, + 0x07, 0x00, 0x01, 0x1b, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x78, 0x00, 0x4e, + 0x06, 0x11, 0x06, 0x00, 0x08, 0x00, 0x8e, 0x06, 0x11, 0x06, 0x00, 0x01, + 0x4d, 0x01, 0x46, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x43, 0x75, + 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x3c, 0x00, 0x01, 0x26, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x01, + 0x1e, 0x0c, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x0e, 0x06, 0x11, 0x06, + 0x00, 0x01, 0x17, 0x0e, 0x0f, 0x0e, 0xb6, 0x00, 0x01, 0xed, 0x01, 0xb8, + 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, + 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, + 0x00, 0x03, 0x1a, 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x0e, 0x0d, 0x8b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x44, 0x00, 0x28, 0x2a, 0x20, 0x4f, + 0x70, 0x65, 0x6e, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, 0x00, 0x03, 0x1a, + 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x1f, 0x08, 0x08, 0x85, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x4c, 0x02, 0x8d, 0x2a, 0x20, 0x53, 0x68, 0x61, 0x6b, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x0e, 0x8c, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0xce, 0x00, 0x49, 0x2a, 0x20, 0x4a, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, + 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x3c, 0x00, 0x05, 0x18, 0x1f, 0x04, 0x0f, 0xf4, 0x00, 0x05, + 0x1c, 0x1f, 0x09, 0x10, 0xf0, 0x00, 0x04, 0x09, 0x51, 0x0b, 0x07, 0xa6, + 0x00, 0x04, 0x0e, 0x51, 0x0b, 0x07, 0xa5, 0x00, 0x08, 0x98, 0x01, 0xb8, + 0x2a, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x54, 0x72, 0x65, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x2c, + 0x00, 0x05, 0x09, 0x14, 0x0d, 0x00, 0x10, 0x00, 0x05, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x14, 0x15, 0x08, 0x44, 0x00, 0x08, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x3a, 0x06, 0x46, 0x2a, 0x20, 0x43, + 0x61, 0x73, 0x74, 0x61, 0x6e, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, + 0x1f, 0x0b, 0x00, 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x04, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0x64, 0x00, 0x14, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x53, 0x75, 0x72, 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, + 0xff, 0x00, 0x71, 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, + 0x14, 0x00, 0xff, 0x00, 0x31, 0x00, 0x0e, 0x0d, 0x0a, 0xf6, 0x00, 0x04, + 0x16, 0x01, 0xc5, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x75, 0x72, + 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x29, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xff, 0x00, 0x71, + 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, 0x14, 0x00, 0xff, + 0x00, 0x31, 0x00, 0x0e, 0x0b, 0x08, 0xf4, 0x00, 0x08, 0xcd, 0x07, 0x9a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x38, + 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x2f, 0x1f, 0x12, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0xff, 0x00, 0x01, 0x08, + 0x13, 0x14, 0x00, 0xfa, 0x00, 0x00, 0xa0, 0x00, 0x71, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x10, 0x00, 0x00, 0x0f, + 0x1f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x1b, 0x1f, 0x12, 0x00, 0xff, 0x00, + 0x00, 0x19, 0x9f, 0x00, 0x00, 0xff, 0x00, 0x01, 0x08, 0x13, 0x14, 0x00, + 0xfa, 0x00, 0x00, 0x92, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x53, 0x6c, + 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xeb, 0x32, 0x34, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x00, 0x04, 0x00, 0x05, + 0x00, 0x1f, 0x19, 0x00, 0xf6, 0x00, 0x00, 0x0e, 0x54, 0x1a, 0x11, 0x18, + 0x00, 0x03, 0x08, 0x94, 0x12, 0x00, 0xf8, 0x00, 0x01, 0x18, 0x01, 0x0a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x18, + 0x00, 0x02, 0x06, 0x1f, 0x12, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x13, + 0x00, 0xff, 0x00, 0x02, 0x21, 0x9f, 0x16, 0x00, 0xff, 0x00, 0x01, 0x00, + 0x9f, 0x15, 0x00, 0xff, 0x00, 0x00, 0x71, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x20, + 0x00, 0x31, 0x07, 0x17, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x04, 0x16, 0x00, + 0x00, 0x05, 0x00, 0x7c, 0x0e, 0x19, 0x00, 0x00, 0x02, 0x00, 0x76, 0x06, + 0x08, 0x08, 0x00, 0x35, 0x00, 0x9c, 0x40, 0x06, 0x89, 0x57, 0x49, 0x50, + 0x20, 0x54, 0x68, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xd0, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x54, 0x1f, 0x0e, 0x07, 0x00, 0x01, 0x00, 0x1f, 0x00, 0x0a, 0x0a, 0x00, + 0x01, 0x00, 0x0f, 0x0b, 0x12, 0xa6, 0x00, 0x02, 0x26, 0x03, 0x1f, 0x0e, + 0xfe, 0x00, 0x06, 0x89, 0x03, 0x19, 0x57, 0x49, 0x50, 0x20, 0x57, 0x69, + 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x20, 0x0f, 0x00, 0x1f, 0x80, 0x00, + 0x00, 0x00, 0x3f, 0x1a, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x19, 0x06, 0x00, 0x00, 0x05, 0x00, 0x9c, + 0x40, 0x07, 0x80, 0x57, 0x49, 0x50, 0x20, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x20, 0x0f, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x3f, + 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x65, 0x0d, 0x0a, 0x08, 0x00, 0x35, 0x00, 0x9c, 0x40, 0x06, 0x18, + 0x42, 0x75, 0x62, 0x62, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x2e, 0x0f, + 0x00, 0x04, 0x0e, 0x5f, 0x0d, 0x06, 0xf4, 0x08, 0x04, 0x0e, 0x5f, 0x0f, + 0x06, 0xf4, 0x0e, 0x07, 0x0e, 0x5f, 0x0e, 0x06, 0xf4, 0x08, 0x04, 0x0e, + 0x5f, 0x0e, 0x06, 0xf4, 0x08, 0x9c, 0x40, 0x00, 0x5d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x06, 0x21, 0x71, 0x05, + 0x11, 0x80, 0x00, 0x17, 0x00, 0x31, 0x0b, 0x8f, 0x8a, 0x0e, 0x68, 0x0e, + 0x01, 0x17, 0x50, 0x1a, 0x0d, 0x65, 0x0e, 0x01, 0x15, 0x4b, 0x8a, 0x11, + 0x04, 0x0e, 0x9c, 0x40, 0x01, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x6f, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xea, 0x3f, 0x3c, + 0x00, 0x36, 0x10, 0x1f, 0x16, 0x00, 0xf0, 0x0e, 0x35, 0x12, 0x1f, 0x18, + 0x00, 0x00, 0x00, 0x7c, 0x08, 0x0e, 0x12, 0x12, 0xf9, 0x00, 0x75, 0x0c, + 0x8e, 0x12, 0x12, 0xf9, 0x00, 0x01, 0x7c, 0x00, 0xa0, 0x48, 0x6f, 0x72, + 0x73, 0x65, 0x20, 0x47, 0x61, 0x6c, 0x6c, 0x6f, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x3d, 0x00, 0x00, 0x00, + 0x16, 0x11, 0x00, 0xf0, 0x08, 0x02, 0x11, 0x1f, 0x0d, 0x00, 0xf7, 0x08, + 0x02, 0x11, 0x1f, 0x0d, 0x00, 0xf7, 0x08, 0x02, 0x11, 0x1f, 0x0d, 0x00, + 0xf7, 0x08, 0x9c, 0x40, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x1f, 0x00, 0x07, 0x19, 0xc6, 0x10, 0x0e, + 0x07, 0x00, 0x15, 0x11, 0x0f, 0x0d, 0x0e, 0x0a, 0x00, 0x08, 0x10, 0xc8, + 0x0e, 0x10, 0x56, 0x00, 0x06, 0x00, 0x8b, 0x0e, 0x10, 0xb6, 0x00, 0x02, + 0x44, 0x01, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x38, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, + 0x17, 0x0e, 0x0f, 0x00, 0xf7, 0x00, 0x71, 0x00, 0x05, 0x06, 0x00, 0x06, + 0x00, 0x02, 0x00, 0x0b, 0x04, 0x0d, 0x07, 0x00, 0x04, 0x60, 0x01, 0x9d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x04, 0x00, 0x02, 0x28, 0xc3, 0x03, 0x01, + 0x61, 0x00, 0x00, 0x23, 0x07, 0x09, 0x00, 0xf3, 0x00, 0x01, 0x06, 0x85, + 0x06, 0x02, 0x73, 0x00, 0x02, 0x1d, 0x0a, 0x06, 0x00, 0xf4, 0x00, 0x24, + 0xfa, 0x09, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xe8, 0x27, 0x3c, 0x00, 0x74, 0x0d, 0x15, 0x00, 0x00, 0x07, 0x00, 0x02, + 0x17, 0x0d, 0x0f, 0x00, 0xf7, 0x00, 0x70, 0x18, 0x13, 0x0b, 0x0e, 0x06, + 0x08, 0x02, 0x00, 0x09, 0x03, 0x0d, 0x07, 0x00, 0x07, 0x86, 0x02, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x20, 0x43, 0x61, 0x6c, 0x6c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x1f, + 0x00, 0x07, 0x19, 0xdf, 0x10, 0x0e, 0x07, 0x00, 0x15, 0x0f, 0x1f, 0x00, + 0x00, 0x0f, 0x00, 0x08, 0x10, 0xdf, 0x0e, 0x10, 0x5d, 0x00, 0x06, 0x0f, + 0x9f, 0x00, 0x10, 0x8f, 0x00, 0x9c, 0x40, 0x00, 0x56, 0x44, 0x6f, 0x6f, + 0x72, 0x53, 0x71, 0x65, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x30, 0x3c, 0x00, 0x31, 0x00, + 0x5f, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x09, 0x00, + 0x01, 0x16, 0x0c, 0x03, 0x0e, 0x1a, 0x0f, 0x01, 0x24, 0x0a, 0x05, 0x0e, + 0x17, 0x0f, 0x04, 0x10, 0x00, 0x7e, 0x44, 0x6f, 0x6f, 0x72, 0x53, 0x6c, + 0x61, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3d, 0x05, 0x30, 0x04, 0x1f, 0x06, 0x10, + 0xf3, 0x00, 0x00, 0x00, 0x13, 0x12, 0x0e, 0xf9, 0x00, 0x00, 0x00, 0x0e, + 0x0f, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x10, 0x18, 0x0d, 0x76, 0x00, 0x02, + 0xa1, 0x01, 0xbe, 0x53, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x43, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x35, 0x05, 0x30, 0x04, 0x1f, 0x06, 0x10, 0xf3, 0x00, 0x00, + 0x00, 0x0e, 0x18, 0x0e, 0xfb, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x1f, 0xfb, + 0x00, 0x00, 0x00, 0x0d, 0x19, 0x0c, 0x78, 0x00, 0x03, 0x26, 0x00, 0xfd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, + 0x37, 0x00, 0x08, 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x8e, 0x10, 0x18, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd9, 0x00, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x3c, 0x00, 0x05, 0x18, + 0x1f, 0x08, 0x1a, 0x64, 0x08, 0x05, 0x1c, 0x1f, 0x09, 0x14, 0x50, 0x08, + 0x04, 0x09, 0x51, 0x0a, 0x1f, 0xa3, 0x08, 0x04, 0x0e, 0x51, 0x0c, 0x1e, + 0xa5, 0x08, 0x9c, 0x40, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x34, 0x3c, 0x00, 0x00, 0x28, 0x1f, 0x0d, 0x1f, + 0x2f, 0x0a, 0x36, 0x7f, 0x1f, 0x00, 0x00, 0x0f, 0x0e, 0x04, 0x04, 0x1f, + 0x09, 0x00, 0x0f, 0x00, 0x32, 0x7f, 0x1f, 0x09, 0x13, 0x0f, 0x00, 0x9c, + 0x40, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x61, 0x72, 0x20, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x04, + 0x00, 0x00, 0x0a, 0x14, 0x1a, 0x11, 0x98, 0x0c, 0x03, 0x00, 0x10, 0x19, + 0x11, 0x98, 0x0c, 0x01, 0x08, 0x1b, 0x0d, 0x00, 0x38, 0x00, 0x31, 0x08, + 0x1c, 0x0b, 0x12, 0x2f, 0x00, 0x9c, 0x40, 0x00, 0xc1, 0x43, 0x61, 0x72, + 0x20, 0x53, 0x74, 0x6f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xee, 0x3e, 0x3c, 0x20, 0x7e, 0x15, + 0x0b, 0x80, 0x00, 0x00, 0x00, 0x3e, 0x16, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x08, 0x8e, 0x89, 0x00, 0xf7, 0x00, 0x40, 0x08, 0x8e, 0x89, 0x00, + 0xf7, 0x00, 0x09, 0xbd, 0x01, 0x5a, 0x43, 0x61, 0x72, 0x20, 0x50, 0x61, + 0x73, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xf4, 0x40, 0x33, 0x10, 0x06, 0x13, 0x18, 0x06, 0x04, + 0x16, 0x00, 0x35, 0x1c, 0x1a, 0x08, 0x03, 0x36, 0x00, 0x32, 0x23, 0x1a, + 0x07, 0x03, 0x36, 0x00, 0x72, 0x03, 0x09, 0x80, 0x0c, 0x07, 0x00, 0x07, + 0x9a, 0x01, 0xd9, 0x43, 0x61, 0x72, 0x20, 0x43, 0x72, 0x61, 0x73, 0x68, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4b, 0x3c, 0x20, 0x0f, 0x00, 0x1f, 0x00, 0x00, 0xf0, 0x08, 0x71, + 0x00, 0x1f, 0x16, 0x00, 0xf0, 0x0e, 0x00, 0x00, 0x1b, 0x8b, 0x00, 0xff, + 0x00, 0x30, 0x01, 0x1b, 0x8b, 0x00, 0xff, 0x00, 0x06, 0x90, 0x00, 0x56, + 0x53, 0x69, 0x72, 0x65, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x2c, + 0x17, 0x08, 0x26, 0x11, 0x9f, 0x00, 0x07, 0x00, 0x76, 0x24, 0x0b, 0x9f, + 0x00, 0x07, 0x00, 0x02, 0x0b, 0x1f, 0x8f, 0x00, 0xf8, 0x0a, 0x33, 0x0a, + 0x1f, 0x8f, 0x00, 0xf9, 0x0a, 0x00, 0xa6, 0x00, 0x6a, 0x54, 0x72, 0x61, + 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x00, 0x30, 0x00, + 0x1f, 0x0f, 0x00, 0xf4, 0x08, 0x01, 0x26, 0x1f, 0x0d, 0x00, 0xf4, 0x08, + 0x30, 0x00, 0x1f, 0x0c, 0x00, 0xf4, 0x08, 0x70, 0x0c, 0x9f, 0x11, 0x01, + 0x17, 0x00, 0x39, 0x8d, 0x03, 0x8a, 0x4a, 0x65, 0x74, 0x70, 0x61, 0x6c, + 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x3e, 0x00, 0x01, 0x06, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x0c, 0x05, 0x00, 0x00, 0x05, 0x00, 0x71, 0x16, 0x05, + 0x00, 0x00, 0xf6, 0x00, 0x03, 0x0b, 0x05, 0x00, 0x00, 0x06, 0x00, 0x9c, + 0x40, 0x07, 0x36, 0x53, 0x74, 0x61, 0x72, 0x73, 0x68, 0x69, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x3c, 0x13, 0x3b, 0x1b, 0x1f, 0x00, 0x00, 0x03, 0x00, 0x7c, + 0x32, 0x1f, 0x00, 0x00, 0x03, 0x00, 0x32, 0x08, 0x05, 0x80, 0x00, 0x06, + 0x00, 0x73, 0x08, 0x05, 0x80, 0x00, 0x06, 0x00, 0x9c, 0x40, 0x03, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x4f, 0x3c, + 0x13, 0x3b, 0x22, 0x18, 0x08, 0x0f, 0x53, 0x08, 0x7c, 0x2c, 0x12, 0x05, + 0x0f, 0x23, 0x08, 0x32, 0x08, 0x0e, 0x80, 0x00, 0x06, 0x00, 0x73, 0x08, + 0x0f, 0x80, 0x00, 0x06, 0x00, 0x9c, 0x40, 0x03, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x04, 0x00, 0x00, 0x0a, + 0x10, 0x1a, 0x11, 0x98, 0x0c, 0x03, 0x00, 0x10, 0x19, 0x11, 0x98, 0x0c, + 0x01, 0x08, 0x1d, 0x0d, 0x00, 0x38, 0x00, 0x31, 0x08, 0x17, 0x0b, 0x12, + 0x2f, 0x00, 0x9c, 0x40, 0x00, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xf4, 0x26, 0x0f, 0x00, 0x04, 0x0e, 0x5f, 0x0d, 0x06, + 0xf4, 0x08, 0x04, 0x0e, 0x5f, 0x0f, 0x06, 0xf4, 0x0e, 0x07, 0x0e, 0x5f, + 0x0e, 0x06, 0xf4, 0x08, 0x04, 0x0e, 0x5f, 0x0e, 0x06, 0xf4, 0x08, 0x9c, + 0x40, 0x02, 0xc9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x61, 0x75, 0x67, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x34, + 0x00, 0x75, 0x00, 0x11, 0x0a, 0x03, 0x17, 0x00, 0x31, 0x08, 0x8f, 0x12, + 0x06, 0x68, 0x00, 0x03, 0x28, 0x50, 0x10, 0x09, 0x67, 0x00, 0x01, 0x00, + 0x4b, 0x91, 0x10, 0x08, 0x0e, 0x9c, 0x40, 0x00, 0x49, 0x53, 0x63, 0x72, + 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x1c, 0x12, 0x00, 0x1f, + 0x4c, 0x00, 0x00, 0x06, 0x00, 0x08, 0x20, 0x4c, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x08, 0x4c, 0x86, 0x10, 0x27, 0x00, 0x08, 0x08, 0x4c, 0x86, 0x10, + 0x27, 0x00, 0x05, 0xa0, 0x01, 0x75, 0x50, 0x75, 0x6e, 0x63, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x29, 0x3c, 0x00, 0x04, 0x03, 0x1f, 0x0d, 0x00, + 0xf7, 0x00, 0x30, 0x00, 0x54, 0x17, 0x00, 0xf9, 0x00, 0x01, 0x08, 0x5f, + 0x0e, 0x0d, 0x27, 0x00, 0x71, 0x08, 0x1f, 0x12, 0x00, 0xf9, 0x00, 0x03, + 0x55, 0x01, 0xbe, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xe8, 0x2c, 0x05, 0x00, 0x00, 0x00, 0x9f, 0x0c, 0x00, 0xf6, 0x08, 0x00, + 0x0e, 0x99, 0x0e, 0x0f, 0xf6, 0x08, 0x00, 0x08, 0x99, 0x13, 0x1e, 0xf6, + 0x00, 0x00, 0x08, 0x99, 0x10, 0x1f, 0xf6, 0x00, 0x01, 0xc5, 0x03, 0xf5, + 0x46, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x65, 0x70, 0x73, 0x20, 0x31, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x34, + 0x00, 0x04, 0x22, 0x1f, 0x15, 0x00, 0xc8, 0x00, 0x01, 0x09, 0x1f, 0x18, + 0x1f, 0xf8, 0x00, 0x01, 0x04, 0xdf, 0x10, 0x11, 0x18, 0x00, 0x02, 0x04, + 0xdf, 0x0a, 0x14, 0x18, 0x00, 0x00, 0x6a, 0x00, 0x5d, 0x41, 0x70, 0x70, + 0x6c, 0x61, 0x75, 0x73, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf4, 0x46, 0x38, 0x10, 0x39, 0x00, + 0x1f, 0x96, 0x00, 0xf0, 0x08, 0x01, 0x06, 0x16, 0x80, 0x00, 0xf1, 0x09, + 0x04, 0x10, 0x1e, 0x96, 0x00, 0xf1, 0x0c, 0x05, 0x0d, 0x03, 0x0f, 0x00, + 0x09, 0x00, 0x9c, 0x40, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x20, 0x47, 0x75, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x09, + 0x00, 0x04, 0x17, 0x1f, 0x01, 0x00, 0xfe, 0x0b, 0x03, 0x06, 0x1f, 0x17, + 0x00, 0xf0, 0x08, 0x01, 0x1c, 0x1f, 0x12, 0x00, 0xfc, 0x00, 0x02, 0x02, + 0x1f, 0x11, 0x18, 0xff, 0x08, 0x9c, 0x40, 0x00, 0x56, 0x4c, 0x61, 0x73, + 0x65, 0x72, 0x20, 0x47, 0x75, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x00, 0x0f, 0x07, + 0x10, 0x0d, 0x00, 0xf8, 0x00, 0x0c, 0x0a, 0x1f, 0x19, 0x00, 0xf8, 0x00, + 0x0b, 0x08, 0x10, 0x0e, 0x0f, 0x28, 0x00, 0x0c, 0x08, 0x1f, 0x0d, 0x0e, + 0x48, 0x00, 0x02, 0xea, 0x01, 0x04, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x73, + 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x3c, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x04, 0x1c, 0x09, 0x11, 0xf3, 0x00, 0x01, 0x04, 0x1f, + 0x0b, 0x0b, 0x36, 0x00, 0x3b, 0x00, 0x1c, 0x0d, 0x0b, 0x27, 0x00, 0x08, + 0x3a, 0x03, 0xcd, 0x46, 0x69, 0x72, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x20, + 0x5b, 0x4e, 0x65, 0x65, 0x64, 0x65, 0x64, 0x20, 0x50, 0x43, 0x4d, 0x5d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x3c, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, 0x93, 0x00, 0x00, + 0x04, 0x1c, 0x09, 0x11, 0xf3, 0x00, 0x01, 0x04, 0x1f, 0x0b, 0x0b, 0x36, + 0x00, 0x3b, 0x00, 0x1c, 0x0d, 0x0b, 0x27, 0x00, 0x08, 0x3a, 0x03, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, + 0x00, 0x01, 0x01, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, 0xe6, 0x00, 0xba, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x3d, + 0x05, 0x33, 0x04, 0xdf, 0x06, 0x10, 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, + 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, 0x10, 0x1f, 0xf8, 0x00, 0x01, 0x00, + 0x9f, 0x10, 0x0e, 0x76, 0x00, 0x01, 0x11, 0x01, 0xc5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x35, 0x05, 0x33, 0x04, + 0xdf, 0x00, 0x10, 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, 0x0e, 0xf9, 0x00, + 0x01, 0x00, 0x9f, 0x11, 0x1f, 0xf8, 0x08, 0x01, 0x00, 0x9f, 0x10, 0x0d, + 0x76, 0x00, 0x9c, 0x40, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x37, 0x00, 0x08, 0x1f, 0x0a, 0x0a, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x8e, 0x12, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x18, 0x00, 0xf6, 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x3c, 0x00, 0x11, 0x00, 0x14, 0x00, 0x05, 0x00, 0x00, 0x02, + 0x00, 0x1f, 0x17, 0x18, 0x09, 0x00, 0x10, 0x00, 0x1d, 0x1f, 0x11, 0x08, + 0x00, 0x00, 0x00, 0x1f, 0x00, 0x11, 0x09, 0x00, 0x01, 0x1e, 0x00, 0xfd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x1f, + 0x00, 0x01, 0x27, 0x1f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x16, + 0x00, 0xff, 0x00, 0x01, 0x27, 0x9f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, + 0x12, 0x18, 0x00, 0xff, 0x00, 0x00, 0x71, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x18, 0x00, 0x02, 0x06, + 0x1f, 0x12, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x16, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, + 0xff, 0x00, 0x00, 0x85, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x18, 0x00, 0x02, 0x06, 0x1f, 0x12, 0x00, + 0xff, 0x00, 0x01, 0x01, 0x1f, 0x16, 0x00, 0xff, 0x00, 0x02, 0x17, 0x9f, + 0x08, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x0e, 0x00, 0xf7, 0x00, 0x02, + 0x01, 0x01, 0x61, 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, + 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x23, 0x22, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x14, 0x07, 0x00, 0x05, + 0x7f, 0x00, 0x0b, 0x0f, 0xf5, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0c, 0xf6, + 0x00, 0x01, 0x00, 0x5f, 0x00, 0x0e, 0x07, 0x00, 0x02, 0xae, 0x01, 0xe0, + 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, 0x6d, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x1a, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x14, 0x07, 0x00, 0x05, 0x7f, 0x00, 0x0b, + 0x0f, 0xf4, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0d, 0xf6, 0x00, 0x01, 0x00, + 0x5f, 0x00, 0x0c, 0x07, 0x00, 0x05, 0x2e, 0x01, 0xe6, 0x2a, 0x20, 0x53, + 0x74, 0x69, 0x63, 0x6b, 0x53, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x02, 0x15, + 0x1f, 0x13, 0x00, 0xff, 0x00, 0x01, 0x0e, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0xa0, 0x00, 0x56, 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x3c, 0x00, 0x11, 0x00, 0x14, 0x00, 0x05, + 0x00, 0x00, 0x02, 0x00, 0x1f, 0x17, 0x18, 0x09, 0x00, 0x10, 0x00, 0x1d, + 0x1f, 0x0f, 0x07, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x11, 0x09, 0x00, 0x01, + 0xed, 0x01, 0xb8, 0x2a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x43, 0x6c, 0x61, + 0x70, 0x20, 0x28, 0x4f, 0x50, 0x4c, 0x33, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3c, 0x00, 0x0f, 0x0d, 0x9f, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x03, 0x1f, 0x14, 0x10, 0x08, 0x00, 0x0f, 0x1b, 0x1f, 0x11, 0x0f, 0x27, + 0x00, 0x01, 0x00, 0x1f, 0x03, 0x12, 0x09, 0x00, 0x00, 0xf6, 0x00, 0xd5, + 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x3c, + 0x00, 0x10, 0x00, 0x14, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x1f, 0x17, + 0x18, 0x09, 0x00, 0x10, 0x00, 0x1d, 0x11, 0x0e, 0xa8, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x11, 0x09, 0x00, 0x01, 0x75, 0x01, 0x04, 0x2a, 0x20, 0x4c, + 0x6f, 0x77, 0x20, 0x46, 0x6c, 0x6f, 0x6f, 0x72, 0x20, 0x54, 0x6f, 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x3d, 0x00, 0x31, 0x17, + 0x1e, 0x14, 0x03, 0xf6, 0x00, 0x13, 0x04, 0x5e, 0x0c, 0x01, 0xf6, 0x00, + 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x05, 0xf6, 0x03, 0xee, 0x2a, 0x20, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x3a, 0x33, 0x3f, 0x19, 0x1f, 0x8e, 0x0d, + 0x84, 0x00, 0x33, 0x00, 0x5f, 0x0a, 0x08, 0x64, 0x00, 0x7f, 0x14, 0x1f, + 0x8e, 0x0d, 0x94, 0x00, 0x71, 0x00, 0xdf, 0x09, 0x09, 0x25, 0x00, 0x00, + 0xfd, 0x00, 0x92, 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x46, 0x6c, + 0x6f, 0x6f, 0x72, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x3d, 0x00, 0x31, 0x16, 0x1e, 0x14, 0x03, 0xf6, 0x00, 0x14, + 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x0e, 0xf6, + 0x00, 0x10, 0x03, 0x1e, 0x11, 0x09, 0xf6, 0x00, 0x06, 0x11, 0x03, 0xf5, + 0x2a, 0x20, 0x50, 0x65, 0x64, 0x61, 0x6c, 0x48, 0x69, 0x68, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x38, + 0x00, 0x3c, 0x00, 0x47, 0x0a, 0x1c, 0xb4, 0x00, 0x13, 0x00, 0xdf, 0x00, + 0x00, 0x04, 0x00, 0x7a, 0x00, 0xc3, 0x8a, 0x10, 0xb7, 0x00, 0x75, 0x07, + 0xc4, 0x0d, 0x1d, 0x59, 0x00, 0x00, 0x85, 0x00, 0x49, 0x2a, 0x20, 0x4c, + 0x6f, 0x77, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3d, 0x00, 0x31, 0x16, + 0x1e, 0x14, 0x03, 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, + 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x05, 0xf6, 0x04, 0x02, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, + 0x48, 0x69, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x2c, 0x00, 0x09, 0x00, 0x1f, 0x0e, 0x00, + 0x10, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x1f, + 0x17, 0x08, 0x44, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, + 0x5c, 0x09, 0xb6, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x2d, 0x6d, 0x65, 0x64, + 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1d, 0x3d, 0x00, 0x30, 0x16, 0x1e, 0x14, 0x03, 0xf6, 0x00, 0x14, + 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, + 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x06, 0x2c, 0x03, 0xfc, + 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, 0x2d, 0x6d, 0x65, 0x64, 0x20, 0x54, + 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, + 0x00, 0x30, 0x16, 0x1e, 0x14, 0x13, 0xf6, 0x00, 0x14, 0x02, 0x5e, 0x0c, + 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, + 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x05, 0x00, 0x03, 0xcd, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, + 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x10, 0x7f, 0x00, + 0x1f, 0x0b, 0x00, 0x40, 0x00, 0x71, 0x00, 0x1f, 0x16, 0x07, 0x34, 0x00, + 0x13, 0x00, 0x1f, 0x8b, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x9f, 0x1b, 0x07, + 0xa6, 0x00, 0x07, 0x36, 0x03, 0x84, 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, + 0x20, 0x54, 0x6f, 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x3d, 0x00, 0x31, 0x16, 0x1e, 0x14, 0x03, + 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, + 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x04, + 0xb0, 0x04, 0x10, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0d, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x18, 0x15, 0x07, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x19, 0x0b, 0x46, + 0x2a, 0x20, 0x43, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x20, 0x43, 0x72, + 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, + 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2c, + 0x00, 0x7f, 0x00, 0x1f, 0x07, 0x00, 0x52, 0x00, 0x71, 0x00, 0x1f, 0x15, + 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0c, 0x04, 0x84, 0x00, 0x07, 0x00, + 0x9f, 0x19, 0x06, 0xa3, 0x00, 0x24, 0xbe, 0x0d, 0xe1, 0x2a, 0x20, 0x52, + 0x69, 0x64, 0x65, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x1c, 0x00, 0x0d, 0x00, + 0x1f, 0x07, 0x07, 0xe1, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x11, 0x1f, 0x0a, 0x12, 0xe5, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x19, 0x05, 0xc1, 0x2a, 0x20, 0x54, 0x61, 0x6d, 0x62, + 0x6f, 0x72, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x35, 0x22, 0x06, 0x5f, 0x05, 0x02, + 0x81, 0x00, 0x75, 0x00, 0x96, 0x0f, 0x0c, 0xa1, 0x00, 0x04, 0x01, 0x9f, + 0x05, 0x02, 0x53, 0x00, 0x3e, 0x08, 0x92, 0x0d, 0x0d, 0xa6, 0x00, 0x01, + 0x82, 0x01, 0x54, 0x2a, 0x20, 0x53, 0x70, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x43, 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, + 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x2c, 0x00, 0x7f, 0x00, 0x1f, 0x05, 0x06, 0x50, 0x00, 0x71, + 0x00, 0x1f, 0x15, 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0b, 0x04, 0x85, + 0x00, 0x07, 0x00, 0x9f, 0x1a, 0x06, 0xa5, 0x00, 0x21, 0x62, 0x06, 0x0a, + 0x2a, 0x20, 0x43, 0x6f, 0x77, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3b, + 0x00, 0x2f, 0x00, 0x1f, 0x15, 0x13, 0x26, 0x00, 0x37, 0x20, 0x1f, 0x15, + 0x0d, 0x36, 0x00, 0x28, 0x23, 0x1f, 0x95, 0x0c, 0x26, 0x00, 0x32, 0x00, + 0x1f, 0x13, 0x10, 0x28, 0x00, 0x01, 0x61, 0x01, 0x04, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x32, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x2c, 0x00, 0x7f, 0x00, + 0x1f, 0x00, 0x00, 0x50, 0x00, 0x71, 0x00, 0x1f, 0x14, 0x06, 0x33, 0x00, + 0x13, 0x00, 0x1f, 0x0a, 0x04, 0x84, 0x00, 0x07, 0x00, 0x9f, 0x19, 0x06, + 0xa4, 0x00, 0x20, 0x4a, 0x0b, 0xe0, 0x2a, 0x20, 0x56, 0x69, 0x62, 0x65, + 0x53, 0x6c, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x34, 0x00, 0x00, 0x08, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x0a, 0x00, 0xf5, 0x00, 0x00, 0x15, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x0c, + 0xc9, 0x07, 0x58, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0c, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x18, 0x14, 0x08, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xd9, 0x0b, 0x32, + 0x2a, 0x20, 0x48, 0x69, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, + 0x12, 0x12, 0x0a, 0xf9, 0x00, 0x00, 0xf0, 0x00, 0xa6, 0x2a, 0x20, 0x4c, + 0x6f, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x36, 0x00, 0x01, 0x05, + 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, 0x0a, 0xf9, 0x00, + 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, 0x12, 0x12, 0x0a, + 0xf9, 0x00, 0x00, 0xe9, 0x00, 0xa6, 0x2a, 0x20, 0x4d, 0x75, 0x48, 0x69, + 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x36, 0x00, 0x01, 0x07, 0x9f, 0x0f, 0x00, + 0xf9, 0x00, 0x72, 0x00, 0x1e, 0x12, 0x0a, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x12, 0x00, 0xf9, 0x00, 0x12, 0x00, 0x12, 0x16, 0x0a, 0xfa, 0x00, 0x00, + 0xf6, 0x00, 0x92, 0x2a, 0x20, 0x4f, 0x70, 0x48, 0x69, 0x43, 0x6f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x71, + 0x00, 0x17, 0x14, 0x0c, 0xfb, 0x00, 0x00, 0x00, 0x9c, 0x17, 0x00, 0xfa, + 0x00, 0x11, 0x00, 0x12, 0x10, 0x0c, 0xf8, 0x00, 0x01, 0x82, 0x00, 0xfd, + 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x0d, 0x17, 0x14, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x16, 0x00, 0xfa, 0x00, 0x31, 0x06, + 0x12, 0x0f, 0x0a, 0xf8, 0x00, 0x02, 0x1c, 0x01, 0x04, 0x2a, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x54, 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2a, 0x05, 0x13, 0x06, + 0x9f, 0x19, 0x08, 0xc6, 0x00, 0x00, 0x0d, 0x5f, 0x13, 0x05, 0xbc, 0x00, + 0x36, 0x05, 0x5f, 0x19, 0x06, 0xa6, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, + 0x86, 0x00, 0x01, 0x32, 0x02, 0x94, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x54, + 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x2a, 0x05, 0x13, 0x06, 0x9f, 0x19, 0x08, + 0xca, 0x00, 0x00, 0x0a, 0x5f, 0x13, 0x05, 0xbc, 0x00, 0x36, 0x05, 0x5f, + 0x19, 0x06, 0xac, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, 0x8c, 0x00, 0x01, + 0x32, 0x00, 0x5d, 0x2a, 0x20, 0x48, 0x69, 0x41, 0x67, 0x6f, 0x67, 0x6f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x3c, 0x00, 0x3e, 0x17, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, + 0x18, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x34, 0x00, 0x54, 0x10, 0x11, 0xa8, + 0x00, 0x04, 0x00, 0x54, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x18, 0x00, 0xce, + 0x2a, 0x20, 0x4c, 0x6f, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x00, 0x3e, 0x1a, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, 0x18, 0x9f, 0x09, + 0x09, 0x86, 0x00, 0x34, 0x00, 0x57, 0x10, 0x11, 0xa8, 0x00, 0x04, 0x00, + 0x15, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x61, 0x00, 0xfd, 0x2a, 0x20, 0x43, + 0x61, 0x62, 0x61, 0x73, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x31, 0x35, 0x2f, 0x06, + 0x5f, 0x05, 0x02, 0x81, 0x00, 0x7d, 0x02, 0x99, 0x05, 0x02, 0x71, 0x00, + 0x0f, 0x01, 0x9f, 0x05, 0x02, 0x53, 0x00, 0x33, 0x00, 0x8d, 0x11, 0x0b, + 0xa9, 0x00, 0x01, 0x6e, 0x00, 0x5d, 0x2a, 0x20, 0x4d, 0x61, 0x72, 0x61, + 0x63, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x00, 0x8e, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x1f, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0x92, 0x00, 0x5d, 0x2a, 0x20, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x57, 0x68, + 0x69, 0x73, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x37, 0x04, 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x0b, 0x16, 0x2b, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0x89, 0x00, 0x64, + 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x04, + 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x08, 0x16, 0x2b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03, 0x55, 0x00, 0x64, 0x2a, 0x20, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x0c, 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x58, 0x01, 0x04, 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, + 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, 0x1f, 0x09, 0x09, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x09, + 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xd4, 0x00, 0xfd, 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x45, 0x3c, 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, + 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, + 0x00, 0x19, 0x05, 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xad, 0x00, 0xf0, + 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, + 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, + 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, + 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xc1, 0x01, 0x0a, 0x2a, 0x20, 0x57, + 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x3c, 0x00, 0x17, 0x14, + 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, + 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, 0xdf, 0x14, 0x0d, + 0x18, 0x00, 0x00, 0xc8, 0x01, 0x04, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x20, 0x43, 0x75, 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3c, 0x00, 0x72, 0x27, 0x0c, 0x0f, 0x0f, + 0x07, 0x00, 0x01, 0x1b, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x78, 0x00, 0x4e, + 0x06, 0x11, 0x06, 0x00, 0x08, 0x00, 0x8e, 0x06, 0x11, 0x06, 0x00, 0x01, + 0x4d, 0x02, 0xc9, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x43, 0x75, + 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x3c, 0x00, 0x01, 0x26, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x01, + 0x1e, 0x0c, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x0e, 0x06, 0x11, 0x06, + 0x00, 0x01, 0x17, 0x0e, 0x0f, 0x0e, 0xb6, 0x00, 0x01, 0xed, 0x03, 0xac, + 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, + 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, + 0x00, 0x03, 0x1a, 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x0e, 0x0d, 0x8b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3d, 0x00, 0x5d, 0x2a, 0x20, 0x4f, + 0x70, 0x65, 0x6e, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, 0x00, 0x03, 0x1a, + 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x1f, 0x08, 0x08, 0x85, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x3e, 0x04, 0xe5, 0x2a, 0x20, 0x53, 0x68, 0x61, 0x6b, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x0e, 0x8c, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0xc8, 0x00, 0x78, 0x2a, 0x20, 0x4a, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, + 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x3c, 0x00, 0x05, 0x18, 0x1f, 0x04, 0x0f, 0xf4, 0x00, 0x05, + 0x1c, 0x1f, 0x09, 0x10, 0xf0, 0x00, 0x04, 0x09, 0x51, 0x0b, 0x07, 0xa6, + 0x00, 0x04, 0x0e, 0x51, 0x0b, 0x07, 0xa5, 0x00, 0x08, 0x76, 0x03, 0x84, + 0x2a, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x54, 0x72, 0x65, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x2c, + 0x00, 0x05, 0x09, 0x14, 0x0d, 0x00, 0x10, 0x00, 0x05, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x14, 0x15, 0x08, 0x44, 0x00, 0x08, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x19, 0x0c, 0x36, 0x2a, 0x20, 0x43, + 0x61, 0x73, 0x74, 0x61, 0x6e, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, + 0x1f, 0x0b, 0x00, 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x04, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0x64, 0x00, 0x49, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x53, 0x75, 0x72, 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, + 0xff, 0x00, 0x71, 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, + 0x14, 0x00, 0xff, 0x00, 0x31, 0x00, 0x0e, 0x0d, 0x0a, 0xf6, 0x00, 0x04, + 0x10, 0x03, 0x76, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x75, 0x72, + 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x29, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xff, 0x00, 0x71, + 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, 0x14, 0x00, 0xff, + 0x00, 0x31, 0x00, 0x0e, 0x0b, 0x08, 0xf4, 0x00, 0x08, 0xc6, 0x0f, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, + 0x00, 0x01, 0x01, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, 0xe6, 0x00, 0xba, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x10, 0x7f, 0x00, 0x1d, 0x01, 0x00, 0x80, 0x00, 0x7f, 0x00, 0x1f, 0x00, + 0x00, 0x14, 0x00, 0x13, 0x09, 0x44, 0x8d, 0x16, 0x09, 0x00, 0x00, 0x1d, + 0x84, 0x1b, 0x06, 0xa6, 0x00, 0x09, 0xb6, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, 0x37, 0x00, 0x08, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x8e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd9, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x25, 0x0a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, + 0x0c, 0x00, 0x05, 0x7f, 0x00, 0x0b, 0x0f, 0xf4, 0x00, 0x06, 0x7f, 0x00, + 0x0f, 0x0d, 0xf6, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, + 0x75, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x36, 0x18, 0x00, 0x02, 0x0a, 0x1f, 0x14, 0x00, 0xff, 0x00, 0x02, + 0x17, 0x1f, 0x12, 0x00, 0xff, 0x00, 0x02, 0x2a, 0x9f, 0x14, 0x00, 0xff, + 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x1f, + 0x00, 0x01, 0x27, 0x1f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x16, + 0x00, 0xff, 0x00, 0x01, 0x27, 0x9f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, + 0x12, 0x18, 0x00, 0xff, 0x00, 0x00, 0x71, 0x00, 0x56, 0x2a, 0x20, 0x42, + 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x22, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x14, 0x07, 0x00, 0x05, 0x7f, 0x00, 0x0b, 0x0f, 0xf5, 0x00, + 0x06, 0x7f, 0x00, 0x0f, 0x0c, 0xf6, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x0e, + 0x07, 0x00, 0x02, 0xae, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x18, 0x00, 0x02, 0x06, 0x1f, 0x12, 0x00, + 0xff, 0x00, 0x01, 0x01, 0x1f, 0x16, 0x00, 0xff, 0x00, 0x02, 0x17, 0x9f, + 0x08, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x0e, 0x00, 0xf7, 0x00, 0x02, + 0x01, 0x01, 0x61, 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, + 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x23, 0x22, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x12, 0x07, 0x00, 0x05, + 0x7f, 0x00, 0x0b, 0x0f, 0xf5, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0c, 0xf6, + 0x00, 0x01, 0x00, 0x5f, 0x00, 0x0e, 0x07, 0x00, 0x02, 0xb5, 0x01, 0xe6, + 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, 0x6d, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x12, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x13, 0x07, 0x00, 0x05, 0x7f, 0x00, 0x0b, + 0x0f, 0xf4, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0d, 0xf6, 0x00, 0x01, 0x00, + 0x5f, 0x00, 0x0c, 0x07, 0x00, 0x05, 0x21, 0x01, 0xe0, 0x2a, 0x20, 0x53, + 0x74, 0x69, 0x63, 0x6b, 0x53, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x02, 0x15, + 0x1f, 0x13, 0x00, 0xff, 0x00, 0x01, 0x0e, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0xa0, 0x00, 0x56, 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x3c, 0x00, 0x11, 0x00, 0x14, 0x00, 0x05, + 0x00, 0x00, 0x02, 0x00, 0x1f, 0x17, 0x18, 0x09, 0x00, 0x10, 0x00, 0x1d, + 0x1f, 0x0f, 0x07, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x11, 0x09, 0x00, 0x01, + 0xed, 0x01, 0xb8, 0x2a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x43, 0x6c, 0x61, + 0x70, 0x20, 0x28, 0x4f, 0x50, 0x4c, 0x33, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3c, 0x00, 0x0f, 0x0d, 0x9f, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x03, 0x1f, 0x14, 0x10, 0x08, 0x00, 0x0f, 0x1b, 0x1f, 0x11, 0x0f, 0x27, + 0x00, 0x01, 0x00, 0x1f, 0x03, 0x12, 0x09, 0x00, 0x00, 0xf6, 0x00, 0xd5, + 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x3c, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x08, 0x01, 0x00, 0x02, 0x00, 0x1f, 0x17, + 0x16, 0x69, 0x00, 0x12, 0x00, 0x1f, 0x0b, 0x12, 0x08, 0x00, 0x01, 0x00, + 0x1f, 0x0e, 0x12, 0x58, 0x00, 0x01, 0xd2, 0x01, 0x25, 0x54, 0x6f, 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3b, 0x00, 0x7f, 0x00, + 0x5f, 0x00, 0x00, 0x06, 0x00, 0x74, 0x15, 0x5f, 0x18, 0x00, 0xff, 0x00, + 0x78, 0x28, 0x5f, 0x17, 0x0c, 0x56, 0x00, 0x74, 0x00, 0x5f, 0x0b, 0x00, + 0xf7, 0x00, 0x08, 0x8a, 0x02, 0x29, 0x2a, 0x20, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x3a, 0x33, 0x3f, 0x19, 0x1f, 0x8e, 0x0d, + 0x84, 0x00, 0x33, 0x00, 0x5f, 0x0a, 0x08, 0x64, 0x00, 0x7f, 0x14, 0x1f, + 0x8e, 0x0d, 0x94, 0x00, 0x71, 0x00, 0xdf, 0x09, 0x09, 0x25, 0x00, 0x00, + 0xfd, 0x00, 0x92, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x3b, 0x00, 0x7f, 0x00, 0x5f, 0x00, 0x00, 0x06, 0x00, 0x74, + 0x15, 0x5f, 0x18, 0x00, 0xff, 0x00, 0x78, 0x28, 0x5f, 0x17, 0x0c, 0x56, + 0x00, 0x74, 0x00, 0x5f, 0x0b, 0x00, 0xf7, 0x00, 0x08, 0x8a, 0x02, 0x29, + 0x2a, 0x20, 0x50, 0x65, 0x64, 0x61, 0x6c, 0x48, 0x69, 0x68, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x38, + 0x00, 0x3c, 0x00, 0x47, 0x0a, 0x1c, 0xb4, 0x00, 0x13, 0x00, 0xdf, 0x00, + 0x00, 0x04, 0x00, 0x7a, 0x00, 0xc3, 0x8a, 0x10, 0xb7, 0x00, 0x75, 0x07, + 0xc4, 0x0d, 0x1d, 0x59, 0x00, 0x00, 0x85, 0x00, 0x49, 0x54, 0x6f, 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x3b, 0x00, 0x7f, 0x00, + 0x5f, 0x00, 0x00, 0x06, 0x00, 0x74, 0x15, 0x5f, 0x18, 0x00, 0xff, 0x00, + 0x78, 0x28, 0x5f, 0x17, 0x0c, 0x56, 0x00, 0x74, 0x00, 0x5f, 0x0b, 0x00, + 0xf7, 0x00, 0x08, 0x91, 0x02, 0x29, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, + 0x48, 0x69, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x2c, 0x00, 0x09, 0x00, 0x1f, 0x0e, 0x00, + 0x10, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x1f, + 0x17, 0x08, 0x44, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, + 0x5c, 0x09, 0xb6, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x3b, 0x00, 0x7f, 0x00, 0x5f, 0x00, 0x00, 0x06, 0x00, 0x74, + 0x15, 0x5f, 0x18, 0x00, 0xff, 0x00, 0x78, 0x28, 0x5f, 0x17, 0x0c, 0x56, + 0x00, 0x74, 0x00, 0x5f, 0x0b, 0x00, 0xf7, 0x00, 0x08, 0x8a, 0x02, 0x29, + 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x3b, + 0x00, 0x7f, 0x00, 0x5f, 0x00, 0x00, 0x06, 0x00, 0x74, 0x15, 0x5f, 0x18, + 0x00, 0xff, 0x00, 0x78, 0x28, 0x5f, 0x17, 0x0c, 0x56, 0x00, 0x74, 0x00, + 0x5f, 0x0b, 0x00, 0xf7, 0x00, 0x07, 0x58, 0x01, 0xe0, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, + 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x10, 0x7f, 0x00, + 0x1f, 0x0b, 0x00, 0x40, 0x00, 0x71, 0x00, 0x1f, 0x16, 0x07, 0x34, 0x00, + 0x13, 0x00, 0x1f, 0x8b, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x9f, 0x1b, 0x07, + 0xa6, 0x00, 0x07, 0x36, 0x03, 0x84, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x25, 0x3b, 0x00, 0x7f, 0x00, 0x5f, 0x00, 0x00, + 0x06, 0x00, 0x74, 0x15, 0x5f, 0x18, 0x00, 0xff, 0x00, 0x78, 0x28, 0x5f, + 0x17, 0x0c, 0x56, 0x00, 0x74, 0x00, 0x5f, 0x0b, 0x00, 0xf7, 0x00, 0x07, + 0x58, 0x01, 0xe0, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0d, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x18, 0x15, 0x07, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x19, 0x0b, 0x46, + 0x2a, 0x20, 0x43, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x20, 0x43, 0x72, + 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, + 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2c, + 0x00, 0x7f, 0x00, 0x1f, 0x07, 0x00, 0x52, 0x00, 0x71, 0x00, 0x1f, 0x15, + 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0c, 0x04, 0x84, 0x00, 0x07, 0x00, + 0x9f, 0x19, 0x06, 0xa3, 0x00, 0x24, 0xbe, 0x0d, 0xe1, 0x2a, 0x20, 0x52, + 0x69, 0x64, 0x65, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x1c, 0x00, 0x0d, 0x00, + 0x1f, 0x07, 0x07, 0xe1, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x11, 0x1f, 0x0a, 0x12, 0xe5, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x19, 0x05, 0xc1, 0x2a, 0x20, 0x54, 0x61, 0x6d, 0x62, + 0x6f, 0x72, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x35, 0x22, 0x06, 0x5f, 0x05, 0x02, + 0x81, 0x00, 0x75, 0x00, 0x96, 0x0f, 0x0c, 0xa1, 0x00, 0x04, 0x01, 0x9f, + 0x05, 0x02, 0x53, 0x00, 0x3e, 0x08, 0x92, 0x0d, 0x0d, 0xa6, 0x00, 0x01, + 0x82, 0x01, 0x54, 0x2a, 0x20, 0x53, 0x70, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x43, 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, + 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x2c, 0x00, 0x7f, 0x00, 0x1f, 0x05, 0x06, 0x50, 0x00, 0x71, + 0x00, 0x1f, 0x15, 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0b, 0x04, 0x85, + 0x00, 0x07, 0x00, 0x9f, 0x1a, 0x06, 0xa5, 0x00, 0x21, 0x62, 0x06, 0x0a, + 0x2a, 0x20, 0x43, 0x6f, 0x77, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3b, + 0x00, 0x2f, 0x00, 0x1f, 0x15, 0x13, 0x26, 0x00, 0x37, 0x20, 0x1f, 0x15, + 0x0d, 0x36, 0x00, 0x28, 0x23, 0x1f, 0x95, 0x0c, 0x26, 0x00, 0x32, 0x00, + 0x1f, 0x13, 0x10, 0x28, 0x00, 0x01, 0x61, 0x01, 0x04, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x32, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x2c, 0x00, 0x7f, 0x00, + 0x1f, 0x00, 0x00, 0x50, 0x00, 0x71, 0x00, 0x1f, 0x14, 0x06, 0x33, 0x00, + 0x13, 0x00, 0x1f, 0x0a, 0x04, 0x84, 0x00, 0x07, 0x00, 0x9f, 0x19, 0x06, + 0xa4, 0x00, 0x20, 0x4a, 0x0b, 0xe0, 0x2a, 0x20, 0x56, 0x69, 0x62, 0x65, + 0x53, 0x6c, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x34, 0x00, 0x00, 0x08, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x0a, 0x00, 0xf5, 0x00, 0x00, 0x15, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x0c, + 0xc9, 0x07, 0x58, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0c, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x18, 0x14, 0x08, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xd9, 0x0b, 0x32, + 0x2a, 0x20, 0x48, 0x69, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, + 0x12, 0x12, 0x0a, 0xf9, 0x00, 0x00, 0xf0, 0x00, 0xa6, 0x2a, 0x20, 0x4c, + 0x6f, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x36, 0x00, 0x01, 0x05, + 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, 0x0a, 0xf9, 0x00, + 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, 0x12, 0x12, 0x0a, + 0xf9, 0x00, 0x00, 0xe9, 0x00, 0xa6, 0x2a, 0x20, 0x4d, 0x75, 0x48, 0x69, + 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x36, 0x00, 0x01, 0x07, 0x9f, 0x0f, 0x00, + 0xf9, 0x00, 0x72, 0x00, 0x1e, 0x12, 0x0a, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x12, 0x00, 0xf9, 0x00, 0x12, 0x00, 0x12, 0x16, 0x0a, 0xfa, 0x00, 0x00, + 0xf6, 0x00, 0x92, 0x2a, 0x20, 0x4f, 0x70, 0x48, 0x69, 0x43, 0x6f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x71, + 0x00, 0x17, 0x14, 0x0c, 0xfb, 0x00, 0x00, 0x00, 0x9c, 0x17, 0x00, 0xfa, + 0x00, 0x11, 0x00, 0x12, 0x10, 0x0c, 0xf8, 0x00, 0x01, 0x82, 0x00, 0xfd, + 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x0d, 0x17, 0x14, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x16, 0x00, 0xfa, 0x00, 0x31, 0x06, + 0x12, 0x0f, 0x0a, 0xf8, 0x00, 0x02, 0x1c, 0x01, 0x04, 0x2a, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x54, 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2a, 0x05, 0x13, 0x06, + 0x9f, 0x19, 0x08, 0xc6, 0x00, 0x00, 0x0d, 0x5f, 0x13, 0x05, 0xbc, 0x00, + 0x36, 0x05, 0x5f, 0x19, 0x06, 0xa6, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, + 0x86, 0x00, 0x01, 0x32, 0x02, 0x94, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x54, + 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x2a, 0x05, 0x13, 0x06, 0x9f, 0x19, 0x08, + 0xca, 0x00, 0x00, 0x0a, 0x5f, 0x13, 0x05, 0xbc, 0x00, 0x36, 0x05, 0x5f, + 0x19, 0x06, 0xac, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, 0x8c, 0x00, 0x01, + 0x32, 0x00, 0x5d, 0x2a, 0x20, 0x48, 0x69, 0x41, 0x67, 0x6f, 0x67, 0x6f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x3c, 0x00, 0x3e, 0x17, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, + 0x18, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x34, 0x00, 0x54, 0x10, 0x11, 0xa8, + 0x00, 0x04, 0x00, 0x54, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x18, 0x00, 0xce, + 0x2a, 0x20, 0x4c, 0x6f, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x00, 0x3e, 0x1a, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, 0x18, 0x9f, 0x09, + 0x09, 0x86, 0x00, 0x34, 0x00, 0x57, 0x10, 0x11, 0xa8, 0x00, 0x04, 0x00, + 0x15, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x61, 0x00, 0xfd, 0x2a, 0x20, 0x43, + 0x61, 0x62, 0x61, 0x73, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x31, 0x35, 0x2f, 0x06, + 0x5f, 0x05, 0x02, 0x81, 0x00, 0x7d, 0x02, 0x99, 0x05, 0x02, 0x71, 0x00, + 0x0f, 0x01, 0x9f, 0x05, 0x02, 0x53, 0x00, 0x33, 0x00, 0x8d, 0x11, 0x0b, + 0xa9, 0x00, 0x01, 0x6e, 0x00, 0x5d, 0x2a, 0x20, 0x4d, 0x61, 0x72, 0x61, + 0x63, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x00, 0x8e, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x1f, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0x92, 0x00, 0x5d, 0x2a, 0x20, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x57, 0x68, + 0x69, 0x73, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x37, 0x04, 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x0b, 0x16, 0x2b, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0x89, 0x00, 0x64, + 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x04, + 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x08, 0x16, 0x2b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03, 0x55, 0x00, 0x64, 0x2a, 0x20, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x0c, 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x58, 0x01, 0x04, 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, + 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, 0x1f, 0x09, 0x09, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x09, + 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xd4, 0x00, 0xfd, 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x45, 0x3c, 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, + 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, + 0x00, 0x19, 0x05, 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xad, 0x00, 0xf0, + 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, + 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, + 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, + 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xc1, 0x01, 0x0a, 0x2a, 0x20, 0x57, + 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x3c, 0x00, 0x17, 0x14, + 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, + 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, 0xdf, 0x14, 0x0d, + 0x18, 0x00, 0x00, 0xc8, 0x01, 0x04, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x20, 0x43, 0x75, 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3c, 0x00, 0x72, 0x27, 0x0c, 0x0f, 0x0f, + 0x07, 0x00, 0x01, 0x1b, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x78, 0x00, 0x4e, + 0x06, 0x11, 0x06, 0x00, 0x08, 0x00, 0x8e, 0x06, 0x11, 0x06, 0x00, 0x01, + 0x4d, 0x02, 0xc9, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x43, 0x75, + 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x3c, 0x00, 0x01, 0x26, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x01, + 0x1e, 0x0c, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x0e, 0x06, 0x11, 0x06, + 0x00, 0x01, 0x17, 0x0e, 0x0f, 0x0e, 0xb6, 0x00, 0x01, 0xed, 0x03, 0xac, + 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, + 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, + 0x00, 0x03, 0x1a, 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x0e, 0x0d, 0x8b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3d, 0x00, 0x5d, 0x2a, 0x20, 0x4f, + 0x70, 0x65, 0x6e, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, 0x00, 0x03, 0x1a, + 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x1f, 0x08, 0x08, 0x85, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x3e, 0x04, 0xe5, 0x2a, 0x20, 0x53, 0x68, 0x61, 0x6b, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x0e, 0x8c, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0xc8, 0x00, 0x78, 0x2a, 0x20, 0x4a, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, + 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x3c, 0x00, 0x05, 0x18, 0x1f, 0x04, 0x0f, 0xf4, 0x00, 0x05, + 0x1c, 0x1f, 0x09, 0x10, 0xf0, 0x00, 0x04, 0x09, 0x51, 0x0b, 0x07, 0xa6, + 0x00, 0x04, 0x0e, 0x51, 0x0b, 0x07, 0xa5, 0x00, 0x08, 0x76, 0x03, 0x84, + 0x2a, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x54, 0x72, 0x65, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x2c, + 0x00, 0x05, 0x09, 0x14, 0x0d, 0x00, 0x10, 0x00, 0x05, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x14, 0x15, 0x08, 0x44, 0x00, 0x08, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x19, 0x0c, 0x36, 0x2a, 0x20, 0x43, + 0x61, 0x73, 0x74, 0x61, 0x6e, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, + 0x1f, 0x0b, 0x00, 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x04, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0x64, 0x00, 0x49, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x53, 0x75, 0x72, 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, + 0xff, 0x00, 0x71, 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, + 0x14, 0x00, 0xff, 0x00, 0x31, 0x00, 0x0e, 0x0d, 0x0a, 0xf6, 0x00, 0x04, + 0x10, 0x03, 0x76, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x75, 0x72, + 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x29, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xff, 0x00, 0x71, + 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, 0x14, 0x00, 0xff, + 0x00, 0x31, 0x00, 0x0e, 0x0b, 0x08, 0xf4, 0x00, 0x08, 0xc6, 0x0f, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, + 0x00, 0x01, 0x01, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, 0xe6, 0x00, 0xba, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x3d, + 0x05, 0x33, 0x04, 0xdf, 0x06, 0x10, 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, + 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, 0x10, 0x1f, 0xf8, 0x00, 0x01, 0x00, + 0x9f, 0x10, 0x0e, 0x76, 0x00, 0x01, 0x11, 0x01, 0xc5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, 0x37, 0x00, 0x08, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x8e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd9, 0x00, 0xf0, 0x2a, 0x20, 0x43, 0x61, 0x73, 0x74, + 0x61, 0x6e, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, 0x1f, 0x0b, 0x00, + 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, 0x04, 0x0d, 0x9f, + 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x00, + 0x64, 0x00, 0x49, 0x2a, 0x20, 0x53, 0x74, 0x69, 0x63, 0x6b, 0x73, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x36, 0x18, 0x00, 0x02, 0x0a, 0x1f, 0x14, 0x00, 0xff, 0x00, 0x02, + 0x17, 0x1f, 0x12, 0x00, 0xff, 0x00, 0x02, 0x2a, 0x9f, 0x14, 0x00, 0xff, + 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x50, + 0x2a, 0x20, 0x53, 0x71, 0x75, 0x61, 0x72, 0x65, 0x20, 0x43, 0x6c, 0x69, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x1f, + 0x00, 0x01, 0x27, 0x1f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x16, + 0x00, 0xff, 0x00, 0x01, 0x27, 0x9f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, + 0x12, 0x18, 0x00, 0xff, 0x00, 0x00, 0x71, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x18, 0x00, 0x02, 0x06, + 0x1f, 0x12, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x16, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, + 0xff, 0x00, 0x00, 0x85, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2e, 0x3d, 0x05, 0x33, 0x04, 0xdf, 0x06, 0x10, + 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x10, 0x1f, 0xf8, 0x00, 0x01, 0x00, 0x9f, 0x10, 0x0f, 0x78, 0x00, 0x00, + 0xf6, 0x00, 0xad, 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, + 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x23, 0x22, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x14, 0x07, 0x00, 0x05, + 0x7f, 0x00, 0x0b, 0x0f, 0xf5, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0c, 0xf6, + 0x00, 0x01, 0x00, 0x5f, 0x00, 0x0e, 0x07, 0x00, 0x02, 0xae, 0x01, 0xe0, + 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, 0x6d, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x1a, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x19, 0x07, 0x00, 0x05, 0x7f, 0x00, 0x0b, + 0x0f, 0xf4, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0d, 0xf6, 0x00, 0x01, 0x00, + 0x5f, 0x00, 0x12, 0x07, 0x00, 0x00, 0xfd, 0x02, 0x22, 0x2a, 0x20, 0x53, + 0x74, 0x69, 0x63, 0x6b, 0x53, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x02, 0x15, + 0x1f, 0x13, 0x00, 0xff, 0x00, 0x01, 0x0e, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0xa0, 0x00, 0x56, 0x2a, 0x20, 0x41, 0x63, 0x75, 0x73, + 0x74, 0x69, 0x63, 0x53, 0x6e, 0x61, 0x72, 0x65, 0x20, 0x28, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x35, 0x32, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x27, 0x35, 0x05, 0x33, 0x04, 0xdf, 0x06, 0x10, + 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x10, 0x1f, 0xf8, 0x00, 0x01, 0x00, 0x9f, 0x10, 0x0d, 0x76, 0x00, 0x01, + 0xcc, 0x02, 0x72, 0x2a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x43, 0x6c, 0x61, + 0x70, 0x20, 0x28, 0x4f, 0x50, 0x4c, 0x33, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3c, 0x00, 0x0f, 0x0d, 0x9f, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x03, 0x1f, 0x14, 0x10, 0x08, 0x00, 0x0f, 0x1b, 0x1f, 0x11, 0x0f, 0x27, + 0x00, 0x01, 0x00, 0x1f, 0x03, 0x12, 0x09, 0x00, 0x00, 0xf6, 0x00, 0xd5, + 0x2a, 0x20, 0x41, 0x63, 0x75, 0x73, 0x74, 0x69, 0x63, 0x53, 0x6e, 0x61, + 0x72, 0x65, 0x20, 0x28, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x35, 0x32, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x35, + 0x05, 0x33, 0x04, 0xdf, 0x05, 0x0f, 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x14, + 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, 0x10, 0x1f, 0xf7, 0x00, 0x01, 0x00, + 0x9f, 0x10, 0x0c, 0x76, 0x00, 0x02, 0x4a, 0x02, 0x79, 0x2a, 0x20, 0x4c, + 0x6f, 0x77, 0x20, 0x46, 0x6c, 0x6f, 0x6f, 0x72, 0x20, 0x54, 0x6f, 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x3d, 0x00, 0x31, 0x15, + 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x13, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, + 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x06, 0x18, 0x04, 0x9c, 0x2a, 0x20, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x3a, 0x33, 0x3f, 0x19, 0x1f, 0x8e, 0x0d, + 0x84, 0x00, 0x33, 0x00, 0x5f, 0x0a, 0x08, 0x64, 0x00, 0x7f, 0x14, 0x1f, + 0x8e, 0x0d, 0x94, 0x00, 0x71, 0x00, 0xdf, 0x09, 0x09, 0x25, 0x00, 0x00, + 0xfd, 0x00, 0x92, 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x46, 0x6c, + 0x6f, 0x6f, 0x72, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x3d, 0x00, 0x31, 0x16, 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, + 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x0e, 0xf6, + 0x00, 0x10, 0x03, 0x1e, 0x11, 0x09, 0xf6, 0x00, 0x06, 0x1e, 0x04, 0xa2, + 0x2a, 0x20, 0x50, 0x65, 0x64, 0x61, 0x6c, 0x48, 0x69, 0x68, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x38, + 0x00, 0x3c, 0x00, 0x47, 0x0a, 0x1c, 0xb4, 0x00, 0x13, 0x00, 0xdf, 0x00, + 0x00, 0x04, 0x00, 0x7a, 0x00, 0xc3, 0x8a, 0x10, 0xb7, 0x00, 0x75, 0x07, + 0xc4, 0x0d, 0x1d, 0x59, 0x00, 0x00, 0x85, 0x00, 0x49, 0x2a, 0x20, 0x4c, + 0x6f, 0x77, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3d, 0x00, 0x31, 0x16, + 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, + 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x06, 0x18, 0x04, 0x16, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, + 0x48, 0x69, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x2c, 0x00, 0x09, 0x00, 0x1f, 0x0e, 0x00, + 0x10, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x1f, + 0x17, 0x08, 0x44, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, + 0x5c, 0x09, 0xb6, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x2d, 0x6d, 0x65, 0x64, + 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1d, 0x3d, 0x00, 0x30, 0x16, 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, + 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, + 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x06, 0x0a, 0x04, 0x38, + 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, 0x2d, 0x6d, 0x65, 0x64, 0x20, 0x54, + 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, + 0x00, 0x30, 0x16, 0x1e, 0x18, 0x13, 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, + 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, + 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x04, 0xd8, 0x04, 0x7a, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, + 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x10, 0x7f, 0x00, + 0x1f, 0x0b, 0x00, 0x40, 0x00, 0x71, 0x00, 0x1f, 0x16, 0x07, 0x34, 0x00, + 0x13, 0x00, 0x1f, 0x8b, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x9f, 0x1b, 0x07, + 0xa6, 0x00, 0x07, 0x36, 0x03, 0x84, 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, + 0x20, 0x54, 0x6f, 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x3d, 0x00, 0x31, 0x16, 0x1e, 0x1a, 0x03, + 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, + 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x04, + 0xd1, 0x04, 0x2a, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0d, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x18, 0x15, 0x07, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x8d, 0x0b, 0x18, + 0x2a, 0x20, 0x43, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x20, 0x43, 0x72, + 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, + 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2c, + 0x00, 0x7f, 0x00, 0x1f, 0x07, 0x00, 0x52, 0x00, 0x71, 0x00, 0x1f, 0x15, + 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0c, 0x04, 0x84, 0x00, 0x07, 0x00, + 0x9f, 0x19, 0x06, 0xa3, 0x00, 0x24, 0xbe, 0x0d, 0xe1, 0x2a, 0x20, 0x52, + 0x69, 0x64, 0x65, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x1c, 0x00, 0x0d, 0x00, + 0x1f, 0x07, 0x07, 0xe1, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x11, 0x1f, 0x0a, 0x12, 0xe5, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x19, 0x05, 0xc1, 0x2a, 0x20, 0x54, 0x61, 0x6d, 0x62, + 0x6f, 0x72, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x35, 0x22, 0x06, 0x5f, 0x05, 0x02, + 0x81, 0x00, 0x75, 0x00, 0x96, 0x0f, 0x0c, 0xa1, 0x00, 0x04, 0x01, 0x9f, + 0x05, 0x02, 0x53, 0x00, 0x3e, 0x08, 0x92, 0x0d, 0x0d, 0xa6, 0x00, 0x01, + 0x82, 0x01, 0x54, 0x2a, 0x20, 0x53, 0x70, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x43, 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, + 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x2c, 0x00, 0x7f, 0x00, 0x1f, 0x05, 0x06, 0x50, 0x00, 0x71, + 0x00, 0x1f, 0x15, 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0b, 0x04, 0x85, + 0x00, 0x07, 0x00, 0x9f, 0x1a, 0x06, 0xa5, 0x00, 0x21, 0x62, 0x06, 0x0a, + 0x2a, 0x20, 0x43, 0x6f, 0x77, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3b, + 0x00, 0x2f, 0x00, 0x1f, 0x15, 0x13, 0x26, 0x00, 0x37, 0x20, 0x1f, 0x15, + 0x0d, 0x36, 0x00, 0x28, 0x23, 0x1f, 0x95, 0x0c, 0x26, 0x00, 0x32, 0x00, + 0x1f, 0x13, 0x10, 0x28, 0x00, 0x01, 0x61, 0x01, 0x04, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x32, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x2c, 0x00, 0x7f, 0x00, + 0x1f, 0x00, 0x00, 0x50, 0x00, 0x71, 0x00, 0x1f, 0x14, 0x06, 0x33, 0x00, + 0x13, 0x00, 0x1f, 0x0a, 0x04, 0x84, 0x00, 0x07, 0x00, 0x9f, 0x19, 0x06, + 0xa4, 0x00, 0x20, 0x4a, 0x0b, 0xe0, 0x2a, 0x20, 0x56, 0x69, 0x62, 0x65, + 0x53, 0x6c, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x34, 0x00, 0x00, 0x08, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x0a, 0x00, 0xf5, 0x00, 0x00, 0x15, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x0c, + 0xc9, 0x07, 0x58, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0c, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x18, 0x14, 0x08, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xd9, 0x0b, 0x32, + 0x2a, 0x20, 0x48, 0x69, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, + 0x12, 0x12, 0x0a, 0xf9, 0x00, 0x00, 0xf0, 0x00, 0xa6, 0x2a, 0x20, 0x4c, + 0x6f, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x36, 0x00, 0x01, 0x05, + 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, 0x0a, 0xf9, 0x00, + 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, 0x12, 0x12, 0x0a, + 0xf9, 0x00, 0x00, 0xe9, 0x00, 0xa6, 0x2a, 0x20, 0x4d, 0x75, 0x48, 0x69, + 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x36, 0x00, 0x01, 0x07, 0x9f, 0x0f, 0x00, + 0xf9, 0x00, 0x72, 0x00, 0x1e, 0x12, 0x0a, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x12, 0x00, 0xf9, 0x00, 0x12, 0x00, 0x12, 0x16, 0x0a, 0xfa, 0x00, 0x00, + 0xf6, 0x00, 0x92, 0x2a, 0x20, 0x4f, 0x70, 0x48, 0x69, 0x43, 0x6f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x71, + 0x00, 0x17, 0x14, 0x0c, 0xfb, 0x00, 0x00, 0x00, 0x9c, 0x17, 0x00, 0xfa, + 0x00, 0x11, 0x00, 0x12, 0x10, 0x0c, 0xf8, 0x00, 0x01, 0x82, 0x00, 0xfd, + 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x0d, 0x17, 0x14, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x16, 0x00, 0xfa, 0x00, 0x31, 0x06, + 0x12, 0x0f, 0x0a, 0xf8, 0x00, 0x02, 0x1c, 0x01, 0x04, 0x2a, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x54, 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2a, 0x05, 0x13, 0x06, + 0x9f, 0x19, 0x08, 0xc6, 0x00, 0x00, 0x0d, 0x5f, 0x13, 0x05, 0xbc, 0x00, + 0x36, 0x05, 0x5f, 0x19, 0x06, 0xa6, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, + 0x86, 0x00, 0x01, 0x32, 0x02, 0x94, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x54, + 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x2a, 0x05, 0x13, 0x06, 0x9f, 0x19, 0x08, + 0xca, 0x00, 0x00, 0x0a, 0x5f, 0x13, 0x05, 0xbc, 0x00, 0x36, 0x05, 0x5f, + 0x19, 0x06, 0xac, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, 0x8c, 0x00, 0x01, + 0x32, 0x00, 0x5d, 0x2a, 0x20, 0x48, 0x69, 0x41, 0x67, 0x6f, 0x67, 0x6f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x3c, 0x00, 0x3e, 0x17, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, + 0x18, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x34, 0x00, 0x54, 0x10, 0x11, 0xa8, + 0x00, 0x04, 0x00, 0x54, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x18, 0x00, 0xce, + 0x2a, 0x20, 0x4c, 0x6f, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x00, 0x3e, 0x1a, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, 0x18, 0x9f, 0x09, + 0x09, 0x86, 0x00, 0x34, 0x00, 0x57, 0x10, 0x11, 0xa8, 0x00, 0x04, 0x00, + 0x15, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x61, 0x00, 0xfd, 0x2a, 0x20, 0x43, + 0x61, 0x62, 0x61, 0x73, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x31, 0x35, 0x2f, 0x06, + 0x5f, 0x05, 0x02, 0x81, 0x00, 0x7d, 0x02, 0x99, 0x05, 0x02, 0x71, 0x00, + 0x0f, 0x01, 0x9f, 0x05, 0x02, 0x53, 0x00, 0x33, 0x00, 0x8d, 0x11, 0x0b, + 0xa9, 0x00, 0x01, 0x6e, 0x00, 0x5d, 0x2a, 0x20, 0x4d, 0x61, 0x72, 0x61, + 0x63, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x00, 0x8e, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x1f, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0x92, 0x00, 0x5d, 0x2a, 0x20, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x57, 0x68, + 0x69, 0x73, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x37, 0x04, 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x0b, 0x16, 0x2b, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0x89, 0x00, 0x64, + 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x04, + 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x08, 0x16, 0x2b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03, 0x55, 0x00, 0x64, 0x2a, 0x20, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x0f, 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd9, 0x01, 0x04, 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, + 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, 0x1f, 0x09, 0x09, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0c, + 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x5e, 0x01, 0x04, 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x45, 0x3c, 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, + 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, + 0x00, 0x19, 0x05, 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xad, 0x00, 0xf0, + 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, + 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, + 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, + 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xc1, 0x01, 0x0a, 0x2a, 0x20, 0x57, + 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x3c, 0x00, 0x17, 0x14, + 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, + 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, 0xdf, 0x14, 0x0d, + 0x18, 0x00, 0x00, 0xc8, 0x01, 0x04, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x20, 0x43, 0x75, 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3c, 0x00, 0x72, 0x27, 0x0c, 0x0f, 0x0f, + 0x07, 0x00, 0x01, 0x1b, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x78, 0x00, 0x4e, + 0x06, 0x11, 0x06, 0x00, 0x08, 0x00, 0x8e, 0x06, 0x11, 0x06, 0x00, 0x01, + 0x4d, 0x02, 0xc9, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x43, 0x75, + 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x3c, 0x00, 0x01, 0x26, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x01, + 0x1e, 0x0c, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x0e, 0x06, 0x11, 0x06, + 0x00, 0x01, 0x17, 0x0e, 0x0f, 0x0e, 0xb6, 0x00, 0x01, 0xed, 0x03, 0xac, + 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, + 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, + 0x00, 0x03, 0x1a, 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x0f, 0x0d, 0x8b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4, 0x00, 0x5d, 0x2a, 0x20, 0x4f, + 0x70, 0x65, 0x6e, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, 0x00, 0x03, 0x1a, + 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x1f, 0x08, 0x08, 0x85, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x3e, 0x04, 0xe5, 0x2a, 0x20, 0x53, 0x68, 0x61, 0x6b, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x0e, 0x8c, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0xc8, 0x00, 0x78, 0x2a, 0x20, 0x4a, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, + 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x3c, 0x00, 0x05, 0x18, 0x1f, 0x04, 0x0f, 0xf4, 0x00, 0x05, + 0x1c, 0x1f, 0x09, 0x10, 0xf0, 0x00, 0x04, 0x09, 0x51, 0x0b, 0x07, 0xa6, + 0x00, 0x04, 0x0e, 0x51, 0x0b, 0x07, 0xa5, 0x00, 0x08, 0x76, 0x03, 0x84, + 0x2a, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x54, 0x72, 0x65, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x2c, + 0x00, 0x05, 0x09, 0x14, 0x0d, 0x00, 0x10, 0x00, 0x05, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x14, 0x15, 0x08, 0x44, 0x00, 0x08, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x19, 0x0c, 0x36, 0x2a, 0x20, 0x43, + 0x61, 0x73, 0x74, 0x61, 0x6e, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, + 0x1f, 0x0b, 0x00, 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x04, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0x64, 0x00, 0x49, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x53, 0x75, 0x72, 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, + 0xff, 0x00, 0x71, 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, + 0x14, 0x00, 0xff, 0x00, 0x31, 0x00, 0x0e, 0x0e, 0x0a, 0xf7, 0x00, 0x03, + 0x05, 0x01, 0xd2, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x75, 0x72, + 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x29, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xff, 0x00, 0x71, + 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, 0x14, 0x00, 0xff, + 0x00, 0x31, 0x00, 0x0e, 0x0b, 0x08, 0xf5, 0x00, 0x08, 0xc6, 0x07, 0xdd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x1d, 0x0c, 0x00, 0x01, 0x00, 0x5f, 0x00, 0x10, 0x09, + 0x00, 0x01, 0x01, 0x5f, 0x00, 0x10, 0x09, 0x00, 0x01, 0xe6, 0x00, 0xba, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x3d, + 0x05, 0x33, 0x04, 0xdf, 0x06, 0x10, 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, + 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, 0x10, 0x1f, 0xf8, 0x00, 0x01, 0x00, + 0x9f, 0x10, 0x0e, 0x76, 0x00, 0x01, 0x11, 0x01, 0xc5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, 0x37, 0x00, 0x08, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x8e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd9, 0x00, 0xf0, 0x2a, 0x20, 0x43, 0x61, 0x73, 0x74, + 0x61, 0x6e, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, 0x1f, 0x0b, 0x00, + 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, 0x04, 0x0d, 0x9f, + 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x00, + 0x64, 0x00, 0x49, 0x2a, 0x20, 0x53, 0x74, 0x69, 0x63, 0x6b, 0x73, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x36, 0x18, 0x00, 0x02, 0x0a, 0x1f, 0x14, 0x00, 0xff, 0x00, 0x02, + 0x17, 0x1f, 0x12, 0x00, 0xff, 0x00, 0x02, 0x2a, 0x9f, 0x14, 0x00, 0xff, + 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x50, + 0x2a, 0x20, 0x53, 0x71, 0x75, 0x61, 0x72, 0x65, 0x20, 0x43, 0x6c, 0x69, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x1f, + 0x00, 0x01, 0x27, 0x1f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x16, + 0x00, 0xff, 0x00, 0x01, 0x27, 0x9f, 0x17, 0x00, 0xff, 0x00, 0x01, 0x00, + 0x12, 0x18, 0x00, 0xff, 0x00, 0x00, 0x71, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x18, 0x00, 0x02, 0x06, + 0x1f, 0x12, 0x00, 0xff, 0x00, 0x01, 0x05, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x16, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x15, 0x00, + 0xff, 0x00, 0x00, 0x85, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2e, 0x3d, 0x05, 0x33, 0x04, 0xdf, 0x06, 0x10, + 0xf3, 0x00, 0x01, 0x00, 0xdf, 0x12, 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x10, 0x1f, 0xf8, 0x00, 0x01, 0x00, 0x9f, 0x10, 0x0f, 0x78, 0x00, 0x00, + 0xf6, 0x00, 0xad, 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, + 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x23, 0x22, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x14, 0x07, 0x00, 0x05, + 0x7f, 0x00, 0x0b, 0x0f, 0xf5, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0c, 0xf6, + 0x00, 0x01, 0x00, 0x5f, 0x00, 0x0e, 0x07, 0x00, 0x02, 0xae, 0x01, 0xe0, + 0x2a, 0x20, 0x42, 0x61, 0x73, 0x73, 0x44, 0x72, 0x75, 0x6d, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x1a, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x19, 0x07, 0x00, 0x05, 0x7f, 0x00, 0x0b, + 0x0f, 0xf4, 0x00, 0x06, 0x7f, 0x00, 0x0f, 0x0d, 0xf6, 0x00, 0x01, 0x00, + 0x5f, 0x00, 0x12, 0x07, 0x00, 0x00, 0xfd, 0x02, 0x22, 0x2a, 0x20, 0x53, + 0x74, 0x69, 0x63, 0x6b, 0x53, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x02, 0x15, + 0x1f, 0x13, 0x00, 0xff, 0x00, 0x01, 0x0e, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x02, 0x21, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x01, 0x00, 0x1f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0xa0, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x27, 0x35, 0x05, 0x33, 0x04, 0xd3, 0x06, 0x10, + 0xf3, 0x00, 0x01, 0x7f, 0xdf, 0x12, 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x11, 0x1c, 0xf8, 0x00, 0x01, 0x7f, 0x9f, 0x10, 0x0d, 0x76, 0x00, 0x00, + 0xd5, 0x00, 0xc8, 0x2a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x43, 0x6c, 0x61, + 0x70, 0x20, 0x28, 0x4f, 0x50, 0x4c, 0x33, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3c, 0x00, 0x0f, 0x0d, 0x9f, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x03, 0x1f, 0x14, 0x10, 0x08, 0x00, 0x0f, 0x1b, 0x1f, 0x11, 0x0f, 0x27, + 0x00, 0x01, 0x00, 0x1f, 0x03, 0x12, 0x09, 0x00, 0x00, 0xf6, 0x00, 0xd5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x35, + 0x05, 0x33, 0x04, 0xd0, 0x05, 0x0f, 0xf3, 0x00, 0x01, 0x7f, 0xdf, 0x14, + 0x0e, 0xf9, 0x00, 0x01, 0x00, 0x9f, 0x12, 0x1f, 0xf7, 0x00, 0x01, 0x7f, + 0x9f, 0x10, 0x0c, 0x76, 0x00, 0x00, 0xa6, 0x01, 0x5a, 0x2a, 0x20, 0x4c, + 0x6f, 0x77, 0x20, 0x46, 0x6c, 0x6f, 0x6f, 0x72, 0x20, 0x54, 0x6f, 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x3d, 0x00, 0x31, 0x15, + 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x13, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, + 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x06, 0x18, 0x04, 0x9c, 0x2a, 0x20, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x3a, 0x33, 0x3f, 0x19, 0x1f, 0x8e, 0x0d, + 0x84, 0x00, 0x33, 0x00, 0x5f, 0x0a, 0x08, 0x64, 0x00, 0x7f, 0x14, 0x1f, + 0x8e, 0x0d, 0x94, 0x00, 0x71, 0x00, 0xdf, 0x09, 0x09, 0x25, 0x00, 0x00, + 0xfd, 0x00, 0x92, 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x46, 0x6c, + 0x6f, 0x6f, 0x72, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x3d, 0x00, 0x31, 0x16, 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, + 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x0e, 0xf6, + 0x00, 0x10, 0x03, 0x1e, 0x11, 0x09, 0xf6, 0x00, 0x06, 0x1e, 0x04, 0xa2, + 0x2a, 0x20, 0x50, 0x65, 0x64, 0x61, 0x6c, 0x48, 0x69, 0x68, 0x61, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x38, + 0x00, 0x3c, 0x00, 0x47, 0x0a, 0x1c, 0xb4, 0x00, 0x13, 0x00, 0xdf, 0x00, + 0x00, 0x04, 0x00, 0x7a, 0x00, 0xc3, 0x8a, 0x10, 0xb7, 0x00, 0x75, 0x07, + 0xc4, 0x0d, 0x1d, 0x59, 0x00, 0x00, 0x85, 0x00, 0x49, 0x2a, 0x20, 0x4c, + 0x6f, 0x77, 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3d, 0x00, 0x31, 0x16, + 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, + 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, + 0xf6, 0x00, 0x06, 0x18, 0x04, 0x16, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, + 0x48, 0x69, 0x48, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x2c, 0x00, 0x09, 0x00, 0x1f, 0x0e, 0x00, + 0x10, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x1f, + 0x17, 0x08, 0x44, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, + 0x5c, 0x09, 0xb6, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x2d, 0x6d, 0x65, 0x64, + 0x20, 0x54, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1d, 0x3d, 0x00, 0x30, 0x16, 0x1e, 0x1a, 0x03, 0xf6, 0x00, 0x14, + 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, + 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x06, 0x0a, 0x04, 0x38, + 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, 0x2d, 0x6d, 0x65, 0x64, 0x20, 0x54, + 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, + 0x00, 0x30, 0x16, 0x1e, 0x18, 0x13, 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, + 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, + 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x04, 0xd8, 0x04, 0x7a, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, + 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x3c, 0x10, 0x7f, 0x00, + 0x1f, 0x0b, 0x00, 0x40, 0x00, 0x71, 0x00, 0x1f, 0x16, 0x07, 0x34, 0x00, + 0x13, 0x00, 0x1f, 0x8b, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x9f, 0x1b, 0x07, + 0xa6, 0x00, 0x07, 0x36, 0x03, 0x84, 0x2a, 0x20, 0x48, 0x69, 0x67, 0x68, + 0x20, 0x54, 0x6f, 0x6d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x3d, 0x00, 0x31, 0x16, 0x1e, 0x1a, 0x03, + 0xf6, 0x00, 0x14, 0x03, 0x5e, 0x0c, 0x01, 0xf6, 0x00, 0x70, 0x00, 0x1e, + 0x11, 0x03, 0xf6, 0x00, 0x10, 0x03, 0x1e, 0x11, 0x03, 0xf6, 0x00, 0x04, + 0xd1, 0x04, 0x2a, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0d, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x18, 0x15, 0x07, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x8d, 0x0b, 0x18, + 0x2a, 0x20, 0x43, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x20, 0x43, 0x72, + 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, + 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x2c, + 0x00, 0x7f, 0x00, 0x1f, 0x07, 0x00, 0x52, 0x00, 0x71, 0x00, 0x1f, 0x15, + 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0c, 0x04, 0x84, 0x00, 0x07, 0x00, + 0x9f, 0x19, 0x06, 0xa3, 0x00, 0x24, 0xbe, 0x0d, 0xe1, 0x2a, 0x20, 0x52, + 0x69, 0x64, 0x65, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x1c, 0x00, 0x0d, 0x00, + 0x1f, 0x07, 0x07, 0xe1, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x11, 0x1f, 0x0a, 0x12, 0xe5, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x19, 0x05, 0xc1, 0x2a, 0x20, 0x54, 0x61, 0x6d, 0x62, + 0x6f, 0x72, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x35, 0x22, 0x06, 0x5f, 0x05, 0x02, + 0x81, 0x00, 0x75, 0x00, 0x96, 0x0f, 0x0c, 0xa1, 0x00, 0x04, 0x01, 0x9f, + 0x05, 0x02, 0x53, 0x00, 0x3e, 0x08, 0x92, 0x0d, 0x0d, 0xa6, 0x00, 0x01, + 0x82, 0x01, 0x54, 0x2a, 0x20, 0x53, 0x70, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x43, 0x72, 0x61, 0x73, 0x68, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, 0x61, + 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x54, 0x2c, 0x00, 0x7f, 0x00, 0x1f, 0x05, 0x06, 0x50, 0x00, 0x71, + 0x00, 0x1f, 0x15, 0x06, 0x33, 0x00, 0x13, 0x00, 0x1f, 0x0b, 0x04, 0x85, + 0x00, 0x07, 0x00, 0x9f, 0x1a, 0x06, 0xa5, 0x00, 0x21, 0x62, 0x06, 0x0a, + 0x2a, 0x20, 0x43, 0x6f, 0x77, 0x42, 0x65, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3b, + 0x00, 0x2f, 0x00, 0x1f, 0x15, 0x13, 0x26, 0x00, 0x37, 0x20, 0x1f, 0x15, + 0x0d, 0x36, 0x00, 0x28, 0x23, 0x1f, 0x95, 0x0c, 0x26, 0x00, 0x32, 0x00, + 0x1f, 0x13, 0x10, 0x28, 0x00, 0x01, 0x61, 0x01, 0x04, 0x2a, 0x20, 0x43, + 0x72, 0x61, 0x73, 0x68, 0x20, 0x32, 0x20, 0x28, 0x41, 0x6e, 0x69, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x73, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x2c, 0x00, 0x7f, 0x00, + 0x1f, 0x00, 0x00, 0x50, 0x00, 0x71, 0x00, 0x1f, 0x14, 0x06, 0x33, 0x00, + 0x13, 0x00, 0x1f, 0x0a, 0x04, 0x84, 0x00, 0x07, 0x00, 0x9f, 0x19, 0x06, + 0xa4, 0x00, 0x20, 0x4a, 0x0b, 0xe0, 0x2a, 0x20, 0x56, 0x69, 0x62, 0x65, + 0x53, 0x6c, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x34, 0x00, 0x00, 0x08, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x0a, 0x00, 0xf5, 0x00, 0x00, 0x15, 0x1f, 0x0a, 0x00, 0xf5, 0x00, 0x0c, + 0xc9, 0x07, 0x58, 0x2a, 0x20, 0x52, 0x69, 0x64, 0x65, 0x43, 0x79, 0x6d, + 0x62, 0x65, 0x6c, 0x6c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x2c, 0x00, 0x0c, 0x00, 0x1f, 0x0c, 0x00, 0x10, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x18, 0x14, 0x08, 0x44, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xd9, 0x0b, 0x32, + 0x2a, 0x20, 0x48, 0x69, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, + 0x12, 0x12, 0x0a, 0xf9, 0x00, 0x00, 0xf0, 0x00, 0xa6, 0x2a, 0x20, 0x4c, + 0x6f, 0x42, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x36, 0x00, 0x01, 0x05, + 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x00, 0x17, 0x12, 0x0a, 0xf9, 0x00, + 0x00, 0x00, 0x9c, 0x14, 0x00, 0xf9, 0x00, 0x33, 0x04, 0x12, 0x12, 0x0a, + 0xf9, 0x00, 0x00, 0xe9, 0x00, 0xa6, 0x2a, 0x20, 0x4d, 0x75, 0x48, 0x69, + 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x36, 0x00, 0x01, 0x07, 0x9f, 0x0f, 0x00, + 0xf9, 0x00, 0x72, 0x00, 0x1e, 0x12, 0x0a, 0xf9, 0x00, 0x01, 0x00, 0x9f, + 0x12, 0x00, 0xf9, 0x00, 0x12, 0x00, 0x12, 0x16, 0x0a, 0xfa, 0x00, 0x00, + 0xf6, 0x00, 0x92, 0x2a, 0x20, 0x4f, 0x70, 0x48, 0x69, 0x43, 0x6f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x71, + 0x00, 0x17, 0x14, 0x0c, 0xfb, 0x00, 0x00, 0x00, 0x9c, 0x17, 0x00, 0xfa, + 0x00, 0x11, 0x00, 0x12, 0x10, 0x0c, 0xf8, 0x00, 0x01, 0x82, 0x00, 0xfd, + 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x43, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x36, + 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xf9, 0x00, 0x72, 0x0d, 0x17, 0x14, + 0x0a, 0xf9, 0x00, 0x00, 0x00, 0x9c, 0x16, 0x00, 0xfa, 0x00, 0x31, 0x06, + 0x12, 0x0f, 0x0a, 0xf8, 0x00, 0x02, 0x1c, 0x01, 0x04, 0x2a, 0x20, 0x48, + 0x69, 0x67, 0x68, 0x54, 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2a, 0x05, 0x13, 0x06, + 0x9f, 0x19, 0x08, 0xc6, 0x00, 0x00, 0x0d, 0x5f, 0x13, 0x05, 0xbc, 0x00, + 0x36, 0x05, 0x5f, 0x19, 0x06, 0xa6, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, + 0x86, 0x00, 0x01, 0x32, 0x02, 0x94, 0x2a, 0x20, 0x4c, 0x6f, 0x77, 0x54, + 0x69, 0x6d, 0x62, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x2a, 0x05, 0x13, 0x06, 0x9f, 0x19, 0x08, + 0xca, 0x00, 0x00, 0x0a, 0x5f, 0x13, 0x05, 0xbc, 0x00, 0x36, 0x05, 0x5f, + 0x19, 0x06, 0xac, 0x00, 0x02, 0x00, 0x5f, 0x0f, 0x11, 0x8c, 0x00, 0x01, + 0x32, 0x00, 0x5d, 0x2a, 0x20, 0x48, 0x69, 0x41, 0x67, 0x6f, 0x67, 0x6f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x3c, 0x00, 0x3e, 0x17, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, + 0x18, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x34, 0x00, 0x54, 0x10, 0x11, 0xa8, + 0x00, 0x04, 0x00, 0x54, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x18, 0x00, 0xce, + 0x2a, 0x20, 0x4c, 0x6f, 0x41, 0x67, 0x6f, 0x67, 0x6f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, + 0x00, 0x3e, 0x1a, 0x9f, 0x09, 0x09, 0x86, 0x00, 0x0e, 0x18, 0x9f, 0x09, + 0x09, 0x86, 0x00, 0x34, 0x00, 0x57, 0x10, 0x11, 0xa8, 0x00, 0x04, 0x00, + 0x15, 0x10, 0x11, 0xa8, 0x00, 0x01, 0x61, 0x00, 0xfd, 0x2a, 0x20, 0x43, + 0x61, 0x62, 0x61, 0x73, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x31, 0x35, 0x2f, 0x06, + 0x5f, 0x05, 0x02, 0x81, 0x00, 0x7d, 0x02, 0x99, 0x05, 0x02, 0x71, 0x00, + 0x0f, 0x01, 0x9f, 0x05, 0x02, 0x53, 0x00, 0x33, 0x00, 0x8d, 0x11, 0x0b, + 0xa9, 0x00, 0x01, 0x6e, 0x00, 0x5d, 0x2a, 0x20, 0x4d, 0x61, 0x72, 0x61, + 0x63, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x00, 0x8e, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x1f, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0x92, 0x00, 0x5d, 0x2a, 0x20, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x57, 0x68, + 0x69, 0x73, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x37, 0x04, 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x0b, 0x16, 0x2b, + 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0x89, 0x00, 0x64, + 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, 0x57, 0x68, 0x69, 0x73, 0x74, 0x6c, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x04, + 0x00, 0x00, 0x25, 0x12, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0xf0, 0x00, 0x0e, 0x00, 0x12, 0x08, 0x16, 0x2b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03, 0x55, 0x00, 0x64, 0x2a, 0x20, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, + 0x1f, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x0f, 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd9, 0x01, 0x04, 0x2a, 0x20, 0x4c, 0x6f, 0x6e, 0x67, + 0x20, 0x47, 0x75, 0x69, 0x72, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, 0x1f, 0x09, 0x09, + 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0c, + 0x0e, 0x10, 0x18, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x5e, 0x01, 0x04, 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x45, 0x3c, 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, + 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, + 0x00, 0x19, 0x05, 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xad, 0x00, 0xf0, + 0x2a, 0x20, 0x57, 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3c, + 0x00, 0x17, 0x14, 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, + 0x0c, 0xe8, 0x00, 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, + 0xdf, 0x14, 0x0d, 0x18, 0x00, 0x00, 0xc1, 0x01, 0x0a, 0x2a, 0x20, 0x57, + 0x6f, 0x6f, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x3c, 0x00, 0x17, 0x14, + 0x1f, 0x16, 0x16, 0xc8, 0x00, 0x1e, 0x0a, 0xdf, 0x19, 0x0c, 0xe8, 0x00, + 0x15, 0x00, 0x1f, 0x12, 0x1c, 0xb8, 0x00, 0x19, 0x06, 0xdf, 0x14, 0x0d, + 0x18, 0x00, 0x00, 0xc8, 0x01, 0x04, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x20, 0x43, 0x75, 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3c, 0x00, 0x72, 0x27, 0x0c, 0x0f, 0x0f, + 0x07, 0x00, 0x01, 0x1b, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x78, 0x00, 0x4e, + 0x06, 0x11, 0x06, 0x00, 0x08, 0x00, 0x8e, 0x06, 0x11, 0x06, 0x00, 0x01, + 0x4d, 0x02, 0xc9, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x20, 0x43, 0x75, + 0x69, 0x6b, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x3c, 0x00, 0x01, 0x26, 0x0c, 0x0f, 0x0f, 0x07, 0x00, 0x01, + 0x1e, 0x0c, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x0e, 0x06, 0x11, 0x06, + 0x00, 0x01, 0x17, 0x0e, 0x0f, 0x0e, 0xb6, 0x00, 0x01, 0xed, 0x03, 0xac, + 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, + 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, + 0x00, 0x03, 0x1a, 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x0f, 0x0d, 0x8b, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4, 0x00, 0x5d, 0x2a, 0x20, 0x4f, + 0x70, 0x65, 0x6e, 0x54, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x24, 0x00, 0x03, 0x1a, + 0x1f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x1f, 0x08, 0x08, 0x85, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x3e, 0x04, 0xe5, 0x2a, 0x20, 0x53, 0x68, 0x61, 0x6b, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x3c, 0x00, 0x0f, 0x00, 0x58, 0x0e, 0x18, + 0x49, 0x00, 0x0f, 0x00, 0xd8, 0x10, 0x12, 0x49, 0x00, 0x02, 0x0e, 0x8c, + 0x16, 0x19, 0x8d, 0x00, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x69, 0x00, 0x00, + 0xc8, 0x00, 0x78, 0x2a, 0x20, 0x4a, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, + 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x3c, 0x00, 0x05, 0x18, 0x1f, 0x04, 0x0f, 0xf4, 0x00, 0x05, + 0x1c, 0x1f, 0x09, 0x10, 0xf0, 0x00, 0x04, 0x09, 0x51, 0x0b, 0x07, 0xa6, + 0x00, 0x04, 0x0e, 0x51, 0x0b, 0x07, 0xa5, 0x00, 0x08, 0x76, 0x03, 0x84, + 0x2a, 0x20, 0x42, 0x65, 0x6c, 0x6c, 0x54, 0x72, 0x65, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x2c, + 0x00, 0x05, 0x09, 0x14, 0x0d, 0x00, 0x10, 0x00, 0x05, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x14, 0x15, 0x08, 0x44, 0x00, 0x08, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x19, 0x0c, 0x36, 0x2a, 0x20, 0x43, + 0x61, 0x73, 0x74, 0x61, 0x6e, 0x65, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1c, 0x00, 0x09, 0x17, + 0x1f, 0x0b, 0x00, 0xff, 0x00, 0x01, 0x0f, 0x1f, 0x13, 0x00, 0xff, 0x00, + 0x04, 0x0d, 0x9f, 0x14, 0x00, 0xff, 0x00, 0x02, 0x0d, 0x9f, 0x14, 0x00, + 0xff, 0x00, 0x00, 0x64, 0x00, 0x49, 0x2a, 0x20, 0x4d, 0x75, 0x74, 0x65, + 0x53, 0x75, 0x72, 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, + 0xff, 0x00, 0x71, 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, + 0x14, 0x00, 0xff, 0x00, 0x31, 0x00, 0x0e, 0x0e, 0x0a, 0xf7, 0x00, 0x03, + 0x05, 0x01, 0xd2, 0x2a, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x75, 0x72, + 0x64, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x29, 0x36, 0x00, 0x01, 0x05, 0x9c, 0x0f, 0x00, 0xff, 0x00, 0x71, + 0x00, 0x10, 0x12, 0x0a, 0xff, 0x00, 0x00, 0x00, 0xd3, 0x14, 0x00, 0xff, + 0x00, 0x31, 0x00, 0x0e, 0x0b, 0x08, 0xf5, 0x00, 0x08, 0xc6, 0x07, 0xdd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 +}; diff --git a/source/data/xg.wopn b/source/data/xg.wopn new file mode 100644 index 0000000000000000000000000000000000000000..38208b255b4759eb1869c24d807ecd02a19fa9ae GIT binary patch literal 186204 zcmWId5AZY6bu#kvW?*7q;9}tB3@%B`OGzwBQLr**zz!nZ6@uL&6qF4N46rM3z%Gms zWkfJ>u$Xa(BZS!yOdKo@9O4LJ9t0Byiw}o5LRbL7#K96|h;RpmmZ1VTY`rr}FcpGT zm{=+VSLPOEU{PTJF~i7A!7(o}Cmo9lNvH}_1=pP9k|HcBBp@mbj1+=P@>0ArON`Ln zK{#F*2r6MDsDg035DrsCbf;tRH5jxM+=~+PQUWp)^YSsIAp%ScjOtPBEQJgyDiNHV z=?q0I_r>`qG8i(=W?@;z;P1+~PfNk6C^J2y1dl1U3@VDzoUGdzLWH6@Im;OoIig!x zmobEKMssqQGuR3;-qccX%}GrzDauR+8>67%n3tKGSdy7stO;=k0|N^~p<)CpTMC1d zR2Vy33IikCOjZ_A21Cx7Ei9=F<};ZeYAJZ+=Ve!hQ#F z;AJpUmgi#q&cMVc$;PJ6z``!c&TyTtX&y@X(@Ol78Mj{CTHXqr6_1PrX=Pf zLIdO?9tKMWF}3*(jI0a+%J&->m>8@S?=vv6F%+=Lv$44|7*;V#Xel`7B$j37rDDla zATt;k7%UkCW#%_9vM~h8fekTKoX^0>&QJ(3MDGiupq7GjYEEi#Ng`ASHp)PlBbZUz zfx%iymS6q{L!n?ayA&HkA-6mOCmVyn872cQ1^1l%AwQUs?c;0IQt&M;&P;a7ufS>m0|O7(5Pm*}?+jMP{Co^w z844NXc^JMi6!3#7wi+fGErqbmq@u)vjQqS*1r5KeM?%-p0z1r5iX#FUgw)aV5{kA=ZiT7EKn z9fOj_{7EcL42BGH{OsQu7`f&7nZ7eHr83HCDMVD}z#M|j7*P3Jz~C@fem+C8d@R5G ze})2v_y!4nh5`n81|BvBwo*{QhLk3i<|Gz5rRL-mV`iQIUl?>4%H_mZ<-RbO%Zahc zwJ;cQNHEJEVJPAdW|E)6(71u6L`%V?G$%PTH?;`la;#X4fmz(2o9!zDmrgJrn;Qco z^9&A#uMCWw(>NGDGl*ny_<>SvZf<^F3XV`^U|>*Z5D|=FVEM%m$RESN@`J%ZJcdE~ z2SbuLSa6O5!vZaZfYhSo(&EgrRR5y%#LPSe4aek?%=|o4BXA`B|H8mx#3w2(&b@;{ zP*7BcRiA;4U4)ZCgMpD-oKb!~C>PghDFo#wXM^px& zxEL7Or-17BATBU9$AKXXl#5G?k~44`!^0rPAS^wFi$R%zg-w`?L6d=zlaGr*jDdlL z2Sm+r0F`V(si`SAjR3jML_kn-h5&;&1EZ)U0|OfaBf9_t0|x^Wdv5~+2gp0iv=khZ zlkqayaQAy1(&3z<`6JM0kMMz zf~Th^+)EY=5$f^m4Br@nmEu_#elQrz#&a?JWH4limuC@YNaA5s1(j?isVNYrU@udo zSd3*t#5ubd3|XV4*}EB7xFf_FcQY_DhzPOpFeGF!cxoy5m!%e^6lIpBDrooz_!z?- zfbbLxv&wuPh8_k*g$f=gy`Nq!M1&*3r!ZirMc z6U>nB5JTKRhTPFofcnHE5t8PL!SS4yS(FNK6AyziE0@d!HjY0GMk4c>8NM-8@J?l8 zZ)6Z;5Mbi}4l;xRlyML90kB@77~@*G^#7?f4zx!7A7 z7#ZZ**((?nyBM};DY#|krKc9*FalI%a4>MH$_p{*GZ+ZRbFt_%6d1_!vFJk*n?6HH zCqsaiLO^D6HpmD>K%%gC7&sU><>W;f`WX!P;(0-a7|Qdp*uxAd5(63HR+L(jlUiI1 zG6+kx!Nb7D!XPWf$Kt1mM{7DEE37Z1ZPXqW`&Bo=@SFhp?xoWsMQ zz+f*tpN~VGfk7gkmqVR_*?`6f|5a^Aeqk@{9E#kqb8D{}%>Z1{d*AIbLoCCe{!+PEH09hDbRsE(T+X z5INRP1_4WkEG-47Ax1dr43JCAB=57cD=-)c&*x+lXD~It&&DRkV8}3^jpG}G^hZWt zP=%hKlZmrzWME*hQZ|(1XJHU!C@|#VWM*eDm**8=U}0cl?&e|;Wtii@tN<|tUlRvx z2p`xGTW*jcCUSfbLwXQ~IH%_1;A%{RgT+eOP(uK0h&~@DGY5mIoFBxHK4_IOS4+V; zKd+=HKPM@%xEQPljo<;fL>pp=JRjI4IuMsI^g&$06rrUMQk0sTpOYV4Qk0pOj?Ek! zRYTDWjSRvJjKaO#3@!|eZ2Vjdpmf5=#UKGnY%@X0zN#uSJFx_(F{TV643~wumoo^l z2J=gNXAtBN5ElQ=AjlKM$9J58=?TL(Ed~FgMP>UALGaxPt1Cz8o7lQ~x zV0}CnQwf70gFGK=3PV7>A2-t%2JNLBx3m;OGII+O^D05gv11+v2Cfi(hVP)v&&Ti! zM1dNSOz!-=4F4E7T0ku&NMN`Y7pLYX<)mWCK{mpMvgf!NSQr>(`nniI8Cco)x3l!Jh@5*n%tbN-}Vp#KWM#V&&rlc8OjO$R*~+ejq0lFhE_xGzBTKd0-nxU|?Xd zU|va32F()1HBcL06eUs)v_Ri-AF? zi-SRnfq`>+JChVcAoKKgNOKEhNGZN91_KX+tq_BB9~)yD10!=UFPAC<14~~!lPE(V z>%?|&VqSkduXJDuYg%SfJ!xT`8h4SV&Fg^o~6(klH2d9D?^a>j8MX9NIDf#K}T7!qd zR7g>(kAcyhL4k8B1Cuy|A;WA|CNl;RwkZuv;Knd016Jla6(trIW6wC?#xSFZ7#pYu zsU;6e!G<#OpuRsNgD|LJJ;wpmLkHD6AVaX^U$E;zWvrYa3xgy>k+~ozGbe+&yr2NM zF+7orK^zptAG8#L^9za+^YVievG+&8-cbl-VDw;6;PYi*l3+06^P^Pyql!Jxn$3^K%!wFhK~2umL{Oj@-RLQ?bci*UIEYKUAv*d<&+ z;DBZA1_i7TOE1)rgIWqsiA9+u`FW{0%mI5xybof?M2I1MAVY*%CPEEaprzoSl%I;V zIAVYT3n4~ziRqv&mxh=;h>{bS&%njN!XN?S&2eCe)lzWHOV7zH#*^ACgc#KzhG;_! z5eFN>#3~>U=`^hcd8fEIKOfYA#M(pWVXzQlP-mXbz{9|xA}`Ot!@#J_Kc9gI)ZYQ| z<~V@bY|c4}MWFE$oL=Hl7Lw~_VB}&b67uhs?P4fo5dw821$l(~*|b1W90saJlaupv z@^P5){|ke-krC%9P~tQa?CIe;!BE83#lcX)AjEyDm!Se=NS&5~TTW>S{xVhoT*itr z7>Z5qWpHI+HVoro%3xq*@N0pTvCFg+f>Oa9NgUn$|6dra845W<_?S}}j8uAA7*rXU z%tHj2LK&DqhJZ8RS}lcu#60|tF<~fW5C%0xjD#ogLS4cjisX_7S_)42B_%njPC5DI z&~$-L*)Wtc2!R?!MiM=}3}Fn+nvuK=W(Jx~p0W!5N9!r5TCI8JVE4%`DDL zRB$OuOoxuPfrma!6olBA)EPpQh1i(X7>d~VIT!hU+o{?Exl7lDF ziVGWZ2!T4|hV=~$3?2-O9Q{n7*r`7Nin2Kl3_V&3{>h+pf~Wlm3KJ17W>E$s%_*=j zfyEBEJo&4o;FFq|5?olCScEH{Z3K)Y;u{#d8JMJ`8W^+~3OQvM7^D~&d7~Q`_(9$Y zgBp^ED^wX67;G2{K?Nc|1Grq|XDDKkXJFuGFoIB!WDjzQb7D?TW_|&7x7e_jG6;j3 zNJbL9ybPfX%9=BI8Ll!Uvih|!XoFnh32}*ub4F&G0%SZEUe3d!+KPcuq>qmwo`F%k zkB?zL1G{!VA4fa`BTEP~$9{&~d!P&ma*3&PMq&|;9s+{}Ly_D?Muz_kg>oVwN?0z4 zo#8)&APbl`$ALi);u72~3s5PmECebZLAf{pl8Z$#axusy=5Cp3C0It;7#RM)V6b5@ zk%(qxW@jkoi(p`8V<=$&^Ne}IK)lvk#!iS!EI{2R1r2vs-(V~7d_{h`LU4W#WE7Hx zkx?+Xmot-rQMPe169+>Pn_Me{2SXx@JUgUh12xMNQw;r5%N^6P#|@~wXP4yXW-DVb zRP1D8P-fr|lV=Atc^LRu!L0(ux1d~{q7a@~l&hc-kY7}inxc?al%K2M;u`^rCUyo! zf#VDe%nXJC?w|p9Mgdk(g>DF;EcdY9(NYM=-r+h9?OO7k$-Fc?Y&aj=Ln1c-%l zu!u4QF$aPSF<=dWv}2k6fnDO9k)Mg9P-5U=U}a%Y@@rrSVqg&LVqgenVBqlqHT)R( zIvW^R80I*zIe^NO^3)Vm+fjKk3~W-TnHduqSe3azEj~uJPBx}u1}0f9Hij<@<$HN+ zwG@0)OF)S}8C53|&ql;h2GX_{>uzQcWZ>juXJ%N+z{JhY$nX@D>|?>+@kq?U(+kvL zQRH!EVF+a?WN>a_3}Iko=4=O*Y+_vO3}&DXf~uB6aCvHK0g}ZSOdVDwF*Y`a5Qb6- zmJU{sA^adin1nf@hU94}xJ4)wCF1KC+bOY$$O|!;GZ+c-fI^f>RDKe#DnlWJH^+fR z7G#KSa9&DCQDSnof<{P2Xugmb5Uh+Nn#GRavEfa4TGz>JTFTb1BIprKd`D;t9$>wFdlSB5|VUQV_! zP{3w@nxL8K86|$H*!$KDp!9B}F{zOykRen(go8zdAy_*elwyt8;vp%PMNCV>8EQ&%bYz)B+g$yi>jLZy-!p`js#te*Ntn3WN409ZKo@gn!CTHa1F@k|X zgvCfRpn)Njfl;@cfgzNEi6;OQNreJaK-3%u=380{!O59!nP4k%5E3kgN>e~(fU?qj zW(H@543+sH9wUcvBRF=Lu4yR*gGOR;=m!h4Ffi%Iv$MJ|FmX&`X86p&E;$)AX2;Ga z&&u$PAv1u<1=QTi%fg-L|9@dHVlb2R7vYd(2ojIsWE5afbdBd1mtZhr5SHW=XAock zje-VeB<5xor{d~kfVyw2()0NkelRdf#IrIyVPI04&&TkHfsHF3RKO_;fnvuyzXZ2K zK>kr-4i=xy!D7rHEHk@AJwl8{nSq&4LX1O{A-Ysxf|i1JVh(722c#T3He)Ce z51A%f&X6J+$IBkdP{=OYz{SQ;A}q?o>BhkOkjV=aCg}m0IBHN(t-~N8z{ov8fWeG` zK}L{)!IXhTod?uaVdCfkQINXWEi)x02UkfCa+{U1p)6!z${ai}WiHPH8B^-wf{$(m zXC&q&f?G-0XR8>9X_kj%=4I!kI;G}dpTJ{aU}rFvyu!l5%wQxa!pg?XP$Y1TnIo8? zn0p#K`(_4>ncSfsb!KAK!8iCC|^!z-+>p z2pWV*P0evBD$T`}fp`pL9C?Kq!Wm4Jcm>$o847u%K=XiwJW_lN-xyezF)3*&gy-j{ zIDuv*L4k;Y!7dROkrxv?!63(VUy|nqgD8W%kjxGSN!I%!yb=t|S3n)fki^XFdJ zA&h~EH(r_}jDe9uUP??2G*ZSVc#MHr5;W-?lvKSAi85$Vs z8RQulL>PoQU3$eh85m?+q}i4+2pnaofDit=6{VIa_~mC7VRTNGxBk^rx_S*8H_=Od|+S{W8+}>!oVm6p=8)WJcS1Ke_9GonMElf z<*B$Q(d8M~C65X4v2%zD^7Ha>F>nZ+;1dYrU}Na!6WGSUs=>Gq)I-ln1%}ozk-sNh{z8R1zH)v$RN)z@SB0Dh+!Ap zKAa4F3kC*I?EGh7(3clt_|L$=FrR_pKLaC&un5C128IO;g*f%XC42=K82IHu+8CrE zhDf3qGKnDzt`;}Lf`LIE+)cu%1Tqw>0Ge9Cs^J);7nAuy;zz$HH&ulxSLvbSL{6Ob2U_`qi>Brn0hz)-`% zCnWTfp_Hc&L^0MdEbzz&_0HWBb8_-=3=f-v@|Tz(!v_u~L3v(=?`YH<2L@BT&VWe5 zy0i=^U0McMm-Y@b4%;ERp`-<*f5y(hB+ds~np4QY%ErLPz{J4K!pqLU@{{oH| zSQwZz4l^?*GB7B!LuL?Kk!KJ#NQDsc2Y8aGgjW+Z<49dm#1^Cz*L_s4U{0tld_xS{38QA#eGYIs9 z+9zQy`T0e`g{j$CwKG7+*$f%txxotJ85lSj7{z#)`K0(6Rk&FB#Q9kS7y`hC?Z%;9%hl^!NM2FBe#@ejsuen0RuqNHVh0J^1Sl;TxRv4 zp(Q>R7I{w4M0~wGr!aUnKNh5F6pw}gT|&S)u?V#ADl-pbxE^M`4GRMUmxz?$3?60% z0ZG9bJd7ODybKl$h8!~7Li(WcO{FgInPcET&g&5^mQ_P zXMj?idl{oZh6LbP{S0!;|1S>40&Kz(Y%F37rZQ3-%w`Nm%2MpiW(-BloDDqe4D7QQ zZ-NYQE=nxU0O`Pry=_4K9R`LE9EA+vRu=<Bl zHNp7|>h0#imDtPKoo48;&?jsvqA$OgQahrveJ zT1kR~KbWINM~IbQnZt;K4`K*Eh??WT)C-Co*SvH*iA@@m*aT1#n=B-;i9-|HRlKeM zS6*rad!r`_88RwQW(fEsmZoQ9=Hct~fEL9Gf|rGJ^b4_NFfeNb2ylXyg?mpF;Nau5 zU~mdf28~$YGsRv;h(X>5v_ykL0YY*3vr6%H<^ZNfn2bB5d^DunlVBnj{ z$N!Ckfn%ny+&2aWj+p}T-xwH=GJkc;&o3!1NiD#&?4N}<%W3XnB;pJ9m;ILvo>y}xR zTAp8&U91qDmztWQ5a8^K*4`(=5U6#d)MyBdh5(5nphfBVCw}ihYqy*zJO9LH7xOX7 z&p(;O&PavxPZ;wkKK~?W7HEGlh3B7Gyd38!bcpDnC=Ex zIcTRdW#^ydvDHw1{z)8BLu3OSR-@LBhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By z2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1J zhQMeDjE2By2#kinXb6mkz-S1JhQNpo0nn+fpv#EJynhFDNInxEx*=HZ@4$Ee4(PHc zcET5|(k+g(Kv#uQcK;3=!Ba=*=9^KA26YH%DS+?IA>#fWH^}`vq8Rt@uy77)AC5Y1 zGz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!n zMnhmU1V%$(Gz5qUfq=~9Yz6S8-B_lZLHc+R9OblMdk_&W|EiK*%cTJgy(azi8Gjn zTxVqyV=!cx&&KhMLHZ;U=p=CreWL=SAut*OqaiRF0@Misx1!X%>7+Ct+nM4@^SSPkKh=DGTGoX%BMok$Ffzc2c4S``7 z0>Oo)iFj_R2l>}VfJri*fw7x`sa~pqL7#zURnkQD`i7D zE*1t+h5|z#PG)unb9q()1{MY;=5{Uy(2*2urNb<6z+qBSl$x8Lldn)*QiR2OHmZiA zXB!y=7#M|nxfvW87}+?u7}OXT8Q8eM^To`6uxJ{}Lf|kdE&&~ZQjEhNHo}Io=eQYI z7#L;xx)?+mSlM{G7&I6d8F;uDBpBv6Fv|I)rYDx)273Mp0|TQ>cNZvp+1NQ4 zR2dkV**U<8jU8KT;Be5W@Ms8(hQMeDjE2By2#kin@D2eVUxF)8Yy^xn;~PNhPqd^O z7_=A)ImN;2Pk8;I>rVsKpg#Y-wU(U=U+5(hP542xVa6oWa4)%FDzP-@wDdTPQH4 zfq{i#jsus*2n@GTFOP=6Xb6mkz-S1JhQMeDkQ)N#3{`USTzo$m0=XhNxqdPj@yHAE z{9+Ikk>uw3&cLt7IDuS~NA-<{z-S1JhQMeDjD`R?A)uw;l384mUsRHrpQoVVAK+uG ziD3*2v&wuvh8_k*sbCI<9Sls8lUNzPGcYksWMOav?Q|YJk^&>hMg_2iKyYdy_H$2Q zig_3m7|f*a!_Gh95oa)UzRv+U|Ad?48v`pZQzcC8C_NeiqaiS&L%=8BEmOfGvjk5d zSTHb11n@9?WnfemVrBTmz$h!m$?%zhks*kS;VT1AAo~S8nn$IELI`LvF$&AGF??ev z6qg4bp=Bs951L?QWR_=O08g;$3nlJgB$}t8v|1gOBybnqvE3>Fd71* zAut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*U^E0qLtr!nMneEk z2=FkN3Mq>BF)+F_C@@cDU=n9AWSGs$WX2%EHl=|HymZ73kM>ci(GVC7fzc2c4S~@R z7!85Z5Eu=CK@tK1`31@OML16<1-X)i!PQ-UGJ73^lE(Z=EKLlC3?f2uzZe)9B!wk@ zGcd9+RDe{CEDSpTBp!19$$SoWRtC`dCw!puPv%3dN(oHOz>ixeCaGm3GX1I@$0!eXEr*uW6TP$1RIz!1n_z~S4#z{*g- z(G8;JIIun%CSd{dMo3Xb9oqSg&1V|*cj6o7zKNIxl|b#nETq9L>U6bC$@uc z(PG>PQa6f6Ltr!nMnhmU1V%$(Gz11N1e`L{)qL{H@j1YTp-^6)nZcjIP(hxJA)cX# zMV_5uJ%bSoSa6O5LjXSAM9YEiX$nY8!Ks0Vft7_p&7*-Kh=D<{nSmjgfq};z)Z$~{ zYlBRG^Nk)!fiv_*B}PMFGz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU z1V%$(Gz3ONU^E0qLtr!nMneEO1e}U8OHv_=P_RzVpsYU;!oL2b2id?;_Gk!BA%6K<}-;E_=`jE2By2#kinXb6mk zz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDFv#&RFfa%)$igW`hIj@6P6kFs2=$DZ z!$l2kS&dolvk81=U}U&2#P^edkzu}o{0}h2SH{37$Z&^nP+QxWGjParbFncn)H5`I z)${iWNPu`C4&zORnOX|YiNz&}d8sAEAR#Q+PKJR~T%McZKLaDbyfDLm1{U7=A`Jf- zm>?8G3WJE2LU2iDayBj#7#JALBp8^u z7?{|=PGDpOdxuGJKG-a%OIjJEu-T0&riUkX_$wF~zA+SPDL5q-7lZFXM%4@FDJwBB zFi10qursjMGjQ{NWnimk;OF_q07@==>8NLx=2*|Zka6`U;4dF#Hgz*_Pc7iig zQsH)^GIS&um_)&W!N>=um=s~L1Bx<+1q@Qbd7!AVK-GuBGqPb2WDsFsWnf@pkQbNW zWDsDGmFH(?U|^7E5aeWFe9O2)OTi;CFU2`0u|Pq?KfuRW6U6`!$I6C*pLae3$P{LI z5djVcerb6@esu;$26<*7PH?~yc8Q4%g8+jF0~-SaGu$Nte9Pf3*~oZCOTj0`5S`(GnitNV_Ko5;GC0RoSLHG zkywJJ8_IRFGPakL@8fM@FlLD7;z(hr7m@Gdox)JaaG#S?m4V?eLyDGyM`n5kKBs_O zA_jJe2*M?N5SMU%V_;Kd+Nz}xkeZU1!3h#3*h2&29|>CqLkyRogo%=tf`373o<}CmAYpLTVc=wt=VK6n z6+=uM^5PsWFdpwb7Fn=MbaPWvaKsG*gRCus0X~-qGPytk7N04KU_&H?zcC;MEW$f1 z_n4+=DL5A;7H24EIOb*MCYEF-7o(?H7aM_k26=7<2Zllhc`J3*N#sm_(TNvSzGh8QL=FfjP&Fz_(Q^CAM4 znMXpD9UicPuUIRz6r3|M^HPgbaeB*9hoPQ9o}D2G&9K}*3ozZ{Qe7#JL_8T1+CMTONE%oXHCdCeF!l;x-Ls4*BZ$O{W- zFfb;9t5I+|F~XTr+`ukjU;w*B1QM`Zuz+2{pb*CX*JJ583^~kwEa5~{ZN+;YBB8Uvg!^heRO0iD)VDoVh zj%EyutaJDoelip>hzoK3WME*JBf{{L!I(t|%=-YU0e$lE8)8Gike3WuS_;0U9+}Q~ z+@fQ~z{oxy;u1NK-HZ(Lg&2M^2r&qWas2{S8L^;33BMtBaF-M^h>P(226;!E;TMA- zgOC8v4+h32hDqQ;37Ji8d1`tss}+H^jz< z!A=US8*WHA#1K&s1CQ=o6FdW#E?=5)#T}V3-6lBse3# zs3bfiv$zC%^q8|SFsKS~F>pi5vJVV=3_{#u+AtntC#VVPlb@H4#|#EzEQW9(3}Frh z83J~Rf_rIZ5%w6evjMedply3_&@u7zp||ZpF2Q341H=$cPH+bSYzR9Kk|9iSpkyDO zpP%BClaIYk&cMLnYQrEdA}=O(f`NIqW`H^fAnhSVso2X#1_p*;9R^m=v10Z5$qE}EMk#hSb#$(k}%=*Cq$exOnCiC z7?PDZn1t7#5Mf9nVi5}?cqI&z_x-21cg&0s`C&49xOe z{L>gX&VpvH!K;I4z5c`ivg(5avg$(;y#7Rhe+6ikgh7atfpG#O3!T=V7!h&_XkkxC zW^O@ZUM1F;0SRjIFfee1fMSyoG^r0-00y4pWpd}|W%$RyvyMdtqzpeMYW)e+B?<~S zRwrl^aLFvtf(@#yKY_Xgr=N83yM#-Rc_L)Uo#OQ;gj}Lez$LuQwOR^<3^5?a5S&XJ zprJ(6`V&GfF(Tj+8|HbCyrbY;nX3m~_2dI}0gUnot%Tv{W)NT~WDt>N|H2^5Al%Kc z4Ybw*v=U~H0|Ue8`V(OWdD^T$0j;b9#|~)ym4XK>Qs6Xr{fQK0{fRVW{fPk5`V+-l ztbeJr{sit4XgWc6i#=#1j1)U~B@7mqXq;d>16rJbf9c5R`V)A}!WpCMPvAiTXN;~t zfd>VgL96vAC`(5oKx-|?SbxHawEl$o7BQVc#QGB^m(len3=D&D z{RuDEHwG3l#-otNFxl%*zA$i{WuFC4Q=^R05Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C z(GVC7fzc2c4S~@R7!85Z5CDY$Ro0)_fF=`V85tTt+tC>qL>PoQU3$eh85m?+q}i4+ zaBgGp1lffj6LJ0tegiM|J91r|v5Oe+s z0TUP)7znRFA;KjD*Pk$)BkU4IRR#kFc?NM#21ZeNLDBCFX7%#?Jm10RpMZ8>eqj_O z+7M9`L%1P^@Uk;7Zz9GJBF;Y{>=Fmist?%Se8~AH@O0|O(gh#12M1{Riypfh(E8N`GI<}ff?GB^=71e;5QWnO|z76rNF1TiUA!j8d^ zK^}bm2}2yeKs!T$mOPKZG6n(2F(^!COlE{#;$y;4z$DGUz|T;?E)P2Hq@EdkzDzy4 zDCiI%hF!!Qwj+bXCBjgb@G&uJDR_85XPdDedBDH`atXUM$i-lnd|;?&lNaDkXQ*cv z<>NPJU~*x28Iqq%U_G}SaV`PPz=LeUj9TvM2G?6fLjL#T58Nl1P!fwZD&$6yanYyv`^u*3$6V&NYQj5Q3)uzLYn6!ZKO z2ITWkgd|z(kd4V);g{zUS7#{Um**A#&cLh&TF(Yqr!;#036;-3an8?6Peflq zgB&>whA4SQx|5+;OP)_+B?Bn$Nd918lwg=rsy!9v9naSCv z@fyiS=ofat!$bjefQG!3Fb6v$o4l|v2Rj3UygVQHAQKS9yp%BmWQZAFLsV2j%PQoB z*+3K@H|Vfvc>xF|$equ?ZU(;C2y{CVt$Qceazkf%MfJ>Sf*5bQm1`z z;<*{VgH8+Aq00R`gk2&+z$FtIb*OUx4q=x_5^%{3kV`S zzXL1kM$bQig#>~gJ^us|7BJT6{X4LbK+xFk-x=QPPqe_d`VIN}cfjjUM$bP%q~uZ7 zXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kgR6+?h3>rV`AS&dol zvk81=U}U&2#0R?mW4?g=4+cht`2u`p42*&dcL+OK%oJn@1BxMn@;^90hVYg#%yD2K z;;tDY?%xT{ODsxNu)yOWdm9Ep1`!5U2Jqqp3Gn4C@`4Z0jnV__n4+oW&H^>ibL{qaon)Ppn^YO zS#E&5GrIm{J}Xl&gB`~tUfyL4jGa_E|3pi{IltT~H75s>#<7r&)(rX#@}k0O4CV^* zqP%7d8p`rhdDIw;803WoG#D5YL04Uju0I)Fe=@rM1S9v1u0O#D5{A+BCzxR}y8Z++ zOh(tAV1@~y^G~eU+_XUVm6jytrIr+9>IaJuasG*xf^TU_D!!|82%Ud2y8Z+l)}zEI z7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!83T8v^>ERX_3! z4F4Gz^yR^Kx-iTK@fbOTMHqfDFf1VE)*D{|1_pkJA<_^-BvA~RM9hUXL& zAut*Ol!gHA>&fPef(n^Jh5$;f7}Y--0;3@?EJ7eCF*6UpM#26S@ z8EW_>LA)|H4h{xWhB*#wo#CDV3LzP#c`2zySk3x>fx(7>fguERC>$e$JOdXO10w@J zx6m>MCN*YxzJCmCoy?LTL&7ujQm~o;5wuX?XOM4TU|_J90$&pdZfGztBuKJ>+%?C6 zy#ZuMa7j^WVlG4n77}3ysF?z~Sr2ShDlZq*I}%Q%Nl7`WShPZg{(sTqXJFxr=VklC z!NM2M&-R6noiCmb!UIur92jDuYVlC|Y>I`ff(;Df42InOU3?jQjN$=Oys3PQqTXGC zEPQhu7&Gwb9+esmfk7AoF8S$rUG@K!y$yqzfV>#P2R>6Fc?kvvh8hk&A)%iPr96Eg zim`@afk%E(ajJrQVopvz_HG7)sVxHogP0)02M#7dd0vL^Xw)1B1_C!Xf*k1}&%iEu zOn{G_K~#{Rmye5qL*N9TKo|oXLpPtmHU=gaMoW+~{Mg?D(m!KoU=ru!XZX%g$iT|R zz{bGDz|F$T&cKquI2XSGgG<(yg@H-qFf(H!1A{UD%a!Yj(a016XcP?!`*2y^qXaWFA(GV`)Cu(vVEK-G>?qaiRF0;3@? z8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0u+QmKt_IEs)BPO?#=o7 zpi$8K;89S}3KD(>4uSi80cEEN;F*|Vu(oA2V37x%*u%ge3_7ugfq{=-{s#jC zgMb8Y83WTo#(lxbMTsTJ84AuIi?CuFQ-mRW5`4cI7#R2@md%nyzo z0w#<~kA?vCLclq(NWnEPJu?sI{HP5J0|S?cl;8{=W(EOC!5KV^9MZfD77T_QGTcJ? zpcH!$WJqucuDKlshX3#EY!vE5xj{M4PK+DmdIyfaPKNIcP>OReV-(1cfW%^4JCqsz ze{nDtU=x;LV-aI8m676LHe)bSmSSf%V<=+gY~W#MV4uZ!6J!YZ+!2r+Sh2SaXwsR1 z;R8n@19;YofkC>P;Xi``BbZ{Fzz`OkS(J)@65T;ZoI_1;J_9>LnF=?EV&azXXZXQo z%q8E?@RN&S8ADb`5uurQ8yf}#26=vlFC2_&^1KXRI1Cu%c^JNM6!6R!WE5wx>}Brq zN-Zf!%)uQu3=AT642*27AfFcSu!3gI3&mI&7``zubF+eO3Y+7=ZWdgUSX7*miDTvs zq&37w*jid1bU0Lv5tuUKU~OPvV2wH zre|d4;h8kHF<~fW5aePOXE5aG7h=m`VAco_;7n&=WbmFSz`-}ifx$C48MNX9pDFe- zLJaagpcKxb0HHX1+62@YI3N`30>;vS(!6B+3obNm7+9D=v&{?!3?bqSKN%P~;`w;h z845Y%g&2M^Fo!bk^GHoBDoILBEWr^n|6k~UGT(e2hHo4Ud^7p@zi}{d%oLXU#=yWa zQ$YS31LINVuWtGICB-GF1;q+jZ%JcdU~o2JU{R76WjMjW$SE%YI@FNmzJQMizN)ZZigJ1{q#%bp{SA=Cf{@ zMXBZaMcE3`eyOP`sVNEp&c3Ki%ZM;!kVe(0vqnQ;Gz3ONU^E0qLtr!nMnhmU1V%$( zGz3ONU^E0qLtr!nMnhmU1O_Su2;aZ6i?GuO-oL|ml&~Rmx_{@}=>0oP;`2Ee{xdML z%JVUNXJBGvOw>{!cy*2v0|SFJ{OX*q3~cob{5;qmuuG7xi9x!5N09j|HSXW>goFv<`*%QLg5(m! z{X4ARSePike8`vfIZq9+;zk|~yY&y(;AOVZd6v+KMBB1+sn6O1L1M^wR@82 zwTYt+Lp_5$J3|mdA%naqn=ykhgFFvQ3j;gDd`Y&&3@ZD0A3|KBfal5@9~lN-26=Y& zhp;7Iyn^yvLQi2l4oTL7S_&bFxk>p&xR=7%TAC`c#j`RsGL$k*BG#UU3mfn20g_VlZSA;^+Foz$61oC!kBgLNarc5^Z5Zst=W}wlG4M%%DHDbe0l^gvED(xOg5ew}Oz;~*`2L;0p!+hC5{na2 z6Bshj+0axUQWVrO&17XNWZ;Js1;z}$g4`<^81q2h@l7mBOiskPppC)ZhJl|Uf=|Me zfuG@qfRHBx6GNYvWG@51ypWJkCIiDHkRibt`9&q+8JWc;*rUgsg@Hj;h>L+6l3PA7 z@G%H+i)q7njGYWApfJfx$72Q&_wS@=DS%y~;9i^2swg;@ zW@h6^aJn`OMe02K{Ok;j(qO8B!H-Rlje&!q4@5EgGMxmKC#iY(3{kgXU{nL^W(0Ky z*%??E_`tfw`S|&^F)+SmUIQxGN>W3LG86OCu_rb{_wU$(QmlVLY96VEaB#FjQY-@l zpB>9nP-08W#&P-(I8|Wg9ljoL-VuW2of8ZdS_)p7Adi5W1=us8KX^qq3;!1e(28z> z4-71vf!yrN7+ClMx!IR8aFl^sN+9haMX5L!UJ<%~N0P@3RGt)0CnR<#xqnB1 zxeSs{@EM{Btup>I6fy`1as6jtV7M&8@SnkufsdQx3j@b7{yK03j1r?EFd71*Aut*O zqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8Umvs01E-ofFpwtgDejN1B3!CC=lRe zU}TJE08!5vh**DOVjY%AQ!wZwvd=QV3K^}aq zLme^KI(W-7FsjRogVuR~Hnaa{U{s$E;R#7V?uG~>V!;OCRUf#$0h39TU|?bc`-72D zUX0;C1KI0Oj?<@pU>xj0f7>P6)Hc&9KFGTi6nRApfJ%P@(sOGJoq zNkD2!Vva{<1}>MRSTNWyxO2&DVGw4x54zu|is5jVz;=dG_9MKqkqiv244#BtB1ym{ z6G34Dnh^2G#2F+Et~v~y4Dx&o@ab);#T_QRWekjGn2M-!{t2-zVdekKz{qf4gzqN< zICj2+DV{P0CNHK+&?e<_g6E%z%1MFFKVimlI0l;p%N(k#KLPDSPtU;T6v+7}AeSJW zI)Zfmi5$x!x}JZ6<`TsDCu|ZdVvsGL6s|u3#}2-eOQ7eU;C2ZYiv=WL@tFcS|3m_G z{t31yX1T#qMV0j@5SI`>{{*LZST3??p`2@h^=1bj@VOSeh=o1OJQAYp@N+E$d$}dF z6r4xTKN&s$Wc2(Ka4n8RjGljj6c|wE==mqmfIy-K_4z0KNJp12-UOZXLB{zfD2J%T zfeayZbjj%XCrAkd%B0!(C;YtKu%k;Dzl@%L0=g_zM3lpYfukNA^1OrM{1ZN|?+nbW zjF%vdVY1IZ`O3g?jQuXyU!%lm2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1J zhQMeDjE2By2*5&Mbo~h|BoOrI`V&N0z*x$JPhuH7>rY^g#!Z9QpIip7$*G5}$q|(K z!N6EQUx2raf#E8{3976=!R-Q=4A> zJEQAQU_lC@C2ScC#lUNFkXFKsu0N?~kmqJ_0NwB+#%=<-u%?^g8|a1?Y4&9d>?GX3 zGrIl+lFT6F=>0p8a6l$U&p$y94G4Sm{1ZqxAd{o#pCE?@gpGNI!*DzQWOV(>@V|d& zbo~hfE2ju(ku57^#R(_F==u{#0XIsHhQMeDjE2By2#kinXb6mkz-S1JhQJ^S0RY(B B?@<5% literal 0 HcmV?d00001 diff --git a/source/decoder/mpg123_decoder.cpp b/source/decoder/mpg123_decoder.cpp new file mode 100644 index 000000000..b357f8010 --- /dev/null +++ b/source/decoder/mpg123_decoder.cpp @@ -0,0 +1,238 @@ +/* +** mpg123_decoder.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2008-2010 Chris Robinson +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include +#include "mpg123_decoder.h" +#include "loader/i_module.h" + +#ifdef HAVE_MPG123 + + +FModule MPG123Module{"MPG123"}; + +#include "mpgload.h" + +#ifdef _WIN32 +#define MPG123LIB "libmpg123-0.dll" +#elif defined(__APPLE__) +#define MPG123LIB "libmpg123.0.dylib" +#else +#define MPG123LIB "libmpg123.so.0" +#endif + +bool IsMPG123Present() +{ +#if !defined DYN_MPG123 + return true; +#else + static bool cached_result = false; + static bool done = false; + + if (!done) + { + done = true; + auto abspath = FModule_GetProgDir() + "/" MPG123LIB; + cached_result = MPG123Module.Load({abspath.c_str(), MPG123LIB}); + } + return cached_result; +#endif +} + + +static bool inited = false; + + +off_t MPG123Decoder::file_lseek(void *handle, off_t offset, int whence) +{ + auto &reader = reinterpret_cast(handle)->Reader; + + if(whence == SEEK_CUR) + { + if(offset < 0 && reader->tell()+offset < 0) + return -1; + } + else if(whence == SEEK_END) + { + if(offset < 0 && reader->filelength() + offset < 0) + return -1; + } + + if(reader->seek(offset, whence) != 0) + return -1; + return (off_t)reader->tell(); +} + +ssize_t MPG123Decoder::file_read(void *handle, void *buffer, size_t bytes) +{ + auto &reader = reinterpret_cast(handle)->Reader; + return (ssize_t)reader->read(buffer, (long)bytes); +} + + +MPG123Decoder::~MPG123Decoder() +{ + if(MPG123) + { + mpg123_close(MPG123); + mpg123_delete(MPG123); + MPG123 = 0; + } + if (Reader) Reader->close(); + Reader = nullptr; +} + +bool MPG123Decoder::open(MusicIO::FileInterface *reader) +{ + if(!inited) + { + if (!IsMPG123Present()) return false; + if(mpg123_init() != MPG123_OK) return false; + inited = true; + } + + Reader = reader; + + { + MPG123 = mpg123_new(NULL, NULL); + if(mpg123_replace_reader_handle(MPG123, file_read, file_lseek, NULL) == MPG123_OK && + mpg123_open_handle(MPG123, this) == MPG123_OK) + { + int enc, channels; + long srate; + + if(mpg123_getformat(MPG123, &srate, &channels, &enc) == MPG123_OK) + { + if((channels == 1 || channels == 2) && srate > 0 && + mpg123_format_none(MPG123) == MPG123_OK && + mpg123_format(MPG123, srate, channels, MPG123_ENC_SIGNED_16) == MPG123_OK) + { + // All OK + Done = false; + return true; + } + } + mpg123_close(MPG123); + } + mpg123_delete(MPG123); + MPG123 = 0; + } + + Reader = nullptr; // need to give it back. + return false; +} + +void MPG123Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) +{ + int enc = 0, channels = 0; + long srate = 0; + + mpg123_getformat(MPG123, &srate, &channels, &enc); + + *samplerate = srate; + + if(channels == 2) + *chans = ChannelConfig_Stereo; + else + *chans = ChannelConfig_Mono; + + *type = SampleType_Int16; +} + +size_t MPG123Decoder::read(char *buffer, size_t bytes) +{ + size_t amt = 0; + while(!Done && bytes > 0) + { + size_t got = 0; + int ret = mpg123_read(MPG123, (unsigned char*)buffer, bytes, &got); + + bytes -= got; + buffer += got; + amt += got; + + if(ret == MPG123_NEW_FORMAT || ret == MPG123_DONE || got == 0) + { + Done = true; + break; + } + } + return amt; +} + +bool MPG123Decoder::seek(size_t ms_offset, bool ms, bool mayrestart) +{ + int enc, channels; + long srate; + + if (!mayrestart || ms_offset > 0) + { + if (mpg123_getformat(MPG123, &srate, &channels, &enc) == MPG123_OK) + { + size_t smp_offset = ms ? (size_t)((double)ms_offset / 1000. * srate) : ms_offset; + if (mpg123_seek(MPG123, (off_t)smp_offset, SEEK_SET) >= 0) + { + Done = false; + return true; + } + } + return false; + } + else + { + // Restart the song instead of rewinding. A rewind seems to cause distortion when done repeatedly. + // offset is intentionally ignored here. + if (MPG123) + { + mpg123_close(MPG123); + mpg123_delete(MPG123); + MPG123 = 0; + } + Reader->seek(0, SEEK_SET); + // Do not call open with our own reader variable, that would be catastrophic. + auto reader = std::move(Reader); + return open(reader); + } +} +size_t MPG123Decoder::getSampleOffset() +{ + return mpg123_tell(MPG123); +} + +size_t MPG123Decoder::getSampleLength() +{ + off_t len = mpg123_length(MPG123); + return (len > 0) ? len : 0; +} + +#endif diff --git a/source/decoder/mpg123_decoder.h b/source/decoder/mpg123_decoder.h new file mode 100644 index 000000000..7e915884e --- /dev/null +++ b/source/decoder/mpg123_decoder.h @@ -0,0 +1,49 @@ +#ifndef MPG123_DECODER_H +#define MPG123_DECODER_H + +#include "zmusic/sounddecoder.h" + +#ifdef HAVE_MPG123 + +#ifdef _MSC_VER +#include +typedef ptrdiff_t ssize_t; +#endif + +#ifndef DYN_MPG123 +#include "mpg123.h" +#else +#include "../thirdparty/mpg123.h" +#endif + +struct MPG123Decoder : public SoundDecoder +{ + virtual void getInfo(int* samplerate, ChannelConfig* chans, SampleType* type) override; + + virtual size_t read(char* buffer, size_t bytes) override; + virtual bool seek(size_t ms_offset, bool ms, bool mayrestart) override; + virtual size_t getSampleOffset() override; + virtual size_t getSampleLength() override; + + // Make non-copyable + MPG123Decoder() = default; + MPG123Decoder(const MPG123Decoder& rhs) = delete; + MPG123Decoder& operator=(const MPG123Decoder& rhs) = delete; + + virtual ~MPG123Decoder(); + +protected: + virtual bool open(MusicIO::FileInterface *reader) override; + +private: + mpg123_handle *MPG123 = nullptr; + bool Done = false; + MusicIO::FileInterface* Reader = nullptr; + + static off_t file_lseek(void *handle, off_t offset, int whence); + static ssize_t file_read(void *handle, void *buffer, size_t bytes); +}; + +#endif + +#endif /* MPG123_DECODER_H */ diff --git a/source/decoder/mpgload.h b/source/decoder/mpgload.h new file mode 100644 index 000000000..f93f08c47 --- /dev/null +++ b/source/decoder/mpgload.h @@ -0,0 +1,40 @@ +#ifndef MPGDEF_H +#define MPGDEF_H + +#if defined HAVE_MPG123 && defined DYN_MPG123 + +#define DEFINE_ENTRY(type, name) static TReqProc p_##name{#name}; +DEFINE_ENTRY(int (*)(mpg123_handle *mh), mpg123_close) +DEFINE_ENTRY(void (*)(mpg123_handle *mh), mpg123_delete) +DEFINE_ENTRY(int (*)(void), mpg123_init) +DEFINE_ENTRY(mpg123_handle* (*)(const char* decoder, int *error), mpg123_new) +DEFINE_ENTRY(int (*)(mpg123_handle *mh, ssize_t (*r_read) (void *, void *, size_t), off_t (*r_lseek)(void *, off_t, int), void (*cleanup)(void*)), mpg123_replace_reader_handle) +DEFINE_ENTRY(int (*)(mpg123_handle *mh, void *iohandle), mpg123_open_handle) +DEFINE_ENTRY(int (*)(mpg123_handle *mh, long *rate, int *channels, int *encoding), mpg123_getformat) +DEFINE_ENTRY(int (*)(mpg123_handle *mh), mpg123_format_none) +DEFINE_ENTRY(int (*)(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done), mpg123_read) +DEFINE_ENTRY(off_t (*)(mpg123_handle *mh, off_t sampleoff, int whence), mpg123_seek) +DEFINE_ENTRY(int (*)(mpg123_handle *mh, long rate, int channels, int encodings), mpg123_format) +DEFINE_ENTRY(off_t (*)(mpg123_handle *mh), mpg123_tell) +DEFINE_ENTRY(off_t (*)(mpg123_handle *mh), mpg123_length) + +#undef DEFINE_ENTRY + +#ifndef IN_IDE_PARSER +#define mpg123_close p_mpg123_close +#define mpg123_delete p_mpg123_delete +#define mpg123_init p_mpg123_init +#define mpg123_new p_mpg123_new +#define mpg123_replace_reader_handle p_mpg123_replace_reader_handle +#define mpg123_open_handle p_mpg123_open_handle +#define mpg123_getformat p_mpg123_getformat +#define mpg123_format_none p_mpg123_format_none +#define mpg123_read p_mpg123_read +#define mpg123_seek p_mpg123_seek +#define mpg123_tell p_mpg123_tell +#define mpg123_format p_mpg123_format +#define mpg123_length p_mpg123_length +#endif + +#endif +#endif diff --git a/source/decoder/sndfile_decoder.cpp b/source/decoder/sndfile_decoder.cpp new file mode 100644 index 000000000..480438327 --- /dev/null +++ b/source/decoder/sndfile_decoder.cpp @@ -0,0 +1,268 @@ +/* +** sndfile_decoder.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2008-2010 Chris Robinson +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include "sndfile_decoder.h" +#include "loader/i_module.h" + +#ifdef HAVE_SNDFILE + +FModule SndFileModule{"SndFile"}; + +#include "sndload.h" + + +#ifdef _WIN32 +static const char* libnames[] = { "sndfile.dll", "libsndfile-1.dll" }; +#elif defined(__APPLE__) +static const char* libnames[] = { "libsndfile.1.dylib" }; +#else +static const char* libnames[] = { "libsndfile.so.1" }; +#endif + +extern "C" int IsSndFilePresent() +{ +#if !defined DYN_SNDFILE + return true; +#else + static bool cached_result = false; + static bool done = false; + + if (!done) + { + done = true; + for (auto libname : libnames) + { + auto abspath = FModule_GetProgDir() + "/" + libname; + cached_result = SndFileModule.Load({ abspath.c_str(), libname }); + if (cached_result) break; + } + } + return cached_result; +#endif +} + +sf_count_t SndFileDecoder::file_get_filelen(void *user_data) +{ + auto &reader = reinterpret_cast(user_data)->Reader; + return reader->filelength(); +} + +sf_count_t SndFileDecoder::file_seek(sf_count_t offset, int whence, void *user_data) +{ + auto &reader = reinterpret_cast(user_data)->Reader; + + if(reader->seek((long)offset, whence) != 0) + return -1; + return reader->tell(); +} + +sf_count_t SndFileDecoder::file_read(void *ptr, sf_count_t count, void *user_data) +{ + auto &reader = reinterpret_cast(user_data)->Reader; + return reader->read(ptr, (long)count); +} + +sf_count_t SndFileDecoder::file_write(const void *ptr, sf_count_t count, void *user_data) +{ + return -1; +} + +sf_count_t SndFileDecoder::file_tell(void *user_data) +{ + auto &reader = reinterpret_cast(user_data)->Reader; + return reader->tell(); +} + + +SndFileDecoder::~SndFileDecoder() +{ + if(SndFile) + sf_close(SndFile); + SndFile = 0; + + if (Reader) Reader->close(); + Reader = nullptr; +} + +bool SndFileDecoder::open(MusicIO::FileInterface *reader) +{ + if (!IsSndFilePresent()) return false; + + SF_VIRTUAL_IO sfio = { file_get_filelen, file_seek, file_read, file_write, file_tell }; + + Reader = reader; + SndInfo.format = 0; + SndFile = sf_open_virtual(&sfio, SFM_READ, &SndInfo, this); + if (SndFile) + { + if (SndInfo.channels == 1 || SndInfo.channels == 2) + return true; + + sf_close(SndFile); + SndFile = 0; + } + Reader = nullptr; // need to give it back. + return false; +} + +void SndFileDecoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) +{ + *samplerate = SndInfo.samplerate; + + if(SndInfo.channels == 2) + *chans = ChannelConfig_Stereo; + else + *chans = ChannelConfig_Mono; + + *type = SampleType_Int16; +} + +size_t SndFileDecoder::read(char *buffer, size_t bytes) +{ + short *out = (short*)buffer; + size_t frames = bytes / SndInfo.channels / 2; + size_t total = 0; + + // It seems libsndfile has a bug with converting float samples from Vorbis + // to the 16-bit shorts we use, which causes some PCM samples to overflow + // and wrap, creating static. So instead, read the samples as floats and + // convert to short ourselves. + // Use a loop to convert a handful of samples at a time, avoiding a heap + // allocation for temporary storage. 64 at a time works, though maybe it + // could be more. + while(total < frames) + { + size_t todo = std::min(frames-total, 64/SndInfo.channels); + float tmp[64]; + + size_t got = (size_t)sf_readf_float(SndFile, tmp, todo); + if(got < todo) frames = total + got; + + for(size_t i = 0;i < got*SndInfo.channels;i++) + *out++ = (short)std::max(std::min(tmp[i] * 32767.f, 32767.f), -32768.f); + total += got; + } + return total * SndInfo.channels * 2; +} + +std::vector SndFileDecoder::readAll() +{ + if(SndInfo.frames <= 0) + return SoundDecoder::readAll(); + + int framesize = 2 * SndInfo.channels; + std::vector output; + + output.resize((unsigned)(SndInfo.frames * framesize)); + size_t got = read((char*)&output[0], output.size()); + output.resize((unsigned)got); + + return output; +} + +bool SndFileDecoder::seek(size_t ms_offset, bool ms, bool /*mayrestart*/) +{ + size_t smp_offset = ms? (size_t)((double)ms_offset / 1000. * SndInfo.samplerate) : ms_offset; + if(sf_seek(SndFile, smp_offset, SEEK_SET) < 0) + return false; + return true; +} + +size_t SndFileDecoder::getSampleOffset() +{ + return (size_t)sf_seek(SndFile, 0, SEEK_CUR); +} + +size_t SndFileDecoder::getSampleLength() +{ + return (size_t)((SndInfo.frames > 0) ? SndInfo.frames : 0); +} + +// band-aid for FluidSynth, which is C, not C++ and cannot use the module interface. +#ifdef DYN_SNDFILE + +#undef sf_open_virtual +extern "C" SNDFILE * sf_open_virtual(SF_VIRTUAL_IO * sfvirtual, int mode, SF_INFO * sfinfo, void* user_data) +{ + return p_sf_open_virtual(sfvirtual, mode, sfinfo, user_data); +} + +extern "C" const char* sf_strerror(SNDFILE * sndfile) +{ + return p_sf_strerror(sndfile); +} + +extern "C" sf_count_t sf_readf_short(SNDFILE * sndfile, short* ptr, sf_count_t frames) +{ + return p_sf_readf_short(sndfile, ptr, frames); +} + +#undef sf_close +extern "C" int sf_close(SNDFILE * sndfile) +{ + return p_sf_close(sndfile); +} + +#endif + + +#else // in case someone decided to build without sndfile support + +extern "C" int IsSndFilePresent() +{ + return false; +} + +extern "C" SNDFILE * sf_open_virtual(SF_VIRTUAL_IO * sfvirtual, int mode, SF_INFO * sfinfo, void* user_data) +{ + return nullptr; +} + +extern "C" const char* sf_strerror(SNDFILE * sndfile) +{ + return "no sndfile support"; +} + +extern "C" sf_count_t sf_readf_short(SNDFILE * sndfile, short* ptr, sf_count_t frames) +{ + return 0; +} + +extern "C" int sf_close(SNDFILE * sndfile) +{ + return 0; +} + +#endif + diff --git a/source/decoder/sndfile_decoder.h b/source/decoder/sndfile_decoder.h new file mode 100644 index 000000000..3bf916a61 --- /dev/null +++ b/source/decoder/sndfile_decoder.h @@ -0,0 +1,50 @@ +#ifndef SNDFILE_DECODER_H +#define SNDFILE_DECODER_H + +#include "zmusic/sounddecoder.h" + +#ifdef HAVE_SNDFILE + +#ifndef DYN_SNDFILE +#include "sndfile.h" +#else +#include "../thirdparty/sndfile.h" +#endif + +struct SndFileDecoder : public SoundDecoder +{ + virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) override; + + virtual size_t read(char *buffer, size_t bytes) override; + virtual std::vector readAll() override; + virtual bool seek(size_t ms_offset, bool ms, bool mayrestart) override; + virtual size_t getSampleOffset() override; + virtual size_t getSampleLength() override; + + SndFileDecoder() = default; + // Make non-copyable + SndFileDecoder(const SndFileDecoder& rhs) = delete; + SndFileDecoder& operator=(const SndFileDecoder& rhs) = delete; + + virtual ~SndFileDecoder(); + +protected: + virtual bool open(MusicIO::FileInterface *reader) override; + +private: + SNDFILE *SndFile = nullptr; + SF_INFO SndInfo; + MusicIO::FileInterface* Reader = nullptr; + + static sf_count_t file_get_filelen(void *user_data); + static sf_count_t file_seek(sf_count_t offset, int whence, void *user_data); + static sf_count_t file_read(void *ptr, sf_count_t count, void *user_data); + static sf_count_t file_write(const void *ptr, sf_count_t count, void *user_data); + static sf_count_t file_tell(void *user_data); +}; + +#else +#include "../thirdparty/sndfile.h" +#endif + +#endif /* SNDFILE_DECODER_H */ diff --git a/source/decoder/sndload.h b/source/decoder/sndload.h new file mode 100644 index 000000000..806120fba --- /dev/null +++ b/source/decoder/sndload.h @@ -0,0 +1,27 @@ +#ifndef SNDDEF_H +#define SNDDEF_H + + + +#if defined HAVE_SNDFILE && defined DYN_SNDFILE + +#define DEFINE_ENTRY(type, name) static TReqProc p_##name{#name}; +DEFINE_ENTRY(const char* (*)(SNDFILE* sndfile), sf_strerror) +DEFINE_ENTRY(int (*)(SNDFILE *sndfile), sf_close) +DEFINE_ENTRY(SNDFILE* (*)(SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data), sf_open_virtual) +DEFINE_ENTRY(sf_count_t (*)(SNDFILE *sndfile, float *ptr, sf_count_t frames), sf_readf_float) +DEFINE_ENTRY(sf_count_t(*)(SNDFILE* sndfile, short* ptr, sf_count_t frames), sf_readf_short) +DEFINE_ENTRY(sf_count_t (*)(SNDFILE *sndfile, sf_count_t frames, int whence), sf_seek) +#undef DEFINE_ENTRY + +#ifndef IN_IDE_PARSER +#define sf_close p_sf_close +#define sf_open_virtual p_sf_open_virtual +#define sf_readf_float p_sf_readf_float +#define sf_seek p_sf_seek +#endif + +#endif +#endif + + diff --git a/source/decoder/sounddecoder.cpp b/source/decoder/sounddecoder.cpp new file mode 100644 index 000000000..098a6b269 --- /dev/null +++ b/source/decoder/sounddecoder.cpp @@ -0,0 +1,184 @@ +/* +** sounddecoder.cpp +** baseclass for sound format decoders +** +**--------------------------------------------------------------------------- +** Copyright 2008-2019 Chris Robinson +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + + +#include "zmusic/zmusic_internal.h" +#include "sndfile_decoder.h" +#include "mpg123_decoder.h" + +SoundDecoder *SoundDecoder::CreateDecoder(MusicIO::FileInterface *reader) +{ + SoundDecoder *decoder = NULL; + auto pos = reader->tell(); + +#ifdef HAVE_SNDFILE + decoder = new SndFileDecoder; + if (decoder->open(reader)) + return decoder; + reader->seek(pos, SEEK_SET); + + delete decoder; + decoder = NULL; +#endif +#ifdef HAVE_MPG123 + decoder = new MPG123Decoder; + if (decoder->open(reader)) + return decoder; + reader->seek(pos, SEEK_SET); + + delete decoder; + decoder = NULL; +#endif + return decoder; +} + + +// Default readAll implementation, for decoders that can't do anything better +std::vector SoundDecoder::readAll() +{ + std::vector output; + unsigned total = 0; + unsigned got; + + output.resize(total+32768); + while((got=(unsigned)read((char*)&output[total], output.size()-total)) > 0) + { + total += got; + output.resize(total*2); + } + output.resize(total); + return output; +} + +//========================================================================== +// +// other callbacks +// +//========================================================================== +extern "C" +short* dumb_decode_vorbis(int outlen, const void* oggstream, int sizebytes) +{ + short* samples = (short*)calloc(1, outlen); + ChannelConfig chans; + SampleType type; + int srate; + + // The decoder will take ownership of the reader if it succeeds so this may not be a local variable. + MusicIO::MemoryReader* reader = new MusicIO::MemoryReader((const uint8_t*)oggstream, sizebytes); + + SoundDecoder* decoder = SoundDecoder::CreateDecoder(reader); + if (!decoder) + { + reader->close(); + return samples; + } + + decoder->getInfo(&srate, &chans, &type); + if (chans != ChannelConfig_Mono) + { + delete decoder; + return samples; + } + + if(type == SampleType_Int16) + decoder->read((char*)samples, outlen); + else if(type == SampleType_Float32) + { + constexpr size_t tempsize = 1024; + float temp[tempsize]; + size_t spos = 0; + + outlen /= sizeof(short); + int done = 0; + while(done < outlen) + { + size_t got = decoder->read((char*)temp, tempsize * sizeof(float)) / sizeof(float); + for(size_t i = 0;i < got;++i) + { + float s = temp[i] * 32768.0f; + samples[spos++] = (s > 32767.0f) ? 32767 : (s < -32768.0f) ? -32768 : (short)s; + } + if(got < tempsize) + break; + done += got; + } + } + else if(type == SampleType_UInt8) + { + constexpr size_t tempsize = 1024; + uint8_t temp[tempsize]; + size_t spos = 0; + + outlen /= sizeof(short); + int done = 0; + while(done < outlen) + { + size_t got = decoder->read((char*)temp, tempsize); + for(size_t i = 0;i < got;++i) + samples[spos++] = (short)((temp[i]-128) * 256); + if(got < tempsize) + break; + done += got; + } + } + delete decoder; + return samples; +} + +DLL_EXPORT struct SoundDecoder* CreateDecoder(const uint8_t* data, size_t size, zmusic_bool isstatic) +{ + MusicIO::FileInterface* reader; + if (isstatic) reader = new MusicIO::MemoryReader(data, (long)size); + else reader = new MusicIO::VectorReader(data, size); + auto res = SoundDecoder::CreateDecoder(reader); + if (!res) reader->close(); + return res; +} + +DLL_EXPORT void SoundDecoder_GetInfo(struct SoundDecoder* decoder, int* samplerate, ChannelConfig* chans, SampleType* type) +{ + if (decoder) decoder->getInfo(samplerate, chans, type); + else if (samplerate) *samplerate = 0; +} + +DLL_EXPORT size_t SoundDecoder_Read(struct SoundDecoder* decoder, void* buffer, size_t length) +{ + if (decoder) return decoder->read((char*)buffer, length); + else return 0; +} + +DLL_EXPORT void SoundDecoder_Close(struct SoundDecoder* decoder) +{ + if (decoder) delete decoder; +} diff --git a/source/loader/i_module.cpp b/source/loader/i_module.cpp new file mode 100644 index 000000000..59742efd0 --- /dev/null +++ b/source/loader/i_module.cpp @@ -0,0 +1,113 @@ +/* +** i_module.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2016 Braden Obrzut +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#include "i_module.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#endif + + +#ifndef _WIN32 +#define LoadLibraryA(x) dlopen((x), RTLD_LAZY) +#define GetProcAddress(a,b) dlsym((a),(b)) +#define FreeLibrary(x) dlclose((x)) +using HMODULE = void*; +#endif + +bool FModule::Load(std::initializer_list libnames) +{ + for(auto lib : libnames) + { + if(!Open(lib)) + continue; + + StaticProc *proc; + for(proc = reqSymbols;proc;proc = proc->Next) + { + if(!(proc->Call = GetSym(proc->Name)) && !proc->Optional) + { + Unload(); + break; + } + } + + if(IsLoaded()) + return true; + } + + return false; +} + +void FModule::Unload() +{ + if(handle) + { + FreeLibrary((HMODULE)handle); + handle = nullptr; + } +} + +bool FModule::Open(const char* lib) +{ +#ifdef _WIN32 + if((handle = GetModuleHandleA(lib)) != nullptr) + return true; +#else + // Loading an empty string in Linux doesn't do what we expect it to. + if(*lib == '\0') + return false; +#endif + handle = LoadLibraryA(lib); + return handle != nullptr; +} + +void *FModule::GetSym(const char* name) +{ + return (void *)GetProcAddress((HMODULE)handle, name); +} + +static std::string module_progdir("."); // current program directory used to look up dynamic libraries. Default to something harmless in case the user didn't set it. + +void FModule_SetProgDir(const char* progdir) +{ + module_progdir = progdir; +} + +const std::string& FModule_GetProgDir() +{ + return module_progdir; +} diff --git a/source/loader/i_module.h b/source/loader/i_module.h new file mode 100644 index 000000000..d270502cc --- /dev/null +++ b/source/loader/i_module.h @@ -0,0 +1,233 @@ +/* +** i_module.h +** +**--------------------------------------------------------------------------- +** Copyright 2016 Braden Obrzut +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#pragma once + +#include +#include +#include + +/* FModule Run Time Library Loader + * + * This provides an interface for loading optional dependencies or detecting + * version specific symbols at run time. These classes largely provide an + * interface for statically declaring the symbols that are going to be used + * ahead of time, thus should not be used on the stack or as part of a + * dynamically allocated object. The procedure templates take the FModule + * as a template argument largely to make such use of FModule awkward. + * + * Declared procedures register themselves with FModule and the module will not + * be considered loaded unless all required procedures can be resolved. In + * order to remove the need for boilerplate code, optional procedures do not + * enforce the requirement that the value is null checked before use. As a + * debugging aid debug builds will check that operator bool was called at some + * point, but this is just a first order sanity check. + */ + +class FModule; +class FStaticModule; + +template +class TOptProc; + +template +class TReqProc; + +template +class TStaticProc; + +class FModule +{ + template + friend class TOptProc; + template + friend class TReqProc; + + struct StaticProc + { + void *Call; + const char* Name; + StaticProc *Next; + bool Optional; + }; + + void RegisterStatic(StaticProc &proc) + { + proc.Next = reqSymbols; + reqSymbols = &proc; + } + + void *handle = nullptr; + + // Debugging aid + const char *name; + + // Since FModule is supposed to be statically allocated it is assumed that + // reqSymbols will be initialized to nullptr avoiding initialization order + // problems with declaring procedures. + StaticProc *reqSymbols; + + bool Open(const char* lib); + void *GetSym(const char* name); + +public: + template + using Opt = TOptProc; + template + using Req = TReqProc; + + FModule(const char* name) : name(name) {}; + ~FModule() { Unload(); } + + // Load a shared library using the first library name which satisfies all + // of the required symbols. + bool Load(std::initializer_list libnames); + void Unload(); + + bool IsLoaded() const { return handle != nullptr; } +}; + +// Null version of FModule which satisfies the API so the same code can be used +// for run time and compile time linking. +class FStaticModule +{ + template + friend class TStaticProc; + + const char *name; +public: + template + using Opt = TStaticProc; + template + using Req = TStaticProc; + + FStaticModule(const char* name) : name(name) {}; + + bool Load(std::initializer_list libnames) { return true; } + void Unload() {} + + bool IsLoaded() const { return true; } +}; + +// Allow FModuleMaybe to switch based on preprocessor flag. +// Use FModuleMaybe::Opt and FModuleMaybe::Req for procs. +template +struct TModuleType { using Type = FModule; }; +template<> +struct TModuleType { using Type = FStaticModule; }; + +template +using FModuleMaybe = typename TModuleType::Type; + +// ------------------------------------------------------------------------ + +template +class TOptProc +{ + FModule::StaticProc proc; +#ifndef NDEBUG + mutable bool checked = false; +#endif + + // I am not a pointer + bool operator==(void*) const; + bool operator!=(void*) const; + +public: + TOptProc(const char* function) + { + proc.Name = function; + proc.Optional = true; + Module.RegisterStatic(proc); + } + + operator Proto() const + { +#ifndef NDEBUG + assert(checked); +#endif + return (Proto)proc.Call; + } + explicit operator bool() const + { +#ifndef NDEBUG + assert(Module.IsLoaded()); + checked = true; +#endif + return proc.Call != nullptr; + } +}; + +template +class TReqProc +{ + FModule::StaticProc proc; + + // I am not a pointer + bool operator==(void*) const; + bool operator!=(void*) const; + +public: + TReqProc(const char* function) + { + proc.Name = function; + proc.Optional = false; + Module.RegisterStatic(proc); + } + + operator Proto() const + { +#ifndef NDEBUG + assert(Module.IsLoaded()); +#endif + return (Proto)proc.Call; + } + explicit operator bool() const { return true; } +}; + +template +class TStaticProc +{ + // I am not a pointer + bool operator==(void*) const; + bool operator!=(void*) const; + +public: + TStaticProc(const char* function) {} + + operator Proto() const { return Sym; } + explicit operator bool() const { return Sym != nullptr; } +}; + +void FModule_SetProgDir(const char* progdir); +const std::string& FModule_GetProgDir(); diff --git a/source/loader/test.c b/source/loader/test.c new file mode 100644 index 000000000..fb9f19173 --- /dev/null +++ b/source/loader/test.c @@ -0,0 +1,2 @@ +// This file is only here to have one module that includes the header from a pure C source, so that it immediately errors out when something incompatible is found. +#include "zmusic.h" \ No newline at end of file diff --git a/source/mididevices/driver_adlib.cpp b/source/mididevices/driver_adlib.cpp new file mode 100644 index 000000000..c1d413179 --- /dev/null +++ b/source/mididevices/driver_adlib.cpp @@ -0,0 +1,783 @@ +/* +Copyright (C) 1994-1995 Apogee Software, Ltd. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +/********************************************************************** + module: AL_MIDI.C + + author: James R. Dose + date: April 1, 1994 + + Low level routines to support General MIDI music on AdLib compatible + cards. + + (c) Copyright 1994 James R. Dose. All Rights Reserved. +**********************************************************************/ + +#include "driver_adlib.h" + +#include "_al_midi.h" +//#include "_multivc.h" +#include "../adlmidi/chips/nuked_opl3.h" +//#include "c_cvars.h" + +/* +CUSTOM_CVAR(Bool, mus_al_stereo, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + AL_Stereo = self; + AL_SetStereo(AL_Stereo); +} + +CVAR(Bool, mus_al_additivemode, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +*/ + +enum +{ + AdLibErr_Warning = -2, + AdLibErr_Error = -1, + AdLibErr_Ok = 0, +}; + +static void AL_Shutdown(void); +static int ErrorCode; + +int AdLibDrv_GetError(void) { return ErrorCode; } + +const char *AdLibDrv_ErrorString(int const ErrorNumber) +{ + const char *ErrorString; + + switch( ErrorNumber ) + { + case AdLibErr_Warning : + case AdLibErr_Error : + ErrorString = AdLibDrv_ErrorString( ErrorCode ); + break; + + case AdLibErr_Ok : + ErrorString = "AdLib ok."; + break; + + default: + ErrorString = "Unknown AdLib error."; + break; + } + + return ErrorString; +} + +#if 0 +int AdLibDrv_MIDI_Init(midifuncs * const funcs) +{ + AdLibDrv_MIDI_Shutdown(); + Bmemset(funcs, 0, sizeof(midifuncs)); + + funcs->NoteOff = AL_NoteOff; + funcs->NoteOn = AL_NoteOn; + funcs->PolyAftertouch = nullptr; + funcs->ControlChange = AL_ControlChange; + funcs->ProgramChange = AL_ProgramChange; + funcs->ChannelAftertouch = nullptr; + funcs->PitchBend = AL_SetPitchBend; + + return AdLibErr_Ok; +} +#endif + +//void AdLibDrv_MIDI_HaltPlayback(void) { MV_UnhookMusicRoutine(); } + +void AdLibDrv_MIDI_Shutdown(void) +{ + AdLibDrv_MIDI_HaltPlayback(); + AL_Shutdown(); +} + +int AdLibDrv_MIDI_StartPlayback(void (*service)(void)) +{ + AdLibDrv_MIDI_HaltPlayback(); + + //AL_Init(MV_MixRate); + //MV_HookMusicRoutine(service); + + return 0;// MIDI_Ok; +} + +void AdLibDrv_MIDI_SetTempo(int const tempo, int const division) +{ + //MV_MIDIRenderTempo = tempo * division / 60; + //MV_MIDIRenderTimer = 0; +} + +static opl3_chip chip; + +opl3_chip *AL_GetChip(void) { return &chip; } + +static constexpr uint32_t OctavePitch[MAX_OCTAVE+1] = { + OCTAVE_0, OCTAVE_1, OCTAVE_2, OCTAVE_3, OCTAVE_4, OCTAVE_5, OCTAVE_6, OCTAVE_7, +}; + +static uint32_t NoteMod12[MAX_NOTE+1]; +static uint32_t NoteDiv12[MAX_NOTE+1]; + +// Pitch table + +//static unsigned NotePitch[ FINETUNE_MAX+1 ][ 12 ] = +// { +// { C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B }, +// }; + +static constexpr uint32_t NotePitch[FINETUNE_MAX+1][12] = { + { 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287 }, + { 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x242, 0x264, 0x288 }, + { 0x158, 0x16c, 0x182, 0x199, 0x1b1, 0x1cb, 0x1e6, 0x203, 0x221, 0x243, 0x265, 0x289 }, + { 0x158, 0x16c, 0x183, 0x19a, 0x1b2, 0x1cc, 0x1e7, 0x204, 0x222, 0x244, 0x266, 0x28a }, + { 0x159, 0x16d, 0x183, 0x19a, 0x1b3, 0x1cd, 0x1e8, 0x205, 0x223, 0x245, 0x267, 0x28b }, + { 0x15a, 0x16e, 0x184, 0x19b, 0x1b3, 0x1ce, 0x1e9, 0x206, 0x224, 0x246, 0x268, 0x28c }, + { 0x15a, 0x16e, 0x185, 0x19c, 0x1b4, 0x1ce, 0x1ea, 0x207, 0x225, 0x247, 0x269, 0x28e }, + { 0x15b, 0x16f, 0x185, 0x19d, 0x1b5, 0x1cf, 0x1eb, 0x208, 0x226, 0x248, 0x26a, 0x28f }, + { 0x15b, 0x170, 0x186, 0x19d, 0x1b6, 0x1d0, 0x1ec, 0x209, 0x227, 0x249, 0x26b, 0x290 }, + { 0x15c, 0x170, 0x187, 0x19e, 0x1b7, 0x1d1, 0x1ec, 0x20a, 0x228, 0x24a, 0x26d, 0x291 }, + { 0x15d, 0x171, 0x188, 0x19f, 0x1b7, 0x1d2, 0x1ed, 0x20b, 0x229, 0x24b, 0x26e, 0x292 }, + { 0x15d, 0x172, 0x188, 0x1a0, 0x1b8, 0x1d3, 0x1ee, 0x20c, 0x22a, 0x24c, 0x26f, 0x293 }, + { 0x15e, 0x172, 0x189, 0x1a0, 0x1b9, 0x1d4, 0x1ef, 0x20d, 0x22b, 0x24d, 0x270, 0x295 }, + { 0x15f, 0x173, 0x18a, 0x1a1, 0x1ba, 0x1d4, 0x1f0, 0x20e, 0x22c, 0x24e, 0x271, 0x296 }, + { 0x15f, 0x174, 0x18a, 0x1a2, 0x1bb, 0x1d5, 0x1f1, 0x20f, 0x22d, 0x24f, 0x272, 0x297 }, + { 0x160, 0x174, 0x18b, 0x1a3, 0x1bb, 0x1d6, 0x1f2, 0x210, 0x22e, 0x250, 0x273, 0x298 }, + { 0x161, 0x175, 0x18c, 0x1a3, 0x1bc, 0x1d7, 0x1f3, 0x211, 0x22f, 0x251, 0x274, 0x299 }, + { 0x161, 0x176, 0x18c, 0x1a4, 0x1bd, 0x1d8, 0x1f4, 0x212, 0x230, 0x252, 0x276, 0x29b }, + { 0x162, 0x176, 0x18d, 0x1a5, 0x1be, 0x1d9, 0x1f5, 0x212, 0x231, 0x254, 0x277, 0x29c }, + { 0x162, 0x177, 0x18e, 0x1a6, 0x1bf, 0x1d9, 0x1f5, 0x213, 0x232, 0x255, 0x278, 0x29d }, + { 0x163, 0x178, 0x18f, 0x1a6, 0x1bf, 0x1da, 0x1f6, 0x214, 0x233, 0x256, 0x279, 0x29e }, + { 0x164, 0x179, 0x18f, 0x1a7, 0x1c0, 0x1db, 0x1f7, 0x215, 0x235, 0x257, 0x27a, 0x29f }, + { 0x164, 0x179, 0x190, 0x1a8, 0x1c1, 0x1dc, 0x1f8, 0x216, 0x236, 0x258, 0x27b, 0x2a1 }, + { 0x165, 0x17a, 0x191, 0x1a9, 0x1c2, 0x1dd, 0x1f9, 0x217, 0x237, 0x259, 0x27c, 0x2a2 }, + { 0x166, 0x17b, 0x192, 0x1aa, 0x1c3, 0x1de, 0x1fa, 0x218, 0x238, 0x25a, 0x27e, 0x2a3 }, + { 0x166, 0x17b, 0x192, 0x1aa, 0x1c3, 0x1df, 0x1fb, 0x219, 0x239, 0x25b, 0x27f, 0x2a4 }, + { 0x167, 0x17c, 0x193, 0x1ab, 0x1c4, 0x1e0, 0x1fc, 0x21a, 0x23a, 0x25c, 0x280, 0x2a6 }, + { 0x168, 0x17d, 0x194, 0x1ac, 0x1c5, 0x1e0, 0x1fd, 0x21b, 0x23b, 0x25d, 0x281, 0x2a7 }, + { 0x168, 0x17d, 0x194, 0x1ad, 0x1c6, 0x1e1, 0x1fe, 0x21c, 0x23c, 0x25e, 0x282, 0x2a8 }, + { 0x169, 0x17e, 0x195, 0x1ad, 0x1c7, 0x1e2, 0x1ff, 0x21d, 0x23d, 0x260, 0x283, 0x2a9 }, + { 0x16a, 0x17f, 0x196, 0x1ae, 0x1c8, 0x1e3, 0x1ff, 0x21e, 0x23e, 0x261, 0x284, 0x2ab }, + { 0x16a, 0x17f, 0x197, 0x1af, 0x1c8, 0x1e4, 0x200, 0x21f, 0x23f, 0x262, 0x286, 0x2ac } +}; + +// Slot numbers as a function of the voice and the operator. +// ( melodic only) + +static constexpr int slotVoice[NUMADLIBVOICES][2] = { + { 0, 3 }, // voice 0 + { 1, 4 }, // 1 + { 2, 5 }, // 2 + { 6, 9 }, // 3 + { 7, 10 }, // 4 + { 8, 11 }, // 5 + { 12, 15 }, // 6 + { 13, 16 }, // 7 + { 14, 17 }, // 8 +}; + +static int VoiceLevel[AL_NumChipSlots][2]; +static int VoiceKsl[AL_NumChipSlots][2]; + +// This table gives the offset of each slot within the chip. +// offset = fn( slot) + +static constexpr int8_t offsetSlot[AL_NumChipSlots] = { 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21 }; + +static int VoiceReserved[NUMADLIBVOICES * 2]; + +static AdLibVoice Voice[NUMADLIBVOICES * 2]; +static AdLibVoiceList Voice_Pool; + +static AdLibChannel Channel[NUMADLIBCHANNELS]; + +static int AL_LeftPort = ADLIB_PORT; +static int AL_RightPort = ADLIB_PORT; +int AL_Stereo = TRUE; +static int AL_MaxMidiChannel = 16; + +// TODO: clean up this shit... +#define OFFSET(structure, offset) (*((char **)&(structure)[offset])) + +#define LL_AddToTail(type, listhead, node) \ + LL_AddNode((char *)(node), (char **)&((listhead)->end), (char **)&((listhead)->start), (intptr_t) & ((type *)0)->prev, \ + (intptr_t) & ((type *)0)->next) + +#define LL_Remove(type, listhead, node) \ + LL_RemoveNode((char *)(node), (char **)&((listhead)->start), (char **)&((listhead)->end), (intptr_t) & ((type *)0)->next, \ + (intptr_t) & ((type *)0)->prev) + +static void LL_RemoveNode(char *item, char **head, char **tail, intptr_t next, intptr_t prev) +{ + if (OFFSET(item, prev) == nullptr) + *head = OFFSET(item, next); + else + OFFSET(OFFSET(item, prev), next) = OFFSET(item, next); + + if (OFFSET(item, next) == nullptr) + *tail = OFFSET(item, prev); + else + OFFSET(OFFSET(item, next), prev) = OFFSET(item, prev); + + OFFSET(item, next) = nullptr; + OFFSET(item, prev) = nullptr; +} + +static void LL_AddNode(char *item, char **head, char **tail, intptr_t next, intptr_t prev) +{ + OFFSET(item, prev) = nullptr; + OFFSET(item, next) = *head; + + if (*head) + OFFSET(*head, prev) = item; + else + *tail = item; + + *head = item; +} + + +static void AL_SendOutputToPort(int const port, int const reg, int const data) +{ + OPL3_WriteRegBuffered(&chip, (Bit16u)(reg + ((port & 2) << 7)), (Bit8u)data); +} + + +static void AL_SendOutput(int const voice, int const reg, int const data) +{ + int port = (voice == 0) ? AL_RightPort : AL_LeftPort; + AL_SendOutputToPort(port, reg, data); +} + + +static void AL_SetVoiceTimbre(int const voice) +{ + int const channel = Voice[voice].channel; + int const patch = (channel == 9) ? Voice[voice].key + 128 : Channel[channel].Timbre; + + if (Voice[voice].timbre == patch) + return; + + Voice[voice].timbre = patch; + + auto const timbre = &ADLIB_TimbreBank[patch]; + + int const port = Voice[voice].port; + int const voc = (voice >= NUMADLIBVOICES) ? voice - NUMADLIBVOICES : voice; + int slot = slotVoice[voc][0]; + int off = offsetSlot[slot]; + + VoiceLevel[slot][port] = 63 - (timbre->Level[0] & 0x3f); + VoiceKsl[slot][port] = timbre->Level[0] & 0xc0; + + AL_SendOutput(port, 0xA0 + voc, 0); + AL_SendOutput(port, 0xB0 + voc, 0); + + // Let voice clear the release + AL_SendOutput(port, 0x80 + off, 0xff); + + AL_SendOutput(port, 0x60 + off, timbre->Env1[0]); + AL_SendOutput(port, 0x80 + off, timbre->Env2[0]); + AL_SendOutput(port, 0x20 + off, timbre->SAVEK[0]); + AL_SendOutput(port, 0xE0 + off, timbre->Wave[0]); + + AL_SendOutput(port, 0x40 + off, timbre->Level[0]); + slot = slotVoice[voc][1]; + + AL_SendOutput(port, 0xC0 + voc, (timbre->Feedback & 0x0f) | 0x30); + + off = offsetSlot[slot]; + + VoiceLevel[slot][port] = 63 - (timbre->Level[1] & 0x3f); + VoiceKsl[slot][port] = timbre->Level[1] & 0xc0; + + AL_SendOutput(port, 0x40 + off, 63); + + // Let voice clear the release + AL_SendOutput(port, 0x80 + off, 0xff); + + AL_SendOutput(port, 0x60 + off, timbre->Env1[1]); + AL_SendOutput(port, 0x80 + off, timbre->Env2[1]); + AL_SendOutput(port, 0x20 + off, timbre->SAVEK[1]); + AL_SendOutput(port, 0xE0 + off, timbre->Wave[1]); +} + + +static void AL_SetVoiceVolume(int const voice) +{ + int const channel = Voice[voice].channel; + auto const timbre = &ADLIB_TimbreBank[Voice[voice].timbre]; + int const velocity = min(Voice[voice].velocity + timbre->Velocity, MAX_VELOCITY); + + int const voc = (voice >= NUMADLIBVOICES) ? voice - NUMADLIBVOICES : voice; + int const slot = slotVoice[voc][1]; + int const port = Voice[voice].port; + + // amplitude + auto t1 = (uint32_t)VoiceLevel[slot][port] * (velocity + 0x80); + t1 = (Channel[channel].Volume * t1) >> 15; + + uint32_t volume = t1 ^ 63; + volume |= (uint32_t)VoiceKsl[slot][port]; + + AL_SendOutput(port, 0x40 + offsetSlot[slot], volume); + + // Check if this timbre is Additive + if (timbre->Feedback & 0x01) + { + int const slot = slotVoice[voc][0]; + uint32_t t2; + + // amplitude + if (mus_al_additivemode) + t1 = (uint32_t)VoiceLevel[slot][port] * (velocity + 0x80); + + t2 = (Channel[channel].Volume * t1) >> 15; + + volume = t2 ^ 63; + volume |= (uint32_t)VoiceKsl[slot][port]; + + AL_SendOutput(port, 0x40 + offsetSlot[slot], volume); + } +} + + +static int AL_AllocVoice(void) +{ + if (Voice_Pool.start) + { + int const voice = Voice_Pool.start->num; + LL_Remove(AdLibVoice, &Voice_Pool, &Voice[voice]); + return voice; + } + + return AL_VoiceNotFound; +} + + +static int AL_GetVoice(int const channel, int const key) +{ + auto const *voice = Channel[channel].Voices.start; + + while (voice != nullptr) + { + if (voice->key == (uint32_t)key) + return voice->num; + voice = voice->next; + } + + return AL_VoiceNotFound; +} + + +static void AL_SetVoicePitch(int const voice) +{ + int const port = Voice[voice].port; + int const voc = (voice >= NUMADLIBVOICES) ? voice - NUMADLIBVOICES : voice; + int const channel = Voice[voice].channel; + + int patch, note; + + if (channel == 9) + { + patch = Voice[voice].key + 128; + note = ADLIB_TimbreBank[patch].Transpose; + } + else + { + patch = Channel[channel].Timbre; + note = Voice[voice].key + ADLIB_TimbreBank[patch].Transpose; + } + + note += Channel[channel].KeyOffset - 12; + note = clamp(note, 0, MAX_NOTE); + + int detune = Channel[channel].KeyDetune; + + int ScaleNote = NoteMod12[note]; + int Octave = NoteDiv12[note]; + + int pitch = OctavePitch[Octave] | NotePitch[detune][ScaleNote]; + + Voice[voice].pitchleft = pitch; + + pitch |= Voice[voice].status; + + AL_SendOutput(port, 0xA0 + voc, pitch); + AL_SendOutput(port, 0xB0 + voc, pitch >> 8); +} + +static void AL_SetVoicePan(int const voice) +{ + int const port = Voice[voice].port; + int const voc = (voice >= NUMADLIBVOICES) ? voice - NUMADLIBVOICES : voice; + int const channel = Voice[voice].channel; + + if (AL_Stereo) + AL_SendOutput(port, 0xD0 + voc, Channel[channel].Pan << 1); +} + + +static void AL_SetChannelVolume(int const channel, int const volume) +{ + Channel[channel].Volume = clamp(volume, 0, AL_MaxVolume); + + auto voice = Channel[channel].Voices.start; + + while (voice != nullptr) + { + AL_SetVoiceVolume(voice->num); + voice = voice->next; + } +} + + +static void AL_SetChannelPan(int const channel, int const pan) +{ + // Don't pan drum sounds + if (channel != 9) + Channel[channel].Pan = pan; + + auto voice = Channel[channel].Voices.start; + while (voice != nullptr) + { + AL_SetVoicePan(voice->num); + voice = voice->next; + } +} + + +static void AL_SetChannelDetune(int const channel, int const detune) { Channel[channel].Detune = detune; } + + +static void AL_ResetVoices(void) +{ + Voice_Pool.start = nullptr; + Voice_Pool.end = nullptr; + + int const numvoices = NUMADLIBVOICES * 2; + + for (int index = 0; index < numvoices; index++) + { + if (VoiceReserved[index] == FALSE) + { + Voice[index].num = index; + Voice[index].key = 0; + Voice[index].velocity = 0; + Voice[index].channel = -1; + Voice[index].timbre = -1; + Voice[index].port = (index < NUMADLIBVOICES) ? 0 : 1; + Voice[index].status = NOTE_OFF; + LL_AddToTail(AdLibVoice, &Voice_Pool, &Voice[index]); + } + } + + for (int index = 0; index < NUMADLIBCHANNELS; index++) + { + Channel[index] = {}; + Channel[index].Volume = AL_DefaultChannelVolume; + Channel[index].Pan = 64; + Channel[index].PitchBendRange = AL_DefaultPitchBendRange; + Channel[index].PitchBendSemiTones = AL_DefaultPitchBendRange / 100; + Channel[index].PitchBendHundreds = AL_DefaultPitchBendRange % 100; + } +} + + +static void AL_CalcPitchInfo(void) +{ + // int finetune; + // double detune; + + for (int note = 0; note <= MAX_NOTE; note++) + { + NoteMod12[note] = note % 12; + NoteDiv12[note] = note / 12; + } + + // for( finetune = 1; finetune <= FINETUNE_MAX; finetune++ ) + // { + // detune = pow( 2, ( double )finetune / ( 12.0 * FINETUNE_RANGE ) ); + // for( note = 0; note < 12; note++ ) + // { + // NotePitch[ finetune ][ note ] = ( ( double )NotePitch[ 0 ][ note ] * detune ); + // } + // } +} + + +static void AL_FlushCard(int const port) +{ + for (int i = 0; i < NUMADLIBVOICES; i++) + { + if (VoiceReserved[i]) + continue; + + auto slot1 = offsetSlot[slotVoice[i][0]]; + auto slot2 = offsetSlot[slotVoice[i][1]]; + + AL_SendOutputToPort(port, 0xA0 + i, 0); + AL_SendOutputToPort(port, 0xB0 + i, 0); + + AL_SendOutputToPort(port, 0xE0 + slot1, 0); + AL_SendOutputToPort(port, 0xE0 + slot2, 0); + + // Set the envelope to be fast and quiet + AL_SendOutputToPort(port, 0x60 + slot1, 0xff); + AL_SendOutputToPort(port, 0x60 + slot2, 0xff); + AL_SendOutputToPort(port, 0x80 + slot1, 0xff); + AL_SendOutputToPort(port, 0x80 + slot2, 0xff); + + // Maximum attenuation + AL_SendOutputToPort(port, 0x40 + slot1, 0xff); + AL_SendOutputToPort(port, 0x40 + slot2, 0xff); + } +} + + +static void AL_Reset(void) +{ + AL_SendOutputToPort(ADLIB_PORT, 1, 0x20); + AL_SendOutputToPort(ADLIB_PORT, 0x08, 0); + + // Set the values: AM Depth, VIB depth & Rhythm + AL_SendOutputToPort(ADLIB_PORT, 0xBD, 0); + + AL_SetStereo(AL_Stereo); + + AL_FlushCard(AL_LeftPort); + AL_FlushCard(AL_RightPort); +} + + +void AL_SetStereo(int const stereo) +{ + AL_SendOutputToPort(AL_RightPort, 0x5, (stereo<<1)+1); +} + + +static void AL_NoteOff(int const channel, int const key, int velocity) +{ + UNREFERENCED_PARAMETER(velocity); + + // We only play channels 1 through 10 + if (channel > AL_MaxMidiChannel) + return; + + int voice = AL_GetVoice(channel, key); + + if (voice == AL_VoiceNotFound) + return; + + Voice[voice].status = NOTE_OFF; + + int port = Voice[voice].port; + int voc = (voice >= NUMADLIBVOICES) ? voice - NUMADLIBVOICES : voice; + + AL_SendOutput(port, 0xB0 + voc, hibyte(Voice[voice].pitchleft)); + + LL_Remove(AdLibVoice, &Channel[channel].Voices, &Voice[voice]); + LL_AddToTail(AdLibVoice, &Voice_Pool, &Voice[voice]); +} + + +static void AL_NoteOn(int const channel, int const key, int const velocity) +{ + // We only play channels 1 through 10 + if (channel > AL_MaxMidiChannel) + return; + + if (velocity == 0) + { + AL_NoteOff(channel, key, velocity); + return; + } + + int voice = AL_AllocVoice(); + + if (voice == AL_VoiceNotFound) + { + if (Channel[9].Voices.start) + { + AL_NoteOff(9, Channel[9].Voices.start->key, 0); + voice = AL_AllocVoice(); + } + + if (voice == AL_VoiceNotFound) + return; + } + + Voice[voice].key = key; + Voice[voice].channel = channel; + Voice[voice].velocity = velocity; + Voice[voice].status = NOTE_ON; + + LL_AddToTail(AdLibVoice, &Channel[channel].Voices, &Voice[voice]); + + AL_SetVoiceTimbre(voice); + AL_SetVoiceVolume(voice); + AL_SetVoicePitch(voice); + AL_SetVoicePan(voice); +} + + +static inline void AL_AllNotesOff(int const channel) +{ + while (Channel[channel].Voices.start != nullptr) + AL_NoteOff(channel, Channel[channel].Voices.start->key, 0); +} + + +static void AL_ControlChange(int const channel, int const type, int const data) +{ + // We only play channels 1 through 10 + if (channel > AL_MaxMidiChannel) + return; + + switch (type) + { + case MIDI_VOLUME: + AL_SetChannelVolume(channel, data); + break; + + case MIDI_PAN: + AL_SetChannelPan(channel, data); + break; + + case MIDI_DETUNE: + AL_SetChannelDetune(channel, data); + break; + + case MIDI_ALL_NOTES_OFF: + AL_AllNotesOff(channel); + break; + + case MIDI_RESET_ALL_CONTROLLERS: + AL_ResetVoices(); + AL_SetChannelVolume(channel, AL_DefaultChannelVolume); + AL_SetChannelPan(channel, 64); + AL_SetChannelDetune(channel, 0); + break; + + case MIDI_RPN_MSB: + Channel[channel].RPN &= 0x00FF; + Channel[channel].RPN |= (data & 0xFF) << 8; + break; + + case MIDI_RPN_LSB: + Channel[channel].RPN &= 0xFF00; + Channel[channel].RPN |= data & 0xFF; + break; + + case MIDI_DATAENTRY_MSB: + if (Channel[channel].RPN == MIDI_PITCHBEND_RPN) + { + Channel[channel].PitchBendSemiTones = data; + Channel[channel].PitchBendRange = Channel[channel].PitchBendSemiTones * 100 + Channel[channel].PitchBendHundreds; + } + break; + + case MIDI_DATAENTRY_LSB: + if (Channel[channel].RPN == MIDI_PITCHBEND_RPN) + { + Channel[channel].PitchBendHundreds = data; + Channel[channel].PitchBendRange = Channel[channel].PitchBendSemiTones * 100 + Channel[channel].PitchBendHundreds; + } + break; + } +} + + +static void AL_ProgramChange(int const channel, int const patch) +{ + // We only play channels 1 through 10 + if (channel > AL_MaxMidiChannel) + return; + + Channel[channel].Timbre = patch; +} + + +static void AL_SetPitchBend(int const channel, int const lsb, int const msb) +{ + // We only play channels 1 through 10 + if (channel > AL_MaxMidiChannel) + return; + + int const pitchbend = lsb + (msb << 8); + + Channel[channel].Pitchbend = pitchbend; + + int TotalBend = pitchbend * Channel[channel].PitchBendRange; + TotalBend /= (PITCHBEND_CENTER / FINETUNE_RANGE); + + Channel[channel].KeyOffset = (int)(TotalBend / FINETUNE_RANGE); + Channel[channel].KeyOffset -= Channel[channel].PitchBendSemiTones; + + Channel[channel].KeyDetune = (uint32_t)(TotalBend % FINETUNE_RANGE); + + auto voice = Channel[channel].Voices.start; + while (voice != nullptr) + { + AL_SetVoicePitch(voice->num); + voice = voice->next; + } +} + + +static void AL_Shutdown(void) +{ + AL_ResetVoices(); + AL_Reset(); +} + + +static int AL_Init(int const rate) +{ + OPL3_Reset(&chip, rate); + + AL_LeftPort = ADLIB_PORT; + AL_RightPort = ADLIB_PORT + 2; + + AL_CalcPitchInfo(); + AL_Reset(); + AL_ResetVoices(); + + return AdLibErr_Ok; +} + + +void AL_RegisterTimbreBank(uint8_t *timbres) +{ + for (int i = 0; i < 256; i++) + { + ADLIB_TimbreBank[i].SAVEK[0] = *(timbres++); + ADLIB_TimbreBank[i].SAVEK[1] = *(timbres++); + ADLIB_TimbreBank[i].Level[0] = *(timbres++); + ADLIB_TimbreBank[i].Level[1] = *(timbres++); + ADLIB_TimbreBank[i].Env1[0] = *(timbres++); + ADLIB_TimbreBank[i].Env1[1] = *(timbres++); + ADLIB_TimbreBank[i].Env2[0] = *(timbres++); + ADLIB_TimbreBank[i].Env2[1] = *(timbres++); + ADLIB_TimbreBank[i].Wave[0] = *(timbres++); + ADLIB_TimbreBank[i].Wave[1] = *(timbres++); + ADLIB_TimbreBank[i].Feedback = *(timbres++); + ADLIB_TimbreBank[i].Transpose = *(int8_t *)(timbres++); + ADLIB_TimbreBank[i].Velocity = *(int8_t *)(timbres++); + } +} diff --git a/source/mididevices/mididevice.h b/source/mididevices/mididevice.h new file mode 100644 index 000000000..bd39fc15e --- /dev/null +++ b/source/mididevices/mididevice.h @@ -0,0 +1,160 @@ +#pragma once + +#include +#include "zmusic/midiconfig.h" +#include "zmusic/mididefs.h" + +typedef void(*MidiCallback)(void *); + +// A device that provides a WinMM-like MIDI streaming interface ------------- + + +struct MidiHeader +{ + uint8_t *lpData; + uint32_t dwBufferLength; + uint32_t dwBytesRecorded; + MidiHeader *lpNext; +}; + +class MIDIDevice +{ +public: + MIDIDevice() = default; + virtual ~MIDIDevice(); + + void SetCallback(MidiCallback callback, void* userdata) + { + Callback = callback; + CallbackData = userdata; + } + virtual int Open() = 0; + virtual void Close() = 0; + virtual bool IsOpen() const = 0; + virtual int GetTechnology() const = 0; + virtual int SetTempo(int tempo) = 0; + virtual int SetTimeDiv(int timediv) = 0; + virtual int StreamOut(MidiHeader *data) = 0; + virtual int StreamOutSync(MidiHeader *data) = 0; + virtual int Resume() = 0; + virtual void Stop() = 0; + virtual int PrepareHeader(MidiHeader *data); + virtual int UnprepareHeader(MidiHeader *data); + virtual bool FakeVolume(); + virtual bool Pause(bool paused) = 0; + virtual void InitPlayback(); + virtual bool Update(); + virtual void PrecacheInstruments(const uint16_t *instruments, int count); + virtual void ChangeSettingInt(const char *setting, int value); + virtual void ChangeSettingNum(const char *setting, double value); + virtual void ChangeSettingString(const char *setting, const char *value); + virtual std::string GetStats(); + virtual int GetDeviceType() const { return MDEV_DEFAULT; } + virtual bool CanHandleSysex() const { return true; } + virtual SoundStreamInfoEx GetStreamInfoEx() const; + +protected: + MidiCallback Callback; + void* CallbackData; +}; + +// Base class for software synthesizer MIDI output devices ------------------ + +class SoftSynthMIDIDevice : public MIDIDevice +{ + friend class MIDIWaveWriter; +public: + SoftSynthMIDIDevice(int samplerate, int minrate = 1, int maxrate = 1000000 /* something higher than any valid value */); + ~SoftSynthMIDIDevice(); + + void Close() override; + bool IsOpen() const override; + int GetTechnology() const override; + int SetTempo(int tempo) override; + int SetTimeDiv(int timediv) override; + int StreamOut(MidiHeader *data) override; + int StreamOutSync(MidiHeader *data) override; + int Resume() override; + void Stop() override; + bool Pause(bool paused) override; + + virtual int Open() override; + virtual bool ServiceStream(void* buff, int numbytes); + int GetSampleRate() const { return SampleRate; } + SoundStreamInfoEx GetStreamInfoEx() const override; + +protected: + double Tempo; + double Division; + double SamplesPerTick; + double NextTickIn; + MidiHeader *Events; + bool Started; + bool isMono = false; // only relevant for OPL. + bool isOpen = false; + uint32_t Position; + int SampleRate; + int StreamBlockSize = 2; + + virtual void CalcTickRate(); + int PlayTick(); + + virtual int OpenRenderer() = 0; + virtual void HandleEvent(int status, int parm1, int parm2) = 0; + virtual void HandleLongEvent(const uint8_t *data, int len) = 0; + virtual void ComputeOutput(float *buffer, int len) = 0; +}; + + +// Internal disk writing version of a MIDI device ------------------ + +class MIDIWaveWriter : public SoftSynthMIDIDevice +{ +public: + MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *devtouse); + //~MIDIWaveWriter(); + bool CloseFile(); + int Resume() override; + int Open() override + { + playDevice->SetCallback(Callback, CallbackData); + return playDevice->Open(); + } + int OpenRenderer() override { return playDevice->OpenRenderer(); } + void Stop() override; + void HandleEvent(int status, int parm1, int parm2) override { playDevice->HandleEvent(status, parm1, parm2); } + void HandleLongEvent(const uint8_t *data, int len) override { playDevice->HandleLongEvent(data, len); } + void ComputeOutput(float *buffer, int len) override { playDevice->ComputeOutput(buffer, len); } + int StreamOutSync(MidiHeader *data) override { return playDevice->StreamOutSync(data); } + int StreamOut(MidiHeader *data) override { return playDevice->StreamOut(data); } + int GetDeviceType() const override { return playDevice->GetDeviceType(); } + bool ServiceStream (void *buff, int numbytes) override { return playDevice->ServiceStream(buff, numbytes); } + int GetTechnology() const override { return playDevice->GetTechnology(); } + int SetTempo(int tempo) override { return playDevice->SetTempo(tempo); } + int SetTimeDiv(int timediv) override { return playDevice->SetTimeDiv(timediv); } + bool IsOpen() const override { return playDevice->IsOpen(); } + void CalcTickRate() override { playDevice->CalcTickRate(); } + +protected: + FILE *File; + SoftSynthMIDIDevice *playDevice; +}; + + +// MIDI devices + +MIDIDevice *CreateFluidSynthMIDIDevice(int samplerate, const char *Args); +MIDIDevice *CreateADLMIDIDevice(const char* args); +MIDIDevice *CreateOPNMIDIDevice(const char *args); +MIDIDevice *CreateOplMIDIDevice(const char* Args); +MIDIDevice *CreateTimidityMIDIDevice(const char* Args, int samplerate); +MIDIDevice *CreateTimidityPPMIDIDevice(const char *Args, int samplerate); +MIDIDevice *CreateWildMIDIDevice(const char *Args, int samplerate); + +#ifdef _WIN32 +MIDIDevice* CreateWinMIDIDevice(int mididevice); +#endif + +#ifdef __linux__ +MIDIDevice* CreateAlsaMIDIDevice(int mididevice); +#endif diff --git a/source/mididevices/music_adlmidi_mididevice.cpp b/source/mididevices/music_adlmidi_mididevice.cpp new file mode 100644 index 000000000..1f850e897 --- /dev/null +++ b/source/mididevices/music_adlmidi_mididevice.cpp @@ -0,0 +1,661 @@ +/* +** music_timidity_mididevice.cpp +** Provides access to TiMidity as a generic MIDI device. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include + +#include "zmusic/zmusic_internal.h" +#include "mididevice.h" + +#ifdef HAVE_ADL +#include "adlmidi.h" +#include "wopl/wopl_file.h" +#include "oplsynth/genmidi.h" + +ADLConfig adlConfig; + +class ADLMIDIDevice : public SoftSynthMIDIDevice +{ + struct ADL_MIDIPlayer *Renderer; + float OutputGainFactor; + float ConfigGainFactor; + std::string custom_bank; + std::vector genmidi; + bool use_custom_bank; + bool use_genmidi; + int last_bank; +public: + ADLMIDIDevice(const ADLConfig *config); + ~ADLMIDIDevice(); + + int OpenRenderer() override; + int GetDeviceType() const override { return MDEV_ADL; } + void ChangeSettingInt(const char *setting, int value) override; + void ChangeSettingNum(const char *setting, double value) override; + void ChangeSettingString(const char *setting, const char *value) override; + +protected: + + void HandleEvent(int status, int parm1, int parm2) override; + void HandleLongEvent(const uint8_t *data, int len) override; + void ComputeOutput(float *buffer, int len) override; + +private: + void initGain(); + int LoadCustomBank(const ADLConfig *config); + void OP2_To_WOPL(const ADLConfig *config); +}; + + +enum +{ + ME_NOTEOFF = 0x80, + ME_NOTEON = 0x90, + ME_KEYPRESSURE = 0xA0, + ME_CONTROLCHANGE = 0xB0, + ME_PROGRAM = 0xC0, + ME_CHANNELPRESSURE = 0xD0, + ME_PITCHWHEEL = 0xE0 +}; + +//========================================================================== +// +// ADLMIDIDevice Constructor +// +//========================================================================== + +ADLMIDIDevice::ADLMIDIDevice(const ADLConfig *config) + :SoftSynthMIDIDevice(44100) +{ + Renderer = adl_init(44100); // todo: make it configurable + OutputGainFactor = 3.5f; + ConfigGainFactor = 1.0f; + if (Renderer != nullptr) + { + adl_switchEmulator(Renderer, config->adl_emulator_id); + adl_setRunAtPcmRate(Renderer, config->adl_run_at_pcm_rate); + last_bank = config->adl_bank; + use_genmidi = config->adl_use_genmidi; + if (config->adl_genmidi_set) + OP2_To_WOPL(config); + if (!LoadCustomBank(config)) + adl_setBank(Renderer, config->adl_bank); + adl_setNumChips(Renderer, config->adl_chips_count); + adl_setVolumeRangeModel(Renderer, config->adl_volume_model); + adl_setChannelAllocMode(Renderer, config->adl_chan_alloc); + adl_setSoftPanEnabled(Renderer, config->adl_fullpan); + adl_setAutoArpeggio(Renderer, (int)config->adl_auto_arpeggio); + ConfigGainFactor = config->adl_gain; + initGain(); + } + else throw std::runtime_error("Failed to create ADL MIDI renderer."); +} + +//========================================================================== +// +// ADLMIDIDevice Destructor +// +//========================================================================== + +ADLMIDIDevice::~ADLMIDIDevice() +{ + Close(); + if (Renderer != nullptr) + { + adl_close(Renderer); + } +} + +//========================================================================== +// +// ADLMIDIDevice :: LoadCustomBank +// +// Loads a custom WOPL bank for libADLMIDI. Returns 1 when bank has been +// loaded, otherwise, returns 0 when custom banks are disabled or failed +// +//========================================================================== + +int ADLMIDIDevice::LoadCustomBank(const ADLConfig *config) +{ + if (config) + { + custom_bank = config->adl_custom_bank; + use_custom_bank = config->adl_use_custom_bank; + } + + const char *bankfile = custom_bank.c_str(); + if(!use_custom_bank) + return 0; + if (use_genmidi && genmidi.size()) // Try to set GENMIDI as a bank, otherwise, try regular custom bank + { + if(adl_openBankData(Renderer, genmidi.data(), (unsigned long)genmidi.size()) == 0) + return true; + } + if(!*bankfile) + return 0; + return (adl_openBankFile(Renderer, bankfile) == 0); +} + +//========================================================================== +// +// gen2ins +// +// Routin to convert a single GENMIDI instrument into WOPL format +// +//========================================================================== + +static void gen2ins(const GenMidiInstrument *genmidi, WOPLInstrument *inst, bool isDrum) +{ + uint8_t notenum = genmidi->fixed_note; + int16_t noteOffset[2]; + + inst->inst_flags = 0; + + if (genmidi->flags & 0x04) + { + inst->inst_flags |= WOPL_Ins_4op | WOPL_Ins_Pseudo4op; + } + + if (isDrum) + { + noteOffset[0] = 12; + noteOffset[1] = 12; + } + else + { + noteOffset[0] = genmidi->voices[0].base_note_offset + 12; + noteOffset[1] = genmidi->voices[1].base_note_offset + 12; + } + + if (isDrum) + { + notenum = (genmidi->flags & 0x01) ? genmidi->fixed_note : 60; + } + + while(notenum && notenum < 20) + { + notenum += 12; + noteOffset[0] -= 12; + noteOffset[1] -= 12; + } + + inst->note_offset1 = noteOffset[0]; + inst->note_offset2 = noteOffset[1]; + + inst->percussion_key_number = notenum; + inst->second_voice_detune = static_cast(static_cast(genmidi->fine_tuning) - 128); + + inst->operators[WOPL_OP_MODULATOR1].avekf_20 = genmidi->voices[0].modulator.tremolo; + inst->operators[WOPL_OP_MODULATOR1].atdec_60 = genmidi->voices[0].modulator.attack; + inst->operators[WOPL_OP_MODULATOR1].susrel_80 = genmidi->voices[0].modulator.sustain; + inst->operators[WOPL_OP_MODULATOR1].waveform_E0 = genmidi->voices[0].modulator.waveform; + inst->operators[WOPL_OP_MODULATOR1].ksl_l_40 = genmidi->voices[0].modulator.scale | genmidi->voices[0].modulator.level; + + inst->operators[WOPL_OP_CARRIER1].avekf_20 = genmidi->voices[0].carrier.tremolo; + inst->operators[WOPL_OP_CARRIER1].atdec_60 = genmidi->voices[0].carrier.attack; + inst->operators[WOPL_OP_CARRIER1].susrel_80 = genmidi->voices[0].carrier.sustain; + inst->operators[WOPL_OP_CARRIER1].waveform_E0 = genmidi->voices[0].carrier.waveform; + inst->operators[WOPL_OP_CARRIER1].ksl_l_40 = genmidi->voices[0].carrier.scale | genmidi->voices[0].carrier.level; + + inst->fb_conn1_C0 = genmidi->voices[0].feedback; + + inst->operators[WOPL_OP_MODULATOR2].avekf_20 = genmidi->voices[1].modulator.tremolo; + inst->operators[WOPL_OP_MODULATOR2].atdec_60 = genmidi->voices[1].modulator.attack; + inst->operators[WOPL_OP_MODULATOR2].susrel_80 = genmidi->voices[1].modulator.sustain; + inst->operators[WOPL_OP_MODULATOR2].waveform_E0 = genmidi->voices[1].modulator.waveform; + inst->operators[WOPL_OP_MODULATOR2].ksl_l_40 = genmidi->voices[1].modulator.scale | genmidi->voices[1].modulator.level; + + inst->operators[WOPL_OP_CARRIER2].avekf_20 = genmidi->voices[1].carrier.tremolo; + inst->operators[WOPL_OP_CARRIER2].atdec_60 = genmidi->voices[1].carrier.attack; + inst->operators[WOPL_OP_CARRIER2].susrel_80 = genmidi->voices[1].carrier.sustain; + inst->operators[WOPL_OP_CARRIER2].waveform_E0 = genmidi->voices[1].carrier.waveform; + inst->operators[WOPL_OP_CARRIER2].ksl_l_40 = genmidi->voices[1].carrier.scale | genmidi->voices[1].carrier.level; + + // FIXME: Supposed to be computed from instrument data, set just constant for a test + inst->delay_on_ms = 5000; + inst->delay_off_ms = 5000; + + inst->fb_conn2_C0 = genmidi->voices[1].feedback; +} + +//========================================================================== +// +// ADLMIDIDevice :: OP2_To_WOPL +// +// Converts the WAD's GENMIDI bank into compatible WOPL format which can be +// loaded +// +//========================================================================== + +void ADLMIDIDevice::OP2_To_WOPL(const ADLConfig *config) +{ + size_t bank_size; + genmidi.clear(); + + if (!config->adl_genmidi_set) + { + return; // No GENMIDI bank presented + } + + const GenMidiInstrument *ginst = reinterpret_cast(config->adl_genmidi_bank); + WOPLFile *wopl_bank = WOPL_Init(1, 1); + + wopl_bank->volume_model = (ADLMIDI_VolumeModel_DMX - 1); + + for(size_t i = 0; i < 128; ++i) + { + wopl_bank->banks_melodic[0].ins[i].inst_flags = WOPL_Ins_IsBlank; + wopl_bank->banks_percussive[0].ins[i].inst_flags = WOPL_Ins_IsBlank; + } + + for (size_t i = 0; i < GENMIDI_NUM_INSTRS; ++i, ++ginst) + { + gen2ins(ginst, &wopl_bank->banks_melodic->ins[i], false); + } + + for (size_t i = GENMIDI_FIST_PERCUSSION; i < GENMIDI_FIST_PERCUSSION + GENMIDI_NUM_PERCUSSION; ++i, ++ginst) + { + gen2ins(ginst, &wopl_bank->banks_percussive->ins[i], true); + } + + bank_size = WOPL_CalculateBankFileSize(wopl_bank, 0); + + genmidi.resize(bank_size); + int ret = WOPL_SaveBankToMem(wopl_bank, genmidi.data(), genmidi.size(), 0, 0); + + if (ret != 0) + { + genmidi.clear(); + + const char *err = "Unknown"; + switch (ret) + { + case WOPL_ERR_UNEXPECTED_ENDING: + err = "Unexpected buffer ending"; + break; + default: + break; + } + + ZMusic_Printf(ZMUSIC_MSG_ERROR, "Failed to convert GENMIDI bank to WOPL: %s.\n", err); + } + + WOPL_Free(wopl_bank); +} + + +//========================================================================== +// +// ADLMIDIDevice :: Open +// +// Returns 0 on success. +// +//========================================================================== + +int ADLMIDIDevice::OpenRenderer() +{ + adl_rt_resetState(Renderer); + return 0; +} + +//========================================================================== +// +// ADLMIDIDevice :: ChangeSettingInt +// +// Changes an integer setting. +// +//========================================================================== + +void ADLMIDIDevice::ChangeSettingInt(const char *setting, int value) +{ + if (Renderer == nullptr || strncmp(setting, "libadl.", 7)) + { + return; + } + setting += 7; + + if (strcmp(setting, "volumemodel") == 0) + { + adl_setVolumeRangeModel(Renderer, value); + initGain(); // Gain should be recomputed after changing this + } + else if (strcmp(setting, "chanalloc") == 0) + { + adl_setChannelAllocMode(Renderer, value); + } + else if (strcmp(setting, "emulator") == 0) + { + adl_switchEmulator(Renderer, value); + } + else if (strcmp(setting, "numchips") == 0) + { + adl_setNumChips(Renderer, value); + } + else if (strcmp(setting, "fullpan") == 0) + { + adl_setSoftPanEnabled(Renderer, value); + } + else if (strcmp(setting, "runatpcmrate") == 0) + { + adl_setRunAtPcmRate(Renderer, value); + } + else if (strcmp(setting, "autoarpeggio") == 0) + { + adl_setAutoArpeggio(Renderer, value); + } + else if (strcmp(setting, "usecustombank") == 0) + { + bool bvalue = (value != 0); + bool update = (bvalue != use_custom_bank); + use_custom_bank = bvalue; + if (update) + { + if (!LoadCustomBank(nullptr)) + { + adl_setBank(Renderer, last_bank); + initGain(); + } + } + } + else if (strcmp(setting, "usegenmidi") == 0) + { + bool bvalue = (value != 0); + bool update = (bvalue != use_genmidi); + use_genmidi = bvalue; + if (update && !genmidi.empty()) + { + if (!LoadCustomBank(nullptr)) + { + adl_setBank(Renderer, last_bank); + initGain(); + } + } + } + else if (strcmp(setting, "banknum") == 0) + { + bool update = (value != last_bank); + last_bank = value; + if (update) + { + adl_setBank(Renderer, last_bank); + initGain(); + } + } +} + +//========================================================================== +// +// ADLMIDIDevice :: ChangeSettingNum +// +// Changes a numeric setting. +// +//========================================================================== + +void ADLMIDIDevice::ChangeSettingNum(const char *setting, double value) +{ + if (Renderer == nullptr || strncmp(setting, "libadl.", 7)) + { + return; + } + setting += 7; + + if (strcmp(setting, "gain") == 0) + { + ConfigGainFactor = value; + initGain(); + } +} + +//========================================================================== +// +// ADLMIDIDevice :: ChangeSettingString +// +// Changes a string setting. +// +//========================================================================== + +void ADLMIDIDevice::ChangeSettingString(const char *setting, const char *value) +{ + if (Renderer == nullptr || strncmp(setting, "libadl.", 7)) + { + return; + } + setting += 7; + + if (strcmp(setting, "custombank") == 0) + { + custom_bank = value; + if (use_custom_bank) + { + if (!LoadCustomBank(nullptr)) + adl_setBank(Renderer, last_bank); + initGain(); + } + } +} + +//========================================================================== +// +// ADLMIDIDevice :: HandleEvent +// +//========================================================================== + +void ADLMIDIDevice::HandleEvent(int status, int parm1, int parm2) +{ + int command = status & 0xF0; + int chan = status & 0x0F; + + switch (command) + { + case ME_NOTEON: + adl_rt_noteOn(Renderer, chan, parm1, parm2); + break; + + case ME_NOTEOFF: + adl_rt_noteOff(Renderer, chan, parm1); + break; + + case ME_KEYPRESSURE: + adl_rt_noteAfterTouch(Renderer, chan, parm1, parm2); + break; + + case ME_CONTROLCHANGE: + adl_rt_controllerChange(Renderer, chan, parm1, parm2); + break; + + case ME_PROGRAM: + adl_rt_patchChange(Renderer, chan, parm1); + break; + + case ME_CHANNELPRESSURE: + adl_rt_channelAfterTouch(Renderer, chan, parm1); + break; + + case ME_PITCHWHEEL: + adl_rt_pitchBendML(Renderer, chan, parm2, parm1); + break; + } +} + +//========================================================================== +// +// ADLMIDIDevice :: HandleLongEvent +// +//========================================================================== + +void ADLMIDIDevice::HandleLongEvent(const uint8_t *data, int len) +{ + adl_rt_systemExclusive(Renderer, data, len); +} + +static const ADLMIDI_AudioFormat audio_output_format = +{ + ADLMIDI_SampleType_F32, + sizeof(float), + 2 * sizeof(float) +}; + +//========================================================================== +// +// ADLMIDIDevice :: ComputeOutput +// +//========================================================================== + +void ADLMIDIDevice::ComputeOutput(float *buffer, int len) +{ + ADL_UInt8* left = reinterpret_cast(buffer); + ADL_UInt8* right = reinterpret_cast(buffer + 1); + auto result = adl_generateFormat(Renderer, len * 2, left, right, &audio_output_format); + for(int i=0; i < result; i++) + { + buffer[i] *= OutputGainFactor; + } +} + +//========================================================================== +// +// ADLMIDIDevice :: initGain +// +//========================================================================== + +void ADLMIDIDevice::initGain() +{ + if (Renderer == NULL) + { + return; + } + + OutputGainFactor = 3.5f; + + // TODO: Please tune the factor for each volume model to avoid too loud or too silent sounding + switch (adl_getVolumeRangeModel(Renderer)) + { + // Louder models + case ADLMIDI_VolumeModel_Generic: + case ADLMIDI_VolumeModel_9X: + case ADLMIDI_VolumeModel_9X_GENERIC_FM: + OutputGainFactor = 2.0f; + break; + // Middle volume models + case ADLMIDI_VolumeModel_HMI: + case ADLMIDI_VolumeModel_HMI_OLD: + OutputGainFactor = 2.5f; + break; + default: + // Quite models + case ADLMIDI_VolumeModel_DMX: + case ADLMIDI_VolumeModel_DMX_Fixed: + case ADLMIDI_VolumeModel_APOGEE: + case ADLMIDI_VolumeModel_APOGEE_Fixed: + case ADLMIDI_VolumeModel_AIL: + OutputGainFactor = 3.5f; + break; + // Quiter models + case ADLMIDI_VolumeModel_NativeOPL3: + OutputGainFactor = 3.8f; + break; + } + + OutputGainFactor *= ConfigGainFactor; +} + +//========================================================================== +// +// +// +//========================================================================== + +extern ADLConfig adlConfig; + +MIDIDevice *CreateADLMIDIDevice(const char *Args) +{ + ADLConfig config = adlConfig; + + const char* bank = Args && *Args ? Args : adlConfig.adl_use_custom_bank ? adlConfig.adl_custom_bank.c_str() : nullptr; + if (bank && *bank) + { + if (*bank >= '0' && *bank <= '9') + { + // Args specify a bank by index. + config.adl_bank = (int)strtoll(bank, nullptr, 10); + config.adl_use_custom_bank = false; + } + else + { + const char* info; + if (musicCallbacks.PathForSoundfont) + { + info = musicCallbacks.PathForSoundfont(bank, SF_WOPL); + } + else + { + info = bank; + } + if (info == nullptr) + { + config.adl_custom_bank = ""; + config.adl_use_custom_bank = false; + } + else + { + config.adl_custom_bank = info; + config.adl_use_custom_bank = true; + } + } + } + return new ADLMIDIDevice(&config); +} + +DLL_EXPORT int ZMusic_GetADLBanks(const char* const** pNames) +{ + if (pNames) *pNames = adl_getBankNames(); + return adl_getBanksCount(); +} + +#else +MIDIDevice* CreateADLMIDIDevice(const char* Args) +{ + throw std::runtime_error("ADL device not supported in this configuration"); +} + +DLL_EXPORT int ZMusic_GetADLBanks(const char* const** pNames) +{ + // The export needs to be there even if non-functional. + return 0; +} + +#endif + diff --git a/source/mididevices/music_alsa_mididevice.cpp b/source/mididevices/music_alsa_mididevice.cpp new file mode 100644 index 000000000..5aa7b7deb --- /dev/null +++ b/source/mididevices/music_alsa_mididevice.cpp @@ -0,0 +1,497 @@ +/* +** Provides an ALSA implementation of a MIDI output device. +** +**--------------------------------------------------------------------------- +** Copyright 2008-2010 Randy Heit +** Copyright 2020 Petr Mrazek +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#if defined __linux__ && defined HAVE_SYSTEM_MIDI + +#include +#include +#include + +#include "mididevice.h" +#include "zmusic/m_swap.h" +#include "zmusic/mus2midi.h" +#include "zmusic_internal.h" + +#include "music_alsa_state.h" +#include + +namespace { + +enum class EventType { + Null, + Action +}; + +struct EventState { + int ticks = 0; + snd_seq_event_t data; + int size_of = 0; + + void Clear() { + ticks = 0; + snd_seq_ev_clear(&data); + size_of = 0; + } +}; + +class AlsaMIDIDevice : public MIDIDevice +{ +public: + AlsaMIDIDevice(int dev_id); + ~AlsaMIDIDevice(); + int Open() override; + void Close() override; + bool IsOpen() const override; + int GetTechnology() const override; + int SetTempo(int tempo) override; + int SetTimeDiv(int timediv) override; + int StreamOut(MidiHeader *data) override; + int StreamOutSync(MidiHeader *data) override; + int Resume() override; + void Stop() override; + + bool FakeVolume() override { + // Not sure if we even can control the volume this way with Alsa, so make it fake. + return true; + }; + + bool Pause(bool paused) override; + void InitPlayback() override; + bool Update() override; + void PrecacheInstruments(const uint16_t *instruments, int count) override {} + + bool CanHandleSysex() const override + { + // Assume we can, let Alsa sort it out. We do not truly have full control. + return true; + } + + void SendStopEvents(); + void SetExit(bool exit); + bool WaitForExit(std::chrono::microseconds usec, snd_seq_queue_status_t * status); + EventType PullEvent(EventState & state); + void PumpEvents(); + + +protected: + AlsaSequencer &sequencer; + + MidiHeader *Events = nullptr; + bool Started = false; + uint32_t Position = 0; + + const static int IntendedPortId = 0; + bool Connected = false; + int PortId = -1; + int QueueId = -1; + + int DestinationClientId; + int DestinationPortId; + int Technology; + + int Tempo = 480000; + int TimeDiv = 480; + + std::thread PlayerThread; + bool Exit = false; + std::mutex ExitLock; + std::condition_variable ExitCond; +}; + +} + +AlsaMIDIDevice::AlsaMIDIDevice(int dev_id) : sequencer(AlsaSequencer::Get()) +{ + auto & internalDevices = sequencer.GetInternalDevices(); + auto & device = internalDevices.at(dev_id); + DestinationClientId = device.ClientID; + DestinationPortId = device.PortNumber; + Technology = device.GetDeviceClass(); +} + +AlsaMIDIDevice::~AlsaMIDIDevice() +{ + Close(); +} + +int AlsaMIDIDevice::Open() +{ + if (!sequencer.IsOpen()) { + return 1; + } + + if(PortId < 0) + { + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca(&pinfo); + + snd_seq_port_info_set_port(pinfo, IntendedPortId); + snd_seq_port_info_set_port_specified(pinfo, 1); + + snd_seq_port_info_set_name(pinfo, "ZMusic Program Music"); + + snd_seq_port_info_set_capability(pinfo, 0); + snd_seq_port_info_set_type(pinfo, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION); + + int err = 0; + err = snd_seq_create_port(sequencer.handle, pinfo); + PortId = IntendedPortId; + } + + if (QueueId < 0) + { + QueueId = snd_seq_alloc_named_queue(sequencer.handle, "ZMusic Program Queue"); + } + + if (!Connected) { + Connected = (snd_seq_connect_to(sequencer.handle, PortId, DestinationClientId, DestinationPortId) == 0); + } + return 0; +} + +void AlsaMIDIDevice::Close() +{ + if(Connected) { + snd_seq_disconnect_to(sequencer.handle, PortId, DestinationClientId, DestinationPortId); + Connected = false; + } + if(QueueId >= 0) { + snd_seq_free_queue(sequencer.handle, QueueId); + QueueId = -1; + } + if(PortId >= 0) { + snd_seq_delete_port(sequencer.handle, PortId); + PortId = -1; + } +} + +bool AlsaMIDIDevice::IsOpen() const +{ + return Connected; +} + +int AlsaMIDIDevice::GetTechnology() const +{ + return Technology; +} + +int AlsaMIDIDevice::SetTempo(int tempo) +{ + Tempo = tempo; + return 0; +} + +int AlsaMIDIDevice::SetTimeDiv(int timediv) +{ + TimeDiv = timediv; + return 0; +} + +EventType AlsaMIDIDevice::PullEvent(EventState & state) { + state.Clear(); + + if(!Events) { + Callback(CallbackData); + if(!Events) { + return EventType::Null; + } + } + if (Position >= Events->dwBytesRecorded) + { + Events = Events->lpNext; + Position = 0; + + if (Callback != NULL) + { + Callback(CallbackData); + } + if(!Events) { + return EventType::Null; + } + } + + uint32_t *event = (uint32_t *)(Events->lpData + Position); + state.ticks = event[0]; + + // Advance to next event. + if (event[2] < 0x80000000) + { // Short message + state.size_of = 12; + } + else + { // Long message + state.size_of = 12 + ((MEVENT_EVENTPARM(event[2]) + 3) & ~3); + } + + if (MEVENT_EVENTTYPE(event[2]) == MEVENT_TEMPO) { + int tempo = MEVENT_EVENTPARM(event[2]); + if(Tempo != tempo) { + Tempo = tempo; + snd_seq_change_queue_tempo(sequencer.handle, QueueId, Tempo, &state.data); + return EventType::Action; + } + } + else if (MEVENT_EVENTTYPE(event[2]) == MEVENT_LONGMSG) { + // SysEx messages... + uint8_t * data = (uint8_t *)&event[3]; + int len = MEVENT_EVENTPARM(event[2]); + if (len > 1 && (data[0] == 0xF0 || data[0] == 0xF7)) + { + snd_seq_ev_set_sysex(&state.data, len, (void *)data); + return EventType::Action; + } + } + else if (MEVENT_EVENTTYPE(event[2]) == 0) { + // Short MIDI event + int command = event[2] & 0xF0; + int channel = event[2] & 0x0F; + int parm1 = (event[2] >> 8) & 0x7f; + int parm2 = (event[2] >> 16) & 0x7f; + switch (command) + { + case MIDI_NOTEOFF: + snd_seq_ev_set_noteoff(&state.data, channel, parm1, parm2); + return EventType::Action; + + case MIDI_NOTEON: + snd_seq_ev_set_noteon(&state.data, channel, parm1, parm2); + return EventType::Action; + + case MIDI_POLYPRESS: + snd_seq_ev_set_keypress(&state.data, channel, parm1, parm2); + return EventType::Action; + + case MIDI_CTRLCHANGE: + snd_seq_ev_set_controller(&state.data, channel, parm1, parm2); + return EventType::Action; + + case MIDI_PRGMCHANGE: + snd_seq_ev_set_pgmchange(&state.data, channel, parm1); + return EventType::Action; + + case MIDI_CHANPRESS: + snd_seq_ev_set_chanpress(&state.data, channel, parm1); + return EventType::Action; + + case MIDI_PITCHBEND: { + long bend = ((long)parm1 + (long)(parm2 << 7)) - 0x2000; + snd_seq_ev_set_pitchbend(&state.data, channel, bend); + return EventType::Action; + } + + default: + break; + } + } + // We didn't really recognize the event, treat it as a NOP + state.data.type = SND_SEQ_EVENT_NONE; + snd_seq_ev_set_fixed(&state.data); + return EventType::Action; +} + +void AlsaMIDIDevice::SetExit(bool exit) { + std::unique_lock lock(ExitLock); + if(exit != Exit) { + Exit = exit; + ExitCond.notify_all(); + } +} + +bool AlsaMIDIDevice::WaitForExit(std::chrono::microseconds usec, snd_seq_queue_status_t * status) { + std::unique_lock lock(ExitLock); + if(Exit) { + return true; + } + ExitCond.wait_for(lock, usec); + if(Exit) { + return true; + } + snd_seq_get_queue_status(sequencer.handle, QueueId, status); + return false; +} + +/* + * Pumps events from the input to the output in a worker thread. + * It tries to keep the amount of events (time-wise) in the ALSA sequencer queue to be between 40 and 80ms by sleeping where necessary. + * This means Alsa can play them safely without running out of things to do, and we have good control over the events themselves (volume, pause, etc.). + */ +void AlsaMIDIDevice::PumpEvents() { + const std::chrono::microseconds pump_step(40000); + + // TODO: fill in error handling throughout this. + snd_seq_queue_tempo_t *tempo; + snd_seq_queue_tempo_alloca(&tempo); + snd_seq_queue_tempo_set_tempo(tempo, Tempo); + snd_seq_queue_tempo_set_ppq(tempo, TimeDiv); + snd_seq_set_queue_tempo(sequencer.handle, QueueId, tempo); + + snd_seq_start_queue(sequencer.handle, QueueId, NULL); + snd_seq_drain_output(sequencer.handle); + + int buffer_ticks = 0; + EventState event; + + snd_seq_queue_status_t *status; + snd_seq_queue_status_malloc(&status); + + while (true) { + auto type = PullEvent(event); + // if we reach the end of events, await our doom at a steady rate while looking for more events + if(type == EventType::Null) { + if(WaitForExit(pump_step, status)) { + break; + } + continue; + } + + // Figure out if we should sleep (the event is too far in the future for us to care), and for how long + int next_event_tick = buffer_ticks + event.ticks; + int queue_tick = snd_seq_queue_status_get_tick_time(status); + int tick_delta = next_event_tick - queue_tick; + auto usecs = std::chrono::microseconds(tick_delta * Tempo / TimeDiv); + auto schedule_time = std::max(std::chrono::microseconds(0), usecs - pump_step); + if(schedule_time >= pump_step) { + if(WaitForExit(schedule_time, status)) { + break; + } + continue; + } + if (tick_delta < 0) { + ZMusic_Printf(ZMUSIC_MSG_ERROR, "Alsa sequencer underrun: %d ticks!\n", tick_delta); + } + + // We found an event worthy of sending to the sequencer + snd_seq_ev_set_source(&event.data, PortId); + snd_seq_ev_set_subs(&event.data); + snd_seq_ev_schedule_tick(&event.data, QueueId, false, buffer_ticks + event.ticks); + int result = snd_seq_event_output(sequencer.handle, &event.data); + if(result < 0) { + ZMusic_Printf(ZMUSIC_MSG_ERROR, "Alsa sequencer did not accept event: error %d!\n", result); + if(WaitForExit(pump_step, status)) { + break; + } + continue; + } + buffer_ticks += event.ticks; + Position += event.size_of; + snd_seq_drain_output(sequencer.handle); + } + + snd_seq_queue_status_free(status); + snd_seq_drop_output(sequencer.handle); + // FIXME: the event source should give use these, but it doesn't. + { + for (int channel = 0; channel < 16; ++channel) + { + snd_seq_event_t ev; + snd_seq_ev_clear(&ev); + snd_seq_ev_set_source(&ev, PortId); + snd_seq_ev_set_subs(&ev); + snd_seq_ev_schedule_tick(&ev, QueueId, true, 0); + snd_seq_ev_set_controller(&ev, channel, MIDI_CTL_ALL_NOTES_OFF, 0); + snd_seq_event_output(sequencer.handle, &ev); + snd_seq_ev_set_controller(&ev, channel, MIDI_CTL_RESET_CONTROLLERS, 0); + snd_seq_event_output(sequencer.handle, &ev); + } + snd_seq_drain_output(sequencer.handle); + snd_seq_sync_output_queue(sequencer.handle); + } + snd_seq_stop_queue(sequencer.handle, QueueId, NULL); + snd_seq_drain_output(sequencer.handle); +} + + +int AlsaMIDIDevice::Resume() +{ + if(!Connected) { + return 1; + } + SetExit(false); + PlayerThread = std::thread(&AlsaMIDIDevice::PumpEvents, this); + return 0; +} + +void AlsaMIDIDevice::InitPlayback() +{ + SetExit(false); +} + +void AlsaMIDIDevice::Stop() +{ + SetExit(true); + PlayerThread.join(); +} + +bool AlsaMIDIDevice::Pause(bool paused) +{ + // TODO: implement + return false; +} + + +int AlsaMIDIDevice::StreamOut(MidiHeader *header) +{ + header->lpNext = NULL; + if (Events == NULL) + { + Events = header; + Position = 0; + } + else + { + MidiHeader **p; + + for (p = &Events; *p != NULL; p = &(*p)->lpNext) + { } + *p = header; + } + return 0; +} + + +int AlsaMIDIDevice::StreamOutSync(MidiHeader *header) +{ + return StreamOut(header); +} + +bool AlsaMIDIDevice::Update() +{ + return true; +} + +MIDIDevice *CreateAlsaMIDIDevice(int mididevice) +{ + return new AlsaMIDIDevice(mididevice); +} +#endif diff --git a/source/mididevices/music_alsa_state.cpp b/source/mididevices/music_alsa_state.cpp new file mode 100644 index 000000000..c7bb8dda6 --- /dev/null +++ b/source/mididevices/music_alsa_state.cpp @@ -0,0 +1,169 @@ +/* +** Provides an implementation of an ALSA sequencer wrapper +** +**--------------------------------------------------------------------------- +** Copyright 2020 Petr Mrazek +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ +#include "music_alsa_state.h" + +#if defined __linux__ && defined HAVE_SYSTEM_MIDI + +#include +#include + +EMidiDeviceClass MidiOutDeviceInternal::GetDeviceClass() const +{ + if (type & SND_SEQ_PORT_TYPE_SYNTH) + return MIDIDEV_FMSYNTH; + if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE)) + return MIDIDEV_SYNTH; + if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION)) + return MIDIDEV_MIDIPORT; + // assume FM synth otherwise + return MIDIDEV_FMSYNTH; +} + +AlsaSequencer & AlsaSequencer::Get() { + static AlsaSequencer sequencer; + return sequencer; +} + +AlsaSequencer::AlsaSequencer() { + Open(); +} + +AlsaSequencer::~AlsaSequencer() { + Close(); +} + +bool AlsaSequencer::Open() { + error = snd_seq_open(&handle, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK); + if(error) { + return false; + } + error = snd_seq_set_client_name(handle, "ZMusic Program"); + if(error) { + snd_seq_close(handle); + handle = nullptr; + return false; + } + OurId = snd_seq_client_id(handle); + if (OurId < 0) { + error = OurId; + OurId = -1; + + snd_seq_close(handle); + handle = nullptr; + return false; + } + + return true; +} + + +void AlsaSequencer::Close() { + if(!handle) { + return; + } + snd_seq_close(handle); + handle = nullptr; +} + +namespace { + +bool filter(snd_seq_port_info_t *pinfo) +{ + int capability = snd_seq_port_info_get_capability(pinfo); + if(capability & SND_SEQ_PORT_CAP_NO_EXPORT) { + return false; + } + const int writable = (SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE); + if((capability & writable) != writable) { + return false; + } + // TODO: filter based on type here? maybe? + // int type = snd_seq_port_info_get_type(pinfo); + return true; +} +} + +int AlsaSequencer::EnumerateDevices() { + if(!handle) { + return 0; + } + + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + + snd_seq_client_info_alloca(&cinfo); + snd_seq_port_info_alloca(&pinfo); + + int index = 0; + + // enumerate clients + snd_seq_client_info_set_client(cinfo, -1); + while (snd_seq_query_next_client(handle, cinfo) >= 0) { + snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); + + int clientID = snd_seq_client_info_get_client(cinfo); + + snd_seq_port_info_set_port(pinfo, -1); + // enumerate ports + while (snd_seq_query_next_port(handle, pinfo) >= 0) { + if (!filter(pinfo)) { + continue; + } + internalDevices.emplace_back(); + + auto & itemInternal = internalDevices.back(); + itemInternal.ID = index++; + const char *name = snd_seq_port_info_get_name(pinfo); + int portNumber = snd_seq_port_info_get_port(pinfo); + if(!name) { + std::ostringstream out; + out << "MIDI Port " << clientID << ":" << portNumber; + itemInternal.Name = out.str(); + } + else { + itemInternal.Name = name; + } + itemInternal.ClientID = clientID; + itemInternal.PortNumber = portNumber; + itemInternal.type = snd_seq_port_info_get_type(pinfo); + } + } + return index; +} + +const std::vector & AlsaSequencer::GetInternalDevices() +{ + return internalDevices; +} + +#endif diff --git a/source/mididevices/music_alsa_state.h b/source/mididevices/music_alsa_state.h new file mode 100644 index 000000000..78e847513 --- /dev/null +++ b/source/mididevices/music_alsa_state.h @@ -0,0 +1,80 @@ +/* +** Provides an implementation of an ALSA sequencer wrapper +** +**--------------------------------------------------------------------------- +** Copyright 2008-2010 Randy Heit +** Copyright 2020 Petr Mrazek +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#pragma once + +#if defined __linux__ && defined HAVE_SYSTEM_MIDI + +#include "zmusic/zmusic_internal.h" +#include +#include +typedef struct _snd_seq snd_seq_t; + +// FIXME: make not visible from outside +struct MidiOutDeviceInternal { + std::string Name; + int ID = -1; + int ClientID = -1; + int PortNumber = -1; + unsigned int type = 0; + EMidiDeviceClass GetDeviceClass() const; +}; + +// NOTE: the sequencer state is shared between actually playing MIDI music and device enumeration, therefore we keep it around. +class AlsaSequencer { +private: + AlsaSequencer(); + ~AlsaSequencer(); + +public: + static AlsaSequencer &Get(); + bool Open(); + void Close(); + bool IsOpen() const { + return nullptr != handle; + } + + int EnumerateDevices(); + const std::vector &GetInternalDevices(); + + snd_seq_t *handle = nullptr; + + int OurId = -1; + int error = -1; + +private: + std::vector internalDevices; +}; + +#endif diff --git a/source/mididevices/music_base_mididevice.cpp b/source/mididevices/music_base_mididevice.cpp new file mode 100644 index 000000000..dcf856ab4 --- /dev/null +++ b/source/mididevices/music_base_mididevice.cpp @@ -0,0 +1,182 @@ +/* +** music_midistream.cpp +** Implements base class for MIDI and MUS streaming. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + + +#include "mididevice.h" + + +//========================================================================== +// +// MIDIDevice stubs. +// +//========================================================================== + +MIDIDevice::~MIDIDevice() +{ +} + +//========================================================================== +// +// MIDIDevice :: PrecacheInstruments +// +// The MIDIStreamer calls this method between device open and the first +// buffered stream with a list of instruments known to be used by the song. +// If the device can benefit from preloading the instruments, it can do so +// now. +// +// Each entry is packed as follows: +// Bits 0- 6: Instrument number +// Bits 7-13: Bank number +// Bit 14: Select drum set if 1, tone bank if 0 +// +//========================================================================== + +void MIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count) +{ +} + +//========================================================================== +// +// MIDIDevice :: PrepareHeader +// +// Wrapper for MCI's midiOutPrepareHeader. +// +//========================================================================== + +int MIDIDevice::PrepareHeader(MidiHeader *header) +{ + return 0; +} + +//========================================================================== +// +// MIDIDevice :: UnprepareHeader +// +// Wrapper for MCI's midiOutUnprepareHeader. +// +//========================================================================== + +int MIDIDevice::UnprepareHeader(MidiHeader *header) +{ + return 0; +} + +//========================================================================== +// +// MIDIDevice :: FakeVolume +// +// Since most implementations render as a normal stream, their volume is +// controlled through the GSnd interface, not here. +// +//========================================================================== + +bool MIDIDevice::FakeVolume() +{ + return false; +} + +//========================================================================== +// +// +// +//========================================================================== + +void MIDIDevice::InitPlayback() +{ +} + +//========================================================================== +// +// +// +//========================================================================== + +bool MIDIDevice::Update() +{ + return true; +} + +//========================================================================== +// +// MIDIDevice :: ChangeSettingInt +// +//========================================================================== + +void MIDIDevice::ChangeSettingInt(const char *setting, int value) +{ +} + +//========================================================================== +// +// MIDIDevice :: ChangeSettingNum +// +//========================================================================== + +void MIDIDevice::ChangeSettingNum(const char *setting, double value) +{ +} + +//========================================================================== +// +// MIDIDevice :: ChangeSettingString +// +//========================================================================== + +void MIDIDevice::ChangeSettingString(const char *setting, const char *value) +{ +} + +//========================================================================== +// +// MIDIDevice :: GetStats +// +//========================================================================== + +std::string MIDIDevice::GetStats() +{ + return "This MIDI device does not have any stats."; +} + +//========================================================================== +// +// MIDIDevice :: GetStreamInfoEx +// +//========================================================================== + +SoundStreamInfoEx MIDIDevice::GetStreamInfoEx() const +{ + return {}; // i.e. do not use streaming. +} diff --git a/source/mididevices/music_fluidsynth_mididevice.cpp b/source/mididevices/music_fluidsynth_mididevice.cpp new file mode 100644 index 000000000..eb50148ad --- /dev/null +++ b/source/mididevices/music_fluidsynth_mididevice.cpp @@ -0,0 +1,525 @@ +/* +** music_fluidsynth_mididevice.cpp +** Provides access to FluidSynth as a generic MIDI device. +** +**--------------------------------------------------------------------------- +** Copyright 2010 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include +#include +#include "zmusic/zmusic_internal.h" +#include "mididevice.h" +#include "zmusic/mus2midi.h" +#include "loader/i_module.h" + +// FluidSynth implementation of a MIDI device ------------------------------- + +FluidConfig fluidConfig; + +#include "../thirdparty/fluidsynth/include/fluidsynth.h" + +class FluidSynthMIDIDevice : public SoftSynthMIDIDevice +{ +public: + FluidSynthMIDIDevice(int samplerate, std::vector &config); + ~FluidSynthMIDIDevice(); + + int OpenRenderer() override; + std::string GetStats() override; + void ChangeSettingInt(const char *setting, int value) override; + void ChangeSettingNum(const char *setting, double value) override; + void ChangeSettingString(const char *setting, const char *value) override; + int GetDeviceType() const override { return MDEV_FLUIDSYNTH; } + +protected: + void HandleEvent(int status, int parm1, int parm2) override; + void HandleLongEvent(const uint8_t *data, int len) override; + void ComputeOutput(float *buffer, int len) override; + int LoadPatchSets(const std::vector& config); + + fluid_settings_t *FluidSettings; + fluid_synth_t *FluidSynth; + + // Possible results returned by fluid_settings_...() functions + // Initial values are for FluidSynth 2.x + int FluidSettingsResultOk = FLUID_OK; + int FluidSettingsResultFailed = FLUID_FAILED; + +}; + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +const char *BaseFileSearch(const char *file, const char *ext, bool lookfirstinprogdir = false); + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// FluidSynthMIDIDevice Constructor +// +//========================================================================== + +FluidSynthMIDIDevice::FluidSynthMIDIDevice(int samplerate, std::vector &config) + : SoftSynthMIDIDevice(samplerate <= 0? fluidConfig.fluid_samplerate : samplerate, 22050, 96000) +{ + StreamBlockSize = 4; + + FluidSynth = NULL; + FluidSettings = NULL; + + FluidSettings = new_fluid_settings(); + if (FluidSettings == NULL) + { + throw std::runtime_error("Failed to create FluidSettings.\n"); + } + fluid_settings_setint(FluidSettings, "synth.dynamic-sample-loading", 1); + fluid_settings_setnum(FluidSettings, "synth.sample-rate", SampleRate); + fluid_settings_setnum(FluidSettings, "synth.gain", fluidConfig.fluid_gain); + fluid_settings_setint(FluidSettings, "synth.reverb.active", fluidConfig.fluid_reverb); + fluid_settings_setint(FluidSettings, "synth.chorus.active", fluidConfig.fluid_chorus); + fluid_settings_setint(FluidSettings, "synth.polyphony", fluidConfig.fluid_voices); + fluid_settings_setint(FluidSettings, "synth.cpu-cores", fluidConfig.fluid_threads); + FluidSynth = new_fluid_synth(FluidSettings); + if (FluidSynth == NULL) + { + delete_fluid_settings(FluidSettings); + throw std::runtime_error("Failed to create FluidSynth.\n"); + } + fluid_synth_set_interp_method(FluidSynth, -1, fluidConfig.fluid_interp); + fluid_synth_set_reverb(FluidSynth, fluidConfig.fluid_reverb_roomsize, fluidConfig.fluid_reverb_damping, + fluidConfig.fluid_reverb_width, fluidConfig.fluid_reverb_level); + fluid_synth_set_chorus(FluidSynth, fluidConfig.fluid_chorus_voices, fluidConfig.fluid_chorus_level, + fluidConfig.fluid_chorus_speed, fluidConfig.fluid_chorus_depth, fluidConfig.fluid_chorus_type); + + // try loading a patch set that got specified with $mididevice. + + if (LoadPatchSets(config)) + { + return; + } + + delete_fluid_synth(FluidSynth); + delete_fluid_settings(FluidSettings); + FluidSynth = nullptr; + FluidSettings = nullptr; + throw std::runtime_error("Failed to load any MIDI patches.\n"); + +} + + +//========================================================================== +// +// FluidSynthMIDIDevice Destructor +// +//========================================================================== + +FluidSynthMIDIDevice::~FluidSynthMIDIDevice() +{ + Close(); + if (FluidSynth != NULL) + { + delete_fluid_synth(FluidSynth); + } + if (FluidSettings != NULL) + { + delete_fluid_settings(FluidSettings); + } +} + +//========================================================================== +// +// FluidSynthMIDIDevice :: Open +// +// Returns 0 on success. +// +//========================================================================== + +int FluidSynthMIDIDevice::OpenRenderer() +{ + // Send MIDI system reset command (big red 'panic' button), turns off notes, resets controllers and restores initial basic channel configuration. + //fluid_synth_system_reset(FluidSynth); + return 0; +} + +//========================================================================== +// +// FluidSynthMIDIDevice :: HandleEvent +// +// Translates a MIDI event into FluidSynth calls. +// +//========================================================================== + +void FluidSynthMIDIDevice::HandleEvent(int status, int parm1, int parm2) +{ + int command = status & 0xF0; + int channel = status & 0x0F; + + switch (command) + { + case MIDI_NOTEOFF: + fluid_synth_noteoff(FluidSynth, channel, parm1); + break; + + case MIDI_NOTEON: + fluid_synth_noteon(FluidSynth, channel, parm1, parm2); + break; + + case MIDI_POLYPRESS: + break; + + case MIDI_CTRLCHANGE: + fluid_synth_cc(FluidSynth, channel, parm1, parm2); + break; + + case MIDI_PRGMCHANGE: + fluid_synth_program_change(FluidSynth, channel, parm1); + break; + + case MIDI_CHANPRESS: + fluid_synth_channel_pressure(FluidSynth, channel, parm1); + break; + + case MIDI_PITCHBEND: + fluid_synth_pitch_bend(FluidSynth, channel, (parm1 & 0x7f) | ((parm2 & 0x7f) << 7)); + break; + } +} + +//========================================================================== +// +// FluidSynthMIDIDevice :: HandleLongEvent +// +// Handle SysEx messages. +// +//========================================================================== + +void FluidSynthMIDIDevice::HandleLongEvent(const uint8_t *data, int len) +{ + constexpr int excludedByteCount = 2; // 0xF0 (first byte) and 0xF7 (last byte) are not given to FluidSynth. + if (len > excludedByteCount && data[0] == 0xF0 && data[len - 1] == 0xF7) + { + fluid_synth_sysex(FluidSynth, (const char *)data + 1, len - excludedByteCount, NULL, NULL, NULL, 0); + } +} + +//========================================================================== +// +// FluidSynthMIDIDevice :: ComputeOutput +// +//========================================================================== + +void FluidSynthMIDIDevice::ComputeOutput(float *buffer, int len) +{ + fluid_synth_write_float(FluidSynth, len, + buffer, 0, 2, + buffer, 1, 2); +} + +//========================================================================== +// +// FluidSynthMIDIDevice :: LoadPatchSets +// +//========================================================================== + +int FluidSynthMIDIDevice::LoadPatchSets(const std::vector &config) +{ + int count = 0; + for (auto& file : config) + { + if (FLUID_FAILED != fluid_synth_sfload(FluidSynth, file.c_str(), count == 0)) + { + ZMusic_Printf(ZMUSIC_MSG_DEBUG, "Loaded patch set %s.\n", file.c_str()); + count++; + } + else + { + ZMusic_Printf(ZMUSIC_MSG_ERROR, "Failed to load patch set %s.\n", file.c_str()); + } + } + return count; +} + +//========================================================================== +// +// FluidSynthMIDIDevice :: ChangeSettingInt +// +// Changes an integer setting. +// +//========================================================================== + +void FluidSynthMIDIDevice::ChangeSettingInt(const char *setting, int value) +{ + if (FluidSynth == nullptr || FluidSettings == nullptr || strncmp(setting, "fluidsynth.", 11)) + { + return; + } + setting += 11; + + if (strcmp(setting, "synth.interpolation") == 0) + { + if (FLUID_OK != fluid_synth_set_interp_method(FluidSynth, -1, value)) + { + ZMusic_Printf(ZMUSIC_MSG_ERROR, "Setting interpolation method %d failed.\n", value); + } + } + else if (strcmp(setting, "synth.polyphony") == 0) + { + if (FLUID_OK != fluid_synth_set_polyphony(FluidSynth, value)) + { + ZMusic_Printf(ZMUSIC_MSG_ERROR, "Setting polyphony to %d failed.\n", value); + } + } + else if (FluidSettingsResultFailed == fluid_settings_setint(FluidSettings, setting, value)) + { + ZMusic_Printf(ZMUSIC_MSG_ERROR, "Failed to set %s to %d.\n", setting, value); + } + // fluid_settings_setint succeeded; update these settings in the running synth, too + else if (strcmp(setting, "synth.reverb.active") == 0) + { + fluid_synth_set_reverb_on(FluidSynth, value); + } + else if (strcmp(setting, "synth.chorus.active") == 0) + { + fluid_synth_set_chorus_on(FluidSynth, value); + } +} + +//========================================================================== +// +// FluidSynthMIDIDevice :: ChangeSettingNum +// +// Changes a numeric setting. +// +//========================================================================== + +void FluidSynthMIDIDevice::ChangeSettingNum(const char *setting, double value) +{ + if (FluidSynth == nullptr || FluidSettings == nullptr || strncmp(setting, "fluidsynth.", 11)) + { + return; + } + setting += 11; + + if (strcmp(setting, "z.reverb") == 0) + { + fluid_synth_set_reverb(FluidSynth, fluidConfig.fluid_reverb_roomsize, fluidConfig.fluid_reverb_damping, fluidConfig.fluid_reverb_width, fluidConfig.fluid_reverb_level); + } + else if (strcmp(setting, "z.chorus") == 0) + { + fluid_synth_set_chorus(FluidSynth, fluidConfig.fluid_chorus_voices, fluidConfig.fluid_chorus_level, fluidConfig.fluid_chorus_speed, fluidConfig.fluid_chorus_depth, fluidConfig.fluid_chorus_type); + } + else if (FluidSettingsResultFailed == fluid_settings_setnum(FluidSettings, setting, value)) + { + ZMusic_Printf(ZMUSIC_MSG_ERROR, "Failed to set %s to %g.\n", setting, value); + } + +} + +//========================================================================== +// +// FluidSynthMIDIDevice :: ChangeSettingString +// +// Changes a string setting. +// +//========================================================================== + +void FluidSynthMIDIDevice::ChangeSettingString(const char *setting, const char *value) +{ + if (FluidSynth == nullptr || FluidSettings == nullptr || strncmp(setting, "fluidsynth.", 11)) + { + return; + } + setting += 11; + + if (FluidSettingsResultFailed == fluid_settings_setstr(FluidSettings, setting, value)) + { + ZMusic_Printf(ZMUSIC_MSG_ERROR, "Failed to set %s to %s.\n", setting, value); + } +} + +//========================================================================== +// +// FluidSynthMIDIDevice :: GetStats +// +//========================================================================== + +std::string FluidSynthMIDIDevice::GetStats() +{ + if (FluidSynth == NULL || FluidSettings == NULL) + { + return "FluidSynth is invalid"; + } + + int polyphony = fluid_synth_get_polyphony(FluidSynth); + int voices = fluid_synth_get_active_voice_count(FluidSynth); + double load = fluid_synth_get_cpu_load(FluidSynth); + int chorus, reverb, maxpoly; + fluid_settings_getint(FluidSettings, "synth.chorus.active", &chorus); + fluid_settings_getint(FluidSettings, "synth.reverb.active", &reverb); + fluid_settings_getint(FluidSettings, "synth.polyphony", &maxpoly); + + char out[100]; + snprintf(out, 100,"Voices: %3d/%3d(%3d) %6.2f%% CPU Reverb: %3s Chorus: %3s", + voices, polyphony, maxpoly, load, reverb ? "yes" : "no", chorus ? "yes" : "no"); + return out; +} + +// +// sndfile +// +//========================================================================== + + +#ifdef _WIN32 +// do this without including windows.h for this one single prototype +extern "C" unsigned __stdcall GetSystemDirectoryA(char* lpBuffer, unsigned uSize); +#endif // _WIN32 + +void Fluid_SetupConfig(const char* patches, std::vector &patch_paths, bool systemfallback) +{ + if (*patches == 0) patches = fluidConfig.fluid_patchset.c_str(); + + //Resolve the paths here, the renderer will only get a final list of file names. + + if (musicCallbacks.PathForSoundfont) + { + auto info = musicCallbacks.PathForSoundfont(patches, SF_SF2); + if (info) patches = info; + } + + int count; + char* wpatches = strdup(patches); + char* tok; +#ifdef _WIN32 + const char* const delim = ";"; +#else + const char* const delim = ":"; +#endif + + if (wpatches != NULL) + { + tok = strtok(wpatches, delim); + count = 0; + while (tok != NULL) + { + std::string path; +#ifdef _WIN32 + // If the path does not contain any path separators, automatically + // prepend $PROGDIR to the path. + if (strcspn(tok, ":/\\") == strlen(tok)) + { + path = FModule_GetProgDir() + "/" + tok; + } + else +#endif + { + path = tok; + } + if (musicCallbacks.NicePath) + path = musicCallbacks.NicePath(path.c_str()); + + if (MusicIO::fileExists(path.c_str())) + { + patch_paths.push_back(path); + } + else + { + ZMusic_Printf(ZMUSIC_MSG_ERROR, "Could not find patch set %s.\n", tok); + } + tok = strtok(NULL, delim); + } + free(wpatches); + if (patch_paths.size() > 0) return; + } + + if (systemfallback) + { + // The following will only be used if no soundfont at all is provided, i.e. even the standard one coming with GZDoom is missing. +#ifdef __unix__ + // This is the standard location on Ubuntu. + Fluid_SetupConfig("/usr/share/sounds/sf2/FluidR3_GS.sf2:/usr/share/sounds/sf2/FluidR3_GM.sf2", patch_paths, false); +#endif +#ifdef _WIN32 + // On Windows, look for the 4 megabyte patch set installed by Creative's drivers as a default. + char sysdir[260 + sizeof("\\CT4MGM.SF2")]; + uint32_t filepart; + if (0 != (filepart = GetSystemDirectoryA(sysdir, 260))) + { + strcat(sysdir, "\\CT4MGM.SF2"); + if (MusicIO::fileExists(sysdir)) + { + patch_paths.push_back(sysdir); + return; + } + // Try again with CT2MGM.SF2 + sysdir[filepart + 3] = '2'; + if (MusicIO::fileExists(sysdir)) + { + patch_paths.push_back(sysdir); + return; + } + } + +#endif + + } +} + +//========================================================================== +// +// +// +//========================================================================== + +MIDIDevice *CreateFluidSynthMIDIDevice(int samplerate, const char *Args) +{ + std::vector fluid_patchset; + + Fluid_SetupConfig(Args, fluid_patchset, true); + return new FluidSynthMIDIDevice(samplerate, fluid_patchset); +} diff --git a/source/mididevices/music_opl_mididevice.cpp b/source/mididevices/music_opl_mididevice.cpp new file mode 100644 index 000000000..c463579fd --- /dev/null +++ b/source/mididevices/music_opl_mididevice.cpp @@ -0,0 +1,369 @@ +/* +** music_opl_mididevice.cpp +** Provides an emulated OPL implementation of a MIDI output device. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include "zmusic/zmusic_internal.h" +#include "mididevice.h" +#include "zmusic/mus2midi.h" + +#ifdef HAVE_OPL +#include "oplsynth/opl.h" +#include "oplsynth/opl_mus_player.h" + +// MACROS ------------------------------------------------------------------ + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +OPLConfig oplConfig; + +// OPL implementation of a MIDI output device ------------------------------- + +class OPLMIDIDevice : public SoftSynthMIDIDevice, protected OPLmusicBlock +{ + float OutputGainFactor; +public: + OPLMIDIDevice(int core); + int OpenRenderer() override; + void Close() override; + int GetTechnology() const override; + std::string GetStats() override; + +protected: + void CalcTickRate() override; + int PlayTick() override; + void HandleEvent(int status, int parm1, int parm2) override; + void HandleLongEvent(const uint8_t *data, int len) override; + void ComputeOutput(float *buffer, int len) override; + bool ServiceStream(void *buff, int numbytes) override; + int GetDeviceType() const override { return MDEV_OPL; } + void ChangeSettingNum(const char *setting, double value) override; +}; + + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// OPLMIDIDevice Contructor +// +//========================================================================== + +OPLMIDIDevice::OPLMIDIDevice(int core) + : SoftSynthMIDIDevice((int)OPL_SAMPLE_RATE), OPLmusicBlock(core, oplConfig.numchips) +{ + FullPan = oplConfig.fullpan; + memcpy(OPLinstruments, oplConfig.OPLinstruments, sizeof(OPLinstruments)); + OutputGainFactor = oplConfig.gain; + StreamBlockSize = 14; +} + +//========================================================================== +// +// OPLMIDIDevice :: Open +// +// Returns 0 on success. +// +//========================================================================== + +int OPLMIDIDevice::OpenRenderer() +{ + if (io == NULL || 0 == (NumChips = io->Init(currentCore, NumChips, FullPan, true))) + { + return 1; + } + isMono = !FullPan && !io->IsOPL3; + stopAllVoices(); + resetAllControllers(100); + return 0; +} + +//========================================================================== +// +// OPLMIDIDevice :: Close +// +//========================================================================== + +void OPLMIDIDevice::Close() +{ + SoftSynthMIDIDevice::Close(); + io->Reset(); +} + +//========================================================================== +// +// OPLMIDIDevice :: GetTechnology +// +//========================================================================== + +int OPLMIDIDevice::GetTechnology() const +{ + return MIDIDEV_FMSYNTH; +} + +//========================================================================== +// +// OPLMIDIDevice :: CalcTickRate +// +// Tempo is the number of microseconds per quarter note. +// Division is the number of ticks per quarter note. +// +//========================================================================== + +void OPLMIDIDevice::CalcTickRate() +{ + SoftSynthMIDIDevice::CalcTickRate(); + io->SetClockRate(OPLmusicBlock::SamplesPerTick = SoftSynthMIDIDevice::SamplesPerTick); +} + +//========================================================================== +// +// OPLMIDIDevice :: PlayTick +// +// We derive from two base classes that both define PlayTick(), so we need +// to be unambiguous about which one to use. +// +//========================================================================== + +int OPLMIDIDevice::PlayTick() +{ + return SoftSynthMIDIDevice::PlayTick(); +} + +//========================================================================== +// +// OPLMIDIDevice :: HandleEvent +// +// Processes a normal MIDI event. +// +//========================================================================== + +void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2) +{ + int command = status & 0xF0; + int channel = status & 0x0F; + + // Swap voices 9 and 15, because their roles are reversed + // in MUS and MIDI formats. + if (channel == 9) + { + channel = 15; + } + else if (channel == 15) + { + channel = 9; + } + + switch (command) + { + case MIDI_NOTEOFF: + playingcount--; + noteOff(channel, parm1); + break; + + case MIDI_NOTEON: + playingcount++; + noteOn(channel, parm1, parm2); + break; + + case MIDI_POLYPRESS: + //DEBUGOUT("Unhandled note aftertouch: Channel %d, note %d, value %d\n", channel, parm1, parm2); + break; + + case MIDI_CTRLCHANGE: + switch (parm1) + { + // some controllers here get passed on but are not handled by the player. + //case 0: changeBank(channel, parm2); break; + case 1: changeModulation(channel, parm2); break; + case 6: changeExtended(channel, ctrlDataEntryHi, parm2); break; + case 7: changeVolume(channel, parm2, false); break; + case 10: changePanning(channel, parm2); break; + case 11: changeVolume(channel, parm2, true); break; + case 38: changeExtended(channel, ctrlDataEntryLo, parm2); break; + case 64: changeSustain(channel, parm2); break; + //case 67: changeSoftPedal(channel, parm2); break; + //case 91: changeReverb(channel, parm2); break; + //case 93: changeChorus(channel, parm2); break; + case 98: changeExtended(channel, ctrlNRPNLo, parm2); break; + case 99: changeExtended(channel, ctrlNRPNHi, parm2); break; + case 100: changeExtended(channel, ctrlRPNLo, parm2); break; + case 101: changeExtended(channel, ctrlRPNHi, parm2); break; + case 120: allNotesOff(channel, parm2); break; + case 121: resetControllers(channel, 100); break; + case 123: notesOff(channel, parm2); break; + //case 126: changeMono(channel, parm2); break; + //case 127: changePoly(channel, parm2); break; + default: + //DEBUGOUT("Unhandled controller: Channel %d, controller %d, value %d\n", channel, parm1, parm2); + break; + } + break; + + case MIDI_PRGMCHANGE: + programChange(channel, parm1); + break; + + case MIDI_CHANPRESS: + //DEBUGOUT("Unhandled channel aftertouch: Channel %d, value %d\n", channel, parm1, 0); + break; + + case MIDI_PITCHBEND: + changePitch(channel, parm1, parm2); + break; + } +} + +//========================================================================== +// +// OPLMIDIDevice :: HandleLongEvent +// +//========================================================================== + +void OPLMIDIDevice::HandleLongEvent(const uint8_t *data, int len) +{ +} + +//========================================================================== +// +// OPLMIDIDevice :: ComputeOutput +// +// We override ServiceStream, so this function is never actually called. +// +//========================================================================== + +void OPLMIDIDevice::ComputeOutput(float *buffer, int len) +{ +} + +//========================================================================== +// +// OPLMIDIDevice :: ServiceStream +// +//========================================================================== + +bool OPLMIDIDevice::ServiceStream(void *buff, int numbytes) +{ + float *samples = (float *)buff; + bool ret = OPLmusicBlock::ServiceStream(buff, numbytes); + int numsamples = numbytes / sizeof(float); + for(int i=0; i < numsamples; i++) + { + samples[i] *= OutputGainFactor; + } + return ret; +} + +//========================================================================== +// +// OPLMIDIDevice :: ChangeSettingNum +// +// Changes a numeric setting. +// +//========================================================================== + +void OPLMIDIDevice::ChangeSettingNum(const char *setting, double value) +{ + if (strncmp(setting, "oplemu.", 7)) + { + return; + } + setting += 7; + + if (strcmp(setting, "gain") == 0) + { + OutputGainFactor = value; + } +} + +//========================================================================== +// +// OPLMIDIDevice :: GetStats +// +//========================================================================== + +std::string OPLMIDIDevice::GetStats() +{ + std::string out; + for (uint32_t i = 0; i < io->NumChannels; ++i) + { + char star = '*'; + if (voices[i].index == ~0u) + { + star = '/'; + } + else if (voices[i].sustained) + { + star = '-'; + } + else if (voices[i].current_instr_voice == &voices[i].current_instr->voices[1]) + { + star = '\\'; + } + else + { + star = '+'; + } + out += star; + } + return out; +} + + +MIDIDevice* CreateOplMIDIDevice(const char *Args) +{ + if (!oplConfig.genmidiset) throw std::runtime_error("Cannot play OPL without GENMIDI data"); + int core = oplConfig.core; + if (Args != NULL && *Args >= '0' && *Args < '4') core = *Args - '0'; + return new OPLMIDIDevice(core); +} + +#else +MIDIDevice* CreateOplMIDIDevice(const char* Args) +{ + throw std::runtime_error("OPL device not supported in this configuration"); +} +#endif diff --git a/source/mididevices/music_opnmidi_mididevice.cpp b/source/mididevices/music_opnmidi_mididevice.cpp new file mode 100644 index 000000000..f4793cd23 --- /dev/null +++ b/source/mididevices/music_opnmidi_mididevice.cpp @@ -0,0 +1,427 @@ +/* +** music_opnmidi_mididevice.cpp +** Provides access to libOPNMIDI as a generic MIDI device. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include "mididevice.h" +#include "zmusic/zmusic_internal.h" + +#ifdef HAVE_OPN +#include "opnmidi.h" + +OpnConfig opnConfig; + +class OPNMIDIDevice : public SoftSynthMIDIDevice +{ + struct OPN2_MIDIPlayer *Renderer; + float OutputGainFactor; + std::vector default_bank; + std::string custom_bank; + bool use_custom_bank; + +public: + OPNMIDIDevice(const OpnConfig *config); + ~OPNMIDIDevice(); + + int OpenRenderer() override; + int GetDeviceType() const override { return MDEV_OPN; } + void ChangeSettingInt(const char *setting, int value) override; + void ChangeSettingNum(const char *setting, double value) override; + void ChangeSettingString(const char *setting, const char *value) override; + +protected: + void HandleEvent(int status, int parm1, int parm2) override; + void HandleLongEvent(const uint8_t *data, int len) override; + void ComputeOutput(float *buffer, int len) override; + +private: + int LoadCustomBank(const OpnConfig *config); + void LoadDefaultBank(); +}; + + +enum +{ + ME_NOTEOFF = 0x80, + ME_NOTEON = 0x90, + ME_KEYPRESSURE = 0xA0, + ME_CONTROLCHANGE = 0xB0, + ME_PROGRAM = 0xC0, + ME_CHANNELPRESSURE = 0xD0, + ME_PITCHWHEEL = 0xE0 +}; + + +//========================================================================== +// +// OPNMIDIDevice Constructor +// +//========================================================================== +#include "data/xg.h" + +OPNMIDIDevice::OPNMIDIDevice(const OpnConfig *config) + :SoftSynthMIDIDevice(44100) +{ + Renderer = opn2_init(44100); // todo: make it configurable + OutputGainFactor = 4.0f; + + if (Renderer != nullptr) + { + default_bank = config->default_bank; + + if (!LoadCustomBank(config)) + LoadDefaultBank(); + + OutputGainFactor *= config->opn_gain; + + opn2_switchEmulator(Renderer, (int)config->opn_emulator_id); + opn2_setRunAtPcmRate(Renderer, (int)config->opn_run_at_pcm_rate); + opn2_setNumChips(Renderer, config->opn_chips_count); + opn2_setVolumeRangeModel(Renderer, config->opn_volume_model); + opn2_setChannelAllocMode(Renderer, config->opn_chan_alloc); + opn2_setSoftPanEnabled(Renderer, (int)config->opn_fullpan); + opn2_setAutoArpeggio(Renderer, (int)config->opn_auto_arpeggio); + } + else + { + throw std::runtime_error("Unable to create OPN renderer."); + } +} + +//========================================================================== +// +// OPNMIDIDevice Destructor +// +//========================================================================== + +OPNMIDIDevice::~OPNMIDIDevice() +{ + Close(); + if (Renderer != nullptr) + { + opn2_close(Renderer); + } +} + +//========================================================================== +// +// OPNMIDIDevice :: LoadCustomBank +// +// Loads a custom WOPN bank for libOPNMIDI. Returns 1 when bank has been +// loaded, otherwise, returns 0 when custom banks are disabled or failed +// +//========================================================================== + + +int OPNMIDIDevice::LoadCustomBank(const OpnConfig *config) +{ + if (config) + { + custom_bank = config->opn_custom_bank; + use_custom_bank = config->opn_use_custom_bank; + } + + const char *bankfile = custom_bank.c_str(); + if(!use_custom_bank) + return 0; + if(!*bankfile) + return 0; + return (opn2_openBankFile(Renderer, bankfile) == 0); +} + +//========================================================================== +// +// OPNMIDIDevice :: LoadDefaultBank +// +// Loads default bank for libOPNMIDI +// +//========================================================================== + +void OPNMIDIDevice::LoadDefaultBank() +{ + if (Renderer == nullptr) + { + return; + } + + if(default_bank.size() == 0) + { + opn2_openBankData(Renderer, xg_default, sizeof(xg_default)); + } + else opn2_openBankData(Renderer, default_bank.data(), (long)default_bank.size()); +} + +//========================================================================== +// +// OPNMIDIDevice :: Open +// +// Returns 0 on success. +// +//========================================================================== + +int OPNMIDIDevice::OpenRenderer() +{ + opn2_rt_resetState(Renderer); + return 0; +} + +//========================================================================== +// +// OPNMIDIDevice :: ChangeSettingInt +// +// Changes an integer setting. +// +//========================================================================== + +void OPNMIDIDevice::ChangeSettingInt(const char *setting, int value) +{ + if (Renderer == nullptr || strncmp(setting, "libopn.", 7)) + { + return; + } + setting += 7; + + if (strcmp(setting, "volumemodel") == 0) + { + opn2_setVolumeRangeModel(Renderer, value); + } + else if (strcmp(setting, "chanalloc") == 0) + { + opn2_setChannelAllocMode(Renderer, value); + } + else if (strcmp(setting, "emulator") == 0) + { + opn2_switchEmulator(Renderer, value); + } + else if (strcmp(setting, "numchips") == 0) + { + opn2_setNumChips(Renderer, value); + } + else if (strcmp(setting, "fullpan") == 0) + { + opn2_setSoftPanEnabled(Renderer, value); + } + else if (strcmp(setting, "runatpcmrate") == 0) + { + opn2_setRunAtPcmRate(Renderer, value); + } + else if (strcmp(setting, "autoarpeggio") == 0) + { + opn2_setAutoArpeggio(Renderer, value); + } + else if (strcmp(setting, "usecustombank") == 0) + { + bool bvalue = (value != 0); + bool update = (bvalue != use_custom_bank); + use_custom_bank = bvalue; + if (update) + { + if (!LoadCustomBank(nullptr)) + LoadDefaultBank(); + } + } +} + +//========================================================================== +// +// OPNMIDIDevice :: ChangeSettingNum +// +// Changes a numeric setting. +// +//========================================================================== + +void OPNMIDIDevice::ChangeSettingNum(const char *setting, double value) +{ + if (Renderer == nullptr || strncmp(setting, "libopn.", 7)) + { + return; + } + setting += 7; + + if (strcmp(setting, "gain") == 0) + { + OutputGainFactor = 4.0f * value; + } +} + +//========================================================================== +// +// OPNMIDIDevice :: ChangeSettingString +// +// Changes a string setting. +// +//========================================================================== + +void OPNMIDIDevice::ChangeSettingString(const char *setting, const char *value) +{ + if (Renderer == nullptr || strncmp(setting, "libopn.", 7)) + { + return; + } + setting += 7; + + if (strcmp(setting, "custombank") == 0) + { + custom_bank = value; + if (use_custom_bank) + { + if (!LoadCustomBank(nullptr)) + LoadDefaultBank(); + } + } +} + +//========================================================================== +// +// OPNMIDIDevice :: HandleEvent +// +//========================================================================== + +void OPNMIDIDevice::HandleEvent(int status, int parm1, int parm2) +{ + int command = status & 0xF0; + int chan = status & 0x0F; + + switch (command) + { + case ME_NOTEON: + opn2_rt_noteOn(Renderer, chan, parm1, parm2); + break; + + case ME_NOTEOFF: + opn2_rt_noteOff(Renderer, chan, parm1); + break; + + case ME_KEYPRESSURE: + opn2_rt_noteAfterTouch(Renderer, chan, parm1, parm2); + break; + + case ME_CONTROLCHANGE: + opn2_rt_controllerChange(Renderer, chan, parm1, parm2); + break; + + case ME_PROGRAM: + opn2_rt_patchChange(Renderer, chan, parm1); + break; + + case ME_CHANNELPRESSURE: + opn2_rt_channelAfterTouch(Renderer, chan, parm1); + break; + + case ME_PITCHWHEEL: + opn2_rt_pitchBendML(Renderer, chan, parm2, parm1); + break; + } +} + +//========================================================================== +// +// OPNMIDIDevice :: HandleLongEvent +// +//========================================================================== + +void OPNMIDIDevice::HandleLongEvent(const uint8_t *data, int len) +{ + opn2_rt_systemExclusive(Renderer, data, len); +} + +static const OPNMIDI_AudioFormat audio_output_format = +{ + OPNMIDI_SampleType_F32, + sizeof(float), + 2 * sizeof(float) +}; + +//========================================================================== +// +// OPNMIDIDevice :: ComputeOutput +// +//========================================================================== + +void OPNMIDIDevice::ComputeOutput(float *buffer, int len) +{ + OPN2_UInt8* left = reinterpret_cast(buffer); + OPN2_UInt8* right = reinterpret_cast(buffer + 1); + auto result = opn2_generateFormat(Renderer, len * 2, left, right, &audio_output_format); + for(int i=0; i < result; i++) + { + buffer[i] *= OutputGainFactor; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +MIDIDevice *CreateOPNMIDIDevice(const char *Args) +{ + OpnConfig config = opnConfig; + + const char* bank = Args && *Args ? Args : opnConfig.opn_use_custom_bank ? opnConfig.opn_custom_bank.c_str() : nullptr; + if (bank && *bank) + { + const char* info; + if (musicCallbacks.PathForSoundfont) + { + info = musicCallbacks.PathForSoundfont(bank, SF_WOPN); + } + else + { + info = bank; + } + + if(info == nullptr) + { + config.opn_custom_bank = ""; + config.opn_use_custom_bank = false; + } + else + { + config.opn_custom_bank = info; + config.opn_use_custom_bank = true; + } + } + + return new OPNMIDIDevice(&config); +} + +#else +MIDIDevice* CreateOPNMIDIDevice(const char* Args) +{ + throw std::runtime_error("OPN device not supported in this configuration"); +} +#endif diff --git a/source/mididevices/music_softsynth_mididevice.cpp b/source/mididevices/music_softsynth_mididevice.cpp new file mode 100644 index 000000000..8d8d69dd8 --- /dev/null +++ b/source/mididevices/music_softsynth_mididevice.cpp @@ -0,0 +1,419 @@ +/* +** music_softsynth_mididevice.cpp +** Common base class for software synthesis MIDI devices. +** +**--------------------------------------------------------------------------- +** Copyright 2008-2010 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include +#include "mididevice.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +//CVAR(Bool, synth_watch, false, 0) + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// SoftSynthMIDIDevice Constructor +// +//========================================================================== + +SoftSynthMIDIDevice::SoftSynthMIDIDevice(int samplerate, int minrate, int maxrate) +{ + Tempo = 0; + Division = 0; + Events = NULL; + Started = false; + SampleRate = samplerate; + if (SampleRate < minrate || SampleRate > maxrate) SampleRate = 44100; +} + +//========================================================================== +// +// SoftSynthMIDIDevice Destructor +// +//========================================================================== + +SoftSynthMIDIDevice::~SoftSynthMIDIDevice() +{ + Close(); +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: GetStreamInfoEx +// +//========================================================================== + +SoundStreamInfoEx SoftSynthMIDIDevice::GetStreamInfoEx() const +{ + int chunksize = (SampleRate / StreamBlockSize) * 4; + if (!isMono) + { + chunksize *= 2; + } + return { chunksize, SampleRate, SampleType_Float32, + isMono ? ChannelConfig_Mono : ChannelConfig_Stereo }; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: Open +// +//========================================================================== + +int SoftSynthMIDIDevice::Open() +{ + Tempo = 500000; + Division = 100; + CalcTickRate(); + isOpen = true; + return OpenRenderer(); +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: Close +// +//========================================================================== + +void SoftSynthMIDIDevice::Close() +{ + Started = false; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: IsOpen +// +//========================================================================== + +bool SoftSynthMIDIDevice::IsOpen() const +{ + return isOpen; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: GetTechnology +// +//========================================================================== + +int SoftSynthMIDIDevice::GetTechnology() const +{ + return MIDIDEV_SWSYNTH; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: SetTempo +// +//========================================================================== + +int SoftSynthMIDIDevice::SetTempo(int tempo) +{ + Tempo = tempo; + CalcTickRate(); + return 0; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: SetTimeDiv +// +//========================================================================== + +int SoftSynthMIDIDevice::SetTimeDiv(int timediv) +{ + Division = timediv; + CalcTickRate(); + return 0; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: CalcTickRate +// +// Tempo is the number of microseconds per quarter note. +// Division is the number of ticks per quarter note. +// +//========================================================================== + +void SoftSynthMIDIDevice::CalcTickRate() +{ + SamplesPerTick = SampleRate / (1000000.0 / Tempo) / Division; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: Resume +// +//========================================================================== + +int SoftSynthMIDIDevice::Resume() +{ + Started = 1; + return 0; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: Stop +// +//========================================================================== + +void SoftSynthMIDIDevice::Stop() +{ +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: StreamOutSync +// +// This version is called from the main game thread and needs to +// synchronize with the player thread. +// +//========================================================================== + +int SoftSynthMIDIDevice::StreamOutSync(MidiHeader *header) +{ + StreamOut(header); + return 0; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: StreamOut +// +// This version is called from the player thread so does not need to +// arbitrate for access to the Events pointer. +// +//========================================================================== + +int SoftSynthMIDIDevice::StreamOut(MidiHeader *header) +{ + header->lpNext = NULL; + if (Events == NULL) + { + Events = header; + NextTickIn = SamplesPerTick * *(uint32_t *)header->lpData; + Position = 0; + } + else + { + MidiHeader **p; + + for (p = &Events; *p != NULL; p = &(*p)->lpNext) + { } + *p = header; + } + return 0; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: Pause +// +//========================================================================== + +bool SoftSynthMIDIDevice::Pause(bool paused) +{ + return true; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: PlayTick +// +// event[0] = delta time +// event[1] = unused +// event[2] = event +// +//========================================================================== + +int SoftSynthMIDIDevice::PlayTick() +{ + uint32_t delay = 0; + + while (delay == 0 && Events != NULL) + { + uint32_t *event = (uint32_t *)(Events->lpData + Position); + if (MEVENT_EVENTTYPE(event[2]) == MEVENT_TEMPO) + { + SetTempo(MEVENT_EVENTPARM(event[2])); + } + else if (MEVENT_EVENTTYPE(event[2]) == MEVENT_LONGMSG) + { + HandleLongEvent((uint8_t *)&event[3], MEVENT_EVENTPARM(event[2])); + } + else if (MEVENT_EVENTTYPE(event[2]) == 0) + { // Short MIDI event + int status = event[2] & 0xff; + int parm1 = (event[2] >> 8) & 0x7f; + int parm2 = (event[2] >> 16) & 0x7f; + HandleEvent(status, parm1, parm2); + +#if 0 + if (synth_watch) + { + static const char *const commands[8] = + { + "Note off", + "Note on", + "Poly press", + "Ctrl change", + "Prgm change", + "Chan press", + "Pitch bend", + "SysEx" + }; + char buffer[128]; + mysnprintf(buffer, countof(buffer), "C%02d: %11s %3d %3d\n", (status & 15) + 1, commands[(status >> 4) & 7], parm1, parm2); +#ifdef _WIN32 + I_DebugPrint(buffer); +#else + fputs(buffer, stderr); +#endif + } +#endif + } + + // Advance to next event. + if (event[2] < 0x80000000) + { // Short message + Position += 12; + } + else + { // Long message + Position += 12 + ((MEVENT_EVENTPARM(event[2]) + 3) & ~3); + } + + // Did we use up this buffer? + if (Position >= Events->dwBytesRecorded) + { + Events = Events->lpNext; + Position = 0; + + if (Callback != NULL) + { + Callback(CallbackData); + } + } + + if (Events == NULL) + { // No more events. Just return something to keep the song playing + // while we wait for more to be submitted. + return int(Division); + } + + delay = *(uint32_t *)(Events->lpData + Position); + } + return delay; +} + +//========================================================================== +// +// SoftSynthMIDIDevice :: ServiceStream +// +//========================================================================== + +bool SoftSynthMIDIDevice::ServiceStream (void *buff, int numbytes) +{ + float *samples = (float *)buff; + float *samples1; + int numsamples = numbytes / sizeof(float) / 2; + bool res = true; + + samples1 = samples; + memset(buff, 0, numbytes); + + while (Events != NULL && numsamples > 0) + { + double ticky = NextTickIn; + int tick_in = int(NextTickIn); + int samplesleft = std::min(numsamples, tick_in); + + if (samplesleft > 0) + { + ComputeOutput(samples1, samplesleft); + assert(NextTickIn == ticky); + NextTickIn -= samplesleft; + assert(NextTickIn >= 0); + numsamples -= samplesleft; + samples1 += samplesleft * 2; + } + + if (NextTickIn < 1) + { + int next = PlayTick(); + assert(next >= 0); + if (next == 0) + { // end of song + if (numsamples > 0) + { + ComputeOutput(samples1, numsamples); + } + res = false; + break; + } + else + { + NextTickIn += SamplesPerTick * next; + assert(NextTickIn >= 0); + } + } + } + + if (Events == NULL) + { + res = false; + } + return res; +} diff --git a/source/mididevices/music_timidity_mididevice.cpp b/source/mididevices/music_timidity_mididevice.cpp new file mode 100644 index 000000000..428104298 --- /dev/null +++ b/source/mididevices/music_timidity_mididevice.cpp @@ -0,0 +1,307 @@ +/* +** music_timidity_mididevice.cpp +** Provides access to TiMidity as a generic MIDI device. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include "mididevice.h" +#include "zmusic/zmusic_internal.h" + +#ifdef HAVE_GUS + +#include "timidity/timidity.h" +#include "timidity/playmidi.h" +#include "timidity/instrum.h" +#include "fileio.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +GUSConfig gusConfig; + +//========================================================================== +// +// The actual device. +// +//========================================================================== + +namespace Timidity { struct Renderer; } + +class TimidityMIDIDevice : public SoftSynthMIDIDevice +{ + void LoadInstruments(); +public: + TimidityMIDIDevice(int samplerate); + ~TimidityMIDIDevice(); + + int OpenRenderer() override; + void PrecacheInstruments(const uint16_t *instruments, int count) override; + int GetDeviceType() const override { return MDEV_GUS; } + +protected: + Timidity::Renderer *Renderer; + + void HandleEvent(int status, int parm1, int parm2) override; + void HandleLongEvent(const uint8_t *data, int len) override; + void ComputeOutput(float *buffer, int len) override; +}; + + +// CODE -------------------------------------------------------------------- + + +void TimidityMIDIDevice::LoadInstruments() +{ + if (gusConfig.reader) + { + // Check if we got some GUS data before using it. + std::string ultradir; + const char *ret = getenv("ULTRADIR"); + if (ret) ultradir = std::string(ret); + // The GUS put its patches in %ULTRADIR%/MIDI so we can try that + if (ultradir.length()) + { + ultradir += "/midi"; + gusConfig.reader->add_search_path(ultradir.c_str()); + } + // Load DMXGUS lump and patches from gus_patchdir + if (gusConfig.gus_patchdir.length() != 0) gusConfig.reader->add_search_path(gusConfig.gus_patchdir.c_str()); + + gusConfig.instruments.reset(new Timidity::Instruments(gusConfig.reader)); + gusConfig.loadedConfig = gusConfig.readerName; + } + + if (gusConfig.instruments == nullptr) + { + throw std::runtime_error("No instruments set for GUS device"); + } + + if (gusConfig.gus_dmxgus && gusConfig.dmxgus.size()) + { + bool success = gusConfig.instruments->LoadDMXGUS(gusConfig.gus_memsize, (const char*)gusConfig.dmxgus.data(), gusConfig.dmxgus.size()) >= 0; + gusConfig.reader = nullptr; + + if (!success) + { + gusConfig.instruments.reset(); + gusConfig.loadedConfig = ""; + throw std::runtime_error("Unable to initialize DMXGUS for GUS MIDI device"); + } + } + else + { + bool err = gusConfig.instruments->LoadConfig() < 0; + gusConfig.reader = nullptr; + + if (err) + { + gusConfig.instruments.reset(); + gusConfig.loadedConfig = ""; + throw std::runtime_error("Unable to initialize instruments for GUS MIDI device"); + } + } +} + +//========================================================================== +// +// TimidityMIDIDevice Constructor +// +//========================================================================== + +TimidityMIDIDevice::TimidityMIDIDevice(int samplerate) + : SoftSynthMIDIDevice(samplerate, 11025, 65535) +{ + LoadInstruments(); + Renderer = new Timidity::Renderer((float)SampleRate, gusConfig.midi_voices, gusConfig.instruments.get()); +} + +//========================================================================== +// +// TimidityMIDIDevice Destructor +// +//========================================================================== + +TimidityMIDIDevice::~TimidityMIDIDevice() +{ + Close(); + if (Renderer != nullptr) + { + delete Renderer; + } +} + +//========================================================================== +// +// TimidityMIDIDevice :: Open +// +// Returns 0 on success. +// +//========================================================================== + +int TimidityMIDIDevice::OpenRenderer() +{ + Renderer->Reset(); + return 0; +} + +//========================================================================== +// +// TimidityMIDIDevice :: PrecacheInstruments +// +// Each entry is packed as follows: +// Bits 0- 6: Instrument number +// Bits 7-13: Bank number +// Bit 14: Select drum set if 1, tone bank if 0 +// +//========================================================================== + +void TimidityMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count) +{ + for (int i = 0; i < count; ++i) + { + Renderer->MarkInstrument((instruments[i] >> 7) & 127, instruments[i] >> 14, instruments[i] & 127); + } + Renderer->load_missing_instruments(); +} + +//========================================================================== +// +// TimidityMIDIDevice :: HandleEvent +// +//========================================================================== + +void TimidityMIDIDevice::HandleEvent(int status, int parm1, int parm2) +{ + Renderer->HandleEvent(status, parm1, parm2); +} + +//========================================================================== +// +// TimidityMIDIDevice :: HandleLongEvent +// +//========================================================================== + +void TimidityMIDIDevice::HandleLongEvent(const uint8_t *data, int len) +{ + Renderer->HandleLongMessage(data, len); +} + +//========================================================================== +// +// TimidityMIDIDevice :: ComputeOutput +// +//========================================================================== + +void TimidityMIDIDevice::ComputeOutput(float *buffer, int len) +{ + Renderer->ComputeOutput(buffer, len); + for (int i = 0; i < len * 2; i++) buffer[i] *= 0.7f; +} + +//========================================================================== +// +// +// +//========================================================================== + +//========================================================================== +// +// Sets up the date to load the instruments for the GUS device. +// The actual instrument loader is part of the device. +// +//========================================================================== + +bool GUS_SetupConfig(const char* args) +{ + if (*args == 0) args = gusConfig.gus_config.c_str(); + if (gusConfig.gus_dmxgus && *args == 0) args = "DMXGUS"; + //if (stricmp(gusConfig.loadedConfig.c_str(), args) == 0) return false; // aleady loaded + + MusicIO::SoundFontReaderInterface* reader = MusicIO::ClientOpenSoundFont(args, SF_GUS); + if (!reader && MusicIO::fileExists(args)) + { + auto f = MusicIO::utf8_fopen(args, "rb"); + if (f) + { + char test[12] = {}; + fread(test, 1, 12, f); + fclose(f); + // If the passed file is an SF2 sound font we need to use the special reader that fakes a config for it. + if (memcmp(test, "RIFF", 4) == 0 && memcmp(test + 8, "sfbk", 4) == 0) + reader = new MusicIO::SF2Reader(args); + } + if (!reader) reader = new MusicIO::FileSystemSoundFontReader(args, true); + } + + if (!reader && gusConfig.gus_dmxgus) + { + reader = new MusicIO::FileSystemSoundFontReader(args, true); + } + + if (reader == nullptr) + { + char error[80]; + snprintf(error, 80, "GUS: %s: Unable to load sound font\n", args); + throw std::runtime_error(error); + } + gusConfig.reader = reader; + gusConfig.readerName = args; + return true; +} + +# +MIDIDevice* CreateTimidityMIDIDevice(const char* Args, int samplerate) +{ + GUS_SetupConfig(Args); + return new TimidityMIDIDevice(samplerate); +} + +#else +MIDIDevice* CreateTimidityMIDIDevice(const char* Args, int samplerate) +{ + throw std::runtime_error("GUS device not supported in this configuration"); +} +#endif diff --git a/source/mididevices/music_timiditypp_mididevice.cpp b/source/mididevices/music_timiditypp_mididevice.cpp new file mode 100644 index 000000000..93632b20a --- /dev/null +++ b/source/mididevices/music_timiditypp_mididevice.cpp @@ -0,0 +1,243 @@ +/* +** music_timiditypp_mididevice.cpp +** Provides access to timidity.exe +** +**--------------------------------------------------------------------------- +** Copyright 2001-2017 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include "mididevice.h" +#include "zmusic/zmusic_internal.h" + +#ifdef HAVE_TIMIDITY + +#include "timiditypp/timidity.h" +#include "timiditypp/instrum.h" +#include "timiditypp/playmidi.h" + + +TimidityConfig timidityConfig; + +class TimidityPPMIDIDevice : public SoftSynthMIDIDevice +{ + std::shared_ptr instruments; +public: + TimidityPPMIDIDevice(int samplerate); + ~TimidityPPMIDIDevice(); + + int OpenRenderer() override; + void PrecacheInstruments(const uint16_t *instruments, int count) override; + //std::string GetStats(); + int GetDeviceType() const override { return MDEV_TIMIDITY; } + + double test[3] = { 0, 0, 0 }; + +protected: + TimidityPlus::Player *Renderer; + + void HandleEvent(int status, int parm1, int parm2) override; + void HandleLongEvent(const uint8_t *data, int len) override; + void ComputeOutput(float *buffer, int len) override; + void LoadInstruments(); +}; + +//========================================================================== +// +// +// +//========================================================================== + +void TimidityPPMIDIDevice::LoadInstruments() +{ + if (timidityConfig.reader) + { + timidityConfig.loadedConfig = timidityConfig.readerName; + timidityConfig.instruments.reset(new TimidityPlus::Instruments()); + bool success = timidityConfig.instruments->load(timidityConfig.reader); + timidityConfig.reader = nullptr; + + if (!success) + { + timidityConfig.instruments.reset(); + timidityConfig.loadedConfig = ""; + throw std::runtime_error("Unable to initialize instruments for Timidity++ MIDI device"); + } + } + else if (timidityConfig.instruments == nullptr) + { + throw std::runtime_error("No instruments set for Timidity++ device"); + } + instruments = timidityConfig.instruments; +} + +//========================================================================== +// +// TimidityPPMIDIDevice Constructor +// +//========================================================================== + +TimidityPPMIDIDevice::TimidityPPMIDIDevice(int samplerate) + :SoftSynthMIDIDevice(samplerate, 4000, 65000) +{ + TimidityPlus::set_playback_rate(SampleRate); + LoadInstruments(); + Renderer = new TimidityPlus::Player(instruments.get()); +} + +//========================================================================== +// +// TimidityPPMIDIDevice Destructor +// +//========================================================================== + +TimidityPPMIDIDevice::~TimidityPPMIDIDevice () +{ + Close(); + if (Renderer != nullptr) + { + delete Renderer; + } +} + +//========================================================================== +// +// TimidityPPMIDIDevice :: Open +// +//========================================================================== + +int TimidityPPMIDIDevice::OpenRenderer() +{ + Renderer->playmidi_stream_init(); + return 0; +} + +//========================================================================== +// +// TimidityPPMIDIDevice :: PrecacheInstruments +// +// Each entry is packed as follows: +// Bits 0- 6: Instrument number +// Bits 7-13: Bank number +// Bit 14: Select drum set if 1, tone bank if 0 +// +//========================================================================== + +void TimidityPPMIDIDevice::PrecacheInstruments(const uint16_t *instrumentlist, int count) +{ + if (instruments != nullptr) + instruments->PrecacheInstruments(instrumentlist, count); +} + +//========================================================================== +// +// TimidityPPMIDIDevice :: HandleEvent +// +//========================================================================== + +void TimidityPPMIDIDevice::HandleEvent(int status, int parm1, int parm2) +{ + if (Renderer != nullptr) + Renderer->send_event(status, parm1, parm2); +} + +//========================================================================== +// +// TimidityPPMIDIDevice :: HandleLongEvent +// +//========================================================================== + +void TimidityPPMIDIDevice::HandleLongEvent(const uint8_t *data, int len) +{ + if (Renderer != nullptr) + Renderer->send_long_event(data, len); +} + +//========================================================================== +// +// TimidityPPMIDIDevice :: ComputeOutput +// +//========================================================================== + +void TimidityPPMIDIDevice::ComputeOutput(float *buffer, int len) +{ + if (Renderer != nullptr) + Renderer->compute_data(buffer, len); +} + +//========================================================================== +// +// +// +//========================================================================== + +bool Timidity_SetupConfig(const char* args) +{ + if (*args == 0) args = timidityConfig.timidity_config.c_str(); + if (stricmp(timidityConfig.loadedConfig.c_str(), args) == 0) return false; // aleady loaded + + MusicIO::SoundFontReaderInterface* reader = MusicIO::ClientOpenSoundFont(args, SF_GUS | SF_SF2); + if (!reader && MusicIO::fileExists(args)) + { + auto f = MusicIO::utf8_fopen(args, "rb"); + if (f) + { + char test[12] = {}; + fread(test, 1, 12, f); + fclose(f); + // If the passed file is an SF2 sound font we need to use the special reader that fakes a config for it. + if (memcmp(test, "RIFF", 4) == 0 && memcmp(test + 8, "sfbk", 4) == 0) + reader = new MusicIO::SF2Reader(args); + } + if (!reader) reader = new MusicIO::FileSystemSoundFontReader(args, true); + } + + if (reader == nullptr) + { + char error[80]; + snprintf(error, 80, "Timidity++: %s: Unable to load sound font\n", args); + throw std::runtime_error(error); + } + timidityConfig.reader = reader; + timidityConfig.readerName = args; + return true; +} + +MIDIDevice *CreateTimidityPPMIDIDevice(const char *Args, int samplerate) +{ + Timidity_SetupConfig(Args); + return new TimidityPPMIDIDevice(samplerate); +} + +#else +MIDIDevice* CreateTimidityPPMIDIDevice(const char* Args, int samplerate) +{ + throw std::runtime_error("Timidity++ device not supported in this configuration"); +} +#endif diff --git a/source/mididevices/music_wavewriter_mididevice.cpp b/source/mididevices/music_wavewriter_mididevice.cpp new file mode 100644 index 000000000..f9358268c --- /dev/null +++ b/source/mididevices/music_wavewriter_mididevice.cpp @@ -0,0 +1,203 @@ +/* +** music_wavewriter_mididevice.cpp +** Dumps a MIDI to a wave file by using one of the other software synths. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** Copyright 2018 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "mididevice.h" +#include "zmusic/m_swap.h" +#include "fileio.h" +#include +#include + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +struct FmtChunk +{ + //uint32_t ChunkID; + uint32_t ChunkLen; + uint16_t FormatTag; + uint16_t Channels; + uint32_t SamplesPerSec; + uint32_t AvgBytesPerSec; + uint16_t BlockAlign; + uint16_t BitsPerSample; + uint16_t ExtensionSize; + uint16_t ValidBitsPerSample; + uint32_t ChannelMask; + uint32_t SubFormatA; + uint16_t SubFormatB; + uint16_t SubFormatC; + uint8_t SubFormatD[8]; +}; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// MIDIWaveWriter Constructor +// +//========================================================================== + +MIDIWaveWriter::MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *playdevice) + : SoftSynthMIDIDevice(playdevice->GetSampleRate()) +{ + File = MusicIO::utf8_fopen(filename, "wb"); + playDevice = playdevice; + if (File != nullptr) + { // Write wave header + FmtChunk fmt; + + if (fwrite("RIFF\0\0\0\0WAVEfmt ", 1, 16, File) != 16) goto fail; + + playDevice->CalcTickRate(); + fmt.ChunkLen = LittleLong(uint32_t(sizeof(fmt) - 4)); + fmt.FormatTag = LittleShort((uint16_t)0xFFFE); // WAVE_FORMAT_EXTENSIBLE + fmt.Channels = LittleShort((uint16_t)2); + fmt.SamplesPerSec = LittleLong(SampleRate); + fmt.AvgBytesPerSec = LittleLong(SampleRate * 8); + fmt.BlockAlign = LittleShort((uint16_t)8); + fmt.BitsPerSample = LittleShort((uint16_t)32); + fmt.ExtensionSize = LittleShort((uint16_t)(2 + 4 + 16)); + fmt.ValidBitsPerSample = LittleShort((uint16_t)32); + fmt.ChannelMask = LittleLong(3); + fmt.SubFormatA = LittleLong(0x00000003); // Set subformat to KSDATAFORMAT_SUBTYPE_IEEE_FLOAT + fmt.SubFormatB = 0x0000; + fmt.SubFormatC = LittleShort((uint16_t)0x0010); + fmt.SubFormatD[0] = 0x80; + fmt.SubFormatD[1] = 0x00; + fmt.SubFormatD[2] = 0x00; + fmt.SubFormatD[3] = 0xaa; + fmt.SubFormatD[4] = 0x00; + fmt.SubFormatD[5] = 0x38; + fmt.SubFormatD[6] = 0x9b; + fmt.SubFormatD[7] = 0x71; + if (sizeof(fmt) != fwrite(&fmt, 1, sizeof(fmt), File)) goto fail; + + if (fwrite("data\0\0\0\0", 1, 8, File) != 8) goto fail; + + return; + fail: + char buffer[80]; + fclose(File); + File = nullptr; + snprintf(buffer, 80, "Failed to write %s: %s\n", filename, strerror(errno)); + throw std::runtime_error(buffer); + } +} + +//========================================================================== +// +// MIDIWaveWriter Destructor +// +//========================================================================== + +bool MIDIWaveWriter::CloseFile() +{ + if (File != nullptr) + { + auto pos = ftell(File); + uint32_t size; + + // data chunk size + size = LittleLong(uint32_t(pos - 8)); + if (0 == fseek(File, 4, SEEK_SET)) + { + if (4 == fwrite(&size, 1, 4, File)) + { + size = LittleLong(uint32_t(pos - 12 - sizeof(FmtChunk) - 8)); + if (0 == fseek(File, 4 + sizeof(FmtChunk) + 8, SEEK_CUR)) + { + if (4 == fwrite(&size, 1, 4, File)) + { + fclose(File); + File = nullptr; + return true; + } + } + } + } + fclose(File); + File = nullptr; + } + return false; +} + +//========================================================================== +// +// MIDIWaveWriter :: Resume +// +//========================================================================== + +int MIDIWaveWriter::Resume() +{ + float writebuffer[4096]; + + while (ServiceStream(writebuffer, sizeof(writebuffer))) + { + if (fwrite(writebuffer, 1, sizeof(writebuffer), File) != sizeof(writebuffer)) + { + fclose(File); + File = nullptr; + char buffer[80]; + snprintf(buffer, 80, "Could not write entire wave file: %s\n", strerror(errno)); + throw std::runtime_error(buffer); + } + } + return 0; +} + +//========================================================================== +// +// MIDIWaveWriter Stop +// +//========================================================================== + +void MIDIWaveWriter::Stop() +{ +} diff --git a/source/mididevices/music_wildmidi_mididevice.cpp b/source/mididevices/music_wildmidi_mididevice.cpp new file mode 100644 index 000000000..73e1ce3e7 --- /dev/null +++ b/source/mididevices/music_wildmidi_mididevice.cpp @@ -0,0 +1,282 @@ +/* +** music_wildmidi_mididevice.cpp +** Provides access to WildMidi as a generic MIDI device. +** +**--------------------------------------------------------------------------- +** Copyright 2015 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include "mididevice.h" +#include "zmusic/zmusic_internal.h" + +#ifdef HAVE_WILDMIDI + +#include "wildmidi/wildmidi_lib.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +WildMidiConfig wildMidiConfig; + +// WildMidi implementation of a MIDI device --------------------------------- + +class WildMIDIDevice : public SoftSynthMIDIDevice +{ +public: + WildMIDIDevice(int samplerate); + ~WildMIDIDevice(); + + int OpenRenderer() override; + void PrecacheInstruments(const uint16_t *instruments, int count) override; + std::string GetStats() override; + int GetDeviceType() const override { return MDEV_WILDMIDI; } + +protected: + WildMidi::Renderer *Renderer; + std::shared_ptr instruments; + + void HandleEvent(int status, int parm1, int parm2) override; + void HandleLongEvent(const uint8_t *data, int len) override; + void ComputeOutput(float *buffer, int len) override; + void ChangeSettingInt(const char *opt, int set) override; + void LoadInstruments(); + +}; + + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// +// +//========================================================================== + +void WildMIDIDevice::LoadInstruments() +{ + if (wildMidiConfig.reader) + { + wildMidiConfig.loadedConfig = wildMidiConfig.readerName; + wildMidiConfig.instruments.reset(new WildMidi::Instruments(wildMidiConfig.reader, SampleRate)); + wildMidiConfig.reader = nullptr; + } + else if (wildMidiConfig.instruments == nullptr) + { + throw std::runtime_error("No instruments set for WildMidi device"); + } + instruments = wildMidiConfig.instruments; + if (instruments->LoadConfig(nullptr) < 0) + { + wildMidiConfig.instruments.reset(); + wildMidiConfig.loadedConfig = ""; + throw std::runtime_error("Unable to initialize instruments for WildMidi device"); + } +} + +//========================================================================== +// +// WildMIDIDevice Constructor +// +//========================================================================== + +WildMIDIDevice::WildMIDIDevice(int samplerate) + :SoftSynthMIDIDevice(samplerate, 11025, 65535) +{ + Renderer = NULL; + LoadInstruments(); + + Renderer = new WildMidi::Renderer(instruments.get()); + int flags = 0; + if (wildMidiConfig.enhanced_resampling) flags |= WildMidi::WM_MO_ENHANCED_RESAMPLING; + if (wildMidiConfig.reverb) flags |= WildMidi::WM_MO_REVERB; + Renderer->SetOption(WildMidi::WM_MO_ENHANCED_RESAMPLING | WildMidi::WM_MO_REVERB, flags); +} + +//========================================================================== +// +// WildMIDIDevice Destructor +// +//========================================================================== + +WildMIDIDevice::~WildMIDIDevice() +{ + Close(); + if (Renderer != NULL) + { + delete Renderer; + } +} + +//========================================================================== +// +// WildMIDIDevice :: Open +// +// Returns 0 on success. +// +//========================================================================== + +int WildMIDIDevice::OpenRenderer() +{ + return 0; // This one's a no-op +} + +//========================================================================== +// +// WildMIDIDevice :: PrecacheInstruments +// +// Each entry is packed as follows: +// Bits 0- 6: Instrument number +// Bits 7-13: Bank number +// Bit 14: Select drum set if 1, tone bank if 0 +// +//========================================================================== + +void WildMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count) +{ + for (int i = 0; i < count; ++i) + { + Renderer->LoadInstrument((instruments[i] >> 7) & 127, instruments[i] >> 14, instruments[i] & 127); + } +} + + +//========================================================================== +// +// WildMIDIDevice :: HandleEvent +// +//========================================================================== + +void WildMIDIDevice::HandleEvent(int status, int parm1, int parm2) +{ + Renderer->ShortEvent(status, parm1, parm2); +} + +//========================================================================== +// +// WildMIDIDevice :: HandleLongEvent +// +//========================================================================== + +void WildMIDIDevice::HandleLongEvent(const uint8_t *data, int len) +{ + Renderer->LongEvent(data, len); +} + +//========================================================================== +// +// WildMIDIDevice :: ComputeOutput +// +//========================================================================== + +void WildMIDIDevice::ComputeOutput(float *buffer, int len) +{ + Renderer->ComputeOutput(buffer, len); +} + +//========================================================================== +// +// WildMIDIDevice :: GetStats +// +//========================================================================== + +std::string WildMIDIDevice::GetStats() +{ + char out[20]; + snprintf(out, 20, "%3d voices", Renderer->GetVoiceCount()); + return out; +} + +//========================================================================== +// +// WildMIDIDevice :: ChangeSettingInt +// +//========================================================================== + +void WildMIDIDevice::ChangeSettingInt(const char *opt, int set) +{ + int option; + if (!stricmp(opt, "wildmidi.reverb")) option = WildMidi::WM_MO_REVERB; + else if (!stricmp(opt, "wildmidi.resampling")) option = WildMidi::WM_MO_ENHANCED_RESAMPLING; + else return; + int setit = option * int(set); + Renderer->SetOption(option, setit); +} + +//========================================================================== +// +// +// +//========================================================================== + +bool WildMidi_SetupConfig(const char* args) +{ + if (*args == 0) args = wildMidiConfig.config.c_str(); + if (stricmp(wildMidiConfig.loadedConfig.c_str(), args) == 0) return false; // aleady loaded + + MusicIO::SoundFontReaderInterface* reader = MusicIO::ClientOpenSoundFont(args, SF_GUS); + if (!reader && MusicIO::fileExists(args)) + { + reader = new MusicIO::FileSystemSoundFontReader(args, true); + } + + if (reader == nullptr) + { + char error[80]; + snprintf(error, 80, "WildMidi: %s: Unable to load sound font\n", args); + throw std::runtime_error(error); + } + + wildMidiConfig.reader = reader; + wildMidiConfig.readerName = args; + return true; +} + + +//========================================================================== +// +// +// +//========================================================================== + +MIDIDevice *CreateWildMIDIDevice(const char *Args, int samplerate) +{ + WildMidi_SetupConfig(Args); + return new WildMIDIDevice(samplerate); +} + +#else +MIDIDevice* CreateWildMIDIDevice(const char* Args, int samplerate) +{ + throw std::runtime_error("WildMidi device not supported in this configuration"); +} +#endif diff --git a/source/mididevices/music_win_mididevice.cpp b/source/mididevices/music_win_mididevice.cpp new file mode 100644 index 000000000..e4d1d5125 --- /dev/null +++ b/source/mididevices/music_win_mididevice.cpp @@ -0,0 +1,696 @@ +/* +** music_win_mididevice.cpp +** Provides a WinMM implementation of a MIDI output device. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +// HEADER FILES ------------------------------------------------------------ + +#include "mididevice.h" +#include "zmusic/m_swap.h" +#include "zmusic/mus2midi.h" + +#ifndef __GNUC__ +#include +#endif + +// MACROS ------------------------------------------------------------------ + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static bool IgnoreMIDIVolume(UINT id); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ +// WinMM implementation of a MIDI output device ----------------------------- + +class WinMIDIDevice : public MIDIDevice +{ +public: + WinMIDIDevice(int dev_id, bool precache); + ~WinMIDIDevice(); + int Open(); + void Close(); + bool IsOpen() const; + int GetTechnology() const; + int SetTempo(int tempo); + int SetTimeDiv(int timediv); + int StreamOut(MidiHeader *data); + int StreamOutSync(MidiHeader *data); + int Resume(); + void Stop(); + int PrepareHeader(MidiHeader *data); + int UnprepareHeader(MidiHeader *data); + bool FakeVolume(); + bool Pause(bool paused); + void InitPlayback() override; + bool Update() override; + void PrecacheInstruments(const uint16_t *instruments, int count); + DWORD PlayerLoop(); + bool CanHandleSysex() const override + { + // No Sysex for GS synth. + return VolumeWorks; + } + + +//protected: + static void CALLBACK CallbackFunc(HMIDIOUT, UINT, DWORD_PTR, DWORD, DWORD); + + HMIDISTRM MidiOut; + UINT DeviceID; + DWORD SavedVolume; + MIDIHDR WinMidiHeaders[2]; + int HeaderIndex; + bool VolumeWorks; + bool Precache; + + HANDLE BufferDoneEvent; + HANDLE ExitEvent; + HANDLE PlayerThread; + + +}; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// WinMIDIDevice Contructor +// +//========================================================================== + +WinMIDIDevice::WinMIDIDevice(int dev_id, bool precache) +{ + DeviceID = std::max(dev_id, 0); + MidiOut = 0; + HeaderIndex = 0; + Precache = precache; + memset(WinMidiHeaders, 0, sizeof(WinMidiHeaders)); + + BufferDoneEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (BufferDoneEvent == nullptr) + { + throw std::runtime_error("Could not create buffer done event for MIDI playback"); + } + ExitEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (ExitEvent == nullptr) + { + CloseHandle(BufferDoneEvent); + BufferDoneEvent = nullptr; + throw std::runtime_error("Could not create exit event for MIDI playback"); + } + PlayerThread = nullptr; +} + +//========================================================================== +// +// WinMIDIDevice Destructor +// +//========================================================================== + +WinMIDIDevice::~WinMIDIDevice() +{ + Close(); + + if (ExitEvent != nullptr) + { + CloseHandle(ExitEvent); + } + if (BufferDoneEvent != nullptr) + { + CloseHandle(BufferDoneEvent); + } +} + +//========================================================================== +// +// WinMIDIDevice :: Open +// +//========================================================================== + +int WinMIDIDevice::Open() +{ + MMRESULT err; + + if (MidiOut == nullptr) + { + err = midiStreamOpen(&MidiOut, &DeviceID, 1, (DWORD_PTR)CallbackFunc, (DWORD_PTR)this, CALLBACK_FUNCTION); + + if (err == MMSYSERR_NOERROR) + { + if (IgnoreMIDIVolume(DeviceID)) + { + VolumeWorks = false; + } + else + { + // Set master volume to full, if the device allows it on this interface. + VolumeWorks = (MMSYSERR_NOERROR == midiOutGetVolume((HMIDIOUT)MidiOut, &SavedVolume)); + if (VolumeWorks) + { + VolumeWorks &= (MMSYSERR_NOERROR == midiOutSetVolume((HMIDIOUT)MidiOut, 0xffffffff)); + } + } + } + else + { + return 1; + } + } + return 0; +} + +//========================================================================== +// +// WinMIDIDevice :: Close +// +//========================================================================== + +void WinMIDIDevice::Close() +{ + if (MidiOut != nullptr) + { + midiStreamClose(MidiOut); + MidiOut = nullptr; + } +} + +//========================================================================== +// +// WinMIDIDevice :: IsOpen +// +//========================================================================== + +bool WinMIDIDevice::IsOpen() const +{ + return MidiOut != nullptr; +} + +//========================================================================== +// +// WinMIDIDevice :: GetTechnology +// +//========================================================================== + +int WinMIDIDevice::GetTechnology() const +{ + MIDIOUTCAPS caps; + + if (MMSYSERR_NOERROR == midiOutGetDevCaps(DeviceID, &caps, sizeof(caps))) + { + return caps.wTechnology; + } + return -1; +} + +//========================================================================== +// +// WinMIDIDevice :: SetTempo +// +//========================================================================== + +int WinMIDIDevice::SetTempo(int tempo) +{ + MIDIPROPTEMPO data = { sizeof(MIDIPROPTEMPO), (DWORD)tempo }; + return midiStreamProperty(MidiOut, (LPBYTE)&data, MIDIPROP_SET | MIDIPROP_TEMPO); +} + +//========================================================================== +// +// WinMIDIDevice :: SetTimeDiv +// +//========================================================================== + +int WinMIDIDevice::SetTimeDiv(int timediv) +{ + MIDIPROPTIMEDIV data = { sizeof(MIDIPROPTIMEDIV), (DWORD)timediv }; + return midiStreamProperty(MidiOut, (LPBYTE)&data, MIDIPROP_SET | MIDIPROP_TIMEDIV); +} + +//========================================================================== +// +// MIDIStreamer :: PlayerProc Static +// +// Entry point for the player thread. +// +//========================================================================== + +DWORD WINAPI PlayerProc(LPVOID lpParameter) +{ + return ((WinMIDIDevice *)lpParameter)->PlayerLoop(); +} + +//========================================================================== +// +// WinMIDIDevice :: Resume +// +//========================================================================== + +int WinMIDIDevice::Resume() +{ + DWORD tid; + int ret = midiStreamRestart(MidiOut); + if (ret == 0) + { + PlayerThread = CreateThread(nullptr, 0, PlayerProc, this, 0, &tid); + if (PlayerThread == nullptr) + { + Stop(); + throw std::runtime_error("Creating MIDI thread failed\n"); + } + } + return ret; +} + +//========================================================================== +// +// WinMIDIDevice :: InitPlayback +// +//========================================================================== + +void WinMIDIDevice::InitPlayback() +{ + ResetEvent(ExitEvent); + ResetEvent(BufferDoneEvent); +} + +//========================================================================== +// +// WinMIDIDevice :: Stop +// +//========================================================================== + +void WinMIDIDevice::Stop() +{ + if (PlayerThread != nullptr) + { + SetEvent(ExitEvent); + WaitForSingleObject(PlayerThread, INFINITE); + CloseHandle(PlayerThread); + PlayerThread = nullptr; + } + + midiStreamStop(MidiOut); + midiOutReset((HMIDIOUT)MidiOut); + if (VolumeWorks) + { + midiOutSetVolume((HMIDIOUT)MidiOut, SavedVolume); + } +} + +//========================================================================== +// +// MIDIStreamer :: PlayerLoop +// +// Services MIDI playback events. +// +//========================================================================== + +DWORD WinMIDIDevice::PlayerLoop() +{ + HANDLE events[2] = { BufferDoneEvent, ExitEvent }; + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + + for (;;) + { + switch (WaitForMultipleObjects(2, events, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + if (Callback != nullptr) Callback(CallbackData); + break; + + case WAIT_OBJECT_0 + 1: + return 0; + + default: + // Should not happen. + return MMSYSERR_ERROR; + } + } + return 0; +} + + +//========================================================================== +// +// WinMIDIDevice :: PrecacheInstruments +// +// Each entry is packed as follows: +// Bits 0- 6: Instrument number +// Bits 7-13: Bank number +// Bit 14: Select drum set if 1, tone bank if 0 +// +// My old GUS PnP needed the instruments to be preloaded, or it would miss +// some notes the first time through a song. I doubt any modern +// hardware has this problem, but since I'd already written the code for +// ZDoom 1.22 and below, I'm resurrecting it now for completeness, since I'm +// using preloading for the internal Timidity. +// +// NOTETOSELF: Why did I never notice the midiOutCache(Drum)Patches calls +// before now? Should I switch to them? This code worked on my GUS, but +// using the APIs intended for caching might be better. +// +//========================================================================== + +void WinMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count) +{ + // Setting snd_midiprecache to false disables this precaching, since it + // does involve sleeping for more than a miniscule amount of time. + if (!Precache) + { + return; + } + uint8_t bank[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int i, chan; + + for (i = 0, chan = 0; i < count; ++i) + { + int instr = instruments[i] & 127; + int banknum = (instruments[i] >> 7) & 127; + int percussion = instruments[i] >> 14; + + if (percussion) + { + if (bank[9] != banknum) + { + midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | 9 | (0 << 8) | (banknum << 16)); + bank[9] = banknum; + } + midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_NOTEON | 9 | ((instruments[i] & 0x7f) << 8) | (1 << 16)); + } + else + { // Melodic + if (bank[chan] != banknum) + { + midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | 9 | (0 << 8) | (banknum << 16)); + bank[chan] = banknum; + } + midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_PRGMCHANGE | chan | (instruments[i] << 8)); + midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_NOTEON | chan | (60 << 8) | (1 << 16)); + if (++chan == 9) + { // Skip the percussion channel + chan = 10; + } + } + // Once we've got an instrument playing on each melodic channel, sleep to give + // the driver time to load the instruments. Also do this for the final batch + // of instruments. + if (chan == 16 || i == count - 1) + { + Sleep(250); + for (chan = 15; chan-- != 0; ) + { + // Turn all notes off + midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | chan | (123 << 8)); + } + // And now chan is back at 0, ready to start the cycle over. + } + } + // Make sure all channels are set back to bank 0. + for (i = 0; i < 16; ++i) + { + if (bank[i] != 0) + { + midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | 9 | (0 << 8) | (0 << 16)); + } + } +} + +//========================================================================== +// +// WinMIDIDevice :: Pause +// +// Some docs claim pause is unreliable and can cause the stream to stop +// functioning entirely. Truth or fiction? +// +//========================================================================== + +bool WinMIDIDevice::Pause(bool paused) +{ + return false; +} + +//========================================================================== +// +// WinMIDIDevice :: StreamOut +// +//========================================================================== + +int WinMIDIDevice::StreamOut(MidiHeader *header) +{ + auto syshdr = (MIDIHDR*)header->lpNext; + assert(syshdr == &WinMidiHeaders[0] || syshdr == &WinMidiHeaders[1]); + return midiStreamOut(MidiOut, syshdr, sizeof(MIDIHDR)); +} + +//========================================================================== +// +// WinMIDIDevice :: StreamOutSync +// +//========================================================================== + +int WinMIDIDevice::StreamOutSync(MidiHeader *header) +{ + return StreamOut(header); +} + +//========================================================================== +// +// WinMIDIDevice :: PrepareHeader +// +//========================================================================== + +int WinMIDIDevice::PrepareHeader(MidiHeader *header) +{ + // This code depends on the driving implementation only having two buffers that get passed alternatingly. + // If there were more buffers this would require more intelligent handling. + assert(header->lpNext == nullptr); + MIDIHDR *syshdr = &WinMidiHeaders[HeaderIndex ^= 1]; + memset(syshdr, 0, sizeof(MIDIHDR)); + syshdr->lpData = (LPSTR)header->lpData; + syshdr->dwBufferLength = header->dwBufferLength; + syshdr->dwBytesRecorded = header->dwBytesRecorded; + // this device does not use the lpNext pointer to link MIDI events so use it to point to the system data structure. + header->lpNext = (MidiHeader*)syshdr; + return midiOutPrepareHeader((HMIDIOUT)MidiOut, syshdr, sizeof(MIDIHDR)); +} + +//========================================================================== +// +// WinMIDIDevice :: UnprepareHeader +// +//========================================================================== + +int WinMIDIDevice::UnprepareHeader(MidiHeader *header) +{ + auto syshdr = (MIDIHDR*)header->lpNext; + if (syshdr != nullptr) + { + assert(syshdr == &WinMidiHeaders[0] || syshdr == &WinMidiHeaders[1]); + header->lpNext = nullptr; + return midiOutUnprepareHeader((HMIDIOUT)MidiOut, syshdr, sizeof(MIDIHDR)); + } + else + { + return MMSYSERR_NOERROR; + } +} + +//========================================================================== +// +// WinMIDIDevice :: FakeVolume +// +// Because there are too many MIDI devices out there that don't support +// global volume changes, fake the volume for all of them. +// +//========================================================================== + +bool WinMIDIDevice::FakeVolume() +{ + return true; +} + +//========================================================================== +// +// WinMIDIDevice :: Update +// +//========================================================================== + +bool WinMIDIDevice::Update() +{ + // If the PlayerThread is signalled, then it's dead. + if (PlayerThread != nullptr && + WaitForSingleObject(PlayerThread, 0) == WAIT_OBJECT_0) + { + static const char *const MMErrorCodes[] = + { + "No error", + "Unspecified error", + "Device ID out of range", + "Driver failed enable", + "Device already allocated", + "Device handle is invalid", + "No device driver present", + "Memory allocation error", + "Function isn't supported", + "Error value out of range", + "Invalid flag passed", + "Invalid parameter passed", + "Handle being used simultaneously on another thread", + "Specified alias not found", + "Bad registry database", + "Registry key not found", + "Registry read error", + "Registry write error", + "Registry delete error", + "Registry value not found", + "Driver does not call DriverCallback", + "More data to be returned", + }; + static const char *const MidiErrorCodes[] = + { + "MIDI header not prepared", + "MIDI still playing something", + "MIDI no configured instruments", + "MIDI hardware is still busy", + "MIDI port no longer connected", + "MIDI invalid MIF", + "MIDI operation unsupported with open mode", + "MIDI through device 'eating' a message", + }; + DWORD code = 0xABADCAFE; + GetExitCodeThread(PlayerThread, &code); + CloseHandle(PlayerThread); + PlayerThread = nullptr; + char errmsg[100]; + const char *m = "MIDI playback failure: "; + if (code < 8) + { + snprintf(errmsg, 100, "%s%s", m, MMErrorCodes[code]); + } + else if (code >= MIDIERR_BASE && code < MIDIERR_BASE + 8) + { + snprintf(errmsg, 100, "%s%s", m, MMErrorCodes[code - MIDIERR_BASE]); + } + else + { + snprintf(errmsg, 100, "%s%08x", m, code); + } + throw std::runtime_error(errmsg); + } + return true; +} + +//========================================================================== +// +// WinMIDIDevice :: CallbackFunc static +// +//========================================================================== + +void CALLBACK WinMIDIDevice::CallbackFunc(HMIDIOUT hOut, UINT uMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD dwParam2) +{ + WinMIDIDevice *self = (WinMIDIDevice *)dwInstance; + if (uMsg == MOM_DONE) + { + SetEvent(self->BufferDoneEvent); + } +} + +//========================================================================== +// +// IgnoreMIDIVolume +// +// Should we ignore this MIDI device's volume control even if it works? +// +// Under Windows Vista and up, when using the standard "Microsoft GS +// Wavetable Synth", midiOutSetVolume() will affect the application's audio +// session volume rather than the volume for just the MIDI stream. At first, +// I thought I could get around this by enumerating the streams in the +// audio session to find the MIDI device's stream to set its volume +// manually, but there doesn't appear to be any way to enumerate the +// individual streams in a session. Consequently, we'll just assume the MIDI +// device gets created at full volume like we want. (Actual volume changes +// are done by sending MIDI channel volume messages to the stream, not +// through midiOutSetVolume().) +// +//========================================================================== + +static bool IgnoreMIDIVolume(UINT id) +{ + MIDIOUTCAPSA caps; + + if (MMSYSERR_NOERROR == midiOutGetDevCapsA(id, &caps, sizeof(caps))) + { + if (caps.wTechnology == MIDIDEV_MAPPER) + { + // We cannot determine what this is so we have to assume the worst, as the default + // devive's volume control is irreparably broken. + return true; + } + // The Microsoft GS Wavetable Synth advertises itself as MIDIDEV_SWSYNTH with a VOLUME control. + // If the one we're using doesn't match that, we don't need to bother checking the name. + if (caps.wTechnology == MIDIDEV_SWSYNTH && (caps.dwSupport & MIDICAPS_VOLUME)) + { + if (strncmp(caps.szPname, "Microsoft GS", 12) == 0) + { + return true; + } + } + } + return false; +} + +MIDIDevice *CreateWinMIDIDevice(int mididevice) +{ + return new WinMIDIDevice(mididevice, miscConfig.snd_midiprecache); +} +#endif + + diff --git a/source/midisources/midisource.cpp b/source/midisources/midisource.cpp new file mode 100644 index 000000000..c25adaf8f --- /dev/null +++ b/source/midisources/midisource.cpp @@ -0,0 +1,516 @@ +/* + ** midisource.cpp + ** Implements base class for the different MIDI formats + ** + **--------------------------------------------------------------------------- + ** Copyright 2008-2016 Randy Heit + ** Copyright 2017-2018 Christoph Oelckers + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. 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. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + **--------------------------------------------------------------------------- + ** + */ + +#include "zmusic_internal.h" +#include "midisource.h" + + +char MIDI_EventLengths[7] = { 2, 2, 2, 2, 1, 1, 2 }; +char MIDI_CommonLengths[15] = { 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + +//========================================================================== +// +// MIDISource :: SetTempo +// +// Sets the tempo from a track's initial meta events. Later tempo changes +// create MEVENT_TEMPO events instead. +// +//========================================================================== + +void MIDISource::SetTempo(int new_tempo) +{ + InitialTempo = new_tempo; + // This intentionally uses a callback to avoid any dependencies on the class that is playing the song. + // This should probably be done differently, but right now that's not yet possible. + if (TempoCallback(new_tempo)) + { + Tempo = new_tempo; + } +} + + +//========================================================================== +// +// MIDISource :: ClampLoopCount +// +// We use the XMIDI interpretation of loop count here, where 1 means it +// plays that section once (in other words, no loop) rather than the EMIDI +// interpretation where 1 means to loop it once. +// +// If LoopLimit is 1, we limit all loops, since this pass over the song is +// used to determine instruments for precaching. +// +// If LoopLimit is higher, we only limit infinite loops, since this song is +// being exported. +// +//========================================================================== + +int MIDISource::ClampLoopCount(int loopcount) +{ + if (LoopLimit == 0) + { + return loopcount; + } + if (LoopLimit == 1) + { + return 1; + } + if (loopcount == 0) + { + return LoopLimit; + } + return loopcount; +} + +//========================================================================== +// +// MIDISource :: VolumeControllerChange +// +// Some devices don't support master volume +// (e.g. the Audigy's software MIDI synth--but not its two hardware ones), +// so assume none of them do and scale channel volumes manually. +// +//========================================================================== + +int MIDISource::VolumeControllerChange(int channel, int volume) +{ + ChannelVolumes[channel] = volume; + // When exporting this MIDI file, + // we should not adjust the volume level. + return Exporting? volume : ((volume + 1) * Volume) >> 16; +} + +//========================================================================== +// +// MIDISource :: Precache +// +// Generates a list of instruments this song uses and passes them to the +// MIDI device for precaching. The default implementation here pretends to +// play the song and watches for program change events on normal channels +// and note on events on channel 10. +// +//========================================================================== + +std::vector MIDISource::PrecacheData() +{ + uint32_t Events[2][MAX_MIDI_EVENTS*3]; + uint8_t found_instruments[256] = { 0, }; + uint8_t found_banks[256] = { 0, }; + bool multiple_banks = false; + + LoopLimit = 1; + DoRestart(); + found_banks[0] = true; // Bank 0 is always used. + found_banks[128] = true; + + // Simulate playback to pick out used instruments. + while (!CheckDone()) + { + uint32_t *event_end = MakeEvents(Events[0], &Events[0][MAX_MIDI_EVENTS*3], 1000000*600); + for (uint32_t *event = Events[0]; event < event_end; ) + { + if (MEVENT_EVENTTYPE(event[2]) == 0) + { + int command = (event[2] & 0x70); + int channel = (event[2] & 0x0f); + int data1 = (event[2] >> 8) & 0x7f; + int data2 = (event[2] >> 16) & 0x7f; + + if (channel != 9 && command == (MIDI_PRGMCHANGE & 0x70)) + { + found_instruments[data1] = true; + } + else if (channel == 9 && command == (MIDI_PRGMCHANGE & 0x70) && data1 != 0) + { // On a percussion channel, program change also serves as bank select. + multiple_banks = true; + found_banks[data1 | 128] = true; + } + else if (channel == 9 && command == (MIDI_NOTEON & 0x70) && data2 != 0) + { + found_instruments[data1 | 128] = true; + } + else if (command == (MIDI_CTRLCHANGE & 0x70) && data1 == 0 && data2 != 0) + { + multiple_banks = true; + if (channel == 9) + { + found_banks[data2 | 128] = true; + } + else + { + found_banks[data2] = true; + } + } + } + // Advance to next event + if (event[2] < 0x80000000) + { // short message + event += 3; + } + else + { // long message + event += 3 + ((MEVENT_EVENTPARM(event[2]) + 3) >> 2); + } + } + } + DoRestart(); + + // Now pack everything into a contiguous region for the PrecacheInstruments call(). + std::vector packed; + + for (int i = 0; i < 256; ++i) + { + if (found_instruments[i]) + { + uint16_t packnum = (i & 127) | ((i & 128) << 7); + if (!multiple_banks) + { + packed.push_back(packnum); + } + else + { // In order to avoid having to multiplex tracks in a type 1 file, + // precache every used instrument in every used bank, even if not + // all combinations are actually used. + for (int j = 0; j < 128; ++j) + { + if (found_banks[j + (i & 128)]) + { + packed.push_back(packnum | (j << 7)); + } + } + } + } + } + return packed; +} + +//========================================================================== +// +// MIDISource :: CheckCaps +// +// Called immediately after the device is opened in case a source should +// want to alter its behavior depending on which device it got. +// +//========================================================================== + +void MIDISource::CheckCaps(int tech) +{ +} + +//========================================================================== +// +// MIDISource :: SetMIDISubsong +// +// Selects which subsong to play. This is private. +// +//========================================================================== + +bool MIDISource::SetMIDISubsong(int subsong) +{ + return subsong == 0; +} + +//========================================================================== +// +// WriteVarLen +// +//========================================================================== + +static void WriteVarLen (std::vector &file, uint32_t value) +{ + uint32_t buffer = value & 0x7F; + + while ( (value >>= 7) ) + { + buffer <<= 8; + buffer |= (value & 0x7F) | 0x80; + } + + for (;;) + { + file.push_back(uint8_t(buffer)); + if (buffer & 0x80) + { + buffer >>= 8; + } + else + { + break; + } + } +} + +//========================================================================== +// +// MIDIStreamer :: CreateSMF +// +// Simulates playback to create a Standard MIDI File. +// +//========================================================================== + +void MIDISource::CreateSMF(std::vector &file, int looplimit) +{ + const int EXPORT_LOOP_LIMIT = 30; // Maximum number of times to loop when exporting a MIDI file. + // (for songs with loop controller events) + + static const uint8_t StaticMIDIhead[] = + { + 'M','T','h','d', 0, 0, 0, 6, + 0, 0, // format 0: only one track + 0, 1, // yes, there is really only one track + 0, 0, // divisions (filled in) + 'M','T','r','k', 0, 0, 0, 0, + // The first event sets the tempo (filled in) + 0, 255, 81, 3, 0, 0, 0 + }; + + uint32_t Events[2][MAX_MIDI_EVENTS*3]; + uint32_t delay = 0; + uint8_t running_status = 255; + + // Always create songs aimed at GM devices. + CheckCaps(MIDIDEV_MIDIPORT); + LoopLimit = looplimit <= 0 ? EXPORT_LOOP_LIMIT : looplimit; + DoRestart(); + StartPlayback(false, LoopLimit); + + file.resize(sizeof(StaticMIDIhead)); + memcpy(file.data(), StaticMIDIhead, sizeof(StaticMIDIhead)); + file[12] = Division >> 8; + file[13] = Division & 0xFF; + file[26] = InitialTempo >> 16; + file[27] = InitialTempo >> 8; + file[28] = InitialTempo; + + while (!CheckDone()) + { + uint32_t *event_end = MakeEvents(Events[0], &Events[0][MAX_MIDI_EVENTS*3], 1000000*600); + for (uint32_t *event = Events[0]; event < event_end; ) + { + delay += event[0]; + if (MEVENT_EVENTTYPE(event[2]) == MEVENT_TEMPO) + { + WriteVarLen(file, delay); + delay = 0; + uint32_t tempo = MEVENT_EVENTPARM(event[2]); + file.push_back(MIDI_META); + file.push_back(MIDI_META_TEMPO); + file.push_back(3); + file.push_back(uint8_t(tempo >> 16)); + file.push_back(uint8_t(tempo >> 8)); + file.push_back(uint8_t(tempo)); + running_status = 255; + } + else if (MEVENT_EVENTTYPE(event[2]) == MEVENT_LONGMSG) + { + WriteVarLen(file, delay); + delay = 0; + uint32_t len = MEVENT_EVENTPARM(event[2]); + uint8_t *bytes = (uint8_t *)&event[3]; + if (bytes[0] == MIDI_SYSEX) + { + len--; + file.push_back(MIDI_SYSEX); + WriteVarLen(file, len); + auto p = file.size(); + file.resize(p + len); + memcpy(&file[p], bytes + 1, len); + } + else + { + file.push_back(MIDI_SYSEXEND); + WriteVarLen(file, len); + auto p = file.size(); + file.resize(p + len); + memcpy(&file[p], bytes, len); + } + running_status = 255; + } + else if (MEVENT_EVENTTYPE(event[2]) == 0) + { + WriteVarLen(file, delay); + delay = 0; + uint8_t status = uint8_t(event[2]); + if (status != running_status) + { + running_status = status; + file.push_back(status); + } + file.push_back(uint8_t((event[2] >> 8) & 0x7F)); + if (MIDI_EventLengths[(status >> 4) & 7] == 2) + { + file.push_back(uint8_t((event[2] >> 16) & 0x7F)); + } + } + // Advance to next event + if (event[2] < 0x80000000) + { // short message + event += 3; + } + else + { // long message + event += 3 + ((MEVENT_EVENTPARM(event[2]) + 3) >> 2); + } + } + } + + // End track + WriteVarLen(file, delay); + file.push_back(MIDI_META); + file.push_back(MIDI_META_EOT); + file.push_back(0); + + // Fill in track length + uint32_t len = (uint32_t)file.size() - 22; + file[18] = uint8_t(len >> 24); + file[19] = uint8_t(len >> 16); + file[20] = uint8_t(len >> 8); + file[21] = uint8_t(len & 255); + + LoopLimit = 0; +} + + +//========================================================================== +// +// Global interface (identification / creation of MIDI sources) +// +//========================================================================== + +extern int MUSHeaderSearch(const uint8_t *head, int len); + +//========================================================================== +// +// identify MIDI file type +// +//========================================================================== + +DLL_EXPORT EMIDIType ZMusic_IdentifyMIDIType(uint32_t *id, int size) +{ + // Check for MUS format + // Tolerate sloppy wads by searching up to 32 bytes for the header + if (MUSHeaderSearch((uint8_t*)id, size) >= 0) + { + return MIDI_MUS; + } + // Check for HMI format + else + if (id[0] == MAKE_ID('H','M','I','-') && + id[1] == MAKE_ID('M','I','D','I') && + id[2] == MAKE_ID('S','O','N','G')) + { + return MIDI_HMI; + } + // Check for HMP format + else + if (id[0] == MAKE_ID('H','M','I','M') && + id[1] == MAKE_ID('I','D','I','P')) + { + return MIDI_HMI; + } + // Check for XMI format + else + if ((id[0] == MAKE_ID('F','O','R','M') && + id[2] == MAKE_ID('X','D','I','R')) || + ((id[0] == MAKE_ID('C','A','T',' ') || id[0] == MAKE_ID('F','O','R','M')) && + id[2] == MAKE_ID('X','M','I','D'))) + { + return MIDI_XMI; + } + // Check for MIDS format + else + if (id[0] == MAKE_ID('R','I','F','F') && + id[2] == MAKE_ID('M','I','D','S')) + { + return MIDI_MIDS; + } + // Check for MIDI format + else if (id[0] == MAKE_ID('M','T','h','d')) + { + return MIDI_MIDI; + } + else + { + return MIDI_NOTMIDI; + } +} + +//========================================================================== +// +// create a source based on MIDI file type +// +//========================================================================== + +DLL_EXPORT ZMusic_MidiSource ZMusic_CreateMIDISource(const uint8_t *data, size_t length, EMIDIType miditype) +{ + try + { + MIDISource* source; + switch (miditype) + { + case MIDI_MUS: + source = new MUSSong2(data, length); + break; + + case MIDI_MIDI: + source = new MIDISong2(data, length); + break; + + case MIDI_HMI: + source = new HMISong(data, length); + break; + + case MIDI_XMI: + source = new XMISong(data, length); + break; + + case MIDI_MIDS: + source = new MIDSSong(data, length); + break; + + default: + SetError("Unable to identify MIDI data"); + source = nullptr; + break; + } + return source; + } + catch (const std::exception & ex) + { + SetError(ex.what()); + return nullptr; + } +} diff --git a/source/midisources/midisource.h b/source/midisources/midisource.h new file mode 100644 index 000000000..0be53d934 --- /dev/null +++ b/source/midisources/midisource.h @@ -0,0 +1,250 @@ +// +// midisources.h +// GZDoom +// +// Created by Christoph Oelckers on 23.02.18. +// + +#ifndef midisources_h +#define midisources_h + +#include +#include +#include +#include +#include +#include "zmusic/mus2midi.h" +#include "zmusic/mididefs.h" + +extern char MIDI_EventLengths[7]; +extern char MIDI_CommonLengths[15]; + + +// base class for the different MIDI sources -------------------------------------- + +class MIDISource +{ + int Volume = 0xffff; + int LoopLimit = 0; + std::function TempoCallback = [](int t) { return false; }; + +protected: + + bool isLooping = false; + bool skipSysex = false; + int Division = 0; + int Tempo = 500000; + int InitialTempo = 500000; + uint8_t ChannelVolumes[16]; + + int VolumeControllerChange(int channel, int volume); + void SetTempo(int new_tempo); + int ClampLoopCount(int loopcount); + + +public: + bool Exporting = false; + + // Virtuals for subclasses to override + virtual ~MIDISource() {} + virtual void CheckCaps(int tech); + virtual void DoInitialSetup() = 0; + virtual void DoRestart() = 0; + virtual bool CheckDone() = 0; + virtual std::vector PrecacheData(); + virtual bool SetMIDISubsong(int subsong); + virtual uint32_t *MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time) = 0; + + void StartPlayback(bool looped = true, int looplimit = 0) + { + Tempo = InitialTempo; + LoopLimit = looplimit; + isLooping = looped; + } + + void SkipSysex() { skipSysex = true; } + + bool isValid() const { return Division > 0; } + int getDivision() const { return Division; } + int getInitialTempo() const { return InitialTempo; } + int getTempo() const { return Tempo; } + int getChannelVolume(int ch) const { return ChannelVolumes[ch]; } + void setVolume(int vol) { Volume = vol; } + void setLoopLimit(int lim) { LoopLimit = lim; } + void setTempoCallback(std::function cb) + { + TempoCallback = cb; + } + + void CreateSMF(std::vector &file, int looplimit); + +}; + +// MUS file played with a MIDI stream --------------------------------------- + +class MUSSong2 : public MIDISource +{ +public: + MUSSong2(const uint8_t *data, size_t len); + +protected: + void DoInitialSetup() override; + void DoRestart() override; + bool CheckDone() override; + std::vector PrecacheData() override; + uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time) override; + +private: + std::vector MusData; + uint8_t* MusBuffer; + uint8_t LastVelocity[16]; + size_t MusP, MaxMusP; +}; + + +// MIDI file played with a MIDI stream -------------------------------------- + +class MIDISong2 : public MIDISource +{ +public: + MIDISong2(const uint8_t* data, size_t len); + +protected: + void CheckCaps(int tech) override; + void DoInitialSetup() override; + void DoRestart() override; + bool CheckDone() override; + uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time) override; + +private: + void AdvanceTracks(uint32_t time); + + struct TrackInfo; + + void ProcessInitialMetaEvents (); + uint32_t *SendCommand (uint32_t *event, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom); + TrackInfo *FindNextDue (); + + std::vector MusHeader; + std::vector Tracks; + TrackInfo *TrackDue; + int NumTracks; + int Format; + uint16_t DesignationMask; +}; + +// HMI file played with a MIDI stream --------------------------------------- + +struct AutoNoteOff +{ + uint32_t Delay; + uint8_t Channel, Key; +}; +// Sorry, std::priority_queue, but I want to be able to modify the contents of the heap. +class NoteOffQueue : public std::vector +{ +public: + void AddNoteOff(uint32_t delay, uint8_t channel, uint8_t key); + void AdvanceTime(uint32_t time); + bool Pop(AutoNoteOff &item); + +protected: + void Heapify(); + + unsigned int Parent(unsigned int i) const { return (i + 1u) / 2u - 1u; } + unsigned int Left(unsigned int i) const { return (i + 1u) * 2u - 1u; } + unsigned int Right(unsigned int i) const { return (i + 1u) * 2u; } +}; + +class HMISong : public MIDISource +{ +public: + HMISong(const uint8_t* data, size_t len); + +protected: + + void DoInitialSetup() override; + void DoRestart() override; + bool CheckDone() override; + void CheckCaps(int tech) override; + uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time) override; + +private: + void SetupForHMI(int len); + void SetupForHMP(int len); + void AdvanceTracks(uint32_t time); + + struct TrackInfo; + + void ProcessInitialMetaEvents (); + uint32_t *SendCommand (uint32_t *event, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom); + TrackInfo *FindNextDue (); + + static uint32_t ReadVarLenHMI(TrackInfo *); + static uint32_t ReadVarLenHMP(TrackInfo *); + + std::vector MusHeader; + int NumTracks; + std::vector Tracks; + TrackInfo *TrackDue; + TrackInfo *FakeTrack; + uint32_t (*ReadVarLen)(TrackInfo *); + NoteOffQueue NoteOffs; +}; + +// XMI file played with a MIDI stream --------------------------------------- + +class XMISong : public MIDISource +{ +public: + XMISong(const uint8_t* data, size_t len); + +protected: + bool SetMIDISubsong(int subsong) override; + void DoInitialSetup() override; + void DoRestart() override; + bool CheckDone() override; + uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time) override; + +private: + struct TrackInfo; + enum EventSource { EVENT_None, EVENT_Real, EVENT_Fake }; + + int FindXMIDforms(const uint8_t *chunk, int len, TrackInfo *songs) const; + void FoundXMID(const uint8_t *chunk, int len, TrackInfo *song) const; + void AdvanceSong(uint32_t time); + + void ProcessInitialMetaEvents(); + uint32_t *SendCommand (uint32_t *event, EventSource track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom); + EventSource FindNextDue(); + + std::vector MusHeader; + int NumSongs; + std::vector Songs; + TrackInfo *CurrSong; + NoteOffQueue NoteOffs; + EventSource EventDue; +}; + +// MIDS file played with a MIDI Stream + +class MIDSSong : public MIDISource +{ +public: + MIDSSong(const uint8_t* data, size_t len); + +protected: + void DoInitialSetup() override; + void DoRestart() override; + bool CheckDone() override; + uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time) override; + +private: + std::vector MidsBuffer; + size_t MidsP, MaxMidsP; + int FormatFlags; + + void ProcessInitialTempoEvents(); +}; + +#endif /* midisources_h */ diff --git a/source/midisources/midisource_hmi.cpp b/source/midisources/midisource_hmi.cpp new file mode 100644 index 000000000..791954eda --- /dev/null +++ b/source/midisources/midisource_hmi.cpp @@ -0,0 +1,996 @@ +/* +** music_hmi_midiout.cpp +** Code to let ZDoom play HMI MIDI music through the MIDI streaming API. +** +**--------------------------------------------------------------------------- +** Copyright 2010 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include "midisource.h" +#include "zmusic/zmusic_internal.h" +#include "zmusic/m_swap.h" + +// MACROS ------------------------------------------------------------------ + +#define HMP_NEW_DATE "013195" +#define HMI_SONG_MAGIC "HMI-MIDISONG061595" +#define TRACK_MAGIC "HMI-MIDITRACK" + +// Used by SendCommand to check for unexpected end-of-track conditions. +#define CHECK_FINISHED \ + if (track->TrackP >= track->MaxTrackP) \ + { \ + track->Finished = true; \ + return events; \ + } + +// In song header +#define HMI_DIVISION_OFFSET 0xD4 +#define HMI_TRACK_COUNT_OFFSET 0xE4 +#define HMI_TRACK_DIR_PTR_OFFSET 0xE8 + +#define HMP_DIVISION_OFFSET 0x38 +#define HMP_TRACK_COUNT_OFFSET 0x30 +#define HMP_DESIGNATIONS_OFFSET 0x94 +#define HMP_TRACK_OFFSET_0 0x308 // original HMP +#define HMP_TRACK_OFFSET_1 0x388 // newer HMP + +// In track header +#define HMITRACK_DATA_PTR_OFFSET 0x57 +#define HMITRACK_DESIGNATION_OFFSET 0x99 + +#define HMPTRACK_LEN_OFFSET 4 +#define HMPTRACK_DESIGNATION_OFFSET 8 +#define HMPTRACK_MIDI_DATA_OFFSET 12 + +#define NUM_HMP_DESIGNATIONS 5 +#define NUM_HMI_DESIGNATIONS 8 + +// MIDI device types for designation +#define HMI_DEV_GM 0xA000 // Generic General MIDI (not a real device) +#define HMI_DEV_MPU401 0xA001 // MPU-401, Roland Sound Canvas, Ensoniq SoundScape, Rolad RAP-10 +#define HMI_DEV_OPL2 0xA002 // SoundBlaster (Pro), ESS AudioDrive +#define HMI_DEV_MT32 0xA004 // MT-32 +#define HMI_DEV_SBAWE32 0xA008 // SoundBlaster AWE32 +#define HMI_DEV_OPL3 0xA009 // SoundBlaster 16, Microsoft Sound System, Pro Audio Spectrum 16 +#define HMI_DEV_GUS 0xA00A // Gravis UltraSound, Gravis UltraSound Max/Ace + +// TYPES ------------------------------------------------------------------- + +struct HMISong::TrackInfo +{ + const uint8_t *TrackBegin; + size_t TrackP; + size_t MaxTrackP; + uint32_t Delay; + uint32_t PlayedTime; + uint16_t Designation[NUM_HMI_DESIGNATIONS]; + bool Enabled; + bool Finished; + uint8_t RunningStatus; + + uint32_t ReadVarLenHMI(); + uint32_t ReadVarLenHMP(); +}; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// HMISong Constructor +// +// Buffers the file and does some validation of the HMI header. +// +//========================================================================== + +HMISong::HMISong (const uint8_t *data, size_t len) +{ + if (len < 0x100) + { // Way too small to be HMI. + return; + } + MusHeader.resize(len); + memcpy(MusHeader.data(), data, len); + NumTracks = 0; + + // Do some validation of the MIDI file + if (memcmp(&MusHeader[0], HMI_SONG_MAGIC, sizeof(HMI_SONG_MAGIC)) == 0) + { + SetupForHMI((int)len); + } + else if (memcmp(&MusHeader[0], "HMIMIDIP", 8) == 0) + { + SetupForHMP((int)len); + } +} + +//========================================================================== +// +// HMISong :: SetupForHMI +// +//========================================================================== + +void HMISong::SetupForHMI(int len) +{ + int i, p; + + auto MusPtr = &MusHeader[0]; + + ReadVarLen = ReadVarLenHMI; + NumTracks = GetShort(MusPtr + HMI_TRACK_COUNT_OFFSET); + + if (NumTracks <= 0) + { + return; + } + + // The division is the number of pulses per quarter note (PPQN). + // HMI files have two values here, a full value and a quarter value. Some games, + // notably Quarantines, have identical values for some reason, so it's safer to + // use the quarter value and multiply it by four than to trust the full value. + Division = GetShort(MusPtr + HMI_DIVISION_OFFSET) << 2; + Tempo = InitialTempo = 4000000; + + Tracks.resize(NumTracks + 1); + int track_dir = GetInt(MusPtr + HMI_TRACK_DIR_PTR_OFFSET); + + // Gather information about each track + for (i = 0, p = 0; i < NumTracks; ++i) + { + int start = GetInt(MusPtr + track_dir + i*4); + int tracklen, datastart; + + if (start > len - HMITRACK_DESIGNATION_OFFSET - 4) + { // Track is incomplete. + continue; + } + + // BTW, HMI does not actually check the track header. + if (memcmp(MusPtr + start, TRACK_MAGIC, 13) != 0) + { + continue; + } + + // The track ends where the next one begins. If this is the + // last track, then it ends at the end of the file. + if (i == NumTracks - 1) + { + tracklen = len - start; + } + else + { + tracklen = GetInt(MusPtr + track_dir + i*4 + 4) - start; + } + // Clamp incomplete tracks to the end of the file. + tracklen = std::min(tracklen, len - start); + if (tracklen <= 0) + { + continue; + } + + // Offset to actual MIDI events. + datastart = GetInt(MusPtr + start + HMITRACK_DATA_PTR_OFFSET); + tracklen -= datastart; + if (tracklen <= 0) + { + continue; + } + + // Store track information + Tracks[p].TrackBegin = MusPtr + start + datastart; + Tracks[p].TrackP = 0; + Tracks[p].MaxTrackP = tracklen; + + // Retrieve track designations. We can't check them yet, since we have not yet + // connected to the MIDI device. + for (int ii = 0; ii < NUM_HMI_DESIGNATIONS; ++ii) + { + Tracks[p].Designation[ii] = GetShort(MusPtr + start + HMITRACK_DESIGNATION_OFFSET + ii*2); + } + + p++; + } + + // In case there were fewer actual chunks in the file than the + // header specified, update NumTracks with the current value of p. + NumTracks = p; +} + +//========================================================================== +// +// HMISong :: SetupForHMP +// +//========================================================================== + +void HMISong::SetupForHMP(int len) +{ + int track_data; + int i, p; + + auto MusPtr = &MusHeader[0]; + + ReadVarLen = ReadVarLenHMP; + if (MusPtr[8] == 0) + { + track_data = HMP_TRACK_OFFSET_0; + } + else if (memcmp(MusPtr + 8, HMP_NEW_DATE, sizeof(HMP_NEW_DATE)) == 0) + { + track_data = HMP_TRACK_OFFSET_1; + } + else + { // unknown HMIMIDIP version + return; + } + + NumTracks = GetInt(MusPtr + HMP_TRACK_COUNT_OFFSET); + + if (NumTracks <= 0) + { + return; + } + + // The division is the number of pulses per quarter note (PPQN). + Division = GetInt(MusPtr + HMP_DIVISION_OFFSET); + Tempo = InitialTempo = 1000000; + + Tracks.resize(NumTracks + 1); + + // Gather information about each track + for (i = 0, p = 0; i < NumTracks; ++i) + { + int start = track_data; + int tracklen; + + if (start > len - HMPTRACK_MIDI_DATA_OFFSET) + { // Track is incomplete. + break; + } + + tracklen = GetInt(MusPtr + start + HMPTRACK_LEN_OFFSET); + track_data += tracklen; + + // Clamp incomplete tracks to the end of the file. + tracklen = std::min(tracklen, len - start); + if (tracklen <= 0) + { + continue; + } + + // Subtract track header size. + tracklen -= HMPTRACK_MIDI_DATA_OFFSET; + if (tracklen <= 0) + { + continue; + } + + // Store track information + Tracks[p].TrackBegin = MusPtr + start + HMPTRACK_MIDI_DATA_OFFSET; + Tracks[p].TrackP = 0; + Tracks[p].MaxTrackP = tracklen; + + // Retrieve track designations. We can't check them yet, since we have not yet + // connected to the MIDI device. +#if 0 + // This is completely a guess based on knowledge of how designations work with + // HMI files. Some songs contain nothing but zeroes for this data, so I'd rather + // not go around using it without confirmation. + + Printf("Track %d: %d %08x %d: \034I", i, GetInt(MusPtr + start), + GetInt(MusPtr + start + 4), GetInt(MusPtr + start + 8)); + + int designations = HMP_DESIGNATIONS_OFFSET + + GetInt(MusPtr + start + HMPTRACK_DESIGNATION_OFFSET) * 4 * NUM_HMP_DESIGNATIONS; + for (int ii = 0; ii < NUM_HMP_DESIGNATIONS; ++ii) + { + Printf(" %04x", GetInt(MusPtr + designations + ii*4)); + } + Printf("\n"); +#endif + Tracks[p].Designation[0] = HMI_DEV_GM; + Tracks[p].Designation[1] = HMI_DEV_GUS; + Tracks[p].Designation[2] = HMI_DEV_OPL2; + Tracks[p].Designation[3] = 0; + + p++; + } + + // In case there were fewer actual chunks in the file than the + // header specified, update NumTracks with the current value of p. + NumTracks = p; +} + +//========================================================================== +// +// HMISong :: CheckCaps +// +// Check track designations and disable tracks that have not been +// designated for the device we will be playing on. +// +//========================================================================== + +void HMISong::CheckCaps(int tech) +{ + // What's the equivalent HMI device for our technology? + if (tech == MIDIDEV_FMSYNTH) + { + tech = HMI_DEV_OPL3; + } + else if (tech == MIDIDEV_MIDIPORT) + { + tech = HMI_DEV_MPU401; + } + else + { // Good enough? Or should we just say we're GM. + tech = HMI_DEV_SBAWE32; + } + + for (int i = 0; i < NumTracks; ++i) + { + Tracks[i].Enabled = false; + // Track designations are stored in a 0-terminated array. + for (unsigned int j = 0; j < NUM_HMI_DESIGNATIONS && Tracks[i].Designation[j] != 0; ++j) + { + if (Tracks[i].Designation[j] == tech) + { + Tracks[i].Enabled = true; + } + // If a track is designated for device 0xA000, it will be played by a MIDI + // driver for device types 0xA000, 0xA001, and 0xA008. Why this does not + // include the GUS, I do not know. + else if (Tracks[i].Designation[j] == HMI_DEV_GM) + { + Tracks[i].Enabled = (tech == HMI_DEV_MPU401 || tech == HMI_DEV_SBAWE32); + } + // If a track is designated for device 0xA002, it will be played by a MIDI + // driver for device types 0xA002 or 0xA009. + else if (Tracks[i].Designation[j] == HMI_DEV_OPL2) + { + Tracks[i].Enabled = (tech == HMI_DEV_OPL3); + } + // Any other designation must match the specific MIDI driver device number. + // (Which we handled first above.) + + if (Tracks[i].Enabled) + { // This track's been enabled, so we can stop checking other designations. + break; + } + } + } +} + + +//========================================================================== +// +// HMISong :: DoInitialSetup +// +// Sets the starting channel volumes. +// +//========================================================================== + +void HMISong :: DoInitialSetup() +{ + for (int i = 0; i < 16; ++i) + { + ChannelVolumes[i] = 100; + } +} + +//========================================================================== +// +// HMISong :: DoRestart +// +// Rewinds every track. +// +//========================================================================== + +void HMISong :: DoRestart() +{ + int i; + + // Set initial state. + FakeTrack = &Tracks[NumTracks]; + NoteOffs.clear(); + for (i = 0; i <= NumTracks; ++i) + { + Tracks[i].TrackP = 0; + Tracks[i].Finished = false; + Tracks[i].RunningStatus = 0; + Tracks[i].PlayedTime = 0; + } + ProcessInitialMetaEvents (); + for (i = 0; i < NumTracks; ++i) + { + Tracks[i].Delay = ReadVarLen(&Tracks[i]); + } + Tracks[i].Delay = 0; // for the FakeTrack + Tracks[i].Enabled = true; + TrackDue = Tracks.data(); + TrackDue = FindNextDue(); +} + +//========================================================================== +// +// HMISong :: CheckDone +// +//========================================================================== + +bool HMISong::CheckDone() +{ + return TrackDue == nullptr; +} + +//========================================================================== +// +// HMISong :: MakeEvents +// +// Copies MIDI events from the file and puts them into a MIDI stream +// buffer. Returns the new position in the buffer. +// +//========================================================================== + +uint32_t *HMISong::MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time) +{ + uint32_t *start_events; + uint32_t tot_time = 0; + uint32_t time = 0; + uint32_t delay; + + start_events = events; + while (TrackDue && events < max_event_p && tot_time <= max_time) + { + // It's possible that this tick may be nothing but meta-events and + // not generate any real events. Repeat this until we actually + // get some output so we don't send an empty buffer to the MIDI + // device. + do + { + delay = TrackDue->Delay; + time += delay; + // Advance time for all tracks by the amount needed for the one up next. + tot_time += delay * Tempo / Division; + AdvanceTracks(delay); + // Play all events for this tick. + do + { + bool sysex_noroom = false; + uint32_t *new_events = SendCommand(events, TrackDue, time, max_event_p - events, sysex_noroom); + if (sysex_noroom) + { + return events; + } + TrackDue = FindNextDue(); + if (new_events != events) + { + time = 0; + } + events = new_events; + } + while (TrackDue && TrackDue->Delay == 0 && events < max_event_p); + } + while (start_events == events && TrackDue); + time = 0; + } + return events; +} + +//========================================================================== +// +// HMISong :: AdvanceTracks +// +// Advances time for all tracks by the specified amount. +// +//========================================================================== + +void HMISong::AdvanceTracks(uint32_t time) +{ + for (int i = 0; i <= NumTracks; ++i) + { + if (Tracks[i].Enabled && !Tracks[i].Finished) + { + Tracks[i].Delay -= time; + Tracks[i].PlayedTime += time; + } + } + NoteOffs.AdvanceTime(time); +} + +//========================================================================== +// +// HMISong :: SendCommand +// +// Places a single MIDIEVENT in the event buffer. +// +//========================================================================== + +uint32_t *HMISong::SendCommand (uint32_t *events, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom) +{ + uint32_t len; + uint8_t event, data1 = 0, data2 = 0; + + // If the next event comes from the fake track, pop an entry off the note-off queue. + if (track == FakeTrack) + { + AutoNoteOff off; + NoteOffs.Pop(off); + events[0] = delay; + events[1] = 0; + events[2] = MIDI_NOTEON | off.Channel | (off.Key << 8); + return events + 3; + } + + sysex_noroom = false; + size_t start_p = track->TrackP; + + CHECK_FINISHED + event = track->TrackBegin[track->TrackP++]; + CHECK_FINISHED + + // The actual event type will be filled in below. If it's not a NOP, + // the events pointer will be advanced once the actual event is written. + // Otherwise, we do it at the end of the function. + events[0] = delay; + events[1] = 0; + events[2] = MEVENT_NOP << 24; + + if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND && event != 0xFe) + { + // Normal short message + if ((event & 0xF0) == 0xF0) + { + if (MIDI_CommonLengths[event & 15] > 0) + { + data1 = track->TrackBegin[track->TrackP++]; + if (MIDI_CommonLengths[event & 15] > 1) + { + data2 = track->TrackBegin[track->TrackP++]; + } + } + } + else if ((event & 0x80) == 0) + { + data1 = event; + event = track->RunningStatus; + } + else + { + track->RunningStatus = event; + data1 = track->TrackBegin[track->TrackP++]; + } + + CHECK_FINISHED + + if (MIDI_EventLengths[(event&0x70)>>4] == 2) + { + data2 = track->TrackBegin[track->TrackP++]; + } + + // Monitor channel volume controller changes. + if ((event & 0x70) == (MIDI_CTRLCHANGE & 0x70) && data1 == 7) + { + data2 = VolumeControllerChange(event & 15, data2); + } + + if (event != MIDI_META) + { + events[2] = event | (data1<<8) | (data2<<16); + } + + if (ReadVarLen == ReadVarLenHMI && (event & 0x70) == (MIDI_NOTEON & 0x70)) + { // HMI note on events include the time until an implied note off event. + NoteOffs.AddNoteOff(track->ReadVarLenHMI(), event & 0x0F, data1); + } + } + else + { + // SysEx events could potentially not have enough room in the buffer... + if (event == MIDI_SYSEX || event == MIDI_SYSEXEND) + { + len = ReadVarLen(track); + if (len >= (MAX_MIDI_EVENTS-1)*3*4 || skipSysex) + { // This message will never fit. Throw it away. + track->TrackP += len; + } + else if (len + 12 >= (size_t)room * 4) + { // Not enough room left in this buffer. Backup and wait for the next one. + track->TrackP = start_p; + sysex_noroom = true; + return events; + } + else + { + uint8_t *msg = (uint8_t *)&events[3]; + if (event == MIDI_SYSEX) + { // Need to add the SysEx marker to the message. + events[2] = (MEVENT_LONGMSG << 24) | (len + 1); + *msg++ = MIDI_SYSEX; + } + else + { + events[2] = (MEVENT_LONGMSG << 24) | len; + } + memcpy(msg, &track->TrackBegin[track->TrackP], len); + msg += len; + // Must pad with 0 + while ((size_t)msg & 3) + { + *msg++ = 0; + } + track->TrackP += len; + } + } + else if (event == MIDI_META) + { + // It's a meta-event + event = track->TrackBegin[track->TrackP++]; + CHECK_FINISHED + len = ReadVarLen(track); + CHECK_FINISHED + + if (track->TrackP + len <= track->MaxTrackP) + { + switch (event) + { + case MIDI_META_EOT: + track->Finished = true; + break; + + case MIDI_META_TEMPO: + Tempo = + (track->TrackBegin[track->TrackP+0]<<16) | + (track->TrackBegin[track->TrackP+1]<<8) | + (track->TrackBegin[track->TrackP+2]); + events[0] = delay; + events[1] = 0; + events[2] = (MEVENT_TEMPO << 24) | Tempo; + break; + } + track->TrackP += len; + if (track->TrackP == track->MaxTrackP) + { + track->Finished = true; + } + } + else + { + track->Finished = true; + } + } + else if (event == 0xFE) + { // Skip unknown HMI events. + event = track->TrackBegin[track->TrackP++]; + CHECK_FINISHED + if (event == 0x13 || event == 0x15) + { + track->TrackP += 6; + } + else if (event == 0x12 || event == 0x14) + { + track->TrackP += 2; + } + else if (event == 0x10) + { + track->TrackP += 2; + CHECK_FINISHED + track->TrackP += track->TrackBegin[track->TrackP] + 5; + CHECK_FINISHED + } + else + { // No idea. + track->Finished = true; + } + } + } + if (!track->Finished) + { + track->Delay = ReadVarLen(track); + } + // Advance events pointer unless this is a non-delaying NOP. + if (events[0] != 0 || MEVENT_EVENTTYPE(events[2]) != MEVENT_NOP) + { + if (MEVENT_EVENTTYPE(events[2]) == MEVENT_LONGMSG) + { + events += 3 + ((MEVENT_EVENTPARM(events[2]) + 3) >> 2); + } + else + { + events += 3; + } + } + return events; +} + +//========================================================================== +// +// HMISong :: ProcessInitialMetaEvents +// +// Handle all the meta events at the start of each track. +// +//========================================================================== + +void HMISong::ProcessInitialMetaEvents () +{ + TrackInfo *track; + int i; + uint8_t event; + uint32_t len; + + for (i = 0; i < NumTracks; ++i) + { + track = &Tracks[i]; + while (!track->Finished && + track->TrackP < track->MaxTrackP - 4 && + track->TrackBegin[track->TrackP] == 0 && + track->TrackBegin[track->TrackP+1] == 0xFF) + { + event = track->TrackBegin[track->TrackP+2]; + track->TrackP += 3; + len = ReadVarLen(track); + if (track->TrackP + len <= track->MaxTrackP) + { + switch (event) + { + case MIDI_META_EOT: + track->Finished = true; + break; + + case MIDI_META_TEMPO: + SetTempo( + (track->TrackBegin[track->TrackP+0]<<16) | + (track->TrackBegin[track->TrackP+1]<<8) | + (track->TrackBegin[track->TrackP+2]) + ); + break; + } + } + track->TrackP += len; + } + if (track->TrackP >= track->MaxTrackP - 4) + { + track->Finished = true; + } + } +} + +//========================================================================== +// +// HMISong :: ReadVarLenHMI static +// +//========================================================================== + +uint32_t HMISong::ReadVarLenHMI(TrackInfo *track) +{ + return track->ReadVarLenHMI(); +} + +//========================================================================== +// +// HMISong :: ReadVarLenHMP static +// +//========================================================================== + +uint32_t HMISong::ReadVarLenHMP(TrackInfo *track) +{ + return track->ReadVarLenHMP(); +} + +//========================================================================== +// +// HMISong :: TrackInfo :: ReadVarLenHMI +// +// Reads a variable-length SMF number. +// +//========================================================================== + +uint32_t HMISong::TrackInfo::ReadVarLenHMI() +{ + uint32_t time = 0, t = 0x80; + + while ((t & 0x80) && TrackP < MaxTrackP) + { + t = TrackBegin[TrackP++]; + time = (time << 7) | (t & 127); + } + return time; +} + +//========================================================================== +// +// HMISong :: TrackInfo :: ReadVarLenHMP +// +// Reads a variable-length HMP number. This is similar to the standard SMF +// variable length number, except it's stored little-endian, and the high +// bit set means the number is done. +// +//========================================================================== + +uint32_t HMISong::TrackInfo::ReadVarLenHMP() +{ + uint32_t time = 0; + uint8_t t = 0; + int off = 0; + + while (!(t & 0x80) && TrackP < MaxTrackP) + { + t = TrackBegin[TrackP++]; + time |= (t & 127) << off; + off += 7; + } + return time; +} + +//========================================================================== +// +// NoteOffQueue :: AddNoteOff +// +//========================================================================== + +void NoteOffQueue::AddNoteOff(uint32_t delay, uint8_t channel, uint8_t key) +{ + uint32_t i = (uint32_t)size(); + resize(i + 1); + while (i > 0 && (*this)[Parent(i)].Delay > delay) + { + (*this)[i] = (*this)[Parent(i)]; + i = Parent(i); + } + (*this)[i].Delay = delay; + (*this)[i].Channel = channel; + (*this)[i].Key = key; +} + +//========================================================================== +// +// NoteOffQueue :: Pop +// +//========================================================================== + +bool NoteOffQueue::Pop(AutoNoteOff &item) +{ + if (size() > 0) + { + item = front(); + front() = back(); + pop_back(); + Heapify(); + return true; + } + return false; +} + +//========================================================================== +// +// NoteOffQueue :: AdvanceTime +// +//========================================================================== + +void NoteOffQueue::AdvanceTime(uint32_t time) +{ + // Because the time is decreasing by the same amount for every entry, + // the heap property is maintained. + for (auto &item : *this) + { + assert(item.Delay >= time); + item.Delay -= time; + } +} + +//========================================================================== +// +// NoteOffQueue :: Heapify +// +//========================================================================== + +void NoteOffQueue::Heapify() +{ + unsigned int i = 0; + for (;;) + { + unsigned int l = Left(i); + unsigned int r = Right(i); + unsigned int smallest = i; + if (l < (unsigned)size() && (*this)[l].Delay < (*this)[i].Delay) + { + smallest = l; + } + if (r < (unsigned)size() && (*this)[r].Delay < (*this)[smallest].Delay) + { + smallest = r; + } + if (smallest == i) + { + break; + } + std::swap((*this)[i], (*this)[smallest]); + i = smallest; + } +} + +//========================================================================== +// +// HMISong :: FindNextDue +// +// Scans every track for the next event to play. Returns nullptr if all events +// have been consumed. +// +//========================================================================== + +HMISong::TrackInfo *HMISong::FindNextDue () +{ + TrackInfo *track; + uint32_t best; + int i; + + // Give precedence to whichever track last had events taken from it. + if (TrackDue != FakeTrack && !TrackDue->Finished && TrackDue->Delay == 0) + { + return TrackDue; + } + if (TrackDue == FakeTrack && NoteOffs.size() != 0 && NoteOffs[0].Delay == 0) + { + FakeTrack->Delay = 0; + return FakeTrack; + } + + // Check regular tracks. + track = nullptr; + best = 0xFFFFFFFF; + for (i = 0; i < NumTracks; ++i) + { + if (Tracks[i].Enabled && !Tracks[i].Finished && Tracks[i].Delay < best) + { + best = Tracks[i].Delay; + track = &Tracks[i]; + } + } + // Check automatic note-offs. + if (NoteOffs.size() != 0 && NoteOffs[0].Delay <= best) + { + FakeTrack->Delay = NoteOffs[0].Delay; + return FakeTrack; + } + return track; +} + diff --git a/source/midisources/midisource_mids.cpp b/source/midisources/midisource_mids.cpp new file mode 100644 index 000000000..f12f89613 --- /dev/null +++ b/source/midisources/midisource_mids.cpp @@ -0,0 +1,192 @@ +/* +** midisource_mids.cpp +** Code to let ZMusic play MIDS MIDI music through the MIDI streaming API. +** +**--------------------------------------------------------------------------- +** Copyright 2020 Cacodemon345 +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include "midisource.h" +#include "zmusic/m_swap.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// MIDSSong constructor +// +// Reads the buffers from the file and validates the MIDS header +// +//========================================================================== + +MIDSSong::MIDSSong(const uint8_t* data, size_t len) +{ + if (len <= 52) + return; + + if ((len % 4) != 0) + return; + + // Validate the header first. + if (data[12] != 'f' || data[13] != 'm' || data[14] != 't' || data[15] != ' ') + { + return; + } + int headerSize = LittleLong(GetInt(&data[16])); + if (headerSize != 12) return; + Division = LittleLong(GetInt(&data[20])); + FormatFlags = LittleLong(GetInt(&data[28])); + // Validate the data chunk. + if (data[32] != 'd' || data[33] != 'a' || data[34] != 't' || data[35] != 'a') + { + return; + } + int NumBlocks = LittleLong(GetInt(&data[40])); + const uint32_t* midiData = (const uint32_t*)&data[44]; + uint32_t tkStart = 0; + uint32_t cbBuffer = 0; + while (NumBlocks-- > 0) + { + tkStart = LittleLong(*midiData); + cbBuffer = LittleLong(*(midiData + 1)); + midiData += 2; + if ((cbBuffer % (FormatFlags ? 8 : 12)) != 0) return; + MidsBuffer.insert(MidsBuffer.end(), midiData, midiData + (cbBuffer / 4)); + midiData += cbBuffer / 4; + } + MidsP = 0; + MaxMidsP = MidsBuffer.size() - 1; + for (auto& curMidiData : MidsBuffer) + { + curMidiData = LittleLong(curMidiData); + } +} + +//========================================================================== +// +// MIDSSong :: DoInitialSetup +// +// Sets the starting channel volumes. +// +//========================================================================== + +void MIDSSong::DoInitialSetup() +{ + for (int i = 0; i < 16; ++i) + { + ChannelVolumes[i] = 100; + } +} + +//========================================================================== +// +// MIDSSong :: CheckDone +// +//========================================================================== + +bool MIDSSong::CheckDone() +{ + return MidsP >= MaxMidsP; +} + +//========================================================================== +// +// MIDSSong :: DoRestart() +// +// Rewinds the song +// +//========================================================================== + +void MIDSSong::DoRestart() +{ + MidsP = 0; + ProcessInitialTempoEvents(); +} + +//========================================================================== +// +// MIDSSong :: ProcessInitialTempoEvents() +// +// Process initial tempo events at the start of the song. +// +//========================================================================== + +void MIDSSong::ProcessInitialTempoEvents() +{ + if (MEVENT_EVENTTYPE(MidsBuffer[FormatFlags ? 1 : 2]) == MEVENT_TEMPO) + { + SetTempo(MEVENT_EVENTPARM(MidsBuffer[FormatFlags ? 1 : 2])); + } +} + +//========================================================================== +// +// MUSSong2 :: MakeEvents +// +// Puts MIDS events into a MIDI stream +// buffer. Returns the new position in the buffer. +// +//========================================================================== + +uint32_t* MIDSSong::MakeEvents(uint32_t* events, uint32_t* max_event_p, uint32_t max_time) +{ + uint32_t time = 0; + uint32_t tot_time = 0; + + max_time = max_time * Division / Tempo; + while (events < max_event_p && tot_time <= max_time) + { + events[0] = time = MidsBuffer[MidsP++]; + events[1] = FormatFlags ? 0 : MidsBuffer[MidsP++]; + events[2] = MidsBuffer[MidsP++]; + events += 3; + tot_time += time; + if (MidsP >= MaxMidsP) break; + } + return events; +} diff --git a/source/midisources/midisource_mus.cpp b/source/midisources/midisource_mus.cpp new file mode 100644 index 000000000..bac5de9ca --- /dev/null +++ b/source/midisources/midisource_mus.cpp @@ -0,0 +1,365 @@ +/* +** music_mus_midiout.cpp +** Code to let ZDoom play MUS music through the MIDI streaming API. +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include "midisource.h" +#include "zmusic/m_swap.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +int MUSHeaderSearch(const uint8_t *head, int len); + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static const uint8_t CtrlTranslate[15] = +{ + 0, // program change + 0, // bank select + 1, // modulation pot + 7, // volume + 10, // pan pot + 11, // expression pot + 91, // reverb depth + 93, // chorus depth + 64, // sustain pedal + 67, // soft pedal + 120, // all sounds off + 123, // all notes off + 126, // mono + 127, // poly + 121, // reset all controllers +}; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// MUSSong2 Constructor +// +// Performs some validity checks on the MUS file, buffers it, and creates +// the playback thread control events. +// +//========================================================================== + +MUSSong2::MUSSong2 (const uint8_t *data, size_t len) +{ + int start; + + // To tolerate sloppy wads (diescum.wad, I'm looking at you), we search + // the first 32 bytes of the file for a signature. My guess is that DMX + // does no validation whatsoever and just assumes it was passed a valid + // MUS file, since where the header is offset affects how it plays. + start = MUSHeaderSearch(data, 32); + if (start < 0) + { + return; + } + data += start; + len -= start; + + // Read the remainder of the song. + if (len < sizeof(MUSHeader)) + { // It's too short. + return; + } + MusData.resize(len); + memcpy(MusData.data(), data, len); + auto MusHeader = (MUSHeader*)MusData.data(); + + // Do some validation of the MUS file. + if (LittleShort(MusHeader->NumChans) > 15) + { + return; + } + + MusBuffer = MusData.data() + LittleShort(MusHeader->SongStart); + MaxMusP = std::min(LittleShort(MusHeader->SongLen), int(len) - LittleShort(MusHeader->SongStart)); + Division = 140; + Tempo = InitialTempo = 1000000; +} + +//========================================================================== +// +// MUSSong2 :: DoInitialSetup +// +// Sets up initial velocities and channel volumes. +// +//========================================================================== + +void MUSSong2::DoInitialSetup() +{ + for (int i = 0; i < 16; ++i) + { + LastVelocity[i] = 127; + ChannelVolumes[i] = 100; + } +} + +//========================================================================== +// +// MUSSong2 :: DoRestart +// +// Rewinds the song. +// +//========================================================================== + +void MUSSong2::DoRestart() +{ + MusP = 0; +} + +//========================================================================== +// +// MUSSong2 :: CheckDone +// +//========================================================================== + +bool MUSSong2::CheckDone() +{ + return MusP >= MaxMusP; +} + +//========================================================================== +// +// MUSSong2 :: Precache +// +// MUS songs contain information in their header for exactly this purpose. +// +//========================================================================== + +std::vector MUSSong2::PrecacheData() +{ + auto MusHeader = (MUSHeader*)MusData.data(); + std::vector work; + const uint8_t *used = MusData.data() + sizeof(MUSHeader) / sizeof(uint8_t); + int i, k; + + int numinstr = LittleShort(MusHeader->NumInstruments); + work.reserve(LittleShort(MusHeader->NumInstruments)); + for (i = k = 0; i < numinstr; ++i) + { + uint8_t instr = used[k++]; + uint16_t val; + if (instr < 128) + { + val = instr; + } + else if (instr >= 135 && instr <= 188) + { // Percussions are 100-based, not 128-based, eh? + val = instr - 100 + (1 << 14); + } + else + { + // skip it. + val = used[k++]; + k += val; + continue; + } + + int numbanks = used[k++]; + if (numbanks > 0) + { + for (int b = 0; b < numbanks; b++) + { + work.push_back(val | (used[k++] << 7)); + } + } + else + { + work.push_back(val); + } + } + return work; +} + +//========================================================================== +// +// MUSSong2 :: MakeEvents +// +// Translates MUS events into MIDI events and puts them into a MIDI stream +// buffer. Returns the new position in the buffer. +// +//========================================================================== + +uint32_t *MUSSong2::MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time) +{ + uint32_t tot_time = 0; + uint32_t time = 0; + auto MusHeader = (MUSHeader*)MusData.data(); + + max_time = max_time * Division / Tempo; + + while (events < max_event_p && tot_time <= max_time) + { + uint8_t mid1, mid2; + uint8_t channel; + uint8_t t = 0, status; + uint8_t event = MusBuffer[MusP++]; + + if ((event & 0x70) != MUS_SCOREEND) + { + t = MusBuffer[MusP++]; + } + channel = event & 15; + + // Map MUS channels to MIDI channels + if (channel == 15) + { + channel = 9; + } + else if (channel >= 9) + { + channel = channel + 1; + } + + status = channel; + + switch (event & 0x70) + { + case MUS_NOTEOFF: + status |= MIDI_NOTEON; + mid1 = t; + mid2 = 0; + break; + + case MUS_NOTEON: + status |= MIDI_NOTEON; + mid1 = t & 127; + if (t & 128) + { + LastVelocity[channel] = MusBuffer[MusP++]; + } + mid2 = LastVelocity[channel]; + break; + + case MUS_PITCHBEND: + status |= MIDI_PITCHBEND; + mid1 = (t & 1) << 6; + mid2 = (t >> 1) & 127; + break; + + case MUS_SYSEVENT: + status |= MIDI_CTRLCHANGE; + mid1 = CtrlTranslate[t]; + mid2 = t == 12 ? LittleShort(MusHeader->NumChans) : 0; + break; + + case MUS_CTRLCHANGE: + if (t == 0) + { // program change + status |= MIDI_PRGMCHANGE; + mid1 = MusBuffer[MusP++]; + mid2 = 0; + } + else + { + status |= MIDI_CTRLCHANGE; + mid1 = CtrlTranslate[t]; + mid2 = MusBuffer[MusP++]; + if (mid1 == 7) + { // Clamp volume to 127, since DMX apparently allows 8-bit volumes. + // Fix courtesy of Gez, courtesy of Ben Ryves. + mid2 = VolumeControllerChange(channel, std::min(mid2, 0x7F)); + } + } + break; + + case MUS_SCOREEND: + default: + MusP = MaxMusP; + goto end; + } + + events[0] = time; // dwDeltaTime + events[1] = 0; // dwStreamID + events[2] = status | (mid1 << 8) | (mid2 << 16); + events += 3; + + time = 0; + if (event & 128) + { + do + { + t = MusBuffer[MusP++]; + time = (time << 7) | (t & 127); + } + while (t & 128); + } + tot_time += time; + } +end: + if (time != 0) + { + events[0] = time; // dwDeltaTime + events[1] = 0; // dwStreamID + events[2] = MEVENT_NOP << 24; // dwEvent + events += 3; + } + return events; +} + +//========================================================================== +// +// MUSHeaderSearch +// +// Searches for the MUS header within the given memory block, returning +// the offset it was found at, or -1 if not present. +// +//========================================================================== + +int MUSHeaderSearch(const uint8_t *head, int len) +{ + len -= 4; + for (int i = 0; i <= len; ++i) + { + if (head[i+0] == 'M' && head[i+1] == 'U' && head[i+2] == 'S' && head[i+3] == 0x1A) + { + return i; + } + } + return -1; +} diff --git a/source/midisources/midisource_smf.cpp b/source/midisources/midisource_smf.cpp new file mode 100644 index 000000000..e9b4615a9 --- /dev/null +++ b/source/midisources/midisource_smf.cpp @@ -0,0 +1,801 @@ +/* +** music_midi_midiout.cpp +** Code to let ZDoom play SMF MIDI music through the MIDI streaming API. +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +** This file also supports the Apogee Sound System's EMIDI files. That +** basically means you can play the Duke3D songs without any editing and +** have them sound right. +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "midisource.h" +#include "zmusic/zmusic_internal.h" + +// MACROS ------------------------------------------------------------------ + +// Used by SendCommand to check for unexpected end-of-track conditions. +#define CHECK_FINISHED \ + if (track->TrackP >= track->MaxTrackP) \ + { \ + track->Finished = true; \ + return events; \ + } + +// TYPES ------------------------------------------------------------------- + +struct MIDISong2::TrackInfo +{ + const uint8_t *TrackBegin; + size_t TrackP; + size_t MaxTrackP; + uint32_t Delay; + uint32_t PlayedTime; + bool Finished; + uint8_t RunningStatus; + bool Designated; + bool EProgramChange; + bool EVolume; + uint16_t Designation; + + size_t LoopBegin; + uint32_t LoopDelay; + int LoopCount; + bool LoopFinished; + + uint32_t ReadVarLen (); +}; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// MIDISong2 Constructor +// +// Buffers the file and does some validation of the SMF header. +// +//========================================================================== + +MIDISong2::MIDISong2 (const uint8_t* data, size_t len) +: MusHeader(0), Tracks(0) +{ + unsigned p; + int i; + + MusHeader.resize(len); + memcpy(MusHeader.data(), data, len); + + // Do some validation of the MIDI file + if (MusHeader[4] != 0 || MusHeader[5] != 0 || MusHeader[6] != 0 || MusHeader[7] != 6) + return; + + if (MusHeader[8] != 0 || MusHeader[9] > 2) + return; + + Format = MusHeader[9]; + + if (Format == 0) + { + NumTracks = 1; + } + else + { + NumTracks = MusHeader[10] * 256 + MusHeader[11]; + } + + // The division is the number of pulses per quarter note (PPQN). + Division = MusHeader[12] * 256 + MusHeader[13]; + if (Division == 0) + { // PPQN is zero? Then the song cannot play because it never pulses. + return; + } + + Tracks.resize(NumTracks); + + // Gather information about each track + for (i = 0, p = 14; i < NumTracks && p < MusHeader.size() + 8; ++i) + { + uint32_t chunkLen = + (MusHeader[p+4]<<24) | + (MusHeader[p+5]<<16) | + (MusHeader[p+6]<<8) | + (MusHeader[p+7]); + + if (chunkLen + p + 8 > MusHeader.size()) + { // Track too long, so truncate it + chunkLen = (uint32_t)MusHeader.size() - p - 8; + } + + if (MusHeader[p+0] == 'M' && + MusHeader[p+1] == 'T' && + MusHeader[p+2] == 'r' && + MusHeader[p+3] == 'k') + { + Tracks[i].TrackBegin = &MusHeader[p + 8]; + Tracks[i].TrackP = 0; + Tracks[i].MaxTrackP = chunkLen; + } + + p += chunkLen + 8; + } + + // In case there were fewer actual chunks in the file than the + // header specified, update NumTracks with the current value of i + NumTracks = i; + + if (NumTracks == 0) + { // No tracks, so nothing to play + return; + } +} + +//========================================================================== +// +// MIDISong2 :: CheckCaps +// +// Find out if this is an FM synth or not for EMIDI's benefit. +// (Do any released EMIDIs use track designations?) +// +//========================================================================== + +enum +{ + EMIDI_GeneralMIDI = 0, + EMIDI_SoundCanvas = 1, + EMIDI_AWE32 = 2, + EMIDI_WaveBlaster = 3, + EMIDI_SoundBlaster = 4, + EMIDI_ProAudio = 5, + EMIDI_SoundMan16 = 6, + EMIDI_Adlib = 7, + EMIDI_Soundscape = 8, + EMIDI_Ultrasound = 9, +}; + +void MIDISong2::CheckCaps(int tech) +{ + if (tech == MIDIDEV_FMSYNTH) + { + DesignationMask = 1 << EMIDI_Adlib; + } + else + { + DesignationMask = 1 << EMIDI_GeneralMIDI; + } +} + + +//========================================================================== +// +// MIDISong2 :: DoInitialSetup +// +// Sets the starting channel volumes. +// +//========================================================================== + +void MIDISong2 :: DoInitialSetup() +{ + for (int i = 0; i < 16; ++i) + { + // The ASS uses a default volume of 90, but all the other + // sources I can find say it's 100. Ideally, any song that + // cares about its volume is going to initialize it to + // whatever it wants and override this default. + ChannelVolumes[i] = 100; + } +} + +//========================================================================== +// +// MIDISong2 :: DoRestart +// +// Rewinds every track. +// +//========================================================================== + +void MIDISong2 :: DoRestart() +{ + int i; + + // Set initial state. + for (i = 0; i < NumTracks; ++i) + { + Tracks[i].TrackP = 0; + Tracks[i].Finished = false; + Tracks[i].RunningStatus = 0; + Tracks[i].Designated = false; + Tracks[i].Designation = 0; + Tracks[i].LoopCount = -1; + Tracks[i].EProgramChange = false; + Tracks[i].EVolume = false; + Tracks[i].PlayedTime = 0; + } + ProcessInitialMetaEvents (); + for (i = 0; i < NumTracks; ++i) + { + Tracks[i].Delay = Tracks[i].ReadVarLen(); + } + TrackDue = Tracks.data(); + TrackDue = FindNextDue(); +} + +//========================================================================== +// +// MIDISong2 :: CheckDone +// +//========================================================================== + +bool MIDISong2::CheckDone() +{ + return TrackDue == nullptr; +} + +//========================================================================== +// +// MIDISong2 :: MakeEvents +// +// Copies MIDI events from the SMF and puts them into a MIDI stream +// buffer. Returns the new position in the buffer. +// +//========================================================================== + +uint32_t *MIDISong2::MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time) +{ + uint32_t *start_events; + uint32_t tot_time = 0; + uint32_t time = 0; + uint32_t delay; + + start_events = events; + while (TrackDue && events < max_event_p && tot_time <= max_time) + { + // It's possible that this tick may be nothing but meta-events and + // not generate any real events. Repeat this until we actually + // get some output so we don't send an empty buffer to the MIDI + // device. + do + { + delay = TrackDue->Delay; + time += delay; + // Advance time for all tracks by the amount needed for the one up next. + tot_time += delay * Tempo / Division; + AdvanceTracks(delay); + // Play all events for this tick. + do + { + bool sysex_noroom = false; + uint32_t *new_events = SendCommand(events, TrackDue, time, max_event_p - events, sysex_noroom); + if (sysex_noroom) + { + return events; + } + TrackDue = FindNextDue(); + if (new_events != events) + { + time = 0; + } + events = new_events; + } + while (TrackDue && TrackDue->Delay == 0 && events < max_event_p); + } + while (start_events == events && TrackDue); + time = 0; + } + return events; +} + +//========================================================================== +// +// MIDISong2 :: AdvanceTracks +// +// Advances time for all tracks by the specified amount. +// +//========================================================================== + +void MIDISong2::AdvanceTracks(uint32_t time) +{ + for (int i = 0; i < NumTracks; ++i) + { + if (!Tracks[i].Finished) + { + Tracks[i].Delay -= time; + Tracks[i].PlayedTime += time; + } + } +} + +//========================================================================== +// +// MIDISong2 :: SendCommand +// +// Places a single MIDIEVENT in the event buffer. +// +//========================================================================== + +uint32_t *MIDISong2::SendCommand (uint32_t *events, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom) +{ + uint32_t len; + uint8_t event, data1 = 0, data2 = 0; + int i; + + sysex_noroom = false; + size_t start_p = track->TrackP; + + CHECK_FINISHED + event = track->TrackBegin[track->TrackP++]; + CHECK_FINISHED + + // The actual event type will be filled in below. + events[0] = delay; + events[1] = 0; + events[2] = MEVENT_NOP << 24; + + if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND) + { + // Normal short message + if ((event & 0xF0) == 0xF0) + { + if (MIDI_CommonLengths[event & 15] > 0) + { + data1 = track->TrackBegin[track->TrackP++]; + if (MIDI_CommonLengths[event & 15] > 1) + { + data2 = track->TrackBegin[track->TrackP++]; + } + } + } + else if ((event & 0x80) == 0) + { + data1 = event; + event = track->RunningStatus; + } + else + { + track->RunningStatus = event; + data1 = track->TrackBegin[track->TrackP++]; + } + + CHECK_FINISHED + + if (MIDI_EventLengths[(event&0x70)>>4] == 2) + { + data2 = track->TrackBegin[track->TrackP++]; + } + + switch (event & 0x70) + { + case MIDI_PRGMCHANGE & 0x70: + if (track->EProgramChange) + { + event = MIDI_META; + } + break; + + case MIDI_CTRLCHANGE & 0x70: + switch (data1) + { + case 7: // Channel volume + if (track->EVolume) + { // Tracks that use EMIDI volume ignore normal volume changes. + event = MIDI_META; + } + else + { + data2 = VolumeControllerChange(event & 15, data2); + } + break; + + case 7+32: // Channel volume (LSB) + if (track->EVolume) + { + event = MIDI_META; + } + // It should be safe to pass this straight through to the + // MIDI device, since it's a very fine amount. + break; + + case 110: // EMIDI Track Designation - InitBeat only + // Instruments 4, 5, 6, and 7 are all FM synth. + // The rest are all wavetable. + if (track->PlayedTime < (uint32_t)Division) + { + if (data2 == 127) + { + track->Designation = ~0; + track->Designated = true; + } + else if (data2 <= 9) + { + track->Designation |= 1 << data2; + track->Designated = true; + } + event = MIDI_META; + } + break; + + case 111: // EMIDI Track Exclusion - InitBeat only + if (track->PlayedTime < (uint32_t)Division) + { + if (!track->Designated) + { + track->Designation = ~0; + track->Designated = true; + } + if (data2 <= 9) + { + track->Designation &= ~(1 << data2); + } + event = MIDI_META; + } + break; + + case 112: // EMIDI Program Change + // Ignored unless it also appears in the InitBeat + if (track->PlayedTime < (uint32_t)Division || track->EProgramChange) + { + track->EProgramChange = true; + event = 0xC0 | (event & 0x0F); + data1 = data2; + data2 = 0; + } + break; + + case 113: // EMIDI Volume + // Ignored unless it also appears in the InitBeat + if (track->PlayedTime < (uint32_t)Division || track->EVolume) + { + track->EVolume = true; + data1 = 7; + data2 = VolumeControllerChange(event & 15, data2); + } + break; + + case 116: // EMIDI Loop Begin + { + // We convert the loop count to XMIDI conventions before clamping. + // Then we convert it back to EMIDI conventions after clamping. + // (XMIDI can create "loops" that don't loop. EMIDI cannot.) + int loopcount = ClampLoopCount(data2 == 0 ? 0 : data2 + 1); + if (loopcount != 1) + { + track->LoopBegin = track->TrackP; + track->LoopDelay = 0; + track->LoopCount = loopcount == 0 ? 0 : loopcount - 1; + track->LoopFinished = track->Finished; + } + } + event = MIDI_META; + break; + + case 117: // EMIDI Loop End + if (track->LoopCount >= 0 && data2 == 127) + { + if (track->LoopCount == 0 && !isLooping) + { + track->Finished = true; + } + else + { + if (track->LoopCount > 0 && --track->LoopCount == 0) + { + track->LoopCount = -1; + } + track->TrackP = track->LoopBegin; + track->Delay = track->LoopDelay; + track->Finished = track->LoopFinished; + } + } + event = MIDI_META; + break; + + case 118: // EMIDI Global Loop Begin + { + int loopcount = ClampLoopCount(data2 == 0 ? 0 : data2 + 1); + if (loopcount != 1) + { + for (i = 0; i < NumTracks; ++i) + { + Tracks[i].LoopBegin = Tracks[i].TrackP; + Tracks[i].LoopDelay = Tracks[i].Delay; + Tracks[i].LoopCount = loopcount == 0 ? 0 : loopcount - 1; + Tracks[i].LoopFinished = Tracks[i].Finished; + } + } + } + event = MIDI_META; + break; + + case 119: // EMIDI Global Loop End + if (data2 == 127) + { + for (i = 0; i < NumTracks; ++i) + { + if (Tracks[i].LoopCount >= 0) + { + if (Tracks[i].LoopCount == 0 && !isLooping) + { + Tracks[i].Finished = true; + } + else + { + if (Tracks[i].LoopCount > 0 && --Tracks[i].LoopCount == 0) + { + Tracks[i].LoopCount = -1; + } + Tracks[i].TrackP = Tracks[i].LoopBegin; + Tracks[i].Delay = Tracks[i].LoopDelay; + Tracks[i].Finished = Tracks[i].LoopFinished; + } + } + } + } + event = MIDI_META; + break; + } + } + if (event != MIDI_META && (!track->Designated || (track->Designation & DesignationMask))) + { + events[2] = event | (data1<<8) | (data2<<16); + } + } + else + { + // SysEx events could potentially not have enough room in the buffer... + if (event == MIDI_SYSEX || event == MIDI_SYSEXEND) + { + len = track->ReadVarLen(); + if (len >= (MAX_MIDI_EVENTS-1)*3*4 || skipSysex) + { // This message will never fit. Throw it away. + track->TrackP += len; + } + else if (len + 12 >= (size_t)room * 4) + { // Not enough room left in this buffer. Backup and wait for the next one. + track->TrackP = start_p; + sysex_noroom = true; + return events; + } + else + { + uint8_t *msg = (uint8_t *)&events[3]; + if (event == MIDI_SYSEX) + { // Need to add the SysEx marker to the message. + events[2] = (MEVENT_LONGMSG << 24) | (len + 1); + *msg++ = MIDI_SYSEX; + } + else + { + events[2] = (MEVENT_LONGMSG << 24) | len; + } + memcpy(msg, &track->TrackBegin[track->TrackP], len); + msg += len; + // Must pad with 0 + while ((size_t)msg & 3) + { + *msg++ = 0; + } + track->TrackP += len; + } + } + else if (event == MIDI_META) + { + // It's a meta-event + event = track->TrackBegin[track->TrackP++]; + CHECK_FINISHED + len = track->ReadVarLen (); + CHECK_FINISHED + + if (track->TrackP + len <= track->MaxTrackP) + { + switch (event) + { + case MIDI_META_EOT: + track->Finished = true; + break; + + case MIDI_META_TEMPO: + Tempo = + (track->TrackBegin[track->TrackP+0]<<16) | + (track->TrackBegin[track->TrackP+1]<<8) | + (track->TrackBegin[track->TrackP+2]); + events[0] = delay; + events[1] = 0; + events[2] = (MEVENT_TEMPO << 24) | Tempo; + break; + } + track->TrackP += len; + if (track->TrackP == track->MaxTrackP) + { + track->Finished = true; + } + } + else + { + track->Finished = true; + } + } + } + if (!track->Finished) + { + track->Delay = track->ReadVarLen(); + } + // Advance events pointer unless this is a non-delaying NOP. + if (events[0] != 0 || MEVENT_EVENTTYPE(events[2]) != MEVENT_NOP) + { + if (MEVENT_EVENTTYPE(events[2]) == MEVENT_LONGMSG) + { + events += 3 + ((MEVENT_EVENTPARM(events[2]) + 3) >> 2); + } + else + { + events += 3; + } + } + return events; +} + +//========================================================================== +// +// MIDISong2 :: ProcessInitialMetaEvents +// +// Handle all the meta events at the start of each track. +// +//========================================================================== + +void MIDISong2::ProcessInitialMetaEvents () +{ + TrackInfo *track; + int i; + uint8_t event; + uint32_t len; + + for (i = 0; i < NumTracks; ++i) + { + track = &Tracks[i]; + while (!track->Finished && + track->TrackP < track->MaxTrackP - 4 && + track->TrackBegin[track->TrackP] == 0 && + track->TrackBegin[track->TrackP+1] == 0xFF) + { + event = track->TrackBegin[track->TrackP+2]; + track->TrackP += 3; + len = track->ReadVarLen (); + if (track->TrackP + len <= track->MaxTrackP) + { + switch (event) + { + case MIDI_META_EOT: + track->Finished = true; + break; + + case MIDI_META_TEMPO: + SetTempo( + (track->TrackBegin[track->TrackP+0]<<16) | + (track->TrackBegin[track->TrackP+1]<<8) | + (track->TrackBegin[track->TrackP+2]) + ); + break; + } + } + track->TrackP += len; + } + if (track->TrackP >= track->MaxTrackP - 4) + { + track->Finished = true; + } + } +} + +//========================================================================== +// +// MIDISong2 :: TrackInfo :: ReadVarLen +// +// Reads a variable-length SMF number. +// +//========================================================================== + +uint32_t MIDISong2::TrackInfo::ReadVarLen () +{ + uint32_t time = 0, t = 0x80; + + while ((t & 0x80) && TrackP < MaxTrackP) + { + t = TrackBegin[TrackP++]; + time = (time << 7) | (t & 127); + } + return time; +} + +//========================================================================== +// +// MIDISong2 :: FindNextDue +// +// Scans every track for the next event to play. Returns nullptr if all events +// have been consumed. +// +//========================================================================== + +MIDISong2::TrackInfo *MIDISong2::FindNextDue () +{ + TrackInfo *track; + uint32_t best; + int i; + + // Give precedence to whichever track last had events taken from it. + if (!TrackDue->Finished && TrackDue->Delay == 0) + { + return TrackDue; + } + + switch (Format) + { + case 0: + return Tracks[0].Finished ? nullptr : Tracks.data(); + + case 1: + track = nullptr; + best = 0xFFFFFFFF; + for (i = 0; i < NumTracks; ++i) + { + if (!Tracks[i].Finished) + { + if (Tracks[i].Delay < best) + { + best = Tracks[i].Delay; + track = &Tracks[i]; + } + } + } + return track; + + case 2: + track = TrackDue; + if (track->Finished) + { + track++; + } + return track < &Tracks[NumTracks] ? track : nullptr; + } + return nullptr; +} + + diff --git a/source/midisources/midisource_xmi.cpp b/source/midisources/midisource_xmi.cpp new file mode 100644 index 000000000..bbd282c42 --- /dev/null +++ b/source/midisources/midisource_xmi.cpp @@ -0,0 +1,674 @@ +/* +** music_xmi_midiout.cpp +** Code to let ZDoom play XMIDI music through the MIDI streaming API. +** +**--------------------------------------------------------------------------- +** Copyright 2010 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "midisource.h" +#include "zmusic/mididefs.h" +#include "zmusic/m_swap.h" + +// MACROS ------------------------------------------------------------------ + +#define MAX_FOR_DEPTH 4 + +#define GET_DELAY (EventDue == EVENT_Real ? CurrSong->Delay : NoteOffs[0].Delay) + +// Used by SendCommand to check for unexpected end-of-track conditions. +#define CHECK_FINISHED \ + if (track->EventP >= track->EventLen) \ + { \ + track->Finished = true; \ + return events; \ + } + +// TYPES ------------------------------------------------------------------- + +struct LoopInfo +{ + size_t LoopBegin; + int LoopCount; + bool LoopFinished; +}; + +struct XMISong::TrackInfo +{ + const uint8_t *EventChunk; + size_t EventLen; + size_t EventP; + + const uint8_t *TimbreChunk; + size_t TimbreLen; + + uint32_t Delay; + uint32_t PlayedTime; + bool Finished; + + LoopInfo ForLoops[MAX_FOR_DEPTH]; + int ForDepth; + + uint32_t ReadVarLen(); + uint32_t ReadDelay(); +}; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// XMISong Constructor +// +// Buffers the file and does some validation of the SMF header. +// +//========================================================================== + +XMISong::XMISong (const uint8_t* data, size_t len) +: MusHeader(0), Songs(0) +{ + MusHeader.resize(len); + memcpy(MusHeader.data(), data, len); + + // Find all the songs in this file. + NumSongs = FindXMIDforms(&MusHeader[0], (int)MusHeader.size(), nullptr); + if (NumSongs == 0) + { + return; + } + + // XMIDI files are played with a constant 120 Hz clock rate. While the + // song may contain tempo events, these are vestigial remnants from the + // original MIDI file that were not removed by the converter and should + // be ignored. + // + // We can use any combination of Division and Tempo values that work out + // to be 120 Hz. + Division = 60; + Tempo = InitialTempo = 500000; + + Songs.resize(NumSongs); + memset(Songs.data(), 0, sizeof(Songs[0]) * NumSongs); + FindXMIDforms(&MusHeader[0], (int)MusHeader.size(), Songs.data()); + CurrSong = Songs.data(); + //DPrintf(DMSG_SPAMMY, "XMI song count: %d\n", NumSongs); +} + +//========================================================================== +// +// XMISong :: FindXMIDforms +// +// Find all FORM XMID chunks in this chunk. +// +//========================================================================== + +int XMISong::FindXMIDforms(const uint8_t *chunk, int len, TrackInfo *songs) const +{ + int count = 0; + + for (int p = 0; p <= len - 12; ) + { + int chunktype = GetNativeInt(chunk + p); + int chunklen = GetBigInt(chunk + p + 4); + + if (chunktype == MAKE_ID('F','O','R','M')) + { + if (GetNativeInt(chunk + p + 8) == MAKE_ID('X','M','I','D')) + { + if (songs != nullptr) + { + FoundXMID(chunk + p + 12, chunklen - 4, songs + count); + } + count++; + } + } + else if (chunktype == MAKE_ID('C','A','T',' ')) + { + // Recurse to handle CAT chunks. + count += FindXMIDforms(chunk + p + 12, chunklen - 4, songs + count); + } + // IFF chunks are padded to even byte boundaries to avoid + // unaligned reads on 68k processors. + p += 8 + chunklen + (chunklen & 1); + // Avoid crashes from corrupt chunks which indicate a negative size. + if (chunklen < 0) p = len; + } + return count; +} + +//========================================================================== +// +// XMISong :: FoundXMID +// +// Records information about this XMID song. +// +//========================================================================== + +void XMISong::FoundXMID(const uint8_t *chunk, int len, TrackInfo *song) const +{ + for (int p = 0; p <= len - 8; ) + { + int chunktype = GetNativeInt(chunk + p); + int chunklen = GetBigInt(chunk + p + 4); + + if (chunktype == MAKE_ID('T','I','M','B')) + { + song->TimbreChunk = chunk + p + 8; + song->TimbreLen = chunklen; + } + else if (chunktype == MAKE_ID('E','V','N','T')) + { + song->EventChunk = chunk + p + 8; + song->EventLen = chunklen; + // EVNT must be the final chunk in the FORM. + break; + } + p += 8 + chunklen + (chunklen & 1); + } +} + +//========================================================================== +// +// XMISong :: SetMIDISubsong +// +// Selects which song in this file to play. +// +//========================================================================== + +bool XMISong::SetMIDISubsong(int subsong) +{ + if ((unsigned)subsong >= (unsigned)NumSongs) + { + return false; + } + CurrSong = &Songs[subsong]; + return true; +} + +//========================================================================== +// +// XMISong :: DoInitialSetup +// +// Sets the starting channel volumes. +// +//========================================================================== + +void XMISong::DoInitialSetup() +{ + for (int i = 0; i < 16; ++i) + { + ChannelVolumes[i] = 100; + } +} + +//========================================================================== +// +// XMISong :: DoRestart +// +// Rewinds the current song. +// +//========================================================================== + +void XMISong::DoRestart() +{ + CurrSong->EventP = 0; + CurrSong->Finished = false; + CurrSong->PlayedTime = 0; + CurrSong->ForDepth = 0; + NoteOffs.clear(); + + ProcessInitialMetaEvents (); + + CurrSong->Delay = CurrSong->ReadDelay(); + EventDue = FindNextDue(); +} + +//========================================================================== +// +// XMISong :: CheckDone +// +//========================================================================== + +bool XMISong::CheckDone() +{ + return EventDue == EVENT_None; +} + +//========================================================================== +// +// XMISong :: MakeEvents +// +// Copies MIDI events from the XMI and puts them into a MIDI stream +// buffer. Returns the new position in the buffer. +// +//========================================================================== + +uint32_t *XMISong::MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time) +{ + uint32_t *start_events; + uint32_t tot_time = 0; + uint32_t time = 0; + uint32_t delay; + + start_events = events; + while (EventDue != EVENT_None && events < max_event_p && tot_time <= max_time) + { + // It's possible that this tick may be nothing but meta-events and + // not generate any real events. Repeat this until we actually + // get some output so we don't send an empty buffer to the MIDI + // device. + do + { + delay = GET_DELAY; + time += delay; + // Advance time for all tracks by the amount needed for the one up next. + tot_time += delay * Tempo / Division; + AdvanceSong(delay); + // Play all events for this tick. + do + { + bool sysex_noroom = false; + uint32_t *new_events = SendCommand(events, EventDue, time, max_event_p - events, sysex_noroom); + if (sysex_noroom) + { + return events; + } + EventDue = FindNextDue(); + if (new_events != events) + { + time = 0; + } + events = new_events; + } + while (EventDue != EVENT_None && GET_DELAY == 0 && events < max_event_p); + } + while (start_events == events && EventDue != EVENT_None); + time = 0; + } + return events; +} + +//========================================================================== +// +// XMISong :: AdvanceSong +// +// Advances time for the current song by the specified amount. +// +//========================================================================== + +void XMISong::AdvanceSong(uint32_t time) +{ + if (time != 0) + { + if (!CurrSong->Finished) + { + CurrSong->Delay -= time; + CurrSong->PlayedTime += time; + } + NoteOffs.AdvanceTime(time); + } +} + +//========================================================================== +// +// XMISong :: SendCommand +// +// Places a single MIDIEVENT in the event buffer. +// +//========================================================================== + +uint32_t *XMISong::SendCommand (uint32_t *events, EventSource due, uint32_t delay, ptrdiff_t room, bool &sysex_noroom) +{ + uint32_t len; + uint8_t event, data1 = 0, data2 = 0; + + if (due == EVENT_Fake) + { + AutoNoteOff off; + NoteOffs.Pop(off); + events[0] = delay; + events[1] = 0; + events[2] = MIDI_NOTEON | off.Channel | (off.Key << 8); + return events + 3; + } + + TrackInfo *track = CurrSong; + + sysex_noroom = false; + size_t start_p = track->EventP; + + CHECK_FINISHED + event = track->EventChunk[track->EventP++]; + CHECK_FINISHED + + // The actual event type will be filled in below. If it's not a NOP, + // the events pointer will be advanced once the actual event is written. + // Otherwise, we do it at the end of the function. + events[0] = delay; + events[1] = 0; + events[2] = MEVENT_NOP << 24; + + if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND) + { + // Normal short message + if ((event & 0xF0) == 0xF0) + { + if (MIDI_CommonLengths[event & 15] > 0) + { + data1 = track->EventChunk[track->EventP++]; + if (MIDI_CommonLengths[event & 15] > 1) + { + data2 = track->EventChunk[track->EventP++]; + } + } + } + else + { + data1 = track->EventChunk[track->EventP++]; + } + + CHECK_FINISHED + + if (MIDI_EventLengths[(event&0x70)>>4] == 2) + { + data2 = track->EventChunk[track->EventP++]; + } + + if ((event & 0x70) == (MIDI_CTRLCHANGE & 0x70)) + { + switch (data1) + { + case 7: // Channel volume + data2 = VolumeControllerChange(event & 15, data2); + break; + + case 110: // XMI channel lock + case 111: // XMI channel lock protect + case 112: // XMI voice protect + case 113: // XMI timbre protect + case 115: // XMI indirect controller prefix + case 118: // XMI clear beat/bar count + case 119: // XMI callback trigger + case 120: + event = MIDI_META; // none of these are relevant to us. + break; + + case 114: // XMI patch bank select + data1 = 0; // Turn this into a standard MIDI bank select controller. + break; + + case 116: // XMI for loop controller + if (track->ForDepth < MAX_FOR_DEPTH) + { + track->ForLoops[track->ForDepth].LoopBegin = track->EventP; + track->ForLoops[track->ForDepth].LoopCount = ClampLoopCount(data2); + track->ForLoops[track->ForDepth].LoopFinished = track->Finished; + } + track->ForDepth++; + event = MIDI_META; + break; + + case 117: // XMI next loop controller + if (track->ForDepth > 0) + { + int depth = track->ForDepth - 1; + if (depth < MAX_FOR_DEPTH) + { + if (data2 < 64 || (track->ForLoops[depth].LoopCount == 0 && !isLooping)) + { // throw away this loop. + track->ForLoops[depth].LoopCount = 1; + } + // A loop count of 0 loops forever. + if (track->ForLoops[depth].LoopCount == 0 || --track->ForLoops[depth].LoopCount > 0) + { + track->EventP = track->ForLoops[depth].LoopBegin; + track->Finished = track->ForLoops[depth].LoopFinished; + } + else + { // done with this loop + track->ForDepth = depth; + } + } + else + { // ignore any loops deeper than the max depth + track->ForDepth = depth; + } + } + event = MIDI_META; + break; + } + } + events[0] = delay; + events[1] = 0; + if (event != MIDI_META) + { + events[2] = event | (data1<<8) | (data2<<16); + } + + + if ((event & 0x70) == (MIDI_NOTEON & 0x70)) + { // XMI note on events include the time until an implied note off event. + NoteOffs.AddNoteOff(track->ReadVarLen(), event & 0x0F, data1); + } + } + else + { + // SysEx events could potentially not have enough room in the buffer... + if (event == MIDI_SYSEX || event == MIDI_SYSEXEND) + { + len = track->ReadVarLen(); + if (len >= (MAX_MIDI_EVENTS-1)*3*4 || skipSysex) + { // This message will never fit. Throw it away. + track->EventP += len; + } + else if (len + 12 >= (size_t)room * 4) + { // Not enough room left in this buffer. Backup and wait for the next one. + track->EventP = start_p; + sysex_noroom = true; + return events; + } + else + { + uint8_t *msg = (uint8_t *)&events[3]; + if (event == MIDI_SYSEX) + { // Need to add the SysEx marker to the message. + events[2] = (MEVENT_LONGMSG << 24) | (len + 1); + *msg++ = MIDI_SYSEX; + } + else + { + events[2] = (MEVENT_LONGMSG << 24) | len; + } + memcpy(msg, &track->EventChunk[track->EventP++], len); + msg += len; + // Must pad with 0 + while ((size_t)msg & 3) + { + *msg++ = 0; + } + track->EventP += len; + } + } + else if (event == MIDI_META) + { + // It's a meta-event + event = track->EventChunk[track->EventP++]; + CHECK_FINISHED + len = track->ReadVarLen (); + CHECK_FINISHED + + if (track->EventP + len <= track->EventLen) + { + if (event == MIDI_META_EOT) + { + track->Finished = true; + } + track->EventP += len; + if (track->EventP == track->EventLen) + { + track->Finished = true; + } + } + else + { + track->Finished = true; + } + } + } + if (!track->Finished) + { + track->Delay = track->ReadDelay(); + } + // Advance events pointer unless this is a non-delaying NOP. + if (events[0] != 0 || MEVENT_EVENTTYPE(events[2]) != MEVENT_NOP) + { + if (MEVENT_EVENTTYPE(events[2]) == MEVENT_LONGMSG) + { + events += 3 + ((MEVENT_EVENTPARM(events[2]) + 3) >> 2); + } + else + { + events += 3; + } + } + return events; +} + +//========================================================================== +// +// XMISong :: ProcessInitialMetaEvents +// +// Handle all the meta events at the start of the current song. +// +//========================================================================== + +void XMISong::ProcessInitialMetaEvents () +{ + TrackInfo *track = CurrSong; + uint8_t event; + uint32_t len; + + while (!track->Finished && + track->EventP < track->EventLen - 3 && + track->EventChunk[track->EventP] == MIDI_META) + { + event = track->EventChunk[track->EventP+1]; + track->EventP += 2; + len = track->ReadVarLen(); + if (track->EventP + len <= track->EventLen && event == MIDI_META_EOT) + { + track->Finished = true; + } + track->EventP += len; + } + if (track->EventP >= track->EventLen - 1) + { + track->Finished = true; + } +} + +//========================================================================== +// +// XMISong :: TrackInfo :: ReadVarLen +// +// Reads a variable length SMF number. +// +//========================================================================== + +uint32_t XMISong::TrackInfo::ReadVarLen() +{ + uint32_t time = 0, t = 0x80; + + while ((t & 0x80) && EventP < EventLen) + { + t = EventChunk[EventP++]; + time = (time << 7) | (t & 127); + } + return time; +} + +//========================================================================== +// +// XMISong :: TrackInfo :: ReadDelay +// +// XMI does not use variable length numbers for delays. Instead, it uses +// runs of bytes with the high bit clear. +// +//========================================================================== + +uint32_t XMISong::TrackInfo::ReadDelay() +{ + uint32_t time = 0, t; + + while (EventP < EventLen && !((t = EventChunk[EventP]) & 0x80)) + { + time += t; + EventP++; + } + return time; +} + +//========================================================================== +// +// XMISong :: FindNextDue +// +// Decides whether the next event should come from the actual stong or +// from the auto note offs. +// +//========================================================================== + +XMISong::EventSource XMISong::FindNextDue() +{ + // Are there still events available? + if (CurrSong->Finished && NoteOffs.size() == 0) + { + return EVENT_None; + } + + // Which is due sooner? The current song or the note-offs? + uint32_t real_delay = CurrSong->Finished ? 0xFFFFFFFF : CurrSong->Delay; + uint32_t fake_delay = NoteOffs.size() == 0 ? 0xFFFFFFFF : NoteOffs[0].Delay; + + return (fake_delay <= real_delay) ? EVENT_Fake : EVENT_Real; +} + + diff --git a/source/musicformats/music_cd.cpp b/source/musicformats/music_cd.cpp new file mode 100644 index 000000000..84fd60bc9 --- /dev/null +++ b/source/musicformats/music_cd.cpp @@ -0,0 +1,211 @@ +/* +** music_cd.cpp +** +**--------------------------------------------------------------------------- +** Copyright 1999-2003 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#include "zmusic/zmusic_internal.h" +#include "zmusic/musinfo.h" + +#ifdef _WIN32 + +#include "zmusic/m_swap.h" +#include "win32/i_cd.h" + + +// CD track/disk played through the multimedia system ----------------------- + +class CDSong : public MusInfo +{ +public: + CDSong(int track, int id); + ~CDSong(); + void Play(bool looping, int subsong); + void Pause(); + void Resume(); + void Stop(); + bool IsPlaying(); + SoundStreamInfoEx GetStreamInfoEx() const override { return {}; } + bool IsValid() const { return m_Inited; } + +protected: + CDSong() : m_Inited(false) {} + + int m_Track; + bool m_Inited; +}; + +// CD track on a specific disk played through the multimedia system --------- + +class CDDAFile : public CDSong +{ +public: + CDDAFile(MusicIO::FileInterface* reader); +}; + + + +void CDSong::Play (bool looping, int subsong) +{ + m_Status = STATE_Stopped; + m_Looping = looping; + if (m_Track != 0 ? CD_Play (m_Track, looping) : CD_PlayCD (looping)) + { + m_Status = STATE_Playing; + } +} + +void CDSong::Pause () +{ + if (m_Status == STATE_Playing) + { + CD_Pause (); + m_Status = STATE_Paused; + } +} + +void CDSong::Resume () +{ + if (m_Status == STATE_Paused) + { + if (CD_Resume ()) + m_Status = STATE_Playing; + } +} + +void CDSong::Stop () +{ + if (m_Status != STATE_Stopped) + { + m_Status = STATE_Stopped; + CD_Stop (); + } +} + +CDSong::~CDSong () +{ + Stop (); + m_Inited = false; +} + +CDSong::CDSong (int track, int id) +{ + bool success; + + m_Inited = false; + + if (id != 0) + { + success = CD_InitID (id); + } + else + { + success = CD_Init (-1); + } + + if (success && (track == 0 || CD_CheckTrack (track))) + { + m_Inited = true; + m_Track = track; + } +} + +bool CDSong::IsPlaying () +{ + if (m_Status == STATE_Playing) + { + if (CD_GetMode () != CDMode_Play) + { + Stop (); + } + } + return m_Status != STATE_Stopped; +} + +CDDAFile::CDDAFile (MusicIO::FileInterface* reader) + : CDSong () +{ + uint32_t chunk; + uint16_t track; + uint32_t discid; + auto endpos = reader->tell() + reader->filelength() - 8; + + // ZMusic_OpenSong already identified this as a CDDA file, so we + // just need to check the contents we're interested in. + reader->seek(12, SEEK_CUR); + + while (reader->tell() < endpos) + { + reader->read(&chunk, 4); + if (chunk != (('f')|(('m')<<8)|(('t')<<16)|((' ')<<24))) + { + reader->read(&chunk, 4); + reader->seek(LittleLong(chunk), SEEK_CUR); + } + else + { + reader->seek(6, SEEK_CUR); + reader->read(&track, 2); + reader->read(&discid, 4); + + if (CD_InitID (LittleLong(discid)) && CD_CheckTrack (LittleShort(track))) + { + m_Inited = true; + m_Track = track; + } + return; + } + } +} + +MusInfo* CD_OpenSong(int track, int id) +{ + return new CDSong(track, id); +} + +MusInfo* CDDA_OpenSong(MusicIO::FileInterface* reader) +{ + return new CDDAFile(reader); +} + +#else + +MusInfo* CD_OpenSong(int track, int id) +{ + return nullptr; +} + +MusInfo* CDDA_OpenSong(MusicIO::FileInterface* reader) +{ + return nullptr; +} + + +#endif diff --git a/source/musicformats/music_midi.cpp b/source/musicformats/music_midi.cpp new file mode 100644 index 000000000..e3c5c69fb --- /dev/null +++ b/source/musicformats/music_midi.cpp @@ -0,0 +1,1047 @@ +/* +** music_midistream.cpp +** Implements base class for MIDI and MUS streaming. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include +#include +#include "zmusic/zmusic_internal.h" +#include "zmusic/musinfo.h" +#include "mididevices/mididevice.h" +#include "midisources/midisource.h" +#include "critsec.h" + +#ifdef HAVE_SYSTEM_MIDI +#ifdef __linux__ +#include "mididevices/music_alsa_state.h" +#endif +#endif + +// MACROS ------------------------------------------------------------------ + +enum +{ + MAX_TIME = (1000000/10) // Send out 1/10 of a sec of events at a time. +}; + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// Base class for streaming MUS and MIDI files ------------------------------ + +class MIDIStreamer : public MusInfo +{ +public: + MIDIStreamer(EMidiDevice type, const char* args); + ~MIDIStreamer(); + + void MusicVolumeChanged() override; + void Play(bool looping, int subsong) override; + void Pause() override; + void Resume() override; + void Stop() override; + bool IsPlaying() override; + bool IsMIDI() const override; + bool IsValid() const override; + bool SetSubsong(int subsong) override; + void Update() override; + std::string GetStats() override; + void ChangeSettingInt(const char* setting, int value) override; + void ChangeSettingNum(const char* setting, double value) override; + void ChangeSettingString(const char* setting, const char* value) override; + int ServiceEvent(); + void SetMIDISource(MIDISource* _source); + bool ServiceStream(void* buff, int len) override; + SoundStreamInfoEx GetStreamInfoEx() const override; + + int GetDeviceType() const override; + + bool DumpWave(const char* filename, int subsong, int samplerate); + + +protected: + MIDIStreamer(const char* dumpname, EMidiDevice type); + + void OutputVolume(uint32_t volume); + int FillBuffer(int buffer_num, int max_events, uint32_t max_time); + int FillStopBuffer(int buffer_num); + uint32_t* WriteStopNotes(uint32_t* events); + int VolumeControllerChange(int channel, int volume); + void SetTempo(int new_tempo); + void Precache(); + void StartPlayback(); + bool InitPlayback(); + + //void SetMidiSynth(MIDIDevice *synth); + + + static EMidiDevice SelectMIDIDevice(EMidiDevice devtype); + MIDIDevice* CreateMIDIDevice(EMidiDevice devtype, int samplerate); + + static void Callback(void* userdata); + + enum + { + SONG_MORE, + SONG_DONE, + SONG_ERROR + }; + + std::unique_ptr MIDI; + uint32_t Events[2][MAX_MIDI_EVENTS * 3]; + MidiHeader Buffer[2]; + int BufferNum; + int EndQueued; + bool VolumeChanged; + bool Restarting; + bool InitialPlayback; + uint32_t NewVolume; + uint32_t Volume; + EMidiDevice DeviceType; + bool CallbackIsThreaded; + int LoopLimit; + std::string Args; + std::unique_ptr source; +}; + + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// MIDIStreamer Constructor +// +//========================================================================== + +MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args) +: + DeviceType(type), Args(args) +{ + memset(Buffer, 0, sizeof(Buffer)); +} + +//========================================================================== +// +// MIDIStreamer Destructor +// +//========================================================================== + +MIDIStreamer::~MIDIStreamer() +{ + Stop(); +} + +//========================================================================== +// +// MIDIStreamer :: IsMIDI +// +// You bet it is! +// +//========================================================================== + +bool MIDIStreamer::IsMIDI() const +{ + return true; +} + +//========================================================================== +// +// MIDIStreamer :: IsValid +// +//========================================================================== + +bool MIDIStreamer::IsValid() const +{ + return source != nullptr && source->isValid(); +} + + +//========================================================================== +// +// MIDIStreamer :: SelectMIDIDevice static +// +// Select the MIDI device to play on +// +//========================================================================== + +EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device) +{ + /* MIDI are played as: + - OPL: + - if explicitly selected by $mididevice + - when snd_mididevice is -3 and no midi device is set for the song + + - Timidity: + - if explicitly selected by $mididevice + - when snd_mididevice is -2 and no midi device is set for the song + + - MMAPI (Win32 only): + - if explicitly selected by $mididevice (non-Win32 redirects this to Sound System) + - when snd_mididevice is >= 0 and no midi device is set for the song + - as fallback when both OPL and Timidity failed and snd_mididevice is >= 0 + */ + + // Choose the type of MIDI device we want. + if (device != MDEV_DEFAULT) + { + return device; + } + switch (miscConfig.snd_mididevice) + { + case -1: return MDEV_SNDSYS; + case -2: return MDEV_TIMIDITY; + case -3: return MDEV_OPL; + case -4: return MDEV_GUS; + case -5: return MDEV_FLUIDSYNTH; + case -6: return MDEV_WILDMIDI; + case -7: return MDEV_ADL; + case -8: return MDEV_OPN; + default: + #ifdef HAVE_SYSTEM_MIDI + return MDEV_STANDARD; + #else + return MDEV_SNDSYS; + #endif + } +} + +//========================================================================== +// +// MIDIStreamer :: CreateMIDIDevice +// +//========================================================================== + +static EMidiDevice lastRequestedDevice, lastSelectedDevice; + +MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate) +{ + bool checked[MDEV_COUNT] = { false }; + + MIDIDevice *dev = nullptr; + if (devtype == MDEV_SNDSYS) devtype = MDEV_FLUIDSYNTH; + EMidiDevice requestedDevice = devtype, selectedDevice; + while (dev == nullptr) + { + selectedDevice = devtype; + try + { + switch (devtype) + { + case MDEV_GUS: + dev = CreateTimidityMIDIDevice(Args.c_str(), samplerate); + break; + + case MDEV_ADL: + dev = CreateADLMIDIDevice(Args.c_str()); + break; + + case MDEV_OPN: + dev = CreateOPNMIDIDevice(Args.c_str()); + break; + + case MDEV_STANDARD: + +#ifdef HAVE_SYSTEM_MIDI +#ifdef _WIN32 + dev = CreateWinMIDIDevice(std::max(0, miscConfig.snd_mididevice)); +#elif __linux__ + dev = CreateAlsaMIDIDevice(std::max(0, miscConfig.snd_mididevice)); +#endif + break; +#endif + // Intentional fall-through for systems without standard midi support + + case MDEV_FLUIDSYNTH: + dev = CreateFluidSynthMIDIDevice(samplerate, Args.c_str()); + break; + + case MDEV_OPL: + dev = CreateOplMIDIDevice(Args.c_str()); + break; + + case MDEV_TIMIDITY: + dev = CreateTimidityPPMIDIDevice(Args.c_str(), samplerate); + break; + + case MDEV_WILDMIDI: + dev = CreateWildMIDIDevice(Args.c_str(), samplerate); + break; + + default: + break; + } + } + catch (std::runtime_error &err) + { + //DPrintf(DMSG_WARNING, "%s\n", err.what()); + checked[devtype] = true; + devtype = MDEV_DEFAULT; + // Opening the requested device did not work out so choose another one. + if (!checked[MDEV_FLUIDSYNTH]) devtype = MDEV_FLUIDSYNTH; + else if (!checked[MDEV_TIMIDITY]) devtype = MDEV_TIMIDITY; + else if (!checked[MDEV_WILDMIDI]) devtype = MDEV_WILDMIDI; + else if (!checked[MDEV_GUS]) devtype = MDEV_GUS; +#ifdef HAVE_SYSTEM_MIDI + else if (!checked[MDEV_STANDARD]) devtype = MDEV_STANDARD; +#endif + else if (!checked[MDEV_ADL]) devtype = MDEV_ADL; + else if (!checked[MDEV_OPN]) devtype = MDEV_OPN; + else if (!checked[MDEV_OPL]) devtype = MDEV_OPL; + + if (devtype == MDEV_DEFAULT) + { + std::string message = std::string(err.what()) + "\n\nFailed to play music: Unable to open any MIDI Device."; + throw std::runtime_error(message); + } + } + } + if (selectedDevice != requestedDevice && (selectedDevice != lastSelectedDevice || requestedDevice != lastRequestedDevice)) + { + static const char *devnames[] = { + "System Default", + "OPL", + "", + "Timidity++", + "FluidSynth", + "GUS", + "WildMidi", + "ADL", + "OPN", + }; + + lastRequestedDevice = requestedDevice; + lastSelectedDevice = selectedDevice; + ZMusic_Printf(ZMUSIC_MSG_ERROR, "Unable to create %s MIDI device. Falling back to %s\n", devnames[requestedDevice], devnames[selectedDevice]); + } + return dev; +} + +//========================================================================== +// +// MIDIStreamer :: Play +// +//========================================================================== + +void MIDIStreamer::Play(bool looping, int subsong) +{ + EMidiDevice devtype; + + if (source == nullptr) return; // We have nothing to play so abort. + + assert(MIDI == NULL); + m_Looping = looping; + source->SetMIDISubsong(subsong); + devtype = SelectMIDIDevice(DeviceType); + MIDI.reset(CreateMIDIDevice(devtype, miscConfig.snd_outputrate)); + InitPlayback(); +} + +//========================================================================== +// +// MIDIStreamer :: DumpWave +// +//========================================================================== + +bool MIDIStreamer::DumpWave(const char *filename, int subsong, int samplerate) +{ + m_Looping = false; + if (source == nullptr) return false; // We have nothing to play so abort. + source->SetMIDISubsong(subsong); + + assert(MIDI == NULL); + auto devtype = SelectMIDIDevice(DeviceType); + if (devtype == MDEV_STANDARD) + { + throw std::runtime_error("System MIDI device is not supported"); + } + auto iMIDI = CreateMIDIDevice(devtype, samplerate); + auto writer = new MIDIWaveWriter(filename, static_cast(iMIDI)); + MIDI.reset(writer); + bool res = InitPlayback(); + if (!writer->CloseFile()) + { + char buffer[80]; + snprintf(buffer, 80, "Could not finish writing wave file: %s\n", strerror(errno)); + throw std::runtime_error(buffer); + } + return res; +} + +//========================================================================== +// +// MIDIStreamer :: InitPlayback +// +//========================================================================== + +bool MIDIStreamer::InitPlayback() +{ + m_Status = STATE_Stopped; + EndQueued = 0; + VolumeChanged = false; + Restarting = true; + InitialPlayback = true; + if (MIDI) MIDI->SetCallback(Callback, this); + + if (MIDI == NULL || 0 != MIDI->Open()) + { + throw std::runtime_error("Could not open MIDI out device"); + } + + source->CheckCaps(MIDI->GetTechnology()); + if (!MIDI->CanHandleSysex()) source->SkipSysex(); + + StartPlayback(); + if (MIDI == nullptr) + { // The MIDI file had no content and has been automatically closed. + return false; + } + + int res = MIDI->Resume(); + + if (res) + { + throw std::runtime_error("Starting MIDI playback failed"); + } + else + { + m_Status = STATE_Playing; + return true; + } +} + +SoundStreamInfoEx MIDIStreamer::GetStreamInfoEx() const +{ + if (MIDI) return MIDI->GetStreamInfoEx(); + else return {}; +} + +//========================================================================== +// +// MIDIStreamer :: StartPlayback +// +//========================================================================== + +void MIDIStreamer::StartPlayback() +{ + auto data = source->PrecacheData(); + MIDI->PrecacheInstruments(data.data(), (int)data.size()); + source->StartPlayback(m_Looping); + + // Set time division and tempo. + if (0 != MIDI->SetTimeDiv(source->getDivision()) || + 0 != MIDI->SetTempo(source->getInitialTempo())) + { + throw std::runtime_error("Setting MIDI stream speed failed"); + } + + MusicVolumeChanged(); // set volume to current music's properties + OutputVolume(Volume); + + MIDI->InitPlayback(); + + // Fill the initial buffers for the song. + BufferNum = 0; + do + { + int res = FillBuffer(BufferNum, MAX_MIDI_EVENTS, MAX_TIME); + if (res == SONG_MORE) + { + if (0 != MIDI->StreamOutSync(&Buffer[BufferNum])) + { + throw std::runtime_error("Initial midiStreamOut failed"); + } + BufferNum ^= 1; + } + else if (res == SONG_DONE) + { + // Do not play super short songs that can't fill the initial two buffers. + Stop(); + return; + } + else + { + Stop(); + return; + } + } + while (BufferNum != 0); +} + +//========================================================================== +// +// MIDIStreamer :: Pause +// +// "Pauses" the song by setting it to zero volume and filling subsequent +// buffers with NOPs until the song is unpaused. A MIDI device that +// supports real pauses will return true from its Pause() method. +// +//========================================================================== + +void MIDIStreamer::Pause() +{ + if (m_Status == STATE_Playing) + { + m_Status = STATE_Paused; + if (!MIDI->Pause(true)) + { + OutputVolume(0); + } + } +} + +//========================================================================== +// +// MIDIStreamer :: Resume +// +// "Unpauses" a song by restoring the volume and letting subsequent +// buffers store real MIDI events again. +// +//========================================================================== + +void MIDIStreamer::Resume() +{ + if (m_Status == STATE_Paused) + { + if (!MIDI->Pause(false)) + { + OutputVolume(Volume); + } + m_Status = STATE_Playing; + } +} + +//========================================================================== +// +// MIDIStreamer :: Stop +// +// Stops playback and closes the player thread and MIDI device. +// +//========================================================================== + +void MIDIStreamer::Stop() +{ + EndQueued = 4; + + if (MIDI != NULL && MIDI->IsOpen()) + { + MIDI->Stop(); + MIDI->UnprepareHeader(&Buffer[0]); + MIDI->UnprepareHeader(&Buffer[1]); + MIDI->Close(); + } + if (MIDI != nullptr) + { + MIDI.reset(); + } + m_Status = STATE_Stopped; +} + +//========================================================================== +// +// MIDIStreamer :: IsPlaying +// +//========================================================================== + +bool MIDIStreamer::IsPlaying() +{ + if (m_Status != STATE_Stopped && (MIDI == NULL || (EndQueued != 0 && EndQueued < 4))) + { + std::lock_guard lock(CritSec); + Stop(); + } + if (m_Status != STATE_Stopped && !MIDI->IsOpen()) + { + std::lock_guard lock(CritSec); + Stop(); + } + return m_Status != STATE_Stopped; +} + +//========================================================================== +// +// MIDIStreamer :: MusicVolumeChanged +// +// WinMM MIDI doesn't go through the sound system, so the normal volume +// changing procedure doesn't work for it. +// +//========================================================================== + +void MIDIStreamer::MusicVolumeChanged() +{ + if (MIDI != NULL && MIDI->FakeVolume()) + { + float realvolume = miscConfig.snd_musicvolume * miscConfig.relative_volume * miscConfig.snd_mastervolume; + if (realvolume < 0 || realvolume > 1) realvolume = 1; + Volume = (uint32_t)(realvolume * 65535.f); + } + else + { + Volume = 0xFFFF; + } + source->setVolume(Volume); + if (m_Status == STATE_Playing) + { + OutputVolume(Volume); + } +} + +//========================================================================== +// +// MIDIStreamer :: ChangeSettingInt +// +//========================================================================== + +void MIDIStreamer::ChangeSettingInt(const char *setting, int value) +{ + if (MIDI != NULL) + { + MIDI->ChangeSettingInt(setting, value); + } +} + +//========================================================================== +// +// MIDIStreamer :: ChangeSettingNum +// +//========================================================================== + +void MIDIStreamer::ChangeSettingNum(const char *setting, double value) +{ + if (MIDI != NULL) + { + MIDI->ChangeSettingNum(setting, value); + } +} + +//========================================================================== +// +// MIDIDeviceStreamer :: ChangeSettingString +// +//========================================================================== + +void MIDIStreamer::ChangeSettingString(const char *setting, const char *value) +{ + if (MIDI != NULL) + { + MIDI->ChangeSettingString(setting, value); + } +} + + +//========================================================================== +// +// MIDIStreamer :: OutputVolume +// +// Signals the buffer filler to send volume change events on all channels. +// +//========================================================================== + +void MIDIStreamer::OutputVolume (uint32_t volume) +{ + if (MIDI != NULL && MIDI->FakeVolume()) + { + NewVolume = volume; + VolumeChanged = true; + } +} + +//========================================================================== +// +// MIDIStreamer :: Callback Static +// +//========================================================================== + +void MIDIStreamer::Callback(void *userdata) +{ + MIDIStreamer *self = (MIDIStreamer *)userdata; + + if (self->EndQueued >= 4) + { + return; + } + self->ServiceEvent(); +} + +//========================================================================== +// +// MIDIStreamer :: Update +// +// Called periodically to see if the player thread is still alive. If it +// isn't, stop playback now. +// +//========================================================================== + +void MIDIStreamer::Update() +{ + if (MIDI != nullptr && !MIDI->Update()) + { + std::lock_guard lock(CritSec); + Stop(); + } +} + +//========================================================================== +// +// MIDIStreamer :: ServiceEvent +// +// Fills the buffer that just finished playing with new events and appends +// it to the MIDI stream queue. Stops the song if playback is over. Returns +// non-zero if a problem occured and playback should stop. +// +//========================================================================== + +int MIDIStreamer::ServiceEvent() +{ + int res; + + if (EndQueued == 2) + { + return 0; + } + if (0 != (res = MIDI->UnprepareHeader(&Buffer[BufferNum]))) + { + return res; + } +fill: + if (EndQueued == 1) + { + res = FillStopBuffer(BufferNum); + if ((res & 3) != SONG_ERROR) + { + EndQueued = 2; + } + } + else + { + res = FillBuffer(BufferNum, MAX_MIDI_EVENTS, MAX_TIME); + } + switch (res & 3) + { + case SONG_MORE: + res = MIDI->StreamOut(&Buffer[BufferNum]); + if (res != 0) + { + return res; + } + else + { + BufferNum ^= 1; + } + break; + + case SONG_DONE: + if (m_Looping) + { + Restarting = true; + goto fill; + } + EndQueued = 1; + break; + + default: + return res >> 2; + } + return 0; +} + +//========================================================================== +// +// MIDIStreamer :: FillBuffer +// +// Copies MIDI events from the MIDI file and puts them into a MIDI stream +// buffer. Filling the buffer stops when the song end is encountered, the +// buffer space is used up, or the maximum time for a buffer is hit. +// +// Can return: +// - SONG_MORE if the buffer was prepared with data. +// - SONG_DONE if the song's end was reached. +// The buffer will never have data in this case. +// - SONG_ERROR if there was a problem preparing the buffer. +// +//========================================================================== + +int MIDIStreamer::FillBuffer(int buffer_num, int max_events, uint32_t max_time) +{ + if (!Restarting && source->CheckDone()) + { + return SONG_DONE; + } + + int i; + uint32_t *events = Events[buffer_num], *max_event_p; + + + // The final event is for a NOP to hold the delay from the last event. + max_event_p = events + (max_events - 1) * 3; + + if (InitialPlayback) + { + InitialPlayback = false; + // Send the GM System Enable SysEx message. + events[0] = 0; // dwDeltaTime + events[1] = 0; // dwStreamID + events[2] = (MEVENT_LONGMSG << 24) | 6; // dwEvent + events[3] = MAKE_ID(0xf0, 0x7e, 0x7f, 0x09); // dwParms[0] + events[4] = MAKE_ID(0x01, 0xf7, 0x00, 0x00); // dwParms[1] + events += 5; + + if (MIDI->CanHandleSysex()) + { + // Send the GS DT1 MODE SET GS Reset SysEx message. + events[0] = 0; // dwDeltaTime + events[1] = 0; // dwStreamID + events[2] = (MEVENT_LONGMSG << 24) | 11; // dwEvent + events[3] = MAKE_ID(0xf0, 0x41, 0x7f, 0x42); // dwParms[0] + events[4] = MAKE_ID(0x12, 0x40, 0x00, 0x7f); // dwParms[1] + events[5] = MAKE_ID(0x00, 0x41, 0xf7, 0x00); // dwParms[2] + events += 6; + } + + // Send the full master volume SysEx message. + events[0] = 0; // dwDeltaTime + events[1] = 0; // dwStreamID + events[2] = (MEVENT_LONGMSG << 24) | 8; // dwEvent + events[3] = MAKE_ID(0xf0,0x7f,0x7f,0x04); // dwParms[0] + events[4] = MAKE_ID(0x01,0x7f,0x7f,0xf7); // dwParms[1] + events += 5; + source->DoInitialSetup(); + } + + // If the volume has changed, stick those events at the start of this buffer. + if (VolumeChanged && (m_Status != STATE_Paused || NewVolume == 0)) + { + VolumeChanged = false; + for (i = 0; i < 16; ++i) + { + uint8_t courseVol = (uint8_t)(((source->getChannelVolume(i)+1) * NewVolume) >> 16); + events[0] = 0; // dwDeltaTime + events[1] = 0; // dwStreamID + events[2] = MIDI_CTRLCHANGE | i | (7<<8) | (courseVol<<16); + events += 3; + } + } + + // Play nothing while paused. + if (m_Status == STATE_Paused) + { + // Be more responsive when unpausing by only playing each buffer + // for a third of the maximum time. + events[0] = std::max(1, (max_time / 3) * source->getDivision() / source->getTempo()); + events[1] = 0; + events[2] = MEVENT_NOP << 24; + events += 3; + } + else + { + if (Restarting) + { + Restarting = false; + // Reset the tempo to the inital value. + events[0] = 0; // dwDeltaTime + events[1] = 0; // dwStreamID + events[2] = (MEVENT_TEMPO << 24) | source->getInitialTempo(); // dwEvent + events += 3; + // Stop all notes in case any were left hanging. + events = WriteStopNotes(events); + source->DoRestart(); + } + events = source->MakeEvents(events, max_event_p, max_time); + } + memset(&Buffer[buffer_num], 0, sizeof(MidiHeader)); + Buffer[buffer_num].lpData = (uint8_t *)Events[buffer_num]; + Buffer[buffer_num].dwBufferLength = uint32_t((uint8_t *)events - Buffer[buffer_num].lpData); + Buffer[buffer_num].dwBytesRecorded = Buffer[buffer_num].dwBufferLength; + if (0 != (i = MIDI->PrepareHeader(&Buffer[buffer_num]))) + { + return SONG_ERROR | (i << 2); + } + return SONG_MORE; +} + +//========================================================================== +// +// MIDIStreamer :: FillStopBuffer +// +// Fills a MIDI buffer with events to stop all channels. +// +//========================================================================== + +int MIDIStreamer::FillStopBuffer(int buffer_num) +{ + uint32_t *events = Events[buffer_num]; + int i; + + events = WriteStopNotes(events); + + // wait some tics, just so that this buffer takes some time + events[0] = 500; + events[1] = 0; + events[2] = MEVENT_NOP << 24; + events += 3; + + memset(&Buffer[buffer_num], 0, sizeof(MidiHeader)); + Buffer[buffer_num].lpData = (uint8_t*)Events[buffer_num]; + Buffer[buffer_num].dwBufferLength = uint32_t((uint8_t*)events - Buffer[buffer_num].lpData); + Buffer[buffer_num].dwBytesRecorded = Buffer[buffer_num].dwBufferLength; + if (0 != (i = MIDI->PrepareHeader(&Buffer[buffer_num]))) + { + return SONG_ERROR | (i << 2); + } + return SONG_MORE; +} + +//========================================================================== +// +// MIDIStreamer :: WriteStopNotes +// +// Generates MIDI events to stop all notes and reset controllers on +// every channel. +// +//========================================================================== + +uint32_t *MIDIStreamer::WriteStopNotes(uint32_t *events) +{ + for (int i = 0; i < 16; ++i) + { + events[0] = 0; // dwDeltaTime + events[1] = 0; // dwStreamID + events[2] = MIDI_CTRLCHANGE | i | (123 << 8); // All notes off + events[3] = 0; + events[4] = 0; + events[5] = MIDI_CTRLCHANGE | i | (121 << 8); // Reset controllers + events += 6; + } + return events; +} + +//========================================================================== +// +// +// +//========================================================================== + +void MIDIStreamer::SetMIDISource(MIDISource *_source) +{ + source.reset(_source); + source->setTempoCallback([=](int tempo) { return !!MIDI->SetTempo(tempo); } ); +} + +int MIDIStreamer::GetDeviceType() const +{ + return nullptr == MIDI + ? MusInfo::GetDeviceType() + : MIDI->GetDeviceType(); +} + +//========================================================================== +// +// MIDIStreamer :: GetStats +// +//========================================================================== + +std::string MIDIStreamer::GetStats() +{ + if (MIDI == NULL) + { + return "No MIDI device in use."; + } + return MIDI->GetStats(); +} + +//========================================================================== +// +// MIDIStreamer :: SetSubsong +// +// Selects which subsong to play in an already-playing file. This is public. +// +//========================================================================== + +bool MIDIStreamer::SetSubsong(int subsong) +{ + if (source->SetMIDISubsong(subsong)) + { + Stop(); + Play(m_Looping, subsong); + return true; + } + return false; +} + +//========================================================================== +// +// MIDIStreamer :: FillStream +// +//========================================================================== + +bool MIDIStreamer::ServiceStream(void* buff, int len) +{ + if (!MIDI) return false; + return static_cast(MIDI.get())->ServiceStream(buff, len); +} + +//========================================================================== +// +// create a streamer +// +//========================================================================== + +MusInfo* CreateMIDIStreamer(MIDISource *source, EMidiDevice devtype, const char* args) +{ + auto me = new MIDIStreamer(devtype, args); + me->SetMIDISource(source); + return me; +} + +DLL_EXPORT zmusic_bool ZMusic_MIDIDumpWave(ZMusic_MidiSource source, EMidiDevice devtype, const char *devarg, const char *outname, int subsong, int samplerate) +{ + try + { + MIDIStreamer me(devtype, devarg ? devarg : ""); + me.SetMIDISource(source); + me.DumpWave(outname, subsong, samplerate); + return true; + } + catch (const std::exception & ex) + { + SetError(ex.what()); + return false; + } +} diff --git a/source/musicformats/music_stream.cpp b/source/musicformats/music_stream.cpp new file mode 100644 index 000000000..86299f040 --- /dev/null +++ b/source/musicformats/music_stream.cpp @@ -0,0 +1,177 @@ +/* +** music_stream.cpp +** Plays a streaming song from a StreamSource +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** Copyright 2019 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +*/ + +#include "zmusic/musinfo.h" +#include "zmusic/zmusic_internal.h" +#include "streamsources/streamsource.h" + +class StreamSong : public MusInfo +{ +public: + StreamSong (StreamSource *source); + ~StreamSong (); + void Play (bool looping, int subsong) override; + void Pause () override; + void Resume () override; + void Stop () override; + bool IsPlaying () override; + bool IsValid () const override { return m_Source != nullptr; } + bool SetPosition (unsigned int pos) override; + bool SetSubsong (int subsong) override; + std::string GetStats() override; + void ChangeSettingInt(const char *name, int value) override { if (m_Source) m_Source->ChangeSettingInt(name, value); } + void ChangeSettingNum(const char *name, double value) override { if (m_Source) m_Source->ChangeSettingNum(name, value); } + void ChangeSettingString(const char *name, const char *value) override { if(m_Source) m_Source->ChangeSettingString(name, value); } + bool ServiceStream(void* buff, int len) override; + SoundStreamInfoEx GetStreamInfoEx() const override { return m_Source->GetFormatEx(); } + + +protected: + + StreamSource *m_Source = nullptr; +}; + + + +void StreamSong::Play (bool looping, int subsong) +{ + m_Status = STATE_Stopped; + m_Looping = looping; + + if (m_Source != nullptr) + { + m_Source->SetPlayMode(looping); + m_Source->SetSubsong(subsong); + if (m_Source->Start()) + { + m_Status = STATE_Playing; + } + } +} + +void StreamSong::Pause () +{ + m_Status = STATE_Paused; +} + +void StreamSong::Resume () +{ + m_Status = STATE_Playing; +} + +void StreamSong::Stop () +{ + m_Status = STATE_Stopped; +} + +StreamSong::~StreamSong () +{ + Stop (); + if (m_Source != nullptr) + { + delete m_Source; + m_Source = nullptr; + } +} + +StreamSong::StreamSong (StreamSource *source) +{ + m_Source = source; +} + +bool StreamSong::IsPlaying () +{ + if (m_Status != STATE_Stopped) + { + return true; + } + return false; +} + +// +// StreamSong :: SetPosition +// +// Sets the position in ms. + +bool StreamSong::SetPosition(unsigned int pos) +{ + if (m_Source != nullptr) + { + return m_Source->SetPosition(pos); + } + else + { + return false; + } +} + +bool StreamSong::SetSubsong(int subsong) +{ + return m_Source->SetSubsong(subsong); +} + +std::string StreamSong::GetStats() +{ + std::string s1, s2; + if (m_Source != NULL) + { + auto stat = m_Source->GetStats(); + s2 = stat.c_str(); + } + if (s1.empty() && s2.empty()) return "No song loaded\n"; + if (s1.empty()) return s2; + if (s2.empty()) return s1; + return s1 + "\n" + s2; +} + +bool StreamSong::ServiceStream (void *buff, int len) +{ + bool written = m_Source->GetData(buff, len); + if (!written) + { + m_Status = STATE_Stopped; + memset((char*)buff, 0, len); + return false; + } + return true; +} + +MusInfo *OpenStreamSong(StreamSource *source) +{ + auto song = new StreamSong(source); + if (song->IsValid()) return song; + delete song; + return nullptr; +} + diff --git a/source/musicformats/win32/helperthread.cpp b/source/musicformats/win32/helperthread.cpp new file mode 100644 index 000000000..ef6d0d3e9 --- /dev/null +++ b/source/musicformats/win32/helperthread.cpp @@ -0,0 +1,292 @@ +/* +** helperthread.cpp +** +** Implements FHelperThread, the base class for helper threads. Includes +** a message queue for passing messages from the main thread to the +** helper thread. (Only used by the CD Audio player) +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#include "helperthread.h" + +//========================================================================== +// +// ---Constructor--- +// +//========================================================================== + +FHelperThread::FHelperThread () +{ + ThreadHandle = NULL; + ThreadID = 0; + Thread_Events[0] = Thread_Events[1] = 0; + memset (Messages, 0, sizeof(Messages)); + MessageHead = 0; + MessageTail = 0; +} + +//========================================================================== +// +// ---Destructor--- +// +//========================================================================== + +FHelperThread::~FHelperThread () +{ + DestroyThread (); +} + +//========================================================================== +// +// LaunchThread +// +//========================================================================== + +bool FHelperThread::LaunchThread () +{ + int i; + + MessageHead = MessageTail = 0; + for (i = 0; i < MSG_QUEUE_SIZE; i++) + { + if ((Messages[i].CompletionEvent = CreateEvent (NULL, FALSE, i > 0, NULL)) == NULL) + break; + } + if (i < MSG_QUEUE_SIZE) + { + for (; i >= 0; i--) + { + CloseHandle (Messages[i].CompletionEvent); + } + return false; + } + InitializeCriticalSection (&Thread_Critical); + if ((Thread_Events[0] = CreateEvent (NULL, TRUE, FALSE, NULL))) + { + if ((Thread_Events[1] = CreateEvent (NULL, TRUE, FALSE, NULL))) + { + if ((ThreadHandle = CreateThread (NULL, 0, ThreadLaunch, (LPVOID)this, 0, &ThreadID))) + { + HANDLE waiters[2] = { Messages[0].CompletionEvent, ThreadHandle }; + + if (WaitForMultipleObjects (2, waiters, FALSE, INFINITE) == WAIT_OBJECT_0+1) + { // Init failed, and the thread exited + DestroyThread (); + return false; + } +#if defined(_MSC_VER) && defined(_DEBUG) + // Give the thread a name in the debugger + struct + { + DWORD type; + LPCSTR name; + DWORD threadID; + DWORD flags; + } info = + { + 0x1000, ThreadName(), ThreadID, 0 + }; + __try + { + RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR *)&info ); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +#endif + + return true; + } + CloseHandle (Thread_Events[1]); + } + CloseHandle (Thread_Events[0]); + } + DeleteCriticalSection (&Thread_Critical); + for (i = 0; i < MSG_QUEUE_SIZE; i++) + { + CloseHandle (Messages[i].CompletionEvent); + } + return false; +} + +//========================================================================== +// +// DestroyThread +// +//========================================================================== + +void FHelperThread::DestroyThread () +{ + int i; + + if (ThreadHandle == NULL) + return; + + SetEvent (Thread_Events[THREAD_KillSelf]); + // 5 seconds should be sufficient. If not, then we'll crash, but at + // least that's better than hanging indefinitely. + WaitForSingleObject (ThreadHandle, 5000); + CloseHandle (ThreadHandle); + + EnterCriticalSection (&Thread_Critical); + + for (i = 0; i < 8; i++) + { + CloseHandle (Messages[i].CompletionEvent); + } + CloseHandle (Thread_Events[0]); + CloseHandle (Thread_Events[1]); + + LeaveCriticalSection (&Thread_Critical); + DeleteCriticalSection (&Thread_Critical); + + ThreadHandle = NULL; +} + +//========================================================================== +// +// HaveThread +// +//========================================================================== + +bool FHelperThread::HaveThread () const +{ + return ThreadHandle != NULL; +} + +//========================================================================== +// +// SendMessage +// +//========================================================================== + +DWORD FHelperThread::SendMessage (DWORD method, + DWORD parm1, DWORD parm2, DWORD parm3, bool wait) +{ + for (;;) + { + EnterCriticalSection (&Thread_Critical); + if (MessageHead - MessageTail == MSG_QUEUE_SIZE) + { // No room, so wait for oldest message to complete + HANDLE waitEvent = Messages[MessageTail].CompletionEvent; + LeaveCriticalSection (&Thread_Critical); + WaitForSingleObject (waitEvent, 1000); + } + + int head = MessageHead++ % MSG_QUEUE_SIZE; + Messages[head].Method = method; + Messages[head].Parm1 = parm1; + Messages[head].Parm2 = parm2; + Messages[head].Parm3 = parm3; + ResetEvent (Messages[head].CompletionEvent); + LeaveCriticalSection (&Thread_Critical); + + SetEvent (Thread_Events[THREAD_NewMessage]); + + if (wait) + { + WaitForSingleObject (Messages[head].CompletionEvent, INFINITE); + return Messages[head].Return; + } + return 0; + } +} + +//========================================================================== +// +// ThreadLaunch (static) +// +//========================================================================== + +DWORD WINAPI FHelperThread::ThreadLaunch (LPVOID me) +{ + return ((FHelperThread *)me)->ThreadLoop (); +} + +//========================================================================== +// +// ThreadLoop +// +//========================================================================== + +DWORD FHelperThread::ThreadLoop () +{ + if (!Init ()) + { + ExitThread (0); + } + else + { + SetEvent (Messages[0].CompletionEvent); + } + + for (;;) + { + switch (MsgWaitForMultipleObjects (2, Thread_Events, + FALSE, INFINITE, QS_ALLEVENTS)) + { + case WAIT_OBJECT_0+1: + // We should quit now. + Deinit (); + ExitThread (0); + break; + + case WAIT_OBJECT_0: + // Process any new messages. MessageTail is not updated until *after* + // the message is finished processing. + for (;;) + { + EnterCriticalSection (&Thread_Critical); + if (MessageHead == MessageTail) + { + ResetEvent (Thread_Events[0]); + LeaveCriticalSection (&Thread_Critical); + break; // Resume outer for (Wait for more messages) + } + int spot = MessageTail % MSG_QUEUE_SIZE; + LeaveCriticalSection (&Thread_Critical); + + Messages[spot].Return = Dispatch (Messages[spot].Method, + Messages[spot].Parm1, Messages[spot].Parm2, + Messages[spot].Parm3); + + // No need to enter critical section, because only the CD thread + // is allowed to touch MessageTail or signal the CompletionEvent + SetEvent (Messages[spot].CompletionEvent); + MessageTail++; + } + break; + + default: + DefaultDispatch (); + } + } +} diff --git a/source/musicformats/win32/helperthread.h b/source/musicformats/win32/helperthread.h new file mode 100644 index 000000000..45ca9a706 --- /dev/null +++ b/source/musicformats/win32/helperthread.h @@ -0,0 +1,85 @@ +/* +** helperthread.h +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 __HELPERTHREAD_H__ +#define __HELPERTHREAD_H__ + +#define WIN32_LEAN_AND_MEAN +#include + +class FHelperThread +{ + struct Message + { + DWORD Method; + DWORD Parm1, Parm2, Parm3; + HANDLE CompletionEvent; // Set to signalled when message is finished processing + DWORD Return; + }; + + enum { MSG_QUEUE_SIZE = 8 }; + +public: + FHelperThread (); + virtual ~FHelperThread (); + void DestroyThread (); + + bool LaunchThread (); + DWORD SendMessage (DWORD method, DWORD parm1, DWORD parm2, + DWORD parm3, bool wait); + bool HaveThread () const; + +protected: + enum EThreadEvents { THREAD_NewMessage, THREAD_KillSelf }; + virtual bool Init () { return true; } + virtual void Deinit () {} + virtual void DefaultDispatch () {} + virtual DWORD Dispatch (DWORD method, DWORD parm1=0, DWORD parm2=0, DWORD parm3=0) = 0; + virtual const char *ThreadName () = 0; + + HANDLE ThreadHandle; + DWORD ThreadID; + HANDLE Thread_Events[2]; + CRITICAL_SECTION Thread_Critical; + Message Messages[MSG_QUEUE_SIZE]; + DWORD MessageHead; + DWORD MessageTail; + +private: + void ReleaseSynchronizers (); + static DWORD WINAPI ThreadLaunch (LPVOID me); + + DWORD ThreadLoop (); +}; + +#endif //__HELPERTHREAD_H__ diff --git a/source/musicformats/win32/i_cd.cpp b/source/musicformats/win32/i_cd.cpp new file mode 100644 index 000000000..5eb8b41e0 --- /dev/null +++ b/source/musicformats/win32/i_cd.cpp @@ -0,0 +1,762 @@ +/* +** i_cd.cpp +** Functions for controlling CD playback +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include "zmusic_internal.h" +#include "helperthread.h" +#include "i_cd.h" + +enum +{ + CDM_Init, // parm1 = device + CDM_Close, + CDM_Play, // parm1 = track, parm2 = 1:looping + CDM_PlayCD, // parm1 = 1:looping + CDM_Replay, // Redos the most recent CDM_Play(CD) + CDM_Stop, + CDM_Eject, + CDM_UnEject, + CDM_Pause, + CDM_Resume, + CDM_GetMode, + CDM_CheckTrack, // parm1 = track + CDM_GetMediaIdentity, + CDM_GetMediaUPC +}; + +class FCDThread : public FHelperThread +{ +public: + FCDThread (); + +protected: + bool Init (); + void Deinit (); + const char *ThreadName (); + DWORD Dispatch (DWORD method, DWORD parm1=0, DWORD parm2=0, DWORD parm3=0); + void DefaultDispatch (); + + bool IsTrackAudio (DWORD track) const; + DWORD GetTrackLength (DWORD track) const; + DWORD GetNumTracks () const; + + static LRESULT CALLBACK CD_WndProc (HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam); + + WNDCLASS CD_WindowClass; + HWND CD_Window; + ATOM CD_WindowAtom; + + bool Looping; + DWORD PlayFrom; + DWORD PlayTo; + UINT DeviceID; +}; + +#define NOT_INITED ((signed)0x80000000) + +static FCDThread *CDThread; +static int Inited = NOT_INITED; +static int Enabled = false; + +//========================================================================== +// +// ---Constructor--- +// +//========================================================================== + +FCDThread::FCDThread () +{ + memset (&CD_WindowClass, 0, sizeof(CD_WindowClass)); + CD_Window = NULL; + CD_WindowAtom = 0; + Inited = NOT_INITED; + Looping = false; + PlayFrom = 0; + PlayTo = 0; + DeviceID = 0; + LaunchThread (); +} + +//========================================================================== +// +// ThreadName +// +//========================================================================== + +const char *FCDThread::ThreadName () +{ + return "CD Player"; +} + +//========================================================================== +// +// Init +// +//========================================================================== + +bool FCDThread::Init () +{ + CD_WindowClass.style = CS_NOCLOSE; + CD_WindowClass.lpfnWndProc = CD_WndProc; + CD_WindowClass.hInstance = GetModuleHandle(nullptr); + CD_WindowClass.lpszClassName = L"ZMusic CD Player"; + CD_WindowAtom = RegisterClass (&CD_WindowClass); + + if (CD_WindowAtom == 0) + return false; + + CD_Window = CreateWindow ( + (LPCTSTR)(INT_PTR)(int)CD_WindowAtom, + CD_WindowClass.lpszClassName, + 0, + 0, 0, 10, 10, + NULL, + NULL, + CD_WindowClass.hInstance, + NULL); + + if (CD_Window == NULL) + { + UnregisterClass ((LPCTSTR)(INT_PTR)(int)CD_WindowAtom, CD_WindowClass.hInstance); + CD_WindowAtom = 0; + return false; + } + +#ifdef _WIN64 + SetWindowLongPtr (CD_Window, GWLP_USERDATA, (LONG_PTR)this); +#else + SetWindowLong (CD_Window, GWL_USERDATA, (LONG)(LONG_PTR)this); +#endif + SetThreadPriority (ThreadHandle, THREAD_PRIORITY_LOWEST); + return true; +} + +//========================================================================== +// +// Deinit +// +//========================================================================== + +void FCDThread::Deinit () +{ + if (CD_Window) + { + DestroyWindow (CD_Window); + CD_Window = NULL; + } + if (CD_WindowAtom) + { + UnregisterClass ((LPCTSTR)(INT_PTR)(int)CD_WindowAtom, GetModuleHandle(nullptr)); + CD_WindowAtom = 0; + } + if (DeviceID) + { + Dispatch (CDM_Close); + } +} + +//========================================================================== +// +// DefaultDispatch +// +//========================================================================== + +void FCDThread::DefaultDispatch () +{ + MSG wndMsg; + + while (PeekMessage (&wndMsg, NULL, 0, 0, PM_REMOVE)) + { + DispatchMessage (&wndMsg); + } +} + +//========================================================================== +// +// Dispatch +// +// Does the actual work of implementing the public CD interface +// +//========================================================================== + +DWORD FCDThread::Dispatch (DWORD method, DWORD parm1, DWORD parm2, DWORD parm3) +{ + MCI_OPEN_PARMS mciOpen; + MCI_SET_PARMS mciSetParms; + MCI_PLAY_PARMS playParms; + MCI_STATUS_PARMS statusParms; + MCI_INFO_PARMS infoParms; + DWORD firstTrack, lastTrack, numTracks; + DWORD length; + DWORD openFlags; + wchar_t ident[32]; + + switch (method) + { + case CDM_Init: + mciOpen.lpstrDeviceType = (LPCWSTR)MCI_DEVTYPE_CD_AUDIO; + openFlags = MCI_OPEN_SHAREABLE|MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID|MCI_WAIT; + if ((signed)parm1 >= 0) + { + ident[0] = (char)(parm1 + 'A'); + ident[1] = ':'; + ident[2] = 0; + mciOpen.lpstrElementName = ident; + openFlags |= MCI_OPEN_ELEMENT; + } + + while (mciSendCommand (0, MCI_OPEN, openFlags, (DWORD_PTR)&mciOpen) != 0) + { + if (!(openFlags & MCI_OPEN_ELEMENT)) + { + return FALSE; + } + openFlags &= ~MCI_OPEN_ELEMENT; + } + + DeviceID = mciOpen.wDeviceID; + mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF; + if (mciSendCommand (DeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&mciSetParms) == 0) + { + return TRUE; + } + + mciSendCommand (DeviceID, MCI_CLOSE, 0, 0); + return FALSE; + + case CDM_Close: + Dispatch (CDM_Stop); + mciSendCommand (DeviceID, MCI_CLOSE, 0, 0); + DeviceID = 0; + return 0; + + case CDM_Play: + if (!IsTrackAudio (parm1)) + { + //Printf ("Track %d is not audio\n", track); + return FALSE; + } + + length = GetTrackLength (parm1); + + if (length == 0) + { // Couldn't get length of track, so won't be able to play last track + PlayTo = MCI_MAKE_TMSF (parm1+1, 0, 0, 0); + } + else + { + PlayTo = MCI_MAKE_TMSF (parm1, + MCI_MSF_MINUTE(length), + MCI_MSF_SECOND(length), + MCI_MSF_FRAME(length)); + } + PlayFrom = MCI_MAKE_TMSF (parm1, 0, 0, 0); + Looping = parm2 & 1; + + // intentional fall-through + + case CDM_Replay: + playParms.dwFrom = PlayFrom; + playParms.dwTo = PlayTo; + playParms.dwCallback = (DWORD_PTR)CD_Window; + + return mciSendCommand (DeviceID, MCI_PLAY, MCI_FROM | MCI_TO | MCI_NOTIFY, + (DWORD_PTR)&playParms); + + case CDM_PlayCD: + numTracks = GetNumTracks (); + if (numTracks == 0) + return FALSE; + + for (firstTrack = 1; firstTrack <= numTracks && !IsTrackAudio (firstTrack); firstTrack++) + ; + for (lastTrack = firstTrack; lastTrack <= numTracks && IsTrackAudio (lastTrack); lastTrack++) + ; + + if (firstTrack > numTracks) + return FALSE; + if (lastTrack > numTracks) + lastTrack = numTracks; + + length = GetTrackLength (lastTrack); + if (length == 0) + return FALSE; + + Looping = parm1 & 1; + PlayFrom = MCI_MAKE_TMSF (firstTrack, 0, 0, 0); + PlayTo = MCI_MAKE_TMSF (lastTrack, + MCI_MSF_MINUTE(length), + MCI_MSF_SECOND(length), + MCI_MSF_FRAME(length)); + + playParms.dwFrom = PlayFrom; + playParms.dwTo = PlayTo; + playParms.dwCallback = (DWORD_PTR)CD_Window; + + return mciSendCommand (DeviceID, MCI_PLAY, MCI_FROM|MCI_TO|MCI_NOTIFY, + (DWORD_PTR)&playParms); + + case CDM_Stop: + return mciSendCommand (DeviceID, MCI_STOP, 0, 0); + + case CDM_Eject: + return mciSendCommand (DeviceID, MCI_SET, MCI_SET_DOOR_OPEN, 0); + + case CDM_UnEject: + return mciSendCommand (DeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, 0); + + case CDM_Pause: + return mciSendCommand (DeviceID, MCI_PAUSE, 0, 0); + + case CDM_Resume: + playParms.dwTo = PlayTo; + playParms.dwCallback = (DWORD_PTR)CD_Window; + return mciSendCommand (DeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, (DWORD_PTR)&playParms); + + case CDM_GetMode: + statusParms.dwItem = MCI_STATUS_MODE; + if (mciSendCommand (DeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&statusParms)) + { + return CDMode_Unknown; + } + else + { + switch (statusParms.dwReturn) + { + case MCI_MODE_NOT_READY: return CDMode_NotReady; + case MCI_MODE_PAUSE: return CDMode_Pause; + case MCI_MODE_PLAY: return CDMode_Play; + case MCI_MODE_STOP: return CDMode_Stop; + case MCI_MODE_OPEN: return CDMode_Open; + default: return CDMode_Unknown; + } + } + + case CDM_CheckTrack: + return IsTrackAudio (parm1) ? TRUE : FALSE; + + case CDM_GetMediaIdentity: + case CDM_GetMediaUPC: + wchar_t ident[32]; + + infoParms.lpstrReturn = ident; + infoParms.dwRetSize = sizeof(ident); + if (mciSendCommand (DeviceID, MCI_INFO, + method == CDM_GetMediaIdentity ? MCI_WAIT|MCI_INFO_MEDIA_IDENTITY + : MCI_WAIT|MCI_INFO_MEDIA_UPC, (DWORD_PTR)&infoParms)) + { + return 0; + } + else + { + return wcstoul (ident, NULL, 0); + } + + default: + return 0; + } +} + +//========================================================================== +// +// KillThread +// +//========================================================================== + +static void KillThread () +{ + if (CDThread != NULL) + { + CDThread->DestroyThread (); + Inited = NOT_INITED; + delete CDThread; + } +} + +//========================================================================== +// +// CD_Init +// +//========================================================================== + +DLL_EXPORT zmusic_bool CD_Enable (const char *cd_drive) +{ + if (!cd_drive) + { + // lock the CD system. + Enabled = false; + CD_Close(); + return false; + } + Enabled = true; // this must have been called at least once to consider the use of the CD system + if ((cd_drive)[0] == 0 || (cd_drive)[1] != 0) + { + return CD_Init (-1); + } + else + { + char drive = toupper ((cd_drive)[0]); + + if (drive >= 'A' && drive <= 'Z' && !CD_Init(drive - 'A')) + { + return CD_Init(-1); + } + } + return true; +} + +bool CD_Init (int device) +{ + if (!Enabled) return false; + + if (CDThread == NULL) + { + CDThread = new FCDThread; + atexit (KillThread); + } + + if (Inited != device) + { + CD_Close (); + + if (CDThread->SendMessage (CDM_Init, device, 0, 0, true)) + { + Inited = device; + return true; + } + else + { + return false; + } + } + return true; +} + +//========================================================================== +// +// CD_InitID +// +//========================================================================== + +bool CD_InitID (unsigned int id, int guess) +{ + char drive; + + if (guess < 0 && Inited != NOT_INITED) + guess = Inited; + + if (guess >= 0 && CD_Init (guess)) + { + if (CDThread->SendMessage (CDM_GetMediaIdentity, 0, 0, 0, true) == id || + CDThread->SendMessage (CDM_GetMediaUPC, 0, 0, 0, true) == id) + { + return true; + } + CD_Close (); + } + + for (drive = 'V'; drive < 'Z'; drive++) + { + if (CD_Init (drive - 'A')) + { + // I don't know which value is stored in a CDDA file, so I try + // them both. All the CDs I've tested have had the same value + // for both, so it probably doesn't matter. + if (CDThread->SendMessage (CDM_GetMediaIdentity, 0, 0, 0, true) == id || + CDThread->SendMessage (CDM_GetMediaUPC, 0, 0, 0, true) == id) + { + return true; + } + CD_Close (); + } + } + return false; +} + +//========================================================================== +// +// CD_Close +// +//========================================================================== + +DLL_EXPORT void CD_Close () +{ + if (Inited != NOT_INITED) + { + CDThread->SendMessage (CDM_Close, 0, 0, 0, true); + Inited = NOT_INITED; + } +} + +//========================================================================== +// +// CD_Eject +// +//========================================================================== + +DLL_EXPORT void CD_Eject () +{ + if (Inited != NOT_INITED) + CDThread->SendMessage (CDM_Eject, 0, 0, 0, false); +} + +//========================================================================== +// +// CD_UnEject +// +//========================================================================== + +DLL_EXPORT zmusic_bool CD_UnEject () +{ + if (Inited == NOT_INITED) + return false; + + return CDThread->SendMessage (CDM_UnEject, 0, 0, 0, true) == 0; +} + +//========================================================================== +// +// CD_Stop +// +//========================================================================== + +DLL_EXPORT void CD_Stop () +{ + if (Inited != NOT_INITED) + CDThread->SendMessage (CDM_Stop, 0, 0, 0, false); +} + +//========================================================================== +// +// CD_Play +// +//========================================================================== + +bool CD_Play (int track, bool looping) +{ + if (Inited == NOT_INITED) + return false; + + return CDThread->SendMessage (CDM_Play, track, looping ? 1 : 0, 0, true) == 0; +} + +//========================================================================== +// +// CD_PlayNoWait +// +//========================================================================== + +void CD_PlayNoWait (int track, bool looping) +{ + if (Inited != NOT_INITED) + CDThread->SendMessage (CDM_Play, track, looping ? 1 : 0, 0, false); +} + +//========================================================================== +// +// CD_PlayCD +// +//========================================================================== + +bool CD_PlayCD (bool looping) +{ + if (Inited == NOT_INITED) + return false; + + return CDThread->SendMessage (CDM_PlayCD, looping ? 1 : 0, 0, 0, true) == 0; +} + +//========================================================================== +// +// CD_PlayCDNoWait +// +//========================================================================== + +void CD_PlayCDNoWait (bool looping) +{ + if (Inited != NOT_INITED) + CDThread->SendMessage (CDM_PlayCD, looping ? 1 : 0, 0, 0, false); +} + +//========================================================================== +// +// CD_Pause +// +//========================================================================== + +DLL_EXPORT void CD_Pause () +{ + if (Inited != NOT_INITED) + CDThread->SendMessage (CDM_Pause, 0, 0, 0, false); +} + +//========================================================================== +// +// CD_Resume +// +//========================================================================== + +DLL_EXPORT zmusic_bool CD_Resume () +{ + if (Inited == NOT_INITED) + return false; + + return CDThread->SendMessage (CDM_Resume, 0, 0, 0, false) == 0; +} + +//========================================================================== +// +// CD_GetMode +// +//========================================================================== + +ECDModes CD_GetMode () +{ + if (Inited == NOT_INITED) + return CDMode_Unknown; + + return (ECDModes)CDThread->SendMessage (CDM_GetMode, 0, 0, 0, true); +} + +//========================================================================== +// +// CD_CheckTrack +// +//========================================================================== + +bool CD_CheckTrack (int track) +{ + if (Inited == NOT_INITED) + return false; + + return CDThread->SendMessage (CDM_CheckTrack, track, 0, 0, true) != 0; +} + +// Functions called only by the helper thread ------------------------------- + +//========================================================================== +// +// IsTrackAudio +// +//========================================================================== + +bool FCDThread::IsTrackAudio (DWORD track) const +{ + MCI_STATUS_PARMS statusParms; + + statusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK; + statusParms.dwTrack = track; + if (mciSendCommand (DeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, + (DWORD_PTR)&statusParms)) + { + return FALSE; + } + return statusParms.dwReturn == MCI_CDA_TRACK_AUDIO; +} + +//========================================================================== +// +// GetTrackLength +// +//========================================================================== + +DWORD FCDThread::GetTrackLength (DWORD track) const +{ + MCI_STATUS_PARMS statusParms; + + statusParms.dwItem = MCI_STATUS_LENGTH; + statusParms.dwTrack = track; + if (mciSendCommand (DeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, + (DWORD_PTR)&statusParms)) + { + return 0; + } + else + { + return (DWORD)statusParms.dwReturn; + } +} + +//========================================================================== +// +// GetNumTracks +// +//========================================================================== + +DWORD FCDThread::GetNumTracks () const +{ + MCI_STATUS_PARMS statusParms; + + statusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + if (mciSendCommand (DeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&statusParms)) + { + return 0; + } + else + { + return (DWORD)statusParms.dwReturn; + } +} + +//========================================================================== +// +// CD_WndProc (static) +// +// Because MCI (under Win 9x anyway) can't notify windows owned by another +// thread, the helper thread creates its own window. +// +//========================================================================== + +LRESULT CALLBACK FCDThread::CD_WndProc (HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case MM_MCINOTIFY: + if (wParam == MCI_NOTIFY_SUCCESSFUL) + { + FCDThread *self = (FCDThread *)(LONG_PTR)GetWindowLongPtr (hWnd, GWLP_USERDATA); + // Using SendMessage could deadlock, so don't do that. + self->Dispatch (self->Looping ? CDM_Replay : CDM_Stop); + } + return 0; + + default: + return DefWindowProc (hWnd, message, wParam, lParam); + } +} diff --git a/source/musicformats/win32/i_cd.h b/source/musicformats/win32/i_cd.h new file mode 100644 index 000000000..6640d104f --- /dev/null +++ b/source/musicformats/win32/i_cd.h @@ -0,0 +1,72 @@ +/* +** i_cd.h +** Defines the CD interface +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 __I_CD_H__ +#define __I_CD_H__ + +enum ECDModes +{ + CDMode_Unknown, + CDMode_NotReady, + CDMode_Pause, + CDMode_Play, + CDMode_Stop, + CDMode_Open +}; + +// Opens a CD device. If device is non-negative, it specifies which device +// to open. 0 is drive A:, 1 is drive B:, etc. If device is not specified, +// the user's preference is used to decide which device to open. +bool CD_Init (int device = -1); + +// Open a CD device containing a specific CD. Tries device guess first. +bool CD_InitID (unsigned int id, int guess=-1); + +// Plays a single track, possibly looping +bool CD_Play (int track, bool looping); + +// Plays the first block of audio tracks on a CD, possibly looping +bool CD_PlayCD (bool looping); + +// Versions of the above that return as soon as possible +void CD_PlayNoWait (int track, bool looping); +void CD_PlayCDNoWait (bool looping); + +// Get the CD drive's status (mode) +ECDModes CD_GetMode (); + +// Check if a track exists and is audio +bool CD_CheckTrack (int track); + +#endif //__I_CD_H__ diff --git a/source/streamsources/music_dumb.cpp b/source/streamsources/music_dumb.cpp new file mode 100644 index 000000000..bf48b7dd4 --- /dev/null +++ b/source/streamsources/music_dumb.cpp @@ -0,0 +1,1232 @@ +/* +** music_dumb.cpp +** Alternative module player, using a modified DUMB for decoding. +** Based on the Foobar2000 component foo_dumb, version 0.9.8.4. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include +#include +#include "streamsource.h" + + +#undef CDECL // w32api's windef.h defines this +#include "../dumb/include/dumb.h" +#include "../dumb/include/internal/it.h" +#include "zmusic/m_swap.h" +#include "zmusic/mididefs.h" +#include "zmusic/midiconfig.h" +#include "fileio.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +DumbConfig dumbConfig; + +class DumbSong : public StreamSource +{ +public: + DumbSong(DUH *myduh, int samplerate); + ~DumbSong(); + //bool SetPosition(int ms); + bool SetSubsong(int subsong) override; + bool Start() override; + SoundStreamInfoEx GetFormatEx() override; + void ChangeSettingNum(const char* setting, double val) override; + std::string GetStats() override; + + std::string Codec; + std::string TrackerVersion; + std::string FormatVersion; + int NumChannels; + int NumPatterns; + int NumOrders; + float MasterVolume; + +protected: + int srate, interp, volramp; + int start_order; + double delta; + double length; + bool eof; + bool started = false; + size_t written; + DUH *duh; + DUH_SIGRENDERER *sr; + + bool open2(long pos); + long render(double volume, double delta, long samples, sample_t **buffer); + int decode_run(void *buffer, unsigned int size); + bool GetData(void *buffer, size_t len) override; + +}; + +#pragma pack(1) + +#if defined(__GNUC__) +#define FORCE_PACKED __attribute__((__packed__)) +#else +#define FORCE_PACKED +#endif + +typedef struct tagITFILEHEADER +{ + uint32_t id; // 0x4D504D49 + char songname[26]; + uint16_t reserved1; // 0x1004 + uint16_t ordnum; + uint16_t insnum; + uint16_t smpnum; + uint16_t patnum; + uint16_t cwtv; + uint16_t cmwt; + uint16_t flags; + uint16_t special; + uint8_t globalvol; + uint8_t mv; + uint8_t speed; + uint8_t tempo; + uint8_t sep; + uint8_t zero; + uint16_t msglength; + uint32_t msgoffset; + uint32_t reserved2; + uint8_t chnpan[64]; + uint8_t chnvol[64]; +} FORCE_PACKED ITFILEHEADER, *PITFILEHEADER; + +typedef struct MODMIDICFG +{ + char szMidiGlb[9*32]; // changed from CHAR + char szMidiSFXExt[16*32]; // changed from CHAR + char szMidiZXXExt[128*32]; // changed from CHAR +} FORCE_PACKED MODMIDICFG, *LPMODMIDICFG; + +#pragma pack() + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// ReadDUH +// +// Reads metadata from a generic module. +// +//========================================================================== + +static void ReadDUH(DUH * duh, DumbSong *info, bool meta, bool dos) +{ + if (!duh) return; + + DUMB_IT_SIGDATA * itsd = duh_get_it_sigdata(duh); + + if (!itsd) return; + + info->NumChannels = itsd->n_pchannels; + info->NumPatterns = itsd->n_patterns; + info->NumOrders = itsd->n_orders; + + if (meta) + { + if (itsd->name[0]) + { + //if (dos) info.meta_add(field_title, string_utf8_from_oem((char*)&itsd->name, sizeof(itsd->name))); + //else info.meta_add(field_title, pfc::stringcvt::string_utf8_from_ansi((char *)&itsd->name, sizeof(itsd->name))); + } + if (itsd->song_message && *itsd->song_message) + { + //if (dos) info.meta_add(field_comment, string_utf8_from_oem_multiline((char*)itsd->song_message)); + //else info.meta_add(field_comment, pfc::stringcvt::string_utf8_from_ansi((char *)itsd->song_message)); + } + } + + // std::string does not like nullptr assignments. Was this really necessary? :( + auto a = duh_get_tag(duh, "FORMAT"); + if (a) info->Codec = a; + a = duh_get_tag(duh, "TRACKERVERSION"); + if (a) info->TrackerVersion = a; + a = duh_get_tag(duh, "FORMATVERSION"); + if (a) info->FormatVersion = a; + + +#if 0 + std::string name; + + if (itsd->n_samples) + { + int i, n; + //info.info_set_int(field_samples, itsd->n_samples); + + if (meta && itsd->sample) + { + for (i = 0, n = itsd->n_samples; i < n; i++) + { + if (itsd->sample[i].name[0]) + { + name = "smpl"; + if (i < 10) name += '0'; + name += '0' + i; + //if (dos) info.meta_add(name, string_utf8_from_oem((char*)&itsd->sample[i].name, sizeof(itsd->sample[i].name))); + //else info.meta_add(name, pfc::stringcvt::string_utf8_from_ansi((char *)&itsd->sample[i].name, sizeof(itsd->sample[i].name))); + } + } + } + } + + if (itsd->n_instruments) + { + //info.info_set_int(field_instruments, itsd->n_instruments); + + if (meta && itsd->instrument) + { + for (i = 0, n = itsd->n_instruments; i < n; i++) + { + if (itsd->instrument[i].name[0]) + { + name = "inst"; + if (i < 10) name += '0'; + name += '0' + i; + //if (dos) info.meta_add(name, string_utf8_from_oem((char*)&itsd->instrument[i].name, sizeof(itsd->instrument[i].name))); + //else info.meta_add(name, pfc::stringcvt::string_utf8_from_ansi((char *)&itsd->instrument[i].name, sizeof(itsd->instrument[i].name))); + } + } + } + } +#endif +} + +//========================================================================== +// +// ReadIT +// +// Reads metadata from an IT file. +// +//========================================================================== + +static bool ReadIT(const uint8_t * ptr, unsigned size, DumbSong *info, bool meta) +{ + PITFILEHEADER pifh = (PITFILEHEADER) ptr; + if ((!ptr) || (size < 0x100)) return false; + if ((LittleLong(pifh->id) != 0x4D504D49) || + (LittleShort(pifh->insnum) >= 256) || + (!pifh->smpnum) || (LittleShort(pifh->smpnum) > 4000) || // XXX + (!pifh->ordnum)) return false; + if (sizeof(ITFILEHEADER) + LittleShort(pifh->ordnum) + + LittleShort(pifh->insnum)*4 + + LittleShort(pifh->smpnum)*4 + + LittleShort(pifh->patnum)*4 > size) return false; + + char ver[40]; + + snprintf(ver, 40, "IT v%u.%02x", LittleShort(pifh->cmwt) >> 8, LittleShort(pifh->cmwt) & 255); + info->Codec = ver; + + snprintf(ver, 40, "%u.%02x", LittleShort(pifh->cwtv) >> 8, LittleShort(pifh->cwtv) & 255); + info->TrackerVersion = ver; + + //if ( pifh->smpnum ) info.info_set_int( field_samples, LittleShort(pifh->smpnum) ); + //if ( pifh->insnum ) info.info_set_int( field_instruments, LittleShort(pifh->insnum) ); + + //if ( meta && pifh->songname[0] ) info.meta_add( field_title, string_utf8_from_it( (char*)&pifh->songname, 26 ) ); + + unsigned n, l, m_nChannels = 0; + + // bah, some file (jm-romance.it) with message length rounded up to a multiple of 256 (384 instead of 300) + unsigned msgoffset = LittleLong(pifh->msgoffset); + unsigned msgend = msgoffset + LittleShort(pifh->msglength); + + uint32_t * offset; +// std::string name; + + if (meta) + { + offset = (uint32_t *)(ptr + 0xC0 + LittleShort(pifh->ordnum) + LittleShort(pifh->insnum) * 4); + + for (n = 0, l = LittleShort(pifh->smpnum); n < l; n++, offset++) + { + uint32_t offset_n = LittleLong( *offset ); + if ( offset_n >= msgoffset && offset_n < msgend ) msgend = offset_n; + if ((!offset_n) || (offset_n + 0x14 + 26 + 2 >= size)) continue; + // XXX + if (ptr[offset_n] == 0 && ptr[offset_n + 1] == 0 && + ptr[offset_n + 2] == 'I' && ptr[offset_n + 3] == 'M' && + ptr[offset_n + 4] == 'P' && ptr[offset_n + 5] == 'S') + { + offset_n += 2; + } +#if 0 + if (*(ptr + offset_n + 0x14)) + { + name = "smpl"; + if (n < 10) name << '0'; + name << ('0' + n); + //info.meta_add(name, string_utf8_from_it((const char *) ptr + offset_n + 0x14, 26)); + } +#endif + } + + offset = (uint32_t *)(ptr + 0xC0 + LittleShort(pifh->ordnum)); + + for (n = 0, l = LittleShort(pifh->insnum); n < l; n++, offset++) + { + uint32_t offset_n = LittleLong( *offset ); + if ( offset_n >= msgoffset && offset_n < msgend ) msgend = offset_n; + if ((!offset_n) || (offset_n + 0x20 + 26 >= size)) continue; +#if 0 + if (*(ptr + offset_n + 0x20)) + { + name = "inst"; + if (n < 10) name << '0'; + name << ('0' + n); + //info.meta_add(name, string_utf8_from_it((const char *) ptr + offset_n + 0x20, 26)); + } +#endif + } + } + + unsigned pos = 0xC0 + LittleShort(pifh->ordnum) + LittleShort(pifh->insnum) * 4 + LittleShort(pifh->smpnum) * 4 + LittleShort(pifh->patnum) * 4; + + if (pos < size) + { + uint16_t val16 = LittleShort( *(uint16_t *)(ptr + pos) ); + pos += 2; + if (pos + val16 * 8 < size) pos += val16 * 8; + } + + if (LittleShort(pifh->flags) & 0x80) + { + if (pos + sizeof(MODMIDICFG) < size) + { + pos += sizeof(MODMIDICFG); + } + } + + if ((pos + 8 < size) && (*(uint32_t *)(ptr + pos) == MAKE_ID('P','N','A','M'))) + { + unsigned len = LittleLong(*(uint32_t *)(ptr + pos + 4)); + pos += 8; + if ((pos + len <= size) && (len <= 240 * 32) && (len >= 32)) + { + if (meta) + { + l = len / 32; + for (n = 0; n < l; n++) + { +#if 0 + if (*(ptr + pos + n * 32)) + { + name = "patt"; + if (n < 10) name << '0'; + name << ('0' + n); + //info.meta_add(name, string_utf8_from_it((const char *) ptr + pos + n * 32, 32)); + } +#endif + } + } + pos += len; + } + } + + if ((pos + 8 < size) && (*(uint32_t *)(ptr + pos) == MAKE_ID('C','N','A','M'))) + { + unsigned len = LittleLong(*(uint32_t *)(ptr + pos + 4)); + pos += 8; + if ((pos + len <= size) && (len <= 64 * 20) && (len >= 20)) + { + l = len / 20; + m_nChannels = l; + if (meta) + { + for (n = 0; n < l; n++) + { +#if 0 + if (*(ptr + pos + n * 20)) + { + name = "chan"; + if (n < 10) name << '0'; + name << ('0' + n); + //info.meta_add(name, string_utf8_from_it((const char *) ptr + pos + n * 20, 20)); + } +#endif + } + } + pos += len; + } + } + + offset = (uint32_t *)(ptr + 0xC0 + LittleShort(pifh->ordnum) + LittleShort(pifh->insnum) * 4 + LittleShort(pifh->smpnum) * 4); + + uint8_t chnmask[64]; + + for (n = 0, l = LittleShort(pifh->patnum); n < l; n++) + { + memset(chnmask, 0, sizeof(chnmask)); + uint32_t offset_n = LittleLong( offset[n] ); + if ((!offset_n) || (offset_n + 4 >= size)) continue; + unsigned len = LittleShort(*(uint16_t *)(ptr + offset_n)); + unsigned rows = LittleShort(*(uint16_t *)(ptr + offset_n + 2)); + if ((rows < 4) || (rows > 256)) continue; + if (offset_n + 8 + len > size) continue; + unsigned i = 0; + const uint8_t * p = ptr + offset_n + 8; + unsigned nrow = 0; + while (nrow < rows) + { + if (i >= len) break; + uint8_t b = p[i++]; + if (!b) + { + nrow++; + continue; + } + unsigned ch = b & 0x7F; + if (ch) ch = (ch - 1) & 0x3F; + if (b & 0x80) + { + if (i >= len) break; + chnmask[ch] = p[i++]; + } + // Channel used + if (chnmask[ch] & 0x0F) + { + if ((ch >= m_nChannels) && (ch < 64)) m_nChannels = ch+1; + } + // Note + if (chnmask[ch] & 1) i++; + // Instrument + if (chnmask[ch] & 2) i++; + // Volume + if (chnmask[ch] & 4) i++; + // Effect + if (chnmask[ch] & 8) i += 2; + if (i >= len) break; + } + } + + if ( meta && ( LittleShort(pifh->special) & 1 ) && ( msgend - msgoffset ) && ( msgend < size ) ) + { + const char * str = (const char *) ptr + msgoffset; + std::string msg(str); + //info.meta_add( field_comment, string_utf8_from_it_multiline( msg ) ); + } + + info->NumChannels = m_nChannels; + info->NumPatterns = LittleShort(pifh->patnum); + info->NumOrders = LittleShort(pifh->ordnum); + + return true; +} + +//========================================================================== +// +// DUMB memory file system +// +//========================================================================== + +typedef struct tdumbfile_mem_status +{ + const uint8_t *ptr; + unsigned int offset, size; +} dumbfile_mem_status; + +static int DUMBCALLBACK dumbfile_mem_skip(void *f, long n) +{ + dumbfile_mem_status * s = (dumbfile_mem_status *) f; + s->offset += n; + if (s->offset > s->size) + { + s->offset = s->size; + return 1; + } + return 0; +} + +static int DUMBCALLBACK dumbfile_mem_getc(void *f) +{ + dumbfile_mem_status * s = (dumbfile_mem_status *) f; + if (s->offset < s->size) + { + return *(s->ptr + s->offset++); + } + return -1; +} + +static int32 DUMBCALLBACK dumbfile_mem_getnc(char *ptr, int32 n, void *f) +{ + dumbfile_mem_status * s = (dumbfile_mem_status *) f; + long max = s->size - s->offset; + if (max > n) max = n; + if (max) + { + memcpy(ptr, s->ptr + s->offset, max); + s->offset += max; + } + return max; +} + +static int DUMBCALLBACK dumbfile_mem_seek(void *f, long n) +{ + dumbfile_mem_status * s = (dumbfile_mem_status *) f; + s->offset = n; + if (s->offset > s->size) + { + s->offset = s->size; + return 1; + } + return 0; +} + +static long DUMBCALLBACK dumbfile_mem_get_size(void *f) +{ + dumbfile_mem_status * s = (dumbfile_mem_status *) f; + return s->size; +} + +static DUMBFILE_SYSTEM mem_dfs = { + NULL, // open + &dumbfile_mem_skip, + &dumbfile_mem_getc, + &dumbfile_mem_getnc, + NULL, // close + &dumbfile_mem_seek, + &dumbfile_mem_get_size +}; + +//========================================================================== +// +// dumb_read_allfile +// +//========================================================================== + +DUMBFILE *dumb_read_allfile(dumbfile_mem_status *filestate, uint8_t *start, MusicIO::FileInterface *reader, int lenhave, int lenfull) +{ + filestate->size = lenfull; + filestate->offset = 0; + if (lenhave >= lenfull) + filestate->ptr = (uint8_t *)start; + else + { + uint8_t *mem = new uint8_t[lenfull]; + memcpy(mem, start, lenhave); + if (reader->read(mem + lenhave, lenfull - lenhave) != (lenfull - lenhave)) + { + delete[] mem; + return NULL; + } + filestate->ptr = mem; + } + return dumbfile_open_ex(filestate, &mem_dfs); +} + +//========================================================================== +// +// MOD_SetAutoChip +// +// Disables interpolation for short samples that meet criteria set by +// the cvars referenced at the top of this function. +// +//========================================================================== + +static void MOD_SetAutoChip(DUH *duh) +{ + int size_force = dumbConfig.mod_autochip_size_force; + int size_scan = dumbConfig.mod_autochip_size_scan; + int scan_threshold_8 = ((dumbConfig.mod_autochip_scan_threshold * 0x100) + 50) / 100; + int scan_threshold_16 = ((dumbConfig.mod_autochip_scan_threshold * 0x10000) + 50) / 100; + DUMB_IT_SIGDATA * itsd = duh_get_it_sigdata(duh); + + if (itsd) + { + for (int i = 0, j = itsd->n_samples; i < j; i++) + { + IT_SAMPLE * sample = &itsd->sample[i]; + if (sample->flags & IT_SAMPLE_EXISTS) + { + int channels = sample->flags & IT_SAMPLE_STEREO ? 2 : 1; + if (sample->length < size_force) sample->max_resampling_quality = 0; + else if (sample->length < size_scan) + { + if ((sample->flags & (IT_SAMPLE_LOOP|IT_SAMPLE_PINGPONG_LOOP)) == IT_SAMPLE_LOOP) + { + int loop_start = sample->loop_start * channels; + int loop_end = sample->loop_end * channels; + int s1, s2; + if (sample->flags & IT_SAMPLE_16BIT) + { + s1 = ((signed short *)sample->data)[loop_start]; + s2 = ((signed short *)sample->data)[loop_end - channels]; + if (abs(s1 - s2) > scan_threshold_16) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + s1 = ((signed short *)sample->data)[loop_start + 1]; + s2 = ((signed short *)sample->data)[loop_end - 1]; + if (abs(s1 - s2) > scan_threshold_16) + { + sample->max_resampling_quality = 0; + continue; + } + } + } + else + { + s1 = ((signed char *)sample->data)[loop_start]; + s2 = ((signed char *)sample->data)[loop_end - channels]; + if (abs(s1 - s2) > scan_threshold_8) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + s1 = ((signed char *)sample->data)[loop_start + 1]; + s2 = ((signed char *)sample->data)[loop_end - 1]; + if (abs(s1 - s2) > scan_threshold_8) + { + sample->max_resampling_quality = 0; + continue; + } + } + } + } + if ((sample->flags & (IT_SAMPLE_SUS_LOOP|IT_SAMPLE_PINGPONG_SUS_LOOP)) == IT_SAMPLE_SUS_LOOP) + { + int sus_loop_start = sample->sus_loop_start * channels; + int sus_loop_end = sample->sus_loop_end * channels; + int s1, s2; + if (sample->flags & IT_SAMPLE_16BIT) + { + s1 = ((signed short *)sample->data)[sus_loop_start]; + s2 = ((signed short *)sample->data)[sus_loop_end - channels]; + if (abs(s1 - s2) > scan_threshold_16) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + s1 = ((signed short *)sample->data)[sus_loop_start + 1]; + s2 = ((signed short *)sample->data)[sus_loop_end - 1]; + if (abs(s1 - s2) > scan_threshold_16) + { + sample->max_resampling_quality = 0; + continue; + } + } + } + else + { + s1 = ((signed char *)sample->data)[sus_loop_start]; + s2 = ((signed char *)sample->data)[sus_loop_end - channels]; + if (abs(s1 - s2) > scan_threshold_8) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + s1 = ((signed char *)sample->data)[sus_loop_start + 1]; + s2 = ((signed char *)sample->data)[sus_loop_end - 1]; + if (abs(s1 - s2) > scan_threshold_8) + { + sample->max_resampling_quality = 0; + continue; + } + } + } + } + + int k, l = sample->length * channels; + if (sample->flags & IT_SAMPLE_LOOP) l = sample->loop_end * channels; + if (sample->flags & IT_SAMPLE_16BIT) + { + for (k = channels; k < l; k += channels) + { + if (abs(((signed short *)sample->data)[k - channels] - ((signed short *)sample->data)[k]) > scan_threshold_16) + { + break; + } + } + if (k < l) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + for (k = 2 + 1; k < l; k += 2) + { + if (abs(((signed short *)sample->data)[k - 2] - ((signed short *)sample->data)[k]) > scan_threshold_16) + { + break; + } + } + } + if (k < l) + { + sample->max_resampling_quality = 0; + continue; + } + } + else + { + for (k = channels; k < l; k += channels) + { + if (abs(((signed char *)sample->data)[k - channels] - ((signed char *)sample->data)[k]) > scan_threshold_8) + { + break; + } + } + if (k < l) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + for (k = 2 + 1; k < l; k += 2) + { + if (abs(((signed char *)sample->data)[k - 2] - ((signed char *)sample->data)[k]) > scan_threshold_8) + { + break; + } + } + } + if (k < l) + { + sample->max_resampling_quality = 0; + continue; + } + } + } + } + } + } +} + +//========================================================================== +// +// MOD_OpenSong +// +//========================================================================== + +StreamSource* MOD_OpenSong(MusicIO::FileInterface *reader, int samplerate) +{ + DUH *duh = 0; + int headsize; + union + { + uint8_t start[64]; + uint32_t dstart[64/4]; + }; + dumbfile_mem_status filestate; + DUMBFILE *f = NULL; + DumbSong *state = NULL; + + bool is_it = false; + bool is_dos = true; + + auto fpos = reader->tell(); + int size = (int)reader->filelength(); + + filestate.ptr = start; + filestate.offset = 0; + headsize = MIN((int)sizeof(start), size); + + if (headsize != reader->read(start, headsize)) + { + return NULL; + } + + if (size >= 4 && dstart[0] == MAKE_ID('I','M','P','M')) + { + is_it = true; + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_it_quick(f); + } + } + else if (size >= 17 && !memcmp(start, "Extended Module: ", 17)) + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_xm_quick(f); + } + } + else if (size >= 0x30 && dstart[11] == MAKE_ID('S','C','R','M')) + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_s3m_quick(f); + } + } + else if (size >= 1168 && + /*start[28] == 0x1A &&*/ start[29] == 2 && + ( !memcmp( &start[20], "!Scream!", 8 ) || + !memcmp( &start[20], "BMOD2STM", 8 ) || + !memcmp( &start[20], "WUZAMOD!", 8 ) ) ) + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_stm_quick(f); + } + } + else if (size >= 2 && + ((start[0] == 0x69 && start[1] == 0x66) || + (start[0] == 0x4A && start[1] == 0x4E))) + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_669_quick(f); + } + } + else if (size >= 0x30 && dstart[11] == MAKE_ID('P','T','M','F')) + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_ptm_quick(f); + } + } + else if (size >= 4 && dstart[0] == MAKE_ID('P','S','M',' ')) + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_psm_quick(f, 0/*start_order*/); + /*start_order = 0;*/ + } + } + else if (size >= 4 && dstart[0] == (uint32_t)MAKE_ID('P','S','M',254)) + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_old_psm_quick(f); + } + } + else if (size >= 3 && start[0] == 'M' && start[1] == 'T' && start[2] == 'M') + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_mtm_quick(f); + } + } + else if (size >= 12 && dstart[0] == MAKE_ID('R','I','F','F') && + (dstart[2] == MAKE_ID('D','S','M','F') || + dstart[2] == MAKE_ID('A','M',' ',' ') || + dstart[2] == MAKE_ID('A','M','F','F'))) + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_riff_quick(f); + } + } + else if (size >= 32 && + !memcmp( start, "ASYLUM Music Format", 19 ) && + !memcmp( start + 19, " V1.0", 5 ) ) + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_asy_quick(f); + } + } + else if (size >= 8 && + dstart[0] == MAKE_ID('O','K','T','A') && + dstart[1] == MAKE_ID('S','O','N','G')) + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_okt_quick(f); + } + } + else if (size >= 4 && + (dstart[0] == MAKE_ID('A','M','F','\xa') || + dstart[0] == MAKE_ID('A','M','F','\xb') || + dstart[0] == MAKE_ID('A','M','F','\xc') || + dstart[0] == MAKE_ID('A','M','F','\xd') || + dstart[0] == MAKE_ID('A','M','F','\xe'))) + { + if ((f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + duh = dumb_read_amf_quick(f); + } + } + + if ( ! duh ) + { + is_dos = false; + if (filestate.ptr == (uint8_t *)start) + { + if (!(f = dumb_read_allfile(&filestate, start, reader, headsize, size))) + { + reader->seek(fpos, SEEK_SET); + return NULL; + } + } + else + { + filestate.offset = 0; + } + // No way to get the filename, so we can't check for a .mod extension, and + // therefore, trying to load an old 15-instrument SoundTracker module is not + // safe. We'll restrict MOD loading to 31-instrument modules with known + // signatures and let the sound system worry about 15-instrument ones. + // (Assuming it even supports them) + duh = dumb_read_mod_quick(f, true); + } + + if (f != NULL) + { + dumbfile_close(f); + } + if ( duh ) + { + if (dumbConfig.mod_autochip) + { + MOD_SetAutoChip(duh); + } + state = new DumbSong(duh, samplerate); + + if (is_it) ReadIT(filestate.ptr, size, state, false); + else ReadDUH(duh, state, false, is_dos); + } + else + { + // Reposition file pointer for other codecs to do their checks. + reader->seek(fpos, SEEK_SET); + } + if (filestate.ptr != (uint8_t *)start) + { + delete[] const_cast(filestate.ptr); + } + return state; +} + +//========================================================================== +// +// DumbSong :: read static +// +//========================================================================== + +bool DumbSong::GetData(void *buffer, size_t sizebytes) +{ + if (eof) + { + memset(buffer, 0, sizebytes); + return false; + } + + while (sizebytes > 0) + { + int written = decode_run(buffer, (unsigned)sizebytes / 8); + if (written < 0) + { + return false; + } + if (written == 0) + { + memset(buffer, 0, sizebytes); + return true; + } + else + { + // Convert to float + for (int i = 0; i < written * 2; ++i) + { + ((float *)buffer)[i] = (((int *)buffer)[i] / (float)(1 << 24)) * MasterVolume; + } + } + buffer = (uint8_t *)buffer + written * 8; + sizebytes -= written * 8; + } + return true; +} + +//========================================================================== +// +// ChangeSetting +// +//========================================================================== + +void DumbSong::ChangeSettingNum(const char* setting, double val) +{ + if (!stricmp(setting, "dumb.mastervolume")) + MasterVolume = (float)val; +} + +//========================================================================== +// +// DumbSong constructor +// +//========================================================================== + +DumbSong::DumbSong(DUH* myduh, int samplerate) +{ + duh = myduh; + sr = NULL; + eof = false; + interp = dumbConfig.mod_interp; + volramp = dumbConfig.mod_volramp; + written = 0; + length = 0; + start_order = 0; + MasterVolume = (float)dumbConfig.mod_dumb_mastervolume * 4; + if (dumbConfig.mod_samplerate != 0) + { + srate = dumbConfig.mod_samplerate; + } + else + { + srate = samplerate; + } + delta = 65536.0 / srate; +} + +//========================================================================== +// +// DumbSong destructor +// +//========================================================================== + +DumbSong::~DumbSong() +{ + if (sr) duh_end_sigrenderer(sr); + if (duh) unload_duh(duh); +} + +//========================================================================== +// +// DumbSong GetFormatEx +// +//========================================================================== + +SoundStreamInfoEx DumbSong::GetFormatEx() +{ + return { 32*1024, srate, SampleType_Float32, ChannelConfig_Stereo }; +} + +//========================================================================== +// +// DumbSong :: Play +// +//========================================================================== + +bool DumbSong::Start() +{ + started = open2(0); + return started; +} + +//========================================================================== +// +// DumbSong :: SetSubsong +// +//========================================================================== + +bool DumbSong::SetSubsong(int order) +{ + if (order == start_order) + { + return true; + } + if (!started) + { + start_order = order; + return true; + } + DUH_SIGRENDERER *oldsr = sr; + sr = NULL; + start_order = order; + if (!open2(0)) + { + sr = oldsr; + return false; + } + duh_end_sigrenderer(oldsr); + return true; +} + +//========================================================================== +// +// DumbSong :: open2 +// +//========================================================================== + +bool DumbSong::open2(long pos) +{ + if (start_order != 0) + { + sr = dumb_it_start_at_order(duh, 2, start_order); + if (sr && pos) duh_sigrenderer_generate_samples(sr, 0, 1, pos, 0); + } + else + { + sr = duh_start_sigrenderer(duh, 0, 2, pos); + } + if (!sr) + { + return false; + } + + DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr); + dumb_it_set_resampling_quality(itsr, interp); + dumb_it_set_ramp_style(itsr, volramp); + if (!m_Looping) + { + dumb_it_set_loop_callback(itsr, &dumb_it_callback_terminate, NULL); + } + dumb_it_set_xm_speed_zero_callback(itsr, &dumb_it_callback_terminate, NULL); + dumb_it_set_global_volume_zero_callback(itsr, &dumb_it_callback_terminate, NULL); + return true; +} + +//========================================================================== +// +// DumbSong :: render +// +//========================================================================== + +long DumbSong::render(double volume, double delta, long samples, sample_t **buffer) +{ + long written = duh_sigrenderer_generate_samples(sr, volume, delta, samples, buffer); + + if (written < samples) + { + if (!m_Looping) + { + eof = true; + } + else + { + duh_end_sigrenderer(sr); + sr = NULL; + if (!open2(0)) + { + eof = true; + } + } + } + return written; +} + +//========================================================================== +// +// DumbSong :: decode_run +// +// Given a buffer of 32-bit PCM stereo pairs and a size specified in +// samples, returns the number of samples written to the buffer. +// +//========================================================================== + +int DumbSong::decode_run(void *buffer, unsigned int size) +{ + if (eof) return 0; + + DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr); + if (itsr == nullptr) return 0; + int dt = int(delta * 65536.0 + 0.5); + long samples = long((((LONG_LONG)itsr->time_left << 16) | itsr->sub_time_left) / dt); + if (samples == 0 || samples > (long)size) samples = size; + sample_t **buf = (sample_t **)&buffer; + int written = 0; + +retry: + dumb_silence(buf[0], size * 2); + written = render(1, delta, samples, buf); + + if (eof) return false; + else if (written == 0) goto retry; + else if (written == -1) return -1; + + return written; +} + +//========================================================================== +// +// DumbSong :: GetStats +// +//========================================================================== + +std::string DumbSong::GetStats() +{ + //return StreamSong::GetStats(); + DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(sr); + DUMB_IT_SIGDATA *itsd = duh_get_it_sigdata(duh); + + int channels = 0; + for (int i = 0; i < DUMB_IT_N_CHANNELS; i++) + { + IT_PLAYING * playing = itsr->channel[i].playing; + if (playing && !(playing->flags & IT_PLAYING_DEAD)) channels++; + } + for (int i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + { + if (itsr->playing[i]) channels++; + } + + if (itsr == NULL || itsd == NULL) + { + return "Problem getting stats"; + } + else + { + char out[120]; + snprintf(out, 120, "%s, Order:%3d/%d Patt:%2d/%d Row:%2d/%2d Chan:%2d/%2d Speed:%2d Tempo:%3d", + Codec.c_str(), + itsr->order, NumOrders, + (itsd->order && itsr->order < itsd->n_orders ? itsd->order[itsr->order] : 0), NumPatterns, + itsr->row, itsr->n_rows, + channels, NumChannels, + itsr->speed, + itsr->tempo + ); + return out; + } +} + diff --git a/source/streamsources/music_gme.cpp b/source/streamsources/music_gme.cpp new file mode 100644 index 000000000..5f59a8849 --- /dev/null +++ b/source/streamsources/music_gme.cpp @@ -0,0 +1,374 @@ +/* +** music_gme.cpp +** General game music player, using Game Music Emu for decoding. +** +**--------------------------------------------------------------------------- +** Copyright 2009 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +// Uncomment if you are using the DLL version of GME. +//#define GME_DLL + +#include +#include +#include + +#include "streamsource.h" +#include +#include "fileio.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +class GMESong : public StreamSource +{ +public: + GMESong(Music_Emu *emu, int sample_rate); + ~GMESong(); + bool SetSubsong(int subsong) override; + bool Start() override; + void ChangeSettingNum(const char *name, double val) override; + std::string GetStats() override; + bool GetData(void *buffer, size_t len) override; + SoundStreamInfoEx GetFormatEx() override; + +protected: + Music_Emu *Emu; + gme_info_t *TrackInfo; + int SampleRate; + int CurrTrack; + bool started = false; + + bool StartTrack(int track, bool getcritsec=true); + bool GetTrackInfo(); + int CalcSongLength(); +}; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// Currently not used. + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// GME_CheckFormat +// +//========================================================================== + +const char *GME_CheckFormat(uint32_t id) +{ + return gme_identify_header(&id); +} + +//========================================================================== +// +// GME_OpenSong +// +//========================================================================== + +StreamSource *GME_OpenSong(MusicIO::FileInterface *reader, const char *fmt, int sample_rate) +{ + gme_type_t type; + gme_err_t err; + uint8_t *song; + Music_Emu *emu; + + type = gme_identify_extension(fmt); + if (type == NULL) + { + return NULL; + } + emu = gme_new_emu(type, sample_rate); + if (emu == nullptr) + { + return nullptr; + } + + auto fpos = reader->tell(); + auto len = reader->filelength(); + + song = new uint8_t[len]; + if (reader->read(song, len) != len) + { + delete[] song; + gme_delete(emu); + reader->seek(fpos, SEEK_SET); + return nullptr; + } + + err = gme_load_data(emu, song, (long)len); + delete[] song; + + if (err != nullptr) + { + gme_delete(emu); + throw std::runtime_error(err); + } + gme_set_stereo_depth(emu, std::min(std::max(miscConfig.gme_stereodepth, 0.f), 1.f)); + gme_set_fade(emu, -1); // Enable infinite loop + +#if GME_VERSION >= 0x602 + gme_set_autoload_playback_limit(emu, 0); +#endif // GME_VERSION >= 0x602 + + return new GMESong(emu, sample_rate); +} + +//========================================================================== +// +// GMESong - Constructor +// +//========================================================================== + +GMESong::GMESong(Music_Emu *emu, int sample_rate) +{ + Emu = emu; + SampleRate = sample_rate; + CurrTrack = 0; + TrackInfo = NULL; +} + + +SoundStreamInfoEx GMESong::GetFormatEx() +{ + return { 32*1024, SampleRate, SampleType_Int16, ChannelConfig_Stereo }; +} + +//========================================================================== +// +// GMESong - Destructor +// +//========================================================================== + +GMESong::~GMESong() +{ + if (TrackInfo != NULL) + { + gme_free_info(TrackInfo); + } + if (Emu != NULL) + { + gme_delete(Emu); + } +} + + +//========================================================================== +// +// GMESong :: GMEDepthChanged +// +//========================================================================== + +void GMESong::ChangeSettingNum(const char *name, double val) +{ + if (Emu != nullptr && !stricmp(name, "gme.stereodepth")) + { + gme_set_stereo_depth(Emu, std::min(std::max(0., val), 1.)); + } +} + +//========================================================================== +// +// GMESong :: Play +// +//========================================================================== + +bool GMESong::Start() +{ + return StartTrack(CurrTrack); +} + + +//========================================================================== +// +// GMESong :: SetSubsong +// +//========================================================================== + +bool GMESong::SetSubsong(int track) +{ + if (CurrTrack == track) + { + return true; + } + if (!started) + { + CurrTrack = track; + return true; + } + return StartTrack(track); +} + +//========================================================================== +// +// GMESong :: StartTrack +// +//========================================================================== + +bool GMESong::StartTrack(int track, bool getcritsec) +{ + gme_err_t err; + + if (getcritsec) + { + err = gme_start_track(Emu, track); + } + else + { + err = gme_start_track(Emu, track); + } + if (err != NULL) + { + // This is called in the data reader thread which may not interact with the UI. + // TBD: How to get the message across? An exception may not be used here! + // Printf("Could not start track %d: %s\n", track, err); + return false; + } + CurrTrack = track; + started = true; + GetTrackInfo(); + if (!m_Looping) + { + gme_set_fade(Emu, CalcSongLength()); + } + return true; +} + +//========================================================================== +// +// GMESong :: GetStats +// +//========================================================================== + +std::string GMESong::GetStats() +{ + char out[80]; + + if (TrackInfo != NULL) + { + int time = gme_tell(Emu); + snprintf(out, 80, + "Track: %d Time: %3d:%02d:%03d System: %s", + CurrTrack, + time/60000, + (time/1000) % 60, + time % 1000, + TrackInfo->system); + } + return out; +} + +//========================================================================== +// +// GMESong :: GetTrackInfo +// +//========================================================================== + +bool GMESong::GetTrackInfo() +{ + gme_err_t err; + + if (TrackInfo != NULL) + { + gme_free_info(TrackInfo); + TrackInfo = NULL; + } + err = gme_track_info(Emu, &TrackInfo, CurrTrack); + if (err != NULL) + { + // This is called in the data reader thread which may not interact with the UI. + // TBD: How to get the message across? An exception may not be used here! + //Printf("Could not get track %d info: %s\n", CurrTrack, err); + return false; + } + return true; +} + +//========================================================================== +// +// GMESong :: CalcSongLength +// +//========================================================================== + +int GMESong::CalcSongLength() +{ + if (TrackInfo == NULL) + { + return 150000; + } + if (TrackInfo->length > 0) + { + return TrackInfo->length; + } + if (TrackInfo->loop_length > 0) + { + return TrackInfo->intro_length + TrackInfo->loop_length * 2; + } + return 150000; +} + +//========================================================================== +// +// GMESong :: Read STATIC +// +//========================================================================== + +bool GMESong::GetData(void *buffer, size_t len) +{ + gme_err_t err; + + if (gme_track_ended(Emu)) + { + if (m_Looping) + { + StartTrack(CurrTrack, false); + } + else + { + memset(buffer, 0, len); + return false; + } + } + err = gme_play(Emu, int(len >> 1), (short *)buffer); + return (err == NULL); +} diff --git a/source/streamsources/music_libsndfile.cpp b/source/streamsources/music_libsndfile.cpp new file mode 100644 index 000000000..e3aaed53d --- /dev/null +++ b/source/streamsources/music_libsndfile.cpp @@ -0,0 +1,568 @@ +/* +** music_libsndfile.cpp +** Uses libsndfile for streaming music formats +** +**--------------------------------------------------------------------------- +** Copyright 2017 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include "zmusic_internal.h" +#include "streamsource.h" +#include "zmusic/sounddecoder.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +class SndFileSong : public StreamSource +{ +public: + SndFileSong(SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass); + ~SndFileSong(); + std::string GetStats() override; + SoundStreamInfoEx GetFormatEx() override; + bool GetData(void *buffer, size_t len) override; + +protected: + SoundDecoder *Decoder; + unsigned int FrameSize; + + uint32_t Loop_Start; + uint32_t Loop_End; + + int CalcSongLength(); +}; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// S_ParseTimeTag +// +// Passed the value of a loop point tag, converts it to numbers. +// +// This may be of the form 00:00:00.00 (HH:MM:SS.ss) to specify by play +// time. Various parts may be left off. The only requirement is that it +// contain a colon. e.g. To start the loop at 20 seconds in, you can use +// ":20", "0:20", "00:00:20", ":20.0", etc. Values after the decimal are +// fractions of a second. +// +// If you don't include a colon but just have a raw number, then it's +// the number of PCM samples at which to loop. +// +// Returns true if the tag made sense, false if not. +// +//========================================================================== + +bool S_ParseTimeTag(const char* tag, zmusic_bool* as_samples, unsigned int* time) +{ + const int time_count = 3; + const char* bit = tag; + char ms[3] = { 0 }; + unsigned int times[time_count] = { 0 }; + int ms_pos = 0, time_pos = 0; + bool pcm = true, in_ms = false; + + for (bit = tag; *bit != '\0'; ++bit) + { + if (*bit >= '0' && *bit <= '9') + { + if (in_ms) + { + // Ignore anything past three fractional digits. + if (ms_pos < 3) + { + ms[ms_pos++] = *bit - '0'; + } + } + else + { + times[time_pos] = times[time_pos] * 10 + *bit - '0'; + } + } + else if (*bit == ':') + { + if (in_ms) + { // If we already specified milliseconds, we can't take any more parts. + return false; + } + pcm = false; + if (++time_pos == time_count) + { // Time too long. (Seriously, starting the loop days in?) + return false; + } + } + else if (*bit == '.') + { + if (pcm || in_ms) + { // It doesn't make sense to have fractional PCM values. + // It also doesn't make sense to have more than one dot. + return false; + } + in_ms = true; + } + else + { // Anything else: We don't understand this. + return false; + } + } + if (pcm) + { + *as_samples = true; + *time = times[0]; + } + else + { + unsigned int mytime = 0; + + // Add in hours, minutes, and seconds + for (int i = 0; i <= time_pos; ++i) + { + mytime = mytime * 60 + times[i]; + } + + // Add in milliseconds + mytime = mytime * 1000 + ms[0] * 100 + ms[1] * 10 + ms[2]; + + *as_samples = false; + *time = mytime; + } + return true; +} + +//========================================================================== +// +// Try to find the LOOP_START/LOOP_END tags in a Vorbis Comment block +// +// We have to parse through the FLAC or Ogg headers manually, since sndfile +// doesn't provide proper access to the comments and we'd rather not require +// using libFLAC and libvorbisfile directly. +// +//========================================================================== + +static void ParseVorbisComments(MusicIO::FileInterface *fr, uint32_t *start, zmusic_bool *startass, uint32_t *end, zmusic_bool *endass) +{ + uint8_t vc_data[4]; + + // The VC block starts with a 32LE integer for the vendor string length, + // followed by the vendor string + if(fr->read(vc_data, 4) != 4) + return; + uint32_t vndr_len = vc_data[0] | (vc_data[1]<<8) | (vc_data[2]<<16) | (vc_data[3]<<24); + + // Skip vendor string + if(fr->seek(vndr_len, SEEK_CUR) == -1) + return; + + // Following the vendor string is a 32LE integer for the number of + // comments, followed by each comment. + if(fr->read(vc_data, 4) != 4) + return; + size_t count = vc_data[0] | (vc_data[1]<<8) | (vc_data[2]<<16) | (vc_data[3]<<24); + + zmusic_bool loopass = false; + uint32_t looplen = 0; + bool endfound = false; + + for(size_t i = 0; i < count; i++) + { + // Each comment is a 32LE integer for the comment length, followed by + // the comment text (not null terminated!) + if(fr->read(vc_data, 4) != 4) + return; + uint32_t length = vc_data[0] | (vc_data[1]<<8) | (vc_data[2]<<16) | (vc_data[3]<<24); + + if(length >= 128) + { + // If the comment is "big", skip it + if(fr->seek(length, SEEK_CUR) == -1) + return; + continue; + } + + char strdat[128]; + if(fr->read(strdat, length) != (long)length) + return; + strdat[length] = 0; + + static const char* loopStartTags[] = { "LOOP_START=", "LOOPSTART=", "LOOP=" }; + static const char* loopEndTags[] = { "LOOP_END=", "LOOPEND=" }; + static const char* loopLengthTags[] = { "LOOP_LENGTH=", "LOOPLENGTH=" }; + + for (auto tag : loopStartTags) + { + const size_t tagLength = strlen(tag); + + if (!strnicmp(strdat, tag, tagLength)) + { + S_ParseTimeTag(strdat + tagLength, startass, start); + break; + } + } + for (auto tag : loopEndTags) + { + const size_t tagLength = strlen(tag); + + if (!strnicmp(strdat, tag, tagLength)) + { + S_ParseTimeTag(strdat + tagLength, endass, end); + endfound = true; + break; + } + } + for (auto tag : loopLengthTags) + { + const size_t tagLength = strlen(tag); + + if (!strnicmp(strdat, tag, tagLength)) + { + S_ParseTimeTag(strdat + tagLength, &loopass, &looplen); + *end += *start; + break; + } + } + } + // Use loop length only if no end defined. + if (!endfound && looplen && loopass == *startass) + { + *endass = loopass; + *end = *start + looplen; + } +} + +static void FindFlacComments(MusicIO::FileInterface *fr, uint32_t *loop_start, zmusic_bool *startass, uint32_t *loop_end, zmusic_bool *endass) +{ + // Already verified the fLaC marker, so we're 4 bytes into the file + bool lastblock = false; + uint8_t header[4]; + + while(!lastblock && fr->read(header, 4) == 4) + { + // The first byte of the block header contains the type and a flag + // indicating the last metadata block + char blocktype = header[0]&0x7f; + lastblock = !!(header[0]&0x80); + // Following the type is a 24BE integer for the size of the block + uint32_t blocksize = (header[1]<<16) | (header[2]<<8) | header[3]; + + // FLAC__METADATA_TYPE_VORBIS_COMMENT is 4 + if(blocktype == 4) + { + ParseVorbisComments(fr, loop_start, startass, loop_end, endass); + return; + } + + if(fr->seek(blocksize, SEEK_CUR) == -1) + break; + } +} + +static void FindOggComments(MusicIO::FileInterface *fr, uint32_t *loop_start, zmusic_bool *startass, uint32_t *loop_end, zmusic_bool *endass) +{ + uint8_t ogghead[27]; + + // We already read and verified the OggS marker, so skip the first 4 bytes + // of the Ogg page header. + while(fr->read(ogghead+4, 23) == 23) + { + // The 19th byte of the Ogg header is a 32LE integer for the page + // number, and the 27th is a uint8 for the number of segments in the + // page. + uint32_t ogg_pagenum = ogghead[18] | (ogghead[19]<<8) | (ogghead[20]<<16) | + (ogghead[21]<<24); + uint8_t ogg_segments = ogghead[26]; + + // Following the Ogg page header is a series of uint8s for the length of + // each segment in the page. The page segment data follows contiguously + // after. + uint8_t segsizes[256]; + if(fr->read(segsizes, ogg_segments) != ogg_segments) + break; + + // Find the segment with the Vorbis Comment packet (type 3) or Opus tags. + bool vorbis_comments = false; + for(int i = 0; i < ogg_segments; ++i) + { + uint8_t segsize = segsizes[i]; + + if(segsize > 16) + { + uint8_t vorbhead[8]; + if(fr->read(vorbhead, 8) != 8) + return; + + if(vorbhead[0] == 3 && memcmp(vorbhead + 1, "vorbis", 6) == 0) + { + // Seek back because the vorbis tag is only 7 bytes long. + if(fr->seek(-1, SEEK_CUR) == -1) + return; + segsize++; + + vorbis_comments = true; + } + else if(memcmp(vorbhead, "OpusTags", 8) == 0) + vorbis_comments = true; + + if(vorbis_comments) + { + // If the packet is 'laced', it spans multiple segments (a + // segment size of 255 indicates the next segment continues + // the packet, ending with a size less than 255). Vorbis + // packets always start and end on segment boundaries. A + // packet that's an exact multiple of 255 ends with a + // segment of 0 size. + while(segsize == 255 && ++i < ogg_segments) + segsize = segsizes[i]; + + // TODO: A Vorbis packet can theoretically span multiple + // Ogg pages (e.g. start in the last segment of one page + // and end in the first segment of a following page). That + // will require extra logic to decode as the VC block will + // be broken up with non-Vorbis data in-between. For now, + // just handle the common case where it's all in one page. + if(i < ogg_segments) + ParseVorbisComments(fr, loop_start, startass, loop_end, endass); + return; + } + + segsize -= 8; + } + if(fr->seek(segsize, SEEK_CUR) == -1) + return; + } + + // Don't keep looking after the third page + if(ogg_pagenum >= 2) + break; + + if(fr->read(ogghead, 4) != 4 || memcmp(ogghead, "OggS", 4) != 0) + break; + } +} + +void FindLoopTags(MusicIO::FileInterface *fr, uint32_t *start, zmusic_bool *startass, uint32_t *end, zmusic_bool *endass) +{ + uint8_t signature[4]; + + fr->read(signature, 4); + if(memcmp(signature, "fLaC", 4) == 0) + FindFlacComments(fr, start, startass, end, endass); + else if(memcmp(signature, "OggS", 4) == 0) + FindOggComments(fr, start, startass, end, endass); +} + +DLL_EXPORT void FindLoopTags(const uint8_t* data, size_t size, uint32_t* start, zmusic_bool* startass, uint32_t* end, zmusic_bool* endass) +{ + MusicIO::FileInterface* reader = new MusicIO::MemoryReader(data, (long)size); + FindLoopTags(reader, start, startass, end, endass); + reader->close(); +} + +//========================================================================== +// +// SndFile_OpenSong +// +//========================================================================== + +StreamSource *SndFile_OpenSong(MusicIO::FileInterface *fr) +{ + fr->seek(0, SEEK_SET); + + uint32_t loop_start = 0, loop_end = ~0u; + zmusic_bool startass = false, endass = false; + FindLoopTags(fr, &loop_start, &startass, &loop_end, &endass); + + fr->seek(0, SEEK_SET); + auto decoder = SoundDecoder::CreateDecoder(fr); + if (decoder == nullptr) return nullptr; // If this fails the file reader has not been taken over and the caller needs to clean up. This is to allow further analysis of the passed file. + return new SndFileSong(decoder, loop_start, loop_end, startass, endass); +} + +//========================================================================== +// +// SndFileSong - Constructor +// +//========================================================================== + +static int32_t Scale(int32_t a, int32_t b, int32_t c) +{ + return (int32_t)(((int64_t)a * b) / c); +} + +SndFileSong::SndFileSong(SoundDecoder *decoder, uint32_t loop_start, uint32_t loop_end, bool startass, bool endass) +{ + ChannelConfig chanconf; + SampleType stype; + int srate; + + decoder->getInfo(&srate, &chanconf, &stype); + + if (!startass) loop_start = Scale(loop_start, srate, 1000); + if (!endass) loop_end = Scale(loop_end, srate, 1000); + + const uint32_t sampleLength = (uint32_t)decoder->getSampleLength(); + Loop_Start = loop_start; + Loop_End = sampleLength == 0 ? loop_end : std::min(loop_end, sampleLength); + Decoder = decoder; + FrameSize = ZMusic_ChannelCount(chanconf) * ZMusic_SampleTypeSize(stype); +} + +SoundStreamInfoEx SndFileSong::GetFormatEx() +{ + ChannelConfig chanconf; + SampleType stype; + int srate; + + Decoder->getInfo(&srate, &chanconf, &stype); + return { 64/*snd_streambuffersize*/ * 1024, srate, stype, chanconf }; +} + +//========================================================================== +// +// SndFileSong - Destructor +// +//========================================================================== + +SndFileSong::~SndFileSong() +{ + if (Decoder != nullptr) + { + delete Decoder; + } +} + +//========================================================================== +// +// SndFileSong :: GetStats +// +//========================================================================== + +std::string SndFileSong::GetStats() +{ + char out[80]; + + ChannelConfig chanconf; + SampleType stype; + int srate; + Decoder->getInfo(&srate, &chanconf, &stype); + + size_t SamplePos = Decoder->getSampleOffset(); + int time = int (SamplePos / srate); + + snprintf(out, 80, + "Track: %s, %dHz Time: %02d:%02d", + ZMusic_ChannelConfigName(chanconf), srate, + time/60, + time % 60); + return out; +} + +//========================================================================== +// +// SndFileSong :: Read STATIC +// +//========================================================================== + +bool SndFileSong::GetData(void *vbuff, size_t len) +{ + char *buff = (char*)vbuff; + + size_t currentpos = Decoder->getSampleOffset(); + size_t framestoread = len / FrameSize; + bool err = false; + if (!m_Looping) + { + size_t maxpos = Decoder->getSampleLength(); + if (currentpos == maxpos) + { + memset(buff, 0, len); + return false; + } + if (currentpos + framestoread > maxpos) + { + size_t got = Decoder->read(buff, (maxpos - currentpos) * FrameSize); + memset(buff + got, 0, len - got); + } + else + { + size_t got = Decoder->read(buff, len); + err = (got != len); + } + } + else + { + // This looks a bit more complicated than necessary because libmpg123 will not read the full requested length for the last block in the file. + if (currentpos + framestoread > Loop_End) + { + // Loop can be very short, make sure the current position doesn't exceed it + if (currentpos < Loop_End) + { + size_t endblock = (Loop_End - currentpos) * FrameSize; + size_t endlen = Decoder->read(buff, endblock); + + // Even if zero bytes was read give it a chance to start from the beginning + buff += endlen; + len -= endlen; + } + + Decoder->seek(Loop_Start, false, true); + } + while (len > 0) + { + size_t readlen = Decoder->read(buff, len); + if (readlen == 0) + { + return false; + } + buff += readlen; + len -= readlen; + if (len > 0) + { + Decoder->seek(Loop_Start, false, true); + } + } + } + return true; +} diff --git a/source/streamsources/music_libxmp.cpp b/source/streamsources/music_libxmp.cpp new file mode 100644 index 000000000..999e0460c --- /dev/null +++ b/source/streamsources/music_libxmp.cpp @@ -0,0 +1,180 @@ +/* +** music_libxmp.cpp +** libxmp module player. +** +**--------------------------------------------------------------------------- +** Copyright 2024 Cacodemon345 +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include +#include +#include +#include +#include "streamsource.h" + +#define LIBXMP_STATIC 1 +#include "../libxmp/include/xmp.h" +#include "zmusic/m_swap.h" +#include "zmusic/mididefs.h" +#include "zmusic/midiconfig.h" +#include "fileio.h" + +extern DumbConfig dumbConfig; + +static unsigned long xmp_read(void *dest, unsigned long len, unsigned long nmemb, void *priv) +{ + if (len == 0 || nmemb == 0) + return (unsigned long)0; + + MusicIO::FileInterface* interface = (MusicIO::FileInterface*)priv; + + auto origpos = interface->tell(); + auto length = interface->read(dest, (int32_t)(len * nmemb)); + + if (length != len * nmemb) + { + // Let's hope the compiler doesn't misoptimize this. + interface->seek(origpos + (length / len) * len, SEEK_SET); + } + return length / len; +} + +static struct xmp_callbacks callbacks = +{ + xmp_read, + [](void *priv, long offset, int whence) -> int { return ((MusicIO::FileInterface*)priv)->seek(offset, whence); }, + [](void *priv) -> long { return ((MusicIO::FileInterface*)priv)->tell(); }, + [](void *priv) -> int { return 0; } +}; + +class XMPSong : public StreamSource +{ +private: + xmp_context context = nullptr; + int samplerate = 44100; + int subsong = 0; + + // libxmp can't output in float. + std::vector int16_buffer; + +public: + XMPSong(xmp_context ctx, int samplerate); + ~XMPSong(); + bool SetSubsong(int subsong) override; + bool Start() override; + SoundStreamInfoEx GetFormatEx() override; + +protected: + bool GetData(void *buffer, size_t len) override; +}; + +XMPSong::XMPSong(xmp_context ctx, int rate) +{ + context = ctx; + samplerate = (dumbConfig.mod_samplerate != 0) ? dumbConfig.mod_samplerate : rate; + xmp_set_player(context, XMP_PLAYER_VOLUME, 100); + xmp_set_player(context, XMP_PLAYER_INTERP, dumbConfig.mod_interp); + + int16_buffer.reserve(16 * 1024); +} + +XMPSong::~XMPSong() +{ + xmp_end_player(context); + xmp_free_context(context); +} + +SoundStreamInfoEx XMPSong::GetFormatEx() +{ + return { 32 * 1024, samplerate, SampleType_Float32, ChannelConfig_Stereo }; +} + +bool XMPSong::SetSubsong(int subsong) +{ + this->subsong = subsong; + if (xmp_get_player(context, XMP_PLAYER_STATE) >= XMP_STATE_PLAYING) + return xmp_set_position(context, subsong) >= 0; + return true; +} + +bool XMPSong::GetData(void *buffer, size_t len) +{ + if ((len / 4) > int16_buffer.size()) + int16_buffer.resize(len / 4); + + int ret = xmp_play_buffer(context, (void*)int16_buffer.data(), len / 2, m_Looping? INT_MAX : 0); + xmp_set_player(context, XMP_PLAYER_INTERP, dumbConfig.mod_interp); + + if (ret >= 0) + { + float* soundbuffer = (float*)buffer; + for (unsigned int i = 0; i < len / 4; i++) + { + soundbuffer[i] = ((int16_buffer[i] < 0.) ? (int16_buffer[i] / 32768.) : (int16_buffer[i] / 32767.)) * dumbConfig.mod_dumb_mastervolume; + } + } + + if (ret < 0 && m_Looping) + { + xmp_restart_module(context); + xmp_set_position(context, subsong); + return true; + } + + return ret >= 0; +} + +bool XMPSong::Start() +{ + int ret = xmp_start_player(context, samplerate, 0); + if (ret >= 0) + xmp_set_position(context, subsong); + return ret >= 0; +} + +StreamSource* XMP_OpenSong(MusicIO::FileInterface* reader, int samplerate) +{ + if (xmp_test_module_from_callbacks((void*)reader, callbacks, nullptr) < 0) + return nullptr; + + xmp_context ctx = xmp_create_context(); + if (!ctx) + return nullptr; + + reader->seek(0, SEEK_SET); + + if (xmp_load_module_from_callbacks(ctx, (void*)reader, callbacks) < 0) + { + return nullptr; + } + + return new XMPSong(ctx, samplerate); +} + diff --git a/source/streamsources/music_opl.cpp b/source/streamsources/music_opl.cpp new file mode 100644 index 000000000..629f0aa13 --- /dev/null +++ b/source/streamsources/music_opl.cpp @@ -0,0 +1,159 @@ +/* +** music_opl.cpp +** Plays raw OPL formats +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +*/ + +#include "zmusic_internal.h" + +#ifdef HAVE_OPL + +#include + +#include "streamsource.h" +#include "oplsynth/opl.h" +#include "oplsynth/opl_mus_player.h" +#include "fileio.h" +#include "zmusic/midiconfig.h" + +//========================================================================== +// +// OPL file played by a software OPL2 synth and streamed through the sound system +// +//========================================================================== + +class OPLMUSSong : public StreamSource +{ +public: + OPLMUSSong (MusicIO::FileInterface *reader, OPLConfig *config); + ~OPLMUSSong (); + bool Start() override; + void ChangeSettingInt(const char *name, int value) override; + SoundStreamInfoEx GetFormatEx() override; + +protected: + bool GetData(void *buffer, size_t len) override; + + OPLmusicFile *Music; + int current_opl_core; +}; + + +//========================================================================== +// +// +// +//========================================================================== + +OPLMUSSong::OPLMUSSong(MusicIO::FileInterface* reader, OPLConfig* config) +{ + const char* error = nullptr; + reader->seek(0, SEEK_END); + auto fs = reader->tell(); + reader->seek(0, SEEK_SET); + std::vector data(fs); + reader->read(data.data(), (int)data.size()); + Music = new OPLmusicFile(data.data(), data.size(), config->core, config->numchips, error); + if (error) + { + delete Music; + throw std::runtime_error(error); + } + current_opl_core = config->core; +} + +//========================================================================== +// +// +// +//========================================================================== + +SoundStreamInfoEx OPLMUSSong::GetFormatEx() +{ + int samples = int(OPL_SAMPLE_RATE / 14); + return { samples * 4, int(OPL_SAMPLE_RATE), SampleType_Float32, + current_opl_core == 0? ChannelConfig_Mono:ChannelConfig_Stereo }; +} + +//========================================================================== +// +// +// +//========================================================================== + +OPLMUSSong::~OPLMUSSong () +{ + if (Music != NULL) + { + delete Music; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void OPLMUSSong::ChangeSettingInt(const char * name, int val) +{ + if (!strcmp(name, "opl.numchips")) + Music->ResetChips (val); +} + +//========================================================================== +// +// +// +//========================================================================== + +bool OPLMUSSong::Start() +{ + Music->SetLooping (m_Looping); + Music->Restart (); + return true; +} + +//========================================================================== +// +// +// +//========================================================================== + +bool OPLMUSSong::GetData(void *buffer, size_t len) +{ + return Music->ServiceStream(buffer, int(len)) ? len : 0; +} + +StreamSource *OPL_OpenSong(MusicIO::FileInterface* reader, OPLConfig *config) +{ + return new OPLMUSSong(reader, config); +} +#endif diff --git a/source/streamsources/music_xa.cpp b/source/streamsources/music_xa.cpp new file mode 100644 index 000000000..d889df8a6 --- /dev/null +++ b/source/streamsources/music_xa.cpp @@ -0,0 +1,354 @@ +#include +#include "streamsource.h" +#include "fileio.h" + +/** + * PlayStation XA (ADPCM) source support for MultiVoc + * Adapted and remixed from superxa2wav + * + * taken from EDuke32 and adapted for GZDoom by Christoph Oelckers + */ + + +//#define NO_XA_HEADER + +enum +{ +kNumOfSamples = 224, +kNumOfSGs = 18, +TTYWidth = 80, + +kBufSize = (kNumOfSGs*kNumOfSamples), +kSamplesMono = (kNumOfSGs*kNumOfSamples), +kSamplesStereo = (kNumOfSGs*kNumOfSamples/2), + +/* ADPCM */ +XA_DATA_START = (0x44-48) +}; + +inline float constexpr DblToPCMF(double dt) { return float(dt) * (1.f/32768.f); } + +typedef struct { + MusicIO::FileInterface *reader; + size_t committed; + size_t length; + bool blockIsMono; + bool blockIs18K; + bool finished; + + double t1, t2; + double t1_x, t2_x; + + float block[kBufSize]; +} xa_data; + +typedef int8_t SoundGroup[128]; + +typedef struct XASector { + int8_t sectorFiller[48]; + SoundGroup SoundGroups[18]; +} XASector; + +static double K0[4] = { + 0.0, + 0.9375, + 1.796875, + 1.53125 +}; +static double K1[4] = { + 0.0, + 0.0, + -0.8125, + -0.859375 +}; + + + +static int8_t getSoundData(int8_t *buf, int32_t unit, int32_t sample) +{ + int8_t ret; + int8_t *p; + int32_t offset, shift; + + p = buf; + shift = (unit%2) * 4; + + offset = 16 + (unit / 2) + (sample * 4); + p += offset; + + ret = (*p >> shift) & 0x0F; + + if (ret > 7) { + ret -= 16; + } + return ret; +} + +static int8_t getFilter(const int8_t *buf, int32_t unit) +{ + return (*(buf + 4 + unit) >> 4) & 0x03; +} + + +static int8_t getRange(const int8_t *buf, int32_t unit) +{ + return *(buf + 4 + unit) & 0x0F; +} + + +static void decodeSoundSectMono(XASector *ssct, xa_data * xad) +{ + size_t count = 0; + int8_t snddat, filt, range; + int32_t unit, sample; + int32_t sndgrp; + double tmp2, tmp3, tmp4, tmp5; + auto &decodeBuf = xad->block; + + for (sndgrp = 0; sndgrp < kNumOfSGs; sndgrp++) + { + for (unit = 0; unit < 8; unit++) + { + range = getRange(ssct->SoundGroups[sndgrp], unit); + filt = getFilter(ssct->SoundGroups[sndgrp], unit); + for (sample = 0; sample < 28; sample++) + { + snddat = getSoundData(ssct->SoundGroups[sndgrp], unit, sample); + tmp2 = (double)(1 << (12 - range)); + tmp3 = (double)snddat * tmp2; + tmp4 = xad->t1 * K0[filt]; + tmp5 = xad->t2 * K1[filt]; + xad->t2 = xad->t1; + xad->t1 = tmp3 + tmp4 + tmp5; + decodeBuf[count++] = DblToPCMF(xad->t1); + } + } + } +} + +static void decodeSoundSectStereo(XASector *ssct, xa_data * xad) +{ + size_t count = 0; + int8_t snddat, filt, range; + int8_t filt1, range1; + int32_t unit, sample; + int32_t sndgrp; + double tmp2, tmp3, tmp4, tmp5; + auto &decodeBuf = xad->block; + + for (sndgrp = 0; sndgrp < kNumOfSGs; sndgrp++) + { + for (unit = 0; unit < 8; unit+= 2) + { + range = getRange(ssct->SoundGroups[sndgrp], unit); + filt = getFilter(ssct->SoundGroups[sndgrp], unit); + range1 = getRange(ssct->SoundGroups[sndgrp], unit+1); + filt1 = getFilter(ssct->SoundGroups[sndgrp], unit+1); + + for (sample = 0; sample < 28; sample++) + { + // Channel 1 + snddat = getSoundData(ssct->SoundGroups[sndgrp], unit, sample); + tmp2 = (double)(1 << (12 - range)); + tmp3 = (double)snddat * tmp2; + tmp4 = xad->t1 * K0[filt]; + tmp5 = xad->t2 * K1[filt]; + xad->t2 = xad->t1; + xad->t1 = tmp3 + tmp4 + tmp5; + decodeBuf[count++] = DblToPCMF(xad->t1); + + // Channel 2 + snddat = getSoundData(ssct->SoundGroups[sndgrp], unit+1, sample); + tmp2 = (double)(1 << (12 - range1)); + tmp3 = (double)snddat * tmp2; + tmp4 = xad->t1_x * K0[filt1]; + tmp5 = xad->t2_x * K1[filt1]; + xad->t2_x = xad->t1_x; + xad->t1_x = tmp3 + tmp4 + tmp5; + decodeBuf[count++] = DblToPCMF(xad->t1_x); + } + } + } +} + +//========================================================================== +// +// Get one decoded block of data +// +//========================================================================== + +static void getNextXABlock(xa_data *xad, bool looping ) +{ + XASector ssct; + int coding; + const int SUBMODE_REAL_TIME_SECTOR = (1 << 6); + const int SUBMODE_FORM = (1 << 5); + const int SUBMODE_AUDIO_DATA = (1 << 2); + + do + { + size_t bytes = xad->length - xad->reader->tell(); + + if (sizeof(XASector) < bytes) + bytes = sizeof(XASector); + + xad->reader->read(&ssct, (int)bytes); + } + while (ssct.sectorFiller[46] != (SUBMODE_REAL_TIME_SECTOR | SUBMODE_FORM | SUBMODE_AUDIO_DATA)); + + coding = ssct.sectorFiller[47]; + + xad->committed = 0; + xad->blockIsMono = (coding & 3) == 0; + xad->blockIs18K = (((coding >> 2) & 3) == 1); + + if (!xad->blockIsMono) + { + decodeSoundSectStereo(&ssct, xad); + } + else + { + decodeSoundSectMono(&ssct, xad); + } + + if (xad->length == xad->reader->tell()) + { + if (looping) + { + xad->reader->seek(XA_DATA_START, SEEK_SET); + xad->t1 = xad->t2 = xad->t1_x = xad->t2_x = 0; + } + else + xad->finished = true; + } + + xad->finished = false; +} + +//========================================================================== +// +// XASong +// +//========================================================================== + +class XASong : public StreamSource +{ +public: + XASong(MusicIO::FileInterface *readr); + SoundStreamInfoEx GetFormatEx() override; + bool Start() override; + bool GetData(void *buffer, size_t len) override; + +protected: + xa_data xad; +}; + +//========================================================================== +// +// XASong - Constructor +// +//========================================================================== + +XASong::XASong(MusicIO::FileInterface * reader) +{ + reader->seek(0, SEEK_END); + xad.length = reader->tell(); + reader->seek(XA_DATA_START, SEEK_SET); + xad.reader = reader; + xad.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0; + + getNextXABlock(&xad, false); +} + +SoundStreamInfoEx XASong::GetFormatEx() +{ + auto SampleRate = xad.blockIs18K? 18900 : 37800; + return { 64*1024, SampleRate, SampleType_Float32, ChannelConfig_Stereo }; +} + +//========================================================================== +// +// XASong :: Play +// +//========================================================================== + +bool XASong::Start() +{ + if (xad.finished && m_Looping) + { + xad.reader->seek(XA_DATA_START, SEEK_SET); + xad.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0; + xad.finished = false; + } + return true; +} + +//========================================================================== +// +// XASong :: Read +// +//========================================================================== + +bool XASong::GetData(void *vbuff, size_t len) +{ + float *dest = (float*)vbuff; + while (len > 0) + { + auto ptr = xad.committed; + auto block = xad.block + ptr; + if (ptr < kBufSize) + { + // commit the data + if (xad.blockIsMono) + { + size_t numsamples = len / 8; + size_t availdata = kBufSize - ptr; + + for(size_t tocopy = std::min(numsamples, availdata); tocopy > 0; tocopy--) + { + float f = *block++; + *dest++ = f; + *dest++ = f; + len -= 8; + ptr++; + } + xad.committed = ptr; + } + else + { + size_t availdata = (kBufSize - ptr) * 4; + size_t tocopy = std::min(availdata, len); + memcpy(dest, block, tocopy); + dest += tocopy / 4; + len -= tocopy; + xad.committed += tocopy / 4; + } + } + if (xad.finished) + { + memset(dest, 0, len); + return true; + } + if (len > 0) + { + // we ran out of data and need more + getNextXABlock(&xad, m_Looping); + // repeat until done. + } + else break; + + } + return !xad.finished; +} + +//========================================================================== +// +// XA_OpenSong +// +//========================================================================== + +StreamSource *XA_OpenSong(MusicIO::FileInterface *reader) +{ + return new XASong(reader); +} + diff --git a/source/streamsources/streamsource.h b/source/streamsources/streamsource.h new file mode 100644 index 000000000..9f31a36b0 --- /dev/null +++ b/source/streamsources/streamsource.h @@ -0,0 +1,40 @@ +#pragma once + +// Anything streamed to the sound system as raw wave data, except MIDIs -------------------- + +#include +#include "zmusic/mididefs.h" // for StreamSourceInfo +#include "zmusic/midiconfig.h" + +class StreamSource +{ +protected: + bool m_Looping = true; + int m_OutputRate; + +public: + + StreamSource (int outputRate) { m_OutputRate = outputRate; } + virtual ~StreamSource () {} + virtual void SetPlayMode(bool looping) { m_Looping = looping; } + virtual bool Start() { return true; } + virtual bool SetPosition(unsigned position) { return false; } + virtual bool SetSubsong(int subsong) { return false; } + virtual bool GetData(void *buffer, size_t len) = 0; + virtual SoundStreamInfoEx GetFormatEx() = 0; + virtual std::string GetStats() { return ""; } + virtual void ChangeSettingInt(const char *name, int value) { } + virtual void ChangeSettingNum(const char *name, double value) { } + virtual void ChangeSettingString(const char *name, const char *value) { } + +protected: + StreamSource() = default; +}; + + +StreamSource *MOD_OpenSong(MusicIO::FileInterface* reader, int samplerate); +StreamSource *XMP_OpenSong(MusicIO::FileInterface* reader, int samplerate); +StreamSource* GME_OpenSong(MusicIO::FileInterface* reader, const char* fmt, int sample_rate); +StreamSource *SndFile_OpenSong(MusicIO::FileInterface* fr); +StreamSource* XA_OpenSong(MusicIO::FileInterface* reader); +StreamSource* OPL_OpenSong(MusicIO::FileInterface* reader, OPLConfig *config); diff --git a/source/zmusic/configuration.cpp b/source/zmusic/configuration.cpp new file mode 100644 index 000000000..def26baf1 --- /dev/null +++ b/source/zmusic/configuration.cpp @@ -0,0 +1,1196 @@ +/* +** configuration.cpp +** Handle zmusic's configuration. +** +**--------------------------------------------------------------------------- +** Copyright 2019 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifdef _WIN32 +#include +#include +#endif +#include +#include "critsec.h" +#include "dumb.h" + +#include "zmusic_internal.h" +#include "musinfo.h" +#include "midiconfig.h" +#include "mididevices/music_alsa_state.h" + +#ifdef HAVE_TIMIDITY +#include "timidity/timidity.h" +#include "timiditypp/timidity.h" +#endif +#ifdef HAVE_OPL +#include "oplsynth/oplio.h" +#endif + +struct Dummy +{ + void ChangeSettingInt(const char*, int) {} + void ChangeSettingNum(const char*, double) {} + void ChangeSettingString(const char*, const char*) {} +}; + +#define devType() ((currSong)? (currSong)->GetDeviceType() : MDEV_DEFAULT) + + +MiscConfig miscConfig; +ZMusicCallbacks musicCallbacks; + +class SoundFontWrapperInterface : public MusicIO::SoundFontReaderInterface +{ + void* handle; + +public: + SoundFontWrapperInterface(void* h) + { + handle = h; + } + + struct MusicIO::FileInterface* open_file(const char* fn) override + { + auto rd = musicCallbacks.SF_OpenFile(handle, fn); + if (rd) + { + auto fr = new CustomFileReader(rd); + if (fr) fr->filename = fn? fn : "timidity.cfg"; + return fr; + } + else return nullptr; + } + void add_search_path(const char* path) override + { + musicCallbacks.SF_AddToSearchPath(handle, path); + } + void close() override + { + musicCallbacks.SF_Close(handle); + delete this; + } +}; + +namespace MusicIO { + SoundFontReaderInterface* ClientOpenSoundFont(const char* name, int type) + { + if (!musicCallbacks.OpenSoundFont) return nullptr; + auto iface = musicCallbacks.OpenSoundFont(name, type); + if (!iface) return nullptr; + return new SoundFontWrapperInterface(iface); + } +} + + +void ZMusic_Print(int type, const char* msg, va_list args) +{ + static char printbuf[4096]; + vsnprintf(printbuf, 4096, msg, args); + if (musicCallbacks.MessageFunc) + { + musicCallbacks.MessageFunc(type, printbuf); + } + else fputs(printbuf, type >= ZMUSIC_MSG_WARNING ? stderr : stdout); +} + +void ZMusic_Printf(int type, const char* msg, ...) +{ + va_list ap; + va_start(ap, msg); + ZMusic_Print(type, msg, ap); + va_end(ap); +} + +DLL_EXPORT void ZMusic_SetCallbacks(const ZMusicCallbacks* cb) +{ + musicCallbacks = *cb; + // If not all these are set the sound font interface is not usable. + if (!cb->SF_AddToSearchPath || !cb->SF_OpenFile || !cb->SF_Close) + musicCallbacks.OpenSoundFont = nullptr; +} + +DLL_EXPORT void ZMusic_SetGenMidi(const uint8_t* data) +{ +#ifdef HAVE_OPL + memcpy(oplConfig.OPLinstruments, data, 175 * 36); + oplConfig.genmidiset = true; +#endif +#ifdef HAVE_ADL + memcpy(adlConfig.adl_genmidi_bank, data, 175 * 36); + adlConfig.adl_genmidi_set = true; +#endif +} + +DLL_EXPORT void ZMusic_SetWgOpn(const void* data, unsigned len) +{ +#ifdef HAVE_OPN + opnConfig.default_bank.resize(len); + memcpy(opnConfig.default_bank.data(), data, len); +#endif +} + +DLL_EXPORT void ZMusic_SetDmxGus(const void* data, unsigned len) +{ +#ifdef HAVE_GUS + gusConfig.dmxgus.resize(len); + memcpy(gusConfig.dmxgus.data(), data, len); +#endif +} + +int ZMusic_EnumerateMidiDevices() +{ +#ifdef HAVE_SYSTEM_MIDI + #ifdef __linux__ + auto & sequencer = AlsaSequencer::Get(); + return sequencer.EnumerateDevices(); + #elif _WIN32 + // TODO: move the weird stuff from music_midi_base.cpp here, or at least to this lib and call it here + return {}; + #endif +#else + return {}; +#endif +} + + +struct MidiDeviceList +{ + std::vector devices; + ~MidiDeviceList() + { + for (auto& device : devices) + { + free(device.Name); + } + } + void Build() + { +#ifdef HAVE_OPN + devices.push_back({ strdup("libOPN"), -8, MIDIDEV_FMSYNTH }); +#endif +#ifdef HAVE_ADL + devices.push_back({ strdup("libADL"), -7, MIDIDEV_FMSYNTH }); +#endif +#ifdef HAVE_WILDMIDI + devices.push_back({ strdup("WildMidi"), -6, MIDIDEV_SWSYNTH }); +#endif + // this will always exist. + devices.push_back({ strdup("FluidSynth"), -5, MIDIDEV_SWSYNTH }); + +#ifdef HAVE_GUS + devices.push_back({ strdup("GUS Emulation"), -4, MIDIDEV_SWSYNTH }); +#endif +#ifdef HAVE_OPL + devices.push_back({ strdup("OPL Synth Emulation"), -3, MIDIDEV_FMSYNTH }); +#endif +#ifdef HAVE_TIMIDITY + devices.push_back({ strdup("TiMidity++"), -2, MIDIDEV_SWSYNTH }); +#endif + +#ifdef HAVE_SYSTEM_MIDI +#ifdef __linux__ + auto& sequencer = AlsaSequencer::Get(); + sequencer.EnumerateDevices(); + auto& dev = sequencer.GetInternalDevices(); + for (auto& d : dev) + { + ZMusicMidiOutDevice mdev = { strdup(d.Name.c_str()), d.ID, MIDIDEV_MAPPER }; // fixme: Correctly determine the type of the device. + devices.push_back(mdev); + } +#elif _WIN32 + UINT nummididevices = midiOutGetNumDevs(); + for (uint32_t id = 0; id < nummididevices; ++id) + { + MIDIOUTCAPSW caps; + MMRESULT res; + + res = midiOutGetDevCapsW(id, &caps, sizeof(caps)); + if (res == MMSYSERR_NOERROR) + { + auto len = wcslen(caps.szPname); + int size_needed = WideCharToMultiByte(CP_UTF8, 0, caps.szPname, (int)len, nullptr, 0, nullptr, nullptr); + char* outbuf = (char*)malloc(size_needed + 1); + WideCharToMultiByte(CP_UTF8, 0, caps.szPname, (int)len, outbuf, size_needed, nullptr, nullptr); + outbuf[size_needed] = 0; + + ZMusicMidiOutDevice mdev = { outbuf, int(id), caps.wTechnology }; + devices.push_back(mdev); + } + } +#endif +#endif + } + +}; + +static MidiDeviceList devlist; + +DLL_EXPORT const ZMusicMidiOutDevice* ZMusic_GetMidiDevices(int* pAmount) +{ + if (devlist.devices.size() == 0) devlist.Build(); + if (pAmount) *pAmount = (int)devlist.devices.size(); + return devlist.devices.data(); +} + + + +template +void ChangeAndReturn(valtype &variable, valtype value, valtype *realv) +{ + variable = value; + if (realv) *realv = value; +} + +#define FLUID_CHORUS_MOD_SINE 0 +#define FLUID_CHORUS_MOD_TRIANGLE 1 +#define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE + +#ifdef HAVE_TIMIDITY + +//========================================================================== +// +// Timidity++ uses a static global set of configuration variables. +// THese can be changed live while the synth is playing but need synchronization. +// +// Currently the synth is not reentrant due to this and a handful +// of other global variables. +// +//========================================================================== + +template void ChangeVarSync(T& var, T value) +{ + std::lock_guard lock(TimidityPlus::ConfigMutex); + var = value; +} + +//========================================================================== +// +// Timidity++ reverb is a bit more complicated because it is two properties in one value. +// +//========================================================================== + +/* +* reverb=0 no reverb 0 +* reverb=1 old reverb 1 +* reverb=1,n set reverb level to n (-1 to -127) +* reverb=2 "global" old reverb 2 +* reverb=2,n set reverb level to n (-1 to -127) - 128 +* reverb=3 new reverb 3 +* reverb=3,n set reverb level to n (-1 to -127) - 256 +* reverb=4 "global" new reverb 4 +* reverb=4,n set reverb level to n (-1 to -127) - 384 +*/ +static int local_timidity_reverb_level; +static int local_timidity_reverb; + +static void TimidityPlus_SetReverb() +{ + int value = 0; + int mode = local_timidity_reverb; + int level = local_timidity_reverb_level; + + if (mode == 0 || level == 0) value = mode; + else value = (mode - 1) * -128 - level; + ChangeVarSync(TimidityPlus::timidity_reverb, value); +} +#endif + + +//========================================================================== +// +// change an integer value +// +//========================================================================== + +DLL_EXPORT zmusic_bool ChangeMusicSettingInt(EIntConfigKey key, MusInfo *currSong, int value, int *pRealValue) +{ + switch (key) + { + default: + return false; + +#ifdef HAVE_ADL + case zmusic_adl_chips_count: + if (currSong != NULL && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libadl.numchips", value); + } + + ChangeAndReturn(adlConfig.adl_chips_count, value, pRealValue); + return false; + + case zmusic_adl_emulator_id: + if (currSong != NULL && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libadl.emulator", value); + } + + ChangeAndReturn(adlConfig.adl_emulator_id, value, pRealValue); + return false; + + case zmusic_adl_run_at_pcm_rate: + if (currSong != NULL && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libadl.runatpcmrate", value); + } + + ChangeAndReturn(adlConfig.adl_run_at_pcm_rate, value, pRealValue); + return false; + + case zmusic_adl_fullpan: + if (currSong != NULL && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libadl.fullpan", value); + } + + ChangeAndReturn(adlConfig.adl_fullpan, value, pRealValue); + return false; + + case zmusic_adl_bank: + if (currSong != NULL && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libadl.banknum", value); + } + + ChangeAndReturn(adlConfig.adl_bank, value, pRealValue); + return false; + + case zmusic_adl_use_custom_bank: + if (currSong != NULL && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libadl.usecustombank", value); + } + + ChangeAndReturn(adlConfig.adl_use_custom_bank, value, pRealValue); + return false; + + case zmusic_adl_use_genmidi: + if (currSong != NULL && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libadl.usegenmidi", value); + } + + ChangeAndReturn(adlConfig.adl_use_genmidi, value, pRealValue); + return false; + + case zmusic_adl_volume_model: + if (currSong != NULL && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libadl.volumemodel", value); + } + + ChangeAndReturn(adlConfig.adl_volume_model, value, pRealValue); + return false; + + case zmusic_adl_chan_alloc: + if (currSong != NULL && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libadl.chanalloc", value); + } + + ChangeAndReturn(adlConfig.adl_chan_alloc, value, pRealValue); + return false; + + case zmusic_adl_auto_arpeggio: + if (currSong != NULL && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libadl.autoarpeggio", value); + } + + ChangeAndReturn(adlConfig.adl_auto_arpeggio, value, pRealValue); + return false; +#endif + + case zmusic_fluid_reverb: + if (currSong != NULL) + currSong->ChangeSettingInt("fluidsynth.synth.reverb.active", value); + + ChangeAndReturn(fluidConfig.fluid_reverb, value, pRealValue); + return false; + + case zmusic_fluid_chorus: + if (currSong != NULL) + currSong->ChangeSettingInt("fluidsynth.synth.chorus.active", value); + + ChangeAndReturn(fluidConfig.fluid_chorus, value, pRealValue); + return false; + + case zmusic_fluid_voices: + if (value < 16) + value = 16; + else if (value > 4096) + value = 4096; + + if (currSong != NULL) + currSong->ChangeSettingInt("fluidsynth.synth.polyphony", value); + + ChangeAndReturn(fluidConfig.fluid_voices, value, pRealValue); + return false; + + case zmusic_fluid_interp: + // Values are: 0 = FLUID_INTERP_NONE + // 1 = FLUID_INTERP_LINEAR + // 4 = FLUID_INTERP_4THORDER (the FluidSynth default) + // 7 = FLUID_INTERP_7THORDER + // (And here I thought it was just a linear list.) + // Round undefined values to the nearest valid one. + if (value < 0) + value = 0; + else if (value == 2) + value = 1; + else if (value == 3 || value == 5) + value = 4; + else if (value == 6 || value > 7) + value = 7; + + if (currSong != NULL) + currSong->ChangeSettingInt("fluidsynth.synth.interpolation", value); + + ChangeAndReturn(fluidConfig.fluid_interp, value, pRealValue); + return false; + + case zmusic_fluid_samplerate: + // This will only take effect for the next song. (Q: Is this even needed?) + ChangeAndReturn(fluidConfig.fluid_samplerate, std::max(value, 0), pRealValue); + return false; + + // I don't know if this setting even matters for us, since we aren't letting + // FluidSynth drives its own output. + case zmusic_fluid_threads: + if (value < 1) + value = 1; + else if (value > 256) + value = 256; + + ChangeAndReturn(fluidConfig.fluid_threads, value, pRealValue); + return false; + + case zmusic_fluid_chorus_voices: + if (value < 0) + value = 0; + else if (value > 99) + value = 99; + + if (currSong != NULL) + currSong->ChangeSettingNum("fluidsynth.z.chorus", value); + + ChangeAndReturn(fluidConfig.fluid_chorus_voices, value, pRealValue); + return false; + + case zmusic_fluid_chorus_type: + if (value != FLUID_CHORUS_MOD_SINE && value != FLUID_CHORUS_MOD_TRIANGLE) + value = FLUID_CHORUS_DEFAULT_TYPE; + + if (currSong != NULL) + currSong->ChangeSettingNum("fluidsynth.z.chorus", value); // Uses float to simplify the checking code in the renderer. + + ChangeAndReturn(fluidConfig.fluid_chorus_type, value, pRealValue); + return false; + +#ifdef HAVE_OPL + case zmusic_opl_numchips: + if (value <= 0) + value = 1; + else if (value > MAXOPL2CHIPS) + value = MAXOPL2CHIPS; + + if (currSong != NULL && devType() == MDEV_OPL) + currSong->ChangeSettingInt("opl.numchips", value); + + ChangeAndReturn(oplConfig.numchips, value, pRealValue); + return false; + + case zmusic_opl_core: + if (value < 0) value = 0; + else if (value > 3) value = 3; + ChangeAndReturn(oplConfig.core, value, pRealValue); + return devType() == MDEV_OPL; + + case zmusic_opl_fullpan: + ChangeAndReturn(oplConfig.fullpan, value, pRealValue); + return false; +#endif +#ifdef HAVE_OPN + case zmusic_opn_chips_count: + if (currSong != NULL && devType() == MDEV_OPN) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libopn.numchips", value); + } + + ChangeAndReturn(opnConfig.opn_chips_count, value, pRealValue); + return false; + + case zmusic_opn_emulator_id: + if (currSong != NULL && devType() == MDEV_OPN) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libopn.emulator", value); + } + + ChangeAndReturn(opnConfig.opn_emulator_id, value, pRealValue); + return false; + + case zmusic_opn_run_at_pcm_rate: + if (currSong != NULL && devType() == MDEV_OPN) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libopn.runatpcmrate", value); + } + + ChangeAndReturn(opnConfig.opn_run_at_pcm_rate, value, pRealValue); + return false; + + case zmusic_opn_fullpan: + if (currSong != NULL && devType() == MDEV_OPN) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libopn.fullpan", value); + } + + ChangeAndReturn(opnConfig.opn_fullpan, value, pRealValue); + return false; + + case zmusic_opn_use_custom_bank: + if (currSong != NULL && devType() == MDEV_OPN) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libopn.usecustombank", value); + } + + ChangeAndReturn(opnConfig.opn_use_custom_bank, value, pRealValue); + return false; + + case zmusic_opn_volume_model: + if (currSong != NULL && devType() == MDEV_OPN) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libopn.volumemodel", value); + } + + ChangeAndReturn(opnConfig.opn_volume_model, value, pRealValue); + return false; + + case zmusic_opn_chan_alloc: + if (currSong != NULL && devType() == MDEV_OPN) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libopn.chanalloc", value); + } + + ChangeAndReturn(opnConfig.opn_chan_alloc, value, pRealValue); + return false; + + case zmusic_opn_auto_arpeggio: + if (currSong != NULL && devType() == MDEV_OPN) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingInt("libopn.autoarpeggio", value); + } + + ChangeAndReturn(opnConfig.opn_auto_arpeggio, value, pRealValue); + return false; +#endif +#ifdef HAVE_GUS + case zmusic_gus_dmxgus: + ChangeAndReturn(gusConfig.gus_dmxgus, value, pRealValue); + return devType() == MDEV_GUS; + + case zmusic_gus_midi_voices: + ChangeAndReturn(gusConfig.midi_voices, value, pRealValue); + return devType() == MDEV_GUS; + + case zmusic_gus_memsize: + ChangeAndReturn(gusConfig.gus_memsize, value, pRealValue); + return devType() == MDEV_GUS; +#endif +#ifdef HAVE_TIMIDITY + case zmusic_timidity_modulation_wheel: + ChangeVarSync(TimidityPlus::timidity_modulation_wheel, value); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_portamento: + ChangeVarSync(TimidityPlus::timidity_portamento, value); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_reverb: + if (value < 0 || value > 4) value = 0; + local_timidity_reverb = value; + TimidityPlus_SetReverb(); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_reverb_level: + if (value < 0 || value > 127) value = 0; + local_timidity_reverb_level = value; + TimidityPlus_SetReverb(); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_chorus: + ChangeVarSync(TimidityPlus::timidity_chorus, value); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_surround_chorus: + ChangeVarSync(TimidityPlus::timidity_surround_chorus, value); + if (pRealValue) *pRealValue = value; + return devType() == MDEV_TIMIDITY; + + case zmusic_timidity_channel_pressure: + ChangeVarSync(TimidityPlus::timidity_channel_pressure, value); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_lpf_def: + ChangeVarSync(TimidityPlus::timidity_lpf_def, value); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_temper_control: + ChangeVarSync(TimidityPlus::timidity_temper_control, value); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_modulation_envelope: + ChangeVarSync(TimidityPlus::timidity_modulation_envelope, value); + if (pRealValue) *pRealValue = value; + return devType() == MDEV_TIMIDITY; + + case zmusic_timidity_overlap_voice_allow: + ChangeVarSync(TimidityPlus::timidity_overlap_voice_allow, value); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_drum_effect: + ChangeVarSync(TimidityPlus::timidity_drum_effect, value); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_pan_delay: + ChangeVarSync(TimidityPlus::timidity_pan_delay, value); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_key_adjust: + if (value < -24) value = -24; + else if (value > 24) value = 24; + ChangeVarSync(TimidityPlus::timidity_key_adjust, value); + if (pRealValue) *pRealValue = value; + return false; +#endif +#ifdef HAVE_WILDMIDI + case zmusic_wildmidi_reverb: + if (currSong != NULL) + currSong->ChangeSettingInt("wildmidi.reverb", value); + wildMidiConfig.reverb = value; + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_wildmidi_enhanced_resampling: + if (currSong != NULL) + currSong->ChangeSettingInt("wildmidi.resampling", value); + wildMidiConfig.enhanced_resampling = value; + if (pRealValue) *pRealValue = value; + return false; +#endif + case zmusic_snd_midiprecache: + ChangeAndReturn(miscConfig.snd_midiprecache, value, pRealValue); + return false; + + case zmusic_snd_streambuffersize: + if (value < 16) + { + value = 16; + } + else if (value > 1024) + { + value = 1024; + } + ChangeAndReturn(miscConfig.snd_streambuffersize, value, pRealValue); + return false; + + case zmusic_mod_samplerate: + ChangeAndReturn(dumbConfig.mod_samplerate, value, pRealValue); + return false; + + case zmusic_mod_volramp: + ChangeAndReturn(dumbConfig.mod_volramp, value, pRealValue); + return false; + + case zmusic_mod_interp: + ChangeAndReturn(dumbConfig.mod_interp, value, pRealValue); + return false; + + case zmusic_mod_autochip: + ChangeAndReturn(dumbConfig.mod_autochip, value, pRealValue); + return false; + + case zmusic_mod_autochip_size_force: + ChangeAndReturn(dumbConfig.mod_autochip_size_force, value, pRealValue); + return false; + + case zmusic_mod_autochip_size_scan: + ChangeAndReturn(dumbConfig.mod_autochip_size_scan, value, pRealValue); + return false; + + case zmusic_mod_autochip_scan_threshold: + ChangeAndReturn(dumbConfig.mod_autochip_scan_threshold, value, pRealValue); + return false; + + case zmusic_snd_mididevice: + { + bool change = miscConfig.snd_mididevice != value; + miscConfig.snd_mididevice = value; + return change; + } + + case zmusic_snd_outputrate: + miscConfig.snd_outputrate = value; + return false; + + case zmusic_mod_preferredplayer: + dumbConfig.mod_preferred_player = value; + return false; + + } + return false; +} + +DLL_EXPORT zmusic_bool ChangeMusicSettingFloat(EFloatConfigKey key, MusInfo* currSong, float value, float *pRealValue) +{ + switch (key) + { + default: + return false; + + case zmusic_fluid_gain: + if (value < 0) + value = 0; + else if (value > 10) + value = 10; + + if (currSong != NULL) + currSong->ChangeSettingNum("fluidsynth.synth.gain", value); + + ChangeAndReturn(fluidConfig.fluid_gain, value, pRealValue); + return false; + + case zmusic_fluid_reverb_roomsize: + if (value < 0) + value = 0; + else if (value > 1.0f) + value = 1.0f; + + if (currSong != NULL) + currSong->ChangeSettingNum("fluidsynth.z.reverb", value); + + ChangeAndReturn(fluidConfig.fluid_reverb_roomsize, value, pRealValue); + return false; + + case zmusic_fluid_reverb_damping: + if (value < 0) + value = 0; + else if (value > 1) + value = 1; + + if (currSong != NULL) + currSong->ChangeSettingNum("fluidsynth.z.reverb", value); + + ChangeAndReturn(fluidConfig.fluid_reverb_damping, value, pRealValue); + return false; + + case zmusic_fluid_reverb_width: + if (value < 0) + value = 0; + else if (value > 100) + value = 100; + + if (currSong != NULL) + currSong->ChangeSettingNum("fluidsynth.z.reverb", value); + + ChangeAndReturn(fluidConfig.fluid_reverb_width, value, pRealValue); + return false; + + case zmusic_fluid_reverb_level: + if (value < 0) + value = 0; + else if (value > 1) + value = 1; + + if (currSong != NULL) + currSong->ChangeSettingNum("fluidsynth.z.reverb", value); + + ChangeAndReturn(fluidConfig.fluid_reverb_level, value, pRealValue); + return false; + + case zmusic_fluid_chorus_level: + if (value < 0) + value = 0; + else if (value > 1) + value = 1; + + if (currSong != NULL) + currSong->ChangeSettingNum("fluidsynth.z.chorus", value); + + ChangeAndReturn(fluidConfig.fluid_chorus_level, value, pRealValue); + return false; + + case zmusic_fluid_chorus_speed: + if (value < 0.1f) + value = 0.1f; + else if (value > 5) + value = 5; + + if (currSong != NULL) + currSong->ChangeSettingNum("fluidsynth.z.chorus", value); + + ChangeAndReturn(fluidConfig.fluid_chorus_speed, value, pRealValue); + return false; + + // depth is in ms and actual maximum depends on the sample rate + case zmusic_fluid_chorus_depth: + if (value < 0) + value = 0; + else if (value > 256) + value = 256; + + if (currSong != NULL) + currSong->ChangeSettingNum("fluidsynth.z.chorus", value); + + ChangeAndReturn(fluidConfig.fluid_chorus_depth, value, pRealValue); + return false; + +#ifdef HAVE_TIMIDITY + case zmusic_timidity_drum_power: + if (value < 0) value = 0; + else if (value > MAX_AMPLIFICATION / 100.f) value = MAX_AMPLIFICATION / 100.f; + ChangeVarSync(TimidityPlus::timidity_drum_power, value); + if (pRealValue) *pRealValue = value; + return false; + + // For testing mainly. + case zmusic_timidity_tempo_adjust: + if (value < 0.25) value = 0.25; + else if (value > 10) value = 10; + ChangeVarSync(TimidityPlus::timidity_tempo_adjust, value); + if (pRealValue) *pRealValue = value; + return false; + + case zmusic_timidity_min_sustain_time: + if (value < 0) value = 0; + ChangeVarSync(TimidityPlus::min_sustain_time, value); + if (pRealValue) *pRealValue = value; + return false; +#endif +#ifdef HAVE_OPL + case zmusic_opl_gain: + if (value < 0) + value = 0; + else if (value > 10) + value = 10; + + if (currSong != NULL && devType() == MDEV_OPL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingNum("oplemu.gain", value); + } + + ChangeAndReturn(oplConfig.gain, value, pRealValue); + return false; +#endif +#ifdef HAVE_ADL + case zmusic_adl_gain: + if (value < 0) + value = 0; + else if (value > 10) + value = 10; + + if (currSong != NULL && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingNum("libadl.gain", value); + } + + ChangeAndReturn(adlConfig.adl_gain, value, pRealValue); + return false; +#endif + +#ifdef HAVE_ADL + case zmusic_opn_gain: + if (value < 0) + value = 0; + else if (value > 10) + value = 10; + + if (currSong != NULL && devType() == MDEV_OPN) + { + std::lock_guard lock(currSong->CritSec); + currSong->ChangeSettingNum("libopn.gain", value); + } + + ChangeAndReturn(opnConfig.opn_gain, value, pRealValue); + return false; +#endif + + case zmusic_gme_stereodepth: + if (currSong != nullptr) + currSong->ChangeSettingNum("GME.stereodepth", value); + ChangeAndReturn(miscConfig.gme_stereodepth, value, pRealValue); + return false; + + case zmusic_mod_dumb_mastervolume: + if (value < 0) value = 0; + ChangeAndReturn(dumbConfig.mod_dumb_mastervolume, value, pRealValue); + return false; + + case zmusic_snd_musicvolume: + miscConfig.snd_musicvolume = value; + return false; + + case zmusic_relative_volume: + miscConfig.relative_volume = value; + return false; + + case zmusic_snd_mastervolume: + miscConfig.snd_mastervolume = value; + return false; + + } + return false; +} + +DLL_EXPORT zmusic_bool ChangeMusicSettingString(EStringConfigKey key, MusInfo* currSong, const char *value) +{ + switch (key) + { + default: + return false; + +#ifdef HAVE_ADL + case zmusic_adl_custom_bank: + if (currSong != nullptr && devType() == MDEV_ADL) + { + std::lock_guard lock(currSong->CritSec); + const char* info; + if (musicCallbacks.PathForSoundfont) + { + info = musicCallbacks.PathForSoundfont(value, SF_WOPL); + } + else + { + info = value; + } + + if (info == nullptr) + { + info = ""; + } + + currSong->ChangeSettingString("libadl.custombank", info); + } + + adlConfig.adl_custom_bank = value; + return false; +#endif + case zmusic_fluid_lib: + fluidConfig.fluid_lib = value; + return false; // only takes effect for next song. + + case zmusic_fluid_patchset: + fluidConfig.fluid_patchset = value; +#ifdef HAVE_TIMIDITY + if (timidityConfig.timidity_config.empty()) timidityConfig.timidity_config = value; // Also use for Timidity++ if nothing has been set. +#endif + return devType() == MDEV_FLUIDSYNTH; + +#ifdef HAVE_OPN + case zmusic_opn_custom_bank: + if (currSong != nullptr && devType() == MDEV_OPN) + { + std::lock_guard lock(currSong->CritSec); + const char* info; + if (musicCallbacks.PathForSoundfont) + { + info = musicCallbacks.PathForSoundfont(value, SF_WOPN); + } + else + { + info = value; + } + + if (info == nullptr) + { + info = ""; + } + + currSong->ChangeSettingString("libopn.custombank", info); + } + + opnConfig.opn_custom_bank = value; + return false; +#endif +#ifdef HAVE_GUS + case zmusic_gus_config: + gusConfig.gus_config = value; + return devType() == MDEV_GUS; +#endif +#ifdef HAVE_GUS + case zmusic_gus_patchdir: + gusConfig.gus_patchdir = value; + return devType() == MDEV_GUS && gusConfig.gus_dmxgus; +#endif +#ifdef HAVE_TIMIDITY + case zmusic_timidity_config: + timidityConfig.timidity_config = value; + return devType() == MDEV_TIMIDITY; +#endif +#ifdef HAVE_WILDMIDI + case zmusic_wildmidi_config: + wildMidiConfig.config = value; + return devType() == MDEV_WILDMIDI; +#endif + } + return false; +} + +static ZMusicConfigurationSetting config[] = { +#ifdef HAVE_ADL + {"zmusic_adl_chips_count", zmusic_adl_chips_count, ZMUSIC_VAR_INT, 5}, + {"zmusic_adl_emulator_id", zmusic_adl_emulator_id, ZMUSIC_VAR_INT, 0}, + {"zmusic_adl_run_at_pcm_rate", zmusic_adl_run_at_pcm_rate, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_adl_fullpan", zmusic_adl_fullpan, ZMUSIC_VAR_BOOL, 1}, + {"zmusic_adl_bank", zmusic_adl_bank, ZMUSIC_VAR_INT, 14}, + {"zmusic_adl_use_custom_bank", zmusic_adl_use_custom_bank, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_adl_volume_model", zmusic_adl_volume_model, ZMUSIC_VAR_INT, 3}, + {"zmusic_adl_custom_bank", zmusic_adl_custom_bank, ZMUSIC_VAR_STRING, 0}, + {"zmusic_adl_gain", zmusic_adl_gain, ZMUSIC_VAR_FLOAT, 1.0f}, +#endif + {"zmusic_fluid_reverb", zmusic_fluid_reverb, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_fluid_chorus", zmusic_fluid_chorus, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_fluid_voices", zmusic_fluid_voices, ZMUSIC_VAR_INT, 128}, + {"zmusic_fluid_interp", zmusic_fluid_interp, ZMUSIC_VAR_INT, 1}, + {"zmusic_fluid_samplerate", zmusic_fluid_samplerate, ZMUSIC_VAR_INT, 0}, + {"zmusic_fluid_threads", zmusic_fluid_threads, ZMUSIC_VAR_INT, 1}, + {"zmusic_fluid_chorus_voices", zmusic_fluid_chorus_voices, ZMUSIC_VAR_INT, 3}, + {"zmusic_fluid_chorus_type", zmusic_fluid_chorus_type, ZMUSIC_VAR_INT, 0}, + {"zmusic_fluid_gain", zmusic_fluid_gain, ZMUSIC_VAR_FLOAT, 0}, + {"zmusic_fluid_reverb_roomsize", zmusic_fluid_reverb_roomsize, ZMUSIC_VAR_FLOAT, 0.75f}, + {"zmusic_fluid_reverb_damping", zmusic_fluid_reverb_damping, ZMUSIC_VAR_FLOAT, 0.23f}, + {"zmusic_fluid_reverb_width", zmusic_fluid_reverb_width, ZMUSIC_VAR_FLOAT, 0.75f}, + {"zmusic_fluid_reverb_level", zmusic_fluid_reverb_level, ZMUSIC_VAR_FLOAT, 0.57f}, + {"zmusic_fluid_chorus_level", zmusic_fluid_chorus_level, ZMUSIC_VAR_FLOAT, 1.2f}, + {"zmusic_fluid_chorus_speed", zmusic_fluid_chorus_speed, ZMUSIC_VAR_FLOAT, 0.3f}, + {"zmusic_fluid_chorus_depth", zmusic_fluid_chorus_depth, ZMUSIC_VAR_FLOAT, 8}, + {"zmusic_fluid_lib", zmusic_fluid_lib, ZMUSIC_VAR_STRING, 0}, +#ifdef HAVE_OPL + {"zmusic_opl_numchips", zmusic_opl_numchips, ZMUSIC_VAR_INT, 2}, + {"zmusic_opl_core", zmusic_opl_core, ZMUSIC_VAR_INT, 0}, + {"zmusic_opl_fullpan", zmusic_opl_fullpan, ZMUSIC_VAR_BOOL, 1}, +#endif +#ifdef HAVE_OPN + {"zmusic_opn_chips_count", zmusic_opn_chips_count, ZMUSIC_VAR_INT, 8}, + {"zmusic_opn_emulator_id", zmusic_opn_emulator_id, ZMUSIC_VAR_INT, 0}, + {"zmusic_opn_run_at_pcm_rate", zmusic_opn_run_at_pcm_rate, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_opn_fullpan", zmusic_opn_fullpan, ZMUSIC_VAR_BOOL, 2}, + {"zmusic_opn_use_custom_bank", zmusic_opn_use_custom_bank, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_opn_custom_bank", zmusic_opn_custom_bank, ZMUSIC_VAR_STRING, 0}, + {"zmusic_opn_gain", zmusic_opn_gain, ZMUSIC_VAR_FLOAT, 1.0f}, +#endif +#ifdef HAVE_GUS + {"zmusic_gus_dmxgus", zmusic_gus_dmxgus, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_gus_midi_voices", zmusic_gus_midi_voices, ZMUSIC_VAR_INT, 32}, + {"zmusic_gus_memsize", zmusic_gus_memsize, ZMUSIC_VAR_INT, 0}, + {"zmusic_gus_config", zmusic_gus_config, ZMUSIC_VAR_STRING, 0}, + {"zmusic_gus_patchdir", zmusic_gus_patchdir, ZMUSIC_VAR_STRING, 0}, +#endif +#ifdef HAVE_TIMIDITY + {"zmusic_timidity_modulation_wheel", zmusic_timidity_modulation_wheel, ZMUSIC_VAR_BOOL, 1}, + {"zmusic_timidity_portamento", zmusic_timidity_portamento, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_timidity_reverb", zmusic_timidity_reverb, ZMUSIC_VAR_INT, 0}, + {"zmusic_timidity_reverb_level", zmusic_timidity_reverb_level, ZMUSIC_VAR_INT, 0}, + {"zmusic_timidity_chorus", zmusic_timidity_chorus, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_timidity_surround_chorus", zmusic_timidity_surround_chorus, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_timidity_channel_pressure", zmusic_timidity_channel_pressure, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_timidity_lpf_def", zmusic_timidity_lpf_def, ZMUSIC_VAR_BOOL, 1}, + {"zmusic_timidity_temper_control", zmusic_timidity_temper_control, ZMUSIC_VAR_BOOL, 1}, + {"zmusic_timidity_modulation_envelope", zmusic_timidity_modulation_envelope, ZMUSIC_VAR_BOOL, 1}, + {"zmusic_timidity_overlap_voice_allow", zmusic_timidity_overlap_voice_allow, ZMUSIC_VAR_BOOL, 1}, + {"zmusic_timidity_drum_effect", zmusic_timidity_drum_effect, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_timidity_pan_delay", zmusic_timidity_pan_delay, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_timidity_key_adjust", zmusic_timidity_key_adjust, ZMUSIC_VAR_INT, 0}, + {"zmusic_timidity_drum_power", zmusic_timidity_drum_power, ZMUSIC_VAR_FLOAT, 1}, + {"zmusic_timidity_tempo_adjust", zmusic_timidity_tempo_adjust, ZMUSIC_VAR_FLOAT, 1}, + {"zmusic_timidity_min_sustain_time", zmusic_timidity_min_sustain_time, ZMUSIC_VAR_FLOAT, 5000}, + {"zmusic_timidity_config", zmusic_timidity_config, ZMUSIC_VAR_STRING, 0}, +#endif +#ifdef HAVE_WILDMIDI + {"zmusic_wildmidi_reverb", zmusic_wildmidi_reverb, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_wildmidi_enhanced_resampling", zmusic_wildmidi_enhanced_resampling, ZMUSIC_VAR_BOOL, 1}, + {"zmusic_wildmidi_config", zmusic_wildmidi_config, ZMUSIC_VAR_STRING, 0}, +#endif + {"zmusic_mod_samplerate", zmusic_mod_samplerate, ZMUSIC_VAR_INT, 0}, + {"zmusic_mod_volramp", zmusic_mod_volramp, ZMUSIC_VAR_INT, 2}, + {"zmusic_mod_interp", zmusic_mod_interp, ZMUSIC_VAR_BOOL, 2}, + {"zmusic_mod_autochip", zmusic_mod_autochip, ZMUSIC_VAR_BOOL, 0}, + {"zmusic_mod_autochip_size_force", zmusic_mod_autochip_size_force, ZMUSIC_VAR_INT, 100}, + {"zmusic_mod_autochip_size_scan", zmusic_mod_autochip_size_scan, ZMUSIC_VAR_INT, 500}, + {"zmusic_mod_autochip_scan_threshold", zmusic_mod_autochip_scan_threshold, ZMUSIC_VAR_INT, 12}, + {"zmusic_mod_preferred_player", zmusic_mod_preferredplayer, ZMUSIC_VAR_INT, 0}, + {"zmusic_mod_dumb_mastervolume", zmusic_mod_dumb_mastervolume, ZMUSIC_VAR_FLOAT, 1}, + + {"zmusic_gme_stereodepth", zmusic_gme_stereodepth, ZMUSIC_VAR_FLOAT, 0}, + + {"zmusic_snd_midiprecache", zmusic_snd_midiprecache, ZMUSIC_VAR_BOOL, 1}, + {"zmusic_snd_streambuffersize", zmusic_snd_streambuffersize, ZMUSIC_VAR_INT, 64}, + {"zmusic_snd_mididevice", zmusic_snd_mididevice, ZMUSIC_VAR_INT, 0}, + {"zmusic_snd_outputrate", zmusic_snd_outputrate, ZMUSIC_VAR_INT, 44100}, + {"zmusic_snd_musicvolume", zmusic_snd_musicvolume, ZMUSIC_VAR_FLOAT, 1}, + {"zmusic_relative_volume", zmusic_relative_volume, ZMUSIC_VAR_FLOAT, 1}, + {"zmusic_snd_mastervolume", zmusic_snd_mastervolume, ZMUSIC_VAR_FLOAT, 1}, + {} +}; + +DLL_EXPORT const ZMusicConfigurationSetting* ZMusic_GetConfiguration() +{ + return config; +} diff --git a/source/zmusic/critsec.cpp b/source/zmusic/critsec.cpp new file mode 100644 index 000000000..24160eff9 --- /dev/null +++ b/source/zmusic/critsec.cpp @@ -0,0 +1,163 @@ +/* +** +** +**--------------------------------------------------------------------------- +** Copyright 2005-2016 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ +// Brought back for ZMusic because std::mutex under Windows pulls in the +// ENTIRE multithreading library, all combined more than 200kb object code! + +#ifdef _WIN32 + +#ifndef _WINNT_ +#define WIN32_LEAN_AND_MEAN +#include +#endif + +class FInternalCriticalSection +{ +public: + FInternalCriticalSection() + { + InitializeCriticalSection(&CritSec); + } + ~FInternalCriticalSection() + { + DeleteCriticalSection(&CritSec); + } + void Enter() + { + EnterCriticalSection(&CritSec); + } + void Leave() + { + LeaveCriticalSection(&CritSec); + } +#if 0 + // SDL has no equivalent functionality, so better not use it on Windows. + bool TryEnter() + { + return TryEnterCriticalSection(&CritSec) != 0; + } +#endif +private: + CRITICAL_SECTION CritSec; +}; + + +FInternalCriticalSection *CreateCriticalSection() +{ + return new FInternalCriticalSection(); +} + +void DeleteCriticalSection(FInternalCriticalSection *c) +{ + delete c; +} + +void EnterCriticalSection(FInternalCriticalSection *c) +{ + c->Enter(); +} + +void LeaveCriticalSection(FInternalCriticalSection *c) +{ + c->Leave(); +} + +#else + +#include "critsec.h" + +#include + +class FInternalCriticalSection +{ +public: + FInternalCriticalSection(); + ~FInternalCriticalSection(); + + void Enter(); + void Leave(); + +private: + pthread_mutex_t m_mutex; + +}; + +// TODO: add error handling + +FInternalCriticalSection::FInternalCriticalSection() +{ + pthread_mutexattr_t attributes; + pthread_mutexattr_init(&attributes); + pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init(&m_mutex, &attributes); + + pthread_mutexattr_destroy(&attributes); +} + +FInternalCriticalSection::~FInternalCriticalSection() +{ + pthread_mutex_destroy(&m_mutex); +} + +void FInternalCriticalSection::Enter() +{ + pthread_mutex_lock(&m_mutex); +} + +void FInternalCriticalSection::Leave() +{ + pthread_mutex_unlock(&m_mutex); +} + + +FInternalCriticalSection *CreateCriticalSection() +{ + return new FInternalCriticalSection(); +} + +void DeleteCriticalSection(FInternalCriticalSection *c) +{ + delete c; +} + +void EnterCriticalSection(FInternalCriticalSection *c) +{ + c->Enter(); +} + +void LeaveCriticalSection(FInternalCriticalSection *c) +{ + c->Leave(); +} + +#endif \ No newline at end of file diff --git a/source/zmusic/critsec.h b/source/zmusic/critsec.h new file mode 100644 index 000000000..7be3afd90 --- /dev/null +++ b/source/zmusic/critsec.h @@ -0,0 +1,37 @@ +#pragma once + +// System independent critical sections without polluting the namespace with the operating system headers. +class FInternalCriticalSection; +FInternalCriticalSection *CreateCriticalSection(); +void DeleteCriticalSection(FInternalCriticalSection *c); +void EnterCriticalSection(FInternalCriticalSection *c); +void LeaveCriticalSection(FInternalCriticalSection *c); + +// This is just a convenience wrapper around the function interface adjusted to use std::lock_guard +class FCriticalSection +{ +public: + FCriticalSection() + { + c = CreateCriticalSection(); + } + + ~FCriticalSection() + { + DeleteCriticalSection(c); + } + + void lock() + { + EnterCriticalSection(c); + } + + void unlock() + { + LeaveCriticalSection(c); + } + +private: + FInternalCriticalSection *c; + +}; diff --git a/source/zmusic/fileio.h b/source/zmusic/fileio.h new file mode 100644 index 000000000..f2ea1b7d6 --- /dev/null +++ b/source/zmusic/fileio.h @@ -0,0 +1,389 @@ +/* + The common sound font reader interface. Used by GUS, Timidity++ and WildMidi + backends for reading sound font configurations. + + The FileInterface is also used by streaming sound formats. + + Copyright (C) 2019 Christoph Oelckers + + 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 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#pragma once +#include +#include +#include +#include +#include + +#if defined _WIN32 && !defined _WINDOWS_ // only define this if windows.h is not included. + // I'd rather not include Windows.h for just this. This header is not supposed to pollute everything it touches. +extern "C" __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned CodePage, unsigned long dwFlags, const char* lpMultiByteStr, int cbMultiByte, const wchar_t* lpWideCharStr, int cchWideChar); +enum +{ + CP_UTF8 = 65001 +}; +#endif + +namespace MusicIO +{ + +//========================================================================== +// +// This class defines a common file wrapper interface which allows these +// libraries to work with any kind of file access API, e.g. stdio (provided below), +// Win32, POSIX, iostream or custom implementations (like GZDoom's FileReader.) +// +//========================================================================== + +struct FileInterface +{ + std::string filename; + long length = -1; + + // It's really too bad that the using code requires long instead of size_t. + // Fortunately 2GB files are unlikely to come by here. +protected: + // + virtual ~FileInterface() {} +public: + virtual char* gets(char* buff, int n) = 0; + virtual long read(void* buff, int32_t size) = 0; + virtual long seek(long offset, int whence) = 0; + virtual long tell() = 0; + virtual void close() + { + delete this; + } + + long filelength() + { + if (length == -1) + { + long pos = tell(); + seek(0, SEEK_END); + length = tell(); + seek(pos, SEEK_SET); + } + return length; + } +}; + +//========================================================================== +// +// Inplementation of the FileInterface for stdio's FILE*. +// +//========================================================================== + +struct StdioFileReader : public FileInterface +{ + FILE* f = nullptr; + + ~StdioFileReader() + { + if (f) fclose(f); + } + char* gets(char* buff, int n) override + { + if (!f) return nullptr; + return fgets(buff, n, f); + } + long read(void* buff, int32_t size) override + { + if (!f) return 0; + return (long)fread(buff, 1, size, f); + } + long seek(long offset, int whence) override + { + if (!f) return 0; + return fseek(f, offset, whence); + } + long tell() override + { + if (!f) return 0; + return ftell(f); + } +}; + + +//========================================================================== +// +// Inplementation of the FileInterface for a block of memory +// +//========================================================================== + +struct MemoryReader : public FileInterface +{ + const uint8_t *mData; + long mLength; + long mPos; + + MemoryReader(const uint8_t *data, long length) + : mData(data), mLength(length), mPos(0) + { + } + + char* gets(char* strbuf, int len) override + { + if (len > mLength - mPos) len = mLength - mPos; + if (len <= 0) return NULL; + + char *p = strbuf; + while (len > 1) + { + if (mData[mPos] == 0) + { + mPos++; + break; + } + if (mData[mPos] != '\r') + { + *p++ = mData[mPos]; + len--; + if (mData[mPos] == '\n') + { + mPos++; + break; + } + } + mPos++; + } + if (p == strbuf) return nullptr; + *p++ = 0; + return strbuf; + } + long read(void* buff, int32_t size) override + { + long len = long(size); + if (len > mLength - mPos) len = mLength - mPos; + if (len < 0) len = 0; + memcpy(buff, mData + mPos, len); + mPos += len; + return len; + } + long seek(long offset, int whence) override + { + switch (whence) + { + case SEEK_CUR: + offset += mPos; + break; + + case SEEK_END: + offset += mLength; + break; + + } + if (offset < 0 || offset > mLength) return -1; + mPos = offset; + return 0; + } + long tell() override + { + return mPos; + } +protected: + MemoryReader() {} +}; + +//========================================================================== +// +// Inplementation of the FileInterface for an std::vector owned by the reader +// +//========================================================================== + +struct VectorReader : public MemoryReader +{ + std::vector mVector; + + template + VectorReader(getFunc getter) // read contents to a buffer and return a reader to it + { + getter(mVector); + mData = mVector.data(); + mLength = (long)mVector.size(); + mPos = 0; + } + VectorReader(const uint8_t* data, size_t size) + { + mVector.resize(size); + memcpy(mVector.data(), data, size); + mData = mVector.data(); + mLength = (long)size; + mPos = 0; + } +}; + + +//========================================================================== +// +// The following two functions are needed to allow using UTF-8 in the file interface. +// fopen on Windows is only safe for ASCII. +// +//========================================================================== + +#ifdef _WIN32 +inline std::wstring wideString(const char *filename) +{ + std::wstring filePathW; + auto len = strlen(filename); + filePathW.resize(len); + int newSize = MultiByteToWideChar(CP_UTF8, 0, filename, (int)len, const_cast(filePathW.c_str()), (int)len); + filePathW.resize(newSize); + return filePathW; +} +#endif + +inline FILE* utf8_fopen(const char* filename, const char *mode) +{ +#ifndef _WIN32 + return fopen(filename, mode); +#else + auto fn = wideString(filename); + auto mo = wideString(mode); + return _wfopen(fn.c_str(), mo.c_str()); +#endif + +} + +inline bool fileExists(const char *fn) +{ + FILE *f = utf8_fopen(fn, "rb"); + if (!f) return false; + fclose(f); + return true; +} + +//========================================================================== +// +// This class providea a framework for reading sound fonts. +// This is needed when the sound font data is not read from +// the file system. e.g. zipped GUS patch sets. +// +//========================================================================== + +class SoundFontReaderInterface +{ +protected: + virtual ~SoundFontReaderInterface() {} +public: + virtual struct FileInterface* open_file(const char* fn) = 0; + virtual void add_search_path(const char* path) = 0; + virtual void close() { delete this; } +}; + + +//========================================================================== +// +// A basic sound font reader for reading data from the file system. +// +//========================================================================== + +class FileSystemSoundFontReader : public SoundFontReaderInterface +{ +protected: + std::vector mPaths; + std::string mBaseFile; + bool mAllowAbsolutePaths; + + bool IsAbsPath(const char *name) + { + if (name[0] == '/' || name[0] == '\\') return true; + #ifdef _WIN32 + /* [A-Za-z]: (for Windows) */ + if (isalpha(name[0]) && name[1] == ':') return true; + #endif /* _WIN32 */ + return 0; + } + +public: + FileSystemSoundFontReader(const char *configfilename, bool allowabs = false) + { + // Note that this does not add the directory the base file is in to the search path! + // The caller of this has to do it themselves! + mBaseFile = configfilename; + mAllowAbsolutePaths = allowabs; + } + + struct FileInterface* open_file(const char* fn) override + { + FILE *f = nullptr; + std::string fullname; + if (!fn) + { + f = utf8_fopen(mBaseFile.c_str(), "rb"); + fullname = mBaseFile; + } + else + { + if (!IsAbsPath(fn)) + { + for(int i = (int)mPaths.size()-1; i>=0; i--) + { + fullname = mPaths[i] + fn; + f = utf8_fopen(fullname.c_str(), "rb"); + if (f) break; + } + } + if (!f) f = fopen(fn, "rb"); + } + if (!f) return nullptr; + auto tf = new StdioFileReader; + tf->f = f; + tf->filename = fullname; + return tf; + } + + void add_search_path(const char* path) override + { + std::string p = path; + if (p.back() != '/' && p.back() != '\\') p += '/'; // always let it end with a slash. + mPaths.push_back(p); + } +}; + +//========================================================================== +// +// This reader exists to trick Timidity config readers into accepting +// a loose SF2 file by providing a fake config pointing to the given file. +// +//========================================================================== + +class SF2Reader : public FileSystemSoundFontReader +{ + std::string mMainConfigForSF2; + +public: + SF2Reader(const char *filename) + : FileSystemSoundFontReader(filename) + { + mMainConfigForSF2 = "soundfont \"" + mBaseFile + "\"\n"; + } + + struct FileInterface* open_file(const char* fn) override + { + if (fn == nullptr) + { + return new MemoryReader((uint8_t*)mMainConfigForSF2.c_str(), (long)mMainConfigForSF2.length()); + } + else return FileSystemSoundFontReader::open_file(fn); + } +}; + +MusicIO::SoundFontReaderInterface* ClientOpenSoundFont(const char* name, int type); + +} + diff --git a/source/zmusic/m_swap.h b/source/zmusic/m_swap.h new file mode 100644 index 000000000..de9b7780a --- /dev/null +++ b/source/zmusic/m_swap.h @@ -0,0 +1,255 @@ +// +// DESCRIPTION: +// Endianess handling, swapping 16bit and 32bit. +// +//----------------------------------------------------------------------------- + + +#ifndef __M_SWAP_H__ +#define __M_SWAP_H__ + +#include + +// Endianess handling. +// WAD files are stored little endian. + +#ifdef __APPLE__ +#include + +inline short LittleShort(short x) +{ + return (short)OSSwapLittleToHostInt16((uint16_t)x); +} + +inline unsigned short LittleShort(unsigned short x) +{ + return OSSwapLittleToHostInt16(x); +} + +inline short LittleShort(int x) +{ + return OSSwapLittleToHostInt16((uint16_t)x); +} + +inline unsigned short LittleShort(unsigned int x) +{ + return OSSwapLittleToHostInt16((uint16_t)x); +} + +inline int LittleLong(int x) +{ + return OSSwapLittleToHostInt32((uint32_t)x); +} + +inline unsigned int LittleLong(unsigned int x) +{ + return OSSwapLittleToHostInt32(x); +} + +inline short BigShort(short x) +{ + return (short)OSSwapBigToHostInt16((uint16_t)x); +} + +inline unsigned short BigShort(unsigned short x) +{ + return OSSwapBigToHostInt16(x); +} + +inline int BigLong(int x) +{ + return OSSwapBigToHostInt32((uint32_t)x); +} + +inline unsigned int BigLong(unsigned int x) +{ + return OSSwapBigToHostInt32(x); +} + +#elif defined __BIG_ENDIAN__ + +// Swap 16bit, that is, MSB and LSB byte. +// No masking with 0xFF should be necessary. +inline short LittleShort (short x) +{ + return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8)); +} + +inline unsigned short LittleShort (unsigned short x) +{ + return (unsigned short)((x>>8) | (x<<8)); +} + +inline short LittleShort (int x) +{ + return LittleShort((short)x); +} + +inline unsigned short LittleShort (unsigned int x) +{ + return LittleShort((unsigned short)x); +} + +// Swapping 32bit. +inline unsigned int LittleLong (unsigned int x) +{ + return (unsigned int)( + (x>>24) + | ((x>>8) & 0xff00) + | ((x<<8) & 0xff0000) + | (x<<24)); +} + +inline int LittleLong (int x) +{ + return (int)( + (((unsigned int)x)>>24) + | ((((unsigned int)x)>>8) & 0xff00) + | ((((unsigned int)x)<<8) & 0xff0000) + | (((unsigned int)x)<<24)); +} + +inline short BigShort(short x) +{ + return x; +} + +inline unsigned short BigShort(unsigned short x) +{ + return x; +} + +inline unsigned int BigLong(unsigned int x) +{ + return x; +} + +inline int BigLong(int x) +{ + return x; +} + +#else + +inline short LittleShort(short x) +{ + return x; +} + +inline unsigned short LittleShort(unsigned short x) +{ + return x; +} + +inline unsigned int LittleLong(unsigned int x) +{ + return x; +} + +inline int LittleLong(int x) +{ + return x; +} + +#ifdef _MSC_VER + +inline short BigShort(short x) +{ + return (short)_byteswap_ushort((unsigned short)x); +} + +inline unsigned short BigShort(unsigned short x) +{ + return _byteswap_ushort(x); +} + +inline int BigLong(int x) +{ + return (int)_byteswap_ulong((unsigned long)x); +} + +inline unsigned int BigLong(unsigned int x) +{ + return (unsigned int)_byteswap_ulong((unsigned long)x); +} +#pragma warning (default: 4035) + +#else + +inline short BigShort (short x) +{ + return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8)); +} + +inline unsigned short BigShort (unsigned short x) +{ + return (unsigned short)((x>>8) | (x<<8)); +} + +inline unsigned int BigLong (unsigned int x) +{ + return (unsigned int)( + (x>>24) + | ((x>>8) & 0xff00) + | ((x<<8) & 0xff0000) + | (x<<24)); +} + +inline int BigLong (int x) +{ + return (int)( + (((unsigned int)x)>>24) + | ((((unsigned int)x)>>8) & 0xff00) + | ((((unsigned int)x)<<8) & 0xff0000) + | (((unsigned int)x)<<24)); +} + +#endif + +#endif // __BIG_ENDIAN__ + +// These may be destructive so they should create errors +unsigned long BigLong(unsigned long) = delete; +long BigLong(long) = delete; +unsigned long LittleLong(unsigned long) = delete; +long LittleLong(long) = delete; + + +// Data accessors, since some data is highly likely to be unaligned. +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) +inline int GetShort(const unsigned char *foo) +{ + return *(const short *)foo; +} +inline int GetInt(const unsigned char *foo) +{ + return *(const int *)foo; +} +#else +inline int GetShort(const unsigned char *foo) +{ + return short(foo[0] | (foo[1] << 8)); +} +inline int GetInt(const unsigned char *foo) +{ + return int(foo[0] | (foo[1] << 8) | (foo[2] << 16) | (foo[3] << 24)); +} +#endif +inline int GetBigInt(const unsigned char *foo) +{ + return int((foo[0] << 24) | (foo[1] << 16) | (foo[2] << 8) | foo[3]); +} + +#ifdef __BIG_ENDIAN__ +inline int GetNativeInt(const unsigned char *foo) +{ + return GetBigInt(foo); +} +#else +inline int GetNativeInt(const unsigned char *foo) +{ + return GetInt(foo); +} +#endif + +#endif // __M_SWAP_H__ diff --git a/source/zmusic/midiconfig.h b/source/zmusic/midiconfig.h new file mode 100644 index 000000000..5ddfe92ed --- /dev/null +++ b/source/zmusic/midiconfig.h @@ -0,0 +1,169 @@ +#pragma once + +#include +#include +#include +#include "zmusic_internal.h" +#include "fileio.h" + +// Note: Bools here are stored as ints to allow having a simpler interface. + +struct ADLConfig +{ + int adl_chips_count = 6; + int adl_emulator_id = 0; + int adl_bank = 14; + int adl_volume_model = 0; // Automatical volume model (by bank properties) + int adl_chan_alloc = -1; // Automatical channel allocation mode + int adl_run_at_pcm_rate = 0; + int adl_fullpan = 1; + int adl_use_custom_bank = false; + int adl_auto_arpeggio = false; + float adl_gain = 1.0f; + std::string adl_custom_bank; + int adl_use_genmidi = false; + int adl_genmidi_set = false; + uint8_t adl_genmidi_bank[36 * 175]; // it really is 'struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL]'; but since this is a public header it cannot pull in a dependency from oplsynth. +}; + +struct FluidConfig +{ + std::string fluid_lib; + std::string fluid_patchset; + int fluid_reverb = false; + int fluid_chorus = false; + int fluid_voices = 128; + int fluid_interp = 1; + int fluid_samplerate = 0; + int fluid_threads = 1; + int fluid_chorus_voices = 3; + int fluid_chorus_type = 0; + float fluid_gain = 0.5f; + float fluid_reverb_roomsize = 0.61f; + float fluid_reverb_damping = 0.23f; + float fluid_reverb_width = 0.76f; + float fluid_reverb_level = 0.57f; + float fluid_chorus_level = 1.2f; + float fluid_chorus_speed = 0.3f; + float fluid_chorus_depth = 8; +}; + +struct OPLConfig +{ + int numchips = 2; + int core = 0; + int fullpan = true; + int genmidiset = false; + uint8_t OPLinstruments[36 * 175]; // it really is 'struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL]'; but since this is a public header it cannot pull in a dependency from oplsynth. + float gain = 1.0f; +}; + +struct OpnConfig +{ + int opn_chips_count = 8; + int opn_emulator_id = 0; + int opn_volume_model = 0; // Automatical volume model (by bank properties) + int opn_chan_alloc = -1; // Automatical channel allocation mode + int opn_run_at_pcm_rate = false; + int opn_fullpan = 1; + int opn_use_custom_bank = false; + int opn_auto_arpeggio = false; + float opn_gain = 1.0f; + std::string opn_custom_bank; + std::vector default_bank; +}; + +namespace Timidity +{ + class Instruments; + class SoundFontReaderInterface; +} + +struct GUSConfig +{ + int midi_voices = 32; + int gus_memsize = 0; + int gus_dmxgus = false; + std::string gus_patchdir; + std::string gus_config; + std::vector dmxgus; // can contain the contents of a DMXGUS lump that may be used as the instrument set. In this case gus_patchdir must point to the location of the GUS data and gus_dmxgus must be true. + + // This is the instrument cache for the GUS synth. + MusicIO::SoundFontReaderInterface *reader; + std::string readerName; + std::string loadedConfig; + std::unique_ptr instruments; +}; + +namespace TimidityPlus +{ + class Instruments; + class SoundFontReaderInterface; +} + +struct TimidityConfig +{ + std::string timidity_config; + + MusicIO::SoundFontReaderInterface* reader; + std::string readerName; + std::string loadedConfig; + std::shared_ptr instruments; // this is held both by the config and the device + +}; + +namespace WildMidi +{ + struct Instruments; + class SoundFontReaderInterface; +} + +struct WildMidiConfig +{ + bool reverb = false; + bool enhanced_resampling = true; + std::string config; + + MusicIO::SoundFontReaderInterface* reader; + std::string readerName; + std::string loadedConfig; + std::shared_ptr instruments; // this is held both by the config and the device + +}; + +struct DumbConfig +{ + int mod_samplerate; + int mod_volramp = 2; + int mod_interp = 2; + int mod_autochip; + int mod_autochip_size_force = 100; + int mod_autochip_size_scan = 500; + int mod_autochip_scan_threshold = 12; + int mod_preferred_player = 0; + float mod_dumb_mastervolume = 1; +}; + +struct MiscConfig +{ + int snd_midiprecache; + float gme_stereodepth; + int snd_streambuffersize = 64; + int snd_mididevice; + int snd_outputrate = 44100; + float snd_musicvolume = 1.f; + float relative_volume = 1.f; + float snd_mastervolume = 1.f; +}; + +extern ADLConfig adlConfig; +extern FluidConfig fluidConfig; +extern OPLConfig oplConfig; +extern OpnConfig opnConfig; +extern GUSConfig gusConfig; +extern TimidityConfig timidityConfig; +extern WildMidiConfig wildMidiConfig; +extern DumbConfig dumbConfig; +extern MiscConfig miscConfig; +extern ZMusicCallbacks musicCallbacks; + diff --git a/source/zmusic/mididefs.h b/source/zmusic/mididefs.h new file mode 100644 index 000000000..29cda0d20 --- /dev/null +++ b/source/zmusic/mididefs.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +enum +{ + MAX_MIDI_EVENTS = 128 +}; + +inline constexpr uint8_t MEVENT_EVENTTYPE(uint32_t x) { return ((uint8_t)((x) >> 24)); } +inline constexpr uint32_t MEVENT_EVENTPARM(uint32_t x) { return ((x) & 0xffffff); } + +enum EMidiEvent : uint8_t +{ + MEVENT_TEMPO = 1, + MEVENT_NOP = 2, + MEVENT_LONGMSG = 128, +}; + +#ifndef MAKE_ID +#ifndef __BIG_ENDIAN__ +#define MAKE_ID(a,b,c,d) ((uint32_t)((a)|((b)<<8)|((c)<<16)|((d)<<24))) +#else +#define MAKE_ID(a,b,c,d) ((uint32_t)((d)|((c)<<8)|((b)<<16)|((a)<<24))) +#endif +#endif + diff --git a/source/zmusic/mus2midi.h b/source/zmusic/mus2midi.h new file mode 100644 index 000000000..d083fe8c3 --- /dev/null +++ b/source/zmusic/mus2midi.h @@ -0,0 +1,78 @@ +/* +** mus2midi.h +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 __MUS2MIDI_H__ +#define __MUS2MIDI_H__ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +#define MIDI_SYSEX ((uint8_t)0xF0) // SysEx begin +#define MIDI_SYSEXEND ((uint8_t)0xF7) // SysEx end +#define MIDI_META ((uint8_t)0xFF) // Meta event begin +#define MIDI_META_TEMPO ((uint8_t)0x51) +#define MIDI_META_EOT ((uint8_t)0x2F) // End-of-track +#define MIDI_META_SSPEC ((uint8_t)0x7F) // System-specific event + +#define MIDI_NOTEOFF ((uint8_t)0x80) // + note + velocity +#define MIDI_NOTEON ((uint8_t)0x90) // + note + velocity +#define MIDI_POLYPRESS ((uint8_t)0xA0) // + pressure (2 bytes) +#define MIDI_CTRLCHANGE ((uint8_t)0xB0) // + ctrlr + value +#define MIDI_PRGMCHANGE ((uint8_t)0xC0) // + new patch +#define MIDI_CHANPRESS ((uint8_t)0xD0) // + pressure (1 byte) +#define MIDI_PITCHBEND ((uint8_t)0xE0) // + pitch bend (2 bytes) + +#define MUS_NOTEOFF ((uint8_t)0x00) +#define MUS_NOTEON ((uint8_t)0x10) +#define MUS_PITCHBEND ((uint8_t)0x20) +#define MUS_SYSEVENT ((uint8_t)0x30) +#define MUS_CTRLCHANGE ((uint8_t)0x40) +#define MUS_SCOREEND ((uint8_t)0x60) + +typedef struct +{ + uint32_t Magic; + uint16_t SongLen; + uint16_t SongStart; + uint16_t NumChans; + uint16_t NumSecondaryChans; + uint16_t NumInstruments; + uint16_t Pad; + // uint16_t UsedInstruments[NumInstruments]; +} MUSHeader; + +#endif //__MUS2MIDI_H__ diff --git a/source/zmusic/musinfo.h b/source/zmusic/musinfo.h new file mode 100644 index 000000000..7387014cc --- /dev/null +++ b/source/zmusic/musinfo.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include "mididefs.h" +#include "zmusic/zmusic_internal.h" +#include "critsec.h" + +// The base music class. Everything is derived from this -------------------- + +class MusInfo +{ +public: + MusInfo() = default; + virtual ~MusInfo() {} + virtual void MusicVolumeChanged() {} // snd_musicvolume changed + virtual void Play (bool looping, int subsong) = 0; + virtual void Pause () = 0; + virtual void Resume () = 0; + virtual void Stop () = 0; + virtual bool IsPlaying () = 0; + virtual bool IsMIDI() const { return false; } + virtual bool IsValid () const = 0; + virtual bool SetPosition(unsigned int ms) { return false; } + virtual bool SetSubsong (int subsong) { return false; } + virtual void Update() {} + virtual int GetDeviceType() const { return MDEV_DEFAULT; } // MDEV_DEFAULT stands in for anything that cannot change playback parameters which needs a restart. + virtual std::string GetStats() { return "No stats available for this song"; } + virtual MusInfo* GetWaveDumper(const char* filename, int rate) { return nullptr; } + virtual void ChangeSettingInt(const char* setting, int value) {} // FluidSynth settings + virtual void ChangeSettingNum(const char* setting, double value) {} // " + virtual void ChangeSettingString(const char* setting, const char* value) {} // " + virtual bool ServiceStream(void *buff, int len) { return false; } + virtual SoundStreamInfoEx GetStreamInfoEx() const = 0; + + enum EState + { + STATE_Stopped, + STATE_Playing, + STATE_Paused + } m_Status = STATE_Stopped; + bool m_Looping = false; + FCriticalSection CritSec; +}; diff --git a/source/zmusic/sounddecoder.h b/source/zmusic/sounddecoder.h new file mode 100644 index 000000000..e0e7d5782 --- /dev/null +++ b/source/zmusic/sounddecoder.h @@ -0,0 +1,28 @@ +#pragma once + +#include "zmusic_internal.h" +#include + +struct SoundDecoder +{ + static SoundDecoder* CreateDecoder(MusicIO::FileInterface* reader); + + virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0; + + virtual size_t read(char *buffer, size_t bytes) = 0; + virtual std::vector readAll(); + virtual bool seek(size_t ms_offset, bool ms, bool mayrestart) = 0; + virtual size_t getSampleOffset() = 0; + virtual size_t getSampleLength() { return 0; } + virtual bool open(MusicIO::FileInterface* reader) = 0; + + SoundDecoder() { } + virtual ~SoundDecoder() { } + +protected: + friend class SoundRenderer; + + // Make non-copyable + SoundDecoder(const SoundDecoder &rhs) = delete; + SoundDecoder& operator=(const SoundDecoder &rhs) = delete; +}; diff --git a/source/zmusic/zmusic.cpp b/source/zmusic/zmusic.cpp new file mode 100644 index 000000000..9924643ab --- /dev/null +++ b/source/zmusic/zmusic.cpp @@ -0,0 +1,549 @@ +/* + ** i_music.cpp + ** Plays music + ** + **--------------------------------------------------------------------------- + ** Copyright 1998-2016 Randy Heit + ** Copyright 2005-2019 Christoph Oelckers + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. 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. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + **--------------------------------------------------------------------------- + ** + */ + +#include +#include +#include +#include +#include "m_swap.h" +#include "zmusic_internal.h" +#include "midiconfig.h" +#include "musinfo.h" +#include "streamsources/streamsource.h" +#include "midisources/midisource.h" +#include "critsec.h" + +#define GZIP_ID1 31 +#define GZIP_ID2 139 +#define GZIP_CM 8 +#define GZIP_ID MAKE_ID(GZIP_ID1,GZIP_ID2,GZIP_CM,0) + +#define GZIP_FTEXT 1 +#define GZIP_FHCRC 2 +#define GZIP_FEXTRA 4 +#define GZIP_FNAME 8 +#define GZIP_FCOMMENT 16 + +class MIDIDevice; +class OPLmusicFile; +class StreamSource; +class MusInfo; + +MusInfo *OpenStreamSong(StreamSource *source); +const char *GME_CheckFormat(uint32_t header); +MusInfo* CDDA_OpenSong(MusicIO::FileInterface* reader); +MusInfo* CD_OpenSong(int track, int id); +MusInfo* CreateMIDIStreamer(MIDISource *source, EMidiDevice devtype, const char* args); + +//========================================================================== +// +// ungzip +// +// VGZ files are compressed with gzip, so we need to uncompress them before +// handing them to GME. +// +//========================================================================== + +static bool ungzip(uint8_t *data, int complen, std::vector &newdata) +{ + const uint8_t *max = data + complen - 8; + const uint8_t *compstart = data + 10; + uint8_t flags = data[3]; + unsigned isize; + z_stream stream; + int err; + + // Find start of compressed data stream + if (flags & GZIP_FEXTRA) + { + compstart += 2 + LittleShort(*(uint16_t *)(data + 10)); + } + if (flags & GZIP_FNAME) + { + while (compstart < max && *compstart != 0) + { + compstart++; + } + } + if (flags & GZIP_FCOMMENT) + { + while (compstart < max && *compstart != 0) + { + compstart++; + } + } + if (flags & GZIP_FHCRC) + { + compstart += 2; + } + if (compstart >= max - 1) + { + return false; + } + + // Decompress + isize = LittleLong(*(uint32_t *)(data + complen - 4)); + newdata.resize(isize); + + stream.next_in = (Bytef *)compstart; + stream.avail_in = (uInt)(max - compstart); + stream.next_out = &newdata[0]; + stream.avail_out = isize; + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit2(&stream, -MAX_WBITS); + if (err != Z_OK) + { + return false; + } + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) + { + inflateEnd(&stream); + return false; + } + err = inflateEnd(&stream); + if (err != Z_OK) + { + return false; + } + return true; +} + + +//========================================================================== +// +// identify a music lump's type and set up a player for it +// +//========================================================================== + +static MusInfo *ZMusic_OpenSongInternal (MusicIO::FileInterface *reader, EMidiDevice device, const char *Args) +{ + MusInfo *info = nullptr; + StreamSource *streamsource = nullptr; + const char *fmt; + uint32_t id[32/4]; + + if(reader->read(id, 32) != 32 || reader->seek(-32, SEEK_CUR) != 0) + { + SetError("Unable to read header"); + reader->close(); + return nullptr; + } + try + { + // Check for gzip compression. Some formats are expected to have players + // that can handle it, so it simplifies things if we make all songs + // gzippable. + if ((id[0] & MAKE_ID(255, 255, 255, 0)) == GZIP_ID) + { + // swap out the reader with one that reads the decompressed content. + auto zreader = new MusicIO::VectorReader([reader](std::vector& array) + { + bool res = false; + auto len = reader->filelength(); + uint8_t* gzipped = new uint8_t[len]; + if (reader->read(gzipped, len) == len) + { + res = ungzip(gzipped, (int)len, array); + } + delete[] gzipped; + }); + reader->close(); + reader = zreader; + + + if (reader->read(id, 32) != 32 || reader->seek(-32, SEEK_CUR) != 0) + { + reader->close(); + return nullptr; + } + } + + EMIDIType miditype = ZMusic_IdentifyMIDIType(id, sizeof(id)); + if (miditype != MIDI_NOTMIDI) + { + std::vector data(reader->filelength()); + if (reader->read(data.data(), (long)data.size()) != (long)data.size()) + { + SetError("Failed to read MIDI data"); + reader->close(); + return nullptr; + } + auto source = ZMusic_CreateMIDISource(data.data(), data.size(), miditype); + if (source == nullptr) + { + reader->close(); + return nullptr; + } + if (!source->isValid()) + { + SetError("Invalid data in MIDI file"); + delete source; + return nullptr; + } + +#ifndef HAVE_SYSTEM_MIDI + // some platforms don't support MDEV_STANDARD so map to MDEV_SNDSYS + if (device == MDEV_STANDARD) + device = MDEV_SNDSYS; +#endif + + info = CreateMIDIStreamer(source, device, Args? Args : ""); + } + + // Check for CDDA "format" + else if ((id[0] == MAKE_ID('R', 'I', 'F', 'F') && id[2] == MAKE_ID('C', 'D', 'D', 'A'))) + { + // This is a CDDA file + info = CDDA_OpenSong(reader); + } + + // Check for various raw OPL formats + else + { +#ifdef HAVE_OPL + if ( + (id[0] == MAKE_ID('R', 'A', 'W', 'A') && id[1] == MAKE_ID('D', 'A', 'T', 'A')) || // Rdos Raw OPL + (id[0] == MAKE_ID('D', 'B', 'R', 'A') && id[1] == MAKE_ID('W', 'O', 'P', 'L')) || // DosBox Raw OPL + (id[0] == MAKE_ID('A', 'D', 'L', 'I') && *((uint8_t*)id + 4) == 'B')) // Martin Fernandez's modified IMF + { + streamsource = OPL_OpenSong(reader, &oplConfig); + + } + else +#endif + if ((id[0] == MAKE_ID('R', 'I', 'F', 'F') && id[2] == MAKE_ID('C', 'D', 'X', 'A'))) + { + streamsource = XA_OpenSong(reader); // this takes over the reader. + reader = nullptr; // We do not own this anymore. + } + // Check for game music + else if ((fmt = GME_CheckFormat(id[0])) != nullptr && fmt[0] != '\0') + { + streamsource = GME_OpenSong(reader, fmt, miscConfig.snd_outputrate); + } + // Check for module formats + else if ((id[0] == MAKE_ID('R', 'I', 'F', 'F') && id[2] == MAKE_ID('D', 'S', 'M', 'F'))) + { + streamsource = MOD_OpenSong(reader, miscConfig.snd_outputrate); + } + else + { + // give the calling app an option to select between XMP and DUMB. + if (dumbConfig.mod_preferred_player != 0) + { + streamsource = MOD_OpenSong(reader, miscConfig.snd_outputrate); + } + if (!streamsource) + { + reader->seek(0, SEEK_SET); + streamsource = XMP_OpenSong(reader, miscConfig.snd_outputrate); + if (!streamsource && dumbConfig.mod_preferred_player == 0) + { + reader->seek(0, SEEK_SET); + streamsource = MOD_OpenSong(reader, miscConfig.snd_outputrate); + } + } + } + if (streamsource == nullptr) + { + streamsource = SndFile_OpenSong(reader); // this only takes over the reader if it succeeds. We need to look out for this. + if (streamsource != nullptr) reader = nullptr; + } + + if (streamsource) + { + info = OpenStreamSong(streamsource); + } + } + + if (!info) + { + // File could not be identified as music. + if (reader) reader->close(); + SetError("Unable to identify as music"); + return nullptr; + } + + if (info && !info->IsValid()) + { + delete info; + SetError("Unable to identify as music"); + info = nullptr; + } + if (reader) reader->close(); + return info; + } + catch (const std::exception &ex) + { + // Make sure the reader is closed if this function abnormally terminates + if (reader) reader->close(); + SetError(ex.what()); + return nullptr; + } +} + +DLL_EXPORT ZMusic_MusicStream ZMusic_OpenSongFile(const char* filename, EMidiDevice device, const char* Args) +{ + auto f = MusicIO::utf8_fopen(filename, "rb"); + if (!f) + { + SetError("File not found"); + return nullptr; + } + auto fr = new MusicIO::StdioFileReader; + fr->f = f; + return ZMusic_OpenSongInternal(fr, device, Args); +} + +DLL_EXPORT ZMusic_MusicStream ZMusic_OpenSongMem(const void* mem, size_t size, EMidiDevice device, const char* Args) +{ + if (!mem || !size) + { + SetError("Invalid data"); + return nullptr; + } + // Data must be copied because it may be used as a streaming source and we cannot guarantee that the client memory stays valid. We also have no means to free it. + auto mr = new MusicIO::VectorReader((uint8_t*)mem, (long)size); + return ZMusic_OpenSongInternal(mr, device, Args); +} + +DLL_EXPORT ZMusic_MusicStream ZMusic_OpenSong(ZMusicCustomReader* reader, EMidiDevice device, const char* Args) +{ + if (!reader) + { + SetError("No reader protocol specified"); + return nullptr; + } + auto cr = new CustomFileReader(reader); // Oh no! We just put another wrapper around the client's wrapper! + return ZMusic_OpenSongInternal(cr, device, Args); +} + + +//========================================================================== +// +// play CD music +// +//========================================================================== + +DLL_EXPORT MusInfo *ZMusic_OpenCDSong (int track, int id) +{ + MusInfo *info = CD_OpenSong (track, id); + + if (info && !info->IsValid ()) + { + delete info; + info = nullptr; + SetError("Unable to open CD Audio"); + } + + return info; +} + +//========================================================================== +// +// streaming callback +// +//========================================================================== + +DLL_EXPORT zmusic_bool ZMusic_FillStream(MusInfo* song, void* buff, int len) +{ + if (song == nullptr) return false; + std::lock_guard lock(song->CritSec); + return song->ServiceStream(buff, len); +} + +//========================================================================== +// +// starts playback +// +//========================================================================== + +DLL_EXPORT zmusic_bool ZMusic_Start(MusInfo *song, int subsong, zmusic_bool loop) +{ + if (!song) return true; // Starting a null song is not an error! It just won't play anything. + try + { + song->Play(loop, subsong); + return true; + } + catch (const std::exception & ex) + { + SetError(ex.what()); + return false; + } +} + +//========================================================================== +// +// Utilities +// +//========================================================================== + +DLL_EXPORT void ZMusic_Pause(MusInfo *song) +{ + if (!song) return; + song->Pause(); +} + +DLL_EXPORT void ZMusic_Resume(MusInfo *song) +{ + if (!song) return; + song->Resume(); +} + +DLL_EXPORT void ZMusic_Update(MusInfo *song) +{ + if (!song) return; + song->Update(); +} + +DLL_EXPORT zmusic_bool ZMusic_IsPlaying(MusInfo *song) +{ + if (!song) return false; + return song->IsPlaying(); +} + +DLL_EXPORT void ZMusic_Stop(MusInfo *song) +{ + if (!song) return; + std::lock_guard lock(song->CritSec); + song->Stop(); +} + +DLL_EXPORT zmusic_bool ZMusic_SetSubsong(MusInfo *song, int subsong) +{ + if (!song) return false; + std::lock_guard lock(song->CritSec); + return song->SetSubsong(subsong); +} + +DLL_EXPORT zmusic_bool ZMusic_IsLooping(MusInfo *song) +{ + if (!song) return false; + return song->m_Looping; +} + +DLL_EXPORT int ZMusic_GetDeviceType(MusInfo* song) +{ + if (!song) return false; + return song->GetDeviceType(); +} + +DLL_EXPORT zmusic_bool ZMusic_IsMIDI(MusInfo *song) +{ + if (!song) return false; + return song->IsMIDI(); +} + +DLL_EXPORT void ZMusic_GetStreamInfo(MusInfo *song, SoundStreamInfo *fmt) +{ + if (!fmt) return; + *fmt = {}; + + if (!song) + return; + + SoundStreamInfoEx fmtex; + { + std::lock_guard lock(song->CritSec); + fmtex = song->GetStreamInfoEx(); + } + if (fmtex.mSampleRate > 0) + { + fmt->mBufferSize = fmtex.mBufferSize; + fmt->mSampleRate = fmtex.mSampleRate; + fmt->mNumChannels = ZMusic_ChannelCount(fmtex.mChannelConfig); + if (fmtex.mSampleType == SampleType_Int16) + fmt->mNumChannels *= -1; + } +} + +DLL_EXPORT void ZMusic_GetStreamInfoEx(MusInfo *song, SoundStreamInfoEx *fmt) +{ + if (!fmt) return; + if (!song) *fmt = {}; + std::lock_guard lock(song->CritSec); + *fmt = song->GetStreamInfoEx(); +} + +DLL_EXPORT void ZMusic_Close(MusInfo *song) +{ + if (!song) return; + delete song; +} + +DLL_EXPORT void ZMusic_VolumeChanged(MusInfo *song) +{ + if (!song) return; + std::lock_guard lock(song->CritSec); + song->MusicVolumeChanged(); +} + +static std::string staticErrorMessage; + +DLL_EXPORT const char *ZMusic_GetStats(MusInfo *song) +{ + if (!song) return ""; + std::lock_guard lock(song->CritSec); + staticErrorMessage = song->GetStats(); + return staticErrorMessage.c_str(); +} + +void SetError(const char* msg) +{ + staticErrorMessage = msg; +} + +DLL_EXPORT const char* ZMusic_GetLastError() +{ + return staticErrorMessage.c_str(); +} + +DLL_EXPORT zmusic_bool ZMusic_WriteSMF(MIDISource* source, const char *fn, int looplimit) +{ + std::vector midi; + bool success; + + if (!source) return false; + source->CreateSMF(midi, 1); + auto f = MusicIO::utf8_fopen(fn, "wt"); + if (f == nullptr) return false; + success = (fwrite(&midi[0], 1, midi.size(), f) == midi.size()); + fclose(f); + return success; +} diff --git a/source/zmusic/zmusic_internal.h b/source/zmusic/zmusic_internal.h new file mode 100644 index 000000000..0de89e93c --- /dev/null +++ b/source/zmusic/zmusic_internal.h @@ -0,0 +1,82 @@ +#pragma once +#define ZMUSIC_INTERNAL + +#if defined(_MSC_VER) && !defined(ZMUSIC_STATIC) +#define DLL_EXPORT __declspec(dllexport) +#define DLL_IMPORT __declspec(dllexport) // without this the compiler complains. +#else +#define DLL_EXPORT +#define DLL_IMPORT +#endif + +typedef class MIDISource *ZMusic_MidiSource; +typedef class MusInfo *ZMusic_MusicStream; + +// Build two configurations - lite and full. +// Lite only uses FluidSynth for MIDI playback and is licensed under the LGPL v2.1 +// Full uses all MIDI synths and is licensed under the GPL v3. + +#ifndef ZMUSIC_LITE +#define HAVE_GUS // legally viable but not really useful +#define HAVE_TIMIDITY // GPL v2.0 +#define HAVE_OPL // GPL v3.0 +#define HAVE_ADL // GPL v3.0 +#define HAVE_OPN // GPL v3.0 +#define HAVE_WILDMIDI // LGPL v3.0 +#endif + +#include "zmusic.h" +#include "fileio.h" + +void SetError(const char *text); + +struct CustomFileReader : public MusicIO::FileInterface +{ + ZMusicCustomReader* cr; + + CustomFileReader(ZMusicCustomReader* zr) : cr(zr) {} + virtual char* gets(char* buff, int n) { return cr->gets(cr, buff, n); } + virtual long read(void* buff, int32_t size) { return cr->read(cr, buff, size); } + virtual long seek(long offset, int whence) { return cr->seek(cr, offset, whence); } + virtual long tell() { return cr->tell(cr); } + virtual void close() + { + cr->close(cr); + delete this; + } + +}; + + +void ZMusic_Printf(int type, const char* msg, ...); + +inline uint8_t ZMusic_SampleTypeSize(SampleType stype) +{ + switch(stype) + { + case SampleType_UInt8: return sizeof(uint8_t); + case SampleType_Int16: return sizeof(int16_t); + case SampleType_Float32: return sizeof(float); + } + return 0; +} + +inline uint8_t ZMusic_ChannelCount(ChannelConfig chans) +{ + switch(chans) + { + case ChannelConfig_Mono: return 1; + case ChannelConfig_Stereo: return 2; + } + return 0; +} + +inline const char *ZMusic_ChannelConfigName(ChannelConfig chans) +{ + switch(chans) + { + case ChannelConfig_Mono: return "Mono"; + case ChannelConfig_Stereo: return "Stereo"; + } + return "(unknown)"; +} diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt new file mode 100644 index 000000000..dd791db7f --- /dev/null +++ b/thirdparty/CMakeLists.txt @@ -0,0 +1,31 @@ +add_subdirectory(miniz) +# GME is not currently released in a way that's conducive to using as a system +# library. Nevertheless at least one person tried, and so the ability to use a +# system copy exists soley to placate people following distro guidelines to the +# letter without regard as to why the vendored copy is forced. +# [Blzut3] Commented out per request from Graf Zahl. +#option(FORCE_INTERNAL_GME "Use internal gme (it is highly unlikely this should be turned off)" ON) +#mark_as_advanced(FORCE_INTERNAL_GME GME_INCLUDE_DIR GME_LIBRARY) +#find_package(GME QUIET) +#if(GME_FOUND AND NOT FORCE_INTERNAL_GME) +# message(STATUS "Using system gme library, includes found at ${GME_INCLUDE_DIRS}") +# set_property(TARGET gme PROPERTY IMPORTED_GLOBAL TRUE) +# determine_package_config_dependency(ZMUSIC_PACKAGE_DEPENDENCIES TARGET gme MODULE GME) +#else() +# message(STATUS "Using internal gme library") + # Use MAME as it's a balanced emulator: well-accurate, but doesn't eats lot of CPU + # Nuked OPN2 is very accurate emulator, but it eats too much CPU for the workflow + set(GME_YM2612_EMU "Nuked" CACHE STRING "Which YM2612 emulator to use: \"Nuked\" (LGPLv2.1+), \"MAME\" (GPLv2+), or \"GENS\" (LGPLv2.1+)") + mark_as_advanced(GME_YM2612_EMU) + add_subdirectory(game-music-emu) +#endif() + +add_subdirectory(dumb) +add_subdirectory(adlmidi) +add_subdirectory(opnmidi) +add_subdirectory(timidity) +add_subdirectory(timidityplus) +add_subdirectory(wildmidi) +add_subdirectory(oplsynth) +add_subdirectory(libxmp) +add_subdirectory(fluidsynth/src) diff --git a/thirdparty/adlmidi/CMakeLists.txt b/thirdparty/adlmidi/CMakeLists.txt new file mode 100644 index 000000000..3c75d418f --- /dev/null +++ b/thirdparty/adlmidi/CMakeLists.txt @@ -0,0 +1,58 @@ +make_release_only() + +add_library(adl OBJECT) + +target_sources(adl +PRIVATE + adlmidi_midiplay.cpp + adlmidi_opl3.cpp + adlmidi_private.cpp + adlmidi.cpp + adlmidi_load.cpp + inst_db.cpp + chips/opal_opl3.cpp + chips/opal/opal.c + chips/dosbox/dbopl.cpp + chips/nuked_opl3_v174.cpp + chips/java_opl3.cpp + chips/dosbox_opl3.cpp + chips/nuked_opl3.cpp + chips/nuked/nukedopl3_174.c + chips/nuked/nukedopl3.c + chips/ym3812_lle.cpp + chips/ym3812_lle/nopl2.c + chips/ym3812_lle/nuked_fmopl2.c + chips/ymf262_lle.cpp + chips/ymf262_lle/nopl3.c + chips/ymf262_lle/nuked_fmopl3.c + chips/esfmu_opl3.cpp + chips/esfmu/esfm.c + chips/esfmu/esfm_registers.c + chips/mame_opl2.cpp + # chips/mame/mame_fmopl.cpp # Duplicate of ../oplsynth/fmopl.cpp + wopl/wopl_file.c +) + +if(COMPILER_SUPPORTS_CXX14) + set(YMFM_SOURCES + chips/ymfm_opl2.cpp + chips/ymfm_opl3.cpp + chips/ymfm/ymfm_adpcm.cpp + chips/ymfm/ymfm_misc.cpp + chips/ymfm/ymfm_opl.cpp + chips/ymfm/ymfm_pcm.cpp + chips/ymfm/ymfm_ssg.cpp + ) + if(DEFINED FLAG_CPP14) + set_source_files_properties(${YMFM_SOURCES} COMPILE_FLAGS ${FLAG_CPP14}) + endif() + target_sources(adl PRIVATE ${YMFM_SOURCES}) +else() + target_compile_definitions(adl PUBLIC -DADLMIDI_DISABLE_YMFM_EMULATOR) +endif() + +target_compile_definitions(adl PRIVATE ADLMIDI_DISABLE_MIDI_SEQUENCER ADLMIDI_ENABLE_OPL2_LLE_EMULATOR ADLMIDI_ENABLE_OPL3_LLE_EMULATOR) + +target_include_directories(adl PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") + +use_fast_math(adl) diff --git a/thirdparty/adlmidi/adlmidi.cpp b/thirdparty/adlmidi/adlmidi.cpp new file mode 100644 index 000000000..b81a34246 --- /dev/null +++ b/thirdparty/adlmidi/adlmidi.cpp @@ -0,0 +1,1926 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_midiplay.hpp" +#include "adlmidi_opl3.hpp" +#include "adlmidi_private.hpp" +#include "chips/opl_chip_base.h" +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER +# include "midi_sequencer.hpp" +#endif +#ifdef ENABLE_HW_OPL_DOS +# include "chips/dos_hw_opl.h" +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 + +#define snprintf c99_snprintf +#define vsnprintf c99_vsnprintf + +__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) +{ + int count = -1; + + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} + +__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...) +{ + int count; + va_list ap; + + va_start(ap, format); + count = c99_vsnprintf(outBuf, size, format, ap); + va_end(ap); + + return count; +} +#endif + +/* Unify MIDI player casting and interface between ADLMIDI and OPNMIDI */ +#define GET_MIDI_PLAYER(device) reinterpret_cast((device)->adl_midiPlayer) +typedef MIDIplay MidiPlayer; + +static ADL_Version adl_version = { + ADLMIDI_VERSION_MAJOR, + ADLMIDI_VERSION_MINOR, + ADLMIDI_VERSION_PATCHLEVEL +}; + +static const ADLMIDI_AudioFormat adl_DefaultAudioFormat = +{ + ADLMIDI_SampleType_S16, + sizeof(int16_t), + 2 * sizeof(int16_t), +}; + +/*---------------------------EXPORTS---------------------------*/ + +ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate) +{ + ADL_MIDIPlayer *midi_device; + midi_device = (ADL_MIDIPlayer *)malloc(sizeof(ADL_MIDIPlayer)); + if(!midi_device) + { + ADLMIDI_ErrorString = "Can't initialize ADLMIDI: out of memory!"; + return NULL; + } + + MIDIplay *player = new(std::nothrow) MIDIplay(static_cast(sample_rate)); + if(!player) + { + free(midi_device); + ADLMIDI_ErrorString = "Can't initialize ADLMIDI: out of memory!"; + return NULL; + } + midi_device->adl_midiPlayer = player; + adlCalculateFourOpChannels(player); + return midi_device; +} + +ADLMIDI_EXPORT void adl_close(struct ADL_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + delete play; + device->adl_midiPlayer = NULL; + free(device); + device = NULL; +} + +ADLMIDI_EXPORT int adl_setDeviceIdentifier(ADL_MIDIPlayer *device, unsigned id) +{ + if(!device || id > 0x0f) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->setDeviceId(static_cast(id)); + return 0; +} + +ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numChips) +{ + if(device == NULL) + return -2; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + +#ifdef ADLMIDI_ENABLE_HW_SERIAL + if(play->m_setup.serial) + numChips = 1; +#endif + +#ifdef ADLMIDI_HW_OPL + play->m_setup.numChips = numChips > 2 ? 1 : static_cast(numChips); +#else + play->m_setup.numChips = static_cast(numChips); +#endif + + if((play->m_setup.numChips < 1) || (play->m_setup.numChips > ADL_MAX_CHIPS)) + { + play->setErrorString("number of chips may only be 1.." ADL_MAX_CHIPS_STR ".\n"); + return -1; + } + + int maxFourOps = static_cast(play->m_setup.numChips * 6); + + if(play->m_setup.numFourOps > maxFourOps) + play->m_setup.numFourOps = maxFourOps; + else if(play->m_setup.numFourOps < -1) + play->m_setup.numFourOps = -1; + + Synth &synth = *play->m_synth; + if(!synth.setupLocked()) + { + synth.m_numChips = play->m_setup.numChips; + if(play->m_setup.numFourOps < 0) + adlCalculateFourOpChannels(play, true); + else + synth.m_numFourOps = static_cast(play->m_setup.numFourOps); + play->partialReset(); + return 0; + } + + return 0; +} + +ADLMIDI_EXPORT int adl_getNumChips(struct ADL_MIDIPlayer *device) +{ + if(device == NULL) + return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_setup.numChips; +} + +ADLMIDI_EXPORT int adl_getNumChipsObtained(struct ADL_MIDIPlayer *device) +{ + if(device == NULL) + return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_synth->m_numChips; +} + +ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) +{ +#ifdef DISABLE_EMBEDDED_BANKS + ADL_UNUSED(bank); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->setErrorString("This build of libADLMIDI has no embedded banks. " + "Please load banks by using adl_openBankFile() or " + "adl_openBankData() functions instead of adl_setBank()."); + return -1; +#else + const uint32_t NumBanks = static_cast(g_embeddedBanksCount); + int32_t bankno = bank; + + if(bankno < 0) + bankno = 0; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + if(static_cast(bankno) >= NumBanks) + { + char errBuf[150]; + snprintf(errBuf, 150, "Embedded bank number may only be 0..%u!\n", static_cast(NumBanks - 1)); + play->setErrorString(errBuf); + return -1; + } + + Synth &synth = *play->m_synth; + play->m_setup.bankId = static_cast(bankno); + synth.setEmbeddedBank(play->m_setup.bankId); + play->applySetup(); + + return 0; +#endif +} + +ADLMIDI_EXPORT int adl_getBanksCount() +{ +#ifndef DISABLE_EMBEDDED_BANKS + return static_cast(g_embeddedBanksCount); +#else + return 0; +#endif +} + +ADLMIDI_EXPORT const char *const *adl_getBankNames() +{ +#ifndef DISABLE_EMBEDDED_BANKS + return g_embeddedBankNames; +#else + return NULL; +#endif +} + +ADLMIDI_EXPORT int adl_reserveBanks(ADL_MIDIPlayer *device, unsigned banks) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth::BankMap &map = play->m_synth->m_insBanks; + map.reserve(banks); + return (int)map.capacity(); +} + +ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, int flags, ADL_Bank *bank) +{ + if(!device || !idp || !bank) + return -1; + + ADL_BankId id = *idp; + if(id.lsb > 127 || id.msb > 127 || id.percussive > 1) + return -1; + size_t idnumber = ((id.msb << 8) | id.lsb | (id.percussive ? size_t(Synth::PercussionTag) : 0)); + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth::BankMap &map = play->m_synth->m_insBanks; + + Synth::BankMap::iterator it; + if(!(flags & ADLMIDI_Bank_Create)) + { + it = map.find(idnumber); + if(it == map.end()) + return -1; + } + else + { + std::pair value; + value.first = idnumber; + memset(&value.second, 0, sizeof(value.second)); + for (unsigned i = 0; i < 128; ++i) + value.second.ins[i].flags = OplInstMeta::Flag_NoSound; + + std::pair ir; + if((flags & ADLMIDI_Bank_CreateRt) == ADLMIDI_Bank_CreateRt) + { + ir = map.insert(value, Synth::BankMap::do_not_expand_t()); + if(ir.first == map.end()) + return -1; + } + else + ir = map.insert(value); + it = ir.first; + } + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getBankId(ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id) +{ + if(!device || !bank) + return -1; + + Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer); + Synth::BankMap::key_type idnumber = it->first; + id->msb = (idnumber >> 8) & 127; + id->lsb = idnumber & 127; + id->percussive = (idnumber & Synth::PercussionTag) ? 1 : 0; + return 0; +} + +ADLMIDI_EXPORT int adl_removeBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device || !bank) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth::BankMap &map = play->m_synth->m_insBanks; + Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer); + size_t size = map.size(); + map.erase(it); + return (map.size() != size) ? 0 : -1; +} + +ADLMIDI_EXPORT int adl_getFirstBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth::BankMap &map = play->m_synth->m_insBanks; + + Synth::BankMap::iterator it = map.begin(); + if(it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getNextBank(ADL_MIDIPlayer *device, ADL_Bank *bank) +{ + if(!device) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth::BankMap &map = play->m_synth->m_insBanks; + + Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer); + if(++it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +ADLMIDI_EXPORT int adl_getInstrument(ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return -1; + + Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer); + cvt_FMIns_to_ADLI(*ins, it->second.ins[index]); + ins->version = 0; + return 0; +} + +ADLMIDI_EXPORT int adl_setInstrument(ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return -1; + + if(ins->version != 0) + return -1; + + Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer); + cvt_ADLI_to_FMIns(it->second.ins[index], *ins); + return 0; +} + +ADLMIDI_EXPORT int adl_loadEmbeddedBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank, int num) +{ + if(!device) + return -1; + +#ifdef DISABLE_EMBEDDED_BANKS + ADL_UNUSED(bank); + ADL_UNUSED(num); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->setErrorString("This build of libADLMIDI has no embedded banks. " + "Please load banks by using adl_openBankFile() or " + "adl_openBankData() functions instead of adl_loadEmbeddedBank()."); + return -1; +#else + if(num < 0 || num >= static_cast(g_embeddedBanksCount)) + return -1; + + Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer); + size_t id = it->first; + + const BanksDump::BankEntry &bankEntry = g_embeddedBanks[num]; + + bool ss = (id & Synth::PercussionTag); + const size_t bankID = 0; + +// bank_count_t maxBanks = ss ? bankEntry.banksPercussionCount : bankEntry.banksMelodicCount; + bank_count_t banksOffset = ss ? bankEntry.banksOffsetPercussive : bankEntry.banksOffsetMelodic; + size_t bankIndex = g_embeddedBanksMidiIndex[banksOffset + bankID]; + const BanksDump::MidiBank &bankData = g_embeddedBanksMidi[bankIndex]; + + for (unsigned i = 0; i < 128; ++i) + { + midi_bank_idx_t instIdx = bankData.insts[i]; + if(instIdx < 0) + { + it->second.ins[i].flags = OplInstMeta::Flag_NoSound; + continue; + } + BanksDump::InstrumentEntry instIn = g_embeddedBanksInstruments[instIdx]; + adlFromInstrument(instIn, it->second.ins[i]); + } + return 0; +#endif +} + +ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4) +{ + if(!device) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + if(ops4 > 6 * static_cast(play->m_setup.numChips)) + { + char errBuff[250]; + snprintf(errBuff, 250, "number of four-op channels may only be 0..%u when %u OPL3 cards are used.\n", (6 * (play->m_setup.numChips)), play->m_setup.numChips); + play->setErrorString(errBuff); + return -1; + } + + Synth &synth = *play->m_synth; + play->m_setup.numFourOps = ops4; + if(!synth.setupLocked()) + { + if(play->m_setup.numFourOps < 0) + adlCalculateFourOpChannels(play, true); + else + synth.m_numFourOps = static_cast(play->m_setup.numFourOps); + synth.updateChannelCategories(); + } + + return 0; +} + +ADLMIDI_EXPORT int adl_getNumFourOpsChn(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_setup.numFourOps; +} + +ADLMIDI_EXPORT int adl_getNumFourOpsChnObtained(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_synth->m_numFourOps; +} + +/* !!!DEPRECATED!!! AND !!DUMMIED!! */ +ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod) +{ + ADL_UNUSED(device); + ADL_UNUSED(percmod); +} + +ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro) +{ + if(!device) return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + play->m_setup.deepVibratoMode = hvibro; + if(!synth.setupLocked()) + { + synth.m_deepVibratoMode = play->m_setup.deepVibratoMode < 0 ? + synth.m_insBankSetup.deepVibrato : + (play->m_setup.deepVibratoMode != 0); + synth.commitDeepFlags(); + } +} + +ADLMIDI_EXPORT int adl_getHVibrato(struct ADL_MIDIPlayer *device) +{ + if(!device) return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth->m_deepVibratoMode; +} + +ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo) +{ + if(!device) return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + play->m_setup.deepTremoloMode = htremo; + if(!synth.setupLocked()) + { + synth.m_deepTremoloMode = play->m_setup.deepTremoloMode < 0 ? + synth.m_insBankSetup.deepTremolo : + (play->m_setup.deepTremoloMode != 0); + synth.commitDeepFlags(); + } +} + +ADLMIDI_EXPORT int adl_getHTremolo(struct ADL_MIDIPlayer *device) +{ + if(!device) return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth->m_deepTremoloMode; +} + +ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + play->m_setup.scaleModulators = smod; + if(!synth.setupLocked()) + { + synth.m_scaleModulators = play->m_setup.scaleModulators < 0 ? + synth.m_insBankSetup.scaleModulators : + (play->m_setup.scaleModulators != 0); + } +} + +ADLMIDI_EXPORT void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.fullRangeBrightnessCC74 = (fr_brightness != 0); +} + +ADLMIDI_EXPORT void adl_setAutoArpeggio(ADL_MIDIPlayer *device, int aaEn) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.enableAutoArpeggio = (aaEn != 0); +} + +ADLMIDI_EXPORT int adl_getAutoArpeggio(ADL_MIDIPlayer *device) +{ + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_setup.enableAutoArpeggio ? 1 : 0; +} + +ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer->setLoopEnabled(loopEn != 0); +#else + ADL_UNUSED(device); + ADL_UNUSED(loopEn); +#endif +} + +ADLMIDI_EXPORT void adl_setLoopCount(ADL_MIDIPlayer *device, int loopCount) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer->setLoopsCount(loopCount); +#else + ADL_UNUSED(device); + ADL_UNUSED(loopCount); +#endif +} + +ADLMIDI_EXPORT void adl_setLoopHooksOnly(ADL_MIDIPlayer *device, int loopHooksOnly) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer->setLoopHooksOnly(loopHooksOnly); +#else + ADL_UNUSED(device); + ADL_UNUSED(loopHooksOnly); +#endif +} + +ADLMIDI_EXPORT void adl_setSoftPanEnabled(ADL_MIDIPlayer *device, int softPanEn) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_synth->m_softPanning = (softPanEn != 0); +#ifdef ADLMIDI_ENABLE_HW_SERIAL + if(play->m_setup.serial) // Soft-panning won't work while serial is active + play->m_synth->m_softPanning = false; +#endif +} + +/* !!!DEPRECATED!!! */ +ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + play->m_setup.logarithmicVolumes = (logvol != 0); + if(!synth.setupLocked()) + { + if(play->m_setup.logarithmicVolumes) + synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); + else + synth.setVolumeScaleModel(static_cast(synth.m_volumeScale)); + } +} + +ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + play->m_setup.volumeScaleModel = volumeModel; + if(!synth.setupLocked()) + { + if(play->m_setup.volumeScaleModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model + synth.m_volumeScale = (Synth::VolumesScale)synth.m_insBankSetup.volumeModel; + else + synth.setVolumeScaleModel(static_cast(volumeModel)); + } +} + +ADLMIDI_EXPORT int adl_getVolumeRangeModel(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth->getVolumeScaleModel(); +} + +ADLMIDI_EXPORT void adl_setChannelAllocMode(struct ADL_MIDIPlayer *device, int chanalloc) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + + if(chanalloc < -1 || chanalloc >= ADLMIDI_ChanAlloc_Count) + chanalloc = ADLMIDI_ChanAlloc_AUTO; + + synth.m_channelAlloc = static_cast(chanalloc); +} + +ADLMIDI_EXPORT int adl_getChannelAllocMode(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return static_cast(play->m_synth->m_channelAlloc); +} + +ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadBank(filePath)) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("ADL MIDI: Can't load file"); + return -1; + } + else + return adlCalculateFourOpChannels(play, true); + } + + ADLMIDI_ErrorString = "Can't load file: ADLMIDI is not initialized"; + return -1; +} + +ADLMIDI_EXPORT int adl_openBankData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadBank(mem, static_cast(size))) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("ADL MIDI: Can't load data from memory"); + return -1; + } + else + return adlCalculateFourOpChannels(play, true); + } + + ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized"; + return -1; +} + +ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, const char *filePath) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadMIDI(filePath)) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("ADL MIDI: Can't load file"); + return -1; + } + else return 0; +#else + ADL_UNUSED(filePath); + play->setErrorString("ADLMIDI: MIDI Sequencer is not supported in this build of library!"); + return -1; +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER + } + + ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized"; + return -1; +} + +ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, const void *mem, unsigned long size) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadMIDI(mem, static_cast(size))) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("ADL MIDI: Can't load data from memory"); + return -1; + } + else return 0; +#else + ADL_UNUSED(mem); + ADL_UNUSED(size); + play->setErrorString("ADLMIDI: MIDI Sequencer is not supported in this build of library!"); + return -1; +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER + } + ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized"; + return -1; +} + +ADLMIDI_EXPORT void adl_selectSongNum(struct ADL_MIDIPlayer *device, int songNumber) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer->setSongNum(songNumber); +#else + ADL_UNUSED(device); + ADL_UNUSED(songNumber); +#endif +} + +ADLMIDI_EXPORT int adl_getSongsCount(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getSongsCount(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +ADLMIDI_EXPORT const char *adl_emulatorName() +{ + return ""; +} + +ADLMIDI_EXPORT const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + if(!synth.m_chips.empty()) + return synth.m_chips[0]->emulatorName(); + } + return "Unknown"; +} + +ADLMIDI_EXPORT int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + if(adl_isEmulatorAvailable(emulator)) + { + play->m_setup.emulator = emulator; +#ifdef ADLMIDI_ENABLE_HW_SERIAL + play->m_setup.serial = false; +#endif + play->partialReset(); + return 0; + } + play->setErrorString("OPL3 MIDI: Unknown emulation core!"); + } + return -1; +} + + +ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + play->m_setup.runAtPcmRate = (enabled != 0); + if(!synth.setupLocked()) + play->partialReset(); + return 0; + } + return -1; +} + +ADLMIDI_EXPORT int adl_switchSerialHW(struct ADL_MIDIPlayer *device, + const char *name, + unsigned baud, + unsigned protocol) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); +#ifdef ADLMIDI_ENABLE_HW_SERIAL + play->m_setup.serial = true; + play->m_setup.serialName = std::string(name); + play->m_setup.serialBaud = baud; + play->m_setup.serialProtocol = protocol; + play->partialReset(); + return 0; +#else + (void)name; (void)baud; (void)protocol; + play->setErrorString("ADLMIDI: The hardware serial mode is not enabled in this build"); + return -1; +#endif + } + + return -1; +} + +int adl_switchDOSHW(int chipType, ADL_UInt16 baseAddress) +{ +#ifdef ENABLE_HW_OPL_DOS + if(baseAddress > 0) + DOS_HW_OPL::setOplAddress(baseAddress); + + switch(chipType) + { + case ADLMIDI_DOS_ChipAuto: + break; + + case ADLMIDI_DOS_ChipOPL2: + DOS_HW_OPL::setChipType(OPLChipBase::CHIPTYPE_OPL2); + break; + case ADLMIDI_DOS_ChipOPL3: + DOS_HW_OPL::setChipType(OPLChipBase::CHIPTYPE_OPL3); + break; + } +#else + (void)chipType; + (void)baseAddress; +#endif + return -1; +} + +ADLMIDI_EXPORT const char *adl_linkedLibraryVersion() +{ +#if !defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + return ADLMIDI_VERSION; +#else + return ADLMIDI_VERSION " (HQ)"; +#endif +} + +ADLMIDI_EXPORT const ADL_Version *adl_linkedVersion() +{ + return &adl_version; +} + +ADLMIDI_EXPORT const char *adl_errorString() +{ + return ADLMIDI_ErrorString.c_str(); +} + +ADLMIDI_EXPORT const char *adl_errorInfo(struct ADL_MIDIPlayer *device) +{ + if(!device) + return adl_errorString(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return adl_errorString(); + return play->getErrorString().c_str(); +} + +ADLMIDI_EXPORT void adl_reset(struct ADL_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->partialReset(); + play->resetMIDI(); +} + +ADLMIDI_EXPORT double adl_totalTimeLength(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->timeLength(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +ADLMIDI_EXPORT double adl_loopStartTime(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getLoopStart(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +ADLMIDI_EXPORT double adl_loopEndTime(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getLoopEnd(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +ADLMIDI_EXPORT double adl_positionTell(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->tell(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +ADLMIDI_EXPORT void adl_positionSeek(struct ADL_MIDIPlayer *device, double seconds) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(seconds < 0.0) + return;//Seeking negative position is forbidden! :-P + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); + play->m_setup.delay = play->m_sequencer->seek(seconds, play->m_setup.mindelay); + play->m_setup.carry = 0.0; +#else + ADL_UNUSED(device); + ADL_UNUSED(seconds); +#endif +} + +ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); + play->m_sequencer->rewind(); +#else + ADL_UNUSED(device); +#endif +} + +ADLMIDI_EXPORT void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device || (tempo <= 0.0)) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer->setTempo(tempo); +#else + ADL_UNUSED(device); + ADL_UNUSED(tempo); +#endif +} + + +ADLMIDI_EXPORT int adl_describeChannels(struct ADL_MIDIPlayer *device, char *str, char *attr, size_t size) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->describeChannels(str, attr, size); + return 0; +} + + +ADLMIDI_EXPORT const char *adl_metaMusicTitle(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return ""; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getMusicTitle().c_str(); +#else + ADL_UNUSED(device); + return ""; +#endif +} + + +ADLMIDI_EXPORT const char *adl_metaMusicCopyright(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return ""; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getMusicCopyright().c_str(); +#else + ADL_UNUSED(device); + return ""; +#endif +} + +ADLMIDI_EXPORT size_t adl_metaTrackTitleCount(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getTrackTitles().size(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +ADLMIDI_EXPORT const char *adl_metaTrackTitle(struct ADL_MIDIPlayer *device, size_t index) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return ""; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + const std::vector &titles = play->m_sequencer->getTrackTitles(); + if(index >= titles.size()) + return "INVALID"; + return titles[index].c_str(); +#else + ADL_UNUSED(device); + ADL_UNUSED(index); + return "NOT SUPPORTED"; +#endif +} + + +ADLMIDI_EXPORT size_t adl_metaMarkerCount(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getMarkers().size(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +ADLMIDI_EXPORT Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size_t index) +{ + struct Adl_MarkerEntry marker; + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + { + marker.label = "INVALID"; + marker.pos_time = 0.0; + marker.pos_ticks = 0; + return marker; + } + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + + const std::vector &markers = play->m_sequencer->getMarkers(); + if(index >= markers.size()) + { + marker.label = "INVALID"; + marker.pos_time = 0.0; + marker.pos_ticks = 0; + return marker; + } + + const MidiSequencer::MIDI_MarkerEntry &mk = markers[index]; + marker.label = mk.label.c_str(); + marker.pos_time = mk.pos_time; + marker.pos_ticks = (unsigned long)mk.pos_ticks; +#else + ADL_UNUSED(device); + ADL_UNUSED(index); + marker.label = "NOT SUPPORTED"; + marker.pos_time = 0.0; + marker.pos_ticks = 0; +#endif + return marker; +} + +ADLMIDI_EXPORT void adl_setRawEventHook(struct ADL_MIDIPlayer *device, ADL_RawEventHook rawEventHook, void *userData) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencerInterface->onEvent = rawEventHook; + play->m_sequencerInterface->onEvent_userData = userData; +#else + ADL_UNUSED(device); + ADL_UNUSED(rawEventHook); + ADL_UNUSED(userData); +#endif +} + +/* Set note hook */ +ADLMIDI_EXPORT void adl_setNoteHook(struct ADL_MIDIPlayer *device, ADL_NoteHook noteHook, void *userData) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->hooks.onNote = noteHook; + play->hooks.onNote_userData = userData; +} + +/* Set debug message hook */ +ADLMIDI_EXPORT void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_DebugMessageHook debugMessageHook, void *userData) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->hooks.onDebugMessage = debugMessageHook; + play->hooks.onDebugMessage_userData = userData; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencerInterface->onDebugMessage = debugMessageHook; + play->m_sequencerInterface->onDebugMessage_userData = userData; +#endif +} + +/* Set loop start hook */ +ADLMIDI_EXPORT void adl_setLoopStartHook(struct ADL_MIDIPlayer *device, ADL_LoopPointHook loopStartHook, void *userData) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->hooks.onLoopStart = loopStartHook; + play->hooks.onLoopStart_userData = userData; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencerInterface->onloopStart = loopStartHook; + play->m_sequencerInterface->onloopStart_userData = userData; +#endif +} + +/* Set loop end hook */ +ADLMIDI_EXPORT void adl_setLoopEndHook(struct ADL_MIDIPlayer *device, ADL_LoopPointHook loopEndHook, void *userData) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->hooks.onLoopEnd = loopEndHook; + play->hooks.onLoopEnd_userData = userData; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencerInterface->onloopEnd = loopEndHook; + play->m_sequencerInterface->onloopEnd_userData = userData; +#endif +} + +#ifndef ADLMIDI_HW_OPL + +# ifndef __WATCOMC__ +template +static void CopySamplesRaw(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + (i * sampleOffset)) = src[2 * i]; + *(Dst *)(dstRight + (i * sampleOffset)) = src[(2 * i) + 1]; + } +} + +template +static void CopySamplesTransformed(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset, + Ret(&transform)(int32_t)) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + (i * sampleOffset)) = static_cast(transform(src[2 * i])); + *(Dst *)(dstRight + (i * sampleOffset)) = static_cast(transform(src[(2 * i) + 1])); + } +} + +static int SendStereoAudio(int samples_requested, + ssize_t in_size, + int32_t *_in, + ssize_t out_pos, + ADL_UInt8 *left, + ADL_UInt8 *right, + const ADLMIDI_AudioFormat *format) +{ + if(!in_size) + return 0; + size_t outputOffset = static_cast(out_pos); + size_t inSamples = static_cast(in_size * 2); + size_t maxSamples = static_cast(samples_requested) - outputOffset; + size_t toCopy = std::min(maxSamples, inSamples); + + ADLMIDI_SampleType sampleType = format->type; + const unsigned containerSize = format->containerSize; + const unsigned sampleOffset = format->sampleOffset; + + left += (outputOffset / 2) * sampleOffset; + right += (outputOffset / 2) * sampleOffset; + + typedef int32_t(&pfnConvert)(int32_t); + typedef float(&ffnConvert)(int32_t); + typedef double(&dfnConvert)(int32_t); + + switch(sampleType) { + case ADLMIDI_SampleType_S8: + case ADLMIDI_SampleType_U8: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S8) ? adl_cvtS8 : adl_cvtU8; + switch(containerSize) { + case sizeof(int8_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_S16: + case ADLMIDI_SampleType_U16: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S16) ? adl_cvtS16 : adl_cvtU16; + switch(containerSize) { + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int32_t): + CopySamplesRaw(left, right, _in, toCopy / 2, sampleOffset); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_S24: + case ADLMIDI_SampleType_U24: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S24) ? adl_cvtS24 : adl_cvtU24; + switch(containerSize) { + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_S32: + case ADLMIDI_SampleType_U32: + { + pfnConvert cvt = (sampleType == ADLMIDI_SampleType_S32) ? adl_cvtS32 : adl_cvtU32; + switch(containerSize) { + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case ADLMIDI_SampleType_F32: + { + if(containerSize != sizeof(float)) + return -1; + ffnConvert cvt = adl_cvtReal; + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + } + case ADLMIDI_SampleType_F64: + { + if(containerSize != sizeof(double)) + return -1; + dfnConvert cvt = adl_cvtReal; + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + } + default: + return -1; + } + + return 0; +} +# else // __WATCOMC__ + +/* + Workaround for OpenWattcom where templates are declared above are causing compiler to be crashed +*/ +static void CopySamplesTransformed(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset, + int32_t(&transform)(int32_t)) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(int16_t *)(dstLeft + (i * sampleOffset)) = (int16_t)transform(src[2 * i]); + *(int16_t *)(dstRight + (i * sampleOffset)) = (int16_t)transform(src[(2 * i) + 1]); + } +} + +static int SendStereoAudio(int samples_requested, + ssize_t in_size, + int32_t *_in, + ssize_t out_pos, + ADL_UInt8 *left, + ADL_UInt8 *right, + const ADLMIDI_AudioFormat *format) +{ + if(!in_size) + return 0; + size_t outputOffset = static_cast(out_pos); + size_t inSamples = static_cast(in_size * 2); + size_t maxSamples = static_cast(samples_requested) - outputOffset; + size_t toCopy = std::min(maxSamples, inSamples); + + ADLMIDI_SampleType sampleType = format->type; + const unsigned containerSize = format->containerSize; + const unsigned sampleOffset = format->sampleOffset; + + left += (outputOffset / 2) * sampleOffset; + right += (outputOffset / 2) * sampleOffset; + + if(sampleType == ADLMIDI_SampleType_U16) + { + switch(containerSize) { + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, adl_cvtS16); + break; + default: + return -1; + } + } + else + return -1; + return 0; +} +# endif // __WATCOM__ + +#endif // ADLMIDI_HW_OPL + + +ADLMIDI_EXPORT int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out) +{ + return adl_playFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat); +} + +ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, + ADL_UInt8 *out_left, ADL_UInt8 *out_right, + const ADLMIDI_AudioFormat *format) +{ +#if defined(ADLMIDI_DISABLE_MIDI_SEQUENCER) || defined(ADLMIDI_HW_OPL) + ADL_UNUSED(device); + ADL_UNUSED(sampleCount); + ADL_UNUSED(out_left); + ADL_UNUSED(out_right); + ADL_UNUSED(format); + return 0; +#endif + +#if !defined(ADLMIDI_DISABLE_MIDI_SEQUENCER) && !defined(ADLMIDI_HW_OPL) + sampleCount -= sampleCount % 2; //Avoid even sample requests + if(sampleCount < 0) + return 0; + if(!device) + return 0; + + MidiPlayer *player = GET_MIDI_PLAYER(device); + assert(player); + MidiPlayer::Setup &setup = player->m_setup; + + ssize_t gotten_len = 0; + ssize_t n_periodCountStereo = 512; + //ssize_t n_periodCountPhys = n_periodCountStereo * 2; + int left = sampleCount; + bool hasSkipped = setup.tick_skip_samples_delay > 0; + + while(left > 0) + { + const double eat_delay = setup.delay < setup.maxdelay ? setup.delay : setup.maxdelay; + if(hasSkipped) + { + size_t samples = setup.tick_skip_samples_delay > sampleCount ? sampleCount : setup.tick_skip_samples_delay; + n_periodCountStereo = samples / 2; + } + else + { + setup.delay -= eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; + n_periodCountStereo = static_cast(setup.carry); + setup.carry -= double(n_periodCountStereo); + } + + //if(setup.SkipForward > 0) + // setup.SkipForward -= 1; + //else + { + if((player->m_sequencer->positionAtEnd()) && (setup.delay <= 0.0)) + break;//Stop to fetch samples at reaching the song end with disabled loop + + ssize_t leftSamples = left / 2; + if(n_periodCountStereo > leftSamples) + { + setup.tick_skip_samples_delay = (n_periodCountStereo - leftSamples) * 2; + n_periodCountStereo = leftSamples; + } + //! Count of stereo samples + ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo; + //! Total count of samples + ssize_t in_generatedPhys = in_generatedStereo * 2; + //! Unsigned total sample count + //fill buffer with zeros + int32_t *out_buf = player->m_outBuf; + std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); + Synth &synth = *player->m_synth; + unsigned int chips = synth.m_numChips; + if(chips == 1) + synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); + else/* if(n_periodCountStereo > 0)*/ + { + /* Generate data from every chip and mix result */ + for(size_t card = 0; card < chips; ++card) + synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + } + + /* Process it */ + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; + + left -= (int)in_generatedPhys; + gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; + } + + if(hasSkipped) + { + setup.tick_skip_samples_delay -= n_periodCountStereo * 2; + hasSkipped = setup.tick_skip_samples_delay > 0; + } + else + { + setup.delay = player->Tick(eat_delay, setup.mindelay); + player->TickIterators(eat_delay); + } + } + + return static_cast(gotten_len); +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER +} + + +ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out) +{ + return adl_generateFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat); +} + +ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, + ADL_UInt8 *out_left, ADL_UInt8 *out_right, + const ADLMIDI_AudioFormat *format) +{ +#ifdef ADLMIDI_HW_OPL + ADL_UNUSED(device); + ADL_UNUSED(sampleCount); + ADL_UNUSED(out_left); + ADL_UNUSED(out_right); + ADL_UNUSED(format); + return 0; +#else + sampleCount -= sampleCount % 2; //Avoid even sample requests + if(sampleCount < 0) + return 0; + if(!device) + return 0; + + MidiPlayer *player = GET_MIDI_PLAYER(device); + assert(player); + MidiPlayer::Setup &setup = player->m_setup; + + ssize_t gotten_len = 0; + ssize_t n_periodCountStereo = 512; + + int left = sampleCount; + double delay = double(sampleCount / 2) / double(setup.PCM_RATE); + + while(left > 0) + { + if(delay <= 0.0) + delay = double(left / 2) / double(setup.PCM_RATE); + const double eat_delay = delay < setup.maxdelay ? delay : setup.maxdelay; + delay -= eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; + n_periodCountStereo = static_cast(setup.carry); + setup.carry -= double(n_periodCountStereo); + + { + ssize_t leftSamples = left / 2; + if(n_periodCountStereo > leftSamples) + n_periodCountStereo = leftSamples; + //! Count of stereo samples + ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo; + //! Total count of samples + ssize_t in_generatedPhys = in_generatedStereo * 2; + //! Unsigned total sample count + //fill buffer with zeros + int32_t *out_buf = player->m_outBuf; + std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); + Synth &synth = *player->m_synth; + unsigned int chips = synth.m_numChips; + if(chips == 1) + synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); + else if(n_periodCountStereo > 0) + { + /* Generate data from every chip and mix result */ + for(unsigned card = 0; card < chips; ++card) + synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + } + /* Process it */ + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; + + left -= (int)in_generatedPhys; + gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; + } + + player->TickIterators(eat_delay); + } + + return static_cast(gotten_len); +#endif +} + +ADLMIDI_EXPORT double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, double granulality) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + double ret = play->Tick(seconds, granulality); + play->TickIterators(seconds); + return ret; +#else + ADL_UNUSED(device); + ADL_UNUSED(seconds); + ADL_UNUSED(granulality); + return -1.0; +#endif +} + +ADLMIDI_EXPORT double adl_tickEventsOnly(struct ADL_MIDIPlayer *device, double seconds, double granulality) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->Tick(seconds, granulality); +#else + ADL_UNUSED(device); + ADL_UNUSED(seconds); + ADL_UNUSED(granulality); + return -1.0; +#endif +} + +ADLMIDI_EXPORT void adl_tickIterators(struct ADL_MIDIPlayer *device, double seconds) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + play->TickIterators(seconds); +} + +ADLMIDI_EXPORT int adl_atEnd(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_sequencer->positionAtEnd(); +#else + ADL_UNUSED(device); + return 1; +#endif +} + +ADLMIDI_EXPORT size_t adl_trackCount(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getTrackCount(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +ADLMIDI_EXPORT int adl_setTrackOptions(struct ADL_MIDIPlayer *device, size_t trackNumber, unsigned trackOptions) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + MidiSequencer &seq = *play->m_sequencer; + + unsigned enableFlag = trackOptions & 3; + trackOptions &= ~3u; + + // handle on/off/solo + switch(enableFlag) + { + default: + break; + case ADLMIDI_TrackOption_On: + case ADLMIDI_TrackOption_Off: + if(!seq.setTrackEnabled(trackNumber, enableFlag == ADLMIDI_TrackOption_On)) + return -1; + break; + case ADLMIDI_TrackOption_Solo: + seq.setSoloTrack(trackNumber); + break; + } + + // handle others... + if(trackOptions != 0) + return -1; + + return 0; + +#else + ADL_UNUSED(device); + ADL_UNUSED(trackNumber); + ADL_UNUSED(trackOptions); + return -1; +#endif +} + +ADLMIDI_EXPORT int adl_setChannelEnabled(struct ADL_MIDIPlayer *device, size_t channelNumber, int enabled) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + MidiSequencer &seq = *play->m_sequencer; + + if(!seq.setChannelEnabled(channelNumber, (bool)enabled)) + return -1; + return 0; +#else + ADL_UNUSED(device); + ADL_UNUSED(channelNumber); + ADL_UNUSED(enabled); + return -1; +#endif +} + +ADLMIDI_EXPORT int adl_setTriggerHandler(struct ADL_MIDIPlayer *device, ADL_TriggerHandler handler, void *userData) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + MidiSequencer &seq = *play->m_sequencer; + seq.setTriggerHandler(handler, userData); + return 0; +#else + ADL_UNUSED(device); + ADL_UNUSED(handler); + ADL_UNUSED(userData); + return -1; +#endif +} + +ADLMIDI_EXPORT void adl_panic(struct ADL_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); +} + +ADLMIDI_EXPORT void adl_rt_resetState(struct ADL_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_ResetState(); +} + +ADLMIDI_EXPORT int adl_rt_noteOn(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 velocity) +{ + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->realTime_NoteOn(channel, note, velocity); +} + +ADLMIDI_EXPORT void adl_rt_noteOff(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_NoteOff(channel, note); +} + +ADLMIDI_EXPORT void adl_rt_noteAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 atVal) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_NoteAfterTouch(channel, note, atVal); +} + +ADLMIDI_EXPORT void adl_rt_channelAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 atVal) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_ChannelAfterTouch(channel, atVal); +} + +ADLMIDI_EXPORT void adl_rt_controllerChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 type, ADL_UInt8 value) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_Controller(channel, type, value); +} + +ADLMIDI_EXPORT void adl_rt_patchChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 patch) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PatchChange(channel, patch); +} + +ADLMIDI_EXPORT void adl_rt_pitchBend(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt16 pitch) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PitchBend(channel, pitch); +} + +ADLMIDI_EXPORT void adl_rt_pitchBendML(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb, ADL_UInt8 lsb) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PitchBend(channel, msb, lsb); +} + +ADLMIDI_EXPORT void adl_rt_bankChangeLSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 lsb) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChangeLSB(channel, lsb); +} + +ADLMIDI_EXPORT void adl_rt_bankChangeMSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChangeMSB(channel, msb); +} + +ADLMIDI_EXPORT void adl_rt_bankChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_SInt16 bank) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChange(channel, (uint16_t)bank); +} + +ADLMIDI_EXPORT int adl_rt_systemExclusive(struct ADL_MIDIPlayer *device, const ADL_UInt8 *msg, size_t size) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->realTime_SysEx(msg, size); +} diff --git a/thirdparty/adlmidi/adlmidi.h b/thirdparty/adlmidi/adlmidi.h new file mode 100644 index 000000000..c41523e04 --- /dev/null +++ b/thirdparty/adlmidi/adlmidi.h @@ -0,0 +1,1498 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_H +#define ADLMIDI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define ADLMIDI_VERSION_MAJOR 1 +#define ADLMIDI_VERSION_MINOR 6 +#define ADLMIDI_VERSION_PATCHLEVEL 0 + +#define ADLMIDI_TOSTR_I(s) #s +#define ADLMIDI_TOSTR(s) ADLMIDI_TOSTR_I(s) +#define ADLMIDI_VERSION \ + ADLMIDI_TOSTR(ADLMIDI_VERSION_MAJOR) "." \ + ADLMIDI_TOSTR(ADLMIDI_VERSION_MINOR) "." \ + ADLMIDI_TOSTR(ADLMIDI_VERSION_PATCHLEVEL) + +#define ADL_CHIP_SAMPLE_RATE 49716 + +#include + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#include +typedef uint8_t ADL_UInt8; +typedef uint16_t ADL_UInt16; +typedef int8_t ADL_SInt8; +typedef int16_t ADL_SInt16; +#else +typedef unsigned char ADL_UInt8; +typedef unsigned short ADL_UInt16; +typedef char ADL_SInt8; +typedef short ADL_SInt16; +#endif + +/* == Deprecated function markers == */ + +#if defined(_MSC_VER) /* MSVC */ +# if _MSC_VER >= 1500 /* MSVC 2008 */ + /*! Indicates that the following function is deprecated. */ +# define ADLMIDI_DEPRECATED(message) __declspec(deprecated(message)) +# endif +#endif /* defined(_MSC_VER) */ + +#ifdef __clang__ +# if __has_extension(attribute_deprecated_with_message) +# define ADLMIDI_DEPRECATED(message) __attribute__((deprecated(message))) +# endif +#elif defined __GNUC__ /* not clang (gcc comes later since clang emulates gcc) */ +# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +# define ADLMIDI_DEPRECATED(message) __attribute__((deprecated(message))) +# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +# define ADLMIDI_DEPRECATED(message) __attribute__((__deprecated__)) +# endif /* GNUC version */ +#endif /* __clang__ || __GNUC__ */ + +#if !defined(ADLMIDI_DEPRECATED) +# define ADLMIDI_DEPRECATED(message) +#endif /* if !defined(ADLMIDI_DEPRECATED) */ + + +#ifdef ADLMIDI_BUILD +# ifndef ADLMIDI_DECLSPEC +# if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL) +# define ADLMIDI_DECLSPEC __declspec(dllexport) +# else +# define ADLMIDI_DECLSPEC +# endif +# endif +#else +# define ADLMIDI_DECLSPEC +#endif + + +/** + * @brief Volume scaling models + */ +enum ADLMIDI_VolumeModels +{ + /*! Automatical choice by the specific bank */ + ADLMIDI_VolumeModel_AUTO = 0, + /*! Linearized scaling model, most standard */ + ADLMIDI_VolumeModel_Generic = 1, + /*! Native OPL3's logarithmic volume scale */ + ADLMIDI_VolumeModel_NativeOPL3 = 2, + /*! Native OPL3's logarithmic volume scale. Alias. */ + ADLMIDI_VolumeModel_CMF = ADLMIDI_VolumeModel_NativeOPL3, + /*! Logarithmic volume scale, using volume map table. Used in DMX. */ + ADLMIDI_VolumeModel_DMX = 3, + /*! Logarithmic volume scale, used in Apogee Sound System. */ + ADLMIDI_VolumeModel_APOGEE = 4, + /*! Aproximated and shorted volume map table (SB16 driver). Similar to general, but has less granularity. */ + ADLMIDI_VolumeModel_9X = 5, + /*! DMX model with a fixed bug of AM voices */ + ADLMIDI_VolumeModel_DMX_Fixed = 6, + /*! Apogee model with a fixed bug of AM voices*/ + ADLMIDI_VolumeModel_APOGEE_Fixed = 7, + /*! Audio Interface Library volume scaling model */ + ADLMIDI_VolumeModel_AIL = 8, + /*! Aproximated and shorted volume map table (Generic FM driver). Similar to general, but has less granularity. */ + ADLMIDI_VolumeModel_9X_GENERIC_FM = 9, + /*! HMI Sound Operating System volume scaling model */ + ADLMIDI_VolumeModel_HMI = 10, + /*! HMI Sound Operating System volume scaling model, older variant with bugs */ + ADLMIDI_VolumeModel_HMI_OLD = 11, + /*! Count of available volume model modes */ + ADLMIDI_VolumeModel_Count +}; + +/*! + * \brief Algorithms of channel allocation for new notes + */ +enum ADLMIDI_ChannelAlloc +{ + /*! Automatical choise of the method according to the volume model and internal preferrences */ + ADLMIDI_ChanAlloc_AUTO = -1, + /*! Take only channels that has expired sounding delay */ + ADLMIDI_ChanAlloc_OffDelay, + /*! Take any first released channel with the same instrument */ + ADLMIDI_ChanAlloc_SameInst, + /*! Take any first released channel */ + ADLMIDI_ChanAlloc_AnyReleased, + /*! Count of available channel allocation modes */ + ADLMIDI_ChanAlloc_Count +}; + +/** + * @brief Sound output format + */ +enum ADLMIDI_SampleType +{ + /*! signed PCM 16-bit */ + ADLMIDI_SampleType_S16 = 0, + /*! signed PCM 8-bit */ + ADLMIDI_SampleType_S8, + /*! float 32-bit */ + ADLMIDI_SampleType_F32, + /*! float 64-bit */ + ADLMIDI_SampleType_F64, + /*! signed PCM 24-bit */ + ADLMIDI_SampleType_S24, + /*! signed PCM 32-bit */ + ADLMIDI_SampleType_S32, + /*! unsigned PCM 8-bit */ + ADLMIDI_SampleType_U8, + /*! unsigned PCM 16-bit */ + ADLMIDI_SampleType_U16, + /*! unsigned PCM 24-bit */ + ADLMIDI_SampleType_U24, + /*! unsigned PCM 32-bit */ + ADLMIDI_SampleType_U32, + /*! Count of available sample format types */ + ADLMIDI_SampleType_Count +}; + +/** + * @brief Sound output format context + */ +struct ADLMIDI_AudioFormat +{ + /*! type of sample */ + enum ADLMIDI_SampleType type; + /*! size in bytes of the storage type */ + unsigned containerSize; + /*! distance in bytes between consecutive samples */ + unsigned sampleOffset; +}; + +/** + * @brief Instance of the library + */ +struct ADL_MIDIPlayer +{ + /*! Private context descriptor */ + void *adl_midiPlayer; +}; + +/* DEPRECATED */ +#define adl_setNumCards adl_setNumChips + +/** + * @brief Sets number of emulated chips (from 1 to 100). Emulation of multiple chips extends polyphony limits + * @param device Instance of the library + * @param numChips Count of virtual chips to emulate + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setNumChips(struct ADL_MIDIPlayer *device, int numChips); + +/** + * @brief Get current number of emulated chips + * @param device Instance of the library + * @return Count of working chip emulators + */ +extern ADLMIDI_DECLSPEC int adl_getNumChips(struct ADL_MIDIPlayer *device); + +/** + * @brief Get obtained number of emulated chips + * @param device Instance of the library + * @return Count of working chip emulators + */ +extern ADLMIDI_DECLSPEC int adl_getNumChipsObtained(struct ADL_MIDIPlayer *device); + +/** + * @brief Sets a number of the patches bank from 0 to N banks. + * + * Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param bank Number of embedded bank + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setBank(struct ADL_MIDIPlayer *device, int bank); + +/** + * @brief Returns total number of available banks + * @return Total number of available embedded banks + */ +extern ADLMIDI_DECLSPEC int adl_getBanksCount(); + +/** + * @brief Returns pointer to array of names of every bank + * @return Array of strings containing the name of every embedded bank + */ +extern ADLMIDI_DECLSPEC const char *const *adl_getBankNames(); + +/** + * @brief Reference to dynamic bank + */ +typedef struct ADL_Bank +{ + void *pointer[3]; +} ADL_Bank; + +/** + * @brief Identifier of dynamic bank + */ +typedef struct ADL_BankId +{ + /*! 0 if bank is melodic set, or 1 if bank is a percussion set */ + ADL_UInt8 percussive; + /*! Assign to MSB bank number */ + ADL_UInt8 msb; + /*! Assign to LSB bank number */ + ADL_UInt8 lsb; +} ADL_BankId; + +/** + * @brief Flags for dynamic bank access + */ +enum ADL_BankAccessFlags +{ + /*! create bank, allocating memory as needed */ + ADLMIDI_Bank_Create = 1, + /*! create bank, never allocating memory */ + ADLMIDI_Bank_CreateRt = 1|2 +}; + + +/* ======== Instrument structures ======== */ + +/** + * @brief Version of the instrument data format + */ +enum +{ + ADLMIDI_InstrumentVersion = 0 +}; + +/** + * @brief Instrument flags + */ +typedef enum ADL_InstrumentFlags +{ + /*! Is two-operator single-voice instrument (no flags) */ + ADLMIDI_Ins_2op = 0x00, + /*! Is true four-operator instrument */ + ADLMIDI_Ins_4op = 0x01, + /*! Is pseudo four-operator (two 2-operator voices) instrument */ + ADLMIDI_Ins_Pseudo4op = 0x02, + /*! Is a blank instrument entry */ + ADLMIDI_Ins_IsBlank = 0x04, + + /*! RythmMode flags mask */ + ADLMIDI_Ins_RhythmModeMask = 0x38, + + /*! Mask of the flags range */ + ADLMIDI_Ins_ALL_MASK = 0x07 +} ADL_InstrumentFlags; + +/** + * @brief Rhythm-mode drum type + */ +typedef enum ADL_RhythmMode +{ + /*! RythmMode: BassDrum */ + ADLMIDI_RM_BassDrum = 0x08, + /*! RythmMode: Snare */ + ADLMIDI_RM_Snare = 0x10, + /*! RythmMode: TomTom */ + ADLMIDI_RM_TomTom = 0x18, + /*! RythmMode: Cymbal */ + ADLMIDI_RM_Cymbal = 0x20, + /*! RythmMode: HiHat */ + ADLMIDI_RM_HiHat = 0x28 +} ADL_RhythmMode; + + +/** + * @brief Operator structure, part of Instrument structure + */ +typedef struct ADL_Operator +{ + /*! AM/Vib/Env/Ksr/FMult characteristics */ + ADL_UInt8 avekf_20; + /*! Key Scale Level / Total level register data */ + ADL_UInt8 ksl_l_40; + /*! Attack / Decay */ + ADL_UInt8 atdec_60; + /*! Systain and Release register data */ + ADL_UInt8 susrel_80; + /*! Wave form */ + ADL_UInt8 waveform_E0; +} ADL_Operator; + +/** + * @brief Instrument structure + */ +typedef struct ADL_Instrument +{ + /*! Version of the instrument object */ + int version; + /*! MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + ADL_SInt16 note_offset1; + /*! MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */ + ADL_SInt16 note_offset2; + /*! MIDI note velocity offset (taken from Apogee TMB format) */ + ADL_SInt8 midi_velocity_offset; + /*! Second voice detune level (taken from DMX OP2) */ + ADL_SInt8 second_voice_detune; + /*! Percussion MIDI base tone number at which this drum will be played */ + ADL_UInt8 percussion_key_number; + /** + * @var inst_flags + * @brief Instrument flags + * + * Enums: #ADL_InstrumentFlags and #ADL_RhythmMode + * + * Bitwise flags bit map: + * ``` + * [0EEEDCBA] + * A) 0x00 - 2-operator mode + * B) 0x01 - 4-operator mode + * C) 0x02 - pseudo-4-operator (two 2-operator voices) mode + * D) 0x04 - is 'blank' instrument (instrument which has no sound) + * E) 0x38 - Reserved for rhythm-mode percussion type number (three bits number) + * -> 0x00 - Melodic or Generic drum (rhythm-mode is disabled) + * -> 0x08 - is Bass drum + * -> 0x10 - is Snare + * -> 0x18 - is Tom-tom + * -> 0x20 - is Cymbal + * -> 0x28 - is Hi-hat + * 0) Reserved / Unused + * ``` + */ + ADL_UInt8 inst_flags; + /*! Feedback&Connection register for first and second operators */ + ADL_UInt8 fb_conn1_C0; + /*! Feedback&Connection register for third and fourth operators */ + ADL_UInt8 fb_conn2_C0; + /*! Operators register data */ + ADL_Operator operators[4]; + /*! Millisecond delay of sounding while key is on */ + ADL_UInt16 delay_on_ms; + /*! Millisecond delay of sounding after key off */ + ADL_UInt16 delay_off_ms; +} ADL_Instrument; + + + + +/* ======== Setup ======== */ + +/** + * @brief Preallocates a minimum number of bank slots. Returns the actual capacity + * @param device Instance of the library + * @param banks Count of bank slots to pre-allocate. + * @return actual capacity of reserved bank slots. + */ +extern ADLMIDI_DECLSPEC int adl_reserveBanks(struct ADL_MIDIPlayer *device, unsigned banks); +/** + * @brief Gets the bank designated by the identifier, optionally creating if it does not exist + * @param device Instance of the library + * @param id Identifier of dynamic bank + * @param flags Flags for dynamic bank access (ADL_BankAccessFlags) + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getBank(struct ADL_MIDIPlayer *device, const ADL_BankId *id, int flags, ADL_Bank *bank); +/** + * @brief Gets the identifier of a bank + * @param device Instance of the library + * @param bank Reference to dynamic bank. + * @param id Identifier of dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getBankId(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id); +/** + * @brief Removes a bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_removeBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/** + * @brief Gets the first bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getFirstBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/** + * @brief Iterates to the next bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred or end has been reached. + */ +extern ADLMIDI_DECLSPEC int adl_getNextBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/** + * @brief Gets the nth intrument in the bank [0..127] + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param index Index of the instrument + * @param ins Instrument entry + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getInstrument(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins); +/** + * @brief Sets the nth intrument in the bank [0..127] + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param index Index of the instrument + * @param ins Instrument structure pointer + * @return 0 on success, <0 when any error has occurred + * + * This function allows to override an instrument on the fly + */ +extern ADLMIDI_DECLSPEC int adl_setInstrument(struct ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins); +/** + * @brief Loads the melodic or percussive part of the nth embedded bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param num Number of embedded bank to load into the current bank array + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_loadEmbeddedBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank, int num); + + + +/** + * @brief Sets number of 4-operator channels between all chips + * + * By default, it is automatically re-calculating every bank change. + * If you want to specify custom number of four operator channels, + * please call this function after bank change (adl_setBank() or adl_openBank()), + * otherwise, value will be overwritten by auto-calculated. + * If the count is specified as -1, an auto-calculated amount is used instead. + * + * @param device Instance of the library + * @param ops4 Count of four-op channels to allocate between all emulating chips + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setNumFourOpsChn(struct ADL_MIDIPlayer *device, int ops4); + +/** + * @brief Get current total count of 4-operator channels between all chips + * @param device Instance of the library + * @return 0 on success, <-1 when any error has occurred, but, -1 - "auto" + */ +extern ADLMIDI_DECLSPEC int adl_getNumFourOpsChn(struct ADL_MIDIPlayer *device); + +/** + * @brief Get obtained total count of 4-operator channels between all chips + * @param device Instance of the library + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getNumFourOpsChnObtained(struct ADL_MIDIPlayer *device); + +/** + * @brief Override Enable(1) or Disable(0) AdLib percussion mode. -1 - use bank default AdLib percussion mode + * + * [DEPRECATED] This function is no more useful and now it makes nothing. Kept for ABI compatibility. Rhythm mode can be set by the bank data only. + * + * This function forces rhythm-mode on any bank. The result will work glitchy. + * + * @param device Instance of the library + * @param percmod 0 - disabled, 1 - enabled + */ +ADLMIDI_DEPRECATED("This function is no more useful and now it makes nothing. Kept for ABI compatibility. Rhythm mode can be set by the bank data only.") +extern ADLMIDI_DECLSPEC void adl_setPercMode(struct ADL_MIDIPlayer *device, int percmod); + +/** + * @brief Override Enable(1) or Disable(0) deep vibrato state. -1 - use bank default vibrato state + * @param device Instance of the library + * @param hvibro 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setHVibrato(struct ADL_MIDIPlayer *device, int hvibro); + +/** + * @brief Get the deep vibrato state. + * @param device Instance of the library + * @return deep vibrato state on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getHVibrato(struct ADL_MIDIPlayer *device); + +/** + * @brief Override Enable(1) or Disable(0) deep tremolo state. -1 - use bank default tremolo state + * @param device Instance of the library + * @param htremo 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setHTremolo(struct ADL_MIDIPlayer *device, int htremo); + +/** + * @brief Get the deep tremolo state. + * @param device Instance of the library + * @return deep tremolo state on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getHTremolo(struct ADL_MIDIPlayer *device); + +/** + * @brief Override Enable(1) or Disable(0) scaling of modulator volumes. -1 - use bank default scaling of modulator volumes + * @param device Instance of the library + * @param smod 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setScaleModulators(struct ADL_MIDIPlayer *device, int smod); + +/** + * @brief Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling + * + * By default, brightness affects sound between 0 and 64. + * When this option is enabled, the brightness will use full range from 0 up to 127. + * + * @param device Instance of the library + * @param fr_brightness 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness); + +/** + * @brief Enable(1) or Disable(0) the automatical arpeggio system + * + * @param device Instance of the library + * @param aaEn 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setAutoArpeggio(struct ADL_MIDIPlayer *device, int aaEn); + +/** + * @brief Get the state of the automatical arpeggio system enable state + * @param device Instalce of the library + * @return 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC int adl_getAutoArpeggio(struct ADL_MIDIPlayer *device); + +/** + * @brief Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part) + * @param device Instance of the library + * @param loopEn 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setLoopEnabled(struct ADL_MIDIPlayer *device, int loopEn); + +/** + * @brief Set how many times loop will be played + * + * Note: The song will be played once if loop has been disabled with no matter which value of loop count was set + * + * @param device Instance of the library + * @param loopCount Number of loops or -1 to loop infinitely + */ +extern ADLMIDI_DECLSPEC void adl_setLoopCount(struct ADL_MIDIPlayer *device, int loopCount); + +/** + * @brief Make song immediately stop on reaching a loop end point + * @param device Instance of the library + * @param loopHooksOnly 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setLoopHooksOnly(struct ADL_MIDIPlayer *device, int loopHooksOnly); + +/** + * @brief Enable or disable soft panning with chip emulators + * @param device Instance of the library + * @param softPanEn 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setSoftPanEnabled(struct ADL_MIDIPlayer *device, int softPanEn); + +/** + * @brief [DEPRECATED] Enable or disable Logarithmic volume changer + * + * This function is deprecated. Suggested replacement: `adl_setVolumeRangeModel` with `ADLMIDI_VolumeModel_NativeOPL3` volume model value; + */ +ADLMIDI_DEPRECATED("Use `adl_setVolumeRangeModel(device, ADLMIDI_VolumeModel_NativeOPL3)` instead") +extern ADLMIDI_DECLSPEC void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol); + +/** + * @brief Set different volume range model + * @param device Instance of the library + * @param volumeModel Volume model type (#ADLMIDI_VolumeModels) + */ +extern ADLMIDI_DECLSPEC void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel); + +/** + * @brief Get the volume range model + * @param device Instance of the library + * @return volume model on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getVolumeRangeModel(struct ADL_MIDIPlayer *device); + +/** + * @brief Set the channel allocation mode + * @param device Instance of the library + * @param chanalloc Channel allocation mode (#ADLMIDI_ChannelAlloc) + */ +extern ADLMIDI_DECLSPEC void adl_setChannelAllocMode(struct ADL_MIDIPlayer *device, int chanalloc); + +/** + * @brief Get the current channel allocation mode + * @param device Instance of the library + * @return Channel allocation mode (#ADLMIDI_ChannelAlloc) + */ +extern ADLMIDI_DECLSPEC int adl_getChannelAllocMode(struct ADL_MIDIPlayer *device); + +/** + * @brief Load WOPL bank file from File System + * + * Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param filePath Absolute or relative path to the WOPL bank file. UTF8 encoding is required, even on Windows. + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath); + +/** + * @brief Load WOPL bank file from memory data + * + * Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param mem Pointer to memory block where is raw data of WOPL bank file is stored + * @param size Size of given memory block + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_openBankData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size); + + +/** + * @brief [DEPRECATED] Dummy function + * + * This function is deprecated. Suggested replacement: `adl_chipEmulatorName` + * + * @return A string that contains a notice to use `adl_chipEmulatorName` instead of this function. + */ +ADLMIDI_DEPRECATED("Use `adl_chipEmulatorName(device)` instead") +extern ADLMIDI_DECLSPEC const char *adl_emulatorName(); + +/** + * @brief Returns chip emulator name string + * @param device Instance of the library + * @return Understandable name of current OPL3 emulator + */ +extern ADLMIDI_DECLSPEC const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device); + +/** + * @brief List of available OPL3 emulators + */ +enum ADL_Emulator +{ + /*! Nuked OPL3 v. 1.8 */ + ADLMIDI_EMU_NUKED = 0, + /*! Nuked OPL3 v. 1.7.4 */ + ADLMIDI_EMU_NUKED_174, + /*! DosBox */ + ADLMIDI_EMU_DOSBOX, + /*! Opal */ + ADLMIDI_EMU_OPAL, + /*! Java */ + ADLMIDI_EMU_JAVA, + /*! ESFMu */ + ADLMIDI_EMU_ESFMu, + /*! MAME OPL2 */ + ADLMIDI_EMU_MAME_OPL2, + /*! YMFM OPL2 */ + ADLMIDI_EMU_YMFM_OPL2, + /*! YMFM OPL3 */ + ADLMIDI_EMU_YMFM_OPL3, + /*! Nuked OPL2 LLE*/ + ADLMIDI_EMU_NUKED_OPL2_LLE, + /*! Nuked OPL3 LLE*/ + ADLMIDI_EMU_NUKED_OPL3_LLE, + /*! Count instrument on the level */ + ADLMIDI_EMU_end +}; + +/** + * @brief Switch the emulation core + * @param device Instance of the library + * @param emulator Type of emulator (#ADL_Emulator) + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator); + +/** + * @brief Library version context + */ +typedef struct { + ADL_UInt16 major; + ADL_UInt16 minor; + ADL_UInt16 patch; +} ADL_Version; + +/** + * @brief Run emulator with PCM rate to reduce CPU usage on slow devices. + * + * May decrease sounding accuracy on some chip emulators. + * + * @param device Instance of the library + * @param enabled 0 - disabled, 1 - enabled + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setRunAtPcmRate(struct ADL_MIDIPlayer *device, int enabled); + +/** + * @brief The list of serial port protocols + */ +enum ADL_SerialProtocol +{ + ADLMIDI_SerialProtocol_Unknown = 0, + ADLMIDI_SerialProtocol_ArduinoOPL2, + ADLMIDI_SerialProtocol_NukeYktOPL3, + ADLMIDI_SerialProtocol_RetroWaveOPL3, + ADLMIDI_SerialProtocol_END +}; + +/** + * @brief Switch the synthesizer into hardware mode using Serial port + * @param name The name of the serial port device (it may look different on various platforms. On UNIX-like systems don't type the /dev/ prefix: only name). + * @param baud The baud speed of the serial port + * @param protocol The binary protocol used to communicate the device (#ADL_SerialProtocol) + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_switchSerialHW(struct ADL_MIDIPlayer *device, + const char *name, + unsigned baud, + unsigned protocol); + + +/** + * \brief The list of possible chip types for DOS hardware interface + */ +enum ADL_DOS_ChipType +{ + ADLMIDI_DOS_ChipAuto = 0, + ADLMIDI_DOS_ChipOPL2, + ADLMIDI_DOS_ChipOPL3 +}; + +/** + * @brief Specify the chip type and the base address before initialization. + * This function has no effect if library didn't built for DOS with hardware OPL mode + * Important: Run this BEFORE creating the instance of the library. + * @param chipType + * @param baseAddress + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_switchDOSHW(int chipType, ADL_UInt16 baseAddress); + +/** + * @brief Set 4-bit device identifier. Used by the SysEx processor. + * @param device Instance of the library + * @param id 4-bit device identifier + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setDeviceIdentifier(struct ADL_MIDIPlayer *device, unsigned id); + +/** + * @section Information + */ + +/** + * @brief Returns string which contains a version number + * @return String which contains a version of the library + */ +extern ADLMIDI_DECLSPEC const char *adl_linkedLibraryVersion(); + +/** + * @brief Returns structure which contains a version number of library + * @return Library version context structure which contains version number of the library + */ +extern ADLMIDI_DECLSPEC const ADL_Version *adl_linkedVersion(); + + +/* ======== Error Info ======== */ + +/** + * @brief Returns string which contains last error message of initialization + * + * Don't use this function to get info on any function except of `adl_init`! + * Use `adl_errorInfo()` to get error information while workflow + * + * @return String with error message related to library initialization + */ +extern ADLMIDI_DECLSPEC const char *adl_errorString(); + +/** + * @brief Returns string which contains last error message on specific device + * @param device Instance of the library + * @return String with error message related to last function call returned non-zero value. + */ +extern ADLMIDI_DECLSPEC const char *adl_errorInfo(struct ADL_MIDIPlayer *device); + + + +/* ======== Initialization ======== */ + +/** + * @brief Initialize ADLMIDI Player device + * + * Tip 1: You can initialize multiple instances and run them in parallel + * Tip 2: Library is NOT thread-safe, therefore don't use same instance in different threads or use mutexes + * Tip 3: Changing of sample rate on the fly is not supported. Re-create the instance again. + * Top 4: To generate output in OPL chip native sample rate, please initialize it with sample rate value as `ADL_CHIP_SAMPLE_RATE` + * + * @param sample_rate Output sample rate + * @return Instance of the library. If NULL was returned, check the `adl_errorString` message for more info. + */ +extern ADLMIDI_DECLSPEC struct ADL_MIDIPlayer *adl_init(long sample_rate); + +/** + * @brief Close and delete ADLMIDI device + * @param device Instance of the library + */ +extern ADLMIDI_DECLSPEC void adl_close(struct ADL_MIDIPlayer *device); + + + +/* ======== MIDI Sequencer ======== */ + +/** + * @brief Load MIDI (or any other supported format) file from File System + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param filePath Absolute or relative path to the music file. UTF8 encoding is required, even on Windows. + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_openFile(struct ADL_MIDIPlayer *device, const char *filePath); + +/** + * @brief Load MIDI (or any other supported format) file from memory data + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param mem Pointer to memory block where is raw data of music file is stored + * @param size Size of given memory block + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_openData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size); + +/** + * @brief Switch another song if multi-song file is playing (for example, XMI) + * + * Note: to set the initial song to load, you should call this function + * BBEFORE calling `adl_openFile` or `adl_openData`. When loaded file has more than + * one built-in songs (Usually XMIformat), it will be started from the selected number. + * You may call this function to switch another song. + * + * @param device Instance of the library + * @param songNumber Identifier of the track to load (or -1 to mix all tracks as one song) + * @return + */ +extern ADLMIDI_DECLSPEC void adl_selectSongNum(struct ADL_MIDIPlayer *device, int songNumber); + +/** + * @brief Retrive the number of songs in a currently opened file + * @param device Instance of the library + * @return Number of songs in the file. If 1 or less, means, the file has only one song inside. + */ +extern ADLMIDI_DECLSPEC int adl_getSongsCount(struct ADL_MIDIPlayer *device); + +/** + * @brief Resets MIDI player (per-channel setup) into initial state + * @param device Instance of the library + */ +extern ADLMIDI_DECLSPEC void adl_reset(struct ADL_MIDIPlayer *device); + +/** + * @brief Get total time length of current song + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Total song length in seconds + */ +extern ADLMIDI_DECLSPEC double adl_totalTimeLength(struct ADL_MIDIPlayer *device); + +/** + * @brief Get loop start time if presented. + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Time position in seconds of loop start point, or -1 when file has no loop points + */ +extern ADLMIDI_DECLSPEC double adl_loopStartTime(struct ADL_MIDIPlayer *device); + +/** + * @brief Get loop endtime if presented. + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Time position in seconds of loop end point, or -1 when file has no loop points + */ +extern ADLMIDI_DECLSPEC double adl_loopEndTime(struct ADL_MIDIPlayer *device); + +/** + * @brief Get current time position in seconds + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Current time position in seconds + */ +extern ADLMIDI_DECLSPEC double adl_positionTell(struct ADL_MIDIPlayer *device); + +/** + * @brief Jump to absolute time position in seconds + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param seconds Destination time position in seconds to seek + */ +extern ADLMIDI_DECLSPEC void adl_positionSeek(struct ADL_MIDIPlayer *device, double seconds); + +/** + * @brief Reset MIDI track position to begin + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + */ +extern ADLMIDI_DECLSPEC void adl_positionRewind(struct ADL_MIDIPlayer *device); + +/** + * @brief Set tempo multiplier + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param tempo Tempo multiplier value: 1.0 - original tempo, >1 - play faster, <1 - play slower + */ +extern ADLMIDI_DECLSPEC void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo); + +/** + * @brief Returns 1 if music position has reached end + * @param device Instance of the library + * @return 1 when end of sing has been reached, otherwise, 0 will be returned. <0 is returned on any error + */ +extern ADLMIDI_DECLSPEC int adl_atEnd(struct ADL_MIDIPlayer *device); + +/** + * @brief Returns the number of tracks of the current sequence + * @param device Instance of the library + * @return Count of tracks in the current sequence + */ +extern ADLMIDI_DECLSPEC size_t adl_trackCount(struct ADL_MIDIPlayer *device); + +/** + * @brief Track options + */ +enum ADLMIDI_TrackOptions +{ + /*! Enabled track */ + ADLMIDI_TrackOption_On = 1, + /*! Disabled track */ + ADLMIDI_TrackOption_Off = 2, + /*! Solo track */ + ADLMIDI_TrackOption_Solo = 3 +}; + +/** + * @brief Sets options on a track of the current sequence + * @param device Instance of the library + * @param trackNumber Identifier of the designated track. + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setTrackOptions(struct ADL_MIDIPlayer *device, size_t trackNumber, unsigned trackOptions); + +/** + * @brief Sets the channel of the current sequence enable state + * @param device Instance of the library + * @param channelNumber Number of the channel (from 0 to 15) + * @param enabled 1 to enable and 0 to disable + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setChannelEnabled(struct ADL_MIDIPlayer *device, size_t channelNumber, int enabled); + +/** + * @brief Handler of callback trigger events + * @param userData Pointer to user data (usually, context of something) + * @param trigger Value of the event which triggered this callback. + * @param track Identifier of the track which triggered this callback. + */ +typedef void (*ADL_TriggerHandler)(void *userData, unsigned trigger, size_t track); + +/** + * @brief Defines a handler for callback trigger events + * @param device Instance of the library + * @param handler Handler to invoke from the sequencer when triggered, or NULL. + * @param userData Instance of the library + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setTriggerHandler(struct ADL_MIDIPlayer *device, ADL_TriggerHandler handler, void *userData); + + + + +/* ======== Meta-Tags ======== */ + +/** + * @brief Returns string which contains a music title + * @param device Instance of the library + * @return A string that contains music title + */ +extern ADLMIDI_DECLSPEC const char *adl_metaMusicTitle(struct ADL_MIDIPlayer *device); + +/** + * @brief Returns string which contains a copyright string* + * @param device Instance of the library + * @return A string that contains copyright notice, otherwise NULL + */ +extern ADLMIDI_DECLSPEC const char *adl_metaMusicCopyright(struct ADL_MIDIPlayer *device); + +/** + * @brief Returns count of available track titles + * + * NOTE: There are CAN'T be associated with channel in any of event or note hooks + * + * @param device Instance of the library + * @return Count of available MIDI tracks, otherwise NULL + */ +extern ADLMIDI_DECLSPEC size_t adl_metaTrackTitleCount(struct ADL_MIDIPlayer *device); + +/** + * @brief Get track title by index + * @param device Instance of the library + * @param index Index of the track to retreive the title + * @return A string that contains track title, otherwise NULL. + */ +extern ADLMIDI_DECLSPEC const char *adl_metaTrackTitle(struct ADL_MIDIPlayer *device, size_t index); + +/** + * @brief MIDI Marker structure + */ +struct Adl_MarkerEntry +{ + /*! MIDI Marker title */ + const char *label; + /*! Absolute time position of the marker in seconds */ + double pos_time; + /*! Absolute time position of the marker in MIDI ticks */ + unsigned long pos_ticks; +}; + +/** + * @brief Returns count of available markers + * @param device Instance of the library + * @return Count of available MIDI markers + */ +extern ADLMIDI_DECLSPEC size_t adl_metaMarkerCount(struct ADL_MIDIPlayer *device); + +/** + * @brief Returns the marker entry + * @param device Instance of the library + * @param index Index of the marker to retreive it. + * @return MIDI Marker description structure. + */ +extern ADLMIDI_DECLSPEC struct Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size_t index); + + + + +/* ======== Audio output Generation ======== */ + +/** + * @brief Generate PCM signed 16-bit stereo audio output and iterate MIDI timers + * + * Use this function when you are playing MIDI file loaded by `adl_openFile` or by `adl_openData` + * with using of built-in MIDI sequencer. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param sampleCount Count of samples (not frames!) + * @param out Pointer to output with 16-bit stereo PCM output + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern ADLMIDI_DECLSPEC int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out); + +/** + * @brief Generate PCM stereo audio output in sample format declared by given context and iterate MIDI timers + * + * Use this function when you are playing MIDI file loaded by `adl_openFile` or by `adl_openData` + * with using of built-in MIDI sequencer. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param sampleCount Count of samples (not frames!) + * @param left Left channel buffer output (Must be casted into bytes array) + * @param right Right channel buffer output (Must be casted into bytes array) + * @param format Destination PCM format format context + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern ADLMIDI_DECLSPEC int adl_playFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format); + +/** + * @brief Generate PCM signed 16-bit stereo audio output without iteration of MIDI timers + * + * Use this function when you are using library as Real-Time MIDI synthesizer or with + * an external MIDI sequencer. You must to request the amount of samples which is equal + * to the delta between of MIDI event rows. One MIDI row is a group of MIDI events + * are having zero delta/delay between each other. When you are receiving events in + * real time, request the minimal possible delay value. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * @param device Instance of the library + * @param sampleCount + * @param out Pointer to output with 16-bit stereo PCM output + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern ADLMIDI_DECLSPEC int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out); + +/** + * @brief Generate PCM stereo audio output in sample format declared by given context without iteration of MIDI timers + * + * Use this function when you are using library as Real-Time MIDI synthesizer or with + * an external MIDI sequencer. You must to request the amount of samples which is equal + * to the delta between of MIDI event rows. One MIDI row is a group of MIDI events + * are having zero delta/delay between each other. When you are receiving events in + * real time, request the minimal possible delay value. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * @param device Instance of the library + * @param sampleCount + * @param left Left channel buffer output (Must be casted into bytes array) + * @param right Right channel buffer output (Must be casted into bytes array) + * @param format Destination PCM format format context + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern ADLMIDI_DECLSPEC int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format); + +/** + * @brief Periodic tick handler. + * + * Notice: The function is provided to use it with Hardware OPL3 mode or for the purpose to iterate + * MIDI playback without of sound generation. + * + * DON'T USE IT TOGETHER WITH adl_play() and adl_playFormat() calls + * as there are all using this function internally!!! + * + * @param device Instance of the library + * @param seconds Previous delay. On a first moment, pass the `0.0` + * @param granulality Minimal size of one MIDI tick in seconds. + * @return desired number of seconds until next call. Pass this value into `seconds` field in next time + */ +extern ADLMIDI_DECLSPEC double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, double granulality); + +/** + * @brief Periodic tick handler without iterators. + * + * Unlike adl_tickEvents(), it doesn't handles iterators, you need to perform + * them naually via adl_tickIterators(). + * + * Notice: The function is provided to use it with Hardware OPL3 mode or for the purpose to iterate + * MIDI playback without of sound generation. + * + * DON'T USE IT TOGETHER WITH adl_play() and adl_playFormat() calls + * as there are all using this function internally!!! + * + * @param device Instance of the library + * @param seconds Previous delay. On a first moment, pass the `0.0` + * @param granulality Minimal size of one MIDI tick in seconds. + * @return desired number of seconds until next call. Pass this value into `seconds` field in next time + */ +extern ADLMIDI_DECLSPEC double adl_tickEventsOnly(struct ADL_MIDIPlayer *device, double seconds, double granulality); + + +/** + * @brief Periodic tick hander for the real-time hardware output + * + * This function runs a single step of vibrato, auto-arpeggio, and the portamento of @seconds duration. + * + * When running the libADLMIDI as a real-time driver for the ral hardware, call + * this function from the timer and specify the @seconds value with a delay of the single cycle. + * + * @param device Instance of the library + * @param seconds Previous delay. On a first moment, pass the `0.0` + */ +extern ADLMIDI_DECLSPEC void adl_tickIterators(struct ADL_MIDIPlayer *device, double seconds); + + + + +/* ======== Real-Time MIDI ======== */ + +/** + * @brief Force Off all notes on all channels + * @param device Instance of the library + */ +extern ADLMIDI_DECLSPEC void adl_panic(struct ADL_MIDIPlayer *device); + +/** + * @brief Reset states of all controllers on all MIDI channels + * @param device Instance of the library + */ +extern ADLMIDI_DECLSPEC void adl_rt_resetState(struct ADL_MIDIPlayer *device); + +/** + * @brief Turn specific MIDI note ON + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to on [Between 0 and 127] + * @param velocity Velocity level [Between 0 and 127] + * @return 1 when note was successfully started, 0 when note was rejected by any reason. + */ +extern ADLMIDI_DECLSPEC int adl_rt_noteOn(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 velocity); + +/** + * @brief Turn specific MIDI note OFF + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to off [Between 0 and 127] + */ +extern ADLMIDI_DECLSPEC void adl_rt_noteOff(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note); + +/** + * @brief Set note after-touch + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to affect by aftertouch event [Between 0 and 127] + * @param atVal After-Touch value [Between 0 and 127] + */ +extern ADLMIDI_DECLSPEC void adl_rt_noteAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 atVal); + +/** + * @brief Set channel after-touch + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param atVal After-Touch level [Between 0 and 127] + */ +extern ADLMIDI_DECLSPEC void adl_rt_channelAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 atVal); + +/** + * @brief Apply controller change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param type Type of the controller [Between 0 and 255] + * @param value Value of the controller event [Between 0 and 127] + */ +extern ADLMIDI_DECLSPEC void adl_rt_controllerChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 type, ADL_UInt8 value); + +/** + * @brief Apply patch change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param patch Patch number [Between 0 and 127] + */ +extern ADLMIDI_DECLSPEC void adl_rt_patchChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 patch); + +/** + * @brief Apply pitch bend change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param pitch 24-bit pitch bend value + */ +extern ADLMIDI_DECLSPEC void adl_rt_pitchBend(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt16 pitch); + +/** + * @brief Apply pitch bend change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param msb MSB part of 24-bit pitch bend value + * @param lsb LSB part of 24-bit pitch bend value + */ +extern ADLMIDI_DECLSPEC void adl_rt_pitchBendML(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb, ADL_UInt8 lsb); + +/** + * @brief Change LSB of the bank number (Alias to CC-32 event) + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param lsb LSB value of the MIDI bank number + */ +extern ADLMIDI_DECLSPEC void adl_rt_bankChangeLSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 lsb); + +/** + * @brief Change MSB of the bank (Alias to CC-0 event) + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param msb MSB value of the MIDI bank number + */ +extern ADLMIDI_DECLSPEC void adl_rt_bankChangeMSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb); + +/** + * @brief Change bank by absolute signed value + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param bank Bank number as concoctated signed 16-bit value of MSB and LSB parts. + */ + +extern ADLMIDI_DECLSPEC void adl_rt_bankChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_SInt16 bank); + +/** + * @brief Perform a system exclusive message + * @param device Instance of the library + * @param msg Raw SysEx message buffer (must begin with 0xF0 and end with 0xF7) + * @param size Size of given SysEx message buffer + * @return 1 when SysEx message was successfully processed, 0 when SysEx message was rejected by any reason + */ +extern ADLMIDI_DECLSPEC int adl_rt_systemExclusive(struct ADL_MIDIPlayer *device, const ADL_UInt8 *msg, size_t size); + + + + +/* ======== Hooks and debugging ======== */ + +/** + * @brief Raw event callback + * @param userdata Pointer to user data (usually, context of someting) + * @param type MIDI event type + * @param subtype MIDI event sub-type (special events only) + * @param channel MIDI channel + * @param data Raw event data + * @param len Length of event data + */ +typedef void (*ADL_RawEventHook)(void *userdata, ADL_UInt8 type, ADL_UInt8 subtype, ADL_UInt8 channel, const ADL_UInt8 *data, size_t len); + +/** + * @brief Note on/off callback + * @param userdata Pointer to user data (usually, context of someting) + * @param adlchn Chip channel where note was played + * @param note Note number [between 0 and 127] + * @param pressure Velocity level, or -1 when it's note off event + * @param bend Pitch bend offset value + */ +typedef void (*ADL_NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); + +/** + * @brief Debug messages callback + * @param userdata Pointer to user data (usually, context of someting) + * @param fmt Format strign output (in context of `printf()` standard function) + */ +typedef void (*ADL_DebugMessageHook)(void *userdata, const char *fmt, ...); + +/** + * @brief Loop start/end point reach hook + * @param userdata Pointer to user data (usually, context of someting) + */ +typedef void (*ADL_LoopPointHook)(void *userdata); + +/** + * @brief Set raw MIDI event hook + * + * CAUTION: Don't call any libADLMIDI API functions from off this hook directly! + * Suggestion: Use boolean variables to mark the fact this hook got been called, and then, + * apply your action outside of this hook, for example, in the next after audio output call. + * + * @param device Instance of the library + * @param rawEventHook Pointer to the callback function which will be called on every MIDI event + * @param userData Pointer to user data which will be passed through the callback. + */ +extern ADLMIDI_DECLSPEC void adl_setRawEventHook(struct ADL_MIDIPlayer *device, ADL_RawEventHook rawEventHook, void *userData); + +/** + * @brief Set note hook + * + * CAUTION: Don't call any libADLMIDI API functions from off this hook directly! + * Suggestion: Use boolean variables to mark the fact this hook got been called, and then, + * apply your action outside of this hook, for example, in the next after audio output call. + * + * @param device Instance of the library + * @param noteHook Pointer to the callback function which will be called on every noteOn MIDI event + * @param userData Pointer to user data which will be passed through the callback. + */ +extern ADLMIDI_DECLSPEC void adl_setNoteHook(struct ADL_MIDIPlayer *device, ADL_NoteHook noteHook, void *userData); + +/** + * @brief Set debug message hook + * + * CAUTION: Don't call any libADLMIDI API functions from off this hook directly! + * Suggestion: Use boolean variables to mark the fact this hook got been called, and then, + * apply your action outside of this hook, for example, in the next after audio output call. + * + * @param device Instance of the library + * @param debugMessageHook Pointer to the callback function which will be called on every debug message + * @param userData Pointer to user data which will be passed through the callback. + */ +extern ADLMIDI_DECLSPEC void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_DebugMessageHook debugMessageHook, void *userData); + +/** + * @brief Set the look start point hook + * + * CAUTION: Don't call any libADLMIDI API functions from off this hook directly! + * Suggestion: Use boolean variables to mark the fact this hook got been called, and then, + * apply your action outside of this hook, for example, in the next after audio output call. + * + * @param device Instance of the library + * @param loopStartHook Pointer to the callback function which will be called on every loop start point passing + * @param userData Pointer to user data which will be passed through the callback. + */ +extern ADLMIDI_DECLSPEC void adl_setLoopStartHook(struct ADL_MIDIPlayer *device, ADL_LoopPointHook loopStartHook, void *userData); + +/** + * @brief Set the look start point hook + * + * CAUTION: Don't call any libADLMIDI API functions from off this hook directly! + * Suggestion: Use boolean variables to mark the fact this hook got been called, and then, + * apply your action outside of this hook, for example, in the next after audio output call. + * + * If you want to switch the song after calling this hook, suggested to call the function + * adl_setLoopHooksOnly(device, 1) to immediately stop the song on reaching the loop point + * + * @param device Instance of the library + * @param loopStartHook Pointer to the callback function which will be called on every loop start point passing + * @param userData Pointer to user data which will be passed through the callback. + */ +extern ADLMIDI_DECLSPEC void adl_setLoopEndHook(struct ADL_MIDIPlayer *device, ADL_LoopPointHook loopEndHook, void *userData); + +/** + * @brief Get a textual description of the channel state. For display only. + * @param device Instance of the library + * @param text Destination char buffer for channel usage state. Every entry is assigned to the chip channel. + * @param attr Destination char buffer for additional attributes like MIDI channel number that uses this chip channel. + * @param size Size of given buffers (both text and attr are must have same size!) + * @return 0 on success, <0 when any error has occurred + * + * Every character in the `text` buffer means the type of usage: + * ``` + * `-` - channel is unused (free) + * `+` - channel is used by two-operator voice + * `#` - channel is used by four-operator voice + * `@` - channel is used to play automatic arpeggio on chip channels overflow + * `r` - rhythm-mode channel note + * ``` + * + * The `attr` field receives the MIDI channel from which the chip channel is used. + * To get the valid MIDI channel you will need to apply the & 0x0F mask to every value. + */ +extern ADLMIDI_DECLSPEC int adl_describeChannels(struct ADL_MIDIPlayer *device, char *text, char *attr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* ADLMIDI_H */ diff --git a/thirdparty/adlmidi/adlmidi_bankmap.h b/thirdparty/adlmidi/adlmidi_bankmap.h new file mode 100644 index 000000000..4a17b459e --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_bankmap.h @@ -0,0 +1,127 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_BANKMAP_H +#define ADLMIDI_BANKMAP_H + +#include +#include +#include +#include + +#include "adlmidi_ptr.hpp" + +/** + * A simple hash map which accepts bank numbers as keys, can be reserved to a + * fixed size, offers O(1) search and insertion, has a hash function to + * optimize for the worst case, and has some good cache locality properties. + */ +template +class BasicBankMap +{ +public: + typedef size_t key_type; /* the bank identifier */ + typedef T mapped_type; + typedef std::pair value_type; + + BasicBankMap(); + void reserve(size_t capacity); + + size_t size() const + { return m_size; } + size_t capacity() const + { return m_capacity; } + bool empty() const + { return m_size == 0; } + + class iterator; + iterator begin() const; + iterator end() const; + + struct do_not_expand_t {}; + + iterator find(key_type key); + void erase(iterator it); + std::pair insert(const value_type &value); + std::pair insert(const value_type &value, do_not_expand_t); + void clear(); + + T &operator[](key_type key); + +private: + struct Slot; + enum { minimum_allocation = 4 }; + enum + { + hash_bits = 8, /* worst case # of collisions: 128^2/2^hash_bits */ + hash_buckets = 1 << hash_bits + }; + +public: + class iterator + { + public: + iterator(); + value_type &operator*() const { return slot->value; } + value_type *operator->() const { return &slot->value; } + iterator &operator++(); + bool operator==(const iterator &o) const; + bool operator!=(const iterator &o) const; + void to_ptrs(void *ptrs[3]); + static iterator from_ptrs(void *const ptrs[3]); + private: + Slot **buckets; + Slot *slot; + size_t index; + iterator(Slot **buckets, Slot *slot, size_t index); +#ifdef _MSC_VER + template + friend class BasicBankMap; +#else + friend class BasicBankMap; +#endif + }; + +private: + struct Slot { + Slot *next, *prev; + value_type value; + Slot() : next(NULL), prev(NULL) {} + }; + AdlMIDI_SPtrArray m_buckets; + std::list< AdlMIDI_SPtrArray > m_allocations; + Slot *m_freeslots; + size_t m_size; + size_t m_capacity; + static size_t hash(key_type key); + Slot *allocate_slot(); + Slot *ensure_allocate_slot(); + void free_slot(Slot *slot); + Slot *bucket_find(size_t index, key_type key); + void bucket_add(size_t index, Slot *slot); + void bucket_remove(size_t index, Slot *slot); +}; + +#include "adlmidi_bankmap.tcc" + +#endif // ADLMIDI_BANKMAP_H diff --git a/thirdparty/adlmidi/adlmidi_bankmap.tcc b/thirdparty/adlmidi/adlmidi_bankmap.tcc new file mode 100644 index 000000000..227ca0a06 --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_bankmap.tcc @@ -0,0 +1,283 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_bankmap.h" +#include + +template +inline BasicBankMap::BasicBankMap() + : m_freeslots(NULL), + m_size(0), + m_capacity(0) +{ + m_buckets.reset(new Slot *[hash_buckets]()); +} + +template +inline size_t BasicBankMap::hash(key_type key) +{ + // disregard the 0 high bit in LSB + key = key_type(key & 127) | key_type((key >> 8) << 7); + // take low part as hash value + return key & (hash_buckets - 1); +} + +template +void BasicBankMap::reserve(size_t capacity) +{ + if(m_capacity >= capacity) + return; + + size_t need = capacity - m_capacity; + const size_t minalloc = static_cast(minimum_allocation); + need = (need < minalloc) ? minalloc : need; + + AdlMIDI_SPtrArray slotz; + slotz.reset(new Slot[need]); + m_allocations.push_back(slotz); + m_capacity += need; + + for(size_t i = need; i-- > 0;) + free_slot(&slotz[i]); +} + +template +typename BasicBankMap::iterator +BasicBankMap::begin() const +{ + iterator it(m_buckets.get(), NULL, 0); + while(it.index < hash_buckets && !(it.slot = m_buckets[it.index])) + ++it.index; + return it; +} + +template +typename BasicBankMap::iterator +BasicBankMap::end() const +{ + iterator it(m_buckets.get(), NULL, hash_buckets); + return it; +} + +template +typename BasicBankMap::iterator BasicBankMap::find(key_type key) +{ + size_t index = hash(key); + Slot *slot = bucket_find(index, key); + if(!slot) + return end(); + return iterator(m_buckets.get(), slot, index); +} + +template +void BasicBankMap::erase(iterator it) +{ + bucket_remove(it.index, it.slot); + free_slot(it.slot); + --m_size; +} + +template +inline BasicBankMap::iterator::iterator() + : buckets(NULL), slot(NULL), index(0) +{ +} + +template +inline BasicBankMap::iterator::iterator(Slot **buckets, Slot *slot, size_t index) + : buckets(buckets), slot(slot), index(index) +{ +} + +template +typename BasicBankMap::iterator & +BasicBankMap::iterator::operator++() +{ + if(slot->next) + slot = slot->next; + else { + Slot *slot = NULL; + ++index; + while(index < hash_buckets && !(slot = buckets[index])) + ++index; + this->slot = slot; + } + return *this; +} + +template +bool BasicBankMap::iterator::operator==(const iterator &o) const +{ + return buckets == o.buckets && slot == o.slot && index == o.index; +} + +template +inline bool BasicBankMap::iterator::operator!=(const iterator &o) const +{ + return !operator==(o); +} + +template +void BasicBankMap::iterator::to_ptrs(void *ptrs[3]) +{ + ptrs[0] = buckets; + ptrs[1] = slot; + ptrs[2] = (void *)index; +} + +template +typename BasicBankMap::iterator +BasicBankMap::iterator::from_ptrs(void *const ptrs[3]) +{ + iterator it; + it.buckets = (Slot **)ptrs[0]; + it.slot = (Slot *)ptrs[1]; + it.index = (size_t)ptrs[2]; + return it; +} + +template +std::pair::iterator, bool> +BasicBankMap::insert(const value_type &value) +{ + size_t index = hash(value.first); + Slot *slot = bucket_find(index, value.first); + if(slot) + return std::make_pair(iterator(m_buckets.get(), slot, index), false); + slot = allocate_slot(); + if(!slot) { + reserve(m_capacity + minimum_allocation); + slot = ensure_allocate_slot(); + } + slot->value = value; + bucket_add(index, slot); + ++m_size; + return std::make_pair(iterator(m_buckets.get(), slot, index), true); +} + +template +std::pair::iterator, bool> +BasicBankMap::insert(const value_type &value, do_not_expand_t) +{ + size_t index = hash(value.first); + Slot *slot = bucket_find(index, value.first); + if(slot) + return std::make_pair(iterator(m_buckets.get(), slot, index), false); + slot = allocate_slot(); + if(!slot) + return std::make_pair(end(), false); + slot->value = value; + bucket_add(index, slot); + ++m_size; + return std::make_pair(iterator(m_buckets.get(), slot, index), true); +} + +template +void BasicBankMap::clear() +{ + for(size_t i = 0; i < hash_buckets; ++i) { + Slot *slot = m_buckets[i]; + while (Slot *cur = slot) { + slot = slot->next; + free_slot(cur); + } + m_buckets[i] = NULL; + } + m_size = 0; +} + +template +inline T &BasicBankMap::operator[](key_type key) +{ + return insert(value_type(key, T())).first->second; +} + +template +typename BasicBankMap::Slot * +BasicBankMap::allocate_slot() +{ + Slot *slot = m_freeslots; + if(!slot) + return NULL; + Slot *next = slot->next; + if(next) + next->prev = NULL; + m_freeslots = next; + return slot; +} + +template +inline typename BasicBankMap::Slot * +BasicBankMap::ensure_allocate_slot() +{ + Slot *slot = allocate_slot(); + assert(slot); + return slot; +} + +template +void BasicBankMap::free_slot(Slot *slot) +{ + Slot *next = m_freeslots; + if(next) + next->prev = slot; + slot->prev = NULL; + slot->next = next; + m_freeslots = slot; + m_freeslots->value.second = T(); +} + +template +typename BasicBankMap::Slot * +BasicBankMap::bucket_find(size_t index, key_type key) +{ + Slot *slot = m_buckets[index]; + while(slot && slot->value.first != key) + slot = slot->next; + return slot; +} + +template +void BasicBankMap::bucket_add(size_t index, Slot *slot) +{ + assert(slot); + Slot *next = m_buckets[index]; + if(next) + next->prev = slot; + slot->next = next; + m_buckets[index] = slot; +} + +template +void BasicBankMap::bucket_remove(size_t index, Slot *slot) +{ + assert(slot); + Slot *prev = slot->prev; + Slot *next = slot->next; + if(!prev) + m_buckets[index] = next; + else + prev->next = next; + if(next) + next->prev = prev; +} diff --git a/thirdparty/adlmidi/adlmidi_cvt.hpp b/thirdparty/adlmidi/adlmidi_cvt.hpp new file mode 100644 index 000000000..94149f247 --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_cvt.hpp @@ -0,0 +1,115 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "oplinst.h" +#include "wopl/wopl_file.h" +#include + +template +static void cvt_generic_to_FMIns(OplInstMeta &ins, const WOPLI &in) +{ + ins.voice2_fine_tune = 0.0; + int voice2_fine_tune = in.second_voice_detune; + + if(voice2_fine_tune != 0) + { + // Simulate behavior of DMX second voice detune + ins.voice2_fine_tune = (double)(((voice2_fine_tune + 128) >> 1) - 64) / 32.0; + } + + ins.midiVelocityOffset = in.midi_velocity_offset; + ins.drumTone = in.percussion_key_number; + ins.flags = (in.inst_flags & WOPL_Ins_4op) && (in.inst_flags & WOPL_Ins_Pseudo4op) ? OplInstMeta::Flag_Pseudo4op : 0; + ins.flags|= (in.inst_flags & WOPL_Ins_4op) && ((in.inst_flags & WOPL_Ins_Pseudo4op) == 0) ? OplInstMeta::Flag_Real4op : 0; + ins.flags|= (in.inst_flags & WOPL_Ins_IsBlank) ? OplInstMeta::Flag_NoSound : 0; + ins.flags|= in.inst_flags & WOPL_RhythmModeMask; + + for(size_t op = 0, slt = 0; op < 4; op++, slt++) + { + ins.op[slt].carrier_E862 = + ((static_cast(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm + | ((static_cast(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel + | ((static_cast(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec + | ((static_cast(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM + ins.op[slt].carrier_40 = in.operators[op].ksl_l_40;//KSLL + + op++; + ins.op[slt].modulator_E862 = + ((static_cast(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm + | ((static_cast(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel + | ((static_cast(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec + | ((static_cast(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM + ins.op[slt].modulator_40 = in.operators[op].ksl_l_40;//KSLL + } + + ins.op[0].noteOffset = static_cast(in.note_offset1); + ins.op[0].feedconn = in.fb_conn1_C0; + ins.op[1].noteOffset = static_cast(in.note_offset2); + ins.op[1].feedconn = in.fb_conn2_C0; + + ins.soundKeyOnMs = in.delay_on_ms; + ins.soundKeyOffMs = in.delay_off_ms; +} + +template +static void cvt_FMIns_to_generic(WOPLI &ins, const OplInstMeta &in) +{ + ins.second_voice_detune = 0; + double voice2_fine_tune = in.voice2_fine_tune; + if(voice2_fine_tune != 0) + { + int m = (int)(voice2_fine_tune * 32.0); + m += 64; + m <<= 1; + m -= 128; + ins.second_voice_detune = (uint8_t)m; + } + + ins.midi_velocity_offset = in.midiVelocityOffset; + ins.percussion_key_number = in.drumTone; + ins.inst_flags = (in.flags & (OplInstMeta::Flag_Pseudo4op|OplInstMeta::Flag_Real4op)) ? WOPL_Ins_4op : 0; + ins.inst_flags|= (in.flags & OplInstMeta::Flag_Pseudo4op) ? WOPL_Ins_Pseudo4op : 0; + ins.inst_flags|= (in.flags & OplInstMeta::Flag_NoSound) ? WOPL_Ins_IsBlank : 0; + ins.inst_flags |= in.flags & OplInstMeta::Mask_RhythmMode; + + for(size_t op = 0; op < 4; op++) + { + const OplTimbre &in2op = in.op[(op < 2) ? 0 : 1]; + uint32_t regE862 = ((op & 1) == 0) ? in2op.carrier_E862 : in2op.modulator_E862; + uint8_t reg40 = ((op & 1) == 0) ? in2op.carrier_40 : in2op.modulator_40; + + ins.operators[op].waveform_E0 = static_cast(regE862 >> 24); + ins.operators[op].susrel_80 = static_cast(regE862 >> 16); + ins.operators[op].atdec_60 = static_cast(regE862 >> 8); + ins.operators[op].avekf_20 = static_cast(regE862 >> 0); + ins.operators[op].ksl_l_40 = reg40; + } + + ins.note_offset1 = in.op[0].noteOffset; + ins.fb_conn1_C0 = in.op[0].feedconn; + ins.note_offset2 = in.op[1].noteOffset; + ins.fb_conn2_C0 = in.op[1].feedconn; + + ins.delay_on_ms = in.soundKeyOnMs; + ins.delay_off_ms = in.soundKeyOffMs; +} diff --git a/thirdparty/adlmidi/adlmidi_db.h b/thirdparty/adlmidi/adlmidi_db.h new file mode 100644 index 000000000..85ccf9618 --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_db.h @@ -0,0 +1,98 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef ADLDATA_DB_H +#define ADLDATA_DB_H + +#pragma once + +#include +#include +#include + +#if !defined(_MSC_VER) && !defined(__APPLE__) && !defined(__aarch64__) && !defined(__3DS__) +#define ATTRIB_PACKED __attribute__((__packed__)) +#else +#define ATTRIB_PACKED +#endif + +typedef uint16_t bank_count_t; +typedef int16_t midi_bank_idx_t; + +#ifndef DISABLE_EMBEDDED_BANKS +extern const size_t g_embeddedBanksCount; +#endif + +namespace BanksDump +{ + +struct BankEntry +{ + uint16_t bankSetup; + bank_count_t banksMelodicCount; + bank_count_t banksPercussionCount; + const char *title; + bank_count_t banksOffsetMelodic; + bank_count_t banksOffsetPercussive; +} ATTRIB_PACKED; + +struct MidiBank +{ + uint8_t msb; + uint8_t lsb; + midi_bank_idx_t insts[128]; +} ATTRIB_PACKED; + +struct InstrumentEntry +{ + int16_t noteOffset1; + int16_t noteOffset2; + int8_t midiVelocityOffset; + uint8_t percussionKeyNumber; + uint8_t instFlags; + int8_t secondVoiceDetune; + uint16_t fbConn; + uint16_t delay_on_ms; + uint16_t delay_off_ms; + int16_t ops[4]; +} ATTRIB_PACKED; + +struct Operator +{ + uint32_t d_E862; + uint8_t d_40; +} ATTRIB_PACKED; + +} /* namespace BanksDump */ + +#ifndef DISABLE_EMBEDDED_BANKS +extern const char* const g_embeddedBankNames[]; +extern const BanksDump::BankEntry g_embeddedBanks[]; +extern const size_t g_embeddedBanksMidiIndex[]; +extern const BanksDump::MidiBank g_embeddedBanksMidi[]; +extern const BanksDump::InstrumentEntry g_embeddedBanksInstruments[]; +extern const BanksDump::Operator g_embeddedBanksOperators[]; +#endif + +#endif // ADLDATA_DB_H diff --git a/thirdparty/adlmidi/adlmidi_load.cpp b/thirdparty/adlmidi/adlmidi_load.cpp new file mode 100644 index 000000000..0b07b14ac --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_load.cpp @@ -0,0 +1,306 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_midiplay.hpp" +#include "adlmidi_opl3.hpp" +#include "adlmidi_private.hpp" +#include "adlmidi_cvt.hpp" +#include "file_reader.hpp" +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER +#include "midi_sequencer.hpp" +#endif +#include "wopl/wopl_file.h" + +bool MIDIplay::LoadBank(const std::string &filename) +{ + FileAndMemReader file; + file.openFile(filename.c_str()); + return LoadBank(file); +} + +bool MIDIplay::LoadBank(const void *data, size_t size) +{ + FileAndMemReader file; + file.openData(data, size); + return LoadBank(file); +} + +void cvt_ADLI_to_FMIns(OplInstMeta &ins, const ADL_Instrument &in) +{ + return cvt_generic_to_FMIns(ins, in); +} + +void cvt_FMIns_to_ADLI(ADL_Instrument &ins, const OplInstMeta &in) +{ + cvt_FMIns_to_generic(ins, in); +} + +bool MIDIplay::LoadBank(FileAndMemReader &fr) +{ + int err = 0; + WOPLFile *wopl = NULL; + char *raw_file_data = NULL; + size_t fsize; + if(!fr.isValid()) + { + errorStringOut = "Custom bank: Invalid data stream!"; + return false; + } + + // Read complete bank file into the memory + fsize = fr.fileSize(); + fr.seek(0, FileAndMemReader::SET); + // Allocate necessary memory block + raw_file_data = (char*)malloc(fsize); + if(!raw_file_data) + { + errorStringOut = "Custom bank: Out of memory before of read!"; + return false; + } + fr.read(raw_file_data, 1, fsize); + + // Parse bank file from the memory + wopl = WOPL_LoadBankFromMem((void*)raw_file_data, fsize, &err); + //Free the buffer no more needed + free(raw_file_data); + + // Check for any erros + if(!wopl) + { + switch(err) + { + case WOPL_ERR_BAD_MAGIC: + errorStringOut = "Custom bank: Invalid magic!"; + return false; + case WOPL_ERR_UNEXPECTED_ENDING: + errorStringOut = "Custom bank: Unexpected ending!"; + return false; + case WOPL_ERR_INVALID_BANKS_COUNT: + errorStringOut = "Custom bank: Invalid banks count!"; + return false; + case WOPL_ERR_NEWER_VERSION: + errorStringOut = "Custom bank: Version is newer than supported by this library!"; + return false; + case WOPL_ERR_OUT_OF_MEMORY: + errorStringOut = "Custom bank: Out of memory!"; + return false; + default: + errorStringOut = "Custom bank: Unknown error!"; + return false; + } + } + + Synth &synth = *m_synth; + + synth.setEmbeddedBank(m_setup.bankId); + + synth.m_insBankSetup.scaleModulators = false; + synth.m_insBankSetup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0; + synth.m_insBankSetup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0; + synth.m_insBankSetup.mt32defaults = (wopl->opl_flags & WOPL_FLAG_MT32) != 0; + synth.m_insBankSetup.volumeModel = wopl->volume_model; + m_setup.deepTremoloMode = -1; + m_setup.deepVibratoMode = -1; + m_setup.volumeScaleModel = ADLMIDI_VolumeModel_AUTO; + + uint16_t slots_counts[2] = {wopl->banks_count_melodic, wopl->banks_count_percussion}; + WOPLBank *slots_src_ins[2] = { wopl->banks_melodic, wopl->banks_percussive }; + + for(size_t ss = 0; ss < 2; ss++) + { + for(size_t i = 0; i < slots_counts[ss]; i++) + { + size_t bankno = (slots_src_ins[ss][i].bank_midi_msb * 256) + + (slots_src_ins[ss][i].bank_midi_lsb) + + (ss ? size_t(Synth::PercussionTag) : 0); + Synth::Bank &bank = synth.m_insBanks[bankno]; + for(int j = 0; j < 128; j++) + { + OplInstMeta &ins = bank.ins[j]; + std::memset(&ins, 0, sizeof(OplInstMeta)); + WOPLInstrument &inIns = slots_src_ins[ss][i].ins[j]; + cvt_generic_to_FMIns(ins, inIns); + } + } + } + + synth.m_embeddedBank = Synth::CustomBankTag; // Use dynamic banks! + //Percussion offset is count of instruments multipled to count of melodic banks + applySetup(); + + WOPL_Free(wopl); + + return true; +} + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + +bool MIDIplay::LoadMIDI_pre() +{ +#ifdef DISABLE_EMBEDDED_BANKS + Synth &synth = *m_synth; + if((synth.m_embeddedBank != Synth::CustomBankTag) || synth.m_insBanks.empty()) + { + errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!"; + return false; + } +#endif + /**** Set all properties BEFORE starting of actial file reading! ****/ + resetMIDI(); + applySetup(); + + return true; +} + +bool MIDIplay::LoadMIDI_post() +{ + Synth &synth = *m_synth; + MidiSequencer &seq = *m_sequencer; + MidiSequencer::FileFormat format = seq.getFormat(); + if(format == MidiSequencer::Format_CMF) + { + const std::vector &instruments = seq.getRawCmfInstruments(); + synth.m_insBanks.clear();//Clean up old banks + + uint16_t ins_count = static_cast(instruments.size()); + for(uint16_t i = 0; i < ins_count; ++i) + { + const uint8_t *insData = instruments[i].data; + size_t bank = i / 256; + bank = ((bank & 127) + ((bank >> 7) << 8)); + if(bank > 127 + (127 << 8)) + break; + bank += (i % 256 < 128) ? 0 : size_t(Synth::PercussionTag); + + /*std::printf("Ins %3u: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", + i, InsData[0],InsData[1],InsData[2],InsData[3], InsData[4],InsData[5],InsData[6],InsData[7], + InsData[8],InsData[9],InsData[10],InsData[11], InsData[12],InsData[13],InsData[14],InsData[15]);*/ + OplInstMeta &adlins = synth.m_insBanks[bank].ins[i % 128]; + OplTimbre adl; + adl.modulator_E862 = + ((static_cast(insData[8] & 0x07) << 24) & 0xFF000000) //WaveForm + | ((static_cast(insData[6]) << 16) & 0x00FF0000) //Sustain/Release + | ((static_cast(insData[4]) << 8) & 0x0000FF00) //Attack/Decay + | ((static_cast(insData[0]) << 0) & 0x000000FF); //MultKEVA + adl.carrier_E862 = + ((static_cast(insData[9] & 0x07) << 24) & 0xFF000000) //WaveForm + | ((static_cast(insData[7]) << 16) & 0x00FF0000) //Sustain/Release + | ((static_cast(insData[5]) << 8) & 0x0000FF00) //Attack/Decay + | ((static_cast(insData[1]) << 0) & 0x000000FF); //MultKEVA + adl.modulator_40 = insData[2]; + adl.carrier_40 = insData[3]; + adl.feedconn = insData[10] & 0x0F; + adl.noteOffset = 0; + adlins.op[0] = adl; + adlins.op[1] = adl; + adlins.soundKeyOnMs = 1000; + adlins.soundKeyOffMs = 500; + adlins.drumTone = 0; + adlins.flags = 0; + adlins.voice2_fine_tune = 0.0; + } + + synth.m_embeddedBank = Synth::CustomBankTag; // Ignore AdlBank number, use dynamic banks instead + //std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo); + synth.m_rhythmMode = true; + synth.m_musicMode = Synth::MODE_CMF; + synth.m_volumeScale = Synth::VOLUME_NATIVE; + + synth.m_numChips = 1; + synth.m_numFourOps = 0; + } + else if(format == MidiSequencer::Format_RSXX) + { + //opl.CartoonersVolumes = true; + synth.m_musicMode = Synth::MODE_RSXX; + synth.m_volumeScale = Synth::VOLUME_NATIVE; + + synth.m_numChips = 1; + synth.m_numFourOps = 0; + } + else if(format == MidiSequencer::Format_IMF) + { + //std::fprintf(stderr, "Done reading IMF file\n"); + synth.m_numFourOps = 0; //Don't use 4-operator channels for IMF playing! + synth.m_rhythmMode = false;//Don't enforce rhythm-mode when it's unneeded + synth.m_musicMode = Synth::MODE_IMF; + + synth.m_numChips = 1; + synth.m_numFourOps = 0; + } + else + { + if(format == MidiSequencer::Format_XMIDI) + synth.m_musicMode = Synth::MODE_XMIDI; + + synth.m_numChips = m_setup.numChips; + if(m_setup.numFourOps < 0) + adlCalculateFourOpChannels(this, true); + } + + resetMIDIDefaults(); + + m_setup.tick_skip_samples_delay = 0; + chipReset(); // Reset OPL3 chip + //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously) + m_chipChannels.clear(); + m_chipChannels.resize(synth.m_numChannels); + + return true; +} + +bool MIDIplay::LoadMIDI(const std::string &filename) +{ + FileAndMemReader file; + file.openFile(filename.c_str()); + if(!LoadMIDI_pre()) + return false; + MidiSequencer &seq = *m_sequencer; + if(!seq.loadMIDI(file)) + { + errorStringOut = seq.getErrorString(); + return false; + } + if(!LoadMIDI_post()) + return false; + return true; +} + +bool MIDIplay::LoadMIDI(const void *data, size_t size) +{ + FileAndMemReader file; + file.openData(data, size); + if(!LoadMIDI_pre()) + return false; + MidiSequencer &seq = *m_sequencer; + if(!seq.loadMIDI(file)) + { + errorStringOut = seq.getErrorString(); + return false; + } + if(!LoadMIDI_post()) + return false; + return true; +} + +#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ diff --git a/thirdparty/adlmidi/adlmidi_midiplay.cpp b/thirdparty/adlmidi/adlmidi_midiplay.cpp new file mode 100644 index 000000000..d001a60ef --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_midiplay.cpp @@ -0,0 +1,2082 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_midiplay.hpp" +#include "adlmidi_opl3.hpp" +#include "adlmidi_private.hpp" +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER +#include "midi_sequencer.hpp" +#endif + +// Minimum life time of percussion notes +static const double s_drum_note_min_time = 0.03; + +enum { MasterVolumeDefault = 127 }; + + +inline bool isXgPercChannel(uint8_t msb, uint8_t lsb) +{ + ADL_UNUSED(lsb); + return (msb == 0x7E || msb == 0x7F); +} + +void MIDIplay::AdlChannel::addAge(int64_t us) +{ + const int64_t neg = 1000 * static_cast(-0x1FFFFFFFl); + if(users.empty()) + { + koff_time_until_neglible_us = std::max(koff_time_until_neglible_us - us, neg); + if(koff_time_until_neglible_us < 0) + koff_time_until_neglible_us = 0; + } + else + { + koff_time_until_neglible_us = 0; + for(users_iterator i = users.begin(); !i.is_end(); ++i) + { + LocationData &d = i->value; + if(!d.fixed_sustain) + d.kon_time_until_neglible_us = std::max(d.kon_time_until_neglible_us - us, neg); + d.vibdelay_us += us; + } + } +} + +MIDIplay::MIDIplay(unsigned long sampleRate): + m_cmfPercussionMode(false), + m_sysExDeviceId(0), + m_synthMode(Mode_XG), + m_arpeggioCounter(0) +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + , m_audioTickCounter(0) +#endif +{ + m_midiDevices.clear(); + + m_setup.emulator = adl_getLowestEmulator(); + m_setup.runAtPcmRate = false; + +#ifdef ADLMIDI_ENABLE_HW_SERIAL + m_setup.serial = false; + m_setup.serialName = std::string(); + m_setup.serialBaud = 0; + m_setup.serialProtocol = 0; +#endif + + m_setup.PCM_RATE = sampleRate; + m_setup.mindelay = 1.0 / (double)m_setup.PCM_RATE; + m_setup.maxdelay = 512.0 / (double)m_setup.PCM_RATE; + + m_setup.bankId = 0; + m_setup.numFourOps = -1; +#ifdef ADLMIDI_ENABLE_HW_DOS + m_setup.numChips = 1; +#else + m_setup.numChips = 2; +#endif + m_setup.deepTremoloMode = -1; + m_setup.deepVibratoMode = -1; + m_setup.rhythmMode = -1; + m_setup.logarithmicVolumes = false; + m_setup.volumeScaleModel = ADLMIDI_VolumeModel_AUTO; + //m_setup.SkipForward = 0; + m_setup.scaleModulators = -1; + m_setup.fullRangeBrightnessCC74 = false; + m_setup.enableAutoArpeggio = false; + m_setup.delay = 0.0; + m_setup.carry = 0.0; + m_setup.tick_skip_samples_delay = 0; + + m_synth.reset(new Synth); + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + m_sequencer.reset(new MidiSequencer); + initSequencerInterface(); +#endif + resetMIDI(); + applySetup(); + realTime_ResetState(); +} + +MIDIplay::~MIDIplay() +{} + +void MIDIplay::applySetup() +{ + Synth &synth = *m_synth; + + synth.m_musicMode = Synth::MODE_MIDI; + m_setup.tick_skip_samples_delay = 0; + synth.m_runAtPcmRate = m_setup.runAtPcmRate; + +#ifndef DISABLE_EMBEDDED_BANKS + if(synth.m_embeddedBank != Synth::CustomBankTag) + { + const BanksDump::BankEntry &b = g_embeddedBanks[m_setup.bankId]; + synth.m_insBankSetup.volumeModel = (b.bankSetup & 0x00FF); + synth.m_insBankSetup.deepTremolo = (b.bankSetup >> 8 & 0x0001) != 0; + synth.m_insBankSetup.deepVibrato = (b.bankSetup >> 8 & 0x0002) != 0; + synth.m_insBankSetup.mt32defaults = (b.bankSetup >> 8 & 0x0004) != 0; + } +#endif + + synth.m_deepTremoloMode = m_setup.deepTremoloMode < 0 ? + synth.m_insBankSetup.deepTremolo : + (m_setup.deepTremoloMode != 0); + synth.m_deepVibratoMode = m_setup.deepVibratoMode < 0 ? + synth.m_insBankSetup.deepVibrato : + (m_setup.deepVibratoMode != 0); + synth.m_scaleModulators = m_setup.scaleModulators < 0 ? + synth.m_insBankSetup.scaleModulators : + (m_setup.scaleModulators != 0); + + if(m_setup.logarithmicVolumes) + synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); + else + synth.setVolumeScaleModel(static_cast(m_setup.volumeScaleModel)); + + if(m_setup.volumeScaleModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model + synth.m_volumeScale = (Synth::VolumesScale)synth.m_insBankSetup.volumeModel; + + synth.m_numChips = m_setup.numChips; + m_cmfPercussionMode = false; + + if(m_setup.numFourOps >= 0) + synth.m_numFourOps = m_setup.numFourOps; + else + adlCalculateFourOpChannels(this, true); + + chipReset(); + m_chipChannels.clear(); + m_chipChannels.resize(synth.m_numChannels); + + // Reset the arpeggio counter + m_arpeggioCounter = 0; +} + +void MIDIplay::partialReset() +{ + Synth &synth = *m_synth; + realTime_panic(); + m_setup.tick_skip_samples_delay = 0; + synth.m_runAtPcmRate = m_setup.runAtPcmRate; + chipReset(); + m_chipChannels.clear(); + m_chipChannels.resize((size_t)synth.m_numChannels); + resetMIDIDefaults(); +} + +void MIDIplay::resetMIDI() +{ + Synth &synth = *m_synth; + synth.m_masterVolume = MasterVolumeDefault; + m_sysExDeviceId = 0; + m_synthMode = Mode_XG; + m_arpeggioCounter = 0; + + m_midiChannels.clear(); + m_midiChannels.resize(16, MIDIchannel()); + + resetMIDIDefaults(); + + caugh_missing_instruments.clear(); + caugh_missing_banks_melodic.clear(); + caugh_missing_banks_percussion.clear(); +} + +void MIDIplay::chipReset() +{ + Synth &synth = *m_synth; + +#ifdef ADLMIDI_ENABLE_HW_SERIAL + if(m_setup.serial) + { + synth.resetSerial(m_setup.serialName, m_setup.serialBaud, m_setup.serialProtocol); + return; + } +#endif + + synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); +} + +void MIDIplay::resetMIDIDefaults(int offset) +{ + Synth &synth = *m_synth; + + for(size_t c = offset, n = m_midiChannels.size(); c < n; ++c) + { + MIDIchannel &ch = m_midiChannels[c]; + + if(synth.m_musicMode == Synth::MODE_RSXX) + ch.def_volume = 127; + else if(synth.m_insBankSetup.mt32defaults) + { + ch.def_volume = 127; + ch.def_bendsense_lsb = 0; + ch.def_bendsense_msb = 12; + } + } +} + +void MIDIplay::TickIterators(double s) +{ + Synth &synth = *m_synth; + for(uint32_t c = 0, n = synth.m_numChannels; c < n; ++c) + { + AdlChannel &ch = m_chipChannels[c]; + ch.addAge(static_cast(s * 1e6)); + } + + // Resolve "hell of all times" of too short drum notes + for(size_t c = 0, n = m_midiChannels.size(); c < n; ++c) + { + MIDIchannel &ch = m_midiChannels[c]; + if(ch.extended_note_count == 0) + continue; + + for(MIDIchannel::notes_iterator inext = ch.activenotes.begin(); !inext.is_end();) + { + MIDIchannel::notes_iterator i(inext++); + MIDIchannel::NoteInfo &ni = i->value; + + double ttl = ni.ttl; + if(ttl <= 0) + continue; + + ni.ttl = ttl = ttl - s; + if(ttl <= 0) + { + --ch.extended_note_count; + if(ni.isOnExtendedLifeTime) + { + noteUpdate(c, i, Upd_Off); + ni.isOnExtendedLifeTime = false; + } + } + } + } + + updateVibrato(s); + updateArpeggio(s); + +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) +# ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + s *= m_sequencer->getTempoMultiplier(); // Glide will follow the tempo +# endif + updateGlide(s); +#endif +} + +void MIDIplay::realTime_ResetState() +{ + Synth &synth = *m_synth; + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) + { + MIDIchannel &chan = m_midiChannels[ch]; + chan.resetAllControllers(); + chan.vibpos = 0.0; + chan.lastlrpn = 0; + chan.lastmrpn = 0; + chan.nrpn = false; + if((m_synthMode & Mode_GS) != 0)// Reset custom drum channels on GS + chan.is_xg_percussion = false; + noteUpdateAll(uint16_t(ch), Upd_All); + noteUpdateAll(uint16_t(ch), Upd_Off); + } + synth.m_masterVolume = MasterVolumeDefault; +} + +bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) +{ + Synth &synth = *m_synth; + + if(note >= 128) + note = 127; + + if((synth.m_musicMode == Synth::MODE_RSXX) && (velocity != 0)) + { + // Check if this is just a note after-touch + MIDIchannel::notes_iterator i = m_midiChannels[channel].find_activenote(note); + if(!i.is_end()) + { + MIDIchannel::NoteInfo &ni = i->value; + const int veloffset = ni.ains ? ni.ains->midiVelocityOffset : 0; + velocity = (uint8_t)std::min(127, std::max(1, (int)velocity + veloffset)); + ni.vol = velocity; + if(ni.ains) + noteUpdate(channel, i, Upd_Volume); + return false; + } + } + + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + + //noteOff(channel, note, velocity != 0); + // On Note on, Keyoff the note first, just in case keyoff + // was omitted; this fixes Dance of sugar-plum fairy + // by Microsoft. Now that we've done a Keyoff, + // check if we still need to do a Keyon. + // vol=0 and event 8x are both Keyoff-only. + if(velocity == 0) + { + noteOff(channel, note, false); + return false; + } + + MIDIchannel &midiChan = m_midiChannels[channel]; + + size_t midiins = midiChan.patch; + bool isPercussion = (channel % 16 == 9) || midiChan.is_xg_percussion; + + size_t bank = (midiChan.bank_msb * 256) + midiChan.bank_lsb; + + if(isPercussion) + { + // == XG bank numbers == + // 0x7E00 - XG "SFX Kits" SFX1/SFX2 channel (16128 signed decimal) + // 0x7F00 - XG "Drum Kits" Percussion channel (16256 signed decimal) + + // MIDI instrument defines the patch: + if((m_synthMode & Mode_XG) != 0) + { + // Let XG SFX1/SFX2 bank will go in 128...255 range of LSB in WOPN file) + // Let XG Percussion bank will use (0...127 LSB range in WOPN file) + + // Choose: SFX or Drum Kits + bank = midiins + ((midiChan.bank_msb == 0x7E) ? 128 : 0); + } + else + { + bank = midiins; + } + midiins = note; // Percussion instrument + } + + if(isPercussion) + bank += Synth::PercussionTag; + + const OplInstMeta *ains = &Synth::m_emptyInstrument; + + //Set bank bank + const Synth::Bank *bnk = NULL; + bool caughtMissingBank = false; + if((bank & ~static_cast(Synth::PercussionTag)) > 0) + { + Synth::BankMap::iterator b = synth.m_insBanks.find(bank); + if(b != synth.m_insBanks.end()) + bnk = &b->second; + if(bnk) + ains = &bnk->ins[midiins]; + else + caughtMissingBank = true; + } + + //Or fall back to bank ignoring LSB (GS/XG) + if(ains->flags & OplInstMeta::Flag_NoSound) + { + size_t fallback = bank & ~(size_t)0x7F; + if(fallback != bank) + { + Synth::BankMap::iterator b = synth.m_insBanks.find(fallback); + caughtMissingBank = false; + if(b != synth.m_insBanks.end()) + bnk = &b->second; + if(bnk) + ains = &bnk->ins[midiins]; + else + caughtMissingBank = true; + } + } + + if(caughtMissingBank && hooks.onDebugMessage) + { + std::set &missing = (isPercussion) ? + caugh_missing_banks_percussion : caugh_missing_banks_melodic; + const char *text = (isPercussion) ? + "percussion" : "melodic"; + if(missing.insert(bank).second) + { + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "[%i] Playing missing %s MIDI bank %i (patch %i)", + channel, text, (bank & ~static_cast(Synth::PercussionTag)), midiins); + } + } + + //Or fall back to first bank + if((ains->flags & OplInstMeta::Flag_NoSound) != 0) + { + Synth::BankMap::iterator b = synth.m_insBanks.find(bank & Synth::PercussionTag); + if(b != synth.m_insBanks.end()) + bnk = &b->second; + if(bnk) + ains = &bnk->ins[midiins]; + } + + const int veloffset = ains->midiVelocityOffset; + velocity = (uint8_t)std::min(127, std::max(1, (int)velocity + veloffset)); + + int32_t tone = note; + if(!isPercussion && (bank > 0)) // For non-zero banks + { + if(ains->flags & OplInstMeta::Flag_NoSound) + { + if(hooks.onDebugMessage && caugh_missing_instruments.insert(static_cast(midiins)).second) + { + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "[%i] Caught a blank instrument %i (offset %i) in the MIDI bank %u", + channel, m_midiChannels[channel].patch, midiins, bank); + } + bank = 0; + midiins = midiChan.patch; + } + } + + if(ains->drumTone) + { + if(ains->drumTone >= 128) + tone = ains->drumTone - 128; + else + tone = ains->drumTone; + } + + //uint16_t i[2] = { ains->adlno1, ains->adlno2 }; + bool is_2op = !(ains->flags & (OplInstMeta::Flag_Pseudo4op|OplInstMeta::Flag_Real4op)); + bool pseudo_4op = ains->flags & OplInstMeta::Flag_Pseudo4op; +#ifndef __WATCOMC__ + MIDIchannel::NoteInfo::Phys voices[MIDIchannel::NoteInfo::MaxNumPhysChans] = + { + {0, ains->op[0], false}, + {0, (!is_2op) ? ains->op[1] : ains->op[0], pseudo_4op} + }; +#else /* Unfortunately, Watcom can't brace-initialize structure that incluses structure fields */ + MIDIchannel::NoteInfo::Phys voices[MIDIchannel::NoteInfo::MaxNumPhysChans]; + voices[0].chip_chan = 0; + voices[0].op = ains->op[0]; + voices[0].pseudo4op = false; + voices[1].chip_chan = 0; + voices[1].op = (!is_2op) ? ains->op[1] : ains->op[0]; + voices[1].pseudo4op = pseudo_4op; +#endif /* __WATCOMC__ */ + + bool rm_ains = (ains->flags & OplInstMeta::Mask_RhythmMode) != 0; + bool rm_cmf = m_cmfPercussionMode && (channel >= 11); + + if(synth.m_rhythmMode && (rm_ains || rm_cmf)) + voices[1] = voices[0];//i[1] = i[0]; + + bool isBlankNote = (ains->flags & OplInstMeta::Flag_NoSound) != 0; + + if(hooks.onDebugMessage) + { + if(isBlankNote && caugh_missing_instruments.insert(static_cast(midiins)).second) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing instrument %i", channel, midiins); + } + + if(isBlankNote) + { + // Don't even try to play the blank instrument! But, insert the dummy note. + MIDIchannel::notes_iterator i = midiChan.ensure_create_activenote(note); + MIDIchannel::NoteInfo &dummy = i->value; + dummy.isBlank = true; + dummy.isOnExtendedLifeTime = false; + dummy.ttl = 0; + dummy.ains = NULL; + dummy.chip_channels_count = 0; + // Record the last note on MIDI channel as source of portamento + midiChan.portamentoSource = static_cast(note); + return false; + } + + // Allocate AdLib channel (the physical sound channel for the note) + int32_t adlchannel[MIDIchannel::NoteInfo::MaxNumPhysChans] = { -1, -1 }; + + for(uint32_t ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) + { + int32_t c = -1; + int32_t bs = -0x7FFFFFFFl; + + if(ccount == 1) + { + if(voices[0] == voices[1]) + break; // No secondary channel + if(adlchannel[0] == -1) + break; // No secondary if primary failed + } + else if(!m_setup.enableAutoArpeggio && + (is_2op || pseudo_4op) && + (ains->flags & OplInstMeta::Mask_RhythmMode) == 0) + { + if(killSecondVoicesIfOverflow(c)) + { + adlchannel[0] = c; + adlchannel[1] = -1; + voices[1] = voices[0]; + break; + } + } + + for(size_t a = 0; a < (size_t)synth.m_numChannels; ++a) + { + if(ccount == 1 && static_cast(a) == adlchannel[0]) continue; + // ^ Don't use the same channel for primary&secondary + + if(is_2op || pseudo_4op) + { + // Only use regular channels + uint32_t expected_mode = 0; + + if(synth.m_rhythmMode) + { + if(m_cmfPercussionMode) // CMF + expected_mode = channel < 11 ? OPL3::ChanCat_Regular : (OPL3::ChanCat_Rhythm_Bass + (channel - 11)); + else + { + switch((ains->flags & OplInstMeta::Mask_RhythmMode)) + { + default: + expected_mode = OPL3::ChanCat_Regular; + break; + case OplInstMeta::Flag_RM_BassDrum: + expected_mode = OPL3::ChanCat_Rhythm_Bass; + break; + case OplInstMeta::Flag_RM_Snare: + expected_mode = OPL3::ChanCat_Rhythm_Snare; + break; + case OplInstMeta::Flag_RM_TomTom: + expected_mode = OPL3::ChanCat_Rhythm_Tom; + break; + case OplInstMeta::Flag_RM_Cymbal: + expected_mode = OPL3::ChanCat_Rhythm_Cymbal; + break; + case OplInstMeta::Flag_RM_HiHat: + expected_mode = OPL3::ChanCat_Rhythm_HiHat; + break; + } + } + } + + if(synth.m_channelCategory[a] != expected_mode) + continue; + } + else + { + if(ccount == 0) + { + // Only use four-op master channels + if(synth.m_channelCategory[a] != Synth::ChanCat_4op_First) + continue; + } + else + { + // The secondary must be played on a specific channel. + if(a != static_cast(adlchannel[0]) + 3) + continue; + } + } + + int64_t s = calculateChipChannelGoodness(a, voices[ccount]); + if(s > bs) + { + bs = (int32_t)s; // Best candidate wins + c = static_cast(a); + } + } + + if(c < 0) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "ignored unplaceable note [bank %i, inst %i, note %i, MIDI channel %i]", + bank, midiChan.patch, note, channel); + continue; // Could not play this note. Ignore it. + } + + prepareChipChannelForNewNote(static_cast(c), voices[ccount]); + adlchannel[ccount] = c; + } + + if(adlchannel[0] < 0 && adlchannel[1] < 0) + { + // The note could not be played, at all. + return false; + } + + //if(hooks.onDebugMessage) + // hooks.onDebugMessage(hooks.onDebugMessage_userData, "i1=%d:%d, i2=%d:%d", i[0],adlchannel[0], i[1],adlchannel[1]); + + if(midiChan.softPedal) // Apply Soft Pedal level reducing + velocity = static_cast(std::floor(static_cast(velocity) * 0.8f)); + + // Allocate active note for MIDI channel + MIDIchannel::notes_iterator ir = midiChan.ensure_create_activenote(note); + MIDIchannel::NoteInfo &ni = ir->value; + ni.vol = velocity; + ni.vibrato = midiChan.noteAftertouch[note]; + ni.noteTone = static_cast(tone); + ni.currentTone = tone; + ni.glideRate = HUGE_VAL; + ni.midiins = midiins; + ni.isPercussion = isPercussion; + ni.isBlank = isBlankNote; + ni.isOnExtendedLifeTime = false; + ni.ttl = 0; + ni.ains = ains; + ni.chip_channels_count = 0; + + int8_t currentPortamentoSource = midiChan.portamentoSource; + double currentPortamentoRate = midiChan.portamentoRate; + bool portamentoEnable = + midiChan.portamentoEnable && currentPortamentoRate != HUGE_VAL && !isPercussion; + // Record the last note on MIDI channel as source of portamento + midiChan.portamentoSource = static_cast(note); + // midiChan.portamentoSource = portamentoEnable ? (int8_t)note : (int8_t)-1; + + // Enable gliding on portamento note + if (portamentoEnable && currentPortamentoSource >= 0) + { + ni.currentTone = currentPortamentoSource; + ni.glideRate = currentPortamentoRate; + ++midiChan.gliding_note_count; + } + + // Enable life time extension on percussion note + if (isPercussion) + { + ni.ttl = s_drum_note_min_time; + ++midiChan.extended_note_count; + } + + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) + { + int32_t c = adlchannel[ccount]; + if(c < 0) + continue; + uint16_t chipChan = static_cast(adlchannel[ccount]); + MIDIchannel::NoteInfo::Phys* p = ni.phys_ensure_find_or_create(chipChan); + p->assign(voices[ccount]); + } + + noteUpdate(channel, ir, Upd_All | Upd_Patch); + + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) + { + int32_t c = adlchannel[ccount]; + if(c < 0) + continue; + m_chipChannels[c].recent_ins = voices[ccount]; + m_chipChannels[c].addAge(0); + } + + return true; +} + +void MIDIplay::realTime_NoteOff(uint8_t channel, uint8_t note) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + noteOff(channel, note); +} + +void MIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + MIDIchannel &chan = m_midiChannels[channel]; + MIDIchannel::notes_iterator i = m_midiChannels[channel].find_activenote(note); + if(!i.is_end()) + { + i->value.vibrato = atVal; + } + + uint8_t oldAtVal = chan.noteAftertouch[note % 128]; + if(atVal != oldAtVal) + { + chan.noteAftertouch[note % 128] = atVal; + bool inUse = atVal != 0; + for(unsigned n = 0; !inUse && n < 128; ++n) + inUse = chan.noteAftertouch[n] != 0; + chan.noteAfterTouchInUse = inUse; + } +} + +void MIDIplay::realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].aftertouch = atVal; +} + +void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) +{ + Synth &synth = *m_synth; + + if(value > 127) // Allowed values 0~127 only + value = 127; + + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + + switch(type) + { + case 1: // Adjust vibrato + //UI.PrintLn("%u:vibrato %d", MidCh,value); + m_midiChannels[channel].vibrato = value; + break; + + case 0: // Set bank msb (GM bank) + m_midiChannels[channel].bank_msb = value; + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); + break; + + case 32: // Set bank lsb (XG bank) + m_midiChannels[channel].bank_lsb = value; + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); + break; + + case 5: // Set portamento msb + m_midiChannels[channel].portamento = static_cast((m_midiChannels[channel].portamento & 0x007F) | (value << 7)); + updatePortamento(channel); + break; + + case 37: // Set portamento lsb + m_midiChannels[channel].portamento = static_cast((m_midiChannels[channel].portamento & 0x3F80) | (value)); + updatePortamento(channel); + break; + + case 65: // Enable/disable portamento + m_midiChannels[channel].portamentoEnable = value >= 64; + updatePortamento(channel); + break; + + case 7: // Change volume + m_midiChannels[channel].volume = value; + noteUpdateAll(channel, Upd_Volume); + break; + + case 74: // Change brightness + m_midiChannels[channel].brightness = value; + noteUpdateAll(channel, Upd_Volume); + break; + + case 64: // Enable/disable sustain + m_midiChannels[channel].sustain = (value >= 64); + if(!m_midiChannels[channel].sustain) + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Pedal); + break; + + case 66: // Enable/disable sostenuto + if(value >= 64) //Find notes and mark them as sostenutoed + markSostenutoNotes(channel); + else + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Sostenuto); + break; + + case 67: // Enable/disable soft-pedal + m_midiChannels[channel].softPedal = (value >= 64); + break; + + case 11: // Change expression (another volume factor) + m_midiChannels[channel].expression = value; + noteUpdateAll(channel, Upd_Volume); + break; + + case 10: // Change panning + m_midiChannels[channel].panning = value; + + noteUpdateAll(channel, Upd_Pan); + break; + + case 121: // Reset all controllers + m_midiChannels[channel].resetAllControllers121(); + noteUpdateAll(channel, Upd_Pan + Upd_Volume + Upd_Pitch); + // Kill all sustained notes + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_ANY); + break; + + case 120: // All sounds off + noteUpdateAll(channel, Upd_OffMute); + break; + + case 123: // All notes off + noteUpdateAll(channel, Upd_Off); + break; + + case 91: + break; // Reverb effect depth. We don't do per-channel reverb. + + case 92: + break; // Tremolo effect depth. We don't do... + + case 93: + break; // Chorus effect depth. We don't do. + + case 94: + break; // Celeste effect depth. We don't do. + + case 95: + break; // Phaser effect depth. We don't do. + + case 98: + if(synth.m_musicMode != Synth::MODE_CMF) + { + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = true; + } + break; + + case 99: + if(synth.m_musicMode == Synth::MODE_CMF) + { + // CMF (ctrl 0x63) Depth control + synth.m_deepVibratoMode = (value & 1) != 0; + synth.m_deepTremoloMode = (value & 2) != 0; + synth.commitDeepFlags(); + } + else + { + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = true; + } + break; + + case 100: + if(synth.m_musicMode != Synth::MODE_CMF) + { + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = false; + } + break; + + case 101: + if(synth.m_musicMode != Synth::MODE_CMF) + { + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = false; + } + break; + + case 113: + break; // Related to pitch-bender, used by missimp.mid in Duke3D + + case 6: + setRPN(channel, value, true); + break; + + case 38: + setRPN(channel, value, false); + break; + + case 103: // CMF (ctrl 0x67) rhythm mode + if(synth.m_musicMode == Synth::MODE_CMF) + m_cmfPercussionMode = (value != 0); + break; + + default: + break; + //UI.PrintLn("Ctrl %d <- %d (ch %u)", ctrlno, value, MidCh); + } +} + +void MIDIplay::realTime_PatchChange(uint8_t channel, uint8_t patch) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].patch = patch; +} + +void MIDIplay::realTime_PitchBend(uint8_t channel, uint16_t pitch) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bend = int(pitch) - 8192; + noteUpdateAll(channel, Upd_Pitch); +} + +void MIDIplay::realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bend = int(lsb) + int(msb) * 128 - 8192; + noteUpdateAll(channel, Upd_Pitch); +} + +void MIDIplay::realTime_BankChangeLSB(uint8_t channel, uint8_t lsb) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_lsb = lsb; +} + +void MIDIplay::realTime_BankChangeMSB(uint8_t channel, uint8_t msb) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_msb = msb; +} + +void MIDIplay::realTime_BankChange(uint8_t channel, uint16_t bank) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_lsb = uint8_t(bank & 0xFF); + m_midiChannels[channel].bank_msb = uint8_t((bank >> 8) & 0xFF); +} + +void MIDIplay::setDeviceId(uint8_t id) +{ + m_sysExDeviceId = id; +} + +bool MIDIplay::realTime_SysEx(const uint8_t *msg, size_t size) +{ + if(size < 4 || msg[0] != 0xF0 || msg[size - 1] != 0xF7) + return false; + + unsigned manufacturer = msg[1]; + unsigned dev = msg[2]; + msg += 3; + size -= 4; + + switch(manufacturer) + { + default: + break; + case Manufacturer_UniversalNonRealtime: + case Manufacturer_UniversalRealtime: + return doUniversalSysEx( + dev, manufacturer == Manufacturer_UniversalRealtime, msg, size); + case Manufacturer_Roland: + return doRolandSysEx(dev, msg, size); + case Manufacturer_Yamaha: + return doYamahaSysEx(dev, msg, size); + } + + return false; +} + +bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || dev == m_sysExDeviceId; + if(size < 2 || !devicematch) + return false; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 8) | + (((unsigned)data[1] & 0x7F)); + data += 2; + size -= 2; + + switch(((unsigned)realtime << 16) | address) + { + case (0 << 16) | 0x0901: // GM System On + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System On"); + m_synthMode = Mode_GM; + realTime_ResetState(); + return true; + case (0 << 16) | 0x0902: // GM System Off + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System Off"); + m_synthMode = Mode_XG;//TODO: TEMPORARY, make something RIGHT + realTime_ResetState(); + return true; + case (1 << 16) | 0x0401: // MIDI Master Volume + if(size != 2) + break; + unsigned volume = + (((unsigned)data[0] & 0x7F)) | + (((unsigned)data[1] & 0x7F) << 7); + if(m_synth.get()) + m_synth->m_masterVolume = static_cast(volume >> 7); + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) + noteUpdateAll(uint16_t(ch), Upd_Volume); + return true; + } + + return false; +} + +bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 6 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + unsigned mode = data[1] & 0x7F; + unsigned checksum = data[size - 1] & 0x7F; + data += 2; + size -= 3; + +#if !defined(ADLMIDI_SKIP_ROLAND_CHECKSUM) + { + unsigned checkvalue = 0; + for(size_t i = 0; i < size; ++i) + checkvalue += data[i] & 0x7F; + checkvalue = (128 - (checkvalue & 127)) & 127; + if(checkvalue != checksum) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught invalid roland SysEx message!"); + return false; + } + } +#endif + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + unsigned target_channel = 0; + + /* F0 41 10 42 12 40 00 7F 00 41 F7 */ + + if((address & 0xFFF0FF) == 0x401015) // Turn channel 1 into percussion + { + address = 0x401015; + target_channel = data[1] & 0x0F; + } + + data += 3; + size -= 3; + + if(mode != RolandMode_Send) // don't have MIDI-Out reply ability + return false; + + // Mode Set + // F0 {41 10 42 12} {40 00 7F} {00 41} F7 + + // Custom drum channels + // F0 {41 10 42 12} {40 1 15} { } F7 + + switch((model << 24) | address) + { + case (RolandModel_GS << 24) | 0x00007F: // System Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned mode = data[0] & 0x7F; + ADL_UNUSED(mode);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland System Mode Set: %02X", mode); + m_synthMode = Mode_GS; + realTime_ResetState(); + return true; + } + case (RolandModel_GS << 24) | 0x40007F: // Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned value = data[0] & 0x7F; + ADL_UNUSED(value);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland Mode Set: %02X", value); + m_synthMode = Mode_GS; + realTime_ResetState(); + return true; + } + case (RolandModel_GS << 24) | 0x401015: // Percussion channel + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + if(m_midiChannels.size() < 16) + break; + unsigned value = data[0] & 0x7F; + const uint8_t channels_map[16] = + { + 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15 + }; + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "SysEx: Caught Roland Percussion set: %02X on channel %u (from %X)", + value, channels_map[target_channel], target_channel); + m_midiChannels[channels_map[target_channel]].is_xg_percussion = ((value == 0x01)) || ((value == 0x02)); + return true; + } + } + + return false; +} + +bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 1 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + ++data; + --size; + + switch((model << 8) | (dev & 0xF0)) + { + case (YamahaModel_XG << 8) | 0x10: // parameter change + { + if(size < 3) + break; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + data += 3; + size -= 3; + + switch(address) + { + case 0x00007E: // XG System On + if(size != 1) + break; + unsigned value = data[0] & 0x7F; + ADL_UNUSED(value);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Yamaha XG System On: %02X", value); + m_synthMode = Mode_XG; + realTime_ResetState(); + return true; + } + + break; + } + } + + return false; +} + +void MIDIplay::realTime_panic() +{ + panic(); + killSustainingNotes(-1, -1, AdlChannel::LocationData::Sustain_ANY); +} + +void MIDIplay::realTime_deviceSwitch(size_t track, const char *data, size_t length) +{ + const std::string indata(data, length); + m_currentMidiDevice[track] = chooseDevice(indata); +} + +size_t MIDIplay::realTime_currentDevice(size_t track) +{ + if(m_currentMidiDevice.empty()) + return 0; + return m_currentMidiDevice[track]; +} + +void MIDIplay::realTime_rawOPL(uint8_t reg, uint8_t value) +{ + Synth &synth = *m_synth; + if((reg & 0xF0) == 0xC0) + value |= 0x30; + //std::printf("OPL poke %02X, %02X\n", reg, value); + //std::fflush(stdout); + synth.writeReg(0, reg, value); +} + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +void MIDIplay::AudioTick(uint32_t chipId, uint32_t rate) +{ + if(chipId != 0) // do first chip ticks only + return; + + uint32_t tickNumber = m_audioTickCounter++; + double timeDelta = 1.0 / rate; + + enum { portamentoInterval = 32 }; // for efficiency, set rate limit on pitch updates + + if(tickNumber % portamentoInterval == 0) + { + double portamentoDelta = timeDelta * portamentoInterval; + updateGlide(portamentoDelta); + } +} +#endif + +void MIDIplay::noteUpdate(size_t midCh, + MIDIplay::MIDIchannel::notes_iterator i, + unsigned props_mask, + int32_t select_adlchn) +{ + Synth &synth = *m_synth; + MIDIchannel::NoteInfo &info = i->value; + const int16_t noteTone = info.noteTone; + const double currentTone = info.currentTone; + const uint8_t vol = info.vol; + const int midiins = static_cast(info.midiins); + const OplInstMeta &ains = *info.ains; + AdlChannel::Location my_loc; + my_loc.MidCh = static_cast(midCh); + my_loc.note = info.note; + + if(info.isBlank) + { + if(props_mask & Upd_Off) + m_midiChannels[midCh].activenotes.erase(i); + return; + } + + for(unsigned ccount = 0, ctotal = info.chip_channels_count; ccount < ctotal; ccount++) + { + const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; + uint16_t c = ins.chip_chan; + + if(select_adlchn >= 0 && c != select_adlchn) continue; + + if(props_mask & Upd_Patch) + { + synth.setPatch(c, ins.op); + AdlChannel::users_iterator i = m_chipChannels[c].find_or_create_user(my_loc); + if(!i.is_end()) // inserts if necessary + { + AdlChannel::LocationData &d = i->value; + d.sustained = AdlChannel::LocationData::Sustain_None; + d.vibdelay_us = 0; + d.fixed_sustain = (ains.soundKeyOnMs == static_cast(OPLNoteOnMaxTime)); + d.kon_time_until_neglible_us = 1000 * ains.soundKeyOnMs; + d.ins = ins; + } + } + } + + for(unsigned ccount = 0; ccount < info.chip_channels_count; ccount++) + { + const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; + uint16_t c = ins.chip_chan; + uint16_t c_secondary = info.chip_channels[1].chip_chan; + + if(select_adlchn >= 0 && c != select_adlchn) + continue; + + if(props_mask & Upd_Off) // note off + { + if(!m_midiChannels[midCh].sustain) + { + AdlChannel::users_iterator k = m_chipChannels[c].find_user(my_loc); + bool do_erase_user = (!k.is_end() && ((k->value.sustained & AdlChannel::LocationData::Sustain_Sostenuto) == 0)); + if(do_erase_user) + m_chipChannels[c].users.erase(k); + + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, 0, 0.0); + + if(do_erase_user && m_chipChannels[c].users.empty()) + { + synth.noteOff(c); + if(props_mask & Upd_Mute) // Mute the note + { + synth.touchNote(c, 0, 0, 0); + m_chipChannels[c].koff_time_until_neglible_us = 0; + } + else + { + m_chipChannels[c].koff_time_until_neglible_us = 1000 * int64_t(ains.soundKeyOffMs); + } + } + } + else + { + // Sustain: Forget about the note, but don't key it off. + // Also will avoid overwriting it very soon. + AdlChannel::users_iterator d = m_chipChannels[c].find_or_create_user(my_loc); + if(!d.is_end()) + d->value.sustained |= AdlChannel::LocationData::Sustain_Pedal; // note: not erased! + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, -1, 0.0); + } + + info.phys_erase_at(&ins); // decrements channel count + --ccount; // adjusts index accordingly + continue; + } + + if(props_mask & Upd_Pan) + synth.setPan(c, m_midiChannels[midCh].panning); + + if(props_mask & Upd_Volume) + { + const MIDIchannel &ch = m_midiChannels[midCh]; + bool is_percussion = (midCh == 9) || ch.is_xg_percussion; + uint_fast32_t brightness = ch.brightness; + + if(!m_setup.fullRangeBrightnessCC74) + { + // Simulate post-High-Pass filter result which affects sounding by half level only + if(brightness >= 64) + brightness = 127; + else + brightness *= 2; + } + + synth.touchNote(c, + vol, + ch.volume, + ch.expression, + brightness, + is_percussion); + + /* DEBUG ONLY!!! + static uint32_t max = 0; + + if(volume == 0) + max = 0; + + if(volume > max) + max = volume; + + printf("%d\n", max); + fflush(stdout); + */ + } + + if(props_mask & Upd_Pitch) + { + AdlChannel::users_iterator d = m_chipChannels[c].find_user(my_loc); + + // Don't bend a sustained note + if(d.is_end() || (d->value.sustained == AdlChannel::LocationData::Sustain_None)) + { + MIDIchannel &chan = m_midiChannels[midCh]; + double midibend = chan.bend * chan.bendsense; + double bend = midibend + ins.op.noteOffset; + double phase = 0.0; + uint8_t vibrato = std::max(chan.vibrato, chan.aftertouch); + + vibrato = std::max(vibrato, info.vibrato); + + if((ains.flags & OplInstMeta::Flag_Pseudo4op) && ins.pseudo4op) + { + phase = ains.voice2_fine_tune; + } + + if(vibrato && (d.is_end() || d->value.vibdelay_us >= chan.vibdelay_us)) + bend += static_cast(vibrato) * chan.vibdepth * std::sin(chan.vibpos); + + synth.noteOn(c, c_secondary, currentTone + bend + phase); + + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, vol, midibend); + } + } + } + + if(info.chip_channels_count == 0) + { + m_midiChannels[midCh].cleanupNote(i); + m_midiChannels[midCh].activenotes.erase(i); + } +} + +void MIDIplay::noteUpdateAll(size_t midCh, unsigned props_mask) +{ + for(MIDIchannel::notes_iterator + i = m_midiChannels[midCh].activenotes.begin(); !i.is_end();) + { + MIDIchannel::notes_iterator j(i++); + noteUpdate(midCh, j, props_mask); + } +} + +const std::string &MIDIplay::getErrorString() +{ + return errorStringOut; +} + +void MIDIplay::setErrorString(const std::string &err) +{ + errorStringOut = err; +} + +int64_t MIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const +{ + Synth &synth = *m_synth; + const AdlChannel &chan = m_chipChannels[c]; + int64_t koff_ms = chan.koff_time_until_neglible_us / 1000; + int64_t s = -koff_ms; + ADLMIDI_ChannelAlloc allocType = synth.m_channelAlloc; + + if(allocType == ADLMIDI_ChanAlloc_AUTO) + { + switch(synth.m_musicMode) + { + case Synth::MODE_CMF: + allocType = ADLMIDI_ChanAlloc_SameInst; + break; + + default: + switch(synth.m_volumeScale) + { + case Synth::VOLUME_HMI: + case Synth::VOLUME_HMI_OLD: + allocType = ADLMIDI_ChanAlloc_AnyReleased; // HMI doesn't care about the same instrument + break; + + default: + allocType = ADLMIDI_ChanAlloc_OffDelay; + break; + } + break; + } + } + + // Rate channel with a releasing note + if(s < 0 && chan.users.empty()) + { + bool isSame = (chan.recent_ins == ins); + s -= 40000; + + // If it's same instrument, better chance to get it when no free channels + switch(allocType) + { + case ADLMIDI_ChanAlloc_SameInst: + if(isSame) + s = 0; // Re-use releasing channel with the same instrument + break; + + case ADLMIDI_ChanAlloc_AnyReleased: + s = 0; // Re-use any releasing channel + break; + + default: + case ADLMIDI_ChanAlloc_OffDelay: + if(isSame) + s = -koff_ms; // Wait until releasing sound will complete + break; + } + + return s; + } + + // Same midi-instrument = some stability + for(AdlChannel::const_users_iterator j = chan.users.begin(); !j.is_end(); ++j) + { + const AdlChannel::LocationData &jd = j->value; + + int64_t kon_ms = jd.kon_time_until_neglible_us / 1000; + + s -= (jd.sustained == AdlChannel::LocationData::Sustain_None) ? + (4000000 + kon_ms) : + (500000 + (kon_ms / 2)); + + MIDIchannel::notes_iterator k = const_cast(m_midiChannels[jd.loc.MidCh]).find_activenote(jd.loc.note); + + if(!k.is_end()) + { + const MIDIchannel::NoteInfo &info = k->value; + + // Same instrument = good + if(jd.ins == ins) + { + s += 300; + // Arpeggio candidate = even better + if((jd.vibdelay_us < 70000) || (jd.kon_time_until_neglible_us > 20000000)) + s += 10; + } + + // Percussion is inferior to melody + s += info.isPercussion ? 50 : 0; + /* + if(k->second.midiins >= 25 + && k->second.midiins < 40 + && j->second.ins != ins) + { + s -= 14000; // HACK: Don't clobber the bass or the guitar + } + */ + } + + // If there is another channel to which this note + // can be evacuated to in the case of congestion, + // increase the score slightly. + unsigned n_evacuation_stations = 0; + + for(size_t c2 = 0; c2 < static_cast(synth.m_numChannels); ++c2) + { + if(c2 == c) + continue; + + if(synth.m_channelCategory[c2] != synth.m_channelCategory[c]) + continue; + + for(AdlChannel::const_users_iterator m = m_chipChannels[c2].users.begin(); !m.is_end(); ++m) + { + const AdlChannel::LocationData &md = m->value; + + if(md.sustained != AdlChannel::LocationData::Sustain_None) + continue; + + if(md.vibdelay_us >= 200000) + continue; + + if(md.ins != jd.ins) + continue; + + n_evacuation_stations += 1; + } + } + + s += (int64_t)n_evacuation_stations * 4; + } + + return s; +} + +bool MIDIplay::killSecondVoicesIfOverflow(int32_t &new_chan) +{ + Synth &synth = *m_synth; + bool ret = false; + + int free2op = 0; + int32_t volume = -1; + + for(size_t a = 0; a < (size_t)synth.m_numChannels; ++a) + { + if(synth.m_channelCategory[a] != OPL3::ChanCat_Regular) + continue; + + AdlChannel &chan = m_chipChannels[a]; + + if(chan.users.empty()) + { + ++free2op; + break; // No reason to loop more + } + else if(chan.recent_ins.pseudo4op) + { + AdlChannel::users_iterator j = &chan.users.front(); + AdlChannel::LocationData &jd = j->value; + MIDIchannel &mc = m_midiChannels[jd.loc.MidCh]; + MIDIchannel::notes_iterator nc = mc.find_activenote(jd.loc.note); + int32_t total = mc.volume + mc.expression; + + if(!nc.is_end()) + total += nc->value.vol; + + // Quitest tones has highest priority to be stolen + if(volume < 0 || total < volume) + { + volume = total; + new_chan = a; + } + } + } + + if(!free2op && new_chan >= 0) + { + AdlChannel::users_iterator j = &m_chipChannels[new_chan].users.front(); + AdlChannel::LocationData &jd = j->value; + + m_midiChannels[jd.loc.MidCh].clear_all_phys_users(new_chan); + m_chipChannels[new_chan].users.erase(j); + assert(m_chipChannels[new_chan].users.empty()); // No users should remain! + synth.noteOff(new_chan); + ret = true; + } + + return ret; +} + +void MIDIplay::prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins) +{ + if(m_chipChannels[c].users.empty()) return; // Nothing to do + + Synth &synth = *m_synth; + + if(!m_setup.enableAutoArpeggio) + { + // Kill all notes on this channel with no mercy + for(AdlChannel::users_iterator jnext = m_chipChannels[c].users.begin(); !jnext.is_end();) + { + AdlChannel::users_iterator j = jnext; + AdlChannel::LocationData &jd = j->value; + ++jnext; + + m_midiChannels[jd.loc.MidCh].clear_all_phys_users(c); + m_chipChannels[c].users.erase(j); + } + + synth.noteOff(c); + assert(m_chipChannels[c].users.empty()); // No users should remain! + + return; + } + + //bool doing_arpeggio = false; + for(AdlChannel::users_iterator jnext = m_chipChannels[c].users.begin(); !jnext.is_end();) + { + AdlChannel::users_iterator j = jnext; + AdlChannel::LocationData &jd = jnext->value; + ++jnext; + + if(jd.sustained == AdlChannel::LocationData::Sustain_None) + { + // Collision: Kill old note, + // UNLESS we're going to do arpeggio + MIDIchannel::notes_iterator i (m_midiChannels[jd.loc.MidCh].ensure_find_activenote(jd.loc.note)); + + // Check if we can do arpeggio. + if(((jd.vibdelay_us < 70000) || (jd.kon_time_until_neglible_us > 20000000)) && jd.ins == ins) + { + // Do arpeggio together with this note. + //doing_arpeggio = true; + continue; + } + + killOrEvacuate(c, j, i); + // ^ will also erase j from ch[c].users. + } + } + + // Kill all sustained notes on this channel + // Don't keep them for arpeggio, because arpeggio requires + // an intact "activenotes" record. This is a design flaw. + killSustainingNotes(-1, static_cast(c), AdlChannel::LocationData::Sustain_ANY); + + // Keyoff the channel so that it can be retriggered, + // unless the new note will be introduced as just an arpeggio. + if(m_chipChannels[c].users.empty()) + synth.noteOff(c); +} + +void MIDIplay::killOrEvacuate(size_t from_channel, + AdlChannel::users_iterator j, + MIDIplay::MIDIchannel::notes_iterator i) +{ + Synth &synth = *m_synth; + uint32_t maxChannels = ADL_MAX_CHIPS * 18; + AdlChannel::LocationData &jd = j->value; + MIDIchannel::NoteInfo &info = i->value; + + // Before killing the note, check if it can be + // evacuated to another channel as an arpeggio + // instrument. This helps if e.g. all channels + // are full of strings and we want to do percussion. + // FIXME: This does not care about four-op entanglements. + for(uint32_t c = 0; c < synth.m_numChannels; ++c) + { + uint16_t cs = static_cast(c); + + if(c >= maxChannels) + break; + + if(c == from_channel) + continue; + + if(synth.m_channelCategory[c] != synth.m_channelCategory[from_channel]) + continue; + + AdlChannel &adlch = m_chipChannels[c]; + if(adlch.users.size() == adlch.users.capacity()) + continue; // no room for more arpeggio on channel + + if(!m_chipChannels[cs].find_user(jd.loc).is_end()) + continue; // channel already has this note playing (sustained) + // avoid introducing a duplicate location. + + for(AdlChannel::users_iterator m = adlch.users.begin(); !m.is_end(); ++m) + { + AdlChannel::LocationData &mv = m->value; + + if(mv.vibdelay_us >= 200000 + && mv.kon_time_until_neglible_us < 10000000) continue; + + if(mv.ins != jd.ins) + continue; + + if(hooks.onNote) + { + hooks.onNote(hooks.onNote_userData, + (int)from_channel, + info.noteTone, + static_cast(info.midiins), 0, 0.0); + hooks.onNote(hooks.onNote_userData, + (int)c, + info.noteTone, + static_cast(info.midiins), + info.vol, 0.0); + } + + info.phys_erase(static_cast(from_channel)); + info.phys_ensure_find_or_create(cs)->assign(jd.ins); + m_chipChannels[cs].users.push_back(jd); +#ifndef NDEBUG + if(!m_setup.enableAutoArpeggio) + assert(m_chipChannels[cs].users.size() <= 1); +#endif + m_chipChannels[from_channel].users.erase(j); + return; + } + } + + // Kill it + noteUpdate(jd.loc.MidCh, + i, + Upd_Off, + static_cast(from_channel)); +} + +void MIDIplay::panic() +{ + for(uint8_t chan = 0; chan < m_midiChannels.size(); chan++) + { + for(uint8_t note = 0; note < 128; note++) + realTime_NoteOff(chan, note); + } +} + +void MIDIplay::killSustainingNotes(int32_t midCh, int32_t this_adlchn, uint32_t sustain_type) +{ + Synth &synth = *m_synth; + uint32_t first = 0, last = synth.m_numChannels; + + if(this_adlchn >= 0) + { + first = static_cast(this_adlchn); + last = first + 1; + } + + for(uint32_t c = first; c < last; ++c) + { + if(m_chipChannels[c].users.empty()) + continue; // Nothing to do + + for(AdlChannel::users_iterator jnext = m_chipChannels[c].users.begin(); !jnext.is_end();) + { + AdlChannel::users_iterator j = jnext; + AdlChannel::LocationData &jd = j->value; + ++jnext; + + if((midCh < 0 || jd.loc.MidCh == midCh) + && ((jd.sustained & sustain_type) != 0)) + { + int midiins = '?'; + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, (int)c, jd.loc.note, midiins, 0, 0.0); + + jd.sustained &= ~sustain_type; + + if(jd.sustained == AdlChannel::LocationData::Sustain_None) + m_chipChannels[c].users.erase(j);//Remove only when note is clean from any holders + } + } + + // Keyoff the channel, if there are no users left. + if(m_chipChannels[c].users.empty()) + synth.noteOff(c); + } +} + +void MIDIplay::markSostenutoNotes(int32_t midCh) +{ + Synth &synth = *m_synth; + uint32_t first = 0, last = synth.m_numChannels; + + for(uint32_t c = first; c < last; ++c) + { + if(m_chipChannels[c].users.empty()) + continue; // Nothing to do + + for(AdlChannel::users_iterator jnext = m_chipChannels[c].users.begin(); !jnext.is_end();) + { + AdlChannel::users_iterator j = jnext; + AdlChannel::LocationData &jd = j->value; + ++jnext; + + if((jd.loc.MidCh == midCh) && (jd.sustained == AdlChannel::LocationData::Sustain_None)) + jd.sustained |= AdlChannel::LocationData::Sustain_Sostenuto; + } + } +} + +void MIDIplay::setRPN(size_t midCh, unsigned value, bool MSB) +{ + bool nrpn = m_midiChannels[midCh].nrpn; + unsigned addr = m_midiChannels[midCh].lastmrpn * 0x100 + m_midiChannels[midCh].lastlrpn; + + switch(addr + nrpn * 0x10000 + MSB * 0x20000) + { + case 0x0000 + 0*0x10000 + 1*0x20000: // Pitch-bender sensitivity + m_midiChannels[midCh].bendsense_msb = value; + m_midiChannels[midCh].updateBendSensitivity(); + break; + case 0x0000 + 0*0x10000 + 0*0x20000: // Pitch-bender sensitivity LSB + m_midiChannels[midCh].bendsense_lsb = value; + m_midiChannels[midCh].updateBendSensitivity(); + break; + case 0x0108 + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato speed + { + if(value == 64) m_midiChannels[midCh].vibspeed = 1.0; + else if(value < 100) m_midiChannels[midCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1)); + else m_midiChannels[midCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385); + m_midiChannels[midCh].vibspeed *= 2 * 3.141592653 * 5.0; + } + break; + case 0x0109 + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato depth + { + m_midiChannels[midCh].vibdepth = (((int)value - 64) * 0.15) * 0.01; + } + break; + case 0x010A + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato delay in millisecons + { + m_midiChannels[midCh].vibdelay_us = value ? int64_t(209.2 * std::exp(0.0795 * (double)value)) : 0; + } + break; + default:/* UI.PrintLn("%s %04X <- %d (%cSB) (ch %u)", + "NRPN"+!nrpn, addr, value, "LM"[MSB], MidCh);*/ + break; + } +} + +void MIDIplay::updatePortamento(size_t midCh) +{ + double rate = HUGE_VAL; + uint16_t midival = m_midiChannels[midCh].portamento; + if(m_midiChannels[midCh].portamentoEnable && midival > 0) + rate = 350.0 * std::pow(2.0, -0.062 * (1.0 / 128) * midival); + m_midiChannels[midCh].portamentoRate = rate; +} + + +void MIDIplay::noteOff(size_t midCh, uint8_t note, bool forceNow) +{ + MIDIchannel &ch = m_midiChannels[midCh]; + MIDIchannel::notes_iterator i = ch.find_activenote(note); + + if(!i.is_end()) + { + MIDIchannel::NoteInfo &ni = i->value; + if(forceNow || ni.ttl <= 0) + noteUpdate(midCh, i, Upd_Off); + else + ni.isOnExtendedLifeTime = true; + } +} + + +void MIDIplay::updateVibrato(double amount) +{ + for(size_t a = 0, b = m_midiChannels.size(); a < b; ++a) + { + if(m_midiChannels[a].hasVibrato() && !m_midiChannels[a].activenotes.empty()) + { + noteUpdateAll(static_cast(a), Upd_Pitch); + m_midiChannels[a].vibpos += amount * m_midiChannels[a].vibspeed; + } + else + m_midiChannels[a].vibpos = 0.0; + } +} + +size_t MIDIplay::chooseDevice(const std::string &name) +{ + std::map::iterator i = m_midiDevices.find(name); + + if(i != m_midiDevices.end()) + return i->second; + + size_t n = m_midiDevices.size() * 16; + m_midiDevices.insert(std::make_pair(name, n)); + m_midiChannels.resize(n + 16); + resetMIDIDefaults(static_cast(n)); + return n; +} + +void MIDIplay::updateArpeggio(double) // amount = amount of time passed +{ + // If there is an adlib channel that has multiple notes + // simulated on the same channel, arpeggio them. + + Synth &synth = *m_synth; + + if(!m_setup.enableAutoArpeggio) // Arpeggio was disabled + { + if(m_arpeggioCounter != 0) + m_arpeggioCounter = 0; + return; + } + +#if 0 + const unsigned desired_arpeggio_rate = 40; // Hz (upper limit) +# if 1 + static unsigned cache = 0; + amount = amount; // Ignore amount. Assume we get a constant rate. + cache += MaxSamplesAtTime * desired_arpeggio_rate; + + if(cache < PCM_RATE) return; + + cache %= PCM_RATE; +# else + static double arpeggio_cache = 0; + arpeggio_cache += amount * desired_arpeggio_rate; + + if(arpeggio_cache < 1.0) return; + + arpeggio_cache = 0.0; +# endif +#endif + + ++m_arpeggioCounter; + + for(uint32_t c = 0; c < synth.m_numChannels; ++c) + { +retry_arpeggio: + if(c > uint32_t(std::numeric_limits::max())) + break; + + size_t n_users = m_chipChannels[c].users.size(); + + if(n_users > 1) + { + AdlChannel::users_iterator i = m_chipChannels[c].users.begin(); + size_t rate_reduction = 3; + + if(n_users >= 3) + rate_reduction = 2; + + if(n_users >= 4) + rate_reduction = 1; + + for(size_t count = (m_arpeggioCounter / rate_reduction) % n_users, + n = 0; n < count; ++n) + ++i; + + AdlChannel::LocationData &d = i->value; + if(d.sustained == AdlChannel::LocationData::Sustain_None) + { + if(d.kon_time_until_neglible_us <= 0) + { + noteUpdate( + d.loc.MidCh, + m_midiChannels[ d.loc.MidCh ].ensure_find_activenote(d.loc.note), + Upd_Off, + static_cast(c)); + goto retry_arpeggio; + } + + noteUpdate( + d.loc.MidCh, + m_midiChannels[ d.loc.MidCh ].ensure_find_activenote(d.loc.note), + Upd_Pitch | Upd_Volume | Upd_Pan, + static_cast(c)); + } + } + } +} + +void MIDIplay::updateGlide(double amount) +{ + size_t num_channels = m_midiChannels.size(); + + for(size_t channel = 0; channel < num_channels; ++channel) + { + MIDIchannel &midiChan = m_midiChannels[channel]; + + if(midiChan.gliding_note_count == 0) + continue; + + for(MIDIchannel::notes_iterator it = midiChan.activenotes.begin(); + !it.is_end(); ++it) + { + MIDIchannel::NoteInfo &info = it->value; + double finalTone = info.noteTone; + double previousTone = info.currentTone; + + bool directionUp = previousTone < finalTone; + double toneIncr = amount * (directionUp ? +info.glideRate : -info.glideRate); + + double currentTone = previousTone + toneIncr; + bool glideFinished = !(directionUp ? (currentTone < finalTone) : (currentTone > finalTone)); + currentTone = glideFinished ? finalTone : currentTone; + + if(currentTone != previousTone) + { + info.currentTone = currentTone; + noteUpdate(static_cast(channel), it, Upd_Pitch); + } + } + } +} + +void MIDIplay::describeChannels(char *str, char *attr, size_t size) +{ + if (!str || size <= 0) + return; + + Synth &synth = *m_synth; + uint32_t numChannels = synth.m_numChannels; + + uint32_t index = 0, index_out = 0; + + while(index < numChannels && index_out < size - 1) + { + const AdlChannel &adlChannel = m_chipChannels[index]; + + AdlChannel::const_users_iterator loc = adlChannel.users.begin(); + AdlChannel::const_users_iterator locnext(loc); + + if(!loc.is_end()) + ++locnext; + + if(synth.m_channelCategory[index] == Synth::ChanCat_None) + { + ++index; // Skip OPL2-absent channels + continue; + } + + if(loc.is_end()) // off + str[index_out] = '-'; + else if(!locnext.is_end()) // arpeggio + str[index_out] = '@'; + else // on + { + switch(synth.m_channelCategory[index]) + { + case Synth::ChanCat_Regular: + str[index_out] = '+'; + break; + case Synth::ChanCat_4op_First: + case Synth::ChanCat_4op_Second: + str[index_out] = '#'; + break; + default: // rhythm-mode percussion + str[index_out] = 'r'; + break; + } + } + + uint8_t attribute = 0; + if(!loc.is_end()) // 4-bit color index of MIDI channel + attribute |= (uint8_t)(loc->value.loc.MidCh & 0xF); + + attr[index_out] = (char)attribute; + ++index; + ++index_out; + } + + str[index_out] = 0; + attr[index_out] = 0; +} diff --git a/thirdparty/adlmidi/adlmidi_midiplay.hpp b/thirdparty/adlmidi/adlmidi_midiplay.hpp new file mode 100644 index 000000000..bcd76a179 --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_midiplay.hpp @@ -0,0 +1,1094 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_MIDIPLAY_HPP +#define ADLMIDI_MIDIPLAY_HPP + +#include "oplinst.h" +#include "adlmidi_private.hpp" +#include "adlmidi_ptr.hpp" +#include "structures/pl_list.hpp" + +/** + * @brief Hooks of the internal events + */ +struct MIDIEventHooks +{ + MIDIEventHooks() : + onNote(NULL), + onNote_userData(NULL), + onLoopStart(NULL), + onLoopStart_userData(NULL), + onLoopEnd(NULL), + onLoopEnd_userData(NULL), + onDebugMessage(NULL), + onDebugMessage_userData(NULL) + {} + + //! Note on/off hooks + typedef void (*NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); + NoteHook onNote; + void *onNote_userData; + + // Loop start/end hooks + ADL_LoopPointHook onLoopStart; + void *onLoopStart_userData; + ADL_LoopPointHook onLoopEnd; + void *onLoopEnd_userData; + + + //! Library internal debug messages + typedef void (*DebugMessageHook)(void *userdata, const char *fmt, ...); + DebugMessageHook onDebugMessage; + void *onDebugMessage_userData; +}; + +class MIDIplay +{ + friend void adl_reset(struct ADL_MIDIPlayer*); +public: + explicit MIDIplay(unsigned long sampleRate = 22050); + ~MIDIplay(); + + void applySetup(); + + void partialReset(); + void resetMIDI(); + +private: + void chipReset(); + void resetMIDIDefaults(int offset = 0); + +public: + /**********************Internal structures and classes**********************/ + + /** + * @brief Persistent settings for each MIDI channel + */ + struct MIDIchannel + { + //! Default MIDI volume + uint8_t def_volume; + //! Default LSB of a bend sensitivity + int def_bendsense_lsb; + //! Default MSB of a bend sensitivity + int def_bendsense_msb; + + //! LSB Bank number + uint8_t bank_lsb, + //! MSB Bank number + bank_msb; + //! Current patch number + uint8_t patch; + //! Volume level + uint8_t volume, + //! Expression level + expression; + //! Panning level + uint8_t panning, + //! Vibrato level + vibrato, + //! Channel aftertouch level + aftertouch; + //! Portamento time + uint16_t portamento; + //! Is Pedal sustain active + bool sustain; + //! Is Soft pedal active + bool softPedal; + //! Is portamento enabled + bool portamentoEnable; + //! Source note number used by portamento + int8_t portamentoSource; // note number or -1 + //! Portamento rate + double portamentoRate; + //! Per note Aftertouch values + uint8_t noteAftertouch[128]; + //! Is note aftertouch has any non-zero value + bool noteAfterTouchInUse; + //! Reserved + char _padding[6]; + //! Pitch bend value + int bend; + //! Pitch bend sensitivity + double bendsense; + //! Pitch bend sensitivity LSB value + int bendsense_lsb, + //! Pitch bend sensitivity MSB value + bendsense_msb; + //! Vibrato position value + double vibpos, + //! Vibrato speed value + vibspeed, + //! Vibrato depth value + vibdepth; + //! Vibrato delay time + int64_t vibdelay_us; + //! Last LSB part of RPN value received + uint8_t lastlrpn, + //! Last MSB poart of RPN value received + lastmrpn; + //! Interpret RPN value as NRPN + bool nrpn; + //! Brightness level + uint8_t brightness; + + //! Is melodic channel turned into percussion + bool is_xg_percussion; + + /** + * @brief Per-Note information + */ + struct NoteInfo + { + //! Note number + uint8_t note; + //! Current pressure + uint8_t vol; + //! Note vibrato (a part of Note Aftertouch feature) + uint8_t vibrato; + //! Tone selected on noteon: + int16_t noteTone; + //! Current tone (!= noteTone if gliding note) + double currentTone; + //! Gliding rate + double glideRate; + //! Patch selected on noteon; index to bank.ins[] + size_t midiins; + //! Is note the percussion instrument + bool isPercussion; + //! Note that plays missing instrument. Doesn't using any chip channels + bool isBlank; + //! Whether releasing and on extended life time defined by TTL + bool isOnExtendedLifeTime; + //! Time-to-live until release (short percussion note fix) + double ttl; + //! Patch selected + const OplInstMeta *ains; + enum + { + MaxNumPhysChans = 2, + MaxNumPhysItemCount = MaxNumPhysChans + }; + + struct FindPredicate + { + explicit FindPredicate(unsigned note) + : note(note) {} + bool operator()(const NoteInfo &ni) const + { return ni.note == note; } + unsigned note; + }; + + /** + * @brief Reference to currently using chip channel + */ + struct Phys + { + //! Destination chip channel + uint16_t chip_chan; + //! ins, inde to adl[] + OplTimbre op; + //! Is this voice must be detunable? + bool pseudo4op; + + void assign(const Phys &oth) + { + op = oth.op; + pseudo4op = oth.pseudo4op; + } + bool operator==(const Phys &oth) const + { + return (op == oth.op) && (pseudo4op == oth.pseudo4op); + } + bool operator!=(const Phys &oth) const + { + return !operator==(oth); + } + }; + + //! List of OPL3 channels it is currently occupying. + Phys chip_channels[MaxNumPhysItemCount]; + //! Count of used channels. + unsigned chip_channels_count; + + Phys *phys_find(unsigned chip_chan) + { + Phys *ph = NULL; + + for(unsigned i = 0; i < chip_channels_count && !ph; ++i) + { + if(chip_channels[i].chip_chan == chip_chan) + ph = &chip_channels[i]; + } + + return ph; + } + + Phys *phys_find_or_create(uint16_t chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(!ph) { + if(chip_channels_count < MaxNumPhysItemCount) { + ph = &chip_channels[chip_channels_count++]; + ph->chip_chan = chip_chan; + } + } + return ph; + } + + Phys *phys_ensure_find_or_create(uint16_t chip_chan) + { + Phys *ph = phys_find_or_create(chip_chan); + assert(ph); + return ph; + } + + void phys_erase_at(const Phys *ph) + { + intptr_t pos = ph - chip_channels; + assert(pos >= 0 && pos < static_cast(chip_channels_count)); + for(intptr_t i = pos + 1; i < static_cast(chip_channels_count); ++i) + chip_channels[i - 1] = chip_channels[i]; + --chip_channels_count; + } + + void phys_erase(unsigned chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(ph) + phys_erase_at(ph); + } + }; + + //! Reserved + char _padding2[5]; + //! Count of gliding notes in this channel + unsigned gliding_note_count; + //! Count of notes having a TTL countdown in this channel + unsigned extended_note_count; + + //! Active notes in the channel + pl_list activenotes; + typedef pl_list::iterator notes_iterator; + typedef pl_list::const_iterator const_notes_iterator; + + void clear_all_phys_users(unsigned chip_chan) + { + for(pl_list::iterator it = activenotes.begin(); it != activenotes.end(); ) + { + NoteInfo::Phys *p = it->value.phys_find(chip_chan); + if(p) + { + it->value.phys_erase_at(p); + if(it->value.chip_channels_count == 0) + it = activenotes.erase(it); + else + ++it; + } + else + ++it; + } + } + + notes_iterator find_activenote(unsigned note) + { + return activenotes.find_if(NoteInfo::FindPredicate(note)); + } + + notes_iterator ensure_find_activenote(unsigned note) + { + notes_iterator it = find_activenote(note); + assert(!it.is_end()); + return it; + } + + notes_iterator find_or_create_activenote(unsigned note) + { + notes_iterator it = find_activenote(note); + if(!it.is_end()) + cleanupNote(it); + else + { + NoteInfo ni; + ni.note = note; + it = activenotes.insert(activenotes.end(), ni); + } + return it; + } + + notes_iterator create_activenote(unsigned note) + { + NoteInfo ni; + ni.note = note; + notes_iterator it = activenotes.insert(activenotes.end(), ni); + return it; + } + + notes_iterator ensure_find_or_create_activenote(unsigned note) + { + notes_iterator it = find_or_create_activenote(note); + assert(!it.is_end()); + return it; + } + + notes_iterator ensure_create_activenote(unsigned note) + { + notes_iterator it = create_activenote(note); + assert(!it.is_end()); + return it; + } + + /** + * @brief Reset channel into initial state + */ + void reset() + { + resetAllControllers(); + patch = 0; + vibpos = 0; + bank_lsb = 0; + bank_msb = 0; + lastlrpn = 0; + lastmrpn = 0; + nrpn = false; + is_xg_percussion = false; + } + + /** + * @brief Reset all MIDI controllers into initial state + */ + void resetAllControllers() + { + volume = def_volume; + brightness = 127; + panning = 64; + + resetAllControllers121(); + } + + /** + * @brief Reset all MIDI controllers into initial state (CC121) + */ + void resetAllControllers121() + { + bend = 0; + bendsense_msb = def_bendsense_msb; + bendsense_lsb = def_bendsense_lsb; + updateBendSensitivity(); + expression = 127; + sustain = false; + softPedal = false; + vibrato = 0; + aftertouch = 0; + std::memset(noteAftertouch, 0, 128); + noteAfterTouchInUse = false; + vibspeed = 2 * 3.141592653 * 5.0; + vibdepth = 0.5 / 127; + vibdelay_us = 0; + portamento = 0; + portamentoEnable = false; + portamentoSource = -1; + portamentoRate = HUGE_VAL; + } + + /** + * @brief Has channel vibrato to process + * @return + */ + bool hasVibrato() + { + return (vibrato > 0) || (aftertouch > 0) || noteAfterTouchInUse; + } + + /** + * @brief Commit pitch bend sensitivity value from MSB and LSB + */ + void updateBendSensitivity() + { + int cent = bendsense_msb * 128 + bendsense_lsb; + bendsense = cent * (1.0 / (128 * 8192)); + } + + /** + * @brief Clean up the state of the active note before removal + */ + void cleanupNote(notes_iterator i) + { + NoteInfo &info = i->value; + if(info.glideRate != HUGE_VAL) + --gliding_note_count; + if(info.ttl > 0) + --extended_note_count; + } + + MIDIchannel() : + def_volume(100), + def_bendsense_lsb(0), + def_bendsense_msb(2), + activenotes(128) + { + gliding_note_count = 0; + extended_note_count = 0; + reset(); + } + }; + + /** + * @brief Additional information about OPL3 channels + */ + struct AdlChannel + { + struct Location + { + uint16_t MidCh; + uint8_t note; + bool operator==(const Location &l) const + { return MidCh == l.MidCh && note == l.note; } + bool operator!=(const Location &l) const + { return !operator==(l); } + }; + struct LocationData + { + Location loc; + enum { + Sustain_None = 0x00, + Sustain_Pedal = 0x01, + Sustain_Sostenuto = 0x02, + Sustain_ANY = Sustain_Pedal | Sustain_Sostenuto + }; + uint32_t sustained; + char _padding[6]; + MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[] + //! Has fixed sustain, don't iterate "on" timeout + bool fixed_sustain; + //! Timeout until note will be allowed to be killed by channel manager while it is on + int64_t kon_time_until_neglible_us; + int64_t vibdelay_us; + + struct FindPredicate + { + explicit FindPredicate(Location loc) + : loc(loc) {} + bool operator()(const LocationData &ld) const + { return ld.loc == loc; } + Location loc; + }; + }; + + //! Time left until sounding will be muted after key off + int64_t koff_time_until_neglible_us; + + //! Recently passed instrument, improves a goodness of released but busy channel when matching + MIDIchannel::NoteInfo::Phys recent_ins; + + pl_list users; + typedef pl_list::iterator users_iterator; + typedef pl_list::const_iterator const_users_iterator; + + users_iterator find_user(const Location &loc) + { + return users.find_if(LocationData::FindPredicate(loc)); + } + + users_iterator find_or_create_user(const Location &loc) + { + users_iterator it = find_user(loc); + if(it.is_end() && users.size() != users.capacity()) + { + LocationData ld; + ld.loc = loc; + it = users.insert(users.end(), ld); + } + return it; + } + + // For channel allocation: + AdlChannel(): koff_time_until_neglible_us(0), users(128) + { + std::memset(&recent_ins, 0, sizeof(MIDIchannel::NoteInfo::Phys)); + } + + AdlChannel(const AdlChannel &oth): koff_time_until_neglible_us(oth.koff_time_until_neglible_us), users(oth.users) + { + } + + AdlChannel &operator=(const AdlChannel &oth) + { + koff_time_until_neglible_us = oth.koff_time_until_neglible_us; + users = oth.users; + return *this; + } + + /** + * @brief Increases age of active note in microseconds time + * @param us Amount time in microseconds + */ + void addAge(int64_t us); + }; + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + /** + * @brief MIDI files player sequencer + */ + AdlMIDI_UPtr m_sequencer; + + /** + * @brief Interface between MIDI sequencer and this library + */ + AdlMIDI_UPtr m_sequencerInterface; + + /** + * @brief Initialize MIDI sequencer interface + */ + void initSequencerInterface(); +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER + + struct Setup + { + int emulator; + bool runAtPcmRate; + +#ifdef ADLMIDI_ENABLE_HW_SERIAL + bool serial; + std::string serialName; + unsigned int serialBaud; + unsigned int serialProtocol; +#endif + + unsigned int bankId; + int numFourOps; + unsigned int numChips; + int deepTremoloMode; + int deepVibratoMode; + int rhythmMode; + bool logarithmicVolumes; + int volumeScaleModel; + //unsigned int SkipForward; + int scaleModulators; + bool fullRangeBrightnessCC74; + bool enableAutoArpeggio; + + double delay; + double carry; + + /* The lag between visual content and audio content equals */ + /* the sum of these two buffers. */ + double mindelay; + double maxdelay; + + /* For internal usage */ + ssize_t tick_skip_samples_delay; /* Skip tick processing after samples count. */ + /* For internal usage */ + + unsigned long PCM_RATE; + }; + + /** + * @brief MIDI Marker entry + */ + struct MIDI_MarkerEntry + { + //! Label of marker + std::string label; + //! Absolute position in seconds + double pos_time; + //! Absolute position in ticks in the track + uint64_t pos_ticks; + }; + + //! Available MIDI Channels + std::vector m_midiChannels; + + //! CMF Rhythm mode + bool m_cmfPercussionMode; + + //! SysEx device ID + uint8_t m_sysExDeviceId; + + /** + * @brief MIDI Synthesizer mode + */ + enum SynthMode + { + Mode_GM = 0x00, + Mode_GS = 0x01, + Mode_XG = 0x02, + Mode_GM2 = 0x04 + }; + //! MIDI Synthesizer mode + uint32_t m_synthMode; + + //! Installed function hooks + MIDIEventHooks hooks; + +private: + //! Per-track MIDI devices map + std::map m_midiDevices; + //! Current MIDI device per track + std::map m_currentMidiDevice; + + //! Padding to fix CLanc code model's warning + char _padding[7]; + + //! Chip channels map + std::vector m_chipChannels; + //! Counter of arpeggio processing + size_t m_arpeggioCounter; + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + //! Audio tick counter + uint32_t m_audioTickCounter; +#endif + + //! Local error string + std::string errorStringOut; + + //! Missing instruments catches + std::set caugh_missing_instruments; + //! Missing melodic banks catches + std::set caugh_missing_banks_melodic; + //! Missing percussion banks catches + std::set caugh_missing_banks_percussion; + +public: + + const std::string &getErrorString(); + void setErrorString(const std::string &err); + + //! OPL3 Chip manager + AdlMIDI_UPtr m_synth; + + //! Generator output buffer + int32_t m_outBuf[1024]; + + //! Synthesizer setup + Setup m_setup; + + /** + * @brief Load custom bank from file + * @param filename Path to bank file + * @return true on succes + */ + bool LoadBank(const std::string &filename); + + /** + * @brief Load custom bank from memory block + * @param data Pointer to memory block where raw bank file is stored + * @param size Size of given memory block + * @return true on succes + */ + bool LoadBank(const void *data, size_t size); + + /** + * @brief Load custom bank from opened FileAndMemReader class + * @param fr Instance with opened file + * @return true on succes + */ + bool LoadBank(FileAndMemReader &fr); + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + /** + * @brief MIDI file loading pre-process + * @return true on success, false on failure + */ + bool LoadMIDI_pre(); + + /** + * @brief MIDI file loading post-process + * @return true on success, false on failure + */ + bool LoadMIDI_post(); + + /** + * @brief Load music file from a file + * @param filename Path to music file + * @return true on success, false on failure + */ + + bool LoadMIDI(const std::string &filename); + + /** + * @brief Load music file from the memory block + * @param data pointer to the memory block + * @param size size of memory block + * @return true on success, false on failure + */ + bool LoadMIDI(const void *data, size_t size); + + /** + * @brief Periodic tick handler. + * @param s seconds since last call + * @param granularity don't expect intervals smaller than this, in seconds + * @return desired number of seconds until next call + */ + double Tick(double s, double granularity); +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER + + /** + * @brief Process extra iterators like vibrato or arpeggio + * @param s seconds since last call + */ + void TickIterators(double s); + + + /* RealTime event triggers */ + /** + * @brief Reset state of all channels + */ + void realTime_ResetState(); + + /** + * @brief Note On event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param velocity Velocity level (from 0 to 127) + * @return true if Note On event was accepted + */ + bool realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity); + + /** + * @brief Note Off event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + */ + void realTime_NoteOff(uint8_t channel, uint8_t note); + + /** + * @brief Note aftertouch event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param atVal After-Touch level (from 0 to 127) + */ + void realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal); + + /** + * @brief Channel aftertouch event + * @param channel MIDI channel + * @param atVal After-Touch level (from 0 to 127) + */ + void realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal); + + /** + * @brief Controller Change event + * @param channel MIDI channel + * @param type Type of controller + * @param value Value of the controller (from 0 to 127) + */ + void realTime_Controller(uint8_t channel, uint8_t type, uint8_t value); + + /** + * @brief Patch change + * @param channel MIDI channel + * @param patch Patch Number (from 0 to 127) + */ + void realTime_PatchChange(uint8_t channel, uint8_t patch); + + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param pitch Concoctated raw pitch value + */ + void realTime_PitchBend(uint8_t channel, uint16_t pitch); + + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param msb MSB of pitch value + * @param lsb LSB of pitch value + */ + void realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb); + + /** + * @brief LSB Bank Change CC + * @param channel MIDI channel + * @param lsb LSB value of bank number + */ + void realTime_BankChangeLSB(uint8_t channel, uint8_t lsb); + + /** + * @brief MSB Bank Change CC + * @param channel MIDI channel + * @param msb MSB value of bank number + */ + void realTime_BankChangeMSB(uint8_t channel, uint8_t msb); + + /** + * @brief Bank Change (united value) + * @param channel MIDI channel + * @param bank Bank number value + */ + void realTime_BankChange(uint8_t channel, uint16_t bank); + + /** + * @brief Sets the Device identifier + * @param id 7-bit Device identifier + */ + void setDeviceId(uint8_t id); + + /** + * @brief System Exclusive message + * @param msg Raw SysEx Message + * @param size Length of SysEx message + * @return true if message was passed successfully. False on any errors + */ + bool realTime_SysEx(const uint8_t *msg, size_t size); + + /** + * @brief Turn off all notes and mute the sound of releasing notes + */ + void realTime_panic(); + + /** + * @brief Device switch (to extend 16-channels limit of MIDI standard) + * @param track MIDI track index + * @param data Device name + * @param length Length of device name string + */ + void realTime_deviceSwitch(size_t track, const char *data, size_t length); + + /** + * @brief Currently selected device index + * @param track MIDI track index + * @return Multiple 16 value + */ + size_t realTime_currentDevice(size_t track); + + /** + * @brief Send raw OPL chip command + * @param reg OPL Register + * @param value Value to write + */ + void realTime_rawOPL(uint8_t reg, uint8_t value); + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + // Audio rate tick handler + void AudioTick(uint32_t chipId, uint32_t rate); +#endif + +private: + /** + * @brief Hardware manufacturer (Used for SysEx) + */ + enum + { + Manufacturer_Roland = 0x41, + Manufacturer_Yamaha = 0x43, + Manufacturer_UniversalNonRealtime = 0x7E, + Manufacturer_UniversalRealtime = 0x7F + }; + + /** + * @brief Roland Mode (Used for SysEx) + */ + enum + { + RolandMode_Request = 0x11, + RolandMode_Send = 0x12 + }; + + /** + * @brief Device model (Used for SysEx) + */ + enum + { + RolandModel_GS = 0x42, + RolandModel_SC55 = 0x45, + YamahaModel_XG = 0x4C + }; + + /** + * @brief Process generic SysEx events + * @param dev Device ID + * @param realtime Is real-time event + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Roland devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doRolandSysEx(unsigned dev, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Yamaha devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size); + +private: + /** + * @brief Note Update properties + */ + enum + { + Upd_Patch = 0x1, + Upd_Pan = 0x2, + Upd_Volume = 0x4, + Upd_Pitch = 0x8, + Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch, + Upd_Off = 0x20, + Upd_Mute = 0x40, + Upd_OffMute = Upd_Off + Upd_Mute + }; + + /** + * @brief Update active note + * @param MidCh MIDI Channel where note is processing + * @param i Iterator that points to active note in the MIDI channel + * @param props_mask Properties to update + * @param select_adlchn Specify chip channel, or -1 - all chip channels used by the note + */ + void noteUpdate(size_t midCh, + MIDIchannel::notes_iterator i, + unsigned props_mask, + int32_t select_adlchn = -1); + + /** + * @brief Update all notes in specified MIDI channel + * @param midCh MIDI channel to update all notes in it + * @param props_mask Properties to update + */ + void noteUpdateAll(size_t midCh, unsigned props_mask); + + /** + * @brief Determine how good a candidate this adlchannel would be for playing a note from this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * @return Calculated coodness points + */ + int64_t calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const; + + /** + * @brief If no free chip channels, try to kill at least one second voice of pseudo-4-op instruments and steal the released channel + * @param new_chan Value of released chip channel to reuse + * @return true if any channel was been stolen, or false when nothing happen + */ + bool killSecondVoicesIfOverflow(int32_t &new_chan); + + /** + * @brief A new note will be played on this channel using this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * Kill existing notes on this channel (or don't, if we do arpeggio) + */ + void prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); + + /** + * @brief Kills note that uses wanted channel. When arpeggio is possible, note is evaluating to another channel + * @param from_channel Wanted chip channel + * @param j Chip channel instance + * @param i MIDI Channel active note instance + */ + void killOrEvacuate( + size_t from_channel, + AdlChannel::users_iterator j, + MIDIchannel::notes_iterator i); + + /** + * @brief Off all notes and silence sound + */ + void panic(); + + /** + * @brief Kill note, sustaining by pedal or sostenuto + * @param MidCh MIDI channel, -1 - all MIDI channels + * @param this_adlchn Chip channel, -1 - all chip channels + * @param sustain_type Type of systain to process + */ + void killSustainingNotes(int32_t midCh = -1, + int32_t this_adlchn = -1, + uint32_t sustain_type = AdlChannel::LocationData::Sustain_ANY); + /** + * @brief Find active notes and mark them as sostenuto-sustained + * @param MidCh MIDI channel, -1 - all MIDI channels + */ + void markSostenutoNotes(int32_t midCh = -1); + + /** + * @brief Set RPN event value + * @param MidCh MIDI channel + * @param value 1 byte part of RPN value + * @param MSB is MSB or LSB part of value + */ + void setRPN(size_t midCh, unsigned value, bool MSB); + + /** + * @brief Update portamento setup in MIDI channel + * @param midCh MIDI channel where portamento needed to be updated + */ + void updatePortamento(size_t midCh); + + /** + * @brief Off the note + * @param midCh MIDI channel + * @param note Note to off + * @param forceNow Do not delay the key-off to a later time + */ + void noteOff(size_t midCh, uint8_t note, bool forceNow = false); + + /** + * @brief Update processing of vibrato to amount of seconds + * @param amount Amount value in seconds + */ + void updateVibrato(double amount); + + /** + * @brief Update auto-arpeggio + * @param amount Amount value in seconds [UNUSED] + */ + void updateArpeggio(double /*amount*/); + + /** + * @brief Update Portamento gliding to amount of seconds + * @param amount Amount value in seconds + */ + void updateGlide(double amount); + +public: + /** + * @brief Checks was device name used or not + * @param name Name of MIDI device + * @return Offset of the MIDI Channels, multiple to 16 + */ + size_t chooseDevice(const std::string &name); + + /** + * @brief Gets a textual description of the state of chip channels + * @param text character pointer for text + * @param attr character pointer for text attributes + * @param size number of characters available to write + */ + void describeChannels(char *text, char *attr, size_t size); +}; + +#endif // ADLMIDI_MIDIPLAY_HPP diff --git a/thirdparty/adlmidi/adlmidi_opl3.cpp b/thirdparty/adlmidi/adlmidi_opl3.cpp new file mode 100644 index 000000000..dcba85220 --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_opl3.cpp @@ -0,0 +1,2020 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_opl3.hpp" +#include "adlmidi_private.hpp" +#include +#include + + +#ifdef ENABLE_HW_OPL_DOS +# include "chips/dos_hw_opl.h" +#else +# if defined(ADLMIDI_DISABLE_NUKED_EMULATOR) && \ + defined(ADLMIDI_DISABLE_DOSBOX_EMULATOR) && \ + defined(ADLMIDI_DISABLE_OPAL_EMULATOR) && \ + defined(ADLMIDI_DISABLE_JAVA_EMULATOR) && \ + defined(ADLMIDI_DISABLE_ESFMU_EMULATOR) && \ + defined(ADLMIDI_DISABLE_MAME_OPL2_EMULATOR) && \ + defined(ADLMIDI_DISABLE_YMFM_EMULATOR) && \ + !defined(ADLMIDI_ENABLE_OPL2_LLE_EMULATOR) && \ + !defined(ADLMIDI_ENABLE_OPL3_LLE_EMULATOR) && \ + !defined(ADLMIDI_ENABLE_HW_SERIAL) +# error "No emulators enabled. You must enable at least one emulator to use this library!" +# endif + +// Nuked OPL3 emulator, Most accurate, but requires the powerful CPU +# ifndef ADLMIDI_DISABLE_NUKED_EMULATOR +# include "chips/nuked_opl3.h" +# include "chips/nuked_opl3_v174.h" +# endif + +// DosBox 0.74 OPL3 emulator, Well-accurate and fast +# ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR +# include "chips/dosbox_opl3.h" +# endif + +// Opal emulator +# ifndef ADLMIDI_DISABLE_OPAL_EMULATOR +# include "chips/opal_opl3.h" +# endif + +// Java emulator +# ifndef ADLMIDI_DISABLE_JAVA_EMULATOR +# include "chips/java_opl3.h" +# endif + +// ESFMu emulator +# ifndef ADLMIDI_DISABLE_ESFMU_EMULATOR +# include "chips/esfmu_opl3.h" +# endif + +// MAME OPL2 emulator +# ifndef ADLMIDI_DISABLE_MAME_OPL2_EMULATOR +# include "chips/mame_opl2.h" +# endif + +// YMFM emulators +# ifndef ADLMIDI_DISABLE_YMFM_EMULATOR +# include "chips/ymfm_opl2.h" +# include "chips/ymfm_opl3.h" +# endif + +// Nuked OPL2 LLE emulator +# ifdef ADLMIDI_ENABLE_OPL2_LLE_EMULATOR +# include "chips/ym3812_lle.h" +# endif + +// Nuked OPL3 LLE emulator +# ifdef ADLMIDI_ENABLE_OPL3_LLE_EMULATOR +# include "chips/ymf262_lle.h" +# endif + +// HW OPL Serial +# ifdef ADLMIDI_ENABLE_HW_SERIAL +# include "chips/opl_serial_port.h" +# endif +#endif + +static const unsigned adl_emulatorSupport = 0 +#ifndef ENABLE_HW_OPL_DOS +# ifndef ADLMIDI_DISABLE_NUKED_EMULATOR + | (1u << ADLMIDI_EMU_NUKED) | (1u << ADLMIDI_EMU_NUKED_174) +# endif + +# ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR + | (1u << ADLMIDI_EMU_DOSBOX) +# endif + +# ifndef ADLMIDI_DISABLE_OPAL_EMULATOR + | (1u << ADLMIDI_EMU_OPAL) +# endif + +# ifndef ADLMIDI_DISABLE_ESFMU_EMULATOR + | (1u << ADLMIDI_EMU_ESFMu) +# endif + +# ifndef ADLMIDI_DISABLE_JAVA_EMULATOR + | (1u << ADLMIDI_EMU_JAVA) +# endif + +# ifndef ADLMIDI_DISABLE_MAME_OPL2_EMULATOR + | (1u << ADLMIDI_EMU_MAME_OPL2) +# endif + +# ifndef ADLMIDI_DISABLE_YMFM_EMULATOR + | (1u << ADLMIDI_EMU_YMFM_OPL2) + | (1u << ADLMIDI_EMU_YMFM_OPL3) +# endif + +# ifdef ADLMIDI_ENABLE_OPL2_LLE_EMULATOR + | (1u << ADLMIDI_EMU_NUKED_OPL2_LLE) +# endif + +# ifdef ADLMIDI_ENABLE_OPL3_LLE_EMULATOR + | (1u << ADLMIDI_EMU_NUKED_OPL3_LLE) +# endif +#endif +; + +//! Check emulator availability +bool adl_isEmulatorAvailable(int emulator) +{ + return (adl_emulatorSupport & (1u << (unsigned)emulator)) != 0; +} + +//! Find highest emulator +int adl_getHighestEmulator() +{ + int emu = -1; + for(unsigned m = adl_emulatorSupport; m > 0; m >>= 1) + ++emu; + return emu; +} + +//! Find lowest emulator +int adl_getLowestEmulator() +{ + int emu = -1; + unsigned m = adl_emulatorSupport; + if(m > 0) + { + for(emu = 0; (m & 1) == 0; m >>= 1) + ++emu; + } + return emu; +} + +//! Per-channel and per-operator registers map +static const uint16_t g_operatorsMap[(NUM_OF_CHANNELS + NUM_OF_RM_CHANNELS) * 2] = +{ + // Channels 0-2 + 0x000, 0x003, 0x001, 0x004, 0x002, 0x005, // operators 0, 3, 1, 4, 2, 5 + // Channels 3-5 + 0x008, 0x00B, 0x009, 0x00C, 0x00A, 0x00D, // operators 6, 9, 7,10, 8,11 + // Channels 6-8 + 0x010, 0x013, 0x011, 0x014, 0x012, 0x015, // operators 12,15, 13,16, 14,17 + // Same for second card + 0x100, 0x103, 0x101, 0x104, 0x102, 0x105, // operators 18,21, 19,22, 20,23 + 0x108, 0x10B, 0x109, 0x10C, 0x10A, 0x10D, // operators 24,27, 25,28, 26,29 + 0x110, 0x113, 0x111, 0x114, 0x112, 0x115, // operators 30,33, 31,34, 32,35 + + //==For Rhythm-mode percussions + // Channel 18 + 0x010, 0x013, // operators 12,15 + // Channel 19 + 0xFFF, 0x014, // operator 16 + // Channel 19 + 0x012, 0xFFF, // operator 14 + // Channel 19 + 0xFFF, 0x015, // operator 17 + // Channel 19 + 0x011, 0xFFF, // operator 13 + + //==For Rhythm-mode percussions in CMF, snare and cymbal operators has inverted! + 0x010, 0x013, // operators 12,15 + // Channel 19 + 0x014, 0xFFF, // operator 16 + // Channel 19 + 0x012, 0xFFF, // operator 14 + // Channel 19 + 0x015, 0xFFF, // operator 17 + // Channel 19 + 0x011, 0xFFF // operator 13 +}; + +//! Channel map to register offsets +static const uint16_t g_channelsMap[NUM_OF_CHANNELS] = +{ + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, // 0..8 + 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, // 9..17 (secondary set) + 0x006, 0x007, 0x008, 0x008, 0x008 // <- hw percussions, hihats and cymbals using tom-tom's channel as pitch source +}; + +//! Channel map to register offsets (separated copy for panning and for CMF) +static const uint16_t g_channelsMapPan[NUM_OF_CHANNELS] = +{ + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, // 0..8 + 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, // 9..17 (secondary set) + 0x006, 0x007, 0x008, 0xFFF, 0xFFF // <- hw percussions, 0xFFF = no support for pitch/pan +}; + +//! Channel map to register offsets (separated copy for feedback+connection bits) +static const uint16_t g_channelsMapFBConn[NUM_OF_CHANNELS] = +{ + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, // 0..8 + 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, // 9..17 (secondary set) + 0x006, 0xFFF, 0xFFF, 0xFFF, 0xFFF // <- hw percussions, 0xFFF = no support for pitch/pan +}; + +/* + In OPL3 mode: + 0 1 2 6 7 8 9 10 11 16 17 18 + op0 op1 op2 op12 op13 op14 op18 op19 op20 op30 op31 op32 + op3 op4 op5 op15 op16 op17 op21 op22 op23 op33 op34 op35 + 3 4 5 13 14 15 + op6 op7 op8 op24 op25 op26 + op9 op10 op11 op27 op28 op29 + Ports: + +0 +1 +2 +10 +11 +12 +100 +101 +102 +110 +111 +112 + +3 +4 +5 +13 +14 +15 +103 +104 +105 +113 +114 +115 + +8 +9 +A +108 +109 +10A + +B +C +D +10B +10C +10D + + Percussion: + bassdrum = op(0): 0xBD bit 0x10, operators 12 (0x10) and 15 (0x13) / channels 6, 6b + snare = op(3): 0xBD bit 0x08, operators 16 (0x14) / channels 7b + tomtom = op(4): 0xBD bit 0x04, operators 14 (0x12) / channels 8 + cym = op(5): 0xBD bit 0x02, operators 17 (0x17) / channels 8b + hihat = op(2): 0xBD bit 0x01, operators 13 (0x11) / channels 7 + + + In OPTi mode ("extended FM" in 82C924, 82C925, 82C931 chips): + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + op0 op4 op6 op10 op12 op16 op18 op22 op24 op28 op30 op34 op36 op38 op40 op42 op44 op46 + op1 op5 op7 op11 op13 op17 op19 op23 op25 op29 op31 op35 op37 op39 op41 op43 op45 op47 + op2 op8 op14 op20 op26 op32 + op3 op9 op15 op21 op27 op33 for a total of 6 quad + 12 dual + Ports: ??? +*/ + + + + + +/*************************************************************** + * Volume model tables * + ***************************************************************/ + +// Mapping from MIDI volume level to OPL level value. + +static const uint_fast32_t s_dmx_volume_model[128] = +{ + 0, 1, 3, 5, 6, 8, 10, 11, + 13, 14, 16, 17, 19, 20, 22, 23, + 25, 26, 27, 29, 30, 32, 33, 34, + 36, 37, 39, 41, 43, 45, 47, 49, + 50, 52, 54, 55, 57, 59, 60, 61, + 63, 64, 66, 67, 68, 69, 71, 72, + 73, 74, 75, 76, 77, 79, 80, 81, + 82, 83, 84, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 92, 93, 94, 95, + 96, 96, 97, 98, 99, 99, 100, 101, + 101, 102, 103, 103, 104, 105, 105, 106, + 107, 107, 108, 109, 109, 110, 110, 111, + 112, 112, 113, 113, 114, 114, 115, 115, + 116, 117, 117, 118, 118, 119, 119, 120, + 120, 121, 121, 122, 122, 123, 123, 123, + 124, 124, 125, 125, 126, 126, 127, 127, +}; + +static const uint_fast32_t s_w9x_sb16_volume_model[32] = +{ + 80, 63, 40, 36, 32, 28, 23, 21, + 19, 17, 15, 14, 13, 12, 11, 10, + 9, 8, 7, 6, 5, 5, 4, 4, + 3, 3, 2, 2, 1, 1, 0, 0 +}; + +static const uint_fast32_t s_w9x_generic_fm_volume_model[32] = +{ + 40, 36, 32, 28, 23, 21, 19, 17, + 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 5, 4, 4, 3, 3, + 2, 2, 1, 1, 1, 0, 0, 0 +}; + +static const uint_fast32_t s_ail_vel_graph[16] = +{ + 82, 85, 88, 91, 94, 97, 100, 103, + 106, 109, 112, 115, 118, 121, 124, 127 +}; + +static const uint_fast32_t s_hmi_volume_table[64] = +{ + 0x3F, 0x3A, 0x35, 0x30, 0x2C, 0x29, 0x25, 0x24, + 0x23, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, + 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, + 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0E, 0x0D, + 0x0D, 0x0C, 0x0C, 0x0B, 0x0B, 0x0A, 0x0A, 0x09, + 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00, +}; + + + + + +/*************************************************************** + * Standard frequency formula * + * *************************************************************/ + +static inline double s_commonFreq(double tone) +{ + return BEND_COEFFICIENT * std::exp(0.057762265 * tone); +} + + + + +/*************************************************************** + * DMX frequency model * + * *************************************************************/ + +// DMX volumes table +static const int_fast32_t s_dmx_freq_table[] = +{ + 0x0133, 0x0133, 0x0134, 0x0134, 0x0135, 0x0136, 0x0136, 0x0137, + 0x0137, 0x0138, 0x0138, 0x0139, 0x0139, 0x013A, 0x013B, 0x013B, + 0x013C, 0x013C, 0x013D, 0x013D, 0x013E, 0x013F, 0x013F, 0x0140, + 0x0140, 0x0141, 0x0142, 0x0142, 0x0143, 0x0143, 0x0144, 0x0144, + + 0x0145, 0x0146, 0x0146, 0x0147, 0x0147, 0x0148, 0x0149, 0x0149, + 0x014A, 0x014A, 0x014B, 0x014C, 0x014C, 0x014D, 0x014D, 0x014E, + 0x014F, 0x014F, 0x0150, 0x0150, 0x0151, 0x0152, 0x0152, 0x0153, + 0x0153, 0x0154, 0x0155, 0x0155, 0x0156, 0x0157, 0x0157, 0x0158, + + 0x0158, 0x0159, 0x015A, 0x015A, 0x015B, 0x015B, 0x015C, 0x015D, + 0x015D, 0x015E, 0x015F, 0x015F, 0x0160, 0x0161, 0x0161, 0x0162, + 0x0162, 0x0163, 0x0164, 0x0164, 0x0165, 0x0166, 0x0166, 0x0167, + 0x0168, 0x0168, 0x0169, 0x016A, 0x016A, 0x016B, 0x016C, 0x016C, + + 0x016D, 0x016E, 0x016E, 0x016F, 0x0170, 0x0170, 0x0171, 0x0172, + 0x0172, 0x0173, 0x0174, 0x0174, 0x0175, 0x0176, 0x0176, 0x0177, + 0x0178, 0x0178, 0x0179, 0x017A, 0x017A, 0x017B, 0x017C, 0x017C, + 0x017D, 0x017E, 0x017E, 0x017F, 0x0180, 0x0181, 0x0181, 0x0182, + + 0x0183, 0x0183, 0x0184, 0x0185, 0x0185, 0x0186, 0x0187, 0x0188, + 0x0188, 0x0189, 0x018A, 0x018A, 0x018B, 0x018C, 0x018D, 0x018D, + 0x018E, 0x018F, 0x018F, 0x0190, 0x0191, 0x0192, 0x0192, 0x0193, + 0x0194, 0x0194, 0x0195, 0x0196, 0x0197, 0x0197, 0x0198, 0x0199, + + 0x019A, 0x019A, 0x019B, 0x019C, 0x019D, 0x019D, 0x019E, 0x019F, + 0x01A0, 0x01A0, 0x01A1, 0x01A2, 0x01A3, 0x01A3, 0x01A4, 0x01A5, + 0x01A6, 0x01A6, 0x01A7, 0x01A8, 0x01A9, 0x01A9, 0x01AA, 0x01AB, + 0x01AC, 0x01AD, 0x01AD, 0x01AE, 0x01AF, 0x01B0, 0x01B0, 0x01B1, + + 0x01B2, 0x01B3, 0x01B4, 0x01B4, 0x01B5, 0x01B6, 0x01B7, 0x01B8, + 0x01B8, 0x01B9, 0x01BA, 0x01BB, 0x01BC, 0x01BC, 0x01BD, 0x01BE, + 0x01BF, 0x01C0, 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C4, 0x01C4, + 0x01C5, 0x01C6, 0x01C7, 0x01C8, 0x01C9, 0x01C9, 0x01CA, 0x01CB, + + 0x01CC, 0x01CD, 0x01CE, 0x01CE, 0x01CF, 0x01D0, 0x01D1, 0x01D2, + 0x01D3, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7, 0x01D8, 0x01D8, + 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DE, 0x01DF, + 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E4, 0x01E5, 0x01E5, 0x01E6, + + 0x01E7, 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01ED, + 0x01EE, 0x01EF, 0x01F0, 0x01F1, 0x01F2, 0x01F3, 0x01F4, 0x01F5, + 0x01F6, 0x01F6, 0x01F7, 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, + 0x01FD, 0x01FE, 0x01FF, + + 0x0200, 0x0201, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206, + 0x0207, 0x0208, 0x0209, 0x020A, 0x020B, 0x020C, 0x020D, 0x020E, + 0x020F, 0x0210, 0x0210, 0x0211, 0x0212, 0x0213, 0x0214, 0x0215, + 0x0216, 0x0217, 0x0218, 0x0219, 0x021A, 0x021B, 0x021C, 0x021D, + + 0x021E, 0x021F, 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, + 0x0226, 0x0227, 0x0228, 0x0229, 0x022A, 0x022B, 0x022C, 0x022D, + 0x022E, 0x022F, 0x0230, 0x0231, 0x0232, 0x0233, 0x0234, 0x0235, + 0x0236, 0x0237, 0x0238, 0x0239, 0x023A, 0x023B, 0x023C, 0x023D, + + 0x023E, 0x023F, 0x0240, 0x0241, 0x0242, 0x0244, 0x0245, 0x0246, + 0x0247, 0x0248, 0x0249, 0x024A, 0x024B, 0x024C, 0x024D, 0x024E, + 0x024F, 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0256, 0x0257, + 0x0258, 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F, + + 0x0260, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, 0x0268, + 0x0269, 0x026A, 0x026C, 0x026D, 0x026E, 0x026F, 0x0270, 0x0271, + 0x0272, 0x0273, 0x0275, 0x0276, 0x0277, 0x0278, 0x0279, 0x027A, + 0x027B, 0x027D, 0x027E, 0x027F, 0x0280, 0x0281, 0x0282, 0x0284, + + 0x0285, 0x0286, 0x0287, 0x0288, 0x0289, 0x028B, 0x028C, 0x028D, + 0x028E, 0x028F, 0x0290, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, + 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029E, 0x029F, 0x02A0, + 0x02A1, 0x02A2, 0x02A4, 0x02A5, 0x02A6, 0x02A7, 0x02A9, 0x02AA, + + 0x02AB, 0x02AC, 0x02AE, 0x02AF, 0x02B0, 0x02B1, 0x02B2, 0x02B4, + 0x02B5, 0x02B6, 0x02B7, 0x02B9, 0x02BA, 0x02BB, 0x02BD, 0x02BE, + 0x02BF, 0x02C0, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C7, 0x02C8, + 0x02C9, 0x02CB, 0x02CC, 0x02CD, 0x02CE, 0x02D0, 0x02D1, 0x02D2, + + 0x02D4, 0x02D5, 0x02D6, 0x02D8, 0x02D9, 0x02DA, 0x02DC, 0x02DD, + 0x02DE, 0x02E0, 0x02E1, 0x02E2, 0x02E4, 0x02E5, 0x02E6, 0x02E8, + 0x02E9, 0x02EA, 0x02EC, 0x02ED, 0x02EE, 0x02F0, 0x02F1, 0x02F2, + 0x02F4, 0x02F5, 0x02F6, 0x02F8, 0x02F9, 0x02FB, 0x02FC, 0x02FD, + + 0x02FF, 0x0300, 0x0302, 0x0303, 0x0304, 0x0306, 0x0307, 0x0309, + 0x030A, 0x030B, 0x030D, 0x030E, 0x0310, 0x0311, 0x0312, 0x0314, + 0x0315, 0x0317, 0x0318, 0x031A, 0x031B, 0x031C, 0x031E, 0x031F, + 0x0321, 0x0322, 0x0324, 0x0325, 0x0327, 0x0328, 0x0329, 0x032B, + + 0x032C, 0x032E, 0x032F, 0x0331, 0x0332, 0x0334, 0x0335, 0x0337, + 0x0338, 0x033A, 0x033B, 0x033D, 0x033E, 0x0340, 0x0341, 0x0343, + 0x0344, 0x0346, 0x0347, 0x0349, 0x034A, 0x034C, 0x034D, 0x034F, + 0x0350, 0x0352, 0x0353, 0x0355, 0x0357, 0x0358, 0x035A, 0x035B, + + 0x035D, 0x035E, 0x0360, 0x0361, 0x0363, 0x0365, 0x0366, 0x0368, + 0x0369, 0x036B, 0x036C, 0x036E, 0x0370, 0x0371, 0x0373, 0x0374, + 0x0376, 0x0378, 0x0379, 0x037B, 0x037C, 0x037E, 0x0380, 0x0381, + 0x0383, 0x0384, 0x0386, 0x0388, 0x0389, 0x038B, 0x038D, 0x038E, + + 0x0390, 0x0392, 0x0393, 0x0395, 0x0397, 0x0398, 0x039A, 0x039C, + 0x039D, 0x039F, 0x03A1, 0x03A2, 0x03A4, 0x03A6, 0x03A7, 0x03A9, + 0x03AB, 0x03AC, 0x03AE, 0x03B0, 0x03B1, 0x03B3, 0x03B5, 0x03B7, + 0x03B8, 0x03BA, 0x03BC, 0x03BD, 0x03BF, 0x03C1, 0x03C3, 0x03C4, + + 0x03C6, 0x03C8, 0x03CA, 0x03CB, 0x03CD, 0x03CF, 0x03D1, 0x03D2, + 0x03D4, 0x03D6, 0x03D8, 0x03DA, 0x03DB, 0x03DD, 0x03DF, 0x03E1, + 0x03E3, 0x03E4, 0x03E6, 0x03E8, 0x03EA, 0x03EC, 0x03ED, 0x03EF, + 0x03F1, 0x03F3, 0x03F5, 0x03F6, 0x03F8, 0x03FA, 0x03FC, 0x03FE, + + 0x036C +}; + +static inline double s_dmxFreq(double tone) +{ + uint_fast32_t noteI = (uint_fast32_t)(tone); + int_fast32_t bendI = 0; + int_fast32_t outHz = 0; + double bendDec = tone - (int)tone; + + bendI = (int_fast32_t)((bendDec * 128.0) / 2.0) + 128; + bendI = bendI >> 1; + + int_fast32_t oct = 0; + int_fast32_t freqIndex = (noteI << 5) + bendI; + +#define MAX_FREQ_IDX 283 // 284 - with the DMX side bug + if(freqIndex < 0) + freqIndex = 0; + else if(freqIndex >= MAX_FREQ_IDX) + { + freqIndex -= MAX_FREQ_IDX; + oct = freqIndex / 384; + freqIndex = (freqIndex % 384) + MAX_FREQ_IDX; + } +#undef MAX_FREQ_IDX + + outHz = s_dmx_freq_table[freqIndex]; + + while(oct > 1) + { + outHz *= 2; + oct -= 1; + } + + return (double)outHz; +} + + + + +/*************************************************************** + * Apogee Sound System frequency model * + ***************************************************************/ + +static const int_fast32_t s_apogee_freq_table[31 + 1][12] = +{ + { 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287 }, + { 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x242, 0x264, 0x288 }, + { 0x158, 0x16c, 0x182, 0x199, 0x1b1, 0x1cb, 0x1e6, 0x203, 0x221, 0x243, 0x265, 0x289 }, + { 0x158, 0x16c, 0x183, 0x19a, 0x1b2, 0x1cc, 0x1e7, 0x204, 0x222, 0x244, 0x266, 0x28a }, + { 0x159, 0x16d, 0x183, 0x19a, 0x1b3, 0x1cd, 0x1e8, 0x205, 0x223, 0x245, 0x267, 0x28b }, + { 0x15a, 0x16e, 0x184, 0x19b, 0x1b3, 0x1ce, 0x1e9, 0x206, 0x224, 0x246, 0x268, 0x28c }, + { 0x15a, 0x16e, 0x185, 0x19c, 0x1b4, 0x1ce, 0x1ea, 0x207, 0x225, 0x247, 0x269, 0x28e }, + { 0x15b, 0x16f, 0x185, 0x19d, 0x1b5, 0x1cf, 0x1eb, 0x208, 0x226, 0x248, 0x26a, 0x28f }, + { 0x15b, 0x170, 0x186, 0x19d, 0x1b6, 0x1d0, 0x1ec, 0x209, 0x227, 0x249, 0x26b, 0x290 }, + { 0x15c, 0x170, 0x187, 0x19e, 0x1b7, 0x1d1, 0x1ec, 0x20a, 0x228, 0x24a, 0x26d, 0x291 }, + { 0x15d, 0x171, 0x188, 0x19f, 0x1b7, 0x1d2, 0x1ed, 0x20b, 0x229, 0x24b, 0x26e, 0x292 }, + { 0x15d, 0x172, 0x188, 0x1a0, 0x1b8, 0x1d3, 0x1ee, 0x20c, 0x22a, 0x24c, 0x26f, 0x293 }, + { 0x15e, 0x172, 0x189, 0x1a0, 0x1b9, 0x1d4, 0x1ef, 0x20d, 0x22b, 0x24d, 0x270, 0x295 }, + { 0x15f, 0x173, 0x18a, 0x1a1, 0x1ba, 0x1d4, 0x1f0, 0x20e, 0x22c, 0x24e, 0x271, 0x296 }, + { 0x15f, 0x174, 0x18a, 0x1a2, 0x1bb, 0x1d5, 0x1f1, 0x20f, 0x22d, 0x24f, 0x272, 0x297 }, + { 0x160, 0x174, 0x18b, 0x1a3, 0x1bb, 0x1d6, 0x1f2, 0x210, 0x22e, 0x250, 0x273, 0x298 }, + { 0x161, 0x175, 0x18c, 0x1a3, 0x1bc, 0x1d7, 0x1f3, 0x211, 0x22f, 0x251, 0x274, 0x299 }, + { 0x161, 0x176, 0x18c, 0x1a4, 0x1bd, 0x1d8, 0x1f4, 0x212, 0x230, 0x252, 0x276, 0x29b }, + { 0x162, 0x176, 0x18d, 0x1a5, 0x1be, 0x1d9, 0x1f5, 0x212, 0x231, 0x254, 0x277, 0x29c }, + { 0x162, 0x177, 0x18e, 0x1a6, 0x1bf, 0x1d9, 0x1f5, 0x213, 0x232, 0x255, 0x278, 0x29d }, + { 0x163, 0x178, 0x18f, 0x1a6, 0x1bf, 0x1da, 0x1f6, 0x214, 0x233, 0x256, 0x279, 0x29e }, + { 0x164, 0x179, 0x18f, 0x1a7, 0x1c0, 0x1db, 0x1f7, 0x215, 0x235, 0x257, 0x27a, 0x29f }, + { 0x164, 0x179, 0x190, 0x1a8, 0x1c1, 0x1dc, 0x1f8, 0x216, 0x236, 0x258, 0x27b, 0x2a1 }, + { 0x165, 0x17a, 0x191, 0x1a9, 0x1c2, 0x1dd, 0x1f9, 0x217, 0x237, 0x259, 0x27c, 0x2a2 }, + { 0x166, 0x17b, 0x192, 0x1aa, 0x1c3, 0x1de, 0x1fa, 0x218, 0x238, 0x25a, 0x27e, 0x2a3 }, + { 0x166, 0x17b, 0x192, 0x1aa, 0x1c3, 0x1df, 0x1fb, 0x219, 0x239, 0x25b, 0x27f, 0x2a4 }, + { 0x167, 0x17c, 0x193, 0x1ab, 0x1c4, 0x1e0, 0x1fc, 0x21a, 0x23a, 0x25c, 0x280, 0x2a6 }, + { 0x168, 0x17d, 0x194, 0x1ac, 0x1c5, 0x1e0, 0x1fd, 0x21b, 0x23b, 0x25d, 0x281, 0x2a7 }, + { 0x168, 0x17d, 0x194, 0x1ad, 0x1c6, 0x1e1, 0x1fe, 0x21c, 0x23c, 0x25e, 0x282, 0x2a8 }, + { 0x169, 0x17e, 0x195, 0x1ad, 0x1c7, 0x1e2, 0x1ff, 0x21d, 0x23d, 0x260, 0x283, 0x2a9 }, + { 0x16a, 0x17f, 0x196, 0x1ae, 0x1c8, 0x1e3, 0x1ff, 0x21e, 0x23e, 0x261, 0x284, 0x2ab }, + { 0x16a, 0x17f, 0x197, 0x1af, 0x1c8, 0x1e4, 0x200, 0x21f, 0x23f, 0x262, 0x286, 0x2ac } +}; + +static inline double s_apogeeFreq(double tone) +{ + uint_fast32_t noteI = (uint_fast32_t)(tone); + int_fast32_t bendI = 0; + int_fast32_t outHz = 0; + double bendDec = tone - (int)tone; + int_fast32_t octave; + int_fast32_t scaleNote; + + bendI = (int_fast32_t)(bendDec * 32) + 32; + + noteI += bendI / 32; + noteI -= 1; + + scaleNote = noteI % 12; + octave = noteI / 12; + + outHz = s_apogee_freq_table[bendI % 32][scaleNote]; + + while(octave > 1) + { + outHz *= 2; + octave -= 1; + } + + return (double)outHz; +} + + + + +/*************************************************************** + * Windows 9x FM drivers frequency model * + ***************************************************************/ + +//static const double s_9x_opl_samplerate = 50000.0; +//static const double s_9x_opl_tune = 440.0; +static const uint_fast8_t s_9x_opl_pitchfrac = 8; + +static const uint_fast32_t s_9x_opl_freq[12] = +{ + 0xAB7, 0xB5A, 0xC07, 0xCBE, 0xD80, 0xE4D, 0xF27, 0x100E, 0x1102, 0x1205, 0x1318, 0x143A +}; + +static const int32_t s_9x_opl_uppitch = 31; +static const int32_t s_9x_opl_downpitch = 27; + +static uint_fast32_t s_9x_opl_applypitch(uint_fast32_t freq, int_fast32_t pitch) +{ + int32_t diff; + + if(pitch > 0) + { + diff = (pitch * s_9x_opl_uppitch) >> s_9x_opl_pitchfrac; + freq += (diff * freq) >> 15; + } + else if (pitch < 0) + { + diff = (-pitch * s_9x_opl_downpitch) >> s_9x_opl_pitchfrac; + freq -= (diff * freq) >> 15; + } + + return freq; +} + +static inline double s_9xFreq(double tone) +{ + uint_fast32_t note = (uint_fast32_t)(tone); + int_fast32_t bend; + double bendDec = tone - (int)tone; // 0.0 ± 1.0 - one halftone + + uint_fast32_t freq; + uint_fast32_t freqpitched; + uint_fast32_t octave; + + uint_fast32_t bendMsb; + uint_fast32_t bendLsb; + + bend = (int_fast32_t)(bendDec * 4096) + 8192; // convert to MIDI standard value + + bendMsb = (bend >> 7) & 0x7F; + bendLsb = (bend & 0x7F); + + bend = (bendMsb << 9) | (bendLsb << 2); + bend = (int16_t)(uint16_t)(bend + 0x8000); + + octave = note / 12; + freq = s_9x_opl_freq[note % 12]; + if(octave < 5) + freq >>= (5 - octave); + else if (octave > 5) + freq <<= (octave - 5); + + freqpitched = s_9x_opl_applypitch(freq, bend); + freqpitched *= 2; + + return (double)freqpitched; +} + + + + +/*************************************************************** + * HMI Sound Operating System frequency model * + ***************************************************************/ + +const size_t s_hmi_freqtable_size = 103; +static uint_fast32_t s_hmi_freqtable[s_hmi_freqtable_size] = +{ + 0x0157, 0x016B, 0x0181, 0x0198, 0x01B0, 0x01CA, 0x01E5, 0x0202, 0x0220, 0x0241, 0x0263, 0x0287, + 0x0557, 0x056B, 0x0581, 0x0598, 0x05B0, 0x05CA, 0x05E5, 0x0602, 0x0620, 0x0641, 0x0663, 0x0687, + 0x0957, 0x096B, 0x0981, 0x0998, 0x09B0, 0x09CA, 0x09E5, 0x0A02, 0x0A20, 0x0A41, 0x0A63, 0x0A87, + 0x0D57, 0x0D6B, 0x0D81, 0x0D98, 0x0DB0, 0x0DCA, 0x0DE5, 0x0E02, 0x0E20, 0x0E41, 0x0E63, 0x0E87, + 0x1157, 0x116B, 0x1181, 0x1198, 0x11B0, 0x11CA, 0x11E5, 0x1202, 0x1220, 0x1241, 0x1263, 0x1287, + 0x1557, 0x156B, 0x1581, 0x1598, 0x15B0, 0x15CA, 0x15E5, 0x1602, 0x1620, 0x1641, 0x1663, 0x1687, + 0x1957, 0x196B, 0x1981, 0x1998, 0x19B0, 0x19CA, 0x19E5, 0x1A02, 0x1A20, 0x1A41, 0x1A63, 0x1A87, + 0x1D57, 0x1D6B, 0x1D81, 0x1D98, 0x1DB0, 0x1DCA, 0x1DE5, 0x1E02, 0x1E20, 0x1E41, 0x1E63, 0x1E87, + 0x1EAE, 0x1EB7, 0x1F02, 0x1F30, 0x1F60, 0x1F94, 0x1FCA +}; + +const size_t s_hmi_bendtable_size = 12; +static uint_fast32_t s_hmi_bendtable[s_hmi_bendtable_size] = +{ + 0x144, 0x132, 0x121, 0x110, 0x101, 0xf8, 0xe5, 0xd8, 0xcc, 0xc1, 0xb6, 0xac +}; + +#define hmi_range_fix(formula, maxVal) \ + ( \ + (formula) < 0 ? \ + 0 : \ + ( \ + (formula) >= (int32_t)maxVal ? \ + (int32_t)maxVal : \ + (formula) \ + )\ + ) + +static uint_fast32_t s_hmi_bend_calc(uint_fast32_t bend, int_fast32_t note) +{ + const int_fast32_t midi_bend_range = 1; + uint_fast32_t bendFactor, outFreq, fmOctave, fmFreq, newFreq, idx; + int_fast32_t noteMod12; + + note -= 12; +// while(doNote >= 12) // ugly way to MOD 12 +// doNote -= 12; + noteMod12 = (note % 12); + + outFreq = s_hmi_freqtable[note]; + + fmOctave = outFreq & 0x1c00; + fmFreq = outFreq & 0x3ff; + + if(bend < 64) + { + bendFactor = ((63 - bend) * 1000) >> 6; + + idx = hmi_range_fix(note - midi_bend_range, s_hmi_freqtable_size); + newFreq = outFreq - s_hmi_freqtable[idx]; + + if(newFreq > 719) + { + newFreq = fmFreq - s_hmi_bendtable[midi_bend_range - 1]; + newFreq &= 0x3ff; + } + + newFreq = (newFreq * bendFactor) / 1000; + outFreq -= newFreq; + } + else + { + bendFactor = ((bend - 64) * 1000) >> 6; + + idx = hmi_range_fix(note + midi_bend_range, s_hmi_freqtable_size); + newFreq = s_hmi_freqtable[idx] - outFreq; + + if(newFreq > 719) + { + idx = hmi_range_fix(11 - noteMod12, s_hmi_bendtable_size); + fmFreq = s_hmi_bendtable[idx]; + outFreq = (fmOctave + 1024) | fmFreq; + + idx = hmi_range_fix(note + midi_bend_range, s_hmi_freqtable_size); + newFreq = s_hmi_freqtable[idx] - outFreq; + } + + newFreq = (newFreq * bendFactor) / 1000; + outFreq += newFreq; + } + + return outFreq; +} +#undef hmi_range_fix + +static inline double s_hmiFreq(double tone) +{ + int_fast32_t note = (int_fast32_t)(tone); + double bendDec = tone - (int)tone; // 0.0 ± 1.0 - one halftone + int_fast32_t bend; + uint_fast32_t inFreq; + uint_fast32_t freq; + int_fast32_t octave; + int_fast32_t octaveOffset = 0; + + bend = (int_fast32_t)(bendDec * 64.0) + 64; + + while(note < 12) + { + octaveOffset--; + note += 12; + } + while(note > 114) + { + octaveOffset++; + note -= 12; + } + + if(bend == 64) + inFreq = s_hmi_freqtable[note - 12]; + else + inFreq = s_hmi_bend_calc(bend, note); + + freq = inFreq & 0x3FF; + octave = (inFreq >> 10) & 0x07; + + octave += octaveOffset; + + while(octave > 0) + { + freq *= 2; + octave -= 1; + } + + return freq; +} + + + + +/*************************************************************** + * Audio Interface Library frequency model * + ***************************************************************/ + +static const uint_fast16_t s_ail_freqtable[] = { + 0x02b2, 0x02b4, 0x02b7, 0x02b9, 0x02bc, 0x02be, 0x02c1, 0x02c3, + 0x02c6, 0x02c9, 0x02cb, 0x02ce, 0x02d0, 0x02d3, 0x02d6, 0x02d8, + 0x02db, 0x02dd, 0x02e0, 0x02e3, 0x02e5, 0x02e8, 0x02eb, 0x02ed, + 0x02f0, 0x02f3, 0x02f6, 0x02f8, 0x02fb, 0x02fe, 0x0301, 0x0303, + 0x0306, 0x0309, 0x030c, 0x030f, 0x0311, 0x0314, 0x0317, 0x031a, + 0x031d, 0x0320, 0x0323, 0x0326, 0x0329, 0x032b, 0x032e, 0x0331, + 0x0334, 0x0337, 0x033a, 0x033d, 0x0340, 0x0343, 0x0346, 0x0349, + 0x034c, 0x034f, 0x0352, 0x0356, 0x0359, 0x035c, 0x035f, 0x0362, + 0x0365, 0x0368, 0x036b, 0x036f, 0x0372, 0x0375, 0x0378, 0x037b, + 0x037f, 0x0382, 0x0385, 0x0388, 0x038c, 0x038f, 0x0392, 0x0395, + 0x0399, 0x039c, 0x039f, 0x03a3, 0x03a6, 0x03a9, 0x03ad, 0x03b0, + 0x03b4, 0x03b7, 0x03bb, 0x03be, 0x03c1, 0x03c5, 0x03c8, 0x03cc, + 0x03cf, 0x03d3, 0x03d7, 0x03da, 0x03de, 0x03e1, 0x03e5, 0x03e8, + 0x03ec, 0x03f0, 0x03f3, 0x03f7, 0x03fb, 0x03fe, 0xfe01, 0xfe03, + 0xfe05, 0xfe07, 0xfe08, 0xfe0a, 0xfe0c, 0xfe0e, 0xfe10, 0xfe12, + 0xfe14, 0xfe16, 0xfe18, 0xfe1a, 0xfe1c, 0xfe1e, 0xfe20, 0xfe21, + 0xfe23, 0xfe25, 0xfe27, 0xfe29, 0xfe2b, 0xfe2d, 0xfe2f, 0xfe31, + 0xfe34, 0xfe36, 0xfe38, 0xfe3a, 0xfe3c, 0xfe3e, 0xfe40, 0xfe42, + 0xfe44, 0xfe46, 0xfe48, 0xfe4a, 0xfe4c, 0xfe4f, 0xfe51, 0xfe53, + 0xfe55, 0xfe57, 0xfe59, 0xfe5c, 0xfe5e, 0xfe60, 0xfe62, 0xfe64, + 0xfe67, 0xfe69, 0xfe6b, 0xfe6d, 0xfe6f, 0xfe72, 0xfe74, 0xfe76, + 0xfe79, 0xfe7b, 0xfe7d, 0xfe7f, 0xfe82, 0xfe84, 0xfe86, 0xfe89, + 0xfe8b, 0xfe8d, 0xfe90, 0xfe92, 0xfe95, 0xfe97, 0xfe99, 0xfe9c, + 0xfe9e, 0xfea1, 0xfea3, 0xfea5, 0xfea8, 0xfeaa, 0xfead, 0xfeaf +}; + +static const uint_fast8_t s_ail_note_octave[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 +}; + +static const uint_fast8_t s_ail_note_halftone[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b +}; + +static inline double s_ailFreq(double tone) +{ + int_fast32_t note = (int_fast32_t)(tone); + double bendDec = tone - (int)tone; // 0.0 ± 1.0 - one halftone + int_fast32_t pitch; + uint_fast16_t freq; + int_fast32_t octave; + int_fast32_t octaveOffset = 0; + uint_fast8_t halftones; + + pitch = (int_fast32_t)(bendDec * 4096) + 8192; // convert to MIDI standard value + pitch = ((pitch - 0x2000) / 0x20) * 2; + + note -= 12; + + while(note < 0) + { + octaveOffset--; + note += 12; + } + while(note > 95) + { + octaveOffset++; + note -= 12; + } + + pitch += (((uint_fast8_t)note) << 8) + 8; + pitch /= 16; + while (pitch < 12 * 16) { + pitch += 12 * 16; + } + while (pitch > 96 * 16 - 1) { + pitch -= 12 * 16; + } + + halftones = (s_ail_note_halftone[pitch >> 4] << 4) + (pitch & 0x0f); + freq = s_ail_freqtable[halftones]; + octave = s_ail_note_octave[pitch >> 4]; + + if((freq & 0x8000) == 0) + { + if (octave > 0) { + octave--; + } else { + freq /= 2; + } + } + + freq &= 0x3FF; + + octave += octaveOffset; + + while(octave > 0) + { + freq *= 2; + octave -= 1; + } + + return freq; +} + + + + + + + +enum +{ + MasterVolumeDefault = 127 +}; + +enum +{ + OPL_PANNING_LEFT = 0x10, + OPL_PANNING_RIGHT = 0x20, + OPL_PANNING_BOTH = 0x30 +}; + + + +static OplInstMeta makeEmptyInstrument() +{ + OplInstMeta ins; + memset(&ins, 0, sizeof(OplInstMeta)); + ins.flags = OplInstMeta::Flag_NoSound; + return ins; +} + +const OplInstMeta OPL3::m_emptyInstrument = makeEmptyInstrument(); + +OPL3::OPL3() : +#ifdef ADLMIDI_ENABLE_HW_SERIAL + m_serial(false), + m_serialBaud(0), + m_serialProtocol(0), +#endif + m_softPanningSup(false), + m_currentChipType((int)OPLChipBase::CHIPTYPE_OPL3), + m_perChipChannels(OPL3_CHANNELS_RHYTHM_BASE), + m_numChips(1), + m_numFourOps(0), + m_deepTremoloMode(false), + m_deepVibratoMode(false), + m_rhythmMode(false), + m_softPanning(false), + m_masterVolume(MasterVolumeDefault), + m_musicMode(MODE_MIDI), + m_volumeScale(VOLUME_Generic), + m_channelAlloc(ADLMIDI_ChanAlloc_AUTO) +{ + m_insBankSetup.volumeModel = OPL3::VOLUME_Generic; + m_insBankSetup.deepTremolo = false; + m_insBankSetup.deepVibrato = false; + m_insBankSetup.scaleModulators = false; + m_insBankSetup.mt32defaults = false; + +#ifdef DISABLE_EMBEDDED_BANKS + m_embeddedBank = CustomBankTag; +#else + setEmbeddedBank(0); +#endif +} + +OPL3::~OPL3() +{ + m_curState.clear(); + +#ifdef ENABLE_HW_OPL_DOS + silenceAll(); + writeRegI(0, 0x0BD, 0); + writeRegI(0, 0x104, 0); + writeRegI(0, 0x105, 0); + silenceAll(); +#endif +} + +bool OPL3::setupLocked() +{ + return (m_musicMode == MODE_CMF || + m_musicMode == MODE_IMF || + m_musicMode == MODE_RSXX); +} + +void OPL3::setEmbeddedBank(uint32_t bank) +{ +#ifndef DISABLE_EMBEDDED_BANKS + m_embeddedBank = bank; + //Embedded banks are supports 128:128 GM set only + m_insBanks.clear(); + + if(bank >= static_cast(g_embeddedBanksCount)) + return; + + const BanksDump::BankEntry &bankEntry = g_embeddedBanks[m_embeddedBank]; + m_insBankSetup.deepTremolo = ((bankEntry.bankSetup >> 8) & 0x01) != 0; + m_insBankSetup.deepVibrato = ((bankEntry.bankSetup >> 8) & 0x02) != 0; + m_insBankSetup.mt32defaults = ((bankEntry.bankSetup >> 8) & 0x04) != 0; + m_insBankSetup.volumeModel = (bankEntry.bankSetup & 0xFF); + m_insBankSetup.scaleModulators = false; + + for(int ss = 0; ss < 2; ss++) + { + bank_count_t maxBanks = ss ? bankEntry.banksPercussionCount : bankEntry.banksMelodicCount; + bank_count_t banksOffset = ss ? bankEntry.banksOffsetPercussive : bankEntry.banksOffsetMelodic; + + for(bank_count_t bankID = 0; bankID < maxBanks; bankID++) + { + size_t bankIndex = g_embeddedBanksMidiIndex[banksOffset + bankID]; + const BanksDump::MidiBank &bankData = g_embeddedBanksMidi[bankIndex]; + size_t bankMidiIndex = static_cast((bankData.msb * 256) + bankData.lsb) + (ss ? static_cast(PercussionTag) : 0); + Bank &bankTarget = m_insBanks[bankMidiIndex]; + + for(size_t instId = 0; instId < 128; instId++) + { + midi_bank_idx_t instIndex = bankData.insts[instId]; + if(instIndex < 0) + { + bankTarget.ins[instId].flags = OplInstMeta::Flag_NoSound; + continue; + } + BanksDump::InstrumentEntry instIn = g_embeddedBanksInstruments[instIndex]; + OplInstMeta &instOut = bankTarget.ins[instId]; + + adlFromInstrument(instIn, instOut); + } + } + } + +#else + ADL_UNUSED(bank); +#endif +} + +void OPL3::writeReg(size_t chip, uint16_t address, uint8_t value) +{ + m_chips[chip]->writeReg(address, value); +} + +void OPL3::writeRegI(size_t chip, uint32_t address, uint32_t value) +{ + m_chips[chip]->writeReg(static_cast(address), static_cast(value)); +} + +void OPL3::writePan(size_t chip, uint32_t address, uint32_t value) +{ + m_chips[chip]->writePan(static_cast(address), static_cast(value)); +} + + +void OPL3::noteOff(size_t c) +{ + size_t chip = c / NUM_OF_CHANNELS, cc = c % NUM_OF_CHANNELS; + + if(cc >= OPL3_CHANNELS_RHYTHM_BASE) + { + m_regBD[chip] &= ~(0x10 >> (cc - OPL3_CHANNELS_RHYTHM_BASE)); + writeRegI(chip, 0xBD, m_regBD[chip]); + return; + } + else if(m_currentChipType == OPLChipBase::CHIPTYPE_OPL2 && cc >= NUM_OF_OPL2_CHANNELS) + return; + + writeRegI(chip, 0xB0 + g_channelsMap[cc], m_keyBlockFNumCache[c] & 0xDF); +} + +void OPL3::noteOn(size_t c1, size_t c2, double tone) +{ + size_t chip = c1 / NUM_OF_CHANNELS, cc1 = c1 % NUM_OF_CHANNELS, cc2 = c2 % NUM_OF_CHANNELS; + uint32_t octave = 0, ftone = 0, mul_offset = 0; + // Hertz range: 0..131071 + double hertz; + + if(tone < 0.0) + tone = 0.0; // Lower than 0 is impossible! + + // Use different frequency formulas in depend on a volume model + switch(m_volumeScale) + { + case VOLUME_DMX: + case VOLUME_DMX_FIXED: + hertz = s_dmxFreq(tone); + break; + + case VOLUME_APOGEE: + case VOLUME_APOGEE_FIXED: + hertz = s_apogeeFreq(tone); + break; + + case VOLUME_9X: + case VOLUME_9X_GENERIC_FM: + hertz = s_9xFreq(tone); + break; + + case VOLUME_HMI: + case VOLUME_HMI_OLD: + hertz = s_hmiFreq(tone); + break; + + case VOLUME_AIL: + hertz = s_ailFreq(tone); + break; + + default: + hertz = s_commonFreq(tone); + } + + if(hertz < 0) + return; + + //Basic range until max of octaves reaching + while((hertz >= 1023.5) && (octave < 0x1C00)) + { + hertz /= 2.0; // Calculate octave + octave += 0x400; + } + //Extended range, rely on frequency multiplication increment + while(hertz >= 1022.75) + { + hertz /= 2.0; // Calculate octave + mul_offset++; + } + + ftone = octave + static_cast(hertz /*+ 0.5*/); + uint32_t chn = m_musicMode == MODE_CMF ? g_channelsMapPan[cc1] : g_channelsMap[cc1]; + const OplTimbre &patch1 = m_insCache[c1]; + const OplTimbre &patch2 = m_insCache[c2 < m_insCache.size() ? c2 : 0]; + + if(cc1 < OPL3_CHANNELS_RHYTHM_BASE) + { + ftone += 0x2000u; /* Key-ON [KON] */ + + const bool natural_4op = (m_channelCategory[c1] == ChanCat_4op_First); + const size_t opsCount = natural_4op ? 4 : 2; + const uint16_t op_addr[4] = + { + g_operatorsMap[cc1 * 2 + 0], g_operatorsMap[cc1 * 2 + 1], + g_operatorsMap[cc2 * 2 + 0], g_operatorsMap[cc2 * 2 + 1] + }; + const uint32_t ops[4] = + { + patch1.modulator_E862 & 0xFF, + patch1.carrier_E862 & 0xFF, + patch2.modulator_E862 & 0xFF, + patch2.carrier_E862 & 0xFF + }; + + for(size_t op = 0; op < opsCount; op++) + { + if(op_addr[op] == 0xFFF) + continue; + if(mul_offset > 0) + { + uint32_t dt = ops[op] & 0xF0; + uint32_t mul = ops[op] & 0x0F; + if((mul + mul_offset) > 0x0F) + { + mul_offset = 0; + mul = 0x0F; + } + writeRegI(chip, 0x20 + op_addr[op], (dt | (mul + mul_offset)) & 0xFF); + } + else + { + writeRegI(chip, 0x20 + op_addr[op], ops[op] & 0xFF); + } + } + } + + if(chn != 0xFFF) + { + writeRegI(chip , 0xA0 + chn, (ftone & 0xFF)); + writeRegI(chip , 0xB0 + chn, (ftone >> 8)); + m_keyBlockFNumCache[c1] = (ftone >> 8); + } + + if(cc1 >= OPL3_CHANNELS_RHYTHM_BASE) + { + m_regBD[chip ] |= (0x10 >> (cc1 - OPL3_CHANNELS_RHYTHM_BASE)); + writeRegI(chip , 0x0BD, m_regBD[chip ]); + //x |= 0x800; // for test + } +} + +static inline uint_fast32_t brightnessToOPL(uint_fast32_t brightness) +{ + double b = static_cast(brightness); + double ret = ::round(127.0 * ::sqrt(b * (1.0 / 127.0))) / 2.0; + return static_cast(ret); +} + +void OPL3::touchNote(size_t c, + uint_fast32_t velocity, + uint_fast32_t channelVolume, + uint_fast32_t channelExpression, + uint_fast32_t brightness, + bool isDrum) +{ + size_t chip = c / NUM_OF_CHANNELS, cc = c % NUM_OF_CHANNELS; + const OplTimbre &adli = m_insCache[c]; + size_t cmf_offset = ((m_musicMode == MODE_CMF) && cc >= OPL3_CHANNELS_RHYTHM_BASE) ? 10 : 0; + uint16_t o1 = g_operatorsMap[cc * 2 + 0 + cmf_offset]; + uint16_t o2 = g_operatorsMap[cc * 2 + 1 + cmf_offset]; + uint8_t srcMod = adli.modulator_40, + srcCar = adli.carrier_40; + uint32_t mode = 1; // 2-op AM + + uint_fast32_t kslMod = srcMod & 0xC0; + uint_fast32_t kslCar = srcCar & 0xC0; + uint_fast32_t tlMod = srcMod & 0x3F; + uint_fast32_t tlCar = srcCar & 0x3F; + + uint_fast32_t modulator; + uint_fast32_t carrier; + + uint_fast32_t volume = 0; + uint_fast32_t midiVolume = 0; + + bool do_modulator = false; + bool do_carrier = true; + + static const bool do_ops[10][2] = + { + { false, true }, /* 2 op FM */ + { true, true }, /* 2 op AM */ + { false, false }, /* 4 op FM-FM ops 1&2 */ + { true, false }, /* 4 op AM-FM ops 1&2 */ + { false, true }, /* 4 op FM-AM ops 1&2 */ + { true, false }, /* 4 op AM-AM ops 1&2 */ + { false, true }, /* 4 op FM-FM ops 3&4 */ + { false, true }, /* 4 op AM-FM ops 3&4 */ + { false, true }, /* 4 op FM-AM ops 3&4 */ + { true, true } /* 4 op AM-AM ops 3&4 */ + }; + + if(m_currentChipType == OPLChipBase::CHIPTYPE_OPL2 && m_channelCategory[c] == ChanCat_None) + return; // Do nothing + + // ------ Mix volumes and compute average ------ + + switch(m_volumeScale) + { + default: + case VOLUME_Generic: + { + volume = velocity * m_masterVolume * + channelVolume * channelExpression; + + /* If the channel has arpeggio, the effective volume of + * *this* instrument is actually lower due to timesharing. + * To compensate, add extra volume that corresponds to the + * time this note is *not* heard. + * Empirical tests however show that a full equal-proportion + * increment sounds wrong. Therefore, using the square root. + */ + //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() )); + const double c1 = 11.541560327111707; + const double c2 = 1.601379199767093e+02; + const uint_fast32_t minVolume = 1108075; // 8725 * 127 + + // The formula below: SOLVE(V=127^4 * 2^( (A-63.49999) / 8), A) + if(volume > minVolume) + { + double lv = std::log(static_cast(volume)); + volume = static_cast(lv * c1 - c2); + } + else + volume = 0; + } + break; + + case VOLUME_NATIVE: + { + volume = velocity * channelVolume * channelExpression; + // 4096766 = (127 * 127 * 127) / 2 + volume = (volume * m_masterVolume) / 4096766; + } + break; + + case VOLUME_DMX: + case VOLUME_DMX_FIXED: + { + volume = (channelVolume * channelExpression * m_masterVolume) / 16129; + volume = (s_dmx_volume_model[volume] + 1) << 1; + volume = (s_dmx_volume_model[(velocity < 128) ? velocity : 127] * volume) >> 9; + } + break; + + case VOLUME_APOGEE: + case VOLUME_APOGEE_FIXED: + { + midiVolume = (channelVolume * channelExpression * m_masterVolume / 16129); + } + break; + + case VOLUME_9X: + { + volume = (channelVolume * channelExpression * m_masterVolume) / 16129; + volume = s_w9x_sb16_volume_model[volume >> 2]; + } + break; + + case VOLUME_9X_GENERIC_FM: + { + volume = (channelVolume * channelExpression * m_masterVolume) / 16129; + volume = s_w9x_generic_fm_volume_model[volume >> 2]; + } + break; + + case VOLUME_AIL: + { + midiVolume = (channelVolume * channelExpression) * 2; + midiVolume >>= 8; + if(midiVolume != 0) + midiVolume++; + + velocity = (velocity & 0x7F) >> 3; + velocity = s_ail_vel_graph[velocity]; + + midiVolume = (midiVolume * velocity) * 2; + midiVolume >>= 8; + if(midiVolume != 0) + midiVolume++; + + if(m_masterVolume < 127) + midiVolume = (midiVolume * m_masterVolume) / 127; + } + break; + + case VOLUME_HMI: + case VOLUME_HMI_OLD: + { + volume = (channelVolume * channelExpression * m_masterVolume) / 16129; + volume = (((volume * 128) / 127) * velocity) >> 7; + volume = s_hmi_volume_table[volume >> 1]; + } + break; + } + + if(volume > 63) + volume = 63; + + if(midiVolume > 127) + midiVolume = 127; + + + if(m_channelCategory[c] == ChanCat_Regular || + m_channelCategory[c] == ChanCat_Rhythm_Bass) + { + mode = adli.feedconn & 1; // 2-op FM or 2-op AM + } + else if(m_channelCategory[c] == ChanCat_4op_First || + m_channelCategory[c] == ChanCat_4op_Second) + { + const OplTimbre *i0, *i1; + + if(m_channelCategory[c] == ChanCat_4op_First) + { + i0 = &adli; + i1 = &m_insCache[c + 3]; + mode = 2; // 4-op xx-xx ops 1&2 + } + else + { + i0 = &m_insCache[c - 3]; + i1 = &adli; + mode = 6; // 4-op xx-xx ops 3&4 + } + + mode += (i0->feedconn & 1) + (i1->feedconn & 1) * 2; + } + + do_modulator = do_ops[mode][0] || m_scaleModulators; + do_carrier = do_ops[mode][1] || m_scaleModulators; + + // ------ Compute the total level register output data ------ + + if(m_musicMode == MODE_RSXX) + { + tlCar -= volume / 2; + } + else if(m_volumeScale == Synth::VOLUME_APOGEE || + m_volumeScale == Synth::VOLUME_APOGEE_FIXED) + { + // volume = ((64 * (velocity + 0x80)) * volume) >> 15; + + if(do_carrier) + { + tlCar = 63 - tlCar; + tlCar *= velocity + 0x80; + tlCar = (midiVolume * tlCar) >> 15; + tlCar = tlCar ^ 63; + } + + if(do_modulator) + { + uint_fast32_t mod = tlCar; + + tlMod = 63 - tlMod; + tlMod *= velocity + 0x80; + + if(m_volumeScale == Synth::VOLUME_APOGEE_FIXED || mode > 1) + mod = tlMod; // Fix the AM voices bug + + // NOTE: Here is a bug of Apogee Sound System that makes modulator + // to not work properly on AM instruments. The fix of this bug, you + // need to replace the tlCar with tmMod in this formula. + // Don't do the bug on 4-op voices. + tlMod = (midiVolume * mod) >> 15; + + tlMod ^= 63; + } + } + else if(m_volumeScale == Synth::VOLUME_DMX && mode <= 1) + { + tlCar = (63 - volume); + + if(do_modulator) + { + if(tlMod < tlCar) + tlMod = tlCar; + } + } + else if(m_volumeScale == Synth::VOLUME_9X) + { + if(do_carrier) + tlCar += volume + s_w9x_sb16_volume_model[velocity >> 2]; + if(do_modulator) + tlMod += volume + s_w9x_sb16_volume_model[velocity >> 2]; + + if(tlCar > 0x3F) + tlCar = 0x3F; + if(tlMod > 0x3F) + tlMod = 0x3F; + } + else if(m_volumeScale == Synth::VOLUME_9X_GENERIC_FM) + { + if(do_carrier) + tlCar += volume + s_w9x_generic_fm_volume_model[velocity >> 2]; + if(do_modulator) + tlMod += volume + s_w9x_generic_fm_volume_model[velocity >> 2]; + + if(tlCar > 0x3F) + tlCar = 0x3F; + if(tlMod > 0x3F) + tlMod = 0x3F; + } + else if(m_volumeScale == Synth::VOLUME_AIL) + { + uint_fast32_t v0_val = (~srcMod) & 0x3f; + uint_fast32_t v1_val = (~srcCar) & 0x3f; + + if(do_modulator) + v0_val = (v0_val * midiVolume) / 127; + if(do_carrier) + v1_val = (v1_val * midiVolume) / 127; + + tlMod = (~v0_val) & 0x3F; + tlCar = (~v1_val) & 0x3F; + } + else if(m_volumeScale == Synth::VOLUME_HMI) + { + uint_fast32_t vol; + + if(do_modulator) + { + vol = (64 - volume) << 1; + vol *= (64 - tlMod); + tlMod = (8192 - vol) >> 7; + } + + if(do_carrier) + { + vol = (64 - volume) << 1; + vol *= (64 - tlCar); + tlCar = (8192 - vol) >> 7; + } + } + else if(m_volumeScale == Synth::VOLUME_HMI_OLD) + { + uint_fast32_t vol; + + if(adli.feedconn == 0 && !isDrum) + { + vol = (channelVolume * channelExpression * 64) / 16129; + vol = (((vol * 128) / 127) * velocity) >> 7; + vol = s_hmi_volume_table[vol >> 1]; + + vol = (64 - vol) << 1; + vol *= (64 - tlCar); + tlMod = (8192 - vol) >> 7; + } + + if(isDrum) // TODO: VERIFY A CORRECTNESS OF THIS!!! + vol = (64 - s_hmi_volume_table[velocity >> 1]) << 1; + else + vol = (64 - volume) << 1; + + vol *= (64 - tlCar); + tlCar = (8192 - vol) >> 7; + } + else + { + if(do_modulator) + tlMod = 63 - volume + (volume * tlMod) / 63; + if(do_carrier) + tlCar = 63 - volume + (volume * tlCar) / 63; + } + + if(brightness != 127 && !isDrum) + { + brightness = brightnessToOPL(brightness); + if(!do_modulator) + tlMod = 63 - brightness + (brightness * tlMod) / 63; + if(!do_carrier) + tlCar = 63 - brightness + (brightness * tlCar) / 63; + } + + modulator = (kslMod & 0xC0) | (tlMod & 63); + carrier = (kslCar & 0xC0) | (tlCar & 63); + + if(o1 != 0xFFF) + writeRegI(chip, 0x40 + o1, modulator); + if(o2 != 0xFFF) + writeRegI(chip, 0x40 + o2, carrier); + + // Correct formula (ST3, AdPlug): + // 63-((63-(instrvol))/63)*chanvol + // Reduces to (tested identical): + // 63 - chanvol + chanvol*instrvol/63 + // Also (slower, floats): + // 63 + chanvol * (instrvol / 63.0 - 1) +} + +void OPL3::setPatch(size_t c, const OplTimbre &instrument) +{ + size_t chip = c / NUM_OF_CHANNELS, cc = c % NUM_OF_CHANNELS; + static const uint8_t data[4] = {0x20, 0x60, 0x80, 0xE0}; + m_insCache[c] = instrument; + size_t cmf_offset = ((m_musicMode == MODE_CMF) && (cc >= OPL3_CHANNELS_RHYTHM_BASE)) ? 10 : 0; + uint16_t o1 = g_operatorsMap[cc * 2 + 0 + cmf_offset]; + uint16_t o2 = g_operatorsMap[cc * 2 + 1 + cmf_offset]; + unsigned x = instrument.modulator_E862, y = instrument.carrier_E862; + uint8_t fbconn = 0; + uint16_t fbconn_reg = 0x00; + + for(size_t a = 0; a < 4; ++a, x >>= 8, y >>= 8) + { + if(o1 != 0xFFF) + writeRegI(chip, data[a] + o1, x & 0xFF); + if(o2 != 0xFFF) + writeRegI(chip, data[a] + o2, y & 0xFF); + } + + if(g_channelsMapFBConn[cc] != 0xFFF) + { + fbconn |= instrument.feedconn; + fbconn_reg = 0xC0 + g_channelsMapFBConn[cc]; + } + + if(m_currentChipType != OPLChipBase::CHIPTYPE_OPL2 && g_channelsMapPan[cc] != 0xFFF) + { + fbconn |= (m_regC0[c] & OPL_PANNING_BOTH); + if(!fbconn_reg) + fbconn_reg = 0xC0 + g_channelsMapPan[cc]; + } + + if(fbconn_reg != 0x00) + writeRegI(chip, fbconn_reg, fbconn); +} + +void OPL3::setPan(size_t c, uint8_t value) +{ + size_t chip = c / NUM_OF_CHANNELS, cc = c % NUM_OF_CHANNELS; + + if(m_currentChipType == OPLChipBase::CHIPTYPE_OPL2) + { + m_regC0[c] = OPL_PANNING_BOTH; + return; // OPL2 chip doesn't support panning at all + } + + if(g_channelsMapPan[cc] != 0xFFF) + { +#ifndef ENABLE_HW_OPL_DOS + if (m_softPanningSup && m_softPanning) + { + writePan(chip, g_channelsMapPan[cc], value); + m_regC0[c] = OPL_PANNING_BOTH; + writeRegI(chip, 0xC0 + g_channelsMapPan[cc], m_insCache[c].feedconn | OPL_PANNING_BOTH); + } + else + { +#endif + uint8_t panning = 0; + if(value < 64 + 16) panning |= OPL_PANNING_LEFT; + if(value >= 64 - 16) panning |= OPL_PANNING_RIGHT; + m_regC0[c] = panning; + writePan(chip, g_channelsMapPan[cc], 64); + writeRegI(chip, 0xC0 + g_channelsMapPan[cc], m_insCache[c].feedconn | panning); +#ifndef ENABLE_HW_OPL_DOS + } +#endif + } +} + +void OPL3::silenceAll() // Silence all OPL channels. +{ + for(size_t c = 0; c < m_numChannels; ++c) + { + noteOff(c); + touchNote(c, 0, 0, 0); + } +} + +void OPL3::updateChannelCategories() +{ + const uint32_t fours = (m_currentChipType != OPLChipBase::CHIPTYPE_OPL2) ? m_numFourOps : 0; + + for(uint32_t chip = 0, fours_left = fours; chip < m_numChips; ++chip) + { + m_regBD[chip] = (m_deepTremoloMode * 0x80 + m_deepVibratoMode * 0x40 + m_rhythmMode * 0x20); + writeRegI(chip, 0x0BD, m_regBD[chip]); + uint32_t fours_this_chip = std::min(fours_left, static_cast(6u)); + if(m_currentChipType != OPLChipBase::CHIPTYPE_OPL2) + writeRegI(chip, 0x104, (1 << fours_this_chip) - 1); + fours_left -= fours_this_chip; + } + + for(size_t p = 0, a = 0, n = m_numChips; a < n; ++a) + { + for(size_t b = 0; b < OPL3_CHANNELS_RHYTHM_BASE; ++b, ++p) + { + if(m_currentChipType == OPLChipBase::CHIPTYPE_OPL2 && b >= NUM_OF_OPL2_CHANNELS) + m_channelCategory[p] = ChanCat_None; + else + m_channelCategory[p] = ChanCat_Regular; + + if(m_rhythmMode && b >= 6 && b < 9) + m_channelCategory[p] = ChanCat_Rhythm_Secondary; + } + + if(!m_rhythmMode) + { + for(size_t b = 0; b < NUM_OF_RM_CHANNELS; ++b) + m_channelCategory[p++] = ChanCat_Rhythm_Secondary; + } + else + { + for(size_t b = 0; b < NUM_OF_RM_CHANNELS; ++b) + m_channelCategory[p++] = (ChanCat_Rhythm_Bass + b); + } + } + + uint32_t nextfour = 0; + for(uint32_t a = 0; a < fours; ++a) + { + m_channelCategory[nextfour] = ChanCat_4op_First; + m_channelCategory[nextfour + 3] = ChanCat_4op_Second; + + switch(a % 6) + { + case 0: + case 1: + nextfour += 1; + break; + case 2: + nextfour += 9 - 2; + break; + case 3: + case 4: + nextfour += 1; + break; + case 5: + nextfour += NUM_OF_CHANNELS - 9 - 2; + break; + } + } + +/**/ +/* + In two-op mode, channels 0..8 go as follows: + Op1[port] Op2[port] + Channel 0: 00 00 03 03 + Channel 1: 01 01 04 04 + Channel 2: 02 02 05 05 + Channel 3: 06 08 09 0B + Channel 4: 07 09 10 0C + Channel 5: 08 0A 11 0D + Channel 6: 12 10 15 13 + Channel 7: 13 11 16 14 + Channel 8: 14 12 17 15 + In four-op mode, channels 0..8 go as follows: + Op1[port] Op2[port] Op3[port] Op4[port] + Channel 0: 00 00 03 03 06 08 09 0B + Channel 1: 01 01 04 04 07 09 10 0C + Channel 2: 02 02 05 05 08 0A 11 0D + Channel 3: CHANNEL 0 SECONDARY + Channel 4: CHANNEL 1 SECONDARY + Channel 5: CHANNEL 2 SECONDARY + Channel 6: 12 10 15 13 + Channel 7: 13 11 16 14 + Channel 8: 14 12 17 15 + Same goes principally for channels 9-17 respectively. + */ +} + +void OPL3::commitDeepFlags() +{ + for(size_t chip = 0; chip < m_numChips; ++chip) + { + m_regBD[chip] = (m_deepTremoloMode * 0x80 + m_deepVibratoMode * 0x40 + m_rhythmMode * 0x20); + writeRegI(chip, 0x0BD, m_regBD[chip]); + } +} + +void OPL3::setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel) +{ + switch(volumeModel) + { + default: + case ADLMIDI_VolumeModel_AUTO://Do nothing until restart playing + break; + + case ADLMIDI_VolumeModel_Generic: + m_volumeScale = OPL3::VOLUME_Generic; + break; + + case ADLMIDI_VolumeModel_NativeOPL3: + m_volumeScale = OPL3::VOLUME_NATIVE; + break; + + case ADLMIDI_VolumeModel_DMX: + m_volumeScale = OPL3::VOLUME_DMX; + break; + + case ADLMIDI_VolumeModel_APOGEE: + m_volumeScale = OPL3::VOLUME_APOGEE; + break; + + case ADLMIDI_VolumeModel_9X: + m_volumeScale = OPL3::VOLUME_9X; + break; + + case ADLMIDI_VolumeModel_DMX_Fixed: + m_volumeScale = OPL3::VOLUME_DMX_FIXED; + break; + + case ADLMIDI_VolumeModel_APOGEE_Fixed: + m_volumeScale = OPL3::VOLUME_APOGEE_FIXED; + break; + + case ADLMIDI_VolumeModel_AIL: + m_volumeScale = OPL3::VOLUME_AIL; + break; + + case ADLMIDI_VolumeModel_9X_GENERIC_FM: + m_volumeScale = OPL3::VOLUME_9X_GENERIC_FM; + break; + + case ADLMIDI_VolumeModel_HMI: + m_volumeScale = OPL3::VOLUME_HMI; + break; + + case ADLMIDI_VolumeModel_HMI_OLD: + m_volumeScale = OPL3::VOLUME_HMI_OLD; + break; + } +} + +ADLMIDI_VolumeModels OPL3::getVolumeScaleModel() +{ + switch(m_volumeScale) + { + default: + case OPL3::VOLUME_Generic: + return ADLMIDI_VolumeModel_Generic; + case OPL3::VOLUME_NATIVE: + return ADLMIDI_VolumeModel_NativeOPL3; + case OPL3::VOLUME_DMX: + return ADLMIDI_VolumeModel_DMX; + case OPL3::VOLUME_APOGEE: + return ADLMIDI_VolumeModel_APOGEE; + case OPL3::VOLUME_9X: + return ADLMIDI_VolumeModel_9X; + case OPL3::VOLUME_DMX_FIXED: + return ADLMIDI_VolumeModel_DMX_Fixed; + case OPL3::VOLUME_APOGEE_FIXED: + return ADLMIDI_VolumeModel_APOGEE_Fixed; + case OPL3::VOLUME_AIL: + return ADLMIDI_VolumeModel_AIL; + case OPL3::VOLUME_9X_GENERIC_FM: + return ADLMIDI_VolumeModel_9X_GENERIC_FM; + case OPL3::VOLUME_HMI: + return ADLMIDI_VolumeModel_HMI; + case OPL3::VOLUME_HMI_OLD: + return ADLMIDI_VolumeModel_HMI_OLD; + } +} + +void OPL3::clearChips() +{ + for(size_t i = 0; i < m_chips.size(); i++) + m_chips[i].reset(NULL); + m_chips.clear(); +} + +void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) +{ + bool rebuild_needed = m_curState.cmp(emulator, m_numChips); + + if(rebuild_needed) + clearChips(); + +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + (void)audioTickHandler; +#endif + + const struct OplTimbre defaultInsCache = { 0x1557403,0x005B381, 0x49,0x80, 0x4, +0 }; + + if(rebuild_needed) + { + m_insCache.clear(); + m_keyBlockFNumCache.clear(); + m_regBD.clear(); + m_regC0.clear(); + m_channelCategory.clear(); + m_chips.resize(m_numChips, AdlMIDI_SPtr()); + } + else + { + adl_fill_vector(m_insCache, defaultInsCache); + adl_fill_vector(m_channelCategory, 0); + adl_fill_vector(m_keyBlockFNumCache, 0); + adl_fill_vector(m_regBD, 0); + adl_fill_vector(m_regC0, OPL_PANNING_BOTH); + } + +#ifdef ADLMIDI_ENABLE_HW_SERIAL + if(emulator >= 0) // If less than zero - it's hardware synth! + m_serial = false; +#endif + + if(rebuild_needed) + { + m_numChannels = m_numChips * NUM_OF_CHANNELS; + m_insCache.resize(m_numChannels, defaultInsCache); + m_channelCategory.resize(m_numChannels, 0); + m_keyBlockFNumCache.resize(m_numChannels, 0); + m_regBD.resize(m_numChips, 0); + m_regC0.resize(m_numChips * m_numChannels, OPL_PANNING_BOTH); + } + + if(!rebuild_needed) + { + bool newRate = m_curState.cmp_rate(PCM_RATE); + + for(size_t i = 0; i < m_numChips; ++i) + { + if(newRate) + m_chips[i]->setRate(PCM_RATE); + + initChip(i); + } + } + else for(size_t i = 0; i < m_numChips; ++i) + { +#ifdef ADLMIDI_ENABLE_HW_SERIAL + if(emulator < 0) + { + OPL_SerialPort *serial = new OPL_SerialPort; + serial->connectPort(m_serialName, m_serialBaud, m_serialProtocol); + m_chips[i].reset(serial); + initChip(i); + break; // Only one REAL chip! + } +#endif + + OPLChipBase *chip; +#ifdef ENABLE_HW_OPL_DOS + chip = new DOS_HW_OPL(); + +#else // ENABLE_HW_OPL_DOS + switch(emulator) + { + default: + assert(false); + abort(); +#ifndef ADLMIDI_DISABLE_NUKED_EMULATOR + case ADLMIDI_EMU_NUKED: /* Latest Nuked OPL3 */ + chip = new NukedOPL3; + break; + case ADLMIDI_EMU_NUKED_174: /* Old Nuked OPL3 1.4.7 modified and optimized */ + chip = new NukedOPL3v174; + break; +#endif +#ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR + case ADLMIDI_EMU_DOSBOX: + chip = new DosBoxOPL3; + break; +#endif +#ifndef ADLMIDI_DISABLE_OPAL_EMULATOR + case ADLMIDI_EMU_OPAL: + chip = new OpalOPL3; + break; +#endif +#ifndef ADLMIDI_DISABLE_JAVA_EMULATOR + case ADLMIDI_EMU_JAVA: + chip = new JavaOPL3; + break; +#endif +#ifndef ADLMIDI_DISABLE_ESFMU_EMULATOR + case ADLMIDI_EMU_ESFMu: + chip = new ESFMuOPL3; + break; +#endif +#ifndef ADLMIDI_DISABLE_MAME_OPL2_EMULATOR + case ADLMIDI_EMU_MAME_OPL2: + chip = new MameOPL2; + break; +#endif +#ifndef ADLMIDI_DISABLE_YMFM_EMULATOR + case ADLMIDI_EMU_YMFM_OPL2: + chip = new YmFmOPL2; + break; + case ADLMIDI_EMU_YMFM_OPL3: + chip = new YmFmOPL3; + break; +#endif +#ifdef ADLMIDI_ENABLE_OPL2_LLE_EMULATOR + case ADLMIDI_EMU_NUKED_OPL2_LLE: + chip = new Ym3812LLEOPL2; + break; +#endif +#ifdef ADLMIDI_ENABLE_OPL3_LLE_EMULATOR + case ADLMIDI_EMU_NUKED_OPL3_LLE: + chip = new Ymf262LLEOPL3; + break; +#endif + } +#endif // ENABLE_HW_OPL_DOS + + m_chips[i].reset(chip); + chip->setChipId((uint32_t)i); + chip->setRate((uint32_t)PCM_RATE); + +#ifndef ENABLE_HW_OPL_DOS + if(m_runAtPcmRate) + chip->setRunningAtPcmRate(true); +#endif + +# if defined(ADLMIDI_AUDIO_TICK_HANDLER) && !defined(ENABLE_HW_OPL_DOS) + chip->setAudioTickHandlerInstance(audioTickHandler); +# endif + + initChip(i); + } + + updateChannelCategories(); + silenceAll(); +} + +void OPL3::initChip(size_t chip) +{ + static const uint16_t data_opl3[] = + { + 0x004, 96, 0x004, 128, // Pulse timer + 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable + 0x001, 32, 0x105, 1, // Enable wave, OPL3 extensions + 0x08, 0 // CSW/Note Sel + }; + static const size_t data_opl3_size = sizeof(data_opl3) / sizeof(uint16_t); + + static const uint16_t data_opl2[] = + { + 0x004, 96, 0x004, 128, // Pulse timer + 0x001, 32, // Enable wave + 0x08, 0 // CSW/Note Sel + }; + static const size_t data_opl2_size = sizeof(data_opl2) / sizeof(uint16_t); + + // Report does emulator/interface supports full-panning stereo or not + if(chip == 0) + { + m_softPanningSup = m_chips[chip]->hasFullPanning(); + m_currentChipType = (int)m_chips[chip]->chipType(); + m_perChipChannels = OPL3_CHANNELS_RHYTHM_BASE; + + if(m_currentChipType == OPLChipBase::CHIPTYPE_OPL2) + { + m_perChipChannels = NUM_OF_OPL2_CHANNELS; + m_numFourOps = 0; // Can't have 4ops on OPL2 chip + } + } + + /* Clean-up channels from any playing junk sounds */ + for(size_t a = 0; a < m_perChipChannels; ++a) + { + writeRegI(chip, 0x20 + g_operatorsMap[a * 2], 0x00); + writeRegI(chip, 0x20 + g_operatorsMap[(a * 2) + 1], 0x00); + writeRegI(chip, 0xA0 + g_channelsMap[a], 0x00); + writeRegI(chip, 0xB0 + g_channelsMap[a], 0x00); + } + + if(m_currentChipType == OPLChipBase::CHIPTYPE_OPL2) + { + for(size_t a = 0; a < data_opl2_size; a += 2) + writeRegI(chip, data_opl2[a], (data_opl2[a + 1])); + } + else + { + for(size_t a = 0; a < data_opl3_size; a += 2) + writeRegI(chip, data_opl3[a], (data_opl3[a + 1])); + } +} + +#ifdef ADLMIDI_ENABLE_HW_SERIAL +void OPL3::resetSerial(const std::string &serialName, unsigned int baud, unsigned int protocol) +{ + m_serial = true; + m_serialName = serialName; + m_serialBaud = baud; + m_serialProtocol = protocol; + m_numChips = 1; // Only one chip! + m_softPanning = false; // Soft-panning doesn't work on hardware + reset(-1, 0, NULL); +} +#endif diff --git a/thirdparty/adlmidi/adlmidi_opl3.hpp b/thirdparty/adlmidi/adlmidi_opl3.hpp new file mode 100644 index 000000000..aee049f9f --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_opl3.hpp @@ -0,0 +1,430 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_OPL3_HPP +#define ADLMIDI_OPL3_HPP + +#include "oplinst.h" +#include "adlmidi_ptr.hpp" +#include "adlmidi_private.hpp" +#include "adlmidi_bankmap.h" + +#define BEND_COEFFICIENT 172.4387 + +#define OPL3_CHANNELS_MELODIC_BASE 0 +#define OPL3_CHANNELS_RHYTHM_BASE 18 + +#define NUM_OF_CHANNELS 23 +#define NUM_OF_4OP_CHANNELS 6 +#define NUM_OF_2OP_CHANNELS 18 +#define NUM_OF_2x2_CHANNELS 9 +#define NUM_OF_OPL2_CHANNELS 9 +#define NUM_OF_RM_CHANNELS 5 + +/** + * @brief OPL3 Chip management class + */ +class OPL3 +{ + friend class MIDIplay; + friend class AdlInstrumentTester; + friend int adlCalculateFourOpChannels(MIDIplay *play, bool silent); +public: + enum + { + PercussionTag = 1 << 15, + CustomBankTag = 0xFFFFFFFF + }; + + //! Total number of chip channels between all running emulators + uint32_t m_numChannels; + //! Just a padding. Reserved. + char _padding[4]; + //! Running chip emulators + std::vector > m_chips; + +private: + //! Cached patch data, needed by Touch() + std::vector m_insCache; + //! Value written to B0, cached, needed by NoteOff. + /*! Contains Key on/off state, octave block and frequency number values + */ + std::vector m_keyBlockFNumCache; + //! Cached BD registry value (flags register: DeepTremolo, DeepVibrato, and RhythmMode) + std::vector m_regBD; + //! Cached C0 register value (primarily for the panning state) + std::vector m_regC0; + +#ifdef ADLMIDI_ENABLE_HW_SERIAL + bool m_serial; + std::string m_serialName; + unsigned m_serialBaud; + unsigned m_serialProtocol; +#endif + //! Does loaded emulator supports soft panning? + bool m_softPanningSup; + //! Current type of chip + int m_currentChipType; + //! Number channels per chip + size_t m_perChipChannels; + + /*! + * \brief Current state of the synth (if values matched to setup, chips and arrays won't be fully re-created) + */ + struct State + { + int emulator; + uint32_t numChips; + unsigned long pcm_rate; + + State() + { + clear(); + } + + void clear() + { + emulator = -2; + numChips = 0; + pcm_rate = 0; + } + + bool cmp_rate(unsigned long rate) + { + bool ret = pcm_rate != rate; + + if(ret) + pcm_rate = rate; + + return ret; + } + + bool cmp(int emu, uint32_t chips) + { + bool ret = emu != emulator || chips != numChips; + + if(ret) + { + emulator = emu; + numChips = chips; + } + + return ret; + } + } m_curState; + +public: + /** + * @brief MIDI bank entry + */ + struct Bank + { + //! MIDI Bank instruments + OplInstMeta ins[128]; + }; + typedef BasicBankMap BankMap; + //! MIDI bank instruments data + BankMap m_insBanks; + //! MIDI bank-wide setup + OplBankSetup m_insBankSetup; + +public: + //! Blank instrument template + static const OplInstMeta m_emptyInstrument; + //! Total number of running concurrent emulated chips + uint32_t m_numChips; + //! Currently running embedded bank number. "CustomBankTag" means usign of the custom bank. + uint32_t m_embeddedBank; + //! Total number of needed four-operator channels in all running chips + uint32_t m_numFourOps; + //! Turn global Deep Tremolo mode on + bool m_deepTremoloMode; + //! Turn global Deep Vibrato mode on + bool m_deepVibratoMode; + //! Use Rhythm Mode percussions + bool m_rhythmMode; + //! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too. + bool m_scaleModulators; + //! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates. + bool m_runAtPcmRate; + //! Enable soft panning + bool m_softPanning; + //! Master volume, controlled via SysEx (0...127) + uint8_t m_masterVolume; + + //! Just a padding. Reserved. + char _padding2[3]; + + /** + * @brief Music playing mode + */ + enum MusicMode + { + //! MIDI mode + MODE_MIDI, + //! AIL XMIDI mode + MODE_XMIDI, + //! Id-Software Music mode + MODE_IMF, + //! Creative Music Files mode + MODE_CMF, + //! EA-MUS (a.k.a. RSXX) mode + MODE_RSXX + } m_musicMode; + + /** + * @brief Volume models enum + */ + enum VolumesScale + { + //! Generic volume model (linearization of logarithmic scale) + VOLUME_Generic, + //! OPL3 native logarithmic scale + VOLUME_NATIVE, + //! DMX volume scale logarithmic table + VOLUME_DMX, + //! Apoge Sound System volume scaling model + VOLUME_APOGEE, + //! Windows 9x SB16 driver volume scale table + VOLUME_9X, + //! DMX model with a fixed bug of AM voices + VOLUME_DMX_FIXED, + //! Apogee model with a fixed bug of AM voices + VOLUME_APOGEE_FIXED, + //! Audio Interfaces Library volume scaling model + VOLUME_AIL, + //! Windows 9x Generic FM driver volume scale table + VOLUME_9X_GENERIC_FM, + //! HMI Sound Operating System volume scale table + VOLUME_HMI, + //! HMI Sound Operating System volume scale model, older variant + VOLUME_HMI_OLD + } m_volumeScale; + + //! Channel allocation algorithm + ADLMIDI_ChannelAlloc m_channelAlloc; + + //! Reserved + char _padding3[8]; + + /** + * @brief Channel categiry enumeration + */ + enum ChanCat + { + //! Regular melodic/percussion channel + ChanCat_Regular = 0, + //! Four-op first part + ChanCat_4op_First = 1, + //! Four-op second part + ChanCat_4op_Second = 2, + //! Rhythm-mode Bass drum + ChanCat_Rhythm_Bass = 3, + //! Rhythm-mode Snare drum + ChanCat_Rhythm_Snare = 4, + //! Rhythm-mode Tom-Tom + ChanCat_Rhythm_Tom = 5, + //! Rhythm-mode Cymbal + ChanCat_Rhythm_Cymbal = 6, + //! Rhythm-mode Hi-Hat + ChanCat_Rhythm_HiHat = 7, + //! Rhythm-mode Secondary channel + ChanCat_Rhythm_Secondary = 8, + //! Here is no channel used (OPL2 only) + ChanCat_None = 9 + }; + + //! Category of the channel + /*! 1 = quad-first, 2 = quad-second, 0 = regular + 3 = percussion BassDrum + 4 = percussion Snare + 5 = percussion Tom + 6 = percussion Crash cymbal + 7 = percussion Hihat + 8 = percussion Secondary + */ + std::vector m_channelCategory; + + + /** + * @brief C.O. Constructor + */ + OPL3(); + + /** + * @brief C.O. Destructor + */ + ~OPL3(); + + /** + * @brief Checks are setup locked to be changed on the fly or not + * @return true when setup on the fly is locked + */ + bool setupLocked(); + + /** + * @brief Choose one of embedded banks + * @param bank ID of the bank + */ + void setEmbeddedBank(uint32_t bank); + + /** + * @brief Write data to OPL3 chip register + * @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored + * @param address Register address to write + * @param value Value to write + */ + void writeReg(size_t chip, uint16_t address, uint8_t value); + + /** + * @brief Write data to OPL3 chip register + * @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored + * @param address Register address to write + * @param value Value to write + */ + void writeRegI(size_t chip, uint32_t address, uint32_t value); + + /** + * @brief Write to soft panning control of OPL3 chip emulator + * @param chip Index of emulated chip. + * @param address Register of channel to write + * @param value Value to write + */ + void writePan(size_t chip, uint32_t address, uint32_t value); + + /** + * @brief Off the note in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + */ + void noteOff(size_t c); + + /** + * @brief On the note in specified chip channel with specified frequency of the tone + * @param c1 Channel of chip [or master 4-op channel] (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param c2 Second 4-op channel of chip, unused for 2op (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param tone The tone to play (integer part - MIDI halftone, decimal part - relative bend offset) + */ + void noteOn(size_t c1, size_t c2, double tone); + + /** + * @brief Change setup of instrument in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param velocity Note velocity (from 0 to 127) + * @param channelVolume Channel volume level (from 0 to 127) + * @param channelExpression Channel expression level (from 0 to 127) + * @param brightness CC74 Brightness level (from 0 to 127) + * @param isDrum Is this a drum note? This flag is needed for some volume model algorithms + */ + void touchNote(size_t c, + uint_fast32_t velocity, + uint_fast32_t channelVolume = 127, + uint_fast32_t channelExpression = 127, + uint_fast32_t brightness = 127, + bool isDrum = false); + + /** + * @brief Set the instrument into specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param instrument Instrument data to set into the chip channel + */ + void setPatch(size_t c, const OplTimbre &instrument); + + /** + * @brief Set panpot position + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param value 3-bit panpot value + */ + void setPan(size_t c, uint8_t value); + + /** + * @brief Shut up all chip channels + */ + void silenceAll(); + + /** + * @brief Commit updated flag states to chip registers + */ + void updateChannelCategories(); + + /** + * @brief commit deepTremolo and deepVibrato flags + */ + void commitDeepFlags(); + + /** + * @brief Set the volume scaling model + * @param volumeModel Type of volume scale model scale + */ + void setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel); + + /** + * @brief Get the volume scaling model + */ + ADLMIDI_VolumeModels getVolumeScaleModel(); + + /** + * @brief Clean up all running emulated chip instances + */ + void clearChips(); + + /** + * @brief Reset chip properties and initialize them + * @param emulator Type of chip emulator + * @param PCM_RATE Output sample rate to generate on output + * @param audioTickHandler PCM-accurate clock hook + */ + void reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler); + + void initChip(size_t chip); + +#ifdef ADLMIDI_ENABLE_HW_SERIAL + /** + * @brief Reset chip properties for hardware use + * @param emulator + * @param PCM_RATE + * @param audioTickHandler + */ + void resetSerial(const std::string &serialName, unsigned int baud, unsigned int protocol); +#endif +}; + +/** + * @brief Check emulator availability + * @param emulator Emulator ID (ADL_Emulator) + * @return true when emulator is available + */ +extern bool adl_isEmulatorAvailable(int emulator); + +/** + * @brief Find highest emulator + * @return The ADL_Emulator enum value which contains ID of highest emulator + */ +extern int adl_getHighestEmulator(); + +/** + * @brief Find lowest emulator + * @return The ADL_Emulator enum value which contains ID of lowest emulator + */ +extern int adl_getLowestEmulator(); + +#endif // ADLMIDI_OPL3_HPP diff --git a/thirdparty/adlmidi/adlmidi_private.cpp b/thirdparty/adlmidi/adlmidi_private.cpp new file mode 100644 index 000000000..53a496f53 --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_private.cpp @@ -0,0 +1,127 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adlmidi_midiplay.hpp" +#include "adlmidi_opl3.hpp" +#include "adlmidi_private.hpp" +#include "wopl/wopl_file.h" + + +std::string ADLMIDI_ErrorString; + +// Generator callback on audio rate ticks + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate) +{ + reinterpret_cast(instance)->AudioTick(chipId, rate); +} +#endif + +int adlCalculateFourOpChannels(MIDIplay *play, bool silent) +{ + Synth &synth = *play->m_synth; + size_t n_fourop[2] = {0, 0}, n_total[2] = {0, 0}; + bool rhythmModeNeeded = false; + size_t numFourOps = 0; + + //Automatically calculate how much 4-operator channels is necessary + { + //For custom bank + Synth::BankMap::iterator it = synth.m_insBanks.begin(); + Synth::BankMap::iterator end = synth.m_insBanks.end(); + for(; it != end; ++it) + { + size_t bank = it->first; + size_t div = (bank & Synth::PercussionTag) ? 1 : 0; + for(size_t i = 0; i < 128; ++i) + { + OplInstMeta &ins = it->second.ins[i]; + if(ins.flags & OplInstMeta::Flag_NoSound) + continue; + if((ins.flags & OplInstMeta::Flag_Real4op) != 0) + ++n_fourop[div]; + ++n_total[div]; + if(div && ((ins.flags & OplInstMeta::Mask_RhythmMode) != 0)) + rhythmModeNeeded = true; + } + } + } + + // All 2ops (no 4ops) + if((n_fourop[0] == 0) && (n_fourop[1] == 0)) + numFourOps = 0; + // All 2op melodics and Some (or All) 4op drums + else if((n_fourop[0] == 0) && (n_fourop[1] > 0)) + numFourOps = 2; + // Many 4op melodics + else if((n_fourop[0] >= (n_total[0] * 7) / 8)) + numFourOps = 6; + // Few 4op melodics + else if(n_fourop[0] > 0) + numFourOps = 4; + + synth.m_numFourOps = static_cast(numFourOps * synth.m_numChips); + + // Update channel categories and set up four-operator channels + if(!silent) + synth.updateChannelCategories(); + + // Set rhythm mode when it needed + synth.m_rhythmMode = rhythmModeNeeded; + + return 0; +} + +#ifndef DISABLE_EMBEDDED_BANKS +void adlFromInstrument(const BanksDump::InstrumentEntry &instIn, OplInstMeta &instOut) +{ + instOut.voice2_fine_tune = 0.0; + if(instIn.secondVoiceDetune != 0) + instOut.voice2_fine_tune = (double)((((int)instIn.secondVoiceDetune + 128) >> 1) - 64) / 32.0; + + instOut.midiVelocityOffset = instIn.midiVelocityOffset; + instOut.drumTone = instIn.percussionKeyNumber; + instOut.flags = (instIn.instFlags & WOPL_Ins_4op) && (instIn.instFlags & WOPL_Ins_Pseudo4op) ? OplInstMeta::Flag_Pseudo4op : 0; + instOut.flags|= (instIn.instFlags & WOPL_Ins_4op) && ((instIn.instFlags & WOPL_Ins_Pseudo4op) == 0) ? OplInstMeta::Flag_Real4op : 0; + instOut.flags|= (instIn.instFlags & WOPL_Ins_IsBlank) ? OplInstMeta::Flag_NoSound : 0; + instOut.flags|= instIn.instFlags & WOPL_RhythmModeMask; + + for(size_t op = 0; op < 2; op++) + { + if((instIn.ops[(op * 2) + 0] < 0) || (instIn.ops[(op * 2) + 1] < 0)) + break; + const BanksDump::Operator &op1 = g_embeddedBanksOperators[instIn.ops[(op * 2) + 0]]; + const BanksDump::Operator &op2 = g_embeddedBanksOperators[instIn.ops[(op * 2) + 1]]; + instOut.op[op].modulator_E862 = op1.d_E862; + instOut.op[op].modulator_40 = op1.d_40; + instOut.op[op].carrier_E862 = op2.d_E862; + instOut.op[op].carrier_40 = op2.d_40; + instOut.op[op].feedconn = (instIn.fbConn >> (op * 8)) & 0xFF; + instOut.op[op].noteOffset = static_cast(op == 0 ? instIn.noteOffset1 : instIn.noteOffset2); + } + + instOut.soundKeyOnMs = instIn.delay_on_ms; + instOut.soundKeyOffMs = instIn.delay_off_ms; +} +#endif diff --git a/thirdparty/adlmidi/adlmidi_private.hpp b/thirdparty/adlmidi/adlmidi_private.hpp new file mode 100644 index 000000000..dcd0d0379 --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_private.hpp @@ -0,0 +1,237 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_PRIVATE_HPP +#define ADLMIDI_PRIVATE_HPP + +// Setup compiler defines useful for exporting required public API symbols in gme.cpp +#ifndef ADLMIDI_EXPORT +# if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL) +# define ADLMIDI_EXPORT __declspec(dllexport) +# elif defined (LIBADLMIDI_VISIBILITY) && defined (__GNUC__) +# define ADLMIDI_EXPORT __attribute__((visibility ("default"))) +# else +# define ADLMIDI_EXPORT +# endif +#endif + + +#ifdef _WIN32 +#define NOMINMAX 1 +#endif + +#if defined(_WIN32) && !defined(__WATCOMC__) +# undef NO_OLDNAMES +# include +# ifdef _MSC_VER +# ifdef _WIN64 +typedef __int64 ssize_t; +# else +typedef __int32 ssize_t; +# endif +# else +# ifdef _WIN64 +typedef int64_t ssize_t; +# else +typedef int32_t ssize_t; +# endif +# endif +# include +#endif + +#if defined(__DJGPP__) || (defined(__WATCOMC__) && (defined(__DOS__) || defined(__DOS4G__) || defined(__DOS4GNZ__))) +# include +# ifdef __DJGPP__ +# include +# include +# include +# include +# include +# endif +#endif + +#include +#include +#include +//#ifdef __WATCOMC__ +//#include //TODO: Implemnet a workaround for OpenWatcom to fix a crash while using those containers +//#include +//#else +#include +#include +#include // nothrow +//#endif +#include +#include +#include +#include +#include +#include +#include // vector +#include // deque +#include // exp, log, ceil +#if defined(__WATCOMC__) +#include // round, sqrt +#endif +#include +#include +#include +#include // numeric_limit + +#ifndef _WIN32 +#include +#endif + +#include +#include + +/* + * Workaround for some compilers are has no those macros in their headers! + */ +#ifndef INT8_MIN +#define INT8_MIN (-0x7f - 1) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-0x7fff - 1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-0x7fffffff - 1) +#endif +#ifndef INT8_MAX +#define INT8_MAX 0x7f +#endif +#ifndef INT16_MAX +#define INT16_MAX 0x7fff +#endif +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff +#endif + +class FileAndMemReader; + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER +// Rename class to avoid ABI collisions +#define BW_MidiSequencer AdlMidiSequencer +class BW_MidiSequencer; +typedef BW_MidiSequencer MidiSequencer; +typedef struct BW_MidiRtInterface BW_MidiRtInterface; +#endif//ADLMIDI_DISABLE_MIDI_SEQUENCER + +class OPL3; +class OPLChipBase; + +typedef class OPL3 Synth; + +#include "oplinst.h" +#include "adlmidi_db.h" + +#define ADLMIDI_BUILD +#include "adlmidi.h" //Main API + +#include "adlmidi_ptr.hpp" + +class MIDIplay; + +#define ADL_UNUSED(x) (void)x + +#ifdef ENABLE_HW_OPL_DOS +#define ADL_MAX_CHIPS 1 +#define ADL_MAX_CHIPS_STR "1" //Why not just "#MaxCards" ? Watcom fails to pass this with "syntax error" :-P +#else +#define ADL_MAX_CHIPS 100 +#define ADL_MAX_CHIPS_STR "100" +#endif + +extern std::string ADLMIDI_ErrorString; + +/* + Sample conversions to various formats +*/ +template +inline Real adl_cvtReal(int32_t x) +{ + return static_cast(x) * (static_cast(1) / static_cast(INT16_MAX)); +} + +inline int32_t adl_cvtS16(int32_t x) +{ + x = (x < INT16_MIN) ? (INT16_MIN) : x; + x = (x > INT16_MAX) ? (INT16_MAX) : x; + return x; +} + +inline int32_t adl_cvtS8(int32_t x) +{ + return adl_cvtS16(x) / 256; +} +inline int32_t adl_cvtS24(int32_t x) +{ + return adl_cvtS16(x) * 256; +} +inline int32_t adl_cvtS32(int32_t x) +{ + return adl_cvtS16(x) * 65536; +} +inline int32_t adl_cvtU16(int32_t x) +{ + return adl_cvtS16(x) - INT16_MIN; +} +inline int32_t adl_cvtU8(int32_t x) +{ + return (adl_cvtS16(x) / 256) - INT8_MIN; +} +inline int32_t adl_cvtU24(int32_t x) +{ + enum { int24_min = -(1 << 23) }; + return adl_cvtS24(x) - int24_min; +} +inline int32_t adl_cvtU32(int32_t x) +{ + // unsigned operation because overflow on signed integers is undefined + return (uint32_t)adl_cvtS32(x) - (uint32_t)INT32_MIN; +} + +template +void adl_fill_vector(std::vector &v, const T &value) +{ + for(typename std::vector::iterator it = v.begin(); it != v.end(); ++it) + *it = value; +} + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); +#endif + +/** + * @brief Automatically calculate and enable necessary count of 4-op channels on emulated chips + * @param device Library context + * @param silent Don't re-count channel categories + * @return Always 0 + */ +extern int adlCalculateFourOpChannels(MIDIplay *play, bool silent = false); + +#ifndef DISABLE_EMBEDDED_BANKS +extern void adlFromInstrument(const BanksDump::InstrumentEntry &instIn, OplInstMeta &instOut); +#endif + +#endif // ADLMIDI_PRIVATE_HPP diff --git a/thirdparty/adlmidi/adlmidi_ptr.hpp b/thirdparty/adlmidi/adlmidi_ptr.hpp new file mode 100644 index 000000000..890e939bd --- /dev/null +++ b/thirdparty/adlmidi/adlmidi_ptr.hpp @@ -0,0 +1,217 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_PTR_HPP_THING +#define ADLMIDI_PTR_HPP_THING + +#include // swap +#include +#include + +/* + Generic deleters for smart pointers + */ +template +struct ADLMIDI_DefaultDelete +{ + void operator()(T *x) { delete x; } +}; +template +struct ADLMIDI_DefaultArrayDelete +{ + void operator()(T *x) { delete[] x; } +}; +struct ADLMIDI_CDelete +{ + void operator()(void *x) { free(x); } +}; + +/* + Safe unique pointer for C++98, non-copyable but swappable. +*/ +template< class T, class Deleter = ADLMIDI_DefaultDelete > +class AdlMIDI_UPtr +{ + T *m_p; +public: + explicit AdlMIDI_UPtr(T *p = NULL) + : m_p(p) {} + ~AdlMIDI_UPtr() + { + reset(); + } + + void reset(T *p = NULL) + { + if(p != m_p) { + if(m_p) { + Deleter del; + del(m_p); + } + m_p = p; + } + } + + void swap(AdlMIDI_UPtr &other) + { + std::swap(m_p, other.m_p); + } + + T *get() const + { + return m_p; + } + T &operator*() const + { + return *m_p; + } + T *operator->() const + { + return m_p; + } + T &operator[](size_t index) const + { + return m_p[index]; + } +private: + AdlMIDI_UPtr(const AdlMIDI_UPtr &); + AdlMIDI_UPtr &operator=(const AdlMIDI_UPtr &); +}; + +template +void swap(AdlMIDI_UPtr &a, AdlMIDI_UPtr &b) +{ + a.swap(b); +} + +/** + Unique pointer for arrays. + */ +template +class AdlMIDI_UPtrArray : + public AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete > +{ +public: + explicit AdlMIDI_UPtrArray(T *p = NULL) + : AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete >(p) {} +}; + +/** + Unique pointer for C memory. + */ +template +class AdlMIDI_CPtr : + public AdlMIDI_UPtr< T, ADLMIDI_CDelete > +{ +public: + explicit AdlMIDI_CPtr(T *p = NULL) + : AdlMIDI_UPtr< T, ADLMIDI_CDelete >(p) {} +}; + +/* + Shared pointer with non-atomic counter + FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it +*/ +template< class T, class Deleter = ADLMIDI_DefaultDelete > +class AdlMIDI_SPtr +{ + T *m_p; + size_t *m_counter; +public: + explicit AdlMIDI_SPtr(T *p = NULL) + : m_p(p), m_counter(p ? new size_t(1) : NULL) {} + ~AdlMIDI_SPtr() + { + reset(NULL); + } + + AdlMIDI_SPtr(const AdlMIDI_SPtr &other) + : m_p(other.m_p), m_counter(other.m_counter) + { + if(m_counter) + ++*m_counter; + } + + AdlMIDI_SPtr &operator=(const AdlMIDI_SPtr &other) + { + if(this == &other) + return *this; + reset(); + m_p = other.m_p; + m_counter = other.m_counter; + if(m_counter) + ++*m_counter; + return *this; + } + + void reset(T *p = NULL) + { + if(p != m_p) { + if(m_p && --*m_counter == 0) { + Deleter del; + del(m_p); + if(!p) { + delete m_counter; + m_counter = NULL; + } + } + m_p = p; + if(p) { + if(!m_counter) + m_counter = new size_t; + *m_counter = 1; + } + } + } + + T *get() const + { + return m_p; + } + T &operator*() const + { + return *m_p; + } + T *operator->() const + { + return m_p; + } + T &operator[](size_t index) const + { + return m_p[index]; + } +}; + +/** + Shared pointer for arrays. + */ +template +class AdlMIDI_SPtrArray : + public AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete > +{ +public: + explicit AdlMIDI_SPtrArray(T *p = NULL) + : AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete >(p) {} +}; + +#endif //ADLMIDI_PTR_HPP_THING diff --git a/thirdparty/adlmidi/chips/common/mutex.hpp b/thirdparty/adlmidi/chips/common/mutex.hpp new file mode 100644 index 000000000..85c3a3a6f --- /dev/null +++ b/thirdparty/adlmidi/chips/common/mutex.hpp @@ -0,0 +1,170 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DOSBOX_NO_MUTEX +# if defined(USE_LIBOGC_MUTEX) +# include +typedef mutex_t MutexNativeObject; +# elif defined(USE_WUT_MUTEX) +# if __cplusplus < 201103L || (defined(_MSC_VER) && _MSC_VER < 1900) +# define static_assert(x, y) +# endif +# include +typedef OSMutex MutexNativeObject; +# elif !defined(_WIN32) +# include +typedef pthread_mutex_t MutexNativeObject; +# else +# include +typedef CRITICAL_SECTION MutexNativeObject; +# endif +#endif + + +class Mutex +{ +public: + Mutex(); + ~Mutex(); + void lock(); + void unlock(); +private: +#if !defined(DOSBOX_NO_MUTEX) + MutexNativeObject m; +#endif + Mutex(const Mutex &); + Mutex &operator=(const Mutex &); +}; + +class MutexHolder +{ +public: + explicit MutexHolder(Mutex &m) : m(m) { m.lock(); } + ~MutexHolder() { m.unlock(); } +private: + Mutex &m; + MutexHolder(const MutexHolder &); + MutexHolder &operator=(const MutexHolder &); +}; + +#if defined(DOSBOX_NO_MUTEX) // No mutex, just a dummy + +inline Mutex::Mutex() +{} + +inline Mutex::~Mutex() +{} + +inline void Mutex::lock() +{} + +inline void Mutex::unlock() +{} + +#elif defined(USE_WUT_MUTEX) + +inline Mutex::Mutex() +{ + OSInitMutex(&m); +} + +inline Mutex::~Mutex() +{} + +inline void Mutex::lock() +{ + OSLockMutex(&m); +} + +inline void Mutex::unlock() +{ + OSUnlockMutex(&m); +} + +#elif defined(USE_LIBOGC_MUTEX) + +inline Mutex::Mutex() +{ + m = LWP_MUTEX_NULL; + LWP_MutexInit(&m, 0); +} + +inline Mutex::~Mutex() +{ + LWP_MutexDestroy(m); +} + +inline void Mutex::lock() +{ + LWP_MutexLock(m); +} + +inline void Mutex::unlock() +{ + LWP_MutexUnlock(m); +} + +#elif !defined(_WIN32) // pthread + +inline Mutex::Mutex() +{ + pthread_mutex_init(&m, NULL); +} + +inline Mutex::~Mutex() +{ + pthread_mutex_destroy(&m); +} + +inline void Mutex::lock() +{ + pthread_mutex_lock(&m); +} + +inline void Mutex::unlock() +{ + pthread_mutex_unlock(&m); +} + +#else // Win32 + +inline Mutex::Mutex() +{ + InitializeCriticalSection(&m); +} + +inline Mutex::~Mutex() +{ + DeleteCriticalSection(&m); +} + +inline void Mutex::lock() +{ + EnterCriticalSection(&m); +} + +inline void Mutex::unlock() +{ + LeaveCriticalSection(&m); +} +#endif diff --git a/thirdparty/adlmidi/chips/common/ptr.hpp b/thirdparty/adlmidi/chips/common/ptr.hpp new file mode 100644 index 000000000..c8c7055dc --- /dev/null +++ b/thirdparty/adlmidi/chips/common/ptr.hpp @@ -0,0 +1,110 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MY_PTR_HPP_THING +#define MY_PTR_HPP_THING + +#include +#include + +/* + Generic deleters for smart pointers + */ +template +struct My_DefaultDelete +{ + void operator()(T *x) { delete x; } +}; + +template +struct My_DefaultArrayDelete +{ + void operator()(T *x) { delete[] x; } +}; + +struct My_CDelete +{ + void operator()(void *x) { free(x); } +}; + +/* + Safe unique pointer for C++98, non-copyable but swappable. +*/ +template< class T, class Deleter = My_DefaultDelete > +class My_UPtr +{ + T *m_p; +public: + explicit My_UPtr(T *p = NULL) + : m_p(p) {} + ~My_UPtr() + { + reset(); + } + + void reset(T *p = NULL) + { + if(p != m_p) + { + if(m_p) + { + Deleter del; + del(m_p); + } + m_p = p; + } + } + + T *get() const + { + return m_p; + } + + T *release() + { + T *ret = m_p; + m_p = NULL; + return ret; + } + + T &operator*() const + { + return *m_p; + } + + T *operator->() const + { + return m_p; + } + + T &operator[](size_t index) const + { + return m_p[index]; + } + +private: + My_UPtr(const My_UPtr &); + My_UPtr &operator=(const My_UPtr &); +}; + +#endif // MY_PTR_HPP_THING diff --git a/thirdparty/adlmidi/chips/dos_hw_opl.cpp b/thirdparty/adlmidi/chips/dos_hw_opl.cpp new file mode 100644 index 000000000..a9f47652b --- /dev/null +++ b/thirdparty/adlmidi/chips/dos_hw_opl.cpp @@ -0,0 +1,163 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#ifdef __DJGPP__ +# include +#endif + +#include "dos_hw_opl.h" + +static uint16_t s_OPLBase[2] = {0x388, 0x38A}; +static char s_devName[80] = {0}; +static OPLChipBase::ChipType s_type = OPLChipBase::CHIPTYPE_OPL3; +static bool s_detected = false; + +static void s_updateDevName() +{ + const char *oplName = (s_type == OPLChipBase::CHIPTYPE_OPL3) ? "OPL3" : "OPL2"; + memset(s_devName, 0, sizeof(s_devName)); + snprintf(s_devName, 80, "%s at 0x%03X", oplName, s_OPLBase[0]); +} + +static void s_detect() +{ + if(s_detected) + return; + + const char *blaster = getenv("BLASTER"); + if(blaster) + { + size_t len = strlen(blaster); + for(size_t i = 0; i < len - 1; ++i) + { + if(blaster[i] == 'T') + { + switch(blaster[i + 1]) + { + case '1': + case '2': + s_type = OPLChipBase::CHIPTYPE_OPL2; + break; + case '3': + case '4': + case '6': + s_type = OPLChipBase::CHIPTYPE_OPL3; + break; + default: + s_type = OPLChipBase::CHIPTYPE_OPL2; + break; + } + + // printf("-- Detected BLASTER T%c\n", blaster[i + 1]); + + break; + } + } + } + else + s_type = OPLChipBase::CHIPTYPE_OPL2; + + s_detected = true; +} + +DOS_HW_OPL::DOS_HW_OPL() +{ + s_detect(); + s_updateDevName(); +} + +void DOS_HW_OPL::setOplAddress(uint16_t address) +{ + s_OPLBase[0] = address; + s_OPLBase[1] = address + 2; + s_updateDevName(); +} + +void DOS_HW_OPL::setChipType(ChipType type) +{ + s_type = type; + s_detected = true; // Assignd manually, no need to detect +} + +DOS_HW_OPL::~DOS_HW_OPL() +{ + DOS_HW_OPL::writeReg(0x0BD, 0); + if(s_type == CHIPTYPE_OPL3) + { + DOS_HW_OPL::writeReg(0x104, 0); + DOS_HW_OPL::writeReg(0x105, 0); + } +} + +void DOS_HW_OPL::writeReg(uint16_t addr, uint8_t data) +{ + assert(m_id <= 1); + unsigned o = addr >> 8; + unsigned port = s_OPLBase[m_id] + o * 2; + +# ifdef __DJGPP__ + outportb(port, addr); + + for(unsigned c = 0; c < 6; ++c) + inportb(port); + + outportb(port + 1, data); + + for(unsigned c = 0; c < 35; ++c) + inportb(port); +# endif + +# ifdef __WATCOMC__ + outp(port, addr); + + for(uint16_t c = 0; c < 6; ++c) + inp(port); + + outp(port + 1, data); + + for(uint16_t c = 0; c < 35; ++c) + inp(port); +# endif//__WATCOMC__ +} + +void DOS_HW_OPL::nativeGenerate(int16_t *frame) +{ + frame[0] = 0; + frame[1] = 0; +} + +const char *DOS_HW_OPL::emulatorName() +{ + return s_devName; +} + +bool DOS_HW_OPL::hasFullPanning() +{ + return false; +} + +OPLChipBase::ChipType DOS_HW_OPL::chipType() +{ + return s_type; +} diff --git a/thirdparty/adlmidi/chips/dos_hw_opl.h b/thirdparty/adlmidi/chips/dos_hw_opl.h new file mode 100644 index 000000000..5e3750b6d --- /dev/null +++ b/thirdparty/adlmidi/chips/dos_hw_opl.h @@ -0,0 +1,48 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef DOS_HW_OPL_H +#define DOS_HW_OPL_H + +#include "opl_chip_base.h" + +class DOS_HW_OPL : public OPLChipBaseT +{ +public: + DOS_HW_OPL(); + virtual ~DOS_HW_OPL() override; + + static void setChipType(ChipType type); + static void setOplAddress(uint16_t address); + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t /*rate*/) override {} + void reset() override {} + void writeReg(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // DOS_HW_OPL_H diff --git a/thirdparty/adlmidi/chips/dosbox/dbopl.cpp b/thirdparty/adlmidi/chips/dosbox/dbopl.cpp new file mode 100644 index 000000000..c5a702417 --- /dev/null +++ b/thirdparty/adlmidi/chips/dosbox/dbopl.cpp @@ -0,0 +1,1746 @@ +/* + * Copyright (C) 2002-2018 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator. + Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2 + Except for the table generation it's all integer math + Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms + The generation was based on the MAME implementation but tried to have it use less memory and be faster in general + MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times + + //TODO Don't delay first operator 1 sample in opl3 mode + //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter + //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though? + //TODO Check if having the same accuracy in all frequency multipliers sounds better or not + + //DUNNO Keyon in 4op, switch to 2op without keyoff. +*/ + + + +#include +#include +#include +#include +#include +#include "dbopl.h" +#include "../common/mutex.hpp" +#include "../common/ptr.hpp" + +#if defined(__GNUC__) && __GNUC__ > 3 +#define INLINE inline __attribute__((__always_inline__)) +#elif defined(_MSC_VER) +#define INLINE __forceinline +#else +#define INLINE inline +#endif + +#if defined(__GNUC__) +#if !defined(__clang__) +#define GCC_LIKELY(x) __builtin_expect(x, 1) +#define GCC_UNLIKELY(x) __builtin_expect(x, 0) +#else // !defined(__clang__) +#if !defined (__c2__) && defined(__has_builtin) +#if __has_builtin(__builtin_expect) +#define GCC_LIKELY(x) __builtin_expect(x, 1) +#define GCC_UNLIKELY(x) __builtin_expect(x, 0) +#endif // __has_builtin(__builtin_expect) +#endif // !defined (__c2__) && defined(__has_builtin) +#endif // !defined(__clang__) +#endif // defined(__GNUC__) + +#if !defined(GCC_LIKELY) +#define GCC_LIKELY(x) (x) +#define GCC_UNLIKELY(x) (x) +#endif + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/* + * Workaround for some compilers are has no those macros in their headers! + */ +#ifndef INT16_MIN +#define INT16_MIN (-0x7fff - 1) +#endif +#ifndef INT16_MAX +#define INT16_MAX 0x7fff +#endif + + +namespace DBOPL { + +#define OPLRATE ((double)(14318180.0 / 288.0)) +#define TREMOLO_TABLE 52 + +//Try to use most precision for frequencies +//Else try to keep different waves in synch +//#define WAVE_PRECISION 1 +#ifndef WAVE_PRECISION +//Wave bits available in the top of the 32bit range +//Original adlib uses 10.10, we use 10.22 +#define WAVE_BITS 10 +#else +//Need some extra bits at the top to have room for octaves and frequency multiplier +//We support to 8 times lower rate +//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits +#define WAVE_BITS 14 +#endif +#define WAVE_SH ( 32 - WAVE_BITS ) +#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 ) + +//Use the same accuracy as the waves +#define LFO_SH ( WAVE_SH - 10 ) +//LFO is controlled by our tremolo 256 sample limit +#define LFO_MAX ( 256 << ( LFO_SH ) ) + + +//Maximum amount of attenuation bits +//Envelope goes to 511, 9 bits +#if (DBOPL_WAVE == WAVE_TABLEMUL ) +//Uses the value directly +#define ENV_BITS ( 9 ) +#else +//Add 3 bits here for more accuracy and would have to be shifted up either way +#define ENV_BITS ( 9 ) +#endif +//Limits of the envelope with those bits and when the envelope goes silent +#define ENV_MIN 0 +#define ENV_EXTRA ( ENV_BITS - 9 ) +#define ENV_MAX ( 511 << ENV_EXTRA ) +#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) ) +#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT ) + +//Attack/decay/release rate counter shift +#define RATE_SH 24 +#define RATE_MASK ( ( 1 << RATE_SH ) - 1 ) +//Has to fit within 16bit lookuptable +#define MUL_SH 16 + +//Check some ranges +#if ENV_EXTRA > 3 +#error Too many envelope bits +#endif + + +//How much to substract from the base value for the final attenuation +static const Bit8u KslCreateTable[16] = { + //0 will always be be lower than 7 * 8 + 64, 32, 24, 19, + 16, 12, 11, 10, + 8, 6, 5, 4, + 3, 2, 1, 0, +}; + +#define M(_X_) ((Bit8u)( (_X_) * 2)) +static const Bit8u FreqCreateTable[16] = { + M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ), + M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15) +}; +#undef M + +//We're not including the highest attack rate, that gets a special value +static const Bit8u AttackSamplesTable[13] = { + 69, 55, 46, 40, + 35, 29, 23, 20, + 19, 15, 11, 10, + 9 +}; +//On a real opl these values take 8 samples to reach and are based upon larger tables +static const Bit8u EnvelopeIncreaseTable[13] = { + 4, 5, 6, 7, + 8, 10, 12, 14, + 16, 20, 24, 28, + 32, +}; + +#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) +static Bit16u ExpTable[ 256 ]; +#endif + +#if ( DBOPL_WAVE == WAVE_HANDLER ) +//PI table used by WAVEHANDLER +static Bit16u SinTable[ 512 ]; +#endif + +#if ( DBOPL_WAVE > WAVE_HANDLER ) +//Layout of the waveform table in 512 entry intervals +//With overlapping waves we reduce the table to half it's size + +// | |//\\|____|WAV7|//__|/\ |____|/\/\| +// |\\//| | |WAV7| | \/| | | +// |06 |0126|17 |7 |3 |4 |4 5 |5 | + +//6 is just 0 shifted and masked + +static Bit16s WaveTable[ 8 * 512 ]; +//Distance into WaveTable the wave starts +static const Bit16u WaveBaseTable[8] = { + 0x000, 0x200, 0x200, 0x800, + 0xa00, 0xc00, 0x100, 0x400, + +}; +//Mask the counter with this +static const Bit16u WaveMaskTable[8] = { + 1023, 1023, 511, 511, + 1023, 1023, 512, 1023, +}; + +//Where to start the counter on at keyon +static const Bit16u WaveStartTable[8] = { + 512, 0, 0, 0, + 0, 512, 512, 256, +}; +#endif + +#if ( DBOPL_WAVE == WAVE_TABLEMUL ) +static Bit16u MulTable[ 384 ]; +#endif + +static Bit8u KslTable[ 8 * 16 ]; +static Bit8u TremoloTable[ TREMOLO_TABLE ]; +//Start of a channel behind the chip struct start +static Bit16u ChanOffsetTable[32]; +//Start of an operator behind the chip struct start +static Bit16u OpOffsetTable[64]; + +//The lower bits are the shift of the operator vibrato value +//The highest bit is right shifted to generate -1 or 0 for negation +//So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0 +static const Bit8s VibratoTable[ 8 ] = { + 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00, + 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80 +}; + +//Shift strength for the ksl value determined by ksl strength +static const Bit8u KslShiftTable[4] = { + 31,1,2,0 +}; + +// Pan law table +static const Bit16u PanLawTable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + +//Generate a table index and table shift value using input value from a selected rate +static void EnvelopeSelect( Bit8u val, Bit8u& index, Bit8u& shift ) { + if ( val < 13 * 4 ) { //Rate 0 - 12 + shift = 12 - ( val >> 2 ); + index = val & 3; + } else if ( val < 15 * 4 ) { //rate 13 - 14 + shift = 0; + index = val - 12 * 4; + } else { //rate 15 and up + shift = 0; + index = 12; + } +} + +#if ( DBOPL_WAVE == WAVE_HANDLER ) +/* + Generate the different waveforms out of the sine/exponetial table using handlers +*/ +static inline Bits MakeVolume( Bitu wave, Bitu volume ) { + Bitu total = wave + volume; + Bitu index = total & 0xff; + Bitu sig = ExpTable[ index ]; + Bitu exp = total >> 8; +#if 0 + //Check if we overflow the 31 shift limit + if ( exp >= 32 ) { + LOG_MSG( "WTF %d %d", total, exp ); + } +#endif + return (sig >> exp); +}; + +static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) { + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + Bitu wave = SinTable[i & 511]; + return (MakeVolume( wave, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) { + Bit32u wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) { + Bitu wave = SinTable[i & 511]; + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) { + Bitu wave = SinTable[i & 255]; + wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) { + //Twice as fast + i <<= 1; + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + Bitu wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return (MakeVolume( wave, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) { + //Twice as fast + i <<= 1; + Bitu wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) { + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + return (MakeVolume( 0, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) { + //Negative is reversed here + Bits neg = (( i >> 9) & 1) - 1; + Bitu wave = (i << 3); + //When negative the volume also runs backwards + wave = ((wave ^ neg) - neg) & 4095; + return (MakeVolume( wave, volume ) ^ neg) - neg; +} + +static const WaveHandler WaveHandlerTable[8] = { + WaveForm0, WaveForm1, WaveForm2, WaveForm3, + WaveForm4, WaveForm5, WaveForm6, WaveForm7 +}; + +#endif + +/* + Operator +*/ + +//We zero out when rate == 0 +inline void Operator::UpdateAttack( const Chip* chip ) { + Bit8u rate = reg60 >> 4; + if ( rate ) { + Bit8u val = (rate << 2) + ksr; + attackAdd = chip->attackRates[ val ]; + rateZero &= ~(1 << ATTACK); + } else { + attackAdd = 0; + rateZero |= (1 << ATTACK); + } +} +inline void Operator::UpdateDecay( const Chip* chip ) { + Bit8u rate = reg60 & 0xf; + if ( rate ) { + Bit8u val = (rate << 2) + ksr; + decayAdd = chip->linearRates[ val ]; + rateZero &= ~(1 << DECAY); + } else { + decayAdd = 0; + rateZero |= (1 << DECAY); + } +} +inline void Operator::UpdateRelease( const Chip* chip ) { + Bit8u rate = reg80 & 0xf; + if ( rate ) { + Bit8u val = (rate << 2) + ksr; + releaseAdd = chip->linearRates[ val ]; + rateZero &= ~(1 << RELEASE); + if ( !(reg20 & MASK_SUSTAIN ) ) { + rateZero &= ~( 1 << SUSTAIN ); + } + } else { + rateZero |= (1 << RELEASE); + releaseAdd = 0; + if ( !(reg20 & MASK_SUSTAIN ) ) { + rateZero |= ( 1 << SUSTAIN ); + } + } +} + +inline void Operator::UpdateAttenuation( ) { + Bit8u kslBase = (Bit8u)((chanData >> SHIFT_KSLBASE) & 0xff); + Bit32u tl = reg40 & 0x3f; + Bit8u kslShift = KslShiftTable[ reg40 >> 6 ]; + //Make sure the attenuation goes to the right bits + totalLevel = tl << ( ENV_BITS - 7 ); //Total level goes 2 bits below max + totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift; +} + +void Operator::UpdateFrequency( ) { + Bit32u freq = chanData & (( 1 << 10 ) - 1); + Bit32u block = (chanData >> 10) & 0xff; +#ifdef WAVE_PRECISION + block = 7 - block; + waveAdd = ( freq * freqMul ) >> block; +#else + waveAdd = ( freq << block ) * freqMul; +#endif + if ( reg20 & MASK_VIBRATO ) { + vibStrength = (Bit8u)(freq >> 7); + +#ifdef WAVE_PRECISION + vibrato = ( vibStrength * freqMul ) >> block; +#else + vibrato = ( vibStrength << block ) * freqMul; +#endif + } else { + vibStrength = 0; + vibrato = 0; + } +} + +void Operator::UpdateRates( const Chip* chip ) { + //Mame seems to reverse this where enabling ksr actually lowers + //the rate, but pdf manuals says otherwise? + Bit8u newKsr = (Bit8u)((chanData >> SHIFT_KEYCODE) & 0xff); + if ( !( reg20 & MASK_KSR ) ) { + newKsr >>= 2; + } + if ( ksr == newKsr ) + return; + ksr = newKsr; + UpdateAttack( chip ); + UpdateDecay( chip ); + UpdateRelease( chip ); +} + +INLINE Bit32s Operator::RateForward( Bit32u add ) { + rateIndex += add; + Bit32s ret = rateIndex >> RATE_SH; + rateIndex = rateIndex & RATE_MASK; + return ret; +} + +template< Operator::State yes> +Bits Operator::TemplateVolume( ) { + Bit32s vol = volume; + Bit32s change; + switch ( yes ) { + case OFF: + return ENV_MAX; + case ATTACK: + change = RateForward( attackAdd ); + if ( !change ) + return vol; + vol += ( (~vol) * change ) >> 3; + if ( vol < ENV_MIN ) { + volume = ENV_MIN; + rateIndex = 0; + SetState( DECAY ); + return ENV_MIN; + } + break; + case DECAY: + vol += RateForward( decayAdd ); + if ( GCC_UNLIKELY(vol >= sustainLevel) ) { + //Check if we didn't overshoot max attenuation, then just go off + if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { + volume = ENV_MAX; + SetState( OFF ); + return ENV_MAX; + } + //Continue as sustain + rateIndex = 0; + SetState( SUSTAIN ); + } + break; + case SUSTAIN: + if ( reg20 & MASK_SUSTAIN ) { + return vol; + } + //In sustain phase, but not sustaining, do regular release + /* fall through */ + case RELEASE: + vol += RateForward( releaseAdd );; + if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { + volume = ENV_MAX; + SetState( OFF ); + return ENV_MAX; + } + break; + } + volume = vol; + return vol; +} + +static const VolumeHandler VolumeHandlerTable[5] = { + &Operator::TemplateVolume< Operator::OFF >, + &Operator::TemplateVolume< Operator::RELEASE >, + &Operator::TemplateVolume< Operator::SUSTAIN >, + &Operator::TemplateVolume< Operator::DECAY >, + &Operator::TemplateVolume< Operator::ATTACK > +}; + +INLINE Bitu Operator::ForwardVolume() { + return currentLevel + (this->*volHandler)(); +} + + +INLINE Bitu Operator::ForwardWave() { + waveIndex += waveCurrent; + return waveIndex >> WAVE_SH; +} + +void Operator::Write20( const Chip* chip, Bit8u val ) { + Bit8u change = (reg20 ^ val ); + if ( !change ) + return; + reg20 = val; + //Shift the tremolo bit over the entire register, saved a branch, YES! + tremoloMask = (Bit8s)(val) >> 7; + tremoloMask &= ~(( 1 << ENV_EXTRA ) -1); + //Update specific features based on changes + if ( change & MASK_KSR ) { + UpdateRates( chip ); + } + //With sustain enable the volume doesn't change + if ( reg20 & MASK_SUSTAIN || ( !releaseAdd ) ) { + rateZero |= ( 1 << SUSTAIN ); + } else { + rateZero &= ~( 1 << SUSTAIN ); + } + //Frequency multiplier or vibrato changed + if ( change & (0xf | MASK_VIBRATO) ) { + freqMul = chip->freqMul[ val & 0xf ]; + UpdateFrequency(); + } +} + +void Operator::Write40( const Chip* /*chip*/, Bit8u val ) { + if (!(reg40 ^ val )) + return; + reg40 = val; + UpdateAttenuation( ); +} + +void Operator::Write60( const Chip* chip, Bit8u val ) { + Bit8u change = reg60 ^ val; + reg60 = val; + if ( change & 0x0f ) { + UpdateDecay( chip ); + } + if ( change & 0xf0 ) { + UpdateAttack( chip ); + } +} + +void Operator::Write80( const Chip* chip, Bit8u val ) { + Bit8u change = (reg80 ^ val ); + if ( !change ) + return; + reg80 = val; + Bit8u sustain = val >> 4; + //Turn 0xf into 0x1f + sustain |= ( sustain + 1) & 0x10; + sustainLevel = sustain << ( ENV_BITS - 5 ); + if ( change & 0x0f ) { + UpdateRelease( chip ); + } +} + +void Operator::WriteE0( const Chip* chip, Bit8u val ) { + if ( !(regE0 ^ val) ) + return; + //in opl3 mode you can always selet 7 waveforms regardless of waveformselect + Bit8u waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) ); + regE0 = val; +#if ( DBOPL_WAVE == WAVE_HANDLER ) + waveHandler = WaveHandlerTable[ waveForm ]; +#else + waveBase = WaveTable + WaveBaseTable[ waveForm ]; + waveStart = WaveStartTable[ waveForm ] << WAVE_SH; + waveMask = WaveMaskTable[ waveForm ]; +#endif +} + +INLINE void Operator::SetState( Bit8u s ) { + state = s; + volHandler = VolumeHandlerTable[ s ]; +} + +INLINE bool Operator::Silent() const { + if ( !ENV_SILENT( totalLevel + volume ) ) + return false; + if ( !(rateZero & ( 1 << state ) ) ) + return false; + return true; +} + +INLINE void Operator::Prepare( const Chip* chip ) { + currentLevel = totalLevel + (chip->tremoloValue & tremoloMask); + waveCurrent = waveAdd; + if ( vibStrength >> chip->vibratoShift ) { + Bit32s add = vibrato >> chip->vibratoShift; + //Sign extend over the shift value + Bit32s neg = chip->vibratoSign; + //Negate the add with -1 or 0 + add = ( add ^ neg ) - neg; + waveCurrent += add; + } +} + +void Operator::KeyOn( Bit8u mask ) { + if ( !keyOn ) { + //Restart the frequency generator +#if ( DBOPL_WAVE > WAVE_HANDLER ) + waveIndex = waveStart; +#else + waveIndex = 0; +#endif + rateIndex = 0; + SetState( ATTACK ); + } + keyOn |= mask; +} + +void Operator::KeyOff( Bit8u mask ) { + keyOn &= ~mask; + if ( !keyOn ) { + if ( state != OFF ) { + SetState( RELEASE ); + } + } +} + +INLINE Bits Operator::GetWave( Bitu index, Bitu vol ) { +#if ( DBOPL_WAVE == WAVE_HANDLER ) + return waveHandler( index, vol << ( 3 - ENV_EXTRA ) ); +#elif ( DBOPL_WAVE == WAVE_TABLEMUL ) + return (waveBase[ index & waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH; +#elif ( DBOPL_WAVE == WAVE_TABLELOG ) + Bit32s wave = waveBase[ index & waveMask ]; + Bit32u total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA ); + Bit32s sig = ExpTable[ total & 0xff ]; + Bit32u exp = total >> 8; + Bit32s neg = wave >> 16; + return ((sig ^ neg) - neg) >> exp; +#else +#error "No valid wave routine" +#endif +} + +Bits INLINE Operator::GetSample( Bits modulation ) { + Bitu vol = ForwardVolume(); + if ( ENV_SILENT( vol ) ) { + //Simply forward the wave + waveIndex += waveCurrent; + return 0; + } else { + Bitu index = ForwardWave(); + index += modulation; + return GetWave( index, vol ); + } +} + +Operator::Operator() { + chanData = 0; + freqMul = 0; + waveIndex = 0; + waveAdd = 0; + waveCurrent = 0; + keyOn = 0; + ksr = 0; + reg20 = 0; + reg40 = 0; + reg60 = 0; + reg80 = 0; + regE0 = 0; + SetState( OFF ); + rateZero = (1 << OFF); + sustainLevel = ENV_MAX; + currentLevel = ENV_MAX; + totalLevel = ENV_MAX; + volume = ENV_MAX; + releaseAdd = 0; +} + +/* + Channel +*/ + +Channel::Channel() { + old[0] = old[1] = 0; + chanData = 0; + regB0 = 0; + regC0 = 0; + maskLeft = -1; + maskRight = -1; + feedback = 31; + fourMask = 0; + synthHandler = &Channel::BlockTemplate< sm2FM >; +} + +void Channel::SetChanData( const Chip* chip, Bit32u data ) { + Bit32u change = chanData ^ data; + chanData = data; + Op( 0 )->chanData = data; + Op( 1 )->chanData = data; + //Since a frequency update triggered this, always update frequency + Op( 0 )->UpdateFrequency(); + Op( 1 )->UpdateFrequency(); + if ( change & ( 0xff << SHIFT_KSLBASE ) ) { + Op( 0 )->UpdateAttenuation(); + Op( 1 )->UpdateAttenuation(); + } + if ( change & ( 0xff << SHIFT_KEYCODE ) ) { + Op( 0 )->UpdateRates( chip ); + Op( 1 )->UpdateRates( chip ); + } +} + +void Channel::UpdateFrequency( const Chip* chip, Bit8u fourOp ) { + //Extrace the frequency bits + Bit32u data = chanData & 0xffff; + Bit32u kslBase = KslTable[ data >> 6 ]; + Bit32u keyCode = ( data & 0x1c00) >> 9; + if ( chip->reg08 & 0x40 ) { + keyCode |= ( data & 0x100)>>8; /* notesel == 1 */ + } else { + keyCode |= ( data & 0x200)>>9; /* notesel == 0 */ + } + //Add the keycode and ksl into the highest bits of chanData + data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE ); + ( this + 0 )->SetChanData( chip, data ); + if ( fourOp & 0x3f ) { + ( this + 1 )->SetChanData( chip, data ); + } +} + +void Channel::WriteA0( const Chip* chip, Bit8u val ) { + Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; + //Don't handle writes to silent fourop channels + if ( fourOp > 0x80 ) + return; + Bit32u change = (chanData ^ val ) & 0xff; + if ( change ) { + chanData ^= change; + UpdateFrequency( chip, fourOp ); + } +} + +void Channel::WriteB0( const Chip* chip, Bit8u val ) { + Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; + //Don't handle writes to silent fourop channels + if ( fourOp > 0x80 ) + return; + Bitu change = (chanData ^ ( val << 8 ) ) & 0x1f00; + if ( change ) { + chanData ^= change; + UpdateFrequency( chip, fourOp ); + } + //Check for a change in the keyon/off state + if ( !(( val ^ regB0) & 0x20)) + return; + regB0 = val; + if ( val & 0x20 ) { + Op(0)->KeyOn( 0x1 ); + Op(1)->KeyOn( 0x1 ); + if ( fourOp & 0x3f ) { + ( this + 1 )->Op(0)->KeyOn( 1 ); + ( this + 1 )->Op(1)->KeyOn( 1 ); + } + } else { + Op(0)->KeyOff( 0x1 ); + Op(1)->KeyOff( 0x1 ); + if ( fourOp & 0x3f ) { + ( this + 1 )->Op(0)->KeyOff( 1 ); + ( this + 1 )->Op(1)->KeyOff( 1 ); + } + } +} + +void Channel::WriteC0(const Chip* chip, Bit8u val) { + Bit8u change = val ^ regC0; + if (!change) + return; + regC0 = val; + feedback = (regC0 >> 1) & 7; + if (feedback) { + //We shift the input to the right 10 bit wave index value + feedback = 9 - feedback; + } + else { + feedback = 31; + } + UpdateSynth(chip); +} + +void Channel::WritePan(Bit8u val) { + panLeft = PanLawTable[val & 0x7F]; + panRight = PanLawTable[0x7F - (val & 0x7F)]; +} + +void Channel::UpdateSynth( const Chip* chip ) { + //Select the new synth mode + if ( chip->opl3Active ) { + //4-op mode enabled for this channel + if ( (chip->reg104 & fourMask) & 0x3f ) { + Channel* chan0, *chan1; + //Check if it's the 2nd channel in a 4-op + if ( !(fourMask & 0x80 ) ) { + chan0 = this; + chan1 = this + 1; + } else { + chan0 = this - 1; + chan1 = this; + } + + Bit8u synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 ); + switch ( synth ) { + case 0: + chan0->synthHandler = &Channel::BlockTemplate< sm3FMFM >; + break; + case 1: + chan0->synthHandler = &Channel::BlockTemplate< sm3AMFM >; + break; + case 2: + chan0->synthHandler = &Channel::BlockTemplate< sm3FMAM >; + break; + case 3: + chan0->synthHandler = &Channel::BlockTemplate< sm3AMAM >; + break; + } + //Disable updating percussion channels + } else if ((fourMask & 0x40) && ( chip->regBD & 0x20) ) { + + //Regular dual op, am or fm + } else if (regC0 & 1 ) { + synthHandler = &Channel::BlockTemplate< sm3AM >; + } else { + synthHandler = &Channel::BlockTemplate< sm3FM >; + } + maskLeft = (regC0 & 0x10 ) ? -1 : 0; + maskRight = (regC0 & 0x20 ) ? -1 : 0; + //opl2 active + } else { + //Disable updating percussion channels + if ( (fourMask & 0x40) && ( chip->regBD & 0x20 ) ) { + + //Regular dual op, am or fm + } else if (regC0 & 1 ) { + synthHandler = &Channel::BlockTemplate< sm2AM >; + } else { + synthHandler = &Channel::BlockTemplate< sm2FM >; + } + } +} + +template< bool opl3Mode> +INLINE void Channel::GeneratePercussion( Chip* chip, Bit32s* output ) { + Channel* chan = this; + + //BassDrum + Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; + old[0] = old[1]; + old[1] = static_cast(Op(0)->GetSample( mod )); + + //When bassdrum is in AM mode first operator is ignoed + if ( chan->regC0 & 1 ) { + mod = 0; + } else { + mod = old[0]; + } + Bit32s sample = static_cast(Op(1)->GetSample( mod )); + + + //Precalculate stuff used by other outputs + Bit32u noiseBit = chip->ForwardNoise() & 0x1; + Bit32u c2 = static_cast(Op(2)->ForwardWave()); + Bit32u c5 = static_cast(Op(5)->ForwardWave()); + Bit32u phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00; + + //Hi-Hat + Bit32u hhVol = static_cast(Op(2)->ForwardVolume()); + if ( !ENV_SILENT( hhVol ) ) { + Bit32u hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 ))); + sample += static_cast(Op(2)->GetWave( hhIndex, hhVol )); + } + //Snare Drum + Bit32u sdVol = static_cast(Op(3)->ForwardVolume()); + if ( !ENV_SILENT( sdVol ) ) { + Bit32u sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 ); + sample += static_cast(Op(3)->GetWave( sdIndex, sdVol )); + } + //Tom-tom + sample += static_cast(Op(4)->GetSample( 0 )); + + //Top-Cymbal + Bit32u tcVol = static_cast(Op(5)->ForwardVolume()); + if ( !ENV_SILENT( tcVol ) ) { + Bit32u tcIndex = (1 + phaseBit) << 8; + sample += static_cast(Op(5)->GetWave( tcIndex, tcVol )); + } + sample <<= 1; + if ( opl3Mode ) { + output[0] += sample; + output[1] += sample; + } else { + output[0] += sample; + } +} + +template +Channel* Channel::BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ) { + switch( mode ) { + case sm2AM: + case sm3AM: + if ( Op(0)->Silent() && Op(1)->Silent() ) { + old[0] = old[1] = 0; + return (this + 1); + } + break; + case sm2FM: + case sm3FM: + if ( Op(1)->Silent() ) { + old[0] = old[1] = 0; + return (this + 1); + } + break; + case sm3FMFM: + if ( Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3AMFM: + if ( Op(0)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3FMAM: + if ( Op(1)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3AMAM: + if ( Op(0)->Silent() && Op(2)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + default: + break; + } + //Init the operators with the the current vibrato and tremolo values + Op( 0 )->Prepare( chip ); + Op( 1 )->Prepare( chip ); + if ( mode > sm4Start ) { + Op( 2 )->Prepare( chip ); + Op( 3 )->Prepare( chip ); + } + if ( mode > sm6Start ) { + Op( 4 )->Prepare( chip ); + Op( 5 )->Prepare( chip ); + } + for ( Bitu i = 0; i < samples; i++ ) { + //Early out for percussion handlers + if ( mode == sm2Percussion ) { + GeneratePercussion( chip, output + i ); + continue; //Prevent some unitialized value bitching + } else if ( mode == sm3Percussion ) { + GeneratePercussion( chip, output + i * 2 ); + continue; //Prevent some unitialized value bitching + } + + //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise + Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; + old[0] = old[1]; + old[1] = static_cast(Op(0)->GetSample( mod )); + Bit32s sample; + Bit32s out0 = old[0]; + if ( mode == sm2AM || mode == sm3AM ) { + sample = static_cast(out0 + Op(1)->GetSample( 0 )); + } else if ( mode == sm2FM || mode == sm3FM ) { + sample = static_cast(Op(1)->GetSample( out0 )); + } else if ( mode == sm3FMFM ) { + Bits next = Op(1)->GetSample( out0 ); + next = Op(2)->GetSample( next ); + sample = static_cast(Op(3)->GetSample( next )); + } else if ( mode == sm3AMFM ) { + sample = out0; + Bits next = Op(1)->GetSample( 0 ); + next = Op(2)->GetSample( next ); + sample += static_cast(Op(3)->GetSample( next )); + } else if ( mode == sm3FMAM ) { + sample = static_cast(Op(1)->GetSample( out0 )); + Bits next = Op(2)->GetSample( 0 ); + sample += static_cast(Op(3)->GetSample( next )); + } else if ( mode == sm3AMAM ) { + sample = out0; + Bits next = Op(1)->GetSample( 0 ); + sample += static_cast(Op(2)->GetSample( next )); + sample += static_cast(Op(3)->GetSample( 0 )); + } + switch( mode ) { + case sm2AM: + case sm2FM: + output[ i ] += sample; + break; + case sm3AM: + case sm3FM: + case sm3FMFM: + case sm3AMFM: + case sm3FMAM: + case sm3AMAM: + output[ i * 2 + 0 ] += (sample * panLeft / 65535) & maskLeft; + output[ i * 2 + 1 ] += (sample * panRight / 65535) & maskRight; + break; + default: + break; + } + } + switch( mode ) { + case sm2AM: + case sm2FM: + case sm3AM: + case sm3FM: + return ( this + 1 ); + case sm3FMFM: + case sm3AMFM: + case sm3FMAM: + case sm3AMAM: + return( this + 2 ); + case sm2Percussion: + case sm3Percussion: + return( this + 3 ); + } + return 0; +} + +/* + Chip +*/ + +Chip::Chip() { + reg08 = 0; + reg04 = 0; + regBD = 0; + reg104 = 0; + opl3Active = 0; +} + +INLINE Bit32u Chip::ForwardNoise() { + noiseCounter += noiseAdd; + Bitu count = noiseCounter >> LFO_SH; + noiseCounter &= WAVE_MASK; + for ( ; count > 0; --count ) { + //Noise calculation from mame + noiseValue ^= ( 0x800302 ) & ( 0 - (noiseValue & 1 ) ); + noiseValue >>= 1; + } + return noiseValue; +} + +INLINE Bit32u Chip::ForwardLFO( Bit32u samples ) { + //Current vibrato value, runs 4x slower than tremolo + vibratoSign = ( VibratoTable[ vibratoIndex >> 2] ) >> 7; + vibratoShift = ( VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength; + tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength; + + //Check hom many samples there can be done before the value changes + Bit32u todo = LFO_MAX - lfoCounter; + Bit32u count = (todo + lfoAdd - 1) / lfoAdd; + if ( count > samples ) { + count = samples; + lfoCounter += count * lfoAdd; + } else { + lfoCounter += count * lfoAdd; + lfoCounter &= (LFO_MAX - 1); + //Maximum of 7 vibrato value * 4 + vibratoIndex = ( vibratoIndex + 1 ) & 31; + //Clip tremolo to the the table size + if ( tremoloIndex + 1 < TREMOLO_TABLE ) + ++tremoloIndex; + else + tremoloIndex = 0; + } + return count; +} + + +void Chip::WriteBD( Bit8u val ) { + Bit8u change = regBD ^ val; + if ( !change ) + return; + regBD = val; + //TODO could do this with shift and xor? + vibratoStrength = (val & 0x40) ? 0x00 : 0x01; + tremoloStrength = (val & 0x80) ? 0x00 : 0x02; + if ( val & 0x20 ) { + //Drum was just enabled, make sure channel 6 has the right synth + if ( change & 0x20 ) { + if ( opl3Active ) { + chan[6].synthHandler = &Channel::BlockTemplate< sm3Percussion >; + } else { + chan[6].synthHandler = &Channel::BlockTemplate< sm2Percussion >; + } + } + //Bass Drum + if ( val & 0x10 ) { + chan[6].op[0].KeyOn( 0x2 ); + chan[6].op[1].KeyOn( 0x2 ); + } else { + chan[6].op[0].KeyOff( 0x2 ); + chan[6].op[1].KeyOff( 0x2 ); + } + //Hi-Hat + if ( val & 0x1 ) { + chan[7].op[0].KeyOn( 0x2 ); + } else { + chan[7].op[0].KeyOff( 0x2 ); + } + //Snare + if ( val & 0x8 ) { + chan[7].op[1].KeyOn( 0x2 ); + } else { + chan[7].op[1].KeyOff( 0x2 ); + } + //Tom-Tom + if ( val & 0x4 ) { + chan[8].op[0].KeyOn( 0x2 ); + } else { + chan[8].op[0].KeyOff( 0x2 ); + } + //Top Cymbal + if ( val & 0x2 ) { + chan[8].op[1].KeyOn( 0x2 ); + } else { + chan[8].op[1].KeyOff( 0x2 ); + } + //Toggle keyoffs when we turn off the percussion + } else if ( change & 0x20 ) { + //Trigger a reset to setup the original synth handler + //This makes it call + chan[6].UpdateSynth( this ); + chan[6].op[0].KeyOff( 0x2 ); + chan[6].op[1].KeyOff( 0x2 ); + chan[7].op[0].KeyOff( 0x2 ); + chan[7].op[1].KeyOff( 0x2 ); + chan[8].op[0].KeyOff( 0x2 ); + chan[8].op[1].KeyOff( 0x2 ); + } +} + + +#define REGOP( _FUNC_ ) \ + index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \ + if ( OpOffsetTable[ index ] ) { \ + Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ] ); \ + regOp->_FUNC_( this, val ); \ + } + +#define REGCHAN( _FUNC_ ) \ + index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \ + if ( ChanOffsetTable[ index ] ) { \ + Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ] ); \ + regChan->_FUNC_( this, val ); \ + } + +//Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers +void Chip::UpdateSynths() { + for (int i = 0; i < 18; i++) { + chan[i].UpdateSynth(this); + } +} + + +void Chip::WriteReg( Bit32u reg, Bit8u val ) { + Bitu index; + switch ( (reg & 0xf0) >> 4 ) { + case 0x00 >> 4: + if ( reg == 0x01 ) { + waveFormMask = ( val & 0x20 ) ? 0x7 : 0x0; + } else if ( reg == 0x104 ) { + //Only detect changes in lowest 6 bits + if ( !((reg104 ^ val) & 0x3f) ) + return; + //Always keep the highest bit enabled, for checking > 0x80 + reg104 = 0x80 | ( val & 0x3f ); + //Switch synths when changing the 4op combinations + UpdateSynths(); + } else if ( reg == 0x105 ) { + //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register + if ( !((opl3Active ^ val) & 1 ) ) + return; + opl3Active = ( val & 1 ) ? 0xff : 0; + //Just tupdate the synths now that opl3 most have been enabled + //This isn't how the real card handles it but need to switch to stereo generating handlers + UpdateSynths(); + } else if ( reg == 0x08 ) { + reg08 = val; + } + case 0x10 >> 4: + break; + case 0x20 >> 4: + case 0x30 >> 4: + REGOP( Write20 ); + break; + case 0x40 >> 4: + case 0x50 >> 4: + REGOP( Write40 ); + break; + case 0x60 >> 4: + case 0x70 >> 4: + REGOP( Write60 ); + break; + case 0x80 >> 4: + case 0x90 >> 4: + REGOP( Write80 ); + break; + case 0xa0 >> 4: + REGCHAN( WriteA0 ); + break; + case 0xb0 >> 4: + if ( reg == 0xbd ) { + WriteBD( val ); + } else { + REGCHAN( WriteB0 ); + } + break; + case 0xc0 >> 4: + REGCHAN( WriteC0 ); + case 0xd0 >> 4: + break; + case 0xe0 >> 4: + case 0xf0 >> 4: + REGOP( WriteE0 ); + break; + } +} + + +Bit32u Chip::WriteAddr( Bit32u port, Bit8u val ) { + switch ( port & 3 ) { + case 0: + return val; + case 2: + if ( opl3Active || (val == 0x05) ) + return 0x100 | val; + else + return val; + } + return 0; +} + +void Chip::GenerateBlock2( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); + memset(output, 0, sizeof(Bit32s) * samples); +// int count = 0; + for( Channel* ch = chan; ch < chan + 9; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples; + } +} + +void Chip::GenerateBlock2_Mix( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); +// int count = 0; + for( Channel* ch = chan; ch < chan + 9; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples; + } +} + +void Chip::GenerateBlock3( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); + memset(output, 0, sizeof(Bit32s) * samples *2); +// int count = 0; + for( Channel* ch = chan; ch < chan + 18; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples * 2; + } +} + +void Chip::GenerateBlock3_Mix( Bitu total, Bit32s* output ) { + while ( total > 0 ) { + Bit32u samples = ForwardLFO( static_cast(total) ); +// int count = 0; + for( Channel* ch = chan; ch < chan + 18; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples * 2; + } +} + +struct CacheEntry { + Bit32u rate; + Bit32u freqMul[16]; + Bit32u linearRates[76]; + Bit32u attackRates[76]; +}; +struct Cache { + Cache() {} + ~Cache(); + Mutex mutex; + std::vector entries; +private: + Cache(const Cache &); + Cache &operator=(const Cache &); +}; + +static Cache cache; + +Cache::~Cache() +{ + for ( size_t i = 0, n = entries.size(); i < n; ++i ) + delete entries[i]; +} + +static const CacheEntry *CacheLookupRateDependent( Bit32u rate ) +{ + for ( size_t i = 0, n = cache.entries.size(); i < n; ++i ) { + const CacheEntry *entry = cache.entries[i]; + if (entry->rate == rate) + return entry; + } + return NULL; +} + +static const CacheEntry &ComputeRateDependent( Bit32u rate ) +{ + { + MutexHolder lock( cache.mutex ); + if (const CacheEntry *entry = CacheLookupRateDependent( rate )) + return *entry; + } + + double original = OPLRATE; + double scale = original / (double)rate; + + My_UPtr entry(new CacheEntry); + entry->rate = rate; + Bit32u *freqMul = entry->freqMul; + Bit32u *linearRates = entry->linearRates; + Bit32u *attackRates = entry->attackRates; + + //With higher octave this gets shifted up + //-1 since the freqCreateTable = *2 +#ifdef WAVE_PRECISION + double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10)); + for ( int i = 0; i < 16; i++ ) { + freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] ); + } +#else + Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10))); + for ( int i = 0; i < 16; i++ ) { + freqMul[i] = freqScale * FreqCreateTable[ i ]; + } +#endif + + //-3 since the real envelope takes 8 steps to reach the single value we supply + for ( Bit8u i = 0; i < 76; i++ ) { + Bit8u index, shift; + EnvelopeSelect( i, index, shift ); + linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 ))); + } + +// Bit32s attackDiffs[62]; + //Generate the best matching attack rate + for ( Bit8u i = 0; i < 62; i++ ) { + Bit8u index, shift; + EnvelopeSelect( i, index, shift ); + //Original amount of samples the attack would take + Bit32s original = (Bit32u)( (AttackSamplesTable[ index ] << shift) / scale); + + Bit32s guessAdd = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 ))); + Bit32s bestAdd = guessAdd; + Bit32u bestDiff = 1 << 30; + for( Bit32u passes = 0; passes < 16; passes ++ ) { + Bit32s volume = ENV_MAX; + Bit32s samples = 0; + Bit32u count = 0; + while ( volume > 0 && samples < original * 2 ) { + count += guessAdd; + Bit32s change = count >> RATE_SH; + count &= RATE_MASK; + if ( GCC_UNLIKELY(change) ) { // less than 1 % + volume += ( ~volume * change ) >> 3; + } + samples++; + + } + Bit32s diff = original - samples; + Bit32u lDiff = labs( diff ); + //Init last on first pass + if ( lDiff < bestDiff ) { + bestDiff = lDiff; + bestAdd = guessAdd; + //We hit an exactly matching sample count + if ( !bestDiff ) + break; + } + //Linear correction factor, not exactly perfect but seems to work + double correct = (original - diff) / (double)original; + guessAdd = (Bit32u)(guessAdd * correct); + //Below our target + if ( diff < 0 ) { + //Always add one here for rounding, an overshoot will get corrected by another pass decreasing + guessAdd++; + } + } + attackRates[i] = bestAdd; + //Keep track of the diffs for some debugging +// attackDiffs[i] = bestDiff; + } + for ( Bit8u i = 62; i < 76; i++ ) { + //This should provide instant volume maximizing + attackRates[i] = 8 << RATE_SH; + } + + MutexHolder lock( cache.mutex ); + if (const CacheEntry *e = CacheLookupRateDependent( rate )) + return *e; + + cache.entries.push_back(entry.get()); + return *entry.release(); +} + +void Chip::Setup( Bit32u rate ) { + double original = OPLRATE; +// double original = rate; + double scale = original / (double)rate; + + //Noise counter is run at the same precision as general waves + noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + noiseCounter = 0; + noiseValue = 1; //Make sure it triggers the noise xor the first time + //The low frequency oscillation counter + //Every time his overflows vibrato and tremoloindex are increased + lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + lfoCounter = 0; + vibratoIndex = 0; + tremoloIndex = 0; + + const CacheEntry &entry = ComputeRateDependent( rate ); + freqMul = entry.freqMul; + linearRates = entry.linearRates; + attackRates = entry.attackRates; + + //Setup the channels with the correct four op flags + //Channels are accessed through a table so they appear linear here + chan[ 0].fourMask = 0x00 | ( 1 << 0 ); + chan[ 1].fourMask = 0x80 | ( 1 << 0 ); + chan[ 2].fourMask = 0x00 | ( 1 << 1 ); + chan[ 3].fourMask = 0x80 | ( 1 << 1 ); + chan[ 4].fourMask = 0x00 | ( 1 << 2 ); + chan[ 5].fourMask = 0x80 | ( 1 << 2 ); + + chan[ 9].fourMask = 0x00 | ( 1 << 3 ); + chan[10].fourMask = 0x80 | ( 1 << 3 ); + chan[11].fourMask = 0x00 | ( 1 << 4 ); + chan[12].fourMask = 0x80 | ( 1 << 4 ); + chan[13].fourMask = 0x00 | ( 1 << 5 ); + chan[14].fourMask = 0x80 | ( 1 << 5 ); + + //mark the percussion channels + chan[ 6].fourMask = 0x40; + chan[ 7].fourMask = 0x40; + chan[ 8].fourMask = 0x40; + + //Clear Everything in opl3 mode + WriteReg( 0x105, 0x1 ); + for ( int i = 0; i < 512; i++ ) { + if ( i == 0x105 ) + continue; + WriteReg( i, 0xff ); + WriteReg( i, 0x0 ); + } + WriteReg( 0x105, 0x0 ); + //Clear everything in opl2 mode + for ( int i = 0; i < 255; i++ ) { + WriteReg( i, 0xff ); + WriteReg( i, 0x0 ); + } + + for ( int i = 0; i < 18; i++ ) { + chan[i].WritePan( 0x40 ); + } +} + +static volatile bool doneTables = false; +static Mutex mutexTables; + +void InitTables( void ) { + if ( doneTables ) + return; + MutexHolder lock( mutexTables ); + if ( doneTables ) + return; +#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) + //Exponential volume table, same as the real adlib + for ( int i = 0; i < 256; i++ ) { + //Save them in reverse + ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 ); + ExpTable[i] += 1024; //or remove the -1 oh well :) + //Preshift to the left once so the final volume can shift to the right + ExpTable[i] *= 2; + } +#endif +#if ( DBOPL_WAVE == WAVE_HANDLER ) + //Add 0.5 for the trunc rounding of the integer cast + //Do a PI sinetable instead of the original 0.5 PI + for ( int i = 0; i < 512; i++ ) { + SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + } +#endif +#if ( DBOPL_WAVE == WAVE_TABLEMUL ) + //Multiplication based tables + for ( int i = 0; i < 384; i++ ) { + int s = i * 8; + //TODO maybe keep some of the precision errors of the original table? + double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH )); + MulTable[i] = (Bit16u)(val); + } + + //Sine Wave Base + for ( int i = 0; i < 512; i++ ) { + WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084); + WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ]; + } + //Exponential wave + for ( int i = 0; i < 256; i++ ) { + WaveTable[ 0x700 + i ] = (Bit16s)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 ); + WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ]; + } +#endif +#if ( DBOPL_WAVE == WAVE_TABLELOG ) + //Sine Wave Base + for ( int i = 0; i < 512; i++ ) { + WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i]; + } + //Exponential wave + for ( int i = 0; i < 256; i++ ) { + WaveTable[ 0x700 + i ] = i * 8; + WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8; + } +#endif + + // | |//\\|____|WAV7|//__|/\ |____|/\/\| + // |\\//| | |WAV7| | \/| | | + // |06 |0126|27 |7 |3 |4 |4 5 |5 | + +#if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL )) + for ( int i = 0; i < 256; i++ ) { + //Fill silence gaps + WaveTable[ 0x400 + i ] = WaveTable[0]; + WaveTable[ 0x500 + i ] = WaveTable[0]; + WaveTable[ 0x900 + i ] = WaveTable[0]; + WaveTable[ 0xc00 + i ] = WaveTable[0]; + WaveTable[ 0xd00 + i ] = WaveTable[0]; + //Replicate sines in other pieces + WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ]; + //double speed sines + WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ]; + WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ]; + WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ]; + WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ]; + } +#endif + + //Create the ksl table + for ( int oct = 0; oct < 8; oct++ ) { + int base = oct * 8; + for ( int i = 0; i < 16; i++ ) { + int val = base - KslCreateTable[i]; + if ( val < 0 ) + val = 0; + //*4 for the final range to match attenuation range + KslTable[ oct * 16 + i ] = val * 4; + } + } + //Create the Tremolo table, just increase and decrease a triangle wave + for ( Bit8u i = 0; i < TREMOLO_TABLE / 2; i++ ) { + Bit8u val = i << ENV_EXTRA; + TremoloTable[i] = val; + TremoloTable[TREMOLO_TABLE - 1 - i] = val; + } + //Create a table with offsets of the channels from the start of the chip + DBOPL::Chip* chip = 0; + for ( Bitu i = 0; i < 32; i++ ) { + Bitu index = i & 0xf; + if ( index >= 9 ) { + ChanOffsetTable[i] = 0; + continue; + } + //Make sure the four op channels follow eachother + if ( index < 6 ) { + index = (index % 3) * 2 + ( index / 3 ); + } + //Add back the bits for highest ones + if ( i >= 16 ) + index += 9; + Bitu blah = reinterpret_cast( &(chip->chan[ index ]) ); + ChanOffsetTable[i] = static_cast(blah); + } + //Same for operators + for ( Bitu i = 0; i < 64; i++ ) { + if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) { + OpOffsetTable[i] = 0; + continue; + } + Bitu chNum = (i / 8) * 3 + (i % 8) % 3; + //Make sure we use 16 and up for the 2nd range to match the chanoffset gap + if ( chNum >= 12 ) + chNum += 16 - 12; + Bitu opNum = ( i % 8 ) / 3; + DBOPL::Channel* chan = 0; + Bitu blah = reinterpret_cast( &(chan->op[opNum]) ); + OpOffsetTable[i] = static_cast(ChanOffsetTable[ chNum ] + blah); + } +#if 0 + //Stupid checks if table's are correct + for ( Bitu i = 0; i < 18; i++ ) { + Bit32u find = (Bit16u)( &(chip->chan[ i ]) ); + for ( Bitu c = 0; c < 32; c++ ) { + if ( ChanOffsetTable[c] == find ) { + find = 0; + break; + } + } + if ( find ) { + find = find; + } + } + for ( Bitu i = 0; i < 36; i++ ) { + Bit32u find = (Bit16u)( &(chip->chan[ i / 2 ].op[i % 2]) ); + for ( Bitu c = 0; c < 64; c++ ) { + if ( OpOffsetTable[c] == find ) { + find = 0; + break; + } + } + if ( find ) { + find = find; + } + } +#endif + doneTables = true; +} + +Bit32u Handler::WriteAddr( Bit32u port, Bit8u val ) { + return chip.WriteAddr( port, val ); + +} +void Handler::WriteReg( Bit32u addr, Bit8u val ) { + chip.WriteReg( addr, val ); +} + +#define DB_MAX(x, y) ((x) > (y) ? (x) : (y)) +#define DB_MIN(x, y) ((x) < (y) ? (x) : (y)) + +#define DBOPL_CLAMP(V, MIN, MAX) DB_MAX(DB_MIN(V, (MAX)), (MIN)) + +void Handler::GenerateArr(Bit32s *out, Bitu *samples) +{ + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + if(!chip.opl3Active) + chip.GenerateBlock2(*samples, out); + else + chip.GenerateBlock3(*samples, out); +} + +void Handler::GenerateArr(Bit16s *out, Bitu *samples) +{ + Bit32s out32[1024]; + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + memset(out32, 0, sizeof(Bit32s) * 1024); + if(!chip.opl3Active) + chip.GenerateBlock2(*samples, out32); + else + chip.GenerateBlock3(*samples, out32); + Bitu sz = *samples * 2; + for(Bitu i = 0; i < sz; i++) + out[i] = static_cast(DBOPL_CLAMP(out32[i], INT16_MIN, INT16_MAX)); +} + +void Handler::GenerateArrMix(Bit32s *out, Bitu *samples) +{ + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + if(!chip.opl3Active) + chip.GenerateBlock2_Mix(*samples, out); + else + chip.GenerateBlock3_Mix(*samples, out); +} + +void Handler::GenerateArrMix(Bit16s *out, Bitu *samples) +{ + Bit32s out32[1024]; + if(GCC_UNLIKELY(*samples > 512)) + *samples = 512; + memset(out32, 0, sizeof(Bit32s) * 1024); + if(!chip.opl3Active) + chip.GenerateBlock2(*samples, out32); + else + chip.GenerateBlock3(*samples, out32); + Bitu sz = *samples * 2; + for(Bitu i = 0; i < sz; i++) + out[i] += static_cast(DBOPL_CLAMP(out32[i], INT16_MIN, INT16_MAX)); +} + +void Handler::Init( Bitu rate ) { + InitTables(); + chip.Setup( static_cast(rate) ); +} + +void Handler::WritePan( Bit32u reg, Bit8u val ) +{ + Bitu index; + index = ((reg >> 4) & 0x10) | (reg & 0xf); + if (ChanOffsetTable[index]) { + Channel* regChan = (Channel*)(((char *)&chip) + ChanOffsetTable[index]); + regChan->WritePan(val); + } +} + +} //Namespace DBOPL diff --git a/thirdparty/adlmidi/chips/dosbox/dbopl.h b/thirdparty/adlmidi/chips/dosbox/dbopl.h new file mode 100644 index 000000000..b113734e5 --- /dev/null +++ b/thirdparty/adlmidi/chips/dosbox/dbopl.h @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2002-2018 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#if defined(__GNUC__) && defined(__i386__) +#define DB_FASTCALL __attribute__((fastcall)) +#elif defined(_MSC_VER) +#define DB_FASTCALL __fastcall +#else +#define DB_FASTCALL +#endif + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume +#define WAVE_HANDLER 10 +//Use a logarithmic wavetable with an exponential table for volume +#define WAVE_TABLELOG 11 +//Use a linear wavetable with a multiply table for volume +#define WAVE_TABLEMUL 12 + +//Select the type of wave generator routine +#define DBOPL_WAVE WAVE_TABLEMUL + +namespace DBOPL { + +struct Chip; +struct Operator; +struct Channel; + +#if (DBOPL_WAVE == WAVE_HANDLER) +typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume ); +#endif + +typedef Bits ( DBOPL::Operator::*VolumeHandler) ( ); +typedef Channel* ( DBOPL::Channel::*SynthHandler) ( Chip* chip, Bit32u samples, Bit32s* output ); + +//Different synth modes that can generate blocks of data +typedef enum { + sm2AM, + sm2FM, + sm3AM, + sm3FM, + sm4Start, + sm3FMFM, + sm3AMFM, + sm3FMAM, + sm3AMAM, + sm6Start, + sm2Percussion, + sm3Percussion +} SynthMode; + +//Shifts for the values contained in chandata variable +enum { + SHIFT_KSLBASE = 16, + SHIFT_KEYCODE = 24 +}; + +struct Operator { +public: + //Masks for operator 20 values + enum { + MASK_KSR = 0x10, + MASK_SUSTAIN = 0x20, + MASK_VIBRATO = 0x40, + MASK_TREMOLO = 0x80 + }; + + typedef enum { + OFF, + RELEASE, + SUSTAIN, + DECAY, + ATTACK + } State; + + VolumeHandler volHandler; + +#if (DBOPL_WAVE == WAVE_HANDLER) + WaveHandler waveHandler; //Routine that generate a wave +#else + Bit16s* waveBase; + Bit32u waveMask; + Bit32u waveStart; +#endif + Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index + Bit32u waveAdd; //The base frequency without vibrato + Bit32u waveCurrent; //waveAdd + vibratao + + Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this + Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove? + Bit32u vibrato; //Scaled up vibrato strength + Bit32s sustainLevel; //When stopping at sustain level stop here + Bit32s totalLevel; //totalLevel is added to every generated volume + Bit32u currentLevel; //totalLevel + tremolo + Bit32s volume; //The currently active volume + + Bit32u attackAdd; //Timers for the different states of the envelope + Bit32u decayAdd; + Bit32u releaseAdd; + Bit32u rateIndex; //Current position of the evenlope + + Bit8u rateZero; //Bits for the different states of the envelope having no changes + Bit8u keyOn; //Bitmask of different values that can generate keyon + //Registers, also used to check for changes + Bit8u reg20, reg40, reg60, reg80, regE0; + //Active part of the envelope we're in + Bit8u state; + //0xff when tremolo is enabled + Bit8u tremoloMask; + //Strength of the vibrato + Bit8u vibStrength; + //Keep track of the calculated KSR so we can check for changes + Bit8u ksr; +private: + void SetState( Bit8u s ); + void UpdateAttack( const Chip* chip ); + void UpdateRelease( const Chip* chip ); + void UpdateDecay( const Chip* chip ); +public: + void UpdateAttenuation(); + void UpdateRates( const Chip* chip ); + void UpdateFrequency( ); + + void Write20( const Chip* chip, Bit8u val ); + void Write40( const Chip* chip, Bit8u val ); + void Write60( const Chip* chip, Bit8u val ); + void Write80( const Chip* chip, Bit8u val ); + void WriteE0( const Chip* chip, Bit8u val ); + + bool Silent() const; + void Prepare( const Chip* chip ); + + void KeyOn( Bit8u mask); + void KeyOff( Bit8u mask); + + template< State state> + Bits TemplateVolume( ); + + Bit32s RateForward( Bit32u add ); + Bitu ForwardWave(); + Bitu ForwardVolume(); + + Bits GetSample( Bits modulation ); + Bits GetWave( Bitu index, Bitu vol ); +public: + Operator(); +}; + +struct Channel { + Operator op[2]; + inline Operator* Op( Bitu index ) { + return &( ( this + (index >> 1) )->op[ index & 1 ]); + } + SynthHandler synthHandler; + Bit32u chanData; //Frequency/octave and derived values + Bit32s old[2]; //Old data for feedback + + Bit8u feedback; //Feedback shift + Bit8u regB0; //Register values to check for changes + Bit8u regC0; + //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel + Bit8u fourMask; + Bit8s maskLeft; //Sign extended values for both channel's panning + Bit8s maskRight; + + Bit16u panLeft; // Extended behavior, scale values for soft panning + Bit16u panRight; + + //Forward the channel data to the operators of the channel + void SetChanData( const Chip* chip, Bit32u data ); + //Change in the chandata, check for new values and if we have to forward to operators + void UpdateFrequency( const Chip* chip, Bit8u fourOp ); + void UpdateSynth(const Chip* chip); + void WriteA0( const Chip* chip, Bit8u val ); + void WriteB0( const Chip* chip, Bit8u val ); + void WriteC0( const Chip* chip, Bit8u val ); + + void WritePan( Bit8u val ); + + //call this for the first channel + template< bool opl3Mode > + void GeneratePercussion( Chip* chip, Bit32s* output ); + + //Generate blocks of data in specific modes + template + Channel* BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ); + Channel(); +}; + +struct Chip { + //This is used as the base counter for vibrato and tremolo + Bit32u lfoCounter; + Bit32u lfoAdd; + + + Bit32u noiseCounter; + Bit32u noiseAdd; + Bit32u noiseValue; + + //Frequency scales for the different multiplications + const Bit32u *freqMul/*[16]*/; + //Rates for decay and release for rate of this chip + const Bit32u *linearRates/*[76]*/; + //Best match attack rates for the rate of this chip + const Bit32u *attackRates/*[76]*/; + + //18 channels with 2 operators each + Channel chan[18]; + + Bit8u reg104; + Bit8u reg08; + Bit8u reg04; + Bit8u regBD; + Bit8u vibratoIndex; + Bit8u tremoloIndex; + Bit8s vibratoSign; + Bit8u vibratoShift; + Bit8u tremoloValue; + Bit8u vibratoStrength; + Bit8u tremoloStrength; + //Mask for allowed wave forms + Bit8u waveFormMask; + //0 or -1 when enabled + Bit8s opl3Active; + + //Return the maximum amount of samples before and LFO change + Bit32u ForwardLFO( Bit32u samples ); + Bit32u ForwardNoise(); + + void WriteBD( Bit8u val ); + void WriteReg(Bit32u reg, Bit8u val ); + + Bit32u WriteAddr( Bit32u port, Bit8u val ); + + void GenerateBlock2( Bitu samples, Bit32s* output ); + void GenerateBlock2_Mix( Bitu samples, Bit32s* output ); + void GenerateBlock3( Bitu samples, Bit32s* output ); + void GenerateBlock3_Mix( Bitu samples, Bit32s* output ); + + //Update the synth handlers in all channels + void UpdateSynths(); + void Generate( Bit32u samples ); + void Setup( Bit32u r ); + + Chip(); +}; + +struct Handler { + DBOPL::Chip chip; + void WritePan( Bit32u port, Bit8u val ); + Bit32u WriteAddr( Bit32u port, Bit8u val ); + void WriteReg( Bit32u addr, Bit8u val ); + void GenerateArr(Bit32s *out, Bitu *samples); + void GenerateArr(Bit16s *out, Bitu *samples); + void GenerateArrMix(Bit32s *out, Bitu *samples); + void GenerateArrMix(Bit16s *out, Bitu *samples); + void Init( Bitu rate ); +}; + +// Pre-Initialize internal tables +void InitTables(void); + +} //Namespace diff --git a/thirdparty/adlmidi/chips/dosbox_opl3.cpp b/thirdparty/adlmidi/chips/dosbox_opl3.cpp new file mode 100644 index 000000000..4cd20635e --- /dev/null +++ b/thirdparty/adlmidi/chips/dosbox_opl3.cpp @@ -0,0 +1,95 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "dosbox_opl3.h" +#include "dosbox/dbopl.h" +#include +#include +#include + +DosBoxOPL3::DosBoxOPL3() : + OPLChipBaseBufferedT(), + m_chip(new DBOPL::Handler) +{ + DosBoxOPL3::reset(); +} + +DosBoxOPL3::~DosBoxOPL3() +{ + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void DosBoxOPL3::globalPreInit() +{ + DBOPL::InitTables(); +} + +void DosBoxOPL3::setRate(uint32_t rate) +{ + OPLChipBaseBufferedT::setRate(rate); + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + chip_r->~Handler(); + new(chip_r) DBOPL::Handler; + chip_r->Init(effectiveRate()); +} + +void DosBoxOPL3::reset() +{ + OPLChipBaseBufferedT::reset(); + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + chip_r->~Handler(); + new(chip_r) DBOPL::Handler; + chip_r->Init(effectiveRate()); +} + +void DosBoxOPL3::writeReg(uint16_t addr, uint8_t data) +{ + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + chip_r->WriteReg(static_cast(addr), data); +} + +void DosBoxOPL3::writePan(uint16_t addr, uint8_t data) +{ + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + chip_r->WritePan(static_cast(addr), data); +} + +void DosBoxOPL3::nativeGenerateN(int16_t *output, size_t frames) +{ + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + Bitu frames_i = frames; + chip_r->GenerateArr(output, &frames_i); +} + +const char *DosBoxOPL3::emulatorName() +{ + return "DOSBox 0.74-r4111 OPL3"; +} + +bool DosBoxOPL3::hasFullPanning() +{ + return true; +} + +OPLChipBase::ChipType DosBoxOPL3::chipType() +{ + return CHIPTYPE_OPL3; +} diff --git a/thirdparty/adlmidi/chips/dosbox_opl3.h b/thirdparty/adlmidi/chips/dosbox_opl3.h new file mode 100644 index 000000000..9911edbf4 --- /dev/null +++ b/thirdparty/adlmidi/chips/dosbox_opl3.h @@ -0,0 +1,48 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DOSBOX_OPL3_H +#define DOSBOX_OPL3_H + +#include "opl_chip_base.h" + +class DosBoxOPL3 final : public OPLChipBaseBufferedT +{ + void *m_chip; +public: + DosBoxOPL3(); + ~DosBoxOPL3() override; + + static void globalPreInit(); + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerateN(int16_t *output, size_t frames) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // DOSBOX_OPL3_H diff --git a/thirdparty/adlmidi/chips/esfmu/LICENSE b/thirdparty/adlmidi/chips/esfmu/LICENSE new file mode 100644 index 000000000..8000a6faa --- /dev/null +++ b/thirdparty/adlmidi/chips/esfmu/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/thirdparty/adlmidi/chips/esfmu/README.md b/thirdparty/adlmidi/chips/esfmu/README.md new file mode 100644 index 000000000..1023f62c5 --- /dev/null +++ b/thirdparty/adlmidi/chips/esfmu/README.md @@ -0,0 +1,75 @@ +# ESFMu + +

+ +

+ +An emulator for the ESS "ESFM" enhanced OPL3 clone, based on Nuke.YKT's **Nuked OPL3** and reverse-engineering efforts from the community. + +## Acknowledgements + +I'd like to thank: + +- **Nuke.YKT** + - Developer of **Nuked OPL3**, which was the basis for **ESFMu**'s code and also a great learning resource on Yamaha FM synthesis for myself. + - Nuke.YKT also gives shoutouts on behalf of **Nuked OPL3** to: + >- MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + > - Feedback and Rhythm part calculation information. + >- forums.submarine.org.uk(carbon14, opl3): + > - Tremolo and phase generator calculation information. + >- OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + > - OPL2 ROMs. + >- siliconpr0n.org(John McMaster, digshadow): + > - YMF262 and VRC VII decaps and die shots. +- **rainwarrior** + - For performing the initial research on ESFM drivers and documenting ESS's patent on native mode operator organization. +- **jwt27** + - For kickstarting the ESFM research project and compiling rainwarrior's findings and more in an accessible document ("ESFM Demystified"). +- **pachuco/CatButts** + - For documenting ESS's patent on ESFM's feedback implementation, which was vital in getting **ESFMu**'s sound output to be accurate. +- **akumanatt** + - For helping out with code optimization. +- **shadex/shidzy13** + - For helping out with the nifty logo above, thanks! +- And everybody who helped out with real hardware testing + +## Usage + +To use **ESFMu**: + +- include the **esfm.h** header file into your source code +- include the **esfm.c** and **esfm_registers.c** files into your build and link process +- declare or allocate a variable of type `esfm_chip` somewhere in your code - this will hold the chip's state +- use the function interface defined in **esfm.h** to interact with the `esfm_chip` structure + +## Function interface + +If you're familiar with **Nuked OPL3**, you'll find many similarities in the function interface provided by **ESFMu**. There are a few things to point out, however: + +### Buffered writes + +Just like **Nuked OPL3**, **ESFMu** offers buffered register writes. However, it offers them in two flavors: "legacy" and fast. + +The fast buffered register writes (`ESFM_write_reg_buffered_fast`) are recommended, since they offer minimal latency which is close to the behavior you'd get with the actual ESS drivers on Windows. + +The "legacy" buffered register writes are only recommended for specific cases, such as programs seeking for a shortcut to emulate the write delays from some sound drivers. + +### Port-level access + +Unlike **Nuked OPL3**, **ESFMu** actually allows port-level access to the ESFM interface. This is relevant because the ESFM port interface is actually modal, meaning that its behavior changes depending on whether the chip is set to emulation (OPL3 compatibility) mode or native (ESFM) mode. + +Using port-level access allows for applications to not need to keep track of whether the chip is in native mode or not, nor to perform the port handling logic on their side. + +Applications that use the register-level access, on the other hand, need to take care to either stick to only one of the operating modes (either native or emulation), or handle the port mapping logic on their own side. + +### Register readback + +ESFM allows for register contents to be read back through its ports, and **ESFMu** implements this functionality, both via dedicated register read functions and via the port read interface. + +Note that in ESFM, register contents can only be read back when the chip is set to native (ESFM) mode, not when the chip is in emulation mode (i.e. OPL3 compatibility mode). + +## Licensing + +**ESFMu** is highly based on **Nuked OPL3**, which is licensed under the GNU Lesser General Public License version 2.1 or later. Therefore, **ESFMu** is licensed under the same license. + +If you'd like to obtain a grant to use **ESFMu** under different terms, you should get in contact with [Nuke.YKT](https://github.com/nukeykt) (author of **Nuked OPL3**) as well as with [Kagamiin~](https://github.com/Kagamiin) (yours truly). diff --git a/thirdparty/adlmidi/chips/esfmu/assets/logo.png b/thirdparty/adlmidi/chips/esfmu/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c47df3b705cb8a659ffc156c0617eaa10741b788 GIT binary patch literal 94294 zcmeAS@N?(olHy`uVBq!ia0y~yV4cgrz@*Q?#=yWJ|EBaX1A|CUYJ_K+r>7PJ2LlN3 zF)%Q2GW=s;VPIrn0AmJ52phywh=DOELf4NWZ*Q!{f5ODks=S2uSLPp{yR z(6I1`$f)F$)U@=B%&g*)(z5c3%Btp;*0%PJ&aO$5r%atTea6gLixw|gx@`H1m8&*w z-m-Pu_8mKS9XfpE=&|D`PM*4S`O4L6*Kgds_3+W-Cr_U}fAR9w$4{TXeEs(Q$Io9N ze}Vi7_7)>3Eval)|vI#i`vL_Y_D;YI%h&WAL zxbYyTvT@J{(WIh_Tw*FF4^=;cyax6eaUN?T%V%(pA^dfVfrpuqfk}{Akinke=f5tE z1x!TaB4v+hfPB;Pq!JnrvZePhnWg1~*tTQ0_m_wLbEZ~o(LH|5#k?oo{$yA2j~X$S zgX`-A0|Sjkho5ab@lff9Ng|IzAa{?Bk>clv*Zn(L?jXo*xAOdI}iS6I5g{n z`d^02|Nd?NaeZD*%8&Ag^Lc(~mh#H`ihXB#D6pf7t}wQZT`2`{m+Yx z6(_f+KmRGO6`uXzeCt2OkBd2T6@J_HSFdan{~+rd_rvqUe};d;`GS)AqWd?k`S5w- zpWK`FQon9pyT&aY@pf&J$HLySMG6O)8Y5N9D*ko;w$2diSi3qt=HBT$?!UL5e;w;% z&sM|DXaC4+U2UA3{k7e<+NyVFC(eAQ)KPWfVf|b&$Fu5-ELDH_KPZ)uligw;de-`% zXn5m)h8HoL{xj^;UTF1g0{@?uxAyNp{b%T9j=RMF=LnReePmtK+?k)gAN$Y1`?5~z zWA560lT&4v?wNM*l>3D_U)LM1Px!kqFJafmviYieE}oj7dhEpwgLAd6MfSWllKQ+0+~r@gZe(}o>iJsxc8a)G62-8Z-W6!+eI z$UZ|lUpunT$uiT3e}RVmp*>Oin*J`zEtK7Q>>l5_+E0i7Xg|NDd?oa2VV(D%rY?$N^U`~1|0MQ_iuz86>jvSR;+I>rAC7uK@>W99nA)-^v> zIDh@McQqRy`3wALK4e}PogF0-;BA!epl3XBPF47d8uoow%iF$If9rAk{ED@!c}`7t zjQU%Z1r@X1Y|k#Pe-T>zfW0%}^4h#FuRBGPCgn`3Z|#t32Wx)n^~=dKu?E z_qZOXDw)=@X2zd+d(I!dXV)HcQT4J@{Lh|O{!OKx|r_uMgI;(2w3 zy3iL>TOVu@{Gsj|VEC_H{$G=J*_LRT;vX{)R`mZ9x_x_P*tJU)Z$&0bIlbcU4=TQY zX-D;1=3~!-55d zKSR{UKknw;e>9i%I){JwBN#2txg+NJJgh3_ z=lLU2fBtFg*>-e6?}Z!}H=T&dU)HbuCsv_n&-%knWoG!>>&oYL*84ktVcHY_z`u!a z=Of{DaqhFVnLn|~*v#Fp^?klGJeF`;{l` z|5#$aUua8&=#OcSPc;_*Q4l)&<3B^xdz+-+HkviHq3+KP?Th?yY^U1FkJTRg(*EAq z!zu8xsWZ{=P7?Uw!@e>96?exqNRSyOrJea>}B^&|dj zZhf&@E~OT!nFfx3R_|P8{!4H6DxUa=gZ8@@oPU0;Q`2_Fi*r-zJEiAL|D!YIKf{9T z`uq3)ZI%D9DfnOG+W!po^}nvBeSTQpn$OI?@ho5c^WMYjzpmN*A^gyhk7lcE_uWiW)dUJ3nVmi=$t6&e0BSgrogz{P#w{^$4A z{}~!}gWNwT9~3lu@%DY--mtd^S3a3^bN1h)Df>G`?w_q|J^v%p|38Dn*8dFq|1G!jPf6Zqpr$)SbB8N z1P`N0nr;l=l-ahV%f!w~^KY?#;(Kxb>vQ^lS;8(p5DtipJiPqI!SuYPiH$57oaY!M zM794O`xO+;=l3b+;p|6Nt405vwUMb``=8-;Q2n8k@xR#S|Ni}-;oACXrgy^w`;S;n zQvG{r(OoZ5mop5~=Vi0nzwKN8pTSXj|L=eIA4lG*J$F2qXYNjW;n(>;BDMcBI9!Ir z?eQA#>uMj@w+Xv#*<^U$^ok?j-)(=;o z{@L`SJ7iYnqMo2gRRDv-%TsqGjECgoVWL3EZg~m^Mtl-h~2n!PxP|M zS2s>m+r$0neb1A9^Sm=|yx%Ettp4f|EB=-L8Kl11ckWZ__q-T&=j0X{G8GJA7MQ6~rk~#mb>{oU^`01ye(ZwbI-abkH&mbrI z?au|BN49?j4?n(BvcBa@CXdzAmgZG=6#p|EZ>_y@-SBWyUP_MJ#`no@d#--9-1;=^ z{Xh4~f3iL*Y`A=nW9i|;a%}d`7k~NV@N8efh15?r?#q1tp58J0Q$hCTADbUBug;$5 z_GZGf%y9dX`Mc~{|CJt$^ibTleaak`sS)!RuIyX6@7dQ|J&XN+*4r(=JH1VHOY}-M z*B!?ED}wCT)^vX~kC`5H-R{)c`8qRG?KYX)FZfsb)ox+o^9nKVR{PgF&2?G!$M)%8 z&-1;vx8Ay5^V>^#^=ki<^%KJVAN*$!JN@wX zhDpY=-4pySe|EcnFY1}bvP)TKW|gY?#ay)8`r-MJ6Yp}Bve(2qoie-Q+yBMMTBZ8nr~V`7zc=fge_cN^O=|A9nFiC&J@bgZ7~T9;mbYZveZj32U7ZWJ&2^7i z8D`&M@mEXNbldW9GpS{p{Pa&Jze%~GvqaK+hxAJY`)f;{xi(G_@Cjv zQ~wsn{|vT^|1<1st(p0sf!pf-zs61f8SY>FCxX@DpA=fG2(x(BcY!zg_uVi5KC~}D zUU+Tcx%n1$=QQkv*BTz1Z)x{Vv-tc^=NpOt8J@p?P;l!%!=Ij6%m3D%{?D***147M z^XhKa7ng3&pR4V;-SU{H#osKhi#bJ}_k0(fo16XZZP2Cj7N4JaY<{`3;osJ8@0yq1 z`VoET+qKNCid)1Kisl*d-dXItcTWt2hFeJR_&LN%T>%>H|y`I6>R#(8vfU9 zwNh?fRn9W(xvsM`$-aA^R;}OPwV@|6XP=$u{$atx@L&H#pH9sV+&4S;irStz{YGJT z3NKW1t@hJ8G3%@Cl6Tu)zVJ_Zr2V^6;$c{5QZ0k&*ZtjpJVoa>=^U8Cza?5{(u68E z5#gWn?5%#X?Xo^E5f@jH#w)SxqEt&}`J`BO*W-D2U;3JFjHo=M_xa_juZCZ(pI`ZE z-F=fyDqXkV@kU)}P?y#j>D;^X(<<^F|4x5sI%f@E>g8X2`^)w@TvN<2U43Il!kTx6 zKW2T~-+Es}-uJ_iA%AFLm4*?dpG%e1;^f=m5HrHKWR`}a2A+N=DizEk0H z$@VS!4c*WEzsQ%?E&0#jb>-@x++{DX9pg1l`P6zSN21sM_zUf4>pSM@<)(dn^JxA# z-{0?4&z0|A@J;^tQnjCZuf$qddEYu4bzASvm-F30>lS{P?7sKh`#Z;+<-bn|Klk2>uE~0D7vJcSdcI=GrRwQilbZL$_Wt!h7Iki=u(Qf*5g&t-TyI&b z=j~sc5a`OOy1{SBviItRk0M#r1eH5F?*tbfwA-;P(A9|e(G=_>FZ};+GD!bUF=Sw1 zY|eCc4)An#RwyXSPs_|nWnie7Q#;Ytf6fYxgLr79CZyA))tz(JUah-bLl48SiC#FySjgO zdBcS@MrU7nGR(@GJu@We%+v6r`@%US961D!q@+m}Pny#9{>Lu&hV|uz^PbkfJ8!r` zOoxrd{9rsd|kBvzQyzyv5K@CXVRW^9zK$^ zvU{ptT57QJJhi@6v;2NKolBnjSuA~RRQlWMPvUJ{zfKul&+Jodb=7ff;$STe325!L zSZk#zX0&Dgmyi9s>N;b%c4{R&iE!YRO1aMekA45{tzE&B>f(Bc7V`9opQu%k>VN~*O%Q>}9WQnFo_krJ% zwZU={`&iko?YVdBw?!?(rYoVp*eonqj(Jbn`|k6*fA8-7v%UQ8J$r9u(Z-ZHk)V-b zmUKs7M+SzC{oH>NSwUmO1s;*b3=G`DAk4@xYmNj1g93x6i(^Q|tv7cgC&-4^J^%jx zr0x5C*Y_>vYOU2~U=eRnnC)GhxoP&=tGee^PFn{07?_D2G zyHr#MROGn+NZix1_QDN5->pw1R?dFq`l|Qr(%CseDgg{xJ72a>7B3B4amZ~!!Sws; zkK6=TFue{-dwTN!&*%UD@OO-Y(GVE?A#h@OS_y{>6tG?Q+MLPIKKHZB+NDpQPV1Z0 zafe%Vt}9gb*A1`DFBv5jrtdRy;9+@roJW8u?pM*}e!dNiF|2lM_ZaI~_UzxXXU{ri zZKd6Tmm;Csmhi7Nnavo$p4RA^AaB4Jvhn0|fsRYl+s_Jb)t+~IJ?{gn51b!_AG~kK zXR7D?bM?}vPsgn;DQ%iN>6{W2)T-o84`k_MTF>yjQFGD~k4Y>IvnQyv_H5KY&UpOb z43p3)Dic&xX3thx8}>Wf-0+#!2gVP%56(CI7yWU1>C>m#R-cqEEnBh~8W|q*BQnio z7clPOso}KnoFvloURr(n;j>Q*Hb>6UIK=5JbN@Ie(;S5ZKkSZ`8uld%UtBA;_G_ux z`q<>Zi~A$0zsH*0tDk>uYc|7ciO+T$nEn`k*q?AaPsNH`X%WH>c@N=p$_MnB;~Ag% zO8lzZ>t|x;XJX|QvihMoS4CmZWVM;H*Y{f9?D3UQ^>n(FwQ=XRW5-@;Wy{wd**gDE zi?;nA#qj-qbIt7j2;}U4p3nGS_QQRq{Ore$f=JN9@M&tLuYxcu^uM}$Sv(mA*^{-Vrc@*5+9bkzoJ+&dcVSU1xrug;0uX2m8khAxT z(7I`NmcO$wc2UvHR}+=5oST1KUh0!Un$!=~2gMG7%iU89Rw7a-`&yIPdLJY{m_BHk ze7=3^=jSWu+IU9j*j;J)%zNxXi=yMg18N0t@0QPfa%(G70PDYHm9MVcJ{|x6*Rpfd zP9y{_g(s{2moFawDL%mYxH9nHi$E>+{&%s5EK_?FHAGg(*m`|C!003L^FZK((ghRu zg$qh5cv*Er6XX{;{tC7VsbsEy9}J!ft9N9Z_U!)7eY^Lu!1dQt`el_fOc;s|Y(Ai= zaOtIxqz;n430sOPxNe`Wyv5QOZBf{C=Z8g}fX$}wPk-3$T&cN{%8pgB+@rGUJ zW}QetN;unp+|YV-Sz>bMvSk{2dXJuePLRu0?^c+8m8%@3#)2x+|7(#gYz zC^M>#cx`^kEZ+FoLB!X8^8DF{Pp9m9X8Y@>&CHdWcbpg}E#Z)1K4n#nYL8+2U5A1j zEelV|+xf(-e{9oqmbv^f8{^D}iF=A9bF?S0E|~ajk=R7W1&I8{@3DH$d`3UULKAE6 zx-v2SzTK~CrH(7E3HhKcS|HOPn7|PbpFWXsAwoy~4fZ+eYm4-@te&&?-RY-QGkNM( zwtTE@kTKvoV%4hD?C3uOn#P_i^ti2gJ7pUGUv}uoImf~sYkjQ4_`C6s*XEz<-ltE$f4SnG#WuZdrz$&DPP!Ny zKS@jbu=(b?+VjUsIkiRi6wTZ!|L+1TA5*vX*=O6QO`E>{^_s3P=I}^d!yk`B++X*X-4+*X;ejZ}Ycrw|8E9^-5^l z)3)uGa{l>)9PmPe*6hLvzh=N|h9dt<{|>3<7?p6|{`OGC)5vd9$_JarqTN57om-rpTeeC$E^Ug}c#v*n z;n?tamx|}1XJ22hsr@22|Igd|MKAULDaF-4pF8=+k&em zNwa6YdZqQ;%znDEx#`iTZp~Y_-|H%zlirZO)?~K4j;{Xx)!Lp*>JbrIJ|&M!fw%^9B$^Yy{t znQOTG7@m7fax$>;Dyx#RpZ|WQxx5G$%T=u{Q3>XKHXMJ1K0Kb9_St{>wCU!hQ#-%N zBT|Lf?MY|VH~y`d&$O(uhm+~?)YB}B7pJ7Boj89+z+B3F)vWiS)*og1dN%wzo9{Hq zq`^~xX&v7S%M6Bo=Fb*=ljqJ?x3=n%X0iQRob$VuqjB?r*9ZC=-oMHcerXRc15fZ~ zZo0|7>PFn5^+}V@PL>e14ouloG&3Ws>(3#kohvmL6-kL^ z)*@Lok#TyMd3uBMf$Rg_2W*a|%+)B`6V?0XkIm6$_LF`6!cU4U=6ydf>1^(?(vP1j z_c%3vEx-5CAdN$V|3&7^r+Y=2@ znP#;)NuR?TrZ*Nou566foBigI(o$vj6TUtIPd zJxATM?XFYfn=?FtYp+cXJvSlD>iwU4#Rmct{vL;?4;&X2Rptww(^TjA_~~bvcbiT?T&CGvMk{^60;UKh&p!oO zU3bp##L0Yra(;8+Q?6|LpPX;*Bpqf{Yp885>2qak)Pl8t*+4~h;_sLL%svRMi{?9a z%;)75%~S1sg4bW~Nc`cUl`VVz@aYtTdG4EbN-~Eku&m=d@lujmPTkXOZcN{sGdwfh zlDF?Wad-VyrJFl)cRw>S=&e2Qp>v(z#DDO5>;!{nwS_!;9ruqloJ~c~4loAWmiHZc zqvNPozFTe&|J=uwJ7)5zb$P6d;cT?p#(VfGqop}}!)6svnV!B$v*#T z*O_(CKJ7S(<;FK|W#*!;R|g^!jNHrVW47-}4oUy!?MV@w>{`Z{Dn$1zs${ivLjqBCQL!PL#^n&-|vYE8Vp$N#ZK z%Tl44FEhjT|MYcUZOZyb%1rV1{q-iZ|6jlK>C;v_8&}r^PK$=uvkGeF+&RLMC-CJD ze_GGMH~$}M*C!`ED%&&vo64KIQ@mEIXZy;}+jmau`L)=(TJ9Zudzk<9YoC2) z9}jB5tZP~4>r@iQt!KWyC8%2CW$E0#v58?^Q-*JN}PQC^@0A&J6`P% zdbIQP`f&N(g%9*6tNh;MbyRbnjc4Znx;+-}c71>S{PPqs@yNgL$|Gw(pJhmXc4?zB zdxiGdXaDcDcRnx7nOb~Qd-}<9X9T95w*BucnVjSQ{PyVta}kk=b_nPC*66wpMRtOMf>^x&S`wyQ+_?&I#FQx_2k{N7P6RFtvh+{ zwc+iFEf;LPe+61U{A2dv|2mV|{|{gK^r_lP=MwA3DVGKPkIHTgS*AR1)w1OBwaGU6 z%N|!--@MbexXHGE;kM)MFHirlQ{yn0x4pRk%DIS>g$DE5r=NYq!nh=Ue{_E6V+)Dv zM}OG)2|W1mPx<}fSvh%=wnVASp1oyV+|sT4{4QnP{bb6JlXod9I{EP_(Mi3$H}0rJ z#;Zq0Z(bjE`q(Ycr1vj9g_wR@Khjd2xBK_5d8~De7RifFoR_n5`FTRH$!VeXbB?~} zg62|Bo(;d7`>aP^n=`L$b6;Yg!KA-e%3x;6r%A{2EDV@@mda<}a4C9zWcgf|J}Wib zx1Hygudd4UPmnq8=W|Qm_IzDQto6!Ff2&LDGyiX&>{lamfBXMyrG-rl=3NDkCCs}D znjS2Zizv6QH0&#WeE;5$dA9G49a(vGQL4KA?z-6J*M3<^hp#Jm`*!=zKhI|8)cr_& z&f?kmoV~i&>c*2g-srW46Zfe#Ui|VaM_r}p<#o55(C@ofOp0VXZ&hHlw{1$rw7nfm z@02v%&$A3XYR4dxrrmkxUxY{Ux^K*XHz?2h^sf5OJ3_-NDf{ANnNb=s%e zIU1V``{tJ&@JLoy_x&PueXrnWiR*hme(!t#*iA4_t@&}KMdq5a-4N`sJ}jM ziEQVTZ`X7F|L}OM{`-L3q$kgAWZriE{q1_`vFMY>*w*&)nuUdDu3dk>A~$B|p{?KV zJ^p0A@8B{2`dgCWf2U_9UYnYAVD$lSiOK!Tmam*+~_@@BWy{GqbaE_PT8) zd#ui1<2q*lesb{@hI5lu7W?VB+nBoEdHVc$o~5J1jf^{*FJ%(tGj`p%_08R-YQm&* zm)1^JT%K$+#XH}QH|Nl*y~Q6j%sQ{G>w5DhzUF&*TJoDCDswkX@XG`Fxj^EP%fz`G za=ck~2bMg3`r}`3me^y9vZhP(GM3cu(@gNl-=X&R-?GI*H%t5uZ1YWdm#deaHYcFMO+_&r0(q{I`@BaT^& zZbV2NkTxhD?xEDwxkSP9#mJjQnNm(|*5}ql|guH|GPIIg@tE<{!P|ue4M4@)FM^Vb>iqdGgGvi)BQQn`^a| zcQ3s4F(_T>*RlE5$xF_$6#V)-_gwqyx!=3beP2<1e0kq~tI5}2uU;KfByga>sV9f>&cc6bOe5GmHN5U@{i9l{W|vE*N>ilF4MVJ`|sJ(@7nyTb6tNm zO*;2!g}U?Gy5p%iN;_i(Z~AF%*UsmXoxHGYHh2E@PJb&w!@w5d;}(e>vrj%qF{+r! zlcxtN7*aQHd!#!1D6jJ!gFS&?ldOb}nyZy3#WJ3d};JIVA?JyZ1IucxPDDqri`{kUxZ_4@tF<#Q|IVl&PD zXZLy+P5&5jBJA{rb64YoGi^-o2zsa9ymjEloo{xx<2GIubl*9%r$r}-?WP?+U zqC1}`+*oB(c6aTLHO4d7NUTbKyuR#Bsq(s6^PexS{r%&~{eRJy?jPiwQu1z@;UV@u zhIlnkzMV%GfksF!9c;Fpd?9CjSjO7>Tih#lUp=<4|U1)jyIK|EF zSJKi~y~5MhN$vE#v~zD%5aWK2`2m+AzZpHqwBI@BNSO1s$)SsHq=Z(#OtW6Or}}nx zS<=nV1)nMpEmZuIHFZPS=FZh+MLUxpCC7*_k2T%7H_NSaP2;0NV?7@&cbxa8 z?k(HP(kiwJCX2S**@5Y+weq4*yhvzbSRbV-C9Aq3jPq1#QxK>9{Nvq^*S5Ia3cj2f z{zscrky$NsYe#!q#@e+XC)a;nUo$C1s)Fao^aHc{Cgey@zH5>;%O`o|;RB92Njpzn zJG{Tl=II+L@1^`N{(77we1a=+Fa&5xur$BQ^TeFE#GcSl-kZdOWKv? zYS!=mz+b;L?;6Ib6ae;B?xrvCfgxAVC+@>{mO zc(|l^*Qs)*9S)O3_5^kQ*;l*mdYOEtc>ddKa)P-(v@VrTDVf%8sd#EBGqTTVXBoqAHq+ReDHdG}W5?Zvn6 zA3ivDLCWRq?u-Bj#~clfUCLC$S0Q>_vsG9)WZJZ< zbxqfH=;evL{i10u%ibP7>umm)53d&J6fjvdJhwdf!C>b}&zSxD^;D04?{st9@#|Xj z?oey(mpYfyr<6=%x70kv#C_w*m6atkF|4h#MXyw5?;7P>5{%k z&Y@MClXe!*+&d|}=>EjlzWc4YvbL>Dmfy|wQ1ZdeFZ+*Q%D#O^3Hs6IRN`@;w!=6>e=3_icM&I-0G zZaefQ=e(-1?DeCkpO$5veUK1d-m_$5)_S$M+BbRyU+3w$mz7Q15c6(#d)-^#clqB{ zHD2mm3ZGIk?Kf`{Pub5q9NYS@WhQXWE?JkndQSe#lM?rj>xyu=yLTQt*5U5XdHSjJ zWX0p(^L6fq1VyHtU-#Um?D8b@2JRbAYj@6Gch^8gLRNKqz5ULal8@XD$89fMbENeI z^PC>ZdICa^KpZ=aviV5vy{zo?_LC+V&{L9zS-7XP2#%hijXodE9q@v z*R32@v!4%b(7k5G$P%~GB`=LF|M{tR^ZJYLSnP|9PY0F6J(JEAZ8tw$HmQ;+`tOa% zO5>i|Lp7h3i?1I&`Wz>wK1TZQH2}rP`kY)RMjU_|E)@KB6|Wf7#>rfp1Pt(z+csJ%Pth z&Dp>v=kdymfsY<09x7wq{76%9XX!mNwi?cgTR-a5Hg9mq&Fowe_4ee(MX`#vKPGGo z-YuZTU{>{E%F~T&->CUY%*se=+Hvv4X~VmPv4`B4KR=tjd+HKc618U4iOjKowtT}> z^Y#r>&umwl*?aJa;mlUnO~%G4$ByNN814>idHp-8O8xFB`30|U9kjFfrSySogU9hU z`(xib#lgi8b=~vNzb})!9rYz|wusNNZF zVVf$m&5zd1`?E25O253ixnberMUgHpc2{>q9}Y9LpL{H&_(scxwW|+%sXm%1x#{xd z6m_+ZEYsf);`raXK6!V1uFeOU584vOFGE-RJ-uVuRb0Gd`}?K0zZUL}*GZB|U!Ie) z+cmIk>CHbYj++VhZ1}CHD|NlJ*{%7_k(Rmc-(UXGmXSKNL+|9TUw-kI4%t~`yv!1N z$?s+5ZGCah;ctR9R&zzpCf`oW`6_6_rryVS{CKCDTHd-np61IhUh< z{e|VsZoaW-5f&ElpSNVk4v(1iw^MKJ>T`9mJ3Ax#$Tr2>win-6R6I4h_{C)ANl&BX z60r;%|?=Vp-~|@J8o6z2{TU&0AXY!yu0TTj9NPJKp8lH#g@7PF&t@ zCj3U@+b)g}Uy+$__SL#PExLc~(XqmurJvvN2Spzc1&=*=S@D-8JW8HkR>xK$czf~B zX)c>*pBFxVesX-ohNG*v#&Pg_%RMH*4Zi7UKt4fEJ+TicDyx@)fNkX@$+JTLU~2(ZguMD`8NGXN+s&O!Hx6#=(!LnIw_)#>eT6c~OzT6G z?9JPy+iHJT-&$8v*3@uqsrxjjQoBjM5$LZAA%SuY~ z?UwCoSes=x_n&3?xzVhS&k~@NxV9^x5H+|#uJ{;i}$nHq}%Av zkBnJT^-X7{+hYA&pCyhT)E7&9uGIO>&PB9J`=v}`wWYtree;V?R0{Xr`~ICxor5FG z)J#8CDRXO?)$~yzzMA!90&&t`9yRE7W>;MP%!7d3Afcd*RwQWzOHu<;hi_D}CeNIceL1 zdvi?u>X}Yh7v}t`WIj0CZ`xylX{TdKWo}r$e6uOHI9Bjwte4gIdV!$BVJ7?eZ1{RE zZM<^%huy&^DocgcHO`zDzMkb}(*5Px*Ese%(cNo`_Sx>M?q!?0eBPp}KNf*oSEX$3 zIPv#*i0cE*oTBHPIjIlBd_>z^jEzP5WjC+wUKRTFr1->kUG6WAdzR~{xZ^K8MrcTaJ?EI7NZ|^_z!NAdO{k(Nb zd#m@(XRVXJ?Y)KP!8ETa^DCN8eSDnK*7j?c{hW>y&N9io+oayIKHz>?5uNYf(feA+ z$1ljDsO8TcnVAn0|Kx-jPVIPe^oK}tcyx_uL0UAa zKlXc?ndko!Df3^%W;@}&$9rwdH_m5veXLdAy}=>Mx$5Ip5HkiKL zm~F@W$LK@#%dhVwud9}SQu(SrU&Y?ERs6WsW+kQXcKfVu{5!Kgaq?OB%YLT{WY2}aPke1l9zpM{TntZb5=;rj5p!r#yZ|R>dNzA!>JW~F*gF!^5 z+1X2VO@}P@g%&skeFYA?s2I|jo7u?D1 zW%b?vLLt-b=nt$p`3GZuKYMfaHK?E~c_DD~V&EPhH*Z_lORtmLpH}WIy6eweFaLw} z(!H~Biyi;o1iK(WAg7aDbX%QuFx zT$z=he|)8YMZd3w{mT@inU8iBUpY5Vb=B2h-(n<^bBt=AA6e6#yZ$2gRWZhGZ=Y@{ zj5VJ%Pwm}gUitTB|3s&pwy+jIW^rT5=4lySUpQ6gR@*3&T1g>U%d%3dXjmVuJwk3D1Z^SM7ud(-CvP}D*mBBo*dpz=LV%D0t{A%v6l6v>_ z#w|aydd}H7^A^wDW5Bll;)0rQI!m3~Pw?>a`nb+IQF|;@Qnsz#EUfZ|nf-p&n1V+Z z;TF}?ekJ!$l$AU7@mEDvKF`Gq0`q^^&3^rQdySYzVs%XU+gC@oY<08kecG+Y;Zs*P zPhr0N3icVtK6kXYNB;iU@0n*e{jIN}b5()ps*XEep3m>`m@&OM#=M`)hTUiD`K2Fi z4&98@NaF=6qJ6Qh&DHrsAE{q4fm|HTLFS@@XxCZ7$e{#W@b{hYuwuT?vQ zUq0PgZ2CV5 zy|VpFn-nC;5O63;>?`)l3`18P5y?y_TZ=_Dy-|Ra<;M^I5KaaYne0v}M zK5gPY$vJsXW+q<||C-phl1+lKj`PP)lYLzWw{doei)-rZ2Hx22&;P;o-IC4M^Cs;$ zkRcVH`|5#BfaM#tkMj=}dR=_8X5}2aT;X%gMs8c9bKZQA%vrR_quhQ&Z-aVc;^SR; zWr>X|f(}Y<`DT(CpRPW6W9{7S=Q4lWY}3Bxy5zX#%RAP>iwoaOIzRt-m_-|d8qdl3 z7LH%vL>4`M{7P4&XgcF+={AY`>O1S^mK04{>^=FM_SrPXAI=Lnc^+DboST#Q<7WEF zzFywA1zMNZJ5PR|xJ_%vr4 zRy*8eVZ8c$Uh<2R>Yb-jzlw;yRL&E4kg_>v-+wy?))|cZ7At=~aP0E8XVLrlY@By- zYWF#tSbM+yQ`zuF#pOPBn(d98-X3E0V&@kMUA-ztZ0CK?RJG>WXSYp0 zS7_CCviafi&xg_tmObe239u{c*$^YQqjG_?>x{XRx3EPkMlY-`mSKG$6JWmXb;|SS zoLsGUZ?%5eU((w0_;*g8>RjG$4;^{8N(jH~|KZTSAY0vSKVyyHZkFQ-JpXP;d|f_& zS;+&2z@2NfWG4CxKd+F>`*!SKqvA`gCOs+cTb$418H(~Yo;V?$A7EP*@^`lDuhJMU zCL?F(n44ZkKI=?opS$#_$lLw-zT=mSwpo2JZ%lq!(I2f5Q1?o6mAd~V$p>D>6WJxh zEd#Gu9_J|uk-KrHKyLS^(o-!9qiqVC?kH@%y5s5e-0Lp8`+co`$5zRSHyn02raSe7 zpWh?5iwD+4nH<>KZ&8pvCob=@C>yUXhlTKY+ zeB|pcH8&-Gr3;03&z)ttnAvfo66*G(4V{^ z;s~d6)b;#ZVd@9oZC@`jEk8>8e_XW(D zE&W1N%1`1E3!~?xHzma-8{V24#s0X)&&1wvBj4miPv5EqJRgcJ=NP0Emu~&+xZ$AM zTNAU{cM&fcirjrui?oaqxCQ*LKB zm|fgC-Oz;lkJdNEe@YJ?TBP*LrM`IN`swoJis-*-t}eT3eYhLXGl(|^8QA&#d=a?F zEA@f-#QkUg+(^)#Ypi{CS!7h4->xSyY&X~!J#LUbe=h6sttr#bKR#ku^W)(+|EU}J z`)m_@m76^HXQ?ne7&xz ztE2Hy$0%cRW+>Oa^AcA4f9%TQd!%mkGG(8)_y2l?Q#I3NiMT979CO-Si!!d}eU%B% zYcG}F=1(i1ba-2;fx{)${7RPH@xeD+6mOn9x$fP`z5hNQ-w}9a=l8SDk6kC`YL{jA zzjLa(qXcRPXL?z^|9fNw%O5kdohIxLo)m@X#Vn|M##6X=-K#WEsKq=8Z}`u!Vx~dL zi>=pJ$XG5)Emu-1m$RSG_>b{}h`=L@Cw_Al6#m<@YPRc=>Ce0MnbR9A?(Ls{sge0d zYg5(5$!c1*`P+MndVH;{9ky>twcyd2pps}{p`l@GwO!AyFXLu5K-r4%R=<%mi zyMO1(Kb(4M>uDu@{&sF?I)_i-tY?MYg(w`w1!^C@vXgD*#fSLKOG#<(}y zGl?0tT?+QUSn6&m zdGqayTK>FQb8IVL-}}SwiqplTRd4G~e(jl)-8*UW*-0~Jym)m@YWn{FwPE{z$9gUI zdwcRbx0mXkAkL_@Z=YXbRofu#+~vU^eCg*y|DL1=-Z#9CxgOZDRLFV#9;dH244a%B z**#MXj<;ErJw2RxAcbE(?a3q8LqVSG?k*|M3okRMH`KH2;doLI+tqJ>?!#sO$+D6c zoofVNJ~h;1n#Vd%&9m#^5tgHe+pkWqlYVS*M=e#Fp>mh{`9hW%>Yj(d73YfV+s{?k zimiUX@ANI}_jlH%e`9{yqL`<)`S$Ym#)nhWE=Nbj-Mf&#&m%wR($9x%e5yH{ey+Z; z{8~$KanHU_Tt|0We$5t@ytDD(@o#1Ik{|xMH9velUp>#Hq`<|cOwK$u$9j?dp>LL7 z=U6QJ_v!Q%|M}OLtp#7Qw{HkNx7p9^wMd`5y0u;3YTu>#MeQtm`tP>9n<&3!YTD+8 zwMBAAC5$d#Zh2aC-OeH6_XFmz`Y+06dhuzWKc!AOd)Bsc!t0}keiDrL z4q9e7Z|RcHhj*X!Vfb(Of!pEc4n5Y;M;#eC8>UvRXWGvw()0fEk2mR>XU?qXas`E; zF+&~aZmSeQgMTkHl-KX``gwryZKtBrUxRZ~8>eshJLmD4M@mQk{(ieEB3G`$`1sDd zhJ}qX+aCQ4oXyBLS%r0djGCPE;rn}6eV@4d)3xZB-}k;7tUG<{_xsJ2U$ySZZTa*0 zk%W2Q%^fwBzbnr!JHDZ1(m5qtZ_nrL!X`RQ|254vPAN8$%+lZE@Ob0#kWOW#?i+DS zf+d`PK5|{<-v4e}XIsaK1;;}KFR<)mJCn$BGx@kg<-f{LlP61AyYBLM{V7$p<+t?n zmkr< zkMEet!};dQ?VAg^C!M?T>W=dPgL&>YhK>QCJ__f`B&%_MBNh#-lS+yTk zs#|;PR=a#sUR}onhQ~{!*}6UQgSPy8pytL_A@xK2rTcw(`SeHL@r%m-><~JqB&B>S z*YqBPlkjm9y*QP1*GmgrT=Ha%SKkOtj%PmRBM?ykGxeDL??|J=hrKMiJ@)sM8Wsrd z@R%fWc%^1nJKxQ-%vZyb)7Mq+4x9RR$>+uAZsc#hwj!j%&287Nt;eo@zxVjn_kHQ_ z*B;yO@uYfA-2Q#)_itC+dmE-ZwIOPkl!Hsv1g*L9J{uk|=qG5;>1S4pib!~KHC*$| znT(gqI~dL+Ykxat&~~boTjcrktM_AFo?P3J#WK6#U+RK{MI8&T$1UEsCFS?*u8uFq zBhniVAMjQ=xn}bD*@rGJexjyUuzL%yv$?^sJL{HjZhrf6PG!sUs|UATzCB;1Q@pQV z`1{X}2iDj|25TeA~P8-*D@{;}yTe!hjt$>qtl zk3}p$mfz@|QJlB&#D~}GgKf)vl9zRSna%aV=7U9_aO$`B%QtLrm~j5}i93CU<*S&^ zf8v;Y?q#JdQ;)=bbvbEeo$0&H^b5Z3{l4SV-1m2`tV_|qoo(URu)A7&rh@H!uT>pi zG*su>-#xNoZoxnOhQkuhAMaSY#aB4)Wpk}M5SKfF(S~sk-w`#QmGOH#mTZ$!Qo8+k z)oh`2achsgkj@XVs}RudDrs<8GXI!0zXkIihe;-X+?pRfJe=b0wuS2*U(e$=A%eFx z4=>JLZ^$d7H6BWrjuG$+yB*EegEe<-<{nlxxLqZ{hGIj_4@Zh7Ke0I*PBm{7CQH|TYPGP*h^JUr%f9#{=2NU z;p2pTmp>oa-0(bQbIAF5N6$Yk`Qz%c?a?Z>+d}7(&1u@71-h1@072NZ2r&?cSV%e`nIC6o|c4 zJozl``MNW+4{U8Wh`Z8wpT&)_>-o8fvu6anmKB^h`}oQKEhZ1&@bemeJE|WvYkv5l zNS9xwdX*ACWIT;ti*bqY$*b90t~7kFS2?riZbjbrC7bzOY-YtAXDR3j(|&$6%O&7rcGDy}ahe#`au}kB??7xOn5`oT?jgH?vCgE-jw&bH{guhnWxNGqxKX z+jiy0ne+}VQCWqFa^NLA=4Ouj*kbB99v}5Ska?h@m+kB3^sR!^I!;`(h-CZ6al1+5 zxv5l7-9v6rSz^DMaX#a@hr1Hf{?~PNci*!4ASZGDFz?~pwVkJxJ*?K7%(mOWe}muR z;Rl163l;C^?VTU{{{gdJ{p)D!Z+U*#u32>#%UK;SE!-n(z2C|~P~)X}{1S7`mo@o* z%>OM5WRiVA8Df65A7}bRxvL*p>X|DpObq3X-Wh&a;#9w_+ntOT3%WaQoU9c5!G2&p z1MBvn#5cX>D`c#ev~2G9@_NVf<)2T6-?Dkm^Z!o`!7`V zfxrcWKo<)ep9sD3+|yHDPuDB_ng8$EHPIQzHdH?4dR=|xvg83?raei8%l$2Hv}_jA zQ?m8)e7>7sh%Zrl&Ups4@<%L-_4h28CZ+T@`7`7H|MUFS#VWp9Jymw!V%gKt(YrF~ zLH2?B%%}2J9(f_Y&*A3*M%6j4OXmAB>Q7W@_m!~P+03r_=JxU*ALJxAbY8kUd8gil zgWGQ2@Lqf%Flz0Z@U=qyJr9oFz1g*+^sm+Rd)57~-|yX?o!#5FH($<5o#R9I(YsO0 zo&+x4>~TKm($9Bl?wfDOofBu!w<~DaI6Xe>>Y|P}W@%R%^%Gam)tqa$_M?-_k-mPt zZF7(1Gn%URN!GnJ)pGBpvH2bDc1+b>@(`+UIb>BaCv@9s(Ow3aMh z{x?uYA%$OiZuqRT3^Q22CHF{Qd40s$dH2?yiYM#pzX;C%^ELi`Tw;FDU5|qPI&+!( z$M5cCO=MePv#3|m^~vS9`&KOFdm4KZqqFuf{CIeV=kfaeeq|*gvl~0VY| zu3YK9Z&&i;PpRwn|F3&5d*$}|!@L3s+Vj49Nm@8f>anV4{^N)8LgCwGcwp1C0r zr*?Vnj_ zKYrinJ&?3MD)r0P>zZfIW-ex*Xt!bKF0bs;uZe%w`zNfP(;KZ3U{&HFTBD>?mh(sX zfxxiJZ7<^B!mBWb+5%y>NF)f78~}(|3LmuPw%p7e2pA`C!SL3f|ol zIc_$ypL`?d(AmM`6=TZyCw<9m!}NaH!Q?HK|yQ z%&if>UnsBp_riU<{jp;gc9r#>T^;`Y*&M6Bw=ee;E8X$#-#E>+6Vx0i=i{5lUMDYc zxNyb(A4*dXw{Jbz+i~Zl$OoPWk1eM3$-cg|v{Xr{S|nVw!TyMz?)RonPB;7Cky~Yj zCOQTj$aDEG(>Ix0U0qYw@{;Im_I0tljCoHE5A-=7TKt4=Cb6t z#><-3ix1edi5a#%n(Tk6;*H@~X_1Myz2qbIa-3mg5f*-;rhfH*tP5LZOiANnB{kC!zFr<#DYv`A;L-B6wI<6DR!snGPC5wE#UvpXF;5TU--WhKm9`3RGt&_NNNwcz2`J{tkmd_2_ zKE1r$;(F9g$!LS&RK_@_I`I_`cU_6EnRw~ZqNu5zJZ)zg|NoSJDN%9l$3F4%X9KM& zK1|G)Rlj_lcsemUyJv=iLWT*4_EhVybLv#0 zva@4qpUbY>_w`zA+`-w~gzDD&t$zGw+8Xnhh3l4YZ0-0WpgMQEn!Df!)10M?Gz z7d@S?RP_BDvpd7%6r(5U>ASa`a&$@Imtp$H5OF}_l)t_2+bf#6YqTz{pR`0>J5B!5 z9wWAC4d)qE>oYnYU0j^f)+SeJRP^~(s9oJ>-?0DR{-?h`+w}O`V|nL{@0~Nuzk7c_oUll_>d@Oh zzViO#xhJQodT&$l-s%*ZqMK3}e}I)`_Ua^n>Oj+=RvQu$^$oLV#UpTA5H1^Lf$JdDO z+g13-I6zYVK4YKM%Pk)A)^6_~9-guBhH-s;-qJ&VUawa^%y(@;)X~V8HyYU@iY-;P ze^2X7`LJj1^IJ}>?e_fw)2`n3&dSl5<*Od_Xxiy1cK<4ppGR>p9Y3fomU(+>qQon? z$XY|i#!u(2MK3j(#pkTOs_XHJyg7y~3*LLjFM9u@@b1zT7cQvU0ZUeu-6=E!*4{zg+V$ z%D?ZE<%4?Wa||l|vdKS+=U+b9DAk*0EW;GX)Z(Gy@&4vw@wK1;|K$3hpQCn8`~IK4 zo7nl4w)Agy+q}6lX3i_Gi<3fc)ercr?734b+ouwUH4e(@$Rm4B=5oX*CYQuX!ea<>2dzBrr7PY{YpH{ z+H=FWx#k~XVry9Pt@K)kRrflvxrcQP>pA{dCCsj^?Wub#t@3qSMX%dt%Y6=qqaHtS zoV07P|Kf`!>9M9=o32*yE%uT=^$rbR$cRBg@XYc(vCN?=7@dVG1IE3 z<<7^)5htG**VotW^?SIuf5p9BUpX6hCfm-v{!T;8LFPr`Y^%A4V{LZ_bn|SxdD*$@ zyxf@zo2u`r0uOZbf_4@r2QQp>>GO*7XCF@U%Key~-*GOx#A^16SQbeWDPQ-CZ|9zi zcj!DvA!1BEI&?c9hf(b_+JTT_(is?O2s4>UBKYnfAFN@z# zjiy$A=WycxkZZeknrqE%MO102)_i%Uf2N7%e%~(7PkZp? zrN=tIER$Ito5d}7Ih1Fr7~8E|Yic6pYyPS7!F=NaW==UJ`uORLZw_j$7_}<$dM)Cm$x(x`5K|)zH5O z{xiuip78OxQ2wYTuHkaVuIYKNi_S6KXNwb@Cffb}w)_2)laDAVIkBxfedu<+@Rh9j zf7~@EI$jX_-7;;{zU7-6H>G6!e#xr^x%8U!w_uHE-mATzZ)JaZb9?#jLKCU8 zY2ALzKTJ*;_gy*IJn_TZ%NZuK%=(-6v+rS(xORX+IsTW>$DA;EnO?WcyvYeXq8|Tm za~NhkD^mJ?$$N>Lo08xHsU3RW@-+@`-`%~sdro4o-TpualO~q=d7YX!)GkVjW|i|u zh%d{$!OX1E-lmj!QF4b~cW^Mv*Q((A`ppizxVf)PpLXlApngl#hJQ=+lmqTQb4-F?&kXpb)p-3=E=3~|KXanOWJ+$MU&%s zQ`+Uz)D|N#}{N za&_Qc3uO++|Kj^S&i~jU)@dT;yIIC5_p{Qr?CEg_E@hb9J(hjIobk5DQi)%erh9z( z{(AAEi*E{=)R<0|NiAIeK5@$43vtSG&5HE*x}4uvD>3csoBIhjo?7jnHvRJ{{kt>g zoojNxdv5KL!h8G)63g!1_b;lj5DL?pZ?S&f@lWpZYpefVU%I?2rbqWU50n-VJ_+4V*o8nL^V?V2+`ty_SNvrNjV8SHgzIzlX$w8c1W`rpRQ5@>U*$>0#| zUicUaN5xi+3P3DNL>nVaJ$UQ9Bb2;QG30!^;VR)GsmRG zi)Y+AAG!Eq38VI#Q~P*0R_~wR=wHiaJ^3QL`{I{OZ|>b&XU}qA^?~hzO6gy}cK(|l zAC_1o__E?_w&}C`Rowey>Rw$xsj#5ze_iySs%W>xrz>mypHI7$y(S{3g43fr!UvQ{ zyi}CZ*DBV|f68yKqA_*Wu9}9^2PzjRES*xfv*-P;+>MEX+xA|oy<+jt_Jx6C@3Q0# zyB<2X&1ts=1kt7VXw6_isQ7>&g^iN?bk9*q|TZdaQ*R4 z5Kw&a`_FUF{@$017hNnl&0;k7-L3O-RloWe|Fcy19Z3p%c13ejJ740OdvkA=EqNcj zqoniG*6i2iJH0#u)|b!OdrZx0ZnUPK)B(l^98NdF^7U6 z4{z8Wc=w#@5}u~4(3{yg+m~+^P-$y747=Z}7tmBJcHx_plyCb5U2T{3jQ1IIlqV;C zxEj9o`zp7~Pfu*nxX<)EFi}imr%d77>%%M1&i^}PCWDZ^ye zTz{b-W*;nhlqK(PZvS_AdFR(}ZpY$0yiOk4{r-`7xAgB{e%wMVqDnXZDL4vS@b+*V zHh5J1P2lv?Yt{1SpZp1be{bo%81~&P@0jx$x*Y^LnyTg=DBMr4;l2pFEIq0|a%hA;rGj=upJz&qYNrJ68{@6e_xVI%J$hjOkLCUgLf7tKaSfcE2Z*-$7*h{M)qEvX{tewAKzFR^5xH*=7yui z&p%9`HcQGaaGR8PKvS`xCi6>+A787g-_mFp=_2;rOdlZ#}0(SYBSe{LPGQ+Lv><<5srBuU2ekSRdh1CD-`; z;dC#Sef?i-_7wdzOgyVUU0&F2k5}uhGd!0{%a0gttgaT!zLTG3v(Ad;x`6%J5U$Uf zjIYv+eg;etb4pltx&NwJS-gDb!-t{!udlzVvG4!CKFf3dlK-m?8-AFwVNJxV8-`W$ ztKUs@5NoyUcx>?fT69HsxAZ?bNfudgwHM)9(v|0RUzhNm|1bBzvZZ78OOunayBB4y zeYk7N^5wI61NE8qGsrEFD&lI;j}_k^m3zKaiDlzHJD%HN)8w@Osm`hXy4ZTI>8!p) z_wIASKWoH(#3o2A`2KXdXK&Ao&FgLoJ)Lb{IDOib2|OXomdpE<+;MW`Jh5-Rnym(R z`!QzYg1i1APwLiQtBk(2ZRZ7p>$jHavbgDd`_Z!MV|iQShBf{_UPi7I`6hf#{8C|r zRXN|w-@Wfxrk^dfx_uz=z#)TqmDjhqExx#=voK_OF`MFx`lq5o%*?-5WSI5xFx@%o zx~lyBBOSejNrwe2}x~6PmKLMnmPaLV#(g#-y!>lTExVxVdkK*F|1SW|}3~ zZ7X&tM@?u(w#hq>^NY*=|MRo|qo!r^Rhna>@$X+Q_h%ZbEDm9A+s1YE<;%0W3?&Qp zwngh4x@^5O`tHjd2K5G8j>Kbb?w#Mix~~5>ebS+ypLg)ft@yla=TQMpALjQ9YUVOd zWAOiFckfusziTpYB64(ZPm(xw&j8eT;3%tF{&_t^JVVRGvzN+0Es5&9mNBm>er3Si znFqB+R`~b_&!2sG=KSNaam(ziSMvu*cd7o@Tqyoec>+t~53LVdd1ueh zl<%|DxSmdb{&4*pseHYLoaKG>OsTqGWFJh}ptJgW@cc#T|Not6VQ&iJ5x4)rkiWTG z*m1+vIg`KDhusf%+BqegbM;!WxsNsMpVuWyxE0mJyxr0>V{01^lb3m(hW-1q`_08G zx7#_vUDG3rhnlXjOSE(_A)`1tXB;$oL{S(SwsOE|2uYpenQu7~R_BzW8DZGkZflPl^H0-KTe!vh+DW*yUBj@p|Q}z11vV zt+rNwFPJv1KwNyKeZ_*-)U$p{oPVS~XgMu;#LhRN`XOu5&76{1e24U&KU(TNJMY4T zeg0p+a?JXkx2D3zXqy)AoTIK@=J~6#6F$Gs zo#@Wkw7@9;S>voJK~+yTP00Op+*~JntzMDv)Rl`tt#;=dC+D7vSAA1tR&4ppfBv=D zb9W6SKiS-AWxW%`nV#2o;PVRmiiYibdZINR=;%BU>#nfAV;s5pEe}&V8 zwQ%{y*4C*8EGkaZnlCOqdGf8rL5n*rtb;&tTOD!n>7Cy%{a@=nm+kseJLjlt*7tW0 z#k#M&zaK8VIH>d2ADc_1+pqrjyS%k$kh)=lPsp~3`HxR$uoM9kxu&Qcc1Pl{*Y~&_5FSD-s1L8S65$o{_F#2Nrq=+M#g_j-|C7N zs%hW9x~{wIy7*#=-nqZO14ZWUZ{Z3!bZ@J7wdH#OrsE6C-pd{Jo<8O1jYCX%3md}q z9*1k~YL2}8)#vGFwGX0K_-@UyHF0_O#8XI+XM$e*{yt;#>oMZ1rd*jm{i7_qv0VSZ zPQfJQx#!k?DS5{_kA2gjO&-0yiA#efZoanS&Q9mrG`Cidyf}gR%=g?_+1D*$5NSNH zKIF^-U)hg`wh3lmm&}~G5spbdR^UmYy$R51u=3 zS;bGIrP|>kj!Uhlo1QQ05q$U~iSfqGH_0#GN%gv2=J|Rv(kpzO!S7~ym)tYnjQd+( zeN(P}CGzoKUF{*OuU4~XC%=3X*(I(Y^yf$N+uds}+RQ!t@JD&Tjcqr7U-C}*_O?5u z;=$qS>P*kpb*9ZzLh|k|`gf=J#>$Ww&?G~<)6*!=Nvh~5ms++#1Zydqko~*TwMqI zBPOrU-geY=TKng3^7a2mo-Da{Y-{fum!&C8KV%XlR*L*hKbNpP=$B79L*w&@db$tw z^n>TmU0U;)?Wp(k6?gZp=I>MUk~oleAgiL!Rd;99=aU;%SA-RIyNYT==>)|`I8@E! z+F$h{!M*O?tF_VApNd~LY})oY^FgSC#7dE!!s;8g^w&O_=-3dh1FE&=&t3ZA+4}VPel(TYq|MBsWsw$<(?~>d0s_t&TwKn?7^XHzt{K;hv zFW!i7vG{Kn%Jf`btPwSr;TuC)(s{Y0oiR^ZD))yiuK8{D?oyEPqGHw=Oz+k`ni%88 zdEPN#?W$j^W#^u2Te$A!ReNut7w(P7SNX1TLge7v-@J&JO%+d#j(Sfoc=k*qcAwA7Tc&%8BC8FvO=gSh+Vye;$eM1BoH%<%z{5tttzw%t zNA`qp&Eoc+e(%qo75Dc0e|vDSZ^I1^n|x(4hqW(C{Fu%&GIKZQ{ zSLN?lZLItJ{;l2zz74m^9%*@1@GW^P&@%VjTLrOSR_iZiBrJBi`#8g-|JYlV0uBq- zIc{(LBpgeR9uMnb@0C8{9(nu@OC8Ujt08vv#Ysg$(c8Ann0xJIjlk2LsobX*y+0%Q z#>Z{`(+%d%Kc|L?r651`viNKm5+a{J&6Z z{{CAWm#=JL%Vv+wV5;cdaN4r)gTl<(-zDrxQ@Rw-FSfEh`^jBCa8ChadZFO*v^w41W|wVZ9kFLafUi$H`1kKbU;Wpgm0s7^Ki1VZ zSGKZa@LBTabC>Jk_1CAS=U;Js-g`58i&KdmQ;%}0&bDmvkN^IiVC|?rst78S zCTpKce9(QcpyO!Iz01o_zPZ~id9_NPBf_eF-XnMUm9eq2g}ZAX=&U-dFCMa*C9eI2 z&g*ik!^-^E)8;pec^#2r?KyHo;$x0kQQf)L)!{2+_uqTtbg-m?S7&W$>%x{y_WQ@z zU%j=h+F;t*q}0tjK7aO~m(6_j<(lnVUPqORq^|#czVWe}m27u^|75#@hATHDKJK$S z6T)RIcRZs<(n+DY;Xr9&w`-Sq-i9xyok#(^VFH4T_wJfo+?k@gcXZ-v=s~y8SA0NMU zyS`Q=6&cMq_1xPb)~oaWu~yE+NRHD^ZR(#ni(0JxO=GR&o(CkZ-`_vw{PAz^dcXSl zT&Vt{(fO}#O^Dao%Tu==Q_q)GxOd$>DS^Ry?lVV$h^^Y;#{?dQH*R;XPv5J$pEbhz zNt1Da_9d+s&wdn6YHt4Z#*TSTE9*4-9}S0oex9NiANJ5x>g+is_Z@Rjzk1mjv)$P| zX!gv9_rC9Qe|U(s*1%-ewJV`&r_Ps{uSj@y_L9SXs?5XSPHkN$TS-LuW<()k% z`McaU*FK)-(7FCTL)q-wTBY(=Q@HqhGMXFXS8gbHCo|Q1`mC>?Y}sR+NJqqLt7&L{i9bov_*m$CJPx$-0i;F(1)$P4^W=;xc#q_OfO{H|0 zQw8IS-=0e7`uZ*D>({kgIVQ1IE&o)RoKzH)pZ~_MkLkY4smIqIBGefpO{{@l5j7HyaR5qjX5!HK;; z7V+)MG=BHV=;>_p$bS!*r=6R3G+WOya`8)+Ux6!6&Um@yT}dBeowFIc1_m<1vud`HW=&garky&p+|ph zYxZ@GIo9p*Yt0^;@8$TzvV-SJqk|RSzQ-E=&sWS}ThRT->V5I*x#!#)YCSTKy192g zE$ZmF`giK?@|AaYTwHVi2k!&tYvR|Y1@1fNRg#y&o>TmNzhHNwnoxG+OVceg zc1>hHkhsK0?c00x>K_|E-MjzfQMUk-^;|VOi;jt253j%0Y?0tvZ>Vdvy}VK_DY}G)qhK${d*{1ATECON!;R#IoiQLYiwk`$2p(+_v44f zPVsmZLko$!9}cv#ik+Xkxa@P9@q0rCJEl1I(w$)p@z-9n^j+SzV8_}Go#%CC?NPh5 z!tazIPxFx%8XI%Wf*89mm98{dV`_H)X^eB}uK2k#=EUTGigV~Z*BHNYi*C-P9?%fd z)-A0pS>H2xbgjxC&&XXQCc8<3?c+kltr}OJ@TDBywBb_bnGmk}M8!UV>cDSS#=kQ1 zWwY613N%Be7xTYZFuUxLo__Ib|Nm2#7C--T@IuL-r!ry>UiX?S)f|vq;QCx+L&-at zxRp!zTih=1+NHH~)4EghY*xNrRo212@BF9V?~7mC|4+ZOR25XLeEVdhW$|^xL@&vk z!OKtd^t)d>ZdV{P&F;^HM>~tpmd(~ZdElq zocDku=-Z6FdsW)nH%>n3A=`ZF?(UHF@$siWSJ*yl{NAuz`p2!LinA@;TORUi1uq~?^MHhCiN zw3lpuvZl6fzjukq1*dxLms?A3j{R+W6!;?Cc9@{e@>i&*JdbDO+BC&fq4JoTJC z!0sIXVdm=j)qN5E{wep1pT3I)<)yjv^RK;osP%!(pj+r|$nwqam1D&v3oqW(p88?h zv}@ewxC8F)IV&X~&%mdcY6Gfv8yip9a%*qi9I`$(dl{n}Q+UH?;mMPy^48Q!M!0e4 zueiFVeeSv3A3bfk>r3Y)Yn*m|`}gY6U`K}}iC0oL56y{@U@J50wl%o0V_P|M_15%? z>=zjqE#FxE;Yz&V`{_mCBxmPeYx0!4-|su!JpbR6Q)g$heBEWIFssikr>;mPVD&4e zE8FxJx;{^Ld-$hv?}w!qx6IRDu*cU^#=^mg-8f(R-6tdO^z%jk^Z!r#)y)2@A*1Zd z)81D`y}x2EF{lM3hU|VjQLxH?u9E1(4-V@iQeGbBUm3IK#+nB)PMqiD{e1k`f`8UH z&-t1vP|~v1?SX~I@ksxaNhUX6+DtuvTjOFSkAj(zQw?|Y_wBE(R@~j&y?f7{GrzT% z(*wKtr5AQ>y78eYs&_$L{_WBv)yWZi_x^bLY4Y>iE}iEjZSR!ndX?}oUFF?>^r(^b zTs1R;h7GwKO?DysJk|DmGmYNaZ~f=*@e?CXN++w%-_Q!(nHe&bcX5WRKTX*kR!QYYQ?kgQ&H)HOHhI{Na+!mdwGhaTRuU`EjVaf5mk9wtb z_I~dBRGokBQLa>l@Q%%oYIbrcax#fVNtHf~?C7m2>l9i#>!$(pfy4#=DwB`2w{1{f zW*Cx|rV+d2LeA&YJYFUDR>bGd4JeuR)v9joHM5I1H{_nFi0M4H`uxc)FYfF;FUdc_ zh=U_)gY)5@qaEMBCx83$Kwo#MK$3h`qVwr`Qrne491fcH`m*{ zwS*~@bH(jEbLRX0^LNhIuVa-v>z5)|VXw2|+F`Xj$5eBoJI~EZ4==dCr!#!z3+9Cv zxAZ-4n9iU$$E4fn&0pqOI3^`1Y~p48y3=x+-H(P}bFI&^92dN{>pkOhH8u6SqR-DT z)-*L7_^gz_zENQI%XhiY4)-vgS4_PnC!t_xAz@kn{?{j?tFz5#M^=1j?AFNUEf>Ci z*2rncrP8S{?x<;N7>gZ_xa%Fgpy^)Gua_6L_PeCc7iRgfRqCMFYt6%(I=+5O`u_c~ zzTPo`BpVs-ycdV}np&ALYHzvrLu&*3l&x-y_Q%h^C>A_>=EHCGKlShY|Jc9v!J3<$ zci8{gKR9px;`*0>60=7iPvq@=!}iC^m6e0-R+{(X%*5EQQma1qA34~p;_mjPG1GAA z>FGQF_LK_0RIolP6bHTgfx;>z&fxGf(bf@gtU*wZ9FL zH+t;5xgak6<)6xF_v?1wb;6f-*0Ely`U4fv}yN-4T9CY8*fId z*NDAi{ieJ$k)MCEghWTk+4%Yo4WE{JpY6(F4cUBl>sgtikQoVk?_Tei>SOoiW^Aim z$hE#@r)SKVGeu+dg03l(7O*bbY~cI!wT;c4iYM>bj(xqDcd+)(!S{O;*9zuGWZ$?I zwRq{2{5uc6&HpR8banXde-R!5c8*S4Hocf`bL&^A`_KE9y@&eUt{+^?(Bn66?k(*) z)4+%P*SZW(M1B1Jt;@bUcTT=3%kz3;L(9s{=KG&FoD$!**7bS1%|nhY35#$3-11q; zf8(J|I^~~K%G};Z<%%{w51c#i*{}WozwbDG`fjwpX-o69E5W?yuT3?b^8Lta#TB8A zPetRed-4ldujvnZcsgqFKBYBtn!g61h6 z=ezg3(6RMT~<;DD`qLRg@x#kp1h%l30`R@C#OF~muhtK8|P}$>e&md;@ znZx;Q%!P}4_7Pd zML%p=&VNIc@6%ozp@q`2$6kuoZ=3&PCa+`l)BjT$9%-v3woE?D`e1pEUqM~X9TOhg z0Grdlx3V@0O8KhW&bxJ>ZpvOZH?5_*r=u#FYUdwasrl~zhxS-S3;W!Kb-e#CD%6;Y z-O5h<8~peq>kl{Gt()q0ao=&$Rhb@EvwriV(D;wv|8-Tb5|&d+a+Te>Da~l!iuFAk z_>2y%kF5M`_D?4Iqp{T2o~r_R4HrL5<&Iw|`Zcu3b?#Rc(-8Kz=U?y~yyewejp;D75_ib-5i^jqWHdsx(o|D7o1o zx_r6e#H>GBVLpeX_OE-hB(H?^>7kRC4&So137@W}rv7~U7q_h+{+#(xx_oombLGPy zSH+%h@7%CKZC}3p$9;CSY*8+$RkLo+|I>2o%$dGD9!D>~*4(*>t?2Nr6(ZL>wO_BC z+Wlek!~LR_HS9OC);nAe&|SY*sx-`bL7ZlUy{3(M)=i@gHinijOAolqvhU-aBeHSE zOuMok(8vle|CD|HFQ55}-LLX~E0VGA@ipG(nyc5=*ow?u`g+^h_1j-23zemGe{ElW zY{zEFvc)dXci38ZBxP_IJKbbE_sM8#_Vp<{w*6%7P@S^%Sg+c@*FphKURPhfvB>?o zr!6&6ZE|+!xs3($#NE5k-SuGH`rysar<>5iY7f z@A0TR72D{k-RG~5k5^>#sCc2fB(wF?!oH<;^JXpgJG5o~=Ct<5ZNf*&u7<7`Sau-2 zPo{GX!{tm1Ztgq3?|ol!XU|UM<2oN#%!|6&8^fEu^yY?nyQHrL89%&#pEG+^+ln`f zk3QD88pqh5<6R~a{If>ths_76pl`>%zwfWBc<@N6JN(!zh{G5;O?yR zzCM0pvwo~U?cm7a7_j!y)vObyWgOc@4j+iHK626NZIACF77yR@K&CW-L%gd(Piu=^ z{ZqjGcu8WU3LCqpyq(YAW4w+6GK~M&ue^Kt-tXz>&AY;G+RVN6@(%YLCLN#2S8mMM zb}i!~bIrSS`{ajn%ggS#Kc2YORdLRfGi!_lcZvJ8qy;K%*}sV6dWMPMp?7bOdZ>J9 zyznOFSwnwj|JF+eS%&uyn0UUwz1;S7bVIn7;s0aOI`tn8`u+5nBixe&^l4q`y)#RVKckiO-M>wmF9bYUh_rgL%FFx((+U?u4 zEQHVHidA&I&^i1wVfUY^?M4D8zgp)??+!D+ylpNspK|K{i9SN|QcBk@3(rmW+Ub*8 z6~*~(w)ouaMBk5rlmFLBUY+xlee0~3*R%L@r)(3}o~vePwk>RVv2)3}mh;lS<$z{o1P?!uI9_iPg{4+a$mFT+_`_6wo{6{LTzWZTIhSeO7SVvXU`_C?J zJIl3ev)kgAXWGNnnguUZ=feb8Dq(*raVzA<^Ckm&p! zjw|o%J^e^Jf5nA8YtNUyU;L-$boQOsp`b~_^1ItOy3Q)`BrkrSyV-WoxUWY9aKQ-^`56$w38;=gFAGjKCt`16)D*&(*+t1Y<wRBBK`7e9bamv{}CI<$GTr zziYE<*i91z756NN>#tn+;tn6r?PI6XK6)+n|06uXmGxrwt_@e5bZe$BdH!&!*N?4I zU%uu2WRE_n@^Yi$ni8q?F>Ftz>x6!YJEUsO|9`pjq~}}ZeHS0!Y_ z6L_1)Tya;4_y1)LZ@=HF-nQmT9e>$@^p)-BH_BgBJ7}rGTO-z?%rdi?U3vG_J(DB; zeQPH8u6WD{JZmXSjR)3F6!ylXp{(*H}*7%;bu|T*sFa+yi$q*gNSy z$uM(J?>blAAkL_mGyBUtTM0q0DQ`p4>c1@3DC6F>!tbeM$oEhBhufbXU!gZ=me>r_ zS?^TKpYe3R-?v-yVx)~7=jT9cwLhZu*I)kd|8vj1(|hhczISUa+)bxg{8pIbu~WnT zl;4rsN0sHA71>EepZuinAFg|#&@l1jgnOIapQq$Zy!A%sbN(K|&hJWc``f!TChiOj z-Yb1ABr4(Bn}|an4ul6@le+oiM#PuH%;I7ZGiDu%j>~hM{#oe5_XGF+ef+HaE5)WZ zI{ezFS!@64o0N1!2>-klAFt_ZHwt^qG`^hqNLl&D%aW3=)}YQq?((M9e$N`e-XMn)UsS;qgmLetvT6ewsQZpt9ZFanWgwhno(m1)o#yYkw89s%`#S zzx3``y`GDO>*gi>m|0`?{JKQy6XU95bD7*2g?@h%%s#m7$h!5b@6}Gf_17asYUc`> z?%9dHU%LY;E|^YTrp=)%%*VXHaSHp{oM~r1KK|0v@4hYbkbqa$l2UDu`#p6GJnfANhUvrLhb3wJF~-6%KmzsU%YRF z!`HB2sp;Z&%=c!`m~+Rg_cfC@N8t2%?g6U>x2(K;v*);HPp_@!KT{7T=HQ=uR=zda zJt=7MGES|jEt~hOIm8mJXmvbE#m)Vy)A_|8FEIUTJh-wZ?uAuWa>KfF=SH{-JD)MeZ+nd6x4`ZJFS|NcJNy3S_PzOP0cEAH(+{^xxC zy)GX%$!pg34EG|R|NGu(<-~HSG_)Ym#Wnrq?;lrRxmndoFZ#U8=}Cy){TFdI4FZQw zJ@w$>+or0Lue@Wa(CO#rOm}PVh3zr00ZeMS~arxqW4c=8NM8A962+gu*>WX5yRKnu8AZ}Lrxq0`0 z%BJo6U;FHSt+*gdukQ5F2e}XIneMZ{%RK11_$LeJ0}jyAI|NbGalR#n6-XU%QJa~^u|itcbU7UuB^Go7UxiE*?)tXS<9xN#AMUsIpKPnE57MW z)eetJ%nf9`$MlbDE5m=27`aNT$Jbx8I6A2GPZE$8ZB|`%x?K75DV{9zuL~x62uMpQ zO*`v$Ebhqa>zc7UF1%5G%$W83!zHo%ZVj)rbd{IBD#@E{x;!!R$gf}XME*7iu<-w{{~^DDfBvJy6&FjaYfb*l3D$h{_wU4)Z(fCD85^%|mEGE- zUh`7ErS!`AL$kcN&q|$DE@Rx!2H7f6HRax7cSB_vxrlwm?MJW2=eOQ?x!CFYf9Zlu z=Fd9(sy@3feO9{rQf7zVxjT2Fn4U~NzTnWYH>OpM;uDNgzkHc+XRmZ|DgVQ5kFRG< zSs!~iG9u&I?YkeWD*D$(uQ~alV%o$Ywy(Q5Ze4wuGb>{6-t4)>!m3v4fzv8yy*uRi zc80#!{poQF75-H1O|`$XCHK+c!y(I;hsRZOK47?DKlOb0*1oNFbJeWgv$`>?X=HrU zk=Of5<<#|cx!;d?ED8(LXQ&t6XUFt~chyUmrVS0w*Iq}J^l`MMPQ0~${@wjQXW#id z+1~NTKE^+CAB;YHURm>ReIR%2_pSG5yM31U9dO7vOmDf>Zj-b23Nr1#{{GJS?6QaN z4ntj^(3E>FpioNodHPxNf#`$T4*5AHQ|^h!DOg(Un8>gz-KjRQk-h$V`Py5W8#(^W zSl-UQZi%4DD?t{Pb<%gG!o5=0Z`^o3Bz5lF_rX8+bbczfYZ6!n+Abw`Y{MeO$v?hY zE$z2=GkZQi#wk@f_Wr9q)k#G`eosG#F1R8vcW=~M-Nwm>3=EPCTLe~bT{~Oj_0^tl zH{N>HgakaZaa8!56u4t)(CKyFhc|h=x#jxx+uNy&HuJt=I~Kq@_1CQEc0b{{^YeZB z9xYyctGr-!dB#$w+_bdY&*C)XH^whIq+Q2w=NdngZ~JG148L+KckaU`l2(;MKj*$T z_7b<2boh=56lDS%H{h#{rrB>F2?nYsk&D(cFhr9!KeRd@nTT2y20+U(b~;eYEMY@hgP!o|zZ&wdncV&`+Qv)Sl-39-Gr-t=vlZoLS;kcy@dC z^}alLF-+Zks+ae!LMBZ^xwR^$N1n+u{}+9Fb1Ti7%6sL3^HzvndDOK3(H#4*`WAiW ztFeg}E4g6!kW{#7pzim;eSh`wXVBh|yyR3m^MJ#=}$aA|LSN}xTeOxtx5n)Oj^W}Gn#~tW<;m0S*a^>Cq z=Lh-gZ*VQ#E-3fI_QCYo5fRf@C^+^jOlW+}A$e87QPVujRB!>ykFOrg%Ny$^`gHwR z7aJj%ne+K|X_tI`PrRP_=eK@8_Ebz?8=Y;kV(~7oy$1??*846pc1l~L7VPhNbxUJ$%Uq`)t6H8{ zu083cqhl2GaD7zCJ*j(L$#H!3`&o4SpME~A(ZBEaff$RMkv#j?EwjG5-DQrbVpHeC z#MtoMi3dtQYM(WqaNTdQ^Fa}{eYd-(@)K{ z1}$|ed(7szlP4%@#gR`w7Im3QMb&F(7?{;uiJkHx%&GIj=L2U~*8F4AGJasB6|#0! z>kD4vODk5p*sOcKDlBHlzJ3Fqq`!Y>|FSdZh=_|jHer(J-e-jmS_60{?pk(fjbWG> z(}R1g|IV+h`6qtYimi?5k%E@Z2UWrD$diZLPs&;^JJPN(&*A)H8{4yor1Q5p?a6Lo z|7SV-`v=|trrcOIM#q+7?3aRgLVD|hatTEqY;XQh4mjKScLiU_ zf60EwV_o6Nxzy}1{6tp5M^P^XV-E7=*hE>+D6UmvuyMzTJ$^FZO6 zc79jg6PeeR@o(*5nX7Ko{Xuc@%Z|;e8-zI5xHLU^pzv4S??IT>j-yIfZ$@Tq-hAcx zvw~;O3dF@ZwM0Msb-!zy-J9&xAmFvY*taG1CEvv~wGhVzIaxOTxsjjuwDm9Hi05TB z*_ZUDqG(bKKkM|?4G(PWr=H*6F0^QQ#>wqrSUxDf9t!t zHpd(N8SguF#8qc>+*ui{;$(l0W1jUZZ|^(*|9#)S#d*=k%T5m;yC1OsP~rZ^ZGZgD z?$$-c7e18sMSWN#@MHd)ve?p$)Y^lljB{ApTy@-PQd%PP#5?;Xcj&c#em;BF^7i?^ zl|Bfrjot9&aMkL*_m4xh-oEZI=V{4Z|7`w6fw}DSf6cj*cF5tB%qhNa5~u18^4yOX z+&>>@OTWLHf8A@lFyX;G3&+|_&h$#j{b7g0g_j%;JHB|2t@q_+ zyKa5wlwmI~ocL;Ov_<*5l!>2SKeYdEn5DWe_}H0{tn@N1QtW`_-CzZUVP2npaT+d=z_q%hK*X6xiuN=PL zF2-^0ka{8zb&o!{-+>U*Fqyf2hb zndfaudU%4>&Y{%uyy?d|+=tTBc{NiX>{6Qad3(XaDrTpkWO0Ymx$C_dmp5#g=+SZI z-d^3ClkQLYbmDWwzYm9d+tloS-kjdBz2T?WbsM{^!o_N}^814nKkV72_0d^s0aW2^Rx4$C%f-yx3&+n z_V-V53|KkCZ{dPJ$D$4Fh1(qW?4N(?@%4F)m(=)~_H!JUJ{!z3#ZObEuho1#*Y3_2 zW-IfA3zKf@Y|Qy~F-1dX)|Z~R&lOKa6q*_K$1VT$fbskPfA2N-efAUkVO5Z-HNSqj zb6BnKA-{A+T~3V)HoHH1OSE}yx}FsMMQv)39nb423k_r|<_Rs%Hl6b{^{QyqcfGB* zw|{*0EaZZpz?v^_?O66O{fUcs6VFm-Hud~}<`0IkGDXtYLffStM;4WcIJa}SCBAa4 zT2{Dr%JtV#=N*M5*ycHNT=rRdWwQa(*4hV^BKq;iX3oyO7WG(d)xzahXL>Pal}Aoh zm{7M=smV;&kK^!xb7}K=O0p9cx;#G;Vi%v8o@+k!{PM#rAbv1Sz) zgIAe8=R3Ezy5jR^kMqWd7Jb&$(IQ_ufr`5_ST%82on+s$c_5^NtE8>i(s zUdeg?cfm!Q$ZYA8DlabvZaUr{T=87?!(X0LR_e!o#m=*C3b3*1c~x{TciCm7>Q5>! zH*U;`iuYW3i1~g6b9rt4quv15(p$CiqAEA8>~<=B_0yxXBzEfy>8+M?r?7ne<#sRl z@a3Jm&J}-<*bqHqSt2i=P=3iJyVw0MD@D%FzweRoDC2<=2Em1P@e=9Q34p)2&Om3woC+8|Fmo%BW8Mu}(W+UijQ6 zlm8Ul;aL@0>ts4BebF`!p36r()HY7?e6&+$rfu~E{e2zbYafMbT@e%0xWCKw=PlD! z;p@!~DNkOpuEqI4c%wa!dRo2l)bsyM3wC<%`F80*9z){oBURN~V<)&>URgRdRq*}m z;MjzB*f%aL9?CI4sZ|MAI|o!qACG=D?xL>cMBe#__YJ)x(Xx6t9^ zsi|peOqr%U|0JDrK2NVROGrz3DNmMZ&GF3Hd)<;MA~@^kfB9k-V14J(hFNbvZ|kx3 zd?0;5o>5q7qQ}yH`Q`OVO=s@yjhNXJ^@WS+PSMOyZ*QO3`)^m^-inR-E3WR7?YmfS zGxhv_dqc~Yi!zT!RB&rN{LOssP}2w5<)++9P6E;r2Re?<^I2&U7^d}CBijD0=I(hF zLWREq4vD0hbx-wrX}tVmi9wpqvM#pPv&KAiH@pI0Og;T8CCfx`Vd6}o&ACZ8byiLk zEOPIAQ1@-~6>)LRyE_~|UQ*SXt}iUot&l2n^{sW^riVYo5AZMh^z%Q`$}4^3YuRT5J71jz?{AYo&h%UAysVU(&`$t_q5e3WKV@ zZ(E{WbXxuDcg^_yec>7=*=q}VXCzhIq^Yi|-qSH<)5O+G>k7|#s9d|XS0Uiv9ox4O zzn6Xb$#P2RQ@vBi^xhDytG`ca^X&U$v@&+L!Q+p|kI5U$9Y2?1Ak6vBJ2ONt=7i_< zzgAk+Pu_*t*)QImvc0GO*2x_j%?b0xpY?7{a&!SNsO$01n&1}U|K7=TL&n9Qn*6W) z5>#po625+WCE(b*?DBK>e)jdHOAI$n5n3N{MLl?N=NkuQgPh-q6)9=&I)7af zdU|`iX51c+r=L>yw6abxYUa|hW2)i)@j9T!Zo6}BAUmfs%iBiAxsw-OeABXP)#k4qJ)>6Pt`a z>`B{|b33>`|4r@54?otZ9+|y@VZ&dEw#27JpgsPH$AwrJyPuyEypnacrkYpP;{Ls3 zx0d$3D&b3!ICNL+f4<@rwk_LU%;9@2)toExf7z!>=^sj~4*xWE)0%p&_FXa0-cMXB zA0|!}-F)gt&QZ{a>*KwjxZd5F^YF}>FM=%bjQ_Z1q-S@#NtK>I&)n_t^7qn7KLbkg zE*f7umsaRg{Pc!yK?)|41{-3|synmw3&iB^#AN%(7@7ld;_4{`( zv#b2BO?agGQ9!p#B-OKR$D|ii!#2(3mWb2IIS}_giLv_n>eg!lV%ePB7cy^5z7#($cbU^WeJVQM5zy6a>4^J8xmlzwi6|z43_FaFs zhY*WnhXv<@$p_LmuIaJ1TJ>vQ-K>g`WQncEw5RXF_pbjC|iULs?i=({)Nk_q)Y>IA}~n!lbX?GsAdx2v{x;tDNoj;0GcO0#X} zXf-L6NTlB0F_ULz?rn*`KaSu0EWQ8WxBZ{XJ?#IMY`#<)T=TE}fPCVS=gZ9|{@ByH z;X&W+`}&`?90jVnPC6Yt)M2`xC1<-3r}p_$JA3<>$5vkmDHTtYlM`omQQ`W;>xz%8z)n~n`<{U zv?d?_FnwB7?tbAJk4{Lx-0{WW(Y*Dx=eM}GbFJ1sy(|37?#V3AM5ga`Ozz`4KIcA@ z-P#=M*>*)Pyr!(rLwl^fG~c95y73^vIPKCQj&nh4*7zRcoye7Dyl&y^Qe~_4mpgfv zikw^Zwav9Xn3BoGA6= z;W7AfCHT$1tMRGs!8K;?7LRA#=&kQ4wEM3=x%JSxg1k$U?-;HxVz?9Y&avCVQJ_U* z5?h_v4~=P4J>J}Ky#MgeHrtaK+Url5CTEX83GJQTC=oUWus7kZW_hO_l z%dwX-jwJ$Xjy1|Av;^kQyfHCN=XA1$=;k2JNvlP8&1+%<*Ke+P6{3AnO6sv)o#>8z zeYHtT>x{UsY<>TDs+X+r+(0h%sZo0CV?Q>&*z=B8^4x5;hIrO}9jUxW%HIYZmj5j= z^W(>@bHy(IYF3*2Y}f4nE#ER3t~aduR=cOQ^{e#pDTmIcyj$aW^$6QG#-9boArX0N zuPR@+;JKmEI{R$d-xu8q983;>pZRL4X>iPDNxgmK_piL}^SA3in|s>+`?2@YXM4tf z3_rG>bXpj``hDbqumg^o%2ucMcnP$~DcTekar_W6$VoTS`hUiFiJSYzHyLtn7x$h2 zWBq={jh1gN6Vg5&$WKZVxpw)dz=aa4>Un~8)~agjez~sAnBuj1XFKDumojTU@Yyir zxN6>(l~THP|NhrQldU*3;`eyuR_&T(g53noad0Q#@V0lCHf#vGl zt@qjQz1+C!aUs}<%`+hxs^LQ64fa1wAC?r!m~XAO|9M>Qu&0XB>!NS3OY`>r&Re~a zv)tFV@p|L>&2~qb-|zBCko&OZ{o~NpERF(}?NJYYyYbW)ME^dZbh^8e?*os)0-e)d z?`|~C_A9Ud9Gd;CU;cP}M8?*)_t^b5ePn&f^gt{?nd8h#|MSL9G3@UaZhAZQUth1_ z+~n6b&u$!WZ(Pstdu_<6x4&PwzqBq$IIFH*bAEBnQ?9M6!!OPgPl(x>K7py^^-c5T z4XSImeiIX$62E?NB)j&`pd;<=8!w;!<-6&CMcQHKJ_mhg{WV6DW-Jz35pl-mPLl$M z;DU`C9Ab;+vL*&E%9(xS0K?()cBj|H|2ExL_f-2x`P(c#gKZ4^`9vDy=j(_}vi)-P z(!)bM+H%LwrYzaW>i9xbribB6<&;;&Y-}O-jn7Yzkm-5zY$hL*nE$*bMK6PP|6C-c z9Q0THLB;N)%+nPQbet<~YFH4}d+kqH_MTSOh`Y8q&-m`ETD8v>`+Fc!VQI|tD?cpC zZ0>#KocAA*%w%grn{<|IVs?XU$R7uM7Sj}XIn13$Ky%&3e zUW+!0)t!@jkk5QR@%G6#o6j44crtm}hbL0UWY(H)jov<^`n~O*+2)R~gKnffSL<>W zoO^wJ{y7Hj3|W)k&#FXSwmvFUwz_W{X41|1PHD|lC7t5;RS%>#JMbOwQaPiyO@YJ7 zCBmv!YJKhVE(H$3rJ8pAHs57-*FQFw_;pBpL(k=dtadzq%syC8Zas8Q?x)wzg1pwb zXOke=?LnW-ul=lVg4exHnO8IE&AYo1A1hMUM_0=gzu(hrZ-4)t`_#twjPG!qjKS8mDfd9i2C<(-;J@7z|-xP0F~sba_P%5zbhD;}|Yl{Sy+G`V=d zwTF4%orT)-n5SKc`n~khF0N|+c@s2~n3ZF!_I$8Vs z%7Y^;Cnruc{VusbDDmA5L$Blg!Xc}D(|?&8{Je4K*G7T9e#Z^x7HtmdI`ZH_(NPxR z19?kkJm~t;^ung2&^kGNbHv6OTV^S6Ok7y=QYdbxj=7V7+3%K^3KLXZy#9+Zp8e*%ZOLj^;dtYo zO*&KW?2}cW`R3O$*V3%6Q&(57&GWf)=!i*4ilGo!wYP)h+U5m8FJtzw37mc&Znbdf zp`E^b!doxuwHxR?Z`Kkk{wyr~MN_SU3vCw$JcMSujs`x-Cv*Jr}O8!-`1PT zZlcOIzi)j$({`dt(CY2RTisH9-Osk5_wU0bKK1Fw*4#}B9I;XM34L#B zKXg~ky|lCV37`D5-O3#O9H%yU^!6BhTj@VhO8)t;@_o)<&q#{u1^u%$FpJdqv$`{> zP_b$He$|+Y$KSrL4PlKFU{Ul+F=+bSzF|#8(S5s5?EH`J&0jAmyJcC;$3-WL)?L57 z^Bmg;F1M0>J12(s2xixAxmLGz)wR$Kk^P<(d-u(%;1xZ0Rr}8S5;^5Faa)5lg`&5) zJQDeO>E>n=pL6e{es>-dSyVm!oNKngzqLXFEL(3y$-Kz*+b80u_StdIvA^H7&RKPw zo7{Rx;e+=9^#-3;4;+8Zt7xjQ+y9K0Ykyq&!#kE<>V5)Kyci3(e)v`Jx2tor8XSFo z%3<$uu3-0x{PX@QIIthw_3dngUi(xp-Ru8VKN&YFEM9K0C}MFu zwkYqBzWK`8_EjNK0RnQ4cS>e8s?SS!zW%S&Pyf12l~0OgezYIhKSM{vZE?xo@{~op z81nSmQ*U3I(4^op&0y0D%LnNVit6*jYtP$x7o{wDy^(|8>9^hH{QbVQS*v0b|0sR< z&!}#;pyI*aDF#iw-?T-U&MSLy%?Q_9EWO%m!iu*4P1X16*&AgWq;GF(jj+4_wLIZM z#)gWAO!wACul+Ka>w}b9(bCDG@~b7bZ(cupQL#YD6MpUEl^4<~g(Nc06-LSA%lNE0 zCf=y~?nK9{`7;cbYhBB#yE5z58y)WBeHUlURNRxE-Y}o(pX3MY1N#~NF@DfYaGA5| zt$mQh!^p*7cjdS3*eO$HwfBzZgc6JW)0{L<3%vA`-6zw=A$v2=j(?g>d(#HVfW1F; z%*z+OpZ76#S4+5-=VGoyi!CN@$~C&ZZR6kPb$t`(|F3-9V9?iopXuNF4pE7mbE{*f z9Ewf;TIwiJ#Weli%`@@5$}?iF9-o?f+{nFe!tK4%>W7XpGkSrx#nHl6_St5_5^d5HYl5Xo>TrtVcOZUzp@cK zYi7Qhs=ZqD^7~XNWvgm;-q;+i*FN>+nlImz9{yQ9ckxGd!=qmO zTGva9Q|y__>s6(&E80cy%?^cHD_t$-XXMVnZO8`rN*}g&d9oa;--L znS(d}TxaeZuzqvNuPs}aDA*i2^81&>-zVylzn{h*JNNW}4Ez85hX2)@0xX0ko-R35 zIhFOnk{~53?n7!0k=N#)-*oy{%B-@v8)jTjH_$sU$J)&8U#EJ<_wVjiZ!YaS|0X(r zCeQV2*Voy7ko!>jps2m??sU$%=Wczz7P}$y_dc`!)Amp5+XP=^f4{T2u4loF&~@HL zIcJ`HE{lwguqbXj_VxAJ7FFa+wq4`&+%c~Et$XJ&#X=I z!kaIis+i1N$M-`bYI!g_|E2OL9^MWVT7%YSn} z;a~NG@W#1HHrwrdT9TI+TCpG8mDh1&rO3HYvqKGgUd)-SwqyQ^^(HYr%Ez5FH(Ll@ zj1(|C`lw^Kxcf)Boh7}6qIb$$a^Jn(zBG~bq2O`f<&ys{wBPtPb^U|BUctUxd+tA{ zJ2=jB_PppRn&Y9fgVizOdu8aPv&Bcs&7aNqEhpN4d*|}`YMX1TRf;O)U4EVtJteNU z<#o}%&Xa|8$%`W1gt%oiZz%H=aZO(zrMkjyOWz_ku|w~R)T=o@&zalEDCF!MyI%CE zu5RGID$)GJg;UE5IV(hW+-j9cSYO${_5I`Nr&pi66=$wWMQ)>XI=X@L=ebQkefC@E!~Ij&)yhAcawojuu9D66t#RyY z&5ty`Pusk#V;+eve_?Xh}SGJ$pS^V~hQ`L+cE&F+% z&z*Znxo7ExUbiieOgZ{b+sEADG4OkSOtyCA%;z?3*UFN9mnHqIxo&*Wd%9lW?y9X< zt7~{Ggo~LrFZL1XW>mYhxa(TQQzOUhj|#dEmjp$eKK|Oo^?3@nymi~n2URjl@4ONA zpKyEg@~4$S4=SfDU$(;6cjohqslgs+o}H*?ud&>tmY5r16LBYh|9@2>?XUYC6C8?m zPS2mBF_}x6U$i^%_~T1SO&cWH*KbQR{OUd@u1pfSVw0nx*d^L^fw|~yamYDweB%gT4>g28?FBgO@Hf?yq9Ha+n^(qoIJ7PZA@0zx$OOX{1xIBcOAw5?7hR}mC+lo_0U{O z_V$8;NuHZemi@oK*&tB#rD>lz-v`eJ%P&+ZY|EAYd0bxI(Bei>Pk;RS;9Uic$F^o? z=f-nCKJh>G!Q#oSO*@#c{jg%m*|Kfc_u9SlKS$`bPd=-6{r{&Kyzw(*^e?Ym?9=w4 zqGhUAqWThvB?>uRjp|~DWhFBH%{V5b{z`P+CZDod@p_vJ-bj4C8JU^jC=jsCx9m9I zw!eq;?`Yfn|CX}w!dgRp!8rCXCBe#%ZUQZ#>oz?u6pG$<=jWXFe66?RRN4RVe9-Yy zy~NHx>Go!J^H-OcadC*>{Ji5&W7y*u<7Q}xS-(wMn^-irHg>(R%ZZnFA|fKz zPI#ZZNd2A2-0ev+OEk47D{f5X_M2YXdh3hHRPX6$%ASh)0}rF5ChoSZN*_ofYz zY;xE478gEYNVeT#v8z#YUg7_iin@89TDdPBXi{KM^U~OyEB)C1|K}s$-zd)E_|NlU zzrPsQ?cD{QCkxIT?K-}@ZcBtfOQ}@0OnXFq{paIpzH{cO*v$Hq*mJt_#>@BWx))m^3uxJu3s;WU0Cw7$huWcpXvRD){I{32o^7mrGJYg_s1;{wms^(eB)*9 zSu>3t%C-u!Pi}Q$`l0z?F^i+x#&rIl%T@I@mFr~TO zE67%oyJvm6>*yKl|HGzRv!Aex6^F*^VV=-yM84(XV;#Nsrc2#~V7`%1aB1 zw@Hw-_R))ZJ>Meub^D$fiXNRFTc9(~ zLQzn8GH8Cn?!0Xv98d3`OL}3R92x$A*ZWyYSA2P z+1R}%>v#ET2>es|pt{^oU4tECuaA1iR%Yh6*esh$~+!f&isS1V4ZjT;o}n8 zOic_n>yw=x9p-pdTC#X!)biq~{Etk38Ghuf6z!gw(Oa@>_5G!0#?DVqi8e)f7oIV1 zyl%Mj{@o=_f^%!%R?7BX4@lf|jO#YP;hD_JbkWDYT#t+D_|{H7xwrb{ySv@9Pi`^g z)7kS|@86+AyR36sL(dlnXXMT~SH55TI;;8g!?H3lc{y7QXGaTl7V!s)&lOI5oTw_a z$wH{*>=uV#4=UenH7{yqPg!v_J$+{FTT`jepD&$gQm8R_xGDFSTy@Vy{aVh`IvGU= z@|pJAMQW5}H5FIKr5=A=)Z`;z_I%H(={LIzE#C)!+%dKKljo(|+uiri*mF&1j;;60 zCnw8T`1mWNXUx$M6W{g4m&s?{=iKGRA&*S|c(K-({yZ*sjQ386V>#pRi&js0cm$*+ zGHh3u&((Gbr?&9M15jSpPZvwZk|iQqTUwYBoHEOWcBdn_#|+BtR3Ilgk2Ep{!< z2bN!~x^wR72{|j5tkYs^#Eu8;4@-V`gwxBte_Qdbhv({S`cFoPhxu#$Nsm0a_uZN)obI%>xqSQ9U=>3LT@?XKLeD*hb>$5X!9 z<^B6{TxYJW_FEl}Lw6_ZT~B>*La@#Lf2Be9EZqbD8ETd_f7X)jxf~f|(WKzgdeTWz zvaeO1*{E}G^+{P94;%TvEZdfHi$~a0ZHb-!ndjKknqA%TOJ53o+xlL*ywGLNIV+B^ z=qYPwdf)vc|DJK%;;pjs^6G{L7p~f*?~hql^OUQ1=FG5PUaZ1v3-y?Mg<7%|eFaLk z%(pnXq|-biuB0z9N%}FrWWLdXD z6TdEL?70>dxiR9jp(g*^=8ro%x9r-_|E%Gm++{Pfy1&97Ul-5rUflc2ph=;mJ+AZ# zs6nu-;dobQ(w?KP&+b)uOGOH_q>6hVK5y&(FIhhO-z$X={%YK61~kHM=I<`b1nmCgJ6cjb(Rw89&&mo!&WHGMDjKr4;Q5F zX1E-Cxn@e#+KP}z%}w{C8)`G=ZZ5Wbboj8vNxdw`sNkK2&8L2TUc0BB`GH75&Z3Al zE(<($92RbM(hS+$Yt=B<57e`tV*W0HYv)oqgL^3r^BLwbK00Yz-E-P>bwO#idF<4aWHtW&5gG#5C>^MA8nWITS zP)|@yI40BIe#P~fzrJmM>c`o%;SWoCPR~aDI>FmAvi?l_Ich351>88XGjf&X_i6{Z zxe6Rk$2m6Z&u6u3zZN~Gk+GNW#az+=FgsM#_w#}zvD*N z?;^p+MRvjy-^m|#iHOL)ArK+HCgP3b%yVXLC3O>CZ0G-R^0wad<8sQTHg`U`)ud*1 zwU*yM8a@9}WATCV#`mR$xA%tMSLIWSQ*O+azF66OPqECVZ}M5a<;*({a4c(Bd7&mv zxvn3$`jM}$bU-;Z$b?i9Da%S7f_qUHfNVmH@=Y9_3 zKf4cK*v_v$bH6-RCB|8xg~NdB5ATD_oZFuARxV}d?29%Bt;>Fw&EA@xdRzM|d#mNutvTNuXUP87`M?u(T-dsN(#@TlQ`u_* z)`Rv8bm#Bi8@EwvJ@fyWFLLz02K~%7bLVT>`3DJ>;ED0`=BVDK+~OMZ?e4BS z{nO-w(y_B=f8@g(#wuqEzx#IDd7T7#UgKfnD);clsojk%j8j)?KI@k=JsQE{DDY0v(BQx{ z|9UCy>B=z&Y8iz$D^|a4QphPQONu$y9-_0%sGV=(&7H#5a{Yf=r>~1i{CC#;cB!R) zQj+Ptz6mEnxc&=(`0u(P>ywJvo2+@&SYcpTSW zJN3WTE~b1=_XXt#42EmBmfkMCbFf#*TI_I~VCg}zg*N+nGdxOb_=b(_-l&P=9>b+A1%sk_9brh*129A@c%Tkvr+&4 z$8{%l1y~#-q=kmN7t;NkN|;})PQNOW!LrE?0(;T)|AWfqxwLy z(RX2%#LYr-#s4inNNzcIwEUe)`3Hs0e=pns*$vcXBe>y&kIsqe0a-#%*{$5O}R<0sNx*Rn58bcM!+ zTc3Rl4|2p@k#c!%^z6!oyoBx|nIvcDBcFb*+xz)X*3?s5v#+G5MeS^pD&VQmH`(jb zD&h9T+0yU${AhK#<8NO_XF99265vjfc+k?mt_ry}fnjStf6)yj^TL|M-qM`mS%ZuQeKZvEKT8VV^wba^uzFbF*hW ze%pT0DnE7ek@mKNpu3mrUlkWm@tc=Z%4^Wxd2NTl#=1vLoJ*$&-nV+Av^_?=`f1O* zh#TxlTVrY+EaId)vf&ySAoo++$>WcJ1`|xc<_+LTZ=)-WERkXwDwlTS~2h z)wE~9vXT&`3G4cGd=i8CctlYQ?(7c@Doz6PTIXY{)r0@Th zTWTEC6Cu!&s+O|Nx9)%4rSmqI|6GlG@ZML9>(LYemc+{jks1**Vy@~h_&USb#O2+G z#Gu8CrL_dsh%e2)KBM+wYgy|J)d!*vu5wsO+x8Slv-G;97|orUy7t&siI(ejEH-`D z3d=J(j-22q6PNw6^=IWYzqwgQOC=MIJln3;bu{S6ug*d%?bfFj{9U_l%=njZn_s9W zx9355ddGK*55gI?8P7j2DSM}KFKJ5u_AN&aZA=EWcdpyMXI5{tKfdE;>o*S}u?aa= zbIm`MG(WY_ermN_;^S`rSt@(N1NUzD@Z-eW?Q{5;wrrWMz;W@BZbH>n|Fbv>m~k&Y@Vp^?%Hta=Yk!}3d;5B*`m3y! zXU`P8hRloLso@tHzgW zLUAiyem-)QGSA<3DSg#}!UumfN+%d-Te2P5p!2xo-G{$hXX#yjn%Q;k9=DB4_D~?yql~YfryA zC3d0z<{zPseV2DSR+QMTxE{{+N{q$vh1m0mWYw3^`TI^@wf|qdC&=^NJOz&Lx~q(D zZ<|>CZs)W0+nE2qOj^Yb>ulrQb&Z!NU!_t;$X z!NF~&(X$(kf^%6N1r{Au`VeGM7;$&j(x}vLTd&Wk{8!n@E5!29?oP?9gPoi@!FP@^ z)d~IBa!1lpz>M*B<9t@WRW43TzrR=CUY>ut@33DGXtnLs@DF0*w|`3XYC8R8x_J6U z?j?@r+vk0&ZK{%$ZS_fC8n)Rm@OVeBKa=M2{qvu^$hiJ%`nOup0kY@kX1gx#{9-1Ow;Ke}3(> z{`Ty)D+f}09@t23WSVGeUCVdddZ7%vw%q*>C&X`;cJlJ*+_pWOEy*W)qu(ZQKc}eE z^3C6BH4mtAI4$8&=d9=TS(~}+VWm&)bL%tvereTh)iMtfXxS}x?d8Y9PxAi?KAR^W zSl{ki5S6wmAmHJW7y*`rk6V6zk!Q4PzIOA+x3^FDWtHZgcmH?mZ`Rb-%gZAxUau{? z+pC%&R8hDn;)~L+(!C$7Y-L#{YN}M+e);FY@A%_Se!OGWDKD7tWSXw5eo^^vw`pEe z&dpKG$XyqI%q3fV7!l%1#bRKETZ_&7Z^yKsKSKOLC zmv2s7_OiEv_i;&{$C+LQj)_lr&s^NT)i>r;>bly`v(N1L&~(SqQQ+M?%jH2lbuXN6 zxbHHtEhcT6*&5Jb~oxT`YLBVTebKB!%SIgxhG*gJX?2K=I#CDwfa%w z;V^1`WPkEiBK3UrC{8kzE@~3tZ>}@-D&v>fr zt#2{+>}`E5_i9kn?NP^S&0wZ z)wo-aSoxe{bri5Vm9p?cdneZ~m1&~=?{7H%d*GO)=B9C}VyO`K`~Che&&>Qxjvp(SbaGuxe!%+RomEq#vWx#% zxlNeUvqCwp_4}=$ZM{!-$%@ZC$Z=lVMZ?7PxnhD$1z(5t-@i-B-W>2;Q>*nfe6`M8 zE7zwscg+VKM-%mbO*ul%Y_DfuWfwY`v=ByXp=dOM@ z$}VVmOPQmoKt1uuvF{m$+jp7So_%WnH(1Pn-jvyz9EWo6gmnE|KJSnbcN+H(mV%18 zV%?8Q#8?(8x6GWz-VpEbdhNLb3_Fj@tJ~Vi{Vnv!US({&jL*G#d7kMqi97Maxzmj$ zmKU=wedT*`<_Zy^nhjzL3@7sc4mef#>E}DQ>?x;p_WaIUz3zt42e|^RQ~vFl5?4hJ zN*>sh``9FQ9?J&a3YiW-h6yx(4Xyps8NgFXAY)3+02I};!2 z_FQg#NniK&F+bha+P59}PPBe3>o*A$pKI%MF_OJ8#frO0AoLv5{o^i7YiG75 zAK&p~>-xZbC7$n=C~%mF7rp#gIEkIlq<%B^kEGeIo=0Bla5ycOWV7S1@z}X;@{PT- z{~T=I5nHriUF)$+LdEa*IOk@qy4$an7;|p#gS;<>m51yc9RgMhx-9tc^{vnIsTUoD zZY)bOOp4wnATRNvPhpj5?@`xjep{DFX7?yqc51$ma4osvZ*um|d%c94X6H^VSA8_Y zV?O`l(*0AtCf?b0Hs!?nk4KyW)(7van0V^v=ePGZOFpnZpe=lK5l8fEZ=M(Kn%<>B zC1o;?4j(@KRg?QfvQnH}W!17-i~0_n_7h-9v^>Om#N1GC+O!pM`_HwmukAe=yjQ>(&58v zpWP|Tdn?Hjl7A=R+j0B6?^|*iVh+yf+I@rhtXP7}Cn-mV3mGnlek_%XbGfkR@s@M9 zXY!m}8(nz$<+_;F@#};4)y(|m<6U^h^nu9-x0Y5<`#pZb2ZL6ZF74MiZS+y<^mlIY z8+akDTDWbtlSW_j zT#u49{I^c$FNm5kS=_lroku=((Y{9>32Rh(%TSy>$oCP&^@+n&z;3OA&>4@5gm>9_xNK*-B<%kziftM3ZVm48#R&9Ldd-cySsZS9_X{Oc> z-R(ac`G5RLS$N^p&M&*w0#9w~DfSUyNo1a(BNBALz2Q8QX8CEcUwtx)_pXcoG>rvK zFF)hq&fkCco%>ITj~{NkM7!*n&L)ttmeWRG%I|sm@$4D@V|A82iry~OxRJZfRF>sR zT6&~a;UdwAm)Mszgg1D#{BG|TJTA@B$oJ~&x;b9k4>{#&Z`s^iIO|*V$|+uNZ#2$6 zz4mYSwbwc_wq5@pC{5#E%(X|ZkJ+@+dX06Ng}tBl?PFeQ8?Cl(%)D(e^Gs24@a7p$ zr+xS(m$^vK%6hxBCPzx43Ww8`pRWrRt+*|@HtX``m_Lv0pZ$vex0K_2(}xx3mpm@~ z#LlRo9;rJ?M*XVPuF+}wD79fyxSxD!!=?1+>^$TR6#a3gj5=Ac)J6LrJ{wr`FM+FkZgPoE{% z!g1}fM;{-vT+Pz{|J|xUDA>i|RNjr<$`QLn);9+6UuxG;Xbsw!wxw-zm|{@2)aTD< z_J8KvWjp_W545Y@6}#{%`?=&J z%U;{6n3=k!3$MSZD*n3G=1)mqP~YPt+j10b?e1;O;9Vb;`tZfYFqa5kodhlpx4rvW zX7Em(^{w_#)>V<&Z$2Dr33}zY{9!I=;op&KYgOFcuGzf*_`3Aj{rdU8&dz>Yvz=>M zN5*N^=dJBY$z0ctaVm#fZQZ#tM|%7E{H6PzCLH-tAzl2<*YQf|9TDHxWuC1H8~bkIwGS&p*2gce`kJ-+qSW+;>4uBym&~8Cq2ByL z6#G@JUEJ!%Q*(u-B_c9oHa0Aodve8>cXzYP_ht9S>lMH5HNSPUtr_QGMG@kTJh}`k>k5~P9Ha5dq&y;=jJI+n3d{_EpwG4NYf{#e|=L7Od zeL-uvi}Uv${b~P$dD;B>c~!qzb6Ff;Y&|*a^`x6Sg*ZQS(35WX3oJcro7S*rF`nAi+1oei?rw4OsjvLDxGg?-sQSI&m8h<_YnhKXJZ^D4soZnSVy?Co z$BVFK#?x9SpFYU-S@P+wvYO|9M!U-rk3Y`--L%jD=cm-H>+2j}FPqPm_V}f{OTF^K z>4k=@zhz|D&8NSJS(1S<@vQbs^unXv(#qZ-D|WaS!zy z-?w)6eU0SM)ie6KHox$1_5HKOWw-&^lpK7hiafof* z{iFlI2c|dFEnn~d)%LGR=fsI8j;myyUtIE#sd)0_*M`TVSmqty5mtTbsSMW>w-UCs zwfA>K?5=$kv6B6pqV`<1|4$|_adY>5;&j}4g4so>r z=!IQz>Msr*Yf&mWpuhd#0+BL3_jex>CpkN3uj3V~klQ?Y^4dIw1MKI%<};atBKy4z zt4f37lubIv4|*=Qj^g%<6f3jZ8)Lu4V7c(;ju$eScb=K9J1nT~DA3~Pd&B!ccw_ME zpF4YZS}v>qzDzvj_!h-oQ7X0eqnB!0T#aCU;=;EtrH-xrRhqEQ{ixdffD%5bUXL}0dA+Q^q$=4-^G!(B z&B@WH3O(N4bj~|3S9~gox7LZB@#@P=&~Spf*%F>T9feli6Sg+)W$<9G)o7jSv1aGq z+xr+!E7_TBiLnonSpHey;`LbbDf51Q+I3N&(N&-&?osH2enVHsYxz1~Y`oIX7d z|IU1iTc2mtzBMh`yHuoA>Vw&`4=L+qQrEQCy1aHjsYvT6z_2qU|9%Sykhs^y}|A~yLJji`lrQLbw1wz+4@e^-_kutT_@M- za~xWxYG~q;bbTEtRTme9vpBvmo7~#On#250Y{P9YwY8vyEjKqZ-|nqg)ql-IOl-!_ zkNqFDrBCv|Rt4RNsB=`j%@~oswE44!dC>cgb6cexW9LOW3RJas zCVk}kP(35bNLuJwh*f#t=l>7w3tsyFpK|o^@sF!RcYbWz;8_@~dFglF?AgHw+SRyO zS8`}@IBoZ2n$K*vD#uz-UVi!i6M}d2TxHi}_P(t2+5g9As`vC;rJvajl=`&2K6#<$ z!s;Iq7jItPm(O+7_2A1rbCOLf zGW&Gm%`$i9DN}<$EsaH+nUWrUwY_8-TIF7nm&VH3r0_+op7oES^ot74RiSC>>N~&n z<`?|keSfK;y?%MDfZ5|EXA3=g`_09#iG5JHpkD4K(2^&ZSM!$rpW23_UQ0v%%}n=k z+`dAbpNGjyT(98u)9DxAt~bs;pnYJwLFKx=J^h`2A2^uaUVnYJ+-m)VmuqC&&&;#E zt0`A;+@|K=-|x|d$6kG~+t20WE7H9;MD@;L7mi=IK7Y`f`Yy*uBbDp;-HYZ^awp&1 zx_T+E{#w)R_i83D_?pqHwXESqiiH}7w2kSpIe)wtOey+YX)IP!wD~gVY`J9J_}vGN zTFe#J75RTJr1y$&$K^*PD^2rt5E?6)4LP zZYb?<{HW+1hu#L!4$aet&OYB=^yr}H^6;+f8yXyMY5dtM*|F=z9S-@pKVGc+Lna;f z5@?yH+;j6seZzP2noqBP>&&$}diDROc#E&q`;Wg_z5dbE)0a4!HbkafpULy_Be!m~jKfPG;2jd5(V=r&K z&gk*pys-HJv*X6udCx_;IB%^Kxyj7FVONNQz}iTQI+?psay$5CxUaMCEn9N?xX9U@ z7YY{~*IvsLJh$!F(Yd#y%Zxm0Dk|QxvK%%%IDdZr*7fEM*9E5+FTZEcCH&Te;l`=b z!p%4P@*P$#yKN^Hz3%>sf_3wjw)5JWjJxM%GL-Y2b+EN?mW z^z@wH+y75X-v9UZnLR(0o}QkbDkIt=cf9Yo=i3_>{}{5`NQ-?GVsU)&rKw)vhw6h3 zMKbEeKMZc(-Mw+TeVUBymSu+K$Io=@KRSDM>+ejv0;wPV2YP)UKHSPmU~}&tcPlbh3K1sa2NGvftYe7b-ZsV6jx_51KaBCrMpM zTTJHe0Y8tV(_(8%s&4)lE0$UT>JOc+`Ju$Vz|2;+f2sn9t*6D$AIT3^AMjECUsjve z)?NR_ea_$O|7Wd^|8K?pet-Y=9U8q#9JU>sa)TZ}KI^%a*@pGW({mh67ZPP22p?D; zsP&qUfAzoR^OWx0zrS@w^VLOLpFNwMA7od(Ew(%@c+OwFWde87PB!rvG$l<{P*@SR z`*G6V*H6W*=Drs(&7J+`-`~613KA?jbFJNL)0R!m;Oy;8s^ATtuwMN4384UWjxFJO zy_u7ZX?S?zw6&=~)yq2G*wK(DQxwMUs z3pYDnZLP}yts^Q~VzB75a`X@DSBr{Ut>>JZ)O#L# zs`Txf$^MgW?vvf!&3ZS?T=CKr{kW8GQ^P;p?w98_xX2ODo|n6b=XzS>Zm9)ZpD(PP zpI5)L`e>^4M1|@P3D@U*z4d)&_4n96t*v4I-t#_?eZaFzdovjc(t+ciH`K-@#wqDbjYnQvoG-|i_c7w0g>pD;GT%5zWNh8&=w)V!yitB#1 zM`pS5yqDZ&AgJIxm)E<#t(_n!wfp3jMO$NYa+zViOAt8-UfZc73M#2*St$pRHo@46H?=E}4+%xAOf z3R-z%TkaRze-?S%?zgOcFJu@r-S}}5wie*?DeWuEm)+`Rx^Te6t-#JKP{+(HQu=zs zu4_^3U#qSg&wby0eS=Prby>;!^O?SXuj!r&Ung)SYwef0(KC2H#NAsHabQPP^MW{Y zzvq761(zP!l(##2YU)G@dH1!2pS4$9jj*YgtIu3{b>jn;WeQJ(oQ_AyZ~i%xRcHHp zer>VE()kKYMf|k31{of-FzD$xH*x79*+s=)f0b5lVR3v>sn&H=^TGV&vLk!f#k5?z zl7BDd<(Evo|HuEI*!};@{Gwmq>*xRces40HP)kSh@y99O?wOgZH>j&|vqpm&S<=2Q zitaJjFiM1e+%dKOS7uOh@?Gs`+uC-XKeRGfN5=YES3re6@0q|~`;S=InG2tFYAUy! zySpo>+;Z;Jpp;#a4gwJtb#soLoxPT0t&|1(9?$n~h8aC!!H(IoSF&PH{tc5jULE|V zxX5yjPg?Frsi@$cWxY|aFG{ICa6j;~GwGu9iyI{pH@H*+x+3D|TV`I?U0$r09`pTs zKf7U%vh}|eW+6w?*>n_Vo@-LbaZ>9#I(c&Iq5oYszy1+<@S@<;m8~Z~|9%fzgqo-4 z{;yO0g#A~Mj|Uy^{HrrwxOqv~Oa&Ll)Y~fV?!B*1f0y++;wE!aP=qD%u_uE)a}CE* zp_6xapL~D6f3lC;y{9qrXD2^fIz3~{{$*DVsQP?7P%M0O(SFX`H<@4E6%m ziJ#pnUeQw4g{Nfo9Nt!5NlSb3QHUim*dS16ALAeY1N;pBj@Lhbx-je59~)!+JqK>C zubXNd|5t1J|2OA%{JZNUyD`{yQHaCxmx2)6KquU)3e}8WG+qW}j@Hz^#@G6&B z${*=CryORq@{_;4Z&AgL>h8a$2IXHgmR??dv3J{KgHtmuXX$UK?7tj-XzLEaxzfGs zj%D9@S`yavD@2u@w8`RelJwN}aL27x@b+Cgm-6+&nEJ+O}_Ql~_ z)HasBp2h85wNz+nxA@0s?M?R%h&0>S1?pTAu=3=SV35(CY4TAlwuXP_O`R~Kop&rU z6Casg>d$g^jV;k@Zh^C_ooWZrVW`LOFqb4(y^=fymb1EU)Q$p0JXc`O5c9~ z%HfYXg|DQg?W(^MBp?@f$A=BnCV9+YpjPfG(88<6TO(K@wDi%+zrRnuyL($$@A;>x z+GqCtv$}UDX7}|~jhAQWJYPL~Ma6@;#h26ev z7sq;~*M3>bV8JPKl;ihaA68*q!539~v!#VkZOi}O`(#`0vKe=zR`~f|oH0B5>#h8j z>>bjds$N()JJ>(CaZ{&Ei80n@>g%-4H&$NpIvj34f!jP!-g1%L`i&e{-v4oZd5HDb zyh=AYLq`F#%TNCGo^*ORpW(mIk11WZZmcn_Tk7=kj-}W0a|>g>f9t+__IW2S-=w?S z`>(vatK#Oi#F2HOu<#C-rXMFbdQVS3xWl%Las8>ef-H&39O9?_leswfZtk3O%Kod! z$%6;i?K&Oi9I(3Z^VaJt=2^2lURd|ShP_6(qIglnn!HD9>l4q!Gw!?pH#l$M<;d*b zx4TmK<&OB3J>Rk`TrYTE8Sna#tfl&)2Ip>M3m;8dAO0b)<-YobdFg%vC5|O!mond^ zT$;lqEpsF)S@7Vcr>85{x@NN+s6KF8;$!R!9c2gkg!_F>Zyr5cKG!Qzb#lhq>D{N# zFjo5TeN`?`*&*)o{OGf8Hc(5mau18+jS?S`?)&?g+bC)Ku z=%>xAmwRe)gpJMP{Y~e07Zg9a_dQ54y0oKd17pNG@0ye)mFuiobLQ)aTuCdr%h@Er z`Kw_*E8oHpo!TFuMojN+ombqail0YVl=1A3$qO&|*0{al|EXu?X1^pJT3*=K&)4+s zSoVR~ylWA9*SBW$F5R5IGIl4^zWZI9KS>#!e0Ji0Zi3&k-@7g<>J+bEXTbL4;;o`zzjoVjBhKifNo-Z%(hv7 zKaWFFiOBZ#{ZD@Eh*bIV_2HAEh;`j_eWcQx9+#9(QsCIz@zmnTuis~tJhR1bG^r+)+=7$0abw($7(@p$L=@9z6fY}mf#$eY*e!3z3Ttu6nefb+^{-uk}3zyBFWYw7IQzwEr9pPQL9CHwtvyAO5+MU!_tvGrNDbGa@5 z{keM z_nepdV6&{@!CLnPXZL=v&aSyH(#gGRHrvHB@6O8E+}Jv~1k}?AU-w|D*V-eebQsnr z+&MR!asILwZ+^3Eoz=PirQ7`inMcvv)>uX@uKB9fD{USXnS8a;yun!bXcF_n%SQ}9 zN@+f|u(+wS?e<^iLwQ=QQ$1$XeEz+@TtL}Q-zKY9wDjW5;~nSz&AlGAyU^Js-BG|y zSNQ0opC_Fj*0Zf++RxgPucls5TQ%#~7n7%-pXdKS=eNAjNb*s0v&BrFFb%8db={Ls zKWx$e_^ftAu;$|_e5~1y6;C+k%AP8n&1&o3sFTQ;<|xqeHDOc0hK7FT`3#@lP1AkC zC$Dzzx_IUE;@q^ho96cx)V;k`B{IXRz0h+0U6#l_t7F@zbHuK>^?8^4`ou_;W5+w6 ze!km%ZAU@ludUf@|NZqoFk2$|WSRoUZ;xgLg%#I#Z!>(jEXd;LpIK&ywmc7raIZ;rHK}kCx@R=rpxoHzxV8(wq2iHBOcB zTItWV^Zj>1u<{Da!s+RnUw7SUVK)fW@$(ns`mbIf_{04G_;-;~C06ea*7{$;&U7TQ0uq!?u0Dy!xNNQ7+Z$vrRX$HVJ(7 zjL~YHeEQ;wu=jHs{7=snY;ot`A9HU-|GlZfd?Ko|@;_8l{zg|!A zpA*sFAI-3jdyiG1j-3|k;hHmQ%hop^a8WP0{W2#qrC;Wt@ABQ3mrL;~TlxELRSLRh zSot8`yxX{JQ3Q_~cdJo=d-k8Kt=X*VZ(W)q;_uHow)EB+p06)6na)SA&W>e1-ndx! z=%Yz{YIhwnFlf29ZtnTVOM)ieU@3J9p0d4Q{>+;{|KF^>eB;EXFFx;gnuocuG<&BR zeXm`=bam9xS4*Sr#;%P^?afJCBblgkyCK)-k3fP18`}ilGyKwRiOk8_3I{eEH@v{J zpm8^AW>nDX=)0F+t~ogR^x4kxZz~1vgy_epayUH?+F2K?cWCnO z1GggdzUMbaACPB?kGm`Wjb&ed_*$o}=8x7Z)z*G_d;9vd`?9CgJbQXxJYIcKqcium z$J_h=AN=Q3%+c>;x%e<~&P>S*uKUUur$0Hx;gtXIYSxtXF_}AsO;4Qt!}Wp7>#I7S zoJ#elC3=sG>7`|27g$-A?_M)kq(RH;VW?L2QLY@$G#$5`jEvw##XsN0 zt~q(q?Z~MrpHsS$wYQGtC9r$BCObPn6q;)+Sy7UFilwb_p3S`5oL7IOU9fKEFW_RG z^E5SynfcQW(*uX9-#--TE>djS7C3W8!pq72Pn>cZF4o**j*~w9^c};0+Yh@l%okti zJ>*cMj0ehEwXt};Rz*tuE^Yjuv8HIhhvpX&%?lk{s@$uoqD%VMNC5DcP+#AKk zG-7r*1eyi4mTfz{bLVo?G8v|Qj50=#3XOl?DPHpZ4X10_v*hEKyjqVIKVNZo-`%c{ zS#m$3A56YjvoF2xC9B>wsRs)~G#;5wy1i|yc~o*z)2$;ei}(K*D`0%F&Gm;-0Ap>} z)$g~0!;}nd_UwE#$t!T~?8E!&_X}SCC1RzP>gP6p+eYad%cdxq*~my;w4CIVv@B?s z*yY9bwjKeiS6+X?a`C5K)%4QcPT|5O-l|!bFDqG@pSZL8ebAnYK)n`)l50=?zlzZN z-hLqR0e?fV?4@>Y*}m|V0efm@e){_Q%iH_sx9Qh3E}R~(Hp}be(M;ns6R(=z&9*C8 z3KDdt8@qk3R1HthF7Q_Edi+>t&+m0HJPTJ#oPH{FAiVMX;t;FK9|nJq_X)m#pYO8d zrqz^126lcgFD(rd;V)_6Z@7NZZAIMSw9q|nyIGD}&1`1x+@=|ISa<3QAHPRu&L`*8 zU0&MNcWuYj*2)J{AAHtaA7(6<JGvRG8JyRh5z{kZV>57m_e-RO&62T3*dk$BklX*w=O^9T>U}+&!|>W48_*)x z-NnmAk20$_$TNvo`>JrPSdgsu@%D`CzY}7;% z+`c|Lerr)No5Uh^-{iRpR^koqB5CWRj&eEOZ#_Ht*8X_CCWR7>#XtAh&pN-K|4;dW z|4eh--|yl+G%ex$dE>9<^@l(0EG{y<)sr~uNZ2M_-vy5%Vkq!(oCNWGv~!GzdW9Ej&sWknfF_zuW>Fao_XNx z_XXYoUAMlNe48`lkCfuhbpDC^>~;z?T{zhD?6dcQ|E)XbwVFK$)w=WZUHQi>)14A; zj~I0D^CYa>QN8Wr;;9>19Wyk~Gu?MsS(|3&{cUFYi`zSmzvw-GwYOSlzJ0&3{rxu| z>sVvh>X`1lcvu$TW*r&0tv9Q9kK67I&z`UJ^LcUTT5s&({>Is{Rh6p_rhl25`}nrm zkB4?54{n=Xd!!V%amUTNLyog+*Rh$Fw|v_me4{sLgaAlTZl3}bz!P_!X z_wIAH>3lC)F5YBIYd-j&aqG*>Ro~y8RKCvev5_%|o&Qv;Knq{`)Ym`vv;K2`F!$G^ zgs-bS1?F8_^7meUoyzjQTRti?RW>PEEf=^LDbS)Oar!A+!+i!j?jynK{+H^XaBM8u zv0H3?Sn|h5uBWE!C7S5>=`*fpeBb-*^WHtXqgb=p9_-G$Z9Mn3Yh_l~r>WY#T+zWV zY@X+xWm(VY%5(UuF8BRyfr{l;(Z$7A)YJoK-=7iX)_tx#bxFqUA1MsG8|HdxCN(!7 zF`5}5B3NUf-+#PwP3-q&dRE_K0_XS%%&pz>;?D(*^t0!JX3u(cQ}%jV2OrOc`bRB0 z=CUsI=GZjvKg$pO#{bMaTqYb;f4J_@<&>X)D!m>*E>gT@IkDT#FkEeW?Ta&@Q>EN0 z<$g$cz3s`n?e^_U=C86h99I|B>3VI?3$!R+CaPW#v&Hypy2E&xuaAYL6|6i&bD%Ohj^=f&q8StQ)jgT7yjQhk$eu;M_SzQh-~2hn2J-ssFr zJ-PV$0VdPpHxjkKznwfP(4v=UGCzFr&p)LP>e>DkmwY+K{4r8s-l?pT%+9CA=}(-t zRQkj}UEy)5nUBj+AnI_0-geoyMRkmaJC^#*S@8dY;vz-!RTiyGr>E-&?=4&``sjzr z2lJ< zrs-jhOPJ7-L7}F0r}n)p=_#DZVeB z=ihm5fwI}1AWylyY##EKhc3K$QN`b)z#)4o!w!^Ej!(zQ^hXZDJx*^CNe8#GGI~8n%7yt2a7RUtgE5WqalNTx|Cb zEz1{8llzM1Tu8U7p2~IYj?BMj{w_L<;uAA^%Qo{`bOfFBF!R=#eUvBC@Og&O&NxFy zW?d`(ueW?(i(IVi@_#yoDMU@JfV*hd!iCcE-5cK)&sW}Ay*-A<@r9Gy=gM-E`Tr$; zJkNOkdB2N1=ju~e1*3M|iilcdWT+^<$X8dCs?4 z8GBCAd__cew@&=N9#hk+U5^^gFX%n@z9iF76_+nCcR|kX<#Wz~ZjHOWy>Qwzsmy4x z0>&Lzo}P;kUy&f8w@?081#foY#9MD~Z}?gBZsmhGv8B-&+SUz!9aPLrTxt>&Ok7!B z&-iFDv7~D6zD@GFHs3|&Dp{Rp>W`Q;^>xs$x>&irY@JJj-u&NtUwPKqQ=k-=I`#9< z`ON=We%x>P9;w{#bFPcC_d%%EjnAKp6mPX8O03Q{IRY*le2u1^c5R&A5dQJ+!XNMM zewlCMSyZtjQt|uBN7j6&r|WP2^KSQA4Y>%0dkptv&OT3Vj!RxR_uRfuHnA2v+UK0R zdNZ=?`8iXqEZqf*P2ZLLO#O6mw(z`9Ho8{rJ9>9a<=VQt+|ca$`p5&O4_1|M=E^TG zW8Q7Zvxu4b$nW3g)%x?TCb}fqE;oLWbpLSj&X^_B-^HpQ&0dq488=Di!%8jpwrijTcf=>t>z0yIZHP_u;hDXS*KD{W7`X|FUjk z!RG5r+Y&4IU7pK1{Yg>sjZg@2-{N2w{-Ee|-Cz59ipY9@3mH zeX>}OF}%U^rO+)ewzoxcCPJLCE(UY??#?zz-grDS+jGlh%X{AD>w;aLD@9e*wcOfU zT`{%Grpedf$D{6?--pDb9tsGsC_d?V_W56iT|M`Y@Qml5>ly#NKcL_6`$clm!rP`| zhkB#eKCDYGWiO-peny-8Oz}?I<5qi@Z?AU8W@4xSVf7|`r6Hg)MhkbLutP172 zzCPHhe4Xgb%bO2mANala=N|2~NvRd;1sfk#YFy{7owy?`@$9)EoBDl$!fvd4J1>4U z)Ls0uCbneVLXmyB?PbM>)`o0IOP8Lx{vQ9krB8Nko38l4z-y`UOrDPm6_q}I$lRW0 zvu-lWwRhG@FK%gf#_cMct7LWkf~8ge9n1AWi9hdHR{+0T%7%rVH5*rSALoef6YP1s`dR79 zjWK>@y)2G5G}s&R7hEYco@-{b+;8H|{qsFebQ#yqI(0?lw0zx-F8Nf(tZsmv>0;FVSM-1!+NDq$GKD6oL`reX^UnB2hYB{r07EA z`@8eb-sQ|;?^_gNV`SivR3Ty9vdq(XqQb`F=T(b}Sx#I^W}E1@aa+{^i>mEkCrVy; z6P<7P=jZv;$v0R&9(3gU_wW7WTl?o%+y67%mwM7wutiR>K=TZfz0B#SbwWB0`+M{> zzGS~h-u_qCB#k55cB1}t*OEN(wuN)gwJ~~0f@f@bji#Mm+Hjsdxz1{1z5UI{$2;5i z-0Ke3*;Mn>=&5#iRAEptlkDokFqft7?d4m#uLjR+Prq$D@#UM))gjB4ZFv4n$}nYT zW8TM;8Fu%z-4A53w4SQH&G+`C`T0#7mMu4zUK{9^Zh3#&y9H~LLs??qd}C;H%t>fC zav>mtcXC?5lj`1d#jdiP^oz@G&0C<%(sHpTuJ>tC_=QTV{yAT@Vt2dcR?T{{F7}g) znWM_oq$kq%9V+FF*Y98V`67$s3(cnFrRELN$vf|Szi=~BpD~_kAA^ibdf?j3#vleS zPn&jyoTpE|B`*K0aEn3r(Md5qJ(h*emOM4wOZjI1{kz0}j>6vZe2u%w{Ldu!`M>;@ zD^~ZhJB))*^}+N5*^7VHc+NVkT#<3{X6U@@#(vLl{ju?WzyE$lj<=wNd%o!0)oka5 z_NBf&%emAkyyeR3s~eUrGyA7nD6Qy`H2 zZQ@~L!IC}467=ux@he&z%PIHEl=;{+yPqww_kZ@;^qtk4vna5v?)PmKH@6-CZ)Jz% z-JR5}l*1~-CJm|>q<8Se$e(`tPv{2Y9*!E;3Py_-u?3r7 z=cO)mG7}8!YYx_UFzxj0n`=H@V7;#2`|NX&?(V%j4C;Ye$;&Pu*`6C6>tbC~{Mm!0 z`0e*}w)sn!tM$!QvGQN!e5Zfi*Xc`NU;ikZzV)_yhJdkD>W`<>4U-Mi?o~9OdV9O# z>Z%xthbzxAKM`t~e}J);e~0RyFsv|_2G@>t{IL3QMQ{+ z4U!FcF1!5P*J#(Oz<)RF+-xDvog7Z?f=~9zv)2hP{pQNXesYe5W7YY20oThv>UM*# z5&vJexyks2(pP)-d+!!Xw75NcA@*RA$f?3zc5#Q}q81grlQ~r?J#+u91EvoiE9_bq zzTtxFH=ESkf%i9+SaSx(Kf0E6<=x%eYpVCU75oa`)wIo+XAaY&i8<4r^vNzEvcLf#AIjP??# zw{6qrIyz<;wq>4r)~CQxYE#1Z^0C-v=7Y0dbE(xiL%ag_*3bXB z{obMVS7*KbEK|aAsQ=&1!So1YY z+wA5$#(MS|m7~n^r#GW$N z$=yC5OkJmQUwUo!XYIRq-o=UGUMh3NE?U~0=oM(W_e`(gnc?SEi4Q^(u5=t-1iHsX z&fagydi^8JOUo?v_xw!LW_hmlf$_uSi#2h5g?5!)pBzfU`kLSGiv689H>mT_&f?y> zYTmb9wzr$#9$}ou;J#CDUTUj%Nf_7mu;|q*Za*$)-uGqJn!wwB_8(-tQqP8TJvlem z<*-fM!pEjUx3{fTw>o}tQL)X7_}%j|+V|eqej&U3O;mm4uDZFGe(dNt=RfhFc)UvO z4}+y23#Xl%llb#Z%K7T$*uvZoKf1a1j@n^X}lX zM~jlb$2_h4F-=q}dDeRi3vTY&xx23>%(YUrb^I-H`stYj--?EnSMI{r0&sP>{`&p$ z_D*4AsXWQc9WEW;z9&l@i+Z+5!$4$BX7}C#d28R2d5;W_vfOoQJoQ8+Zmrm>tZ8%o zauOO2ipMX>zi$`B&R6jD>-CjBzJlzJE@~KFzVgULc%9`!^%dOb>P=>yKY!}!i?^=4 z+LBXJ!#;`DZ}pnc$C@0b@*_%o#^sFXC3kJ-x^`b%9ee3({qH01a<5*$`oB*5%9UTg z&#wEmCfd-=J-oJ;{rdL0y~++(BCbx?UMeT3*5lEVVJ7{HbAf`5uFdI1uPnZF-j2`E z-`;4?`tSU4i;C^c_Zi|09u;nW{~>Xcd%qBGp`p*yT~pN6XI4M%4g0f$>4VvaUm14! zo|A71m?bP0IaL}MS3Zxee06BobiIeuym)J^3)n0A@2v@5{QN`PLxGNSiCOCV9tYg6 zN%68hzf&dFA-XfTKeNZBgO~4>O}gEV;0* ziT&@sn7#L6r*952DUkXhsAR$IpD zz2|n--AdlFGfVG6yV#!JP1hMVNY7nZCNuZ!+_l{2vaU+^fB7mXFE{O&))$q1&ja_A zb}!*L=k$ApJo9?)bEjYTbQhLPy#Jn|d84fL3A@icCw~b(n2@Bezxl(v-9NV7?Rs6p z`y)ERY1Y|g>t^$6Jgs!w-gPc+>#D`3QpGRA8a^LbD%YFz;liUeYir^wr=|W#6q;+> zf9uMXw0CRS&n);n$8~pxe(k{>ThIEa^!zx{ajuxJ`eV*zm02e=xY;trt9d&YJiSTE9Z#z8Zd(b?~chtiilN z-ZnALROrj&7DR=gzwb45DKN7Y3Uh6ZgXMWFew7<3Y{9WA( zeS!B2zw9;Gl|CbIU;o}ME}J~uo2 zbMOJ)B^PRUpJdW!&@`U9DF1z&lA_(r&lg>m)+dB%heCXYNP=57+s~cn9NqvZYAkR?GP;=3MhxxFAnT=1;_q?4M zx%2Nz&7XIHE4m^5cEwTli8a9o>f7wjCU1N+L1_7u4<;WtSq@&T`L|J8CV6KJ(+{2x zz6!#!l8V3IY*t}oKm7aUltpIt=l{pA@EA`zIpNmU)ya?kobhP+@xkERpFdHHirHk#Q@b`}WUek%rxifBuQxY2?%)_Rh`V|3BXcD{fv-U(zqX{Qm>TGXlpC?RaX` zot%6{O?`H$rFO~Mj`yqv(AUVU;BE?Rr0o;x^!ILeAmuEmSYXk zws*F@T2vgWxpYcyyIPxTrCi>^hVa$7&o+xRo@U{zUzn@Xklq;UW|DQp-OYoCuk1|Q z^u$@aF8o@2l!doMcK%$Om=F81Umgj4WKzIaaWNu2aH&oI&hmSL7k9??YRz2o{{Z8q z-Q}C#9u?QTzt{WWt5B}=a}Um)YkGe?LU4h5<9%*5%WVKe!t)R zIr_emrIAv9_H2V>!%2q^tFW<4T9onp_Ksg-J zmJ4g-v(wUy^~%KePArH%_IX=htYCup2c0&-{w0@}e0zJCr;xYFJ^eP@#C`j&e*S#* z$i(&+eC- zn?&6^%Qs0AZ{3lZy1RVkoqfK!MMC9yd`~S_yS`X_V0A|OA*HB}eS9%er=PA{uz6Y@ z6YI%VF&4)gMvRZ64`ep{cz0K&y?ytp1p&+ZHXrM1xY8~*ul1YmK2}8?m5Y8G!wt{w zyK#b}`MjO?n*H{B_$Ry$nmH@!;otAk4~z5`bf2Xr4ic)V-l)8gkh z{=MGsm-M@=GROJLhpVhFuJp(8W8s58K4E5f8 zz_CPbOShfe_J%TjP*CVGPH*5$V2gFUJ~4yOQJ`g75z`4qX9+fD{k<+p86NArb|1gi zF59iS<-(T}_LH?$Irbi29&_&Z*?o4(#h+AW*8V>9wrE}0%)dX+2VPxvnJZ>X)dP~dWi?N=7wS#9xO;U9v$5FWo}(R0FY~)(e4iS= zfxA^y{mWeI8<#Kd;O5piZWy4&#>2XPv7_~d3#>I_P2UVzeR`i3l{axZ#`fs3FfU^L zVZex_*+*Bi<&GA+Cy!S9l8!@oV~ z;P*Z2G-Mt`ihX{+clq7hvzN2gG>K^(eX_*7lIse$U)bth_uN%3eX~0n8Fw$EKB#!q zt+>12KeuYutFyCz998`7Zh>O@-B z#cz4fR>yuL``cH^nd$Qs_on-=m|uH-(~RFw^*_!ox3qpyWl-NMGdcg)wgs#;-aqY5 zZ7Y4_$+1Xymg7#nr_tLtJby0T$^EhU>yv1Qz+2awOTRNc(75hfbv5hu=KZffYVLcS z^K9Qa#_|chEoDNk;`?oLW-ODGmRy*hvgGlzwQZR~_xH&r=N9)hmRa&EEGk~{lVk43 z-#6a=dNfsAq)$e1XZ3cS4OY2=xw9met(<#dCeO-=f>$;s|2Q~Nc6rDmGkgEsvT3ib zh+O+(a_){y-O{F_IaMa^BC)bxEg_6|?i7dhk^+7}WYP%k=Zj9Z1lk;mV_CKISQD03;ig4@VPDXnDFj$;~!^?j|8o}z}0%{`^vLt0(MkvytpWD_46Ob z2lyL{gT62Q8QpC5{z`z?&uzIS&WeurKS$Wr%Y|mIXq(>N_xq2)Nv`->Uol4U`{DCa zC3R1vr3;bxMxtY7BhMJd@GCsU6-eyyq}k)eD- zLBR2KP^`Cl_qppAU7z&XZTqf0J<_Ur+QIX6VVmaeW;krpsUqYe#QC=E-d<^YjjV@} zVsUHRg7qGYbl>pyzR}%%Rs z<3Gj?r+t>5`IkOlb!&0@3UjX7+iCyimVY?)G_5{FbnfQgj}!RxP1>E@Ju5U?-|w0m z?-8JRC#dsMuk`G~gX{LmrIs)r)Lbe5g+V_;|Mm~NxV6{*l`Rta_DCrwrlhPv+9CAX zyIk4*d<^>nlp8oy<*7W(<+nfu+BdwP!_VJ+vLt79eJ#CbHl z(rN0qPhVe8Sr;pOai_E)&lwLEV-3F_2|XWcUkNY%^}10bR_mcicSLb<)RcL1kItNb zTz~3^(u>T(OV5P9&C;E|MSRbk2dV7Bo1zR3u63_C!}Oi0u!>RFV|sIaFrV5n)38Mj zHFA8D>OZ7Q{%PTx=TP!$qhfOJf3xQGZtb;v%zG!#M6lNNZrI^XbLsv;+0^Cz2k`@cyw>+f+;nL5dRr@Pn#hKU|2XO+TFeGie~rw|RV<;6um5k8s>$yZU3fe0>+guK)ps8}UGwYr)kkF!M{<&{h8ItDNV0o) zU-*x-@rymL`Mz01nRv6*#;SiUzZlAWyeh8pZ*l1?hT!_8*DC&RP>QIU-w>G%*<-4@qx&B3RcV^>gtido(ON- z`>D(3;vwhCi~PEKraEc1ezKYJ-u}^~GHubnTGI7WUi7A-(_n z=L5EK>)I7_9%`{z@a<_xR9Vc-eCxv`k1b1iE2na$#m5&*Z)1=SJ6>;MZ`W34 zrM~O$v9e!Bc5v+B{Bz7L@7k02zoN|gdlr;Svn0;#J7~}&y8MfkUgnfbc`tvR{`|q# zU)+*M$>xri>+YqmnM!x|gs;wQ+!b_iec0=tmjpWQXoT9c<=d5rc$}Q$_To?aYVCXF zd+QB)&i(Q^_}f%Tu*ZeTc%s6k`~D`A6(@NK`Y!L}=M|KfQQ28*{V``+hL!Hlv$AcH z-4pL^ZU=4W59^&CF=hFOjH|z!r?R%hq%72B(R+UQh0b$9YS@`3#D;rwMF0$81 zSM+(POtYz&(D?Yc^W3K~`Y*QC{4|Q&y0RjM{fESl=miRVSEfB);2JhRLH6>>Hi_*) z3k$wxExssa_G`*l-IV0V6PM4dVp$TjFMjn}i@CQu(j%qssuW(V{2=<$e%m~SsATpJ zmI)Vnj`yDV`1nY(&6BnAvqYXoZ})h2@9vZrIiOo?8GBrkbg%E{WYv3dWTrucFNJMK9?>%Y8Q2xFXX%Hk<{R|Nj- zX1~8F>J8tKXICGUuUH=&yMiZ9dgr-aulbUKnKs^vcPE=s$E~wwa$A<&FM?W zG;huDOqbYkbyeJVyUKrYta(b2AKu33?cp( zi~s(e@tXU3s+{(!_}S7Z<1W%jN!x_;C4{?(|5ja=!HuInRzq z9MEsP9-(uUvg%EFzq#NU&$~lT%J<(fKR%%DvE^9R+a+!f zO2WRrd2ePlck27szT4HGdtG1bs3vf6XKYG?KJ#y$!yQZdM_${LZ)~abdQ(kM%!}gbk;El)p&U^OyNE<$2(alFmy%ez}q&0;pY8(o;^R3+;_}*e*D-9UmugrYM&dW4}5RzyjT+_R_@Qv zzhdE(^)l|J*!$AX-kxIq4g?*oyE9yX<2|?rFA5 z)m_ueE5Z^5DZ}G|Vk#YCVupPG9)v+K)BgZzdgz=!V!!h zt5E^cH#JH_pdv|oiuiduhuiGTgo)@#yp0RA;?I{1u`)(Q6?e~n+ z6CNji4X*!eextkl(+tqPS4|2Wy!tt;3w={A>OH^gqO|dh@%an&9~355?6|)E+$W>% z^z*NB?jGy+*Jrk8JlFf|^Q6PSN-lnsdLSsG@qSB1?^EyT8F zOE_-)u*=(et41wPct!8=12+z+SV_6uaB9lv4SZ0!K!0C4|H|6m9bdj~eQP^aU9I5R zvjDG$x7SKWJ(_u@%2jaj0gemT&wk%IJMComi>4-NyQrr+`o9ZC{(%n$m z6XB!RbfNF)E=iqh`*~6pH#euuuQmIkmp&!w#j0c{g+-Jbkk>9(dj~O)w$Y# zQ?d@k9yxlCkF8Ok@wXw5aqT0P+S}W=uWe9pc=|4O&Q437!wako8^d$$c4|)YNN`)6 zo!jKn__pK49mzFX8Wrir_qW$PWNQ8V{Ar;$3#il(FzXjtCU)zvz=!Aqp2|Xq2gbFfCho;um`%$1VUKPyEQ>=Qep!{xtox-)mtx=*#TvmccDeaZcKj5llY zE$E3}>D9p1AY4zzf6E4x&pxWSjmm+x5uy^ri2APX6@~jdi&&Y4?t&Hr#ty zQWhURoWducm(A$7KWwp)ZLjnaMUCsZo^`b*lMnhVz4l96c<#$N@1D)Rllz=epHY5e zW5jc-Q>8oqf1V#ach)5LI03NZZy22VZvXbjWF?nF!Rb2FyBBcl*K(e4+n%5PU}n12 zp{ILF{@Gl8eSNl|ZtVl658pdZSDt<+W3~Fan!wz@d_r@%>RV`EaadO=QW!Q<0f$ z(u+F!RtDrPy>#zS{^cVEJ-r?Go-WXzIAh*g+2cV6CCX$vmi5W!=QBEPIel~9JU7<`Fx)8*7E!pvFGwu9{F^2^^M}UQ?Lx6m!@ zGHXhlCB1g%1{RSgSB^|87OG&qk@xNUWXrOi9|s(w&Hc2$ze~KozdunzV`)I&HJAYv|cT34*_G;N5DZ%$2{yww!GoSJPAFl3Uk43>DE^w>i{rb3o?iV^X zrA<1|!yEJ&{%)LWEoo6Z>&StIvahW(mUfG;ytn6OPqEKJ|84tP&-ICJ>l1Q(maCp> zC4T()%DX!+zt}$gA}h;#55|MM!4A9U)~=7b+Ir)biq-GF#jc0dzjt$=Sav9^KiTua z^~UoHLKeM$5qRnMcj4S*M#uZxR-QRybE3|ihq<~;Z2h&GE9EAW6))Z2e~-lu|D8^(6vKoyA1c_X+aO$Rp?da*3p9KZI_&9d8b&iU=^39m_d>&`#J*}aJ)`nX`pmScLx zbC>;p;yLN|_U%`n#_TQUug-e4a8BIzsH;1l_zHQd$ce9?=wLl}_cpE4>z(Hg>OHy7 zG~N97-fw?wiu3pD=B2~Zjm?`9v5S!vSuA?b^%s7g^ZRl2w+Xg|O%Z0dw;l49PW^dG zRO|XW)7M4&y2>oQ_r1Fo%$%jMwC4Crj^)KKuj!t8{J2+4;LC-Z53jp@uGBQxUbyPT zS;h0~!%7YJmmSNt*%@DQaWwY-D-A(1vHuEQ;S|Q7q7mA68|KdJ;U{^W+a@NxONq_$yVPn58 zJooX7q^6U3dmqMXg7#h6eR!XF;O!cl{+ILrO*zVMKjobRH1)J>GoE(Z^@8M#c$by` zj{N(Z(l4)m@4o!6?{O0H%WIx`Z7$^5-eBICnaP{GkZFn`&;9jRKRDGH?XWU+{rRi( zPuAAJi$B#q7)I5Inf0}b6~;cbxb^amUihD5bI*x>S=RO8%khm8x1Rnn^7^XEC%fqX zhs30rKGFBXE0dZ|U0vOO#q~q%c2n12US?&v_kn$o#G|m-W=qqoVI!I28Y6$KaPdV*j6&V zXL|1bxl*wGmNV;Hv+uR1FR|WodQ+lzcc=5uTU*_3$B61$#PgrYIdrVBx$Mz{${*YA zn%N%ha4{?TZ@VQxZ@K!mHs=H34a{yvD`U^?F57zbDNn%M`OD2Le->05^6W_xJfG#e z%WsR^#*Z3;TP|B(UTeNL+x2-u!w>ES3r{tCyCY+~e!t(>cd;*}U{229ZnS>mec*Y3 z)?AyK8E-ZwA2EIyzgR5q-jbT1MVsxP>3ul;VD$yLtc-^@!{>=-B~Odi3Y&Tx4yUIT(n`|(MElSc!!lCpB^|G zwet&mFTee3--GQQ;@tBG?rCFH z_RZs^&eH1_+Vp?gTOE>r?@or|PXEn3H8)n&DKG>$aG$TZsI9U%pnhGT0Ee|0X|A+3UaN z)eCy#8R{IE^PX2Z@A-Yb{_G|7`5yU-pvHES0>`1SBBr-R_wttoeVK11%2j@2@`kkZ zNV|Hu?5Zg7FIhb7x(xfwXPrNN@$$D%4kh1mqtm~(1(dA2x7+>YDbYX2lx5$sYk{zy3!)jcS;HvqBrfHZ{^;we&H2Hu+@G|R-WFcMHln-{EXEHl^S{V+|dv6B{r6u*lnaCu8?)Ry&5g({J;- zgf?*esN32VzINNvwK)Zk7mDp^S$>@J!EBAv18?46Utap{&B2V#r*F=hzr5ttS^wUc(c+O03aC!Bk5VWxBHN*7c%B-$K>hn#MEtv1H-*cJW-tz77V%PNW zO1XWHTlC@<+t~U(eB3WxYFT|iwdA0eros&w(FDfB1%IM=*N0{IzmVz7S;Ak`H2p)# zv5kyd4*%qqaNG0#gy64!S#DQfrtHFrmzH|_ztR5_wN};dc}Bea$A=HUx;)po^>WUe zV`T~70%p%=_@`^Ye&K|`N}K+to6~px{`)?7_RNWKF0cw^`9aPHp;KbNt6RHl?#H+C^W`(G8oG~UZ<(_(MvrSx zLz!iMcW=)G+fpr=<)tsr?Y_T* z!>)bZ-xBwCCEgzzm;wSpTgD%4_b#b(S}(dMJUTD(^iA#>>56L}DrdfQ3a9kTD$mNO zpKFty30mT+k^Z=vH*RUu?-@-3y{8pxY~+ktxKMEJ)I*>G;bYQbJ}>dPk8R9moxlD>%-ny2V<5{ZmPN(dTpM$z z@mkH@`#^O|N5bPTsSm6VB-Ff*+jQQ>xhP}BP2Q8&N|IWcPRrMBc^E9%miR!F%i{ED z-I>OAZ6*d+rUpNis};**{x7xT_8b0By4^eD|4q96|K0t-oyxNpLrRmqO^LHsiwi1Q zai7(@Hu*sJf#-o*Gc5`l{w;RzTzWV*+M`l#>PpRN_WyQ>>OJ4yP<%UW=T zR|Ri%X6K=$-aD4I@f(Ua^b4fduGU;wJU2XMQS=v@GqXI_WnI?=mfwx{9ajELQ*(X= z9yls(IbHd2aeslBSk%L!f7OMS?PBZ&PQvRKih%AeId)7cx11~1;e)LMm+97NLRQ<3 zd|w@U$1C+Tx4+cH6O*-md+l+1Ey)tc8mB7csm3pt^5JHB=lAx-cJpF&Hhp=h#>~&d zyggunm2Gcy_B(?X$!=S}5X{Fky;M)=?3K@J<9PHB|nIs7%-^iEObX>(1p2PJH8%cfQI zUh7E=d=UA-p0&)qz5QkVJIhPe-`kQNU)in$T0675di%onYkmELXV1=l)zbd)$2SS* zbgSdL*XDltrSsG1!|@k8das$J-G271eE;b`*6+8deRqYG;(6)(A4;cKr7R1&^>k0Q zd78U9MvGKNie9 z+h_Tpc1gp7%URmS4UeP6{8nyxTBsv^rX=ofXhvV`fqIrb8Al#Zys|NwXRe*E$yBc6 z5urMpe%!0>-RgY*_^$H)M`vgIKAkMRtW&ak?f2_}TVBk0v90amnG?`+$zTJ8JG&8&B? zT;%iGpg+a3oJq`z`DS?I3!m98>FcAGR=kzk+S%z=-@+TiSZ6xx{At0b*Mi08>Zab_ zu~g_^SbBr>F*h?6AD5E6qY~4c#QFc^@+2Eg_D|w`tzr>0DW} z=i#;|db%$si~8nlm~~NOE z&GS1xr`NZIPj=b?$*fHZYfj36niMyDmU_tBdR5)GJ2N5O@;3O!(|6ChjMjckZt(6n zy;FF@1;6z8%D8B5POGOz-tsjUd{UeRf3WD2IoIEH z?MJdrJOh5b6Zp{Ikep_fo8mV=?ZJycrH=(Ft~;)3wSIm+JF|J;CY@AY|72Y~vper4 zE^1u-YChSw#PDE9z=Vz269lXhOy=+B{Ucbgb*laK?K)!iKN__EKZ;K{d*%daJsLRu zBz|qO<=n%urz>&Equu3`ZA-OGTBl!&eQv?`w3G8~QQoH9vp0{oc3h!5+WWCJiPNc>moo?RX($Fzfv8jEg_@Iaht!CRANGvD5y4 zvD*Jn`Wv4;d(s3QS$4|TzcKm1=L6FlQgWXAvH8ta5-Xa%xhQs3aIlDeOvbC@3pUTG zXPm>C^_JtIoAum-;p@87BctD!e5-x)G_^}y-|X_cXAR{Izl*%*^EJ+hZ}~reuQ~rD z<mk*}6-yOIN>JwdR%8f`$GI7AA^GBrMn{DfYoodUI@!L|OTaty?3@ z(s+~4v_&3^NaJAJ^>U&3io9LYuimWN{5mAGHBYltTXpFYpQoY|3+3Pa`_}(lacRgY zZ6#yTiN9*k98#Kks;8r*{@=X)?|<)m->(w&Sd`1+=buyC#FswnzJ6gw1@Fu1Eb09s zYErMn8{5}pm|WOZtRB0&OfV<2CnD)><&@>iJl@{t-@0{gR{cl4374O4TBkDEboaMV z1CJfc)3=qaG^jkMWVQa&IYZt*?g{scO6>fse2pe1e0e%uv#;+IBXr=nW%qQ`1L7-o z&Doy6Joj*0+xen(Pd@vo3hmgrYZB+F1BDN&J5KM6G`M}{;Kjb?aE&!PUc3?ETEZuP z?CkT`ndRyRN|ThAmqeaS?&7qH+vs6A*>Qcs!;`EQbFcQaZ>X?pIL{n^WYaAtX|oHT zZzQ`^SHJF!i89&~)Ols1^4BY}8p%o(_t`GpDlEA4a#r_>RSpdW`TBS2de(QHyW8<% z&nrvLKaKzQ=i7=_-T)VI5|-7l@G`TvYj1C#t@E6%+|v4}(&YF18Tq*ctqf~ZdJd<4DM2`if&;54E*Gx!=j()sj&gs-;{j!UV zOhdC79mN;3@9Wg;vTS{xsXyO5&~WRnZ|V9fD;f+gG^rGY@hb)$4AliFSNid76pz&UH-QYACPai;7u*Sz-dm1|;Rnyy*=v|0K0_v^xNaf5Y7 zlRf(0dj%Y{aM^sTaD|WmY^EITD`_DL2H|yj5);oeP0?S%#l4tUc=bo&h%lW!Wouu_ zN+|3~I~&7yPxReNpL)Zu7u~<^cwWOV z4$-^YdcXP3-q3Wg?casGl}DcZe*bvdt5rMW&KA|N7FwE1&$HM6w&nT5sa|Qfe{pqm z#4UCDbw#sFS^4V@F?WMjZDXOe8;xSw)Gk|gzs@qByKejY2oW#MH3b!R(!~rHkBFVo z-6%DidE<<$A8#Zd*_Nkz!Qt`r=N3Nl_Iba$Z;7q6={fPoY{DUzqdg3wu`2^SmuGjq z37F=(WV^WNtk7V-U*FjmUfm*~Bz{AN=g4Ev2j3gT4Yf;8w@p4-@b>F<$;ywem(=~P zOcjQ-F%>wR4*T6IXPVD?t7-1sl6QB#cWmCgF!tQ7!V~ekT)usIS*0Xuk~aI(>jR#J zmgcfj%KJnvMpZxkbSlLvJhofDt|MIU`Y)~2p5%|oOBx<{J^Xe3_m*!qZ+}JZ(03O3};Koz`JIRd^ zKBe#OdM{bMx^R<0zIo^RkP~w(oxdIFoV88l{EQRJ8_XMw9~I?!nWSx8z>yKVa)E9B z&BGJ7J~o)@@!+c5*?>~rea(eBT-pu#@17g-1-ei73R$-N^-E{5R#BcypMN~wa=+d_ z>zk`>=kNKIIM^_J9&nm>g}x3>#(m`~HZt}8O*&yVCiHD_P?>KU-?Iof+P zP<*ogyo05->XQ$y4as`-Tjb1i<9lo_k*<6-I}eE6%amiN6TfksOH}T|snoXl)qTPB zKiZeDv-_&QJ^6Pgc#y40!9>yU!SP00EfKCQIrsg3Ut1fo@Yp)ZCt}?`bw3hq7s*RZ z|6KS$Rps=~$dzJ3I@xQtmd&YJ6ejkqZJl>r#f&>Ui?@ogTy_vkmXnt1I38=+tL-Hz zaqNgtfS+2S_SC6`*4nNcwod)$Uq36M{QKKS@<(3oVfYi#8~0#(gL;UDNZ#F@li#(= zDc$kdC{50IhWVerkLYPp_Y_*wvOM(jxwG1D<9Wuppy;#vp>Xlt zyS6gR?Bj~n|7>=Ce*U`D(w|YEIovwJP>Ae{t3J$-ot=*D$&n9|FV{3}ld3TW?o)1oITorxu zI@s3V&NI9Ie~h7EIm`Bll%)H1cYgiq)~>5J-27k#yAN~bmT!Av_?8E(SouD9n(KaISdc{TFfU|8xs-T&rJ~%)0XMaL=AELW|e$aeDXYsOarkO3*Cl z6uX7lp7BlC!hi)|?^So^-n{ff?|IP49dllvE-Ew7dwyNu;rf{uL%Opv*RBkiR&KY- zbm>V?qvigKv&~~g4UTPeT+;AB_N7yUw}V&pyzrT`4n>|!YLb|^KKZ)dnrYMg|A@Qs zbDzlEW&1DbW%2=g#%&R6*EYWMukGr4d)s&7F}GK9Z2G^vyey(0zixT_DhBq3<{G0e zb#9*pTVIQITl}oa)nxG!eD`98_XhrCC9@+hYqmdn-oN*RtiU`*uIc*5pDuaF{OI2Q z;M4Z~zIhoQO6IUEw^9Aw?#A;Au9QwMt|;3cob2=H@7hy4H>YR!nlUJc_%YpQKi7GB z=gXB0st(mtyKXk__F-C77CWh(KiE?(yS9H-_Q&c2TSNr*C|As2%9&g-@A(qYsgGTv z!Yq%=`rh90T(I}A@PAN*ndWIG*nSXwP$#hS94=Sc|P4!`0A69sKQ(Cv`N~^ia9hZN;JC@&Y|0y5m z`3kwC7Xy#QRrea#e{t{G^Py>u2zW4x#WBMts!^Y5cZh;O&drU@-_FjKUdM1*UG3vf zo5%0>MV}8#HF$T)@yOFXQ7afMw|v|3_=?^xodtiVE?edyYu6L5bM2Rws=~pyvE7Hb zzN*Z~n46vPcGpdvrzKUXzQtakiGUkdORX3B8_i8$&ifOskR;JLY5;CuA$I?%|A?o9f=zebaK{;m;+OQ;mK; zF@0ur-cdmK_|~kX_$N%O*9))ZI{%;Ths5UECw&i6jADMCE4TQrUVmt5eogbq+TR7A zlhnZdOGkkgYY*-Zl?mIqSekUd-}C-GO_z6i`_Imsr5jH?ahHGiw5ZfKut8bkp}w|n zV(5dUn1*@ZY#x7!X1jP(S8S%7%#qc#cCV9q66F(%=C)e9vs&<{e6)#OCnkJFdpBP~ z>*4KgP9DoX=YB{weRudsmdz5Ug&xNbFwEUuE}WISHuOrY-Ilah7Zka~^{+`Ymr7NX z_igk_UX*kxD*L?W(t^`ZkLvHzOnqdU_A_3grQ_TopPGk1Ss#=)l<%0#%4O5PX+Lk~ zm!;Ep{5r(Fgq{8M%5U1RLL+x_`RVz4>cw7cii!$(es8bu!y>==R<8U0dHxakAXMPE zX-+t=+_9HAk4&|us+L=2uRS@(()r`7t5LV(uWtRpW?*DIJ9I~K?lYwc*XAuheNrWF z<(5T9Spu`R@g#9S)(I_|weB+aAEOV8Bf{3CKKHAVtL~Y0?_QhCcCX2kRrqBNcrJY< zc*MA_zOb%O)>3NnOPk(n^Cq9XvaMo~$ii=BSKjn}RJiceemz%Hk?+<1{kc!(w&ea7 z`|w-B{IKlZ@&GCM^fzCxd&*fjY}r4*&$c?^^Gko>ob1WMYEbW;_GeMHcz<-0(?wb9 zjQhK`KHNM<`|@RMtfT=00Ws2jiV^3DE-wkmbMFb<@v3e)8G$YS8AUgFD|bGtE%y zTYXGHXVGUNV}btp4FAL*=o@M)@*aJ{E?@AaynZ^U%=FpwTkq%T>8f?kFwagi_^|i@ zb3~ZMu3GDjYoj?`lz9IxoC~fOk3Eg~t#Xf%FR{xgb&2L0Et3gsr!soO_4G3ttG9uug@7To{=E7)^>eJ)U=3cKUbs}y$E`FtxWVFN6n({nHnqN zS?4JTWc1p8ld$EtP2n;yOIXuZCNrs>FK|!E%6Y6kS@j>SmVJ0pBl2hd>K8$r>eHiZ z7T(QT_W8}<*-S}m79ADnydE*H@2k_HZU1=-M1Pbfcss`~Sm#|;FyYGsg^By@qPFKK zZp#+Wy}eEF(}WXoyEwKMH1Cp~(4I!!5d z_Y3pBlSOhu(?73rPM&ilb?w$~r&700SyWt>(ebC1`$f^tYv00;9LeJ0JMvi7GM3l* zWWM>lbs_IO6dl)oUF)=+Y0({xTV>aGJrdq_m;H~@2i7UCvi82WtD0ZYH0@vA8aCO@ zMTZaj@bjG$3)~oR>OlK}kBt?3b{v>5&)#*b)LMIHkLI#n9#`%hWzpU5>lWtydy?qf z)FcHtf2RM8Kkge&E#CV|<8Hv-|355Fx-?A`k4t#+^ZCwiuU7lW+jd1)FHL~@bH{AX z#F%sZi$hE{m1=Lyz0I+0i^-REJ%9hnHZ?o$HTmcrd-})hQBhoP#+kB`$0d4G&oCVo z;^>H5>GJ7S9*Dd=6drnuz_=P$L9^5fEcG^mYr*nQN-Or5*<-Tw@Hs_enjWX#E zmk*q0eC?clebv0>^Ox0LUe>nO{=rl)A6dJY^`|8ta2PDG{Yr#^JlMS5}Z3-u)g%``o{l~!MvNVtVj*izI=HJpM2Vvr_*=-2(Ldn z^Z%dulK-BWd(OA%oP7G^NfAiPrKNQv?+>O8*SOk4k2CWN#BN@uxLxDCO6ebq)`bhJ zHqAMhV!59yMr8WuRjU1k>$0{794MZnY8aNPAE#n&(h|Pv=7JbTp&3lmSS@{dTf5^M zf_Cf0E#35lRpYtyUAs#QWN&b7x!0?%nt0ps4AaZ?4A#sWBR2e=nf~I=&dCYckL4!4 zG*0)Bx9ysK{`%{pe-aiJ0{!}KAEb`D@VxZWO6}s*+oY?#^x5w_xend?vsb;GSkarB zrXcsd!CZKoZI`CekIfF{i|gXv?g|Og)LJRBbD<)r6_eo3ez)J&?LPls0o~bVCp{r+ z*;e?;IVoCl99ld1hd(nvj~Ibl%C2n}Yq8ddOM^`A^f6NPMZVOd~id z>hP*Lw_mm7rZ+N0Wt-18XIgZvbZh?l7`4hT8c!!q+_`P;`Qs8FqzaCn7Zj6X=ie;w z;8A6{%H^L`TP~i>-O5_Q^5U^x@$uhH&mJGJX9jg@w>&KdHT=|^ zKYd6%6~6AuE>vH#z79{cYy!JRBWA0}#DzHIvR2Bdmf z!!>16vcj1Z*$>VKqC~k?-nh5-*i)IDob~>%ZWwkQKKwN=RE|B4?OgZi%FxNjc7)lk zkJzJVb=?uvPx|?J$I+y<`VW(2&i$SkG2QLRVT&iSZ!32!y);Q|LCl4K+Un)S44WN( z6dc<2-fa8lyq*Vtlr^qi{C7h63m?ygOV_V&*vh`;`9zz#Id4u*?$&=$ZY9C;kfdHY}gfy$oOKLhtWn(|If znY8hGe7pI5l{;5mUoZ1@xxQ9;S0eMoe|3vqUo-s@#skf)5qvezZyY|r?2uh?nC;f~ zoXj6O&%LaCe_heknmqaIt~vY(T|0cevm>rP`RJN%KL5Dx^ovo!*{3f~%xwcnd%r~gr*liHWcWu=DdB5S=5GqeMC&*<5|xu)vR7juD#>tbMgA$121={thvQg z!}`NB!T0q%+clN1G;f*b9LRcbH~Q$sz)9@czHJAThlppv&^TEHC-1slAi`GU7fy&|J2hT55C8zu@yZZah z<|EtkR0Vp?-UWneO|&WN`Ezsg*JD{0Gg#aDSLm{7z5i}3x<2B_$>-wlcYWiIK3A-N z@tBt9GJ%Uf`5vraCgXAA+x{at%)d$#!Y`X?onLHX=a*SC=gR|yFcqg2i<=#?wGVDH zyY+tW^c(A9TdJ!$%geo)q4jH+^4@ax(W@+5M$X&sSnAk*q;9z<7`6Z2tB>65ZBjMc ztI{$$t{iN(xP1BRvHBk7e1_*)cSHk^=xh=zUD`DH?5vK3u5U7eyk^GA79Y>vqOfRl zfPUSbWB;24=lxgxaCipSD&O94tw5>$6A#aqlQD^p5SzHq?$);4#kIf77QMbEczdh2 z6tvT_LFxw29cCA&>}vuq-{!uNt&f^)RoL|CLcrcbKTLa0|D3a9&U>z|88MeP=}ldc zVpP%RtZNjISTg0!&SHz5F}6;ZSRd5-H0m!bG-TCqtbV%bQeSX6vOKO#u9l`y$O6r9_Jm}rrt2;n~l~@op*0#zibW`otu9A(2lvTC1S6R$M@EnCf;s7 zV9#F1xTF7S#FLAkHDY(Wy}V_*?Au%8>(L4hD|XNJsJGXBeO>t7%Y~_j*_`IiT@ldX z;LY8b5u#+p0?ySfZt1ocy03+$+_iGr7;#uEg-OHLccNwWwG_+i?~2yth5lnGw7jo7 zr7=*U{q;Icf8U29-7UAopU=It&imC9Pp<_oEprGh(G!~* zzH&k1)Jaxr12{}~FH1-iA1;wK$90|W1;%QD9?`(3T+)uekE<%)&MLG_H~K7kbym0bf!+T$U%2t)Z~Jpmu4&PF z>sMU16zLXx^J0OKsp;+NvLvNRPBU7khB_YFw!3jYQyuf#`5Yx>+e&Od^JL3a_q>>8 z%DdNqOYO659~8~xCraoV(< zk4%~FXsF!y((&R>1oIB&Ju^4FRX*^a;g8yf1xDr@R!7y$>nxLb+`(4l>}adG2}odvqx0oB%8B8RxxI7>@C4?|15FsAeM3!E0VmGz4rKMrbe zp88R-SMNE?qoRG%I~ih}R<@r~R$h4NiORiWy(~%-ThD&C>3?aQeqz2=X#b1QZ@2F+ zY^>O$uzbnGlZ=^DKJ5uQ`Q*W?sm3DiyLNMa|32BizHZw-OQs_RQa4u#%oX0Ym+eR9 zgX_6LUcH}3guSe1{U`U6t0UMSqS$mpGg+{iz`I=KbWMM-}mCu>i#`NZe{ZE6LNNYL?X|HPTxH3a(#HAq(x_t z{UephwymqfSH8KiG2!A)(D{3BB7;TNluiFEaqMYLpr3nizyXf)ZY6rtd#y`a@7Y|s z?OpQt*v^)N0&2G_Lo-r1Z^yM88vQf2c|Xy?-+#yJmCGYO9}(WNF1A}V=a0RywaJk$ zjm$fq7VYick?O;9zMA=CMHQ!Al+@$0bvt&(oX{1yaPN@$uN4jnihj?-_WPQFf<)tA z@9mc@x&Ncjbe&nx7|(F+#KoPBWwBdcKN9WsE)Ogz5%{=$7O2+0ae_OLhY-Metz^7&?b&(#FybU%LV zBWvUF@zK$x9Sl2r=0DxzBCp@xd0nIWXwvFnjTx=aTsJg0HNBPiUVB-2QE~HQahD3y z#M|=B_RKY`6+$m2EZ%Hm@zTWa)}7rc=jTM;Nf6@`e)V!^=ISodUZHm{9a)=QcJ1c8 zGs!vj?D~CQOVehvXq-O8)t)bSh2gyM>X+Un$;qvUeGMZ(^Wdkg zw@p!3FTA{{oa6e;Lmh>?Y|NIOdbwrh1M>@FX>To#J*{~WuvhqJ;Dgc!TXxKOf68)| z-;HmE4-CBCYQ*ihFllc= z*1hBM%eU^||Im025$tvlTI#hc5#XS38+v99H^=9y=cG6&zeK z)yui_+|SkFp7U)wqcyG+uMKbbdtjfc#t9V@{pSta17CUSPv5O?Y~k_xSTD=si;>R_ zcLgcde$%+)L7@?a=jSii{~=JNfj(sp8_ycKw$l6Bn7fl!%3i-FGceO}zb|^M~Gteu0mb zp^u&w?bvx!{E^r7b-KUPHynESOl0ozC-;H{SQd(fC^#61Wxsfoe!6q=S-q-D3LM)F z*ygydTr>ORmMO=M7jm;U@d_P%CdQ@GFRNVs>nG+|1ioK%T;$l(nwXexzOJk%7KN}LKe(6KFI|-5 z!p3CwosEpk?p1N;f{y~oO`x)M_MLpr*^ll8->u%8IXetn27(3ldOO@S8;MMF0^O%oqdHh9meeB{X zK0@zaL{zoUyta&EdJwl3+ltufR~{&=to_|{A-J0HAXmYG`A59B^%SuEuuj;lH{E`& zZOIDN8`s)Ic`ofLd+Yo5p01eZT&vWWb6ey2;K-{NUN=AGM~6 z{uj8o&u*7|?UXw=H;b%~nW=f~RrEaRc`do@jb{(^Gw$!W#;hfg)BWy-r?tfN<5GTo$>b6i z?mkyNr@Uz1>4!@$b9eK7w)qsk&Y<)G!_n2@uLb8Yx;2;!PT!nxtLEtZ`a0gF-*&$& z(cGFZFWkw}`tjpyzp(W|iB$y?p8S4)efMIOr~B^<7FwFeMt}VCdHU6P?UTgC4y9yh-I=dI~|B&2?5?@umdu@Asp@Gq%8`ke9_^G@qjhWuq+;KW`N2T5@ z&l}&g9XOUcheNr-=#ov9rd-gknJ=Y(Z4?I!d zS*oit>YoT!$<^|_-?nz`ncGxxsH4VB0uB`N@v=YiAlJx z?3#U^eNK+p`BF}u51Y?lex|#CHzSr;d~e6O)!7HWH#}^~{covO_V~Er&h!2Z(hUKj z1=mapBg4Q+qUD&fLc7BO5%DTUjs;)WPg@^XYcciY77@L;C8f8`l0Z`o%Y0jsHvZ~S z`22U>wH-&5W-e4bcCTjgmxImTh7;vk>lo%txvX=?h~HN==QpcDThQHePoA865}hAt zQ!mH*L_hYB=h9cX&6f|@J!r6hFFI>;{hpp6Kh>E$)0Qo}5WW4Lpn2Ys?bG+A&s%6@ z>-%eQzk&b<<6j5Xv&8ofZ~UTvoG^ubuLaU2ehF{qn2- zWps%ywJ|+<<=@{FcJ}F7U9Y4*XibPXwomE(et%E#?m+eV@gZ(IMY#ksvS**ZDhpb7 z-N;`1w*19?j~n0aGxsyaGud(9VK33VI0v%GW13Xwy2-ykWIcTUKEcRm|D$D>`=7D1 z-|m{OH_^7ZZJoRP(&rQ1JHCC<`OD|B_T#BkuazPz7rW1_d9CY}oa}8lQJ=ZJ;XB)- zk3DlTADZRb&)o3#dc*O6tj&}C+>MX-&HTZ%bb(!BU~*C9{lgr#PQ^x@n%icTFI#5S zwRo{bE7L=3l}X#yM7y6~Zs+>&lB!m6vf`9md~f`2e7oq@ng8+VGqVKgV|V`;A9$Q` zSMtT1khQzZFaP~;xa7?Z#~q!YX0NvRX%p<;H=*YJ-lbJ1l|C4%ownTBBfqypGb`%a zRIa>@J2vdlp6{UHvSD%arz1z+icJ(%i&EYsrB@|5H~jyD`KTsvuetxnq0cN{e?>lQnSM4;Yo6t0tBF2Ad#a}1*p|CxO{8p|L6VZ!bW?&fj|6IS+MkmE6^E{Pr7o2_eeq_to zdo+LdbKz~Z6*D(Ah`Suxrt5R(xZ9a?U-cQ!FZyaF%Ex~A`yJ;ujqES>{ja-qZN2&h zmqMxQ$MudY);{AAUca|3T0_S*eqkf`0q+AFQ{yZO8hE^>Zh8G^s#mwr+)#xE)AB1} z94AY}X1zIf{i)lRWA~Z-6IouIXR>aPcvL6G0?INgD*ttLoI7;(`~76j+jWx4SL|AX z?D_t*7~9>mpX)Wzy09rhr|s%JzZI*yGxGJ-t zve;8AMSglu*NEHe_3H>{SNJ-etl96GG!i$>xi2<-vjIP=Pg|B$#BP`MD;v8HSeqog zY22{FZsU{hT`;{)ZB5ug7 zRzH8kTX}}?uuEU(YaVfoF^Am{&vylgX}MF?3?Q$ylXb& zme-SZmvnZP-&<&77x&(;Lo4$^?t`t36?-=oxTG)i zw65xn)T}d2yq(Vw-6(&E<3c~a@6g~AE#0`yY|Xc_5DgavnPsVWtq#qd`=HXTVx3)fZ?sn6o~o&T zmU{0vs+UM2BO@}Z%Z0Yt9=w#7eV&ms;l6{-yAy$VReB8T7hjp|r?u6v z_z_F)Y_p4%@ArPTw4dJUpy879Ic<*ozrzu>Esjc9p$dv~>S`hb5ku#kVZ8use67`u#-9 znu?Vy>FgGKHvBPBjr$KBequHKqSW?)1KQKY|C~Ox>Dlkyt|ek&M|~T)=P;jB&G~Ja z!2iMZ!Q}&SJ`?JuNWRv;8W+Zr0ZL5*Emtia1*Wz7hb^A}u;^0MP^ zFJ=?vX?osx?K*3^!A7a;tTzvT&Q(u5WJ~Tb+DLOD0&??Fkj{=3jJ0 zt%u={;)l9J+rBeTXM6N9$Tt6_dwR%w$)p2nJ*$64J`j8$wpGwYeg%9xti#{9Akm&nu80qxZ zi|Yxv&`~&37O(sL+Myg5L%nS~*Jc{4Exh&lLhGTq|L$DXNiVeI=2q?PJXo+|e_eo_ z@Y_#~JLZJ5Gk@IScjH^+1IsD9uAhEB|Ad^a)QmUpmi=G3yrZOS-JkZLt^ysm@9*xm zSX+7I-tl<$A#t^d&7J1wybG@Q`RKgld{A*{8++sB85%Qr0uEZl+kZS|EEl?PC+mx2 z9A~Qyls+FmAhv*;W2J`+tF<-f`e^st-`*Z6d$XYQZ^W#kgmlYZ! zYyT)D-v0eZ`pMfFqWk2Zxv{ugD>*3MU4CYu z6ty;^XT^7BpJkgjcu8~|=jOcqkKu>ahGR45+?nX55~3lt`1`~5_YwPwH!N+vF-_NH z*Y3>S&*d&H_n&NCd1+NoPf6zWw=OGX5A-uUp0Q}=bp!6=>5YjALX#WcCmcDXkhX9m z?~9`xXKx!YeP&z0zj$>^h{lpN+ss7Yyi=<>Z@VS`zTa)z?*g%#+Y(G}eMvHIh}JQx zeI+8ie*eBH)+IJRDW~ljnzi3>onu<8@pQ+|m=#mJ7RHu4KF@0Od(P*xR$%?i^Se9F z9g1mgJhnzk_V}6?wX(_%^<2sh_9uEpcXr4ag7e6`q9qd+7)almZ(%k$Ig_P=xzLik zTbX$dliCXZl687jdyED8r$61W&PuIOym4zrZuq*GNc*k%@(cU;c9p;1r~LCALzscb zmgVgM(b8+B%_iL6+I{C}_mgvT0+&8~xFTG`rP1_2twejV zoBUyQ4(_EqbJ))%@OX#yUOydrX3_BpGM7SE>b_{njc1(4Y?g8T@6$_1yK_%AvuD11 zzkKWd|9y4)98{dLleq-B$h*oAqk~x!qn`{Bzt_t_j!KHeF4-xm7ckd2{RMZMh_TIu5+_T~8{)D{h z$jto`r?MiC3WIB6ftKikKb%>u$L=exFuZ*<{wPO#(}o$9i~MeU)A(Sx%q>7yaWAXS zw9nCB#d3wEcCFpPdE1V?M!4dJw%B})Io8XKoSj{E?-qX|5gRr|Nq|q7qn#7ffh|<_ zTi7u9^us4bA)6vky)=26BBVYyY*!Fx#`V2%Dq)dgS^^HN%@GqhKts8)v7ix~@)CiZ zY-NdFDfiER3b}nd!}W}7zv+bU`{{dX@?`H{mu5U#be~m7JJ9BX*_4mTp<5oWS?lAw z(#yC-Xy0r-pa!&D#wf=MGJ}6Y<0R;_}AJoVWi;`}{lOmY}{& z;NjjG8hxcRY!;iHnbx?V<1BavVU19XCCecl5yNw5ZPX5Lo_LZ$J|X6u(htt96PWHX z@9QyC);;$2P2w5f`^PPowO(DTIz?z(t;psRTelmTUY%!Kd`#r)Zedr}bVpVu7G}n# ziyFH&Ulyq~_1otMs$}jEHMBH_u=7 zDG;2?`p}HIz5C=fPp0ES+y2U)+14PTW>9af=1|YBHYr(q*{pKuWd#ejc!P8QEM9lM zMg#UVX}zArGA1*vb^k5n9g=ukZar^a+L9}OJY{Vj>n`z+uPq-aKk$wGvLo9gNo?1@ zPQ9tOxWx5V-1%Uz@Y}bxo>>8*2Mr|lOsrk7wNvP;>+{)l|9**=ci!~N-cvO5%+ct&ZWzS&s}no{1fyZYSc+Pg1p z+>H;vND^AF$icl+;%{d}Xouar4Y{q6AM=i;IOiYUv9LTr=HhI_z87UaCGIWHR{zii z)oX|DHaByeKlJAN;i)aT{LJZ%w!BsouN<)WVEZ7~-p^kkD^;dKu7cx-$+4I%6Y9R` zeBeLeYS~iGEBe1Co^ji>^>Lxe`Fk9fEN*`B>ub4PTC$?X)>m^+b05_en`u|rG*LW$ ziG|UoD@~hUZ=af{y@ zO_}6n$6sK3OX_*x3~Ja==xTv3sUOy z)l1(?kZoddIWE=|)+NvoZG?$57lvfxZB z)yTAr;nH=()?Ajvb=y7(e(?RjuPD6v%<-ZF?FZ&F=JOtToN5_*Na*bSRWp~@pYP>Z zbbM!I#kW5@?{07W&o^h9Yya*^LIpSHzjkM6ef}_Qa|%2A$@%vFE4EKJJ#XXu{4l>T zM{B^eR#Bcf^SaFAio5D;wIq({iWR!MJ7yO>Kfk!)#j~>!4-Mw-F7xC$7PQE8s!!X= ze!HYEQ^N~3-+U@CNBaQh{(g%`+0MO}&QBHIHkaX_eZu0|e(ybMzf2T;`+oWJM)thj z9*bn#z0@`fT(rFN=B%?zdZOu_Ga+{`ZfMXItDCoFR-vKx|6}`KE$K^~b0z!F`s6!r zrp)^}Lo=>CX_14~pZDRd?@#nxU*B~tG`+j@bn(~CHJ2)RjV!pD6gZrc14ZZjm;SJb zL-kP2_cffi_woGUb*uTEdZur$wWa!LCeEG*fA%(3*r=y_MLa57-xzXe+x+jp&&)q? zL@lf5LClxi54JZ1U+TZNbxNc0jH9<_bNOdoKYI3Q!RN|#HNS87?EA#E@%KCBE0>*5 zxA8tpx8Z-gYf7-@7azs*{WlmiV)vdp;jJ&2mA}07vD?gOea2g#HI`rQ+4F&E<=@|h ze={bl9y_e#@rU;VTUz6yizkwl?8_E^T_1Yv`0i!j-iPbHzE-Q0=Db^P=}FH$NrmgK z&(^fk&U@^hBlbi~xW)AW_qFZ2+gAC#Se3ksSJ`5F@nOr@+K}xYXVj9uGsgbA9xCoo zaO`}@(bo@p{_nn9JayFrTlvQ5f750Ls+_$L9%7wzy*}g*L@U4 z=FHudO?PDMOSeD#ncVWcU-67#+LXVX#|rZ}&doDXPi*`?nbGIsoB4-1&RhSjw%dB> ztu5=D!xqQmWIB!;7nQZ7=KW4gSbhKB>P5wp!fOqc{=SI4um6tmA4^5rzMa=f1^qRZ zjl$(WC$t_evHQp(z5k1<(%!vFHrI`RmdSJp3-i>!j(*}d|CqLJx7ZrPuLA$remEzr z2ozr?v`zNNn@*F+?;CtYMwUvhRU?{-GJ7hm7BGPOsXx>~HnL#0R=P zyT5=^nSQ#fK#R4F`a9Nt{67E6e;4hYe)W>m{pWVaZ^{1@bdr@V`jCAV|Gj*~EZ>clN$uU! zPpNt}e_xBVw?B{4C^TT@su#An_oDv0lbUps!X3rD$d$8SUeh%@aG*Z>;88cX^ED@~ z**FTc%=%S3YsvZeK0_8s>lV>H|BHpU*)rAKpC6IOR>sVqu!!A^(N;px>QwKKtx+u* zZ|_GxGi1Hhm#|G^Ki8b8uELH2Qi*5K&{T@b3y9^5+1SJD_pqbbpNtH zc!lnU3kQ#UF6ee$xA!C4+UP|&VBU5A~}D*Z)Qr0U3#5q+18ik@?9@e?MPe+D849OnB&~kWDE)U|L8REhyFP8%`ulsh*djk45mvI%XILM!u;3|| z^7{RLWd#D})1&*E8bf93&-{`~3-?$oO!dFYJ2mt6EuW`!>%RV>&pcZm zPuN}3x$*mb_2MrY=e~R?+BN&*@ePI#-Z$)LJ2pG_k5tET+xqW8Oci2F4{3?4S{fMo zy2{j4$qL#JZ&EOM$FfbTGHl+@ztKfE^1oX*DRB5+zyFgv_dd%t=J>0LeX<&-cdb6({LMydrO4#p-;KA{+do|${?SW&Qh{M-=dvH~4=hz% zXBlwNCC!)V?v-gqiG_>jIbT*DXK05uw&CMzD=q`kq=5A z?l%bg23ZtbQ)A^xEYr+gyv6ukX}FHg@NJXs<7LdQ^O- zk4iz&&a1TxwyM>>5}7`~e%|T-ANYOhe<(iPxN)k;oaK%08UKq0o2=i&b!#2lr5Q~f zN2TK39+^T@3M4Tnu9WOJ?#+3-j{9+09;4jH`P2EAe$W!VBwz3I@|38NvvbMYo6gVQ z-HmwoVU^{!xSc-l9=T3xZU)`7QS#F$Zes?Q=DHZeqb47u+zS3xD9GurU-T@R&5yxz zLHE(_<6^#g7R?>!;NAgO=|K$t7#GL?W&5D9;ZNh~X|9+y>}UAR_UPj!t?cb`1tL2* z?l51ulrbx=gb$VuAQtkv)%>q}b6j4ZIo;6dX;lL6*3OFV!{Ui^Qzjj1<$mJsezL!> z>9N4OSBJRF`ujJV*q!@wap~D{_DT{>*NzE<_##g=~fg+oQs{8{W!%ko8I2cAtT*fiLV3 zZ&qZ+`Nb9{E}0cG?!3J0BWK}o``Ou&yY==H_t{NJT^MFID=F?yOy8fYtDmfoRj&SL z(^~voqqlF7?RgQ8n4e0WSJyl?H<&2(p6AHp*3dg8%NxTR)EnfP7M0)Lx*ApjfNU3N zVN*}Mz4kyq(|#@+?k~&}S^w=~`}9w_qw^)l=7`)g`kTbxyiwUwrh71=XIu5Vg})!R zcYgc6n2AyO)w&G~YqdqSX7U_8>FIS+<>;3}d%wq2px^rJukYP2AZOt~tYudd{jDmdV1mJNRvB)jDN26bNM93?Ef$7 zvE~rZ^PRU|+aAbgjAy)Nv|ns>BY(sFVW_`^UzX&faL1C34CA4jAL)inz2gU~` zAEg?_|7;65||eGJ<^)<5?gZ0dH+ z{GWHS1Tv=rjpJEujTJG>Gh1@&8DvURasRXZez}q&UJGT}&1WAyo3_%&X96!<;6a5~)%Tf~c6|9F@bjcaZtf2-Db zu-KgEIQwt&w>_`p#ogU|lOwY~&pN4+XXmdv^^o@T-VHX+DPg^#cQ!D^u3Ut1ND_XF3A|1H-nhnpa}x z&h!47^7X4*#NV5*zO8fmu*dRfj56K|?eeUf2`KRFW&8yO-MD;`G zuYNc4-0f$9Kf=ETeC>Vp@vWT2bclInJD=Xn@&9`6mG)KU-fp(O+*S=S$ zdlNn+G(G3OxyGP!(Z%RLb6=hJ{`|9h+p{_8ISY3!-nD<%QbDWrVf9%)7W40DW{X-e zGcYhX{GVO1Z}+bt<%)fqqqZ17-x4O9?ens3^)JuTl3)}sCC%{SX#Exszgir=)VqC9!t zwz$8`tJYL)_~rO@x{vv1A&7||gr=o$p1taO*!-1tH(ylV+wDDVf8g9T|DyixD2#ld ze6{fM**X0eBthYFF1ga|cC_poyD-~;z3X@FytUbR`ktG&|M-0Ee)YSTn|c0vzu9@u zoFL{FFb1FheD-SfDt{BE7uBnzZ$(Pp&MMtub!ElHAE94!wrb@(OX8f%$iTqxK*=}w zeEynu`B!plb_DJ$ti5fXeS1Zi)a0}6tK?1fxNYvd$c_7cwpbHtaObr2&97IPhkg&0 zUvC$2H}LM26?bp&?Yv!8y657}nianyzutZ||Eg!A@pSJOJRo;ZPh559M*Q8dy{mtD zd~JNS?$x!dc;$w=t)=$sO@7UkUoW?=F7oe2t4kcSuV#GM!~tWo9rYp-Un-fptW z{9gRt-G+Z_vX$q=y_w3n;QDIuRg16QU+uo?>8tNoFJHa5^yi-``IEF=A=ZVT_>{Bl z2FHy27jGDT-FxNm^_ScK&yT*u^w#99H9T9h=vVpIl&|ftF23q?EC2lYDz}!&4k@VD zT755{uU@tHYHu&YuX}FnU+!LgdsSLW_QLPg*GtlbsumVMFO!a*ce%IVXUW&3t+zO4 z_J^GhtzSKFR^0zU+h|$uTDI#Akhog6NV6d>gXQ$^Rnbzx=kwR{h2`h@IP70%sJM5# z@y@%qrllJv#AU`XRO}HnnLp#6e0IyZ<5I%sjvJjlqwN6k$Mp--1gc(}d@1cc@blQq zzmL9r{QC0djJWGk!Dm-rJ^3mmSVA|34HUkkcr*lHA<&ThZ2!H9wyhs#T%QOM^K|ud JS?83{1OTWGO+5er literal 0 HcmV?d00001 diff --git a/thirdparty/adlmidi/chips/esfmu/esfm.c b/thirdparty/adlmidi/chips/esfmu/esfm.c new file mode 100644 index 000000000..7ba1f8fe1 --- /dev/null +++ b/thirdparty/adlmidi/chips/esfmu/esfm.c @@ -0,0 +1,2329 @@ +/* + * ESFMu: emulator for the ESS "ESFM" enhanced OPL3 clone + * Copyright (C) 2023 Kagamiin~ + * + * This file includes code and data from the Nuked OPL3 project, copyright (C) + * 2013-2023 Nuke.YKT. Its usage, modification and redistribution is allowed + * under the terms of the GNU Lesser General Public License version 2.1 or + * later. + * + * ESFMu 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. + * + * ESFMu 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 ESFMu. If not, see . + */ + +/* + * ESFMu wouldn't have been possible without the hard work and dedication of + * the retro computer hardware research and preservation community. + * + * I'd like to thank: + * - Nuke.YKT + * Developer of Nuked OPL3, which was the basis for ESFMu's code and + * also a great learning resource on Yamaha FM synthesis for myself. + * Nuke.YKT also gives shoutouts on behalf of Nuked OPL3 to: + * - MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * - forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * - OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * - siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * - rainwarrior + * For performing the initial research on ESFM drivers and documenting + * ESS's patent on native mode operator organization. + * - jwt27 + * For kickstarting the ESFM research project and compiling rainwarrior's + * findings and more in an accessible document ("ESFM Demystified"). + * - pachuco/CatButts + * For documenting ESS's patent on ESFM's feedback implementation, which + * was vital in getting ESFMu's sound output to be accurate. + * - akumanatt + * For helping out with code optimization. + * - And everybody who helped out with real hardware testing + */ + +#include "esfm.h" +#include +#include +#include +#include + +/* + * Log-scale quarter sine table extracted from OPL3 ROM; taken straight from + * Nuked OPL3 source code. + * TODO: Extract sine table from ESFM die scans... does ESFM even use a sine + * table? Patent documents give a hint to a possible method of generating sine + * waves using some sort of boolean logic wizardry (lol) + * Optimization: All 8 waveforms are calculated and unfolded from the actual + * data in OPL3's ROM. Negative entries are marked by 0x8000. + */ +static const uint16_t logsinrom[1024*8] = { + /* wave 0 */ + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004, + 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007, + 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b, + 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010, + 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, + 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, + 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e, + 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038, + 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043, + 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050, + 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e, + 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, + 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f, + 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092, + 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7, + 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be, + 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7, + 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4, + 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114, + 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137, + 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160, + 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f, + 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5, + 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206, + 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256, + 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd, + 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e, + 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443, + 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859, + 0x8859, 0x86c3, 0x8607, 0x858b, 0x852e, 0x84e4, 0x84a6, 0x8471, + 0x8443, 0x841a, 0x83f5, 0x83d3, 0x83b5, 0x8398, 0x837e, 0x8365, + 0x834e, 0x8339, 0x8324, 0x8311, 0x82ff, 0x82ed, 0x82dc, 0x82cd, + 0x82bd, 0x82af, 0x82a0, 0x8293, 0x8286, 0x8279, 0x826d, 0x8261, + 0x8256, 0x824b, 0x8240, 0x8236, 0x822c, 0x8222, 0x8218, 0x820f, + 0x8206, 0x81fd, 0x81f5, 0x81ec, 0x81e4, 0x81dc, 0x81d4, 0x81cd, + 0x81c5, 0x81be, 0x81b7, 0x81b0, 0x81a9, 0x81a2, 0x819b, 0x8195, + 0x818f, 0x8188, 0x8182, 0x817c, 0x8177, 0x8171, 0x816b, 0x8166, + 0x8160, 0x815b, 0x8155, 0x8150, 0x814b, 0x8146, 0x8141, 0x813c, + 0x8137, 0x8133, 0x812e, 0x8129, 0x8125, 0x8121, 0x811c, 0x8118, + 0x8114, 0x810f, 0x810b, 0x8107, 0x8103, 0x80ff, 0x80fb, 0x80f8, + 0x80f4, 0x80f0, 0x80ec, 0x80e9, 0x80e5, 0x80e2, 0x80de, 0x80db, + 0x80d7, 0x80d4, 0x80d1, 0x80cd, 0x80ca, 0x80c7, 0x80c4, 0x80c1, + 0x80be, 0x80bb, 0x80b8, 0x80b5, 0x80b2, 0x80af, 0x80ac, 0x80a9, + 0x80a7, 0x80a4, 0x80a1, 0x809f, 0x809c, 0x8099, 0x8097, 0x8094, + 0x8092, 0x808f, 0x808d, 0x808a, 0x8088, 0x8086, 0x8083, 0x8081, + 0x807f, 0x807d, 0x807a, 0x8078, 0x8076, 0x8074, 0x8072, 0x8070, + 0x806e, 0x806c, 0x806a, 0x8068, 0x8066, 0x8064, 0x8062, 0x8060, + 0x805e, 0x805c, 0x805b, 0x8059, 0x8057, 0x8055, 0x8053, 0x8052, + 0x8050, 0x804e, 0x804d, 0x804b, 0x804a, 0x8048, 0x8046, 0x8045, + 0x8043, 0x8042, 0x8040, 0x803f, 0x803e, 0x803c, 0x803b, 0x8039, + 0x8038, 0x8037, 0x8035, 0x8034, 0x8033, 0x8031, 0x8030, 0x802f, + 0x802e, 0x802d, 0x802b, 0x802a, 0x8029, 0x8028, 0x8027, 0x8026, + 0x8025, 0x8024, 0x8023, 0x8022, 0x8021, 0x8020, 0x801f, 0x801e, + 0x801d, 0x801c, 0x801b, 0x801a, 0x8019, 0x8018, 0x8017, 0x8017, + 0x8016, 0x8015, 0x8014, 0x8014, 0x8013, 0x8012, 0x8011, 0x8011, + 0x8010, 0x800f, 0x800f, 0x800e, 0x800d, 0x800d, 0x800c, 0x800c, + 0x800b, 0x800a, 0x800a, 0x8009, 0x8009, 0x8008, 0x8008, 0x8007, + 0x8007, 0x8007, 0x8006, 0x8006, 0x8005, 0x8005, 0x8005, 0x8004, + 0x8004, 0x8004, 0x8003, 0x8003, 0x8003, 0x8002, 0x8002, 0x8002, + 0x8002, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8002, + 0x8002, 0x8002, 0x8002, 0x8003, 0x8003, 0x8003, 0x8004, 0x8004, + 0x8004, 0x8005, 0x8005, 0x8005, 0x8006, 0x8006, 0x8007, 0x8007, + 0x8007, 0x8008, 0x8008, 0x8009, 0x8009, 0x800a, 0x800a, 0x800b, + 0x800c, 0x800c, 0x800d, 0x800d, 0x800e, 0x800f, 0x800f, 0x8010, + 0x8011, 0x8011, 0x8012, 0x8013, 0x8014, 0x8014, 0x8015, 0x8016, + 0x8017, 0x8017, 0x8018, 0x8019, 0x801a, 0x801b, 0x801c, 0x801d, + 0x801e, 0x801f, 0x8020, 0x8021, 0x8022, 0x8023, 0x8024, 0x8025, + 0x8026, 0x8027, 0x8028, 0x8029, 0x802a, 0x802b, 0x802d, 0x802e, + 0x802f, 0x8030, 0x8031, 0x8033, 0x8034, 0x8035, 0x8037, 0x8038, + 0x8039, 0x803b, 0x803c, 0x803e, 0x803f, 0x8040, 0x8042, 0x8043, + 0x8045, 0x8046, 0x8048, 0x804a, 0x804b, 0x804d, 0x804e, 0x8050, + 0x8052, 0x8053, 0x8055, 0x8057, 0x8059, 0x805b, 0x805c, 0x805e, + 0x8060, 0x8062, 0x8064, 0x8066, 0x8068, 0x806a, 0x806c, 0x806e, + 0x8070, 0x8072, 0x8074, 0x8076, 0x8078, 0x807a, 0x807d, 0x807f, + 0x8081, 0x8083, 0x8086, 0x8088, 0x808a, 0x808d, 0x808f, 0x8092, + 0x8094, 0x8097, 0x8099, 0x809c, 0x809f, 0x80a1, 0x80a4, 0x80a7, + 0x80a9, 0x80ac, 0x80af, 0x80b2, 0x80b5, 0x80b8, 0x80bb, 0x80be, + 0x80c1, 0x80c4, 0x80c7, 0x80ca, 0x80cd, 0x80d1, 0x80d4, 0x80d7, + 0x80db, 0x80de, 0x80e2, 0x80e5, 0x80e9, 0x80ec, 0x80f0, 0x80f4, + 0x80f8, 0x80fb, 0x80ff, 0x8103, 0x8107, 0x810b, 0x810f, 0x8114, + 0x8118, 0x811c, 0x8121, 0x8125, 0x8129, 0x812e, 0x8133, 0x8137, + 0x813c, 0x8141, 0x8146, 0x814b, 0x8150, 0x8155, 0x815b, 0x8160, + 0x8166, 0x816b, 0x8171, 0x8177, 0x817c, 0x8182, 0x8188, 0x818f, + 0x8195, 0x819b, 0x81a2, 0x81a9, 0x81b0, 0x81b7, 0x81be, 0x81c5, + 0x81cd, 0x81d4, 0x81dc, 0x81e4, 0x81ec, 0x81f5, 0x81fd, 0x8206, + 0x820f, 0x8218, 0x8222, 0x822c, 0x8236, 0x8240, 0x824b, 0x8256, + 0x8261, 0x826d, 0x8279, 0x8286, 0x8293, 0x82a0, 0x82af, 0x82bd, + 0x82cd, 0x82dc, 0x82ed, 0x82ff, 0x8311, 0x8324, 0x8339, 0x834e, + 0x8365, 0x837e, 0x8398, 0x83b5, 0x83d3, 0x83f5, 0x841a, 0x8443, + 0x8471, 0x84a6, 0x84e4, 0x852e, 0x858b, 0x8607, 0x86c3, 0x8859, + /* wave 1 */ + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004, + 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007, + 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b, + 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010, + 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, + 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, + 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e, + 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038, + 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043, + 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050, + 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e, + 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, + 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f, + 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092, + 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7, + 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be, + 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7, + 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4, + 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114, + 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137, + 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160, + 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f, + 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5, + 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206, + 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256, + 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd, + 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e, + 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443, + 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + /* wave 2 */ + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004, + 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007, + 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b, + 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010, + 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, + 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, + 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e, + 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038, + 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043, + 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050, + 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e, + 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, + 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f, + 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092, + 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7, + 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be, + 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7, + 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4, + 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114, + 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137, + 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160, + 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f, + 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5, + 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206, + 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256, + 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd, + 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e, + 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443, + 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859, + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004, + 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007, + 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b, + 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010, + 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, + 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, + 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e, + 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038, + 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043, + 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050, + 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e, + 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, + 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f, + 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092, + 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7, + 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be, + 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7, + 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4, + 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114, + 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137, + 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160, + 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f, + 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5, + 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206, + 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256, + 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd, + 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e, + 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443, + 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859, + /* wave 3 */ + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471, + 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365, + 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd, + 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261, + 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f, + 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd, + 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195, + 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166, + 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c, + 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118, + 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8, + 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db, + 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1, + 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9, + 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094, + 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081, + 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070, + 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060, + 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052, + 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045, + 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039, + 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f, + 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026, + 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e, + 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017, + 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011, + 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c, + 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007, + 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004, + 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + /* wave 4 */ + 0x0859, 0x0607, 0x052e, 0x04a6, 0x0443, 0x03f5, 0x03b5, 0x037e, + 0x034e, 0x0324, 0x02ff, 0x02dc, 0x02bd, 0x02a0, 0x0286, 0x026d, + 0x0256, 0x0240, 0x022c, 0x0218, 0x0206, 0x01f5, 0x01e4, 0x01d4, + 0x01c5, 0x01b7, 0x01a9, 0x019b, 0x018f, 0x0182, 0x0177, 0x016b, + 0x0160, 0x0155, 0x014b, 0x0141, 0x0137, 0x012e, 0x0125, 0x011c, + 0x0114, 0x010b, 0x0103, 0x00fb, 0x00f4, 0x00ec, 0x00e5, 0x00de, + 0x00d7, 0x00d1, 0x00ca, 0x00c4, 0x00be, 0x00b8, 0x00b2, 0x00ac, + 0x00a7, 0x00a1, 0x009c, 0x0097, 0x0092, 0x008d, 0x0088, 0x0083, + 0x007f, 0x007a, 0x0076, 0x0072, 0x006e, 0x006a, 0x0066, 0x0062, + 0x005e, 0x005b, 0x0057, 0x0053, 0x0050, 0x004d, 0x004a, 0x0046, + 0x0043, 0x0040, 0x003e, 0x003b, 0x0038, 0x0035, 0x0033, 0x0030, + 0x002e, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, + 0x001d, 0x001b, 0x0019, 0x0017, 0x0016, 0x0014, 0x0013, 0x0011, + 0x0010, 0x000f, 0x000d, 0x000c, 0x000b, 0x000a, 0x0009, 0x0008, + 0x0007, 0x0006, 0x0005, 0x0005, 0x0004, 0x0003, 0x0003, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0003, 0x0003, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000f, 0x0010, + 0x0011, 0x0013, 0x0014, 0x0016, 0x0017, 0x0019, 0x001b, 0x001d, + 0x001f, 0x0021, 0x0023, 0x0025, 0x0027, 0x0029, 0x002b, 0x002e, + 0x0030, 0x0033, 0x0035, 0x0038, 0x003b, 0x003e, 0x0040, 0x0043, + 0x0046, 0x004a, 0x004d, 0x0050, 0x0053, 0x0057, 0x005b, 0x005e, + 0x0062, 0x0066, 0x006a, 0x006e, 0x0072, 0x0076, 0x007a, 0x007f, + 0x0083, 0x0088, 0x008d, 0x0092, 0x0097, 0x009c, 0x00a1, 0x00a7, + 0x00ac, 0x00b2, 0x00b8, 0x00be, 0x00c4, 0x00ca, 0x00d1, 0x00d7, + 0x00de, 0x00e5, 0x00ec, 0x00f4, 0x00fb, 0x0103, 0x010b, 0x0114, + 0x011c, 0x0125, 0x012e, 0x0137, 0x0141, 0x014b, 0x0155, 0x0160, + 0x016b, 0x0177, 0x0182, 0x018f, 0x019b, 0x01a9, 0x01b7, 0x01c5, + 0x01d4, 0x01e4, 0x01f5, 0x0206, 0x0218, 0x022c, 0x0240, 0x0256, + 0x026d, 0x0286, 0x02a0, 0x02bd, 0x02dc, 0x02ff, 0x0324, 0x034e, + 0x037e, 0x03b5, 0x03f5, 0x0443, 0x04a6, 0x052e, 0x0607, 0x0859, + 0x8859, 0x8607, 0x852e, 0x84a6, 0x8443, 0x83f5, 0x83b5, 0x837e, + 0x834e, 0x8324, 0x82ff, 0x82dc, 0x82bd, 0x82a0, 0x8286, 0x826d, + 0x8256, 0x8240, 0x822c, 0x8218, 0x8206, 0x81f5, 0x81e4, 0x81d4, + 0x81c5, 0x81b7, 0x81a9, 0x819b, 0x818f, 0x8182, 0x8177, 0x816b, + 0x8160, 0x8155, 0x814b, 0x8141, 0x8137, 0x812e, 0x8125, 0x811c, + 0x8114, 0x810b, 0x8103, 0x80fb, 0x80f4, 0x80ec, 0x80e5, 0x80de, + 0x80d7, 0x80d1, 0x80ca, 0x80c4, 0x80be, 0x80b8, 0x80b2, 0x80ac, + 0x80a7, 0x80a1, 0x809c, 0x8097, 0x8092, 0x808d, 0x8088, 0x8083, + 0x807f, 0x807a, 0x8076, 0x8072, 0x806e, 0x806a, 0x8066, 0x8062, + 0x805e, 0x805b, 0x8057, 0x8053, 0x8050, 0x804d, 0x804a, 0x8046, + 0x8043, 0x8040, 0x803e, 0x803b, 0x8038, 0x8035, 0x8033, 0x8030, + 0x802e, 0x802b, 0x8029, 0x8027, 0x8025, 0x8023, 0x8021, 0x801f, + 0x801d, 0x801b, 0x8019, 0x8017, 0x8016, 0x8014, 0x8013, 0x8011, + 0x8010, 0x800f, 0x800d, 0x800c, 0x800b, 0x800a, 0x8009, 0x8008, + 0x8007, 0x8006, 0x8005, 0x8005, 0x8004, 0x8003, 0x8003, 0x8002, + 0x8002, 0x8001, 0x8001, 0x8001, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8001, 0x8001, 0x8002, + 0x8002, 0x8003, 0x8003, 0x8004, 0x8005, 0x8005, 0x8006, 0x8007, + 0x8008, 0x8009, 0x800a, 0x800b, 0x800c, 0x800d, 0x800f, 0x8010, + 0x8011, 0x8013, 0x8014, 0x8016, 0x8017, 0x8019, 0x801b, 0x801d, + 0x801f, 0x8021, 0x8023, 0x8025, 0x8027, 0x8029, 0x802b, 0x802e, + 0x8030, 0x8033, 0x8035, 0x8038, 0x803b, 0x803e, 0x8040, 0x8043, + 0x8046, 0x804a, 0x804d, 0x8050, 0x8053, 0x8057, 0x805b, 0x805e, + 0x8062, 0x8066, 0x806a, 0x806e, 0x8072, 0x8076, 0x807a, 0x807f, + 0x8083, 0x8088, 0x808d, 0x8092, 0x8097, 0x809c, 0x80a1, 0x80a7, + 0x80ac, 0x80b2, 0x80b8, 0x80be, 0x80c4, 0x80ca, 0x80d1, 0x80d7, + 0x80de, 0x80e5, 0x80ec, 0x80f4, 0x80fb, 0x8103, 0x810b, 0x8114, + 0x811c, 0x8125, 0x812e, 0x8137, 0x8141, 0x814b, 0x8155, 0x8160, + 0x816b, 0x8177, 0x8182, 0x818f, 0x819b, 0x81a9, 0x81b7, 0x81c5, + 0x81d4, 0x81e4, 0x81f5, 0x8206, 0x8218, 0x822c, 0x8240, 0x8256, + 0x826d, 0x8286, 0x82a0, 0x82bd, 0x82dc, 0x82ff, 0x8324, 0x834e, + 0x837e, 0x83b5, 0x83f5, 0x8443, 0x84a6, 0x852e, 0x8607, 0x8859, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + /* wave 5 */ + 0x0859, 0x0607, 0x052e, 0x04a6, 0x0443, 0x03f5, 0x03b5, 0x037e, + 0x034e, 0x0324, 0x02ff, 0x02dc, 0x02bd, 0x02a0, 0x0286, 0x026d, + 0x0256, 0x0240, 0x022c, 0x0218, 0x0206, 0x01f5, 0x01e4, 0x01d4, + 0x01c5, 0x01b7, 0x01a9, 0x019b, 0x018f, 0x0182, 0x0177, 0x016b, + 0x0160, 0x0155, 0x014b, 0x0141, 0x0137, 0x012e, 0x0125, 0x011c, + 0x0114, 0x010b, 0x0103, 0x00fb, 0x00f4, 0x00ec, 0x00e5, 0x00de, + 0x00d7, 0x00d1, 0x00ca, 0x00c4, 0x00be, 0x00b8, 0x00b2, 0x00ac, + 0x00a7, 0x00a1, 0x009c, 0x0097, 0x0092, 0x008d, 0x0088, 0x0083, + 0x007f, 0x007a, 0x0076, 0x0072, 0x006e, 0x006a, 0x0066, 0x0062, + 0x005e, 0x005b, 0x0057, 0x0053, 0x0050, 0x004d, 0x004a, 0x0046, + 0x0043, 0x0040, 0x003e, 0x003b, 0x0038, 0x0035, 0x0033, 0x0030, + 0x002e, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, + 0x001d, 0x001b, 0x0019, 0x0017, 0x0016, 0x0014, 0x0013, 0x0011, + 0x0010, 0x000f, 0x000d, 0x000c, 0x000b, 0x000a, 0x0009, 0x0008, + 0x0007, 0x0006, 0x0005, 0x0005, 0x0004, 0x0003, 0x0003, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0003, 0x0003, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000f, 0x0010, + 0x0011, 0x0013, 0x0014, 0x0016, 0x0017, 0x0019, 0x001b, 0x001d, + 0x001f, 0x0021, 0x0023, 0x0025, 0x0027, 0x0029, 0x002b, 0x002e, + 0x0030, 0x0033, 0x0035, 0x0038, 0x003b, 0x003e, 0x0040, 0x0043, + 0x0046, 0x004a, 0x004d, 0x0050, 0x0053, 0x0057, 0x005b, 0x005e, + 0x0062, 0x0066, 0x006a, 0x006e, 0x0072, 0x0076, 0x007a, 0x007f, + 0x0083, 0x0088, 0x008d, 0x0092, 0x0097, 0x009c, 0x00a1, 0x00a7, + 0x00ac, 0x00b2, 0x00b8, 0x00be, 0x00c4, 0x00ca, 0x00d1, 0x00d7, + 0x00de, 0x00e5, 0x00ec, 0x00f4, 0x00fb, 0x0103, 0x010b, 0x0114, + 0x011c, 0x0125, 0x012e, 0x0137, 0x0141, 0x014b, 0x0155, 0x0160, + 0x016b, 0x0177, 0x0182, 0x018f, 0x019b, 0x01a9, 0x01b7, 0x01c5, + 0x01d4, 0x01e4, 0x01f5, 0x0206, 0x0218, 0x022c, 0x0240, 0x0256, + 0x026d, 0x0286, 0x02a0, 0x02bd, 0x02dc, 0x02ff, 0x0324, 0x034e, + 0x037e, 0x03b5, 0x03f5, 0x0443, 0x04a6, 0x052e, 0x0607, 0x0859, + 0x0859, 0x0607, 0x052e, 0x04a6, 0x0443, 0x03f5, 0x03b5, 0x037e, + 0x034e, 0x0324, 0x02ff, 0x02dc, 0x02bd, 0x02a0, 0x0286, 0x026d, + 0x0256, 0x0240, 0x022c, 0x0218, 0x0206, 0x01f5, 0x01e4, 0x01d4, + 0x01c5, 0x01b7, 0x01a9, 0x019b, 0x018f, 0x0182, 0x0177, 0x016b, + 0x0160, 0x0155, 0x014b, 0x0141, 0x0137, 0x012e, 0x0125, 0x011c, + 0x0114, 0x010b, 0x0103, 0x00fb, 0x00f4, 0x00ec, 0x00e5, 0x00de, + 0x00d7, 0x00d1, 0x00ca, 0x00c4, 0x00be, 0x00b8, 0x00b2, 0x00ac, + 0x00a7, 0x00a1, 0x009c, 0x0097, 0x0092, 0x008d, 0x0088, 0x0083, + 0x007f, 0x007a, 0x0076, 0x0072, 0x006e, 0x006a, 0x0066, 0x0062, + 0x005e, 0x005b, 0x0057, 0x0053, 0x0050, 0x004d, 0x004a, 0x0046, + 0x0043, 0x0040, 0x003e, 0x003b, 0x0038, 0x0035, 0x0033, 0x0030, + 0x002e, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, + 0x001d, 0x001b, 0x0019, 0x0017, 0x0016, 0x0014, 0x0013, 0x0011, + 0x0010, 0x000f, 0x000d, 0x000c, 0x000b, 0x000a, 0x0009, 0x0008, + 0x0007, 0x0006, 0x0005, 0x0005, 0x0004, 0x0003, 0x0003, 0x0002, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0002, 0x0003, 0x0003, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000f, 0x0010, + 0x0011, 0x0013, 0x0014, 0x0016, 0x0017, 0x0019, 0x001b, 0x001d, + 0x001f, 0x0021, 0x0023, 0x0025, 0x0027, 0x0029, 0x002b, 0x002e, + 0x0030, 0x0033, 0x0035, 0x0038, 0x003b, 0x003e, 0x0040, 0x0043, + 0x0046, 0x004a, 0x004d, 0x0050, 0x0053, 0x0057, 0x005b, 0x005e, + 0x0062, 0x0066, 0x006a, 0x006e, 0x0072, 0x0076, 0x007a, 0x007f, + 0x0083, 0x0088, 0x008d, 0x0092, 0x0097, 0x009c, 0x00a1, 0x00a7, + 0x00ac, 0x00b2, 0x00b8, 0x00be, 0x00c4, 0x00ca, 0x00d1, 0x00d7, + 0x00de, 0x00e5, 0x00ec, 0x00f4, 0x00fb, 0x0103, 0x010b, 0x0114, + 0x011c, 0x0125, 0x012e, 0x0137, 0x0141, 0x014b, 0x0155, 0x0160, + 0x016b, 0x0177, 0x0182, 0x018f, 0x019b, 0x01a9, 0x01b7, 0x01c5, + 0x01d4, 0x01e4, 0x01f5, 0x0206, 0x0218, 0x022c, 0x0240, 0x0256, + 0x026d, 0x0286, 0x02a0, 0x02bd, 0x02dc, 0x02ff, 0x0324, 0x034e, + 0x037e, 0x03b5, 0x03f5, 0x0443, 0x04a6, 0x052e, 0x0607, 0x0859, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, + /* wave 6 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + /* wave 7 */ + 0x0000, 0x0008, 0x0010, 0x0018, 0x0020, 0x0028, 0x0030, 0x0038, + 0x0040, 0x0048, 0x0050, 0x0058, 0x0060, 0x0068, 0x0070, 0x0078, + 0x0080, 0x0088, 0x0090, 0x0098, 0x00a0, 0x00a8, 0x00b0, 0x00b8, + 0x00c0, 0x00c8, 0x00d0, 0x00d8, 0x00e0, 0x00e8, 0x00f0, 0x00f8, + 0x0100, 0x0108, 0x0110, 0x0118, 0x0120, 0x0128, 0x0130, 0x0138, + 0x0140, 0x0148, 0x0150, 0x0158, 0x0160, 0x0168, 0x0170, 0x0178, + 0x0180, 0x0188, 0x0190, 0x0198, 0x01a0, 0x01a8, 0x01b0, 0x01b8, + 0x01c0, 0x01c8, 0x01d0, 0x01d8, 0x01e0, 0x01e8, 0x01f0, 0x01f8, + 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230, 0x0238, + 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278, + 0x0280, 0x0288, 0x0290, 0x0298, 0x02a0, 0x02a8, 0x02b0, 0x02b8, + 0x02c0, 0x02c8, 0x02d0, 0x02d8, 0x02e0, 0x02e8, 0x02f0, 0x02f8, + 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338, + 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378, + 0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8, + 0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8, + 0x0400, 0x0408, 0x0410, 0x0418, 0x0420, 0x0428, 0x0430, 0x0438, + 0x0440, 0x0448, 0x0450, 0x0458, 0x0460, 0x0468, 0x0470, 0x0478, + 0x0480, 0x0488, 0x0490, 0x0498, 0x04a0, 0x04a8, 0x04b0, 0x04b8, + 0x04c0, 0x04c8, 0x04d0, 0x04d8, 0x04e0, 0x04e8, 0x04f0, 0x04f8, + 0x0500, 0x0508, 0x0510, 0x0518, 0x0520, 0x0528, 0x0530, 0x0538, + 0x0540, 0x0548, 0x0550, 0x0558, 0x0560, 0x0568, 0x0570, 0x0578, + 0x0580, 0x0588, 0x0590, 0x0598, 0x05a0, 0x05a8, 0x05b0, 0x05b8, + 0x05c0, 0x05c8, 0x05d0, 0x05d8, 0x05e0, 0x05e8, 0x05f0, 0x05f8, + 0x0600, 0x0608, 0x0610, 0x0618, 0x0620, 0x0628, 0x0630, 0x0638, + 0x0640, 0x0648, 0x0650, 0x0658, 0x0660, 0x0668, 0x0670, 0x0678, + 0x0680, 0x0688, 0x0690, 0x0698, 0x06a0, 0x06a8, 0x06b0, 0x06b8, + 0x06c0, 0x06c8, 0x06d0, 0x06d8, 0x06e0, 0x06e8, 0x06f0, 0x06f8, + 0x0700, 0x0708, 0x0710, 0x0718, 0x0720, 0x0728, 0x0730, 0x0738, + 0x0740, 0x0748, 0x0750, 0x0758, 0x0760, 0x0768, 0x0770, 0x0778, + 0x0780, 0x0788, 0x0790, 0x0798, 0x07a0, 0x07a8, 0x07b0, 0x07b8, + 0x07c0, 0x07c8, 0x07d0, 0x07d8, 0x07e0, 0x07e8, 0x07f0, 0x07f8, + 0x0800, 0x0808, 0x0810, 0x0818, 0x0820, 0x0828, 0x0830, 0x0838, + 0x0840, 0x0848, 0x0850, 0x0858, 0x0860, 0x0868, 0x0870, 0x0878, + 0x0880, 0x0888, 0x0890, 0x0898, 0x08a0, 0x08a8, 0x08b0, 0x08b8, + 0x08c0, 0x08c8, 0x08d0, 0x08d8, 0x08e0, 0x08e8, 0x08f0, 0x08f8, + 0x0900, 0x0908, 0x0910, 0x0918, 0x0920, 0x0928, 0x0930, 0x0938, + 0x0940, 0x0948, 0x0950, 0x0958, 0x0960, 0x0968, 0x0970, 0x0978, + 0x0980, 0x0988, 0x0990, 0x0998, 0x09a0, 0x09a8, 0x09b0, 0x09b8, + 0x09c0, 0x09c8, 0x09d0, 0x09d8, 0x09e0, 0x09e8, 0x09f0, 0x09f8, + 0x0a00, 0x0a08, 0x0a10, 0x0a18, 0x0a20, 0x0a28, 0x0a30, 0x0a38, + 0x0a40, 0x0a48, 0x0a50, 0x0a58, 0x0a60, 0x0a68, 0x0a70, 0x0a78, + 0x0a80, 0x0a88, 0x0a90, 0x0a98, 0x0aa0, 0x0aa8, 0x0ab0, 0x0ab8, + 0x0ac0, 0x0ac8, 0x0ad0, 0x0ad8, 0x0ae0, 0x0ae8, 0x0af0, 0x0af8, + 0x0b00, 0x0b08, 0x0b10, 0x0b18, 0x0b20, 0x0b28, 0x0b30, 0x0b38, + 0x0b40, 0x0b48, 0x0b50, 0x0b58, 0x0b60, 0x0b68, 0x0b70, 0x0b78, + 0x0b80, 0x0b88, 0x0b90, 0x0b98, 0x0ba0, 0x0ba8, 0x0bb0, 0x0bb8, + 0x0bc0, 0x0bc8, 0x0bd0, 0x0bd8, 0x0be0, 0x0be8, 0x0bf0, 0x0bf8, + 0x0c00, 0x0c08, 0x0c10, 0x0c18, 0x0c20, 0x0c28, 0x0c30, 0x0c38, + 0x0c40, 0x0c48, 0x0c50, 0x0c58, 0x0c60, 0x0c68, 0x0c70, 0x0c78, + 0x0c80, 0x0c88, 0x0c90, 0x0c98, 0x0ca0, 0x0ca8, 0x0cb0, 0x0cb8, + 0x0cc0, 0x0cc8, 0x0cd0, 0x0cd8, 0x0ce0, 0x0ce8, 0x0cf0, 0x0cf8, + 0x0d00, 0x0d08, 0x0d10, 0x0d18, 0x0d20, 0x0d28, 0x0d30, 0x0d38, + 0x0d40, 0x0d48, 0x0d50, 0x0d58, 0x0d60, 0x0d68, 0x0d70, 0x0d78, + 0x0d80, 0x0d88, 0x0d90, 0x0d98, 0x0da0, 0x0da8, 0x0db0, 0x0db8, + 0x0dc0, 0x0dc8, 0x0dd0, 0x0dd8, 0x0de0, 0x0de8, 0x0df0, 0x0df8, + 0x0e00, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38, + 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78, + 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8, + 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8, + 0x0f00, 0x0f08, 0x0f10, 0x0f18, 0x0f20, 0x0f28, 0x0f30, 0x0f38, + 0x0f40, 0x0f48, 0x0f50, 0x0f58, 0x0f60, 0x0f68, 0x0f70, 0x0f78, + 0x0f80, 0x0f88, 0x0f90, 0x0f98, 0x0fa0, 0x0fa8, 0x0fb0, 0x0fb8, + 0x0fc0, 0x0fc8, 0x0fd0, 0x0fd8, 0x0fe0, 0x0fe8, 0x0ff0, 0x0ff8, + 0x8ff8, 0x8ff0, 0x8fe8, 0x8fe0, 0x8fd8, 0x8fd0, 0x8fc8, 0x8fc0, + 0x8fb8, 0x8fb0, 0x8fa8, 0x8fa0, 0x8f98, 0x8f90, 0x8f88, 0x8f80, + 0x8f78, 0x8f70, 0x8f68, 0x8f60, 0x8f58, 0x8f50, 0x8f48, 0x8f40, + 0x8f38, 0x8f30, 0x8f28, 0x8f20, 0x8f18, 0x8f10, 0x8f08, 0x8f00, + 0x8ef8, 0x8ef0, 0x8ee8, 0x8ee0, 0x8ed8, 0x8ed0, 0x8ec8, 0x8ec0, + 0x8eb8, 0x8eb0, 0x8ea8, 0x8ea0, 0x8e98, 0x8e90, 0x8e88, 0x8e80, + 0x8e78, 0x8e70, 0x8e68, 0x8e60, 0x8e58, 0x8e50, 0x8e48, 0x8e40, + 0x8e38, 0x8e30, 0x8e28, 0x8e20, 0x8e18, 0x8e10, 0x8e08, 0x8e00, + 0x8df8, 0x8df0, 0x8de8, 0x8de0, 0x8dd8, 0x8dd0, 0x8dc8, 0x8dc0, + 0x8db8, 0x8db0, 0x8da8, 0x8da0, 0x8d98, 0x8d90, 0x8d88, 0x8d80, + 0x8d78, 0x8d70, 0x8d68, 0x8d60, 0x8d58, 0x8d50, 0x8d48, 0x8d40, + 0x8d38, 0x8d30, 0x8d28, 0x8d20, 0x8d18, 0x8d10, 0x8d08, 0x8d00, + 0x8cf8, 0x8cf0, 0x8ce8, 0x8ce0, 0x8cd8, 0x8cd0, 0x8cc8, 0x8cc0, + 0x8cb8, 0x8cb0, 0x8ca8, 0x8ca0, 0x8c98, 0x8c90, 0x8c88, 0x8c80, + 0x8c78, 0x8c70, 0x8c68, 0x8c60, 0x8c58, 0x8c50, 0x8c48, 0x8c40, + 0x8c38, 0x8c30, 0x8c28, 0x8c20, 0x8c18, 0x8c10, 0x8c08, 0x8c00, + 0x8bf8, 0x8bf0, 0x8be8, 0x8be0, 0x8bd8, 0x8bd0, 0x8bc8, 0x8bc0, + 0x8bb8, 0x8bb0, 0x8ba8, 0x8ba0, 0x8b98, 0x8b90, 0x8b88, 0x8b80, + 0x8b78, 0x8b70, 0x8b68, 0x8b60, 0x8b58, 0x8b50, 0x8b48, 0x8b40, + 0x8b38, 0x8b30, 0x8b28, 0x8b20, 0x8b18, 0x8b10, 0x8b08, 0x8b00, + 0x8af8, 0x8af0, 0x8ae8, 0x8ae0, 0x8ad8, 0x8ad0, 0x8ac8, 0x8ac0, + 0x8ab8, 0x8ab0, 0x8aa8, 0x8aa0, 0x8a98, 0x8a90, 0x8a88, 0x8a80, + 0x8a78, 0x8a70, 0x8a68, 0x8a60, 0x8a58, 0x8a50, 0x8a48, 0x8a40, + 0x8a38, 0x8a30, 0x8a28, 0x8a20, 0x8a18, 0x8a10, 0x8a08, 0x8a00, + 0x89f8, 0x89f0, 0x89e8, 0x89e0, 0x89d8, 0x89d0, 0x89c8, 0x89c0, + 0x89b8, 0x89b0, 0x89a8, 0x89a0, 0x8998, 0x8990, 0x8988, 0x8980, + 0x8978, 0x8970, 0x8968, 0x8960, 0x8958, 0x8950, 0x8948, 0x8940, + 0x8938, 0x8930, 0x8928, 0x8920, 0x8918, 0x8910, 0x8908, 0x8900, + 0x88f8, 0x88f0, 0x88e8, 0x88e0, 0x88d8, 0x88d0, 0x88c8, 0x88c0, + 0x88b8, 0x88b0, 0x88a8, 0x88a0, 0x8898, 0x8890, 0x8888, 0x8880, + 0x8878, 0x8870, 0x8868, 0x8860, 0x8858, 0x8850, 0x8848, 0x8840, + 0x8838, 0x8830, 0x8828, 0x8820, 0x8818, 0x8810, 0x8808, 0x8800, + 0x87f8, 0x87f0, 0x87e8, 0x87e0, 0x87d8, 0x87d0, 0x87c8, 0x87c0, + 0x87b8, 0x87b0, 0x87a8, 0x87a0, 0x8798, 0x8790, 0x8788, 0x8780, + 0x8778, 0x8770, 0x8768, 0x8760, 0x8758, 0x8750, 0x8748, 0x8740, + 0x8738, 0x8730, 0x8728, 0x8720, 0x8718, 0x8710, 0x8708, 0x8700, + 0x86f8, 0x86f0, 0x86e8, 0x86e0, 0x86d8, 0x86d0, 0x86c8, 0x86c0, + 0x86b8, 0x86b0, 0x86a8, 0x86a0, 0x8698, 0x8690, 0x8688, 0x8680, + 0x8678, 0x8670, 0x8668, 0x8660, 0x8658, 0x8650, 0x8648, 0x8640, + 0x8638, 0x8630, 0x8628, 0x8620, 0x8618, 0x8610, 0x8608, 0x8600, + 0x85f8, 0x85f0, 0x85e8, 0x85e0, 0x85d8, 0x85d0, 0x85c8, 0x85c0, + 0x85b8, 0x85b0, 0x85a8, 0x85a0, 0x8598, 0x8590, 0x8588, 0x8580, + 0x8578, 0x8570, 0x8568, 0x8560, 0x8558, 0x8550, 0x8548, 0x8540, + 0x8538, 0x8530, 0x8528, 0x8520, 0x8518, 0x8510, 0x8508, 0x8500, + 0x84f8, 0x84f0, 0x84e8, 0x84e0, 0x84d8, 0x84d0, 0x84c8, 0x84c0, + 0x84b8, 0x84b0, 0x84a8, 0x84a0, 0x8498, 0x8490, 0x8488, 0x8480, + 0x8478, 0x8470, 0x8468, 0x8460, 0x8458, 0x8450, 0x8448, 0x8440, + 0x8438, 0x8430, 0x8428, 0x8420, 0x8418, 0x8410, 0x8408, 0x8400, + 0x83f8, 0x83f0, 0x83e8, 0x83e0, 0x83d8, 0x83d0, 0x83c8, 0x83c0, + 0x83b8, 0x83b0, 0x83a8, 0x83a0, 0x8398, 0x8390, 0x8388, 0x8380, + 0x8378, 0x8370, 0x8368, 0x8360, 0x8358, 0x8350, 0x8348, 0x8340, + 0x8338, 0x8330, 0x8328, 0x8320, 0x8318, 0x8310, 0x8308, 0x8300, + 0x82f8, 0x82f0, 0x82e8, 0x82e0, 0x82d8, 0x82d0, 0x82c8, 0x82c0, + 0x82b8, 0x82b0, 0x82a8, 0x82a0, 0x8298, 0x8290, 0x8288, 0x8280, + 0x8278, 0x8270, 0x8268, 0x8260, 0x8258, 0x8250, 0x8248, 0x8240, + 0x8238, 0x8230, 0x8228, 0x8220, 0x8218, 0x8210, 0x8208, 0x8200, + 0x81f8, 0x81f0, 0x81e8, 0x81e0, 0x81d8, 0x81d0, 0x81c8, 0x81c0, + 0x81b8, 0x81b0, 0x81a8, 0x81a0, 0x8198, 0x8190, 0x8188, 0x8180, + 0x8178, 0x8170, 0x8168, 0x8160, 0x8158, 0x8150, 0x8148, 0x8140, + 0x8138, 0x8130, 0x8128, 0x8120, 0x8118, 0x8110, 0x8108, 0x8100, + 0x80f8, 0x80f0, 0x80e8, 0x80e0, 0x80d8, 0x80d0, 0x80c8, 0x80c0, + 0x80b8, 0x80b0, 0x80a8, 0x80a0, 0x8098, 0x8090, 0x8088, 0x8080, + 0x8078, 0x8070, 0x8068, 0x8060, 0x8058, 0x8050, 0x8048, 0x8040, + 0x8038, 0x8030, 0x8028, 0x8020, 0x8018, 0x8010, 0x8008, 0x8000, +}; + +/* + * Inverse exponent table extracted from OPL3 ROM; taken straight from + * Nuked OPL3 source code. + * TODO: Verify if ESFM uses an exponent table or if it possibly uses another + * method to skirt around Yamaha's patents? + * Optimization: All entries are shifted left by one from the actual data in + * OPL3's ROM. + */ +static const uint16_t exprom[256] = { + 0xff4, 0xfea, 0xfde, 0xfd4, 0xfc8, 0xfbe, 0xfb4, 0xfa8, + 0xf9e, 0xf92, 0xf88, 0xf7e, 0xf72, 0xf68, 0xf5c, 0xf52, + 0xf48, 0xf3e, 0xf32, 0xf28, 0xf1e, 0xf14, 0xf08, 0xefe, + 0xef4, 0xeea, 0xee0, 0xed4, 0xeca, 0xec0, 0xeb6, 0xeac, + 0xea2, 0xe98, 0xe8e, 0xe84, 0xe7a, 0xe70, 0xe66, 0xe5c, + 0xe52, 0xe48, 0xe3e, 0xe34, 0xe2a, 0xe20, 0xe16, 0xe0c, + 0xe04, 0xdfa, 0xdf0, 0xde6, 0xddc, 0xdd2, 0xdca, 0xdc0, + 0xdb6, 0xdac, 0xda4, 0xd9a, 0xd90, 0xd88, 0xd7e, 0xd74, + 0xd6a, 0xd62, 0xd58, 0xd50, 0xd46, 0xd3c, 0xd34, 0xd2a, + 0xd22, 0xd18, 0xd10, 0xd06, 0xcfe, 0xcf4, 0xcec, 0xce2, + 0xcda, 0xcd0, 0xcc8, 0xcbe, 0xcb6, 0xcae, 0xca4, 0xc9c, + 0xc92, 0xc8a, 0xc82, 0xc78, 0xc70, 0xc68, 0xc60, 0xc56, + 0xc4e, 0xc46, 0xc3c, 0xc34, 0xc2c, 0xc24, 0xc1c, 0xc12, + 0xc0a, 0xc02, 0xbfa, 0xbf2, 0xbea, 0xbe0, 0xbd8, 0xbd0, + 0xbc8, 0xbc0, 0xbb8, 0xbb0, 0xba8, 0xba0, 0xb98, 0xb90, + 0xb88, 0xb80, 0xb78, 0xb70, 0xb68, 0xb60, 0xb58, 0xb50, + 0xb48, 0xb40, 0xb38, 0xb32, 0xb2a, 0xb22, 0xb1a, 0xb12, + 0xb0a, 0xb02, 0xafc, 0xaf4, 0xaec, 0xae4, 0xade, 0xad6, + 0xace, 0xac6, 0xac0, 0xab8, 0xab0, 0xaa8, 0xaa2, 0xa9a, + 0xa92, 0xa8c, 0xa84, 0xa7c, 0xa76, 0xa6e, 0xa68, 0xa60, + 0xa58, 0xa52, 0xa4a, 0xa44, 0xa3c, 0xa36, 0xa2e, 0xa28, + 0xa20, 0xa18, 0xa12, 0xa0c, 0xa04, 0x9fe, 0x9f6, 0x9f0, + 0x9e8, 0x9e2, 0x9da, 0x9d4, 0x9ce, 0x9c6, 0x9c0, 0x9b8, + 0x9b2, 0x9ac, 0x9a4, 0x99e, 0x998, 0x990, 0x98a, 0x984, + 0x97c, 0x976, 0x970, 0x96a, 0x962, 0x95c, 0x956, 0x950, + 0x948, 0x942, 0x93c, 0x936, 0x930, 0x928, 0x922, 0x91c, + 0x916, 0x910, 0x90a, 0x904, 0x8fc, 0x8f6, 0x8f0, 0x8ea, + 0x8e4, 0x8de, 0x8d8, 0x8d2, 0x8cc, 0x8c6, 0x8c0, 0x8ba, + 0x8b4, 0x8ae, 0x8a8, 0x8a2, 0x89c, 0x896, 0x890, 0x88a, + 0x884, 0x87e, 0x878, 0x872, 0x86c, 0x866, 0x860, 0x85a, + 0x854, 0x850, 0x84a, 0x844, 0x83e, 0x838, 0x832, 0x82c, + 0x828, 0x822, 0x81c, 0x816, 0x810, 0x80c, 0x806, 0x800 +}; + +/* + * Frequency multiplier table multiplied by 2; taken straight from Nuked OPL3 + * source code. + */ +static const uint8_t mt[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 +}; + +/* + * This is used during the envelope generation to apply KSL to the envelope by + * determining how much to shift right the keyscale attenuation value before + * adding it to the envelope level. + */ +static const uint8_t kslshift[4] = { + 8, 1, 2, 0 +}; + +/* + * This encodes which emulation mode channels are the secondary channel in a + * 4-op channel pair (where the entry is non-negative), and which is the + * corresponding primary channel for that secondary channel. + */ +static const int emu_4op_secondary_to_primary[18] = +{ + -1, -1, -1, 0, 1, 2, -1, -1, -1, + -1, -1, -1, 9, 10, 11, -1, -1, -1 +}; + +/* + * Envelope generator dither table, taken straight from Nuked OPL3 source code. + */ +static const uint8_t eg_incstep[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +/* ------------------------------------------------------------------------- */ +static /*inline*/ int13 +ESFM_envelope_wavegen(uint3 waveform, int16 phase, uint10 envelope) +{ + int13 out; + uint16 lookup = logsinrom[((uint16)waveform << 10) | (phase & 0x3ff)]; + uint16 level = (lookup & 0x1fff) + (envelope << 3); + if (level > 0x1fff) + { + level = 0x1fff; + } + out = exprom[level & 0xff] >> (level >> 8); + if (lookup & 0x8000) + { + out = -out; + } + return out; +} + +/* ------------------------------------------------------------------------- */ +static void +ESFM_envelope_calc(esfm_slot *slot) +{ + uint8 nonzero; + uint8 rate; + uint5 rate_hi; + uint2 rate_lo; + uint4 reg_rate = 0; + uint4 ks; + uint8 eg_shift, shift; + esbool eg_off; + uint9 eg_rout; + int16 eg_inc; + esbool reset = 0; + esbool key_on; + esbool key_on_signal; + + key_on = *slot->in.key_on; + if (!slot->chip->native_mode) + { + int pair_primary_idx = emu_4op_secondary_to_primary[slot->channel->channel_idx]; + if (pair_primary_idx >= 0) + { + esfm_channel *pair_primary = &slot->channel->chip->channels[pair_primary_idx]; + if (pair_primary->emu_mode_4op_enable) + { + key_on = *pair_primary->slots[0].in.key_on; + } + } + else if ((slot->channel->channel_idx == 7 || slot->channel->channel_idx == 8) + && slot->slot_idx == 1) + { + key_on = slot->channel->key_on_2; + } + } + + slot->in.eg_output = slot->in.eg_position + (slot->t_level << 2) + + (slot->in.eg_ksl_offset >> kslshift[slot->ksl]); + if (slot->tremolo_en) + { + uint8 tremolo; + if (slot->chip->native_mode) + { + tremolo = slot->channel->chip->tremolo >> ((!slot->tremolo_deep << 1) + 2); + } + else + { + tremolo = slot->channel->chip->tremolo >> ((!slot->chip->emu_tremolo_deep << 1) + 2); + } + slot->in.eg_output += tremolo; + } + + if (slot->in.eg_delay_run && slot->in.eg_delay_counter < 32768) + { + slot->in.eg_delay_counter++; + } + + /* triggers on key-on edge */ + if (key_on && !slot->in.key_on_gate) + { + slot->in.eg_delay_run = 1; + slot->in.eg_delay_counter = 0; + slot->in.eg_delay_transitioned_01 = 0; + slot->in.eg_delay_transitioned_01_gate = 0; + slot->in.eg_delay_transitioned_10 = 0; + slot->in.eg_delay_transitioned_10_gate = 0; + slot->in.eg_delay_counter_compare = 0; + if (slot->env_delay > 0) + { + slot->in.eg_delay_counter_compare = 256 << slot->env_delay; + } + } + else if (!key_on) + { + slot->in.eg_delay_run = 0; + } + + /* TODO: is this really how the chip behaves? Can it only transition the envelope delay once? Am I implementing this in a sane way? I feel like this is a roundabout hack. */ + if ((slot->in.eg_delay_transitioned_10 && !slot->in.eg_delay_transitioned_10_gate) || + (slot->in.eg_delay_transitioned_01 && !slot->in.eg_delay_transitioned_01_gate) + ) + { + slot->in.eg_delay_counter_compare = 0; + if (slot->env_delay > 0) + { + slot->in.eg_delay_counter_compare = 256 << slot->env_delay; + } + if (slot->in.eg_delay_transitioned_10) + { + slot->in.eg_delay_transitioned_10_gate = 1; + } + if (slot->in.eg_delay_transitioned_01) + { + slot->in.eg_delay_transitioned_01_gate = 1; + } + } + + if (key_on && ((slot->in.eg_delay_counter >= slot->in.eg_delay_counter_compare) || !slot->chip->native_mode)) + { + key_on_signal = 1; + } else { + key_on_signal = 0; + } + + if (key_on && slot->in.eg_state == EG_RELEASE) + { + + if ((slot->in.eg_delay_counter >= slot->in.eg_delay_counter_compare) || !slot->chip->native_mode) + { + reset = 1; + reg_rate = slot->attack_rate; + } + else + { + reg_rate = slot->release_rate; + } + } + else + { + switch (slot->in.eg_state) + { + case EG_ATTACK: + reg_rate = slot->attack_rate; + break; + case EG_DECAY: + reg_rate = slot->decay_rate; + break; + case EG_SUSTAIN: + if (!slot->env_sustaining) + { + reg_rate = slot->release_rate; + } + break; + case EG_RELEASE: + reg_rate = slot->release_rate; + break; + } + } + slot->in.key_on_gate = key_on; + slot->in.phase_reset = reset; + ks = slot->in.keyscale >> ((!slot->ksr) << 1); + nonzero = (reg_rate != 0); + rate = ks + (reg_rate << 2); + rate_hi = rate >> 2; + rate_lo = rate & 0x03; + if (rate_hi & 0x10) + { + rate_hi = 0x0f; + } + eg_shift = rate_hi + slot->chip->eg_clocks; + shift = 0; + if (nonzero) + { + if (rate_hi < 12) + { + if (slot->chip->eg_tick) + { + switch (eg_shift) + { + case 12: + shift = 1; + break; + case 13: + shift = (rate_lo >> 1) & 0x01; + break; + case 14: + shift = rate_lo & 0x01; + break; + default: + break; + } + } + } + else + { + shift = (rate_hi & 0x03) + + eg_incstep[rate_lo][slot->chip->global_timer & 0x03]; + if (shift & 0x04) + { + shift = 0x03; + } + if (!shift) + { + shift = slot->chip->eg_tick; + } + } + } + eg_rout = slot->in.eg_position; + eg_inc = 0; + eg_off = 0; + /* Instant attack */ + if (reset && rate_hi == 0x0f) + { + eg_rout = 0x00; + } + /* Envelope off */ + if ((slot->in.eg_position & 0x1f8) == 0x1f8) + { + eg_off = 1; + } + if (slot->in.eg_state != EG_ATTACK && !reset && eg_off) + { + eg_rout = 0x1ff; + } + switch (slot->in.eg_state) + { + case EG_ATTACK: + if (slot->in.eg_position == 0) + { + slot->in.eg_state = EG_DECAY; + } + else if (key_on_signal && shift > 0 && rate_hi != 0x0f) + { + eg_inc = ~slot->in.eg_position >> (4 - shift); + } + break; + case EG_DECAY: + if ((slot->in.eg_position >> 4) == slot->sustain_lvl) + { + slot->in.eg_state = EG_SUSTAIN; + } + else if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + case EG_SUSTAIN: + case EG_RELEASE: + if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + } + slot->in.eg_position = (eg_rout + eg_inc) & 0x1ff; + /* Key off */ + if (reset) + { + slot->in.eg_state = EG_ATTACK; + } + if (!key_on_signal) + { + slot->in.eg_state = EG_RELEASE; + } +} + +/* ------------------------------------------------------------------------- */ +static void +ESFM_phase_generate(esfm_slot *slot) +{ + esfm_chip *chip; + uint10 f_num; + uint32 basefreq; + esbool rm_xor, n_bit; + uint23 noise; + uint10 phase; + + chip = slot->chip; + f_num = slot->f_num; + if (slot->vibrato_en) + { + int8_t range; + uint8_t vibpos; + + range = (f_num >> 7) & 7; + vibpos = chip->vibrato_pos; + + if (!(vibpos & 3)) + { + range = 0; + } + else if (vibpos & 1) + { + range >>= 1; + } + range >>= !slot->vibrato_deep; + + if (vibpos & 4) + { + range = -range; + } + f_num += range; + } + basefreq = (f_num << slot->block) >> 1; + phase = (uint10)(slot->in.phase_acc >> 9); + if (slot->in.phase_reset) + { + slot->in.phase_acc = 0; + } + slot->in.phase_acc += (basefreq * mt[slot->mult]) >> 1; + slot->in.phase_acc &= (1 << 19) - 1; + slot->in.phase_out = phase; + /* Noise mode (rhythm) sounds */ + noise = chip->lfsr; + if (slot->slot_idx == 3 && slot->rhy_noise) + { + esfm_slot *prev_slot = &slot->channel->slots[2]; + + chip->rm_hh_bit2 = (phase >> 2) & 1; + chip->rm_hh_bit3 = (phase >> 3) & 1; + chip->rm_hh_bit7 = (phase >> 7) & 1; + chip->rm_hh_bit8 = (phase >> 8) & 1; + + chip->rm_tc_bit3 = (prev_slot->in.phase_out >> 3) & 1; + chip->rm_tc_bit5 = (prev_slot->in.phase_out >> 5) & 1; + + rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) + | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) + | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); + + switch(slot->rhy_noise) + { + case 1: + /* SD */ + slot->in.phase_out = (chip->rm_hh_bit8 << 9) + | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8); + break; + case 2: + /* HH */ + slot->in.phase_out = rm_xor << 9; + if (rm_xor ^ (noise & 1)) + { + slot->in.phase_out |= 0xd0; + } + else + { + slot->in.phase_out |= 0x34; + } + break; + case 3: + /* TC */ + slot->in.phase_out = (rm_xor << 9) | 0x80; + break; + } + } + + n_bit = ((noise >> 14) ^ noise) & 0x01; + chip->lfsr = (noise >> 1) | (n_bit << 22); +} + +/* ------------------------------------------------------------------------- */ +static void +ESFM_phase_generate_emu(esfm_slot *slot) +{ + esfm_chip *chip; + uint3 block; + uint10 f_num; + uint32 basefreq; + esbool rm_xor, n_bit; + uint23 noise; + uint10 phase; + int pair_primary_idx; + + chip = slot->chip; + block = slot->channel->slots[0].block; + f_num = slot->channel->slots[0].f_num; + + pair_primary_idx = emu_4op_secondary_to_primary[slot->channel->channel_idx]; + if (pair_primary_idx >= 0) + { + esfm_channel *pair_primary = &slot->channel->chip->channels[pair_primary_idx]; + if (pair_primary->emu_mode_4op_enable) + { + block = pair_primary->slots[0].block; + f_num = pair_primary->slots[0].f_num; + } + } + + if (slot->vibrato_en) + { + int8_t range; + uint8_t vibpos; + + range = (f_num >> 7) & 7; + vibpos = chip->vibrato_pos; + + if (!(vibpos & 3)) + { + range = 0; + } + else if (vibpos & 1) + { + range >>= 1; + } + range >>= !chip->emu_vibrato_deep; + + if (vibpos & 4) + { + range = -range; + } + f_num += range; + } + basefreq = (f_num << block) >> 1; + phase = (uint10)(slot->in.phase_acc >> 9); + if (slot->in.phase_reset) + { + slot->in.phase_acc = 0; + } + slot->in.phase_acc += (basefreq * mt[slot->mult]) >> 1; + slot->in.phase_acc &= (1 << 19) - 1; + slot->in.phase_out = phase; + + /* Noise mode (rhythm) sounds */ + noise = chip->lfsr; + /* HH */ + if (slot->channel->channel_idx == 7 && slot->slot_idx == 0) + { + chip->rm_hh_bit2 = (phase >> 2) & 1; + chip->rm_hh_bit3 = (phase >> 3) & 1; + chip->rm_hh_bit7 = (phase >> 7) & 1; + chip->rm_hh_bit8 = (phase >> 8) & 1; + } + /* TC */ + if (slot->channel->channel_idx == 8 && slot->slot_idx == 1) + { + chip->rm_tc_bit3 = (phase >> 3) & 1; + chip->rm_tc_bit5 = (phase >> 5) & 1; + } + if (chip->emu_rhy_mode_flags & 0x20) + { + rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) + | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) + | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); + if (slot->channel->channel_idx == 7) + { + if (slot->slot_idx == 0) { + /* HH */ + slot->in.phase_out = rm_xor << 9; + if (rm_xor ^ (noise & 1)) + { + slot->in.phase_out |= 0xd0; + } + else + { + slot->in.phase_out |= 0x34; + } + } + else if (slot->slot_idx == 1) + { + /* SD */ + slot->in.phase_out = (chip->rm_hh_bit8 << 9) + | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8); + } + } + else if (slot->channel->channel_idx == 8 && slot->slot_idx == 1) + { + /* TC */ + slot->in.phase_out = (rm_xor << 9) | 0x80; + } + } + + n_bit = ((noise >> 14) ^ noise) & 0x01; + chip->lfsr = (noise >> 1) | (n_bit << 22); +} + +/** + * TODO: Figure out what's ACTUALLY going on inside the real chip! + * This is not accurate at all, but it's the closest I was able to get with + * empirical testing (and it's closer than nothing). + */ +/* ------------------------------------------------------------------------- */ +static int16 +ESFM_slot3_noise3_mod_input_calc(esfm_slot *slot) +{ + esfm_channel *channel = slot->channel; + int16 phase; + int13 output_buf = *channel->slots[1].in.mod_input; + int i; + + /* Go through previous slots' partial results and recalculate outputs */ + /* (we skip slot 0 because its calculation happens at the end, not at the beginning) */ + for (i = 1; i < 3; i++) + { + /* double the pitch */ + phase = channel->slots[i].in.phase_acc >> 8; + if (channel->slots[i].mod_in_level) + { + phase += output_buf >> (7 - channel->slots[i].mod_in_level); + } + output_buf = ESFM_envelope_wavegen(channel->slots[2].waveform, phase, channel->slots[i].in.eg_output); + } + + return output_buf >> (8 - slot->mod_in_level); +} + +/* ------------------------------------------------------------------------- */ +static void +ESFM_slot_generate(esfm_slot *slot) +{ + int16 phase = slot->in.phase_out; + if (slot->mod_in_level) + { + if (slot->slot_idx == 3 && slot->rhy_noise == 3) + { + phase += ESFM_slot3_noise3_mod_input_calc(slot); + } + else + { + phase += *slot->in.mod_input >> (7 - slot->mod_in_level); + } + } + slot->in.output = ESFM_envelope_wavegen(slot->waveform, phase, slot->in.eg_output); + if (slot->output_level) + { + int13 output_value = slot->in.output >> (7 - slot->output_level); + slot->channel->output[0] += output_value & slot->out_enable[0]; + slot->channel->output[1] += output_value & slot->out_enable[1]; + } +} + +/* ------------------------------------------------------------------------- */ +static void +ESFM_slot_generate_emu(esfm_slot *slot) +{ + const esfm_chip *chip = slot->chip; + uint3 waveform = slot->waveform & (chip->emu_newmode != 0 ? 0x07 : 0x03); + esbool rhythm_slot_double_volume = (slot->chip->emu_rhy_mode_flags & 0x20) != 0 + && slot->channel->channel_idx >= 6 && slot->channel->channel_idx < 9; + int16 phase = slot->in.phase_out; + int14 output_value; + + phase += *slot->in.mod_input & slot->in.emu_mod_enable; + slot->in.output = ESFM_envelope_wavegen(waveform, phase, slot->in.eg_output); + output_value = (slot->in.output & slot->in.emu_output_enable) << rhythm_slot_double_volume; + if (chip->emu_newmode) + { + slot->channel->output[0] += output_value & slot->channel->slots[0].out_enable[0]; + slot->channel->output[1] += output_value & slot->channel->slots[0].out_enable[1]; + } + else + { + slot->channel->output[0] += output_value; + slot->channel->output[1] += output_value; + } +} + +/* ------------------------------------------------------------------------- */ +#ifndef _MSC_VER +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wunused-variable" +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#endif +static void +ESFM_process_feedback(esfm_chip *chip) +{ + int channel_idx; + + for (channel_idx = 0; channel_idx < 18; channel_idx++) + { + esfm_slot *slot = &chip->channels[channel_idx].slots[0]; + uint32 basefreq, phase_offset; + uint3 block; + uint10 f_num; + int32_t wave_out, wave_last; + int32_t phase_feedback; + uint32_t iter_counter; + uint3 waveform; + uint3 mod_in_shift; + uint32_t phase, phase_acc; + uint10 eg_output; +#if defined(__GNUC__) && defined(__i386__) && !defined(__x86_64__) && !defined(__arm__) && !defined(_ESFMU_DISABLE_ASM_OPTIMIZATIONS) + size_t logsinrom_addr; + size_t exprom_addr; +#endif + + if (slot->mod_in_level && (chip->native_mode || (slot->in.mod_input == &slot->in.feedback_buf))) + { + if (chip->native_mode) + { + waveform = slot->waveform; + } + else + { + waveform = slot->waveform & (0x03 | (0x02 << (chip->emu_newmode != 0))); + } + f_num = slot->f_num; + block = slot->block; + basefreq = (f_num << block) >> 1; + phase_offset = (basefreq * mt[slot->mult]) >> 1; + mod_in_shift = 7 - slot->mod_in_level; + phase_acc = (uint32_t)(slot->in.phase_acc - phase_offset * 28); + eg_output = slot->in.eg_output; + + /* ASM optimizaions! */ +#if defined(__GNUC__) && defined(__x86_64__) && !defined(_ESFMU_DISABLE_ASM_OPTIMIZATIONS) + asm ( + "movzbq %[wave], %%r8 \n\t" + "shll $11, %%r8d \n\t" + "leaq %[sinrom], %%rax \n\t" + "addq %%rax, %%r8 \n\t" + "leaq %[exprom], %%r9 \n\t" + "movzwl %[eg_out], %%r10d \n\t" + "shll $3, %%r10d \n\t" + "xorl %%r11d, %%r11d \n\t" + "movl %%r11d, %[out] \n\t" + "movl $29, %%edx \n" + "1: \n\t" + /* phase_feedback = (wave_out + wave_last) >> 2; */ + "movl %[out], %[p_fb] \n\t" + "addl %%r11d, %[p_fb] \n\t" + "sarl $2, %[p_fb] \n\t" + /* wave_last = wave_out */ + "movl %[out], %%r11d \n\t" + /* phase = phase_feedback >> mod_in_shift; */ + "movl %[p_fb], %%eax \n\t" + "movb %[mod_in], %%cl \n\t" + "sarl %%cl, %%eax \n\t" + /* phase += phase_acc >> 9; */ + "movl %[p_acc], %%ebx \n\t" + "sarl $9, %%ebx \n\t" + "addl %%ebx, %%eax \n\t" + /* lookup = logsinrom[(waveform << 10) | (phase & 0x3ff)]; */ + "andq $0x3ff, %%rax \n\t" + "movzwl (%%r8, %%rax, 2), %%ebx \n\t" + "movl %%ebx, %%eax \n\t" + /* level = (lookup & 0x1fff) + (envelope << 3); */ + "movl $0x1fff, %%ecx \n\t" + "andl %%ecx, %%eax \n\t" + "addl %%r10d, %%eax \n\t" + /* if (level > 0x1fff) level = 0x1fff; */ + "cmpl %%ecx, %%eax \n\t" + "cmoval %%ecx, %%eax \n\t" + /* wave_out = exprom[level & 0xff] >> (level >> 8); */ + "movb %%ah, %%cl \n\t" + "movzbl %%al, %%eax \n\t" + "movzwl (%%r9, %%rax, 2), %[out] \n\t" + "shrl %%cl, %[out] \n\t" + /* if (lookup & 0x8000) wave_out = -wave_out; */ + /* in other words, lookup is negative */ + "movl %[out], %%ecx \n\t" + "negl %%ecx \n\t" + "testw %%bx, %%bx \n\t" + "cmovsl %%ecx, %[out] \n\t" + /* phase_acc += phase_offset */ + "addl %[p_off], %[p_acc] \n\t" + /* loop* */ + "decl %%edx \n\t" + "jne 1b \n\t" + : [p_fb] "=&r" (phase_feedback), + [p_acc] "+r" (phase_acc), + [out] "=&r" (wave_out) + : [p_off] "r" (phase_offset), + [mod_in] "r" (mod_in_shift), + [wave] "g" (waveform), + [eg_out] "g" (eg_output), + [sinrom] "m" (logsinrom), + [exprom] "m" (exprom) + : "cc", "ax", "bx", "cx", "dx", "r8", "r9", "r10", "r11" + ); +#elif defined(__GNUC__) && defined(__i386__) && !defined(_ESFMU_DISABLE_ASM_OPTIMIZATIONS) + logsinrom_addr = (size_t)logsinrom; + exprom_addr = (size_t)exprom; + + asm ( + "movzbl %b[wave], %%eax \n\t" + "shll $11, %%eax \n\t" + "movl %[sinrom], %%edi \n\t" + "addl %%eax, %%edi \n\t" + "shlw $3, %[eg_out] \n\t" + "xorl %[out], %[out] \n\t" + "movl %[out], %[last] \n\t" + "movl $29, %[i] \n" + "1: \n\t" + /* phase_feedback = (wave_out + wave_last) >> 2; */ + "movl %[out], %%eax \n\t" + "addl %[last], %%eax \n\t" + "sarl $2, %%eax \n\t" + "movl %%eax, %[p_fb] \n\t" + /* wave_last = wave_out */ + "movl %[out], %[last] \n\t" + /* phase = phase_feedback >> mod_in_shift; */ + "movb %[mod_in], %%cl \n\t" + "sarl %%cl, %%eax \n\t" + /* phase += phase_acc >> 9; */ + "movl %[p_acc], %%ebx \n\t" + "shrl $9, %%ebx \n\t" + "addl %%ebx, %%eax \n\t" + /* lookup = logsinrom[(waveform << 10) | (phase & 0x3ff)]; */ + "andl $0x3ff, %%eax \n\t" + "movzwl (%%edi, %%eax, 2), %%ebx \n\t" + "movl %%ebx, %%eax \n\t" + /* level = (lookup & 0x1fff) + (envelope << 3); */ + "movl $0x1fff, %%ecx \n\t" + "andl %%ecx, %%eax \n\t" + "addw %[eg_out], %%ax \n\t" + /* if (level > 0x1fff) level = 0x1fff; */ + "cmpl %%ecx, %%eax \n\t" + "cmoval %%ecx, %%eax \n\t" + /* wave_out = exprom[level & 0xff] >> (level >> 8); */ + "movb %%ah, %%cl \n\t" + "movzbl %%al, %%eax \n\t" + "movl %[exprom], %[out] \n\t" + "movzwl (%[out], %%eax, 2), %[out] \n\t" + "shrl %%cl, %[out] \n\t" + /* if (lookup & 0x8000) wave_out = -wave_out; */ + /* in other words, lookup is negative */ + "movl %[out], %%ecx \n\t" + "negl %%ecx \n\t" + "testw %%bx, %%bx \n\t" + "cmovsl %%ecx, %[out] \n\t" + /* phase_acc += phase_offset */ + "addl %[p_off], %[p_acc] \n\t" + /* loop */ + "decl %[i] \n\t" + "jne 1b \n\t" + : [p_fb] "=&m" (phase_feedback), + [p_acc] "+r" (phase_acc), + [out] "=&r" (wave_out), + [last] "=&m" (wave_last), + [eg_out] "+m" (eg_output) + : [p_off] "m" (phase_offset), + [mod_in] "m" (mod_in_shift), + [wave] "m" (waveform), + [sinrom] "m" (logsinrom_addr), + [exprom] "m" (exprom_addr), + [i] "m" (iter_counter) + : "cc", "ax", "bx", "cx", "di" + ); +#elif defined(__GNUC__) && defined(__arm__) && !defined(_ESFMU_DISABLE_ASM_OPTIMIZATIONS) + asm ( + "movs r3, #0 \n\t" + "movs %[out], #0 \n\t" + "ldr r8, =0x1fff \n\t" + "movs r2, #29 \n" + "1: \n\t" + /* phase_feedback = (wave_out + wave_last) >> 2; */ + "adds %[p_fb], %[out], r3 \n\t" + "asrs %[p_fb], %[p_fb], #2 \n\t" + /* wave_last = wave_out */ + "mov r3, %[out] \n\t" + /* phase = phase_feedback >> mod_in_shift; */ + "asr r0, %[p_fb], %[mod_in] \n\t" + /* phase += phase_acc >> 9; */ + "add r0, r0, %[p_acc], asr #9 \n\t" + /* lookup = logsinrom[(waveform << 10) | (phase & 0x3ff)]; */ + "lsls r0, r0, #22 \n\t" + "lsrs r0, r0, #21 \n\t" + "ldrsh r1, [%[sinrom], r0] \n\t" + /* level = (lookup & 0x1fff) + (envelope << 3); */ + "and r0, r8, r1 \n\t" + "add r0, r0, %[eg_out], lsl #3 \n\t" + /* if (level > 0x1fff) level = 0x1fff; */ + "cmp r0, r8 \n\t" + "it hi \n\t" + "movhi r0, r8 \n\t" + /* wave_out = exprom[level & 0xff] >> (level >> 8); */ + "lsrs %[out], r0, #8 \n\t" + "ands r0, r0, #255 \n\t" + "lsls r0, r0, #1 \n\t" + "ldrh r0, [%[exprom], r0] \n\t" + "lsr %[out], r0, %[out] \n\t" + /* if (lookup & 0x8000) wave_out = -wave_out; */ + /* in other words, lookup is negative */ + "tst r1, r1 \n\t" + "it mi \n\t" + "negmi %[out], %[out] \n\t" + /* phase_acc += phase_offset */ + "adds %[p_acc], %[p_acc], %[p_off]\n\t" + /* loop */ + "subs r2, r2, #1 \n\t" + "bne 1b \n\t" + : [p_fb] "=&r" (phase_feedback), + [p_acc] "+r" (phase_acc), + [out] "=&r" (wave_out) + : [p_off] "r" (phase_offset), + [mod_in] "r" (mod_in_shift), + [eg_out] "r" (eg_output), + [sinrom] "r" (logsinrom + waveform * 1024), + [exprom] "r" (exprom) + : "cc", "r0", "r1", "r2", "r3", "r8" + ); +#else + wave_out = 0; + wave_last = 0; + for (iter_counter = 0; iter_counter < 29; iter_counter++) + { + phase_feedback = (wave_out + wave_last) >> 2; + wave_last = wave_out; + phase = phase_feedback >> mod_in_shift; + phase += phase_acc >> 9; + wave_out = ESFM_envelope_wavegen(waveform, phase, eg_output); + phase_acc += phase_offset; + } +#endif + + /* TODO: Figure out - is this how the ESFM chip does it, like the */ + /* patent literally says? (it's really hacky...) */ + /* slot->in.output = wave_out; */ + + /* This would be the more canonical way to do it, reusing the rest of */ + /* the synthesis pipeline to finish the calculation: */ + if (chip->native_mode) + { + slot->in.feedback_buf = phase_feedback; + } + else + { + slot->in.feedback_buf = phase_feedback >> (7 - slot->mod_in_level); + } + } + } +} + +/* ------------------------------------------------------------------------- */ +static void +ESFM_process_channel(esfm_channel *channel) +{ + int slot_idx; + channel->output[0] = channel->output[1] = 0; + for (slot_idx = 0; slot_idx < 4; slot_idx++) + { + esfm_slot *slot = &channel->slots[slot_idx]; + ESFM_envelope_calc(slot); + ESFM_phase_generate(slot); + if(slot_idx > 0) + { + ESFM_slot_generate(slot); + } + } + /* ESFM feedback calculation takes a large number of clock cycles, so + // defer slot 0 generation to the end + // TODO: verify this behavior on real hardware */ +} + +/* ------------------------------------------------------------------------- */ +static void +ESFM_process_channel_emu(esfm_channel *channel) +{ + int slot_idx; + channel->output[0] = channel->output[1] = 0; + for (slot_idx = 0; slot_idx < 2; slot_idx++) + { + esfm_slot *slot = &channel->slots[slot_idx]; + ESFM_envelope_calc(slot); + ESFM_phase_generate_emu(slot); + if(slot_idx > 0) + { + ESFM_slot_generate_emu(slot); + } + } + /* ESFM feedback calculation takes a large number of clock cycles, so + // defer slot 0 generation to the end + // TODO: verify this behavior on real hardware */ +} + +/* ------------------------------------------------------------------------- */ +static int16_t +ESFM_clip_sample(int32 sample) +{ + /* TODO: Supposedly, the real ESFM chip actually overflows rather than + // clipping. Verify that. */ + if (sample > 32767) + { + sample = 32767; + } + else if (sample < -32768) + { + sample = -32768; + } + return (int16_t)sample; +} + +#define TIMER1_CONST (0.2517482517482517) +#define TIMER2_CONST (0.06293706293706293) +/* ------------------------------------------------------------------------- */ +static void +ESFM_update_timers(esfm_chip *chip) +{ + int i; + /* Tremolo */ + if ((chip->global_timer & 0x3f) == 0x3f) + { + chip->tremolo_pos = (chip->tremolo_pos + 1) % 210; + if (chip->tremolo_pos < 105) + { + chip->tremolo = chip->tremolo_pos; + } + else + { + chip->tremolo = (210 - chip->tremolo_pos); + } + } + + /* Vibrato */ + if ((chip->global_timer & 0x3ff) == 0x3ff) + { + chip->vibrato_pos = (chip->vibrato_pos + 1) & 0x07; + } + + chip->global_timer = (chip->global_timer + 1) & 0x3ff; + + /* Envelope generator dither clocks */ + chip->eg_clocks = 0; + if (chip->eg_timer) + { + uint8 shift = 0; + while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0) + { + shift++; + } + + if (shift <= 12) + { + chip->eg_clocks = shift + 1; + } + } + + if (chip->eg_tick || chip->eg_timer_overflow) + { + if (chip->eg_timer == (1llu << 36) - 1) + { + chip->eg_timer = 0; + chip->eg_timer_overflow = 1; + } + else + { + chip->eg_timer++; + chip->eg_timer_overflow = 0; + } + } + + for (i = 0; i < 2; i++) + { + if (chip->timer_enable[i]) + { + chip->timer_accumulator[i] += (i == 0) ? TIMER1_CONST : TIMER2_CONST; + if (chip->timer_accumulator[i] > 1.0) + { + chip->timer_accumulator[i] -= 1.0; + chip->timer_counter[i]++; + if (chip->timer_counter[i] == 0) + { + if (chip->timer_mask[i] == 0) + { + chip->irq_bit = estrue; + chip->timer_overflow[i] = estrue; + } + chip->timer_counter[i] = chip->timer_reload[i]; + } + } + } + } + + chip->eg_tick ^= 1; +} + +#define KEY_ON_REGS_START (18 * 4 * 8) +/* ------------------------------------------------------------------------- */ +int +ESFM_reg_write_chan_idx(esfm_chip *chip, uint16_t reg) +{ + int which_reg = -1; + if (chip->native_mode) + { + esbool is_key_on_reg = reg >= KEY_ON_REGS_START && reg < (KEY_ON_REGS_START + 20); + if (is_key_on_reg) + { + which_reg = reg - KEY_ON_REGS_START; + } + } + else + { + uint8_t reg_low = reg & 0xff; + esbool high = (reg & 0x100) != 0; + esbool is_key_on_reg = reg_low >= 0xb0 && reg_low < 0xb9; + if (is_key_on_reg) + { + which_reg = (reg_low & 0x0f) + high * 9; + } + } + + return which_reg; +} + +/* ------------------------------------------------------------------------- */ +void +ESFM_update_write_buffer(esfm_chip *chip) +{ + esfm_write_buf *write_buf; + esbool note_off_written[20]; + esbool bassdrum_written = esfalse; + int i; + for (i = 0; i < 20; i++) + { + note_off_written[i] = esfalse; + } + while((write_buf = &chip->write_buf[chip->write_buf_start]), + write_buf->valid && write_buf->timestamp <= chip->write_buf_timestamp) + { + int is_which_note_on_reg = + ESFM_reg_write_chan_idx(chip, write_buf->address); + if (is_which_note_on_reg >= 0) + { + if ((chip->native_mode && (write_buf->data & 0x01) == 0) + || (!chip->native_mode && (write_buf->data & 0x20) == 0) + ) + { + /* this is a note off command; note down that we got note off for this channel */ + note_off_written[is_which_note_on_reg] = estrue; + } + else + { + /* this is a note on command; have we gotten a note off for this channel in this cycle? */ + if (note_off_written[is_which_note_on_reg]) + { + /* we have a conflict; let the note off be processed first and defer the + // rest of the buffer to the next cycle */ + break; + } + } + } + if ((chip->native_mode && write_buf->address == 0x4bd) + || (!chip->native_mode && (write_buf->address & 0xff) == 0xbd) + ) + { + /* bassdrum register write (rhythm mode note-on/off control) + // have we already written to the bassdrum register in this cycle */ + if (bassdrum_written) { + /* we have a conflict */ + break; + } + bassdrum_written = estrue; + } + + write_buf->valid = 0; + ESFM_write_reg(chip, write_buf->address, write_buf->data); + chip->write_buf_start = (chip->write_buf_start + 1) % ESFM_WRITEBUF_SIZE; + } + + chip->write_buf_timestamp++; +} + +/* ------------------------------------------------------------------------- */ +void +ESFM_generate(esfm_chip *chip, int16_t *buf) +{ + int channel_idx; + + chip->output_accm[0] = chip->output_accm[1] = 0; + for (channel_idx = 0; channel_idx < 18; channel_idx++) + { + esfm_channel *channel = &chip->channels[channel_idx]; + if (chip->native_mode) + { + ESFM_process_channel(channel); + } + else + { + ESFM_process_channel_emu(channel); + } + } + ESFM_process_feedback(chip); + for (channel_idx = 0; channel_idx < 18; channel_idx++) + { + esfm_channel *channel = &chip->channels[channel_idx]; + if (chip->native_mode) + { + ESFM_slot_generate(&channel->slots[0]); + } + else + { + ESFM_slot_generate_emu(&channel->slots[0]); + } + chip->output_accm[0] += channel->output[0]; + chip->output_accm[1] += channel->output[1]; + } + + buf[0] = ESFM_clip_sample(chip->output_accm[0]); + buf[1] = ESFM_clip_sample(chip->output_accm[1]); + + ESFM_update_timers(chip); + ESFM_update_write_buffer(chip); +} + +/* ------------------------------------------------------------------------- */ +int16_t +ESFM_get_channel_output_native(esfm_chip *chip, int channel_idx) +{ + int16_t result; + int32_t temp_mix = 0; + int i; + + if (channel_idx < 0 || channel_idx >= 18) + { + return 0; + } + + for (i = 0; i < 4; i++) + { + esfm_slot *slot = &chip->channels[channel_idx].slots[i]; + + if (slot->output_level) + { + int13 output_value = slot->in.output >> (7 - slot->output_level); + temp_mix += output_value & slot->out_enable[0]; + temp_mix += output_value & slot->out_enable[1]; + } + } + + if (temp_mix > 32767) + { + temp_mix = 32767; + } + else if (temp_mix < -32768) + { + temp_mix = -32768; + } + result = temp_mix; + return result; +} + +/* ------------------------------------------------------------------------- */ +void +ESFM_generate_stream(esfm_chip *chip, int16_t *sndptr, uint32_t num_samples) +{ + uint32_t i; + + for (i = 0; i < num_samples; i++) + { + ESFM_generate(chip, sndptr); + sndptr += 2; + } +} diff --git a/thirdparty/adlmidi/chips/esfmu/esfm.h b/thirdparty/adlmidi/chips/esfmu/esfm.h new file mode 100644 index 000000000..0a1ff74ef --- /dev/null +++ b/thirdparty/adlmidi/chips/esfmu/esfm.h @@ -0,0 +1,293 @@ +/* + * ESFMu: emulator for the ESS "ESFM" enhanced OPL3 clone + * Copyright (C) 2023 Kagamiin~ + * + * ESFMu 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. + * + * ESFMu 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 ESFMu. If not, see . + */ + +/* + * ESFMu wouldn't have been possible without the hard work and dedication of + * the retro computer hardware research and preservation community. + * + * I'd like to thank: + * - Nuke.YKT + * Developer of Nuked OPL3, which was the basis for ESFMu's code and + * also a great learning resource on Yamaha FM synthesis for myself. + * Nuke.YKT also gives shoutouts on behalf of Nuked OPL3 to: + * - MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * - forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * - OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * - siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * - rainwarrior + * For performing the initial research on ESFM drivers and documenting + * ESS's patent on native mode operator organization. + * - jwt27 + * For kickstarting the ESFM research project and compiling rainwarrior's + * findings and more in an accessible document ("ESFM Demystified"). + * - pachuco/CatButts + * For documenting ESS's patent on ESFM's feedback implementation, which + * was vital in getting ESFMu's sound output to be accurate. + * - akumanatt + * For helping out with code optimization. + * - And everybody who helped out with real hardware testing + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _esfm_slot esfm_slot; +/* typedef struct _esfm_slot_internal esfm_slot_internal; */ +typedef struct _esfm_channel esfm_channel; +typedef struct _esfm_chip esfm_chip; + + +void ESFM_init (esfm_chip *chip); +void ESFM_write_reg (esfm_chip *chip, uint16_t address, uint8_t data); +void ESFM_write_reg_buffered (esfm_chip *chip, uint16_t address, uint8_t data); +void ESFM_write_reg_buffered_fast (esfm_chip *chip, uint16_t address, uint8_t data); +void ESFM_write_port (esfm_chip *chip, uint8_t offset, uint8_t data); +uint8_t ESFM_readback_reg (esfm_chip *chip, uint16_t address); +uint8_t ESFM_read_port (esfm_chip *chip, uint8_t offset); +void ESFM_generate(esfm_chip *chip, int16_t *buf); +void ESFM_generate_stream(esfm_chip *chip, int16_t *sndptr, uint32_t num_samples); +int16_t ESFM_get_channel_output_native(esfm_chip *chip, int channel_idx); + + +/* These are fake types just for syntax sugar. */ +/* Beware of their underlying types when reading/writing to them. */ +typedef int8_t esbool; +#define estrue 1 +#define esfalse 0 +typedef uint8_t flag; +typedef uint8_t uint2; +typedef uint8_t uint3; +typedef uint8_t uint4; +typedef uint8_t uint5; +typedef uint8_t uint6; +typedef uint8_t uint8; +typedef uint16_t uint9; +typedef uint16_t uint10; +typedef uint16_t uint11; +typedef uint16_t uint12; +typedef uint16_t uint16; +typedef uint32_t uint19; +typedef uint32_t uint23; +typedef uint32_t uint32; +typedef uint64_t uint36; + +typedef int16_t int13; +typedef int16_t int14; +typedef int16_t int16; +typedef int32_t int32; + +enum eg_states +{ + EG_ATTACK, + EG_DECAY, + EG_SUSTAIN, + EG_RELEASE +}; + + +typedef struct _esfm_write_buf +{ + uint64_t timestamp; + uint16_t address; + uint8_t data; + flag valid; + +} esfm_write_buf; + +typedef struct _emu_slot_channel_mapping +{ + int channel_idx; + int slot_idx; + +} emu_slot_channel_mapping; + +typedef struct _esfm_slot_internal +{ + uint9 eg_position; + uint9 eg_ksl_offset; + uint10 eg_output; + + uint4 keyscale; + + int13 output; + int13 emu_output_enable; + int13 emu_mod_enable; + int13 feedback_buf; + int13 *mod_input; + + uint19 phase_acc; + uint10 phase_out; + flag phase_reset; + flag *key_on; + flag key_on_gate; + + uint2 eg_state; + flag eg_delay_run; + flag eg_delay_transitioned_10; + flag eg_delay_transitioned_10_gate; + flag eg_delay_transitioned_01; + flag eg_delay_transitioned_01_gate; + uint16 eg_delay_counter; + uint16 eg_delay_counter_compare; + +} esfm_slot_internal; + +struct _esfm_slot +{ + /* Metadata */ + esfm_channel *channel; + esfm_chip *chip; + uint2 slot_idx; + + /* Register data */ + int13 out_enable[2]; + uint10 f_num; + uint3 block; + uint3 output_level; + /* a.k.a. feedback level in emu mode */ + uint3 mod_in_level; + + uint6 t_level; + uint4 mult; + uint3 waveform; + /* Only for 4th slot */ + uint2 rhy_noise; + + uint4 attack_rate; + uint4 decay_rate; + uint4 sustain_lvl; + uint4 release_rate; + + flag tremolo_en; + flag tremolo_deep; + flag vibrato_en; + flag vibrato_deep; + flag emu_connection_typ; + flag env_sustaining; + flag ksr; + uint2 ksl; + uint3 env_delay; + /* overlaps with env_delay bit 0 */ + /* TODO: check if emu mode only uses this, or if it actually overwrites the channel field used by native mode */ + flag emu_key_on; + + /* Internal state */ + esfm_slot_internal in; +}; + +struct _esfm_channel +{ + esfm_chip *chip; + esfm_slot slots[4]; + uint5 channel_idx; + int16 output[2]; + flag key_on; + flag emu_mode_4op_enable; + /* Only for 17th and 18th channels */ + flag key_on_2; + flag emu_mode_4op_enable_2; +}; + +#define ESFM_WRITEBUF_SIZE 1024 +#define ESFM_WRITEBUF_DELAY 2 + +struct _esfm_chip +{ + esfm_channel channels[18]; + int32 output_accm[2]; + uint16 addr_latch; + + flag emu_wavesel_enable; + flag emu_newmode; + flag native_mode; + + flag keyscale_mode; + + /* Global state */ + uint36 eg_timer; + uint10 global_timer; + uint8 eg_clocks; + flag eg_tick; + flag eg_timer_overflow; + uint8 tremolo; + uint8 tremolo_pos; + uint8 vibrato_pos; + uint23 lfsr; + + flag rm_hh_bit2; + flag rm_hh_bit3; + flag rm_hh_bit7; + flag rm_hh_bit8; + flag rm_tc_bit3; + flag rm_tc_bit5; + + + /* 0xbd register in emulation mode, exposed in 0x4bd in native mode */ + /* ("bass drum" register */ + uint8 emu_rhy_mode_flags; + + flag emu_vibrato_deep; + flag emu_tremolo_deep; + + double timer_accumulator[2]; + uint8 timer_reload[2]; + uint8 timer_counter[2]; + flag timer_enable[2]; + flag timer_mask[2]; + flag timer_overflow[2]; + flag irq_bit; + + /* -- Test bits (NOT IMPLEMENTED) -- */ + /* Halts the envelope generators from advancing. Written on bit 0, read back from bit 5. */ + flag test_bit_w0_r5_eg_halt; + /* + * Activates some sort of waveform test mode that amplifies the output volume greatly + * and continuously shifts the waveform table downwards, possibly also outputting the + * waveform's derivative? (it's so weird!) + */ + flag test_bit_1_distort; + /* Seems to do nothing. */ + flag test_bit_2; + /* Seems to do nothing. */ + flag test_bit_3; + /* Appears to attenuate the output by about 3 dB. */ + flag test_bit_4_attenuate; + /* Written on bit 5, read back from bit 0. Seems to do nothing. */ + flag test_bit_w5_r0; + /* Resets all phase generators and holds them in the reset state while this bit is set. */ + flag test_bit_6_phase_stop_reset; + /* Seems to do nothing. */ + flag test_bit_7; + + esfm_write_buf write_buf[ESFM_WRITEBUF_SIZE]; + size_t write_buf_start; + size_t write_buf_end; + uint64_t write_buf_timestamp; +}; + +#ifdef __cplusplus +} +#endif diff --git a/thirdparty/adlmidi/chips/esfmu/esfm_registers.c b/thirdparty/adlmidi/chips/esfmu/esfm_registers.c new file mode 100644 index 000000000..749815f63 --- /dev/null +++ b/thirdparty/adlmidi/chips/esfmu/esfm_registers.c @@ -0,0 +1,1010 @@ +/* + * ESFMu: emulator for the ESS "ESFM" enhanced OPL3 clone + * Copyright (C) 2023 Kagamiin~ + * + * This file includes code and data from the Nuked OPL3 project, copyright (C) + * 2013-2023 Nuke.YKT. Its usage, modification and redistribution is allowed + * under the terms of the GNU Lesser General Public License version 2.1 or + * later. + * + * ESFMu 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. + * + * ESFMu 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 ESFMu. If not, see . + */ + +/* + * ESFMu wouldn't have been possible without the hard work and dedication of + * the retro computer hardware research and preservation community. + * + * I'd like to thank: + * - Nuke.YKT + * Developer of Nuked OPL3, which was the basis for ESFMu's code and + * also a great learning resource on Yamaha FM synthesis for myself. + * Nuke.YKT also gives shoutouts on behalf of Nuked OPL3 to: + * - MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * - forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * - OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * - siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * - rainwarrior + * For performing the initial research on ESFM drivers and documenting + * ESS's patent on native mode operator organization. + * - jwt27 + * For kickstarting the ESFM research project and compiling rainwarrior's + * findings and more in an accessible document ("ESFM Demystified"). + * - pachuco/CatButts + * For documenting ESS's patent on ESFM's feedback implementation, which + * was vital in getting ESFMu's sound output to be accurate. + * - And everybody who helped out with real hardware testing + */ + +#include "esfm.h" +#include +#include +#include +#include + + +/* + * Table of KSL values extracted from OPL3 ROM; taken straight from Nuked OPL3 + * source code. + * TODO: Check if ESFM uses the same KSL values. + */ + +static const int16 kslrom[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 +}; + +/* + * This maps the low 5 bits of emulation mode address to an emulation mode + * slot; taken straight from Nuked OPL3. Used for decoding certain emulation + * mode address ranges. + */ +static const int8_t ad_slot[0x20] = { + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +/* + * This maps an emulation mode slot index to a tuple representing the + * corresponding native mode channel and slot. + */ +static const emu_slot_channel_mapping emu_slot_map[36] = +{ + { 0, 0}, { 1, 0}, { 2, 0}, { 0, 1}, { 1, 1}, { 2, 1}, + { 3, 0}, { 4, 0}, { 5, 0}, { 3, 1}, { 4, 1}, { 5, 1}, + { 6, 0}, { 7, 0}, { 8, 0}, { 6, 1}, { 7, 1}, { 8, 1}, + { 9, 0}, {10, 0}, {11, 0}, { 9, 1}, {10, 1}, {11, 1}, + {12, 0}, {13, 0}, {14, 0}, {12, 1}, {13, 1}, {14, 1}, + {15, 0}, {16, 0}, {17, 0}, {15, 1}, {16, 1}, {17, 1} +}; + +/* + * This encodes which emulation mode channels are the secondary channel in a + * 4-op channel pair (where the entry is non-negative), and which is the + * corresponding primary channel for that secondary channel. + */ +static const int emu_4op_secondary_to_primary[18] = +{ + -1, -1, -1, 0, 1, 2, -1, -1, -1, + -1, -1, -1, 9, 10, 11, -1, -1, -1 +}; + +/* + * This encodes the operator outputs to be enabled or disabled for + * each 4-op algorithm in emulation mode. + * Indices: FM+FM, FM+AM, AM+FM, AM+AM (lower channel MSB, upper channel LSB) + * Values: enable OP1, OP2, OP3, OP4 + */ +static const esbool emu_4op_alg_output_enable[4][4] = +{ + {0, 0, 0, 1}, + {0, 1, 0, 1}, + {1, 0, 0, 1}, + {1, 0, 1, 1} +}; + +/* + * This encodes the operator interconnections to be enabled or disabled for + * each 4-op algorithm in emulation mode. + * Indices: FM+FM, FM+AM, AM+FM, AM+AM (lower channel MSB, upper channel LSB) + * Values: enable OP1FB, OP1->2, OP2->3, OP3->4 + */ +static const esbool emu_4op_alg_mod_enable[4][4] = +{ + {1, 1, 1, 1}, + {1, 1, 0, 1}, + {1, 0, 1, 1}, + {1, 0, 1, 0} +}; + + +/* ------------------------------------------------------------------------- */ +static void +ESFM_emu_rearrange_connections(esfm_channel *channel) +{ + int secondary_to_primary; + + secondary_to_primary = emu_4op_secondary_to_primary[channel->channel_idx]; + if (secondary_to_primary >= 0) + { + esfm_channel *pair_primary = &channel->chip->channels[secondary_to_primary]; + if (pair_primary->emu_mode_4op_enable) + { + /* always work from primary channel in pair when dealing with 4-op */ + channel = pair_primary; + } + } + + if (channel->emu_mode_4op_enable && (channel->channel_idx % 9) < 3 && channel->chip->emu_newmode) + { + esfm_channel *secondary = &channel->chip->channels[channel->channel_idx + 3]; + uint2 algorithm = ((channel->slots[0].emu_connection_typ != 0) << 1) + | (secondary->slots[0].emu_connection_typ != 0); + int i; + + secondary->slots[0].in.mod_input = &channel->slots[1].in.output; + + for (i = 0; i < 2; i++) + { + channel->slots[i].in.emu_mod_enable = + emu_4op_alg_mod_enable[algorithm][i] ? ~((int13) 0) : 0; + channel->slots[i].in.emu_output_enable = + emu_4op_alg_output_enable[algorithm][i] ? ~((int13) 0) : 0; + + secondary->slots[i].in.emu_mod_enable = + emu_4op_alg_mod_enable[algorithm][i + 2] ? ~((int13) 0) : 0; + secondary->slots[i].in.emu_output_enable = + emu_4op_alg_output_enable[algorithm][i + 2] ? ~((int13) 0) : 0; + } + } + else if ((channel->chip->emu_rhy_mode_flags & 0x20) != 0 + && (channel->channel_idx == 7 || channel->channel_idx == 8)) + { + channel->slots[0].in.emu_mod_enable = 0; + channel->slots[1].in.emu_mod_enable = 0; + channel->slots[0].in.emu_output_enable = ~((int13) 0); + channel->slots[1].in.emu_output_enable = ~((int13) 0); + } + else + { + channel->slots[0].in.mod_input = &channel->slots[0].in.feedback_buf; + + channel->slots[0].in.emu_mod_enable = ~((int13) 0); + channel->slots[0].in.emu_output_enable = + (channel->slots[0].emu_connection_typ != 0) ? ~((int13) 0) : 0; + channel->slots[1].in.emu_output_enable = ~((int13) 0); + channel->slots[1].in.emu_mod_enable = + (channel->slots[0].emu_connection_typ != 0) ? 0 : ~((int13) 0); + } +} + + +/* ------------------------------------------------------------------------- */ +static void +ESFM_emu_to_native_switch(esfm_chip *chip) +{ + size_t channel_idx, slot_idx; + for (channel_idx = 0; channel_idx < 18; channel_idx++) + { + for (slot_idx = 0; slot_idx < 4; slot_idx++) + { + esfm_channel *channel = &chip->channels[channel_idx]; + esfm_slot *slot = &channel->slots[slot_idx]; + + if (slot_idx == 0) + { + slot->in.mod_input = &slot->in.feedback_buf; + } + else + { + esfm_slot *prev_slot = &channel->slots[slot_idx - 1]; + slot->in.mod_input = &prev_slot->in.output; + } + } + } +} + +/* ------------------------------------------------------------------------- */ +static void +ESFM_native_to_emu_switch(esfm_chip *chip) +{ + size_t channel_idx; + for (channel_idx = 0; channel_idx < 18; channel_idx++) + { + ESFM_emu_rearrange_connections(&chip->channels[channel_idx]); + } +} + +/* ------------------------------------------------------------------------- */ +static void +ESFM_slot_update_keyscale(esfm_slot *slot) +{ + int16 ksl; + + if (slot->slot_idx > 0 && !slot->chip->native_mode) + { + return; + } + + ksl = (kslrom[slot->f_num >> 6] << 2) - ((0x08 - slot->block) << 5); + if (ksl < 0) + { + ksl = 0; + } + slot->in.eg_ksl_offset = ksl; + slot->in.keyscale = (slot->block << 1) + | ((slot->f_num >> (8 + !slot->chip->keyscale_mode)) & 0x01); +} + +/* ------------------------------------------------------------------------- */ +static void +ESFM_emu_channel_update_keyscale(esfm_channel *channel) +{ + int secondary_to_primary; + + secondary_to_primary = emu_4op_secondary_to_primary[channel->channel_idx]; + if (secondary_to_primary >= 0) + { + esfm_channel *pair_primary = &channel->chip->channels[secondary_to_primary]; + if (pair_primary->emu_mode_4op_enable) + { + /* always work from primary channel in pair when dealing with 4-op */ + channel = pair_primary; + } + } + + ESFM_slot_update_keyscale(&channel->slots[0]); + channel->slots[1].in.eg_ksl_offset = channel->slots[0].in.eg_ksl_offset; + channel->slots[1].in.keyscale = channel->slots[0].in.keyscale; + + if (channel->emu_mode_4op_enable && (channel->channel_idx % 9) < 3 && channel->chip->emu_newmode) + { + int i; + esfm_channel *secondary = &channel->chip->channels[channel->channel_idx + 3]; + secondary->slots[0].f_num = channel->slots[0].f_num; + secondary->slots[0].block = channel->slots[0].block; + + for (i = 0; i < 2; i++) + { + secondary->slots[i].in.eg_ksl_offset = channel->slots[0].in.eg_ksl_offset; + secondary->slots[i].in.keyscale = channel->slots[0].in.keyscale; + } + } +} + +/* ------------------------------------------------------------------------- */ +static inline uint8_t +ESFM_slot_readback (esfm_slot *slot, uint8_t register_idx) +{ + uint8_t data = 0; + switch (register_idx & 0x07) + { + case 0x00: + data |= (slot->tremolo_en != 0) << 7; + data |= (slot->vibrato_en != 0) << 6; + data |= (slot->env_sustaining != 0) << 5; + data |= (slot->vibrato_en != 0) << 4; + data |= slot->mult & 0x0f; + break; + case 0x01: + data |= slot->ksl << 6; + data |= slot->t_level & 0x3f; + break; + case 0x02: + data |= slot->attack_rate << 4; + data |= slot->decay_rate & 0x0f; + break; + case 0x03: + data |= slot->sustain_lvl << 4; + data |= slot->release_rate & 0x0f; + break; + case 0x04: + data = slot->f_num & 0xff; + break; + case 0x05: + data |= slot->env_delay << 5; + data |= (slot->block & 0x07) << 2; + data |= (slot->f_num >> 8) & 0x03; + break; + case 0x06: + data |= (slot->tremolo_deep != 0) << 7; + data |= (slot->vibrato_deep != 0) << 6; + data |= (slot->out_enable[1] != 0) << 5; + data |= (slot->out_enable[0] != 0) << 4; + data |= (slot->mod_in_level & 0x07) << 1; + data |= slot->emu_connection_typ & 0x01; + break; + case 0x07: + data |= slot->output_level << 5; + data |= (slot->rhy_noise & 0x03) << 3; + data |= slot->waveform & 0x07; + break; + } + return data; +} + +/* ------------------------------------------------------------------------- */ +static inline void +ESFM_slot_write (esfm_slot *slot, uint8_t register_idx, uint8_t data) +{ + switch (register_idx & 0x07) + { + case 0x00: + slot->tremolo_en = (data & 0x80) != 0; + slot->vibrato_en = (data & 0x40) != 0; + slot->env_sustaining = (data & 0x20) != 0; + slot->ksr = (data & 0x10) != 0; + slot->mult = data & 0x0f; + break; + case 0x01: + slot->ksl = data >> 6; + slot->t_level = data & 0x3f; + ESFM_slot_update_keyscale(slot); + break; + case 0x02: + slot->attack_rate = data >> 4; + slot->decay_rate = data & 0x0f; + break; + case 0x03: + slot->sustain_lvl = data >> 4; + slot->release_rate = data & 0x0f; + break; + case 0x04: + slot->f_num = (slot->f_num & 0x300) | data; + ESFM_slot_update_keyscale(slot); + break; + case 0x05: + if (slot->env_delay < (data >> 5)) + { + slot->in.eg_delay_transitioned_01 = 1; + } + else if (slot->env_delay > (data >> 5)) + { + slot->in.eg_delay_transitioned_10 = 1; + } + slot->env_delay = data >> 5; + slot->emu_key_on = (data >> 5) & 0x01; + slot->block = (data >> 2) & 0x07; + slot->f_num = (slot->f_num & 0xff) | ((data & 0x03) << 8); + ESFM_slot_update_keyscale(slot); + break; + case 0x06: + slot->tremolo_deep = (data & 0x80) != 0; + slot->vibrato_deep = (data & 0x40) != 0; + slot->out_enable[1] = (data & 0x20) ? ~((int13) 0) : 0; + slot->out_enable[0] = (data & 0x10) ? ~((int13) 0) : 0; + slot->mod_in_level = (data >> 1) & 0x07; + slot->emu_connection_typ = data & 0x01; + break; + case 0x07: + slot->output_level = data >> 5; + slot->rhy_noise = (data >> 3) & 0x03; + slot->waveform = data & 0x07; + break; + } +} + +#define KEY_ON_REGS_START (18 * 4 * 8) +#define TIMER1_REG (0x402) +#define TIMER2_REG (0x403) +#define TIMER_SETUP_REG (0x404) +#define CONFIG_REG (0x408) +#define BASSDRUM_REG (0x4bd) +#define TEST_REG (0x501) +#define FOUROP_CONN_REG (0x504) +#define NATIVE_MODE_REG (0x505) + +/* ------------------------------------------------------------------------- */ +static void +ESFM_write_reg_native (esfm_chip *chip, uint16_t address, uint8_t data) +{ + int i; + address = address & 0x7ff; + + if (address < KEY_ON_REGS_START) + { + /* Slot register write */ + size_t channel_idx = address >> 5; + size_t slot_idx = (address >> 3) & 0x03; + size_t register_idx = address & 0x07; + esfm_slot *slot = &chip->channels[channel_idx].slots[slot_idx]; + + ESFM_slot_write(slot, register_idx, data); + } + else if (address < KEY_ON_REGS_START + 16) + { + /* Key-on registers */ + size_t channel_idx = (address - KEY_ON_REGS_START); + esfm_channel *channel = &chip->channels[channel_idx]; + channel->key_on = data & 0x01; + channel->emu_mode_4op_enable = (data & 0x02) != 0; + } + else if (address < KEY_ON_REGS_START + 20) + { + /* Key-on channels 17 and 18 (each half) */ + size_t channel_idx = 16 + ((address & 0x02) >> 1); + esbool second_half = address & 0x01; + esfm_channel *channel = &chip->channels[channel_idx]; + if (second_half) + { + channel->key_on_2 = data & 0x01; + channel->emu_mode_4op_enable_2 = (data & 0x02) != 0; + } + else + { + channel->key_on = data & 0x01; + channel->emu_mode_4op_enable = (data & 0x02) != 0; + } + } + else + { + switch (address & 0x5ff) + { + case TIMER1_REG: + chip->timer_reload[0] = data; + chip->timer_counter[0] = data; + break; + case TIMER2_REG: + chip->timer_reload[1] = data; + chip->timer_counter[1] = data; + break; + case TIMER_SETUP_REG: + if (data & 0x80) + { + chip->irq_bit = 0; + chip->timer_overflow[0] = 0; + chip->timer_overflow[1] = 0; + break; + } + chip->timer_enable[0] = (data & 0x01) != 0; + chip->timer_enable[1] = (data & 0x02) != 0; + chip->timer_mask[1] = (data & 0x20) != 0; + chip->timer_mask[0] = (data & 0x40) != 0; + break; + case CONFIG_REG: + chip->keyscale_mode = (data & 0x40) != 0; + break; + case BASSDRUM_REG: + chip->emu_rhy_mode_flags = data & 0x3f; + chip->emu_vibrato_deep = (data & 0x40) != 0; + chip->emu_tremolo_deep = (data & 0x80) != 0; + break; + case FOUROP_CONN_REG: + for (i = 0; i < 3; i++) + { + chip->channels[i].emu_mode_4op_enable = (data >> i) & 0x01; + chip->channels[i + 9].emu_mode_4op_enable = (data >> (i + 3)) & 0x01; + } + break; + case TEST_REG: + chip->test_bit_w0_r5_eg_halt = (data & 0x01) | ((data & 0x20) != 0); + chip->test_bit_1_distort = (data & 0x02) != 0; + chip->test_bit_2 = (data & 0x04) != 0; + chip->test_bit_3 = (data & 0x08) != 0; + chip->test_bit_4_attenuate = (data & 0x10) != 0; + chip->test_bit_w5_r0 = (data & 0x20) != 0; + chip->test_bit_6_phase_stop_reset = (data & 0x40) != 0; + chip->test_bit_7 = (data & 0x80) != 0; + break; + } + } +} + +/* ------------------------------------------------------------------------- */ +static uint8_t +ESFM_readback_reg_native (esfm_chip *chip, uint16_t address) +{ + int i; + uint8_t data = 0; + address = address & 0x7ff; + + if (address < KEY_ON_REGS_START) + { + /* Slot register read */ + size_t channel_idx = address >> 5; + size_t slot_idx = (address >> 3) & 0x03; + size_t register_idx = address & 0x07; + esfm_slot *slot = &chip->channels[channel_idx].slots[slot_idx]; + + data = ESFM_slot_readback(slot, register_idx); + } + else if (address < KEY_ON_REGS_START + 16) + { + /* Key-on registers */ + size_t channel_idx = (address - KEY_ON_REGS_START); + esfm_channel *channel = &chip->channels[channel_idx]; + + data |= channel->key_on != 0; + data |= (channel->emu_mode_4op_enable != 0) << 1; + } + else if (address < KEY_ON_REGS_START + 20) + { + /* Key-on channels 17 and 18 (each half) */ + size_t channel_idx = 16 + ((address & 0x02) >> 1); + esbool second_half = address & 0x01; + esfm_channel *channel = &chip->channels[channel_idx]; + if (second_half) + { + data |= channel->key_on_2 != 0; + data |= (channel->emu_mode_4op_enable_2 != 0) << 1; + } + else + { + data |= channel->key_on != 0; + data |= (channel->emu_mode_4op_enable != 0) << 1; + } + } + else + { + switch (address & 0x5ff) + { + case TIMER1_REG: + data = chip->timer_counter[0]; + break; + case TIMER2_REG: + data = chip->timer_counter[1]; + break; + case TIMER_SETUP_REG: + data |= chip->timer_enable[0] != 0; + data |= (chip->timer_enable[1] != 0) << 1; + data |= (chip->timer_mask[1] != 0) << 5; + data |= (chip->timer_mask[0] != 0) << 6; + break; + case CONFIG_REG: + data |= (chip->keyscale_mode != 0) << 6; + break; + case BASSDRUM_REG: + data |= chip->emu_rhy_mode_flags; + data |= chip->emu_vibrato_deep << 6; + data |= chip->emu_tremolo_deep << 7; + break; + case TEST_REG: + data |= chip->test_bit_w5_r0 != 0; + data |= (chip->test_bit_1_distort != 0) << 1; + data |= (chip->test_bit_2 != 0) << 2; + data |= (chip->test_bit_3 != 0) << 3; + data |= (chip->test_bit_4_attenuate != 0) << 4; + data |= (chip->test_bit_w0_r5_eg_halt != 0) << 5; + data |= (chip->test_bit_6_phase_stop_reset != 0) << 6; + data |= (chip->test_bit_7 != 0) << 7; + break; + case FOUROP_CONN_REG: + for (i = 0; i < 3; i++) + { + data |= (chip->channels[i].emu_mode_4op_enable != 0) << i; + data |= (chip->channels[i + 9].emu_mode_4op_enable != 0) << (i + 3); + } + break; + case NATIVE_MODE_REG: + data |= (chip->emu_newmode != 0); + data |= (chip->native_mode != 0) << 7; + break; + } + } + return data; +} + +/* ------------------------------------------------------------------------- */ +static void +ESFM_write_reg_emu (esfm_chip *chip, uint16_t address, uint8_t data) +{ + bool high = (address & 0x100) != 0; + uint8_t reg = address & 0xff; + int emu_slot_idx = ad_slot[address & 0x1f]; + int natv_chan_idx = -1; + int natv_slot_idx = -1; + int emu_chan_idx = (reg & 0x0f) > 8 ? -1 : ((reg & 0x0f) + high * 9); + + if (emu_slot_idx >= 0) + { + if (high) + { + emu_slot_idx += 18; + } + + natv_chan_idx = emu_slot_map[emu_slot_idx].channel_idx; + natv_slot_idx = emu_slot_map[emu_slot_idx].slot_idx; + } + + if (reg == 0xbd) + { + chip->emu_rhy_mode_flags = data & 0x3f; + chip->emu_vibrato_deep = (data & 0x40) != 0; + chip->emu_tremolo_deep = (data & 0x80) != 0; + if (chip->emu_rhy_mode_flags & 0x20) + { + /* TODO: check if writes to 0xbd actually affect the readable key-on flags at */ + /* 0x246, 0x247, 0x248; and if there's any visible effect from the SD and TC flags */ + chip->channels[6].key_on = (data & 0x10) != 0; + chip->channels[7].key_on = (data & 0x01) != 0; + chip->channels[8].key_on = (data & 0x04) != 0; + chip->channels[7].key_on_2 = (data & 0x08) != 0; + chip->channels[8].key_on_2 = (data & 0x02) != 0; + } + ESFM_emu_rearrange_connections(&chip->channels[7]); + ESFM_emu_rearrange_connections(&chip->channels[8]); + return; + } + + switch(reg & 0xf0) + { + case 0x00: + if (high) + { + int i; + switch(reg & 0x0f) + { + case 0x01: + chip->emu_wavesel_enable = (data & 0x20) != 0; + break; + case 0x02: + chip->timer_reload[0] = data; + chip->timer_counter[0] = data; + break; + case 0x03: + chip->timer_reload[1] = data; + chip->timer_counter[1] = data; + break; + case 0x04: + for (i = 0; i < 3; i++) + { + chip->channels[i].emu_mode_4op_enable = (data >> i) & 0x01; + chip->channels[i + 9].emu_mode_4op_enable = (data >> (i + 3)) & 0x01; + } + for (i = 0; i < 6; i++) + { + ESFM_emu_rearrange_connections(&chip->channels[i]); + ESFM_emu_rearrange_connections(&chip->channels[i + 9]); + } + break; + case 0x05: + chip->emu_newmode = data & 0x01; + if ((data & 0x80) != 0) + { + chip->native_mode = 1; + ESFM_emu_to_native_switch(chip); + } + break; + case 0x08: + chip->keyscale_mode = (data & 0x40) != 0; + break; + } + } + else + { + switch(reg & 0x0f) + { + case 0x01: + chip->emu_wavesel_enable = (data & 0x20) != 0; + break; + case 0x02: + chip->timer_reload[0] = data; + chip->timer_counter[0] = data; + break; + case 0x03: + chip->timer_reload[1] = data; + chip->timer_counter[1] = data; + break; + case 0x04: + if (data & 0x80) + { + chip->irq_bit = 0; + chip->timer_overflow[0] = 0; + chip->timer_overflow[1] = 0; + break; + } + chip->timer_enable[0] = data & 0x01; + chip->timer_enable[1] = (data & 0x02) != 0; + chip->timer_mask[1] = (data & 0x20) != 0; + chip->timer_mask[0] = (data & 0x40) != 0; + break; + case 0x08: + chip->keyscale_mode = (data & 0x40) != 0; + break; + } + } + break; + case 0x20: case 0x30: + if (emu_slot_idx >= 0) + { + ESFM_slot_write(&chip->channels[natv_chan_idx].slots[natv_slot_idx], 0x0, data); + } + break; + case 0x40: case 0x50: + if (emu_slot_idx >= 0) + { + ESFM_slot_write(&chip->channels[natv_chan_idx].slots[natv_slot_idx], 0x1, data); + ESFM_emu_channel_update_keyscale(&chip->channels[natv_chan_idx]); + } + break; + case 0x60: case 0x70: + if (emu_slot_idx >= 0) + { + ESFM_slot_write(&chip->channels[natv_chan_idx].slots[natv_slot_idx], 0x2, data); + } + break; + case 0x80: case 0x90: + if (emu_slot_idx >= 0) + { + ESFM_slot_write(&chip->channels[natv_chan_idx].slots[natv_slot_idx], 0x3, data); + } + break; + case 0xa0: + if (emu_chan_idx >= 0) + { + ESFM_slot_write(&chip->channels[emu_chan_idx].slots[0], 0x4, data); + ESFM_emu_channel_update_keyscale(&chip->channels[emu_chan_idx]); + } + break; + case 0xb0: + if (emu_chan_idx >= 0) + { + esfm_channel *channel = &chip->channels[emu_chan_idx]; + /* TODO: check if emulation mode actually writes to the native mode key on registers */ + /* it might only use slot 0's emu key on field... */ + channel->key_on = (data & 0x20) != 0; + if (channel->channel_idx == 7 || channel->channel_idx == 8) + { + channel->key_on_2 = (data & 0x20) != 0; + } + ESFM_slot_write(&channel->slots[0], 0x5, data); + ESFM_emu_channel_update_keyscale(&chip->channels[emu_chan_idx]); + } + break; + case 0xc0: + if (emu_chan_idx >= 0) + { + ESFM_slot_write(&chip->channels[emu_chan_idx].slots[0], 0x6, data); + ESFM_emu_rearrange_connections(&chip->channels[emu_chan_idx]); + } + break; + case 0xe0: case 0xf0: + if (emu_slot_idx >= 0) + { + ESFM_slot_write(&chip->channels[natv_chan_idx].slots[natv_slot_idx], 0x7, data); + } + break; + } +} + + +/* ------------------------------------------------------------------------- */ +void +ESFM_write_reg (esfm_chip *chip, uint16_t address, uint8_t data) +{ + if (chip->native_mode) + { + ESFM_write_reg_native(chip, address, data); + return; + } + else + { + ESFM_write_reg_emu(chip, address, data); + return; + } +} + +/* ------------------------------------------------------------------------- */ +void +ESFM_write_reg_buffered (esfm_chip *chip, uint16_t address, uint8_t data) +{ + uint64_t timestamp; + esfm_write_buf *new_entry, *last_entry; + + new_entry = &chip->write_buf[chip->write_buf_end]; + last_entry = &chip->write_buf[(chip->write_buf_end - 1) % ESFM_WRITEBUF_SIZE]; + + if (new_entry->valid) { + ESFM_write_reg(chip, new_entry->address, new_entry->data); + chip->write_buf_start = (chip->write_buf_end + 1) % ESFM_WRITEBUF_SIZE; + } + + new_entry->valid = 1; + new_entry->address = address; + new_entry->data = data; + timestamp = last_entry->timestamp + ESFM_WRITEBUF_DELAY; + if (timestamp < chip->write_buf_timestamp) + { + timestamp = chip->write_buf_timestamp; + } + + new_entry->timestamp = timestamp; + chip->write_buf_end = (chip->write_buf_end + 1) % ESFM_WRITEBUF_SIZE; +} + +/* ------------------------------------------------------------------------- */ +void +ESFM_write_reg_buffered_fast (esfm_chip *chip, uint16_t address, uint8_t data) +{ + esfm_write_buf *new_entry; + + new_entry = &chip->write_buf[chip->write_buf_end]; + + if (new_entry->valid) { + ESFM_write_reg(chip, new_entry->address, new_entry->data); + chip->write_buf_start = (chip->write_buf_end + 1) % ESFM_WRITEBUF_SIZE; + } + + new_entry->valid = 1; + new_entry->address = address; + new_entry->data = data; + new_entry->timestamp = chip->write_buf_timestamp; + chip->write_buf_end = (chip->write_buf_end + 1) % ESFM_WRITEBUF_SIZE; +} + +/* ------------------------------------------------------------------------- */ +uint8_t +ESFM_readback_reg (esfm_chip *chip, uint16_t address) +{ + if (chip->native_mode) + { + return ESFM_readback_reg_native(chip, address); + } + else + { + return 0; + } +} + +/* ------------------------------------------------------------------------- */ +void +ESFM_write_port (esfm_chip *chip, uint8_t offset, uint8_t data) +{ + if (chip->native_mode) + { + switch(offset) + { + case 0: + chip->native_mode = 0; + ESFM_native_to_emu_switch(chip); + /* TODO: verify if the address write goes through */ + chip->addr_latch = data; + break; + case 1: + ESFM_write_reg_native(chip, chip->addr_latch, data); + break; + case 2: + chip->addr_latch = (chip->addr_latch & 0xff00) | data; + break; + case 3: + chip->addr_latch = chip->addr_latch & 0xff; + chip->addr_latch |= (uint16)data << 8; + break; + } + } + else + { + switch(offset) + { + case 0: + chip->addr_latch = data; + break; + case 1: case 3: + ESFM_write_reg_emu(chip, chip->addr_latch, data); + break; + case 2: + chip->addr_latch = (uint16)data | 0x100; + break; + } + } +} + +/* ------------------------------------------------------------------------- */ +uint8_t +ESFM_read_port (esfm_chip *chip, uint8_t offset) +{ + uint8_t data = 0; + + switch(offset) + { + case 0: + data |= (chip->irq_bit != 0) << 7; + data |= (chip->timer_overflow[0] != 0) << 6; + data |= (chip->timer_overflow[1] != 0) << 5; + break; + case 1: + if (chip->native_mode) + { + data = ESFM_readback_reg_native(chip, chip->addr_latch); + } + else + { + data = 0; + } + break; + case 2: case 3: + /* This matches OPL3 behavior. */ + data = 0xff; + break; + } + + return data; +} + +/* ------------------------------------------------------------------------- */ +void +ESFM_set_mode (esfm_chip *chip, esbool native_mode) +{ + native_mode = native_mode != 0; + + if (native_mode != (chip->native_mode != 0)) + { + chip->native_mode = native_mode; + if (native_mode) + { + ESFM_emu_to_native_switch(chip); + } + else + { + ESFM_native_to_emu_switch(chip); + } + } +} + +/* ------------------------------------------------------------------------- */ +void +ESFM_init (esfm_chip *chip) +{ + esfm_slot *slot; + esfm_channel *channel; + size_t channel_idx, slot_idx; + + memset(chip, 0, sizeof(esfm_chip)); + for (channel_idx = 0; channel_idx < 18; channel_idx++) + { + for (slot_idx = 0; slot_idx < 4; slot_idx++) + { + channel = &chip->channels[channel_idx]; + slot = &channel->slots[slot_idx]; + + channel->chip = chip; + channel->channel_idx = channel_idx; + slot->channel = channel; + slot->chip = chip; + slot->slot_idx = slot_idx; + slot->in.eg_position = slot->in.eg_output = 0x1ff; + slot->in.eg_state = EG_RELEASE; + slot->in.emu_mod_enable = ~((int13) 0); + if (slot_idx == 0) + { + slot->in.mod_input = &slot->in.feedback_buf; + } + else + { + esfm_slot *prev_slot = &channel->slots[slot_idx - 1]; + slot->in.mod_input = &prev_slot->in.output; + } + + if (slot_idx == 1) + { + slot->in.emu_output_enable = ~((int13) 0); + } + + if (channel_idx > 15 && slot_idx & 0x02) + { + slot->in.key_on = &channel->key_on_2; + } + else + { + slot->in.key_on = &channel->key_on; + } + + slot->out_enable[0] = slot->out_enable[1] = ~((int13) 0); + } + } + + chip->lfsr = 1; +} diff --git a/thirdparty/adlmidi/chips/esfmu_opl3.cpp b/thirdparty/adlmidi/chips/esfmu_opl3.cpp new file mode 100644 index 000000000..f5f20e99b --- /dev/null +++ b/thirdparty/adlmidi/chips/esfmu_opl3.cpp @@ -0,0 +1,88 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "esfmu_opl3.h" +#include "esfmu/esfm.h" +#include + +ESFMuOPL3::ESFMuOPL3() : + OPLChipBaseT() +{ + m_chip = new esfm_chip; + ESFMuOPL3::setRate(m_rate); +} + +ESFMuOPL3::~ESFMuOPL3() +{ + esfm_chip *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void ESFMuOPL3::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + esfm_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(esfm_chip)); + ESFM_init(chip_r/*, rate*/); +} + +void ESFMuOPL3::reset() +{ + OPLChipBaseT::reset(); + esfm_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(esfm_chip)); + ESFM_init(chip_r/*, m_rate*/); +} + +void ESFMuOPL3::writeReg(uint16_t addr, uint8_t data) +{ + esfm_chip *chip_r = reinterpret_cast(m_chip); + ESFM_write_reg_buffered_fast(chip_r, addr, data); +} + +void ESFMuOPL3::writePan(uint16_t addr, uint8_t data) +{ + // FIXME: Implement panning support + // esfm_chip *chip_r = reinterpret_cast(m_chip); + // ESFM_write?pan(chip_r, addr, data); + (void)(addr); + (void)(data); +} + +void ESFMuOPL3::nativeGenerate(int16_t *frame) +{ + esfm_chip *chip_r = reinterpret_cast(m_chip); + ESFM_generate(chip_r, frame); +} + +const char *ESFMuOPL3::emulatorName() +{ + return "ESFMu"; +} + +bool ESFMuOPL3::hasFullPanning() +{ + return false; +} + +OPLChipBase::ChipType ESFMuOPL3::chipType() +{ + return CHIPTYPE_ESFM; +} diff --git a/thirdparty/adlmidi/chips/esfmu_opl3.h b/thirdparty/adlmidi/chips/esfmu_opl3.h new file mode 100644 index 000000000..b7572bbf8 --- /dev/null +++ b/thirdparty/adlmidi/chips/esfmu_opl3.h @@ -0,0 +1,46 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ESFMU_OPL3_H +#define ESFMU_OPL3_H + +#include "opl_chip_base.h" + +class ESFMuOPL3 final : public OPLChipBaseT +{ + void *m_chip; +public: + ESFMuOPL3(); + ~ESFMuOPL3() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // ESFMU_OPL3_H diff --git a/thirdparty/adlmidi/chips/java/JavaOPL3.hpp b/thirdparty/adlmidi/chips/java/JavaOPL3.hpp new file mode 100644 index 000000000..e9f039935 --- /dev/null +++ b/thirdparty/adlmidi/chips/java/JavaOPL3.hpp @@ -0,0 +1,1900 @@ +/* + * File: OPL3.java + * Software implementation of the Yamaha YMF262 sound generator. + * Copyright (C) 2008 Robson Cozendey + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * One of the objectives of this emulator is to stimulate further research in the + * OPL3 chip emulation. There was an explicit effort in making no optimizations, + * and making the code as legible as possible, so that a new programmer + * interested in modify and improve upon it could do so more easily. + * This emulator's main body of information was taken from reverse engineering of + * the OPL3 chip, from the YMF262 Datasheet and from the OPL3 section in the + * YMF278b Application's Manual, + * together with the vibrato table information, eighth waveform parameter + * information and feedback averaging information provided in MAME's YMF262 and + * YM3812 emulators, by Jarek Burczynski and Tatsuyuki Satoh. + * This emulator has a high degree of accuracy, and most of music files sound + * almost identical, exception made in some games which uses specific parts of + * the rhythm section. In this respect, some parts of the rhythm mode are still + * only an approximation of the real chip. + * The other thing to note is that this emulator was done through recordings of + * the SB16 DAC, so it has not bitwise precision. Additional equipment should be + * used to verify the samples directly from the chip, and allow this exact + * per-sample correspondence. As a good side-effect, since this emulator uses + * floating point and has a more fine-grained envelope generator, it can produce + * sometimes a crystal-clear, denser kind of OPL3 sound that, because of that, + * may be useful for creating new music. + * + * Version 1.0.6 + * + */ + +#include +#include +#include +#include +#include "../common/mutex.hpp" + +#ifndef UINT32_MAX +#define UINT32_MAX 0xffffffffU +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define OPL_SAMPLE_RATE 49716.0 +#define CENTER_PANNING_POWER 0.70710678118 /* [RH] volume at center for EQP */ +#define VOLUME_MUL 0.3333 + +/* libADLMIDI: Renamed from JavaOPL3 into to prevent symbol conflicting with original version linked */ +namespace ADL_JavaOPL3 +{ + +/* libADLMIDI: random generator */ +struct FRandom +{ + uint32_t GenRand32() { return (seed = seed * 1664525u + 1013904223u); } + double GenRand_Real1() { return GenRand32() * (1.0 / UINT32_MAX); } +private: + uint32_t seed; +}; + +static FRandom pr_opl3; + +class Operator; + +static inline double StripIntPart(double num) +{ + return num - trunc(num); /* libOPNMIDI */ +} + +// +// Channels +// + + +class Channel +{ +protected: + double feedback[2]; + + int fnuml, fnumh, kon, block, fb, cha, chb, cnt; + + // Factor to convert between normalized amplitude to normalized + // radians. The amplitude maximum is equivalent to 8*Pi radians. +#define toPhase (4.f) + +public: + int channelBaseAddress; + + double leftPan, rightPan; + + Channel (int baseAddress, double startvol); + virtual ~Channel() {} + void update_2_KON1_BLOCK3_FNUMH2(class OPL3 *OPL3); + void update_FNUML8(class OPL3 *OPL3); + void update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(class OPL3 *OPL3); + void updateChannel(class OPL3 *OPL3); + void updatePan(class OPL3 *OPL3); + virtual double getChannelOutput(class OPL3 *OPL3) = 0; + + virtual void keyOn() = 0; + virtual void keyOff() = 0; + virtual void updateOperators(class OPL3 *OPL3) = 0; +}; + + +class Channel2op : public Channel +{ +public: + Operator *op1, *op2; + + Channel2op (int baseAddress, double startvol, Operator *o1, Operator *o2); + double getChannelOutput(class OPL3 *OPL3); + + void keyOn(); + void keyOff(); + void updateOperators(class OPL3 *OPL3); +}; + + +class Channel4op : public Channel +{ +public: + Operator *op1, *op2, *op3, *op4; + + Channel4op (int baseAddress, double startvol, Operator *o1, Operator *o2, Operator *o3, Operator *o4); + double getChannelOutput(class OPL3 *OPL3); + + void keyOn(); + void keyOff(); + void updateOperators(class OPL3 *OPL3); +}; + +// There's just one instance of this class, that fills the eventual gaps in the Channel array; +class DisabledChannel : public Channel +{ +public: + DisabledChannel() : Channel(0, 0) { } + double getChannelOutput(class OPL3 *) { return 0; } + void keyOn() { } + void keyOff() { } + void updateOperators(class OPL3 *) { } +}; + + + +// +// Envelope Generator +// + + +class EnvelopeGenerator +{ +public: + enum Stage {ATTACK,DECAY,SUSTAIN,RELEASE,OFF}; + Stage stage; + int actualAttackRate, actualDecayRate, actualReleaseRate; + double xAttackIncrement, xMinimumInAttack; + double dBdecayIncrement; + double dBreleaseIncrement; + double attenuation, totalLevel, sustainLevel; + double x, envelope; + +public: + EnvelopeGenerator(); + void setActualSustainLevel(int sl); + void setTotalLevel(int tl); + void setAtennuation(int f_number, int block, int ksl); + void setActualAttackRate(int attackRate, int ksr, int keyScaleNumber); + void setActualDecayRate(int decayRate, int ksr, int keyScaleNumber); + void setActualReleaseRate(int releaseRate, int ksr, int keyScaleNumber); +private: + int calculateActualRate(int rate, int ksr, int keyScaleNumber); +public: + double getEnvelope(OPL3 *OPL3, int egt, int am); + void keyOn(); + void keyOff(); + +private: + static double dBtoX(double dB); + static double percentageToDB(double percentage); + static double percentageToX(double percentage); +}; + + +// +// Phase Generator +// + + +class PhaseGenerator { + double phase, phaseIncrement; + +public: + PhaseGenerator(); + void setFrequency(int f_number, int block, int mult); + double getPhase(class OPL3 *OPL3, int vib); + void keyOn(); +}; + + +// +// Operators +// + + +class Operator +{ +public: + PhaseGenerator phaseGenerator; + EnvelopeGenerator envelopeGenerator; + + double envelope, phase; + + int operatorBaseAddress; + int am, vib, ksr, egt, mult, ksl, tl, ar, dr, sl, rr, ws; + int keyScaleNumber, f_number, block; + + static const double noModulator; + +public: + Operator(int baseAddress); + void update_AM1_VIB1_EGT1_KSR1_MULT4(class OPL3 *OPL3); + void update_KSL2_TL6(class OPL3 *OPL3); + void update_AR4_DR4(class OPL3 *OPL3); + void update_SL4_RR4(class OPL3 *OPL3); + void update_5_WS3(class OPL3 *OPL3); + double getOperatorOutput(class OPL3 *OPL3, double modulator); + + void keyOn(); + void keyOff(); + void updateOperator(class OPL3 *OPL3, int ksn, int f_num, int blk); +protected: + double getOutput(double modulator, double outputPhase, double *waveform); +}; + + +// +// Rhythm +// + +// The getOperatorOutput() method in TopCymbalOperator, HighHatOperator and SnareDrumOperator +// were made through purely empyrical reverse engineering of the OPL3 output. + +class RhythmChannel : public Channel2op +{ +public: + RhythmChannel(int baseAddress, double startvol, Operator *o1, Operator *o2) + : Channel2op(baseAddress, startvol, o1, o2) + { } + double getChannelOutput(class OPL3 *OPL3); + + // Rhythm channels are always running, + // only the envelope is activated by the user. + void keyOn() { } + void keyOff() { } +}; + +class HighHatSnareDrumChannel : public RhythmChannel { + static const int highHatSnareDrumChannelBaseAddress = 7; +public: + HighHatSnareDrumChannel(double startvol, Operator *o1, Operator *o2) + : RhythmChannel(highHatSnareDrumChannelBaseAddress, startvol, o1, o2) + { } +}; + +class TomTomTopCymbalChannel : public RhythmChannel { + static const int tomTomTopCymbalChannelBaseAddress = 8; +public: + TomTomTopCymbalChannel(double startvol, Operator *o1, Operator *o2) + : RhythmChannel(tomTomTopCymbalChannelBaseAddress, startvol, o1, o2) + { } +}; + +class TopCymbalOperator : public Operator { + static const int topCymbalOperatorBaseAddress = 0x15; +public: + TopCymbalOperator(int baseAddress); + TopCymbalOperator(); + double getOperatorOutput(class OPL3 *OPL3, double modulator); + double getOperatorOutput(class OPL3 *OPL3, double modulator, double externalPhase); +}; + +class HighHatOperator : public TopCymbalOperator { + static const int highHatOperatorBaseAddress = 0x11; +public: + HighHatOperator(); + double getOperatorOutput(class OPL3 *OPL3, double modulator); +}; + +class SnareDrumOperator : public Operator { + static const int snareDrumOperatorBaseAddress = 0x14; +public: + SnareDrumOperator(); + double getOperatorOutput(class OPL3 *OPL3, double modulator); +}; + +class TomTomOperator : public Operator { + static const int tomTomOperatorBaseAddress = 0x12; +public: + TomTomOperator() : Operator(tomTomOperatorBaseAddress) { } +}; + +class BassDrumChannel : public Channel2op { + static const int bassDrumChannelBaseAddress = 6; + static const int op1BaseAddress = 0x10; + static const int op2BaseAddress = 0x13; + + Operator my_op1, my_op2; + +public: + BassDrumChannel(double startvol); + double getChannelOutput(class OPL3 *OPL3); + + // Key ON and OFF are unused in rhythm channels. + void keyOn() { } + void keyOff() { } +}; + + +// +// OPl3 Data +// + + +struct OPL3DataStruct +{ +public: + // OPL3-wide registers offsets: + static const int + _1_NTS1_6_Offset = 0x08, + DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1_Offset = 0xBD, + _7_NEW1_Offset = 0x105, + _2_CONNECTIONSEL6_Offset = 0x104; + + // The OPL3 tremolo repetition rate is 3.7 Hz. + #define tremoloFrequency (3.7) + + //static const int tremoloTableLength = (int)(OPL_SAMPLE_RATE/tremoloFrequency); + static const int tremoloTableLength = 13436; /* libADLMIDI */ + static const int vibratoTableLength = 8192; + + OPL3DataStruct() + { + loadVibratoTable(); + loadTremoloTable(); + } + + // The first array is used when DVB=0 and the second array is used when DVB=1. + double vibratoTable[2][vibratoTableLength]; + + // First array used when AM = 0 and second array used when AM = 1. + double tremoloTable[2][tremoloTableLength]; + + static double calculateIncrement(double begin, double end, double period) { + return (end-begin)/OPL_SAMPLE_RATE * (1/period); + } + +private: + void loadVibratoTable(); + void loadTremoloTable(); +}; + + +// +// Channel Data +// + + +struct ChannelData +{ + static const int + _2_KON1_BLOCK3_FNUMH2_Offset = 0xB0, + FNUML8_Offset = 0xA0, + CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset = 0xC0; + + // Feedback rate in fractions of 2*Pi, normalized to (0,1): + // 0, Pi/16, Pi/8, Pi/4, Pi/2, Pi, 2*Pi, 4*Pi turns to be: + static const float feedback[8]; +}; +const float ChannelData::feedback[8] = {0,1/32.f,1/16.f,1/8.f,1/4.f,1/2.f,1,2}; + + +// +// Operator Data +// + + +struct OperatorDataStruct +{ + static const int + AM1_VIB1_EGT1_KSR1_MULT4_Offset = 0x20, + KSL2_TL6_Offset = 0x40, + AR4_DR4_Offset = 0x60, + SL4_RR4_Offset = 0x80, + _5_WS3_Offset = 0xE0; + + enum type {NO_MODULATION, CARRIER, FEEDBACK}; + + static const int waveLength = 1024; + + static const float multTable[16]; + static const float ksl3dBtable[16][8]; + + //OPL3 has eight waveforms: + double waveforms[8][waveLength]; + +#define MIN_DB (-120.0) +#define DB_TABLE_RES (4.0) +//#define DB_TABLE_SIZE (int)(-MIN_DB * DB_TABLE_RES) +#define DB_TABLE_SIZE 480 /* libADLMIDI */ + + double dbpow[DB_TABLE_SIZE]; + +#define ATTACK_MIN (-5.0) +#define ATTACK_MAX (8.0) +#define ATTACK_RES (0.03125) +//#define ATTACK_TABLE_SIZE (int)((ATTACK_MAX - ATTACK_MIN) / ATTACK_RES) +#define ATTACK_TABLE_SIZE 416 /* libADLMIDI */ + + double attackTable[ATTACK_TABLE_SIZE]; + + OperatorDataStruct() + { + loadWaveforms(); + loaddBPowTable(); + loadAttackTable(); + } + + static double log2(double x) { + return log(x)/log(2.0); + } +private: + void loadWaveforms(); + void loaddBPowTable(); + void loadAttackTable(); +}; +const float OperatorDataStruct::multTable[16] = {0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15}; + +const float OperatorDataStruct::ksl3dBtable[16][8] = { + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,-3,-6,-9}, + {0,0,0,0,-3,-6,-9,-12}, + {0,0,0, -1.875, -4.875, -7.875, -10.875, -13.875}, + + {0,0,0,-3,-6,-9,-12,-15}, + {0,0, -1.125, -4.125, -7.125, -10.125, -13.125, -16.125}, + {0,0, -1.875, -4.875, -7.875, -10.875, -13.875, -16.875}, + {0,0, -2.625, -5.625, -8.625, -11.625, -14.625, -17.625}, + + {0,0,-3,-6,-9,-12,-15,-18}, + {0, -0.750, -3.750, -6.750, -9.750, -12.750, -15.750, -18.750}, + {0, -1.125, -4.125, -7.125, -10.125, -13.125, -16.125, -19.125}, + {0, -1.500, -4.500, -7.500, -10.500, -13.500, -16.500, -19.500}, + + {0, -1.875, -4.875, -7.875, -10.875, -13.875, -16.875, -19.875}, + {0, -2.250, -5.250, -8.250, -11.250, -14.250, -17.250, -20.250}, + {0, -2.625, -5.625, -8.625, -11.625, -14.625, -17.625, -20.625}, + {0,-3,-6,-9,-12,-15,-18,-21} +}; + +// +// Envelope Generator Data +// + + +namespace EnvelopeGeneratorData +{ + static const double MUGEN = std::numeric_limits::infinity(); + // This table is indexed by the value of Operator.ksr + // and the value of ChannelRegister.keyScaleNumber. + static const int rateOffset[2][16] = { + {0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3}, + {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} + }; + // These attack periods in miliseconds were taken from the YMF278B manual. + // The attack actual rates range from 0 to 63, with different data for + // 0%-100% and for 10%-90%: + static const double attackTimeValuesTable[64][2] = { + {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN}, + {2826.24,1482.75}, {2252.80,1155.07}, {1884.16,991.23}, {1597.44,868.35}, + {1413.12,741.38}, {1126.40,577.54}, {942.08,495.62}, {798.72,434.18}, + {706.56,370.69}, {563.20,288.77}, {471.04,247.81}, {399.36,217.09}, + + {353.28,185.34}, {281.60,144.38}, {235.52,123.90}, {199.68,108.54}, + {176.76,92.67}, {140.80,72.19}, {117.76,61.95}, {99.84,54.27}, + {88.32,46.34}, {70.40,36.10}, {58.88,30.98}, {49.92,27.14}, + {44.16,23.17}, {35.20,18.05}, {29.44,15.49}, {24.96,13.57}, + + {22.08,11.58}, {17.60,9.02}, {14.72,7.74}, {12.48,6.78}, + {11.04,5.79}, {8.80,4.51}, {7.36,3.87}, {6.24,3.39}, + {5.52,2.90}, {4.40,2.26}, {3.68,1.94}, {3.12,1.70}, + {2.76,1.45}, {2.20,1.13}, {1.84,0.97}, {1.56,0.85}, + + {1.40,0.73}, {1.12,0.61}, {0.92,0.49}, {0.80,0.43}, + {0.70,0.37}, {0.56,0.31}, {0.46,0.26}, {0.42,0.22}, + {0.38,0.19}, {0.30,0.14}, {0.24,0.11}, {0.20,0.11}, + {0.00,0.00}, {0.00,0.00}, {0.00,0.00}, {0.00,0.00} + }; + + // These decay and release periods in milliseconds were taken from the YMF278B manual. + // The rate index range from 0 to 63, with different data for + // 0%-100% and for 10%-90%: + static const double decayAndReleaseTimeValuesTable[64][2] = { + {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN}, + {39280.64,8212.48}, {31416.32,6574.08}, {26173.44,5509.12}, {22446.08,4730.88}, + {19640.32,4106.24}, {15708.16,3287.04}, {13086.72,2754.56}, {11223.04,2365.44}, + {9820.16,2053.12}, {7854.08,1643.52}, {6543.36,1377.28}, {5611.52,1182.72}, + + {4910.08,1026.56}, {3927.04,821.76}, {3271.68,688.64}, {2805.76,591.36}, + {2455.04,513.28}, {1936.52,410.88}, {1635.84,344.34}, {1402.88,295.68}, + {1227.52,256.64}, {981.76,205.44}, {817.92,172.16}, {701.44,147.84}, + {613.76,128.32}, {490.88,102.72}, {488.96,86.08}, {350.72,73.92}, + + {306.88,64.16}, {245.44,51.36}, {204.48,43.04}, {175.36,36.96}, + {153.44,32.08}, {122.72,25.68}, {102.24,21.52}, {87.68,18.48}, + {76.72,16.04}, {61.36,12.84}, {51.12,10.76}, {43.84,9.24}, + {38.36,8.02}, {30.68,6.42}, {25.56,5.38}, {21.92,4.62}, + + {19.20,4.02}, {15.36,3.22}, {12.80,2.68}, {10.96,2.32}, + {9.60,2.02}, {7.68,1.62}, {6.40,1.35}, {5.48,1.15}, + {4.80,1.01}, {3.84,0.81}, {3.20,0.69}, {2.74,0.58}, + {2.40,0.51}, {2.40,0.51}, {2.40,0.51}, {2.40,0.51} + }; +} + +class OPL3 +{ +public: + uint8_t registers[0x200]; + + Operator *operators[2][0x20]; + Channel2op *channels2op[2][9]; + Channel4op *channels4op[2][3]; + Channel *channels[2][9]; + + // Unique instance to fill future gaps in the Channel array, + // when there will be switches between 2op and 4op mode. + DisabledChannel disabledChannel; + + // Specific operators to switch when in rhythm mode: + HighHatOperator highHatOperator; + SnareDrumOperator snareDrumOperator; + TomTomOperator tomTomOperator; + TomTomTopCymbalChannel tomTomTopCymbalChannel; + + // Rhythm channels + BassDrumChannel bassDrumChannel; + HighHatSnareDrumChannel highHatSnareDrumChannel; + TopCymbalOperator topCymbalOperator; + + Operator *highHatOperatorInNonRhythmMode; + Operator *snareDrumOperatorInNonRhythmMode; + Operator *tomTomOperatorInNonRhythmMode; + Operator *topCymbalOperatorInNonRhythmMode; + + int nts, dam, dvb, ryt, bd, sd, tom, tc, hh, _new, connectionsel; + int vibratoIndex, tremoloIndex; + + bool FullPan; + + static OperatorDataStruct *OperatorData; + static OPL3DataStruct *OPL3Data; + + // The methods read() and write() are the only + // ones needed by the user to interface with the emulator. + // read() returns one frame at a time, to be played at 49700 Hz, + // with each frame being four 16-bit samples, + // corresponding to the OPL3 four output channels CHA...CHD. +public: + //void read(float output[2]); + void write(int array, int address, int data); + + OPL3(bool fullpan); + ~OPL3(); + +private: + void initOperators(); + void initChannels2op(); + void initChannels4op(); + void initRhythmChannels(); + void initChannels(); + void update_1_NTS1_6(); + void update_DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1(); + void update_7_NEW1(); + void setEnabledChannels(); + void updateChannelPans(); + void update_2_CONNECTIONSEL6(); + void set4opConnections(); + void setRhythmMode(); + + static int InstanceCount; + static Mutex InstanceMutex; + + // OPLEmul interface +public: + void Reset(); + void WriteReg(int reg, int v); + void Update(float *buffer, int length); + void SetPanning(int c, float left, float right); +}; + +OperatorDataStruct *OPL3::OperatorData; +OPL3DataStruct *OPL3::OPL3Data; +int OPL3::InstanceCount; +Mutex OPL3::InstanceMutex; + +void OPL3::Update(float *output, int numsamples) { + while (numsamples--) { + // If _new = 0, use OPL2 mode with 9 channels. If _new = 1, use OPL3 18 channels; + for(int array=0; array < (_new + 1); array++) + for(int channelNumber=0; channelNumber < 9; channelNumber++) { + // Reads output from each OPL3 channel, and accumulates it in the output buffer: + Channel *channel = channels[array][channelNumber]; + if (channel != &disabledChannel) + { + double channelOutput = channel->getChannelOutput(this); + output[0] += float(channelOutput * channel->leftPan); + output[1] += float(channelOutput * channel->rightPan); + } + } + + // Advances the OPL3-wide vibrato index, which is used by + // PhaseGenerator.getPhase() in each Operator. + vibratoIndex = (vibratoIndex + 1) & (OPL3DataStruct::vibratoTableLength - 1); + // Advances the OPL3-wide tremolo index, which is used by + // EnvelopeGenerator.getEnvelope() in each Operator. + tremoloIndex++; + if(tremoloIndex >= OPL3DataStruct::tremoloTableLength) tremoloIndex = 0; + output += 2; + } +} + +void OPL3::write(int array, int address, int data) { + // The OPL3 has two registers arrays, each with adresses ranging + // from 0x00 to 0xF5. + // This emulator uses one array, with the two original register arrays + // starting at 0x00 and at 0x100. + int registerAddress = (array<<8) | address; + // If the address is out of the OPL3 memory map, returns. + if(registerAddress<0 || registerAddress>=0x200) return; + + registers[registerAddress] = data; + switch(address&0xE0) { + // The first 3 bits masking gives the type of the register by using its base address: + // 0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0 + // When it is needed, we further separate the register type inside each base address, + // which is the case of 0x00 and 0xA0. + + // Through out this emulator we will use the same name convention to + // reference a byte with several bit registers. + // The name of each bit register will be followed by the number of bits + // it occupies inside the byte. + // Numbers without accompanying names are unused bits. + case 0x00: + // Unique registers for the entire OPL3: + if(array==1) { + if(address==0x04) + update_2_CONNECTIONSEL6(); + else if(address==0x05) + update_7_NEW1(); + } + else if(address==0x08) update_1_NTS1_6(); + break; + + case 0xA0: + // 0xBD is a control register for the entire OPL3: + if(address==0xBD) { + if(array==0) + update_DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1(); + break; + } + // Registers for each channel are in A0-A8, B0-B8, C0-C8, in both register arrays. + // 0xB0...0xB8 keeps kon,block,fnum(h) for each channel. + if( (address&0xF0) == 0xB0 && address <= 0xB8) { + // If the address is in the second register array, adds 9 to the channel number. + // The channel number is given by the last four bits, like in A0,...,A8. + channels[array][address&0x0F]->update_2_KON1_BLOCK3_FNUMH2(this); + break; + } + // 0xA0...0xA8 keeps fnum(l) for each channel. + if( (address&0xF0) == 0xA0 && address <= 0xA8) + channels[array][address&0x0F]->update_FNUML8(this); + break; + // 0xC0...0xC8 keeps cha,chb,chc,chd,fb,cnt for each channel: + case 0xC0: + if(address <= 0xC8) + channels[array][address&0x0F]->update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(this); + break; + + // Registers for each of the 36 Operators: + default: + int operatorOffset = address&0x1F; + if(operators[array][operatorOffset] == NULL) break; + switch(address&0xE0) { + // 0x20...0x35 keeps am,vib,egt,ksr,mult for each operator: + case 0x20: + operators[array][operatorOffset]->update_AM1_VIB1_EGT1_KSR1_MULT4(this); + break; + // 0x40...0x55 keeps ksl,tl for each operator: + case 0x40: + operators[array][operatorOffset]->update_KSL2_TL6(this); + break; + // 0x60...0x75 keeps ar,dr for each operator: + case 0x60: + operators[array][operatorOffset]->update_AR4_DR4(this); + break; + // 0x80...0x95 keeps sl,rr for each operator: + case 0x80: + operators[array][operatorOffset]->update_SL4_RR4(this); + break; + // 0xE0...0xF5 keeps ws for each operator: + case 0xE0: + operators[array][operatorOffset]->update_5_WS3(this); + } + } +} + +OPL3::OPL3(bool fullpan) +: tomTomTopCymbalChannel(fullpan ? CENTER_PANNING_POWER : 1, &tomTomOperator, &topCymbalOperator), + bassDrumChannel(fullpan ? CENTER_PANNING_POWER : 1), + highHatSnareDrumChannel(fullpan ? CENTER_PANNING_POWER : 1, &highHatOperator, &snareDrumOperator) +{ + FullPan = fullpan; + nts = dam = dvb = ryt = bd = sd = tom = tc = hh = _new = connectionsel = 0; + vibratoIndex = tremoloIndex = 0; + + { + MutexHolder lock(InstanceMutex); + if (InstanceCount++ == 0) + { + OPL3Data = new struct OPL3DataStruct; + OperatorData = new struct OperatorDataStruct; + } + } + + initOperators(); + initChannels2op(); + initChannels4op(); + initRhythmChannels(); + initChannels(); +} + +OPL3::~OPL3() +{ + ryt = 0; + setRhythmMode(); // Make sure all operators point to the dynamically allocated ones. + for (int array = 0; array < 2; array++) + { + for (int operatorNumber = 0; operatorNumber < 0x20; operatorNumber++) + { + if (operators[array][operatorNumber] != NULL) + { + delete operators[array][operatorNumber]; + } + } + for (int channelNumber = 0; channelNumber < 9; channelNumber++) + { + delete channels2op[array][channelNumber]; + } + for (int channelNumber = 0; channelNumber < 3; channelNumber++) + { + delete channels4op[array][channelNumber]; + } + } + + { + MutexHolder lock(InstanceMutex); + if (--InstanceCount == 0) + { + delete OPL3Data; + OPL3Data = NULL; + delete OperatorData; + OperatorData = NULL; + } + } +} + + +void OPL3::initOperators() { + memset(registers, 0, sizeof(registers)); + + int baseAddress; + // The YMF262 has 36 operators: + memset(operators, 0, sizeof(operators)); + for(int array=0; array<2; array++) + for(int group = 0; group<=0x10; group+=8) + for(int offset=0; offset<6; offset++) { + baseAddress = (array<<8) | (group+offset); + operators[array][group+offset] = new Operator(baseAddress); + } + + // Save operators when they are in non-rhythm mode: + // Channel 7: + highHatOperatorInNonRhythmMode = operators[0][0x11]; + snareDrumOperatorInNonRhythmMode = operators[0][0x14]; + // Channel 8: + tomTomOperatorInNonRhythmMode = operators[0][0x12]; + topCymbalOperatorInNonRhythmMode = operators[0][0x15]; + +} + +void OPL3::initChannels2op() { + // The YMF262 has 18 2-op channels. + // Each 2-op channel can be at a serial or parallel operator configuration: + memset(channels2op, 0, sizeof(channels2op)); + double startvol = FullPan ? CENTER_PANNING_POWER : 1; + for(int array=0; array<2; array++) + for(int channelNumber=0; channelNumber<3; channelNumber++) { + int baseAddress = (array<<8) | channelNumber; + // Channels 1, 2, 3 -> Operator offsets 0x0,0x3; 0x1,0x4; 0x2,0x5 + channels2op[array][channelNumber] = new Channel2op(baseAddress, startvol, operators[array][channelNumber], operators[array][channelNumber+0x3]); + // Channels 4, 5, 6 -> Operator offsets 0x8,0xB; 0x9,0xC; 0xA,0xD + channels2op[array][channelNumber+3] = new Channel2op(baseAddress+3, startvol, operators[array][channelNumber+0x8], operators[array][channelNumber+0xB]); + // Channels 7, 8, 9 -> Operators 0x10,0x13; 0x11,0x14; 0x12,0x15 + channels2op[array][channelNumber+6] = new Channel2op(baseAddress+6, startvol, operators[array][channelNumber+0x10], operators[array][channelNumber+0x13]); + } +} + +void OPL3::initChannels4op() { + // The YMF262 has 3 4-op channels in each array: + memset(channels4op, 0, sizeof(channels4op)); + double startvol = FullPan ? CENTER_PANNING_POWER : 1; + for(int array=0; array<2; array++) + for(int channelNumber=0; channelNumber<3; channelNumber++) { + int baseAddress = (array<<8) | channelNumber; + // Channels 1, 2, 3 -> Operators 0x0,0x3,0x8,0xB; 0x1,0x4,0x9,0xC; 0x2,0x5,0xA,0xD; + channels4op[array][channelNumber] = new Channel4op(baseAddress, startvol, operators[array][channelNumber], operators[array][channelNumber+0x3], operators[array][channelNumber+0x8], operators[array][channelNumber+0xB]); + } +} + +void OPL3::initRhythmChannels() { +} + +void OPL3::initChannels() { + // Channel is an abstract class that can be a 2-op, 4-op, rhythm or disabled channel, + // depending on the OPL3 configuration at the time. + // channels[] inits as a 2-op serial channel array: + for(int array=0; array<2; array++) + for(int i=0; i<9; i++) channels[array][i] = channels2op[array][i]; +} + +void OPL3::update_1_NTS1_6() { + int _1_nts1_6 = registers[OPL3DataStruct::_1_NTS1_6_Offset]; + // Note Selection. This register is used in Channel.updateOperators() implementations, + // to calculate the channel´s Key Scale Number. + // The value of the actual envelope rate follows the value of + // OPL3.nts,Operator.keyScaleNumber and Operator.ksr + nts = (_1_nts1_6 & 0x40) >> 6; +} + +void OPL3::update_DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1() { + int dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 = registers[OPL3DataStruct::DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1_Offset]; + // Depth of amplitude. This register is used in EnvelopeGenerator.getEnvelope(); + dam = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x80) >> 7; + + // Depth of vibrato. This register is used in PhaseGenerator.getPhase(); + dvb = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x40) >> 6; + + int new_ryt = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x20) >> 5; + if(new_ryt != ryt) { + ryt = new_ryt; + setRhythmMode(); + } + + int new_bd = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x10) >> 4; + if(new_bd != bd) { + bd = new_bd; + if(bd==1) { + bassDrumChannel.op1->keyOn(); + bassDrumChannel.op2->keyOn(); + } + } + + int new_sd = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x08) >> 3; + if(new_sd != sd) { + sd = new_sd; + if(sd==1) snareDrumOperator.keyOn(); + } + + int new_tom = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x04) >> 2; + if(new_tom != tom) { + tom = new_tom; + if(tom==1) tomTomOperator.keyOn(); + } + + int new_tc = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x02) >> 1; + if(new_tc != tc) { + tc = new_tc; + if(tc==1) topCymbalOperator.keyOn(); + } + + int new_hh = dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x01; + if(new_hh != hh) { + hh = new_hh; + if(hh==1) highHatOperator.keyOn(); + } + +} + +void OPL3::update_7_NEW1() { + int _7_new1 = registers[OPL3DataStruct::_7_NEW1_Offset]; + // OPL2/OPL3 mode selection. This register is used in + // OPL3.read(), OPL3.write() and Operator.getOperatorOutput(); + _new = (_7_new1 & 0x01); + if(_new==1) setEnabledChannels(); + set4opConnections(); + updateChannelPans(); +} + +void OPL3::setEnabledChannels() { + for(int array=0; array<2; array++) + for(int i=0; i<9; i++) { + int baseAddress = channels[array][i]->channelBaseAddress; + registers[baseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset] |= 0xF0; + channels[array][i]->update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(this); + } +} + +void OPL3::updateChannelPans() { + for(int array=0; array<2; array++) + for(int i=0; i<9; i++) { + int baseAddress = channels[array][i]->channelBaseAddress; + registers[baseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset] |= 0xF0; + channels[array][i]->updatePan(this); + } + +} + +void OPL3::update_2_CONNECTIONSEL6() { + // This method is called only if _new is set. + int _2_connectionsel6 = registers[OPL3DataStruct::_2_CONNECTIONSEL6_Offset]; + // 2-op/4-op channel selection. This register is used here to configure the OPL3.channels[] array. + connectionsel = (_2_connectionsel6 & 0x3F); + set4opConnections(); +} + +void OPL3::set4opConnections() { + // bits 0, 1, 2 sets respectively 2-op channels (1,4), (2,5), (3,6) to 4-op operation. + // bits 3, 4, 5 sets respectively 2-op channels (10,13), (11,14), (12,15) to 4-op operation. + for(int array=0; array<2; array++) + for(int i=0; i<3; i++) { + if(_new == 1) { + int shift = array*3 + i; + int connectionBit = (connectionsel >> shift) & 0x01; + if(connectionBit == 1) { + channels[array][i] = channels4op[array][i]; + channels[array][i+3] = &disabledChannel; + channels[array][i]->updateChannel(this); + continue; + } + } + channels[array][i] = channels2op[array][i]; + channels[array][i+3] = channels2op[array][i+3]; + channels[array][i]->updateChannel(this); + channels[array][i+3]->updateChannel(this); + } +} + +void OPL3::setRhythmMode() { + if(ryt==1) { + channels[0][6] = &bassDrumChannel; + channels[0][7] = &highHatSnareDrumChannel; + channels[0][8] = &tomTomTopCymbalChannel; + operators[0][0x11] = &highHatOperator; + operators[0][0x14] = &snareDrumOperator; + operators[0][0x12] = &tomTomOperator; + operators[0][0x15] = &topCymbalOperator; + } + else { + for(int i=6; i<=8; i++) channels[0][i] = channels2op[0][i]; + operators[0][0x11] = highHatOperatorInNonRhythmMode; + operators[0][0x14] = snareDrumOperatorInNonRhythmMode; + operators[0][0x12] = tomTomOperatorInNonRhythmMode; + operators[0][0x15] = topCymbalOperatorInNonRhythmMode; + } + for(int i=6; i<=8; i++) channels[0][i]->updateChannel(this); +} + +static double EnvelopeFromDB(double db) +{ +#if 0 + return pow(10.0, db/10); +#else + if (db < MIN_DB) + return 0; + return OPL3::OperatorData->dbpow[(int)floor(-db * DB_TABLE_RES)]; +#endif +} + +Channel::Channel (int baseAddress, double startvol) { + channelBaseAddress = baseAddress; + fnuml = fnumh = kon = block = fb = cha = chb = cnt = 0; + feedback[0] = feedback[1] = 0; + leftPan = rightPan = startvol; +} + +void Channel::update_2_KON1_BLOCK3_FNUMH2(OPL3 *OPL3) { + + int _2_kon1_block3_fnumh2 = OPL3->registers[channelBaseAddress+ChannelData::_2_KON1_BLOCK3_FNUMH2_Offset]; + + // Frequency Number (hi-register) and Block. These two registers, together with fnuml, + // sets the Channel´s base frequency; + block = (_2_kon1_block3_fnumh2 & 0x1C) >> 2; + fnumh = _2_kon1_block3_fnumh2 & 0x03; + updateOperators(OPL3); + + // Key On. If changed, calls Channel.keyOn() / keyOff(). + int newKon = (_2_kon1_block3_fnumh2 & 0x20) >> 5; + if(newKon != kon) { + if(newKon == 1) keyOn(); + else keyOff(); + kon = newKon; + } +} + +void Channel::update_FNUML8(OPL3 *OPL3) { + int fnuml8 = OPL3->registers[channelBaseAddress+ChannelData::FNUML8_Offset]; + // Frequency Number, low register. + fnuml = fnuml8&0xFF; + updateOperators(OPL3); +} + +void Channel::update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(OPL3 *OPL3) { + int chd1_chc1_chb1_cha1_fb3_cnt1 = OPL3->registers[channelBaseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset]; +// chd = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x80) >> 7; +// chc = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x40) >> 6; + chb = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x20) >> 5; + cha = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x10) >> 4; + fb = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x0E) >> 1; + cnt = chd1_chc1_chb1_cha1_fb3_cnt1 & 0x01; + updatePan(OPL3); + updateOperators(OPL3); +} + +void Channel::updatePan(OPL3 *OPL3) { + if (!OPL3->FullPan) + { + if (OPL3->_new == 0) + { + leftPan = VOLUME_MUL; + rightPan = VOLUME_MUL; + } + else + { + leftPan = cha * VOLUME_MUL; + rightPan = chb * VOLUME_MUL; + } + } +} + +void Channel::updateChannel(OPL3 *OPL3) { + update_2_KON1_BLOCK3_FNUMH2(OPL3); + update_FNUML8(OPL3); + update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(OPL3); +} + +Channel2op::Channel2op (int baseAddress, double startvol, Operator *o1, Operator *o2) +: Channel(baseAddress, startvol) +{ + op1 = o1; + op2 = o2; +} + +double Channel2op::getChannelOutput(OPL3 *OPL3) { + double channelOutput = 0, op1Output = 0, op2Output = 0; + // The feedback uses the last two outputs from + // the first operator, instead of just the last one. + double feedbackOutput = (feedback[0] + feedback[1]) / 2; + + switch(cnt) { + // CNT = 0, the operators are in series, with the first in feedback. + case 0: + if(op2->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + channelOutput = op2->getOperatorOutput(OPL3, op1Output*toPhase); + break; + // CNT = 1, the operators are in parallel, with the first in feedback. + case 1: + if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF && + op2->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator); + channelOutput = (op1Output + op2Output) / 2; + } + + feedback[0] = feedback[1]; + feedback[1] = StripIntPart(op1Output * ChannelData::feedback[fb]); + return channelOutput; +} + +void Channel2op::keyOn() { + op1->keyOn(); + op2->keyOn(); + feedback[0] = feedback[1] = 0; +} + +void Channel2op::keyOff() { + op1->keyOff(); + op2->keyOff(); +} + +void Channel2op::updateOperators(OPL3 *OPL3) { + // Key Scale Number, used in EnvelopeGenerator.setActualRates(). + int keyScaleNumber = block*2 + ((fnumh>>OPL3->nts)&0x01); + int f_number = (fnumh<<8) | fnuml; + op1->updateOperator(OPL3, keyScaleNumber, f_number, block); + op2->updateOperator(OPL3, keyScaleNumber, f_number, block); +} + +Channel4op::Channel4op (int baseAddress, double startvol, Operator *o1, Operator *o2, Operator *o3, Operator *o4) +: Channel(baseAddress, startvol) +{ + op1 = o1; + op2 = o2; + op3 = o3; + op4 = o4; +} + +double Channel4op::getChannelOutput(OPL3 *OPL3) { + double channelOutput = 0, + op1Output = 0, op2Output = 0, op3Output = 0, op4Output = 0; + + int secondChannelBaseAddress = channelBaseAddress+3; + int secondCnt = OPL3->registers[secondChannelBaseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset] & 0x1; + int cnt4op = (cnt << 1) | secondCnt; + + double feedbackOutput = (feedback[0] + feedback[1]) / 2; + + switch(cnt4op) { + case 0: + if(op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + op2Output = op2->getOperatorOutput(OPL3, op1Output*toPhase); + op3Output = op3->getOperatorOutput(OPL3, op2Output*toPhase); + channelOutput = op4->getOperatorOutput(OPL3, op3Output*toPhase); + + break; + case 1: + if(op2->envelopeGenerator.stage==EnvelopeGenerator::OFF && + op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + op2Output = op2->getOperatorOutput(OPL3, op1Output*toPhase); + + op3Output = op3->getOperatorOutput(OPL3, Operator::noModulator); + op4Output = op4->getOperatorOutput(OPL3, op3Output*toPhase); + + channelOutput = (op2Output + op4Output) / 2; + break; + case 2: + if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF && + op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + + op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator); + op3Output = op3->getOperatorOutput(OPL3, op2Output*toPhase); + op4Output = op4->getOperatorOutput(OPL3, op3Output*toPhase); + + channelOutput = (op1Output + op4Output) / 2; + break; + case 3: + if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF && + op3->envelopeGenerator.stage==EnvelopeGenerator::OFF && + op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + + op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator); + op3Output = op3->getOperatorOutput(OPL3, op2Output*toPhase); + + op4Output = op4->getOperatorOutput(OPL3, Operator::noModulator); + + channelOutput = (op1Output + op3Output + op4Output) / 3; + } + + feedback[0] = feedback[1]; + feedback[1] = StripIntPart(op1Output * ChannelData::feedback[fb]); + + return channelOutput; +} + +void Channel4op::keyOn() { + op1->keyOn(); + op2->keyOn(); + op3->keyOn(); + op4->keyOn(); + feedback[0] = feedback[1] = 0; +} + +void Channel4op::keyOff() { + op1->keyOff(); + op2->keyOff(); + op3->keyOff(); + op4->keyOff(); +} + +void Channel4op::updateOperators(OPL3 *OPL3) { + // Key Scale Number, used in EnvelopeGenerator.setActualRates(). + int keyScaleNumber = block*2 + ((fnumh>>OPL3->nts)&0x01); + int f_number = (fnumh<<8) | fnuml; + op1->updateOperator(OPL3, keyScaleNumber, f_number, block); + op2->updateOperator(OPL3, keyScaleNumber, f_number, block); + op3->updateOperator(OPL3, keyScaleNumber, f_number, block); + op4->updateOperator(OPL3, keyScaleNumber, f_number, block); +} + +const double Operator::noModulator = 0; + +Operator::Operator(int baseAddress) { + operatorBaseAddress = baseAddress; + + envelope = phase = 0; + am = vib = ksr = egt = mult = ksl = tl = ar = dr = sl = rr = ws = 0; + keyScaleNumber = f_number = block = 0; +} + +void Operator::update_AM1_VIB1_EGT1_KSR1_MULT4(OPL3 *OPL3) { + + int am1_vib1_egt1_ksr1_mult4 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::AM1_VIB1_EGT1_KSR1_MULT4_Offset]; + + // Amplitude Modulation. This register is used int EnvelopeGenerator.getEnvelope(); + am = (am1_vib1_egt1_ksr1_mult4 & 0x80) >> 7; + // Vibrato. This register is used in PhaseGenerator.getPhase(); + vib = (am1_vib1_egt1_ksr1_mult4 & 0x40) >> 6; + // Envelope Generator Type. This register is used in EnvelopeGenerator.getEnvelope(); + egt = (am1_vib1_egt1_ksr1_mult4 & 0x20) >> 5; + // Key Scale Rate. Sets the actual envelope rate together with rate and keyScaleNumber. + // This register os used in EnvelopeGenerator.setActualAttackRate(). + ksr = (am1_vib1_egt1_ksr1_mult4 & 0x10) >> 4; + // Multiple. Multiplies the Channel.baseFrequency to get the Operator.operatorFrequency. + // This register is used in PhaseGenerator.setFrequency(). + mult = am1_vib1_egt1_ksr1_mult4 & 0x0F; + + phaseGenerator.setFrequency(f_number, block, mult); + envelopeGenerator.setActualAttackRate(ar, ksr, keyScaleNumber); + envelopeGenerator.setActualDecayRate(dr, ksr, keyScaleNumber); + envelopeGenerator.setActualReleaseRate(rr, ksr, keyScaleNumber); +} + +void Operator::update_KSL2_TL6(OPL3 *OPL3) { + + int ksl2_tl6 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::KSL2_TL6_Offset]; + + // Key Scale Level. Sets the attenuation in accordance with the octave. + ksl = (ksl2_tl6 & 0xC0) >> 6; + // Total Level. Sets the overall damping for the envelope. + tl = ksl2_tl6 & 0x3F; + + envelopeGenerator.setAtennuation(f_number, block, ksl); + envelopeGenerator.setTotalLevel(tl); +} + +void Operator::update_AR4_DR4(OPL3 *OPL3) { + + int ar4_dr4 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::AR4_DR4_Offset]; + + // Attack Rate. + ar = (ar4_dr4 & 0xF0) >> 4; + // Decay Rate. + dr = ar4_dr4 & 0x0F; + + envelopeGenerator.setActualAttackRate(ar, ksr, keyScaleNumber); + envelopeGenerator.setActualDecayRate(dr, ksr, keyScaleNumber); +} + +void Operator::update_SL4_RR4(OPL3 *OPL3) { + + int sl4_rr4 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::SL4_RR4_Offset]; + + // Sustain Level. + sl = (sl4_rr4 & 0xF0) >> 4; + // Release Rate. + rr = sl4_rr4 & 0x0F; + + envelopeGenerator.setActualSustainLevel(sl); + envelopeGenerator.setActualReleaseRate(rr, ksr, keyScaleNumber); +} + +void Operator::update_5_WS3(OPL3 *OPL3) { + int _5_ws3 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::_5_WS3_Offset]; + ws = _5_ws3 & 0x07; +} + +double Operator::getOperatorOutput(OPL3 *OPL3, double modulator) { + if(envelopeGenerator.stage == EnvelopeGenerator::OFF) return 0; + + double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am); + envelope = EnvelopeFromDB(envelopeInDB); + + // If it is in OPL2 mode, use first four waveforms only: + ws &= ((OPL3->_new<<2) + 3); + double *waveform = OPL3::OperatorData->waveforms[ws]; + + phase = phaseGenerator.getPhase(OPL3, vib); + + double operatorOutput = getOutput(modulator, phase, waveform); + return operatorOutput; +} + +double Operator::getOutput(double modulator, double outputPhase, double *waveform) { + int sampleIndex = (int)floor((outputPhase + modulator) * OperatorDataStruct::waveLength) & (OperatorDataStruct::waveLength - 1); + return waveform[sampleIndex] * envelope; +} + +void Operator::keyOn() { + if(ar > 0) { + envelopeGenerator.keyOn(); + phaseGenerator.keyOn(); + } + else envelopeGenerator.stage = EnvelopeGenerator::OFF; +} + +void Operator::keyOff() { + envelopeGenerator.keyOff(); +} + +void Operator::updateOperator(OPL3 *OPL3, int ksn, int f_num, int blk) { + keyScaleNumber = ksn; + f_number = f_num; + block = blk; + update_AM1_VIB1_EGT1_KSR1_MULT4(OPL3); + update_KSL2_TL6(OPL3); + update_AR4_DR4(OPL3); + update_SL4_RR4(OPL3); + update_5_WS3(OPL3); +} + +EnvelopeGenerator::EnvelopeGenerator() { + stage = OFF; + actualAttackRate = actualDecayRate = actualReleaseRate = 0; + xAttackIncrement = xMinimumInAttack = 0; + dBdecayIncrement = 0; + dBreleaseIncrement = 0; + attenuation = totalLevel = sustainLevel = 0; + x = dBtoX(-96); + envelope = -96; +} + +void EnvelopeGenerator::setActualSustainLevel(int sl) { + // If all SL bits are 1, sustain level is set to -93 dB: + if(sl == 0x0F) { + sustainLevel = -93; + return; + } + // The datasheet states that the SL formula is + // sustainLevel = -24*d7 -12*d6 -6*d5 -3*d4, + // translated as: + sustainLevel = -3*sl; +} + +void EnvelopeGenerator::setTotalLevel(int tl) { + // The datasheet states that the TL formula is + // TL = -(24*d5 + 12*d4 + 6*d3 + 3*d2 + 1.5*d1 + 0.75*d0), + // translated as: + totalLevel = tl*-0.75; +} + +void EnvelopeGenerator::setAtennuation(int f_number, int block, int ksl) { + int hi4bits = (f_number>>6)&0x0F; + switch(ksl) { + case 0: + attenuation = 0; + break; + case 1: + // ~3 dB/Octave + attenuation = OperatorDataStruct::ksl3dBtable[hi4bits][block]; + break; + case 2: + // ~1.5 dB/Octave + attenuation = OperatorDataStruct::ksl3dBtable[hi4bits][block]/2; + break; + case 3: + // ~6 dB/Octave + attenuation = OperatorDataStruct::ksl3dBtable[hi4bits][block]*2; + } +} + +void EnvelopeGenerator::setActualAttackRate(int attackRate, int ksr, int keyScaleNumber) { + // According to the YMF278B manual's OPL3 section, the attack curve is exponential, + // with a dynamic range from -96 dB to 0 dB and a resolution of 0.1875 dB + // per level. + // + // This method sets an attack increment and attack minimum value + // that creates a exponential dB curve with 'period0to100' seconds in length + // and 'period10to90' seconds between 10% and 90% of the curve total level. + actualAttackRate = calculateActualRate(attackRate, ksr, keyScaleNumber); + double period0to100inSeconds = EnvelopeGeneratorData::attackTimeValuesTable[actualAttackRate][0]/1000.0; + int period0to100inSamples = (int)(period0to100inSeconds*OPL_SAMPLE_RATE); + double period10to90inSeconds = EnvelopeGeneratorData::attackTimeValuesTable[actualAttackRate][1]/1000.0; + int period10to90inSamples = (int)(period10to90inSeconds*OPL_SAMPLE_RATE); + // The x increment is dictated by the period between 10% and 90%: + xAttackIncrement = OPL3DataStruct::calculateIncrement(percentageToX(0.1), percentageToX(0.9), period10to90inSeconds); + // Discover how many samples are still from the top. + // It cannot reach 0 dB, since x is a logarithmic parameter and would be + // negative infinity. So we will use -0.1875 dB as the resolution + // maximum. + // + // percentageToX(0.9) + samplesToTheTop*xAttackIncrement = dBToX(-0.1875); -> + // samplesToTheTop = (dBtoX(-0.1875) - percentageToX(0.9)) / xAttackIncrement); -> + // period10to100InSamples = period10to90InSamples + samplesToTheTop; -> + int period10to100inSamples = (int) (period10to90inSamples + (dBtoX(-0.1875) - percentageToX(0.9)) / xAttackIncrement); + // Discover the minimum x that, through the attackIncrement value, keeps + // the 10%-90% period, and reaches 0 dB at the total period: + xMinimumInAttack = percentageToX(0.1) - (period0to100inSamples-period10to100inSamples)*xAttackIncrement; +} + + +void EnvelopeGenerator::setActualDecayRate(int decayRate, int ksr, int keyScaleNumber) { + actualDecayRate = calculateActualRate(decayRate, ksr, keyScaleNumber); + double period10to90inSeconds = EnvelopeGeneratorData::decayAndReleaseTimeValuesTable[actualDecayRate][1]/1000.0; + // Differently from the attack curve, the decay/release curve is linear. + // The dB increment is dictated by the period between 10% and 90%: + dBdecayIncrement = OPL3DataStruct::calculateIncrement(percentageToDB(0.1), percentageToDB(0.9), period10to90inSeconds); +} + +void EnvelopeGenerator::setActualReleaseRate(int releaseRate, int ksr, int keyScaleNumber) { + actualReleaseRate = calculateActualRate(releaseRate, ksr, keyScaleNumber); + double period10to90inSeconds = EnvelopeGeneratorData::decayAndReleaseTimeValuesTable[actualReleaseRate][1]/1000.0; + dBreleaseIncrement = OPL3DataStruct::calculateIncrement(percentageToDB(0.1), percentageToDB(0.9), period10to90inSeconds); +} + +int EnvelopeGenerator::calculateActualRate(int rate, int ksr, int keyScaleNumber) { + int rof = EnvelopeGeneratorData::rateOffset[ksr][keyScaleNumber]; + int actualRate = rate*4 + rof; + // If, as an example at the maximum, rate is 15 and the rate offset is 15, + // the value would + // be 75, but the maximum allowed is 63: + if(actualRate > 63) actualRate = 63; + return actualRate; +} + +double EnvelopeGenerator::getEnvelope(OPL3 *OPL3, int egt, int am) { + // The datasheets attenuation values + // must be halved to match the real OPL3 output. + double envelopeSustainLevel = sustainLevel / 2; + double envelopeTremolo = + OPL3::OPL3Data->tremoloTable[OPL3->dam][OPL3->tremoloIndex] / 2; + double envelopeAttenuation = attenuation / 2; + double envelopeTotalLevel = totalLevel / 2; + + double envelopeMinimum = -96; + double envelopeResolution = 0.1875; + + double outputEnvelope; + // + // Envelope Generation + // + switch(stage) { + case ATTACK: + // Since the attack is exponential, it will never reach 0 dB, so + // we´ll work with the next to maximum in the envelope resolution. + if(envelope<-envelopeResolution && xAttackIncrement != -EnvelopeGeneratorData::MUGEN) { + // The attack is exponential. +#if 0 + envelope = -pow(2.0,x); +#else + int index = (int)floor((x - ATTACK_MIN) / ATTACK_RES); + if (index < 0) + envelope = OPL3::OperatorData->attackTable[0]; + else if (index >= ATTACK_TABLE_SIZE) + envelope = OPL3::OperatorData->attackTable[ATTACK_TABLE_SIZE-1]; + else + envelope = OPL3::OperatorData->attackTable[index]; +#endif + x += xAttackIncrement; + break; + } + else { + // It is needed here to explicitly set envelope = 0, since + // only the attack can have a period of + // 0 seconds and produce an infinity envelope increment. + envelope = 0; + stage = DECAY; + } + // libADLMIDI: + // fall through + case DECAY: + // The decay and release are linear. + if(envelope>envelopeSustainLevel) { + envelope -= dBdecayIncrement; + break; + } + else + stage = SUSTAIN; + // libADLMIDI: + // fall through + case SUSTAIN: + // The Sustain stage is mantained all the time of the Key ON, + // even if we are in non-sustaining mode. + // This is necessary because, if the key is still pressed, we can + // change back and forth the state of EGT, and it will release and + // hold again accordingly. + if(egt==1) break; + else { + if(envelope > envelopeMinimum) + envelope -= dBreleaseIncrement; + else stage = OFF; + } + break; + case RELEASE: + // If we have Key OFF, only here we are in the Release stage. + // Now, we can turn EGT back and forth and it will have no effect,i.e., + // it will release inexorably to the Off stage. + if(envelope > envelopeMinimum) + envelope -= dBreleaseIncrement; + else stage = OFF; + case OFF: + break; + } + + // Ongoing original envelope + outputEnvelope = envelope; + + //Tremolo + if(am == 1) outputEnvelope += envelopeTremolo; + + //Attenuation + outputEnvelope += envelopeAttenuation; + + //Total Level + outputEnvelope += envelopeTotalLevel; + + return outputEnvelope; +} + +void EnvelopeGenerator::keyOn() { + // If we are taking it in the middle of a previous envelope, + // start to rise from the current level: + // envelope = - (2 ^ x); -> + // 2 ^ x = -envelope -> + // x = log2(-envelope); -> + double xCurrent = OperatorDataStruct::log2(-envelope); + x = xCurrent < xMinimumInAttack ? xCurrent : xMinimumInAttack; + stage = ATTACK; +} + +void EnvelopeGenerator::keyOff() { + if(stage != OFF) stage = RELEASE; +} + +double EnvelopeGenerator::dBtoX(double dB) { + return OperatorDataStruct::log2(-dB); +} + +double EnvelopeGenerator::percentageToDB(double percentage) { + return log10(percentage) * 10.0; +} + +double EnvelopeGenerator::percentageToX(double percentage) { + return dBtoX(percentageToDB(percentage)); +} + +PhaseGenerator::PhaseGenerator() { + phase = phaseIncrement = 0; +} + +void PhaseGenerator::setFrequency(int f_number, int block, int mult) { + // This frequency formula is derived from the following equation: + // f_number = baseFrequency * pow(2,19) / OPL_SAMPLE_RATE / pow(2,block-1); + double baseFrequency = + f_number * pow(2.0, block-1) * OPL_SAMPLE_RATE / pow(2.0,19); + double operatorFrequency = baseFrequency*OperatorDataStruct::multTable[mult]; + + // phase goes from 0 to 1 at + // period = (1/frequency) seconds -> + // Samples in each period is (1/frequency)*OPL_SAMPLE_RATE = + // = OPL_SAMPLE_RATE/frequency -> + // So the increment in each sample, to go from 0 to 1, is: + // increment = (1-0) / samples in the period -> + // increment = 1 / (OPL_SAMPLE_RATE/operatorFrequency) -> + phaseIncrement = operatorFrequency/OPL_SAMPLE_RATE; +} + +double PhaseGenerator::getPhase(OPL3 *OPL3, int vib) { + if(vib==1) + // phaseIncrement = (operatorFrequency * vibrato) / OPL_SAMPLE_RATE + phase += phaseIncrement*OPL3::OPL3Data->vibratoTable[OPL3->dvb][OPL3->vibratoIndex]; + else + // phaseIncrement = operatorFrequency / OPL_SAMPLE_RATE + phase += phaseIncrement; + // Originally clamped phase to [0,1), but that's not needed + return phase; +} + +void PhaseGenerator::keyOn() { + phase = 0; +} + +double RhythmChannel::getChannelOutput(OPL3 *OPL3) { + double channelOutput = 0, op1Output = 0, op2Output = 0; + + // Note that, different from the common channel, + // we do not check to see if the Operator's envelopes are Off. + // Instead, we always do the calculations, + // to update the publicly available phase. + op1Output = op1->getOperatorOutput(OPL3, Operator::noModulator); + op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator); + channelOutput = (op1Output + op2Output) / 2; + + return channelOutput; +} + +TopCymbalOperator::TopCymbalOperator(int baseAddress) +: Operator(baseAddress) +{ } + +TopCymbalOperator::TopCymbalOperator() +: Operator(topCymbalOperatorBaseAddress) +{ } + +double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator) { + double highHatOperatorPhase = + OPL3->highHatOperator.phase * OperatorDataStruct::multTable[OPL3->highHatOperator.mult]; + // The Top Cymbal operator uses its own phase together with the High Hat phase. + return getOperatorOutput(OPL3, modulator, highHatOperatorPhase); +} + +// This method is used here with the HighHatOperator phase +// as the externalPhase. +// Conversely, this method is also used through inheritance by the HighHatOperator, +// now with the TopCymbalOperator phase as the externalPhase. +double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator, double externalPhase) { + (void)modulator; + + double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am); + envelope = EnvelopeFromDB(envelopeInDB); + + phase = phaseGenerator.getPhase(OPL3, vib); + + int waveIndex = ws & ((OPL3->_new<<2) + 3); + double *waveform = OPL3::OperatorData->waveforms[waveIndex]; + + // Empirically tested multiplied phase for the Top Cymbal: + double carrierPhase = 8 * phase; + double modulatorPhase = externalPhase; + double modulatorOutput = getOutput(Operator::noModulator, modulatorPhase, waveform); + double carrierOutput = getOutput(modulatorOutput, carrierPhase, waveform); + + int cycles = 4; + double chopped = (carrierPhase * cycles) /* %cycles */; + chopped = chopped - floor(chopped / cycles) * cycles; + if( chopped > 0.1) carrierOutput = 0; + + return carrierOutput*2; +} + +HighHatOperator::HighHatOperator() +: TopCymbalOperator(highHatOperatorBaseAddress) +{ } + +double HighHatOperator::getOperatorOutput(OPL3 *OPL3, double modulator) { + double topCymbalOperatorPhase = + OPL3->topCymbalOperator.phase * OperatorDataStruct::multTable[OPL3->topCymbalOperator.mult]; + // The sound output from the High Hat resembles the one from + // Top Cymbal, so we use the parent method and modify its output + // accordingly afterwards. + double operatorOutput = TopCymbalOperator::getOperatorOutput(OPL3, modulator, topCymbalOperatorPhase); + if(operatorOutput == 0) operatorOutput = pr_opl3.GenRand_Real1()*envelope; + return operatorOutput; +} + +SnareDrumOperator::SnareDrumOperator() +: Operator(snareDrumOperatorBaseAddress) +{ } + +double SnareDrumOperator::getOperatorOutput(OPL3 *OPL3, double modulator) { + if(envelopeGenerator.stage == EnvelopeGenerator::OFF) return 0; + + double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am); + envelope = EnvelopeFromDB(envelopeInDB); + + // If it is in OPL2 mode, use first four waveforms only: + int waveIndex = ws & ((OPL3->_new<<2) + 3); + double *waveform = OPL3::OperatorData->waveforms[waveIndex]; + + phase = OPL3->highHatOperator.phase * 2; + + double operatorOutput = getOutput(modulator, phase, waveform); + + double noise = pr_opl3.GenRand_Real1() * envelope; + + if(operatorOutput/envelope != 1 && operatorOutput/envelope != -1) { + if(operatorOutput > 0) operatorOutput = noise; + else if(operatorOutput < 0) operatorOutput = -noise; + else operatorOutput = 0; + } + + return operatorOutput*2; +} + +BassDrumChannel::BassDrumChannel(double startvol) +: Channel2op(bassDrumChannelBaseAddress, startvol, &my_op1, &my_op2), + my_op1(op1BaseAddress), my_op2(op2BaseAddress) +{ } + +double BassDrumChannel::getChannelOutput(OPL3 *OPL3) { + // Bass Drum ignores first operator, when it is in series. + if(cnt == 1) op1->ar=0; + return Channel2op::getChannelOutput(OPL3); +} + +void OPL3DataStruct::loadVibratoTable() { + + // According to the YMF262 datasheet, the OPL3 vibrato repetition rate is 6.1 Hz. + // According to the YMF278B manual, it is 6.0 Hz. + // The information that the vibrato table has 8 levels standing 1024 samples each + // was taken from the emulator by Jarek Burczynski and Tatsuyuki Satoh, + // with a frequency of 6,06689453125 Hz, what makes sense with the difference + // in the information on the datasheets. + + const double semitone = pow(2.0,1/12.0); + // A cent is 1/100 of a semitone: + const double cent = pow(semitone, 1/100.0); + + // When dvb=0, the depth is 7 cents, when it is 1, the depth is 14 cents. + const double DVB0 = pow(cent,7.0); + const double DVB1 = pow(cent,14.0); + int i; + for(i = 0; i<1024; i++) + vibratoTable[0][i] = vibratoTable[1][i] = 1; + for(;i<2048; i++) { + vibratoTable[0][i] = sqrt(DVB0); + vibratoTable[1][i] = sqrt(DVB1); + } + for(;i<3072; i++) { + vibratoTable[0][i] = DVB0; + vibratoTable[1][i] = DVB1; + } + for(;i<4096; i++) { + vibratoTable[0][i] = sqrt(DVB0); + vibratoTable[1][i] = sqrt(DVB1); + } + for(; i<5120; i++) + vibratoTable[0][i] = vibratoTable[1][i] = 1; + for(;i<6144; i++) { + vibratoTable[0][i] = 1/sqrt(DVB0); + vibratoTable[1][i] = 1/sqrt(DVB1); + } + for(;i<7168; i++) { + vibratoTable[0][i] = 1/DVB0; + vibratoTable[1][i] = 1/DVB1; + } + for(;i<8192; i++) { + vibratoTable[0][i] = 1/sqrt(DVB0); + vibratoTable[1][i] = 1/sqrt(DVB1); + } + +} + +void OPL3DataStruct::loadTremoloTable() +{ + // The tremolo depth is -1 dB when DAM = 0, and -4.8 dB when DAM = 1. + static const double tremoloDepth[] = {-1, -4.8}; + + // According to the YMF278B manual's OPL3 section graph, + // the tremolo waveform is not + // \ / a sine wave, but a single triangle waveform. + // \ / Thus, the period to achieve the tremolo depth is T/2, and + // \ / the increment in each T/2 section uses a frequency of 2*f. + // \/ Tremolo varies from 0 dB to depth, to 0 dB again, at frequency*2: + const double tremoloIncrement[] = { + calculateIncrement(tremoloDepth[0],0,1/(2*tremoloFrequency)), + calculateIncrement(tremoloDepth[1],0,1/(2*tremoloFrequency)) + }; + + int tremoloTableLength = (int)(OPL_SAMPLE_RATE/tremoloFrequency); + + // This is undocumented. The tremolo starts at the maximum attenuation, + // instead of at 0 dB: + tremoloTable[0][0] = tremoloDepth[0]; + tremoloTable[1][0] = tremoloDepth[1]; + int counter = 0; + // The first half of the triangle waveform: + while(tremoloTable[0][counter]<0) { + counter++; + tremoloTable[0][counter] = tremoloTable[0][counter-1] + tremoloIncrement[0]; + tremoloTable[1][counter] = tremoloTable[1][counter-1] + tremoloIncrement[1]; + } + // The second half of the triangle waveform: + while(tremoloTable[0][counter]>tremoloDepth[0] && counter> 8, reg & 0xFF, v); +} + +void OPL3::SetPanning(int c, float left, float right) +{ + if (FullPan) + { + Channel *channel; + + if (c < 9) + { + channel = channels[0][c]; + } + else + { + channel = channels[1][c - 9]; + } + channel->leftPan = left; + channel->rightPan = right; + } +} + +} // JavaOPL diff --git a/thirdparty/adlmidi/chips/java_opl3.cpp b/thirdparty/adlmidi/chips/java_opl3.cpp new file mode 100644 index 000000000..22ae417b9 --- /dev/null +++ b/thirdparty/adlmidi/chips/java_opl3.cpp @@ -0,0 +1,122 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "java_opl3.h" +#include "java/JavaOPL3.hpp" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.70710678118654752440 +#endif + +JavaOPL3::JavaOPL3() : + OPLChipBaseBufferedT(), + m_chip(new ADL_JavaOPL3::OPL3(true)) +{ + JavaOPL3::reset(); +} + +JavaOPL3::~JavaOPL3() +{ + ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void JavaOPL3::setRate(uint32_t rate) +{ + OPLChipBaseBufferedT::setRate(rate); + ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast(m_chip); + chip_r->Reset(); + + float pan = sinf((float)M_SQRT1_2); + for (unsigned channel = 0; channel < 18; ++channel) + chip_r->SetPanning(channel, pan, pan); +} + +void JavaOPL3::reset() +{ + OPLChipBaseBufferedT::reset(); + ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast(m_chip); + chip_r->Reset(); +} + +void JavaOPL3::writeReg(uint16_t addr, uint8_t data) +{ + ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast(m_chip); + chip_r->WriteReg(addr, data); +} + +void JavaOPL3::writePan(uint16_t addr, uint8_t data) +{ + ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast(m_chip); + + unsigned high = (addr >> 8) & 0x01; + unsigned regm = addr & 0xff; + unsigned channel = 9 * high + (regm & 0x0f); + + float phase = (data == 63 || data == 64) ? 63.5f : (float)data; + phase *= (float)(M_PI / 2 / 127); + chip_r->SetPanning(channel, cosf(phase), sinf(phase)); +} + +void JavaOPL3::nativeGenerateN(int16_t *output, size_t frames) +{ + ADL_JavaOPL3::OPL3 *chip_r = reinterpret_cast(m_chip); + + enum { maxframes = 256 }; + + float buf[2 * maxframes]; + while(frames > 0) + { + memset(buf, 0, sizeof(buf)); + + size_t curframes = (frames < (size_t)maxframes) ? frames : (size_t)maxframes; + chip_r->Update(buf, (int)curframes); + + size_t cursamples = 2 * curframes; + for(size_t i = 0; i < cursamples; ++i) + { + int32_t sample = (int32_t)lround(4096 * buf[i]); + sample = (sample > -32768) ? sample : -32768; + sample = (sample < +32767) ? sample : +32767; + output[i] = (int16_t)sample; + } + + output += cursamples; + frames -= curframes; + } +} + +const char *JavaOPL3::emulatorName() +{ + return "Java 1.0.6 OPL3"; +} + +bool JavaOPL3::hasFullPanning() +{ + return true; +} + +OPLChipBase::ChipType JavaOPL3::chipType() +{ + return CHIPTYPE_OPL3; +} diff --git a/thirdparty/adlmidi/chips/java_opl3.h b/thirdparty/adlmidi/chips/java_opl3.h new file mode 100644 index 000000000..e6b481b20 --- /dev/null +++ b/thirdparty/adlmidi/chips/java_opl3.h @@ -0,0 +1,46 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef JAVA_OPL3_H +#define JAVA_OPL3_H + +#include "opl_chip_base.h" + +class JavaOPL3 final : public OPLChipBaseBufferedT +{ + void *m_chip; +public: + JavaOPL3(); + ~JavaOPL3() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerateN(int16_t *output, size_t frames) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // JAVA_OPL3_H diff --git a/thirdparty/adlmidi/chips/mame/mame_fmopl.cpp b/thirdparty/adlmidi/chips/mame/mame_fmopl.cpp new file mode 100644 index 000000000..37312117d --- /dev/null +++ b/thirdparty/adlmidi/chips/mame/mame_fmopl.cpp @@ -0,0 +1,1852 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh +/* + +This file is based on fmopl.c from MAME. The non-YM3816 parts have been +ripped out in the interest of making this simpler, since Doom music doesn't +need them. I also made it render the sound a voice at a time instead of a +sample at a time, so unused voices don't waste time being calculated. If all +voices are playing, it's not much difference, but it does offer a big +improvement when only a few voices are playing. + + + +** +** File: fmopl.c - software implementation of FM sound generator +** types OPL and OPL2 +** +** Copyright Jarek Burczynski (bujar at mame dot net) +** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 0.72 +** + +Revision History: + +04-08-2003 Jarek Burczynski: + - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip + handles memory read/write or during the adpcm synthesis when the chip + requests another byte of ADPCM data. + +24-07-2003 Jarek Burczynski: + - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after + some (unknown) delay). Right now it's always set. + +14-06-2003 Jarek Burczynski: + - implemented all of the status register flags in Y8950 emulation + - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since + they can be either RAM or ROM + +08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip) + - corrected ym3526_read() to always set bit 2 and bit 1 + to HIGH state - identical to ym3812_read (verified on real YM3526) + +04-28-2002 Jarek Burczynski: + - binary exact Envelope Generator (verified on real YM3812); + compared to YM2151: the EG clock is equal to internal_clock, + rates are 2 times slower and volume resolution is one bit less + - modified interface functions (they no longer return pointer - + that's internal to the emulator now): + - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init() + - corrected 'off by one' error in feedback calculations (when feedback is off) + - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22) + - speeded up noise generator calculations (Nicola Salmoria) + +03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip) + Complete rewrite (all verified on real YM3812): + - corrected sin_tab and tl_tab data + - corrected operator output calculations + - corrected waveform_select_enable register; + simply: ignore all writes to waveform_select register when + waveform_select_enable == 0 and do not change the waveform previously selected. + - corrected KSR handling + - corrected Envelope Generator: attack shape, Sustain mode and + Percussive/Non-percussive modes handling + - Envelope Generator rates are two times slower now + - LFO amplitude (tremolo) and phase modulation (vibrato) + - rhythm sounds phase generation + - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm) + - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM) + - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1) + +12-28-2001 Acho A. Tang + - reflected Delta-T EOS status on Y8950 status port. + - fixed subscription range of attack/decay tables + + + To do: + add delay before key off in CSM mode (see CSMKeyControll) + verify volume of the FM part on the Y8950 +*/ + +#include +#include +#include +#include +#include +#include +//#include "driver.h" /* use M.A.M.E. */ +#include "opl.h" + +/* compiler dependence */ +#ifndef OSD_CPU_H +#define OSD_CPU_H +#endif + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4244) +#endif + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (EG timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<>KSR */ + uint8_t mul; /* multiple: mul_tab[ML] */ + + /* Phase Generator */ + uint32_t Cnt; /* frequency counter */ + uint32_t Incr; /* frequency counter step */ + uint8_t FB; /* feedback shift value */ + int32_t *connect1; /* slot1 output pointer */ + int32_t op1_out[2]; /* slot1 output for feedback */ + uint8_t CON; /* connection (algorithm) type */ + + /* Envelope Generator */ + uint8_t eg_type; /* percussive/non-percussive mode */ + uint8_t state; /* phase type */ + uint32_t TL; /* total level: TL << 2 */ + int32_t TLL; /* adjusted now TL */ + int32_t volume; /* envelope counter */ + uint32_t sl; /* sustain level: sl_tab[SL] */ + uint8_t eg_sh_ar; /* (attack state) */ + uint8_t eg_sel_ar; /* (attack state) */ + uint8_t eg_sh_dr; /* (decay state) */ + uint8_t eg_sel_dr; /* (decay state) */ + uint8_t eg_sh_rr; /* (release state) */ + uint8_t eg_sel_rr; /* (release state) */ + uint32_t key; /* 0 = KEY OFF, >0 = KEY ON */ + + /* LFO */ + uint32_t AMmask; /* LFO Amplitude Modulation enable mask */ + uint8_t vib; /* LFO Phase Modulation enable flag (active high)*/ + + /* waveform select */ + uint16_t wavetable; +}; + +struct OPL_CH +{ + OPL_SLOT SLOT[2]; + /* phase generator state */ + uint32_t block_fnum; /* block+fnum */ + uint32_t fc; /* Freq. Increment base */ + uint32_t ksl_base; /* KeyScaleLevel Base step */ + uint8_t kcode; /* key code (for key scaling) */ + float LeftVol; /* volumes for stereo panning */ + float RightVol; +}; + +/* OPL state */ +struct FM_OPL +{ + /* FM channel slots */ + OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/ + + uint32_t eg_cnt; /* global envelope generator counter */ + uint32_t eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ + uint32_t eg_timer_add; /* step of eg_timer */ + uint32_t eg_timer_overflow; /* envelope generator timer overflows every 1 sample (on real chip) */ + + uint8_t rhythm; /* Rhythm mode */ + + uint32_t fn_tab[1024]; /* fnumber->increment counter */ + + /* LFO */ + + uint8_t lfo_am_depth; + uint8_t lfo_pm_depth_range; + uint32_t lfo_am_cnt; + uint32_t lfo_am_inc; + uint32_t lfo_pm_cnt; + uint32_t lfo_pm_inc; + + uint32_t noise_rng; /* 23 bit noise shift register */ + uint32_t noise_p; /* current noise 'phase' */ + uint32_t noise_f; /* current noise period */ + + uint8_t wavesel; /* waveform select enable flag */ + + int T[2]; /* timer counters */ + uint8_t st[2]; /* timer enable */ + + + uint8_t address; /* address register */ + uint8_t status; /* status flag */ + uint8_t statusmask; /* status mask */ + uint8_t mode; /* Reg.08 : CSM,notesel,etc. */ + + bool IsStereo; /* Write stereo output */ +}; + + + +/* mapping of register number (offset) to slot number used by the emulator */ +static const int slot_array[32]= +{ + 0, 2, 4, 1, 3, 5,-1,-1, + 6, 8,10, 7, 9,11,-1,-1, + 12,14,16,13,15,17,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +/* key scale level */ +/* table is 3dB/octave , DV converts this into 6dB/octave */ +/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ +#define DV (0.1875/2.0) +static const uint32_t ksl_tab[8*16]= +{ + /* OCT 0 */ + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + /* OCT 1 */ + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(0.750/DV), uint32_t(1.125/DV), uint32_t(1.500/DV), + uint32_t(1.875/DV), uint32_t(2.250/DV), uint32_t(2.625/DV), uint32_t(3.000/DV), + /* OCT 2 */ + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(1.125/DV), uint32_t(1.875/DV), uint32_t(2.625/DV), + uint32_t(3.000/DV), uint32_t(3.750/DV), uint32_t(4.125/DV), uint32_t(4.500/DV), + uint32_t(4.875/DV), uint32_t(5.250/DV), uint32_t(5.625/DV), uint32_t(6.000/DV), + /* OCT 3 */ + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(1.875/DV), + uint32_t(3.000/DV), uint32_t(4.125/DV), uint32_t(4.875/DV), uint32_t(5.625/DV), + uint32_t(6.000/DV), uint32_t(6.750/DV), uint32_t(7.125/DV), uint32_t(7.500/DV), + uint32_t(7.875/DV), uint32_t(8.250/DV), uint32_t(8.625/DV), uint32_t(9.000/DV), + /* OCT 4 */ + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(3.000/DV), uint32_t(4.875/DV), + uint32_t(6.000/DV), uint32_t(7.125/DV), uint32_t(7.875/DV), uint32_t(8.625/DV), + uint32_t(9.000/DV), uint32_t(9.750/DV),uint32_t(10.125/DV),uint32_t(10.500/DV), + uint32_t(10.875/DV),uint32_t(11.250/DV),uint32_t(11.625/DV),uint32_t(12.000/DV), + /* OCT 5 */ + uint32_t(0.000/DV), uint32_t(3.000/DV), uint32_t(6.000/DV), uint32_t(7.875/DV), + uint32_t(9.000/DV),uint32_t(10.125/DV),uint32_t(10.875/DV),uint32_t(11.625/DV), + uint32_t(12.000/DV),uint32_t(12.750/DV),uint32_t(13.125/DV),uint32_t(13.500/DV), + uint32_t(13.875/DV),uint32_t(14.250/DV),uint32_t(14.625/DV),uint32_t(15.000/DV), + /* OCT 6 */ + uint32_t(0.000/DV), uint32_t(6.000/DV), uint32_t(9.000/DV),uint32_t(10.875/DV), + uint32_t(12.000/DV),uint32_t(13.125/DV),uint32_t(13.875/DV),uint32_t(14.625/DV), + uint32_t(15.000/DV),uint32_t(15.750/DV),uint32_t(16.125/DV),uint32_t(16.500/DV), + uint32_t(16.875/DV),uint32_t(17.250/DV),uint32_t(17.625/DV),uint32_t(18.000/DV), + /* OCT 7 */ + uint32_t(0.000/DV), uint32_t(9.000/DV),uint32_t(12.000/DV),uint32_t(13.875/DV), + uint32_t(15.000/DV),uint32_t(16.125/DV),uint32_t(16.875/DV),uint32_t(17.625/DV), + uint32_t(18.000/DV),uint32_t(18.750/DV),uint32_t(19.125/DV),uint32_t(19.500/DV), + uint32_t(19.875/DV),uint32_t(20.250/DV),uint32_t(20.625/DV),uint32_t(21.000/DV) +}; +#undef DV + +/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ +static const uint32_t ksl_shift[4] = { 31, 1, 2, 0 }; + + +/* sustain level table (3dB per step) */ +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +#define SC(db) (uint32_t) ( db * (2.0/ENV_STEP) ) +static const uint32_t sl_tab[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const unsigned char eg_inc[15*RATE_STEPS]={ +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ +/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ +/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(13) in this table - it's directly in the code */ +static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), + +/* rates 00-12 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 13 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 14 */ +O( 8),O( 9),O(10),O(11), + +/* rate 15 */ +O(12),O(12),O(12),O(12), + +/* 16 dummy rates (same as 15 3) */ +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ +/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ +/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + +/* rates 00-12 */ +O(12),O(12),O(12),O(12), +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 16 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), + +}; +#undef O + + +/* multiple table */ +#define ML 2 +static const uint8_t mul_tab[16]= { +/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ + uint8_t(0.50*ML), uint8_t(1.00*ML), uint8_t(2.00*ML), uint8_t(3.00*ML), uint8_t(4.00*ML), uint8_t(5.00*ML), uint8_t(6.00*ML), uint8_t(7.00*ML), + uint8_t(8.00*ML), uint8_t(9.00*ML),uint8_t(10.00*ML),uint8_t(10.00*ML),uint8_t(12.00*ML),uint8_t(12.00*ML),uint8_t(15.00*ML),uint8_t(15.00*ML) +}; +#undef ML + +/* TL_TAB_LEN is calculated as: +* 12 - sinus amplitude bits (Y axis) +* 2 - sinus sign bit (Y axis) +* TL_RES_LEN - sinus resolution (X axis) +*/ +#define TL_TAB_LEN (12*2*TL_RES_LEN) +static signed int tl_tab[TL_TAB_LEN]; + +#define ENV_QUIET (TL_TAB_LEN>>4) + +/* sin waveform table in 'decibel' scale */ +/* four waveforms on OPL2 type chips */ +static unsigned int sin_tab[SIN_LEN * 4]; + + +/* LFO Amplitude Modulation table (verified on real YM3812) + 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples + + Length: 210 elements. + + Each of the elements has to be repeated + exactly 64 times (on 64 consecutive samples). + The whole table takes: 64 * 210 = 13440 samples. + + When AM = 1 data is used directly + When AM = 0 data is divided by 4 before being used (losing precision is important) +*/ + +#define LFO_AM_TAB_ELEMENTS 210 + +static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS] = { +0,0,0,0,0,0,0, +1,1,1,1, +2,2,2,2, +3,3,3,3, +4,4,4,4, +5,5,5,5, +6,6,6,6, +7,7,7,7, +8,8,8,8, +9,9,9,9, +10,10,10,10, +11,11,11,11, +12,12,12,12, +13,13,13,13, +14,14,14,14, +15,15,15,15, +16,16,16,16, +17,17,17,17, +18,18,18,18, +19,19,19,19, +20,20,20,20, +21,21,21,21, +22,22,22,22, +23,23,23,23, +24,24,24,24, +25,25,25,25, +26,26,26, +25,25,25,25, +24,24,24,24, +23,23,23,23, +22,22,22,22, +21,21,21,21, +20,20,20,20, +19,19,19,19, +18,18,18,18, +17,17,17,17, +16,16,16,16, +15,15,15,15, +14,14,14,14, +13,13,13,13, +12,12,12,12, +11,11,11,11, +10,10,10,10, +9,9,9,9, +8,8,8,8, +7,7,7,7, +6,6,6,6, +5,5,5,5, +4,4,4,4, +3,3,3,3, +2,2,2,2, +1,1,1,1 +}; + +/* LFO Phase Modulation table (verified on real YM3812) */ +static const int8_t lfo_pm_table[8*8*2] = { +/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ +}; + + + +/* work table */ +struct FM_WorkTable +{ + int32_t phase_modulation; /* phase modulation input (SLOT 2) */ + int32_t output; + + uint32_t LFO_AM; + int32_t LFO_PM; +}; + +static bool CalcVoice (FM_WorkTable *wt, FM_OPL *OPL, int voice, float *buffer, int length); +static bool CalcVoice (FM_WorkTable *wt, FM_OPL *OPL, int voice, short *buffer, int length); +static bool CalcRhythm (FM_WorkTable *wt, FM_OPL *OPL, float *buffer, int length); +static bool CalcRhythm (FM_WorkTable *wt, FM_OPL *OPL, short *buffer, int length); + + + +/* status set and IRQ handling */ +static inline void OPL_STATUS_SET(FM_OPL *OPL,int flag) +{ + /* set status flag */ + OPL->status |= flag; + if(!(OPL->status & 0x80)) + { + if(OPL->status & OPL->statusmask) + { /* IRQ on */ + OPL->status |= 0x80; + } + } +} + +/* status reset and IRQ handling */ +static inline void OPL_STATUS_RESET(FM_OPL *OPL,int flag) +{ + /* reset status flag */ + OPL->status &=~flag; + if((OPL->status & 0x80)) + { + if (!(OPL->status & OPL->statusmask) ) + { + OPL->status &= 0x7f; + } + } +} + +/* IRQ mask set */ +static inline void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) +{ + OPL->statusmask = flag; + /* IRQ handling check */ + OPL_STATUS_SET(OPL,0); + OPL_STATUS_RESET(OPL,0); +} + + +/* advance LFO to next sample */ +static inline void advance_lfo(FM_WorkTable *wt, FM_OPL *OPL) +{ + uint8_t tmp; + + /* LFO */ + OPL->lfo_am_cnt += OPL->lfo_am_inc; + if (OPL->lfo_am_cnt >= (uint32_t)(LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= (LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ]; + + if (OPL->lfo_am_depth) + wt->LFO_AM = tmp; + else + wt->LFO_AM = tmp>>2; + + OPL->lfo_pm_cnt += OPL->lfo_pm_inc; + wt->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range; +} + +/* advance to next sample */ +static inline void advance(FM_WorkTable *wt, FM_OPL *OPL, int loch, int hich) +{ + OPL_CH *CH; + OPL_SLOT *op; + int i; + + OPL->eg_timer += OPL->eg_timer_add; + loch *= 2; + hich *= 2; + + while (OPL->eg_timer >= OPL->eg_timer_overflow) + { + OPL->eg_timer -= OPL->eg_timer_overflow; + + OPL->eg_cnt++; + + for (i = loch; i <= hich + 1; i++) + { + CH = &OPL->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + /* Envelope Generator */ + switch(op->state) + { + case EG_ATT: /* attack phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_ar)-1) ) ) + { + op->volume += (~op->volume * + (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)]) + ) >>3; + + if (op->volume <= MIN_ATT_INDEX) + { + op->volume = MIN_ATT_INDEX; + op->state = EG_DEC; + } + + } + break; + + case EG_DEC: /* decay phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_dr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)]; + + if ( op->volume >= (int32_t)op->sl ) + op->state = EG_SUS; + + } + break; + + case EG_SUS: /* sustain phase */ + + /* this is important behaviour: + one can change percusive/non-percussive modes on the fly and + the chip will remain in sustain phase - verified on real YM3812 */ + + if(op->eg_type) /* non-percussive mode */ + { + /* do nothing */ + } + else /* percussive mode */ + { + /* during sustain phase chip adds Release Rate (in percussive mode) */ + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + op->volume = MAX_ATT_INDEX; + } + /* else do nothing in sustain phase */ + } + break; + + case EG_REL: /* release phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + + } + break; + + default: + break; + } + + /* Phase Generator */ + if(op->vib) + { + uint8_t block; + unsigned int block_fnum = CH->block_fnum; + + unsigned int fnum_lfo = (block_fnum&0x0380) >> 7; + + signed int lfo_fn_table_index_offset = lfo_pm_table[wt->LFO_PM + 16*fnum_lfo ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + block_fnum += lfo_fn_table_index_offset; + block = (block_fnum&0x1c00) >> 10; + op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; + } + else /* LFO phase modulation = zero */ + { + op->Cnt += op->Incr; + } + } + else /* LFO phase modulation disabled for this operator */ + { + op->Cnt += op->Incr; + } + } + } +} + +static inline void advance_noise(FM_OPL *OPL) +{ + int i; + + /* The Noise Generator of the YM3812 is 23-bit shift register. + * Period is equal to 2^23-2 samples. + * Register works at sampling frequency of the chip, so output + * can change on every sample. + * + * Output of the register and input to the bit 22 is: + * bit0 XOR bit14 XOR bit15 XOR bit22 + * + * Simply use bit 22 as the noise output. + */ + + OPL->noise_p += OPL->noise_f; + i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ + OPL->noise_p &= FREQ_MASK; + while (i) + { + /* + uint32_t j; + j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1; + OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1); + */ + + /* + Instead of doing all the logic operations above, we + use a trick here (and use bit 0 as the noise output). + The difference is only that the noise bit changes one + step ahead. This doesn't matter since we don't know + what is real state of the noise_rng after the reset. + */ + + if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302; + OPL->noise_rng >>= 1; + + i--; + } +} + + +static inline signed int op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + uint32_t p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +static inline signed int op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + uint32_t p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + + +#define volume_calc(OP) ((OP)->TLL + ((uint32_t)(OP)->volume) + (wt->LFO_AM & (OP)->AMmask)) + +/* calculate output */ +static inline float OPL_CALC_CH( FM_WorkTable *wt, OPL_CH *CH ) +{ + OPL_SLOT *SLOT; + unsigned int env; + signed int out; + + wt->phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + *SLOT->connect1 += SLOT->op1_out[0]; + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + { + wt->output += op_calc(SLOT->Cnt, env, wt->phase_modulation, SLOT->wavetable); + /* [RH] Convert to floating point. */ + return float(wt->output) / 10240; + } + return 0; +} + +static inline short OPL_CALC_CH_S( FM_WorkTable *wt, OPL_CH *CH ) +{ + OPL_SLOT *SLOT; + unsigned int env; + signed int out; + + wt->phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + *SLOT->connect1 += SLOT->op1_out[0]; + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + { + wt->output += op_calc(SLOT->Cnt, env, wt->phase_modulation, SLOT->wavetable); + return wt->output; + } + return 0; +} + +/* + operators used in the rhythm sounds generation process: + + Envelope Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal + 6 / 0 12 50 70 90 f0 + + 6 / 1 15 53 73 93 f3 + + 7 / 0 13 51 71 91 f1 + + 7 / 1 16 54 74 94 f4 + + 8 / 0 14 52 72 92 f2 + + 8 / 1 17 55 75 95 f5 + + + Phase Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number MULTIPLE Drum Hat Drum Tom Cymbal + 6 / 0 12 30 + + 6 / 1 15 33 + + 7 / 0 13 31 + + + + 7 / 1 16 34 ----- n o t u s e d ----- + 8 / 0 14 32 + + 8 / 1 17 35 + + + +channel operator register number Bass High Snare Tom Top +number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal + 6 12,15 B6 A6 + + + 7 13,16 B7 A7 + + + + + 8 14,17 B8 A8 + + + + +*/ + +/* calculate rhythm */ + +static inline void OPL_CALC_RH( FM_WorkTable *wt, OPL_CH *CH, unsigned int noise ) +{ + OPL_SLOT *SLOT; + signed int out; + unsigned int env; + + + /* Bass Drum (verified on real YM3812): + - depends on the channel 6 'connect' register: + when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) + when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored + - output sample always is multiplied by 2 + */ + + wt->phase_modulation = 0; + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env = volume_calc(SLOT); + + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + + if (!SLOT->CON) + wt->phase_modulation = SLOT->op1_out[0]; + /* else ignore output of operator 1 */ + + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + wt->output += op_calc(SLOT->Cnt, env, wt->phase_modulation, SLOT->wavetable) * 2; + + + /* Phase generation is based on: */ + /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */ + /* SD (16) channel 7->slot 1 */ + /* TOM (14) channel 8->slot 1 */ + /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */ + + /* Envelope generation based on: */ + /* HH channel 7->slot1 */ + /* SD channel 7->slot2 */ + /* TOM channel 8->slot1 */ + /* TOP channel 8->slot2 */ + + + /* The following formulas can be well optimized. + I leave them in direct form for now (in case I've missed something). + */ + + /* High Hat (verified on real YM3812) */ + env = volume_calc(&CH[7].SLOT[SLOT1]); + if( env < ENV_QUIET ) + { + + /* high hat phase generation: + phase = d0 or 234 (based on frequency only) + phase = 34 or 2d0 (based on noise) + */ + + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0xd0; */ + /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ + uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((CH[8].SLOT[SLOT2].Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((CH[8].SLOT[SLOT2].Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ + if (res2) + phase = (0x200|(0xd0>>2)); + + + /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ + /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ + if (phase&0x200) + { + if (noise) + phase = 0x200|0xd0; + } + else + /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ + /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ + { + if (noise) + phase = 0xd0>>2; + } + + wt->output += op_calc(phase<>FREQ_SH)>>8)&1; + + /* when bit8 = 0 phase = 0x100; */ + /* when bit8 = 1 phase = 0x200; */ + uint32_t phase = bit8 ? 0x200 : 0x100; + + /* Noise bit XOR'es phase by 0x100 */ + /* when noisebit = 0 pass the phase from calculation above */ + /* when noisebit = 1 phase ^= 0x100; */ + /* in other words: phase ^= (noisebit<<8); */ + if (noise) + phase ^= 0x100; + + wt->output += op_calc(phase<output += op_calc(CH[8].SLOT[SLOT1].Cnt, env, 0, CH[8].SLOT[SLOT2].wavetable) * 2; + + /* Top Cymbal (verified on real YM3812) */ + env = volume_calc(&CH[8].SLOT[SLOT2]); + if( env < ENV_QUIET ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0x100; */ + /* when res1 = 1 phase = 0x200 | 0x100; */ + uint32_t phase = res1 ? 0x300 : 0x100; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((CH[8].SLOT[SLOT2].Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((CH[8].SLOT[SLOT2].Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | 0x100; */ + if (res2) + phase = 0x300; + + wt->output += op_calc(phase<>= 4; /* 12 bits here */ + n = (n+1)>>1; /* round to nearest */ + /* 11 bits here (rounded) */ + n <<= 1; /* 12 bits here (as in real chip) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + for (i=1; i<12; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 ]>>i; + } + } + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + } + + for (i=0; i>1) ]; + + /* waveform 3: _ _ _ _ */ + /* / |_/ |_/ |_/ |_*/ + /* abs(output only first quarter of the sinus waveform) */ + + if (i & (1<<(SIN_BITS-2)) ) + sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; + } + + did_init = true; +} + +static void OPL_initalize(FM_OPL *OPL) +{ + int i; + + /* make fnumber -> increment counter table */ + for( i=0 ; i < 1024 ; i++ ) + { + /* opn phase increment counter = 20bit */ + OPL->fn_tab[i] = (uint32_t)( (double)i * 64 * OPL_FREQBASE * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ + } + + /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ + /* One entry from LFO_AM_TABLE lasts for 64 samples */ + OPL->lfo_am_inc = uint32_t((1.0 / 64.0 ) * (1<lfo_pm_inc = uint32_t((1.0 / 1024.0) * (1<eg_timer_add = uint32_t((1<eg_timer_overflow = uint32_t(( 1 ) * (1<IsStereo = false; + for (int i = 0; i < 9; ++i) + { + OPL->P_CH[i].LeftVol = (float)CENTER_PANNING_POWER; + OPL->P_CH[i].RightVol = (float)CENTER_PANNING_POWER; + } +} + +static inline void FM_KEYON(OPL_SLOT *SLOT, uint32_t key_set) +{ + if( !SLOT->key ) + { + /* restart Phase Generator */ + SLOT->Cnt = 0; + /* phase -> Attack */ + SLOT->state = EG_ATT; + } + SLOT->key |= key_set; +} + +static inline void FM_KEYOFF(OPL_SLOT *SLOT, uint32_t key_clr) +{ + if( SLOT->key ) + { + SLOT->key &= key_clr; + + if( !SLOT->key ) + { + /* phase -> Release */ + if (SLOT->state>EG_REL) + SLOT->state = EG_REL; + } + } +} + +/* update phase increment counter of operator (also update the EG rates if necessary) */ +static inline void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) +{ + int ksr; + + /* (frequency) phase increment counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; + } +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +static inline void set_mul(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = mul_tab[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_type = (v&0x20); + SLOT->vib = (v&0x40); + SLOT->AMmask = (v&0x80) ? ~0 : 0; + CALC_FCSLOT(CH,SLOT); +} + +/* set ksl & tl */ +static inline void set_ksl_tl(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ksl = ksl_shift[v >> 6]; + SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ + + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); +} + +/* set attack rate & decay rate */ +static inline void set_ar_dr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; + + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + + SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; +} + +/* set sustain level & release rate */ +static inline void set_sl_rr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->sl = sl_tab[ v>>4 ]; + + SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; +} + + +/* write a value v to register r on OPL chip */ +static void WriteRegister(FM_WorkTable *wt, FM_OPL *OPL, int r, int v) +{ + OPL_CH *CH; + int slot; + int block_fnum; + + /* adjust bus to 8 bits */ + r &= 0xff; + v &= 0xff; + + switch(r&0xe0) + { + case 0x00: /* 00-1f:control */ + switch(r&0x1f) + { + case 0x01: /* waveform select enable */ + OPL->wavesel = v&0x20; + break; + case 0x02: /* Timer 1 */ + OPL->T[0] = (256-v)*4; + break; + case 0x03: /* Timer 2 */ + OPL->T[1] = (256-v)*16; + break; + case 0x04: /* IRQ clear / mask and Timer enable */ + if(v&0x80) + { /* IRQ flag clear */ + OPL_STATUS_RESET(OPL,0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */ + } + else + { /* set IRQ mask ,timer enable*/ + uint8_t st1 = v&1; + uint8_t st2 = (v>>1)&1; + + /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ + OPL_STATUS_RESET(OPL, v & (0x78-0x08) ); + OPL_STATUSMASK_SET(OPL, (~v) & 0x78 ); + + /* timer 2 */ + if(OPL->st[1] != st2) + { + OPL->st[1] = st2; + } + /* timer 1 */ + if(OPL->st[0] != st1) + { + OPL->st[0] = st1; + } + } + break; + case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ + OPL->mode = v; + break; + } + break; + case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_mul(OPL,slot,v); + break; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ksl_tl(OPL,slot,v); + break; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ar_dr(OPL,slot,v); + break; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_sl_rr(OPL,slot,v); + break; + case 0xa0: + if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ + { + OPL->lfo_am_depth = v & 0x80; + OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0; + + OPL->rhythm = v&0x3f; + + if(OPL->rhythm&0x20) + { + /* BD key on/off */ + if(v&0x10) + { + FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2); + FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2); + } + else + { + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); + } + /* HH key on/off */ + if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2); + else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); + /* SD key on/off */ + if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2); + else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); + /* TOM key on/off */ + if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2); + else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY key on/off */ + if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2); + else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); + } + else + { + /* BD key off */ + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); + /* HH key off */ + FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); + /* SD key off */ + FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); + /* TOM key off */ + FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY off */ + FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); + } + return; + } + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + /* update */ + if(CH->block_fnum != (uint32_t)block_fnum) + { + uint8_t block = block_fnum >> 10; + + CH->block_fnum = block_fnum; + + CH->ksl_base = ksl_tab[block_fnum>>6]; + CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block); + + /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ + CH->kcode = (CH->block_fnum&0x1c00)>>9; + + /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */ + /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ + /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ + if (OPL->mode&0x40) + CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ + else + CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ + + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + break; + case 0xc0: + /* FB,C */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; + CH->SLOT[SLOT1].CON = v&1; + CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &wt->output : &wt->phase_modulation; + break; + case 0xe0: /* waveform select */ + /* simply ignore write to the waveform select register if selecting not enabled in test register */ + if(OPL->wavesel) + { + slot = slot_array[r&0x1f]; + if(slot < 0) return; + CH = &OPL->P_CH[slot/2]; + + CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN; + } + break; + } +} + +static void OPLResetChip(FM_WorkTable *wt, FM_OPL *OPL) +{ + int c,s; + int i; + + OPL->eg_timer = 0; + OPL->eg_cnt = 0; + + OPL->noise_rng = 1; /* noise shift register */ + OPL->mode = 0; /* normal mode */ + OPL_STATUS_RESET(OPL,0x7f); + + /* reset with register write */ + WriteRegister(wt,OPL,0x01,0); /* wavesel disable */ + WriteRegister(wt,OPL,0x02,0); /* Timer1 */ + WriteRegister(wt,OPL,0x03,0); /* Timer2 */ + WriteRegister(wt,OPL,0x04,0); /* IRQ mask clear */ + for(i = 0xff ; i >= 0x20 ; i-- ) WriteRegister(wt,OPL,i,0); + + /* reset operator parameters */ + for( c = 0 ; c < 9 ; c++ ) + { + OPL_CH *CH = &OPL->P_CH[c]; + for(s = 0 ; s < 2 ; s++ ) + { + /* wave table */ + CH->SLOT[s].wavetable = 0; + CH->SLOT[s].state = EG_OFF; + CH->SLOT[s].volume = MAX_ATT_INDEX; + } + } +} + + +class YM3812 : public OPLEmul +{ +private: + FM_OPL Chip; + FM_WorkTable WorkTable; + +public: + /* Create one of virtual YM3812 */ + YM3812(bool stereo) + { + memset(&WorkTable, 0, sizeof(WorkTable)); + init_tables(); + + /* clear */ + memset(&Chip, 0, sizeof(Chip)); + + /* init global tables */ + OPL_initalize(&Chip); + + Chip.IsStereo = stereo; + + Reset(); + } + + /* YM3812 I/O interface */ + void WriteReg(int reg, int v) + { + WriteRegister(&WorkTable, &Chip, reg & 0xff, v); + } + + void Reset() + { + OPLResetChip(&WorkTable, &Chip); + } + + /* [RH] Full support for MIDI panning */ + void SetPanning(int c, float left, float right) + { + Chip.P_CH[c].LeftVol = left; + Chip.P_CH[c].RightVol = right; + } + + + /* + ** Generate samples for one of the YM3812's + ** + ** '*buffer' is the output buffer pointer + ** 'length' is the number of samples that should be generated + */ + void Update(float *buffer, int length) + { + int i; + + uint8_t rhythm = Chip.rhythm&0x20; + + uint32_t lfo_am_cnt_bak = Chip.lfo_am_cnt; + uint32_t eg_timer_bak = Chip.eg_timer; + uint32_t eg_cnt_bak = Chip.eg_cnt; + + uint32_t lfo_am_cnt_out = lfo_am_cnt_bak; + uint32_t eg_timer_out = eg_timer_bak; + uint32_t eg_cnt_out = eg_cnt_bak; + + for (i = 0; i <= (rhythm ? 5 : 8); ++i) + { + Chip.lfo_am_cnt = lfo_am_cnt_bak; + Chip.eg_timer = eg_timer_bak; + Chip.eg_cnt = eg_cnt_bak; + if (CalcVoice (&WorkTable, &Chip, i, buffer, length)) + { + lfo_am_cnt_out = Chip.lfo_am_cnt; + eg_timer_out = Chip.eg_timer; + eg_cnt_out = Chip.eg_cnt; + } + } + + Chip.lfo_am_cnt = lfo_am_cnt_out; + Chip.eg_timer = eg_timer_out; + Chip.eg_cnt = eg_cnt_out; + + if (rhythm) /* Rhythm part */ + { + Chip.lfo_am_cnt = lfo_am_cnt_bak; + Chip.eg_timer = eg_timer_bak; + Chip.eg_cnt = eg_cnt_bak; + CalcRhythm (&WorkTable, &Chip, buffer, length); + } + } + + void UpdateS(short *buffer, int length) + { + int i; + + uint8_t rhythm = Chip.rhythm&0x20; + + uint32_t lfo_am_cnt_bak = Chip.lfo_am_cnt; + uint32_t eg_timer_bak = Chip.eg_timer; + uint32_t eg_cnt_bak = Chip.eg_cnt; + + uint32_t lfo_am_cnt_out = lfo_am_cnt_bak; + uint32_t eg_timer_out = eg_timer_bak; + uint32_t eg_cnt_out = eg_cnt_bak; + + for (i = 0; i <= (rhythm ? 5 : 8); ++i) + { + Chip.lfo_am_cnt = lfo_am_cnt_bak; + Chip.eg_timer = eg_timer_bak; + Chip.eg_cnt = eg_cnt_bak; + if (CalcVoice (&WorkTable, &Chip, i, buffer, length)) + { + lfo_am_cnt_out = Chip.lfo_am_cnt; + eg_timer_out = Chip.eg_timer; + eg_cnt_out = Chip.eg_cnt; + } + } + + Chip.lfo_am_cnt = lfo_am_cnt_out; + Chip.eg_timer = eg_timer_out; + Chip.eg_cnt = eg_cnt_out; + + if (rhythm) /* Rhythm part */ + { + Chip.lfo_am_cnt = lfo_am_cnt_bak; + Chip.eg_timer = eg_timer_bak; + Chip.eg_cnt = eg_cnt_bak; + CalcRhythm (&WorkTable, &Chip, buffer, length); + } + } + + std::string GetVoiceString(void *chip) + { + FM_OPL *OPL = (FM_OPL *)chip; + char out[9*3]; + + for (int i = 0; i <= 8; ++i) + { + int color; + + if (OPL != NULL && (OPL->P_CH[i].SLOT[0].state != EG_OFF || OPL->P_CH[i].SLOT[1].state != EG_OFF)) + { + color = 'D'; // Green means in use + } + else + { + color = 'A'; // Brick means free + } + out[i*3+0] = '\x1c'; + out[i*3+1] = color; + out[i*3+2] = '*'; + } + return std::string (out, 9*3); + } +}; + +OPLEmul *YM3812Create(bool stereo) +{ + /* emulator create */ + return new YM3812(stereo); +} + +// [RH] Render a whole voice at once. If nothing else, it lets us avoid +// wasting a lot of time on voices that aren't playing anything. + +static bool CalcVoice (FM_WorkTable *wt, FM_OPL *OPL, int voice, float *buffer, int length) +{ + OPL_CH *const CH = &OPL->P_CH[voice]; + int i; + + if (CH->SLOT[0].state == EG_OFF && CH->SLOT[1].state == EG_OFF) + { // Voice is not playing, so don't do anything for it + return false; + } + + for (i = 0; i < length; ++i) + { + advance_lfo(wt, OPL); + + wt->output = 0; + float sample = OPL_CALC_CH(wt, CH); + if (!OPL->IsStereo) + { + buffer[i] += sample; + } + else + { + buffer[i*2] += sample * CH->LeftVol; + buffer[i*2+1] += sample * CH->RightVol; + } + + advance(wt, OPL, voice, voice); + } + return true; +} + +static bool CalcVoice (FM_WorkTable *wt, FM_OPL *OPL, int voice, short *buffer, int length) +{ + OPL_CH *const CH = &OPL->P_CH[voice]; + int i; + + if (CH->SLOT[0].state == EG_OFF && CH->SLOT[1].state == EG_OFF) + { // Voice is not playing, so don't do anything for it + return false; + } + + for (i = 0; i < length; ++i) + { + advance_lfo(wt, OPL); + + wt->output = 0; + short sample = OPL_CALC_CH_S(wt, CH); + + if (!OPL->IsStereo) + { + buffer[i] += sample; + } + else + { + buffer[i*2] += sample * CH->LeftVol; + buffer[i*2+1] += sample * CH->RightVol; + } + + advance(wt, OPL, voice, voice); + } + return true; +} + +static bool CalcRhythm (FM_WorkTable *wt, FM_OPL *OPL, float *buffer, int length) +{ + int i; + + for (i = 0; i < length; ++i) + { + advance_lfo(wt, OPL); + + wt->output = 0; + OPL_CALC_RH(wt, &OPL->P_CH[0], OPL->noise_rng & 1); + /* [RH] Convert to floating point. */ + float sample = float(wt->output) / 10240; + if (!OPL->IsStereo) + { + buffer[i] += sample; + } + else + { + // [RH] Always use center panning for rhythm. + // The MIDI player doesn't use the rhythm section anyway. + buffer[i*2] += sample * CENTER_PANNING_POWER; + buffer[i*2+1] += sample * CENTER_PANNING_POWER; + } + + advance(wt, OPL, 6, 8); + advance_noise(OPL); + } + return true; +} + +static bool CalcRhythm (FM_WorkTable *wt, FM_OPL *OPL, short *buffer, int length) +{ + int i; + + for (i = 0; i < length; ++i) + { + advance_lfo(wt, OPL); + + wt->output = 0; + OPL_CALC_RH(wt, &OPL->P_CH[0], OPL->noise_rng & 1); + + if (!OPL->IsStereo) + { + buffer[i] += wt->output; + } + else + { + // [RH] Always use center panning for rhythm. + // The MIDI player doesn't use the rhythm section anyway. + buffer[i*2] += wt->output * CENTER_PANNING_POWER; + buffer[i*2+1] += wt->output * CENTER_PANNING_POWER; + } + + advance(wt, OPL, 6, 8); + advance_noise(OPL); + } + return true; +} diff --git a/thirdparty/adlmidi/chips/mame/opl.h b/thirdparty/adlmidi/chips/mame/opl.h new file mode 100644 index 000000000..a1d7184c4 --- /dev/null +++ b/thirdparty/adlmidi/chips/mame/opl.h @@ -0,0 +1,27 @@ +#ifndef OPL_H +#define OPL_H + +// Abstract base class for OPL emulators + +class OPLEmul +{ +public: + OPLEmul() {} + virtual ~OPLEmul() {} + + virtual void Reset() = 0; + virtual void WriteReg(int reg, int v) = 0; + virtual void Update(float *buffer, int length) = 0; + virtual void UpdateS(short *buffer, int length) = 0; + virtual void SetPanning(int c, float left, float right) = 0; +}; + +OPLEmul *YM3812Create(bool stereo); + +#define OPL_SAMPLE_RATE 49716.0 +#define CENTER_PANNING_POWER 0.70710678118 /* [RH] volume at center for EQP */ +#define ADLIB_CLOCK_MUL 24.0 + + + +#endif diff --git a/thirdparty/adlmidi/chips/mame_opl2.cpp b/thirdparty/adlmidi/chips/mame_opl2.cpp new file mode 100644 index 000000000..30cfcd7ac --- /dev/null +++ b/thirdparty/adlmidi/chips/mame_opl2.cpp @@ -0,0 +1,79 @@ +/* + * Interfaces over Yamaha OPL3 (ym3812) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mame_opl2.h" +#include "mame/opl.h" +#include + +MameOPL2::MameOPL2() : + OPLChipBaseT() +{ + m_chip = YM3812Create(false); + MameOPL2::setRate(m_rate); +} + +MameOPL2::~MameOPL2() +{ + OPLEmul *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void MameOPL2::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + OPLEmul *chip_r = reinterpret_cast(m_chip); + chip_r->Reset(); +} + +void MameOPL2::reset() +{ + OPLChipBaseT::reset(); + OPLEmul *chip_r = reinterpret_cast(m_chip); + chip_r->Reset(); +} + +void MameOPL2::writeReg(uint16_t addr, uint8_t data) +{ + OPLEmul *chip_r = reinterpret_cast(m_chip); + chip_r->WriteReg(addr, data); +} + +void MameOPL2::nativeGenerate(int16_t *frame) +{ + OPLEmul *chip_r = reinterpret_cast(m_chip); + frame[0] = 0; + chip_r->UpdateS(frame, 1); + frame[1] = frame[0]; +} + +const char *MameOPL2::emulatorName() +{ + return "MAME OPL2"; +} + +bool MameOPL2::hasFullPanning() +{ + return false; +} + +OPLChipBase::ChipType MameOPL2::chipType() +{ + return CHIPTYPE_OPL2; +} diff --git a/thirdparty/adlmidi/chips/mame_opl2.h b/thirdparty/adlmidi/chips/mame_opl2.h new file mode 100644 index 000000000..8a5ea9ca1 --- /dev/null +++ b/thirdparty/adlmidi/chips/mame_opl2.h @@ -0,0 +1,46 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAME_OPL2_H +#define MAME_OPL2_H + +#include "opl_chip_base.h" + +class MameOPL2 final : public OPLChipBaseT +{ + void *m_chip; + +public: + MameOPL2(); + ~MameOPL2() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // MAME_OPL2_H diff --git a/thirdparty/adlmidi/chips/nuked/LICENSE b/thirdparty/adlmidi/chips/nuked/LICENSE new file mode 100644 index 000000000..8000a6faa --- /dev/null +++ b/thirdparty/adlmidi/chips/nuked/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/thirdparty/adlmidi/chips/nuked/nukedopl3.c b/thirdparty/adlmidi/chips/nuked/nukedopl3.c new file mode 100644 index 000000000..55cf8bbc5 --- /dev/null +++ b/thirdparty/adlmidi/chips/nuked/nukedopl3.c @@ -0,0 +1,1781 @@ +/* Nuked OPL3 + * Copyright (C) 2013-2020 Nuke.YKT + * + * This file is part of Nuked OPL3. + * + * Nuked OPL3 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. + * + * Nuked OPL3 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 Nuked OPL3. If not, see . + + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * + * version: 1.8 + */ + +#include +#include +#include +#include "nukedopl3.h" + +#if OPL_ENABLE_STEREOEXT && !defined OPL_SIN +#ifndef _USE_MATH_DEFINES +#define _USE_MATH_DEFINES 1 +#endif +#include +/* input: [0, 256), output: [0, 65536] */ +#define OPL_SIN(x) ((int32_t)(sin((x) * M_PI / 512.0) * 65536.0)) +#endif + +/* Quirk: Some FM channels are output one sample later on the left side than the right. */ +#ifndef OPL_QUIRK_CHANNELSAMPLEDELAY +#define OPL_QUIRK_CHANNELSAMPLEDELAY (!OPL_ENABLE_STEREOEXT) +#endif + +#define RSM_FRAC 10 + +/* Channel types */ + +enum { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2, + ch_drum = 3 +}; + +/* Envelope key types */ + +enum { + egk_norm = 0x01, + egk_drum = 0x02 +}; + + +#if OPL_FAST_WAVEGEN +/* + logsin table +*/ + +static const uint16_t logsinrom[512] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x002, + 0x002, 0x002, 0x002, 0x003, 0x003, 0x003, 0x004, 0x004, + 0x004, 0x005, 0x005, 0x005, 0x006, 0x006, 0x007, 0x007, + 0x007, 0x008, 0x008, 0x009, 0x009, 0x00a, 0x00a, 0x00b, + 0x00c, 0x00c, 0x00d, 0x00d, 0x00e, 0x00f, 0x00f, 0x010, + 0x011, 0x011, 0x012, 0x013, 0x014, 0x014, 0x015, 0x016, + 0x017, 0x017, 0x018, 0x019, 0x01a, 0x01b, 0x01c, 0x01d, + 0x01e, 0x01f, 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, + 0x026, 0x027, 0x028, 0x029, 0x02a, 0x02b, 0x02d, 0x02e, + 0x02f, 0x030, 0x031, 0x033, 0x034, 0x035, 0x037, 0x038, + 0x039, 0x03b, 0x03c, 0x03e, 0x03f, 0x040, 0x042, 0x043, + 0x045, 0x046, 0x048, 0x04a, 0x04b, 0x04d, 0x04e, 0x050, + 0x052, 0x053, 0x055, 0x057, 0x059, 0x05b, 0x05c, 0x05e, + 0x060, 0x062, 0x064, 0x066, 0x068, 0x06a, 0x06c, 0x06e, + 0x070, 0x072, 0x074, 0x076, 0x078, 0x07a, 0x07d, 0x07f, + 0x081, 0x083, 0x086, 0x088, 0x08a, 0x08d, 0x08f, 0x092, + 0x094, 0x097, 0x099, 0x09c, 0x09f, 0x0a1, 0x0a4, 0x0a7, + 0x0a9, 0x0ac, 0x0af, 0x0b2, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c1, 0x0c4, 0x0c7, 0x0ca, 0x0cd, 0x0d1, 0x0d4, 0x0d7, + 0x0db, 0x0de, 0x0e2, 0x0e5, 0x0e9, 0x0ec, 0x0f0, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x103, 0x107, 0x10b, 0x10f, 0x114, + 0x118, 0x11c, 0x121, 0x125, 0x129, 0x12e, 0x133, 0x137, + 0x13c, 0x141, 0x146, 0x14b, 0x150, 0x155, 0x15b, 0x160, + 0x166, 0x16b, 0x171, 0x177, 0x17c, 0x182, 0x188, 0x18f, + 0x195, 0x19b, 0x1a2, 0x1a9, 0x1b0, 0x1b7, 0x1be, 0x1c5, + 0x1cd, 0x1d4, 0x1dc, 0x1e4, 0x1ec, 0x1f5, 0x1fd, 0x206, + 0x20f, 0x218, 0x222, 0x22c, 0x236, 0x240, 0x24b, 0x256, + 0x261, 0x26d, 0x279, 0x286, 0x293, 0x2a0, 0x2af, 0x2bd, + 0x2cd, 0x2dc, 0x2ed, 0x2ff, 0x311, 0x324, 0x339, 0x34e, + 0x365, 0x37e, 0x398, 0x3b5, 0x3d3, 0x3f5, 0x41a, 0x443, + 0x471, 0x4a6, 0x4e4, 0x52e, 0x58b, 0x607, 0x6c3, 0x859 +}; + +/* + exp table +*/ + +static const uint16_t exprom[256] = { + 0xff4, 0xfea, 0xfde, 0xfd4, 0xfc8, 0xfbe, 0xfb4, 0xfa8, + 0xf9e, 0xf92, 0xf88, 0xf7e, 0xf72, 0xf68, 0xf5c, 0xf52, + 0xf48, 0xf3e, 0xf32, 0xf28, 0xf1e, 0xf14, 0xf08, 0xefe, + 0xef4, 0xeea, 0xee0, 0xed4, 0xeca, 0xec0, 0xeb6, 0xeac, + 0xea2, 0xe98, 0xe8e, 0xe84, 0xe7a, 0xe70, 0xe66, 0xe5c, + 0xe52, 0xe48, 0xe3e, 0xe34, 0xe2a, 0xe20, 0xe16, 0xe0c, + 0xe04, 0xdfa, 0xdf0, 0xde6, 0xddc, 0xdd2, 0xdca, 0xdc0, + 0xdb6, 0xdac, 0xda4, 0xd9a, 0xd90, 0xd88, 0xd7e, 0xd74, + 0xd6a, 0xd62, 0xd58, 0xd50, 0xd46, 0xd3c, 0xd34, 0xd2a, + 0xd22, 0xd18, 0xd10, 0xd06, 0xcfe, 0xcf4, 0xcec, 0xce2, + 0xcda, 0xcd0, 0xcc8, 0xcbe, 0xcb6, 0xcae, 0xca4, 0xc9c, + 0xc92, 0xc8a, 0xc82, 0xc78, 0xc70, 0xc68, 0xc60, 0xc56, + 0xc4e, 0xc46, 0xc3c, 0xc34, 0xc2c, 0xc24, 0xc1c, 0xc12, + 0xc0a, 0xc02, 0xbfa, 0xbf2, 0xbea, 0xbe0, 0xbd8, 0xbd0, + 0xbc8, 0xbc0, 0xbb8, 0xbb0, 0xba8, 0xba0, 0xb98, 0xb90, + 0xb88, 0xb80, 0xb78, 0xb70, 0xb68, 0xb60, 0xb58, 0xb50, + 0xb48, 0xb40, 0xb38, 0xb32, 0xb2a, 0xb22, 0xb1a, 0xb12, + 0xb0a, 0xb02, 0xafc, 0xaf4, 0xaec, 0xae4, 0xade, 0xad6, + 0xace, 0xac6, 0xac0, 0xab8, 0xab0, 0xaa8, 0xaa2, 0xa9a, + 0xa92, 0xa8c, 0xa84, 0xa7c, 0xa76, 0xa6e, 0xa68, 0xa60, + 0xa58, 0xa52, 0xa4a, 0xa44, 0xa3c, 0xa36, 0xa2e, 0xa28, + 0xa20, 0xa18, 0xa12, 0xa0c, 0xa04, 0x9fe, 0x9f6, 0x9f0, + 0x9e8, 0x9e2, 0x9da, 0x9d4, 0x9ce, 0x9c6, 0x9c0, 0x9b8, + 0x9b2, 0x9ac, 0x9a4, 0x99e, 0x998, 0x990, 0x98a, 0x984, + 0x97c, 0x976, 0x970, 0x96a, 0x962, 0x95c, 0x956, 0x950, + 0x948, 0x942, 0x93c, 0x936, 0x930, 0x928, 0x922, 0x91c, + 0x916, 0x910, 0x90a, 0x904, 0x8fc, 0x8f6, 0x8f0, 0x8ea, + 0x8e4, 0x8de, 0x8d8, 0x8d2, 0x8cc, 0x8c6, 0x8c0, 0x8ba, + 0x8b4, 0x8ae, 0x8a8, 0x8a2, 0x89c, 0x896, 0x890, 0x88a, + 0x884, 0x87e, 0x878, 0x872, 0x86c, 0x866, 0x860, 0x85a, + 0x854, 0x850, 0x84a, 0x844, 0x83e, 0x838, 0x832, 0x82c, + 0x828, 0x822, 0x81c, 0x816, 0x810, 0x80c, 0x806, 0x800 +}; +#else +/* + logsin table +*/ + +static const uint16_t logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +/* + exp table +*/ + +static const uint16_t exprom[256] = { + 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, + 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, + 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, + 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, + 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, + 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, + 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, + 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, + 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, + 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, + 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, + 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, + 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, + 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, + 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, + 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, + 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, + 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, + 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, + 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, + 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, + 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, + 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, + 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, + 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, + 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, + 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, + 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, + 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, + 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, + 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, + 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 +}; +#endif + +/* + freq mult table multiplied by 2 + + 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 +*/ + +static const uint8_t mt[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 +}; + +/* + ksl table +*/ + +static const uint8_t kslrom[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 +}; + +static const uint8_t kslshift[4] = { + 8, 1, 2, 0 +}; + +/* + envelope generator constants +*/ + +static const uint8_t eg_incstep[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +/* + address decoding +*/ + +static const int8_t ad_slot[0x20] = { + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static const uint8_t ch_slot[18] = { + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 +}; + +#if OPL_ENABLE_STEREOEXT +/* + stereo extension panning table +*/ + +static int32_t panpot_lut[256]; +static uint8_t panpot_lut_build = 0; +#endif + +/* + Pan law table +*/ + +static const uint16_t panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + +/* + Envelope generator +*/ + +#if !OPL_FAST_WAVEGEN +typedef int16_t(*envelope_sinfunc)(uint16_t phase, uint16_t envelope); +typedef void(*envelope_genfunc)(opl3_slot *slott); + +static int16_t OPL3_EnvelopeCalcExp(uint32_t level) +{ + if (level > 0x1fff) + { + level = 0x1fff; + } + return (exprom[level & 0xffu] << 1) >> (level >> 8); +} + +static int16_t OPL3_EnvelopeCalcSin0(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + uint16_t neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + if (phase & 0x100) + { + out = logsinrom[(phase & 0xffu) ^ 0xffu]; + } + else + { + out = logsinrom[phase & 0xffu]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static int16_t OPL3_EnvelopeCalcSin1(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x100) + { + out = logsinrom[(phase & 0xffu) ^ 0xffu]; + } + else + { + out = logsinrom[phase & 0xffu]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static int16_t OPL3_EnvelopeCalcSin2(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = logsinrom[(phase & 0xffu) ^ 0xffu]; + } + else + { + out = logsinrom[phase & 0xffu]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static int16_t OPL3_EnvelopeCalcSin3(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = 0x1000; + } + else + { + out = logsinrom[phase & 0xffu]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static int16_t OPL3_EnvelopeCalcSin4(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + uint16_t neg = 0; + phase &= 0x3ff; + if ((phase & 0x300) == 0x100) + { + neg = 0xffff; + } + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xffu) << 1u) & 0xffu]; + } + else + { + out = logsinrom[(phase << 1u) & 0xffu]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static int16_t OPL3_EnvelopeCalcSin5(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xffu) << 1u) & 0xffu]; + } + else + { + out = logsinrom[(phase << 1u) & 0xffu]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static int16_t OPL3_EnvelopeCalcSin6(uint16_t phase, uint16_t envelope) +{ + uint16_t neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg; +} + +static int16_t OPL3_EnvelopeCalcSin7(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + uint16_t neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + phase = (phase & 0x1ff) ^ 0x1ff; + } + out = phase << 3; + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static const envelope_sinfunc envelope_sin[8] = { + OPL3_EnvelopeCalcSin0, + OPL3_EnvelopeCalcSin1, + OPL3_EnvelopeCalcSin2, + OPL3_EnvelopeCalcSin3, + OPL3_EnvelopeCalcSin4, + OPL3_EnvelopeCalcSin5, + OPL3_EnvelopeCalcSin6, + OPL3_EnvelopeCalcSin7 +}; +#endif + +enum envelope_gen_num +{ + envelope_gen_num_attack = 0, + envelope_gen_num_decay = 1, + envelope_gen_num_sustain = 2, + envelope_gen_num_release = 3 +}; + +static void OPL3_EnvelopeUpdateKSL(opl3_slot *slot) +{ + int16_t ksl = (kslrom[slot->channel->f_num >> 6u] << 2) + - ((0x08 - slot->channel->block) << 5); + if (ksl < 0) + { + ksl = 0; + } + slot->eg_ksl = (uint8_t)ksl; +} + +static void OPL3_EnvelopeCalc(opl3_slot *slot) +{ + uint8_t nonzero; + uint8_t rate; + uint8_t rate_hi; + uint8_t rate_lo; + uint8_t reg_rate = 0; + uint8_t ks; + uint8_t eg_shift, shift; + uint16_t eg_rout; + int16_t eg_inc; + uint8_t eg_off; + uint8_t reset = 0; + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + +#if OPL_FAST_WAVEGEN + if (slot->eg_out > 0x1ff) + { + slot->eg_out = 0x1ff; + } + slot->eg_out <<= 3; +#endif + + if (slot->key && slot->eg_gen == envelope_gen_num_release) + { + reset = 1; + reg_rate = slot->reg_ar; + } + else + { + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + reg_rate = slot->reg_ar; + break; + case envelope_gen_num_decay: + reg_rate = slot->reg_dr; + break; + case envelope_gen_num_sustain: + if (!slot->reg_type) + { + reg_rate = slot->reg_rr; + } + break; + case envelope_gen_num_release: + reg_rate = slot->reg_rr; + break; + } + } + slot->pg_reset = reset; + ks = slot->channel->ksv >> ((slot->reg_ksr ^ 1) << 1); + nonzero = (reg_rate != 0); + rate = ks + (reg_rate << 2); + rate_hi = rate >> 2; + rate_lo = rate & 0x03; + if (rate_hi & 0x10) + { + rate_hi = 0x0f; + } + eg_shift = rate_hi + slot->chip->eg_add; + shift = 0; + if (nonzero) + { + if (rate_hi < 12) + { + if (slot->chip->eg_state) + { + switch (eg_shift) + { + case 12: + shift = 1; + break; + case 13: + shift = (rate_lo >> 1) & 0x01; + break; + case 14: + shift = rate_lo & 0x01; + break; + default: + break; + } + } + } + else + { + shift = (rate_hi & 0x03) + eg_incstep[rate_lo][slot->chip->timer & 0x03u]; + if (shift & 0x04) + { + shift = 0x03; + } + if (!shift) + { + shift = slot->chip->eg_state; + } + } + } + eg_rout = slot->eg_rout; + eg_inc = 0; + eg_off = 0; + /* Instant attack */ + if (reset && rate_hi == 0x0f) + { + eg_rout = 0x00; + } + /* Envelope off */ + if ((slot->eg_rout & 0x1f8) == 0x1f8) + { + eg_off = 1; + } + if (slot->eg_gen != envelope_gen_num_attack && !reset && eg_off) + { + eg_rout = 0x1ff; + } + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + if (!slot->eg_rout) + { + slot->eg_gen = envelope_gen_num_decay; + } + else if (slot->key && shift > 0 && rate_hi != 0x0f) + { + eg_inc = ~slot->eg_rout >> (4 - shift); + } + break; + case envelope_gen_num_decay: + if ((slot->eg_rout >> 4) == slot->reg_sl) + { + slot->eg_gen = envelope_gen_num_sustain; + } + else if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + } + slot->eg_rout = (eg_rout + eg_inc) & 0x1ff; + /* Key off */ + if (reset) + { + slot->eg_gen = envelope_gen_num_attack; + } + if (!slot->key) + { + slot->eg_gen = envelope_gen_num_release; + } +} + +static void OPL3_EnvelopeKeyOn(opl3_slot *slot, uint8_t type) +{ + slot->key |= type; +} + +static void OPL3_EnvelopeKeyOff(opl3_slot *slot, uint8_t type) +{ + slot->key &= ~type; +} + +/* + Phase Generator +*/ + +static void OPL3_PhaseGenerate(opl3_slot *slot) +{ + opl3_chip *chip; + uint16_t f_num; + uint32_t basefreq; + uint8_t rm_xor, n_bit; + uint32_t noise; + uint16_t phase; + + chip = slot->chip; + f_num = slot->channel->f_num; + if (slot->reg_vib) + { + int8_t range; + uint8_t vibpos; + + range = (f_num >> 7) & 7; + vibpos = slot->chip->vibpos; + + if (!(vibpos & 3)) + { + range = 0; + } + else if (vibpos & 1) + { + range >>= 1; + } + range >>= slot->chip->vibshift; + + if (vibpos & 4) + { + range = -range; + } + f_num += range; + } + basefreq = (f_num << slot->channel->block) >> 1; + phase = (uint16_t)(slot->pg_phase >> 9); + if (slot->pg_reset) + { + slot->pg_phase = 0; + } + slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1; + /* Rhythm mode */ + noise = chip->noise; + slot->pg_phase_out = phase; + if (slot->slot_num == 13) /* hh */ + { + chip->rm_hh_bit2 = (phase >> 2) & 1; + chip->rm_hh_bit3 = (phase >> 3) & 1; + chip->rm_hh_bit7 = (phase >> 7) & 1; + chip->rm_hh_bit8 = (phase >> 8) & 1; + } + if (slot->slot_num == 17 && (chip->rhy & 0x20)) /* tc */ + { + chip->rm_tc_bit3 = (phase >> 3) & 1; + chip->rm_tc_bit5 = (phase >> 5) & 1; + } + if (chip->rhy & 0x20) + { + rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) + | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) + | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); + switch (slot->slot_num) + { + case 13: /* hh */ + slot->pg_phase_out = rm_xor << 9; + if (rm_xor ^ (noise & 1)) + { + slot->pg_phase_out |= 0xd0; + } + else + { + slot->pg_phase_out |= 0x34; + } + break; + case 16: /* sd */ + slot->pg_phase_out = (chip->rm_hh_bit8 << 9) + | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8); + break; + case 17: /* tc */ + slot->pg_phase_out = (rm_xor << 9) | 0x80; + break; + default: + break; + } + } + n_bit = ((noise >> 14) ^ noise) & 0x01; + chip->noise = (noise >> 1) | (n_bit << 22); +} + +/* + Slot +*/ + +static void OPL3_SlotWrite20(opl3_slot *slot, uint8_t data) +{ + if ((data >> 7) & 0x01) + { + slot->trem = &slot->chip->tremolo; + } + else + { + slot->trem = (uint8_t*)&slot->chip->zeromod; + } + slot->reg_vib = (data >> 6) & 0x01; + slot->reg_type = (data >> 5) & 0x01; + slot->reg_ksr = (data >> 4) & 0x01; + slot->reg_mult = data & 0x0f; +} + +static void OPL3_SlotWrite40(opl3_slot *slot, uint8_t data) +{ + slot->reg_ksl = (data >> 6) & 0x03; + slot->reg_tl = data & 0x3f; + OPL3_EnvelopeUpdateKSL(slot); +} + +static void OPL3_SlotWrite60(opl3_slot *slot, uint8_t data) +{ + slot->reg_ar = (data >> 4) & 0x0f; + slot->reg_dr = data & 0x0f; +} + +static void OPL3_SlotWrite80(opl3_slot *slot, uint8_t data) +{ + slot->reg_sl = (data >> 4) & 0x0f; + if (slot->reg_sl == 0x0f) + { + slot->reg_sl = 0x1f; + } + slot->reg_rr = data & 0x0f; +} + +static void OPL3_SlotWriteE0(opl3_slot *slot, uint8_t data) +{ + slot->reg_wf = data & 0x07; + if (slot->chip->newm == 0x00) + { + slot->reg_wf &= 0x03; + } + +#if OPL_FAST_WAVEGEN + switch (slot->reg_wf) + { + case 1: + case 4: + case 5: + slot->maskzero = 0x200; + break; + case 3: + slot->maskzero = 0x100; + break; + default: + slot->maskzero = 0; + break; + } + + switch (slot->reg_wf) + { + case 4: + slot->signpos = (31-8); /* sigext of (phase & 0x100) */ + break; + case 0: + case 6: + case 7: + slot->signpos = (31-9); /* sigext of (phase & 0x200) */ + break; + default: + slot->signpos = (31-16); /* set "neg" to zero */ + break; + } + + switch (slot->reg_wf) + { + case 4: + case 5: + slot->phaseshift = 1; + break; + case 6: + slot->phaseshift = 16; /* set phase to zero and flag for non-sin wave */ + break; + case 7: + slot->phaseshift = 32; /* no shift (work by mod 32), but flag for non-sin wave */ + break; + default: + slot->phaseshift = 0; + break; + } +#endif +} + +#if OPL_FAST_WAVEGEN +static void OPL3_SlotGenerate(opl3_slot *slot) +{ + uint16_t phase = slot->pg_phase_out + *slot->mod; + uint32_t neg, level; + uint8_t phaseshift; + + /* Fast paths for mute segments */ + if (phase & slot->maskzero) + { + slot->out = 0; + return; + } + + neg = (int32_t)((uint32_t)phase << slot->signpos) >> 31; + phaseshift = slot->phaseshift; + level = slot->eg_out; + + phase <<= phaseshift; + if (phaseshift <= 1) + { + level += logsinrom[phase & 0x1ff]; + } + else + { + level += ((phase ^ neg) & 0x3ff) << 3; + } + slot->out = exprom[level & 0xff] >> (level >> 8) ^ neg; +} +#else +static void OPL3_SlotGenerate(opl3_slot *slot) +{ + slot->out = envelope_sin[slot->reg_wf](slot->pg_phase_out + *slot->mod, slot->eg_out); +} +#endif + +static void OPL3_SlotCalcFB(opl3_slot *slot) +{ + if (slot->channel->fb != 0x00) + { + slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb); + } + else + { + slot->fbmod = 0; + } + slot->prout = slot->out; +} + +/* + Channel +*/ + +static void OPL3_ChannelSetupAlg(opl3_channel *channel); + +static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, uint8_t data) +{ + opl3_channel *channel6; + opl3_channel *channel7; + opl3_channel *channel8; + uint8_t chnum; + + chip->rhy = data & 0x3f; + if (chip->rhy & 0x20) + { + channel6 = &chip->channel[6]; + channel7 = &chip->channel[7]; + channel8 = &chip->channel[8]; + channel6->out[0] = &channel6->slotz[1]->out; + channel6->out[1] = &channel6->slotz[1]->out; + channel6->out[2] = &chip->zeromod; + channel6->out[3] = &chip->zeromod; + channel7->out[0] = &channel7->slotz[0]->out; + channel7->out[1] = &channel7->slotz[0]->out; + channel7->out[2] = &channel7->slotz[1]->out; + channel7->out[3] = &channel7->slotz[1]->out; + channel8->out[0] = &channel8->slotz[0]->out; + channel8->out[1] = &channel8->slotz[0]->out; + channel8->out[2] = &channel8->slotz[1]->out; + channel8->out[3] = &channel8->slotz[1]->out; + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_drum; + } + OPL3_ChannelSetupAlg(channel6); + OPL3_ChannelSetupAlg(channel7); + OPL3_ChannelSetupAlg(channel8); + /* hh */ + if (chip->rhy & 0x01) + { + OPL3_EnvelopeKeyOn(channel7->slotz[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slotz[0], egk_drum); + } + /* tc */ + if (chip->rhy & 0x02) + { + OPL3_EnvelopeKeyOn(channel8->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slotz[1], egk_drum); + } + /* tom */ + if (chip->rhy & 0x04) + { + OPL3_EnvelopeKeyOn(channel8->slotz[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slotz[0], egk_drum); + } + /* sd */ + if (chip->rhy & 0x08) + { + OPL3_EnvelopeKeyOn(channel7->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slotz[1], egk_drum); + } + /* bd */ + if (chip->rhy & 0x10) + { + OPL3_EnvelopeKeyOn(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOn(channel6->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(channel6->slotz[1], egk_drum); + } + } + else + { + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_2op; + OPL3_ChannelSetupAlg(&chip->channel[chnum]); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[1], egk_drum); + } + } +} + +static void OPL3_ChannelWriteA0(opl3_channel *channel, uint8_t data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0x300) | data; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + } +} + +static void OPL3_ChannelWriteB0(opl3_channel *channel, uint8_t data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8); + channel->block = (data >> 2) & 0x07; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + } +} + +static void OPL3_ChannelSetupAlg(opl3_channel *channel) +{ + if (channel->chtype == ch_drum) + { + if (channel->ch_num == 7 || channel->ch_num == 8) + { + channel->slotz[0]->mod = &channel->chip->zeromod; + channel->slotz[1]->mod = &channel->chip->zeromod; + return; + } + switch (channel->alg & 0x01) + { + case 0x00: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + break; + case 0x01: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + break; + } + return; + } + if (channel->alg & 0x08) + { + return; + } + if (channel->alg & 0x04) + { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) + { + case 0x00: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->chip->zeromod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[1]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x02: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x03: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[0]->out; + channel->out[2] = &channel->slotz[1]->out; + channel->out[3] = &channel->chip->zeromod; + break; + } + } + else + { + switch (channel->alg & 0x01) + { + case 0x00: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + } + } +} + +static void OPL3_ChannelUpdateAlg(opl3_channel *channel) +{ + channel->alg = channel->con; + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + OPL3_ChannelSetupAlg(channel->pair); + } + else if (channel->chtype == ch_4op2) + { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + OPL3_ChannelSetupAlg(channel); + } + else + { + OPL3_ChannelSetupAlg(channel); + } + } + else + { + OPL3_ChannelSetupAlg(channel); + } +} + +static void OPL3_ChannelWriteC0(opl3_channel *channel, uint8_t data) +{ + channel->fb = (data & 0x0e) >> 1; + channel->con = data & 0x01; + OPL3_ChannelUpdateAlg(channel); + if (channel->chip->newm) + { + channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; + channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; + channel->chc = ((data >> 6) & 0x01) ? ~0 : 0; + channel->chd = ((data >> 7) & 0x01) ? ~0 : 0; + } + else + { + channel->cha = channel->chb = (uint16_t)~0; + /* TODO: Verify on real chip if DAC2 output is disabled in compat mode */ + channel->chc = channel->chd = 0; + } +#if OPL_ENABLE_STEREOEXT + if (!channel->chip->stereoext) + { + channel->leftpan = channel->cha << 16; + channel->rightpan = channel->chb << 16; + } +#endif +} + +#if OPL_ENABLE_STEREOEXT +static void OPL3_ChannelWriteD0(opl3_channel* channel, uint8_t data) +{ + if (channel->chip->stereoext) + { + channel->leftpan = panpot_lut[data ^ 0xffu]; + channel->rightpan = panpot_lut[data]; + } +} +#endif + +static void OPL3_ChannelKeyOn(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + } +} + +static void OPL3_ChannelKeyOff(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + } +} + +static void OPL3_ChannelSet4Op(opl3_chip *chip, uint8_t data) +{ + uint8_t bit; + uint8_t chnum; + for (bit = 0; bit < 6; bit++) + { + chnum = bit; + if (bit >= 3) + { + chnum += 9 - 3; + } + if ((data >> bit) & 0x01) + { + chip->channel[chnum].chtype = ch_4op; + chip->channel[chnum + 3u].chtype = ch_4op2; + OPL3_ChannelUpdateAlg(&chip->channel[chnum]); + } + else + { + chip->channel[chnum].chtype = ch_2op; + chip->channel[chnum + 3u].chtype = ch_2op; + OPL3_ChannelUpdateAlg(&chip->channel[chnum]); + OPL3_ChannelUpdateAlg(&chip->channel[chnum + 3u]); + } + } +} + +static int16_t OPL3_ClipSample(int32_t sample) +{ + if (sample > 32767) + { + sample = 32767; + } + else if (sample < -32768) + { + sample = -32768; + } + return (int16_t)sample; +} + +static void OPL3_ProcessSlot(opl3_slot *slot) +{ + OPL3_SlotCalcFB(slot); + OPL3_EnvelopeCalc(slot); + OPL3_PhaseGenerate(slot); + OPL3_SlotGenerate(slot); +} + +inline void OPL3_Generate4Ch(opl3_chip *chip, int16_t *buf4) +{ + opl3_channel *channel; + opl3_writebuf *writebuf; + int16_t **out; + int32_t mix[2]; + uint8_t ii; + int16_t accm; + uint8_t shift = 0; + + buf4[1] = OPL3_ClipSample(chip->mixbuff[1]); + buf4[3] = OPL3_ClipSample(chip->mixbuff[3]); + +#if OPL_QUIRK_CHANNELSAMPLEDELAY + for (ii = 0; ii < 15; ii++) +#else + for (ii = 0; ii < 36; ii++) +#endif + { + OPL3_ProcessSlot(&chip->slot[ii]); + } + + mix[0] = mix[1] = 0; + for (ii = 0; ii < 18; ii++) + { + channel = &chip->channel[ii]; + out = channel->out; + accm = *out[0] + *out[1] + *out[2] + *out[3]; +#if OPL_ENABLE_STEREOEXT + mix[0] += (int16_t)((accm * channel->leftpan) >> 16); +#else + mix[0] += (int16_t)((accm * chip->channel[ii].chl / 65535) & channel->cha); +#endif + mix[1] += (int16_t)(accm & channel->chc); + } + chip->mixbuff[0] = mix[0]; + chip->mixbuff[2] = mix[1]; + +#if OPL_QUIRK_CHANNELSAMPLEDELAY + for (ii = 15; ii < 18; ii++) + { + OPL3_ProcessSlot(&chip->slot[ii]); + } +#endif + + buf4[0] = OPL3_ClipSample(chip->mixbuff[0]); + buf4[2] = OPL3_ClipSample(chip->mixbuff[2]); + +#if OPL_QUIRK_CHANNELSAMPLEDELAY + for (ii = 18; ii < 33; ii++) + { + OPL3_ProcessSlot(&chip->slot[ii]); + } +#endif + + mix[0] = mix[1] = 0; + for (ii = 0; ii < 18; ii++) + { + channel = &chip->channel[ii]; + out = channel->out; + accm = *out[0] + *out[1] + *out[2] + *out[3]; +#if OPL_ENABLE_STEREOEXT + mix[0] += (int16_t)((accm * channel->rightpan) >> 16); +#else + mix[0] += (int16_t)((accm * chip->channel[ii].chr / 65535) & channel->chb); +#endif + mix[1] += (int16_t)(accm & channel->chd); + } + chip->mixbuff[1] = mix[0]; + chip->mixbuff[3] = mix[1]; + +#if OPL_QUIRK_CHANNELSAMPLEDELAY + for (ii = 33; ii < 36; ii++) + { + OPL3_ProcessSlot(&chip->slot[ii]); + } +#endif + + if ((chip->timer & 0x3f) == 0x3f) + { + chip->tremolopos = (chip->tremolopos + 1) % 210; + } + if (chip->tremolopos < 105) + { + chip->tremolo = chip->tremolopos >> chip->tremoloshift; + } + else + { + chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift; + } + + if ((chip->timer & 0x3ff) == 0x3ff) + { + chip->vibpos = (chip->vibpos + 1) & 7; + } + + chip->timer++; + + chip->eg_add = 0; + if (chip->eg_timer) + { + while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0) + { + shift++; + } + if (shift > 12) + { + chip->eg_add = 0; + } + else + { + chip->eg_add = shift + 1; + } + } + + if (chip->eg_timerrem || chip->eg_state) + { + if (chip->eg_timer == UINT64_C(0xfffffffff)) + { + chip->eg_timer = 0; + chip->eg_timerrem = 1; + } + else + { + chip->eg_timer++; + chip->eg_timerrem = 0; + } + } + + chip->eg_state ^= 1; + + while ((writebuf = &chip->writebuf[chip->writebuf_cur]), writebuf->time <= chip->writebuf_samplecnt) + { + if (!(writebuf->reg & 0x200)) + { + break; + } + writebuf->reg &= 0x1ff; + OPL3_WriteReg(chip, writebuf->reg, writebuf->data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; +} + +void OPL3_Generate(opl3_chip *chip, int16_t *buf) +{ + int16_t samples[4]; + OPL3_Generate4Ch(chip, samples); + buf[0] = samples[0]; + buf[1] = samples[1]; +} + +void OPL3_Generate4ChResampled(opl3_chip *chip, int16_t *buf4) +{ + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + chip->oldsamples[2] = chip->samples[2]; + chip->oldsamples[3] = chip->samples[3]; + OPL3_Generate4Ch(chip, chip->samples); + chip->samplecnt -= chip->rateratio; + } + buf4[0] = (int16_t)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio); + buf4[1] = (int16_t)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio); + buf4[2] = (int16_t)((chip->oldsamples[2] * (chip->rateratio - chip->samplecnt) + + chip->samples[2] * chip->samplecnt) / chip->rateratio); + buf4[3] = (int16_t)((chip->oldsamples[3] * (chip->rateratio - chip->samplecnt) + + chip->samples[3] * chip->samplecnt) / chip->rateratio); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPL3_GenerateResampled(opl3_chip *chip, int16_t *buf) +{ + int16_t samples[4]; + OPL3_Generate4ChResampled(chip, samples); + buf[0] = samples[0]; + buf[1] = samples[1]; +} + +void OPL3_Reset(opl3_chip *chip, uint32_t samplerate) +{ + opl3_slot *slot; + opl3_channel *channel; + uint8_t slotnum; + uint8_t channum; + uint8_t local_ch_slot; + + memset(chip, 0, sizeof(opl3_chip)); + for (slotnum = 0; slotnum < 36; slotnum++) + { + slot = &chip->slot[slotnum]; + slot->chip = chip; + slot->mod = &chip->zeromod; + slot->eg_rout = 0x1ff; +#if OPL_FAST_WAVEGEN + slot->eg_out = 0x1ff << 3; +#else + slot->eg_out = 0x1ff; +#endif + slot->eg_gen = envelope_gen_num_release; + slot->trem = (uint8_t*)&chip->zeromod; + slot->slot_num = slotnum; +#if OPL_FAST_WAVEGEN + slot->signpos = (31-9); /* for wf=0 need use sigext of (phase & 0x200) */ +#endif + } + for (channum = 0; channum < 18; channum++) + { + channel = &chip->channel[channum]; + local_ch_slot = ch_slot[channum]; + channel->slotz[0] = &chip->slot[local_ch_slot]; + channel->slotz[1] = &chip->slot[local_ch_slot + 3u]; + chip->slot[local_ch_slot].channel = channel; + chip->slot[local_ch_slot + 3u].channel = channel; + if ((channum % 9) < 3) + { + channel->pair = &chip->channel[channum + 3u]; + } + else if ((channum % 9) < 6) + { + channel->pair = &chip->channel[channum - 3u]; + } + channel->chip = chip; + channel->out[0] = &chip->zeromod; + channel->out[1] = &chip->zeromod; + channel->out[2] = &chip->zeromod; + channel->out[3] = &chip->zeromod; + channel->chtype = ch_2op; + channel->cha = 0xffff; + channel->chb = 0xffff; + channel->chl = 46340; + channel->chr = 46340; +#if OPL_ENABLE_STEREOEXT + channel->leftpan = 0x10000; + channel->rightpan = 0x10000; +#endif + channel->ch_num = channum; + OPL3_ChannelSetupAlg(channel); + } + chip->noise = 1; + chip->rateratio = (samplerate << RSM_FRAC) / 49716; + chip->tremoloshift = 4; + chip->vibshift = 1; + +#if OPL_ENABLE_STEREOEXT + if (!panpot_lut_build) + { + int32_t i; + for (i = 0; i < 256; i++) + { + panpot_lut[i] = OPL_SIN(i); + } + panpot_lut_build = 1; + } +#endif +} + +static void OPL3_ChannelWritePan(opl3_channel *channel, uint8_t data) +{ + channel->chl = panlawtable[data & 0x7F]; + channel->chr = panlawtable[0x7F - (data & 0x7F)]; +} + +void OPL3_WritePan(opl3_chip *chip, uint16_t reg, uint8_t v) +{ + uint8_t high = (reg >> 8) & 0x01; + uint8_t regm = reg & 0xff; + OPL3_ChannelWritePan(&chip->channel[9 * high + (regm & 0x0f)], v); +} + +void OPL3_WriteReg(opl3_chip *chip, uint16_t reg, uint8_t v) +{ + uint8_t high = (reg >> 8) & 0x01; + uint8_t regm = reg & 0xff; + switch (regm & 0xf0) + { + case 0x00: + if (high) + { + switch (regm & 0x0f) + { + case 0x04: + OPL3_ChannelSet4Op(chip, v); + break; + case 0x05: + chip->newm = v & 0x01; +#if OPL_ENABLE_STEREOEXT + chip->stereoext = (v >> 1) & 0x01; +#endif + break; + } + } + else + { + switch (regm & 0x0f) + { + case 0x08: + chip->nts = (v >> 6) & 0x01; + break; + } + } + break; + case 0x20: + case 0x30: + if (ad_slot[regm & 0x1fu] >= 0) + { + OPL3_SlotWrite20(&chip->slot[18u * high + ad_slot[regm & 0x1fu]], v); + } + break; + case 0x40: + case 0x50: + if (ad_slot[regm & 0x1fu] >= 0) + { + OPL3_SlotWrite40(&chip->slot[18u * high + ad_slot[regm & 0x1fu]], v); + } + break; + case 0x60: + case 0x70: + if (ad_slot[regm & 0x1fu] >= 0) + { + OPL3_SlotWrite60(&chip->slot[18u * high + ad_slot[regm & 0x1fu]], v); + } + break; + case 0x80: + case 0x90: + if (ad_slot[regm & 0x1fu] >= 0) + { + OPL3_SlotWrite80(&chip->slot[18u * high + ad_slot[regm & 0x1fu]], v); + } + break; + case 0xe0: + case 0xf0: + if (ad_slot[regm & 0x1fu] >= 0) + { + OPL3_SlotWriteE0(&chip->slot[18u * high + ad_slot[regm & 0x1fu]], v); + } + break; + case 0xa0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteA0(&chip->channel[9u * high + (regm & 0x0fu)], v); + } + break; + case 0xb0: + if (regm == 0xbd && !high) + { + chip->tremoloshift = (((v >> 7) ^ 1) << 1) + 2; + chip->vibshift = ((v >> 6) & 0x01) ^ 1; + OPL3_ChannelUpdateRhythm(chip, v); + } + else if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteB0(&chip->channel[9u * high + (regm & 0x0fu)], v); + if (v & 0x20) + { + OPL3_ChannelKeyOn(&chip->channel[9u * high + (regm & 0x0fu)]); + } + else + { + OPL3_ChannelKeyOff(&chip->channel[9u * high + (regm & 0x0fu)]); + } + } + break; + case 0xc0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteC0(&chip->channel[9u * high + (regm & 0x0fu)], v); + } + break; +#if OPL_ENABLE_STEREOEXT + case 0xd0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteD0(&chip->channel[9u * high + (regm & 0x0fu)], v); + } + break; +#endif + } +} + +void OPL3_WriteRegBuffered(opl3_chip *chip, uint16_t reg, uint8_t v) +{ + uint64_t time1, time2; + opl3_writebuf *writebuf; + uint32_t writebuf_last; + + writebuf_last = chip->writebuf_last; + writebuf = &chip->writebuf[writebuf_last]; + + if (writebuf->reg & 0x200) + { + OPL3_WriteReg(chip, writebuf->reg & 0x1ff, writebuf->data); + + chip->writebuf_cur = (writebuf_last + 1) % OPL_WRITEBUF_SIZE; + chip->writebuf_samplecnt = writebuf->time; + } + + writebuf->reg = reg | 0x200; + writebuf->data = v; + time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + writebuf->time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (writebuf_last + 1) % OPL_WRITEBUF_SIZE; +} + +void OPL3_Generate4ChStream(opl3_chip *chip, int16_t *sndptr1, int16_t *sndptr2, uint32_t numsamples) +{ + uint_fast32_t i; + int16_t samples[4]; + + for(i = 0; i < numsamples; i++) + { + OPL3_Generate4ChResampled(chip, samples); + sndptr1[0] = samples[0]; + sndptr1[1] = samples[1]; + sndptr2[0] = samples[2]; + sndptr2[1] = samples[3]; + sndptr1 += 2; + sndptr2 += 2; + } +} + +void OPL3_GenerateStream(opl3_chip *chip, int16_t *sndptr, uint32_t numsamples) +{ + uint_fast32_t i; + + for(i = 0; i < numsamples; i++) + { + OPL3_GenerateResampled(chip, sndptr); + sndptr += 2; + } +} diff --git a/thirdparty/adlmidi/chips/nuked/nukedopl3.h b/thirdparty/adlmidi/chips/nuked/nukedopl3.h new file mode 100644 index 000000000..fa32e77f1 --- /dev/null +++ b/thirdparty/adlmidi/chips/nuked/nukedopl3.h @@ -0,0 +1,184 @@ +/* Nuked OPL3 + * Copyright (C) 2013-2020 Nuke.YKT + * + * This file is part of Nuked OPL3. + * + * Nuked OPL3 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. + * + * Nuked OPL3 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 Nuked OPL3. If not, see . + + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * + * version: 1.8 + */ + +#ifndef OPL_OPL3_H +#define OPL_OPL3_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef OPL_ENABLE_STEREOEXT +#define OPL_ENABLE_STEREOEXT 0 +#endif + +#ifndef OPL_FAST_WAVEGEN +#define OPL_FAST_WAVEGEN 1 /* optimized waveform generation */ +#endif + +#define OPL_WRITEBUF_SIZE 1024 +#define OPL_WRITEBUF_DELAY 2 + +typedef struct _opl3_slot opl3_slot; +typedef struct _opl3_channel opl3_channel; +typedef struct _opl3_chip opl3_chip; + +struct _opl3_slot { + opl3_channel *channel; + opl3_chip *chip; + int16_t out; + int16_t fbmod; + int16_t *mod; + int16_t prout; + uint16_t eg_rout; + uint16_t eg_out; + uint8_t eg_inc; + uint8_t eg_gen; + uint8_t eg_rate; + uint8_t eg_ksl; + uint8_t *trem; + uint8_t reg_vib; + uint8_t reg_type; + uint8_t reg_ksr; + uint8_t reg_mult; + uint8_t reg_ksl; + uint8_t reg_tl; + uint8_t reg_ar; + uint8_t reg_dr; + uint8_t reg_sl; + uint8_t reg_rr; + uint8_t reg_wf; + uint8_t key; + uint32_t pg_reset; + uint32_t pg_phase; + uint16_t pg_phase_out; + uint8_t slot_num; + +#if OPL_FAST_WAVEGEN + uint16_t maskzero; + uint8_t signpos; + uint8_t phaseshift; +#endif +}; + +struct _opl3_channel { + opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/ + opl3_channel *pair; + opl3_chip *chip; + int16_t *out[4]; + +#if OPL_ENABLE_STEREOEXT + int32_t leftpan; + int32_t rightpan; +#endif + + uint8_t chtype; + uint16_t f_num; + uint8_t block; + uint8_t fb; + uint8_t con; + uint8_t alg; + uint8_t ksv; + uint16_t cha, chb; + uint16_t chc, chd; + uint16_t chl, chr; + uint8_t ch_num; +}; + +typedef struct _opl3_writebuf { + uint64_t time; + uint16_t reg; + uint8_t data; +} opl3_writebuf; + +struct _opl3_chip { + opl3_channel channel[18]; + opl3_slot slot[36]; + uint16_t timer; + uint64_t eg_timer; + uint8_t eg_timerrem; + uint8_t eg_state; + uint8_t eg_add; + uint8_t newm; + uint8_t nts; + uint8_t rhy; + uint8_t vibpos; + uint8_t vibshift; + uint8_t tremolo; + uint8_t tremolopos; + uint8_t tremoloshift; + uint32_t noise; + int16_t zeromod; + int32_t mixbuff[4]; + uint8_t rm_hh_bit2; + uint8_t rm_hh_bit3; + uint8_t rm_hh_bit7; + uint8_t rm_hh_bit8; + uint8_t rm_tc_bit3; + uint8_t rm_tc_bit5; + +#if OPL_ENABLE_STEREOEXT + uint8_t stereoext; +#endif + + /* OPL3L */ + int32_t rateratio; + int32_t samplecnt; + int16_t oldsamples[4]; + int16_t samples[4]; + + uint64_t writebuf_samplecnt; + uint32_t writebuf_cur; + uint32_t writebuf_last; + uint64_t writebuf_lasttime; + opl3_writebuf writebuf[OPL_WRITEBUF_SIZE]; +}; + +void OPL3_Generate(opl3_chip *chip, int16_t *buf); +void OPL3_GenerateResampled(opl3_chip *chip, int16_t *buf); +void OPL3_Reset(opl3_chip *chip, uint32_t samplerate); +void OPL3_WriteReg(opl3_chip *chip, uint16_t reg, uint8_t v); +void OPL3_WriteRegBuffered(opl3_chip *chip, uint16_t reg, uint8_t v); +void OPL3_WritePan(opl3_chip *chip, uint16_t reg, uint8_t v); +void OPL3_GenerateStream(opl3_chip *chip, int16_t *sndptr, uint32_t numsamples); + +void OPL3_Generate4Ch(opl3_chip *chip, int16_t *buf4); +void OPL3_Generate4ChResampled(opl3_chip *chip, int16_t *buf4); +void OPL3_Generate4ChStream(opl3_chip *chip, int16_t *sndptr1, int16_t *sndptr2, uint32_t numsamples); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/adlmidi/chips/nuked/nukedopl3_174.c b/thirdparty/adlmidi/chips/nuked/nukedopl3_174.c new file mode 100644 index 000000000..8f818d4ee --- /dev/null +++ b/thirdparty/adlmidi/chips/nuked/nukedopl3_174.c @@ -0,0 +1,1432 @@ +/* + * Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT) + * + * This library 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 library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.7.4 + */ + +#include +#include +#include +#include "nukedopl3_174.h" + +#define RSM_FRAC 10 + +/* Channel types */ + +enum { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2, + ch_drum = 3 +}; + +/* Envelope key types */ + +enum { + egk_norm = 0x01, + egk_drum = 0x02 +}; + + +/* + * logsin table + */ + +static const Bit16u logsinrom[512] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x002, + 0x002, 0x002, 0x002, 0x003, 0x003, 0x003, 0x004, 0x004, + 0x004, 0x005, 0x005, 0x005, 0x006, 0x006, 0x007, 0x007, + 0x007, 0x008, 0x008, 0x009, 0x009, 0x00a, 0x00a, 0x00b, + 0x00c, 0x00c, 0x00d, 0x00d, 0x00e, 0x00f, 0x00f, 0x010, + 0x011, 0x011, 0x012, 0x013, 0x014, 0x014, 0x015, 0x016, + 0x017, 0x017, 0x018, 0x019, 0x01a, 0x01b, 0x01c, 0x01d, + 0x01e, 0x01f, 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, + 0x026, 0x027, 0x028, 0x029, 0x02a, 0x02b, 0x02d, 0x02e, + 0x02f, 0x030, 0x031, 0x033, 0x034, 0x035, 0x037, 0x038, + 0x039, 0x03b, 0x03c, 0x03e, 0x03f, 0x040, 0x042, 0x043, + 0x045, 0x046, 0x048, 0x04a, 0x04b, 0x04d, 0x04e, 0x050, + 0x052, 0x053, 0x055, 0x057, 0x059, 0x05b, 0x05c, 0x05e, + 0x060, 0x062, 0x064, 0x066, 0x068, 0x06a, 0x06c, 0x06e, + 0x070, 0x072, 0x074, 0x076, 0x078, 0x07a, 0x07d, 0x07f, + 0x081, 0x083, 0x086, 0x088, 0x08a, 0x08d, 0x08f, 0x092, + 0x094, 0x097, 0x099, 0x09c, 0x09f, 0x0a1, 0x0a4, 0x0a7, + 0x0a9, 0x0ac, 0x0af, 0x0b2, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c1, 0x0c4, 0x0c7, 0x0ca, 0x0cd, 0x0d1, 0x0d4, 0x0d7, + 0x0db, 0x0de, 0x0e2, 0x0e5, 0x0e9, 0x0ec, 0x0f0, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x103, 0x107, 0x10b, 0x10f, 0x114, + 0x118, 0x11c, 0x121, 0x125, 0x129, 0x12e, 0x133, 0x137, + 0x13c, 0x141, 0x146, 0x14b, 0x150, 0x155, 0x15b, 0x160, + 0x166, 0x16b, 0x171, 0x177, 0x17c, 0x182, 0x188, 0x18f, + 0x195, 0x19b, 0x1a2, 0x1a9, 0x1b0, 0x1b7, 0x1be, 0x1c5, + 0x1cd, 0x1d4, 0x1dc, 0x1e4, 0x1ec, 0x1f5, 0x1fd, 0x206, + 0x20f, 0x218, 0x222, 0x22c, 0x236, 0x240, 0x24b, 0x256, + 0x261, 0x26d, 0x279, 0x286, 0x293, 0x2a0, 0x2af, 0x2bd, + 0x2cd, 0x2dc, 0x2ed, 0x2ff, 0x311, 0x324, 0x339, 0x34e, + 0x365, 0x37e, 0x398, 0x3b5, 0x3d3, 0x3f5, 0x41a, 0x443, + 0x471, 0x4a6, 0x4e4, 0x52e, 0x58b, 0x607, 0x6c3, 0x859 +}; + +/* + * exp table + */ + +static const Bit16u exprom[256] = { + 0xff4, 0xfea, 0xfde, 0xfd4, 0xfc8, 0xfbe, 0xfb4, 0xfa8, + 0xf9e, 0xf92, 0xf88, 0xf7e, 0xf72, 0xf68, 0xf5c, 0xf52, + 0xf48, 0xf3e, 0xf32, 0xf28, 0xf1e, 0xf14, 0xf08, 0xefe, + 0xef4, 0xeea, 0xee0, 0xed4, 0xeca, 0xec0, 0xeb6, 0xeac, + 0xea2, 0xe98, 0xe8e, 0xe84, 0xe7a, 0xe70, 0xe66, 0xe5c, + 0xe52, 0xe48, 0xe3e, 0xe34, 0xe2a, 0xe20, 0xe16, 0xe0c, + 0xe04, 0xdfa, 0xdf0, 0xde6, 0xddc, 0xdd2, 0xdca, 0xdc0, + 0xdb6, 0xdac, 0xda4, 0xd9a, 0xd90, 0xd88, 0xd7e, 0xd74, + 0xd6a, 0xd62, 0xd58, 0xd50, 0xd46, 0xd3c, 0xd34, 0xd2a, + 0xd22, 0xd18, 0xd10, 0xd06, 0xcfe, 0xcf4, 0xcec, 0xce2, + 0xcda, 0xcd0, 0xcc8, 0xcbe, 0xcb6, 0xcae, 0xca4, 0xc9c, + 0xc92, 0xc8a, 0xc82, 0xc78, 0xc70, 0xc68, 0xc60, 0xc56, + 0xc4e, 0xc46, 0xc3c, 0xc34, 0xc2c, 0xc24, 0xc1c, 0xc12, + 0xc0a, 0xc02, 0xbfa, 0xbf2, 0xbea, 0xbe0, 0xbd8, 0xbd0, + 0xbc8, 0xbc0, 0xbb8, 0xbb0, 0xba8, 0xba0, 0xb98, 0xb90, + 0xb88, 0xb80, 0xb78, 0xb70, 0xb68, 0xb60, 0xb58, 0xb50, + 0xb48, 0xb40, 0xb38, 0xb32, 0xb2a, 0xb22, 0xb1a, 0xb12, + 0xb0a, 0xb02, 0xafc, 0xaf4, 0xaec, 0xae4, 0xade, 0xad6, + 0xace, 0xac6, 0xac0, 0xab8, 0xab0, 0xaa8, 0xaa2, 0xa9a, + 0xa92, 0xa8c, 0xa84, 0xa7c, 0xa76, 0xa6e, 0xa68, 0xa60, + 0xa58, 0xa52, 0xa4a, 0xa44, 0xa3c, 0xa36, 0xa2e, 0xa28, + 0xa20, 0xa18, 0xa12, 0xa0c, 0xa04, 0x9fe, 0x9f6, 0x9f0, + 0x9e8, 0x9e2, 0x9da, 0x9d4, 0x9ce, 0x9c6, 0x9c0, 0x9b8, + 0x9b2, 0x9ac, 0x9a4, 0x99e, 0x998, 0x990, 0x98a, 0x984, + 0x97c, 0x976, 0x970, 0x96a, 0x962, 0x95c, 0x956, 0x950, + 0x948, 0x942, 0x93c, 0x936, 0x930, 0x928, 0x922, 0x91c, + 0x916, 0x910, 0x90a, 0x904, 0x8fc, 0x8f6, 0x8f0, 0x8ea, + 0x8e4, 0x8de, 0x8d8, 0x8d2, 0x8cc, 0x8c6, 0x8c0, 0x8ba, + 0x8b4, 0x8ae, 0x8a8, 0x8a2, 0x89c, 0x896, 0x890, 0x88a, + 0x884, 0x87e, 0x878, 0x872, 0x86c, 0x866, 0x860, 0x85a, + 0x854, 0x850, 0x84a, 0x844, 0x83e, 0x838, 0x832, 0x82c, + 0x828, 0x822, 0x81c, 0x816, 0x810, 0x80c, 0x806, 0x800 +}; + +/* + * freq mult table multiplied by 2 + * + * 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 + */ + +static const Bit8u mt[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 +}; + +/* + * ksl table + */ + +static const Bit8u kslrom[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 +}; + +static const Bit8u kslshift[4] = { + 8, 1, 2, 0 +}; + +/* + * envelope generator constants + */ + +static const Bit8u eg_incstep[3][4][8] = { + { + { 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 } + }, + { + { 0, 1, 0, 1, 0, 1, 0, 1 }, + { 0, 1, 0, 1, 1, 1, 0, 1 }, + { 0, 1, 1, 1, 0, 1, 1, 1 }, + { 0, 1, 1, 1, 1, 1, 1, 1 } + }, + { + { 1, 1, 1, 1, 1, 1, 1, 1 }, + { 2, 2, 1, 1, 1, 1, 1, 1 }, + { 2, 2, 1, 1, 2, 2, 1, 1 }, + { 2, 2, 2, 2, 2, 2, 1, 1 } + } +}; + +static const Bit8u eg_incdesc[16] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 +}; + +static const Bit8s eg_incsh[16] = { + 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2 +}; + +/* + * address decoding + */ + +static const Bit8s ad_slot[0x20] = { + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static const Bit8u ch_slot[18] = { + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 +}; + +/* + * Pan law table + */ + +static const Bit16u panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + +/* + * Envelope generator + */ + +static void OPL3_EnvelopeGenOff(opl3_slot *slot); +static void OPL3_EnvelopeGenAttack(opl3_slot *slot); +static void OPL3_EnvelopeGenDecay(opl3_slot *slot); +static void OPL3_EnvelopeGenSustain(opl3_slot *slot); +static void OPL3_EnvelopeGenRelease(opl3_slot *slot); + +typedef void(*envelope_genfunc)(opl3_slot *slot); + +envelope_genfunc envelope_gen[5] = { + OPL3_EnvelopeGenOff, + OPL3_EnvelopeGenAttack, + OPL3_EnvelopeGenDecay, + OPL3_EnvelopeGenSustain, + OPL3_EnvelopeGenRelease +}; + +enum envelope_gen_num +{ + envelope_gen_num_off = 0, + envelope_gen_num_attack = 1, + envelope_gen_num_decay = 2, + envelope_gen_num_sustain = 3, + envelope_gen_num_release = 4 +}; + +static Bit8u OPL3_EnvelopeCalcRate(opl3_slot *slot, Bit8u reg_rate) +{ + Bit8u rate; + if (reg_rate == 0x00) + { + return 0x00; + } + rate = (reg_rate << 2) + + (slot->reg_ksr ? slot->channel->ksv : (slot->channel->ksv >> 2)); + if (rate > 0x3c) + { + rate = 0x3c; + } + return rate; +} + +static void OPL3_EnvelopeUpdateKSL(opl3_slot *slot) +{ + Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 2) + - ((0x08 - slot->channel->block) << 5); + if (ksl < 0) + { + ksl = 0; + } + slot->eg_ksl = (Bit8u)ksl; +} + +static void OPL3_EnvelopeUpdateRate(opl3_slot *slot) +{ + switch (slot->eg_gen) + { + case envelope_gen_num_off: + case envelope_gen_num_attack: + slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_ar); + break; + case envelope_gen_num_decay: + slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_dr); + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_rr); + break; + } +} + +static void OPL3_EnvelopeGenOff(opl3_slot *slot) +{ + slot->eg_rout = 0x1ff; +} + +static void OPL3_EnvelopeGenAttack(opl3_slot *slot) +{ + if (slot->eg_rout == 0x00) + { + slot->eg_gen = envelope_gen_num_decay; + OPL3_EnvelopeUpdateRate(slot); + return; + } + slot->eg_rout += ((~slot->eg_rout) * slot->eg_inc) >> 3; + if (slot->eg_rout < 0x00) + { + slot->eg_rout = 0x00; + } +} + +static void OPL3_EnvelopeGenDecay(opl3_slot *slot) +{ + if (slot->eg_rout >= slot->reg_sl << 4) + { + slot->eg_gen = envelope_gen_num_sustain; + OPL3_EnvelopeUpdateRate(slot); + return; + } + slot->eg_rout += slot->eg_inc; +} + +static void OPL3_EnvelopeGenSustain(opl3_slot *slot) +{ + if (!slot->reg_type) + { + OPL3_EnvelopeGenRelease(slot); + } +} + +static void OPL3_EnvelopeGenRelease(opl3_slot *slot) +{ + if (slot->eg_rout >= 0x1ff) + { + slot->eg_gen = envelope_gen_num_off; + slot->eg_rout = 0x1ff; + OPL3_EnvelopeUpdateRate(slot); + return; + } + slot->eg_rout += slot->eg_inc; +} + +static void OPL3_EnvelopeCalc(opl3_slot *slot) +{ + Bit8u rate_h, rate_l; + Bit8u inc = 0; + rate_h = slot->eg_rate >> 2; + rate_l = slot->eg_rate & 3; + if (eg_incsh[rate_h] > 0) + { + if ((slot->chip->timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) + { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l] + [((slot->chip->timer)>> eg_incsh[rate_h]) & 0x07]; + } + } + else + { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l] + [slot->chip->timer & 0x07] << (-eg_incsh[rate_h]); + } + slot->eg_inc = inc; + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + if (slot->eg_out > 0x1ff) /* TODO: Remove this if possible */ + { + slot->eg_out = 0x1ff; + } + slot->eg_out <<= 3; + + envelope_gen[slot->eg_gen](slot); +} + +static void OPL3_EnvelopeKeyOn(opl3_slot *slot, Bit8u type) +{ + if (!slot->key) + { + slot->eg_gen = envelope_gen_num_attack; + OPL3_EnvelopeUpdateRate(slot); + if ((slot->eg_rate >> 2) == 0x0f) + { + slot->eg_gen = envelope_gen_num_decay; + OPL3_EnvelopeUpdateRate(slot); + slot->eg_rout = 0x00; + } + slot->pg_phase = 0x00; + } + slot->key |= type; +} + +static void OPL3_EnvelopeKeyOff(opl3_slot *slot, Bit8u type) +{ + if (slot->key) + { + slot->key &= (~type); + if (!slot->key) + { + slot->eg_gen = envelope_gen_num_release; + OPL3_EnvelopeUpdateRate(slot); + } + } +} + +/* + * Phase Generator + */ + +static void OPL3_PhaseGenerate(opl3_slot *slot) +{ + Bit16u f_num; + Bit32u basefreq; + + f_num = slot->channel->f_num; + if (slot->reg_vib) + { + Bit8s range; + Bit8u vibpos; + + range = (f_num >> 7) & 7; + vibpos = slot->chip->vibpos; + + if (!(vibpos & 3)) + { + range = 0; + } + else if (vibpos & 1) + { + range >>= 1; + } + range >>= slot->chip->vibshift; + + if (vibpos & 4) + { + range = -range; + } + f_num += range; + } + basefreq = (f_num << slot->channel->block) >> 1; + slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1; +} + +/* + * Noise Generator + */ + +static void OPL3_NoiseGenerate(opl3_chip *chip) +{ + if (chip->noise & 0x01) + { + chip->noise ^= 0x800302; + } + chip->noise >>= 1; +} + +/* + * Slot + */ + +static void OPL3_SlotWrite20(opl3_slot *slot, Bit8u data) +{ + if ((data >> 7) & 0x01) + { + slot->trem = &slot->chip->tremolo; + } + else + { + slot->trem = (Bit8u*)&slot->chip->zeromod; + } + slot->reg_vib = (data >> 6) & 0x01; + slot->reg_type = (data >> 5) & 0x01; + slot->reg_ksr = (data >> 4) & 0x01; + slot->reg_mult = data & 0x0f; + OPL3_EnvelopeUpdateRate(slot); +} + +static void OPL3_SlotWrite40(opl3_slot *slot, Bit8u data) +{ + slot->reg_ksl = (data >> 6) & 0x03; + slot->reg_tl = data & 0x3f; + OPL3_EnvelopeUpdateKSL(slot); +} + +static void OPL3_SlotWrite60(opl3_slot *slot, Bit8u data) +{ + slot->reg_ar = (data >> 4) & 0x0f; + slot->reg_dr = data & 0x0f; + OPL3_EnvelopeUpdateRate(slot); +} + +static void OPL3_SlotWrite80(opl3_slot *slot, Bit8u data) +{ + slot->reg_sl = (data >> 4) & 0x0f; + if (slot->reg_sl == 0x0f) + { + slot->reg_sl = 0x1f; + } + slot->reg_rr = data & 0x0f; + OPL3_EnvelopeUpdateRate(slot); +} + +static void OPL3_SlotWriteE0(opl3_slot *slot, Bit8u data) +{ + slot->reg_wf = data & 0x07; + if (slot->chip->newm == 0x00) + { + slot->reg_wf &= 0x03; + } + + switch (slot->reg_wf) + { + case 1: + case 4: + case 5: + slot->maskzero = 0x200; + break; + case 3: + slot->maskzero = 0x100; + break; + default: + slot->maskzero = 0; + break; + } + + switch (slot->reg_wf) + { + case 4: + slot->signpos = (31-8); /* sigext of (phase & 0x100) */ + break; + case 0: + case 6: + case 7: + slot->signpos = (31-9); /* sigext of (phase & 0x200) */ + break; + default: + slot->signpos = (31-16); /* set "neg" to zero */ + break; + } + + switch (slot->reg_wf) + { + case 4: + case 5: + slot->phaseshift = 1; + break; + case 6: + slot->phaseshift = 16; /* set phase to zero and flag for non-sin wave */ + break; + case 7: + slot->phaseshift = 32; /* no shift (work by mod 32), but flag for non-sin wave */ + break; + default: + slot->phaseshift = 0; + break; + } +} + +static void OPL3_SlotGeneratePhase(opl3_slot *slot, Bit16u phase) +{ + Bit32u neg, level; + Bit8u phaseshift; + + /* Fast paths for mute segments */ + if (phase & slot->maskzero) + { + slot->out = 0; + return; + } + + neg = (Bit32s)((Bit32u)phase << slot->signpos) >> 31; + phaseshift = slot->phaseshift; + level = slot->eg_out; + + phase <<= phaseshift; + if (phaseshift <= 1) + { + level += logsinrom[phase & 0x1ff]; + } + else + { + level += ((phase ^ neg) & 0x3ff) << 3; + } + slot->out = exprom[level & 0xff] >> (level >> 8) ^ neg; +} + +static void OPL3_SlotGenerate(opl3_slot *slot) +{ + OPL3_SlotGeneratePhase(slot, (Bit16u)(slot->pg_phase >> 9) + *slot->mod); +} + +static void OPL3_SlotGenerateZM(opl3_slot *slot) +{ + OPL3_SlotGeneratePhase(slot, (Bit16u)(slot->pg_phase >> 9)); +} + +static void OPL3_SlotCalcFB(opl3_slot *slot) +{ + if (slot->channel->fb != 0x00) + { + slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb); + } + else + { + slot->fbmod = 0; + } + slot->prout = slot->out; +} + +/* + * Channel + */ + +static void OPL3_ChannelSetupAlg(opl3_channel *channel); + +static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) +{ + opl3_channel *channel6; + opl3_channel *channel7; + opl3_channel *channel8; + Bit8u chnum; + + chip->rhy = data & 0x3f; + if (chip->rhy & 0x20) + { + channel6 = &chip->channel[6]; + channel7 = &chip->channel[7]; + channel8 = &chip->channel[8]; + channel6->out[0] = &channel6->slotz[1]->out; + channel6->out[1] = &channel6->slotz[1]->out; + channel6->out[2] = &chip->zeromod; + channel6->out[3] = &chip->zeromod; + channel7->out[0] = &channel7->slotz[0]->out; + channel7->out[1] = &channel7->slotz[0]->out; + channel7->out[2] = &channel7->slotz[1]->out; + channel7->out[3] = &channel7->slotz[1]->out; + channel8->out[0] = &channel8->slotz[0]->out; + channel8->out[1] = &channel8->slotz[0]->out; + channel8->out[2] = &channel8->slotz[1]->out; + channel8->out[3] = &channel8->slotz[1]->out; + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_drum; + } + OPL3_ChannelSetupAlg(channel6); + /*hh*/ + if (chip->rhy & 0x01) + { + OPL3_EnvelopeKeyOn(channel7->slotz[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slotz[0], egk_drum); + } + /*tc*/ + if (chip->rhy & 0x02) + { + OPL3_EnvelopeKeyOn(channel8->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slotz[1], egk_drum); + } + /*tom*/ + if (chip->rhy & 0x04) + { + OPL3_EnvelopeKeyOn(channel8->slotz[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slotz[0], egk_drum); + } + /*sd*/ + if (chip->rhy & 0x08) + { + OPL3_EnvelopeKeyOn(channel7->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slotz[1], egk_drum); + } + /*bd*/ + if (chip->rhy & 0x10) + { + OPL3_EnvelopeKeyOn(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOn(channel6->slotz[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(channel6->slotz[1], egk_drum); + } + } + else + { + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_2op; + OPL3_ChannelSetupAlg(&chip->channel[chnum]); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[1], egk_drum); + } + } +} + +static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0x300) | data; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->slotz[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[1]); + } +} + +static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8); + channel->block = (data >> 2) & 0x07; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->slotz[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateRate(channel->pair->slotz[1]); + } +} + +static void OPL3_ChannelSetupAlg(opl3_channel *channel) +{ + if (channel->chtype == ch_drum) + { + switch (channel->alg & 0x01) + { + case 0x00: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + break; + case 0x01: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + break; + } + return; + } + if (channel->alg & 0x08) + { + return; + } + if (channel->alg & 0x04) + { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) + { + case 0x00: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->chip->zeromod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[1]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x02: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x03: + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[0]->out; + channel->out[2] = &channel->slotz[1]->out; + channel->out[3] = &channel->chip->zeromod; + break; + } + } + else + { + switch (channel->alg & 0x01) + { + case 0x00: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + } + } +} + +static void OPL3_ChannelWriteC0(opl3_channel *channel, Bit8u data) +{ + channel->fb = (data & 0x0e) >> 1; + channel->con = data & 0x01; + channel->alg = channel->con; + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + OPL3_ChannelSetupAlg(channel->pair); + } + else if (channel->chtype == ch_4op2) + { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + OPL3_ChannelSetupAlg(channel); + } + else + { + OPL3_ChannelSetupAlg(channel); + } + } + else + { + OPL3_ChannelSetupAlg(channel); + } + if (channel->chip->newm) + { + channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; + channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; + } + else + { + channel->cha = channel->chb = ~0; + } +} + +static void OPL3_ChannelKeyOn(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + } +} + +static void OPL3_ChannelKeyOff(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + } +} + +static void OPL3_ChannelSet4Op(opl3_chip *chip, Bit8u data) +{ + Bit8u bit; + Bit8u chnum; + for (bit = 0; bit < 6; bit++) + { + chnum = bit; + if (bit >= 3) + { + chnum += 9 - 3; + } + if ((data >> bit) & 0x01) + { + chip->channel[chnum].chtype = ch_4op; + chip->channel[chnum + 3].chtype = ch_4op2; + } + else + { + chip->channel[chnum].chtype = ch_2op; + chip->channel[chnum + 3].chtype = ch_2op; + } + } +} + +static Bit16s OPL3_ClipSample(Bit32s sample) +{ + if (sample > 32767) + { + sample = 32767; + } + else if (sample < -32768) + { + sample = -32768; + } + return (Bit16s)sample; +} + +static void OPL3_GenerateRhythm1(opl3_chip *chip) +{ + opl3_channel *channel6; + opl3_channel *channel7; + opl3_channel *channel8; + Bit16u phase14; + Bit16u phase17; + Bit16u phase; + Bit16u phasebit; + + channel6 = &chip->channel[6]; + channel7 = &chip->channel[7]; + channel8 = &chip->channel[8]; + OPL3_SlotGenerate(channel6->slotz[0]); + phase14 = (channel7->slotz[0]->pg_phase >> 9) & 0x3ff; + phase17 = (channel8->slotz[1]->pg_phase >> 9) & 0x3ff; + phase = 0x00; + /*hh tc phase bit*/ + phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) + | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; + /*hh*/ + phase = (phasebit << 9) + | (0x34 << ((phasebit ^ (chip->noise & 0x01)) << 1)); + OPL3_SlotGeneratePhase(channel7->slotz[0], phase); + /*tt*/ + OPL3_SlotGenerateZM(channel8->slotz[0]); +} + +static void OPL3_GenerateRhythm2(opl3_chip *chip) +{ + opl3_channel *channel6; + opl3_channel *channel7; + opl3_channel *channel8; + Bit16u phase14; + Bit16u phase17; + Bit16u phase; + Bit16u phasebit; + + channel6 = &chip->channel[6]; + channel7 = &chip->channel[7]; + channel8 = &chip->channel[8]; + OPL3_SlotGenerate(channel6->slotz[1]); + phase14 = (channel7->slotz[0]->pg_phase >> 9) & 0x3ff; + phase17 = (channel8->slotz[1]->pg_phase >> 9) & 0x3ff; + phase = 0x00; + /*hh tc phase bit*/ + phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) + | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; + /*sd*/ + phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8); + OPL3_SlotGeneratePhase(channel7->slotz[1], phase); + /*tc*/ + phase = 0x100 | (phasebit << 9); + OPL3_SlotGeneratePhase(channel8->slotz[1], phase); +} + +void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf) +{ + Bit8u ii; + Bit8u jj; + Bit16s accm; + + buf[1] = OPL3_ClipSample(chip->mixbuff[1]); + + for (ii = 0; ii < 12; ii++) + { + OPL3_SlotCalcFB(&chip->chipslot[ii]); + OPL3_PhaseGenerate(&chip->chipslot[ii]); + OPL3_EnvelopeCalc(&chip->chipslot[ii]); + OPL3_SlotGenerate(&chip->chipslot[ii]); + } + + for (ii = 12; ii < 15; ii++) + { + OPL3_SlotCalcFB(&chip->chipslot[ii]); + OPL3_PhaseGenerate(&chip->chipslot[ii]); + OPL3_EnvelopeCalc(&chip->chipslot[ii]); + } + + if (chip->rhy & 0x20) + { + OPL3_GenerateRhythm1(chip); + } + else + { + OPL3_SlotGenerate(&chip->chipslot[12]); + OPL3_SlotGenerate(&chip->chipslot[13]); + OPL3_SlotGenerate(&chip->chipslot[14]); + } + + chip->mixbuff[0] = 0; + for (ii = 0; ii < 18; ii++) + { + accm = 0; + for (jj = 0; jj < 4; jj++) + { + accm += *chip->channel[ii].out[jj]; + } + chip->mixbuff[0] += (Bit16s)((accm * chip->channel[ii].chl / 65535) & chip->channel[ii].cha); + } + + for (ii = 15; ii < 18; ii++) + { + OPL3_SlotCalcFB(&chip->chipslot[ii]); + OPL3_PhaseGenerate(&chip->chipslot[ii]); + OPL3_EnvelopeCalc(&chip->chipslot[ii]); + } + + if (chip->rhy & 0x20) + { + OPL3_GenerateRhythm2(chip); + } + else + { + OPL3_SlotGenerate(&chip->chipslot[15]); + OPL3_SlotGenerate(&chip->chipslot[16]); + OPL3_SlotGenerate(&chip->chipslot[17]); + } + + buf[0] = OPL3_ClipSample(chip->mixbuff[0]); + + for (ii = 18; ii < 33; ii++) + { + OPL3_SlotCalcFB(&chip->chipslot[ii]); + OPL3_PhaseGenerate(&chip->chipslot[ii]); + OPL3_EnvelopeCalc(&chip->chipslot[ii]); + OPL3_SlotGenerate(&chip->chipslot[ii]); + } + + chip->mixbuff[1] = 0; + for (ii = 0; ii < 18; ii++) + { + accm = 0; + for (jj = 0; jj < 4; jj++) + { + accm += *chip->channel[ii].out[jj]; + } + chip->mixbuff[1] += (Bit16s)((accm * chip->channel[ii].chr / 65535) & chip->channel[ii].chb); + } + + for (ii = 33; ii < 36; ii++) + { + OPL3_SlotCalcFB(&chip->chipslot[ii]); + OPL3_PhaseGenerate(&chip->chipslot[ii]); + OPL3_EnvelopeCalc(&chip->chipslot[ii]); + OPL3_SlotGenerate(&chip->chipslot[ii]); + } + + OPL3_NoiseGenerate(chip); + + if ((chip->timer & 0x3f) == 0x3f) + { + chip->tremolopos = (chip->tremolopos + 1) % 210; + } + if (chip->tremolopos < 105) + { + chip->tremolo = chip->tremolopos >> chip->tremoloshift; + } + else + { + chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift; + } + + if ((chip->timer & 0x3ff) == 0x3ff) + { + chip->vibpos = (chip->vibpos + 1) & 7; + } + + chip->timer++; + + while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + { + if (!(chip->writebuf[chip->writebuf_cur].reg & 0x200)) + { + break; + } + chip->writebuf[chip->writebuf_cur].reg &= 0x1ff; + OPL3v17_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg, + chip->writebuf[chip->writebuf_cur].data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; +} + +void OPL3v17_GenerateResampled(opl3_chip *chip, Bit16s *buf) +{ + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPL3v17_Generate(chip, chip->samples); + chip->samplecnt -= chip->rateratio; + } + buf[0] = (Bit16s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio); + buf[1] = (Bit16s)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate) +{ + Bit8u slotnum; + Bit8u channum; + + memset(chip, 0, sizeof(opl3_chip)); + for (slotnum = 0; slotnum < 36; slotnum++) + { + chip->chipslot[slotnum].chip = chip; + chip->chipslot[slotnum].mod = &chip->zeromod; + chip->chipslot[slotnum].eg_rout = 0x1ff; + chip->chipslot[slotnum].eg_out = 0x1ff << 3; + chip->chipslot[slotnum].eg_gen = envelope_gen_num_off; + chip->chipslot[slotnum].trem = (Bit8u*)&chip->zeromod; + chip->chipslot[slotnum].signpos = (31-9); /* for wf=0 need use sigext of (phase & 0x200) */ + } + for (channum = 0; channum < 18; channum++) + { + chip->channel[channum].slotz[0] = &chip->chipslot[ch_slot[channum]]; + chip->channel[channum].slotz[1] = &chip->chipslot[ch_slot[channum] + 3]; + chip->chipslot[ch_slot[channum]].channel = &chip->channel[channum]; + chip->chipslot[ch_slot[channum] + 3].channel = &chip->channel[channum]; + if ((channum % 9) < 3) + { + chip->channel[channum].pair = &chip->channel[channum + 3]; + } + else if ((channum % 9) < 6) + { + chip->channel[channum].pair = &chip->channel[channum - 3]; + } + chip->channel[channum].chip = chip; + chip->channel[channum].out[0] = &chip->zeromod; + chip->channel[channum].out[1] = &chip->zeromod; + chip->channel[channum].out[2] = &chip->zeromod; + chip->channel[channum].out[3] = &chip->zeromod; + chip->channel[channum].chtype = ch_2op; + chip->channel[channum].cha = 0xffff; + chip->channel[channum].chb = 0xffff; + chip->channel[channum].chl = 46340; + chip->channel[channum].chr = 46340; + OPL3_ChannelSetupAlg(&chip->channel[channum]); + } + chip->noise = 0x306600; + chip->rateratio = (samplerate << RSM_FRAC) / 49716; + chip->tremoloshift = 4; + chip->vibshift = 1; +} + +static void OPL3v17_ChannelWritePan(opl3_channel *channel, Bit8u data) +{ + channel->chl = panlawtable[data & 0x7F]; + channel->chr = panlawtable[0x7F - (data & 0x7F)]; +} + +void OPL3v17_WritePan(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit8u high = (reg >> 8) & 0x01; + Bit8u regm = reg & 0xff; + OPL3v17_ChannelWritePan(&chip->channel[9 * high + (regm & 0x0f)], v); +} + +void OPL3v17_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit8u high = (reg >> 8) & 0x01; + Bit8u regm = reg & 0xff; + switch (regm & 0xf0) + { + case 0x00: + if (high) + { + switch (regm & 0x0f) + { + case 0x04: + OPL3_ChannelSet4Op(chip, v); + break; + case 0x05: + chip->newm = v & 0x01; + break; + } + } + else + { + switch (regm & 0x0f) + { + case 0x08: + chip->nts = (v >> 6) & 0x01; + break; + } + } + break; + case 0x20: + case 0x30: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite20(&chip->chipslot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x40: + case 0x50: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite40(&chip->chipslot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x60: + case 0x70: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite60(&chip->chipslot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x80: + case 0x90: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite80(&chip->chipslot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xe0: + case 0xf0: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWriteE0(&chip->chipslot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xa0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteA0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; + case 0xb0: + if (regm == 0xbd && !high) + { + chip->tremoloshift = (((v >> 7) ^ 1) << 1) + 2; + chip->vibshift = ((v >> 6) & 0x01) ^ 1; + OPL3_ChannelUpdateRhythm(chip, v); + } + else if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteB0(&chip->channel[9 * high + (regm & 0x0f)], v); + if (v & 0x20) + { + OPL3_ChannelKeyOn(&chip->channel[9 * high + (regm & 0x0f)]); + } + else + { + OPL3_ChannelKeyOff(&chip->channel[9 * high + (regm & 0x0f)]); + } + } + break; + case 0xc0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteC0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; + } +} + +void OPL3v17_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit64u time1, time2; + + if (chip->writebuf[chip->writebuf_last].reg & 0x200) + { + OPL3v17_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff, + chip->writebuf[chip->writebuf_last].data); + + chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; + chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + } + + chip->writebuf[chip->writebuf_last].reg = reg | 0x200; + chip->writebuf[chip->writebuf_last].data = v; + time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + chip->writebuf[chip->writebuf_last].time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; +} + +void OPL3v17_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +{ + Bit32u i; + + for(i = 0; i < numsamples; i++) + { + OPL3v17_GenerateResampled(chip, sndptr); + sndptr += 2; + } +} + +#define OPL3_MIN(A, B) (((A) > (B)) ? (B) : (A)) +#define OPL3_MAX(A, B) (((A) < (B)) ? (B) : (A)) +#define OPL3_CLAMP(V, MIN, MAX) OPL3_MAX(OPL3_MIN(V, MAX), MIN) + +void OPL3v17_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +{ + Bit32u i; + Bit16s sample[2]; + Bit32s mix[2]; + + for(i = 0; i < numsamples; i++) + { + OPL3v17_GenerateResampled(chip, sample); + mix[0] = sndptr[0] + sample[0]; + mix[1] = sndptr[1] + sample[1]; + sndptr[0] = OPL3_CLAMP(mix[0], INT16_MIN, INT16_MAX); + sndptr[1] = OPL3_CLAMP(mix[1], INT16_MIN, INT16_MAX); + sndptr += 2; + } +} + diff --git a/thirdparty/adlmidi/chips/nuked/nukedopl3_174.h b/thirdparty/adlmidi/chips/nuked/nukedopl3_174.h new file mode 100644 index 000000000..cd185620c --- /dev/null +++ b/thirdparty/adlmidi/chips/nuked/nukedopl3_174.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT) + * + * This library 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 library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.7.4 + */ + +#ifndef OPL_OPL3_H +#define OPL_OPL3_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define OPL_WRITEBUF_SIZE 1024 +#define OPL_WRITEBUF_DELAY 2 + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +typedef struct _opl3_slot opl3_slot; +typedef struct _opl3_channel opl3_channel; +typedef struct _opl3_chip opl3_chip; + +struct _opl3_slot { + opl3_channel *channel; + opl3_chip *chip; + Bit16s out; + Bit16s fbmod; + Bit16s *mod; + Bit16s prout; + Bit16s eg_rout; + Bit16s eg_out; + Bit8u eg_inc; + Bit8u eg_gen; + Bit8u eg_rate; + Bit8u eg_ksl; + Bit8u *trem; + Bit8u reg_vib; + Bit8u reg_type; + Bit8u reg_ksr; + Bit8u reg_mult; + Bit8u reg_ksl; + Bit8u reg_tl; + Bit8u reg_ar; + Bit8u reg_dr; + Bit8u reg_sl; + Bit8u reg_rr; + Bit8u reg_wf; + Bit8u key; + Bit32u pg_phase; + Bit32u timer; + + Bit16u maskzero; + Bit8u signpos; + Bit8u phaseshift; +}; + +struct _opl3_channel { + opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/ + opl3_channel *pair; + opl3_chip *chip; + Bit16s *out[4]; + Bit8u chtype; + Bit16u f_num; + Bit8u block; + Bit8u fb; + Bit8u con; + Bit8u alg; + Bit8u ksv; + Bit16u cha, chb; + Bit16u chl, chr; +}; + +typedef struct _opl3_writebuf { + Bit64u time; + Bit16u reg; + Bit8u data; +} opl3_writebuf; + +struct _opl3_chip { + opl3_channel channel[18]; + opl3_slot chipslot[36]; + Bit16u timer; + Bit8u newm; + Bit8u nts; + Bit8u rhy; + Bit8u vibpos; + Bit8u vibshift; + Bit8u tremolo; + Bit8u tremolopos; + Bit8u tremoloshift; + Bit32u noise; + Bit16s zeromod; + Bit32s mixbuff[2]; + /* OPL3L */ + Bit32s rateratio; + Bit32s samplecnt; + Bit16s oldsamples[2]; + Bit16s samples[2]; + + Bit64u writebuf_samplecnt; + Bit32u writebuf_cur; + Bit32u writebuf_last; + Bit64u writebuf_lasttime; + opl3_writebuf writebuf[OPL_WRITEBUF_SIZE]; +}; + +void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf); +void OPL3v17_GenerateResampled(opl3_chip *chip, Bit16s *buf); +void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate); +void OPL3v17_WritePan(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3v17_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3v17_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3v17_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); +void OPL3v17_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/adlmidi/chips/nuked_opl3.cpp b/thirdparty/adlmidi/chips/nuked_opl3.cpp new file mode 100644 index 000000000..2c15fac4c --- /dev/null +++ b/thirdparty/adlmidi/chips/nuked_opl3.cpp @@ -0,0 +1,85 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nuked_opl3.h" +#include "nuked/nukedopl3.h" +#include + +NukedOPL3::NukedOPL3() : + OPLChipBaseT() +{ + m_chip = new opl3_chip; + NukedOPL3::setRate(m_rate); +} + +NukedOPL3::~NukedOPL3() +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void NukedOPL3::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3_Reset(chip_r, rate); +} + +void NukedOPL3::reset() +{ + OPLChipBaseT::reset(); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3_Reset(chip_r, m_rate); +} + +void NukedOPL3::writeReg(uint16_t addr, uint8_t data) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3_WriteRegBuffered(chip_r, addr, data); +} + +void NukedOPL3::writePan(uint16_t addr, uint8_t data) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3_WritePan(chip_r, addr, data); +} + +void NukedOPL3::nativeGenerate(int16_t *frame) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3_Generate(chip_r, frame); +} + +const char *NukedOPL3::emulatorName() +{ + return "Nuked OPL3 (v 1.8)"; +} + +bool NukedOPL3::hasFullPanning() +{ + return true; +} + +OPLChipBase::ChipType NukedOPL3::chipType() +{ + return CHIPTYPE_OPL3; +} diff --git a/thirdparty/adlmidi/chips/nuked_opl3.h b/thirdparty/adlmidi/chips/nuked_opl3.h new file mode 100644 index 000000000..613e5b569 --- /dev/null +++ b/thirdparty/adlmidi/chips/nuked_opl3.h @@ -0,0 +1,46 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NUKED_OPL3_H +#define NUKED_OPL3_H + +#include "opl_chip_base.h" + +class NukedOPL3 final : public OPLChipBaseT +{ + void *m_chip; +public: + NukedOPL3(); + ~NukedOPL3() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // NUKED_OPL3_H diff --git a/thirdparty/adlmidi/chips/nuked_opl3_v174.cpp b/thirdparty/adlmidi/chips/nuked_opl3_v174.cpp new file mode 100644 index 000000000..e967f7976 --- /dev/null +++ b/thirdparty/adlmidi/chips/nuked_opl3_v174.cpp @@ -0,0 +1,85 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nuked_opl3_v174.h" +#include "nuked/nukedopl3_174.h" +#include + +NukedOPL3v174::NukedOPL3v174() : + OPLChipBaseT() +{ + m_chip = new opl3_chip; + NukedOPL3v174::setRate(m_rate); +} + +NukedOPL3v174::~NukedOPL3v174() +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void NukedOPL3v174::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3v17_Reset(chip_r, rate); +} + +void NukedOPL3v174::reset() +{ + OPLChipBaseT::reset(); + opl3_chip *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(opl3_chip)); + OPL3v17_Reset(chip_r, m_rate); +} + +void NukedOPL3v174::writeReg(uint16_t addr, uint8_t data) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3v17_WriteReg(chip_r, addr, data); +} + +void NukedOPL3v174::writePan(uint16_t addr, uint8_t data) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3v17_WritePan(chip_r, addr, data); +} + +void NukedOPL3v174::nativeGenerate(int16_t *frame) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3v17_Generate(chip_r, frame); +} + +const char *NukedOPL3v174::emulatorName() +{ + return "Nuked OPL3 (v 1.7.4)"; +} + +bool NukedOPL3v174::hasFullPanning() +{ + return true; +} + +OPLChipBase::ChipType NukedOPL3v174::chipType() +{ + return CHIPTYPE_OPL3; +} diff --git a/thirdparty/adlmidi/chips/nuked_opl3_v174.h b/thirdparty/adlmidi/chips/nuked_opl3_v174.h new file mode 100644 index 000000000..84f6c99f7 --- /dev/null +++ b/thirdparty/adlmidi/chips/nuked_opl3_v174.h @@ -0,0 +1,46 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NUKED_OPL3174_H +#define NUKED_OPL3174_H + +#include "opl_chip_base.h" + +class NukedOPL3v174 final : public OPLChipBaseT +{ + void *m_chip; +public: + NukedOPL3v174(); + ~NukedOPL3v174() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // NUKED_OPL3174_H diff --git a/thirdparty/adlmidi/chips/opal/LICENSE.txt b/thirdparty/adlmidi/chips/opal/LICENSE.txt new file mode 100644 index 000000000..326b10498 --- /dev/null +++ b/thirdparty/adlmidi/chips/opal/LICENSE.txt @@ -0,0 +1,19 @@ +A quote from a letter sent by Willy Reeve to Vitaly Novichkov +as a reply from a letter sent by Reality's (https://www.3eality.com/) +contact form +---------------------------------------------------------------- +All source code supplied with RAD is in the public domain. +---------------------------------------------------------------- + +================================================================ +The complete letter: +================================================================ +Hi, + +All source code supplied with RAD is in the public domain. +We'll clarify that in the documentation for the next release. + +Later... +SHAYDE +================================================================ + diff --git a/thirdparty/adlmidi/chips/opal/old/LICENSE.txt b/thirdparty/adlmidi/chips/opal/old/LICENSE.txt new file mode 100644 index 000000000..326b10498 --- /dev/null +++ b/thirdparty/adlmidi/chips/opal/old/LICENSE.txt @@ -0,0 +1,19 @@ +A quote from a letter sent by Willy Reeve to Vitaly Novichkov +as a reply from a letter sent by Reality's (https://www.3eality.com/) +contact form +---------------------------------------------------------------- +All source code supplied with RAD is in the public domain. +---------------------------------------------------------------- + +================================================================ +The complete letter: +================================================================ +Hi, + +All source code supplied with RAD is in the public domain. +We'll clarify that in the documentation for the next release. + +Later... +SHAYDE +================================================================ + diff --git a/thirdparty/adlmidi/chips/opal/old/README.old b/thirdparty/adlmidi/chips/opal/old/README.old new file mode 100644 index 000000000..902dee5ab --- /dev/null +++ b/thirdparty/adlmidi/chips/opal/old/README.old @@ -0,0 +1,9 @@ +Copy opal.cpp here to make this emulator available. +This is not distributed with the software while the license is undetermined. + +The opal.cpp file is part of REALITY ADLIB TRACKER V2. +SHA1: c034dd637887d81770cee92da309fc547ef5fe18 + +Configure the software with the option USE_OPAL_EMULATOR=ON. +To enable soft panning, it is needed to patch with the file `opal-pan.diff` provided. +for example: `patch -p0 -i opal-pan.diff` diff --git a/thirdparty/adlmidi/chips/opal/old/opal-orig.hpp b/thirdparty/adlmidi/chips/opal/old/opal-orig.hpp new file mode 100644 index 000000000..e8110a51d --- /dev/null +++ b/thirdparty/adlmidi/chips/opal/old/opal-orig.hpp @@ -0,0 +1,1398 @@ +/* + + The Opal OPL3 emulator. + + Note: this is not a complete emulator, just enough for Reality Adlib Tracker tunes. + + Missing features compared to a real OPL3: + + - Timers/interrupts + - OPL3 enable bit (it defaults to always on) + - CSW mode + - Test register + - Percussion mode + +*/ +#define OPAL_HAVE_SOFT_PANNING 1 /* libADLMIDI */ + + + +#include + + + +//================================================================================================== +// Opal class. +//================================================================================================== +class Opal { + + class Channel; + + // Various constants + enum { + OPL3SampleRate = 49716, + NumChannels = 18, + NumOperators = 36, + + EnvOff = -1, + EnvAtt, + EnvDec, + EnvSus, + EnvRel + }; + + // A single FM operator + class Operator { + + public: + Operator(); + void SetMaster(Opal *opal) { Master = opal; } + void SetChannel(Channel *chan) { Chan = chan; } + + int16_t Output(uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod = 0, int16_t fbshift = 0); + + void SetKeyOn(bool on); + void SetTremoloEnable(bool on); + void SetVibratoEnable(bool on); + void SetSustainMode(bool on); + void SetEnvelopeScaling(bool on); + void SetFrequencyMultiplier(uint16_t scale); + void SetKeyScale(uint16_t scale); + void SetOutputLevel(uint16_t level); + void SetAttackRate(uint16_t rate); + void SetDecayRate(uint16_t rate); + void SetSustainLevel(uint16_t level); + void SetReleaseRate(uint16_t rate); + void SetWaveform(uint16_t wave); + + void ComputeRates(); + void ComputeKeyScaleLevel(); + + protected: + Opal * Master; // Master object + Channel * Chan; // Owning channel + uint32_t Phase; // The current offset in the selected waveform + uint16_t Waveform; // The waveform id this operator is using + uint16_t FreqMultTimes2; // Frequency multiplier * 2 + int EnvelopeStage; // Which stage the envelope is at (see Env* enums above) + int16_t EnvelopeLevel; // 0 - $1FF, 0 being the loudest + uint16_t OutputLevel; // 0 - $FF + uint16_t AttackRate; + uint16_t DecayRate; + uint16_t SustainLevel; + uint16_t ReleaseRate; + uint16_t AttackShift; + uint16_t AttackMask; + uint16_t AttackAdd; + const uint16_t *AttackTab; + uint16_t DecayShift; + uint16_t DecayMask; + uint16_t DecayAdd; + const uint16_t *DecayTab; + uint16_t ReleaseShift; + uint16_t ReleaseMask; + uint16_t ReleaseAdd; + const uint16_t *ReleaseTab; + uint16_t KeyScaleShift; + uint16_t KeyScaleLevel; + int16_t Out[2]; + bool KeyOn; + bool KeyScaleRate; // Affects envelope rate scaling + bool SustainMode; // Whether to sustain during the sustain phase, or release instead + bool TremoloEnable; + bool VibratoEnable; + }; + + // A single channel, which can contain two or more operators + class Channel { + + public: + Channel(); + void SetMaster(Opal *opal) { Master = opal; } + void SetOperators(Operator *a, Operator *b, Operator *c, Operator *d) { + Op[0] = a; + Op[1] = b; + Op[2] = c; + Op[3] = d; + if (a) + a->SetChannel(this); + if (b) + b->SetChannel(this); + if (c) + c->SetChannel(this); + if (d) + d->SetChannel(this); + } + + void Output(int16_t &left, int16_t &right); + void SetEnable(bool on) { Enable = on; } + void SetChannelPair(Channel *pair) { ChannelPair = pair; } + + void SetFrequencyLow(uint16_t freq); + void SetFrequencyHigh(uint16_t freq); + void SetKeyOn(bool on); + void SetOctave(uint16_t oct); + void SetLeftEnable(bool on); + void SetRightEnable(bool on); + void SetPan(uint8_t pan); + void SetFeedback(uint16_t val); + void SetModulationType(uint16_t type); + + uint16_t GetFreq() const { return Freq; } + uint16_t GetOctave() const { return Octave; } + uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; } + uint16_t GetModulationType() const { return ModulationType; } + Channel * GetChannelPair() const { return ChannelPair; } /* libADLMIDI */ + + void ComputeKeyScaleNumber(); + + protected: + void ComputePhaseStep(); + + Operator * Op[4]; + + Opal * Master; // Master object + uint16_t Freq; // Frequency; actually it's a phase stepping value + uint16_t Octave; // Also known as "block" in Yamaha parlance + uint32_t PhaseStep; + uint16_t KeyScaleNumber; + uint16_t FeedbackShift; + uint16_t ModulationType; + Channel * ChannelPair; + bool Enable; + bool LeftEnable, RightEnable; + uint16_t LeftPan, RightPan; + }; + + public: + Opal(int sample_rate); + ~Opal(); + + void SetSampleRate(int sample_rate); + void Port(uint16_t reg_num, uint8_t val); + void Pan(uint16_t reg_num, uint8_t pan); + void Sample(int16_t *left, int16_t *right); + + protected: + void Init(int sample_rate); + void Output(int16_t &left, int16_t &right); + + int32_t SampleRate; + int32_t SampleAccum; + int16_t LastOutput[2], CurrOutput[2]; + Channel Chan[NumChannels]; + Operator Op[NumOperators]; +// uint16_t ExpTable[256]; +// uint16_t LogSinTable[256]; + uint16_t Clock; + uint16_t TremoloClock; + uint16_t TremoloLevel; + uint16_t VibratoTick; + uint16_t VibratoClock; + bool NoteSel; + bool TremoloDepth; + bool VibratoDepth; + + static const uint16_t RateTables[4][8]; + static const uint16_t ExpTable[256]; + static const uint16_t LogSinTable[256]; + static const uint16_t PanLawTable[128]; +}; +//-------------------------------------------------------------------------------------------------- +const uint16_t Opal::RateTables[4][8] = { + { 1, 0, 1, 0, 1, 0, 1, 0 }, + { 1, 0, 1, 0, 0, 0, 1, 0 }, + { 1, 0, 0, 0, 1, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, 0, 0 }, +}; +//-------------------------------------------------------------------------------------------------- +const uint16_t Opal::ExpTable[0x100] = { + 1018, 1013, 1007, 1002, 996, 991, 986, 980, 975, 969, 964, 959, 953, 948, 942, 937, + 932, 927, 921, 916, 911, 906, 900, 895, 890, 885, 880, 874, 869, 864, 859, 854, + 849, 844, 839, 834, 829, 824, 819, 814, 809, 804, 799, 794, 789, 784, 779, 774, + 770, 765, 760, 755, 750, 745, 741, 736, 731, 726, 722, 717, 712, 708, 703, 698, + 693, 689, 684, 680, 675, 670, 666, 661, 657, 652, 648, 643, 639, 634, 630, 625, + 621, 616, 612, 607, 603, 599, 594, 590, 585, 581, 577, 572, 568, 564, 560, 555, + 551, 547, 542, 538, 534, 530, 526, 521, 517, 513, 509, 505, 501, 496, 492, 488, + 484, 480, 476, 472, 468, 464, 460, 456, 452, 448, 444, 440, 436, 432, 428, 424, + 420, 416, 412, 409, 405, 401, 397, 393, 389, 385, 382, 378, 374, 370, 367, 363, + 359, 355, 352, 348, 344, 340, 337, 333, 329, 326, 322, 318, 315, 311, 308, 304, + 300, 297, 293, 290, 286, 283, 279, 276, 272, 268, 265, 262, 258, 255, 251, 248, + 244, 241, 237, 234, 231, 227, 224, 220, 217, 214, 210, 207, 204, 200, 197, 194, + 190, 187, 184, 181, 177, 174, 171, 168, 164, 161, 158, 155, 152, 148, 145, 142, + 139, 136, 133, 130, 126, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93, + 90, 87, 84, 81, 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45, + 42, 40, 37, 34, 31, 28, 25, 22, 20, 17, 14, 11, 8, 6, 3, 0, +}; +//-------------------------------------------------------------------------------------------------- +const uint16_t Opal::LogSinTable[0x100] = { + 2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, + 846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, + 598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, + 453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, + 352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, + 276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, + 215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, + 167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, + 127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, + 94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, + 67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, + 46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, + 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, + 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, +}; +//-------------------------------------------------------------------------------------------------- +const uint16_t Opal::PanLawTable[128] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, // Center left + 46340, // Center right + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + + + +//================================================================================================== +// This is the temporary code for generating the above tables. Maths and data from this nice +// reverse-engineering effort: +// +// https://docs.google.com/document/d/18IGx18NQY_Q1PJVZ-bHywao9bhsDoAqoIn1rIm42nwo/edit +//================================================================================================== +#if 0 +#include + +void GenerateTables() { + + // Build the exponentiation table (reversed from the official OPL3 ROM) + FILE *fd = fopen("exptab.txt", "wb"); + if (fd) { + for (int i = 0; i < 0x100; i++) { + int v = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; + if (i & 15) + fprintf(fd, " %4d,", v); + else + fprintf(fd, "\n\t%4d,", v); + } + fclose(fd); + } + + // Build the log-sin table + fd = fopen("sintab.txt", "wb"); + if (fd) { + for (int i = 0; i < 0x100; i++) { + int v = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; + if (i & 15) + fprintf(fd, " %4d,", v); + else + fprintf(fd, "\n\t%4d,", v); + } + fclose(fd); + } +} +#endif + + + +//================================================================================================== +// Constructor/destructor. +//================================================================================================== +Opal::Opal(int sample_rate) { + + Init(sample_rate); +} +//-------------------------------------------------------------------------------------------------- +Opal::~Opal() { +} + + + +//================================================================================================== +// Initialise the emulation. +//================================================================================================== +void Opal::Init(int sample_rate) { + + Clock = 0; + TremoloClock = 0; + TremoloLevel = 0; + VibratoTick = 0; + VibratoClock = 0; + NoteSel = false; + TremoloDepth = false; + VibratoDepth = false; + +// // Build the exponentiation table (reversed from the official OPL3 ROM) +// for (int i = 0; i < 0x100; i++) +// ExpTable[i] = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; +// +// // Build the log-sin table +// for (int i = 0; i < 0x100; i++) +// LogSinTable[i] = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; + + // Let sub-objects know where to find us + for (int i = 0; i < NumOperators; i++) + Op[i].SetMaster(this); + + for (int i = 0; i < NumChannels; i++) + Chan[i].SetMaster(this); + + // Add the operators to the channels. Note, some channels can't use all the operators + // FIXME: put this into a separate routine + const int chan_ops[] = { + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32, + }; + + for (int i = 0; i < NumChannels; i++) { + Channel *chan = &Chan[i]; + int op = chan_ops[i]; + if (i < 3 || (i >= 9 && i < 12)) + chan->SetOperators(&Op[op], &Op[op + 3], &Op[op + 6], &Op[op + 9]); + else + chan->SetOperators(&Op[op], &Op[op + 3], 0, 0); + } + + // Initialise the operator rate data. We can't do this in the Operator constructor as it + // relies on referencing the master and channel objects + for (int i = 0; i < NumOperators; i++) + Op[i].ComputeRates(); + + // Initialise channel panning at center. + for (int i = 0; i < NumChannels; i++) { + Chan[i].SetPan(64); + Chan[i].SetLeftEnable(true); + Chan[i].SetRightEnable(true); + } + + SetSampleRate(sample_rate); +} + + + +//================================================================================================== +// Change the sample rate. +//================================================================================================== +void Opal::SetSampleRate(int sample_rate) { + + // Sanity + if (sample_rate == 0) + sample_rate = OPL3SampleRate; + + SampleRate = sample_rate; + SampleAccum = 0; + LastOutput[0] = LastOutput[1] = 0; + CurrOutput[0] = CurrOutput[1] = 0; +} + + + +//================================================================================================== +// Write a value to an OPL3 register. +//================================================================================================== +void Opal::Port(uint16_t reg_num, uint8_t val) { + + const int op_lookup[] = { + // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + // 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + uint16_t type = reg_num & 0xE0; + + // Is it BD, the one-off register stuck in the middle of the register array? + if (reg_num == 0xBD) { + TremoloDepth = (val & 0x80) != 0; + VibratoDepth = (val & 0x40) != 0; + return; + } + + // Global registers + if (type == 0x00) { + + // 4-OP enables + if (reg_num == 0x104) { + + // Enable/disable channels based on which 4-op enables + uint8_t mask = 1; + for (int i = 0; i < 6; i++, mask <<= 1) { + + // The 4-op channels are 0, 1, 2, 9, 10, 11 + uint16_t chan = i < 3 ? i : i + 6; + Channel *primary = &Chan[chan]; + Channel *secondary = &Chan[chan + 3]; + + if (val & mask) { + + // Let primary channel know it's controlling the secondary channel + primary->SetChannelPair(secondary); + + // Turn off the second channel in the pair + secondary->SetEnable(false); + + } else { + + // Let primary channel know it's no longer controlling the secondary channel + primary->SetChannelPair(0); + + // Turn on the second channel in the pair + secondary->SetEnable(true); + } + } + + // CSW / Note-sel + } else if (reg_num == 0x08) { + + NoteSel = (val & 0x40) != 0; + + // Get the channels to recompute the Key Scale No. as this varies based on NoteSel + for (int i = 0; i < NumChannels; i++) + Chan[i].ComputeKeyScaleNumber(); + } + + // Channel registers + } else if (type >= 0xA0 && type <= 0xC0) { + + // Convert to channel number + int chan_num = reg_num & 15; + + // Valid channel? + if (chan_num >= 9) + return; + + // Is it the other bank of channels? + if (reg_num & 0x100) + chan_num += 9; + + Channel &chan = Chan[chan_num]; + + /* libADLMIDI: registers Ax and Cx affect both channels */ + Channel *chans[2] = {&chan, chan.GetChannelPair()}; + unsigned numchans = chans[1] ? 2 : 1; + + // Do specific registers + switch (reg_num & 0xF0) { + + // Frequency low + case 0xA0: { + for (unsigned i = 0; i < numchans; ++i) { /* libADLMIDI */ + chans[i]->SetFrequencyLow(val); + } + break; + } + + // Key-on / Octave / Frequency High + case 0xB0: { + for (unsigned i = 0; i < numchans; ++i) { /* libADLMIDI */ + chans[i]->SetKeyOn((val & 0x20) != 0); + chans[i]->SetOctave(val >> 2 & 7); + chans[i]->SetFrequencyHigh(val & 3); + } + break; + } + + // Right Stereo Channel Enable / Left Stereo Channel Enable / Feedback Factor / Modulation Type + case 0xC0: { + chan.SetRightEnable((val & 0x20) != 0); + chan.SetLeftEnable((val & 0x10) != 0); + chan.SetFeedback(val >> 1 & 7); + chan.SetModulationType(val & 1); + break; + } + } + + // Operator registers + } else if ((type >= 0x20 && type <= 0x80) || type == 0xE0) { + + // Convert to operator number + int op_num = op_lookup[reg_num & 0x1F]; + + // Valid register? + if (op_num < 0) + return; + + // Is it the other bank of operators? + if (reg_num & 0x100) + op_num += 18; + + Operator &op = Op[op_num]; + + // Do specific registers + switch (type) { + + // Tremolo Enable / Vibrato Enable / Sustain Mode / Envelope Scaling / Frequency Multiplier + case 0x20: { + op.SetTremoloEnable((val & 0x80) != 0); + op.SetVibratoEnable((val & 0x40) != 0); + op.SetSustainMode((val & 0x20) != 0); + op.SetEnvelopeScaling((val & 0x10) != 0); + op.SetFrequencyMultiplier(val & 15); + break; + } + + // Key Scale / Output Level + case 0x40: { + op.SetKeyScale(val >> 6); + op.SetOutputLevel(val & 0x3F); + break; + } + + // Attack Rate / Decay Rate + case 0x60: { + op.SetAttackRate(val >> 4); + op.SetDecayRate(val & 15); + break; + } + + // Sustain Level / Release Rate + case 0x80: { + op.SetSustainLevel(val >> 4); + op.SetReleaseRate(val & 15); + break; + } + + // Waveform + case 0xE0: { + op.SetWaveform(val & 7); + break; + } + } + } +} + + + +//================================================================================================== +// Set panning on the channel designated by the register number. +// This is extended functionality. +//================================================================================================== +void Opal::Pan(uint16_t reg_num, uint8_t pan) +{ + uint8_t high = (reg_num >> 8) & 1; + uint8_t regm = reg_num & 0xff; + Chan[9 * high + (regm & 0x0f)].SetPan(pan); +} + + + +//================================================================================================== +// Generate sample. Every time you call this you will get two signed 16-bit samples (one for each +// stereo channel) which will sound correct when played back at the sample rate given when the +// class was constructed. +//================================================================================================== +void Opal::Sample(int16_t *left, int16_t *right) { + + // If the destination sample rate is higher than the OPL3 sample rate, we need to skip ahead + while (SampleAccum >= SampleRate) { + + LastOutput[0] = CurrOutput[0]; + LastOutput[1] = CurrOutput[1]; + + Output(CurrOutput[0], CurrOutput[1]); + + SampleAccum -= SampleRate; + } + + // Mix with the partial accumulation + int32_t omblend = SampleRate - SampleAccum; + *left = (LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate; + *right = (LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate; + + SampleAccum += OPL3SampleRate; +} + + + +//================================================================================================== +// Produce final output from the chip. This is at the OPL3 sample-rate. +//================================================================================================== +void Opal::Output(int16_t &left, int16_t &right) { + + int32_t leftmix = 0, rightmix = 0; + + // Sum the output of each channel + for (int i = 0; i < NumChannels; i++) { + + int16_t chanleft, chanright; + Chan[i].Output(chanleft, chanright); + + leftmix += chanleft; + rightmix += chanright; + } + + // Clamp + if (leftmix < -0x8000) + left = -0x8000; + else if (leftmix > 0x7FFF) + left = 0x7FFF; + else + left = leftmix; + + if (rightmix < -0x8000) + right = -0x8000; + else if (rightmix > 0x7FFF) + right = 0x7FFF; + else + right = rightmix; + + Clock++; + + // Tremolo. According to this post, the OPL3 tremolo is a 13,440 sample length triangle wave + // with a peak at 26 and a trough at 0 and is simply added to the logarithmic level accumulator + // http://forums.submarine.org.uk/phpBB/viewtopic.php?f=9&t=1171 + TremoloClock = (TremoloClock + 1) % 13440; + TremoloLevel = ((TremoloClock < 13440 / 2) ? TremoloClock : 13440 - TremoloClock) / 256; + if (!TremoloDepth) + TremoloLevel >>= 2; + + // Vibrato. This appears to be a 8 sample long triangle wave with a magnitude of the three + // high bits of the channel frequency, positive and negative, divided by two if the vibrato + // depth is zero. It is only cycled every 1,024 samples. + VibratoTick++; + if (VibratoTick >= 1024) { + VibratoTick = 0; + VibratoClock = (VibratoClock + 1) & 7; + } +} + + + +//================================================================================================== +// Channel constructor. +//================================================================================================== +Opal::Channel::Channel() { + + Master = 0; + Freq = 0; + Octave = 0; + PhaseStep = 0; + KeyScaleNumber = 0; + FeedbackShift = 0; + ModulationType = 0; + ChannelPair = 0; + Enable = true; +} + + + +//================================================================================================== +// Produce output from channel. +//================================================================================================== +void Opal::Channel::Output(int16_t &left, int16_t &right) { + + // Has the channel been disabled? This is usually a result of the 4-op enables being used to + // disable the secondary channel in each 4-op pair + if (!Enable) { + left = right = 0; + return; + } + + int16_t vibrato = (Freq >> 7) & 7; + if (!Master->VibratoDepth) + vibrato >>= 1; + + // 0 3 7 3 0 -3 -7 -3 + uint16_t clk = Master->VibratoClock; + if (!(clk & 3)) + vibrato = 0; // Position 0 and 4 is zero + else { + if (clk & 1) + vibrato >>= 1; // Odd positions are half the magnitude + if (clk & 4) + vibrato = -vibrato; // The second half positions are negative + } + + vibrato <<= Octave; + + // Combine individual operator outputs + int16_t out, acc; + + // Running in 4-op mode? + if (ChannelPair) { + + // Get the secondary channel's modulation type. This is the only thing from the secondary + // channel that is used + if (ChannelPair->GetModulationType() == 0) { + + if (ModulationType == 0) { + + // feedback -> modulator -> modulator -> modulator -> carrier + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + out = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + out = Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + + } else { + + // (feedback -> carrier) + (modulator -> modulator -> carrier) + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + } + + } else { + + if (ModulationType == 0) { + + // (feedback -> modulator -> carrier) + (modulator -> carrier) + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + + } else { + + // (feedback -> carrier) + (modulator -> carrier) + carrier + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + out += Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + } + } + + } else { + + // Standard 2-op mode + if (ModulationType == 0) { + + // Frequency modulation (well, phase modulation technically) + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + + } else { + + // Additive + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out += Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato); + } + } + + left = LeftEnable ? out : 0; + right = RightEnable ? out : 0; + + left = left * LeftPan / 65536; + right = right * RightPan / 65536; +} + + + +//================================================================================================== +// Set phase step for operators using this channel. +//================================================================================================== +void Opal::Channel::SetFrequencyLow(uint16_t freq) { + + Freq = (Freq & 0x300) | (freq & 0xFF); + ComputePhaseStep(); +} +//-------------------------------------------------------------------------------------------------- +void Opal::Channel::SetFrequencyHigh(uint16_t freq) { + + Freq = (Freq & 0xFF) | ((freq & 3) << 8); + ComputePhaseStep(); + + // Only the high bits of Freq affect the Key Scale No. + ComputeKeyScaleNumber(); +} + + + +//================================================================================================== +// Set the octave of the channel (0 to 7). +//================================================================================================== +void Opal::Channel::SetOctave(uint16_t oct) { + + Octave = oct & 7; + ComputePhaseStep(); + ComputeKeyScaleNumber(); +} + + + +//================================================================================================== +// Keys the channel on/off. +//================================================================================================== +void Opal::Channel::SetKeyOn(bool on) { + + Op[0]->SetKeyOn(on); + Op[1]->SetKeyOn(on); +} + + + +//================================================================================================== +// Enable left stereo channel. +//================================================================================================== +void Opal::Channel::SetLeftEnable(bool on) { + + LeftEnable = on; +} + + + +//================================================================================================== +// Enable right stereo channel. +//================================================================================================== +void Opal::Channel::SetRightEnable(bool on) { + + RightEnable = on; +} + + + +//================================================================================================== +// Pan the channel to the position given. +//================================================================================================== +void Opal::Channel::SetPan(uint8_t pan) +{ + pan &= 127; + LeftPan = PanLawTable[pan]; + RightPan = PanLawTable[127 - pan]; +} + + + +//================================================================================================== +// Set the channel feedback amount. +//================================================================================================== +void Opal::Channel::SetFeedback(uint16_t val) { + + FeedbackShift = val ? 9 - val : 0; +} + + + +//================================================================================================== +// Set frequency modulation/additive modulation +//================================================================================================== +void Opal::Channel::SetModulationType(uint16_t type) { + + ModulationType = type; +} + + + +//================================================================================================== +// Compute the stepping factor for the operator waveform phase based on the frequency and octave +// values of the channel. +//================================================================================================== +void Opal::Channel::ComputePhaseStep() { + + PhaseStep = uint32_t(Freq) << Octave; +} + + + +//================================================================================================== +// Compute the key scale number and key scale levels. +// +// From the Yamaha data sheet this is the block/octave number as bits 3-1, with bit 0 coming from +// the MSB of the frequency if NoteSel is 1, and the 2nd MSB if NoteSel is 0. +//================================================================================================== +void Opal::Channel::ComputeKeyScaleNumber() { + + uint16_t lsb = Master->NoteSel ? Freq >> 9 : (Freq >> 8) & 1; + KeyScaleNumber = Octave << 1 | lsb; + + // Get the channel operators to recompute their rates as they're dependent on this number. They + // also need to recompute their key scale level + for (int i = 0; i < 4; i++) { + + if (!Op[i]) + continue; + + Op[i]->ComputeRates(); + Op[i]->ComputeKeyScaleLevel(); + } +} + + + +//================================================================================================== +// Operator constructor. +//================================================================================================== +Opal::Operator::Operator() { + + Master = 0; + Chan = 0; + Phase = 0; + Waveform = 0; + FreqMultTimes2 = 1; + EnvelopeStage = EnvOff; + EnvelopeLevel = 0x1FF; + AttackRate = 0; + DecayRate = 0; + SustainLevel = 0; + ReleaseRate = 0; + KeyScaleShift = 0; + KeyScaleLevel = 0; + Out[0] = Out[1] = 0; + KeyOn = false; + KeyScaleRate = false; + SustainMode = false; + TremoloEnable = false; + VibratoEnable = false; +} + + + +//================================================================================================== +// Produce output from operator. +//================================================================================================== +int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, int16_t vibrato, int16_t mod, int16_t fbshift) { + + // Advance wave phase + if (VibratoEnable) + phase_step += vibrato; + Phase += (phase_step * FreqMultTimes2) / 2; + + uint16_t level = (EnvelopeLevel + OutputLevel + KeyScaleLevel + (TremoloEnable ? Master->TremoloLevel : 0)) << 3; + + switch (EnvelopeStage) { + + // Attack stage + case EnvAtt: { + uint16_t add = ((AttackAdd >> AttackTab[Master->Clock >> AttackShift & 7]) * ~EnvelopeLevel) >> 3; + if (AttackRate == 0) + add = 0; + if (AttackMask && (Master->Clock & AttackMask)) + add = 0; + EnvelopeLevel += add; + if (EnvelopeLevel <= 0) { + EnvelopeLevel = 0; + EnvelopeStage = EnvDec; + } + break; + } + + // Decay stage + case EnvDec: { + uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7]; + if (DecayRate == 0) + add = 0; + if (DecayMask && (Master->Clock & DecayMask)) + add = 0; + EnvelopeLevel += add; + if (EnvelopeLevel >= SustainLevel) { + EnvelopeLevel = SustainLevel; + EnvelopeStage = EnvSus; + } + break; + } + + // Sustain stage + case EnvSus: { + if (SustainMode) + break; + // Note: fall-through! + + }//fallthrough + + // Release stage + case EnvRel: { + uint16_t add = ReleaseAdd >> ReleaseTab[Master->Clock >> ReleaseShift & 7]; + if (ReleaseRate == 0) + add = 0; + if (ReleaseMask && (Master->Clock & ReleaseMask)) + add = 0; + EnvelopeLevel += add; + if (EnvelopeLevel >= 0x1FF) { + EnvelopeLevel = 0x1FF; + EnvelopeStage = EnvOff; + Out[0] = Out[1] = 0; + return 0; + } + break; + } + + // Envelope, and therefore the operator, is not running + default: + Out[0] = Out[1] = 0; + return 0; + } + + // Feedback? In that case we modulate by a blend of the last two samples + if (fbshift) + mod += (Out[0] + Out[1]) >> fbshift; + + uint16_t phase = (Phase >> 10) + mod; + uint16_t offset = phase & 0xFF; + uint16_t logsin; + bool negate = false; + + switch (Waveform) { + + //------------------------------------ + // Standard sine wave + //------------------------------------ + case 0: + if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + negate = (phase & 0x200) != 0; + break; + + //------------------------------------ + // Half sine wave + //------------------------------------ + case 1: + if (phase & 0x200) + offset = 0; + else if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Positive sine wave + //------------------------------------ + case 2: + if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Quarter positive sine wave + //------------------------------------ + case 3: + if (phase & 0x100) + offset = 0; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Double-speed sine wave + //------------------------------------ + case 4: + if (phase & 0x200) + offset = 0; + + else { + + if (phase & 0x80) + offset ^= 0xFF; + + offset = (offset + offset) & 0xFF; + negate = (phase & 0x100) != 0; + } + + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Double-speed positive sine wave + //------------------------------------ + case 5: + if (phase & 0x200) + offset = 0; + + else { + + offset = (offset + offset) & 0xFF; + if (phase & 0x80) + offset ^= 0xFF; + } + + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Square wave + //------------------------------------ + case 6: + logsin = 0; + negate = (phase & 0x200) != 0; + break; + + //------------------------------------ + // Exponentiation wave + //------------------------------------ + default: + logsin = phase & 0x1FF; + if (phase & 0x200) { + logsin ^= 0x1FF; + negate = true; + } + logsin <<= 3; + break; + } + + uint16_t mix = logsin + level; + if (mix > 0x1FFF) + mix = 0x1FFF; + + // From the OPLx decapsulated docs: + // "When such a table is used for calculation of the exponential, the table is read at the + // position given by the 8 LSB's of the input. The value + 1024 (the hidden bit) is then the + // significand of the floating point output and the yet unused MSB's of the input are the + // exponent of the floating point output." + int16_t v = Master->ExpTable[mix & 0xFF] + 1024; + v >>= mix >> 8; + v += v; + if (negate) + v = ~v; + + // Keep last two results for feedback calculation + Out[1] = Out[0]; + Out[0] = v; + + return v; +} + + + +//================================================================================================== +// Trigger operator. +//================================================================================================== +void Opal::Operator::SetKeyOn(bool on) { + + // Already on/off? + if (KeyOn == on) + return; + KeyOn = on; + + if (on) { + + // The highest attack rate is instant; it bypasses the attack phase + if (AttackRate == 15) { + EnvelopeStage = EnvDec; + EnvelopeLevel = 0; + } else + EnvelopeStage = EnvAtt; + + Phase = 0; + + } else { + + // Stopping current sound? + if (EnvelopeStage != EnvOff && EnvelopeStage != EnvRel) + EnvelopeStage = EnvRel; + } +} + + + +//================================================================================================== +// Enable amplitude vibrato. +//================================================================================================== +void Opal::Operator::SetTremoloEnable(bool on) { + + TremoloEnable = on; +} + + + +//================================================================================================== +// Enable frequency vibrato. +//================================================================================================== +void Opal::Operator::SetVibratoEnable(bool on) { + + VibratoEnable = on; +} + + + +//================================================================================================== +// Sets whether we release or sustain during the sustain phase of the envelope. 'true' is to +// sustain, otherwise release. +//================================================================================================== +void Opal::Operator::SetSustainMode(bool on) { + + SustainMode = on; +} + + + +//================================================================================================== +// Key scale rate. Sets how much the Key Scaling Number affects the envelope rates. +//================================================================================================== +void Opal::Operator::SetEnvelopeScaling(bool on) { + + KeyScaleRate = on; + ComputeRates(); +} + + + +//================================================================================================== +// Multiplies the phase frequency. +//================================================================================================== +void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) { + + // Needs to be multiplied by two (and divided by two later when we use it) because the first + // entry is actually .5 + const uint16_t mul_times_2[] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30, + }; + + FreqMultTimes2 = mul_times_2[scale & 15]; +} + + + +//================================================================================================== +// Attenuates output level towards higher pitch. +//================================================================================================== +void Opal::Operator::SetKeyScale(uint16_t scale) { + + /* libADLMIDI: KSL computation fix */ + const unsigned KeyScaleShiftTable[4] = {8, 1, 2, 0}; + KeyScaleShift = KeyScaleShiftTable[scale]; + + ComputeKeyScaleLevel(); +} + + + +//================================================================================================== +// Sets the output level (volume) of the operator. +//================================================================================================== +void Opal::Operator::SetOutputLevel(uint16_t level) { + + OutputLevel = level * 4; +} + + + +//================================================================================================== +// Operator attack rate. +//================================================================================================== +void Opal::Operator::SetAttackRate(uint16_t rate) { + + AttackRate = rate; + + ComputeRates(); +} + + + +//================================================================================================== +// Operator decay rate. +//================================================================================================== +void Opal::Operator::SetDecayRate(uint16_t rate) { + + DecayRate = rate; + + ComputeRates(); +} + + + +//================================================================================================== +// Operator sustain level. +//================================================================================================== +void Opal::Operator::SetSustainLevel(uint16_t level) { + + SustainLevel = level < 15 ? level : 31; + SustainLevel *= 16; +} + + + +//================================================================================================== +// Operator release rate. +//================================================================================================== +void Opal::Operator::SetReleaseRate(uint16_t rate) { + + ReleaseRate = rate; + + ComputeRates(); +} + + + +//================================================================================================== +// Assign the waveform this operator will use. +//================================================================================================== +void Opal::Operator::SetWaveform(uint16_t wave) { + + Waveform = wave & 7; +} + + + +//================================================================================================== +// Compute actual rate from register rate. From the Yamaha data sheet: +// +// Actual rate = Rate value * 4 + Rof, if Rate value = 0, actual rate = 0 +// +// Rof is set as follows depending on the KSR setting: +// +// Key scale 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// KSR = 0 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 +// KSR = 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// +// Note: zero rates are infinite, and are treated separately elsewhere +//================================================================================================== +void Opal::Operator::ComputeRates() { + + int combined_rate = AttackRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); + int rate_high = combined_rate >> 2; + int rate_low = combined_rate & 3; + + AttackShift = rate_high < 12 ? 12 - rate_high : 0; + AttackMask = (1 << AttackShift) - 1; + AttackAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + AttackTab = Master->RateTables[rate_low]; + + // Attack rate of 15 is always instant + if (AttackRate == 15) + AttackAdd = 0xFFF; + + combined_rate = DecayRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); + rate_high = combined_rate >> 2; + rate_low = combined_rate & 3; + + DecayShift = rate_high < 12 ? 12 - rate_high : 0; + DecayMask = (1 << DecayShift) - 1; + DecayAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + DecayTab = Master->RateTables[rate_low]; + + combined_rate = ReleaseRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); + rate_high = combined_rate >> 2; + rate_low = combined_rate & 3; + + ReleaseShift = rate_high < 12 ? 12 - rate_high : 0; + ReleaseMask = (1 << ReleaseShift) - 1; + ReleaseAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + ReleaseTab = Master->RateTables[rate_low]; +} + + + +//================================================================================================== +// Compute the operator's key scale level. This changes based on the channel frequency/octave and +// operator key scale value. +//================================================================================================== +void Opal::Operator::ComputeKeyScaleLevel() { + + static const uint16_t levtab[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 16, 20, 24, 28, 32, + 0, 0, 0, 0, 0, 12, 20, 28, 32, 40, 44, 48, 52, 56, 60, 64, + 0, 0, 0, 20, 32, 44, 52, 60, 64, 72, 76, 80, 84, 88, 92, 96, + 0, 0, 32, 52, 64, 76, 84, 92, 96, 104, 108, 112, 116, 120, 124, 128, + 0, 32, 64, 84, 96, 108, 116, 124, 128, 136, 140, 144, 148, 152, 156, 160, + 0, 64, 96, 116, 128, 140, 148, 156, 160, 168, 172, 176, 180, 184, 188, 192, + 0, 96, 128, 148, 160, 172, 180, 188, 192, 200, 204, 208, 212, 216, 220, 224, + }; + + // This uses a combined value of the top four bits of frequency with the octave/block + uint16_t i = (Chan->GetOctave() << 4) | (Chan->GetFreq() >> 6); + KeyScaleLevel = levtab[i] >> KeyScaleShift; +} diff --git a/thirdparty/adlmidi/chips/opal/old/opal-pan.diff b/thirdparty/adlmidi/chips/opal/old/opal-pan.diff new file mode 100644 index 000000000..a00a9fcbf --- /dev/null +++ b/thirdparty/adlmidi/chips/opal/old/opal-pan.diff @@ -0,0 +1,131 @@ +--- opal.cpp.orig 2019-02-23 20:32:00.477658789 +0100 ++++ opal.cpp 2019-02-23 20:46:36.745113958 +0100 +@@ -13,6 +13,7 @@ + - Percussion mode + + */ ++#define OPAL_HAVE_SOFT_PANNING 1 /* libADLMIDI */ + + + +@@ -133,6 +134,7 @@ + void SetOctave(uint16_t oct); + void SetLeftEnable(bool on); + void SetRightEnable(bool on); ++ void SetPan(uint8_t pan); + void SetFeedback(uint16_t val); + void SetModulationType(uint16_t type); + +@@ -158,6 +160,7 @@ + Channel * ChannelPair; + bool Enable; + bool LeftEnable, RightEnable; ++ uint16_t LeftPan, RightPan; + }; + + public: +@@ -166,6 +169,7 @@ + + void SetSampleRate(int sample_rate); + void Port(uint16_t reg_num, uint8_t val); ++ void Pan(uint16_t reg_num, uint8_t pan); + void Sample(int16_t *left, int16_t *right); + + protected: +@@ -191,6 +195,7 @@ + static const uint16_t RateTables[4][8]; + static const uint16_t ExpTable[256]; + static const uint16_t LogSinTable[256]; ++ static const uint16_t PanLawTable[128]; + }; + //-------------------------------------------------------------------------------------------------- + const uint16_t Opal::RateTables[4][8] = { +@@ -237,6 +242,28 @@ + 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + }; ++//-------------------------------------------------------------------------------------------------- ++const uint16_t Opal::PanLawTable[128] = ++{ ++ 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, ++ 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, ++ 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, ++ 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, ++ 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, ++ 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, ++ 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, ++ 50433, 49912, 49383, 48846, 48302, 47750, 47191, ++ 46340, // Center left ++ 46340, // Center right ++ 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, ++ 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, ++ 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, ++ 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, ++ 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, ++ 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, ++ 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, ++ 4858, 4050, 3240, 2431, 1620, 810, 0 ++}; + + + +@@ -342,6 +369,10 @@ + for (int i = 0; i < NumOperators; i++) + Op[i].ComputeRates(); + ++ // Initialise channel panning at center. ++ for (int i = 0; i < NumChannels; i++) ++ Chan[i].SetPan(64); ++ + SetSampleRate(sample_rate); + } + +@@ -533,6 +564,19 @@ + + + //================================================================================================== ++// Set panning on the channel designated by the register number. ++// This is extended functionality. ++//================================================================================================== ++void Opal::Pan(uint16_t reg_num, uint8_t pan) ++{ ++ uint8_t high = (reg_num >> 8) & 1; ++ uint8_t regm = reg_num & 0xff; ++ Chan[9 * high + (regm & 0x0f)].SetPan(pan); ++} ++ ++ ++ ++//================================================================================================== + // Generate sample. Every time you call this you will get two signed 16-bit samples (one for each + // stereo channel) which will sound correct when played back at the sample rate given when the + // class was constructed. +@@ -727,6 +771,9 @@ + + left = LeftEnable ? out : 0; + right = RightEnable ? out : 0; ++ ++ left = left * LeftPan / 65536; ++ right = right * RightPan / 65536; + } + + +@@ -793,6 +840,18 @@ + } + + ++ ++//================================================================================================== ++// Pan the channel to the position given. ++//================================================================================================== ++void Opal::Channel::SetPan(uint8_t pan) ++{ ++ pan &= 127; ++ LeftPan = PanLawTable[pan]; ++ RightPan = PanLawTable[127 - pan]; ++} ++ ++ + + //================================================================================================== + // Set the channel feedback amount. diff --git a/thirdparty/adlmidi/chips/opal/opal.c b/thirdparty/adlmidi/chips/opal/opal.c new file mode 100644 index 000000000..23cb4ffc6 --- /dev/null +++ b/thirdparty/adlmidi/chips/opal/opal.c @@ -0,0 +1,1448 @@ +/* + + The Opal OPL3 emulator. + + Note: this is not a complete emulator, just enough for Reality Adlib Tracker tunes. + + Missing features compared to a real OPL3: + + - Timers/interrupts + - OPL3 enable bit (it defaults to always on) + - CSW mode + - Test register + - Percussion mode + +*/ + +#include +#include +#include "opal.h" + +#ifndef INT_MAX +# define INT_MAX 2147483647 +#endif + +#ifndef INT_MIN +# define INT_MIN -2147483648 +#endif + +/* Various constants */ +typedef enum OpalEnumPriv_t +{ + OpalEnvOff = -1, + OpalEnvAtt, + OpalEnvDec, + OpalEnvSus, + OpalEnvRel +} OpalEnumPriv; + +/*--------------------------------------------------------------------------------------------------*/ +static const uint16_t RateTables[4][8] = +{ + { 1, 0, 1, 0, 1, 0, 1, 0 }, + { 1, 0, 1, 0, 0, 0, 1, 0 }, + { 1, 0, 0, 0, 1, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, 0, 0 }, +}; + +/*--------------------------------------------------------------------------------------------------*/ +static const uint16_t ExpTable[0x100] = +{ + 1018, 1013, 1007, 1002, 996, 991, 986, 980, 975, 969, 964, 959, 953, 948, 942, 937, + 932, 927, 921, 916, 911, 906, 900, 895, 890, 885, 880, 874, 869, 864, 859, 854, + 849, 844, 839, 834, 829, 824, 819, 814, 809, 804, 799, 794, 789, 784, 779, 774, + 770, 765, 760, 755, 750, 745, 741, 736, 731, 726, 722, 717, 712, 708, 703, 698, + 693, 689, 684, 680, 675, 670, 666, 661, 657, 652, 648, 643, 639, 634, 630, 625, + 621, 616, 612, 607, 603, 599, 594, 590, 585, 581, 577, 572, 568, 564, 560, 555, + 551, 547, 542, 538, 534, 530, 526, 521, 517, 513, 509, 505, 501, 496, 492, 488, + 484, 480, 476, 472, 468, 464, 460, 456, 452, 448, 444, 440, 436, 432, 428, 424, + 420, 416, 412, 409, 405, 401, 397, 393, 389, 385, 382, 378, 374, 370, 367, 363, + 359, 355, 352, 348, 344, 340, 337, 333, 329, 326, 322, 318, 315, 311, 308, 304, + 300, 297, 293, 290, 286, 283, 279, 276, 272, 268, 265, 262, 258, 255, 251, 248, + 244, 241, 237, 234, 231, 227, 224, 220, 217, 214, 210, 207, 204, 200, 197, 194, + 190, 187, 184, 181, 177, 174, 171, 168, 164, 161, 158, 155, 152, 148, 145, 142, + 139, 136, 133, 130, 126, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93, + 90, 87, 84, 81, 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45, + 42, 40, 37, 34, 31, 28, 25, 22, 20, 17, 14, 11, 8, 6, 3, 0, +}; + +/*--------------------------------------------------------------------------------------------------*/ +static const uint16_t LogSinTable[0x100] = +{ + 2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, + 846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, + 598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, + 453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, + 352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, + 276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, + 215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, + 167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, + 127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, + 94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, + 67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, + 46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, + 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, + 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/*--------------------------------------------------------------------------------------------------*/ +static const uint16_t PanLawTable[128] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + + +/*================================================================================================== + * OpalOperator calls + *=================================================================================================*/ +static void OpalOperator_Init(OpalOperator *op); + +static void OpalOperator_SetMaster(OpalOperator *self, Opal* opal); +static void OpalOperator_SetChannel(OpalOperator *self, OpalChannel* chan); + +static int16_t OpalOperator_Output(OpalOperator *self, uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod /*= 0*/, int16_t fbshift /* = 0*/); + +static void OpalOperator_SetKeyOn(OpalOperator *self, opal_bool on); +static void OpalOperator_SetTremoloEnable(OpalOperator *self, opal_bool on); +static void OpalOperator_SetVibratoEnable(OpalOperator *self, opal_bool on); +static void OpalOperator_SetSustainMode(OpalOperator *self, opal_bool on); +static void OpalOperator_SetEnvelopeScaling(OpalOperator *self, opal_bool on); +static void OpalOperator_SetFrequencyMultiplier(OpalOperator *self, uint16_t scale); +static void OpalOperator_SetKeyScale(OpalOperator *self, uint16_t scale); +static void OpalOperator_SetOutputLevel(OpalOperator *self, uint16_t level); +static void OpalOperator_SetAttackRate(OpalOperator *self, uint16_t rate); +static void OpalOperator_SetDecayRate(OpalOperator *self, uint16_t rate); +static void OpalOperator_SetSustainLevel(OpalOperator *self, uint16_t level); +static void OpalOperator_SetReleaseRate(OpalOperator *self, uint16_t rate); +static void OpalOperator_SetWaveform(OpalOperator *self, uint16_t wave); + +static void OpalOperator_ComputeRates(OpalOperator *self); +static void OpalOperator_ComputeKeyScaleLevel(OpalOperator *self); + + +/*================================================================================================== + * OpalChannel calls + *=================================================================================================*/ +static void OpalChannel_Init(OpalChannel *self); + +/*public:*/ +static void OpalChannel_SetMaster(OpalChannel *self, Opal* opal); +static void OpalChannel_SetOperators(OpalChannel *self, OpalOperator* a, OpalOperator* b, OpalOperator* c, OpalOperator* d); + +static void OpalChannel_Output(OpalChannel *self, int16_t *left, int16_t *right); +static void OpalChannel_SetEnable(OpalChannel *self, opal_bool on); +static void OpalChannel_SetChannelPair(OpalChannel *self, OpalChannel* pair); + +static void OpalChannel_SetFrequencyLow(OpalChannel *self, uint16_t freq); +static void OpalChannel_SetFrequencyHigh(OpalChannel *self, uint16_t freq); +static void OpalChannel_SetKeyOn(OpalChannel *self, opal_bool on); +static void OpalChannel_SetOctave(OpalChannel *self, uint16_t oct); +static void OpalChannel_SetLeftEnable(OpalChannel *self, opal_bool on); +static void OpalChannel_SetRightEnable(OpalChannel *self, opal_bool on); +static void OpalChannel_SetPan(OpalChannel *self, uint8_t pan); +static void OpalChannel_SetFeedback(OpalChannel *self, uint16_t val); +static void OpalChannel_SetModulationType(OpalChannel *self, uint16_t type); + +static uint16_t OpalChannel_GetFreq(OpalChannel *self); +static uint16_t OpalChannel_GetOctave(OpalChannel *self); +static uint16_t OpalChannel_GetKeyScaleNumber(OpalChannel *self); +static uint16_t OpalChannel_GetModulationType(OpalChannel *self); +static OpalChannel* OpalChannel_GetChannelPair(OpalChannel *self); /* libADLMIDI */ + +static void OpalChannel_ComputeKeyScaleNumber(OpalChannel *self); + +/*protected:*/ +static void OpalChannel_ComputePhaseStep(OpalChannel *self); + +/*================================================================================================== + * Opal private calls + *=================================================================================================*/ +static void Opal_Output(Opal *self, int16_t *left, int16_t *right); + + + + +/*================================================================================================== + * Implementations * + *=================================================================================================*/ + + +static const int chan_ops[] = +{ + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32, +}; + +/*================================================================================================== + * Initialise the emulation. + *=================================================================================================*/ +void Opal_Init(Opal *self, int sample_rate) +{ + int i, op; + OpalChannel* chan; + + self->Clock = 0; + self->TremoloClock = 0; + self->TremoloLevel = 0; + self->VibratoTick = 0; + self->VibratoClock = 0; + self->NoteSel = OPAL_FALSE; + self->TremoloDepth = OPAL_FALSE; + self->VibratoDepth = OPAL_FALSE; + + for(i = 0; i < OpalNumOperators; i++) + OpalOperator_Init(&self->Op[i]); + + for(i = 0; i < OpalNumChannels; i++) + OpalChannel_Init(&self->Chan[i]); + + /* + // // Build the exponentiation table (reversed from the official OPL3 ROM) + // for (int i = 0; i < 0x100; i++) + // ExpTable[i] = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; + // + // // Build the log-sin table + // for (int i = 0; i < 0x100; i++) + // LogSinTable[i] = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; + */ + + /* Let sub-objects know where to find us */ + for(i = 0; i < OpalNumOperators; i++) + OpalOperator_SetMaster(&self->Op[i], self); + + for(i = 0; i < OpalNumChannels; i++) + OpalChannel_SetMaster(&self->Chan[i], self); + + /* Add the operators to the channels. Note, some channels can't use all the operators */ + /* FIXME: put this into a separate routine */ + for(i = 0; i < OpalNumChannels; i++) + { + chan = &self->Chan[i]; + op = chan_ops[i]; + + if(i < 3 || (i >= 9 && i < 12)) + OpalChannel_SetOperators(chan, &self->Op[op], &self->Op[op + 3], &self->Op[op + 6], &self->Op[op + 9]); + else + OpalChannel_SetOperators(chan, &self->Op[op], &self->Op[op + 3], 0, 0); + } + + /* Initialise the operator rate data. We can't do this in the Operator constructor as it + * relies on referencing the master and channel objects */ + for(i = 0; i < OpalNumOperators; i++) + OpalOperator_ComputeRates(&self->Op[i]); + + /* Initialise channel panning at center. */ + for(i = 0; i < OpalNumChannels; i++) + { + OpalChannel_SetPan(&self->Chan[i], 64); + OpalChannel_SetLeftEnable(&self->Chan[i], OPAL_TRUE); + OpalChannel_SetRightEnable(&self->Chan[i], OPAL_TRUE); + } + + Opal_SetSampleRate(self, sample_rate); +} + + + +/*================================================================================================== + * Change the sample rate. + *=================================================================================================*/ +void Opal_SetSampleRate(Opal *self, int sample_rate) +{ + /* Sanity */ + if(sample_rate == 0) + sample_rate = OpalOPL3SampleRate; + + self->SampleRate = sample_rate; + self->SampleAccum = 0; + self->LastOutput[0] = self->LastOutput[1] = 0; + self->CurrOutput[0] = self->CurrOutput[1] = 0; +} + + +static const int op_lookup[] = +{ + /* 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F */ + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + /* 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F */ + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +/*================================================================================================== + * Write a value to an OPL3 register. + *=================================================================================================*/ +void Opal_Port(Opal *self, uint16_t reg_num, uint8_t val) +{ + int i; + unsigned ui; + uint8_t mask; + unsigned numchans; + uint16_t chan; + int chan_num; + int op_num; + OpalChannel *primary, *secondary, *cchan; + OpalChannel* chans[2]; + uint16_t type = reg_num & 0xE0; + OpalOperator *op; + + /* Is it BD, the one-off register stuck in the middle of the register array? */ + if(reg_num == 0xBD) + { + self->TremoloDepth = (val & 0x80) != 0; + self->VibratoDepth = (val & 0x40) != 0; + return; + } + + /* Global registers */ + if(type == 0x00) + { + + /* 4-OP enables*/ + if(reg_num == 0x104) + { + + /* Enable/disable channels based on which 4-op enables */ + mask = 1; + + for(i = 0; i < 6; i++, mask <<= 1) + { + /* The 4-op channels are 0, 1, 2, 9, 10, 11 */ + chan = i < 3 ? i : i + 6; + primary = &self->Chan[chan]; + secondary = &self->Chan[chan + 3]; + + if(val & mask) + { + /* Let primary channel know it's controlling the secondary channel */ + OpalChannel_SetChannelPair(primary, secondary); + + /* Turn off the second channel in the pair */ + OpalChannel_SetEnable(secondary, OPAL_FALSE); + + } + else + { + /* Let primary channel know it's no longer controlling the secondary channel */ + OpalChannel_SetChannelPair(primary, 0); + + /* Turn on the second channel in the pair */ + OpalChannel_SetEnable(secondary, OPAL_TRUE); + } + } + + /* CSW / Note-sel */ + } + else if(reg_num == 0x08) + { + self->NoteSel = (val & 0x40) != 0; + + /* Get the channels to recompute the Key Scale No. as this varies based on NoteSel */ + for(i = 0; i < OpalNumChannels; i++) + OpalChannel_ComputeKeyScaleNumber(&self->Chan[i]); + } + + /* Channel registers */ + } + else if(type >= 0xA0 && type <= 0xC0) + { + /* Convert to channel number */ + chan_num = reg_num & 15; + + /* Valid channel? */ + if(chan_num >= 9) + return; + + /* Is it the other bank of channels? */ + if(reg_num & 0x100) + chan_num += 9; + + cchan = &self->Chan[chan_num]; + + /* libADLMIDI: registers Ax and Cx affect both channels */ + chans[0] = cchan; + chans[1] = OpalChannel_GetChannelPair(cchan); + numchans = chans[1] ? 2 : 1; + + /* Do specific registers */ + switch(reg_num & 0xF0) + { + + /* Frequency low */ + case 0xA0: + { + for(ui = 0; ui < numchans; ++ui) /* libADLMIDI */ + { + OpalChannel_SetFrequencyLow(chans[ui], val); + } + + break; + } + + /* Key-on / Octave / Frequency High */ + case 0xB0: + { + for(ui = 0; ui < numchans; ++ui) /* libADLMIDI */ + { + OpalChannel_SetKeyOn(chans[ui],(val & 0x20) != 0); + OpalChannel_SetOctave(chans[ui], val >> 2 & 7); + OpalChannel_SetFrequencyHigh(chans[ui], val & 3); + } + + break; + } + + /* Right Stereo Channel Enable / Left Stereo Channel Enable / Feedback Factor / Modulation Type */ + case 0xC0: + { + OpalChannel_SetRightEnable(cchan, (val & 0x20) != 0); + OpalChannel_SetLeftEnable(cchan, (val & 0x10) != 0); + OpalChannel_SetFeedback(cchan, val >> 1 & 7); + OpalChannel_SetModulationType(cchan, val & 1); + break; + } + } + + /* Operator registers */ + } + else if((type >= 0x20 && type <= 0x80) || type == 0xE0) + { + /* Convert to operator number */ + op_num = op_lookup[reg_num & 0x1F]; + + /* Valid register? */ + if(op_num < 0) + return; + + /* Is it the other bank of operators? */ + if(reg_num & 0x100) + op_num += 18; + + op = &self->Op[op_num]; + + /* Do specific registers */ + switch(type) + { + + /* Tremolo Enable / Vibrato Enable / Sustain Mode / Envelope Scaling / Frequency Multiplier */ + case 0x20: + { + OpalOperator_SetTremoloEnable(op, (val & 0x80) != 0); + OpalOperator_SetVibratoEnable(op, (val & 0x40) != 0); + OpalOperator_SetSustainMode(op, (val & 0x20) != 0); + OpalOperator_SetEnvelopeScaling(op, (val & 0x10) != 0); + OpalOperator_SetFrequencyMultiplier(op, val & 15); + break; + } + + /* Key Scale / Output Level */ + case 0x40: + { + OpalOperator_SetKeyScale(op, val >> 6); + OpalOperator_SetOutputLevel(op, val & 0x3F); + break; + } + + /* Attack Rate / Decay Rate */ + case 0x60: + { + OpalOperator_SetAttackRate(op, val >> 4); + OpalOperator_SetDecayRate(op, val & 15); + break; + } + + /* Sustain Level / Release Rate */ + case 0x80: + { + OpalOperator_SetSustainLevel(op, val >> 4); + OpalOperator_SetReleaseRate(op, val & 15); + break; + } + + /* Waveform */ + case 0xE0: + { + OpalOperator_SetWaveform(op, val & 7); + break; + } + } + } +} + + + +/*================================================================================================== + * Set panning on the channel designated by the register number. + * This is extended functionality. + *=================================================================================================*/ +void Opal_Pan(Opal *self, uint16_t reg_num, uint8_t pan) +{ + uint8_t high = (reg_num >> 8) & 1; + uint8_t regm = reg_num & 0xff; + OpalChannel_SetPan(&self->Chan[9 * high + (regm & 0x0f)], pan); +} + + +static inline int32_t s_opal_MulDivR(int32_t a, int32_t b, int32_t c) +{ + int64_t ret = (((int64_t)(a) * b) + ( c / 2 )) / c; + + if(ret > INT_MAX) + return INT_MAX; + else if(ret < INT_MIN) + return INT_MIN; + else + return (int32_t)(ret); +} + + +/*================================================================================================== + * Generate sample. Every time you call this you will get two signed 16-bit samples (one for each + * stereo channel) which will sound correct when played back at the sample rate given when the + * class was constructed. + *=================================================================================================*/ +void Opal_Sample(Opal *self, int16_t* left, int16_t* right) +{ + int32_t fract; + + /* If the destination sample rate is higher than the OPL3 sample rate, we need to skip ahead */ + while(self->SampleAccum >= self->SampleRate) + { + self->LastOutput[0] = self->CurrOutput[0]; + self->LastOutput[1] = self->CurrOutput[1]; + + Opal_Output(self, &self->CurrOutput[0], &self->CurrOutput[1]); + + self->SampleAccum -= self->SampleRate; + } + + /* Mix with the partial accumulation */ + fract = s_opal_MulDivR(self->SampleAccum, 65536, self->SampleRate); + *left = (int16_t)(self->LastOutput[0] + ((fract * (self->CurrOutput[0] - self->LastOutput[0])) / 65536)); + *right = (int16_t)(self->LastOutput[1] + ((fract * (self->CurrOutput[1] - self->LastOutput[1])) / 65536)); + + self->SampleAccum += OpalOPL3SampleRate; +} + + + +/*================================================================================================== + * Produce final output from the chip. This is at the OPL3 sample-rate. + *=================================================================================================*/ +static void Opal_Output(Opal *self, int16_t *left, int16_t *right) +{ + int i; + int16_t chanleft, chanright; + int32_t leftmix = 0, rightmix = 0; + + /* Sum the output of each channel */ + for(i = 0; i < OpalNumChannels; i++) + { + OpalChannel_Output(&self->Chan[i], &chanleft, &chanright); + leftmix += chanleft; + rightmix += chanright; + } + + /* Clamp */ + if(leftmix < -0x8000) + *left = -0x8000; + else if(leftmix > 0x7FFF) + *left = 0x7FFF; + else + *left = leftmix; + + if(rightmix < -0x8000) + *right = -0x8000; + else if(rightmix > 0x7FFF) + *right = 0x7FFF; + else + *right = rightmix; + + self->Clock++; + + /* Tremolo. According to this post, the OPL3 tremolo is a 13,440 sample length triangle wave + * with a peak at 26 and a trough at 0 and is simply added to the logarithmic level accumulator + * http://forums.submarine.org.uk/phpBB/viewtopic.php?f=9&t=1171 */ + self->TremoloClock = (self->TremoloClock + 1) % 13440; + self->TremoloLevel = ((self->TremoloClock < 13440 / 2) ? self->TremoloClock : 13440 - self->TremoloClock) / 256; + + if(!self->TremoloDepth) + self->TremoloLevel >>= 2; + + /* Vibrato. This appears to be a 8 sample long triangle wave with a magnitude of the three + * high bits of the channel frequency, positive and negative, divided by two if the vibrato + * depth is zero. It is only cycled every 1,024 samples. */ + self->VibratoTick++; + + if(self->VibratoTick >= 1024) + { + self->VibratoTick = 0; + self->VibratoClock = (self->VibratoClock + 1) & 7; + } +} + + + +/*================================================================================================== + * Channel constructor. + *=================================================================================================*/ +static void OpalChannel_Init(OpalChannel *self) +{ + self->Master = 0; + self->Freq = 0; + self->Octave = 0; + self->PhaseStep = 0; + self->KeyScaleNumber = 0; + self->FeedbackShift = 0; + self->ModulationType = 0; + self->ChannelPair = 0; + self->Enable = OPAL_TRUE; + self->LeftPan = 46340; + self->RightPan = 46340; +} + +static void OpalChannel_SetMaster(OpalChannel *self, Opal* opal) +{ + self->Master = opal; +} + + +static void OpalChannel_SetOperators(OpalChannel *self, OpalOperator* a, OpalOperator* b, OpalOperator* c, OpalOperator* d) +{ + self->Op[0] = a; + self->Op[1] = b; + self->Op[2] = c; + self->Op[3] = d; + + if(a) OpalOperator_SetChannel(a, self); + if(b) OpalOperator_SetChannel(b, self); + if(c) OpalOperator_SetChannel(c, self); + if(d) OpalOperator_SetChannel(d, self); +} + + +/*================================================================================================== + * Produce output from channel. + *==================================================================================================*/ +static void OpalChannel_Output(OpalChannel *self, int16_t *left, int16_t *right) +{ + int16_t vibrato; + uint16_t clk; + /* Combine individual operator outputs */ + int16_t out, acc; + + /* Has the channel been disabled? This is usually a result of the 4-op enables being used to + * disable the secondary channel in each 4-op pair */ + if(!self->Enable) + { + *left = *right = 0; + return; + } + + vibrato = (self->Freq >> 7) & 7; + + if(!self->Master->VibratoDepth) + vibrato >>= 1; + + /* 0 3 7 3 0 -3 -7 -3 */ + clk = self->Master->VibratoClock; + + if(!(clk & 3)) + vibrato = 0; /* Position 0 and 4 is zero */ + else + { + if(clk & 1) + vibrato >>= 1; /* Odd positions are half the magnitude */ + + if(clk & 4) + vibrato = -vibrato; /* The second half positions are negative */ + } + + vibrato <<= self->Octave; + + /* Combine individual operator outputs */ + + /* Running in 4-op mode? */ + if(self->ChannelPair) + { + + /* Get the secondary channel's modulation type. This is the only thing from the secondary + * channel that is used */ + if(OpalChannel_GetModulationType(self->ChannelPair) == 0) + { + if(self->ModulationType == 0) + { + /* feedback -> modulator -> modulator -> modulator -> carrier */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + out = OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, out, 0); + out = OpalOperator_Output(self->Op[2], self->KeyScaleNumber, self->PhaseStep, vibrato, out, 0); + out = OpalOperator_Output(self->Op[3], self->KeyScaleNumber, self->PhaseStep, vibrato, out, 0); + + } + else + { + /* (feedback -> carrier) + (modulator -> modulator -> carrier) */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + acc = OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, 0); + acc = OpalOperator_Output(self->Op[2], self->KeyScaleNumber, self->PhaseStep, vibrato, acc, 0); + out += OpalOperator_Output(self->Op[3], self->KeyScaleNumber, self->PhaseStep, vibrato, acc, 0); + } + + } + else + { + + if(self->ModulationType == 0) + { + /* (feedback -> modulator -> carrier) + (modulator -> carrier) */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + out = OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, out, 0); + acc = OpalOperator_Output(self->Op[2], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, 0); + out += OpalOperator_Output(self->Op[3], self->KeyScaleNumber, self->PhaseStep, vibrato, acc, 0); + + } + else + { + /* (feedback -> carrier) + (modulator -> carrier) + carrier */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + acc = OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, 0); + out += OpalOperator_Output(self->Op[2], self->KeyScaleNumber, self->PhaseStep, vibrato, acc, 0); + out += OpalOperator_Output(self->Op[3], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, 0); + } + } + + } + else + { + /* Standard 2-op mode */ + if(self->ModulationType == 0) + { + /* Frequency modulation (well, phase modulation technically) */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + out = OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, out, 0); + + } + else + { + /* Additive */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + out += OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, 0); + } + } + + *left = self->LeftEnable ? out : 0; + *right = self->RightEnable ? out : 0; + + *left = (*left * self->LeftPan) / 65536; + *right = (*right * self->RightPan) / 65536; +} + +static void OpalChannel_SetEnable(OpalChannel *self, opal_bool on) +{ + self->Enable = on; +} + +static void OpalChannel_SetChannelPair(OpalChannel *self, OpalChannel* pair) +{ + self->ChannelPair = pair; +} + + + +/*================================================================================================== + * Set phase step for operators using this channel. + *=================================================================================================*/ +static void OpalChannel_SetFrequencyLow(OpalChannel *self, uint16_t freq) +{ + self->Freq = (self->Freq & 0x300) | (freq & 0xFF); + OpalChannel_ComputePhaseStep(self); +} + +/*-------------------------------------------------------------------------------------------------*/ +static void OpalChannel_SetFrequencyHigh(OpalChannel *self, uint16_t freq) +{ + self->Freq = (self->Freq & 0xFF) | ((freq & 3) << 8); + OpalChannel_ComputePhaseStep(self); + + /* Only the high bits of Freq affect the Key Scale No. */ + OpalChannel_ComputeKeyScaleNumber(self); +} + + + +/*================================================================================================== + * Set the octave of the channel (0 to 7). + *=================================================================================================*/ +static void OpalChannel_SetOctave(OpalChannel *self, uint16_t oct) +{ + self->Octave = oct & 7; + OpalChannel_ComputePhaseStep(self); + OpalChannel_ComputeKeyScaleNumber(self); +} + + + +/*================================================================================================== + * Keys the channel on/off. + *=================================================================================================*/ +static void OpalChannel_SetKeyOn(OpalChannel *self, opal_bool on) +{ + OpalOperator_SetKeyOn(self->Op[0], on); + OpalOperator_SetKeyOn(self->Op[1], on); +} + + + +/*================================================================================================== + * Enable left stereo channel. + *=================================================================================================*/ +static void OpalChannel_SetLeftEnable(OpalChannel *self, opal_bool on) +{ + self->LeftEnable = on; +} + + + +/*================================================================================================== + * Enable right stereo channel. + *=================================================================================================*/ +static void OpalChannel_SetRightEnable(OpalChannel *self, opal_bool on) +{ + self->RightEnable = on; +} + + + +/*================================================================================================== + * Pan the channel to the position given. + *=================================================================================================*/ +static void OpalChannel_SetPan(OpalChannel *self, uint8_t pan) +{ + pan &= 127; + self->LeftPan = PanLawTable[pan]; + self->RightPan = PanLawTable[127 - pan]; +} + + + +/*================================================================================================== + * Set the channel feedback amount. + *=================================================================================================*/ +static void OpalChannel_SetFeedback(OpalChannel *self, uint16_t val) +{ + self->FeedbackShift = val ? 9 - val : 0; +} + + + +/*================================================================================================== + * Set frequency modulation/additive modulation + *=================================================================================================*/ +static void OpalChannel_SetModulationType(OpalChannel *self, uint16_t type) +{ + self->ModulationType = type; +} + +static uint16_t OpalChannel_GetFreq(OpalChannel *self) +{ + return self->Freq; +} + +static uint16_t OpalChannel_GetOctave(OpalChannel *self) +{ + return self->Octave; +} + +static uint16_t OpalChannel_GetKeyScaleNumber(OpalChannel *self) +{ + return self->KeyScaleNumber; +} + +static uint16_t OpalChannel_GetModulationType(OpalChannel *self) +{ + return self->ModulationType; +} + +static OpalChannel* OpalChannel_GetChannelPair(OpalChannel *self) /* libADLMIDI */ +{ + return self->ChannelPair; +} + +/*================================================================================================== + Compute the stepping factor for the operator waveform phase based on the frequency and octave + values of the channel. + ==================================================================================================*/ +static void OpalChannel_ComputePhaseStep(OpalChannel *self) +{ + self->PhaseStep = (uint32_t)(self->Freq) << self->Octave; +} + + + +/*================================================================================================== + Compute the key scale number and key scale levels. + + From the Yamaha data sheet this is the block/octave number as bits 3-1, with bit 0 coming from + the MSB of the frequency if NoteSel is 1, and the 2nd MSB if NoteSel is 0. + ==================================================================================================*/ +static void OpalChannel_ComputeKeyScaleNumber(OpalChannel *self) +{ + int i; + uint16_t lsb = self->Master->NoteSel ? self->Freq >> 9 : (self->Freq >> 8) & 1; + self->KeyScaleNumber = self->Octave << 1 | lsb; + + /* Get the channel operators to recompute their rates as they're dependent on this number. + * They also need to recompute their key scale level */ + for(i = 0; i < 4; i++) + { + if(!self->Op[i]) + continue; + + OpalOperator_ComputeRates(self->Op[i]); + OpalOperator_ComputeKeyScaleLevel(self->Op[i]); + } +} + + +/*================================================================================================== + * Operator constructor. + *=================================================================================================*/ +static void OpalOperator_Init(OpalOperator *op) +{ + op->Master = 0; + op->Chan = 0; + op->Phase = 0; + op->Waveform = 0; + op->FreqMultTimes2 = 1; + op->EnvelopeStage = OpalEnvOff; + op->EnvelopeLevel = 0x1FF; + op->AttackRate = 0; + op->DecayRate = 0; + op->SustainLevel = 0; + op->ReleaseRate = 0; + op->KeyScaleShift = 0; + op->KeyScaleLevel = 0; + op->Out[0] = op->Out[1] = 0; + op->KeyOn = OPAL_FALSE; + op->KeyScaleRate = OPAL_FALSE; + op->SustainMode = OPAL_FALSE; + op->TremoloEnable = OPAL_FALSE; + op->VibratoEnable = OPAL_FALSE; +} + +static void OpalOperator_SetMaster(OpalOperator *self, Opal* opal) +{ + self->Master = opal; +} + +static void OpalOperator_SetChannel(OpalOperator *self, OpalChannel* chan) +{ + self->Chan = chan; +} + + +/*================================================================================================== + * Produce output from operator. + *=================================================================================================*/ +static int16_t OpalOperator_Output(OpalOperator *self, uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod, int16_t fbshift) +{ + int16_t v; + uint16_t mix, phase, offset, logsin, add, level, negate; + + (void)keyscalenum; + + /* Advance wave phase */ + if(self->VibratoEnable) + phase_step += vibrato; + + self->Phase += (phase_step * self->FreqMultTimes2) / 2; + + level = (self->EnvelopeLevel + self->OutputLevel + self->KeyScaleLevel + (self->TremoloEnable ? self->Master->TremoloLevel : 0)) << 3; + + switch(self->EnvelopeStage) + { + + /* Attack stage */ + case OpalEnvAtt: + { + add = ((self->AttackAdd >> self->AttackTab[self->Master->Clock >> self->AttackShift & 7]) * ~self->EnvelopeLevel) >> 3; + + if(self->AttackRate == 0) + add = 0; + + if(self->AttackMask && (self->Master->Clock & self->AttackMask)) + add = 0; + + self->EnvelopeLevel += add; + + if(self->EnvelopeLevel <= 0) + { + self->EnvelopeLevel = 0; + self->EnvelopeStage = OpalEnvDec; + } + + break; + } + + /* Decay stage */ + case OpalEnvDec: + { + add = self->DecayAdd >> self->DecayTab[self->Master->Clock >> self->DecayShift & 7]; + + if(self->DecayRate == 0) + add = 0; + + if(self->DecayMask && (self->Master->Clock & self->DecayMask)) + add = 0; + + self->EnvelopeLevel += add; + + if(self->EnvelopeLevel >= self->SustainLevel) + { + self->EnvelopeLevel = self->SustainLevel; + self->EnvelopeStage = OpalEnvSus; + } + + break; + } + + /* Sustain stage */ + case OpalEnvSus: + { + if(self->SustainMode) + break; + + /* Note: fall-through! */ + + } /* fallthrough */ + + /* Release stage */ + case OpalEnvRel: + { + add = self->ReleaseAdd >> self->ReleaseTab[self->Master->Clock >> self->ReleaseShift & 7]; + + if(self->ReleaseRate == 0) + add = 0; + + if(self->ReleaseMask && (self->Master->Clock & self->ReleaseMask)) + add = 0; + + self->EnvelopeLevel += add; + + if(self->EnvelopeLevel >= 0x1FF) + { + self->EnvelopeLevel = 0x1FF; + self->EnvelopeStage = OpalEnvOff; + self->Out[0] = self->Out[1] = 0; + return 0; + } + + break; + } + + /* Envelope, and therefore the operator, is not running */ + default: + self->Out[0] = self->Out[1] = 0; + return 0; + } + + /* Feedback? In that case we modulate by a blend of the last two samples */ + if(fbshift) + mod += (self->Out[0] + self->Out[1]) >> fbshift; + + phase = (self->Phase >> 10) + mod; + offset = phase & 0xFF; + negate = OPAL_FALSE; + + switch(self->Waveform) + { + + /*------------------------------------ + * Standard sine wave + *------------------------------------*/ + case 0: + if(phase & 0x100) + offset ^= 0xFF; + + logsin = LogSinTable[offset]; + negate = (phase & 0x200) != 0; + break; + + /*------------------------------------ + * Half sine wave + *------------------------------------*/ + case 1: + if(phase & 0x200) + offset = 0; + else if(phase & 0x100) + offset ^= 0xFF; + + logsin = LogSinTable[offset]; + break; + + /*------------------------------------ + * Positive sine wave + *------------------------------------*/ + case 2: + if(phase & 0x100) + offset ^= 0xFF; + + logsin = LogSinTable[offset]; + break; + + /*------------------------------------ + * Quarter positive sine wave + *------------------------------------*/ + case 3: + if(phase & 0x100) + offset = 0; + + logsin = LogSinTable[offset]; + break; + + /*------------------------------------ + * Double-speed sine wave + *------------------------------------*/ + case 4: + if(phase & 0x200) + offset = 0; + + else + { + + if(phase & 0x80) + offset ^= 0xFF; + + offset = (offset + offset) & 0xFF; + negate = (phase & 0x100) != 0; + } + + logsin = LogSinTable[offset]; + break; + + /*------------------------------------ + * Double-speed positive sine wave + *------------------------------------*/ + case 5: + if(phase & 0x200) + offset = 0; + + else + { + + offset = (offset + offset) & 0xFF; + + if(phase & 0x80) + offset ^= 0xFF; + } + + logsin = LogSinTable[offset]; + break; + + /*------------------------------------ + * Square wave + *------------------------------------*/ + case 6: + logsin = 0; + negate = (phase & 0x200) != 0; + break; + + /*------------------------------------ + * Exponentiation wave + *------------------------------------*/ + default: + logsin = phase & 0x1FF; + + if(phase & 0x200) + { + logsin ^= 0x1FF; + negate = OPAL_TRUE; + } + + logsin <<= 3; + break; + } + + mix = logsin + level; + + if(mix > 0x1FFF) + mix = 0x1FFF; + + /* From the OPLx decapsulated docs: + * "When such a table is used for calculation of the exponential, the table is read at the + * position given by the 8 LSB's of the input. The value + 1024 (the hidden bit) is then the + * significand of the floating point output and the yet unused MSB's of the input are the + * exponent of the floating point output." */ + v = ExpTable[mix & 0xFF] + 1024; + v >>= mix >> 8; + v += v; + + if(negate) + v = ~v; + + /* Keep last two results for feedback calculation */ + self->Out[1] = self->Out[0]; + self->Out[0] = v; + + return v; +} + + + +/*================================================================================================== + * Trigger operator. + *=================================================================================================*/ +void OpalOperator_SetKeyOn(OpalOperator* self, opal_bool on) +{ + /* Already on/off? */ + if(self->KeyOn == on) + return; + + self->KeyOn = on; + + if(on) + { + /* The highest attack rate is instant; it bypasses the attack phase */ + if(self->AttackRate == 15) + { + self->EnvelopeStage = OpalEnvDec; + self->EnvelopeLevel = 0; + } + else + self->EnvelopeStage = OpalEnvAtt; + + self->Phase = 0; + + } + else + { + /* Stopping current sound? */ + if(self->EnvelopeStage != OpalEnvOff && self->EnvelopeStage != OpalEnvRel) + self->EnvelopeStage = OpalEnvRel; + } +} + + +/*================================================================================================== + * Enable amplitude vibrato. + *=================================================================================================*/ +static void OpalOperator_SetTremoloEnable(OpalOperator *self, opal_bool on) +{ + self->TremoloEnable = on; +} + + + +/*================================================================================================== + * Enable frequency vibrato. + *=================================================================================================*/ +static void OpalOperator_SetVibratoEnable(OpalOperator *self, opal_bool on) +{ + self->VibratoEnable = on; +} + + + +/*================================================================================================== + * Sets whether we release or sustain during the sustain phase of the envelope. 'true' is to + * sustain, otherwise release. + *=================================================================================================*/ +static void OpalOperator_SetSustainMode(OpalOperator *self, opal_bool on) +{ + self->SustainMode = on; +} + + + +/*================================================================================================== + * Key scale rate. Sets how much the Key Scaling Number affects the envelope rates. + *==================================================================================================*/ +static void OpalOperator_SetEnvelopeScaling(OpalOperator *self, opal_bool on) +{ + self->KeyScaleRate = on; + OpalOperator_ComputeRates(self); +} + + +/* Needs to be multiplied by two (and divided by two later when we use it) because + * the first entry is actually .5 */ +static const uint16_t mul_times_2[] = +{ + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30, +}; + +/*================================================================================================== + * Multiplies the phase frequency. + *==================================================================================================*/ +static void OpalOperator_SetFrequencyMultiplier(OpalOperator *self, uint16_t scale) +{ + self->FreqMultTimes2 = mul_times_2[scale & 15]; +} + + + +/*================================================================================================== + * Attenuates output level towards higher pitch. + *==================================================================================================*/ +static void OpalOperator_SetKeyScale(OpalOperator *self, uint16_t scale) +{ + /* libADLMIDI: KSL computation fix */ + const unsigned KeyScaleShiftTable[4] = {8, 1, 2, 0}; + self->KeyScaleShift = KeyScaleShiftTable[scale]; + + OpalOperator_ComputeKeyScaleLevel(self); +} + + + +/*================================================================================================== + * Sets the output level (volume) of the operator. + *=================================================================================================*/ +static void OpalOperator_SetOutputLevel(OpalOperator *self, uint16_t level) +{ + self->OutputLevel = level * 4; +} + + + +/*================================================================================================== + * Operator attack rate. + *=================================================================================================*/ +static void OpalOperator_SetAttackRate(OpalOperator *self, uint16_t rate) +{ + self->AttackRate = rate; + OpalOperator_ComputeRates(self); +} + + + +/*================================================================================================== + * Operator decay rate. + *=================================================================================================*/ +static void OpalOperator_SetDecayRate(OpalOperator *self, uint16_t rate) +{ + self->DecayRate = rate; + OpalOperator_ComputeRates(self); +} + + + +/*================================================================================================== + * Operator sustain level. + *=================================================================================================*/ +static void OpalOperator_SetSustainLevel(OpalOperator *self, uint16_t level) +{ + self->SustainLevel = level < 15 ? level : 31; + self->SustainLevel *= 16; +} + + + +/*================================================================================================== + * Operator release rate. + *=================================================================================================*/ +static void OpalOperator_SetReleaseRate(OpalOperator *self, uint16_t rate) +{ + self->ReleaseRate = rate; + OpalOperator_ComputeRates(self); +} + + + +/*================================================================================================== + * Assign the waveform this operator will use. + *=================================================================================================*/ +static void OpalOperator_SetWaveform(OpalOperator *self, uint16_t wave) +{ + self->Waveform = wave & 7; +} + + + +/*================================================================================================== + * Compute actual rate from register rate. From the Yamaha data sheet: + * + * Actual rate = Rate value * 4 + Rof, if Rate value = 0, actual rate = 0 + * + * Rof is set as follows depending on the KSR setting: + * + * Key scale 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * KSR = 0 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 + * KSR = 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * + * Note: zero rates are infinite, and are treated separately elsewhere + *=================================================================================================*/ +static void OpalOperator_ComputeRates(OpalOperator *self) +{ + int combined_rate = self->AttackRate * 4 + (OpalChannel_GetKeyScaleNumber(self->Chan) >> (self->KeyScaleRate ? 0 : 2)); + int rate_high = combined_rate >> 2; + int rate_low = combined_rate & 3; + + self->AttackShift = rate_high < 12 ? 12 - rate_high : 0; + self->AttackMask = (1 << self->AttackShift) - 1; + self->AttackAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + self->AttackTab = RateTables[rate_low]; + + /* Attack rate of 15 is always instant */ + if(self->AttackRate == 15) + self->AttackAdd = 0xFFF; + + combined_rate = self->DecayRate * 4 + (OpalChannel_GetKeyScaleNumber(self->Chan) >> (self->KeyScaleRate ? 0 : 2)); + rate_high = combined_rate >> 2; + rate_low = combined_rate & 3; + + self->DecayShift = rate_high < 12 ? 12 - rate_high : 0; + self->DecayMask = (1 << self->DecayShift) - 1; + self->DecayAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + self->DecayTab = RateTables[rate_low]; + + combined_rate = self->ReleaseRate * 4 + (OpalChannel_GetKeyScaleNumber(self->Chan) >> (self->KeyScaleRate ? 0 : 2)); + rate_high = combined_rate >> 2; + rate_low = combined_rate & 3; + + self->ReleaseShift = rate_high < 12 ? 12 - rate_high : 0; + self->ReleaseMask = (1 << self->ReleaseShift) - 1; + self->ReleaseAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + self->ReleaseTab = RateTables[rate_low]; +} + + +static const uint16_t levtab[] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 16, 20, 24, 28, 32, + 0, 0, 0, 0, 0, 12, 20, 28, 32, 40, 44, 48, 52, 56, 60, 64, + 0, 0, 0, 20, 32, 44, 52, 60, 64, 72, 76, 80, 84, 88, 92, 96, + 0, 0, 32, 52, 64, 76, 84, 92, 96, 104, 108, 112, 116, 120, 124, 128, + 0, 32, 64, 84, 96, 108, 116, 124, 128, 136, 140, 144, 148, 152, 156, 160, + 0, 64, 96, 116, 128, 140, 148, 156, 160, 168, 172, 176, 180, 184, 188, 192, + 0, 96, 128, 148, 160, 172, 180, 188, 192, 200, 204, 208, 212, 216, 220, 224, +}; + +/*=================================================================================================* + * Compute the operator's key scale level. This changes based on the channel frequency/octave and * + * operator key scale value. * + *=================================================================================================*/ +static void OpalOperator_ComputeKeyScaleLevel(OpalOperator *self) +{ + /* This uses a combined value of the top four bits of frequency with the octave/block */ + uint16_t i = (OpalChannel_GetOctave(self->Chan) << 4) | (OpalChannel_GetFreq(self->Chan) >> 6); + self->KeyScaleLevel = levtab[i] >> self->KeyScaleShift; +} diff --git a/thirdparty/adlmidi/chips/opal/opal.h b/thirdparty/adlmidi/chips/opal/opal.h new file mode 100644 index 000000000..ef01069ba --- /dev/null +++ b/thirdparty/adlmidi/chips/opal/opal.h @@ -0,0 +1,165 @@ +/* + + The Opal OPL3 emulator. + + Note: this is not a complete emulator, just enough for Reality Adlib Tracker tunes. + + Missing features compared to a real OPL3: + + - Timers/interrupts + - OPL3 enable bit (it defaults to always on) + - CSW mode + - Test register + - Percussion mode + +*/ + +#ifndef OPAL_HHHH +#define OPAL_HHHH + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OPAL_TRUE 1 +#define OPAL_FALSE 0 +typedef int opal_bool; + +typedef struct OpalChannel_t OpalChannel; +typedef struct OpalOperator_t OpalOperator; +typedef struct Opal_t Opal; + +/* Various constants */ +typedef enum OpalEnum_t +{ + OpalOPL3SampleRate = 49716, + OpalNumChannels = 18, + OpalNumOperators = 36 +} OpalEnum; + +/* A single FM operator */ +struct OpalOperator_t +{ + Opal* Master; /* Master object */ + OpalChannel* Chan; /* Owning channel */ + uint32_t Phase; /* The current offset in the selected waveform */ + uint16_t Waveform; /* The waveform id this operator is using */ + uint16_t FreqMultTimes2; /* Frequency multiplier * 2 */ + int EnvelopeStage; /* Which stage the envelope is at (see Env* enums above) */ + int16_t EnvelopeLevel; /* 0 - $1FF, 0 being the loudest */ + uint16_t OutputLevel; /* 0 - $FF */ + uint16_t AttackRate; + uint16_t DecayRate; + uint16_t SustainLevel; + uint16_t ReleaseRate; + uint16_t AttackShift; + uint16_t AttackMask; + uint16_t AttackAdd; + const uint16_t* AttackTab; + uint16_t DecayShift; + uint16_t DecayMask; + uint16_t DecayAdd; + const uint16_t* DecayTab; + uint16_t ReleaseShift; + uint16_t ReleaseMask; + uint16_t ReleaseAdd; + const uint16_t* ReleaseTab; + uint16_t KeyScaleShift; + uint16_t KeyScaleLevel; + int16_t Out[2]; + opal_bool KeyOn; + opal_bool KeyScaleRate; /* Affects envelope rate scaling */ + opal_bool SustainMode; /* Whether to sustain during the sustain phase, or release instead */ + opal_bool TremoloEnable; + opal_bool VibratoEnable; +}; + +/* A single channel, which can contain two or more operators */ +struct OpalChannel_t +{ + OpalOperator* Op[4]; + + Opal* Master; /* Master object */ + uint16_t Freq; /* Frequency; actually it's a phase stepping value */ + uint16_t Octave; /* Also known as "block" in Yamaha parlance */ + uint32_t PhaseStep; + uint16_t KeyScaleNumber; + uint16_t FeedbackShift; + uint16_t ModulationType; + OpalChannel* ChannelPair; + opal_bool Enable; + opal_bool LeftEnable, RightEnable; + uint16_t LeftPan, RightPan; +}; + + +/*================================================================================================== + Opal instance. + ==================================================================================================*/ +struct Opal_t +{ + int32_t SampleRate; + int32_t SampleAccum; + int16_t LastOutput[2], CurrOutput[2]; + OpalChannel Chan[OpalNumChannels]; + OpalOperator Op[OpalNumOperators]; + uint16_t Clock; + uint16_t TremoloClock; + uint16_t TremoloLevel; + uint16_t VibratoTick; + uint16_t VibratoClock; + opal_bool NoteSel; + opal_bool TremoloDepth; + opal_bool VibratoDepth; +}; + + +/*================================================================================================== + Public API. + ==================================================================================================*/ +/*! + * \brief Initialize Opal with a given output sample rate + * \param self Pointer to the Opal instance + * \param sample_rate Desired output sample rate + */ +extern void Opal_Init(Opal *self, int sample_rate); + +/*! + * \brief Change the output sample rate of existing initialized instance + * \param self Pointer to the Opal instance + * \param sample_rate Desired output sample rate + */ +extern void Opal_SetSampleRate(Opal *self, int sample_rate); + +/*! + * \brief Send the register data to the Opal + * \param self Pointer to the Opal instance + * \param reg_num OPL3 Register address + * \param val Value to write + */ +extern void Opal_Port(Opal *self, uint16_t reg_num, uint8_t val); + +/*! + * \brief Panorama level per channel + * \param self Pointer to the Opal instance + * \param reg_num Channel number (multiplied by 256) + * \param pan Panorama level (0 - left, 64 - middle, 127 - right) + */ +extern void Opal_Pan(Opal *self, uint16_t reg_num, uint8_t pan); + +/*! + * \brief Generate one 16-bit PCM sample in system endian (there are two integers will be written) + * \param self Pointer to the Opal instance + * \param left Sample for the left chanel + * \param right Sample for the right channel + */ +extern void Opal_Sample(Opal *self, int16_t* left, int16_t* right); + + +#ifdef __cplusplus +} +#endif + +#endif /* OPAL_HHHH */ diff --git a/thirdparty/adlmidi/chips/opal_opl3.cpp b/thirdparty/adlmidi/chips/opal_opl3.cpp new file mode 100644 index 000000000..f2a377c52 --- /dev/null +++ b/thirdparty/adlmidi/chips/opal_opl3.cpp @@ -0,0 +1,85 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "opal_opl3.h" +#include "opal/opal.h" +#include + +OpalOPL3::OpalOPL3() : + OPLChipBaseT() +{ + m_chip = new Opal; + OpalOPL3::setRate(m_rate); +} + +OpalOPL3::~OpalOPL3() +{ + Opal *chip_r = reinterpret_cast(m_chip); + delete chip_r; +} + +void OpalOPL3::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + Opal *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(Opal)); + Opal_Init(chip_r, effectiveRate()); +} + +void OpalOPL3::reset() +{ + OPLChipBaseT::reset(); + Opal *chip_r = reinterpret_cast(m_chip); + std::memset(chip_r, 0, sizeof(Opal)); + Opal_Init(chip_r, effectiveRate()); +} + +void OpalOPL3::writeReg(uint16_t addr, uint8_t data) +{ + Opal *chip_r = reinterpret_cast(m_chip); + Opal_Port(chip_r, addr, data); +} + +void OpalOPL3::writePan(uint16_t addr, uint8_t data) +{ + Opal *chip_r = reinterpret_cast(m_chip); + Opal_Pan(chip_r, addr, data); +} + +void OpalOPL3::nativeGenerate(int16_t *frame) +{ + Opal *chip_r = reinterpret_cast(m_chip); + Opal_Sample(chip_r, &frame[0], &frame[1]); +} + +const char *OpalOPL3::emulatorName() +{ + return "Opal OPL3"; +} + +bool OpalOPL3::hasFullPanning() +{ + return true; +} + +OPLChipBase::ChipType OpalOPL3::chipType() +{ + return CHIPTYPE_OPL3; +} diff --git a/thirdparty/adlmidi/chips/opal_opl3.h b/thirdparty/adlmidi/chips/opal_opl3.h new file mode 100644 index 000000000..6f1e573f6 --- /dev/null +++ b/thirdparty/adlmidi/chips/opal_opl3.h @@ -0,0 +1,46 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OPAL_OPL3_H +#define OPAL_OPL3_H + +#include "opl_chip_base.h" + +class OpalOPL3 final : public OPLChipBaseT +{ + void *m_chip; +public: + OpalOPL3(); + ~OpalOPL3() override; + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // NUKED_OPL3_H diff --git a/thirdparty/adlmidi/chips/opl_chip_base.h b/thirdparty/adlmidi/chips/opl_chip_base.h new file mode 100644 index 000000000..718839a40 --- /dev/null +++ b/thirdparty/adlmidi/chips/opl_chip_base.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ONP_CHIP_BASE_H +#define ONP_CHIP_BASE_H + +#include +#include + +#if !defined(_MSC_VER) && (__cplusplus <= 199711L) +#define final +#define override +#endif + +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) +class VResampler; +#endif + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); +#endif + +class OPLChipBase +{ +public: + enum { nativeRate = 49716 }; + enum ChipType + { + CHIPTYPE_OPL3 = 0, + CHIPTYPE_OPL2 = 1, + CHIPTYPE_ESFM = 2 + }; +protected: + uint32_t m_id; + uint32_t m_rate; +public: + OPLChipBase(); + virtual ~OPLChipBase(); + + uint32_t chipId() const { return m_id; } + void setChipId(uint32_t id) { m_id = id; } + + virtual bool canRunAtPcmRate() const = 0; + virtual bool isRunningAtPcmRate() const = 0; + virtual bool setRunningAtPcmRate(bool r) = 0; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + virtual void setAudioTickHandlerInstance(void *instance) = 0; +#endif + + virtual void setRate(uint32_t rate) = 0; + virtual uint32_t effectiveRate() const = 0; + virtual void reset() = 0; + virtual void writeReg(uint16_t addr, uint8_t data) = 0; + + // extended + virtual void writePan(uint16_t addr, uint8_t data) { (void)addr; (void)data; } + + virtual void nativePreGenerate() = 0; + virtual void nativePostGenerate() = 0; + virtual void nativeGenerate(int16_t *frame) = 0; + virtual void resampledGenerate(int32_t *frame) = 0; + + virtual void generate(int16_t *output, size_t frames) = 0; + virtual void generateAndMix(int16_t *output, size_t frames) = 0; + virtual void generate32(int32_t *output, size_t frames) = 0; + virtual void generateAndMix32(int32_t *output, size_t frames) = 0; + + virtual const char* emulatorName() = 0; + virtual ChipType chipType() = 0; + /** + * @brief Does emulator has the per-channel full-panning extension? + * @return true if emulator has this extension, false if emulator has only original behaviour + */ + virtual bool hasFullPanning() = 0; +private: + OPLChipBase(const OPLChipBase &c); + OPLChipBase &operator=(const OPLChipBase &c); +}; + +// A base class providing F-bounded generic and efficient implementations, +// supporting resampling of chip outputs +template +class OPLChipBaseT : public OPLChipBase +{ +public: + OPLChipBaseT(); + virtual ~OPLChipBaseT(); + + bool isRunningAtPcmRate() const override; + bool setRunningAtPcmRate(bool r) override; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + void setAudioTickHandlerInstance(void *instance); +#endif + + virtual void setRate(uint32_t rate) override; + uint32_t effectiveRate() const override; + virtual void reset() override; + void generate(int16_t *output, size_t frames) override; + void generateAndMix(int16_t *output, size_t frames) override; + void generate32(int32_t *output, size_t frames) override; + void generateAndMix32(int32_t *output, size_t frames) override; +private: + bool m_runningAtPcmRate; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + void *m_audioTickHandlerInstance; +#endif + void nativeTick(int16_t *frame); + void setupResampler(uint32_t rate); + void resetResampler(); + void resampledGenerate(int32_t *output) override; +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + VResampler *m_resampler; +#else + int32_t m_oldsamples[2]; + int32_t m_samples[2]; + int32_t m_samplecnt; + int32_t m_rateratio; + enum { rsm_frac = 10 }; +#endif + // amplitude scale factors in and out of resampler, varying for chips; + // values are OK to "redefine", the static polymorphism will accept it. + enum { resamplerPreAmplify = 1, resamplerPostAttenuate = 1 }; +}; + +// A base class which provides frame-by-frame interfaces on emulations which +// don't have a routine for it. It produces outputs in fixed size buffers. +// Fast register updates will suffer some latency because of buffering. +template +class OPLChipBaseBufferedT : public OPLChipBaseT +{ +public: + OPLChipBaseBufferedT() + : OPLChipBaseT(), m_bufferIndex(0) {} + virtual ~OPLChipBaseBufferedT() + {} +public: + void reset() override; + void nativeGenerate(int16_t *frame) override; +protected: + virtual void nativeGenerateN(int16_t *output, size_t frames) = 0; +private: + unsigned m_bufferIndex; + int16_t m_buffer[2 * Buffer]; +}; + +#include "opl_chip_base.tcc" + +#endif // ONP_CHIP_BASE_H diff --git a/thirdparty/adlmidi/chips/opl_chip_base.tcc b/thirdparty/adlmidi/chips/opl_chip_base.tcc new file mode 100644 index 000000000..e6d3f6b1e --- /dev/null +++ b/thirdparty/adlmidi/chips/opl_chip_base.tcc @@ -0,0 +1,294 @@ +#include "opl_chip_base.h" +#include + +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) +#include +#endif + +#if !defined(LIKELY) && defined(__GNUC__) +#define LIKELY(x) __builtin_expect((x), 1) +#elif !defined(LIKELY) +#define LIKELY(x) (x) +#endif + +#if !defined(UNLIKELY) && defined(__GNUC__) +#define UNLIKELY(x) __builtin_expect((x), 0) +#elif !defined(UNLIKELY) +#define UNLIKELY(x) (x) +#endif + +/* OPLChipBase */ + +inline OPLChipBase::OPLChipBase() : + m_id(0), + m_rate(44100) +{ +} + +inline OPLChipBase::~OPLChipBase() +{ +} + +/* OPLChipBaseT */ + +template +OPLChipBaseT::OPLChipBaseT() + : OPLChipBase(), + m_runningAtPcmRate(false) +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + , + m_audioTickHandlerInstance(NULL) +#endif +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + m_resampler = new VResampler; +#endif + setupResampler(m_rate); +} + +template +OPLChipBaseT::~OPLChipBaseT() +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + delete m_resampler; +#endif +} + +template +bool OPLChipBaseT::isRunningAtPcmRate() const +{ + return m_runningAtPcmRate; +} + +template +bool OPLChipBaseT::setRunningAtPcmRate(bool r) +{ + if(r != m_runningAtPcmRate) + { + if(r && !static_cast(this)->canRunAtPcmRate()) + return false; + m_runningAtPcmRate = r; + static_cast(this)->setRate(m_rate); + } + return true; +} + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +template +void OPLChipBaseT::setAudioTickHandlerInstance(void *instance) +{ + m_audioTickHandlerInstance = instance; +} +#endif + +template +void OPLChipBaseT::setRate(uint32_t rate) +{ + uint32_t oldRate = m_rate; + m_rate = rate; + if(rate != oldRate) + setupResampler(rate); + else + resetResampler(); +} + +template +uint32_t OPLChipBaseT::effectiveRate() const +{ + return m_runningAtPcmRate ? m_rate : (uint32_t)nativeRate; +} + +template +void OPLChipBaseT::reset() +{ + resetResampler(); +} + +template +void OPLChipBaseT::generate(int16_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + for (unsigned c = 0; c < 2; ++c) { + int32_t temp = frame[c]; + temp = (temp > -32768) ? temp : -32768; + temp = (temp < 32767) ? temp : 32767; + output[c] = (int16_t)temp; + } + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::generateAndMix(int16_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + for (unsigned c = 0; c < 2; ++c) { + int32_t temp = (int32_t)output[c] + frame[c]; + temp = (temp > -32768) ? temp : -32768; + temp = (temp < 32767) ? temp : 32767; + output[c] = (int16_t)temp; + } + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::generate32(int32_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + static_cast(this)->resampledGenerate(output); + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::generateAndMix32(int32_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + output[0] += frame[0]; + output[1] += frame[1]; + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPLChipBaseT::nativeTick(int16_t *frame) +{ +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + adl_audioTickHandler(m_audioTickHandlerInstance, m_id, effectiveRate()); +#endif + static_cast(this)->nativeGenerate(frame); +} + +template +void OPLChipBaseT::setupResampler(uint32_t rate) +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + m_resampler->setup(rate * (1.0 / 49716), 2, 48); +#else + m_oldsamples[0] = m_oldsamples[1] = 0; + m_samples[0] = m_samples[1] = 0; + m_samplecnt = 0; + m_rateratio = (int32_t)((rate << rsm_frac) / 49716); +#endif +} + +template +void OPLChipBaseT::resetResampler() +{ +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) + m_resampler->reset(); +#else + m_oldsamples[0] = m_oldsamples[1] = 0; + m_samples[0] = m_samples[1] = 0; + m_samplecnt = 0; +#endif +} + +#if defined(ADLMIDI_ENABLE_HQ_RESAMPLER) +template +void OPLChipBaseT::resampledGenerate(int32_t *output) +{ + if(UNLIKELY(m_runningAtPcmRate)) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + return; + } + + VResampler *rsm = m_resampler; + float scale = (float)T::resamplerPreAmplify / + (float)T::resamplerPostAttenuate; + float f_in[2]; + float f_out[2]; + rsm->inp_count = 0; + rsm->inp_data = f_in; + rsm->out_count = 1; + rsm->out_data = f_out; + while(rsm->process(), rsm->out_count != 0) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + f_in[0] = scale * (float)in[0]; + f_in[1] = scale * (float)in[1]; + rsm->inp_count = 1; + rsm->inp_data = f_in; + rsm->out_count = 1; + rsm->out_data = f_out; + } + output[0] = static_cast(lround(f_out[0])); + output[1] = static_cast(lround(f_out[1])); +} +#else +template +void OPLChipBaseT::resampledGenerate(int32_t *output) +{ + if(UNLIKELY(m_runningAtPcmRate)) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + return; + } + + int32_t samplecnt = m_samplecnt; + const int32_t rateratio = m_rateratio; + while(samplecnt >= rateratio) + { + m_oldsamples[0] = m_samples[0]; + m_oldsamples[1] = m_samples[1]; + int16_t buffer[2]; + static_cast(this)->nativeTick(buffer); + m_samples[0] = buffer[0] * T::resamplerPreAmplify; + m_samples[1] = buffer[1] * T::resamplerPreAmplify; + samplecnt -= rateratio; + } + output[0] = (int32_t)(((m_oldsamples[0] * (rateratio - samplecnt) + + m_samples[0] * samplecnt) / rateratio)/T::resamplerPostAttenuate); + output[1] = (int32_t)(((m_oldsamples[1] * (rateratio - samplecnt) + + m_samples[1] * samplecnt) / rateratio)/T::resamplerPostAttenuate); + m_samplecnt = samplecnt + (1 << rsm_frac); +} +#endif + +/* OPLChipBaseBufferedT */ + +template +void OPLChipBaseBufferedT::reset() +{ + OPLChipBaseT::reset(); + m_bufferIndex = 0; +} + +template +void OPLChipBaseBufferedT::nativeGenerate(int16_t *frame) +{ + unsigned bufferIndex = m_bufferIndex; + if(bufferIndex == 0) + static_cast(this)->nativeGenerateN(m_buffer, Buffer); + frame[0] = m_buffer[2 * bufferIndex]; + frame[1] = m_buffer[2 * bufferIndex + 1]; + bufferIndex = (bufferIndex + 1 < Buffer) ? (bufferIndex + 1) : 0; + m_bufferIndex = bufferIndex; +} diff --git a/thirdparty/adlmidi/chips/opl_serial_misc.h b/thirdparty/adlmidi/chips/opl_serial_misc.h new file mode 100644 index 000000000..9fcd2e9fe --- /dev/null +++ b/thirdparty/adlmidi/chips/opl_serial_misc.h @@ -0,0 +1,350 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OPL_SERIAL_MISC_H +#define OPL_SERIAL_MISC_H + +#if defined( __unix__) || defined(__APPLE__) +#include +#include +#include +#include +#include +#include +#include +#endif + +#ifdef __APPLE__ +#include +#endif + +#ifdef _WIN32 +#include +#endif + +#include + + +class ChipSerialPortBase +{ +public: + ChipSerialPortBase() {} + virtual ~ChipSerialPortBase() {} + + virtual bool isOpen() + { + return false; + } + + virtual void close() {} + + virtual bool open(const std::string &/*portName*/, unsigned /*baudRate*/) + { + return false; + } + + virtual int write(uint8_t * /*data*/, size_t /*size*/) + { + return 0; + } +}; + + + +#if defined( __unix__) || defined(__APPLE__) +class ChipSerialPort : public ChipSerialPortBase +{ + int m_port; + struct termios m_portSetup; + + static unsigned int baud2enum(unsigned int baud) + { + if(baud == 0) + return B0; + else if(baud <= 50) + return B50; + else if(baud <= 75) + return B75; + else if(baud <= 110) + return B110; + else if(baud <= 134) + return B134; + else if(baud <= 150) + return B150; + else if(baud <= 200) + return B200; + else if(baud <= 300) + return B300; + else if(baud <= 600) + return B600; + else if(baud <= 1200) + return B1200; + else if(baud <= 1800) + return B1800; + else if(baud <= 2400) + return B2400; + else if(baud <= 4800) + return B4800; + else if(baud <= 9600) + return B9600; + else if(baud <= 19200) + return B19200; + else if(baud <= 38400) + return B38400; + else if(baud <= 57600) + return B57600; + else if(baud <= 115200) + return B115200; + else + return B230400; + } + +public: + ChipSerialPort() : + ChipSerialPortBase() + { + m_port = 0; + std::memset(&m_portSetup, 0, sizeof(struct termios)); + } + + virtual ~ChipSerialPort() + { + close(); + } + + bool isOpen() + { + return m_port != 0; + } + + void close() + { + if(m_port) + ::close(m_port); + + m_port = 0; + } + + bool open(const std::string &portName, unsigned baudRate) + { + if(m_port) + this->close(); + + std::string portPath = "/dev/" + portName; + m_port = ::open(portPath.c_str(), O_WRONLY); + + if(m_port < 0) + { + std::fprintf(stderr, "-- OPL Serial ERROR: failed to open tty device `%s': %s\n", portPath.c_str(), strerror(errno)); + std::fflush(stderr); + m_port = 0; + return false; + } + + if(tcgetattr(m_port, &m_portSetup) != 0) + { + std::fprintf(stderr, "-- OPL Serial ERROR: failed to retrieve setup `%s': %s\n", portPath.c_str(), strerror(errno)); + std::fflush(stderr); + close(); + return false; + } + + m_portSetup.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + m_portSetup.c_oflag &= ~OPOST; + m_portSetup.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + m_portSetup.c_cflag &= ~(CSIZE | PARENB); + m_portSetup.c_cflag |= CS8; + +#if defined (__linux__) || defined (__CYGWIN__) + m_portSetup.c_cflag &= ~CBAUD; +#endif + +#if defined (BSD) || defined(__FreeBSD__) + cfsetispeed(&m_portSetup, baudRate); + cfsetospeed(&m_portSetup, baudRate); +#elif !defined(__APPLE__) + cfsetospeed(&m_portSetup, baud2enum(baudRate)); +#endif + + if(tcsetattr(m_port, TCSANOW, &m_portSetup) != 0) + { + std::fprintf(stderr, "-- OPL Serial ERROR: failed to apply setup `%s': %s\n", portPath.c_str(), strerror(errno)); + std::fflush(stderr); + close(); + return false; + } + +#ifdef __APPLE__ + if(ioctl(m_port, IOSSIOSPEED, &baudRate) == -1) + { + std::fprintf(stderr, "-- OPL Serial ERROR: Failed to set MacOS specific tty attributes for `%s': %s", portPath.c_str(), strerror(errno)); + std::fflush(stderr); + close(); + return false; + } +#endif + + return true; + } + + int write(uint8_t *data, size_t size) + { + if(!m_port) + return 0; + + return ::write(m_port, data, size); + } +}; + +#endif // __unix__ + + + + +#ifdef _WIN32 + +class ChipSerialPort : public ChipSerialPortBase +{ + HANDLE m_port; + + static unsigned int baud2enum(unsigned int baud) + { + if(baud <= 110) + return CBR_110; + else if(baud <= 300) + return CBR_300; + else if(baud <= 600) + return CBR_600; + else if(baud <= 1200) + return CBR_1200; + else if(baud <= 2400) + return CBR_2400; + else if(baud <= 4800) + return CBR_4800; + else if(baud <= 9600) + return CBR_9600; + else if(baud <= 19200) + return CBR_19200; + else if(baud <= 38400) + return CBR_38400; + else if(baud <= 57600) + return CBR_57600; + else if(baud <= 115200) + return CBR_115200; + else + return CBR_115200; + } + +public: + ChipSerialPort() : ChipSerialPortBase() + { + m_port = NULL; + } + + virtual ~ChipSerialPort() + { + close(); + } + + bool isOpen() + { + return m_port != NULL; + } + + void close() + { + if(m_port) + CloseHandle(m_port); + + m_port = NULL; + } + + bool open(const std::string &portName, unsigned baudRate) + { + if(m_port) + this->close(); + + std::string portPath = "\\\\.\\" + portName; +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + std::wstring wportPath; + wportPath.resize(portPath.size()); + int newSize = MultiByteToWideChar(CP_UTF8, + 0, + portPath.c_str(), + (int)portPath.size(), + (wchar_t *)wportPath.c_str(), + (int)wportPath.size()); + m_port = CreateFile2(wportPath.c_str(), GENERIC_WRITE, 0, OPEN_EXISTING, NULL); +#else + m_port = CreateFileA(portPath.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); +#endif + + if(m_port == INVALID_HANDLE_VALUE) + { + m_port = NULL; + return false; + } + + DCB dcb; + BOOL succ; + + SecureZeroMemory(&dcb, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + + succ = GetCommState(m_port, &dcb); + if(!succ) + { + this->close(); + return false; + } + + dcb.BaudRate = baud2enum(baudRate); + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.StopBits = ONESTOPBIT; + + succ = SetCommState(m_port, &dcb); + + if(!succ) + { + this->close(); + return false; + } + + return true; + } + + int write(uint8_t *data, size_t size) + { + if(!m_port) + return 0; + + DWORD written = 0; + + if(!WriteFile(m_port, data, size, &written, 0)) + return 0; + + return written; + } +}; + +#endif // _WIN32 + +#endif // OPL_SERIAL_MISC_H diff --git a/thirdparty/adlmidi/chips/opl_serial_port.cpp b/thirdparty/adlmidi/chips/opl_serial_port.cpp new file mode 100644 index 000000000..d491d696d --- /dev/null +++ b/thirdparty/adlmidi/chips/opl_serial_port.cpp @@ -0,0 +1,171 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifdef ENABLE_HW_OPL_SERIAL_PORT + +#include "opl_serial_port.h" +#include "opl_serial_misc.h" + + +static size_t retrowave_protocol_serial_pack(const uint8_t *buf_in, size_t len_in, uint8_t *buf_out) +{ + size_t in_cursor = 0; + size_t out_cursor = 0; + + buf_out[out_cursor] = 0x00; + out_cursor += 1; + + uint8_t shift_count = 0; + + while(in_cursor < len_in) + { + uint8_t cur_byte_out = buf_in[in_cursor] >> shift_count; + if(in_cursor > 0) + cur_byte_out |= (buf_in[in_cursor - 1] << (8 - shift_count)); + + cur_byte_out |= 0x01; + buf_out[out_cursor] = cur_byte_out; + + shift_count += 1; + in_cursor += 1; + out_cursor += 1; + if(shift_count > 7) + { + shift_count = 0; + in_cursor -= 1; + } + } + + if(shift_count) + { + buf_out[out_cursor] = buf_in[in_cursor - 1] << (8 - shift_count); + buf_out[out_cursor] |= 0x01; + out_cursor += 1; + } + + buf_out[out_cursor] = 0x02; + out_cursor += 1; + + return out_cursor; +} + +OPL_SerialPort::OPL_SerialPort() + : m_port(NULL), m_protocol(ProtocolUnknown) +{} + +OPL_SerialPort::~OPL_SerialPort() +{ + delete m_port; + m_port = NULL; +} + +bool OPL_SerialPort::connectPort(const std::string& name, unsigned baudRate, unsigned protocol) +{ + delete m_port; + m_port = NULL; + + // ensure audio thread reads protocol atomically and in order, + // so chipType() will be correct after the port is live + m_protocol = protocol; + + // QSerialPort *port = m_port = new QSerialPort(name); + // port->setBaudRate(baudRate); + // return port->open(QSerialPort::WriteOnly); + + m_port = new ChipSerialPort; + return m_port->open(name, baudRate); +} + +bool OPL_SerialPort::hasFullPanning() +{ + return false; +} + +void OPL_SerialPort::writeReg(uint16_t addr, uint8_t data) +{ + uint8_t sendBuffer[16]; + ChipSerialPortBase *port = m_port; + + if(!port || !port->isOpen()) + return; + + switch(m_protocol) + { + default: + case ProtocolArduinoOPL2: + { + if(addr >= 0x100) + break; + sendBuffer[0] = (uint8_t)addr; + sendBuffer[1] = (uint8_t)data; + port->write(sendBuffer, 2); + break; + } + case ProtocolNukeYktOPL3: + { + sendBuffer[0] = (addr >> 6) | 0x80; + sendBuffer[1] = ((addr & 0x3f) << 1) | (data >> 7); + sendBuffer[2] = (data & 0x7f); + port->write(sendBuffer, 3); + break; + } + case ProtocolRetroWaveOPL3: + { + bool port1 = (addr & 0x100) != 0; + uint8_t buf[8] = + { + static_cast(0x21 << 1), 0x12, + static_cast(port1 ? 0xe5 : 0xe1), static_cast(addr & 0xff), + static_cast(port1 ? 0xe7 : 0xe3), static_cast(data), + 0xfb, static_cast(data) + }; + size_t packed_len = retrowave_protocol_serial_pack(buf, sizeof(buf), sendBuffer); + port->write(sendBuffer, packed_len); + break; + } + } +} + +void OPL_SerialPort::nativeGenerate(int16_t *frame) +{ + frame[0] = 0; + frame[1] = 0; +} + +const char *OPL_SerialPort::emulatorName() +{ + return "OPL Serial Port Driver"; +} + +OPLChipBase::ChipType OPL_SerialPort::chipType() +{ + switch(m_protocol) + { + default: + case ProtocolArduinoOPL2: + return OPLChipBase::CHIPTYPE_OPL2; + case ProtocolNukeYktOPL3: + case ProtocolRetroWaveOPL3: + return OPLChipBase::CHIPTYPE_OPL3; + } +} + +#endif // ENABLE_HW_OPL_SERIAL_PORT diff --git a/thirdparty/adlmidi/chips/opl_serial_port.h b/thirdparty/adlmidi/chips/opl_serial_port.h new file mode 100644 index 000000000..927a5e414 --- /dev/null +++ b/thirdparty/adlmidi/chips/opl_serial_port.h @@ -0,0 +1,66 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef OPL_SERIAL_PORT_H +#define OPL_SERIAL_PORT_H + +#ifdef ENABLE_HW_OPL_SERIAL_PORT + +#include +#include "opl_chip_base.h" + +class ChipSerialPortBase; + +class OPL_SerialPort : public OPLChipBaseT +{ +public: + OPL_SerialPort(); + virtual ~OPL_SerialPort() override; + + enum Protocol + { + ProtocolUnknown, + ProtocolArduinoOPL2, + ProtocolNukeYktOPL3, + ProtocolRetroWaveOPL3 + }; + + bool connectPort(const std::string &name, unsigned baudRate, unsigned protocol); + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t /*rate*/) override {} + void reset() override {} + void writeReg(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; + +private: + ChipSerialPortBase *m_port; + int m_protocol; +}; + +#endif // ENABLE_HW_OPL_SERIAL_PORT + +#endif // OPL_SERIAL_PORT_H diff --git a/thirdparty/adlmidi/chips/ym3812_lle.cpp b/thirdparty/adlmidi/chips/ym3812_lle.cpp new file mode 100644 index 000000000..8e39bf61e --- /dev/null +++ b/thirdparty/adlmidi/chips/ym3812_lle.cpp @@ -0,0 +1,82 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ym3812_lle.h" +#include "ym3812_lle/nuked_fmopl2.h" +#include "ym3812_lle/nopl2.h" +#include + +Ym3812LLEOPL2::Ym3812LLEOPL2() : + OPLChipBaseT() +{ + m_chip = nopl2_init(14318182, m_rate); + setRate(m_rate); +} + +Ym3812LLEOPL2::~Ym3812LLEOPL2() +{ + fmopl2_t *chip_r = reinterpret_cast(m_chip); + nopl2_shutdown(chip_r); +} + +void Ym3812LLEOPL2::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + nopl2_set_rate(m_chip, 14318182, rate); +} + +void Ym3812LLEOPL2::reset() +{ + OPLChipBaseT::reset(); + nopl2_reset(m_chip); +} + +void Ym3812LLEOPL2::writeReg(uint16_t addr, uint8_t data) +{ + nopl2_write(m_chip, 2 * ((addr >> 8) & 3), addr); + nopl2_write(m_chip, 1, data); +} + +void Ym3812LLEOPL2::writePan(uint16_t addr, uint8_t data) +{ + (void)addr; + (void)data; + // Uninmplemented +} + +void Ym3812LLEOPL2::nativeGenerate(int16_t *frame) +{ + nopl2_getsample_one_native(m_chip, frame); +} + +const char *Ym3812LLEOPL2::emulatorName() +{ + return "YM3812-LLE OPL2"; +} + +bool Ym3812LLEOPL2::hasFullPanning() +{ + return false; +} + +OPLChipBase::ChipType Ym3812LLEOPL2::chipType() +{ + return CHIPTYPE_OPL2; +} diff --git a/thirdparty/adlmidi/chips/ym3812_lle.h b/thirdparty/adlmidi/chips/ym3812_lle.h new file mode 100644 index 000000000..f1937345a --- /dev/null +++ b/thirdparty/adlmidi/chips/ym3812_lle.h @@ -0,0 +1,46 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef YM3812LLE_OPL3_H +#define YM3812LLE_OPL3_H + +#include "opl_chip_base.h" + +class Ym3812LLEOPL2 final : public OPLChipBaseT +{ + void *m_chip; +public: + Ym3812LLEOPL2(); + ~Ym3812LLEOPL2() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // YM3812LLE_OPL3_H diff --git a/thirdparty/adlmidi/chips/ym3812_lle/nopl2.c b/thirdparty/adlmidi/chips/ym3812_lle/nopl2.c new file mode 100644 index 000000000..7dfb8dd7f --- /dev/null +++ b/thirdparty/adlmidi/chips/ym3812_lle/nopl2.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YM3812-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * YM3812 emulator + * Thanks: + * Travis Goodspeed: + * YM3812 decap and die shot + * + */ + +#include "nuked_fmopl2.h" +#include +#include +#include "nopl2.h" + +#define OPL_WRITEBUF_SIZE 2048 +#define OPL_WRITEBUF_DELAY 1 +#define RSM_FRAC 10 + +typedef struct _opl2_writebuf { + uint64_t time; + uint8_t reg; + uint8_t data; +} opl2_writebuf; + +typedef struct { + fmopl2_t chip; + + int sample; + int o_sy; + int shifter; + int o_sh; + + int32_t rateratio; + int32_t samplecnt; + int oldsample; + + uint64_t writebuf_samplecnt; + uint32_t writebuf_cur; + uint32_t writebuf_last; + uint64_t writebuf_lasttime; + opl2_writebuf writebuf[OPL_WRITEBUF_SIZE]; +} nopl2_t; + +void nopl2_cycle(nopl2_t *chip) +{ + int i, mant, shift; + + for (i = 0; i < 144; i++) + { + chip->chip.input.mclk = i & 1; + FMOPL2_Clock(&chip->chip); + + if (!chip->o_sy && chip->chip.o_sy) + { + + if (chip->o_sh && !chip->chip.o_sh) + { + mant = chip->shifter & 0x3ff; + mant -= 512; + shift = (chip->shifter >> 10) & 7; + + chip->sample = 0; + if (shift) + { + shift--; + chip->sample = mant << shift; + } + } + chip->shifter = (chip->shifter >> 1) | (chip->chip.o_mo << 12); + + chip->o_sh = chip->chip.o_sh; + } + + chip->o_sy = chip->chip.o_sy; + } +} + +void *nopl2_init(int clock, int samplerate) +{ + nopl2_t *chip = calloc(1, sizeof(nopl2_t)); + nopl2_set_rate(chip, clock, samplerate); + return chip; +} + +void nopl2_set_rate(void* chip_p, int clock, int samplerate) +{ + nopl2_t *chip = (nopl2_t*)chip_p; + + chip->chip.input.cs = 0; + chip->chip.input.ic = 1; + chip->chip.input.rd = 1; + chip->chip.input.wr = 1; + + samplerate *= 1; + + chip->rateratio = ((samplerate << RSM_FRAC) * (int64_t)72) / clock; + + /* printf("%i %i\n", clock, samplerate); */ + + nopl2_reset(chip); +} + +void nopl2_shutdown(void *chip) +{ + free(chip); +} + +void nopl2_reset(void *chip) +{ + nopl2_t* chip2 = chip; + int i = 0; + + chip2->chip.input.ic = 0; + for (i = 0; i < 100; i++) + { + nopl2_cycle(chip2); + } + chip2->chip.input.ic = 1; + for (i = 0; i < 100; i++) + { + nopl2_cycle(chip2); + } + +} + +void nopl2_write2(nopl2_t *chip, int port, int val) +{ + chip->chip.input.address = port; + chip->chip.input.data_i = val; + chip->chip.input.wr = 0; + FMOPL2_Clock(&chip->chip); /* propagate */ + chip->chip.input.wr = 1; + FMOPL2_Clock(&chip->chip); /* propagate */ +} + +void nopl2_getsample(void *chip, short *sndptr, int numsamples) +{ + nopl2_t* chip2 = chip; + int i, buf; + short *p = sndptr; + opl2_writebuf* writebuf; + + for (i = 0; i < numsamples; i++) + { + while (chip2->samplecnt >= chip2->rateratio) + { + chip2->oldsample = chip2->sample; + nopl2_cycle(chip2); + + while ((writebuf = &chip2->writebuf[chip2->writebuf_cur]), writebuf->time <= chip2->writebuf_samplecnt) + { + if (!(writebuf->reg & 2)) + break; + + writebuf->reg &= 1; + nopl2_write2(chip2, writebuf->reg, writebuf->data); + chip2->writebuf_cur = (chip2->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; + } + chip2->writebuf_samplecnt++; + chip2->samplecnt -= chip2->rateratio; + } + + buf = (chip2->oldsample * (chip2->rateratio - chip2->samplecnt) + + chip2->sample * chip2->samplecnt) / chip2->rateratio; + *p++ = buf; + *p++ = buf; + } +} + +void nopl2_getsample_one_native(void *chip, short *sndptr) +{ + nopl2_t* chip2 = chip; + short *p = sndptr; + opl2_writebuf* writebuf; + + chip2->oldsample = chip2->sample; + nopl2_cycle(chip2); + + while ((writebuf = &chip2->writebuf[chip2->writebuf_cur]), writebuf->time <= chip2->writebuf_samplecnt) + { + if (!(writebuf->reg & 2)) + break; + + writebuf->reg &= 1; + nopl2_write2(chip2, writebuf->reg, writebuf->data); + chip2->writebuf_cur = (chip2->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; + } + + chip2->writebuf_samplecnt++; + chip2->samplecnt -= chip2->rateratio; + chip2->samplecnt += 1 << RSM_FRAC; + + *p++ = chip2->sample; + *p++ = chip2->sample; +} + +void nopl2_write(void *chip, int port, int val) +{ + nopl2_t* chip2 = chip; + uint64_t time1, time2; + opl2_writebuf *writebuf; + uint32_t writebuf_last; + + writebuf_last = chip2->writebuf_last; + writebuf = &chip2->writebuf[writebuf_last]; + + if (writebuf->reg & 2) + { + nopl2_write2(chip2, writebuf->reg & 1, writebuf->data); + nopl2_cycle(chip2); + + chip2->writebuf_cur = (writebuf_last + 1) % OPL_WRITEBUF_SIZE; + chip2->writebuf_samplecnt = writebuf->time; + } + + writebuf->reg = port | 2; + writebuf->data = val; + time1 = chip2->writebuf_lasttime + OPL_WRITEBUF_DELAY; + time2 = chip2->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + writebuf->time = time1; + chip2->writebuf_lasttime = time1; + chip2->writebuf_last = (writebuf_last + 1) % OPL_WRITEBUF_SIZE; +} diff --git a/thirdparty/adlmidi/chips/ym3812_lle/nopl2.h b/thirdparty/adlmidi/chips/ym3812_lle/nopl2.h new file mode 100644 index 000000000..784f088d9 --- /dev/null +++ b/thirdparty/adlmidi/chips/ym3812_lle/nopl2.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YM3812-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * YM3812 emulator + * Thanks: + * Travis Goodspeed: + * YM3812 decap and die shot + * + */ + +#pragma once +#ifndef NOPL2_H +#define NOPL2_H + +#ifdef __cplusplus +extern "C" { +#endif + +void *nopl2_init(int clock, int samplerate); +void nopl2_set_rate(void *chip, int clock, int samplerate); +void nopl2_shutdown(void *chip); +void nopl2_reset(void *chip); + +void nopl2_getsample(void *chip, short *sndptr, int numsamples); +void nopl2_getsample_one_native(void *chip, short *sndptr); + +void nopl2_write(void *chip, int port, int val); + +#ifdef __cplusplus +} +#endif + +#endif /* NOPL2_H */ diff --git a/thirdparty/adlmidi/chips/ym3812_lle/nuked_fmopl2.c b/thirdparty/adlmidi/chips/ym3812_lle/nuked_fmopl2.c new file mode 100644 index 000000000..94ff509ac --- /dev/null +++ b/thirdparty/adlmidi/chips/ym3812_lle/nuked_fmopl2.c @@ -0,0 +1,1556 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YM3812-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * YM3812 emulator + * Thanks: + * Travis Goodspeed: + * YM3812 decap and die shot + * + */ + +#include "nuked_fmopl2.h" + +void FMOPL2_DoShiftRegisters(fmopl2_t *chip, int sel) +{ + int j; + int to = sel; + int from = sel ^ 1; + int rot = sel == 0 ? 1 : 0; +#define CH_ROTATE(x) rot ? ((x << 1) | ((x >> 8) & 1)) : x +#define OP_ROTATE(x) rot ? ((x << 1) | ((x >> 17) & 1)) : x + /* channel registers */ + + /* fnum */ + for (j = 0; j < 10; j++) + chip->ch_fnum[j][to] = CH_ROTATE(chip->ch_fnum[j][from]); + /* block */ + for (j = 0; j < 3; j++) + chip->ch_block[j][to] = CH_ROTATE(chip->ch_block[j][from]); + /* kon */ + chip->ch_keyon[to] = CH_ROTATE(chip->ch_keyon[from]); + /* connect */ + chip->ch_connect[to] = CH_ROTATE(chip->ch_connect[from]); + /* feedback */ + for (j = 0; j < 3; j++) + chip->ch_fb[j][to] = CH_ROTATE(chip->ch_fb[j][from]); + /* multi */ + for (j = 0; j < 4; j++) + chip->op_multi[j][to] = OP_ROTATE(chip->op_multi[j][from]); + /* ksr */ + chip->op_ksr[to] = OP_ROTATE(chip->op_ksr[from]); + /* egt */ + chip->op_egt[to] = OP_ROTATE(chip->op_egt[from]); + /* vib */ + chip->op_vib[to] = OP_ROTATE(chip->op_vib[from]); + /* am */ + chip->op_am[to] = OP_ROTATE(chip->op_am[from]); + /* tl */ + for (j = 0; j < 6; j++) + chip->op_tl[j][to] = OP_ROTATE(chip->op_tl[j][from]); + /* ksl */ + for (j = 0; j < 2; j++) + chip->op_ksl[j][to] = OP_ROTATE(chip->op_ksl[j][from]); + /* ar */ + for (j = 0; j < 4; j++) + chip->op_ar[j][to] = OP_ROTATE(chip->op_ar[j][from]); + /* dr */ + for (j = 0; j < 4; j++) + chip->op_dr[j][to] = OP_ROTATE(chip->op_dr[j][from]); + /* sl */ + for (j = 0; j < 4; j++) + chip->op_sl[j][to] = OP_ROTATE(chip->op_sl[j][from]); + /* rr */ + for (j = 0; j < 4; j++) + chip->op_rr[j][to] = OP_ROTATE(chip->op_rr[j][from]); + /* wf */ + for (j = 0; j < 2; j++) + chip->op_wf[j][to] = OP_ROTATE(chip->op_wf[j][from]); +#undef CH_ROTATE +#undef OP_ROTATE +} + +enum { + eg_state_attack = 0, + eg_state_decay, + eg_state_sustain, + eg_state_release +}; + +void FMOPL2_Clock(fmopl2_t *chip) +{ + int i, irq; + + chip->mclk1 = !chip->input.mclk; + chip->mclk2 = chip->input.mclk; + + chip->reset1 = !chip->input.ic; + chip->io_rd = !chip->input.rd; + chip->io_wr = !chip->input.wr; + chip->io_cs = !chip->input.cs; + chip->io_a0 = chip->input.address & 1; + + if (chip->mclk1) + { + int prescaler_reset = !(chip->prescaler_reset_l[1] & 2) && chip->reset1; + chip->prescaler_reset_l[0] = (chip->prescaler_reset_l[1] << 1) | chip->reset1; + if (prescaler_reset) + chip->prescaler_cnt[0] = 0; + else + chip->prescaler_cnt[0] = (chip->prescaler_cnt[1] + 1) & 3; + + chip->prescaler_l1[0] = !prescaler_reset && chip->prescaler_cnt[1] == 1; + chip->prescaler_l2[0] = chip->prescaler_cnt[1] == 3; + } + if (chip->mclk2) + { + chip->prescaler_reset_l[1] = chip->prescaler_reset_l[0]; + chip->prescaler_cnt[1] = chip->prescaler_cnt[0]; + chip->prescaler_l1[1] = chip->prescaler_l1[0]; + chip->prescaler_l2[1] = chip->prescaler_l2[0]; + } + + chip->clk1 = chip->prescaler_l1[1]; + chip->clk2 = chip->prescaler_l2[1]; + + chip->io_read0 = !chip->reset1 && chip->io_cs && chip->io_rd && !chip->io_a0; + chip->io_read1 = !chip->reset1 && chip->io_cs && chip->io_rd && chip->io_a0; + chip->io_write = !chip->reset1 && chip->io_cs && chip->io_wr; + chip->io_write0 = !chip->reset1 && chip->io_cs && chip->io_wr && !chip->io_a0; + chip->io_write1 = !chip->reset1 && chip->io_cs && chip->io_wr && chip->io_a0; + chip->io_dir = chip->io_cs && chip->io_rd; + + irq = chip->t1_status || chip->t2_status || chip->unk_status1 || chip->unk_status2; + + if (!chip->io_dir) + chip->io_data = chip->input.data_i; + + if (chip->io_write) + chip->data_latch = chip->io_data; + + if (chip->write0) + chip->write0_sr = 0; + else if (chip->io_write0) + chip->write0_sr = 1; + + if (chip->write1) + chip->write1_sr = 0; + else if (chip->io_write1) + chip->write1_sr = 1; + + if (chip->mclk1) + { + chip->write0_latch[1] = chip->write0_latch[0]; + chip->write1_latch[1] = chip->write1_latch[0]; + } + if (chip->mclk2) + { + chip->write0_latch[0] = chip->write0_sr; + chip->write0_latch[2] = chip->write0_latch[1]; + + chip->write1_latch[0] = chip->write1_sr; + chip->write1_latch[2] = chip->write1_latch[1]; + } + + if (chip->clk1) + { + chip->write0_latch[4] = chip->write0_latch[3]; + chip->write1_latch[4] = chip->write1_latch[3]; + } + if (chip->clk2) + { + chip->write0_latch[3] = chip->write0_latch[2]; + chip->write0_latch[5] = chip->write0_latch[4]; + + chip->write1_latch[3] = chip->write1_latch[2]; + chip->write1_latch[5] = chip->write1_latch[4]; + } + + chip->write0 = chip->write0_latch[5]; + chip->write1 = chip->write1_latch[5]; + + /****/ + + if (chip->o_clk1 == chip->clk1 && chip->o_clk2 == chip->clk2 && chip->o_reset1 == chip->reset1 + && chip->o_write0 == chip->write0 && chip->o_write1 == chip->write1 && chip->o_data_latch == chip->data_latch) + goto end; /* opt */ + + chip->o_clk1 = chip->clk1; + chip->o_clk2 = chip->clk2; + chip->o_reset1 = chip->reset1; + chip->o_write0 = chip->write0; + chip->o_write1 = chip->write1; + chip->o_data_latch = chip->data_latch; + + if (chip->write0) + { + chip->reg_sel1 = chip->data_latch == 1; + chip->reg_sel2 = chip->data_latch == 2; + chip->reg_sel3 = chip->data_latch == 3; + chip->reg_sel4 = chip->data_latch == 4; + chip->reg_sel8 = chip->data_latch == 8; + chip->reg_selbd = chip->data_latch == 0xbd; + } + + chip->reg_sel4_wr = chip->write1 && chip->reg_sel4 && (chip->data_latch & 128) == 0; + chip->reg_sel4_rst = (chip->write1 && chip->reg_sel4 && (chip->data_latch & 128) != 0) || chip->reset1; + + if (chip->reset1) + { + chip->reg_test = 0; + chip->reg_timer1 = 0; + chip->reg_timer2 = 0; + chip->reg_notesel = 0; + chip->reg_csm = 0; + chip->rhythm = 0; + chip->reg_rh_kon = 0; + chip->reg_da = 0; + chip->reg_dv = 0; + } + else if (chip->write1) + { + if (chip->reg_sel1) + chip->reg_test = chip->data_latch & 255; + if (chip->reg_sel2) + chip->reg_timer1 = chip->data_latch & 255; + if (chip->reg_sel3) + chip->reg_timer2 = chip->data_latch & 255; + if (chip->reg_sel8) + { + chip->reg_notesel = (chip->data_latch & 64) != 0; + chip->reg_csm = (chip->data_latch & 128) != 0; + } + if (chip->reg_selbd) + { + chip->reg_rh_kon = chip->data_latch & 31; + chip->rhythm = (chip->data_latch & 32) != 0; + chip->reg_dv = (chip->data_latch & 64) != 0; + chip->reg_da = (chip->data_latch & 128) != 0; + } + } + + if (chip->reset1) + { + chip->reg_t1_mask = 0; + chip->reg_t2_mask = 0; + chip->reg_t1_start = 0; + chip->reg_t2_start = 0; + chip->reg_mode_b3 = 0; + chip->reg_mode_b4 = 0; + } + else if (chip->reg_sel4_wr) + { + chip->reg_t1_mask = (chip->data_latch & 64) != 0; + chip->reg_t2_mask = (chip->data_latch & 32) != 0; + chip->reg_t1_start = (chip->data_latch & 1) != 0; + chip->reg_t2_start = (chip->data_latch & 2) != 0; + chip->reg_mode_b3 = (chip->data_latch & 8) != 0; + chip->reg_mode_b4 = (chip->data_latch & 16) != 0; + } + + { + int fsm_mc; + chip->fsm_reset = !(chip->fsm_reset_l[1] & 2) && chip->reset1; + chip->fsm_cnt1_of = (chip->fsm_cnt1[1] & 5) == 5; + chip->fsm_cnt2_of = chip->fsm_cnt1_of && (chip->fsm_cnt2[1] & 2) != 0; + + chip->fsm_cnt = (chip->fsm_cnt2[1] << 3) | chip->fsm_cnt1[1]; + + chip->fsm_sel[0] = chip->fsm_cnt == 20 && chip->rhythm; + chip->fsm_sel[1] = chip->fsm_cnt == 19 && chip->rhythm; + chip->fsm_sel[2] = chip->fsm_cnt == 18 && chip->rhythm; + chip->fsm_sel[3] = chip->fsm_cnt == 17 && chip->rhythm; + chip->fsm_sel[4] = chip->fsm_cnt == 16 && chip->rhythm; + chip->fsm_sel[5] = chip->fsm_cnt == 20 && chip->rhythm; + chip->fsm_sel[6] = chip->fsm_cnt == 19 && chip->rhythm; + chip->fsm_sel[7] = (chip->fsm_cnt & 5) == 4; + chip->fsm_sel[8] = chip->fsm_cnt == 16; + chip->fsm_sel[9] = (chip->fsm_cnt & 29) == 5; + chip->fsm_sel[10] = chip->fsm_cnt == 16; + chip->fsm_sel[11] = chip->fsm_cnt == 11; + chip->fsm_sel[12] = chip->fsm_cnt == 20; + + fsm_mc = !(chip->fsm_sel[7] || (chip->fsm_cnt & 2) != 0); + + chip->fsm_out[0] = ((chip->connect_l[1] & 2) != 0 || chip->fsm_sel[0] || chip->fsm_sel[1] || fsm_mc) && !chip->fsm_sel[2]; + + chip->fsm_out[1] = fsm_mc && !chip->fsm_sel[3] && !chip->fsm_sel[4]; + + chip->fsm_out[2] = !fsm_mc && !chip->fsm_sel[5] && !chip->fsm_sel[6]; + + chip->fsm_out[3] = !(chip->fsm_l1[1] && 1); + + chip->fsm_out[4] = chip->fsm_l2[1]; + + chip->fsm_out[5] = chip->fsm_sel[10]; + + chip->fsm_out[6] = chip->fsm_sel[11]; + + chip->fsm_out[7] = chip->fsm_sel[12]; + + chip->fsm_out[8] = (chip->fsm_l3[1] & 1) != 0; + + chip->fsm_out[9] = (chip->fsm_l3[1] & 2) != 0; + + chip->fsm_out[10] = (chip->fsm_l3[1] & 2) != 0; + + chip->fsm_out[11] = (chip->fsm_l4[1] & 2) != 0 && chip->rhythm; + + chip->fsm_out[12] = (chip->fsm_l5[1] & 4) != 0; + + chip->fsm_out[13] = (chip->fsm_l6[1] & 4) != 0; + + chip->fsm_out[14] = !(chip->fsm_out[12] || (chip->fsm_cnt & 16) != 0); + + chip->fsm_out[15] = !(chip->fsm_out[12] || chip->fsm_out[13]); + } + + if (chip->clk1) + { + if (chip->fsm_reset || chip->fsm_cnt1_of) + chip->fsm_cnt1[0] = 0; + else + chip->fsm_cnt1[0] = (chip->fsm_cnt1[1] + 1) & 7; + + if (chip->fsm_reset || chip->fsm_cnt2_of) + chip->fsm_cnt2[0] = 0; + else + chip->fsm_cnt2[0] = (chip->fsm_cnt2[1] + chip->fsm_cnt1_of) & 3; + + chip->fsm_reset_l[0] = (chip->fsm_reset_l[1] << 1) | chip->reset1; + + chip->fsm_l1[0] = !chip->fsm_sel[8] && !chip->fsm_sel[9] && (chip->fsm_cnt & 8) == 0; + + chip->fsm_l2[0] = chip->fsm_sel[10]; + + chip->fsm_l3[0] = (chip->fsm_l3[1] << 1) | chip->fsm_sel[12]; + + chip->fsm_l4[0] = (chip->fsm_l4[1] << 1) | ((chip->fsm_cnt & 16) != 0); + + chip->fsm_l5[0] = (chip->fsm_l5[1] << 1) | ((chip->fsm_cnt & 8) != 0); + + chip->fsm_l6[0] = (chip->fsm_l6[1] << 1) | ((chip->fsm_cnt & 16) != 0); + } + + if (chip->clk2) + { + chip->fsm_cnt1[1] = chip->fsm_cnt1[0]; + chip->fsm_cnt2[1] = chip->fsm_cnt2[0]; + chip->fsm_reset_l[1] = chip->fsm_reset_l[0]; + chip->fsm_l1[1] = chip->fsm_l1[0]; + chip->fsm_l2[1] = chip->fsm_l2[0]; + chip->fsm_l3[1] = chip->fsm_l3[0]; + chip->fsm_l4[1] = chip->fsm_l4[0]; + chip->fsm_l5[1] = chip->fsm_l5[0]; + chip->fsm_l6[1] = chip->fsm_l6[0]; + } + + if (chip->clk1) + chip->timer_st_load_l = chip->fsm_out[8]; + + chip->timer_st_load = chip->fsm_out[8] && !chip->timer_st_load_l; + + if (chip->timer_st_load) + chip->t1_start = chip->reg_t1_start; + + if (chip->clk1) + { + int lfo = chip->lfo_cnt[1]; + int add = chip->fsm_out[8]; + + chip->lfo_cnt[0] = (chip->reg_test & 128) != 0 ? 0 : (lfo + add) & 1023; + chip->vib_cnt[0] = (chip->reg_test & 128) != 0 ? 0 : (chip->vib_cnt[1] + chip->vib_step) & 7; + } + + if (chip->clk2) + { + chip->lfo_cnt[1] = chip->lfo_cnt[0]; + chip->vib_cnt[1] = chip->vib_cnt[0]; + } + + { + int lfo = chip->lfo_cnt[1]; + int add = chip->fsm_out[8]; + + chip->t1_step = (((lfo & 3) + add) & 4) != 0; + chip->t2_step = (((lfo & 15) + add) & 16) != 0; + chip->am_step = (((lfo & 63) + add) & 64) != 0; + chip->vib_step = (((lfo & 1023) + add) & 1024) != 0; + chip->vib_step |= (chip->reg_test & 8) != 0 && add; + } + + if (chip->clk1) + { + int value = chip->t1_load ? chip->reg_timer1 : chip->t1_cnt[1]; + value += ((chip->t1_start_l[1] & 1) != 0 && chip->t1_step) || (chip->reg_test & 2) != 0; + chip->t1_of[0] = (value & 256) != 0; + chip->t1_cnt[0] = (chip->t1_start_l[1] & 1) == 0 ? 0 : (value & 255); + + value = (chip->t2_of[1] || (chip->t2_start_l[1] & 3) == 1) ? chip->reg_timer2 : chip->t2_cnt[1]; + value += ((chip->t2_start_l[1] & 1) != 0 && chip->t2_step) || (chip->reg_test & 2) != 0; + chip->t2_of[0] = (value & 256) != 0; + chip->t2_cnt[0] = (chip->t2_start_l[1] & 1) == 0 ? 0 : (value & 255); + + chip->t1_start_l[0] = (chip->t1_start_l[1] << 1) | chip->t1_start; + chip->t2_start_l[0] = (chip->t2_start_l[1] << 1) | chip->reg_t2_start; + } + if (chip->clk2) + { + chip->t1_cnt[1] = chip->t1_cnt[0]; + chip->t1_of[1] = chip->t1_of[0]; + chip->t2_cnt[1] = chip->t2_cnt[0]; + chip->t2_of[1] = chip->t2_of[0]; + + chip->t1_start_l[1] = chip->t1_start_l[0]; + chip->t2_start_l[1] = chip->t2_start_l[0]; + + chip->t1_load = (chip->t1_of[1] || (chip->t1_start_l[1] & 3) == 1); /* opt */ + } + + if (chip->reg_sel4_rst || chip->reg_t1_mask) + chip->t1_status = 0; + else if (chip->t1_of[1]) + chip->t1_status = 1; + + if (chip->reg_sel4_rst || chip->reg_t2_mask) + chip->t2_status = 0; + else if (chip->t2_of[1]) + chip->t2_status = 1; + + if (chip->reg_sel4_rst || chip->reg_mode_b4) + chip->unk_status1 = 0; + else if (0) + chip->unk_status1 = 1; + + chip->unk_status2 = 0; + + if (chip->clk1) + chip->csm_load_l = chip->fsm_out[10]; + + chip->csm_load = chip->fsm_out[10] && !chip->csm_load_l; + + if (chip->csm_load) + chip->csm_kon = chip->reg_csm && chip->t1_load; + + chip->rh_sel0 = chip->rhythm && chip->fsm_out[5]; + + if (chip->clk1) + { + chip->rh_sel[0] = (chip->rh_sel[1] << 1) | chip->rh_sel0; + } + if (chip->clk2) + { + chip->rh_sel[1] = chip->rh_sel[0]; + } + + /* if (chip->clk1) // opt */ + { + chip->keyon_comb = chip->keyon || chip->csm_kon + || (chip->rh_sel0 && (chip->reg_rh_kon & 16) != 0) /* bd0 */ + || ((chip->rh_sel[1] & 1) != 0 && (chip->reg_rh_kon & 1) != 0) /* hh */ + || ((chip->rh_sel[1] & 2) != 0 && (chip->reg_rh_kon & 4) != 0) /* tom */ + || ((chip->rh_sel[1] & 4) != 0 && (chip->reg_rh_kon & 16) != 0) /* bd1 */ + || ((chip->rh_sel[1] & 8) != 0 && (chip->reg_rh_kon & 8) != 0) /* sd */ + || ((chip->rh_sel[1] & 16) != 0 && (chip->reg_rh_kon & 2) != 0); /* tc */ + } + + if (chip->reset1) + chip->address = 0; + else if ((chip->data_latch & 0xe0) != 0 && chip->write0) + chip->address = chip->data_latch; + + if (chip->write0) + chip->address_valid = (chip->data_latch & 0xe0) != 0; + + if (chip->reset1) + chip->data = 0; + else if (chip->address_valid && chip->write1) + chip->data = chip->data_latch; + + chip->address_valid2 = chip->address_valid_l[1] && !chip->write0; + + if (chip->clk1) + { + int slot_cnt1_of; + + chip->address_valid_l[0] = (chip->address_valid && chip->write1) || chip->address_valid2; + + slot_cnt1_of = (chip->slot_cnt1[1] & 5) == 5; + + if (chip->fsm_out[8] || slot_cnt1_of) + chip->slot_cnt1[0] = 0; + else + chip->slot_cnt1[0] = (chip->slot_cnt1[1] + 1) & 7; + + if (chip->fsm_out[8] || (slot_cnt1_of && (chip->slot_cnt2[1] & 2) != 0)) + chip->slot_cnt2[0] = 0; + else + chip->slot_cnt2[0] = (chip->slot_cnt2[1] + slot_cnt1_of) & 3; + } + + if (chip->clk2) + { + chip->address_valid_l[1] = chip->address_valid_l[0]; + + chip->slot_cnt1[1] = chip->slot_cnt1[0]; + chip->slot_cnt2[1] = chip->slot_cnt2[0]; + + chip->slot_cnt = (chip->slot_cnt2[1] << 3) | chip->slot_cnt1[1]; /* opt */ + } + + if (chip->clk1) + { + int addr_match; + int sel_ch, addr_add, addr_sel; + int sel_20, sel_40, sel_60, sel_80, sel_e0, sel_a0, sel_b0, sel_c0; + + sel_ch = (chip->address & 0xf0) == 0xa0 || (chip->address & 0xf0) == 0xb0 || (chip->address & 0xf0) == 0xc0; + addr_add = sel_ch && ((chip->address & 8) != 0 || (chip->address & 6) == 6); + + addr_sel = chip->address & 1; + + addr_sel |= (((chip->address >> 1) + addr_add) & 7) << 1; + + if (!sel_ch) + addr_sel |= chip->address & 16; + + addr_match = addr_sel == chip->slot_cnt && chip->address_valid2; + + sel_20 = (chip->address & 0xe0) == 0x20 && addr_match; + sel_40 = (chip->address & 0xe0) == 0x40 && addr_match; + sel_60 = (chip->address & 0xe0) == 0x60 && addr_match; + sel_80 = (chip->address & 0xe0) == 0x80 && addr_match; + sel_e0 = (chip->address & 0xe0) == 0xe0 && addr_match && (chip->reg_test & 32) != 0; + + sel_a0 = (chip->address & 0xf0) == 0xa0 && addr_match; + sel_b0 = (chip->address & 0xf0) == 0xb0 && addr_match; + sel_c0 = (chip->address & 0xf0) == 0xc0 && addr_match; + + FMOPL2_DoShiftRegisters(chip, 0); + + if (chip->reset1) + { + for (i = 0; i < 10; i++) + chip->ch_fnum[i][0] &= ~1; + + for (i = 0; i < 3; i++) + chip->ch_block[i][0] &= ~1; + + chip->ch_keyon[0] &= ~1; + chip->ch_connect[0] &= ~1; + + for (i = 0; i < 3; i++) + chip->ch_fb[i][0] &= ~1; + + for (i = 0; i < 4; i++) + chip->op_multi[i][0] &= ~1; + + chip->op_ksr[0] &= ~1; + chip->op_egt[0] &= ~1; + chip->op_vib[0] &= ~1; + chip->op_am[0] &= ~1; + + for (i = 0; i < 6; i++) + chip->op_tl[i][0] &= ~1; + for (i = 0; i < 2; i++) + chip->op_ksl[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_ar[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_dr[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_sl[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_rr[i][0] &= ~1; + for (i = 0; i < 2; i++) + chip->op_wf[i][0] &= ~1; + } + else + { + if (sel_a0) + { + for (i = 0; i < 8; i++) + chip->ch_fnum[i][0] &= ~1; + + for (i = 0; i < 8; i++) + chip->ch_fnum[i][0] |= (chip->data >> i) & 1; + } + + if (sel_b0) + { + for (i = 8; i < 10; i++) + chip->ch_fnum[i][0] &= ~1; + for (i = 0; i < 3; i++) + chip->ch_block[i][0] &= ~1; + chip->ch_keyon[0] &= ~1; + + for (i = 8; i < 10; i++) + chip->ch_fnum[i][0] |= (chip->data >> (i - 8)) & 1; + for (i = 0; i < 3; i++) + chip->ch_block[i][0] |= (chip->data >> (i + 2)) & 1; + chip->ch_keyon[0] |= (chip->data >> 5) & 1; + } + + if (sel_c0) + { + chip->ch_connect[0] &= ~1; + for (i = 0; i < 3; i++) + chip->ch_fb[i][0] &= ~1; + + chip->ch_connect[0] |= (chip->data >> 0) & 1; + for (i = 0; i < 3; i++) + chip->ch_fb[i][0] |= (chip->data >> (i + 1)) & 1; + } + + if (sel_20) + { + for (i = 0; i < 4; i++) + chip->op_multi[i][0] &= ~1; + chip->op_ksr[0] &= ~1; + chip->op_egt[0] &= ~1; + chip->op_vib[0] &= ~1; + chip->op_am[0] &= ~1; + + for (i = 0; i < 4; i++) + chip->op_multi[i][0] |= (chip->data >> i) & 1; + chip->op_ksr[0] |= (chip->data >> 4) & 1; + chip->op_egt[0] |= (chip->data >> 5) & 1; + chip->op_vib[0] |= (chip->data >> 6) & 1; + chip->op_am[0] |= (chip->data >> 7) & 1; + } + + if (sel_40) + { + for (i = 0; i < 6; i++) + chip->op_tl[i][0] &= ~1; + for (i = 0; i < 2; i++) + chip->op_ksl[i][0] &= ~1; + + for (i = 0; i < 6; i++) + chip->op_tl[i][0] |= (chip->data >> i) & 1; + for (i = 0; i < 2; i++) + chip->op_ksl[i][0] |= (chip->data >> (i + 6)) & 1; + } + + if (sel_60) + { + for (i = 0; i < 4; i++) + chip->op_ar[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_dr[i][0] &= ~1; + + for (i = 0; i < 4; i++) + chip->op_ar[i][0] |= (chip->data >> (i + 4)) & 1; + for (i = 0; i < 4; i++) + chip->op_dr[i][0] |= (chip->data >> i) & 1; + } + + if (sel_80) + { + for (i = 0; i < 4; i++) + chip->op_sl[i][0] &= ~1; + for (i = 0; i < 4; i++) + chip->op_rr[i][0] &= ~1; + + for (i = 0; i < 4; i++) + chip->op_sl[i][0] |= (chip->data >> (i + 4)) & 1; + for (i = 0; i < 4; i++) + chip->op_rr[i][0] |= (chip->data >> i) & 1; + } + + if (sel_e0) + { + for (i = 0; i < 2; i++) + chip->op_wf[i][0] &= ~1; + + for (i = 0; i < 2; i++) + chip->op_wf[i][0] |= (chip->data >> i) & 1; + } + } + } + if (chip->clk2) + { + FMOPL2_DoShiftRegisters(chip, 1); + } + + /*if (chip->clk2) // opt */ + { + int shift = 0; + + if (chip->fsm_out[13]) + shift = 8; + else if (chip->fsm_out[12]) + shift = 5; + else if (chip->fsm_out[15]) + shift = 2; + + chip->block = 0; + chip->fnum = 0; + for (i = 0; i < 3; i++) + chip->block |= ((chip->ch_block[i][1] >> shift) & 1) << i; + for (i = 0; i < 10; i++) + chip->fnum |= ((chip->ch_fnum[i][1] >> shift) & 1) << i; + chip->keyon = (chip->ch_keyon[1] >> shift) & 1; + chip->connect = (chip->ch_connect[1] >> shift) & 1; + + chip->fb = 0; + if (chip->fsm_out[13]) + shift = 5; + else if (chip->fsm_out[12]) + shift = 2; + else if (chip->fsm_out[15]) + shift = 8; + + for (i = 0; i < 3; i++) + chip->fb |= ((chip->ch_fb[i][1] >> shift) & 1) << i; + + chip->multi = 0; + chip->tl = 0; + chip->ksl = 0; + chip->ar = 0; + chip->dr = 0; + chip->sl = 0; + chip->rr = 0; + chip->wf = 0; + + for (i = 0; i < 4; i++) + chip->multi |= ((chip->op_multi[i][1] >> 17) & 1) << i; + + chip->ksr = (chip->op_ksr[1] >> 17) & 1; + chip->egt = (chip->op_egt[1] >> 17) & 1; + chip->vib = (chip->op_vib[1] >> 17) & 1; + chip->am = (chip->op_am[1] >> 17) & 1; + + for (i = 0; i < 6; i++) + chip->tl |= ((chip->op_tl[i][1] >> 17) & 1) << i; + + for (i = 0; i < 2; i++) + chip->ksl |= ((chip->op_ksl[i][1] >> 17) & 1) << i; + + for (i = 0; i < 4; i++) + chip->ar |= ((chip->op_ar[i][1] >> 17) & 1) << i; + + for (i = 0; i < 4; i++) + chip->dr |= ((chip->op_dr[i][1] >> 17) & 1) << i; + + for (i = 0; i < 4; i++) + chip->sl |= ((chip->op_sl[i][1] >> 17) & 1) << i; + + for (i = 0; i < 4; i++) + chip->rr |= ((chip->op_rr[i][1] >> 17) & 1) << i; + + for (i = 0; i < 2; i++) + chip->wf |= ((chip->op_wf[i][1] >> 17) & 1) << i; + + } + + if (chip->clk1) + { + chip->connect_l[0] = (chip->connect_l[1] << 1) | chip->connect; + chip->fb_l[0][0] = chip->fb; + chip->fb_l[1][0] = chip->fb_l[0][1]; + } + if (chip->clk2) + { + chip->connect_l[1] = chip->connect_l[0]; + chip->fb_l[0][1] = chip->fb_l[0][0]; + chip->fb_l[1][1] = chip->fb_l[1][0]; + } + + if (chip->clk1) + { + chip->eg_load1_l = chip->fsm_out[8]; + chip->eg_load2_l = chip->fsm_out[9]; + chip->eg_load3_l = chip->eg_subcnt_l[1] && chip->eg_sync_l[1]; + } + chip->eg_load1 = !chip->eg_load1_l && chip->fsm_out[8]; + chip->eg_load2 = !chip->eg_load2_l && chip->fsm_out[9]; + chip->eg_load3 = !chip->eg_load3_l && chip->eg_subcnt_l[1] && chip->eg_sync_l[1]; + + { + + if (chip->eg_load1) + chip->trem_step = chip->am_step; + + if (chip->eg_load2) + chip->trem_out = chip->trem_value[1] & 127; + + if (chip->clk1) + { + int bit, reset, step, carry, of; + + bit = chip->trem_value[1] & 1; + reset = chip->reset1 || (chip->reg_test & 128) != 0; + + step = ((chip->trem_step || (chip->reg_test & 8) != 0) && (chip->fsm_out[9] || chip->trem_dir[1])) + && chip->fsm_out[14]; + carry = chip->fsm_out[14] && chip->trem_carry[1]; + + bit += step + carry; + + of = (chip->trem_out == 0) || (chip->trem_out & 105) == 105; + + chip->trem_carry[0] = (bit & 2) != 0; + chip->trem_value[0] = (chip->trem_value[1] >> 1) & 255; + + if (!reset) + chip->trem_value[0] |= (bit & 1) << 8; + chip->trem_of[0] = of; + + if (reset) + chip->trem_dir[0] = 0; + else + chip->trem_dir[0] = chip->trem_dir[1] ^ (of && !chip->trem_of[1]); + } + + if (chip->clk2) + { + chip->trem_carry[1] = chip->trem_carry[0]; + chip->trem_value[1] = chip->trem_value[0]; + chip->trem_of[1] = chip->trem_of[0]; + chip->trem_dir[1] = chip->trem_dir[0]; + } + } + + { + + if (chip->eg_load3) + { + chip->eg_timer_low = chip->eg_timer[1] & 3; + chip->eg_shift = 0; + if (chip->eg_timer_masked[1] & 0x1555) + chip->eg_shift |= 1; + if (chip->eg_timer_masked[1] & 0x666) + chip->eg_shift |= 2; + if (chip->eg_timer_masked[1] & 0x1878) + chip->eg_shift |= 4; + if (chip->eg_timer_masked[1] & 0x1f80) + chip->eg_shift |= 8; + } + + if (chip->clk1) + { + int bit, bit2, carry; + + bit = chip->eg_timer[1] & 1; + carry = chip->eg_carry[1] || (chip->eg_subcnt[1] && chip->eg_sync_l[1]); + bit += carry; + + if (chip->reset1) + bit2 = 0; + else + bit2 = bit & 1; + + chip->eg_timer[0] = (chip->eg_timer[1] >> 1) & 0x1ffff; + chip->eg_timer[0] |= bit2 << 17; + chip->eg_carry[0] = (bit & 2) != 0; + chip->eg_sync_l[0] = chip->fsm_out[8]; + chip->eg_mask[0] = (chip->reset1 || chip->fsm_out[8]) ? 0 : + (chip->eg_mask[1] || bit2); + chip->eg_timer_masked[0] = (chip->eg_timer_masked[1] >> 1) & 0x1ffff; + + if (!chip->eg_mask[1]) + chip->eg_timer_masked[0] |= bit2 << 17; + + if (chip->reset1) + chip->eg_subcnt[0] = 0; + else + chip->eg_subcnt[0] = chip->eg_subcnt[1] ^ chip->fsm_out[8]; + + chip->eg_subcnt_l[0] = chip->eg_subcnt[1]; + } + + if (chip->clk2) + { + chip->eg_timer[1] = chip->eg_timer[0]; + chip->eg_carry[1] = chip->eg_carry[0]; + chip->eg_sync_l[1] = chip->eg_sync_l[0]; + chip->eg_mask[1] = chip->eg_mask[0]; + chip->eg_timer_masked[1] = chip->eg_timer_masked[0]; + chip->eg_subcnt[1] = chip->eg_subcnt[0]; + chip->eg_subcnt_l[1] = chip->eg_subcnt_l[0]; + } + } + + if (chip->clk1) + { + static const int eg_stephi[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } + }; + + static const int eg_ksltable[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 + }; + + static const int eg_kslshift[4] = { + 31, 1, 2, 0 + }; + + int state = 0, dokon, rate_sel, rate, ksr, sl, ns, + rate_hi, maxrate, inclow, stephi, step1, step2, step3, + level, slreach, zeroreach, silent, nextstate, + linear, exponent, instantattack, mute, level2, add, addshift, levelnext, + ksl, ksltl, tremolo, ksltltrem, levelof, totallevel, totallevelclamp; + + + if (chip->eg_state[0][1] & 0x20000) + state |= 1; + if (chip->eg_state[1][1] & 0x20000) + state |= 2; + + dokon = state == eg_state_release && chip->keyon_comb; + rate_sel = dokon ? eg_state_attack : state; + rate = 0; + + if (rate_sel == 0) + rate |= chip->ar; + if (rate_sel == 1) + rate |= chip->dr; + if (rate_sel == 3 || (rate_sel == 2 && !chip->egt)) + rate |= chip->rr; + + sl = chip->sl; + if (chip->sl == 15) + sl |= 16; + + ns = chip->reg_notesel ? (chip->fnum & 256) != 0 : (chip->fnum & 512) != 0; + + if (chip->ksr) + ksr = (chip->block << 1) | ns; + else + ksr = chip->block >> 1; + + rate_hi = rate + (ksr >> 2); + if (rate_hi & 16) + rate_hi = 15; + + maxrate = rate_hi == 15; + + /* int rate12 = rate_hi == 12; */ + /* int rate13 = rate_hi == 13; */ + /* int rate14 = rate_hi == 14; */ + + inclow = 0; + + if (rate_hi < 12 && rate != 0 && chip->eg_subcnt[1]) + { + int sum = (rate_hi + chip->eg_shift) & 15; + switch (sum) + { + case 12: + inclow = 1; + break; + case 13: + inclow = (ksr & 2) != 0; + break; + case 14: + inclow = (ksr & 1) != 0; + break; + } + } + + stephi = eg_stephi[ksr & 3][chip->eg_timer_low]; + + step1 = 0; + step2 = 0; + step3 = 0; + + switch (rate_hi) + { + case 12: + step1 = stephi || chip->eg_subcnt[1]; + break; + case 13: + if (stephi) + step2 = 1; + else + step1 = 1; + break; + case 14: + if (stephi) + step3 = 1; + else + step2 = 1; + break; + case 15: + step3 = 1; + break; + } + + step1 |= inclow; + + level = 0; + + for (i = 0; i < 9; i++) + { + level |= ((chip->eg_level[i][1] >> 17) & 1) << i; + } + + slreach = (level >> 4) == sl; + zeroreach = level == 0; + silent = (level & 0x1f8) == 0x1f8; + + nextstate = eg_state_attack; + + if (chip->reset1) + nextstate = eg_state_release; + else if (dokon) + nextstate = eg_state_attack; + else + { + if (!chip->keyon_comb) + nextstate = eg_state_release; + else if (state == eg_state_attack) + nextstate = zeroreach ? eg_state_decay : eg_state_attack; + else if (state == eg_state_decay) + nextstate = slreach ? eg_state_sustain : eg_state_decay; + else if (state == eg_state_sustain) + nextstate = eg_state_sustain; + else if (state == eg_state_release) + nextstate = eg_state_release; + } + + linear = !dokon && !silent && ((state & 2) != 0 || (state == eg_state_decay && !slreach)); + exponent = state == eg_state_attack && chip->keyon_comb && !maxrate && !zeroreach; + instantattack = dokon && maxrate; + mute = chip->reset1 || (state != eg_state_attack && silent && !dokon); + + level2 = mute ? 0x1ff : (instantattack ? 0 : level); + + add = 0; + addshift = 0; + + if (exponent) + add |= (~level) >> 1; + if (linear) + add |= 4; + + if (step1) + addshift |= add >> 2; + if (step2) + addshift |= add >> 1; + if (step3) + addshift |= add >> 0; + + levelnext = level2 + addshift; + + ksl = eg_ksltable[chip->fnum >> 6] ^ 127; + + ksl += ((chip->block ^ 7) + 1) << 3; + if (ksl & 128) + ksl = 0; + else + ksl = (ksl ^ 63) & 63; + + ksl = (ksl << 2) >> eg_kslshift[chip->ksl]; + ksltl = ksl + (chip->tl << 2); + + if (!chip->am) + tremolo = 0; + else if (chip->reg_da) + tremolo = chip->trem_out >> 2; + else + tremolo = chip->trem_out >> 4; + + ksltltrem = ksltl + tremolo; + levelof = 0; + + if (ksltltrem & 0x200) + levelof = 1; + + totallevel = level + (ksltltrem & 0x1ff); + if (totallevel & 0x200) + levelof = 1; + + totallevelclamp = (chip->reg_test & 1) != 0 ? 0 : (levelof ? 0x1ff : (totallevel & 0x1ff)); + + chip->eg_dokon = dokon; + + chip->eg_state[0][0] = (chip->eg_state[0][1] << 1) | ((nextstate & 1) != 0); + chip->eg_state[1][0] = (chip->eg_state[1][1] << 1) | ((nextstate & 2) != 0); + + for (i = 0; i < 9; i++) + { + chip->eg_level[i][0] = (chip->eg_level[i][1] << 1) | ((levelnext >> i) & 1); + } + chip->eg_out[0] = totallevelclamp; + + if (chip->fsm_out[9]) + { + for (i = 0; i < 9; i++) + { + if (chip->eg_out[1] & (1 << i)) + chip->dbg_serial[0] |= 1 << (17 - i); + } + } + + chip->eg_mute[0] = (chip->eg_mute[1] << 1) | mute; + } + if (chip->clk2) + { + chip->eg_state[0][1] = chip->eg_state[0][0]; + chip->eg_state[1][1] = chip->eg_state[1][0]; + + for (i = 0; i < 9; i++) + { + chip->eg_level[i][1] = chip->eg_level[i][0]; + } + chip->eg_out[1] = chip->eg_out[0]; + chip->eg_mute[1] = chip->eg_mute[0]; + } + + if (chip->clk1) + { + static const int pg_multi[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 + }; + int fnum = chip->fnum; + int freq; + int pg_add; + int vib_sel1 = (chip->vib_cnt[1] & 3) == 2; + int vib_sel2 = (chip->vib_cnt[1] & 1) == 1; + int vib_sh0 = chip->reg_dv && chip->vib && vib_sel1; + int vib_sh1 = (chip->reg_dv && chip->vib && vib_sel2) || (!chip->reg_dv && chip->vib && vib_sel1); + int vib_sh2 = !chip->reg_dv && chip->vib && vib_sel2; + int vib_sign = (chip->vib_cnt[1] & 4) != 0 && chip->vib; + int vib_add = 0; + int pg_out = 0; + int phase; + int noise_bit; + + if (vib_sh0) + vib_add |= (chip->fnum >> 7) & 7; + + if (vib_sh1) + vib_add |= (chip->fnum >> 8) & 3; + + if (vib_sh2) + vib_add |= (chip->fnum >> 9) & 1; + + if (vib_sign) + vib_add ^= 1023; + + fnum += vib_add; + fnum += vib_sign; + + if (vib_sign) + fnum &= 1023; + + freq = (fnum << chip->block) >> 1; + + pg_add = (freq * pg_multi[chip->multi]) >> 1; + + for (i = 0; i < 19; i++) + { + pg_out |= ((chip->pg_phase[i][1] >> 17) & 1) << i; + } + + phase = ((chip->eg_dokon || (chip->reg_test & 4) != 0) ? 0 : pg_out) + pg_add; + + for (i = 0; i < 19; i++) + { + chip->pg_phase[i][0] = chip->pg_phase[i][1] << 1; + chip->pg_phase[i][0] |= (phase >> i) & 1; + } + + chip->dbg_serial[0] = chip->dbg_serial[1] >> 1; + + if (chip->fsm_out[9]) + { + chip->dbg_serial[0] |= pg_out & 511; + } + + noise_bit = ((chip->noise_lfsr[1] >> 22) ^ (chip->noise_lfsr[1] >> 8)) & 1; + + if ((chip->noise_lfsr[1] & 0x7fffff) == 0) + noise_bit |= 1; + + noise_bit |= (chip->reg_test & 128) != 0; + + chip->noise_lfsr[0] = (chip->noise_lfsr[1] << 1) | noise_bit; + } + if (chip->clk2) + { + for (i = 0; i < 19; i++) + { + chip->pg_phase[i][1] = chip->pg_phase[i][0]; + } + + chip->noise_lfsr[1] = chip->noise_lfsr[0]; + + chip->pg_out = 0; + for (i = 0; i < 10; i++) + { + chip->pg_out |= ((chip->pg_phase[i+9][1] >> 17) & 1) << i; + } + + chip->dbg_serial[1] = chip->dbg_serial[0]; + } + + { + int hh = chip->fsm_out[4] && chip->rhythm; + int sd = chip->fsm_out[7] && chip->rhythm; + int tc = chip->fsm_out[8] && chip->rhythm; + int rhy = (chip->fsm_out[4] || chip->fsm_out[7] || chip->fsm_out[8]) && chip->rhythm; + + if (chip->clk1) + chip->hh_load = chip->fsm_out[4]; + + if (!chip->hh_load && chip->fsm_out[5]) + { + chip->hh_bit2 = (chip->pg_out >> 2) & 1; + chip->hh_bit3 = (chip->pg_out >> 3) & 1; + chip->hh_bit7 = (chip->pg_out >> 7) & 1; + chip->hh_bit8 = (chip->pg_out >> 8) & 1; + } + + if (chip->clk1) + chip->tc_load = tc; + + if (!chip->tc_load && tc) + { + chip->tc_bit3 = (chip->pg_out >> 3) & 1; + chip->tc_bit5 = (chip->pg_out >> 5) & 1; + } + + if (chip->clk1) /* opt */ + { + int rm_bit; + int noise = (chip->noise_lfsr[1] >> 22) & 1; + + rm_bit = (chip->hh_bit2 ^ chip->hh_bit7) + | (chip->tc_bit5 ^ chip->hh_bit3) + | (chip->tc_bit5 ^ chip->tc_bit3); + + chip->pg_out_rhy = 0; + if (!rhy) + chip->pg_out_rhy |= chip->pg_out; + + if (hh) + { + chip->pg_out_rhy |= rm_bit << 9; + if (noise ^ rm_bit) + chip->pg_out_rhy |= 0xd0; + else + chip->pg_out_rhy |= 0x34; + } + + if (sd) + chip->pg_out_rhy |= (chip->hh_bit8 << 9) | ((noise ^ chip->hh_bit8) << 8); + + if (tc) + chip->pg_out_rhy |= (rm_bit << 9) | 0x100; + } + } + + { + if (chip->clk1) + { + static const int logsin[128] = { + 0x6c3, 0x58b, 0x4e4, 0x471, 0x41a, 0x3d3, 0x398, 0x365, 0x339, 0x311, 0x2ed, 0x2cd, 0x2af, 0x293, 0x279, 0x261, + 0x24b, 0x236, 0x222, 0x20f, 0x1fd, 0x1ec, 0x1dc, 0x1cd, 0x1be, 0x1b0, 0x1a2, 0x195, 0x188, 0x17c, 0x171, 0x166, + 0x15b, 0x150, 0x146, 0x13c, 0x133, 0x129, 0x121, 0x118, 0x10f, 0x107, 0x0ff, 0x0f8, 0x0f0, 0x0e9, 0x0e2, 0x0db, + 0x0d4, 0x0cd, 0x0c7, 0x0c1, 0x0bb, 0x0b5, 0x0af, 0x0a9, 0x0a4, 0x09f, 0x099, 0x094, 0x08f, 0x08a, 0x086, 0x081, + 0x07d, 0x078, 0x074, 0x070, 0x06c, 0x068, 0x064, 0x060, 0x05c, 0x059, 0x055, 0x052, 0x04e, 0x04b, 0x048, 0x045, + 0x042, 0x03f, 0x03c, 0x039, 0x037, 0x034, 0x031, 0x02f, 0x02d, 0x02a, 0x028, 0x026, 0x024, 0x022, 0x020, 0x01e, + 0x01c, 0x01a, 0x018, 0x017, 0x015, 0x014, 0x012, 0x011, 0x00f, 0x00e, 0x00d, 0x00c, 0x00a, 0x009, 0x008, 0x007, + 0x007, 0x006, 0x005, 0x004, 0x004, 0x003, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000 + }; + static const int logsin_d[128] = { + 0x196, 0x07c, 0x04a, 0x035, 0x029, 0x022, 0x01d, 0x019, 0x015, 0x013, 0x012, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x009, 0x008, 0x007, 0x007, 0x007, 0x007, 0x006, 0x007, 0x006, 0x006, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x004, 0x005, 0x004, 0x004, 0x005, 0x004, 0x004, 0x003, 0x004, 0x003, 0x003, 0x003, + 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, 0x003, 0x003, 0x003, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x002, 0x002, 0x002, 0x001, + 0x001, 0x001, 0x002, 0x002, 0x001, 0x001, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x000, 0x001, 0x000, 0x001, 0x000, 0x001, 0x001, 0x000, 0x000, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x001, 0x000, 0x000, 0x001, 0x000, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 + }; + static const int pow[128] = { + 0x3f5, 0x3ea, 0x3df, 0x3d4, 0x3c9, 0x3bf, 0x3b4, 0x3a9, 0x39f, 0x394, 0x38a, 0x37f, 0x375, 0x36a, 0x360, 0x356, + 0x34c, 0x342, 0x338, 0x32e, 0x324, 0x31a, 0x310, 0x306, 0x2fd, 0x2f3, 0x2e9, 0x2e0, 0x2d6, 0x2cd, 0x2c4, 0x2ba, + 0x2b1, 0x2a8, 0x29e, 0x295, 0x28c, 0x283, 0x27a, 0x271, 0x268, 0x25f, 0x257, 0x24e, 0x245, 0x23c, 0x234, 0x22b, + 0x223, 0x21a, 0x212, 0x209, 0x201, 0x1f9, 0x1f0, 0x1e8, 0x1e0, 0x1d8, 0x1d0, 0x1c8, 0x1c0, 0x1b8, 0x1b0, 0x1a8, + 0x1a0, 0x199, 0x191, 0x189, 0x181, 0x17a, 0x172, 0x16b, 0x163, 0x15c, 0x154, 0x14d, 0x146, 0x13e, 0x137, 0x130, + 0x129, 0x122, 0x11b, 0x114, 0x10c, 0x106, 0x0ff, 0x0f8, 0x0f1, 0x0ea, 0x0e3, 0x0dc, 0x0d6, 0x0cf, 0x0c8, 0x0c2, + 0x0bb, 0x0b5, 0x0ae, 0x0a8, 0x0a1, 0x09b, 0x094, 0x08e, 0x088, 0x082, 0x07b, 0x075, 0x06f, 0x069, 0x063, 0x05d, + 0x057, 0x051, 0x04b, 0x045, 0x03f, 0x039, 0x033, 0x02d, 0x028, 0x022, 0x01c, 0x016, 0x011, 0x00b, 0x006, 0x000, + }; + static const int pow_d[128] = { + 0x005, 0x005, 0x005, 0x006, 0x006, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x006, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x005, + 0x004, 0x004, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x005, 0x004, 0x005, + 0x004, 0x004, 0x004, 0x005, 0x004, 0x004, 0x005, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x003, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x003, 0x004, 0x004, 0x004, + 0x003, 0x003, 0x003, 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x004, 0x003, 0x003, 0x004, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, + }; + int phase = chip->pg_out_rhy + chip->op_mod[1]; + int sign = (phase & 512) != 0; + int quarter = (phase & 256) != 0; + int ls, att, pw, value, sign_wf, mute_wf, fb1, fb2, fb_sum, mod; + + phase &= 255; + if (quarter) + phase ^= 255; + + ls = logsin[phase >> 1]; + if ((phase & 1) == 0) + ls += logsin_d[phase >> 1]; + + att = chip->op_logsin[1] + (chip->eg_out[1] << 3); + if (att & 4096) + att = 4095; + + pw = pow[(att >> 1) & 127]; + if ((att & 1) == 0) + pw += pow_d[(att >> 1) & 127]; + + value = 0; + + if (chip->op_mute[1] & 2) + { + value = ((chip->op_pow[1] | 0x400) << 1) >> chip->op_shift[1]; + } + + if (chip->op_sign[1] & 2) + value ^= 8191; + + sign_wf = sign && chip->wf == 0; + mute_wf = !((chip->wf == 1 && sign) || (chip->wf == 3 && quarter)); + + fb1 = 0; + fb2 = 0; + + for (i = 0; i < 14; i++) + { + int j = i; + if (i == 13) + j = 12; + fb1 |= ((chip->op_fb[0][j][1] >> 5) & 1) << i; + fb2 |= ((chip->op_fb[1][j][1] >> 5) & 1) << i; + } + + fb_sum = fb1 + fb2; + fb_sum &= 16383; + if (fb_sum & 8192) + fb_sum |= ~8191; + + mod = 0; + + if (chip->fsm_out[2] && !(chip->connect_l[1] & 2)) + mod |= value & 1023; + + if (chip->fsm_out[1]) + { + if (chip->fb_l[1][1]) + { + mod |= (fb_sum >> (9 - chip->fb_l[1][1])) & 1023; + } + } + + chip->op_logsin[0] = ls; + chip->op_shift[0] = (att >> 8) & 15; + chip->op_pow[0] = pw; + chip->op_mute[0] = (chip->op_mute[1] << 1) | mute_wf; + chip->op_sign[0] = (chip->op_sign[1] << 1) | sign_wf; + + for (i = 0; i < 13; i++) + { + int bit; + chip->op_fb[0][i][0] = chip->op_fb[0][i][1] << 1; + if (chip->fsm_out[2]) + bit = (value >> i) & 1; + else + bit = (chip->op_fb[0][i][1] >> 8) & 1; + chip->op_fb[0][i][0] |= bit; + chip->op_fb[1][i][0] = chip->op_fb[1][i][1] << 1; + if (chip->fsm_out[2]) + bit = (chip->op_fb[0][i][1] >> 8) & 1; + else + bit = (chip->op_fb[1][i][1] >> 8) & 1; + chip->op_fb[1][i][0] |= bit; + } + chip->op_mod[0] = mod & 1023; + + chip->op_value = value; + } + + if (chip->clk2) + { + chip->op_logsin[1] = chip->op_logsin[0]; + chip->op_shift[1] = chip->op_shift[0]; + chip->op_pow[1] = chip->op_pow[0]; + chip->op_mute[1] = chip->op_mute[0]; + chip->op_sign[1] = chip->op_sign[0]; + + for (i = 0; i < 13; i++) + { + chip->op_fb[0][i][1] = chip->op_fb[0][i][0]; + chip->op_fb[1][i][1] = chip->op_fb[1][i][0]; + } + chip->op_mod[1] = chip->op_mod[0]; + } + } + + { + int accm_out, top, clamplow, clamphigh; + + accm_out = chip->fsm_out[8] ? (chip->accm_value[1] & 0x7fff) : 0; + if (chip->fsm_out[8] && !(chip->accm_value[1] & 0x20000)) + accm_out |= 0x8000; + + top = (chip->accm_value[1] >> 15) & 7; + + clamplow = top == 4 || top == 5 || top == 6; + clamphigh = top == 3 || top == 2 || top == 1; + + if (chip->clk1) + chip->accm_load1_l = chip->fsm_out[8]; + chip->accm_load1 = !chip->accm_load1_l && chip->fsm_out[8]; + + if (chip->accm_load1) + { + chip->accm_clamplow = clamplow; + chip->accm_clamphigh = clamphigh; + chip->accm_top = (accm_out >> 9) & 127; + } + + if (chip->clk1) + { + int add = 0; + int op_out = chip->op_value; + int value, sign, top_unsigned, shift, accm_bit; + + if (op_out & 0x1000) + op_out |= ~0xfff; + + if (!(chip->eg_mute[1] & 2) && chip->fsm_out[0]) + add = chip->fsm_out[11] ? (op_out * 2) : op_out; + + value = chip->fsm_out[8] ? 0 : chip->accm_value[1]; + value += add; + + sign = ((chip->accm_top & 64) != 0 && !chip->accm_clamplow) || chip->accm_clamphigh; + + top_unsigned = chip->accm_top & 63; + if (!sign) + top_unsigned ^= 63; + + shift = 0; + + if (top_unsigned & 32) + shift |= 7; + if ((top_unsigned & 48) == 16) + shift |= 6; + if ((top_unsigned & 56) == 8) + shift |= 5; + if ((top_unsigned & 60) == 4) + shift |= 4; + if ((top_unsigned & 62) == 2) + shift |= 3; + if (top_unsigned == 1) + shift |= 2; + if (top_unsigned == 0) + shift |= 1; + if (chip->accm_clamplow) + shift |= 7; + if (chip->accm_clamphigh) + shift |= 7; + + accm_bit = 0; + + if (chip->fsm_out[6]) + accm_bit |= sign; + if (chip->accm_sel[1] & 1) + accm_bit |= (shift & 1) != 0; + if (chip->accm_sel[1] & 2) + accm_bit |= (shift & 2) != 0; + if (chip->accm_sel[1] & 4) + accm_bit |= (shift & 4) != 0; + + if ((chip->accm_sel[1] & 7) == 0 && !chip->fsm_out[6]) + { + if (top_unsigned & 32) + accm_bit |= (chip->accm_shifter[1] >> 6) & 1; + if ((top_unsigned & 48) == 16) + accm_bit |= (chip->accm_shifter[1] >> 5) & 1; + if ((top_unsigned & 56) == 8) + accm_bit |= (chip->accm_shifter[1] >> 4) & 1; + if ((top_unsigned & 60) == 4) + accm_bit |= (chip->accm_shifter[1] >> 3) & 1; + if ((top_unsigned & 62) == 2) + accm_bit |= (chip->accm_shifter[1] >> 2) & 1; + if (top_unsigned == 1) + accm_bit |= (chip->accm_shifter[1] >> 1) & 1; + if (top_unsigned == 0) + accm_bit |= chip->accm_shifter[1] & 1; + if (chip->accm_clamphigh) + accm_bit |= 1; + if (chip->accm_clamplow) + accm_bit = 0; + } + + chip->accm_value[0] = value & 0x3ffff; + chip->accm_shifter[0] = (chip->accm_shifter[1] >> 1) & 0x7fff; + if (chip->fsm_out[8]) + chip->accm_shifter[0] |= accm_out; + chip->accm_sel[0] = (chip->accm_sel[1] << 1) | chip->fsm_out[6]; + chip->accm_mo[0] = accm_bit; + } + + if (chip->clk2) + { + chip->accm_value[1] = chip->accm_value[0]; + chip->accm_shifter[1] = chip->accm_shifter[0]; + chip->accm_sel[1] = chip->accm_sel[0]; + chip->accm_mo[1] = chip->accm_mo[0]; + } + } + +end: + chip->o_sh = chip->fsm_out[3]; + chip->o_mo = chip->accm_mo[1]; + chip->o_irq_pull = irq; + chip->o_sy = chip->clk1; + + if (chip->io_read0) + { + chip->io_data &= ~6; + if (chip->reg_test & 64) + chip->io_data |= chip->dbg_serial[1] & 1; + if (irq) + chip->io_data |= 128; + if (chip->t1_status) + chip->io_data |= 64; + if (chip->t2_status) + chip->io_data |= 32; + if (chip->unk_status1) + chip->io_data |= 16; + if (chip->unk_status2) + chip->io_data |= 8; + } + + if (chip->io_dir) + { + chip->data_o = chip->io_data; + chip->data_z = 0; + } + else + chip->data_z = 1; +} diff --git a/thirdparty/adlmidi/chips/ym3812_lle/nuked_fmopl2.h b/thirdparty/adlmidi/chips/ym3812_lle/nuked_fmopl2.h new file mode 100644 index 000000000..4acc78b2e --- /dev/null +++ b/thirdparty/adlmidi/chips/ym3812_lle/nuked_fmopl2.h @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YM3812-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * YM3812 emulator + * Thanks: + * Travis Goodspeed: + * YM3812 decap and die shot + * + */ + +#pragma once + +#ifndef NUKED_FMOPL2_H +#define NUKED_FMOPL2_H + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct +{ + int mclk; + int address; + int data_i; + int ic; + int cs; + int rd; + int wr; +} fmopl2_input_t; + +typedef struct +{ + fmopl2_input_t input; + + int mclk1; + int mclk2; + int clk1; + int clk2; + + int prescaler_reset_l[2]; + int prescaler_cnt[2]; + int prescaler_l1[2]; + int prescaler_l2[2]; + + int reset1; + + int fsm_reset_l[2]; + int fsm_reset; /* wire */ + int fsm_cnt1[2]; + int fsm_cnt2[2]; + int fsm_cnt1_of; /* wire */ + int fsm_cnt2_of; /* wire */ + int fsm_sel[13]; + int fsm_cnt; /* wire */ + int fsm_ch_out; + int fsm_do_fb; + int fsm_load_fb; + int fsm_l1[2]; + int fsm_l2[2]; + int fsm_l3[2]; + int fsm_l4[2]; + int fsm_l5[2]; + int fsm_l6[2]; + int fsm_out[16]; + + int io_rd; + int io_wr; + int io_cs; + int io_a0; + + int io_read0; + int io_read1; + int io_write; + int io_write0; + int io_write1; + int io_dir; + int io_data; + + int data_latch; + + int write0; + int write0_sr; + int write0_latch[6]; + int write1; + int write1_sr; + int write1_latch[6]; + + int reg_sel1; + int reg_sel2; + int reg_sel3; + int reg_sel4; + int reg_sel8; + int reg_selbd; + int reg_test; + int reg_timer1; + int reg_timer2; + int reg_notesel; + int reg_csm; + int reg_da; + int reg_dv; + int rhythm; + int reg_rh_kon; + int reg_sel4_wr; /* wire */ + int reg_sel4_rst; /* wire */ + int reg_t1_mask; + int reg_t2_mask; + int reg_t1_start; + int reg_t2_start; + int reg_mode_b3; + int reg_mode_b4; + + int t1_cnt[2]; + int t2_cnt[2]; + int t1_of[2]; + int t2_of[2]; + int t1_status; + int t2_status; + int unk_status1; + int unk_status2; + int timer_st_load_l; + int timer_st_load; + int t1_start; + int t1_start_l[2]; + int t2_start_l[2]; + int t1_load; /* wire */ + int csm_load_l; + int csm_load; + int csm_kon; + int rh_sel0; + int rh_sel[2]; + + int keyon_comb; + int address; + int address_valid; + int address_valid_l[2]; + int address_valid2; + int data; + int slot_cnt1[2]; + int slot_cnt2[2]; + int slot_cnt; + int sel_ch; + + int ch_fnum[10][2]; + int ch_block[3][2]; + int ch_keyon[2]; + int ch_connect[2]; + int ch_fb[3][2]; + int op_multi[4][2]; + int op_ksr[2]; + int op_egt[2]; + int op_vib[2]; + int op_am[2]; + int op_tl[6][2]; + int op_ksl[2][2]; + int op_ar[4][2]; + int op_dr[4][2]; + int op_sl[4][2]; + int op_rr[4][2]; + int op_wf[2][2]; + int op_mod[2]; + int op_value; /* wire */ + + int eg_load1_l; + int eg_load1; + int eg_load2_l; + int eg_load2; + int eg_load3_l; + int eg_load3; + + int trem_carry[2]; + int trem_value[2]; + int trem_dir[2]; + int trem_step; + int trem_out; + int trem_of[2]; + + int eg_timer[2]; + int eg_timer_masked[2]; + int eg_carry[2]; + int eg_mask[2]; + int eg_subcnt[2]; + int eg_subcnt_l[2]; + int eg_sync_l[2]; + int eg_timer_low; + int eg_shift; + int eg_state[2][2]; + int eg_level[9][2]; + int eg_out[2]; + int eg_dokon; /* wire */ + int eg_mute[2]; + + int block; + int fnum; + int keyon; + int connect; + int connect_l[2]; + int fb; + int fb_l[2][2]; + int multi; + int ksr; + int egt; + int vib; + int am; + int tl; + int ksl; + int ar; + int dr; + int sl; + int rr; + int wf; + + int lfo_cnt[2]; + int t1_step; /* wire */ + int t2_step; /* wire */ + int am_step; /* wire */ + int vib_step; /* wire */ + int vib_cnt[2]; + int pg_phase[19][2]; + int dbg_serial[2]; + + int noise_lfsr[2]; + + int hh_load; + int tc_load; + int hh_bit2; + int hh_bit3; + int hh_bit7; + int hh_bit8; + int tc_bit3; + int tc_bit5; + int op_logsin[2]; + int op_shift[2]; + int op_pow[2]; + int op_mute[2]; + int op_sign[2]; + int op_fb[2][13][2]; + + int pg_out; /* wire */ + int pg_out_rhy; /* wire */ + + int accm_value[2]; + int accm_shifter[2]; + int accm_load1_l; + int accm_load1; + int accm_clamplow; + int accm_clamphigh; + int accm_top; + int accm_sel[2]; + int accm_mo[2]; + + int o_sh; + int o_mo; + int o_irq_pull; + int o_sy; + + int data_o; + int data_z; + + int o_clk1; + int o_clk2; + int o_reset1; + int o_write0; + int o_write1; + int o_data_latch; + +} fmopl2_t; + +extern void FMOPL2_Clock(fmopl2_t *chip); + +#ifdef __cplusplus +} +#endif + +#endif /* NUKED_FMOPL2_H */ diff --git a/thirdparty/adlmidi/chips/ymf262_lle.cpp b/thirdparty/adlmidi/chips/ymf262_lle.cpp new file mode 100644 index 000000000..6492fbbc0 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymf262_lle.cpp @@ -0,0 +1,82 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ymf262_lle.h" +#include "ymf262_lle/nuked_fmopl3.h" +#include "ymf262_lle/nopl3.h" +#include + +Ymf262LLEOPL3::Ymf262LLEOPL3() : + OPLChipBaseT() +{ + m_chip = nopl3_init(14318182, m_rate); + setRate(m_rate); +} + +Ymf262LLEOPL3::~Ymf262LLEOPL3() +{ + fmopl3_t *chip_r = reinterpret_cast(m_chip); + nopl3_shutdown(chip_r); +} + +void Ymf262LLEOPL3::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + nopl3_set_rate(m_chip, 14318182, rate); +} + +void Ymf262LLEOPL3::reset() +{ + OPLChipBaseT::reset(); + nopl3_reset(m_chip); +} + +void Ymf262LLEOPL3::writeReg(uint16_t addr, uint8_t data) +{ + nopl3_write(m_chip, 2 * ((addr >> 8) & 3), addr); + nopl3_write(m_chip, 1, data); +} + +void Ymf262LLEOPL3::writePan(uint16_t addr, uint8_t data) +{ + (void)addr; + (void)data; + // Uninmplemented +} + +void Ymf262LLEOPL3::nativeGenerate(int16_t *frame) +{ + nopl3_getsample_one_native(m_chip, frame); +} + +const char *Ymf262LLEOPL3::emulatorName() +{ + return "YMF262-LLE OPL3"; +} + +bool Ymf262LLEOPL3::hasFullPanning() +{ + return false; +} + +OPLChipBase::ChipType Ymf262LLEOPL3::chipType() +{ + return CHIPTYPE_OPL3; +} diff --git a/thirdparty/adlmidi/chips/ymf262_lle.h b/thirdparty/adlmidi/chips/ymf262_lle.h new file mode 100644 index 000000000..d772e61b6 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymf262_lle.h @@ -0,0 +1,46 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef YMF262LLE_OPL3_H +#define YMF262LLE_OPL3_H + +#include "opl_chip_base.h" + +class Ymf262LLEOPL3 final : public OPLChipBaseT +{ + void *m_chip; +public: + Ymf262LLEOPL3(); + ~Ymf262LLEOPL3() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // YMF262LLE_OPL3_H diff --git a/thirdparty/adlmidi/chips/ymf262_lle/LICENSE b/thirdparty/adlmidi/chips/ymf262_lle/LICENSE new file mode 100644 index 000000000..89e08fb00 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymf262_lle/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/thirdparty/adlmidi/chips/ymf262_lle/Readme.md b/thirdparty/adlmidi/chips/ymf262_lle/Readme.md new file mode 100644 index 000000000..d79b47ebe --- /dev/null +++ b/thirdparty/adlmidi/chips/ymf262_lle/Readme.md @@ -0,0 +1,7 @@ +# YMF262-LLE + +Yamaha YMF262 (OPL3) emulator using YMF262 die shot. + +Special thanks to John McMaster for decapping YMF262. + +https://siliconpr0n.org/map/yamaha/ymf262-m/ diff --git a/thirdparty/adlmidi/chips/ymf262_lle/nopl3.c b/thirdparty/adlmidi/chips/ymf262_lle/nopl3.c new file mode 100644 index 000000000..850cc1e3c --- /dev/null +++ b/thirdparty/adlmidi/chips/ymf262_lle/nopl3.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YMF262-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * YMF262 emulator + * Thanks: + * John McMaster (siliconpr0n.org): + * YMF262 decap and die shot + * + */ + +#include "nuked_fmopl3.h" +#include +#include +#include "nopl3.h" + +#define OPL_WRITEBUF_SIZE 2048 +#define OPL_WRITEBUF_DELAY 1 +#define RSM_FRAC 10 + +typedef struct _opl3_writebuf { + uint64_t time; + uint8_t reg; + uint8_t data; +} opl3_writebuf; + +typedef struct { + fmopl3_t chip; + + int sample_a; + int sample_b; + int o_sy; + int o_smpbd; + int o_smpac; + int shifter_ab; + + int32_t rateratio; + int32_t samplecnt; + int oldsample[2]; + + uint64_t writebuf_samplecnt; + uint32_t writebuf_cur; + uint32_t writebuf_last; + uint64_t writebuf_lasttime; + opl3_writebuf writebuf[OPL_WRITEBUF_SIZE]; +} nopl3_t; + +void nopl3_cycle(nopl3_t *chip) +{ + int i; + for (i = 0; i < 576/4; i++) + { + chip->chip.input.mclk = i & 1; + FMOPL3_Clock(&chip->chip); + + if (chip->o_sy && !chip->chip.o_sy) + { + + if (chip->o_smpac && !chip->chip.o_smpac) + { + chip->sample_a = chip->shifter_ab & 0xffff; + if (chip->sample_a & 0x8000) + { + chip->sample_a &= 0x7fff; + } + else + { + chip->sample_a |= ~0x7fff; + } + } + if (chip->o_smpbd && !chip->chip.o_smpbd) + { + chip->sample_b = chip->shifter_ab & 0xffff; + if (chip->sample_b & 0x8000) + { + chip->sample_b &= 0x7fff; + } + else + { + chip->sample_b |= ~0x7fff; + } + } + chip->shifter_ab = (chip->shifter_ab >> 1) | (chip->chip.o_doab << 15); + + chip->o_smpac = chip->chip.o_smpac; + chip->o_smpbd = chip->chip.o_smpbd; + } + + chip->o_sy = chip->chip.o_sy; + } +} + +void *nopl3_init(int clock, int samplerate) +{ + nopl3_t *chip = calloc(1, sizeof(nopl3_t)); + nopl3_set_rate(chip, clock, samplerate); + return chip; +} + +void nopl3_set_rate(void* chip_p, int clock, int samplerate) +{ + nopl3_t *chip = (nopl3_t*)chip_p; + + chip->chip.input.cs = 0; + chip->chip.input.ic = 1; + chip->chip.input.rd = 1; + chip->chip.input.wr = 1; + + samplerate *= 1; + + chip->rateratio = ((samplerate << RSM_FRAC) * (int64_t)288) / clock; + + /* printf("%i %i\n", clock, samplerate); */ + + nopl3_reset(chip); +} + +void nopl3_shutdown(void *chip) +{ + free(chip); +} + +void nopl3_reset(void *chip) +{ + nopl3_t* chip2 = chip; + int i = 0; + + chip2->chip.input.ic = 0; + for (i = 0; i < 100; i++) + { + nopl3_cycle(chip2); + } + chip2->chip.input.ic = 1; + for (i = 0; i < 100; i++) + { + nopl3_cycle(chip2); + } + +} + +void nopl3_write2(nopl3_t *chip, int port, int val) +{ + chip->chip.input.address = port; + chip->chip.input.data_i = val; + chip->chip.input.wr = 0; + FMOPL3_Clock(&chip->chip); /* propagate */ + chip->chip.input.wr = 1; + FMOPL3_Clock(&chip->chip); /* propagate */ +} + +void nopl3_getsample(void *chip, short *sndptr, int numsamples) +{ + nopl3_t* chip2 = chip; + int i, buf0, buf1; + short *p = sndptr; + opl3_writebuf* writebuf; + + for (i = 0; i < numsamples; i++) + { + while (chip2->samplecnt >= chip2->rateratio) + { + chip2->oldsample[0] = chip2->sample_a; + chip2->oldsample[1] = chip2->sample_b; + nopl3_cycle(chip2); + + while ((writebuf = &chip2->writebuf[chip2->writebuf_cur]), writebuf->time <= chip2->writebuf_samplecnt) + { + if (!(writebuf->reg & 4)) + { + break; + } + writebuf->reg &= 3; + nopl3_write2(chip2, writebuf->reg, writebuf->data); + chip2->writebuf_cur = (chip2->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; + } + chip2->writebuf_samplecnt++; + chip2->samplecnt -= chip2->rateratio; + } + + buf0 = (chip2->oldsample[0] * (chip2->rateratio - chip2->samplecnt) + + chip2->sample_a * chip2->samplecnt) / chip2->rateratio; + buf1 = (chip2->oldsample[1] * (chip2->rateratio - chip2->samplecnt) + + chip2->sample_b * chip2->samplecnt) / chip2->rateratio; + chip2->samplecnt += 1 << RSM_FRAC; + *p++ = buf1; + *p++ = buf0; + } +} + +void nopl3_getsample_one_native(void *chip, short *sndptr) +{ + nopl3_t* chip2 = chip; + short *p = sndptr; + opl3_writebuf* writebuf; + + chip2->oldsample[0] = chip2->sample_a; + chip2->oldsample[1] = chip2->sample_b; + nopl3_cycle(chip2); + + while ((writebuf = &chip2->writebuf[chip2->writebuf_cur]), writebuf->time <= chip2->writebuf_samplecnt) + { + if (!(writebuf->reg & 4)) + break; + + writebuf->reg &= 3; + nopl3_write2(chip2, writebuf->reg, writebuf->data); + chip2->writebuf_cur = (chip2->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; + } + + chip2->writebuf_samplecnt++; + chip2->samplecnt -= chip2->rateratio; + chip2->samplecnt += 1 << RSM_FRAC; + + *p++ = chip2->sample_b; + *p++ = chip2->sample_a; +} + +void nopl3_write(void *chip, int port, int val) +{ + nopl3_t* chip2 = chip; + uint64_t time1, time2; + opl3_writebuf *writebuf; + uint32_t writebuf_last; + + writebuf_last = chip2->writebuf_last; + writebuf = &chip2->writebuf[writebuf_last]; + + if (writebuf->reg & 4) + { + nopl3_write2(chip2, writebuf->reg & 1, writebuf->data); + nopl3_cycle(chip2); + + chip2->writebuf_cur = (writebuf_last + 1) % OPL_WRITEBUF_SIZE; + chip2->writebuf_samplecnt = writebuf->time; + } + + writebuf->reg = port | 4; + writebuf->data = val; + time1 = chip2->writebuf_lasttime + OPL_WRITEBUF_DELAY; + time2 = chip2->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + writebuf->time = time1; + chip2->writebuf_lasttime = time1; + chip2->writebuf_last = (writebuf_last + 1) % OPL_WRITEBUF_SIZE; +} diff --git a/thirdparty/adlmidi/chips/ymf262_lle/nopl3.h b/thirdparty/adlmidi/chips/ymf262_lle/nopl3.h new file mode 100644 index 000000000..5ad3e7511 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymf262_lle/nopl3.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YMF262-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * YMF262 emulator + * Thanks: + * John McMaster (siliconpr0n.org): + * YMF262 decap and die shot + * + */ + +#pragma once +#ifndef NOPL3_H +#define NOPL3_H + +#ifdef __cplusplus +extern "C" { +#endif + +void *nopl3_init(int clock, int samplerate); +void nopl3_set_rate(void *chip, int clock, int samplerate); +void nopl3_shutdown(void *chip); +void nopl3_reset(void *chip); + +void nopl3_getsample(void *chip, short *sndptr, int numsamples); +void nopl3_getsample_one_native(void *chip, short *sndptr); + +void nopl3_write(void *chip, int port, int val); + +#ifdef __cplusplus +} +#endif + +#endif /* NOPL3_H */ diff --git a/thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.c b/thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.c new file mode 100644 index 000000000..07fb6b93b --- /dev/null +++ b/thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.c @@ -0,0 +1,1689 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YMF262-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * YMF262 emulator + * Thanks: + * John McMaster (siliconpr0n.org): + * YMF262 decap and die shot + * + */ + +#include "nuked_fmopl3.h" + + +enum { + eg_state_attack = 0, + eg_state_decay, + eg_state_sustain, + eg_state_release +}; + +void FMOPL3_Clock(fmopl3_t *chip) +{ + int i, prescaler1_clk, bank_masked, reg_sel4_wr, reg_sel4_rst; + + chip->mclk1 = !chip->input.mclk; + chip->mclk2 = chip->input.mclk; + + chip->io_rd = !chip->input.rd; + chip->io_wr = !chip->input.wr; + chip->io_cs = !chip->input.cs; + chip->io_a0 = chip->input.address & 1; + chip->io_a1 = (chip->input.address & 2) != 0; + + if (chip->mclk1) + { + chip->ic_latch[0] = (chip->ic_latch[1] << 1) | (!chip->input.ic); + } + if (chip->mclk2) + { + chip->ic_latch[1] = chip->ic_latch[0]; + + chip->reset0 = (chip->ic_latch[1] & 2) != 0; + } + + chip->io_read = !chip->reset0 && chip->io_cs && chip->io_rd && !chip->io_a0 && !chip->io_a1; + chip->io_write = !chip->reset0 && chip->io_cs && chip->io_wr; + chip->io_write0 = !chip->reset0 && chip->io_cs && chip->io_wr && !chip->io_a0; + chip->io_write1 = !chip->reset0 && chip->io_cs && chip->io_wr && chip->io_a0; + + if (chip->reset0) + chip->data_latch = 0; + else if (chip->io_write) + chip->data_latch = chip->input.data_i & 255; + if (chip->reset0) + chip->bank_latch = 0; + else if (chip->io_write0) + chip->bank_latch = chip->io_a1; + + if (chip->mclk2) + { + chip->prescaler1_reset[1] = chip->prescaler1_reset[0]; + chip->prescaler1_cnt[1] = chip->prescaler1_cnt[0]; + } + + /* int prescaler1_clk = (chip->reg_test1 & 0x40) != 0 ? chip->input.mclk : !(chip->prescaler1_cnt[1] & 2);*/ + prescaler1_clk = chip->input.mclk; /* Temp: disable prescaler for performance reasons */ + + chip->aclk1 = !prescaler1_clk; + chip->aclk2 = prescaler1_clk; + + if (chip->aclk2) + { + chip->prescaler2_reset_l[1] = chip->prescaler2_reset_l[0]; + chip->prescaler2_cnt[1] = chip->prescaler2_cnt[0]; + chip->prescaler2_l1[1] = chip->prescaler2_l1[0]; + chip->prescaler2_l3[1] = chip->prescaler2_l3[0]; + chip->prescaler2_l5[1] = chip->prescaler2_l5[0]; + chip->prescaler2_l6[1] = chip->prescaler2_l6[0]; + } + + + chip->clk1 = chip->prescaler2_l1[1] && !chip->prescaler2_l2; + chip->clk2 = chip->prescaler2_l3[1] && !chip->prescaler2_l4; + + chip->rclk1 = chip->prescaler2_l6[1]; + chip->rclk2 = chip->prescaler2_l5[1]; + + if (chip->aclk1) + { + + int ga = (chip->data_latch & 0xe0) != 0; + + int write0 = ga && chip->write0 && (chip->reg_test1 & 16) != 0; + int write = chip->write1 || write0; + + chip->ra_w1_l1 = write; + } + + if (chip->clk2) + { + chip->write0_l[1] = chip->write0_l[0]; + chip->write0_l[3] = chip->write0_l[2]; + + chip->write1_l[1] = chip->write1_l[0]; + chip->write1_l[3] = chip->write1_l[2]; + + chip->write0 = chip->write0_l[3] && !chip->write0_l[1]; + chip->write1 = chip->write1_l[3] && !chip->write1_l[1]; + } + + /*////////////////////*/ + + /*if (chip->o_clk1 == chip->clk1 && chip->o_clk2 == chip->clk2 && chip->o_rclk1 == chip->rclk1 && chip->o_rclk2 == chip->rclk2 && chip->o_reset0 == chip->reset0 + && chip->o_ra_w1_l1 == chip->ra_w1_l1 && chip->o_bank_latch == chip->bank_latch && chip->o_data_latch == chip->data_latch) + goto end; */ /* opt*/ + + chip->o_clk1 = chip->clk1; + chip->o_clk2 = chip->clk2; + chip->o_rclk1 = chip->rclk1; + chip->o_rclk2 = chip->rclk2; + chip->o_reset0 = chip->reset0; + chip->o_data_latch = chip->data_latch; + chip->o_bank_latch = chip->bank_latch; + chip->o_ra_w1_l1 = chip->ra_w1_l1; + + if (chip->reset0) + { + chip->reg_sel1 = 0; + chip->reg_sel2 = 0; + chip->reg_sel3 = 0; + chip->reg_sel4 = 0; + chip->reg_sel5 = 0; + chip->reg_sel8 = 0; + chip->reg_selbd = 0; + } + else if (chip->write0) + { + chip->reg_sel1 = chip->data_latch == 1; + chip->reg_sel2 = chip->data_latch == 2; + chip->reg_sel3 = chip->data_latch == 3; + chip->reg_sel4 = chip->data_latch == 4; + chip->reg_sel5 = chip->data_latch == 5; + chip->reg_sel8 = chip->data_latch == 8; + chip->reg_selbd = chip->data_latch == 0xbd; + } + + if (chip->reset0) + chip->reg_new = 0; + else if (chip->write1 && chip->bank_latch && chip->reg_sel5) + chip->reg_new = chip->data_latch & 1; + + bank_masked = chip->reg_new && chip->bank_latch; + + if (chip->reset0) + { + chip->reg_test0 = 0; + chip->reg_test1 = 0; + chip->reg_timer1 = 0; + chip->reg_timer2 = 0; + chip->reg_notesel = 0; + chip->rhythm = 0; + chip->reg_rh_kon = 0; + chip->reg_da = 0; + chip->reg_dv = 0; + } + else if (chip->write1) + { + if (chip->reg_sel1 && !bank_masked) + chip->reg_test0 = chip->data_latch & 255; + if (chip->reg_sel2 && !bank_masked) + chip->reg_timer1 = chip->data_latch & 255; + if (chip->reg_sel3 && !bank_masked) + chip->reg_timer2 = chip->data_latch & 255; + + if (chip->reg_sel8 && !bank_masked) + { + chip->reg_notesel = (chip->data_latch & 64) != 0; + } + + if (chip->reg_selbd && !bank_masked) + { + chip->reg_rh_kon = chip->data_latch & 31; + chip->rhythm = (chip->data_latch & 32) != 0; + chip->reg_dv = (chip->data_latch & 64) != 0; + chip->reg_da = (chip->data_latch & 128) != 0; + } + + if (chip->reg_sel1 && bank_masked) + chip->reg_test1 = chip->data_latch & 255; + + if (chip->reg_sel4 && bank_masked) + chip->reg_4op = chip->data_latch & 63; + } + + reg_sel4_wr = chip->write1 && chip->reg_sel4 && !bank_masked && (chip->data_latch & 128) == 0; + reg_sel4_rst = (chip->write1 && chip->reg_sel4 && !bank_masked && (chip->data_latch & 128) != 0) || chip->reset0; + + if (chip->reset0) + { + chip->reg_t1_mask = 0; + chip->reg_t2_mask = 0; + chip->reg_t1_start = 0; + chip->reg_t2_start = 0; + } + else if (reg_sel4_wr) + { + chip->reg_t1_mask = (chip->data_latch & 64) != 0; + chip->reg_t2_mask = (chip->data_latch & 32) != 0; + chip->reg_t1_start = (chip->data_latch & 1) != 0; + chip->reg_t2_start = (chip->data_latch & 2) != 0; + } + + chip->reset1 = chip->reset0 || (chip->reg_test1 & 0xc0) == 0xc0; + + { + int write0, write; + int rst, of1, of2, of4; + /* int bclk = !prescaler2_reset && chip->prescaler2_l7 && (chip->prescaler2_cnt[1] & 1) == 0; */ + + int ga = (chip->data_latch & 0xe0) != 0; + + if (chip->reset1) + chip->ra_address_latch = 0; + else if (chip->write0 && ga) + chip->ra_address_latch = (bank_masked << 8) | chip->data_latch; + if (chip->reset1) + chip->ra_address_good = 0; + else if (chip->write0) + chip->ra_address_good = ga; + if (chip->reset1) + chip->ra_data_latch = 0; + else if (chip->write1 && chip->ra_address_good) + chip->ra_data_latch = chip->data_latch; + + write0 = ga && chip->write0 && (chip->reg_test1 & 16) != 0; + write = chip->write1 || write0; + + if (chip->aclk1) + chip->ra_w1_l1 = write; + chip->ra_write = (write && !chip->ra_w1_l1) || (chip->reset1 && chip->clk2); + if (chip->clk1) + chip->ra_w1_l2 = write; + chip->ra_write_a = write && !chip->ra_w1_l2; + + if (chip->clk1) + { + chip->ra_rst_l[0] = chip->reset1; + rst = (chip->reset1 && !chip->ra_rst_l[1]) || chip->fsm_out[5]; + + of1 = (chip->ra_cnt1[1] & 5) == 5; + of2 = (chip->ra_cnt2[1] & 2) == 2 && of1; + of4 = (chip->ra_cnt4[1] & 2) == 2; + + if (rst || of1) + chip->ra_cnt1[0] = 0; + else + chip->ra_cnt1[0] = (chip->ra_cnt1[1] + 1) & 7; + + if (rst || of2) + chip->ra_cnt2[0] = 0; + else + chip->ra_cnt2[0] = (chip->ra_cnt2[1] + of1) & 3; + + if (rst) + chip->ra_cnt3[0] = 0; + else + chip->ra_cnt3[0] = (chip->ra_cnt3[1] + of2) & 1; + + if (rst || of4 || of1) + chip->ra_cnt4[0] = 0; + else + chip->ra_cnt4[0] = (chip->ra_cnt4[1] + 1) & 3; + + } + + if (chip->clk2) + { + chip->ra_rst_l[1] = chip->ra_rst_l[0]; + chip->ra_cnt1[1] = chip->ra_cnt1[0]; + chip->ra_cnt2[1] = chip->ra_cnt2[0]; + chip->ra_cnt3[1] = chip->ra_cnt3[0]; + chip->ra_cnt4[1] = chip->ra_cnt4[0]; + chip->ra_cnt = (chip->ra_cnt3[1] << 5) | (chip->ra_cnt2[1] << 3) | chip->ra_cnt1[1]; + } + + if (chip->ra_write || chip->clk1) + { + static const int ch_map[32] = { + 0, 1, 2, -1, + 3, 4, 5, -1, + 6, 7, 8, -1, + -1, -1, -1, -1, + 9, 10, 11, -1, + 12, 13, 14, -1, + 15, 16, 17, -1, + -1, -1, -1, -1 + }; + + static const int op_map[64] = { + 0, 1, 2, 3, 4, 5, -1, -1, + 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 18, 19, 20, 21, 22, 23, -1, -1, + 24, 25, 26, 27, 28, 29, -1, -1, + 30, 31, 32, 33, 34, 35, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 + }; + + int ch_address_write, add, ch_address_mapped, ch_address_mapped2; + int ch_address_read, ch_address, ch_address_read_4op; + int ch_address_4op, ch_address_fb, idx1, idx2, idx3; + + int bank = (chip->ra_address_latch & 0x100) != 0; + int op_address = chip->ra_write_a ? ((chip->ra_address_latch & 0x1f) | (bank << 5)) : chip->ra_cnt; + int idx = op_map[op_address]; + + if (chip->ra_write && idx != -1) + { + if ((chip->ra_address_latch & 0xe0) == 0x20 || write0 || chip->reset1) + { + chip->ra_multi[idx] = chip->ra_data_latch & 15; + chip->ra_ksr[idx] = (chip->ra_data_latch >> 4) & 1; + chip->ra_egt[idx] = (chip->ra_data_latch >> 5) & 1; + chip->ra_vib[idx] = (chip->ra_data_latch >> 6) & 1; + chip->ra_am[idx] = (chip->ra_data_latch >> 7) & 1; + } + if ((chip->ra_address_latch & 0xe0) == 0x40 || write0 || chip->reset1) + { + chip->ra_tl[idx] = chip->ra_data_latch & 63; + chip->ra_ksl[idx] = (chip->ra_data_latch >> 6) & 3; + } + if ((chip->ra_address_latch & 0xe0) == 0x60 || write0 || chip->reset1) + { + chip->ra_dr[idx] = chip->ra_data_latch & 15; + chip->ra_ar[idx] = (chip->ra_data_latch >> 4) & 15; + } + if ((chip->ra_address_latch & 0xe0) == 0x80 || write0 || chip->reset1) + { + chip->ra_rr[idx] = chip->ra_data_latch & 15; + chip->ra_sl[idx] = (chip->ra_data_latch >> 4) & 15; + } + if ((chip->ra_address_latch & 0xe0) == 0xe0 || write0 || chip->reset1) + { + int data = chip->ra_data_latch & 3; + if (chip->reg_new) + data |= chip->ra_data_latch & 4; + chip->ra_wf[idx] = data; + } + } + + ch_address_write = chip->ra_address_latch & 15; + add = 0; + + if (ch_address_write == 3 || ch_address_write == 4 || ch_address_write == 5) + add |= 1; + if (ch_address_write == 6 || ch_address_write == 7 || ch_address_write == 8) + add |= 2; + + ch_address_mapped = (ch_address_write & 1) + (add & 1); + ch_address_mapped |= add & 2; + ch_address_mapped += ch_address_write & 14; + ch_address_mapped &= 15; + ch_address_mapped |= bank << 4; + ch_address_mapped2 = ch_address_mapped & 3; + if ((ch_address_mapped & 12) == 8) + ch_address_mapped2 |= 4; + if ((ch_address_mapped & 12) == 0) + ch_address_mapped2 |= 8; + if ((ch_address_mapped & 28) == 0 || (ch_address_mapped & 28) == 20 || (ch_address_mapped & 28) == 24) + ch_address_mapped2 |= 16; + + ch_address_read = (chip->ra_cnt4[1] & 3) | (chip->ra_cnt2[1] << 2) | (chip->ra_cnt3[1] << 4); + ch_address = chip->ra_write_a ? ch_address_mapped : ch_address_read; + ch_address_read_4op = ch_address_read; + + if ((chip->ra_cnt2[1] & 2) == 0) + { + switch (chip->ra_cnt3[1] * 4 + chip->ra_cnt4[1]) + { + case 0: /* 0, 3, 6, 9 */ + if (chip->reg_4op & 1) + ch_address_read_4op &= ~4; + break; + case 1: /* 1, 4, 7, 10 */ + if (chip->reg_4op & 2) + ch_address_read_4op &= ~4; + break; + case 2: /* 2, 5, 8, 11 */ + if (chip->reg_4op & 4) + ch_address_read_4op &= ~4; + break; + case 4: /* 0, 3, 6, 9 */ + if (chip->reg_4op & 8) + ch_address_read_4op &= ~4; + break; + case 5: /* 1, 4, 7, 10 */ + if (chip->reg_4op & 16) + ch_address_read_4op &= ~4; + break; + case 6: /* 2, 5, 8, 11 */ + if (chip->reg_4op & 32) + ch_address_read_4op &= ~4; + break; + } + } + + ch_address_4op = chip->ra_write_a ? ch_address_mapped : ch_address_read_4op; + ch_address_fb = chip->ra_write_a ? ch_address_mapped2 : ch_address_read; + + idx1 = ch_map[ch_address]; + idx2 = ch_map[ch_address_4op]; + idx3 = ch_map[ch_address_fb]; + + if (chip->ra_write && idx1 != -1) + { + if ((chip->ra_address_latch & 0xf0) == 0xc0 || write0 || chip->reset1) + { + int pan_data = 0; + chip->ra_connect[idx1] = chip->ra_data_latch & 1; + if (!chip->reg_new || chip->reset1) + pan_data |= 3; + if (chip->reg_new) + pan_data |= (chip->ra_data_latch >> 4) & 15; + chip->ra_pan[idx1] = pan_data; + } + } + + if (chip->ra_write && idx2 != -1) + { + if ((chip->ra_address_latch & 0xf0) == 0xa0 || write0 || chip->reset1) + { + chip->ra_fnum[idx2] &= 0x300; + chip->ra_fnum[idx2] |= chip->ra_data_latch & 0xff; + } + + if ((chip->ra_address_latch & 0xf0) == 0xb0 || write0 || chip->reset1) + { + chip->ra_fnum[idx2] &= 0xff; + chip->ra_fnum[idx2] |= (chip->ra_data_latch & 3) << 8; + chip->ra_block[idx2] = (chip->ra_data_latch >> 2) & 7; + chip->ra_keyon[idx2] = (chip->ra_data_latch >> 5) & 1; + } + } + + if (chip->ra_write && idx3 != -1) + { + if ((chip->ra_address_latch & 0xf0) == 0xc0 || write0 || chip->reset1) + { + chip->ra_connect_pair[idx3] = chip->ra_data_latch & 1; + chip->ra_fb[idx3] = (chip->ra_data_latch >> 1) & 7; + } + } + + if (chip->clk1) + { + if (idx != -1) + { + chip->multi[0] = chip->ra_multi[idx]; + chip->ksr[0] = chip->ra_ksr[idx]; + chip->egt[0] = chip->ra_egt[idx]; + chip->am[0] = chip->ra_am[idx]; + chip->vib[0] = chip->ra_vib[idx]; + chip->tl[0] = chip->ra_tl[idx]; + chip->ksl[0] = chip->ra_ksl[idx]; + chip->ar[0] = chip->ra_ar[idx]; + chip->dr[0] = chip->ra_dr[idx]; + chip->sl[0] = chip->ra_sl[idx]; + chip->rr[0] = chip->ra_rr[idx]; + chip->wf[0] = chip->ra_wf[idx]; + } + if (idx1 != -1) + { + chip->connect[0] = chip->ra_connect[idx1]; + chip->pan[0] = chip->ra_pan[idx1]; + } + if (idx2 != -1) + { + chip->fnum[0] = chip->ra_fnum[idx2]; + chip->block[0] = chip->ra_block[idx2]; + chip->keyon[0] = chip->ra_keyon[idx2]; + } + if (idx3 != -1) + { + chip->connect_pair[0] = chip->ra_connect_pair[idx3]; + chip->fb[0] = chip->ra_fb[idx3]; + } + } + } + + if (chip->clk2) + { + chip->multi[1] = chip->multi[0]; + chip->ksr[1] = chip->ksr[0]; + chip->egt[1] = chip->egt[0]; + chip->am[1] = chip->am[0]; + chip->vib[1] = chip->vib[0]; + chip->tl[1] = chip->tl[0]; + chip->ksl[1] = chip->ksl[0]; + chip->ar[1] = chip->ar[0]; + chip->dr[1] = chip->dr[0]; + chip->sl[1] = chip->sl[0]; + chip->rr[1] = chip->rr[0]; + chip->wf[1] = chip->wf[0]; + + chip->connect[1] = chip->connect[0]; + chip->pan[1] = chip->pan[0]; + + chip->fnum[1] = chip->fnum[0]; + chip->block[1] = chip->block[0]; + chip->keyon[1] = chip->keyon[0]; + + chip->connect_pair[1] = chip->connect_pair[0]; + chip->fb[1] = chip->fb[0]; + } + } + + if (chip->clk1) + { + chip->connect_l[0] = (chip->connect_l[1] << 1) | chip->connect[1]; + chip->connect_pair_l[0] = (chip->connect_pair_l[1] << 1) | chip->connect_pair[1]; + chip->fb_l[0][0] = chip->fb[1]; + chip->fb_l[1][0] = chip->fb_l[0][1]; + chip->pan_l[0][0] = chip->pan[1]; + chip->pan_l[1][0] = chip->pan_l[0][1]; + } + + if (chip->clk2) + { + chip->connect_l[1] = chip->connect_l[0]; + chip->connect_pair_l[1] = chip->connect_pair_l[0]; + chip->fb_l[0][1] = chip->fb_l[0][0]; + chip->fb_l[1][1] = chip->fb_l[1][0]; + chip->pan_l[0][1] = chip->pan_l[0][0]; + chip->pan_l[1][1] = chip->pan_l[1][0]; + } + + { + int fsm_4op = 0, con_4op, fsm_reset, fsm_of1, fsm_of2; + int fsm_mc, fsm_mc_4op, rhy_19_20; + + switch (chip->fsm_cnt) + { + case 5: /* 5 */ + fsm_4op = (chip->reg_4op & 1) != 0; + break; + case 8: /* 6 */ + fsm_4op = (chip->reg_4op & 2) != 0; + break; + case 9: /* 7 */ + fsm_4op = (chip->reg_4op & 4) != 0; + break; + case 37: /* 23 */ + fsm_4op = (chip->reg_4op & 8) != 0; + break; + case 40: /* 24 */ + fsm_4op = (chip->reg_4op & 16) != 0; + break; + case 41: /* 25 */ + fsm_4op = (chip->reg_4op & 32) != 0; + break; + } + + con_4op = fsm_4op && (chip->fsm_l10[1] & 4) != 0; /* 01 connect */ + + if (chip->clk1) + { + fsm_reset = (chip->fsm_reset_l[1] & 2) == 0 && chip->reset1; + chip->fsm_reset_l[0] = (chip->fsm_reset_l[1] << 1) | chip->reset1; + + fsm_of1 = (chip->fsm_cnt1[1] & 5) == 5; + fsm_of2 = (chip->fsm_cnt2[1] & 2) == 2 && fsm_of1; + + if (fsm_reset || fsm_of1) + chip->fsm_cnt1[0] = 0; + else + chip->fsm_cnt1[0] = (chip->fsm_cnt1[1] + 1) & 7; + + if (fsm_reset || fsm_of2) + chip->fsm_cnt2[0] = 0; + else + chip->fsm_cnt2[0] = (chip->fsm_cnt2[1] + fsm_of1) & 3; + + if (fsm_reset) + chip->fsm_cnt3[0] = 0; + else + chip->fsm_cnt3[0] = (chip->fsm_cnt3[1] + fsm_of2) & 1; + + chip->fsm_l1[0] = chip->fsm_cnt == 53; + chip->fsm_l2[0] = chip->fsm_cnt == 16; + chip->fsm_l3[0] = chip->fsm_cnt == 20; + chip->fsm_l4[0] = chip->fsm_cnt == 52; + chip->fsm_l5[0] = (chip->fsm_l5[1] << 1) | ((chip->fsm_cnt & 56) == 0); + chip->fsm_l6[0] = (chip->fsm_l6[1] << 1) | ((chip->fsm_cnt & 56) == 8 || (chip->fsm_cnt & 62) == 16); + chip->fsm_l7[0] = (chip->fsm_l7[1] << 1) | ((chip->fsm_cnt & 56) == 40 || (chip->fsm_cnt & 62) == 48); + chip->fsm_l8[0] = (chip->fsm_l8[1] << 1) | ((chip->fsm_cnt & 48) == 16); + chip->fsm_l9[0] = (chip->fsm_l9[1] << 1) | con_4op; + chip->fsm_l10[0] = (chip->fsm_l10[1] << 1) | ((chip->connect_l[1] & 2) == 0 && (chip->connect_pair_l[1] & 2) != 0); + } + + if (chip->clk2) + { + chip->fsm_reset_l[1] = chip->fsm_reset_l[0]; + chip->fsm_cnt1[1] = chip->fsm_cnt1[0]; + chip->fsm_cnt2[1] = chip->fsm_cnt2[0]; + chip->fsm_cnt3[1] = chip->fsm_cnt3[0]; + + chip->fsm_cnt = (chip->fsm_cnt3[1] << 5) | (chip->fsm_cnt2[1] << 3) | chip->fsm_cnt1[1]; + + chip->fsm_l1[1] = chip->fsm_l1[0]; + chip->fsm_l2[1] = chip->fsm_l2[0]; + chip->fsm_l3[1] = chip->fsm_l3[0]; + chip->fsm_l4[1] = chip->fsm_l4[0]; + chip->fsm_l5[1] = chip->fsm_l5[0]; + chip->fsm_l6[1] = chip->fsm_l6[0]; + chip->fsm_l7[1] = chip->fsm_l7[0]; + chip->fsm_l8[1] = chip->fsm_l8[0]; + chip->fsm_l9[1] = chip->fsm_l9[0]; + chip->fsm_l10[1] = chip->fsm_l10[0]; + } + { + chip->fsm_out[0] = chip->fsm_l1[1]; /* 0 */ + chip->fsm_out[1] = chip->fsm_cnt == 16; /* 12 */ + chip->fsm_out[2] = chip->fsm_l2[1]; /* 13 */ + chip->fsm_out[3] = chip->fsm_cnt == 20; /* 16 */ + chip->fsm_out[4] = chip->fsm_l3[1]; /* 17 */ + chip->fsm_out[5] = chip->fsm_cnt == 52; /* 34 */ + chip->fsm_out[6] = chip->fsm_l4[1]; /* 35 */ + chip->fsm_out[7] = (chip->fsm_l5[1] & 4) != 0 || ((chip->fsm_cnt & 56) == 0); /* 0-8 */ + chip->fsm_out[8] = (chip->fsm_cnt & 32) == 0; + chip->fsm_out[9] = (chip->fsm_l6[1] & 2) != 0; + chip->fsm_out[10] = (chip->fsm_l7[1] & 2) != 0; + chip->fsm_out[11] = chip->rhythm && (chip->fsm_l8[1] & 2) != 0; /* r 14, 15, 16, 17, 18, 19 */ + + fsm_mc = !((chip->fsm_cnt & 5) == 4 || (chip->fsm_cnt & 2) != 0); + fsm_mc_4op = fsm_mc && !fsm_4op; + rhy_19_20 = chip->rhythm && (chip->fsm_cnt == 19 || chip->fsm_cnt == 20); + + chip->fsm_out[12] = fsm_mc_4op && !(chip->rhythm && (chip->fsm_cnt == 16 || chip->fsm_cnt == 17)); /* feedback */ + chip->fsm_out[14] = con_4op || (!fsm_4op && !(chip->fsm_l9[1] & 4) && (chip->connect_l[1] & 2) != 0); /* connect */ + chip->fsm_out[13] = !(chip->rhythm && chip->fsm_cnt == 18) && (fsm_mc_4op || rhy_19_20 || chip->fsm_out[14]); /* output */ + chip->fsm_out[15] = !fsm_mc && !rhy_19_20; /* load fb */ + chip->fsm_out[16] = !fsm_mc_4op && !rhy_19_20; /* modulate */ + } + } + + if (chip->clk1) + chip->timer_st_load_l = chip->fsm_out[6]; + chip->timer_st_load = chip->fsm_out[6] && !chip->timer_st_load_l; + + if (chip->timer_st_load) + { + chip->t1_start = chip->reg_t1_start; + chip->t2_start = chip->reg_t2_start; + } + + if (chip->clk1) + { + int lfo = chip->lfo_cnt[1]; + int add = chip->fsm_out[6]; + int reset = (chip->reg_test0 & 2) != 0 || chip->reset1; + + chip->lfo_cnt[0] = reset ? 0 : (lfo + add) & 1023; + chip->vib_cnt[0] = reset ? 0 : (chip->vib_cnt[1] + chip->vib_step) & 7; + } + + if (chip->clk2) + { + chip->lfo_cnt[1] = chip->lfo_cnt[0]; + chip->vib_cnt[1] = chip->vib_cnt[0]; + } + + { + int lfo = chip->lfo_cnt[1]; + int add = chip->fsm_out[6]; + + chip->t1_step = (((lfo & 3) + add) & 4) != 0; + chip->t2_step = (((lfo & 15) + add) & 16) != 0; + chip->am_step = (((lfo & 63) + add) & 64) != 0; + chip->vib_step = (((lfo & 1023) + add) & 1024) != 0; + chip->vib_step |= (chip->reg_test0 & 16) != 0 && add; + } + + if (chip->clk1) + { + int value = (chip->t1_of[1] || (chip->t1_start_l[1] & 3) == 1) ? chip->reg_timer1 : chip->t1_cnt[1]; + value += ((chip->t1_start_l[1] & 1) != 0 && chip->t1_step) || (chip->reg_test1 & 8) != 0; + chip->t1_of[0] = (value & 256) != 0; + chip->t1_cnt[0] = (chip->t1_start_l[1] & 1) == 0 ? 0 : (value & 255); + + value = (chip->t2_of[1] || (chip->t2_start_l[1] & 3) == 1) ? chip->reg_timer2 : chip->t2_cnt[1]; + value += ((chip->t2_start_l[1] & 1) != 0 && chip->t2_step) || (chip->reg_test1 & 8) != 0; + chip->t2_of[0] = (value & 256) != 0; + chip->t2_cnt[0] = (chip->t2_start_l[1] & 1) == 0 ? 0 : (value & 255); + + chip->t1_start_l[0] = (chip->t1_start_l[1] << 1) | chip->t1_start; + chip->t2_start_l[0] = (chip->t2_start_l[1] << 1) | chip->t2_start; + } + if (chip->clk2) + { + chip->t1_cnt[1] = chip->t1_cnt[0]; + chip->t1_of[1] = chip->t1_of[0]; + chip->t2_cnt[1] = chip->t2_cnt[0]; + chip->t2_of[1] = chip->t2_of[0]; + + chip->t1_start_l[1] = chip->t1_start_l[0]; + chip->t2_start_l[1] = chip->t2_start_l[0]; + } + + if (reg_sel4_rst || chip->reg_t1_mask) + chip->t1_status = 0; + else if (chip->t1_of[1]) + chip->t1_status = 1; + + if (reg_sel4_rst || chip->reg_t2_mask) + chip->t2_status = 0; + else if (chip->t2_of[1]) + chip->t2_status = 1; + + chip->rh_sel0 = chip->rhythm && chip->fsm_out[1]; + + if (chip->clk1) + { + chip->rh_sel[0] = (chip->rh_sel[1] << 1) | chip->rh_sel0; + } + if (chip->clk2) + { + chip->rh_sel[1] = chip->rh_sel[0]; + } + + chip->keyon_comb = chip->keyon[1] + || (chip->rh_sel0 && (chip->reg_rh_kon & 16) != 0) /* bd0 */ + || ((chip->rh_sel[1] & 1) != 0 && (chip->reg_rh_kon & 1) != 0) /* hh */ + || ((chip->rh_sel[1] & 2) != 0 && (chip->reg_rh_kon & 4) != 0) /* tom */ + || ((chip->rh_sel[1] & 4) != 0 && (chip->reg_rh_kon & 16) != 0) /* bd1 */ + || ((chip->rh_sel[1] & 8) != 0 && (chip->reg_rh_kon & 8) != 0) /* sd */ + || ((chip->rh_sel[1] & 16) != 0 && (chip->reg_rh_kon & 2) != 0); /* tc */ + + + if (chip->clk1) + { + chip->trem_load_l = chip->fsm_out[0]; + chip->trem_st_load_l = chip->fsm_out[6]; + chip->eg_load_l = chip->eg_load_l1[1]; + } + chip->trem_load = !chip->trem_load_l && chip->fsm_out[0]; + chip->trem_st_load = !chip->trem_st_load_l && chip->fsm_out[6]; + chip->eg_load = !chip->eg_load_l && chip->eg_load_l1[1]; + + { + if (chip->trem_st_load) + chip->trem_step = chip->am_step; + if (chip->trem_load) + chip->trem_out = chip->trem_value[1] & 127; + + if (chip->clk1) + { + int bit = chip->trem_value[1] & 1; + int reset = chip->reset1 || (chip->reg_test0 & 2) != 0; + + int step = ((chip->trem_step || (chip->reg_test0 & 16) != 0) && (chip->fsm_out[0] || chip->trem_dir[1])) + && chip->fsm_out[7]; + int carry = chip->fsm_out[7] && chip->trem_carry[1]; + int of; + + bit += step + carry; + + of = (chip->trem_out == 0) || (chip->trem_out & 105) == 105; + + chip->trem_carry[0] = (bit & 2) != 0; + chip->trem_value[0] = (chip->trem_value[1] >> 1) & 255; + if (!reset) + chip->trem_value[0] |= (bit & 1) << 8; + chip->trem_of[0] = of; + + if (reset) + chip->trem_dir[0] = 0; + else + chip->trem_dir[0] = chip->trem_dir[1] ^ (of && !chip->trem_of[1]); + } + + if (chip->clk2) + { + chip->trem_carry[1] = chip->trem_carry[0]; + chip->trem_value[1] = chip->trem_value[0]; + chip->trem_of[1] = chip->trem_of[0]; + chip->trem_dir[1] = chip->trem_dir[0]; + } + } + + { + + if (chip->reset1) + { + chip->eg_timer_low = 0; + chip->eg_shift = 0; + } + else if (chip->eg_load) + { + chip->eg_timer_low = chip->eg_timer_o[3] | (chip->eg_timer_o[1] << 1); + chip->eg_shift = 0; + if (chip->eg_timer_masked[1] & 0x1555) + chip->eg_shift |= 1; + if (chip->eg_timer_masked[1] & 0x666) + chip->eg_shift |= 2; + if (chip->eg_timer_masked[1] & 0x1878) + chip->eg_shift |= 4; + if (chip->eg_timer_masked[1] & 0x1f80) + chip->eg_shift |= 8; + } + + if (chip->clk1) + { + int bit = chip->eg_timer_o[3]; + int bit2; + int carry = chip->eg_carry[1] || (chip->eg_subcnt[1] && chip->eg_sync_l[1]); + int rst; + + bit += carry; + rst = chip->reset1 || (chip->reg_test1 & 8) != 0; + + if (rst) + bit2 = 0; + else + bit2 = bit & 1; + + chip->eg_timer_i = bit2; + chip->eg_carry[0] = (bit & 2) != 0; + chip->eg_sync_l[0] = chip->fsm_out[6]; + chip->eg_mask[0] = (rst || chip->fsm_out[6]) ? 0 : + (chip->eg_mask[1] || bit2); + chip->eg_timer_masked[0] = (chip->eg_timer_masked[1] >> 1) & 0x7ffffffffLL; + if (!chip->eg_mask[1]) + chip->eg_timer_masked[0] |= ((int64_t)bit2) << 35; + if (!chip->eg_timer_dbg[1] && (chip->reg_test0 & 64) != 0) + chip->eg_timer_masked[0] |= 1LL << 35; + + if (chip->reset1) + chip->eg_subcnt[0] = 0; + else + chip->eg_subcnt[0] = chip->eg_subcnt[1] ^ chip->fsm_out[6]; + + chip->eg_load_l1[0] = chip->eg_subcnt[1] && chip->fsm_out[6]; + + chip->eg_timer_dbg[0] = (chip->reg_test0 & 64) != 0; + } + + if (chip->clk2) + { + chip->eg_carry[1] = chip->eg_carry[0]; + chip->eg_sync_l[1] = chip->eg_sync_l[0]; + chip->eg_mask[1] = chip->eg_mask[0]; + chip->eg_timer_masked[1] = chip->eg_timer_masked[0]; + chip->eg_subcnt[1] = chip->eg_subcnt[0]; + chip->eg_load_l1[1] = chip->eg_load_l1[0]; + chip->eg_timer_dbg[1] = chip->eg_timer_dbg[0]; + } + } + + if (chip->clk1) + { + static const int eg_stephi[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } + }; + + static const int eg_ksltable[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 + }; + + static const int eg_kslshift[4] = { + 31, 1, 2, 0 + }; + + int rst = chip->reset1 || (chip->reg_test1 & 32) != 0; + + int state = chip->eg_state_o[3]; + int dokon = state == eg_state_release && chip->keyon_comb; + int rate_sel = dokon ? eg_state_attack : state; + int rate = 0; + int ksr, sl, ns, rate_hi, maxrate, inclow, stephi, step1, step2, step3; + int level, slreach, zeroreach, silent, nextstate; + int linear, exponent, instantattack, mute, level2, add, addshift; + int levelnext, ksl, ksl_hi, ksltl, tremolo; + int ksltltrem, levelof, totallevel, totallevelclamp; + + if (rate_sel == 0) + rate |= chip->ar[1]; + if (rate_sel == 1) + rate |= chip->dr[1]; + if (rate_sel == 3 || (rate_sel == 2 && !chip->egt[1])) + rate |= chip->rr[1]; + + sl = chip->sl[1]; + if (chip->sl[1] == 15) + sl |= 16; + + ns = chip->reg_notesel ? (chip->fnum[1] & 256) != 0 : (chip->fnum[1] & 512) != 0; + + if (chip->ksr[1]) + ksr = (chip->block[1] << 1) | ns; + else + ksr = chip->block[1] >> 1; + + rate_hi = rate + (ksr >> 2); + if (rate_hi & 16) + rate_hi = 15; + + maxrate = rate_hi == 15; + + /* int rate12 = rate_hi == 12; */ + /* int rate13 = rate_hi == 13; */ + /* int rate14 = rate_hi == 14; */ + + inclow = 0; + + if (rate_hi < 12 && rate != 0 && chip->eg_subcnt[1]) + { + int sum = (rate_hi + chip->eg_shift) & 15; + switch (sum) + { + case 12: + inclow = 1; + break; + case 13: + inclow = (ksr & 2) != 0; + break; + case 14: + inclow = (ksr & 1) != 0; + break; + } + } + + stephi = eg_stephi[ksr & 3][chip->eg_timer_low]; + + step1 = 0; + step2 = 0; + step3 = 0; + + switch (rate_hi) + { + case 12: + step1 = stephi || chip->eg_subcnt[1]; + break; + case 13: + if (stephi) + step2 = 1; + else + step1 = 1; + break; + case 14: + if (stephi) + step3 = 1; + else + step2 = 1; + break; + case 15: + step3 = 1; + break; + } + + step1 |= inclow; + + level = chip->eg_level_o[3]; + slreach = (level >> 4) == sl; + zeroreach = level == 0; + silent = (level & 0x1f8) == 0x1f8; + + nextstate = eg_state_attack; + + if (rst) + nextstate = eg_state_release; + else if (dokon) + nextstate = eg_state_attack; + else + { + if (!chip->keyon_comb) + nextstate = eg_state_release; + else if (state == eg_state_attack) + nextstate = zeroreach ? eg_state_decay : eg_state_attack; + else if (state == eg_state_decay) + nextstate = slreach ? eg_state_sustain : eg_state_decay; + else if (state == eg_state_sustain) + nextstate = eg_state_sustain; + else if (state == eg_state_release) + nextstate = eg_state_release; + } + + linear = !dokon && !silent && ((state & 2) != 0 || (state == eg_state_decay && !slreach)); + exponent = state == eg_state_attack && chip->keyon_comb && !maxrate && !zeroreach; + instantattack = (dokon && maxrate) || (chip->reg_test0 & 16) != 0; + mute = rst || (state != eg_state_attack && silent && !dokon && !(chip->reg_test0 & 16)); + + level2 = mute ? 0x1ff : (instantattack ? 0 : level); + + add = 0; + addshift = 0; + + if (exponent) + add |= (level >> 1) ^ 0xff; + if (linear) + add |= 4; + + if (exponent && (step1 || step2 || step3)) + addshift |= 256; + + if (step1) + addshift |= (add >> 2) | (exponent << 6) | (exponent << 7) | linear; + if (step2) + addshift |= (add >> 1) | (exponent << 7) | (linear << 1); + if (step3) + addshift |= (add >> 0) | (linear << 2); + + levelnext = level2 + addshift; + ksl = eg_ksltable[chip->fnum[1] >> 6]; + ksl_hi = (ksl & 64) != 0; + + ksl = (ksl & 63) + (chip->block[1] << 3); + if (ksl_hi || (ksl & 64) != 0) + ksl = ksl & 63; + else + ksl = 0; + + ksl = (ksl << 2) >> eg_kslshift[chip->ksl[1]]; + ksltl = ksl + (chip->tl[1] << 2); + + if (!chip->am[1]) + tremolo = 0; + else if (chip->reg_da) + tremolo = chip->trem_out >> 2; + else + tremolo = chip->trem_out >> 4; + + ksltltrem = ksltl + tremolo; + levelof = 0; + + if (ksltltrem & 0x200) + levelof = 1; + + totallevel = level + (ksltltrem & 0x1ff); + if (totallevel & 0x200) + levelof = 1; + + totallevelclamp = (chip->reg_test0 & 1) != 0 ? 0 : (levelof ? 0x1ff : (totallevel & 0x1ff)); + + chip->eg_out[0] = totallevelclamp; + chip->eg_dbg[0] = chip->eg_dbg[1] >> 1; + + if ((chip->reg_test0 & 32) != 0 && !chip->eg_dbg_load_l[1]) + { + chip->eg_dbg[0] |= chip->eg_out[1]; + } + chip->eg_dbg_load_l[0] = (chip->reg_test0 & 32) != 0; + + if (chip->fsm_out[4] || chip->fsm_out[6]) + chip->eg_index[0] = 0; + else + chip->eg_index[0] = chip->eg_index[1] + 1; + + if (chip->eg_index[1] < 18) + { + int index1 = chip->eg_index[1]; + int index2 = (index1 + 17) % 18; + chip->eg_cells[index2] = (nextstate & 3) | ((levelnext & 511) << 2) | (chip->eg_timer_i << 11); + chip->eg_cells[index2 + 18] = chip->eg_cells[index1]; + chip->eg_state_o[0] = chip->eg_cells[18 + index1] & 3; + chip->eg_level_o[0] = (chip->eg_cells[18 + index1] >> 2) & 511; + chip->eg_timer_o[0] = (chip->eg_cells[18 + index1] >> 11) & 1; + } + + chip->eg_state_o[2] = chip->eg_state_o[1]; + chip->eg_level_o[2] = chip->eg_level_o[1]; + chip->eg_timer_o[2] = chip->eg_timer_o[1]; + } + + if (chip->clk2) + { + chip->eg_out[1] = chip->eg_out[0]; + chip->eg_dbg[1] = chip->eg_dbg[0]; + chip->eg_dbg_load_l[1] = chip->eg_dbg_load_l[0]; + + chip->eg_index[1] = chip->eg_index[0]; + chip->eg_state_o[1] = chip->eg_state_o[0]; + chip->eg_state_o[3] = chip->eg_state_o[2]; + chip->eg_level_o[1] = chip->eg_level_o[0]; + chip->eg_level_o[3] = chip->eg_level_o[2]; + chip->eg_timer_o[1] = chip->eg_timer_o[0]; + chip->eg_timer_o[3] = chip->eg_timer_o[2]; + } + + if (chip->clk1) + { + static const int pg_multi[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 + }; + int fnum = chip->fnum[1]; + int freq; + int pg_add; + int vib_sel1 = (chip->vib_cnt[1] & 3) == 2; + int vib_sel2 = (chip->vib_cnt[1] & 1) == 1; + int vib_sh0 = chip->reg_dv && chip->vib[1] && vib_sel1; + int vib_sh1 = (chip->reg_dv && chip->vib[1] && vib_sel2) + || (!chip->reg_dv && chip->vib[1] && vib_sel1); + int vib_sh2 = !chip->reg_dv && chip->vib[1] && vib_sel2; + int vib_sign = (chip->vib_cnt[1] & 4) != 0 && chip->vib[1]; + int vib_add = 0; + int phase, state, dokon; + + if (vib_sh0) + vib_add |= (chip->fnum[1] >> 7) & 7; + if (vib_sh1) + vib_add |= (chip->fnum[1] >> 8) & 3; + if (vib_sh2) + vib_add |= (chip->fnum[1] >> 9) & 1; + if (vib_sign) + { + vib_add ^= 1023; + } + fnum += vib_add; + fnum += vib_sign; + if (vib_sign) + fnum &= 1023; + + freq = (fnum << chip->block[1]) >> 1; + + pg_add = (freq * pg_multi[chip->multi[1]]) >> 1; + + state = chip->eg_state_o[3]; + dokon = state == eg_state_release && chip->keyon_comb; + + phase = ((dokon || (chip->reg_test0 & 4) != 0 || chip->reset1) ? 0 : chip->pg_phase_o[3]) + pg_add; + + if (chip->fsm_out[4] || chip->fsm_out[6]) + chip->pg_index[0] = 0; + else + chip->pg_index[0] = chip->pg_index[1] + 1; + + if (chip->pg_index[1] < 18) + { + int index1 = chip->pg_index[1]; + int index2 = (index1 + 17) % 18; + chip->pg_cells[index2] = phase; + chip->pg_cells[index2 + 18] = chip->pg_cells[index1]; + chip->pg_phase_o[0] = chip->pg_cells[18 + index1]; + } + chip->pg_phase_o[2] = chip->pg_phase_o[1]; + } + + if (chip->clk2) + { + chip->pg_index[1] = chip->pg_index[0]; + chip->pg_phase_o[1] = chip->pg_phase_o[0]; + chip->pg_phase_o[3] = chip->pg_phase_o[2]; + } + + if (chip->rclk1) + { + int noise_bit; + + noise_bit = ((chip->noise_lfsr[1] >> 22) ^ (chip->noise_lfsr[1] >> 8)) & 1; + + if ((chip->noise_lfsr[1] & 0x7fffff) == 0) + noise_bit |= 1; + + noise_bit |= (chip->reg_test0 & 2) != 0; + + if (chip->reset1) + noise_bit = 0; + + chip->noise_lfsr[0] = (chip->noise_lfsr[1] << 1) | noise_bit; + } + if (chip->rclk2) + { + chip->noise_lfsr[1] = chip->noise_lfsr[0]; + } + + { + int pg_out = chip->pg_phase_o[3] >> 9; + int hh = chip->fsm_out[2] && chip->rhythm; + int sd = chip->fsm_out[3] && chip->rhythm; + int tc = chip->fsm_out[4] && chip->rhythm; + int rhy = (chip->fsm_out[2] || chip->fsm_out[3] || chip->fsm_out[4]) && chip->rhythm; + if (chip->clk1) + chip->hh_load = chip->fsm_out[2]; + if (!chip->hh_load && chip->fsm_out[2]) + { + chip->hh_bit2 = (pg_out >> 2) & 1; + chip->hh_bit3 = (pg_out >> 3) & 1; + chip->hh_bit7 = (pg_out >> 7) & 1; + chip->hh_bit8 = (pg_out >> 8) & 1; + } + if (chip->clk1) + chip->tc_load = tc; + if (!chip->tc_load && tc) + { + chip->tc_bit3 = (pg_out >> 3) & 1; + chip->tc_bit5 = (pg_out >> 5) & 1; + } + + if (chip->clk1) /* opt */ + { + int rm_bit; + int noise = (chip->noise_lfsr[1] >> 22) & 1; + + rm_bit = (chip->hh_bit2 ^ chip->hh_bit7) + | (chip->tc_bit5 ^ chip->hh_bit3) + | (chip->tc_bit5 ^ chip->tc_bit3); + + chip->pg_out_rhy = 0; + if (!rhy) + chip->pg_out_rhy |= pg_out; + if (hh) + { + chip->pg_out_rhy |= rm_bit << 9; + if (noise ^ rm_bit) + chip->pg_out_rhy |= 0xd0; + else + chip->pg_out_rhy |= 0x34; + } + if (sd) + chip->pg_out_rhy |= (chip->hh_bit8 << 9) | ((noise ^ chip->hh_bit8) << 8); + if (tc) + chip->pg_out_rhy |= (rm_bit << 9) | 0x80; + } + + if (chip->clk1) + { + chip->pg_dbg[0] = chip->pg_dbg[1] >> 1; + + chip->pg_dbg_load_l[0] = (chip->reg_test0 & 8) != 0; + + if ((chip->reg_test0 & 8) != 0 && !chip->pg_dbg_load_l[1]) + { + chip->pg_dbg[0] |= chip->pg_phase_o[3] & 0x1ff; + chip->pg_dbg[0] |= (chip->pg_out_rhy & 0x3ff) << 9; + } + } + if (chip->clk2) + { + chip->pg_dbg_load_l[1] = chip->pg_dbg_load_l[0]; + + chip->pg_dbg[1] = chip->pg_dbg[0]; + } + } + + { + + if (chip->clk1) + { + static const int logsin[128] = { + 0x6c3, 0x58b, 0x4e4, 0x471, 0x41a, 0x3d3, 0x398, 0x365, 0x339, 0x311, 0x2ed, 0x2cd, 0x2af, 0x293, 0x279, 0x261, + 0x24b, 0x236, 0x222, 0x20f, 0x1fd, 0x1ec, 0x1dc, 0x1cd, 0x1be, 0x1b0, 0x1a2, 0x195, 0x188, 0x17c, 0x171, 0x166, + 0x15b, 0x150, 0x146, 0x13c, 0x133, 0x129, 0x121, 0x118, 0x10f, 0x107, 0x0ff, 0x0f8, 0x0f0, 0x0e9, 0x0e2, 0x0db, + 0x0d4, 0x0cd, 0x0c7, 0x0c1, 0x0bb, 0x0b5, 0x0af, 0x0a9, 0x0a4, 0x09f, 0x099, 0x094, 0x08f, 0x08a, 0x086, 0x081, + 0x07d, 0x078, 0x074, 0x070, 0x06c, 0x068, 0x064, 0x060, 0x05c, 0x059, 0x055, 0x052, 0x04e, 0x04b, 0x048, 0x045, + 0x042, 0x03f, 0x03c, 0x039, 0x037, 0x034, 0x031, 0x02f, 0x02d, 0x02a, 0x028, 0x026, 0x024, 0x022, 0x020, 0x01e, + 0x01c, 0x01a, 0x018, 0x017, 0x015, 0x014, 0x012, 0x011, 0x00f, 0x00e, 0x00d, 0x00c, 0x00a, 0x009, 0x008, 0x007, + 0x007, 0x006, 0x005, 0x004, 0x004, 0x003, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000 + }; + static const int logsin_d[128] = { + 0x196, 0x07c, 0x04a, 0x035, 0x029, 0x022, 0x01d, 0x019, 0x015, 0x013, 0x012, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x009, 0x008, 0x007, 0x007, 0x007, 0x007, 0x006, 0x007, 0x006, 0x006, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x004, 0x005, 0x004, 0x004, 0x005, 0x004, 0x004, 0x003, 0x004, 0x003, 0x003, 0x003, + 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, 0x003, 0x003, 0x003, 0x002, 0x002, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x002, 0x002, 0x002, 0x001, + 0x001, 0x001, 0x002, 0x002, 0x001, 0x001, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x000, 0x001, 0x000, 0x001, 0x000, 0x001, 0x001, 0x000, 0x000, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x001, 0x000, 0x000, 0x001, 0x000, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 + }; + static const int pow[128] = { + 0x3f5, 0x3ea, 0x3df, 0x3d4, 0x3c9, 0x3bf, 0x3b4, 0x3a9, 0x39f, 0x394, 0x38a, 0x37f, 0x375, 0x36a, 0x360, 0x356, + 0x34c, 0x342, 0x338, 0x32e, 0x324, 0x31a, 0x310, 0x306, 0x2fd, 0x2f3, 0x2e9, 0x2e0, 0x2d6, 0x2cd, 0x2c4, 0x2ba, + 0x2b1, 0x2a8, 0x29e, 0x295, 0x28c, 0x283, 0x27a, 0x271, 0x268, 0x25f, 0x257, 0x24e, 0x245, 0x23c, 0x234, 0x22b, + 0x223, 0x21a, 0x212, 0x209, 0x201, 0x1f9, 0x1f0, 0x1e8, 0x1e0, 0x1d8, 0x1d0, 0x1c8, 0x1c0, 0x1b8, 0x1b0, 0x1a8, + 0x1a0, 0x199, 0x191, 0x189, 0x181, 0x17a, 0x172, 0x16b, 0x163, 0x15c, 0x154, 0x14d, 0x146, 0x13e, 0x137, 0x130, + 0x129, 0x122, 0x11b, 0x114, 0x10c, 0x106, 0x0ff, 0x0f8, 0x0f1, 0x0ea, 0x0e3, 0x0dc, 0x0d6, 0x0cf, 0x0c8, 0x0c2, + 0x0bb, 0x0b5, 0x0ae, 0x0a8, 0x0a1, 0x09b, 0x094, 0x08e, 0x088, 0x082, 0x07b, 0x075, 0x06f, 0x069, 0x063, 0x05d, + 0x057, 0x051, 0x04b, 0x045, 0x03f, 0x039, 0x033, 0x02d, 0x028, 0x022, 0x01c, 0x016, 0x011, 0x00b, 0x006, 0x000, + }; + static const int pow_d[128] = { + 0x005, 0x005, 0x005, 0x006, 0x006, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x006, 0x005, 0x005, + 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x005, + 0x004, 0x004, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x005, 0x004, 0x005, + 0x004, 0x004, 0x004, 0x005, 0x004, 0x004, 0x005, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, + 0x004, 0x003, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x003, 0x004, 0x004, 0x004, + 0x003, 0x003, 0x003, 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x004, 0x003, 0x003, 0x004, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, + 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x003, + }; + int wf = chip->wf[1]; + int phase = chip->pg_out_rhy + chip->op_mod[1]; + int square = wf == 6; + int sawtooth = wf == 7; + + int phase2, mute, sign, index, ls, att, pw, value, fb1, fb2, fb_sum, mod; + + if (wf == 4 || wf == 5) + phase2 = phase << 1; + else + phase2 = phase; + phase2 &= 1023; + + if (wf == 7 ? (phase2 & 512) != 0 : (phase2 & 256) != 0) + phase2 ^= 511; + + mute = ((phase & 512) != 0 && (wf == 1 || wf == 4 || wf == 5)) || ((phase & 256) != 0 && wf == 3); + sign = (wf == 2 || wf == 3 || wf == 5) ? 0 : (phase2 & 512) != 0; + + index = square ? 255 : (phase2 & 255); + + ls = logsin[index >> 1]; + if ((index & 1) == 0) + ls += logsin_d[index >> 1]; + + chip->op_logsin[0] = ls; + chip->op_saw[0] = sawtooth; + chip->op_saw_phase[0] = phase2 & 511; + + att = (chip->op_saw[1] ? (chip->op_saw_phase[1] << 3) : chip->op_logsin[1]) + (chip->eg_out[1] << 3); + if (att & 4096) + att = 4095; + + pw = pow[(att >> 1) & 127]; + if ((att & 1) == 0) + pw += pow_d[(att >> 1) & 127]; + + chip->op_shift[0] = (att >> 8) & 15; + chip->op_pow[0] = pw; + + chip->op_mute[0] = (chip->op_mute[1] << 1) | mute; + chip->op_sign[0] = (chip->op_sign[1] << 1) | sign; + + value = 0; + + if ((chip->op_mute[1] & 2) == 0) + { + value = ((chip->op_pow[1] | 0x400) << 1) >> chip->op_shift[1]; + } + + if ((chip->op_mute[1] & 2) == 0 && (chip->op_sign[1] & 2) != 0) + value ^= 8191; + + for (i = 0; i < 13; i++) + { + int bit; + chip->op_fb[0][i][0] = chip->op_fb[0][i][1] << 1; + if (chip->fsm_out[15]) + bit = (value >> i) & 1; + else + bit = (chip->op_fb[0][i][1] >> 8) & 1; + chip->op_fb[0][i][0] |= bit; + chip->op_fb[1][i][0] = chip->op_fb[1][i][1] << 1; + if (chip->fsm_out[15]) + bit = (chip->op_fb[0][i][1] >> 8) & 1; + else + bit = (chip->op_fb[1][i][1] >> 8) & 1; + chip->op_fb[1][i][0] |= bit; + chip->op_fb[2][i][0] = chip->op_fb[2][i][1] << 1; + if (chip->fsm_out[15]) + bit = (chip->op_fb[1][i][1] >> 8) & 1; + else + bit = (chip->op_fb[2][i][1] >> 8) & 1; + chip->op_fb[2][i][0] |= bit; + chip->op_fb[3][i][0] = chip->op_fb[3][i][1] << 1; + if (chip->fsm_out[15]) + bit = (chip->op_fb[2][i][1] >> 8) & 1; + else + bit = (chip->op_fb[3][i][1] >> 8) & 1; + chip->op_fb[3][i][0] |= bit; + } + + fb1 = 0; + fb2 = 0; + + for (i = 0; i < 14; i++) + { + int j = i; + if (i == 13) + j = 12; + fb1 |= ((chip->op_fb[1][j][1] >> 5) & 1) << i; + fb2 |= ((chip->op_fb[3][j][1] >> 5) & 1) << i; + } + + fb_sum = fb1 + fb2; + fb_sum &= 16383; + if (fb_sum & 8192) + fb_sum |= ~8191; + + mod = 0; + + if (chip->fsm_out[16] && !chip->fsm_out[14]) + mod |= value & 1023; + + if (chip->fsm_out[12]) + { + if (chip->fb_l[1][1]) + { + mod |= (fb_sum >> (9 - chip->fb_l[1][1])) & 1023; + } + } + + chip->op_mod[0] = mod & 1023; + chip->op_value = value; + } + + if (chip->clk2) + { + chip->op_logsin[1] = chip->op_logsin[0]; + chip->op_saw[1] = chip->op_saw[0]; + chip->op_saw_phase[1] = chip->op_saw_phase[0]; + chip->op_shift[1] = chip->op_shift[0]; + chip->op_pow[1] = chip->op_pow[0]; + chip->op_mute[1] = chip->op_mute[0]; + chip->op_sign[1] = chip->op_sign[0]; + + for (i = 0; i < 13; i++) + { + chip->op_fb[0][i][1] = chip->op_fb[0][i][0]; + chip->op_fb[1][i][1] = chip->op_fb[1][i][0]; + chip->op_fb[2][i][1] = chip->op_fb[2][i][0]; + chip->op_fb[3][i][1] = chip->op_fb[3][i][0]; + } + chip->op_mod[1] = chip->op_mod[0]; + } + } + + { + if (chip->clk1) + { + chip->accm_load_ac_l = chip->fsm_out[6]; + chip->accm_load_bd_l = chip->fsm_out[4]; + } + + chip->accm_load_ac = !chip->accm_load_ac_l && chip->fsm_out[6]; + chip->accm_load_bd = !chip->accm_load_bd_l && chip->fsm_out[4]; + + if (chip->accm_load_ac) + { + chip->accm_a_sign = (chip->accm_a[1] & 0x40000) == 0; + chip->accm_a_of = !((chip->accm_a[1] & 0x78000) == 0 || (chip->accm_a[1] & 0x78000) == 0x78000); + chip->accm_c_sign = (chip->accm_c[1] & 0x40000) == 0; + chip->accm_c_of = !((chip->accm_c[1] & 0x78000) == 0 || (chip->accm_c[1] & 0x78000) == 0x78000); + } + + if (chip->accm_load_bd) + { + chip->accm_b_sign = (chip->accm_b[1] & 0x40000) == 0; + chip->accm_b_of = !((chip->accm_b[1] & 0x78000) == 0 || (chip->accm_b[1] & 0x78000) == 0x78000); + chip->accm_d_sign = (chip->accm_d[1] & 0x40000) == 0; + chip->accm_d_of = !((chip->accm_d[1] & 0x78000) == 0 || (chip->accm_d[1] & 0x78000) == 0x78000); + } + + if (chip->clk1) + { + int value = 0, sign, accm_a, accm_b, accm_c, accm_d; + + if (chip->fsm_out[13]) + { + if (chip->fsm_out[11]) + value = chip->op_value << 1; + else + { + value = chip->op_value; + if (chip->op_value & 0x1000) + value |= 0x2000; + } + } + if (value & 0x2000) + { + value |= 0x7c000; + } + + accm_a = chip->fsm_out[6] ? 0 : chip->accm_a[1]; + accm_a += (chip->pan_l[1][1] & 1) != 0 ? value : 0; + chip->accm_a[0] = accm_a; + sign = (chip->accm_a[1] & 0x40000) == 0; + chip->accm_shift_a[0] = (chip->accm_shift_a[1] >> 1); + if (chip->fsm_out[6]) + { + chip->accm_shift_a[0] |= chip->accm_a[1] & 0x7fff; + if (sign) + chip->accm_shift_a[0] |= 0x8000; + } + + accm_b = chip->fsm_out[4] ? 0 : chip->accm_b[1]; + accm_b += (chip->pan_l[1][1] & 2) != 0 ? value : 0; + chip->accm_b[0] = accm_b; + sign = (chip->accm_b[1] & 0x40000) == 0; + chip->accm_shift_b[0] = (chip->accm_shift_b[1] >> 1); + if (chip->fsm_out[4]) + { + chip->accm_shift_b[0] |= chip->accm_b[1] & 0x7fff; + if (sign) + chip->accm_shift_b[0] |= 0x8000; + } + + accm_c = chip->fsm_out[6] ? 0 : chip->accm_c[1]; + accm_c += (chip->pan_l[1][1] & 4) != 0 ? value : 0; + chip->accm_c[0] = accm_c; + sign = (chip->accm_c[1] & 0x40000) == 0; + chip->accm_shift_c[0] = (chip->accm_shift_c[1] >> 1); + if (chip->fsm_out[6]) + { + chip->accm_shift_c[0] |= chip->accm_c[1] & 0x7fff; + if (sign) + chip->accm_shift_c[0] |= 0x8000; + } + + accm_d = chip->fsm_out[4] ? 0 : chip->accm_d[1]; + accm_d += (chip->pan_l[1][1] & 8) != 0 ? value : 0; + chip->accm_d[0] = accm_d; + sign = (chip->accm_d[1] & 0x40000) == 0; + chip->accm_shift_d[0] = (chip->accm_shift_d[1] >> 1); + if (chip->fsm_out[4]) + { + chip->accm_shift_d[0] |= chip->accm_d[1] & 0x7fff; + if (sign) + chip->accm_shift_d[0] |= 0x8000; + } + } + + if (chip->clk2) + { + chip->accm_a[1] = chip->accm_a[0]; + chip->accm_b[1] = chip->accm_b[0]; + chip->accm_c[1] = chip->accm_c[0]; + chip->accm_d[1] = chip->accm_d[0]; + + chip->accm_shift_a[1] = chip->accm_shift_a[0]; + chip->accm_shift_b[1] = chip->accm_shift_b[0]; + chip->accm_shift_c[1] = chip->accm_shift_c[0]; + chip->accm_shift_d[1] = chip->accm_shift_d[0]; + } + + if (chip->fsm_out[8]) + { + chip->o_doab = chip->accm_a_of ? chip->accm_a_sign : chip->accm_shift_a[1] & 1; + chip->o_docd = chip->accm_c_of ? chip->accm_c_sign : chip->accm_shift_c[1] & 1; + } + else + { + chip->o_doab = chip->accm_b_of ? chip->accm_b_sign : chip->accm_shift_b[1] & 1; + chip->o_docd = chip->accm_d_of ? chip->accm_d_sign : chip->accm_shift_d[1] & 1; + } + } + + chip->o_sy = chip->clk2; + chip->o_smpac = chip->fsm_out[10]; + chip->o_smpbd = chip->fsm_out[9]; + chip->o_irq_pull = chip->t1_status || chip->t2_status; + + if (chip->io_read) + { + chip->data_o = 0; + if (chip->t1_status || chip->t2_status) + chip->data_o |= 128; + if (chip->t1_status) + chip->data_o |= 64; + if (chip->t2_status) + chip->data_o |= 32; + chip->data_z = 0; + } + else + chip->data_z = 1; + + { + if (chip->clk1) + { + chip->ra_dbg1[0] = chip->ra_dbg1[1] >> 1; + chip->ra_dbg2[0] = chip->ra_dbg2[1] >> 1; + if ((chip->reg_test0 & 128) != 0 && !chip->ra_dbg_load[1]) + { + chip->ra_dbg1[0] |= (int64_t)chip->multi[1] << 0; + chip->ra_dbg1[0] |= (int64_t)chip->ksr[1] << 4; + chip->ra_dbg1[0] |= (int64_t)chip->egt[1] << 5; + chip->ra_dbg1[0] |= (int64_t)chip->vib[1] << 6; + chip->ra_dbg1[0] |= (int64_t)chip->am[1] << 7; + chip->ra_dbg1[0] |= (int64_t)chip->tl[1] << 8; + chip->ra_dbg1[0] |= (int64_t)chip->ksl[1] << 14; + chip->ra_dbg1[0] |= (int64_t)chip->dr[1] << 16; + chip->ra_dbg1[0] |= (int64_t)chip->ar[1] << 20; + chip->ra_dbg1[0] |= (int64_t)chip->rr[1] << 24; + chip->ra_dbg1[0] |= (int64_t)chip->sl[1] << 28; + chip->ra_dbg1[0] |= (int64_t)chip->wf[1] << 32; + chip->ra_dbg2[0] |= (int64_t)chip->fnum[1] << 0; + chip->ra_dbg2[0] |= (int64_t)chip->block[1] << 10; + chip->ra_dbg2[0] |= (int64_t)chip->keyon[1] << 13; + chip->ra_dbg2[0] |= (int64_t)chip->connect[1] << 14; + chip->ra_dbg2[0] |= (int64_t)chip->pan[1] << 15; + chip->ra_dbg2[0] |= (int64_t)chip->connect_pair[1] << 19; + chip->ra_dbg2[0] |= (int64_t)chip->fb[1] << 20; + } + chip->ra_dbg_load[0] = (chip->reg_test0 & 128) != 0; + } + if (chip->clk2) + { + chip->ra_dbg1[1] = chip->ra_dbg1[0]; + chip->ra_dbg2[1] = chip->ra_dbg2[0]; + chip->ra_dbg_load[1] = chip->ra_dbg_load[0]; + } + } + + switch (chip->reg_test1 & 7) + { + case 0: + chip->o_test = 0; + break; + case 1: + chip->o_test = (chip->ra_dbg1[1] & 1) != 0; + break; + case 2: + chip->o_test = (chip->ra_dbg2[1] & 1) != 0; + break; + case 3: + chip->o_test = (chip->pg_dbg[1] & 1) != 0; + break; + case 4: + chip->o_test = (chip->eg_dbg[1] & 1) != 0; + break; + } + +/* end: */ + + if (chip->io_write0) + chip->write0_sr = 1; + else if (chip->reset0 || chip->write0_l[1]) + chip->write0_sr = 0; + + if (chip->io_write1) + chip->write1_sr = 1; + else if (chip->reset0 || chip->write1_l[1]) + chip->write1_sr = 0; + + if (chip->clk1) + { + chip->write0_l[0] = chip->write0_sr; + chip->write0_l[2] = chip->write0_l[1]; + + chip->write1_l[0] = chip->write1_sr; + chip->write1_l[2] = chip->write1_l[1]; + } + + if (chip->mclk1) + { + chip->prescaler1_reset[0] = (chip->prescaler1_reset[1] << 1) | chip->reset1; + + if (!(chip->prescaler1_reset[1] & 2) && chip->reset1) + chip->prescaler1_cnt[0] = 0; + else + chip->prescaler1_cnt[0] = (chip->prescaler1_cnt[1] + 1) & 3; + } + + if (chip->aclk1) + { + int prescaler2_reset = !(chip->prescaler2_reset_l[1] & 2) && chip->reset1; + chip->prescaler2_reset_l[0] = (chip->prescaler2_reset_l[1] << 1) | chip->reset1; + + if (prescaler2_reset) + chip->prescaler2_cnt[0] = 0; + else + chip->prescaler2_cnt[0] = (chip->prescaler2_cnt[1] + 1) & 3; + + chip->prescaler2_l1[0] = !prescaler2_reset && (chip->prescaler2_cnt[1] & 1) == 0; + chip->prescaler2_l2 = chip->prescaler2_l1[1]; + + chip->prescaler2_l3[0] = !prescaler2_reset && (chip->prescaler2_cnt[1] & 1) != 0; + chip->prescaler2_l4 = chip->prescaler2_l3[1]; + + chip->prescaler2_l5[0] = !prescaler2_reset && chip->prescaler2_cnt[1] == 3; + + chip->prescaler2_l6[0] = !prescaler2_reset && chip->prescaler2_cnt[1] == 1; + + chip->prescaler2_l7 = (chip->prescaler2_cnt[1] & 1) == 0; + } +} diff --git a/thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.h b/thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.h new file mode 100644 index 000000000..dec55c84b --- /dev/null +++ b/thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.h @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YMF262-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * YMF262 emulator + * Thanks: + * John McMaster (siliconpr0n.org): + * YMF262 decap and die shot + * + */ + +#pragma once + +#ifndef NUKED_FMOPL3_H +#define NUKED_FMOPL3_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct +{ + int mclk; + int address; + int data_i; + int ic; + int cs; + int rd; + int wr; +} fmopl3_input_t; + +typedef struct +{ + fmopl3_input_t input; + + int mclk1; + int mclk2; + int aclk1; + int aclk2; + int clk1; + int clk2; + int rclk1; + int rclk2; + + int o_clk1; + int o_clk2; + int o_rclk1; + int o_rclk2; + int o_wrcheck; + int o_data_latch; + int o_bank_latch; + int o_reset0; + int o_ra_w1_l1; + + int prescaler1_reset[2]; + int prescaler1_cnt[2]; + + int prescaler2_reset_l[2]; + int prescaler2_cnt[2]; + int prescaler2_reset; + int prescaler2_l1[2]; + int prescaler2_l2; + int prescaler2_l3[2]; + int prescaler2_l4; + int prescaler2_l5[2]; + int prescaler2_l6[2]; + int prescaler2_l7; + + int fsm_cnt1[2]; + int fsm_cnt2[2]; + int fsm_cnt3[2]; + int fsm_cnt; + + int fsm_reset_l[2]; + int fsm_out[17]; + int fsm_l1[2]; + int fsm_l2[2]; + int fsm_l3[2]; + int fsm_l4[2]; + int fsm_l5[2]; + int fsm_l6[2]; + int fsm_l7[2]; + int fsm_l8[2]; + int fsm_l9[2]; + int fsm_l10[2]; + + int ic_latch[2]; + + int io_rd; + int io_wr; + int io_cs; + int io_a0; + int io_a1; + + int io_read; + int io_write; + int io_write0; + int io_write1; + int io_bank; + + int data_latch; + int bank_latch; + int bank_masked; + + int reg_sel1; + int reg_sel2; + int reg_sel3; + int reg_sel4; + int reg_sel5; + int reg_sel8; + int reg_selbd; + + int reg_test0; + int reg_timer1; + int reg_timer2; + int reg_notesel; + int rhythm; + int reg_rh_kon; + int reg_da; + int reg_dv; + + int reg_test1; + int reg_new; + int reg_4op; + + int reg_t1_mask; + int reg_t2_mask; + int reg_t1_start; + int reg_t2_start; + + int lfo_cnt[2]; + int vib_cnt[2]; + int t1_step; + int t2_step; + int am_step; + int vib_step; + + int rh_sel0; + int rh_sel[2]; + + int keyon_comb; + + int ra_address_latch; + int ra_address_good; + int ra_data_latch; + int ra_cnt1[2]; + int ra_cnt2[2]; + int ra_cnt3[2]; + int ra_cnt4[2]; + int ra_cnt; + int ra_rst_l[2]; + int ra_w1_l1; + int ra_w1_l2; + int ra_write; + int ra_write_a; + + int ra_multi[36]; + int ra_ksr[36]; + int ra_egt[36]; + int ra_am[36]; + int ra_vib[36]; + int ra_tl[36]; + int ra_ksl[36]; + int ra_ar[36]; + int ra_dr[36]; + int ra_sl[36]; + int ra_rr[36]; + int ra_wf[36]; + int ra_fnum[18]; + int ra_block[18]; + int ra_keyon[18]; + int ra_connect[18]; + int ra_fb[18]; + int ra_pan[18]; + int ra_connect_pair[18]; + int multi[2]; + int ksr[2]; + int egt[2]; + int am[2]; + int vib[2]; + int tl[2]; + int ksl[2]; + int ar[2]; + int dr[2]; + int sl[2]; + int rr[2]; + int wf[2]; + int fnum[2]; + int block[2]; + int keyon[2]; + int connect[2]; + int fb[2]; + int pan[2]; + int connect_pair[2]; + + int64_t ra_dbg1[2]; + int ra_dbg2[2]; + int ra_dbg_load[2]; + + int fb_l[2][2]; + int pan_l[2][2]; + + int write0_sr; + int write0_l[4]; + int write0; + + int write1_sr; + int write1_l[4]; + int write1; + + int connect_l[2]; + int connect_pair_l[2]; + + int t1_cnt[2]; + int t2_cnt[2]; + int t1_of[2]; + int t2_of[2]; + int t1_status; + int t2_status; + int timer_st_load_l; + int timer_st_load; + int t1_start; + int t2_start; + int t1_start_l[2]; + int t2_start_l[2]; + + int reset0; + int reset1; + + int pg_phase_o[4]; + int pg_dbg[2]; + int pg_dbg_load_l[2]; + int noise_lfsr[2]; + int pg_index[2]; + int pg_cells[36]; + int pg_out_rhy; + + int trem_load_l; + int trem_load; + int trem_st_load_l; + int trem_st_load; + int trem_carry[2]; + int trem_value[2]; + int trem_dir[2]; + int trem_step; + int trem_out; + int trem_of[2]; + + int eg_load_l1[2]; + int eg_load_l; + int eg_load; + + int64_t eg_timer_masked[2]; + int eg_carry[2]; + int eg_mask[2]; + int eg_subcnt[2]; + int eg_subcnt_l[2]; + int eg_sync_l[2]; + int eg_timer_low; + int eg_shift; + int eg_timer_dbg[2]; + + int eg_timer_i; + int eg_timer_o[4]; + int eg_state_o[4]; + int eg_level_o[4]; + int eg_index[2]; + int eg_cells[36]; + + int eg_out[2]; + int eg_dbg[2]; + int eg_dbg_load_l[2]; + + int hh_load; + int tc_load; + int hh_bit2; + int hh_bit3; + int hh_bit7; + int hh_bit8; + int tc_bit3; + int tc_bit5; + + int op_logsin[2]; + int op_saw[2]; + int op_saw_phase[2]; + int op_shift[2]; + int op_pow[2]; + int op_mute[2]; + int op_sign[2]; + int op_fb[4][13][2]; + int op_mod[2]; + + int op_value; + + int accm_a[2]; + int accm_b[2]; + int accm_c[2]; + int accm_d[2]; + int accm_shift_a[2]; + int accm_shift_b[2]; + int accm_shift_c[2]; + int accm_shift_d[2]; + int accm_load_ac_l; + int accm_load_ac; + int accm_load_bd_l; + int accm_load_bd; + int accm_a_of; + int accm_a_sign; + int accm_b_of; + int accm_b_sign; + int accm_c_of; + int accm_c_sign; + int accm_d_of; + int accm_d_sign; + + int o_doab; + int o_docd; + int o_sy; + int o_smpac; + int o_smpbd; + int o_irq_pull; + int o_test; + + int data_o; + int data_z; +} fmopl3_t; + +extern void FMOPL3_Clock(fmopl3_t *chip); + +#ifdef __cplusplus +} +#endif + +#endif /* NUKED_FMOPL3_H */ diff --git a/thirdparty/adlmidi/chips/ymfm/README.md b/thirdparty/adlmidi/chips/ymfm/README.md new file mode 100644 index 000000000..29f0d87dc --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/README.md @@ -0,0 +1,125 @@ +# ymfm + +
+ +
+ +[ymfm](https://github.com/aaronsgiles/ymfm) is a collection of BSD-licensed Yamaha FM sound cores (OPM, OPN, OPL, and others), written by [Aaron Giles](https://aarongiles.com) + +## Supported environments + +This code should compile cleanly in any environment that has C++14 support. +It has been tested on gcc, clang, and Microsoft Visual C++ 2019. + +## Supported chip families + +Currently, support is present for the following chips (organized by header file): + +* ymfm_misc.h: + * YM2149 (SSG) [1983: MSX; Atari ST] +* ymfm_opm.h: + * YM2151 (OPM) [1983: Sharp X1, X68000; MSX; synths: DX21, DX27, DX100] + * YM2164 (OPP) [1985: FB-01 MIDI Expander; IBM Music Feature Card; MSX; synths: Korg DS-8, 707] +* ymfm_opn.h: + * YM2203 (OPN) [1984: NEC PC-88, PC-98, NEC PC-6001mkII SR, PC-6601 SR] + * YM2608 (OPNA) [1985: NEC PC-88, PC-98] + * YM2610 (OPNB) [1987: Neo Geo] + * YM2610B (OPNB2) + * YM2612 (OPN2) [1988: Sega Mega Drive/Genesis; FM Towns] + * YM3438 (OPN2C) + * YMF276 (OPN2L) + * YMF288 (OPN3L) [1995: NEC PC-98] +* ymfm_opl.h: + * YM3526 (OPL) [1984: C64 SFX Sound Expander] + * Y8950 (MSX-Audio) [1984: MSX] + * YM3812 (OPL2) [1985: AdLib, Sound Blaster; synths: some Portasound keyboards] + * YMF262 (OPL3) [1988: Sound Blaster Pro 2.0, SB16] + * YMF289B (OPL3L) + * YMF278B (OPL4) [1993: MSX Moonsound cartridge] + * YM2413 (OPLL) [1986: Sega Master System, Mark III; MSX; synths: Portasound PSS-140, PSS-170, PSS-270] + * YM2423 (OPLL-X) + * YMF281 (OPLLP) + * DS1001 (Konami 053982/VRC7) [1991: Famicom cartridge Lagrange Point] +* ymfm_opq.h: + * YM3806 (OPQ) [synths: PSR-60/70] +* ymfm_opz.h: + * YM2414 (OPZ) [1987: synths: TX81Z, DX11, YS200; Korg Z3 guitar synth] + +There are some obviously-related chips that also are on my horizon but have no implementation as yet: + +* YMW-258-F 'GEW8' (aka Sega 315-5560 aka Sega Multi-PCM) +* YMF271 (OPX) +* YM21280 (OPS) / YM21290 (EGS) [synths: DX7, DX1, DX5, DX9, TX7, TX216, TX416, TX816] +* OPK? + +## History + +These cores were originally written during the summer and fall of 2020 as part of the [MAME](https://mamedev.org/) project. +As such, their design started off heavily based on how MAME works. + +The OPM/OPN cores first appeared in MAME 0.230. +The OPL cores were added in MAME 0.231. +A further rewrite to abstract MAME dependencies is planned for MAME 0.232. + +The goal was threefold: +1. provide BSD-licensed emulation cores that are more compatible with MAME's core licensing +1. modernize and unify the code around a common implementation of shared features +1. improve accuracy where possible based on discoveries made by others + +## Accuracy + +The goal of these cores is not 100% digital accuracy. +To achieve that would require full emulation of the pipelines, which would make the code extremely difficult to comprehend. +It would also make it much harder to share common implementations of features, or to add support for less well-known chip types. +If you want that level of accuracy, there are [several](https://github.com/nukeykt/Nuked-OPN2) [decap-based](https://github.com/nukeykt/Nuked-OPM) [emulation cores](https://github.com/nukeykt/Nuked-OPLL) out there. + +Instead, the main goals are: +1. Extremely high (audibly indistinguishable) accuracy +1. Reasonable performance +1. Clean design with readable code +1. Clear documentation of the various chips + +## General approach + +Check out the [examples directory](https://github.com/aaronsgiles/ymfm/tree/main/examples) for some example usage patterns. +I'm not a big fan of makefiles for simple things, so instructions on how to compile each example are provided at the top. + +# IMPORTANT + +As of May 2021, the interface to these is still a bit in flux. +Be prepared when syncing with upstream to make some adjustments. + +### Clocking + +The general philosophy of the emulators provided here is that they are clock-independent. +Much like the actual chips, you (the consumer) control the clock; the chips themselves have no idea what time it is. +They just tick forward each time you ask them to. + +The way you move things along is via the `generate()` function, which ticks the internal system forward one or more samples, and writes out an array out chip-specific `output_data`. +But what, exactly, is a "sample", and how long is it? + +This is where the external clock comes in. +Most of the Yamaha chips are externally clocked in the MHz range. +They then divide that clock by a factor (sometimes dynamically controllable), and then the internal operators are pipelined to further divide the clock. + +For example, the YM2151 internally divides the clock by 2, and has 32 operators to iterate through. +Thus, for a nominal input lock of 3.58MHz, you end up at around a 55.9kHz sample rate. +Fortunately, all the chip implementations can compute this for you; just pass the raw external clock value to the `sample_rate()` method and it will hand you back the output sample rate you want. + +Then call `generate()` that many times per second to output the results. + +But what if I want to output at a "normal" rate, like 44.1kHz? +Sorry, you'll have to rate convert as needed. + +### Reading and Writing + +To read or write to the chips, you can call the `read()` and `write()` methods. +The offset provided corresponds to the addressing input lines in a (hopefully) logical way. + +For reads, almost all chips have a status register, which you can read via `read_status()`. +Some chips have a data port that can be read via `read_data()`. +And chips with extended addressing may also have `read_status_hi()` and `read_data_hi()`. + +For writes, almost all chips have an address register and a data register, and so you can reliably count on there being a `write_address()` and `write_data()` method as well. +If the chip supports extended addressing, it may also have `write_address_hi()` and `write_data_hi()`. + diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm.h b/thirdparty/adlmidi/chips/ymfm/ymfm.h new file mode 100644 index 000000000..2cc2ac1f2 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm.h @@ -0,0 +1,572 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_H +#define YMFM_H + +#pragma once + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) + #define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ymfm +{ + +//********************************************************* +// DEBUGGING +//********************************************************* + +class debug +{ +public: + // masks to help isolate specific channels + static constexpr uint32_t GLOBAL_FM_CHANNEL_MASK = 0xffffffff; + static constexpr uint32_t GLOBAL_ADPCM_A_CHANNEL_MASK = 0xffffffff; + static constexpr uint32_t GLOBAL_ADPCM_B_CHANNEL_MASK = 0xffffffff; + static constexpr uint32_t GLOBAL_PCM_CHANNEL_MASK = 0xffffffff; + + // types of logging + static constexpr bool LOG_FM_WRITES = false; + static constexpr bool LOG_KEYON_EVENTS = false; + static constexpr bool LOG_UNEXPECTED_READ_WRITES = false; + + // helpers to write based on the log type + template static void log_fm_write(Params &&... args) { if (LOG_FM_WRITES) log(args...); } + template static void log_keyon(Params &&... args) { if (LOG_KEYON_EVENTS) log(args...); } + template static void log_unexpected_read_write(Params &&... args) { if (LOG_UNEXPECTED_READ_WRITES) log(args...); } + + // downstream helper to output log data; defaults to printf + template static void log(Params &&... args) { printf(args...); } +}; + + + +//********************************************************* +// GLOBAL HELPERS +//********************************************************* + +//------------------------------------------------- +// bitfield - extract a bitfield from the given +// value, starting at bit 'start' for a length of +// 'length' bits +//------------------------------------------------- + +inline uint32_t bitfield(uint32_t value, int start, int length = 1) +{ + return (value >> start) & ((1 << length) - 1); +} + + +//------------------------------------------------- +// clamp - clamp between the minimum and maximum +// values provided +//------------------------------------------------- + +inline int32_t clamp(int32_t value, int32_t minval, int32_t maxval) +{ + if (value < minval) + return minval; + if (value > maxval) + return maxval; + return value; +} + + +//------------------------------------------------- +// array_size - return the size of an array +//------------------------------------------------- + +template +constexpr uint32_t array_size(ArrayType (&array)[ArraySize]) +{ + (void)array; + return ArraySize; +} + + +//------------------------------------------------- +// count_leading_zeros - return the number of +// leading zeros in a 32-bit value; CPU-optimized +// versions for various architectures are included +// below +//------------------------------------------------- + +#if defined(__GNUC__) + +inline uint8_t count_leading_zeros(uint32_t value) +{ + if (value == 0) + return 32; + return __builtin_clz(value); +} + +#elif defined(_MSC_VER) + +inline uint8_t count_leading_zeros(uint32_t value) +{ + unsigned long index; + return _BitScanReverse(&index, value) ? uint8_t(31U - index) : 32U; +} + +#else + +inline uint8_t count_leading_zeros(uint32_t value) +{ + if (value == 0) + return 32; + uint8_t count; + for (count = 0; int32_t(value) >= 0; count++) + value <<= 1; + return count; +} + +#endif + + +// Many of the Yamaha FM chips emit a floating-point value, which is sent to +// a DAC for processing. The exact format of this floating-point value is +// documented below. This description only makes sense if the "internal" +// format treats sign as 1=positive and 0=negative, so the helpers below +// presume that. +// +// Internal OPx data 16-bit signed data Exp Sign Mantissa +// ================= ================= === ==== ======== +// 1 1xxxxxxxx------ -> 0 1xxxxxxxx------ -> 111 1 1xxxxxxx +// 1 01xxxxxxxx----- -> 0 01xxxxxxxx----- -> 110 1 1xxxxxxx +// 1 001xxxxxxxx---- -> 0 001xxxxxxxx---- -> 101 1 1xxxxxxx +// 1 0001xxxxxxxx--- -> 0 0001xxxxxxxx--- -> 100 1 1xxxxxxx +// 1 00001xxxxxxxx-- -> 0 00001xxxxxxxx-- -> 011 1 1xxxxxxx +// 1 000001xxxxxxxx- -> 0 000001xxxxxxxx- -> 010 1 1xxxxxxx +// 1 000000xxxxxxxxx -> 0 000000xxxxxxxxx -> 001 1 xxxxxxxx +// 0 111111xxxxxxxxx -> 1 111111xxxxxxxxx -> 001 0 xxxxxxxx +// 0 111110xxxxxxxx- -> 1 111110xxxxxxxx- -> 010 0 0xxxxxxx +// 0 11110xxxxxxxx-- -> 1 11110xxxxxxxx-- -> 011 0 0xxxxxxx +// 0 1110xxxxxxxx--- -> 1 1110xxxxxxxx--- -> 100 0 0xxxxxxx +// 0 110xxxxxxxx---- -> 1 110xxxxxxxx---- -> 101 0 0xxxxxxx +// 0 10xxxxxxxx----- -> 1 10xxxxxxxx----- -> 110 0 0xxxxxxx +// 0 0xxxxxxxx------ -> 1 0xxxxxxxx------ -> 111 0 0xxxxxxx + +//------------------------------------------------- +// encode_fp - given a 32-bit signed input value +// convert it to a signed 3.10 floating-point +// value +//------------------------------------------------- + +inline int16_t encode_fp(int32_t value) +{ + // handle overflows first + if (value < -32768) + return (7 << 10) | 0x000; + if (value > 32767) + return (7 << 10) | 0x3ff; + + // we need to count the number of leading sign bits after the sign + // we can use count_leading_zeros if we invert negative values + int32_t scanvalue = value ^ (int32_t(value) >> 31); + + // exponent is related to the number of leading bits starting from bit 14 + int exponent = 7 - count_leading_zeros(scanvalue << 17); + + // smallest exponent value allowed is 1 + exponent = std::max(exponent, 1); + + // mantissa + int32_t mantissa = value >> (exponent - 1); + + // assemble into final form, inverting the sign + return ((exponent << 10) | (mantissa & 0x3ff)) ^ 0x200; +} + + +//------------------------------------------------- +// decode_fp - given a 3.10 floating-point value, +// convert it to a signed 16-bit value +//------------------------------------------------- + +inline int16_t decode_fp(int16_t value) +{ + // invert the sign and the exponent + value ^= 0x1e00; + + // shift mantissa up to 16 bits then apply inverted exponent + return int16_t(value << 6) >> bitfield(value, 10, 3); +} + + +//------------------------------------------------- +// roundtrip_fp - compute the result of a round +// trip through the encode/decode process above +//------------------------------------------------- + +inline int16_t roundtrip_fp(int32_t value) +{ + // handle overflows first + if (value < -32768) + return -32768; + if (value > 32767) + return 32767; + + // we need to count the number of leading sign bits after the sign + // we can use count_leading_zeros if we invert negative values + int32_t scanvalue = value ^ (int32_t(value) >> 31); + + // exponent is related to the number of leading bits starting from bit 14 + int exponent = 7 - count_leading_zeros(scanvalue << 17); + + // smallest exponent value allowed is 1 + exponent = std::max(exponent, 1); + + // apply the shift back and forth to zero out bits that are lost + exponent -= 1; + return (value >> exponent) << exponent; +} + + + +//********************************************************* +// HELPER CLASSES +//********************************************************* + +// various envelope states +enum envelope_state : uint32_t +{ + EG_DEPRESS = 0, // OPLL only; set EG_HAS_DEPRESS to enable + EG_ATTACK = 1, + EG_DECAY = 2, + EG_SUSTAIN = 3, + EG_RELEASE = 4, + EG_REVERB = 5, // OPQ/OPZ only; set EG_HAS_REVERB to enable + EG_STATES = 6 +}; + +// external I/O access classes +enum access_class : uint32_t +{ + ACCESS_IO = 0, + ACCESS_ADPCM_A, + ACCESS_ADPCM_B, + ACCESS_PCM, + ACCESS_CLASSES +}; + + + +//********************************************************* +// HELPER CLASSES +//********************************************************* + +// ======================> ymfm_output + +// struct containing an array of output values +template +struct ymfm_output +{ + // clear all outputs to 0 + ymfm_output &clear() + { + for (uint32_t index = 0; index < NumOutputs; index++) + data[index] = 0; + return *this; + } + + // clamp all outputs to a 16-bit signed value + ymfm_output &clamp16() + { + for (uint32_t index = 0; index < NumOutputs; index++) + data[index] = clamp(data[index], -32768, 32767); + return *this; + } + + // run each output value through the floating-point processor + ymfm_output &roundtrip_fp() + { + for (uint32_t index = 0; index < NumOutputs; index++) + data[index] = ymfm::roundtrip_fp(data[index]); + return *this; + } + + // internal state + int32_t data[NumOutputs]; +}; + + +// ======================> ymfm_wavfile + +// this class is a debugging helper that accumulates data and writes it to wav files +template +class ymfm_wavfile +{ +public: + // construction + ymfm_wavfile(uint32_t samplerate = 44100) : + m_samplerate(samplerate) + { + } + + // configuration + ymfm_wavfile &set_index(uint32_t index) { m_index = index; return *this; } + ymfm_wavfile &set_samplerate(uint32_t samplerate) { m_samplerate = samplerate; return *this; } + + // destruction + ~ymfm_wavfile() + { + if (!m_buffer.empty()) + { + // create file + char name[20]; + sprintf(name, "wavlog-%02d.wav", m_index); + FILE *out = fopen(name, "wb"); + + // make the wav file header + uint8_t header[44]; + memcpy(&header[0], "RIFF", 4); + *(uint32_t *)&header[4] = m_buffer.size() * 2 + 44 - 8; + memcpy(&header[8], "WAVE", 4); + memcpy(&header[12], "fmt ", 4); + *(uint32_t *)&header[16] = 16; + *(uint16_t *)&header[20] = 1; + *(uint16_t *)&header[22] = Channels; + *(uint32_t *)&header[24] = m_samplerate; + *(uint32_t *)&header[28] = m_samplerate * 2 * Channels; + *(uint16_t *)&header[32] = 2 * Channels; + *(uint16_t *)&header[34] = 16; + memcpy(&header[36], "data", 4); + *(uint32_t *)&header[40] = m_buffer.size() * 2 + 44 - 44; + + // write header then data + fwrite(&header[0], 1, sizeof(header), out); + fwrite(&m_buffer[0], 2, m_buffer.size(), out); + fclose(out); + } + } + + // add data to the file + template + void add(ymfm_output output) + { + int16_t sum[Channels] = { 0 }; + for (int index = 0; index < Outputs; index++) + sum[index % Channels] += output.data[index]; + for (int index = 0; index < Channels; index++) + m_buffer.push_back(sum[index]); + } + + // add data to the file, using a reference + template + void add(ymfm_output output, ymfm_output const &ref) + { + int16_t sum[Channels] = { 0 }; + for (int index = 0; index < Outputs; index++) + sum[index % Channels] += output.data[index] - ref.data[index]; + for (int index = 0; index < Channels; index++) + m_buffer.push_back(sum[index]); + } + +private: + // internal state + uint32_t m_index; + uint32_t m_samplerate; + std::vector m_buffer; +}; + + +// ======================> ymfm_saved_state + +// this class contains a managed vector of bytes that is used to save and +// restore state +class ymfm_saved_state +{ +public: + // construction + ymfm_saved_state(std::vector &buffer, bool saving) : + m_buffer(buffer), + m_offset(saving ? -1 : 0) + { + if (saving) + buffer.resize(0); + } + + // are we saving or restoring? + bool saving() const { return (m_offset < 0); } + + // generic save/restore + template + void save_restore(DataType &data) + { + if (saving()) + save(data); + else + restore(data); + } + +public: + // save data to the buffer + void save(bool &data) { write(data ? 1 : 0); } + void save(int8_t &data) { write(data); } + void save(uint8_t &data) { write(data); } + void save(int16_t &data) { write(uint8_t(data)).write(data >> 8); } + void save(uint16_t &data) { write(uint8_t(data)).write(data >> 8); } + void save(int32_t &data) { write(data).write(data >> 8).write(data >> 16).write(data >> 24); } + void save(uint32_t &data) { write(data).write(data >> 8).write(data >> 16).write(data >> 24); } + void save(envelope_state &data) { write(uint8_t(data)); } + template + void save(DataType (&data)[Count]) { for (uint32_t index = 0; index < Count; index++) save(data[index]); } + + // restore data from the buffer + void restore(bool &data) { data = read() ? true : false; } + void restore(int8_t &data) { data = read(); } + void restore(uint8_t &data) { data = read(); } + void restore(int16_t &data) { data = read(); data |= read() << 8; } + void restore(uint16_t &data) { data = read(); data |= read() << 8; } + void restore(int32_t &data) { data = read(); data |= read() << 8; data |= read() << 16; data |= read() << 24; } + void restore(uint32_t &data) { data = read(); data |= read() << 8; data |= read() << 16; data |= read() << 24; } + void restore(envelope_state &data) { data = envelope_state(read()); } + template + void restore(DataType (&data)[Count]) { for (uint32_t index = 0; index < Count; index++) restore(data[index]); } + + // internal helper + ymfm_saved_state &write(uint8_t data) { m_buffer.push_back(data); return *this; } + uint8_t read() { return (m_offset < int32_t(m_buffer.size())) ? m_buffer[m_offset++] : 0; } + + // internal state + std::vector &m_buffer; + int32_t m_offset; +}; + + + +//********************************************************* +// INTERFACE CLASSES +//********************************************************* + +// ======================> ymfm_engine_callbacks + +// this class represents functions in the engine that the ymfm_interface +// needs to be able to call; it is represented here as a separate interface +// that is independent of the actual engine implementation +class ymfm_engine_callbacks +{ +public: + // timer callback; called by the interface when a timer fires + virtual void engine_timer_expired(uint32_t tnum) = 0; + + // check interrupts; called by the interface after synchronization + virtual void engine_check_interrupts() = 0; + + // mode register write; called by the interface after synchronization + virtual void engine_mode_write(uint8_t data) = 0; +}; + + +// ======================> ymfm_interface + +// this class represents the interface between the fm_engine and the outside +// world; it provides hooks for timers, synchronization, and I/O +class ymfm_interface +{ + // the engine is our friend + template friend class fm_engine_base; + +public: + // the following functions must be implemented by any derived classes; the + // default implementations are sufficient for some minimal operation, but will + // likely need to be overridden to integrate with the outside world; they are + // all prefixed with ymfm_ to reduce the likelihood of namespace collisions + + // + // timing and synchronizaton + // + + // the chip implementation calls this when a write happens to the mode + // register, which could affect timers and interrupts; our responsibility + // is to ensure the system is up to date before calling the engine's + // engine_mode_write() method + void ymfm_sync_mode_write(uint8_t data) { m_engine->engine_mode_write(data); } + + // the chip implementation calls this when the chip's status has changed, + // which may affect the interrupt state; our responsibility is to ensure + // the system is up to date before calling the engine's + // engine_check_interrupts() method + void ymfm_sync_check_interrupts() { m_engine->engine_check_interrupts(); } + + // the chip implementation calls this when one of the two internal timers + // has changed state; our responsibility is to arrange to call the engine's + // engine_timer_expired() method after the provided number of clocks; if + // duration_in_clocks is negative, we should cancel any outstanding timers + void ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks) { (void)tnum; (void)duration_in_clocks; } + + // the chip implementation calls this to indicate that the chip should be + // considered in a busy state until the given number of clocks has passed; + // our responsibility is to compute and remember the ending time based on + // the chip's clock for later checking + void ymfm_set_busy_end(uint32_t clocks) { (void)clocks; } + + // the chip implementation calls this to see if the chip is still currently + // is a busy state, as specified by a previous call to ymfm_set_busy_end(); + // our responsibility is to compare the current time against the previously + // noted busy end time and return true if we haven't yet passed it + bool ymfm_is_busy() { return false; } + + // + // I/O functions + // + + // the chip implementation calls this when the state of the IRQ signal has + // changed due to a status change; our responsibility is to respond as + // needed to the change in IRQ state, signaling any consumers + void ymfm_update_irq(bool asserted) { (void)asserted; } + + // the chip implementation calls this whenever data is read from outside + // of the chip; our responsibility is to provide the data requested + uint8_t ymfm_external_read(access_class type, uint32_t address) { (void)type; (void)address; return 0; } + + // the chip implementation calls this whenever data is written outside + // of the chip; our responsibility is to pass the written data on to any consumers + void ymfm_external_write(access_class type, uint32_t address, uint8_t data) { (void)type; (void)address; (void)data; } + +protected: + // pointer to engine callbacks -- this is set directly by the engine at + // construction time + ymfm_engine_callbacks *m_engine; +}; + +} + +#endif // YMFM_H diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_adpcm.cpp b/thirdparty/adlmidi/chips/ymfm/ymfm_adpcm.cpp new file mode 100644 index 000000000..4bc22beb2 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_adpcm.cpp @@ -0,0 +1,807 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +#include "ymfm_adpcm.h" + +namespace ymfm +{ + +//********************************************************* +// ADPCM "A" REGISTERS +//********************************************************* + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void adpcm_a_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); + + // initialize the pans to on by default, and max instrument volume; + // some neogeo homebrews (for example ffeast) rely on this + m_regdata[0x08] = m_regdata[0x09] = m_regdata[0x0a] = + m_regdata[0x0b] = m_regdata[0x0c] = m_regdata[0x0d] = 0xdf; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_a_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_regdata); +} + + +//********************************************************* +// ADPCM "A" CHANNEL +//********************************************************* + +//------------------------------------------------- +// adpcm_a_channel - constructor +//------------------------------------------------- + +adpcm_a_channel::adpcm_a_channel(adpcm_a_engine &owner, uint32_t choffs, uint32_t addrshift) : + m_choffs(choffs), + m_address_shift(addrshift), + m_playing(0), + m_curnibble(0), + m_curbyte(0), + m_curaddress(0), + m_accumulator(0), + m_step_index(0), + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +void adpcm_a_channel::reset() +{ + m_playing = 0; + m_curnibble = 0; + m_curbyte = 0; + m_curaddress = 0; + m_accumulator = 0; + m_step_index = 0; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_a_channel::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_playing); + state.save_restore(m_curnibble); + state.save_restore(m_curbyte); + state.save_restore(m_curaddress); + state.save_restore(m_accumulator); + state.save_restore(m_step_index); +} + + +//------------------------------------------------- +// keyonoff - signal key on/off +//------------------------------------------------- + +void adpcm_a_channel::keyonoff(bool on) +{ + // QUESTION: repeated key ons restart the sample? + m_playing = on; + if (m_playing) + { + m_curaddress = m_regs.ch_start(m_choffs) << m_address_shift; + m_curnibble = 0; + m_curbyte = 0; + m_accumulator = 0; + m_step_index = 0; + + // don't log masked channels + if (((debug::GLOBAL_ADPCM_A_CHANNEL_MASK >> m_choffs) & 1) != 0) + debug::log_keyon("KeyOn ADPCM-A%d: pan=%d%d start=%04X end=%04X level=%02X\n", + m_choffs, + m_regs.ch_pan_left(m_choffs), + m_regs.ch_pan_right(m_choffs), + m_regs.ch_start(m_choffs), + m_regs.ch_end(m_choffs), + m_regs.ch_instrument_level(m_choffs)); + } +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +bool adpcm_a_channel::clock() +{ + // if not playing, just output 0 + if (m_playing == 0) + { + m_accumulator = 0; + return false; + } + + // if we're about to read nibble 0, fetch the data + uint8_t data; + if (m_curnibble == 0) + { + // stop when we hit the end address; apparently only low 20 bits are used for + // comparison on the YM2610: this affects sample playback in some games, for + // example twinspri character select screen music will skip some samples if + // this is not correct + // + // note also: end address is inclusive, so wait until we are about to fetch + // the sample just after the end before stopping; this is needed for nitd's + // jump sound, for example + uint32_t end = (m_regs.ch_end(m_choffs) + 1) << m_address_shift; + if (((m_curaddress ^ end) & 0xfffff) == 0) + { + m_playing = m_accumulator = 0; + return true; + } + + m_curbyte = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_A, m_curaddress++); + data = m_curbyte >> 4; + m_curnibble = 1; + } + + // otherwise just extract from the previosuly-fetched byte + else + { + data = m_curbyte & 0xf; + m_curnibble = 0; + } + + // compute the ADPCM delta + static uint16_t const s_steps[49] = + { + 16, 17, 19, 21, 23, 25, 28, + 31, 34, 37, 41, 45, 50, 55, + 60, 66, 73, 80, 88, 97, 107, + 118, 130, 143, 157, 173, 190, 209, + 230, 253, 279, 307, 337, 371, 408, + 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552 + }; + int32_t delta = (2 * bitfield(data, 0, 3) + 1) * s_steps[m_step_index] / 8; + if (bitfield(data, 3)) + delta = -delta; + + // the 12-bit accumulator wraps on the ym2610 and ym2608 (like the msm5205) + m_accumulator = (m_accumulator + delta) & 0xfff; + + // adjust ADPCM step + static int8_t const s_step_inc[8] = { -1, -1, -1, -1, 2, 5, 7, 9 }; + m_step_index = clamp(m_step_index + s_step_inc[bitfield(data, 0, 3)], 0, 48); + + return false; +} + + +//------------------------------------------------- +// output - return the computed output value, with +// panning applied +//------------------------------------------------- + +template +void adpcm_a_channel::output(ymfm_output &output) const +{ + // volume combines instrument and total levels + int vol = (m_regs.ch_instrument_level(m_choffs) ^ 0x1f) + (m_regs.total_level() ^ 0x3f); + + // if combined is maximum, don't add to outputs + if (vol >= 63) + return; + + // convert into a shift and a multiplier + // QUESTION: verify this from other sources + int8_t mul = 15 - (vol & 7); + uint8_t shift = 4 + 1 + (vol >> 3); + + // m_accumulator is a 12-bit value; shift up to sign-extend; + // the downshift is incorporated into 'shift' + int16_t value = ((int16_t(m_accumulator << 4) * mul) >> shift) & ~3; + + // apply to left/right as appropriate + if (NumOutputs == 1 || m_regs.ch_pan_left(m_choffs)) + output.data[0] += value; + if (NumOutputs > 1 && m_regs.ch_pan_right(m_choffs)) + output.data[1] += value; +} + + + +//********************************************************* +// ADPCM "A" ENGINE +//********************************************************* + +//------------------------------------------------- +// adpcm_a_engine - constructor +//------------------------------------------------- + +adpcm_a_engine::adpcm_a_engine(ymfm_interface &intf, uint32_t addrshift) : + m_intf(intf) +{ + // create the channels + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum] = std::make_unique(*this, chnum, addrshift); +} + + +//------------------------------------------------- +// reset - reset the engine state +//------------------------------------------------- + +void adpcm_a_engine::reset() +{ + // reset register state + m_regs.reset(); + + // reset each channel + for (auto &chan : m_channel) + chan->reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_a_engine::save_restore(ymfm_saved_state &state) +{ + // save register state + m_regs.save_restore(state); + + // save channel state + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum]->save_restore(state); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +uint32_t adpcm_a_engine::clock(uint32_t chanmask) +{ + // clock each channel, setting a bit in result if it finished + uint32_t result = 0; + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + if (m_channel[chnum]->clock()) + result |= 1 << chnum; + + // return the bitmask of completed samples + return result; +} + + +//------------------------------------------------- +// update - master update function +//------------------------------------------------- + +template +void adpcm_a_engine::output(ymfm_output &output, uint32_t chanmask) +{ + // mask out some channels for debug purposes + chanmask &= debug::GLOBAL_ADPCM_A_CHANNEL_MASK; + + // compute the output of each channel + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->output(output); +} + +template void adpcm_a_engine::output<1>(ymfm_output<1> &output, uint32_t chanmask); +template void adpcm_a_engine::output<2>(ymfm_output<2> &output, uint32_t chanmask); + + +//------------------------------------------------- +// write - handle writes to the ADPCM-A registers +//------------------------------------------------- + +void adpcm_a_engine::write(uint32_t regnum, uint8_t data) +{ + // store the raw value to the register array; + // most writes are passive, consumed only when needed + m_regs.write(regnum, data); + + // actively handle writes to the control register + if (regnum == 0x00) + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(data, chnum)) + m_channel[chnum]->keyonoff(bitfield(~data, 7)); +} + + + +//********************************************************* +// ADPCM "B" REGISTERS +//********************************************************* + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void adpcm_b_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); + + // default limit to wide open + m_regdata[0x0c] = m_regdata[0x0d] = 0xff; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_b_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_regdata); +} + + + +//********************************************************* +// ADPCM "B" CHANNEL +//********************************************************* + +//------------------------------------------------- +// adpcm_b_channel - constructor +//------------------------------------------------- + +adpcm_b_channel::adpcm_b_channel(adpcm_b_engine &owner, uint32_t addrshift) : + m_address_shift(addrshift), + m_status(STATUS_BRDY), + m_curnibble(0), + m_curbyte(0), + m_dummy_read(0), + m_position(0), + m_curaddress(0), + m_accumulator(0), + m_prev_accum(0), + m_adpcm_step(STEP_MIN), + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +void adpcm_b_channel::reset() +{ + m_status = STATUS_BRDY; + m_curnibble = 0; + m_curbyte = 0; + m_dummy_read = 0; + m_position = 0; + m_curaddress = 0; + m_accumulator = 0; + m_prev_accum = 0; + m_adpcm_step = STEP_MIN; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_b_channel::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_status); + state.save_restore(m_curnibble); + state.save_restore(m_curbyte); + state.save_restore(m_dummy_read); + state.save_restore(m_position); + state.save_restore(m_curaddress); + state.save_restore(m_accumulator); + state.save_restore(m_prev_accum); + state.save_restore(m_adpcm_step); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void adpcm_b_channel::clock() +{ + // only process if active and not recording (which we don't support) + if (!m_regs.execute() || m_regs.record() || (m_status & STATUS_PLAYING) == 0) + { + m_status &= ~STATUS_PLAYING; + return; + } + + // otherwise, advance the step + uint32_t position = m_position + m_regs.delta_n(); + m_position = uint16_t(position); + if (position < 0x10000) + return; + + // if we're about to process nibble 0, fetch sample + if (m_curnibble == 0) + { + // playing from RAM/ROM + if (m_regs.external()) + m_curbyte = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress); + } + + // extract the nibble from our current byte + uint8_t data = uint8_t(m_curbyte << (4 * m_curnibble)) >> 4; + m_curnibble ^= 1; + + // we just processed the last nibble + if (m_curnibble == 0) + { + // if playing from RAM/ROM, check the end/limit address or advance + if (m_regs.external()) + { + // handle the sample end, either repeating or stopping + if (at_end()) + { + // if repeating, go back to the start + if (m_regs.repeat()) + load_start(); + + // otherwise, done; set the EOS bit + else + { + m_accumulator = 0; + m_prev_accum = 0; + m_status = (m_status & ~STATUS_PLAYING) | STATUS_EOS; + debug::log_keyon("%s\n", "ADPCM EOS"); + return; + } + } + + // wrap at the limit address + else if (at_limit()) + m_curaddress = 0; + + // otherwise, advance the current address + else + { + m_curaddress++; + m_curaddress &= 0xffffff; + } + } + + // if CPU-driven, copy the next byte and request more + else + { + m_curbyte = m_regs.cpudata(); + m_status |= STATUS_BRDY; + } + } + + // remember previous value for interpolation + m_prev_accum = m_accumulator; + + // forecast to next forecast: 1/8, 3/8, 5/8, 7/8, 9/8, 11/8, 13/8, 15/8 + int32_t delta = (2 * bitfield(data, 0, 3) + 1) * m_adpcm_step / 8; + if (bitfield(data, 3)) + delta = -delta; + + // add and clamp to 16 bits + m_accumulator = clamp(m_accumulator + delta, -32768, 32767); + + // scale the ADPCM step: 0.9, 0.9, 0.9, 0.9, 1.2, 1.6, 2.0, 2.4 + static uint8_t const s_step_scale[8] = { 57, 57, 57, 57, 77, 102, 128, 153 }; + m_adpcm_step = clamp((m_adpcm_step * s_step_scale[bitfield(data, 0, 3)]) / 64, STEP_MIN, STEP_MAX); +} + + +//------------------------------------------------- +// output - return the computed output value, with +// panning applied +//------------------------------------------------- + +template +void adpcm_b_channel::output(ymfm_output &output, uint32_t rshift) const +{ + // mask out some channels for debug purposes + if ((debug::GLOBAL_ADPCM_B_CHANNEL_MASK & 1) == 0) + return; + + // do a linear interpolation between samples + int32_t result = (m_prev_accum * int32_t((m_position ^ 0xffff) + 1) + m_accumulator * int32_t(m_position)) >> 16; + + // apply volume (level) in a linear fashion and reduce + result = (result * int32_t(m_regs.level())) >> (8 + rshift); + + // apply to left/right + if (NumOutputs == 1 || m_regs.pan_left()) + output.data[0] += result; + if (NumOutputs > 1 && m_regs.pan_right()) + output.data[1] += result; +} + + +//------------------------------------------------- +// read - handle special register reads +//------------------------------------------------- + +uint8_t adpcm_b_channel::read(uint32_t regnum) +{ + uint8_t result = 0; + + // register 8 reads over the bus under some conditions + if (regnum == 0x08 && !m_regs.execute() && !m_regs.record() && m_regs.external()) + { + // two dummy reads are consumed first + if (m_dummy_read != 0) + { + load_start(); + m_dummy_read--; + } + + // read the data + else + { + // read from outside of the chip + result = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress++); + + // did we hit the end? if so, signal EOS + if (at_end()) + { + m_status = STATUS_EOS | STATUS_BRDY; + debug::log_keyon("%s\n", "ADPCM EOS"); + } + else + { + // signal ready + m_status = STATUS_BRDY; + } + + // wrap at the limit address + if (at_limit()) + m_curaddress = 0; + } + } + return result; +} + + +//------------------------------------------------- +// write - handle special register writes +//------------------------------------------------- + +void adpcm_b_channel::write(uint32_t regnum, uint8_t value) +{ + // register 0 can do a reset; also use writes here to reset the + // dummy read counter + if (regnum == 0x00) + { + if (m_regs.execute()) + { + load_start(); + + // don't log masked channels + if ((debug::GLOBAL_ADPCM_B_CHANNEL_MASK & 1) != 0) + debug::log_keyon("KeyOn ADPCM-B: rep=%d spk=%d pan=%d%d dac=%d 8b=%d rom=%d ext=%d rec=%d start=%04X end=%04X pre=%04X dn=%04X lvl=%02X lim=%04X\n", + m_regs.repeat(), + m_regs.speaker(), + m_regs.pan_left(), + m_regs.pan_right(), + m_regs.dac_enable(), + m_regs.dram_8bit(), + m_regs.rom_ram(), + m_regs.external(), + m_regs.record(), + m_regs.start(), + m_regs.end(), + m_regs.prescale(), + m_regs.delta_n(), + m_regs.level(), + m_regs.limit()); + } + else + m_status &= ~STATUS_EOS; + if (m_regs.resetflag()) + reset(); + if (m_regs.external()) + m_dummy_read = 2; + } + + // register 8 writes over the bus under some conditions + else if (regnum == 0x08) + { + // if writing from the CPU during execute, clear the ready flag + if (m_regs.execute() && !m_regs.record() && !m_regs.external()) + m_status &= ~STATUS_BRDY; + + // if writing during "record", pass through as data + else if (!m_regs.execute() && m_regs.record() && m_regs.external()) + { + // clear out dummy reads and set start address + if (m_dummy_read != 0) + { + load_start(); + m_dummy_read = 0; + } + + // did we hit the end? if so, signal EOS + if (at_end()) + { + debug::log_keyon("%s\n", "ADPCM EOS"); + m_status = STATUS_EOS | STATUS_BRDY; + } + + // otherwise, write the data and signal ready + else + { + m_owner.intf().ymfm_external_write(ACCESS_ADPCM_B, m_curaddress++, value); + m_status = STATUS_BRDY; + } + } + } +} + + +//------------------------------------------------- +// address_shift - compute the current address +// shift amount based on register settings +//------------------------------------------------- + +uint32_t adpcm_b_channel::address_shift() const +{ + // if a constant address shift, just provide that + if (m_address_shift != 0) + return m_address_shift; + + // if ROM or 8-bit DRAM, shift is 5 bits + if (m_regs.rom_ram()) + return 5; + if (m_regs.dram_8bit()) + return 5; + + // otherwise, shift is 2 bits + return 2; +} + + +//------------------------------------------------- +// load_start - load the start address and +// initialize the state +//------------------------------------------------- + +void adpcm_b_channel::load_start() +{ + m_status = (m_status & ~STATUS_EOS) | STATUS_PLAYING; + m_curaddress = m_regs.external() ? (m_regs.start() << address_shift()) : 0; + m_curnibble = 0; + m_curbyte = 0; + m_position = 0; + m_accumulator = 0; + m_prev_accum = 0; + m_adpcm_step = STEP_MIN; +} + + + +//********************************************************* +// ADPCM "B" ENGINE +//********************************************************* + +//------------------------------------------------- +// adpcm_b_engine - constructor +//------------------------------------------------- + +adpcm_b_engine::adpcm_b_engine(ymfm_interface &intf, uint32_t addrshift) : + m_intf(intf) +{ + // create the channel (only one supported for now, but leaving possibilities open) + m_channel = std::make_unique(*this, addrshift); +} + + +//------------------------------------------------- +// reset - reset the engine state +//------------------------------------------------- + +void adpcm_b_engine::reset() +{ + // reset registers + m_regs.reset(); + + // reset each channel + m_channel->reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_b_engine::save_restore(ymfm_saved_state &state) +{ + // save our state + m_regs.save_restore(state); + + // save channel state + m_channel->save_restore(state); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void adpcm_b_engine::clock() +{ + // clock each channel, setting a bit in result if it finished + m_channel->clock(); +} + + +//------------------------------------------------- +// output - master output function +//------------------------------------------------- + +template +void adpcm_b_engine::output(ymfm_output &output, uint32_t rshift) +{ + // compute the output of each channel + m_channel->output(output, rshift); +} + +template void adpcm_b_engine::output<1>(ymfm_output<1> &output, uint32_t rshift); +template void adpcm_b_engine::output<2>(ymfm_output<2> &output, uint32_t rshift); + + +//------------------------------------------------- +// write - handle writes to the ADPCM-B registers +//------------------------------------------------- + +void adpcm_b_engine::write(uint32_t regnum, uint8_t data) +{ + // store the raw value to the register array; + // most writes are passive, consumed only when needed + m_regs.write(regnum, data); + + // let the channel handle any special writes + m_channel->write(regnum, data); +} + +} diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_adpcm.h b/thirdparty/adlmidi/chips/ymfm/ymfm_adpcm.h new file mode 100644 index 000000000..d74e24f27 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_adpcm.h @@ -0,0 +1,411 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_ADPCM_H +#define YMFM_ADPCM_H + +#pragma once + +#include "ymfm.h" + +namespace ymfm +{ + +//********************************************************* +// INTERFACE CLASSES +//********************************************************* + +// forward declarations +class adpcm_a_engine; +class adpcm_b_engine; + + +// ======================> adpcm_a_registers + +// +// ADPCM-A register map: +// +// System-wide registers: +// 00 x------- Dump (disable=1) or keyon (0) control +// --xxxxxx Mask of channels to dump or keyon +// 01 --xxxxxx Total level +// 02 xxxxxxxx Test register +// 08-0D x------- Pan left +// -x------ Pan right +// ---xxxxx Instrument level +// 10-15 xxxxxxxx Start address (low) +// 18-1D xxxxxxxx Start address (high) +// 20-25 xxxxxxxx End address (low) +// 28-2D xxxxxxxx End address (high) +// +class adpcm_a_registers +{ +public: + // constants + static constexpr uint32_t OUTPUTS = 2; + static constexpr uint32_t CHANNELS = 6; + static constexpr uint32_t REGISTERS = 0x30; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + + // constructor + adpcm_a_registers() { } + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // map channel number to register offset + static constexpr uint32_t channel_offset(uint32_t chnum) + { + assert(chnum < CHANNELS); + return chnum; + } + + // direct read/write access + void write(uint32_t index, uint8_t data) { m_regdata[index] = data; } + + // system-wide registers + uint32_t dump() const { return bitfield(m_regdata[0x00], 7); } + uint32_t dump_mask() const { return bitfield(m_regdata[0x00], 0, 6); } + uint32_t total_level() const { return bitfield(m_regdata[0x01], 0, 6); } + uint32_t test() const { return m_regdata[0x02]; } + + // per-channel registers + uint32_t ch_pan_left(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 7); } + uint32_t ch_pan_right(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 6); } + uint32_t ch_instrument_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 0, 5); } + uint32_t ch_start(uint32_t choffs) const { return m_regdata[choffs + 0x10] | (m_regdata[choffs + 0x18] << 8); } + uint32_t ch_end(uint32_t choffs) const { return m_regdata[choffs + 0x20] | (m_regdata[choffs + 0x28] << 8); } + + // per-channel writes + void write_start(uint32_t choffs, uint32_t address) + { + write(choffs + 0x10, address); + write(choffs + 0x18, address >> 8); + } + void write_end(uint32_t choffs, uint32_t address) + { + write(choffs + 0x20, address); + write(choffs + 0x28, address >> 8); + } + +private: + // internal state + uint8_t m_regdata[REGISTERS]; // register data +}; + + +// ======================> adpcm_a_channel + +class adpcm_a_channel +{ +public: + // constructor + adpcm_a_channel(adpcm_a_engine &owner, uint32_t choffs, uint32_t addrshift); + + // reset the channel state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // signal key on/off + void keyonoff(bool on); + + // master clockingfunction + bool clock(); + + // return the computed output value, with panning applied + template + void output(ymfm_output &output) const; + +private: + // internal state + uint32_t const m_choffs; // channel offset + uint32_t const m_address_shift; // address bits shift-left + uint32_t m_playing; // currently playing? + uint32_t m_curnibble; // index of the current nibble + uint32_t m_curbyte; // current byte of data + uint32_t m_curaddress; // current address + int32_t m_accumulator; // accumulator + int32_t m_step_index; // index in the stepping table + adpcm_a_registers &m_regs; // reference to registers + adpcm_a_engine &m_owner; // reference to our owner +}; + + +// ======================> adpcm_a_engine + +class adpcm_a_engine +{ +public: + static constexpr int CHANNELS = adpcm_a_registers::CHANNELS; + + // constructor + adpcm_a_engine(ymfm_interface &intf, uint32_t addrshift); + + // reset our status + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // master clocking function + uint32_t clock(uint32_t chanmask); + + // compute sum of channel outputs + template + void output(ymfm_output &output, uint32_t chanmask); + + // write to the ADPCM-A registers + void write(uint32_t regnum, uint8_t data); + + // set the start/end address for a channel (for hardcoded YM2608 percussion) + void set_start_end(uint8_t chnum, uint16_t start, uint16_t end) + { + uint32_t choffs = adpcm_a_registers::channel_offset(chnum); + m_regs.write_start(choffs, start); + m_regs.write_end(choffs, end); + } + + // return a reference to our interface + ymfm_interface &intf() { return m_intf; } + + // return a reference to our registers + adpcm_a_registers ®s() { return m_regs; } + +private: + // internal state + ymfm_interface &m_intf; // reference to the interface + std::unique_ptr m_channel[CHANNELS]; // array of channels + adpcm_a_registers m_regs; // registers +}; + + +// ======================> adpcm_b_registers + +// +// ADPCM-B register map: +// +// System-wide registers: +// 00 x------- Start of synthesis/analysis +// -x------ Record +// --x----- External/manual driving +// ---x---- Repeat playback +// ----x--- Speaker off +// -------x Reset +// 01 x------- Pan left +// -x------ Pan right +// ----x--- Start conversion +// -----x-- DAC enable +// ------x- DRAM access (1=8-bit granularity; 0=1-bit) +// -------x RAM/ROM (1=ROM, 0=RAM) +// 02 xxxxxxxx Start address (low) +// 03 xxxxxxxx Start address (high) +// 04 xxxxxxxx End address (low) +// 05 xxxxxxxx End address (high) +// 06 xxxxxxxx Prescale value (low) +// 07 -----xxx Prescale value (high) +// 08 xxxxxxxx CPU data/buffer +// 09 xxxxxxxx Delta-N frequency scale (low) +// 0a xxxxxxxx Delta-N frequency scale (high) +// 0b xxxxxxxx Level control +// 0c xxxxxxxx Limit address (low) +// 0d xxxxxxxx Limit address (high) +// 0e xxxxxxxx DAC data [YM2608/10] +// 0f xxxxxxxx PCM data [YM2608/10] +// 0e xxxxxxxx DAC data high [Y8950] +// 0f xx------ DAC data low [Y8950] +// 10 -----xxx DAC data exponent [Y8950] +// +class adpcm_b_registers +{ +public: + // constants + static constexpr uint32_t REGISTERS = 0x11; + + // constructor + adpcm_b_registers() { } + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // direct read/write access + void write(uint32_t index, uint8_t data) { m_regdata[index] = data; } + + // system-wide registers + uint32_t execute() const { return bitfield(m_regdata[0x00], 7); } + uint32_t record() const { return bitfield(m_regdata[0x00], 6); } + uint32_t external() const { return bitfield(m_regdata[0x00], 5); } + uint32_t repeat() const { return bitfield(m_regdata[0x00], 4); } + uint32_t speaker() const { return bitfield(m_regdata[0x00], 3); } + uint32_t resetflag() const { return bitfield(m_regdata[0x00], 0); } + uint32_t pan_left() const { return bitfield(m_regdata[0x01], 7); } + uint32_t pan_right() const { return bitfield(m_regdata[0x01], 6); } + uint32_t start_conversion() const { return bitfield(m_regdata[0x01], 3); } + uint32_t dac_enable() const { return bitfield(m_regdata[0x01], 2); } + uint32_t dram_8bit() const { return bitfield(m_regdata[0x01], 1); } + uint32_t rom_ram() const { return bitfield(m_regdata[0x01], 0); } + uint32_t start() const { return m_regdata[0x02] | (m_regdata[0x03] << 8); } + uint32_t end() const { return m_regdata[0x04] | (m_regdata[0x05] << 8); } + uint32_t prescale() const { return m_regdata[0x06] | (bitfield(m_regdata[0x07], 0, 3) << 8); } + uint32_t cpudata() const { return m_regdata[0x08]; } + uint32_t delta_n() const { return m_regdata[0x09] | (m_regdata[0x0a] << 8); } + uint32_t level() const { return m_regdata[0x0b]; } + uint32_t limit() const { return m_regdata[0x0c] | (m_regdata[0x0d] << 8); } + uint32_t dac() const { return m_regdata[0x0e]; } + uint32_t pcm() const { return m_regdata[0x0f]; } + +private: + // internal state + uint8_t m_regdata[REGISTERS]; // register data +}; + + +// ======================> adpcm_b_channel + +class adpcm_b_channel +{ + static constexpr int32_t STEP_MIN = 127; + static constexpr int32_t STEP_MAX = 24576; + +public: + static constexpr uint8_t STATUS_EOS = 0x01; + static constexpr uint8_t STATUS_BRDY = 0x02; + static constexpr uint8_t STATUS_PLAYING = 0x04; + + // constructor + adpcm_b_channel(adpcm_b_engine &owner, uint32_t addrshift); + + // reset the channel state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // signal key on/off + void keyonoff(bool on); + + // master clocking function + void clock(); + + // return the computed output value, with panning applied + template + void output(ymfm_output &output, uint32_t rshift) const; + + // return the status register + uint8_t status() const { return m_status; } + + // handle special register reads + uint8_t read(uint32_t regnum); + + // handle special register writes + void write(uint32_t regnum, uint8_t value); + +private: + // helper - return the current address shift + uint32_t address_shift() const; + + // load the start address + void load_start(); + + // limit checker; stops at the last byte of the chunk described by address_shift() + bool at_limit() const { return (m_curaddress == (((m_regs.limit() + 1) << address_shift()) - 1)); } + + // end checker; stops at the last byte of the chunk described by address_shift() + bool at_end() const { return (m_curaddress == (((m_regs.end() + 1) << address_shift()) - 1)); } + + // internal state + uint32_t const m_address_shift; // address bits shift-left + uint32_t m_status; // currently playing? + uint32_t m_curnibble; // index of the current nibble + uint32_t m_curbyte; // current byte of data + uint32_t m_dummy_read; // dummy read tracker + uint32_t m_position; // current fractional position + uint32_t m_curaddress; // current address + int32_t m_accumulator; // accumulator + int32_t m_prev_accum; // previous accumulator (for linear interp) + int32_t m_adpcm_step; // next forecast + adpcm_b_registers &m_regs; // reference to registers + adpcm_b_engine &m_owner; // reference to our owner +}; + + +// ======================> adpcm_b_engine + +class adpcm_b_engine +{ +public: + // constructor + adpcm_b_engine(ymfm_interface &intf, uint32_t addrshift = 0); + + // reset our status + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // master clocking function + void clock(); + + // compute sum of channel outputs + template + void output(ymfm_output &output, uint32_t rshift); + + // read from the ADPCM-B registers + uint32_t read(uint32_t regnum) { return m_channel->read(regnum); } + + // write to the ADPCM-B registers + void write(uint32_t regnum, uint8_t data); + + // status + uint8_t status() const { return m_channel->status(); } + + // return a reference to our interface + ymfm_interface &intf() { return m_intf; } + + // return a reference to our registers + adpcm_b_registers ®s() { return m_regs; } + +private: + // internal state + ymfm_interface &m_intf; // reference to our interface + std::unique_ptr m_channel; // channel pointer + adpcm_b_registers m_regs; // registers +}; + +} + +#endif // YMFM_ADPCM_H diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_fm.h b/thirdparty/adlmidi/chips/ymfm/ymfm_fm.h new file mode 100644 index 000000000..623093cb5 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_fm.h @@ -0,0 +1,463 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_FM_H +#define YMFM_FM_H + +#pragma once + +#define YMFM_DEBUG_LOG_WAVFILES (0) + +namespace ymfm +{ + +//********************************************************* +// GLOBAL ENUMERATORS +//********************************************************* + +// three different keyon sources; actual keyon is an OR over all of these +enum keyon_type : uint32_t +{ + KEYON_NORMAL = 0, + KEYON_RHYTHM = 1, + KEYON_CSM = 2 +}; + + + +//********************************************************* +// CORE IMPLEMENTATION +//********************************************************* + +// ======================> opdata_cache + +// this class holds data that is computed once at the start of clocking +// and remains static during subsequent sound generation +struct opdata_cache +{ + // set phase_step to this value to recalculate it each sample; needed + // in the case of PM LFO changes + static constexpr uint32_t PHASE_STEP_DYNAMIC = 1; + + uint16_t const *waveform; // base of sine table + uint32_t phase_step; // phase step, or PHASE_STEP_DYNAMIC if PM is active + uint32_t total_level; // total level * 8 + KSL + uint32_t block_freq; // raw block frequency value (used to compute phase_step) + int32_t detune; // detuning value (used to compute phase_step) + uint32_t multiple; // multiple value (x.1, used to compute phase_step) + uint32_t eg_sustain; // sustain level, shifted up to envelope values + uint8_t eg_rate[EG_STATES]; // envelope rate, including KSR + uint8_t eg_shift = 0; // envelope shift amount +}; + + +// ======================> fm_registers_base + +// base class for family-specific register classes; this provides a few +// constants, common defaults, and helpers, but mostly each derived class is +// responsible for defining all commonly-called methods +class fm_registers_base +{ +public: + // this value is returned from the write() function for rhythm channels + static constexpr uint32_t RHYTHM_CHANNEL = 0xff; + + // this is the size of a full sin waveform + static constexpr uint32_t WAVEFORM_LENGTH = 0x400; + + // + // the following constants need to be defined per family: + // uint32_t OUTPUTS: The number of outputs exposed (1-4) + // uint32_t CHANNELS: The number of channels on the chip + // uint32_t ALL_CHANNELS: A bitmask of all channels + // uint32_t OPERATORS: The number of operators on the chip + // uint32_t WAVEFORMS: The number of waveforms offered + // uint32_t REGISTERS: The number of 8-bit registers allocated + // uint32_t DEFAULT_PRESCALE: The starting clock prescale + // uint32_t EG_CLOCK_DIVIDER: The clock divider of the envelope generator + // uint32_t CSM_TRIGGER_MASK: Mask of channels to trigger in CSM mode + // uint32_t REG_MODE: The address of the "mode" register controlling timers + // uint8_t STATUS_TIMERA: Status bit to set when timer A fires + // uint8_t STATUS_TIMERB: Status bit to set when tiemr B fires + // uint8_t STATUS_BUSY: Status bit to set when the chip is busy + // uint8_t STATUS_IRQ: Status bit to set when an IRQ is signalled + // + // the following constants are uncommon: + // bool DYNAMIC_OPS: True if ops/channel can be changed at runtime (OPL3+) + // bool EG_HAS_DEPRESS: True if the chip has a DP ("depress"?) envelope stage (OPLL) + // bool EG_HAS_REVERB: True if the chip has a faux reverb envelope stage (OPQ/OPZ) + // bool EG_HAS_SSG: True if the chip has SSG envelope support (OPN) + // bool MODULATOR_DELAY: True if the modulator is delayed by 1 sample (OPL pre-OPL3) + // + static constexpr bool DYNAMIC_OPS = false; + static constexpr bool EG_HAS_DEPRESS = false; + static constexpr bool EG_HAS_REVERB = false; + static constexpr bool EG_HAS_SSG = false; + static constexpr bool MODULATOR_DELAY = false; + + // system-wide register defaults + uint32_t status_mask() const { return 0; } // OPL only + uint32_t irq_reset() const { return 0; } // OPL only + uint32_t noise_enable() const { return 0; } // OPM only + uint32_t rhythm_enable() const { return 0; } // OPL only + + // per-operator register defaults + uint32_t op_ssg_eg_enable(uint32_t opoffs) const { (void)opoffs; return 0; } // OPN(A) only + uint32_t op_ssg_eg_mode(uint32_t opoffs) const { (void)opoffs; return 0; } // OPN(A) only + +protected: + // helper to encode four operator numbers into a 32-bit value in the + // operator maps for each register class + static constexpr uint32_t operator_list(uint8_t o1 = 0xff, uint8_t o2 = 0xff, uint8_t o3 = 0xff, uint8_t o4 = 0xff) + { + return o1 | (o2 << 8) | (o3 << 16) | (o4 << 24); + } + + // helper to apply KSR to the raw ADSR rate, ignoring ksr if the + // raw value is 0, and clamping to 63 + static constexpr uint32_t effective_rate(uint32_t rawrate, uint32_t ksr) + { + return (rawrate == 0) ? 0 : std::min(rawrate + ksr, 63); + } +}; + + + +//********************************************************* +// CORE ENGINE CLASSES +//********************************************************* + +// forward declarations +template class fm_engine_base; + +// ======================> fm_operator + +// fm_operator represents an FM operator (or "slot" in FM parlance), which +// produces an output sine wave modulated by an envelope +template +class fm_operator +{ + // "quiet" value, used to optimize when we can skip doing work + static constexpr uint32_t EG_QUIET = 0x380; + +public: + // constructor + fm_operator(fm_engine_base &owner, uint32_t opoffs); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset the operator state + void reset(); + + // return the operator/channel offset + uint32_t opoffs() const { return m_opoffs; } + uint32_t choffs() const { return m_choffs; } + + // set the current channel + void set_choffs(uint32_t choffs) { m_choffs = choffs; } + + // prepare prior to clocking + bool prepare(); + + // master clocking function + void clock(uint32_t env_counter, int32_t lfo_raw_pm); + + // return the current phase value + uint32_t phase() const { return m_phase >> 10; } + + // compute operator volume + int32_t compute_volume(uint32_t phase, uint32_t am_offset) const; + + // compute volume for the OPM noise channel + int32_t compute_noise_volume(uint32_t am_offset) const; + + // key state control + void keyonoff(uint32_t on, keyon_type type); + + // return a reference to our registers + RegisterType ®s() const { return m_regs; } + + // simple getters for debugging + envelope_state debug_eg_state() const { return m_env_state; } + uint16_t debug_eg_attenuation() const { return m_env_attenuation; } + uint8_t debug_ssg_inverted() const { return m_ssg_inverted; } + opdata_cache &debug_cache() { return m_cache; } + +private: + // start the attack phase + void start_attack(bool is_restart = false); + + // start the release phase + void start_release(); + + // clock phases + void clock_keystate(uint32_t keystate); + void clock_ssg_eg_state(); + void clock_envelope(uint32_t env_counter); + void clock_phase(int32_t lfo_raw_pm); + + // return effective attenuation of the envelope + uint32_t envelope_attenuation(uint32_t am_offset) const; + + // internal state + uint32_t m_choffs; // channel offset in registers + uint32_t m_opoffs; // operator offset in registers + uint32_t m_phase; // current phase value (10.10 format) + uint16_t m_env_attenuation; // computed envelope attenuation (4.6 format) + envelope_state m_env_state; // current envelope state + uint8_t m_ssg_inverted; // non-zero if the output should be inverted (bit 0) + uint8_t m_key_state; // current key state: on or off (bit 0) + uint8_t m_keyon_live; // live key on state (bit 0 = direct, bit 1 = rhythm, bit 2 = CSM) + opdata_cache m_cache; // cached values for performance + RegisterType &m_regs; // direct reference to registers + fm_engine_base &m_owner; // reference to the owning engine +}; + + +// ======================> fm_channel + +// fm_channel represents an FM channel which combines the output of 2 or 4 +// operators into a final result +template +class fm_channel +{ + using output_data = ymfm_output; + +public: + // constructor + fm_channel(fm_engine_base &owner, uint32_t choffs); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset the channel state + void reset(); + + // return the channel offset + uint32_t choffs() const { return m_choffs; } + + // assign operators + void assign(uint32_t index, fm_operator *op) + { + assert(index < array_size(m_op)); + m_op[index] = op; + if (op != nullptr) + op->set_choffs(m_choffs); + } + + // signal key on/off to our operators + void keyonoff(uint32_t states, keyon_type type, uint32_t chnum); + + // prepare prior to clocking + bool prepare(); + + // master clocking function + void clock(uint32_t env_counter, int32_t lfo_raw_pm); + + // specific 2-operator and 4-operator output handlers + void output_2op(output_data &output, uint32_t rshift, int32_t clipmax) const; + void output_4op(output_data &output, uint32_t rshift, int32_t clipmax) const; + + // compute the special OPL rhythm channel outputs + void output_rhythm_ch6(output_data &output, uint32_t rshift, int32_t clipmax) const; + void output_rhythm_ch7(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const; + void output_rhythm_ch8(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const; + + // are we a 4-operator channel or a 2-operator one? + bool is4op() const + { + if (RegisterType::DYNAMIC_OPS) + return (m_op[2] != nullptr); + return (RegisterType::OPERATORS / RegisterType::CHANNELS == 4); + } + + // return a reference to our registers + RegisterType ®s() const { return m_regs; } + + // simple getters for debugging + fm_operator *debug_operator(uint32_t index) const { return m_op[index]; } + +private: + // helper to add values to the outputs based on channel enables + void add_to_output(uint32_t choffs, output_data &output, int32_t value) const + { + // create these constants to appease overzealous compilers checking array + // bounds in unreachable code (looking at you, clang) + constexpr int out0_index = 0; + constexpr int out1_index = 1 % RegisterType::OUTPUTS; + constexpr int out2_index = 2 % RegisterType::OUTPUTS; + constexpr int out3_index = 3 % RegisterType::OUTPUTS; + + if (RegisterType::OUTPUTS == 1 || m_regs.ch_output_0(choffs)) + output.data[out0_index] += value; + if (RegisterType::OUTPUTS >= 2 && m_regs.ch_output_1(choffs)) + output.data[out1_index] += value; + if (RegisterType::OUTPUTS >= 3 && m_regs.ch_output_2(choffs)) + output.data[out2_index] += value; + if (RegisterType::OUTPUTS >= 4 && m_regs.ch_output_3(choffs)) + output.data[out3_index] += value; + } + + // internal state + uint32_t m_choffs; // channel offset in registers + int16_t m_feedback[2]; // feedback memory for operator 1 + mutable int16_t m_feedback_in; // next input value for op 1 feedback (set in output) + fm_operator *m_op[4]; // up to 4 operators + RegisterType &m_regs; // direct reference to registers + fm_engine_base &m_owner; // reference to the owning engine +}; + + +// ======================> fm_engine_base + +// fm_engine_base represents a set of operators and channels which together +// form a Yamaha FM core; chips that implement other engines (ADPCM, wavetable, +// etc) take this output and combine it with the others externally +template +class fm_engine_base : public ymfm_engine_callbacks +{ +public: + // expose some constants from the registers + static constexpr uint32_t OUTPUTS = RegisterType::OUTPUTS; + static constexpr uint32_t CHANNELS = RegisterType::CHANNELS; + static constexpr uint32_t ALL_CHANNELS = RegisterType::ALL_CHANNELS; + static constexpr uint32_t OPERATORS = RegisterType::OPERATORS; + + // also expose status flags for consumers that inject additional bits + static constexpr uint8_t STATUS_TIMERA = RegisterType::STATUS_TIMERA; + static constexpr uint8_t STATUS_TIMERB = RegisterType::STATUS_TIMERB; + static constexpr uint8_t STATUS_BUSY = RegisterType::STATUS_BUSY; + static constexpr uint8_t STATUS_IRQ = RegisterType::STATUS_IRQ; + + // expose the correct output class + using output_data = ymfm_output; + + // constructor + fm_engine_base(ymfm_interface &intf); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset the overall state + void reset(); + + // master clocking function + uint32_t clock(uint32_t chanmask); + + // compute sum of channel outputs + void output(output_data &output, uint32_t rshift, int32_t clipmax, uint32_t chanmask) const; + + // write to the OPN registers + void write(uint16_t regnum, uint8_t data); + + // return the current status + uint8_t status() const; + + // set/reset bits in the status register, updating the IRQ status + uint8_t set_reset_status(uint8_t set, uint8_t reset) + { + m_status = (m_status | set) & ~(reset | STATUS_BUSY); + m_intf.ymfm_sync_check_interrupts(); + return m_status & ~m_regs.status_mask(); + } + + // set the IRQ mask + void set_irq_mask(uint8_t mask) { m_irq_mask = mask; m_intf.ymfm_sync_check_interrupts(); } + + // return the current clock prescale + uint32_t clock_prescale() const { return m_clock_prescale; } + + // set prescale factor (2/3/6) + void set_clock_prescale(uint32_t prescale) { m_clock_prescale = prescale; } + + // compute sample rate + uint32_t sample_rate(uint32_t baseclock) const + { +#if (YMFM_DEBUG_LOG_WAVFILES) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + m_wavfile[chnum].set_samplerate(baseclock / (m_clock_prescale * OPERATORS)); +#endif + return baseclock / (m_clock_prescale * OPERATORS); + } + + // return the owning device + ymfm_interface &intf() const { return m_intf; } + + // return a reference to our registers + RegisterType ®s() { return m_regs; } + + // invalidate any caches + void invalidate_caches() { m_modified_channels = RegisterType::ALL_CHANNELS; } + + // simple getters for debugging + fm_channel *debug_channel(uint32_t index) const { return m_channel[index].get(); } + fm_operator *debug_operator(uint32_t index) const { return m_operator[index].get(); } + +public: + // timer callback; called by the interface when a timer fires + virtual void engine_timer_expired(uint32_t tnum) override; + + // check interrupts; called by the interface after synchronization + virtual void engine_check_interrupts() override; + + // mode register write; called by the interface after synchronization + virtual void engine_mode_write(uint8_t data) override; + +protected: + // assign the current set of operators to channels + void assign_operators(); + + // update the state of the given timer + void update_timer(uint32_t which, uint32_t enable, int32_t delta_clocks); + + // internal state + ymfm_interface &m_intf; // reference to the system interface + uint32_t m_env_counter; // envelope counter; low 2 bits are sub-counter + uint8_t m_status; // current status register + uint8_t m_clock_prescale; // prescale factor (2/3/6) + uint8_t m_irq_mask; // mask of which bits signal IRQs + uint8_t m_irq_state; // current IRQ state + uint8_t m_timer_running[2]; // current timer running state + uint8_t m_total_clocks; // low 8 bits of the total number of clocks processed + uint32_t m_active_channels; // mask of active channels (computed by prepare) + uint32_t m_modified_channels; // mask of channels that have been modified + uint32_t m_prepare_count; // counter to do periodic prepare sweeps + RegisterType m_regs; // register accessor + std::unique_ptr> m_channel[CHANNELS]; // channel pointers + std::unique_ptr> m_operator[OPERATORS]; // operator pointers +#if (YMFM_DEBUG_LOG_WAVFILES) + mutable ymfm_wavfile<1> m_wavfile[CHANNELS]; // for debugging +#endif +}; + +} + +#endif // YMFM_FM_H diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_fm.ipp b/thirdparty/adlmidi/chips/ymfm/ymfm_fm.ipp new file mode 100644 index 000000000..fcfffcbd9 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_fm.ipp @@ -0,0 +1,1597 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +namespace ymfm +{ + +//********************************************************* +// GLOBAL TABLE LOOKUPS +//********************************************************* + +//------------------------------------------------- +// abs_sin_attenuation - given a sin (phase) input +// where the range 0-2*PI is mapped onto 10 bits, +// return the absolute value of sin(input), +// logarithmically-adjusted and treated as an +// attenuation value, in 4.8 fixed point format +//------------------------------------------------- + +inline uint32_t abs_sin_attenuation(uint32_t input) +{ + // the values here are stored as 4.8 logarithmic values for 1/4 phase + // this matches the internal format of the OPN chip, extracted from the die + static uint16_t const s_sin_table[256] = + { + 0x859,0x6c3,0x607,0x58b,0x52e,0x4e4,0x4a6,0x471,0x443,0x41a,0x3f5,0x3d3,0x3b5,0x398,0x37e,0x365, + 0x34e,0x339,0x324,0x311,0x2ff,0x2ed,0x2dc,0x2cd,0x2bd,0x2af,0x2a0,0x293,0x286,0x279,0x26d,0x261, + 0x256,0x24b,0x240,0x236,0x22c,0x222,0x218,0x20f,0x206,0x1fd,0x1f5,0x1ec,0x1e4,0x1dc,0x1d4,0x1cd, + 0x1c5,0x1be,0x1b7,0x1b0,0x1a9,0x1a2,0x19b,0x195,0x18f,0x188,0x182,0x17c,0x177,0x171,0x16b,0x166, + 0x160,0x15b,0x155,0x150,0x14b,0x146,0x141,0x13c,0x137,0x133,0x12e,0x129,0x125,0x121,0x11c,0x118, + 0x114,0x10f,0x10b,0x107,0x103,0x0ff,0x0fb,0x0f8,0x0f4,0x0f0,0x0ec,0x0e9,0x0e5,0x0e2,0x0de,0x0db, + 0x0d7,0x0d4,0x0d1,0x0cd,0x0ca,0x0c7,0x0c4,0x0c1,0x0be,0x0bb,0x0b8,0x0b5,0x0b2,0x0af,0x0ac,0x0a9, + 0x0a7,0x0a4,0x0a1,0x09f,0x09c,0x099,0x097,0x094,0x092,0x08f,0x08d,0x08a,0x088,0x086,0x083,0x081, + 0x07f,0x07d,0x07a,0x078,0x076,0x074,0x072,0x070,0x06e,0x06c,0x06a,0x068,0x066,0x064,0x062,0x060, + 0x05e,0x05c,0x05b,0x059,0x057,0x055,0x053,0x052,0x050,0x04e,0x04d,0x04b,0x04a,0x048,0x046,0x045, + 0x043,0x042,0x040,0x03f,0x03e,0x03c,0x03b,0x039,0x038,0x037,0x035,0x034,0x033,0x031,0x030,0x02f, + 0x02e,0x02d,0x02b,0x02a,0x029,0x028,0x027,0x026,0x025,0x024,0x023,0x022,0x021,0x020,0x01f,0x01e, + 0x01d,0x01c,0x01b,0x01a,0x019,0x018,0x017,0x017,0x016,0x015,0x014,0x014,0x013,0x012,0x011,0x011, + 0x010,0x00f,0x00f,0x00e,0x00d,0x00d,0x00c,0x00c,0x00b,0x00a,0x00a,0x009,0x009,0x008,0x008,0x007, + 0x007,0x007,0x006,0x006,0x005,0x005,0x005,0x004,0x004,0x004,0x003,0x003,0x003,0x002,0x002,0x002, + 0x002,0x001,0x001,0x001,0x001,0x001,0x001,0x001,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000 + }; + + // if the top bit is set, we're in the second half of the curve + // which is a mirror image, so invert the index + if (bitfield(input, 8)) + input = ~input; + + // return the value from the table + return s_sin_table[input & 0xff]; +} + + +//------------------------------------------------- +// attenuation_to_volume - given a 5.8 fixed point +// logarithmic attenuation value, return a 13-bit +// linear volume +//------------------------------------------------- + +inline uint32_t attenuation_to_volume(uint32_t input) +{ + // the values here are 10-bit mantissas with an implied leading bit + // this matches the internal format of the OPN chip, extracted from the die + + // as a nod to performance, the implicit 0x400 bit is pre-incorporated, and + // the values are left-shifted by 2 so that a simple right shift is all that + // is needed; also the order is reversed to save a NOT on the input +#define X(a) (((a) | 0x400) << 2) + static uint16_t const s_power_table[256] = + { + X(0x3fa),X(0x3f5),X(0x3ef),X(0x3ea),X(0x3e4),X(0x3df),X(0x3da),X(0x3d4), + X(0x3cf),X(0x3c9),X(0x3c4),X(0x3bf),X(0x3b9),X(0x3b4),X(0x3ae),X(0x3a9), + X(0x3a4),X(0x39f),X(0x399),X(0x394),X(0x38f),X(0x38a),X(0x384),X(0x37f), + X(0x37a),X(0x375),X(0x370),X(0x36a),X(0x365),X(0x360),X(0x35b),X(0x356), + X(0x351),X(0x34c),X(0x347),X(0x342),X(0x33d),X(0x338),X(0x333),X(0x32e), + X(0x329),X(0x324),X(0x31f),X(0x31a),X(0x315),X(0x310),X(0x30b),X(0x306), + X(0x302),X(0x2fd),X(0x2f8),X(0x2f3),X(0x2ee),X(0x2e9),X(0x2e5),X(0x2e0), + X(0x2db),X(0x2d6),X(0x2d2),X(0x2cd),X(0x2c8),X(0x2c4),X(0x2bf),X(0x2ba), + X(0x2b5),X(0x2b1),X(0x2ac),X(0x2a8),X(0x2a3),X(0x29e),X(0x29a),X(0x295), + X(0x291),X(0x28c),X(0x288),X(0x283),X(0x27f),X(0x27a),X(0x276),X(0x271), + X(0x26d),X(0x268),X(0x264),X(0x25f),X(0x25b),X(0x257),X(0x252),X(0x24e), + X(0x249),X(0x245),X(0x241),X(0x23c),X(0x238),X(0x234),X(0x230),X(0x22b), + X(0x227),X(0x223),X(0x21e),X(0x21a),X(0x216),X(0x212),X(0x20e),X(0x209), + X(0x205),X(0x201),X(0x1fd),X(0x1f9),X(0x1f5),X(0x1f0),X(0x1ec),X(0x1e8), + X(0x1e4),X(0x1e0),X(0x1dc),X(0x1d8),X(0x1d4),X(0x1d0),X(0x1cc),X(0x1c8), + X(0x1c4),X(0x1c0),X(0x1bc),X(0x1b8),X(0x1b4),X(0x1b0),X(0x1ac),X(0x1a8), + X(0x1a4),X(0x1a0),X(0x19c),X(0x199),X(0x195),X(0x191),X(0x18d),X(0x189), + X(0x185),X(0x181),X(0x17e),X(0x17a),X(0x176),X(0x172),X(0x16f),X(0x16b), + X(0x167),X(0x163),X(0x160),X(0x15c),X(0x158),X(0x154),X(0x151),X(0x14d), + X(0x149),X(0x146),X(0x142),X(0x13e),X(0x13b),X(0x137),X(0x134),X(0x130), + X(0x12c),X(0x129),X(0x125),X(0x122),X(0x11e),X(0x11b),X(0x117),X(0x114), + X(0x110),X(0x10c),X(0x109),X(0x106),X(0x102),X(0x0ff),X(0x0fb),X(0x0f8), + X(0x0f4),X(0x0f1),X(0x0ed),X(0x0ea),X(0x0e7),X(0x0e3),X(0x0e0),X(0x0dc), + X(0x0d9),X(0x0d6),X(0x0d2),X(0x0cf),X(0x0cc),X(0x0c8),X(0x0c5),X(0x0c2), + X(0x0be),X(0x0bb),X(0x0b8),X(0x0b5),X(0x0b1),X(0x0ae),X(0x0ab),X(0x0a8), + X(0x0a4),X(0x0a1),X(0x09e),X(0x09b),X(0x098),X(0x094),X(0x091),X(0x08e), + X(0x08b),X(0x088),X(0x085),X(0x082),X(0x07e),X(0x07b),X(0x078),X(0x075), + X(0x072),X(0x06f),X(0x06c),X(0x069),X(0x066),X(0x063),X(0x060),X(0x05d), + X(0x05a),X(0x057),X(0x054),X(0x051),X(0x04e),X(0x04b),X(0x048),X(0x045), + X(0x042),X(0x03f),X(0x03c),X(0x039),X(0x036),X(0x033),X(0x030),X(0x02d), + X(0x02a),X(0x028),X(0x025),X(0x022),X(0x01f),X(0x01c),X(0x019),X(0x016), + X(0x014),X(0x011),X(0x00e),X(0x00b),X(0x008),X(0x006),X(0x003),X(0x000) + }; +#undef X + + // look up the fractional part, then shift by the whole + return s_power_table[input & 0xff] >> (input >> 8); +} + + +//------------------------------------------------- +// attenuation_increment - given a 6-bit ADSR +// rate value and a 3-bit stepping index, +// return a 4-bit increment to the attenutaion +// for this step (or for the attack case, the +// fractional scale factor to decrease by) +//------------------------------------------------- + +inline uint32_t attenuation_increment(uint32_t rate, uint32_t index) +{ + static uint32_t const s_increment_table[64] = + { + 0x00000000, 0x00000000, 0x10101010, 0x10101010, // 0-3 (0x00-0x03) + 0x10101010, 0x10101010, 0x11101110, 0x11101110, // 4-7 (0x04-0x07) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 8-11 (0x08-0x0B) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 12-15 (0x0C-0x0F) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 16-19 (0x10-0x13) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 20-23 (0x14-0x17) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 24-27 (0x18-0x1B) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 28-31 (0x1C-0x1F) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 32-35 (0x20-0x23) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 36-39 (0x24-0x27) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 40-43 (0x28-0x2B) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 44-47 (0x2C-0x2F) + 0x11111111, 0x21112111, 0x21212121, 0x22212221, // 48-51 (0x30-0x33) + 0x22222222, 0x42224222, 0x42424242, 0x44424442, // 52-55 (0x34-0x37) + 0x44444444, 0x84448444, 0x84848484, 0x88848884, // 56-59 (0x38-0x3B) + 0x88888888, 0x88888888, 0x88888888, 0x88888888 // 60-63 (0x3C-0x3F) + }; + return bitfield(s_increment_table[rate], 4*index, 4); +} + + +//------------------------------------------------- +// detune_adjustment - given a 5-bit key code +// value and a 3-bit detune parameter, return a +// 6-bit signed phase displacement; this table +// has been verified against Nuked's equations, +// but the equations are rather complicated, so +// we'll keep the simplicity of the table +//------------------------------------------------- + +inline int32_t detune_adjustment(uint32_t detune, uint32_t keycode) +{ + static uint8_t const s_detune_adjustment[32][4] = + { + { 0, 0, 1, 2 }, { 0, 0, 1, 2 }, { 0, 0, 1, 2 }, { 0, 0, 1, 2 }, + { 0, 1, 2, 2 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, + { 0, 1, 2, 4 }, { 0, 1, 3, 4 }, { 0, 1, 3, 4 }, { 0, 1, 3, 5 }, + { 0, 2, 4, 5 }, { 0, 2, 4, 6 }, { 0, 2, 4, 6 }, { 0, 2, 5, 7 }, + { 0, 2, 5, 8 }, { 0, 3, 6, 8 }, { 0, 3, 6, 9 }, { 0, 3, 7, 10 }, + { 0, 4, 8, 11 }, { 0, 4, 8, 12 }, { 0, 4, 9, 13 }, { 0, 5, 10, 14 }, + { 0, 5, 11, 16 }, { 0, 6, 12, 17 }, { 0, 6, 13, 19 }, { 0, 7, 14, 20 }, + { 0, 8, 16, 22 }, { 0, 8, 16, 22 }, { 0, 8, 16, 22 }, { 0, 8, 16, 22 } + }; + int32_t result = s_detune_adjustment[keycode][detune & 3]; + return bitfield(detune, 2) ? -result : result; +} + + +//------------------------------------------------- +// opm_key_code_to_phase_step - converts an +// OPM concatenated block (3 bits), keycode +// (4 bits) and key fraction (6 bits) to a 0.10 +// phase step, after applying the given delta; +// this applies to OPM and OPZ, so it lives here +// in a central location +//------------------------------------------------- + +inline uint32_t opm_key_code_to_phase_step(uint32_t block_freq, int32_t delta) +{ + // The phase step is essentially the fnum in OPN-speak. To compute this table, + // we used the standard formula for computing the frequency of a note, and + // then converted that frequency to fnum using the formula documented in the + // YM2608 manual. + // + // However, the YM2608 manual describes everything in terms of a nominal 8MHz + // clock, which produces an FM clock of: + // + // 8000000 / 24(operators) / 6(prescale) = 55555Hz FM clock + // + // Whereas the descriptions for the YM2151 use a nominal 3.579545MHz clock: + // + // 3579545 / 32(operators) / 2(prescale) = 55930Hz FM clock + // + // To correct for this, the YM2608 formula was adjusted to use a clock of + // 8053920Hz, giving this equation for the fnum: + // + // fnum = (double(144) * freq * (1 << 20)) / double(8053920) / 4; + // + // Unfortunately, the computed table differs in a few spots from the data + // verified from an actual chip. The table below comes from David Viens' + // analysis, used with his permission. + static const uint32_t s_phase_step[12*64] = + { + 41568,41600,41632,41664,41696,41728,41760,41792,41856,41888,41920,41952,42016,42048,42080,42112, + 42176,42208,42240,42272,42304,42336,42368,42400,42464,42496,42528,42560,42624,42656,42688,42720, + 42784,42816,42848,42880,42912,42944,42976,43008,43072,43104,43136,43168,43232,43264,43296,43328, + 43392,43424,43456,43488,43552,43584,43616,43648,43712,43744,43776,43808,43872,43904,43936,43968, + 44032,44064,44096,44128,44192,44224,44256,44288,44352,44384,44416,44448,44512,44544,44576,44608, + 44672,44704,44736,44768,44832,44864,44896,44928,44992,45024,45056,45088,45152,45184,45216,45248, + 45312,45344,45376,45408,45472,45504,45536,45568,45632,45664,45728,45760,45792,45824,45888,45920, + 45984,46016,46048,46080,46144,46176,46208,46240,46304,46336,46368,46400,46464,46496,46528,46560, + 46656,46688,46720,46752,46816,46848,46880,46912,46976,47008,47072,47104,47136,47168,47232,47264, + 47328,47360,47392,47424,47488,47520,47552,47584,47648,47680,47744,47776,47808,47840,47904,47936, + 48032,48064,48096,48128,48192,48224,48288,48320,48384,48416,48448,48480,48544,48576,48640,48672, + 48736,48768,48800,48832,48896,48928,48992,49024,49088,49120,49152,49184,49248,49280,49344,49376, + 49440,49472,49504,49536,49600,49632,49696,49728,49792,49824,49856,49888,49952,49984,50048,50080, + 50144,50176,50208,50240,50304,50336,50400,50432,50496,50528,50560,50592,50656,50688,50752,50784, + 50880,50912,50944,50976,51040,51072,51136,51168,51232,51264,51328,51360,51424,51456,51488,51520, + 51616,51648,51680,51712,51776,51808,51872,51904,51968,52000,52064,52096,52160,52192,52224,52256, + 52384,52416,52448,52480,52544,52576,52640,52672,52736,52768,52832,52864,52928,52960,52992,53024, + 53120,53152,53216,53248,53312,53344,53408,53440,53504,53536,53600,53632,53696,53728,53792,53824, + 53920,53952,54016,54048,54112,54144,54208,54240,54304,54336,54400,54432,54496,54528,54592,54624, + 54688,54720,54784,54816,54880,54912,54976,55008,55072,55104,55168,55200,55264,55296,55360,55392, + 55488,55520,55584,55616,55680,55712,55776,55808,55872,55936,55968,56032,56064,56128,56160,56224, + 56288,56320,56384,56416,56480,56512,56576,56608,56672,56736,56768,56832,56864,56928,56960,57024, + 57120,57152,57216,57248,57312,57376,57408,57472,57536,57568,57632,57664,57728,57792,57824,57888, + 57952,57984,58048,58080,58144,58208,58240,58304,58368,58400,58464,58496,58560,58624,58656,58720, + 58784,58816,58880,58912,58976,59040,59072,59136,59200,59232,59296,59328,59392,59456,59488,59552, + 59648,59680,59744,59776,59840,59904,59936,60000,60064,60128,60160,60224,60288,60320,60384,60416, + 60512,60544,60608,60640,60704,60768,60800,60864,60928,60992,61024,61088,61152,61184,61248,61280, + 61376,61408,61472,61536,61600,61632,61696,61760,61824,61856,61920,61984,62048,62080,62144,62208, + 62272,62304,62368,62432,62496,62528,62592,62656,62720,62752,62816,62880,62944,62976,63040,63104, + 63200,63232,63296,63360,63424,63456,63520,63584,63648,63680,63744,63808,63872,63904,63968,64032, + 64096,64128,64192,64256,64320,64352,64416,64480,64544,64608,64672,64704,64768,64832,64896,64928, + 65024,65056,65120,65184,65248,65312,65376,65408,65504,65536,65600,65664,65728,65792,65856,65888, + 65984,66016,66080,66144,66208,66272,66336,66368,66464,66496,66560,66624,66688,66752,66816,66848, + 66944,66976,67040,67104,67168,67232,67296,67328,67424,67456,67520,67584,67648,67712,67776,67808, + 67904,67936,68000,68064,68128,68192,68256,68288,68384,68448,68512,68544,68640,68672,68736,68800, + 68896,68928,68992,69056,69120,69184,69248,69280,69376,69440,69504,69536,69632,69664,69728,69792, + 69920,69952,70016,70080,70144,70208,70272,70304,70400,70464,70528,70560,70656,70688,70752,70816, + 70912,70976,71040,71104,71136,71232,71264,71360,71424,71488,71552,71616,71648,71744,71776,71872, + 71968,72032,72096,72160,72192,72288,72320,72416,72480,72544,72608,72672,72704,72800,72832,72928, + 72992,73056,73120,73184,73216,73312,73344,73440,73504,73568,73632,73696,73728,73824,73856,73952, + 74080,74144,74208,74272,74304,74400,74432,74528,74592,74656,74720,74784,74816,74912,74944,75040, + 75136,75200,75264,75328,75360,75456,75488,75584,75648,75712,75776,75840,75872,75968,76000,76096, + 76224,76288,76352,76416,76448,76544,76576,76672,76736,76800,76864,76928,77024,77120,77152,77248, + 77344,77408,77472,77536,77568,77664,77696,77792,77856,77920,77984,78048,78144,78240,78272,78368, + 78464,78528,78592,78656,78688,78784,78816,78912,78976,79040,79104,79168,79264,79360,79392,79488, + 79616,79680,79744,79808,79840,79936,79968,80064,80128,80192,80256,80320,80416,80512,80544,80640, + 80768,80832,80896,80960,80992,81088,81120,81216,81280,81344,81408,81472,81568,81664,81696,81792, + 81952,82016,82080,82144,82176,82272,82304,82400,82464,82528,82592,82656,82752,82848,82880,82976 + }; + + // extract the block (octave) first + uint32_t block = bitfield(block_freq, 10, 3); + + // the keycode (bits 6-9) is "gappy", mapping 12 values over 16 in each + // octave; to correct for this, we multiply the 4-bit value by 3/4 (or + // rather subtract 1/4); note that a (invalid) value of 15 will bleed into + // the next octave -- this is confirmed + uint32_t adjusted_code = bitfield(block_freq, 6, 4) - bitfield(block_freq, 8, 2); + + // now re-insert the 6-bit fraction + int32_t eff_freq = (adjusted_code << 6) | bitfield(block_freq, 0, 6); + + // now that the gaps are removed, add the delta + eff_freq += delta; + + // handle over/underflow by adjusting the block: + if (uint32_t(eff_freq) >= 768) + { + // minimum delta is -512 (PM), so we can only underflow by 1 octave + if (eff_freq < 0) + { + eff_freq += 768; + if (block-- == 0) + return s_phase_step[0] >> 7; + } + + // maximum delta is +512+608 (PM+detune), so we can overflow by up to 2 octaves + else + { + eff_freq -= 768; + if (eff_freq >= 768) + block++, eff_freq -= 768; + if (block++ >= 7) + return s_phase_step[767]; + } + } + + // look up the phase shift for the key code, then shift by octave + return s_phase_step[eff_freq] >> (block ^ 7); +} + + +//------------------------------------------------- +// opn_lfo_pm_phase_adjustment - given the 7 most +// significant frequency number bits, plus a 3-bit +// PM depth value and a signed 5-bit raw PM value, +// return a signed PM adjustment to the frequency; +// algorithm written to match Nuked behavior +//------------------------------------------------- + +inline int32_t opn_lfo_pm_phase_adjustment(uint32_t fnum_bits, uint32_t pm_sensitivity, int32_t lfo_raw_pm) +{ + // this table encodes 2 shift values to apply to the top 7 bits + // of fnum; it is effectively a cheap multiply by a constant + // value containing 0-2 bits + static uint8_t const s_lfo_pm_shifts[8][8] = + { + { 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 }, + { 0x77, 0x77, 0x77, 0x77, 0x72, 0x72, 0x72, 0x72 }, + { 0x77, 0x77, 0x77, 0x72, 0x72, 0x72, 0x17, 0x17 }, + { 0x77, 0x77, 0x72, 0x72, 0x17, 0x17, 0x12, 0x12 }, + { 0x77, 0x77, 0x72, 0x17, 0x17, 0x17, 0x12, 0x07 }, + { 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 }, + { 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 }, + { 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 } + }; + + // look up the relevant shifts + int32_t abs_pm = (lfo_raw_pm < 0) ? -lfo_raw_pm : lfo_raw_pm; + uint32_t const shifts = s_lfo_pm_shifts[pm_sensitivity][bitfield(abs_pm, 0, 3)]; + + // compute the adjustment + int32_t adjust = (fnum_bits >> bitfield(shifts, 0, 4)) + (fnum_bits >> bitfield(shifts, 4, 4)); + if (pm_sensitivity > 5) + adjust <<= pm_sensitivity - 5; + adjust >>= 2; + + // every 16 cycles it inverts sign + return (lfo_raw_pm < 0) ? -adjust : adjust; +} + + + +//********************************************************* +// FM OPERATOR +//********************************************************* + +//------------------------------------------------- +// fm_operator - constructor +//------------------------------------------------- + +template +fm_operator::fm_operator(fm_engine_base &owner, uint32_t opoffs) : + m_choffs(0), + m_opoffs(opoffs), + m_phase(0), + m_env_attenuation(0x3ff), + m_env_state(EG_RELEASE), + m_ssg_inverted(false), + m_key_state(0), + m_keyon_live(0), + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +template +void fm_operator::reset() +{ + // reset our data + m_phase = 0; + m_env_attenuation = 0x3ff; + m_env_state = EG_RELEASE; + m_ssg_inverted = 0; + m_key_state = 0; + m_keyon_live = 0; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +template +void fm_operator::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_phase); + state.save_restore(m_env_attenuation); + state.save_restore(m_env_state); + state.save_restore(m_ssg_inverted); + state.save_restore(m_key_state); + state.save_restore(m_keyon_live); +} + + +//------------------------------------------------- +// prepare - prepare for clocking +//------------------------------------------------- + +template +bool fm_operator::prepare() +{ + // cache the data + m_regs.cache_operator_data(m_choffs, m_opoffs, m_cache); + + // clock the key state + clock_keystate(uint32_t(m_keyon_live != 0)); + m_keyon_live &= ~(1 << KEYON_CSM); + + // we're active until we're quiet after the release + return (m_env_state != (RegisterType::EG_HAS_REVERB ? EG_REVERB : EG_RELEASE) || m_env_attenuation < EG_QUIET); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +template +void fm_operator::clock(uint32_t env_counter, int32_t lfo_raw_pm) +{ + // clock the SSG-EG state (OPN/OPNA) + if (m_regs.op_ssg_eg_enable(m_opoffs)) + clock_ssg_eg_state(); + else + m_ssg_inverted = false; + + // clock the envelope if on an envelope cycle; env_counter is a x.2 value + if (bitfield(env_counter, 0, 2) == 0) + clock_envelope(env_counter >> 2); + + // clock the phase + clock_phase(lfo_raw_pm); +} + + +//------------------------------------------------- +// compute_volume - compute the 14-bit signed +// volume of this operator, given a phase +// modulation and an AM LFO offset +//------------------------------------------------- + +template +int32_t fm_operator::compute_volume(uint32_t phase, uint32_t am_offset) const +{ + // the low 10 bits of phase represents a full 2*PI period over + // the full sin wave + + // early out if the envelope is effectively off + if (m_env_attenuation > EG_QUIET) + return 0; + + // get the absolute value of the sin, as attenuation, as a 4.8 fixed point value + uint32_t sin_attenuation = m_cache.waveform[phase & (RegisterType::WAVEFORM_LENGTH - 1)]; + + // get the attenuation from the evelope generator as a 4.6 value, shifted up to 4.8 + uint32_t env_attenuation = envelope_attenuation(am_offset) << 2; + + // combine into a 5.8 value, then convert from attenuation to 13-bit linear volume + int32_t result = attenuation_to_volume((sin_attenuation & 0x7fff) + env_attenuation); + + // negate if in the negative part of the sin wave (sign bit gives 14 bits) + return bitfield(sin_attenuation, 15) ? -result : result; +} + + +//------------------------------------------------- +// compute_noise_volume - compute the 14-bit +// signed noise volume of this operator, given a +// noise input value and an AM offset +//------------------------------------------------- + +template +int32_t fm_operator::compute_noise_volume(uint32_t am_offset) const +{ + // application manual says the logarithmic transform is not applied here, so we + // just use the raw envelope attenuation, inverted (since 0 attenuation should be + // maximum), and shift it up from a 10-bit value to an 11-bit value + int32_t result = (envelope_attenuation(am_offset) ^ 0x3ff) << 1; + + // QUESTION: is AM applied still? + + // negate based on the noise state + return bitfield(m_regs.noise_state(), 0) ? -result : result; +} + + +//------------------------------------------------- +// keyonoff - signal a key on/off event +//------------------------------------------------- + +template +void fm_operator::keyonoff(uint32_t on, keyon_type type) +{ + m_keyon_live = (m_keyon_live & ~(1 << int(type))) | (bitfield(on, 0) << int(type)); +} + + +//------------------------------------------------- +// start_attack - start the attack phase; called +// when a keyon happens or when an SSG-EG cycle +// is complete and restarts +//------------------------------------------------- + +template +void fm_operator::start_attack(bool is_restart) +{ + // don't change anything if already in attack state + if (m_env_state == EG_ATTACK) + return; + m_env_state = EG_ATTACK; + + // generally not inverted at start, except if SSG-EG is enabled and + // one of the inverted modes is specified; leave this alone on a + // restart, as it is managed by the clock_ssg_eg_state() code + if (RegisterType::EG_HAS_SSG && !is_restart) + m_ssg_inverted = m_regs.op_ssg_eg_enable(m_opoffs) & bitfield(m_regs.op_ssg_eg_mode(m_opoffs), 2); + + // reset the phase when we start an attack due to a key on + // (but not when due to an SSG-EG restart except in certain cases + // managed directly by the SSG-EG code) + if (!is_restart) + m_phase = 0; + + // if the attack rate >= 62 then immediately go to max attenuation + if (m_cache.eg_rate[EG_ATTACK] >= 62) + m_env_attenuation = 0; +} + + +//------------------------------------------------- +// start_release - start the release phase; +// called when a keyoff happens +//------------------------------------------------- + +template +void fm_operator::start_release() +{ + // don't change anything if already in release state + if (m_env_state >= EG_RELEASE) + return; + m_env_state = EG_RELEASE; + + // if attenuation if inverted due to SSG-EG, snap the inverted attenuation + // as the starting point + if (RegisterType::EG_HAS_SSG && m_ssg_inverted) + { + m_env_attenuation = (0x200 - m_env_attenuation) & 0x3ff; + m_ssg_inverted = false; + } +} + + +//------------------------------------------------- +// clock_keystate - clock the keystate to match +// the incoming keystate +//------------------------------------------------- + +template +void fm_operator::clock_keystate(uint32_t keystate) +{ + assert(keystate == 0 || keystate == 1); + + // has the key changed? + if ((keystate ^ m_key_state) != 0) + { + m_key_state = keystate; + + // if the key has turned on, start the attack + if (keystate != 0) + { + // OPLL has a DP ("depress"?) state to bring the volume + // down before starting the attack + if (RegisterType::EG_HAS_DEPRESS && m_env_attenuation < 0x200) + m_env_state = EG_DEPRESS; + else + start_attack(); + } + + // otherwise, start the release + else + start_release(); + } +} + + +//------------------------------------------------- +// clock_ssg_eg_state - clock the SSG-EG state; +// should only be called if SSG-EG is enabled +//------------------------------------------------- + +template +void fm_operator::clock_ssg_eg_state() +{ + // work only happens once the attenuation crosses above 0x200 + if (!bitfield(m_env_attenuation, 9)) + return; + + // 8 SSG-EG modes: + // 000: repeat normally + // 001: run once, hold low + // 010: repeat, alternating between inverted/non-inverted + // 011: run once, hold high + // 100: inverted repeat normally + // 101: inverted run once, hold low + // 110: inverted repeat, alternating between inverted/non-inverted + // 111: inverted run once, hold high + uint32_t mode = m_regs.op_ssg_eg_mode(m_opoffs); + + // hold modes (1/3/5/7) + if (bitfield(mode, 0)) + { + // set the inverted flag to the end state (0 for modes 1/7, 1 for modes 3/5) + m_ssg_inverted = bitfield(mode, 2) ^ bitfield(mode, 1); + + // if holding, force the attenuation to the expected value once we're + // past the attack phase + if (m_env_state != EG_ATTACK) + m_env_attenuation = m_ssg_inverted ? 0x200 : 0x3ff; + } + + // continuous modes (0/2/4/6) + else + { + // toggle invert in alternating mode (even in attack state) + m_ssg_inverted ^= bitfield(mode, 1); + + // restart attack if in decay/sustain states + if (m_env_state == EG_DECAY || m_env_state == EG_SUSTAIN) + start_attack(true); + + // phase is reset to 0 in modes 0/4 + if (bitfield(mode, 1) == 0) + m_phase = 0; + } + + // in all modes, once we hit release state, attenuation is forced to maximum + if (m_env_state == EG_RELEASE) + m_env_attenuation = 0x3ff; +} + + +//------------------------------------------------- +// clock_envelope - clock the envelope state +// according to the given count +//------------------------------------------------- + +template +void fm_operator::clock_envelope(uint32_t env_counter) +{ + // handle attack->decay transitions + if (m_env_state == EG_ATTACK && m_env_attenuation == 0) + m_env_state = EG_DECAY; + + // handle decay->sustain transitions; it is important to do this immediately + // after the attack->decay transition above in the event that the sustain level + // is set to 0 (in which case we will skip right to sustain without doing any + // decay); as an example where this can be heard, check the cymbals sound + // in channel 0 of shinobi's test mode sound #5 + if (m_env_state == EG_DECAY && m_env_attenuation >= m_cache.eg_sustain) + m_env_state = EG_SUSTAIN; + + // fetch the appropriate 6-bit rate value from the cache + uint32_t rate = m_cache.eg_rate[m_env_state]; + + // compute the rate shift value; this is the shift needed to + // apply to the env_counter such that it becomes a 5.11 fixed + // point number + uint32_t rate_shift = rate >> 2; + env_counter <<= rate_shift; + + // see if the fractional part is 0; if not, it's not time to clock + if (bitfield(env_counter, 0, 11) != 0) + return; + + // determine the increment based on the non-fractional part of env_counter + uint32_t relevant_bits = bitfield(env_counter, (rate_shift <= 11) ? 11 : rate_shift, 3); + uint32_t increment = attenuation_increment(rate, relevant_bits); + + // attack is the only one that increases + if (m_env_state == EG_ATTACK) + { + // glitch means that attack rates of 62/63 don't increment if + // changed after the initial key on (where they are handled + // specially); nukeykt confirms this happens on OPM, OPN, OPL/OPLL + // at least so assuming it is true for everyone + if (rate < 62) + m_env_attenuation += (~m_env_attenuation * increment) >> 4; + } + + // all other cases are similar + else + { + // non-SSG-EG cases just apply the increment + if (!m_regs.op_ssg_eg_enable(m_opoffs)) + m_env_attenuation += increment; + + // SSG-EG only applies if less than mid-point, and then at 4x + else if (m_env_attenuation < 0x200) + m_env_attenuation += 4 * increment; + + // clamp the final attenuation + if (m_env_attenuation >= 0x400) + m_env_attenuation = 0x3ff; + + // transition from depress to attack + if (RegisterType::EG_HAS_DEPRESS && m_env_state == EG_DEPRESS && m_env_attenuation >= 0x200) + start_attack(); + + // transition from release to reverb, should switch at -18dB + if (RegisterType::EG_HAS_REVERB && m_env_state == EG_RELEASE && m_env_attenuation >= 0xc0) + m_env_state = EG_REVERB; + } +} + + +//------------------------------------------------- +// clock_phase - clock the 10.10 phase value; the +// OPN version of the logic has been verified +// against the Nuked phase generator +//------------------------------------------------- + +template +void fm_operator::clock_phase(int32_t lfo_raw_pm) +{ + // read from the cache, or recalculate if PM active + uint32_t phase_step = m_cache.phase_step; + if (phase_step == opdata_cache::PHASE_STEP_DYNAMIC) + phase_step = m_regs.compute_phase_step(m_choffs, m_opoffs, m_cache, lfo_raw_pm); + + // finally apply the step to the current phase value + m_phase += phase_step; +} + + +//------------------------------------------------- +// envelope_attenuation - return the effective +// attenuation of the envelope +//------------------------------------------------- + +template +uint32_t fm_operator::envelope_attenuation(uint32_t am_offset) const +{ + uint32_t result = m_env_attenuation >> m_cache.eg_shift; + + // invert if necessary due to SSG-EG + if (RegisterType::EG_HAS_SSG && m_ssg_inverted) + result = (0x200 - result) & 0x3ff; + + // add in LFO AM modulation + if (m_regs.op_lfo_am_enable(m_opoffs)) + result += am_offset; + + // add in total level and KSL from the cache + result += m_cache.total_level; + + // clamp to max, apply shift, and return + return std::min(result, 0x3ff); +} + + + +//********************************************************* +// FM CHANNEL +//********************************************************* + +//------------------------------------------------- +// fm_channel - constructor +//------------------------------------------------- + +template +fm_channel::fm_channel(fm_engine_base &owner, uint32_t choffs) : + m_choffs(choffs), + m_feedback{ 0, 0 }, + m_feedback_in(0), + m_op{ nullptr, nullptr, nullptr, nullptr }, + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +template +void fm_channel::reset() +{ + // reset our data + m_feedback[0] = m_feedback[1] = 0; + m_feedback_in = 0; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +template +void fm_channel::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_feedback[0]); + state.save_restore(m_feedback[1]); + state.save_restore(m_feedback_in); +} + + +//------------------------------------------------- +// keyonoff - signal key on/off to our operators +//------------------------------------------------- + +template +void fm_channel::keyonoff(uint32_t states, keyon_type type, uint32_t chnum) +{ + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + if (m_op[opnum] != nullptr) + m_op[opnum]->keyonoff(bitfield(states, opnum), type); + + if (debug::LOG_KEYON_EVENTS && ((debug::GLOBAL_FM_CHANNEL_MASK >> chnum) & 1) != 0) + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + if (m_op[opnum] != nullptr) + debug::log_keyon("%c%s\n", bitfield(states, opnum) ? '+' : '-', m_regs.log_keyon(m_choffs, m_op[opnum]->opoffs()).c_str()); +} + + +//------------------------------------------------- +// prepare - prepare for clocking +//------------------------------------------------- + +template +bool fm_channel::prepare() +{ + uint32_t active_mask = 0; + + // prepare all operators and determine if they are active + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + if (m_op[opnum] != nullptr) + if (m_op[opnum]->prepare()) + active_mask |= 1 << opnum; + + return (active_mask != 0); +} + + +//------------------------------------------------- +// clock - master clock of all operators +//------------------------------------------------- + +template +void fm_channel::clock(uint32_t env_counter, int32_t lfo_raw_pm) +{ + // clock the feedback through + m_feedback[0] = m_feedback[1]; + m_feedback[1] = m_feedback_in; + + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + if (m_op[opnum] != nullptr) + m_op[opnum]->clock(env_counter, lfo_raw_pm); + +/* +useful temporary code for envelope debugging +if (m_choffs == 0x101) +{ + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + { + auto &op = *m_op[((opnum & 1) << 1) | ((opnum >> 1) & 1)]; + printf(" %c%03X%c%c ", + "PADSRV"[op.debug_eg_state()], + op.debug_eg_attenuation(), + op.debug_ssg_inverted() ? '-' : '+', + m_regs.op_ssg_eg_enable(op.opoffs()) ? '0' + m_regs.op_ssg_eg_mode(op.opoffs()) : ' '); + } +printf(" -- "); +} +*/ +} + + +//------------------------------------------------- +// output_2op - combine 4 operators according to +// the specified algorithm, returning a sum +// according to the rshift and clipmax parameters, +// which vary between different implementations +//------------------------------------------------- + +template +void fm_channel::output_2op(output_data &output, uint32_t rshift, int32_t clipmax) const +{ + // The first 2 operators should be populated + assert(m_op[0] != nullptr); + assert(m_op[1] != nullptr); + + // AM amount is the same across all operators; compute it once + uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); + + // operator 1 has optional self-feedback + int32_t opmod = 0; + uint32_t feedback = m_regs.ch_feedback(m_choffs); + if (feedback != 0) + opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback); + + // compute the 14-bit volume/value of operator 1 and update the feedback + int32_t op1value = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset); + + // now that the feedback has been computed, skip the rest if all volumes + // are clear; no need to do all this work for nothing + if (m_regs.ch_output_any(m_choffs) == 0) + return; + + // Algorithms for two-operator case: + // 0: O1 -> O2 -> out + // 1: (O1 + O2) -> out + int32_t result; + if (bitfield(m_regs.ch_algorithm(m_choffs), 0) == 0) + { + // some OPL chips use the previous sample for modulation instead of + // the current sample + opmod = (RegisterType::MODULATOR_DELAY ? m_feedback[1] : op1value) >> 1; + result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift; + } + else + { + result = (RegisterType::MODULATOR_DELAY ? m_feedback[1] : op1value) >> rshift; + result += m_op[1]->compute_volume(m_op[1]->phase(), am_offset) >> rshift; + int32_t clipmin = -clipmax - 1; + result = clamp(result, clipmin, clipmax); + } + + // add to the output + add_to_output(m_choffs, output, result); +} + + +//------------------------------------------------- +// output_4op - combine 4 operators according to +// the specified algorithm, returning a sum +// according to the rshift and clipmax parameters, +// which vary between different implementations +//------------------------------------------------- + +template +void fm_channel::output_4op(output_data &output, uint32_t rshift, int32_t clipmax) const +{ + // all 4 operators should be populated + assert(m_op[0] != nullptr); + assert(m_op[1] != nullptr); + assert(m_op[2] != nullptr); + assert(m_op[3] != nullptr); + + // AM amount is the same across all operators; compute it once + uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); + + // operator 1 has optional self-feedback + int32_t opmod = 0; + uint32_t feedback = m_regs.ch_feedback(m_choffs); + if (feedback != 0) + opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback); + + // compute the 14-bit volume/value of operator 1 and update the feedback + int32_t op1value = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset); + + // now that the feedback has been computed, skip the rest if all volumes + // are clear; no need to do all this work for nothing + if (m_regs.ch_output_any(m_choffs) == 0) + return; + + // OPM/OPN offer 8 different connection algorithms for 4 operators, + // and OPL3 offers 4 more, which we designate here as 8-11. + // + // The operators are computed in order, with the inputs pulled from + // an array of values (opout) that is populated as we go: + // 0 = 0 + // 1 = O1 + // 2 = O2 + // 3 = O3 + // 4 = (O4) + // 5 = O1+O2 + // 6 = O1+O3 + // 7 = O2+O3 + // + // The s_algorithm_ops table describes the inputs and outputs of each + // algorithm as follows: + // + // ---------x use opout[x] as operator 2 input + // ------xxx- use opout[x] as operator 3 input + // ---xxx---- use opout[x] as operator 4 input + // --x------- include opout[1] in final sum + // -x-------- include opout[2] in final sum + // x--------- include opout[3] in final sum + #define ALGORITHM(op2in, op3in, op4in, op1out, op2out, op3out) \ + ((op2in) | ((op3in) << 1) | ((op4in) << 4) | ((op1out) << 7) | ((op2out) << 8) | ((op3out) << 9)) + static uint16_t const s_algorithm_ops[8+4] = + { + ALGORITHM(1,2,3, 0,0,0), // 0: O1 -> O2 -> O3 -> O4 -> out (O4) + ALGORITHM(0,5,3, 0,0,0), // 1: (O1 + O2) -> O3 -> O4 -> out (O4) + ALGORITHM(0,2,6, 0,0,0), // 2: (O1 + (O2 -> O3)) -> O4 -> out (O4) + ALGORITHM(1,0,7, 0,0,0), // 3: ((O1 -> O2) + O3) -> O4 -> out (O4) + ALGORITHM(1,0,3, 0,1,0), // 4: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4) + ALGORITHM(1,1,1, 0,1,1), // 5: ((O1 -> O2) + (O1 -> O3) + (O1 -> O4)) -> out (O2+O3+O4) + ALGORITHM(1,0,0, 0,1,1), // 6: ((O1 -> O2) + O3 + O4) -> out (O2+O3+O4) + ALGORITHM(0,0,0, 1,1,1), // 7: (O1 + O2 + O3 + O4) -> out (O1+O2+O3+O4) + ALGORITHM(1,2,3, 0,0,0), // 8: O1 -> O2 -> O3 -> O4 -> out (O4) [same as 0] + ALGORITHM(0,2,3, 1,0,0), // 9: (O1 + (O2 -> O3 -> O4)) -> out (O1+O4) [unique] + ALGORITHM(1,0,3, 0,1,0), // 10: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4) [same as 4] + ALGORITHM(0,2,0, 1,0,1) // 11: (O1 + (O2 -> O3) + O4) -> out (O1+O3+O4) [unique] + }; + uint32_t algorithm_ops = s_algorithm_ops[m_regs.ch_algorithm(m_choffs)]; + + // populate the opout table + int16_t opout[8]; + opout[0] = 0; + opout[1] = op1value; + + // compute the 14-bit volume/value of operator 2 + opmod = opout[bitfield(algorithm_ops, 0, 1)] >> 1; + opout[2] = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset); + opout[5] = opout[1] + opout[2]; + + // compute the 14-bit volume/value of operator 3 + opmod = opout[bitfield(algorithm_ops, 1, 3)] >> 1; + opout[3] = m_op[2]->compute_volume(m_op[2]->phase() + opmod, am_offset); + opout[6] = opout[1] + opout[3]; + opout[7] = opout[2] + opout[3]; + + // compute the 14-bit volume/value of operator 4; this could be a noise + // value on the OPM; all algorithms consume OP4 output at a minimum + int32_t result; + if (m_regs.noise_enable() && m_choffs == 7) + result = m_op[3]->compute_noise_volume(am_offset); + else + { + opmod = opout[bitfield(algorithm_ops, 4, 3)] >> 1; + result = m_op[3]->compute_volume(m_op[3]->phase() + opmod, am_offset); + } + result >>= rshift; + + // optionally add OP1, OP2, OP3 + int32_t clipmin = -clipmax - 1; + if (bitfield(algorithm_ops, 7) != 0) + result = clamp(result + (opout[1] >> rshift), clipmin, clipmax); + if (bitfield(algorithm_ops, 8) != 0) + result = clamp(result + (opout[2] >> rshift), clipmin, clipmax); + if (bitfield(algorithm_ops, 9) != 0) + result = clamp(result + (opout[3] >> rshift), clipmin, clipmax); + + // add to the output + add_to_output(m_choffs, output, result); +} + + +//------------------------------------------------- +// output_rhythm_ch6 - special case output +// computation for OPL channel 6 in rhythm mode, +// which outputs a Bass Drum instrument +//------------------------------------------------- + +template +void fm_channel::output_rhythm_ch6(output_data &output, uint32_t rshift, int32_t clipmax) const +{ + (void)clipmax; + // AM amount is the same across all operators; compute it once + uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); + + // Bass Drum: this uses operators 12 and 15 (i.e., channel 6) + // in an almost-normal way, except that if the algorithm is 1, + // the first operator is ignored instead of added in + + // operator 1 has optional self-feedback + int32_t opmod = 0; + uint32_t feedback = m_regs.ch_feedback(m_choffs); + if (feedback != 0) + opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback); + + // compute the 14-bit volume/value of operator 1 and update the feedback + int32_t opout1 = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset); + + // compute the 14-bit volume/value of operator 2, which is the result + opmod = bitfield(m_regs.ch_algorithm(m_choffs), 0) ? 0 : (opout1 >> 1); + int32_t result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift; + + // add to the output + add_to_output(m_choffs, output, result * 2); +} + + +//------------------------------------------------- +// output_rhythm_ch7 - special case output +// computation for OPL channel 7 in rhythm mode, +// which outputs High Hat and Snare Drum +// instruments +//------------------------------------------------- + +template +void fm_channel::output_rhythm_ch7(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const +{ + // AM amount is the same across all operators; compute it once + uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); + uint32_t noise_state = bitfield(m_regs.noise_state(), 0); + + // High Hat: this uses the envelope from operator 13 (channel 7), + // and a combination of noise and the operator 13/17 phase select + // to compute the phase + uint32_t phase = (phase_select << 9) | (0xd0 >> (2 * (noise_state ^ phase_select))); + int32_t result = m_op[0]->compute_volume(phase, am_offset) >> rshift; + + // Snare Drum: this uses the envelope from operator 16 (channel 7), + // and a combination of noise and operator 13 phase to pick a phase + uint32_t op13phase = m_op[0]->phase(); + phase = (0x100 << bitfield(op13phase, 8)) ^ (noise_state << 8); + result += m_op[1]->compute_volume(phase, am_offset) >> rshift; + result = clamp(result, -clipmax - 1, clipmax); + + // add to the output + add_to_output(m_choffs, output, result * 2); +} + + +//------------------------------------------------- +// output_rhythm_ch8 - special case output +// computation for OPL channel 8 in rhythm mode, +// which outputs Tom Tom and Top Cymbal instruments +//------------------------------------------------- + +template +void fm_channel::output_rhythm_ch8(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const +{ + // AM amount is the same across all operators; compute it once + uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); + + // Tom Tom: this is just a single operator processed normally + int32_t result = m_op[0]->compute_volume(m_op[0]->phase(), am_offset) >> rshift; + + // Top Cymbal: this uses the envelope from operator 17 (channel 8), + // and the operator 13/17 phase select to compute the phase + uint32_t phase = 0x100 | (phase_select << 9); + result += m_op[1]->compute_volume(phase, am_offset) >> rshift; + result = clamp(result, -clipmax - 1, clipmax); + + // add to the output + add_to_output(m_choffs, output, result * 2); +} + + + +//********************************************************* +// FM ENGINE BASE +//********************************************************* + +//------------------------------------------------- +// fm_engine_base - constructor +//------------------------------------------------- + +template +fm_engine_base::fm_engine_base(ymfm_interface &intf) : + m_intf(intf), + m_env_counter(0), + m_status(0), + m_clock_prescale(RegisterType::DEFAULT_PRESCALE), + m_irq_mask(STATUS_TIMERA | STATUS_TIMERB), + m_irq_state(0), + m_timer_running{0,0}, + m_total_clocks(0), + m_active_channels(ALL_CHANNELS), + m_modified_channels(ALL_CHANNELS), + m_prepare_count(0) +{ + // inform the interface of their engine + m_intf.m_engine = this; + + // create the channels + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum] = std::make_unique>(*this, RegisterType::channel_offset(chnum)); + + // create the operators + for (uint32_t opnum = 0; opnum < OPERATORS; opnum++) + m_operator[opnum] = std::make_unique>(*this, RegisterType::operator_offset(opnum)); + +#if (YMFM_DEBUG_LOG_WAVFILES) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + m_wavfile[chnum].set_index(chnum); +#endif + + // do the initial operator assignment + assign_operators(); +} + + +//------------------------------------------------- +// reset - reset the overall state +//------------------------------------------------- + +template +void fm_engine_base::reset() +{ + // reset all status bits + set_reset_status(0, 0xff); + + // register type-specific initialization + m_regs.reset(); + + // explicitly write to the mode register since it has side-effects + // QUESTION: old cores initialize this to 0x30 -- who is right? + write(RegisterType::REG_MODE, 0); + + // reset the channels + for (auto &chan : m_channel) + chan->reset(); + + // reset the operators + for (auto &op : m_operator) + op->reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +template +void fm_engine_base::save_restore(ymfm_saved_state &state) +{ + // save our data + state.save_restore(m_env_counter); + state.save_restore(m_status); + state.save_restore(m_clock_prescale); + state.save_restore(m_irq_mask); + state.save_restore(m_irq_state); + state.save_restore(m_timer_running[0]); + state.save_restore(m_timer_running[1]); + state.save_restore(m_total_clocks); + + // save the register/family data + m_regs.save_restore(state); + + // save channel data + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum]->save_restore(state); + + // save operator data + for (uint32_t opnum = 0; opnum < OPERATORS; opnum++) + m_operator[opnum]->save_restore(state); + + // invalidate any caches + invalidate_caches(); +} + + +//------------------------------------------------- +// clock - iterate over all channels, clocking +// them forward one step +//------------------------------------------------- + +template +uint32_t fm_engine_base::clock(uint32_t chanmask) +{ + // update the clock counter + m_total_clocks++; + + // if something was modified, prepare + // also prepare every 4k samples to catch ending notes + if (m_modified_channels != 0 || m_prepare_count++ >= 4096) + { + // reassign operators to channels if dynamic + if (RegisterType::DYNAMIC_OPS) + assign_operators(); + + // call each channel to prepare + m_active_channels = 0; + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + if (m_channel[chnum]->prepare()) + m_active_channels |= 1 << chnum; + + // reset the modified channels and prepare count + m_modified_channels = m_prepare_count = 0; + } + + // if the envelope clock divider is 1, just increment by 4; + // otherwise, increment by 1 and manually wrap when we reach the divide count + if (RegisterType::EG_CLOCK_DIVIDER == 1) + m_env_counter += 4; + else if (bitfield(++m_env_counter, 0, 2) == RegisterType::EG_CLOCK_DIVIDER) + m_env_counter += 4 - RegisterType::EG_CLOCK_DIVIDER; + + // clock the noise generator + int32_t lfo_raw_pm = m_regs.clock_noise_and_lfo(); + + // now update the state of all the channels and operators + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->clock(m_env_counter, lfo_raw_pm); + + // return the envelope counter as it is used to clock ADPCM-A + return m_env_counter; +} + + +//------------------------------------------------- +// output - compute a sum over the relevant +// channels +//------------------------------------------------- + +template +void fm_engine_base::output(output_data &output, uint32_t rshift, int32_t clipmax, uint32_t chanmask) const +{ + // mask out some channels for debug purposes + chanmask &= debug::GLOBAL_FM_CHANNEL_MASK; + + // mask out inactive channels + if (!YMFM_DEBUG_LOG_WAVFILES) + chanmask &= m_active_channels; + + // handle the rhythm case, where some of the operators are dedicated + // to percussion (this is an OPL-specific feature) + if (m_regs.rhythm_enable()) + { + // we don't support the OPM noise channel here; ensure it is off + assert(m_regs.noise_enable() == 0); + + // precompute the operator 13+17 phase selection value + uint32_t op13phase = m_operator[13]->phase(); + uint32_t op17phase = m_operator[17]->phase(); + uint32_t phase_select = (bitfield(op13phase, 2) ^ bitfield(op13phase, 7)) | bitfield(op13phase, 3) | (bitfield(op17phase, 5) ^ bitfield(op17phase, 3)); + + // sum over all the desired channels + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + { +#if (YMFM_DEBUG_LOG_WAVFILES) + auto reference = output; +#endif + if (chnum == 6) + m_channel[chnum]->output_rhythm_ch6(output, rshift, clipmax); + else if (chnum == 7) + m_channel[chnum]->output_rhythm_ch7(phase_select, output, rshift, clipmax); + else if (chnum == 8) + m_channel[chnum]->output_rhythm_ch8(phase_select, output, rshift, clipmax); + else if (m_channel[chnum]->is4op()) + m_channel[chnum]->output_4op(output, rshift, clipmax); + else + m_channel[chnum]->output_2op(output, rshift, clipmax); +#if (YMFM_DEBUG_LOG_WAVFILES) + m_wavfile[chnum].add(output, reference); +#endif + } + } + else + { + // sum over all the desired channels + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + { +#if (YMFM_DEBUG_LOG_WAVFILES) + auto reference = output; +#endif + if (m_channel[chnum]->is4op()) + m_channel[chnum]->output_4op(output, rshift, clipmax); + else + m_channel[chnum]->output_2op(output, rshift, clipmax); +#if (YMFM_DEBUG_LOG_WAVFILES) + m_wavfile[chnum].add(output, reference); +#endif + } + } +} + + +//------------------------------------------------- +// write - handle writes to the OPN registers +//------------------------------------------------- + +template +void fm_engine_base::write(uint16_t regnum, uint8_t data) +{ + debug::log_fm_write("%03X = %02X\n", regnum, data); + + // special case: writes to the mode register can impact IRQs; + // schedule these writes to ensure ordering with timers + if (regnum == RegisterType::REG_MODE) + { + m_intf.ymfm_sync_mode_write(data); + return; + } + + // for now just mark all channels as modified + m_modified_channels = ALL_CHANNELS; + + // most writes are passive, consumed only when needed + uint32_t keyon_channel; + uint32_t keyon_opmask; + if (m_regs.write(regnum, data, keyon_channel, keyon_opmask)) + { + // handle writes to the keyon register(s) + if (keyon_channel < CHANNELS) + { + // normal channel on/off + m_channel[keyon_channel]->keyonoff(keyon_opmask, KEYON_NORMAL, keyon_channel); + } + else if (CHANNELS >= 9 && keyon_channel == RegisterType::RHYTHM_CHANNEL) + { + // special case for the OPL rhythm channels + m_channel[6]->keyonoff(bitfield(keyon_opmask, 4) ? 3 : 0, KEYON_RHYTHM, 6); + m_channel[7]->keyonoff(bitfield(keyon_opmask, 0) | (bitfield(keyon_opmask, 3) << 1), KEYON_RHYTHM, 7); + m_channel[8]->keyonoff(bitfield(keyon_opmask, 2) | (bitfield(keyon_opmask, 1) << 1), KEYON_RHYTHM, 8); + } + } +} + + +//------------------------------------------------- +// status - return the current state of the +// status flags +//------------------------------------------------- + +template +uint8_t fm_engine_base::status() const +{ + return m_status & ~STATUS_BUSY & ~m_regs.status_mask(); +} + + +//------------------------------------------------- +// assign_operators - get the current mapping of +// operators to channels and assign them all +//------------------------------------------------- + +template +void fm_engine_base::assign_operators() +{ + typename RegisterType::operator_mapping map; + m_regs.operator_map(map); + + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + for (uint32_t index = 0; index < 4; index++) + { + uint32_t opnum = bitfield(map.chan[chnum], 8 * index, 8); + m_channel[chnum]->assign(index, (opnum == 0xff) ? nullptr : m_operator[opnum].get()); + } +} + + +//------------------------------------------------- +// update_timer - update the state of the given +// timer +//------------------------------------------------- + +template +void fm_engine_base::update_timer(uint32_t tnum, uint32_t enable, int32_t delta_clocks) +{ + if (tnum >= 2) + return; + + // if the timer is live, but not currently enabled, set the timer + if (enable && !m_timer_running[tnum]) + { + // period comes from the registers, and is different for each + uint32_t period = (tnum == 0) ? (1024 - m_regs.timer_a_value()) : 16 * (256 - m_regs.timer_b_value()); + + // caller can also specify a delta to account for other effects + period += delta_clocks; + + // reset it + m_intf.ymfm_set_timer(tnum, period * OPERATORS * m_clock_prescale); + m_timer_running[tnum] = 1; + } + + // if the timer is not live, ensure it is not enabled + else if (!enable) + { + m_intf.ymfm_set_timer(tnum, -1); + m_timer_running[tnum] = 0; + } +} + + +//------------------------------------------------- +// engine_timer_expired - timer has expired - signal +// status and possibly IRQs +//------------------------------------------------- + +template +void fm_engine_base::engine_timer_expired(uint32_t tnum) +{ + if (tnum >= 2) + return; + + // update status + if (tnum == 0 && m_regs.enable_timer_a()) + set_reset_status(STATUS_TIMERA, 0); + else if (tnum == 1 && m_regs.enable_timer_b()) + set_reset_status(STATUS_TIMERB, 0); + + // if timer A fired in CSM mode, trigger CSM on all relevant channels + if (tnum == 0 && m_regs.csm()) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(RegisterType::CSM_TRIGGER_MASK, chnum)) + { + m_channel[chnum]->keyonoff(1, KEYON_CSM, chnum); + m_modified_channels |= 1 << chnum; + } + + // reset + m_timer_running[tnum] = false; + update_timer(tnum, 1, 0); +} + + +//------------------------------------------------- +// check_interrupts - check the interrupt sources +// for interrupts +//------------------------------------------------- + +template +void fm_engine_base::engine_check_interrupts() +{ + // update the state + uint8_t old_state = m_irq_state; + m_irq_state = ((m_status & m_irq_mask & ~m_regs.status_mask()) != 0); + + // set the IRQ status bit + if (m_irq_state) + m_status |= STATUS_IRQ; + else + m_status &= ~STATUS_IRQ; + + // if changed, signal the new state + if (old_state != m_irq_state) + m_intf.ymfm_update_irq(m_irq_state ? true : false); +} + + +//------------------------------------------------- +// engine_mode_write - handle a mode register write +// via timer callback +//------------------------------------------------- + +template +void fm_engine_base::engine_mode_write(uint8_t data) +{ + // mark all channels as modified + m_modified_channels = ALL_CHANNELS; + + // actually write the mode register now + uint32_t dummy1, dummy2; + m_regs.write(RegisterType::REG_MODE, data, dummy1, dummy2); + + // reset IRQ status -- when written, all other bits are ignored + // QUESTION: should this maybe just reset the IRQ bit and not all the bits? + // That is, check_interrupts would only set, this would only clear? + if (m_regs.irq_reset()) + set_reset_status(0, 0x78); + else + { + // reset timer status + uint8_t reset_mask = 0; + if (m_regs.reset_timer_b()) + reset_mask |= RegisterType::STATUS_TIMERB; + if (m_regs.reset_timer_a()) + reset_mask |= RegisterType::STATUS_TIMERA; + set_reset_status(0, reset_mask); + + // load timers; note that timer B gets a small negative adjustment because + // the *16 multiplier is free-running, so the first tick of the clock + // is a bit shorter + update_timer(1, m_regs.load_timer_b(), -(m_total_clocks & 15)); + update_timer(0, m_regs.load_timer_a(), 0); + } +} + +} diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_misc.cpp b/thirdparty/adlmidi/chips/ymfm/ymfm_misc.cpp new file mode 100644 index 000000000..fd0575f55 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_misc.cpp @@ -0,0 +1,175 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +#include "ymfm_misc.h" + +namespace ymfm +{ + +//********************************************************* +// YM2149 +//********************************************************* + +//------------------------------------------------- +// ym2149 - constructor +//------------------------------------------------- + +ym2149::ym2149(ymfm_interface &intf) : + m_address(0), + m_ssg(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ym2149::reset() +{ + // reset the engines + m_ssg.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ym2149::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_ssg.save_restore(state); +} + + +//------------------------------------------------- +// read_data - read the data register +//------------------------------------------------- + +uint8_t ym2149::read_data() +{ + return m_ssg.read(m_address & 0x0f); +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ym2149::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 3) // BC2,BC1 + { + case 0: // inactive + break; + + case 1: // address + break; + + case 2: // inactive + break; + + case 3: // read + result = read_data(); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ym2149::write_address(uint8_t data) +{ + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym2149::write_data(uint8_t data) +{ + m_ssg.write(m_address & 0x0f, data); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym2149::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) // BC2,BC1 + { + case 0: // address + write_address(data); + break; + + case 1: // inactive + break; + + case 2: // write + write_data(data); + break; + + case 3: // address + write_address(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of SSG sound +//------------------------------------------------- + +void ym2149::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the SSG + m_ssg.clock(); + + // YM2149 keeps the three SSG outputs independent + m_ssg.output(*output); + } +} + +} diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_misc.h b/thirdparty/adlmidi/chips/ymfm/ymfm_misc.h new file mode 100644 index 000000000..628d128f6 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_misc.h @@ -0,0 +1,93 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_MISC_H +#define YMFM_MISC_H + +#pragma once + +#include "ymfm.h" +#include "ymfm_adpcm.h" +#include "ymfm_ssg.h" + +namespace ymfm +{ + +//********************************************************* +// SSG IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ym2149 + +// ym2149 is just an SSG with no FM part, but we expose FM-like parts so that it +// integrates smoothly with everything else; they just don't do anything +class ym2149 +{ +public: + static constexpr uint32_t OUTPUTS = ssg_engine::OUTPUTS; + static constexpr uint32_t SSG_OUTPUTS = ssg_engine::OUTPUTS; + using output_data = ymfm_output; + + // constructor + ym2149(ymfm_interface &intf); + + // configuration + void ssg_override(ssg_override &intf) { m_ssg.override(intf); } + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return input_clock / ssg_engine::CLOCK_DIVIDER / 8; } + + // read access + uint8_t read_data(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate one sample of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint8_t m_address; // address register + ssg_engine m_ssg; // SSG engine +}; + +} + +#endif // YMFM_MISC_H diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_opl.cpp b/thirdparty/adlmidi/chips/ymfm/ymfm_opl.cpp new file mode 100644 index 000000000..ab9934d04 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_opl.cpp @@ -0,0 +1,2223 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +#include "ymfm_opl.h" +#include "ymfm_fm.ipp" + +namespace ymfm +{ + +//------------------------------------------------- +// opl_key_scale_atten - converts an +// OPL concatenated block (3 bits) and fnum +// (10 bits) into an attenuation offset; values +// here are for 6dB/octave, in 0.75dB units +// (matching total level LSB) +//------------------------------------------------- + +inline uint32_t opl_key_scale_atten(uint32_t block, uint32_t fnum_4msb) +{ + // this table uses the top 4 bits of FNUM and are the maximal values + // (for when block == 7). Values for other blocks can be computed by + // subtracting 8 for each block below 7. + static uint8_t const fnum_to_atten[16] = { 0,24,32,37,40,43,45,47,48,50,51,52,53,54,55,56 }; + int32_t result = fnum_to_atten[fnum_4msb] - 8 * (block ^ 7); + return std::max(0, result); +} + + +//********************************************************* +// OPL REGISTERS +//********************************************************* + +//------------------------------------------------- +// opl_registers_base - constructor +//------------------------------------------------- + +template +opl_registers_base::opl_registers_base() : + m_lfo_am_counter(0), + m_lfo_pm_counter(0), + m_noise_lfsr(1), + m_lfo_am(0) +{ + // create these pointers to appease overzealous compilers checking array + // bounds in unreachable code (looking at you, clang) + uint16_t *wf0 = &m_waveform[0][0]; + uint16_t *wf1 = &m_waveform[1 % WAVEFORMS][0]; + uint16_t *wf2 = &m_waveform[2 % WAVEFORMS][0]; + uint16_t *wf3 = &m_waveform[3 % WAVEFORMS][0]; + uint16_t *wf4 = &m_waveform[4 % WAVEFORMS][0]; + uint16_t *wf5 = &m_waveform[5 % WAVEFORMS][0]; + uint16_t *wf6 = &m_waveform[6 % WAVEFORMS][0]; + uint16_t *wf7 = &m_waveform[7 % WAVEFORMS][0]; + + // create the waveforms + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) + wf0[index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15); + + if (WAVEFORMS >= 4) + { + uint16_t zeroval = wf0[0]; + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) + { + wf1[index] = bitfield(index, 9) ? zeroval : wf0[index]; + wf2[index] = wf0[index] & 0x7fff; + wf3[index] = bitfield(index, 8) ? zeroval : (wf0[index] & 0x7fff); + if (WAVEFORMS >= 8) + { + wf4[index] = bitfield(index, 9) ? zeroval : wf0[index * 2]; + wf5[index] = bitfield(index, 9) ? zeroval : wf0[(index * 2) & 0x1ff]; + wf6[index] = bitfield(index, 9) << 15; + wf7[index] = (bitfield(index, 9) ? (index ^ 0x13ff) : index) << 3; + } + } + } + + // OPL3/OPL4 have dynamic operators, so initialize the fourop_enable value here + // since operator_map() is called right away, prior to reset() + if (Revision > 2) + m_regdata[0x104 % REGISTERS] = 0; +} + + +//------------------------------------------------- +// reset - reset to initial state +//------------------------------------------------- + +template +void opl_registers_base::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +template +void opl_registers_base::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_lfo_am_counter); + state.save_restore(m_lfo_pm_counter); + state.save_restore(m_lfo_am); + state.save_restore(m_noise_lfsr); + state.save_restore(m_regdata); +} + + +//------------------------------------------------- +// operator_map - return an array of operator +// indices for each channel; for OPL this is fixed +//------------------------------------------------- + +template +void opl_registers_base::operator_map(operator_mapping &dest) const +{ + if (Revision <= 2) + { + // OPL/OPL2 has a fixed map, all 2 operators + static const operator_mapping s_fixed_map = + { { + operator_list( 0, 3 ), // Channel 0 operators + operator_list( 1, 4 ), // Channel 1 operators + operator_list( 2, 5 ), // Channel 2 operators + operator_list( 6, 9 ), // Channel 3 operators + operator_list( 7, 10 ), // Channel 4 operators + operator_list( 8, 11 ), // Channel 5 operators + operator_list( 12, 15 ), // Channel 6 operators + operator_list( 13, 16 ), // Channel 7 operators + operator_list( 14, 17 ), // Channel 8 operators + } }; + dest = s_fixed_map; + } + else + { + // OPL3/OPL4 can be configured for 2 or 4 operators + uint32_t fourop = fourop_enable(); + + dest.chan[ 0] = bitfield(fourop, 0) ? operator_list( 0, 3, 6, 9 ) : operator_list( 0, 3 ); + dest.chan[ 1] = bitfield(fourop, 1) ? operator_list( 1, 4, 7, 10 ) : operator_list( 1, 4 ); + dest.chan[ 2] = bitfield(fourop, 2) ? operator_list( 2, 5, 8, 11 ) : operator_list( 2, 5 ); + dest.chan[ 3] = bitfield(fourop, 0) ? operator_list() : operator_list( 6, 9 ); + dest.chan[ 4] = bitfield(fourop, 1) ? operator_list() : operator_list( 7, 10 ); + dest.chan[ 5] = bitfield(fourop, 2) ? operator_list() : operator_list( 8, 11 ); + dest.chan[ 6] = operator_list( 12, 15 ); + dest.chan[ 7] = operator_list( 13, 16 ); + dest.chan[ 8] = operator_list( 14, 17 ); + + dest.chan[ 9] = bitfield(fourop, 3) ? operator_list( 18, 21, 24, 27 ) : operator_list( 18, 21 ); + dest.chan[10] = bitfield(fourop, 4) ? operator_list( 19, 22, 25, 28 ) : operator_list( 19, 22 ); + dest.chan[11] = bitfield(fourop, 5) ? operator_list( 20, 23, 26, 29 ) : operator_list( 20, 23 ); + dest.chan[12] = bitfield(fourop, 3) ? operator_list() : operator_list( 24, 27 ); + dest.chan[13] = bitfield(fourop, 4) ? operator_list() : operator_list( 25, 28 ); + dest.chan[14] = bitfield(fourop, 5) ? operator_list() : operator_list( 26, 29 ); + dest.chan[15] = operator_list( 30, 33 ); + dest.chan[16] = operator_list( 31, 34 ); + dest.chan[17] = operator_list( 32, 35 ); + } +} + + +//------------------------------------------------- +// write - handle writes to the register array +//------------------------------------------------- + +template +bool opl_registers_base::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask) +{ + assert(index < REGISTERS); + + // writes to the mode register with high bit set ignore the low bits + if (index == REG_MODE && bitfield(data, 7) != 0) + m_regdata[index] |= 0x80; + else + m_regdata[index] = data; + + // handle writes to the rhythm keyons + if (index == 0xbd) + { + channel = RHYTHM_CHANNEL; + opmask = bitfield(data, 5) ? bitfield(data, 0, 5) : 0; + return true; + } + + // handle writes to the channel keyons + if ((index & 0xf0) == 0xb0) + { + channel = index & 0x0f; + if (channel < 9) + { + if (IsOpl3Plus) + channel += 9 * bitfield(index, 8); + opmask = bitfield(data, 5) ? 15 : 0; + return true; + } + } + return false; +} + + +//------------------------------------------------- +// clock_noise_and_lfo - clock the noise and LFO, +// handling clock division, depth, and waveform +// computations +//------------------------------------------------- + +static int32_t opl_clock_noise_and_lfo(uint32_t &noise_lfsr, uint16_t &lfo_am_counter, uint16_t &lfo_pm_counter, uint8_t &lfo_am, uint32_t am_depth, uint32_t pm_depth) +{ + // OPL has a 23-bit noise generator for the rhythm section, running at + // a constant rate, used only for percussion input + noise_lfsr <<= 1; + noise_lfsr |= bitfield(noise_lfsr, 23) ^ bitfield(noise_lfsr, 9) ^ bitfield(noise_lfsr, 8) ^ bitfield(noise_lfsr, 1); + + // OPL has two fixed-frequency LFOs, one for AM, one for PM + + // the AM LFO has 210*64 steps; at a nominal 50kHz output, + // this equates to a period of 50000/(210*64) = 3.72Hz + uint32_t am_counter = lfo_am_counter++; + if (am_counter >= 210*64 - 1) + lfo_am_counter = 0; + + // low 8 bits are fractional; depth 0 is divided by 2, while depth 1 is times 2 + int shift = 9 - 2 * am_depth; + + // AM value is the upper bits of the value, inverted across the midpoint + // to produce a triangle + lfo_am = ((am_counter < 105*64) ? am_counter : (210*64+63 - am_counter)) >> shift; + + // the PM LFO has 8192 steps, or a nominal period of 6.1Hz + uint32_t pm_counter = lfo_pm_counter++; + + // PM LFO is broken into 8 chunks, each lasting 1024 steps; the PM value + // depends on the upper bits of FNUM, so this value is a fraction and + // sign to apply to that value, as a 1.3 value + static int8_t const pm_scale[8] = { 8, 4, 0, -4, -8, -4, 0, 4 }; + return pm_scale[bitfield(pm_counter, 10, 3)] >> (pm_depth ^ 1); +} + +template +int32_t opl_registers_base::clock_noise_and_lfo() +{ + return opl_clock_noise_and_lfo(m_noise_lfsr, m_lfo_am_counter, m_lfo_pm_counter, m_lfo_am, lfo_am_depth(), lfo_pm_depth()); +} + + +//------------------------------------------------- +// cache_operator_data - fill the operator cache +// with prefetched data; note that this code is +// also used by ymopna_registers, so it must +// handle upper channels cleanly +//------------------------------------------------- + +template +void opl_registers_base::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache) +{ + // set up the easy stuff + cache.waveform = &m_waveform[op_waveform(opoffs) % WAVEFORMS][0]; + + // get frequency from the channel + uint32_t block_freq = cache.block_freq = ch_block_freq(choffs); + + // compute the keycode: block_freq is: + // + // 111 | + // 21098|76543210 + // BBBFF|FFFFFFFF + // ^^^?? + // + // the 4-bit keycode uses the top 3 bits plus one of the next two bits + uint32_t keycode = bitfield(block_freq, 10, 3) << 1; + + // lowest bit is determined by note_select(); note that it is + // actually reversed from what the manual says, however + keycode |= bitfield(block_freq, 9 - note_select(), 1); + + // no detune adjustment on OPL + cache.detune = 0; + + // multiple value, as an x.1 value (0 means 0.5) + // replace the low bit with a table lookup to give 0,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 + uint32_t multiple = op_multiple(opoffs); + cache.multiple = ((multiple & 0xe) | bitfield(0xc2aa, multiple)) * 2; + if (cache.multiple == 0) + cache.multiple = 1; + + // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on block_freq, detune, + // and multiple, so compute it after we've done those + if (op_lfo_pm_enable(opoffs) == 0) + cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0); + else + cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC; + + // total level, scaled by 8 + cache.total_level = op_total_level(opoffs) << 3; + + // pre-add key scale level + uint32_t ksl = op_ksl(opoffs); + if (ksl != 0) + cache.total_level += opl_key_scale_atten(bitfield(block_freq, 10, 3), bitfield(block_freq, 6, 4)) << ksl; + + // 4-bit sustain level, but 15 means 31 so effectively 5 bits + cache.eg_sustain = op_sustain_level(opoffs); + cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10; + cache.eg_sustain <<= 5; + + // determine KSR adjustment for enevlope rates + uint32_t ksrval = keycode >> (2 * (op_ksr(opoffs) ^ 1)); + cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_SUSTAIN] = op_eg_sustain(opoffs) ? 0 : effective_rate(op_release_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_RELEASE] = effective_rate(op_release_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_DEPRESS] = 0x3f; +} + + +//------------------------------------------------- +// compute_phase_step - compute the phase step +//------------------------------------------------- + +static uint32_t opl_compute_phase_step(uint32_t block_freq, uint32_t multiple, int32_t lfo_raw_pm) +{ + // OPL phase calculation has no detuning, but uses FNUMs like + // the OPN version, and computes PM a bit differently + + // extract frequency number as a 12-bit fraction + uint32_t fnum = bitfield(block_freq, 0, 10) << 2; + + // apply the phase adjustment based on the upper 3 bits + // of FNUM and the PM depth parameters + fnum += (lfo_raw_pm * bitfield(block_freq, 7, 3)) >> 1; + + // keep fnum to 12 bits + fnum &= 0xfff; + + // apply block shift to compute phase step + uint32_t block = bitfield(block_freq, 10, 3); + uint32_t phase_step = (fnum << block) >> 2; + + // apply frequency multiplier (which is cached as an x.1 value) + return (phase_step * multiple) >> 1; +} + +template +uint32_t opl_registers_base::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm) +{ + (void)choffs; (void)opoffs; + return opl_compute_phase_step(cache.block_freq, cache.multiple, op_lfo_pm_enable(opoffs) ? lfo_raw_pm : 0); +} + + +//------------------------------------------------- +// log_keyon - log a key-on event +//------------------------------------------------- + +template +std::string opl_registers_base::log_keyon(uint32_t choffs, uint32_t opoffs) +{ + uint32_t chnum = (choffs & 15) + 9 * bitfield(choffs, 8); + uint32_t opnum = (opoffs & 31) - 2 * ((opoffs & 31) / 8) + 18 * bitfield(opoffs, 8); + + char buffer[256]; + char *end = &buffer[0]; + + end += sprintf(end, "%2u.%02u freq=%04X fb=%u alg=%X mul=%X tl=%02X ksr=%u ns=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u", + (unsigned)chnum, (unsigned)opnum, + (unsigned)ch_block_freq(choffs), + (unsigned)ch_feedback(choffs), + (unsigned)ch_algorithm(choffs), + (unsigned)op_multiple(opoffs), + (unsigned)op_total_level(opoffs), + (unsigned)op_ksr(opoffs), + (unsigned)note_select(), + (unsigned)op_ksl(opoffs), + (unsigned)op_attack_rate(opoffs), + (unsigned)op_decay_rate(opoffs), + (unsigned)op_release_rate(opoffs), + (unsigned)op_sustain_level(opoffs), + (unsigned)op_eg_sustain(opoffs)); + + if (OUTPUTS > 1) + end += sprintf(end, " out=%c%c%c%c", + ch_output_0(choffs) ? 'L' : '-', + ch_output_1(choffs) ? 'R' : '-', + ch_output_2(choffs) ? '0' : '-', + ch_output_3(choffs) ? '1' : '-'); + if (op_lfo_am_enable(opoffs) != 0) + end += sprintf(end, " am=%u", (unsigned)lfo_am_depth()); + if (op_lfo_pm_enable(opoffs) != 0) + end += sprintf(end, " pm=%u", (unsigned)lfo_pm_depth()); + if (waveform_enable() && op_waveform(opoffs) != 0) + end += sprintf(end, " wf=%u", (unsigned)op_waveform(opoffs)); + if (is_rhythm(choffs)) + end += sprintf(end, " rhy=1"); + if (DYNAMIC_OPS) + { + operator_mapping map; + operator_map(map); + if (bitfield(map.chan[chnum], 16, 8) != 0xff) + end += sprintf(end, " 4op"); + } + + return buffer; +} + + +//********************************************************* +// OPLL SPECIFICS +//********************************************************* + +//------------------------------------------------- +// opll_registers - constructor +//------------------------------------------------- + +opll_registers::opll_registers() : + m_lfo_am_counter(0), + m_lfo_pm_counter(0), + m_noise_lfsr(1), + m_lfo_am(0) +{ + // create the waveforms + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) + m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15); + + uint16_t zeroval = m_waveform[0][0]; + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) + m_waveform[1][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index]; + + // initialize the instruments to something sane + for (uint32_t choffs = 0; choffs < CHANNELS; choffs++) + m_chinst[choffs] = &m_regdata[0]; + for (uint32_t opoffs = 0; opoffs < OPERATORS; opoffs++) + m_opinst[opoffs] = &m_regdata[bitfield(opoffs, 0)]; +} + + +//------------------------------------------------- +// reset - reset to initial state +//------------------------------------------------- + +void opll_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void opll_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_lfo_am_counter); + state.save_restore(m_lfo_pm_counter); + state.save_restore(m_lfo_am); + state.save_restore(m_noise_lfsr); + state.save_restore(m_regdata); +} + + +//------------------------------------------------- +// operator_map - return an array of operator +// indices for each channel; for OPLL this is fixed +//------------------------------------------------- + +void opll_registers::operator_map(operator_mapping &dest) const +{ + static const operator_mapping s_fixed_map = + { { + operator_list( 0, 1 ), // Channel 0 operators + operator_list( 2, 3 ), // Channel 1 operators + operator_list( 4, 5 ), // Channel 2 operators + operator_list( 6, 7 ), // Channel 3 operators + operator_list( 8, 9 ), // Channel 4 operators + operator_list( 10, 11 ), // Channel 5 operators + operator_list( 12, 13 ), // Channel 6 operators + operator_list( 14, 15 ), // Channel 7 operators + operator_list( 16, 17 ), // Channel 8 operators + } }; + dest = s_fixed_map; +} + + +//------------------------------------------------- +// write - handle writes to the register array; +// note that this code is also used by +// ymopl3_registers, so it must handle upper +// channels cleanly +//------------------------------------------------- + +bool opll_registers::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask) +{ + // unclear the address is masked down to 6 bits or if writes above + // the register top are ignored; assuming the latter for now + if (index >= REGISTERS) + return false; + + // write the new data + m_regdata[index] = data; + + // handle writes to the rhythm keyons + if (index == 0x0e) + { + channel = RHYTHM_CHANNEL; + opmask = bitfield(data, 5) ? bitfield(data, 0, 5) : 0; + return true; + } + + // handle writes to the channel keyons + if ((index & 0xf0) == 0x20) + { + channel = index & 0x0f; + if (channel < CHANNELS) + { + opmask = bitfield(data, 4) ? 3 : 0; + return true; + } + } + return false; +} + + +//------------------------------------------------- +// clock_noise_and_lfo - clock the noise and LFO, +// handling clock division, depth, and waveform +// computations +//------------------------------------------------- + +int32_t opll_registers::clock_noise_and_lfo() +{ + // implementation is the same as OPL with fixed depths + return opl_clock_noise_and_lfo(m_noise_lfsr, m_lfo_am_counter, m_lfo_pm_counter, m_lfo_am, 1, 1); +} + + +//------------------------------------------------- +// cache_operator_data - fill the operator cache +// with prefetched data; note that this code is +// also used by ymopna_registers, so it must +// handle upper channels cleanly +//------------------------------------------------- + +void opll_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache) +{ + // first set up the instrument data + uint32_t instrument = ch_instrument(choffs); + if (rhythm_enable() && choffs >= 6) + m_chinst[choffs] = &m_instdata[8 * (15 + (choffs - 6))]; + else + m_chinst[choffs] = (instrument == 0) ? &m_regdata[0] : &m_instdata[8 * (instrument - 1)]; + m_opinst[opoffs] = m_chinst[choffs] + bitfield(opoffs, 0); + + // set up the easy stuff + cache.waveform = &m_waveform[op_waveform(opoffs) % WAVEFORMS][0]; + + // get frequency from the channel + uint32_t block_freq = cache.block_freq = ch_block_freq(choffs); + + // compute the keycode: block_freq is: + // + // 11 | + // 1098|76543210 + // BBBF|FFFFFFFF + // ^^^^ + // + // the 4-bit keycode uses the top 4 bits + uint32_t keycode = bitfield(block_freq, 8, 4); + + // no detune adjustment on OPLL + cache.detune = 0; + + // multiple value, as an x.1 value (0 means 0.5) + // replace the low bit with a table lookup to give 0,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 + uint32_t multiple = op_multiple(opoffs); + cache.multiple = ((multiple & 0xe) | bitfield(0xc2aa, multiple)) * 2; + if (cache.multiple == 0) + cache.multiple = 1; + + // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on + // block_freq, detune, and multiple, so compute it after we've done those + if (op_lfo_pm_enable(opoffs) == 0) + cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0); + else + cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC; + + // total level, scaled by 8; for non-rhythm operator 0, this is the total + // level from the instrument data; for other operators it is 4*volume + if (bitfield(opoffs, 0) == 1 || (rhythm_enable() && choffs >= 7)) + cache.total_level = op_volume(opoffs) * 4; + else + cache.total_level = ch_total_level(choffs); + cache.total_level <<= 3; + + // pre-add key scale level + uint32_t ksl = op_ksl(opoffs); + if (ksl != 0) + cache.total_level += opl_key_scale_atten(bitfield(block_freq, 9, 3), bitfield(block_freq, 5, 4)) << ksl; + + // 4-bit sustain level, but 15 means 31 so effectively 5 bits + cache.eg_sustain = op_sustain_level(opoffs); + cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10; + cache.eg_sustain <<= 5; + + // The envelope diagram in the YM2413 datasheet gives values for these + // in ms from 0->48dB. The attack/decay tables give values in ms from + // 0->96dB, so to pick an equivalent decay rate, we want to find the + // closest match that is 2x the 0->48dB value: + // + // DP = 10ms (0->48db) -> 20ms (0->96db); decay of 12 gives 19.20ms + // RR = 310ms (0->48db) -> 620ms (0->96db); decay of 7 gives 613.76ms + // RS = 1200ms (0->48db) -> 2400ms (0->96db); decay of 5 gives 2455.04ms + // + // The envelope diagram for percussive sounds (eg_sustain() == 0) also uses + // "RR" to mean both the constant RR above and the Release Rate specified in + // the instrument data. In this case, Relief Pitcher's credit sound bears out + // that the Release Rate is used during sustain, and that the constant RR + // (or RS) is used during the release phase. + constexpr uint8_t DP = 12 * 4; + constexpr uint8_t RR = 7 * 4; + constexpr uint8_t RS = 5 * 4; + + // determine KSR adjustment for envelope rates + uint32_t ksrval = keycode >> (2 * (op_ksr(opoffs) ^ 1)); + cache.eg_rate[EG_DEPRESS] = DP; + cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 4, ksrval); + if (op_eg_sustain(opoffs)) + { + cache.eg_rate[EG_SUSTAIN] = 0; + cache.eg_rate[EG_RELEASE] = ch_sustain(choffs) ? RS : effective_rate(op_release_rate(opoffs) * 4, ksrval); + } + else + { + cache.eg_rate[EG_SUSTAIN] = effective_rate(op_release_rate(opoffs) * 4, ksrval); + cache.eg_rate[EG_RELEASE] = ch_sustain(choffs) ? RS : RR; + } +} + + +//------------------------------------------------- +// compute_phase_step - compute the phase step +//------------------------------------------------- + +uint32_t opll_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm) +{ + (void)choffs; (void)opoffs; + // phase step computation is the same as OPL but the block_freq has one + // more bit, which we shift in + return opl_compute_phase_step(cache.block_freq << 1, cache.multiple, op_lfo_pm_enable(opoffs) ? lfo_raw_pm : 0); +} + + +//------------------------------------------------- +// log_keyon - log a key-on event +//------------------------------------------------- + +std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs) +{ + uint32_t chnum = choffs; + uint32_t opnum = opoffs; + + char buffer[256]; + char *end = &buffer[0]; + + end += sprintf(end, "%u.%02u freq=%04X inst=%X fb=%u mul=%X", + (unsigned)chnum, (unsigned)opnum, + (unsigned)ch_block_freq(choffs), + (unsigned)ch_instrument(choffs), + (unsigned)ch_feedback(choffs), + (unsigned)op_multiple(opoffs) + ); + + if (bitfield(opoffs, 0) == 1 || (is_rhythm(choffs) && choffs >= 6)) + end += sprintf(end, " vol=%X", (unsigned)op_volume(opoffs)); + else + end += sprintf(end, " tl=%02X", (unsigned)ch_total_level(choffs)); + + end += sprintf(end, " ksr=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u/%u", + (unsigned)op_ksr(opoffs), + (unsigned)op_ksl(opoffs), + (unsigned)op_attack_rate(opoffs), + (unsigned)op_decay_rate(opoffs), + (unsigned)op_release_rate(opoffs), + (unsigned)op_sustain_level(opoffs), + (unsigned)op_eg_sustain(opoffs), + (unsigned)ch_sustain(choffs)); + + if (op_lfo_am_enable(opoffs)) + end += sprintf(end, " am=1"); + if (op_lfo_pm_enable(opoffs)) + end += sprintf(end, " pm=1"); + if (op_waveform(opoffs) != 0) + end += sprintf(end, " wf=1"); + if (is_rhythm(choffs)) + end += sprintf(end, " rhy=1"); + + return buffer; +} + + + +//********************************************************* +// YM3526 +//********************************************************* + +//------------------------------------------------- +// ym3526 - constructor +//------------------------------------------------- + +ym3526::ym3526(ymfm_interface &intf) : + m_address(0), + m_fm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ym3526::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ym3526::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ym3526::read_status() +{ + return m_fm.status() | 0x06; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ym3526::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 1) + { + case 0: // status port + result = read_status(); + break; + + case 1: // when A0=1 datasheet says "the data on the bus are not guaranteed" + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ym3526::write_address(uint8_t data) +{ + // YM3526 doesn't expose a busy signal, and the datasheets don't indicate + // delays, but all other OPL chips need 12 cycles for address writes + m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale()); + + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym3526::write_data(uint8_t data) +{ + // YM3526 doesn't expose a busy signal, and the datasheets don't indicate + // delays, but all other OPL chips need 84 cycles for data writes + m_fm.intf().ymfm_set_busy_end(84 * m_fm.clock_prescale()); + + // write to FM + m_fm.write(m_address, data); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym3526::write(uint32_t offset, uint8_t data) +{ + switch (offset & 1) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of sound +//------------------------------------------------- + +void ym3526::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; mixing details for YM3526 need verification + m_fm.output(output->clear(), 1, 32767, fm_engine::ALL_CHANNELS); + + // YM3526 uses an external DAC (YM3014) with mantissa/exponent format + // convert to 10.3 floating point value and back to simulate truncation + output->roundtrip_fp(); + } +} + + + +//********************************************************* +// Y8950 +//********************************************************* + +//------------------------------------------------- +// y8950 - constructor +//------------------------------------------------- + +y8950::y8950(ymfm_interface &intf) : + m_address(0), + m_io_ddr(0), + m_fm(intf), + m_adpcm_b(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void y8950::reset() +{ + // reset the engines + m_fm.reset(); + m_adpcm_b.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void y8950::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + state.save_restore(m_io_ddr); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t y8950::read_status() +{ + // start with current FM status, masking out bits we might set + uint8_t status = m_fm.status() & ~(STATUS_ADPCM_B_EOS | STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_PLAYING); + + // insert the live ADPCM status bits + uint8_t adpcm_status = m_adpcm_b.status(); + if ((adpcm_status & adpcm_b_channel::STATUS_EOS) != 0) + status |= STATUS_ADPCM_B_EOS; + if ((adpcm_status & adpcm_b_channel::STATUS_BRDY) != 0) + status |= STATUS_ADPCM_B_BRDY; + if ((adpcm_status & adpcm_b_channel::STATUS_PLAYING) != 0) + status |= STATUS_ADPCM_B_PLAYING; + + // run it through the FM engine to handle interrupts for us + return m_fm.set_reset_status(status, ~status); +} + + +//------------------------------------------------- +// read_data - read the data port +//------------------------------------------------- + +uint8_t y8950::read_data() +{ + uint8_t result = 0xff; + switch (m_address) + { + case 0x05: // keyboard in + result = m_fm.intf().ymfm_external_read(ACCESS_IO, 1); + break; + + case 0x09: // ADPCM data + case 0x1a: + result = m_adpcm_b.read(m_address - 0x07); + break; + + case 0x19: // I/O data + result = m_fm.intf().ymfm_external_read(ACCESS_IO, 0); + break; + + default: + debug::log_unexpected_read_write("Unexpected read from Y8950 data port %02X\n", m_address); + break; + } + return result; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t y8950::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 1) + { + case 0: // status port + result = read_status(); + break; + + case 1: // when A0=1 datasheet says "the data on the bus are not guaranteed" + result = read_data(); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void y8950::write_address(uint8_t data) +{ + // Y8950 doesn't expose a busy signal, but it does indicate that + // address writes should be no faster than every 12 clocks + m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale()); + + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void y8950::write_data(uint8_t data) +{ + // Y8950 doesn't expose a busy signal, but it does indicate that + // data writes should be no faster than every 12 clocks for + // registers 00-1A, or every 84 clocks for other registers + m_fm.intf().ymfm_set_busy_end(((m_address <= 0x1a) ? 12 : 84) * m_fm.clock_prescale()); + + // handle special addresses + switch (m_address) + { + case 0x04: // IRQ control + m_fm.write(m_address, data); + read_status(); + break; + + case 0x06: // keyboard out + m_fm.intf().ymfm_external_write(ACCESS_IO, 1, data); + break; + + case 0x08: // split FM/ADPCM-B + m_adpcm_b.write(m_address - 0x07, (data & 0x0f) | 0x80); + m_fm.write(m_address, data & 0xc0); + break; + + case 0x07: // ADPCM-B registers + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x15: + case 0x16: + case 0x17: + m_adpcm_b.write(m_address - 0x07, data); + break; + + case 0x18: // I/O direction + m_io_ddr = data & 0x0f; + break; + + case 0x19: // I/O data + m_fm.intf().ymfm_external_write(ACCESS_IO, 0, data & m_io_ddr); + break; + + default: // everything else to FM + m_fm.write(m_address, data); + break; + } +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void y8950::write(uint32_t offset, uint8_t data) +{ + switch (offset & 1) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of sound +//------------------------------------------------- + +void y8950::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + m_adpcm_b.clock(); + + // update the FM content; clipping need verification + m_fm.output(output->clear(), 1, 32767, fm_engine::ALL_CHANNELS); + + // mix in the ADPCM; ADPCM-B is stereo, but only one channel + // not sure how it's wired up internally + m_adpcm_b.output(*output, 3); + + // Y8950 uses an external DAC (YM3014) with mantissa/exponent format + // convert to 10.3 floating point value and back to simulate truncation + output->roundtrip_fp(); + } +} + + + +//********************************************************* +// YM3812 +//********************************************************* + +//------------------------------------------------- +// ym3812 - constructor +//------------------------------------------------- + +ym3812::ym3812(ymfm_interface &intf) : + m_address(0), + m_fm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ym3812::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ym3812::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ym3812::read_status() +{ + return m_fm.status() | 0x06; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ym3812::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 1) + { + case 0: // status port + result = read_status(); + break; + + case 1: // "inhibit" according to datasheet + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ym3812::write_address(uint8_t data) +{ + // YM3812 doesn't expose a busy signal, but it does indicate that + // address writes should be no faster than every 12 clocks + m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale()); + + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym3812::write_data(uint8_t data) +{ + // YM3812 doesn't expose a busy signal, but it does indicate that + // data writes should be no faster than every 84 clocks + m_fm.intf().ymfm_set_busy_end(84 * m_fm.clock_prescale()); + + // write to FM + m_fm.write(m_address, data); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym3812::write(uint32_t offset, uint8_t data) +{ + switch (offset & 1) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of sound +//------------------------------------------------- + +void ym3812::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; mixing details for YM3812 need verification + m_fm.output(output->clear(), 1, 32767, fm_engine::ALL_CHANNELS); + + // YM3812 uses an external DAC (YM3014) with mantissa/exponent format + // convert to 10.3 floating point value and back to simulate truncation + output->roundtrip_fp(); + } +} + + + +//********************************************************* +// YMF262 +//********************************************************* + +//------------------------------------------------- +// ymf262 - constructor +//------------------------------------------------- + +ymf262::ymf262(ymfm_interface &intf) : + m_address(0), + m_fm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ymf262::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ymf262::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ymf262::read_status() +{ + return m_fm.status(); +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ymf262::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 3) + { + case 0: // status port + result = read_status(); + break; + + case 1: + case 2: + case 3: + debug::log_unexpected_read_write("Unexpected read from YMF262 offset %d\n", offset & 3); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ymf262::write_address(uint8_t data) +{ + // YMF262 doesn't expose a busy signal, but it does indicate that + // address writes should be no faster than every 32 clocks + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); + + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write_data - handle a write to the data +// register +//------------------------------------------------- + +void ymf262::write_data(uint8_t data) +{ + // YMF262 doesn't expose a busy signal, but it does indicate that + // data writes should be no faster than every 32 clocks + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); + + // write to FM + m_fm.write(m_address, data); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf262::write_address_hi(uint8_t data) +{ + // YMF262 doesn't expose a busy signal, but it does indicate that + // address writes should be no faster than every 32 clocks + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); + + // just set the address + m_address = data | 0x100; + + // tests reveal that in compatibility mode, upper bit is masked + // except for register 0x105 + if (m_fm.regs().newflag() == 0 && m_address != 0x105) + m_address &= 0xff; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ymf262::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // address port + write_address_hi(data); + break; + + case 3: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of sound +//------------------------------------------------- + +void ymf262::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; mixing details for YMF262 need verification + m_fm.output(output->clear(), 0, 32767, fm_engine::ALL_CHANNELS); + + // YMF262 output is 16-bit offset serial via YAC512 DAC + output->clamp16(); + } +} + + + +//********************************************************* +// YMF289B +//********************************************************* + +// YMF289B is a YMF262 with the following changes: +// * "Power down" mode added +// * Bulk register clear added +// * Busy flag added to the status register +// * Shorter busy times +// * All registers can be read +// * Only 2 outputs exposed + +//------------------------------------------------- +// ymf289b - constructor +//------------------------------------------------- + +ymf289b::ymf289b(ymfm_interface &intf) : + m_address(0), + m_fm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ymf289b::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ymf289b::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ymf289b::read_status() +{ + uint8_t result = m_fm.status(); + + // YMF289B adds a busy flag + if (ymf289b_mode() && m_fm.intf().ymfm_is_busy()) + result |= STATUS_BUSY_FLAGS; + return result; +} + + +//------------------------------------------------- +// read_data - read the data register +//------------------------------------------------- + +uint8_t ymf289b::read_data() +{ + uint8_t result = 0xff; + + // YMF289B can read register data back + if (ymf289b_mode()) + result = m_fm.regs().read(m_address); + return result; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ymf289b::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 3) + { + case 0: // status port + result = read_status(); + break; + + case 1: // data port + result = read_data(); + break; + + case 2: + case 3: + debug::log_unexpected_read_write("Unexpected read from YMF289B offset %d\n", offset & 3); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ymf289b::write_address(uint8_t data) +{ + m_address = data; + + // count busy time + m_fm.intf().ymfm_set_busy_end(56); +} + + +//------------------------------------------------- +// write_data - handle a write to the data +// register +//------------------------------------------------- + +void ymf289b::write_data(uint8_t data) +{ + // write to FM + m_fm.write(m_address, data); + + // writes to 0x108 with the CLR flag set clear the registers + if (m_address == 0x108 && bitfield(data, 2) != 0) + m_fm.regs().reset(); + + // count busy time + m_fm.intf().ymfm_set_busy_end(56); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf289b::write_address_hi(uint8_t data) +{ + // just set the address + m_address = data | 0x100; + + // tests reveal that in compatibility mode, upper bit is masked + // except for register 0x105 + if (m_fm.regs().newflag() == 0 && m_address != 0x105) + m_address &= 0xff; + + // count busy time + m_fm.intf().ymfm_set_busy_end(56); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ymf289b::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // address port + write_address_hi(data); + break; + + case 3: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of sound +//------------------------------------------------- + +void ymf289b::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; mixing details for YMF262 need verification + fm_engine::output_data full; + m_fm.output(full.clear(), 0, 32767, fm_engine::ALL_CHANNELS); + + // YMF278B output is 16-bit offset serial via YAC512 DAC, but + // only 2 of the 4 outputs are exposed + output->data[0] = full.data[0]; + output->data[1] = full.data[1]; + output->clamp16(); + } +} + + + +//********************************************************* +// YMF278B +//********************************************************* + +//------------------------------------------------- +// ymf278b - constructor +//------------------------------------------------- + +ymf278b::ymf278b(ymfm_interface &intf) : + m_address(0), + m_fm_pos(0), + m_load_remaining(0), + m_next_status_id(false), + m_fm(intf), + m_pcm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ymf278b::reset() +{ + // reset the engines + m_fm.reset(); + m_pcm.reset(); + + // next status read will return ID + m_next_status_id = true; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ymf278b::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + state.save_restore(m_fm_pos); + state.save_restore(m_load_remaining); + state.save_restore(m_next_status_id); + m_fm.save_restore(state); + m_pcm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ymf278b::read_status() +{ + uint8_t result; + + // first status read after initialization returns a chip ID, which + // varies based on the "new" flags, indicating the mode + if (m_next_status_id) + { + if (m_fm.regs().new2flag()) + result = 0x02; + else if (m_fm.regs().newflag()) + result = 0x00; + else + result = 0x06; + m_next_status_id = false; + } + else + { + result = m_fm.status(); + if (m_fm.intf().ymfm_is_busy()) + result |= STATUS_BUSY; + if (m_load_remaining != 0) + result |= STATUS_LD; + + // if new2 flag is not set, we're in OPL2 or OPL3 mode + if (!m_fm.regs().new2flag()) + result &= ~(STATUS_BUSY | STATUS_LD); + } + return result; +} + + +//------------------------------------------------- +// write_data_pcm - handle a write to the PCM data +// register +//------------------------------------------------- + +uint8_t ymf278b::read_data_pcm() +{ + // read from PCM + if (bitfield(m_address, 9) != 0) + { + uint8_t result = m_pcm.read(m_address & 0xff); + if ((m_address & 0xff) == 0x02) + result |= 0x20; + + return result; + } + return 0; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ymf278b::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 7) + { + case 0: // status port + result = read_status(); + break; + + case 5: // PCM data port + result = read_data_pcm(); + break; + + default: + debug::log_unexpected_read_write("Unexpected read from ymf278b offset %d\n", offset & 3); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ymf278b::write_address(uint8_t data) +{ + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write_data - handle a write to the data +// register +//------------------------------------------------- + +void ymf278b::write_data(uint8_t data) +{ + // write to FM + if (bitfield(m_address, 9) == 0) + { + uint8_t old = m_fm.regs().new2flag(); + m_fm.write(m_address, data); + + // changing NEW2 from 0->1 causes the next status read to + // return the chip ID + if (old == 0 && m_fm.regs().new2flag() != 0) + m_next_status_id = true; + } + + // BUSY goes for 56 clocks on FM writes + m_fm.intf().ymfm_set_busy_end(56); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf278b::write_address_hi(uint8_t data) +{ + // just set the address + m_address = data | 0x100; + + // YMF262, in compatibility mode, treats the upper bit as masked + // except for register 0x105; assuming YMF278B works the same way? + if (m_fm.regs().newflag() == 0 && m_address != 0x105) + m_address &= 0xff; +} + + +//------------------------------------------------- +// write_address_pcm - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf278b::write_address_pcm(uint8_t data) +{ + // just set the address + m_address = data | 0x200; +} + + +//------------------------------------------------- +// write_data_pcm - handle a write to the PCM data +// register +//------------------------------------------------- + +void ymf278b::write_data_pcm(uint8_t data) +{ + // ignore data writes if new2 is not yet set + if (m_fm.regs().new2flag() == 0) + return; + + // write to FM + if (bitfield(m_address, 9) != 0) + { + uint8_t addr = m_address & 0xff; + m_pcm.write(addr, data); + + // writes to the waveform number cause loads to happen for "about 300usec" + // which is ~13 samples at the nominal output frequency of 44.1kHz + if (addr >= 0x08 && addr <= 0x1f) + m_load_remaining = 13; + } + + // BUSY goes for 88 clocks on PCM writes + m_fm.intf().ymfm_set_busy_end(88); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ymf278b::write(uint32_t offset, uint8_t data) +{ + switch (offset & 7) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // address port + write_address_hi(data); + break; + + case 3: // data port + write_data(data); + break; + + case 4: // PCM address port + write_address_pcm(data); + break; + + case 5: // PCM address port + write_data_pcm(data); + break; + + default: + debug::log_unexpected_read_write("Unexpected write to ymf278b offset %d\n", offset & 7); + break; + } +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void ymf278b::generate(output_data *output, uint32_t numsamples) +{ + static const int16_t s_mix_scale[8] = { 0x7fa, 0x5a4, 0x3fd, 0x2d2, 0x1fe, 0x169, 0xff, 0 }; + int32_t const pcm_l = s_mix_scale[m_pcm.regs().mix_pcm_l()]; + int32_t const pcm_r = s_mix_scale[m_pcm.regs().mix_pcm_r()]; + int32_t const fm_l = s_mix_scale[m_pcm.regs().mix_fm_l()]; + int32_t const fm_r = s_mix_scale[m_pcm.regs().mix_fm_r()]; + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm_pos += FM_EXTRA_SAMPLE_STEP; + if (m_fm_pos >= FM_EXTRA_SAMPLE_THRESH) + { + m_fm.clock(fm_engine::ALL_CHANNELS); + m_fm_pos -= FM_EXTRA_SAMPLE_THRESH; + } + m_fm.clock(fm_engine::ALL_CHANNELS); + m_pcm.clock(pcm_engine::ALL_CHANNELS); + + // update the FM content; mixing details for YMF278B need verification + fm_engine::output_data fmout; + m_fm.output(fmout.clear(), 0, 32767, fm_engine::ALL_CHANNELS); + + // update the PCM content + pcm_engine::output_data pcmout; + m_pcm.output(pcmout.clear(), pcm_engine::ALL_CHANNELS); + + // DO0 output: FM channels 2+3 only + output->data[0] = fmout.data[2]; + output->data[1] = fmout.data[3]; + + // DO1 output: wavetable channels 2+3 only + output->data[2] = pcmout.data[2]; + output->data[3] = pcmout.data[3]; + + // DO2 output: mixed FM channels 0+1 and wavetable channels 0+1 + output->data[4] = (fmout.data[0] * fm_l + pcmout.data[0] * pcm_l) >> 11; + output->data[5] = (fmout.data[1] * fm_r + pcmout.data[1] * pcm_r) >> 11; + + // YMF278B output is 16-bit 2s complement serial + output->clamp16(); + } + + // decrement the load waiting count + if (m_load_remaining > 0) + m_load_remaining -= std::min(m_load_remaining, numsamples); +} + + + +//********************************************************* +// OPLL BASE +//********************************************************* + +//------------------------------------------------- +// opll_base - constructor +//------------------------------------------------- + +opll_base::opll_base(ymfm_interface &intf, uint8_t const *instrument_data) : + m_address(0), + m_fm(intf) +{ + m_fm.regs().set_instrument_data(instrument_data); +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void opll_base::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void opll_base::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void opll_base::write_address(uint8_t data) +{ + // OPLL doesn't expose a busy signal, but datasheets are pretty consistent + // in indicating that address writes should be no faster than every 12 clocks + m_fm.intf().ymfm_set_busy_end(12); + + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void opll_base::write_data(uint8_t data) +{ + // OPLL doesn't expose a busy signal, but datasheets are pretty consistent + // in indicating that address writes should be no faster than every 84 clocks + m_fm.intf().ymfm_set_busy_end(84); + + // write to FM + m_fm.write(m_address, data); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void opll_base::write(uint32_t offset, uint8_t data) +{ + switch (offset & 1) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void opll_base::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; OPLL has a built-in 9-bit DAC + m_fm.output(output->clear(), 5, 256, fm_engine::ALL_CHANNELS); + + // final output is multiplexed; we don't simulate that here except + // to average over everything + output->data[0] = (output->data[0] * 128) / 9; + output->data[1] = (output->data[1] * 128) / 9; + } +} + + + +//********************************************************* +// YM2413 +//********************************************************* + +//------------------------------------------------- +// ym2413 - constructor +//------------------------------------------------- + +ym2413::ym2413(ymfm_interface &intf, uint8_t const *instrument_data) : + opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments) +{ +} + +// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches +uint8_t const ym2413::s_default_instruments[] = +{ + //April 2015 David Viens, tweaked May 19-21th 2015 Hubert Lamontagne + 0x71, 0x61, 0x1E, 0x17, 0xEF, 0x7F, 0x00, 0x17, //Violin + 0x13, 0x41, 0x1A, 0x0D, 0xF8, 0xF7, 0x23, 0x13, //Guitar + 0x13, 0x01, 0x99, 0x00, 0xF2, 0xC4, 0x11, 0x23, //Piano + 0x31, 0x61, 0x0E, 0x07, 0x98, 0x64, 0x70, 0x27, //Flute + 0x22, 0x21, 0x1E, 0x06, 0xBF, 0x76, 0x00, 0x28, //Clarinet + 0x31, 0x22, 0x16, 0x05, 0xE0, 0x71, 0x0F, 0x18, //Oboe + 0x21, 0x61, 0x1D, 0x07, 0x82, 0x8F, 0x10, 0x07, //Trumpet + 0x23, 0x21, 0x2D, 0x14, 0xFF, 0x7F, 0x00, 0x07, //Organ + 0x41, 0x61, 0x1B, 0x06, 0x64, 0x65, 0x10, 0x17, //Horn + 0x61, 0x61, 0x0B, 0x18, 0x85, 0xFF, 0x81, 0x07, //Synthesizer + 0x13, 0x01, 0x83, 0x11, 0xFA, 0xE4, 0x10, 0x04, //Harpsichord + 0x17, 0x81, 0x23, 0x07, 0xF8, 0xF8, 0x22, 0x12, //Vibraphone + 0x61, 0x50, 0x0C, 0x05, 0xF2, 0xF5, 0x29, 0x42, //Synthesizer Bass + 0x01, 0x01, 0x54, 0x03, 0xC3, 0x92, 0x03, 0x02, //Acoustic Bass + 0x41, 0x41, 0x89, 0x03, 0xF1, 0xE5, 0x11, 0x13, //Electric Guitar + 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1 + 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2 + 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3 +}; + + + +//********************************************************* +// YM2423 +//********************************************************* + +//------------------------------------------------- +// ym2423 - constructor +//------------------------------------------------- + +ym2423::ym2423(ymfm_interface &intf, uint8_t const *instrument_data) : + opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments) +{ +} + +// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches +uint8_t const ym2423::s_default_instruments[] = +{ + // May 4-6 2016 Hubert Lamontagne + // Doesn't seem to have any diff between opllx-x and opllx-y + // Drums seem identical to regular opll + 0x61, 0x61, 0x1B, 0x07, 0x94, 0x5F, 0x10, 0x06, //1 Strings Saw wave with vibrato Violin + 0x93, 0xB1, 0x51, 0x04, 0xF3, 0xF2, 0x70, 0xFB, //2 Guitar Jazz GuitarPiano + 0x41, 0x21, 0x11, 0x85, 0xF2, 0xF2, 0x70, 0x75, //3 Electric Guitar Same as OPLL No.15 Synth + 0x93, 0xB2, 0x28, 0x07, 0xF3, 0xF2, 0x70, 0xB4, //4 Electric Piano 2 Slow attack, tremoloDing-a-ling + 0x72, 0x31, 0x97, 0x05, 0x51, 0x6F, 0x60, 0x09, //5 Flute Same as OPLL No.4Clarinet + 0x13, 0x30, 0x18, 0x06, 0xF7, 0xF4, 0x50, 0x85, //6 Marimba Also be used as steel drumXyophone + 0x51, 0x31, 0x1C, 0x07, 0x51, 0x71, 0x20, 0x26, //7 Trumpet Same as OPLL No.7Trumpet + 0x41, 0xF4, 0x1B, 0x07, 0x74, 0x34, 0x00, 0x06, //8 Harmonica Harmonica synth + 0x50, 0x30, 0x4D, 0x03, 0x42, 0x65, 0x20, 0x06, //9 Tuba Tuba + 0x40, 0x20, 0x10, 0x85, 0xF3, 0xF5, 0x20, 0x04, //10 Synth Brass 2 Synth sweep + 0x61, 0x61, 0x1B, 0x07, 0xC5, 0x96, 0xF3, 0xF6, //11 Short Saw Saw wave with short envelopeSynth hit + 0xF9, 0xF1, 0xDC, 0x00, 0xF5, 0xF3, 0x77, 0xF2, //12 Vibraphone Bright vibraphoneVibes + 0x60, 0xA2, 0x91, 0x03, 0x94, 0xC1, 0xF7, 0xF7, //13 Electric Guitar 2 Clean guitar with feedbackHarmonic bass + 0x30, 0x30, 0x17, 0x06, 0xF3, 0xF1, 0xB7, 0xFC, //14 Synth Bass 2Snappy bass + 0x31, 0x36, 0x0D, 0x05, 0xF2, 0xF4, 0x27, 0x9C, //15 Sitar Also be used as ShamisenBanjo + 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1 + 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2 + 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3 +}; + + + +//********************************************************* +// YMF281 +//********************************************************* + +//------------------------------------------------- +// ymf281 - constructor +//------------------------------------------------- + +ymf281::ymf281(ymfm_interface &intf, uint8_t const *instrument_data) : + opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments) +{ +} + +// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches +uint8_t const ymf281::s_default_instruments[] = +{ + // May 14th 2015 Hubert Lamontagne + 0x72, 0x21, 0x1A, 0x07, 0xF6, 0x64, 0x01, 0x16, // Clarinet ~~ Electric String Square wave with vibrato + 0x00, 0x10, 0x45, 0x00, 0xF6, 0x83, 0x73, 0x63, // Synth Bass ~~ Bow wow Triangular wave + 0x13, 0x01, 0x96, 0x00, 0xF1, 0xF4, 0x31, 0x23, // Piano ~~ Electric Guitar Despite of its name, same as Piano of YM2413. + 0x71, 0x21, 0x0B, 0x0F, 0xF9, 0x64, 0x70, 0x17, // Flute ~~ Organ Sine wave + 0x02, 0x21, 0x1E, 0x06, 0xF9, 0x76, 0x00, 0x28, // Square Wave ~~ Clarinet Same as ones of YM2413. + 0x00, 0x61, 0x82, 0x0E, 0xF9, 0x61, 0x20, 0x27, // Space Oboe ~~ Saxophone Saw wave with vibrato + 0x21, 0x61, 0x1B, 0x07, 0x84, 0x8F, 0x10, 0x07, // Trumpet ~~ Trumpet Same as ones of YM2413. + 0x37, 0x32, 0xCA, 0x02, 0x66, 0x64, 0x47, 0x29, // Wow Bell ~~ Street Organ Calliope + 0x41, 0x41, 0x07, 0x03, 0xF5, 0x70, 0x51, 0xF5, // Electric Guitar ~~ Synth Brass Same as Synthesizer of YM2413. + 0x36, 0x01, 0x5E, 0x07, 0xF2, 0xF3, 0xF7, 0xF7, // Vibes ~~ Electric Piano Simulate of Rhodes Piano + 0x00, 0x00, 0x18, 0x06, 0xC5, 0xF3, 0x20, 0xF2, // Bass ~~ Bass Electric bass + 0x17, 0x81, 0x25, 0x07, 0xF7, 0xF3, 0x21, 0xF7, // Vibraphone ~~ Vibraphone Same as ones of YM2413. + 0x35, 0x64, 0x00, 0x00, 0xFF, 0xF3, 0x77, 0xF5, // Vibrato Bell ~~ Chime Bell + 0x11, 0x31, 0x00, 0x07, 0xDD, 0xF3, 0xFF, 0xFB, // Click Sine ~~ Tom Tom II Tom + 0x3A, 0x21, 0x00, 0x07, 0x95, 0x84, 0x0F, 0xF5, // Noise and Tone ~~ Noise for S.E. + 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1 + 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2 + 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3 +}; + + + +//********************************************************* +// DS1001 +//********************************************************* + +//------------------------------------------------- +// ds1001 - constructor +//------------------------------------------------- + +ds1001::ds1001(ymfm_interface &intf, uint8_t const *instrument_data) : + opll_base(intf, (instrument_data != nullptr) ? instrument_data : s_default_instruments) +{ +} + +// table below taken from https://github.com/plgDavid/misc/wiki/Copyright-free-OPLL(x)-ROM-patches +uint8_t const ds1001::s_default_instruments[] = +{ + // May 15th 2015 Hubert Lamontagne & David Viens + 0x03, 0x21, 0x05, 0x06, 0xC8, 0x81, 0x42, 0x27, // Buzzy Bell + 0x13, 0x41, 0x14, 0x0D, 0xF8, 0xF7, 0x23, 0x12, // Guitar + 0x31, 0x11, 0x08, 0x08, 0xFA, 0xC2, 0x28, 0x22, // Wurly + 0x31, 0x61, 0x0C, 0x07, 0xF8, 0x64, 0x60, 0x27, // Flute + 0x22, 0x21, 0x1E, 0x06, 0xFF, 0x76, 0x00, 0x28, // Clarinet + 0x02, 0x01, 0x05, 0x00, 0xAC, 0xF2, 0x03, 0x02, // Synth + 0x21, 0x61, 0x1D, 0x07, 0x82, 0x8F, 0x10, 0x07, // Trumpet + 0x23, 0x21, 0x22, 0x17, 0xFF, 0x73, 0x00, 0x17, // Organ + 0x15, 0x11, 0x25, 0x00, 0x41, 0x71, 0x00, 0xF1, // Bells + 0x95, 0x01, 0x10, 0x0F, 0xB8, 0xAA, 0x50, 0x02, // Vibes + 0x17, 0xC1, 0x5E, 0x07, 0xFA, 0xF8, 0x22, 0x12, // Vibraphone + 0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x10, 0x16, // Tutti + 0x01, 0x02, 0xD3, 0x05, 0xF3, 0x92, 0x83, 0xF2, // Fretless + 0x61, 0x63, 0x0C, 0x00, 0xA4, 0xFF, 0x30, 0x06, // Synth Bass + 0x21, 0x62, 0x0D, 0x00, 0xA1, 0xFF, 0x50, 0x08, // Sweep + 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, //rhythm 1 + 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x48, //rhythm 2 + 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55 //rhythm 3 +}; + + +//********************************************************* +// EXPLICIT INSTANTIATION +//********************************************************* + +template class opl_registers_base<4>; +template class fm_engine_base>; + +} diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_opl.h b/thirdparty/adlmidi/chips/ymfm/ymfm_opl.h new file mode 100644 index 000000000..320ff2168 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_opl.h @@ -0,0 +1,902 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_OPL_H +#define YMFM_OPL_H + +#pragma once + +#include "ymfm.h" +#include "ymfm_adpcm.h" +#include "ymfm_fm.h" +#include "ymfm_pcm.h" + +namespace ymfm +{ + +//********************************************************* +// REGISTER CLASSES +//********************************************************* + +// ======================> opl_registers_base + +// +// OPL/OPL2/OPL3/OPL4 register map: +// +// System-wide registers: +// 01 xxxxxxxx Test register +// --x----- Enable OPL compatibility mode [OPL2 only] (1 = enable) +// 02 xxxxxxxx Timer A value (4 * OPN) +// 03 xxxxxxxx Timer B value +// 04 x------- RST +// -x------ Mask timer A +// --x----- Mask timer B +// ------x- Load timer B +// -------x Load timer A +// 08 x------- CSM mode [OPL/OPL2 only] +// -x------ Note select +// BD x------- AM depth +// -x------ PM depth +// --x----- Rhythm enable +// ---x---- Bass drum key on +// ----x--- Snare drum key on +// -----x-- Tom key on +// ------x- Top cymbal key on +// -------x High hat key on +// 101 --xxxxxx Test register 2 [OPL3 only] +// 104 --x----- Channel 6 4-operator mode [OPL3 only] +// ---x---- Channel 5 4-operator mode [OPL3 only] +// ----x--- Channel 4 4-operator mode [OPL3 only] +// -----x-- Channel 3 4-operator mode [OPL3 only] +// ------x- Channel 2 4-operator mode [OPL3 only] +// -------x Channel 1 4-operator mode [OPL3 only] +// 105 -------x New [OPL3 only] +// ------x- New2 [OPL4 only] +// +// Per-channel registers (channel in address bits 0-3) +// Note that all these apply to address+100 as well on OPL3+ +// A0-A8 xxxxxxxx F-number (low 8 bits) +// B0-B8 --x----- Key on +// ---xxx-- Block (octvate, 0-7) +// ------xx F-number (high two bits) +// C0-C8 x------- CHD output (to DO0 pin) [OPL3+ only] +// -x------ CHC output (to DO0 pin) [OPL3+ only] +// --x----- CHB output (mixed right, to DO2 pin) [OPL3+ only] +// ---x---- CHA output (mixed left, to DO2 pin) [OPL3+ only] +// ----xxx- Feedback level for operator 1 (0-7) +// -------x Operator connection algorithm +// +// Per-operator registers (operator in bits 0-5) +// Note that all these apply to address+100 as well on OPL3+ +// 20-35 x------- AM enable +// -x------ PM enable (VIB) +// --x----- EG type +// ---x---- Key scale rate +// ----xxxx Multiple value (0-15) +// 40-55 xx------ Key scale level (0-3) +// --xxxxxx Total level (0-63) +// 60-75 xxxx---- Attack rate (0-15) +// ----xxxx Decay rate (0-15) +// 80-95 xxxx---- Sustain level (0-15) +// ----xxxx Release rate (0-15) +// E0-F5 ------xx Wave select (0-3) [OPL2 only] +// -----xxx Wave select (0-7) [OPL3+ only] +// + +template +class opl_registers_base : public fm_registers_base +{ + static constexpr bool IsOpl2 = (Revision == 2); + static constexpr bool IsOpl2Plus = (Revision >= 2); + static constexpr bool IsOpl3Plus = (Revision >= 3); + static constexpr bool IsOpl4Plus = (Revision >= 4); + +public: + // constants + static constexpr uint32_t OUTPUTS = IsOpl3Plus ? 4 : 1; + static constexpr uint32_t CHANNELS = IsOpl3Plus ? 18 : 9; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + static constexpr uint32_t OPERATORS = CHANNELS * 2; + static constexpr uint32_t WAVEFORMS = IsOpl3Plus ? 8 : (IsOpl2Plus ? 4 : 1); + static constexpr uint32_t REGISTERS = IsOpl3Plus ? 0x200 : 0x100; + static constexpr uint32_t REG_MODE = 0x04; + static constexpr uint32_t DEFAULT_PRESCALE = IsOpl4Plus ? 19 : (IsOpl3Plus ? 8 : 4); + static constexpr uint32_t EG_CLOCK_DIVIDER = 1; + static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS; + static constexpr bool DYNAMIC_OPS = IsOpl3Plus; + static constexpr bool MODULATOR_DELAY = !IsOpl3Plus; + static constexpr uint8_t STATUS_TIMERA = 0x40; + static constexpr uint8_t STATUS_TIMERB = 0x20; + static constexpr uint8_t STATUS_BUSY = 0; + static constexpr uint8_t STATUS_IRQ = 0x80; + + // constructor + opl_registers_base(); + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // map channel number to register offset + static constexpr uint32_t channel_offset(uint32_t chnum) + { + assert(chnum < CHANNELS); + if (!IsOpl3Plus) + return chnum; + else + return (chnum % 9) + 0x100 * (chnum / 9); + } + + // map operator number to register offset + static constexpr uint32_t operator_offset(uint32_t opnum) + { + assert(opnum < OPERATORS); + if (!IsOpl3Plus) + return opnum + 2 * (opnum / 6); + else + return (opnum % 18) + 2 * ((opnum % 18) / 6) + 0x100 * (opnum / 18); + } + + // return an array of operator indices for each channel + struct operator_mapping { uint32_t chan[CHANNELS]; }; + void operator_map(operator_mapping &dest) const; + + // OPL4 apparently can read back FM registers? + uint8_t read(uint16_t index) const { return m_regdata[index]; } + + // handle writes to the register array + bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask); + + // clock the noise and LFO, if present, returning LFO PM value + int32_t clock_noise_and_lfo(); + + // reset the LFO + void reset_lfo() { m_lfo_am_counter = m_lfo_pm_counter = 0; } + + // return the AM offset from LFO for the given channel + // on OPL this is just a fixed value + uint32_t lfo_am_offset(uint32_t choffs) const { (void)choffs; return m_lfo_am; } + + // return LFO/noise states + uint32_t noise_state() const { return m_noise_lfsr >> 23; } + + // caching helpers + void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache); + + // compute the phase step, given a PM value + uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm); + + // log a key-on event + std::string log_keyon(uint32_t choffs, uint32_t opoffs); + + // system-wide registers + uint32_t test() const { return byte(0x01, 0, 8); } + uint32_t waveform_enable() const { return IsOpl2 ? byte(0x01, 5, 1) : (IsOpl3Plus ? 1 : 0); } + uint32_t timer_a_value() const { return byte(0x02, 0, 8) * 4; } // 8->10 bits + uint32_t timer_b_value() const { return byte(0x03, 0, 8); } + uint32_t status_mask() const { return byte(0x04, 0, 8) & 0x78; } + uint32_t irq_reset() const { return byte(0x04, 7, 1); } + uint32_t reset_timer_b() const { return byte(0x04, 7, 1) | byte(0x04, 5, 1); } + uint32_t reset_timer_a() const { return byte(0x04, 7, 1) | byte(0x04, 6, 1); } + uint32_t enable_timer_b() const { return 1; } + uint32_t enable_timer_a() const { return 1; } + uint32_t load_timer_b() const { return byte(0x04, 1, 1); } + uint32_t load_timer_a() const { return byte(0x04, 0, 1); } + uint32_t csm() const { return IsOpl3Plus ? 0 : byte(0x08, 7, 1); } + uint32_t note_select() const { return byte(0x08, 6, 1); } + uint32_t lfo_am_depth() const { return byte(0xbd, 7, 1); } + uint32_t lfo_pm_depth() const { return byte(0xbd, 6, 1); } + uint32_t rhythm_enable() const { return byte(0xbd, 5, 1); } + uint32_t rhythm_keyon() const { return byte(0xbd, 4, 0); } + uint32_t newflag() const { return IsOpl3Plus ? byte(0x105, 0, 1) : 0; } + uint32_t new2flag() const { return IsOpl4Plus ? byte(0x105, 1, 1) : 0; } + uint32_t fourop_enable() const { return IsOpl3Plus ? byte(0x104, 0, 6) : 0; } + + // per-channel registers + uint32_t ch_block_freq(uint32_t choffs) const { return word(0xb0, 0, 5, 0xa0, 0, 8, choffs); } + uint32_t ch_feedback(uint32_t choffs) const { return byte(0xc0, 1, 3, choffs); } + uint32_t ch_algorithm(uint32_t choffs) const { return byte(0xc0, 0, 1, choffs) | (IsOpl3Plus ? (8 | (byte(0xc3, 0, 1, choffs) << 1)) : 0); } + uint32_t ch_output_any(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 4, 4) : 1; } + uint32_t ch_output_0(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 4, 1) : 1; } + uint32_t ch_output_1(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 5, 1) : (IsOpl3Plus ? 1 : 0); } + uint32_t ch_output_2(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 6, 1) : 0; } + uint32_t ch_output_3(uint32_t choffs) const { return newflag() ? byte(0xc0 + choffs, 7, 1) : 0; } + + // per-operator registers + uint32_t op_lfo_am_enable(uint32_t opoffs) const { return byte(0x20, 7, 1, opoffs); } + uint32_t op_lfo_pm_enable(uint32_t opoffs) const { return byte(0x20, 6, 1, opoffs); } + uint32_t op_eg_sustain(uint32_t opoffs) const { return byte(0x20, 5, 1, opoffs); } + uint32_t op_ksr(uint32_t opoffs) const { return byte(0x20, 4, 1, opoffs); } + uint32_t op_multiple(uint32_t opoffs) const { return byte(0x20, 0, 4, opoffs); } + uint32_t op_ksl(uint32_t opoffs) const { uint32_t temp = byte(0x40, 6, 2, opoffs); return bitfield(temp, 1) | (bitfield(temp, 0) << 1); } + uint32_t op_total_level(uint32_t opoffs) const { return byte(0x40, 0, 6, opoffs); } + uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x60, 4, 4, opoffs); } + uint32_t op_decay_rate(uint32_t opoffs) const { return byte(0x60, 0, 4, opoffs); } + uint32_t op_sustain_level(uint32_t opoffs) const { return byte(0x80, 4, 4, opoffs); } + uint32_t op_release_rate(uint32_t opoffs) const { return byte(0x80, 0, 4, opoffs); } + uint32_t op_waveform(uint32_t opoffs) const { return IsOpl2Plus ? byte(0xe0, 0, newflag() ? 3 : 2, opoffs) : 0; } + +protected: + // return a bitfield extracted from a byte + uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const + { + return bitfield(m_regdata[offset + extra_offset], start, count); + } + + // return a bitfield extracted from a pair of bytes, MSBs listed first + uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const + { + return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset); + } + + // helper to determine if the this channel is an active rhythm channel + bool is_rhythm(uint32_t choffs) const + { + return rhythm_enable() && (choffs >= 6 && choffs <= 8); + } + + // internal state + uint16_t m_lfo_am_counter; // LFO AM counter + uint16_t m_lfo_pm_counter; // LFO PM counter + uint32_t m_noise_lfsr; // noise LFSR state + uint8_t m_lfo_am; // current LFO AM value + uint8_t m_regdata[REGISTERS]; // register data + uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms +}; + +using opl_registers = opl_registers_base<1>; +using opl2_registers = opl_registers_base<2>; +using opl3_registers = opl_registers_base<3>; +using opl4_registers = opl_registers_base<4>; + + + +// ======================> opll_registers + +// +// OPLL register map: +// +// System-wide registers: +// 0E --x----- Rhythm enable +// ---x---- Bass drum key on +// ----x--- Snare drum key on +// -----x-- Tom key on +// ------x- Top cymbal key on +// -------x High hat key on +// 0F xxxxxxxx Test register +// +// Per-channel registers (channel in address bits 0-3) +// 10-18 xxxxxxxx F-number (low 8 bits) +// 20-28 --x----- Sustain on +// ---x---- Key on +// --- xxx- Block (octvate, 0-7) +// -------x F-number (high bit) +// 30-38 xxxx---- Instrument selection +// ----xxxx Volume +// +// User instrument registers (for carrier, modulator operators) +// 00-01 x------- AM enable +// -x------ PM enable (VIB) +// --x----- EG type +// ---x---- Key scale rate +// ----xxxx Multiple value (0-15) +// 02 xx------ Key scale level (carrier, 0-3) +// --xxxxxx Total level (modulator, 0-63) +// 03 xx------ Key scale level (modulator, 0-3) +// ---x---- Rectified wave (carrier) +// ----x--- Rectified wave (modulator) +// -----xxx Feedback level for operator 1 (0-7) +// 04-05 xxxx---- Attack rate (0-15) +// ----xxxx Decay rate (0-15) +// 06-07 xxxx---- Sustain level (0-15) +// ----xxxx Release rate (0-15) +// +// Internal (fake) registers: +// 40-48 xxxxxxxx Current instrument base address +// 4E-5F xxxxxxxx Current instrument base address + operator slot (0/1) +// 70-FF xxxxxxxx Data for instruments (1-16 plus 3 drums) +// + +class opll_registers : public fm_registers_base +{ +public: + static constexpr uint32_t OUTPUTS = 2; + static constexpr uint32_t CHANNELS = 9; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + static constexpr uint32_t OPERATORS = CHANNELS * 2; + static constexpr uint32_t WAVEFORMS = 2; + static constexpr uint32_t REGISTERS = 0x40; + static constexpr uint32_t REG_MODE = 0x3f; + static constexpr uint32_t DEFAULT_PRESCALE = 4; + static constexpr uint32_t EG_CLOCK_DIVIDER = 1; + static constexpr uint32_t CSM_TRIGGER_MASK = 0; + static constexpr bool EG_HAS_DEPRESS = true; + static constexpr bool MODULATOR_DELAY = true; + static constexpr uint8_t STATUS_TIMERA = 0; + static constexpr uint8_t STATUS_TIMERB = 0; + static constexpr uint8_t STATUS_BUSY = 0; + static constexpr uint8_t STATUS_IRQ = 0; + + // OPLL-specific constants + static constexpr uint32_t INSTDATA_SIZE = 0x90; + + // constructor + opll_registers(); + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // map channel number to register offset + static constexpr uint32_t channel_offset(uint32_t chnum) + { + assert(chnum < CHANNELS); + return chnum; + } + + // map operator number to register offset + static constexpr uint32_t operator_offset(uint32_t opnum) + { + assert(opnum < OPERATORS); + return opnum; + } + + // return an array of operator indices for each channel + struct operator_mapping { uint32_t chan[CHANNELS]; }; + void operator_map(operator_mapping &dest) const; + + // read a register value + uint8_t read(uint16_t index) const { return m_regdata[index]; } + + // handle writes to the register array + bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask); + + // clock the noise and LFO, if present, returning LFO PM value + int32_t clock_noise_and_lfo(); + + // reset the LFO + void reset_lfo() { m_lfo_am_counter = m_lfo_pm_counter = 0; } + + // return the AM offset from LFO for the given channel + // on OPL this is just a fixed value + uint32_t lfo_am_offset(uint32_t choffs) const { (void)choffs; return m_lfo_am; } + + // return LFO/noise states + uint32_t noise_state() const { return m_noise_lfsr >> 23; } + + // caching helpers + void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache); + + // compute the phase step, given a PM value + uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm); + + // log a key-on event + std::string log_keyon(uint32_t choffs, uint32_t opoffs); + + // set the instrument data + void set_instrument_data(uint8_t const *data) + { + std::copy_n(data, INSTDATA_SIZE, &m_instdata[0]); + } + + // system-wide registers + uint32_t rhythm_enable() const { return byte(0x0e, 5, 1); } + uint32_t rhythm_keyon() const { return byte(0x0e, 4, 0); } + uint32_t test() const { return byte(0x0f, 0, 8); } + uint32_t waveform_enable() const { return 1; } + uint32_t timer_a_value() const { return 0; } + uint32_t timer_b_value() const { return 0; } + uint32_t status_mask() const { return 0; } + uint32_t irq_reset() const { return 0; } + uint32_t reset_timer_b() const { return 0; } + uint32_t reset_timer_a() const { return 0; } + uint32_t enable_timer_b() const { return 0; } + uint32_t enable_timer_a() const { return 0; } + uint32_t load_timer_b() const { return 0; } + uint32_t load_timer_a() const { return 0; } + uint32_t csm() const { return 0; } + + // per-channel registers + uint32_t ch_block_freq(uint32_t choffs) const { return word(0x20, 0, 4, 0x10, 0, 8, choffs); } + uint32_t ch_sustain(uint32_t choffs) const { return byte(0x20, 5, 1, choffs); } + uint32_t ch_total_level(uint32_t choffs) const { return instchbyte(0x02, 0, 6, choffs); } + uint32_t ch_feedback(uint32_t choffs) const { return instchbyte(0x03, 0, 3, choffs); } + uint32_t ch_algorithm(uint32_t choffs) const { (void)choffs; return 0; } + uint32_t ch_instrument(uint32_t choffs) const { return byte(0x30, 4, 4, choffs); } + uint32_t ch_output_any(uint32_t choffs) const { (void)choffs; return 1; } + uint32_t ch_output_0(uint32_t choffs) const { return !is_rhythm(choffs); } + uint32_t ch_output_1(uint32_t choffs) const { return is_rhythm(choffs); } + uint32_t ch_output_2(uint32_t choffs) const { (void)choffs; return 0; } + uint32_t ch_output_3(uint32_t choffs) const { (void)choffs; return 0; } + + // per-operator registers + uint32_t op_lfo_am_enable(uint32_t opoffs) const { return instopbyte(0x00, 7, 1, opoffs); } + uint32_t op_lfo_pm_enable(uint32_t opoffs) const { return instopbyte(0x00, 6, 1, opoffs); } + uint32_t op_eg_sustain(uint32_t opoffs) const { return instopbyte(0x00, 5, 1, opoffs); } + uint32_t op_ksr(uint32_t opoffs) const { return instopbyte(0x00, 4, 1, opoffs); } + uint32_t op_multiple(uint32_t opoffs) const { return instopbyte(0x00, 0, 4, opoffs); } + uint32_t op_ksl(uint32_t opoffs) const { return instopbyte(0x02, 6, 2, opoffs); } + uint32_t op_waveform(uint32_t opoffs) const { return instchbyte(0x03, 3 + bitfield(opoffs, 0), 1, opoffs >> 1); } + uint32_t op_attack_rate(uint32_t opoffs) const { return instopbyte(0x04, 4, 4, opoffs); } + uint32_t op_decay_rate(uint32_t opoffs) const { return instopbyte(0x04, 0, 4, opoffs); } + uint32_t op_sustain_level(uint32_t opoffs) const { return instopbyte(0x06, 4, 4, opoffs); } + uint32_t op_release_rate(uint32_t opoffs) const { return instopbyte(0x06, 0, 4, opoffs); } + uint32_t op_volume(uint32_t opoffs) const { return byte(0x30, 4 * bitfield(~opoffs, 0), 4, opoffs >> 1); } + +private: + // return a bitfield extracted from a byte + uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const + { + return bitfield(m_regdata[offset + extra_offset], start, count); + } + + // return a bitfield extracted from a pair of bytes, MSBs listed first + uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const + { + return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset); + } + + // helpers to read from instrument channel/operator data + uint32_t instchbyte(uint32_t offset, uint32_t start, uint32_t count, uint32_t choffs) const { return bitfield(m_chinst[choffs][offset], start, count); } + uint32_t instopbyte(uint32_t offset, uint32_t start, uint32_t count, uint32_t opoffs) const { return bitfield(m_opinst[opoffs][offset], start, count); } + + // helper to determine if the this channel is an active rhythm channel + bool is_rhythm(uint32_t choffs) const + { + return rhythm_enable() && choffs >= 6; + } + + // internal state + uint16_t m_lfo_am_counter; // LFO AM counter + uint16_t m_lfo_pm_counter; // LFO PM counter + uint32_t m_noise_lfsr; // noise LFSR state + uint8_t m_lfo_am; // current LFO AM value + uint8_t const *m_chinst[CHANNELS]; // pointer to instrument data for each channel + uint8_t const *m_opinst[OPERATORS]; // pointer to instrument data for each operator + uint8_t m_regdata[REGISTERS]; // register data + uint8_t m_instdata[INSTDATA_SIZE]; // instrument data + uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms +}; + + + +//********************************************************* +// OPL IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ym3526 + +class ym3526 +{ +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + + // constructor + ym3526(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); +protected: + // internal state + uint8_t m_address; // address register + fm_engine m_fm; // core FM engine +}; + + +// ======================> y8950 + +class y8950 +{ +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + + static constexpr uint8_t STATUS_ADPCM_B_PLAYING = 0x01; + static constexpr uint8_t STATUS_ADPCM_B_BRDY = 0x08; + static constexpr uint8_t STATUS_ADPCM_B_EOS = 0x10; + static constexpr uint8_t ALL_IRQS = STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_EOS | fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB; + + // constructor + y8950(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint8_t m_address; // address register + uint8_t m_io_ddr; // data direction register for I/O + fm_engine m_fm; // core FM engine + adpcm_b_engine m_adpcm_b; // ADPCM-B engine +}; + + + +//********************************************************* +// OPL2 IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ym3812 + +class ym3812 +{ +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + + // constructor + ym3812(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint8_t m_address; // address register + fm_engine m_fm; // core FM engine +}; + + + +//********************************************************* +// OPL3 IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ymf262 + +class ymf262 +{ +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + + // constructor + ymf262(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint16_t m_address; // address register + fm_engine m_fm; // core FM engine +}; + + +// ======================> ymf289b + +class ymf289b +{ + static constexpr uint8_t STATUS_BUSY_FLAGS = 0x05; + +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = 2; + + // constructor + ymf289b(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal helpers + bool ymf289b_mode() { return ((m_fm.regs().read(0x105) & 0x04) != 0); } + + // internal state + uint16_t m_address; // address register + fm_engine m_fm; // core FM engine +}; + + + +//********************************************************* +// OPL4 IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ymf278b + +class ymf278b +{ + // Using the nominal datasheet frequency of 33.868MHz, the output of the + // chip will be clock/768 = 44.1kHz. However, the FM engine is clocked + // internally at clock/(19*36), or 49.515kHz, so the FM output needs to + // be downsampled. We treat this as needing to clock the FM engine an + // extra tick every few samples. The exact ratio is 768/(19*36) or + // 768/684 = 192/171. So if we always clock the FM once, we'll have + // 192/171 - 1 = 21/171 left. Thus we count 21 for each sample and when + // it gets above 171, we tick an extra time. + static constexpr uint32_t FM_EXTRA_SAMPLE_THRESH = 171; + static constexpr uint32_t FM_EXTRA_SAMPLE_STEP = 192 - FM_EXTRA_SAMPLE_THRESH; + +public: + using fm_engine = fm_engine_base; + static constexpr uint32_t OUTPUTS = 6; + using output_data = ymfm_output; + + static constexpr uint8_t STATUS_BUSY = 0x01; + static constexpr uint8_t STATUS_LD = 0x02; + + // constructor + ymf278b(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return input_clock / 768; } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data_pcm(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write_address_pcm(uint8_t data); + void write_data_pcm(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint16_t m_address; // address register + uint32_t m_fm_pos; // FM resampling position + uint32_t m_load_remaining; // how many more samples until LD flag clears + bool m_next_status_id; // flag to track which status ID to return + fm_engine m_fm; // core FM engine + pcm_engine m_pcm; // core PCM engine +}; + + + +//********************************************************* +// OPLL IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> opll_base + +class opll_base +{ +public: + using fm_engine = fm_engine_base; + using output_data = fm_engine::output_data; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + + // constructor + opll_base(ymfm_interface &intf, uint8_t const *data); + + // configuration + void set_instrument_data(uint8_t const *data) { m_fm.regs().set_instrument_data(data); } + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access -- doesn't really have any, but provide these for consistency + uint8_t read_status() { return 0x00; } + uint8_t read(uint32_t offset) { (void)offset; return 0x00; } + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint8_t m_address; // address register + fm_engine m_fm; // core FM engine +}; + + +// ======================> ym2413 + +class ym2413 : public opll_base +{ +public: + // constructor + ym2413(ymfm_interface &intf, uint8_t const *instrument_data = nullptr); + +private: + // internal state + static uint8_t const s_default_instruments[]; +}; + + +// ======================> ym2413 + +class ym2423 : public opll_base +{ +public: + // constructor + ym2423(ymfm_interface &intf, uint8_t const *instrument_data = nullptr); + +private: + // internal state + static uint8_t const s_default_instruments[]; +}; + + +// ======================> ymf281 + +class ymf281 : public opll_base +{ +public: + // constructor + ymf281(ymfm_interface &intf, uint8_t const *instrument_data = nullptr); + +private: + // internal state + static uint8_t const s_default_instruments[]; +}; + + +// ======================> ds1001 + +class ds1001 : public opll_base +{ +public: + // constructor + ds1001(ymfm_interface &intf, uint8_t const *instrument_data = nullptr); + +private: + // internal state + static uint8_t const s_default_instruments[]; +}; + +} + +#endif // YMFM_OPL_H diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_pcm.cpp b/thirdparty/adlmidi/chips/ymfm/ymfm_pcm.cpp new file mode 100644 index 000000000..34417490c --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_pcm.cpp @@ -0,0 +1,714 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +#include "ymfm_pcm.h" +#include "ymfm_fm.h" +#include "ymfm_fm.ipp" + +namespace ymfm +{ + +//********************************************************* +// PCM REGISTERS +//********************************************************* + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void pcm_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); + m_regdata[0xf8] = 0x1b; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_regdata); +} + + +//------------------------------------------------- +// cache_channel_data - update the cache with +// data from the registers +//------------------------------------------------- + +void pcm_registers::cache_channel_data(uint32_t choffs, pcm_cache &cache) +{ + // compute step from octave and fnumber; the math here implies + // a .18 fraction but .16 should be perfectly fine + int32_t octave = int8_t(ch_octave(choffs) << 4) >> 4; + uint32_t fnum = ch_fnumber(choffs); + cache.step = ((0x400 | fnum) << (octave + 7)) >> 2; + + // total level is computed as a .10 value for interpolation + cache.total_level = ch_total_level(choffs) << 10; + + // compute panning values in terms of envelope attenuation + int32_t panpot = int8_t(ch_panpot(choffs) << 4) >> 4; + if (panpot >= 0) + { + cache.pan_left = (panpot == 7) ? 0x3ff : 0x20 * panpot; + cache.pan_right = 0; + } + else if (panpot >= -7) + { + cache.pan_left = 0; + cache.pan_right = (panpot == -7) ? 0x3ff : -0x20 * panpot; + } + else + cache.pan_left = cache.pan_right = 0x3ff; + + // determine the LFO stepping value; this how much to add to a running + // x.18 value for the LFO; steps were derived from frequencies in the + // manual and come out very close with these values + static const uint8_t s_lfo_steps[8] = { 1, 12, 19, 25, 31, 35, 37, 42 }; + cache.lfo_step = s_lfo_steps[ch_lfo_speed(choffs)]; + + // AM LFO depth values, derived from the manual; note each has at most + // 2 bits to make the "multiply" easy in hardware + static const uint8_t s_am_depth[8] = { 0, 0x14, 0x20, 0x28, 0x30, 0x40, 0x50, 0x80 }; + cache.am_depth = s_am_depth[ch_am_depth(choffs)]; + + // PM LFO depth values; these are converted from the manual's cents values + // into f-numbers; the computations come out quite cleanly so pretty sure + // these are correct + static const uint8_t s_pm_depth[8] = { 0, 2, 3, 4, 6, 12, 24, 48 }; + cache.pm_depth = s_pm_depth[ch_vibrato(choffs)]; + + // 4-bit sustain level, but 15 means 31 so effectively 5 bits + cache.eg_sustain = ch_sustain_level(choffs); + cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10; + cache.eg_sustain <<= 5; + + // compute the key scaling correction factor; 15 means don't do any correction + int32_t correction = ch_rate_correction(choffs); + if (correction == 15) + correction = 0; + else + correction = (octave + correction) * 2 + bitfield(fnum, 9); + + // compute the envelope generator rates + cache.eg_rate[EG_ATTACK] = effective_rate(ch_attack_rate(choffs), correction); + cache.eg_rate[EG_DECAY] = effective_rate(ch_decay_rate(choffs), correction); + cache.eg_rate[EG_SUSTAIN] = effective_rate(ch_sustain_rate(choffs), correction); + cache.eg_rate[EG_RELEASE] = effective_rate(ch_release_rate(choffs), correction); + cache.eg_rate[EG_REVERB] = 5; + + // if damping is on, override some things; essentially decay at a hardcoded + // rate of 48 until -12db (0x80), then at maximum rate for the rest + if (ch_damp(choffs) != 0) + { + cache.eg_rate[EG_DECAY] = 48; + cache.eg_rate[EG_SUSTAIN] = 63; + cache.eg_rate[EG_RELEASE] = 63; + cache.eg_sustain = 0x80; + } +} + + +//------------------------------------------------- +// effective_rate - return the effective rate, +// clamping and applying corrections as needed +//------------------------------------------------- + +uint32_t pcm_registers::effective_rate(uint32_t raw, uint32_t correction) +{ + // raw rates of 0 and 15 just pin to min/max + if (raw == 0) + return 0; + if (raw == 15) + return 63; + + // otherwise add the correction and clamp to range + return clamp(raw * 4 + correction, 0, 63); +} + + + +//********************************************************* +// PCM CHANNEL +//********************************************************* + +//------------------------------------------------- +// pcm_channel - constructor +//------------------------------------------------- + +pcm_channel::pcm_channel(pcm_engine &owner, uint32_t choffs) : + m_choffs(choffs), + m_baseaddr(0), + m_endpos(0), + m_looppos(0), + m_curpos(0), + m_nextpos(0), + m_lfo_counter(0), + m_eg_state(EG_RELEASE), + m_env_attenuation(0x3ff), + m_total_level(0x7f << 10), + m_format(0), + m_key_state(0), + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +void pcm_channel::reset() +{ + m_baseaddr = 0; + m_endpos = 0; + m_looppos = 0; + m_curpos = 0; + m_nextpos = 0; + m_lfo_counter = 0; + m_eg_state = EG_RELEASE; + m_env_attenuation = 0x3ff; + m_total_level = 0x7f << 10; + m_format = 0; + m_key_state = 0; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_channel::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_baseaddr); + state.save_restore(m_endpos); + state.save_restore(m_looppos); + state.save_restore(m_curpos); + state.save_restore(m_nextpos); + state.save_restore(m_lfo_counter); + state.save_restore(m_eg_state); + state.save_restore(m_env_attenuation); + state.save_restore(m_total_level); + state.save_restore(m_format); + state.save_restore(m_key_state); +} + + +//------------------------------------------------- +// prepare - prepare for clocking +//------------------------------------------------- + +bool pcm_channel::prepare() +{ + // cache the data + m_regs.cache_channel_data(m_choffs, m_cache); + + // clock the key state + if ((m_key_state & KEY_PENDING) != 0) + { + uint8_t oldstate = m_key_state; + m_key_state = (m_key_state >> 1) & KEY_ON; + if (((oldstate ^ m_key_state) & KEY_ON) != 0) + { + if ((m_key_state & KEY_ON) != 0) + start_attack(); + else + start_release(); + } + } + + // set the total level directly if not interpolating + if (m_regs.ch_level_direct(m_choffs)) + m_total_level = m_cache.total_level; + + // we're active until we're quiet after the release + return (m_eg_state < EG_RELEASE || m_env_attenuation < EG_QUIET); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void pcm_channel::clock(uint32_t env_counter) +{ + // clock the LFO, which is an x.18 value incremented based on the + // LFO speed value + m_lfo_counter += m_cache.lfo_step; + + // clock the envelope + clock_envelope(env_counter); + + // determine the step after applying vibrato + uint32_t step = m_cache.step; + if (m_cache.pm_depth != 0) + { + // shift the LFO by 1/4 cycle for PM so that it starts at 0 + uint32_t lfo_shifted = m_lfo_counter + (1 << 16); + int32_t lfo_value = bitfield(lfo_shifted, 10, 7); + if (bitfield(lfo_shifted, 17) != 0) + lfo_value ^= 0x7f; + lfo_value -= 0x40; + step += (lfo_value * int32_t(m_cache.pm_depth)) >> 7; + } + + // advance the sample step and loop as needed + m_curpos = m_nextpos; + m_nextpos = m_curpos + step; + if (m_nextpos >= m_endpos) + m_nextpos += m_looppos - m_endpos; + + // interpolate total level if needed + if (m_total_level != m_cache.total_level) + { + // max->min volume takes 156.4ms, or pretty close to 19/1024 per 44.1kHz sample + // min->max volume is half that, so advance by 38/1024 per sample + if (m_total_level < m_cache.total_level) + m_total_level = std::min(m_total_level + 19, m_cache.total_level); + else + m_total_level = std::max(m_total_level - 38, m_cache.total_level); + } +} + + +//------------------------------------------------- +// output - return the computed output value, with +// panning applied +//------------------------------------------------- + +void pcm_channel::output(output_data &output) const +{ + // early out if the envelope is effectively off + uint32_t envelope = m_env_attenuation; + if (envelope > EG_QUIET) + return; + + // add in LFO AM modulation + if (m_cache.am_depth != 0) + { + uint32_t lfo_value = bitfield(m_lfo_counter, 10, 7); + if (bitfield(m_lfo_counter, 17) != 0) + lfo_value ^= 0x7f; + envelope += (lfo_value * m_cache.am_depth) >> 7; + } + + // add in the current interpolated total level value, which is a .10 + // value shifted left by 2 + envelope += m_total_level >> 8; + + // add in panning effect and clamp + uint32_t lenv = std::min(envelope + m_cache.pan_left, 0x3ff); + uint32_t renv = std::min(envelope + m_cache.pan_right, 0x3ff); + + // convert to volume as a .11 fraction + int32_t lvol = attenuation_to_volume(lenv << 2); + int32_t rvol = attenuation_to_volume(renv << 2); + + // fetch current sample and add + int16_t sample = fetch_sample(); + uint32_t outnum = m_regs.ch_output_channel(m_choffs) * 2; + output.data[outnum + 0] += (lvol * sample) >> 15; + output.data[outnum + 1] += (rvol * sample) >> 15; +} + + +//------------------------------------------------- +// keyonoff - signal key on/off +//------------------------------------------------- + +void pcm_channel::keyonoff(bool on) +{ + // mark the key state as pending + m_key_state |= KEY_PENDING | (on ? KEY_PENDING_ON : 0); + + // don't log masked channels + if ((m_key_state & (KEY_PENDING_ON | KEY_ON)) == KEY_PENDING_ON && ((debug::GLOBAL_PCM_CHANNEL_MASK >> m_choffs) & 1) != 0) + { + debug::log_keyon("KeyOn PCM-%02d: num=%3d oct=%2d fnum=%03X level=%02X%c ADSR=%X/%X/%X/%X SL=%X", + m_choffs, + m_regs.ch_wave_table_num(m_choffs), + int8_t(m_regs.ch_octave(m_choffs) << 4) >> 4, + m_regs.ch_fnumber(m_choffs), + m_regs.ch_total_level(m_choffs), + m_regs.ch_level_direct(m_choffs) ? '!' : '/', + m_regs.ch_attack_rate(m_choffs), + m_regs.ch_decay_rate(m_choffs), + m_regs.ch_sustain_rate(m_choffs), + m_regs.ch_release_rate(m_choffs), + m_regs.ch_sustain_level(m_choffs)); + + if (m_regs.ch_rate_correction(m_choffs) != 15) + debug::log_keyon(" RC=%X", m_regs.ch_rate_correction(m_choffs)); + + if (m_regs.ch_pseudo_reverb(m_choffs) != 0) + debug::log_keyon(" %s", "REV"); + if (m_regs.ch_damp(m_choffs) != 0) + debug::log_keyon(" %s", "DAMP"); + + if (m_regs.ch_vibrato(m_choffs) != 0 || m_regs.ch_am_depth(m_choffs) != 0) + { + if (m_regs.ch_vibrato(m_choffs) != 0) + debug::log_keyon(" VIB=%d", m_regs.ch_vibrato(m_choffs)); + if (m_regs.ch_am_depth(m_choffs) != 0) + debug::log_keyon(" AM=%d", m_regs.ch_am_depth(m_choffs)); + debug::log_keyon(" LFO=%d", m_regs.ch_lfo_speed(m_choffs)); + } + debug::log_keyon("%s", "\n"); + } +} + + +//------------------------------------------------- +// load_wavetable - load a wavetable by fetching +// its data from external memory +//------------------------------------------------- + +void pcm_channel::load_wavetable() +{ + // determine the address of the wave table header + uint32_t wavnum = m_regs.ch_wave_table_num(m_choffs); + uint32_t wavheader = 12 * wavnum; + + // above 384 it may be in a different bank + if (wavnum >= 384) + { + uint32_t bank = m_regs.wave_table_header(); + if (bank != 0) + wavheader = 512*1024 * bank + (wavnum - 384) * 12; + } + + // fetch the 22-bit base address and 2-bit format + uint8_t byte = read_pcm(wavheader + 0); + m_format = bitfield(byte, 6, 2); + m_baseaddr = bitfield(byte, 0, 6) << 16; + m_baseaddr |= read_pcm(wavheader + 1) << 8; + m_baseaddr |= read_pcm(wavheader + 2) << 0; + + // fetch the 16-bit loop position + m_looppos = read_pcm(wavheader + 3) << 8; + m_looppos |= read_pcm(wavheader + 4); + m_looppos <<= 16; + + // fetch the 16-bit end position, which is stored as a negative value + // for some reason that is unclear + m_endpos = read_pcm(wavheader + 5) << 8; + m_endpos |= read_pcm(wavheader + 6); + m_endpos = -int32_t(m_endpos) << 16; + + // remaining data values set registers + m_owner.write(0x80 + m_choffs, read_pcm(wavheader + 7)); + m_owner.write(0x98 + m_choffs, read_pcm(wavheader + 8)); + m_owner.write(0xb0 + m_choffs, read_pcm(wavheader + 9)); + m_owner.write(0xc8 + m_choffs, read_pcm(wavheader + 10)); + m_owner.write(0xe0 + m_choffs, read_pcm(wavheader + 11)); + + // reset the envelope so we don't continue playing mid-sample from previous key ons + m_env_attenuation = 0x3ff; +} + + +//------------------------------------------------- +// read_pcm - read a byte from the external PCM +// memory interface +//------------------------------------------------- + +uint8_t pcm_channel::read_pcm(uint32_t address) const +{ + return m_owner.intf().ymfm_external_read(ACCESS_PCM, address); +} + + +//------------------------------------------------- +// start_attack - start the attack phase +//------------------------------------------------- + +void pcm_channel::start_attack() +{ + // don't change anything if already in attack state + if (m_eg_state == EG_ATTACK) + return; + m_eg_state = EG_ATTACK; + + // reset the LFO if requested + if (m_regs.ch_lfo_reset(m_choffs)) + m_lfo_counter = 0; + + // if the attack rate == 63 then immediately go to max attenuation + if (m_cache.eg_rate[EG_ATTACK] == 63) + m_env_attenuation = 0; + + // reset the positions + m_curpos = m_nextpos = 0; +} + + +//------------------------------------------------- +// start_release - start the release phase +//------------------------------------------------- + +void pcm_channel::start_release() +{ + // don't change anything if already in release or reverb state + if (m_eg_state >= EG_RELEASE) + return; + m_eg_state = EG_RELEASE; +} + + +//------------------------------------------------- +// clock_envelope - clock the envelope generator +//------------------------------------------------- + +void pcm_channel::clock_envelope(uint32_t env_counter) +{ + // handle attack->decay transitions + if (m_eg_state == EG_ATTACK && m_env_attenuation == 0) + m_eg_state = EG_DECAY; + + // handle decay->sustain transitions + if (m_eg_state == EG_DECAY && m_env_attenuation >= m_cache.eg_sustain) + m_eg_state = EG_SUSTAIN; + + // fetch the appropriate 6-bit rate value from the cache + uint32_t rate = m_cache.eg_rate[m_eg_state]; + + // compute the rate shift value; this is the shift needed to + // apply to the env_counter such that it becomes a 5.11 fixed + // point number + uint32_t rate_shift = rate >> 2; + env_counter <<= rate_shift; + + // see if the fractional part is 0; if not, it's not time to clock + if (bitfield(env_counter, 0, 11) != 0) + return; + + // determine the increment based on the non-fractional part of env_counter + uint32_t relevant_bits = bitfield(env_counter, (rate_shift <= 11) ? 11 : rate_shift, 3); + uint32_t increment = attenuation_increment(rate, relevant_bits); + + // attack is the only one that increases + if (m_eg_state == EG_ATTACK) + m_env_attenuation += (~m_env_attenuation * increment) >> 4; + + // all other cases are similar + else + { + // apply the increment + m_env_attenuation += increment; + + // clamp the final attenuation + if (m_env_attenuation >= 0x400) + m_env_attenuation = 0x3ff; + + // transition to reverb at -18dB if enabled + if (m_env_attenuation >= 0xc0 && m_eg_state < EG_REVERB && m_regs.ch_pseudo_reverb(m_choffs)) + m_eg_state = EG_REVERB; + } +} + + +//------------------------------------------------- +// fetch_sample - fetch a sample at the current +// position +//------------------------------------------------- + +int16_t pcm_channel::fetch_sample() const +{ + uint32_t addr = m_baseaddr; + uint32_t pos = m_curpos >> 16; + + // 8-bit PCM: shift up by 8 + if (m_format == 0) + return read_pcm(addr + pos) << 8; + + // 16-bit PCM: assemble from 2 halves + if (m_format == 2) + { + addr += pos * 2; + return (read_pcm(addr) << 8) | read_pcm(addr + 1); + } + + // 12-bit PCM: assemble out of half of 3 bytes + addr += (pos / 2) * 3; + if ((pos & 1) == 0) + return (read_pcm(addr + 0) << 8) | ((read_pcm(addr + 1) << 4) & 0xf0); + else + return (read_pcm(addr + 2) << 8) | ((read_pcm(addr + 1) << 0) & 0xf0); +} + + + +//********************************************************* +// PCM ENGINE +//********************************************************* + +//------------------------------------------------- +// pcm_engine - constructor +//------------------------------------------------- + +pcm_engine::pcm_engine(ymfm_interface &intf) : + m_intf(intf), + m_env_counter(0), + m_modified_channels(ALL_CHANNELS), + m_active_channels(ALL_CHANNELS) +{ + // create the channels + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum] = std::make_unique(*this, chnum); +} + + +//------------------------------------------------- +// reset - reset the engine state +//------------------------------------------------- + +void pcm_engine::reset() +{ + // reset register state + m_regs.reset(); + + // reset each channel + for (auto &chan : m_channel) + chan->reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_engine::save_restore(ymfm_saved_state &state) +{ + // save our data + state.save_restore(m_env_counter); + + // save channel state + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum]->save_restore(state); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void pcm_engine::clock(uint32_t chanmask) +{ + // if something was modified, prepare + // also prepare every 4k samples to catch ending notes + if (m_modified_channels != 0 || m_prepare_count++ >= 4096) + { + // call each channel to prepare + m_active_channels = 0; + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + if (m_channel[chnum]->prepare()) + m_active_channels |= 1 << chnum; + + // reset the modified channels and prepare count + m_modified_channels = m_prepare_count = 0; + } + + // increment the envelope counter; the envelope generator + // only clocks every other sample in order to make the PCM + // envelopes line up with the FM envelopes (after taking into + // account the different FM sampling rate) + m_env_counter++; + + // now update the state of all the channels and operators + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->clock(m_env_counter >> 1); +} + + +//------------------------------------------------- +// update - master update function +//------------------------------------------------- + +void pcm_engine::output(output_data &output, uint32_t chanmask) +{ + // mask out some channels for debug purposes + chanmask &= debug::GLOBAL_PCM_CHANNEL_MASK; + + // compute the output of each channel + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->output(output); +} + + +//------------------------------------------------- +// read - handle reads from the PCM registers +//------------------------------------------------- + +uint8_t pcm_engine::read(uint32_t regnum) +{ + // handle reads from the data register + if (regnum == 0x06 && m_regs.memory_access_mode() != 0) + return m_intf.ymfm_external_read(ACCESS_PCM, m_regs.memory_address_autoinc()); + + return m_regs.read(regnum); +} + + +//------------------------------------------------- +// write - handle writes to the PCM registers +//------------------------------------------------- + +void pcm_engine::write(uint32_t regnum, uint8_t data) +{ + // handle reads to the data register + if (regnum == 0x06 && m_regs.memory_access_mode() != 0) + { + m_intf.ymfm_external_write(ACCESS_PCM, m_regs.memory_address_autoinc(), data); + return; + } + + // for now just mark all channels as modified + m_modified_channels = ALL_CHANNELS; + + // most writes are passive, consumed only when needed + m_regs.write(regnum, data); + + // however, process keyons immediately + if (regnum >= 0x68 && regnum <= 0x7f) + m_channel[regnum - 0x68]->keyonoff(bitfield(data, 7)); + + // and also wavetable writes + else if (regnum >= 0x08 && regnum <= 0x1f) + m_channel[regnum - 0x08]->load_wavetable(); +} + +} diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_pcm.h b/thirdparty/adlmidi/chips/ymfm/ymfm_pcm.h new file mode 100644 index 000000000..b471fa611 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_pcm.h @@ -0,0 +1,347 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_PCM_H +#define YMFM_PCM_H + +#pragma once + +#include "ymfm.h" + +namespace ymfm +{ + +/* +Note to self: Sega "Multi-PCM" is almost identical to this + +28 channels + +Writes: +00 = data reg, causes write +01 = target slot = data - (data / 8) +02 = address (clamped to 7) + +Slot data (registers with ADSR/KSR seem to be inaccessible): +0: xxxx---- panpot +1: xxxxxxxx wavetable low +2: xxxxxx-- pitch low + -------x wavetable high +3: xxxx---- octave + ----xxxx pitch hi +4: x------- key on +5: xxxxxxx- total level + -------x level direct (0=interpolate) +6: --xxx--- LFO frequency + -----xxx PM sensitivity +7: -----xxx AM sensitivity + +Sample data: ++00: start hi ++01: start mid ++02: start low ++03: loop hi ++04: loop low ++05: -end hi ++06: -end low ++07: vibrato (reg 6) ++08: attack/decay ++09: sustain level/rate ++0A: ksr/release ++0B: LFO amplitude (reg 7) + +*/ + +//********************************************************* +// INTERFACE CLASSES +//********************************************************* + +class pcm_engine; + + +// ======================> pcm_cache + +// this class holds data that is computed once at the start of clocking +// and remains static during subsequent sound generation +struct pcm_cache +{ + uint32_t step; // sample position step, as a .16 value + uint32_t total_level; // target total level, as a .10 value + uint32_t pan_left; // left panning attenuation + uint32_t pan_right; // right panning attenuation + uint32_t eg_sustain; // sustain level, shifted up to envelope values + uint8_t eg_rate[EG_STATES]; // envelope rate, including KSR + uint8_t lfo_step; // stepping value for LFO + uint8_t am_depth; // scale value for AM LFO + uint8_t pm_depth; // scale value for PM LFO +}; + + +// ======================> pcm_registers + +// +// PCM register map: +// +// System-wide registers: +// 00-01 xxxxxxxx LSI Test +// 02 -------x Memory access mode (0=sound gen, 1=read/write) +// ------x- Memory type (0=ROM, 1=ROM+SRAM) +// ---xxx-- Wave table header +// xxx----- Device ID (=1 for YMF278B) +// 03 --xxxxxx Memory address high +// 04 xxxxxxxx Memory address mid +// 05 xxxxxxxx Memory address low +// 06 xxxxxxxx Memory data +// F8 --xxx--- Mix control (FM_R) +// -----xxx Mix control (FM_L) +// F9 --xxx--- Mix control (PCM_R) +// -----xxx Mix control (PCM_L) +// +// Channel-specific registers: +// 08-1F xxxxxxxx Wave table number low +// 20-37 -------x Wave table number high +// xxxxxxx- F-number low +// 38-4F -----xxx F-number high +// ----x--- Pseudo-reverb +// xxxx---- Octave +// 50-67 xxxxxxx- Total level +// -------x Level direct +// 68-7F x------- Key on +// -x------ Damp +// --x----- LFO reset +// ---x---- Output channel +// ----xxxx Panpot +// 80-97 --xxx--- LFO speed +// -----xxx Vibrato +// 98-AF xxxx---- Attack rate +// ----xxxx Decay rate +// B0-C7 xxxx---- Sustain level +// ----xxxx Sustain rate +// C8-DF xxxx---- Rate correction +// ----xxxx Release rate +// E0-F7 -----xxx AM depth + +class pcm_registers +{ +public: + // constants + static constexpr uint32_t OUTPUTS = 4; + static constexpr uint32_t CHANNELS = 24; + static constexpr uint32_t REGISTERS = 0x100; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + + // constructor + pcm_registers() { } + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset to initial state + void reset(); + + // update cache information + void cache_channel_data(uint32_t choffs, pcm_cache &cache); + + // direct read/write access + uint8_t read(uint32_t index ) { return m_regdata[index]; } + void write(uint32_t index, uint8_t data) { m_regdata[index] = data; } + + // system-wide registers + uint32_t memory_access_mode() const { return bitfield(m_regdata[0x02], 0); } + uint32_t memory_type() const { return bitfield(m_regdata[0x02], 1); } + uint32_t wave_table_header() const { return bitfield(m_regdata[0x02], 2, 3); } + uint32_t device_id() const { return bitfield(m_regdata[0x02], 5, 3); } + uint32_t memory_address() const { return (bitfield(m_regdata[0x03], 0, 6) << 16) | (m_regdata[0x04] << 8) | m_regdata[0x05]; } + uint32_t memory_data() const { return m_regdata[0x06]; } + uint32_t mix_fm_r() const { return bitfield(m_regdata[0xf8], 3, 3); } + uint32_t mix_fm_l() const { return bitfield(m_regdata[0xf8], 0, 3); } + uint32_t mix_pcm_r() const { return bitfield(m_regdata[0xf9], 3, 3); } + uint32_t mix_pcm_l() const { return bitfield(m_regdata[0xf9], 0, 3); } + + // per-channel registers + uint32_t ch_wave_table_num(uint32_t choffs) const { return m_regdata[choffs + 0x08] | (bitfield(m_regdata[choffs + 0x20], 0) << 8); } + uint32_t ch_fnumber(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x20], 1, 7) | (bitfield(m_regdata[choffs + 0x38], 0, 3) << 7); } + uint32_t ch_pseudo_reverb(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 3); } + uint32_t ch_octave(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 4, 4); } + uint32_t ch_total_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 1, 7); } + uint32_t ch_level_direct(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 0); } + uint32_t ch_keyon(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 7); } + uint32_t ch_damp(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 6); } + uint32_t ch_lfo_reset(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 5); } + uint32_t ch_output_channel(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 4); } + uint32_t ch_panpot(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 0, 4); } + uint32_t ch_lfo_speed(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 3, 3); } + uint32_t ch_vibrato(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 0, 3); } + uint32_t ch_attack_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 4, 4); } + uint32_t ch_decay_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 0, 4); } + uint32_t ch_sustain_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 4, 4); } + uint32_t ch_sustain_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 0, 4); } + uint32_t ch_rate_correction(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 4, 4); } + uint32_t ch_release_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 0, 4); } + uint32_t ch_am_depth(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xe0], 0, 3); } + + // return the memory address and increment it + uint32_t memory_address_autoinc() + { + uint32_t result = memory_address(); + uint32_t newval = result + 1; + m_regdata[0x05] = newval >> 0; + m_regdata[0x04] = newval >> 8; + m_regdata[0x03] = (newval >> 16) & 0x3f; + return result; + } + +private: + // internal helpers + uint32_t effective_rate(uint32_t raw, uint32_t correction); + + // internal state + uint8_t m_regdata[REGISTERS]; // register data +}; + + +// ======================> pcm_channel + +class pcm_channel +{ + static constexpr uint8_t KEY_ON = 0x01; + static constexpr uint8_t KEY_PENDING_ON = 0x02; + static constexpr uint8_t KEY_PENDING = 0x04; + + // "quiet" value, used to optimize when we can skip doing working + static constexpr uint32_t EG_QUIET = 0x200; + +public: + using output_data = ymfm_output; + + // constructor + pcm_channel(pcm_engine &owner, uint32_t choffs); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset the channel state + void reset(); + + // return the channel offset + uint32_t choffs() const { return m_choffs; } + + // prepare prior to clocking + bool prepare(); + + // master clocking function + void clock(uint32_t env_counter); + + // return the computed output value, with panning applied + void output(output_data &output) const; + + // signal key on/off + void keyonoff(bool on); + + // load a new wavetable entry + void load_wavetable(); + +private: + // internal helpers + void start_attack(); + void start_release(); + void clock_envelope(uint32_t env_counter); + int16_t fetch_sample() const; + uint8_t read_pcm(uint32_t address) const; + + // internal state + uint32_t const m_choffs; // channel offset + uint32_t m_baseaddr; // base address + uint32_t m_endpos; // ending position + uint32_t m_looppos; // loop position + uint32_t m_curpos; // current position + uint32_t m_nextpos; // next position + uint32_t m_lfo_counter; // LFO counter + envelope_state m_eg_state; // envelope state + uint16_t m_env_attenuation; // computed envelope attenuation + uint32_t m_total_level; // total level with as 7.10 for interp + uint8_t m_format; // sample format + uint8_t m_key_state; // current key state + pcm_cache m_cache; // cached data + pcm_registers &m_regs; // reference to registers + pcm_engine &m_owner; // reference to our owner +}; + + +// ======================> pcm_engine + +class pcm_engine +{ +public: + static constexpr int OUTPUTS = pcm_registers::OUTPUTS; + static constexpr int CHANNELS = pcm_registers::CHANNELS; + static constexpr uint32_t ALL_CHANNELS = pcm_registers::ALL_CHANNELS; + using output_data = pcm_channel::output_data; + + // constructor + pcm_engine(ymfm_interface &intf); + + // reset our status + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // master clocking function + void clock(uint32_t chanmask); + + // compute sum of channel outputs + void output(output_data &output, uint32_t chanmask); + + // read from the PCM registers + uint8_t read(uint32_t regnum); + + // write to the PCM registers + void write(uint32_t regnum, uint8_t data); + + // return a reference to our interface + ymfm_interface &intf() { return m_intf; } + + // return a reference to our registers + pcm_registers ®s() { return m_regs; } + +private: + // internal state + ymfm_interface &m_intf; // reference to the interface + uint32_t m_env_counter; // envelope counter + uint32_t m_modified_channels; // bitmask of modified channels + uint32_t m_active_channels; // bitmask of active channels + uint32_t m_prepare_count; // counter to do periodic prepare sweeps + std::unique_ptr m_channel[CHANNELS]; // array of channels + pcm_registers m_regs; // registers +}; + +} + +#endif // YMFM_PCM_H diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_ssg.cpp b/thirdparty/adlmidi/chips/ymfm/ymfm_ssg.cpp new file mode 100644 index 000000000..1c477d0de --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_ssg.cpp @@ -0,0 +1,279 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +#include "ymfm_ssg.h" + +namespace ymfm +{ + +//********************************************************* +// SSG REGISTERS +//********************************************************* + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void ssg_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ssg_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_regdata); +} + + + +//********************************************************* +// SSG ENGINE +//********************************************************* + +//------------------------------------------------- +// ssg_engine - constructor +//------------------------------------------------- + +ssg_engine::ssg_engine(ymfm_interface &intf) : + m_intf(intf), + m_tone_count{ 0,0,0 }, + m_tone_state{ 0,0,0 }, + m_envelope_count(0), + m_envelope_state(0), + m_noise_count(0), + m_noise_state(1), + m_override(nullptr) +{ +} + + +//------------------------------------------------- +// reset - reset the engine state +//------------------------------------------------- + +void ssg_engine::reset() +{ + // defer to the override if present + if (m_override != nullptr) + return m_override->ssg_reset(); + + // reset register state + m_regs.reset(); + + // reset engine state + for (int chan = 0; chan < 3; chan++) + { + m_tone_count[chan] = 0; + m_tone_state[chan] = 0; + } + m_envelope_count = 0; + m_envelope_state = 0; + m_noise_count = 0; + m_noise_state = 1; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ssg_engine::save_restore(ymfm_saved_state &state) +{ + // save register state + m_regs.save_restore(state); + + // save engine state + state.save_restore(m_tone_count); + state.save_restore(m_tone_state); + state.save_restore(m_envelope_count); + state.save_restore(m_envelope_state); + state.save_restore(m_noise_count); + state.save_restore(m_noise_state); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void ssg_engine::clock() +{ + // clock tones; tone period units are clock/16 but since we run at clock/8 + // that works out for us to toggle the state (50% duty cycle) at twice the + // programmed period + for (int chan = 0; chan < 3; chan++) + { + m_tone_count[chan]++; + if (m_tone_count[chan] >= m_regs.ch_tone_period(chan)) + { + m_tone_state[chan] ^= 1; + m_tone_count[chan] = 0; + } + } + + // clock noise; noise period units are clock/16 but since we run at clock/8, + // our counter needs a right shift prior to compare; note that a period of 0 + // should produce an indentical result to a period of 1, so add a special + // check against that case + m_noise_count++; + if ((m_noise_count >> 1) >= m_regs.noise_period() && m_noise_count != 1) + { + m_noise_state ^= (bitfield(m_noise_state, 0) ^ bitfield(m_noise_state, 3)) << 17; + m_noise_state >>= 1; + m_noise_count = 0; + } + + // clock envelope; envelope period units are clock/8 (manual says clock/256 + // but that's for all 32 steps) + m_envelope_count++; + if (m_envelope_count >= m_regs.envelope_period()) + { + m_envelope_state++; + m_envelope_count = 0; + } +} + + +//------------------------------------------------- +// output - output the current state +//------------------------------------------------- + +void ssg_engine::output(output_data &output) +{ + // volume to amplitude table, taken from MAME's implementation but biased + // so that 0 == 0 + static int16_t const s_amplitudes[32] = + { + 0, 32, 78, 141, 178, 222, 262, 306, + 369, 441, 509, 585, 701, 836, 965, 1112, + 1334, 1595, 1853, 2146, 2576, 3081, 3576, 4135, + 5000, 6006, 7023, 8155, 9963,11976,14132,16382 + }; + + // compute the envelope volume + uint32_t envelope_volume; + if ((m_regs.envelope_hold() | (m_regs.envelope_continue() ^ 1)) && m_envelope_state >= 32) + { + m_envelope_state = 32; + envelope_volume = ((m_regs.envelope_attack() ^ m_regs.envelope_alternate()) & m_regs.envelope_continue()) ? 31 : 0; + } + else + { + uint32_t attack = m_regs.envelope_attack(); + if (m_regs.envelope_alternate()) + attack ^= bitfield(m_envelope_state, 5); + envelope_volume = (m_envelope_state & 31) ^ (attack ? 0 : 31); + } + + // iterate over channels + for (int chan = 0; chan < 3; chan++) + { + // noise depends on the noise state, which is the LSB of m_noise_state + uint32_t noise_on = m_regs.ch_noise_enable_n(chan) | m_noise_state; + + // tone depends on the current tone state + uint32_t tone_on = m_regs.ch_tone_enable_n(chan) | m_tone_state[chan]; + + // if neither tone nor noise enabled, return 0 + uint32_t volume; + if ((noise_on & tone_on) == 0) + volume = 0; + + // if the envelope is enabled, use its amplitude + else if (m_regs.ch_envelope_enable(chan)) + volume = envelope_volume; + + // otherwise, scale the tone amplitude up to match envelope values + // according to the datasheet, amplitude 15 maps to envelope 31 + else + { + volume = m_regs.ch_amplitude(chan) * 2; + if (volume != 0) + volume |= 1; + } + + // convert to amplitude + output.data[chan] = s_amplitudes[volume]; + } +} + + +//------------------------------------------------- +// read - handle reads from the SSG registers +//------------------------------------------------- + +uint8_t ssg_engine::read(uint32_t regnum) +{ + // defer to the override if present + if (m_override != nullptr) + return m_override->ssg_read(regnum); + + // read from the I/O ports call the handlers if they are configured for input + if (regnum == 0x0e && !m_regs.io_a_out()) + return m_intf.ymfm_external_read(ACCESS_IO, 0); + else if (regnum == 0x0f && !m_regs.io_b_out()) + return m_intf.ymfm_external_read(ACCESS_IO, 1); + + // otherwise just return the register value + return m_regs.read(regnum); +} + + +//------------------------------------------------- +// write - handle writes to the SSG registers +//------------------------------------------------- + +void ssg_engine::write(uint32_t regnum, uint8_t data) +{ + // defer to the override if present + if (m_override != nullptr) + return m_override->ssg_write(regnum, data); + + // store the raw value to the register array; + // most writes are passive, consumed only when needed + m_regs.write(regnum, data); + + // writes to the envelope shape register reset the state + if (regnum == 0x0d) + m_envelope_state = 0; + + // writes to the I/O ports call the handlers if they are configured for output + else if (regnum == 0x0e && m_regs.io_a_out()) + m_intf.ymfm_external_write(ACCESS_IO, 0, data); + else if (regnum == 0x0f && m_regs.io_b_out()) + m_intf.ymfm_external_write(ACCESS_IO, 1, data); +} + +} diff --git a/thirdparty/adlmidi/chips/ymfm/ymfm_ssg.h b/thirdparty/adlmidi/chips/ymfm/ymfm_ssg.h new file mode 100644 index 000000000..749ad146f --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm/ymfm_ssg.h @@ -0,0 +1,205 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_SSG_H +#define YMFM_SSG_H + +#pragma once + +#include "ymfm.h" + +namespace ymfm +{ + +//********************************************************* +// OVERRIDE INTERFACE +//********************************************************* + +// ======================> ssg_override + +// this class describes a simple interface to allow the internal SSG to be +// overridden with another implementation +class ssg_override +{ +public: + // reset our status + virtual void ssg_reset() = 0; + + // read/write to the SSG registers + virtual uint8_t ssg_read(uint32_t regnum) = 0; + virtual void ssg_write(uint32_t regnum, uint8_t data) = 0; + + // notification when the prescale has changed + virtual void ssg_prescale_changed() = 0; +}; + + +//********************************************************* +// REGISTER CLASS +//********************************************************* + +// ======================> ssg_registers + +// +// SSG register map: +// +// System-wide registers: +// 06 ---xxxxx Noise period +// 07 x------- I/O B in(0) or out(1) +// -x------ I/O A in(0) or out(1) +// --x----- Noise enable(0) or disable(1) for channel C +// ---x---- Noise enable(0) or disable(1) for channel B +// ----x--- Noise enable(0) or disable(1) for channel A +// -----x-- Tone enable(0) or disable(1) for channel C +// ------x- Tone enable(0) or disable(1) for channel B +// -------x Tone enable(0) or disable(1) for channel A +// 0B xxxxxxxx Envelope period fine +// 0C xxxxxxxx Envelope period coarse +// 0D ----x--- Envelope shape: continue +// -----x-- Envelope shape: attack/decay +// ------x- Envelope shape: alternate +// -------x Envelope shape: hold +// 0E xxxxxxxx 8-bit parallel I/O port A +// 0F xxxxxxxx 8-bit parallel I/O port B +// +// Per-channel registers: +// 00,02,04 xxxxxxxx Tone period (fine) for channel A,B,C +// 01,03,05 ----xxxx Tone period (coarse) for channel A,B,C +// 08,09,0A ---x---- Mode: fixed(0) or variable(1) for channel A,B,C +// ----xxxx Amplitude for channel A,B,C +// +class ssg_registers +{ +public: + // constants + static constexpr uint32_t OUTPUTS = 3; + static constexpr uint32_t CHANNELS = 3; + static constexpr uint32_t REGISTERS = 0x10; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + + // constructor + ssg_registers() { } + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // direct read/write access + uint8_t read(uint32_t index) { return m_regdata[index]; } + void write(uint32_t index, uint8_t data) { m_regdata[index] = data; } + + // system-wide registers + uint32_t noise_period() const { return bitfield(m_regdata[0x06], 0, 5); } + uint32_t io_b_out() const { return bitfield(m_regdata[0x07], 7); } + uint32_t io_a_out() const { return bitfield(m_regdata[0x07], 6); } + uint32_t envelope_period() const { return m_regdata[0x0b] | (m_regdata[0x0c] << 8); } + uint32_t envelope_continue() const { return bitfield(m_regdata[0x0d], 3); } + uint32_t envelope_attack() const { return bitfield(m_regdata[0x0d], 2); } + uint32_t envelope_alternate() const { return bitfield(m_regdata[0x0d], 1); } + uint32_t envelope_hold() const { return bitfield(m_regdata[0x0d], 0); } + uint32_t io_a_data() const { return m_regdata[0x0e]; } + uint32_t io_b_data() const { return m_regdata[0x0f]; } + + // per-channel registers + uint32_t ch_noise_enable_n(uint32_t choffs) const { return bitfield(m_regdata[0x07], 3 + choffs); } + uint32_t ch_tone_enable_n(uint32_t choffs) const { return bitfield(m_regdata[0x07], 0 + choffs); } + uint32_t ch_tone_period(uint32_t choffs) const { return m_regdata[0x00 + 2 * choffs] | (bitfield(m_regdata[0x01 + 2 * choffs], 0, 4) << 8); } + uint32_t ch_envelope_enable(uint32_t choffs) const { return bitfield(m_regdata[0x08 + choffs], 4); } + uint32_t ch_amplitude(uint32_t choffs) const { return bitfield(m_regdata[0x08 + choffs], 0, 4); } + +private: + // internal state + uint8_t m_regdata[REGISTERS]; // register data +}; + + +// ======================> ssg_engine + +class ssg_engine +{ +public: + static constexpr int OUTPUTS = ssg_registers::OUTPUTS; + static constexpr int CHANNELS = ssg_registers::CHANNELS; + static constexpr int CLOCK_DIVIDER = 8; + + using output_data = ymfm_output; + + // constructor + ssg_engine(ymfm_interface &intf); + + // configure an override + void override(ssg_override &override) { m_override = &override; } + + // reset our status + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // master clocking function + void clock(); + + // compute sum of channel outputs + void output(output_data &output); + + // read/write to the SSG registers + uint8_t read(uint32_t regnum); + void write(uint32_t regnum, uint8_t data); + + // return a reference to our interface + ymfm_interface &intf() { return m_intf; } + + // return a reference to our registers + ssg_registers ®s() { return m_regs; } + + // true if we are overridden + bool overridden() const { return (m_override != nullptr); } + + // indicate the prescale has changed + void prescale_changed() { if (m_override != nullptr) m_override->ssg_prescale_changed(); } + +private: + // internal state + ymfm_interface &m_intf; // reference to the interface + uint32_t m_tone_count[3]; // current tone counter + uint32_t m_tone_state[3]; // current tone state + uint32_t m_envelope_count; // envelope counter + uint32_t m_envelope_state; // envelope state + uint32_t m_noise_count; // current noise counter + uint32_t m_noise_state; // current noise state + ssg_registers m_regs; // registers + ssg_override *m_override; // override interface +}; + +} + +#endif // YMFM_SSG_H diff --git a/thirdparty/adlmidi/chips/ymfm_opl2.cpp b/thirdparty/adlmidi/chips/ymfm_opl2.cpp new file mode 100644 index 000000000..beb6c16a1 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm_opl2.cpp @@ -0,0 +1,119 @@ +/* + * Interfaces over Yamaha OPL3 (ym3812) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ymfm_opl2.h" +#include "ymfm/ymfm_opl.h" +#include + +YmFmOPL2::YmFmOPL2() : + OPLChipBaseT(), + m_headPos(0), + m_tailPos(0), + m_queueCount(0) +{ + ymfm::ymfm_interface* intf = new ymfm::ymfm_interface; + m_intf = intf; + m_chip = new ymfm::ym3812(*intf); + YmFmOPL2::setRate(m_rate); +} + +YmFmOPL2::~YmFmOPL2() +{ + ymfm::ym3812 *chip_r = reinterpret_cast(m_chip); + ymfm::ymfm_interface *intf_r = reinterpret_cast(m_intf); + delete chip_r; + delete intf_r; +} + +void YmFmOPL2::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + ymfm::ym3812 *chip_r = reinterpret_cast(m_chip); + chip_r->reset(); +} + +void YmFmOPL2::reset() +{ + OPLChipBaseT::reset(); + ymfm::ym3812 *chip_r = reinterpret_cast(m_chip); + chip_r->reset(); +} + +void YmFmOPL2::writeReg(uint16_t addr, uint8_t data) +{ + Reg &back = m_queue[m_headPos++]; + back.addr = addr; + back.data = data; + + if(m_headPos >= c_queueSize) + m_headPos = 0; + + ++m_queueCount; +} + +void YmFmOPL2::nativeGenerate(int16_t *frame) +{ + ymfm::ym3812 *chip_r = reinterpret_cast(m_chip); + ymfm::ym3812::output_data frames_i; + + uint32_t addr1 = 0xffff, addr2 = 0xffff; + uint8_t data1 = 0, data2 = 0; + + // see if there is data to be written; if so, extract it and dequeue + if(m_queueCount > 0) + { + const Reg &front = m_queue[m_tailPos++]; + + if(m_tailPos >= c_queueSize) + m_tailPos = 0; + --m_queueCount; + + addr1 = 0 + 2 * ((front.addr >> 8) & 3); + data1 = front.addr & 0xff; + addr2 = addr1 + 1; + data2 = front.data; + } + + // write to the chip + if(addr1 != 0xffff) + { + chip_r->write(addr1, data1); + chip_r->write(addr2, data2); + } + + chip_r->generate(&frames_i); + frame[0] = static_cast(ymfm::clamp(frames_i.data[0], -32768, 32767)); + frame[1] = frame[0]; +} + +const char *YmFmOPL2::emulatorName() +{ + return "YMFM OPL2"; +} + +bool YmFmOPL2::hasFullPanning() +{ + return false; +} + +OPLChipBase::ChipType YmFmOPL2::chipType() +{ + return CHIPTYPE_OPL2; +} diff --git a/thirdparty/adlmidi/chips/ymfm_opl2.h b/thirdparty/adlmidi/chips/ymfm_opl2.h new file mode 100644 index 000000000..f8105e645 --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm_opl2.h @@ -0,0 +1,60 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef YMFM_OPL2_H +#define YMFM_OPL2_H + +#include "opl_chip_base.h" + +class YmFmOPL2 final : public OPLChipBaseT +{ + void *m_chip; + void *m_intf; + + static const size_t c_queueSize = 500; + + struct Reg + { + uint32_t addr; + uint8_t data; + }; + + Reg m_queue[c_queueSize]; + size_t m_headPos; + size_t m_tailPos; + long m_queueCount; + +public: + YmFmOPL2(); + ~YmFmOPL2() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // YMFM_OPL2_H diff --git a/thirdparty/adlmidi/chips/ymfm_opl3.cpp b/thirdparty/adlmidi/chips/ymfm_opl3.cpp new file mode 100644 index 000000000..3852039ee --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm_opl3.cpp @@ -0,0 +1,119 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ymfm_opl3.h" +#include "ymfm/ymfm_opl.h" +#include + +YmFmOPL3::YmFmOPL3() : + OPLChipBaseT(), + m_headPos(0), + m_tailPos(0), + m_queueCount(0) +{ + ymfm::ymfm_interface* intf = new ymfm::ymfm_interface; + m_intf = intf; + m_chip = new ymfm::ymf262(*intf); + YmFmOPL3::setRate(m_rate); +} + +YmFmOPL3::~YmFmOPL3() +{ + ymfm::ymf262 *chip_r = reinterpret_cast(m_chip); + ymfm::ymfm_interface *intf_r = reinterpret_cast(m_intf); + delete chip_r; + delete intf_r; +} + +void YmFmOPL3::setRate(uint32_t rate) +{ + OPLChipBaseT::setRate(rate); + ymfm::ymf262 *chip_r = reinterpret_cast(m_chip); + chip_r->reset(); +} + +void YmFmOPL3::reset() +{ + OPLChipBaseT::reset(); + ymfm::ymf262 *chip_r = reinterpret_cast(m_chip); + chip_r->reset(); +} + +void YmFmOPL3::writeReg(uint16_t addr, uint8_t data) +{ + Reg &back = m_queue[m_headPos++]; + back.addr = addr; + back.data = data; + + if(m_headPos >= c_queueSize) + m_headPos = 0; + + ++m_queueCount; +} + +void YmFmOPL3::nativeGenerate(int16_t *frame) +{ + ymfm::ymf262 *chip_r = reinterpret_cast(m_chip); + ymfm::ymf262::output_data frames_i; + + uint32_t addr1 = 0xffff, addr2 = 0xffff; + uint8_t data1 = 0, data2 = 0; + + // see if there is data to be written; if so, extract it and dequeue + if(m_queueCount > 0) + { + const Reg &front = m_queue[m_tailPos++]; + + if(m_tailPos >= c_queueSize) + m_tailPos = 0; + --m_queueCount; + + addr1 = 0 + 2 * ((front.addr >> 8) & 3); + data1 = front.addr & 0xff; + addr2 = addr1 + 1; + data2 = front.data; + } + + // write to the chip + if(addr1 != 0xffff) + { + chip_r->write(addr1, data1); + chip_r->write(addr2, data2); + } + + chip_r->generate(&frames_i); + frame[0] = static_cast(ymfm::clamp(frames_i.data[0] / 2, -32768, 32767)); + frame[1] = static_cast(ymfm::clamp(frames_i.data[1] / 2, -32768, 32767)); +} + +const char *YmFmOPL3::emulatorName() +{ + return "YMFM OPL3"; +} + +OPLChipBase::ChipType YmFmOPL3::chipType() +{ + return CHIPTYPE_OPL3; +} + +bool YmFmOPL3::hasFullPanning() +{ + return false; +} diff --git a/thirdparty/adlmidi/chips/ymfm_opl3.h b/thirdparty/adlmidi/chips/ymfm_opl3.h new file mode 100644 index 000000000..efd2a8cec --- /dev/null +++ b/thirdparty/adlmidi/chips/ymfm_opl3.h @@ -0,0 +1,60 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef YMFM_OPL3_H +#define YMFM_OPL3_H + +#include "opl_chip_base.h" + +class YmFmOPL3 final : public OPLChipBaseT +{ + void *m_chip; + void *m_intf; + + static const size_t c_queueSize = 500; + + struct Reg + { + uint32_t addr; + uint8_t data; + }; + + Reg m_queue[c_queueSize]; + size_t m_headPos; + size_t m_tailPos; + long m_queueCount; + +public: + YmFmOPL3(); + ~YmFmOPL3() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate) override; + void reset() override; + void writeReg(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + ChipType chipType() override; + bool hasFullPanning() override; +}; + +#endif // YMFM_OPL3_H diff --git a/thirdparty/adlmidi/file_reader.hpp b/thirdparty/adlmidi/file_reader.hpp new file mode 100644 index 000000000..8a3a8d43b --- /dev/null +++ b/thirdparty/adlmidi/file_reader.hpp @@ -0,0 +1,301 @@ +/* + * FileAndMemoryReader - a tiny helper to utify file reading from a disk and memory block + * + * Copyright (c) 2015-2025 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#ifndef FILE_AND_MEM_READER_HHHH +#define FILE_AND_MEM_READER_HHHH + +#include // std::string +#include // std::fopen, std::fread, std::fseek, std::ftell, std::fclose, std::feof +#include // uint*_t +#include // size_t and friends +#ifdef _WIN32 +#define NOMINMAX 1 +#include // std::strlen +#include // MultiByteToWideChar +#endif + +/** + * @brief A little class gives able to read filedata from disk and also from a memory segment + */ +class FileAndMemReader +{ + //! Currently loaded filename (empty for a memory blocks) + std::string m_file_name; + //! File reader descriptor + std::FILE *m_fp; + + //! Memory pointer descriptor + const void *m_mp; + //! Size of memory block + size_t m_mp_size; + //! Cursor position in the memory block + size_t m_mp_tell; + +public: + /** + * @brief Relation direction + */ + enum relTo + { + //! At begin position + SET = SEEK_SET, + //! At current position + CUR = SEEK_CUR, + //! At end position + END = SEEK_END + }; + + /** + * @brief C.O.: It's a constructor! + */ + FileAndMemReader() : + m_fp(NULL), + m_mp(NULL), + m_mp_size(0), + m_mp_tell(0) + {} + + /** + * @brief C.O.: It's a destructor! + */ + ~FileAndMemReader() + { + close(); + } + + /** + * @brief Open file from a disk + * @param path Path to the file in UTF-8 (even on Windows!) + */ + void openFile(const char *path) + { + if(m_fp) + this->close();//Close previously opened file first! +#if !defined(_WIN32) || defined(__WATCOMC__) + m_fp = std::fopen(path, "rb"); +#else + wchar_t widePath[MAX_PATH]; + int size = MultiByteToWideChar(CP_UTF8, 0, path, static_cast(std::strlen(path)), widePath, MAX_PATH); + widePath[size] = '\0'; + m_fp = _wfopen(widePath, L"rb"); +#endif + m_file_name = path; + m_mp = NULL; + m_mp_size = 0; + m_mp_tell = 0; + } + + /** + * @brief Open file from memory block + * @param mem Pointer to the memory block + * @param length Size of given block + */ + void openData(const void *mem, size_t length) + { + if(m_fp) + this->close();//Close previously opened file first! + m_fp = NULL; + m_mp = mem; + m_mp_size = length; + m_mp_tell = 0; + } + + /** + * @brief Seek to given position + * @param pos Offset or position + * @param rel_to Relation (at begin, at current, or at end) + */ + void seek(long pos, int rel_to) + { + if(!this->isValid()) + return; + + if(m_fp)//If a file + { + std::fseek(m_fp, pos, rel_to); + } + else//If a memory block + { + switch(rel_to) + { + default: + case SET: + m_mp_tell = static_cast(pos); + break; + + case END: + m_mp_tell = m_mp_size - static_cast(pos); + break; + + case CUR: + m_mp_tell = m_mp_tell + static_cast(pos); + break; + } + + if(m_mp_tell > m_mp_size) + m_mp_tell = m_mp_size; + } + } + + /** + * @brief Seek to given position (unsigned integer 64 as relation. Negative values not supported) + * @param pos Offset or position + * @param rel_to Relation (at begin, at current, or at end) + */ + inline void seeku(uint64_t pos, int rel_to) + { + this->seek(static_cast(pos), rel_to); + } + + /** + * @brief Read the buffer from a file + * @param buf Pointer to the destination memory block + * @param num Number of elements + * @param size Size of one element + * @return Size + */ + size_t read(void *buf, size_t num, size_t size) + { + if(!this->isValid()) + return 0; + if(m_fp) + return std::fread(buf, num, size, m_fp); + else + { + size_t pos = 0; + size_t maxSize = static_cast(size * num); + + while((pos < maxSize) && (m_mp_tell < m_mp_size)) + { + reinterpret_cast(buf)[pos] = reinterpret_cast(m_mp)[m_mp_tell]; + m_mp_tell++; + pos++; + } + + return pos / num; + } + } + + /** + * @brief Get one byte and seek forward + * @return Readed byte or EOF (a.k.a. -1) + */ + int getc() + { + if(!this->isValid()) + return -1; + if(m_fp)//If a file + { + return std::getc(m_fp); + } + else //If a memory block + { + if(m_mp_tell >= m_mp_size) + return -1; + int x = reinterpret_cast(m_mp)[m_mp_tell]; + m_mp_tell++; + return x; + } + } + + /** + * @brief Returns current offset of cursor in a file + * @return Offset position + */ + size_t tell() + { + if(!this->isValid()) + return 0; + if(m_fp)//If a file + return static_cast(std::ftell(m_fp)); + else//If a memory block + return m_mp_tell; + } + + /** + * @brief Close the file + */ + void close() + { + if(m_fp) + std::fclose(m_fp); + + m_fp = NULL; + m_mp = NULL; + m_mp_size = 0; + m_mp_tell = 0; + } + + /** + * @brief Is file instance valid + * @return true if vaild + */ + bool isValid() + { + return (m_fp) || (m_mp); + } + + /** + * @brief Is End Of File? + * @return true if end of file was reached + */ + bool eof() + { + if(!this->isValid()) + return true; + if(m_fp) + return (std::feof(m_fp) != 0); + else + return m_mp_tell >= m_mp_size; + } + + /** + * @brief Get a current file name + * @return File name of currently loaded file + */ + const std::string &fileName() + { + return m_file_name; + } + + /** + * @brief Retrieve file size + * @return Size of file in bytes + */ + size_t fileSize() + { + if(!this->isValid()) + return 0; + if(!m_fp) + return m_mp_size; //Size of memory block is well known + size_t old_pos = this->tell(); + seek(0l, FileAndMemReader::END); + size_t file_size = this->tell(); + seek(static_cast(old_pos), FileAndMemReader::SET); + return file_size; + } +}; + +#endif /* FILE_AND_MEM_READER_HHHH */ diff --git a/thirdparty/adlmidi/fraction.hpp b/thirdparty/adlmidi/fraction.hpp new file mode 100644 index 000000000..892ba25d4 --- /dev/null +++ b/thirdparty/adlmidi/fraction.hpp @@ -0,0 +1,220 @@ +/* + * Fraction number handling. + * Copyright (C) 1992,2001 Bisqwit (http://iki.fi/bisqwit/) + * + * The license of this file is in Public Domain: + * https://bisqwit.iki.fi/src/index.html + * + * "... and orphan source code files are copyrighted public domain." + */ + +#ifndef bqw_fraction_h +#define bqw_fraction_h + +#include +#include + +template +class fraction +{ + inttype num1, num2; + typedef fraction self; + void Optim(); + + #if 1 + inline void Debug(char, const self &) { } + #else + inline void Debug(char op, const self &b) + { + cerr << nom() << '/' << denom() << ' ' << op + << ' ' << b.nom() << '/' << denom() + << ":\n"; + } + #endif +public: + void set(inttype n, inttype d) { num1=n; num2=d; Optim(); } + + fraction() : num1(0), num2(1) { } + fraction(inttype value) : num1(value), num2(1) { } + fraction(inttype n, inttype d) : num1(n), num2(d) { } + fraction(int value) : num1(value), num2(1) { } + template + explicit fraction(const floattype value) { operator= (value); } + inline double value() const {return nom() / (double)denom(); } + inline long double valuel() const {return nom() / (long double)denom(); } + self &operator+= (const inttype &value) { num1+=value*denom(); Optim(); return *this; } + self &operator-= (const inttype &value) { num1-=value*denom(); Optim(); return *this; } + self &operator*= (const inttype &value) { num1*=value; Optim(); return *this; } + self &operator/= (const inttype &value) { num2*=value; Optim(); return *this; } + self &operator+= (const self &b); + self &operator-= (const self &b); + self &operator*= (const self &b) { Debug('*',b);num1*=b.nom(); num2*=b.denom(); Optim(); return *this; } + self &operator/= (const self &b) { Debug('/',b);num1*=b.denom(); num2*=b.nom(); Optim(); return *this; } + self operator- () const { return self(-num1, num2); } + +#define fraction_blah_func(op1, op2) \ + self operator op1 (const self &b) const { self tmp(*this); tmp op2 b; return tmp; } + + fraction_blah_func( +, += ) + fraction_blah_func( -, -= ) + fraction_blah_func( /, /= ) + fraction_blah_func( *, *= ) + +#undef fraction_blah_func +#define fraction_blah_func(op) \ + bool operator op(const self &b) const { return value() op b.value(); } \ + bool operator op(inttype b) const { return value() op b; } + + fraction_blah_func( < ) + fraction_blah_func( > ) + fraction_blah_func( <= ) + fraction_blah_func( >= ) + +#undef fraction_blah_func + + const inttype &nom() const { return num1; } + const inttype &denom() const { return num2; } + inline bool operator == (inttype b) const { return denom() == 1 && nom() == b; } + inline bool operator != (inttype b) const { return denom() != 1 || nom() != b; } + inline bool operator == (const self &b) const { return denom()==b.denom() && nom()==b.nom(); } + inline bool operator != (const self &b) const { return denom()!=b.denom() || nom()!=b.nom(); } + //operator bool () const { return nom() != 0; } + inline bool negative() const { return (nom() < 0) ^ (denom() < 0); } + + self &operator= (const inttype value) { num2=1; num1=value; return *this; } + //self &operator= (int value) { num2=1; num1=value; return *this; } + + self &operator= (double orig) { return *this = (long double)orig; } + self &operator= (long double orig); +}; + +#ifdef _MSC_VER +#pragma warning(disable:4146) +#endif + +template +void fraction::Optim() +{ + /* Euclidean algorithm */ + inttype n1, n2, nn1, nn2; + + nn1 = std::numeric_limits::is_signed ? (num1 >= 0 ? num1 : -num1) : num1; + nn2 = std::numeric_limits::is_signed ? (num2 >= 0 ? num2 : -num2) : num2; + + if(nn1 < nn2) + n1 = num1, n2 = num2; + else + n1 = num2, n2 = num1; + + if(!num1) { num2 = 1; return; } + for(;;) + { + //fprintf(stderr, "%d/%d: n1=%d,n2=%d\n", nom(),denom(),n1,n2); + inttype tmp = n2 % n1; + if(!tmp)break; + n2 = n1; + n1 = tmp; + } + num1 /= n1; + num2 /= n1; + //fprintf(stderr, "result: %d/%d\n\n", nom(), denom()); +} + +#ifdef _MSC_VER +#pragma warning(default:4146) +#endif + +template +inline const fraction abs(const fraction &f) +{ + return fraction(abs(f.nom()), abs(f.denom())); +} + +#define fraction_blah_func(op) \ + template \ + fraction operator op \ + (const inttype bla, const fraction &b) \ + { return fraction (bla) op b; } +fraction_blah_func( + ) +fraction_blah_func( - ) +fraction_blah_func( * ) +fraction_blah_func( / ) +#undef fraction_blah_func + +#define fraction_blah_func(op1, op2) \ + template \ + fraction &fraction::operator op2 (const fraction &b) \ + { \ + inttype newnom = nom()*b.denom() op1 denom()*b.nom(); \ + num2 *= b.denom(); \ + num1 = newnom; \ + Optim(); \ + return *this; \ + } +fraction_blah_func( +, += ) +fraction_blah_func( -, -= ) +#undef fraction_blah_func + +template +fraction &fraction::operator= (long double orig) +{ + if(orig == 0.0) + { + set(0, 0); + return *this; + } + + inttype cf[25]; + for(int maxdepth=1; maxdepth<25; ++maxdepth) + { + inttype u,v; + long double virhe, a=orig; + int i, viim; + + for(i = 0; i < maxdepth; ++i) + { + cf[i] = (inttype)a; + if(cf[i]-1 > cf[i])break; + a = 1.0 / (a - cf[i]); + } + + for(viim=i-1; i < maxdepth; ++i) + cf[i] = 0; + + u = cf[viim]; + v = 1; + for(i = viim-1; i >= 0; --i) + { + inttype w = cf[i] * u + v; + v = u; + u = w; + } + + virhe = (orig - (u / (long double)v)) / orig; + + set(u, v); + //if(verbose > 4) + // cerr << "Guess: " << *this << " - error = " << virhe*100 << "%\n"; + + if(virhe < 1e-8 && virhe > -1e-8)break; + } + + //if(verbose > 4) + //{ + // cerr << "Fraction=" << orig << ": " << *this << endl; + //} + + return *this; +} + + +/* +template +ostream &operator << (ostream &dest, const fraction &m) +{ + if(m.denom() == (inttype)1) return dest << m.nom(); + return dest << m.nom() << '/' << m.denom(); +} +*/ + +#endif diff --git a/thirdparty/adlmidi/inst_db.cpp b/thirdparty/adlmidi/inst_db.cpp new file mode 100644 index 000000000..4275f5cb4 --- /dev/null +++ b/thirdparty/adlmidi/inst_db.cpp @@ -0,0 +1,39 @@ +/********************************************************** + This file is generated by `gen_adldata` automatically + Don't edit it directly! + To modify content of this file, modify banks + and re-run the `gen_adldata` build step. +***********************************************************/ + +#include "adlmidi_db.h" + + +const size_t g_embeddedBanksCount = 78; + +const BanksDump::BankEntry g_embeddedBanks[] = +{ +{0x0307,1,1,"AIL (The Fat Man 2op set, default AIL)",0,1},{0x0300,1,1,"Bisqwit (selection of 4op and 2op)",2,3},{0x0309,1,1,"HMI (Descent, Asterix)",4,5},{0x0309,1,1,"HMI (Descent:: Int)",6,7},{0x0309,1,1,"HMI (Descent:: Ham)",8,9},{0x0309,1,1,"HMI (Descent:: Rick)",10,11},{0x0309,1,1,"HMI (Descent 2)",12,13},{0x0309,1,1,"HMI (Normality)",14,15},{0x0309,1,1,"HMI (Shattered Steel)",16,17},{0x0309,1,1,"HMI (Theme Park)",18,19},{0x0309,1,1,"HMI (MegaPatch by LoudMouth)",20,21},{0x0309,1,1,"HMI (Aces of the Deep)",22,23},{0x0309,1,1,"HMI (Earthsiege)",24,25},{0x0309,1,1,"HMI (Anvil of Dawn)",26,27},{0x0002,1,1,"DMX (Bobby Prince v2)",28,29},{0x0002,1,1,"DMX (Cygnus Studios, default DMX)",30,31},{0x0002,1,1,"DMX (Bobby Prince v1)",32,33},{0x0307,1,1,"AIL (Discworld, Grandest Fleet, etc.)",34,35},{0x0307,1,1,"AIL (Warcraft 2)",36,37},{0x0707,1,1,"AIL (Syndicate)",38,39},{0x0307,1,1,"AIL (Guilty, Orion Conspiracy, TNSFC ::4op)",40,41},{0x0707,1,1,"AIL (Magic Carpet 2) :NON-GM:",42,43},{0x0307,1,1,"AIL (Nemesis)",44,45},{0x0707,3,1,"AIL (Jagged Alliance) :NON-GM:",46,49},{0x0707,1,1,"AIL (When Two Worlds War) :MISS-INS:",50,51},{0x0707,1,1,"AIL (Bards Tale Construction) :MISS-INS:",52,53},{0x0707,1,1,"AIL (Return to Zork) :NON-GM:",54,55},{0x0307,1,1,"AIL (Theme Hospital)",56,57},{0x0307,1,1,"AIL (National Hockey League PA)",58,59},{0x0707,1,1,"AIL (Inherit The Earth) :NON-GM:",60,61},{0x0707,1,1,"AIL (Inherit The Earth, file two) :NON-GM:",62,63},{0x0307,1,1,"AIL (Little Big Adventure) :4op:",64,65},{0x0707,1,1,"AIL (Heroes of Might and Magic II) :NON-GM:",66,67},{0x0307,1,1,"AIL (Death Gate)",68,69},{0x0307,1,1,"AIL (FIFA International Soccer)",70,71},{0x0307,2,1,"AIL (Starship Invasion)",72,74},{0x0307,1,1,"AIL (Super Street Fighter 2 :4op:)",75,76},{0x0307,1,1,"AIL (Lords of the Realm) :MISS-INS:",77,78},{0x0307,1,1,"AIL (SimFarm, SimHealth) :4op:",79,80},{0x0307,1,1,"AIL (SimFarm, Settlers, Serf City)",81,82},{0x0307,1,1,"AIL (Caesar 2) :p4op: :MISS-INS:",83,84},{0x0707,1,1,"AIL (Syndicate Wars) :NON-GM:",85,86},{0x0307,1,1,"AIL (LoudMouth by Probe Ent.)",87,88},{0x0307,1,1,"AIL (Warcraft) :NON-GM:",89,90},{0x0307,1,1,"AIL (Terra Nova Strike Force Centuri) :p4op:",91,92},{0x0307,1,1,"AIL (System Shock) :p4op:",93,94},{0x0307,1,1,"AIL (Advanced Civilization)",95,96},{0x0707,1,1,"AIL (Battle Chess 4000) :p4op: :NON-GM:",97,98},{0x0307,1,1,"AIL (Ultimate Soccer Manager :p4op:)",99,100},{0x0307,1,1,"AIL (Air Bucks, Blue And The Gray, etc) :NON-GM:",101,102},{0x0707,2,1,"AIL (Ultima Underworld 2) :NON-GM:",103,105},{0x0707,1,1,"AIL (FatMan MT32) :NON-GM:",106,107},{0x0307,1,1,"AIL (High Seas Trader) :MISS-INS:",108,109},{0x0007,1,2,"AIL (Master of Magic) :4op:",110,111},{0x0307,1,1,"AIL (Master of Magic) :4op: orchestral drums",113,114},{0x0300,1,1,"SB (Action Soccer)",115,116},{0x0300,1,1,"SB (3d Cyberpuck :: melodic only)",117,118},{0x0700,1,0,"SB (Simon the Sorcerer :: melodic only)",119,120},{0x0304,1,1,"OP3 (The Fat Man 2op set; Win9x)",120,121},{0x0304,1,1,"OP3 (The Fat Man 4op set)",122,123},{0x0304,1,1,"OP3 (JungleVision 2op set :: melodic only)",124,125},{0x0304,1,1,"OP3 (Wallace 2op set, Nitemare 3D :: melodic only)",126,127},{0x0003,1,1,"TMB (Duke Nukem 3D)",128,129},{0x0003,1,1,"TMB (Shadow Warrior)",130,131},{0x0002,1,1,"DMX (Scott Host)",132,133},{0x0300,1,1,"SB (Modded GMOPL by Wohlstand)",134,135},{0x0300,1,1,"SB (Jamie O'Connell's bank)",136,137},{0x0003,1,1,"TMB (Apogee Sound System Default bank) :broken drums:",138,139},{0x0300,1,1,"WOPL (4op bank by James Alan Nguyen and Wohlstand)",140,141},{0x0003,1,1,"TMB (Blood)",142,143},{0x0003,1,1,"TMB (Rise of the Triad)",144,145},{0x0003,1,1,"TMB (Nam)",146,147},{0x0000,11,3,"WOPL (DMXOPL3 bank by Sneakernets)",148,159},{0x0201,1,1,"EA (Cartooners)",162,163},{0x0209,1,1,"WOPL (Apogee IMF 90-ish)",164,165},{0x0307,3,1,"AIL (The Lost Vikings) :NON-GM:",166,169},{0x0002,1,1,"DMX (Strife)",170,171},{0x0009,1,1,"WOPL (MS-AdLib, Windows 3.x)",172,173},}; + +#define q(x) g_embeddedBanks[x].title +const char* const g_embeddedBankNames[] = +{ + q(0),q(1),q(2),q(3),q(4),q(5),q(6),q(7),q(8),q(9),q(10),q(11),q(12),q(13),q(14),q(15),q(16),q(17),q(18),q(19),q(20),q(21),q(22),q(23),q(24),q(25),q(26),q(27),q(28),q(29),q(30),q(31),q(32),q(33),q(34),q(35),q(36),q(37),q(38),q(39),q(40),q(41),q(42),q(43),q(44),q(45),q(46),q(47),q(48),q(49),q(50),q(51),q(52),q(53),q(54),q(55),q(56),q(57),q(58),q(59),q(60),q(61),q(62),q(63),q(64),q(65),q(66),q(67),q(68),q(69),q(70),q(71),q(72),q(73),q(74),q(75),q(76),q(77), + NULL +}; + +const size_t g_embeddedBanksMidiIndex[] = +{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,15,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,34,62,63,64,65,66,67,68,69,70,71,72,39,73,74,75,39,76,77,34,78,79,80,81,82,83,84,85,86,87,88,89,90,67,91,92,93,94,95,96,97,98,99,100,54,101,102,103,104,105,39,106,107,108,109,110,111,26,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,110,14,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162}; + +const BanksDump::MidiBank g_embeddedBanksMidi[] = +{ +{0,0,{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,33,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,127,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{0,179,2,180,181,182,183,184,185,9,186,11,187,188,189,190,191,192,193,194,20,195,196,197,198,199,200,201,202,203,204,205,206,207,208,35,209,34,210,211,212,213,214,215,216,217,218,219,220,221,222,50,223,224,53,225,55,56,226,227,228,229,230,231,232,233,234,235,236,237,238,239,71,72,240,241,242,243,244,245,246,247,248,82,249,250,251,86,252,253,254,255,91,92,256,257,258,259,260,98,99,261,262,263,264,265,266,267,268,269,270,271,111,272,273,274,115,275,276,277,278,120,279,280,281,282,283,284}},{0,0,{127,132,285,286,127,287,288,289,290,291,292,127,127,293,294,295,289,296,297,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,318,320,318,321,318,318,322,318,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,337,338,339,320,340,341,342,343,344,345,346,346,169,170,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,164,363,156,364,292,365,366,367,368,178,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369}},{0,0,{370,371,372,373,374,375,376,377,378,379,380,381,187,382,383,384,385,386,387,388,20,389,390,391,392,393,26,394,28,370,395,396,397,398,399,400,401,402,403,398,404,405,406,407,408,44,45,409,410,48,411,412,51,52,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,69,429,71,72,430,74,75,76,431,432,79,80,81,82,83,433,85,434,435,436,437,90,438,92,439,94,440,96,97,441,99,442,443,444,103,104,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,122,462,463,464,465}},{0,0,{466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,467,468,469,470,471,472,473,474,475,476,308,477,478,479,480,481,482,481,483,484,485,486,487,488,489,487,489,490,487,491,487,492,493,494,495,496,497,498,499,500,501,502,503,504,341,342,505,506,507,508,509,510,511,512,513,503,514,515,516,308,517,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466}},{0,0,{518,519,520,28,521,396,395,522,523,524,525,526,527,403,409,528,79,433,529,530,411,437,92,439,445,531,532,533,534,535,536,537,538,539,540,541,459,542,543,544,545,546,547,548,549,550,551,552,553,554,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518}},{0,0,{518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518}},{0,0,{518,519,520,28,521,396,395,522,523,524,525,526,527,403,409,528,79,433,529,530,411,437,92,439,445,531,532,533,534,535,536,537,538,539,540,541,459,542,543,544,545,546,547,548,549,550,551,552,553,518,452,99,622,623,439,624,248,447,456,625,28,432,626,627,628,79,94,403,398,455,629,630,631,632,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,633,634,635,636,637,638,518,-1}},{0,0,{639,640,641,642,643,644,645,646,647,648,292,649,650,651,652,653,654,655,656,657,518,518,518,518,518,518,518,555,556,557,558,559,560,561,562,563,564,565,566,567,568,658,659,660,661,573,574,575,576,577,578,579,662,581,582,583,584,585,586,587,663,589,590,591,592,593,594,664,665,597,598,599,600,666,667,603,604,605,606,607,608,609,610,611,612,613,614,615,668,617,669,619,620,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,-1}},{0,0,{518,519,520,28,521,396,395,522,523,524,525,526,527,403,409,528,79,433,529,530,411,437,92,439,445,531,532,533,534,535,536,537,538,539,540,541,459,542,543,544,545,546,547,548,549,550,551,552,553,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,670,671,672,673,674,399,675,676,522,51,52,433,677,678,679,680,681,85,622,90,439,94,443,682,454,459,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518}},{0,0,{518,518,518,518,518,518,518,518,518,518,518,518,518,683,649,650,652,684,685,684,685,686,687,688,689,690,691,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,692,693,655,694,363,647,695,292,365,366,367,368,696,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518}},{0,0,{518,519,520,28,521,396,395,522,523,524,525,526,527,403,409,528,79,433,529,530,411,437,92,439,445,531,532,533,534,535,536,537,538,539,540,541,459,542,543,544,545,546,547,548,549,550,551,552,553,518,697,698,699,518,518,700,518,518,518,518,701,702,703,704,705,529,706,707,708,518,709,710,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,681,670,522,90,545,28,711,712,713,714,396,715,716,28,526,527,671,622,439,676,717,718,395,520,442,94,679,403,719,522,532,51,518,518}},{0,0,{518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,617,618,619,620,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,720,721,722,649,650,684,723,724,652,725,726,655,727,728,729,693,730,731,732,733,593,734,735,639,586,736,695,646,737,366,720,738,739,740,741,742,743,518,518}},{0,0,{744,371,372,373,374,375,376,377,378,379,380,381,187,382,383,384,385,386,387,388,20,389,390,391,392,393,26,394,28,370,395,396,397,398,399,400,401,402,403,398,404,405,406,407,408,44,45,409,410,48,411,412,51,52,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,69,429,71,72,430,74,75,76,431,432,79,80,81,82,83,433,85,434,435,436,437,90,438,92,439,94,440,96,97,441,99,442,443,444,103,104,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,122,462,463,464,465}},{0,0,{649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,745,746,130,747,640,748,641,728,642,749,643,644,750,645,751,657,752,753,734,754,755,756,757,758,759,760,761,762,647,763,764,765,766,767,768,769,770,771,166,167,168,169,170,772,773,774,775,776,777,778,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696,696}},{0,0,{779,780,372,527,374,781,675,782,378,783,380,784,785,382,383,786,787,716,715,788,28,789,522,522,790,791,26,792,28,676,522,793,794,670,674,795,671,399,672,673,796,797,798,407,408,799,800,801,802,803,804,805,51,52,413,677,806,807,808,809,810,678,679,680,811,717,812,813,814,428,815,816,71,817,818,74,75,819,820,821,681,85,526,527,403,433,409,434,622,436,822,90,438,92,439,94,823,824,97,624,99,442,443,825,628,826,827,828,829,830,549,450,831,682,453,454,832,456,833,717,396,547,718,464,550,719,518,518}},{0,0,{518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,834,835,836,586,589,736,837,838,684,654,652,725,720,650,722,649,721,683,651,724,653,723,686,655,687,728,688,693,689,690,639,691,751,657,752,741,734,648,839,756,840,367,368,292,365,366,695,841,764,842,843,737,768,769,694,363,844,167,168,169,170,772,773,646,775,845,777,778,696,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518,518}},{0,0,{866,867,868,869,870,867,871,872,873,874,875,876,877,878,869,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,893,893,894,895,896,897,898,625,899,672,831,900,901,902,893,893,903,904,905,906,905,907,908,796,908,909,910,911,912,913,913,914,915,916,917,893,893,918,917,917,919,920,921,921,921,921,922,923,922,924,924,925,926,816,927,927,928,929,930,931,932,933,934,935,936,937,938,939,940,941,942,943,943,944,941,907,945,935,935,935,946,946,947,948,949,950,951,833,833,952,953,954,955,956,957,958,896,896}},{0,0,{959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,960,961,962,963,964,965,966,967,966,968,969,970,968,971,972,965,959,973,974,959,959,969,975,959,959,976,966,965,977,969,965,966,978,979,968,968,980,981,982,959,983,984,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959,959}},{0,0,{985,986,987,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,-1}},{0,0,{466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,1112,1113,1114,1115,1116,310,1117,1118,1119,1115,1120,1121,1122,1123,1124,1125,1124,1126,1127,1128,1129,1130,1131,1132,1130,1132,1133,1130,1134,1130,492,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,310,1153,1154,1155,513,1145,514,1156,1157,1115,1158,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,-1}},{0,0,{1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1203,1204,1205,1206,1207,1208,1209,1210,1211,1212,1213,1214,1215,1216,1217,1218,1219,1220,1221,1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237,1238,1239,1240,1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1251,1252,1253,1254,1255,1256,1257,1258,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,1269,1270,1271,1272,1273,1274,1275,1276,1277,1278,1279,1280,1281,1282,1283,1284,1285,1286}},{0,0,{466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,1287,306,307,308,1288,509,1289,1290,476,308,477,478,479,480,1291,482,1291,483,1292,485,486,487,488,489,487,489,490,487,491,487,492,493,494,495,496,497,498,499,500,501,502,503,504,341,342,505,506,507,508,509,510,511,512,513,503,514,515,516,308,517,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466}},{0,0,{1293,1160,1161,1294,1295,1296,1165,1297,1167,1168,1169,1170,1171,1298,1299,1300,1301,1176,1302,1178,1179,1303,1181,1182,1183,1304,1185,1305,1306,1307,1308,1190,1309,1310,1311,1312,1313,1314,1315,1316,1199,1317,1201,1202,1203,1318,1205,1319,1320,1208,1209,1321,1211,1212,1213,1214,1322,1323,1324,1218,1325,1326,1327,1222,1223,1224,1225,1226,1328,1228,1329,1330,1231,1331,1233,1234,1332,1333,1063,1334,1335,1336,1241,1242,1337,1244,1338,1339,1247,1340,1341,1342,1343,1344,1345,1346,1255,1347,1348,1349,1350,1260,1351,1352,1263,1353,1354,1266,1267,1268,1269,1355,1271,1356,1357,1274,1275,1358,1359,1360,1279,1361,1362,1282,1283,1363,1364,1365}},{0,0,{466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,467,468,469,470,471,472,473,474,1366,1366,308,477,478,479,480,481,482,481,483,484,485,486,487,488,489,487,489,490,487,491,487,492,493,494,495,496,497,498,499,500,1367,1368,503,504,341,342,505,506,507,508,509,510,511,512,513,503,514,515,516,308,517,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466}},{0,0,{1369,1370,1371,1372,4,5,1373,1374,8,1375,1376,11,12,1377,1378,1379,385,386,387,388,20,389,390,391,392,393,26,1380,28,1381,395,396,397,398,399,400,401,1382,403,398,404,405,406,407,408,44,45,409,410,48,411,412,51,52,413,414,415,416,417,418,419,420,61,422,423,424,425,426,427,428,69,429,71,72,430,74,75,76,431,432,79,80,81,82,83,433,85,434,435,436,437,90,438,92,439,94,440,96,97,441,99,442,443,444,103,104,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,122,462,463,464,465}},{0,0,{1383,1384,1385,1386,1387,1388,1389,1390,1391,1392,1393,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403,1404,1405,1406,1407,1408,1409,1410,1411,1412,1413,1414,1415,1416,1417,1418,1419,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1510,1511,1512,1513,1514,1515,1516,1517,1518,1519,1520,1521,1522,1523,1524,1525,1526,1527,1528,1529,1530,1531,1524,1532,1526,1533,1534,1535,1536,1537,1538,1539,1540,1541,1519,1542,1543,1543,1543,1543,1544,1545,1546,1547,1543,1548,1549,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1383,1384,1385,1386,1387,1388,1389,1390,1391,1392,1393,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403,1404,1405,1406,1407,1408,1409,1410,1411,1550,1551,1414,1552,1416,1417,1553,1554,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1555,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1510,1511,1512,1513,1514,1556,1516,1517,1518,1519,1520,1521,1522,1523,1524,1525,1526,1527,1528,1529,1557,1531,1524,1558,1526,1533,1534,1535,1536,1537,1538,1539,1540,1541,1519,1542,1543,1543,1543,1543,1544,1545,1546,1547,1543,1548,1549,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1383,1384,1385,1386,1387,1388,1389,1390,1391,1392,1393,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403,1404,1405,1406,1407,1408,1409,1410,1411,1559,1413,1414,1560,1416,1417,1553,1554,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1555,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469,1470,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1510,1511,1512,1513,1514,1515,1516,1517,1518,1519,1520,1521,1522,1523,1524,1525,1526,1527,1528,1529,1557,1531,1524,1561,1526,1533,1534,1535,1536,1537,1538,1539,1540,1541,1519,1542,1543,1543,1543,1543,1544,1545,1546,1547,1543,1548,1549,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1562,179,1563,1564,374,375,1565,1566,1567,1568,186,1569,187,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,25,26,1582,28,1583,30,1584,1585,33,34,35,36,1586,38,33,39,40,1587,42,43,44,45,46,222,1588,1589,1590,1591,1592,1593,1594,55,56,1595,58,59,1596,1597,62,1598,1599,1600,1601,67,68,69,70,71,72,1602,74,1603,1604,1605,1606,79,80,81,1607,83,1608,1609,86,1610,1611,1612,1613,1614,1615,256,257,1616,96,97,1617,1618,1619,262,263,103,104,105,1620,107,108,109,110,111,112,113,114,115,116,117,1621,119,120,121,122,123,124,125,126}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,127,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,1622,147,148,1623,150,151,152,153,154,155,156,157,158,159,160,161,162,163,1624,1625,166,167,168,169,1626,171,172,173,174,175,176,177,178,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{0,1,2,3,4,5,1627,7,8,1628,10,11,12,13,1629,15,16,17,18,1630,20,21,22,23,1631,25,26,27,28,29,30,31,32,33,34,35,36,37,38,33,39,40,41,42,1632,1633,1634,1635,1636,1637,1638,50,1639,52,53,1640,1641,1642,1643,58,1644,1645,61,62,63,64,65,66,1646,68,1647,1648,1649,1650,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,1651,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,1652,116,1653,277,119,120,121,122,123,124,125,126}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1654,1654,1655,1656,1657,1658,1659,1660,1661,1662,1663,1664,1665,1666,1667,1668,142,1669,144,1670,1671,147,1672,1673,150,151,152,153,154,155,156,157,158,159,1674,161,162,163,164,165,166,1675,168,169,170,171,172,173,174,175,176,177,178,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1676,1677,1678,1679,1680,1677,1681,1676,1682,1683,875,1684,1685,1686,1679,1687,1688,1688,1688,1689,1690,1691,1692,1692,1693,1694,1695,1696,1697,1697,1697,1697,1698,1699,1700,1701,1699,1702,1703,1704,1705,1706,1707,1708,1697,1697,1709,1710,1711,1712,1711,1713,1714,1715,1714,1716,910,911,1717,1718,1718,1719,1720,1721,1722,1697,1697,1697,1722,1722,1723,1724,1725,1725,1725,1725,922,1726,1727,1727,1727,925,1728,1729,1730,1730,1731,1732,1733,1733,1734,1735,1736,1737,1712,1738,1739,1740,1741,1742,1743,1744,1744,1745,1742,1713,1746,1737,1737,1737,1747,1747,1748,1749,1750,1751,1752,1750,1750,1753,1754,1755,1756,1757,1758,1759,1760,1761}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,961,961,1762,1763,1764,1762,1765,1766,1765,1767,1768,1767,1767,971,1769,1762,-1,973,974,-1,-1,1768,-1,-1,-1,1770,1765,1762,1771,1768,1762,1765,1772,1773,1767,1767,1767,1767,1767,-1,1767,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1774,1775,1776,1777,181,182,183,1778,185,1779,1780,1781,1782,188,189,1783,1784,1785,1786,1787,1788,195,196,197,198,199,200,201,202,1789,1790,205,206,207,208,1791,1792,1793,1794,211,1795,1796,214,215,216,217,218,219,220,221,1797,1798,1799,1800,1801,225,1802,1803,1804,1805,1806,229,230,231,232,233,234,235,1807,237,238,239,1808,1809,240,1810,242,243,244,1811,246,247,1812,1813,249,250,251,1814,252,253,254,255,1815,1816,1817,1818,1819,259,1820,1821,1822,1823,1824,1825,264,265,266,267,268,269,270,271,1826,272,273,1827,1828,1829,276,1830,1831,1832,1833,1834,1835,1836,283,284}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,127,127,1837,1838,1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851,1852,1853,1854,1855,1856,1857,1858,1859,1860,1861,1862,1863,1864,1865,1866,1867,1868,1869,1870,1871,1872,1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883,1884,1885,1886,1887,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1676,1677,1678,1888,1889,1890,1681,1676,1682,1891,1892,876,1685,1893,1679,1687,1894,1895,1688,1689,1690,1896,1897,887,1693,889,1695,1898,1899,1900,1697,1697,1698,1901,1902,1903,1904,1905,1906,1907,1908,1706,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,1920,1897,909,910,911,1717,1718,1718,1921,204,1922,1923,1924,1925,1926,1927,1928,1929,1930,1725,1931,1932,1725,1933,1934,1935,1936,1937,1938,1939,1940,1941,1941,1942,1938,1921,1943,1943,1943,1943,1943,1944,1945,1946,1740,1947,1948,1949,1950,1950,1951,1948,1713,1952,1953,1953,1953,1954,1954,1748,1749,1955,1956,1752,1750,1750,1957,1958,1959,1960,1757,1961,1759,1760,1761}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,960,1962,1963,1964,1965,1965,1765,1966,1765,1967,1768,1968,1967,971,1769,1762,-1,973,974,-1,-1,1768,-1,-1,-1,1770,1765,1762,1969,1768,1762,1765,1772,1773,1967,1970,1967,981,982,-1,983,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1971,1972,1973,1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984,1985,1986,1987,1988,1989,1990,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030,228,2031,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041,2042,2043,2044,2045,2046,2047,2011,245,2048,2049,2050,2051,2052,2053,2054,2055,2056,2057,2058,2059,2060,2061,2062,2063,2064,2065,2066,2067,2068,2069,2070,2071,2072,2073,2074,2075,2076,2077,2078,2079,2080,2081,2082,274,2083,2084,2085,2086,278,2087,2088,2089,2090,2091,2092,2093}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2094,2094,2095,2096,2097,2098,2099,2100,2101,2102,2103,2104,2105,2106,2107,2108,2109,2110,2109,2111,2112,2113,2114,2115,2109,2116,2117,2118,2119,2120,2121,2122,2123,2124,2125,2126,2127,2128,2129,2130,2131,2132,2133,2134,2135,2136,2137,2138,2139,2140,2141,2142,2143,2144,2145,2146,2147,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{2148,-1,2150,1677,2151,1677,1681,1676,716,1683,1683,2152,2153,2153,2153,1721,1688,1688,1688,1690,1690,1690,1692,1692,2154,2155,2156,2156,1697,1697,2157,1697,2158,2159,1700,1701,2159,1702,2160,1704,1705,1705,1707,1723,1697,1697,1709,1710,2161,2162,2163,2164,1714,1715,1714,1716,910,911,1717,1718,1718,1719,1720,2165,2155,2167,2169,-1,2156,-1,1723,2172,2173,-1,-1,-1,2174,2175,-1,1727,2179,2180,-1,-1,-1,-1,2184,2185,2183,1733,1734,2182,2170,2170,2186,2187,2188,2189,2190,2191,2192,-1,1744,2194,2190,2187,-1,2193,-1,-1,-1,2193,2198,-1,-1,-1,2181,2200,-1,2202,-1,-1,2169,-1,280,2148,1705,-1}},{0,1,{2148,2149,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2156,2156,-1,-1,2157,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2155,2167,2169,2156,2156,2170,-1,2172,2173,2161,2156,2169,2174,2175,2176,-1,2179,2180,2181,2182,354,2183,2184,-1,-1,-1,-1,-1,-1,-1,2186,2187,-1,-1,-1,2191,2192,2193,-1,-1,-1,2187,2169,-1,2195,2196,2197,2193,-1,2186,2199,2176,2181,-1,2201,2202,2203,1610,-1,1610,-1,2148,-1,2172}},{0,2,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2166,2168,2168,-1,-1,-1,2171,1603,-1,-1,-1,-1,-1,-1,2177,2178,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2170,-1,-1,-1,-1,2189,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2204,2205,2206,2207,2206,1765,2208,1765,1767,1768,1767,1768,1762,2209,1762,2210,-1,2206,2211,2206,-1,-1,-1,-1,1762,1765,1762,1762,1768,1762,1765,2206,2212,1767,2212,1767,1767,1767,-1,1767,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2213,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2214,-1,-1,-1,-1,-1,-1,-1,-1,2215,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2216,-1,-1,2217,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{2218,2219,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2220,2220,2220,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2221,2222,-1,-1,-1,2223,-1,-1,-1,-1,-1,-1,-1,-1,-1,2224,2225,-1,-1,-1,-1,-1,-1,-1,911,2226,2227,2227,-1,-1,-1,-1,-1,-1,-1,-1,-1,2228,2228,2229,2230,2229,-1,-1,-1,-1,-1,-1,-1,2231,2231,-1,-1,-1,-1,-1,2232,1735,-1,1736,-1,-1,-1,-1,-1,-1,-1,-1,-1,2233,2223,2234,2235,2236,2237,-1,-1,2238,2239,2240,2241,2242,-1,-1,2243,-1,-1,2244,-1,-1,2245,2246,2247,2248,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2249,2249,2249,-1,-1,-1,-1,-1,-1,-1,-1,2250,-1,-1,-1,-1,2251,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{2252,2253,2254,2255,2256,2257,2258,2259,2260,2261,2262,2263,2264,2265,2266,1578,2267,2267,2267,2268,2268,2268,2269,2270,2271,2272,2273,2274,34,2275,33,2276,2277,2278,2279,2280,2281,2282,2283,2284,2285,2286,2287,2288,2289,2290,2291,2292,2293,2294,2295,2296,2297,2298,2299,2300,2301,45,45,2302,2303,2304,2305,2306,2307,1697,2308,2309,2310,2311,2312,2312,2313,2314,2314,2315,2316,2317,2318,2319,2320,2321,2322,2323,2324,2325,2326,2327,2328,2329,2330,2331,2332,2332,2333,2334,2335,2336,2337,2338,2339,2340,2341,275,2342,2343,1746,2344,2345,2346,2347,2348,2349,2350,2351,2352,2353,2354,2355,2356,2357,2358,2359,2360,122,2361,2362,2363}},{0,0,{2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2365,2366,2367,2368,2369,2370,2371,2372,2371,2373,2374,2375,2374,2376,2377,2376,2378,2364,2364,2379,2364,2380,2364,2364,2364,2381,2382,2383,2384,2385,2386,2387,2388,2389,160,161,162,163,2390,2364,2391,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,-1,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364,2364}},{0,0,{2392,2392,2392,2392,2392,2392,1895,2393,2394,2395,2396,2397,2398,2399,2400,2401,2402,2402,2402,2402,2403,2404,2405,2404,2406,2407,2406,2406,2406,2408,204,2409,2410,2410,2410,2410,2410,2410,2410,2410,2411,2411,2411,2410,2411,2412,2413,2414,2411,2411,2415,2415,2420,2416,2416,2417,227,2418,2419,227,2421,227,227,227,227,227,227,227,887,2421,2422,2423,2424,2424,2424,2425,2424,2424,2426,2426,2427,925,2428,2428,2392,2416,2429,2430,2431,2432,2433,2434,2435,2436,2437,2429,2437,2438,2400,2439,2440,2441,2442,2443,2444,2445,2446,2447,2448,2449,2450,2450,2394,2451,2452,2453,2454,2454,2454,2455,2456,2428,2457,2458,2459,282,2460,1760}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,960,960,960,960,960,1769,1769,1769,960,961,2461,1763,1763,1770,2462,2463,2462,2464,2462,2464,2465,2466,2467,2468,1966,2467,1966,1966,2469,2470,2467,2471,1966,2472,2473,983,982,981,2474,2475,1772,1773,2476,2476,1967,981,982,982,2461,2477,2478,2478,2478,1969,2479,2476,2479,2479,2461,981,983,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{985,2480,2481,988,989,990,2482,992,2483,994,2484,996,2485,998,2486,1000,2487,2488,2489,2490,1005,2491,1007,2492,2493,1010,2494,1012,2495,2496,1015,2497,1017,1018,2498,2499,2500,2501,2502,2503,2504,2505,2506,2507,2508,2509,2510,1032,2511,2512,2513,2514,2515,1038,1039,2516,2517,2518,2519,2520,2521,2522,2523,2524,2525,2526,2527,2528,1053,2529,2530,1056,1057,2531,2532,1060,2533,2534,1063,1064,2535,2536,2537,2538,1069,1070,2539,2540,1073,2541,2542,1076,2543,2544,2545,2546,1081,2547,1083,1084,2548,2549,2550,2551,2552,2553,2554,2555,1093,2556,1095,2557,1097,2558,2559,1100,1101,2560,2561,2562,1105,2563,2564,2565,1109,2566,2567,2568}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2569,2570,2571,1115,1116,310,1117,2572,2573,1115,2574,2575,2576,2577,2578,2579,2580,2581,2582,2583,2584,2585,2586,325,2585,325,1133,2585,2587,2588,330,2589,2590,2591,2592,2593,2594,2595,2596,2597,2598,2599,340,2600,1148,2601,2602,2603,1152,310,1153,1154,2604,2605,2599,2606,2607,1157,1115,1158,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{0,1,2,-1,-1,-1,5,2608,-1,-1,1575,-1,1576,1576,2609,1578,-1,-1,-1,-1,-1,-1,2610,2610,2611,2611,62,2611,33,33,38,38,1610,2612,1591,1591,96,1617,34,1593,260,1616,35,1616,62,-1,1573,79,1589,1590,2613,44,-1,-1,1587,1587,42,45,45,2614,25,2615,-1,103,-1,1585,-1,34,36,37,35,35,72,72,-1,-1,2616,74,-1,-1,1603,1601,70,70,67,68,69,1579,-1,-1,56,-1,59,59,1595,-1,-1,12,260,-1,10,9,2617,13,12,1620,-1,1604,1605,1603,1603,1606,2618,116,-1,-1,-1,2619,115,2620,2621,2622,1594,123,122,2620,2623,2620}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,127,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,2624,1622,147,148,1623,150,151,152,153,154,155,156,2625,158,159,160,161,162,163,1624,1625,166,167,168,169,1626,171,172,173,174,175,176,177,178,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{0,1,2,-1,-1,-1,5,2608,-1,-1,1575,-1,1576,1576,1576,1578,-1,-1,-1,-1,-1,-1,2610,2610,2611,2611,62,2611,33,33,38,38,1610,1591,1591,1591,96,1617,34,1593,260,1616,35,1616,62,-1,1573,79,1589,1590,222,44,-1,-1,1587,1587,42,45,45,1581,25,27,-1,103,-1,1585,-1,34,36,37,35,35,72,72,-1,-1,1602,74,-1,-1,1603,1601,70,70,67,68,69,1579,-1,-1,56,-1,59,59,1595,-1,-1,12,260,-1,10,9,2617,13,12,1620,-1,1604,1605,1603,1603,1606,46,116,-1,-1,-1,115,115,2620,2621,2622,1594,123,122,2620,2623,2620}},{0,0,{1774,1775,1776,1777,2626,182,183,1778,2627,1779,1780,2628,1782,188,2629,1783,1784,1785,1786,1787,1788,195,196,197,198,199,200,201,202,1789,1790,205,206,207,208,1791,1792,1793,1794,211,1795,1796,214,215,216,268,2630,2631,2632,2632,2633,1798,1799,1800,1801,225,1802,1803,1804,1805,2634,2635,230,254,232,233,234,235,2636,237,238,239,1808,1809,2637,1810,242,243,244,1811,246,247,1812,1813,249,250,251,1814,2638,253,254,255,1815,1816,1817,1818,1819,259,1820,1821,1822,1823,1824,1825,264,265,266,267,268,269,270,271,1826,272,273,1827,1828,1829,276,1830,1831,1832,1833,1834,1835,1836,283,284}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2639,-1,-1,-1,2639,2640,2641,1838,1839,2642,2643,1842,1843,1844,1845,1846,1847,1848,1849,1850,2644,2645,1853,1854,1855,2646,1857,2647,2648,2649,2650,1862,1863,1864,1865,1866,1867,1868,1869,1870,1871,1872,2651,2652,1875,1876,1877,1878,1879,1880,1881,2653,2654,2655,1885,1886,1887,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1676,1677,2150,1677,2151,1677,1681,1676,716,2656,2656,787,2657,2657,2657,1721,1688,1688,1688,1690,1690,1690,1692,1692,1696,1696,1696,1696,1697,1697,1697,1697,2658,2159,2659,2660,2159,1702,2160,2661,1705,1705,2662,1723,1697,1697,2663,1710,2664,2162,2664,2665,2420,212,2420,909,910,911,1717,1718,1718,1719,1720,2165,1722,1697,1697,1697,1722,1722,1723,1723,2666,2666,2666,2666,2667,2667,1727,1727,1727,2668,2669,1940,1941,1941,1942,2185,2670,2670,2671,2672,1736,2673,2674,1738,2675,2676,1947,1742,1950,1950,1950,2677,1742,2665,1952,2678,2678,2678,2667,2667,1748,2679,1750,1752,1752,1750,1750,1957,2680,1755,1960,1757,280,2159,1705,2681}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2682,2682,1762,1764,1764,1764,1765,2683,1765,1967,1768,1967,1768,1762,2684,1762,1967,1967,-1,1967,-1,1768,2684,-1,-1,1762,1765,1762,1762,1768,1762,1765,1764,2685,1967,1967,1967,1967,1967,-1,1967,-1,-1,-1,-1,1967,1967,1967,-1,-1,1967,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1971,1972,1973,2686,1975,2687,1977,1978,1979,1980,1981,2688,1983,1984,2689,1986,2690,1988,1989,1990,1991,1992,1993,2691,1995,1996,1997,1998,1999,2000,2001,2692,2003,2004,2005,2693,2007,2008,2009,2010,2694,2695,2696,2697,2698,2016,2017,2018,2019,2020,2021,2022,2699,2700,2701,2026,2702,2703,2704,2705,228,2031,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041,2706,2707,2044,2045,2046,2708,2709,245,2048,2049,2710,2711,2052,2712,2054,2055,2056,2057,2058,2713,2060,2061,2714,2063,2715,2716,2717,2067,2718,2069,2719,2071,2072,2073,2074,2075,2076,2077,2078,2079,2080,2081,2082,274,2083,2084,2085,2720,278,2087,2721,2722,2723,2724,2092,2725}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2094,2094,2095,2096,2097,2098,2099,2726,2101,2102,2103,2727,2105,2106,2728,2108,2729,2730,2729,2731,2732,2113,2733,2115,2729,2116,2117,2118,2119,2120,2121,2122,2123,2124,2734,2735,2736,2737,2738,2739,2131,2132,2133,2134,2135,2136,2137,2740,2741,2742,2141,2142,2143,2144,2145,2146,2743,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{2,2,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,33,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,2744,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126}},{0,0,{2745,2746,2747,2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2759,2760,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,127,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{2761,2762,1563,1564,374,375,2763,2764,1567,2765,186,2766,2767,1570,1571,2768,2152,716,2769,2770,1577,1578,2185,1580,2771,2772,26,2773,28,1583,30,2774,2775,2776,2777,795,2778,34,38,33,1715,2779,2780,42,43,2781,800,2782,2783,2784,2785,2786,1591,1592,1593,2787,1733,1735,2788,58,2789,2790,2791,2792,1598,1599,1600,1601,2793,68,1731,1729,71,2794,1747,74,1603,2795,820,1606,79,629,248,1607,1702,1608,1609,86,2796,1611,822,1613,1614,1615,256,257,258,2797,97,2798,1618,1619,262,263,628,2799,2800,1620,107,108,109,110,1705,112,113,114,115,116,1750,1621,119,120,279,280,281,2801,125,2802}},{0,1,{2817,2818,2819,2283,2288,2820,2291,2821,2159,1702,2822,2823,2824,2825,2826,2827,2828,2829,2830,2831,2832,2833,2834,2835,1755,-1,2836,2837,2838,2839,2881,2840,2841,2842,2843,2844,2845,2846,2847,2882,-1,2848,-1,2849,2850,2851,2852,2281,2853,2854,2855,2856,2857,1715,1714,1716,910,911,1717,2858,1718,1719,1720,2859,-1,2778,-1,2860,1704,2860,1723,2861,-1,2862,-1,2863,-1,2864,1727,-1,2865,2866,2669,1729,1730,2867,-1,-1,1721,-1,1734,-1,1736,2673,2674,1738,2188,2868,1741,2767,1744,2869,2870,2677,1742,2665,1746,2871,1701,2872,2873,2338,1748,2874,2875,2876,-1,2877,122,2878,1754,2245,2879,2880,280,-1,-1,2165}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2803,2804,2805,2806,127,2807,651,2808,2809,2810,132,362,134,135,136,361,138,139,2377,141,142,143,144,2811,1622,291,2812,1623,150,367,368,292,365,366,156,364,158,2813,2814,2815,162,163,1624,363,2816,167,168,169,1626,171,172,173,174,175,176,177,178,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2883,2883,2884,2885,2886,2887,2888,2889,2890,2889,2891,2889,2892,2893,2894,2895,2896,2897,2898,2899,2900,2901,2902,2903,2904,2905,2906,2907,2908,2909,2910,2911,2912,2913,2914,2915,2916,2917,2918,2919,2920,2921,2922,2923,2924,2925,2926,2927,2928,2929,2921,2930,2931,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1676,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2932,2932,2933,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2934,213,-1,-1,-1,-1,2935,2936,2937,2938,-1,-1,-1,-1,-1,-1,2939,2671,-1,2940,2941,2942,-1,-1,-1,-1,-1,-1,2943,2944,2945,1940,2666,2666,2946,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2947,-1,-1,-1,2948,2949,-1,-1,-1,2950,2951,-1,2952,-1,2953,-1,2954,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,127,127,2641,1838,1839,1840,2643,1842,1843,1844,1845,1846,1847,1848,1849,1850,2644,2645,1853,1854,1855,2646,1857,2647,2648,2649,2650,1862,1863,1864,1865,1866,1867,1868,1869,1870,1871,1872,2651,2652,1875,1876,1877,1878,1879,1880,1881,2653,2654,2655,1885,1886,1887,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1562,179,1563,1564,374,375,1565,1566,1567,1568,186,1569,187,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,25,26,1582,28,1583,30,1584,1585,33,34,35,36,1586,38,33,39,40,1587,42,43,44,45,46,222,1588,1589,1590,1591,1592,1593,1594,55,56,1595,58,59,1596,1597,62,1598,1599,1600,1601,67,68,69,70,71,72,1602,74,1603,1604,1605,1606,79,80,81,1607,83,1608,1609,86,1610,1611,1612,1613,1614,1615,256,257,1616,96,97,1617,1618,1619,262,263,103,104,105,1620,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126}},{0,0,{2955,2956,2957,2958,2959,2960,2961,1690,2962,2963,2964,2965,2966,2967,2968,2969,2970,2971,2972,2973,2974,2975,2976,2977,2978,2979,2980,1719,2981,2982,2982,2982,2983,2984,2985,2986,2987,2987,2988,2989,212,2990,2991,-1,2982,2992,2993,2936,2994,2938,2982,2982,2982,2982,2982,2982,2995,2996,2997,2940,2998,2942,2999,1738,3000,2982,2982,2982,224,3001,3002,3003,3004,2666,3005,2982,-1,3006,-1,-1,-1,-1,-1,-1,3007,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3008,3009,-1,-1,-1,3010,-1,-1,-1,2874,2936,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2947,-1,3011,-1,3012,2949,3013,3014,3015,2950,2951,3016,2952,3017,2953,3018,2954,3019,3020,3021,2982,-1,-1,3022,-1,3023,-1,-1,-1,3026,3027,-1,3028,3029,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1676,1677,1678,3030,3031,1890,3032,1676,1682,3033,875,3034,1685,1893,1679,1687,1894,1895,1688,1689,1690,1896,1897,887,1693,1694,1695,3035,1899,1697,1697,1697,1698,1901,2659,2660,1901,1702,1703,2661,1705,1706,1909,1910,1697,1697,2663,3036,1917,1916,1917,1713,2420,212,2420,909,910,911,1717,1718,3037,3038,204,1721,1722,1697,1697,1697,1722,1722,1723,1724,1725,1725,1725,1725,922,1934,3039,3039,3039,925,1728,1940,1941,1941,1942,1938,1921,2670,2671,2672,1736,1953,1712,1945,3040,1740,3041,1948,1949,1950,1950,1951,1948,1713,1952,1953,1953,1953,1954,1954,1748,1749,1750,3042,1752,1750,1750,1957,1958,1959,1960,1757,1758,1759,1760,1761}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,960,961,1963,1763,1764,1762,1765,1966,1765,1967,1768,1968,1967,971,1769,1762,-1,973,974,-1,-1,1768,-1,-1,-1,3043,1765,1762,1969,1768,1762,1765,1772,1773,1967,1967,1967,981,982,-1,983,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{985,2480,2481,988,989,990,2482,992,2483,3044,2484,996,2485,998,2486,1000,2487,2488,2489,2490,1005,2491,1007,2492,2493,1010,2494,1012,3045,2496,1015,2497,1017,1018,2498,3046,2500,3047,2502,2503,2504,2505,2506,2507,2508,2509,2510,1032,2511,2512,2513,3048,2515,1038,1039,2516,2517,3049,3050,2520,3051,2522,3052,3053,1049,2526,3054,2528,1053,1054,3055,1056,1057,1058,1059,1060,2533,2534,1063,1064,3056,2536,2537,2538,1069,1070,3057,2540,1073,2541,2542,1076,1077,3058,2545,3059,1081,3060,1083,1084,2548,2549,2550,2551,2552,2553,2554,2555,1093,2556,1095,2557,1097,3061,2559,1100,1101,2560,2561,3062,3063,3064,3065,3066,3067,3068,3069,3070}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3071,3072,3072,3073,3074,3075,3076,3077,127,3078,3079,3080,3081,132,3082,134,3083,136,3084,138,139,3085,141,3086,3087,3088,3089,3087,3090,3087,3091,3092,3093,3094,3095,3096,3097,3098,3099,3100,3101,160,3102,3103,3104,3105,3106,3107,3108,3109,3110,3111,3112,3113,173,3114,3115,3116,3117,1158,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{2761,1676,3118,2150,781,1677,3119,3120,3121,-1,-1,-1,-1,-1,3122,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3123,3124,3125,-1,-1,-1,3126,3126,1697,3127,3128,3129,3130,3131,3132,2990,3133,3134,3135,3136,911,3137,3138,803,3139,3140,3141,3142,3143,3144,3145,3146,2788,3147,1736,2999,1738,2942,-1,3148,3149,3150,2662,224,3002,1940,2678,2666,241,3151,3152,-1,2667,-1,79,-1,-1,3153,3154,-1,-1,-1,3155,-1,-1,-1,91,3156,3157,94,-1,-1,-1,-1,-1,3158,-1,-1,-1,-1,-1,-1,-1,-1,3159,-1,-1,-1,-1,-1,2243,-1,-1,-1,-1,-1,3160,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,3161,3162,3163,3164,3165,3166,3167,3168,3169,3170,3171,3172,3173,3174,3175,3176,3177,3178,3179,3180,289,3181,-1,3182,-1,-1,3183,-1,-1,3184,3183,3185,3186,3187,3188,3189,3190,3191,3192,3193,3194,3195,3196,3197,3198,3199,3200,3201,-1,3202,3203,-1,3204,3205,3206,3207,3208,3179,3209,3210,3211,3202,3212,3213,3214,3215,3216,-1,3217,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3218,3219,3220,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1774,1775,3221,3222,181,182,183,3223,3224,3225,186,1781,3226,3227,3228,190,191,192,3229,1787,3230,3231,196,197,198,199,200,3232,202,3233,3234,3235,3236,3237,3238,3239,3240,3241,210,3242,1795,3243,3244,215,3245,3246,218,3247,3248,3249,1797,50,3250,3251,1801,3252,3253,3254,3255,3256,3257,3258,59,3259,3260,3261,3262,235,236,3263,3264,3265,3266,3267,240,3268,242,3269,244,1811,3270,3271,3272,1813,249,3273,3274,3275,252,3276,3277,255,3278,3279,3280,1818,3281,3282,1820,3283,1822,3284,3285,1825,264,265,3286,3287,268,269,270,271,1826,272,273,3288,3289,3290,276,3291,3292,3293,3294,3295,3296,3297,3298,3299}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3300,3301,3302,3303,3304,3305,3306,3307,3308,3309,3310,3311,3312,3313,3314,3315,3316,3317,3318,3319,3320,147,3321,3322,3323,151,152,153,154,155,156,157,3324,3325,3326,3327,162,163,3328,3329,3330,167,168,3331,1626,3332,3333,3334,174,3335,176,3336,3337,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{203,1789,3338,3339,181,182,183,3340,3341,3342,1780,1781,1782,188,189,1783,1784,1785,3343,1787,1788,195,196,197,3342,199,200,3344,3345,203,3346,3347,3341,3348,3349,1791,1792,3350,3351,207,1795,1796,214,215,216,217,218,3352,220,221,3353,1798,1799,1800,1801,225,1802,1803,1804,1805,1806,229,230,231,232,233,234,235,1807,237,238,239,1808,1809,240,3354,242,243,244,3355,246,247,1812,1813,249,3356,3357,3358,3359,253,254,255,1815,1816,1817,3360,1819,259,1820,3361,1822,1823,1824,1825,264,265,266,3362,268,108,270,3340,3363,3364,273,1827,1828,1829,276,3365,3366,3367,1833,1834,3368,1836,3369,284}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3370,3370,128,129,130,3371,132,3372,134,135,136,137,138,139,140,141,142,143,144,145,1622,147,148,1623,150,151,152,153,154,155,156,157,158,159,160,161,162,163,1624,1625,166,167,168,169,1626,171,172,173,174,175,176,177,178,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1971,1972,1973,2686,1975,2687,1977,1978,1979,1980,1981,2688,1983,1984,2689,1986,2690,1988,1989,1990,1991,1992,1993,2691,1995,1996,1997,1998,1999,2000,2001,2692,2003,2004,2005,2693,2007,2008,2009,2010,3373,2695,2696,2697,2698,2016,2017,2018,2019,2020,2021,2022,2699,2700,2701,2026,2702,2703,2704,2705,228,2031,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041,2706,2707,2044,2045,2046,2708,3373,245,2048,2049,2710,2711,2052,2712,2054,2055,2056,2057,2058,2713,2060,2061,2714,2063,2715,2716,2717,2067,2718,2069,2719,2071,2072,2073,2074,2075,2076,2077,2078,2079,2080,2081,2082,274,2083,2084,2085,2720,278,2087,2721,2722,2723,2724,2092,2725}},{0,0,{3374,3375,3376,3377,3378,3379,3380,3381,3382,3383,3384,3385,3386,3387,3388,3389,3390,3391,3392,3393,3394,3395,3396,3397,3398,3399,3400,3401,3402,3403,3404,3405,3406,3407,3408,3409,3410,3411,3412,3413,3414,3415,3416,3417,3418,3419,3420,3421,3422,3423,3424,3422,3425,3426,3427,3428,3429,3430,3431,3432,3433,3434,3435,3436,3437,3438,3439,3440,3441,3442,3443,3444,3445,2664,3446,3447,3448,3006,3449,3450,3451,3452,3010,3453,2943,3454,3455,3456,3457,2999,3458,3459,3460,1736,3461,3462,3463,3464,3465,3466,3467,3468,3469,3470,3471,3472,3009,3473,3474,3475,3476,3477,3478,3479,1750,1752,3480,1957,3137,3481,3482,3483,2968,3484,3485,2159,3486,3487}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,466,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3488,-1,-1,-1,-1,1366,1366,308,3489,3490,3491,3492,481,3493,481,3494,3495,3496,3497,3498,3499,3500,3498,3500,490,3498,491,3498,330,3501,494,495,496,3502,3503,3504,3505,1367,1368,3506,3507,341,342,3508,506,507,508,509,510,511,3509,2605,3506,2606,351,516,308,3510,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466}},{0,0,{2955,2956,2957,2958,2959,2960,2961,1690,2962,2963,2964,2965,2966,2967,2968,2969,2970,2971,2972,2973,2974,2975,2976,2977,2978,2979,2980,1719,2981,2982,2982,2982,2983,2984,2985,2986,2987,3437,2988,2989,212,213,2991,-1,2982,2992,2935,2936,3511,2938,2982,2982,2982,2982,2982,2982,3512,2996,3513,2940,3514,2942,2999,1738,3000,2982,2982,2982,224,3001,3002,3003,3004,2666,3005,241,-1,3006,-1,-1,2659,3515,-1,-1,3007,-1,-1,3516,-1,-1,3516,-1,-1,-1,-1,-1,-1,-1,-1,3379,-1,-1,-1,-1,-1,-1,3008,3009,-1,-1,-1,3010,-1,-1,-1,2874,2936,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2947,-1,3011,-1,3012,2949,3013,3014,3015,2950,2951,3016,2952,3017,2953,3018,2954,3019,3517,3021,2982,-1,-1,3022,-1,3023,-1,-1,-1,3518,3519,3520,3521,3522,3521,3522,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1676,1677,2150,1677,2151,1677,1681,1676,716,1683,1683,2152,2153,2153,2153,1721,1688,1688,1688,1690,1690,1690,1692,1692,1696,1696,1696,1696,1697,1697,1697,1697,2158,2159,1700,1701,2159,1702,2160,1704,1705,1705,1707,1723,1697,1697,1709,1710,3523,2162,3523,2665,1714,1715,1714,1716,910,911,1717,1718,1718,1719,1720,2165,1722,1697,1697,1697,1722,1722,1723,1723,2794,2794,2794,2794,1747,1747,1727,1727,1727,2866,2669,1729,1730,1730,1731,2185,1733,1733,1734,1735,1736,2673,2674,1738,2188,2868,1741,1742,1744,1744,1744,2677,1742,2665,1746,2871,2871,2871,1747,1747,1748,2679,1750,1752,1752,1750,1750,1753,1754,1755,1756,1757,280,2159,1705,3524}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2682,2682,1762,1764,1764,1764,1765,3525,1765,1767,1768,1767,1768,1762,3526,1762,1767,-1,-1,1767,-1,1768,-1,-1,-1,1762,1765,1762,1762,1768,1762,1765,1764,2685,1767,1767,1767,1767,1767,-1,1767,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{3527,3527,3527,3528,3529,3530,3531,3532,3533,3534,3535,2152,3536,3537,3538,2820,2267,2267,2267,2268,2268,2268,2269,2270,3539,3540,1696,3541,1697,1697,1697,1697,3542,2278,2279,2817,2281,1702,2283,1704,2285,3543,1707,2288,1697,1697,1709,2292,3544,2294,3545,2296,1714,3546,1714,3547,3548,45,45,3549,3550,3551,3552,3553,2307,1697,1697,1697,1722,1722,2312,2312,2313,2314,2314,2315,2316,1747,3554,3555,3556,3557,2322,2323,2324,2325,2326,2185,2328,2328,3558,3558,2332,2332,3559,1738,2188,2336,2336,1742,1744,1744,1744,2677,1742,2665,1746,2344,2871,2871,1747,1747,3560,2882,3561,3562,3563,318,1750,3563,318,3564,3565,1757,3566,3567,3568,3524}},{0,1,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,293,3569,3570,3571,3572,3573,3574,3575,3576,2373,3577,2375,3578,3579,2377,3580,3581,-1,-1,2379,-1,-1,-1,-1,-1,2381,3582,3583,3584,3585,-1,-1,-1,-1,3586,3587,-1,-1,-1,-1,2391,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{2252,2253,2254,2255,2256,2257,2258,2259,2260,2261,2262,2263,2264,2265,2266,1578,2267,2267,2267,2268,2268,2268,2269,2270,3588,2272,2273,2274,34,2275,33,2276,2277,2278,2279,2280,2281,2282,2283,2284,2285,2286,2287,2288,2289,2290,2291,2292,2293,2294,2295,2296,2297,2298,2299,2300,2301,45,45,2302,2303,2304,2305,2306,2307,1697,2308,2309,2310,2311,2312,2312,2313,2314,2314,2315,2316,2317,2318,2319,2320,2321,2322,2323,2324,2325,2326,2327,3589,2329,2330,2331,3590,3591,2333,2334,2335,2336,2337,2338,2339,2340,2341,275,2342,2343,1746,2344,2345,2346,2347,2348,2349,2350,2351,2352,2353,2354,2355,2356,2357,2358,2359,2360,122,2361,2362,2363}},{0,0,{2955,2956,2957,2958,2959,2960,2961,1690,2962,2963,2964,2965,2966,2967,2968,2969,2970,2971,2972,2973,2974,2975,2976,2977,2978,2979,2980,1719,2981,2982,2982,2982,2983,2984,2985,2986,2987,2987,2988,2989,212,213,2991,-1,2982,2992,2935,2936,3511,2938,2982,2982,2982,2982,2982,2982,3512,2996,3513,2940,3514,2942,2999,1738,3000,2982,2982,2982,224,3001,3002,3003,3004,2666,3005,2982,-1,3006,-1,-1,-1,-1,-1,-1,3007,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3008,3009,-1,-1,-1,3010,-1,-1,-1,2874,2936,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2947,-1,3011,-1,3592,2949,3013,3014,3015,2950,2951,3018,2952,3017,2953,3018,2954,3019,3517,3021,2982,-1,-1,3022,-1,3023,-1,-1,-1,3026,3027,-1,3028,3593,-1,-1,-1,-1,-1,3594,-1,-1,3595,3596,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3597,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,3643,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685,3686,3687,3688,3689,3690,3691,3692,3693,3694,3695,3696,3697,3698,3699,3700,3701,3702,3703,3704,3705,3706,3707,3708,3709,3710,3711,3712,3713,3714,3715,3716,3717,3718,-1,-1,3719,3720,3721,-1,3722}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3723,3723,3724,3725,3726,3727,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737,3738,3739,3740,3741,3742,3743,3744,3745,3746,3747,3748,3749,3750,3751,3752,3753,3754,3755,3756,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770,3771,3772,3773,3774,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,48,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3775,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784,3785,3786,3787,3788,3789,3790,3791,3792,3793,3794,3795,3796,3797,3798,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813,3814,3815,3816,3817,3818,3819,3820,3808,3821,3822,3823,3801,3802,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3824,3824,3825,3826,2886,3827,3828,3829,2889,3830,3831,3832,2888,3833,2890,3834,2891,3835,3836,2899,2900,3837,2902,3838,3839,3840,3841,2907,2908,2909,3842,2911,2912,2913,2914,2915,2916,2917,3843,3844,3845,3846,3847,3848,2924,3849,3850,2914,3851,2929,3852,2907,2908,3853,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{3854,3855,3856,3857,3858,3859,3860,3861,3862,3863,3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3876,3877,3878,3879,3880,3881,3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893,3894,3895,3896,3897,3898,3899,3900,3901,3902,3903,3904,3905,3906,3907,3908,3909,3910,3911,3876,3912,3913,3914,3915,3876,3916,3917,3918,3876,3919,3876,3920,3876,3921,3876,3922,3876,3923,3924,3925,3926,3927,3928,3876,3929,3930,3876,3931,3932,3933,3934,3935,3876,3876,3936,3876,3937,3876,3938,3876,3939,3940,3876,3941,3876,3876,3942,3876,3876,3876,3876,3876,3876,3876,3876,3943,3876,3876,3876,3876,3876,3876,3944,3945,3876,3876,3876,3876}},{0,0,{3946,3947,3948,3949,3950,3951,3952,3953,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3954,3955,3876,3956,3876,3957,3958,3959,3960,3961,3962,3963,3964,3964,3965,3962,3966,3876,3876,3967,3968,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876}},{0,0,{3969,3970,551,3971,867,3876,3972,552,3973,783,3974,3975,3976,3977,3978,3979,3980,716,3981,3982,3983,3984,3985,3986,3987,3988,3976,3989,913,522,522,3990,3991,3992,524,3993,525,3994,527,526,3995,3996,3997,3998,3999,4000,4001,700,4002,4003,537,4004,4005,3972,4006,4007,4008,933,4009,4010,934,4011,4012,4013,4014,924,4015,4016,4017,4018,815,816,4019,4020,4021,4022,4023,4024,4025,4026,4027,4028,4029,4030,4031,4032,4033,527,4034,4035,4036,3986,4037,4038,4039,4040,4041,4042,4043,4044,4041,261,4045,4046,2165,4047,4048,4049,829,4050,4051,4052,4053,4054,4055,4056,830,4057,830,4058,4059,830,4060,540,4061,4062,4063,550}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,4064,4065,4066,4067,4068,4069,4070,4071,4072,4073,4074,4075,4076,4077,4078,4079,4080,4081,4082,4083,4084,4085,4086,4087,4088,4089,4090,4091,4092,4093,4076,4074,4094,4095,4096,4097,4098,4099,4100,4101,4102,4103,4104,4105,4106,4107,4108,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{4109,4109,4110,4111,4112,4113,4113,4114,4115,4116,4117,4118,4119,4120,4121,4122,4123,4124,4125,4126,4127,4128,4129,4130,4131,4132,4133,4134,4135,4136,4137,4138,4139,4140,4141,4142,4143,4144,4145,4146,4147,4148,4149,4150,4151,4152,4153,4154,4155,4156,4157,4158,4159,4160,4161,4162,4160,4163,4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4174,4175,4176,4177,4178,4179,4180,4181,4021,4182,4183,4184,4185,4186,4187,4188,4189,4190,4191,4192,4193,4194,4195,4196,4197,4197,4198,4199,4200,4201,4202,4203,4204,4205,4206,4207,4208,4209,4210,4211,4212,4213,4214,4215,4216,4217,4216,4218,4219,4220,4221,4222,4223,4224,4225,4226,4227,4228,4229,4230}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,649,649,745,746,130,747,640,748,641,728,642,749,643,644,750,645,751,657,752,4231,734,754,755,4232,757,758,759,760,761,762,647,763,764,765,766,767,768,769,694,4233,166,167,168,169,170,772,773,646,775,845,777,778,696,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,4234,4235,4236,3611,3612,3613,3614,3615,3616,4237,3618,3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,4238,3638,3639,3640,3641,3642,3643,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685,3686,3687,3688,3689,3690,3691,3692,3693,3694,3695,3696,3697,3698,3699,3700,3701,3702,3703,3704,3705,3706,3707,3708,3709,3710,3711,3712,3713,3714,3715,3716,3717,3718,-1,-1,3719,4239,4240,-1,3722}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3723,4241,3724,3725,4242,3727,4243,3729,4244,3731,4245,3733,4246,4247,3736,4248,4249,3739,3740,3741,3742,4250,3744,3745,4251,3747,3748,3749,3750,3751,3752,3753,3754,3755,3756,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770,3771,3772,3773,3774,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{4252,4253,4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265,4266,4267,4268,4269,4270,4271,4272,4273,4274,4275,4276,4277,4278,4279,4280,4281,4282,4283,4284,4285,4286,4287,4288,4289,4290,4291,4292,4293,4294,4295,4296,4297,4298,4299,4300,4301,4302,4303,4304,4305,4306,4307,4308,4309,4310,4311,4312,4313,4314,4315,4316,4317,4318,4319,4320,4321,4322,4323,4324,4325,4326,4327,4328,4329,4330,4331,4332,4333,4334,4335,4336,4337,4338,4339,4340,4341,4342,4343,4344,4345,4346,4347,4348,4349,4350,4351,4352,4353,4354,4355,4356,4357,4358,4359,4360,4361,4362,4363,4364,4365,4366,4367,4368,4369,4370,4371,4372,4373,4374,4375,4376,4377,4378,4379}},{0,0,{4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4381,4382,4383,4384,4385,4386,4387,4388,4389,4390,4391,4392,4393,4394,4395,4396,4397,4395,4398,4399,4395,4400,4395,4401,4402,4403,4404,4405,4406,4407,4408,4409,4410,4411,4412,4413,4414,4415,4416,4417,4418,4419,4420,4421,4422,4423,4424,4425,4425,4425,4425,4425,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380,4380}},{0,0,{3969,4426,4427,3971,867,4428,4429,884,4430,4431,4432,3975,4433,4434,4435,4436,3980,4437,4438,4439,4440,4441,4442,4443,4444,4445,4446,4447,913,522,4448,4449,3991,4450,4451,4452,4453,4454,527,4455,4456,4457,4458,4459,4460,4461,4462,4463,4464,3999,4465,4466,4467,4468,4469,4470,4471,4472,4473,4474,4475,4476,4477,4478,4479,4480,4481,4482,4483,4484,4485,4486,4487,4488,4489,4490,4491,4492,4493,4494,4495,4496,4497,4498,4499,4500,4501,4502,4503,4504,4505,4506,4507,4508,4509,4510,4511,4512,4513,4514,4515,4516,4517,4518,4519,4520,4521,4522,4523,4524,4525,4526,4527,4528,4529,4530,4531,4532,4533,4534,4535,4536,4060,4537,4538,4062,4539,4540}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,4381,4382,4383,4384,4385,4386,4387,4388,4389,4390,4391,4392,4393,4394,4395,4396,4397,4395,4398,4399,4395,4400,4395,4401,4402,4403,4404,4405,4406,4407,4408,4409,4410,4411,4412,4413,4414,4415,4416,4417,4418,4419,4420,4421,4422,4423,4424,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{4541,4542,4543,4544,4545,4546,4547,4548,4549,4550,4551,4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567,4568,4569,4570,4571,4572,4573,4574,4575,4576,4577,4578,4579,4580,4581,4582,4583,4584,4585,4586,4587,4588,4581,4589,4590,4591,4592,4593,4594,4595,4596,4597,4598,4599,4600,4601,4602,4603,4604,4605,4605,4606,4607,4608,4609,4610,4611,4612,4613,4614,4615,4616,4617,4618,4619,4620,4621,4622,4623,4624,4625,4626,4627,4628,4629,4630,4631,4632,4633,4634,4635,4636,4637,4638,4639,4640,4641,4642,4643,4644,4645,4646,4647,4648,4649,4650,4651,4652,4653,4654,4655,4656,4657,4658,4659,4660,4661,4662,4663,4664,4665,4666}},{0,0,{4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4668,4669,4670,4671,4672,4673,4674,4675,4676,4677,4678,4679,4680,4681,4682,4683,4684,4685,4686,4687,4688,4689,4690,4691,4692,4693,4694,4695,4696,4697,4698,4699,4700,4701,4677,4702,4703,4704,4705,4706,4707,4708,4709,4710,4711,4712,4713,4714,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667,4667}},{0,0,{4541,4542,4543,4544,4545,4546,4547,4548,4549,4550,4551,4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567,4568,4569,4715,4716,4572,4573,4574,4575,4576,4577,4578,4579,4580,4581,4582,4583,4584,4717,4586,4587,4588,4581,4589,4590,4591,4592,4593,4594,4595,4596,4597,4598,4599,4718,4601,4719,4603,4604,4605,4605,4606,4607,4608,4609,4610,4611,4612,4613,4614,4615,4616,4617,4618,4720,4620,4721,4622,4623,4624,4625,4626,4627,4628,4629,4630,4631,4632,4633,4634,4635,4636,4637,4638,4639,4640,4641,4642,4643,4644,4722,4723,4724,4648,4649,4650,4651,4652,4653,4654,4725,4656,4657,4726,4659,4660,4661,4662,4663,4664,4665,4666}},{0,0,{4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4728,4728,4728,4728,4729,4728,4728,4728,4668,4669,4730,4671,4672,4673,4731,4675,4676,4677,4678,4679,4680,4681,4682,4683,4684,4685,4686,4687,4688,4689,4690,4691,4692,4693,4694,4695,4696,4697,4698,4699,4700,4701,4677,4702,4703,4704,4732,4733,4707,4734,4735,4710,4711,4712,4713,4714,4728,4728,4728,4728,4728,4736,4727,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737}},{0,0,{1383,1384,1385,1386,1387,1388,1389,1390,1391,1392,1393,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403,1404,1405,1406,4738,4739,4740,1410,1411,1550,1551,1414,1552,1416,1417,4741,1554,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,4742,1431,4743,4744,4745,4746,1436,1437,1438,1439,1440,1555,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1466,4747,1468,1469,1470,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,4748,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1510,1511,1512,4749,1514,4750,1516,4751,1518,1519,1520,1521,1522,1523,1524,1525,1526,1527,1528,1529,1557,1531,1524,1558,1526,1533,1534,1535,1536,1537,1538,1539,1540,1541,1519,1542,1543,1543,1543,1543,1544,1545,1546,1547,1543,1548,1549,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{4752,4753,4754,4755,4756,4757,4758,1374,4759,1375,1376,4760,12,1377,1378,1379,4761,4762,4763,4764,20,4765,4766,4767,4768,4769,4770,4771,4772,4773,4774,4775,4776,4777,4778,4779,4780,4781,4782,4783,4784,4785,4786,4787,4788,4789,4790,409,4791,4792,4793,4794,4795,4796,4797,414,4798,4799,4800,4801,4802,4803,4804,4805,4806,4807,4808,426,4809,4810,69,4811,4812,4813,4814,74,75,76,431,432,4815,4816,4817,82,4818,4819,85,4820,4821,436,4822,4823,4824,4825,439,4826,440,96,97,4827,4828,442,443,4829,103,4830,445,446,447,4831,4832,4833,4834,452,453,454,455,456,457,458,459,460,461,4835,4836,4837,464,4838}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,4839,4839,4840,4841,4842,4843,4844,4845,4846,4847,4848,4849,4850,4851,4852,4853,4854,4855,4856,4857,4858,4859,4858,4860,4861,4862,4863,4864,4865,4866,4867,4868,4869,4870,4871,4872,4873,4874,4875,4876,4877,4878,4879,-1,-1,4880,4881,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{3969,3970,551,3971,867,3876,3972,552,3973,783,3974,3975,3976,3977,4882,3979,3980,716,4883,3982,3983,3984,3985,3986,3987,3988,4446,3989,913,4884,4885,4886,3991,3992,524,3993,525,4887,527,526,3995,3996,3997,3998,3999,4888,4001,700,4002,4003,537,4004,4889,4890,4006,4007,4008,933,4009,4010,934,4011,4012,4013,4014,924,4015,4016,4017,4018,815,816,4019,4020,4021,4022,4023,4024,4025,4026,4027,4028,4891,4030,4031,4892,4893,527,4894,4895,4036,3986,4037,4896,4039,4040,4041,4042,4043,4897,4898,261,4045,4899,2165,4047,4048,4049,829,4050,4051,4052,4053,4900,4055,4056,4901,4902,833,4058,4059,547,4060,540,4061,4062,4063,550}},{0,0,{649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,4903,4904,4905,645,4906,4907,4908,4907,4909,4907,4910,4911,4912,4913,4914,4915,4916,4917,4918,4919,4920,4921,4922,4923,4924,4925,4926,4927,4928,4929,4930,4931,4932,4933,769,4934,771,4935,4936,4937,4938,4939,4940,4941,4942,4943,4944,4945,4946,4947,4948,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649,649}},{0,0,{4949,4950,4951,4952,4953,4954,4955,4956,4957,4958,4959,4960,4961,4962,4963,4964,4965,4966,4967,4968,4969,4970,4971,4972,4973,4974,4975,4976,4977,4978,4979,4980,4981,4982,4983,4984,4985,4986,4987,4988,4989,4990,4991,4992,4993,4994,4995,4996,4997,4998,4999,5000,5001,5002,5003,5004,5005,5006,5007,5008,5009,5010,5011,5012,5013,5014,5015,5016,5017,5018,5019,5020,5021,5022,5023,5024,5025,5026,5027,5028,5029,5030,5031,5032,5033,5034,5035,5036,5037,5038,5039,5040,5041,5042,5043,5044,5045,5046,5047,5048,5049,5050,5051,5052,5053,5054,5055,5056,5057,5058,5059,5060,5061,5062,5063,5064,5065,5066,5067,5068,5069,5070,5071,5072,5073,5074,5075,5076}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5077,5078,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5079,5080,5081,5082,5083,5084,5085,5086,5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102,5103,5104,5105,5106,5107,5108,5109,5110,5111,5112,5113,5114,5115,5116,5117,5118,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133,5134,5135,5136,5137,5077,5078,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{4380,986,987,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,5138,1014,1015,1016,1017,1018,1019,5139,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,5140,1037,1038,1039,1040,1041,5141,5142,1044,5143,1046,5144,5145,2525,1050,5146,1052,1053,2529,5147,1056,1057,2531,2532,1060,1061,1062,1063,1064,5148,1066,1067,1068,1069,1070,5149,1072,1073,1074,1075,1076,2543,5150,1079,5151,1081,5152,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,5153,1109,5154,4380,4380}},{0,0,{5155,5156,5157,5158,5159,5160,5161,1119,5162,5163,5164,5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,5176,5177,5178,5179,5180,1116,1112,1113,1114,1115,1116,310,1117,1118,1119,1115,5181,1121,5182,1123,5183,1125,5184,1126,5185,1128,1129,5186,1131,1132,5186,1132,1133,5186,2587,5187,492,1135,2590,2591,2592,1139,1140,5188,5189,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,310,1153,1154,5190,513,1145,514,1156,1157,1115,1158,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191,5191}},{0,0,{3969,5192,3876,3876,5193,3876,3876,3876,3876,3876,3876,381,3876,3876,4435,3876,3876,5194,3876,5195,5196,5197,3876,3876,3876,370,3876,5198,5199,5200,5201,5202,3876,5203,5204,5205,3993,3993,5206,527,5207,3876,3876,5208,5209,5208,829,5210,5211,3876,3876,5212,51,3876,3876,3876,5213,4008,5214,3876,419,5215,5213,422,3876,5216,5216,5217,427,3876,816,5218,3876,4020,3876,5219,429,3876,5220,432,5221,5222,81,3876,3876,3876,3876,3876,3876,3876,3876,90,3876,5223,439,5224,3876,3876,3876,3876,3876,3876,3876,5225,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,4901,3876,5226,3876,3876,3876,3876,3876,3876,3876,3876}},{0,0,{3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,5227,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,5237,5238,5239,5240,5241,5242,5243,5244,5245,5246,5247,5248,5249,5250,3876,3876,5251,761,5252,5253,5254,3876,3876,766,3876,3876,3876,5255,5256,5257,5258,5259,5260,3876,3876,5261,766,5262,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876,3876}},{0,0,{4541,4542,4543,4544,4545,4546,4547,4548,4549,4550,4551,4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567,4568,4569,4570,4571,4572,4573,4574,4575,4576,4577,4578,4579,4580,4581,4582,4583,4584,4585,4586,4587,4588,4581,4589,4590,4591,4592,4593,4594,4595,4596,4597,4598,4599,4600,4601,4602,4603,4604,4605,4605,4606,4607,4608,4609,4610,4611,4612,4613,4614,4615,4616,4617,4618,4619,4620,4621,4622,4623,4624,4625,4626,4627,4628,4629,4630,4631,4632,4633,4634,4635,4636,4637,4638,4639,4640,4641,4642,4643,4644,4645,4646,4647,4648,4649,4650,4651,4652,4653,4654,4655,4656,4657,4658,4659,4660,5263,4662,4663,4664,4665,4666}},{0,0,{4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4727,4728,4728,4728,4728,4728,4728,4728,4728,4668,4669,4670,4671,4672,4673,4674,4675,4676,4677,4678,4679,4680,4681,4682,4683,4684,4685,4686,4687,4688,4689,4690,4691,4692,4693,4694,4695,4696,4697,4698,4699,4700,4701,4677,4702,4703,4704,4705,4706,4707,4708,4709,4710,4711,4712,4713,4714,4728,4728,4728,4728,4728,4736,4727,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737,4737}},{0,0,{5264,5265,5266,5267,5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283,5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299,5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315,5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331,5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347,5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363,5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379,5380,5381,5382,5383,5384,5385,5386,5387,5388,5389,5390,5391}},{8,0,{5392,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5393,-1,-1,-1,-1,-1,-1,-1,-1,-1,5394,-1,5395,-1,-1,-1,5396,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5397,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5398,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{16,0,{5399,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5400,-1,-1,-1,-1,-1,-1,-1,5401,-1,-1,-1,-1,-1,5402,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5403,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{10,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5404,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{2,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5405,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5406,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5407,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5408,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{3,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5409,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{24,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5410,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{1,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5411,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{32,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5412,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5299,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{34,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5413,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{6,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5414,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424,5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,5439,5440,5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456,5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472,5473,5474,5475,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,16,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5415,5416,5417,5418,5419,5420,5421,5422,5476,5477,5425,5478,5427,5428,5479,5480,5481,5432,5482,5434,5483,5484,5485,5486,5487,5440,5441,5442,5443,5444,5488,5446,5489,5448,5449,5450,5451,5452,5453,5454,5455,5456,5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472,5473,5474,5475,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,25,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424,5425,5490,5427,5428,5479,5491,5481,5432,5482,5492,5483,5484,5485,5486,5487,5440,5441,5442,5443,5493,5488,5446,5489,5448,5449,5450,5451,5452,5453,5454,5455,5456,5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472,5473,5474,5475,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{5494,5495,5496,5497,5495,5498,5499,5500,5501,5502,5503,5505,5506,5507,5508,5509,5510,5512,5514,5507,5516,5517,5518,5519,5520,5521,5522,5523,28,29,30,31,32,33,34,35,36,37,38,33,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,127,5511,128,5513,130,131,132,5515,134,135,136,137,138,139,140,141,142,143,144,5504,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536,5537,5538,5539,5540,5541,5542,5540,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552,5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5565,5566,5567,5568,5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5578,5583,5584,5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600,5601,5602,5603,5543,5604,5578,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616,5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5537,5627,5563,5628,5629,5630,5631,5632,5633,5634,5635,5636,5637,5638,5639,5640,5641,5639,5642,5643}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5644,5645,5646,5647,5648,5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664,5665,5658,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680,5677,5681,5682,5683,5684,5685,5686,5687,5688,5689,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{5690,5691,5692,5693,5694,5695,5696,5697,5698,5698,5699,5700,5701,5702,5703,2823,5704,5705,5706,5707,2292,5708,3531,5709,3551,3436,5710,2820,5711,122,2354,5712,5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728,5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744,5729,5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,2874,5758,5759,5760,5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5738,5772,5729,5773,5774,5775,5776,5777,5778,5779,2852,5780,5781,5782,5783,5784,5785,5786,5787,5788,5789,5790,1755,5791,5792,5793,5794,5795,5796,5797,5798,5799,5800,5801,5802}},{0,1,{5803,5804,5805,5806,5807,716,5808,5809,5810,5811,5812,1736,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824,5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840,5841,5842,5843,5844,5845,5846,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,2,{3444,3462,3402,3460,2939,3382,2945,3404,3405,3374,3376,2943,3454,3470,5898,3400,3432,3449,3443,5899,3430,5900,3010,3453,3462,5901,3437,3412,3458,3459,3403,5902,5903,3377,5904,5905,5906,5907,5897,3480,3419,5908,5909,3445,5910,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856,5857,5858,5859,5860,5861,5862,5861,5863,5864,5865,5864,5866,5867,5866,5868,5869,5870,5871,5872,5873,5874,5875,-1,5866,5861,5876,5877,5878,5879,5880,5881,5864,5882,5883,5884,5885,5886,5887,5888,5889,5890,5891,5892,5893,5894,5895,5896,5897,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{1383,1384,1385,1386,1387,1388,1389,1390,5911,1392,1393,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403,1404,1405,1406,1407,1408,1409,1410,1411,1412,1413,1414,1415,1416,1417,1553,1419,1420,1421,1422,1423,1424,1425,1426,1427,5912,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1555,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,5913,1461,1462,1463,5914,1465,1466,1467,1468,1469,1470,1470,1471,1472,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1510,1511,1512,5915,1514,5916,1516,1517,1518,1519,1520,1521,1522,1523,1524,1525,1526,1527,1528,1529,1530,1531,1524,1558,1526,1533,1534,1535,1536,1537,1538,1539,1540,1541,1519,1542,1543,1543,1543,1543,1544,1545,1546,1547,1543,1548,1549,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},{0,0,{5917,5918,5919,5920,5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936,5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952,5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968,5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984,5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000,6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032,6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044}},{0,0,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,6045,6046,6047,6048,6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064,6065,6066,6067,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080,6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}},}; + +const BanksDump::InstrumentEntry g_embeddedBanksInstruments[] = +{ +{0,0,0,0,0,0,0x808,0x2314,0x189,{0,1}},{0,0,0,0,0,0,0x808,0x23E2,0x189,{2,3}},{0,0,0,0,0,0,0x808,0x23F0,0x2D0,{4,5}},{0,0,0,0,0,0,0x606,0x245A,0x190,{6,7}},{0,0,0,0,0,0,0,0x22A2,0x182,{8,3}},{0,0,0,0,0,0,0,0x23F6,0x190,{9,3}},{0,0,0,0,0,0,0x808,0x1C8A,0x459,{10,11}},{0,0,0,0,0,0,0xA0A,0x23FD,0xE2,{12,13}},{0,0,0,0,0,0,0,0x11F1,0x57E,{14,15}},{0,0,0,0,0,0,0x202,0x1CA5,0x445C,{16,17}},{0,0,0,0,0,0,0x202,0x938,0xACE,{18,19}},{0,0,0,0,0,0,0,0x23F0,0x2DD,{20,21}},{0,0,0,0,0,0,0,0x161,0x196,{22,23}},{0,0,0,0,0,0,0x404,0x26C,0x2D6,{24,25}},{0,0,0,0,0,0,0xC0C,0x1220,0x1589,{26,27}},{0,0,0,0,0,0,0x404,0x4BD,0x56A,{28,29}},{0,0,0,0,0,0,0x202,0x9C40,0x132,{30,31}},{0,0,0,0,0,0,0x202,0x9C40,0xC1,{32,33}},{0,0,0,0,0,0,0x404,0x9C40,0xC1,{34,35}},{0,0,0,0,0,0,0x404,0x9C40,0x84E,{36,37}},{0,0,0,0,0,0,0,0x9C40,0x916,{38,39}},{0,0,0,0,0,0,0xC0C,0x9C40,0x6A,{40,41}},{0,0,0,0,0,0,0xA0A,0x9C40,0x19D,{42,43}},{0,0,0,0,0,0,0x606,0x9C40,0x189,{44,45}},{0,0,0,0,0,0,0xC0C,0xFC1,0xE9,{46,47}},{0,0,0,0,0,0,0xC0C,0x3929,0x146,{48,49}},{0,0,0,0,0,0,0,0x23DC,0xE2,{50,51}},{0,0,0,0,0,0,0x606,0x11EA,0xE2,{52,53}},{0,0,0,0,0,0,0,0x9C40,0x6A,{54,55}},{0,0,0,0,0,0,0x808,0x9C40,0x8C,{56,57}},{0,0,0,0,0,0,0x808,0x9C40,0x8C,{58,59}},{0,0,0,0,0,0,0x808,0x47CC,0xE2,{60,61}},{0,0,0,0,0,0,0x202,0x9C40,0x140,{62,63}},{0,0,0,0,0,0,0xA0A,0x9C40,0xBA,{64,65}},{0,0,0,0,0,0,0xA0A,0x9C40,0x85,{66,67}},{0,0,0,0,0,0,0xC0C,0x9C40,0xA0,{68,69}},{0,0,0,0,0,0,0x808,0x9C40,0x42,{70,71}},{0,0,0,0,0,0,0x808,0x9C40,0x42,{72,73}},{0,0,0,0,0,0,0xA0A,0x9C40,0x85,{74,67}},{0,0,0,0,0,0,0x808,0x9C40,0x294,{75,76}},{0,0,0,0,0,0,0x808,0x9C40,0x2E4,{77,78}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{79,80}},{0,0,0,0,0,0,0x202,0x9C40,0x2D0,{81,82}},{0,0,0,0,0,0,0x202,0x9C40,0x2C2,{83,84}},{0,0,0,0,0,0,0,0x4DE,0x585,{85,86}},{0,0,0,0,0,0,0,0x120C,0xAAD,{87,88}},{0,0,0,0,0,0,0x202,0x1AE0,0x113D,{89,90}},{0,0,0,0,0,0,0xE0E,0x9C40,0xE2,{91,92}},{0,0,0,0,0,0,0,0x9C40,0x189,{93,94}},{0,0,0,0,0,0,0xA0A,0x9C40,0x506,{95,96}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5A0,{97,96}},{0,0,0,0,0,0,0,0x9C40,0x488,{98,99}},{0,0,0,0,0,0,0,0x9C40,0x10A,{100,101}},{0,0,0,0,0,0,0xC0C,0x9C40,0x47A,{102,103}},{0,0,0,0,0,0,0x808,0x459,0x55D,{104,105}},{0,0,0,0,0,0,0xC0C,0x9C40,0x8C,{106,107}},{0,0,0,0,0,0,0xC0C,0x9C40,0x8C,{108,109}},{0,0,0,0,0,0,0xC0C,0x9C40,0x139,{110,111}},{0,0,0,0,0,0,0x202,0x9C40,0x56,{112,113}},{0,0,0,0,0,0,0x202,0x9C40,0x71,{114,115}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{116,117}},{0,0,0,0,0,0,0,0x9C40,0xD5,{118,119}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{120,121}},{0,0,0,0,0,0,0x808,0x9C40,0x64,{122,123}},{0,0,0,0,0,0,0x808,0x9C40,0x92,{124,125}},{0,0,0,0,0,0,0xA0A,0x9C40,0x92,{126,127}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{128,129}},{0,0,0,0,0,0,0x808,0x9C40,0x71,{130,131}},{0,0,0,0,0,0,0x606,0x9C40,0x50,{132,133}},{0,0,0,0,0,0,0,0x9C40,0x7E,{134,135}},{0,0,0,0,0,0,0x404,0x9C40,0x16E,{136,137}},{0,0,0,0,0,0,0,0x9C40,0x71,{138,139}},{0,0,0,0,0,0,0,0x9C40,0x6A,{140,139}},{0,0,0,0,0,0,0x202,0x9C40,0x71,{141,142}},{0,0,0,0,0,0,0,0x9C40,0x6A,{143,144}},{0,0,0,0,0,0,0,0x9C40,0x294,{145,146}},{0,0,0,0,0,0,0,0x9C40,0x26C,{147,148}},{0,0,0,0,0,0,0xA0B,0x9C40,0x189,{149,150}},{0,0,0,0,0,0,0xA0B,0x9C40,0x189,{151,152}},{0,0,0,0,0,0,0,0x9C40,0x5D,{153,154}},{0,0,0,0,0,0,0,0x9C40,0x56,{155,156}},{0,0,0,0,0,0,0,0x9C40,0xDC,{157,158}},{0,0,0,0,0,0,0,0x9C40,0x71,{159,160}},{0,0,0,0,0,0,0,0x9C40,0x6A,{161,162}},{0,0,0,0,0,0,0x202,0x9C40,0x190,{163,164}},{0,0,0,0,0,0,0,0x9C40,0x2BC,{165,166}},{0,0,0,0,0,0,0xA0A,0x9C40,0x92,{167,168}},{0,0,0,0,0,0,0x202,0x9C40,0x585,{169,170}},{0,0,0,0,0,0,0xA0A,0x9C40,0x1205,{171,172}},{0,0,0,0,0,0,0xC0C,0x9C40,0x2BC,{173,174}},{0,0,0,0,0,0,0,0x9C40,0x1172,{175,176}},{0,0,0,0,0,0,0x808,0x9C40,0x495,{177,178}},{0,0,0,0,0,0,0,0x9C40,0x146,{179,180}},{0,0,0,0,0,0,0xA0A,0x9C40,0x49C,{181,182}},{0,0,0,0,0,0,0,0x9C40,0x189,{183,184}},{0,0,0,0,0,0,0x202,0x1CE8,0x224C,{185,186}},{0,0,0,0,0,0,0,0x9C40,0x1450,{187,188}},{0,0,0,0,0,0,0,0x14A6,0x2A5E,{189,190}},{0,0,0,0,0,0,0xC0C,0x9C40,0x28D,{191,192}},{0,0,0,0,0,0,0,0x2349,0x564,{193,194}},{0,0,0,0,0,0,0x606,0x9C40,0x14A6,{195,196}},{0,0,0,0,0,0,0x606,0x9C40,0x1492,{197,198}},{0,0,0,0,0,0,0x404,0x9C40,0x1309,{199,200}},{0,0,0,0,0,0,0,0x23A0,0x2DD,{201,202}},{0,0,0,0,0,0,0,0x1C5C,0x466,{203,204}},{0,0,0,0,0,0,0x404,0x1C69,0x24A,{205,206}},{0,0,0,0,0,0,0x808,0x3750,0x41E9,{207,208}},{0,0,0,0,0,0,0xC0C,0x49C,0x56A,{209,210}},{0,0,0,0,0,0,0x606,0x9C40,0x5D,{211,212}},{0,0,0,0,0,0,0xA0A,0x9C40,0x2C2,{213,214}},{0,0,0,0,0,0,0x606,0x9C40,0x64,{215,216}},{0,0,0,0,0,0,0x606,0x11F8,0x578,{217,218}},{0,0,0,0,0,0,0xA0A,0x265,0x2DD,{219,220}},{0,0,0,0,0,0,0x808,0x4BD,0x578,{221,222}},{0,0,0,0,0,0,0xA0A,0xC1,0x474,{223,224}},{0,0,0,0,0,0,0x606,0x11DD,0x1539,{225,226}},{0,0,0,0,0,0,0x404,0xE7A,0x113D,{227,228}},{0,0,0,0,0,0,0xE0E,0x1C41,0x217D,{229,230}},{0,0,0,0,0,0,0xE0E,0xBCC,0x64,{231,232}},{0,0,0,0,0,0,0xE0E,0x625,0xA49,{233,234}},{0,0,0,0,0,0,0xE0E,0x98E,0x876,{235,236}},{0,0,0,0,0,0,0xE0E,0x2ECC,0x2A36,{237,238}},{0,0,0,0,0,0,0,0x1D2,0x139,{239,240}},{0,0,0,0,0,0,0xA0A,0x3EE,0x452,{241,242}},{0,0,0,0,0,0,0xE0E,0x17C,0x49,{237,243}},{0,0,0,0,0,0,0xE0E,0x9C40,0x2B5,{244,245}},{0,0,0,0,0,0,0xE0E,0x230,0x92,{246,247}},{0,0,0,35,0,0,0x808,0x17C,0x1B8,{248,249}},{0,0,0,52,0,0,0x808,0x78,0x5D,{250,251}},{0,0,0,48,0,0,0xE0E,0x11E,0x146,{252,253}},{0,0,0,58,0,0,0,0xAD,0xC1,{254,255}},{0,0,0,60,0,0,0xE0E,0x118,0x140,{252,253}},{0,0,0,47,0,0,0x404,0x208,0x25E,{256,257}},{0,0,0,43,0,0,0xA0A,0xAD,0xC1,{258,259}},{0,0,0,49,0,0,0x404,0x201,0x258,{256,257}},{0,0,0,43,0,0,0xA0A,0xA0,0xA6,{258,260}},{0,0,0,51,0,0,0x404,0x201,0x258,{256,257}},{0,0,0,43,0,0,0xA0A,0x70E,0x82D,{261,262}},{0,0,0,54,0,0,0x404,0x201,0x25E,{256,257}},{0,0,0,57,0,0,0x404,0x208,0x258,{256,257}},{0,0,0,72,0,0,0xE0E,0x74A,0x8A5,{237,263}},{0,0,0,60,0,0,0x404,0x208,0x251,{256,257}},{0,0,0,76,0,0,0xE0E,0x5C8,0x6A4,{264,265}},{0,0,0,84,0,0,0xE0E,0x542,0x632,{266,267}},{0,0,0,36,0,0,0x606,0x460,0x4CA,{268,269}},{0,0,0,65,0,0,0xE0E,0x125,0x140,{270,271}},{0,0,0,84,0,0,0xE0E,0x542,0x639,{272,273}},{0,0,0,83,0,0,0x808,0xDC,0xF6,{274,275}},{0,0,0,84,0,0,0xE0E,0x550,0x646,{237,263}},{0,0,0,24,0,0,0xC0C,0x73D,0x876,{276,277}},{0,0,0,77,0,0,0xE0E,0x5BA,0x690,{264,265}},{0,0,0,60,0,0,0x606,0xAD,0xC1,{278,279}},{0,0,0,65,0,0,0x607,0xC1,0x132,{280,281}},{0,0,0,59,0,0,0x606,0xA0,0x140,{282,283}},{0,0,0,51,0,0,0x606,0xAD,0xC1,{284,285}},{0,0,0,45,0,0,0x606,0xFD,0x258,{286,287}},{0,0,0,71,0,0,0xE0E,0x1B1,0x1FA,{288,289}},{0,0,0,60,0,0,0xE0E,0x118,0x5D,{290,291}},{0,0,0,58,0,0,0xE0F,0x1ED,0x244,{292,293}},{0,0,0,53,0,0,0xE0F,0x1FA,0x251,{294,295}},{0,0,0,64,0,0,0xE0E,0xDC,0xAD,{296,297}},{0,0,0,71,0,0,0xE0E,0x6A,0x78,{298,299}},{0,0,0,61,0,0,0xE0E,0x3DA,0x46D,{300,301}},{0,0,0,61,0,0,0xE0E,0x76C,0x89E,{302,303}},{0,0,0,44,0,0,0xE0E,0xD5,0xAD,{304,305}},{0,0,0,40,0,0,0xE0E,0x24A,0x190,{306,307}},{0,0,0,69,0,0,0x1,0x78,0x140,{308,309}},{0,0,0,68,0,0,0,0x78,0x140,{310,311}},{0,0,0,63,0,0,0x1,0x8C,0x1B1,{308,311}},{0,0,0,74,0,0,0,0x118,0x10A,{312,313}},{0,0,0,60,0,0,0,0x3FC,0x445,{314,315}},{0,0,0,80,0,0,0x808,0xD5,0xE9,{316,317}},{0,0,0,64,0,0,0x808,0xA06,0xB9D,{318,319}},{0,0,0,72,0,0,0xE0E,0x7E,0x56,{320,321}},{0,0,0,73,0,0,0x606,0x182,0x78,{268,322}},{0,0,0,70,0,0,0xE0E,0x20E,0x3A5,{323,324}},{0,0,0,68,0,0,0x808,0x78,0x140,{325,309}},{0,0,0,48,0,0,0x607,0x16E,0x474,{326,327}},{0,0,0,53,0,0,0x404,0x201,0x258,{256,257}},{0,0,0,0,0,0,0x808,0x9C40,0x208,{328,329}},{0,0,0,0,0,0,0x606,0x9C40,0x64,{330,331}},{0,0,0,0,0x1,0,0xD0C,0x1CA5,0xDC,{332,333,334,335} },{0,0,0,0,0x1,0,0x100,0x1AFA,0xE2,{336,337,338,339} },{0,0,0,0,0x1,0,0x808,0x1309,0x139,{340,341,342,343} },{0,0,0,0,0,0,0x606,0x9C40,0x56,{344,345}},{0,0,0,0,0x1,0,0x100,0x100A,0x500,{346,347,348,349} },{0,0,0,0,0,0,0x202,0x938,0xACE,{350,351}},{0,0,0,0,0,0,0,0x9C40,0x5D,{352,353}},{0,0,0,0,0x1,0,0xB0A,0x251,0x2DD,{354,355,356,357} },{0,0,0,0,0x1,0,0x504,0x175C,0x1575,{358,359,360,361} },{0,0,0,0,0,0,0x404,0x4BD,0x56A,{362,363}},{0,0,0,0,0x1,0,0x707,0x9C40,0x5D,{364,365,366,367} },{0,0,0,0,0x1,0,0x707,0x9C40,0x6A,{368,369,370,371} },{0,0,0,0,0x1,0,0x908,0x9C40,0x6A,{372,373,374,375} },{0,0,0,0,0,0,0x101,0x9C40,0x5D,{376,377}},{0,0,0,0,0x1,0,0x100,0x9C40,0x6A,{378,379,380,381} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x92,{382,383,384,385} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x6A,{386,387,388,389} },{0,0,0,0,0x1,0,0xB0A,0xE81,0xE9,{390,391,392,393} },{0,0,0,0,0x1,0,0xC0C,0x395E,0x146,{394,395,396,49} },{0,0,0,0,0x1,0,0xA0A,0x2411,0x189,{397,398,399,400} },{0,0,0,0,0x1,0,0x808,0x3930,0x146,{401,402,403,404} },{0,0,0,0,0x1,0,0x908,0x1165,0xE2,{405,406,407,408} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x6A,{409,410,411,162} },{0,0,0,0,0,0,0x808,0x9C40,0x5D,{412,413}},{0,0,0,0,0x1,0,0x908,0x42AA,0xE2,{60,414,415,416} },{0,0,0,0,0x1,0,0x302,0xFCE,0xDC,{417,418,419,420} },{0,0,0,0,0x1,0,0x404,0x38B1,0x251,{421,422,423,424} },{0,0,0,0,0x1,0,0x606,0xAFD,0xE2,{425,426,426,427} },{0,0,0,0,0x1,0,0x606,0x29A,0x24A,{428,429,430,431} },{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{432,433}},{0,0,0,0,0x1,0,0xA0A,0x1F39,0xE2,{434,435,436,437} },{0,0,0,0,0,0,0xE0E,0x9C40,0x125,{438,439}},{0,0,0,0,0,0,0xE0E,0x9C40,0x182,{440,441}},{0,0,0,0,0x1,0,0x908,0x9C40,0x2A1,{442,76,443,444} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x196,{445,446,447,448} },{0,0,0,0,0x1,0,0x706,0x9C40,0x599,{449,450,451,452} },{0,0,0,0,0x1,0,0xE0E,0x3E1,0x44C,{453,454,455,456} },{0,0,0,0,0x1,0,0xE0E,0xCBC,0x1090,{453,454,455,457} },{0,0,0,0,0x1,0,0x302,0x1ACC,0x1130,{89,90,458,459} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x190,{460,461,462,463} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x189,{464,465,93,94} },{0,0,0,0,0,0,0xE0E,0x9C40,0xE2,{91,466}},{0,0,0,0,0,0,0x606,0x9C40,0x85,{467,468}},{0,0,0,0,0,0,0xE0E,0x9C40,0x85,{469,470}},{0,0,0,0,0x1,0,0xB0B,0x2BC,0x2A1,{471,472,473,474} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x1ED,{475,111,476,477} },{0,0,0,0,0,0,0xE0E,0x9C40,0x56,{478,479}},{0,0,0,0,0,0,0xA0A,0x9C40,0x189,{480,481}},{0,0,0,0,0x1,0,0xD0C,0x9C40,0x6A,{482,483,484,485} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0xD5,{486,487,488,489} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x64,{490,491,492,493} },{0,0,0,0,0x1,0,0x908,0x9C40,0xF6,{494,495,496,497} },{0,0,0,0,0x1,0,0x908,0x9C40,0x104,{124,498,499,500} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x236,{501,502,503,504} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x21C,{505,506,507,508} },{0,0,0,0,0x1,0,0x808,0x9C40,0x71,{509,510,511,512} },{0,0,0,0,0x1,0,0x202,0x9C40,0x92,{513,514,515,516} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x7E,{517,518,519,520} },{0,0,0,0,0x1,0,0x908,0x9C40,0x175,{136,137,136,521} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xE2,{522,523,524,525} },{0,0,0,0,0,0,0xE0E,0x9C40,0x132,{526,527}},{0,0,0,0,0x1,0,0xE0F,0x9C40,0x2A1,{528,529,530,146} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x272,{531,532,533,534} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x279,{535,536,537,538} },{0,0,0,0,0,0,0,0x9C40,0x8C,{539,540}},{0,0,0,0,0x1,0,0x404,0x9C40,0x5D,{541,542,543,544} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x8C,{545,546,547,548} },{0,0,0,0,0,0,0,0x9C40,0xDC,{549,158}},{0,0,0,0,0x1,0,0xA0A,0x9C40,0x6A,{550,551,552,162} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x196,{553,554,555,556} },{0,0,0,0,0x1,0,0x908,0x9C40,0x2A1,{557,558,559,560} },{0,0,0,0,0x1,0,0x302,0x9C40,0x53C,{561,562,563,564} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x11E4,{171,172,565,566} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x495,{567,174,568,569} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x1179,{570,571,572,176} },{0,0,0,0,0,0,0xA0A,0x9C40,0x49C,{181,573}},{0,0,0,0,0,0,0,0x9C40,0x189,{574,184}},{0,0,0,0,0,0,0xE0E,0x3354,0x21B9,{575,576}},{0,0,0,0,0x1,0,0x706,0x9C40,0x141A,{577,578,579,580} },{0,0,0,0,0,0,0,0x23F0,0x2DD,{581,582}},{0,0,0,0,0,0,0,0x4D8,0x56A,{583,584}},{0,0,0,0,0,0,0x606,0x9C40,0x1492,{585,586}},{0,0,0,0,0,0,0x404,0x9C40,0x1309,{587,588}},{0,0,0,0,0x1,0,0x100,0x3C28,0x2C9,{589,590,591,592} },{0,0,0,0,0x1,0,0,0x1C70,0x466,{593,594,595,596} },{0,0,0,0,0x1,0,0xE0E,0xDE1,0x43E,{597,598,599,600} },{0,0,0,0,0x1,0,0x908,0x362A,0x40DE,{207,601,602,603} },{0,0,0,0,0x1,0,0xD0C,0x4D8,0x5AD,{209,210,604,605} },{0,0,0,0,0x1,0,0x706,0x9C40,0x5D,{211,212,606,607} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x29A,{213,214,608,609} },{0,0,0,0,0x1,0,0x606,0x9C40,0x64,{610,611,612,613} },{0,0,0,0,0x1,0,0xB0A,0x26C,0x2EA,{614,615,219,220} },{0,0,0,0,0x1,0,0x100,0x4C4,0x57E,{616,617,221,222} },{0,0,0,0,0,0,0xE0E,0xAD,0xE9,{618,619}},{0,0,0,0,0,0,0xE0E,0x3C0,0x466,{620,621}},{0,0,0,0,0x1,0,0xF0E,0x78D,0x49C,{622,623,624,623} },{0,0,0,0,0,0,0xE0E,0xC36,0x64,{625,626}},{0,0,0,0,0,0,0,0xE9,0x64,{627,628}},{0,0,0,0,0,0,0xE0E,0x19B4,0xAD5,{629,630}},{0,0,0,0,0,0,0xE0E,0x111,0x50,{631,632}},{0,0,0,0,0,0,0x808,0x2EF4,0x898,{633,634}},{0,0,0,0,0,0,0x606,0x9C40,0x2D6,{635,636}},{0,0,0,0,0x1,0,0xF0E,0x9C40,0x2A1,{244,637,638,638} },{0,0,0,0,0x1,0,0xE0E,0x230,0x92,{639,640,641,642} },{0,0,0,35,0,0,0xE0E,0x9C40,0x71,{643,644}},{0,0,0,0,0,0,0xE0E,0xAD,0xC1,{645,646}},{0,0,0,35,0,0,0x202,0x14D,0x182,{647,648}},{0,0,0,35,0,0,0,0xD5,0x5D,{649,650}},{0,0,0,50,0,0,0xE0E,0x139,0x64,{651,652}},{0,0,0,18,0,0,0x606,0x1A4,0x189,{653,654}},{0,0,0,72,0,0,0xE0E,0xAD,0xAD,{655,656}},{0,0,0,74,0,0,0,0x99,0xA6,{657,658}},{0,0,0,35,0,0,0x808,0x17C,0x1B1,{659,249}},{0,0,0,16,0,0,0,0x49C,0x578,{660,661}},{0,0,0,0,0x4,0,0,0x6,0,{662,663}},{0,0,0,38,0,0,0xC0C,0xC8,0xE2,{664,665}},{0,0,0,38,0,0,0xE0E,0x15A,0x189,{666,667}},{0,0,0,31,0,0,0xE0E,0x196,0x5D,{668,669}},{0,0,0,35,0,0,0xE0E,0x19D,0x5D,{668,669}},{0,0,0,38,0,0,0xE0E,0x196,0x5D,{668,669}},{0,0,0,41,0,0,0xE0E,0x19D,0x5D,{668,669}},{0,0,0,45,0,0,0xE0E,0x139,0x5D,{668,669}},{0,0,0,50,0,0,0xE0E,0x139,0x5D,{668,669}},{0,0,0,36,0,0,0xE0E,0x55D,0x646,{670,671}},{0,0,0,36,0,0,0xE0E,0x8C,0x64,{672,673}},{0,0,0,48,0,0,0,0xD5,0xB4,{674,675}},{0,0,0,36,0,0,0,0xF6,0xC8,{674,675}},{0,0,0,36,0,0,0,0x6A,0x71,{676,677}},{0,0,0,0,0,0,0,0x85,0x71,{678,679}},{0,0,0,61,0,0,0,0xB4,0x5D,{680,681}},{0,0,0,96,0,0,0x101,0x2BC,0x33A,{682,683}},{0,0,0,38,0,0,0,0x222,0x294,{684,685}},{0,0,0,16,0,0,0x808,0x279,0x2D0,{248,249}},{0,0,0,18,0x1,0,0x606,0xC8,0x5D,{686,687,688,251} },{0,0,0,30,0,0,0xE0E,0x190,0x2DD,{689,690}},{0,0,0,35,0x1,0,0xF0E,0x251,0x2D0,{691,692,254,255} },{0,0,0,0,0,0,0xE0E,0x154,0x190,{252,253}},{0,0,0,0,0,0,0,0xD5,0x5D,{693,694}},{0,0,0,88,0,0,0xF0F,0x189,0x1CC,{695,696}},{0,0,0,88,0,0,0xF0F,0x99,0x5D,{697,698}},{0,0,0,79,0,0,0xE0E,0xA42,0xDD4,{699,700}},{0,0,0,14,0,0,0xE0E,0x2399,0x2A4A,{237,263}},{0,0,0,46,0,0,0xE0E,0x3AC,0x5D,{701,702}},{0,0,0,129,0x1,0,0xE0E,0x4A9,0x585,{703,704,705,706} },{0,0,0,58,0,0,0xE0E,0x5D5,0x876,{707,708}},{0,0,0,164,0,0,0xE0E,0x1E6,0xA99,{709,710}},{0,0,0,142,0,0,0xE0E,0x2392,0x2A5E,{711,712}},{0,0,0,9,0,0,0xE0E,0xC8,0xE2,{713,714}},{0,0,0,35,0x1,0,0xE0E,0x924,0xADC,{715,716,717,718} },{0,0,0,28,0,0,0xC0C,0x44C,0x168,{719,720}},{0,0,0,46,0,0,0xE0E,0x3B9,0x5D,{721,722}},{0,0,0,60,0,0,0x404,0x1E6,0x23D,{723,724}},{0,0,0,54,0,0,0x404,0x1ED,0x23D,{723,724}},{0,0,0,72,0,0,0,0xF6,0x118,{725,726}},{0,0,0,67,0,0,0,0xF0,0x111,{725,726}},{0,0,0,60,0,0,0,0x118,0x146,{725,726}},{0,0,0,1,0,0,0x808,0x3C6,0xA14,{727,728}},{0,0,0,77,0,0,0x808,0x154,0xAD,{729,730}},{0,0,0,72,0,0,0x808,0x154,0xAD,{729,730}},{0,0,0,90,0,0,0xE0E,0xCE,0xE2,{731,732}},{0,0,0,39,0,0,0,0x10A,0x64,{733,734}},{0,0,0,36,0,0,0,0x258,0x64,{735,736}},{0,0,0,35,0x1,0,0xD0C,0x17C,0x1BE,{737,738,739,739} },{0,0,0,35,0x1,0,0xD0C,0x1C5,0x1BE,{737,738,740,740} },{0,0,0,60,0,0,0xC0C,0xAD,0x5D,{741,742}},{0,0,0,7,0,0,0xE0E,0xA6,0xE2,{618,619}},{0,0,0,90,0,0,0x808,0xF6,0x5D,{743,744}},{0,0,0,90,0,0,0x808,0x452,0x78,{745,746}},{0,0,0,35,0,0,0xE0E,0xFD,0xC8,{747,297}},{0,0,0,5,0x1,0,0xF0E,0x744,0x2C2,{323,748,749,750} },{0,0,0,103,0,0,0x606,0x2A8,0x319,{751,752}},{0,0,0,3,0,0,0,0x64,0x5D,{753,754}},{0,0,0,1,0,0,0x707,0x20E,0x55D,{326,327}},{0,0,0,0,0,0,0x404,0x28D,0x2F8,{256,257}},{0,0,0,36,0,0,0xE0E,0x111,0x49,{755,756}},{0,0,0,60,0,0,0xE0E,0x9C40,0x5D,{757,758}},{0,0,0,37,0,0,0xC0C,0x4B0,0x578,{759,760}},{0,0,0,36,0,0,0xE0E,0x196,0x5D,{761,652}},{0,0,0,32,0,0,0x404,0x92,0xA0,{762,762}},{0,0,0,50,0,0,0x303,0x9C40,0x2C7A,{763,764}},{0,0,0,50,0,0,0xE0E,0x30C,0x42A,{765,766}},{0,0,0,83,0,0,0xA0A,0x78,0x5D,{767,768}},{0,0,0,72,0,0,0,0x190,0x1E6,{769,770}},{0,0,0,59,0,0,0xE0E,0x201,0x258,{288,289}},{0,0,0,64,0,0,0,0xB4,0xC1,{771,658}},{0,0,0,60,0,0,0,0xAD,0xC1,{771,658}},{0,0,0,72,0,0,0,0x99,0xA6,{772,773}},{0,0,0,62,0,0,0,0xA6,0xBA,{772,773}},{0,0,0,83,0,0,0x808,0x305,0x7E,{774,775}},{0,0,0,0,0,0,0x8,0x9C40,0x8C,{776,777}},{0,0,0,0,0,0,0x8,0x9C40,0x208,{328,329}},{0,0,0,0,0,0,0x8,0x9C40,0x1B8,{778,779}},{0,0,0,0,0,0,0x6,0x9C40,0x64,{330,331}},{0,0,0,0,0,0,0,0x9C40,0xC8,{780,781}},{0,0,0,0,0,0,0,0x9C40,0xCE,{782,781}},{0,0,0,0,0,0,0x8,0x9C40,0xF0,{783,784}},{0,0,0,0,0,0,0xA,0xD70,0xE2,{785,786}},{0,0,0,0,0,0,0,0xF42,0x57E,{787,788}},{0,0,0,0,0,0,0x2,0x3584,0x4441,{789,790}},{0,0,0,0,0,0,0x2,0x938,0xACE,{350,351}},{0,0,0,0,0,0,0,0x23F0,0x2DD,{791,21}},{0,0,0,0,0,0,0x4,0x26C,0x2D6,{356,357}},{0,0,0,0,0,0,0xC,0x1220,0x1582,{360,361}},{0,0,0,0,0,0,0x4,0x4BD,0x56A,{362,363}},{0,0,0,0,0,0,0x2,0x9C40,0x132,{30,31}},{0,0,0,0,0,0,0x2,0x9C40,0xC1,{32,33}},{0,0,0,0,0,0,0x4,0x9C40,0xC1,{34,35}},{0,0,0,0,0,0,0x4,0x9C40,0x84E,{36,37}},{0,0,0,0,0,0,0xC,0x9C40,0x6A,{40,41}},{0,0,0,0,0,0,0xA,0x9C40,0x19D,{42,43}},{0,0,0,0,0,0,0x6,0x9C40,0x189,{44,45}},{0,0,0,0,0,0,0xC,0xFC1,0xE9,{46,47}},{0,0,0,0,0,0,0xC,0x3929,0x146,{48,49}},{0,0,0,0,0,0,0x6,0x11EA,0x1561,{52,792}},{0,0,0,0,0,0,0x8,0x9C40,0x8C,{58,59}},{0,0,0,0,0,0,0x8,0x47CC,0xE2,{60,61}},{0,0,0,0,0,0,0x2,0x9C40,0x140,{62,63}},{0,0,0,0,0,0,0xA,0x9C40,0xBA,{64,65}},{0,0,0,0,0,0,0xA,0x9C40,0x85,{66,67}},{0,0,0,0,0,0,0xC,0x9C40,0xA0,{68,69}},{0,0,0,0,0,0,0x8,0x9C40,0x42,{70,71}},{0,0,0,0,0,0,0x8,0x21AC,0x5D,{793,71}},{0,0,0,0,0,0,0xA,0x9C40,0x85,{74,67}},{0,0,0,0,0,0,0x8,0x9C40,0x294,{75,76}},{0,0,0,0,0,0,0x8,0x9C40,0x2E4,{77,78}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{79,80}},{0,0,0,0,0,0,0x2,0x9C40,0x2D0,{81,82}},{0,0,0,0,0,0,0x2,0x9C40,0x2C2,{83,84}},{0,0,0,0,0,0,0x2,0x1AE0,0x113D,{89,90}},{0,0,0,0,0,0,0xE,0x9C40,0xE2,{91,92}},{0,0,0,0,0,0,0xA,0x9C40,0x506,{95,96}},{0,0,0,0,0,0,0xC,0x9C40,0x5A0,{97,96}},{0,0,0,0,0,0,0xC,0x9C40,0x47A,{102,103}},{0,0,0,0,0,0,0x8,0x459,0x55D,{104,105}},{0,0,0,0,0,0,0xC,0x9C40,0x8C,{106,107}},{0,0,0,0,0,0,0xC,0x9C40,0x8C,{108,109}},{0,0,0,0,0,0,0xC,0x9C40,0x139,{110,111}},{0,0,0,0,0,0,0x2,0x9C40,0x56,{112,113}},{0,0,0,0,0,0,0x2,0x9C40,0x71,{114,115}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{116,117}},{0,0,0,0,0,0,0,0x9C40,0xCE,{118,794}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{120,121}},{0,0,0,0,0,0,0x8,0x9C40,0x64,{122,123}},{0,0,0,0,0,0,0x8,0x9C40,0x92,{124,125}},{0,0,0,0,0,0,0xA,0x9C40,0x92,{126,127}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{128,129}},{0,0,0,0,0,0,0x8,0x9C40,0x71,{130,131}},{0,0,0,0,0,0,0x6,0x9C40,0x50,{132,133}},{0,0,0,0,0,0,0x4,0x9C40,0x16E,{136,137}},{0,0,0,0,0,0,0x2,0x9C40,0x71,{141,142}},{0,0,0,0,0,0,0xB,0x9C40,0x189,{149,150}},{0,0,0,0,0,0,0xB,0x9C40,0x189,{151,152}},{0,0,0,0,0,0,0x2,0x9C40,0x190,{163,164}},{0,0,0,0,0,0,0xA,0x9C40,0x92,{167,168}},{0,0,0,0,0,0,0x2,0x9C40,0x585,{169,170}},{0,0,0,0,0,0,0xA,0x9C40,0x1205,{171,172}},{0,0,0,0,0,0,0xC,0x9C40,0x2BC,{173,174}},{0,0,0,0,0,0,0x8,0x9C40,0x495,{177,178}},{0,0,0,0,0,0,0xA,0x9C40,0x49C,{181,182}},{0,0,0,0,0,0,0x2,0x1CE8,0x224C,{185,186}},{0,0,0,0,0,0,0xC,0x9C40,0x28D,{191,192}},{0,0,0,0,0,0,0x6,0x9C40,0x14A6,{195,196}},{0,0,0,0,0,0,0x6,0x9C40,0x1492,{197,198}},{0,0,0,0,0,0,0x4,0x9C40,0x1309,{199,200}},{0,0,0,0,0,0,0x4,0x1C69,0x24A,{205,206}},{0,0,0,0,0,0,0x8,0x3750,0x41E9,{207,208}},{0,0,0,0,0,0,0xC,0x49C,0x56A,{209,210}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{211,212}},{0,0,0,0,0,0,0xA,0x9C40,0x2C2,{213,214}},{0,0,0,0,0,0,0x6,0x9C40,0x64,{215,216}},{0,0,0,0,0,0,0x6,0x11F8,0x578,{217,218}},{0,0,0,0,0,0,0xA,0x265,0x2DD,{219,220}},{0,0,0,0,0,0,0x8,0x4BD,0x578,{221,222}},{0,0,0,0,0,0,0xA,0xC1,0x474,{223,224}},{0,0,0,0,0,0,0x6,0x11DD,0x1539,{225,226}},{0,0,0,0,0,0,0x4,0xE7A,0x113D,{227,228}},{0,0,0,0,0,0,0xE,0x1C41,0x217D,{229,230}},{0,0,0,0,0,0,0xE,0xBCC,0x64,{231,232}},{0,0,0,0,0,0,0xE,0x625,0xA49,{233,234}},{0,0,0,0,0,0,0xE,0x98E,0x876,{235,236}},{0,0,0,0,0,0,0xE,0x2ECC,0x2A36,{237,238}},{0,0,0,0,0,0,0xA,0x3EE,0x452,{241,242}},{0,0,0,0,0,0,0xE,0x17C,0x49,{237,243}},{0,0,0,0,0,0,0xE,0x9C40,0x2B5,{244,245}},{0,0,0,0,0,0,0xE,0x230,0x92,{246,247}},{0,0,0,60,0x4,0,0,0x6,0,{795,795}},{0,0,0,73,0,0,0x8,0x251,0xAD,{796,797}},{0,0,0,74,0,0,0x8,0x251,0xAD,{796,797}},{0,0,0,80,0,0,0x8,0x251,0xAD,{796,797}},{0,0,0,84,0,0,0x8,0x251,0xAD,{796,797}},{0,0,0,92,0,0,0x8,0x208,0xA0,{796,797}},{0,0,0,81,0,0,0x6,0x312,0x78,{798,799}},{0,0,0,83,0,0,0x6,0x312,0x78,{798,799}},{0,0,0,95,0,0,0x6,0x2A8,0x78,{798,799}},{0,0,0,83,0,0,0x8,0x305,0x7E,{774,775}},{0,0,0,35,0,0,0x8,0x251,0x182,{800,801}},{0,0,0,60,0,0,0xE,0xC8,0x140,{802,803}},{0,0,0,59,0,0,0xE,0x6A,0x5D,{672,673}},{0,0,0,44,0,0,0xE,0xC8,0x140,{802,803}},{0,0,0,41,0,0,0xF,0x2C9,0x33A,{804,805}},{0,0,0,97,0,0,0,0x71,0x78,{806,807}},{0,0,0,44,0,0,0xF,0x201,0x258,{804,805}},{0,0,0,48,0,0,0xF,0x201,0x258,{804,805}},{0,0,0,96,0,0,0x8,0x2BC,0x99,{808,809}},{0,0,0,51,0,0,0xF,0x201,0x258,{804,805}},{0,0,0,54,0,0,0xF,0x201,0x258,{804,805}},{0,0,0,40,0,0,0xE,0x5C8,0xA7E,{810,811}},{0,0,0,57,0,0,0xF,0x17C,0x1B8,{804,805}},{0,0,0,58,0,0,0xE,0x5B4,0x862,{707,812}},{0,0,0,97,0,0,0,0xE9,0xF6,{813,814}},{0,0,0,50,0,0,0,0xBA,0xC1,{815,816}},{0,0,0,28,0,0,0xC,0x44C,0x168,{719,720}},{0,0,0,60,0,0,0xE,0x5A0,0x826,{707,812}},{0,0,0,53,0,0,0,0xB4,0x85,{817,818}},{0,0,0,46,0,0,0,0xAD,0x146,{819,820}},{0,0,0,57,0,0,0,0xB4,0x85,{817,818}},{0,0,0,42,0,0,0xE,0x280,0x2D6,{821,822}},{0,0,0,37,0,0,0xE,0x279,0x2D6,{821,822}},{0,0,0,41,0,0,0xA,0x272,0x2DD,{823,824}},{0,0,0,37,0,0,0xA,0x265,0x2D0,{823,824}},{0,0,0,77,0,0,0xE,0xAD,0x6A,{825,826}},{0,0,0,72,0,0,0xE,0xAD,0x6A,{825,826}},{0,0,0,70,0,0,0xE,0xE9,0x104,{827,828}},{0,0,0,90,0,0,0xE,0xE9,0x104,{827,828}},{0,0,0,46,0,0,0xC,0x85,0x64,{829,830}},{0,0,0,48,0,0,0,0x14D,0x64,{831,832}},{0,0,0,85,0,0,0,0x6A,0x5D,{833,834}},{0,0,0,66,0,0,0,0xB4,0x5D,{835,681}},{0,0,0,61,0,0,0,0xB4,0x5D,{835,681}},{0,0,0,41,0,0,0,0xC8,0x64,{836,837}},{0,0,0,41,0,0,0,0xFD,0x64,{838,839}},{0,0,0,81,0,0,0x6,0xF0,0x5D,{751,840}},{0,0,0,81,0,0,0x6,0x320,0x3AC,{751,752}},{0,0,0,76,0,0,0x6,0x320,0x3A5,{751,752}},{0,0,0,103,0,0,0x6,0x2A8,0x319,{751,752}},{0,0,0,60,0,0,0,0x64,0x64,{676,677}},{0,0,0,53,0,0,0xC,0x9C40,0x50,{841,842}},{0,0,0,0,0x4,0,0,0x6,0,{843,843}},{0,0,0,0,0,0,0xC,0xC8,0x5D,{844,845}},{0,0,0,0,0,0,0x6,0x11EA,0xE2,{52,846}},{0,0,0,0,0,0,0x6,0x4A2,0x578,{225,847}},{0,0,0,0,0,0,0x6,0x9C40,0x11E,{848,849}},{0,0,0,0,0,0,0x8,0x9C40,0x6A,{850,851}},{0,0,0,0,0,0,0,0xE74,0x92,{852,853}},{0,0,0,0,0,0,0x8,0x9C40,0x85,{854,855}},{0,0,0,0,0,0,0xA,0x1A4D,0x2300,{856,857}},{0,0,0,0,0,0,0xA,0x9C40,0x701,{858,859}},{0,0,0,0,0,0,0xC,0x9C40,0x139,{860,111}},{0,0,0,0,0,0,0x8,0x9C40,0xE2,{861,862}},{0,0,0,0,0,0,0xC,0x9C40,0xE2,{863,862}},{0,0,0,0,0,0,0x6,0x16BC,0xAC1,{864,865}},{0,0,0,0,0,0,0xC,0x9C40,0x521,{866,867}},{0,0,0,0,0,0,0xA,0x9C40,0x9A9,{868,869}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{870,871}},{0,0,0,0,0,0,0xA,0x9C40,0x189,{872,873}},{0,0,0,0,0,0,0x4,0xCEA,0xAC8,{874,875}},{0,0,0,0,0,0,0xE,0x9C40,0x876,{876,877}},{0,0,0,0,0,0,0x6,0x9C40,0x28D,{878,879}},{0,0,0,0,0,0,0x4,0x91D,0xADC,{880,881}},{0,0,0,0,0,0,0x2,0x9C40,0x4372,{882,883}},{0,0,0,0,0,0,0xE,0x495,0x571,{884,885}},{0,0,0,0,0,0,0,0xEA9,0x110E,{886,887}},{0,0,0,0,0,0,0xC,0x3CD,0xAC8,{888,889}},{0,0,0,0,0,0,0x4,0x1C41,0x215C,{890,891}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{757,892}},{0,0,0,0,0,0,0x8,0x9D8,0x64,{893,894}},{0,0,0,0,0,0,0x6,0x189,0x16E,{895,896}},{0,0,0,0,0,0,0x2,0x9C40,0x251,{882,897}},{0,0,0,0,0,0,0x8,0x9C40,0xA00,{898,899}},{0,0,0,0,0,0,0xE,0x4A9,0x57E,{900,901}},{0,0,0,0,0,0,0,0x37D5,0x459,{902,903}},{0,0,0,0,0,0,0x6,0x2072,0x88A,{904,905}},{0,0,0,0,0,0,0x8,0x201,0x258,{906,907}},{0,0,0,0,0,0,0x8,0x9C40,0xDC,{908,909}},{0,0,0,36,0,0,0xE,0x55D,0x646,{670,671}},{0,0,0,50,0,0,0x7,0x578,0x47A,{910,911}},{0,0,0,37,0,0,0x4,0x402,0x578,{912,913}},{0,0,0,39,0,0,0x8,0x196,0x5D,{914,652}},{0,0,0,39,0,0,0xA,0xEB0,0xC3D,{915,916}},{0,0,0,86,0,0,0x8,0x855,0x1FA,{917,918}},{0,0,0,43,0,0,0xA,0x8C,0x85,{919,920}},{0,0,0,24,0,0,0x8,0x208,0x251,{248,249}},{0,0,0,29,0,0,0x8,0x208,0x251,{248,249}},{0,0,0,50,0,0,0,0x154,0x5D,{921,922}},{0,0,0,30,0,0,0x8,0x14B4,0x1151,{917,923}},{0,0,0,33,0,0,0x8,0xED8,0xC6C,{917,923}},{0,0,0,38,0,0,0x8,0xEA9,0xC36,{917,923}},{0,0,0,42,0,0,0x8,0xED8,0xC6C,{917,923}},{0,0,0,24,0,0,0x1,0x104,0x190,{281,281}},{0,0,0,27,0,0,0x1,0x104,0x190,{281,281}},{0,0,0,29,0,0,0x1,0x104,0x190,{281,281}},{0,0,0,32,0,0,0x1,0x104,0x190,{281,281}},{0,0,0,32,0,0,0x8,0x6A,0x5D,{280,924}},{0,0,0,53,0,0,0xE,0x17C,0x258,{925,926}},{0,0,0,57,0,0,0xE,0x17C,0x258,{925,926}},{0,0,0,60,0,0,0xE,0x11E,0x146,{927,928}},{0,0,0,55,0,0,0xC,0x1D2,0x146,{929,930}},{0,0,0,85,0,0,0xE,0x32D,0x3C0,{900,901}},{0,0,0,90,0,0,0xE,0x62C,0x744,{929,931}},{0,0,0,84,0,0,0xE,0xF6,0x111,{932,933}},{0,0,0,48,0,0,0xE,0x326,0x8C0,{929,934}},{0,0,0,48,0,0,0xE,0xF6,0x251,{929,935}},{0,0,0,72,0,0,0xA,0x7E,0x85,{258,259}},{0,0,0,72,0,0,0x4,0x64,0x6A,{890,936}},{0,0,0,72,0,0,0x8,0x64,0x5D,{280,937}},{0,0,0,63,0,0,0xE,0x730,0x891,{938,939}},{0,0,0,65,0,0,0xE,0x736,0x891,{938,939}},{0,0,0,79,0,0,0xE,0x61E,0x74A,{940,941}},{0,0,0,38,0,0,0xE,0x201,0xAC1,{942,710}},{0,0,0,94,0,0,0x8,0x17C,0x1B8,{943,944}},{0,0,0,87,0,0,0x8,0x1B1,0x3C0,{943,945}},{0,0,0,94,0,0,0x8,0x17C,0x33A,{943,945}},{0,0,0,80,0,0,0x6,0x20E,0x326,{946,947}},{0,0,0,47,0,0,0x8,0x1FA,0x251,{948,949}},{0,0,0,61,0,0,0x8,0x118,0x140,{950,951}},{0,0,0,68,0,0,0x8,0xF6,0x118,{950,951}},{0,0,0,61,0,0,0x8,0x1FA,0x251,{950,952}},{0,0,0,68,0,0,0x8,0x1B1,0x1F4,{950,952}},{0,0,0,60,0,0,0x1,0xD5,0x146,{281,281}},{0,0,0,60,0,0,0xC,0x99,0x78,{953,954}},{0,0,0,36,0,0,0x8,0xC8,0x5D,{955,742}},{0,0,0,60,0,0,0x8,0xAD,0x5D,{955,742}},{0,0,0,60,0,0,0xC,0xAD,0x5D,{741,742}},{0,0,0,60,0,0,0xE,0xAD,0x5D,{956,742}},{0,0,0,68,0,0,0x1,0x78,0x5D,{957,958}},{0,0,0,71,0,0,0,0x99,0x1F4,{959,959}},{0,0,0,72,0,0,0,0x99,0x1F4,{959,959}},{0,0,0,101,0,0,0x8,0x3C0,0x466,{960,961}},{0,0,0,36,0,0,0x8,0xCF8,0x62C,{633,962}},{0,0,0,25,0,0,0x8,0x9C40,0x27DE,{963,964}},{0,0,0,37,0,0,0xE,0x83A,0x585,{965,966}},{0,0,0,36,0,0,0xC,0x2D0,0x33A,{967,968}},{0,0,0,41,0,0,0xC,0x2C9,0x341,{967,968}},{0,0,0,84,0,0,0xE,0xAD,0x56,{969,970}},{0,0,0,54,0,0,0xE,0x9C40,0xE9,{971,972}},{0,0,0,36,0,0,0xE,0x111,0x49,{755,756}},{0,0,0,60,0,0,0xE,0x9C40,0x5D,{757,758}},{0,0,0,37,0,0,0xC,0x4B0,0x578,{759,760}},{0,0,0,36,0,0,0xE,0x196,0x5D,{761,652}},{0,0,0,32,0,0,0x4,0x92,0xA0,{762,762}},{0,0,0,48,0,0,0xE,0x9C40,0x5D,{757,892}},{0,0,0,0,0,0,0x2,0x2738,0x1136,{973,974}},{0,0,0,0,0,0,0xE,0x11F1,0x876,{975,976}},{0,0,0,0,0,0,0xC,0x9C40,0xD4E,{977,978}},{0,0,0,0,0,0,0x8,0x9C40,0x64,{979,980}},{0,0,0,0,0,0,0,0x9C40,0x431,{981,982}},{0,0,0,0,0,0,0,0x9C40,0x83A,{981,983}},{0,0,0,0,0,0,0,0x2522,0x152C,{984,985}},{0,0,0,0,0,0,0,0x2314,0x5D,{986,987}},{0,0,0,0,0,0,0,0x9C40,0x409,{981,988}},{0,0,0,0,0,0,0xA,0x9C40,0xBA,{989,990}},{0,0,0,0,0,0,0x1,0x9C40,0x168,{991,992}},{0,0,0,0,0,0,0xA,0xFD,0x64,{993,994}},{0,0,0,0,0,0,0x8,0x9C40,0x8C,{995,996}},{0,0,0,0,0,0,0x8,0x4725,0xACE,{997,998}},{0,0,0,0,0,0,0,0x9C40,0x10BE,{999,1000}},{0,0,0,0,0,0,0x8,0x9C40,0x1449,{1001,1002}},{0,0,0,0,0,0,0,0x9C40,0x294,{1003,146}},{0,0,0,49,0,0,0xE,0xE60,0x1129,{237,263}},{0,0,0,47,0,0,0x4,0x208,0x25E,{256,257}},{0,0,0,49,0,0,0x4,0x201,0x258,{256,257}},{0,0,0,51,0,0,0x4,0x201,0x258,{256,257}},{0,0,0,54,0,0,0x4,0x201,0x25E,{256,257}},{0,0,0,57,0,0,0x4,0x208,0x258,{256,257}},{0,0,0,60,0,0,0x4,0x208,0x251,{256,257}},{0,0,0,72,0,0,0xE,0x7E,0x56,{320,321}},{0,0,0,71,0,0,0xE,0x1B1,0x1FA,{288,289}},{0,0,0,72,0,0,0xE,0xAD,0xAD,{655,656}},{0,0,0,35,0,0,0x8,0x17C,0x1B8,{248,249}},{0,0,0,35,0,0,0x4,0x898,0x71,{1004,1005}},{0,0,0,41,0,0,0,0x50,0x5D,{1006,1007}},{0,0,0,38,0,0,0xE,0x189,0x2D6,{689,690}},{0,0,0,39,0,0,0xE,0x6A,0x5D,{1008,1009}},{0,0,0,49,0,0,0xE,0xC1,0x140,{1010,1011}},{0,0,0,83,0,0,0xA,0x78,0x5D,{767,768}},{0,0,0,59,0,0,0xE,0x305,0x452,{765,766}},{0,0,0,84,0,0,0xE,0x542,0x632,{266,267}},{0,0,0,24,0,0,0x1,0x9C40,0,{281,1012}},{0,0,0,27,0,0,0x1,0x9C40,0,{281,1012}},{0,0,0,29,0,0,0x1,0x9C40,0,{281,1012}},{0,0,0,32,0,0,0x1,0x9C40,0,{281,1012}},{0,0,0,84,0,0,0xE,0xC8,0x64,{1013,1014}},{0,0,0,79,0,0,0xE,0x154,0x722,{942,710}},{0,0,0,61,0,0,0xE,0x190,0x140,{1015,1016}},{0,0,0,68,0,0,0xE,0x15A,0x111,{1015,1016}},{0,0,0,36,0,0,0xA,0x92,0xA0,{1017,1018}},{0,0,0,60,0,0,0xA,0x6A,0x71,{1017,1018}},{0,0,0,36,0,0,0xE,0x111,0x49,{1019,756}},{0,0,0,37,0,0,0x6,0x11DD,0x154D,{225,226}},{0,0,0,0,0,0,0xA,0xEDE,0xC1,{1020,1021}},{0,0,0,0,0,0,0xE,0x9C40,0xA0,{1022,1023}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{1024,1025}},{0,0,0,0,0,0,0xA,0x16F8,0x146,{1026,1027}},{0,0,0,0,0,0,0xA,0xFD,0x5D,{1028,994}},{0,0,0,0,0,0,0x8,0xC9A,0xF00,{1029,1030}},{0,0,0,0,0,0,0x8,0x9C40,0xDC,{1031,1032}},{0,0,0,0,0,0,0xA,0x654,0x431,{1033,1034}},{0,0,0,0,0,0,0xE,0x9C40,0x56,{1035,1036}},{0,0,0,0,0,0,0xE,0x9C40,0x50,{1037,1038}},{0,0,0,0,0,0,0xE,0x9C40,0x50,{1039,1038}},{0,0,0,0,0,0,0x1,0x9C40,0x92,{1040,1041}},{0,0,0,0,0,0,0x8,0x2F1,0x474,{1042,1043}},{0,0,0,35,0,0,0x8,0x17C,0x1B1,{659,249}},{0,0,0,38,0,0,0xC,0xC8,0xE2,{664,665}},{0,0,0,38,0,0,0xE,0x15A,0x189,{666,667}},{0,0,0,31,0,0,0xE,0x196,0x5D,{668,669}},{0,0,0,35,0,0,0xE,0x19D,0x5D,{668,669}},{0,0,0,38,0,0,0xE,0x196,0x5D,{668,669}},{0,0,0,41,0,0,0xE,0x19D,0x5D,{668,669}},{0,0,0,45,0,0,0xE,0x139,0x5D,{668,669}},{0,0,0,50,0,0,0xE,0x139,0x5D,{668,669}},{0,0,0,50,0,0,0x3,0x9C40,0x2C7A,{763,764}},{0,0,0,50,0,0,0xE,0x30C,0x42A,{765,766}},{0,0,0,44,0,0,0xE,0xD5,0xAD,{304,305}},{0,0,0,59,0,0,0xE,0x201,0x258,{288,289}},{0,0,0,53,0,0,0x4,0x201,0x258,{256,257}},{0,0,0,0,0,0,0x8,0x9C40,0x2A8,{1044,1045}},{0,0,0,0,0,0,0x8,0x9C40,0x9B0,{1046,1047}},{0,0,0,0,0,0,0x8,0x9C40,0x9CA,{1048,1047}},{0,0,0,0,0,0,0xE,0x779,0x8AC,{670,671}},{0,0,0,0,0,0,0xC,0x9C40,0xC1,{1049,1050}},{0,0,0,0,0,0,0xC,0x9C40,0xC1,{1049,1051}},{0,0,0,0,0,0,0xE,0x9C40,0x251,{1052,1053}},{0,0,0,0,0,0,0xE,0x9C40,0x251,{1054,1053}},{0,0,0,0,0,0,0xE,0x9C40,0x146,{1054,1055}},{0,0,0,0,0,0,0xE,0x9C40,0x474,{1056,1057}},{0,0,0,0,0,0,0xE,0x9C40,0x46D,{1056,1058}},{0,0,0,0,0,0,0xE,0x9C40,0x474,{1059,1057}},{0,0,0,0,0,0,0x6,0x9C40,0xD5,{1060,909}},{0,0,0,0,0,0,0x8,0x9C40,0x17C,{1061,1062}},{0,0,0,0,0,0,0,0x931,0xAC8,{189,1063}},{0,0,0,0,0,0,0x6,0x9C40,0x28D,{878,1064}},{0,0,0,0,0,0,0xE,0x9C40,0x99,{1022,1065}},{0,0,0,0,0,0,0x8,0x26C,0x2D6,{948,949}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{1066,1067}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{1068,1069}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1070,1071}},{0,0,0,0,0,0,0xE,0x19B4,0xAD5,{629,630}},{0,0,0,0,0,0,0x8,0x2EF4,0x898,{633,634}},{0,0,0,26,0,0,0x8,0x14B4,0x1151,{917,923}},{0,0,0,35,0,0,0xA,0xED1,0xC65,{915,916}},{0,0,0,41,0,0,0x8,0x17C,0x1B8,{659,249}},{0,0,0,48,0,0,0xC,0xAD,0xC1,{664,665}},{0,0,0,67,0,0,0xE,0xF6,0x118,{666,667}},{0,0,0,24,0,0,0xE,0x154,0x189,{927,928}},{0,0,0,36,0,0,0xE,0x196,0x5D,{651,652}},{0,0,0,38,0,0,0xE,0x196,0x5D,{651,652}},{0,0,0,43,0,0,0xA,0xA0,0xA6,{258,260}},{0,0,0,40,0,0,0xE,0x196,0x5D,{651,652}},{0,0,0,42,0,0,0xE,0x196,0x5D,{651,652}},{0,0,0,44,0,0,0xE,0x139,0x5D,{651,652}},{0,0,0,55,0,0,0xE,0x751,0x8B2,{938,939}},{0,0,0,46,0,0,0xE,0x139,0x5D,{651,652}},{0,0,0,84,0,0,0xE,0x542,0x639,{272,273}},{0,0,0,80,0,0,0xE,0x5BA,0x6A4,{264,265}},{0,0,0,24,0,0,0xE,0x4A9,0x585,{900,901}},{0,0,0,50,0,0,0xE,0x71,0x7E,{298,299}},{0,0,0,24,0,0,0xE,0x21C,0x5D,{651,652}},{0,0,0,31,0,0,0x8,0x26C,0x2DD,{948,949}},{0,0,0,35,0,0,0x8,0xED8,0xC6C,{917,923}},{0,0,0,60,0,0,0x8,0x118,0x140,{950,951}},{0,0,0,36,0,0,0xE,0x9C40,0x139,{971,972}},{0,0,0,48,0,0,0xE,0x9C40,0xE9,{971,972}},{0,0,0,0,0,0,0x8,0x9C40,0x1AA,{1072,1073}},{0,0,0,52,0,0,0x8,0x78,0x5D,{250,251}},{0,0,0,48,0,0,0xE,0x11E,0x146,{252,253}},{0,0,0,60,0,0,0xE,0x118,0x140,{252,253}},{0,0,0,43,0,0,0xA,0xAD,0xC1,{258,259}},{0,0,0,43,0,0,0xA,0x70E,0x82D,{261,262}},{0,0,0,72,0,0,0xE,0x74A,0x8A5,{237,263}},{0,0,0,76,0,0,0xE,0x5C8,0x6A4,{264,265}},{0,0,0,36,0,0,0x6,0x460,0x4CA,{268,269}},{0,0,0,76,0,0,0xE,0xFD,0x118,{270,271}},{0,0,0,83,0,0,0x8,0xDC,0xF6,{274,275}},{0,0,0,84,0,0,0xE,0x550,0x646,{237,263}},{0,0,0,24,0,0,0xC,0x73D,0x876,{276,277}},{0,0,0,77,0,0,0xE,0x5BA,0x690,{264,265}},{0,0,0,60,0,0,0x6,0xAD,0xC1,{278,279}},{0,0,0,65,0,0,0x7,0xC1,0x132,{280,281}},{0,0,0,59,0,0,0x6,0xA0,0x140,{282,283}},{0,0,0,51,0,0,0x6,0xAD,0xC1,{284,285}},{0,0,0,45,0,0,0x6,0xFD,0x258,{286,287}},{0,0,0,60,0,0,0xE,0x118,0x5D,{290,291}},{0,0,0,58,0,0,0xF,0x1ED,0x244,{292,293}},{0,0,0,53,0,0,0xF,0x1FA,0x251,{294,295}},{0,0,0,64,0,0,0xE,0xDC,0xAD,{296,297}},{0,0,0,71,0,0,0xE,0x6A,0x78,{298,299}},{0,0,0,61,0,0,0xE,0x3DA,0x46D,{300,301}},{0,0,0,61,0,0,0xE,0x76C,0x89E,{302,303}},{0,0,0,48,0,0,0xE,0xD5,0xAD,{304,305}},{0,0,0,48,0,0,0xE,0x1AA,0x125,{306,307}},{0,0,0,80,0,0,0x8,0xD5,0xE9,{316,317}},{0,0,0,64,0,0,0x8,0xA06,0xB9D,{318,319}},{0,0,0,69,0,0,0xE,0x7E,0x56,{320,321}},{0,0,0,73,0,0,0x6,0x182,0x78,{268,322}},{0,0,0,75,0,0,0xE,0x215,0x3AC,{323,324}},{0,0,0,68,0,0,0x8,0x78,0x140,{325,309}},{0,0,0,48,0,0,0x7,0x16E,0x474,{326,327}},{0,0,0,0,0,0,0x6,0x9C40,0x4F2,{1074,1075}},{0,0,0,0,0,0,0x6,0x3C6A,0x1450,{1076,1077}},{0,0,0,0,0,0,0,0xD7D,0xE9,{1078,1079}},{0,0,0,0,0,0,0xA,0x786,0x189,{1080,1081}},{0,0,0,0,0,0,0x8,0x1C70,0x21C6,{1082,1083}},{0,0,0,0,0,0,0,0x1DA9,0x1589,{1084,1085}},{0,0,0,0,0,0,0xC,0x49C,0x5B4,{1086,1087}},{0,0,0,0,0,0,0x4,0x4A9,0x57E,{1088,1089}},{0,0,0,0,0,0,0,0x9C40,0x5D,{1090,1091}},{0,0,0,0,0,0,0xA,0x9C40,0xA8C,{1066,1092}},{0,0,0,0,0,0,0,0x9C40,0x92,{1093,1094}},{0,0,0,0,0,0,0xA,0x74A,0x5C1,{1095,1096}},{0,0,0,0,0,0,0xA,0x9C40,0x56,{1097,1098}},{0,0,0,0,0,0,0x6,0x11EA,0xAC8,{52,1099}},{0,0,0,0,0,0,0x8,0x47CC,0xAE9,{1100,1101}},{0,0,0,0,0,0,0xA,0x10ED,0x2EA,{1102,1103}},{0,0,0,0,0,0,0,0x9C40,0x26C,{1104,1105}},{0,0,0,0,0,0,0xE,0x9C40,0x125,{438,439}},{0,0,0,0,0,0,0xC,0x9C40,0x535,{1106,1107}},{0,0,0,0,0,0,0x2,0x9C40,0x2AE,{1108,1109}},{0,0,0,0,0,0,0xC,0x2C9,0x251,{1110,1111}},{0,0,0,0,0,0,0,0x2266,0x14BA,{1112,1113}},{0,0,0,0,0,0,0x6,0x938,0x5D,{1114,1115}},{0,0,0,0,0,0,0x6,0x9C40,0x175,{1116,1117}},{0,0,0,0,0,0,0,0x9C40,0x15D9,{1118,1119}},{0,0,0,0,0,0,0xC,0x9C40,0x51A,{1106,1120}},{0,0,0,0,0,0,0xC,0x9C40,0x514,{1121,1122}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{1123,1124}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1125,1124}},{0,0,0,0,0,0,0xE,0x1090,0x474,{1126,1127}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1128,1124}},{0,0,0,0,0,0,0xE,0x9C40,0x71,{1129,1130}},{0,0,0,0,0,0,0xE,0x2335,0x5D,{1131,1132}},{0,0,0,0,0,0,0xE,0xBCC,0,{231,1133}},{0,0,0,0,0,0,0,0x9C40,0x7E,{1134,1135}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{1136,1137}},{0,0,0,0,0,0,0xC,0x9C40,0x64,{1138,1139}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{1140,1141}},{0,0,0,0,0,0,0xE,0x9C40,0x56,{1142,1143}},{0,0,0,0,0,0,0xE,0x9C40,0x6A,{1144,1145}},{0,0,0,0,0,0,0xD,0x9C40,0x7E,{1146,1147}},{0,0,0,0,0,0,0,0x170C,0x571,{1148,1149}},{0,0,0,0,0,0,0x8,0x9C40,0x2E4,{1150,1151}},{0,0,0,0,0,0,0,0x9C40,0x107C,{981,1152}},{0,0,0,0,0,0,0xE,0x3354,0x21B9,{575,576}},{0,0,0,0,0,0,0xA,0x9C40,0x13F9,{1153,1154}},{0,0,0,0,0,0,0xC,0x98E,0xAC1,{1155,1156}},{0,0,0,0,0,0,0x6,0x1270,0x2D6,{1157,1158}},{0,0,0,0,0,0,0x4,0x71C,0x460,{1159,1160}},{0,0,0,0,0,0,0x6,0x9C40,0xCE,{1161,1162}},{0,0,0,0,0,0,0,0xD55,0x1651,{1163,1164}},{0,0,0,0,0,0,0xE,0x21C,0x5D,{651,652}},{0,0,0,0,0,0,0x8,0x1CAC,0x223E,{960,1165}},{0,0,0,0,0,0,0xC,0x4A2,0x571,{759,760}},{0,0,0,0,0,0,0xE,0x3E1,0xADC,{929,934}},{0,0,0,60,0,0,0x8,0xA6A,0xC65,{960,1165}},{0,0,0,79,0,0,0x8,0x550,0x64D,{960,1165}},{0,0,0,65,0,0,0xE,0x7EA,0x891,{1166,1167}},{0,0,0,31,0,0,0xE,0x48E,0x56A,{900,901}},{0,0,0,36,0,0,0xE,0x4A9,0x58C,{900,901}},{0,0,0,72,0,0,0xE,0x6A4,0x722,{1166,1167}},{0,0,0,79,0,0,0xE,0x5BA,0x6AA,{264,265}},{0,0,0,57,0,0,0xE,0x201,0x258,{288,289}},{0,0,0,53,0,0,0xF,0x1F4,0x244,{292,293}},{0,0,0,84,0,0,0xE,0xE2,0x99,{1168,1169}},{0,0,0,66,0,0,0x8,0xAD,0x5D,{955,742}},{0,0,0,70,0,0,0xE,0x20E,0x3A5,{323,324}},{0,0,0,31,0,0,0x7,0x9C40,0x168,{1170,1171}},{0,0,0,29,0,0,0x7,0x9C40,0x168,{1170,1171}},{0,0,0,31,0,0,0xE,0x564,0x646,{670,671}},{0,0,0,19,0,0,0xE,0x780,0x89E,{670,671}},{0,0,0,31,0,0,0x7,0x9C40,0x64,{1172,1173}},{0,0,0,29,0,0,0x7,0x9C40,0x64,{1172,1173}},{0,0,0,31,0,0,0xE,0x8FC,0x56A,{1174,1175}},{0,0,0,35,0,0,0xE,0x8E8,0x56A,{1174,1175}},{0,0,0,40,0,0,0xE,0x902,0x571,{1174,1175}},{0,0,0,47,0,0,0xE,0x74A,0x46D,{1174,1175}},{0,0,0,32,0,0,0x8,0x26C,0x2D6,{948,949}},{0,0,0,43,0,0,0x8,0x201,0x251,{948,949}},{0,0,0,26,0,0,0x8,0xC6C,0x2DD,{917,918}},{0,0,0,44,0,0,0x8,0x9F9,0x258,{917,918}},{0,0,0,26,0,0,0xA,0x99,0x92,{919,920}},{0,0,0,51,0,0,0xA,0x92,0x85,{919,920}},{0,0,0,39,0,0,0xA,0xA0,0x92,{919,920}},{0,0,0,30,0,0,0x8,0xC6C,0x2DD,{917,918}},{0,0,0,44,0,0,0xA,0x751,0x8A5,{1176,1177}},{0,0,0,43,0,0,0xA,0x74A,0x8A5,{1176,1177}},{0,0,0,0,0,0,0x4,0x952,0xAAD,{1178,1179}},{0,0,0,0,0,0,0x6,0x1255,0x64,{1180,1181}},{0,0,0,0,0,0,0xE,0x8FC,0xABA,{1182,1183}},{0,0,0,0,0,0,0x2,0x9C40,0x140,{1184,1185}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1186,1187}},{0,0,0,0,0,0,0x2,0x1686,0x114A,{1188,1189}},{0,0,0,0,0,0,0,0x9C40,0x57E,{1190,1191}},{0,0,0,0,0,0,0x8,0x744,0x89E,{1192,1193}},{0,0,0,0,0,0,0x4,0x9C40,0x64,{1194,1195}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{1196,1197}},{0,0,0,0,0,0,0,0x9C40,0x5D,{1090,1198}},{0,0,0,0,0,0,0x4,0x9C40,0x1E6,{1199,1200}},{0,0,0,0,0,0,0x8,0x805,0x556,{1201,1202}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1203,1204}},{0,0,0,0,0,0,0x8,0xE74,0x1136,{1205,1206}},{0,0,0,0,0,0,0xC,0xFBA,0x1309,{1207,1208}},{0,0,0,0,0,0,0xA,0x2231,0x425A,{1209,1210}},{0,0,0,0,0,0,0xE,0x786,0x5D,{1211,1212}},{0,0,0,0,0,0,0x6,0x2079,0x88A,{904,1213}},{0,0,0,0,0,0,0xF,0x9C40,0x2DD,{1214,1215}},{0,0,0,0,0,0,0x2,0x9C40,0x2C9,{1216,1217}},{0,0,0,0,0,0,0,0x9C40,0x2D0,{1218,1219}},{0,0,0,0,0,0,0x4,0x9C40,0x1E6,{1220,1200}},{0,0,0,0,0,0,0x1,0x9C40,0x28D,{1221,1215}},{0,0,0,0,0,0,0xA,0x370,0x5D,{1222,1223}},{0,0,0,0,0,0,0xA,0x1310,0x140,{1224,1225}},{0,0,0,0,0,0,0x4,0x9C40,0x585,{1226,1227}},{0,0,0,0,0,0,0xA,0x2BC,0xE9,{1228,1229}},{0,0,0,0,0,0,0x6,0x2BC,0xE9,{1230,1229}},{0,0,0,0,0,0,0x7,0x9C40,0x175,{1231,1232}},{0,0,0,0,0,0,0,0x1FA4,0xAD5,{1233,1234}},{0,0,0,0,0,0,0x4,0xD5,0x52F8,{1235,1236}},{0,0,0,0,0,0,0x8,0x751,0x891,{1205,1237}},{0,0,0,0,0,0,0x8,0xF7E,0x251,{633,1238}},{0,0,0,0,0,0,0x6,0x9C40,0x862,{1239,1240}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{1241,1242}},{0,0,0,0,0,0,0xE,0x9C40,0x161,{1243,1244}},{0,0,0,0,0,0,0xA,0x640,0x8A5,{1245,1246}},{0,0,0,0,0,0,0x6,0x3C56,0x142E,{1076,1247}},{0,0,0,0,0,0,0xA,0x9C40,0x500,{1248,1249}},{0,0,0,0,0,0,0x6,0x9C40,0xAA0,{1250,1251}},{0,0,0,0,0,0,0xE,0x175,0x2D6,{1252,1253}},{0,0,0,0,0,0,0xE,0x9C40,0x16F8,{1254,1255}},{0,0,0,0,0,0,0,0x3BF9,0x5D,{1256,1257}},{0,0,0,0,0,0,0,0x4B6,0x2DD,{1258,1259}},{0,0,0,0,0,0,0,0x2266,0x14BA,{1260,1261}},{0,0,0,0,0,0,0xC,0x1720,0x21E1,{1262,1263}},{0,0,0,0,0,0,0xA,0x305,0xE2,{1264,1265}},{0,0,0,0,0,0,0xC,0xED1,0x5D,{1266,1267}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{412,413}},{0,0,0,0,0,0,0xE,0x9C40,0x64,{1268,1204}},{0,0,0,0,0,0,0xA,0xE31,0x10F4,{1176,1177}},{0,0,0,0,0,0,0x8,0x9C40,0x85,{1269,1270}},{0,0,0,0,0,0,0xA,0x9C40,0x56,{1271,1272}},{0,0,0,0,0,0,0x4,0x9C40,0x5D,{1273,1274}},{0,0,0,0,0,0,0x2,0x9C40,0x6A,{1275,1145}},{0,0,0,0,0,0,0,0x6A,0x71,{1276,1277}},{0,0,0,0,0,0,0xB,0x9C40,0x258,{1278,1279}},{0,0,0,0,0,0,0xA,0xF49,0x64,{1280,1281}},{0,0,0,0,0,0,0,0x9C40,0x5D,{1282,1283}},{0,0,0,0,0,0,0x4,0x9C40,0x64,{1284,1285}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{1241,1286}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{1287,1288}},{0,0,0,0,0,0,0x2,0x9C40,0x452,{1289,1290}},{0,0,0,0,0,0,0xE,0x9C40,0xE2,{1291,1292}},{0,0,0,0,0,0,0x2,0x9C40,0x2C2,{1293,1294}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{1295,1296}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1297,1124}},{0,0,0,0,0,0,0xC,0x9C40,0x71,{1298,1130}},{0,0,0,0,0,0,0x7,0x9C40,0x182,{1299,1300}},{0,0,0,0,0,0,0x6,0x9C40,0xA0D,{1301,1302}},{0,0,0,0,0,0,0xE,0x9C40,0x2C2,{1303,1304}},{0,0,0,0,0,0,0x2,0xE8E,0x1144,{1305,1306}},{0,0,0,0,0,0,0xA,0x9C40,0x92,{1307,1308}},{0,0,0,0,0,0,0xE,0x924,0xAE2,{1309,1310}},{0,0,0,0,0,0,0xA,0x495,0x578,{1311,1312}},{0,0,0,0,0,0,0xE,0x32C8,0x39E4,{1313,1314}},{0,0,0,0,0,0,0x4,0x3C6,0x114A,{1315,1316}},{0,0,0,0,0,0,0xF,0x9C40,0x826,{1317,1318}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{1319,1320}},{0,0,0,0,0,0,0xE,0x9C40,0x6A,{1321,1322}},{0,0,0,0,0,0,0x6,0x4DE,0x64,{1114,1323}},{0,0,0,0,0,0,0xE,0xE81,0x113D,{1324,1325}},{0,0,0,0,0,0,0x8,0x916,0xAD5,{1326,1327}},{0,0,0,0,0,0,0xA,0x4B6,0x58C,{1328,1329}},{0,0,0,0,0,0,0xE,0x125,0x2D6,{929,935}},{0,0,0,0,0,0,0xE,0x1FA,0xAD5,{942,710}},{0,0,0,0,0,0,0x2,0x3980,0x447D,{1305,1330}},{0,0,0,0,0,0,0x6,0x3882,0x42D9,{1331,1332}},{0,0,0,0,0,0,0xA,0x146A,0x8A5,{1333,1334}},{0,0,0,0,0,0,0x2,0x9C40,0x2274,{1335,1336}},{0,0,0,0,0,0,0xE,0x9C40,0xAE2,{631,1337}},{0,0,0,0,0,0,0x6,0x9C40,0x21DA,{1338,1339}},{0,0,0,35,0x4,0,0,0x6,0,{1340,1340}},{0,0,0,12,0,0,0,0xAD,0x5D,{1341,1342}},{0,0,0,48,0,0,0,0xDC,0x5D,{693,1343}},{0,0,0,52,0,0,0xE,0x118,0x5D,{1344,1345}},{0,0,0,60,0,0,0xE,0xDC,0xFD,{1346,1347}},{0,0,0,60,0,0,0xE,0xDC,0x5D,{1348,1349}},{0,0,0,55,0,0,0x8,0x64,0x5D,{280,924}},{0,0,0,41,0,0,0x8,0x6A,0x5D,{280,924}},{0,0,0,60,0,0,0xE,0x8C,0x99,{1350,1351}},{0,0,0,84,0,0,0xE,0x15A,0x730,{709,710}},{0,0,0,48,0,0,0x8,0x64,0x5D,{280,924}},{0,0,0,60,0,0,0xE,0xDC,0xF6,{1352,1353}},{0,0,0,15,0,0,0,0x15A,0x189,{1354,1355}},{0,0,0,49,0,0,0x8,0xAD,0x5D,{955,845}},{0,0,0,16,0,0,0,0x15A,0x182,{1354,1355}},{0,0,0,12,0,0,0,0x15A,0x189,{1354,1355}},{0,0,0,48,0,0,0x4,0x1FA,0x891,{1315,1316}},{0,0,0,55,0,0,0xE,0xDC,0xF6,{1346,1347}},{0,0,0,95,0,0,0x6,0x74A,0x88A,{1331,1332}},{0,0,0,15,0,0,0x8,0xC8,0x5D,{955,845}},{0,0,0,17,0,0,0x8,0xC8,0x5D,{955,845}},{0,0,0,65,0,0,0,0xBA,0x5D,{1356,1357}},{0,0,0,20,0,0,0,0x272,0x2DD,{1358,1359}},{0,0,0,22,0,0,0,0x26C,0x2D6,{1358,1359}},{0,0,0,24,0,0,0,0x272,0x2DD,{1358,1359}},{0,0,0,12,0,0,0,0xC1,0x5D,{1360,1361}},{0,0,0,0,0,0,0,0x37DC,0x459,{1362,1363}},{0,0,0,0,0,0,0x6,0x375D,0x466,{1364,1365}},{0,0,0,0,0,0,0xC,0x3A26,0x481,{1366,1367}},{0,0,0,0,0,0,0,0x1C41,0x460,{1368,1369}},{0,0,0,0,0,0,0,0x3979,0x85,{1370,1371}},{0,0,0,0,0,0,0,0x3979,0x474,{1372,1373}},{0,0,0,0,0,0,0xE,0x38FA,0x24A,{1374,1375}},{0,0,0,0,0,0,0,0x9C40,0x56,{1376,1377}},{0,0,0,0,0,0,0x9,0xB68,0x6E6,{1378,1379}},{0,0,0,0,0,0,0x1,0xBD2,0xAA6,{1380,1381}},{0,0,0,0,0,0,0xB,0x73D,0xAFD,{1382,1383}},{0,0,0,0,0,0,0x1,0x38F4,0x113D,{1384,1385}},{0,0,0,0,0,0,0x5,0x701,0x862,{1386,1387}},{0,0,0,0,0,0,0,0x3C6,0x466,{1388,1389}},{0,0,0,0,0,0,0xA,0xD7D,0xAA0,{1390,1391}},{0,0,0,0,0,0,0,0xB61,0x826,{1392,1393}},{0,0,0,0,0,0,0x7,0x9C40,0x5D,{1394,1395}},{0,0,0,0,0,0,0x5,0x9C40,0x5D,{1396,1397}},{0,0,0,0,0,0,0xB,0x9C40,0x3C0,{1398,1399}},{0,0,0,0,0,0,0x9,0x9C40,0x265,{1400,1401}},{0,0,0,0,0,0,0,0x9C40,0x132,{1402,1403}},{0,0,0,0,0,0,0xE,0x9C40,0x132,{1404,1405}},{0,0,0,0,0,0,0,0x9C40,0xC1,{1406,1407}},{0,0,0,0,0,0,0xE,0x9C40,0x24A,{1408,1409}},{0,0,0,0,0,0,0x4,0x1CC0,0xC1,{1410,1411}},{0,0,0,0,0,0,0,0x1906,0x140,{1412,1413}},{0,0,0,0,0,0,0xE,0x9C40,0x56,{1414,1415}},{0,0,0,0,0,0,0,0x9C40,0x50,{1416,1417}},{0,0,0,0,0,0,0x6,0x4BD,0x64,{1418,1419}},{0,0,0,0,0,0,0x6,0x9C40,0x78,{1420,1421}},{0,0,0,0,0,0,0,0x4498,0x5D,{1422,1423}},{0,0,0,0,0,0,0x8,0x91D,0x5D,{1424,1425}},{0,0,0,0,0,0,0,0x11BC,0x196,{1426,1427}},{0,0,0,0,0,0,0,0x9C40,0xAD,{1428,1429}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{1430,1431}},{0,0,0,0,0,0,0xA,0x9C40,0xDC,{1432,1433}},{0,0,0,0,0,0,0x6,0x9CA,0x146,{1434,1435}},{0,0,0,0,0,0,0xC,0x9CA,0x146,{1436,1437}},{0,0,0,0,0,0,0x8,0x9C40,0x49,{1438,1439}},{0,0,0,0,0,0,0x8,0xD3A,0x71,{1440,1441}},{0,0,0,0,0,0,0xC,0x9C40,0xDC,{1442,1443}},{0,0,0,0,0,0,0xC,0x9C40,0xDC,{1444,1443}},{0,0,0,0,0,0,0xA,0x9C40,0x175,{1445,1446}},{0,0,0,0,0,0,0xC,0x9C40,0x1B8,{1447,1448}},{0,0,0,0,0,0,0xC,0x9C40,0x2AE,{1449,1450}},{0,0,0,0,0,0,0x9,0x43E,0x2A8,{1451,1452}},{0,0,0,0,0,0,0xE,0xE1D,0x10B8,{1453,1454}},{0,0,0,0,0,0,0,0x4BD,0x599,{1455,1456}},{0,0,0,0,0,0,0xE,0x9C40,0x2C9,{1457,1458}},{0,0,0,0,0,0,0xE,0x9C40,0x578,{1459,1460}},{0,0,0,0,0,0,0xE,0x9C40,0x279,{1461,1462}},{0,0,0,0,0,0,0xE,0x9C40,0x189,{1463,1464}},{0,0,0,0,0,0,0x8,0x9C40,0x7E,{1465,1466}},{0,0,0,0,0,0,0,0x9C40,0x16E,{1467,1468}},{0,0,0,0,0,0,0,0x9C40,0x24A,{1469,1470}},{0,0,0,0,0,0,0xB,0x4A2,0x229,{1471,1472}},{0,0,0,0,0,0,0xE,0x9C40,0xAD,{1473,1474}},{0,0,0,0,0,0,0xE,0x9C40,0x8C,{1475,1476}},{0,0,0,0,0,0,0xE,0x9C40,0x92,{1477,1478}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{1479,1480}},{0,0,0,0,0,0,0x8,0x9C40,0x78,{1481,1482}},{0,0,0,0,0,0,0xE,0x9C40,0x8C,{1483,1484}},{0,0,0,0,0,0,0xE,0x9C40,0x175,{1485,1486}},{0,0,0,0,0,0,0xE,0x9C40,0x2C2,{1487,1488}},{0,0,0,0,0,0,0,0x9C40,0x71,{1489,1490}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{1491,1492}},{0,0,0,0,0,0,0xC,0x9C40,0x6A,{1493,1494}},{0,0,0,0,0,0,0x8,0x9C40,0xDC,{1495,1496}},{0,0,0,0,0,0,0,0x9C40,0xC1,{1497,1498}},{0,0,0,0,0,0,0,0x9C40,0x71,{1499,1500}},{0,0,0,0,0,0,0xA,0x9C40,0x7E,{1501,1502}},{0,0,0,0,0,0,0,0x9C40,0x92,{1503,1504}},{0,0,0,0,0,0,0,0x9C40,0xCE,{1505,1506}},{0,0,0,0,0,0,0,0x9C40,0x71,{1507,1508}},{0,0,0,0,0,0,0,0x9C40,0x8C,{1509,1510}},{0,0,0,0,0,0,0,0x9C40,0x92,{1511,1512}},{0,0,0,0,0,0,0xE,0x9C40,0xCE,{1513,1514}},{0,0,0,0,0,0,0xE,0x9C40,0x12C,{1515,1516}},{0,0,0,0,0,0,0x1,0x9C40,0xE2,{1517,1518}},{0,0,0,0,0,0,0,0x9C40,0x189,{1519,1520}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1521,1522}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1523,1524}},{0,0,0,0,0,0,0x2,0x9C40,0x11E,{1525,1526}},{0,0,0,0,0,0,0xE,0x9C40,0x7E,{1527,1528}},{0,0,0,0,0,0,0,0x9C40,0x6A,{1529,1530}},{0,0,0,0,0,0,0,0x9C40,0x71,{1531,1532}},{0,0,0,0,0,0,0x9,0x9C40,0xC1,{1533,1534}},{0,0,0,0,0,0,0xE,0x9C40,0x56,{1535,1536}},{0,0,0,0,0,0,0x1,0x9C40,0x49C,{1537,1538}},{0,0,0,0,0,0,0xE,0x9C40,0x52E,{1539,1540}},{0,0,0,0,0,0,0xC,0x9C40,0x2D6,{1541,1542}},{0,0,0,0,0,0,0,0x9C40,0x14D,{1543,1544}},{0,0,0,0,0,0,0,0x9C40,0x495,{1545,1546}},{0,0,0,0,0,0,0x4,0x269E,0x571,{1547,1548}},{0,0,0,0,0,0,0xA,0x9C40,0x2B5,{1549,1550}},{0,0,0,0,0,0,0xA,0x9C40,0x55D,{1551,1552}},{0,0,0,0,0,0,0,0x38D9,0x251,{1553,1554}},{0,0,0,0,0,0,0x7,0x9C40,0x52E,{1555,1556}},{0,0,0,0,0,0,0,0x1212,0xABA,{1557,1558}},{0,0,0,0,0,0,0,0x2295,0x550,{1559,1560}},{0,0,0,0,0,0,0xE,0x230D,0xA99,{1561,1562}},{0,0,0,0,0,0,0xC,0x9C40,0x4EC,{1563,1564}},{0,0,0,0,0,0,0xE,0x9C40,0x26C,{1565,1566}},{0,0,0,0,0,0,0xA,0x9C40,0x564,{1567,1568}},{0,0,0,0,0,0,0x8,0x1194,0x2C9,{1569,1570}},{0,0,0,0,0,0,0xC,0x279,0xE2,{1571,1572}},{0,0,0,0,0,0,0xC,0xE81,0x474,{1573,1574}},{0,0,0,0,0,0,0x8,0x910,0x56A,{1575,1576}},{0,0,0,0,0,0,0,0x786,0x85C,{1577,1578}},{0,0,0,0,0,0,0x8,0x9C40,0xD5,{1579,1580}},{0,0,0,0,0,0,0,0x9C40,0xE2,{1581,1582}},{0,0,0,0,0,0,0xA,0x9C40,0xDC,{1583,1584}},{0,0,0,0,0,0,0,0xC15,0x2D0,{1585,1586}},{0,0,0,0,0,0,0xE,0x201,0xC1,{1587,1588}},{0,0,0,0,0,0,0x7,0x2B5,0x2D0,{1589,1590}},{0,0,0,0,0,0,0,0x175,0x6A,{680,681}},{0,0,0,0,0,0,0,0x424,0x5A0,{1591,1592}},{0,0,0,0,0,0,0x4,0x786,0x902,{1593,1594}},{0,0,0,0,0,0,0x8,0x4C4,0x542,{1595,1596}},{0,0,0,0,0,0,0xE,0x1061,0x64,{1597,1598}},{0,0,0,0,0,0,0,0x33A,0x140,{1599,1600}},{0,0,0,0,0,0,0xC,0x111,0x17C,{1601,1602}},{0,0,0,0,0,0,0xE,0x147E,0xAAD,{1603,1604}},{0,0,0,0,0,0,0,0x182,0x8C,{1605,1606}},{0,0,0,0,0,0,0,0x9C40,0xE2,{1607,1608}},{0,0,0,0,0,0,0x2,0x9C40,0x571,{1609,1610}},{0,0,0,0,0,0,0xE,0x9C40,0x3D4,{1611,1612}},{0,0,0,36,0,0,0xE,0xE9,0x64,{1613,1614}},{0,0,0,48,0,0,0x8,0xC1,0xB4,{1615,1616}},{0,0,0,36,0,0,0x8,0xE2,0xDC,{1615,1616}},{0,0,0,36,0,0,0,0x6A,0x71,{676,1617}},{0,0,0,32,0,0,0,0x85,0x71,{678,1618}},{0,0,0,96,0,0,0x1,0x6E6,0x640,{1380,1381}},{0,0,0,30,0,0,0x4,0xF6,0x71,{1619,1620}},{0,0,0,35,0,0,0x4,0x1A4,0x182,{1621,1622}},{0,0,0,60,0,0,0xE,0xF0,0x85,{1623,1624}},{0,0,0,59,0,0,0xE,0x92,0x5D,{1613,1614}},{0,0,0,44,0,0,0xE,0xF0,0x85,{1623,1624}},{0,0,0,41,0,0,0xD,0x2C9,0x33A,{804,805}},{0,0,0,47,0,0,0x6,0xAD,0xC1,{1625,1626}},{0,0,0,44,0,0,0xD,0x201,0x258,{804,805}},{0,0,0,48,0,0,0xD,0x201,0x258,{804,805}},{0,0,0,62,0,0,0x4,0x751,0xBA,{1627,1628}},{0,0,0,51,0,0,0xD,0x201,0x258,{804,805}},{0,0,0,54,0,0,0xD,0x201,0x258,{804,805}},{0,0,0,40,0,0,0xE,0x4DE,0xAB4,{810,1629}},{0,0,0,57,0,0,0xD,0x17C,0x1B8,{804,805}},{0,0,0,58,0,0,0xE,0x5D5,0x876,{707,708}},{0,0,0,97,0,0,0,0xE9,0xF6,{813,1630}},{0,0,0,50,0,0,0x8,0xDC,0xC1,{1631,1632}},{0,0,0,60,0,0,0xE,0x5C1,0x855,{707,708}},{0,0,0,53,0,0,0,0x85,0x85,{1633,1634}},{0,0,0,46,0,0,0,0xAD,0x14D,{1635,1636}},{0,0,0,57,0,0,0,0x85,0x85,{1633,1634}},{0,0,0,42,0,0,0xE,0x279,0x2DD,{1637,1638}},{0,0,0,37,0,0,0xE,0x279,0x2DD,{1637,1638}},{0,0,0,41,0,0,0x6,0x26C,0x2D6,{1639,1640}},{0,0,0,37,0,0,0x6,0x272,0x2DD,{1639,1640}},{0,0,0,77,0,0,0xE,0xAD,0x6A,{1587,1588}},{0,0,0,72,0,0,0xE,0xAD,0x6A,{1587,1588}},{0,0,0,70,0,0,0xE,0xD5,0xE2,{731,732}},{0,0,0,90,0,0,0xE,0xCE,0xE2,{731,732}},{0,0,0,39,0,0,0x4,0x104,0x64,{1641,1642}},{0,0,0,36,0,0,0,0x445,0x64,{1643,1644}},{0,0,0,46,0,0,0xA,0x78,0x64,{829,1645}},{0,0,0,48,0,0,0x8,0x2FE,0x64,{1646,1647}},{0,0,0,85,0,0,0x8,0x7E,0x5D,{1648,1649}},{0,0,0,66,0,0,0,0xB4,0x5D,{680,681}},{0,0,0,41,0,0,0,0xC1,0x64,{1650,1651}},{0,0,0,41,0,0,0,0x14D,0x161,{1652,1653}},{0,0,0,81,0,0,0x6,0x99,0x5D,{1654,1655}},{0,0,0,10,0,0,0x6,0x4A9,0x57E,{751,752}},{0,0,0,60,0,0,0,0x64,0x64,{676,1617}},{0,0,0,53,0,0,0,0x348,0x47A,{1591,1592}},{0,0,0,0,0,0,0,0x15CC,0x460,{1656,1657}},{0,0,0,0,0,0,0,0x38E0,0x466,{1658,1659}},{0,0,0,0,0,0,0,0x1B79,0x445,{1660,1661}},{0,0,0,0,0,0,0,0x1AC5,0x438,{1662,1663}},{0,0,0,0,0,0,0x5,0x23D5,0x182,{1,1664}},{0,0,0,0,0,0,0,0x38AA,0x460,{1665,1666}},{0,0,0,0,0,0,0xC,0x1BF8,0x140,{1667,1668}},{0,0,0,0,0,0,0,0x9C40,0x42,{1669,1670}},{0,0,0,0,0,0,0x6,0x744,0x56A,{1671,1672}},{0,0,0,0,0,0,0x1,0x488,0x564,{682,683}},{0,0,0,0,0,0,0x9,0x460,0x535,{1673,1674}},{0,0,0,0,0,0,0,0x3972,0x113D,{1675,1676}},{0,0,0,0,0,0,0xC,0x73D,0x88A,{1677,1678}},{0,0,0,0,0,0,0x9,0x3C6,0x459,{1679,1680}},{0,0,0,0,0,0,0,0x91D,0xABA,{1681,1682}},{0,0,0,0,0,0,0,0xBB1,0x862,{1683,1684}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{1685,1686}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{1687,1688}},{0,0,0,0,0,0,0x7,0x9C40,0x5D,{1689,1690}},{0,0,0,0,0,0,0xB,0x9C40,0x258,{1691,1692}},{0,0,0,0,0,0,0,0x9C40,0x132,{1402,1693}},{0,0,0,0,0,0,0xE,0x9C40,0x132,{1694,1695}},{0,0,0,0,0,0,0,0x9C40,0xC1,{1696,1697}},{0,0,0,0,0,0,0,0x9C40,0x132,{1698,1699}},{0,0,0,0,0,0,0xE,0x1CA5,0xC1,{1700,1701}},{0,0,0,0,0,0,0,0x1504,0x139,{1702,1703}},{0,0,0,0,0,0,0x8,0x9C40,0x56,{1704,1705}},{0,0,0,0,0,0,0xA,0x9C40,0x49,{1706,1707}},{0,0,0,0,0,0,0xA,0x938,0x64,{1708,1709}},{0,0,0,0,0,0,0,0x9C40,0x5D,{1710,1711}},{0,0,0,0,0,0,0,0x9C40,0x8C,{1712,1713}},{0,0,0,0,0,0,0x4,0x177D,0x5D,{1714,1715}},{0,0,0,0,0,0,0,0xE7A,0x146,{1716,1717}},{0,0,0,0,0,0,0x8,0x44F5,0x5D,{1718,1719}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{1720,1721}},{0,0,0,0,0,0,0xA,0x9C40,0xBA,{1722,1723}},{0,0,0,0,0,0,0x8,0x9CA,0x146,{1724,1725}},{0,0,0,0,0,0,0xC,0xE4C,0x5D,{1726,1727}},{0,0,0,0,0,0,0xE,0x9C40,0x49,{1728,1729}},{0,0,0,0,0,0,0xA,0xD3A,0x71,{1730,1731}},{0,0,0,0,0,0,0xC,0x9C40,0xDC,{1442,1732}},{0,0,0,0,0,0,0xC,0x9C40,0xDC,{1444,1733}},{0,0,0,0,0,0,0xC,0x9C40,0xE9,{1734,1735}},{0,0,0,0,0,0,0xE,0x9C40,0x1B1,{1736,1737}},{0,0,0,0,0,0,0xC,0x9C40,0x2A8,{1449,1738}},{0,0,0,0,0,0,0x9,0x474,0x2B5,{1739,1740}},{0,0,0,0,0,0,0xE,0x862,0x556,{1741,1742}},{0,0,0,0,0,0,0,0x32D,0x2D6,{1743,1744}},{0,0,0,0,0,0,0,0x9C40,0xE2,{1745,1746}},{0,0,0,0,0,0,0xE,0x9C40,0x556,{1747,1748}},{0,0,0,0,0,0,0xE,0x9C40,0x20E,{1749,1750}},{0,0,0,0,0,0,0xE,0x9C40,0x182,{1751,1752}},{0,0,0,0,0,0,0x6,0x9C40,0x85,{467,468}},{0,0,0,0,0,0,0,0x9C40,0x168,{1753,1754}},{0,0,0,0,0,0,0,0x9C40,0x244,{1469,1755}},{0,0,0,0,0,0,0xD,0x4A9,0x229,{1756,1472}},{0,0,0,0,0,0,0xE,0x9C40,0xBA,{1757,1758}},{0,0,0,0,0,0,0xE,0x9C40,0x92,{1759,1760}},{0,0,0,0,0,0,0xE,0x9C40,0x92,{1761,1762}},{0,0,0,0,0,0,0,0x9C40,0x56,{1763,1764}},{0,0,0,0,0,0,0xC,0x9C40,0x78,{1765,1766}},{0,0,0,0,0,0,0xE,0x9C40,0xD5,{1767,1768}},{0,0,0,0,0,0,0xE,0x9C40,0x146,{1769,1770}},{0,0,0,0,0,0,0xE,0x9C40,0x2BC,{1771,1772}},{0,0,0,0,0,0,0,0x9C40,0x71,{1773,1774}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{1491,1775}},{0,0,0,0,0,0,0x8,0x9C40,0x42,{1776,1777}},{0,0,0,0,0,0,0x6,0x9C40,0xC1,{1778,1779}},{0,0,0,0,0,0,0,0x9C40,0x64,{1780,1781}},{0,0,0,0,0,0,0xA,0x9C40,0x71,{1782,1783}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{1784,1785}},{0,0,0,0,0,0,0xE,0x9C40,0x64,{1786,1787}},{0,0,0,0,0,0,0,0x9C40,0xE2,{1788,1789}},{0,0,0,0,0,0,0,0x9C40,0x64,{1790,1791}},{0,0,0,0,0,0,0,0x9C40,0x85,{1792,1793}},{0,0,0,0,0,0,0,0x9C40,0x92,{1794,1795}},{0,0,0,0,0,0,0xE,0x9C40,0xDC,{1796,1797}},{0,0,0,0,0,0,0xE,0x9C40,0x12C,{1798,1799}},{0,0,0,0,0,0,0x1,0x9C40,0xE2,{1517,1800}},{0,0,0,0,0,0,0,0x9C40,0x92,{1801,1802}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1803,1804}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1805,1806}},{0,0,0,0,0,0,0xC,0x9C40,0x99,{1807,1808}},{0,0,0,0,0,0,0x8,0x9C40,0x7E,{1809,1810}},{0,0,0,0,0,0,0,0x9C40,0x92,{1811,1812}},{0,0,0,0,0,0,0,0x9C40,0x71,{1813,1814}},{0,0,0,0,0,0,0xB,0x9C40,0xD5,{1815,1816}},{0,0,0,0,0,0,0xE,0x9C40,0x56,{1817,1818}},{0,0,0,0,0,0,0x5,0x9C40,0x3DA,{1819,1820}},{0,0,0,0,0,0,0xE,0x9C40,0x52E,{1821,1822}},{0,0,0,0,0,0,0xC,0x9C40,0x2B5,{1823,1824}},{0,0,0,0,0,0,0,0x9C40,0x14D,{1825,1826}},{0,0,0,0,0,0,0,0x9C40,0x25E,{1827,1828}},{0,0,0,0,0,0,0x4,0x26AC,0x57E,{1829,1830}},{0,0,0,0,0,0,0xC,0x9C40,0x2A8,{1831,1832}},{0,0,0,0,0,0,0xA,0x9C40,0x550,{1833,1834}},{0,0,0,0,0,0,0xE,0x4E41,0x2EA,{1835,1836}},{0,0,0,0,0,0,0xB,0x9C40,0x542,{1837,1838}},{0,0,0,0,0,0,0,0x11A1,0x556,{1839,1840}},{0,0,0,0,0,0,0x8,0x20C2,0x521,{1841,1842}},{0,0,0,0,0,0,0xE,0x2191,0x53C,{1843,1844}},{0,0,0,0,0,0,0xE,0x9C40,0x286,{1845,1846}},{0,0,0,0,0,0,0xE,0x9C40,0x272,{1847,1848}},{0,0,0,0,0,0,0xA,0x9C40,0x550,{1567,1849}},{0,0,0,0,0,0,0x8,0x113D,0x2B5,{1850,1851}},{0,0,0,0,0,0,0,0x6C5,0x132,{1852,1853}},{0,0,0,0,0,0,0xC,0xE74,0x474,{1854,1855}},{0,0,0,0,0,0,0,0x1136,0x550,{1856,1857}},{0,0,0,0,0,0,0xA,0x3EE,0x24A,{1858,1859}},{0,0,0,0,0,0,0xE,0x9C40,0x6A,{1860,1861}},{0,0,0,0,0,0,0,0x9C40,0xE2,{1862,1863}},{0,0,0,0,0,0,0,0x9C40,0xDC,{1864,1865}},{0,0,0,0,0,0,0,0xC0E,0x2D0,{1585,1866}},{0,0,0,0,0,0,0xE,0x201,0xC1,{825,826}},{0,0,0,0,0,0,0x5,0x286,0x2B5,{1867,1868}},{0,0,0,0,0,0,0,0x175,0x6A,{835,681}},{0,0,0,0,0,0,0xC,0x9C40,0x50,{841,842}},{0,0,0,0,0,0,0,0x3F5,0x438,{1869,1870}},{0,0,0,0,0,0,0xE,0x4A9,0x549,{1871,1872}},{0,0,0,0,0,0,0xE,0x1061,0x64,{1597,1873}},{0,0,0,0,0,0,0x8,0x33A,0x140,{1874,1600}},{0,0,0,0,0,0,0xE,0x2A1,0x2A8,{1875,1876}},{0,0,0,0,0,0,0xE,0x142E,0xA64,{1603,1877}},{0,0,0,0,0,0,0,0x23B4,0x99,{1878,1879}},{0,0,0,0,0,0,0,0x9C40,0xE2,{1607,1880}},{0,0,0,0,0,0,0xE,0x9C40,0x57E,{1881,1882}},{0,0,0,0,0,0,0xE,0x9C40,0x4CA,{1611,1883}},{0,0,0,0,0,0,0xE,0xD98,0x5D,{1884,1885}},{0,0,0,36,0,0,0xE,0x8C,0x64,{672,673}},{0,0,0,32,0,0,0x6,0x85,0x71,{1886,1887}},{0,0,0,96,0,0,0x1,0x2BC,0x33A,{682,683}},{0,0,0,30,0,0,0,0xC8,0x6A,{1888,1889}},{0,0,0,96,0,0,0x8,0x92,0x99,{1890,1891}},{0,0,0,60,0,0,0xC,0x222,0x236,{1892,1893}},{0,0,0,0,0,0,0,0x3598,0x438,{1894,1895}},{0,0,0,0,0,0,0xC,0x9C40,0xE2,{1896,1897}},{0,0,0,0,0,0,0,0x1F32,0x92,{1898,1899}},{0,0,0,0,0,0,0xC,0x9C40,0x258,{1900,1901}},{0,0,0,0,0,0,0x6,0x9C40,0x56,{344,345}},{0,0,0,0,0,0,0x8,0x3CD,0x474,{1902,1903}},{0,0,0,0,0,0,0x6,0x1C84,0x2216,{1904,1905}},{0,0,0,0,0,0,0,0xB96,0x84E,{1906,1907}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{1908,1909}},{0,0,0,0,0,0,0,0x9C40,0x5D,{1910,1911}},{0,0,0,0,0,0,0xE,0x9C40,0x132,{1912,1913}},{0,0,0,0,0,0,0,0x150A,0x139,{1914,1703}},{0,0,0,0,0,0,0,0x9C40,0x50,{1915,1916}},{0,0,0,0,0,0,0,0x931,0x64,{1917,1918}},{0,0,0,0,0,0,0,0x9C40,0x78,{1919,1920}},{0,0,0,0,0,0,0xA,0x46D5,0x64,{1921,1922}},{0,0,0,0,0,0,0x8,0x47CC,0x189,{1923,1924}},{0,0,0,0,0,0,0xE,0x23FD,0x71,{1925,1926}},{0,0,0,0,0,0,0x6,0x9C40,0x2D6,{1927,1928}},{0,0,0,0,0,0,0xA,0x9C40,0xC1,{1929,1930}},{0,0,0,0,0,0,0,0x445,0x146,{1931,1932}},{0,0,0,0,0,0,0,0xE81,0x5D,{1933,1934}},{0,0,0,0,0,0,0x8,0x49C,0x2C9,{1935,1936}},{0,0,0,0,0,0,0xC,0xD2D,0x71,{1937,1938}},{0,0,0,0,0,0,0xE,0x9C40,0x2D0,{1939,1940}},{0,0,0,0,0,0,0xE,0x154,0x2A1,{1941,1942}},{0,0,0,0,0,0,0,0x4A2,0x564,{1943,1456}},{0,0,0,0,0,0,0xE,0x9C40,0x2DD,{1944,1945}},{0,0,0,0,0,0,0xE,0x9C40,0x585,{1946,1947}},{0,0,0,0,0,0,0xE,0x9C40,0xBA,{1757,1948}},{0,0,0,0,0,0,0xE,0x9C40,0x92,{1759,1949}},{0,0,0,0,0,0,0xE,0x9C40,0x175,{1950,1951}},{0,0,0,0,0,0,0xC,0x9C40,0x17C,{1952,1953}},{0,0,0,0,0,0,0xE,0x9C40,0xDC,{1954,1955}},{0,0,0,0,0,0,0xE,0x9C40,0x14D,{1769,1956}},{0,0,0,0,0,0,0,0x9C40,0xC1,{1780,1957}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{1784,1958}},{0,0,0,0,0,0,0xE,0x9C40,0xD5,{1786,1959}},{0,0,0,0,0,0,0,0x9C40,0x71,{1790,1960}},{0,0,0,0,0,0,0xE,0x9C40,0x236,{1961,1962}},{0,0,0,0,0,0,0xC,0x24A,0x2AE,{1963,1964}},{0,0,0,0,0,0,0,0x9C40,0x17C,{1965,1966}},{0,0,0,0,0,0,0xE,0x4732,0x64,{1967,1968}},{0,0,0,0,0,0,0xC,0x34E,0xDC,{1969,1970}},{0,0,0,0,0,0,0,0x9C40,0x6A,{1971,1972}},{0,0,0,0,0,0,0xD,0x9C40,0xD5,{1973,1974}},{0,0,0,0,0,0,0xE,0x9C40,0x56,{1817,1975}},{0,0,0,0,0,0,0xE,0x9C40,0x535,{1976,1977}},{0,0,0,0,0,0,0,0x9C40,0x4D1,{1978,1979}},{0,0,0,0,0,0,0,0x9C40,0x891,{1980,1981}},{0,0,0,0,0,0,0,0x259A,0x14C1,{1982,1983}},{0,0,0,0,0,0,0xC,0x9C40,0x549,{1984,1985}},{0,0,0,0,0,0,0xE,0x9C40,0x696,{1986,1987}},{0,0,0,0,0,0,0xE,0x26DA,0xB11,{1988,1989}},{0,0,0,0,0,0,0xB,0x9C40,0x556,{1837,1990}},{0,0,0,0,0,0,0,0x3C6,0x146,{1991,1992}},{0,0,0,0,0,0,0xC,0x9C40,0xADC,{1993,1994}},{0,0,0,0,0,0,0xE,0x228E,0x55D,{1995,1996}},{0,0,0,0,0,0,0xE,0x9C40,0xAC8,{1997,1998}},{0,0,0,0,0,0,0,0x9C40,0x56A,{1999,2000}},{0,0,0,0,0,0,0,0x272,0x64,{2001,2002}},{0,0,0,0,0,0,0xC,0xE38,0x139,{2003,2004}},{0,0,0,0,0,0,0xA,0x9C40,0x85,{2005,2006}},{0,0,0,0,0,0,0x8,0x9C40,0x841,{2007,2008}},{0,0,0,0,0,0,0x4,0x9C40,0x6A4,{2009,2010}},{0,0,0,0,0,0,0x8,0x9C40,0x46D,{2011,2012}},{0,0,0,0,0,0,0xC,0x9C40,0x876,{2013,2014}},{0,0,0,0,0,0,0xA,0xDE1,0x29FA,{2015,2016}},{0,0,0,0,0,0,0xE,0x751,0x8B9,{2017,2018}},{0,0,0,0,0,0,0xA,0x9C40,0x945,{2019,2020}},{0,0,0,0,0,0,0xC,0x2C09,0xB2C,{2021,2022}},{0,0,0,0,0,0,0x8,0x9C40,0x988,{2023,2024}},{0,0,0,0,0,0,0x6,0x47A,0x92,{798,799}},{0,0,0,35,0,0,0,0x244,0x71,{2025,2026}},{0,0,0,77,0,0,0,0x118,0x85,{1991,1992}},{0,0,0,72,0,0,0,0x118,0x85,{1991,1992}},{0,0,0,0,0,0,0x8,0x2314,0x189,{0,1}},{0,0,0,0,0,0,0x8,0x23E2,0x189,{2,3}},{0,0,0,0,0,0,0x8,0x23F0,0x2D0,{4,5}},{0,0,0,0,0,0,0x6,0x245A,0x190,{6,7}},{0,0,0,0,0,0,0x8,0x1C8A,0x459,{10,11}},{0,0,0,0,0,0,0xA,0x23FD,0xE2,{12,13}},{0,0,0,0,0,0,0x2,0x1CA5,0x445C,{16,17}},{0,0,0,0,0,0,0x2,0x938,0xACE,{18,19}},{0,0,0,0,0,0,0x4,0x26C,0x2D6,{24,25}},{0,0,0,0,0,0,0xC,0x1220,0x1589,{26,27}},{0,0,0,0,0,0,0x4,0x4BD,0x56A,{28,29}},{0,0,0,0,0,0,0x6,0x11EA,0xE2,{52,53}},{0,0,0,0,0,0,0x8,0x9C40,0x8C,{56,57}},{0,0,0,0,0,0,0x8,0x9C40,0x42,{72,73}},{12,12,0,0,0,0,0xA,0x27A9,0x6D2,{2027,2028}},{12,12,0,0,0,0,0xA,0x2731,0x5F6,{2029,2028}},{12,12,0,0,0x3,0,0x108,0x24B1,0x5FD,{2030,2031,2032,2033} },{12,12,0,0,0x3,2,0x606,0x2975,0x661,{2034,2035,2036,2037} },{0,12,0,0,0,0,0x6,0x9C40,0xF0,{2038,2039}},{12,12,0,0,0x3,0,0x906,0x25D6,0x30C,{2040,2041,2042,2043} },{12,12,0,0,0,0,0x6,0x2788,0x625,{2044,2045}},{12,12,0,0,0,0,0xC,0x1E00,0xD5,{2046,2047}},{0,12,0,0,0,0,0x8,0x1CD4,0x8C0,{2048,2049}},{0,12,0,0,0,0,0x9,0x2CE5,0x37F6,{2050,2051}},{12,12,0,0,0,0,0,0x146A,0x1874,{2052,2053}},{12,12,0,0,0,0,0x1,0x2918,0xB82,{2054,2055}},{12,12,0,0,0,0,0x1,0x4B6,0x55D,{2056,2057}},{12,12,0,0,0,0,0x8,0x751,0xC65,{2058,2059}},{0,12,0,0,0,0,0xA,0x1205,0x1568,{2060,2061}},{12,12,0,0,0,0,0x8,0xEB6,0xBFA,{2062,2063}},{12,12,0,0,0,0,0x7,0x9C40,0x24A,{2064,2065}},{0,12,0,0,0,0,0x7,0x9C40,0x3A5,{2066,2067}},{0,12,0,0,0x3,10,0,0x9C40,0x17C,{2068,2069,2070,2071} },{12,12,0,0,0,0,0x909,0x9C40,0x60A,{2072,2073}},{12,12,0,0,0,0,0x6,0x9C40,0x189,{2074,2075}},{12,12,0,0,0x3,-3,0xA0A,0x9C40,0x196,{2076,2077,2078,2079} },{12,24,0,0,0,0,0x808,0x9C40,0x19D,{2080,2081}},{12,12,0,0,0x3,1,0x8,0x9C40,0x33A,{2082,2083,2082,2083} },{12,12,0,0,0,0,0,0x47B1,0x2D0,{2084,2085}},{12,12,0,0,0,0,0xA,0x145D,0xC51,{2086,2087}},{12,12,0,0,0,0,0xA,0x5A0,0xE9,{2088,2089}},{12,12,0,0,0,0,0x2,0x13A2,0xF6,{2090,2091}},{12,12,0,0,0,0,0,0x143C,0x341,{2092,2093}},{12,12,0,0,0,0,0xA,0x9C40,0x5D,{2094,2095}},{12,12,0,0,0,0,0xC,0x9C40,0x5D,{2096,2095}},{12,12,0,0,0,0,0,0x2940,0xFD,{2097,2098}},{12,12,0,0,0,0,0,0xA99,0x1B8,{2099,2100}},{12,12,0,0,0,0,0xA,0x264E,0xA0,{2101,2102}},{12,12,0,0,0,0,0x8,0x9C40,0x7E,{2103,2104}},{0,12,0,0,0x3,-2,0,0x9C40,0x208,{2105,2106,2107,2108} },{0,12,0,0,0,0,0xD00,0x9C40,0x104,{2109,2110}},{12,12,0,0,0,0,0x8,0x2918,0x32D,{2111,2112}},{12,12,0,0,0,0,0xA,0xA78,0x1B8,{2113,2114}},{12,12,0,0,0x3,-10,0xA0A,0x9C40,0xF0,{2115,2116,2115,2116} },{12,12,0,0,0,0,0x6,0x9C40,0x23D,{2117,2118}},{12,12,0,0,0,0,0x2,0x9C40,0x320,{2119,2120}},{0,12,0,0,0,0,0x6,0x9C40,0x161,{2121,2122}},{12,12,0,0,0,0,0x6,0x9C40,0x578,{2123,2124}},{0,12,0,0,0x3,11,0x606,0x9C40,0x5A0,{2125,2126,2127,2128} },{12,12,0,0,0,0,0xE,0x9C40,0xAD,{2129,2130}},{12,12,0,0,0,0,0,0x9C40,0x6AA,{2131,2132}},{12,12,0,0,0,0,0xA,0x1270,0xC58,{2133,2134}},{12,12,0,0,0x3,-8,0x604,0x9C40,0x618,{2135,2136,2137,2138} },{12,12,0,0,0x3,5,0x604,0x9C40,0x55D,{2139,2140,2141,2142} },{0,0,0,0,0x3,-5,0xA0A,0x9C40,0x2210,{2143,2144,2143,2144} },{12,12,0,0,0x3,4,0x800,0x9C40,0x9F2,{2145,2146,2147,2148} },{12,12,0,0,0x3,10,0,0x9C40,0xB0A,{2149,2150,2151,2152} },{12,0,0,0,0,0,0xC00,0x9C40,0x2F1,{2153,2154}},{12,12,0,0,0,0,0,0x9C40,0x66E,{2155,2156}},{-12,0,0,0,0x3,0,0x202,0x8FC,0x9CA,{2157,2158,2159,2160} },{12,12,0,0,0,0,0xA,0x9C40,0x168,{2161,2162}},{12,12,0,0,0,0,0xE,0x9C40,0x64,{2163,2164}},{12,12,0,0,0,0,0xC,0x9C40,0x1ED,{2165,2166}},{12,12,0,0,0,0,0xC,0x2925,0x1D9,{2167,2168}},{12,12,0,0,0x3,1,0xE0E,0x9C40,0x182,{2169,2170,2171,2170} },{12,12,0,0,0x3,3,0xE0A,0x9C40,0x17C,{2172,2162,2173,2174} },{12,12,0,0,0x3,6,0xC0C,0x9C40,0xC8,{2175,2176,2177,2178} },{12,12,0,0,0x3,6,0xA0C,0x9C40,0x99,{2179,2180,2181,2180} },{12,12,0,0,0,0,0x6,0x9C40,0x1AA,{2182,2183}},{12,12,0,0,0,0,0x6,0x9C40,0x1B8,{2184,2185}},{12,12,0,0,0,0,0xA,0x9C40,0x5D,{2186,2187}},{12,12,0,0,0,0,0xA,0x9C40,0x56,{2188,2189}},{12,12,0,0,0,0,0x6,0x9C40,0xE9,{2190,2191}},{12,12,0,0,0,0,0xA,0x9C40,0xF0,{2192,2193}},{12,12,0,0,0,0,0xA,0x9C40,0x5D,{2194,2195}},{12,12,0,0,0,0,0x8,0x9C40,0xF6,{2196,2197}},{12,12,0,0,0,0,0xE,0x9C40,0x64,{2198,2199}},{12,12,0,0,0,0,0x8,0x9C40,0x6A,{2200,2201}},{12,12,0,0,0,0,0xB,0x9C40,0x16E,{2202,2203}},{12,12,0,0,0,0,0xE09,0x9C40,0xF6,{2204,2205}},{12,12,0,0,0,0,0xE,0x9C40,0x14D,{2206,2207}},{0,12,0,0,0,0,0xE,0x9C40,0x5D,{2208,2209}},{12,12,0,0,0,0,0xB,0x9C40,0x320,{2210,2211}},{12,12,0,0,0,0,0xB,0x9C40,0x29A,{2212,2213}},{12,12,0,0,0x3,2,0,0x9C40,0x33A,{2214,2215,2216,2217} },{12,12,0,0,0x3,-1,0x808,0x9C40,0x334,{2218,2219,2220,2221} },{12,12,0,0,0,0,0xE,0x9C40,0x189,{2222,2223}},{12,12,0,0,0x3,2,0xE0E,0x9C40,0x5D,{2224,2225,2226,2227} },{12,12,0,0,0,0,0x8,0x9C40,0x5D,{2228,2229}},{12,12,0,0,0x3,-6,0x808,0x9C40,0xAA6,{2230,2231,2232,2233} },{12,7,0,0,0x3,-3,0xA0A,0x9C40,0xBAA,{2234,2235,2236,2237} },{0,12,0,0,0,0,0x9,0x9C40,0x17C,{2238,2239}},{12,12,0,0,0x3,2,0x909,0x9C40,0x51A,{2240,2241,2242,2243} },{12,12,0,0,0x3,2,0xC0C,0x9C40,0x15A,{2244,2245,2244,2245} },{12,12,0,0,0x3,11,0x101,0x9C40,0x722,{2246,2247,2248,2249} },{12,12,0,0,0,0,0x7,0x9C40,0x722,{2250,2251}},{12,12,0,0,0,0,0xE,0x17B2,0x1518,{2252,2253}},{12,12,0,0,0x3,-2,0,0x9C40,0x229,{2105,2106,2107,2108} },{12,12,0,0,0,0,0x8,0x9C40,0x150A,{2254,2255}},{12,12,0,0,0,0,0xB,0x9C40,0x12C,{2256,2257}},{0,7,0,0,0x3,8,0xA0A,0x9C40,0x47A,{2258,2259,2260,2261} },{-12,12,0,0,0,0,0,0xA64,0xAE2,{2262,2263}},{12,12,0,0,0x3,-2,0,0x9C40,0x4C4,{2264,2265,2266,2267} },{12,12,0,0,0x3,-12,0,0x2321,0x25BC,{2268,2269,2268,2270} },{0,12,0,0,0,0,0,0x9C40,0x1040,{2271,2272}},{12,12,0,0,0,0,0,0x9C40,0x16D6,{2273,2274}},{12,12,0,0,0,0,0xA,0x2572,0x2AD0,{2275,2276}},{12,12,0,0,0,0,0x2,0x9C40,0x71C,{2277,2278}},{12,12,0,0,0,0,0xF04,0x995,0x5F0,{2279,2280}},{12,12,0,0,0x3,0,0,0xB40,0x1360,{2281,2282,2283,2284} },{12,12,0,0,0,0,0x8,0x47D2,0xADC,{2285,2286}},{0,12,0,0,0,0,0x6,0x4A2,0x8AC,{2287,2288}},{12,12,0,0,0,0,0xA,0x9C40,0x189,{2289,2290}},{12,12,0,0,0,0,0xE,0x9C40,0x28D,{2291,2292}},{12,12,0,0,0,0,0x8,0x9C40,0x196,{2293,2294}},{5,12,0,0,0,0,0x1,0x21EE,0x2209,{2295,2296}},{12,12,0,0,0,0,0x4,0x125,0xF6,{2297,2298}},{12,12,0,0,0,0,0x6,0x9C40,0x58C,{2299,2300}},{12,12,0,0,0,0,0x6,0x1AA,0xA0,{2301,2302}},{0,12,0,0,0,0,0,0x3C0,0x46D,{2303,2304}},{0,12,0,0,0,0,0,0x236,0x189,{2305,2306}},{12,12,0,0,0,0,0,0xE9C,0x189,{2307,2308}},{0,12,0,0,0,0,0xE,0x909,0x5D,{2309,2310}},{12,12,0,0,0,0,0xE,0x5BA,0xF6,{2311,2312}},{12,12,0,0,0,0,0xE,0x5DC,0x58C,{2313,2314}},{0,12,0,0,0,0,0xE,0x1499,0x1589,{2315,2316}},{0,12,0,0,0,0,0x7,0x9C40,0xC36,{2317,2318}},{0,12,0,0,0,0,0x8,0x9C40,0x189,{2319,2320}},{-24,0,0,29,0,0,0x8,0x9C40,0x4A81,{898,2321}},{12,12,0,65,0,0,0xE,0x9C40,0x189C,{2322,2323}},{0,12,0,0,0,0,0xE,0x272,0x2DD,{2324,2325}},{12,12,0,38,0,0,0,0x1CC,0x21C,{684,685}},{12,12,0,25,0,0,0x6,0x272,0x2E4,{2326,2327}},{12,12,0,83,0,0,0x6,0xB4,0xA0,{2328,2329}},{12,12,0,32,0,0,0xE,0xF6,0x140,{2330,2331}},{12,12,0,60,0,0,0xF,0x9C40,0x56,{2332,2333}},{12,12,0,36,0,0,0xE,0x11E,0x6A,{2334,2335}},{0,0,0,27,0,0,0x7,0x23D,0xC8,{2336,2337}},{12,12,0,88,0,0,0xF,0x154,0x196,{695,696}},{0,0,0,31,0,0,0x7,0x2B5,0x154,{2338,2339}},{12,12,0,88,0,0,0xF,0x8C,0x64,{697,698}},{12,12,0,21,0,0,0x3,0x1F4,0x189,{2340,2341}},{12,12,0,79,0,0,0xE,0x8F5,0xBCC,{699,700}},{12,12,0,26,0,0,0x3,0x1F4,0x190,{2340,2341}},{12,12,0,28,0,0,0x3,0x1F4,0x189,{2340,2341}},{12,12,0,60,0,0,0xE,0x924,0xE16,{2342,2343}},{12,12,0,32,0,0,0x3,0x19D,0x146,{2340,2341}},{12,12,0,60,0,0,0xA,0x326,0x3AC,{2344,2345}},{12,12,0,96,0,0,0xE,0x46D,0x60A,{2346,2347}},{12,12,0,72,0,0,0xE,0x49C,0x729,{2348,2349}},{12,12,0,79,0,0,0xF,0x11E,0xF6,{2350,2351}},{12,12,0,69,0,0,0xE,0x4A9,0xE59,{2352,2353}},{12,12,0,71,0,0,0x6,0x154,0xB4,{2354,730}},{0,0,0,22,0,0,0xA,0x758,0x8C0,{2355,2356}},{12,12,0,60,0,0,0x4,0x1B1,0x1F4,{723,724}},{12,12,0,54,0,0,0x4,0x1E6,0x23D,{723,724}},{12,12,0,72,0,0,0,0xF6,0x118,{725,726}},{12,12,0,67,0,0,0,0xF0,0x118,{725,726}},{12,12,0,60,0,0,0,0xF6,0x118,{725,726}},{12,12,0,55,0,0,0,0xF6,0x118,{2357,726}},{12,12,0,48,0,0,0,0x118,0x140,{2357,726}},{12,12,0,77,0,0,0x8,0x154,0xAD,{729,730}},{12,12,0,72,0,0,0x8,0x154,0xAD,{729,730}},{12,12,0,60,0x4,0,0xA,0x28,0x5D,{2358,2359}},{12,12,0,49,0,0,0xA,0x28,0x5D,{2360,2361}},{12,12,0,73,0,0,0x6,0xA6,0x64,{2362,2302}},{12,12,0,68,0,0,0x6,0xA6,0x64,{2362,2302}},{12,12,0,61,0,0,0x6,0xC8,0x6A,{2362,2302}},{12,12,0,60,0x4,0,0xA,0x28,0x5D,{2363,2359}},{12,12,0,90,0,0,0x8,0xC1,0x5D,{743,744}},{12,12,0,90,0,0,0x8,0x319,0x6A,{745,746}},{12,12,0,0,0,0,0xA,0x9C40,0x161,{2364,2365}},{12,12,0,0,0,0,0x6,0x9C40,0x215,{2366,2367}},{12,12,0,0,0,0,0xC,0x9C40,0x140,{2368,2369}},{12,12,0,0,0,0,0x8,0x280D,0x1B1,{2370,2371}},{0,12,0,0,0x3,0,0xD00,0x9C40,0xF6,{2372,2110,2373,2374} },{12,12,0,0,0,0,0xC,0x9C40,0x201,{2375,2376}},{12,12,0,36,0x3,0,0xC00,0x175,0x190,{2377,2378,2379,2380} },{12,12,0,69,0,0,0xE,0x4A2,0xE52,{2381,2353}},{12,12,0,60,0,0,0xB,0x9C40,0,{2382,2383}},{12,12,0,0,0,0,0x2,0x9C40,0x5D,{2384,2095}},{0,12,0,0,0,0,0x2,0x9C40,0x140,{2385,63}},{0,0,0,22,0,0,0xA,0x758,0x8C0,{2386,2387}},{0,0,0,0,0,0,0x808,0x9C40,0x1AA,{1072,1073}},{0,0,0,0,0,0,0x808,0x9C40,0x1B8,{778,779}},{0,0,0,0,0,0,0x606,0x9C40,0x64,{2388,2389}},{0,0,0,0,0,0,0x808,0x9C40,0xF0,{783,784}},{0,0,0,0,0,0,0xA0A,0xD70,0xE2,{785,786}},{0,0,0,0,0,0,0,0xF42,0x57E,{787,2390}},{0,0,0,0,0,0,0x202,0x3584,0x4441,{789,790}},{0,0,0,0,0,0,0,0x23F0,0x2DD,{791,582}},{0,0,0,0,0,0,0x404,0x26C,0x2D6,{356,357}},{0,0,0,0,0,0,0xC0C,0x1220,0x1582,{2391,2392}},{0,0,0,0,0,0,0x404,0x4BD,0x56A,{362,2393}},{0,0,0,0,0,0,0x202,0x9C40,0x132,{2394,31}},{0,0,0,0,0,0,0x202,0x9C40,0xC1,{2395,33}},{0,0,0,0,0,0,0x404,0x9C40,0xC1,{2396,2397}},{0,0,0,0,0,0,0x404,0x9C40,0x84E,{36,2398}},{0,0,0,0,0,0,0,0x9C40,0x916,{2399,2400}},{0,0,0,0,0,0,0xC0C,0x9C40,0x6A,{40,2401}},{0,0,0,0,0,0,0xA0A,0x9C40,0x19D,{2402,43}},{0,0,0,0,0,0,0x606,0x9C40,0x189,{44,2403}},{0,0,0,0,0,0,0xC0C,0xFC1,0xE9,{46,2404}},{0,0,0,0,0,0,0x606,0x11EA,0x1561,{52,792}},{0,0,0,0,0,0,0x808,0x9C40,0x8C,{776,777}},{0,0,0,0,0,0,0x808,0x47CC,0xE2,{60,2405}},{0,0,0,0,0,0,0x202,0x9C40,0x140,{62,2406}},{0,0,0,0,0,0,0x808,0x21AC,0x5D,{793,71}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{2407,80}},{0,0,0,0,0,0,0,0x9C40,0x189,{2408,94}},{0,0,0,0,0,0,0xA0A,0x9C40,0x506,{2409,2410}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5A0,{97,2410}},{0,0,0,0,0,0,0,0x9C40,0x488,{98,2411}},{0,0,0,0,0,0,0,0x9C40,0x10A,{2412,2413}},{0,0,0,0,0,0,0xC0C,0x9C40,0x47A,{2414,2415}},{0,0,0,0,0,0,0x808,0x459,0x55D,{2416,2417}},{0,0,0,0,0,0,0xC0C,0x9C40,0x139,{110,2418}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{2419,117}},{0,0,0,0,0,0,0,0x9C40,0xCE,{2420,794}},{0,0,0,0,0,0,0x808,0x9C40,0x64,{122,2421}},{0,0,0,0,0,0,0x808,0x9C40,0x92,{124,498}},{0,0,0,0,0,0,0xA0A,0x9C40,0x92,{126,2422}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{128,495}},{0,0,0,0,0,0,0x202,0x9C40,0x71,{2423,142}},{0,0,0,0,0,0,0,0x9C40,0x294,{145,2424}},{0,0,0,0,0,0,0,0x9C40,0x26C,{2425,2426}},{0,0,0,0,0,0,0xA0B,0x9C40,0x189,{2427,2428}},{0,0,0,0,0,0,0xA0B,0x9C40,0x189,{2429,2430}},{0,0,0,0,0,0,0,0x9C40,0x71,{159,2431}},{0,0,0,0,0,0,0x202,0x9C40,0x190,{2432,2433}},{0,0,0,0,0,0,0,0x9C40,0x2BC,{165,2434}},{0,0,0,0,0,0,0x202,0x9C40,0x585,{2435,2436}},{0,0,0,0,0,0,0xA0A,0x9C40,0x1205,{2437,2438}},{0,0,0,0,0,0,0xC0C,0x9C40,0x2BC,{2439,2440}},{0,0,0,0,0,0,0,0x9C40,0x1172,{2441,2442}},{0,0,0,0,0,0,0x808,0x9C40,0x495,{177,2443}},{0,0,0,0,0,0,0,0x9C40,0x146,{2444,180}},{0,0,0,0,0,0,0x202,0x1CE8,0x224C,{185,2445}},{0,0,0,0,0,0,0xC0C,0x9C40,0x28D,{191,2446}},{0,0,0,0,0,0,0,0x2349,0x564,{2447,2448}},{0,0,0,0,0,0,0x606,0x9C40,0x14A6,{2449,2450}},{0,0,0,0,0,0,0x808,0x3750,0x41E9,{602,2451}},{0,0,0,0,0,0,0xE0E,0xBCC,0,{231,1133}},{0,0,0,84,0,0,0xE0E,0x542,0x639,{711,712}},{0,0,0,24,0,0,0xC0C,0x73D,0x876,{2452,277}},{0,0,0,44,0,0,0xE0E,0xD5,0xAD,{2453,305}},{0,0,0,40,0,0,0xE0E,0x24A,0x190,{2454,307}},{0,0,0,60,0,0,0,0x3FC,0x445,{2455,2456}},{0,0,0,0,0,0,0x808,0x1A18,0x85,{2457,2458}},{0,0,0,0,0,0,0x202,0x1CAC,0x2238,{2459,2460}},{0,0,0,0,0,0,0x606,0x1220,0x157C,{2461,2462}},{0,0,0,0,0,0,0x607,0x9C40,0x4F9,{2463,2464}},{0,0,0,0,0,0,0xC0C,0x1BD6,0xC1,{2465,2466}},{0,0,0,0,0,0,0,0x9C40,0x2BC,{2467,2468}},{0,0,0,0,0,0,0,0x466,0x542,{2469,2470}},{0,0,0,0,0,0,0x202,0xF21,0x1165,{2471,2472}},{0,0,0,0,0,0,0x606,0x820,0x110E,{89,2473}},{0,0,0,0,0,0,0x606,0x9C40,0x2AE,{2474,2475}},{0,0,0,0,0,0,0x606,0x9C40,0x52E,{2476,2477}},{0,0,0,0,0,0,0xE0E,0x9C40,0x320,{2478,2479}},{0,0,0,0,0,0,0,0x9C40,0x585,{2480,2481}},{0,0,0,0,0,0,0xE0E,0x7D0,0x98E,{2482,2483}},{0,0,0,0,0,0,0xC0C,0x9C40,0xCE,{2484,2485}},{0,0,0,0,0,0,0xC0C,0x9C40,0x17C,{2486,2487}},{0,0,0,0,0,0,0xC0C,0x9C40,0x146,{2488,111}},{0,0,0,0,0,0,0xA0A,0x9C40,0x182,{2489,2490}},{0,0,0,0,0,0,0xA0A,0x9C40,0xDC,{2491,2492}},{0,0,0,0,0,0,0x808,0x9C40,0x6A,{2493,131}},{0,0,0,0,0,0,0,0x9C40,0x7E,{2494,2495}},{0,0,0,0,0,0,0x404,0x9C40,0xDC,{2496,2497}},{0,0,0,0,0,0,0,0x9C40,0x6A,{2498,2499}},{0,0,0,0,0,0,0,0x9C40,0x8C,{2500,2501}},{0,0,0,0,0,0,0x808,0x9C40,0xA85,{2502,2503}},{0,0,0,0,0,0,0x606,0x924,0xAD5,{2504,2505}},{0,0,0,0,0,0,0xE0E,0x15A,0x189,{2506,2507}},{0,0,0,35,0,0,0x606,0x2C9,0x341,{2508,2509}},{0,0,0,49,0,0,0xA0A,0xAD,0xC1,{2510,2511}},{0,0,0,48,0,0,0xE0E,0x11E,0x146,{2506,2507}},{0,0,0,58,0,0,0xE0E,0xAD,0xC1,{731,2512}},{0,0,0,60,0,0,0xE0E,0x11E,0x146,{2506,2507}},{0,0,0,47,0,0,0x404,0x3D4,0x481,{2513,2514}},{0,0,0,60,0,0,0xE0E,0x92,0x99,{2515,259}},{0,0,0,49,0,0,0x404,0x3C6,0x46D,{2513,2514}},{0,0,0,72,0,0,0xE0E,0x1ED,0x236,{2516,2517}},{0,0,0,51,0,0,0x404,0x3C0,0x46D,{2513,2514}},{0,0,0,84,0,0,0xA0A,0x52E,0x632,{2518,2519}},{0,0,0,54,0,0,0x404,0x3C0,0x46D,{2513,2514}},{0,0,0,57,0,0,0x404,0x3B9,0x46D,{2513,2514}},{0,0,0,72,0,0,0xE0E,0x61E,0x736,{2520,2521}},{0,0,0,60,0,0,0x404,0x3C0,0x474,{2513,2514}},{0,0,0,36,0,0,0xE0E,0xA64,0xC44,{266,2522}},{0,0,0,93,0,0,0xA0A,0xE9,0xF6,{2523,2524}},{0,0,0,72,0,0,0xE0E,0x3C0,0x46D,{272,2525}},{0,0,0,84,0,0,0xE0E,0x550,0x646,{237,2526}},{0,0,0,36,0,0,0xC0C,0x53C,0x639,{2527,2528}},{0,0,0,64,0,0,0xE0E,0xDC,0xAD,{296,2529}},{0,0,0,68,0,0,0x606,0x78,0x244,{2530,2531}},{0,0,0,0,0,0,0x606,0x11B5,0x862,{2532,2533}},{0,0,0,0,0,0,0x606,0x1255,0x64,{1180,1181}},{0,0,0,0,0,0,0xE0E,0x8FC,0xABA,{1182,1183}},{0,0,0,0,0,0,0x202,0x9C40,0x140,{1184,1185}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{1186,1187}},{0,0,0,0,0,0,0x202,0x1686,0x114A,{1188,1189}},{0,0,0,0,0,0,0x808,0x744,0x89E,{1192,1193}},{0,0,0,0,0,0,0x202,0x9C40,0x47A,{2534,2535}},{0,0,0,0,0,0,0,0x9C40,0x5D,{1090,2536}},{0,0,0,0,0,0,0x404,0x9C40,0x1E6,{1199,1200}},{0,0,0,0,0,0,0x808,0x805,0x556,{1201,2537}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{1203,1204}},{0,0,0,0,0,0,0xA0A,0x2231,0x425A,{1209,1210}},{0,0,0,0,0,0,0xE0E,0x786,0x5D,{1211,1212}},{0,0,0,0,0,0,0x606,0x2079,0x88A,{904,1213}},{0,0,0,0,0,0,0xE0F,0x9C40,0x2DD,{1214,2538}},{0,0,0,0,0,0,0x808,0xCA8,0xF3C,{2539,2540}},{0,0,0,0,0,0,0x404,0x9C40,0x1E6,{1220,1200}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{2541,2542}},{0,0,0,0,0,0,0xA0A,0x370,0x5D,{1222,1223}},{0,0,0,0,0,0,0xA0A,0x9C40,0x50,{2543,2544}},{0,0,0,0,0,0,0xA0A,0x2BC,0xE9,{1228,1229}},{0,0,0,0,0,0,0x606,0x2BC,0xE9,{1230,1229}},{0,0,0,0,0,0,0x808,0x9C40,0x62C,{2545,2546}},{0,0,0,0,0,0,0,0x1E00,0x113D,{2547,2548}},{0,0,0,0,0,0,0x404,0xD5,0x52F8,{2549,1236}},{0,0,0,0,0,0,0x808,0x9C40,0x64,{979,980}},{0,0,0,0,0,0,0x808,0xF7E,0x251,{633,1238}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{1024,2550}},{0,0,0,0,0,0,0x808,0x1CAC,0x223E,{960,1165}},{0,0,0,0,0,0,0x606,0x9C40,0x862,{1239,1240}},{0,0,0,0,0,0,0x202,0x9C40,0x5D,{2551,1242}},{0,0,0,0,0,0,0xE0E,0x9C40,0x161,{2552,2553}},{0,0,0,0,0,0,0xA0A,0x640,0x8A5,{2554,2555}},{0,0,0,0,0,0,0x606,0x3C56,0x1428,{1076,2556}},{0,0,0,0,0,0,0x606,0x9C40,0x51A,{2557,2558}},{0,0,0,0,0,0,0x606,0x9C40,0xA0D,{1301,1302}},{0,0,0,0,0,0,0xE0E,0x175,0x2D6,{1252,1253}},{0,0,0,0,0,0,0xE0E,0x9C40,0x16F8,{2559,2560}},{0,0,0,0,0,0,0xE0E,0x9C40,0x125,{438,2561}},{0,0,0,0,0,0,0,0x3BF9,0x5D,{2562,2563}},{0,0,0,0,0,0,0xC0C,0x1720,0x21E1,{1262,1263}},{0,0,0,0,0,0,0xA0A,0x305,0xE2,{1264,1265}},{0,0,0,0,0,0,0xA0A,0xE81,0x1122,{2564,2565}},{0,0,0,0,0,0,0x808,0x9C40,0xCE,{2566,2567}},{0,0,0,0,0,0,0xE0E,0x9C40,0x64,{1268,1204}},{0,0,0,0,0,0,0xA0A,0xE31,0x10F4,{1176,1177}},{0,0,0,0,0,0,0xA0A,0x9C40,0x56,{1271,1272}},{0,0,0,0,0,0,0x404,0x9C40,0x5D,{1273,1274}},{0,0,0,0,0,0,0x202,0x9C40,0x6A,{1275,1145}},{0,0,0,0,0,0,0xA0B,0x9C40,0x258,{1278,2568}},{0,0,0,0,0,0,0xA0A,0xF49,0x64,{1280,1281}},{0,0,0,0,0,0,0x404,0x9C40,0x64,{1284,1285}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{1140,2569}},{0,0,0,0,0,0,0x202,0x9C40,0x5D,{2551,2570}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{1287,2571}},{0,0,0,0,0,0,0x202,0x9C40,0x452,{1289,1290}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{1123,2572}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{1295,2573}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{2574,2572}},{0,0,0,0,0,0,0xC0C,0x9C40,0x71,{1298,1130}},{0,0,0,0,0,0,0x607,0x9C40,0x175,{2575,2576}},{0,0,0,0,0,0,0xE0E,0x9C40,0x56,{2577,2578}},{0,0,0,0,0,0,0x202,0xE8E,0x1144,{1305,1306}},{0,0,0,0,0,0,0xA0A,0x9C40,0x92,{1307,1308}},{0,0,0,0,0,0,0xE0E,0x33F4,0x3F20,{2579,2580}},{0,0,0,0,0,0,0xA0A,0x495,0x578,{1311,1312}},{0,0,0,0,0,0,0xE0E,0x32C8,0x39E4,{2581,2582}},{0,0,0,0,0,0,0x404,0x3C6,0x114A,{2583,1316}},{0,0,0,0,0,0,0xE0F,0x9C40,0x826,{2584,1318}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{2585,1320}},{0,0,0,0,0,0,0xE0E,0x9C40,0x6A,{1144,2586}},{0,0,0,0,0,0,0x606,0x4DE,0x64,{1114,1323}},{0,0,0,0,0,0,0xE0E,0xE81,0x113D,{1324,1325}},{0,0,0,0,0,0,0xE0E,0x3E1,0xADC,{929,934}},{0,0,0,0,0,0,0xA0A,0x4B6,0x58C,{1328,1329}},{0,0,0,0,0,0,0xE0E,0x125,0x2D6,{929,935}},{0,0,0,0,0,0,0xE0E,0x1FA,0xAD5,{2587,710}},{0,0,0,0,0,0,0x404,0x118,0x10F4,{2588,2589}},{0,0,0,0,0,0,0x808,0x38D2,0x42E6,{2590,1332}},{0,0,0,0,0,0,0xA0A,0x146A,0x8A5,{2591,2592}},{0,0,0,0,0,0,0x808,0x3340,0x21DA,{633,2593}},{0,0,0,0,0,0,0xE0E,0x9C40,0xAE2,{631,1337}},{0,0,0,0,0,0,0x606,0x9C40,0x21DA,{1338,1339}},{0,0,0,0,0,0,0xE0E,0x201,0x258,{1346,1347}},{0,0,0,0,0,0,0x606,0x9C40,0x53AC,{1076,2594}},{0,0,0,55,0,0,0x808,0x64,0x5D,{280,924}},{0,0,0,60,0,0,0xE0E,0xDC,0xFD,{1346,1347}},{0,0,0,60,0,0,0xE0E,0xDC,0x5D,{1348,1349}},{0,0,0,41,0,0,0x808,0x6A,0x5D,{280,924}},{0,0,0,84,0,0,0xE0E,0xA0,0x64,{2595,2596}},{0,0,0,84,0,0,0xE0E,0x15A,0x730,{2595,710}},{0,0,0,48,0,0,0x808,0x64,0x5D,{280,924}},{0,0,0,49,0,0,0x808,0xAD,0x5D,{955,845}},{0,0,0,55,0,0,0xE0E,0xDC,0xF6,{1346,1347}},{0,0,0,18,0,0,0x808,0xC8,0x5D,{955,845}},{0,0,0,15,0,0,0x808,0xC8,0x5D,{955,845}},{0,0,0,17,0,0,0x808,0xC8,0x5D,{955,845}},{0,0,0,0,0x1,0,0x908,0x34F8,0x3F5,{2597,2598,2599,2600} },{0,0,0,0,0x1,0,0xB0A,0x1CE1,0x244,{2601,2602,2603,2604} },{0,0,0,0,0x1,0,0xE0E,0x2E39,0x460,{2605,2606,2607,2608} },{12,0,0,0,0x1,0,0xC0D,0x26C0,0x51A,{2609,2610,2611,2612} },{12,0,0,0,0x1,0,0x404,0x2795,0x71,{2613,2614,2615,2616} },{0,0,0,0,0x1,0,0xB0A,0x1FED,0x2579,{2617,2618,2619,2620} },{0,0,0,0,0x1,0,0x302,0x916,0xA85,{350,351,2621,2622} },{12,0,0,0,0x1,0,0xB0A,0x23E2,0x2DD,{2623,2624,2625,2626} },{0,0,0,0,0x1,0,0x706,0x9C40,0x64,{2627,2628,352,353} },{0,0,0,0,0x1,0,0xD0D,0x43E,0x4D1,{2629,362,2630,2631} },{0,0,0,0,0x1,0,0x707,0x9C40,0x6A,{2632,2633,2634,2635} },{0,0,0,0,0x1,0,0x707,0x9C40,0x104,{368,369,2636,2637} },{-12,0,0,0,0x1,0,0x908,0x9C40,0x71,{372,373,374,375} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x474,{2638,2639,2640,2641} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x488,{2642,2643,2644,2645} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x6A,{2646,410,2647,162} },{0,0,0,0,0x1,0,0,0x9C40,0x64,{2648,2649,2650,2651} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x99,{68,69,2652,2653} },{0,0,0,0,0x1,0,0x908,0x1F7C,0x5D,{70,71,2654,2655} },{0,0,0,0,0x1,0,0x706,0x1F11,0x5D,{793,71,2654,2655} },{0,0,0,0,0x1,0,0xA0A,0x1F32,0xDC,{2656,435,2657,437} },{0,0,0,0,0x1,0,0x908,0x9C40,0x294,{75,76,2658,2659} },{0,0,0,0,0x1,0,0x908,0x9C40,0x2A1,{2660,76,2658,2659} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x57E,{2661,96,95,2662} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x599,{97,96,2663,2664} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x1ED,{2665,2666,2667,2668} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x2B5,{2669,2670,100,101} },{0,0,0,0,0x1,0,0x808,0x9C40,0x2DD,{2671,2672,2673,2674} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xBA,{106,107,2675,2676} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xC1,{2677,2678,2675,2679} },{12,0,0,0,0x1,0,0xD0C,0x9C40,0x17C,{475,111,476,477} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x85,{2680,2681,2682,2683} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x64,{2684,2685,2686,2687} },{0,0,0,0,0x1,0,0x808,0x9C40,0x71,{509,2688,2689,2690} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x71,{2691,2692,138,139} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x6A,{2691,2692,140,139} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x71,{2691,2693,2694,144} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x17C,{2695,2696,2697,2698} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0xF0,{2699,2700,157,158} },{0,0,0,0,0x1,0,0x908,0x9C40,0x6A,{2701,2702,159,2703} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x92,{167,168,2704,2705} },{0,0,0,0,0x1,0,0x908,0x9C40,0x3EE,{2706,2707,2708,2709} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x42A,{2710,2711,179,180} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x398,{181,2711,2712,2713} },{0,0,0,0,0x1,0,0x100,0x9C40,0x182,{2714,184,2715,2716} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xA71,{2717,2718,2719,2720} },{0,0,0,0,0x1,0,0x908,0x1270,0x28C1,{2721,2722,189,190} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x58C,{2723,2724,2725,2726} },{0,0,0,0,0x1,0,0xF0E,0x33BE,0x578,{2727,2728,2729,2730} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x14D5,{2731,2732,2733,198} },{0,0,0,0,0x1,0,0x504,0x9C40,0xA99,{2734,2735,2736,2737} },{0,0,0,0,0x1,0,0x606,0x9C40,0x1324,{2738,2739,2740,200} },{0,0,0,0,0x1,0,0x706,0xFD5,0x585,{217,218,2741,2742} },{0,0,0,0,0x1,0,0xB0A,0xA6,0x438,{2743,2744,2745,224} },{0,0,0,0,0x1,0,0x706,0x10ED,0x156E,{225,226,2746,2747} },{0,0,0,0,0x1,0,0xF0E,0xD3A,0x7D0,{2748,2749,2750,2751} },{0,0,0,0,0x1,0,0xF0E,0xBCC,0,{2752,1133,2753,2754} },{0,0,0,0,0x1,0,0xF0E,0x5D5,0xA8C,{233,2755,2756,2757} },{0,0,0,0,0x1,0,0xF0E,0x98E,0x88A,{2758,2759,638,638} },{0,0,0,0,0x1,0,0xF0E,0x23F0,0xAD5,{244,2760,638,638} },{0,0,0,0,0x1,0,0x505,0x3C0,0xCE,{2761,638,2762,2763} },{1,0,0,0,0x1,0,0xB0A,0x35C,0x3C6,{2764,242,2765,2766} },{-36,0,0,0,0x1,0,0x908,0x9C40,0x1F4,{2767,2768,2769,2768} },{0,0,0,44,0x1,0,0x606,0x78,0x5D,{686,687,688,251} },{0,0,0,48,0x1,0,0xF0E,0x104,0x132,{252,253,2770,2771} },{0,0,0,58,0x1,0,0xF0E,0x146,0x182,{691,692,254,255} },{0,0,0,60,0x1,0,0xF0E,0xFD,0x12C,{252,253,2772,2773} },{0,0,0,48,0x1,0,0x100,0x1C5,0x215,{2774,2775,256,257} },{0,0,0,43,0x1,0,0xB0A,0xAD,0xC1,{258,259,258,259} },{0,0,0,49,0x1,0,0x100,0x1C5,0x215,{2774,2775,256,257} },{0,0,0,43,0x1,0,0xB0A,0xA6,0xAD,{258,2776,258,2776} },{0,0,0,51,0x1,0,0x100,0x1C5,0x20E,{2774,2775,256,257} },{0,0,0,43,0x1,0,0xB0A,0x701,0x819,{261,262,261,2777} },{0,0,0,54,0x1,0,0x100,0x1C5,0x215,{2774,2775,256,257} },{0,0,0,57,0x1,0,0x100,0x1C5,0x215,{2774,2775,256,257} },{0,0,0,72,0x1,0,0xE0E,0x625,0x74A,{715,716,717,718} },{0,0,0,60,0x1,0,0x100,0x1C5,0x215,{2774,2775,256,257} },{0,0,0,70,0x1,0,0x706,0x319,0x3AC,{2778,2779,2780,2781} },{0,0,0,60,0x1,0,0xE0E,0x3CD,0x47A,{703,704,705,706} },{0,0,0,36,0x1,0,0x706,0x481,0x549,{2778,2782,2783,2784} },{0,0,0,65,0x1,0,0,0x125,0x146,{2785,2786,2787,2788} },{0,0,0,84,0x1,0,0xE0E,0x549,0x3B2,{2789,2790,2791,2792} },{0,0,0,59,0x1,0,0x908,0x196,0x1CC,{274,2793,2794,275} },{0,0,0,84,0x1,0,0xE0E,0x632,0x751,{2795,716,2796,718} },{0,0,0,35,0x1,0,0xD0C,0x556,0x62C,{2797,2528,2797,277} },{0,0,0,44,0x1,0,0x706,0x39E,0x139,{2798,2799,2780,2800} },{0,0,0,67,0x1,0,0x706,0xF0,0x111,{2801,2802,2803,2804} },{0,0,0,66,0x1,0,0x706,0x140,0x265,{2805,2806,2807,2808} },{0,0,0,59,0x1,0,0x706,0xA0,0x140,{282,283,2809,2810} },{0,0,0,51,0x1,0,0x706,0x17C,0x488,{2811,327,2811,2812} },{0,0,0,45,0x1,0,0x706,0x104,0x258,{2813,287,2813,2814} },{0,0,0,71,0x1,0,0xF0E,0x1B1,0x1E6,{2815,2327,2815,2816} },{0,0,0,60,0x1,0,0xF0E,0x118,0x5D,{290,291,2817,2818} },{0,0,0,58,0x1,0,0xF0E,0x1FA,0x251,{2819,295,2820,2821} },{0,0,0,53,0x1,0,0xF0E,0x1E6,0x23D,{2822,2823,2822,2824} },{0,0,0,64,0x1,0,0xF0E,0xDC,0xB4,{747,297,2825,2826} },{0,0,0,71,0x1,0,0xF0E,0x6A,0x78,{2827,2828,2829,2830} },{0,0,0,61,0x1,0,0xF0E,0x3DA,0x46D,{2831,2832,2833,2834} },{0,0,0,61,0x1,0,0xF0E,0x6D2,0x812,{2835,2836,2835,2837} },{0,0,0,48,0x1,0,0xD0C,0x118,0x146,{737,738,739,739} },{0,0,0,48,0x1,0,0xD0C,0x17C,0x146,{737,738,740,740} },{0,0,0,69,0x1,0,0x100,0x78,0x140,{310,309,310,2838} },{0,0,0,68,0x1,0,0x100,0x78,0x140,{310,311,310,2839} },{0,0,0,63,0x1,0,0x100,0x8C,0x1B1,{310,311,310,2839} },{0,0,0,74,0x1,0,0x908,0x38A,0x3B9,{2840,2841,2842,2843} },{0,0,0,60,0x1,0,0xB0A,0x416,0x460,{2844,2845,2846,2847} },{0,0,0,80,0x1,0,0x908,0xD5,0xF0,{316,2848,316,2849} },{0,0,0,64,0x1,0,0x908,0x8E8,0xAE2,{318,2850,2851,2852} },{0,0,0,69,0x1,0,0xF0E,0x78,0x56,{2853,2854,2853,2855} },{0,0,0,55,0x1,0,0xF0E,0x5DC,0x244,{323,748,749,750} },{0,0,0,75,0x1,0,0xA0B,0x618,0x3AC,{2856,2857,2858,2859} },{0,0,0,68,0x1,0,0x908,0x78,0x140,{2860,309,2860,2838} },{0,0,0,48,0x1,0,0x706,0x190,0x495,{326,327,326,2812} },{0,0,0,53,0x1,0,0x706,0x244,0x8AC,{2861,2862,2861,2862} },{0,0,0,0,0,0,0xE0F,0x9C40,0x1596,{2863,2864}},{0,0,0,0,0,0,0x808,0x9C40,0x111C,{2865,2866}},{0,0,0,0,0,0,0x404,0x11B5,0xAA6,{2867,2868}},{0,0,0,0,0,0,0x606,0x9C40,0x57E,{2869,2870}},{0,0,0,0,0,0,0,0x9C40,0x14FD,{2871,2872}},{0,0,0,0,0,0,0x808,0x805,0x556,{1201,1202}},{0,0,0,0,0,0,0x808,0xE74,0x1136,{1205,1206}},{0,0,0,0,0,0,0xC0C,0xFBA,0x1309,{1207,1208}},{0,0,0,0,0,0,0xE0F,0x9C40,0x2DD,{1214,1215}},{0,0,0,0,0,0,0x202,0x9C40,0x2C9,{1216,1217}},{0,0,0,0,0,0,0x202,0xCFE,0xAE2,{2873,2874}},{0,0,0,0,0,0,0xA0B,0x9C40,0xAE2,{2875,2876}},{0,0,0,0,0,0,0,0x9C40,0xA92,{2877,2878}},{0,0,0,0,0,0,0x808,0x2F6C,0x1590,{2879,2880}},{0,0,0,0,0,0,0x808,0x9C40,0x85,{2881,1270}},{0,0,0,0,0,0,0x808,0x9C40,0x92,{2882,2883}},{0,0,0,0,0,0,0xE0E,0x9C40,0x92,{2884,2885}},{0,0,0,0,0,0,0xA0A,0x9C40,0x2335,{2886,2887}},{0,0,0,0,0,0,0xC0C,0x9C40,0x92,{2888,2889}},{0,0,0,0,0,0,0x808,0x9C40,0x85,{2890,1270}},{0,0,0,0,0,0,0x808,0x9C40,0x85,{2891,1270}},{0,0,0,0,0,0,0,0x9C40,0xAB4,{2892,2893}},{0,0,0,0,0,0,0xE0E,0x9C40,0x161,{1243,1244}},{0,0,0,0,0,0,0x808,0x9C40,0x85,{2894,1270}},{0,0,0,0,0,0,0x808,0x9C40,0x85,{2895,1270}},{0,0,0,0,0,0,0x808,0x9C40,0x85,{2896,1270}},{0,0,0,0,0,0,0x808,0x9C40,0x251,{2897,2898}},{0,0,0,0,0,0,0xA0A,0xB75,0xB04,{2899,2900}},{0,0,0,0,0,0,0x606,0x9C40,0xAA0,{1250,1251}},{0,0,0,0,0,0,0xA0A,0x9C40,0x50D,{1248,2901}},{0,0,0,0,0,0,0xE0E,0x909,0x2D6,{2902,2903}},{0,0,0,0,0,0,0x202,0x9C40,0x2B5,{2904,2905}},{0,0,0,0,0,0,0x202,0x9C40,0x2C2,{2904,2906}},{0,0,0,0,0,0,0xE0E,0x9C40,0x58C,{2907,2908}},{0,0,0,0,0,0,0x606,0x9C40,0xAE2,{2909,2910}},{0,0,0,0,0,0,0xA0A,0x9C40,0x85,{2911,2912}},{0,0,0,0,0,0,0xA0A,0x9C40,0x7E,{2913,2912}},{0,0,0,0,0,0,0xA0A,0x9C40,0x85,{2914,2912}},{0,0,0,0,0,0,0xA0A,0x9C40,0x85,{2915,2912}},{0,0,0,0,0,0,0xA0A,0x9C40,0x85,{2916,2912}},{0,0,0,0,0,0,0x808,0x9C40,0x85,{2917,1270}},{0,0,0,0,0,0,0,0x9C40,0x189,{2918,2919}},{0,0,0,0,0,0,0xA0B,0x9C40,0x1151,{2920,2921}},{0,0,0,0,0,0,0x202,0x9C40,0x5D5,{2922,2923}},{0,0,0,0,0,0,0x202,0x9C40,0x44C,{2924,2925}},{0,0,0,0,0,0,0x1,0x9C40,0xAC8,{2926,2927}},{0,0,0,0,0,0,0xA0B,0x9C40,0x258,{1278,1279}},{0,0,0,0,0,0,0x202,0x1CD4,0x2260,{2928,2929}},{0,0,0,0,0,0,0x808,0xE95,0x1158,{2930,2931}},{0,0,0,0,0,0,0x202,0x9C40,0x47A,{2932,2933}},{0,0,0,0,0,0,0x202,0x9C40,0x48E,{2934,1290}},{0,0,0,0,0,0,0x202,0x9C40,0x2ADD,{2935,2936}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{1140,1141}},{0,0,0,0,0,0,0x202,0x9C40,0x5D,{1241,1286}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{1287,1288}},{0,0,0,0,0,0,0,0x9C40,0x55E9,{2937,2938}},{0,0,0,0,0,0,0,0x9C40,0x1511,{2939,2940}},{0,0,0,0,0,0,0xE0E,0x9C40,0x2C2,{1303,1304}},{0,0,0,0,0,0,0x202,0xE8E,0x1144,{1305,2941}},{0,0,0,0,0,0,0xE0E,0x33F4,0x3F20,{2942,2943}},{0,0,0,0,0,0,0xA0A,0x1180,0x1504,{2944,2945}},{0,0,0,0,0,0,0xE0E,0x32C8,0x39E4,{1313,1314}},{0,0,0,0,0,0,0x404,0x3C6,0x114A,{1315,1316}},{0,0,0,0,0,0,0xE0F,0x9C40,0x826,{1317,1318}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{1319,1320}},{0,0,0,0,0,0,0x607,0x9C40,0x182,{1299,1300}},{0,0,0,0,0,0,0xE0E,0x9C40,0x6A,{1321,1322}},{0,0,0,0,0,0,0x202,0x9C40,0x870,{2946,2947}},{0,0,0,0,0,0,0xA0A,0x18F2,0x585,{2948,2949}},{0,0,0,0,0,0,0xE0E,0x1FA,0xAD5,{942,710}},{0,0,0,0,0,0,0x202,0x3980,0x447D,{1305,1330}},{0,0,0,0,0,0,0x606,0x3882,0x42D9,{1331,1332}},{0,0,0,0,0,0,0xA0A,0x146A,0x8A5,{1333,1334}},{0,0,0,0,0,0,0x808,0x9C40,0xA78,{2950,2951}},{0,0,0,48,0,0,0xE0E,0x1CC,0x24A,{2952,2953}},{0,0,0,52,0,0,0xE0E,0x118,0x5D,{1344,1345}},{0,0,0,48,0,0,0x1,0x201,0x258,{2954,2955}},{0,0,0,36,0,0,0x1,0x2C9,0x33A,{2954,2955}},{0,0,0,60,0,0,0xE0E,0x8C,0x99,{1350,1351}},{0,0,0,84,0,0,0xE0E,0x15A,0x730,{709,710}},{0,0,0,60,0,0,0xE0E,0xDC,0xF6,{1352,1353}},{0,0,0,95,0,0,0x606,0x74A,0x88A,{1331,1332}},{0,0,0,84,0,0,0xE0E,0x182,0x1F4,{2952,2953}},{0,0,0,0,0,0,0xA0A,0x8B9,0x2C9,{2956,2957}},{0,0,0,0,0,0,0,0x8B2,0x2D0,{2958,2957}},{0,0,0,0,0,0,0xA0A,0x8B9,0x2E4,{2959,2957}},{0,0,0,0,0,0,0x404,0x88A,0x29A,{2960,2961}},{0,0,0,0,0,0,0x808,0x1D31,0x5D,{2962,2963}},{0,0,0,0,0,0,0,0x9C40,0xA6,{2964,2965}},{0,0,0,0,0,0,0xA0A,0xDA5,0x244,{2966,2967}},{0,0,0,0,0,0,0xE0E,0x6D2,0x5D,{2968,2969}},{0,0,0,0,0,0,0xE0E,0x42A,0x57E,{2970,2971}},{-12,0,0,0,0,0,0,0xD91,0x1590,{2972,2973}},{0,0,0,0,0,0,0,0x438,0x71,{2974,2975}},{0,0,0,0,0,0,0x203,0x9C40,0x564,{2976,2977}},{0,0,0,0,0,0,0x404,0x4BD,0x585,{2978,2979}},{0,0,0,0,0,0,0xC0C,0x258,0x189,{2980,2981}},{-12,0,0,0,0,0,0xE0E,0x1226,0x1590,{2982,2983}},{0,0,0,0,0,0,0,0x46D,0x92,{2984,2985}},{0,0,0,0,0,0,0xC0D,0x9C40,0x5D,{2986,2987}},{0,0,0,0,0,0,0xE0F,0x9C40,0x56,{2988,2989}},{0,0,0,0,0,0,0x607,0x9C40,0x5D,{2990,2991}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{376,377}},{0,0,0,0,0,0,0xE0F,0x9C40,0x64,{2992,377}},{0,0,0,0,0,0,0xA0A,0x9C40,0x64,{2993,2994}},{0,0,0,0,0,0,0,0x9C40,0x5D,{2995,2996}},{0,0,0,0,0,0,0,0x9C40,0x64,{2997,2998}},{0,0,0,0,0,0,0,0x18F2,0xBA,{2999,3000}},{0,0,0,0,0,0,0,0x18F2,0xBA,{3001,3000}},{0,0,0,0,0,0,0xE0E,0x9C40,0x64,{3002,55}},{0,0,0,0,0,0,0xE0E,0x9C40,0x50,{3003,3004}},{0,0,0,0,0,0,0xE0E,0x75E,0x5D,{3005,3006}},{0,0,0,0,0,0,0,0x9C40,0x56,{3007,3008}},{0,0,0,0,0,0,0,0x9C40,0x50,{3009,3010}},{0,0,0,0,0,0,0xC0C,0x5BA,0x71,{3011,3012}},{12,0,0,0,0,0,0x606,0x9C40,0x5D,{3013,3014}},{12,0,0,0,0,0,0x808,0x9C40,0x8C,{3015,3016}},{12,0,0,0,0,0,0xC0C,0x9C40,0x1B8,{3017,2898}},{12,0,0,0,0,0,0x808,0x9C40,0x5D,{3018,3019}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{3020,3021}},{12,0,0,0,0,0,0x606,0x9C40,0x1B8,{3022,2898}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{3023,3021}},{0,0,0,0,0,0,0x404,0x9C40,0x251,{3024,2898}},{0,0,0,0,0,0,0xE0E,0xE88,0x1144,{2982,2983}},{0,0,0,0,0,0,0,0x9C40,0x272,{3025,3026}},{0,0,0,0,0,0,0,0x9C40,0x286,{3027,3028}},{0,0,0,0,0,0,0,0x9C40,0x25E,{3029,3030}},{0,0,0,0,0,0,0xE0E,0x9C40,0xD5,{3031,3032}},{0,0,0,0,0,0,0,0x736,0x891,{3033,3034}},{0,0,0,0,0,0,0xA0A,0x9C40,0x56,{3035,3036}},{0,0,0,0,0,0,0,0x42A,0x4DE,{3037,3038}},{0,0,0,0,0,0,0xE0E,0x9C40,0xD5,{3039,3040}},{0,0,0,0,0,0,0xE0E,0x9C40,0x8A5,{3041,3042}},{0,0,0,0,0,0,0xE0E,0x9C40,0xD5,{3043,3044}},{0,0,0,0,0,0,0xE0E,0x9C40,0xDC,{3045,3046}},{0,0,0,0,0,0,0,0x9C40,0x488,{98,3047}},{0,0,0,0,0,0,0,0x9C40,0x139,{3048,3049}},{0,0,0,0,0,0,0xC0C,0x9C40,0x47A,{3050,3051}},{0,0,0,0,0,0,0x606,0x1A4,0x64,{3052,3053}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{3054,3021}},{0,0,0,0,0,0,0xE0E,0x9C40,0x56,{3055,3056}},{0,0,0,0,0,0,0xE0E,0x9C40,0x85,{3057,3058}},{0,0,0,0,0,0,0xE0E,0x9C40,0xE2,{3059,3060}},{0,0,0,0,0,0,0xE0E,0x9C40,0xE2,{3061,3062}},{0,0,0,0,0,0,0xE0E,0x9C40,0x50,{3063,3064}},{0,0,0,0,0,0,0xE0E,0x9C40,0x71,{3065,3066}},{0,0,0,0,0,0,0x606,0x9C40,0x85,{3067,3068}},{0,0,0,0,0,0,0xA0A,0x9C40,0x85,{3069,3068}},{0,0,0,0,0,0,0xA0A,0x9C40,0x8C,{3070,3071}},{0,0,0,0,0,0,0xA0A,0x9C40,0x8C,{3072,3071}},{0,0,0,0,0,0,0,0x9C40,0x1C5,{3073,3074}},{0,0,0,0,0,0,0,0x9C40,0x71,{3075,3076}},{0,0,0,0,0,0,0,0x9C40,0x71,{3077,3078}},{0,0,0,0,0,0,0xC0C,0x9C40,0x17C,{3079,3080}},{0,0,0,0,0,0,0xC0C,0x9C40,0x71,{3081,3082}},{0,0,0,0,0,0,0xC0C,0x9C40,0x6A,{3083,3082}},{0,0,0,0,0,0,0,0x9C40,0x92,{3084,3085}},{0,0,0,0,0,0,0xE0E,0x9C40,0x64,{3086,3087}},{0,0,0,0,0,0,0xE0E,0x9C40,0x92,{3088,3085}},{0,0,0,0,0,0,0xE0E,0x9C40,0x64,{3089,3090}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{3091,3021}},{0,0,0,0,0,0,0xC0C,0x9C40,0x11E,{3092,3093}},{0,0,0,0,0,0,0xE0E,0x9C40,0x92,{3094,3085}},{0,0,0,0,0,0,0xE0E,0x9C40,0x6A,{3095,3096}},{0,0,0,0,0,0,0,0x9C40,0xB4,{3097,3098}},{-12,0,0,0,0,0,0,0x9C40,0x92,{3099,3100}},{-24,0,0,0,0,0,0xA0B,0x9C40,0xAD,{3101,3102}},{0,0,0,0,0,0,0x808,0x9C40,0x56,{3103,3104}},{0,0,0,0,0,0,0,0x9C40,0x4E5,{3105,3106}},{0,0,0,0,0,0,0,0x9C40,0x24A,{3107,3108}},{0,0,0,0,0,0,0xA0A,0x9C40,0xDC,{3109,3110}},{0,0,0,0,0,0,0,0x9C40,0x8C6,{3111,3112}},{0,0,0,0,0,0,0xE0E,0x9C40,0x154D,{3113,3114}},{0,0,0,0,0,0,0,0x9C40,0x8B2,{3115,3042}},{0,0,0,0,0,0,0x606,0x9C40,0x265,{3116,3117}},{0,0,0,0,0,0,0xC0C,0x9C40,0x154,{3118,3119}},{0,0,0,0,0,0,0,0x2670,0x571,{3120,3121}},{-12,0,0,0,0,0,0x809,0x9C40,0xFD,{3122,3123}},{0,0,0,0,0,0,0xE0E,0xE95,0x1136,{3124,3125}},{0,0,0,0,0,0,0xA0A,0x9C40,0x64,{3126,3127}},{0,0,0,0,0,0,0,0x9C40,0x56,{3128,3129}},{0,0,0,0,0,0,0,0x9C40,0xADC,{3130,3131}},{0,0,0,0,0,0,0,0x9C40,0x56,{3132,3129}},{0,0,0,0,0,0,0xE0E,0x9C40,0x2184,{3133,3134}},{0,0,0,0,0,0,0,0x1115,0x542,{3135,3136}},{-12,0,0,0,0,0,0xE0E,0x33A,0x2B5,{3137,3138}},{0,0,0,0,0,0,0xE0E,0xE24,0xC1,{3139,3140}},{-12,0,0,0,0,0,0,0x689,0xA28,{3141,3142}},{0,0,0,0,0,0,0,0x1C5,0x182,{3143,3144}},{0,0,0,0,0,0,0xE0E,0x9C40,0x56,{3145,3146}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{3147,3148}},{0,0,0,0,0,0,0,0x9C40,0x251,{3149,3150}},{-7,0,0,0,0,0,0,0x11E4,0x1511,{3151,3152}},{-7,0,0,0,0,0,0,0x161,0x189,{3153,3154}},{0,0,0,0,0,0,0,0x514,0x56A,{3155,3156}},{-7,0,0,0,0,0,0x404,0x11DD,0x1539,{3157,3158}},{2,0,0,0,0,0,0x404,0x286,0x2F1,{3159,3160}},{0,0,0,0,0,0,0xE0E,0x280,0x2DD,{3161,3160}},{0,0,0,0,0,0,0xE0E,0xBCC,0x56,{3162,3163}},{0,0,0,0,0,0,0xE0E,0x445,0x236,{3164,3165}},{0,0,0,0,0,0,0xE0E,0x1798,0x1115,{3166,3167}},{0,0,0,0,0,0,0,0x279,0x146,{3168,3169}},{-12,0,0,0,0,0,0xC0C,0x9C40,0x6A,{3170,3171}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{3172,3173}},{0,0,0,0,0,0,0xE0E,0x9C40,0x1122,{3174,3175}},{0,0,0,0,0,0,0xE0E,0x272,0x2DD,{3176,3177}},{0,0,0,48,0,0,0xA0A,0x11E,0x139,{3178,3179}},{0,0,0,48,0,0,0,0x64,0x6A,{3180,3181}},{0,0,0,60,0,0,0xE0E,0xF0,0x14D,{3182,3183}},{0,0,0,60,0,0,0xE0E,0xA6,0xC1,{3184,3185}},{0,0,0,70,0,0,0xE0E,0x85,0x6A,{3186,3187}},{0,0,0,51,0,0,0x404,0x201,0x258,{3188,3189}},{0,0,0,60,0,0,0xE0E,0xAD,0xC1,{3190,3191}},{0,0,0,54,0,0,0x404,0x208,0x258,{3188,3189}},{0,0,0,60,0,0,0xC0C,0x99,0x85,{3192,3193}},{0,0,0,56,0,0,0x404,0x208,0x258,{3188,3189}},{0,0,0,60,0,0,0xE0E,0x2A1,0x251,{3194,3195}},{0,0,0,61,0,0,0x404,0x201,0x251,{3188,3189}},{0,0,0,63,0,0,0x404,0x201,0x258,{3188,3189}},{0,0,0,48,0,0,0xE0E,0x2A1,0x251,{3196,3195}},{0,0,0,68,0,0,0x404,0x1B8,0x1FA,{3188,3189}},{0,0,0,60,0,0,0xE0E,0x6E0,0x7D6,{3197,3198}},{0,0,0,60,0,0,0xE0E,0x2A1,0x251,{3199,3200}},{0,0,0,66,0,0,0xE0E,0x12C,0x139,{3201,3202}},{0,0,0,60,0,0,0,0x2A1,0x251,{3203,3195}},{0,0,0,59,0,0,0xE0E,0xAD,0xC1,{713,714}},{0,0,0,64,0,0,0xE0E,0x29A,0x251,{3196,3195}},{0,0,0,48,0,0,0x606,0x3EE,0x5D,{3204,3205}},{0,0,0,56,0,0,0,0x78,0x6A,{3206,3207}},{0,0,0,53,0,0,0,0x118,0x140,{3208,3209}},{0,0,0,65,0,0,0xE0E,0x64,0x6A,{3206,3210}},{0,0,0,49,0,0,0,0x11E,0x146,{3211,3209}},{0,0,0,43,0,0,0,0x11E,0x146,{3211,3209}},{0,0,0,65,0,0,0x808,0x3C6,0x8A5,{727,728}},{0,0,0,60,0,0,0x808,0x3F5,0x8CD,{727,728}},{0,0,0,70,0,0,0,0x104,0x118,{3212,3213}},{0,0,0,65,0,0,0,0x132,0x140,{3212,3213}},{0,0,0,60,0,0,0xE0E,0xF6,0x132,{3214,3215}},{0,0,0,60,0,0,0xE0E,0xBA,0xFD,{3216,3217}},{0,0,0,56,0,0,0xA0A,0xCE,0x5D,{3218,3219}},{0,0,0,53,0,0,0,0x1B1,0x64,{3220,3221}},{0,0,0,60,0,0,0xE0E,0xDC,0xF6,{3222,3223}},{0,0,0,48,0,0,0xE0E,0x12C,0x5D,{3224,3225}},{0,0,0,69,0,0,0x1,0x78,0,{3226,3227}},{0,0,0,67,0,0,0xE0E,0x85,0xA6,{618,619}},{0,0,0,62,0,0,0xE0E,0x92,0xBA,{618,619}},{0,0,0,65,0,0,0,0x1B1,0x12C,{3228,3229}},{0,0,0,60,0,0,0,0x1AA,0x12C,{3230,3231}},{0,0,0,63,0,0,0,0x78,0x78,{3232,3233}},{0,0,0,63,0,0,0,0x744,0x891,{3234,3235}},{0,0,0,67,0,0,0xE0E,0x10A,0xD5,{3236,3237}},{0,0,0,60,0,0,0,0x3B9,0x431,{3238,3239}},{0,0,0,72,0,0,0,0x320,0x391,{3238,3239}},{0,0,0,62,0,0,0,0x3C,0x5D,{753,754}},{0,0,0,48,0,0,0x202,0x78,0x85,{3240,3241}},{0,0,0,53,0,0,0x404,0x3C6,0x47A,{3242,3243}},{0,0,0,60,0,0,0,0x125,0x146,{3244,3154}},{0,0,0,60,0,0,0xE0E,0xA0,0x5D,{3245,3246}},{0,0,0,60,0,0,0xE0E,0x78,0x99,{3247,3248}},{0,0,0,60,0,0,0xE0E,0xAD,0xC1,{3249,3191}},{0,0,0,0,0,0,0xA0A,0x9C40,0x125,{3250,3251}},{0,0,0,0,0,0,0x809,0x9C40,0x460,{3252,3253}},{0,0,0,0,0,0,0x808,0xEC4,0x64,{3254,3255}},{0,0,0,0,0,0,0xE0E,0xED1,0x15E0,{3256,3257}},{0,0,0,0,0,0,0,0x9C40,0x5D,{1090,3258}},{0,0,0,0,0,0,0x202,0x9C40,0x46D,{2534,3259}},{0,0,0,0,0,0,0xC0C,0x9C40,0x2D0,{3260,3261}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{3262,3263}},{0,0,0,0,0,0,0xC0C,0x9C40,0x14D,{3264,3265}},{0,0,0,0,0,0,0,0x9C40,0x424,{3266,3267}},{0,0,0,0,0,0,0x606,0x16EA,0x2198,{3268,3269}},{0,0,0,0,0,0,0x606,0x1DF9,0x2238,{3270,3271}},{0,0,0,0,0,0,0x808,0x1219,0x891,{633,962}},{0,0,0,0,0,0,0xA0A,0x9C40,0x452,{3272,3273}},{0,0,0,0,0,0,0x606,0x9C40,0x639,{3274,3275}},{0,0,0,0,0,0,0xA0A,0x9C40,0x50,{3276,3277}},{0,0,0,0,0,0,0,0x4D1,0x578,{85,3278}},{0,0,0,0,0,0,0,0x256C,0x1596,{984,3279}},{0,0,0,0,0,0,0x808,0x9C40,0x10C5,{3280,3281}},{0,0,0,0,0,0,0xA0A,0x9C40,0x11E,{3250,3282}},{0,0,0,0,0,0,0,0x9C40,0x154,{352,3283}},{0,0,0,0,0,0,0x808,0x7FE,0x4D1,{3284,3285}},{0,0,0,0,0,0,0xC0C,0x9C40,0x71,{3286,3287}},{0,0,0,0,0,0,0,0x265,0x55D,{3288,3289}},{0,0,0,0,0,0,0x202,0x9C40,0x71,{3290,3291}},{0,0,0,0,0,0,0xE0E,0x1082,0x891,{3292,3293}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{3294,3295}},{0,0,0,0,0,0,0xC0C,0x9C40,0x58C,{3296,3297}},{0,0,0,0,0,0,0xC0C,0x9C40,0x24A,{3298,3299}},{0,0,0,0,0,0,0x808,0x9C40,0x2A8,{3300,3301}},{0,0,0,0,0,0,0xA0A,0x9C40,0x2A8,{3302,3303}},{0,0,0,0,0,0,0xC0C,0x9C40,0x8F5,{3304,3305}},{0,0,0,0,0,0,0xE0E,0x9C40,0x99,{3306,3307}},{0,0,0,0,0,0,0,0x780,0x876,{3308,3309}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{478,3310}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{3311,2572}},{0,0,0,0,0,0,0xC0C,0x9C40,0x2DD,{3312,3313}},{0,0,0,0,0,0,0x202,0x9C40,0x44C,{3314,3315}},{0,0,0,0,0,0,0x606,0x1AE0,0x6A,{3316,3317}},{0,0,0,0,0,0,0xE0E,0x9C40,0x2D0,{3318,3319}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{3320,3321}},{0,0,0,0,0,0,0,0x201C,0x14FD,{3322,3323}},{0,0,0,0,0,0,0,0x9C40,0x146,{352,3324}},{0,0,0,0,0x4,0,0x202,0x9C40,0x481,{3325,3326}},{0,0,0,0,0,0,0,0x9C40,0x215,{3327,3328}},{0,0,0,0,0,0,0,0x9C40,0x2C9,{3329,3330}},{0,0,0,0,0,0,0,0x272,0x2DD,{3331,3332}},{0,0,0,0,0,0,0,0x8E8,0xA92,{3333,3334}},{0,0,0,0,0,0,0xA0B,0x9C40,0x549,{3335,3336}},{0,0,0,0,0,0,0,0x9C40,0x51A,{3337,3338}},{0,0,0,0,0,0,0,0x916,0xABA,{3339,3340}},{0,0,0,0,0x4,0,0x606,0xF56,0x474,{3341,3342}},{0,0,0,0,0,0,0xE0E,0x109D,0x898,{3292,3343}},{0,0,0,0,0,0,0x808,0x9C40,0x481,{3344,3345}},{0,0,0,0,0,0,0,0x9C40,0x251,{3346,3347}},{0,0,0,0,0,0,0xA0A,0x9C40,0x44C,{3272,3348}},{0,0,0,41,0,0,0x808,0x182,0x1B8,{248,3349}},{0,0,0,70,0,0,0xE0E,0x64,0x5D,{3350,3351}},{0,0,0,60,0,0,0xE0E,0x161,0x23D,{3352,3353}},{0,0,0,80,0,0,0,0x64,0x5D,{3354,3355}},{0,0,0,84,0,0,0xE0E,0x71,0x78,{3356,3357}},{0,0,0,72,0,0,0xE0E,0x1FA,0x744,{3358,3359}},{0,0,0,84,0,0,0,0x35C,0x74A,{3360,3361}},{0,0,0,70,0,0,0x808,0x64,0x5D,{250,251}},{0,0,0,60,0,0,0xA0A,0x92,0x99,{258,259}},{0,0,0,0,0x1,0,0xE0E,0x9C40,0x2E4,{3362,3363,3364,3365} },{0,0,0,0,0,0,0xE0E,0x9C40,0x1906,{3366,3367}},{0,0,0,0,0x1,0,0xE0E,0x9C40,0x64,{3368,3369,3370,3371} },{0,0,0,0,0x1,0,0xC0C,0x9C40,0x92,{3372,3373,3374,3375} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x71,{3376,3377,3378,3379} },{0,0,0,0,0,0,0xE0E,0x495,0x571,{884,885}},{0,0,0,0,0,0,0xA0A,0x9C40,0x8E1,{3380,3381}},{0,0,0,0,0,0,0x203,0x9C40,0x128A,{3382,3383}},{0,0,0,0,0,0,0xE0E,0x9C40,0xA50,{3384,3385}},{0,0,0,0,0,0,0x606,0x9C40,0x4BD,{3386,3387}},{0,0,0,0,0,0,0xE0E,0x66E,0xADC,{3388,3389}},{0,0,0,0,0,0,0xE0E,0x9C40,0x251,{3390,3391}},{0,0,0,0,0,0,0x606,0x9C40,0x28D,{3392,3393}},{0,0,0,0,0,0,0,0x765,0xAFD,{3394,3395}},{0,0,0,0,0,0,0xE0E,0x1212,0xACE,{3396,3397}},{0,0,0,0,0,0,0x606,0x9C40,0x64,{3398,3399}},{0,0,0,0,0,0,0x808,0x9C40,0x2DD,{3400,3401}},{0,0,0,0,0,0,0x607,0x9C40,0x17C,{3402,3403}},{0,0,0,0,0,0,0xC0C,0x9C40,0xC1,{3404,3405}},{0,0,0,0,0,0,0xE0E,0x9C40,0x14D,{3406,3407}},{0,0,0,0,0,0,0x808,0x1CC6,0x2259,{3408,1165}},{0,0,0,0,0,0,0x404,0x47A,0x55D,{3409,1312}},{0,0,0,0,0,0,0,0x9C40,0xA28,{3410,3411}},{0,0,0,0,0,0,0,0x9C40,0x521,{3412,3413}},{0,0,0,0,0,0,0,0x9C40,0xA56,{3414,3415}},{0,0,0,0,0,0,0x606,0x1270,0x2D6,{1157,1158}},{0,0,0,0,0,0,0x607,0x94C,0xAB4,{3416,3417}},{0,0,0,0,0,0,0x404,0x1C41,0x215C,{890,891}},{0,0,0,0,0,0,0xE0E,0x1E0,0x2C9,{925,926}},{0,0,0,0,0,0,0xE0E,0x9C40,0x140,{3418,3419}},{0,0,0,0,0,0,0xE0E,0x272,0x2BC,{3420,3421}},{0,0,0,0,0,0,0x404,0x91D,0xADC,{3422,3423}},{0,0,0,0,0,0,0x202,0x9C40,0x4372,{882,3424}},{0,0,0,0,0,0,0x808,0x272,0x2D6,{3425,952}},{0,0,0,0,0,0,0xE0E,0x924,0xAD5,{940,941}},{0,0,0,0,0,0,0x808,0x154,0x189,{3425,951}},{0,0,0,60,0,0,0xE0E,0x9C40,0x10A,{3418,3419}},{0,0,0,72,0,0,0xE0E,0x61E,0x744,{940,941}},{0,0,0,84,0,0,0x808,0xF6,0x118,{3425,951}},{0,0,0,0,0,0,0x404,0x9C40,0x506,{3426,3427}},{0,0,0,0,0,0,0x606,0xA92,0x855,{3428,3429}},{0,0,0,0,0,0,0x404,0x9C40,0x592,{3430,3431}},{0,0,0,0,0,0,0x808,0x9C40,0xA0,{3432,3433}},{0,0,0,0,0,0,0x606,0x9C40,0x99,{3434,3435}},{0,0,0,0,0,0,0,0x9C40,0xA0,{3436,3433}},{0,0,0,0,0,0,0x606,0x9C40,0x64,{3437,3438}},{0,0,0,0,0,0,0x808,0x812,0x2DD,{3439,3440}},{0,0,0,0,0,0,0xC0C,0x9C40,0x189,{3441,3442}},{0,0,0,0,0,0,0,0x9C40,0xE2,{3443,3444}},{0,0,0,0,0,0,0x202,0x9C40,0xC1,{3445,3446}},{0,0,0,0,0,0,0x606,0x9C40,0x5D,{3447,3448}},{0,0,0,0,0,0,0x404,0x9C40,0x848,{36,3449}},{0,0,0,0,0,0,0x202,0x9C40,0x891,{3450,3451}},{0,0,0,0,0,0,0xA0A,0x9C40,0x88A,{3452,3449}},{0,0,0,0,0,0,0x808,0x1CB2,0x24A,{3453,3454}},{0,0,0,0,0,0,0x404,0x1712,0x140,{3455,3456}},{0,0,0,0,0,0,0,0x1D02,0x2DD,{3457,3458}},{0,0,0,0,0,0,0,0x1D02,0x2DD,{3459,3458}},{0,0,0,0,0,0,0x606,0x9C40,0x5D,{3460,3461}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{3462,3463}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{3464,3465}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{3466,121}},{0,0,0,0,0,0,0xA0A,0x9C40,0xC8,{3467,3468}},{0,0,0,0,0,0,0xA0A,0x9C40,0xA6,{3469,3470}},{0,0,0,0,0,0,0x808,0x9C40,0x20EA,{3471,3472}},{0,0,0,0,0,0,0,0x9C40,0x10D2,{3473,3474}},{0,0,0,0,0,0,0,0x9C40,0x104D,{3475,3476}},{0,0,0,0,0,0,0xA0A,0x9C40,0x57E,{3477,3478}},{0,0,0,0,0,0,0x202,0x9C40,0x13F9,{3479,1154}},{0,0,0,0,0,0,0xC0C,0x9C40,0x215,{3480,3481}},{0,0,0,0,0,0,0x808,0x1220,0x8B2,{633,3482}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{3483,3484}},{0,0,0,0,0,0,0,0x9C40,0x4178,{3485,3486}},{0,0,0,0,0,0,0x202,0x1E00,0x223E,{3487,3488}},{0,0,0,0,0,0,0xA0A,0x9C40,0x71,{3489,3490}},{0,0,0,0,0,0,0x607,0x1F7C,0x251C,{3491,3492}},{0,0,0,0,0,0,0xC0C,0x9C40,0xBA,{3493,3494}},{0,0,0,0,0,0,0xC0C,0x9C40,0xD5,{3495,3496}},{0,0,0,0,0,0,0x202,0xFFD,0x1518,{3497,3498}},{0,0,0,0,0,0,0,0x9C40,0x5D,{3499,1069}},{0,0,0,0,0,0,0xE0E,0x9C40,0xE2,{3500,3501}},{0,0,0,0,0,0,0x202,0x9C40,0x2C2,{3502,3503}},{0,0,0,0,0,0,0,0x9C40,0xE2,{3504,3505}},{0,0,0,0,0,0,0,0x4B0,0xE2,{3506,3507}},{0,0,0,0,0,0,0x808,0x9C40,0x5AD,{3508,3509}},{0,0,0,0,0,0,0xE0E,0x9C40,0x189,{3510,3511}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5B4,{3512,3513}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{3514,3515}},{0,0,0,0,0,0,0x202,0x9C40,0x2B5,{3516,3517}},{0,0,0,0,0,0,0x202,0x481,0xE2,{3518,3519}},{0,0,0,0,0,0,0x606,0x1310,0x1540,{3520,3521}},{0,0,0,0,0,0,0x404,0xB75,0xA99,{3522,3523}},{0,0,0,0,0,0,0xC0C,0x9C40,0x8C,{3524,3525}},{0,0,0,0,0,0,0xC0C,0x952,0xADC,{3526,3527}},{0,0,0,0,0,0,0x202,0x9C40,0x6A,{3528,3529}},{0,0,0,0,0,0,0x606,0x9C40,0xD5,{3530,3531}},{0,0,0,0,0,0,0xA0A,0x9C40,0xD5,{3532,3533}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{3534,3535}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{3536,3537}},{0,0,0,0,0,0,0xA0A,0x9C40,0xAD,{3538,3539}},{0,0,0,0,0,0,0x202,0x9C40,0x64,{3540,3541}},{0,0,0,0,0,0,0x202,0x9C40,0x6A,{3540,3542}},{0,0,0,0,0,0,0x202,0x9C40,0x6A,{3543,3544}},{0,0,0,0,0,0,0x202,0x9C40,0x6A,{3545,3546}},{0,0,0,0,0,0,0xC0C,0x9C40,0x6A,{3547,3548}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{3549,3550}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{3551,3552}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{3553,3554}},{0,0,0,0,0,0,0xC0C,0x9C40,0x2F1,{3555,3556}},{0,0,0,0,0,0,0,0x9C40,0x17C,{3557,3080}},{0,0,0,0,0,0,0,0x9C40,0x175,{3558,3080}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{3559,3560}},{0,0,0,0,0,0,0x202,0x9C40,0x56,{3561,133}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{3562,3563}},{0,0,0,0,0,0,0xA0A,0x9C40,0x190,{3564,3565}},{0,0,0,0,0,0,0xE0E,0x9C40,0x50,{3566,3567}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{3568,3569}},{0,0,0,0,0,0,0x606,0x9C40,0x5D,{3570,3571}},{0,0,0,0,0,0,0x606,0x9C40,0x5D,{3572,3573}},{0,0,0,0,0,0,0x202,0x9C40,0x71,{3574,115}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{3575,3576}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{3577,3578}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{3579,3580}},{0,0,0,0,0,0,0xA0A,0x9C40,0x578,{3581,3582}},{0,0,0,0,0,0,0xA0A,0x9C40,0x578,{3583,3584}},{0,0,0,0,0,0,0x202,0x9D8,0xADC,{3585,3586}},{0,0,0,0,0,0,0x202,0x361D,0x444E,{3587,790}},{0,0,0,0,0,0,0xA0A,0x1CD4,0x2266,{3588,3589}},{0,0,0,0,0,0,0x606,0x1C62,0x21E8,{3590,3591}},{0,0,0,0,0,0,0x404,0x272,0x2DD,{3592,3593}},{0,0,0,0,0,0,0x808,0x909,0xABA,{3594,3595}},{0,0,0,0,0,0,0x202,0x9C40,0x71,{3540,3548}},{0,0,0,0,0,0,0xA0B,0x9C40,0x161,{3596,3597}},{0,0,0,0,0,0,0x405,0x9C40,0x175,{3598,3599}},{0,0,0,0,0,0,0xA0A,0x9C40,0x49C,{3600,3601}},{0,0,0,0,0,0,0xC0C,0x9C40,0x6A,{3602,3603}},{0,0,0,0,0,0,0x202,0x1AD9,0x114A,{3604,90}},{0,0,0,0,0,0,0x404,0x9EC,0x8B9,{3605,3606}},{0,0,0,0,0,0,0xC0C,0xC8,0xE2,{3607,665}},{0,0,0,0,0,0,0xA0A,0xD5,0xF0,{3608,3609}},{0,0,0,0,0,0,0xA0A,0x168,0x189,{3610,3611}},{0,0,0,0,0,0,0x606,0x3F5,0x4D1,{3612,3613}},{0,0,0,0,0,0,0xE0E,0x10A,0x132,{3614,3615}},{0,0,0,0,0,0,0xE0E,0x758,0x8B2,{3616,3617}},{0,0,0,0,0,0,0x1,0x118,0x251,{3618,3619}},{0,0,0,0,0,0,0x808,0xE4C,0x11EA,{3620,3621}},{0,0,0,0,0,0,0xA0A,0x46D,0x578,{3622,2417}},{0,0,0,0,0,0,0xA0A,0x3354,0x21E1,{241,2593}},{0,0,0,0,0,0,0xE0F,0x14D,0x64,{3623,3624}},{0,0,0,0,0,0,0x404,0x1CC0,0x2252,{3625,3626}},{0,0,0,0,0,0,0x808,0x4EC,0x564,{3627,3628}},{0,0,0,51,0x4,0,0,0x6,0,{662,663}},{0,0,0,35,0,0,0x808,0x2BC,0x33A,{3629,3630}},{0,0,0,36,0,0,0x808,0x2BC,0x33A,{3629,3630}},{0,0,0,47,0,0,0xC0D,0x64,0x64,{3631,3632}},{0,0,0,38,0,0,0xE0E,0x154,0x189,{3633,3634}},{0,0,0,39,0,0,0xE0E,0xDC,0xF6,{3614,3615}},{0,0,0,45,0,0,0xE0E,0x118,0x140,{3635,3636}},{0,0,0,41,0,0,0x404,0x410,0x55D,{912,913}},{0,0,0,42,0,0,0xC0C,0xD5,0xF0,{3637,3638}},{0,0,0,44,0,0,0xE0E,0x1FA,0x251,{237,3639}},{0,0,0,48,0,0,0x404,0x341,0x445,{912,913}},{0,0,0,46,0,0,0xE0E,0x74A,0x898,{237,3640}},{0,0,0,53,0,0,0x404,0x355,0x481,{912,913}},{0,0,0,49,0,0,0xE0E,0xE60,0x1129,{237,263}},{0,0,0,56,0,0,0xE0E,0x279,0x466,{323,324}},{0,0,0,61,0,0,0xE0F,0x16E,0x1AA,{3641,3642}},{0,0,0,56,0,0,0x808,0x542,0x639,{3643,3644}},{0,0,0,60,0,0,0x607,0xC8,0x139,{280,3645}},{0,0,0,59,0,0,0x607,0xC8,0x139,{280,281}},{0,0,0,59,0,0,0x607,0x64,0x6A,{326,3646}},{0,0,0,51,0,0,0x607,0x168,0x474,{326,327}},{0,0,0,45,0,0,0x607,0x168,0x474,{326,327}},{0,0,0,72,0,0,0xE0F,0xF6,0x5D,{3647,3648}},{0,0,0,60,0,0,0xE0F,0x118,0x5D,{3623,3649}},{0,0,0,58,0,0,0xE0F,0x15A,0x460,{3650,3651}},{0,0,0,53,0,0,0xE0F,0x154,0x459,{3652,3651}},{0,0,0,73,0,0,0xC0C,0x78,0x5D,{3653,3654}},{0,0,0,75,0,0,0x1,0x78,0x140,{308,309}},{0,0,0,0,0,0,0x404,0x1B5E,0x10F4,{2532,3655}},{0,0,0,0,0,0,0xC0C,0x805,0x995,{3656,3657}},{0,0,0,0,0,0,0x202,0xE88,0x1130,{1305,3658}},{0,0,0,0,0,0,0xA0A,0x49C,0x2A5E,{3659,3660}},{0,0,0,0,0,0,0xA0A,0x4B0,0x2B05,{3661,3660}},{0,0,0,0,0,0,0x808,0x9C40,0x21FC,{3662,3663}},{0,0,0,0,0,0,0xA0A,0x9C40,0x58C,{3664,3665}},{0,0,0,0,0,0,0xC0C,0x3DA,0x21A5,{3666,3667}},{0,0,0,0,0,0,0,0x9C40,0x2252,{3668,3669}},{0,0,0,0,0,0,0,0x9C40,0x226D,{3670,3669}},{0,0,0,0,0,0,0x1,0x9C40,0x585,{3671,3672}},{0,0,0,0,0,0,0xE0E,0x9C40,0x8C0,{3673,3674}},{0,0,0,0,0,0,0xE0E,0x9C40,0x64,{3675,3676}},{0,0,0,0,0,0,0x202,0x9C40,0x48E,{3677,3678}},{0,0,0,0,0,0,0xA0A,0x341,0x189,{3679,3680}},{0,0,0,0,0,0,0xA0A,0x689,0xADC,{3681,3682}},{0,0,0,0,0,0,0x606,0x9C40,0x5D,{3683,413}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{1311,3684}},{0,0,0,0,0,0,0x808,0x744,0x898,{1192,3685}},{0,0,0,0,0,0,0xA0A,0x9C40,0x514,{3686,3687}},{0,0,0,0,0,0,0xE0E,0x168,0x2FE,{3688,3689}},{0,0,0,0,0,0,0,0x2274,0x14C1,{1260,3690}},{0,0,0,0,0,0,0,0x9C40,0x1590,{3691,3692}},{0,0,0,0,0,0,0xA0A,0x9C40,0x514,{3686,3693}},{0,0,0,0,0,0,0x808,0x254A,0x155A,{3694,3695}},{0,0,0,0,0,0,0,0xCC9,0xF2E,{3696,3697}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{3698,3699}},{0,0,0,0,0,0,0xE0E,0x9C40,0x175,{3700,1244}},{0,0,0,0,0,0,0xE0E,0x9C40,0x16F8,{1254,1255}},{0,0,0,0,0,0,0xC0C,0x9C40,0x71,{1298,3701}},{0,0,0,0,0,0,0,0x9C40,0x2DD,{1218,3702}},{0,0,0,0,0,0,0,0x9C40,0x2DD,{1218,3703}},{0,0,0,0,0,0,0x607,0x9C40,0x182,{3704,3705}},{0,0,0,0,0,0,0x607,0x9C40,0x168,{3704,3706}},{0,0,0,0,0,0,0xE0F,0x9C40,0x2DD,{1214,3707}},{0,0,0,0,0,0,0x202,0xE09,0x10B1,{1305,3708}},{0,0,0,0,0,0,0x202,0x9C40,0x146,{3709,3710}},{0,0,0,0,0,0,0x606,0x9C40,0x2210,{3711,3712}},{0,0,0,0,0,0,0xE0E,0x9C40,0x62C,{3713,3714}},{0,0,0,0,0,0,0xC0C,0x9C40,0x11A1,{3715,3716}},{0,0,0,0,0,0,0x1,0x9C40,0x13DE,{3717,3718}},{0,0,0,0,0,0,0x606,0x9C40,0x4BA0,{3719,3720}},{0,0,0,0,0,0,0x202,0x9C40,0x1596,{3721,3722}},{0,0,0,0,0,0,0x203,0x9C40,0x1589,{3723,3724}},{0,0,0,0,0,0,0x809,0x9C40,0x1590,{3725,3726}},{0,0,0,0,0,0,0x809,0x9C40,0x15E6,{3727,3726}},{0,0,0,0,0,0,0x607,0x9C40,0x1590,{3728,3729}},{0,0,0,0,0,0,0,0x9C40,0x430E,{3730,3731}},{0,0,0,0,0,0,0,0x9C40,0x44C0,{3732,3733}},{0,0,0,0,0,0,0,0x533A,0x5599,{3734,3735}},{0,0,0,0,0,0,0x202,0x9C40,0x2126,{3736,3737}},{0,0,0,0,0,0,0,0x46D5,0x51C5,{3738,3739}},{0,0,0,0,0,0,0,0x8FC,0xAE2,{3740,3741}},{0,0,0,0,0,0,0x808,0xAF6,0xE16,{3742,3743}},{0,0,0,0,0,0,0,0xC22,0xD55,{3744,3745}},{0,0,0,0,0,0,0xE0E,0x398,0x154D,{3746,3747}},{0,0,0,0,0,0,0xC0C,0x772,0x46D,{967,3748}},{0,0,0,0,0,0,0x202,0x9C40,0x5D,{3749,3750}},{0,0,0,0,0,0,0x202,0x9C40,0x5D,{3751,3750}},{0,0,0,0,0,0,0x404,0x4A9,0x1630,{3752,3753}},{0,0,0,0,0,0,0x202,0x3CD,0x115E,{3754,3755}},{0,0,0,0,0,0,0x808,0xC8,0x5D,{955,3756}},{0,0,0,0,0,0,0,0x272,0x2DD,{3757,3160}},{0,0,0,0,0,0,0xE0E,0xC36,0x2DD,{3758,3759}},{0,0,0,0,0,0,0x808,0x2F1,0x201,{3760,3761}},{0,0,0,0,0,0,0xE0E,0x9C40,0x2AB5,{3762,3763}},{0,0,0,0,0,0,0x606,0x9C40,0x2C9,{3764,3765}},{0,0,0,0,0,0,0x606,0x9C40,0x2D6,{3766,3765}},{0,0,0,0,0,0,0xE0E,0x9C40,0x2DD,{3767,3768}},{0,0,0,60,0,0,0x808,0xAD,0x5D,{955,845}},{0,0,0,12,0,0,0,0x26C,0x2DD,{1358,1359}},{0,0,0,89,0,0,0xE0F,0x6A,0x5D,{3769,3770}},{0,0,0,89,0,0,0xE0E,0x2B5,0x326,{3771,3772}},{0,0,0,14,0,0,0,0x26C,0x2DD,{1358,1359}},{0,0,0,16,0,0,0,0x272,0x2DD,{1358,1359}},{0,0,0,84,0,0,0xE0E,0x62C,0x73D,{3773,3774}},{0,0,0,19,0,0,0,0x272,0x2DD,{1358,1359}},{0,0,0,60,0,0,0xE0E,0x19D,0x869,{709,710}},{0,0,0,38,0,0,0x404,0xDC,0x1B8,{3775,3776}},{0,0,0,36,0,0,0xC0C,0x6D9,0xAF6,{3777,3778}},{0,0,0,28,0,0,0,0x26C,0x2D6,{1358,1359}},{0,0,0,26,0,0,0,0x272,0x2E4,{1358,1359}},{0,0,0,35,0,0,0,0x272,0x2E4,{1358,1359}},{0,0,0,30,0,0,0,0x272,0x2DD,{1358,1359}},{0,0,0,60,0,0,0xE0E,0xAD,0xA0,{3779,3780}},{0,0,0,60,0,0,0x808,0xAD,0x5D,{955,3756}},{0,0,0,55,0,0,0x808,0xAD,0x5D,{955,3756}},{0,0,0,94,0,0,0x606,0x744,0x884,{1331,1332}},{0,0,0,0,0,0,0x606,0x375D,0x466,{1364,1365}},{0,0,0,0,0,0,0xC0C,0x3A26,0x481,{1366,1367}},{0,0,0,0,0,0,0xE0E,0x38FA,0x24A,{1374,1375}},{0,0,0,0,0,0,0x809,0xB68,0x6E6,{1378,1379}},{0,0,0,0,0,0,0xA0B,0x73D,0xAFD,{1382,1383}},{0,0,0,0,0,0,0x405,0x701,0x862,{1386,1387}},{0,0,0,0,0,0,0xA0A,0xD7D,0xAA0,{1390,1391}},{0,0,0,0,0,0,0x607,0x9C40,0x5D,{1394,1395}},{0,0,0,0,0,0,0x405,0x9C40,0x5D,{1396,1397}},{0,0,0,0,0,0,0xA0B,0x9C40,0x3C0,{1398,1399}},{0,0,0,0,0,0,0x809,0x9C40,0x265,{1400,1401}},{0,0,0,0,0,0,0xE0E,0x9C40,0x132,{1404,1405}},{0,0,0,0,0,0,0xE0E,0x9C40,0x24A,{1408,1409}},{0,0,0,0,0,0,0x404,0x1CC0,0xC1,{1410,1411}},{0,0,0,0,0,0,0xE0E,0x9C40,0x56,{1414,1415}},{0,0,0,0,0,0,0x808,0x4C4,0x64,{3781,3782}},{0,0,0,0,0,0,0x606,0x9C40,0x78,{1420,1421}},{0,0,0,0,0,0,0x808,0x91D,0x5D,{1424,1425}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{1430,1431}},{0,0,0,0,0,0,0xA0A,0x9C40,0xDC,{3783,3784}},{0,0,0,0,0,0,0x606,0x9CA,0x146,{1434,1435}},{0,0,0,0,0,0,0xC0C,0x9CA,0x146,{1436,1437}},{0,0,0,0,0,0,0x808,0x9C40,0x49,{1438,1439}},{0,0,0,0,0,0,0x808,0xD3A,0x71,{1440,1441}},{0,0,0,0,0,0,0xC0C,0x9C40,0xDC,{1442,1443}},{0,0,0,0,0,0,0xC0C,0x9C40,0xDC,{1444,1443}},{0,0,0,0,0,0,0xA0A,0x9C40,0x175,{1445,1446}},{0,0,0,0,0,0,0xC0C,0x9C40,0x1B8,{1447,1448}},{0,0,0,0,0,0,0xC0C,0x9C40,0x2AE,{1449,1450}},{0,0,0,0,0,0,0x809,0x43E,0x2A8,{1451,1452}},{0,0,0,0,0,0,0xE0E,0xE1D,0x10B8,{1453,1454}},{0,0,0,0,0,0,0xE0E,0x9C40,0x2C9,{1457,1458}},{0,0,0,0,0,0,0xE0E,0x9C40,0x578,{1459,1460}},{0,0,0,0,0,0,0xE0E,0x9C40,0x279,{1461,1462}},{0,0,0,0,0,0,0xE0E,0x9C40,0x189,{1463,3785}},{0,0,0,0,0,0,0x808,0x9C40,0x7E,{1465,1466}},{0,0,0,0,0,0,0xA0B,0x4A2,0x229,{1471,1472}},{0,0,0,0,0,0,0xE0E,0x9C40,0xAD,{1473,1474}},{0,0,0,0,0,0,0xE0E,0x9C40,0x8C,{3786,3787}},{0,0,0,0,0,0,0xE0E,0x9C40,0x92,{3788,3789}},{0,0,0,0,0,0,0x606,0x9C40,0x5D,{1479,1480}},{0,0,0,0,0,0,0x808,0x9C40,0x78,{1481,3790}},{0,0,0,0,0,0,0xE0E,0x9C40,0x8C,{1483,1484}},{0,0,0,0,0,0,0xE0E,0x9C40,0x17C,{3791,3792}},{0,0,0,0,0,0,0xE0E,0x9C40,0x2C2,{1487,3793}},{0,0,0,0,0,0,0,0x9C40,0x71,{1489,3794}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{1491,1492}},{0,0,0,0,0,0,0xC0C,0x9C40,0x6A,{1493,3795}},{0,0,0,0,0,0,0x808,0x9C40,0xDC,{1495,1496}},{0,0,0,0,0,0,0,0x9C40,0x71,{1499,3796}},{0,0,0,0,0,0,0xA0A,0x9C40,0x7E,{3797,3798}},{0,0,0,0,0,0,0,0x9C40,0x71,{3799,3800}},{0,0,0,0,0,0,0,0x9C40,0x85,{3801,3802}},{0,0,0,0,0,0,0xE0E,0x9C40,0xCE,{1513,1514}},{0,0,0,0,0,0,0xE0E,0x9C40,0x12C,{1515,1516}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{3803,3804}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{1523,1524}},{0,0,0,0,0,0,0x202,0x9C40,0x11E,{1525,1526}},{0,0,0,0,0,0,0xE0E,0x9C40,0x7E,{1527,1528}},{-12,0,0,0,0,0,0x809,0x9C40,0xC1,{1533,1534}},{0,0,0,0,0,0,0xE0E,0x9C40,0x56,{1535,1536}},{0,0,0,0,0,0,0xE0E,0x9C40,0x52E,{1539,1540}},{0,0,0,0,0,0,0xC0C,0x9C40,0x2D6,{1541,1542}},{0,0,0,0,0,0,0,0x9C40,0x481,{3805,3806}},{0,0,0,0,0,0,0x404,0x265C,0x556,{1547,3807}},{0,0,0,0,0,0,0xA0A,0x9C40,0x2B5,{1549,1550}},{0,0,0,0,0,0,0xA0A,0x9C40,0x549,{3808,3809}},{-12,0,0,0,0,0,0x607,0x9C40,0x521,{1555,1556}},{0,0,0,0,0,0,0xE0E,0x230D,0xA99,{1561,1562}},{0,0,0,0,0,0,0xC0C,0x9C40,0x4EC,{1563,1564}},{0,0,0,0,0,0,0xE0E,0x9C40,0x26C,{1565,1566}},{0,0,0,0,0,0,0xA0A,0x9C40,0x564,{1567,1568}},{0,0,0,0,0,0,0x808,0x1194,0x2C9,{1569,1570}},{0,0,0,0,0,0,0xC0C,0x279,0xE2,{1571,1572}},{0,0,0,0,0,0,0xC0C,0xE81,0x474,{1573,1574}},{0,0,0,0,0,0,0x808,0x910,0x56A,{1575,1576}},{0,0,0,0,0,0,0x808,0x9C40,0xD5,{1579,1580}},{0,0,0,0,0,0,0xA0A,0x9C40,0xDC,{1583,1584}},{0,0,0,0,0,0,0xE0E,0x201,0xC1,{1587,1588}},{0,0,0,0,0,0,0x607,0x2B5,0x2D0,{1589,1590}},{0,0,0,0,0,0,0x404,0x786,0x902,{1593,1594}},{0,0,0,0,0,0,0x808,0x4C4,0x542,{1595,1596}},{0,0,0,0,0,0,0xE0E,0x1061,0x64,{1597,1598}},{0,0,0,0,0,0,0xC0C,0x111,0x17C,{1601,1602}},{0,0,0,0,0,0,0xE0E,0x147E,0xAAD,{1603,1604}},{-24,0,0,0,0,0,0,0x182,0x8C,{1605,1606}},{-36,0,0,0,0,0,0x202,0x9C40,0x49C,{1609,1610}},{0,0,0,0,0,0,0xE0E,0x9C40,0x3D4,{1611,1612}},{0,0,0,0,0,0,0xE0E,0xD91,0x5D,{3810,3811}},{0,0,0,36,0,0,0xE0E,0xE9,0x64,{1613,1614}},{0,0,0,48,0,0,0x808,0xC1,0xB4,{1615,1616}},{0,0,0,36,0,0,0x808,0xE2,0xDC,{1615,1616}},{0,0,0,30,0,0,0x404,0xF6,0x71,{1619,1620}},{0,0,0,35,0,0,0x404,0x1A4,0x182,{1621,1622}},{0,0,0,60,0,0,0xE0E,0xA0,0x85,{1623,3812}},{0,0,0,59,0,0,0xE0E,0x92,0x5D,{1613,1614}},{0,0,0,44,0,0,0xE0E,0xA0,0x85,{1623,3812}},{0,0,0,41,0,0,0xC0D,0x2C9,0x33A,{804,805}},{0,0,0,47,0,0,0x606,0xAD,0xC1,{1625,3813}},{0,0,0,44,0,0,0xC0D,0x201,0x258,{804,805}},{0,0,0,47,0,0,0x606,0xBA,0x85,{3814,3815}},{0,0,0,48,0,0,0xC0D,0x201,0x258,{804,805}},{0,0,0,62,0,0,0x404,0x76C,0xC1,{1627,3816}},{0,0,0,51,0,0,0xC0D,0x201,0x258,{804,805}},{0,0,0,54,0,0,0xC0D,0x201,0x258,{804,805}},{0,0,0,93,0,0,0xC0C,0x474,0x632,{3817,3818}},{0,0,0,57,0,0,0xC0D,0x17C,0x1B8,{804,805}},{0,0,0,50,0,0,0,0x118,0xC1,{3819,3820}},{0,0,0,40,0,0,0xC0C,0x78D,0xAA6,{3817,3818}},{0,0,0,60,0,0,0xE0E,0x5C1,0x855,{707,708}},{0,0,0,60,0,0,0,0x6A,0x85,{3821,3822}},{0,0,0,60,0,0,0,0xAD,0x14D,{1635,1636}},{0,0,0,57,0,0,0,0x6A,0x85,{3821,3822}},{0,0,0,42,0,0,0xE0E,0x279,0x2DD,{1637,1638}},{0,0,0,37,0,0,0xE0E,0x279,0x2DD,{1637,1638}},{0,0,0,42,0,0,0x606,0x26C,0x2D6,{1639,1640}},{0,0,0,38,0,0,0x606,0x272,0x2DD,{1639,1640}},{0,0,0,77,0,0,0xE0E,0xAD,0x6A,{1587,1588}},{0,0,0,72,0,0,0xE0E,0xAD,0x6A,{1587,1588}},{0,0,0,70,0,0,0xE0E,0xD5,0xE2,{731,732}},{0,0,0,39,0,0,0x404,0x104,0x64,{1641,1642}},{0,0,0,46,0,0,0xA0A,0x78,0x64,{829,1645}},{0,0,0,48,0,0,0x808,0x2FE,0x64,{1646,1647}},{0,0,0,85,0,0,0x808,0x7E,0x5D,{1648,1649}},{0,0,0,88,0,0,0x606,0x99,0x5D,{1654,1655}},{0,0,0,81,0,0,0x606,0x320,0x3AC,{751,752}},{0,0,0,76,0,0,0x606,0x320,0x3A5,{751,752}},{0,0,0,10,0,0,0x606,0x4A9,0x57E,{751,752}},{0,0,0,0,0,0,0x606,0x245A,0x190,{3823,3824}},{0,0,0,0,0,0,0x1,0x9C40,0x78,{3825,3826}},{0,0,0,0,0,0,0,0x11F1,0x57E,{14,3827}},{0,0,0,0,0,0,0,0x9C40,0xD5,{2420,119}},{0,0,0,0,0,0,0x1,0x9C40,0x7A1,{3828,3829}},{0,0,0,0,0,0,0xC0C,0x9C40,0x58C,{3830,3831}},{0,0,0,0,0,0,0,0x10FA,0x182,{3832,3833}},{0,0,0,0,0,0,0x808,0x115E,0xA5D,{3834,3835}},{0,0,0,0,0,0,0xE0E,0x9C40,0x71,{3836,3837}},{0,0,0,0,0,0,0xC0C,0x1220,0x1589,{3838,3839}},{0,0,0,0,0,0,0xE0E,0x3E1,0x460,{3840,3841}},{0,0,0,0,0,0,0xE0E,0x780,0x89E,{3842,3843}},{0,0,0,0,0,0,0xE0E,0x8E8,0xA85,{264,265}},{0,0,0,0,0,0,0x808,0x11E,0x47A,{325,309}},{0,0,0,0,0,0,0x808,0x1C20,0x2170,{318,319}},{0,0,0,0,0,0,0xE0E,0x312,0x58C,{323,324}},{0,0,0,65,0,0,0x606,0xAD,0xBA,{3844,3845}},{0,0,0,21,0,0,0xE0E,0x1D9,0x182,{3846,3847}},{12,0,0,0,0x1,0,0xD0C,0x1CA5,0xDC,{332,333,334,335} },{0,0,0,0,0x1,0,0,0x1205,0x2AC2,{3848,3849,3850,3851} },{0,0,0,0,0x1,0,0x908,0x1D2A,0x3965,{3852,3853,3854,3855} },{0,0,0,0,0x1,0,0x908,0x1A89,0x200E,{3856,3857,3858,1454} },{0,0,0,0,0x1,0,0xE0E,0x1852,0x2210,{3859,3860,3861,3862} },{-12,0,0,0,0x1,0,0x504,0x4060,0x4CA,{3863,3864,3865,3866} },{12,0,0,0,0x1,0,0xF0E,0x9C40,0xA85,{3867,3868,3869,3870} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0xA71,{3867,3868,3869,3870} },{12,0,0,0,0x1,0,0xD0C,0x9C40,0x495,{567,174,568,569} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x898,{3871,3872,3873,3874} },{0,0,0,0,0x1,0,0x908,0x9C40,0x5AD,{3875,3876,3877,3878} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x57E,{3879,3880,524,3881} },{24,0,0,0,0x1,0,0xB0A,0x1AC5,0x779,{3882,3883,3884,3885} },{0,0,0,65,0,0,0x808,0xE2,0xFD,{248,249}},{0,0,0,72,0,0,0x808,0xB4,0xC8,{248,249}},{0,0,0,52,0x1,0,0x606,0x78,0x5D,{686,687,688,251} },{0,0,0,60,0x1,0,0xF0E,0xF6,0x111,{3886,253,3887,3888} },{0,0,0,47,0x1,0,0x100,0x1CC,0x215,{2774,2775,256,257} },{0,0,0,76,0x1,0,0x706,0x32D,0x3C0,{2778,2779,2780,2781} },{0,0,0,84,0x1,0,0xE0E,0x32D,0x3C0,{703,704,705,706} },{0,0,0,83,0x1,0,0x908,0xE2,0x104,{274,2793,2794,275} },{0,0,0,24,0x1,0,0xD0C,0x729,0x8C6,{2797,2528,2797,277} },{0,0,0,77,0x1,0,0x706,0x32D,0x3C6,{2778,2779,2780,2781} },{0,0,0,60,0x1,0,0x706,0x111,0x140,{2801,2802,2803,2804} },{0,0,0,65,0x1,0,0x706,0x140,0x265,{2805,2806,2807,2808} },{0,0,0,44,0x1,0,0xD0C,0x118,0x146,{737,738,739,739} },{0,0,0,40,0x1,0,0xD0C,0x1C5,0x1B8,{737,738,740,740} },{0,0,0,72,0x1,0,0xF0E,0x78,0x56,{2853,2854,2853,2855} },{0,0,0,73,0x1,0,0xF0E,0x4E5,0x1ED,{323,748,749,750} },{0,0,0,70,0x1,0,0xA0B,0x618,0x3AC,{2856,2857,2858,2859} },{0,0,0,0,0,0,0x202,0x9C40,0x47A,{1184,3889}},{0,0,0,0,0,0,0x202,0x9C40,0x46D,{1184,3890}},{0,0,0,0,0,0,0x606,0x16EA,0x2198,{3891,3269}},{0,0,0,0,0,0,0,0x1E00,0x113D,{3892,2548}},{0,0,0,0,0,0,0x404,0xD5,0x52F8,{1235,1236}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{1024,1025}},{0,0,0,0,0,0,0x202,0x9C40,0x5D,{1241,1242}},{0,0,0,0,0,0,0xA0A,0x640,0x8A5,{1245,1246}},{0,0,0,0,0,0,0x606,0x9C40,0x53C,{1116,3893}},{0,0,0,0,0,0,0xE0E,0x460,0xAE2,{3894,3895}},{0,0,0,0,0,0,0x202,0x9C40,0x6A,{3896,3897}},{0,0,0,0,0,0,0xE0E,0x9C40,0x6A,{1144,1145}},{0,0,0,0,0,0,0xE0E,0x1158,0x5D,{3898,3899}},{0,0,0,0,0,0,0xE0E,0x9C40,0x140,{3900,3901}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{1123,1124}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{1295,1296}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{1297,1124}},{0,0,0,0,0,0,0x606,0x9C40,0x5D,{3902,3903}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{3904,3905}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{3906,3907}},{0,0,0,0,0,0,0,0x203D,0x157C,{3322,3908}},{0,0,0,0,0,0,0xC0C,0x3E1,0x466,{967,968}},{0,0,0,0,0,0,0x607,0x9C40,0x175,{2575,3909}},{0,0,0,0,0,0,0x808,0x6A,0x5D,{280,924}},{0,0,0,0,0,0,0x404,0x118,0x10F4,{2588,3910}},{0,0,0,0,0,0,0xE0E,0x9C40,0,{3911,3912}},{0,0,0,41,0,0,0,0xFD,0x5D,{693,3913}},{0,0,0,84,0,0,0xE0E,0x99,0x64,{709,3914}},{0,0,0,72,0,0,0xE0E,0x1B8,0x1ED,{3915,3916}},{0,0,0,48,0,0,0xE0E,0xDC,0x5D,{1348,1349}},{0,0,0,0,0,0,0x404,0x88A,0x29A,{3917,3918}},{0,0,0,0,0,0,0,0x9C40,0xA6,{3919,2965}},{0,0,0,0,0,0,0x203,0x9C40,0x564,{2976,3920}},{-12,0,0,0,0,0,0xE0E,0x1226,0x1590,{3921,2983}},{0,0,0,0,0,0,0xC0D,0x9C40,0x5D,{3922,3923}},{0,0,0,0,0,0,0,0x9C40,0x64,{2997,3924}},{0,0,0,0,0,0,0xC0C,0x5BA,0x71,{3011,3925}},{12,0,0,0,0,0,0x808,0x9C40,0x5D,{3018,3926}},{0,0,0,0,0,0,0xA0A,0x9C40,0x251,{3927,3928}},{0,0,0,0,0,0,0,0x9C40,0x272,{3025,3929}},{0,0,0,0,0,0,0,0x9C40,0x286,{3027,3930}},{0,0,0,0,0,0,0,0x9C40,0x25E,{3029,3931}},{0,0,0,0,0,0,0xE0E,0x9C40,0xD5,{3932,3933}},{0,0,0,0,0,0,0,0x9C40,0x488,{98,3934}},{0,0,0,0,0,0,0,0x9C40,0x139,{3048,3935}},{0,0,0,0,0,0,0xC0C,0x9C40,0x47A,{3936,3937}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{3938,3021}},{0,0,0,0,0,0,0xE0E,0x9C40,0x56,{3055,3939}},{0,0,0,0,0,0,0xE0E,0x9C40,0x85,{3057,3940}},{0,0,0,0,0,0,0xE0E,0x9C40,0xE2,{3941,3060}},{0,0,0,0,0,0,0xC0C,0x9C40,0x71,{3942,3082}},{0,0,0,0,0,0,0xC0C,0x9C40,0x6A,{3943,3082}},{0,0,0,0,0,0,0xE0E,0x9C40,0x64,{3089,3944}},{0,0,0,0,0,0,0xE0E,0x9C40,0x6A,{3945,3946}},{0,0,0,0,0,0,0xE0E,0x9C40,0x92,{3947,3085}},{0,0,0,0,0,0,0xE0E,0x9C40,0x6A,{3095,3948}},{-12,0,0,0,0,0,0,0x9C40,0x92,{3949,3100}},{0,0,0,0,0,0,0,0x9C40,0x8C6,{3950,3951}},{0,0,0,0,0,0,0x606,0x9C40,0x265,{3116,3952}},{0,0,0,0,0,0,0,0x2670,0x571,{3953,3121}},{-12,0,0,0,0,0,0x809,0x9C40,0xFD,{3122,3954}},{0,0,0,0,0,0,0xE0E,0xE95,0x1136,{3955,3125}},{0,0,0,0,0,0,0,0x9C40,0x56,{3128,3956}},{0,0,0,0,0,0,0,0x9C40,0x56,{3132,3956}},{0,0,0,0,0,0,0xE0E,0xBCC,0x56,{3957,3958}},{0,0,0,0,0,0,0xE0E,0x1798,0x1115,{3166,3959}},{0,0,0,0,0,0,0,0x279,0x146,{3960,3169}},{-12,0,0,0,0,0,0xC0C,0x9C40,0x6A,{3961,3171}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{3962,3173}},{0,0,0,0,0,0,0xE0E,0x272,0x2DD,{3176,3963}},{0,0,0,60,0,0,0xE0E,0xAD,0xC1,{3964,3191}},{0,0,0,60,0,0,0xE0E,0x2A1,0x251,{3194,3965}},{0,0,0,48,0,0,0xE0E,0x2A1,0x251,{3966,3965}},{0,0,0,60,0,0,0xE0E,0x6E0,0x7D6,{3197,3967}},{0,0,0,60,0,0,0xE0E,0x2A1,0x251,{3199,3968}},{0,0,0,66,0,0,0xE0E,0x12C,0x139,{3201,3969}},{0,0,0,60,0,0,0,0x2A1,0x251,{3970,3965}},{0,0,0,64,0,0,0xE0E,0x29A,0x251,{3966,3965}},{0,0,0,60,0,0,0xE0E,0xF6,0x132,{3214,3971}},{0,0,0,60,0,0,0xE0E,0xBA,0xFD,{3216,3972}},{0,0,0,56,0,0,0xA0A,0xCE,0x5D,{3973,3974}},{0,0,0,53,0,0,0,0x1B1,0x64,{3975,3976}},{0,0,0,60,0,0,0xE0E,0xDC,0xF6,{3977,3223}},{0,0,0,48,0,0,0xE0E,0x12C,0x5D,{3224,3978}},{0,0,0,67,0,0,0xE0E,0x10A,0xD5,{3236,3979}},{0,0,0,60,0,0,0,0x3B9,0x431,{3238,3980}},{0,0,0,72,0,0,0,0x320,0x391,{3238,3980}},{0,0,0,60,0,0,0xE0E,0xAD,0xC1,{3981,3191}},{0,0,0,0,0,0,0x404,0x981,0xACE,{3982,3983}},{0,0,0,24,0,0,0x606,0xB4,0xC1,{3984,3985}},{0,0,0,72,0,0,0xA0A,0x7E,0x85,{258,259}},{0,0,0,36,0,0,0x606,0x8C,0x99,{3986,3987}},{0,0,0,36,0,0,0xE0E,0x8C,0x99,{645,646}},{0,0,0,0,0,0,0x606,0x15A,0x146,{653,654}},{0,0,0,12,0,0,0x606,0x1A4,0x189,{653,654}},{0,0,0,12,0,0,0x808,0x161,0xE2,{3988,3989}},{0,0,0,24,0,0,0x405,0x6A,0x6A,{3990,3991}},{0,0,0,36,0,0,0x405,0x64,0x64,{3990,3991}},{0,0,0,0,0,0,0xA0A,0x3EE,0x424,{3992,3993}},{0,0,0,24,0,0,0x808,0x125,0xC1,{3988,3989}},{0,0,0,88,0,0,0x203,0x459,0x118,{3994,3995}},{0,0,0,88,0,0,0x203,0x2A1,0x118,{3996,3997}},{0,0,0,13,0x4,0,0,0x28D,0x265,{3998,3999}},{0,0,0,0,0,0,0,0x3C0,0x474,{660,661}},{0,0,0,15,0x4,0,0,0x2AE,0x294,{3998,3999}},{0,0,0,0,0,0,0x606,0x11B5,0x862,{4000,4001}},{0,0,0,0,0,0,0x606,0x3C6A,0x1450,{1076,4002}},{0,0,0,0,0,0,0x606,0x1B1C,0x64,{3316,4003}},{0,0,0,0,0,0,0xA0A,0x786,0x189,{1080,1081}},{0,0,0,0,0,0,0x202,0x1AFA,0x221D,{789,4004}},{0,0,0,0,0,0,0,0x1DA9,0x1589,{1084,4005}},{0,0,0,0,0,0,0xA0A,0x495,0x578,{3664,1312}},{0,0,0,0,0,0,0x404,0x4A9,0x57E,{1088,4006}},{0,0,0,0,0,0,0xA0A,0x9C40,0x297C,{1066,4007}},{0,0,0,0,0,0,0xA0A,0x9C40,0xA8C,{1066,1092}},{0,0,0,0,0,0,0xE0E,0x11F1,0x876,{975,976}},{0,0,0,0,0,0,0xA0A,0x9C40,0x56,{4008,1098}},{0,0,0,0,0,0,0x606,0x11EA,0xAC8,{52,1099}},{0,0,0,0,0,0,0x808,0x47CC,0xAE9,{1100,4009}},{0,0,0,0,0,0,0xA0A,0x10ED,0x2EA,{1102,1103}},{0,0,0,0,0,0,0xA0A,0x9C40,0x7E,{4010,4011}},{0,0,0,0,0,0,0xA0A,0xFD,0x5D,{1028,994}},{0,0,0,0,0,0,0xE0E,0x9C40,0xA0,{1022,1023}},{0,0,0,0,0,0,0xC0C,0x9C40,0x535,{1106,1107}},{0,0,0,0,0,0,0x202,0x9C40,0x2AE,{1108,1109}},{0,0,0,0,0,0,0xC0C,0x2C9,0x251,{4012,4013}},{0,0,0,0,0,0,0x606,0x938,0x5D,{1114,1115}},{0,0,0,0,0,0,0x606,0x9C40,0x175,{2557,4014}},{0,0,0,0,0,0,0,0x9C40,0x550,{1118,4015}},{0,0,0,0,0,0,0xC0C,0x9C40,0x51A,{1106,1120}},{0,0,0,0,0,0,0xC0C,0x9C40,0x514,{4016,1122}},{0,0,0,0,0,0,0xA0A,0x9C40,0x2F8,{4017,4018}},{0,0,0,0,0,0,0xE0E,0x1090,0x474,{1126,1127}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{4019,4020}},{0,0,0,0,0,0,0xE0E,0x9C40,0x56,{1035,1036}},{0,0,0,0,0,0,0xA0A,0x9C40,0x50,{4021,4022}},{0,0,0,0,0,0,0xC0C,0x9C40,0x7E,{4023,4024}},{0,0,0,0,0,0,0x202,0x9C40,0x5D,{4025,1137}},{0,0,0,0,0,0,0x202,0x9C40,0x6A,{1275,4026}},{0,0,0,0,0,0,0xA0B,0x3DCC,0x196,{4027,4028}},{0,0,0,0,0,0,0x202,0x2738,0x1136,{973,974}},{0,0,0,0,0,0,0xA0A,0x9C40,0x13F9,{1153,1154}},{0,0,0,0,0,0,0xC0C,0x9C40,0xD4E,{977,4029}},{0,0,0,0,0,0,0x808,0xAF6,0xE16,{4030,4031}},{0,0,0,0,0,0,0x404,0x71C,0x460,{1159,1160}},{0,0,0,0,0,0,0x808,0x9C40,0x4F74,{898,4032}},{0,0,0,0,0,0,0xE0E,0x2335,0x5D,{1131,1132}},{0,0,0,79,0,0,0x606,0x71,0xF0,{2532,4033}},{0,0,0,72,0,0,0x606,0x78,0x139,{2532,4033}},{0,0,0,72,0,0,0,0x64,0x5D,{4034,4035}},{0,0,0,79,0,0,0,0x64,0x5D,{4034,4035}},{0,0,0,35,0,0,0x404,0x898,0x71,{1004,1005}},{0,0,0,38,0,0,0xE0E,0x189,0x2D6,{689,690}},{0,0,0,39,0,0,0xE0E,0x6A,0x5D,{1008,1009}},{0,0,0,49,0,0,0xE0E,0xC1,0x140,{1010,1011}},{0,0,0,60,0,0,0xE0E,0x190,0x140,{1015,1016}},{0,0,0,72,0,0,0xE0E,0x319,0x1F4,{4036,4037}},{0,0,0,53,0,0,0xE0F,0x1F4,0x244,{292,293}},{0,0,0,84,0,0,0xE0E,0xE2,0x99,{4038,1169}},{0,0,0,50,0,0,0xE0E,0x71,0x7E,{298,299}},{0,0,0,66,0,0,0xA0A,0x6A,0x71,{1017,1018}},{0,0,0,0,0,0,0xE0E,0x94C,0xAA6,{4039,4040}},{0,0,0,0,0,0,0xE0E,0x9C40,0x541D,{4041,4041}},{0,0,0,0,0,0,0,0x2BC,0x2AE,{4042,4043}},{0,0,0,0,0,0,0,0x9C40,0x56,{4044,4045}},{0,0,0,0,0,0,0xE0F,0x190,0x564,{3650,3651}},{0,0,0,0,0,0,0x404,0x46E2,0x54F2,{4046,4047}},{0,0,0,0,0,0,0xC0C,0x9C40,0x251,{4048,4049}},{0,0,0,0,0,0,0,0x190D,0x507E,{4050,4051}},{0,0,0,0,0,0,0xC0C,0x9C40,0x251,{4052,4049}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{4053,4054}},{0,0,0,0,0,0,0xE0E,0x111,0x50,{1019,756}},{0,0,0,0,0,0,0xE0E,0x377E,0x42AA,{4055,4056}},{0,0,0,0,0,0,0x606,0x16EA,0x2198,{4057,4058}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{1066,1067}},{0,0,0,0,0,0,0x606,0x9C40,0x5D,{4059,4060}},{0,0,0,0,0,0,0x606,0x9C40,0x1596,{4061,4062}},{0,0,0,0,0,0,0,0x4858,0x555D,{4063,4064}},{0,0,0,0,0,0,0xA0A,0x4808,0x5611,{4065,4066}},{0,0,0,0,0,0,0x808,0x477C,0x5549,{4067,4068}},{0,0,0,0,0,0,0,0x250E,0x1525,{4069,3279}},{0,0,0,0,0,0,0,0x2385,0x64,{4070,987}},{0,0,0,0,0,0,0xA0A,0x9C40,0xCAE,{4071,4072}},{0,0,0,0,0,0,0xE0E,0x3CFD,0x4B28,{4073,4074}},{0,0,0,0,0,0,0,0x9C40,0x152C,{4075,4076}},{0,0,0,0,0,0,0xE0E,0xBF4,0x85,{4077,4078}},{0,0,0,0,0,0,0x606,0x3951,0x445,{4079,4080}},{0,0,0,0,0,0,0xE0E,0xEBD,0xABA,{4081,4082}},{0,0,0,0,0,0,0xE0E,0x1D52,0x154D,{4083,4084}},{0,0,0,0,0,0,0xE0E,0x190,0x2DD,{689,690}},{0,0,0,0,0,0,0xE0F,0x6A,0xBA,{4085,4086}},{0,0,0,0,0,0,0xE0E,0x1F4,0x71,{4077,4087}},{0,0,0,0,0,0,0xA0A,0x3785,0x445,{4079,4080}},{0,0,0,0,0,0,0x607,0x5B4,0x2D6,{4088,4089}},{0,0,0,0,0,0,0x202,0x9C40,0x6A,{4090,4091}},{0,0,0,0,0,0,0x606,0x118,0x466,{2532,4033}},{0,0,0,0,0,0,0,0x6A,0x5D,{4034,4035}},{0,0,0,0,0,0,0,0x13DE,0x146,{4092,4093}},{0,0,0,0,0,0,0xE0E,0x22BD,0x2A9A,{4094,4095}},{0,0,0,0,0,0,0x808,0x1316,0x71,{4096,4097}},{0,0,0,0,0,0,0xE0E,0x111,0x49,{4098,4099}},{0,0,0,0,0,0,0xE0F,0x14D,0x5D,{3623,3649}},{0,0,0,0,0,0,0xC0C,0x16C9,0x2119,{4100,4101}},{0,0,0,0,0,0,0xE0E,0x64,0,{4102,4103}},{0,0,0,0,0,0,0x808,0x9C40,0x258,{4104,4105}},{0,0,0,0,0,0,0x606,0x1219,0x8A5,{4106,4107}},{0,0,0,0,0,0,0,0xD7D,0xE9,{4108,1079}},{0,0,0,0,0,0,0xE0E,0x9C40,0xD84,{4109,4110}},{0,0,0,0,0,0,0x808,0x21C,0x5D,{914,652}},{0,0,0,0,0,0,0xE0E,0x1395,0x156E,{4111,4112}},{0,0,0,0,0,0,0xE0E,0x1158,0x5D,{4113,4114}},{0,0,0,0,0,0,0xA0A,0x41A0,0x4351,{4115,4116}},{0,0,0,0,0,0,0,0x203D,0x157C,{3322,4117}},{0,0,0,0,0,0,0xA0A,0x9C40,0x58C,{4118,4119}},{0,0,0,0,0,0,0x808,0x24FA,0x1504,{4120,4121}},{0,0,0,0,0,0,0x607,0x9C40,0x175,{2575,4122}},{0,0,0,0,0,0,0x404,0x1CFC,0x251,{4123,4124}},{0,0,0,0,0,0,0x203,0x85,0x5D,{4125,4126}},{0,0,0,0,0,0,0xE0E,0x21C,0x5D,{651,652}},{0,0,0,0,0,0,0x808,0x9C40,0x506,{4127,4128}},{0,0,0,0,0,0,0xA0A,0x9C40,0x4DD6,{4129,4130}},{0,0,0,0,0,0,0xE0E,0x21A5,0x5D,{4131,4132}},{0,0,0,0,0,0,0xE0E,0x291E,0x1568,{4111,4133}},{0,0,0,0,0,0,0xA0A,0x9C40,0x55D,{584,4134}},{0,0,0,0,0,0,0xA0A,0x9C40,0x2B5,{4135,4136}},{0,0,0,0,0,0,0,0xF6,0x5D,{4137,4138}},{0,0,0,0,0,0,0x808,0x201,0x258,{659,249}},{0,0,0,35,0x1,0,0x100,0x8C6,0xB18,{2774,4139,256,4140} },{0,0,0,52,0x1,0,0x606,0x78,0x5D,{686,4141,4142,4143} },{0,0,0,48,0x1,0,0xF0E,0x104,0x132,{4144,253,2770,2771} },{0,0,0,58,0,0,0,0x8C,0x1B1,{310,309}},{0,0,0,60,0x1,0,0xF0E,0x104,0x132,{4144,253,2770,2771} },{0,0,0,47,0x1,0,0x707,0x6D2,0x891,{4145,4146,4147,4148} },{0,0,0,43,0x1,0,0x707,0x6C5,0x87D,{4145,4146,4147,4148} },{0,0,0,49,0x1,0,0x707,0x6BE,0x884,{4145,4146,4147,4148} },{0,0,0,51,0x1,0,0x707,0x6C5,0x884,{4145,4146,4147,4148} },{0,0,0,54,0x1,0,0x707,0x6E0,0x89E,{4145,4146,4147,4148} },{0,0,0,57,0x1,0,0x707,0x708,0x8C6,{4145,4146,4147,4148} },{0,0,0,72,0x1,0,0x707,0x5F0,0x75E,{4145,4146,4147,4148} },{0,0,0,60,0x1,0,0x707,0x708,0x8C6,{4145,4146,4147,4148} },{0,0,0,76,0x1,0,0x707,0x5F0,0x765,{4145,4146,4147,4148} },{0,0,0,84,0x1,0,0x707,0x5F0,0x765,{4145,4146,4147,4148} },{0,0,0,36,0x1,0,0x707,0x876,0xA92,{4145,4146,4147,4148} },{0,0,0,65,0x1,0,0,0x125,0x258,{2785,2786,2787,4149} },{0,0,0,84,0x1,0,0xE0E,0x556,0x3B9,{4150,4151,4152,4153} },{0,0,0,83,0,0,0x808,0xDC,0xF6,{274,2793}},{0,0,0,84,0x1,0,0xE0E,0x62C,0x758,{715,716,717,718} },{0,0,0,24,0,0,0xC0C,0x715,0x884,{2797,2528}},{0,0,0,77,0x1,0,0x707,0x5F0,0x75E,{4145,4146,4147,4148} },{0,0,0,60,0x1,0,0xF0E,0x11E,0x1FA,{4154,4155,4156,4157} },{0,0,0,65,0x1,0,0x706,0x20E,0x410,{2805,4158,4156,4159} },{0,0,0,59,0,0,0x606,0x64,0x6A,{326,3646}},{0,0,0,51,0,0,0x606,0x17C,0x481,{326,4160}},{0,0,0,45,0,0,0x606,0x196,0x46D,{326,327}},{0,0,0,71,0,0,0xE0E,0x1B8,0x201,{4161,289}},{0,0,0,60,0,0,0xE0E,0x118,0x5D,{3623,3649}},{0,0,0,58,0,0,0xE0E,0x161,0x466,{3650,3651}},{0,0,0,53,0,0,0xE0E,0x161,0x466,{3652,3651}},{0,0,0,64,0,0,0xE0E,0xDC,0xAD,{747,297}},{0,0,0,71,0,0,0xE0E,0x6A,0x78,{4162,299}},{0,0,0,61,0,0,0xE0E,0x3CD,0x460,{2831,4163}},{0,0,0,61,0,0,0xE0E,0x758,0x898,{2835,4164}},{0,0,0,44,0x1,0,0xD0C,0x118,0x140,{4165,4166,739,739} },{0,0,0,40,0x1,0,0xD0C,0x1C5,0x1B1,{4165,4166,740,740} },{0,0,0,69,0,0,0,0x78,0x140,{310,309}},{0,0,0,68,0,0,0,0x78,0x140,{310,309}},{0,0,0,63,0,0,0,0x8C,0x1B1,{310,309}},{0,0,0,74,0x1,0,0x908,0x17C,0x10A,{4167,4168,4169,4170} },{0,0,0,60,0x1,0,0xB0A,0x3FC,0x44C,{4171,4172,4173,4174} },{0,0,0,80,0x1,0,0x100,0x9C40,0x15A,{4175,4176,4177,4178} },{0,0,0,64,0x1,0,0x100,0x9C40,0x17C,{4179,4180,4181,4182} },{0,0,0,72,0,0,0xE0E,0xC1,0xA0,{747,297}},{0,0,0,78,0x1,0,0x908,0xB18,0x3B2,{4183,4184,4185,4186} },{0,0,0,82,0x1,0,0xA0B,0x618,0x3AC,{2856,2857,2858,2859} },{0,0,0,48,0,0,0x606,0x64,0x6A,{326,3646}},{0,0,0,53,0,0,0x606,0x175,0x47A,{326,4160}},{0,0,0,0,0,0,0x606,0xE52,0x111C,{4187,2931}},{0,0,0,0,0x1,0,0xB0A,0x6D2,0x599,{1095,4188,4189,4190} },{0,0,0,0,0x1,0,0x808,0x9C40,0x50D,{4191,4192,4193,4194} },{0,0,0,0,0,0,0xC0C,0x224C,0x14A0,{4195,1261}},{-12,0,0,0,0,0,0x606,0xDAC,0xAD5,{4196,4197}},{0,0,0,0,0,0,0x606,0x9C40,0x506,{4198,4199}},{0,0,0,0,0,0,0x606,0x9C40,0x556,{4200,4201}},{0,0,0,0,0x1,0,0xF0E,0x9C40,0x7E,{4202,4203,4204,4205} },{0,0,0,0,0,0,0xE0E,0x9C40,0x6A,{4206,4207}},{0,0,0,0,0x1,0,0xD0C,0x9C40,0x154,{1298,4208,4209,4210} },{0,0,0,0,0,0,0xE0E,0x9C40,0x50,{1039,1038}},{12,0,0,0,0x1,0,0xA0A,0x9C40,0x64,{4211,4212,4213,4214} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x6A,{4215,4216,4217,4218} },{12,0,0,0,0x1,0,0xE0E,0x9C40,0x92,{4219,4220,4221,4222} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x4DE,{4223,4224,4225,4226} },{0,0,0,14,0,0,0x808,0xBA,0x5D,{4227,4228}},{0,0,0,35,0,0,0,0xF6,0x5D,{4229,1343}},{0,0,0,19,0,0,0x808,0xA6,0x5D,{4227,4228}},{0,0,0,43,0,0,0xE0E,0x11E,0x146,{4230,4231}},{0,0,0,41,0,0,0x809,0x12C,0x15A,{4232,4233}},{0,0,0,43,0,0,0x809,0xFD,0x11E,{4232,4233}},{0,0,0,45,0,0,0x809,0xF6,0x125,{4232,4233}},{0,0,0,47,0,0,0x809,0xF6,0x11E,{4232,4233}},{0,0,0,0,0x1,0,0x606,0x3929,0x466,{4234,4235,4236,4237} },{0,0,0,0,0,0,0x606,0x1CE1,0x1158,{4238,4239}},{0,0,0,0,0,0,0x606,0x132A,0x2DD,{1180,4240}},{0,0,0,0,0,0,0x808,0x13DE,0x84E,{4241,4242}},{0,0,0,0,0,0,0x808,0x535,0x17C,{4243,4244}},{0,0,0,0,0,0,0xE0E,0x826,0xB0A,{3256,4245}},{0,0,0,0,0,0,0x606,0xE6D,0x2C2,{4246,4247}},{0,0,0,0,0,0,0x808,0xD69,0x10B1,{2539,4248}},{0,0,0,0,0,0,0x808,0x1219,0x571,{4249,4250}},{0,0,0,0,0,0,0xE0E,0x395E,0x8A5,{4251,4252}},{0,0,0,0,0,0,0x202,0x9C40,0x585,{4253,4254}},{0,0,0,0,0,0,0x404,0xE66,0x251,{4255,4256}},{0,0,0,0,0,0,0xE0E,0x370,0x592,{4257,4258}},{0,0,0,0,0,0,0x808,0x1220,0x8B2,{4259,3482}},{0,0,0,0,0,0,0,0xE3E,0x1108,{4260,4261}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{4262,4263}},{0,0,0,0,0,0,0x809,0x9C40,0x64,{4264,4265}},{0,0,0,0,0,0,0x202,0x9C40,0x46D,{1184,4266}},{0,0,0,0,0,0,0x1,0x9C40,0x6A,{4267,4268}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{4269,4270}},{0,0,0,0,0,0,0xE0E,0x9C40,0x6A,{4271,4272}},{0,0,0,0,0,0,0xE0E,0x9C40,0x64,{4273,4274}},{0,0,0,0,0,0,0xE0E,0x9C40,0x6A,{4275,4272}},{0,0,0,0,0,0,0xA0A,0x74A,0x5C1,{1095,1096}},{0,0,0,0,0,0,0x606,0x348,0x2C9,{4276,4277}},{0,0,0,0,0,0,0x808,0x82D,0x92,{4278,4279}},{0,0,0,0,0,0,0x606,0x38A,0x5D,{4280,4281}},{0,0,0,0,0,0,0xE0E,0x111,0x49,{755,756}},{0,0,0,0,0,0,0x202,0x9C40,0xAA6,{4282,4283}},{0,0,0,0,0,0,0xA0A,0x1284,0xDC,{4284,4285}},{0,0,0,0,0,0,0x808,0x2C2,0xE2,{4286,1229}},{0,0,0,0,0,0,0xC0C,0x9C40,0x2F1,{4287,4288}},{0,0,0,0,0,0,0,0xF56,0x283C,{4289,4290}},{12,0,0,0,0,0,0x606,0xD41,0x654,{4291,4292}},{0,0,0,0,0,0,0xA0A,0x9C40,0x50,{4293,4294}},{0,0,0,0,0,0,0xE0E,0x9C40,0x17C,{4295,441}},{0,0,0,0,0x1,0,0x706,0x9C40,0x1ED,{4296,4297,4298,4299} },{0,0,0,0,0,0,0xC0C,0x48E,0x870,{4300,4301}},{0,0,0,0,0x1,0,0x808,0x8AC,0x550,{4302,4303,4304,4305} },{0,0,0,0,0,0,0,0x9C40,0x8E8,{4306,4307}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{4308,4309}},{0,0,0,0,0x1,0,0xF0E,0x9C40,0x189,{4310,4311,4312,4313} },{12,0,0,0,0x1,0,0x908,0x9C40,0x294,{4314,4315,4316,4317} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x64,{4318,4319,4320,4321} },{0,0,0,0,0,0,0xE0E,0x9C40,0x50,{1037,1038}},{0,0,0,0,0,0,0xC0C,0x9C40,0x56,{4322,4323}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{4324,4325}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{4326,1025}},{0,0,0,0,0x1,0,0xF0E,0x9C40,0x182,{4327,4328,4329,4330} },{0,0,0,0,0,0,0,0x9C40,0x161,{4331,4332}},{0,0,0,0,0x1,0,0xD0C,0x9C40,0x521,{4333,4334,4335,4336} },{-12,0,0,0,0x1,0,0xE0E,0x9C40,0x15A,{4337,4338,4339,4340} },{0,0,0,0,0x1,0,0x706,0x6B8,0x2B5,{4341,4342,4343,4344} },{0,0,0,0,0x1,0,0,0x452,0x189,{4345,4346,4347,4348} },{0,0,0,0,0x1,0,0,0xE38,0x140,{4349,4350,4351,4352} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x71,{4327,4353,4354,4355} },{0,0,0,33,0,0,0xE0E,0x12C,0x2DD,{929,4356}},{0,0,0,38,0x1,0,0xB0A,0xCE,0x5D,{4357,4358,4359,4360} },{0,0,0,38,0,0,0xC0D,0x6A,0x71,{4361,4362}},{0,0,0,38,0,0,0xE0E,0x146,0x5D,{4363,4364}},{0,0,0,40,0,0,0x404,0x42,0x5D,{4365,4366}},{0,0,0,41,0x1,0,0xE0E,0x125,0x15A,{4367,4368,4369,4370} },{0,0,0,0,0,0,0x606,0x85,0x92,{4371,4372}},{0,0,0,41,0,0,0x606,0x85,0x92,{4371,4372}},{0,0,0,48,0,0,0x809,0xF0,0x11E,{4232,4233}},{0,0,0,17,0,0,0xE0E,0x109D,0x1421,{4373,4374}},{0,0,0,50,0,0,0x809,0xF0,0x118,{4232,4233}},{0,0,0,45,0,0,0x606,0x78,0x85,{4371,4372}},{0,0,0,254,0x4,0,0xA0A,0x6,0,{4375,4376}},{0,0,0,60,0,0,0xE0E,0xE9,0xC1,{4377,4378}},{0,0,0,56,0,0,0xE0E,0xE2,0xC1,{4379,4380}},{0,0,0,60,0,0,0x607,0x8C,0x85,{4381,4382}},{0,0,0,55,0,0,0x607,0x92,0x85,{4381,4382}},{0,0,0,63,0,0,0x809,0x10A,0x132,{4383,4384}},{0,0,0,57,0,0,0x607,0xAD,0xC1,{4385,4386}},{0,0,0,0,0,0,0xE0E,0x9C40,0x44C,{4387,4388}},{0,0,0,0,0,0,0,0x9C40,0xD2D,{4389,4390}},{0,0,0,0,0,0,0x202,0x9C40,0xA7E,{4391,4392}},{0,0,0,0,0,0,0x202,0x9C40,0x47A,{4393,4394}},{0,0,0,0,0,0,0,0x9C40,0x1151,{4395,4396}},{0,0,0,0,0,0,0xA0A,0x1310,0x140,{1224,1225}},{0,0,0,0,0,0,0x606,0x3C56,0x142E,{1076,1247}},{0,0,0,0,0,0,0,0x9C40,0x182,{4397,4398}},{0,0,0,0,0,0,0xC0C,0xED1,0x5D,{1266,1267}},{0,0,0,0,0,0,0xA0A,0x9C40,0x10D9,{4399,4400}},{0,0,0,0,0,0,0x808,0x9C40,0x47A,{4401,4402}},{0,0,0,0,0,0,0,0x475A,0x5535,{4403,4404}},{0,0,0,0,0,0,0xA0A,0x4A9,0x585,{2948,4405}},{0,0,0,55,0,0,0x808,0xAD,0x5D,{955,845}},{0,0,0,0,0,0,0x1,0xB25,0xADC,{4406,1381}},{0,0,0,0,0,0,0x606,0x4BD,0x64,{1418,1419}},{0,0,0,0,0,0,0xA0A,0x9C40,0xDC,{1432,1433}},{0,0,0,0,0,0,0xC0C,0x9D1,0x146,{4407,1437}},{0,0,0,0,0,0,0xE0E,0x9C40,0x189,{1463,1464}},{0,0,0,0,0,0,0xE0E,0x9C40,0x8C,{1475,1476}},{0,0,0,0,0,0,0xE0E,0x9C40,0x92,{1477,1478}},{0,0,0,0,0,0,0x808,0x9C40,0x78,{1481,1482}},{0,0,0,0,0,0,0xE0E,0x9C40,0x175,{1485,1486}},{0,0,0,0,0,0,0xE0E,0x9C40,0x2C2,{1487,1488}},{0,0,0,0,0,0,0xC0C,0x9C40,0x6A,{1493,1494}},{0,0,0,0,0,0,0xA0A,0x9C40,0x7E,{1501,1502}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{1521,1522}},{0,0,0,0,0,0,0x809,0x9C40,0xC1,{1533,1534}},{0,0,0,0,0,0,0x404,0x269E,0x571,{1547,1548}},{0,0,0,0,0,0,0xA0A,0x9C40,0x55D,{1551,1552}},{0,0,0,0,0,0,0x607,0x9C40,0x52E,{1555,1556}},{0,0,0,0,0,0,0xE0E,0x208,0xC1,{4408,4409}},{0,0,0,0,0,0,0xE0E,0x57E,0x190,{4410,4411}},{0,0,0,0,0,0,0xC0C,0x9C40,0xBA,{4412,4413}},{0,0,0,0,0,0,0xA0A,0x235D,0xE9,{4414,13}},{0,0,0,0,0,0,0xA0A,0xE7A,0xDC,{4415,4416}},{0,0,0,0,0,0,0xC0C,0x4524,0xDC,{4417,4418}},{0,0,0,0,0,0,0xC0C,0x242C,0xE2,{4419,4420}},{0,0,0,0,0,0,0xA0A,0x9C40,0x6A,{4421,4422}},{0,0,0,0,0,0,0xA0A,0x9C40,0x2C9,{4423,4424}},{0,0,0,0,0,0,0xE0E,0x132,0x6A,{1613,1614}},{0,0,0,28,0,0,0xE0E,0x132,0x6A,{1613,1614}},{0,0,0,29,0,0,0x808,0xE2,0xD5,{1615,4425}},{0,0,0,31,0,0,0,0x6A,0x71,{676,1617}},{0,0,0,32,0,0,0,0x85,0x71,{678,679}},{0,0,0,33,0,0,0,0x11E,0x64,{680,681}},{0,0,0,34,0,0,0x1,0xB1E,0xACE,{4406,1381}},{0,0,0,29,0,0,0x404,0xF6,0x71,{1619,1620}},{0,0,0,55,0,0,0,0x64,0x64,{676,1617}},{0,0,0,48,0,0,0xE0E,0xF0,0x85,{1623,1624}},{0,0,0,58,0,0,0xE0E,0x92,0x5D,{1613,1614}},{0,0,0,60,0,0,0xE0E,0xF0,0x85,{1623,1624}},{0,0,0,45,0,0,0x606,0xA6,0xBA,{1625,4426}},{0,0,0,43,0,0,0x606,0xA6,0xBA,{1625,4426}},{0,0,0,73,0,0,0x404,0x5A6,0xA0,{1627,4427}},{0,0,0,72,0,0,0xE0E,0x362,0x744,{4428,4429}},{0,0,0,76,0,0,0xE0E,0x4D1,0x6F4,{707,4430}},{0,0,0,84,0,0,0xE0E,0x362,0x744,{4428,4429}},{0,0,0,36,0,0,0xE0E,0x6FA,0xA7E,{707,4430}},{0,0,0,65,0,0,0,0x132,0x140,{813,4431}},{0,0,0,83,0,0,0x808,0xC1,0xAD,{4432,1632}},{0,0,0,50,0,0,0xC0C,0x3C0,0x146,{4433,4434}},{0,0,0,77,0,0,0xE0E,0x4D1,0x6ED,{707,4430}},{0,0,0,55,0,0,0,0x85,0x85,{1633,1634}},{0,0,0,60,0,0,0,0xB4,0x14D,{4435,1636}},{0,0,0,50,0,0,0,0x85,0x85,{1633,1634}},{0,0,0,42,0,0,0xE0E,0x279,0x2DD,{4436,1638}},{0,0,0,46,0,0,0xE0E,0x201,0x251,{4436,1638}},{0,0,0,71,0,0,0x606,0x1B1,0x1FA,{4437,4438}},{0,0,0,60,0,0,0x606,0x1FA,0x251,{4437,4438}},{0,0,0,58,0,0,0xE0E,0xDC,0x78,{4408,4409}},{0,0,0,53,0,0,0xE0E,0x11E,0x85,{4408,4409}},{0,0,0,91,0,0,0xE0E,0xBA,0xC8,{731,4439}},{0,0,0,61,0,0,0x404,0xE2,0x5D,{4440,4441}},{0,0,0,61,0,0,0,0x37D,0x64,{4442,4443}},{0,0,0,44,0,0,0xA0A,0x78,0x64,{829,4444}},{0,0,0,40,0,0,0x808,0x3A5,0x64,{1646,4445}},{0,0,0,69,0,0,0x808,0x92,0x64,{4446,4447}},{0,0,0,68,0,0,0,0x99,0x5D,{680,681}},{0,0,0,63,0,0,0,0xB4,0x5D,{680,681}},{0,0,0,74,0,0,0,0xA0,0x64,{1650,4448}},{0,0,0,60,0,0,0,0x118,0x125,{4449,4450}},{0,0,0,80,0,0,0x606,0x99,0x5D,{1654,1655}},{0,0,0,64,0,0,0x404,0x369,0x3E1,{4451,4452}},{0,0,0,73,0,0,0x404,0x2DD,0x334,{4451,4452}},{0,0,0,70,0,0,0x404,0x2DD,0x33A,{4451,4452}},{0,0,0,68,0,0,0,0x56,0x5D,{676,1617}},{0,0,0,48,0,0,0,0x64,0x6A,{676,1617}},{0,0,0,0,0,0,0x606,0x9C40,0x910,{4453,4454}},{0,0,0,0,0,0,0x808,0xC8D,0xF2E,{4455,4456}},{0,0,0,0,0,0,0,0x708,0x869,{4457,4458}},{0,0,0,0,0,0,0x808,0x1D02,0x2238,{2539,4459}},{0,0,0,0,0,0,0x404,0x9C40,0x160E,{4460,4461}},{0,0,0,0,0,0,0x606,0x9C40,0x51A,{4462,4463}},{0,0,0,0,0,0,0x606,0x9C40,0x2AE,{4464,4465}},{0,0,0,0,0,0,0,0x9C40,0xBA,{4457,4466}},{0,0,0,0,0,0,0xA0A,0x128A,0xE2,{1020,4285}},{0,0,0,0,0,0,0x404,0x1831,0x2D0,{4467,4468}},{0,0,0,0,0,0,0x404,0x1838,0x2D0,{4469,4468}},{0,0,0,0,0,0,0x202,0x9C40,0x50,{4470,4471}},{12,0,0,0,0,0,0xA0A,0x632,0x64,{4472,4473}},{12,0,0,0,0,0,0xC0C,0x230,0x5D,{4474,4475}},{0,0,0,0,0,0,0x1,0x9C40,0x97A,{4476,4477}},{0,0,0,0,0,0,0x606,0x9C40,0xAD,{4478,4479}},{0,0,0,0,0,0,0,0x9C40,0x5D,{4480,4481}},{0,0,0,0,0,0,0x606,0x9C40,0x53C,{4482,4483}},{0,0,0,0,0,0,0xA0A,0x11E,0xE2,{4484,4485}},{-24,0,0,0,0,0,0x404,0x182A,0x2D0,{4486,4468}},{0,0,0,0,0,0,0x606,0x9C40,0x53C,{2557,4483}},{0,0,0,0,0,0,0x606,0x9C40,0x514,{4487,4488}},{0,0,0,0,0,0,0x606,0x9C40,0x53C,{2557,3893}},{0,0,0,0,0,0,0x606,0x9C40,0x2A8,{4489,4490}},{0,0,0,0,0,0,0x606,0x9C40,0x500,{4491,4492}},{0,0,0,0,0,0,0x405,0x9C40,0x175,{4493,4494}},{0,0,0,0,0,0,0x405,0x9C40,0x4DE,{4495,4496}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{4497,1124}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{4498,1124}},{0,0,0,0,0,0,0xA0B,0x9C40,0x15A,{4499,4500}},{0,0,0,0,0,0,0xA0A,0xF06,0x5D,{4501,4502}},{0,0,0,0,0,0,0xA0A,0x9C40,0x56,{4503,4504}},{12,0,0,0,0,0,0xE0E,0x9C40,0xD5,{4505,4506}},{0,0,0,0,0,0,0xE0E,0x9C40,0x83A,{4507,4508}},{0,0,0,0,0,0,0xE0E,0x9C40,0x1046,{4509,4510}},{0,0,0,0,0,0,0x606,0x9C40,0xCE,{4511,4512}},{0,0,0,0,0,0,0x808,0x9C40,0xAD,{4513,4514}},{0,0,0,0,0,0,0,0x1881,0x157C,{4515,4516}},{0,0,0,0,0,0,0x606,0x4970,0xAC1,{4517,4518}},{0,0,0,0,0,0,0x606,0x9C40,0x585,{4519,4520}},{0,0,0,0,0,0,0,0x9C40,0x7B5,{4521,4522}},{0,0,0,0,0,0,0xE0E,0x9C40,0x182,{4523,4524}},{0,0,0,0,0,0,0xE0E,0x115E,0x13D8,{4525,4526}},{0,0,0,30,0,0,0xE0E,0x21C,0x64,{651,652}},{0,0,0,31,0,0,0xE0E,0x196,0x5D,{651,652}},{0,0,0,32,0,0,0xE0E,0x196,0x5D,{651,652}},{0,0,0,33,0,0,0xE0E,0x196,0x5D,{651,652}},{0,0,0,34,0,0,0xE0E,0x196,0x5D,{651,652}},{0,0,0,35,0,0,0xE0E,0x19D,0x5D,{651,652}},{0,0,0,36,0,0,0xE0E,0x196,0x5D,{651,652}},{0,0,0,37,0,0,0xE0E,0x19D,0x5D,{651,652}},{0,0,0,38,0,0,0xE0E,0x196,0x5D,{651,652}},{0,0,0,39,0,0,0xE0E,0x196,0x5D,{651,652}},{0,0,0,40,0,0,0xE0E,0x196,0x5D,{651,652}},{0,0,0,41,0,0,0xE0E,0x196,0x5D,{651,652}},{0,0,0,42,0,0,0xE0E,0x196,0x5D,{651,652}},{0,0,0,43,0,0,0xE0E,0x132,0x5D,{651,652}},{0,0,0,44,0,0,0xE0E,0x139,0x5D,{651,652}},{0,0,0,45,0,0,0xE0E,0x139,0x5D,{651,652}},{0,0,0,46,0,0,0xE0E,0x139,0x5D,{651,652}},{0,0,0,47,0,0,0xE0E,0x139,0x5D,{651,652}},{0,0,0,48,0,0,0xE0E,0x132,0x5D,{651,652}},{0,0,0,49,0,0,0xE0E,0x139,0x64,{651,652}},{0,0,0,84,0,0,0xE0E,0x14D,0x730,{942,710}},{0,0,0,84,0,0,0xE0E,0x1B1,0x1ED,{3915,3916}},{0,0,0,55,0,0,0xA0A,0x64,0,{4527,4528}},{0,0,0,36,0,0,0x808,0x190,0x1BE,{4529,4530}},{0,0,0,38,0,0,0xE0E,0x132,0x2C9,{4531,4532}},{0,0,0,60,0,0,0xE0E,0x118,0x140,{666,933}},{0,0,0,38,0,0,0xC0C,0xC8,0xE2,{3607,4533}},{0,0,0,17,0,0,0x404,0x1831,0x2B5,{4486,4468}},{0,0,0,18,0,0,0x404,0x1831,0x2D0,{4486,4468}},{0,0,0,19,0,0,0x404,0x1831,0x2C9,{4486,4468}},{0,0,0,20,0,0,0x404,0x1831,0x2D0,{4486,4468}},{0,0,0,21,0,0,0x404,0x182A,0x2D0,{4486,4468}},{0,0,0,22,0,0,0x404,0x1831,0x2D0,{4486,4468}},{0,0,0,23,0,0,0x404,0x1831,0x2D0,{4486,4468}},{0,0,0,24,0,0,0x404,0x1831,0x2D0,{4486,4468}},{0,0,0,25,0,0,0x404,0x1831,0x2D0,{4486,4468}},{0,0,0,26,0,0,0x404,0x1831,0x2D0,{4486,4468}},{0,0,0,27,0,0,0x404,0x1831,0x2B5,{4486,4468}},{0,0,0,28,0,0,0x404,0x1831,0x2AE,{4486,4468}},{0,0,0,29,0,0,0x404,0x182A,0x2B5,{4486,4468}},{0,0,0,84,0,0,0x808,0x1AA,0x1E6,{4534,4535}},{0,0,0,48,0,0,0xC0C,0x118,0xC1,{4536,4537}},{0,0,0,65,0,0,0xE0E,0x488,0x474,{929,4538}},{0,0,0,65,0,0,0xE0E,0x736,0x891,{4539,4540}},{0,0,0,55,0,0,0x606,0x1BE,0x481,{3341,4541}},{0,0,0,41,0,0,0x606,0x236,0x571,{3341,4541}},{0,0,0,63,0,0,0xE0E,0xF6,0x64,{651,652}},{0,0,0,55,0,0,0xE0E,0xF6,0x5D,{651,652}},{0,0,0,55,0,0,0xA0A,0xA1A,0x251,{4542,4543}},{0,0,0,53,0,0,0xA0A,0xA1A,0x251,{4542,4543}},{0,0,0,50,0,0,0xC0C,0x118,0xC1,{4536,4537}},{0,0,0,84,0,0,0xE0E,0x2A1,0x744,{929,934}},{0,0,0,74,0,0,0xE0E,0x2A1,0x74A,{929,934}},{0,0,0,84,0,0,0xE0E,0x61E,0x744,{929,931}},{0,0,0,74,0,0,0xE0E,0x62C,0x74A,{929,931}},{0,0,0,84,0,0,0xE0E,0x1B1,0x5D,{4544,4545}},{0,0,0,74,0,0,0,0x78,0x5D,{957,958}},{0,0,0,48,0,0,0xE0E,0x1F4,0x23D,{3420,3421}},{0,0,0,36,0,0,0xE0E,0x25E,0x2BC,{3420,3421}},{0,0,0,74,0,0,0xE0E,0x279,0x6D9,{929,4546}},{0,0,0,0,0,0,0x606,0x1C76,0x5D,{4547,4548}},{0,0,0,0,0,0,0x808,0x9C40,0x1172,{4549,4550}},{0,0,0,0,0,0,0x808,0x9C40,0x56,{4551,4552}},{0,0,0,0,0,0,0xA0A,0x1220,0x5D,{4553,4554}},{0,0,0,0,0x1,0,0xB0A,0x1C0C,0x161,{4555,4556,4557,4558} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xBA,{4559,4560,4561,4562} },{0,0,0,0,0,0,0,0x23F0,0xE2,{4563,4564}},{0,0,0,0,0x1,0,0x504,0x1BA1,0x5D,{4565,4566,4567,4568} },{0,0,0,0,0,0,0x404,0x9C40,0x5D,{4569,4570}},{0,0,0,0,0,0,0x404,0x9C40,0x50,{4571,4572}},{0,0,0,0,0x1,0,0x908,0x9C40,0x5D,{4573,4574,4575,4576} },{0,0,0,0,0,0,0x404,0x9C40,0x41D,{4577,4578}},{0,0,0,0,0x1,0,0xA0A,0x9C40,0x6A,{4579,4580,2647,162} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x56,{4581,4582,4583,4584} },{0,0,0,0,0,0,0x808,0x9C40,0x495,{177,4585}},{0,0,0,0,0,0,0x202,0xFBA,0x64,{4586,4587}},{0,0,0,0,0x1,0,0x404,0x38B1,0x6A,{4588,4589,4590,4591} },{0,0,0,0,0,0,0xA0A,0x9C40,0x56,{4592,3104}},{0,0,0,0,0x1,0,0xD0C,0x9C40,0x5D,{4593,4594,4595,4596} },{0,0,0,0,0,0,0x606,0xFBA,0x64,{4597,4598}},{0,0,0,0,0,0,0x606,0x9C40,0x3C,{4599,4600}},{0,0,0,0,0,0,0x808,0x9C40,0x56,{4601,4602}},{0,0,0,0,0x1,0,0xB0A,0x9C40,0x175,{4603,4604,4605,4606} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xD5,{442,4607,443,4608} },{0,0,0,0,0,0,0x202,0x9C40,0x2C2,{3502,84}},{0,0,0,0,0x1,0,0x908,0x9C40,0x2A1,{442,76,4609,4610} },{0,0,0,0,0,0,0x202,0x1CCD,0x5D,{4611,4612}},{0,0,0,0,0x1,0,0xF0E,0x9C40,0x190,{460,461,4613,463} },{0,0,0,0,0,0,0x808,0x9C40,0x92,{4614,4615}},{0,0,0,0,0,0,0x202,0x9C40,0x182,{3502,4616}},{0,0,0,0,0x1,0,0xE0E,0x4211,0x118D,{4617,4618,4619,4620} },{0,0,0,0,0x1,0,0xB0B,0x2BC,0xC8,{471,4621,4622,4623} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x5D,{4624,4625,4626,4627} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x64,{4628,4629,4630,4631} },{0,0,0,0,0,0,0xC0C,0x9C40,0x132,{4632,4633}},{0,0,0,0,0,0,0xC0C,0x9C40,0x8C,{106,4634}},{0,0,0,0,0x1,0,0xF0E,0x9C40,0x64,{4635,4636,4637,4638} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x5D,{4639,4640,4641,4642} },{0,0,0,0,0,0,0x808,0x9C40,0x5D,{4643,4644}},{0,0,0,0,0x1,0,0x908,0x9C40,0x6A,{4645,4646,4647,4648} },{0,0,0,0,0x1,0,0x908,0x9C40,0x5D,{4649,4650,4651,4652} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x1F4,{4653,4654,4655,4656} },{0,0,0,0,0,0,0x606,0x9C40,0x56,{4657,4658}},{0,0,0,0,0,0,0xA0A,0x9C40,0x7E,{4659,520}},{0,0,0,0,0,0,0x404,0x9C40,0xE2,{4660,4661}},{0,0,0,0,0x1,0,0x202,0x9C40,0x5D,{4662,4663,4664,4665} },{0,0,0,0,0,0,0x202,0x9C40,0x5D,{4666,4667}},{0,0,0,0,0x1,0,0xD0C,0x9C40,0x71,{4668,4669,4670,4671} },{0,0,0,0,0,0,0,0x9C40,0xD5,{4672,4673}},{0,0,0,0,0,0,0x405,0x9C40,0x5D,{4674,544}},{0,0,0,0,0,0,0,0x37A6,0x6A,{4675,4676}},{0,0,0,0,0,0,0xA0A,0x9C40,0x201,{4677,4678}},{0,0,0,0,0x1,0,0xF0E,0x9C40,0x64,{4679,4680,4681,4682} },{0,0,0,0,0x1,0,0x908,0x9C40,0x6A,{4683,4684,559,4685} },{0,0,0,0,0x1,0,0xB0A,0x46D,0x5D,{4686,4687,4688,4689} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x11E4,{171,4690,4691,566} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x265,{567,4692,4693,4694} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x416,{4695,4696,4697,4698} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x452,{2710,4699,179,4700} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x410,{4701,4696,4702,4703} },{0,0,0,0,0x1,0,0xD0C,0x46D,0x542,{2717,2718,4704,4705} },{0,0,0,0,0x1,0,0x706,0x9C40,0xA28,{4706,4707,4708,4709} },{0,0,0,0,0,0,0xC0C,0x9C40,0x76C,{2723,4710}},{0,0,0,0,0x1,0,0xB0A,0x9C40,0xA7E,{4711,4712,4713,4714} },{0,0,0,0,0,0,0x606,0x9C40,0x56A,{4715,4716}},{0,0,0,0,0,0,0x404,0x730,0x5D,{4717,4718}},{0,0,0,0,0x1,0,0x908,0x6FA,0x848,{4719,4720,4721,4722} },{0,0,0,0,0x1,0,0xC0C,0x4232,0x1130,{4723,4724,4725,4620} },{0,0,0,0,0,0,0xA0A,0x9C40,0x8F5,{4726,4727}},{0,0,0,0,0x1,0,0xF0E,0xD41,0x56,{4728,4729,4730,4731} },{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{4732,4733}},{0,0,0,0,0x1,0,0xE0E,0x9C40,0x5D,{4734,4735,4736,4737} },{0,0,0,0,0,0,0xE0E,0x98E,0x85,{4738,4739}},{0,0,0,0,0,0,0xC0C,0x9C40,0x2BC,{4740,4741}},{0,0,0,0,0x1,0,0x505,0x3C0,0x6A,{4742,4743,4744,4745} },{0,0,0,0,0,0,0,0x9C40,0x2AE,{4746,4747}},{-36,0,0,0,0x1,0,0x504,0x9C40,0x5D,{4748,4749,4750,4749} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x5D,{4751,4752,4753,4754} },{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{4755,4756}},{0,0,0,35,0,0,0x808,0x272,0x5D,{4757,4758}},{0,0,0,35,0,0,0x808,0x132,0x5D,{248,4759}},{0,0,0,52,0,0,0x404,0x78,0x5D,{4760,4761}},{0,0,0,60,0,0,0xC0C,0x118,0x5D,{4762,4763}},{0,0,0,58,0,0,0x202,0x6A,0x5D,{4764,4765}},{0,0,0,60,0,0,0xC0C,0x17C,0x5D,{4766,4767}},{0,0,0,50,0,0,0x404,0x668,0x5D,{4768,4769}},{0,0,0,43,0,0,0xE0E,0x99,0x5D,{4770,4771}},{0,0,0,55,0,0,0x404,0x668,0x5D,{4768,4769}},{0,0,0,43,0,0,0xA0A,0xA0,0xA6,{258,2776}},{0,0,0,50,0,0,0x404,0x3C6,0x5D,{4772,4773}},{0,0,0,43,0,0,0xE0E,0x1BE,0x5D,{4774,4775}},{0,0,0,53,0,0,0x404,0x3D4,0x5D,{4772,4773}},{0,0,0,57,0,0,0x404,0x2BC,0x5D,{4772,4773}},{0,0,0,72,0,0,0xC0C,0x305,0x5D,{4776,4777}},{0,0,0,60,0,0,0x404,0x2B5,0x5D,{4772,4773}},{0,0,0,76,0,0,0xE0E,0x320,0x5D,{701,702}},{0,0,0,84,0,0,0xE0E,0x2C2,0x5D,{4778,4779}},{0,0,0,42,0,0,0x606,0x49C,0x5D,{4780,4781}},{0,0,0,65,0,0,0xE0E,0x125,0x5D,{4782,4783}},{0,0,0,84,0,0,0xE0E,0x175,0x5D,{4784,4785}},{0,0,0,84,0,0,0xC0C,0x535,0x5D,{4786,4787}},{0,0,0,24,0,0,0xC0C,0x3CD,0x64,{4788,4789}},{0,0,0,77,0,0,0xE0E,0x320,0x5D,{721,722}},{0,0,0,58,0,0,0xE0F,0x1AA,0x5D,{3650,4790}},{0,0,0,53,0,0,0xE0F,0x1AA,0x5D,{4791,4792}},{0,0,0,64,0,0,0xE0E,0xC8,0x5D,{296,4793}},{0,0,0,71,0,0,0xE0E,0x71,0x5D,{4794,4795}},{0,0,0,44,0,0,0xE0E,0x2F8,0x5D,{4796,4797}},{0,0,0,40,0,0,0x808,0x1CC,0x56,{4798,4799}},{0,0,0,69,0,0,0x1,0x78,0x5D,{308,4800}},{0,0,0,60,0,0,0,0x23D,0x5D,{4801,4802}},{0,0,0,80,0,0,0x808,0xDC,0x5D,{4803,4804}},{0,0,0,64,0,0,0x808,0xA14,0x5D,{4805,4806}},{0,0,0,72,0,0,0xE0E,0x78,0x56,{4807,4808}},{0,0,0,70,0,0,0xE0E,0x320,0x5D,{4809,4810}},{0,0,0,48,0,0,0x607,0xAD,0x5D,{4811,4812}},{0,0,0,53,0,0,0x404,0x3D4,0x64,{4813,4814}},{0,0,0,0,0x1,0,0xE0F,0x9C40,0x42A,{4815,4816,4817,4818} },{0,0,0,0,0x1,0,0x908,0x2EE6,0xDC,{4819,4820,4821,4822} },{0,0,0,0,0x1,0,0,0x17C,0x8C,{4823,4824,4825,4826} },{0,0,0,0,0x1,0,0xE0E,0x39AE,0x5D,{4827,4828,4829,4830} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x5D,{4831,4832,4833,4834} },{-12,0,0,0,0x1,0,0x908,0x9C40,0x71,{372,373,4835,4836} },{0,0,0,0,0x1,0,0x808,0x3CD,0x140,{401,4837,4838,4839} },{0,0,0,0,0x1,0,0x908,0x1082,0xE2,{4840,4841,407,4842} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x5D,{4843,4844,4845,4846} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x5D,{4827,4847,4848,4849} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x5D,{4850,4851,4852,4853} },{0,0,0,0,0x1,0,0x606,0x805,0xE2,{4854,4855,4856,4857} },{0,0,0,0,0x1,0,0x706,0x500,0x5D,{4858,4859,2654,2655} },{0,0,0,0,0x1,0,0xA0A,0x23D,0xDC,{4860,4861,4862,4863} },{0,0,0,0,0x1,0,0x302,0xD2D,0x46D,{89,4864,458,4865} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x4F9,{2661,96,95,4866} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x71,{4668,4867,4868,4671} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x8C,{545,546,547,4869} },{0,0,0,0,0x1,0,0,0x9C40,0x92,{4870,4871,4872,4873} },{0,0,0,0,0x1,0,0x302,0x9C40,0x5D,{4874,4875,4876,4877} },{0,0,0,0,0x1,0,0xB0A,0x736,0x64,{4878,4879,4880,4881} },{0,0,0,0,0x1,0,0x302,0x9C40,0x5D,{4882,4883,4876,4877} },{0,0,0,0,0x1,0,0x100,0x9C40,0x3C6,{2714,184,4884,4885} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x175,{4886,4887,4888,4889} },{0,0,0,0,0x1,0,0x908,0x6FA,0x848,{4890,4720,4891,4722} },{0,0,0,0,0x1,0,0x1,0x2DD,0x5D,{4892,4893,4894,4895} },{0,0,0,0,0x1,0,0,0x11E,0x5D,{4896,4893,4897,4898} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x5D,{4899,4900,4901,4902} },{0,0,0,0,0x1,0,0xF0E,0x55D,0xA8C,{4903,4904,4905,4906} },{0,0,0,0,0x1,0,0xB0A,0x5CE,0xA71,{4907,4908,4909,4910} },{1,0,0,0,0x1,0,0xF0F,0x9C40,0x49,{4911,4912,4913,4914} },{0,0,0,0,0x1,0,0xE0E,0x7FE,0x92,{639,4915,641,4916} },{0,0,0,35,0,0,0x808,0x17C,0x1B8,{4917,4918}},{0,0,0,60,0,0,0xC0C,0x1ED,0x258,{4919,4920}},{0,0,0,43,0,0,0xE0E,0x78,0x85,{4921,4922}},{0,0,0,0,0,0,0xE0E,0xE88,0x1144,{3921,2983}},{0,0,0,0,0x1,0,0x404,0x39F1,0x47A,{4923,4924,4925,4926} },{12,0,0,0,0x1,0,0xC0C,0x27DE,0x33A,{4927,4928,4929,4930} },{0,0,0,0,0x1,0,0xD0C,0x3D75,0x168,{4931,4932,4933,4934} },{0,0,0,0,0x1,0,0xE0E,0x3861,0x460,{4935,4936,4937,4938} },{0,0,0,0,0x1,0,0x100,0x369C,0x862,{4939,4940,4941,4942} },{0,0,0,0,0x1,0,0xF0E,0x38E6,0x466,{4943,4944,4945,4946} },{12,0,0,0,0x1,0,0xE0E,0x28BA,0xF6,{4947,4948,4949,4950} },{12,0,0,0,0x1,0,0x707,0x9C40,0xBA,{4265,4951,4952,4953} },{12,0,0,0,0x1,0,0xD0C,0x9C40,0x319,{4954,4955,4956,4957} },{0,0,0,0,0x1,0,0x101,0x9C40,0x71,{4958,4959,4960,4961} },{0,0,0,0,0x1,0,0x505,0x9C40,0x71,{4962,4963,4964,4965} },{0,0,0,0,0x1,0,0x100,0x9C40,0x5D,{4966,4967,4968,4969} },{0,0,0,0,0x1,0,0x707,0x9C40,0x71,{4970,4971,4972,4973} },{12,0,0,0,0x1,0,0xD0C,0x9C40,0x5D,{4974,4975,4976,4977} },{0,0,0,0,0x1,0,0x100,0x9C40,0x64,{4978,4979,4980,4981} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x6A,{4982,4983,4984,4985} },{0,0,0,0,0x1,0,0xA0A,0x1C34,0x244,{4986,4987,4988,4989} },{12,0,0,0,0x1,0,0xE0E,0x283C,0xF6,{4990,4991,4992,4993} },{-12,0,0,0,0x1,0,0xE0E,0x9C40,0x2D0,{4994,4995,4996,4997} },{-12,0,0,0,0x1,0,0x908,0x2392,0xAE2,{4998,4999,5000,5001} },{0,0,0,0,0x1,0,0x908,0x1C55,0x8A5,{5002,5003,5004,5005} },{0,0,0,0,0x1,0,0x100,0x9C40,0xC8,{5006,5007,5008,5009} },{0,0,0,0,0x1,0,0,0x9C40,0x56A,{5010,5011,5012,5013} },{0,0,0,0,0x1,0,0,0x9C40,0x14F6,{5010,5011,5014,5015} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0xC8,{5016,5017,5018,5017} },{12,0,0,0,0x1,0,0xD0C,0x9C40,0x99C,{5019,5020,5021,5022} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x6A,{3791,5023,3791,5024} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x6A,{5025,5026,5027,5028} },{12,0,0,0,0x1,0,0xC0C,0x1194,0x64,{5029,5030,5031,5032} },{12,0,0,0,0x1,0,0xE0E,0x9C40,0x5D,{5033,5034,5035,5036} },{12,0,0,0,0x1,0,0x808,0x49C,0xDC,{5037,5038,5039,5040} },{12,0,0,0,0x1,0,0,0x3B9,0x189,{5041,5042,5043,5044} },{0,0,0,0,0x1,0,0xE0E,0x3965,0x146,{5045,5046,5047,5048} },{0,0,0,0,0x1,0,0xE0E,0x3965,0x24A,{5049,5050,5051,5052} },{0,0,0,0,0x1,0,0xC0C,0x3889,0x898,{5053,5054,5055,5056} },{0,0,0,0,0x1,0,0xC0C,0x898,0x71,{5057,5058,5059,5060} },{12,0,0,0,0x1,0,0x606,0x1ED,0x1BE,{428,429,430,431} },{0,0,0,0,0x1,0,0x706,0x182,0x99,{5061,5062,5063,5064} },{0,0,0,0,0x1,0,0xE0E,0x31EC,0x251,{5065,5066,5067,5068} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x64,{5069,5070,5071,5072} },{0,0,0,0,0x1,0,0xD0C,0x2288,0x17C,{5073,5074,5075,5076} },{0,0,0,0,0x1,0,0xE0E,0x9DE,0x459,{5077,5078,5079,5080} },{0,0,0,0,0x1,0,0xE0E,0x1B15,0x24A,{5081,5082,5083,5084} },{12,0,0,0,0x1,0,0xE0E,0x9C40,0x571,{5085,5086,5087,5088} },{0,0,0,0,0x1,0,0xA0A,0x12C6,0x116C,{5089,5090,5091,5092} },{0,0,0,0,0x1,0,0xD0C,0x3124,0x488,{5093,5094,5095,5096} },{0,0,0,0,0x1,0,0xA0A,0x74A,0x898,{5097,5098,5099,5100} },{12,0,0,0,0x1,0,0x302,0x14A6,0x320,{5101,5102,5103,5104} },{-12,0,0,0,0x1,0,0xE0E,0x9C40,0x2C9,{5105,4995,4996,5106} },{-12,0,0,0,0x1,0,0xE0E,0x9C40,0x2C9,{5107,5108,5109,3365} },{-12,0,0,0,0x1,0,0xE0E,0x9C40,0x2D6,{5110,5111,5112,5113} },{-12,0,0,0,0x1,0,0xF0E,0x9C40,0x5C1,{5114,5115,5116,5117} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x5DC,{5118,5119,5120,5121} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x294,{5122,5123,5124,5125} },{12,0,0,0,0,0,0xC0C,0x9A9,0xD41,{5126,5127}},{12,0,0,0,0,0,0xC0C,0x981,0xCDD,{5126,5128}},{0,0,0,0,0x1,0,0xE0E,0x11EA,0x71,{5129,5130,5131,5132} },{-12,0,0,0,0x1,0,0xF0E,0x9C40,0x5A0,{5133,5134,5135,5136} },{0,0,0,0,0x1,0,0xC0C,0x11C9,0xE2,{5137,5138,5139,5140} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x71,{5141,5142,5143,5144} },{12,0,0,0,0x1,0,0xB0A,0x1449,0x78,{5145,5146,5147,5148} },{12,0,0,0,0x1,0,0xD0C,0x138E,0xF6,{5149,5150,5151,5152} },{0,0,0,0,0,0,0x606,0x9C40,0x11E,{848,849}},{0,0,0,0,0x1,0,0xE0E,0x1B80,0x244,{5153,5154,5155,5156} },{12,0,0,0,0x1,0,0xE0E,0x1255,0xE2,{5157,5158,5159,5160} },{12,0,0,0,0x1,0,0xE0E,0x9C40,0x5D,{5161,5162,5163,5164} },{12,0,0,0,0x1,0,0xC0C,0x661,0x189,{5165,5166,5167,5168} },{0,0,0,0,0x1,0,0xE0E,0x1C62,0x244,{5169,5170,5171,5172} },{0,0,0,0,0x1,0,0xA0A,0x1CAC,0x1130,{5173,5174,5175,5176} },{12,0,0,0,0x1,0,0xE0E,0x11F8,0xE2,{5177,5178,5179,5180} },{12,0,0,0,0x1,0,0xA0A,0xD76,0xC6C,{5181,5182,5183,5092} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x64,{5184,5185,5186,5187} },{0,0,0,0,0,0,0x606,0x49E8,0x55D,{1116,5188}},{0,0,0,0,0,0,0xE0E,0x4858,0x5D,{5189,5190}},{0,0,0,0,0,0,0xE0E,0x481C,0x5D,{5191,5190}},{-12,0,0,0,0x1,0,0xF0E,0x9C40,0x182,{5192,4340,5193,5194} },{0,0,0,0,0,0,0xE0E,0x1FF4,0x189,{5195,5196}},{0,0,0,0,0,0,0xE0E,0x4865,0x56,{5197,5190}},{0,0,0,0,0,0,0xE0E,0x394A,0x445C,{5198,5199}},{0,0,0,0,0x1,0,0x908,0x9C40,0x85,{5200,5201,5202,5203} },{12,0,0,0,0x1,0,0,0x9C40,0x71,{5204,5205,5206,5207} },{12,0,0,0,0x1,0,0xE0E,0x47F4,0x92,{4219,4220,4221,5208} },{0,0,0,0,0,0,0xE0E,0x9C40,0x56,{5209,1124}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{1128,1124}},{0,0,0,0,0x1,0,0xF0E,0x9C40,0xC8,{5210,5211,5212,5213} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0xC1,{5214,5215,5216,5217} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x71,{5218,5219,5220,5221} },{12,0,0,0,0x1,0,0xF0E,0x468C,0x2D0,{5222,5223,5224,5225} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x64,{5226,5227,5228,5229} },{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{5230,1124}},{12,0,0,0,0x1,0,0x908,0x21EE,0x585,{5231,5232,5233,5234} },{0,0,0,0,0x1,0,0,0x3929,0x8B2,{5235,5236,5237,5238} },{12,0,0,0,0x1,0,0,0x23CE,0x57E,{5239,5240,5241,5242} },{0,0,0,0,0x1,0,0xF0E,0x2446,0x4E5,{5243,5244,5245,5246} },{0,0,0,0,0x1,0,0x908,0x2022,0x592,{5247,4250,5248,5249} },{12,0,0,0,0x1,0,0x101,0x11D6,0x3B9,{5250,5251,5252,5253} },{0,0,0,0,0x1,0,0,0x938,0x57E,{5254,5255,5256,5257} },{0,0,0,0,0x1,0,0xB0A,0x805,0x514,{5258,5259,5260,5261} },{0,0,0,0,0,0,0,0x9C40,0x182,{5262,5263}},{0,0,0,0,0x1,0,0xA0A,0x9C40,0xE2,{5264,5265,5266,5267} },{0,0,0,0,0x1,0,0x808,0x9C40,0x71,{5268,5269,5270,5271} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x92,{5272,5273,5274,5275} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x92,{5276,5277,5278,5279} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x2E4,{5280,5281,5282,5283} },{0,0,0,0,0,0,0x606,0x272,0x5BA,{5284,4541}},{0,0,0,0,0,0,0x606,0x9C40,0x28D,{5285,5286}},{-12,0,0,0,0x1,0,0x100,0x4D1,0x585,{5287,5288,5289,5288} },{-12,0,0,0,0,0,0x606,0x1B58,0x1575,{4291,5290}},{-24,0,0,0,0,0,0xA0A,0xD5C,0x564,{4542,5291}},{0,0,0,0,0x1,0,0x706,0x1BF8,0x87D,{5292,5293,5294,5295} },{0,0,0,0,0,0,0x606,0x9C40,0x57E,{5296,5297}},{0,0,0,0,0,0,0xE0E,0x7A1,0xA92,{5298,5299}},{0,0,0,0,0x1,0,0xB0A,0x3ACD,0xAFD,{5300,5301,5302,5303} },{0,0,0,0,0x1,0,0x808,0x9C40,0x156E,{5304,5305,5306,5307} },{0,0,0,60,0x4,0,0,0x6,0,{795,1340}},{0,0,0,60,0,0,0xE0E,0xC8,0x140,{802,803}},{0,0,0,59,0,0,0xE0E,0x6A,0x5D,{672,673}},{0,0,0,44,0,0,0xE0E,0xC8,0x140,{802,803}},{0,0,0,41,0,0,0xE0F,0x2C9,0x33A,{804,805}},{0,0,0,44,0,0,0xE0F,0x201,0x258,{804,805}},{0,0,0,48,0,0,0xE0F,0x201,0x258,{804,805}},{0,0,0,96,0,0,0x808,0x2BC,0x99,{808,809}},{0,0,0,51,0,0,0xE0F,0x201,0x258,{804,805}},{0,0,0,54,0,0,0xE0F,0x201,0x258,{804,805}},{0,0,0,40,0,0,0xE0E,0x5C8,0xA7E,{810,811}},{0,0,0,57,0,0,0xE0F,0x17C,0x1B8,{804,805}},{0,0,0,58,0,0,0xE0E,0x5B4,0x862,{707,812}},{0,0,0,60,0,0,0xE0E,0x5A0,0x826,{707,812}},{0,0,0,42,0,0,0xE0E,0x280,0x2D6,{821,822}},{0,0,0,37,0,0,0xE0E,0x279,0x2D6,{821,822}},{0,0,0,41,0,0,0xA0A,0x272,0x2DD,{823,824}},{0,0,0,37,0,0,0xA0A,0x265,0x2D0,{823,824}},{0,0,0,70,0,0,0xE0E,0xE9,0x104,{827,828}},{0,0,0,90,0,0,0xE0E,0xE9,0x104,{827,828}},{0,0,0,46,0,0,0xC0C,0x85,0x64,{829,830}},{0,0,0,81,0,0,0x606,0xF0,0x5D,{751,840}},{0,0,0,53,0,0,0xC0C,0x9C40,0x50,{841,842}},{-12,0,0,0,0x1,0,0xE0E,0x9C40,0x2D0,{5308,4995,5309,5310} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0xC8,{5311,5312,5313,5314} },{12,0,0,0,0x1,0,0xF0E,0x9C40,0x265,{5315,5316,5317,5318} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x2F1,{5319,5320,5321,5322} },{0,0,0,0,0,0,0xA0A,0x2120,0x8AC,{5323,5324}},{0,0,0,0,0,0,0xA0A,0x9C40,0x141A,{5325,5326}},{0,0,0,49,0,0,0xE0E,0x736,0x870,{4373,4374}},{0,0,0,61,0,0,0x808,0x71,0x5D,{4227,4228}},{0,0,0,56,0,0,0x808,0x71,0x5D,{4227,4228}},{0,0,0,58,0,0,0x808,0x71,0x5D,{4227,4228}},{0,0,0,49,0,0,0x808,0x7E,0x5D,{4227,4228}},{0,0,0,44,0,0,0x808,0x7E,0x5D,{4227,4228}},{0,0,0,0,0,0,0x606,0x9C40,0x53C,{2557,5327}},{0,0,0,0,0,0,0xE0E,0x9C40,0,{3911,5328}},{0,0,0,84,0,0,0xE0E,0x99,0x64,{2595,3914}},{0,0,0,72,0,0,0xE0E,0x1B8,0x1ED,{5329,3916}},{0,0,0,0,0,0,0x606,0x2080,0x84E,{3426,5330}},{0,0,0,0,0,0,0xE0E,0x9C40,0x84E,{5331,5332}},{0,0,0,0,0,0,0x404,0x9C40,0x29A4,{5333,5334}},{0,0,0,0,0,0,0x606,0x2086,0x898,{5335,5330}},{0,0,0,0,0,0,0x808,0x3CD,0x8C6,{5336,5337}},{0,0,0,0,0,0,0x202,0x9C40,0x5D,{5338,5339}},{0,0,0,0,0,0,0xA0A,0x9C40,0x189,{5340,5341}},{0,0,0,0,0,0,0xA0A,0x9C40,0x182,{5342,5341}},{0,0,0,0,0,0,0x202,0x9C40,0x474,{5343,3259}},{0,0,0,0,0,0,0x404,0x9C40,0x1054,{5344,5345}},{0,0,0,0,0,0,0x202,0x9C40,0x1061,{5346,5347}},{0,0,0,0,0,0,0,0x9C40,0x56,{5348,4045}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{5349,5350}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{5351,3461}},{0,0,0,0,0,0,0xE0E,0x9C40,0x56,{5352,5353}},{0,0,0,0,0,0,0x808,0x9C40,0x1AAA,{5354,5355}},{0,0,0,0,0,0,0xE0E,0x334,0x56,{5356,5357}},{0,0,0,0,0,0,0x202,0x9C40,0x2D6,{5358,5359}},{0,0,0,0,0,0,0,0x9C40,0x2C9,{5360,5361}},{0,0,0,0,0,0,0xE0E,0x9C40,0x104,{438,5362}},{0,0,0,0,0,0,0,0x3A98,0x5D,{2562,5363}},{0,0,0,0,0,0,0x202,0x9C40,0x2B5,{5364,5365}},{0,0,0,0,0,0,0xA0A,0x981,0xDC,{5366,5367}},{0,0,0,0,0,0,0xE0E,0x1219,0x159D,{5368,5369}},{0,0,0,0,0,0,0,0x9C40,0x5D,{5370,5371}},{0,0,0,0,0,0,0x606,0x9C40,0x85,{5372,5373}},{0,0,0,0,0,0,0x808,0x466,0x89E,{5374,5375}},{0,0,0,0,0,0,0x808,0x9C40,0,{5351,5376}},{0,0,0,0,0,0,0x202,0x9C40,0x107C,{5346,5377}},{0,0,0,0,0,0,0x808,0x122D,0x8B9,{5378,3482}},{0,0,0,0,0,0,0x202,0x9C40,0x106E,{5346,5379}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{3566,3567}},{0,0,0,0,0,0,0xC0C,0x9C40,0x5D,{5380,5381}},{0,0,0,0,0,0,0,0x9C40,0x14CE,{5382,5383}},{0,0,0,0,0,0,0xE0E,0xD5,0xE2,{5384,5385}},{0,0,0,0,0,0,0xA0A,0x118,0x139,{5386,3638}},{0,0,0,0,0,0,0,0x4A2,0x57E,{5387,5388}},{0,0,0,0,0,0,0x808,0x3728,0x3FCD,{5389,5390}},{0,0,0,0,0,0,0xA0A,0x410,0x409,{5391,5392}},{0,0,0,0,0,0,0x808,0x1644,0x219E,{5393,5394}},{0,0,0,0,0,0,0x808,0x9C40,0x5D,{5395,3560}},{0,0,0,0,0,0,0x202,0x9C40,0x2D6,{5358,5396}},{0,0,0,36,0,0,0x808,0x17C,0x1B8,{659,249}},{0,0,0,37,0,0,0,0xD5,0x5D,{693,694}},{0,0,0,38,0,0,0xE0E,0xCE,0xE2,{5384,5385}},{0,0,0,24,0,0,0,0xC1,0x5D,{693,694}},{0,0,0,32,0,0,0xE0E,0xCE,0xE2,{5384,5385}},{0,0,0,48,0,0,0,0xBA,0x5D,{693,694}},{0,0,0,42,0,0,0xA0A,0xD5,0xF0,{5386,3638}},{0,0,0,50,0,0,0,0xBA,0x5D,{693,694}},{0,0,0,52,0,0,0,0xBA,0x5D,{693,694}},{0,0,0,54,0,0,0,0xBA,0x5D,{693,694}},{0,0,0,55,0,0,0,0xBA,0x5D,{693,694}},{0,0,0,57,0,0,0,0xBA,0x5D,{693,694}},{0,0,0,51,0,0,0,0x3B9,0x459,{5387,5388}},{0,0,0,61,0,0,0x607,0xC1,0x132,{280,281}},{0,0,0,0,0,0,0x607,0x20E4,0x1108,{2532,5397}},{0,0,0,63,0,0,0x607,0x178A,0xAE2,{2532,5397}},{0,0,0,64,0,0,0x607,0x1769,0xAC1,{2532,5397}},{0,0,0,40,0,0,0xE0E,0xCE,0xE2,{5398,5399}},{0,0,0,70,0,0,0xE0E,0xA0,0xAD,{5398,5399}},{0,0,0,0,0,0,0x606,0x9C40,0x64,{5400,5401}},{0,0,0,0,0,0,0xA0A,0x9C40,0x5D,{5402,5403}},{0,0,0,0,0,0,0x404,0x9C40,0x71,{5404,5405}},{0,0,0,0,0,0,0x404,0x9C40,0x71,{5406,5405}},{0,0,0,38,0,0,0xA0A,0xF6,0x5D,{4357,5407}},{0,0,0,57,0,0,0x809,0x104,0x12C,{4383,4384}},{0,0,0,63,0,0,0xE0E,0x92,0x111,{5408,5409}},{0,0,0,74,0,0,0xE0E,0x118,0x6A,{5410,5411}},{0,0,0,74,0,0,0xE0E,0x1E0,0x1D9,{5412,5413}},{0,0,0,60,0,0,0x606,0x2A1,0x6A,{5414,5415}},{0,0,0,0,0x1,0,0x908,0x34F8,0x3F5,{2597,2598,2599,2600} },{0,0,0,0,0x1,0,0xB0A,0x1CE1,0x244,{2601,2602,2603,2604} },{0,0,0,0,0x1,0,0xE0E,0x2E39,0x460,{2605,2606,2607,2608} },{12,0,0,0,0x1,0,0xC0D,0x26C0,0x51A,{2609,2610,2611,2612} },{0,0,0,0,0x1,0,0xD0C,0x1CA5,0xDC,{332,333,334,335} },{0,0,0,0,0x1,0,0x100,0x1AFA,0xE2,{336,337,338,339} },{0,0,0,0,0x1,0,0x808,0x1309,0x139,{340,341,342,343} },{12,0,0,0,0x1,0,0x404,0x2795,0x71,{2613,2614,2615,2616} },{0,0,0,0,0x1,0,0x100,0x100A,0x500,{346,347,348,349} },{0,0,0,0,0x1,0,0xB0A,0x1FED,0x2579,{2617,2618,2619,2620} },{0,0,0,0,0x1,0,0x302,0x916,0xA85,{350,351,2621,2622} },{12,0,0,0,0x1,0,0xB0A,0x23E2,0x2DD,{2623,2624,2625,2626} },{0,0,0,0,0x1,0,0x706,0x9C40,0x64,{2627,2628,352,353} },{0,0,0,0,0x1,0,0xB0A,0x251,0x2DD,{354,355,356,357} },{0,0,0,0,0x1,0,0x504,0x175C,0x1575,{358,359,360,361} },{0,0,0,0,0x1,0,0xD0D,0x43E,0x4D1,{2629,362,2630,2631} },{0,0,0,0,0x1,0,0x707,0x9C40,0x6A,{2632,2633,2634,2635} },{0,0,0,0,0x1,0,0x707,0x9C40,0x104,{368,369,2636,2637} },{-12,0,0,0,0x1,0,0x908,0x9C40,0x71,{372,373,374,375} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x474,{2638,2639,2640,2641} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x488,{2642,2643,2644,2645} },{0,0,0,0,0x1,0,0x100,0x9C40,0x6A,{378,379,380,381} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x92,{382,383,384,385} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x6A,{386,387,388,389} },{0,0,0,0,0x1,0,0xB0A,0xE81,0xE9,{390,391,392,393} },{0,0,0,0,0x1,0,0xC0C,0x395E,0x146,{394,395,396,49} },{0,0,0,0,0x1,0,0xA0A,0x2411,0x189,{397,398,399,400} },{0,0,0,0,0x1,0,0x808,0x3930,0x146,{401,402,403,404} },{0,0,0,0,0x1,0,0x908,0x1165,0xE2,{405,406,407,408} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x6A,{2646,410,2647,162} },{0,0,0,0,0x1,0,0,0x9C40,0x64,{2648,2649,2650,2651} },{0,0,0,0,0x1,0,0x908,0x42AA,0xE2,{60,414,415,416} },{0,0,0,0,0x1,0,0x302,0xFCE,0xDC,{417,418,419,420} },{0,0,0,0,0x1,0,0x404,0x38B1,0x251,{421,422,423,424} },{0,0,0,0,0x1,0,0x606,0xAFD,0xE2,{425,426,426,427} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x99,{68,69,2652,2653} },{0,0,0,0,0x1,0,0x908,0x1F7C,0x5D,{70,71,2654,2655} },{0,0,0,0,0x1,0,0x706,0x1F11,0x5D,{793,71,2654,2655} },{0,0,0,0,0x1,0,0xA0A,0x1F32,0xDC,{2656,435,2657,437} },{0,0,0,0,0x1,0,0xA0A,0x1F39,0xE2,{434,435,436,437} },{0,0,0,0,0x1,0,0x908,0x9C40,0x294,{75,76,2658,2659} },{0,0,0,0,0x1,0,0x908,0x9C40,0x2A1,{2660,76,2658,2659} },{0,0,0,0,0x1,0,0x908,0x9C40,0x2A1,{442,76,443,444} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x196,{445,446,447,448} },{0,0,0,0,0x1,0,0x706,0x9C40,0x599,{449,450,451,452} },{0,0,0,0,0x1,0,0xE0E,0x3E1,0x44C,{453,454,455,456} },{0,0,0,0,0x1,0,0xE0E,0xCBC,0x1090,{453,454,455,457} },{0,0,0,0,0x1,0,0x302,0x1ACC,0x1130,{89,90,458,459} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x190,{460,461,462,463} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x189,{464,465,93,94} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x57E,{2661,96,95,2662} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x599,{97,96,2663,2664} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x1ED,{2665,2666,2667,2668} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x2B5,{2669,2670,100,101} },{0,0,0,0,0x1,0,0x808,0x9C40,0x2DD,{2671,2672,2673,2674} },{0,0,0,0,0x1,0,0xB0B,0x2BC,0x2A1,{471,472,473,474} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xBA,{106,107,2675,2676} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xC1,{2677,2678,2675,2679} },{12,0,0,0,0x1,0,0xD0C,0x9C40,0x17C,{475,111,476,477} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x85,{2680,2681,2682,2683} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x64,{2684,2685,2686,2687} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x6A,{482,483,484,485} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0xD5,{486,487,488,489} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x64,{490,491,492,493} },{0,0,0,0,0x1,0,0x908,0x9C40,0xF6,{494,495,496,497} },{0,0,0,0,0x1,0,0x908,0x9C40,0x104,{124,498,499,500} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x236,{501,502,503,504} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x21C,{505,506,507,508} },{0,0,0,0,0x1,0,0x808,0x9C40,0x71,{509,2688,2689,2690} },{0,0,0,0,0x1,0,0x202,0x9C40,0x92,{513,514,515,516} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x7E,{517,518,519,520} },{0,0,0,0,0x1,0,0x908,0x9C40,0x175,{136,137,136,521} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x71,{2691,2692,138,139} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x6A,{2691,2692,140,139} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xE2,{522,523,524,525} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x71,{2691,2693,2694,144} },{0,0,0,0,0x1,0,0xE0F,0x9C40,0x2A1,{528,529,530,146} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x272,{531,532,533,534} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x279,{535,536,537,538} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x17C,{2695,2696,2697,2698} },{0,0,0,0,0x1,0,0x404,0x9C40,0x5D,{541,542,543,544} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x8C,{545,546,547,548} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0xF0,{2699,2700,157,158} },{0,0,0,0,0x1,0,0x908,0x9C40,0x6A,{2701,2702,159,2703} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x6A,{550,551,552,162} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x196,{553,554,555,556} },{0,0,0,0,0x1,0,0x908,0x9C40,0x2A1,{557,558,559,560} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x92,{167,168,2704,2705} },{0,0,0,0,0x1,0,0x302,0x9C40,0x53C,{561,562,563,564} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x11E4,{171,172,565,566} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x495,{567,174,568,569} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x1179,{570,571,572,176} },{0,0,0,0,0x1,0,0x908,0x9C40,0x3EE,{2706,2707,2708,2709} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x42A,{2710,2711,179,180} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x398,{181,2711,2712,2713} },{0,0,0,0,0x1,0,0x100,0x9C40,0x182,{2714,184,2715,2716} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xA71,{2717,2718,2719,2720} },{0,0,0,0,0x1,0,0x706,0x9C40,0x141A,{577,578,579,580} },{0,0,0,0,0x1,0,0x908,0x1270,0x28C1,{2721,2722,189,190} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x58C,{2723,2724,2725,2726} },{0,0,0,0,0x1,0,0xF0E,0x33BE,0x578,{2727,2728,2729,2730} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x14D5,{2731,2732,2733,198} },{0,0,0,0,0x1,0,0x504,0x9C40,0xA99,{2734,2735,2736,2737} },{0,0,0,0,0x1,0,0x606,0x9C40,0x1324,{2738,2739,2740,200} },{0,0,0,0,0x1,0,0x100,0x3C28,0x2C9,{589,590,591,592} },{0,0,0,0,0x1,0,0,0x1C70,0x466,{593,594,595,596} },{0,0,0,0,0x1,0,0xE0E,0xDE1,0x43E,{597,598,599,600} },{0,0,0,0,0x1,0,0x908,0x362A,0x40DE,{207,601,602,603} },{0,0,0,0,0x1,0,0xD0C,0x4D8,0x5AD,{209,210,604,605} },{0,0,0,0,0x1,0,0x706,0x9C40,0x5D,{211,212,606,607} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x29A,{213,214,608,609} },{0,0,0,0,0x1,0,0x606,0x9C40,0x64,{610,611,612,613} },{0,0,0,0,0x1,0,0x706,0xFD5,0x585,{217,218,2741,2742} },{0,0,0,0,0x1,0,0xB0A,0x26C,0x2EA,{614,615,219,220} },{0,0,0,0,0x1,0,0x100,0x4C4,0x57E,{616,617,221,222} },{0,0,0,0,0x1,0,0xB0A,0xA6,0x438,{2743,2744,2745,224} },{0,0,0,0,0x1,0,0x706,0x10ED,0x156E,{225,226,2746,2747} },{0,0,0,0,0x1,0,0xF0E,0xD3A,0x7D0,{2748,2749,2750,2751} },{0,0,0,0,0x1,0,0xF0E,0x78D,0x49C,{622,623,624,623} },{0,0,0,0,0x1,0,0xF0E,0xBCC,0,{2752,1133,2753,2754} },{0,0,0,0,0x1,0,0xF0E,0x5D5,0xA8C,{233,2755,2756,2757} },{0,0,0,0,0x1,0,0x505,0x3C0,0xCE,{2761,638,2762,2763} },{1,0,0,0,0x1,0,0xB0A,0x35C,0x3C6,{2764,242,2765,2766} },{-36,0,0,0,0x1,0,0x908,0x9C40,0x1F4,{2767,2768,2769,2768} },{0,0,0,0,0x1,0,0xE0E,0x230,0x92,{639,640,641,642} },{0,0,0,35,0,0,0x8,0x17C,0x1B8,{248,249}},{0,0,0,44,0x1,0,0x606,0x78,0x5D,{686,687,688,251} },{0,0,0,48,0x1,0,0xF0E,0x104,0x132,{252,253,2770,2771} },{0,0,0,58,0x1,0,0xF0E,0x146,0x182,{691,692,254,255} },{0,0,0,60,0x1,0,0xF0E,0xFD,0x12C,{252,253,2772,2773} },{0,0,0,48,0x1,0,0x100,0x1C5,0x215,{2774,2775,256,257} },{0,0,0,43,0x1,0,0xB0A,0xAD,0xC1,{258,259,258,259} },{0,0,0,49,0x1,0,0x100,0x1C5,0x215,{2774,2775,256,257} },{0,0,0,43,0x1,0,0xB0A,0xA6,0xAD,{258,2776,258,2776} },{0,0,0,51,0x1,0,0x100,0x1C5,0x20E,{2774,2775,256,257} },{0,0,0,43,0x1,0,0xB0A,0x701,0x819,{261,262,261,2777} },{0,0,0,54,0x1,0,0x100,0x1C5,0x215,{2774,2775,256,257} },{0,0,0,57,0x1,0,0x100,0x1C5,0x215,{2774,2775,256,257} },{0,0,0,72,0x1,0,0xE0E,0x625,0x74A,{715,716,717,718} },{0,0,0,60,0x1,0,0x100,0x1C5,0x215,{2774,2775,256,257} },{0,0,0,70,0x1,0,0x706,0x319,0x3AC,{2778,2779,2780,2781} },{0,0,0,60,0x1,0,0xE0E,0x3CD,0x47A,{703,704,705,706} },{0,0,0,36,0x1,0,0x706,0x481,0x549,{2778,2782,2783,2784} },{0,0,0,65,0x1,0,0,0x125,0x146,{2785,2786,2787,2788} },{0,0,0,84,0x1,0,0xE0E,0x549,0x3B2,{2789,2790,2791,2792} },{0,0,0,59,0x1,0,0x908,0x196,0x1CC,{274,2793,2794,275} },{0,0,0,84,0x1,0,0xE0E,0x632,0x751,{2795,716,2796,718} },{0,0,0,35,0x1,0,0xD0C,0x556,0x62C,{2797,2528,2797,277} },{0,0,0,44,0x1,0,0x706,0x39E,0x139,{2798,2799,2780,2800} },{0,0,0,67,0x1,0,0x706,0xF0,0x111,{2801,2802,2803,2804} },{0,0,0,66,0x1,0,0x706,0x140,0x265,{2805,2806,2807,2808} },{0,0,0,59,0x1,0,0x706,0xA0,0x140,{282,283,2809,2810} },{0,0,0,51,0x1,0,0x706,0x17C,0x488,{2811,327,2811,2812} },{0,0,0,45,0x1,0,0x706,0x104,0x258,{2813,287,2813,2814} },{0,0,0,71,0x1,0,0xF0E,0x1B1,0x1E6,{2815,2327,2815,2816} },{0,0,0,60,0x1,0,0xF0E,0x118,0x5D,{290,291,2817,2818} },{0,0,0,58,0x1,0,0xF0E,0x1FA,0x251,{2819,295,2820,2821} },{0,0,0,53,0x1,0,0xF0E,0x1E6,0x23D,{2822,2823,2822,2824} },{0,0,0,64,0x1,0,0xF0E,0xDC,0xB4,{747,297,2825,2826} },{0,0,0,71,0x1,0,0xF0E,0x6A,0x78,{2827,2828,2829,2830} },{0,0,0,61,0x1,0,0xF0E,0x3DA,0x46D,{2831,2832,2833,2834} },{0,0,0,61,0x1,0,0xF0E,0x6D2,0x812,{2835,2836,2835,2837} },{0,0,0,48,0x1,0,0xD0C,0x118,0x146,{737,738,739,739} },{0,0,0,48,0x1,0,0xD0C,0x17C,0x146,{737,738,740,740} },{0,0,0,69,0x1,0,0x100,0x78,0x140,{310,309,310,2838} },{0,0,0,68,0x1,0,0x100,0x78,0x140,{310,311,310,2839} },{0,0,0,63,0x1,0,0x100,0x8C,0x1B1,{310,311,310,2839} },{0,0,0,74,0x1,0,0x908,0x38A,0x3B9,{2840,2841,2842,2843} },{0,0,0,60,0x1,0,0xB0A,0x416,0x460,{2844,2845,2846,2847} },{0,0,0,80,0x1,0,0x908,0xD5,0xF0,{316,2848,316,2849} },{0,0,0,64,0x1,0,0x908,0x8E8,0xAE2,{318,2850,2851,2852} },{0,0,0,69,0x1,0,0xF0E,0x78,0x56,{2853,2854,2853,2855} },{0,0,0,55,0x1,0,0xF0E,0x5DC,0x244,{323,748,749,750} },{0,0,0,75,0x1,0,0xA0B,0x618,0x3AC,{2856,2857,2858,2859} },{0,0,0,68,0x1,0,0x908,0x78,0x140,{2860,309,2860,2838} },{0,0,0,48,0x1,0,0x706,0x190,0x495,{326,327,326,2812} },{0,0,0,53,0x1,0,0x706,0x244,0x8AC,{2861,2862,2861,2862} },{0,0,0,44,0x1,0,0x100,0x70E,0x8B9,{2774,4139,256,4140} },{0,0,0,44,0x1,0,0x606,0x78,0x5D,{686,4141,4142,4143} },{0,0,0,43,0x1,0,0xF0E,0x104,0x139,{4144,253,2770,2771} },{0,0,0,58,0,0,0,0x8C,0x1B1,{310,309}},{0,0,0,38,0x1,0,0xF0E,0x139,0x175,{4144,253,2770,2771} },{0,0,0,41,0x1,0,0x707,0x870,0xA99,{4145,4146,4147,4148} },{0,0,0,42,0x1,0,0x707,0x869,0xA92,{4145,4146,4147,4148} },{0,0,0,43,0x1,0,0x707,0x6C5,0x87D,{4145,4146,4147,4148} },{0,0,0,44,0x1,0,0x707,0x6CC,0x884,{4145,4146,4147,4148} },{0,0,0,45,0x1,0,0x707,0x6D2,0x88A,{4145,4146,4147,4148} },{0,0,0,46,0x1,0,0x707,0x6D9,0x891,{4145,4146,4147,4148} },{0,0,0,47,0x1,0,0x707,0x6D2,0x891,{4145,4146,4147,4148} },{0,0,0,48,0x1,0,0x707,0x6C5,0x88A,{4145,4146,4147,4148} },{0,0,0,49,0x1,0,0x707,0x6BE,0x884,{4145,4146,4147,4148} },{0,0,0,50,0x1,0,0x707,0x6BE,0x884,{4145,4146,4147,4148} },{0,0,0,51,0x1,0,0x707,0x6C5,0x884,{4145,4146,4147,4148} },{0,0,0,52,0x1,0,0x707,0x6CC,0x88A,{4145,4146,4147,4148} },{0,0,0,53,0x1,0,0x707,0x6D9,0x898,{4145,4146,4147,4148} },{0,0,0,65,0x1,0,0,0x125,0x258,{2785,2786,2787,4149} },{0,0,0,84,0x1,0,0xE0E,0x556,0x3B9,{4150,4151,4152,4153} },{0,0,0,56,0,0,0x8,0x175,0x1B8,{274,2793}},{0,0,0,84,0x1,0,0xE0E,0x62C,0x758,{715,716,717,718} },{0,0,0,35,0,0,0xC,0x528,0x618,{2797,2528}},{0,0,0,73,0x1,0,0xE0E,0x62C,0x751,{715,716,717,718} },{0,0,0,67,0x1,0,0xF0E,0x104,0x1AA,{4154,4155,4156,4157} },{0,0,0,66,0x1,0,0x706,0x20E,0x410,{2805,4158,4156,4159} },{0,0,0,59,0,0,0x6,0x64,0x6A,{326,3646}},{0,0,0,51,0,0,0x6,0x17C,0x481,{326,4160}},{0,0,0,45,0,0,0x6,0x196,0x46D,{326,327}},{0,0,0,72,0,0,0xE,0x1B8,0x201,{4161,289}},{0,0,0,60,0,0,0xE,0x118,0x5D,{3623,3649}},{0,0,0,58,0,0,0xE,0x161,0x466,{3650,3651}},{0,0,0,53,0,0,0xE,0x161,0x466,{3652,3651}},{0,0,0,64,0,0,0xE,0xDC,0xAD,{747,297}},{0,0,0,71,0,0,0xE,0x6A,0x78,{4162,299}},{0,0,0,61,0,0,0xE,0x3CD,0x460,{2831,4163}},{0,0,0,61,0,0,0xE,0x758,0x898,{2835,4164}},{0,0,0,55,0x1,0,0xD0C,0xD5,0xF6,{4165,4166,739,739} },{0,0,0,45,0x1,0,0xD0C,0x17C,0x140,{4165,4166,740,740} },{0,0,0,75,0,0,0,0x78,0x140,{310,309}},{0,0,0,70,0,0,0,0x78,0x140,{310,309}},{0,0,0,65,0,0,0,0x8C,0x1B1,{310,309}},{0,0,0,76,0x1,0,0x908,0x182,0x111,{4167,4168,4169,4170} },{0,0,0,60,0x1,0,0xB0A,0x3FC,0x44C,{4171,4172,4173,4174} },{0,0,0,85,0x1,0,0x100,0x9C40,0x161,{4175,4176,4177,4178} },{0,0,0,85,0x1,0,0x100,0x9C40,0x14D,{4179,4180,4181,4182} },{0,0,0,60,0x1,0,0x908,0xD12,0x474,{4183,4184,4185,4186} },{0,0,0,82,0x1,0,0xA0B,0x618,0x3AC,{2856,2857,2858,2859} },{0,0,0,80,0,0,0,0x6A,0xF0,{310,309}},{0,0,0,44,0x1,0,0x100,0x70E,0x8B9,{2774,4139,256,4140} },{0,0,0,44,0x1,0,0x606,0x78,0x5D,{686,4141,4142,4143} },{0,0,0,43,0x1,0,0xF0E,0x104,0x139,{4144,253,2770,2771} },{0,0,0,38,0x1,0,0xF0E,0x139,0x175,{4144,253,2770,2771} },{0,0,0,41,0x1,0,0x707,0x870,0xA99,{4145,4146,4147,4148} },{0,0,0,42,0x1,0,0x707,0x869,0xA92,{4145,4146,4147,4148} },{0,0,0,44,0x1,0,0x707,0x6CC,0x884,{4145,4146,4147,4148} },{0,0,0,45,0x1,0,0x707,0x6D2,0x88A,{4145,4146,4147,4148} },{0,0,0,46,0x1,0,0x707,0x6D9,0x891,{4145,4146,4147,4148} },{0,0,0,48,0x1,0,0x707,0x6C5,0x88A,{4145,4146,4147,4148} },{0,0,0,50,0x1,0,0x707,0x6BE,0x884,{4145,4146,4147,4148} },{0,0,0,52,0x1,0,0x707,0x6CC,0x88A,{4145,4146,4147,4148} },{0,0,0,53,0x1,0,0x707,0x6D9,0x898,{4145,4146,4147,4148} },{0,0,0,56,0,0,0x808,0x175,0x1B8,{274,2793}},{0,0,0,35,0,0,0xC0C,0x528,0x618,{2797,2528}},{0,0,0,73,0x1,0,0xE0E,0x62C,0x751,{715,716,717,718} },{0,0,0,67,0x1,0,0xF0E,0x104,0x1AA,{4154,4155,4156,4157} },{0,0,0,66,0x1,0,0x706,0x20E,0x410,{2805,4158,4156,4159} },{0,0,0,72,0,0,0xE0E,0x1B8,0x201,{4161,289}},{0,0,0,55,0x1,0,0xD0C,0xD5,0xF6,{4165,4166,739,739} },{0,0,0,45,0x1,0,0xD0C,0x17C,0x140,{4165,4166,740,740} },{0,0,0,75,0,0,0,0x78,0x140,{310,309}},{0,0,0,70,0,0,0,0x78,0x140,{310,309}},{0,0,0,65,0,0,0,0x8C,0x1B1,{310,309}},{0,0,0,76,0x1,0,0x908,0x182,0x111,{4167,4168,4169,4170} },{0,0,0,85,0x1,0,0x100,0x9C40,0x161,{4175,4176,4177,4178} },{0,0,0,85,0x1,0,0x100,0x9C40,0x14D,{4179,4180,4181,4182} },{0,0,0,60,0x1,0,0x908,0xD12,0x474,{4183,4184,4185,4186} },{0,0,0,80,0,0,0,0x6A,0xF0,{310,309}},{0,0,0,60,0x1,0,0xF0E,0x1248,0x460,{244,5416,638,638} },{0,0,0,0,0,0,0x6,0x9C40,0x2D0,{5417,5418}},{0,0,0,0,0,0,0xE,0x17D4,0x189,{5419,5420}},{0,0,0,0,0,0,0x6,0x959,0x64,{1180,5421}},{0,0,0,0,0,0,0x4,0x10E6,0xBA,{5422,5423}},{0,0,0,0,0,0,0x9,0xD84,0x8C6,{5424,5425}},{0,0,0,0,0,0,0xC,0x19A6,0x21DA,{5426,5427}},{0,0,0,0,0,0,0x8,0xE60,0x10F4,{5428,5429}},{0,0,0,0,0,0,0xC,0x9C40,0x175,{5430,5431}},{0,0,0,0,0,0,0x8,0x119A,0x10C5,{5432,5433}},{0,0,0,0,0,0,0x6,0x9C40,0xA8C,{5434,5435}},{0,0,0,0,0,0,0x8,0x45DE,0x151E,{5436,5437}},{0,0,0,0,0,0,0x6,0x869,0x564,{5438,5439}},{0,0,0,0,0,0,0xA,0x48E,0x556,{3664,5440}},{0,0,0,0,0,0,0xE,0x2F1,0x56A,{5441,5442}},{0,0,0,0,0,0,0xE,0x37FD,0x4192,{5443,5444}},{0,0,0,0,0,0,0x6,0x3C3C,0x13F2,{5445,5446}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{5447,5448}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{5449,5450}},{0,0,0,0,0,0,0,0x1284,0xDC,{5451,5452}},{0,0,0,0,0,0,0xB,0x9C40,0x5D,{5453,5454}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{5455,5456}},{0,0,0,0,0,0,0xE,0x9C40,0x64,{5457,5458}},{0,0,0,0,0,0,0xD,0x9C40,0x9A9,{1262,1263}},{0,0,0,0,0,0,0x4,0x2079,0x855,{5459,5460}},{0,0,0,0,0,0,0xA,0x1D81,0x140,{5461,5462}},{0,0,0,0,0,0,0xD,0x9C40,0x78,{5463,5464}},{0,0,0,0,0,0,0x6,0x9C40,0xB4,{5465,5466}},{0,0,0,0,0,0,0xA,0x15A,0x189,{5467,5468}},{0,0,0,0,0,0,0x4,0x9C40,0x118,{5469,5470}},{0,0,0,0,0,0,0x8,0x9C40,0x215,{5471,5472}},{0,0,0,0,0,0,0x8,0x10E6,0x156E,{5473,5474}},{0,0,0,0,0,0,0,0x891,0x6A,{5475,5476}},{0,0,0,0,0,0,0x6,0x12EE,0xC1,{5477,5478}},{0,0,0,0,0,0,0x4,0x31C4,0xC1,{5479,5480}},{0,0,0,0,0,0,0x4,0x1B22,0x251,{5481,5482}},{0,0,0,0,0,0,0x2,0x35CD,0x64,{5483,5484}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{5485,5486}},{0,0,0,0,0,0,0x8,0x16B5,0x88A,{5487,5488}},{0,0,0,0,0,0,0xA,0x89E,0x64,{5489,5490}},{0,0,0,0,0,0,0xE,0x9C40,0x190,{5491,5492}},{0,0,0,0,0,0,0xE,0x9C40,0x7E,{5493,5494}},{0,0,0,0,0,0,0xE,0x9C40,0x14D,{5495,5496}},{0,0,0,0,0,0,0xE,0x9C40,0x190,{5491,5497}},{0,0,0,0,0,0,0xC,0x9C40,0x175,{5498,5499}},{0,0,0,0,0,0,0x8,0x168,0x175,{5500,5501}},{0,0,0,0,0,0,0,0x2252,0x1478,{5502,5503}},{0,0,0,0,0,0,0x2,0x251,0x92,{5504,5505}},{0,0,0,0,0,0,0xC,0x9C40,0x236,{5506,5507}},{0,0,0,0,0,0,0xA,0x9C40,0x244,{5508,5509}},{0,0,0,0,0,0,0xA,0x9C40,0x229,{5510,5511}},{0,0,0,0,0,0,0xA,0x9C40,0x23D,{5512,5513}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{5514,5515}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{5516,5517}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{5518,5519}},{0,0,0,0,0,0,0x8,0x2FE,0x2B5,{5520,5521}},{0,0,0,0,0,0,0xC,0x9C40,0x17C,{5522,5523}},{0,0,0,0,0,0,0xC,0x9C40,0x2D0,{5524,5525}},{0,0,0,0,0,0,0xC,0x9C40,0xDC,{5526,5527}},{0,0,0,0,0,0,0xC,0x9C40,0x71,{5528,5529}},{0,0,0,0,0,0,0xC,0x9C40,0x189,{5530,5531}},{0,0,0,0,0,0,0xC,0x9C40,0x175,{5532,5533}},{0,0,0,0,0,0,0xE,0x9C40,0x17C,{5534,5535}},{0,0,0,0,0,0,0xC,0x9C40,0x64,{5536,5537}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{5538,5539}},{0,0,0,0,0,0,0xA,0x9C40,0x64,{5540,5541}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{5542,5543}},{0,0,0,0,0,0,0xC,0x9C40,0xBA,{1140,5544}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{5545,5546}},{0,0,0,0,0,0,0,0x9C40,0x5D,{5547,5548}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{5549,5550}},{0,0,0,0,0,0,0x2,0x9C40,0x64,{5551,5552}},{0,0,0,0,0,0,0x2,0x9C40,0x64,{5551,5553}},{0,0,0,0,0,0,0xE,0x9C40,0xBA,{5554,5555}},{0,0,0,0,0,0,0x8,0x1511,0xDC,{5556,5557}},{0,0,0,0,0,0,0,0x9C40,0x56,{5558,5559}},{0,0,0,0,0,0,0x2,0x44E8,0x71,{5560,5561}},{0,0,0,0,0,0,0xC,0x9C40,0xD5,{5562,5563}},{0,0,0,0,0,0,0xA,0x9C40,0xBA,{5564,5565}},{0,0,0,0,0,0,0,0x9C40,0x466,{5566,5567}},{0,0,0,0,0,0,0xC,0x9C40,0x1CCD,{5568,5569}},{0,0,0,0,0,0,0xF,0x4469,0x5D,{5570,5571}},{0,0,0,0,0,0,0x4,0x9C40,0xDC,{5572,5573}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{5574,5575}},{0,0,0,0,0,0,0xC,0x15F4,0x24A,{5576,5577}},{0,0,0,0,0,0,0x8,0xE74,0x71,{5578,5579}},{0,0,0,0,0,0,0xE,0x9C40,0xC1,{5580,5581}},{0,0,0,0,0,0,0x8,0xABA,0x71,{5582,5583}},{0,0,0,0,0,0,0x8,0x215C,0x2A1,{5584,5585}},{0,0,0,0,0,0,0xA,0x8E8,0x83A,{5586,5587}},{0,0,0,0,0,0,0x8,0x154,0x17C,{5588,5589}},{0,0,0,0,0,0,0xE,0x4AD8,0x5201,{5590,5591}},{0,0,0,0,0,0,0x1,0x161,0x5D,{5592,5593}},{0,0,0,0,0,0,0xE,0x236,0x2BC,{5594,5595}},{0,0,0,0,0,0,0,0xE2,0x64,{5596,5597}},{0,0,0,0,0,0,0xE,0x9C40,0x71,{643,644}},{0,0,0,0,0,0,0xC,0x9C40,0xE2,{5598,5599}},{0,0,0,0,0,0,0x2,0x9C40,0xE2,{5600,5599}},{0,0,0,0,0,0,0x2,0x140,0x175,{647,648}},{0,0,0,0,0,0,0,0xD5,0x5D,{649,650}},{0,0,0,0,0,0,0xE,0x168,0x182,{5601,5602}},{0,0,0,0,0,0,0x8,0x16E,0x5D,{5603,5604}},{0,0,0,0,0,0,0,0xFD,0x5D,{693,1343}},{0,0,0,0,0,0,0xC,0x14D,0x64,{5605,5606}},{0,0,0,0,0,0,0xE,0x236,0x24A,{5607,5608}},{0,0,0,0,0,0,0,0x272,0x2D6,{5609,5610}},{0,0,0,0,0,0,0x2,0xE9,0xE2,{5611,5612}},{0,0,0,0,0,0,0,0x474,0x571,{5609,5613}},{0,0,0,0,0,0,0x8,0xAD,0x92,{5614,5615}},{0,0,0,0,0,0,0,0x46D,0x564,{5609,5616}},{0,0,0,0,0,0,0xA,0x32D,0xE2,{5617,5618}},{0,0,0,0,0,0,0x8,0x3F5,0x56A,{5619,5620}},{0,0,0,0,0,0,0x2,0x9C40,0x6F4,{5621,5622}},{0,0,0,0,0,0,0xC,0x9C40,0x78D,{5623,5624}},{0,0,0,0,0,0,0xA,0xB4,0x71,{5625,5626}},{0,0,0,0,0,0,0x4,0x9C40,0x6FA,{5627,5622}},{0,0,0,0,0,0,0x6,0x9C40,0x9B0,{5628,5629}},{0,0,0,0,0,0,0x6,0x132A,0x2DD,{5630,5631}},{0,0,0,0,0,0,0xA,0xD91,0x85,{5632,5633}},{0,0,0,0,0,0,0xA,0xDFC,0x85,{5632,5634}},{0,0,0,0,0,0,0x8,0x1212,0x898,{5635,5636}},{0,0,0,0,0,0,0x8,0x114A,0x219E,{5637,5638}},{0,0,0,0,0,0,0xE,0x33F4,0x3F20,{2942,5639}},{0,0,0,0,0,0,0xA,0x4A9,0x585,{5640,5641}},{0,0,0,0,0,0,0xE,0x2BC,0x5B4,{5642,5643}},{0,0,0,0,0,0,0x8,0x9C40,0x11E,{5644,5645}},{0,0,0,0,0,0,0x6,0x1205,0x88A,{4106,5646}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{5647,1069}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{5648,5649}},{0,0,0,0,0,0,0x9,0x9C40,0x20E,{5650,5651}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{4059,5652}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{5653,5654}},{0,0,0,0,0,0,0xA,0x9C40,0x10A,{5655,5656}},{0,0,0,0,0,0,0xC,0x9C40,0x56,{5657,5658}},{0,0,0,0,0,0,0xA,0xE81,0x1122,{2564,2565}},{0,0,0,0,0,0,0,0xE4C,0x1096,{5659,2565}},{0,0,0,0,0,0,0,0x924,0xACE,{5660,5661}},{0,0,0,0,0,0,0x8,0x618,0x5D,{5662,5663}},{0,0,0,0,0,0,0x1,0x79A,0x2D6,{5664,5665}},{0,0,0,0,0,0,0xA,0x10F4,0x5D,{5666,5667}},{0,0,0,0,0,0,0xE,0x9C40,0x99,{5668,1065}},{0,0,0,0,0,0,0x8,0x9C40,0x71,{5669,5670}},{0,0,0,0,0,0,0xE,0x9C40,0x14D,{5671,5672}},{0,0,0,0,0,0,0,0x9C40,0x3B9,{5673,5674}},{0,0,0,0,0,0,0,0x3BBD,0x5D,{5675,5676}},{0,0,0,0,0,0,0x6,0x46B4,0x64,{5677,5678}},{0,0,0,0,0,0,0x6,0x9C40,0x53C,{1116,3893}},{0,0,0,0,0,0,0xE,0x7DD,0xDC,{5679,5680}},{0,0,0,0,0,0,0xE,0x1658,0x110E,{5681,5682}},{0,0,0,0,0,0,0xE,0x9C40,0x391,{5683,5684}},{0,0,0,0,0,0,0x6,0x9C40,0x52E,{5685,3893}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{5686,5687}},{0,0,0,0,0,0,0xA,0x12B2,0x5D,{5688,5689}},{0,0,0,0,0,0,0x6,0x161C,0xA71,{5690,5691}},{0,0,0,0,0,0,0xC,0x506,0x564,{5692,5693}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1123,1124}},{0,0,0,0,0,0,0xE,0xA7E,0x2D6,{5694,5695}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{3904,3905}},{0,0,0,0,0,0,0xC,0x9C40,0x50,{5696,5697}},{0,0,0,0,0,0,0x8,0x9C40,0x6A,{5698,5699}},{0,0,0,0,0,0,0xA,0x9C40,0x50,{2543,5700}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{5701,5702}},{0,0,0,0,0,0,0xA,0x9C40,0x56,{5703,5704}},{0,0,0,0,0,0,0xE,0xF50,0x64,{5705,5706}},{0,0,0,0,0,0,0xA,0x9C40,0x71,{5707,5708}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{1241,5709}},{0,0,0,0,0,0,0x2,0x9C40,0x71,{5710,5711}},{0,0,0,0,0,0,0x7,0x9C40,0x175,{2575,3909}},{0,0,0,0,0,0,0xC,0x9C40,0x8C,{5712,5713}},{0,0,0,0,0,0,0x2,0x9C40,0x6A,{5714,5715}},{0,0,0,0,0,0,0xE,0x9C40,0xFD,{5716,5717}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{2208,2209}},{0,0,0,0,0,0,0xD,0x9C40,0x47A,{5718,5719}},{0,0,0,0,0,0,0xA,0xF50,0x64,{5720,5721}},{0,0,0,0,0,0,0x8,0x16BC,0xAC1,{5722,865}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{5723,5724}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{2208,5725}},{0,0,0,0,0,0,0x8,0x571,0x578,{5726,5727}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{5728,5729}},{0,0,0,0,0,0,0xA,0x231A,0x29ED,{5730,5731}},{0,0,0,0,0,0,0x6,0x5C8,0xAFD,{5732,5733}},{0,0,0,0,0,0,0x5,0x9C40,0x7A8,{5734,5735}},{0,0,0,0,0,0,0,0x9C40,0x445,{5736,5737}},{0,0,0,0,0,0,0x4,0x23CE,0x2A8D,{5738,5739}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{5740,5741}},{0,0,0,0,0,0,0x2,0xCFE,0xAE2,{2873,2874}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{870,5742}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{5743,5742}},{0,0,0,0,0,0,0x6,0x9C40,0x1A61,{5744,5745}},{0,0,0,0,0,0,0x2,0x9C40,0x4016,{5746,5746}},{0,0,0,0,0,0,0x6,0x9C40,0x1A61,{5744,5747}},{0,0,0,0,0,0,0x6,0x9C40,0x12C,{5748,5749}},{0,0,0,0,0,0,0xE,0x9C40,0x4CE0,{5750,5751}},{0,0,0,0,0,0,0x4,0x9C40,0x4455,{5752,5753}},{0,0,0,0,0,0,0x8,0xAF6,0xE16,{4030,4031}},{0,0,0,0,0,0,0x6,0x122D,0x8C0,{2532,5754}},{0,0,0,0,0,0,0x8,0x305,0xE2,{1264,5755}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{3749,5756}},{0,0,0,0,0,0,0xE,0x9C40,0x870,{5757,5758}},{0,0,0,0,0,0,0xE,0x640,0x8A5,{5759,5760}},{0,0,0,0,0,0,0,0x924,0xAD5,{5761,5762}},{0,0,0,0,0,0,0xE,0x3C6,0x474,{5763,5764}},{0,0,0,0,0,0,0xA,0x4CA,0x571,{5765,584}},{0,0,0,0,0,0,0x1,0x104,0x190,{281,281}},{0,0,0,0,0,0,0,0x3C0,0x459,{2303,5766}},{0,0,0,0,0,0,0x4,0x111,0x42,{5767,5767}},{0,0,0,0,0,0,0,0x2D0,0x2BC,{5768,5769}},{0,0,0,0,0,0,0xE,0x4DA8,0x5592,{5770,5771}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{5772,5773}},{0,0,0,0,0,0,0x8,0x9C40,0x4F74,{898,4032}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{757,758}},{0,0,0,44,0x8,0,0,0x3E8,0xA,{5774,5775}},{0,0,0,36,0x8,0,0,0x3E8,0xA,{5776,5777}},{0,0,0,67,0x8,0,0xE,0x3E8,0xA,{5778,5779}},{0,0,0,60,0x10,0,0x2,0x3E8,0xA,{5780,5780}},{0,0,0,60,0x10,0,0x2,0x3E8,0xA,{5781,5781}},{0,0,0,60,0x10,0,0x1,0x3E8,0xA,{5782,5782}},{0,0,0,48,0x18,0,0x5,0x3E8,0xA,{5783,5783}},{0,0,0,60,0x28,0,0xD,0x3E8,0xA,{5784,5785}},{0,0,0,52,0x18,0,0x5,0x3E8,0xA,{5786,5783}},{0,0,0,60,0x28,0,0x3,0x3E8,0xA,{5787,5788}},{0,0,0,55,0x18,0,0x5,0x3E8,0xA,{5783,5783}},{0,0,0,60,0x28,0,0x3,0x3E8,0xA,{5789,5790}},{0,0,0,60,0x18,0,0x5,0x3E8,0xA,{5783,5783}},{0,0,0,64,0x18,0,0x5,0x3E8,0xA,{5783,5783}},{0,0,0,48,0x20,0,0xA,0x3E8,0xA,{5791,5792}},{0,0,0,67,0x18,0,0x5,0x3E8,0xA,{5783,5783}},{0,0,0,60,0x28,0,0x3,0x3E8,0xA,{5793,5793}},{0,0,0,48,0x20,0,0x5,0x3E8,0xA,{5794,5795}},{0,0,0,48,0x28,0,0xB,0x3E8,0xA,{5796,5797}},{0,0,0,48,0x10,0,0xA,0x3E8,0xA,{5798,5799}},{0,0,0,60,0x20,0,0x2,0x3E8,0xA,{5800,5801}},{0,0,0,48,0x20,0,0xA,0x3E8,0xA,{5791,5791}},{0,0,0,60,0x20,0,0xC,0x3E8,0xA,{5802,5803}},{0,0,0,60,0x20,0,0xB,0x3E8,0xA,{2382,2382}},{0,0,0,60,0x20,0,0xD,0x3E8,0xA,{5804,5804}},{0,0,0,67,0x18,0,0x3,0x3E8,0xA,{5805,5805}},{0,0,0,62,0x18,0,0xB,0x3E8,0xA,{5805,5805}},{0,0,0,67,0x18,0,0xA,0x3E8,0xA,{5806,5806}},{0,0,0,67,0x18,0,0xA,0x3E8,0xA,{5807,5807}},{0,0,0,60,0x18,0,0xA,0x3E8,0xA,{5807,5807}},{0,0,0,53,0x20,0,0xA,0x3E8,0xA,{5791,5791}},{0,0,0,48,0x20,0,0xA,0x3E8,0xA,{5808,5808}},{0,0,0,48,0x10,0,0xA,0x3E8,0xA,{5798,5809}},{0,0,0,60,0x10,0,0xA,0x3E8,0xA,{2358,5810}},{0,0,0,79,0x18,0,0xB,0x3E8,0xA,{5811,5811}},{0,0,0,79,0x18,0,0xB,0x3E8,0xA,{5812,5812}},{0,0,0,60,0x10,0,0xA,0x3E8,0xA,{5813,5813}},{0,0,0,60,0x10,0,0xA,0x3E8,0xA,{5814,5814}},{0,0,0,91,0x18,0,0x2,0x3E8,0xA,{5764,5764}},{0,0,0,60,0x18,0,0x3,0x3E8,0xA,{5815,5815}},{0,0,0,53,0x18,0,0x3,0x3E8,0xA,{5816,5816}},{0,0,0,60,0x10,0,0xA,0x3E8,0xA,{2363,2363}},{0,0,0,60,0x10,0,0xA,0x3E8,0xA,{5817,5817}},{0,0,0,79,0x8,0,0x8,0x3E8,0xA,{743,5818}},{0,0,0,79,0x8,0,0x8,0x3E8,0xA,{743,5819}},{0,0,0,0,0,0,0x6,0x1360,0x2C9,{5820,5821}},{0,0,0,0,0,0,0x6,0x134C,0x2D0,{5822,5821}},{0,0,0,0,0,0,0x6,0x134C,0x2C9,{5823,5821}},{0,0,0,0,0,0,0xA,0x11B5,0x15FA,{5824,5825}},{0,0,0,0,0,0,0xA,0x910,0xAA0,{5826,5827}},{0,0,0,0,0,0,0x6,0x1165,0x244,{5828,5829}},{0,0,0,0,0,0,0,0x9C40,0x5D,{5830,5831}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{5832,5833}},{0,0,0,0,0,0,0,0x9C40,0x5D,{5834,5835}},{0,0,0,0,0,0,0,0x9C40,0x5D,{5836,5831}},{0,0,0,0,0,0,0x2,0x9C40,0x2D0,{5837,5838}},{0,0,0,0,0,0,0x2,0x9C40,0x2C9,{5839,5838}},{0,0,0,0,0,0,0x2,0x9C40,0x2D0,{5840,5838}},{0,0,0,0,0,0,0x2,0x9C40,0x2D0,{5841,5842}},{0,0,0,0,0,0,0x8,0x10A4,0x10F4,{5843,5844}},{0,0,0,0,0,0,0x8,0xEE5,0x10AA,{5845,5846}},{0,0,0,0,0,0,0x8,0x1205,0x87D,{5847,5848}},{0,0,0,0,0,0,0x8,0x4D1,0x64,{5849,5850}},{0,0,0,0,0,0,0x8,0x4D1,0x64,{5849,5851}},{0,0,0,0,0,0,0x8,0x4D1,0x64,{5852,5851}},{0,0,0,0,0,0,0x8,0x11A8,0x10F4,{5432,5853}},{0,0,0,0,0,0,0x8,0xE9C,0x10E6,{5432,5854}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{5855,5856}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{5857,5856}},{0,0,0,0,0,0,0xE,0x9C40,0x189,{5858,5859}},{0,0,0,0,0,0,0xE,0x9C40,0x182,{5860,5861}},{0,0,0,0,0,0,0x6,0x9C40,0x118,{5862,5863}},{0,0,0,0,0,0,0x8,0x172D,0x23D,{5864,5865}},{0,0,0,0,0,0,0x8,0x1720,0x258,{5866,5867}},{0,0,0,0,0,0,0x8,0x16EA,0x5D,{5868,5869}},{0,0,0,0,0,0,0x2,0x4642,0x52C9,{5870,5871}},{0,0,0,0,0,0,0x2,0x9C40,0x3764,{5872,5873}},{0,0,0,0,0,0,0x2,0x9C40,0x71,{5874,5875}},{0,0,0,0,0,0,0x8,0x47CC,0x151E,{5876,5877}},{0,0,0,0,0,0,0x6,0x9C40,0x375D,{5878,5879}},{0,0,0,0,0,0,0,0x9C40,0x1129,{5880,5881}},{0,0,0,0,0,0,0x8,0x4628,0x5376,{5882,5883}},{0,0,0,0,0,0,0x8,0x3868,0xC1,{5884,5885}},{0,0,0,0,0,0,0x8,0x9C40,0x2954,{5886,5887}},{0,0,0,0,0,0,0x6,0x39B5,0x436C,{5888,5889}},{0,0,0,0,0,0,0xC,0x9C40,0xD5,{5890,5891}},{0,0,0,0,0,0,0,0x9C40,0x13CA,{5892,5893}},{0,0,0,0,0,0,0x8,0x9C40,0x244,{5894,5895}},{0,0,0,0,0,0,0x6,0x9C40,0x542,{5896,5897}},{0,0,0,0,0,0,0x8,0x3311,0x21CD,{5898,5899}},{0,0,0,0,0,0,0xA,0x9C40,0xDC,{5900,5901}},{0,0,0,0,0,0,0x6,0x9C40,0x1262,{5902,5903}},{0,0,0,0,0,0,0xA,0x9C40,0x51A,{5904,5905}},{0,0,0,0,0,0,0x2,0x9C40,0x528,{5902,5906}},{0,0,0,0,0,0,0xE,0x9C40,0xC8,{5907,5908}},{0,0,0,0,0,0,0xE,0x9C40,0x11E,{5491,5909}},{0,0,0,0,0,0,0xE,0x9C40,0x190,{5491,5910}},{0,0,0,0,0,0,0xE,0x9C40,0x7E,{5493,5911}},{0,0,0,0,0,0,0xE,0x9C40,0x154,{5495,5912}},{0,0,0,0,0,0,0,0x224C,0x1471,{1260,5503}},{0,0,0,0,0,0,0,0x1039,0x1456,{5913,5914}},{0,0,0,0,0,0,0x6,0x208D,0x82D,{5915,5916}},{0,0,0,0,0,0,0x6,0x208D,0x82D,{5917,5916}},{0,0,0,0,0,0,0x6,0x20B5,0x8B9,{5918,5916}},{0,0,0,0,0,0,0x6,0x20E4,0x855,{5919,5916}},{0,0,0,0,0,0,0,0x254A,0x1546,{984,5920}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{5921,5922}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{5923,5924}},{0,0,0,0,0,0,0x4,0x675,0xC1,{5925,5926}},{0,0,0,0,0,0,0x4,0xA92,0x64,{5927,5928}},{0,0,0,0,0,0,0x4,0x2DA0,0x5D,{5929,5930}},{0,0,0,0,0,0,0x4,0x195D,0x460,{5931,5932}},{0,0,0,0,0,0,0xA,0x9C40,0x49,{5933,5934}},{0,0,0,0,0,0,0xA,0x9C40,0x50,{5935,5936}},{0,0,0,0,0,0,0x6,0x9C40,0x71,{5937,5938}},{0,0,0,0,0,0,0x6,0x9C40,0x71,{5939,5938}},{0,0,0,0,0,0,0x2,0x9C40,0x71,{5940,5941}},{0,0,0,0,0,0,0x2,0x9C40,0x71,{5942,5941}},{0,0,0,0,0,0,0xA,0x9C40,0x71,{5943,5944}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{5945,5946}},{0,0,0,0,0,0,0xC,0x9C40,0x64,{5536,5947}},{0,0,0,0,0,0,0xC,0x9C40,0x64,{5948,5947}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{5949,5947}},{0,0,0,0,0,0,0xC,0x9C40,0x175,{5430,5950}},{0,0,0,0,0,0,0xC,0x9C40,0x265,{5951,5952}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{5953,5954}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{5542,5955}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{5956,5957}},{0,0,0,0,0,0,0xE,0x9C40,0x24A,{5958,5959}},{0,0,0,0,0,0,0xE,0x9C40,0x182,{5960,5961}},{0,0,0,0,0,0,0xC,0x9C40,0x258,{5962,5963}},{0,0,0,0,0,0,0xC,0x9C40,0x2D6,{5964,5965}},{0,0,0,0,0,0,0xC,0x9C40,0x2D6,{5524,5966}},{0,0,0,0,0,0,0xE,0x9C40,0x2D6,{5967,5968}},{0,0,0,0,0,0,0xC,0x9C40,0xDC,{5969,5970}},{0,0,0,0,0,0,0xC,0x9C40,0x182,{5534,5971}},{0,0,0,0,0,0,0xE,0x9C40,0x161,{5972,5973}},{0,0,0,0,0,0,0x6,0x9C40,0x56A,{5974,5975}},{0,0,0,0,0,0,0x6,0x9C40,0x56A,{5976,5977}},{0,0,0,0,0,0,0xE,0xE4C,0x1108,{5978,2565}},{0,0,0,0,0,0,0xE,0x9C40,0xE38,{5979,5980}},{0,0,0,0,0,0,0x6,0x9C40,0xAAD,{5434,5981}},{0,0,0,0,0,0,0xE,0x380A,0x426E,{5443,5982}},{0,0,0,0,0,0,0,0x2DD,0x56A,{5983,5984}},{0,0,0,0,0,0,0xA,0x9C40,0x1589,{5985,5986}},{0,0,0,0,0,0,0x6,0x9C40,0x139,{5987,5988}},{0,0,0,0,0,0,0,0x9C40,0x215C,{5989,5990}},{0,0,0,0,0,0,0xC,0x15BE,0x1F46,{5991,5992}},{0,0,0,0,0,0,0x2,0x9C40,0x64,{5551,5993}},{0,0,0,0,0,0,0x6,0x9C40,0x21EE,{5744,5990}},{0,0,0,0,0,0,0xE,0x4CA,0xABA,{5994,5995}},{0,0,0,0,0,0,0xA,0x966,0x564,{5996,5997}},{0,0,0,0,0,0,0xE,0x26C,0x2D6,{5998,5999}},{0,0,0,0,0,0,0xE,0x111,0x49,{755,6000}},{0,0,0,0,0,0,0xE,0x21C,0x5D,{651,6001}},{0,0,0,0,0,0,0xE,0x21C,0x5D,{651,6002}},{0,0,0,0,0,0,0xE,0x21C,0x5D,{651,6003}},{0,0,0,0,0,0,0xE,0x460,0xAE2,{6004,3895}},{0,0,0,0,0,0,0x8,0x9C40,0xDC,{6005,6006}},{0,0,0,0,0,0,0xE,0x1C62,0x217D,{6007,6008}},{0,0,0,0,0,0,0xE,0x9C40,0x4DFE,{6009,6010}},{0,0,0,0,0,0,0x8,0x1C62,0x21C0,{960,6011}},{0,0,0,0,0,0,0,0xFD,0x5D,{6012,1343}},{0,0,0,0,0,0,0x1,0xFD,0x5D,{6013,1343}},{0,0,0,0,0,0,0x1,0x125,0x5D,{6014,1343}},{0,0,0,0,0,0,0x1,0x3C6,0x5D,{6015,1343}},{0,0,0,0,0,0,0x1,0x97A,0x5D,{6016,6017}},{0,0,0,65,0,0,0xE,0x125,0x140,{270,271}},{0,0,0,20,0,0,0xC,0x73D,0x87D,{276,277}},{0,0,0,40,0,0,0xE,0x24A,0x190,{306,307}},{12,0,0,0,0x1,0,0x302,0x91D,0xABA,{350,351,2621,2622} },{0,0,0,0,0x1,0,0xB0A,0x23CE,0x2DD,{6018,6019,6020,3885} },{12,0,0,0,0x1,0,0x706,0x9C40,0x5D,{2627,2628,352,353} },{12,0,0,0,0x1,0,0xD0C,0x9C40,0x341,{2638,2639,2640,2641} },{0,0,0,0,0x1,0,0xA0A,0x1F39,0xE2,{434,435,6021,437} },{0,0,0,0,0x1,0,0xB0A,0x35C,0x3C6,{2764,242,2765,2766} },{0,0,0,0,0x1,0,0x908,0x9C40,0x2BC,{2767,2768,2769,2768} },{0,0,0,39,0x1,0,0x100,0x26C,0x2DD,{6022,6023,257,256} },{0,0,0,58,0x1,0,0xF0E,0xF0,0x12C,{691,6024,254,255} },{0,0,0,48,0x1,0,0x100,0x208,0x258,{2774,6023,256,257} },{0,0,0,49,0x1,0,0x100,0x201,0x258,{2774,6023,256,256} },{0,0,0,51,0x1,0,0x100,0x201,0x258,{2774,6023,256,256} },{0,0,0,54,0x1,0,0x100,0x1FA,0x251,{2774,6023,256,256} },{0,0,0,57,0x1,0,0x100,0x1FA,0x251,{2774,6023,256,256} },{0,0,0,60,0x1,0,0x100,0x1FA,0x251,{2774,6023,256,256} },{0,0,0,70,0x1,0,0x706,0xB68,0xD76,{6025,6026,6027,2781} },{0,0,0,80,0x1,0,0x908,0xE9,0x104,{274,2793,2794,275} },{0,0,0,44,0x1,0,0x706,0x708,0x83A,{6025,6028,2780,2800} },{0,0,0,0,0,0,0x6,0x209A,0x898,{904,6029}},{0,0,0,0,0,0,0x6,0x209A,0x884,{6030,6031}},{0,0,0,0,0,0,0x8,0xEBD,0x64,{6032,6033}},{0,0,0,0,0,0,0x6,0x20A1,0x898,{6034,5330}},{0,0,0,0,0,0,0x6,0x1255,0x5D,{6035,6036}},{0,0,0,0,0,0,0xF,0x1CAC,0x8C,{6037,6038}},{0,0,0,0,0,0,0xA,0xDB2,0x85,{6039,6040}},{0,0,0,0,0,0,0x6,0x20A1,0x898,{904,6041}},{0,0,0,0,0,0,0x8,0xEB6,0x1122,{6042,6043}},{0,0,0,0,0,0,0x8,0x2321,0x14E2,{6044,6045}},{0,0,0,0,0,0,0,0x4A2,0x5D,{6046,6047}},{0,0,0,0,0,0,0,0x203D,0x157C,{6048,3908}},{0,0,0,0,0,0,0xA,0xE4C,0x110E,{6049,6050}},{0,0,0,0,0,0,0xE,0xCE,0xE2,{6051,6052}},{0,0,0,0,0,0,0x4,0x9C40,0x1B4A,{6053,6054}},{0,0,0,0,0,0,0,0x4B0,0x57E,{6055,6056}},{0,0,0,0,0,0,0x9,0x9C40,0x3A41,{6057,6058}},{0,0,0,0,0,0,0x9,0x9C40,0x3BC4,{6059,6060}},{0,0,0,0,0,0,0xE,0x9C40,0x466,{6061,6062}},{0,0,0,0,0,0,0x3,0x9C40,0x5D,{6063,6064}},{0,0,0,0,0,0,0xE,0x9C40,0xDC,{6065,6066}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{5653,6067}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{6068,6069}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{6070,6071}},{0,0,0,0,0,0,0,0x884,0x58C,{6072,6073}},{0,0,0,0,0,0,0xA,0x3965,0x2B69,{6074,6075}},{0,0,0,0,0,0,0xE,0x2AB5,0x42ED,{6076,6077}},{0,0,0,0,0,0,0xA,0x995,0xE2,{6078,6079}},{0,0,0,0,0,0,0xA,0xE2A,0x10ED,{6080,2931}},{0,0,0,0,0,0,0x8,0x9C40,0x182,{6081,6082}},{0,0,0,0,0,0,0x8,0x9C40,0xD5,{6083,6084}},{0,0,0,0,0,0,0x8,0x9C40,0xD5,{6085,6086}},{0,0,0,0,0,0,0xA,0x2496,0xE2,{1230,6087}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{6088,6089}},{0,0,0,0,0,0,0x8,0x9C40,0x369C,{6090,6091}},{0,0,0,0,0,0,0xA,0x9C40,0x7E,{6092,6093}},{0,0,0,0,0,0,0x6,0x64D,0x578,{6035,6094}},{0,0,0,0,0,0,0,0x1291,0x29C5,{6095,6096}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{6097,1025}},{0,0,0,0,0,0,0x8,0x9C40,0x251,{6098,6099}},{0,0,0,0,0,0,0xE,0x9C40,0x2F8,{6100,6101}},{0,0,0,0,0,0,0xE,0x9C40,0x305,{6102,6101}},{0,0,0,0,0,0,0,0x9C40,0x47A,{6103,6104}},{0,0,0,0,0,0,0x1,0x7BC,0x2C2,{6105,6106}},{0,0,0,0,0,0,0x6,0x9C40,0x535,{6107,6108}},{0,0,0,0,0,0,0x6,0x8DA,0x571,{6109,6110}},{0,0,0,0,0,0,0xC,0x9C40,0x14BA,{6111,6112}},{0,0,0,0,0,0,0,0x93E,0xABA,{6113,6114}},{0,0,0,0,0,0,0x6,0x9C40,0x52E,{6115,6116}},{0,0,0,0,0,0,0x6,0x9C40,0x52E,{6117,6118}},{0,0,0,0,0,0,0x6,0x9C40,0x909,{6119,6120}},{0,0,0,0,0,0,0x6,0x9C40,0x53C,{6121,6108}},{0,0,0,0,0,0,0,0x9C40,0x474,{6122,6123}},{0,0,0,0,0,0,0,0x9C40,0x2C9,{6124,6125}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{6126,6127}},{0,0,0,0,0,0,0,0x3A5,0x2E4,{6128,6129}},{0,0,0,0,0,0,0xE,0x9C40,0x92,{6130,6131}},{0,0,0,0,0,0,0xE,0x9C40,0x8C,{6132,6133}},{0,0,0,0,0,0,0xE,0x9C40,0x2D0,{6134,6135}},{0,0,0,0,0,0,0xE,0x9C40,0x8C,{6136,6137}},{0,0,0,0,0,0,0xC,0x9C40,0x71,{6138,6139}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6140,6141}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{6142,6143}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{6144,6143}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6145,6146}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6145,6147}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6148,6149}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6150,6151}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6150,6147}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6148,6152}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{6153,6154}},{0,0,0,0,0,0,0xC,0x9C40,0x64,{6155,6156}},{0,0,0,0,0,0,0xB,0x9C40,0x92,{6157,6158}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{6159,6160}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{6161,6162}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{6163,6160}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{6164,6165}},{0,0,0,0,0,0,0xE,0x9C40,0x92,{6166,6167}},{0,0,0,0,0,0,0xD,0x9C40,0x47A,{6168,6169}},{0,0,0,0,0,0,0x7,0x9C40,0x50,{6170,6171}},{0,0,0,0,0,0,0x8,0x462E,0xE2,{6172,6173}},{0,0,0,0,0,0,0x8,0x47B1,0xE2,{6174,6175}},{0,0,0,0,0,0,0x6,0x10A,0x64,{6176,6177}},{0,0,0,0,0,0,0xA,0x9C40,0x92,{6178,6179}},{0,0,0,0,0,0,0xA,0x9C40,0xDC,{6180,6181}},{0,0,0,0,0,0,0xF,0x9C40,0x92,{6182,6183}},{0,0,0,0,0,0,0xD,0x9C40,0x17C,{6184,6185}},{0,0,0,0,0,0,0xC,0x9C40,0xD5,{6186,6187}},{0,0,0,0,0,0,0x5,0x9C40,0xD2D,{6188,6189}},{0,0,0,0,0,0,0,0x9C40,0x1D9,{6190,6191}},{0,0,0,0,0,0,0x8,0x9C40,0xD5,{6192,6193}},{0,0,0,0,0,0,0x8,0x9C40,0xABA,{6194,6195}},{0,0,0,0,0,0,0x6,0x9C40,0x44B9,{6196,6197}},{0,0,0,0,0,0,0xE,0x9C40,0x182,{6198,6199}},{0,0,0,0,0,0,0,0x9C40,0xE2,{6200,6201}},{0,0,0,0,0,0,0xA,0x9C40,0x40F9,{6202,6203}},{0,0,0,0,0,0,0xA,0x9C40,0x8B9,{6204,6205}},{0,0,0,0,0,0,0x6,0x9C40,0x521,{6206,6207}},{0,0,0,0,0,0,0,0x9C40,0x1546,{6208,6209}},{0,0,0,0,0,0,0x6,0x9C40,0x891,{6210,6211}},{0,0,0,0,0,0,0x6,0x9C40,0x2281,{6212,6213}},{0,0,0,0,0,0,0x6,0x9C40,0x2260,{6214,6215}},{0,0,0,0,0,0,0,0x9C40,0x2266,{6216,6217}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{6218,6219}},{0,0,0,0,0,0,0,0x256C,0x1596,{6220,6221}},{0,0,0,0,0,0,0x4,0x9C40,0x5D,{6222,6223}},{0,0,0,0,0,0,0,0x29B1,0xE2,{6220,6224}},{0,0,0,0,0,0,0x8,0x924,0xAE2,{6225,6226}},{0,0,0,0,0,0,0xA,0xE59,0x110E,{6227,6228}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{6229,6230}},{0,0,0,0,0,0,0xE,0x9C40,0x189,{6100,6231}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6232,6147}},{0,0,0,0,0,0,0xC,0x8E1,0xAA0,{6233,6234}},{0,0,0,0,0,0,0xC,0x14D,0x182,{6235,6236}},{0,0,0,0,0,0,0,0x931,0xAAD,{6237,6238}},{0,0,0,0,0,0,0,0x11E,0x146,{6239,6240}},{0,0,0,0,0,0,0,0xE38,0x10E6,{6241,6242}},{0,0,0,0,0,0,0xE,0xE88,0x107C,{6243,6244}},{0,0,0,0,0,0,0,0xE74,0x1096,{6245,6246}},{0,0,0,0,0,0,0xA,0xBCC,0x5D,{6247,6248}},{0,0,0,0,0,0,0,0x319,0x4CA,{6249,6250}},{0,0,0,0,0,0,0x6,0x9C40,0x182,{6251,6252}},{0,0,0,0,0,0,0xA,0x9C40,0x2AD0,{6247,6253}},{0,0,0,0,0,0,0xE,0x111,0x50,{6254,6255}},{0,0,0,0,0,0,0x8,0x9C40,0x8C,{6256,6257}},{0,0,0,0,0,0,0x8,0x9C40,0x8C,{6258,6259}},{0,0,0,0,0,0,0xA,0x9C40,0x2AFE,{6247,6260}},{0,0,0,0,0,0,0xA,0x9C40,0x2DD,{6261,6262}},{0,0,0,0,0x4,0,0,0x6,0,{6263,6263}},{0,0,0,46,0,0,0,0xDC,0x5D,{693,1343}},{0,0,0,47,0,0,0x6,0x3CD,0xC1,{6264,6265}},{0,0,0,64,0,0,0xE,0x78,0x85,{6266,6267}},{0,0,0,40,0,0,0xE,0x154,0x189,{6268,6269}},{0,0,0,48,0,0,0xE,0x64,0x5D,{6270,6271}},{0,0,0,48,0,0,0xE,0x11E,0x146,{929,6272}},{0,0,0,46,0,0,0x1,0x1D9,0x229,{6273,6274}},{0,0,0,111,0,0,0xE,0xD5,0xF0,{6275,6276}},{0,0,0,49,0,0,0x1,0x1D2,0x229,{6273,6277}},{0,0,0,56,0,0,0xE,0x85,0x85,{6278,6279}},{0,0,0,52,0,0,0x1,0x208,0x258,{6273,6280}},{0,0,0,96,0,0,0xE,0x542,0x640,{6281,6282}},{0,0,0,54,0,0,0x1,0x208,0x258,{6273,6280}},{0,0,0,57,0,0,0x1,0x3CD,0x3DA,{6283,6284}},{0,0,0,82,0,0,0xE,0x62C,0x73D,{6285,6286}},{0,0,0,60,0,0,0x1,0x201,0x258,{6273,6280}},{0,0,0,60,0,0,0xE,0x744,0x884,{6287,6288}},{0,0,0,92,0,0,0xE,0x3C0,0x625,{6289,6290}},{0,0,0,60,0,0,0xE,0xB4,0xC1,{6291,6292}},{0,0,0,58,0,0,0x4,0xC1,0x24A,{6293,6294}},{0,0,0,22,0,0,0xE,0x924,0xAA0,{6295,6296}},{0,0,0,60,0,0,0xE,0x73D,0x85C,{6287,6297}},{0,0,0,72,0,0,0,0x104,0xC1,{6298,6299}},{0,0,0,77,0,0,0,0xFD,0xC1,{6300,6301}},{0,0,0,70,0,0,0,0xCE,0xAD,{6302,6303}},{0,0,0,75,0,0,0,0xAD,0xAD,{6304,6305}},{0,0,0,69,0,0,0,0x196,0x118,{6306,6307}},{0,0,0,59,0,0,0xC,0x17C,0x1B8,{6308,6309}},{0,0,0,48,0,0,0xC,0x175,0x6A,{6310,6311}},{0,0,0,89,0,0,0,0x1B1,0x1F4,{6312,6313}},{0,0,0,84,0,0,0,0x326,0x1F4,{6312,6314}},{0,0,0,33,0,0,0xE,0xF0,0x92,{6315,6316}},{0,0,0,55,0,0,0xE,0xDC,0xAD,{296,6317}},{0,0,0,58,0,0,0xC,0x208,0x23D,{6318,6319}},{0,0,0,52,0,0,0xC,0x208,0x23D,{6320,6319}},{0,0,0,57,0,0,0xE,0xA6,0x92,{6321,6322}},{0,0,0,57,0,0,0xE,0xF0,0xE9,{6321,6323}},{0,0,0,85,0,0,0x6,0xDC,0xF6,{6324,6325}},{0,0,0,68,0,0,0x6,0xAD,0xC1,{6326,6327}},{0,0,0,61,0,0,0x6,0xDC,0xF6,{6326,6327}},{0,0,0,64,0,0,0,0x154,0x2C2,{6328,6329}},{0,0,0,44,0,0,0,0x438,0x46D,{6328,6330}},{0,0,0,100,0,0,0xA,0xC1,0x5D,{6331,6332}},{0,0,0,100,0,0,0xA,0x319,0x64,{6333,6334}},{0,0,0,0,0x4,0,0,0x6,0,{1340,1340}},{0,0,0,0,0,0,0x6,0x11B5,0x862,{2532,2533}},{0,0,0,0,0,0,0,0x3818,0x460,{902,1373}},{0,0,0,0,0,0,0xE,0xF0D,0x15B1,{6335,6336}},{0,0,0,0,0,0,0xA,0xDFC,0x85,{5632,6337}},{0,0,0,0,0,0,0x8,0x1219,0x891,{633,962}},{0,0,0,0,0,0,0x8,0x1C62,0x21C0,{960,6338}},{0,0,0,0,0,0,0x8,0x3340,0x21DA,{633,2593}},{0,0,0,0,0,0,0xA,0x1194,0x1518,{6339,6340}},{0,0,0,0,0,0,0xE,0x1BE,0x592,{6341,6342}},{0,0,0,0,0,0,0x8,0x2349,0x2975,{6343,6344}},{0,0,0,0,0,0,0,0x1205,0x88A,{6345,5646}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{1068,6346}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{4059,6347}},{0,0,0,0,0,0,0x9,0x9C40,0x208,{6348,6349}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{4059,6350}},{0,0,0,0,0,0,0xE,0x9C40,0x64,{1268,6351}},{0,0,0,0,0,0,0xA,0x9C40,0xF6,{5655,6352}},{0,0,0,0,0,0,0xC,0x9C40,0x56,{6353,5658}},{0,0,0,0,0,0,0xA,0xE45,0x1108,{6354,2565}},{0,0,0,0,0,0,0,0xE1D,0x10CC,{6355,2565}},{0,0,0,0,0,0,0xE,0x1672,0x10FA,{6356,6357}},{0,0,0,0,0,0,0,0x92A,0xAE2,{5660,6358}},{0,0,0,0,0,0,0x8,0x9C40,0x6A,{850,6359}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{2543,6360}},{0,0,0,0,0,0,0xA,0x10F4,0x5D,{6361,5667}},{0,0,0,0,0,0,0,0xE74,0x92,{6362,853}},{0,0,0,0,0,0,0xE,0x9C40,0xA0,{1022,6363}},{0,0,0,0,0,0,0x6,0xE74,0x1144,{6364,6365}},{0,0,0,0,0,0,0,0x48E,0x182,{6366,6367}},{0,0,0,0,0,0,0xA,0x9C40,0x244,{6368,6369}},{0,0,0,0,0,0,0xE,0x9C40,0xF0,{6370,6371}},{0,0,0,0,0,0,0,0x394A,0x5D,{6372,6373}},{0,0,0,0,0,0,0,0x37D5,0x56,{6103,6374}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{6375,6376}},{0,0,0,0,0,0,0x6,0x9C40,0x51A,{1116,4463}},{0,0,0,0,0,0,0xE,0x9C40,0x1AA,{6377,6378}},{0,0,0,0,0,0,0x6,0x9C40,0x2AE,{6379,6380}},{0,0,0,0,0,0,0xE,0x780,0x891,{670,6381}},{0,0,0,0,0,0,0xE,0x9C40,0x32D,{5683,6382}},{0,0,0,0,0,0,0xE,0x9C40,0x88A,{5757,6383}},{0,0,0,0,0,0,0xC,0x9C40,0x56,{6384,6385}},{0,0,0,0,0,0,0,0x9C40,0x64,{6386,6387}},{0,0,0,0,0,0,0x4,0x2350,0x2B05,{6388,6389}},{0,0,0,0,0,0,0x6,0x161C,0xA5D,{5690,6390}},{0,0,0,0,0,0,0,0x280,0x2D0,{6391,6392}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1123,6393}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1297,6394}},{0,0,0,0,0,0,0xE,0x9C40,0x1BE,{6395,6396}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{3904,6397}},{0,0,0,0,0,0,0xC,0x9C40,0x6A,{1298,6398}},{0,0,0,0,0,0,0xE,0x9C40,0x56,{6399,6400}},{0,0,0,0,0,0,0x8,0x9C40,0x6A,{5698,6401}},{0,0,0,0,0,0,0xA,0x9C40,0x49,{2543,6402}},{0,0,0,0,0,0,0xA,0x9C40,0x56,{5701,6403}},{0,0,0,0,0,0,0xA,0xECA,0x64,{1280,6404}},{0,0,0,0,0,0,0xA,0x9C40,0x56,{6405,6406}},{0,0,0,0,0,0,0xE,0xECA,0x5D,{6407,6408}},{0,0,0,0,0,0,0xA,0x9C40,0x71,{6409,6410}},{0,0,0,0,0,0,0x2,0x9C40,0x56,{1241,6411}},{0,0,0,0,0,0,0x8,0x9C40,0x6A,{6412,6413}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{1140,6414}},{0,0,0,0,0,0,0xA,0x9C40,0x58C,{4118,6415}},{0,0,0,0,0,0,0x7,0x9C40,0x17C,{2575,6416}},{0,0,0,0,0,0,0xE,0x9C40,0x6A,{6417,6418}},{0,0,0,0,0,0,0xA,0x9C40,0x58C,{4118,6419}},{0,0,0,0,0,0,0x6,0x189,0x175,{6420,6421}},{0,0,0,0,0,0,0xE,0x9C40,0x6A,{1144,6422}},{0,0,0,0,0,0,0xD,0x9C40,0x47A,{6423,6424}},{0,0,0,0,0,0,0xA,0xEE5,0x5D,{5720,6425}},{0,0,0,0,0,0,0x8,0x169A,0xAC1,{6426,6427}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{6428,6429}},{0,0,0,0,0,0,0x4,0x9C40,0xBA,{6430,6431}},{0,0,0,0,0,0,0x8,0x32D,0x2C2,{5726,6432}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{5728,6433}},{0,0,0,0,0,0,0x4,0x9C40,0x2AE,{6434,6435}},{0,0,0,0,0,0,0xA,0x47B8,0xA85,{6436,6437}},{0,0,0,0,0,0,0xA,0x9C40,0xE9,{6438,6439}},{0,0,0,0,0,0,0x6,0x9C40,0x1A89,{6440,6441}},{0,0,0,0,0,0,0x8,0x9C40,0x459,{6442,6443}},{0,0,0,0,0,0,0x4,0x8C0,0xA42,{6444,6445}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{5657,6446}},{0,0,0,0,0,0,0x3,0x9C40,0x3CCE,{6447,6448}},{0,0,0,0,0,0,0x2,0x916,0x571,{2873,6449}},{0,0,0,0,0,0,0x2,0x39C9,0x5D,{6450,6451}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{6452,6453}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6454,6455}},{0,0,0,0,0,0,0x2,0x9C40,0x1E8C,{6456,6457}},{0,0,0,0,0,0,0x6,0x9C40,0x1A1E,{6458,6459}},{0,0,0,0,0,0,0x6,0x9C40,0x2B0C,{6460,6461}},{0,0,0,0,0,0,0x4,0x47B8,0x55D5,{6462,6463}},{0,0,0,0,0,0,0x2,0x466,0x506,{6464,6465}},{0,0,0,0,0,0,0xE,0x9C40,0x4BA0,{6466,6467}},{0,0,0,0,0,0,0x6,0x9C40,0x44B2,{6468,6469}},{0,0,0,0,0,0,0,0x9C40,0xF28,{984,6470}},{0,0,0,0,0,0,0x8,0xB2C,0xD76,{4030,6471}},{0,0,0,0,0,0,0x6,0x9C40,0x521,{2532,6472}},{0,0,0,0,0,0,0,0x9C40,0x8C,{6473,6474}},{0,0,0,0,0,0,0,0x9C40,0x100A,{1163,6475}},{0,0,0,0,0,0,0x2,0x9C40,0x56,{3749,6476}},{0,0,0,0,0,0,0xA,0x9C40,0x80C,{6477,6478}},{0,0,0,0,0,0,0xE,0x3DA,0x459,{6479,6480}},{0,0,0,0,0,0,0,0x729,0x87D,{6481,6482}},{0,0,0,0,0,0,0,0xC8,0x2D6,{6483,6484}},{0,0,0,0,0,0,0xA,0x48E,0x556,{6485,6486}},{0,0,0,0,0,0,0x1,0x104,0x190,{281,6487}},{0,0,0,0,0,0,0x5,0x146,0x17C,{1170,6488}},{0,0,0,0,0,0,0xA,0x196,0x2EA,{6489,6490}},{0,0,0,0,0,0,0xE,0x3E8,0xADC,{6491,934}},{0,0,0,0,0,0,0x6,0x111,0x49,{6492,6493}},{0,0,0,0,0,0,0x2,0x2C9,0x2BC,{5768,6494}},{0,0,0,0,0,0,0x4,0x189,0x16E,{6495,6496}},{0,0,0,0,0,0,0x2,0x9C40,0xF50,{6497,6498}},{0,0,0,0,0,0,0,0x9C40,0x5D,{6499,6500}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{757,6501}},{0,0,0,0,0,0,0xE,0x4A9,0x585,{6502,6503}},{0,0,0,0,0,0,0xA,0x40E5,0x2D6,{6504,6505}},{0,0,0,0,0,0,0x8,0x1F54,0x2C2,{6506,6507}},{0,0,0,0,0,0,0xA,0x4711,0x189,{6508,6509}},{0,0,0,0,0,0,0x4,0x38AA,0x251,{6510,6511}},{0,0,0,0,0,0,0,0x1F2C,0x64,{6512,6513}},{0,0,0,0,0,0,0,0x1101,0x92,{6514,6515}},{0,0,0,0,0,0,0x6,0x11F1,0xE2,{6516,6517}},{0,0,0,0,0,0,0xA,0x21CD,0x175,{6518,6519}},{0,0,0,0,0,0,0x4,0xE45,0x89E,{6520,6521}},{0,0,0,0,0,0,0x2,0x73D,0x89E,{5434,6522}},{0,0,0,0,0,0,0x2,0x7FE,0xACE,{6523,6524}},{0,0,0,0,0,0,0,0x23CE,0x2DD,{6525,21}},{0,0,0,0,0,0,0,0x46D,0xE2,{6526,6527}},{0,0,0,0,0,0,0,0x265,0x2D0,{6528,25}},{0,0,0,0,0,0,0x8,0x229C,0x2968,{6529,6530}},{0,0,0,0,0,0,0,0x876,0x571,{6531,6532}},{0,0,0,0,0,0,0,0x9C40,0x5D,{6533,6534}},{0,0,0,0,0,0,0,0x9C40,0x5D,{6535,6536}},{0,0,0,0,0,0,0,0x9C40,0x5D,{6537,6538}},{0,0,0,0,0,0,0,0x9C40,0x5D,{6539,6540}},{0,0,0,0,0,0,0,0x9C40,0x49C,{6541,1058}},{0,0,0,0,0,0,0x6,0x9C40,0x481,{6542,6543}},{0,0,0,0,0,0,0xE,0x9C40,0x24A,{6544,6545}},{0,0,0,0,0,0,0,0x9C40,0x46D,{6546,6547}},{0,0,0,0,0,0,0xA,0x1220,0xE2,{6548,6549}},{0,0,0,0,0,0,0xE,0x1158,0x17C,{6550,6551}},{0,0,0,0,0,0,0x2,0x11F1,0xE2,{6552,6549}},{0,0,0,0,0,0,0x8,0x909,0x182,{6553,6554}},{0,0,0,0,0,0,0x6,0x91D,0x71,{6555,6556}},{0,0,50,0,0,0,0x8,0x9C40,0x5D,{6557,6558}},{0,0,20,0,0,0,0xC,0x25DD,0x236,{6559,6560}},{0,0,0,0,0,0,0,0x37DC,0x5D,{6561,6562}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{5923,6563}},{0,0,0,0,0,0,0xA,0x9C40,0x146,{6564,6565}},{0,0,0,0,0,0,0xA,0x9C40,0x14D,{6566,6565}},{0,0,0,0,0,0,0xA,0x2184,0x5D,{5933,6567}},{0,0,0,0,0,0,0xC,0x9C40,0x8C,{6568,6569}},{0,0,0,0,0,0,0xC,0x9C40,0x8C,{6570,6569}},{0,0,0,0,0,0,0xA,0x9C40,0x8C,{6571,6569}},{0,0,0,0,0,0,0x8,0x9C40,0x56,{6572,6573}},{0,0,0,0,0,0,0xE,0x9C40,0x585,{6574,6575}},{0,0,0,0,0,0,0x8,0x9C40,0x272,{6576,6577}},{0,0,0,0,0,0,0xE,0x9C40,0x154,{5495,536}},{0,0,0,0,0,0,0xE,0x9C40,0x132,{6578,6579}},{0,0,0,0,0,0,0x2,0x9C40,0x2C9,{6580,84}},{0,0,0,0,0,0,0,0x1CD4,0x10F4,{6581,6582}},{0,0,0,0,0,0,0,0xCD0,0x154D,{6583,6584}},{0,0,0,0,0,0,0,0xE81,0x1144,{6585,6586}},{0,0,0,0,0,0,0xE,0x9C40,0x5D5,{6100,6587}},{0,0,0,0,0,0,0xA,0x9C40,0x564,{6588,6589}},{0,0,0,0,0,0,0xC,0x9C40,0x58C,{6590,6591}},{0,0,0,0,0,0,0,0x9C40,0x146,{6592,6593}},{0,0,0,0,0,0,0,0x9C40,0x111,{6594,6595}},{0,0,0,0,0,0,0,0x9C40,0x111,{6596,6595}},{0,0,0,0,0,0,0x8,0x4BD,0x571,{6597,2417}},{0,0,0,0,0,0,0xE,0x9C40,0x189,{6598,6599}},{0,0,0,0,0,0,0xC,0x9C40,0x182,{108,6600}},{0,0,0,0,0,0,0xC,0x9C40,0xE2,{6601,6602}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{6603,6604}},{0,0,0,0,0,0,0xE,0x9C40,0x2DD,{5967,6605}},{0,0,0,0,0,0,0xE,0x9C40,0x189,{6606,6599}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6607,6608}},{0,0,0,0,0,0,0xE,0x9C40,0x2DD,{6609,6605}},{0,0,0,0,0,0,0x8,0x9C40,0xE2,{6610,6611}},{0,0,0,0,0,0,0x8,0x9C40,0x92,{6612,6613}},{0,0,0,0,0,0,0xA,0x9C40,0xDC,{6614,6611}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{6615,6616}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{5953,6617}},{0,0,0,0,0,0,0x8,0x9C40,0x6A,{6618,6619}},{0,0,0,0,0,0,0x4,0x9C40,0x146,{6620,6621}},{0,0,0,0,0,0,0,0x9C40,0x71,{6622,6623}},{0,0,0,0,0,0,0x6,0x9C40,0x71,{6624,6625}},{0,0,0,0,0,0,0xC,0x9C40,0x99,{6626,6627}},{0,0,0,0,0,0,0,0x9C40,0xDC,{6628,6629}},{0,0,0,0,0,0,0x8,0x9C40,0xE9,{6630,6631}},{0,0,0,0,0,0,0x4,0x9C40,0x16E,{6632,536}},{0,0,0,0,0,0,0x4,0x9C40,0x175,{6633,536}},{0,0,0,0,0,0,0,0x9C40,0x182,{6634,6635}},{0,0,0,0,0,0,0,0x9C40,0x5D,{6636,6637}},{0,0,0,0,0,0,0,0x9C40,0x56,{6638,6639}},{0,0,0,0,0,0,0,0x9C40,0xCE,{6640,158}},{0,0,0,0,0,0,0x6,0x9C40,0x92,{159,6641}},{0,0,0,0,0,0,0xA,0x9C40,0x6A,{6642,6643}},{0,0,0,0,0,0,0,0x9C40,0x10A,{6644,6595}},{0,0,0,0,0,0,0,0x9C40,0x2C9,{6645,6646}},{0,0,0,0,0,0,0xA,0x9C40,0x8C,{6647,6648}},{0,0,0,0,0,0,0xE,0x9C40,0x58C,{6649,170}},{0,0,0,0,0,0,0xA,0x9C40,0x1205,{6650,6651}},{0,0,0,0,0,0,0xC,0x9C40,0xDC,{6652,6653}},{0,0,0,0,0,0,0,0x9C40,0x1180,{6654,6655}},{0,0,-50,0,0,0,0x6,0x9C40,0x27EC,{6656,6657}},{0,0,0,0,0,0,0,0x2CC4,0x5F0,{6658,6659}},{0,0,0,0,0,0,0,0x9C40,0x10ED,{6660,6661}},{0,0,-20,0,0,0,0,0x9C40,0x189,{6662,6663}},{0,0,0,0,0,0,0xA,0x3756,0x23AD,{6664,6665}},{0,0,0,0,0,0,0,0x9C40,0xA42,{6666,6667}},{0,0,0,0,0,0,0x2,0x938,0xAD5,{6668,6669}},{0,0,0,0,0,0,0,0x9C40,0x4D8,{6670,6671}},{0,0,0,0,0,0,0,0x1B1C,0x564,{6672,6673}},{0,0,0,0,0,0,0xC,0x9C40,0x148C,{6674,6675}},{0,0,0,0,0,0,0xA,0x9C40,0x14F0,{6676,6677}},{0,0,0,0,0,0,0,0x9C40,0x71,{6678,6679}},{0,0,0,0,0,0,0,0x2B55,0x585,{6680,6681}},{0,0,0,0,0,0,0,0x1964,0x460,{6682,6683}},{0,0,0,0,0,0,0x4,0xDA5,0x140,{6684,6685}},{0,0,0,0,0,0,0x8,0x1A75,0x2155,{6686,6687}},{0,0,0,0,0,0,0xE,0x4A2,0x578,{6688,6689}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{6690,212}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{6691,212}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{6692,6693}},{0,0,0,0,0,0,0x6,0x11D0,0x578,{6694,6695}},{0,0,0,0,0,0,0xE,0x15A,0x190,{6696,6697}},{0,0,0,0,0,0,0xE,0x4BD,0x578,{6698,6699}},{0,0,0,0,0,0,0x8,0x53C,0x8B9,{6700,6701}},{0,0,0,0,0,0,0,0x4DE,0x571,{6702,584}},{0,0,0,0,0,0,0x4,0xE59,0x110E,{6703,6704}},{0,0,0,0,0,0,0x4,0xA92,0x564,{6705,6706}},{0,0,0,0,0,0,0xE,0x6F4,0x5D,{6707,6708}},{0,0,0,0,0,0,0xE,0x320,0x2A8,{233,6709}},{0,0,0,0,0,0,0xE,0x28D,0x229,{6710,6711}},{0,0,0,0,0,0,0xE,0x2F3D,0x5D,{6712,6713}},{0,0,0,0,0,0,0xE,0x111,0x49,{6714,6715}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6716,6717}},{0,0,0,0,0,0,0xE,0x17C,0x42,{6718,6719}},{0,0,0,0,0,0,0xE,0x9C40,0x2B5,{6720,6721}},{0,0,0,0,0,0,0xE,0x24A,0x5D,{6722,6723}},{0,0,40,0,0x4,0,0xD,0x6,0,{6724,1340}},{0,0,33,44,0,0,0,0x33A,0x46D,{693,6725}},{0,0,33,36,0,0,0,0x1CC6,0x189,{6726,6727}},{0,0,40,32,0,0,0xF,0x64,0x64,{6728,6729}},{0,0,50,60,0,0,0xE,0x118,0x146,{3633,3634}},{0,0,40,24,0,0,0xE,0x64,0,{4102,4103}},{0,0,50,60,0,0,0xE,0x7E,0x85,{6730,6731}},{0,0,50,44,0,0,0x5,0x189,0xC1,{5783,5783}},{0,0,40,44,0,0,0xA,0xAD,0xBA,{258,259}},{0,0,50,47,0,0,0x5,0x189,0xC1,{5786,5783}},{0,0,40,44,0,0,0xE,0xD5,0xAD,{296,297}},{0,0,50,50,0,0,0x5,0x189,0xC1,{5783,5783}},{0,0,40,44,0,0,0xE,0x125,0x146,{270,271}},{0,0,50,54,0,0,0x5,0x189,0xC1,{5783,5783}},{0,0,50,57,0,0,0x5,0x189,0xC1,{5783,5783}},{0,0,40,60,0,0,0xE,0x74A,0x884,{6732,6288}},{0,0,50,60,0,0,0x5,0x189,0xC1,{5783,5783}},{0,0,30,60,0,0,0xE,0x744,0x884,{6287,6288}},{0,0,45,60,0,0,0xE,0x74A,0x884,{6733,6288}},{0,0,30,60,0,0,0xE,0x722,0x855,{6734,6735}},{0,0,40,60,0,0,0xE,0xB4,0xC1,{6291,6292}},{0,0,40,60,0,0,0xE,0x73D,0x884,{6733,6736}},{0,0,40,58,0,0,0x4,0xC1,0x24A,{6293,6294}},{0,0,40,60,0,0,0xE,0x744,0x87D,{6733,6737}},{0,0,40,44,0,0,0xE,0x3C0,0x466,{6738,6739}},{0,0,20,60,0,0,0xE,0x73D,0x85C,{6287,6297}},{0,0,40,44,0,0,0x7,0xD5,0x146,{280,3645}},{0,0,40,44,0,0,0x7,0xD5,0x140,{280,281}},{0,0,40,44,0,0,0x7,0x64,0x6A,{326,3646}},{0,0,40,44,0,0,0x7,0x168,0x46D,{326,6740}},{0,0,40,44,0,0,0xF,0x208,0x258,{288,289}},{0,0,40,45,0,0,0xB,0x10A,0x11E,{6741,6742}},{0,0,40,33,0,0,0xB,0x140,0x154,{6741,6743}},{0,0,40,56,0,0,0x4,0x1FA,0x24A,{6293,6744}},{0,0,40,51,0,0,0x4,0x1FA,0x24A,{6293,6744}},{0,0,40,55,0,0,0xE,0xDC,0xAD,{296,6317}},{0,0,40,44,0,0,0xE,0x7E,0x56,{6745,6746}},{0,0,40,44,0,0,0xE,0x229,0x23D,{6747,6748}},{0,0,40,57,0,0,0xE,0xA6,0x92,{6321,6322}},{0,0,40,56,0,0,0xE,0xF0,0xE9,{6321,6323}},{0,0,40,68,0,0,0x1,0x78,0x140,{308,309}},{0,0,40,51,0,0,0x1,0x1FA,0x251,{308,6749}},{0,0,40,46,0,0,0x1,0x201,0x258,{308,6750}},{0,0,40,64,0,0,0,0x154,0x2C2,{6328,6329}},{0,0,40,44,0,0,0,0x438,0x46D,{6328,6330}},{0,0,40,44,0,0,0x8,0x1FA,0x251,{6751,6752}},{0,0,40,44,0,0,0x8,0xE7A,0x1129,{6753,6754}},{0,0,20,45,0,0,0xE,0xDC,0xAD,{296,297}},{0,0,50,0,0,0,0xC,0x25DD,0x236,{6559,6560}},{0,0,20,0,0,0,0xA,0x9C40,0x11E,{6755,6756}},{0,0,0,0,0,0,0x2,0x9C40,0x2C9,{6757,6758}},{0,0,50,0,0,0,0xE,0x9C40,0x2DD,{5967,6605}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{6759,6608}},{0,0,0,0,0,0,0,0x9C40,0x5D,{6760,6761}},{0,0,0,0,0,0,0,0x9C40,0xC1,{6762,6763}},{0,0,-18,0,0,0,0x6,0x122D,0x8C0,{2532,4107}},{0,0,10,0,0,0,0x8,0x1AAA,0x21C6,{6764,6687}},{0,0,0,0,0,0,0xE,0x4A2,0x578,{6765,6689}},{0,0,30,0,0,0,0,0x3EE,0x46D,{6766,6767}},{0,0,0,0,0,0,0xE,0x6FA,0x5D,{6768,6769}},{0,0,20,45,0,0,0,0x1712,0x146,{6726,6727}},{0,0,40,45,0,0,0xD,0x9C40,0x7A8,{1262,1263}},{0,0,60,60,0,0,0,0x78,0x229,{6770,6771}},{0,0,60,60,0,0,0xF,0x56,0x5D,{6728,6729}},{0,0,50,44,0,0,0x5,0x182,0xC1,{730,6772}},{0,0,127,57,0,0,0xE,0xA6,0x92,{6321,6773}},{0,0,127,56,0,0,0xE,0xF0,0xE9,{6321,6774}},{0,0,60,60,0,0,0x1,0x6A,0x5D,{6775,6776}},{0,0,60,60,0,0,0x1,0x6A,0x5D,{957,6777}},{0,0,20,45,0,0,0xD,0x9C40,0x7A8,{1262,1263}},{0,0,20,45,0,0,0x4,0xC1,0x24A,{6293,6294}},{12,12,0,0,0,0,0xC,0xFC1,0xE2,{6778,6779}},{12,12,0,0,0,0,0xA,0x1449,0xC44,{6780,2087}},{12,12,0,0,0,0,0xA,0x9C40,0x92,{6781,6782}},{12,12,0,0,0,0,0x8,0x2710,0x1B1,{6783,6784}},{12,12,0,0,0,10,0,0x9C40,0xAA0,{6785,6786}},{12,12,0,0,0x3,5,0x604,0x9C40,0x55D,{2139,6787,6788,6789} },{12,0,0,0,0,-5,0xA0A,0x9C40,0x30C0,{6790,6791}},{12,12,0,0,0,4,0x800,0x9C40,0xAFD,{6792,6793}},{12,12,0,0,0x3,10,0,0x9C40,0xAF6,{6794,6795,2151,2152} },{12,12,0,0,0,0,0xA,0x9C40,0x8C,{6796,6797}},{0,12,0,0,0,0,0,0x236,0x189,{6798,2306}},{12,12,0,32,0,0,0x50F,0x92,0xBA,{6799,6800}},{12,12,0,36,0,0,0xC01,0x111,0x154,{6801,2378}},{12,12,0,88,0,0,0x50F,0x15A,0x190,{6802,6803}},{0,0,0,0,0,0,0x8,0x2314,0x564,{6804,6805}},{0,0,0,0,0,0,0x8,0x23E2,0x585,{6806,6807}},{0,0,0,0,0,0,0x8,0x23F0,0x571,{6808,6807}},{0,0,0,0,0,0,0x6,0x245A,0x2E4,{6809,582}},{0,0,0,0,0,0,0,0x22A2,0x182,{6810,3}},{0,0,0,0,0,0,0,0x23F6,0x2E4,{6811,5}},{0,0,0,0,0,0,0x8,0x1C8A,0x459,{6812,11}},{0,0,0,0,0,0,0,0x11F1,0x57E,{6813,15}},{0,0,0,0,0,0,0,0x23F0,0x585,{6814,6815}},{0,0,0,0,0,0,0x2,0x9C40,0x24A,{6816,6817}},{0,0,0,0,0,0,0x2,0x9C40,0x251,{6818,6819}},{0,0,0,0,0,0,0x4,0x9C40,0x47A,{6820,6821}},{0,0,0,0,0,0,0x4,0x9C40,0x106E,{6822,6823}},{0,0,0,0,0,0,0xC,0x9C40,0x236,{6824,6825}},{0,0,0,0,0,0,0xA,0x9C40,0x305,{2402,6826}},{0,0,0,0,0,0,0x6,0x9C40,0x2E4,{6827,6828}},{0,0,0,0,0,0,0xC,0xFBA,0x92,{6829,6830}},{0,0,0,0,0,0,0xC,0x3929,0x251,{6831,6832}},{0,0,0,0,0,0,0,0x23DC,0xE2,{6833,51}},{0,0,0,0,0,0,0x6,0x11EA,0x189,{6834,6835}},{0,0,0,0,0,0,0,0x9C40,0x5D,{54,6836}},{0,0,0,0,0,0,0x8,0x9C40,0x6A,{6837,6838}},{0,0,0,0,0,0,0x8,0x9C40,0x175,{6839,6840}},{0,0,0,0,0,0,0x8,0x47CC,0x189,{6841,6842}},{0,0,0,0,0,0,0x2,0x9C40,0x140,{6843,63}},{0,0,0,0,0,0,0xA,0x9C40,0x24A,{6844,6845}},{0,0,0,0,0,0,0xA,0x9C40,0x161,{6846,6847}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{6848,6849}},{0,0,0,0,0,0,0x8,0x9C40,0x7E,{6850,6851}},{0,0,0,0,0,0,0x8,0x9C40,0x71,{6852,6853}},{0,0,0,0,0,0,0xA,0x9C40,0x29A,{6854,6855}},{0,0,0,0,0,0,0x6,0x9C40,0x182,{6856,6857}},{0,0,0,0,0,0,0x8,0x9C40,0x514,{6858,6859}},{0,0,0,0,0,0,0x8,0x9C40,0x2E4,{6860,6861}},{0,0,0,0,0,0,0x8,0x9C40,0x258,{6862,6863}},{0,0,0,0,0,0,0x2,0x9C40,0x58C,{6864,6865}},{0,0,0,0,0,0,0x2,0x9C40,0x564,{6866,6867}},{0,0,0,0,0,0,0,0x4DE,0x585,{6868,86}},{0,0,0,0,0,0,0,0x124E,0x157C,{6869,6870}},{0,0,0,0,0,0,0xE,0x9C40,0xE2,{6871,466}},{0,0,0,0,0,0,0,0x9C40,0xAD5,{6872,6873}},{0,0,0,0,0,0,0xA,0x9C40,0x1504,{6874,6875}},{0,0,0,0,0,0,0xC,0x9C40,0x5BA,{6876,96}},{0,0,0,0,0,0,0,0x9C40,0x118D,{6877,6878}},{0,0,0,0,0,0,0,0x9C40,0x1E0,{100,6879}},{0,0,0,0,0,0,0xC,0x9C40,0x47A,{6880,103}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{6881,6882}},{0,0,0,0,0,0,0xC,0x9C40,0x17C,{6883,6884}},{0,0,0,0,0,0,0xC,0x9C40,0x251,{110,6885}},{0,0,0,0,0,0,0x2,0x9C40,0x161,{112,6886}},{0,0,0,0,0,0,0x2,0x9C40,0x2DD,{6887,6888}},{0,0,0,0,0,0,0x8,0x9C40,0x182,{6889,6890}},{0,0,0,0,0,0,0,0x9C40,0x175,{6891,6892}},{0,0,0,0,0,0,0x8,0x9C40,0xDC,{6893,6894}},{0,0,0,0,0,0,0x8,0x9C40,0x189,{6895,6896}},{0,0,0,0,0,0,0x8,0x9C40,0x189,{6897,6896}},{0,0,0,0,0,0,0xA,0x9C40,0x196,{6898,6899}},{0,0,0,0,0,0,0x8,0x9C40,0x189,{6900,2487}},{0,0,0,0,0,0,0x6,0x9C40,0x92,{6901,6902}},{0,0,0,0,0,0,0x4,0x9C40,0xDC,{6903,6904}},{0,0,0,0,0,0,0,0x9C40,0x189,{6905,6906}},{0,0,0,0,0,0,0,0x9C40,0x175,{6907,6906}},{0,0,0,0,0,0,0x2,0x9C40,0x196,{141,6908}},{0,0,0,0,0,0,0,0x9C40,0x182,{6909,1171}},{0,0,0,0,0,0,0,0x9C40,0xE2,{6910,6911}},{0,0,0,0,0,0,0,0x9C40,0x8C,{6912,6913}},{0,0,0,0,0,0,0,0x9C40,0x6A,{6914,162}},{0,0,0,0,0,0,0x2,0x9C40,0x578,{163,6915}},{0,0,0,0,0,0,0xA,0x9C40,0x92,{6916,6648}},{0,0,0,0,0,0,0x2,0x9C40,0x585,{6917,6918}},{0,0,0,0,0,0,0xC,0x9C40,0x549,{6919,6920}},{0,0,0,0,0,0,0,0x9C40,0x1172,{6921,176}},{0,0,0,0,0,0,0x8,0x9C40,0x495,{6922,178}},{0,0,0,0,0,0,0,0x9C40,0x116C,{6923,6924}},{0,0,0,0,0,0,0,0x9C40,0x189,{183,6925}},{0,0,0,0,0,0,0xC,0x9C40,0x4C4,{6926,6927}},{0,0,0,0,0,0,0,0x2356,0x564,{6928,6929}},{0,0,0,0,0,0,0x4,0x9C40,0x1309,{6930,6931}},{0,0,0,0,0,0,0,0x1C5C,0x89E,{6932,6933}},{0,0,0,0,0,0,0x6,0x9C40,0x182,{6934,6935}},{0,0,0,0,0,0,0xA,0x9C40,0x2C9,{6936,6937}},{0,0,0,0,0,0,0x6,0x9C40,0x189,{6938,6939}},{0,0,0,0,0,0,0x6,0x11F8,0x154D,{217,6940}},{0,0,0,0,0,0,0,0x9C40,0x118,{239,6941}},{0,0,0,0,0,0,0xA,0x9C40,0x1C5,{6942,6943}},{0,0,0,0,0,0,0xE,0x9C40,0x1E0,{6944,6945}},{0,0,0,0,0,0,0xE,0x265,0x2C2,{246,6946}},{0,0,0,34,0x8,0,0x8,0x3E8,0xA,{6947,6948}},{0,0,0,64,0x8,0,0xE,0x3E8,0xA,{6266,6267}},{0,0,0,19,0x10,0,0xE,0x3E8,0xA,{6949,6950}},{0,0,0,74,0x10,0,0xA,0x3E8,0xA,{6951,6952}},{0,0,0,29,0x10,0,0xE,0x3E8,0xA,{6953,6954}},{0,0,0,46,0x18,0,0,0x3E8,0xA,{6273,6274}},{0,0,0,48,0x28,0,0x8,0x3E8,0xA,{6955,6956}},{0,0,0,49,0x18,0,0,0x3E8,0xA,{6273,6277}},{0,0,0,48,0x28,0,0x8,0x3E8,0xA,{6957,6956}},{0,0,0,52,0x18,0,0,0x3E8,0xA,{6273,6280}},{0,0,0,48,0x28,0,0x8,0x3E8,0xA,{6958,6956}},{0,0,0,54,0x18,0,0,0x3E8,0xA,{6273,6280}},{0,0,0,57,0x18,0,0,0x3E8,0xA,{6283,6284}},{0,0,0,13,0x28,0,0xE,0x3E8,0xA,{6959,6960}},{0,0,0,60,0x18,0,0,0x3E8,0xA,{6273,6280}},{0,0,0,56,0x28,0,0x8,0x3E8,0xA,{6961,6956}},{0,0,0,13,0x28,0,0xE,0x3E8,0xA,{6962,6960}},{0,0,0,113,0x8,0,0xE,0x3E8,0xA,{6963,6964}},{0,0,0,91,0x10,0,0xE,0x3E8,0xA,{6965,6966}},{0,0,0,13,0x28,0,0xE,0x3E8,0xA,{6967,6960}},{0,0,0,83,0x8,0,0xE,0x3E8,0xA,{6968,6969}},{0,0,0,19,0x8,0,0xE,0x3E8,0xA,{6970,6971}},{0,0,0,54,0x28,0,0x8,0x3E8,0xA,{6961,6956}},{0,0,0,72,0x8,0,0,0x3E8,0xA,{6298,6299}},{0,0,0,77,0x8,0,0,0x3E8,0xA,{6300,6301}},{0,0,0,70,0x8,0,0,0x3E8,0xA,{6302,6303}},{0,0,0,75,0x8,0,0,0x3E8,0xA,{6304,6305}},{0,0,0,69,0x8,0,0,0x3E8,0xA,{6306,6307}},{0,0,0,59,0x8,0,0xC,0x3E8,0xA,{6308,6309}},{0,0,0,48,0x8,0,0xC,0x3E8,0xA,{6310,6311}},{0,0,0,89,0x8,0,0,0x3E8,0xA,{6312,6313}},{0,0,0,84,0x8,0,0,0x3E8,0xA,{6312,6314}},{0,0,0,33,0x8,0,0xE,0x3E8,0xA,{6315,6316}},{0,0,0,78,0x10,0,0xD,0x3E8,0xA,{6972,6973}},{0,0,0,58,0x8,0,0xC,0x3E8,0xA,{6318,6319}},{0,0,0,52,0x8,0,0xC,0x3E8,0xA,{6320,6319}},{0,0,0,49,0x28,0,0xE,0x3E8,0xA,{6974,6975}},{0,0,0,49,0x28,0,0xE,0x3E8,0xA,{6976,6975}},{0,0,0,85,0x8,0,0x6,0x3E8,0xA,{6324,6325}},{0,0,0,68,0x8,0,0x6,0x3E8,0xA,{6326,6327}},{0,0,0,61,0x8,0,0x6,0x3E8,0xA,{6326,6327}},{0,0,0,100,0x8,0,0xA,0x3E8,0xA,{6331,6332}},{0,0,0,100,0x8,0,0xA,0x3E8,0xA,{6333,6977}},{0,0,0,0,0,0,0x8,0x2335,0x2961,{6978,6979}},{0,0,0,0,0,0,0,0x9C40,0x5D,{6980,6981}},{0,0,0,0,0,0,0x8,0x9C40,0x140,{6982,6983}},{0,0,0,0,0,0,0x8,0x9C40,0x6A,{6984,6985}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{6986,6987}},{0,0,0,0,0,0,0xA,0x9C40,0x92,{6988,6989}},{0,0,0,0,0,0,0xE,0x9C40,0x15A,{6990,5680}},{0,0,0,0,0,0,0,0x9C40,0x6A,{6991,6992}},{0,0,0,0,0,0,0x2,0x9C40,0x92,{6993,6994}},{0,0,0,0,0,0,0,0x9C40,0xBA,{6995,6996}},{0,0,0,0,0,0,0xA,0x231A,0xAA0,{6997,6998}},{-12,0,0,0,0,0,0xB,0x9C40,0x44C,{6999,7000}},{0,0,0,0,0,0,0x7,0x9C40,0x8A5,{7001,7002}},{0,0,0,0,0,0,0,0x9C40,0x431,{7003,7004}},{-12,0,0,0,0,0,0x2,0xCF8,0xABA,{7005,7006}},{0,0,0,0,0,0,0x8,0x9C40,0x6A,{7007,7008}},{0,0,0,0,0,0,0x8,0x9C40,0x140,{7009,7010}},{0,0,0,0,0,0,0x4,0x9C40,0x20D6,{7011,7012}},{0,0,0,0,0,0,0,0xC8,0x2D6,{959,959}},{0,0,0,0,0,0,0x7,0x161,0x19D,{1170,1170}},{0,0,0,0,0,0,0xA,0x161,0x2D6,{7013,7013}},{0,0,0,52,0,0,0xE,0x118,0x146,{252,253}},{0,0,0,48,0,0,0,0xAD,0xC1,{254,255}},{0,0,0,58,0,0,0xE,0x11E,0x146,{252,253}},{0,0,0,47,0,0,0xA,0xAD,0xBA,{258,259}},{0,0,0,43,0,0,0x4,0x334,0x445,{912,913}},{0,0,0,49,0,0,0xA,0xAD,0xBA,{258,259}},{0,0,0,51,0,0,0xA,0xAD,0xBA,{258,259}},{0,0,0,54,0,0,0xA,0xAD,0xC1,{258,259}},{0,0,0,57,0,0,0xA,0x92,0x99,{258,259}},{0,0,0,72,0,0,0x4,0x2AE,0x3A5,{912,913}},{0,0,0,60,0,0,0xA,0x92,0x99,{258,259}},{0,0,0,76,0,0,0xE,0x74A,0x8A5,{266,267}},{0,0,0,84,0,0,0x6,0x272,0x280,{268,269}},{0,0,0,36,0,0,0xE,0x161,0x182,{270,271}},{0,0,0,76,0,0,0xE,0x75E,0x8B2,{272,273}},{0,0,0,84,0,0,0x8,0xDC,0xF6,{274,275}},{0,0,0,83,0,0,0xE,0x549,0x640,{237,263}},{0,0,0,84,0,0,0xC,0x175,0x1B1,{276,277}},{0,0,0,24,0,0,0xE,0x8CD,0xA50,{264,7014}},{0,0,0,77,0,0,0xE,0x744,0x898,{266,267}},{0,0,0,60,0,0,0x7,0xC1,0x132,{280,281}},{0,0,0,65,0,0,0x6,0x99,0x140,{282,283}},{0,0,0,59,0,0,0x6,0xAD,0xC1,{284,285}},{0,0,0,51,0,0,0x6,0xFD,0x258,{286,287}},{0,0,0,45,0,0,0xE,0x201,0x24A,{288,289}},{0,0,0,71,0,0,0xE,0xF6,0x5D,{290,291}},{0,0,0,60,0,0,0xF,0x1F4,0x244,{292,293}},{0,0,0,58,0,0,0xF,0x1FA,0x251,{294,295}},{0,0,0,53,0,0,0xE,0xDC,0xAD,{296,297}},{0,0,0,64,0,0,0xE,0x71,0x7E,{298,299}},{0,0,0,71,0,0,0xE,0x341,0x3B9,{300,301}},{0,0,0,61,0,0,0xE,0xA6,0x92,{304,305}},{0,0,0,48,0,0,0x1,0xAD,0x24A,{308,309}},{0,0,0,69,0,0,0,0x78,0x140,{310,311}},{0,0,0,68,0,0,0x1,0x78,0x140,{308,311}},{0,0,0,63,0,0,0,0x146,0x132,{312,313}},{0,0,0,74,0,0,0,0x355,0x39E,{314,315}},{0,0,0,60,0,0,0x8,0x16E,0x1A4,{316,317}},{0,0,0,80,0,0,0x8,0x514,0x5BA,{318,319}},{0,0,0,64,0,0,0xE,0x85,0x56,{320,321}},{0,0,0,69,0,0,0x6,0x182,0x78,{268,322}},{0,0,0,73,0,0,0xE,0x215,0x3A5,{323,324}},{0,0,0,75,0,0,0x8,0x78,0x140,{325,309}},{0,0,0,68,0,0,0x7,0x132,0x3B2,{326,327}},{0,0,0,48,0,0,0x4,0x201,0x258,{256,257}},{0,0,0,53,0,0,0xD,0x9C40,0x7A1,{1262,1263}},{0,0,0,0,0x1,0,0x100,0x974,0x57E,{7015,7016,7017,7018} },{0,0,0,0,0x1,0,0x100,0x2126,0x5D5,{7019,7020,7021,7022} },{0,0,0,0,0x1,0,0xE0E,0x2E39,0x460,{2605,2606,2607,2608} },{12,0,0,0,0x1,0,0xC0D,0x26C0,0x51A,{2609,2610,2611,2612} },{0,0,0,0,0x1,0,0xD0C,0x1CA5,0xDC,{332,333,334,335} },{0,0,0,0,0x1,0,0x100,0x1B0E,0x452,{336,337,338,7023} },{12,0,0,0,0x1,0,0xE0E,0x2468,0x182,{7024,7025,7026,7027} },{12,0,0,0,0x1,0,0x404,0x2795,0x71,{2613,2614,2615,2616} },{0,0,0,0,0x1,0,0x100,0x100A,0x500,{346,347,348,349} },{0,0,0,0,0x1,0,0x302,0x1CE1,0x44C0,{16,17,7028,7029} },{12,0,0,0,0x1,0,0x302,0x91D,0xABA,{350,351,2621,2622} },{0,0,0,0,0x1,0,0x101,0x41F6,0x819,{7030,7031,7032,7033} },{0,0,0,0,0x1,0,0x706,0x9C40,0x64,{2627,2628,352,353} },{0,0,0,0,0x1,0,0xB0A,0x251,0x2DD,{354,355,356,357} },{-12,-12,0,0,0x3,4,0xA0A,0x1D02,0x12CD,{7034,7035,7036,7037} },{0,0,0,0,0x1,0,0x504,0x10B8,0x1AFA,{28,29,7038,7039} },{0,0,0,0,0x1,0,0x707,0x9C40,0x146,{7040,7041,7042,7043} },{0,0,0,0,0x1,0,0x707,0x9C40,0x104,{368,369,2636,2637} },{-12,0,0,0,0x1,0,0x908,0x9C40,0x71,{7044,7045,7046,7047} },{12,0,0,0,0x1,0,0x504,0x9C40,0x604,{36,37,7048,7049} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x488,{2642,2643,2644,2645} },{0,0,0,0,0x1,0,0x100,0x9C40,0x6A,{378,379,380,381} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x64,{7050,7051,7052,7053} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x6A,{386,387,388,389} },{12,0,0,0,0x1,0,0x606,0x470A,0x190,{7054,7055,7056,7057} },{12,0,0,0,0x1,0,0x606,0x4815,0x190,{7058,7059,7060,7057} },{0,0,0,0,0x1,0,0xA0A,0x2411,0x189,{397,398,399,400} },{0,0,0,0,0x1,0,0x808,0x3930,0x146,{401,402,403,404} },{0,0,0,0,0,0,0,0x9C40,0x6A,{54,55}},{0,0,0,0,0x1,0,0xA0A,0x9C40,0x6A,{2646,410,2647,162} },{0,0,0,0,0x1,0,0,0x9C40,0x64,{2648,2649,2650,2651} },{0,0,0,0,0x1,0,0x908,0x9C40,0x50,{60,61,7061,7062} },{12,0,0,0,0x1,0,0xE0E,0x45DE,0xE2,{7063,7064,7065,7066} },{12,0,0,0,0x1,0,0xE0E,0x9C40,0x5D,{7067,7068,7069,7070} },{12,0,0,0,0x1,0,0xE0E,0x9C40,0x5D,{7071,7072,7069,7070} },{12,0,0,0,0x1,0,0x100,0x9C40,0x8C,{7073,7074,7075,7076} },{12,0,0,0,0x1,0,0x808,0x4879,0xE2,{7077,7078,7079,7080} },{12,0,0,0,0x1,0,0x808,0x481C,0xE2,{7081,7082,7079,7080} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x7E,{7083,67,7084,7085} },{12,0,0,0,0x1,0,0xC0C,0x2371,0x2D6,{5029,7086,7087,7088} },{0,0,0,0,0x1,0,0x908,0x9C40,0x294,{75,76,2658,2659} },{0,0,0,0,0x1,0,0x908,0x9C40,0x2A1,{2660,76,2658,2659} },{0,0,0,0,0x1,0,0x908,0x9C40,0x2A1,{442,76,443,444} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x196,{445,446,447,448} },{0,0,0,0,0x1,0,0x706,0x9C40,0x599,{449,450,451,452} },{0,0,0,0,0x1,0,0xE0E,0x3E1,0x44C,{453,454,455,456} },{0,0,0,0,0x1,0,0xE0E,0xCBC,0x1090,{453,454,455,457} },{0,0,0,0,0x1,0,0x302,0x1ACC,0x1130,{89,90,458,459} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x190,{460,461,462,463} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x8F5,{460,7089,7090,7091} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xA1A,{7092,96,7093,7094} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x599,{97,96,2663,2664} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x1ED,{2665,2666,2667,2668} },{0,0,0,0,0,0,0,0x9C40,0x111,{6594,6595}},{0,0,0,0,0x1,0,0x808,0x9C40,0x2DD,{2671,2672,2673,2674} },{0,0,0,0,0x1,0,0x908,0x945,0x6C5,{104,105,7095,7096} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xBA,{106,107,2675,2676} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xC1,{2677,2678,2675,2679} },{12,0,0,0,0x1,0,0xD0C,0x9C40,0x230,{475,6885,476,7097} },{-12,12,0,0,0,0,0x4,0x9C40,0x175,{7098,7099}},{0,0,0,0,0x1,0,0xF0E,0x9C40,0x2A1,{7100,2685,2686,7101} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x2DD,{7102,7103,484,485} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0xC8,{7104,7105,7106,7105} },{0,0,0,0,0x1,0,0x70E,0x9C40,0x189,{7107,7108,7109,7110} },{0,0,0,0,0x1,0,0x908,0x9C40,0xF6,{494,495,496,497} },{0,0,0,0,0x1,0,0x908,0x9C40,0x104,{124,498,499,500} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x236,{501,502,503,504} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x21C,{505,506,507,508} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x71,{7111,7112,7113,7114} },{0,0,0,0,0x1,0,0x202,0x9C40,0x92,{513,514,515,516} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x7E,{517,518,519,520} },{0,0,0,0,0x1,0,0x504,0x9C40,0x15A,{6903,137,7115,7116} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x71,{2691,2692,138,139} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x6A,{2691,2692,140,139} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xE2,{522,523,524,525} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x71,{2691,2693,2694,144} },{0,0,0,0,0x3,3,0xE0E,0x9C40,0x14D,{7117,7118,7119,7120} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x272,{531,532,533,534} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x279,{535,536,537,538} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x17C,{2695,2696,2697,2698} },{0,0,0,0,0x1,0,0x404,0x9C40,0x5D,{541,542,543,544} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x8C,{7121,7122,7123,7124} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0xF0,{2699,2700,7125,7126} },{0,0,0,0,0x1,0,0x908,0x9C40,0x6A,{2701,2702,159,2703} },{0,0,0,0,0x1,0,0xA0A,0x9C40,0x6A,{550,551,552,162} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x196,{553,554,555,556} },{0,0,0,0,0x1,0,0x908,0x9C40,0x2A1,{557,558,559,560} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x92,{167,168,2704,2705} },{0,0,0,0,0x1,0,0x302,0x9C40,0x53C,{561,562,563,564} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x11EA,{171,172,7127,7128} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x495,{567,174,568,569} },{0,0,0,0,0,0,0,0x9C40,0x1180,{6654,6655}},{0,0,0,0,0x1,0,0x706,0x9C40,0x2619,{6656,6657,3892,7129} },{0,0,0,0,0x1,0,0x100,0x9C40,0x744,{179,180,7130,7131} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x9D8,{181,182,7132,7133} },{0,0,0,0,0x1,0,0x100,0x9C40,0x182,{183,184,3892,7129} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0xA71,{2717,2718,2719,2720} },{0,0,0,0,0x1,0,0x100,0x9C40,0x1C26,{187,188,7134,7135} },{0,0,0,0,0x1,0,0x908,0x1270,0x28C1,{2721,2722,189,190} },{0,0,0,0,0x1,0,0xD0C,0x9C40,0x58C,{2723,2724,2725,2726} },{0,0,0,0,0x1,0,0xD00,0x9C40,0x571,{7136,6673,7137,2726} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x14D5,{2731,2732,2733,198} },{0,0,0,0,0x1,0,0x504,0x9C40,0xA99,{2734,2735,2736,2737} },{0,0,0,0,0x1,0,0x606,0x9C40,0x1324,{2738,2739,2740,200} },{0,0,0,0,0x1,0,0x100,0x223E,0xF92,{201,202,4030,7138} },{0,0,0,0,0x3,0,0xA06,0x1719,0x779,{7139,7140,7141,7142} },{0,-12,0,0,0x3,2,0xA00,0x974,0x118D,{7143,7144,7145,7146} },{0,0,0,0,0x1,0,0x908,0x362A,0x40DE,{207,601,602,603} },{0,0,0,0,0x1,0,0xD0C,0x6AA,0x909,{209,210,7147,7148} },{0,0,0,0,0x3,4,0xA06,0x9C40,0x15A,{7149,7150,7151,7152} },{0,0,0,0,0x1,0,0xB0A,0x9C40,0x29A,{213,214,608,609} },{0,0,0,0,0x1,0,0x706,0x9C40,0xBA,{215,216,7153,7154} },{0,0,0,0,0x1,0,0x706,0x11F8,0x7A1,{217,218,7155,7156} },{0,0,0,0,0x1,0,0xB0A,0x341,0x2D0,{219,7157,7158,7159} },{0,0,0,0,0x3,2,0xA06,0x4B6,0x550,{7160,7161,7162,7163} },{0,0,0,0,0x1,0,0xB0A,0xA6,0x438,{2743,2744,2745,224} },{0,0,0,0,0x1,0,0x706,0x107C,0x13E5,{225,226,7164,7165} },{0,12,0,0,0x3,0,0xE00,0x229,0x17C,{7166,7167,7168,7169} },{0,0,0,0,0x1,0,0xF0E,0x78D,0x49C,{622,623,624,623} },{0,0,0,0,0x1,0,0xE0E,0x611,0x49,{231,232,731,7170} },{-14,12,0,0,0,0,0xE,0x265,0x132,{7171,7172}},{0,0,0,0,0x1,0,0xF0E,0x99C,0x8B9,{235,236,7173,7174} },{-23,-24,0,0,0x3,0,0xE0E,0x2E75,0x2A3D,{7175,7176,7177,7176} },{0,0,0,0,0x1,0,0x505,0x3C0,0xCE,{2761,638,2762,2763} },{0,0,0,0,0x1,0,0xB0A,0x1316,0x1852,{241,242,7178,7179} },{0,0,0,0,0x1,0,0xF0E,0x17C,0x1241,{237,243,898,7180} },{0,0,0,0,0x1,0,0xE0E,0x9C40,0x1546,{244,245,7181,7182} },{0,0,0,0,0,0,0xE,0x230,0x92,{246,247}},{0,0,0,48,0x1,0,0x706,0x190,0x495,{326,327,326,2812} },{0,0,0,53,0x1,0,0x706,0x244,0x8AC,{2861,2862,2861,2862} },{0,0,0,34,0,0,0xC,0x85,0x5D,{7183,7184}},{0,0,0,28,0,0,0xE,0xC1,0x71,{7185,7186}},{0,0,0,39,0x3,0,0x806,0x230,0x196,{7187,7188,7189,7190} },{0,0,0,33,0x3,0,0x806,0x230,0x19D,{7187,7188,7189,7190} },{-9,0,0,63,0x1,0,0xF0E,0x99,0xA6,{7191,7192,7193,7194} },{0,0,0,15,0,0,0xE,0x6A,0x71,{7195,7196}},{0,0,0,36,0,0,0x8,0x6A,0x71,{7197,7198}},{0,21,0,36,0x3,26,0x8,0x1CC,0x229,{7197,7198,7199,7200} },{0,0,0,35,0,0,0x8,0x2C2,0x341,{7201,2509}},{0,0,0,35,0,0,0x6,0x2C9,0x341,{2508,2509}},{0,0,0,44,0x1,0,0x606,0x78,0x5D,{686,687,688,251} },{0,12,0,38,0x3,0,0xC00,0xFD,0xE2,{7202,7203,7204,7205} },{0,0,0,58,0x1,0,0xF0E,0x146,0x182,{691,692,254,255} },{8,1,0,38,0x3,0,0xE00,0x15A,0xC1,{7206,7207,7208,7209} },{0,0,0,47,0,0,0x4,0x3D4,0x481,{2513,2514}},{-5,0,0,48,0x3,-122,0x806,0x118,0x140,{7210,7211,7210,7212} },{0,0,0,49,0,0,0x4,0x3C6,0x46D,{2513,2514}},{-5,12,0,48,0,0,0xA,0xB4,0xB4,{7213,7214}},{0,0,0,51,0,0,0x4,0x3C0,0x46D,{2513,2514}},{-5,0,0,48,0x3,-122,0x80A,0x3B9,0x452,{7215,7216,7217,7218} },{0,0,0,54,0,0,0x4,0x3C0,0x46D,{2513,2514}},{0,0,0,57,0,0,0x4,0x3B9,0x46D,{2513,2514}},{12,12,0,61,0x3,6,0x808,0xC72,0x708,{7219,7220,7219,7220} },{0,0,0,60,0,0,0x4,0x3C0,0x474,{2513,2514}},{0,0,0,70,0x1,0,0x706,0xB68,0xD76,{6025,6026,2780,2781} },{12,11,0,79,0x3,5,0x808,0x514,0x6CC,{7221,7222,7223,7224} },{0,12,0,62,0,0,0x8,0x1450,0x618,{7225,7226}},{0,0,0,65,0x1,0,0,0x125,0x146,{2785,2786,2787,2788} },{0,1,0,67,0x3,5,0xA0A,0x869,0xDFC,{7227,7228,7229,7230} },{0,0,0,83,0x1,0,0x908,0xE2,0x104,{274,2793,2794,275} },{12,12,0,62,0x3,6,0x808,0xC8D,0x73D,{7219,7231,7219,7231} },{0,0,0,35,0x1,0,0xD0C,0x556,0x62C,{2797,2528,2797,277} },{0,0,0,44,0x1,0,0x706,0x708,0x83A,{6025,6028,2780,2800} },{0,0,0,67,0x1,0,0x706,0xF0,0x111,{2801,2802,2803,2804} },{0,0,0,66,0x1,0,0x706,0x140,0x265,{2805,2806,2807,2808} },{0,0,0,59,0x1,0,0x706,0xA0,0x140,{282,283,2809,2810} },{0,0,0,51,0x1,0,0x706,0x17C,0x488,{2811,327,2811,2812} },{0,0,0,45,0x1,0,0x706,0x104,0x258,{2813,287,2813,2814} },{12,10,0,54,0x3,0,0xA0C,0x118,0x146,{7232,726,7233,7234} },{12,10,0,48,0x3,0,0xA0C,0x11E,0x146,{7232,726,7235,7234} },{0,0,0,58,0x1,0,0xF0E,0x1FA,0x251,{2819,295,2820,2821} },{0,0,0,53,0x1,0,0xF0E,0x1E6,0x23D,{2822,2823,2822,2824} },{12,12,0,88,0,0,0xF,0x8C,0x64,{697,698}},{0,0,0,71,0,0,0xF0E,0x6A,0x78,{2827,2828}},{0,0,0,42,0,0,0,0x10A,0x64,{733,734}},{0,0,0,36,0,0,0,0x258,0x64,{735,736}},{0,0,0,60,0x3,0,0xE0C,0x78,0x64,{1340,1340,7236,7237} },{0,-12,0,60,0x3,0,0x20E,0x175,0x64,{7236,7237,7238,7239} },{12,12,0,73,0,0,0x8,0xA6,0x64,{7240,2302}},{0,-2,0,68,0x3,0,0,0x99,0x6A,{7241,7242,7241,7243} },{12,12,0,61,0,0,0x6,0xC8,0x6A,{2362,2302}},{13,13,0,18,0x3,0,0x8,0xC8,0x92,{7244,7245,7246,7247} },{13,13,0,18,0x3,0,0xC,0xFD,0xAD,{7248,7249,7250,7251} },{0,0,0,80,0x1,0,0x908,0xD5,0xF0,{316,2848,316,2849} },{0,0,0,64,0x1,0,0x908,0x8EE,0x92,{318,7252,2851,7253} },{12,12,0,64,0x3,2,0xE0E,0x175,0x6A,{7254,7255,7256,7257} },{0,0,0,55,0x1,0,0xF0E,0x5DC,0x244,{323,748,749,750} },{0,0,0,75,0x1,0,0xA0B,0x618,0x3AC,{2856,2857,2858,2859} },{0,0,0,67,0,0,0xE,0x6A,0x5D,{7258,7259}},{0,0,0,0,0,0,0x8,0x4C4,0x64,{3781,3782}},{0,0,0,0,0,0,0xA,0x9C40,0xDC,{3783,3784}},{0,0,0,0,0,0,0xE,0x9C40,0x189,{1463,3785}},{0,0,0,0,0,0,0xE,0x9C40,0x8C,{3786,3787}},{0,0,0,0,0,0,0xE,0x9C40,0x92,{3788,3789}},{0,0,0,0,0,0,0x8,0x9C40,0x78,{1481,3790}},{0,0,0,0,0,0,0xE,0x9C40,0x17C,{3791,3792}},{0,0,0,0,0,0,0xE,0x9C40,0x2C2,{1487,3793}},{0,0,0,0,0,0,0xC,0x9C40,0x6A,{1493,3795}},{0,0,0,0,0,0,0xA,0x9C40,0x7E,{3797,3798}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{3803,3804}},{0,0,0,244,0x4,0,0x9,0x6,0,{1533,1534}},{0,0,0,0,0,0,0x4,0x265C,0x556,{1547,3807}},{0,0,0,0,0,0,0xA,0x9C40,0x549,{3808,3809}},{0,0,0,244,0x4,0,0x7,0x6,0,{1555,1556}},{0,0,0,232,0,0,0,0xF6,0x71,{1605,1606}},{0,0,0,220,0,0,0x2,0x9C40,0x33A,{1609,1610}},{0,0,0,35,0,0,0,0x85,0x71,{678,1618}},{0,0,0,35,0,0,0xE,0xE9,0x64,{1613,1614}},{0,0,0,35,0,0,0x8,0xE2,0xCE,{1615,1616}},{0,0,0,35,0,0,0,0x6A,0x71,{676,1617}},{0,0,0,35,0,0,0,0x11E,0x64,{680,681}},{0,0,0,35,0,0,0x1,0xBB8,0xA8C,{1380,1381}},{0,0,0,35,0,0,0x4,0xF6,0x71,{1619,1620}},{0,0,0,35,0,0,0xE,0xBA,0x92,{1623,3812}},{0,0,0,35,0,0,0xD,0x2C9,0x33A,{804,805}},{0,0,0,35,0,0,0x6,0xC8,0xE2,{1625,3813}},{0,0,0,35,0,0,0x6,0xDC,0x8C,{3814,3815}},{0,0,0,35,0,0,0x4,0x938,0xDC,{1627,3816}},{0,0,0,35,0,0,0xC,0x786,0xAA6,{3817,3818}},{0,0,0,35,0,0,0xE,0x6B1,0xA35,{707,708}},{0,0,0,35,0,0,0,0x16E,0x17C,{813,1630}},{0,0,0,35,0,0,0,0x154,0xE2,{3819,3820}},{0,0,0,35,0,0,0xC,0x459,0x175,{719,720}},{0,0,0,35,0,0,0,0x71,0x92,{3821,3822}},{0,0,0,35,0,0,0,0xCE,0x189,{1635,1636}},{0,0,0,35,0,0,0xE,0x279,0x2DD,{1637,1638}},{0,0,0,35,0,0,0x6,0x272,0x2DD,{1639,1640}},{0,0,0,35,0,0,0xE,0x17C,0x99,{1587,1588}},{0,0,0,35,0,0,0xE,0x11E,0x12C,{731,732}},{0,0,0,35,0,0,0x4,0x104,0x64,{1641,1642}},{0,0,0,35,0,0,0,0x445,0x64,{1643,1644}},{0,0,0,35,0,0,0xA,0x7E,0x64,{829,1645}},{0,0,0,60,0,0,0xE,0xA0,0x85,{1623,3812}},{0,0,0,44,0,0,0xE,0xA0,0x85,{1623,3812}},{0,0,0,47,0,0,0x6,0xAD,0xC1,{1625,3813}},{0,0,0,47,0,0,0x6,0xBA,0x85,{3814,3815}},{0,0,0,62,0,0,0x4,0x76C,0xC1,{1627,3816}},{0,0,0,93,0,0,0xC,0x474,0x632,{3817,3818}},{0,0,0,40,0,0,0xC,0x78D,0xAA6,{3817,3818}},{0,0,0,42,0,0,0x6,0x26C,0x2D6,{1639,1640}},{0,0,0,38,0,0,0x6,0x272,0x2DD,{1639,1640}},{0,0,0,88,0,0,0x6,0x99,0x5D,{1654,1655}},{0,0,0,35,0x4,0,0,0x6,0,{6263,6263}},{0,0,0,0,0,0,0,0x37C8,0x452,{902,7260}},{0,0,0,0,0,0,0xF,0x1C55,0x876,{7261,7262}},{0,0,0,0,0,0,0x9,0x9C40,0x222,{6348,7263}},{0,0,0,0,0,0,0x9,0x9C40,0x208,{6348,7264}},{0,0,0,0,0,0,0x9,0x4185,0x2BC,{7265,7266}},{0,0,0,0,0,0,0xE,0x9C40,0x64,{1268,7267}},{0,0,0,0,0,0,0xA,0x9C40,0x416,{7268,7269}},{0,0,0,0,0,0,0x8,0x3E1,0x812,{7270,7271}},{0,0,0,0,0,0,0x6,0x9C40,0x92,{7272,7273}},{0,0,0,0,0,0,0x6,0x9C40,0x8C,{7272,7274}},{0,0,0,0,0,0,0x8,0x47C5,0xDC,{7275,7276}},{0,0,0,0,0,0,0xA,0x9C40,0xC1,{7277,7278}},{0,0,0,0,0,0,0xE,0x9C40,0x14D,{7279,7280}},{0,0,0,0,0,0,0xE,0x9C40,0xA6,{5668,7281}},{0,0,0,0,0,0,0xA,0x9C40,0x6E0,{858,7282}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{7283,7284}},{0,0,0,0,0,0,0xC,0x57E,0x1040,{7285,7286}},{0,0,0,0,0,0,0,0x20E,0x265,{7287,7288}},{0,0,0,0,0,0,0,0xE60,0x131D,{7289,7290}},{0,0,0,0,0,0,0,0x9C40,0x2C2,{7291,7292}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{6384,7293}},{0,0,0,0,0,0,0xE,0x9C40,0x56,{7294,2578}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{1123,7295}},{0,0,0,0,0,0,0xE,0x9C40,0x50,{5696,7296}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{7297,7298}},{0,0,0,0,0,0,0xA,0x9C40,0x56,{6405,7299}},{0,0,0,0,0,0,0x4,0x9C40,0x190,{7300,7301}},{0,0,0,0,0,0,0xE,0x9C40,0x6A,{7302,7303}},{0,0,0,0,0,0,0xD,0x9C40,0x47A,{7304,7305}},{0,0,0,0,0,0,0,0x9C40,0x5D,{153,7306}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{7307,7308}},{0,0,0,0,0,0,0x6,0x9C40,0x5D,{7309,5742}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{7310,7311}},{0,0,0,0,0,0,0x4,0x9C40,0x1269,{199,7312}},{0,0,0,0,0,0,0xC,0xB11,0x251,{7313,7314}},{0,0,0,36,0,0,0x8,0x17C,0x1B8,{248,249}},{0,0,0,36,0,0,0xF,0x64,0x64,{6728,7315}},{0,0,0,36,0,0,0xE,0x15A,0x189,{3633,3634}},{0,0,0,48,0,0,0xE,0x56,0,{4102,4103}},{0,0,0,36,0,0,0xE,0x8C,0x92,{6730,6731}},{0,0,0,48,0,0,0,0x751,0x8B2,{7316,7317}},{0,0,0,69,0,0,0xA,0x7E,0x85,{258,259}},{0,0,0,52,0,0,0,0x74A,0x8AC,{7316,7317}},{0,0,0,48,0,0,0xE,0xDC,0xAD,{296,297}},{0,0,0,55,0,0,0,0x74A,0x8AC,{7316,7317}},{0,0,0,57,0,0,0xE,0x125,0x146,{270,271}},{0,0,0,58,0,0,0,0x744,0x8A5,{7316,7317}},{0,0,0,60,0,0,0,0x758,0x8A5,{7316,7317}},{0,0,0,62,0,0,0xC,0xA64,0xC51,{7318,7319}},{0,0,0,63,0,0,0,0x74A,0x8B2,{7316,7317}},{0,0,0,70,0,0,0xA,0x3B2,0x466,{261,262}},{0,0,0,70,0,0,0xA,0x3C0,0x46D,{261,7320}},{0,0,0,53,0,0,0xE,0x73D,0x870,{7321,6735}},{0,0,0,48,0,0,0xE,0xB4,0xC1,{6291,6292}},{0,0,0,84,0,0,0xC,0x528,0x625,{7313,7319}},{0,0,0,43,0,0,0x4,0x1FA,0x251,{6293,7322}},{0,0,0,56,0,0,0xE,0x549,0x646,{7323,7324}},{0,0,0,24,0,0,0xE,0x744,0x876,{6738,6739}},{0,0,0,65,0,0,0xA,0x535,0x632,{261,262}},{0,0,0,48,0,0,0x6,0xB4,0xC1,{284,285}},{0,0,0,54,0,0,0x6,0xAD,0xC1,{284,285}},{0,0,0,42,0,0,0xB,0xF0,0x17C,{7325,7326}},{0,0,0,39,0,0,0xB,0xF0,0x17C,{7325,7326}},{0,0,0,52,0,0,0xE,0x132,0x132,{6321,6774}},{0,0,0,52,0,0,0xE,0x19D,0x11E,{6321,7327}},{0,0,0,60,0,0,0x1,0x8C,0x1B1,{308,309}},{0,0,0,66,0,0,0x1,0x8C,0x1B1,{308,309}},{0,0,0,59,0,0,0x1,0x8C,0x1B1,{308,309}},{0,0,0,91,0,0,0,0x305,0x319,{6328,6330}},{0,0,0,109,0,0,0x8,0x1449,0x17E1,{6343,6344}},{0,0,0,79,0,0,0xE,0x20E,0x3A5,{7328,7329}},{0,0,0,0,0,0,0xE,0x2990,0x5D,{6712,7330}},{0,0,0,0,0x3,2,0x606,0x3332,0x819,{7331,7332,7333,7334} },{0,0,0,0,0x3,2,0x608,0x377E,0x82D,{7335,7336,7337,7338} },{0,0,0,0,0x3,3,0x404,0x2DD5,0x416,{7339,7340,7341,7342} },{0,0,0,0,0x3,10,0xA0A,0x38CC,0x8B9,{7343,7344,7345,7346} },{0,0,0,0,0x3,3,0xC,0x1C91,0xD5,{7347,7348,334,335} },{0,0,0,0,0x3,4,0xE,0x9C40,0x64,{7349,7350,7351,7352} },{0,0,0,0,0x3,0,0x606,0x3721,0x409,{7353,7354,7355,7356} },{0,0,0,0,0x3,0,0x906,0x37A0,0x140,{7357,7358,1340,7359} },{0,0,0,0,0x3,1,0x909,0x1CA5,0x891,{7360,7361,7362,7361} },{-12,-12,0,0,0x3,0,0xC,0x1331,0x2B62,{7363,7364,7365,7366} },{0,0,0,0,0x3,3,0x909,0x1CDA,0x21CD,{7367,7368,7369,7370} },{0,0,0,0,0x3,0,0x10F,0x22CA,0x2D0,{7371,7372,7373,7374} },{12,0,0,0,0x3,0,0x6,0x466,0x189,{7375,7376,7377,7378} },{-19,2,0,0,0x3,0,0x104,0x909,0x564,{7379,7380,1340,7381} },{-12,-12,0,0,0x3,2,0x606,0x11C9,0x151E,{7382,7383,7384,7383} },{0,0,0,0,0x3,2,0x808,0x9C40,0x1ED,{7385,7386,7385,7386} },{0,12,0,0,0x3,10,0x507,0x9C40,0xC1,{7387,7388,7389,7390} },{-12,-12,0,0,0x3,5,0x10E,0x9C40,0xDC,{7391,7392,7393,7394} },{0,0,0,0,0x3,10,0x404,0x9C40,0xBA,{34,7395,7396,7397} },{0,0,0,0,0x3,2,0xB0D,0x9C40,0x7F1,{7398,7399,7400,7401} },{0,0,0,0,0x3,4,0x604,0x9C40,0x182,{7402,7403,7404,7405} },{0,0,0,0,0x3,9,0x606,0x9C40,0x208,{7406,7407,7408,7409} },{0,0,0,0,0x3,0,0x80C,0x9C40,0x6A,{7410,7411,7412,7413} },{0,0,0,0,0x3,7,0x8,0x9C40,0x460,{7414,7415,7414,7415} },{0,-12,0,0,0x3,0,0xA06,0xE7A,0xDC,{7416,7417,7418,7419} },{0,0,0,0,0x3,1,0x6,0x1B8D,0x84E,{7420,7421,7422,7423} },{12,0,0,0,0x3,0,0xA0A,0x10E0,0xBA,{7424,7425,7426,7425} },{0,0,0,0,0x3,3,0x206,0x1C76,0x140,{7427,7428,7429,7430} },{0,0,0,0,0x3,0,0xE08,0xE16,0x452,{7431,7432,7433,7434} },{0,14,0,0,0x3,-125,0xC0A,0x9C40,0x16E,{7435,7436,7437,7438} },{12,14,0,0,0x3,-124,0xC0C,0x9C40,0x17C,{7439,7440,7441,7442} },{0,0,0,0,0x3,15,0xA0A,0x9C40,0x71,{7443,7444,7445,7446} },{0,0,0,0,0x3,2,0x400,0x3591,0x772,{7447,7448,7449,7450} },{12,12,0,0,0x3,0,0xA0A,0x2885,0xA0,{7451,7452,7453,7454} },{0,0,0,0,0x3,0,0x20A,0x32BA,0x355,{7455,7456,7457,7458} },{0,0,0,0,0x3,3,0x806,0x2E2C,0x39E,{7459,7460,7461,7462} },{0,0,0,0,0x3,0,0,0xFAD,0x222,{7463,7464,1430,7465} },{0,0,0,0,0x3,0,0,0x9C40,0x56,{7466,7467,1430,7468} },{12,12,0,0,0,0,0xA,0xA64,0x1B8,{7469,2114}},{0,0,0,0,0x3,5,0xA0C,0x39E4,0xC1,{7470,7471,7470,7471} },{0,0,0,0,0x3,0,0x80A,0x9C40,0x279,{7472,7473,7474,7475} },{0,12,0,0,0,0,0xA,0x9C40,0x43E,{7476,7477}},{0,0,0,0,0x3,1,0x2,0x9C40,0x175,{7478,7479,7480,7481} },{0,12,0,0,0,0,0x6,0x9C40,0x2D6,{7482,7483}},{12,0,0,0,0x3,5,0xC04,0x9C40,0x53C,{7484,7485,7486,7487} },{0,0,0,0,0x3,7,0xA06,0x7F8,0x88A,{7488,7489,7490,7491} },{0,0,0,0,0x3,2,0xE0E,0x38AA,0x213A,{7492,7493,7494,7495} },{-31,-19,0,0,0x3,2,0,0x1E3C,0xAA0,{7496,7497,7498,7499} },{0,0,0,0,0x3,4,0xA0C,0x9C40,0x592,{7500,7501,7502,7503} },{0,0,0,0,0x3,-4,0xA04,0x9C40,0xA8C,{7504,7505,7506,7507} },{0,0,0,0,0x3,5,0xB0B,0x9C40,0x10A4,{7508,7509,7508,7509} },{0,0,0,0,0x3,4,0xB0B,0x9C40,0x1EFD,{7510,7511,7510,7511} },{0,12,0,0,0x3,8,0,0x9C40,0x578,{7512,7513,7514,7515} },{0,0,0,0,0x3,9,0x6,0x9C40,0x189,{7516,7517,7518,7519} },{0,0,0,0,0x3,7,0,0x9C40,0x945,{7520,7521,7522,7523} },{0,0,0,0,0x3,8,0xA0A,0x6C5,0x765,{7524,7525,7526,7527} },{0,0,0,0,0x3,0,0xC0E,0x9C40,0x17C,{7528,7529,7530,7531} },{0,0,0,0,0x3,2,0xE0E,0x9C40,0x8C,{1475,7532,7533,7534} },{0,0,0,0,0,0,0xA,0x9C40,0x140,{7535,111}},{-12,12,0,0,0,0,0x4,0x9C40,0x175,{7098,7099}},{0,0,0,0,0x3,4,0,0x9C40,0x550,{7536,7537,7538,7539} },{0,0,0,0,0x3,4,0x80C,0x9C40,0x175,{7540,7541,7542,7543} },{0,0,0,0,0x3,6,0xE0E,0x9C40,0x140,{7544,7545,7546,7547} },{0,0,0,0,0x3,8,0xE0E,0x9C40,0xF6,{7548,7549,7548,7549} },{0,0,0,0,0x3,0,0xA0A,0x9C40,0x251,{7550,7551,7552,7553} },{0,12,0,0,0,0,0x6,0x9C40,0x24A,{7554,7555}},{12,12,0,0,0,0,0xA,0x9C40,0x5D,{7556,7557}},{12,12,0,0,0,0,0x8,0x9C40,0x56,{7558,7559}},{0,0,0,0,0x3,0,0x402,0x9C40,0x182,{7560,7561,7562,7563} },{0,12,0,0,0,0,0x6,0x9C40,0x146,{7564,7565}},{0,12,0,0,0,0,0xA,0x9C40,0x5D,{7566,7567}},{0,12,0,0,0x3,0,0xE0C,0x9C40,0x140,{7568,7569,7570,7571} },{0,12,0,0,0,0,0xE,0x9C40,0xBA,{7572,7573}},{0,12,0,0,0,0,0x8,0x9C40,0x6A,{7574,4671}},{0,12,0,0,0,0,0x6,0x9C40,0x161,{7575,7576}},{0,0,0,0,0x3,2,0xA04,0x9C40,0x236,{7577,7578,7579,7580} },{0,0,0,0,0x3,3,0xE0E,0x9C40,0x14D,{7117,7118,7119,7120} },{0,0,0,0,0x3,2,0x608,0x9C40,0x5D,{7581,7582,7583,7584} },{0,12,0,0,0,0,0xB,0x9C40,0x445,{7585,7586}},{0,12,0,0,0,0,0x6,0x9C40,0x78,{7587,7588}},{0,0,0,0,0x3,8,0x505,0x9C40,0x5D,{7589,7590,7591,7590} },{0,0,0,0,0x3,7,0,0x9C40,0xBA,{7592,7593,7594,7595} },{0,0,0,0,0x3,7,0xA,0x9C40,0x189,{7596,7597,7598,7597} },{0,0,0,0,0x3,-2,0xA08,0x9C40,0x5D,{7599,7600,7601,7602} },{0,12,0,0,0,0,0x8,0x9C40,0x5D,{7603,7604}},{0,0,0,0,0x3,10,0x608,0x9C40,0x2D6,{7605,7606,7607,7606} },{-36,-36,0,0,0x3,8,0x909,0x9C40,0x416,{7608,7609,7610,7611} },{12,12,0,0,0x3,2,0x606,0x9C40,0xE9,{7612,7613,7614,7615} },{0,0,0,0,0x3,5,0x402,0x9C40,0x1AF4,{7616,7617,7618,7619} },{0,0,0,0,0x3,7,0x404,0x9C40,0x55D,{7620,7621,7622,7623} },{0,0,0,0,0x3,5,0xC0C,0x9C40,0x279,{7624,7625,7626,7625} },{0,0,0,0,0x3,9,0x602,0x9C40,0x9F2,{7627,7628,7629,7630} },{0,0,0,0,0x3,7,0x6,0x9C40,0x7B5,{7631,7632,7633,7632} },{0,0,0,0,0x3,4,0xC0C,0x1381,0x1590,{7634,7635,7636,7637} },{0,0,0,0,0x3,6,0xA06,0x9C40,0x1359,{7638,7639,7640,7641} },{0,0,0,0,0x3,7,0x808,0x9C40,0x1561,{7642,7643,7644,7643} },{0,0,0,0,0x3,6,0x707,0x9C40,0x104,{7645,7646,7645,7647} },{0,7,0,0,0x3,8,0x406,0x9C40,0x1485,{7648,7649,7650,7651} },{0,0,0,0,0x3,7,0,0xA56,0xA8C,{7652,7653,7652,7653} },{0,0,0,0,0x3,-3,0,0x9C40,0x736,{7654,7655,7656,7657} },{0,0,0,0,0x3,8,0x60E,0x231A,0x2BC0,{7658,7659,7660,7659} },{0,0,0,0,0x3,-5,0x707,0x9C40,0x12B9,{7661,7662,7663,7664} },{0,0,0,0,0x3,9,0xA0A,0x9C40,0x1E3C,{7665,7666,7667,7668} },{0,0,0,0,0x3,5,0xA0E,0x9C40,0x779,{7669,7670,7671,7672} },{0,0,0,0,0x3,2,0x602,0x244D,0xAE2,{7673,7674,7675,7674} },{0,0,0,0,0x3,0,0xA06,0x1719,0x779,{7139,7140,7141,7142} },{0,-12,0,0,0x3,2,0xA00,0x974,0x118D,{7143,7144,7145,7146} },{0,12,0,0,0,0,0x8,0x4CA,0xAE9,{7676,7677}},{0,0,0,0,0x3,0,0x6,0x49C,0x2D0,{7678,7679,352,7680} },{0,0,0,0,0x3,4,0xA06,0x9C40,0x15A,{7149,7150,7151,7152} },{0,12,0,0,0,0,0xE,0x9C40,0x28D,{7681,7682}},{0,12,0,0,0,0,0x8,0x9C40,0x21C,{7683,7684}},{5,12,0,0,0,0,0x1,0x21DA,0x2202,{7685,2296}},{0,12,0,0,0,0,0x4,0x189,0x146,{7686,7687}},{0,0,0,0,0x3,2,0xA06,0x4B6,0x550,{7160,7161,7162,7163} },{0,12,0,0,0,0,0xC,0xF0,0xBA,{7688,7689}},{-12,0,0,0,0x3,0,0x4,0x79A,0x938,{7690,7691,7692,7693} },{0,12,0,0,0x3,0,0xE00,0x229,0x17C,{7166,7167,7168,7169} },{-12,0,0,0,0x3,0,0x608,0x10E6,0x175,{7694,7695,7696,7697} },{0,12,0,0,0x3,7,0xA0A,0xB39,0x5D,{7698,7699,7700,7701} },{-14,12,0,0,0,0,0xE,0x265,0x132,{7171,7172}},{0,0,0,0,0x3,0,0xE0C,0x9C40,0x6D2,{7702,7703,6707,7704} },{-23,-24,0,0,0x3,0,0xE0E,0x2E75,0x2A3D,{7175,7176,7177,7176} },{12,12,0,0,0,0,0x7,0x611,0x722,{7705,7706}},{-24,-20,0,0,0x3,0,0x404,0x9C40,0xA5D,{7707,7708,7709,7708} },{-36,-14,0,0,0x3,0,0xC0C,0x9C40,0x876,{7710,7711,7712,7713} },{-36,-35,0,0,0x3,0,0xE0E,0x9C40,0xAD5,{7714,7715,7714,7715} },{0,12,0,0,0,0,0xE,0x280,0x182,{7716,7717}},{0,0,0,0,0x1,2,0xF,0x9C40,0x9A2,{7718,7719,7720,7721} },{-12,-12,0,0,0x3,7,0xC,0x902,0xAA0,{7722,7723,7724,7725} },{0,0,0,0,0x3,0,0xA06,0xE66,0xD5,{7416,7726,7727,7728} },{0,-12,0,0,0x3,0,0xA06,0xE66,0xDC,{7729,7730,7731,7419} },{0,14,0,0,0x1,-122,0xE,0x9C40,0x17C,{7732,7733,7734,7735} },{0,0,0,0,0x3,4,0x606,0x9C40,0x25E,{7736,7555,7737,7555} },{0,0,0,0,0,0,0,0x9C40,0x64,{7738,7739}},{0,0,0,0,0x3,2,0x406,0x2DF0,0x424,{7740,7340,7741,7342} },{0,12,0,0,0x3,3,0x507,0x9C40,0xC8,{7742,7743,7744,7745} },{0,-12,0,0,0x3,0,0xA06,0x3DA,0xDC,{7416,7746,7747,7748} },{0,14,-32,0,0x1,-122,0x60E,0x9C40,0x189,{7732,7749,7750,7751} },{0,0,0,0,0x3,7,0x404,0x9C40,0x55D,{7620,7752,7622,7753} },{0,0,0,0,0x3,7,0x808,0x9C40,0x1582,{7754,7643,7755,7643} },{12,12,0,0,0x3,2,0x408,0x9C40,0x78,{7756,7757,7449,7758} },{12,0,0,0,0x3,5,0xC04,0x9C40,0x53C,{7759,7760,7761,7762} },{0,0,0,0,0x3,4,0xC0C,0x9C40,0x535,{7763,7764,7765,7766} },{0,0,0,0,0x3,0,0xE08,0x9C40,0x6A,{7767,7768,7769,7770} },{0,0,0,0,0x3,5,0x2,0x9C40,0x175,{7771,7772,7773,7774} },{0,0,0,0,0x3,4,0xA0C,0x9C40,0x564,{7500,7775,7776,7777} },{0,0,0,0,0x3,4,0xE0E,0x9C40,0x57E,{7778,7487,7779,7503} },{0,0,0,0,0x3,4,0x808,0x9C40,0x71,{7574,144,7574,7780} },{-12,0,0,0,0x3,7,0,0x9C40,0xC1,{7592,7593,7594,7595} },{0,0,0,0,0x3,7,0x404,0x9C40,0xC1,{7781,7395,7782,7397} },{0,0,0,34,0,0,0xC,0x85,0x5D,{7183,7184}},{0,0,0,28,0,0,0xE,0xC1,0x71,{7185,7186}},{0,0,0,39,0x3,0,0x806,0x230,0x196,{7187,7188,7189,7190} },{0,0,0,33,0x3,0,0x806,0x230,0x19D,{7187,7188,7189,7190} },{-9,0,0,63,0x3,0,0xE0E,0xA0,0xB4,{7191,7192,7783,7784} },{0,0,0,15,0,0,0xE,0x6A,0x71,{7195,7196}},{0,0,0,36,0,0,0x8,0x6A,0x71,{7197,7198}},{0,21,0,36,0x3,26,0x8,0x1CC,0x229,{7197,7198,7199,7200} },{0,0,0,25,0x3,0,0xC04,0x139,0x189,{7785,7786,7787,7788} },{12,0,0,25,0x3,0,0xC06,0xCE,0xE2,{7789,7790,7787,7788} },{12,12,0,61,0x3,0,0xE0E,0x99,0xA6,{7791,7792,7793,7794} },{-24,-12,0,38,0x3,0,0xC0D,0x154,0x17C,{7795,7796,7797,7798} },{0,-2,0,37,0x3,0,0xA00,0xD5,0xDC,{7799,7800,7801,7800} },{8,1,0,38,0x3,0,0xE00,0x15A,0xC1,{7206,7207,7208,7209} },{0,12,10,15,0x3,0,0xC01,0x154,0x190,{7802,7802,7803,7804} },{0,4,0,100,0x3,9,0x806,0x8C,0x99,{7805,7806,7807,7808} },{0,12,10,19,0x3,0,0xC01,0x21C,0x286,{7809,7802,7810,7804} },{-5,12,0,48,0,0,0xA,0xB4,0xB4,{7213,7214}},{0,12,20,15,0x3,0,0xC00,0x154,0x189,{7811,7812,7813,7804} },{-5,0,0,48,0x3,-122,0x80A,0x3B9,0x452,{7215,7216,7217,7218} },{0,12,20,12,0x3,0,0xC00,0x154,0x189,{7811,7814,7815,7816} },{0,12,20,11,0x3,0,0xC00,0x154,0x189,{7811,7817,7810,7818} },{12,12,0,61,0x3,6,0x808,0xA50,0xE1D,{7819,7820,7819,7821} },{0,12,20,8,0x3,0,0xC00,0x146,0x182,{7811,7822,7810,7818} },{12,11,0,91,0x3,-3,0,0x4CA,0x5C1,{7823,7824,7825,7826} },{12,11,0,79,0x3,5,0x808,0x514,0x6CC,{7221,7222,7223,7224} },{0,12,0,70,0,0,0x8,0x3C6,0x474,{7827,7828}},{12,12,0,80,0x3,8,0xE06,0x12C,0xFD,{7829,7830,7831,7830} },{0,1,0,67,0x3,5,0xA0A,0x869,0xDFC,{7227,7228,7229,7230} },{12,12,0,58,0,0,0xC,0xCE,0x78,{7832,7833}},{12,12,0,62,0x3,6,0x808,0x891,0xCA8,{7819,7834,7819,7821} },{12,12,0,31,0x3,0,0xB0A,0x2D0,0x222,{7835,7836,7837,7838} },{12,11,0,91,0x3,-2,0,0x4D1,0x5C8,{7823,7839,7825,7826} },{6,0,0,41,0x3,0,0x2,0x175,0x125,{7840,7841,7842,7843} },{12,0,0,35,0x3,0,0xC0E,0x196,0x146,{7840,7844,7845,7846} },{0,10,0,29,0x3,0,0xC0C,0x92,0xE2,{7847,7848,7849,7846} },{8,0,0,41,0x3,0,0xC0E,0x196,0x146,{7850,7851,7852,7853} },{8,0,0,37,0x3,0,0xC0E,0x196,0x140,{7850,7851,7852,7853} },{12,10,0,54,0x3,0,0xA0C,0x118,0x146,{7232,726,7233,7234} },{12,10,0,48,0x3,0,0xA0C,0x11E,0x146,{7232,726,7235,7234} },{12,6,0,77,0x3,0,0x808,0xC1,0xAD,{7854,7855,7856,7857} },{12,11,0,72,0x3,0,0x808,0xC8,0xA6,{7858,7859,7856,7860} },{12,12,0,88,0,0,0xF,0x8C,0x64,{697,698}},{12,12,0,40,0,0,0xE,0x201,0x5D,{7861,7862}},{12,12,0,38,0,0,0,0xC8,0x5D,{7863,7864}},{12,12,0,36,0,0,0xA,0x26C,0x5D,{7865,7866}},{0,0,0,60,0x3,0,0xE0C,0x78,0x64,{1340,1340,7236,7237} },{0,-12,0,60,0x3,0,0x20E,0x175,0x64,{7236,7237,7238,7239} },{12,12,0,73,0,0,0x8,0xA6,0x64,{7240,2302}},{0,-2,0,68,0x3,0,0,0x99,0x6A,{7241,7242,7241,7243} },{12,12,0,61,0,0,0x6,0xC8,0x6A,{2362,2302}},{13,13,0,18,0x3,0,0x8,0xC8,0x92,{7244,7245,7246,7247} },{13,13,0,18,0x3,0,0xC,0xFD,0xAD,{7248,7249,7250,7251} },{12,12,0,90,0,0,0x8,0xC1,0x5D,{7867,744}},{12,12,0,90,0,0,0x8,0x319,0x6A,{7868,746}},{12,12,0,64,0x3,2,0xE0E,0x175,0x6A,{7254,7255,7256,7257} },{12,12,0,80,0x3,2,0x606,0x19D,0x1A4,{7869,7870,7871,7870} },{0,3,0,64,0x3,0,0x101,0x76C,0x898,{7872,7873,7874,7875} },{0,0,0,67,0,0,0xE,0x6A,0x5D,{7258,7259}},{0,-7,0,50,0x3,0,0x8,0xAD,0x139,{7876,7877,7878,7879} },{0,0,0,36,0,0,0,0x11F1,0x14FD,{7880,7881}},{0,0,0,25,0x3,0,0x804,0x11E,0x189,{7785,7786,7882,7883} },{12,0,0,25,0x3,0,0x408,0x416,0x161,{7789,7790,7884,7885} },{0,0,20,43,0x1,0,0xE,0x1FA,0x24A,{7886,7887,7888,7889} },{0,12,10,15,0x3,0,0xC01,0x4B0,0x58C,{7890,7891,7892,7893} },{-5,0,0,48,0x3,-122,0x806,0x118,0x140,{7210,7211,7210,7212} },{0,12,10,19,0x3,0,0xC01,0x4A9,0x585,{7890,7891,7894,7893} },{0,12,10,22,0x3,0,0xC01,0x4A9,0x58C,{7890,7891,7892,7893} },{0,12,10,24,0x3,0,0xC01,0x4A9,0x585,{7890,7891,7892,7893} },{0,12,10,27,0x3,0,0x1,0x4A9,0x58C,{7890,7891,7892,7893} },{12,12,0,61,0x3,6,0x808,0xC72,0x708,{7219,7220,7219,7220} },{0,12,10,31,0x3,0,0xC01,0x4B0,0x58C,{7890,7891,7892,7893} },{12,12,0,60,0x3,2,0xA0E,0x320,0x3A5,{7895,7824,7896,7824} },{12,12,0,62,0x3,6,0x808,0xC8D,0x73D,{7219,7231,7219,7231} },{12,12,0,61,0,0,0x8,0x326,0x3B9,{7897,7898}},{0,12,0,49,0x3,0,0xC01,0x104,0x12C,{7899,7900,7204,7901} },{0,0,0,86,0x3,-122,0xE08,0x99,0xAD,{7902,7903,7904,7905} },{0,0,0,86,0x1,0,0xC0C,0x9C40,0xF0,{7906,7907,7908,7909} },{24,31,0,49,0x3,0,0,0x1AA,0x1F4,{7910,25,7911,25} },{12,0,0,0,0,0,0xA,0x9C40,0xC8,{7912,7913}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{7914,7915}},{12,0,0,0,0,0,0xA,0x855,0x960,{7916,7917}},{12,0,0,0,0,0,0x6,0x4D1,0x5D,{1180,7918}},{12,0,0,0,0,0,0xA,0x9C40,0x5D,{7919,7920}},{12,0,0,0,0,0,0x8,0x9C40,0x5D,{7921,7922}},{-12,0,0,0,0,0,0x4,0x9C40,0x5D,{7923,7924}},{12,0,0,0,0,0,0xE,0x9C40,0x6A,{7925,7926}},{12,0,0,0,0,0,0xC,0x9C40,0x5D,{7927,7928}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1123,7929}},{0,0,0,73,0,0,0xE,0x9C40,0x5D,{1123,7929}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{4498,7930}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{7931,7932}},{0,0,0,0,0,0,0x8,0x1BB5,0x201C,{960,7933}},{-6,0,0,0,0,0,0,0x10E6,0x1366,{7934,7935}},{-18,0,0,0,0,0,0xE,0x286,0x5D,{651,7936}},{12,0,0,0,0,0,0,0xFD,0x5D,{7937,7938}},{12,0,0,12,0,0,0,0xFD,0x5D,{7937,7938}},{12,0,0,0,0,0,0xE,0x258,0xD5,{7939,7940}},{12,0,0,1,0,0,0xE,0x258,0xD5,{7939,7940}},{12,0,0,0,0,0,0,0x85,0xCE,{7941,7942}},{12,0,0,23,0,0,0,0x85,0xCE,{7941,7942}},{12,0,0,0,0,0,0xE,0x258,0xD5,{7943,7944}},{-12,0,0,0,0,0,0x8,0xE09,0x1316,{7945,7946}},{12,0,0,0,0,0,0xE,0x9C40,0x6A,{7947,7948}},{-12,0,0,0,0,0,0xA,0x9C40,0x5D,{7949,7950}},{12,0,0,0,0,0,0x8,0x9C40,0xAD,{7951,7952}},{-12,0,0,0,0,0,0x6,0x9C40,0x50,{7953,7954}},{-12,0,0,0,0,0,0,0xF6,0x5D,{7937,7955}},{21,0,0,0,0x4,0,0x8,0x6,0,{5397,7956}},{0,0,0,0,0,0,0x6,0x1352,0x2DD,{5822,7957}},{0,0,0,0,0,0,0x6,0x1352,0x2DD,{5823,7957}},{0,0,0,0,0,0,0x6,0x1352,0x2DD,{7958,7957}},{0,0,0,0,0,0,0x6,0x1172,0x251,{5828,7959}},{0,0,0,0,0,0,0,0x471E,0x5D,{5834,7960}},{0,0,0,0,0,0,0xA,0x924,0x189,{7961,7962}},{0,0,0,0,0,0,0x8,0x1212,0x89E,{5847,7963}},{0,0,0,0,0,0,0x8,0x4D1,0x64,{5852,5851}},{0,0,0,0,0,0,0x8,0x11C2,0x1136,{5432,7964}},{0,12,0,0,0,-128,0x8,0x46FD,0x54DE,{7965,7966}},{0,12,0,0,0,-128,0x8,0x49C,0x578,{7967,7968}},{0,0,0,0,0,0,0,0x11FE,0x2DD,{7969,7970}},{-12,0,0,0,0,0,0xE,0x30C,0x57E,{5441,7971}},{0,0,0,0,0,0,0xE,0x2BC,0x5B4,{5642,5643}},{0,0,0,0,0,0,0x8,0x335A,0x224C,{5898,7972}},{0,0,0,0,0,0,0x8,0x16F1,0x5D,{7973,7974}},{0,0,0,0,0,0,0x1,0x9C40,0x5D,{5647,1069}},{-12,0,0,0,0,0,0,0x9C40,0x5D,{5836,7975}},{-12,0,0,0,0,0,0,0x9C40,0x5D,{5834,7976}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{5832,7977}},{0,0,0,0,0,0,0x2,0x9C40,0x189,{5841,7978}},{0,0,0,0,0,0,0xE,0x9C40,0x258,{5958,7979}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{7980,7981}},{0,0,0,0,0,0,0x6,0x209A,0x8B9,{7982,7983}},{0,0,0,0,0,0,0x6,0x1DF2,0x251,{7984,7985}},{0,0,0,0,0,0,0xE,0x9C40,0x56,{1414,1415}},{0,0,0,0,0,0,0x4,0x1964,0x46D,{7986,7987}},{0,0,0,0,0,0,0x4,0x196A,0x46D,{7988,7989}},{0,12,0,0,0,-128,0x8,0x9C40,0x5D,{7990,7991}},{0,12,0,0,0,-128,0x8,0x9C40,0x5D,{7990,6558}},{0,0,0,0,0,0,0xC,0x9C40,0xA6,{7992,7993}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{5921,7994}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{5923,7995}},{0,0,0,0,0,0,0x4,0x9C40,0x5D,{7996,7997}},{0,12,0,0,0,-128,0xA,0x9C40,0x5D,{5923,7998}},{0,0,0,0,0,0,0x4,0x2DA0,0x5D,{5929,5930}},{12,0,0,0,0,0,0,0x959,0x64,{7999,8000}},{12,12,0,0,0,-128,0xE,0x9C40,0x5D,{5857,8001}},{0,12,0,0,0,-128,0x8,0x16EA,0x5D,{5868,7974}},{0,0,0,0,0,0,0xE,0x9C40,0x154,{5495,536}},{0,0,0,0,0,0,0xE,0x9C40,0x189,{8002,536}},{0,0,0,0,0,0,0xE,0x9C40,0x189,{8003,536}},{0,0,0,0,0,0,0xE,0x9C40,0x175,{5491,8004}},{12,0,0,0,0,0,0,0xFD,0x5D,{693,1343}},{0,0,0,0,0,0,0,0x2274,0x14C1,{1260,8005}},{-12,0,0,0,0,0,0xF,0x49C,0xB40,{8006,3895}},{0,12,0,0,0,-128,0,0x9C40,0x2AE,{8007,8008}},{0,0,0,0,0,0,0x6,0x9C40,0x12C0,{5902,8009}},{0,12,0,0,0,-128,0,0x9C40,0x9D1,{5892,8010}},{0,0,0,0,0,0,0x2,0x9C40,0x535,{8011,8012}},{0,0,0,0,0,0,0x2,0x9C40,0x2D6,{8013,8014}},{0,0,0,0,0,0,0x2,0x9C40,0x189,{6993,8015}},{0,0,0,0,0,0,0x6,0x16F1,0xA7E,{5690,8016}},{-12,0,0,0,0,0,0x8,0x916,0xAC1,{8017,8018}},{0,12,0,0,0,-128,0xC,0x9C40,0x189,{5534,8019}},{0,0,0,0,0,0,0xC,0x9C40,0x2DD,{5524,8020}},{0,12,0,0,0,-128,0xC,0x9C40,0x2DD,{8021,8022}},{0,0,0,0,0,0,0xE,0x9C40,0x125,{5491,8023}},{0,12,0,0,0,-128,0xE,0x9C40,0x2DD,{5967,6605}},{0,0,0,0,0,0,0xE,0x9C40,0x16E,{5972,8024}},{0,12,0,0,0,-128,0xC,0x9C40,0x189,{8025,8019}},{0,0,0,0,0,0,0xC,0x9C40,0x64,{5948,8026}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{5945,8027}},{0,0,0,0,0,0,0xC,0x9C40,0x64,{5536,8026}},{12,0,0,0,0,0,0xC,0x9C40,0x64,{5949,8026}},{12,0,0,0,0,0,0x2,0x9C40,0x5D,{5956,8028}},{12,0,0,0,0,0,0xE,0x9C40,0x5D,{8029,8030}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{8031,8032}},{0,0,0,0,0,0,0xC,0x9C40,0x17C,{5430,8033}},{-12,0,0,0,0,0,0x2,0x9C40,0x71,{5942,8034}},{0,12,0,0,0,-128,0x2,0x9C40,0x71,{5940,8034}},{-12,12,0,0,0,-128,0x2,0x9C40,0x71,{8035,8034}},{0,12,0,0,0,-128,0x2,0x9C40,0x71,{8036,8034}},{12,0,0,0,0,0,0x6,0x208,0x16E,{8037,8038}},{0,0,0,0,0,0,0xE,0x9C40,0x6A,{1144,1145}},{-31,0,0,0,0,0,0xD,0x9C40,0x58C,{7304,8039}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{5832,8040}},{0,0,0,0,0,0,0,0x9C40,0xDC,{8041,8042}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{8043,8044}},{-12,0,0,0,0,0,0x2,0x9C40,0x71,{8045,5941}},{0,0,0,0,0,0,0,0x2EB1,0x5D,{8046,8047}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{8048,8049}},{0,0,0,0,0,0,0x6,0x9C40,0x140,{5987,8050}},{0,0,0,0,0,0,0x6,0x9C40,0x578,{5976,8051}},{0,0,0,0,0,0,0x6,0x9C40,0x1428,{8052,8009}},{12,0,0,0,0,0,0xC,0x9C40,0xA35,{8053,8054}},{0,0,0,0,0,0,0xA,0x9C40,0x4E5,{8055,8056}},{-12,0,0,0,0,0,0xC,0x646,0x826,{7992,8057}},{0,0,0,0,0,0,0x8,0x47CC,0x151E,{5876,5877}},{0,0,0,0,0,0,0x8,0x3CBA,0x21E1,{8058,8059}},{0,0,0,0,0,0,0x8,0x38D2,0x2169,{8060,8061}},{0,0,0,0,0,0,0xA,0x916,0xAD5,{8062,8063}},{0,0,0,0,0,0,0x6,0x9C40,0x3861,{5878,8064}},{12,0,0,0,0,0,0xE,0x9C40,0x27F9,{5443,8065}},{0,0,0,0,0,0,0,0x9C40,0x1179,{5880,8066}},{0,0,0,0,0,0,0x8,0x9C40,0x10ED,{5894,8067}},{0,0,0,0,0,0,0x2,0x9C40,0x1406,{8068,8069}},{0,0,0,0,0,0,0x8,0x9C40,0x1511,{5886,8070}},{0,0,0,0,0,0,0x6,0x9C40,0x2274,{5744,8071}},{0,0,0,0,0,0,0,0x256C,0x1596,{984,3279}},{0,0,0,0,0,0,0x8,0x2E4,0x370,{4030,8072}},{0,0,0,0,0,0,0xE,0x1C62,0x217D,{6007,6008}},{-12,0,0,0,0,0,0xE,0x48E,0xB40,{6004,3895}},{0,0,0,0,0,0,0xA,0x9C40,0xE2,{5900,8073}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{8074,8075}},{0,0,0,0,0,0,0x6,0x9C40,0xAD5,{5434,8076}},{12,12,0,0,0,0,0x4,0x125,0xF6,{2297,2298}},{0,0,0,0,0,0,0xA,0x96D,0x57E,{5996,8077}},{12,12,0,0,0,-128,0xE,0x19D,0x5D,{651,652}},{12,0,0,0,0,0,0x1,0x4BD,0x578,{8078,584}},{0,0,0,0,0,0,0xE,0x4B6,0xE2,{651,406}},{0,0,0,0,0,0,0x6,0x4DE,0x64,{1114,1323}},{0,0,0,0,0,0,0xE,0x111,0x49,{755,756}},{12,12,0,0,0,0,0xE,0x5BA,0xF6,{2311,2312}},{-12,0,0,0,0,0,0xA,0x9C40,0x58C,{4118,8079}},{0,0,0,0,0,0,0xE,0x1435,0x2D0,{8080,8081}},{0,0,0,0,0,0,0xE,0x111,0x49,{6714,8082}},{0,0,0,0,0,0,0,0x9C40,0x2169,{5989,8083}},{0,0,0,0,0,0,0xE,0xECA,0x2D6,{8084,8085}},{0,0,0,0,0,0,0xE,0x272,0x2DD,{5998,8086}},{0,12,0,49,0,-128,0x6,0x3CD,0x5D,{1114,1323}},{0,12,0,41,0,-128,0x6,0x4B0,0x5D,{1114,1323}},{0,12,0,60,0,-128,0xE,0x64,0x5D,{6270,6271}},{0,12,0,60,0,-128,0xE,0x11E,0x146,{929,6272}},{0,12,0,21,0,-128,0xE,0xD5,0xE2,{8087,8088}},{0,0,0,60,0,0,0xE,0xAD,0x466,{8089,8090}},{0,0,0,44,0,0,0x1,0x3D4,0x46D,{8078,584}},{0,0,0,80,0,0,0xE,0x64,0x5D,{6270,8091}},{0,0,0,50,0,0,0x1,0x3CD,0x474,{8078,584}},{0,12,0,60,0,-128,0xE,0x64,0x6A,{6270,8092}},{0,0,0,55,0,0,0x1,0x3CD,0x474,{8078,584}},{0,0,0,61,0,0,0xE,0x1FA,0x146,{8093,8094}},{0,0,0,58,0,0,0x1,0x3CD,0x47A,{8078,584}},{0,0,0,63,0,0,0x1,0x3C6,0x47A,{8078,584}},{0,0,0,71,0,0,0xE,0x53C,0x74A,{8095,8096}},{0,0,0,72,0,0,0x1,0x32D,0x3C0,{8078,584}},{0,0,0,70,0,0,0,0x736,0x862,{8097,8098}},{0,0,0,88,0,0,0xE,0x53C,0x74A,{8095,8096}},{0,0,0,76,0,0,0,0x73D,0x876,{8097,8099}},{0,0,0,84,0,0,0xE,0x104,0x111,{8100,8101}},{0,0,0,68,0,0,0xE,0x52E,0x74A,{8095,8096}},{0,0,0,72,0,0,0x8,0x92,0x78,{8102,8103}},{0,0,0,28,0,0,0xA,0x4A2,0x564,{4531,8104}},{0,0,0,81,0,0,0,0x535,0x611,{8097,8098}},{0,12,0,82,0,-128,0x1,0x1C5,0x3B9,{8105,8106}},{0,12,0,79,0,-128,0x1,0x1C5,0x3B9,{8105,8106}},{0,12,0,68,0,-128,0x1,0x190,0x3A5,{8105,8107}},{0,12,0,73,0,-128,0x1,0x1C5,0x3B9,{8105,8106}},{0,12,0,64,0,-128,0x1,0x21C,0x47A,{8105,8106}},{0,0,0,65,0,0,0xE,0x3A5,0x23D,{651,8108}},{0,0,0,60,0,0,0xE,0x140,0x85,{651,8109}},{0,0,0,64,0,0,0xE,0x1FA,0x251,{8110,8111}},{0,0,0,57,0,0,0xE,0x1FA,0x251,{8110,8111}},{0,12,0,60,0,-128,0xE,0x92,0x2BC,{8112,8113}},{0,12,0,60,0,-128,0xE,0x78,0x46D,{8112,8114}},{0,0,0,39,0,0,0x4,0x104,0x64,{1641,1642}},{0,0,0,36,0,0,0,0x445,0x64,{1643,1644}},{0,12,0,60,0,-128,0xE,0xF6,0x175,{8112,8115}},{0,12,0,85,0,-128,0xE,0xA0,0x5D,{651,652}},{0,12,0,80,0,-128,0xE,0xA0,0x5D,{651,652}},{0,12,0,75,0,-128,0xE,0xC1,0x5D,{651,652}},{0,0,0,41,0,0,0,0xC1,0x64,{1650,1651}},{0,0,0,41,0,0,0,0x14D,0x161,{1652,1653}},{0,0,0,108,0,0,0x8,0x2E4,0x5D,{5849,8116}},{0,0,0,108,0,0,0x8,0x2E4,0x5D,{5849,8117}},{0,0,0,80,0,0,0xE,0xBA,0x64,{6270,8118}},{0,0,0,0,0,0,0,0x4A9,0x58C,{8119,8120}},{0,0,0,0,0,0,0x404,0x9C40,0xC1,{8121,8122}},{0,0,0,0,0,0,0x808,0x14E2,0x1129,{3254,8123}},{0,0,0,0,0,0,0x202,0x9C40,0x47A,{8124,8125}},{0,0,0,0,0,0,0x606,0x201,0x251,{4069,8126}},{0,0,0,0,0,0,0xC0C,0x2EA,0x64,{8127,8128}},{0,0,0,0,0,0,0x606,0x9C40,0x6A,{8129,8130}},{0,0,0,0,0,0,0x606,0xD76,0xE2,{8131,8132}},{0,0,0,0,0,0,0x606,0x9C40,0x5D,{4059,8133}},{0,0,0,0,0,0,0x606,0x9C40,0xC1,{2532,8134}},{0,0,0,0,0,0,0xE0E,0x9C40,0x42,{8135,8136}},{0,0,0,0,0,0,0xE0E,0x9C40,0x42,{8137,8136}},{0,0,0,0,0,0,0xE0E,0x6FA,0x64,{8138,8139}},{0,0,0,0,0,0,0x202,0x9C40,0x5D,{8140,8141}},{0,0,0,0,0,0,0xE0E,0x334,0x56,{8142,8143}},{0,0,0,0,0,0,0,0x48E,0x564,{8144,8145}},{0,0,0,0,0,0,0xA0A,0x9C40,0x549,{584,8146}},{0,0,0,0,0,0,0xA0A,0x9C40,0x535,{8147,8148}},{0,0,0,0,0,0,0x808,0x9C40,0xA7E,{8149,8150}},{0,0,0,0,0,0,0xE0E,0x1C5,0x50,{5356,8151}},{0,0,0,0,0,0,0xA0B,0x9C40,0x5D,{8152,8152}},{0,0,0,0,0,0,0x808,0x26C,0x2D6,{948,949}},{0,0,0,0,0,0,0xE0E,0x9C40,0xAE2,{8153,8154}},{0,0,0,0,0,0,0,0x71,0x5D,{8155,8156}},{0,0,0,0,0,0,0xA0A,0x265,0x5D,{8157,8158}},{0,0,0,0,0,0,0xE0E,0x334,0x56,{8159,8160}},{0,0,0,0,0,0,0xE0F,0x99,0x6A,{8161,8162}},{0,0,0,0,0,0,0xE0F,0x6A,0x71,{8163,8162}},{0,0,0,0,0,0,0x808,0x71,0x6A,{8164,8165}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{8166,8167}},{0,0,0,0,0,0,0xE0E,0x9C40,0xAE9,{8168,8169}},{0,0,0,0,0,0,0xE0E,0x9C40,0xAE2,{8170,8171}},{0,0,0,0,0,0,0xE0E,0xBA,0xC8,{8172,8173}},{0,0,0,0,0,0,0xE0E,0xB4,0xC8,{8174,8175}},{0,0,0,0,0,0,0x808,0x140,0x168,{3189,8176}},{0,0,0,0,0,0,0x808,0x1D9,0x64,{8177,8178}},{0,0,0,0,0,0,0x1,0x294,0x25E,{8179,8180}},{0,0,0,0,0,0,0,0x1810,0xAC8,{8181,8182}},{0,0,0,0,0,0,0xE0E,0x3D4,0x2AE,{8183,8184}},{0,0,0,0,0,0,0xE0E,0x319,0x5D,{8183,8185}},{0,0,0,0,0,0,0xE0E,0x9C40,0x251,{8186,8187}},{0,0,0,0,0,0,0xE0E,0x272,0x5D,{8188,8189}},{0,0,0,0,0,0,0xC0C,0x9C40,0xB61,{8190,8191}},{0,0,0,0,0,0,0xE0E,0x9C40,0x2DD,{8192,8193}},{0,0,0,0,0,0,0xE0E,0x9C40,0x189,{8194,8195}},{0,0,0,0,0,0,0x808,0x9C40,0x409,{8196,8197}},{0,0,0,0,0,0,0xE0E,0x125,0x168,{3189,928}},{0,0,0,0,0,0,0xE0E,0x11E,0x146,{8198,8199}},{0,0,0,0,0,0,0xE0E,0x3D4,0x2AE,{8200,8201}},{0,0,0,0,0,0,0xE0E,0x118,0x50,{8202,8203}},{0,0,0,0,0,0,0,0x1FA,0x251,{8204,8205}},{0,0,0,0,0,0,0,0x6F4,0xA99,{8206,8207}},{0,0,0,0,0,0,0,0x1A4,0xC1,{8208,8209}},{0,0,0,0,0,0,0xE0E,0x1C5,0x42,{8210,8211}},{0,0,0,0,0,0,0xE0E,0x1C5,0x42,{8210,8212}},{0,0,0,0,0,0,0x808,0x9C40,0x409,{8213,8214}},{0,0,0,0,0,0,0xE0E,0x11E,0x146,{8215,8216}},{0,0,0,0,0,0,0,0x2C2,0x258,{8217,8218}},{0,0,0,0,0,0,0x606,0x161,0x5D,{8217,8219}},{0,0,0,0,0,0,0xE0E,0xD91,0x182,{8220,8221}},{0,0,0,0,0,0,0xC0C,0x15A,0x189,{8222,8223}},{0,0,0,0,0,0,0xE0E,0x154,0x5D,{8224,8225}},{0,0,0,0,0,0,0xE0E,0x154,0x5D,{8226,8227}},{0,0,0,0,0,0,0xE0E,0x1220,0x891,{8228,962}},{0,0,0,0,0,0,0xA0A,0x1220,0x898,{8228,962}},{0,0,0,0,0,0,0xE0E,0x320,0x466,{8229,8230}},{0,0,0,0,0,0,0xC0C,0x9C40,0x47A,{8231,8232}},{0,0,0,0,0,0,0xA0A,0x9C40,0x585,{8233,8234}},{0,0,0,0,0,0,0xE0E,0x21C,0x5D,{914,8235}},{0,0,0,0,0,0,0xE0E,0xB4,0x42,{8210,8236}},{0,0,0,0,0,0,0xE0E,0xF6,0xA6,{8210,8237}},{0,0,0,0,0,0,0xE0E,0x17C,0x42,{8238,8239}},{0,0,0,0,0,0,0xE0E,0x1FA,0x251,{8240,8241}},{0,0,0,0,0,0,0xE0E,0x1838,0x1C98,{8242,8243}},{0,0,0,0,0,0,0x808,0x1D2,0x64,{8177,8244}},{0,0,0,0,0,0,0,0x9C40,0x58C,{8245,8246}},{0,0,0,0,0,0,0,0x9C40,0x226D,{8247,8248}},{0,0,0,0,0,0,0,0x1A90,0x2176,{8249,8250}},{0,0,0,0,0,0,0,0x1A54,0x2134,{8251,8252}},{0,0,0,0,0,0,0x808,0x2086,0x215C,{8253,8254}},{0,0,0,0,0,0,0x808,0xA5D,0xAB4,{8255,8256}},{0,0,0,0,0,0,0,0x1FA,0x251,{8204,8257}},{0,0,0,0,0,0,0xE0E,0x1C5,0x71C,{8258,8259}},{0,0,0,0,0,0,0xC0C,0x1220,0x8AC,{8260,8261}},{0,0,0,0,0,0,0xE0E,0x123A,0x1568,{8262,8263}},{0,0,0,0,0,0,0xE0E,0x1130,0x1492,{8264,8265}},{0,0,0,0,0,0,0xE0E,0x7D0,0x139,{8266,8267}},{0,0,0,0,0,0,0xE0E,0x182,0x139,{8268,8269}},{0,0,0,0,0,0,0x606,0x1E0,0x251,{8270,8271}},{0,0,0,0,0,0,0x606,0x47A,0x64,{8272,8273}},{0,0,0,0,0,0,0xC0C,0x1D9,0x6A,{8274,8275}},{0,0,0,0,0,0,0x606,0x78,0x6A,{8276,8277}},{0,0,0,0,0,0,0x606,0x56,0x5D,{8131,8278}},{0,0,0,0,0,0,0x606,0x71,0x5D,{8131,8279}},{0,0,0,0,0,0,0x606,0x71,0x5D,{8280,8281}},{0,0,0,0,0,0,0xE0E,0x9C40,0x104D,{8282,8283}},{0,0,0,0,0,0,0xE0E,0x9C40,0xA8C,{8284,8285}},{0,0,0,0,0,0,0xE0E,0x9C40,0xAE9,{8284,8286}},{0,0,0,0,0,0,0xE0E,0x111,0x49,{755,8287}},{0,0,0,0,0,0,0xE0E,0x265,0x26C,{8288,8289}},{0,0,0,0,0,0,0xE0E,0x9C40,0x5D,{8290,8291}},{0,0,0,0,0,0,0xE0E,0x168,0xC1,{8292,8293}},{0,0,0,0,0,0,0xE0E,0x3616,0x4455,{8294,8295}},{0,0,0,0,0,0,0xE0E,0x1BEA,0x2245,{8296,8297}},{0,0,0,0,0,0,0xE0E,0x9C40,0x2274,{8298,8299}},{0,0,0,0,0,0,0,0x4B0,0x58C,{8300,8301}},{0,0,0,0,0,0,0x606,0x4B0,0x58C,{8300,8301}},{0,0,0,0,0,0,0x808,0x9C40,0x8A5,{8302,8303}},{0,0,0,0,0,0,0xE0E,0x3D4,0xADC,{8304,934}},{0,0,0,0,0,0,0,0x474,0x5D,{8206,8305}},{0,0,0,0,0,0,0x808,0x848,0x258,{8306,8307}},{0,0,0,0,0,0,0xA0B,0x4E5,0x5BA,{8308,8308}},{0,0,0,0,0,0,0,0x85,0x64,{8309,8310}},{0,0,0,0,0,0,0x808,0x7AE,0x87D,{8302,8311}},{0,0,0,0,0,0,0xC0C,0x9C40,0x182,{8312,8313}},{0,0,0,0,0,0,0xE0E,0x179E,0x152C,{8314,8315}},{0,0,0,0,0,0,0xC0C,0x9C40,0x14D,{8316,8317}},{0,0,0,0,0,0,0,0x9C40,0x58C,{8318,8319}},{0,0,0,0,0,0,0,0x9C40,0x5D,{8320,8321}},{0,0,0,0,0,0,0x404,0x9C40,0x550,{8322,8323}},{0,0,0,0,0,0,0xA0A,0x9C40,0x182,{8324,8325}},{0,0,0,0,0,0,0xE0F,0x9C40,0x5D,{8326,8327}},{0,0,0,0,0,0,0xE0F,0x139,0xA0,{8328,8329}},{0,0,0,0,0,0,0,0x9C40,0x92,{8330,8331}},{0,0,0,0,0,0,0x404,0x15C5,0x2DD,{8332,8333}},{0,0,0,0,0,0,0x202,0x192E,0x258,{8334,8335}},{0,0,0,0,0,0,0x606,0x1040,0x44C,{8336,8337}},{0,0,0,0,0,0,0x606,0x1004,0x416,{8338,8339}},{0,0,0,0,0,0,0,0x71,0x5D,{8340,8341}},{0,0,0,0,0,0,0,0x85,0x64,{8342,8343}},{0,0,0,0,0,0,0xE0E,0x85,0x92,{8344,8345}},{0,0,0,0,0,0,0,0x4A9,0xAAD,{8346,8347}},{0,0,0,0,0,0,0,0x25E,0x2AE,{8144,8348}},{0,0,0,0,0,0,0x808,0x9C40,0x84E,{8349,8350}},{0,0,0,0,0,0,0xE0E,0xC8,0x64,{8351,8352}},{0,0,0,0,0,0,0xA0A,0x265,0x2D6,{8353,8354}},{0,0,0,0,0,0,0xA0A,0x1194,0x1518,{7969,8355}},{0,0,0,0,0,0,0xE0E,0x92,0x5D,{8356,8357}},{0,0,0,0,0,0,0xE0E,0x15A,0x190,{8358,8359}},{0,0,0,0,0,0,0x202,0x9C40,0x2C2,{8360,8361}},{0,0,0,0,0,0,0x202,0x9C40,0x542,{8362,8363}},{0,0,0,0,0,0,0xA0A,0x2FE,0x190,{8364,8365}},{0,0,0,0,0,0,0x404,0x9C40,0x1ED,{8366,8367}},{0,0,0,0,0,0,0x808,0x765,0x8B9,{8368,8369}},{0,0,0,0,0,0,0xC0C,0x9C40,0x4F2,{8370,8371}},{0,0,0,0,0,0,0x808,0x1068,0x474,{3254,8372}},{0,0,0,0,0,0,0x606,0x3C6A,0x1450,{5445,8373}},{0,0,0,0,0,0,0xC0C,0x9C40,0x500,{8374,8375}},{0,0,0,0,0,0,0x1,0x189,0x535,{3651,1343}},{0,0,0,0,0,0,0xA0A,0x9C40,0x222,{8376,8377}},{0,0,0,0,0,0,0xE0E,0x9C40,0x3CCE,{8378,8379}},{0,0,0,0,0,0,0,0x11E,0x5D,{8380,8381}},{0,0,0,0,0,0,0xA0A,0x2BC,0xDC,{8382,1229}},{0,0,0,0,0,0,0x404,0x9C40,0x1ED,{8383,8384}},{0,0,0,0,0,0,0x606,0x208,0x25E,{8385,8126}},{0,0,0,0,0,0,0x606,0x201,0x25E,{8386,8126}},{0,0,0,0,0,0,0x202,0x9C40,0x549,{8387,8363}},{0,0,0,60,0,0,0,0x64,0x6A,{8388,8389}},{0,0,0,36,0,0,0,0x6A,0x71,{8388,8389}},{0,0,0,12,0,0,0,0x71,0x71,{8388,8389}},{0,0,0,60,0,0,0,0xAD,0xC1,{8388,8390}},{0,0,0,36,0,0,0,0xC8,0xE2,{8388,8390}},{0,0,0,12,0,0,0,0xCE,0xE9,{8388,8390}},{0,0,0,12,0,0,0xE0E,0xCE,0xDC,{5384,5385}},{0,0,0,12,0,0,0xE0E,0x279,0x2DD,{8391,8392}},{0,0,0,58,0,0,0,0x1D2,0x5D,{8393,8273}},{0,0,0,53,0,0,0,0x265,0x5D,{8393,8273}},{0,0,0,55,0,0,0xE0E,0x146,0x334,{8394,8395}},{0,0,0,64,0,0,0xE0E,0x11E,0x140,{932,933}},{0,0,0,78,0,0,0xE0E,0xF6,0x111,{932,933}},{0,0,0,84,0,0,0xE0E,0xF6,0x111,{932,933}},{0,0,0,41,0,0,0x606,0x9C40,0x55D,{8396,859}},{0,0,0,84,0,0,0x808,0x146,0x118,{943,8397}},{0,0,0,84,0,0,0xE0E,0x9C40,0x5D,{8398,8399}},{0,0,0,48,0,0,0x606,0x9C40,0x3C6,{8396,859}},{0,0,0,84,0,0,0xE0E,0x9C40,0xF6,{8400,8125}},{0,0,0,55,0,0,0x606,0x9C40,0x2C2,{8396,859}},{0,0,0,72,0,0,0xE0E,0xD5,0x49,{755,756}},{0,0,0,84,0,0,0xE0E,0x92,0x50,{8401,8402}},{0,0,0,54,0,0,0xE0E,0x11E,0x146,{8403,8359}},{0,0,0,60,0,0,0xE0E,0xF0,0x49,{8404,8405}},{0,0,0,84,0,0,0xE0E,0x305,0x64,{8153,8406}},{0,0,0,32,0,0,0x808,0x2C2,0x64D,{5336,5337}},{0,0,0,48,0,0,0x808,0x1FA,0x251,{948,949}},{0,0,0,48,0,0,0x404,0x1FA,0x24A,{8407,8408}},{0,0,0,48,0,0,0xA0A,0x104,0x146,{8409,2378}},{0,0,0,80,0,0,0,0x8C,0x99,{8204,8257}},{0,0,0,55,0,0,0,0xDC,0xFD,{8204,8257}},{0,0,0,12,0,0,0,0x258,0x2C9,{8204,8257}},{0,0,0,55,0,0,0x808,0x500,0x891,{8410,8411}},{0,0,0,41,0,0,0x808,0x3AC,0x571,{8412,8413}},{0,0,0,60,0,0,0,0x5B4,0x87D,{8206,8207}},{0,0,0,84,0,0,0x606,0x9C40,0x175,{8396,859}},{0,0,0,84,0,0,0xE0E,0x29A,0x73D,{8304,934}},{0,0,0,84,0,0,0x606,0x6A,0x5D,{8131,8279}},{0,0,0,84,0,0,0x606,0x6A,0x5D,{8280,8281}},{0,0,0,84,0,0,0xE0E,0xD5,0x49,{755,8287}},{0,0,0,84,0,0,0xE0E,0x2A1,0x744,{8414,934}},{0,0,0,84,0,0,0xE0E,0x2A1,0x744,{8415,934}},{0,0,0,48,0,0,0xE0E,0xA6,0x5D,{914,8416}},{0,0,0,64,0,0,0xA0A,0x208,0x251,{8417,8418}},{0,0,0,48,0,0,0xA0A,0x14D,0x146,{8419,8420}},{0,0,0,24,0,0,0xA0A,0x25E,0x258,{8421,8420}},{0,0,0,72,0,0,0xE0E,0x32D,0x3B2,{8422,8423}},{0,0,0,72,0,0,0xE0E,0x7E,0x5D,{8356,8357}},{0,0,0,72,0,0,0xE0E,0xF6,0x118,{8358,8359}},{0,0,0,72,0,0,0,0x99,0x5D,{8424,8425}},{0,0,0,0,0x4,0,0,0x6,0,{3651,6263}},{0,0,0,0,0x1,0,0,0x280,0x71,{8426,8427,8428,8429} },{0,0,0,0,0x1,0,0xF0E,0x41E9,0xB68,{8430,8431,8432,8433} },{0,0,0,0,0x1,0,0x405,0x9C40,0x2C2,{8434,8435,8436,8437} },{-12,0,0,0,0x1,0,0x908,0x9C40,0x2BC,{8438,8439,8440,8441} },{0,0,0,0,0x1,0,0x808,0x4A9,0xE2,{5037,5038,5039,5040} },{0,0,0,0,0x1,0,0xE0E,0x1CD4,0x251,{8442,8443,8444,5156} },{12,0,0,0,0x1,0,0xA0A,0x9C40,0x182,{4211,4212,4213,8445} },{0,0,0,0,0x1,0,0xE0E,0x3861,0x1108,{4935,4936,4937,8446} },{0,0,0,0,0x1,0,0x808,0x9C40,0x55D,{8447,8448,8449,8450} },{12,0,0,0,0x1,0,0x706,0x4509,0x556,{8451,8452,8453,8454} },{0,0,0,0,0x1,0,0xA0A,0x76C,0x8C6,{8455,8456,8457,8458} },{0,0,0,0,0x1,0,0xF0E,0x9C40,0x5DC,{5118,5119,5120,8459} },{12,0,0,0,0x1,0,0x808,0x85,0x92,{8460,8461,8462,8463} },{0,12,0,0,0,0,0x8,0xE8E,0x6A,{8464,8465}},{12,12,0,0,0x3,0,0xE0E,0x2B5,0x154,{8466,8467,8468,8469} },{0,12,0,0,0,0,0xE,0x9C40,0x5D,{8470,2209}},{12,12,0,0,0,-1,0x808,0x9C40,0x5D,{8471,8472}},{12,12,0,32,0,0,0xE,0x1FA,0x251,{8473,8474}},{12,12,0,36,0x3,0,0xE,0x11E,0x6A,{2334,2335,1340,8475} },{0,0,0,0,0,0,0x6,0x11B5,0x1082,{8476,8477}},{-12,0,0,0,0,0,0x2,0x15A4,0x13F2,{8478,8479}},{12,0,0,0,0,0,0x6,0xCDD,0x611,{8480,8481}},{0,0,0,0,0,0,0x6,0x11A1,0x83A,{8482,8483}},{0,0,0,0,0,0,0x6,0x1B29,0x1172,{8484,8485}},{12,0,0,0,0,0,0x6,0xF3C,0x640,{8486,8487}},{-12,0,0,0,0,0,0x8,0x11F8,0x150A,{8488,8489}},{0,0,0,0,0,0,0,0x1518,0x146,{8490,8491}},{0,0,0,0,0,0,0x8,0x7DD,0x140,{8492,8493}},{0,0,0,0,0,0,0x8,0x9C40,0x7B5,{8494,8495}},{0,0,0,0,0,0,0x4,0x1316,0x2191,{8496,8497}},{0,0,0,0,0,0,0x8,0x9C40,0xA3C,{8498,8499}},{0,0,0,0,0,0,0xA,0x48E,0x571,{8500,8501}},{0,0,0,0,0,0,0x8,0x9C40,0x11E,{5644,5645}},{0,0,0,0,0,0,0,0x6FA,0x2DD,{8502,8503}},{0,0,0,0,0,0,0x8,0xB46,0xE10,{4030,8504}},{0,0,0,0,0,0,0x5,0x9C40,0x10A,{8505,8506}},{0,0,0,0,0,0,0x7,0x9C40,0xFD,{8507,8508}},{-12,0,0,0,0,0,0x1,0x9C40,0x5D,{8509,8510}},{0,0,0,0,0,0,0x3,0x9C40,0x7E,{8511,8512}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{8513,8514}},{0,0,0,0,0,0,0xA,0x9C40,0x64,{8515,8516}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{5648,5649}},{0,0,0,0,0,0,0,0x9C40,0x64,{8517,8518}},{0,0,0,0,0,0,0,0x6FA,0x2DD,{8519,8520}},{0,0,0,0,0,0,0,0x6E6,0x2D0,{8521,8522}},{0,0,0,0,0,0,0xA,0x5A0,0xE9,{8523,8524}},{0,0,0,0,0,0,0,0x1C26,0x140,{8525,8526}},{0,0,0,0,0,0,0,0x3CD,0x146,{8527,8528}},{0,0,0,0,0,0,0x4,0x9C40,0x85,{8529,8530}},{0,0,0,0,0,0,0x4,0x9C40,0x1FA,{8531,8532}},{0,0,0,0,0,0,0,0xE6D,0x146,{8533,8534}},{12,0,0,0,0,0,0xC,0x9C40,0x46D,{2368,8535}},{12,0,0,0,0,0,0xC,0x9C40,0xA0,{8536,8537}},{12,0,0,0,0,0,0xA,0x9C40,0x92,{8538,8537}},{0,0,0,0,0,0,0xC,0x9C40,0xE2,{8539,8540}},{0,0,0,0,0,0,0xA,0x9C40,0xA0,{8541,8542}},{12,0,0,0,0,0,0x8,0x9C40,0x85,{8543,8544}},{0,0,0,0,0,0,0xA,0x9C40,0xD26,{8545,8546}},{0,0,0,0,0,0,0xA,0x9C40,0xD05,{8547,8548}},{-12,0,0,0,0,0,0,0x9C40,0x82D,{8549,8550}},{-12,0,0,0,0,0,0,0x9C40,0x42A,{8551,8552}},{0,0,0,0,0,0,0x6,0x9C40,0x535,{8553,4463}},{12,0,0,0,0,0,0x6,0x9C40,0x578,{2123,2124}},{-12,0,0,0,0,0,0x6,0x9C40,0x592,{8554,8555}},{-12,0,0,0,0,0,0xE,0x15A,0x2BC,{8556,8557}},{0,0,0,0,0,0,0,0x4BD,0x550,{8558,8559}},{0,0,0,0,0,0,0xA,0x2022,0x1575,{2133,8560}},{0,0,0,0,0,0,0xA,0x9C40,0x5D,{8561,8562}},{0,0,0,0,0,0,0xA,0x9C40,0x44CD,{8563,8564}},{-12,0,0,0,0,0,0xA,0x9C40,0x29F4,{8565,8566}},{0,0,0,0,0,0,0,0x9C40,0x42A,{8567,8568}},{0,0,0,0,0,0,0,0x9C40,0x2259,{8569,8570}},{-12,0,0,0,0,0,0,0x9C40,0x2961,{8571,8572}},{12,0,0,0,0,0,0,0x9C40,0xC65,{8573,8574}},{0,0,0,0,0,0,0xA,0x4F9,0x85,{8575,8576}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1123,8577}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{1297,8578}},{12,0,0,0,0,0,0xC,0x9C40,0x99C,{8579,8580}},{0,0,0,0,0,0,0xE,0x10B8,0x521,{8581,8582}},{0,0,0,0,0,0,0xE,0x9C40,0x85,{8583,8584}},{0,0,0,0,0,0,0xE,0x9C40,0x50,{8585,8586}},{12,0,0,0,0,0,0xE,0x9C40,0xC8,{8587,8588}},{0,0,0,0,0,0,0xE,0x9C40,0xBA,{8587,8589}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{8590,8591}},{0,0,0,0,0,0,0x8,0x9C40,0x64,{8592,8593}},{12,0,0,0,0,0,0x8,0x9C40,0x5D,{8594,8595}},{12,0,0,0,0,0,0x8,0x9C40,0x5D,{8596,8597}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{8598,8599}},{12,0,0,0,0,0,0xA,0x9C40,0x5D,{8600,8601}},{0,0,0,0,0,0,0xC,0x9C40,0x64,{1138,8602}},{0,0,0,0,0,0,0xC,0x9C40,0x5D,{8603,8604}},{-12,0,0,0,0,0,0xE,0x9C40,0x6A,{8605,8606}},{0,0,0,0,0,0,0xE,0x9C40,0x6A,{8607,1145}},{-12,0,0,0,0,0,0x9,0x9C40,0x16E,{8608,8609}},{-12,0,0,0,0,0,0x9,0x9C40,0x16E,{8610,8609}},{0,0,0,0,0,0,0xE,0x9C40,0xFD,{8611,8612}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{2208,2209}},{0,0,0,0,0,0,0xB,0x9C40,0x7E,{7585,8613}},{0,0,0,0,0,0,0xB,0x9C40,0x3FC,{8614,8615}},{0,0,0,0,0,0,0,0x9C40,0x452,{8616,8617}},{0,0,0,0,0,0,0,0x9C40,0x431,{8618,8619}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{2208,5725}},{0,0,0,0,0,0,0xE,0x9C40,0x5D,{8620,8621}},{0,0,0,0,0,0,0x8,0x9C40,0x5D,{8622,8623}},{0,0,0,0,0,0,0x8,0x9C40,0x150A,{8624,8625}},{-12,0,0,0,0,0,0xD,0x9C40,0x175,{8626,8627}},{0,0,0,0,0,0,0x9,0x9C40,0x16E,{8628,8629}},{0,0,0,0,0,0,0,0x9C40,0x445,{5736,5737}},{-12,0,0,0,0,0,0x9,0x9C40,0x38A,{8630,8631}},{0,0,0,0,0,0,0xA,0x9C40,0xBA,{8632,8633}},{0,0,0,0,0,0,0x1,0x9C40,0x3E9A,{8634,8635}},{0,0,0,0,0,0,0x6,0x9C40,0x661,{8636,8637}},{0,0,0,0,0,0,0xE,0x2030,0x1525,{8638,8639}},{0,0,0,0,0,0,0,0x9C40,0x410,{8640,8641}},{0,0,0,0,0,0,0x8,0x9C40,0x1504,{8642,8643}},{0,0,0,0,0,0,0xB,0x9C40,0x132,{8644,8645}},{0,0,0,0,0,0,0x7,0x9C40,0xF21,{8646,8647}},{0,0,0,0,0,0,0,0x9C40,0xE02,{8648,8649}},{0,0,0,0,0,0,0,0x9C40,0x104,{8650,8651}},{12,0,0,0,0,0,0xF,0x9C40,0x1838,{8652,8653}},{0,0,0,0,0,0,0,0x9C40,0x1032,{2271,8654}},{0,0,0,0,0,0,0,0x9C40,0x1EA0,{2273,8655}},{0,0,0,0,0,0,0xA,0x1C8A,0x215C,{8656,8657}},{12,0,0,0,0,0,0,0x156E,0xC36,{8658,8659}},{0,0,0,0,0,0,0x8,0xDCD,0x107C,{8660,8661}},{12,0,0,0,0,0,0,0x3E8,0x640,{8658,8662}},{0,0,0,0,0,0,0x8,0xE8E,0xC1,{8663,8664}},{0,0,0,0,0,0,0x6,0x49C,0x8AC,{2287,8665}},{0,0,0,0,0,0,0x2,0x9C40,0x5D,{3749,8666}},{0,0,0,0,0,0,0xE,0x9C40,0x290A,{8667,8668}},{12,0,0,0,0,0,0x8,0x9C40,0x5D,{8669,8670}},{0,0,0,0,0,0,0,0x30C,0x578,{8671,8672}},{0,0,0,0,0,0,0x4,0x189,0x146,{2297,2298}},{0,0,0,0,0,0,0x4,0x466,0x542,{8673,8674}},{12,0,0,0,0,0,0xE,0x2BC,0x33A,{5763,5764}},{0,0,0,0,0,0,0,0x3C0,0x459,{2303,5766}},{0,0,0,0,0,0,0,0x230,0x182,{2305,8675}},{0,0,0,0,0,0,0,0x3FC,0x189,{8676,8677}},{0,0,0,0,0,0,0x8,0x4B0,0x64,{2309,8678}},{0,0,0,0,0,0,0xB,0x9C40,0xC8,{8679,8680}},{0,0,0,0,0,0,0xE,0x604,0x50D,{8681,8682}},{0,0,0,0,0,0,0xE,0x1499,0x1596,{2315,8683}},{0,0,0,0,0,0,0x7,0x9C40,0xC36,{2317,2318}},{0,0,0,0,0,0,0x8,0x9C40,0x189,{2319,2320}},{0,0,0,0,0,0,0x8,0x9C40,0x4F74,{898,4032}},{0,0,0,0,0,0,0xE,0x9C40,0x290A,{8684,8685}},{0,0,0,0,0,0,0xE,0x272,0x2DD,{2324,2325}},{0,0,0,47,0x8,0,0,0x3E8,0xA,{693,6725}},{0,0,0,36,0x8,0,0,0x3E8,0xA,{8686,6727}},{0,0,0,67,0x8,0,0xE,0x3E8,0xA,{5778,5779}},{12,12,0,48,0x10,-128,0x3,0x3E8,0xA,{8687,8687}},{0,0,0,60,0x10,0,0x2,0x3E8,0xA,{5781,8688}},{12,12,0,60,0x10,-128,0x1,0x3E8,0xA,{5782,8689}},{0,0,0,48,0x18,0,0x5,0x3E8,0xA,{8690,8691}},{0,0,0,60,0x28,0,0xD,0x3E8,0xA,{5785,8691}},{0,0,0,52,0x18,0,0x1,0x3E8,0xA,{8690,8691}},{0,0,0,60,0x28,0,0x3,0x3E8,0xA,{5788,8692}},{0,0,0,55,0x18,0,0x3,0x3E8,0xA,{8693,8691}},{0,0,0,60,0x28,0,0x3,0x3E8,0xA,{5790,8692}},{0,0,0,60,0x18,0,0x3,0x3E8,0xA,{8693,8691}},{0,0,0,64,0x18,0,0x5,0x3E8,0xA,{8693,8691}},{0,0,0,60,0x20,0,0x5,0x3E8,0xA,{5794,8694}},{0,0,0,67,0x18,0,0x5,0x3E8,0xA,{5786,8691}},{0,0,0,60,0x28,0,0x3,0x3E8,0xA,{5793,8691}},{0,0,0,60,0x28,0,0xB,0x3E8,0xA,{8695,8696}},{0,0,0,60,0x28,0,0xB,0x3E8,0xA,{5797,8697}},{0,0,0,60,0x28,0,0xB,0x3E8,0xA,{8698,8699}},{0,0,0,60,0x20,0,0x2,0x3E8,0xA,{5800,8700}},{0,0,0,48,0x28,0,0xA,0x3E8,0xA,{8701,8702}},{0,0,0,60,0x20,0,0xC,0x3E8,0xA,{5802,8703}},{0,0,0,60,0x20,0,0xB,0x3E8,0xA,{2382,8704}},{0,0,0,60,0x20,0,0xD,0x3E8,0xA,{5804,8703}},{0,0,0,67,0x18,0,0x3,0x3E8,0xA,{5805,8705}},{0,0,0,62,0x18,0,0xB,0x3E8,0xA,{5805,8706}},{0,0,0,67,0x18,0,0xA,0x3E8,0xA,{5806,8707}},{0,0,0,67,0x18,0,0xA,0x3E8,0xA,{5807,8708}},{0,0,0,60,0x18,0,0xA,0x3E8,0xA,{5807,8709}},{0,0,0,60,0x8,0,0xE,0x3E8,0xA,{2297,8710}},{0,0,0,55,0x8,0,0xE,0x3E8,0xA,{2297,8710}},{0,0,0,53,0x20,0,0xA,0x3E8,0xA,{5791,8711}},{0,0,0,48,0x20,0,0xA,0x3E8,0xA,{5808,8712}},{12,12,0,60,0x28,-128,0xA,0x3E8,0xA,{5798,8713}},{12,12,0,60,0x28,-128,0xA,0x3E8,0xA,{2358,8714}},{0,0,0,79,0x18,0,0xB,0x3E8,0xA,{5811,8715}},{0,0,0,79,0x18,0,0xB,0x3E8,0xA,{5812,8716}},{0,0,0,60,0x10,0,0xA,0x3E8,0xA,{5813,8717}},{0,0,0,60,0x10,0,0xA,0x3E8,0xA,{5814,8718}},{0,0,0,91,0x18,0,0x2,0x3E8,0xA,{5764,8719}},{0,0,0,60,0x18,0,0x3,0x3E8,0xA,{5815,8691}},{0,0,0,53,0x18,0,0x3,0x3E8,0xA,{5816,8691}},{0,0,0,60,0x10,0,0xA,0x3E8,0xA,{2363,8720}},{0,0,0,60,0x10,0,0xA,0x3E8,0xA,{5817,8721}},{0,0,0,79,0x8,0,0x8,0x3E8,0xA,{743,8722}},{0,0,0,79,0x8,0,0x8,0x3E8,0xA,{743,8723}},}; + +const BanksDump::Operator g_embeddedBanksOperators[] = +{ +{0x0F4F201,0x8F},{0x0F7F201,0x06},{0x0F4F201,0x4B},{0x0F7F201,00},{0x0F4F201,0x49},{0x0F6F201,00},{0x0F7F281,0x12},{0x0F7F241,00},{0x0F7F101,0x57},{0x0F7F101,0x93},{0x0F2A101,0x80},{0x0F5F216,0x0E},{0x0F8C201,0x92},{0x0F8C201,00},{0x0F4F60C,0x5C},{0x0F5F381,00},{0x0F2F307,0x97},{0x0F1F211,0x80},{0x0F45417,0x21},{0x0F4F401,00},{0x0F6F398,0x62},{0x0F6F281,00},{0x0F6F618,0x23},{0x0F7E701,00},{0x0F6F615,0x91},{0x0F6F601,00},{0x0F3D345,0x59},{0x0F3A381,0x80},{0x1F57503,0x49},{0x0F5B581,0x80},{0x014F671,0x92},{0x007F131,00},{0x058C772,0x14},{0x008C730,00},{0x018AA70,0x44},{0x0088AB1,00},{0x1239723,0x93},{0x01455B1,00},{0x1049761,0x13},{0x00455B1,0x80},{0x12A9824,0x48},{0x01A46B1,00},{0x1069161,0x13},{0x0076121,00},{0x0067121,0x13},{0x00761A1,0x89},{0x194F302,0x9C},{0x0C8F341,0x80},{0x19AF303,0x54},{0x0E7F111,00},{0x03AF123,0x5F},{0x0F8F221,00},{0x122F603,0x87},{0x0F8F321,0x80},{0x054F903,0x47},{0x03AF621,00},{0x1419123,0x4A},{0x0198421,0x05},{0x1199523,0x4A},{0x0199421,00},{0x04F2009,0xA1},{0x0F8D184,0x80},{0x0069421,0x1E},{0x0A6C3A2,00},{0x028F131,0x12},{0x018F131,00},{0x0E8F131,0x8D},{0x078F131,00},{0x0285131,0x5B},{0x0487132,00},{0x09AA101,0x8B},{0x0DFF221,0x40},{0x016A221,0x8B},{0x0DFA121,0x08},{0x0E8F431,0x8B},{0x113DD31,0x15},{0x0265621,00},{0x113DD31,0x16},{0x0066621,00},{0x11CD171,0x49},{0x00C6131,00},{0x1127121,0x4D},{0x0067223,0x80},{0x121F1F1,0x40},{0x0166FE1,00},{0x175F502,0x1A},{0x0358501,0x80},{0x175F502,0x1D},{0x0F4F301,0x80},{0x105F510,0x41},{0x0C3F211,00},{0x125B121,0x9B},{0x00872A2,0x01},{0x1037FA1,0x98},{0x1073F21,00},{0x012C1A1,0x93},{0x0054F61,00},{0x022C121,0x18},{0x015F431,0x5B},{0x0058A72,0x83},{0x03974A1,0x90},{0x0677161,00},{0x0055471,0x57},{0x0057A72,00},{0x0635490,00},{0x045A541,00},{0x0178521,0x92},{0x0098F21,0x01},{0x0177521,0x94},{0x0098F21,0x05},{0x0157621,0x94},{0x0378261,00},{0x1179E31,0x43},{0x12C6221,00},{0x06A6121,0x9B},{0x00A7F21,00},{0x01F7561,0x8A},{0x00F7422,0x06},{0x15572A1,0x86},{0x0187121,0x83},{0x03C5421,0x4D},{0x01CA621,00},{0x1029331,0x8F},{0x00B7261,00},{0x1039331,0x8E},{0x0097261,00},{0x1039331,0x91},{0x0098261,00},{0x10F9331,0x8E},{0x00F7261,00},{0x116AA21,0x4B},{0x00A8F21,00},{0x1177E31,0x90},{0x10C8B21,00},{0x1197531,0x81},{0x0196132,00},{0x0219B32,0x90},{0x0177221,00},{0x05F85E1,0x1F},{0x01A65E1,00},{0x05F88E1,0x46},{0x01F75A1,0x9C},{0x00A7521,00},{0x0588431,0x8B},{0x01A6521,00},{0x05666E1,0x4C},{0x02665A1,00},{0x0467662,0xCB},{0x03655A1,00},{0x0075762,0x99},{0x00756A1,00},{0x0077762,0x93},{0x00776A1,00},{0x203FF22,0x59},{0x00FFF21,00},{0x10FFF21,0x0E},{0x10FFF21,00},{0x0558622,0x46},{0x0186421,0x80},{0x0126621,0x45},{0x00A96A1,00},{0x12A9221,0x8B},{0x02A9122,00},{0x005DFA2,0x9E},{0x0076F61,0x40},{0x001EF20,0x1A},{0x2068F60,00},{0x029F121,0x8F},{0x009F421,0x80},{0x0945377,0xA5},{0x005A0A1,00},{0x011A861,0x1F},{0x00325B1,0x80},{0x0349161,0x17},{0x0165561,00},{0x0015471,0x5D},{0x0036A72,00},{0x0432121,0x97},{0x03542A2,00},{0x177A1A1,0x1C},{0x1473121,00},{0x0331121,0x89},{0x0254261,0x03},{0x14711A1,0x15},{0x007CF21,00},{0x0F6F83A,0xCE},{0x0028651,00},{0x1232121,0x15},{0x0134121,00},{0x0957406,0x5B},{0x072A501,00},{0x081B122,0x92},{0x026F261,0x83},{0x151F141,0x4D},{0x0F5F242,00},{0x1511161,0x94},{0x01311A3,0x80},{0x0311161,0x8C},{0x0031DA1,0x80},{0x173F3A4,0x4C},{0x0238161,00},{0x053D202,0x85},{0x1F6F207,0x03},{0x111A311,0x0C},{0x0E5A213,0x80},{0x141F611,0x06},{0x2E6F211,00},{0x032D493,0x91},{0x111EB91,00},{0x056FA04,0x4F},{0x005C201,00},{0x0207C21,0x49},{0x10C6F22,00},{0x133DD31,0x85},{0x0165621,00},{0x205DA20,0x04},{0x00B8F21,0x81},{0x0E5F105,0x6A},{0x0E5C303,0x80},{0x026EC07,0x15},{0x016F802,00},{0x0356705,0x9D},{0x005DF01,00},{0x028FA18,0x96},{0x0E5F812,00},{0x007A810,0x86},{0x003FA00,0x03},{0x247F811,0x41},{0x003F310,0x03},{0x206F101,0x8E},{0x002F310,00},{0x0001F0E,00},{0x3FF1FC0,00},{0x024F806,0x80},{0x2845603,0x88},{0x000F80E,00},{0x30434D0,0x05},{0x000F60E,00},{0x3021FC0,00},{0x0A337D5,0x95},{0x03756DA,0x40},{0x261B235,0x5C},{0x015F414,0x08},{0x3F54FD0,00},{0x001FF26,00},{0x11612E4,00},{0x0F0F300,00},{0x2C9F600,00},{0x277F810,0x44},{0x006F311,00},{0x0FFF902,0x07},{0x0FFF811,00},{0x205FC00,00},{0x017FA00,00},{0x007FF00,0x02},{0x008FF01,00},{0x00CF600,00},{0x006F600,00},{0x008F60C,00},{0x247FB12,00},{0x2477B12,0x05},{0x002F60C,00},{0x243CB12,00},{0x3029FD0,00},{0x042F80E,0x08},{0x3E4F407,0x4A},{0x030F50E,00},{0x0029FD0,0x0A},{0x3E4E40E,0x0A},{0x1E5F507,0x5D},{0x004B402,0x03},{0x0F79705,0x0A},{0x000F64E,00},{0x3029F9E,00},{0x237F811,0x45},{0x005F310,0x08},{0x303FF80,00},{0x014FF10,0x0D},{0x00CF506,0x0B},{0x008F502,00},{0x0BFFA01,00},{0x097C802,00},{0x087FA01,0x51},{0x0B7FA01,00},{0x08DFA01,0x54},{0x0B8F802,00},{0x088FA01,0x59},{0x0B6F802,00},{0x30AF901,00},{0x006FA00,00},{0x389F900,0x80},{0x06CF600,00},{0x388F803,0x80},{0x0B6F60C,0x08},{0x388F803,0x85},{0x0B6F60C,00},{0x04F760E,0x40},{0x2187700,0x08},{0x049C80E,0x40},{0x2699B03,00},{0x305ADD7,0xDC},{0x0058DC7,00},{0x304A8D7,0xDC},{0x00488C7,00},{0x306F680,00},{0x3176711,00},{0x205F580,00},{0x3164611,0x09},{0x0F40006,0x3F},{0x0F5F715,00},{0x3F40006,0x3F},{0x0F5F712,00},{0x0E76701,0x58},{0x0077502,00},{0x048F841,0x45},{0x0057542,0x08},{0x3F0E00A,0x40},{0x005FF1E,0x4E},{0x3F0E00A,0x7C},{0x002FF1E,0x52},{0x04A7A0E,0x40},{0x21B7B00,0x08},{0x1395507,0x40},{0x332F905,0x05},{0x0A5D604,0x40},{0x3F30002,0x3F},{0x08DFA01,0x4F},{0x0B5F802,00},{0x054F231,0x4B},{0x056F221,00},{0x03BF2B1,0x0E},{0x00BF361,00},{0x0E7F21C,0x6F},{0x0B8F201,0x80},{0x0E5B111,0x9C},{0x0B8F211,0x80},{0x0E7C21C,0x3A},{0x0B8F301,0x80},{0x0F5B111,0x1B},{0x0D8F211,0x80},{0x031F031,0x90},{0x037F234,0x9F},{0x451F324,0x1C},{0x497F211,00},{0x010A831,0x0A},{0x1B9D234,0x03},{0x0E6CE02,0x25},{0x0E6F401,00},{0x0E6F507,0xA1},{0x0E5F341,00},{0x0045617,0x21},{0x004F601,00},{0x055F718,0x23},{0x0D8E521,00},{0x0D6F90A,0x53},{0x0D6F784,0x80},{0x0A6F615,0x91},{0x0E6F601,00},{0x0B3D441,0x8A},{0x0B4C280,0x13},{0x082D345,0x59},{0x0E3A381,0x80},{0x1557403,0x49},{0x005B381,0x80},{0x02FA2A0,0x85},{0x02FA522,0x9E},{0x02FA5A2,0x83},{0x02FA128,0x95},{0x02A91A0,0x85},{0x03AC821,0x0B},{0x03AC620,0x81},{0x05AF621,0x80},{0x12AA6E3,0x56},{0x00AAF61,0x83},{0x00AAFE1,0x91},{0x00AAF62,0x83},{0x10BF024,0x12},{0x20B5030,00},{0x71A7223,0xAC},{0x02A7221,0x83},{0x41A6223,0x22},{0x02A62A1,00},{0x006FF25,0xA1},{0x005FF23,0x2F},{0x405FFA1,0x1F},{0x0096F22,0x80},{0x11A6223,0x19},{0x02A7221,0x80},{0x41A6223,0x1E},{0x02A7222,0x83},{0x074F302,0x9C},{0x0B8F341,0x80},{0x274D302,0xA5},{0x0B8D382,0x40},{0x2F6F234,0x5B},{0x0F7F231,0x9E},{0x0F7F223,0xAB},{0x0FAF322,0x53},{0x0FAF223,0x66},{0x0FAC221,0xA7},{0x0F7C221,00},{0x022FA02,0x4C},{0x0F3F301,0x97},{0x1F3C204,0x9D},{0x0F7C111,00},{0x0AFC711,0x87},{0x0F8F501,00},{0x098C301,0x18},{0x0F8C302,00},{0x0F2B913,0x0D},{0x0119102,0x1A},{0x74A9221,0x8F},{0x103FF80,0x01},{0x3FFF021,00},{0x0F8D104,0x80},{0x2F8F802,0x87},{0x0F8F602,00},{0x015A701,0x4D},{0x0C8A301,00},{0x0317101,0x93},{0x0C87301,00},{0x0E5F111,0xA8},{0x0E5F312,0x57},{0x0E5E111,0x97},{0x0E6E111,00},{0x0C7F001,0xB3},{0x027F101,0x16},{0x028F101,00},{0x00C0300,0x30},{0x024FA20,0x03},{0x024F820,0x12},{0x056F510,00},{0x0EBF431,0x8B},{0x07AF131,00},{0x1C8F621,0x1C},{0x0C8F101,0x1F},{0x0425401,0x12},{0x0C8F201,00},{0x0035131,0x1C},{0x0675461,0x80},{0x21351A0,0x98},{0x2275360,0x01},{0x513DD31,0x95},{0x1038D13,0x95},{0x0866605,0x8C},{0x243CC70,0x92},{0x21774A0,0x03},{0x007BF21,0x95},{0x1076F21,00},{0x515C261,0x97},{0x0056FA1,00},{0x08FB563,0x13},{0x08FB5A5,0x94},{0x0848523,0xA7},{0x0748212,0xA4},{0x0748202,0x27},{0x0358511,00},{0x0338411,00},{0x005F511,0x01},{0x0C3F212,0x1E},{0x2036130,0x98},{0x21764A0,0x03},{0x1176561,0x92},{0x0176521,00},{0x2234130,0x98},{0x2174460,0x01},{0x0087262,0x01},{0x001D3E1,0xCA},{0x0396262,0x83},{0x2197320,0x22},{0x0297563,0x02},{0x2686500,00},{0x613C500,00},{0x606C800,00},{0x3077400,00},{0x0157620,0x94},{0x02661B1,0xD3},{0x0266171,0x80},{0x00B5131,0x1C},{0x13BB261,00},{0x0265121,0x18},{0x007F021,00},{0x0257221,0x16},{0x00A7F21,0x05},{0x0357A21,0x1D},{0x03A7A21,0x09},{0x035C221,0x16},{0x00ACF61,0x09},{0x04574A1,0x8A},{0x0087F21,00},{0x01A52A1,0x97},{0x01B8F61,00},{0x01A7521,0xA1},{0x01B8F21,00},{0x20F9331,0x96},{0x00F72A1,00},{0x0078521,0x96},{0x1278431,00},{0x00972A1,00},{0x006C524,0xA1},{0x1276431,00},{0x10693B1,0x8E},{0x0067271,00},{0x0088521,0x5D},{0x02884B1,00},{0x10F9331,0x93},{0x00F7272,00},{0x0068522,0x61},{0x01684B1,00},{0x02AA961,0xA3},{0x036A863,0x52},{0x016AA61,0x94},{0x00A8F61,0x80},{0x0297721,0x21},{0x1267A33,0x55},{0x0167AA1,0x93},{0x0197A22,00},{0x1077B21,0x2B},{0x0007F22,0x57},{0x0197531,0x51},{0x0196172,00},{0x0177221,0x13},{0x029C9A4,0xA2},{0x0086F21,0x80},{0x015CAA2,0xAA},{0x0086F21,00},{0x0AA7724,0x5B},{0x0173431,00},{0x0C676A1,0x0D},{0x0868726,0x59},{0x0566622,0x56},{0x0019F26,00},{0x0487664,0x25},{0x0465622,0xCB},{0x03645A1,00},{0x11467E1,0x67},{0x0175461,00},{0x1146721,0x6D},{0x0164421,00},{0x00F4032,0xDF},{0x0097021,00},{0x00FFF21,0x35},{0x00FFF21,0xB7},{0x00FFF21,0xB9},{0x60FFF21,0x80},{0x00FFF21,0x36},{0x00FFF21,0x1B},{0x00FFF21,0x1D},{0x409CF61,00},{0x0658722,0x46},{0x4F2B912,0x0D},{0x0119101,0x1A},{0x12A9221,0x99},{0x0157D61,0x40},{0x01572B1,0xA3},{0x005DFA2,0x5D},{0x0077F61,0x40},{0x001FF20,0x36},{0x4068F61,00},{0x00FFF21,0x27},{0x4078F61,00},{0x1035317,0x1A},{0x004F608,0x0D},{0x03241A1,0x9D},{0x0156161,00},{0x031A181,0xA1},{0x0032571,00},{0x0141161,0x17},{0x445C361,0x14},{0x025C361,00},{0x021542A,0x80},{0x0136A27,0xA6},{0x0015431,0x5D},{0x02542A1,0x03},{0x1471161,0x15},{0x1B1F2DE,0x57},{0x0B281D1,0x0A},{0x2322121,0x8C},{0x0133220,0x97},{0x1031121,0x0E},{0x0133121,00},{0x0F6F358,0x62},{0x0F6F241,00},{0x00F5F00,00},{0x005FF00,00},{0x03111A1,0x8C},{0x0031D61,0x80},{0x173F364,0x4C},{0x02381A1,00},{0x053F301,0x46},{0x1F6F101,0x80},{0x053F201,0x43},{0x0F6F208,0x40},{0x135A511,0x10},{0x133A517,0xA4},{0x141F611,0x0D},{0x2E5F211,00},{0x0F8F755,0x92},{0x1E4F752,0x9F},{0x0E4F341,0x13},{0x1E5F351,00},{0x111EB11,00},{0x032D453,0x91},{0x112EB13,0x0D},{0x3E5F720,00},{0x0E5F521,0x0C},{0x0207C21,0x09},{0x10C6F22,0x09},{0x133DD02,0x83},{0x0166601,0x80},{0x0298961,0x33},{0x406D8A3,0xA4},{0x005DA21,0x17},{0x00B8F22,0x80},{0x026EC08,0x15},{0x016F804,00},{0x024682C,0xAB},{0x035DF01,00},{0x0A3FD07,0xC0},{0x078F902,00},{0x055FC14,0x8D},{0x005F311,00},{0x455F811,0x86},{0x0E5F410,00},{0x155F311,0x9C},{0x0001E0E,00},{0x3FE1800,00},{0x05C5F0E,00},{0x16C870E,0x02},{0x0F0F00E,00},{0x0841300,00},{0x0F0F000,0x2E},{0x0F05F0C,00},{0x061F217,0x4F},{0x0B4F112,0x0A},{0x001EFEE,0x01},{0x0069FE0,0x04},{0x71612E4,00},{0x0F10001,0x3F},{0x059F200,00},{0x000F701,00},{0x0F0F301,00},{0x6C9F601,00},{0x029A100,0x02},{0x0696521,0x08},{0x29BF300,0x0C},{0x008F311,00},{0x068FAC0,0x02},{0x377F701,00},{0x0C4FA01,0x03},{0x33FF600,00},{0x0FFF832,0x44},{0x07FF511,00},{0x27AFB12,0x40},{0x047F611,00},{0x144F5C6,0x5C},{0x018F6C1,0x83},{0x0D0CCC0,0x10},{0x028EAC1,00},{0x2B7F811,0x46},{0x2BAE610,0x04},{0x005EA10,00},{0x0F70700,0xFF},{0x0F70710,0xFF},{0x218F401,00},{0x008F800,00},{0x0F0F009,0x0E},{0x0F7B700,00},{0x0FEF812,0x47},{0x07ED511,00},{0x005F010,0x25},{0x004D011,0x80},{0x00F9F30,00},{0x0FAE83A,00},{0x0976800,00},{0x3987802,00},{0x0FBF116,0x08},{0x069F911,0x02},{0x06CF800,00},{0x04AE80E,0x40},{0x0F2FA25,0x1B},{0x09AF612,00},{0x2F5F5C5,0x08},{0x005C301,0x06},{0x257F900,00},{0x046FB00,00},{0x0FEF512,0x11},{0x0FFF612,0xA2},{0x0FFF901,0x0F},{0x0F0F01E,00},{0x0B6F70E,00},{0x204FF82,00},{0x015FF10,0x06},{0x04CA800,0x0B},{0x13FD600,00},{0x25E980C,00},{0x306FB0F,00},{0x25E780C,00},{0x32B8A0A,0x80},{0x201C700,0x45},{0x233F90B,00},{0x04FF82E,0x07},{0x3EFF521,0x0B},{0x065F981,00},{0x030F241,00},{0x000FE46,00},{0x055F585,00},{0x0009429,0x10},{0x344F904,0x04},{0x282B2A4,00},{0x1D49703,0x80},{0x000F68E,00},{0x3029F5E,00},{0x152FE09,0xC0},{0x008F002,00},{0x055F201,00},{0x000F441,00},{0x000F301,00},{0x0A4F48F,00},{0x100FF80,00},{0x1F7F500,00},{0x05EFD2E,0x07},{0x3EFF527,0x0C},{0x256FB00,00},{0x026FA00,00},{0x256FB00,0x80},{0x017F700,00},{0x1779A01,00},{0x084F700,00},{0x367FD01,00},{0x098F601,00},{0x001FF0E,00},{0x377790E,0x02},{0x2079F20,0x1C},{0x22B950E,00},{0x2079F20,0x1E},{0x23B940E,00},{0x506F680,00},{0x016F610,00},{0x50F6F00,00},{0x50F4F00,00},{0x0FFEE03,0x40},{0x0FFE808,00},{0x060F2C5,0x4F},{0x07AF4D4,0x80},{0x160F285,0x4F},{0x0B7F294,0x80},{0x04F760F,0x40},{0x0A6D604,0x40},{0x332F805,0x05},{0x0A67404,0x40},{0x0F0F126,0x44},{0x0F5F527,0x40},{0x3948F03,00},{0x06FFA15,00},{0x0F0F007,00},{0x0DC5C00,00},{0x00FFF7E,00},{0x00F3F6E,00},{0x0B3FA00,00},{0x005D000,00},{0x0FFF832,0x84},{0x0089FD4,0xC0},{0x2F4F50E,00},{0x424120CA,0x51},{0x283E0C4,0x81},{0x14588C0,00},{0x0B0900E,0x03},{0x0BF990E,0x03},{0x0DFDCC2,0x17},{0x026C9C0,00},{0x0D0ACC0,0x18},{0x0A7CDC2,0x2B},{0x028EAC1,0x02},{0x0FE6227,00},{0x3D9950A,0x07},{0x1199523,0x48},{0x0198421,00},{0x055F231,0x49},{0x076F221,00},{0x038F101,0x57},{0x028F121,00},{0x038F101,0x93},{0x001A221,0x80},{0x0D5F136,0x0E},{0x0A8C201,0x92},{0x058C201,00},{0x054F60C,0x5C},{0x0B5F381,00},{0x032F607,0x97},{0x011F511,0x80},{0x0E6F318,0x62},{0x0F3F321,0x80},{0x09AA101,0x89},{0x0187121,0x0D},{0x0F00010,0x3F},{0x0F1F02E,00},{0x3487407,0x07},{0x0FE5229,00},{0x3D9850E,0x07},{0x0FDF800,0x0B},{0x0C7F601,00},{0x000FF26,00},{0x0A7F802,0x02},{0x01FFA06,0x0A},{0x0F5F511,00},{0x0F1F52E,0x05},{0x3F99906,0x02},{0x0F89227,00},{0x3D8750A,0x03},{0x2009F2C,00},{0x3A4C50E,0x09},{0x344F904,0x0C},{0x0F1F52E,0x09},{0x3F78706,0x02},{0x2F1F535,0x19},{0x028F703,0x02},{0x0FAFA25,0xCD},{0x0F99803,00},{0x1FAF825,0x1B},{0x0F7A803,00},{0x1FAF825,0x21},{0x0F69603,00},{0x2F5F504,0x16},{0x236F603,0x03},{0x091F015,0x1E},{0x0E8A617,0x04},{0x001FF0E,0x06},{0x077780E,0x04},{0x0F7F020,00},{0x33B8809,00},{0x0F7F420,0x03},{0x33B560A,00},{0x05BF714,0x4B},{0x089F712,00},{0x0F2FA27,0x22},{0x1F75020,0x09},{0x03B7708,0x05},{0x1077F26,0x29},{0x06B7703,0x05},{0x0FCF727,0x40},{0x0F3F821,0x1C},{0x0ADC620,00},{0x0FFFF01,0x3F},{0x4FFEE03,0x80},{0x0FFE804,00},{0x0F8F3A1,0x80},{0x005FA00,0x03},{0x053F131,0x48},{0x227F232,00},{0x01A9161,0x40},{0x01AC1E6,0x03},{0x071FB11,00},{0x0B9F301,00},{0x1B57231,0x0B},{0x098D523,00},{0x024D501,0x0F},{0x0228511,00},{0x025F911,0x05},{0x034F131,00},{0x01576A1,0x94},{0x1362261,0x10},{0x0084F22,0x40},{0x2363360,0x15},{0x007F804,0x0E},{0x0748201,0x05},{0x0E5F131,0x89},{0x174F131,00},{0x0E3F131,0x8A},{0x073F172,00},{0x0FFF101,0x0D},{0x0FF5091,0x80},{0x1473161,0x0F},{0x007AF61,00},{0x0D3B303,0x40},{0x024F204,0x80},{0x1037531,0x1A},{0x0445462,0x40},{0x021A1A1,0x92},{0x116C261,0x40},{0x0F0F240,00},{0x0F4F440,00},{0x003F1C0,0x4F},{0x001107E,0x0C},{0x0459BC0,0x05},{0x015F9C1,00},{0x0064F50,0x10},{0x003FF50,00},{0x2F0F005,0x08},{0x1B4F600,00},{0x0F2F931,0x40},{0x042F210,00},{0x00F2F6E,00},{0x2F95401,0x19},{0x2FB5401,00},{0x0665F53,0x05},{0x0077F00,00},{0x006707E,0x03},{0x1111EF0,00},{0x11411E2,0xC0},{0x0F0A006,00},{0x075C584,00},{0x1F5F213,0xC6},{0x0F5F111,0x05},{0x153F101,0x49},{0x274F111,0x02},{0x0E4F4D0,0x80},{0x006A29E,00},{0x0871321,0xCD},{0x0084221,0x80},{0x065B400,00},{0x075B400,00},{0x02AF800,0x03},{0x145F600,00},{0x0FFF830,0x44},{0x0F9F900,0x08},{0x023F110,00},{0x0F9F900,0x04},{0x026F180,00},{0x1FDF800,0xC4},{0x059F800,00},{0x06FFA00,0x0B},{0x08FF600,00},{0x023F191,00},{0x0BFDA02,00},{0x2F0FB01,0x10},{0x096F701,00},{0x002FF04,00},{0x007FF00,00},{0x0F0F006,00},{0x0B7F600,00},{0x034C4C4,0x03},{0x0F0F019,0x0E},{0x0F7B720,0x0A},{0x0B4F600,00},{0x0B6F800,00},{0x008F210,00},{0x0BFDA09,0x08},{0x210BA2E,0x0E},{0x2F4B40E,00},{0x210FA2E,0x0E},{0x2F4F40E,00},{0x2A2B2A4,0x02},{0x200FF04,00},{0x206FFC3,00},{0x2F5F6C3,00},{0x0E1C000,0x80},{0x153951E,0x80},{0x200FF03,00},{0x3F6F6C4,00},{0x202FF4E,00},{0x3F7F701,00},{0x3F6F601,00},{0x2588A51,00},{0x018A452,00},{0x0FFFB13,0x40},{0x0FFEE05,0x55},{0x0FF0006,0x3F},{0x0FDF715,0x0D},{0x0F6F80E,00},{0x060F207,0x4F},{0x072F212,0x09},{0x074F212,0x08},{0x022FB18,0x88},{0x012F425,0x80},{0x0F0FF04,00},{0x0B5F4C1,00},{0x02FC811,0x2D},{0x0F5F531,00},{0x03D6709,00},{0x3FC692C,00},{0x053D144,0x80},{0x05642B2,0x15},{0x253B1C4,0x8F},{0x083B1D2,0x84},{0x175F5C2,0x21},{0x074F2D1,0x83},{0x1F6FB34,0x83},{0x04394B1,00},{0x0BDF211,0x46},{0x09BA004,0x40},{0x144F221,0x8A},{0x3457122,0x40},{0x1447122,0x40},{0x053F101,0x40},{0x153F108,0x40},{0x102FF00,0x08},{0x3FFF200,00},{0x345A122,0x40},{0x028F131,0x0F},{0x018F031,00},{0x307D7E1,0x8D},{0x107B6E0,00},{0x03DD500,0x11},{0x02CD500,00},{0x1199563,0x46},{0x219C420,00},{0x044D08C,0xA1},{0x2F4D181,0x80},{0x0022171,0x93},{0x1035231,0x80},{0x1611161,0x91},{0x01311A2,0x80},{0x25666E1,0x4C},{0x038FB00,00},{0x0DAF400,00},{0x2BFFB15,0x0A},{0x31FF817,00},{0x0BFFBC6,00},{0x02FE8C9,00},{0x2F0F006,00},{0x2B7F800,00},{0x040E000,00},{0x00FFF2E,0x0A},{0x04AF602,0x1B},{0x3A5F0EE,00},{0x36786CE,00},{0x0B0FCD6,00},{0x008BDD6,0x05},{0x0F0F007,0x08},{0x0E7F301,0x58},{0x078F211,00},{0x0EFF230,0x1E},{0x078F521,00},{0x019D530,0x88},{0x01B6171,0x80},{0x001F201,0x0D},{0x0B7F211,0x0D},{0x03DD500,0x14},{0x010E032,0x87},{0x0337D16,0x84},{0x0F8F161,0x80},{0x008F062,0x80},{0x0745391,00},{0x0755451,00},{0x08E6121,0x15},{0x09E7231,00},{0x0BC7321,0x19},{0x0BC8121,00},{0x23C7320,0x19},{0x209A060,0x02},{0x20FF014,0x80},{0x064F207,0x73},{0x075F612,00},{0x054D221,0x4D},{0x075B231,0x80},{0x053D221,0x56},{0x073B231,0x80},{0x053D221,0x55},{0x201AF70,0x19},{0x0084F32,0x40},{0x0083F32,0x40},{0x0302221,0x99},{0x0064F32,00},{0x0006F71,0x99},{0x0074F32,0x80},{0x0006F71,0x9E},{0x0054F32,0x80},{0x0054F31,0x80},{0x0006F71,0x9C},{0x006F231,0xCF},{0x0811321,0xCD},{0x0074221,0x80},{0x074A401,00},{0x116C261,00},{0x078F522,00},{0x01FF003,0x5B},{0x01FF001,0x80},{0x00FFF24,0x80},{0x00FFF21,0x80},{0x00F4021,00},{0x10F1020,00},{0x045F221,0x8F},{0x076F221,0x06},{0x053B121,0x4F},{0x074C231,00},{0x011F111,0x4A},{0x0B3C101,0x80},{0x058F381,0x63},{0x058F201,0x80},{0x001F701,0x0D},{0x0B7F407,0x06},{0x060F206,0x4F},{0x072F211,0x0C},{0x0E3F318,0x62},{0x093F281,00},{0x326CE15,0x57},{0x025F901,00},{0x1558403,0x49},{0x005D381,0x80},{0x0F0FB3E,0x29},{0x09BA071,0x40},{0x014F001,0x88},{0x14941A1,0x15},{0x009CF21,00},{0x074A302,0x9A},{0x075C441,0x80},{0x01FF260,0x11},{0x07CF521,00},{0x0F4F321,0x80},{0x0442009,0xA1},{0x0F4D184,0x80},{0x066C101,0x9A},{0x066A201,0x40},{0x0236321,0x97},{0x0266421,00},{0x111C031,0x20},{0x1157221,0x06},{0x1107421,0x0C},{0x0165223,0x08},{0x1DBB851,0x17},{0x1567591,00},{0x075C502,0x29},{0x0F3C201,0x83},{0x0EFE800,0x0D},{0x0FFA401,00},{0x01171B1,0x8B},{0x1177261,0x40},{0x111F0F1,0x95},{0x1131121,00},{0x1159221,0x06},{0x111C0B1,0x20},{0x1159221,0x08},{0x00B4131,0x1C},{0x03B9261,0x80},{0x01F4131,0x1C},{0x0646300,0x1C},{0x0757211,00},{0x0014131,0x1C},{0x05A5321,0x9C},{0x01AAA21,0x80},{0x003F200,0x80},{0x0FFF220,00},{0x3F01FC0,00},{0x179A1A1,0x1C},{0x1495121,00},{0x0177EB1,0xC5},{0x00E7B22,0x05},{0x019D531,0xD1},{0x01B6132,0x80},{0x01B5132,0x9A},{0x03BA261,0x82},{0x1047021,0xC6},{0x06D6361,00},{0x08F6EE0,0xEC},{0x02A6561,00},{0x0297122,0x08},{0x0296431,0x04},{0x20FF2D0,0xEB},{0x08562C1,0x06},{0x0154221,0xE3},{0x0065021,00},{0x0439422,0x40},{0x05312C4,0x17},{0x07212F1,00},{0x0536244,0x56},{0x0046041,00},{0x0E6E800,0x0D},{0x0F6A300,00},{0x141FA11,0x06},{0x2F5F411,00},{0x0268721,0x07},{0x1188421,00},{0x055F502,0x99},{0x053F601,0x80},{0x072F212,00},{0x0105AEC,00},{0x1F454EE,00},{0x286F2A5,00},{0x228670E,00},{0x007FF01,00},{0x007FF21,00},{0x00CFF01,00},{0x00BFF21,00},{0x211BA12,0x0B},{0x2F5B400,00},{0x021FF13,0x51},{0x003FF10,0x40},{0x002F002,0xC0},{0x004D001,00},{0x050F101,0x4F},{0x07CD201,0x04},{0x2129A14,0x97},{0x004FA01,0x80},{0x0038165,0xD2},{0x007F171,00},{0x0AE7121,0x1C},{0x01ED320,00},{0x053F101,0xCF},{0x083F212,00},{0x154FF0A,0x04},{0x0F5F002,00},{0x035F813,0x12},{0x004FF11,00},{0x100FF22,0x92},{0x10BF020,00},{0x00FFF24,00},{0x00FFF21,0x40},{0x09BA071,00},{0x275F602,0x9B},{0x066F521,00},{0x315EF11,0x53},{0x0B5F481,00},{0x10BF224,0x50},{0x00B5231,00},{0x000EA36,0x8B},{0x003D01A,00},{0x1C3C223,0x14},{0x103D000,00},{0x001F211,0x0D},{0x0B1F215,0x0D},{0x0AFF832,0x45},{0x07FF310,00},{0x274F111,00},{0x0F7F000,0x30},{0x00687A1,00},{0x0009F71,0x45},{0x1069F62,00},{0x0009F71,0x51},{0x1069062,00},{0x275F602,0x1B},{0x0F7F001,00},{0x141B403,0x5E},{0x03FF311,00},{0x141B203,0x5E},{0x097F211,00},{0x101F901,0x34},{0x0F5F001,00},{0x0EFF201,0x1D},{0x078F501,00},{0x1EFF201,0x1D},{0x01774E1,0x83},{0x01765E2,00},{0x154F103,00},{0x054F10A,00},{0x001EF8F,0x81},{0x0F19801,00},{0x024DF1A,00},{0x076F212,00},{0x2298432,0x1A},{0x0448421,00},{0x0176EB1,0xC5},{0x00E8B22,0x05},{0x01572A1,0x1C},{0x02784A1,00},{0x0427887,0x4D},{0x0548594,00},{0x0B3F101,0x85},{0x0115172,0x89},{0x11552A2,00},{0x2F3F021,0x4F},{0x004F021,00},{0x095AB0E,0xC0},{0x0C6F702,00},{0x00351B2,0x1C},{0x01352A2,0x05},{0x01152B0,0xC5},{0x0FE31B1,0x40},{0x0B69401,00},{0x0268300,00},{0x075F502,0x29},{0x0F3F201,0x83},{0x243A321,0x11},{0x022C411,00},{0x01FF201,0x11},{0x088F501,00},{0x07D8207,0x8F},{0x07D8214,0x80},{0x00BF224,0x4F},{0x025DC03,0x90},{0x009F031,00},{0x02F2501,0x15},{0x06C6521,0x80},{0x0176E30,0x4B},{0x12F8B32,0x05},{0x08F7461,0x27},{0x0EBFA10,00},{0x0DAFA0E,00},{0x0F7F0F5,0x2E},{0x0068771,00},{0x0537101,0x4F},{0x07C5212,00},{0x3DFFF20,00},{0x20FFF21,00},{0x000FF24,0x97},{0x00BF020,00},{0x00E8BA2,0x05},{0x019D530,0xCD},{0x01B6171,0x40},{0x203B122,0x4F},{0x005F172,00},{0x0F16000,0x1D},{0x0F87001,00},{0x1009F71,0x45},{0x1069F22,00},{0x01D5321,0x1C},{0x03B5261,0x80},{0x01F41B1,0x1C},{0x05A5321,0x9F},{0x0078061,0x80},{0x0077062,00},{0x2D3B121,0x4F},{0x0149121,0x80},{0x1F27021,0x14},{0x0F68021,00},{0x2129A16,0x97},{0x0039A12,00},{0x01FF003,0x1F},{0x019F000,0x05},{0x204D983,0x17},{0x004D081,00},{0x025DA05,0x8E},{0x015F901,00},{0x112AA83,0x1C},{0x1119B91,00},{0x001FF64,0xDB},{0x0F3F53E,0xC0},{0x0AC9051,0x1A},{0x1F4F071,00},{0x22F5570,0x16},{0x31E87E0,0x80},{0x08F6EA0,0xEC},{0x02A65E1,00},{0x0FFA500,00},{0x102FD16,0x96},{0x0039F12,0x80},{0x035F803,0x12},{0x004FF01,00},{0x006FA15,0xD3},{0x025F501,00},{0x0019A12,00},{0x0F0E029,0x1A},{0x031FF1E,00},{0x0056581,0x83},{0x0743251,00},{0x2129FD6,0x17},{0x0F290D2,00},{0x0048C2C,00},{0x0111E00,00},{0x0A11220,00},{0x0F00000,0x3F},{0x0FFF000,00},{0x02FF607,00},{0x04FD600,00},{0x069F000,00},{0x0FFF633,00},{0x0F0F31E,00},{0x0F6F610,00},{0x00FFF2E,00},{0x04CF600,0x18},{0x3F6F01E,00},{0x307F01E,00},{0x30AFF2E,00},{0x306FF1E,00},{0x06F9A02,00},{0x007A006,00},{0x26EF800,0x08},{0x03FF600,0x02},{0x16FAA12,00},{0x006FF06,00},{0x0FFB000,00},{0x02F8607,00},{0x050B233,0x5A},{0x1F5B131,00},{0x153F231,0x49},{0x0F5F111,0x03},{0x183D131,0x95},{0x0F5C132,0x03},{0x163F334,0x9B},{0x1F59211,00},{0x2B7F827,0x28},{0x0F9F191,00},{0x1EEF31A,0x2D},{0x0F5F111,00},{0x158F235,0x95},{0x1F68132,0x02},{0x040C931,0x85},{0x1B9C235,00},{0x064C709,0x15},{0x035B201,0x05},{0x144F406,0x03},{0x034F201,0x1B},{0x124A904,0x06},{0x074F501,0x01},{0x033F6D4,00},{0x0E361F1,00},{0x0E8F7D4,00},{0x064A4D1,00},{0x0F7F736,0x16},{0x0F5B531,0x07},{0x043A203,0x1B},{0x074F300,00},{0x135F8C3,0x8E},{0x194C311,00},{0x11BF4E2,0x07},{0x10DF4E0,00},{0x02CF6F2,00},{0x10BF5F0,00},{0x015B6F1,0x06},{0x007BFF0,00},{0x1167922,0x03},{0x1086DE0,00},{0x0066331,0x27},{0x1175172,00},{0x11653B4,0x1D},{0x1175171,00},{0x0159725,0x29},{0x1085332,00},{0x0156724,0x9E},{0x1065331,00},{0x1B4A313,0x27},{0x0F8D231,00},{0x032F317,0xA3},{0x1C7E211,00},{0x1C1D233,0x24},{0x09CF131,00},{0x044F831,0x05},{0x1C9F232,0x02},{0x07B9C21,0x09},{0x0FB9502,0x03},{0x1988121,0x84},{0x059A121,0x04},{0x04378B1,0x0C},{0x3FC9122,0x03},{0x08C8200,0x0A},{0x0ECB408,0x02},{0x046AB21,0x13},{0x0F79321,00},{0x032F901,0x0A},{0x058C122,0x04},{0x077FA21,0x07},{0x06AC322,0x02},{0x0577121,0x17},{0x0876221,00},{0x178FA25,0x01},{0x097F312,00},{0x088FA21,0x03},{0x097B313,00},{0x17FF521,0x09},{0x0CCF323,0x04},{0x09BA301,0x10},{0x0AA9301,00},{0x129F6E2,0x19},{0x10878E1,00},{0x129F6E2,0x1C},{0x1166961,0x19},{0x1275461,00},{0x1318271,0x18},{0x0566132,00},{0x10670E2,0x23},{0x11675E1,00},{0x0E68802,00},{0x1F6F561,00},{0x1D5F612,0x20},{0x0E3F311,0x80},{0x1F4F461,0x0E},{0x0F5B500,00},{0x1049C61,0x1E},{0x0167121,0x80},{0x2D6C0A2,0x2A},{0x1553021,00},{0x1357261,0x21},{0x1366261,00},{0x1237221,0x1A},{0x0075121,0x02},{0x03197E1,0x16},{0x0396261,00},{0x0457922,0xC3},{0x0276621,00},{0x1556321,0xDE},{0x0467321,00},{0x0F78642,0x05},{0x1767450,00},{0x0026131,0x1C},{0x0389261,0x81},{0x0235271,0x1E},{0x0197161,0x02},{0x0167621,0x1A},{0x0098121,0x01},{0x22C8925,0x28},{0x24B8320,00},{0x0167921,0x1F},{0x05971A2,0x05},{0x0168721,0x19},{0x0398221,0x03},{0x0357521,0x17},{0x0178422,0x82},{0x0586221,0x22},{0x0167221,0x02},{0x10759B1,0x1B},{0x00A7BA1,00},{0x0049F21,0x16},{0x10C8521,00},{0x020A821,0x0F},{0x10A7B23,00},{0x0048821,0x0F},{0x1187926,00},{0x0058F31,0x18},{0x0087332,0x01},{0x1378CA1,0x0A},{0x00A7724,0x04},{0x067A831,0x04},{0x0195175,00},{0x12677A2,0x1F},{0x0097421,0x01},{0x194B8E1,0x07},{0x0286321,0x01},{0x05987A1,0x93},{0x00A65E1,00},{0x0389F22,0x10},{0x0296761,00},{0x19A88E2,0x0D},{0x0096721,00},{0x09498A2,0x10},{0x0286A21,0x01},{0x02686F1,0x1C},{0x02755F1,00},{0x0099FE1,0x3F},{0x0086FE1,00},{0x019F7E2,0x3B},{0x0077A21,00},{0x00C9222,0x1E},{0x00DA261,0x06},{0x122F421,0x15},{0x05FA321,00},{0x16647F2,0x20},{0x02742F1,00},{0x0288861,0x19},{0x049B261,0x05},{0x01B8221,0x16},{0x179B223,00},{0x093CA21,00},{0x01A7A22,00},{0x1C99223,00},{0x1288222,00},{0x07BF321,0x1D},{0x05FC322,0x02},{0x12581E1,00},{0x195C4A6,0x86},{0x0013121,0x27},{0x0154421,00},{0x2358360,0x14},{0x006D161,00},{0x101D3E1,0x5C},{0x0378262,00},{0x2863428,0x38},{0x0354121,00},{0x1F35224,0x12},{0x1F53223,0x02},{0x0A66261,0x1D},{0x02661A1,00},{0x1D52222,0x0F},{0x1053F21,0x84},{0x024F9E3,0x1F},{0x0F6D131,0x01},{0x1554163,00},{0x10541A2,00},{0x165A7C7,0x25},{0x0E4F3C1,0x05},{0x1B7F7E3,0x19},{0x1F59261,00},{0x044A866,0x9B},{0x1E4F241,0x04},{0x0752261,0x20},{0x0254561,00},{0x084F6E1,0x21},{0x036A3E1,0x01},{0x16473E2,0x14},{0x10598E1,0x01},{0x0347221,0x0B},{0x1F6A324,0x02},{0x053F421,0x16},{0x0F8F604,00},{0x002DA21,0x18},{0x0F5F335,00},{0x063FA25,0x0F},{0x1E59402,00},{0x096F932,0x07},{0x0448411,00},{0x2189720,0x0E},{0x1188325,0x03},{0x029F661,0x18},{0x1087862,0x01},{0x01976E6,0x21},{0x1088E61,0x03},{0x0D4F027,0x23},{0x046F205,0x09},{0x131F91C,0x0C},{0x1E89615,00},{0x2167502,00},{0x1F6F601,00},{0x093F502,0x1D},{0x045C600,00},{0x032F511,0x15},{0x0B4F410,00},{0x099FA22,0x06},{0x025D501,00},{0x200FF2E,00},{0x02D210E,0x0E},{0x1E45630,0x0B},{0x2875517,00},{0x003FF24,00},{0x1879805,0x08},{0x200F00E,00},{0x304170A,0x04},{0x0F7F620,0x08},{0x2F9770E,0x05},{0x008F120,0x14},{0x008F42E,0x02},{0x100F220,0x04},{0x1053623,00},{0x002FF2E,00},{0x355322A,0x05},{0x00F9F3E,00},{0x0FA8730,00},{0x0977801,00},{0x3988802,00},{0x069F911,00},{0x04AE80E,0x80},{0x0F3F900,00},{0x08AF701,00},{0x0FDFA01,0x07},{0x047F601,00},{0x000FF24,00},{0x0A9F702,00},{0x0FEF22C,00},{0x3D8B802,0x06},{0x0F6822E,00},{0x3F87404,0x10},{0x3D4C50E,0x05},{0x3F78706,0x03},{0x1A1F737,0x14},{0x028F603,00},{0x0FAFA25,0xC4},{0x0F99903,00},{0x1FAFB21,0x03},{0x0F7A802,00},{0x2FAF924,0x18},{0x0F6A603,00},{0x2F5F505,0x14},{0x236F603,00},{0x107AF20,0x15},{0x22BA50E,00},{0x107BF20,0x18},{0x23B930E,00},{0x33B8908,0x01},{0x0FAF320,00},{0x22B5308,0x0A},{0x19AF815,0x21},{0x089F613,00},{0x0075F20,0x01},{0x14B8708,00},{0x1F75725,0x12},{0x1677803,00},{0x0F0F122,0x2F},{0x0FCF827,0x02},{0x0E5AD37,0x40},{0x1A58211,00},{0x053F335,0xDA},{0x1F5F111,0x03},{0x163F435,0xCF},{0x1F5F211,0x03},{0x163F374,0xD3},{0x1F5F251,0x03},{0x2C9F887,0x15},{0x08EF63C,0x1B},{0x0F5F131,0x09},{0x20AFAB2,0x15},{0x1F7C231,0x05},{0x020F831,0x0F},{0x1DCF236,0x04},{0x234F825,0xA2},{0x085F401,0x07},{0x226F6C2,0x05},{0x075A501,0x05},{0x131F6F5,0x2A},{0x0E3F1F1,0x02},{0x0F8F8F8,0x1A},{0x064E4D1,0x07},{0x0F7F73C,0x0C},{0x0F5F531,0x06},{0x0F0B022,0x21},{0x0F4C425,0x08},{0x136F8C5,0x09},{0x194C311,0x06},{0x11BF4E2,0x08},{0x11DD4E0,0x04},{0x04CF7F2,0x02},{0x00BF5F0,0x04},{0x13DF4E0,0x03},{0x13BF5E0,00},{0x1166722,0x09},{0x1086DE0,0x05},{0x1175172,0x04},{0x11653B4,0x1B},{0x1175171,0x06},{0x1057824,0x1E},{0x1085333,0x09},{0x11653B3,0x1F},{0x1175172,0x05},{0x127F833,0x23},{0x0F8F231,0x04},{0x132F418,0x26},{0x1A7E211,0x03},{0x0C1A233,0x9D},{0x09CB131,0x85},{0x1F4F335,0x16},{0x1C9F232,0x07},{0x07B9C21,0x12},{0x0FB9402,0x03},{0x24C8120,0x06},{0x17AF126,0x0C},{0x28B7120,0x11},{0x378F120,0x06},{0x38C7205,0x13},{0x19CE203,0x0A},{0x0B6AF31,00},{0x0F78331,00},{0x068F321,0x17},{0x0FCC121,0x06},{0x077FB21,00},{0x06AC322,0x03},{0x047A131,0x97},{0x0878231,0x84},{0x0A8FA25,0x0D},{0x197F312,00},{0x06CFA21,0x05},{0x0FCF334,0x07},{0x17FF521,0x17},{0x0CCF322,0x03},{0x09BA301,0x13},{0x0AA9301,0x04},{0x10878E1,0x05},{0x10878E1,0x03},{0x0099861,0x20},{0x1087E61,0x03},{0x1017171,0x1E},{0x05651F1,0x06},{0x11675E1,0x04},{0x0E69802,0x05},{0x0F6F521,0x07},{0x075F602,0x2A},{0x0C5F401,0x82},{0x1BABF61,0x40},{0x0468501,00},{0x195CCE1,00},{0x12850E1,00},{0x2D6C0E2,0x27},{0x15530E1,0x09},{0x1556261,0x26},{0x1566261,0x03},{0x16372A1,0x18},{0x00751A1,0x07},{0x145B822,0xD2},{0x0278621,0x02},{0x0467321,0x05},{0x0F78642,0x0A},{0x0026131,0x1F},{0x0388261,0x87},{0x0135571,0x20},{0x0197061,0x0B},{0x0166621,0x1C},{0x0097121,0x06},{0x21C7824,0x19},{0x14B9321,0x84},{0x0167921,0x21},{0x05971A1,0x03},{0x0358221,0x1B},{0x0388221,0x07},{0x0357221,0x1A},{0x0378222,0x87},{0x0586221,0x23},{0x0167221,0x06},{0x10759F1,0x1B},{0x00A7B61,0x06},{0x10C8521,0x07},{0x010B821,0x04},{0x1DC72A6,0x04},{0x0096831,0x0B},{0x1086334,0x09},{0x1058F31,0x14},{0x00B5333,0x16},{0x1079FA1,0x1D},{0x00A7724,0x08},{0x009D531,0x1B},{0x01D6175,0x4C},{0x0076172,0x26},{0x01B6223,0x10},{0x194A8E1,0x0F},{0x0086221,0x04},{0x00986F1,0x9C},{0x00B75E1,0x0B},{0x008DF22,0x2C},{0x0297761,0x03},{0x27A88E2,0x2C},{0x0097721,00},{0x05488E2,0x17},{0x0087721,0x0B},{0x02686F1,0x1F},{0x02755F1,0x04},{0x0086FE1,0x05},{0x004A822,0xE6},{0x0096A21,0x05},{0x00C9222,0x1B},{0x00DA261,0x0A},{0x122F461,0x15},{0x05FA361,0x04},{0x10ABB21,0xD2},{0x0096FA1,0x03},{0x0387761,0x17},{0x0499261,0x09},{0x21D7120,0x08},{0x178F124,0x05},{0x193CA21,00},{0x01A7A21,0x03},{0x1C99223,0x06},{0x1089122,0x08},{0x01BF321,0x1D},{0x05FE122,0x04},{0x15562E1,0x01},{0x125FAC8,0x0B},{0x0012161,0x26},{0x01534E1,0x02},{0x0358361,0x19},{0x106D161,0x02},{0x101D3E1,0xDC},{0x0378262,0x82},{0x166446A,0x33},{0x0365161,0x04},{0x0F38262,0x0B},{0x1F53261,0x06},{0x1766261,0x9A},{0x02661A1,0x04},{0x1D52222,0x13},{0x1053F21,0x06},{0x0F4F2E1,0x9C},{0x0F69121,0x05},{0x1554163,0x0A},{0x10541A2,0x06},{0x005F604,0x18},{0x0E5F301,0x0E},{0x196F9E3,0x10},{0x1F5C261,00},{0x1C6A144,0xD2},{0x1E5B241,0x06},{0x1772261,0x94},{0x0264561,0x05},{0x184F5E1,0x19},{0x036A2E1,0x07},{0x10598E1,0x07},{0x0348321,0x0B},{0x1F6C324,0x09},{0x19AFB25,00},{0x1F7F432,0x03},{0x002DA21,0x1B},{0x0F5F335,0x04},{0x034F763,0x4E},{0x1E5F301,0x05},{0x296F931,0x0F},{0x0F6F531,0x04},{0x1176731,0x17},{0x01A7325,0x0A},{0x129F6E1,0x15},{0x20868E2,0x07},{0x019A6E6,0x23},{0x1088E61,0x05},{0x046F205,0x0C},{0x1167504,0x07},{0x1F6C601,00},{0x033F731,0x19},{0x085F510,00},{0x089FA22,0x0F},{0x025F501,0x05},{0x02D210E,0x18},{0x0F45630,00},{0x003FF20,00},{0x3967604,0x06},{0x304170A,0x13},{0x007F020,0x0C},{0x2F9920E,0x08},{0x008F42E,0x08},{0x100F220,0x09},{0x0052423,0x05},{0x325332E,0x0A},{0x0DF8120,00},{0x0DFF310,0x03},{0x1FCF720,00},{0x04AF80A,00},{0x053F600,0x0C},{0x07AF710,00},{0x0FEF227,00},{0x3D8980A,0x0C},{0x0F8F128,00},{0x3667606,0x0A},{0x050F335,0x69},{0x1F5F111,0x02},{0x2B49230,0x0F},{0x208A421,00},{0x0A7FB2C,0x16},{0x0C9F281,0x08},{0x08EA43A,0x35},{0x085A131,0x07},{0x0F7F838,0x13},{0x0F5F537,0x06},{0x061C21A,0x18},{0x072C212,0x03},{0x136F8C2,0x03},{0x194C311,0x03},{0x34FFAE1,0x07},{0x11AD4E0,0x07},{0x13DF9E3,00},{0x03BF5E0,00},{0x1F62334,0x1E},{0x1173131,0x06},{0x1F2F235,0x02},{0x084FA37,0x09},{0x1C9F232,00},{0x3CEFA21,0x03},{0x0FBF403,00},{0x2989120,0x06},{0x159B125,0x06},{0x073F9A1,0x0D},{0x3FCA120,0x04},{0x036F821,0x11},{0x0F7C123,00},{0x017F821,0x9E},{0x0FAF223,00},{0x146F821,0x0C},{0x006C322,0x07},{0x047F531,0x96},{0x087F233,0x80},{0x0B8FA21,0x04},{0x077F412,0x07},{0x08CF921,0x05},{0x0FCF334,00},{0x066F801,0x08},{0x1F6F521,0x06},{0x09BF501,0x19},{0x0AAF302,0x04},{0x124F661,0x17},{0x2065860,0x0B},{0x006F701,0x19},{0x3F6F720,0x08},{0x1F4F461,0x14},{0x104F6E1,0x23},{0x12670E1,0x05},{0x113F221,0x20},{0x0055121,0x09},{0x0388261,0x83},{0x0197061,0x06},{0x0157121,0x1C},{0x0177122,00},{0x0257521,0x21},{0x01771A1,00},{0x0358221,0x19},{0x0388221,0x03},{0x0378222,0x82},{0x0085333,0x0A},{0x01B6175,0x84},{0x0186223,0x0A},{0x00A75E1,0x05},{0x02384F1,0x1D},{0x01655F2,00},{0x2D86901,0x1B},{0x0B65701,00},{0x0C4FF22,00},{0x0077921,0x0D},{0x05FB9A2,0x0B},{0x0FB9121,0x0F},{0x072FA62,0x09},{0x198F541,00},{0x21D8120,0x08},{0x179F125,0x05},{0x1C99223,0x0C},{0x1089122,0x0E},{0x05FE121,0x0A},{0x001F1A1,0x27},{0x0153421,0x07},{0x2A2F120,0x14},{0x315F321,0x12},{0x034D2E8,0xDD},{0x1343261,0x8B},{0x053F265,0x0E},{0x1F33263,0x11},{0x0837222,0x19},{0x1055221,0x05},{0x074F161,0x22},{0x07441A1,0x06},{0x00553A1,0x25},{0x0F43221,00},{0x10541A2,0x03},{0x091F010,0x0C},{0x0E7A51E,00},{0x2B29130,0x10},{0x204A121,00},{0x0D6F662,0x22},{0x2E5B241,00},{0x104F021,0x2B},{0x0043221,0x06},{0x06473E4,0x25},{0x10548E1,0x08},{0x156FA23,00},{0x0FBF622,00},{0x28CFA21,0x13},{0x1F7F331,0x04},{0x0559131,0x0D},{0x3788133,0x02},{0x0411160,0x05},{0x14431E6,00},{0x0722121,0x0D},{0x2646129,0x0D},{0x3922220,0x84},{0x0A44125,0x82},{0x1023220,0x03},{0x3343120,00},{0x0B5F100,0x0B},{0x0C2D400,0x07},{0x300FF36,0x09},{0x2F4F41E,00},{0x0211131,0x0A},{0x0937122,0x02},{0x1728281,0x0E},{0x0743182,0x05},{0x0331221,00},{0x1243122,00},{0x0F9F700,0x08},{0x0CA8601,00},{0x1F3F030,0x54},{0x1F4F130,00},{0x0F3F030,0x52},{0x1F3E130,0x4E},{0x0F4F130,00},{0x015E811,00},{0x014F712,00},{0x153F110,0x4F},{0x0F4D110,00},{0x053F111,0x4F},{0x0F4D111,00},{0x051F121,0x66},{0x0E5D231,00},{0x0E6F130,0x51},{0x0E5F1B0,0x40},{0x079F212,0x43},{0x099F110,0x40},{0x201F230,0x87},{0x1F4C130,00},{0x162A190,0x8E},{0x1A79110,00},{0x164F228,0x4F},{0x0E4F231,00},{0x0119113,0x0E},{0x0347D14,00},{0x041F6B2,0x0F},{0x092D290,00},{0x0F3F1F0,0x02},{0x0F4F1F2,00},{0x0157980,00},{0x275F883,00},{0x093F614,0x1F},{0x053F610,00},{0x113B681,0x99},{0x013FF02,00},{0x0119130,0x47},{0x0535211,0x80},{0x016B1A0,0x88},{0x117D161,0x80},{0x105F130,00},{0x036F494,00},{0x017F2E2,0x9E},{0x107FF60,0x80},{0x117F2E0,0x9E},{0x007FFA0,0x80},{0x0043030,0x92},{0x1145431,0x80},{0x0178000,0x49},{0x1176081,0x80},{0x015A220,0x48},{0x1264131,00},{0x0158220,0x4A},{0x1264631,00},{0x03460B0,0x0C},{0x01642B2,0x80},{0x105F020,0x92},{0x2055231,00},{0x0F5F120,0x8D},{0x0F6F120,00},{0x1E4E130,0x0D},{0x0E3F230,00},{0x21FF100,0x21},{0x088F400,00},{0x132EA10,0x87},{0x2E7D210,00},{0x0F4E030,0x92},{0x0F5F230,0x80},{0x0FFF100,0x10},{0x1FFF051,00},{0x0FFF110,0x0D},{0x297A110,0x43},{0x0E7E111,00},{0x020C420,0x0E},{0x0F6C3B0,00},{0x0FFF030,0x96},{0x0F8F131,00},{0x014E020,0x8F},{0x0D6E130,0x80},{0x14551E1,0x4D},{0x14691A0,00},{0x14551A1,0x4D},{0x14681A0,00},{0x2E7F030,00},{0x047F131,00},{0x0E5F030,0x90},{0x0F5F131,0x80},{0x1F5F430,0x0A},{0x0F6F330,00},{0x1468330,0x15},{0x017D231,00},{0x1455060,0x17},{0x14661A1,00},{0x04460F0,0x8F},{0x0154171,00},{0x214D0B0,0x0F},{0x1176261,0x80},{0x211B1F0,0x8A},{0x115A020,0x80},{0x201C3F0,0x89},{0x0058361,0x40},{0x201B370,0x89},{0x1059360,0x40},{0x2F9F830,0x97},{0x0E67620,00},{0x035F131,0x24},{0x0B3F320,00},{0x0C8AA00,0x04},{0x0B3D210,00},{0x104C060,0x51},{0x10455B1,0x80},{0x10490A0,0x52},{0x1045531,0x80},{0x1059020,0x51},{0x10535A1,0x80},{0x10590A0,0x52},{0x1053521,0x80},{0x20569A1,0x93},{0x20266F1,00},{0x0031121,0x4D},{0x1043120,0x80},{0x2331100,0x82},{0x1363100,0x80},{0x0549060,0x56},{0x0047060,0x40},{0x0549020,0x92},{0x0047060,0xC0},{0x0B7B1A0,0x99},{0x08572A0,0x80},{0x05460B0,0x5A},{0x07430B0,0x80},{0x0433010,0x90},{0x0146410,00},{0x0425090,0x8F},{0x0455411,00},{0x1158020,0x8E},{0x0365130,00},{0x01F71B0,0x1A},{0x03B7220,0x80},{0x0468020,0x16},{0x1569220,00},{0x1E68080,0x8D},{0x1F65190,00},{0x0B87020,0x22},{0x0966120,0x80},{0x0B87020,0x23},{0x1156020,0x8E},{0x1177030,0x92},{0x1366130,00},{0x2A69120,0x4D},{0x1978120,00},{0x2A69120,0x8C},{0x1979120,00},{0x2A68130,0x50},{0x1976130,00},{0x2A68130,0x4A},{0x00560A0,0x96},{0x11652B1,00},{0x10670A0,0x89},{0x11662B0,00},{0x00B98A0,0x4A},{0x10B73B0,00},{0x10B90A0,0x85},{0x11B63B0,00},{0x0167070,0x90},{0x0085CA2,0x80},{0x007C820,0x4F},{0x1077331,00},{0x0199030,0x91},{0x01B6131,0x80},{0x017A530,0x8D},{0x01763B0,0x80},{0x08F6EF0,0x80},{0x02A3570,00},{0x08850A0,0x93},{0x02A5560,0x80},{0x0176520,0x0A},{0x02774A0,00},{0x12724B0,0x84},{0x01745B0,00},{0x00457E1,0xAD},{0x0375760,00},{0x33457F1,0x28},{0x05D67E1,00},{0x00F31D0,0xC7},{0x0053270,00},{0x00551B0,0xC7},{0x0294230,00},{0x15B5122,0x52},{0x1256030,00},{0x15B9122,0x4D},{0x125F030,00},{0x19BC120,0x43},{0x165C031,00},{0x1ABB160,0x41},{0x005F131,00},{0x33357F0,0x28},{0x00767E0,00},{0x30457E0,0x23},{0x04D67E0,00},{0x304F7E0,0x23},{0x04D87E0,00},{0x10B78A1,0x42},{0x12BF130,00},{0x0558060,0x21},{0x014F2E0,00},{0x0559020,0x21},{0x014A2A0,00},{0x195C120,0x43},{0x16370B0,0x80},{0x19591A0,0x49},{0x1636131,00},{0x1075124,0x40},{0x229FDA0,00},{0x0053280,0xC0},{0x0053360,00},{0x0053240,0x40},{0x00533E0,00},{0x2A5A1A0,0x8F},{0x196A1A0,00},{0x005F0E0,0x44},{0x0548160,00},{0x105F0E0,0x44},{0x0547160,0x80},{0x033A180,0x8A},{0x05452E0,00},{0x1528081,0x9D},{0x1532340,0x80},{0x15211E1,0x8C},{0x17380E0,0x80},{0x0477220,0x40},{0x019F883,00},{0x1028500,0xD2},{0x11245C1,00},{0x0034522,0xD2},{0x23535E3,00},{0x074F604,0xC0},{0x024A302,00},{0x0D2C090,0x8E},{0x0D2D130,00},{0x0D2D090,0x8E},{0x0D2F130,00},{0x0F390D0,0x12},{0x0F3C2C0,00},{0x0F2C2C0,0x80},{0x15213E0,0x1A},{0x21333F1,0x80},{0x0BA45E0,0x1A},{0x19132F0,00},{0x1025810,0x18},{0x0724202,00},{0x0B36320,0x08},{0x0B36324,00},{0x0127730,0x0D},{0x1F4F310,00},{0x033F900,0x80},{0x273F400,0x80},{0x2ACF907,0x1A},{0x229F90F,00},{0x153F220,0x21},{0x0E49122,00},{0x339F103,0x4F},{0x074D615,00},{0x1158930,0x42},{0x2076B21,00},{0x003A130,0x1F},{0x0265221,00},{0x0134030,0x13},{0x1166130,0x80},{0x032A113,00},{0x172B212,0x80},{0x001E795,0x81},{0x0679616,00},{0x104F003,0x49},{0x0058220,00},{0x0D1F813,0x44},{0x078F512,00},{0x0ECA710,0x0B},{0x0F5D510,00},{0x0C8A820,0x0B},{0x0B7D601,00},{0x0C4F800,0x0B},{0x0B7D300,00},{0x031410C,0x8F},{0x31D2110,0x80},{0x1B33432,0x21},{0x3F75431,00},{0x00437D1,0xAD},{0x0343750,00},{0x2013E02,00},{0x2F31408,00},{0x003EBF5,0xD4},{0x06845F6,00},{0x171DAF0,00},{0x117B0CA,0xC0},{0x11121E2,0xC0},{0x20053EF,0x86},{0x30210EF,0xC0},{0x2F0F00C,00},{0x0E6F604,00},{0x047FA00,00},{0x006F900,00},{0x067FD02,0x80},{0x078F703,00},{0x214F70F,0x05},{0x247F900,00},{0x3FB88E1,00},{0x2A8A6FF,00},{0x0FFAA06,00},{0x0FAF700,00},{0x06CF502,00},{0x138F703,00},{0x078F502,00},{0x137F700,00},{0x037F502,00},{0x137F702,00},{0x0E6C204,0x10},{0x343E800,00},{0x212FD03,0x80},{0x205FD02,0x80},{0x085E400,0x80},{0x234D7C0,0x80},{0x0E6E204,0x90},{0x144B801,00},{0x2777602,0x87},{0x3679801,00},{0x270F604,0x81},{0x3A3C607,00},{0x067FD00,00},{0x0F0F081,00},{0x004F49F,0xC3},{0x056FB03,0x81},{0x2D65A00,0x0E},{0x0FFFFBF,0xC0},{0x1C7F900,0x07},{0x0FFFF80,0xC0},{0x1D1F813,0x44},{0x1DC5E01,0x0B},{0x113F020,0x8C},{0x027E322,0x80},{0x125A020,0x86},{0x136B220,00},{0x015C520,0x28},{0x0A6D221,00},{0x1006010,0x1A},{0x0F68110,00},{0x2E7F030,0x12},{0x1E7F510,0x0D},{0x2E7F610,00},{0x0465020,0x96},{0x1569220,0x80},{0x075FC01,00},{0x037F800,00},{0x175F701,0xC0},{0x336FC00,00},{0x2709404,0x81},{0x0B5F901,0x07},{0x050D4BF,0xC0},{0x0FFF110,0x06},{0x0069421,0x0E},{0x000F081,00},{0x004F41F,0xC3},{0x03BF271,0x0E},{0x00BF3A1,00},{0x0B5F341,00},{0x082D385,0x59},{0x0E3A341,0x80},{0x005B341,0x80},{0x014F6B1,0x92},{0x058C7B2,0x14},{0x018AAB0,0x44},{0x0088A71,00},{0x0145571,00},{0x10497A1,0x13},{0x0045571,0x80},{0x01A4671,00},{0x10691A1,0x13},{0x0076161,0x89},{0x0C8F381,0x80},{0x0F8D144,0x80},{0x0A6C362,00},{0x11CD1B1,0x49},{0x1037F61,0x98},{0x012C161,0x93},{0x0054FA1,00},{0x0058AB2,0x83},{0x0397461,0x90},{0x06771A1,00},{0x00554B1,0x57},{0x0057AB2,00},{0x0635450,00},{0x045A581,00},{0x03782A1,00},{0x01F75A1,0x8A},{0x1557261,0x86},{0x00B72A1,00},{0x00982A1,00},{0x01F7561,0x9C},{0x0266561,00},{0x04676A2,0xCB},{0x0365561,00},{0x00757A2,0x99},{0x0075661,00},{0x00777A2,0x93},{0x0077661,00},{0x00A9661,00},{0x005DF62,0x9E},{0x0076FA1,0x40},{0x2068FA0,00},{0x09453B7,0xA5},{0x005A061,00},{0x011A8A1,0x1F},{0x0032571,0x80},{0x03491A1,0x17},{0x01655A1,00},{0x00154B1,0x5D},{0x0036AB2,00},{0x0354262,00},{0x177A161,0x1C},{0x0028691,00},{0x026F2A1,0x83},{0x151F181,0x4D},{0x0F5F282,00},{0x15111A1,0x94},{0x0131163,0x80},{0x111EB51,00},{0x303FF40,00},{0x306F640,00},{0x205F540,00},{0x048F881,0x45},{0x0057582,0x08},{0x132FA13,0x80},{0x1F9F211,0x0A},{0x0F2F409,0x1B},{0x0E2F211,0x80},{0x0F3D403,0x94},{0x0F3A340,0x40},{0x1058761,0x80},{0x0058730,0x03},{0x174A423,0x9D},{0x0F8F271,0x80},{0x0007FF1,0x8D},{0x1167F21,00},{0x0759511,0x0D},{0x1F5C501,0x80},{0x073F222,0x97},{0x0F3F331,0x80},{0x0C3F411,00},{0x01096C1,0x8B},{0x1166221,00},{0x01096C1,0x8E},{0x1153221,00},{0x012C4A1,0x97},{0x0065F61,00},{0x010E4B1,0xCD},{0x0056A62,0x83},{0x0F57591,0x0D},{0x144A440,00},{0x0256421,0x92},{0x0088F21,0x01},{0x0167421,0x93},{0x0078F21,00},{0x0176421,0x94},{0x0195361,0x94},{0x0077F21,0x04},{0x0187461,0x8F},{0x0088422,00},{0x016A571,0x4A},{0x00A8871,0x4A},{0x1198131,00},{0x0219632,0x4A},{0x0187261,00},{0x04A85E2,0x59},{0x01A85E1,00},{0x02887E1,0x48},{0x01975E1,00},{0x0451261,0x8E},{0x1045F21,0x84},{0x106A510,0x86},{0x004FA00,0x03},{0x202A50E,0x09},{0x017A700,00},{0x0F6B710,0x40},{0x005F011,00},{0x00BF506,0x07},{0x008F602,00},{0x008FF0E,00},{0x209F300,0x06},{0x005F600,00},{0x006F60C,00},{0x004F60C,00},{0x244CB12,0x05},{0x001F60C,00},{0x242CB12,00},{0x000F00E,00},{0x3049F40,00},{0x0039F50,0x04},{0x204940E,0x02},{0x0F78700,0x0A},{0x2039F1E,00},{0x3029F50,00},{0x100FF00,00},{0x014FF10,00},{0x2187700,0x03},{0x1F4FC02,00},{0x0F4F712,0x05},{0x053F101,0x4F},{0x074D211,00},{0x00381A5,0xD2},{0x005F1B2,0x80},{0x09BA0B1,00},{0x0B5F441,00},{0x0068761,00},{0x0100133,0x87},{0x0337D14,0x80},{0x1FFF000,0x0A},{0x1FFF001,00},{0x0AE71E1,0x16},{0x09E81E1,00},{0x2831621,0xDA},{0x0C31320,00},{0x0022A95,0x97},{0x0F34212,0x80},{0x001EF4F,0x81},{0x01B61B1,0x80},{0x0176E71,0xC5},{0x0157261,0x1C},{0x0278461,00},{0x0427847,0x4D},{0x0548554,00},{0x0B3F101,0x88},{0x0117171,0x8B},{0x11562A1,00},{0x0035172,0x1C},{0x0135262,0x05},{0x06754A1,0x80},{0x0115270,0xC5},{0x0FE3171,0x40},{0x021FF13,0x96},{0x003FF11,0x80},{0x01797F1,0x01},{0x018F121,0x0D},{0x00687B1,00},{0x03BA2A1,0x82},{0x00E8B62,0x05},{0x01B61B1,0x40},{0x03B92A1,0x80},{0x03B52A1,0x80},{0x01F4171,0x1C},{0x0177421,0x83},{0x0176562,00},{0x0AE7121,0x16},{0x09E8121,00},{0x212AA53,0x97},{0x021AC51,0x80},{0x112AA43,0x1C},{0x1119B51,00},{0x001FFA4,0xDB},{0x0AC9011,0x1A},{0x22F55B0,0x16},{0x02A65A1,00},{0x2A2B264,0x02},{0x0F3F8E2,0x86},{0x0F3F770,0x40},{0x0F0E026,0x03},{0x0056541,0x83},{0x0743291,00},{0x0B2F112,0x08},{0x061D001,0x40},{0x282B264,00},{0x1DA9803,00},{0x0B2F131,0x8F},{0x0AFF111,0x83},{0x0B2F131,0x19},{0x0D5C131,0x01},{0x0D2F111,0x4C},{0x0E6F211,0x83},{0x0D5C111,0x15},{0x0E6C231,00},{0x0D4F315,0x5F},{0x0E4B115,0x61},{0x0E4B111,0x5C},{0x0B5B111,00},{0x0D4F111,0x89},{0x0E4C302,0x5F},{0x035C100,0x9B},{0x0D5C111,00},{0x050F210,0x60},{0x0F0E131,0x5D},{0x040B230,0xA2},{0x5E9F111,0x80},{0x0E3F217,0x54},{0x0E2C211,0x06},{0x0C3F219,0x2B},{0x0D2F291,0x07},{0x004A61A,0x27},{0x004F600,0x0A},{0x0790824,0x9A},{0x0E6E384,0x5B},{0x0E6F314,0x62},{0x0E6F280,00},{0x055F71C,0xA3},{0x0D88520,0x0D},{0x0F7E701,0x84},{0x005B301,0x80},{0x0F77601,0x80},{0x02AA2A0,0x85},{0x02AA522,0x9E},{0x02AA5A2,0x83},{0x02AA128,0x95},{0x038C620,0x81},{0x057F621,0x80},{0x002B025,0x5F},{0x0057030,0x40},{0x002C031,0x46},{0x0056031,0x80},{0x015C821,0x93},{0x0056F31,00},{0x005CF31,0x16},{0x0057F32,0x87},{0x4F2B913,0x0D},{0x14A9221,0x99},{0x242F823,0x96},{0x2FA9122,0x1A},{0x0BA9221,0x99},{0x04A9122,00},{0x0487131,0x19},{0x0487131,00},{0x0DAF904,0x0B},{0x0DFF701,0x80},{0x0C8F621,0x1C},{0x0C8F101,0xD8},{0x1038D12,0x95},{0x0866503,0x8B},{0x113DD31,0x17},{0x012C121,0x1A},{0x0054F21,00},{0x022C122,0x0B},{0x0054F22,0x1C},{0x0F5A006,0x03},{0x035A3E4,0x23},{0x0077FA1,0x51},{0x0077F61,00},{0x0578402,0x05},{0x074A7E4,0x16},{0x054990A,0x65},{0x0639707,0x60},{0x1045FA1,0x59},{0x0066F61,00},{0x0178421,0x15},{0x008AF61,0x0B},{0x0178521,0x94},{0x0097F21,0x05},{0x008AF61,0x0D},{0x1277131,0x15},{0x0499161,0x83},{0x0277DB1,0x10},{0x0297A21,0x08},{0x00A6321,0x9F},{0x00B7F21,00},{0x00A65A1,0xA2},{0x00B7F61,00},{0x036A823,0x52},{0x016AAA1,0x94},{0x00A8F21,0x80},{0x011DA25,00},{0x068A6E3,0x2B},{0x068A623,0x1E},{0x0588821,0x8C},{0x001DF26,00},{0x03876E4,0x2B},{0x0369522,0xD8},{0x00776E1,00},{0x087C4A3,00},{0x076C626,0x57},{0x04AA321,0x48},{0x00A8621,00},{0x00A9621,00},{0x109F121,0x1D},{0x109F121,0x80},{0x0332121,0x97},{0x0454222,0x03},{0x0D421A1,0x99},{0x0D54221,0x03},{0x0336121,0x8D},{0x0354261,0x03},{0x0E42121,0x8C},{0x0D54261,0x03},{0x1471121,0x15},{0x0E41121,0x8C},{0x0D55261,00},{0x58AFE0F,0x83},{0x006FB04,0x85},{0x003A821,0x99},{0x004A722,00},{0x0937501,0x61},{0x0B4C502,0x80},{0x056B222,0x92},{0x056F261,0x8A},{0x2343121,0x9D},{0x00532A1,0x80},{0x088A324,0x40},{0x087A322,0x5B},{0x151F101,0x13},{0x0F5F241,00},{0x04211A1,0x10},{0x0731161,0x92},{0x0211161,0x98},{0x0167D62,0x57},{0x01672A2,0x80},{0x0069F61,0x5B},{0x0049FA1,00},{0x024A238,0x9F},{0x024F231,0x9C},{0x014F123,0x9F},{0x053C601,0x71},{0x0D5F583,0x40},{0x4FCFA15,0x11},{0x0ECFA12,0x80},{0x0FCFA18,0x9D},{0x007A801,0x5C},{0x083F600,0x03},{0x458F811,0x8F},{0x0E5F310,00},{0x154F610,0x92},{0x0E4F410,00},{0x0001F0F,00},{0x0001F0F,0x3F},{0x3F11FC0,0x3F},{0x7845603,0x88},{0x024D803,0x1E},{0x7846604,0x08},{0x001FF06,00},{0x3043414,00},{0x1841204,00},{0x0F86848,00},{0x0F86747,00},{0x0F8464C,00},{0x261B235,0x1C},{0x715FE11,0x20},{0x019F487,0xC0},{0x1112EF0,00},{0x11621E2,0xC0},{0x7112EF0,00},{0x007FC01,0x03},{0x638F802,0x03},{0x007FC00,0x03},{0x638F801,0x03},{0x00CFD01,0x07},{0x03CD600,00},{0x2477B12,00},{0x243CB12,0x15},{0x3E4E40F,00},{0x1E5F508,0x0A},{0x366F50F,00},{0x1A5F508,0x19},{0x1E5F507,0x11},{0x365F50F,00},{0x1A5F506,0x1E},{0x0C49406,00},{0x2F5F604,00},{0x004F902,00},{0x0F79705,0x03},{0x156F28F,0x03},{0x100F446,00},{0x000F38F,00},{0x0A5F442,0x06},{0x005F310,00},{0x037F811,0x05},{0x155F381,00},{0x000F341,00},{0x503FF80,00},{0x3E5E40F,00},{0x1E7F508,0x0A},{0x1A8F608,0x19},{0x00CF506,0xC8},{0x008F502,0x0B},{0x00CF506,0xC5},{0x007F501,0x03},{0x0BFFA01,0x8F},{0x096C802,0x80},{0x0BFFA01,0xCF},{0x096C802,0x0B},{0x087FA01,0x4F},{0x0B7FA01,0x08},{0x08DFA01,0x55},{0x0B5F802,0x12},{0x08DFA01,0x59},{0x0B6F802,0x12},{0x00AFA01,00},{0x006F900,0x0D},{0x089F900,0x80},{0x06CF600,0x08},{0x388F803,0x8D},{0x088F803,0x88},{0x0B8F80C,0x12},{0x388F803,0x88},{0x0B6F60C,0x03},{0x0B8F80C,0x0F},{0x04F760F,00},{0x2187700,0x12},{0x249C80F,0x40},{0x2699B02,0x80},{0x249C80F,0xC0},{0x2699B0F,0x19},{0x305AD57,0xDC},{0x0058D87,00},{0x305AD47,0xDC},{0x0058D87,0x12},{0x304A857,0xDC},{0x0048887,00},{0x0058887,0x08},{0x0F5F715,0x08},{0x0F5F712,0x08},{0x7476701,0xCD},{0x0476703,0x40},{0x0476701,0xC0},{0x0556501,00},{0x0A76701,0x17},{0x0356503,0x1E},{0x0777701,0x9D},{0x0057501,00},{0x005FF1F,0x40},{0x005FF1F,0x48},{0x002FF1F,0x40},{0x3E0F50A,0x7C},{0x003FF1F,0x40},{0x04F7F0F,0x40},{0x21E7E00,0x88},{0x21E7E00,0x14},{0x6E5E403,0x0D},{0x7E7F507,0x11},{0x366F500,0x1B},{0x4A8F604,0x15},{0x3F40003,0x3F},{0x084FA01,0x4F},{0x0B4F800,00},{0x0F3F040,0x30},{0x0038761,00},{0x033E813,0x12},{0x0F3F011,00},{0x133F721,0x48},{0x2F4F320,00},{0x1F4F201,00},{0x0F5F009,00},{0x1114070,0x84},{0x0034061,00},{0x0D3B305,0x40},{0x024F246,0x80},{0x106F90E,0x2F},{0x0F4F001,00},{0x0126E71,0x0D},{0x0045061,00},{0x2A31321,0x1A},{0x0F31220,00},{0x025DC03,0xA2},{0x025DC03,0x17},{0x009F021,00},{0x025DF23,0x20},{0x0F9F021,00},{0x1025161,0x52},{0x0024173,00},{0x0195132,0x5A},{0x0396061,0x85},{0x025DC03,0x9A},{0x025DC03,0x98},{0x1126EB1,0x47},{0x0045021,0x02},{0x025DC03,0x97},{0x025DC03,0x96},{0x025DC03,0x94},{0x025DB02,0x10},{0x006F030,00},{0x1145152,0x88},{0x0147242,00},{0x01572A2,00},{0x0F8AF00,0xC0},{0x0F6F401,00},{0x0009FB1,0x45},{0x1069FA2,0x0D},{0x1069FA2,0x08},{0x1016F00,0x19},{0x0F57001,00},{0x229FFF2,0x1A},{0x0F480E1,00},{0x025DC03,0x12},{0x009F032,00},{0x025DC03,0x10},{0x025DC03,0x0E},{0x025DC03,0x0C},{0x025DC03,0x0A},{0x025DC03,0x92},{0x1062F01,0x07},{0x0076521,00},{0x00470F5,0x1C},{0x0F38071,00},{0x0F77061,0x21},{0x0256061,00},{0x0C76012,0x28},{0x00550F1,00},{0x0049F21,00},{0x0049F62,00},{0x2119A16,0x14},{0x0029012,00},{0x033F813,0x0E},{0x003FF11,00},{0x0057F72,0x1D},{0x0F56071,00},{0x203B162,0x4A},{0x2027062,0x4A},{0x0029062,00},{0x0FF0F20,0xFF},{0x0F1F021,00},{0x0F28021,0x8F},{0x0037021,00},{0x0039012,00},{0x212AA93,0x97},{0x021AC91,0x80},{0x024DA05,0x8B},{0x013F901,00},{0x203B162,0xCF},{0x0046172,00},{0x006FA04,0xD3},{0x095F201,00},{0x0847162,0x21},{0x0246061,00},{0x3F27026,00},{0x0568705,00},{0x005FC11,00},{0x1F5DF12,00},{0x104F021,0xCF},{0x0D6F401,00},{0x104F021,0xC7},{0x004F021,0x1B},{0x104F061,0xCE},{0x1D6F441,00},{0x065F301,0x12},{0x07DF111,00},{0x254F5A8,0xE8},{0x0B7F321,00},{0x14FF101,0xC6},{0x3D6F311,0x06},{0x0ADF303,0x58},{0x15E8301,00},{0x01F4C28,0xD4},{0x045F601,00},{0x223F208,0x92},{0x073F414,0x80},{0x22F6216,0x64},{0x06AF401,0x41},{0x036F506,0x10},{0x025FDA1,0x80},{0x0176D0A,0xD5},{0x005F001,00},{0x265F812,0xC8},{0x0D7F601,00},{0x092FF43,00},{0x003F015,00},{0x0388B03,0xC0},{0x2398300,0x80},{0x00FF060,0xC0},{0x00FF062,0x06},{0x29FFF24,00},{0x10FF021,00},{0x11FFF30,00},{0x14C5E32,00},{0x10BF024,0x49},{0x00BF024,0xCC},{0x10B5031,0x0A},{0x12F6F24,0xCA},{0x20D4030,0x0A},{0x00BF022,0xCD},{0x10B5071,0x03},{0x105F003,0xCE},{0x1C8F211,00},{0x125FF03,0x49},{0x145F503,0xD3},{0x1269E03,0x90},{0x0BBF221,0x80},{0x047FF01,0xC0},{0x2BCF400,00},{0x04F6F20,0xE0},{0x31FFF20,0x01},{0x32F5F30,0xE0},{0x31FFE30,0x01},{0x3598600,0x42},{0x02A7284,0x80},{0x054FE10,00},{0x00FF030,00},{0x0397530,0xC2},{0x088F220,0x40},{0x125FF10,0x0A},{0x039F330,0x0F},{0x00CF0A0,00},{0x07FF420,0x18},{0x00FF021,00},{0x106F010,00},{0x05FF620,0x16},{0x006F010,0x08},{0x106F031,0xC5},{0x10650B1,00},{0x11FF431,0x40},{0x13653A1,00},{0x01FF431,0xC0},{0x13663A1,00},{0x043F271,0x1D},{0x1285161,00},{0x279A702,0xD2},{0x284F410,00},{0x194F622,0x1B},{0x09BF231,0x80},{0x126F801,0x40},{0x105F000,00},{0x043F231,0x1D},{0x1285121,00},{0x1011031,0x56},{0x2042030,00},{0x136F131,0x1B},{0x0286121,00},{0x034F131,0x1C},{0x0285121,00},{0x0056072,0x83},{0x172FCE1,0x46},{0x01762B1,00},{0x0053071,0x57},{0x0055072,00},{0x062F600,00},{0x01BF301,0x08},{0x06553B1,0x14},{0x0254231,0x56},{0x00FF0A1,0x01},{0x1255221,0x55},{0x02993A1,0x01},{0x07554B1,0x20},{0x0089021,00},{0x0375421,0x1B},{0x008F021,00},{0x1396521,0x16},{0x09EF221,00},{0x0375621,0x1E},{0x00AF021,00},{0x0046021,0x4E},{0x1095031,00},{0x0046021,0x8E},{0x0055021,0x8E},{0x1095021,00},{0x0055031,0x8E},{0x0038031,0x17},{0x136F132,00},{0x2066020,0x19},{0x10A7022,00},{0x1065020,0x1E},{0x00A6022,00},{0x0258C32,0x4C},{0x0176221,00},{0x00430B1,0x57},{0x00A5021,00},{0x04451B1,0x55},{0x20F4032,0xDF},{0x0095021,00},{0x39C4611,0x20},{0x05A6321,00},{0x39D7531,0x17},{0x35AF802,00},{0x02A42B1,00},{0x20FF022,0x5D},{0x0535231,0x0F},{0x147F221,00},{0x39D65B1,0x17},{0x05AF802,00},{0x22A42B0,00},{0x057F421,0xC0},{0x228F232,00},{0x29D6561,0xC6},{0x2095021,00},{0x358F423,0xC0},{0x3486422,0x10},{0x0EDF331,0xCB},{0x07DF131,00},{0x395FF09,0xC0},{0x02552E1,00},{0x0052031,0x58},{0x0063031,0x40},{0x0735421,0x0E},{0x008F021,0x07},{0x0033071,0x5D},{0x0044072,00},{0x2023034,0x27},{0x003F021,0x09},{0x3042001,0x63},{0x0585201,0x99},{0x0364161,00},{0x0261131,0x1B},{0x0071031,00},{0x0B4F251,0xD0},{0x075F101,00},{0x0572132,0x06},{0x01942A3,00},{0x3859F45,0x15},{0x043F311,00},{0x115F403,0xD7},{0x0C8F221,00},{0x295F300,0x11},{0x2B9F260,00},{0x0050021,0xCF},{0x2041020,00},{0x2A3F400,0x1B},{0x0644312,0x22},{0x2028030,00},{0x098F201,0x40},{0x1D5F307,0x09},{0x083FF00,00},{0x166F502,00},{0x275FF12,0x80},{0x2E8F310,00},{0x163F402,0x0F},{0x164F502,00},{0x064FB05,0xC9},{0x2579600,00},{0x1B2FF13,0x0C},{0x30F5030,0x0A},{0x21DF230,0x0E},{0x10C4021,00},{0x3023030,0xC0},{0x2064030,00},{0x375FF25,0xC0},{0x033FE03,00},{0x37DFE25,0xC0},{0x0079003,00},{0x0034007,0xDC},{0x0056001,00},{0x2B3F811,0xC1},{0x003F010,0x03},{0x00CF000,00},{0x006F000,00},{0x32C8F01,00},{0x2A2FF40,00},{0x30E104E,00},{0x092FF11,0xC0},{0x306301E,00},{0x003402E,00},{0x003105E,00},{0x2A3375B,0x95},{0x237461A,0x40},{0x344FF6B,0xC0},{0x02AF1EA,0x01},{0x10EF07E,00},{0x00E3030,0x0A},{0x003F02E,00},{0x00310FE,00},{0x023FCC0,00},{0x006F04E,00},{0x0A3FB00,0xC0},{0x007F000,00},{0x0C2FD05,0xC0},{0x3D9F910,00},{0x03A8F2E,00},{0x067A800,00},{0x22C8305,00},{0x0589903,00},{0x25C8400,00},{0x08AF800,00},{0x00CFF00,00},{0x006FF00,00},{0x004F041,0xC0},{0x308F009,00},{0x006F001,0x40},{0x339880D,00},{0x12FF201,0xC0},{0x356F54E,00},{0x12FF241,0xC0},{0x155AF00,00},{0x364FF4B,00},{0x1496401,0xC0},{0x356F54A,00},{0x2678900,00},{0x357874E,00},{0x02FF241,0xC0},{0x05FF210,00},{0x27FC40E,00},{0x00CF003,0xC0},{0x03AF802,00},{0x00BF003,0xC0},{0x037F702,00},{0x01AFD02,00},{0x00BF002,0xC0},{0x325FF25,0xC0},{0x0078003,00},{0x0089011,0xC0},{0x357894E,00},{0x11BF100,00},{0x3468B5E,00},{0x205508C,0x80},{0x05C855D,0x0A},{0x205504C,0x40},{0x05C858D,0x0A},{0x206F04B,00},{0x346F610,00},{0x392F700,00},{0x2AF475E,00},{0x30FF01D,00},{0x0F0F715,00},{0x0EB3402,0x87},{0x0075004,00},{0x0EF3301,0xCB},{0x0075002,00},{0x2B2FF04,0x80},{0x2188719,0x04},{0x27FFF06,0x80},{0x204F009,0x0A},{0x053F300,0x43},{0x247694E,00},{0x224F10E,0x40},{0x335FF4E,0x02},{0x274F911,0x41},{0x108F010,00},{0x288F911,0xC1},{0x004F010,0x03},{0x15DFD25,0xC0},{0x015FF0E,00},{0x0BFF800,00},{0x008A000,00},{0x1679810,00},{0x104F041,0xC0},{0x040F520,0x0D},{0x0F7F010,0x89},{0x0256361,00},{0x0099161,00},{0x060F101,0x4D},{0x07BD211,00},{0x013F202,0x22},{0x043F502,00},{0x09BA0B1,0x40},{0x005F1B1,0x40},{0x0F466E1,0x13},{0x086B0E1,00},{0x0014171,0x1C},{0x03B92A1,00},{0x0064131,0x1A},{0x03792A1,0x80},{0x175A563,0x0F},{0x045A421,0x8D},{0x002A474,0x47},{0x04245D7,0x40},{0x05331C5,0x8F},{0x07242D9,00},{0x1F07151,0x91},{0x1856092,0x80},{0x3D3B1E1,0x4F},{0x1741221,00},{0x00FF071,0x8D},{0x15F63B2,0x80},{0x0358501,0x88},{0x053F108,0x40},{0x114D1B1,0x46},{0x0036131,0x10},{0x0F7F010,0x90},{0x0D5E521,0x06},{0x0A4F3F0,00},{0x1F5F460,0x07},{0x03A4331,0x1C},{0x00AAA21,00},{0x055F518,0x23},{0x0F5E622,0x0E},{0x0051F21,0x98},{0x00A7121,00},{0x03FFA10,0x86},{0x064F210,0x0C},{0x0013171,0x1C},{0x03BF2A1,00},{0x0754231,0x98},{0x0F590A1,0x80},{0x0345160,0x8D},{0x12652A0,0x04},{0x02422E0,0x8D},{0x02640A0,0x09},{0x02452E0,0x8D},{0x026B0A0,0x09},{0x0044131,0x1A},{0x034F2A1,0x80},{0x0289130,0x58},{0x048C131,0x0E},{0x0F9F913,0x86},{0x0047310,0x06},{0x03BA2A1,0x40},{0x0175331,0x18},{0x0F463E0,0x1E},{0x08670E1,00},{0x2034122,0x4F},{0x10561F2,0x80},{0x1FAF000,0x02},{0x1FAF211,0x85},{0x1A57121,0x17},{0x0958121,00},{0x0AE7161,0x1C},{0x02E8160,00},{0x054F606,0x73},{0x0B3F241,0x0E},{0x0D5E521,0x0E},{0x0A21B14,0x7F},{0x0A4A0F0,0x7F},{0x05285E1,0x18},{0x05662E1,00},{0x2448711,00},{0x0B68041,0x84},{0x3F0FB02,00},{0x006F3C2,0x0D},{0x00C900C,00},{0x004F001,0x12},{0x1251041,00},{0x1042041,00},{0x3F0F781,00},{0x135FFE1,0x02},{0x00FBF0C,0x07},{0x004F001,0x0A},{0x053F101,0x7F},{0x074F211,0x3F},{0x064F210,0x06},{0x0431121,0x97},{0x0354262,0x07},{0x1F0F001,00},{0x136F7E4,0x0A},{0x1856092,0x85},{0x006F311,0x07},{0x200A01E,00},{0x0FFF810,0x0E},{0x018BF20,00},{0x066F800,0x11},{0x0FFF902,0x19},{0x0FFF811,0x06},{0x215CF3E,00},{0x0F9D92E,0x11},{0x2A0B26E,00},{0x2D4960E,00},{0x2E0136E,00},{0x1D4A502,00},{0x025F522,0x95},{0x005EF24,0x9A},{0x004EF26,0xA1},{0x0065F24,0x07},{0x1047B20,0x4B},{0x072F521,00},{0x019992F,00},{0x0BFFAA2,0x22},{0x015FAA1,0x55},{0x00B7F21,0x08},{0x0137221,0x94},{0x0B26425,0x3E},{0x0739321,0x38},{0x0099DA1,0x04},{0x0298421,0x9C},{0x0CFF828,0xB2},{0x0187521,0x22},{0x00A9F21,0x07},{0x0F3F211,0x0F},{0x034F2E1,00},{0x1039761,0x41},{0x004C770,00},{0x00221C1,0x1A},{0x014B421,00},{0x001F2F1,0xCE},{0x02562E1,0x40},{0x212F1C2,0x25},{0x054F743,0x03},{0x2017230,0x1C},{0x2269420,00},{0x021A161,0x92},{0x116C2A1,0x40},{0x046A502,0x64},{0x044F901,0x80},{0x175F403,0x31},{0x0F4F301,0x83},{0x0858300,0x2A},{0x0C872A0,0x80},{0x0437721,0x25},{0x006A5E1,0x80},{0x0177423,0x83},{0x017C563,0x8D},{0x0187132,0x9A},{0x038B2A1,0x82},{0x0065231,0x1B},{0x037F2A1,0x80},{0x060F207,0x13},{0x036BA02,0x0A},{0x024F621,0x13},{0x014C421,0x80},{0x025F521,0x17},{0x015C521,0x80},{0x02C6621,0x17},{0x014A521,0x80},{0x064E400,00},{0x074A400,00},{0x2F0F009,0x0D},{0x047F920,00},{0x0F6E901,0x15},{0x006D600,00},{0x0F0F280,00},{0x0F4F480,00},{0x00110BE,0x0C},{0x202FF8E,00},{0x053F101,0x4B},{0x074F131,00},{0x053F201,0x49},{0x064F311,00},{0x053F201,0x50},{0x064F331,00},{0x078C423,0x99},{0x048C231,00},{0x098C423,0x97},{0x058C231,00},{0x088C423,0x5E},{0x05AC421,0x4E},{0x03AC231,00},{0x056B301,0x8D},{0x056B301,00},{0x019D0A3,0x5C},{0x017F021,0x80},{0x018D0A3,0x64},{0x018F021,0x80},{0x018F6B3,0x61},{0x008F131,00},{0x09EAAB3,0x08},{0x03E80A1,00},{0x0144571,00},{0x12497A1,0x0D},{0x0145571,0x80},{0x1249761,0x8F},{0x000A121,0x80},{0x0F6F236,00},{0x085F211,0x87},{0x0B7F212,0x80},{0x054F607,0x73},{0x0B6F242,00},{0x054F60E,0x73},{0x1E26301,0x46},{0x01E8821,00},{0x24D7520,0x8B},{0x01D8921,0x80},{0x01C6421,0xC4},{0x03CD621,00},{0x03C6421,0x4A},{0x008F321,0x92},{0x228F322,0x80},{0x028F331,0x92},{0x038B1B1,00},{0x002DB77,0xE0},{0x0125831,00},{0x00211B1,0x93},{0x0034231,0x80},{0x0023AB1,0xAF},{0x0134232,0x80},{0x2556823,0xD2},{0x1055461,00},{0x05312C4,0x10},{0x1D6FB34,0x83},{0x0269471,00},{0x074F212,00},{0x0096821,0x11},{0x01B5731,0x80},{0x02FA433,0x14},{0x0117575,00},{0x078F71A,0xC6},{0x0024691,00},{0x0287C31,0x91},{0x01AAB23,00},{0x0124D01,0x02},{0x013F501,00},{0x118D671,0x1E},{0x018F571,00},{0x0287271,0x95},{0x0186361,00},{0x054F589,0x5E},{0x023F582,0x07},{0x20FFF22,0x5A},{0x125F121,0x56},{0x0087262,00},{0x121F131,0x40},{0x0166F21,00},{0x1388231,0x4B},{0x0086821,00},{0x175F502,0x58},{0x0F8F501,0x80},{0x11561B1,0x16},{0x00562A1,00},{0x01351A1,0x1E},{0x0175221,0x80},{0x1145131,0x92},{0x00552A1,00},{0x12CF131,0x8F},{0x01C61B1,00},{0x1228131,0x4D},{0x0167223,0x80},{0x171D201,0x55},{0x238F301,00},{0x114F413,0x49},{0x013F201,0x80},{0x154F203,0x4C},{0x044F301,0x40},{0x119F523,0x51},{0x019F421,00},{0x1547003,0x51},{0x004B301,0x80},{0x05FF561,0x21},{0x02AF562,00},{0x018F221,0x0F},{0x018F521,0x80},{0x038F2A1,0x93},{0x018F321,00},{0x13FF631,0x89},{0x01FF321,0x40},{0x13FF431,0x88},{0x01FF221,0x40},{0x04F6421,0x91},{0x028F231,00},{0x05FF561,0x1E},{0x05A6661,00},{0x02A7561,0x07},{0x03FF561,0x28},{0x01A7562,0x04},{0x01F7561,0x21},{0x02A7561,00},{0x05F8571,0x51},{0x01A6661,00},{0x13F93B1,0x45},{0x01F6221,0x80},{0x13FA3B1,0x89},{0x00F8221,0x80},{0x13F86B1,0x8F},{0x00F7221,0x80},{0x137C6B1,0x87},{0x0067221,0x80},{0x0217B32,0x95},{0x0219B32,0x97},{0x0115231,0xC5},{0x11E3132,00},{0x1177E31,0x43},{0x019D520,0x93},{0x11B6121,00},{0x0069161,0x12},{0x0076161,00},{0x00D5131,0x1C},{0x01F7221,0x80},{0x13DC231,0x8A},{0x00F7761,0x80},{0x02DF431,0x8B},{0x00F7321,0x80},{0x02DA831,0x8B},{0x00F8321,0x80},{0x06A6121,0x26},{0x01C8D21,0x90},{0x00FA521,00},{0x01F75A1,0x10},{0x00F7422,00},{0x11F75A0,0x15},{0x01F7521,00},{0x033F5C5,0x53},{0x025FDE1,0x80},{0x013F5C5,0x59},{0x005FDE1,0x80},{0x0248305,0x66},{0x014A301,00},{0x031A585,0xD3},{0x033F284,0xC7},{0x022F211,0x80},{0x122F210,0xC9},{0x012FC11,00},{0x206FB03,0xD2},{0x006D901,00},{0x024D443,0x51},{0x004E741,0x40},{0x0275722,0x59},{0x0275661,0x40},{0x0175622,0xA7},{0x0176361,0x40},{0x205A8F1,0x9B},{0x00563B1,00},{0x05F8571,0x4B},{0x00A6B61,00},{0x105F510,0x47},{0x247F811,0x47},{0x054F311,00},{0x21AF400,00},{0x01AF400,00},{0x038F800,00},{0x079F400,0x03},{0x017F600,00},{0x007A810,0x06},{0x115DA00,00},{0x009A810,0x07},{0x107DF10,00},{0x334F407,00},{0x2D4F415,00},{0x0F4000A,0x3F},{0x0F6F717,00},{0x0F2E00E,0x5E},{0x033FF1E,0x40},{0x0645451,00},{0x38CF800,0x80},{0x06BF600,00},{0x060F207,0x54},{0x072F212,0x80},{0x0557542,0x96},{0x0257541,0x87},{0x268F911,0x46},{0x005F211,00},{0x14BFA01,0x08},{0x03BFA08,00},{0x007FF21,0x80},{0x107F900,00},{0x20DFF20,00},{0x027FF02,00},{0x0C8F60C,0xC2},{0x257FF12,00},{0x3059F10,00},{0x3039F10,00},{0x0C5F59E,00},{0x2F7F70E,00},{0x2B7F811,0x45},{0x003F310,00},{0x097C803,00},{0x0BAFA03,00},{0x38FF801,0x47},{0x06FF600,00},{0x06EF600,00},{0x38CF803,0x80},{0x0B5F80C,00},{0x38CF803,0x83},{0x0DFF611,0x4F},{0x0DEF710,0x40},{0x0F3F211,0x80},{0x1C5C202,0x11},{0x104D000,00},{0x0039012,0x04},{0x0F3F507,0x19},{0x0F2F501,00},{0x2F3F507,0x19},{0x0229F16,0x16},{0x032B0D2,00},{0x025DA05,0x4E},{0x015F001,00},{0x025C811,0x29},{0x0F2F511,00},{0x012FF54,0x16},{0x0F2F051,00},{0x212FF54,0x16},{0x106DF24,0x15},{0x005FF21,00},{0x104F223,0x50},{0x0045231,0x80},{0x00BF223,0x4F},{0x00B5230,0x82},{0x2036162,0x4A},{0x0058172,00},{0x01CF201,0x10},{0x087F501,00},{0x014F201,0x10},{0x084F501,00},{0x103AF00,0x06},{0x06A5334,00},{0x004FF11,0x03},{0x0114172,0x89},{0x01562A2,0x40},{0x0F9F121,0x1C},{0x0F6F721,00},{0x0F3F201,00},{0x005FF00,0x18},{0x0F3F020,00},{0x01562A1,0x40},{0x2A32321,0x1A},{0x1F34221,00},{0x010A130,0x07},{0x0337D10,00},{0x01D5320,0x18},{0x03B6261,00},{0x01572A1,0x17},{0x01A8A21,00},{0x0069060,00},{0x0069062,00},{0x0077061,0x80},{0x0077062,0x80},{0x0077041,0x80},{0x00687A2,00},{0x1039012,0x04},{0x0037165,0xD2},{0x0076171,00},{0x0011E00,0x40},{0x0A11220,0x40},{0x0059221,0x1C},{0x1059421,00},{0x044FF25,0x15},{0x033F324,0x01},{0x0132F20,0x0D},{0x0132321,00},{0x0012E01,0x40},{0x0216221,0x40},{0x3134362,0x2E},{0x0038261,00},{0x2035FE6,0x0F},{0x00350E1,00},{0x3034F61,0x0D},{0x0035061,00},{0x1034F61,00},{0x3033F60,0x0D},{0x0033061,00},{0x112FF53,0x13},{0x0F1F071,00},{0x112FFD1,0x12},{0x0F1F0F1,00},{0x0E11126,0xA5},{0x0E11120,00},{0x30244A1,0x51},{0x04245E1,00},{0x0E1A126,0xA5},{0x0E1A120,0x0E},{0x054F101,0x40},{0x004F008,00},{0x011A131,0x47},{0x0437D16,0x40},{0x211A131,0x14},{0x0437D11,00},{0x091AB0E,0xC0},{0x0C3F702,00},{0x0F5F431,00},{0x1176E31,0x43},{0x20CAB22,0x08},{0x1176E31,0x4F},{0x002FF64,0xDB},{0x0F3F522,0x02},{0x001FF63,0xDB},{0x0F3F534,00},{0x0FFE802,00},{0x108FF00,00},{0x0F1100E,00},{0x0F61800,00},{0x1F18F2A,00},{0x1F63816,00},{0x0F0102E,00},{0x2821020,00},{0x201EFEE,0x10},{0x0069FEE,0x04},{0x201EFEE,0x01},{0x001F02E,00},{0x0064820,00},{0x3EFF71C,00},{0x08FFD0E,00},{0x202FF0E,00},{0x103FF1E,0x80},{0x202BF8E,00},{0x2049F0E,00},{0x003FF64,0xDB},{0x0F6F73E,00},{0x100F300,00},{0x054F600,00},{0x2F3F40C,00},{0x3D66E0E,00},{0x07B9C21,0x0A},{0x0FB9502,00},{0x0778121,0x17},{0x0879221,00},{0x0075121,0x06},{0x0295231,0x1E},{0x0197121,0x04},{0x0187621,0x1A},{0x0098121,0x05},{0x05971A1,00},{0x0257521,0x1A},{0x0178421,0x81},{0x0167221,0x01},{0x00A7BA1,0x05},{0x10A7B23,0x05},{0x00A7724,0x07},{0x06BFF31,0x04},{0x0195175,0x03},{0x0599BA1,0x8C},{0x00A75E1,00},{0x0389F22,0x1D},{0x0296761,0x01},{0x00C9222,0x1D},{0x00DA261,0x03},{0x2863428,0x39},{0x0354121,0x07},{0x1F53223,0x09},{0x1D52222,0x10},{0x1053F21,0x88},{0x0EFA120,00},{0x0DFF310,00},{0x0A9F802,0x03},{0x3D8B802,0x01},{0x0FE822C,00},{0x3D98802,0x07},{0x3F87404,0x09},{0x100FF2E,00},{0x334D609,0x01},{0x389F837,0x0C},{0x0F8F703,0x04},{0x0FAFA25,0x14},{0x0F9AA03,00},{0x0F7F241,0x12},{0x0F7F281,00},{0x10BD0E0,0x80},{0x109E0A4,0x8E},{0x0F5F341,00},{0x09612F3,0x45},{0x10430B1,0x86},{0x204F061,0x9D},{0x2055020,0x83},{0x236F312,0x2A},{0x2D7B300,00},{0x143F701,00},{0x1E4F3A2,00},{0x35B8721,0x99},{0x00A6021,00},{0x0F3D385,0x59},{0x0F3A341,0x80},{0x125FF10,0x56},{0x015F711,00},{0x04AFA02,0x16},{0x074F490,0x01},{0x045F668,00},{0x0289E87,0x01},{0x164F923,0x95},{0x177F607,00},{0x0E2CE02,0x25},{0x4E2F402,00},{0x0E2F507,0xA1},{0x0E2F341,00},{0x2E5F5D9,0x22},{0x0E5F251,00},{0x0E1F111,0x10},{0x0E1F251,0x08},{0x4B1F0C9,0x98},{0x0B2F251,0x01},{0x082F311,0x44},{0x0828523,0xB3},{0x0728212,0xA7},{0x0728201,0x27},{0x0328411,00},{0x4E5F111,0xA1},{0x4E5F312,0x40},{0x0E5F111,0x89},{0x0E6F111,00},{0x5047130,0x99},{0x01474A0,0x01},{0x1147561,0x88},{0x0147522,00},{0x7217230,0x1B},{0x604BF31,0x03},{0x0357A31,0x1D},{0x03A7A31,0x09},{0x06599E1,0x80},{0x0154825,0x85},{0x015AA62,0x94},{0x0058F21,0x80},{0x025C9A4,0xA2},{0x0056F21,0x80},{0x0056F21,00},{0x07E0824,0x80},{0x0E4E383,0x40},{0x0E6F314,0x63},{0x0E6F281,00},{0x205FC00,0x40},{0x007FC00,00},{0x638F801,0x80},{0x005F172,0x80},{0x005F171,0x40},{0x002A4B4,0x47},{0x0022A55,0x97},{0x1154261,0x40},{0x053090E,0x80},{0x094F702,00},{0x08F74A1,0x27},{0x02A65A1,0x80},{0x0667190,0x92},{0x08B5250,00},{0x0247332,0x16},{0x0577521,0x80},{0x28FA520,0x8E},{0x03D3621,00},{0x08C4321,0x19},{0x02F8521,0x80},{0x0AE71A1,0x1C},{0x02E81A0,00},{0x0B3F281,0x03},{0x01765A2,0x8D},{0x0F3F7B0,0x40},{0x0031801,0x80},{0x090F6B4,0xC1},{0x04FD600,0x03},{0x1DA9803,0x93},{0x0A0B2A4,0x02},{0x1D69603,0x80},{0x104F0A1,0xCE},{0x1D6F481,00},{0x254F568,0xE8},{0x025FD61,0x80},{0x092FF83,00},{0x00FF0A0,0xC0},{0x00FF0A2,0x06},{0x10B50B1,0x03},{0x02A7244,0x80},{0x00CF060,00},{0x1378D31,0x85},{0x0163871,00},{0x1065071,00},{0x1365361,00},{0x1366361,00},{0x043F2B1,0x1D},{0x12851A1,00},{0x00560B2,0x83},{0x0176271,00},{0x00530B1,0x57},{0x00550B2,00},{0x0655371,0x14},{0x00FF061,0x01},{0x0299361,0x01},{0x0755471,0x20},{0x0043071,0x57},{0x0445171,0x55},{0x02A4271,00},{0x08F4EE0,0xEC},{0x02A55A1,00},{0x39D6571,0x17},{0x22A4270,00},{0x29D65A1,0xC6},{0x00330B1,0x5D},{0x00440B2,00},{0x03641A1,00},{0x0B4F291,0xD0},{0x0194263,00},{0x3859F85,0x15},{0x2B9F2A0,00},{0x2A2FF80,00},{0x30E108E,00},{0x003109E,00},{0x2A3379B,0x95},{0x344FFAB,0xC0},{0x10EF0BE,00},{0x006F08E,00},{0x004F081,0xC0},{0x356F58E,00},{0x12FF281,0xC0},{0x364FF8B,00},{0x356F58A,00},{0x357878E,00},{0x02FF281,0xC0},{0x357898E,00},{0x3468B9E,00},{0x205504C,0x80},{0x05C859D,0x0A},{0x205508C,0x40},{0x05C854D,0x0A},{0x206F08B,00},{0x2AF479E,00},{0x247698E,00},{0x335FF8E,0x02},{0x104F081,0xC0},{0x215BFD1,0x9C},{0x20473C1,00},{0x177F810,0x91},{0x008F711,00},{0x277F810,0xF9},{0x108F311,0xC0},{0x25DFB14,0x80},{0x058F611,00},{0x12AF900,0x02},{0x22BFA01,00},{0x28268D1,0x42},{0x10563D0,00},{0x317B142,0x93},{0x317B101,00},{0x317B242,0x93},{0x317B201,00},{0x2BAE610,0x3F},{0x005EA10,0x3F},{0x053B101,0x4F},{0x074C211,00},{0x0B3F101,0x80},{0x1FAF211,0x80},{0x012F511,0x80},{0x093F241,00},{0x005D341,0x80},{0x012F001,0x92},{0x01FF2A0,0x11},{0x0F4D144,0x80},{0x08AE220,0x11},{0x0A8E420,00},{0x1DBB891,0x17},{0x1567551,00},{0x11772A1,0x40},{0x1151121,00},{0x111C071,0x20},{0x0C57461,0x0F},{0x165B220,0x08},{0x08153E1,0x9F},{0x0B962E1,0x05},{0x0AE71E1,0x19},{0x09E81E1,0x07},{0x0AE73E1,0x49},{0x09881E2,0x08},{0x0177E71,0xC5},{0x02A6561,0x80},{0x0D761E1,0x85},{0x0F793E1,0x80},{0x0439471,00},{0x011A131,0x87},{0x0437D16,0x80},{0x11111E2,0xC0},{0x1F5F718,00},{0x20CA808,0x09},{0x13FD903,00},{0x0A1B2E0,0x84},{0x1D6950E,00},{0x286F265,00},{0x256F605,0xC0},{0x2047404,00},{0x0111020,00},{0x1F085C1,0x47},{0x136C5C2,00},{0x00F3FF1,0x02},{0x06FF2F1,00},{0x0012F00,00},{0x001FF00,00},{0x08434F6,0xC0},{0x006A271,00},{0x101F310,0x40},{0x3B1F300,0x08},{0x08434F6,0x80},{0x00F4131,0x4F},{0x03F7221,00},{0x0F1F100,0x83},{0x0F1F110,00},{0x002A474,0x87},{0x04245D7,0x80},{0x0FFF001,0x4F},{0x00F6030,00},{0x0031D20,00},{0x0031D2C,00},{0x101F18A,00},{0x301F14E,00},{0x101F18E,00},{0x001F14E,00},{0x102FE00,00},{0x101F14E,00},{0x053F100,0x40},{0x302FF00,0x08},{0x072F423,0x99},{0x143F431,00},{0x121F193,0x80},{0x101F100,00},{0x101F1A9,00},{0x303F16E,00},{0x1F0F007,00},{0x2D91900,00},{0x2F48200,0x90},{0x3F57111,00},{0x0003F08,0x0F},{0x0042F0E,00},{0x000FE0E,00},{0x0031F0E,00},{0x31FF71A,0x07},{0x366A71E,0x3A},{0x2D94900,00},{0x0154D01,0x02},{0x016F501,00},{0x05FF771,0x39},{0x02ADC71,0x06},{0x098240A,0x4F},{0x0A71319,00},{0x0427F01,0x56},{0x0223501,00},{0x0732100,0x40},{0x07A5200,00},{0x0F0F09E,0x06},{0x1CC5C00,0x02},{0x254A321,0x13},{0x032C411,00},{0x3FFFFF0,0xC0},{0x0F0FBE5,00},{0x025F8B0,0x40},{0x085F1B1,00},{0x053F103,0x4F},{0x074F217,00},{0x058F341,0x63},{0x0C25240,0x80},{0x0632660,00},{0x2F0F000,00},{0x0934300,00},{0x0667150,0x92},{0x08B5290,00},{0x0F0103E,0x04},{0x1F111B0,0x03},{0x0B3F241,0x03},{0x0160020,0x5B},{0x0155022,0x80},{0x153F101,0x40},{0x353F108,0x40},{0x0176562,0x8D},{0x08434F2,0xC0},{0x0F6A271,00},{0x2FFFE00,0x03},{0x0FFF902,0x80},{0x01497F1,0x01},{0x0157121,0x0D},{0x010572A,0x01},{0x1111125,0x0B},{0x023F100,0x80},{0x0DFF200,00},{0x0931200,00},{0x305FF23,00},{0x0064F00,00},{0x306FF20,00},{0x0FFF900,0x03},{0x07F8700,00},{0x034D600,00},{0x004F600,00},{0x0FFF652,0xA2},{0x0FFF941,0x0F},{0x0FFF851,00},{0x205FC80,00},{0x034A501,00},{0x602FF01,00},{0x007FB00,0x09},{0x004A401,00},{0x0F69705,0x03},{0x156F284,0x03},{0x100F442,00},{0x000F34F,00},{0x0A5F48F,0x06},{0x0B6FA01,0x8A},{0x096C802,0x40},{0x00CF505,0xEC},{0x007F501,00},{0x095C802,0x80},{0x006F501,00},{0x0B5F803,00},{0x006FA01,00},{0x049C80F,0x40},{0x2058D47,00},{0x2048847,00},{0x506FF80,00},{0x016FF10,00},{0x7476601,0xCD},{0x0476603,0x40},{0x0476601,0xC0},{0x0576601,00},{0x0E56701,0x11},{0x0356503,0x24},{0x0757900,0x9A},{0x0057601,00},{0x0E6F622,0x1E},{0x0E5F923,0x03},{0x0E6F924,0x28},{0x0E4F623,00},{0x0E6F522,0x1E},{0x0E5F623,0x03},{0x0E6F524,0x28},{0x0E4F423,00},{0x0E5F108,0x66},{0x0E5C302,0x86},{0x052F605,0x69},{0x0D5F582,0x47},{0x131FF13,0x43},{0x075C401,0x80},{0x103E702,0x86},{0x005E604,0x40},{0x0145321,0x8B},{0x025D221,0x21},{0x104C3A1,0x9F},{0x0158221,0x0F},{0x075F502,0x20},{0x7D2FE85,0x8F},{0x074F342,0x80},{0x0119131,0x8A},{0x11572A1,00},{0x0013121,0x4D},{0x10545A1,0x82},{0x0075131,0x1D},{0x0399261,0x80},{0x00741B1,0x1C},{0x0398221,0x87},{0x21A73A0,0x95},{0x03A8523,00},{0x01A6C21,0x80},{0x0565321,0x18},{0x0277C21,00},{0x0299960,0xA3},{0x036F823,0x5D},{0x015FAA0,0x90},{0x00B8F22,0x08},{0x22871A0,0x23},{0x01A8124,00},{0x2287320,0x97},{0x01A8424,0x98},{0x0068B20,0x2F},{0x0008F21,0x20},{0x007CF20,0x5B},{0x0097F22,00},{0x0277784,0x9B},{0x01655A1,0x85},{0x01566A2,0x9B},{0x00566A1,0x06},{0x137FB00,0x05},{0x05CE711,00},{0x04CA900,0x0B},{0x023F302,0x08},{0x067F700,00},{0x017FB01,0x40},{0x008FD02,00},{0x0F4F306,0xA4},{0x0E4E203,0x6D},{0x0D4E101,0x53},{0x0E5E111,0x02},{0x053F241,0x9D},{0x0F3F213,00},{0x076D201,0x04},{0x053F101,0xC3},{0x0849212,0x09},{0x074F202,0x92},{0x077F401,0x83},{0x044F502,00},{0x475F113,0x96},{0x256F201,0x81},{0x033AD14,0x80},{0x0E5F14C,0x69},{0x0E5C301,0x06},{0x0E2660F,0x9D},{0x0E4C191,0x06},{0x033F584,0x59},{0x015FDA0,0x80},{0x0B5F615,0x97},{0x0E6F311,0x01},{0x0F8FF06,0x01},{0x055F8C4,00},{0x063F207,0x4F},{0x341F5A3,0x11},{0x203F811,00},{0x01AF003,0x5B},{0x01DF001,0x80},{0x22A9132,0xCD},{0x12A91B1,0x80},{0x005F171,0x80},{0x00AFF24,0x80},{0x00DFF21,0x80},{0x01CF003,0x54},{0x01EA001,0x84},{0x0186223,0x19},{0x02A6221,0x84},{0x0087224,0x4F},{0x00B4231,00},{0x0186222,0x19},{0x0C3C201,0x0A},{0x056F501,00},{0x034F401,0x13},{0x039F201,0x80},{0x07FC611,0x4D},{0x0DFF511,00},{0x4C5A421,0x20},{0x004F821,00},{0x0E78301,0x56},{0x078F201,00},{0x0AFF301,0x11},{0x114FF20,0xCB},{0x0D4F561,00},{0x1937510,00},{0x182F501,00},{0x01379C0,0x4F},{0x07472D2,00},{0x2355612,0x9C},{0x12D9531,00},{0x21351A0,0x9B},{0x163F2A1,0x48},{0x0368331,00},{0x171A501,0x0D},{0x2539600,0x02},{0x051F431,0x57},{0x074B711,00},{0x005F624,0xDB},{0x095C702,0x23},{0x095F422,0x22},{0x0D5F401,00},{0x016F521,0x8C},{0x03493A1,00},{0x01FB431,0x1A},{0x01FA2A1,0x80},{0x04654A1,0x1C},{0x0078FA1,0x07},{0x0466421,0x14},{0x0078FE1,0x01},{0x0796520,0x8C},{0x0268AA1,0x03},{0x2179280,0xCF},{0x03686A0,00},{0x03A5321,0x9C},{0x00B6521,0x01},{0x01C7321,0xC0},{0x02C7C21,0x97},{0x06581E1,0x51},{0x07C52F2,00},{0x22E71E0,0x23},{0x01E80E4,00},{0x019D530,0xC8},{0x01582A3,0x21},{0x007E562,0x9E},{0x005D224,0x9F},{0x0076F21,0x02},{0x48674A1,0x1F},{0x02765A1,00},{0x0277584,0xA0},{0x01655A1,0x81},{0x01566A2,0x8A},{0x00566A1,00},{0x016D322,0x9B},{0x07DE82F,0x2E},{0x006C524,0x62},{0x02764B2,0x04},{0x0557221,0x0B},{0x096F481,0x08},{0x0A6CF22,0xD5},{0x09C8410,0x0D},{0x001F501,0x37},{0x0F1F101,0x20},{0x0E3F201,0x11},{0x0E7F501,00},{0x03CF201,0x3F},{0x0E2F111,0x14},{0x0E6F541,0x13},{0x0E7F312,0x01},{0x00AF562,0xA3},{0x005F224,0xA2},{0x00A6F21,0x09},{0x2B6F800,00},{0x04CA900,0x07},{0x03FF600,00},{0x008B902,00},{0x01DFC03,00},{0x60AF905,00},{0x41CFC0A,00},{0x033F400,0x04},{0x4FFF700,00},{0x40AFF02,0xC0},{0x01CFF00,0x01},{0x003F902,00},{0x247FB00,00},{0x403FB02,00},{0x447FB01,00},{0x609F505,00},{0x709F30F,00},{0x201C687,0xC0},{0x023BC15,0x40},{0x435DE00,0xC0},{0x438F801,00},{0x30AF400,0x47},{0x278F700,0x04},{0x30AF400,0x4B},{0x278F700,0x02},{0x509F601,00},{0x429F701,00},{0x407FF00,00},{0x769A901,0x40},{0x408FA01,00},{0x769DB02,0x40},{0x112AA03,0x1C},{0x1F59011,00},{0x073F668,0x1B},{0x063F5A1,0x0D},{0x054F1A1,0x54},{0x0F4F060,00},{0x0038164,0xD2},{0x005D171,0x80},{0x0F1FB3E,0x29},{0x093A071,00},{0x022FE30,0x07},{0x007FB20,00},{0x0527101,0x8F},{0x0735012,00},{0x1249F16,0x11},{0x035B012,00},{0x1119183,0xD7},{0x0F1B142,00},{0x005FF01,00},{0x044F406,0x03},{0x088FA21,0x06},{0x031F91C,0x0C},{0x0E89615,00},{0x0F7F521,0x99},{0x0F7F521,0x80},{0x038B2F1,0x19},{0x0488122,0x40},{0x016D221,0x1D},{0x082D301,0x4E},{0x0B8D301,0x06},{0x0036101,0x14},{0x0F86101,0x0D},{0x017F321,0x17},{0x0E8F222,0x08},{0x0CEB161,0x13},{0x1BAD061,0x40},{0x075C130,0x10},{0x0659131,0x42},{0x0988802,00},{0x0D8B802,0x1A},{0x0F87404,0x27},{0x0009F2C,00},{0x0D4C50E,0x05},{0x044F904,0x04},{0x0F78706,0x03},{0x0A1F737,0x14},{0x000FF80,00},{0x0F7F500,00},{0x0FAFB21,0x03},{0x0FAF924,0x18},{0x0F5F505,0x14},{0x036F603,00},{0x077790E,0x02},{0x007AF20,0x15},{0x02BA50E,00},{0x007BF20,0x18},{0x03B930E,00},{0x03B8908,0x01},{0x02B5308,0x0A},{0x09AF815,0x21},{0x089F613,0x10},{0x04B8708,00},{0x0F75725,0x12},{0x0677803,00},{0x0F0F126,0x97},{0x0F5F527,0xA1},{0x054F123,0x66},{0x173F231,00},{0x010A132,0x87},{0x0337D16,0x80},{0x143F523,0x0E},{0x204F811,00},{0x0027D14,0x80},{0x001AF64,0xDB},{0x062A33F,0xC0},{0x0118171,0x8B},{0x1156261,0x40},{0x0127171,0x8B},{0x11652E1,0x40},{0x208F831,00},{0x054C701,0x4D},{0x096A201,00},{0x154C701,0x4D},{0x0C28621,0x16},{0x0BDF221,00},{0x08DF520,0x49},{0x08CF311,00},{0x09EF520,0x90},{0x05BF411,00},{0x5144261,0x87},{0x3344261,0x82},{0x02371A1,0x4F},{0x1286371,0x02},{0x11152F0,0xC5},{0x12E32F1,0x80},{0x01171F1,0x8B},{0x11542E1,0x40},{0x01FF201,0x17},{0x088F701,00},{0x054C701,0x8D},{0x053F121,0x4F},{0x1743232,00},{0x01271B1,0x8B},{0x1166261,0x40},{0x011A1B1,0x8B},{0x1159261,0x40},{0x5176261,0x80},{0x3176261,0x82},{0x5155261,0x80},{0x3166362,0x83},{0x0065131,0x1C},{0x01F61B1,0x1C},{0x0276561,0x83},{0x2275570,0x03},{0x0537101,0x4E},{0x07C6212,00},{0x0658181,0x93},{0x07C52B2,00},{0x02661B0,0x96},{0x0375271,00},{0x0A6FF64,0x8A},{0x01424B1,00},{0x0A4F724,0x5B},{0x0132431,00},{0x0384161,0x97},{0x028E1A1,00},{0x01797F1,0x06},{0x048F321,0x0D},{0x054F406,0x73},{0x053F281,0x03},{0x1E31111,0x09},{0x0D42101,0x05},{0x30217B1,0x29},{0x0057321,0x03},{0x08311E6,0x11},{0x0541120,00},{0x00361B1,0x1F},{0x0175461,0x01},{0x0F00000,0x02},{0x0A21B14,0x80},{0x03FB300,0x80},{0x0F0AB08,00},{0x1B29510,0x11},{0x0069510,00},{0x0F0F000,00},{0x0B69800,0x08},{0x008F800,0x08},{0x202FF4F,00},{0x3F6F601,0x0F},{0x300EF9E,0x80},{0x0D8A705,00},{0x035C4C4,0x03},{0x210BA2F,0x0E},{0x2F4B40F,00},{0x0B5F700,00},{0x013FA43,0xD6},{0x096F342,0x80},{0x030F930,0x01},{0x0FEF600,00},{0x0B4F600,0x20},{0x1DEB421,0x45},{0x0EEF231,00},{0x0135821,0x2B},{0x0031531,00},{0x0ADF321,0x08},{0x05DF321,00},{0x0EFD245,0x4F},{0x0EFA301,00},{0x0E7F217,0x54},{0x0E7C211,0x06},{0x0C7F219,0x2B},{0x0D7F291,0x07},{0x1084331,0x93},{0x0084232,00},{0x0084522,0x65},{0x01844F1,00},{0x0E8F318,0x62},{0x0F8F281,00},{0x0DFD441,0x8A},{0x0DFC280,0x0C},{0x0DFD345,0x93},{0x0FFA381,00},{0x02CA760,0xC6},{0x00DAFE1,0x80},{0x0EEF121,00},{0x17FD131,00},{0x02FA7A3,0x56},{0x00FAFE1,0x83},{0x00FAF61,0x91},{0x00FAFA2,0x83},{0x275A421,0x13},{0x1456161,00},{0x4FAB913,0x0D},{0x0DA9102,0x1A},{0x04FF923,0xA1},{0x2FF9122,0x16},{0x0BF9120,0x99},{0x04F9122,00},{0x0355222,00},{0x0AD9101,0x53},{0x0CD9301,00},{0x0EAF111,0xA8},{0x0EAF312,0x57},{0x0EAE111,0x97},{0x0EAE111,00},{0x0ECF131,0x8D},{0x02A5131,0x5B},{0x04A7132,00},{0x04A7131,0x19},{0x04A7131,00},{0x0AE9101,0x93},{0x0CE9302,00},{0x02FF120,0x8C},{0x3CFF220,00},{0x04FF220,0x94},{0x35FF222,00},{0x2036130,0x95},{0x21754A0,00},{0x3107560,0x89},{0x2176520,00},{0x0385621,00},{0x0786025,0x89},{0x1038D14,0x95},{0x0266620,0x89},{0x1FFF510,0x41},{0x0FFF211,00},{0x1176561,0x96},{0x2097861,0x16},{0x1095821,00},{0x0177C61,00},{0x6EF1F15,0xC0},{0x6E21115,0x40},{0x0E21111,0x40},{0x0E31111,00},{0x616C500,00},{0x6DAC600,00},{0x30E7400,00},{0x01C8521,0x92},{0x00C8F21,0x01},{0x01C8421,0x15},{0x00CAF61,0x0B},{0x01B8521,0x94},{0x00B7F21,0x05},{0x01B8421,0x15},{0x00BAF61,0x0D},{0x0158621,0x94},{0x0378221,00},{0x0098F61,00},{0x00A7321,0x9F},{0x00B8F21,00},{0x00A65A1,0x9B},{0x00B9F61,00},{0x02E7221,0x16},{0x00E8F21,00},{0x0EE7521,0x1D},{0x03E8A21,00},{0x0AC54A1,0x50},{0x01CA661,00},{0x2089331,0x96},{0x00A72A1,00},{0x0088521,0x96},{0x12A8431,00},{0x10A9331,0x8E},{0x00D72A1,00},{0x00AC524,0xA1},{0x12D6431,00},{0x10F9331,0x8D},{0x00F7271,00},{0x006A524,0x9D},{0x11664B1,00},{0x51E7E71,0x4D},{0x10F8B21,00},{0x1197531,0x8E},{0x0269B32,0x90},{0x0187321,00},{0x02F7721,0x21},{0x02F7A73,0x55},{0x01F7A21,0x93},{0x01F7A22,00},{0x01DAFA1,0x9C},{0x00D7521,00},{0x011DA65,00},{0x068A663,0x1E},{0x0588861,0x8C},{0x01A6561,00},{0x1282121,0x12},{0x0184161,00},{0x00FFF21,0x09},{0x3FAF100,0x8E},{0x3FAF111,00},{0x2C686A1,0x46},{0x0569321,0x80},{0x01B7D61,0x40},{0x01B72B1,0x23},{0x00BDFA2,0x5D},{0x00B7F61,0x80},{0x009FF20,0x36},{0x40A8F61,00},{0x40D8F61,00},{0x0FCF521,0x0F},{0x0FDF523,00},{0x0FDF926,0x16},{0x6FCF921,00},{0x0032531,0x80},{0x031A101,0xA1},{0x0175561,00},{0x446C361,0x14},{0x026C361,00},{0x63311E1,0x89},{0x0353261,0x03},{0x6E42161,0x8C},{0x6D53261,0x03},{0x0355261,0x03},{0x1471121,00},{0x03311E1,0x89},{0x0E42161,0x8C},{0x0D53261,0x03},{0x003A801,0x99},{0x005A742,00},{0x2332121,0x8C},{0x0143260,0x97},{0x1041161,0x0E},{0x0143121,00},{0x054F261,00},{0x04311A1,0x0E},{0x0741161,0x92},{0x0841161,0x8E},{0x0041DA1,0x80},{0x0346161,0x4C},{0x0055D21,0x80},{0x0CFF411,0x05},{0x1EFF411,00},{0x035D493,0x11},{0x114EB11,00},{0x035D453,0x11},{0x116EB13,0x0D},{0x1E31117,0x10},{0x2E31114,0x6E},{0x0E31111,0x80},{0x017A821,0x23},{0x0042571,00},{0x45FF811,0x4F},{0x0EFF310,00},{0x15FF630,0x12},{0x0EFF410,00},{0x00F4F2F,00},{0x30F3F20,00},{0x03FF923,0x23},{0x2FF9222,0x0A},{0x0BF9122,0x18},{0x04FA123,00},{0x000F80F,00},{0x3F93410,0x05},{0x034A121,0x17},{0x0166521,00},{0x0FA6848,00},{0x04AAA01,0x3F},{0x0FA6747,00},{0x0FA464C,00},{0x2037F21,0x18},{0x1065F61,00},{0x10C2EF0,00},{0x10C21E2,00},{0x70C2EF0,00},{0x039A321,0x8D},{0x03C7461,0x03},{0x179A3A1,0x1C},{0x14C2321,00},{0x01A7521,0x97},{0x00F8F21,00},{0x0FFF920,0xC0},{0x0FFF620,00},{0x0AFF611,00},{0x01FF933,0x80},{0x0FFF810,00},{0x2FFF500,00},{0x0FFF700,00},{0x0DFF712,0x08},{0x0DFF811,00},{0x0FFF210,00},{0x0FFF510,00},{0x1DFE920,00},{0x0CEF400,00},{0x2DFF50E,00},{0x0AFF712,00},{0x03FF800,0x03},{0x1FFF410,00},{0x2FFF012,0x11},{0x3BF8608,0x80},{0x0FFF20E,00},{0x2DF9502,00},{0x2DDF014,00},{0x0FF93F0,00},{0x3EFE40E,0x0A},{0x1EFF507,0x40},{0x0EFB402,0x03},{0x0FF9705,0x0A},{0x01FF66E,0x08},{0x3FF945E,00},{0x200F6CE,0x04},{0x3FFF21A,00},{0x3FFF040,00},{0x0FEF510,00},{0x0BCF60C,0x08},{0x38FF803,0x85},{0x0BFF60C,00},{0x2CF7800,0x08},{0x04FC80E,0x40},{0x26F9903,00},{0x1DF75CE,00},{0x2EF38E1,00},{0x03FF162,00},{0x0FF4B20,00},{0x0FBF715,00},{0x0FF47E1,00},{0x0FF47EA,00},{0x3FFE00A,0x40},{0x0FFF51E,0x0E},{0x3FFE00A,0x7C},{0x0FFF21E,0x52},{0x04E7A0E,0x81},{0x21E7B00,00},{0x35FF925,0x05},{0x0FFD524,0x40},{0x08FFA01,0x4F},{0x0FFF802,00},{0x0FFFC00,00},{0x0FFF520,00},{0x60FF331,0x94},{0x70FB135,0xD5},{0x302B133,0x63},{0x305B131,00},{0x04F270C,0x98},{0x0F8D104,0x90},{0x0F8F502,0x96},{0x0F8F402,00},{0x759F201,0x40},{0x600F701,00},{0x6F0F301,00},{0x7C9F601,00},{0x60FFF15,0xC0},{0x66FB115,0x40},{0x68FB111,0x40},{0x6EFB111,00},{0x44FF920,0x80},{0x2FF9122,0x09},{0x7BF9121,0x99},{0x64F9122,00},{0x00AAFE1,0x11},{0x00AAF62,00},{0x0F3F501,0x97},{0x1F3C504,0x9D},{0x0F7C511,00},{0x0AFC711,0x8D},{0x0F8F501,0x04},{0x0F8C302,0x06},{0x40FF923,0x90},{0x20F9122,0x1B},{0x00F9121,0x9F},{0x00F9122,00},{0x61FB015,0x40},{0x65FB111,0x40},{0x63FB011,00},{0x60FFF35,0xC0},{0x60FB135,0x40},{0x6BFB131,0x40},{0x60FB131,00},{0x0C8F121,0x13},{0x0C8F501,0x29},{0x0C8F501,0x14},{0x0C8F401,00},{0x09AF381,0x89},{0x0DFF521,0x40},{0x0C8F121,0x0F},{0x0C8F701,0x25},{0x0C8F601,0x12},{0x0C8F601,00},{0x0C5F411,00},{0x0C5F212,0x1E},{0x0076F21,00},{0x068A663,0x1B},{0x0588861,0x0A},{0x409CF61,0x05},{0x70FFF20,0x1A},{0x30FFF61,0x14},{0x00FFF61,0x1A},{0x609CF61,0x07},{0x10D5317,0x1A},{0x00E3608,0x0D},{0x03D41A1,0x9D},{0x01E6161,00},{0x0FC8561,0x15},{0x4FD8463,0x07},{0x0FD8966,0x1F},{0x6FC7761,00},{0x10A5317,0x1A},{0x0033608,0x0D},{0x0041121,0x8C},{0x3355261,00},{0x0C6F521,0x92},{0x096F461,0x8A},{0x266F521,0x90},{0x496F5A1,0x80},{0x035D493,0x91},{0x035D453,0x91},{0x56FF500,0x08},{0x40FF300,00},{0x65FF604,00},{0x38FF580,0x40},{0x66FF100,0x09},{0x65FF601,0x1C},{0x73FF580,00},{0x00F112F,00},{0x30F1120,00},{0x00F1129,0x38},{0x30F1120,0x35},{0x024F806,00},{0x7845603,0x04},{0x624D803,0x0B},{0x784F604,00},{0x624F802,00},{0x7845604,0x04},{0x624D800,0x0B},{0x784F603,00},{0x46FF220,0x14},{0x07FF400,00},{0x01FF501,00},{0x51FF487,0xC0},{0x700F701,00},{0x6C9F401,00},{0x0F7F810,0x40},{0x006F211,00},{0x002F010,00},{0x006FE00,00},{0x207F70E,00},{0x008FF12,00},{0x0F4C306,0xB5},{0x0E4C203,0x76},{0x0D4C101,0x53},{0x0E5B111,0x02},{0x0F3C301,0xA1},{0x0F3C307,0x70},{0x034B000,0xCC},{0x0F5A111,00},{0x034FB31,0x93},{0x0F7C131,00},{0x0DFB811,0x97},{0x0F7F121,0x8B},{0x0E4A115,0x6A},{0x0E4A115,0x67},{0x0E4A111,0x55},{0x0E5A111,0x03},{0x0E7C21A,0x33},{0x0E7C201,0x85},{0x0F4B111,0x1D},{0x0E4B111,0x83},{0x0E7C21C,0xBD},{0x0E6C201,0x8B},{0x0E4B111,0x52},{0x0E5B111,0x85},{0x050F210,0xA1},{0x0F0E12A,0x64},{0x020BD20,0x19},{0x0E7C112,0x03},{0x00AF021,0xA1},{0x038D620,0x03},{0x0B7F8A6,0x05},{0x017F820,0x94},{0x0057F31,0x08},{0x029F623,0x1E},{0x00A8F22,0x0B},{0x00AB028,0x5A},{0x00AB0A1,0x21},{0x00A8024,0xC0},{0x00AB021,0x09},{0x00AF0A2,0x06},{0x00AF024,0xA1},{0x00AF0A4,0x0A},{0x00AF021,0x06},{0x00FFF27,0x29},{0x00FFF21,0x07},{0x00FFF21,0x18},{0x00FFF22,0x06},{0x00AFF61,0x0E},{0x00AFF22,0xA1},{0x00AFF64,0x0A},{0x00AFF21,0x0B},{0x00FFF20,0x22},{0x00FFFA1,0x88},{0x00FFF22,0x56},{0x00FFFA1,0x84},{0x0F6EA09,0x0F},{0x0F4F518,0x8C},{0x00FEFA2,0x3E},{0x00B8F21,0x07},{0x0186223,0x1C},{0x02A6221,0x87},{0x1186223,0x19},{0x02A62A2,0x82},{0x001F201,0x21},{0x0F1F101,0x1D},{0x0E3F301,0x4B},{0x0E6F211,00},{0x030FE10,0x9F},{0x0F0E13A,0x65},{0x020BD20,0x8D},{0x0E7C112,0x07},{0x025F5E2,0x1E},{0x005EF24,0x9F},{0x004EF26,0x9E},{0x006CF24,0x06},{0x043D227,0x9A},{0x0E4E215,0x03},{0x023A7B7,0x19},{0x0E4C215,0x08},{0x043D223,0x98},{0x0E4E212,0x03},{0x023A7B3,0x19},{0x0E4C212,0x08},{0x0E6CE22,0x25},{0x0E6F421,0x03},{0x0E6F727,0x32},{0x0E5F521,0x09},{0x006F504,0x3F},{0x041F001,0x05},{0x035D208,00},{0x005F120,0x06},{0x034D201,00},{0x003F120,0x06},{0x0276621,0x1C},{0x0486621,00},{0x00A6621,0x94},{0x0E44100,0x91},{0x0046620,0x08},{0x0E65120,0x8E},{0x0066620,0x08},{0x00AAF21,0x08},{0x00AAF21,0x0C},{0x015A221,0x12},{0x00AAF21,0x02},{0x055F2A1,0x28},{0x00AAF21,0x05},{0x0CFF416,0x23},{0x0E6F205,0x69},{0x0D5F200,0x15},{0x0ECE301,00},{0x058F620,0x98},{0x05AF520,0x19},{0x009FF21,0x24},{0x00CFF20,00},{0x006F801,0x17},{0x0D5D500,0x17},{0x4E6F511,0x14},{0x0E8F500,00},{0x045FB01,0x10},{0x050FF12,0x0C},{0x034FF00,0x16},{0x027F300,00},{0x0EAF50C,0x21},{0x0E6F21F,0x21},{0x0F6F401,0x15},{0x0E7F113,0x03},{0x0E6F407,0x9B},{0x0F6A114,0x1D},{0x00FFF21,0x12},{0x0E6F112,0x04},{0x062F227,0x26},{0x062F231,0x18},{0x066F521,0x0E},{0x0E4F116,0x03},{0x015A221,0x13},{0x0DAC401,0x14},{0x055F221,0x2A},{0x0DAA401,00},{0x09CF901,00},{0x0F98701,0x03},{0x0ACF904,00},{0x0F98701,00},{0x025F261,0x22},{0x015F2A5,0x5E},{0x015F223,0x5B},{0x0C6E111,0x02},{0x006FF22,0x1C},{0x00B9F22,0x08},{0x005FA21,0x19},{0x00B9F21,0x07},{0x0F6D133,0x9A},{0x0F7F221,0x03},{0x0E4F22F,0x28},{0x0F7F224,0x8A},{0x03FF43A,0x64},{0x04FF231,0x5A},{0x024F211,0x25},{0x085F311,0x08},{0x026F211,0x23},{0x04FF43A,0x5F},{0x04FF231,0x63},{0x0D6F211,0x07},{0x03AA021,0x23},{0x097A123,0x21},{0x0F2A310,0x12},{0x0F5A020,0x05},{0x030F70C,0x23},{0x0A8F101,0x26},{0x0C6F201,0x13},{0x043F212,00},{0x054D41F,0x65},{0x0F5C411,0x42},{0x0F4B113,0x50},{0x0E5A111,0x05},{0x0AFF505,0x3F},{0x03DFD2C,0x13},{0x0B0F607,0x0F},{0x074F411,0x08},{0x022E832,0x08},{0x0F5B210,0x12},{0x021F730,0x08},{0x0F5B214,0x0D},{0x025F5E2,0x20},{0x0065F24,0x06},{0x004EFE2,0x24},{0x005EF24,0x21},{0x004EF26,0x9F},{0x002EFE2,0xAA},{0x003EF24,0xA1},{0x003EF26,0xA4},{0x0065F24,0x03},{0x016D122,0x9A},{0x0055572,0x06},{0x0F6C102,0xD9},{0x2055571,0x0D},{0x012F322,0x1D},{0x0054F22,0x04},{0x013F321,0x91},{0x0054F22,0x80},{0x015F322,0x1D},{0x0065F22,0x05},{0x015F321,0x91},{0x0075F23,0x80},{0x295F520,0x90},{0x353F411,00},{0x353F411,0x09},{0x0FAF52F,0xB2},{0x0FAF423,0x64},{0x0FAE323,0x66},{0x0FAF321,0x03},{0x036D122,0x9A},{0x0055572,00},{0x4F6C102,0xD9},{0x2055574,0x07},{0x0D6F328,0xA2},{0x0F9F423,0x5F},{0x0F8E223,0xA6},{0x0E8F301,0x03},{0x01AD1A1,0x2C},{0x00A9F22,0x8F},{0x00A9F22,0x0F},{0x00A9F22,0x08},{0x020FE70,0x13},{0x0E9C212,0x80},{0x07FBC20,0x11},{0x0E9C212,0x05},{0x020FE10,0x12},{0x0E7C212,00},{0x053BD00,0x15},{0x0E7C212,0x07},{0x0E54151,0xA9},{0x0E8F652,0x63},{0x0E8D151,0x9B},{0x0E6C251,0x80},{0x0C8F621,0x1D},{0x0F8F821,0x23},{0x0F8F420,0x20},{0x0F8F320,00},{0x058F520,0x9B},{0x059F520,0x19},{0x089F320,0x19},{0x00CFF20,0x07},{0x061F800,0x2B},{0x0EAF582,0x15},{0x0FFF420,0x1B},{0x097F400,00},{0x0E54711,0x29},{0x0E68511,0x1E},{0x0E8F512,0x5E},{0x0E6C251,0x40},{0x010F101,0x35},{0x0C2F101,0x17},{0x0C4F307,0x12},{0x0E3F212,00},{0x0DFF63C,0xA7},{0x0DFF521,0x18},{0x0D7F220,0x1A},{0x0E8F320,00},{0x0A0F400,0x05},{0x0A7F101,0x26},{0x0C5F201,0x12},{0x019AA2F,00},{0x0CFF9A2,0x1F},{0x015FAA1,0x9F},{0x00B7F21,0x06},{0x1E54141,0x40},{0x0AE7101,0x1E},{0x0EE8101,00},{0x0AE7101,0x20},{0x016D322,0x9A},{0x006C524,0x61},{0x02764B2,0x09},{0x0066231,0x1E},{0x0E7A241,0x80},{0x0AE7101,0x1C},{0x2129A13,0x97},{0x0119B91,0x80},{0x0056F22,0x56},{0x0094F31,0x0A},{0x0056F22,0x59},{0x0094FB1,0x0C},{0x1298920,0x1F},{0x1268532,0x5F},{0x0159AA0,0x4C},{0x01A8D22,0x03},{0x0E97102,00},{0x0014131,0x99},{0x0475421,0x1D},{0x0097F21,0x07},{0x0476421,0x19},{0x0087F61,0x0B},{0x0176421,0x98},{0x0098F21,0x07},{0x0176421,0x17},{0x0087F61,0x0F},{0x0296321,0x22},{0x00A7F21,0x03},{0x0186521,0x1B},{0x00A7F61,0x0D},{0x0156220,0x9A},{0x0E67141,00},{0x02651B1,0xDB},{0x0E65151,0x87},{0x02365A3,0x1C},{0x0059F21,0x1C},{0x003DFA1,0x1A},{0x00BDF21,0x07},{0x0014131,0x20},{0x04AF823,0xB5},{0x0C5D283,0x52},{0x0E6F414,0x99},{0x0D5F280,00},{0x0FAF40C,0x37},{0x0F4C212,0x2B},{0x053F685,0x64},{0x0E4F191,00},{0x006F600,0x35},{0x0E9F51F,0x25},{0x000F023,0x5E},{0x0E5F280,00},{0x0F5F50C,0xA9},{0x0F5F2A1,0x05},{0x0F6F307,0x31},{0x0F6F281,0x04},{0x0E5F14F,0x69},{0x052F605,0x2D},{0x0D5F281,0x03},{0x0E6F482,0x0F},{0x03AFE00,0x26},{0x0F6F380,0x03},{0x0F5F787,0x10},{0x0F5FD2C,0x8E},{0x0F5F427,0x20},{0x0F4F827,0x20},{0x0F5F421,00},{0x097CB05,0x9F},{0x0D5E801,00},{0x035F705,0x28},{0x0E6E401,0x05},{0x0095FE1,0x58},{0x0076FE1,0x03},{0x054890A,0x6C},{0x063A726,0x63},{0x0094F21,0xCE},{0x0083F61,0x02},{0x00F7F04,0x30},{0x0CFF5EA,0xA9},{0x00F5F21,0x1C},{0x00AAF61,0x06},{0x0549963,0x98},{0x06AA768,0xA9},{0x0095F61,0xD1},{0x0097F61,0x03},{0x0549963,0xD4},{0x06AA768,0x5E},{0x0095F61,0xC9},{0x0097F61,0x06},{0x0B643A1,0x2A},{0x0B6F6A3,0xB0},{0x0067FA1,0x2C},{0x0066F61,0x02},{0x053F101,0x73},{0x021A121,0x92},{0x116C221,0x40},{0x024A80F,0xB8},{0x005DF02,0x03},{0x035A70A,0xA2},{0x07372D2,00},{0x095F342,0x80},{0x020D933,0x08},{0x0E4B211,0x08},{0x02278B0,0x06},{0x0E4B214,0x0D},{0x10475A0,0x12},{0x0057221,0x40},{0x0F1F007,00},{0x0349800,00},{0x1137521,0x92},{0x0B47182,0x40},{0x6B5F100,0xD5},{0x6B8F100,0x51},{0x0F0F601,0x3F},{0x0E2F01C,0x1C},{0x003F103,00},{0x093F0A0,00},{0x025C5A2,0x20},{0x004EF26,0x9C},{0x0068F24,0x02},{0x0064131,0x1C},{0x03892A1,0x80},{0x0064131,0x1B},{0x02882A1,0x80},{0x0156220,0x98},{0x0267321,00},{0x02651B1,0xD1},{0x0265171,00},{0x0766321,0x93},{0x0167CA1,00},{0x1168321,0x4D},{0x0269CA1,00},{0x163F401,0x12},{0x174F111,00},{0x201F130,0x44},{0x083F001,0x83},{0x11542A1,0x40},{0x090F674,0xC1},{0x0A0B264,0x02},{0x074F111,00},{0x0117F27,0x0E},{0x0441122,00},{0x0111122,0x15},{0x0121123,00},{0x053F101,0x59},{0x0FFF691,00},{0x0F4F511,00},{0x3087631,0x08},{0x00F6531,00},{0x019D083,0x5D},{0x017F002,0x80},{0x019D083,0x58},{0x013F6A6,0xE5},{0x1239722,0x44},{0x013457A,00},{0x1239721,0x8A},{0x0134572,0x80},{0x0FFF4F1,0x02},{0x1E26301,0x16},{0x01EB821,00},{0x1226341,0x8F},{0x0024471,0x9D},{0x01E8831,00},{0x002A434,0x54},{0x0427575,0x40},{0x0FFF09E,0x07},{0x00F3F00,00},{0x1217131,0x40},{0x0066222,0x40},{0x131F231,0x47},{0x0066F21,00},{0x06764A1,0x80},{0x0FE4171,0x40},{0x1218131,0x4D},{0x0167423,0x40},{0x151D203,0x1D},{0x278F301,00},{0x0F0F09E,0x07},{0x063F300,00},{0x0F7B096,00},{0x00FFFE0,00},{0x3199B85,0x49},{0x0297424,00},{0x0FFA691,00},{0x0F45511,00},{0x000A821,00},{0x0136572,0x80},{0x061F217,0x6C},{0x0138572,0x80},{0x08C6320,0x19},{0x02F9520,0x80},{0x1F5E510,0x46},{0x162E231,00},{0x24FF60E,0x40},{0x318F700,00},{0x0C8F60C,0xC0},{0x354B506,00},{0x095D507,0xC0},{0x0F0E02A,0x52},{0x031FF1E,0x54},{0x0745451,00},{0x0756591,00},{0x002A414,0x54},{0x0427555,0x40},{0x0115F31,0xC5},{0x0069222,0x40},{0x0B5F704,00},{0x04FF60E,0x40},{0x218F700,00},{0x0297721,0x89},{0x00B9721,0x80},{0x12DC331,0x8A},{0x00F7861,00},{0x07A6161,0x99},{0x00AC121,0x80},{0x07A6161,0x9A},{0x04FF600,00},{0x075F80F,0x80},{0x2B78A03,00},{0x059A490,0x0B},{0x4C86590,00},{0x055A210,0x0A},{0x4766600,00},{0x059FA00,0x05},{0x09AF500,00},{0x3751304,00},{0x053F101,0x4E},{0x065D131,0x0C},{0x014F201,0x22},{0x097F201,0x08},{0x07CD301,0x10},{0x001F141,0x4E},{0x188D251,0x0A},{0x134A401,0x0A},{0x0A6C301,0x09},{0x103A361,0x28},{0x022C411,0x05},{0x010C733,0x84},{0x033D311,0x8A},{0x0188232,0x1C},{0x0076061,0x91},{0x0100132,0x80},{0x0337212,0x8F},{0x055F587,0x91},{0x054F022,0x13},{0x013F218,0x4D},{0x0E3C1E1,0x15},{0x043FA07,0x51},{0x045F341,0x11},{0x015F901,0x0C},{0x0F0FE04,00},{0x0B5F6C2,0x0C},{0x032B6B3,0x4A},{0x031D190,0x0E},{0x011F111,0x8A},{0x0B3F101,0x4F},{0x10FFF22,0x8D},{0x00FFF21,0x87},{0x0B5F708,0x07},{0x0CFD001,0x11},{0x107E465,0xD7},{0x078F241,0x8C},{0x11F4001,0x42},{0x11F8002,0x0B},{0x0038121,0x12},{0x00C6171,0x92},{0x00BD224,0x4F},{0x00B5231,0x16},{0x143F401,0x49},{0x074F111,0x11},{0x133FF01,0x80},{0x077F111,0x17},{0x249A320,0x0A},{0x039C411,0x0C},{0x1E7C271,0x09},{0x018F131,0x13},{0x01FF201,0x16},{0x047F701,0x0D},{0x10BB021,0x08},{0x057E221,0x14},{0x010F631,0x02},{0x016E233,0x1A},{0x267AA01,0x17},{0x013C603,0x80},{0x1539321,0x1B},{0x08AC311,0x0B},{0x07BE001,0x4E},{0x098E212,0x12},{0x126F531,0x49},{0x0C8E111,0x0F},{0x023F221,0x1C},{0x0D6B212,0x0D},{0x0B8F413,0x13},{0x0DBF111,0x18},{0x147D621,0x88},{0x00BF431,0x8E},{0x175A501,0x86},{0x0A48251,0x16},{0x019D531,0x89},{0x08B8352,0x91},{0x0035171,0x1C},{0x0175421,0x0D},{0x0155471,0x1C},{0x0495321,0x11},{0x0035171,0x56},{0x0175461,0x10},{0x0175421,0x0C},{0x10351F1,0x16},{0x01754A1,0x0D},{0x0038171,0x0E},{0x017B601,0x0E},{0x075F502,0x2A},{0x0F3F201,0x8B},{0x117ED40,0x80},{0x069C541,0x89},{0x1DAD0A1,0x17},{0x1D69012,0x0D},{0x1DAD0A1,0x11},{0x0D69012,0x16},{0x1DAD061,0x11},{0x1D69012,0x11},{0x2DAD021,0x11},{0x1D69091,0x11},{0x207F0A0,0x17},{0x03C7222,0x16},{0x307F020,0x1A},{0x00C7022,0x16},{0x3078020,0x32},{0x00C7022,0x12},{0x20B73A1,0x13},{0x246A500,0x09},{0x00753B1,0x19},{0x067D061,0x13},{0x0064131,0x1F},{0x036A061,0x0F},{0x0586361,0x19},{0x018A021,0x12},{0x05A6321,0x9F},{0x01A7A21,0x80},{0x0577261,0x19},{0x017A021,0x12},{0x0777261,0x15},{0x017A021,0x14},{0x0577361,0x19},{0x017A021,0x13},{0x00A6331,0x16},{0x00B63A1,0x12},{0x00E7321,0x0E},{0x00E6361,0x15},{0x00A6331,0x13},{0x00B6321,0x14},{0x0178E71,0xC3},{0x00E8B22,0x13},{0x0389261,0x89},{0x06FF4A1,0x27},{0x01D53A1,0x8A},{0x24369C1,0x1C},{0x00DBD21,0x0C},{0x0537901,0x0D},{0x20DBD21,0x0D},{0x0F0F530,0x35},{0x09BF034,0x13},{0x09BF032,0x10},{0x047F021,0x1B},{0x078F012,0x16},{0x0FFF001,0x0B},{0x088F202,0x17},{0x25368C1,0x14},{0x10DBD61,0x0C},{0x014F6A1,0x09},{0x0EAF102,0x19},{0x02811A1,0x20},{0x0187121,0x11},{0x047F121,0x11},{0x078D012,0x19},{0x232B583,0x52},{0x035D221,0x14},{0x1137323,0x1C},{0x0229331,0x14},{0x0BF1182,0x10},{0x38C9301,0x16},{0x1E884A1,0x19},{0x0487061,0x95},{0x357B260,0x0E},{0x13C9022,0x91},{0x3679400,0x4E},{0x056B191,0x0E},{0x23AACA3,0x18},{0x0BAC301,0x12},{0x068C2A1,0x20},{0x04872A1,0x0D},{0x23A9CA3,0x17},{0x04A9241,0x95},{0x1A3C282,0x18},{0x1F6E201,0x11},{0x3C3A621,0x14},{0x144E311,0x0C},{0x243F702,0x18},{0x027DC01,00},{0x001A021,0x02},{0x0612102,0x18},{0x24C5803,00},{0x11FF315,00},{0x049C441,0x08},{0x026F741,0x03},{0x038FA00,0x06},{0x07BF701,00},{0x20A60E0,0x1D},{0x228F00E,00},{0x10A60E0,0x1D},{0x227F0F0,0x0B},{0x017F6C1,00},{0x05CA800,0x0F},{0x07FD600,00},{0x218F201,0x09},{0x06BE601,0x04},{0x246A321,0x06},{0x026C511,0x04},{0x248A721,0x0A},{0x006C801,0x04},{0x047A34F,0x03},{0x138B703,00},{0x015C801,0x04},{0x017A30E,0x03},{0x119B602,00},{0x015C801,0x07},{0x286F30D,0x07},{0x148E404,0x03},{0x248A721,0x0C},{0x025C801,0x07},{0x473A128,0x07},{0x264A329,00},{0x344F427,0x09},{0x254F526,00},{0x15B8308,0x11},{0x32AC60A,00},{0x473A048,0x0D},{0x052F221,0x4F},{0x073D231,00},{0x050F201,0x4B},{0x076D201,0x03},{0x0F9F131,0x8E},{0x0F9F331,0x80},{0x0F9F332,0x81},{0x061F216,0x4F},{0x074F211,0x0A},{0x0617216,0x4F},{0x0B2F311,0x08},{0x021AC91,00},{0x016DA85,0x4D},{0x005F981,0x80},{0x065FE05,0x05},{0x085F8C4,00},{0x096F527,0x1F},{0x057F521,0x03},{0x074F217,0x0B},{0x00FFF64,0x86},{0x20BD8F0,0x93},{0x10BB3F2,0x07},{0x4069FB2,0x43},{0x10F95B0,00},{0x00F9033,0x05},{0x10BF224,0x4F},{0x00B5231,0x10},{0x0035121,0x15},{0x06742A2,0x80},{0x0AFF5E1,0xD0},{0x10FF4E1,00},{0x001FF11,0x8D},{0x031F121,0x40},{0x044F406,0x85},{0x0BF73C8,0x12},{0x09FF4C4,0x03},{0x0B69402,00},{0x0268301,00},{0x0EEC101,0x62},{0x0DEF302,00},{0x0EFF231,0x1E},{0x1B57431,0x0B},{0x0B8D423,00},{0x2035130,0x1C},{0x24753A0,00},{0x3115230,0xD0},{0x1254131,0x80},{0x11152B1,0xC5},{0x1FE41B2,0x80},{0x07572C1,0xCA},{0x1FE61C1,0x80},{0x0A7F131,0x50},{0x0C6F731,0x80},{0x171F502,0x60},{0x083F211,0x40},{0x2005130,0x1C},{0x2655420,00},{0x01151B1,0x8B},{0x1817021,0x16},{0x12C7322,0x07},{0x0537141,0x4F},{0x07C62C2,0x40},{0x173F141,0x4F},{0x074F241,0x10},{0x10691C1,0x0F},{0x20562C1,00},{0x0655201,0x1D},{0x0767301,00},{0x0AE71E1,0x15},{0x09E81E2,0x0A},{0x029BB21,0x8E},{0x00A9021,0x80},{0x09E81E1,0x0A},{0x2AE71E0,0x23},{0x19E80E2,00},{0x0687121,0x4E},{0x05E5232,00},{0x05B7111,0x56},{0x07B5212,00},{0x009F021,0x94},{0x00A9024,0x05},{0x00E8BA1,0x80},{0x02495A1,0x1D},{0x02A60A1,0x85},{0x0195132,0x9A},{0x0396061,0x8B},{0x030F5A2,0x12},{0x03A61A1,0x8B},{0x00457E2,0x6D},{0x0775761,00},{0x0C70CF1,0x9A},{0x0A560F1,0x80},{0x0537102,0x4F},{0x07C5211,0x05},{0x007F804,0x08},{0x04FF660,0x03},{0x00F7660,0x04},{0x00D67E1,0x04},{0x0F55551,0x80},{0x0F55501,00},{0x0339661,00},{0x02B5521,0x02},{0x0F2F251,0x0D},{0x2F2F241,00},{0x091A311,0x80},{0x094C503,0x80},{0x145F171,00},{0x044F423,00},{0x251B1E0,0x16},{0x275E0F0,0x03},{0x102FF51,0x03},{0x002FF01,0x08},{0x11122F1,0x46},{0x02E31F1,0x80},{0x0FF5011,0x80},{0x0FF1000,0x12},{0x002A4B4,0x87},{0x04245F5,0x80},{0x01111F1,0x41},{0x04245F7,0x80},{0x1007861,0x54},{0x247A260,0x03},{0x0417F21,0x56},{0x0213521,00},{0x301F171,00},{0x001F131,0x40},{0x074F219,00},{0x088F508,00},{0x20C8B22,0x05},{0x1037531,0x1C},{0x0445462,00},{0x0427880,0x4D},{0x0548595,00},{0x072F107,0x48},{0x004FC08,0x80},{0x0FFF835,0x44},{0x075F511,00},{0x1068F02,0xC0},{0x0F5D510,0x08},{0x10B5F01,0x80},{0x2056651,00},{0x0066642,0x05},{0x000200E,00},{0x001210E,00},{0x08785F4,0x50},{0x09974F3,0x80},{0x04CA800,0x03},{0x045D600,0x07},{0x0D8BB00,0x05},{0x0FED500,0x05},{0x3F0F026,00},{0x3B7FA00,00},{0x2F5F710,0x07},{0x2F488F2,0x0A},{0x2F6A6D0,0x81},{0x098C601,0x08},{0x1B5B801,0x01},{0x1B5B801,0x03},{0x098C601,00},{0x1A58801,0x02},{0x1A58801,0x08},{0x1A58601,0x06},{0x1A58601,0x09},{0x0679610,00},{0x0B79311,0x0D},{0x1D5F601,0x82},{0x0F6C504,0x0C},{0x0D5C503,0xC9},{0x1A8C601,0x06},{0x115B50E,0x07},{0x2DC5800,0x0E},{0x2469600,0x0F},{0x078950E,0xC7},{0x078950E,0xC3},{0x078950A,0xC7},{0x078950A,0xC0},{0x165E7D1,0x05},{0x094E701,00},{0x0679601,00},{0x026B401,00},{0x0679611,00},{0x2A87700,0x0F},{0x2D66A00,0x0E},{0x3044952,0x07},{0x3024152,0x07},{0x1D65A00,0x0E},{0x19F5310,0x0E},{0x1B6F804,00},{0x1B7F804,00},{0x1DC5C00,0x07},{0x07AF4D5,0x10},{0x072F2D5,0x10},{0x050F102,0x50},{0x076D201,0x0E},{0x050F101,0x4B},{0x050F113,0x50},{0x011FF32,0x92},{0x013FF01,0x8B},{0x010FF34,0x92},{0x004FF03,0x0B},{0x000F153,0x4E},{0x086D251,0x11},{0x0E5F828,0xCF},{0x0FFC021,0x0B},{0x0E5F8E2,0xCA},{0x00EC0E1,0x0B},{0x0FFF92C,0xD4},{0x0FFC0A1,0x0B},{0x0E5F82B,0xCA},{0x091F029,0xCD},{0x086E021,0x0B},{0x001F024,0xD0},{0x001F023,0xC8},{0x001B064,0xC9},{0x086F061,0x0B},{0x010A133,0x85},{0x0237215,0x8B},{0x010A131,0x85},{0x0337315,0x8B},{0x030A131,0x81},{0x074C216,0x8B},{0x07BF003,0x8A},{0x07BF402,0x8B},{0x07BF401,0x80},{0x07BF223,0x8A},{0x0337212,0x8B},{0x0337314,0x8B},{0x08E7331,0x16},{0x09E8021,0x0B},{0x07E7330,0x16},{0x0733331,0x94},{0x097A021,00},{0x073D331,0x94},{0x097A021,0x0C},{0x053F131,0x45},{0x027F232,0x0B},{0x001F213,0x0C},{0x0B6F215,0x18},{0x001F211,0x0C},{0x0B6F211,0x0B},{0x004FE11,0x0A},{0x0BDF211,0x0B},{0x011CA53,0x4D},{0x0F171E1,0x13},{0x011BA12,0x40},{0x03124F1,0x0C},{0x08E7261,0xA7},{0x01A50E1,0x8B},{0x0133218,0x4D},{0x0E351E1,0x0C},{0x0411217,0xC0},{0x0311331,0x8B},{0x055F503,0x8F},{0x033F321,0x8B},{0x011FA13,0x4D},{0x0F1F1E1,0x0C},{0x0154011,0x43},{0x0F8A1F1,0x0C},{0x0978211,0x03},{0x0F2F0E4,0x4C},{0x053D105,0x40},{0x0715114,0x0B},{0x01727F1,0x01},{0x0185120,0x0B},{0x01132F1,0x18},{0x013F1E1,0x0B},{0x053F173,0x48},{0x006F171,0x17},{0x0117171,0x8D},{0x0157261,0x4B},{0x061F2D7,0x4F},{0x0B2F1D2,0x0B},{0x0FFF001,0x11},{0x0F8F001,0x0B},{0x0114131,0x8B},{0x0132261,0x0B},{0x021FF31,0x8B},{0x0154461,0x0B},{0x0153261,0x0B},{0x013FD71,0x1C},{0x0D6E721,0x0B},{0x0675421,0x0B},{0x0175421,0x0B},{0x0495321,0x0B},{0x0175461,0x0B},{0x075F002,0x29},{0x033F401,0x8B},{0x053F101,0x49},{0x074F111,0x0B},{0x053F101,0x89},{0x053F102,0x89},{0x053F102,0x80},{0x053F108,0x4B},{0x02CD321,0x15},{0x02CC321,0x8B},{0x0F2D401,0x18},{0x08AC421,0x8B},{0x07BF001,0x4E},{0x0C8F411,0x0B},{0x0ABF001,0x44},{0x0ABF311,0x0B},{0x0C8F453,0x0E},{0x0BBF111,0x0B},{0x0C8F253,0x0B},{0x0C5F211,0x0B},{0x04CB421,0x15},{0x0AC9421,0x0B},{0x01C9421,0x15},{0x0AC6421,0x0B},{0x08F7721,0x16},{0x02A60A1,0x8B},{0x0BF7721,0x19},{0x0AFD6A1,0x13},{0x02A60E2,0x8B},{0x02495A2,0x1D},{0x130F4A4,0x12},{0x02A60E1,0x8B},{0x00E6321,0x16},{0x00E6321,0x0B},{0x00B6321,0x0B},{0x00A6321,0x1B},{0x00A6320,0x1B},{0x0076061,0x8B},{0x0145132,0x18},{0x03662E1,0x8B},{0x0178731,0xC3},{0x00E8B22,0x0B},{0x00E8B22,0x0F},{0x0176E70,0x8D},{0x00E6B22,0x0B},{0x006F224,0x4F},{0x0065231,0x0B},{0x0076431,0x1B},{0x067D061,0x0B},{0x0066131,0x1B},{0x036D261,0x0B},{0x0063131,0x1F},{0x0365061,0x0B},{0x036A061,0x0B},{0x0565321,0x9A},{0x016A021,0x8B},{0x0585361,0x19},{0x018A021,0x0B},{0x017A021,0x0B},{0x0A67121,0x1B},{0x096A121,0x0B},{0x044F585,0x91},{0x045F0A1,0x0B},{0x033F507,0x51},{0x025F061,0x0B},{0x021FF13,0x8C},{0x00DF338,0x8C},{0x033F5B1,0x40},{0x054F022,0x0B},{0x031D190,0x0B},{0x0F0FEC4,0x0E},{0x0B5F6C2,0x12},{0x015DA05,0x4E},{0x013F001,0x80},{0x09AF231,0x44},{0x027F032,0x0B},{0x002A4B0,0xC4},{0x04240D7,0x8B},{0x0F0F0CA,0x84},{0x06259CC,0x0B},{0x09BF035,0x0B},{0x0530907,0x40},{0x094F605,0x0B},{0x025DA09,0x4E},{0x015F101,0x0B},{0x0A0F406,00},{0x046F600,0x0B},{0x0DC5C00,0x0B},{0x07FF511,0x0B},{0x07FF511,0x0E},{0x07FF511,0x10},{0x0530900,0x40},{0x0A8F211,0x86},{0x0A8A001,0x8B},{0x070F200,0x50},{0x072F213,0x0B},{0x01111F0,00},{0x01111E0,0xCB},{0x072F212,0x0B},{0x04FA800,0x0B},{0x0BFF80C,00},{0x0BFF704,00},{0x0BFF501,00},{0x0BFF701,00},{0x00F10DE,00},{0x0790825,0x9A},{0x0E6E385,0x5B},{0x0E6F315,0x62},{0x042B401,0x12},{0x00AF601,0x07},{0x036D600,00},{0x055FF10,0x06},{0x3E2E20F,00},{0x1E3F308,0x0A},{0x366F30F,00},{0x1E4F408,0x0A},{0x074F111,0x04},{0x153F101,0x89},{0x074F111,0x07},{0x160F101,0x4D},{0x07BD211,0x01},{0x153F181,0x49},{0x150F101,0x4F},{0x07CD201,0x05},{0x118F603,0x1C},{0x0F9F212,0x04},{0x1F9F131,0x0E},{0x0F9F331,0x04},{0x074F111,0x01},{0x1100133,0x07},{0x0037D14,00},{0x1F0F517,0x53},{0x0F3F201,0x09},{0x1FFF5A3,0x47},{0x0FFF5A2,00},{0x154F606,0x73},{0x105F012,0x15},{0x003F011,0x80},{0x108F006,0x0E},{0x008F001,00},{0x101FF64,0x1B},{0x062F32E,00},{0x4049404,00},{0x0059500,00},{0x1118371,0x03},{0x0828F73,0x80},{0x111C371,0x03},{0x082CF73,0x80},{0x10381F0,0xD9},{0x005F171,0x85},{0x10F75F2,0x81},{0x00FFFF0,0x0E},{0x1037532,0x1C},{0x0F8B062,0x04},{0x00B5231,0x08},{0x1F09091,0x88},{0x0FC4082,0x80},{0x10BF261,0x68},{0x00B5270,0x10},{0x131F121,0x0F},{0x045C302,0x03},{0x112F101,0x10},{0x082F101,0x04},{0x1518503,0x5E},{0x071D211,0x07},{0x113F201,0x11},{0x0F88401,00},{0x121FF13,0x16},{0x14AF8F0,00},{0x047F022,0x0A},{0x11797F0,0x01},{0x018F161,0x0A},{0x11797F1,0x01},{0x018F126,0x08},{0x078F101,0x0A},{0x10FF7E1,0x9A},{0x00BF9B1,0x09},{0x1618221,0x12},{0x0619522,0x05},{0x18AE221,0x11},{0x0A8E421,00},{0x025F301,0x05},{0x1937511,0x4F},{0x082F501,0x05},{0x119D531,0x88},{0x125F871,0x40},{0x085F171,0x08},{0x1035131,0x1C},{0x0065461,0x04},{0x1035131,0x16},{0x11152B0,0xC5},{0x00531B1,0x82},{0x1B69401,00},{0x0268300,0x14},{0x11171B1,0x82},{0x0154261,0x04},{0x171E4B1,0x8B},{0x0E5E461,0x40},{0x1829531,0x9C},{0x0B1F130,0x88},{0x1847824,0x9A},{0x004B000,00},{0x111A1B1,0x81},{0x0157261,0x04},{0x11161B1,0x81},{0x0153261,0x04},{0x1339111,0x8A},{0x0345122,0x80},{0x11171B1,0x85},{0x015E5D1,0x5B},{0x0057B72,0x82},{0x04964F2,0x90},{0x0069261,0x06},{0x1537101,0x4F},{0x00CB222,0x08},{0x1526641,00},{0x0768501,00},{0x0177E61,0x92},{0x0098E21,00},{0x0176E60,0x92},{0x0096E21,0x10},{0x165C201,0x1D},{0x006F321,0x0C},{0x0177E61,0x8F},{0x0098E21,0x04},{0x15A5321,0x9F},{0x01AAA21,0x82},{0x1AE71E1,0x15},{0x00E81E2,0x08},{0x1AE7081,0x12},{0x09EB023,0x09},{0x1AE7081,0x18},{0x1FB7012,0x92},{0x0FF5014,0x04},{0x0FF5013,0x06},{0x1FB7011,0x92},{0x0FF5013,0x02},{0x1FB7010,0x92},{0x0FF5011,0x0A},{0x0FF5011,0x02},{0x119D530,0xC8},{0x01B6171,0x82},{0x11B5132,0x1A},{0x00BA261,0x0A},{0x1297461,0x12},{0x0097362,0x80},{0x05FF732,0x43},{0x01F65B1,0x80},{0x05F87B1,0x83},{0x01F67B0,0x83},{0x05F8732,0x83},{0x15F87A2,0x03},{0x01F65B1,00},{0x0177E62,0x92},{0x0098E21,0x0C},{0x1C70CB3,0x9A},{0x0A560B2,0x80},{0x15F6721,0x83},{0x0FF5501,0x86},{0x11797F1,00},{0x0E8F121,0x04},{0x31797F1,00},{0x0E8F121,0x06},{0x15F8781,0x83},{0x01B6580,0x80},{0x1F69401,0x80},{0x009F426,0x04},{0x1F69442,0x80},{0x008F423,0x04},{0x10875E6,0x66},{0x00963E3,00},{0x1177426,0x8E},{0x017F5A0,0x83},{0x116F1A1,0x88},{0x008F421,0x02},{0x143C373,0x0C},{0x0432370,00},{0x04914F2,0x90},{0x0665261,0x08},{0x11797B1,0x06},{0x018F161,0x08},{0x1176E81,0xC5},{0x0048B22,0x08},{0x100586E,0x18},{0x0012632,0x80},{0x104C113,0xD3},{0x0075161,0x0A},{0x107F021,0x8E},{0x0089022,0x40},{0x111D570,0xC8},{0x0112671,0x82},{0x1427887,0x4D},{0x00485B6,0x02},{0x11171B1,0x8B},{0x0154261,00},{0x654F699,0x33},{0x003F2A1,0x08},{0x1537101,0x49},{0x0047132,0x0A},{0x102A4B4,0x07},{0x00245F6,00},{0x10214B3,0x07},{0x00285F5,00},{0x015E5D1,0x9B},{0x0027B72,0x83},{0x1339660,00},{0x02B5520,0x03},{0x153F101,00},{0x053F108,00},{0x11FF721,0x0A},{0x03FF523,00},{0x088F108,00},{0x110F201,0x11},{0x004F508,00},{0x105F011,0x15},{0x003F010,0x40},{0x1176E30,0x61},{0x00C8B21,0x0C},{0x0075462,0x05},{0x2FB7010,0x52},{0x106FF09,0x4D},{0x004FF84,00},{0x106FF09,0x0D},{0x007FF84,00},{0x1847825,0x9A},{0x004B001,0x06},{0x340FF55,0x80},{0x007FF12,00},{0x340FF90,0x80},{0x003FF10,0x11},{0x040FF10,0x80},{0x003FF10,0x8C},{0x640FF10,0x37},{0x003FF10,0x0E},{0x000FF4E,00},{0x0FD1F40,00},{0x1945315,00},{0x0757800,00},{0x3063F72,0x85},{0x0075F20,0x0A},{0x0021E60,00},{0x1F0F000,0x2E},{0x0FF5F09,00},{0x111FE3E,00},{0x019F123,0xC0},{0x111FEB0,00},{0x019F1A0,0xC0},{0x0022C60,00},{0x000FF0D,00},{0x006F020,00},{0x0000000,00},{0x0E8E800,0x0D},{0x0F8A500,00},{0x038EC12,0x06},{0x009FA00,0x06},{0x2F5F02F,00},{0x207FA0F,00},{0x077F005,00},{0x0EDFA00,00},{0x0F7F700,00},{0x0F6F600,00},{0x097F700,0x03},{0x100F046,00},{0x067FE02,00},{0x0C7F700,0x03},{0x0F0F063,00},{0x2099902,0x03},{0x0C6F600,0x03},{0x1F0F043,00},{0x204FD02,0x03},{0x0F6F500,00},{0x0C5F500,0x03},{0x000F00F,00},{0x2F4F4A0,00},{0x342F809,0x06},{0x3E4F407,0x40},{0x320F413,0x4B},{0x254F800,00},{0x04F960E,0x40},{0x218B700,0x08},{0x276F502,0x1B},{0x0D6F809,0x05},{0x10070E1,00},{0x0F4A4E0,0x09},{0x3E4F404,0x44},{0x1F8F830,0x21},{0x0B6F511,0x08},{0x1F8F830,0x1E},{0x0A6F511,0x08},{0x248EB00,0x95},{0x078F700,0x0D},{0x259FB00,0x94},{0x038E700,0x0D},{0x256FB00,0x98},{0x0C7F600,0x0D},{0x1F8F832,0x85},{0x0F5F531,0x08},{0x1BAE812,0x80},{0x099F511,0x08},{0x387FD00,00},{0x0F6E622,0x08},{0x0F6F522,0x08},{0x0FEF025,00},{0x2586C03,0x93},{0x2187704,0x08},{0x3F77723,0x04},{0x2F68623,0x0A},{0x3F76623,0x04},{0x306FF80,00},{0x0176F11,0x0B},{0x0166F11,0x0B},{0x1D1F813,0x61},{0x0F5F532,0x0C},{0x1D1F813,0x6C},{0x0F6F632,0x08},{0x045FC41,0x45},{0x0C56943,00},{0x0056942,00},{0x060F205,0x51},{0x07AF414,0x80},{0x060F285,0x51},{0x0B8F294,0x80},{0x013F201,0x22},{0x043F501,00},{0x0F9F332,0x80},{0x072F212,0x0A},{0x015DA85,0x4E},{0x013F981,0x80},{0x0F0FF06,00},{0x0B5F8C4,00},{0x060F217,0x4F},{0x072F202,0x10},{0x053F103,0x0F},{0x00FFF22,0x40},{0x00F9031,00},{0x1069FB2,0xC0},{0x10FB4B0,0x80},{0x00F9033,0x08},{0x00B9231,0x10},{0x0677262,0x80},{0x1AFF5E0,0xCE},{0x021FF13,0x93},{0x101FF11,0x8B},{0x171F503,0x5E},{0x083F211,00},{0x044F406,0x80},{0x01AC1E5,0x03},{0x07EF0E7,0x40},{0x0EEC101,0x23},{0x071FB51,00},{0x078F520,00},{0x1889501,0x40},{0x003FF12,00},{0x1F7F501,0x10},{0x2F7F501,00},{0x029D521,0x4F},{0x006B332,00},{0x2035170,0x1C},{0x267B420,00},{0x21152F0,0xD0},{0x1FE91F1,0x40},{0x1FE71B1,0x80},{0x01152B1,0xC5},{0x1CF80B1,0x84},{0x0F9F131,0x9C},{0x0D5F531,0x80},{0x123B391,0x4F},{0x106F761,0x40},{0x004D010,0x80},{0x2656420,00},{0x1445462,0x02},{0x081B021,0x16},{0x12CD323,00},{0x10872E1,0xC0},{0x02BFAE2,0x89},{0x1C2F071,0x46},{0x0F2F2C1,00},{0x174F242,0x03},{0x0059100,0x0F},{0x3068200,00},{0x03BC262,0x80},{0x03BB261,0x80},{0x0655200,0x1D},{0x076A321,00},{0x12FA522,0x80},{0x11ABA21,0x80},{0x1AE91E1,0x55},{0x09EA1E1,0x0A},{0x00AB061,0x80},{0x19EA1E1,0x06},{0x19EA1E2,00},{0x07C9212,00},{0x0687120,0x4E},{0x05E9232,00},{0x05B7110,0x4F},{0x07B9250,00},{0x009F021,0x96},{0x10AC024,00},{0x10EDBA2,00},{0x019D531,0x4D},{0x00A9173,00},{0x03BB261,0x02},{0x015B022,00},{0x117A5A1,0x40},{0x18F7EE2,0xDB},{0x02A8661,00},{0x01560E1,0x40},{0x1063F54,0x85},{0x0077E01,00},{0x02AA661,00},{0x0C70CF4,0x9A},{0x0A580F3,0x40},{0x07C7211,00},{0x007F803,0x08},{0x074B201,00},{0x14FF661,0x0B},{0x00FA661,00},{0x0086882,0x90},{0x008C7F1,00},{0x1E65602,00},{0x02B6522,00},{0x303F660,0x07},{0x016F621,00},{0x0E1B311,0x85},{0x0E4A101,00},{0x1E9F251,0x41},{0x0B6F272,00},{0x002A4B3,0x87},{0x04285F5,00},{0x19041F1,0xC0},{0x005B2B1,00},{0x102FF52,0x03},{0x104FF01,0x01},{0x20FF4E0,00},{0x21133F4,0x02},{0x32E53F1,00},{0x125F243,00},{0x3CF7232,0x4D},{0x1EE5111,00},{0x0FF1001,0x12},{0x0FF5011,00},{0x00FFF7E,0x1A},{0x10F6F61,00},{0x01131F1,0x41},{0x11222F1,0x40},{0x203E5B6,0x4B},{0x14245F1,00},{0x1005872,0x18},{0x0022620,0x40},{0x202F950,0x90},{0x001FFC5,00},{0x00F4D20,0x03},{0x105FF00,00},{0x0427F35,0xD7},{0x02135A2,00},{0x303F17C,0x40},{0x001F130,00},{0x053F128,0x80},{0x0438D13,0x80},{0x074F237,00},{0x01FF201,0x0B},{0x188F521,00},{0x053F621,00},{0x10CABA1,00},{0x2035530,0x19},{0x1448461,00},{0x0427881,0x4B},{0x0558593,00},{0x272F107,0x46},{0x104FC18,00},{0x0E6F80E,00},{0x0F6F80F,00},{0x1078F03,0xC0},{0x1059F02,00},{0x097F802,00},{0x107FF00,00},{0x196C801,00},{0x086F800,00},{0x0B3F109,00},{0x00B5F01,0x80},{0x30F5F00,00},{0x2066642,00},{0x3665F54,0x0A},{0x0077F40,00},{0x005F1C0,0x51},{0x02394FB,00},{0x10FFFFC,0xC0},{0x30FFFF0,00},{0x00F5F6E,00},{0x0F0A00A,00},{0x075C586,00},{0x050F101,0x4E},{0x0D6D101,0x06},{0x054F231,0x48},{0x0C6F201,00},{0x023F503,0x47},{0x0E7D101,0x06},{0x000F113,0x54},{0x0F6D194,00},{0x15BF80C,0x71},{0x0CCD201,0x03},{0x0DAF101,0x93},{0x0E9F301,00},{0x128FB23,0x87},{0x0E8D301,0x40},{0x0A5C201,0x92},{0x0D7C201,00},{0x040FF36,0xC0},{0x0F4F311,0x80},{0x0C4F411,00},{0x0045616,0x21},{0x034F601,00},{0x0E6F318,0xD0},{0x0FFF718,0x21},{0x0D8B501,00},{0x0FFF816,0x98},{0x032FD13,0x86},{0x042FD00,0x03},{0x1058401,0x49},{0x0C5F481,0x82},{0x2E5F062,0x5D},{0x00EC060,00},{0x0FFF062,0xCF},{0x00FCF60,00},{0x00FAA30,0x57},{0x10FFF71,00},{0x14BF02C,0x55},{0x01B5071,0x08},{0x1059721,0x13},{0x1058721,0x8F},{0x0054F32,00},{0x006F223,0x53},{0x00642B1,0x0B},{0x1058721,0x50},{0x0054F31,00},{0x2F4F502,0x64},{0x0F8F301,00},{0x1FAF303,0x57},{0x0F7C301,00},{0x0F0F003,0xD5},{0x120F723,0x86},{0x0F7F401,0x40},{0x043F903,0xC0},{0x0FAF421,00},{0x0FFF101,0x43},{0x3FFF054,0x40},{0x353F100,0x49},{0x396F110,00},{0x15FF510,0x40},{0x1FFF134,00},{0x08AC321,0x80},{0x02FF131,0x8F},{0x086F131,00},{0x02FF131,0x8C},{0x0FC8201,00},{0x016F701,0x8E},{0x088F321,00},{0x016FD01,0x0D},{0x016F501,0x8C},{0x004F311,0x06},{0x06DF231,00},{0x1035171,0x1C},{0x0155221,00},{0x113FF31,0x16},{0x0366661,00},{0x0035171,0x1D},{0x0476421,00},{0x121F131,0x46},{0x0FFF611,0x05},{0x0F37211,00},{0x075F002,0x1D},{0x053F701,00},{0x1057510,0x41},{0x0F3F311,00},{0x0153061,00},{0x014C121,0x93},{0x0054161,00},{0x0223101,0x18},{0x0159041,00},{0x01FF421,0xDB},{0x0073F72,0x07},{0x0697961,0x96},{0x0677121,00},{0x069E961,0x96},{0x0665410,0x04},{0x0076431,0x1E},{0x067D021,00},{0x0078F21,0x80},{0x0586321,0x19},{0x018A021,00},{0x00F7321,0x16},{0x00F9321,00},{0x016A021,0x80},{0x0076431,0x18},{0x03E4131,0x16},{0x09EF022,00},{0x0566121,0x99},{0x100FF31,0x94},{0x0087F61,00},{0x1009831,0x8E},{0x0096F61,00},{0x1055E31,0x8D},{0x0178731,0xC1},{0x00E8BA2,00},{0x10E8BA2,00},{0x017FE71,0x0D},{0x00A6B22,00},{0x0219F32,0x48},{0x0F770B1,00},{0x03794A1,0x1F},{0x00A6521,00},{0x05F7621,0x19},{0x02A60A1,0x80},{0x0195131,0x9A},{0x0396021,0x80},{0x05084B2,0x8D},{0x0186721,00},{0x00457E2,0x52},{0x0876861,00},{0x1032171,0x96},{0x0031171,0xD6},{0x00FF032,0xF4},{0x0077621,00},{0x203F422,0xA1},{0x00CF061,00},{0x10FFC21,0x0E},{0x10FF9A1,00},{0x0558721,0x42},{0x0099021,00},{0x121A221,0x8E},{0x02A91A2,00},{0x069E962,0xAA},{0x0104100,0xC4},{0x206F760,00},{0x030F201,0x8F},{0x009F461,0x80},{0x0F45217,0xA7},{0x011E861,0x1F},{0x00327B1,0x80},{0x02C6161,0x16},{0x018F521,00},{0x001EF71,0x60},{0x0036172,00},{0x0935136,0xC4},{0x0714331,0x80},{0x175A1C1,0x51},{0x1752101,00},{0x010F4A1,0xDB},{0x0033F32,0x07},{0x1181121,0x15},{0x007CFA1,00},{0x0F1B061,0x1F},{0x0F2F1B1,00},{0x1051201,0x15},{0x0144121,00},{0x0156215,0x58},{0x004AD81,00},{0x056F523,0x48},{0x025F3A1,0x80},{0x151F261,0x4D},{0x0A5F242,00},{0x1511261,0x09},{0x0131123,0x80},{0x0F11141,0x8E},{0x0031DA1,0x06},{0x15AB061,0x94},{0x01AB0A3,0x80},{0x083F101,0x40},{0x085F108,0x40},{0x1119311,0x0C},{0x0C5A213,0x09},{0x1429811,0x06},{0x0D7F311,00},{0x0328513,0x91},{0x112E591,00},{0x2569D04,0x8F},{0x005F201,0x80},{0x1206721,0x41},{0x0206721,0x41},{0x1178731,0x48},{0x00E8B22,0x80},{0x0E5F105,0xD8},{0x0E5C302,0x80},{0x026EC07,0x0A},{0x087F702,00},{0x0155805,0x9D},{0x005EF01,00},{0x018FA17,0x18},{0x054F812,00},{0x0F3E900,0x11},{0x147F811,0x01},{0x003F310,0x80},{0x0696940,0x96},{0x0657300,00},{0x0F0F00C,00},{0x0DF270C,00},{0x2D65602,0x8D},{0x07DF011,0x0A},{0x0865611,0x89},{0x0FFF00B,00},{0x2FF120C,00},{0x05BE51C,0x1E},{0x0FA5D0C,00},{0x0FFD02C,0x40},{0x0FFF020,00},{0x200F600,00},{0x2FF4FD0,00},{0x001FF6C,00},{0x016126C,0x40},{0x0FFF30C,00},{0x1DFF60C,00},{0x0F00020,0x3F},{0x045D600,00},{0x0C8AA00,00},{0x0B7D200,00},{0x22BFB03,00},{0x00BF507,00},{0x00AFF21,0x80},{0x119F800,00},{0x342F80E,00},{0x342F80F,00},{0x342F804,00},{0x3E4F407,0x44},{0x3E4F40D,0x40},{0x3E4F408,0x40},{0x200F880,0x0D},{0x3049F90,00},{0x0B5F801,00},{0x0EFF702,00},{0x397C802,00},{0x397C802,0x40},{0x2D6F609,0x05},{0x05BE51C,0x16},{0x0FA7D07,00},{0x0FEE51C,0x16},{0x0067D07,00},{0x005F713,00},{0x005F712,00},{0x3F0E02A,0x40},{0x005FF1E,0x40},{0x3F0E02A,0x7C},{0x002FF1E,0x40},{0x053F171,0x48},{0x227F272,00},{0x121F1B1,0x46},{0x0166F61,00},{0x03EC131,0x1B},{0x203F4A2,0xA1},{0x00CF0F1,00},{0x008C782,0x0D},{0x00857F1,00},{0x0328513,0x90},{0x2569D04,0xCF},{0x0F2EB20,0x08},{0x005FF10,00},{0x0F0F029,00},{0x1DF2703,00},{0x0F6A90F,0x02},{0x2F6F90F,0xC0},{0x008CB00,00},{0x0176F11,00},{0x0166F11,00},{0x0F00006,0x3F},{0x0FFF816,00},{0x0FFF815,00},{0x094F3C1,0x8C},{0x0C8E3C1,0x40},{0x1E4E130,0x8D},{0x21FF120,0x21},{0x088F420,00},{0x100A010,0x15},{0x0F6B110,00},{0x054A1E0,0x4B},{0x0049160,0x40},{0x10575A1,0x80},{0x10580A0,0x52},{0x1056521,0x80},{0x10569A0,0x93},{0x10266E0,00},{0x0033221,0x4D},{0x1042120,0x80},{0x054A160,0x4D},{0x0049160,0x80},{0x10BA8A1,0x48},{0x128D330,00},{0x0C8A820,00},{0x117F7CE,0x21},{0x04CF9C0,00},{0x075FC01,0x21},{0x25E980C,0x80},{0x306FB0F,0x80},{0x0F5F201,0x8F},{0x0F5F201,0x06},{0x0F5F201,0x4B},{0x0F5F201,00},{0x0F5F201,0x49},{0x0F7F2C1,0x12},{0x0F9F181,0x57},{0x0F8F101,0x93},{0x0F3A101,0x80},{0x0F5F60C,0x5C},{0x0F5F3D8,0x62},{0x0F5F281,00},{0x01BF6F1,0x92},{0x006F171,00},{0x05FC772,0x14},{0x006C730,00},{0x01BAA70,0x44},{0x0058AB1,00},{0x1259723,0x93},{0x01355B1,00},{0x12998A4,0x48},{0x01646B1,00},{0x0066161,00},{0x0077121,0x13},{0x00661E1,0x89},{0x19BF302,0x9C},{0x0C9F341,0x80},{0x19EF303,0x54},{0x0E6F151,00},{0x03EF123,0x5F},{0x127F623,0x87},{0x0F7F321,0x80},{0x03FF621,00},{0x1479163,0x4A},{0x01A8421,0x05},{0x1189563,0x4A},{0x0179461,00},{0x0482029,0xA1},{0x0F7D1A4,0x80},{0x00694A1,0x1E},{0x078F171,0x13},{0x006F771,00},{0x0E8F171,0x8E},{0x076F171,00},{0x02F6131,0x5B},{0x01B7132,00},{0x097A101,0x8B},{0x0D7F221,0x40},{0x017A261,0x8A},{0x0D7A121,0x08},{0x0E5F431,0x8B},{0x075F131,00},{0x05AF2E1,0x0E},{0x007F121,00},{0x114DD31,0x15},{0x02556A1,00},{0x113DDB1,0x16},{0x00666E1,00},{0x116D171,0x49},{0x0066131,00},{0x11471A1,0x4D},{0x0057263,0x80},{0x124F1F1,0x40},{0x0156FE1,00},{0x176F502,0x1A},{0x175F422,0x1D},{0x0F3F301,0x80},{0x126B1E1,0x9B},{0x1057FA1,0x98},{0x0043F22,0x07},{0x03441A1,0x93},{0x0035161,00},{0x025C121,0x18},{0x013F431,0x5B},{0x0038A72,0x83},{0x0667161,00},{0x08662E1,0x57},{0x0198521,0x92},{0x00F8F21,0x01},{0x01775A1,0x94},{0x0078F21,0x05},{0x0368261,00},{0x1276261,00},{0x0676121,0x9B},{0x0067F21,00},{0x0177561,0x8A},{0x00774A2,0x06},{0x15772A1,0x86},{0x0177161,0x83},{0x03F5461,0x4D},{0x018A661,00},{0x1079331,0x8F},{0x0077261,00},{0x1079331,0x8E},{0x1079331,0x91},{0x0078261,00},{0x117AA61,0x4B},{0x1167E31,0x90},{0x1098B21,00},{0x02A9B32,0x90},{0x0187221,00},{0x05C85E1,0x1F},{0x01765E1,00},{0x05C88E1,0x46},{0x0077521,00},{0x2FFF122,0x51},{0x1FCF121,0x0E},{0x208FF21,00},{0x05A8622,0x46},{0x01964E1,0x80},{0x17A9221,0x8B},{0x0056F61,0x40},{0x0AFF121,0x8F},{0x0945337,0xA5},{0x005A0E1,00},{0x0759121,0x17},{0x0155561,00},{0x0035471,0x5D},{0x0432161,0x97},{0x173A161,0x1C},{0x1433161,00},{0x007CF61,00},{0x085B122,0x92},{0x025F261,0x83},{0x153F141,0x4D},{0x0F5F202,00},{0x173C3A4,0x4C},{0x02381E1,00},{0x1F3A131,0x0C},{0x0F4A233,0x80},{0x0277C21,0x49},{0x1076F22,00},{0x134DD31,0x85},{0x0165661,00},{0x207DA20,0x04},{0x0078F21,0x81},{0x0E3C303,0x80},{0x03756FA,0x40},{0x261B2B5,0x5C},{0x0A5F4B4,0x08},{0x001F6EE,00},{0x3A54FF0,00},{0x2C6F600,00},{0x277F810,0x86},{0x006FC11,00},{0x2E5F60F,00},{0x217FA0F,00},{0x037EC12,0x18},{0x2D68800,0x0F},{0x0FAAA06,00},{0x217F800,00},{0x077ED43,0x06},{0x205FD02,0x03},{0x087AD43,0x07},{0x164DD43,0x0E},{0x314EE9F,00},{0x2F4F4A0,0xCD},{0x064DD03,0x1D},{0x314FE9F,00},{0x320F413,0x8B},{0x244F800,0x47},{0x5777602,0x80},{0x2679801,0x08},{0x314FE9F,0xC0},{0x159FE01,0x41},{0x085F510,0x89},{0x60070E0,0xC0},{0x6F4A4E3,0xA0},{0x0F77720,0x11},{0x2F87811,0x13},{0x2F97869,0xC0},{0x0F9D721,00},{0x2F76769,0xC0},{0x0B2F294,0x80},{0x060F213,0x4F},{0x072F201,0x10},{0x0FAF140,0x49},{0x00F9070,0x03},{0x037F261,0x06},{0x107F132,00},{0x0277261,0x40},{0x01AC1E6,0x04},{0x02F72E8,0x12},{0x03FB4E4,0x03},{0x09F8331,0x10},{0x078F422,0x04},{0x0A7F131,0x5A},{0x0666262,0x4F},{0x03A61E1,0x80},{0x08B7261,0xA7},{0x01950E1,0x81},{0x0089782,0x0D},{0x00897F1,00},{0x0F4F251,0x0D},{0x2F4F241,00},{0x125A423,00},{0x106A222,00},{0x034F161,0x40},{0x054F466,0x85},{0x251B1E0,0x8F},{0x175E0F1,00},{0x0D3B309,0x41},{0x024F246,0x86},{0x045E271,0x89},{0x01AA363,0x82},{0x08DE471,0x15},{0x047A262,0x03},{0x3F1F151,0x0A},{0x002F931,0x49},{0x096D801,00},{0x3E4F407,0x51},{0x37CFD23,0x15},{0x0F58401,0x08},{0x035F203,0x5D},{0x4F5F401,0x08},{0x0F5F303,0x7D},{0x4F5F301,0x08},{0x0F5F203,0x55},{0x4F5F201,0x08},{0x0D5F211,0x80},{0x005F276,0x25},{0x006F27A,0x29},{0x0F2F100,0x24},{0x0F7F200,00},{0x185DC85,0x91},{0x055F401,0x0E},{0x0F6E181,00},{0x0F6E798,0x2B},{0x0F4F194,00},{0x0A7E98A,0x15},{0x0B3D407,0x9A},{0x0B4C202,00},{0x082D307,0x9A},{0x0E3A302,00},{0x156940A,0xA7},{0x132F411,0x05},{0x027A2A0,0x85},{0x023A522,0x9E},{0x02AA5A2,0x80},{0x02AA168,0x8F},{0x02AA623,0x51},{0x00AAF61,0x80},{0x00AAF61,0x91},{0x00AAF22,0x80},{0x1069FB2,0x09},{0x10B55B0,0x22},{0x066752A,0x26},{0x067702A,0x2B},{0x013C321,0x22},{0x00B7022,00},{0x0F4F505,0x29},{0x0F9F200,0x1E},{0x0F1F101,0x2F},{0x0F7F100,00},{0x0F4F405,0x29},{0x0F6F110,0x19},{0x0F1F111,0x31},{0x19F53C8,0x1C},{0x07FFAE4,0x03},{0x0049420,0x2A},{0x0A5C523,0x24},{0x0F9F200,0x21},{0x0F8F101,00},{0x02BF82A,0x24},{0x02BF620,0x2D},{0x02BF420,0x12},{0x02BF420,00},{0x0ABF82A,0x23},{0x02BF620,0x32},{0x0285130,0x5B},{0x0487130,00},{0x0487130,0x1E},{0x048A130,00},{0x0F7F52F,0x14},{0x1C7F523,0x33},{0x097F320,0x20},{0x0F8F121,00},{0x0F7F52F,0x1B},{0x1C7F523,0x30},{0x0E8F431,0x15},{0x0E8F501,0x15},{0x078F101,00},{0x0E6F405,0x69},{0x0D6F200,0x14},{0x0E6E201,00},{0x20434A0,0x0B},{0x1156561,0x92},{0x0073521,0x01},{0x012D121,0x1A},{0x016C1A1,0x93},{0x0044F21,00},{0x0049100,0x0F},{0x2045240,00},{0x0276171,0x80},{0x118543A,0x1E},{0x5177472,00},{0x04A6121,0x9F},{0x0067F61,00},{0x0277221,0x16},{0x0067F21,0x05},{0x0866131,0x1A},{0x0D6C261,00},{0x0866131,0x16},{0x0678221,0x1A},{0x0179222,00},{0x0678221,0x15},{0x0179222,0x0A},{0x00AD961,0x28},{0x006A861,0x1E},{0x0069A21,0x25},{0x00ACF24,00},{0x01CB632,0x92},{0x01B66E1,0x82},{0x00457F2,0xA8},{0x0375761,00},{0x2545C73,00},{0x0776821,0x0D},{0x00FFF61,0x1C},{0x00FFF22,00},{0x00FFF21,0x1C},{0x009CF62,00},{0x0559622,0x46},{0x0187421,0x80},{0x09041F1,0xCB},{0x00322B1,0x07},{0x0F34212,0x86},{0x1D38201,0x40},{0x04442E1,0x0D},{0x2164460,0xAB},{0x00450E1,0x01},{0x1623524,0x20},{0x1023171,0x05},{0x155F261,0x4D},{0x2343161,0x9D},{0x0137D16,0x08},{0x1127533,0x58},{0x4F4F211,0x03},{0x3F0F014,0x40},{0x6F7F611,0x43},{0x033F201,0xD1},{0x373F402,0x8A},{0x6A7F907,0x1A},{0x229A904,00},{0x055C902,0x1A},{0x024A601,0x05},{0x1397931,0x80},{0x2099B22,00},{0x2137931,0x42},{0x1079B22,0xC2},{0x119FFA1,0x0C},{0x0089024,0x11},{0x004F007,0x51},{0x004F081,0x13},{0x016F801,00},{0x001FF17,0x1C},{0x0057A12,0x0B},{0x4046306,0xCA},{0x005A902,0x08},{0x0045413,0x51},{0x005A601,0x08},{0x09FF831,0x8B},{0x004FF10,0x05},{0x5C8FB00,00},{0x0B7E601,00},{0x2F0F00F,00},{0x0F8F800,0x40},{0x20F2F01,0x0D},{0x0534313,0x20},{0x7574A1F,0x03},{0x003FF15,0x09},{0x0934511,0x1F},{0x200C327,0x80},{0x6021300,0x12},{0x200C32B,0x80},{0x060F209,0x4F},{0x072F214,0x19},{0x11311E2,0xC5},{0x000FFEE,00},{0x30318EE,00},{0x059F802,0x11},{0x01CF600,00},{0x2159506,00},{0x65AB701,0x04},{0x10F5F81,00},{0x0164611,0x0A},{0x00F5F01,00},{0x20F5F00,00},{0x0D6D725,0x1F},{0x3A9A909,00},{0x0F0A00F,0x80},{0x0F8F80F,0x8C},{0x2FDFD00,00},{0x6FAFA00,00},{0x4F1F103,00},{0x6FAFA07,00},{0x0F0F007,0x27},{0x2F6F60F,00},{0x0F7B710,0x42},{0x6EF8801,0x0D},{0x608B502,00},{0x0F1F10F,00},{0x007840F,0x08},{0x6EF8800,0x13},{0x608F502,00},{0x0F1D101,00},{0x0078400,00},{0x254F307,0x04},{0x307F905,0x08},{0x207F905,0x08},{0x254D307,0x04},{0x3288905,0x03},{0x2F2E327,0x04},{0x3F5C525,0x08},{0x2F2F326,0x04},{0x2F5C525,0x08},{0x292F108,00},{0x354F201,0x08},{0x283E108,00},{0x334D700,0x08},{0x283E109,00},{0x334D500,0x08},{0x2E1F119,0x04},{0x3F3F11B,0x08},{0x251F206,0x04},{0x263C504,0x09},{0x241F287,0x05},{0x353B502,0x09},{0x354F201,0x03},{0x456FB02,0x81},{0x556FA01,00},{0x117F701,0x0D},{0x556FB02,0x81},{0x000F020,0x0A},{0x40A8A00,00},{0x70F5F20,00},{0x70F4F00,00},{0x0D1F815,0x44},{0x2D1F213,0x9D},{0x098F614,00},{0x098F614,0x21},{0x0985900,0x07},{0x039870F,00},{0x2F3F307,0x1D},{0x09C9B0F,00},{0x09C4B00,0x21},{0x43A6705,00},{0x0F7F907,0x1C},{0x2987805,00},{0x0F7F21F,0x40},{0x0FAF31F,0x40},{0x227A305,0x87},{0x36A560A,0x08},{0x247C345,0x87},{0x3697809,0x08},{0x037A309,0x11},{0x06DF904,00},{0x0F5F111,0x0A},{0x019F603,0x30},{0x0F4F212,0x10},{0x10F94B0,0x86},{0x10F94B0,0x80},{0x1F69182,0xC0},{0x1F69180,0x86},{0x00B5231,0x1B},{0x021FE13,0x96},{0x094F231,0x80},{0x153F201,0x4D},{0x174F511,00},{0x0199421,0x01},{0x0099428,0x09},{0x0099428,0x13},{0x06FFA24,0x8A},{0x0F891C2,0x03},{0x0CFF121,0x1A},{0x048F621,00},{0x022B701,0x1D},{0x037C422,00},{0x068F122,00},{0x034F131,0x09},{0x019D531,0x8A},{0x01B6172,00},{0x255A511,0x96},{0x1B3F511,0x80},{0x0058001,0x9C},{0x006F011,0x80},{0x243A5C0,0x0D},{0x123D400,00},{0x1E3C221,0x58},{0x3166120,00},{0x12CB322,00},{0x0AE7121,0x1D},{0x01B92F1,00},{0x09E81E2,0x03},{0x0537121,0x4F},{0x04C5232,00},{0x05E5231,00},{0x0219B32,0xC0},{0x0077221,00},{0x08F6EE1,0xEC},{0x02A6562,00},{0x0C70CF5,0x9A},{0x0A560F2,0x80},{0x00FFF21,0x08},{0x00E7121,0x1D},{0x00E8121,00},{0x0FFF100,0x0D},{0x0EF1100,0x10},{0x00FB031,0x80},{0x0238161,0x10},{0x200F601,00},{0x3061FDD,00},{0x00BF50F,00},{0x07CFA01,00},{0x004F200,00},{0x000F601,0x0C},{0x3029FDD,00},{0x213CB12,00},{0x342F80E,0x06},{0x0D6F609,00},{0x000F600,0x09},{0x24393DF,00},{0x0BFFA02,00},{0x097C804,00},{0x0164F11,00},{0x332F985,0x05},{0x0A5D684,0x40},{0x2FF220C,00},{0x223A133,0xD6},{0x4F4F131,0x09},{0x023B131,0xD3},{0x0F4F131,0x0A},{0x433F133,0xD6},{0x0F4F131,0x09},{0x2F3F132,0xD3},{0x4F6F131,0x0A},{0x2A4A112,0xD2},{0x4B5F211,0x05},{0x4A49112,0xCF},{0x2B5D110,0x05},{0x073FA31,0x8E},{0x4F4D111,0x08},{0x473FA32,0x8C},{0x4F4D111,0x09},{0x2E7F21A,0x6F},{0x0B8F201,0x48},{0x2C7F436,0x9D},{0x0D7F231,0x0A},{0x0C7F021,0x1E},{0x0F8F111,0x0F},{0x523F134,0x51},{0x4F5D111,0x0D},{0x203FC32,0x4B},{0x1F7D111,0x0D},{0x559F101,0x44},{0x0F7F111,0x08},{0x4F7F111,0x0D},{0x087F607,0x54},{0x0E4F231,0x08},{0x587F617,0x54},{0x0A5F33F,0xA1},{0x0F2C312,0x06},{0x0A5F43F,0xD5},{0x0F2F392,0x07},{0x462A417,0x9C},{0x0027A11,0x08},{0x062A416,0x99},{0x0028811,0x07},{0x0F6F2B2,0xE8},{0x0F6F281,0x05},{0x0F6F2A4,0x45},{0x007F08F,0x05},{0x0F6F618,0x63},{0x0F7E500,0x80},{0x5A6F40E,0x5B},{0x007D804,0x80},{0x2F6F71A,0x1F},{0x0F5F413,0x03},{0x1F7F715,00},{0x082F307,0x97},{0x0E3F302,0x8A},{0x082D307,0x97},{0x4109131,0x52},{0x3B5F322,0x88},{0x118B1A4,0x88},{0x11BD161,0x80},{0x108B1A3,0x88},{0x11BD161,0x88},{0x0F8F032,0x65},{0x0F8F001,0x07},{0x0F8F024,0x43},{0x008F009,0x07},{0x0088AB1,0x10},{0x118AA71,0x4B},{0x0088AB2,0x10},{0x1043031,0x92},{0x1145432,0x80},{0x1045033,0x92},{0x1145430,0x80},{0x1178001,0x5D},{0x1176082,0x83},{0x4178000,0x54},{0x1176081,0x83},{0x025A721,0x4D},{0x1264132,0x08},{0x1258621,0x4F},{0x1264633,0x08},{0x4FAF022,0x96},{0x01A6221,0x08},{0x105FF2C,0x9D},{0x01A6222,0x12},{0x107F021,0x92},{0x2055232,0x07},{0x574A613,0x9D},{0x4B8F401,0x0D},{0x2249134,0x61},{0x2B8D301,0x05},{0x5E5F133,0x99},{0x1E4F211,0x07},{0x1E5F133,0x9E},{0x5E4F211,0x0B},{0x21FF021,0xA5},{0x088F211,0x80},{0x11FF023,0x5E},{0x132ED11,0x87},{0x3E7D211,0x0A},{0x332ED12,0x80},{0x1E7D211,0x45},{0x0F4E431,0x97},{0x0F5F331,0x86},{0x3F0F701,00},{0x1F8F900,0x0D},{0x0F77111,0x48},{0x3F7F011,0x87},{0x0F78140,0x86},{0x3F7F040,00},{0x0F78140,0x07},{0x3F7F040,0x40},{0x0F78100,0x86},{0x3F7F000,0x03},{0x6F78AE8,0x03},{0x649B1F4,0x0A},{0x6F78AE8,0x43},{0x649B1F4,0x4B},{0x0609533,0x63},{0x4E5C131,0x05},{0x0608521,0xD4},{0x0E4A131,0x05},{0x0F9F030,0x9D},{0x0F8F131,0x05},{0x7F0F017,00},{0x7F9B700,0x0F},{0x026AA21,0xCF},{0x0D7F132,0x84},{0x5F9F40B,0x4B},{0x445F711,0x4D},{0x010D331,0x9A},{0x0B68112,0x40},{0x0404121,0x9B},{0x0B56113,0x4C},{0x2E69419,0x5E},{0x5B6B311,0x08},{0x0F79321,0x0D},{0x2E69515,0x17},{0x1B6B211,0x08},{0x06AC332,0x0D},{0x0F5F430,0x0E},{0x139A331,0x93},{0x0F8F133,0x08},{0x2257020,0x95},{0x4266161,0x05},{0x1257021,0x99},{0x0266141,0x07},{0x2426070,0x4F},{0x2154130,00},{0x214D070,0x0F},{0x1175222,0x88},{0x524D071,0x13},{0x5075222,0x88},{0x521F571,0x90},{0x4166022,0x09},{0x52151F0,0x97},{0x4156021,0x0D},{0x223F8F2,0x99},{0x4055421,0x8A},{0x4A35211,0x9C},{0x0E4C411,0x08},{0x2C79613,0xD7},{0x4E45411,0x08},{0x023E133,0xA2},{0x0F2F131,0x09},{0x023F132,0x24},{0x0F2F131,0x0A},{0x4C3C404,0x21},{0x4B4B519,0x05},{0x17A9913,0x0F},{0x0B4F213,00},{0x223F832,0x99},{0x4056421,0x8A},{0x433CB32,0x9B},{0x5057561,0x8A},{0x1029033,0x5B},{0x4044561,0x85},{0x4109033,0xA8},{0x2044520,0x85},{0x2034170,0x0B},{0x0043671,0x20},{0x1024171,0x0C},{0x0043671,0x17},{0x005A061,0x69},{0x0F55022,0x06},{0x0008060,0x33},{0x0F55021,0x08},{0x239B420,0x50},{0x0076121,0x05},{0x139B462,0x91},{0x00D7161,0x14},{0x05470F1,0x69},{0x07440B1,0x80},{0x054A0F1,0x5E},{0x07430B1,0x80},{0x2436110,0xCD},{0x714D211,00},{0x5436192,0xCB},{0x745F312,00},{0x0147421,0x94},{0x0077521,0x04},{0x0178461,0x10},{0x008AF28,0xA6},{0x0198161,0x08},{0x0235361,0x1D},{0x0196161,0x03},{0x0155331,0x94},{0x0365121,0x1E},{0x0257221,0x08},{0x2844521,0x23},{0x20592A0,0x03},{0x0578321,0x19},{0x117C021,0x03},{0x2E77530,0x10},{0x307F520,0x08},{0x036F121,0x95},{0x337F121,0x08},{0x0368121,0x95},{0x037F121,0x08},{0x0A66121,0x9B},{0x0976121,0x08},{0x5237731,0x4B},{0x1F65012,00},{0x0137732,0xC7},{0x0F65011,0x0A},{0x1067021,0x46},{0x1165231,00},{0x00B9820,0x8E},{0x10B5330,00},{0x10B8020,0x87},{0x11B6330,00},{0x1235031,0xC0},{0x0077C24,0x08},{0x045D933,0xD0},{0x4076C35,0x26},{0x6077831,0x1E},{0x2076331,00},{0x0199031,0x95},{0x01B6134,0x80},{0x0177532,0x93},{0x0174531,0x03},{0x0277530,0x14},{0x0174536,0x9C},{0x08B8EF1,0xC0},{0x0285571,00},{0x08860A1,0x5C},{0x2176522,0x5A},{0x0277421,00},{0x1267532,0x8D},{0x0166531,0x05},{0x2F0F011,0x03},{0x0987801,0x17},{0x5543737,0x28},{0x25D67A1,00},{0x6243371,0x20},{0x46D6331,00},{0x00F31D1,0xC7},{0x0053271,00},{0x00581A2,0x37},{0x0295231,00},{0x20FFF22,0x7F},{0x60FFF21,0x12},{0x30FFF22,0xBF},{0x39BC120,0xBF},{0x368C030,0x06},{0x3AB8120,0x9E},{0x308F130,0x06},{0x13357F1,0x21},{0x00767E1,00},{0x43357F2,0x28},{0x2444830,0x22},{0x21D67A1,00},{0x534B821,0x1F},{0x02D87A1,00},{0x32B7420,0x46},{0x12BF134,00},{0x5029072,0x96},{0x0069061,0x0C},{0x1019031,0x1A},{0x245C224,0x81},{0x2550133,0x80},{0x2459224,0x81},{0x2556133,0x80},{0x132ED10,0x87},{0x3E7D010,0x0D},{0x132ED30,0x87},{0x3E7D010,0x12},{0x073513A,0xA4},{0x013C121,0x0A},{0x273F325,0x20},{0x0228231,0x0A},{0x0031131,0xD4},{0x0054361,0x08},{0x20311B0,0xD9},{0x00543E1,0x08},{0x245A121,0x98},{0x126A121,0x05},{0x255A421,0x98},{0x50470E1,0x59},{0x1148161,0x03},{0x10460E2,0x5F},{0x4148161,0x83},{0x0336186,0xA7},{0x05452E1,00},{0x13351A6,0xA7},{0x2529084,0x9D},{0x1534341,0x80},{0x2529082,0x9D},{0x0534341,0x80},{0x2345231,0x98},{0x2135120,00},{0x410F422,0x20},{0x1233231,00},{0x1522162,0x99},{0x1633021,0x80},{0x1522161,0x99},{0x157B261,0x04},{0x019F806,0x40},{0x0145114,0x40},{0x2322122,0x8C},{0x0133221,0x92},{0x4033121,0x93},{0x0132122,0x48},{0x074F624,0xC0},{0x0249303,0x0D},{0x3D2C092,0x8E},{0x1D2D131,0x09},{0x0D2D091,0x8E},{0x1D23132,0x09},{0x5F29054,0x99},{0x0F2C241,0x06},{0x1F19011,0x1A},{0x05233E1,0x1A},{0x0131371,0x88},{0x5522363,0x1A},{0x0131331,0x8D},{0x0B67061,0x9C},{0x0928032,0x11},{0x0057F21,0x9C},{0x0038F62,0x11},{0x0625331,0x94},{0x1648221,0x06},{0x2645321,0x15},{0x2445521,0x0D},{0x0B37121,0x16},{0x5F48221,0x08},{0x2B37102,0x90},{0x5E2F321,0x1B},{0x6E4F523,0x08},{0x455F71C,0xA3},{0x0D68501,0x08},{0x0D6E501,0x08},{0x302A130,0x1E},{0x0266221,00},{0x0136031,0x12},{0x1169131,0x80},{0x032A115,00},{0x001E79A,0x81},{0x067961C,00},{0x4D1F214,0xA0},{0x098F715,00},{0x008F312,0x08},{0x004F600,0xC8},{0x27CFA01,0x08},{0x004F200,0x08},{0x518F890,00},{0x0E7F310,00},{0x250F610,00},{0x0E7F510,0xC8},{0x2114109,0x05},{0x51D2101,0x80},{0x2114108,0x05},{0x31D2101,0x80},{0x00437D2,0xA1},{0x0343471,0x07},{0x0F66700,0xCD},{0x003EBD7,0xD4},{0x06845D8,00},{0x62FDA20,0x42},{0x614B009,0x48},{0x62FDA20,0x82},{0x101FE30,00},{0x6142120,00},{0x6019460,0x26},{0x1142120,00},{0x200832F,0x80},{0x6044020,00},{0x2305431,00},{0x6E7F600,00},{0x023A121,0xA5},{0x0F4F33C,0x61},{0x023B113,0x24},{0x0F5F131,0x0A},{0x002D3C4,0xA3},{0x004F50B,0x4A},{0x013F5C4,0xA7},{0x004F60F,0x07},{0x4B8F401,0x05},{0x2249132,0x5C},{0x1B8D301,0x05},{0x574A412,0xDD},{0x2B8F400,0x05},{0x2249334,0x64},{0x0A78923,0x08},{0x0E5C162,0x1B},{0x0F6E041,0x17},{0x0F7F001,0x08},{0x106A021,0x8A},{0x1064021,0x8A},{0x3FBF000,0x9E},{0x0FBF001,0x08},{0x2A4A111,0xDD},{0x1A49113,0x5E},{0x018B1A4,0x88},{0x01BD161,0x80},{0x008B1A3,0x88},{0x01BD161,0x88},{0x4B8F501,0x0D},{0x2649334,0x5E},{0x2B8D501,0x05},{0x0E5C122,0x1B},{0x3F6E001,0x17},{0x6F7F001,0x01},{0x0054361,0x88},{0x00543E1,0x88},{0x5522123,0x99},{0x1522121,0x99},{0x0209220,0x97},{0x0E6C130,0x05},{0x0E6A130,0x05},{0x5316130,0x90},{0x4157021,0x0D},{0x223D832,0x99},{0x4057421,0x8A},{0x0364121,0x21},{0x02B7221,0x08},{0x026F021,0x26},{0x0056121,0x03},{0x0886021,0x5C},{0x01A6561,0x05},{0x3A8A760,0x4B},{0x21A6520,0x05},{0x214B070,0x0F},{0x1177222,0x88},{0x524B071,0x13},{0x5077222,0x88},{0x4059421,0x8A},{0x433BB32,0x9B},{0x5156571,0x8A},{0x223F832,0x94},{0x431CB32,0x93},{0x01A3521,0x0C},{0x518AA70,0x44},{0x518AA71,0x4B},{0x0F0A00F,00},{0x0F8F80F,0x0C},{0x559FA00,00},{0x047F800,00},{0x3F1F102,00},{0x0078400,0x26},{0x048FA00,00},{0x008F900,00},{0x287F702,0x80},{0x678F802,0x88},{0x2F7F602,00},{0x0F8F802,0x88},{0x008F700,00},{0x007F609,00},{0x0F1F105,00},{0x0078407,0x08},{0x05476C1,0x80},{0x30892C5,0x08},{0x05477C1,00},{0x007C604,0x08},{0x201F302,0x03},{0x057AB09,0x07},{0x058F30B,0x04},{0x308F90D,0x08},{0x255F308,0x04},{0x308F909,0x08},{0x006C604,0x08},{0x201F312,0x03},{0x0015500,0x0C},{0x007C716,00},{0x201F312,00},{0x007C718,00},{0x001F312,0x03},{0x047BB05,0x07},{0x007C71B,00},{0x047BB09,0x07},{0x291F108,00},{0x333F401,00},{0x333F501,00},{0x007C71F,00},{0x300F50C,0x07},{0x605FE05,0x8A},{0x310F508,0x86},{0x604FE05,0x8A},{0x2E1F11E,0x04},{0x3F3F318,00},{0x2777603,0x87},{0x3679601,0x08},{0x277C643,0x87},{0x366F905,00},{0x099F701,00},{0x334F401,00},{0x431A000,0x81},{0x085B41A,0x05},{0x459F640,00},{0x185B418,0x20},{0x605FE04,0x8A},{0x2A8F9E3,0x1E},{0x0779643,0x08},{0x0A5F7E8,0xDE},{0x0D89949,00},{0x0779643,00},{0x0A5F7E9,0xDE},{0x0D8994A,0x08},{0x0A8F7E9,0x08},{0x5D8990A,00},{0x0A5F7E9,0x29},{0x2A8F9E2,0x1E},{0x0779642,00},{0x0A5F7E9,0x08},{0x5D8994A,00},{0x367FE06,0x09},{0x668F701,0x08},{0x367FD10,00},{0x098F901,0x0D},{0x367FE05,0x09},{0x678F701,0x08},{0x078F901,0x0D},{0x098600F,0x08},{0x3FC8590,0xC0},{0x009F020,0x25},{0x27DA788,00},{0x00FC020,0x25},{0x22DA388,00},{0x160F2C6,0x4F},{0x160F286,0x4F},{0x4755406,0x87},{0x3667601,0x08},{0x275A346,0x87},{0x6E4840B,0x12},{0x6E4B409,0x09},{0x6E4440B,0x21},{0x6E46407,0x13},{0x6F9A902,00},{0x2F7C801,0x40},{0x4F9F901,0x1F},{0x4F7C713,0x48},{0x4B7C720,0x0B},{0x1F3F300,00},{0x0F1F600,00},{0x0078400,0x12},{0x0F1F802,00},{0x0058400,0x09},{0x7F0F312,0x80},{0x0F5F501,00},{0x100F010,0x0C},{0x6059110,00},{0x005C604,0x08},{0x005C604,00},{0x509F902,0x03},{0x057AB07,0x07},{0x509F912,0x03},{0x210F509,0x8A},{0x400F509,0x07},{0x212FD08,0x01},{0x305FD03,0x03},{0x0EFA901,0x0D},{0x009F902,00},{0x007E50F,0x08},{0x154F300,0x04},{0x008F80E,0x08},{0x054F100,0x04},{0x208F805,0x08},{0x051F00C,0x0C},{0x016FF28,0x12},{0x041F00C,00},{0x037D527,0x0C},{0x3F0F000,0xD2},{0x3F0F000,0xCD},{0x01BF4E0,0x8D},{0x018F3E0,0x23},{0x00FFFE4,0x8A},{0x00FFFE1,0xA9},{0x031FF10,0x07},{0x004FF01,0x25},{0x07CE401,0x22},{0x00361F0,0x86},{0x02CE371,0x1F},{0x00361B0,0x86},{0x02CE3F3,0x1F},{0x00331F2,0x4B},{0x02C53F4,0x21},{0x08FAEE2,0x11},{0x02A8561,0x23},{0x019D530,0x15},{0x01B6171,0x9B},{0x03B9261,0x99},{0x01B9261,0x9D},{0x04C6321,0x18},{0x00FC521,0xA0},{0x072F212,0x21},{0x053F401,0x40},{0x053F308,0x64},{0x07FF511,0x1F},{0x04CA700,00},{0x04FC600,0x22},{0x0F5F062,00},{0x0F8F60E,0x1F},{0x005FC4E,00},{0x0F8F90C,0x24},{0x005756E,00},{0x0F8F601,0x22},{0x011F131,0x90},{0x043D418,0xA5},{0x08FAEE0,0xE8},{0x00A8561,0x21},{0x02990F2,0x16},{0x02C61F2,0x22},{0x02BF4E0,0x8D},{0x048F3E0,0x1F},{0x023F331,0x45},{0x09C4333,0x25},{0x04FC600,0x2B},{0x002010C,00},{0x076D201,00},{0x054F113,0x53},{0x086D251,00},{0x0FFC1A1,00},{0x013FF34,0x92},{0x0F7F423,00},{0x074C216,0x80},{0x0337212,0x80},{0x011FA16,0x4D},{0x0F1F1E1,00},{0x011FAD6,0x4D},{0x0F5F561,00},{0x015DA45,0x4E},{0x0F6F361,0x80},{0x0B5F6C2,00},{0x0B2F1D2,00},{0x004FE11,0x11},{0x0BDF211,00},{0x0FFC021,00},{0x0FFC0A1,00},{0x00EC0E1,00},{0x0877061,00},{0x0065231,00},{0x0FD5524,0x54},{0x02D5031,00},{0x055F101,0x4D},{0x074F111,0x02},{0x056F101,0x90},{0x076F111,0x04},{0x0C8F253,0x16},{0x0C5F211,0x40},{0x0C8F253,0x20},{0x0C5F211,00},{0x0FFF111,0x43},{0x3FFF054,00},{0x0F0F0CA,0x4E},{0x06859EC,00},{0x02CC321,0x80},{0x08AC421,0x80},{0x07BF021,0x4E},{0x0C8F331,00},{0x08A8421,0x80},{0x07AB400,0x1D},{0x07CC301,00},{0x09E8021,00},{0x0035171,0x20},{0x0035171,0x1E},{0x0175423,00},{0x0F3F201,0x80},{0x053F300,0x7F},{0x01432F1,0x18},{0x016F1E1,00},{0x0132261,00},{0x01451E1,00},{0x0313221,0x8A},{0x0153261,00},{0x0887261,0x98},{0x01650E1,0x80},{0x01780E1,0x81},{0x0745241,0x10},{0x0948411,0x03},{0x0F4F4E4,0x40},{0x017A021,00},{0x036A061,00},{0x0565361,0x19},{0x016A021,00},{0x0675421,00},{0x096A121,00},{0x0576361,0x1C},{0x00B6321,00},{0x00E6321,00},{0x00E6B22,00},{0x00E7170,0x16},{0x00E7823,0x07},{0x0178731,0x45},{0x00E8B22,00},{0x0076061,0x80},{0x02A60E2,0x80},{0x02498A2,0x1D},{0x04FD6A1,0x13},{0x1063F53,0x85},{0x0075F00,00},{0x0A560F6,0x80},{0x00E70E1,00},{0x30FF221,0x1D},{0x018F221,00},{0x0FFF041,0x11},{0x0FFF001,00},{0x030F4A4,0x12},{0x0BDF101,0xCE},{0x39FF102,0x80},{0x0FFF141,0x0E},{0x0FFF001,0x09},{0x027F032,00},{0x025F061,00},{0x0132121,0x8E},{0x049F430,0x90},{0x033F430,00},{0x021FF31,0x8E},{0x0159461,0x01},{0x06459CC,00},{0x0152001,0x43},{0x0F221F1,00},{0x0132011,0x92},{0x0F231F1,00},{0x010FF34,0x91},{0x004FF03,00},{0x0311331,0x80},{0x031D1B0,00},{0x033F321,0x80},{0x003F171,0x0B},{0x0141121,0x8B},{0x0131261,00},{0x0F3F0E4,0x40},{0x04240D7,0x80},{0x0457D16,0x80},{0x0F8F001,00},{0x0EE7130,0x16},{0x01E8823,00},{0x054F022,00},{0x015F101,00},{0x0F33900,0x3F},{0x01550E2,0x80},{0x053D10F,0x40},{0x0562200,0x0B},{0x0FA5D0C,0x0B},{0x04240D7,0x89},{0x053D100,0x40},{0x0566200,0x0B},{0x046F600,00},{0x077F00F,00},{0x0889800,00},{0x0F0B506,00},{0x0F5F800,00},{0x0FBFA00,00},{0x0EAFA00,00},{0x0F1F005,00},{0x0F7F600,0x06},{0x0C0F006,0x0E},{0x034C6CF,00},{0x360F207,0x0A},{0x352F212,0x0C},{0x352F212,0x0B},{0x0F0F406,00},{0x0F78700,0x0D},{0x1FFF005,00},{0x0B9F800,00},{0x0F5F500,0x09},{0x0590900,0x7F},{0x095F700,00},{0x095F900,00},{0x0F6F501,00},{0x079F601,00},{0x056F60F,0x6D},{0x096F605,00},{0x070F005,00},{0x0E57A00,0x10},{0x0E59A00,0x10},{0x0E55A00,0x10},{0x07BF502,0x80},{0x07BF402,0x80},{0x0FB7800,0x18},{0x0064F80,00},{0x005FF04,00},{0x084E90A,00},{0x3A85036,00},{0x073D211,00},{0x104AC00,0x0F},{0x0F5F018,00},{0x006FF10,00},{0x29EF520,0x90},{0x35BF411,00},{0x0FA9000,0x02},{0x0FAB011,00},{0x21DFBA2,0xC0},{0x058F201,00},{0x00F9039,00},{0x3A850F5,00},{0x08FF022,0x0C},{0x0DFF520,00},{0x08FF022,00},{0x08FF320,00},{0x0DFF620,00},{0x3FFF003,0x02},{0x2FFF011,0x80},{0x00FFFBE,00},{0x00F3FAE,00},{0x005FF00,0xC0},{0x305FF00,00},{0x305AF20,00},{0x0052F00,00},{0x305FF20,00},{0x212FEF0,0x04},{0x314F9A5,0xC0},{0x00F4F00,00},{0x00FFF20,0x80},{0x08AF002,0x09},{0x0DAF500,00},{0x0534100,0x3F},{0x03CF814,00},{0x003FF10,00},{0x32FF310,00},{0x3FFF09E,0x07},{0x00F3F01,00},{0x24FA710,00},{0x0DA9C04,0x87},{0x00FFF00,0x3F},{0x05A4900,0x48},{0x0DA8C00,00},{0x0F5F0BE,0x39},{0x1043F20,00},{0x0F5F0BE,0x3F},{0x004FF20,00},{0x30DF9B0,0xFF},{0x0043AA0,00},{0x044FF00,00},{0x008FF90,00},{0x044FC00,00},{0x008A190,00},{0x307FF00,00},{0x004FF00,00},{0x30A4F00,00},{0x101FFC0,0x7D},{0x2065F5A,00},{0x004AC48,00},{0x33441C0,00},{0x05FF93E,00},{0x04FF030,0x80},{0x0BFF510,0x80},{0x057F9EE,00},{0x0A5F1E0,0x80},{0x05FF9EE,00},{0x0FFF6E0,0x80},{0x204FF20,0x5A},{0x004FF20,0x80},{0x272FEF0,0x04},{0x016F9E1,0xC0},{0x0F590A0,0x25},{0x0F7F0A0,00},{0x301FFA0,0x54},{0x20FFF20,0x80},{0x0017B00,00},{0x3066B00,00},{0x0014B00,00},{0x3064B00,00},{0x0025B00,00},{0x30B5B00,00},{0x2F6F690,00},{0x006DC11,00},{0x207FA40,00},{0x044F9C0,00},{0x0F0F900,0x3F},{0x028E412,00},{0x0FFF090,0x0C},{0x20F4F00,00},{0x20F4F00,0x0A},{0x004AC60,0x0F},{0x2F5F030,0x0F},{0x01BFC10,0x0F},{0x007FF14,00},{0x00F6300,00},{0x026E412,00},{0x02FE412,00},{0x097B530,0x40},{0x0A77300,00},{0x00DF913,0xDA},{0x007F6C2,0x80},{0x0EF7602,0x47},{0x05F7603,00},{0x0EF7602,0x45},{0x05F7603,0x02},{0x061F237,0x47},{0x065F431,0x47},{0x055F615,0x08},{0x156E500,0x0F},{0x0F5F012,00},{0x005DF20,0x04},{0x005DF20,00},{0x07FF510,00},{0x00F6F00,00},{0x0086F10,00},{0x0FFF09E,0x0C},{0x20F4F9E,00},{0x0F9F09E,0x0C},{0x206FF9E,00},{0x1E0620E,0xC2},{0x0324B14,0x40},{0x00A4F00,00},{0x00549C8,0x07},{0x03540C6,00},{0x00349D8,0x07},{0x03240D6,00},{0x01383D4,0x06},{0x02293D6,0x07},{0x11383D4,0x06},{0x12293D6,0x07},{0x11382D4,0x06},{0x12291D6,0x07},{0x115F2C4,0x46},{0x024F3C6,0x07},{0x006DC10,00},{0x101FF00,00},{0x2D45900,0x87},{0x056E502,0x50},{0x074F210,00},{0x072B400,0x1D},{0x073C301,00},{0x072B480,0x1D},{0x173C382,00},{0x172B480,0x16},{0x177C3D2,00},{0x1757480,0x96},{0x177C6D1,00},{0x053F100,0x60},{0x006FC10,00},{0x053F101,0xEC},{0x04CD390,00},{0x004FF00,0x07},{0x10A4F00,00},{0x2534101,0x4F},{0x03AF811,00},{0x01EF90E,00},{0x01EF70E,00},{0x2BDA0A2,0xC0},{0x01FF70E,00},{0x1F3FF32,00},{0x01393F1,00},{0x3F3E921,0x15},{0x204D620,00},{0x004D620,00},{0x0DC5C09,00},{0x0FFF09E,0x3F},{0x1067F00,00},{0x0014131,0x15},{0x03B93A1,0x83},{0x0F9F131,0x95},{0x0E6F731,0x80},{0x263D8F2,00},{0x011C3D1,00},{0x384FEF2,00},{0x012C3D1,00},{0x382FAF2,00},{0x002E1F1,00},{0x00549C4,0x07},{0x055E5C6,00},{0x00381A1,0x0C},{0x00461B1,0x80},{0x032FA06,00},{0x04FF3D0,00},{0x062F101,0x4D},{0x076D311,00},{0x005FF00,0x80},{0x30FFFC0,00},{0x01FF6C0,00},{0x0046191,0x80},{0x205CF10,0x07},{0x207E220,00},{0x211300E,00},{0x2032F0E,00},{0x1115132,0x9A},{0x037A2E3,0x80},{0x205FFA2,0x57},{0x005FF21,0x80},{0x0115230,0xC5},{0x02E3131,0x80},{0x105FF00,0x0E},{0x205AF20,00},{0x105FF00,0x82},{0x207DF20,00},{0x10AFF20,00},{0x00DFF40,0x80},{0x20F8FC0,0xC0},{0x2085F5E,0x80},{0x005FC08,0x07},{0x039C006,00},{0x105E2C0,0x05},{0x086B241,00},{0x2945251,0x40},{0x0C6B252,00},{0x054F101,0x4F},{0x075F211,00},{0x254F101,0x4F},{0x175F211,00},{0x3AFBFC0,00},{0x01FE7C0,00},{0x30FF6C0,00},{0x01FE6C0,00},{0x30DE01A,00},{0x009F002,00},{0x004AC00,0x13},{0x1E4F504,00},{0x306FF00,00},{0x0F2E026,0x03},{0x034FF3E,00},{0x10FF510,0x1C},{0x0DFF800,00},{0x026DA05,0x4E},{0x016F901,00},{0x013F941,0x80},{0x28EF0E2,00},{0x03EF700,00},{0x286F002,00},{0x037F700,00},{0x1146E31,0xC5},{0x1068BE4,0x05},{0x0126CF1,0xC0},{0x01556E5,0x05},{0x0EFF200,0x16},{0x077F501,00},{0x10532C1,0x05},{0x086B2C1,00},{0x104C1B1,0x8C},{0x0049D96,0x80},{0x0856321,0x19},{0x0258521,0x80},{0x075D211,00},{0x0B3F101,0x40},{0x0155321,0x1C},{0x03552A1,0x80},{0x10381B2,0x8C},{0x02663B5,0x80},{0x12263A2,0x9D},{0x051C5A1,0x80},{0x30FFFD0,00},{0x03FF5D0,00},{0x0EFF201,0x16},{0x212DCC0,0x28},{0x066B4E1,00},{0x007FF04,0x60},{0x007FF06,0x60},{0x2126CF1,0xC0},{0x0AFFF00,0x3F},{0x00AFC02,00},{0x008FC02,00},{0x042F200,0x80},{0x006F200,0x40},{0x00F8F00,0x0F},{0x0F0F016,00},{0x0B4F610,00},{0x053F101,0x60},{0x367F6C2,0x04},{0x03FD8F9,0x07},{0x03FE9F9,0x03},{0x156E507,0x0F},{0x35FC600,0x1C},{0x04E4C15,00},{0x086F002,00},{0x3F0F50E,0x87},{0x3DC5C00,00},{0x0DA950E,00},{0x0F0E028,0x03},{0x015E813,00},{0x29EF002,00},{0x200FF34,00},{0x364F6C0,0x84},{0x200FF84,00},{0x365F6C2,00},{0x232FA06,00},{0x332FA06,00},{0x05FF610,00},{0x220F203,00},{0x03696C3,00},{0x231F013,00},{0x016F4D0,00},{0x231F003,00},{0x243F2E0,00},{0x055F502,0x08},{0x207FA50,00},{0x04FF5D0,00},{0x0F9FD0C,0x8E},{0x0F9FA07,0x20},{0x0F9F807,0x20},{0x0FAF601,00},{0x042F00F,00},{0x0A41100,00},{0x050300F,0x3F},{0x0C13002,0x3F},{0x04667A3,0x13},{0x0668726,0x52},{0x0568722,0x52},{0x00655A1,00},{0x0567522,0x1F},{0x09A7262,0x0F},{0x0507002,0x26},{0x0C67002,0x02},{0x0E54150,0xA9},{0x0E8F651,0x63},{0x0E8D150,0x9B},{0x0078F22,0x08},{0x0E3A111,0x03},{0x003C821,0x21},{0x042D520,0x17},{0x033C520,0x14},{0x005C520,00},{0x011F610,0x06},{0x0F5F121,00},{0x024F300,0x09},{0x0B5F300,0x0C},{0x0AFF505,0x2E},{0x03DFD20,0x13},{0x0B0F602,0x0F},{0x074F411,00},{0x005FF22,0x80},{0x0612800,0x33},{0x001FCA1,0x08},{0x00FF420,0x1B},{0x009F900,00},{0x164F227,0x62},{0x0FAF331,0x80},{0x2F0A830,0x80},{0x0F77620,00},{0x1F09832,0x80},{0x0FA7620,00},{0x33457F1,0x1F},{0x19BC120,0x40},{0x11FDD31,00},{0x2A0F406,00},{0x206F700,00},{0x0F00000,00},{0x051F101,0x4F},{0x043D211,00},{0x051F102,0x4F},{0x043D212,0x06},{0x053F100,0x4A},{0x074D211,0x04},{0x153F103,0x4F},{0x174D211,0x04},{0x051F101,0x66},{0x0C3D211,00},{0x053F1C0,0x52},{0x094D2D2,0x05},{0x000F312,0x86},{0x033FC18,00},{0x332A8D0,0x4E},{0x2A79212,0x03},{0x064F2C8,0x4F},{0x077F3D1,0x08},{0x0110133,0x0E},{0x0347D34,00},{0x052D117,0x50},{0x192D316,00},{0x077F5E7,0x21},{0x014F6E1,0x06},{0x025DA95,0x4E},{0x015F981,00},{0x033B187,0x4E},{0x042E6F1,0x80},{0x0437D11,0x80},{0x2079132,0x8C},{0x019A1B1,0x03},{0x007F131,0x54},{0x019F5B4,0x83},{0x00FFF24,0x40},{0x00FFF21,0x53},{0x15791D2,0x44},{0x109A1F1,0x80},{0x11FF001,0x52},{0x01FF002,0x88},{0x00BF221,0x4F},{0x10B5232,0x0B},{0x30BF220,0x5D},{0x20B5231,0x07},{0x225F401,0x1B},{0x046F301,0x04},{0x125F411,0x0F},{0x046F301,0x07},{0x21FF101,0x27},{0x088F401,00},{0x132EA12,0x44},{0x1E7D213,0x03},{0x332A430,0x45},{0x0E7F531,00},{0x217F521,0x0F},{0x178F121,0x03},{0x334D101,0x41},{0x3A5C120,0x07},{0x397A710,0x43},{0x2E7E312,0x02},{0x0A4D221,0x01},{0x09FF230,0x16},{0x078F321,0x05},{0x09F8230,0x11},{0x01A7321,0x23},{0x0879321,00},{0x2556230,0x0E},{0x068F321,0x09},{0x1B56230,0x0C},{0x098D522,00},{0x0436470,0x93},{0x043A172,0x43},{0x2434430,0x8D},{0x0439232,0x85},{0x24365E1,0x4E},{0x24461E2,00},{0x24365A1,0x8E},{0x24563A2,0x05},{0x211D1B0,0x87},{0x20173F1,0x89},{0x00543E2,0x43},{0x3F9F831,0x57},{0x2E6F721,0x80},{0x035F132,0x24},{0x035F501,0x82},{0x0B3D200,00},{0x20BD4E0,0x4F},{0x20B55F1,0x03},{0x20596E0,0x52},{0x20135F0,00},{0x20536E1,0x4F},{0x20245F1,00},{0x24321E2,0x48},{0x14541E1,0x81},{0x25241E0,0x16},{0x27220F1,00},{0x25201E0,0x11},{0x272D0F1,00},{0x05261E0,0x1A},{0x27330F1,00},{0x04B8450,0x0B},{0x099A450,0x07},{0x03B9261,0x84},{0x03B9261,0x05},{0x0155220,0x18},{0x024A221,00},{0x2EA74C1,0x94},{0x1F5A3C1,0x84},{0x0B44121,0x28},{0x0988121,00},{0x2AE5121,0x1D},{0x13EE121,0x01},{0x2A651E0,0x93},{0x19781E0,0x82},{0x197E1E1,0x83},{0x20BD8E0,0x4B},{0x10BB3F2,0x0B},{0x20BB8E0,0x49},{0x10BB3F1,0x0B},{0x10B98E0,0x4E},{0x20BC3F0,0x0B},{0x10B88E0,0x4C},{0x10BD3F1,0x0B},{0x0177EF1,0xC5},{0x00E8CE4,0x08},{0x00BD860,0x4F},{0x10BB372,0x0A},{0x01B9172,0x80},{0x019D532,0xC8},{0x01B7371,0x80},{0x08F9EE2,0x6A},{0x02A5562,00},{0x08F7EE0,0xEC},{0x0278462,0x88},{0x01775A2,0x8D},{0x0278462,0x84},{0x00457E3,0x6D},{0x0775762,00},{0x0094272,0x03},{0x00551F2,0xC7},{0x0694272,0x05},{0x15B5123,0x4F},{0x1256031,0x06},{0x29B3122,0x48},{0x165C031,0x06},{0x30457E1,0x23},{0x04D67E1,00},{0x10B78E2,0x42},{0x10BF331,0x12},{0x15211E2,0x21},{0x17340E2,0x07},{0x1075123,0xC0},{0x27935A4,00},{0x1075124,0xC0},{0x20975A0,0x01},{0x0035203,0xC0},{0x055F4A4,0x04},{0x1A631E1,0x93},{0x197A1E1,0x81},{0x20110F0,0xC4},{0x2C11171,0x87},{0x253B1C1,0x4F},{0x27412E0,00},{0x121F9C0,0x6D},{0x0B3F241,0x07},{0x14321E3,0x4C},{0x145A1E2,0x07},{0x15211E3,0x0C},{0x17380E2,0x09},{0x0475526,0xC0},{0x019F888,00},{0x003E523,0xD4},{0x06535E4,00},{0x0623227,0xC0},{0x033A432,0x07},{0x13298D0,0x4E},{0x247A231,0x03},{0x10393F0,0xC0},{0x0024371,0x04},{0x21333F1,0x82},{0x29132F1,0x04},{0x1025811,0x18},{0x072A215,0x0D},{0x053F110,0x80},{0x053F118,0x45},{0x011A131,0x86},{0x0237D17,0x80},{0x054F618,0x40},{0x156F531,0x21},{0x0E89334,0x02},{0x074D615,0x03},{0x20C8B22,0x06},{0x0036131,0x1C},{0x0675222,0x89},{0x0038160,0x0C},{0x10C61F0,0x89},{0x0623127,0x55},{0x075A705,0x05},{0x105F00C,0x87},{0x105F201,0x80},{0x0B7D601,0x07},{0x0C48800,0x0B},{0x0B7D501,00},{0x32B3310,0x80},{0x3DF3B17,00},{0x097EAF7,00},{0x0027312,0x06},{0x0743C18,0x09},{0x2F31408,0x02},{0x20034E7,00},{0x2B210E8,0x0E},{0x0C8AA00,0x0B},{0x2D5F700,00},{0x288ADB1,0x38},{0x2F6A6D0,0x80},{0x038F540,00},{0x641E082,0x23},{0x0501833,0xC1},{0x098C600,00},{0x0F6C582,0x0C},{0x097E303,0x09},{0x050D03F,0xFF},{0x050D1AF,0xFF},{0x1737701,0x07},{0x050D1AF,0xC1},{0x18EADB1,0x38},{0x1B6F801,00},{0x050D23F,0xFF},{0x644E382,0x23},{0x054D42F,0xC1},{0x282A99E,0x38},{0x050D83F,0xFF},{0x050D82F,0xFF},{0x050DA2F,0xFF},{0x050DBBF,0xC1},{0x0659513,00},{0x053DE2F,0xC1},{0x053DF9F,0xC0},{0x050D0BF,0xC1},{0x050D2BF,0xFF},{0x050D3BF,0xC1},{0x050D42F,0xC1},{0x055D59F,0xC1},{0x056D60F,0xC1},{0x280A99E,0x38},{0x055D80F,0xC1},{0x052DA1F,0xC1},{0x07AF4D5,00},{0x072F2D5,00}, +}; + diff --git a/thirdparty/adlmidi/oplinst.h b/thirdparty/adlmidi/oplinst.h new file mode 100644 index 000000000..6bf65caa6 --- /dev/null +++ b/thirdparty/adlmidi/oplinst.h @@ -0,0 +1,122 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OPLINST_H +#define OPLINST_H + +#include +#include +#include + +#pragma pack(push, 1) +#define OPLINST_BYTE_COMPARABLE(T) \ + inline bool operator==(const T &a, const T &b) \ + { return !memcmp(&a, &b, sizeof(T)); } \ + inline bool operator!=(const T &a, const T &b) \ + { return !operator==(a, b); } + +/** + * @brief OPL3 Operator data for single tumbre + */ +struct OplTimbre +{ + //! WaveForm, Sustain/Release, AttackDecay, and AM/VIB/EG/KSR/F-Mult settings + uint32_t modulator_E862, carrier_E862; + //! KSL/attenuation settings + uint8_t modulator_40, carrier_40; + //! Feedback/connection bits for the channel + uint8_t feedconn; + //! Semi-tone offset + int8_t noteOffset; +}; +OPLINST_BYTE_COMPARABLE(struct OplTimbre) + + +enum { OPLNoteOnMaxTime = 40000 }; + + +/** + * @brief Instrument data with operators included + */ +struct OplInstMeta +{ + enum + { + Flag_Pseudo4op = 0x01, + Flag_NoSound = 0x02, + Flag_Real4op = 0x04 + }; + + enum + { + Flag_RM_BassDrum = 0x08, + Flag_RM_Snare = 0x10, + Flag_RM_TomTom = 0x18, + Flag_RM_Cymbal = 0x20, + Flag_RM_HiHat = 0x28, + Mask_RhythmMode = 0x38 + }; + + //! Operator data + OplTimbre op[2]; + //! Fixed note for drum instruments + uint8_t drumTone; + //! Instrument flags + uint8_t flags; + //! Number of milliseconds it produces sound while key on + uint16_t soundKeyOnMs; + //! Number of milliseconds it produces sound while releasing after key off + uint16_t soundKeyOffMs; + //! MIDI velocity offset + int8_t midiVelocityOffset; + //! Second voice detune + double voice2_fine_tune; +}; +OPLINST_BYTE_COMPARABLE(struct OplInstMeta) + +#undef OPLINST_BYTE_COMPARABLE +#pragma pack(pop) + +/** + * @brief Bank global setup + */ +struct OplBankSetup +{ + int volumeModel; + bool deepTremolo; + bool deepVibrato; + bool scaleModulators; + bool mt32defaults; +}; + +/** + * @brief Convert external instrument to internal instrument + */ +void cvt_ADLI_to_FMIns(OplInstMeta &dst, const struct ADL_Instrument &src); + +/** + * @brief Convert internal instrument to external instrument + */ +void cvt_FMIns_to_ADLI(struct ADL_Instrument &dst, const OplInstMeta &src); + +#endif // OPLINST_H diff --git a/thirdparty/adlmidi/structures/pl_list.hpp b/thirdparty/adlmidi/structures/pl_list.hpp new file mode 100644 index 000000000..0cbd23384 --- /dev/null +++ b/thirdparty/adlmidi/structures/pl_list.hpp @@ -0,0 +1,133 @@ +// Copyright Jean Pierre Cimalando 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef PL_LIST_HPP +#define PL_LIST_HPP + +#include +#include + +/* + pl_cell: the linked list cell + */ +template +struct pl_cell; + +template +struct pl_basic_cell +{ + pl_cell *prev, *next; +}; + +template +struct pl_cell : pl_basic_cell +{ + T value; +}; + +/* + pl_iterator: the linked list iterator + */ +template +class pl_iterator +{ +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef Cell value_type; + typedef Cell &reference; + typedef Cell *pointer; + typedef std::ptrdiff_t difference_type; + + pl_iterator(Cell *cell = NULL); + bool is_end() const; + Cell &operator*() const; + Cell *operator->() const; + bool operator==(const pl_iterator &i) const; + bool operator!=(const pl_iterator &i) const; + pl_iterator &operator++(); + pl_iterator operator++(int); + pl_iterator &operator--(); + pl_iterator operator--(int); + +private: + Cell *cell_; +}; + +/* + pl_list: the preallocated linked list + */ +template +class pl_list +{ +public: + typedef pl_cell value_type; + typedef value_type *pointer; + typedef value_type &reference; + typedef const value_type *const_pointer; + typedef const value_type &const_reference; + typedef pl_iterator< pl_cell > iterator; + typedef pl_iterator< const pl_cell > const_iterator; + + pl_list(std::size_t capacity = 0); + ~pl_list(); + + struct external_storage_policy {}; + pl_list(pl_cell *cells, std::size_t ncells, external_storage_policy); + + pl_list(const pl_list &other); + pl_list &operator=(const pl_list &other); + + std::size_t size() const; + std::size_t capacity() const; + bool empty() const; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + void clear(); + + pl_cell &front(); + const pl_cell &front() const; + pl_cell &back(); + const pl_cell &back() const; + + iterator insert(iterator pos, const T &x); + iterator erase(iterator pos); + void push_front(const T &x); + void push_back(const T &x); + void pop_front(); + void pop_back(); + + iterator find(const T &x); + const_iterator find(const T &x) const; + template iterator find_if(const Pred &p); + template const_iterator find_if(const Pred &p) const; + +private: + // number of cells in the list + std::size_t size_; + // number of cells allocated + std::size_t capacity_; + // array of cells allocated + pl_cell *cells_; + // pointer to the head cell + pl_cell *first_; + // pointer to the next free cell + pl_cell *free_; + // value-less cell which terminates the linked list + pl_basic_cell endcell_; + // whether cell storage is allocated + bool cells_allocd_; + + void initialize(std::size_t capacity, pl_cell *extcells = NULL); + pl_cell *allocate(pl_cell *pos); + void deallocate(pl_cell *cell); +}; + +#include "pl_list.tcc" + +#endif // PL_LIST_HPP diff --git a/thirdparty/adlmidi/structures/pl_list.tcc b/thirdparty/adlmidi/structures/pl_list.tcc new file mode 100644 index 000000000..959b08526 --- /dev/null +++ b/thirdparty/adlmidi/structures/pl_list.tcc @@ -0,0 +1,338 @@ +// Copyright Jean Pierre Cimalando 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "pl_list.hpp" + +template +pl_iterator::pl_iterator(Cell *cell) + : cell_(cell) +{ +} + +template +bool pl_iterator::is_end() const +{ + return cell_->next == NULL; +} + +template +Cell &pl_iterator::operator*() const +{ + return *cell_; +} + +template +Cell *pl_iterator::operator->() const +{ + return cell_; +} + +template +bool pl_iterator::operator==(const pl_iterator &i) const +{ + return cell_ == i.cell_; +} + +template +bool pl_iterator::operator!=(const pl_iterator &i) const +{ + return cell_ != i.cell_; +} + +template +pl_iterator &pl_iterator::operator++() +{ + cell_ = cell_->next; + return *this; +} + +template +pl_iterator pl_iterator::operator++(int) +{ + pl_iterator i(cell_); + cell_ = cell_->next; + return i; +} + +template +pl_iterator &pl_iterator::operator--() +{ + cell_ = cell_->prev; + return *this; +} + +template +pl_iterator pl_iterator::operator--(int) +{ + pl_iterator i(cell_); + cell_ = cell_->prev; + return i; +} + +template +pl_list::pl_list(std::size_t capacity) +{ + initialize(capacity); +} + +template +pl_list::~pl_list() +{ + if (cells_allocd_) + delete[] cells_; +} + +template +pl_list::pl_list(pl_cell *cells, std::size_t ncells, external_storage_policy) +{ + initialize(ncells, cells); +} + +template +pl_list::pl_list(const pl_list &other) +{ + initialize(other.capacity()); + for(const_iterator i = other.end(), b = other.begin(); i-- != b;) + push_front(i->value); +} + +template +pl_list &pl_list::operator=(const pl_list &other) +{ + if(this != &other) + { + std::size_t size = other.size(); + if(size > capacity()) + { + pl_cell *oldcells = cells_; + bool allocd = cells_allocd_; + initialize(other.capacity()); + if (allocd) + delete[] oldcells; + } + clear(); + for(const_iterator i = other.end(), b = other.begin(); i-- != b;) + push_front(i->value); + } + return *this; +} + +template +std::size_t pl_list::size() const +{ + return size_; +} + +template +std::size_t pl_list::capacity() const +{ + return capacity_; +} + +template +bool pl_list::empty() const +{ + return size_ == 0; +} + +template +typename pl_list::iterator pl_list::begin() +{ + return iterator(first_); +} + +template +typename pl_list::iterator pl_list::end() +{ + return iterator(reinterpret_cast *>(&endcell_)); +} + +template +typename pl_list::const_iterator pl_list::begin() const +{ + return const_iterator(first_); +} + +template +typename pl_list::const_iterator pl_list::end() const +{ + return const_iterator(reinterpret_cast *>(&endcell_)); +} + +template +void pl_list::clear() +{ + std::size_t capacity = capacity_; + pl_cell *cells = cells_; + pl_cell *endcell = &*end(); + size_ = 0; + first_ = endcell; + free_ = cells; + endcell->prev = NULL; + for(std::size_t i = 0; i < capacity; ++i) + { + cells[i].prev = (i > 0) ? &cells[i - 1] : NULL; + cells[i].next = (i + 1 < capacity) ? &cells[i + 1] : NULL; + cells[i].value = T(); + } +} + +template +pl_cell &pl_list::front() +{ + return *first_; +} + +template +const pl_cell &pl_list::front() const +{ + return *first_; +} + +template +pl_cell &pl_list::back() +{ + iterator i = end(); + return *--i; +} + +template +const pl_cell &pl_list::back() const +{ + const_iterator i = end(); + return *--i; +} + +template +typename pl_list::iterator pl_list::insert(iterator pos, const T &x) +{ + pl_cell *cell = allocate(&*pos); + if (!cell) + throw std::bad_alloc(); + cell->value = x; + return iterator(cell); +} + +template +typename pl_list::iterator pl_list::erase(iterator pos) +{ + deallocate(&*(pos++)); + return pos; +} + +template +void pl_list::push_front(const T &x) +{ + insert(begin(), x); +} + +template +void pl_list::push_back(const T &x) +{ + insert(end(), x); +} + +template +void pl_list::pop_front() +{ + deallocate(first_); +} + +template +void pl_list::pop_back() +{ + iterator i(&*end()); + deallocate(&*--i); +} + +template +typename pl_list::iterator pl_list::find(const T &x) +{ + const_iterator i = const_cast *>(this)->find(x); + return iterator(&const_cast(*i)); +} + +template +typename pl_list::const_iterator pl_list::find(const T &x) const +{ + const_iterator e = end(); + for (const_iterator i = begin(); i != e; ++i) + { + if(i->value == x) + return i; + } + return e; +} + +template +template +typename pl_list::iterator pl_list::find_if(const Pred &p) +{ + const_iterator i = const_cast *>(this)->find_if(p); + return iterator(&const_cast(*i)); +} + +template +template +typename pl_list::const_iterator pl_list::find_if(const Pred &p) const +{ + const_iterator e = end(); + for (const_iterator i = begin(); i != e; ++i) + { + if(p(i->value)) + return i; + } + return e; +} + +template +void pl_list::initialize(std::size_t capacity, pl_cell *extcells) +{ + cells_ = extcells ? extcells : new pl_cell[capacity]; + cells_allocd_ = extcells ? false : true; + capacity_ = capacity; + endcell_.next = NULL; + clear(); +} + +template +pl_cell *pl_list::allocate(pl_cell *pos) +{ + // remove free cells front + pl_cell *cell = free_; + if(!cell) + return NULL; + free_ = cell->next; + if(free_) + free_->prev = NULL; + + // insert at position + if (pos == first_) + first_ = cell; + cell->prev = pos->prev; + if (cell->prev) + cell->prev->next = cell; + cell->next = pos; + pos->prev = cell; + + ++size_; + return cell; +} + +template +void pl_list::deallocate(pl_cell *cell) +{ + if(cell->prev) + cell->prev->next = cell->next; + if(cell->next) + cell->next->prev = cell->prev; + if(cell == first_) + first_ = cell->next; + cell->prev = NULL; + cell->next = free_; + cell->value = T(); + free_ = cell; + --size_; +} diff --git a/thirdparty/adlmidi/wopl/wopl_file.c b/thirdparty/adlmidi/wopl/wopl_file.c new file mode 100644 index 000000000..685dc2a0c --- /dev/null +++ b/thirdparty/adlmidi/wopl/wopl_file.c @@ -0,0 +1,601 @@ +/* + * Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup + * + * Copyright (c) 2015-2025 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "wopl_file.h" +#include +#include + +static const char *wopl3_magic = "WOPL3-BANK\0"; +static const char *wopli_magic = "WOPL3-INST\0"; + +static const uint16_t wopl_latest_version = 3; + +#define WOPL_INST_SIZE_V2 62 +#define WOPL_INST_SIZE_V3 66 + +static uint16_t toUint16LE(const uint8_t *arr) +{ + uint16_t num = arr[0]; + num |= ((arr[1] << 8) & 0xFF00); + return num; +} + +static uint16_t toUint16BE(const uint8_t *arr) +{ + uint16_t num = arr[1]; + num |= ((arr[0] << 8) & 0xFF00); + return num; +} + +static int16_t toSint16BE(const uint8_t *arr) +{ + int16_t num = *(const int8_t *)(&arr[0]); + num *= 1 << 8; + num |= arr[1]; + return num; +} + +static void fromUint16LE(uint16_t in, uint8_t *arr) +{ + arr[0] = in & 0x00FF; + arr[1] = (in >> 8) & 0x00FF; +} + +static void fromUint16BE(uint16_t in, uint8_t *arr) +{ + arr[1] = in & 0x00FF; + arr[0] = (in >> 8) & 0x00FF; +} + +static void fromSint16BE(int16_t in, uint8_t *arr) +{ + arr[1] = in & 0x00FF; + arr[0] = ((uint16_t)in >> 8) & 0x00FF; +} + + +WOPLFile *WOPL_Init(uint16_t melodic_banks, uint16_t percussive_banks) +{ + WOPLFile *file = (WOPLFile*)calloc(1, sizeof(WOPLFile)); + if(!file) + return NULL; + + file->banks_count_melodic = (melodic_banks != 0) ? melodic_banks : 1; + file->banks_melodic = (WOPLBank*)calloc(file->banks_count_melodic, sizeof(WOPLBank)); + + if(melodic_banks == 0) + { + unsigned i; + for(i = 0; i < 128; ++i) + file->banks_melodic[0].ins[i].inst_flags = WOPL_Ins_IsBlank; + } + + file->banks_count_percussion = (percussive_banks != 0) ? percussive_banks : 1; + file->banks_percussive = (WOPLBank*)calloc(file->banks_count_percussion, sizeof(WOPLBank)); + + if(percussive_banks == 0) + { + unsigned i; + for(i = 0; i < 128; ++i) + file->banks_percussive[0].ins[i].inst_flags = WOPL_Ins_IsBlank; + } + + return file; +} + +void WOPL_Free(WOPLFile *file) +{ + if(file) + { + if(file->banks_melodic) + free(file->banks_melodic); + if(file->banks_percussive) + free(file->banks_percussive); + free(file); + } +} + +int WOPL_BanksCmp(const WOPLFile *bank1, const WOPLFile *bank2) +{ + int res = 1; + + res &= (bank1->version == bank2->version); + res &= (bank1->opl_flags == bank2->opl_flags); + res &= (bank1->volume_model == bank2->volume_model); + res &= (bank1->banks_count_melodic == bank2->banks_count_melodic); + res &= (bank1->banks_count_percussion == bank2->banks_count_percussion); + + if(res) + { + int i; + for(i = 0; i < bank1->banks_count_melodic; i++) + res &= (memcmp(&bank1->banks_melodic[i], &bank2->banks_melodic[i], sizeof(WOPLBank)) == 0); + if(res) + { + for(i = 0; i < bank1->banks_count_percussion; i++) + res &= (memcmp(&bank1->banks_percussive[i], &bank2->banks_percussive[i], sizeof(WOPLBank)) == 0); + } + } + + return res; +} + +static void WOPL_parseInstrument(WOPLInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) +{ + int l; + strncpy(ins->inst_name, (const char*)cursor, 32); + ins->inst_name[32] = '\0'; + ins->note_offset1 = toSint16BE(cursor + 32); + ins->note_offset2 = toSint16BE(cursor + 34); + ins->midi_velocity_offset = (int8_t)cursor[36]; + ins->second_voice_detune = (int8_t)cursor[37]; + ins->percussion_key_number = cursor[38]; + ins->inst_flags = cursor[39]; + ins->fb_conn1_C0 = cursor[40]; + ins->fb_conn2_C0 = cursor[41]; + for(l = 0; l < 4; l++) + { + size_t off = 42 + (size_t)(l) * 5; + ins->operators[l].avekf_20 = cursor[off + 0]; + ins->operators[l].ksl_l_40 = cursor[off + 1]; + ins->operators[l].atdec_60 = cursor[off + 2]; + ins->operators[l].susrel_80 = cursor[off + 3]; + ins->operators[l].waveform_E0 = cursor[off + 4]; + } + if((version >= 3) && has_sounding_delays) + { + ins->delay_on_ms = toUint16BE(cursor + 62); + ins->delay_off_ms = toUint16BE(cursor + 64); + } +} + +static void WOPL_writeInstrument(WOPLInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) +{ + int l; + memcpy((char*)cursor, ins->inst_name, 32); + cursor[32] = '\0'; + fromSint16BE(ins->note_offset1, cursor + 32); + fromSint16BE(ins->note_offset2, cursor + 34); + cursor[36] = (uint8_t)ins->midi_velocity_offset; + cursor[37] = (uint8_t)ins->second_voice_detune; + cursor[38] = ins->percussion_key_number; + cursor[39] = ins->inst_flags; + cursor[40] = ins->fb_conn1_C0; + cursor[41] = ins->fb_conn2_C0; + for(l = 0; l < 4; l++) + { + size_t off = 42 + (size_t)(l) * 5; + cursor[off + 0] = ins->operators[l].avekf_20; + cursor[off + 1] = ins->operators[l].ksl_l_40; + cursor[off + 2] = ins->operators[l].atdec_60; + cursor[off + 3] = ins->operators[l].susrel_80; + cursor[off + 4] = ins->operators[l].waveform_E0; + } + if((version >= 3) && has_sounding_delays) + { + fromUint16BE(ins->delay_on_ms, cursor + 62); + fromUint16BE(ins->delay_off_ms, cursor + 64); + } +} + +WOPLFile *WOPL_LoadBankFromMem(void *mem, size_t length, int *error) +{ + WOPLFile *outFile = NULL; + uint16_t i = 0, j = 0, k = 0; + uint16_t version = 0; + uint16_t count_melodic_banks = 1; + uint16_t count_percusive_banks = 1; + uint8_t *cursor = (uint8_t *)mem; + + WOPLBank *bankslots[2]; + uint16_t bankslots_sizes[2]; + +#define SET_ERROR(err) \ +{\ + WOPL_Free(outFile);\ + if(error)\ + {\ + *error = err;\ + }\ +} + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + if(!cursor) + { + SET_ERROR(WOPL_ERR_NULL_POINTER); + return NULL; + } + + {/* Magic number */ + if(length < 11) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + if(memcmp(cursor, wopl3_magic, 11) != 0) + { + SET_ERROR(WOPL_ERR_BAD_MAGIC); + return NULL; + } + GO_FORWARD(11); + } + + {/* Version code */ + if(length < 2) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + version = toUint16LE(cursor); + if(version > wopl_latest_version) + { + SET_ERROR(WOPL_ERR_NEWER_VERSION); + return NULL; + } + GO_FORWARD(2); + } + + {/* Header of WOPL */ + uint8_t head[6]; + if(length < 6) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + memcpy(head, cursor, 6); + count_melodic_banks = toUint16BE(head); + count_percusive_banks = toUint16BE(head + 2); + GO_FORWARD(6); + + outFile = WOPL_Init(count_melodic_banks, count_percusive_banks); + if(!outFile) + { + SET_ERROR(WOPL_ERR_OUT_OF_MEMORY); + return NULL; + } + + outFile->version = version; + outFile->opl_flags = head[4]; + outFile->volume_model = head[5]; + } + + bankslots_sizes[0] = count_melodic_banks; + bankslots[0] = outFile->banks_melodic; + bankslots_sizes[1] = count_percusive_banks; + bankslots[1] = outFile->banks_percussive; + + if(version >= 2) /* Bank names and LSB/MSB titles */ + { + for(i = 0; i < 2; i++) + { + for(j = 0; j < bankslots_sizes[i]; j++) + { + if(length < 34) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + strncpy(bankslots[i][j].bank_name, (const char*)cursor, 32); + bankslots[i][j].bank_name[32] = '\0'; + bankslots[i][j].bank_midi_lsb = cursor[32]; + bankslots[i][j].bank_midi_msb = cursor[33]; + GO_FORWARD(34); + } + } + } + + {/* Read instruments data */ + uint16_t insSize = 0; + if(version > 2) + insSize = WOPL_INST_SIZE_V3; + else + insSize = WOPL_INST_SIZE_V2; + for(i = 0; i < 2; i++) + { + if(length < (insSize * 128) * (size_t)bankslots_sizes[i]) + { + SET_ERROR(WOPL_ERR_UNEXPECTED_ENDING); + return NULL; + } + + for(j = 0; j < bankslots_sizes[i]; j++) + { + for(k = 0; k < 128; k++) + { + WOPLInstrument *ins = &bankslots[i][j].ins[k]; + WOPL_parseInstrument(ins, cursor, version, 1); + GO_FORWARD(insSize); + } + } + } + } + +#undef GO_FORWARD +#undef SET_ERROR + + return outFile; +} + +int WOPL_LoadInstFromMem(WOPIFile *file, void *mem, size_t length) +{ + uint16_t version = 0; + uint8_t *cursor = (uint8_t *)mem; + uint16_t ins_size; + + if(!cursor) + return WOPL_ERR_NULL_POINTER; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + {/* Magic number */ + if(length < 11) + return WOPL_ERR_UNEXPECTED_ENDING; + if(memcmp(cursor, wopli_magic, 11) != 0) + return WOPL_ERR_BAD_MAGIC; + GO_FORWARD(11); + } + + {/* Version code */ + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + version = toUint16LE(cursor); + if(version > wopl_latest_version) + return WOPL_ERR_NEWER_VERSION; + GO_FORWARD(2); + } + + file->version = version; + + {/* is drum flag */ + if(length < 1) + return WOPL_ERR_UNEXPECTED_ENDING; + file->is_drum = *cursor; + GO_FORWARD(1); + } + + if(version > 2) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2); + else + ins_size = WOPL_INST_SIZE_V2; + + if(length < ins_size) + return WOPL_ERR_UNEXPECTED_ENDING; + + WOPL_parseInstrument(&file->inst, cursor, version, 0); + GO_FORWARD(ins_size); + + return WOPL_ERR_OK; +#undef GO_FORWARD +} + +size_t WOPL_CalculateBankFileSize(WOPLFile *file, uint16_t version) +{ + size_t final_size = 0; + size_t ins_size = 0; + + if(version == 0) + version = wopl_latest_version; + + if(!file) + return 0; + final_size += 11 + 2 + 2 + 2 + 1 + 1; + /* + * Magic number, + * Version, + * Count of melodic banks, + * Count of percussive banks, + * Chip specific flags + * Volume Model + */ + + if(version >= 2) + { + /* Melodic banks meta-data */ + final_size += (32 + 1 + 1) * file->banks_count_melodic; + /* Percussive banks meta-data */ + final_size += (32 + 1 + 1) * file->banks_count_percussion; + } + + if(version >= 3) + ins_size = WOPL_INST_SIZE_V3; + else + ins_size = WOPL_INST_SIZE_V2; + /* Melodic instruments */ + final_size += (ins_size * 128) * file->banks_count_melodic; + /* Percusive instruments */ + final_size += (ins_size * 128) * file->banks_count_percussion; + + return final_size; +} + +size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version) +{ + size_t final_size = 0; + size_t ins_size = 0; + + if(version == 0) + version = wopl_latest_version; + + if(!file) + return 0; + final_size += 11 + 2 + 1; + /* + * Magic number, + * version, + * is percussive instrument + */ + + if(version > 2) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2); + else + ins_size = WOPL_INST_SIZE_V2; + final_size += ins_size; + + return final_size; +} + +int WOPL_SaveBankToMem(WOPLFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm) +{ + uint8_t *cursor = (uint8_t *)dest_mem; + uint16_t ins_size = 0; + uint16_t i, j, k; + uint16_t banks_melodic = force_gm ? 1 : file->banks_count_melodic; + uint16_t banks_percusive = force_gm ? 1 : file->banks_count_percussion; + + WOPLBank *bankslots[2]; + uint16_t bankslots_sizes[2]; + + if(version == 0) + version = wopl_latest_version; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + if(length < 11) + return WOPL_ERR_UNEXPECTED_ENDING; + memcpy(cursor, wopl3_magic, 11); + GO_FORWARD(11); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16LE(version, cursor); + GO_FORWARD(2); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16BE(banks_melodic, cursor); + GO_FORWARD(2); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16BE(banks_percusive, cursor); + GO_FORWARD(2); + + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + cursor[0] = file->opl_flags; + cursor[1] = file->volume_model; + GO_FORWARD(2); + + bankslots[0] = file->banks_melodic; + bankslots_sizes[0] = banks_melodic; + bankslots[1] = file->banks_percussive; + bankslots_sizes[1] = banks_percusive; + + if(version >= 2) + { + for(i = 0; i < 2; i++) + { + for(j = 0; j < bankslots_sizes[i]; j++) + { + if(length < 34) + return WOPL_ERR_UNEXPECTED_ENDING; + strncpy((char*)cursor, bankslots[i][j].bank_name, 32); + cursor[32] = bankslots[i][j].bank_midi_lsb; + cursor[33] = bankslots[i][j].bank_midi_msb; + GO_FORWARD(34); + } + } + } + + {/* Write instruments data */ + if(version >= 3) + ins_size = WOPL_INST_SIZE_V3; + else + ins_size = WOPL_INST_SIZE_V2; + for(i = 0; i < 2; i++) + { + if(length < (ins_size * 128) * (size_t)bankslots_sizes[i]) + return WOPL_ERR_UNEXPECTED_ENDING; + + for(j = 0; j < bankslots_sizes[i]; j++) + { + for(k = 0; k < 128; k++) + { + WOPLInstrument *ins = &bankslots[i][j].ins[k]; + WOPL_writeInstrument(ins, cursor, version, 1); + GO_FORWARD(ins_size); + } + } + } + } + + return WOPL_ERR_OK; +#undef GO_FORWARD +} + +int WOPL_SaveInstToMem(WOPIFile *file, void *dest_mem, size_t length, uint16_t version) +{ + uint8_t *cursor = (uint8_t *)dest_mem; + uint16_t ins_size; + + if(!cursor) + return WOPL_ERR_NULL_POINTER; + + if(version == 0) + version = wopl_latest_version; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + {/* Magic number */ + if(length < 11) + return WOPL_ERR_UNEXPECTED_ENDING; + memcpy(cursor, wopli_magic, 11); + GO_FORWARD(11); + } + + {/* Version code */ + if(length < 2) + return WOPL_ERR_UNEXPECTED_ENDING; + fromUint16LE(version, cursor); + GO_FORWARD(2); + } + + {/* is drum flag */ + if(length < 1) + return WOPL_ERR_UNEXPECTED_ENDING; + *cursor = file->is_drum; + GO_FORWARD(1); + } + + if(version > 2) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2); + else + ins_size = WOPL_INST_SIZE_V2; + + if(length < ins_size) + return WOPL_ERR_UNEXPECTED_ENDING; + + WOPL_writeInstrument(&file->inst, cursor, version, 0); + GO_FORWARD(ins_size); + + return WOPL_ERR_OK; +#undef GO_FORWARD +} diff --git a/thirdparty/adlmidi/wopl/wopl_file.h b/thirdparty/adlmidi/wopl/wopl_file.h new file mode 100644 index 000000000..bfa28c097 --- /dev/null +++ b/thirdparty/adlmidi/wopl/wopl_file.h @@ -0,0 +1,314 @@ +/* + * Wohlstand's OPL3 Bank File - a bank format to store OPL3 timbre data and setup + * + * Copyright (c) 2015-2025 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef WOPL_FILE_H +#define WOPL_FILE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Solaris defines the integer types regardless of what C/C++ standard is actually available, + * so avoid defining them at all by ourselves. */ +#if !defined(WOPL_STDINT_TYPEDEFS_NOT_NEEDED) && defined(__sun) +# define WOPL_STDINT_TYPEDEFS_NOT_NEEDED +#endif + +#if !defined(WOPL_STDINT_TYPEDEFS_NEEDED) && !defined(WOPL_STDINT_TYPEDEFS_NOT_NEEDED) +# if !defined(__STDC_VERSION__) || \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L)) || \ + defined(__STRICT_ANSI__) || \ + !defined(__cplusplus) +# define WOPL_STDINT_TYPEDEFS_NEEDED +# endif +#endif + +#ifdef WOPL_STDINT_TYPEDEFS_NEEDED +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int int16_t; +typedef unsigned short int uint16_t; +#endif + +/* Global OPL flags */ +typedef enum WOPLFileFlags +{ + /* Enable Deep-Tremolo flag */ + WOPL_FLAG_DEEP_TREMOLO = 0x01, + /* Enable Deep-Vibrato flag */ + WOPL_FLAG_DEEP_VIBRATO = 0x02, + /* Enable MT32 defaults (127 initials and octave-wide pitch bend by default, etc.) */ + WOPL_FLAG_MT32 = 0x04 +} WOPLFileFlags; + +/* Volume scaling model implemented in the libADLMIDI */ +typedef enum WOPL_VolumeModel +{ + WOPL_VM_Generic = 0, + WOPL_VM_Native, + WOPL_VM_DMX, + WOPL_VM_Apogee, + WOPL_VM_Win9x, + WOPL_VM_DMX_Fixed, + WOPL_VM_Apogee_Fixed, + WOPL_VM_AIL, + WOPL_VM_Win9x_GenericFM, + WOPL_VM_HMI +} WOPL_VolumeModel; + +typedef enum WOPL_InstrumentFlags +{ + /* Is two-operator single-voice instrument (no flags) */ + WOPL_Ins_2op = 0x00, + /* Is true four-operator instrument */ + WOPL_Ins_4op = 0x01, + /* Is pseudo four-operator (two 2-operator voices) instrument */ + WOPL_Ins_Pseudo4op = 0x02, + /* Is a blank instrument entry */ + WOPL_Ins_IsBlank = 0x04, + + /* RythmMode flags mask */ + WOPL_RhythmModeMask = 0x38, + + /* Mask of the flags range */ + WOPL_Ins_ALL_MASK = 0x07 +} WOPL_InstrumentFlags; + +typedef enum WOPL_RhythmMode +{ + /* RythmMode: BassDrum */ + WOPL_RM_BassDrum = 0x08, + /* RythmMode: Snare */ + WOPL_RM_Snare = 0x10, + /* RythmMode: TomTom */ + WOPL_RM_TomTom = 0x18, + /* RythmMode: Cymbell */ + WOPL_RM_Cymbal = 0x20, + /* RythmMode: HiHat */ + WOPL_RM_HiHat = 0x28 +} WOPL_RhythmMode; + +/* DEPRECATED: It has typo. Don't use it! */ +typedef WOPL_RhythmMode WOPL_RythmMode; + +/* Error codes */ +typedef enum WOPL_ErrorCodes +{ + WOPL_ERR_OK = 0, + /* Magic number is not maching */ + WOPL_ERR_BAD_MAGIC, + /* Too short file */ + WOPL_ERR_UNEXPECTED_ENDING, + /* Zero banks count */ + WOPL_ERR_INVALID_BANKS_COUNT, + /* Version of file is newer than supported by current version of library */ + WOPL_ERR_NEWER_VERSION, + /* Out of memory */ + WOPL_ERR_OUT_OF_MEMORY, + /* Given null pointer memory data */ + WOPL_ERR_NULL_POINTER +} WOPL_ErrorCodes; + +/* Operator indeces inside of Instrument Entry */ +#define WOPL_OP_CARRIER1 0 +#define WOPL_OP_MODULATOR1 1 +#define WOPL_OP_CARRIER2 2 +#define WOPL_OP_MODULATOR2 3 + +/* OPL3 Oerators data */ +typedef struct WOPLOperator +{ + /* AM/Vib/Env/Ksr/FMult characteristics */ + uint8_t avekf_20; + /* Key Scale Level / Total level register data */ + uint8_t ksl_l_40; + /* Attack / Decay */ + uint8_t atdec_60; + /* Systain and Release register data */ + uint8_t susrel_80; + /* Wave form */ + uint8_t waveform_E0; +} WOPLOperator; + +/* Instrument entry */ +typedef struct WOPLInstrument +{ + /* Title of the instrument */ + char inst_name[34]; + /* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + int16_t note_offset1; + /* MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */ + int16_t note_offset2; + /* MIDI note velocity offset (taken from Apogee TMB format) */ + int8_t midi_velocity_offset; + /* Second voice detune level (taken from DMX OP2) */ + int8_t second_voice_detune; + /* Percussion MIDI base tone number at which this drum will be played */ + uint8_t percussion_key_number; + /* Enum WOPL_InstrumentFlags */ + uint8_t inst_flags; + /* Feedback&Connection register for first and second operators */ + uint8_t fb_conn1_C0; + /* Feedback&Connection register for third and fourth operators */ + uint8_t fb_conn2_C0; + /* Operators register data */ + WOPLOperator operators[4]; + /* Millisecond delay of sounding while key is on */ + uint16_t delay_on_ms; + /* Millisecond delay of sounding after key off */ + uint16_t delay_off_ms; +} WOPLInstrument; + +/* Bank entry */ +typedef struct WOPLBank +{ + /* Name of bank */ + char bank_name[33]; + /* MIDI Bank LSB code */ + uint8_t bank_midi_lsb; + /* MIDI Bank MSB code */ + uint8_t bank_midi_msb; + /* Instruments data of this bank */ + WOPLInstrument ins[128]; +} WOPLBank; + +/* Instrument data file */ +typedef struct WOPIFile +{ + /* Version of instrument file */ + uint16_t version; + /* Is this a percussion instrument */ + uint8_t is_drum; + /* Instrument data */ + WOPLInstrument inst; +} WOPIFile; + +/* Bank data file */ +typedef struct WOPLFile +{ + /* Version of bank file */ + uint16_t version; + /* Count of melodic banks in this file */ + uint16_t banks_count_melodic; + /* Count of percussion banks in this file */ + uint16_t banks_count_percussion; + /* Enum WOPLFileFlags */ + uint8_t opl_flags; + /* Enum WOPL_VolumeModel */ + uint8_t volume_model; + /* dynamically allocated data Melodic banks array */ + WOPLBank *banks_melodic; + /* dynamically allocated data Percussive banks array */ + WOPLBank *banks_percussive; +} WOPLFile; + + +/** + * @brief Initialize blank WOPL data structure with allocated bank data + * @param melodic_banks Count of melodic banks + * @param percussive_banks Count of percussive banks + * @return pointer to heap-allocated WOPL data structure or NULL when out of memory or incorrectly given banks counts + */ +extern WOPLFile *WOPL_Init(uint16_t melodic_banks, uint16_t percussive_banks); + +/** + * @brief Clean up WOPL data file (all allocated bank arrays will be fried too) + * @param file pointer to heap-allocated WOPL data structure + */ +extern void WOPL_Free(WOPLFile *file); + +/** + * @brief Compare two bank entries + * @param bank1 First bank + * @param bank2 Second bank + * @return 1 if banks are equal or 0 if there are different + */ +extern int WOPL_BanksCmp(const WOPLFile *bank1, const WOPLFile *bank2); + + +/** + * @brief Load WOPL bank file from the memory. + * WOPL data structure will be allocated. (don't forget to clear it with WOPL_Free() after use!) + * @param mem Pointer to memory block contains raw WOPL bank file data + * @param length Length of given memory block + * @param error pointer to integer to return an error code. Pass NULL if you don't want to use error codes. + * @return Heap-allocated WOPL file data structure or NULL if any error has occouped + */ +extern WOPLFile *WOPL_LoadBankFromMem(void *mem, size_t length, int *error); + +/** + * @brief Load WOPI instrument file from the memory. + * You must allocate WOPIFile structure by yourself and give the pointer to it. + * @param file Pointer to destination WOPIFile structure to fill it with parsed data. + * @param mem Pointer to memory block contains raw WOPI instrument file data + * @param length Length of given memory block + * @return 0 if no errors occouped, or an error code of WOPL_ErrorCodes enumeration + */ +extern int WOPL_LoadInstFromMem(WOPIFile *file, void *mem, size_t length); + +/** + * @brief Calculate the size of the output memory block + * @param file Heap-allocated WOPL file data structure + * @param version Destination version of the file + * @return Size of the raw WOPL file data + */ +extern size_t WOPL_CalculateBankFileSize(WOPLFile *file, uint16_t version); + +/** + * @brief Calculate the size of the output memory block + * @param file Pointer to WOPI file data structure + * @param version Destination version of the file + * @return Size of the raw WOPI file data + */ +extern size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version); + +/** + * @brief Write raw WOPL into given memory block + * @param file Heap-allocated WOPL file data structure + * @param dest_mem Destination memory block pointer + * @param length Length of destination memory block + * @param version Wanted WOPL version + * @param force_gm Force GM set in saved bank file + * @return Error code or 0 on success + */ +extern int WOPL_SaveBankToMem(WOPLFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm); + +/** + * @brief Write raw WOPI into given memory block + * @param file Pointer to WOPI file data structure + * @param dest_mem Destination memory block pointer + * @param length Length of destination memory block + * @param version Wanted WOPI version + * @return Error code or 0 on success + */ +extern int WOPL_SaveInstToMem(WOPIFile *file, void *dest_mem, size_t length, uint16_t version); + +#ifdef __cplusplus +} +#endif + +#endif /* WOPL_FILE_H */ diff --git a/thirdparty/dumb/CMakeLists.txt b/thirdparty/dumb/CMakeLists.txt new file mode 100644 index 000000000..5e621913c --- /dev/null +++ b/thirdparty/dumb/CMakeLists.txt @@ -0,0 +1,124 @@ +make_release_only() + +include(CheckFunctionExists) +include(CheckCXXCompilerFlag) +include(CheckIncludeFile) + +check_include_file(alloca.h HAVE_ALLOCA_H) +if(HAVE_ALLOCA_H) +add_compile_definitions(HAVE_ALLOCA_H=1) +endif() + +add_library(dumb OBJECT + src/core/unload.c + src/core/rendsig.c + src/core/rendduh.c + src/core/register.c + src/core/readduh.c + src/core/rawsig.c + src/core/makeduh.c + src/core/loadduh.c + src/core/dumbfile.c + src/core/duhtag.c + src/core/duhlen.c + src/core/atexit.c + src/helpers/stdfile.c + src/helpers/silence.c + src/helpers/sampbuf.c + src/helpers/riff.c + src/helpers/resample.c + src/helpers/memfile.c + src/helpers/clickrem.c + src/helpers/barray.c + src/it/xmeffect.c + src/it/readxm2.c + src/it/readxm.c + src/it/readstm2.c + src/it/readstm.c + src/it/reads3m2.c + src/it/reads3m.c + src/it/readriff.c + src/it/readptm.c + src/it/readpsm.c + src/it/readoldpsm.c + src/it/readokt2.c + src/it/readokt.c + src/it/readmtm.c + src/it/readmod2.c + src/it/readmod.c + src/it/readdsmf.c + src/it/readasy.c + src/it/readamf2.c + src/it/readamf.c + src/it/readam.c + src/it/read6692.c + src/it/read669.c + src/it/ptmeffect.c + src/it/loadxm2.c + src/it/loadxm.c + src/it/loadstm2.c + src/it/loadstm.c + src/it/loads3m2.c + src/it/loads3m.c + src/it/loadriff2.c + src/it/loadriff.c + src/it/loadptm2.c + src/it/loadptm.c + src/it/loadpsm2.c + src/it/loadpsm.c + src/it/loadoldpsm2.c + src/it/loadoldpsm.c + src/it/loadokt2.c + src/it/loadokt.c + src/it/loadmtm2.c + src/it/loadmtm.c + src/it/loadmod2.c + src/it/loadmod.c + src/it/loadasy2.c + src/it/loadasy.c + src/it/loadamf2.c + src/it/loadamf.c + src/it/load6692.c + src/it/load669.c + src/it/itunload.c + src/it/itrender.c + src/it/itread2.c + src/it/itread.c + src/it/itorder.c + src/it/itmisc.c + src/it/itload2.c + src/it/itload.c + src/it/readany.c + src/it/loadany2.c + src/it/loadany.c + src/it/readany2.c + src/helpers/resampler.c + src/helpers/lpc.c +) + +use_fast_math(dumb) + +target_include_directories(dumb PUBLIC include) + +if(COMPILER_IS_GNUC_COMPATIBLE) + target_compile_options(dumb + PRIVATE + -Wall -Wno-pointer-sign -Wno-uninitialized + ) + if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER "4.5") + target_compile_options(dumb PRIVATE -Wno-unused-but-set-variable) + endif() +endif() + +check_function_exists(itoa ITOA_EXISTS) +if(NOT ITOA_EXISTS) + target_compile_definitions(dumb PRIVATE NEED_ITOA=1) +endif() + +if(COMPILER_IS_GNUCXX_COMPATIBLE) + check_cxx_compiler_flag(-msse DUMB_CAN_USE_SSE) + + if(DUMB_CAN_USE_SSE) + set_source_files_properties(src/helpers/resampler.c PROPERTIES COMPILE_FLAGS -msse) + endif() +endif() diff --git a/thirdparty/dumb/cmake/CMakeLists.txt b/thirdparty/dumb/cmake/CMakeLists.txt new file mode 100644 index 000000000..6cafa7219 --- /dev/null +++ b/thirdparty/dumb/cmake/CMakeLists.txt @@ -0,0 +1,118 @@ +cmake_minimum_required(VERSION 2.8.7) +project(libdumb C) + +set(CMAKE_C_FLAGS "-Wall -DDUMB_DECLARE_DEPRECATED -D_USE_SSE -msse -Wno-unused-variable -Wno-unused-but-set-variable") +set(CMAKE_C_FLAGS_DEBUG "-ggdb -DDEBUGMODE=1 -D_DEBUG") +set(CMAKE_C_FLAGS_RELEASE "-ffast-math -O2 -DNDEBUG") +set(CMAKE_C_FLAGS_RELWITHDEBINFO "-ffast-math -g -O2 -DNDEBUG") +set(CMAKE_C_FLAGS_MINSIZEREL "-ffast-math -Os -DNDEBUG") + +link_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(../include/) + +SET(SOURCES + ../src/core/unload.c + ../src/core/rendsig.c + ../src/core/rendduh.c + ../src/core/register.c + ../src/core/readduh.c + ../src/core/rawsig.c + ../src/core/makeduh.c + ../src/core/loadduh.c + ../src/core/dumbfile.c + ../src/core/duhtag.c + ../src/core/duhlen.c + ../src/core/atexit.c + ../src/helpers/stdfile.c + ../src/helpers/silence.c + ../src/helpers/sampbuf.c + ../src/helpers/riff.c + ../src/helpers/resample.c + ../src/helpers/memfile.c + ../src/helpers/clickrem.c + ../src/helpers/barray.c + ../src/helpers/tarray.c + ../src/it/xmeffect.c + ../src/it/readxm2.c + ../src/it/readxm.c + ../src/it/readstm2.c + ../src/it/readstm.c + ../src/it/reads3m2.c + ../src/it/reads3m.c + ../src/it/readriff.c + ../src/it/readptm.c + ../src/it/readpsm.c + ../src/it/readoldpsm.c + ../src/it/readokt2.c + ../src/it/readokt.c + ../src/it/readmtm.c + ../src/it/readmod2.c + ../src/it/readmod.c + ../src/it/readdsmf.c + ../src/it/readasy.c + ../src/it/readamf2.c + ../src/it/readamf.c + ../src/it/readam.c + ../src/it/read6692.c + ../src/it/read669.c + ../src/it/ptmeffect.c + ../src/it/loadxm2.c + ../src/it/loadxm.c + ../src/it/loadstm2.c + ../src/it/loadstm.c + ../src/it/loads3m2.c + ../src/it/loads3m.c + ../src/it/loadriff2.c + ../src/it/loadriff.c + ../src/it/loadptm2.c + ../src/it/loadptm.c + ../src/it/loadpsm2.c + ../src/it/loadpsm.c + ../src/it/loadoldpsm2.c + ../src/it/loadoldpsm.c + ../src/it/loadokt2.c + ../src/it/loadokt.c + ../src/it/loadmtm2.c + ../src/it/loadmtm.c + ../src/it/loadmod2.c + ../src/it/loadmod.c + ../src/it/loadasy2.c + ../src/it/loadasy.c + ../src/it/loadamf2.c + ../src/it/loadamf.c + ../src/it/load6692.c + ../src/it/load669.c + ../src/it/itunload.c + ../src/it/itrender.c + ../src/it/itread2.c + ../src/it/itread.c + ../src/it/itorder.c + ../src/it/itmisc.c + ../src/it/itload2.c + ../src/it/itload.c + ../src/it/readany.c + ../src/it/loadany2.c + ../src/it/loadany.c + ../src/it/readany2.c + ../src/helpers/resampler.c + ../src/helpers/lpc.c +) + +set(INSTALL_HEADERS + ../include/dumb.h +) + +add_library(dumb ${SOURCES}) +set_target_properties(dumb PROPERTIES DEBUG_POSTFIX d) + +# Make sure the dylib install name path is set on OSX so you can include dumb in app bundles +IF(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set_target_properties(dumb PROPERTIES INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/lib) +ENDIF() + +INSTALL(FILES ${INSTALL_HEADERS} DESTINATION include/) +INSTALL(TARGETS dumb + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/thirdparty/dumb/cmake/readme.txt b/thirdparty/dumb/cmake/readme.txt new file mode 100644 index 000000000..32897c797 --- /dev/null +++ b/thirdparty/dumb/cmake/readme.txt @@ -0,0 +1,30 @@ +Howto build libdumb with cmake +============================== + +A quick example +--------------- + +In libdumb cmake directory (dumb/cmake/), run: +``` +mkdir -p build +cd build +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local -DBUILD_SHARED_LIBS:BOOL=ON .. +make +make install +``` + +Steps +----- + +1. Create a new temporary build directory and cd into it +2. Run libdumb cmake file with cmake (eg. `cmake -DCMAKE_INSTALL_PREFIX=/install/dir -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE=Release path/to/dumb/cmake/dir`). +3. Run make (eg. just `make` or `mingw32-make` or something). +4. If needed, run make install. + +Flags +----- + +* CMAKE_INSTALL_PREFIX sets the installation path prefix +* CMAKE_BUILD_TYPE sets the build type (eg. Release, Debug, RelWithDebInfo, MinSizeRel). Debug libraries will be named libdumbd, release libraries libdumb. +* BUILD_SHARED_LIBS selects whether cmake should build dynamic or static library (On=shared, OFF=static) +* You may also need to tell cmake what kind of makefiles to create with the "-G" flag. Eg. for MSYS one would say something like `cmake -G "MSYS Makefiles" .`. diff --git a/thirdparty/dumb/include/dumb.h b/thirdparty/dumb/include/dumb.h new file mode 100644 index 000000000..14cf184df --- /dev/null +++ b/thirdparty/dumb/include/dumb.h @@ -0,0 +1,810 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * dumb.h - The user header file for DUMB. / / \ \ + * | < / \_ + * Include this file in any of your files in | \/ /\ / + * which you wish to use the DUMB functions \_ / > / + * and variables. | \ / / + * | ' / + * Allegro users, you will probably want aldumb.h. \__/ + */ + +#ifndef DUMB_H +#define DUMB_H + + +#include +#include + +#if defined(_DEBUG) && defined(_MSC_VER) +#ifndef _CRTDBG_MAP_ALLOC +//#define _CRTDBG_MAP_ALLOC +#endif +#include +#endif + +#ifdef __cplusplus + extern "C" { +#endif + + +#define DUMB_MAJOR_VERSION 1 +#define DUMB_MINOR_VERSION 0 +#define DUMB_REVISION_VERSION 0 + +#define DUMB_VERSION (DUMB_MAJOR_VERSION*10000 + DUMB_MINOR_VERSION*100 + DUMB_REVISION_VERSION) + +#define DUMB_VERSION_STR "1.0.0" + +#define DUMB_NAME "DUMB v" DUMB_VERSION_STR + +#define DUMB_YEAR 2015 +#define DUMB_MONTH 1 +#define DUMB_DAY 17 + +#define DUMB_YEAR_STR2 "15" +#define DUMB_YEAR_STR4 "2015" +#define DUMB_MONTH_STR1 "1" +#define DUMB_DAY_STR1 "17" + +#if DUMB_MONTH < 10 +#define DUMB_MONTH_STR2 "0" DUMB_MONTH_STR1 +#else +#define DUMB_MONTH_STR2 DUMB_MONTH_STR1 +#endif + +#if DUMB_DAY < 10 +#define DUMB_DAY_STR2 "0" DUMB_DAY_STR1 +#else +#define DUMB_DAY_STR2 DUMB_DAY_STR1 +#endif + + +/* WARNING: The month and day were inadvertently swapped in the v0.8 release. + * Please do not compare this constant against any date in 2002. In + * any case, DUMB_VERSION is probably more useful for this purpose. + */ +#define DUMB_DATE (DUMB_YEAR*10000 + DUMB_MONTH*100 + DUMB_DAY) + +#define DUMB_DATE_STR DUMB_DAY_STR1 "." DUMB_MONTH_STR1 "." DUMB_YEAR_STR4 + + +#undef MIN +#undef MAX +#undef MID + +#define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#define MAX(x,y) (((x) > (y)) ? (x) : (y)) +#define MID(x,y,z) MAX((x), MIN((y), (z))) + +#undef ABS +#define ABS(x) (((x) >= 0) ? (x) : (-(x))) + + +#ifdef DEBUGMODE + +#ifndef ASSERT +#include +#define ASSERT(n) assert(n) +#endif +#ifndef TRACE +// it would be nice if this did actually trace ... +#define TRACE 1 ? (void)0 : (void)printf +#endif + +#else + +#ifndef ASSERT +#define ASSERT(n) +#endif +#ifndef TRACE +#define TRACE 1 ? (void)0 : (void)printf +#endif + +#endif + + +#define DUMB_ID(a,b,c,d) (((unsigned int)(a) << 24) | \ + ((unsigned int)(b) << 16) | \ + ((unsigned int)(c) << 8) | \ + ((unsigned int)(d) )) + + +#ifdef __DOS__ +typedef long int32; +typedef unsigned long uint32; +typedef signed long sint32; +#else +typedef int int32; +typedef unsigned int uint32; +typedef signed int sint32; +#endif + +#define CDECL +#ifndef LONG_LONG +#if defined __GNUC__ || defined __INTEL_COMPILER || defined __MWERKS__ +#define LONG_LONG long long +#elif defined _MSC_VER || defined __WATCOMC__ +#define LONG_LONG __int64 +#undef CDECL +#define CDECL __cdecl +#elif defined __sgi +#define LONG_LONG long long +#else +#error 64-bit integer type unknown +#endif +#endif + +#if __GNUC__ * 100 + __GNUC_MINOR__ >= 301 /* GCC 3.1+ */ +#ifndef DUMB_DECLARE_DEPRECATED +#define DUMB_DECLARE_DEPRECATED +#endif +#define DUMB_DEPRECATED __attribute__((__deprecated__)) +#else +#define DUMB_DEPRECATED +#endif + +#define DUMBEXPORT CDECL +#define DUMBCALLBACK CDECL + +/* Basic Sample Type. Normal range is -0x800000 to 0x7FFFFF. */ + +typedef int sample_t; + + +/* Library Clean-up Management */ + +int dumb_atexit(void (*proc)(void)); + +void dumb_exit(void); + + +/* File Input Functions */ + +typedef struct DUMBFILE_SYSTEM +{ + void *(DUMBCALLBACK *open)(const char *filename); + int (DUMBCALLBACK *skip)(void *f, long n); + int (DUMBCALLBACK *getc)(void *f); + int32 (DUMBCALLBACK *getnc)(char *ptr, int32 n, void *f); + void (DUMBCALLBACK *close)(void *f); + int (DUMBCALLBACK *seek)(void *f, long n); + long (DUMBCALLBACK *get_size)(void *f); +} +DUMBFILE_SYSTEM; + +typedef struct DUMBFILE DUMBFILE; + +void DUMBEXPORT register_dumbfile_system(const DUMBFILE_SYSTEM *dfs); + +DUMBFILE *DUMBEXPORT dumbfile_open(const char *filename); +DUMBFILE *DUMBEXPORT dumbfile_open_ex(void *file, const DUMBFILE_SYSTEM *dfs); + +int32 DUMBEXPORT dumbfile_pos(DUMBFILE *f); +int DUMBEXPORT dumbfile_skip(DUMBFILE *f, long n); + +#define DFS_SEEK_SET 0 +#define DFS_SEEK_CUR 1 +#define DFS_SEEK_END 2 + +int DUMBEXPORT dumbfile_seek(DUMBFILE *f, long n, int origin); + +int32 DUMBEXPORT dumbfile_get_size(DUMBFILE *f); + +int DUMBEXPORT dumbfile_getc(DUMBFILE *f); + +int DUMBEXPORT dumbfile_igetw(DUMBFILE *f); +int DUMBEXPORT dumbfile_mgetw(DUMBFILE *f); + +int32 DUMBEXPORT dumbfile_igetl(DUMBFILE *f); +int32 DUMBEXPORT dumbfile_mgetl(DUMBFILE *f); + +uint32 DUMBEXPORT dumbfile_cgetul(DUMBFILE *f); +sint32 DUMBEXPORT dumbfile_cgetsl(DUMBFILE *f); + +int32 DUMBEXPORT dumbfile_getnc(char *ptr, int32 n, DUMBFILE *f); + +int DUMBEXPORT dumbfile_error(DUMBFILE *f); +int DUMBEXPORT dumbfile_close(DUMBFILE *f); + + +/* stdio File Input Module */ + +void DUMBEXPORT dumb_register_stdfiles(void); + +DUMBFILE *DUMBEXPORT dumbfile_open_stdfile(FILE *p); + + +/* Memory File Input Module */ + +DUMBFILE *DUMBEXPORT dumbfile_open_memory(const char *data, int32 size); + + +/* DUH Management */ + +typedef struct DUH DUH; + +#define DUH_SIGNATURE DUMB_ID('D','U','H','!') + +void DUMBEXPORT unload_duh(DUH *duh); + +DUH *DUMBEXPORT load_duh(const char *filename); +DUH *DUMBEXPORT read_duh(DUMBFILE *f); + +int32 DUMBEXPORT duh_get_length(DUH *duh); + +const char *DUMBEXPORT duh_get_tag(DUH *duh, const char *key); + +/* Signal Rendering Functions */ + +typedef struct DUH_SIGRENDERER DUH_SIGRENDERER; + +DUH_SIGRENDERER *DUMBEXPORT duh_start_sigrenderer(DUH *duh, int sig, int n_channels, int32 pos); + +#ifdef DUMB_DECLARE_DEPRECATED +typedef void (*DUH_SIGRENDERER_CALLBACK)(void *data, sample_t **samples, int n_channels, int32 length); +/* This is deprecated, but is not marked as such because GCC tends to + * complain spuriously when the typedef is used later. See comments below. + */ + +void duh_sigrenderer_set_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_CALLBACK callback, void *data +) DUMB_DEPRECATED; +/* The 'callback' argument's type has changed for const-correctness. See the + * DUH_SIGRENDERER_CALLBACK definition just above. Also note that the samples + * in the buffer are now 256 times as large; the normal range is -0x800000 to + * 0x7FFFFF. The function has been renamed partly because its functionality + * has changed slightly and partly so that its name is more meaningful. The + * new one is duh_sigrenderer_set_analyser_callback(), and the typedef for + * the function pointer has also changed, from DUH_SIGRENDERER_CALLBACK to + * DUH_SIGRENDERER_ANALYSER_CALLBACK. (If you wanted to use this callback to + * apply a DSP effect, don't worry; there is a better way of doing this. It + * is undocumented, so contact me and I shall try to help. Contact details + * are in readme.txt.) + */ + +typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data, const sample_t *const *samples, int n_channels, int32 length); +/* This is deprecated, but is not marked as such because GCC tends to + * complain spuriously when the typedef is used later. See comments below. + */ + +void duh_sigrenderer_set_analyser_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data +) DUMB_DEPRECATED; +/* This is deprecated because the meaning of the 'samples' parameter in the + * callback needed to change. For stereo applications, the array used to be + * indexed with samples[channel][pos]. It is now indexed with + * samples[0][pos*2+channel]. Mono sample data are still indexed with + * samples[0][pos]. The array is still 2D because samples will probably only + * ever be interleaved in twos. In order to fix your code, adapt it to the + * new sample layout and then call + * duh_sigrenderer_set_sample_analyser_callback below instead of this + * function. + */ +#endif + +typedef void (*DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK)(void *data, const sample_t *const *samples, int n_channels, int32 length); + +void duh_sigrenderer_set_sample_analyser_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data +); + +int DUMBEXPORT duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer); +int32 DUMBEXPORT duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer); + +void DUMBEXPORT duh_sigrenderer_set_sigparam(DUH_SIGRENDERER *sigrenderer, unsigned char id, int32 value); + +#ifdef DUMB_DECLARE_DEPRECATED +int32 duh_sigrenderer_get_samples( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + int32 size, sample_t **samples +) DUMB_DEPRECATED; +/* The sample format has changed, so if you were using this function, + * you should switch to duh_sigrenderer_generate_samples() and change + * how you interpret the samples array. See the comments for + * duh_sigrenderer_set_analyser_callback(). + */ +#endif + +int32 DUMBEXPORT duh_sigrenderer_generate_samples( + DUH_SIGRENDERER *sigrenderer, + double volume, double delta, + int32 size, sample_t **samples +); + +void DUMBEXPORT duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples); + +void DUMBEXPORT duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer); + + +/* DUH Rendering Functions */ + +int32 DUMBEXPORT duh_render( + DUH_SIGRENDERER *sigrenderer, + int bits, int unsign, + float volume, float delta, + int32 size, void *sptr +); + +#ifdef DUMB_DECLARE_DEPRECATED + +int32 duh_render_signal( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + int32 size, sample_t **samples +) DUMB_DEPRECATED; +/* Please use duh_sigrenderer_generate_samples(), and see the + * comments for the deprecated duh_sigrenderer_get_samples() too. + */ + +typedef DUH_SIGRENDERER DUH_RENDERER DUMB_DEPRECATED; +/* Please use DUH_SIGRENDERER instead of DUH_RENDERER. */ + +DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, int32 pos) DUMB_DEPRECATED; +/* Please use duh_start_sigrenderer() instead. Pass 0 for 'sig'. */ + +int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; +int32 duh_renderer_get_position(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; +/* Please use the duh_sigrenderer_*() equivalents of these two functions. */ + +void duh_end_renderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; +/* Please use duh_end_sigrenderer() instead. */ + +DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer) DUMB_DEPRECATED; +DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; +DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; +/* These functions have become no-ops that just return the parameter. + * So, for instance, replace + * duh_renderer_encapsulate_sigrenderer(my_sigrenderer) + * with + * my_sigrenderer + */ + +#endif + + +/* Impulse Tracker Support */ + +extern int dumb_it_max_to_mix; + +typedef struct DUMB_IT_SIGDATA DUMB_IT_SIGDATA; +typedef struct DUMB_IT_SIGRENDERER DUMB_IT_SIGRENDERER; + +DUMB_IT_SIGDATA *DUMBEXPORT duh_get_it_sigdata(DUH *duh); +DUH_SIGRENDERER *DUMBEXPORT duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, int32 pos); +DUMB_IT_SIGRENDERER *DUMBEXPORT duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer); + +int DUMBEXPORT dumb_it_trim_silent_patterns(DUH * duh); + +typedef int (*dumb_scan_callback)(void *, int, int32); +int DUMBEXPORT dumb_it_scan_for_playable_orders(DUMB_IT_SIGDATA *sigdata, dumb_scan_callback callback, void * callback_data); + +DUH_SIGRENDERER *DUMBEXPORT dumb_it_start_at_order(DUH *duh, int n_channels, int startorder); + +enum +{ + DUMB_IT_RAMP_NONE = 0, + DUMB_IT_RAMP_ONOFF_ONLY = 1, + DUMB_IT_RAMP_FULL = 2 +}; + +void DUMBEXPORT dumb_it_set_ramp_style(DUMB_IT_SIGRENDERER * sigrenderer, int ramp_style); + +void DUMBEXPORT dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (DUMBCALLBACK *callback)(void *data), void *data); +void DUMBEXPORT dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (DUMBCALLBACK *callback)(void *data), void *data); +void DUMBEXPORT dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (DUMBCALLBACK *callback)(void *data, int channel, unsigned char midi_byte), void *data); +void DUMBEXPORT dumb_it_set_global_volume_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (DUMBCALLBACK *callback)(void *data), void *data); + +int DUMBCALLBACK dumb_it_callback_terminate(void *data); +int DUMBCALLBACK dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte); + +/* dumb_*_mod*: restrict_ |= 1-Don't read 15 sample files / 2-Use old pattern counting method */ + +DUH *DUMBEXPORT dumb_load_it(const char *filename); +DUH *DUMBEXPORT dumb_load_xm(const char *filename); +DUH *DUMBEXPORT dumb_load_s3m(const char *filename); +DUH *DUMBEXPORT dumb_load_stm(const char *filename); +DUH *DUMBEXPORT dumb_load_mod(const char *filename, int restrict_); +DUH *DUMBEXPORT dumb_load_ptm(const char *filename); +DUH *DUMBEXPORT dumb_load_669(const char *filename); +DUH *DUMBEXPORT dumb_load_psm(const char *filename, int subsong); +DUH *DUMBEXPORT dumb_load_old_psm(const char * filename); +DUH *DUMBEXPORT dumb_load_mtm(const char *filename); +DUH *DUMBEXPORT dumb_load_riff(const char *filename); +DUH *DUMBEXPORT dumb_load_asy(const char *filename); +DUH *DUMBEXPORT dumb_load_amf(const char *filename); +DUH *DUMBEXPORT dumb_load_okt(const char *filename); + +DUH *DUMBEXPORT dumb_read_it(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_xm(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_s3m(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_stm(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_mod(DUMBFILE *f, int restrict_); +DUH *DUMBEXPORT dumb_read_ptm(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_669(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_psm(DUMBFILE *f, int subsong); +DUH *DUMBEXPORT dumb_read_old_psm(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_mtm(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_riff(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_asy(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_amf(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_okt(DUMBFILE *f); + +DUH *DUMBEXPORT dumb_load_it_quick(const char *filename); +DUH *DUMBEXPORT dumb_load_xm_quick(const char *filename); +DUH *DUMBEXPORT dumb_load_s3m_quick(const char *filename); +DUH *DUMBEXPORT dumb_load_stm_quick(const char *filename); +DUH *DUMBEXPORT dumb_load_mod_quick(const char *filename, int restrict_); +DUH *DUMBEXPORT dumb_load_ptm_quick(const char *filename); +DUH *DUMBEXPORT dumb_load_669_quick(const char *filename); +DUH *DUMBEXPORT dumb_load_psm_quick(const char *filename, int subsong); +DUH *DUMBEXPORT dumb_load_old_psm_quick(const char * filename); +DUH *DUMBEXPORT dumb_load_mtm_quick(const char *filename); +DUH *DUMBEXPORT dumb_load_riff_quick(const char *filename); +DUH *DUMBEXPORT dumb_load_asy_quick(const char *filename); +DUH *DUMBEXPORT dumb_load_amf_quick(const char *filename); +DUH *DUMBEXPORT dumb_load_okt_quick(const char *filename); + +DUH *DUMBEXPORT dumb_read_it_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_xm_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_s3m_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_stm_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_mod_quick(DUMBFILE *f, int restrict_); +DUH *DUMBEXPORT dumb_read_ptm_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_669_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_psm_quick(DUMBFILE *f, int subsong); +DUH *DUMBEXPORT dumb_read_old_psm_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_mtm_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_riff_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_asy_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_amf_quick(DUMBFILE *f); +DUH *DUMBEXPORT dumb_read_okt_quick(DUMBFILE *f); + +DUH *DUMBEXPORT dumb_read_any_quick(DUMBFILE *f, int restrict_, int subsong); +DUH *DUMBEXPORT dumb_read_any(DUMBFILE *f, int restrict_, int subsong); + +DUH *DUMBEXPORT dumb_load_any_quick(const char *filename, int restrict_, int subsong); +DUH *DUMBEXPORT dumb_load_any(const char *filename, int restrict_, int subsong); + +int32 DUMBEXPORT dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder); +void DUMBEXPORT dumb_it_do_initial_runthrough(DUH *duh); + +int DUMBEXPORT dumb_get_psm_subsong_count(DUMBFILE *f); + +const unsigned char *DUMBEXPORT dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd); + +int DUMBEXPORT dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd); +int DUMBEXPORT dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd); +int DUMBEXPORT dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd); + +const unsigned char *DUMBEXPORT dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i); +const unsigned char *DUMBEXPORT dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i); +const unsigned char *DUMBEXPORT dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i); +const unsigned char *DUMBEXPORT dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i); + +int DUMBEXPORT dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd); +void DUMBEXPORT dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv); + +int DUMBEXPORT dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd); +void DUMBEXPORT dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv); + +int DUMBEXPORT dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd); +void DUMBEXPORT dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed); + +int DUMBEXPORT dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd); +void DUMBEXPORT dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo); + +int DUMBEXPORT dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel); +void DUMBEXPORT dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume); + +int DUMBEXPORT dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr); +int DUMBEXPORT dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr); + +int DUMBEXPORT dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr); +void DUMBEXPORT dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv); + +int DUMBEXPORT dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr); +void DUMBEXPORT dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo); + +int DUMBEXPORT dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr); +void DUMBEXPORT dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed); + +#define DUMB_IT_N_CHANNELS 64 +#define DUMB_IT_N_NNA_CHANNELS 192 +#define DUMB_IT_TOTAL_CHANNELS (DUMB_IT_N_CHANNELS + DUMB_IT_N_NNA_CHANNELS) + +/* Channels passed to any of these functions are 0-based */ +int DUMBEXPORT dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel); +void DUMBEXPORT dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume); + +int DUMBEXPORT dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel); +void DUMBEXPORT dumb_it_sr_set_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel, int muted); + +typedef struct DUMB_IT_CHANNEL_STATE DUMB_IT_CHANNEL_STATE; + +struct DUMB_IT_CHANNEL_STATE +{ + int channel; /* 0-based; meaningful for NNA channels */ + int sample; /* 1-based; 0 if nothing playing, then other fields undef */ + int freq; /* in Hz */ + float volume; /* 1.0 maximum; affected by ALL factors, inc. mixing vol */ + unsigned char pan; /* 0-64, 100 for surround */ + signed char subpan; /* use (pan + subpan/256.0f) or ((pan<<8)+subpan) */ + unsigned char filter_cutoff; /* 0-127 cutoff=127 AND resonance=0 */ + unsigned char filter_subcutoff; /* 0-255 -> no filters (subcutoff */ + unsigned char filter_resonance; /* 0-127 always 0 in this case) */ + /* subcutoff only changes from zero if filter envelopes are in use. The + * calculation (filter_cutoff + filter_subcutoff/256.0f) gives a more + * accurate filter cutoff measurement as a float. It would often be more + * useful to use a scaled int such as ((cutoff<<8) + subcutoff). + */ +}; + +/* Values of 64 or more will access NNA channels here. */ +void DUMBEXPORT dumb_it_sr_get_channel_state(DUMB_IT_SIGRENDERER *sr, int channel, DUMB_IT_CHANNEL_STATE *state); + + +/* Signal Design Helper Values */ + +/* Use pow(DUMB_SEMITONE_BASE, n) to get the 'delta' value to transpose up by + * n semitones. To transpose down, use negative n. + */ +#define DUMB_SEMITONE_BASE 1.059463094359295309843105314939748495817 + +/* Use pow(DUMB_QUARTERTONE_BASE, n) to get the 'delta' value to transpose up + * by n quartertones. To transpose down, use negative n. + */ +#define DUMB_QUARTERTONE_BASE 1.029302236643492074463779317738953977823 + +/* Use pow(DUMB_PITCH_BASE, n) to get the 'delta' value to transpose up by n + * units. In this case, 256 units represent one semitone; 3072 units + * represent one octave. These units are used by the sequence signal (SEQU). + */ +#define DUMB_PITCH_BASE 1.000225659305069791926712241547647863626 + + +/* Signal Design Function Types */ + +typedef void sigdata_t; +typedef void sigrenderer_t; + +typedef sigdata_t *(*DUH_LOAD_SIGDATA)(DUH *duh, DUMBFILE *file); + +typedef sigrenderer_t *(*DUH_START_SIGRENDERER)( + DUH *duh, + sigdata_t *sigdata, + int n_channels, + int32 pos +); + +typedef void (*DUH_SIGRENDERER_SET_SIGPARAM)( + sigrenderer_t *sigrenderer, + unsigned char id, int32 value +); + +typedef int32 (*DUH_SIGRENDERER_GENERATE_SAMPLES)( + sigrenderer_t *sigrenderer, + double volume, double delta, + int32 size, sample_t **samples +); + +typedef void (*DUH_SIGRENDERER_GET_CURRENT_SAMPLE)( + sigrenderer_t *sigrenderer, + double volume, + sample_t *samples +); + +typedef void (*DUH_END_SIGRENDERER)(sigrenderer_t *sigrenderer); + +typedef void (*DUH_UNLOAD_SIGDATA)(sigdata_t *sigdata); + + +/* Signal Design Function Registration */ + +typedef struct DUH_SIGTYPE_DESC +{ + int32 type; + DUH_LOAD_SIGDATA load_sigdata; + DUH_START_SIGRENDERER start_sigrenderer; + DUH_SIGRENDERER_SET_SIGPARAM sigrenderer_set_sigparam; + DUH_SIGRENDERER_GENERATE_SAMPLES sigrenderer_generate_samples; + DUH_SIGRENDERER_GET_CURRENT_SAMPLE sigrenderer_get_current_sample; + DUH_END_SIGRENDERER end_sigrenderer; + DUH_UNLOAD_SIGDATA unload_sigdata; +} +DUH_SIGTYPE_DESC; + +void DUMBEXPORT dumb_register_sigtype(DUH_SIGTYPE_DESC *desc); + + +// Decide where to put these functions; new heading? + +sigdata_t *DUMBEXPORT duh_get_raw_sigdata(DUH *duh, int sig, int32 type); + +DUH_SIGRENDERER *DUMBEXPORT duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, int32 pos); +sigrenderer_t *DUMBEXPORT duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, int32 type); + +int DUMBEXPORT duh_add_signal(DUH *duh, DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata); + + +/* Standard Signal Types */ + +//void dumb_register_sigtype_sample(void); + + +/* Sample Buffer Allocation Helpers */ + +#ifdef DUMB_DECLARE_DEPRECATED +sample_t **create_sample_buffer(int n_channels, int32 length) DUMB_DEPRECATED; +/* DUMB has been changed to interleave stereo samples. Use + * allocate_sample_buffer() instead, and see the comments for + * duh_sigrenderer_set_analyser_callback(). + */ +#endif +sample_t **DUMBEXPORT allocate_sample_buffer(int n_channels, int32 length); +void DUMBEXPORT destroy_sample_buffer(sample_t **samples); + + +/* Silencing Helper */ + +void DUMBEXPORT dumb_silence(sample_t *samples, int32 length); + + +/* Click Removal Helpers */ + +typedef struct DUMB_CLICK_REMOVER DUMB_CLICK_REMOVER; + +DUMB_CLICK_REMOVER *DUMBEXPORT dumb_create_click_remover(void); +void DUMBEXPORT dumb_record_click(DUMB_CLICK_REMOVER *cr, int32 pos, sample_t step); +void DUMBEXPORT dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, int32 length, int step, double halflife); +sample_t DUMBEXPORT dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr); +void DUMBEXPORT dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr); + +DUMB_CLICK_REMOVER **DUMBEXPORT dumb_create_click_remover_array(int n); +void DUMBEXPORT dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, int32 pos, sample_t *step); +void DUMBEXPORT dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, int32 pos, sample_t *step); +void DUMBEXPORT dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, int32 length, double halflife); +void DUMBEXPORT dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset); +void DUMBEXPORT dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr); + + +/* Resampling Helpers */ + +#define DUMB_RQ_ALIASING 0 +#define DUMB_LQ_LINEAR 1 +#define DUMB_LQ_CUBIC 2 + +#define DUMB_RQ_BLEP 3 +#define DUMB_RQ_LINEAR 4 +#define DUMB_RQ_BLAM 5 +#define DUMB_RQ_CUBIC 6 +#define DUMB_RQ_FIR 7 +#define DUMB_RQ_N_LEVELS 8 + +/* Subtract quality above by this to convert to resampler.c's quality */ +#define DUMB_RESAMPLER_BASE 2 + +extern int dumb_resampling_quality; /* This specifies the default */ +void DUMBEXPORT dumb_it_set_resampling_quality(DUMB_IT_SIGRENDERER * sigrenderer, int quality); /* This overrides it */ + +typedef struct DUMB_RESAMPLER DUMB_RESAMPLER; + +typedef struct DUMB_VOLUME_RAMP_INFO DUMB_VOLUME_RAMP_INFO; + +typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data); + +struct DUMB_RESAMPLER +{ + void *src; + int32 pos; + int subpos; + int32 start, end; + int dir; + DUMB_RESAMPLE_PICKUP pickup; + void *pickup_data; + int quality; + /* Everything below this point is internal: do not use. */ + union { + sample_t x24[3*2]; + short x16[3*2]; + signed char x8[3*2]; + } x; + int overshot; + double fir_resampler_ratio; + void* fir_resampler[2]; +}; + +struct DUMB_VOLUME_RAMP_INFO +{ + float volume; + float delta; + float target; + float mix; + unsigned char declick_stage; +}; + +void dumb_reset_resampler(DUMB_RESAMPLER *resampler, sample_t *src, int src_channels, int32 pos, int32 start, int32 end, int quality); +DUMB_RESAMPLER *dumb_start_resampler(sample_t *src, int src_channels, int32 pos, int32 start, int32 end, int quality); +//int32 dumb_resample_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume, double delta); +int32 dumb_resample_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +//int32 dumb_resample_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +int32 dumb_resample_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +//void dumb_resample_get_current_sample_1_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst); +void dumb_resample_get_current_sample_1_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +//void dumb_resample_get_current_sample_2_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_2_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_end_resampler(DUMB_RESAMPLER *resampler); + +void dumb_reset_resampler_16(DUMB_RESAMPLER *resampler, short *src, int src_channels, int32 pos, int32 start, int32 end, int quality); +DUMB_RESAMPLER *dumb_start_resampler_16(short *src, int src_channels, int32 pos, int32 start, int32 end, int quality); +//int32 dumb_resample_16_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume, double delta); +int32 dumb_resample_16_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +//int32 dumb_resample_16_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +int32 dumb_resample_16_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +//void dumb_resample_get_current_sample_16_1_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst); +void dumb_resample_get_current_sample_16_1_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +//void dumb_resample_get_current_sample_16_2_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_16_2_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_end_resampler_16(DUMB_RESAMPLER *resampler); + +void dumb_reset_resampler_8(DUMB_RESAMPLER *resampler, signed char *src, int src_channels, int32 pos, int32 start, int32 end, int quality); +DUMB_RESAMPLER *dumb_start_resampler_8(signed char *src, int src_channels, int32 pos, int32 start, int32 end, int quality); +//int32 dumb_resample_8_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume, double delta); +int32 dumb_resample_8_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +//int32 dumb_resample_8_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +int32 dumb_resample_8_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +//void dumb_resample_get_current_sample_8_1_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst); +void dumb_resample_get_current_sample_8_1_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +//void dumb_resample_get_current_sample_8_2_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_8_2_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_end_resampler_8(DUMB_RESAMPLER *resampler); + +void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, int src_channels, int32 pos, int32 start, int32 end, int quality); +DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, int32 pos, int32 start, int32 end, int quality); +//int32 dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume, double delta); +int32 dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +//int32 dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +int32 dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta); +//void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst); +void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +//void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler); + +/* This sets the default panning separation for hard panned formats, + or for formats with default panning information. This must be set + before using any readers or loaders, and is not really thread safe. */ + +extern int dumb_it_default_panning_separation; /* in percent, default 25 */ + +/* DUH Construction */ + +DUH *make_duh( + int32 length, + int n_tags, + const char *const tag[][2], + int n_signals, + DUH_SIGTYPE_DESC *desc[], + sigdata_t *sigdata[] +); + +void DUMBEXPORT duh_set_length(DUH *duh, int32 length); + +#ifdef __cplusplus + } +#endif + + +#endif /* DUMB_H */ diff --git a/thirdparty/dumb/include/internal/aldumb.h b/thirdparty/dumb/include/internal/aldumb.h new file mode 100644 index 000000000..a0c6d63c0 --- /dev/null +++ b/thirdparty/dumb/include/internal/aldumb.h @@ -0,0 +1,27 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * internal/aldumb.h - The internal header file / / \ \ + * for DUMB with Allegro. | < / \_ + * | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#ifndef INTERNAL_ALDUMB_H +#define INTERNAL_ALDUMB_H + + +void _dat_unload_duh(void *duh); + + +#endif /* INTERNAL_DUMB_H */ diff --git a/thirdparty/dumb/include/internal/barray.h b/thirdparty/dumb/include/internal/barray.h new file mode 100644 index 000000000..de9fab70c --- /dev/null +++ b/thirdparty/dumb/include/internal/barray.h @@ -0,0 +1,41 @@ +#ifndef _B_ARRAY_H_ +#define _B_ARRAY_H_ + +#include + +#ifdef BARRAY_DECORATE +#define PASTE(a,b) a ## b +#define EVALUATE(a,b) PASTE(a,b) +#define bit_array_create EVALUATE(BARRAY_DECORATE,_bit_array_create) +#define bit_array_destroy EVALUATE(BARRAY_DECORATE,_bit_array_destroy) +#define bit_array_dup EVALUATE(BARRAY_DECORATE,_bit_array_dup) +#define bit_array_reset EVALUATE(BARRAY_DECORATE,_bit_array_reset) +#define bit_array_set EVALUATE(BARRAY_DECORATE,_bit_array_set) +#define bit_array_set_range EVALUATE(BARRAY_DECORATE,_bit_array_set_range) +#define bit_array_test EVALUATE(BARRAY_DECORATE,_bit_array_test) +#define bit_array_test_range EVALUATE(BARRAY_DECORATE,_bit_array_test_range) +#define bit_array_clear EVALUATE(BARRAY_DECORATE,_bit_array_clear) +#define bit_array_clear_range EVALUATE(BARRAY_DECORATE,_bit_array_clear_range) +#define bit_array_merge EVALUATE(BARRAY_DECORATE,_bit_array_merge) +#define bit_array_mask EVALUATE(BARRAY_DECORATE,_bit_array_mask) +#endif + +void * bit_array_create(size_t size); +void bit_array_destroy(void * array); +void * bit_array_dup(void * array); + +void bit_array_reset(void * array); + +void bit_array_set(void * array, size_t bit); +void bit_array_set_range(void * array, size_t bit, size_t count); + +int bit_array_test(void * array, size_t bit); +int bit_array_test_range(void * array, size_t bit, size_t count); + +void bit_array_clear(void * array, size_t bit); +void bit_array_clear_range(void * array, size_t bit, size_t count); + +void bit_array_merge(void * array, void * source, size_t offset); +void bit_array_mask(void * array, void * source, size_t offset); + +#endif diff --git a/thirdparty/dumb/include/internal/dumb.h b/thirdparty/dumb/include/internal/dumb.h new file mode 100644 index 000000000..bb2fe5c1c --- /dev/null +++ b/thirdparty/dumb/include/internal/dumb.h @@ -0,0 +1,61 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * internal/dumb.h - DUMB's internal declarations. / / \ \ + * | < / \_ + * This header file provides access to the | \/ /\ / + * internal structure of DUMB, and is liable \_ / > / + * to change, mutate or cease to exist at any | \ / / + * moment. Include it at your own peril. | ' / + * \__/ + * ... + * + * Seriously. You don't need access to anything in this file. All right, you + * probably do actually. But if you use it, you will be relying on a specific + * version of DUMB, so please check DUMB_VERSION defined in dumb.h. Please + * contact the authors so that we can provide a public API for what you need. + */ + +#ifndef INTERNAL_DUMB_H +#define INTERNAL_DUMB_H + + +typedef struct DUH_SIGTYPE_DESC_LINK +{ + struct DUH_SIGTYPE_DESC_LINK *next; + DUH_SIGTYPE_DESC *desc; +} +DUH_SIGTYPE_DESC_LINK; + + +typedef struct DUH_SIGNAL +{ + sigdata_t *sigdata; + DUH_SIGTYPE_DESC *desc; +} +DUH_SIGNAL; + + +struct DUH +{ + int32 length; + + int n_tags; + char *(*tag)[2]; + + int n_signals; + DUH_SIGNAL **signal; +}; + + +DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(int32 type); + + +#endif /* INTERNAL_DUMB_H */ diff --git a/thirdparty/dumb/include/internal/dumbfile.h b/thirdparty/dumb/include/internal/dumbfile.h new file mode 100644 index 000000000..c83cc9a00 --- /dev/null +++ b/thirdparty/dumb/include/internal/dumbfile.h @@ -0,0 +1,13 @@ +#ifndef DUMBFILE_H +#define DUMBFILE_H + +#include "../dumb.h" + +struct DUMBFILE +{ + const DUMBFILE_SYSTEM *dfs; + void *file; + long pos; +}; + +#endif // DUMBFILE_H diff --git a/thirdparty/dumb/include/internal/it.h b/thirdparty/dumb/include/internal/it.h new file mode 100644 index 000000000..b5806223b --- /dev/null +++ b/thirdparty/dumb/include/internal/it.h @@ -0,0 +1,914 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * internal/it.h - Internal stuff for IT playback / / \ \ + * and MOD/XM/S3M conversion. | < / \_ + * | \/ /\ / + * This header file provides access to the \_ / > / + * internal structure of DUMB, and is liable | \ / / + * to change, mutate or cease to exist at any | ' / + * moment. Include it at your own peril. \__/ + * + * ... + * + * Seriously. You don't need access to anything in this file. All right, you + * probably do actually. But if you use it, you will be relying on a specific + * version of DUMB, so please check DUMB_VERSION defined in dumb.h. Please + * contact the authors so that we can provide a public API for what you need. + */ + +#ifndef INTERNAL_IT_H +#define INTERNAL_IT_H + + +#define BIT_ARRAY_BULLSHIT + +#include + +#include "barray.h" + + +/** TO DO: THINK ABOUT THE FOLLOWING: + +sigdata->flags & IT_COMPATIBLE_GXX + + Bit 5: On = Link Effect G's memory with Effect E/F. Also + Gxx with an instrument present will cause the + envelopes to be retriggered. If you change a + sample on a row with Gxx, it'll adjust the + frequency of the current note according to: + + NewFrequency = OldFrequency * NewC5 / OldC5; +*/ + + + +/* These #defines are TEMPORARY. They are used to write alternative code to + * handle ambiguities in the format specification. The correct code in each + * case will be determined most likely by experimentation. + */ +//#define STEREO_SAMPLES_COUNT_AS_TWO +#define INVALID_ORDERS_END_SONG +#define SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP +#define VOLUME_OUT_OF_RANGE_SETS_MAXIMUM + + + +#define SIGTYPE_IT DUMB_ID('I', 'T', ' ', ' ') + +#define IT_SIGNATURE DUMB_ID('I', 'M', 'P', 'M') +#define IT_INSTRUMENT_SIGNATURE DUMB_ID('I', 'M', 'P', 'I') +#define IT_SAMPLE_SIGNATURE DUMB_ID('I', 'M', 'P', 'S') + +// olivier sux +#define IT_MPTX_SIGNATURE DUMB_ID('X', 'T', 'P', 'M') +#define IT_INSM_SIGNATURE DUMB_ID('M', 'S', 'N', 'I') + + +/* This is divided by the tempo times 256 to get the interval between ticks. + */ +#define TICK_TIME_DIVIDEND (65536 * 5 * 128) + + + +/* I'm not going to try to explain this, because I didn't derive it very + * formally ;) + */ +/* #define AMIGA_DIVISOR ((float)(4.0 * 14317056.0)) */ +/* I believe the following one to be more accurate. */ +//#define AMIGA_DIVISOR ((float)(8.0 * 7159090.5)) +#define AMIGA_CLOCK 3546895 +#define AMIGA_DIVISOR ((float)(16.0 * AMIGA_CLOCK)) + + + +typedef struct IT_MIDI IT_MIDI; +typedef struct IT_FILTER_STATE IT_FILTER_STATE; +typedef struct IT_ENVELOPE IT_ENVELOPE; +typedef struct IT_INSTRUMENT IT_INSTRUMENT; +typedef struct IT_SAMPLE IT_SAMPLE; +typedef struct IT_ENTRY IT_ENTRY; +typedef struct IT_PATTERN IT_PATTERN; +typedef struct IT_PLAYING_ENVELOPE IT_PLAYING_ENVELOPE; +typedef struct IT_PLAYING IT_PLAYING; +typedef struct IT_CHANNEL IT_CHANNEL; +typedef struct IT_CHECKPOINT IT_CHECKPOINT; +typedef struct IT_CALLBACKS IT_CALLBACKS; + + + +struct IT_MIDI +{ + unsigned char SFmacro[16][16]; // read these from 0x120 + unsigned char SFmacrolen[16]; + unsigned short SFmacroz[16]; /* Bitfield; bit 0 set = z in first position */ + unsigned char Zmacro[128][16]; // read these from 0x320 + unsigned char Zmacrolen[128]; +}; + + + +struct IT_FILTER_STATE +{ + sample_t currsample, prevsample; +}; + + + +#define IT_ENVELOPE_ON 1 +#define IT_ENVELOPE_LOOP_ON 2 +#define IT_ENVELOPE_SUSTAIN_LOOP 4 +#define IT_ENVELOPE_CARRY 8 +#define IT_ENVELOPE_PITCH_IS_FILTER 128 + +struct IT_ENVELOPE +{ + unsigned char flags; + unsigned char n_nodes; + unsigned char loop_start; + unsigned char loop_end; + unsigned char sus_loop_start; + unsigned char sus_loop_end; + signed char node_y[25]; + unsigned short node_t[25]; +}; + + + +#define NNA_NOTE_CUT 0 +#define NNA_NOTE_CONTINUE 1 +#define NNA_NOTE_OFF 2 +#define NNA_NOTE_FADE 3 + +#define DCT_OFF 0 +#define DCT_NOTE 1 +#define DCT_SAMPLE 2 +#define DCT_INSTRUMENT 3 + +#define DCA_NOTE_CUT 0 +#define DCA_NOTE_OFF 1 +#define DCA_NOTE_FADE 2 + +struct IT_INSTRUMENT +{ + unsigned char name[27]; + unsigned char filename[14]; + + int fadeout; + + IT_ENVELOPE volume_envelope; + IT_ENVELOPE pan_envelope; + IT_ENVELOPE pitch_envelope; + + unsigned char new_note_action; + unsigned char dup_check_type; + unsigned char dup_check_action; + signed char pp_separation; + unsigned char pp_centre; + unsigned char global_volume; + unsigned char default_pan; + unsigned char random_volume; + unsigned char random_pan; + + unsigned char filter_cutoff; + unsigned char filter_resonance; + + unsigned char map_note[120]; + unsigned short map_sample[120]; + + //int output; +}; + + + +#define IT_SAMPLE_EXISTS 1 +#define IT_SAMPLE_16BIT 2 +#define IT_SAMPLE_STEREO 4 +#define IT_SAMPLE_LOOP 16 +#define IT_SAMPLE_SUS_LOOP 32 +#define IT_SAMPLE_PINGPONG_LOOP 64 +#define IT_SAMPLE_PINGPONG_SUS_LOOP 128 + +#define IT_VIBRATO_SINE 0 +#define IT_VIBRATO_SAWTOOTH 1 +#define IT_VIBRATO_SQUARE 2 +#define IT_VIBRATO_RANDOM 3 +#define IT_VIBRATO_XM_SQUARE 4 +#define IT_VIBRATO_RAMP_DOWN 5 +#define IT_VIBRATO_RAMP_UP 6 + +struct IT_SAMPLE +{ + unsigned char name[35]; + unsigned char filename[15]; + unsigned char flags; + unsigned char global_volume; + unsigned char default_volume; + unsigned char default_pan; + /* default_pan: + * 0-255 for XM + * ignored for MOD + * otherwise, 0-64, and add 128 to enable + */ + + int32 length; + int32 loop_start; + int32 loop_end; + int32 C5_speed; + int32 sus_loop_start; + int32 sus_loop_end; + + unsigned char vibrato_speed; + unsigned char vibrato_depth; + unsigned char vibrato_rate; + unsigned char vibrato_waveform; + + signed short finetune; + + void *data; + + int max_resampling_quality; +}; + + + +#define IT_ENTRY_NOTE 1 +#define IT_ENTRY_INSTRUMENT 2 +#define IT_ENTRY_VOLPAN 4 +#define IT_ENTRY_EFFECT 8 + +#define IT_SET_END_ROW(entry) ((entry)->channel = 255) +#define IT_IS_END_ROW(entry) ((entry)->channel >= DUMB_IT_N_CHANNELS) + +#define IT_NOTE_OFF 255 +#define IT_NOTE_CUT 254 + +#define IT_ENVELOPE_SHIFT 8 + +#define IT_SURROUND 100 +#define IT_IS_SURROUND(pan) ((pan) > 64) +#define IT_IS_SURROUND_SHIFTED(pan) ((pan) > 64 << IT_ENVELOPE_SHIFT) + +#define IT_SET_SPEED 1 +#define IT_JUMP_TO_ORDER 2 +#define IT_BREAK_TO_ROW 3 +#define IT_VOLUME_SLIDE 4 +#define IT_PORTAMENTO_DOWN 5 +#define IT_PORTAMENTO_UP 6 +#define IT_TONE_PORTAMENTO 7 +#define IT_VIBRATO 8 +#define IT_TREMOR 9 +#define IT_ARPEGGIO 10 +#define IT_VOLSLIDE_VIBRATO 11 +#define IT_VOLSLIDE_TONEPORTA 12 +#define IT_SET_CHANNEL_VOLUME 13 +#define IT_CHANNEL_VOLUME_SLIDE 14 +#define IT_SET_SAMPLE_OFFSET 15 +#define IT_PANNING_SLIDE 16 +#define IT_RETRIGGER_NOTE 17 +#define IT_TREMOLO 18 +#define IT_S 19 +#define IT_SET_SONG_TEMPO 20 +#define IT_FINE_VIBRATO 21 +#define IT_SET_GLOBAL_VOLUME 22 +#define IT_GLOBAL_VOLUME_SLIDE 23 +#define IT_SET_PANNING 24 +#define IT_PANBRELLO 25 +#define IT_MIDI_MACRO 26 //see MIDI.TXT + +/* Some effects needed for XM compatibility */ +#define IT_XM_PORTAMENTO_DOWN 27 +#define IT_XM_PORTAMENTO_UP 28 +#define IT_XM_FINE_VOLSLIDE_DOWN 29 +#define IT_XM_FINE_VOLSLIDE_UP 30 +#define IT_XM_RETRIGGER_NOTE 31 +#define IT_XM_KEY_OFF 32 +#define IT_XM_SET_ENVELOPE_POSITION 33 + +/* More effects needed for PTM compatibility */ +#define IT_PTM_NOTE_SLIDE_DOWN 34 +#define IT_PTM_NOTE_SLIDE_UP 35 +#define IT_PTM_NOTE_SLIDE_DOWN_RETRIG 36 +#define IT_PTM_NOTE_SLIDE_UP_RETRIG 37 + +/* More effects needed for OKT compatibility */ +#define IT_OKT_NOTE_SLIDE_DOWN 38 +#define IT_OKT_NOTE_SLIDE_DOWN_ROW 39 +#define IT_OKT_NOTE_SLIDE_UP 40 +#define IT_OKT_NOTE_SLIDE_UP_ROW 41 +#define IT_OKT_ARPEGGIO_3 42 +#define IT_OKT_ARPEGGIO_4 43 +#define IT_OKT_ARPEGGIO_5 44 +#define IT_OKT_VOLUME_SLIDE_DOWN 45 +#define IT_OKT_VOLUME_SLIDE_UP 46 + +#define IT_N_EFFECTS 47 + +/* These represent the top nibble of the command value. */ +#define IT_S_SET_FILTER 0 /* Greyed out in IT... */ +#define IT_S_SET_GLISSANDO_CONTROL 1 /* Greyed out in IT... */ +#define IT_S_FINETUNE 2 /* Greyed out in IT... */ +#define IT_S_SET_VIBRATO_WAVEFORM 3 +#define IT_S_SET_TREMOLO_WAVEFORM 4 +#define IT_S_SET_PANBRELLO_WAVEFORM 5 +#define IT_S_FINE_PATTERN_DELAY 6 +#define IT_S7 7 +#define IT_S_SET_PAN 8 +#define IT_S_SET_SURROUND_SOUND 9 +#define IT_S_SET_HIGH_OFFSET 10 +#define IT_S_PATTERN_LOOP 11 +#define IT_S_DELAYED_NOTE_CUT 12 +#define IT_S_NOTE_DELAY 13 +#define IT_S_PATTERN_DELAY 14 +#define IT_S_SET_MIDI_MACRO 15 + +/* +S0x Set filter +S1x Set glissando control +S2x Set finetune + + +S3x Set vibrato waveform to type x +S4x Set tremelo waveform to type x +S5x Set panbrello waveform to type x + Waveforms for commands S3x, S4x and S5x: + 0: Sine wave + 1: Ramp down + 2: Square wave + 3: Random wave +S6x Pattern delay for x ticks +S70 Past note cut +S71 Past note off +S72 Past note fade +S73 Set NNA to note cut +S74 Set NNA to continue +S75 Set NNA to note off +S76 Set NNA to note fade +S77 Turn off volume envelope +S78 Turn on volume envelope +S79 Turn off panning envelope +S7A Turn on panning envelope +S7B Turn off pitch envelope +S7C Turn on pitch envelope +S8x Set panning position +S91 Set surround sound +SAy Set high value of sample offset yxx00h +SB0 Set loopback point +SBx Loop x times to loopback point +SCx Note cut after x ticks +SDx Note delay for x ticks +SEx Pattern delay for x rows +SFx Set parameterised MIDI Macro +*/ + +struct IT_ENTRY +{ + unsigned char channel; /* End of row if channel >= DUMB_IT_N_CHANNELS */ + unsigned char mask; + unsigned char note; + unsigned char instrument; + unsigned char volpan; + unsigned char effect; + unsigned char effectvalue; +}; + + + +struct IT_PATTERN +{ + int n_rows; + int n_entries; + IT_ENTRY *entry; +}; + + + +#define IT_STEREO 1 +#define IT_USE_INSTRUMENTS 4 +#define IT_LINEAR_SLIDES 8 /* If not set, use Amiga slides */ +#define IT_OLD_EFFECTS 16 +#define IT_COMPATIBLE_GXX 32 + +/* Make sure IT_WAS_AN_XM and IT_WAS_A_MOD aren't set accidentally */ +#define IT_REAL_FLAGS 63 + +#define IT_WAS_AN_XM 64 /* Set for both XMs and MODs */ +#define IT_WAS_A_MOD 128 + +#define IT_WAS_AN_S3M 256 + +#define IT_WAS_A_PTM 512 + +#define IT_WAS_A_669 1024 + +#define IT_WAS_AN_OKT 2048 + +#define IT_WAS_AN_STM 4096 + +#define IT_WAS_PROCESSED 8192 /* Will be set the first time a sigdata passes through a sigrenderer */ + +#define IT_ORDER_END 255 +#define IT_ORDER_SKIP 254 + +struct DUMB_IT_SIGDATA +{ + unsigned char name[65]; + + unsigned char *song_message; + + int n_orders; + int n_instruments; + int n_samples; + int n_patterns; + int n_pchannels; + + int flags; + + int global_volume; + int mixing_volume; + int speed; + int tempo; + int pan_separation; + + unsigned char channel_pan[DUMB_IT_N_CHANNELS]; + unsigned char channel_volume[DUMB_IT_N_CHANNELS]; + + unsigned char *order; + unsigned char restart_position; /* for XM compatiblity */ + + IT_INSTRUMENT *instrument; + IT_SAMPLE *sample; + IT_PATTERN *pattern; + + IT_MIDI *midi; + + IT_CHECKPOINT *checkpoint; +}; + + + +struct IT_PLAYING_ENVELOPE +{ + int next_node; + int tick; + int value; +}; + + + +#define IT_PLAYING_BACKGROUND 1 +#define IT_PLAYING_SUSTAINOFF 2 +#define IT_PLAYING_FADING 4 +#define IT_PLAYING_DEAD 8 +#define IT_PLAYING_REVERSE 16 + +struct IT_PLAYING +{ + int flags; + + int resampling_quality; + + IT_CHANNEL *channel; + IT_SAMPLE *sample; + IT_INSTRUMENT *instrument; + IT_INSTRUMENT *env_instrument; + + unsigned short sampnum; + unsigned char instnum; + + unsigned char declick_stage; + + float float_volume[2]; + float ramp_volume[2]; + float ramp_delta[2]; + + unsigned char channel_volume; + + unsigned char volume; + unsigned short pan; + + signed char volume_offset, panning_offset; + + unsigned char note; + + unsigned char enabled_envelopes; + + unsigned char filter_cutoff; + unsigned char filter_resonance; + + unsigned short true_filter_cutoff; /* These incorporate the filter envelope, and will not */ + unsigned char true_filter_resonance; /* be changed if they would be set to 127<<8 and 0. */ + + unsigned char vibrato_speed; + unsigned char vibrato_depth; + unsigned char vibrato_n; /* May be specified twice: volpan & effect. */ + unsigned char vibrato_time; + unsigned char vibrato_waveform; + + unsigned char tremolo_speed; + unsigned char tremolo_depth; + unsigned char tremolo_time; + unsigned char tremolo_waveform; + + unsigned char panbrello_speed; + unsigned char panbrello_depth; + unsigned char panbrello_time; + unsigned char panbrello_waveform; + signed char panbrello_random; + + unsigned char sample_vibrato_time; + unsigned char sample_vibrato_waveform; + int sample_vibrato_depth; /* Starts at rate?0:depth, increases by rate */ + + int slide; + float delta; + int finetune; + + IT_PLAYING_ENVELOPE volume_envelope; + IT_PLAYING_ENVELOPE pan_envelope; + IT_PLAYING_ENVELOPE pitch_envelope; + + int fadeoutcount; + + IT_FILTER_STATE filter_state[2]; /* Left and right */ + + DUMB_RESAMPLER resampler; + + /* time_lost is used to emulate Impulse Tracker's sample looping + * characteristics. When time_lost is added to pos, the result represents + * the position in the theoretical version of the sample where all loops + * have been expanded. If this is stored, the resampling helpers will + * safely convert it for use with new loop boundaries. The situation is + * slightly more complicated if dir == -1 when the change takes place; we + * must reflect pos off the loop end point and set dir to 1 before + * proceeding. + */ + int32 time_lost; + + //int output; + + IT_PLAYING *next; +}; + + + +#define IT_CHANNEL_MUTED 1 + +#define IT_ENV_VOLUME 1 +#define IT_ENV_PANNING 2 +#define IT_ENV_PITCH 4 + +struct IT_CHANNEL +{ + int flags; + + unsigned char volume; + signed char volslide; + signed char xm_volslide; + signed char panslide; + + /* xm_volslide is used for volume slides done in the volume column in an + * XM file, since it seems the volume column slide is applied first, + * followed by clamping, followed by the effects column slide. IT does + * not exhibit this behaviour, so xm_volslide is maintained at zero. + */ + + unsigned char pan; + unsigned short truepan; + + unsigned char channelvolume; + signed char channelvolslide; + + unsigned char instrument; + unsigned char note; + + unsigned char SFmacro; + + unsigned char filter_cutoff; + unsigned char filter_resonance; + + unsigned char key_off_count; + unsigned char note_cut_count; + unsigned char note_delay_count; + IT_ENTRY *note_delay_entry; + + unsigned char new_note_action; + + unsigned char const* arpeggio_table; + signed char arpeggio_offsets[3]; + + int arpeggio_shift; + unsigned char retrig; + unsigned char xm_retrig; + int retrig_tick; + + unsigned char tremor; + unsigned char tremor_time; /* Bit 6 set if note on; bit 7 set if tremor active. */ + + unsigned char vibrato_waveform; + unsigned char tremolo_waveform; + unsigned char panbrello_waveform; + + int portamento; + int toneporta; + int toneslide; + unsigned char toneslide_tick, last_toneslide_tick, ptm_toneslide, ptm_last_toneslide, okt_toneslide; + unsigned char destnote; + unsigned char toneslide_retrig; + + unsigned char glissando; + + /** WARNING - for neatness, should one or both of these be in the IT_PLAYING struct? */ + unsigned short sample; + unsigned char truenote; + + unsigned char midi_state; + + signed char lastvolslide; + unsigned char lastDKL; + unsigned char lastEF; /* Doubles as last portamento up for XM files */ + unsigned char lastG; + unsigned char lastHspeed; + unsigned char lastHdepth; + unsigned char lastRspeed; + unsigned char lastRdepth; + unsigned char lastYspeed; + unsigned char lastYdepth; + unsigned char lastI; + unsigned char lastJ; /* Doubles as last portamento down for XM files */ + unsigned char lastN; + unsigned char lastO; + unsigned char high_offset; + unsigned char lastP; + unsigned char lastQ; + unsigned char lastS; + unsigned char pat_loop_row; + unsigned char pat_loop_count; + unsigned char pat_loop_end_row; /* Used to catch infinite pattern loops */ + unsigned char lastW; + + unsigned char xm_lastE1; + unsigned char xm_lastE2; + unsigned char xm_lastEA; + unsigned char xm_lastEB; + unsigned char xm_lastX1; + unsigned char xm_lastX2; + + unsigned char inv_loop_delay; + unsigned char inv_loop_speed; + int inv_loop_offset; + + IT_PLAYING *playing; + +#ifdef BIT_ARRAY_BULLSHIT + void * played_patjump; + int played_patjump_order; +#endif + + //int output; +}; + + + +struct DUMB_IT_SIGRENDERER +{ + DUMB_IT_SIGDATA *sigdata; + + int n_channels; + + int resampling_quality; + + unsigned char globalvolume; + signed char globalvolslide; + + int tempo; + signed char temposlide; + + IT_CHANNEL channel[DUMB_IT_N_CHANNELS]; + + IT_PLAYING *playing[DUMB_IT_N_NNA_CHANNELS]; + + int tick; + int speed; + int rowcount; + + int order; /* Set to -1 if the song is terminated by a callback. */ + int row; + int processorder; + int processrow; + int breakrow; + + int restart_position; + + int n_rows; + + IT_ENTRY *entry_start; + IT_ENTRY *entry; + IT_ENTRY *entry_end; + + int32 time_left; /* Time before the next tick is processed */ + int sub_time_left; + + DUMB_CLICK_REMOVER **click_remover; + + IT_CALLBACKS *callbacks; + +#ifdef BIT_ARRAY_BULLSHIT + /* bit array, which rows are played, only checked by pattern break or loop commands */ + void * played; +#endif + + int32 gvz_time; + int gvz_sub_time; + + int ramp_style; + + //int max_output; + + IT_PLAYING *free_playing; +}; + + + +struct IT_CHECKPOINT +{ + IT_CHECKPOINT *next; + int32 time; + DUMB_IT_SIGRENDERER *sigrenderer; +}; + + + +struct IT_CALLBACKS +{ + int (DUMBCALLBACK *loop)(void *data); + void *loop_data; + /* Return 1 to prevent looping; the music will terminate abruptly. If you + * want to make the music stop but allow samples to fade (beware, as they + * might not fade at all!), use dumb_it_sr_set_speed() and set the speed + * to 0. Note that xm_speed_zero() will not be called if you set the + * speed manually, and also that this will work for IT and S3M files even + * though the music can't stop in this way by itself. + */ + + int (DUMBCALLBACK *xm_speed_zero)(void *data); + void *xm_speed_zero_data; + /* Return 1 to terminate the mod, without letting samples fade. */ + + int (DUMBCALLBACK *midi)(void *data, int channel, unsigned char byte); + void *midi_data; + /* Return 1 to prevent DUMB from subsequently interpreting the MIDI bytes + * itself. In other words, return 1 if the Zxx macros in an IT file are + * controlling filters and shouldn't be. + */ + + int (DUMBCALLBACK *global_volume_zero)(void *data); + void *global_volume_zero_data; + /* Return 1 to terminate the module when global volume is set to zero. */ +}; + + + +void _dumb_it_end_sigrenderer(sigrenderer_t *sigrenderer); +void _dumb_it_unload_sigdata(sigdata_t *vsigdata); + +extern DUH_SIGTYPE_DESC _dumb_sigtype_it; + + + +#define XM_APPREGIO 0 +#define XM_PORTAMENTO_UP 1 +#define XM_PORTAMENTO_DOWN 2 +#define XM_TONE_PORTAMENTO 3 +#define XM_VIBRATO 4 +#define XM_VOLSLIDE_TONEPORTA 5 +#define XM_VOLSLIDE_VIBRATO 6 +#define XM_TREMOLO 7 +#define XM_SET_PANNING 8 +#define XM_SAMPLE_OFFSET 9 +#define XM_VOLUME_SLIDE 10 /* A */ +#define XM_POSITION_JUMP 11 /* B */ +#define XM_SET_CHANNEL_VOLUME 12 /* C */ +#define XM_PATTERN_BREAK 13 /* D */ +#define XM_E 14 /* E */ +#define XM_SET_TEMPO_BPM 15 /* F */ +#define XM_SET_GLOBAL_VOLUME 16 /* G */ +#define XM_GLOBAL_VOLUME_SLIDE 17 /* H */ +#define XM_KEY_OFF 20 /* K (undocumented) */ +#define XM_SET_ENVELOPE_POSITION 21 /* L */ +#define XM_PANNING_SLIDE 25 /* P */ +#define XM_MULTI_RETRIG 27 /* R */ +#define XM_TREMOR 29 /* T */ +#define XM_X 33 /* X */ +#define XM_N_EFFECTS (10+26) + +#define XM_E_SET_FILTER 0x0 +#define XM_E_FINE_PORTA_UP 0x1 +#define XM_E_FINE_PORTA_DOWN 0x2 +#define XM_E_SET_GLISSANDO_CONTROL 0x3 +#define XM_E_SET_VIBRATO_CONTROL 0x4 +#define XM_E_SET_FINETUNE 0x5 +#define XM_E_SET_LOOP 0x6 +#define XM_E_SET_TREMOLO_CONTROL 0x7 +#define XM_E_SET_PANNING 0x8 +#define XM_E_RETRIG_NOTE 0x9 +#define XM_E_FINE_VOLSLIDE_UP 0xA +#define XM_E_FINE_VOLSLIDE_DOWN 0xB +#define XM_E_NOTE_CUT 0xC +#define XM_E_NOTE_DELAY 0xD +#define XM_E_PATTERN_DELAY 0xE +#define XM_E_SET_MIDI_MACRO 0xF + +#define XM_X_EXTRAFINE_PORTA_UP 1 +#define XM_X_EXTRAFINE_PORTA_DOWN 2 + +/* To make my life a bit simpler during conversion, effect E:xy is converted + * to effect number EBASE+x:y. The same applies to effect X, and IT's S. That + * way, these effects can be manipulated like regular effects. + */ +#define EBASE (XM_N_EFFECTS) +#define XBASE (EBASE+16) +#define SBASE (IT_N_EFFECTS) + +#define EFFECT_VALUE(x, y) (((x)<<4)|(y)) +#define HIGH(v) ((v)>>4) +#define LOW(v) ((v)&0x0F) +#define SET_HIGH(v, x) v = (((x)<<4)|((v)&0x0F)) +#define SET_LOW(v, y) v = (((v)&0xF0)|(y)) +#define BCD_TO_NORMAL(v) (HIGH(v)*10+LOW(v)) + + + +#if 0 +unsigned char **_dumb_malloc2(int w, int h); +void _dumb_free2(unsigned char **line); +#endif + +void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry, int mod); +int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata); + + +#define PTM_APPREGIO 0 +#define PTM_PORTAMENTO_UP 1 +#define PTM_PORTAMENTO_DOWN 2 +#define PTM_TONE_PORTAMENTO 3 +#define PTM_VIBRATO 4 +#define PTM_VOLSLIDE_TONEPORTA 5 +#define PTM_VOLSLIDE_VIBRATO 6 +#define PTM_TREMOLO 7 +#define PTM_SAMPLE_OFFSET 9 +#define PTM_VOLUME_SLIDE 10 /* A */ +#define PTM_POSITION_JUMP 11 /* B */ +#define PTM_SET_CHANNEL_VOLUME 12 /* C */ +#define PTM_PATTERN_BREAK 13 /* D */ +#define PTM_E 14 /* E */ +#define PTM_SET_TEMPO_BPM 15 /* F */ +#define PTM_SET_GLOBAL_VOLUME 16 /* G */ +#define PTM_RETRIGGER 17 /* H */ +#define PTM_FINE_VIBRATO 18 /* I */ +#define PTM_NOTE_SLIDE_UP 19 /* J */ +#define PTM_NOTE_SLIDE_DOWN 20 /* K */ +#define PTM_NOTE_SLIDE_UP_RETRIG 21 /* L */ +#define PTM_NOTE_SLIDE_DOWN_RETRIG 22 /* M */ +#define PTM_N_EFFECTS 23 + +#define PTM_E_FINE_PORTA_DOWN 0x1 +#define PTM_E_FINE_PORTA_UP 0x2 +#define PTM_E_SET_VIBRATO_CONTROL 0x4 +#define PTM_E_SET_FINETUNE 0x5 +#define PTM_E_SET_LOOP 0x6 +#define PTM_E_SET_TREMOLO_CONTROL 0x7 +#define PTM_E_SET_PANNING 0x8 +#define PTM_E_RETRIG_NOTE 0x9 +#define PTM_E_FINE_VOLSLIDE_UP 0xA +#define PTM_E_FINE_VOLSLIDE_DOWN 0xB +#define PTM_E_NOTE_CUT 0xC +#define PTM_E_NOTE_DELAY 0xD +#define PTM_E_PATTERN_DELAY 0xE + +/* To make my life a bit simpler during conversion, effect E:xy is converted + * to effect number EBASE+x:y. The same applies to effect X, and IT's S. That + * way, these effects can be manipulated like regular effects. + */ +#define PTM_EBASE (PTM_N_EFFECTS) + +void _dumb_it_ptm_convert_effect(int effect, int value, IT_ENTRY *entry); + +int32 _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f); + +void _dumb_it_interleave_stereo_sample(IT_SAMPLE *sample); + +/* Calling either of these is optional */ +void _dumb_init_cubic(); +#ifdef _USE_SSE +void _dumb_init_sse(); +#endif + +#endif /* INTERNAL_IT_H */ diff --git a/thirdparty/dumb/include/internal/lpc.h b/thirdparty/dumb/include/internal/lpc.h new file mode 100644 index 000000000..47fb03334 --- /dev/null +++ b/thirdparty/dumb/include/internal/lpc.h @@ -0,0 +1,30 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: LPC low level routines + last mod: $Id: lpc.h 16037 2009-05-26 21:10:58Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_LPC_H_ +#define _V_LPC_H_ + +/* simple linear scale LPC code */ +extern float vorbis_lpc_from_data(float *data,float *lpc,int n,int m); + +extern void vorbis_lpc_predict(float *coeff,float *prime,int m, + float *data,long n); + +struct DUMB_IT_SIGDATA; +extern void dumb_it_add_lpc(struct DUMB_IT_SIGDATA *sigdata); + +#endif diff --git a/thirdparty/dumb/include/internal/mulsc.h b/thirdparty/dumb/include/internal/mulsc.h new file mode 100644 index 000000000..57d6ec291 --- /dev/null +++ b/thirdparty/dumb/include/internal/mulsc.h @@ -0,0 +1,36 @@ +#ifndef INTERNAL_MULSC_H +#define INTERNAL_MULSC_H + +#if !defined(_MSC_VER) || !defined(_M_IX86) || _MSC_VER >= 1800 +//#define MULSC(a, b) ((int)((LONG_LONG)(a) * (b) >> 16)) +//#define MULSC(a, b) ((a) * ((b) >> 2) >> 14) +#define MULSCV(a, b) ((int)((LONG_LONG)(a) * (b) >> 32)) +#define MULSCA(a, b) ((int)((LONG_LONG)((a) << 4) * (b) >> 32)) +#define MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32)) +#define MULSC16(a, b) ((int)((LONG_LONG)((a) << 12) * ((b) << 12) >> 32)) +#else +/* VC++ calls __allmull and __allshr for the above math. I don't know why. + * [Need to check if this still applies to recent versions of the compiler.] */ +static __forceinline unsigned long long MULLL(int a, int b) +{ + __asm mov eax,a + __asm imul b +} +static __forceinline int MULSCV (int a, int b) +{ +#ifndef _DEBUG + union { unsigned long long q; struct { int l, h; }; } val; + val.q = MULLL(a,b); + return val.h; +#else + __asm mov eax,a + __asm imul b + __asm mov eax,edx +#endif +} +#define MULSCA(a, b) MULSCV((a) << 4, b) +#define MULSC(a, b) MULSCV((a) << 4, (b) << 12) +#define MULSC16(a, b) MULSCV((a) << 12, (b) << 12) +#endif + +#endif /* INTERNAL_MULSC_H */ \ No newline at end of file diff --git a/thirdparty/dumb/include/internal/resampler.h b/thirdparty/dumb/include/internal/resampler.h new file mode 100644 index 000000000..0050ebf1a --- /dev/null +++ b/thirdparty/dumb/include/internal/resampler.h @@ -0,0 +1,58 @@ +#ifndef _RESAMPLER_H_ +#define _RESAMPLER_H_ + +// Ugglay +#ifdef RESAMPLER_DECORATE +#define PASTE(a,b) a ## b +#define EVALUATE(a,b) PASTE(a,b) +#define resampler_init EVALUATE(RESAMPLER_DECORATE,_resampler_init) +#define resampler_create EVALUATE(RESAMPLER_DECORATE,_resampler_create) +#define resampler_delete EVALUATE(RESAMPLER_DECORATE,_resampler_delete) +#define resampler_dup EVALUATE(RESAMPLER_DECORATE,_resampler_dup) +#define resampler_dup_inplace EVALUATE(RESAMPLER_DECORATE,_resampler_dup_inplace) +#define resampler_set_quality EVALUATE(RESAMPLER_DECORATE,_resampler_set_quality) +#define resampler_get_free_count EVALUATE(RESAMPLER_DECORATE,_resampler_get_free_count) +#define resampler_write_sample EVALUATE(RESAMPLER_DECORATE,_resampler_write_sample) +#define resampler_write_sample_fixed EVALUATE(RESAMPLER_DECORATE,_resampler_write_sample_fixed) +#define resampler_set_rate EVALUATE(RESAMPLER_DECORATE,_resampler_set_rate) +#define resampler_ready EVALUATE(RESAMPLER_DECORATE,_resampler_ready) +#define resampler_clear EVALUATE(RESAMPLER_DECORATE,_resampler_clear) +#define resampler_get_sample_count EVALUATE(RESAMPLER_DECORATE,_resampler_get_sample_count) +#define resampler_get_sample EVALUATE(RESAMPLER_DECORATE,_resampler_get_sample) +#define resampler_get_sample_float EVALUATE(RESAMPLER_DECORATE,_resampler_get_sample_float) +#define resampler_remove_sample EVALUATE(RESAMPLER_DECORATE,_resampler_remove_sample) +#endif + +void resampler_init(void); + +void * resampler_create(void); +void resampler_delete(void *); +void * resampler_dup(const void *); +void resampler_dup_inplace(void *, const void *); + +enum +{ + RESAMPLER_QUALITY_MIN = 0, + RESAMPLER_QUALITY_ZOH = 0, + RESAMPLER_QUALITY_BLEP = 1, + RESAMPLER_QUALITY_LINEAR = 2, + RESAMPLER_QUALITY_BLAM = 3, + RESAMPLER_QUALITY_CUBIC = 4, + RESAMPLER_QUALITY_SINC = 5, + RESAMPLER_QUALITY_MAX = 5 +}; + +void resampler_set_quality(void *, int quality); + +int resampler_get_free_count(void *); +void resampler_write_sample(void *, short sample); +void resampler_write_sample_fixed(void *, int sample, unsigned char depth); +void resampler_set_rate( void *, double new_factor ); +int resampler_ready(void *); +void resampler_clear(void *); +int resampler_get_sample_count(void *); +int resampler_get_sample(void *); +float resampler_get_sample_float(void *); +void resampler_remove_sample(void *, int decay); + +#endif diff --git a/thirdparty/dumb/include/internal/riff.h b/thirdparty/dumb/include/internal/riff.h new file mode 100644 index 000000000..54c87c47c --- /dev/null +++ b/thirdparty/dumb/include/internal/riff.h @@ -0,0 +1,24 @@ +#ifndef RIFF_H +#define RIFF_H + +struct riff; + +struct riff_chunk +{ + unsigned type; + int32 offset; + unsigned size; + struct riff * nested; +}; + +struct riff +{ + unsigned type; + unsigned chunk_count; + struct riff_chunk * chunks; +}; + +struct riff * riff_parse( DUMBFILE * f, int32 offset, int32 size, unsigned proper ); +void riff_free( struct riff * ); + +#endif diff --git a/thirdparty/dumb/include/internal/stack_alloc.h b/thirdparty/dumb/include/internal/stack_alloc.h new file mode 100644 index 000000000..4cab5b9c6 --- /dev/null +++ b/thirdparty/dumb/include/internal/stack_alloc.h @@ -0,0 +1,113 @@ +/* Copyright (C) 2002 Jean-Marc Valin */ +/** + @file stack_alloc.h + @brief Temporary memory allocation on stack +*/ +/* + 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 the Xiph.org Foundation 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``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 THE FOUNDATION OR + CONTRIBUTORS 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 STACK_ALLOC_H +#define STACK_ALLOC_H + +#ifdef _WIN32 +# include +#else +# ifdef HAVE_ALLOCA_H +# include +# else +# include +# endif +#endif + +/** + * @def ALIGN(stack, size) + * + * Aligns the stack to a 'size' boundary + * + * @param stack Stack + * @param size New size boundary + */ + +/** + * @def PUSH(stack, size, type) + * + * Allocates 'size' elements of type 'type' on the stack + * + * @param stack Stack + * @param size Number of elements + * @param type Type of element + */ + +/** + * @def VARDECL(var) + * + * Declare variable on stack + * + * @param var Variable to declare + */ + +/** + * @def ALLOC(var, size, type) + * + * Allocate 'size' elements of 'type' on stack + * + * @param var Name of variable to allocate + * @param size Number of elements + * @param type Type of element + */ + +#ifdef ENABLE_VALGRIND + +#include + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) + +#define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) + +#else + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) + +#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) + +#endif + +#if defined(VAR_ARRAYS) +#define VARDECL(var) +#define ALLOC(var, size, type) type var[size] +#elif defined(USE_ALLOCA) +#define VARDECL(var) var +#define ALLOC(var, size, type) var = alloca(sizeof(type)*(size)) +#else +#define VARDECL(var) var +#define ALLOC(var, size, type) var = PUSH(stack, size, type) +#endif + + +#endif diff --git a/thirdparty/dumb/licence.txt b/thirdparty/dumb/licence.txt new file mode 100644 index 000000000..961fe4ef8 --- /dev/null +++ b/thirdparty/dumb/licence.txt @@ -0,0 +1,87 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * licence.txt - Conditions for use of DUMB. / / \ \ + * | < / \_ + * If you do not agree to these terms, please | \/ /\ / + * do not use DUMB. \_ / > / + * | \ / / + * Information in [brackets] is provided to aid | ' / + * interpretation of the licence. \__/ + */ + + +Dynamic Universal Music Bibliotheque, Version 0.9.3 + +Copyright (C) 2001-2005 Ben Davis, Robert J Ohannessian and Julien Cugniere + +This software is provided 'as-is', without any express or implied warranty. +In no event shall the authors be held liable for any damages arising from the +use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a + product, you are requested to acknowledge its use in the product + documentation, along with details on where to get an unmodified version of + this software, but this is not a strict requirement. + + [Note that the above point asks for a link to DUMB, not just a mention. + Googling for DUMB doesn't help much! The URL is "http://dumb.sf.net/".] + + [The link was originally strictly required. This was changed for two + reasons. Firstly, if many projects request an acknowledgement, the list of + acknowledgements can become quite unmanageable. Secondly, DUMB was placing + a restriction on the code using it, preventing people from using the GNU + General Public Licence which disallows any such restrictions. See + http://www.gnu.org/philosophy/bsd.html for more information on this + subject. However, if DUMB plays a significant part in your project, we do + urge you to acknowledge its use.] + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed from or altered in any source distribution. + +4. If you are using the Program in someone else's bedroom on any Monday at + 3:05 pm, you are not allowed to modify the Program for ten minutes. [This + clause provided by Inphernic; every licence should contain at least one + clause, the reasoning behind which is far from obvious.] + +5. Users who wish to use DUMB for the specific purpose of playing music are + required to feed their dog on every full moon (if deemed appropriate). + [This clause provided by Allefant, who couldn't remember what Inphernic's + clause was.] + +6. No clause in this licence shall prevent this software from being depended + upon by a product licensed under the GNU General Public Licence. If such a + clause is deemed to exist, Debian, then it shall be respected in spirit as + far as possible and all other clauses shall continue to apply in full + force. + +8. Take the number stated as introducing this clause. Multiply it by two, + then subtract four. Now insert a '+' between the two digits and evaluate + the resulting sum. Call the result 'x'. If you have not yet concluded that + every numbered clause in this licence whose ordinal number is strictly + greater than 'x' (with the exception of the present clause) is null and + void, Debian, then you are hereby informed that laughter is good for one's + health and you are warmly suggested to do it. By the way, Clauses 4, 5 and + 6 are null and void. Incidentally, I like Kubuntu. The work you guys do is + awesome. (Lawyers, on the other hand ...) + +We regret that we cannot provide any warranty, not even the implied warranty +of merchantability or fitness for a particular purpose. + +Some files generated or copied by automake, autoconf and friends are +available in an extra download. These fall under separate licences but are +all free to distribute. Please check their licences as necessary. diff --git a/thirdparty/dumb/prj/.gitignore b/thirdparty/dumb/prj/.gitignore new file mode 100644 index 000000000..36d588baa --- /dev/null +++ b/thirdparty/dumb/prj/.gitignore @@ -0,0 +1,3 @@ +dumb-build-Desktop-Release +dumb-build-Desktop-Debug +*.user diff --git a/thirdparty/dumb/prj/dumb/dumb.pro b/thirdparty/dumb/prj/dumb/dumb.pro new file mode 100644 index 000000000..9244ce4bd --- /dev/null +++ b/thirdparty/dumb/prj/dumb/dumb.pro @@ -0,0 +1,128 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2012-12-22T16:33:53 +# +#------------------------------------------------- + +QT -= core gui + +TARGET = dumb +TEMPLATE = lib +CONFIG += staticlib + +DEFINES += _USE_SSE + +INCLUDEPATH += ../../include + +QMAKE_CFLAGS += -msse + +SOURCES += \ + ../../src/core/unload.c \ + ../../src/core/rendsig.c \ + ../../src/core/rendduh.c \ + ../../src/core/register.c \ + ../../src/core/readduh.c \ + ../../src/core/rawsig.c \ + ../../src/core/makeduh.c \ + ../../src/core/loadduh.c \ + ../../src/core/dumbfile.c \ + ../../src/core/duhtag.c \ + ../../src/core/duhlen.c \ + ../../src/core/atexit.c \ + ../../src/helpers/stdfile.c \ + ../../src/helpers/silence.c \ + ../../src/helpers/sampbuf.c \ + ../../src/helpers/riff.c \ + ../../src/helpers/resample.c \ + ../../src/helpers/memfile.c \ + ../../src/helpers/clickrem.c \ + ../../src/helpers/barray.c \ + ../../src/it/xmeffect.c \ + ../../src/it/readxm2.c \ + ../../src/it/readxm.c \ + ../../src/it/readstm2.c \ + ../../src/it/readstm.c \ + ../../src/it/reads3m2.c \ + ../../src/it/reads3m.c \ + ../../src/it/readriff.c \ + ../../src/it/readptm.c \ + ../../src/it/readpsm.c \ + ../../src/it/readoldpsm.c \ + ../../src/it/readokt2.c \ + ../../src/it/readokt.c \ + ../../src/it/readmtm.c \ + ../../src/it/readmod2.c \ + ../../src/it/readmod.c \ + ../../src/it/readdsmf.c \ + ../../src/it/readasy.c \ + ../../src/it/readamf2.c \ + ../../src/it/readamf.c \ + ../../src/it/readam.c \ + ../../src/it/read6692.c \ + ../../src/it/read669.c \ + ../../src/it/ptmeffect.c \ + ../../src/it/loadxm2.c \ + ../../src/it/loadxm.c \ + ../../src/it/loadstm2.c \ + ../../src/it/loadstm.c \ + ../../src/it/loads3m2.c \ + ../../src/it/loads3m.c \ + ../../src/it/loadriff2.c \ + ../../src/it/loadriff.c \ + ../../src/it/loadptm2.c \ + ../../src/it/loadptm.c \ + ../../src/it/loadpsm2.c \ + ../../src/it/loadpsm.c \ + ../../src/it/loadoldpsm2.c \ + ../../src/it/loadoldpsm.c \ + ../../src/it/loadokt2.c \ + ../../src/it/loadokt.c \ + ../../src/it/loadmtm2.c \ + ../../src/it/loadmtm.c \ + ../../src/it/loadmod2.c \ + ../../src/it/loadmod.c \ + ../../src/it/loadasy2.c \ + ../../src/it/loadasy.c \ + ../../src/it/loadamf2.c \ + ../../src/it/loadamf.c \ + ../../src/it/load6692.c \ + ../../src/it/load669.c \ + ../../src/it/itunload.c \ + ../../src/it/itrender.c \ + ../../src/it/itread2.c \ + ../../src/it/itread.c \ + ../../src/it/itorder.c \ + ../../src/it/itmisc.c \ + ../../src/it/itload2.c \ + ../../src/it/itload.c \ + ../../src/it/readany.c \ + ../../src/it/loadany2.c \ + ../../src/it/loadany.c \ + ../../src/it/readany2.c \ + ../../src/helpers/sinc_resampler.c \ + ../../src/helpers/lpc.c + +HEADERS += \ + ../../include/dumb.h \ + ../../include/internal/riff.h \ + ../../include/internal/it.h \ + ../../include/internal/dumb.h \ + ../../include/internal/barray.h \ + ../../include/internal/aldumb.h \ + ../../include/internal/sinc_resampler.h \ + ../../include/internal/stack_alloc.h \ + ../../include/internal/lpc.h \ + ../../include/internal/dumbfile.h +unix:!symbian { + maemo5 { + target.path = /opt/usr/lib + } else { + target.path = /usr/lib + } + INSTALLS += target +} + +OTHER_FILES += \ + ../../src/helpers/resample.inc \ + ../../src/helpers/resamp3.inc \ + ../../src/helpers/resamp2.inc diff --git a/thirdparty/dumb/readme.txt b/thirdparty/dumb/readme.txt new file mode 100644 index 000000000..e86af048a --- /dev/null +++ b/thirdparty/dumb/readme.txt @@ -0,0 +1,541 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readme.txt - General information on DUMB. / / \ \ + * | < / \_ + * | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + + +******************** +*** Introduction *** +******************** + + +Thank you for downloading DUMB v0.9.3! You should have the following +documentation: + + readme.txt - This file + licence.txt - Conditions for the use of this software + release.txt - Release notes and changes for this and past releases + docs/ + howto.txt - Step-by-step instructions on adding DUMB to your project + faq.txt - Frequently asked questions and answers to them + dumb.txt - DUMB library reference + deprec.txt - Information about deprecated parts of the API + ptr.txt - Quick introduction to pointers for those who need it + fnptr.txt - Explanation of function pointers for those who need it + modplug.txt - Our official position regarding ModPlug Tracker + +This file will help you get DUMB set up. If you have not yet done so, please +read licence.txt and release.txt before proceeding. After you've got DUMB set +up, please refer to the files in the docs/ directory at your convenience. I +recommend you start with howto.txt. + + +**************** +*** Features *** +**************** + + +Here is the statutory feature list: + +- Freeware + +- Supports playback of IT, XM, S3M and MOD files + +- Faithful to the original trackers, especially IT; if it plays your module + wrongly, please tell me so I can fix the bug! (But please don't complain + about differences between DUMB and ModPlug Tracker; see docs/modplug.txt) + +- Accurate support for low-pass resonant filters for IT files + +- Very accurate timing and pitching; completely deterministic playback + +- Click removal + +- Facility to embed music files in other files (e.g. Allegro datafiles) + +- Three resampling quality settings: aliasing, linear interpolation and cubic + interpolation + +- Number of samples playing at once can be limited to reduce processor usage, + but samples will come back in when other louder ones stop + +- All notes will be present and correct even if you start a piece of music in + the middle + +- Option to take longer loading but seek fast to any point before the music + first loops (seeking time increases beyond this point) + +- Audio generated can be used in any way; DUMB does not necessarily send it + straight to a sound output system + +- Can be used with Allegro, can be used without (if you'd like to help make + DUMB more approachable to people who aren't using Allegro, please contact + me) + +- Makefile provided for DJGPP, MinGW, Linux, BeOS and Mac OS X + +- Project files provided for MSVC 6 + +- Autotools-based configure script available as a separate download for + masochists + +- Code should port anywhere that has a 32-bit C compiler; instructions on + compiling it manually are available further down + + +********************* +*** What you need *** +********************* + + +To use DUMB, you need a 32-bit C compiler (GCC and MSVC are fine). If you +have Allegro, DUMB can integrate with its audio streams and datafiles, making +your life easier. If you do not wish to use Allegro, you will have to do some +work to get music playing back. The 'dumbplay' example program requires +Allegro. + + Allegro - http://alleg.sf.net/ + + +********************************************** +*** How to set DUMB up with DJGPP or MinGW *** +********************************************** + + +You should have got the .zip version. If for some reason you got the .tar.gz +version instead, you may have to convert make/config.bat to DOS text file +format. WinZip does this automatically by default. Otherwise, loading it into +MS EDIT and saving it again should do the trick (but do not do this to the +Makefiles as it destroys tabs). You will have to do the same for any files +you want to view in Windows Notepad. If you have problems, just go and +download the .zip instead. + +Make sure you preserved the directory structure when you extracted DUMB from +the archive. Most unzipping programs will do this by default, but pkunzip +requires you to pass -d. If not, please delete DUMB and extract it again +properly. + +If you are using Windows, open an MS-DOS Prompt or a Windows Command Line. +Change to the directory into which you unzipped DUMB. + +If you are using MinGW (and you haven't renamed 'mingw32-make'), type: + + mingw32-make + +Otherwise, type the following: + + make + +DUMB will ask you whether you wish to compile for DJGPP or MinGW. Then it +will ask you whether you want support for Allegro. (You have to have made and +installed Allegro's optimised library for this to work.) Finally, it will +compile optimised and debugging builds of DUMB, along with the example +programs. When it has finished, run one of the following to install the +libraries: + + make install + mingw32-make install + +All done! If you ever need the configuration again (e.g. if you compiled for +DJGPP before and you want to compile for MinGW now), run one of the +following: + + make config + mingw32-make config + +See the comments in the Makefile for other targets. + +Note: the Makefile will only work properly if you have COMSPEC or ComSpec set +to point to command.com or cmd.exe. If you set it to point to a Unix-style +shell, the Makefile won't work. + +Please let me know if you have any trouble. + +As an alternative, MSYS users may attempt to use the configure script, +available in dumb-0.9.3-autotools.tar.gz. This has been found to work without +Allegro, and is untested with Allegro. I should appreciate feedback from +anyone else who tries this. I do not recommend its use, partly because it +creates dynamically linked libraries and I don't know how to stop it from +doing that (see the section on compiling DUMB manually), and partly because +autotools are plain evil. + +Scroll down for information on the example programs. Refer to docs/howto.txt +when you are ready to start programming with DUMB. If you use DUMB in a game, +let me know - I might decide to place a link to your game on DUMB's website! + + +****************************************************** +*** How to set DUMB up with Microsoft Visual C++ 6 *** +****************************************************** + + +If you have a newer version of Microsoft Visual C++ or Visual Something that +supports C++, please try these instructions and let me know if it works. + +You should have got the .zip version. If for some reason you got the .tar.gz +version instead, you may have to convert some files to DOS text file format. +WinZip does this automatically by default. Otherwise, loading such files into +MS EDIT and saving them again should do the trick. You will have to do this +for any files you want to view in Windows Notepad. If you have problems, just +go and download the .zip instead. + +Make sure you preserved the directory structure when you extracted DUMB from +the archive. Most unzipping programs will do this by default, but pkunzip +requires you to pass -d. If not, please delete DUMB and extract it again +properly. + +DUMB comes with a workspace Microsoft Visual C++ 6, containing projects for +the DUMB core, the Allegro interface library and each of the examples. The +first thing you might want to do is load the workspace up and have a look +around. You will find it in the dumb\vc6 directory under the name dumb.dsw. +Note that the aldumb and dumbplay projects require Allegro, so they won't +work if you don't have Allegro. Nevertheless, dumbplay is the best-commented +of the examples, so do have a look. + +When you are ready to add DUMB to your project, follow these instructions: + +1. Open your project in VC++. +2. Select Project|Insert Project into Workspace... +3. Navigate to the dumb\vc6\dumb directory and select dumb.dsp. + Alternatively, if you know that you are statically linking with a library + that uses the statically linked multithreaded runtime (/MT), you may wish + to select dumb_static.dsp in the dumb_static subdirectory instead. +4. Select Build|Set Active Configuration..., and reselect one of your + project's configurations. +5. Select Project|Dependencies... and ensure your project is dependent on + DUMB. +6. Select Project|Settings..., Settings for: All Configurations, C/C++ tab, + Preprocessor category. Add the DUMB include directory to the Additional + Include Directories box. +7. Ensure that for all the projects in the workspace (or more likely just all + the projects in a particular dependency chain) the run-time libraries are + the same. That's in Project|Settings, C/C++ tab, Code generation category, + Use run-time library dropdown. The settings for Release and Debug are + separate, so you'll have to change them one at a time. Exactly which run- + time library you use will depend on what you need; it doesn't appear that + DUMB has any particular requirements, so set it to whatever you're using + now. (It will have to be /MD, the multithreaded DLL library, if you are + statically linking with Allegro. If you are dynamically linking with + Allegro than it doesn't matter.) +8. If you are using Allegro, do some or all of the above for the aldumb.dsp + project in the aldumb directory too. + +Good thing you only have to do all that once ... or twice ... + +If you have the Intel compiler installed, it will - well, should - be used to +compile DUMB. The only setting I [Tom Seddon] added is /QxiM. This allows the +compiler to use PPro and MMX instructions, and so when compiling with Intel +the resultant EXE will require a Pentium II or greater. I don't think this is +unreasonable. After all, it is 2003 :) + +[Note from Ben: the Intel compiler is evil! It makes AMD processors look bad! +Patch it or boycott it or something!] + +If you don't have the Intel compiler, VC will compile DUMB as normal. + +This project file and these instructions were provided by Tom Seddon (I hope +I got his name right; I had to guess it from his e-mail address!). Chad +Austin has since changed the project files around, and I've just attempted to +hack them to incorporate new source files. I've also tried to update the +instructions using guesswork and some knowledge of Visual J++ (you heard me). +The instructions and the project files are to this day untested by me. If you +have problems, check the download page at http://dumb.sf.net/ to see if they +are addressed; failing that, direct queries to me and I'll try to figure them +out. + +If you have any comments at all on how the VC6 projects are laid out, or how +the instructions could be improved, I should be really grateful to hear them. +I am a perfectionist, after all. :) + +Scroll down for information on the example programs. When you are ready to +start using DUMB, refer to docs/howto.txt. If you use DUMB in a game, let me +know - I might decide to place a link to your game on DUMB's website! + + +****************************************************** +*** How to set DUMB up on Linux, BeOS and Mac OS X *** +****************************************************** + + +You should have got the .tar.gz version. If for some reason you got the .zip +version instead, you may have to strip all characters with ASCII code 13 from +some of the text files. If you have problems, just go and download the +.tar.gz instead. + +You have two options. There is a Makefile which should cope with most +systems. The first option is to use this default Makefile, and the procedure +is explained below. The second option is to download +dumb-0.9.3-autotools.tar.gz, extract it over the installation, run +./configure and use the generated Makefile. Users who choose to do this are +left to their own devices but advised to read the information at the end of +this section. I strongly recommend the first option. + +If you are not using the configure script, the procedure is as follows. + +First, run the following command as a normal user: + + make + +You will be asked whether you want Allegro support. Then, unless you are on +BeOS, you will be asked where you'd like DUMB to install its headers, +libraries and examples (which will go in the include/, lib/ and bin/ +subdirectories of the prefix you specify). BeOS has fixed locations for these +files. You may use shell variables here, e.g. $HOME or ${HOME}, but ~ will +not work. Once you have specified these pieces of information, the optimised +and debugging builds of DUMB will be compiled, along with the examples. When +it has finished, you can install them with: + + make install + +You may need to be root for this to work. It depends on the prefix you chose. + +Note: the Makefile will only work if COMSPEC and ComSpec are both undefined. +If either of these is defined, the Makefile will try to build for a Windows +system, and will fail. + +Please let me know if you have any trouble. + +Scroll down for information on the example programs. Refer to docs/howto.txt +when you are ready to start programming with DUMB. If you use DUMB in a game, +let me know - I might decide to place a link to your game on DUMB's website! + +Important information for users of the configure script follows. + +The Makefile generated by the configure script creates dynamically linked +libraries, and I don't know how to stop it from doing so. See the section +below on building DUMB manually for why I recommend linking DUMB statically. +However, if you choose to use the configure script, note the following. + +The default Makefile is a copy of Makefile.rdy (short for 'ready'), and it +must exist with the name Makefile.rdy in order to work. The configure script +will overwrite Makefile, so if you want the default Makefile back, just run: + + cp Makefile.rdy Makefile + +Do not use a symlink, as that would result in Makefile.rdy getting +overwritten next time the configure script is run! + +You can also access the usual build system by passing '-f Makefile.rdy' to +Make. + + +******************************************************** +*** How to build DUMB manually if nothing else works *** +******************************************************** + + +Those porting to platforms without floating point support should be aware +that DUMB does use floating point operations but not in the inner loops. They +are used for volume and note pitch calculations, and they are used when +initialising the filter algorithm for given cut-off and resonance values. +Please let me know if this is a problem for you. If there is enough demand, I +may be able to eliminate one or both of these cases. + +All of the library source code may be found in the src/ subdirectory. There +are headers in the include/ subdirectory, and src/helpers/resample.c also +#includes some .inc files in its own directory. + +There are four subdirectories under src/. For projects not using Allegro, you +will need all the files in src/core/, src/helpers/ and src/it/. If you are +using Allegro, you will want the src/allegro/ subdirectory too. For +consistency with the other build systems, the contents of src/allegro/ should +be compiled into a separate library. + +I recommend static-linking DUMB, since the version information is done via +macros and the API has a tendency to change. If you static-link, then once +your program is in binary form, you can be sure that changes to the installed +version of DUMB won't cause it to malfuction. It is my fault that the API has +been so unstable. Sorry! + +Compile each .c file separately. As mentioned above, you will need to specify +two places to look for #include files: the include/ directory and the source +file's own directory. You will also need to define the symbol +DUMB_DECLARE_DEPRECATED on the command line. + +Do not compile the .inc files separately. + +You may need to edit dumb.h and add your own definition for LONG_LONG. It +should be a 64-bit integer. If you do this, please see if you can add a check +for your compiler so that it still works with other compilers. + +DUMB has two build modes. If you define the symbol DEBUGMODE, some checks for +programmer error will be incorporated into the library. Otherwise it will be +built without any such checks. (DUMB will however always thoroughly check the +validity of files it is loading. If you ever find a module file that crashes +DUMB, please let me know!) + +I recommend building two versions of the library, one with DEBUGMODE defined +and debugging information included, and the other with compiler optimisation +enabled. If you can install DUMB system-wide so that your projects, and other +people's, can simply #include or and link with libraries +by simple name with no path, then that is ideal. + +If you successfully port DUMB to a new platform, please let me know! + + +**************************** +*** The example programs *** +**************************** + + +Three example programs are provided. On DOS and Windows, you can find them in +the examples subdirectory. On other systems they will be installed system- +wide. + +dumbplay + This program will only be built if you have Allegro. Pass it the filename + of an IT, XM, S3M or MOD file, and it will play it. It's not a polished + player with real-time threading or anything - so don't complain about it + stuttering while you use other programs - but it does show DUMB's fidelity + nicely. You can control the playback quality by editing dumb.ini, which + must be in the current working directory. (This is a flaw for systems + where the program is installed system-wide, but it is non-fatal.) Have a + look at the examples/dumb.ini file for further information. + +dumbout + This program does not need Allegro. You can use it to stream an IT, XM, + S3M or MOD file to raw PCM. This can be used as input to an encoder like + oggenc (with appropriate command-line options), or it can be sent to a + .pcm file which can be read by any respectable waveform editor. This + program is also convenient for timing DUMB. Compare the time it takes to + render a module with the module's playing time! dumbout doesn't try to + read any configuration file; the options are set on the command line. + +dumb2wav + This program is much the same as dumbout, but it writes a .wav file with + the appropriate header. Thanks go to Chad Austin for this useful tool. + + +********************************************* +*** Downloading music or writing your own *** +********************************************* + + +If you would like to compose your own music modules, then this section should +help get you started. + +The best programs for the job are the trackers that pioneered the file +formats: + + Impulse Tracker - IT files - http://www.lim.com.au/ImpulseTracker/ + Fast Tracker II - XM files - http://www.fasttracker2.com/ + Scream Tracker 3 - S3M files - No official site known, please use Google + +MOD files come from the Amiga; I do not know what PC tracker to recommend for +editing these. If you know of one, let me know! In the meantime, I would +recommend using a more advanced file format. However, don't convert your +existing MODs just for the sake of it. + +Fast Tracker II is Shareware. It offers a very flashy interface and has a +game embedded, but the IT file format is more powerful and better defined. By +all means try them both and see which you prefer; it is largely a matter of +taste (and, in some cases, religion). Impulse Tracker and Scream Tracker 3 +are Freeware, although you can donate to Impulse Tracker and receive a +slightly upgraded version. DUMB is likely to be at its best with IT files. + +These editors are DOS programs. Users of DOS-incapable operating systems may +like to try ModPlug Tracker, but should read docs/modplug.txt before using it +for any serious work. If you use a different operating system, or if you know +of any module editors for Windows that are more faithful to the original +trackers' playback, please give me some links so I can put them here! + + ModPlug Tracker - http://www.modplug.com/ + +If you have an x86 Linux system with VGA-compatible hardware (which covers +all PC graphics cards I've ever seen), you should be able to get Impulse +Tracker running with DOSEMU. You will have to give it access to the VGA ports +and run it in a true console, as it will not work with the X-based VGA +emulation. I personally added the SB16 emulation to DOSEMU, so you can even +use filters! However, it corrupts samples alarmingly often when saving on my +system - probably a DOSEMU issue. If you set this up, I am curious to know +whether it works for you. + + DOSEMU - http://www.dosemu.org/ + +BEWARE OF WINAMP! Although it's excellent for MP3s, it is notorious for being +one of the worst module players in existence; very many modules play wrongly +with it. There are plug-ins available to improve Winamp's module support, for +example WSP. + + Winamp - http://www.winamp.com/ + WSP - http://www.spytech.cz/index.php?sec=demo + +(There is a Winamp plug-in that uses DUMB, but it is unreliable. If anyone +would like to work on it, please get in touch.) + +While I am at it I should also point out that Winamp is notorious for +containing security flaws. Install it at your own risk, and if it is your +work computer, check with your boss first! + +Samples and instruments are the building blocks of music modules. You can +download samples at + + http://www.tump.net/ + +If you would like to download module files composed by other people, check +the following sites: + + http://www.modarchive.com/ + http://www.scene.org/ + http://www.tump.net/ + http://www.homemusic.cc/main.php + http://www.modplug.com/ + +Once again, if you know of more sites where samples or module files are +available for download, please let me know. + +If you wish to use someone's music in your game, please respect the +composer's wishes. In general, you should ask the composer. Music that has +been placed in the Public Domain can be used by anyone for anything, but it +wouldn't do any harm to ask anyway if you know who the author is. In many +cases the author will be thrilled, so don't hesitate! + +A note about converting modules from one format to another, or converting +from MIDI: don't do it, unless you are a musician and are prepared to go +through the file and make sure everything sounds the way it should! The +module formats are all slightly different, and MIDI is very different; +converting from one format to another will usually do some damage. + +Instead, it is recommended that you allow DUMB to interpret the original file +as it sees fit. DUMB may make mistakes (it does a lot of conversion on +loading), but future versions of DUMB will be able to rectify these mistakes. +On the other hand, if you convert the file, the damage is permanent. + + +*********************** +*** Contact details *** +*********************** + + +If you have trouble with DUMB, or want to contact me for any other reason, my +e-mail address is given below. Please do get in touch, even if I appear to +have disappeared! + +If you wish to chat online about something, perhaps on IRC, that can most +likely be arranged. Send me an e-mail. + + +****************** +*** Conclusion *** +****************** + + +This is the conclusion. + + +Ben Davis +entheh@users.sf.net diff --git a/thirdparty/dumb/release.txt b/thirdparty/dumb/release.txt new file mode 100644 index 000000000..527d44933 --- /dev/null +++ b/thirdparty/dumb/release.txt @@ -0,0 +1,561 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * release.txt - Release notes for DUMB. / / \ \ + * | < / \_ + * | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + + +******************************************* +*** DUMB v0.9.3, released 7 August 2005 *** +******************************************* + +Hello! Welcome to a long-awaited-or-probably-just-given-up-on-by-everybody +release! New to this release are lower memory usage, faster mixing loops, +loading of text fields in the module files, and faster load functions for +projects that don't need to seek within the module or know its length. +Additionally, Chad Austin has contributed a dumb2wav tool for converting +modules to .wav files and updated the Visual Studio 6 project files to +compile all the examples as well as the library. Users of Unix-like systems +will be pleased to know that on Chad's suggestion I have made the build +system cope with variables such as $HOME or ${HOME} in the prefix. + +Chad has also contributed an Autotools build system, but neither of us +recommends its use. The Autotools are an evil black box, we haven't quite +managed to get it right, and goodness help you if it happens not to work for +you. The files are available in a separate download if you absolutely need +them. Notice that that download is almost twice as large as the rest of DUMB! + +Maybe we'll do SCons next time. + +Thanks to Chad for all his work. Chad is the author of Audiere, a portable +sound library which has started using DUMB for its module playback! Wahoo! + + http://audiere.sf.net/ + +There are three main optimisations that went into the mixing loops. + +First, I downloaded ModPlugXMMS and had a peek at the mixing code, which is +Public Domain. It uses look-up tables for the cubic mixing. I pinched the +idea, and that sped DUMB's cubic (best quality) resamplers up by a factor of +two or three. + +Secondly, the samples loaded from the file are now kept in 8-bit or 16-bit +format, whereas previously they were being converted to 24-bit-in-32-bit on +loading. This means the samples occupy a half or a quarter of the memory they +used to occupy. It also had the side-effect of speeding up the mixing loops, +but it meant I had to duplicate the resampling code. (It is all done with +macros in the source code, but it becomes two copies on the binary level.) + +Secondly, stereo samples and stereo mixing buffers are now kept in +interleaved format, where previously the two channels were done separately to +keep the code simpler. This change has made the library quite a bit bigger, +but has made the code run almost twice as fast for stereo output (tested for +modules whose samples are mostly mono)! + +DUMB is now as fast as ModPlugXMMS on my system. + +Some people have also commented that DUMB seems to take a long time loading +files. This is because immediately upon loading the file it runs the playback +engine over it up as far as the point of first loop, taking snapshots at 30- +second intervals to be used as references for fast seeking and finally +storing the playback time. Of course, most games don't need this. You can now +skip it by calling the _quick versions of the dumb_load_*(), dumb_read_*() or +dumb_register_dat_*() functions. Should you need the data later, you can call +dumb_it_do_initial_runthrough() to calculate it. Please note that this cannot +currently be done safely from a concurrent thread while the music is playing. + +As mentioned, DUMB loads the text fields in module files now. You can +retrieve the song title with duh_get_tag(). Sample names and file names and +instrument names and filenames, and the song message for IT files, are +available with a call to duh_get_it_sigdata() and various dumb_it_sd_*() +functions. Please note that text fields added as extensions by ModPlug +Tracker are not supported. + +DUMB's timing is ever so slightly more accurate. This is hardly noticeable, +but it has meant that the length computed will increase very slightly. + +There are many small playback fixes in this release: + +* The Lxx effect in XM files (set envelope position) is now supported. + +* Pattern looping is now correct for XM files. Bizarrely, an ordinary pattern + loop whose start point isn't the first row seems to cause the next pattern + to start at the row corresponding to the loop start point. That must have + been a headache for people creating XM files! Nevertheless, DUMB now + emulates this behaviour. If you have an XM file that was written in a + tracker other than Fast Tracker II and breaks in DUMB, you can get around + it by putting a D00 effect (break to row 0) right at the end of the pattern + containing the loop. + +* XM pattern looping can be infinite. DUMB should detect this and call the + loop callback when it happens. Specifically, it has a loop counter for each + channel, so each time it sets or decrements that counter, it remembers the + loop end point for that channel. When the loop terminates, the loop end + point is reset to 0. If the loop end point ever decreases during a loop, + the loop callback is called. If anyone manages to get around this check and + prevent DUMB from calling the callback, please let me know and send me an + illustrative XM file! + +* For IT files, notes are now removed from channels if they have faded out, + even if they are still in the foreground. After this has happened, a row + with a note and Gxx (tone portamento) specified will cause a new note to + start playing, which is what Impulse Tracker does in this scenario. + (Normally, Gxx prevents the new note from playing and instead causes the + old note to start sliding towards the new note.) + +* If a tone portamento command occurred when no note was playing, the effect + value wasn't stored. This has been fixed. Thanks to Maim from #trax on + EFnet for discovering this bug. + +* DUMB now treats the parameter to the undocumented XM key off effect Kxx as + a delay, consistent with Fast Tracker II's behaviour. It has also been made + not to clear the note, so a subsequent volume command will restore it, as + in Fast Tracker II. + +* DUMB used to process the first row when you created the + DUMB_IT_SIGRENDERER. This happened before you had a chance to install any + callbacks. If an F00 effect occurred on the first row, the music would stop + immediately and the xm_speed_zero callback would be called if it were + present. Unfortunately, it wasn't present, and the algorithm for + calculating the length subsequently went into an endless loop while waiting + for it. Worse still, the same algorithm accumulated data for fast seeking, + and never stopped, so it pretty quickly consumed all the resources. DUMB + will now not process the first row until you first request some samples, + provided you pass zero for pos. Of course, any MOD or XM file with F00 in + the very first row won't do much anyway, but such files won't crash the + library now. + +* There was a subtle bug that affected a few XM files. For instruments with + no associated samples, the array mapping notes to samples is uninitialised. + This became a problem if such instruments were then used, which does happen + sometimes. On many systems, memory is initialised to zero when first given + to a program (for security reasons), so the problem didn't come up most of + the time. However, on platforms like DOS where memory isn't initialised, or + in programs that reuse memory later on (this includes the XMMS plug-in with + which I discovered the bug), a rogue note would occasionally play. This has + now been fixed. + +* DUMB's envelope handling for IT files was subtly wrong. Upon note off, it + stopped obeying the sustain loop points one tick too early. Notes were + often shorter than they should have been, and in pathological cases a whole + extra iteration of the sustain loop section might have been skipped. The + envelope code has now been rewritten. Thanks go to Allefant for Valgrinding + the new code! + +Finally, there were two build problems in the last version, which were fixed +in the download marked with -fixed. They are of course correct in this +version. For the record: + +* The make/config.bat file, responsible for generating make/config.txt, wrote + a crucial line to the wrong place, causing it to be left out of the file. + As a result, the makefile would fail to install everything for Allegro + users, and enter infinite recursion for other users. This applied to people + using DJGPP and MinGW. + +* DUMB's Makefile was supposed to install the example programs on Unix-based + platforms, but it wasn't doing. The fix was to edit Makefile and change the + one occurrence of $COMSPEC to $(COMSPEC). + +That's it! I hope you enjoy this long-awaited-or-probably-just-given-up-on- +by-everybody release of DUMB! + + +****************************************** +*** DUMB v0.9.2, released 2 April 2003 *** +****************************************** + +Yes, there really has been a release. This is not a day-late April fools' +joke. + +DUMB's full name has changed! The old "Dedicated Universal Music +Bastardisation" was rather silly, and not much more than a forced attempt at +finding words beginning with D, U, M and B. I spent weeks and weeks browsing +dictionaries and hopelessly asking others for bright ideas, until the +brilliant Chris "Kitty Cat" Robinson came up with "Dynamic". I decided to +keep the U as Universal, since a DUH struct can hold digital music in any +format. Now all that remained was the B, but it didn't take me long to come +up with Bibliotheque, which, despite looking French, is indeed considered an +English word by Oxford English Dictionary Online, to which my university has +a subscription. So there you have it - the name now makes sense. + +The two most significant additions to the project would have to be the new +thread safety (with an important restriction, detailed in docs/dumb.txt), and +the new build system. The silly 'makeall' and 'makecore' scripts are gone. If +you are a GCC user, all you need do now is run 'make' and 'make install', as +for other projects. You don't even have to run a 'fix' script any more! There +are some caveats, which are covered in readme.txt. If you use Microsoft +Visual C++ 6, you no longer need to obtain GCC and GNU Make - there is a +project file just for you. + +Huge thanks go to Steve Terry for testing on Windows XP - about five times - +and to lillo for testing on BeOS and Mac OS X. Thanks also to X-G for testing +on a Windows system that has consistently posed problems for DUMB's old +makefiles. + +There was a bug whereby al_poll_duh() would sometimes cause the music to +resume playing if you called it after al_pause_duh(). Whether this was DUMB's +fault for misusing Allegro's API, or a bug in Allegro, is unclear, but this +release makes it work. + +In one of my projects, I found that my AL_DUH_PLAYER stopped playing when +there were lots of other sound effects. In order to fix this, I programmed +DUMB to set the priority of the stream's voice to 255, the maximum. I also +added al_duh_set_priority(), so you can set the priority yourself if you need +to. + +The resampling code has undergone a transformation. The bad news is that the +linear average code is no longer in use. The good news is that where DUMB's +resamplers used to require three extra samples' worth of memory to be +allocated and initialised, it now copes with just the sample data. And it +does a very good job at bouncing off loop points and otherwise hurtling +around the sample. The resampling code is considerably more complicated, but +the code that uses the resamplers is considerably simpler - and if you +noticed a slight click in some bidirectionally looping samples, you'll be +pleased to know that that click is gone! + +I have also devoted some effort to optimisation. It seemed hopeless for a +while, but then I actually figured out a way of making it faster AND more +accurate at the same time! DUMB is now quite a bit faster than it was, and it +mixes not with 16-bit precision, but with 24-bit precision. (It used 32-bit +integers all along, but the difference is that it now makes use of 256 times +as much of the integer's range.) + +There have been the usual improvements to playback. The last release occurred +rather too soon after I had fixed the XM effect memories; EAx and EBx, fine +volume ramps, had been neglected. These are now handled properly. + +In previous versions of DUMB, muted channels in IT were actually played with +surround sound panning (where the right-hand channel is inverted). This has +been fixed, so muted channels will really be muted now. + +There were also some subtle problems with the way DUMB handled New Note +Actions for IT files. It turned out that, in all releases of DUMB so far, +pitch, filter and panning envelopes and sample vibrato were not being +processed for any note that was forced into the background by a new note on +the same channel! This only affected IT files. Not only has this been fixed, +but envelope interpolation is much more accurate. Long trailing envelope- +driven fade-outs sound a lot better now! + +Since panning and filter envelopes are more precise, extra fields have been +added to the DUMB_IT_CHANNEL_STATE struct, used by +dumb_it_sr_get_channel_state(). These fields hold the 'decimal' parts of the +pan and filter cut-off. See dumb.txt for details. + +Mxx (set channel volume) now correctly only modifies the last note played on +the channel, not any previous notes that have been forced into the background +by New Note Actions, and filter effect processing is now closer to what +Impulse Tracker does. + +The XM loader was slightly flawed and could crash on files containing samples +with out-of-range loop points. One such file was given to me. This has been +fixed. + +Finally, the legal stuff. Julien Cugniere has been added to the list of +copyright owners. He deserves it, for all the work he did on the XM support! +And the licence has been changed. You are no longer required to include a +link to DUMB in a project that uses DUMB; the reasons for this relaxation are +explained in licence.txt. However, the request is still there ... + +As usual, enjoy! + + +********************************************** +*** DUMB v0.9.1, released 19 December 2002 *** +********************************************** + +Hi again! Lots to say this time, so I shall cut right to the chase. + +DUMB now supports Impulse Tracker's low-pass resonant filters! Huge thanks go +to Jeffrey Lim, author of Impulse Tracker, for giving me what information he +still had regarding the algorithm; to cut a long story short, modifying +ModPlug Tracker's source code (which is in the Public Domain) led to an +algorithm whose output matched Impulse Tracker's perfectly. + +Please note that ModPlug Tracker's filters as they stand do not match Impulse +Tracker's, and I have no interest in supporting ModPlug Tracker's variant +(especially not the integer rounding problems). Please see docs/modplug.txt, +new in this release, for details. + +Thanks also go to Fatso Huuskonen for motivating me to add filter support, +and providing me with several great IT files to test it with! + +The other important feature added for this release is click removal. Up until +now, DUMB has generated clicks when cutting notes, starting samples in the +middle, and so on. This version of DUMB will remove any such clicks. Note +that DUMB does not use volume ramps to accomplish this; the algorithm will +not take the bite out of the music! + +In other news, DUMB now supports sample vibrato for IT files, and instrument +vibrato for XM files. A slight bug in New Note Action handling for IT files +has been fixed; Note Fade will not break the sustain loops of the sample and +envelope, as it did before. Tremor handling (Ixy) had a strange bug in it, +which has been fixed. + +Support for XM files has been greatly enhanced. The XM envelope handling new +in the last release contained a huge bug, resulting in notes seeming not to +stop when they should; this has been fixed. Some XM files crashed DUMB, while +others failed to load; these problems have been solved. Effect memories now +work properly for XM and MOD files, to the best of my knowledge. Some other +differences between IT and XM have been accounted for, most notably the +Retrigger Note effects, Rxy and E9x. + +DUMB's sound quality and accuracy are not the only areas that have been +enhanced. The API has been expanded, at last. You can now detect when a +module loops, or make it play through just once. You can ask DUMB to inform +you every time it generates some samples; this is useful for visualisation. +For IT files, you can intercept the MIDI messages generated by Zxx macros, +enabling you to synchronise your game with the music to some extent. (There +is no such method for XM, S3M or MOD files yet; sorry. Also note that the +function will be called before you actually hear the sound; I cannot improve +this until DUMB has its own sound drivers, which won't be for a while.) You +can query the current order and row. Finally, operations like changing the +speed and tempo are now possible, and you can query the playback state on +each channel. + +Some parts of DUMB's API have been deprecated. Simple programs that use +Allegro will be unaffected, but if you get some compiler warnings or errors, +please review docs/deprec.txt. This file explains why those parts of the API +were deprecated, and tells you how to adapt your code; the changes you need +to make are straightforward. Sorry for the inconvenience. + +For various reasons, I have made DUMB's makefiles use different compiler +flags depending on your GCC version (unless you are using MSVC). There is no +elegant way of getting the makefiles to detect when GCC is upgraded. If you +upgrade GCC, you should execute 'make clean' in order to make DUMB detect the +GCC version again. Otherwise you may get some annoying error messages. (It is +wise to do this in any case, so that all the object files are built with the +same GCC version.) + +DUMB's example players have been unified into a single player called +'dumbplay'. The player has been enhanced to display messages when the music +loops, and when XM and MOD files freeze (effect F00; more information on this +in docs/howto.txt). + +Finally, as noted on DUMB's website, the release notes from the last release +were inaccurate. It has been verified that DUMBOGG v0.5 does still work with +that release, and still works with this release. The esoteric DUMBOGG v0.6 +has not been created yet, since DUMBOGG v0.5 still works. + +Please scroll down and read through the indented paragraphs in the notes for +the last release; they are relevant for this release too. + +That's all folks! Until next time. + + +******************************************* +*** DUMB v0.9, released 16 October 2002 *** +******************************************* + +MOD support is here! DUMB now supports all four of the common module formats. +As usual, there have also been some improvements to the way modules are +played back. Most notably, handling of tone portamento in IT files has been +improved a lot, and XM envelopes are now processed correctly. + +The other major change is that DUMB now does a dummy run through each module +on loading. It stores the playback state at thirty-second intervals. It stops +when the module first loops, and then stores the playback time. This results +in a slightly longer load time and a greater memory overhead, but seeking is +faster (to any point before the module first loops) and the length is +calculated! duh_get_length() will return this and is now documented in +docs/howto.txt and docs/dumb.txt. + +DUMB's build process has been changed to use 'mingw' wherever it used +'mingw32' before; some directories have been renamed, and the 'fix' command +you had to run for MinGW has been changed from 'fix mingw32' to 'fix mingw'. + +Last time, I directed you to scroll down and read the notes from a past +release, but ignore this point, and that point applies to something else, and +so on. Did anyone do so? Well, if you're reading this at all, you probably +did. Nevertheless, this time I shall be much less confusing and restate any +relevant information. So the least you can do is read it! + +- If your program ever aborts with exit code 37 while loading an IT file, + PLEASE LET ME KNOW! The IT file in question has a stereo compressed sample + in it, and the format is unspecified for this case (Impulse Tracker itself + doesn't use stereo samples at all). I will need the IT file in question, + and any information you can give me about how the IT file was created (e.g. + what program). (If you don't get to see an exit code, let me know anyway.) + +- If your program ever outputs a line resembling "Inst 01 Env: 0,64 8,32 + 15,48" to stderr while loading an IT file, PLEASE LET ME KNOW! You have an + old IT file (saved by an Impulse Tracker version older than 2.00), and + support for such files is STILL untested. + +- Not all parts of DUMB's API are documented yet. You will find some + functions in dumb.h which are not listed in docs/dumb.txt; the reason is + that these functions still need work and will probably change. If you + really, really want to use them, talk to me first (IRC EFnet #dumb is a + good place for this; see readme.txt for details on using IRC). I intend to + finalise and document the whole of DUMB's API for Version 1.0. + +There have been some changes to the naming conventions in DUMB's undocumented +API. DUMBOGG v0.5 will not work with this and subsequent releases of DUMB; +please upgrade to DUMBOGG v0.6. These changes should not break anything in +your own code, since you didn't use those parts of the API, did you ;) + +There is still a great deal of work to be done before DUMB's API can be +finalised, and thus it will be a while before DUMB v1.0 comes out. It should +be worth the wait. In the meantime, there will be 0.9.x releases with +additional functionality, improved playback, and possibly support for some +extra file formats. + +Finally I should like to offer an apology; there is a strong possibility that +some of DUMB's official API will change in the near future. There will not be +any drastic changes, and the corresponding changes to your source code will +be simple enough. If I didn't make these changes, DUMB's API would start to +become limited, or messy, or both, so it's for the better. I apologise in +advance for this. + +Now scroll down and read the notes for the first r... oh wait, we already did +that. I guess that's it then. You can stop reading now. + +Right after you've read this. + +And this. + +Off you go. + +Bye. + + +******************************************** +*** DUMB v0.8.1, released 11 August 2002 *** +******************************************** + +This is a minor release that fixes a few bugs. One of these bugs, however, +was pretty serious. dumb_register_dat_xm() was never coded! It was prototyped +in aldumb.h, so code would compile, but there would be an unresolved symbol +at the linking stage. This has been fixed. + +Platforms other than Unix did not have a working 'make veryclean' target; +this has been fixed. In addition, the makefiles now use 'xcopy' instead of +'copy', since on some systems GNU Make seems to have trouble calling commands +built in to the shell. + +Contrary to the errata that was on the DUMB website, the makeall.sh and +makecore.sh scripts actually DID install in /usr. This has now been +corrected, and regardless of whether you use these scripts or call make +directly, the files will now be installed to /usr/local by default. + +The XM loader used to treat stereo samples as mono samples with the data for +the right channel positioned after the data for the left channel. This +generally resulted in an unwanted echo effect. This has been fixed. + +When playing XM files, specifying an invalid instrument would cause an old +note on that channel to come back (roughly speaking). Fast Tracker 2 does not +exhibit this behaviour. This has been fixed. + +The GCC makefiles used -mpentium, which is deprecated in gcc 3.x. This was +generating warnings, and has now been fixed. + +In XM files, the length of a sample is stored in bytes. DUMB was assuming +that the length of a 16-bit sample would be even. I had two XM files where +this was not the case, and DUMB was unable to load them. This has been fixed. + +In order to accommodate the extra part of the version number, +DUMB_REVISION_VERSION has been added. DUMB_VERSION has also been added in +order to facilitate checking if the version of DUMB installed is sufficient. +See docs/dumb.txt for details. + +As a last-minute fix, the XM "Break to row" effect is now loaded properly. It +was necessary to convert from binary-coded decimal to hexadecimal (those who +have experience with Fast Tracker 2 will know what I mean). In short, this +means the effect will now work properly when breaking to row 10 or greater. + +DUMB v0.8 had faulty release date constants; DUMB_MONTH and DUMB_DAY were +swapped! For this reason, DUMB_DATE should not be compared against any date +in 2002. This note has been added to docs/dumb.txt and also to dumb.h. + +Please scroll to the end and read the release notes for the first version, +DUMB v0.7. Most of them apply equally to this release. However, the +non-portable code was rewritten for DUMB v0.8, so that point does not apply. +The point about length not being calculated also applies to XM files. + +Enjoy :) + + +**************************************** +*** DUMB v0.8, released 14 June 2002 *** +**************************************** + +Welcome to the second release of DUMB! + +In addition to these notes, please read below the release notes for the +previous version, DUMB v0.7. Most of them apply equally to this release. +However, the non-portable code has been rewritten; DUMB should now port to +big-endian platforms. + +The main improvement in this release of DUMB is the support for XM files. +Enormous thanks go to Julien Cugniere for working on this while I had to +revise for my exams! + +There was a mistake in the makefiles in the last release. The debugging +Allegro interface library was mistakenly named libaldmbd.a instead of +libaldmd.a, meaning you had to compile with -laldmbd, contrary to what the +docs said. Apologies to everyone who lost sleep trying to work out what was +wrong! The reason for using libaldmd.a is to maintain compatibility with +plain DOS, where filenames are limited to eight characters (plus a three- +letter extension). The makefiles have now been changed to match the +information in the docs, so you may have to alter your project files +accordingly. + +The example programs were faulty, and crashed on Windows if they were unable +to load the file. It was also difficult to work out how to exit them (you had +to click the taskbar button that didn't have a window, then press a key). +They have been improved in both these respects. + +I have now added a docs/faq.txt file (Frequently Asked Questions), which is +based on problems and misconceptions people have had with the first release. +Please refer to it before contacting me with problems. + +Thanks to networm for touching up the Unix makefile and writing the +instructions on using it. + +Incidentally, today (Friday 14 June) is the Robinson College May Ball at +Cambridge Uni. God knows why it's called a May Ball if it's in June. I'm not +going myself (72 GBP, and I'd have to wear a suit, ugh), but with all the +noise outside I shall enjoy pumping up the speakers tonight! + + +**************************************** +*** DUMB v0.7, released 2 March 2002 *** +**************************************** + +This is the first release of DUMB, and parts of the library are not +crystallised. Don't let this put you off! Provided you don't try to use any +features that aren't documented in docs/dumb.txt, the library should be rock +solid and you should be able to upgrade more or less without problems. + +Here are some notes on this release: + +- There is some non-portable code in this release of DUMB. It is likely that + the library will fail to load IT files with compressed samples on + big-endian machines such as the Apple Macintosh. + +- If your program ever aborts with exit code 37 while loading an IT file, + PLEASE LET ME KNOW! The IT file in question has a stereo compressed sample + in it, and the format is unspecified for this case (Impulse Tracker itself + doesn't use stereo samples at all). I will need the IT file in question, + and any information you can give me about how the IT file was created (e.g. + what program). (If you don't get to see an exit code, let me know anyway.) + +- If your program ever outputs a line resembling "Inst 01 Env: 0,64 8,32 + 15,48" to stderr while loading an IT file, PLEASE LET ME KNOW! You have an + old IT file (saved by an Impulse Tracker version older than 2.00), and + support for such files is untested. + +- The length of IT and S3M files is not currently calculated. It is just set + to ten minutes. diff --git a/thirdparty/dumb/src/core/atexit.c b/thirdparty/dumb/src/core/atexit.c new file mode 100644 index 000000000..16c6abdb2 --- /dev/null +++ b/thirdparty/dumb/src/core/atexit.c @@ -0,0 +1,71 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * atexit.c - Library Clean-up Management. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +typedef struct DUMB_ATEXIT_PROC +{ + struct DUMB_ATEXIT_PROC *next; + void (*proc)(void); +} +DUMB_ATEXIT_PROC; + + + +static DUMB_ATEXIT_PROC *dumb_atexit_proc = NULL; + + + +int dumb_atexit(void (*proc)(void)) +{ + DUMB_ATEXIT_PROC *dap = dumb_atexit_proc; + + while (dap) { + if (dap->proc == proc) return 0; + dap = dap->next; + } + + dap = malloc(sizeof(*dap)); + + if (!dap) + return -1; + + dap->next = dumb_atexit_proc; + dap->proc = proc; + dumb_atexit_proc = dap; + + return 0; +} + + + +void dumb_exit(void) +{ + while (dumb_atexit_proc) { + DUMB_ATEXIT_PROC *next = dumb_atexit_proc->next; + (*dumb_atexit_proc->proc)(); + free(dumb_atexit_proc); + dumb_atexit_proc = next; + } +} diff --git a/thirdparty/dumb/src/core/duhlen.c b/thirdparty/dumb/src/core/duhlen.c new file mode 100644 index 000000000..4570f1508 --- /dev/null +++ b/thirdparty/dumb/src/core/duhlen.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * duhlen.c - Functions to set and return the / / \ \ + * length of a DUH. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * Note that the length of a DUH is a constant | ' / + * stored in the DUH struct and in the DUH disk \__/ + * format. It will be calculated on loading for + * other formats in which the length is not explicitly stored. Also note that + * it does not necessarily correspond to the length of time for which the DUH + * will generate samples. Rather it represents a suitable point for a player + * such as Winamp to stop, and in any good DUH it will allow for any final + * flourish to fade out and be appreciated. + */ + +#include "dumb.h" +#include "internal/dumb.h" + + + +int32 DUMBEXPORT duh_get_length(DUH *duh) +{ + return duh ? duh->length : 0; +} + + + +void DUMBEXPORT duh_set_length(DUH *duh, int32 length) +{ + if (duh) + duh->length = length; +} diff --git a/thirdparty/dumb/src/core/duhtag.c b/thirdparty/dumb/src/core/duhtag.c new file mode 100644 index 000000000..95664d58b --- /dev/null +++ b/thirdparty/dumb/src/core/duhtag.c @@ -0,0 +1,38 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * duhtag.c - Function to return the tags stored / / \ \ + * in a DUH struct (typically author | < / \_ + * information). | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +const char *DUMBEXPORT duh_get_tag(DUH *duh, const char *key) +{ + int i; + ASSERT(key); + if (!duh || !duh->tag) return NULL; + + for (i = 0; i < duh->n_tags; i++) + if (strcmp(key, duh->tag[i][0]) == 0) + return duh->tag[i][1]; + + return NULL; +} diff --git a/thirdparty/dumb/src/core/dumbfile.c b/thirdparty/dumb/src/core/dumbfile.c new file mode 100644 index 000000000..f0876b752 --- /dev/null +++ b/thirdparty/dumb/src/core/dumbfile.c @@ -0,0 +1,418 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * dumbfile.c - Hookable, strictly sequential / / \ \ + * file input functions. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" + + + +static const DUMBFILE_SYSTEM *the_dfs = NULL; + + + +void DUMBEXPORT register_dumbfile_system(const DUMBFILE_SYSTEM *dfs) +{ + ASSERT(dfs); + ASSERT(dfs->open); + ASSERT(dfs->getc); + ASSERT(dfs->close); + ASSERT(dfs->seek); + ASSERT(dfs->get_size); + the_dfs = dfs; +} + + + +#include "internal/dumbfile.h" + + + +DUMBFILE *DUMBEXPORT dumbfile_open(const char *filename) +{ + DUMBFILE *f; + + ASSERT(the_dfs); + + f = (DUMBFILE *) malloc(sizeof(*f)); + + if (!f) + return NULL; + + f->dfs = the_dfs; + + f->file = (*the_dfs->open)(filename); + + if (!f->file) { + free(f); + return NULL; + } + + f->pos = 0; + + return f; +} + + + +DUMBFILE *DUMBEXPORT dumbfile_open_ex(void *file, const DUMBFILE_SYSTEM *dfs) +{ + DUMBFILE *f; + + ASSERT(dfs); + ASSERT(dfs->getc); + ASSERT(file); + + f = (DUMBFILE *) malloc(sizeof(*f)); + + if (!f) { + if (dfs->close) + (*dfs->close)(file); + return NULL; + } + + f->dfs = dfs; + f->file = file; + + f->pos = 0; + + return f; +} + + + +int32 DUMBEXPORT dumbfile_pos(DUMBFILE *f) +{ + ASSERT(f); + + return f->pos; +} + + + +int DUMBEXPORT dumbfile_skip(DUMBFILE *f, long n) +{ + int rv; + + ASSERT(f); + ASSERT(n >= 0); + + if (f->pos < 0) + return -1; + + f->pos += n; + + if (f->dfs->skip) { + rv = (*f->dfs->skip)(f->file, n); + if (rv) { + f->pos = -1; + return rv; + } + } else { + while (n) { + rv = (*f->dfs->getc)(f->file); + if (rv < 0) { + f->pos = -1; + return rv; + } + n--; + } + } + + return 0; +} + + + +int DUMBEXPORT dumbfile_getc(DUMBFILE *f) +{ + int rv; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + rv = (*f->dfs->getc)(f->file); + + if (rv < 0) { + f->pos = -1; + return rv; + } + + f->pos++; + + return rv; +} + + + +int DUMBEXPORT dumbfile_igetw(DUMBFILE *f) +{ + int l, h; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + l = (*f->dfs->getc)(f->file); + if (l < 0) { + f->pos = -1; + return l; + } + + h = (*f->dfs->getc)(f->file); + if (h < 0) { + f->pos = -1; + return h; + } + + f->pos += 2; + + return l | (h << 8); +} + + + +int DUMBEXPORT dumbfile_mgetw(DUMBFILE *f) +{ + int l, h; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + h = (*f->dfs->getc)(f->file); + if (h < 0) { + f->pos = -1; + return h; + } + + l = (*f->dfs->getc)(f->file); + if (l < 0) { + f->pos = -1; + return l; + } + + f->pos += 2; + + return l | (h << 8); +} + + + +int32 DUMBEXPORT dumbfile_igetl(DUMBFILE *f) +{ + uint32 rv, b; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + rv = (*f->dfs->getc)(f->file); + if ((sint32)rv < 0) { + f->pos = -1; + return rv; + } + + b = (*f->dfs->getc)(f->file); + if ((sint32)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 8; + + b = (*f->dfs->getc)(f->file); + if ((sint32)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 16; + + b = (*f->dfs->getc)(f->file); + if ((sint32)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 24; + + f->pos += 4; + + return rv; +} + + + +int32 DUMBEXPORT dumbfile_mgetl(DUMBFILE *f) +{ + uint32 rv, b; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + rv = (*f->dfs->getc)(f->file); + if ((sint32)rv < 0) { + f->pos = -1; + return rv; + } + rv <<= 24; + + b = (*f->dfs->getc)(f->file); + if ((sint32)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 16; + + b = (*f->dfs->getc)(f->file); + if ((sint32)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 8; + + b = (*f->dfs->getc)(f->file); + if ((sint32)b < 0) { + f->pos = -1; + return b; + } + rv |= b; + + f->pos += 4; + + return rv; +} + + + +uint32 DUMBEXPORT dumbfile_cgetul(DUMBFILE *f) +{ + uint32 rv = 0; + int v; + + do { + v = dumbfile_getc(f); + + if (v < 0) + return v; + + rv <<= 7; + rv |= v & 0x7F; + } while (v & 0x80); + + return rv; +} + + + +sint32 DUMBEXPORT dumbfile_cgetsl(DUMBFILE *f) +{ + uint32 rv = dumbfile_cgetul(f); + + if (f->pos < 0) + return rv; + + return (rv >> 1) | (rv << 31); +} + + + +int32 DUMBEXPORT dumbfile_getnc(char *ptr, int32 n, DUMBFILE *f) +{ + int32 rv; + + ASSERT(f); + ASSERT(n >= 0); + + if (f->pos < 0) + return -1; + + if (f->dfs->getnc) { + rv = (*f->dfs->getnc)(ptr, n, f->file); + if (rv < n) { + f->pos = -1; + return MAX(rv, 0); + } + } else { + for (rv = 0; rv < n; rv++) { + int c = (*f->dfs->getc)(f->file); + if (c < 0) { + f->pos = -1; + return rv; + } + *ptr++ = c; + } + } + + f->pos += rv; + + return rv; +} + + + +int DUMBEXPORT dumbfile_seek(DUMBFILE *f, long n, int origin) +{ + switch ( origin ) + { + case DFS_SEEK_CUR: n += f->pos; break; + case DFS_SEEK_END: n += (*f->dfs->get_size)(f->file); break; + } + f->pos = n; + return (*f->dfs->seek)(f->file, n); +} + + + +int32 DUMBEXPORT dumbfile_get_size(DUMBFILE *f) +{ + return (*f->dfs->get_size)(f->file); +} + + + +int DUMBEXPORT dumbfile_error(DUMBFILE *f) +{ + ASSERT(f); + + return f->pos < 0; +} + + + +int DUMBEXPORT dumbfile_close(DUMBFILE *f) +{ + int rv; + + ASSERT(f); + + rv = f->pos < 0; + + if (f->dfs->close) + (*f->dfs->close)(f->file); + + free(f); + + return rv; +} diff --git a/thirdparty/dumb/src/core/loadduh.c b/thirdparty/dumb/src/core/loadduh.c new file mode 100644 index 000000000..2891298f9 --- /dev/null +++ b/thirdparty/dumb/src/core/loadduh.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadduh.c - Code to read a DUH from a file, / / \ \ + * opening and closing the file for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/dumb.h" + + + +/* load_duh(): loads a .duh file, returning a pointer to a DUH struct. + * When you have finished with it, you must pass the pointer to unload_duh() + * so that the memory can be freed. + */ +DUH *DUMBEXPORT load_duh(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = read_duh(f); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/core/makeduh.c b/thirdparty/dumb/src/core/makeduh.c new file mode 100644 index 000000000..1c2695cfb --- /dev/null +++ b/thirdparty/dumb/src/core/makeduh.c @@ -0,0 +1,151 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * makeduh.c - Function to construct a DUH from / / \ \ + * its components. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +static DUH_SIGNAL *make_signal(DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata) +{ + DUH_SIGNAL *signal; + + ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer)); + ASSERT(desc->sigrenderer_generate_samples && desc->sigrenderer_get_current_sample); + + signal = malloc(sizeof(*signal)); + + if (!signal) { + if (desc->unload_sigdata) + if (sigdata) + (*desc->unload_sigdata)(sigdata); + return NULL; + } + + signal->desc = desc; + signal->sigdata = sigdata; + + return signal; +} + + + +DUH *make_duh( + int32 length, + int n_tags, + const char *const tags[][2], + int n_signals, + DUH_SIGTYPE_DESC *desc[], + sigdata_t *sigdata[] +) +{ + DUH *duh = malloc(sizeof(*duh)); + int i; + int fail; + + if (duh) { + duh->n_signals = n_signals; + + duh->signal = malloc(n_signals * sizeof(*duh->signal)); + + if (!duh->signal) { + free(duh); + duh = NULL; + } + } + + if (!duh) { + for (i = 0; i < n_signals; i++) + if (desc[i]->unload_sigdata) + if (sigdata[i]) + (*desc[i]->unload_sigdata)(sigdata[i]); + return NULL; + } + + duh->n_tags = 0; + duh->tag = NULL; + + fail = 0; + + for (i = 0; i < n_signals; i++) { + duh->signal[i] = make_signal(desc[i], sigdata[i]); + if (!duh->signal[i]) + fail = 1; + } + + if (fail) { + unload_duh(duh); + return NULL; + } + + duh->length = length; + + { + int mem = n_tags * 2; /* account for NUL terminators here */ + char *ptr; + + for (i = 0; i < n_tags; i++) + mem += (int)(strlen(tags[i][0]) + strlen(tags[i][1])); + + if (mem <= 0) return duh; + + duh->tag = malloc(n_tags * sizeof(*duh->tag)); + if (!duh->tag) return duh; + duh->tag[0][0] = malloc(mem); + if (!duh->tag[0][0]) { + free(duh->tag); + duh->tag = NULL; + return duh; + } + duh->n_tags = n_tags; + ptr = duh->tag[0][0]; + for (i = 0; i < n_tags; i++) { + duh->tag[i][0] = ptr; + strcpy(ptr, tags[i][0]); + ptr += strlen(tags[i][0]) + 1; + duh->tag[i][1] = ptr; + strcpy(ptr, tags[i][1]); + ptr += strlen(tags[i][1]) + 1; + } + } + + return duh; +} + +int DUMBEXPORT duh_add_signal(DUH *duh, DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata) +{ + DUH_SIGNAL **signal; + + if ( !duh || !desc || !sigdata ) return -1; + + signal = ( DUH_SIGNAL ** ) realloc( duh->signal, ( duh->n_signals + 1 ) * sizeof( *duh->signal ) ); + if ( !signal ) return -1; + duh->signal = signal; + + memmove( signal + 1, signal, duh->n_signals * sizeof( *signal ) ); + duh->n_signals++; + + signal[ 0 ] = make_signal( desc, sigdata ); + if ( !signal[ 0 ] ) return -1; + + return 0; +} diff --git a/thirdparty/dumb/src/core/rawsig.c b/thirdparty/dumb/src/core/rawsig.c new file mode 100644 index 000000000..1651d06bc --- /dev/null +++ b/thirdparty/dumb/src/core/rawsig.c @@ -0,0 +1,58 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * rawsig.c - Function to retrieve raw signal / / \ \ + * data from a DUH provided you know | < / \_ + * what type of signal it is. | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +/* You have to specify the type of sigdata, proving you know what to do with + * the pointer. If you get it wrong, you can expect NULL back. + */ +sigdata_t *DUMBEXPORT duh_get_raw_sigdata(DUH *duh, int sig, int32 type) +{ + int i; + DUH_SIGNAL *signal; + + if (!duh) return NULL; + + if ( sig >= 0 ) + { + if ((unsigned int)sig >= (unsigned int)duh->n_signals) return NULL; + + signal = duh->signal[sig]; + + if (signal && signal->desc->type == type) + return signal->sigdata; + } + else + { + for ( i = 0; i < duh->n_signals; i++ ) + { + signal = duh->signal[i]; + + if (signal && signal->desc->type == type) + return signal->sigdata; + } + } + + return NULL; +} diff --git a/thirdparty/dumb/src/core/readduh.c b/thirdparty/dumb/src/core/readduh.c new file mode 100644 index 000000000..4c40c98e9 --- /dev/null +++ b/thirdparty/dumb/src/core/readduh.c @@ -0,0 +1,107 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readduh.c - Code to read a DUH from an open / / \ \ + * file. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +static DUH_SIGNAL *read_signal(DUH *duh, DUMBFILE *f) +{ + DUH_SIGNAL *signal; + int32 type; + + signal = malloc(sizeof(*signal)); + + if (!signal) + return NULL; + + type = dumbfile_mgetl(f); + if (dumbfile_error(f)) { + free(signal); + return NULL; + } + + signal->desc = _dumb_get_sigtype_desc(type); + if (!signal->desc) { + free(signal); + return NULL; + } + + if (signal->desc->load_sigdata) { + signal->sigdata = (*signal->desc->load_sigdata)(duh, f); + if (!signal->sigdata) { + free(signal); + return NULL; + } + } else + signal->sigdata = NULL; + + return signal; +} + + + +/* read_duh(): reads a DUH from an already open DUMBFILE, and returns its + * pointer, or null on error. The file is not closed. + */ +DUH *DUMBEXPORT read_duh(DUMBFILE *f) +{ + DUH *duh; + int i; + + if (dumbfile_mgetl(f) != DUH_SIGNATURE) + return NULL; + + duh = malloc(sizeof(*duh)); + if (!duh) + return NULL; + + duh->length = dumbfile_igetl(f); + if (dumbfile_error(f) || duh->length <= 0) { + free(duh); + return NULL; + } + + duh->n_signals = dumbfile_igetl(f); + if (dumbfile_error(f) || duh->n_signals <= 0) { + free(duh); + return NULL; + } + + duh->signal = malloc(sizeof(*duh->signal) * duh->n_signals); + if (!duh->signal) { + free(duh); + return NULL; + } + + for (i = 0; i < duh->n_signals; i++) + duh->signal[i] = NULL; + + for (i = 0; i < duh->n_signals; i++) { + if (!(duh->signal[i] = read_signal(duh, f))) { + unload_duh(duh); + return NULL; + } + } + + return duh; +} diff --git a/thirdparty/dumb/src/core/register.c b/thirdparty/dumb/src/core/register.c new file mode 100644 index 000000000..7d7cce533 --- /dev/null +++ b/thirdparty/dumb/src/core/register.c @@ -0,0 +1,104 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * register.c - Signal type registration. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +static DUH_SIGTYPE_DESC_LINK *sigtype_desc = NULL; +static DUH_SIGTYPE_DESC_LINK **sigtype_desc_tail = &sigtype_desc; + + + +/* destroy_sigtypes(): frees all memory allocated while registering signal + * types. This function is set up to be called by dumb_exit(). + */ +static void destroy_sigtypes(void) +{ + DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc, *next; + sigtype_desc = NULL; + sigtype_desc_tail = &sigtype_desc; + + while (desc_link) { + next = desc_link->next; + free(desc_link); + desc_link = next; + } +} + + + +/* dumb_register_sigtype(): registers a new signal type with DUMB. The signal + * type is identified by a four-character string (e.g. "WAVE"), which you can + * encode using the the DUMB_ID() macro (e.g. DUMB_ID('W','A','V','E')). The + * signal's behaviour is defined by four functions, whose pointers you pass + * here. See the documentation for details. + * + * If a DUH tries to use a signal that has not been registered using this + * function, then the library will fail to load the DUH. + */ +void DUMBEXPORT dumb_register_sigtype(DUH_SIGTYPE_DESC *desc) +{ + DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc; + + ASSERT((desc->load_sigdata && desc->unload_sigdata) || (!desc->load_sigdata && !desc->unload_sigdata)); + ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer)); + ASSERT(desc->sigrenderer_generate_samples && desc->sigrenderer_get_current_sample); + + if (desc_link) { + do { + if (desc_link->desc->type == desc->type) { + desc_link->desc = desc; + return; + } + desc_link = desc_link->next; + } while (desc_link); + } else + dumb_atexit(&destroy_sigtypes); + + desc_link = *sigtype_desc_tail = malloc(sizeof(DUH_SIGTYPE_DESC_LINK)); + + if (!desc_link) + return; + + desc_link->next = NULL; + sigtype_desc_tail = &desc_link->next; + + desc_link->desc = desc; +} + + + +/* _dumb_get_sigtype_desc(): searches the registered functions for a signal + * type matching the parameter. If such a sigtype is found, it returns a + * pointer to a sigtype descriptor containing the necessary functions to + * manage the signal. If none is found, it returns NULL. + */ +DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(int32 type) +{ + DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc; + + while (desc_link && desc_link->desc->type != type) + desc_link = desc_link->next; + + return desc_link ? desc_link->desc : NULL; +} diff --git a/thirdparty/dumb/src/core/rendduh.c b/thirdparty/dumb/src/core/rendduh.c new file mode 100644 index 000000000..71f6201d2 --- /dev/null +++ b/thirdparty/dumb/src/core/rendduh.c @@ -0,0 +1,184 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * rendduh.c - Functions for rendering a DUH into / / \ \ + * an end-user sample format. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +/* On the x86, we can use some tricks to speed stuff up */ +#if (defined _MSC_VER) || (defined __DJGPP__) || (defined __MINGW__) +// Can't we detect Linux and other x86 platforms here? :/ + +#define FAST_MID(var, min, max) { \ + var -= (min); \ + var &= (~var) >> (sizeof(var) * CHAR_BIT - 1); \ + var += (min); \ + var -= (max); \ + var &= var >> (sizeof(var) * CHAR_BIT - 1); \ + var += (max); \ +} + +#define CONVERT8(src, pos, signconv) { \ + signed int f = (src + 0x8000) >> 16; \ + FAST_MID(f, -128, 127); \ + ((char*)sptr)[pos] = (char)f ^ signconv; \ +} + +#define CONVERT16(src, pos, signconv) { \ + signed int f = (src + 0x80) >> 8; \ + FAST_MID(f, -32768, 32767); \ + ((short*)sptr)[pos] = (short)(f ^ signconv); \ +} + +#else + +#define CONVERT8(src, pos, signconv) \ +{ \ + signed int f = (src + 0x8000) >> 16; \ + f = MID(-128, f, 127); \ + ((char *)sptr)[pos] = (char)f ^ signconv; \ +} + + + +#define CONVERT16(src, pos, signconv) \ +{ \ + signed int f = (src + 0x80) >> 8; \ + f = MID(-32768, f, 32767); \ + ((short *)sptr)[pos] = (short)(f ^ signconv); \ +} + +#endif + + + +/* DEPRECATED */ +DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, int32 pos) +{ + return duh_start_sigrenderer(duh, 0, n_channels, pos); +} + + + +int32 DUMBEXPORT duh_render( + DUH_SIGRENDERER *sigrenderer, + int bits, int unsign, + float volume, float delta, + int32 size, void *sptr +) +{ + int32 n; + + sample_t **sampptr; + + int n_channels; + + ASSERT(bits == 8 || bits == 16); + ASSERT(sptr); + + if (!sigrenderer) + return 0; + + n_channels = duh_sigrenderer_get_n_channels(sigrenderer); + + ASSERT(n_channels > 0); + /* This restriction will be removed when need be. At the moment, tightly + * optimised loops exist for exactly one or two channels. + */ + ASSERT(n_channels <= 2); + + sampptr = allocate_sample_buffer(n_channels, size); + + if (!sampptr) + return 0; + + dumb_silence(sampptr[0], n_channels * size); + + size = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, sampptr); + + if (bits == 16) { + int signconv = unsign ? 0x8000 : 0x0000; + + for (n = 0; n < size * n_channels; n++) { + CONVERT16(sampptr[0][n], n, signconv); + } + } else { + char signconv = unsign ? 0x80 : 0x00; + + for (n = 0; n < size * n_channels; n++) { + CONVERT8(sampptr[0][n], n, signconv); + } + } + + destroy_sample_buffer(sampptr); + + return size; +} + + + +/* DEPRECATED */ +int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr) +{ + return duh_sigrenderer_get_n_channels(dr); +} + + + +/* DEPRECATED */ +int32 duh_renderer_get_position(DUH_SIGRENDERER *dr) +{ + return duh_sigrenderer_get_position(dr); +} + + + +/* DEPRECATED */ +void duh_end_renderer(DUH_SIGRENDERER *dr) +{ + duh_end_sigrenderer(dr); +} + + + +/* DEPRECATED */ +DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer) +{ + return sigrenderer; +} + + + +/* DEPRECATED */ +DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr) +{ + return dr; +} + + + +/* DEPRECATED */ +DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr) +{ + return dr; +} diff --git a/thirdparty/dumb/src/core/rendsig.c b/thirdparty/dumb/src/core/rendsig.c new file mode 100644 index 000000000..1e6fa1f88 --- /dev/null +++ b/thirdparty/dumb/src/core/rendsig.c @@ -0,0 +1,348 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * rendsig.c - Wrappers to render samples from / / \ \ + * the signals in a DUH. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +struct DUH_SIGRENDERER +{ + DUH_SIGTYPE_DESC *desc; + + sigrenderer_t *sigrenderer; + + int n_channels; + + int32 pos; + int subpos; + + DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback; + void *callback_data; +}; + + + +DUH_SIGRENDERER *DUMBEXPORT duh_start_sigrenderer(DUH *duh, int sig, int n_channels, int32 pos) +{ + DUH_SIGRENDERER *sigrenderer; + + DUH_SIGNAL *signal; + DUH_START_SIGRENDERER proc; + + /* [RH] Mono destination mixers are disabled. */ + if (n_channels != 2) + return NULL; + + if (!duh) + return NULL; + + if ((unsigned int)sig >= (unsigned int)duh->n_signals) + return NULL; + + signal = duh->signal[sig]; + if (!signal) + return NULL; + + sigrenderer = malloc(sizeof(*sigrenderer)); + if (!sigrenderer) + return NULL; + + sigrenderer->desc = signal->desc; + + proc = sigrenderer->desc->start_sigrenderer; + + if (proc) { + duh->signal[sig] = NULL; + sigrenderer->sigrenderer = (*proc)(duh, signal->sigdata, n_channels, pos); + duh->signal[sig] = signal; + + if (!sigrenderer->sigrenderer) { + free(sigrenderer); + return NULL; + } + } else + sigrenderer->sigrenderer = NULL; + + sigrenderer->n_channels = n_channels; + + sigrenderer->pos = pos; + sigrenderer->subpos = 0; + + sigrenderer->callback = NULL; + + return sigrenderer; +} + + +#ifdef DUMB_DECLARE_DEPRECATED +#include +void duh_sigrenderer_set_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_CALLBACK callback, void *data +) +{ + (void)sigrenderer; + (void)callback; + (void)data; + /*fprintf(stderr, + "Call to deprecated function duh_sigrenderer_set_callback(). The callback\n" + "was not installed. See dumb/docs/deprec.txt for how to fix this.\n");*/ +} + + + +void duh_sigrenderer_set_analyser_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data +) +{ + (void)sigrenderer; + (void)callback; + (void)data; + fprintf(stderr, + "Call to deprecated function duh_sigrenderer_set_analyser_callback(). The\n" + "callback was not installed. See dumb/docs/deprec.txt for how to fix this.\n"); +} +#endif + + +void duh_sigrenderer_set_sample_analyser_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data +) +{ + if (sigrenderer) { + sigrenderer->callback = callback; + sigrenderer->callback_data = data; + } +} + + + +int DUMBEXPORT duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer) +{ + return sigrenderer ? sigrenderer->n_channels : 0; +} + + + +int32 DUMBEXPORT duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer) +{ + return sigrenderer ? sigrenderer->pos : -1; +} + + + +void DUMBEXPORT duh_sigrenderer_set_sigparam( + DUH_SIGRENDERER *sigrenderer, + unsigned char id, int32 value +) +{ + DUH_SIGRENDERER_SET_SIGPARAM proc; + + if (!sigrenderer) return; + + proc = sigrenderer->desc->sigrenderer_set_sigparam; + if (proc) + (*proc)(sigrenderer->sigrenderer, id, value); + else + TRACE("Parameter #%d = %d for signal %c%c%c%c, which does not take parameters.\n", + (int)id, + value, + (int)(sigrenderer->desc->type >> 24), + (int)(sigrenderer->desc->type >> 16), + (int)(sigrenderer->desc->type >> 8), + (int)(sigrenderer->desc->type)); +} + + + +int32 DUMBEXPORT duh_sigrenderer_generate_samples( + DUH_SIGRENDERER *sigrenderer, + double volume, double delta, + int32 size, sample_t **samples +) +{ + int32 rendered; + LONG_LONG t; + + if (!sigrenderer) return 0; + + rendered = (*sigrenderer->desc->sigrenderer_generate_samples) + (sigrenderer->sigrenderer, volume, delta, size, samples); + + if (rendered) { + if (sigrenderer->callback) + (*sigrenderer->callback)(sigrenderer->callback_data, + (const sample_t *const *)samples, sigrenderer->n_channels, rendered); + + t = sigrenderer->subpos + (LONG_LONG)(delta * 65536.0 + 0.5) * rendered; + + sigrenderer->pos += (int32)(t >> 16); + sigrenderer->subpos = (int)t & 65535; + } + + return rendered; +} + + + +/* DEPRECATED */ +int32 duh_sigrenderer_get_samples( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + int32 size, sample_t **samples +) +{ + sample_t **s; + int32 rendered; + int32 i; + int j; + if (!samples) return duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, NULL); + s = allocate_sample_buffer(sigrenderer->n_channels, size); + if (!s) return 0; + dumb_silence(s[0], sigrenderer->n_channels * size); + rendered = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, s); + for (j = 0; j < sigrenderer->n_channels; j++) + for (i = 0; i < rendered; i++) + samples[j][i] += s[0][i*sigrenderer->n_channels+j]; + destroy_sample_buffer(s); + return rendered; +} + + + +/* DEPRECATED */ +int32 duh_render_signal( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + int32 size, sample_t **samples +) +{ + sample_t **s; + int32 rendered; + int32 i; + int j; + if (!samples) return duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, NULL); + s = allocate_sample_buffer(sigrenderer->n_channels, size); + if (!s) return 0; + dumb_silence(s[0], sigrenderer->n_channels * size); + rendered = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, s); + for (j = 0; j < sigrenderer->n_channels; j++) + for (i = 0; i < rendered; i++) + samples[j][i] += s[0][i*sigrenderer->n_channels+j] >> 8; + destroy_sample_buffer(s); + return rendered; +} + + + +void DUMBEXPORT duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples) +{ + if (sigrenderer) + (*sigrenderer->desc->sigrenderer_get_current_sample)(sigrenderer->sigrenderer, volume, samples); +} + + + +void DUMBEXPORT duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer) +{ + if (sigrenderer) { + if (sigrenderer->desc->end_sigrenderer) + if (sigrenderer->sigrenderer) + (*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer); + + free(sigrenderer); + } +} + + + +DUH_SIGRENDERER *DUMBEXPORT duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, int32 pos) +{ + DUH_SIGRENDERER *sigrenderer; + + if (desc->start_sigrenderer && !vsigrenderer) return NULL; + + sigrenderer = malloc(sizeof(*sigrenderer)); + if (!sigrenderer) { + if (desc->end_sigrenderer) + if (vsigrenderer) + (*desc->end_sigrenderer)(vsigrenderer); + return NULL; + } + + sigrenderer->desc = desc; + sigrenderer->sigrenderer = vsigrenderer; + + sigrenderer->n_channels = n_channels; + + sigrenderer->pos = pos; + sigrenderer->subpos = 0; + + sigrenderer->callback = NULL; + + return sigrenderer; +} + + + +sigrenderer_t *DUMBEXPORT duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, int32 type) +{ + if (sigrenderer && sigrenderer->desc->type == type) + return sigrenderer->sigrenderer; + + return NULL; +} + + + +#if 0 +// This function is disabled because we don't know whether we want to destroy +// the sigrenderer if the type doesn't match. We don't even know if we need +// the function at all. Who would want to keep an IT_SIGRENDERER (for +// instance) without keeping the DUH_SIGRENDERER? +sigrenderer_t *duh_decompose_to_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, int32 type) +{ + if (sigrenderer && sigrenderer->desc->type == type) { + + + + if (sigrenderer) { + if (sigrenderer->desc->end_sigrenderer) + if (sigrenderer->sigrenderer) + (*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer); + + free(sigrenderer); + } + + + + + + + return sigrenderer->sigrenderer; + } + + return NULL; +} +#endif diff --git a/thirdparty/dumb/src/core/unload.c b/thirdparty/dumb/src/core/unload.c new file mode 100644 index 000000000..6495ab1f4 --- /dev/null +++ b/thirdparty/dumb/src/core/unload.c @@ -0,0 +1,64 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * unload.c - Code to free a DUH from memory. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +static void destroy_signal(DUH_SIGNAL *signal) +{ + if (signal) { + if (signal->desc) + if (signal->desc->unload_sigdata) + if (signal->sigdata) + (*signal->desc->unload_sigdata)(signal->sigdata); + + free(signal); + } +} + + + +/* unload_duh(): destroys a DUH struct. You must call this for every DUH + * struct created, when you've finished with it. + */ +void DUMBEXPORT unload_duh(DUH *duh) +{ + int i; + + if (duh) { + if (duh->signal) { + for (i = 0; i < duh->n_signals; i++) + destroy_signal(duh->signal[i]); + + free(duh->signal); + } + + if (duh->tag) { + if (duh->tag[0][0]) + free(duh->tag[0][0]); + free(duh->tag); + } + + free(duh); + } +} diff --git a/thirdparty/dumb/src/helpers/barray.c b/thirdparty/dumb/src/helpers/barray.c new file mode 100644 index 000000000..71e8dc352 --- /dev/null +++ b/thirdparty/dumb/src/helpers/barray.c @@ -0,0 +1,189 @@ +#include "internal/barray.h" + +#include + + +void * bit_array_create(size_t size) +{ + size_t bsize = ((size + 7) >> 3) + sizeof(size_t); + void * ret = calloc(1, bsize); + if (ret) *(size_t *)ret = size; + return ret; +} + +void bit_array_destroy(void * array) +{ + if (array) free(array); +} + +void * bit_array_dup(void * array) +{ + if (array) + { + size_t * size = (size_t *) array; + size_t bsize = ((*size + 7) >> 3) + sizeof(*size); + void * ret = malloc(bsize); + if (ret) memcpy(ret, array, bsize); + return ret; + } + return NULL; +} + +void bit_array_reset(void * array) +{ + if (array) + { + size_t * size = (size_t *) array; + size_t bsize = (*size + 7) >> 3; + memset(size + 1, 0, bsize); + } +} + + +void bit_array_set(void * array, size_t bit) +{ + if (array) + { + size_t * size = (size_t *) array; + if (bit < *size) + { + unsigned char * ptr = (unsigned char *)(size + 1); + ptr[bit >> 3] |= (1U << (bit & 7)); + } + } +} + +void bit_array_set_range(void * array, size_t bit, size_t count) +{ + if (array && count) + { + size_t * size = (size_t *) array; + if (bit < *size) + { + unsigned char * ptr = (unsigned char *)(size + 1); + size_t i; + for (i = bit; i < *size && i < bit + count; ++i) + ptr[i >> 3] |= (1U << (i & 7)); + } + } +} + +int bit_array_test(void * array, size_t bit) +{ + if (array) + { + size_t * size = (size_t *) array; + if (bit < *size) + { + unsigned char * ptr = (unsigned char *)(size + 1); + if (ptr[bit >> 3] & (1U << (bit & 7))) + { + return 1; + } + } + } + return 0; +} + +int bit_array_test_range(void * array, size_t bit, size_t count) +{ + if (array) + { + size_t * size = (size_t *) array; + if (bit < *size) + { + unsigned char * ptr = (unsigned char *)(size + 1); + if ((bit & 7) && (count > 8)) + { + while ((bit < *size) && count && (bit & 7)) + { + if (ptr[bit >> 3] & (1U << (bit & 7))) return 1; + bit++; + count--; + } + } + if (!(bit & 7)) + { + while (((*size - bit) >= 8) && (count >= 8)) + { + if (ptr[bit >> 3]) return 1; + bit += 8; + count -= 8; + } + } + while ((bit < *size) && count) + { + if (ptr[bit >> 3] & (1U << (bit & 7))) return 1; + bit++; + count--; + } + } + } + return 0; +} + +void bit_array_clear(void * array, size_t bit) +{ + if (array) + { + size_t * size = (size_t *) array; + if (bit < *size) + { + unsigned char * ptr = (unsigned char *)(size + 1); + ptr[bit >> 3] &= ~(1U << (bit & 7)); + } + } +} + +void bit_array_clear_range(void * array, size_t bit, size_t count) +{ + if (array && count) + { + size_t * size = (size_t *) array; + if (bit < *size) + { + unsigned char * ptr = (unsigned char *)(size + 1); + size_t i; + for (i = bit; i < *size && i < bit + count; ++i) + ptr[i >> 3] &= ~(1U << (i & 7)); + } + } +} + +void bit_array_merge(void * dest, void * source, size_t offset) +{ + if (dest && source) + { + size_t * dsize = (size_t *) dest; + size_t * ssize = (size_t *) source; + size_t soffset = 0; + while (offset < *dsize && soffset < *ssize) + { + if (bit_array_test(source, soffset)) + { + bit_array_set(dest, offset); + } + soffset++; + offset++; + } + } +} + +void bit_array_mask(void * dest, void * source, size_t offset) +{ + if (dest && source) + { + size_t * dsize = (size_t *) dest; + size_t * ssize = (size_t *) source; + size_t soffset = 0; + while (offset < *dsize && soffset < *ssize) + { + if (bit_array_test(source, soffset)) + { + bit_array_clear(dest, offset); + } + soffset++; + offset++; + } + } +} diff --git a/thirdparty/dumb/src/helpers/clickrem.c b/thirdparty/dumb/src/helpers/clickrem.c new file mode 100644 index 000000000..e1db4a663 --- /dev/null +++ b/thirdparty/dumb/src/helpers/clickrem.c @@ -0,0 +1,306 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * clickrem.c - Click removal helpers. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include "dumb.h" + + + +typedef struct DUMB_CLICK DUMB_CLICK; + + +struct DUMB_CLICK_REMOVER +{ + DUMB_CLICK *click; + int n_clicks; + + int offset; + + DUMB_CLICK *free_clicks; +}; + + +struct DUMB_CLICK +{ + DUMB_CLICK *next; + int32 pos; + sample_t step; +}; + + +static DUMB_CLICK *alloc_click(DUMB_CLICK_REMOVER *cr) +{ + if (cr->free_clicks != NULL) + { + DUMB_CLICK *click = cr->free_clicks; + cr->free_clicks = click->next; + return click; + } + return malloc(sizeof(DUMB_CLICK)); +} + +static void free_click(DUMB_CLICK_REMOVER *cr, DUMB_CLICK *cl) +{ + cl->next = cr->free_clicks; + cr->free_clicks = cl; +} + +DUMB_CLICK_REMOVER *DUMBEXPORT dumb_create_click_remover(void) +{ + DUMB_CLICK_REMOVER *cr = malloc(sizeof(*cr)); + if (!cr) return NULL; + + cr->click = NULL; + cr->n_clicks = 0; + + cr->offset = 0; + cr->free_clicks = NULL; + + return cr; +} + + + +void DUMBEXPORT dumb_record_click(DUMB_CLICK_REMOVER *cr, int32 pos, sample_t step) +{ + DUMB_CLICK *click; + + ASSERT(pos >= 0); + + if (!cr || !step) return; + + if (pos == 0) { + cr->offset -= step; + return; + } + + click = alloc_click(cr); + if (!click) return; + + click->pos = pos; + click->step = step; + + click->next = cr->click; + cr->click = click; + cr->n_clicks++; +} + + + +static DUMB_CLICK *dumb_click_mergesort(DUMB_CLICK *click, int n_clicks) +{ + int i; + DUMB_CLICK *c1, *c2, **cp; + + if (n_clicks <= 1) return click; + + /* Split the list into two */ + c1 = click; + cp = &c1; + for (i = 0; i < n_clicks; i += 2) cp = &(*cp)->next; + c2 = *cp; + *cp = NULL; + + /* Sort the sublists */ + c1 = dumb_click_mergesort(c1, (n_clicks + 1) >> 1); + c2 = dumb_click_mergesort(c2, n_clicks >> 1); + + /* Merge them */ + cp = &click; + while (c1 && c2) { + if (c1->pos > c2->pos) { + *cp = c2; + c2 = c2->next; + } else { + *cp = c1; + c1 = c1->next; + } + cp = &(*cp)->next; + } + if (c2) + *cp = c2; + else + *cp = c1; + + return click; +} + + + +void DUMBEXPORT dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, int32 length, int step, double halflife) +{ + DUMB_CLICK *click; + int32 pos = 0; + int offset; + int factor; + + if (!cr) return; + + factor = (int)floor(pow(0.5, 1.0/halflife) * (1U << 31)); + + click = dumb_click_mergesort(cr->click, cr->n_clicks); + cr->click = NULL; + cr->n_clicks = 0; + + length *= step; + + while (click) { + DUMB_CLICK *next = click->next; + int end = click->pos * step; + ASSERT(end <= length); + offset = cr->offset; + if (offset < 0) { + offset = -offset; + while (pos < end) { + samples[pos] -= offset; + offset = (int)(((LONG_LONG)(offset << 1) * factor) >> 32); + pos += step; + } + offset = -offset; + } else { + while (pos < end) { + samples[pos] += offset; + offset = (int)(((LONG_LONG)(offset << 1) * factor) >> 32); + pos += step; + } + } + cr->offset = offset - click->step; + free_click(cr, click); + click = next; + } + + offset = cr->offset; + if (offset < 0) { + offset = -offset; + while (pos < length) { + samples[pos] -= offset; + offset = (int)((LONG_LONG)(offset << 1) * factor >> 32); + pos += step; + } + offset = -offset; + } else { + while (pos < length) { + samples[pos] += offset; + offset = (int)((LONG_LONG)(offset << 1) * factor >> 32); + pos += step; + } + } + cr->offset = offset; +} + + + +sample_t DUMBEXPORT dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr) +{ + return cr ? cr->offset : 0; +} + + + +void DUMBEXPORT dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr) +{ + if (cr) { + DUMB_CLICK *click = cr->click; + while (click) { + DUMB_CLICK *next = click->next; + free(click); + click = next; + } + click = cr->free_clicks; + while (click) { + DUMB_CLICK *next = click->next; + free(click); + click = next; + } + free(cr); + } +} + + + +DUMB_CLICK_REMOVER **DUMBEXPORT dumb_create_click_remover_array(int n) +{ + int i; + DUMB_CLICK_REMOVER **cr; + if (n <= 0) return NULL; + cr = malloc(n * sizeof(*cr)); + if (!cr) return NULL; + for (i = 0; i < n; i++) cr[i] = dumb_create_click_remover(); + return cr; +} + + + +void DUMBEXPORT dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, int32 pos, sample_t *step) +{ + if (cr) { + int i; + for (i = 0; i < n; i++) + dumb_record_click(cr[i], pos, step[i]); + } +} + + + +void DUMBEXPORT dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, int32 pos, sample_t *step) +{ + if (cr) { + int i; + for (i = 0; i < n; i++) + dumb_record_click(cr[i], pos, -step[i]); + } +} + + + +void DUMBEXPORT dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, int32 length, double halflife) +{ + if (cr) { + int i; + for (i = 0; i < n >> 1; i++) { + dumb_remove_clicks(cr[i << 1], samples[i], length, 2, halflife); + dumb_remove_clicks(cr[(i << 1) + 1], samples[i] + 1, length, 2, halflife); + } + if (n & 1) + dumb_remove_clicks(cr[i << 1], samples[i], length, 1, halflife); + } +} + + + +void DUMBEXPORT dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset) +{ + if (cr) { + int i; + for (i = 0; i < n; i++) + if (cr[i]) offset[i] += cr[i]->offset; + } +} + + + +void DUMBEXPORT dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr) +{ + if (cr) { + int i; + for (i = 0; i < n; i++) dumb_destroy_click_remover(cr[i]); + free(cr); + } +} diff --git a/thirdparty/dumb/src/helpers/lpc.c b/thirdparty/dumb/src/helpers/lpc.c new file mode 100644 index 000000000..04284dac5 --- /dev/null +++ b/thirdparty/dumb/src/helpers/lpc.c @@ -0,0 +1,324 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: LPC low level routines + last mod: $Id: lpc.c 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +/* Some of these routines (autocorrelator, LPC coefficient estimator) + are derived from code written by Jutta Degener and Carsten Bormann; + thus we include their copyright below. The entirety of this file + is freely redistributable on the condition that both of these + copyright notices are preserved without modification. */ + +/* Preserved Copyright: *********************************************/ + +/* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann, +Technische Universita"t Berlin + +Any use of this software is permitted provided that this notice is not +removed and that neither the authors nor the Technische Universita"t +Berlin are deemed to have made any representations as to the +suitability of this software for any purpose nor are held responsible +for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR +THIS SOFTWARE. + +As a matter of courtesy, the authors request to be informed about uses +this software has found, about bugs in this software, and about any +improvements that may be of general interest. + +Berlin, 28.11.1994 +Jutta Degener +Carsten Bormann + +*********************************************************************/ + +#include +#include +#include +#include "internal/stack_alloc.h" +#include "internal/lpc.h" + +#ifdef HAVE_ALLOCA_H +#include +#endif + +/* Autocorrelation LPC coeff generation algorithm invented by + N. Levinson in 1947, modified by J. Durbin in 1959. */ + +/* Input : n elements of time doamin data + Output: m lpc coefficients, excitation energy */ + +float vorbis_lpc_from_data(float *data,float *lpci,int n,int m){ + double *aut=alloca(sizeof(*aut)*(m+1)); + double *lpc=alloca(sizeof(*lpc)*(m)); + double error; + double epsilon; + int i,j; + + /* autocorrelation, p+1 lag coefficients */ + j=m+1; + while(j--){ + double d=0; /* double needed for accumulator depth */ + for(i=j;in_samples; n++ ) { + IT_SAMPLE * sample = sigdata->sample + n; + if ( ( sample->flags & ( IT_SAMPLE_EXISTS | IT_SAMPLE_LOOP) ) == IT_SAMPLE_EXISTS ) { + /* If we have enough sample data to train the filter, use the filter to generate the padding */ + if ( sample->length >= lpc_order ) { + lpc_samples = sample->length; + if (lpc_samples > lpc_max) lpc_samples = lpc_max; + offset = sample->length - lpc_samples; + + if ( sample->flags & IT_SAMPLE_STEREO ) + { + if ( sample->flags & IT_SAMPLE_16BIT ) + { + s16 = ( signed short * ) sample->data; + s16 += offset * 2; + for ( o = 0; o < lpc_samples; o++ ) + { + lpc_input[ o ] = s16[ o * 2 + 0 ]; + lpc_input[ o + lpc_max ] = s16[ o * 2 + 1 ]; + } + } + else + { + s8 = ( signed char * ) sample->data; + s8 += offset * 2; + for ( o = 0; o < lpc_samples; o++ ) + { + lpc_input[ o ] = s8[ o * 2 + 0 ]; + lpc_input[ o + lpc_max ] = s8[ o * 2 + 1 ]; + } + } + + vorbis_lpc_from_data( lpc_input, lpc, lpc_samples, lpc_order ); + vorbis_lpc_from_data( lpc_input + lpc_max, lpc + lpc_order, lpc_samples, lpc_order ); + + vorbis_lpc_predict( lpc, lpc_input + lpc_samples - lpc_order, lpc_order, lpc_output, lpc_extra ); + vorbis_lpc_predict( lpc + lpc_order, lpc_input + lpc_max + lpc_samples - lpc_order, lpc_order, lpc_output + lpc_extra, lpc_extra ); + + if ( sample->flags & IT_SAMPLE_16BIT ) + { + s16 = ( signed short * ) realloc( sample->data, ( sample->length + lpc_extra ) * 2 * sizeof(short) ); + sample->data = s16; + + s16 += sample->length * 2; + sample->length += lpc_extra; + + for ( o = 0; o < lpc_extra; o++ ) + { + s16[ o * 2 + 0 ] = (signed short)lpc_output[ o ]; + s16[ o * 2 + 1 ] = (signed short)lpc_output[ o + lpc_extra ]; + } + } + else + { + s8 = ( signed char * ) realloc( sample->data, ( sample->length + lpc_extra ) * 2 ); + sample->data = s8; + + s8 += sample->length * 2; + sample->length += lpc_extra; + + for ( o = 0; o < lpc_extra; o++ ) + { + s8[ o * 2 + 0 ] = (signed char)lpc_output[ o ]; + s8[ o * 2 + 1 ] = (signed char)lpc_output[ o + lpc_extra ]; + } + } + } + else + { + if ( sample->flags & IT_SAMPLE_16BIT ) + { + s16 = ( signed short * ) sample->data; + s16 += offset; + for ( o = 0; o < lpc_samples; o++ ) + { + lpc_input[ o ] = s16[ o ]; + } + } + else + { + s8 = ( signed char * ) sample->data; + s8 += offset; + for ( o = 0; o < lpc_samples; o++ ) + { + lpc_input[ o ] = s8[ o ]; + } + } + + vorbis_lpc_from_data( lpc_input, lpc, lpc_samples, lpc_order ); + + vorbis_lpc_predict( lpc, lpc_input + lpc_samples - lpc_order, lpc_order, lpc_output, lpc_extra ); + + if ( sample->flags & IT_SAMPLE_16BIT ) + { + s16 = ( signed short * ) realloc( sample->data, ( sample->length + lpc_extra ) * sizeof(short) ); + sample->data = s16; + + s16 += sample->length; + sample->length += lpc_extra; + + for ( o = 0; o < lpc_extra; o++ ) + { + s16[ o ] = (signed short)lpc_output[ o ]; + } + } + else + { + s8 = ( signed char * ) realloc( sample->data, sample->length + lpc_extra ); + sample->data = s8; + + s8 += sample->length; + sample->length += lpc_extra; + + for ( o = 0; o < lpc_extra; o++ ) + { + s8[ o ] = (signed char)lpc_output[ o ]; + } + } + } + } + else + /* Otherwise, pad with silence. */ + { + offset = sample->length; + lpc_samples = lpc_extra; + + sample->length += lpc_samples; + + n = 1; + if ( sample->flags & IT_SAMPLE_STEREO ) n *= 2; + if ( sample->flags & IT_SAMPLE_16BIT ) n *= 2; + + offset *= n; + lpc_samples *= n; + + sample->data = realloc( sample->data, offset + lpc_samples ); + memset( (char*)sample->data + offset, 0, lpc_samples ); + } + } + } +} diff --git a/thirdparty/dumb/src/helpers/memfile.c b/thirdparty/dumb/src/helpers/memfile.c new file mode 100644 index 000000000..476683944 --- /dev/null +++ b/thirdparty/dumb/src/helpers/memfile.c @@ -0,0 +1,117 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * memfile.c - Module for reading data from / / \ \ + * memory using a DUMBFILE. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" + + + +typedef struct MEMFILE MEMFILE; + +struct MEMFILE +{ + const char *ptr, *ptr_begin; + long left, size; +}; + + + +static int DUMBCALLBACK dumb_memfile_skip(void *f, long n) +{ + MEMFILE *m = f; + if (n > m->left) return -1; + m->ptr += n; + m->left -= n; + return 0; +} + + + +static int DUMBCALLBACK dumb_memfile_getc(void *f) +{ + MEMFILE *m = f; + if (m->left <= 0) return -1; + m->left--; + return *(const unsigned char *)m->ptr++; +} + + + +static int32 DUMBCALLBACK dumb_memfile_getnc(char *ptr, int32 n, void *f) +{ + MEMFILE *m = f; + if (n > m->left) n = m->left; + memcpy(ptr, m->ptr, n); + m->ptr += n; + m->left -= n; + return n; +} + + + +static void DUMBCALLBACK dumb_memfile_close(void *f) +{ + free(f); +} + + +static int DUMBCALLBACK dumb_memfile_seek(void *f, long n) +{ + MEMFILE *m = f; + + m->ptr = m->ptr_begin + n; + m->left = m->size - n; + + return 0; +} + + +static long DUMBCALLBACK dumb_memfile_get_size(void *f) +{ + MEMFILE *m = f; + return m->size; +} + + +static const DUMBFILE_SYSTEM memfile_dfs = { + NULL, + &dumb_memfile_skip, + &dumb_memfile_getc, + &dumb_memfile_getnc, + &dumb_memfile_close, + &dumb_memfile_seek, + &dumb_memfile_get_size +}; + + + +DUMBFILE *DUMBEXPORT dumbfile_open_memory(const char *data, int32 size) +{ + MEMFILE *m = malloc(sizeof(*m)); + if (!m) return NULL; + + m->ptr_begin = data; + m->ptr = data; + m->left = size; + m->size = size; + + return dumbfile_open_ex(m, &memfile_dfs); +} diff --git a/thirdparty/dumb/src/helpers/resamp2.inc b/thirdparty/dumb/src/helpers/resamp2.inc new file mode 100644 index 000000000..63b59e94e --- /dev/null +++ b/thirdparty/dumb/src/helpers/resamp2.inc @@ -0,0 +1,174 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * resamp2.inc - Resampling helper template. / / \ \ + * | < / \_ + * By Bob and entheh. | \/ /\ / + * \_ / > / + * In order to find a good trade-off between | \ / / + * speed and accuracy in this code, some tests | ' / + * were carried out regarding the behaviour of \__/ + * long long ints with gcc. The following code + * was tested: + * + * int a, b, c; + * c = ((long long)a * b) >> 16; + * + * DJGPP GCC Version 3.0.3 generated the following assembly language code for + * the multiplication and scaling, leaving the 32-bit result in EAX. + * + * movl -8(%ebp), %eax ; read one int into EAX + * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX + * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX + * + * Note that a 32*32->64 multiplication is performed, allowing for high + * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), + * so it is a minor concern when four multiplications are being performed + * (the cubic resampler). On the Pentium MMX and earlier, it takes four or + * more cycles, so this method is unsuitable for use in the low-quality + * resamplers. + * + * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, + * defined in dumb.h. We may investigate later what code MSVC generates, but + * if it seems too slow then we suggest you use a good compiler. + * + * FIXME: these comments are somewhat out of date now. + */ + + + +#define SUFFIX3 _2 + +/* For convenience, returns nonzero on stop. */ +static int process_pickup(DUMB_RESAMPLER *resampler) +{ + if (resampler->overshot < 0) { + resampler->overshot = 0; + dumb_resample(resampler, NULL, 2, MONO_DEST_VOLUME_ZEROS, 1.0f); /* Doesn't matter which SUFFIX3. */ + COPYSRC(resampler->X, 0, resampler->X, 1); + } + + for (;;) { + SRCTYPE *src = resampler->src; + + if (resampler->dir < 0) { + if (resampler->overshot >= 3 && resampler->pos+3 >= resampler->start) COPYSRC(resampler->X, 0, src, resampler->pos+3); + if (resampler->overshot >= 2 && resampler->pos+2 >= resampler->start) COPYSRC(resampler->X, 1, src, resampler->pos+2); + if (resampler->overshot >= 1 && resampler->pos+1 >= resampler->start) COPYSRC(resampler->X, 2, src, resampler->pos+1); + resampler->overshot = resampler->start - resampler->pos - 1; + } else { + if (resampler->overshot >= 3 && resampler->pos-3 < resampler->end) COPYSRC(resampler->X, 0, src, resampler->pos-3); + if (resampler->overshot >= 2 && resampler->pos-2 < resampler->end) COPYSRC(resampler->X, 1, src, resampler->pos-2); + if (resampler->overshot >= 1 && resampler->pos-1 < resampler->end) COPYSRC(resampler->X, 2, src, resampler->pos-1); + resampler->overshot = resampler->pos - resampler->end; + } + + if (resampler->overshot < 0) { + resampler->overshot = 0; + return 0; + } + + if (!resampler->pickup) { + resampler->dir = 0; + return 1; + } + (*resampler->pickup)(resampler, resampler->pickup_data); + if (resampler->dir == 0) return 1; + ASSERT(resampler->dir == -1 || resampler->dir == 1); + } +} + + + +/* Create mono destination resampler. */ +/* SUFFIX3 was set above. */ +#if 0 +#define VOLUME_PARAMETERS MONO_DEST_VOLUME_PARAMETERS +#define VOLUME_VARIABLES MONO_DEST_VOLUME_VARIABLES +#define SET_VOLUME_VARIABLES SET_MONO_DEST_VOLUME_VARIABLES +#define RETURN_VOLUME_VARIABLES RETURN_MONO_DEST_VOLUME_VARIABLES +#define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO +#define PEEK_FIR MONO_DEST_PEEK_FIR +#define MIX_FIR MONO_DEST_MIX_FIR +#define MIX_ZEROS(op) *dst++ op 0 +#include "resamp3.inc" +#else +#undef SUFFIX3 +#endif + +/* Create stereo destination resampler. */ +#define SUFFIX3 _2 +#define VOLUME_PARAMETERS DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right +#define VOLUME_VARIABLES lvol, lvolr, lvold, lvolt, lvolm, rvol, rvolr, rvold, rvolt, rvolm +#define SET_VOLUME_VARIABLES { \ + if ( volume_left ) { \ + lvolr = xs_FloorToInt(volume_left->volume * 16777216.f); \ + lvold = xs_FloorToInt(volume_left->delta * 16777216.f); \ + lvolt = xs_FloorToInt(volume_left->target * 16777216.f); \ + lvolm = xs_FloorToInt(volume_left->mix * 16777216.f); \ + lvol = MULSCV( lvolr, lvolm ); \ + if ( lvolr == lvolt ) volume_left = NULL; \ + } else { \ + lvol = 0; \ + lvold = 0; \ + lvolt = 0; \ + lvolm = 0; \ + } \ + if ( volume_right ) { \ + rvolr = xs_FloorToInt(volume_right->volume * 16777216.f); \ + rvold = xs_FloorToInt(volume_right->delta * 16777216.f); \ + rvolt = xs_FloorToInt(volume_right->target * 16777216.f); \ + rvolm = xs_FloorToInt(volume_right->mix * 16777216.f); \ + rvol = MULSCV( rvolr, rvolm ); \ + if ( rvolr == rvolt ) volume_right = NULL; \ + } else { \ + rvol = 0; \ + rvold = 0; \ + rvolt = 0; \ + rvolm = 0; \ + } \ +} +#define RETURN_VOLUME_VARIABLES { \ + if ( volume_left ) volume_left->volume = (float)lvolr / 16777216.0f; \ + if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \ +} +#define VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0) +#define MIX_ALIAS(op, upd, offset) STEREO_DEST_MIX_ALIAS(op, upd, offset) +#define MIX_LINEAR(op, upd, o0, o1) STEREO_DEST_MIX_LINEAR(op, upd, o0, o1) +#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) +#define PEEK_FIR STEREO_DEST_PEEK_FIR +#define MIX_FIR STEREO_DEST_MIX_FIR +#define MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; } +#include "resamp3.inc" + + + +#undef STEREO_DEST_MIX_CUBIC +#undef STEREO_DEST_MIX_LINEAR +#undef STEREO_DEST_MIX_ALIAS +#undef MONO_DEST_VOLUMES_ARE_ZERO +#undef SET_MONO_DEST_VOLUME_VARIABLES +#undef RETURN_MONO_DEST_VOLUME_VARIABLES +#undef MONO_DEST_VOLUME_ZEROS +#undef MONO_DEST_VOLUME_VARIABLES +#undef MONO_DEST_VOLUME_PARAMETERS +#undef STEREO_DEST_PEEK_ALIAS +#undef POKE_ALIAS +#undef MONO_DEST_PEEK_FIR +#undef STEREO_DEST_PEEK_FIR +#undef MONO_DEST_MIX_FIR +#undef STEREO_DEST_MIX_FIR +#undef ADVANCE_FIR +#undef POKE_FIR +#undef COPYSRC2 +#undef COPYSRC +#undef DIVIDE_BY_SRC_CHANNELS +#undef SRC_CHANNELS +#undef SUFFIX2 diff --git a/thirdparty/dumb/src/helpers/resamp3.inc b/thirdparty/dumb/src/helpers/resamp3.inc new file mode 100644 index 000000000..5fc13618b --- /dev/null +++ b/thirdparty/dumb/src/helpers/resamp3.inc @@ -0,0 +1,436 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * resamp3.inc - Resampling helper template. / / \ \ + * | < / \_ + * By Bob and entheh. | \/ /\ / + * \_ / > / + * In order to find a good trade-off between | \ / / + * speed and accuracy in this code, some tests | ' / + * were carried out regarding the behaviour of \__/ + * long long ints with gcc. The following code + * was tested: + * + * int a, b, c; + * c = ((long long)a * b) >> 16; + * + * DJGPP GCC Version 3.0.3 generated the following assembly language code for + * the multiplication and scaling, leaving the 32-bit result in EAX. + * + * movl -8(%ebp), %eax ; read one int into EAX + * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX + * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX + * + * Note that a 32*32->64 multiplication is performed, allowing for high + * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), + * so it is a minor concern when four multiplications are being performed + * (the cubic resampler). On the Pentium MMX and earlier, it takes four or + * more cycles, so this method is unsuitable for use in the low-quality + * resamplers. + * + * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, + * defined in dumb.h. We may investigate later what code MSVC generates, but + * if it seems too slow then we suggest you use a good compiler. + * + * FIXME: these comments are somewhat out of date now. + */ + + + +int32 dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, VOLUME_PARAMETERS, double delta) +{ + int dt, inv_dt; + int VOLUME_VARIABLES; + long done; + long todo; + double tododbl; + int quality; + + if (!resampler || resampler->dir == 0) return 0; + ASSERT(resampler->dir == -1 || resampler->dir == 1); + + done = 0; + dt = xs_CRoundToInt(delta * 65536.0); + if (dt == 0 || dt == 0x80000000) return 0; + inv_dt = xs_CRoundToInt(1.0 / delta * 65536.0); + SET_VOLUME_VARIABLES; + + if (VOLUMES_ARE_ZERO) dst = NULL; + + _dumb_init_cubic(); + + quality = resampler->quality; + + while (done < dst_size) { + if (process_pickup(resampler)) { + RETURN_VOLUME_VARIABLES; + return done; + } + + if ((resampler->dir ^ dt) < 0) + dt = -dt; + + if (resampler->dir < 0) + tododbl = ((resampler->pos - resampler->start) * 65536.f + (resampler->subpos - dt)) / -dt; + else + tododbl = ((resampler->end - resampler->pos) * 65536.f - (resampler->subpos + 1 - dt)) / dt; + + if (tododbl <= 0) + todo = 0; + else if (tododbl >= dst_size - done) + todo = dst_size - done; + else + todo = xs_FloorToInt(tododbl); + + done += todo; + + { + SRCTYPE *src = resampler->src; + long pos = resampler->pos; + int subpos = resampler->subpos; + long diff = pos; + long overshot; + if (resampler->dir < 0) { + if (!dst) { + /* Silence or simulation */ + LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo; + pos += (long)(new_subpos >> 16); + subpos = (long)new_subpos & 65535; + } else if (quality <= DUMB_RQ_ALIASING) { + /* Aliasing, backwards */ + SRCTYPE xbuf[2*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[0]; + SRCTYPE *xstart; + COPYSRC(xbuf, 0, resampler->X, 1); + COPYSRC(xbuf, 1, resampler->X, 2); + while (todo && x < &xbuf[2*SRC_CHANNELS]) { + // TODO: check what happens when multiple tempo slides occur per row + HEAVYASSERT(pos >= resampler->start); + MIX_ALIAS(+=, 1, 0); + subpos += dt; + pos += subpos >> 16; + x -= (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + todo--; + } + x = xstart = &src[pos*SRC_CHANNELS]; + LOOP4(todo, + MIX_ALIAS(+=, 1, 2); + subpos += dt; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + ); + pos += DIVIDE_BY_SRC_CHANNELS(x - xstart); + } else if (quality <= DUMB_LQ_LINEAR) { + /* Linear interpolation, backwards */ + SRCTYPE xbuf[3*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[1*SRC_CHANNELS]; + COPYSRC(xbuf, 0, resampler->X, 1); + COPYSRC(xbuf, 1, resampler->X, 2); + COPYSRC(xbuf, 2, src, pos); + while (todo && x < &xbuf[3*SRC_CHANNELS]) { + HEAVYASSERT(pos >= resampler->start); + MIX_LINEAR(+=, 1, 0, -1); + subpos += dt; + pos += subpos >> 16; + x -= (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + todo--; + } + // TODO: use xstart for others too + x = &src[pos*SRC_CHANNELS]; + LOOP4(todo, + HEAVYASSERT(pos >= resampler->start); + MIX_LINEAR(+=, 1, 1, 2); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + ); + } else if (quality <= DUMB_LQ_CUBIC) { + /* Cubic interpolation, backwards */ + SRCTYPE xbuf[6*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[3*SRC_CHANNELS]; + COPYSRC(xbuf, 0, resampler->X, 0); + COPYSRC(xbuf, 1, resampler->X, 1); + COPYSRC(xbuf, 2, resampler->X, 2); + COPYSRC(xbuf, 3, src, pos); + if (pos-1 >= resampler->start) COPYSRC(xbuf, 4, src, pos-1); + if (pos-2 >= resampler->start) COPYSRC(xbuf, 5, src, pos-2); + while (todo && x < &xbuf[6*SRC_CHANNELS]) { + HEAVYASSERT(pos >= resampler->start); + MIX_CUBIC(+=, 1, x, x, 0, -1, -2, -3); + subpos += dt; + pos += subpos >> 16; + x -= (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + todo--; + } + x = &src[pos*SRC_CHANNELS]; + LOOP4(todo, + HEAVYASSERT(pos >= resampler->start); + MIX_CUBIC(+=, 1, x, x, 0, 1, 2, 3); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + ); + } else { + /* FIR resampling, backwards */ + SRCTYPE *x; + if ( resampler->fir_resampler_ratio != delta ) { + resampler_set_rate( resampler->fir_resampler[0], delta ); + resampler_set_rate( resampler->fir_resampler[1], delta ); + resampler->fir_resampler_ratio = delta; + } + x = &src[pos*SRC_CHANNELS]; + while ( todo ) { + while ( ( resampler_get_free_count( resampler->fir_resampler[0] ) || + (!resampler_get_sample_count( resampler->fir_resampler[0] ) + #if SRC_CHANNELS == 2 + && !resampler_get_sample_count( resampler->fir_resampler[1] ) + #endif + ) ) && pos >= resampler->start ) + { + POKE_FIR(0); + pos--; + x -= SRC_CHANNELS; + } + if ( !resampler_get_sample_count( resampler->fir_resampler[0] ) ) break; + MIX_FIR; + ADVANCE_FIR; + --todo; + } + done -= todo; + } + diff = diff - pos; + overshot = resampler->start - pos - 1; + if (diff >= 3) { + COPYSRC2(resampler->X, 0, overshot < 3, src, pos+3); + COPYSRC2(resampler->X, 1, overshot < 2, src, pos+2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1); + } else if (diff >= 2) { + COPYSRC(resampler->X, 0, resampler->X, 2); + COPYSRC2(resampler->X, 1, overshot < 2, src, pos+2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1); + } else if (diff >= 1) { + COPYSRC(resampler->X, 0, resampler->X, 1); + COPYSRC(resampler->X, 1, resampler->X, 2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1); + } + } else { + if (!dst) { + /* Silence or simulation */ + LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo; + pos += (long)(new_subpos >> 16); + subpos = (long)new_subpos & 65535; + } else if (quality <= DUMB_RQ_ALIASING) { + /* Aliasing, forwards */ + SRCTYPE xbuf[2*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[0]; + SRCTYPE *xstart; + COPYSRC(xbuf, 0, resampler->X, 1); + COPYSRC(xbuf, 1, resampler->X, 2); + while (todo && x < &xbuf[2*SRC_CHANNELS]) { + HEAVYASSERT(pos < resampler->end); + MIX_ALIAS(+=, 1, 0); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + todo--; + } + x = xstart = &src[pos*SRC_CHANNELS]; + LOOP4(todo, + MIX_ALIAS(+=, 1, -2); + subpos += dt; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + ); + pos += DIVIDE_BY_SRC_CHANNELS(x - xstart); + } else if (quality <= DUMB_LQ_LINEAR) { + /* Linear interpolation, forwards */ + SRCTYPE xbuf[3*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[1*SRC_CHANNELS]; + COPYSRC(xbuf, 0, resampler->X, 1); + COPYSRC(xbuf, 1, resampler->X, 2); + COPYSRC(xbuf, 2, src, pos); + while (todo && x < &xbuf[3*SRC_CHANNELS]) { + HEAVYASSERT(pos < resampler->end); + MIX_LINEAR(+=, 1, -1, 0); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + todo--; + } + x = &src[pos*SRC_CHANNELS]; + LOOP4(todo, + HEAVYASSERT(pos < resampler->end); + MIX_LINEAR(+=, 1, -2, -1); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + ); + } else if (quality <= DUMB_LQ_CUBIC) { + /* Cubic interpolation, forwards */ + SRCTYPE xbuf[6*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[3*SRC_CHANNELS]; + COPYSRC(xbuf, 0, resampler->X, 0); + COPYSRC(xbuf, 1, resampler->X, 1); + COPYSRC(xbuf, 2, resampler->X, 2); + COPYSRC(xbuf, 3, src, pos); + if (pos+1 < resampler->end) COPYSRC(xbuf, 4, src, pos+1); + if (pos+2 < resampler->end) COPYSRC(xbuf, 5, src, pos+2); + while (todo && x < &xbuf[6*SRC_CHANNELS]) { + HEAVYASSERT(pos < resampler->end); + MIX_CUBIC(+=, 1, x, x, -3, -2, -1, 0); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + todo--; + } + x = &src[pos*SRC_CHANNELS]; + LOOP4(todo, + HEAVYASSERT(pos < resampler->end); + MIX_CUBIC(+=, 1, x, x, -3, -2, -1, 0); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + ); + } else { + /* FIR resampling, forwards */ + SRCTYPE *x; + if ( resampler->fir_resampler_ratio != delta ) { + resampler_set_rate( resampler->fir_resampler[0], delta ); + resampler_set_rate( resampler->fir_resampler[1], delta ); + resampler->fir_resampler_ratio = delta; + } + x = &src[pos*SRC_CHANNELS]; + while ( todo ) { + while ( ( resampler_get_free_count( resampler->fir_resampler[0] ) || + (!resampler_get_sample_count( resampler->fir_resampler[0] ) + #if SRC_CHANNELS == 2 + && !resampler_get_sample_count( resampler->fir_resampler[1] ) + #endif + ) ) && pos < resampler->end ) + { + POKE_FIR(0); + pos++; + x += SRC_CHANNELS; + } + if ( !resampler_get_sample_count( resampler->fir_resampler[0] ) ) break; + MIX_FIR; + ADVANCE_FIR; + --todo; + } + done -= todo; + } + diff = pos - diff; + overshot = pos - resampler->end; + if (diff >= 3) { + COPYSRC2(resampler->X, 0, overshot < 3, src, pos-3); + COPYSRC2(resampler->X, 1, overshot < 2, src, pos-2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1); + } else if (diff >= 2) { + COPYSRC(resampler->X, 0, resampler->X, 2); + COPYSRC2(resampler->X, 1, overshot < 2, src, pos-2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1); + } else if (diff >= 1) { + COPYSRC(resampler->X, 0, resampler->X, 1); + COPYSRC(resampler->X, 1, resampler->X, 2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1); + } + } + resampler->pos = pos; + resampler->subpos = subpos; + } + } + + RETURN_VOLUME_VARIABLES; + return done; +} + + + +void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETERS, sample_t *dst) +{ + int VOLUME_VARIABLES; + SRCTYPE *src; + long pos; + int subpos; + int quality; + SRCTYPE *x; + + if (!resampler || resampler->dir == 0) { MIX_ZEROS(=); return; } + ASSERT(resampler->dir == -1 || resampler->dir == 1); + + if (process_pickup(resampler)) { MIX_ZEROS(=); return; } + + SET_VOLUME_VARIABLES; + + if (VOLUMES_ARE_ZERO) { MIX_ZEROS(=); return; } + + _dumb_init_cubic(); + + quality = resampler->quality; + + src = resampler->src; + pos = resampler->pos; + subpos = resampler->subpos; + x = resampler->X; + + if (resampler->dir < 0) { + HEAVYASSERT(pos >= resampler->start); + if (quality <= DUMB_RQ_ALIASING) { + /* Aliasing, backwards */ + MIX_ALIAS(=, 0, 1); + } else if (quality <= DUMB_LQ_LINEAR) { + /* Linear interpolation, backwards */ + MIX_LINEAR(=, 0, 2, 1); + } else if (quality <= DUMB_LQ_CUBIC) { + /* Cubic interpolation, backwards */ + MIX_CUBIC(=, 0, src, x, pos, 2, 1, 0); + } else { + /* FIR resampling, backwards */ + PEEK_FIR; + } + } else { + HEAVYASSERT(pos < resampler->end); + if (quality <= DUMB_RQ_ALIASING) { + /* Aliasing */ + MIX_ALIAS(=, 0, 1); + } else if (quality <= DUMB_LQ_LINEAR) { + /* Linear interpolation, forwards */ + MIX_LINEAR(=, 0, 1, 2); + } else if (quality <= DUMB_LQ_CUBIC) { + /* Cubic interpolation, forwards */ + MIX_CUBIC(=, 0, x, src, 0, 1, 2, pos); + } else { + /* FIR resampling, forwards */ + PEEK_FIR; + } + } +} + + + +#undef MIX_ZEROS +#undef MIX_FIR +#undef PEEK_FIR +#undef VOLUMES_ARE_ZERO +#undef SET_VOLUME_VARIABLES +#undef RETURN_VOLUME_VARIABLES +#undef VOLUME_VARIABLES +#undef VOLUME_PARAMETERS +#undef SUFFIX3 diff --git a/thirdparty/dumb/src/helpers/resample.c b/thirdparty/dumb/src/helpers/resample.c new file mode 100644 index 000000000..30a60d8da --- /dev/null +++ b/thirdparty/dumb/src/helpers/resample.c @@ -0,0 +1,420 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * resample.c - Resampling helpers. / / \ \ + * | < / \_ + * By Bob and entheh. | \/ /\ / + * \_ / > / + * In order to find a good trade-off between | \ / / + * speed and accuracy in this code, some tests | ' / + * were carried out regarding the behaviour of \__/ + * long long ints with gcc. The following code + * was tested: + * + * int a, b, c; + * c = ((long long)a * b) >> 16; + * + * DJGPP GCC Version 3.0.3 generated the following assembly language code for + * the multiplication and scaling, leaving the 32-bit result in EAX. + * + * movl -8(%ebp), %eax ; read one int into EAX + * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX + * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX + * + * Note that a 32*32->64 multiplication is performed, allowing for high + * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), + * so it is a minor concern when four multiplications are being performed + * (the cubic resampler). On the Pentium MMX and earlier, it takes four or + * more cycles, so this method is unsuitable for use in the low-quality + * resamplers. + * + * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, + * defined in dumb.h. We may investigate later what code MSVC generates, but + * if it seems too slow then we suggest you use a good compiler. + * + * FIXME: these comments are somewhat out of date now. + */ + +#include +#include "dumb.h" + +#include "internal/resampler.h" +#include "internal/mulsc.h" + + + +/* Compile with -DHEAVYDEBUG if you want to make sure the pick-up function is + * called when it should be. There will be a considerable performance hit, + * since at least one condition has to be tested for every sample generated. + */ +#ifdef HEAVYDEBUG +#define HEAVYASSERT(cond) ASSERT(cond) +#else +#define HEAVYASSERT(cond) +#endif + + + +/* Make MSVC shut the hell up about if ( upd ) UPDATE_VOLUME() conditions being constant */ +#ifdef _MSC_VER +#pragma warning(disable:4127 4701) +#endif + + + +/* A global variable for controlling resampling quality wherever a local + * specification doesn't override it. The following values are valid: + * + * 0 - DUMB_RQ_ALIASING - fastest + * 1 - DUMB_RQ_BLEP - nicer than aliasing, but slower + * 2 - DUMB_RQ_LINEAR + * 3 - DUMB_RQ_BLAM - band-limited linear interpolation, nice but slower + * 4 - DUMB_RQ_CUBIC + * 5 - DUMB_RQ_FIR - nicest + * + * Values outside the range 0-4 will behave the same as the nearest + * value within the range. + */ +int dumb_resampling_quality = DUMB_RQ_CUBIC; + + + +/* From xs_Float.h ==============================================*/ +#if __BIG_ENDIAN__ + #define _xs_iman_ 1 +#else + #define _xs_iman_ 0 +#endif //BigEndian_ + +#ifdef __GNUC__ +#define finline inline +#else +#define finline __forceinline +#endif + +union _xs_doubleints +{ + double val; + unsigned int ival[2]; +}; + +static const double _xs_doublemagic = (6755399441055744.0); //2^52 * 1.5, uses limited precisicion to floor +static const double _xs_doublemagicroundeps = (.5f-(1.5e-8)); //almost .5f = .5f - 1e^(number of exp bit) + +static finline int xs_CRoundToInt(double val) +{ + union _xs_doubleints uval; + val += _xs_doublemagic; + uval.val = val; + return uval.ival[_xs_iman_]; +} +static finline int xs_FloorToInt(double val) +{ + union _xs_doubleints uval; + val -= _xs_doublemagicroundeps; + val += _xs_doublemagic; + uval.val = val; + return uval.ival[_xs_iman_]; +} +/* Not from xs_Float.h ==========================================*/ + + +/* Executes the content 'iterator' times. + * Clobbers the 'iterator' variable. + * The loop is unrolled by four. + */ +#if 0 +#define LOOP4(iterator, CONTENT) \ +{ \ + if ((iterator) & 2) { \ + CONTENT; \ + CONTENT; \ + } \ + if ((iterator) & 1) { \ + CONTENT; \ + } \ + (iterator) >>= 2; \ + while (iterator) { \ + CONTENT; \ + CONTENT; \ + CONTENT; \ + CONTENT; \ + (iterator)--; \ + } \ +} +#else +#define LOOP4(iterator, CONTENT) \ +{ \ + while ( (iterator)-- ) \ + { \ + CONTENT; \ + } \ +} +#endif + +#define PASTERAW(a, b) a ## b /* This does not expand macros in b ... */ +#define PASTE(a, b) PASTERAW(a, b) /* ... but b is expanded during this substitution. */ + +#define X PASTE(x.x, SRCBITS) + + + +/* Cubic resampler: look-up tables + * + * a = 1.5*x1 - 1.5*x2 + 0.5*x3 - 0.5*x0 + * b = 2*x2 + x0 - 2.5*x1 - 0.5*x3 + * c = 0.5*x2 - 0.5*x0 + * d = x1 + * + * x = a*t*t*t + b*t*t + c*t + d + * = (-0.5*x0 + 1.5*x1 - 1.5*x2 + 0.5*x3) * t*t*t + + * ( 1*x0 - 2.5*x1 + 2 *x2 - 0.5*x3) * t*t + + * (-0.5*x0 + 0.5*x2 ) * t + + * ( 1*x1 ) + * = (-0.5*t*t*t + 1 *t*t - 0.5*t ) * x0 + + * ( 1.5*t*t*t - 2.5*t*t + 1) * x1 + + * (-1.5*t*t*t + 2 *t*t + 0.5*t ) * x2 + + * ( 0.5*t*t*t - 0.5*t*t ) * x3 + * = A0(t) * x0 + A1(t) * x1 + A2(t) * x2 + A3(t) * x3 + * + * A0, A1, A2 and A3 stay within the range [-1,1]. + * In the tables, they are scaled with 14 fractional bits. + * + * Turns out we don't need to store A2 and A3; they are symmetrical to A1 and A0. + * + * TODO: A0 and A3 stay very small indeed. Consider different scale/resolution? + */ + +static short cubicA0[1025], cubicA1[1025]; + +void _dumb_init_cubic(void) +{ + unsigned int t; /* 3*1024*1024*1024 is within range if it's unsigned */ + static int done = 0; + if (done) return; + for (t = 0; t < 1025; t++) { + /* int casts to pacify warnings about negating unsigned values */ + cubicA0[t] = -(int)( t*t*t >> 17) + (int)( t*t >> 6) - (int)(t << 3); + cubicA1[t] = (int)(3*t*t*t >> 17) - (int)(5*t*t >> 7) + (int)(1 << 14); + } + resampler_init(); + + done = 1; +} + + + +/* Create resamplers for 24-in-32-bit source samples. */ + +/* #define SUFFIX + * MSVC warns if we try to paste a null SUFFIX, so instead we define + * special macros for the function names that don't bother doing the + * corresponding paste. The more generic definitions are further down. + */ +#define process_pickup PASTE(process_pickup, SUFFIX2) +#define dumb_resample PASTE(PASTE(dumb_resample, SUFFIX2), SUFFIX3) +#define dumb_resample_get_current_sample PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX2), SUFFIX3) + +#define SRCTYPE sample_t +#define SRCBITS 24 +#define ALIAS(x, vol) MULSC(x, vol) +#define LINEAR(x0, x1) (x0 + MULSC(x1 - x0, subpos)) +#define CUBIC(x0, x1, x2, x3) ( \ + MULSC(x0, cubicA0[subpos >> 6] << 2) + \ + MULSC(x1, cubicA1[subpos >> 6] << 2) + \ + MULSC(x2, cubicA1[1 + (subpos >> 6 ^ 1023)] << 2) + \ + MULSC(x3, cubicA0[1 + (subpos >> 6 ^ 1023)] << 2)) +#define CUBICVOL(x, vol) MULSC(x, vol) +#define FIR(x) (x >> 8) +#include "resample.inc" + +/* Undefine the simplified macros. */ +#undef dumb_resample_get_current_sample +#undef dumb_resample +#undef process_pickup + + +/* Now define the proper ones that use SUFFIX. */ +#define dumb_reset_resampler PASTE(dumb_reset_resampler, SUFFIX) +#define dumb_start_resampler PASTE(dumb_start_resampler, SUFFIX) +#define process_pickup PASTE(PASTE(process_pickup, SUFFIX), SUFFIX2) +#define dumb_resample PASTE(PASTE(PASTE(dumb_resample, SUFFIX), SUFFIX2), SUFFIX3) +#define dumb_resample_get_current_sample PASTE(PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX), SUFFIX2), SUFFIX3) +#define dumb_end_resampler PASTE(dumb_end_resampler, SUFFIX) + +/* Create resamplers for 16-bit source samples. */ +#define SUFFIX _16 +#define SRCTYPE short +#define SRCBITS 16 +#define ALIAS(x, vol) (x * vol >> 8) +#define LINEAR(x0, x1) ((x0 << 8) + MULSC16(x1 - x0, subpos)) +#define CUBIC(x0, x1, x2, x3) ( \ + x0 * cubicA0[subpos >> 6] + \ + x1 * cubicA1[subpos >> 6] + \ + x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \ + x3 * cubicA0[1 + (subpos >> 6 ^ 1023)]) +#define CUBICVOL(x, vol) MULSCV((x), ((vol) << 10)) +#define FIR(x) (x) +#include "resample.inc" + +/* Create resamplers for 8-bit source samples. */ +#define SUFFIX _8 +#define SRCTYPE signed char +#define SRCBITS 8 +#define ALIAS(x, vol) (x * vol) +#define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos) +#define CUBIC(x0, x1, x2, x3) (( \ + x0 * cubicA0[subpos >> 6] + \ + x1 * cubicA1[subpos >> 6] + \ + x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \ + x3 * cubicA0[1 + (subpos >> 6 ^ 1023)]) << 6) +#define CUBICVOL(x, vol) MULSCV((x), ((vol) << 12)) +#define FIR(x) (x << 8) +#include "resample.inc" + + +#undef dumb_reset_resampler +#undef dumb_start_resampler +#undef process_pickup +#undef dumb_resample +#undef dumb_resample_get_current_sample +#undef dumb_end_resampler + + + +void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, int src_channels, int32 pos, int32 start, int32 end, int quality) +{ + if (n == 8) + dumb_reset_resampler_8(resampler, src, src_channels, pos, start, end, quality); + else if (n == 16) + dumb_reset_resampler_16(resampler, src, src_channels, pos, start, end, quality); + else + dumb_reset_resampler(resampler, src, src_channels, pos, start, end, quality); +} + + + +DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, int32 pos, int32 start, int32 end, int quality) +{ + if (n == 8) + return dumb_start_resampler_8(src, src_channels, pos, start, end, quality); + else if (n == 16) + return dumb_start_resampler_16(src, src_channels, pos, start, end, quality); + else + return dumb_start_resampler(src, src_channels, pos, start, end, quality); +} + + +#if 0 +int32 dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume, double delta) +{ + if (n == 8) + return dumb_resample_8_1_1(resampler, dst, dst_size, volume, delta); + else if (n == 16) + return dumb_resample_16_1_1(resampler, dst, dst_size, volume, delta); + else + return dumb_resample_1_1(resampler, dst, dst_size, volume, delta); +} +#endif + + +int32 dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta) +{ + if (n == 8) + return dumb_resample_8_1_2(resampler, dst, dst_size, volume_left, volume_right, delta); + else if (n == 16) + return dumb_resample_16_1_2(resampler, dst, dst_size, volume_left, volume_right, delta); + else + return dumb_resample_1_2(resampler, dst, dst_size, volume_left, volume_right, delta); +} + + +#if 0 +int32 dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta) +{ + if (n == 8) + return dumb_resample_8_2_1(resampler, dst, dst_size, volume_left, volume_right, delta); + else if (n == 16) + return dumb_resample_16_2_1(resampler, dst, dst_size, volume_left, volume_right, delta); + else + return dumb_resample_2_1(resampler, dst, dst_size, volume_left, volume_right, delta); +} +#endif + + +int32 dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, double delta) +{ + if (n == 8) + return dumb_resample_8_2_2(resampler, dst, dst_size, volume_left, volume_right, delta); + else if (n == 16) + return dumb_resample_16_2_2(resampler, dst, dst_size, volume_left, volume_right, delta); + else + return dumb_resample_2_2(resampler, dst, dst_size, volume_left, volume_right, delta); +} + + +#if 0 +void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst) +{ + if (n == 8) + dumb_resample_get_current_sample_8_1_1(resampler, volume, dst); + else if (n == 16) + dumb_resample_get_current_sample_16_1_1(resampler, volume, dst); + else + dumb_resample_get_current_sample_1_1(resampler, volume, dst); +} +#endif + + +void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst) +{ + if (n == 8) + dumb_resample_get_current_sample_8_1_2(resampler, volume_left, volume_right, dst); + else if (n == 16) + dumb_resample_get_current_sample_16_1_2(resampler, volume_left, volume_right, dst); + else + dumb_resample_get_current_sample_1_2(resampler, volume_left, volume_right, dst); +} + + +#if 0 +void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst) +{ + if (n == 8) + dumb_resample_get_current_sample_8_2_1(resampler, volume_left, volume_right, dst); + else if (n == 16) + dumb_resample_get_current_sample_16_2_1(resampler, volume_left, volume_right, dst); + else + dumb_resample_get_current_sample_2_1(resampler, volume_left, volume_right, dst); +} +#endif + + +void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst) +{ + if (n == 8) + dumb_resample_get_current_sample_8_2_2(resampler, volume_left, volume_right, dst); + else if (n == 16) + dumb_resample_get_current_sample_16_2_2(resampler, volume_left, volume_right, dst); + else + dumb_resample_get_current_sample_2_2(resampler, volume_left, volume_right, dst); +} + + + +void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler) +{ + if (n == 8) + dumb_end_resampler_8(resampler); + else if (n == 16) + dumb_end_resampler_16(resampler); + else + dumb_end_resampler(resampler); +} diff --git a/thirdparty/dumb/src/helpers/resample.inc b/thirdparty/dumb/src/helpers/resample.inc new file mode 100644 index 000000000..e5b8345d5 --- /dev/null +++ b/thirdparty/dumb/src/helpers/resample.inc @@ -0,0 +1,299 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * resample.inc - Resampling helper template. / / \ \ + * | < / \_ + * By Bob and entheh. | \/ /\ / + * \_ / > / + * In order to find a good trade-off between | \ / / + * speed and accuracy in this code, some tests | ' / + * were carried out regarding the behaviour of \__/ + * long long ints with gcc. The following code + * was tested: + * + * int a, b, c; + * c = ((long long)a * b) >> 16; + * + * DJGPP GCC Version 3.0.3 generated the following assembly language code for + * the multiplication and scaling, leaving the 32-bit result in EAX. + * + * movl -8(%ebp), %eax ; read one int into EAX + * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX + * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX + * + * Note that a 32*32->64 multiplication is performed, allowing for high + * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), + * so it is a minor concern when four multiplications are being performed + * (the cubic resampler). On the Pentium MMX and earlier, it takes four or + * more cycles, so this method is unsuitable for use in the low-quality + * resamplers. + * + * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, + * defined in dumb.h. We may investigate later what code MSVC generates, but + * if it seems too slow then we suggest you use a good compiler. + * + * FIXME: these comments are somewhat out of date now. + */ + + + +void dumb_reset_resampler(DUMB_RESAMPLER *resampler, SRCTYPE *src, int src_channels, int32 pos, int32 start, int32 end, int quality) +{ + int i; + resampler->src = src; + resampler->pos = pos; + resampler->subpos = 0; + resampler->start = start; + resampler->end = end; + resampler->dir = 1; + resampler->pickup = NULL; + resampler->pickup_data = NULL; + if (quality < 0) + { + resampler->quality = 0; + } + else if (quality > DUMB_RQ_N_LEVELS - 1) + { + resampler->quality = DUMB_RQ_N_LEVELS - 1; + } + else + { + resampler->quality = quality; + } + for (i = 0; i < src_channels*3; i++) resampler->X[i] = 0; + resampler->overshot = -1; + resampler->fir_resampler_ratio = 0; + resampler_clear(resampler->fir_resampler[0]); + resampler_clear(resampler->fir_resampler[1]); + resampler_set_quality(resampler->fir_resampler[0], resampler->quality - DUMB_RESAMPLER_BASE); + resampler_set_quality(resampler->fir_resampler[1], resampler->quality - DUMB_RESAMPLER_BASE); +} + + + +DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, int32 pos, int32 start, int32 end, int quality) +{ + DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler)); + if (!resampler) return NULL; + dumb_reset_resampler(resampler, src, src_channels, pos, start, end, quality); + return resampler; +} + + + +#define UPDATE_VOLUME( pvol, vol ) { \ + if (pvol) { \ + vol##r += vol##d; \ + if ((vol##d < 0 && vol##r <= vol##t) || \ + (vol##d > 0 && vol##r >= vol##t)) { \ + pvol->volume = pvol->target; \ + if ( pvol->declick_stage == 0 || \ + pvol->declick_stage >= 3) \ + pvol->declick_stage++; \ + pvol = NULL; \ + vol = MULSCV( vol##t, vol##m ); \ + } else { \ + vol = MULSCV( vol##r, vol##m ); \ + } \ + } \ +} + + + +/* Create mono source resampler. */ +#define SUFFIX2 _1 +#define SRC_CHANNELS 1 +#define DIVIDE_BY_SRC_CHANNELS(x) (int)(x) +#define COPYSRC(dstarray, dstindex, srcarray, srcindex) (dstarray)[dstindex] = (srcarray)[srcindex] +#define COPYSRC2(dstarray, dstindex, condition, srcarray, srcindex) (dstarray)[dstindex] = condition ? (srcarray)[srcindex] : 0 +#define MONO_DEST_VOLUME_PARAMETERS DUMB_VOLUME_RAMP_INFO * volume +#define MONO_DEST_VOLUME_VARIABLES vol, volr, vold, volt, volm +#define MONO_DEST_VOLUME_ZEROS 0, 0 +#define SET_MONO_DEST_VOLUME_VARIABLES { \ + if ( volume ) { \ + volr = xs_FloorToInt(volume->volume * 16777216.f); \ + vold = xs_FloorToInt(volume->delta * 16777216.f); \ + volt = xs_FloorToInt(volume->target * 16777216.f); \ + volm = xs_FloorToInt(volume->mix * 16777216.f); \ + vol = MULSCV( volr, volm ); \ + if ( volr == volt ) volume = NULL; \ + } else { \ + vol = 0; \ + vold = 0; \ + volt = 0; \ + volm = 0; \ + } \ +} +#define RETURN_MONO_DEST_VOLUME_VARIABLES if ( volume ) volume->volume = (float)volr / 16777216.0f +#define MONO_DEST_VOLUMES_ARE_ZERO (vol == 0 && volt == 0) +#define STEREO_DEST_MIX_ALIAS(op, upd, offset) { \ + int xm = x[offset]; \ + *dst++ op ALIAS(xm, lvol); \ + *dst++ op ALIAS(xm, rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#define STEREO_DEST_MIX_LINEAR(op, upd, o0, o1) { \ + int xm = LINEAR(x[o0], x[o1]); \ + *dst++ op MULSC(xm, lvol); \ + *dst++ op MULSC(xm, rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#define STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) { \ + int xm = CUBIC(x0[o0], x[o1], x[o2], x3[o3]); \ + *dst++ op CUBICVOL(xm, lvol); \ + *dst++ op CUBICVOL(xm, rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#define POKE_FIR(offset) { \ + resampler_write_sample( resampler->fir_resampler[0], FIR(x[offset]) ); \ +} +#define MONO_DEST_PEEK_FIR *dst = MULSC( resampler_get_sample( resampler->fir_resampler[0] ), vol ) +#define MONO_DEST_MIX_FIR { \ + *dst++ += MULSC( resampler_get_sample( resampler->fir_resampler[0] ), vol ); \ + UPDATE_VOLUME( volume, vol ); \ +} +#define ADVANCE_FIR resampler_remove_sample( resampler->fir_resampler[0], 1 ) +#define STEREO_DEST_PEEK_FIR { \ + int sample = resampler_get_sample( resampler->fir_resampler[0] ); \ + *dst++ = MULSC( sample, lvol ); \ + *dst++ = MULSC( sample, rvol ); \ +} +#define STEREO_DEST_MIX_FIR { \ + int sample = resampler_get_sample( resampler->fir_resampler[0] ); \ + *dst++ += MULSC( sample, lvol ); \ + *dst++ += MULSC( sample, rvol ); \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ +} +#include "resamp2.inc" + +/* Create stereo source resampler. */ +#define SUFFIX2 _2 +#define SRC_CHANNELS 2 +#define DIVIDE_BY_SRC_CHANNELS(x) (int)((x) >> 1) +#define COPYSRC(dstarray, dstindex, srcarray, srcindex) { \ + (dstarray)[(dstindex)*2] = (srcarray)[(srcindex)*2]; \ + (dstarray)[(dstindex)*2+1] = (srcarray)[(srcindex)*2+1]; \ +} +#define COPYSRC2(dstarray, dstindex, condition, srcarray, srcindex) { \ + if (condition) { \ + (dstarray)[(dstindex)*2] = (srcarray)[(srcindex)*2]; \ + (dstarray)[(dstindex)*2+1] = (srcarray)[(srcindex)*2+1]; \ + } else { \ + (dstarray)[(dstindex)*2] = 0; \ + (dstarray)[(dstindex)*2+1] = 0; \ + } \ +} + +#define MONO_DEST_VOLUME_PARAMETERS DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right +#define MONO_DEST_VOLUME_VARIABLES lvol, lvolr, lvold, lvolt, lvolm, rvol, rvolr, rvold, rvolt, rvolm +#define MONO_DEST_VOLUME_ZEROS 0, 0 +#define SET_MONO_DEST_VOLUME_VARIABLES { \ + if ( volume_left ) { \ + lvolr = xs_FloorToInt(volume_left->volume * 16777216.f); \ + lvold = xs_FloorToInt(volume_left->delta * 16777216.f); \ + lvolt = xs_FloorToInt(volume_left->target * 16777216.f); \ + lvolm = xs_FloorToInt(volume_left->mix * 16777216.f); \ + lvol = MULSCV( lvolr, lvolm ); \ + if ( lvolr == lvolt ) volume_left = NULL; \ + } else { \ + lvol = 0; \ + lvold = 0; \ + lvolt = 0; \ + lvolm = 0; \ + } \ + if ( volume_right ) { \ + rvolr = xs_FloorToInt(volume_right->volume * 16777216.f); \ + rvold = xs_FloorToInt(volume_right->delta * 16777216.f); \ + rvolt = xs_FloorToInt(volume_right->target * 16777216.f); \ + rvolm = xs_FloorToInt(volume_right->mix * 16777216.f); \ + rvol = MULSCV( rvolr, rvolm ); \ + if ( rvolr == rvolt ) volume_right = NULL; \ + } else { \ + rvol = 0; \ + rvold = 0; \ + rvolt = 0; \ + rvolm = 0; \ + } \ +} +#define RETURN_MONO_DEST_VOLUME_VARIABLES { \ + if ( volume_left ) volume_left->volume = (float)lvolr / 16777216.0f; \ + if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \ +} +#define MONO_DEST_VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0) +#define STEREO_DEST_MIX_ALIAS(op, upd, offset) { \ + *dst++ op ALIAS(x[(offset)*2], lvol); \ + *dst++ op ALIAS(x[(offset)*2+1], rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#define STEREO_DEST_MIX_LINEAR(op, upd, o0, o1) { \ + *dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol); \ + *dst++ op MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#define STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) { \ + *dst++ op CUBICVOL(CUBIC(x0[(o0)*2], x[(o1)*2], x[(o2)*2], x3[(o3)*2]), lvol); \ + *dst++ op CUBICVOL(CUBIC(x0[(o0)*2+1], x[(o1)*2+1], x[(o2)*2+1], x3[(o3)*2+1]), rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#define POKE_FIR(offset) { \ + resampler_write_sample( resampler->fir_resampler[0], FIR(x[(offset)*2+0]) ); \ + resampler_write_sample( resampler->fir_resampler[1], FIR(x[(offset)*2+1]) ); \ +} +#define MONO_DEST_PEEK_FIR { \ + *dst = MULSC( resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \ + MULSC( resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ +} +#define MONO_DEST_MIX_FIR { \ + *dst++ += MULSC( resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \ + MULSC( resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ +} +#define ADVANCE_FIR { \ + resampler_remove_sample( resampler->fir_resampler[0], 1 ); \ + resampler_remove_sample( resampler->fir_resampler[1], 1 ); \ +} +#define STEREO_DEST_PEEK_FIR { \ + *dst++ = MULSC( resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \ + *dst++ = MULSC( resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ +} +#define STEREO_DEST_MIX_FIR { \ + *dst++ += MULSC( resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \ + *dst++ += MULSC( resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ +} +#include "resamp2.inc" + + + +void dumb_end_resampler(DUMB_RESAMPLER *resampler) +{ + if (resampler) + free(resampler); +} + + + +#undef FIR +#undef CUBICVOL +#undef CUBIC +#undef LINEAR +#undef ALIAS +#undef SRCBITS +#undef SRCTYPE +#undef SUFFIX diff --git a/thirdparty/dumb/src/helpers/resampler.c b/thirdparty/dumb/src/helpers/resampler.c new file mode 100644 index 000000000..10b08f3f5 --- /dev/null +++ b/thirdparty/dumb/src/helpers/resampler.c @@ -0,0 +1,1512 @@ +#include +#include +#define _USE_MATH_DEFINES +#include +#if (defined(_M_IX86) || defined(__i386__) || defined(_M_X64) || defined(__amd64__)) +#include +#define RESAMPLER_SSE +#endif +#ifdef __APPLE__ +#include +#if TARGET_CPU_ARM || TARGET_CPU_ARM64 +#include +#define RESAMPLER_NEON +#endif +#endif + +#ifdef _MSC_VER +#define ALIGNED _declspec(align(16)) +#else +#define ALIGNED __attribute__((aligned(16))) +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#include "internal/resampler.h" + +enum { RESAMPLER_SHIFT = 10 }; +enum { RESAMPLER_SHIFT_EXTRA = 8 }; +enum { RESAMPLER_RESOLUTION = 1 << RESAMPLER_SHIFT }; +enum { RESAMPLER_RESOLUTION_EXTRA = 1 << (RESAMPLER_SHIFT + RESAMPLER_SHIFT_EXTRA) }; +enum { SINC_WIDTH = 16 }; +enum { SINC_SAMPLES = RESAMPLER_RESOLUTION * SINC_WIDTH }; +enum { CUBIC_SAMPLES = RESAMPLER_RESOLUTION * 4 }; + +typedef union bigint +{ + unsigned long long quad; +#ifndef __BIG_ENDIAN__ + struct { unsigned int lo, hi; }; +#else + struct { unsigned int hi, lo; }; +#endif +} bigint; + +// What works well on 32-bit can make for extra work on 64-bit +#if defined(_M_X64) || defined(__amd64__) || TARGET_CPU_ARM64 +#define CLEAR_HI(p) (p.quad &= 0xffffffffu) +#define ADD_HI(a,p) (a += p.quad >> 32) +#define PHASE_REDUCE(p) (int)(p.quad >> (32 - RESAMPLER_SHIFT)) +#else +#define CLEAR_HI(p) p.hi = 0 +#define ADD_HI(a,p) a += p.hi +// Should be equivalent to (int)(p.quad >> (32 - RESAMPLER_SHIFT)), +// since the high part should get zeroed after every sample. +#define PHASE_REDUCE(p) (p.lo >> (32 - RESAMPLER_SHIFT)) +#endif + +static const float RESAMPLER_BLEP_CUTOFF = 0.90f; +static const float RESAMPLER_BLAM_CUTOFF = 0.93f; +static const float RESAMPLER_SINC_CUTOFF = 0.999f; + +ALIGNED static float cubic_lut[CUBIC_SAMPLES]; + +static float sinc_lut[SINC_SAMPLES + 1]; +static float window_lut[SINC_SAMPLES + 1]; + +enum { resampler_buffer_size = SINC_WIDTH * 4 }; + +static int fEqual(const double b, const double a) +{ + return fabs(a - b) < 1.0e-6; +} + +static double sinc(double x) +{ + return fEqual(x, 0.0) ? 1.0 : sin(x * M_PI) / (x * M_PI); +} + +#ifdef RESAMPLER_SSE +#ifdef _MSC_VER +#include +#elif defined(__clang__) || defined(__GNUC__) +static inline void +__cpuid(int *data, int selector) +{ +#if defined(__PIC__) && defined(__i386__) + asm("xchgl %%ebx, %%esi; cpuid; xchgl %%ebx, %%esi" + : "=a" (data[0]), + "=S" (data[1]), + "=c" (data[2]), + "=d" (data[3]) + : "0" (selector)); +#elif defined(__PIC__) && defined(__amd64__) + asm("xchg{q} {%%}rbx, %q1; cpuid; xchg{q} {%%}rbx, %q1" + : "=a" (data[0]), + "=&r" (data[1]), + "=c" (data[2]), + "=d" (data[3]) + : "0" (selector)); +#else + asm("cpuid" + : "=a" (data[0]), + "=b" (data[1]), + "=c" (data[2]), + "=d" (data[3]) + : "0" (selector)); +#endif +} +#else +#define __cpuid(a,b) memset((a), 0, sizeof(int) * 4) +#endif + +static int query_cpu_feature_sse() { + int buffer[4]; + __cpuid(buffer,1); + if ((buffer[3]&(1<<25)) == 0) return 0; + return 1; +} + +static int resampler_has_sse = 0; +#endif + +void resampler_init(void) +{ + unsigned i; + double dx = (float)(SINC_WIDTH) / SINC_SAMPLES, x = 0.0; + for (i = 0; i < SINC_SAMPLES + 1; ++i, x += dx) + { + double y = x / SINC_WIDTH; +#if 0 + // Blackman + float window = 0.42659 - 0.49656 * cos(M_PI + M_PI * y) + 0.076849 * cos(2.0 * M_PI * y); +#elif 1 + // Nuttal 3 term + double window = 0.40897 + 0.5 * cos(M_PI * y) + 0.09103 * cos(2.0 * M_PI * y); +#elif 0 + // C.R.Helmrich's 2 term window + float window = 0.79445 * cos(0.5 * M_PI * y) + 0.20555 * cos(1.5 * M_PI * y); +#elif 0 + // Lanczos + float window = sinc(y); +#endif + sinc_lut[i] = (float)(fabs(x) < SINC_WIDTH ? sinc(x) : 0.0); + window_lut[i] = (float)window; + } + dx = 1.0 / RESAMPLER_RESOLUTION; + x = 0.0; + for (i = 0; i < RESAMPLER_RESOLUTION; ++i, x += dx) + { + cubic_lut[i*4] = (float)(-0.5 * x * x * x + x * x - 0.5 * x); + cubic_lut[i*4+1] = (float)( 1.5 * x * x * x - 2.5 * x * x + 1.0); + cubic_lut[i*4+2] = (float)(-1.5 * x * x * x + 2.0 * x * x + 0.5 * x); + cubic_lut[i*4+3] = (float)( 0.5 * x * x * x - 0.5 * x * x); + } +#ifdef RESAMPLER_SSE + resampler_has_sse = query_cpu_feature_sse(); +#endif +} + +typedef struct resampler +{ + int write_pos, write_filled; + int read_pos, read_filled; + bigint phase; + bigint phase_inc; + bigint inv_phase; + bigint inv_phase_inc; + unsigned char quality; + signed char delay_added; + signed char delay_removed; + double last_amp; + double accumulator; + float buffer_in[resampler_buffer_size * 2]; + float buffer_out[resampler_buffer_size + SINC_WIDTH * 2 - 1]; +} resampler; + +void * resampler_create(void) +{ + resampler * r = ( resampler * ) malloc( sizeof(resampler) ); + if ( !r ) return 0; + + r->write_pos = SINC_WIDTH - 1; + r->write_filled = 0; + r->read_pos = 0; + r->read_filled = 0; + r->phase.quad = 0; + r->phase_inc.quad = 0; + r->inv_phase.quad = 0; + r->inv_phase_inc.quad = 0; + r->quality = RESAMPLER_QUALITY_MAX; + r->delay_added = -1; + r->delay_removed = -1; + r->last_amp = 0; + r->accumulator = 0; + memset( r->buffer_in, 0, sizeof(r->buffer_in) ); + memset( r->buffer_out, 0, sizeof(r->buffer_out) ); + + return r; +} + +void resampler_delete(void * _r) +{ + free( _r ); +} + +void * resampler_dup(const void * _r) +{ + void * r_out = malloc( sizeof(resampler) ); + if ( !r_out ) return 0; + + resampler_dup_inplace(r_out, _r); + + return r_out; +} + +void resampler_dup_inplace(void *_d, const void *_s) +{ + const resampler * r_in = ( const resampler * ) _s; + resampler * r_out = ( resampler * ) _d; + + r_out->write_pos = r_in->write_pos; + r_out->write_filled = r_in->write_filled; + r_out->read_pos = r_in->read_pos; + r_out->read_filled = r_in->read_filled; + r_out->phase = r_in->phase; + r_out->phase_inc = r_in->phase_inc; + r_out->inv_phase = r_in->inv_phase; + r_out->inv_phase_inc = r_in->inv_phase_inc; + r_out->quality = r_in->quality; + r_out->delay_added = r_in->delay_added; + r_out->delay_removed = r_in->delay_removed; + r_out->last_amp = r_in->last_amp; + r_out->accumulator = r_in->accumulator; + memcpy( r_out->buffer_in, r_in->buffer_in, sizeof(r_in->buffer_in) ); + memcpy( r_out->buffer_out, r_in->buffer_out, sizeof(r_in->buffer_out) ); +} + +void resampler_set_quality(void *_r, int quality) +{ + resampler * r = ( resampler * ) _r; + if (quality < RESAMPLER_QUALITY_MIN) + quality = RESAMPLER_QUALITY_MIN; + else if (quality > RESAMPLER_QUALITY_MAX) + quality = RESAMPLER_QUALITY_MAX; + if ( r->quality != quality ) + { + if ( quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLEP || + quality == RESAMPLER_QUALITY_BLAM || r->quality == RESAMPLER_QUALITY_BLAM ) + { + r->read_pos = 0; + r->read_filled = 0; + r->last_amp = 0; + r->accumulator = 0; + memset( r->buffer_out, 0, sizeof(r->buffer_out) ); + } + r->delay_added = -1; + r->delay_removed = -1; + } + r->quality = (unsigned char)quality; +} + +int resampler_get_free_count(void *_r) +{ + resampler * r = ( resampler * ) _r; + return resampler_buffer_size - r->write_filled; +} + +static int resampler_min_filled(resampler *r) +{ + switch (r->quality) + { + default: + case RESAMPLER_QUALITY_ZOH: + case RESAMPLER_QUALITY_BLEP: + return 1; + + case RESAMPLER_QUALITY_LINEAR: + case RESAMPLER_QUALITY_BLAM: + return 2; + + case RESAMPLER_QUALITY_CUBIC: + return 4; + + case RESAMPLER_QUALITY_SINC: + return SINC_WIDTH * 2; + } +} + +static int resampler_input_delay(resampler *r) +{ + switch (r->quality) + { + default: + case RESAMPLER_QUALITY_ZOH: + case RESAMPLER_QUALITY_BLEP: + case RESAMPLER_QUALITY_LINEAR: + case RESAMPLER_QUALITY_BLAM: + return 0; + + case RESAMPLER_QUALITY_CUBIC: + return 1; + + case RESAMPLER_QUALITY_SINC: + return SINC_WIDTH - 1; + } +} + +static int resampler_output_delay(resampler *r) +{ + switch (r->quality) + { + default: + case RESAMPLER_QUALITY_ZOH: + case RESAMPLER_QUALITY_LINEAR: + case RESAMPLER_QUALITY_CUBIC: + case RESAMPLER_QUALITY_SINC: + return 0; + + case RESAMPLER_QUALITY_BLEP: + case RESAMPLER_QUALITY_BLAM: + return SINC_WIDTH - 1; + } +} + +int resampler_ready(void *_r) +{ + resampler * r = ( resampler * ) _r; + return r->write_filled > resampler_min_filled(r); +} + +void resampler_clear(void *_r) +{ + resampler * r = ( resampler * ) _r; + r->write_pos = SINC_WIDTH - 1; + r->write_filled = 0; + r->read_pos = 0; + r->read_filled = 0; + r->phase.quad = 0; + r->delay_added = -1; + r->delay_removed = -1; + memset(r->buffer_in, 0, (SINC_WIDTH - 1) * sizeof(r->buffer_in[0])); + memset(r->buffer_in + resampler_buffer_size, 0, (SINC_WIDTH - 1) * sizeof(r->buffer_in[0])); + if (r->quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLAM) + { + r->inv_phase.quad = 0; + r->last_amp = 0; + r->accumulator = 0; + memset(r->buffer_out, 0, sizeof(r->buffer_out)); + } +} + +void resampler_set_rate(void *_r, double new_factor) +{ + resampler * r = ( resampler * ) _r; + r->phase_inc.quad = (long long)(new_factor * 0x100000000ll); + new_factor = 1.0 / new_factor; + r->inv_phase_inc.quad = (long long)(new_factor * 0x100000000ll); +} + +void resampler_write_sample(void *_r, short s) +{ + resampler * r = ( resampler * ) _r; + + if ( r->delay_added < 0 ) + { + r->delay_added = 0; + r->write_filled = resampler_input_delay( r ); + } + + if ( r->write_filled < resampler_buffer_size ) + { + float s32 = s; + s32 *= 256.0; + + r->buffer_in[ r->write_pos ] = s32; + r->buffer_in[ r->write_pos + resampler_buffer_size ] = s32; + + ++r->write_filled; + + r->write_pos = ( r->write_pos + 1 ) % resampler_buffer_size; + } +} + +void resampler_write_sample_fixed(void *_r, int s, unsigned char depth) +{ + resampler * r = ( resampler * ) _r; + + if ( r->delay_added < 0 ) + { + r->delay_added = 0; + r->write_filled = resampler_input_delay( r ); + } + + if ( r->write_filled < resampler_buffer_size ) + { + double s32 = s; + s32 /= (double)(1 << (depth - 1)); + + r->buffer_in[ r->write_pos ] = (float)s32; + r->buffer_in[ r->write_pos + resampler_buffer_size ] = (float)s32; + + ++r->write_filled; + + r->write_pos = ( r->write_pos + 1 ) % resampler_buffer_size; + } +} + +static int resampler_run_zoh(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= 1; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + bigint phase = r->phase; + bigint phase_inc = r->phase_inc; + do + { + if ( out >= out_end ) + break; + + *out++ = *in; + + phase.quad += phase_inc.quad; + + ADD_HI(in, phase); + + CLEAR_HI(phase); + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} + +#ifndef RESAMPLER_NEON +static int resampler_run_blep(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= 1; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + double last_amp = r->last_amp; + bigint inv_phase = r->inv_phase; + bigint inv_phase_inc = r->inv_phase_inc; + + const int step = (int)(RESAMPLER_BLEP_CUTOFF * RESAMPLER_RESOLUTION); + const int window_step = RESAMPLER_RESOLUTION; + + do + { + double sample; + + if ( out + SINC_WIDTH * 2 > out_end ) + break; + + sample = *in++ - last_amp; + + if (sample) + { + double kernel[SINC_WIDTH * 2], kernel_sum = 0.0f; + int phase_reduced = PHASE_REDUCE(inv_phase); + int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION; + int i = SINC_WIDTH; + + for (; i >= -SINC_WIDTH + 1; --i) + { + int pos = i * step; + int window_pos = i * window_step; + kernel_sum += kernel[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)]; + } + last_amp += sample; + sample /= kernel_sum; + for (i = 0; i < SINC_WIDTH * 2; ++i) + out[i] += (float)(sample * kernel[i]); + } + + inv_phase.quad += inv_phase_inc.quad; + + ADD_HI(out, inv_phase); + + CLEAR_HI(inv_phase); + } + while ( in < in_end ); + + r->inv_phase = inv_phase; + r->last_amp = last_amp; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +#ifdef RESAMPLER_SSE +static int resampler_run_blep_sse(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= 1; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + double last_amp = r->last_amp; + bigint inv_phase = r->inv_phase; + bigint inv_phase_inc = r->inv_phase_inc; + + const int step = (int)(RESAMPLER_BLEP_CUTOFF * RESAMPLER_RESOLUTION); + const int window_step = RESAMPLER_RESOLUTION; + + do + { + double sample; + + if ( out + SINC_WIDTH * 2 > out_end ) + break; + + sample = *in++ - last_amp; + + if (sample) + { + float kernel_sum = 0.0f; + __m128 kernel[SINC_WIDTH / 2]; + __m128 temp1, temp2; + __m128 samplex; + float *kernelf = (float*)(&kernel); + int phase_reduced = PHASE_REDUCE(inv_phase); + int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION; + int i = SINC_WIDTH; + + for (; i >= -SINC_WIDTH + 1; --i) + { + int pos = i * step; + int window_pos = i * window_step; + kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)]; + } + last_amp += sample; + sample /= kernel_sum; + samplex = _mm_set1_ps( (float)sample ); + for (i = 0; i < SINC_WIDTH / 2; ++i) + { + temp1 = _mm_load_ps( (const float *)( kernel + i ) ); + temp1 = _mm_mul_ps( temp1, samplex ); + temp2 = _mm_loadu_ps( (const float *) out + i * 4 ); + temp1 = _mm_add_ps( temp1, temp2 ); + _mm_storeu_ps( (float *) out + i * 4, temp1 ); + } + } + + inv_phase.quad += inv_phase_inc.quad; + + ADD_HI(out, inv_phase); + + CLEAR_HI(inv_phase); + } + while ( in < in_end ); + + r->inv_phase = inv_phase; + r->last_amp = last_amp; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +#ifdef RESAMPLER_NEON +static int resampler_run_blep(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= 1; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + float last_amp = r->last_amp; + bigint inv_phase = r->inv_phase; + bigint inv_phase_inc = r->inv_phase_inc; + + const int step = RESAMPLER_BLEP_CUTOFF * RESAMPLER_RESOLUTION; + const int window_step = RESAMPLER_RESOLUTION; + + do + { + float sample; + + if ( out + SINC_WIDTH * 2 > out_end ) + break; + + sample = *in++ - last_amp; + + if (sample) + { + float kernel_sum = 0.0f; + float32x4_t kernel[SINC_WIDTH / 2]; + float32x4_t temp1, temp2; + float32x4_t samplex; + float *kernelf = (float*)(&kernel); + int phase_reduced = PHASE_REDUCE(inv_phase); + int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION; + int i = SINC_WIDTH; + + for (; i >= -SINC_WIDTH + 1; --i) + { + int pos = i * step; + int window_pos = i * window_step; + kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)]; + } + last_amp += sample; + sample /= kernel_sum; + samplex = vdupq_n_f32(sample); + for (i = 0; i < SINC_WIDTH / 2; ++i) + { + temp1 = vld1q_f32( (const float32_t *)( kernel + i ) ); + temp2 = vld1q_f32( (const float32_t *) out + i * 4 ); + temp2 = vmlaq_f32( temp2, temp1, samplex ); + vst1q_f32( (float32_t *) out + i * 4, temp2 ); + } + } + + inv_phase.quad += inv_phase_inc.quad; + + ADD_HI(out, inv_phase); + + CLEAR_HI(inv_phase); + } + while ( in < in_end ); + + r->inv_phase = inv_phase; + r->last_amp = last_amp; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +static int resampler_run_linear(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= 2; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + bigint phase = r->phase; + bigint phase_inc = r->phase_inc; + + do + { + if ( out >= out_end ) + break; + + *out++ = (float)(in[0] + (in[1] - in[0]) * phase.lo * (1.f / 0x100000000ll)); + + phase.quad += phase_inc.quad; + + ADD_HI(in, phase); + + CLEAR_HI(phase); + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} + +#ifndef RESAMPLER_NEON +static int resampler_run_blam(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= 2; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + double last_amp = r->last_amp; + bigint phase = r->phase; + bigint phase_inc = r->phase_inc; + bigint inv_phase = r->inv_phase; + bigint inv_phase_inc = r->inv_phase_inc; + + const int step = (int)(RESAMPLER_BLAM_CUTOFF * RESAMPLER_RESOLUTION); + const int window_step = RESAMPLER_RESOLUTION; + + do + { + double sample; + + if ( out + SINC_WIDTH * 2 > out_end ) + break; + + sample = in[0]; + if (phase_inc.quad < 0x100000000ll) + sample += (in[1] - in[0]) * phase.quad * (1.f / 0x100000000ll); + sample -= last_amp; + + if (sample) + { + double kernel[SINC_WIDTH * 2], kernel_sum = 0.0f; + int phase_reduced = PHASE_REDUCE(inv_phase); + int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION; + int i = SINC_WIDTH; + + for (; i >= -SINC_WIDTH + 1; --i) + { + int pos = i * step; + int window_pos = i * window_step; + kernel_sum += kernel[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)]; + } + last_amp += sample; + sample /= kernel_sum; + for (i = 0; i < SINC_WIDTH * 2; ++i) + out[i] += (float)(sample * kernel[i]); + } + + if (inv_phase_inc.quad < 0x100000000ll) + { + ++in; + inv_phase.quad += inv_phase_inc.quad; + ADD_HI(out, inv_phase); + CLEAR_HI(inv_phase); + } + else + { + phase.quad += phase_inc.quad; + ++out; + ADD_HI(in, phase); + CLEAR_HI(phase); + } + } + while ( in < in_end ); + + r->phase = phase; + r->inv_phase = inv_phase; + r->last_amp = last_amp; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +#ifdef RESAMPLER_SSE +static int resampler_run_blam_sse(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= 2; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + double last_amp = r->last_amp; + bigint phase = r->phase; + bigint phase_inc = r->phase_inc; + bigint inv_phase = r->inv_phase; + bigint inv_phase_inc = r->inv_phase_inc; + + const int step = (int)(RESAMPLER_BLAM_CUTOFF * RESAMPLER_RESOLUTION); + const int window_step = RESAMPLER_RESOLUTION; + + do + { + double sample; + + if ( out + SINC_WIDTH * 2 > out_end ) + break; + + sample = in[0]; + if (phase_inc.quad < 0x100000000ll) + { + sample += (in[1] - in[0]) * phase.quad * (1.f / 0x100000000ll); + } + sample -= last_amp; + + if (sample) + { + float kernel_sum = 0.0f; + __m128 kernel[SINC_WIDTH / 2]; + __m128 temp1, temp2; + __m128 samplex; + float *kernelf = (float*)(&kernel); + int phase_reduced = PHASE_REDUCE(inv_phase); + int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION; + int i = SINC_WIDTH; + + for (; i >= -SINC_WIDTH + 1; --i) + { + int pos = i * step; + int window_pos = i * window_step; + kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)]; + } + last_amp += sample; + sample /= kernel_sum; + samplex = _mm_set1_ps( (float)sample ); + for (i = 0; i < SINC_WIDTH / 2; ++i) + { + temp1 = _mm_load_ps( (const float *)( kernel + i ) ); + temp1 = _mm_mul_ps( temp1, samplex ); + temp2 = _mm_loadu_ps( (const float *) out + i * 4 ); + temp1 = _mm_add_ps( temp1, temp2 ); + _mm_storeu_ps( (float *) out + i * 4, temp1 ); + } + } + + if (inv_phase_inc.quad < 0x100000000ll) + { + ++in; + inv_phase.quad += inv_phase_inc.quad; + ADD_HI(out, inv_phase); + CLEAR_HI(inv_phase); + } + else + { + phase.quad += phase_inc.quad; + ++out; + + if (phase.quad >= 0x100000000ll) + { + ++in; + CLEAR_HI(phase); + } + } + } + while ( in < in_end ); + + r->phase = phase; + r->inv_phase = inv_phase; + r->last_amp = last_amp; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +#ifdef RESAMPLER_NEON +static int resampler_run_blam(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= 2; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + float last_amp = r->last_amp; + bigint phase = r->phase; + bigint phase_inc = r->phase_inc; + bigint inv_phase = r->inv_phase; + bigint inv_phase_inc = r->inv_phase_inc; + + const int step = RESAMPLER_BLAM_CUTOFF * RESAMPLER_RESOLUTION; + const int window_step = RESAMPLER_RESOLUTION; + + do + { + float sample; + + if ( out + SINC_WIDTH * 2 > out_end ) + break; + + sample = in[0]; + if (phase_inc.quad < 0x100000000ll) + sample += (in[1] - in[0]) * phase.quad; + sample -= last_amp; + + if (sample) + { + float kernel_sum = 0.0; + float32x4_t kernel[SINC_WIDTH / 2]; + float32x4_t temp1, temp2; + float32x4_t samplex; + float *kernelf = (float*)(&kernel); + int phase_reduced = PHASE_REDUCE(inv_phase); + int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION; + int i = SINC_WIDTH; + + for (; i >= -SINC_WIDTH + 1; --i) + { + int pos = i * step; + int window_pos = i * window_step; + kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)]; + } + last_amp += sample; + sample /= kernel_sum; + samplex = vdupq_n_f32(sample); + for (i = 0; i < SINC_WIDTH / 2; ++i) + { + temp1 = vld1q_f32( (const float32_t *)( kernel + i ) ); + temp2 = vld1q_f32( (const float32_t *) out + i * 4 ); + temp2 = vmlaq_f32( temp2, temp1, samplex ); + vst1q_f32( (float32_t *) out + i * 4, temp2 ); + } + } + + if (inv_phase_inc.quad < 0x100000000ll) + { + ++in; + inv_phase.quad += inv_phase_inc.quad; + ADD_HI(out, inv_phase); + CLEAR_HI(inv_phase); + } + else + { + phase.quad += phase_inc.quad; + ++out; + + if (phase.quad >= 0x100000000ll) + { + ++in; + CLEAR_HI(phase); + } + } + } + while ( in < in_end ); + + r->phase = phase; + r->inv_phase = inv_phase; + r->last_amp = last_amp; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +#ifndef RESAMPLER_NEON +static int resampler_run_cubic(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= 4; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + bigint phase = r->phase; + bigint phase_inc = r->phase_inc; + + do + { + float * kernel; + int i; + float sample; + + if ( out >= out_end ) + break; + + kernel = cubic_lut + PHASE_REDUCE(phase) * 4; + + for (sample = 0, i = 0; i < 4; ++i) + sample += in[i] * kernel[i]; + *out++ = sample; + + phase.quad += phase_inc.quad; + + ADD_HI(in, phase); + + CLEAR_HI(phase); + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +#ifdef RESAMPLER_SSE +static int resampler_run_cubic_sse(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= 4; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + bigint phase = r->phase; + bigint phase_inc = r->phase_inc; + + do + { + __m128 temp1, temp2; + __m128 samplex = _mm_setzero_ps(); + + if ( out >= out_end ) + break; + + temp1 = _mm_loadu_ps( (const float *)( in ) ); + temp2 = _mm_load_ps( (const float *)( cubic_lut + PHASE_REDUCE(phase) * 4 ) ); + temp1 = _mm_mul_ps( temp1, temp2 ); + samplex = _mm_add_ps( samplex, temp1 ); + temp1 = _mm_movehl_ps( temp1, samplex ); + samplex = _mm_add_ps( samplex, temp1 ); + temp1 = samplex; + temp1 = _mm_shuffle_ps( temp1, samplex, _MM_SHUFFLE(0, 0, 0, 1) ); + samplex = _mm_add_ps( samplex, temp1 ); + _mm_store_ss( out, samplex ); + ++out; + + phase.quad += phase_inc.quad; + + ADD_HI(in, phase); + + CLEAR_HI(phase); + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +#ifdef RESAMPLER_NEON +static int resampler_run_cubic(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= 4; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + bigint phase = r->phase; + bigint phase_inc = r->phase_inc; + + do + { + float32x4_t temp1, temp2; + float32x2_t half; + + if ( out >= out_end ) + break; + + temp1 = vld1q_f32( (const float32_t *)( in ) ); + temp2 = vld1q_f32( (const float32_t *)( cubic_lut + PHASE_REDUCE(phase) * 4 ) ); + temp1 = vmulq_f32( temp1, temp2 ); + half = vadd_f32(vget_high_f32(temp1), vget_low_f32(temp1)); + *out++ = vget_lane_f32(vpadd_f32(half, half), 0); + + phase.quad += phase_inc.quad; + + ADD_HI(in, phase); + + CLEAR_HI(phase); + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +#ifndef RESAMPLER_NEON +static int resampler_run_sinc(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= SINC_WIDTH * 2; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + bigint phase = r->phase; + bigint phase_inc = r->phase_inc; + + int step = phase_inc.quad > 0x100000000ll ? + (int)(RESAMPLER_RESOLUTION / (phase_inc.quad * (1.f / 0x100000000ll)) * RESAMPLER_SINC_CUTOFF) : + (int)(RESAMPLER_RESOLUTION * RESAMPLER_SINC_CUTOFF); + int window_step = RESAMPLER_RESOLUTION; + + do + { + double kernel[SINC_WIDTH * 2], kernel_sum = 0.0; + int i = SINC_WIDTH; + int phase_reduced = PHASE_REDUCE(phase); + int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION; + float sample; + + if ( out >= out_end ) + break; + + for (; i >= -SINC_WIDTH + 1; --i) + { + int pos = i * step; + int window_pos = i * window_step; + kernel_sum += kernel[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)]; + } + for (sample = 0, i = 0; i < SINC_WIDTH * 2; ++i) + sample += (float)(in[i] * kernel[i]); + *out++ = (float)(sample / kernel_sum); + + phase.quad += phase_inc.quad; + + ADD_HI(in, phase); + + CLEAR_HI(phase); + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +#ifdef RESAMPLER_SSE +static int resampler_run_sinc_sse(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= SINC_WIDTH * 2; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + bigint phase = r->phase; + bigint phase_inc = r->phase_inc; + + int step = phase_inc.quad > 0x100000000ll ? + (int)(RESAMPLER_RESOLUTION / (phase_inc.quad * (1.f / 0x100000000ll)) * RESAMPLER_SINC_CUTOFF) : + (int)(RESAMPLER_RESOLUTION * RESAMPLER_SINC_CUTOFF); + int window_step = RESAMPLER_RESOLUTION; + + do + { + // accumulate in extended precision + float kernel_sum = 0.0; + __m128 kernel[SINC_WIDTH / 2]; + __m128 temp1, temp2; + __m128 samplex = _mm_setzero_ps(); + float *kernelf = (float*)(&kernel); + int i = SINC_WIDTH; + int phase_reduced = PHASE_REDUCE(phase); + int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION; + + if ( out >= out_end ) + break; + + for (; i >= -SINC_WIDTH + 1; --i) + { + int pos = i * step; + int window_pos = i * window_step; + kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)]; + } + for (i = 0; i < SINC_WIDTH / 2; ++i) + { + temp1 = _mm_loadu_ps( (const float *)( in + i * 4 ) ); + temp2 = _mm_load_ps( (const float *)( kernel + i ) ); + temp1 = _mm_mul_ps( temp1, temp2 ); + samplex = _mm_add_ps( samplex, temp1 ); + } + kernel_sum = 1.0f / kernel_sum; + temp1 = _mm_movehl_ps( temp1, samplex ); + samplex = _mm_add_ps( samplex, temp1 ); + temp1 = samplex; + temp1 = _mm_shuffle_ps( temp1, samplex, _MM_SHUFFLE(0, 0, 0, 1) ); + samplex = _mm_add_ps( samplex, temp1 ); + temp1 = _mm_set_ss( kernel_sum ); + samplex = _mm_mul_ps( samplex, temp1 ); + _mm_store_ss( out, samplex ); + ++out; + + phase.quad += phase_inc.quad; + + ADD_HI(in, phase); + + CLEAR_HI(phase); + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +#ifdef RESAMPLER_NEON +static int resampler_run_sinc(resampler * r, float ** out_, float * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= SINC_WIDTH * 2; + if ( in_size > 0 ) + { + float* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + bigint phase = r->phase; + bigint phase_inc = r->phase_inc; + + int step = phase_inc.quad > 0x100000000ll ? + (int)(RESAMPLER_RESOLUTION / (phase_inc.quad * (1.f / 0x100000000ll)) * RESAMPLER_SINC_CUTOFF) : + (int)(RESAMPLER_RESOLUTION * RESAMPLER_SINC_CUTOFF); + int window_step = RESAMPLER_RESOLUTION; + + do + { + // accumulate in extended precision + float kernel_sum = 0.0; + float32x4_t kernel[SINC_WIDTH / 2]; + float32x4_t temp1, temp2; + float32x4_t samplex = {0}; + float32x2_t half; + float *kernelf = (float*)(&kernel); + int i = SINC_WIDTH; + int phase_reduced = PHASE_REDUCE(phase); + int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION; + + if ( out >= out_end ) + break; + + for (; i >= -SINC_WIDTH + 1; --i) + { + int pos = i * step; + int window_pos = i * window_step; + kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)]; + } + for (i = 0; i < SINC_WIDTH / 2; ++i) + { + temp1 = vld1q_f32( (const float32_t *)( in + i * 4 ) ); + temp2 = vld1q_f32( (const float32_t *)( kernel + i ) ); + samplex = vmlaq_f32( samplex, temp1, temp2 ); + } + kernel_sum = 1.0 / kernel_sum; + samplex = vmulq_f32(samplex, vmovq_n_f32(kernel_sum)); + half = vadd_f32(vget_high_f32(samplex), vget_low_f32(samplex)); + *out++ = vget_lane_f32(vpadd_f32(half, half), 0); + + phase.quad += phase_inc.quad; + + ADD_HI(in, phase); + + CLEAR_HI(phase); + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = (int)(in - in_); + + r->write_filled -= used; + } + + return used; +} +#endif + +static void resampler_fill(resampler * r) +{ + int min_filled = resampler_min_filled(r); + int quality = r->quality; + while ( r->write_filled > min_filled && + r->read_filled < resampler_buffer_size ) + { + int write_pos = ( r->read_pos + r->read_filled ) % resampler_buffer_size; + int write_size = resampler_buffer_size - write_pos; + float * out = r->buffer_out + write_pos; + if ( write_size > ( resampler_buffer_size - r->read_filled ) ) + write_size = resampler_buffer_size - r->read_filled; + switch (quality) + { + case RESAMPLER_QUALITY_ZOH: + resampler_run_zoh( r, &out, out + write_size ); + break; + + case RESAMPLER_QUALITY_BLEP: + { + int used; + int write_extra = 0; + if ( write_pos >= r->read_pos ) + write_extra = r->read_pos; + if ( write_extra > SINC_WIDTH * 2 - 1 ) + write_extra = SINC_WIDTH * 2 - 1; + memcpy( r->buffer_out + resampler_buffer_size, r->buffer_out, write_extra * sizeof(r->buffer_out[0]) ); +#ifdef RESAMPLER_SSE + if ( resampler_has_sse ) + used = resampler_run_blep_sse( r, &out, out + write_size + write_extra ); + else +#endif + used = resampler_run_blep( r, &out, out + write_size + write_extra ); + memcpy( r->buffer_out, r->buffer_out + resampler_buffer_size, write_extra * sizeof(r->buffer_out[0]) ); + if (!used) + return; + break; + } + + case RESAMPLER_QUALITY_LINEAR: + resampler_run_linear( r, &out, out + write_size ); + break; + + case RESAMPLER_QUALITY_BLAM: + { + float * out_ = out; + int write_extra = 0; + if ( write_pos >= r->read_pos ) + write_extra = r->read_pos; + if ( write_extra > SINC_WIDTH * 2 - 1 ) + write_extra = SINC_WIDTH * 2 - 1; + memcpy( r->buffer_out + resampler_buffer_size, r->buffer_out, write_extra * sizeof(r->buffer_out[0]) ); +#ifdef RESAMPLER_SSE + if ( resampler_has_sse ) + resampler_run_blam_sse( r, &out, out + write_size + write_extra ); + else +#endif + resampler_run_blam( r, &out, out + write_size + write_extra ); + memcpy( r->buffer_out, r->buffer_out + resampler_buffer_size, write_extra * sizeof(r->buffer_out[0]) ); + if ( out == out_ ) + return; + break; + } + + case RESAMPLER_QUALITY_CUBIC: +#ifdef RESAMPLER_SSE + if ( resampler_has_sse ) + resampler_run_cubic_sse( r, &out, out + write_size ); + else +#endif + resampler_run_cubic( r, &out, out + write_size ); + break; + + case RESAMPLER_QUALITY_SINC: +#ifdef RESAMPLER_SSE + if ( resampler_has_sse ) + resampler_run_sinc_sse( r, &out, out + write_size ); + else +#endif + resampler_run_sinc( r, &out, out + write_size ); + break; + } + r->read_filled += (int)(out - r->buffer_out - write_pos); + } +} + +static void resampler_fill_and_remove_delay(resampler * r) +{ + resampler_fill( r ); + if ( r->delay_removed < 0 ) + { + int delay = resampler_output_delay( r ); + r->delay_removed = 0; + while ( delay-- ) + resampler_remove_sample( r, 1 ); + } +} + +int resampler_get_sample_count(void *_r) +{ + resampler * r = ( resampler * ) _r; + if ( r->read_filled < 1 && ((r->quality != RESAMPLER_QUALITY_BLEP && r->quality != RESAMPLER_QUALITY_BLAM) || r->inv_phase_inc.quad)) + resampler_fill_and_remove_delay( r ); + return r->read_filled; +} + +int resampler_get_sample(void *_r) +{ + resampler * r = ( resampler * ) _r; + if ( r->read_filled < 1 && r->phase_inc.quad) + resampler_fill_and_remove_delay( r ); + if ( r->read_filled < 1 ) + return 0; + if ( r->quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLAM ) + return (int)(r->buffer_out[ r->read_pos ] + r->accumulator); + else + return (int)r->buffer_out[ r->read_pos ]; +} + +float resampler_get_sample_float(void *_r) +{ + resampler * r = ( resampler * ) _r; + if ( r->read_filled < 1 && r->phase_inc.quad) + resampler_fill_and_remove_delay( r ); + if ( r->read_filled < 1 ) + return 0; + if ( r->quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLAM ) + return (float)(r->buffer_out[ r->read_pos ] + r->accumulator); + else + return r->buffer_out[ r->read_pos ]; +} + +void resampler_remove_sample(void *_r, int decay) +{ + resampler * r = ( resampler * ) _r; + if ( r->read_filled > 0 ) + { + if ( r->quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLAM ) + { + r->accumulator += r->buffer_out[ r->read_pos ]; + r->buffer_out[ r->read_pos ] = 0; + if (decay) + { + r->accumulator -= r->accumulator * (1.0f / 8192.0f); + if (fabs(r->accumulator) < 1e-20f) + r->accumulator = 0; + } + } + --r->read_filled; + r->read_pos = ( r->read_pos + 1 ) % resampler_buffer_size; + } +} diff --git a/thirdparty/dumb/src/helpers/riff.c b/thirdparty/dumb/src/helpers/riff.c new file mode 100644 index 000000000..6589d12ff --- /dev/null +++ b/thirdparty/dumb/src/helpers/riff.c @@ -0,0 +1,87 @@ +#include "dumb.h" +#include "internal/riff.h" + +#include +#include + +struct riff * riff_parse( DUMBFILE * f, int32 offset, int32 size, unsigned proper ) +{ + unsigned stream_size; + struct riff * stream; + + + if ( size < 8 ) return 0; + + if ( dumbfile_seek(f, offset, DFS_SEEK_SET) ) return 0; + if ( dumbfile_mgetl(f) != DUMB_ID('R','I','F','F') ) return 0; + + stream_size = dumbfile_igetl(f); + if ( stream_size + 8 > (unsigned)size ) return 0; + if ( stream_size < 4 ) return 0; + + stream = (struct riff *) malloc( sizeof( struct riff ) ); + if ( ! stream ) return 0; + + stream->type = dumbfile_mgetl(f); + stream->chunk_count = 0; + stream->chunks = 0; + + stream_size -= 4; + + while ( stream_size && !dumbfile_error(f) ) + { + struct riff_chunk * chunk; + if ( stream_size < 8 ) break; + stream->chunks = ( struct riff_chunk * ) realloc( stream->chunks, ( stream->chunk_count + 1 ) * sizeof( struct riff_chunk ) ); + if ( ! stream->chunks ) break; + chunk = stream->chunks + stream->chunk_count; + chunk->type = dumbfile_mgetl(f); + chunk->size = dumbfile_igetl(f); + chunk->offset = dumbfile_pos(f); + stream_size -= 8; + if ( stream_size < chunk->size ) break; + if ( chunk->type == DUMB_ID('R','I','F','F') ) + { + chunk->nested = riff_parse( f, chunk->offset - 8, chunk->size + 8, proper ); + if ( ! chunk->nested ) break; + } + else + { + chunk->nested = 0; + } + dumbfile_seek(f, chunk->offset + chunk->size, DFS_SEEK_SET); + stream_size -= chunk->size; + if ( proper && ( chunk->size & 1 ) ) + { + dumbfile_skip(f, 1); + -- stream_size; + } + ++stream->chunk_count; + } + + if ( stream_size ) + { + riff_free( stream ); + stream = 0; + } + + return stream; +} + +void riff_free( struct riff * stream ) +{ + if ( stream ) + { + if ( stream->chunks ) + { + unsigned i; + for ( i = 0; i < stream->chunk_count; ++i ) + { + struct riff_chunk * chunk = stream->chunks + i; + if ( chunk->nested ) riff_free( chunk->nested ); + } + free( stream->chunks ); + } + free( stream ); + } +} diff --git a/thirdparty/dumb/src/helpers/sampbuf.c b/thirdparty/dumb/src/helpers/sampbuf.c new file mode 100644 index 000000000..ea30d506f --- /dev/null +++ b/thirdparty/dumb/src/helpers/sampbuf.c @@ -0,0 +1,64 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * sampbuf.c - Helper for allocating sample / / \ \ + * buffers. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include "dumb.h" + + + +/* DEPRECATED */ +sample_t **create_sample_buffer(int n_channels, int32 length) +{ + int i; + sample_t **samples = malloc(n_channels * sizeof(*samples)); + if (!samples) return NULL; + samples[0] = malloc(n_channels * length * sizeof(*samples[0])); + if (!samples[0]) { + free(samples); + return NULL; + } + for (i = 1; i < n_channels; i++) samples[i] = samples[i-1] + length; + return samples; +} + + + +sample_t **DUMBEXPORT allocate_sample_buffer(int n_channels, int32 length) +{ + int i; + sample_t **samples = malloc(((n_channels + 1) >> 1) * sizeof(*samples)); + if (!samples) return NULL; + samples[0] = malloc(n_channels * length * sizeof(*samples[0])); + if (!samples[0]) { + free(samples); + return NULL; + } + for (i = 1; i < (n_channels + 1) >> 1; i++) samples[i] = samples[i-1] + length*2; + return samples; +} + + + +void DUMBEXPORT destroy_sample_buffer(sample_t **samples) +{ + if (samples) { + free(samples[0]); + free(samples); + } +} diff --git a/thirdparty/dumb/src/helpers/silence.c b/thirdparty/dumb/src/helpers/silence.c new file mode 100644 index 000000000..428f6577f --- /dev/null +++ b/thirdparty/dumb/src/helpers/silence.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * silence.c - Silencing helper. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include "dumb.h" + + + +void DUMBEXPORT dumb_silence(sample_t *samples, int32 length) +{ + memset(samples, 0, length * sizeof(*samples)); +} + diff --git a/thirdparty/dumb/src/helpers/stdfile.c b/thirdparty/dumb/src/helpers/stdfile.c new file mode 100644 index 000000000..f46022791 --- /dev/null +++ b/thirdparty/dumb/src/helpers/stdfile.c @@ -0,0 +1,146 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * stdfile.c - stdio file input module. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" + + + +typedef struct dumb_stdfile +{ + FILE * file; + long size; +} dumb_stdfile; + + + +static void *DUMBCALLBACK dumb_stdfile_open(const char *filename) +{ + dumb_stdfile * file = ( dumb_stdfile * ) malloc( sizeof(dumb_stdfile) ); + if ( !file ) return 0; + file->file = fopen(filename, "rb"); + fseek(file->file, 0, SEEK_END); + file->size = ftell(file->file); + fseek(file->file, 0, SEEK_SET); + return file; +} + + + +static int DUMBCALLBACK dumb_stdfile_skip(void *f, long n) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return fseek(file->file, n, SEEK_CUR); +} + + + +static int DUMBCALLBACK dumb_stdfile_getc(void *f) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return fgetc(file->file); +} + + + +static int32 DUMBCALLBACK dumb_stdfile_getnc(char *ptr, int32 n, void *f) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return (int32)fread(ptr, 1, n, file->file); +} + + + +static void DUMBCALLBACK dumb_stdfile_close(void *f) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + fclose(file->file); + free(f); +} + + + +static void DUMBCALLBACK dumb_stdfile_noclose(void *f) +{ + free(f); +} + + + +static int DUMBCALLBACK dumb_stdfile_seek(void *f, long n) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return fseek(file->file, n, SEEK_SET); +} + + + +static long DUMBCALLBACK dumb_stdfile_get_size(void *f) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return file->size; +} + + + +static const DUMBFILE_SYSTEM stdfile_dfs = { + &dumb_stdfile_open, + &dumb_stdfile_skip, + &dumb_stdfile_getc, + &dumb_stdfile_getnc, + &dumb_stdfile_close, + &dumb_stdfile_seek, + &dumb_stdfile_get_size +}; + + + +void DUMBEXPORT dumb_register_stdfiles(void) +{ + register_dumbfile_system(&stdfile_dfs); +} + + + +static const DUMBFILE_SYSTEM stdfile_dfs_leave_open = { + NULL, + &dumb_stdfile_skip, + &dumb_stdfile_getc, + &dumb_stdfile_getnc, + &dumb_stdfile_noclose, + &dumb_stdfile_seek, + &dumb_stdfile_get_size +}; + + + +DUMBFILE *DUMBEXPORT dumbfile_open_stdfile(FILE *p) +{ + dumb_stdfile * file = ( dumb_stdfile * ) malloc( sizeof(dumb_stdfile) ); + DUMBFILE *d; + if ( !file ) return 0; + file->file = p; + fseek(p, 0, SEEK_END); + file->size = ftell(p); + fseek(p, 0, SEEK_SET); + d = dumbfile_open_ex(file, &stdfile_dfs_leave_open); + + return d; +} diff --git a/thirdparty/dumb/src/it/itload.c b/thirdparty/dumb/src/it/itload.c new file mode 100644 index 000000000..01f7cf019 --- /dev/null +++ b/thirdparty/dumb/src/it/itload.c @@ -0,0 +1,43 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itload.c - Code to read an Impulse Tracker / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By entheh. Don't worry Bob, you're credited | \ / / + * in itread.c! | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_it_quick(): loads an IT file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must pass + * the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_it_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_it_quick(f); + + dumbfile_close(f); + + return duh; +} + diff --git a/thirdparty/dumb/src/it/itload2.c b/thirdparty/dumb/src/it/itload2.c new file mode 100644 index 000000000..68b38cd77 --- /dev/null +++ b/thirdparty/dumb/src/it/itload2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itload2.c - Function to read an Impulse Tracker / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * Split off from itload.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_load_it(const char *filename) +{ + DUH *duh = dumb_load_it_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/itmisc.c b/thirdparty/dumb/src/it/itmisc.c new file mode 100644 index 000000000..389c74736 --- /dev/null +++ b/thirdparty/dumb/src/it/itmisc.c @@ -0,0 +1,249 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itmisc.c - Miscellaneous functions relating / / \ \ + * to module files. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + +int dumb_it_default_panning_separation = 25; + + +DUMB_IT_SIGDATA *DUMBEXPORT duh_get_it_sigdata(DUH *duh) +{ + return duh_get_raw_sigdata(duh, -1, SIGTYPE_IT); +} + + + +const unsigned char *DUMBEXPORT dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->song_message : NULL; +} + + + +int DUMBEXPORT dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->n_orders : 0; +} + + + +int DUMBEXPORT dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->n_samples : 0; +} + + + +int DUMBEXPORT dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->n_instruments : 0; +} + + + +const unsigned char *DUMBEXPORT dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i) +{ + ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples); + return sd->sample[i].name; +} + + + +const unsigned char *DUMBEXPORT dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i) +{ + ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples); + return sd->sample[i].filename; +} + + + +const unsigned char *DUMBEXPORT dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i) +{ + ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments); + return sd->instrument[i].name; +} + + + +const unsigned char *DUMBEXPORT dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i) +{ + ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments); + return sd->instrument[i].filename; +} + + + +int DUMBEXPORT dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->global_volume : 0; +} + + + +void DUMBEXPORT dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv) +{ + if (sd) sd->global_volume = gv; +} + + + +int DUMBEXPORT dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->mixing_volume : 0; +} + + + +void DUMBEXPORT dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv) +{ + if (sd) sd->mixing_volume = mv; +} + + + +int DUMBEXPORT dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->speed : 0; +} + + + +void DUMBEXPORT dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed) +{ + if (sd) sd->speed = speed; +} + + + +int DUMBEXPORT dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->tempo : 0; +} + + + +void DUMBEXPORT dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo) +{ + if (sd) sd->tempo = tempo; +} + + + +int DUMBEXPORT dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel) +{ + ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS); + return sd ? sd->channel_volume[channel] : 0; +} + +void DUMBEXPORT dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume) +{ + ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS); + if (sd) sd->channel_volume[channel] = volume; +} + + + +int DUMBEXPORT dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->order : -1; +} + + + +int DUMBEXPORT dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->row : -1; +} + + + +int DUMBEXPORT dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->globalvolume : 0; +} + + + +void DUMBEXPORT dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv) +{ + if (sr) sr->globalvolume = gv; +} + + + +int DUMBEXPORT dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->tempo : 0; +} + + + +void DUMBEXPORT dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo) +{ + if (sr) sr->tempo = tempo; +} + + + +int DUMBEXPORT dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->speed : 0; +} + + + +void DUMBEXPORT dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed) +{ + if (sr) sr->speed = speed; +} + + + +int DUMBEXPORT dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel) +{ + return sr ? sr->channel[channel].channelvolume : 0; +} + + + +void DUMBEXPORT dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume) +{ + if (sr) sr->channel[channel].channelvolume = volume; +} + + + +void DUMBEXPORT dumb_it_sr_set_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel, int muted) +{ + if (sr) { + if (muted) + sr->channel[channel].flags |= IT_CHANNEL_MUTED; + else + sr->channel[channel].flags &= ~IT_CHANNEL_MUTED; + } +} + + + +int DUMBEXPORT dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel) +{ + return sr ? (sr->channel[channel].flags & IT_CHANNEL_MUTED) != 0 : 0; +} diff --git a/thirdparty/dumb/src/it/itorder.c b/thirdparty/dumb/src/it/itorder.c new file mode 100644 index 000000000..6959f0544 --- /dev/null +++ b/thirdparty/dumb/src/it/itorder.c @@ -0,0 +1,63 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itorder.c - Code to fix invalid patterns in / / \ \ + * the pattern table. | < / \_ + * | \/ /\ / + * By Julien Cugniere. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + + + +#include + +#include "dumb.h" +#include "internal/it.h" + + + +/* This function ensures that any pattern mentioned in the order table but + * not present in the pattern table is treated as an empty 64 rows pattern. + * This is done by adding such a dummy pattern at the end of the pattern + * table, and redirect invalid orders to it. + * Patterns 254 and 255 are left untouched, unless the signal is an XM. + */ +int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata) +{ + int i; + int found_some = 0; + + int first_invalid = sigdata->n_patterns; + int last_invalid = (sigdata->flags & IT_WAS_AN_XM) ? 255 : 253; + + for (i = 0; i < sigdata->n_orders; i++) { + if (sigdata->order[i] >= first_invalid && sigdata->order[i] <= last_invalid) { + sigdata->order[i] = sigdata->n_patterns; + found_some = 1; + } + } + + if (found_some) { + IT_PATTERN *new_pattern = realloc(sigdata->pattern, sizeof(*sigdata->pattern) * (sigdata->n_patterns + 1)); + if (!new_pattern) + return -1; + + new_pattern[sigdata->n_patterns].n_rows = 64; + new_pattern[sigdata->n_patterns].n_entries = 0; + new_pattern[sigdata->n_patterns].entry = NULL; + sigdata->pattern = new_pattern; + sigdata->n_patterns++; + } + + return 0; +} diff --git a/thirdparty/dumb/src/it/itread.c b/thirdparty/dumb/src/it/itread.c new file mode 100644 index 000000000..a226c530f --- /dev/null +++ b/thirdparty/dumb/src/it/itread.c @@ -0,0 +1,1414 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itread.c - Code to read an Impulse Tracker / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * Based on the loader from an IT player by Bob. \_ / > / + * Adapted for DUMB by entheh. | \ / / + * | ' / + * \__/ + */ + +#include +#include //might not be necessary later; required for memset + +#include "dumb.h" +#include "internal/it.h" + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + + +#define INVESTIGATE_OLD_INSTRUMENTS + + + +typedef unsigned char byte; +typedef unsigned short word; +typedef uint32 dword; + +typedef struct readblock_crap readblock_crap; + +struct readblock_crap { + unsigned char *sourcebuf; + unsigned char *sourcepos; + unsigned char *sourceend; + int rembits; +}; + + +static int readblock(DUMBFILE *f, readblock_crap * crap) +{ + int32 size; + int c; + + size = dumbfile_igetw(f); + if (size < 0) + return size; + + crap->sourcebuf = malloc(size); + if (!crap->sourcebuf) + return -1; + + c = dumbfile_getnc((char *)crap->sourcebuf, size, f); + if (c < size) { + free(crap->sourcebuf); + crap->sourcebuf = NULL; + return -1; + } + + crap->sourcepos = crap->sourcebuf; + crap->sourceend = crap->sourcebuf + size; + crap->rembits = 8; + return 0; +} + + + +static void freeblock(readblock_crap * crap) +{ + free(crap->sourcebuf); + crap->sourcebuf = NULL; +} + + + +static int readbits(int bitwidth, readblock_crap * crap) +{ + int val = 0; + int b = 0; + + if (crap->sourcepos >= crap->sourceend) return val; + + while (bitwidth > crap->rembits) { + val |= *crap->sourcepos++ << b; + if (crap->sourcepos >= crap->sourceend) return val; + b += crap->rembits; + bitwidth -= crap->rembits; + crap->rembits = 8; + } + + val |= (*crap->sourcepos & ((1 << bitwidth) - 1)) << b; + *crap->sourcepos >>= bitwidth; + crap->rembits -= bitwidth; + + return val; +} + + + +/** WARNING - do we even need to pass `right`? */ +/** WARNING - why bother memsetting at all? The whole array is written... */ +// if we do memset, dumb_silence() would be neater... +static int decompress8(DUMBFILE *f, signed char *data, int len, int it215, int stereo) +{ + int blocklen, blockpos; + byte bitwidth; + word val; + char d1, d2; + readblock_crap crap; + + memset(&crap, 0, sizeof(crap)); + + for (blocklen = 0, blockpos = 0; blocklen < len; blocklen++, blockpos += 1 + stereo) + data[ blockpos ] = 0; + + while (len > 0) { + //Read a block of compressed data: + if (readblock(f, &crap)) + return -1; + //Set up a few variables + blocklen = (len < 0x8000) ? len : 0x8000; //Max block length is 0x8000 bytes + blockpos = 0; + bitwidth = 9; + d1 = d2 = 0; + //Start the decompression: + while (blockpos < blocklen) { + //Read a value: + val = (word)readbits(bitwidth, &crap); + //Check for bit width change: + + if (bitwidth < 7) { //Method 1: + if (val == (1 << (bitwidth - 1))) { + val = (word)readbits(3, &crap) + 1; + bitwidth = (val < bitwidth) ? val : val + 1; + continue; + } + } + else if (bitwidth < 9) { //Method 2 + byte border = (0xFF >> (9 - bitwidth)) - 4; + + if (val > border && val <= (border + 8)) { + val -= border; + bitwidth = (val < bitwidth) ? val : val + 1; + continue; + } + } + else if (bitwidth == 9) { //Method 3 + if (val & 0x100) { + bitwidth = (val + 1) & 0xFF; + continue; + } + } + else { //Illegal width, abort ? + freeblock(&crap); + return -1; + } + + //Expand the value to signed byte: + { + char v; //The sample value: + if (bitwidth < 8) { + byte shift = 8 - bitwidth; + v = (val << shift); + v >>= shift; + } + else + v = (char)val; + + //And integrate the sample value + //(It always has to end with integration doesn't it ? ;-) + d1 += v; + d2 += d1; + } + + //Store ! + /* Version 2.15 was an unofficial version with hacked compression + * code. Yay, better compression :D + */ + *data++ = it215 ? d2 : d1; + data += stereo; + len--; + blockpos++; + } + freeblock(&crap); + } + return 0; +} + + + +static int decompress16(DUMBFILE *f, short *data, int len, int it215, int stereo) +{ + int blocklen, blockpos; + byte bitwidth; + int32 val; + short d1, d2; + readblock_crap crap; + + memset(&crap, 0, sizeof(crap)); + + for ( blocklen = 0, blockpos = 0; blocklen < len; blocklen++, blockpos += 1 + stereo ) + data[ blockpos ] = 0; + + while (len > 0) { + //Read a block of compressed data: + if (readblock(f, &crap)) + return -1; + //Set up a few variables + blocklen = (len < 0x4000) ? len : 0x4000; // Max block length is 0x4000 bytes + blockpos = 0; + bitwidth = 17; + d1 = d2 = 0; + //Start the decompression: + while (blockpos < blocklen) { + val = readbits(bitwidth, &crap); + //Check for bit width change: + + if (bitwidth < 7) { //Method 1: + if (val == (1 << (bitwidth - 1))) { + val = readbits(4, &crap) + 1; + bitwidth = (byte)((val < bitwidth) ? val : val + 1); + continue; + } + } + else if (bitwidth < 17) { //Method 2 + word border = (0xFFFF >> (17 - bitwidth)) - 8; + + if (val > border && val <= (border + 16)) { + val -= border; + bitwidth = (byte)(val < bitwidth ? val : val + 1); + continue; + } + } + else if (bitwidth == 17) { //Method 3 + if (val & 0x10000) { + bitwidth = (byte)((val + 1) & 0xFF); + continue; + } + } + else { //Illegal width, abort ? + freeblock(&crap); + return -1; + } + + //Expand the value to signed byte: + { + short v; //The sample value: + if (bitwidth < 16) { + byte shift = 16 - bitwidth; + v = (short)(val << shift); + v >>= shift; + } + else + v = (short)val; + + //And integrate the sample value + //(It always has to end with integration doesn't it ? ;-) + d1 += v; + d2 += d1; + } + + //Store ! + /* Version 2.15 was an unofficial version with hacked compression + * code. Yay, better compression :D + */ + *data++ = it215 ? d2 : d1; + data += stereo; + len--; + blockpos++; + } + freeblock(&crap); + } + return 0; +} + + + +static int it_read_envelope(IT_ENVELOPE *envelope, DUMBFILE *f) +{ + int n; + + envelope->flags = dumbfile_getc(f); + envelope->n_nodes = dumbfile_getc(f); + if(envelope->n_nodes > 25) { + TRACE("IT error: wrong number of envelope nodes (%d)\n", envelope->n_nodes); + envelope->n_nodes = 0; + return -1; + } + envelope->loop_start = dumbfile_getc(f); + envelope->loop_end = dumbfile_getc(f); + envelope->sus_loop_start = dumbfile_getc(f); + envelope->sus_loop_end = dumbfile_getc(f); + for (n = 0; n < envelope->n_nodes; n++) { + envelope->node_y[n] = dumbfile_getc(f); + envelope->node_t[n] = dumbfile_igetw(f); + } + dumbfile_skip(f, 75 - envelope->n_nodes * 3 + 1); + + if (envelope->n_nodes <= 0) + envelope->flags &= ~IT_ENVELOPE_ON; + else { + if (envelope->loop_end >= envelope->n_nodes || envelope->loop_start > envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON; + if (envelope->sus_loop_end >= envelope->n_nodes || envelope->sus_loop_start > envelope->sus_loop_end) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP; + } + + return dumbfile_error(f); +} + + + +static int it_read_old_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f) +{ + int n; + + /*if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE) + return -1;*/ + // XXX + dumbfile_skip(f, 4); + + dumbfile_getnc((char *)instrument->filename, 13, f); + instrument->filename[13] = 0; + + instrument->volume_envelope.flags = dumbfile_getc(f); + instrument->volume_envelope.loop_start = dumbfile_getc(f); + instrument->volume_envelope.loop_end = dumbfile_getc(f); + instrument->volume_envelope.sus_loop_start = dumbfile_getc(f); + instrument->volume_envelope.sus_loop_end = dumbfile_getc(f); + + /* Skip two unused bytes. */ + dumbfile_skip(f, 2); + + /* In the old instrument format, fadeout ranges from 0 to 64, and is + * subtracted at intervals from a value starting at 512. In the new + * format, all these values are doubled. Therefore we double when loading + * from the old instrument format - that way we don't have to think about + * it later. + */ + instrument->fadeout = dumbfile_igetw(f) << 1; + instrument->new_note_action = dumbfile_getc(f); + instrument->dup_check_type = dumbfile_getc(f); + instrument->dup_check_action = DCA_NOTE_CUT; // This might be wrong! + /** WARNING - what is the duplicate check action for old-style instruments? */ + + /* Skip Tracker Version and Number of Samples. These are only used in + * separate instrument files. Also skip unused byte. + */ + dumbfile_skip(f, 4); + + dumbfile_getnc((char *)instrument->name, 26, f); + instrument->name[26] = 0; + + /* Skip unused bytes following the Instrument Name. */ + dumbfile_skip(f, 6); + + instrument->pp_separation = 0; + instrument->pp_centre = 60; + instrument->global_volume = 128; + /** WARNING - should global_volume be 64 or something? */ + instrument->default_pan = 32; + /** WARNING - should default_pan be 128, meaning don`t use? */ + instrument->random_volume = 0; + instrument->random_pan = 0; + + for (n = 0; n < 120; n++) { + instrument->map_note[n] = dumbfile_getc(f); + instrument->map_sample[n] = dumbfile_getc(f); + } + + /* Skip "Volume envelope (200 bytes)". */ + // - need to know better what this is for though. + dumbfile_skip(f, 200); + +#ifdef INVESTIGATE_OLD_INSTRUMENTS + fprintf(stderr, "Inst %02d Env:", n); +#endif + + for (n = 0; n < 25; n++) + { + instrument->volume_envelope.node_t[n] = dumbfile_getc(f); + instrument->volume_envelope.node_y[n] = dumbfile_getc(f); + +#ifdef INVESTIGATE_OLD_INSTRUMENTS + fprintf(stderr, " %d,%d", + instrument->volume_envelope.node_t[n], + instrument->volume_envelope.node_y[n]); +#endif + + // This loop is unfinished, as we can probably escape from it before + // the end if we want to. Hence the otherwise useless dumbfile_skip() + // call below. + } + dumbfile_skip(f, 50 - (n << 1)); + instrument->volume_envelope.n_nodes = n; + +#ifdef INVESTIGATE_OLD_INSTRUMENTS + fprintf(stderr, "\n"); +#endif + + if (dumbfile_error(f)) + return -1; + + { + IT_ENVELOPE *envelope = &instrument->volume_envelope; + if (envelope->n_nodes <= 0) + envelope->flags &= ~IT_ENVELOPE_ON; + else { + if (envelope->loop_end >= envelope->n_nodes || envelope->loop_start > envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON; + if (envelope->sus_loop_end >= envelope->n_nodes || envelope->sus_loop_start > envelope->sus_loop_end) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP; + } + } + + instrument->filter_cutoff = 127; + instrument->filter_resonance = 0; + + instrument->pan_envelope.flags = 0; + instrument->pitch_envelope.flags = 0; + + return 0; +} + + + +static int it_read_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f, int maxlen) +{ + int n, len = 0; + + /*if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE) + return -1;*/ + // XXX + + if (maxlen) len = dumbfile_pos(f); + + dumbfile_skip(f, 4); + + dumbfile_getnc((char *)instrument->filename, 13, f); + instrument->filename[13] = 0; + + instrument->new_note_action = dumbfile_getc(f); + instrument->dup_check_type = dumbfile_getc(f); + instrument->dup_check_action = dumbfile_getc(f); + instrument->fadeout = dumbfile_igetw(f); + instrument->pp_separation = dumbfile_getc(f); + instrument->pp_centre = dumbfile_getc(f); + instrument->global_volume = dumbfile_getc(f); + instrument->default_pan = dumbfile_getc(f); + instrument->random_volume = dumbfile_getc(f); + instrument->random_pan = dumbfile_getc(f); + + /* Skip Tracker Version and Number of Samples. These are only used in + * separate instrument files. Also skip unused byte. + */ + dumbfile_skip(f, 4); + + dumbfile_getnc((char *)instrument->name, 26, f); + instrument->name[26] = 0; + + instrument->filter_cutoff = dumbfile_getc(f); + instrument->filter_resonance = dumbfile_getc(f); + + /* Skip MIDI Channel, Program and Bank. */ + //dumbfile_skip(f, 4); + /*instrument->output = dumbfile_getc(f); + if ( instrument->output > 16 ) { + instrument->output -= 128; + } else { + instrument->output = 0; + } + dumbfile_skip(f, 3);*/ + dumbfile_skip(f, 4); + + for (n = 0; n < 120; n++) { + instrument->map_note[n] = dumbfile_getc(f); + instrument->map_sample[n] = dumbfile_getc(f); + } + + if (dumbfile_error(f)) + return -1; + + if (it_read_envelope(&instrument->volume_envelope, f)) return -1; + if (it_read_envelope(&instrument->pan_envelope, f)) return -1; + if (it_read_envelope(&instrument->pitch_envelope, f)) return -1; + + if (maxlen) { + len = dumbfile_pos(f) - len; + if ( maxlen - len < 124 ) return 0; + } + + if ( dumbfile_mgetl(f) == IT_MPTX_SIGNATURE ) { + for ( n = 0; n < 120; n++ ) { + instrument->map_sample[ n ] += dumbfile_getc( f ) << 8; + } + + if (dumbfile_error(f)) + return -1; + } + + /*if ( dumbfile_mgetl(f) == IT_INSM_SIGNATURE ) { + int32 end = dumbfile_igetl(f); + end += dumbfile_pos(f); + while ( dumbfile_pos(f) < end ) { + int chunkid = dumbfile_igetl(f); + switch ( chunkid ) { + case DUMB_ID('P','L','U','G'): + instrument->output = dumbfile_getc(f); + break; + default: + chunkid = chunkid / 0x100 + dumbfile_getc(f) * 0x1000000; + break; + } + } + + if (dumbfile_error(f)) + return -1; + }*/ + + return 0; +} + + + +static int it_read_sample_header(IT_SAMPLE *sample, unsigned char *convert, int32 *offset, DUMBFILE *f) +{ + /* XXX + if (dumbfile_mgetl(f) != IT_SAMPLE_SIGNATURE) + return -1;*/ + int hax = 0; + int32 s = dumbfile_mgetl(f); + if (s != IT_SAMPLE_SIGNATURE) { + if ( s == ( IT_SAMPLE_SIGNATURE >> 16 ) ) { + s <<= 16; + s |= dumbfile_mgetw(f); + if ( s != IT_SAMPLE_SIGNATURE ) + return -1; + hax = 1; + } + } + + dumbfile_getnc((char *)sample->filename, 13, f); + sample->filename[13] = 0; + + sample->global_volume = dumbfile_getc(f); + sample->flags = dumbfile_getc(f); + sample->default_volume = dumbfile_getc(f); + + dumbfile_getnc((char *)sample->name, 26, f); + sample->name[26] = 0; + + *convert = dumbfile_getc(f); + sample->default_pan = dumbfile_getc(f); + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = dumbfile_igetl(f); + sample->C5_speed = dumbfile_igetl(f); + sample->sus_loop_start = dumbfile_igetl(f); + sample->sus_loop_end = dumbfile_igetl(f); + +#ifdef STEREO_SAMPLES_COUNT_AS_TWO + if (sample->flags & IT_SAMPLE_STEREO) { + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + sample->C5_speed >>= 1; + sample->sus_loop_start >>= 1; + sample->sus_loop_end >>= 1; + } +#endif + + if (sample->flags & IT_SAMPLE_EXISTS) { + if (sample->length <= 0) + sample->flags &= ~IT_SAMPLE_EXISTS; + else { + if ((unsigned int)sample->loop_end > (unsigned int)sample->length) + sample->flags &= ~IT_SAMPLE_LOOP; + else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end) + sample->flags &= ~IT_SAMPLE_LOOP; + + if ((unsigned int)sample->sus_loop_end > (unsigned int)sample->length) + sample->flags &= ~IT_SAMPLE_SUS_LOOP; + else if ((unsigned int)sample->sus_loop_start >= (unsigned int)sample->sus_loop_end) + sample->flags &= ~IT_SAMPLE_SUS_LOOP; + + /* We may be able to truncate the sample to save memory. */ + if (sample->flags & IT_SAMPLE_LOOP && + *convert != 0xFF) { /* not truncating compressed samples, for now... */ + if ((sample->flags & IT_SAMPLE_SUS_LOOP) && sample->sus_loop_end >= sample->loop_end) + sample->length = sample->sus_loop_end; + else + sample->length = sample->loop_end; + } + } + } + + *offset = dumbfile_igetl(f); + + sample->vibrato_speed = dumbfile_getc(f); + sample->vibrato_depth = dumbfile_getc(f); + if ( ! hax ) { + sample->vibrato_rate = dumbfile_getc(f); + sample->vibrato_waveform = dumbfile_getc(f); + } else { + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; + } + sample->finetune = 0; + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + +int32 _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f) +{ + int32 n, len, delta; + signed char * ptr, * end; + signed char compression_table[16]; + if (dumbfile_getnc((char *)compression_table, 16, f) != 16) + return -1; + ptr = (signed char *) sample->data; + delta = 0; + + end = ptr + sample->length; + len = (sample->length + 1) / 2; + for (n = 0; n < len; n++) { + int b = dumbfile_getc(f); + if (b < 0) return -1; + delta += compression_table[b & 0x0F]; + *ptr++ = (signed char)delta; + if (ptr >= end) break; + delta += compression_table[b >> 4]; + *ptr++ = (signed char)delta; + } + + return 0; +} + + +static int32 it_read_sample_data(IT_SAMPLE *sample, unsigned char convert, DUMBFILE *f) +{ + int32 n; + + int32 datasize = sample->length; + if (sample->flags & IT_SAMPLE_STEREO) datasize <<= 1; + + sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); + if (!sample->data) + return -1; + + if (!(sample->flags & IT_SAMPLE_16BIT) && (convert == 0xFF)) { + if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0) + return -1; + } else if (sample->flags & 8) { + /* If the sample is packed, then we must unpack it. */ + + /* Behavior as defined by greasemonkey's munch.py and observed by XMPlay and OpenMPT */ + + if (sample->flags & IT_SAMPLE_STEREO) { + if (sample->flags & IT_SAMPLE_16BIT) { + decompress16(f, (short *) sample->data, datasize >> 1, convert & 4, 1); + decompress16(f, (short *) sample->data + 1, datasize >> 1, convert & 4, 1); + } else { + decompress8(f, (signed char *) sample->data, datasize >> 1, convert & 4, 1); + decompress8(f, (signed char *) sample->data + 1, datasize >> 1, convert & 4, 1); + } + } else { + if (sample->flags & IT_SAMPLE_16BIT) + decompress16(f, (short *) sample->data, datasize, convert & 4, 0); + else + decompress8(f, (signed char *) sample->data, datasize, convert & 4, 0); + } + } else if (sample->flags & IT_SAMPLE_16BIT) { + if (sample->flags & IT_SAMPLE_STEREO) { + if (convert & 2) { + for (n = 0; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_mgetw(f); + for (n = 1; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_mgetw(f); + } else { + for (n = 0; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_igetw(f); + for (n = 1; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_igetw(f); + } + } else { + if (convert & 2) + for (n = 0; n < datasize; n++) + ((short *)sample->data)[n] = dumbfile_mgetw(f); + else + for (n = 0; n < datasize; n++) + ((short *)sample->data)[n] = dumbfile_igetw(f); + } + } else { + if (sample->flags & IT_SAMPLE_STEREO) { + for (n = 0; n < datasize; n += 2) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + for (n = 1; n < datasize; n += 2) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + } else + for (n = 0; n < datasize; n++) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + } + + if (dumbfile_error(f)) + return -1; + + if (!(convert & 1)) { + /* Convert to signed. */ + if (sample->flags & IT_SAMPLE_16BIT) + for (n = 0; n < datasize; n++) + ((short *)sample->data)[n] ^= 0x8000; + else + for (n = 0; n < datasize; n++) + ((signed char *)sample->data)[n] ^= 0x80; + } + + /* NOT SUPPORTED: + * + * convert & 4 - Samples stored as delta values + * convert & 16 - Samples stored as TX-Wave 12-bit values + * convert & 32 - Left/Right/All Stereo prompt + */ + + return 0; +} + + + +//#define DETECT_DUPLICATE_CHANNELS +#ifdef DETECT_DUPLICATE_CHANNELS +#include +#endif +static int it_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer) +{ + unsigned char cmask[DUMB_IT_N_CHANNELS]; + unsigned char cnote[DUMB_IT_N_CHANNELS]; + unsigned char cinstrument[DUMB_IT_N_CHANNELS]; + unsigned char cvolpan[DUMB_IT_N_CHANNELS]; + unsigned char ceffect[DUMB_IT_N_CHANNELS]; + unsigned char ceffectvalue[DUMB_IT_N_CHANNELS]; +#ifdef DETECT_DUPLICATE_CHANNELS + IT_ENTRY *dupentry[DUMB_IT_N_CHANNELS]; +#endif + + int n_entries = 0; + int buflen; + int bufpos = 0; + + IT_ENTRY *entry; + + unsigned char channel; + unsigned char mask; + + memset(cmask, 0, sizeof(cmask)); + memset(cnote, 0, sizeof(cnote)); + memset(cinstrument, 0, sizeof(cinstrument)); + memset(cvolpan, 0, sizeof(cvolpan)); + memset(ceffect, 0, sizeof(ceffect)); + memset(ceffectvalue, 0, sizeof(ceffectvalue)); +#ifdef DETECT_DUPLICATE_CHANNELS + { + int i; + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL; + } +#endif + + buflen = dumbfile_igetw(f); + pattern->n_rows = dumbfile_igetw(f); + + /* Skip four unused bytes. */ + dumbfile_skip(f, 4); + + if (dumbfile_error(f)) + return -1; + + /* Read in the pattern data. */ + dumbfile_getnc((char *)buffer, buflen, f); + + if (dumbfile_error(f)) + return -1; + + /* Scan the pattern data, and work out how many entries we need room for. */ + while (bufpos < buflen) { + unsigned char b = buffer[bufpos++]; + + if (b == 0) { + /* End of row */ + n_entries++; + continue; + } + + channel = (b - 1) & 63; + + if (b & 128) + cmask[channel] = mask = buffer[bufpos++]; + else + mask = cmask[channel]; + + { + static const unsigned char used[16] = {0, 1, 1, 2, 1, 2, 2, 3, 2, 3, 3, 4, 3, 4, 4, 5}; + n_entries += (mask != 0); + bufpos += used[mask & 15]; + } + } + + pattern->n_entries = n_entries; + + pattern->entry = malloc(n_entries * sizeof(*pattern->entry)); + + if (!pattern->entry) + return -1; + + bufpos = 0; + memset(cmask, 0, sizeof(cmask)); + + entry = pattern->entry; + + while (bufpos < buflen) { + unsigned char b = buffer[bufpos++]; + + if (b == 0) { + /* End of row */ + IT_SET_END_ROW(entry); + entry++; +#ifdef DETECT_DUPLICATE_CHANNELS + { + int i; + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL; + } +#endif + continue; + } + + channel = (b - 1) & 63; + + if (b & 128) + cmask[channel] = mask = buffer[bufpos++]; + else + mask = cmask[channel]; + + if (mask) { + entry->mask = (mask & 15) | (mask >> 4); + entry->channel = channel; + + if (mask & IT_ENTRY_NOTE) + cnote[channel] = entry->note = buffer[bufpos++]; + else if (mask & (IT_ENTRY_NOTE << 4)) + entry->note = cnote[channel]; + + if (mask & IT_ENTRY_INSTRUMENT) + cinstrument[channel] = entry->instrument = buffer[bufpos++]; + else if (mask & (IT_ENTRY_INSTRUMENT << 4)) + entry->instrument = cinstrument[channel]; + + if (mask & IT_ENTRY_VOLPAN) + cvolpan[channel] = entry->volpan = buffer[bufpos++]; + else if (mask & (IT_ENTRY_VOLPAN << 4)) + entry->volpan = cvolpan[channel]; + + if (mask & IT_ENTRY_EFFECT) { + ceffect[channel] = entry->effect = buffer[bufpos++]; + ceffectvalue[channel] = entry->effectvalue = buffer[bufpos++]; + } else { + entry->effect = ceffect[channel]; + entry->effectvalue = ceffectvalue[channel]; + } + +#ifdef DETECT_DUPLICATE_CHANNELS + if (dupentry[channel]) { + FILE *f = fopen("dupentry.txt", "a"); + if (!f) abort(); + fprintf(f, "Two events on channel %d:", channel); + fprintf(f, " Event #1:"); + if (dupentry[channel]->mask & IT_ENTRY_NOTE ) fprintf(f, " %03d", dupentry[channel]->note ); else fprintf(f, " ..."); + if (dupentry[channel]->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", dupentry[channel]->instrument); else fprintf(f, " ..."); + if (dupentry[channel]->mask & IT_ENTRY_VOLPAN ) fprintf(f, " %03d", dupentry[channel]->volpan ); else fprintf(f, " ..."); + if (dupentry[channel]->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + dupentry[channel]->effect, dupentry[channel]->effectvalue); else fprintf(f, " ...\n"); + fprintf(f, " Event #2:"); + if (entry->mask & IT_ENTRY_NOTE ) fprintf(f, " %03d", entry->note ); else fprintf(f, " ..."); + if (entry->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", entry->instrument); else fprintf(f, " ..."); + if (entry->mask & IT_ENTRY_VOLPAN ) fprintf(f, " %03d", entry->volpan ); else fprintf(f, " ..."); + if (entry->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + entry->effect, entry->effectvalue); else fprintf(f, " ...\n"); + fclose(f); + } + dupentry[channel] = entry; +#endif + + entry++; + } + } + + ASSERT(entry == pattern->entry + n_entries); + + return 0; +} + + + +/* Currently we assume the sample data are stored after the sample headers in + * module files. This assumption may be unjustified; let me know if you have + * trouble. + */ + +#define IT_COMPONENT_SONG_MESSAGE 1 +#define IT_COMPONENT_INSTRUMENT 2 +#define IT_COMPONENT_PATTERN 3 +#define IT_COMPONENT_SAMPLE 4 + +typedef struct IT_COMPONENT +{ + unsigned char type; + unsigned short n; + int32 offset; + short sampfirst; /* component[sampfirst] = first sample data after this */ + short sampnext; /* sampnext is used to create linked lists of sample data */ +} +IT_COMPONENT; + + + +static int CDECL it_component_compare(const void *e1, const void *e2) +{ + return ((const IT_COMPONENT *)e1)->offset - + ((const IT_COMPONENT *)e2)->offset; +} + + + +static sigdata_t *it_load_sigdata(DUMBFILE *f) +{ + DUMB_IT_SIGDATA *sigdata; + + int cwt, cmwt; + int special; + int message_length, message_offset; + + IT_COMPONENT *component; + int n_components = 0; + + unsigned char sample_convert[4096]; + + int n; + + unsigned char *buffer; + + if (dumbfile_mgetl(f) != IT_SIGNATURE) + { + return NULL; + } + + sigdata = malloc(sizeof(*sigdata)); + + if (!sigdata) + { + return NULL; + } + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + dumbfile_getnc((char *)sigdata->name, 26, f); + sigdata->name[26] = 0; + + /* Skip pattern row highlight info. */ + dumbfile_skip(f, 2); + + sigdata->n_orders = dumbfile_igetw(f); + sigdata->n_instruments = dumbfile_igetw(f); + sigdata->n_samples = dumbfile_igetw(f); + sigdata->n_patterns = dumbfile_igetw(f); + + cwt = dumbfile_igetw(f); + cmwt = dumbfile_igetw(f); + + sigdata->flags = dumbfile_igetw(f); + special = dumbfile_igetw(f); + + sigdata->global_volume = dumbfile_getc(f); + sigdata->mixing_volume = dumbfile_getc(f); + sigdata->speed = dumbfile_getc(f); + if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo? + sigdata->tempo = dumbfile_getc(f); + sigdata->pan_separation = dumbfile_getc(f); /** WARNING: use this */ + + /* Skip Pitch Wheel Depth */ + dumbfile_skip(f, 1); + + message_length = dumbfile_igetw(f); + message_offset = dumbfile_igetl(f); + + /* Skip Reserved. */ + dumbfile_skip(f, 4); + + dumbfile_getnc((char *)sigdata->channel_pan, DUMB_IT_N_CHANNELS, f); + dumbfile_getnc((char *)sigdata->channel_volume, DUMB_IT_N_CHANNELS, f); + + // XXX sample count + if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_instruments > 256 || sigdata->n_samples > 4000 || sigdata->n_patterns > 256) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->order = malloc(sigdata->n_orders); + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->n_instruments) { + sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument)); + if (!sigdata->instrument) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + if (sigdata->n_samples) { + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + } + + if (sigdata->n_patterns) { + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + } + + dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f); + sigdata->restart_position = 0; + + component = malloc(769 * sizeof(*component)); + if (!component) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (special & 1) { + component[n_components].type = IT_COMPONENT_SONG_MESSAGE; + component[n_components].offset = message_offset; + component[n_components].sampfirst = -1; + n_components++; + } + + for (n = 0; n < sigdata->n_instruments; n++) { + component[n_components].type = IT_COMPONENT_INSTRUMENT; + component[n_components].n = n; + component[n_components].offset = dumbfile_igetl(f); + component[n_components].sampfirst = -1; + n_components++; + } + + for (n = 0; n < sigdata->n_samples; n++) { + component[n_components].type = IT_COMPONENT_SAMPLE; + component[n_components].n = n; + component[n_components].offset = dumbfile_igetl(f); + component[n_components].sampfirst = -1; + n_components++; + } + + for (n = 0; n < sigdata->n_patterns; n++) { + int32 offset = dumbfile_igetl(f); + if (offset) { + component[n_components].type = IT_COMPONENT_PATTERN; + component[n_components].n = n; + component[n_components].offset = offset; + component[n_components].sampfirst = -1; + n_components++; + } else { + /* Empty 64-row pattern */ + sigdata->pattern[n].n_rows = 64; + sigdata->pattern[n].n_entries = 0; + } + } + + if (dumbfile_error(f)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + /* + if (!(sigdata->flags & 128) != !(special & 8)) { + fprintf(stderr, "Flags Bit 7 (\"Request embedded MIDI configuration\"): %s\n", sigdata->flags & 128 ? "=SET=" : "clear"); + fprintf(stderr, "Special Bit 3 (\"MIDI configuration embedded\") : %s\n", special & 8 ? "=SET=" : "clear"); + fprintf(stderr, "entheh would like to investigate this IT file.\n"); + fprintf(stderr, "Please contact him! entheh@users.sf.net\n"); + } + */ + + if (special & 8) { + /* MIDI configuration is embedded. */ + unsigned char mididata[32]; + int i; + sigdata->midi = malloc(sizeof(*sigdata->midi)); + if (!sigdata->midi) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + // Should we be happy with this outcome in some situations? + } + // What are we skipping? + i = dumbfile_igetw(f); + if (dumbfile_error(f) || dumbfile_skip(f, 8*i)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + /* Read embedded MIDI configuration */ + // What are the first 9 commands for? + if (dumbfile_skip(f, 32*9)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < 16; i++) { + unsigned char len = 0; + int j, leftdigit = -1; + if (dumbfile_getnc((char *)mididata, 32, f) < 32) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sigdata->midi->SFmacroz[i] = 0; + for (j = 0; j < 32; j++) { + if (leftdigit >= 0) { + if (mididata[j] == 0) { + sigdata->midi->SFmacro[i][len++] = leftdigit; + break; + } else if (mididata[j] == ' ') + sigdata->midi->SFmacro[i][len++] = leftdigit; + else if (mididata[j] >= '0' && mididata[j] <= '9') + sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0'); + else if (mididata[j] >= 'A' && mididata[j] <= 'F') + sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA); + leftdigit = -1; + } else if (mididata[j] == 0) + break; + else if (mididata[j] == 'z') + sigdata->midi->SFmacroz[i] |= 1 << len++; + else if (mididata[j] >= '0' && mididata[j] <= '9') + leftdigit = mididata[j] - '0'; + else if (mididata[j] >= 'A' && mididata[j] <= 'F') + leftdigit = mididata[j] - 'A' + 0xA; + } + sigdata->midi->SFmacrolen[i] = len; + } + for (i = 0; i < 128; i++) { + unsigned char len = 0; + int j, leftdigit = -1; + dumbfile_getnc((char *)mididata, 32, f); + for (j = 0; j < 32; j++) { + if (leftdigit >= 0) { + if (mididata[j] == 0) { + sigdata->midi->Zmacro[i][len++] = leftdigit; + break; + } else if (mididata[j] == ' ') + sigdata->midi->Zmacro[i][len++] = leftdigit; + else if (mididata[j] >= '0' && mididata[j] <= '9') + sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0'); + else if (mididata[j] >= 'A' && mididata[j] <= 'F') + sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA); + leftdigit = -1; + } else if (mididata[j] == 0) + break; + else if (mididata[j] >= '0' && mididata[j] <= '9') + leftdigit = mididata[j] - '0'; + else if (mididata[j] >= 'A' && mididata[j] <= 'F') + leftdigit = mididata[j] - 'A' + 0xA; + } + sigdata->midi->Zmacrolen[i] = len; + } + } + + sigdata->flags &= IT_REAL_FLAGS; + + qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare); + + buffer = malloc(65536); + if (!buffer) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < n_components; n++) { + int32 offset; + int m; + + /* XXX */ + if ( component[n].offset == 0 ) { + switch (component[n].type) { + case IT_COMPONENT_INSTRUMENT: + memset( &sigdata->instrument[component[n].n], 0, sizeof(IT_INSTRUMENT) ); + break; + case IT_COMPONENT_SAMPLE: + memset( &sigdata->sample[component[n].n], 0, sizeof(IT_SAMPLE) ); + break; + case IT_COMPONENT_PATTERN: + { + IT_PATTERN * p = &sigdata->pattern[component[n].n]; + p->entry = 0; + p->n_rows = 64; + p->n_entries = 0; + } + break; + } + continue; + } + + if (dumbfile_seek(f, component[n].offset, DFS_SEEK_SET)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + switch (component[n].type) { + + case IT_COMPONENT_SONG_MESSAGE: + if ( n < n_components ) { + message_length = min( message_length, component[n+1].offset - component[n].offset ); + } + sigdata->song_message = malloc(message_length + 1); + if (sigdata->song_message) { + if (dumbfile_getnc((char *)sigdata->song_message, message_length, f) < message_length) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sigdata->song_message[message_length] = 0; + } + break; + + case IT_COMPONENT_INSTRUMENT: + if (cmwt < 0x200) + m = it_read_old_instrument(&sigdata->instrument[component[n].n], f); + else + m = it_read_instrument(&sigdata->instrument[component[n].n], f, (n + 1 < n_components) ? (component[n+1].offset - component[n].offset) : 0); + + if (m) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + break; + + case IT_COMPONENT_PATTERN: + if (it_read_pattern(&sigdata->pattern[component[n].n], f, buffer)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + break; + + case IT_COMPONENT_SAMPLE: + if (it_read_sample_header(&sigdata->sample[component[n].n], &sample_convert[component[n].n], &offset, f)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) { + short *sample; + + for (m = n + 1; m < n_components; m++) + if (component[m].offset > offset) + break; + m--; + + sample = &component[m].sampfirst; + + while (*sample >= 0 && component[*sample].offset <= offset) + sample = &component[*sample].sampnext; + + component[n].sampnext = *sample; + *sample = n; + + component[n].offset = offset; + } + } + + m = component[n].sampfirst; + + while (m >= 0) { + if (dumbfile_seek(f, component[m].offset, DFS_SEEK_SET)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (it_read_sample_data(&sigdata->sample[component[m].n], sample_convert[component[m].n], f)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + m = component[m].sampnext; + } + } + + for ( n = 0; n < 10; n++ ) + { + if ( dumbfile_getc( f ) == 'X' ) + { + if ( dumbfile_getc( f ) == 'T' ) + { + if ( dumbfile_getc( f ) == 'P' ) + { + if ( dumbfile_getc( f ) == 'M' ) + { + break; + } + } + } + } + } + + if ( !dumbfile_error( f ) && n < 10 ) + { + unsigned int mptx_id = dumbfile_igetl( f ); + while ( !dumbfile_error( f ) && mptx_id != DUMB_ID('M','P','T','S') ) + { + unsigned int size = dumbfile_igetw( f ); + switch (mptx_id) + { + /* TODO: Add instrument extension readers */ + + default: + dumbfile_skip(f, size * sigdata->n_instruments); + break; + } + + mptx_id = dumbfile_igetl( f ); + } + + mptx_id = dumbfile_igetl( f ); + while ( !dumbfile_error(f) && dumbfile_pos(f) < dumbfile_get_size(f) ) + { + unsigned int size = dumbfile_igetw( f ); + switch (mptx_id) + { + /* TODO: Add more song extension readers */ + + case DUMB_ID('D','T','.','.'): + if ( size == 2 ) + sigdata->tempo = dumbfile_igetw( f ); + else if ( size == 4 ) + sigdata->tempo = dumbfile_igetl( f ); + break; + + default: + dumbfile_skip(f, size); + break; + } + mptx_id = dumbfile_igetl( f ); + } + } + + free(buffer); + free(component); + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *DUMBEXPORT dumb_read_it_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_load_sigdata(f); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "IT"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/itread2.c b/thirdparty/dumb/src/it/itread2.c new file mode 100644 index 000000000..718565729 --- /dev/null +++ b/thirdparty/dumb/src/it/itread2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itread2.c - Function to read an Impulse Tracker / / \ \ + * module from an open file and do an | < / \_ + * initial run-through. | \/ /\ / + * \_ / > / + * Split off from itread.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_read_it(DUMBFILE *f) +{ + DUH *duh = dumb_read_it_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/itrender.c b/thirdparty/dumb/src/it/itrender.c new file mode 100644 index 000000000..f9dc268e5 --- /dev/null +++ b/thirdparty/dumb/src/it/itrender.c @@ -0,0 +1,5961 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itrender.c - Code to render an Impulse Tracker / / \ \ + * module. | < / \_ + * | \/ /\ / + * Written - painstakingly - by entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/dumb.h" +#include "internal/it.h" +#include "internal/lpc.h" + +#include "internal/resampler.h" +#include "internal/mulsc.h" + +// #define BIT_ARRAY_BULLSHIT + +static IT_PLAYING *new_playing(DUMB_IT_SIGRENDERER *itsr) +{ + IT_PLAYING *r; + + if (itsr->free_playing != NULL) + { + r = itsr->free_playing; + itsr->free_playing = r->next; + return r; + } + r = (IT_PLAYING *)malloc(sizeof(IT_PLAYING)); + if (r) + { + r->resampler.fir_resampler_ratio = 0.0; + r->resampler.fir_resampler[0] = resampler_create(); + if ( !r->resampler.fir_resampler[0] ) { + free( r ); + return NULL; + } + r->resampler.fir_resampler[1] = resampler_create(); + if ( !r->resampler.fir_resampler[1] ) { + resampler_delete( r->resampler.fir_resampler[0] ); + free( r ); + return NULL; + } + } + return r; +} + +static void free_playing(DUMB_IT_SIGRENDERER *itsr, IT_PLAYING *playing) +{ + playing->next = itsr->free_playing; + itsr->free_playing = playing; +} + +static void free_playing_orig(IT_PLAYING * r) +{ + resampler_delete( r->resampler.fir_resampler[1] ); + resampler_delete( r->resampler.fir_resampler[0] ); + free( r ); +} + +static IT_PLAYING *dup_playing(IT_PLAYING *src, IT_CHANNEL *dstchannel, IT_CHANNEL *srcchannel) +{ + IT_PLAYING *dst; + + if (!src) return NULL; + + dst = malloc(sizeof(*dst)); + if (!dst) return NULL; + + dst->flags = src->flags; + dst->resampling_quality = src->resampling_quality; + + ASSERT(src->channel); + dst->channel = &dstchannel[src->channel - srcchannel]; + dst->sample = src->sample; + dst->instrument = src->instrument; + dst->env_instrument = src->env_instrument; + + dst->sampnum = src->sampnum; + dst->instnum = src->instnum; + + dst->declick_stage = src->declick_stage; + + dst->float_volume[0] = src->float_volume[0]; + dst->float_volume[1] = src->float_volume[1]; + + dst->ramp_volume[0] = src->ramp_volume[0]; + dst->ramp_volume[1] = src->ramp_volume[1]; + + dst->ramp_delta[0] = src->ramp_delta[0]; + dst->ramp_delta[1] = src->ramp_delta[1]; + + dst->channel_volume = src->channel_volume; + + dst->volume = src->volume; + dst->pan = src->pan; + + dst->volume_offset = src->volume_offset; + dst->panning_offset = src->panning_offset; + + dst->note = src->note; + + dst->enabled_envelopes = src->enabled_envelopes; + + dst->filter_cutoff = src->filter_cutoff; + dst->filter_resonance = src->filter_resonance; + + dst->true_filter_cutoff = src->true_filter_cutoff; + dst->true_filter_resonance = src->true_filter_resonance; + + dst->vibrato_speed = src->vibrato_speed; + dst->vibrato_depth = src->vibrato_depth; + dst->vibrato_n = src->vibrato_n; + dst->vibrato_time = src->vibrato_time; + dst->vibrato_waveform = src->vibrato_waveform; + + dst->tremolo_speed = src->tremolo_speed; + dst->tremolo_depth = src->tremolo_depth; + dst->tremolo_time = src->tremolo_time; + dst->tremolo_waveform = src->tremolo_waveform; + + dst->panbrello_speed = src->panbrello_speed; + dst->panbrello_depth = src->panbrello_depth; + dst->panbrello_time = src->panbrello_time; + dst->panbrello_waveform = src->panbrello_waveform; + dst->panbrello_random = src->panbrello_random; + + dst->sample_vibrato_time = src->sample_vibrato_time; + dst->sample_vibrato_waveform = src->sample_vibrato_waveform; + dst->sample_vibrato_depth = src->sample_vibrato_depth; + + dst->slide = src->slide; + dst->delta = src->delta; + dst->finetune = src->finetune; + + dst->volume_envelope = src->volume_envelope; + dst->pan_envelope = src->pan_envelope; + dst->pitch_envelope = src->pitch_envelope; + + dst->fadeoutcount = src->fadeoutcount; + + dst->filter_state[0] = src->filter_state[0]; + dst->filter_state[1] = src->filter_state[1]; + + dst->resampler = src->resampler; + dst->resampler.pickup_data = dst; + dst->resampler.fir_resampler_ratio = src->resampler.fir_resampler_ratio; + dst->resampler.fir_resampler[0] = resampler_dup( src->resampler.fir_resampler[0] ); + if ( !dst->resampler.fir_resampler[0] ) { + free( dst ); + return NULL; + } + dst->resampler.fir_resampler[1] = resampler_dup( src->resampler.fir_resampler[1] ); + if ( !dst->resampler.fir_resampler[1] ) { + resampler_delete( dst->resampler.fir_resampler[0] ); + free( dst ); + return NULL; + } + dst->time_lost = src->time_lost; + + //dst->output = src->output; + + return dst; +} + + + +static void dup_channel(IT_CHANNEL *dst, IT_CHANNEL *src) +{ + dst->flags = src->flags; + + dst->volume = src->volume; + dst->volslide = src->volslide; + dst->xm_volslide = src->xm_volslide; + dst->panslide = src->panslide; + + dst->pan = src->pan; + dst->truepan = src->truepan; + + dst->channelvolume = src->channelvolume; + dst->channelvolslide = src->channelvolslide; + + dst->instrument = src->instrument; + dst->note = src->note; + + dst->SFmacro = src->SFmacro; + + dst->filter_cutoff = src->filter_cutoff; + dst->filter_resonance = src->filter_resonance; + + dst->key_off_count = src->key_off_count; + dst->note_cut_count = src->note_cut_count; + dst->note_delay_count = src->note_delay_count; + dst->note_delay_entry = src->note_delay_entry; + + dst->new_note_action = src->new_note_action; + + dst->arpeggio_table = src->arpeggio_table; + memcpy(dst->arpeggio_offsets, src->arpeggio_offsets, sizeof(dst->arpeggio_offsets)); + dst->retrig = src->retrig; + dst->xm_retrig = src->xm_retrig; + dst->retrig_tick = src->retrig_tick; + + dst->tremor_time = src->tremor_time; + + dst->vibrato_waveform = src->vibrato_waveform; + dst->tremolo_waveform = src->tremolo_waveform; + dst->panbrello_waveform = src->panbrello_waveform; + + dst->portamento = src->portamento; + dst->toneporta = src->toneporta; + dst->toneslide = src->toneslide; + dst->toneslide_tick = src->toneslide_tick; + dst->last_toneslide_tick = src->last_toneslide_tick; + dst->ptm_toneslide = src->ptm_toneslide; + dst->ptm_last_toneslide = src->ptm_last_toneslide; + dst->okt_toneslide = src->okt_toneslide; + dst->destnote = src->destnote; + + dst->glissando = src->glissando; + + dst->sample = src->sample; + dst->truenote = src->truenote; + + dst->midi_state = src->midi_state; + + dst->lastvolslide = src->lastvolslide; + dst->lastDKL = src->lastDKL; + dst->lastEF = src->lastEF; + dst->lastG = src->lastG; + dst->lastHspeed = src->lastHspeed; + dst->lastHdepth = src->lastHdepth; + dst->lastRspeed = src->lastRspeed; + dst->lastRdepth = src->lastRdepth; + dst->lastYspeed = src->lastYspeed; + dst->lastYdepth = src->lastYdepth; + dst->lastI = src->lastI; + dst->lastJ = src->lastJ; + dst->lastN = src->lastN; + dst->lastO = src->lastO; + dst->high_offset = src->high_offset; + dst->lastP = src->lastP; + dst->lastQ = src->lastQ; + dst->lastS = src->lastS; + dst->pat_loop_row = src->pat_loop_row; + dst->pat_loop_count = src->pat_loop_count; + dst->pat_loop_end_row = src->pat_loop_end_row; + dst->lastW = src->lastW; + + dst->xm_lastE1 = src->xm_lastE1; + dst->xm_lastE2 = src->xm_lastE2; + dst->xm_lastEA = src->xm_lastEA; + dst->xm_lastEB = src->xm_lastEB; + dst->xm_lastX1 = src->xm_lastX1; + dst->xm_lastX2 = src->xm_lastX2; + + dst->inv_loop_delay = src->inv_loop_delay; + dst->inv_loop_speed = src->inv_loop_speed; + dst->inv_loop_offset = src->inv_loop_offset; + + dst->playing = dup_playing(src->playing, dst, src); + +#ifdef BIT_ARRAY_BULLSHIT + dst->played_patjump = bit_array_dup(src->played_patjump); + dst->played_patjump_order = src->played_patjump_order; +#endif + + //dst->output = src->output; +} + + + +/* Allocate the new callbacks first, then pass them to this function! + * It will free them on failure. + */ +static DUMB_IT_SIGRENDERER *dup_sigrenderer(DUMB_IT_SIGRENDERER *src, int n_channels, IT_CALLBACKS *callbacks) +{ + DUMB_IT_SIGRENDERER *dst; + int i; + + if (!src) { + if (callbacks) free(callbacks); + return NULL; + } + + dst = malloc(sizeof(*dst)); + if (!dst) { + if (callbacks) free(callbacks); + return NULL; + } + + dst->free_playing = NULL; + dst->sigdata = src->sigdata; + + dst->n_channels = n_channels; + + dst->resampling_quality = src->resampling_quality; + + dst->globalvolume = src->globalvolume; + dst->globalvolslide = src->globalvolslide; + + dst->tempo = src->tempo; + dst->temposlide = src->temposlide; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) + dup_channel(&dst->channel[i], &src->channel[i]); + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + dst->playing[i] = dup_playing(src->playing[i], dst->channel, src->channel); + + dst->tick = src->tick; + dst->speed = src->speed; + dst->rowcount = src->rowcount; + + dst->order = src->order; + dst->row = src->row; + dst->processorder = src->processorder; + dst->processrow = src->processrow; + dst->breakrow = src->breakrow; + + dst->restart_position = src->restart_position; + + dst->n_rows = src->n_rows; + + dst->entry_start = src->entry_start; + dst->entry = src->entry; + dst->entry_end = src->entry_end; + + dst->time_left = src->time_left; + dst->sub_time_left = src->sub_time_left; + + dst->ramp_style = src->ramp_style; + + dst->click_remover = NULL; + + dst->callbacks = callbacks; + +#ifdef BIT_ARRAY_BULLSHIT + dst->played = bit_array_dup(src->played); +#endif + + dst->gvz_time = src->gvz_time; + dst->gvz_sub_time = src->gvz_sub_time; + + //dst->max_output = src->max_output; + + return dst; +} + + + +static const IT_MIDI default_midi = { + /* unsigned char SFmacro[16][16]; */ + { + {0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + }, + /* unsigned char SFmacrolen[16]; */ + {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* unsigned short SFmacroz[16]; */ + /* Bitfield; bit 0 set = z in first position */ + { + 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, + /* unsigned char Zmacro[128][16]; */ + { + {0xF0, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + }, + /* unsigned char Zmacrolen[128]; */ + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + } +}; + + + +static void it_reset_filter_state(IT_FILTER_STATE *state) +{ + state->currsample = 0; + state->prevsample = 0; +} + + + +#define LOG10 2.30258509299 + +/* IMPORTANT: This function expects one extra sample in 'src' so it can apply + * click removal. It reads size samples, starting from src[0], and writes its + * output starting at dst[pos]. The pos parameter is required for getting + * click removal right. + */ + +static void it_filter_int(DUMB_CLICK_REMOVER *cr, IT_FILTER_STATE *state, sample_t *dst, int32 pos, sample_t *src, int32 size, int step, int sampfreq, int cutoff, int resonance) +{ + sample_t currsample = state->currsample; + sample_t prevsample = state->prevsample; + + float a, b, c; + + int32 datasize; + + { + float inv_angle = (float)(sampfreq * pow(0.5, 0.25 + cutoff*(1.0/(24< 2.0f) d = 2.0f; + d = (loss - d) * inv_angle; + e = inv_angle * inv_angle; + a = 1.0f / (1.0f + d + e); + c = -e * a; + b = 1.0f - a - c; +#else + a = 1.0f / (inv_angle*inv_angle + inv_angle*loss + loss); + c = -(inv_angle*inv_angle) * a; + b = 1.0f - a - c; +#endif + } + + dst += pos * step; + datasize = size * step; + +#define INT_FILTERS +#ifdef INT_FILTERS +#define SCALEB 12 + { + int ai = (int)(a * (1 << (16+SCALEB))); + int bi = (int)(b * (1 << (16+SCALEB))); + int ci = (int)(c * (1 << (16+SCALEB))); + int i; + + if (cr) { + sample_t startstep = MULSCA(src[0], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); + dumb_record_click(cr, pos, startstep); + } + + for (i = 0; i < datasize; i += step) { + { + sample_t newsample = MULSCA(src[i], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); + prevsample = currsample; + currsample = newsample; + } + dst[i] += currsample; + } + + if (cr) { + sample_t endstep = MULSCA(src[datasize], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); + dumb_record_click(cr, pos + size, -endstep); + } + } +#else +#error This version is broken - it does not use step, and state should contain floats for it + if (cr) { + float startstep = src[0]*a + currsample*b + prevsample*c; + dumb_record_click(cr, pos, (sample_t)startstep); + } + + { + int i = size % 3; + while (i > 0) { + { + float newsample = *src++*a + currsample*b + prevsample*c; + prevsample = currsample; + currsample = newsample; + } + *dst++ += (sample_t)currsample; + i--; + } + i = size / 3; + while (i > 0) { + float newsample; + /* Gotta love unrolled loops! */ + *dst++ += (sample_t)(newsample = *src++*a + currsample*b + prevsample*c); + *dst++ += (sample_t)(prevsample = *src++*a + newsample*b + currsample*c); + *dst++ += (sample_t)(currsample = *src++*a + prevsample*b + newsample*c); + i--; + } + } + + if (cr) { + float endstep = src[datasize]*a + currsample*b + prevsample*c; + dumb_record_click(cr, pos + size, -(sample_t)endstep); + } +#endif + + state->currsample = currsample; + state->prevsample = prevsample; +} + +#if defined(_USE_SSE) && (defined(_M_IX86) || defined(__i386__) || defined(_M_X64) || defined(__amd64__)) +#include + +static void it_filter_sse(DUMB_CLICK_REMOVER *cr, IT_FILTER_STATE *state, sample_t *dst, long pos, sample_t *src, long size, int step, int sampfreq, int cutoff, int resonance) +{ + __m128 data, impulse; + __m128 temp1, temp2; + + sample_t currsample = state->currsample; + sample_t prevsample = state->prevsample; + + float imp[4]; + + //profiler( filter_sse ); On ClawHammer Athlon64 3200+, ~12000 cycles, ~500 for that x87 setup code (as opposed to ~25500 for the original integer code) + + long datasize; + + { + float inv_angle = (float)(sampfreq * pow(0.5, 0.25 + cutoff*(1.0/(24< 2.0f) d = 2.0f; + d = (loss - d) * inv_angle; + e = inv_angle * inv_angle; + imp[0] = 1.0f / (1.0f + d + e); + imp[2] = -e * imp[0]; + imp[1] = 1.0f - imp[0] - imp[2]; +#else + imp[0] = 1.0f / (inv_angle*inv_angle + inv_angle*loss + loss); + imp[2] = -(inv_angle*inv_angle) * imp[0]; + imp[1] = 1.0f - imp[0] - imp[2]; +#endif + imp[3] = 0.0f; + } + + dst += pos * step; + datasize = size * step; + + { + int ai, bi, ci, i; + + if (cr) { + sample_t startstep; + ai = (int)(imp[0] * (1 << (16+SCALEB))); + bi = (int)(imp[1] * (1 << (16+SCALEB))); + ci = (int)(imp[2] * (1 << (16+SCALEB))); + startstep = MULSCA(src[0], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); + dumb_record_click(cr, pos, startstep); + } + + temp1 = _mm_setzero_ps(); + data = _mm_cvtsi32_ss( temp1, currsample ); + temp2 = _mm_cvtsi32_ss( temp1, prevsample ); + impulse = _mm_loadu_ps( (const float *) &imp ); + data = _mm_shuffle_ps( data, temp2, _MM_SHUFFLE(1, 0, 0, 1) ); + + for (i = 0; i < datasize; i += step) { + temp1 = _mm_cvtsi32_ss( data, src [i] ); + temp1 = _mm_mul_ps( temp1, impulse ); + temp2 = _mm_movehl_ps( temp2, temp1 ); + temp1 = _mm_add_ps( temp1, temp2 ); + temp2 = temp1; + temp2 = _mm_shuffle_ps( temp2, temp1, _MM_SHUFFLE(0, 0, 0, 1) ); + temp1 = _mm_add_ps( temp1, temp2 ); + temp1 = _mm_shuffle_ps( temp1, data, _MM_SHUFFLE(2, 1, 0, 0) ); + data = temp1; + dst [i] += _mm_cvtss_si32( temp1 ); + } + + currsample = _mm_cvtss_si32( temp1 ); + temp1 = _mm_shuffle_ps( temp1, data, _MM_SHUFFLE(0, 0, 0, 2) ); + prevsample = _mm_cvtss_si32( temp1 ); + + if (cr) { + sample_t endstep = MULSCA(src[datasize], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); + dumb_record_click(cr, pos + size, -endstep); + } + } + + state->currsample = currsample; + state->prevsample = prevsample; +} +#endif + +#undef LOG10 + +#ifdef _USE_SSE +#if defined(_M_IX86) || defined(__i386__) + +#ifdef _MSC_VER +#include +#elif defined(__clang__) || defined(__GNUC__) +static inline void +__cpuid(int *data, int selector) +{ +#if defined(__PIC__) && defined(__i386__) + asm("xchgl %%ebx, %%esi; cpuid; xchgl %%ebx, %%esi" + : "=a" (data[0]), + "=S" (data[1]), + "=c" (data[2]), + "=d" (data[3]) + : "0" (selector)); +#elif defined(__PIC__) && defined(__amd64__) + asm("xchg{q} {%%}rbx, %q1; cpuid; xchg{q} {%%}rbx, %q1" + : "=a" (data[0]), + "=&r" (data[1]), + "=c" (data[2]), + "=d" (data[3]) + : "0" (selector)); +#else + asm("cpuid" + : "=a" (data[0]), + "=b" (data[1]), + "=c" (data[2]), + "=d" (data[3]) + : "a"(selector)); +#endif +} +#else +#define __cpuid(a,b) memset((a), 0, sizeof(int) * 4) +#endif + +static int query_cpu_feature_sse() { + int buffer[4]; + __cpuid(buffer,1); + if ((buffer[3]&(1<<25)) == 0) return 0; + return 1; +} + +static int _dumb_it_use_sse = 0; + +void _dumb_init_sse() +{ + static int initialized = 0; + if (!initialized) + { + _dumb_it_use_sse = query_cpu_feature_sse(); + initialized = 1; + } +} + +#elif defined(_M_X64) || defined(__amd64__) + +static const int _dumb_it_use_sse = 1; + +void _dumb_init_sse() { } + +#else + +static const int _dumb_it_use_sse = 0; + +void _dumb_init_sse() { } + +#endif +#endif + +static void it_filter(DUMB_CLICK_REMOVER *cr, IT_FILTER_STATE *state, sample_t *dst, int32 pos, sample_t *src, int32 size, int step, int sampfreq, int cutoff, int resonance) +{ +#if defined(_USE_SSE) && (defined(_M_IX86) || defined(__i386__) || defined(_M_X64) || defined(__amd64__)) + _dumb_init_sse(); + if ( _dumb_it_use_sse ) it_filter_sse( cr, state, dst, pos, src, size, step, sampfreq, cutoff, resonance ); + else +#endif + it_filter_int( cr, state, dst, pos, src, size, step, sampfreq, cutoff, resonance ); +} + + + +static const signed char it_sine[256] = { + 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, + 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, + 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, + 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, + 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, + 0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23, + -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, + -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, + -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, + -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, + -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, + -24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2 +}; + + + +#if 1 +/** WARNING: use these! */ +/** JULIEN: Plus for XM compatibility it could be interesting to rename + * it_sawtooth[] to it_rampdown[], and add an it_rampup[]. + * Also, still for XM compat', twood be good if it was possible to tell the + * the player not to retrig' the waveform on a new instrument. + * Both of these are only for completness though, as I don't think it would + * be very noticeable ;) + */ +/** ENTHEH: IT also has the 'don't retrig' thingy :) */ +static const signed char it_sawtooth[256] = { + 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56, + 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, + 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, + 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, + 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, + 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16, + 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8, + -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16, + -16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24, + -24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32, + -32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40, + -40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48, + -48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56, + -56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64 +}; + +static const signed char it_squarewave[256] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const signed char it_xm_ramp[256] = { + 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8, + -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16, + -16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24, + -24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32, + -32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40, + -40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48, + -48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56, + -56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64, + 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56, + 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, + 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, + 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, + 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, + 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16, + 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0 +}; + +static const signed char it_xm_squarewave[256] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64 +}; + +#endif + + + +static void reset_tick_counts(DUMB_IT_SIGRENDERER *sigrenderer) +{ + int i; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; + channel->key_off_count = 0; + channel->note_cut_count = 0; + channel->note_delay_count = 0; + } +} + + + +static const unsigned char arpeggio_mod[32] = {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1}; +static const unsigned char arpeggio_xm[32] = {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; +static const unsigned char arpeggio_okt_3[32] = {1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0}; +static const unsigned char arpeggio_okt_4[32] = {0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1}; +static const unsigned char arpeggio_okt_5[32] = {2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2}; + + + +static void reset_channel_effects(IT_CHANNEL *channel) +{ + channel->volslide = 0; + channel->xm_volslide = 0; + channel->panslide = 0; + channel->channelvolslide = 0; + channel->arpeggio_table = (const unsigned char *) &arpeggio_mod; + memset(channel->arpeggio_offsets, 0, sizeof(channel->arpeggio_offsets)); + channel->retrig = 0; + if (channel->xm_retrig) { + channel->xm_retrig = 0; + channel->retrig_tick = 0; + } + channel->tremor_time &= 127; + channel->portamento = 0; + channel->toneporta = 0; + if (channel->ptm_toneslide) { + channel->ptm_last_toneslide = channel->ptm_toneslide; + channel->last_toneslide_tick = channel->toneslide_tick; + } else + channel->ptm_last_toneslide = 0; + channel->ptm_toneslide = 0; + channel->toneslide_tick = 0; + channel->okt_toneslide = 0; + if (channel->playing) { + channel->playing->vibrato_n = 0; + channel->playing->tremolo_speed = 0; + channel->playing->tremolo_depth = 0; + channel->playing->panbrello_speed = 0; + } +} + +static void reset_effects(DUMB_IT_SIGRENDERER *sigrenderer) +{ + int i; + + sigrenderer->globalvolslide = 0; + sigrenderer->temposlide = 0; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + reset_channel_effects(&sigrenderer->channel[i]); + } +} + + + +static void update_tremor(IT_CHANNEL *channel) +{ + if ((channel->tremor_time & 128) && channel->playing) { + if (channel->tremor_time == 128) + channel->tremor_time = (channel->lastI >> 4) | 192; + else if (channel->tremor_time == 192) + channel->tremor_time = (channel->lastI & 15) | 128; + else + channel->tremor_time--; + } +} + + + +static void it_pickup_loop(DUMB_RESAMPLER *resampler, void *data) +{ + resampler->pos -= resampler->end - resampler->start; + ((IT_PLAYING *)data)->time_lost += resampler->end - resampler->start; +} + + + +static void it_pickup_pingpong_loop(DUMB_RESAMPLER *resampler, void *data) +{ + if (resampler->dir < 0) { + resampler->pos = (resampler->start << 1) - 1 - resampler->pos; + resampler->subpos ^= 65535; + resampler->dir = 1; + ((IT_PLAYING *)data)->time_lost += (resampler->end - resampler->start) << 1; + } else { + resampler->pos = (resampler->end << 1) - 1 - resampler->pos; + resampler->subpos ^= 65535; + resampler->dir = -1; + } +} + + + +static void it_pickup_stop_at_end(DUMB_RESAMPLER *resampler, void *data) +{ + (void)data; + + if (resampler->dir < 0) { + resampler->pos = (resampler->start << 1) - 1 - resampler->pos; + resampler->subpos ^= 65535; + /* By rights, time_lost would be updated here. However, there is no + * need at this point; it will not be used. + * + * ((IT_PLAYING *)data)->time_lost += (resampler->src_end - resampler->src_start) << 1; + */ + resampler->dir = 1; + } else + resampler->dir = 0; +} + + + +static void it_pickup_stop_after_reverse(DUMB_RESAMPLER *resampler, void *data) +{ + (void)data; + + resampler->dir = 0; +} + + + +static void it_playing_update_resamplers(IT_PLAYING *playing) +{ + if ((playing->sample->flags & IT_SAMPLE_SUS_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) { + playing->resampler.start = playing->sample->sus_loop_start; + playing->resampler.end = playing->sample->sus_loop_end; + if (playing->resampler.start == playing->resampler.end) + playing->resampler.pickup = &it_pickup_stop_at_end; + else if (playing->sample->flags & IT_SAMPLE_PINGPONG_SUS_LOOP) + playing->resampler.pickup = &it_pickup_pingpong_loop; + else + playing->resampler.pickup = &it_pickup_loop; + } else if (playing->sample->flags & IT_SAMPLE_LOOP) { + playing->resampler.start = playing->sample->loop_start; + playing->resampler.end = playing->sample->loop_end; + if (playing->resampler.start == playing->resampler.end) + playing->resampler.pickup = &it_pickup_stop_at_end; + else if (playing->sample->flags & IT_SAMPLE_PINGPONG_LOOP) + playing->resampler.pickup = &it_pickup_pingpong_loop; + else + playing->resampler.pickup = &it_pickup_loop; + } else if (playing->flags & IT_PLAYING_REVERSE) { + playing->resampler.start = 0; + playing->resampler.end = playing->sample->length; + playing->resampler.dir = -1; + playing->resampler.pickup = &it_pickup_stop_after_reverse; + } else { + if (playing->sample->flags & IT_SAMPLE_SUS_LOOP) + playing->resampler.start = playing->sample->sus_loop_start; + else + playing->resampler.start = 0; + playing->resampler.end = playing->sample->length; + playing->resampler.pickup = &it_pickup_stop_at_end; + } + ASSERT(playing->resampler.pickup_data == playing); +} + + + +/* This should be called whenever the sample or sample position changes. */ +static void it_playing_reset_resamplers(IT_PLAYING *playing, int32 pos) +{ + int bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8; + int quality = playing->resampling_quality; + int channels = playing->sample->flags & IT_SAMPLE_STEREO ? 2 : 1; + if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality) + quality = playing->sample->max_resampling_quality; + dumb_reset_resampler_n(bits, &playing->resampler, playing->sample->data, channels, pos, 0, 0, quality); + playing->resampler.pickup_data = playing; + playing->time_lost = 0; + playing->flags &= ~IT_PLAYING_DEAD; + it_playing_update_resamplers(playing); +} + +static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel); + +/* Should we only be retriggering short samples on XM? */ + +static void update_retrig(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel) +{ + if (channel->xm_retrig) { + channel->retrig_tick--; + if (channel->retrig_tick <= 0) { + if (channel->playing) { + it_playing_reset_resamplers(channel->playing, 0); + channel->playing->declick_stage = 0; + } else if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) it_retrigger_note(sigrenderer, channel); + channel->retrig_tick = channel->xm_retrig; + } + } else if (channel->retrig & 0x0F) { + channel->retrig_tick--; + if (channel->retrig_tick <= 0) { + if (channel->retrig < 0x10) { + } else if (channel->retrig < 0x20) { + channel->volume--; + if (channel->volume > 64) channel->volume = 0; + } else if (channel->retrig < 0x30) { + channel->volume -= 2; + if (channel->volume > 64) channel->volume = 0; + } else if (channel->retrig < 0x40) { + channel->volume -= 4; + if (channel->volume > 64) channel->volume = 0; + } else if (channel->retrig < 0x50) { + channel->volume -= 8; + if (channel->volume > 64) channel->volume = 0; + } else if (channel->retrig < 0x60) { + channel->volume -= 16; + if (channel->volume > 64) channel->volume = 0; + } else if (channel->retrig < 0x70) { + channel->volume <<= 1; + channel->volume /= 3; + } else if (channel->retrig < 0x80) { + channel->volume >>= 1; + } else if (channel->retrig < 0x90) { + } else if (channel->retrig < 0xA0) { + channel->volume++; + if (channel->volume > 64) channel->volume = 64; + } else if (channel->retrig < 0xB0) { + channel->volume += 2; + if (channel->volume > 64) channel->volume = 64; + } else if (channel->retrig < 0xC0) { + channel->volume += 4; + if (channel->volume > 64) channel->volume = 64; + } else if (channel->retrig < 0xD0) { + channel->volume += 8; + if (channel->volume > 64) channel->volume = 64; + } else if (channel->retrig < 0xE0) { + channel->volume += 16; + if (channel->volume > 64) channel->volume = 64; + } else if (channel->retrig < 0xF0) { + channel->volume *= 3; + channel->volume >>= 1; + if (channel->volume > 64) channel->volume = 64; + } else { + channel->volume <<= 1; + if (channel->volume > 64) channel->volume = 64; + } + if (channel->playing) { + it_playing_reset_resamplers(channel->playing, 0); + channel->playing->declick_stage = 0; + } else if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) it_retrigger_note(sigrenderer, channel); + channel->retrig_tick = channel->retrig & 0x0F; + } + } +} + + +static void update_smooth_effects_playing(IT_PLAYING *playing) +{ + playing->vibrato_time += playing->vibrato_n * + (playing->vibrato_speed << 2); + playing->tremolo_time += playing->tremolo_speed << 2; + playing->panbrello_time += playing->panbrello_speed; + if (playing->panbrello_waveform == 3) + playing->panbrello_random = (rand() % 129) - 64; +} + +static void update_smooth_effects(DUMB_IT_SIGRENDERER *sigrenderer) +{ + int i; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; + IT_PLAYING *playing = channel->playing; + + if (playing) { + update_smooth_effects_playing(playing); + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + IT_PLAYING *playing = sigrenderer->playing[i]; + + if (playing) { + update_smooth_effects_playing(playing); + } + } +} + + +static const unsigned char pt_tab_invloop[16] = +{ + 0x00, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0D, + 0x0F, 0x13, 0x16, 0x1A, 0x20, 0x2B, 0x40, 0x80 +}; + +static void update_invert_loop(IT_CHANNEL *channel, IT_SAMPLE *sample) +{ + channel->inv_loop_delay += pt_tab_invloop[channel->inv_loop_speed]; + if (channel->inv_loop_delay >= 0x80) + { + channel->inv_loop_delay = 0; + + if (sample && ((sample->flags & (IT_SAMPLE_EXISTS | IT_SAMPLE_LOOP)) == (IT_SAMPLE_EXISTS | IT_SAMPLE_LOOP)) && !(sample->flags & (IT_SAMPLE_STEREO | IT_SAMPLE_16BIT))) + { + if (sample->loop_end - sample->loop_start >= 4) + { + channel->inv_loop_offset++; + if (channel->inv_loop_offset >= (sample->loop_end - sample->loop_start)) channel->inv_loop_offset = 0; + + ((char *)sample->data)[sample->loop_start + channel->inv_loop_offset] ^= 0xFF; + } + } + } +} + + +static void update_playing_effects(IT_PLAYING *playing) +{ + IT_CHANNEL *channel = playing->channel; + + if (channel->channelvolslide) { + playing->channel_volume = channel->channelvolume; + } + + if (channel->okt_toneslide) { + if (channel->okt_toneslide--) { + playing->note += channel->toneslide; + if (playing->note >= 120) { + if (channel->toneslide < 0) playing->note = 0; + else playing->note = 119; + } + } + } else if (channel->ptm_toneslide) { + if (--channel->toneslide_tick == 0) { + channel->toneslide_tick = channel->ptm_toneslide; + if (playing) { + playing->note += channel->toneslide; + if (playing->note >= 120) { + if (channel->toneslide < 0) playing->note = 0; + else playing->note = 119; + } + if (channel->playing == playing) { + channel->note = channel->truenote = playing->note; + } + if (channel->toneslide_retrig) { + it_playing_reset_resamplers(playing, 0); + playing->declick_stage = 0; + } + } + } + } +} + + +static void update_effects(DUMB_IT_SIGRENDERER *sigrenderer) +{ + int i; + + if (sigrenderer->globalvolslide) { + sigrenderer->globalvolume += sigrenderer->globalvolslide; + if (sigrenderer->globalvolume > 128) { + if (sigrenderer->globalvolslide >= 0) + sigrenderer->globalvolume = 128; + else + sigrenderer->globalvolume = 0; + } + } + + if (sigrenderer->temposlide) { + sigrenderer->tempo += sigrenderer->temposlide; + if (sigrenderer->tempo < 32) { + if (sigrenderer->temposlide >= 0) + sigrenderer->tempo = 255; + else + sigrenderer->tempo = 32; + } + } + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; + IT_PLAYING *playing = channel->playing; + + if (channel->xm_volslide) { + channel->volume += channel->xm_volslide; + if (channel->volume > 64) { + if (channel->xm_volslide >= 0) + channel->volume = 64; + else + channel->volume = 0; + } + } + + if (channel->volslide) { + int clip = (sigrenderer->sigdata->flags & IT_WAS_AN_S3M) ? 63 : 64; + channel->volume += channel->volslide; + if (channel->volume > clip) { + if (channel->volslide >= 0) + channel->volume = clip; + else + channel->volume = 0; + } + } + + if (channel->panslide) { + if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) { + if (IT_IS_SURROUND(channel->pan)) + { + channel->pan = 32; + channel->truepan = 32 + 128 * 64; + } + if (channel->panslide == -128) + channel->truepan = 32; + else + channel->truepan = MID(32, channel->truepan + channel->panslide*64, 32+255*64); + } else { + if (IT_IS_SURROUND(channel->pan)) + { + channel->pan = 32; + } + channel->pan += channel->panslide; + if (channel->pan > 64) { + if (channel->panslide >= 0) + channel->pan = 64; + else + channel->pan = 0; + } + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + } + } + + if (channel->channelvolslide) { + channel->channelvolume += channel->channelvolslide; + if (channel->channelvolume > 64) { + if (channel->channelvolslide >= 0) + channel->channelvolume = 64; + else + channel->channelvolume = 0; + } + } + + update_tremor(channel); + + update_retrig(sigrenderer, channel); + + if (channel->inv_loop_speed) update_invert_loop(channel, playing ? playing->sample : NULL); + + if (playing) { + playing->slide += channel->portamento; + + if (sigrenderer->sigdata->flags & IT_LINEAR_SLIDES) { + if (channel->toneporta && channel->destnote < 120) { + int currpitch = ((playing->note - 60) << 8) + playing->slide; + int destpitch = (channel->destnote - 60) << 8; + if (currpitch > destpitch) { + currpitch -= channel->toneporta; + if (currpitch < destpitch) { + currpitch = destpitch; + channel->destnote = IT_NOTE_OFF; + } + } else if (currpitch < destpitch) { + currpitch += channel->toneporta; + if (currpitch > destpitch) { + currpitch = destpitch; + channel->destnote = IT_NOTE_OFF; + } + } + playing->slide = currpitch - ((playing->note - 60) << 8); + } + } else { + if (channel->toneporta && channel->destnote < 120) { + float amiga_multiplier = playing->sample->C5_speed * (1.0f / AMIGA_DIVISOR); + + float deltanote = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note); + /* deltanote is 1.0 for C-5, 0.5 for C-6, etc. */ + + float deltaslid = deltanote - playing->slide * amiga_multiplier; + + float destdelta = (float)pow(DUMB_SEMITONE_BASE, 60 - channel->destnote); + if (deltaslid < destdelta) { + playing->slide -= channel->toneporta; + deltaslid = deltanote - playing->slide * amiga_multiplier; + if (deltaslid > destdelta) { + playing->note = channel->destnote; + playing->slide = 0; + channel->destnote = IT_NOTE_OFF; + } + } else { + playing->slide += channel->toneporta; + deltaslid = deltanote - playing->slide * amiga_multiplier; + if (deltaslid < destdelta) { + playing->note = channel->destnote; + playing->slide = 0; + channel->destnote = IT_NOTE_OFF; + } + } + } + } + + update_playing_effects(playing); + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + IT_PLAYING *playing = sigrenderer->playing[i]; + if (playing) update_playing_effects(playing); + } + + update_smooth_effects(sigrenderer); +} + + +static void it_note_off(IT_PLAYING *playing); + +// This function should be renamed; it doesn't do the 'Update Pattern Variables' operation ittech.txt describes +/* Returns 1 if a pattern loop is happening. */ +static int update_pattern_variables(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) +{ + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + + if (entry->mask & IT_ENTRY_EFFECT) { + switch (entry->effect) { + case IT_JUMP_TO_ORDER: + /* XXX jump and break in same row */ + if ( ( ( sigrenderer->processrow | 0xC00 ) == 0xFFFE ) && + ! ( sigrenderer->processrow & 0x800 ) ) { + sigrenderer->processrow = 0xFFFE & ~0xC00; + } else { + sigrenderer->breakrow = 0; + sigrenderer->processrow = 0xFFFE & ~0x400; + } + sigrenderer->processorder = entry->effectvalue - 1; + break; + + case IT_S: + { + unsigned char effectvalue = entry->effectvalue; + if (sigrenderer->sigdata->flags & IT_WAS_AN_S3M) { + if (effectvalue == 0) + effectvalue = channel->lastDKL; + channel->lastDKL = effectvalue; + } else { + if (effectvalue == 0) + effectvalue = channel->lastS; + } + channel->lastS = effectvalue; + switch (effectvalue >> 4) { + case IT_S_PATTERN_LOOP: + { + unsigned char v = effectvalue & 15; + if (v == 0) { +#ifdef BIT_ARRAY_BULLSHIT + if (!channel->played_patjump) + channel->played_patjump = bit_array_create(256); + else { + if ( channel->played_patjump_order != 0xFFFE && channel->played_patjump_order != sigrenderer->order ) + bit_array_merge(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256); + //if (channel->played_patjump_order != sigrenderer->order) + bit_array_reset(channel->played_patjump); + } + channel->played_patjump_order = sigrenderer->order; +#endif + channel->pat_loop_row = sigrenderer->processrow; + } else { + if (channel->pat_loop_count == 0) { +#ifdef BIT_ARRAY_BULLSHIT + /* wft, uninitialized and no start marker yet... */ + if (channel->played_patjump_order == 0xFFFE) { + int n; + bit_array_destroy(channel->played_patjump); + channel->played_patjump = bit_array_create(256); + for (n = channel->pat_loop_row; n <= sigrenderer->row; n++) + bit_array_clear(sigrenderer->played, sigrenderer->order * 256 + n); + channel->played_patjump_order = sigrenderer->order; + } else if (channel->played_patjump_order == sigrenderer->order) { + bit_array_set(channel->played_patjump, sigrenderer->row); + bit_array_mask(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256); + //bit_array_reset(channel->played_patjump); + } +#endif + channel->pat_loop_count = v; + sigrenderer->breakrow = channel->pat_loop_row; + if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) { + /* For XM files, if a loop occurs by itself, keep breakrow set for when the pattern ends - fun bug in FT2! */ + if ((sigrenderer->processrow|0xC00) < 0xFFFE) { + /* Infinite pattern loops are possible, so we check whether the pattern loop we're hitting now is earlier than the last one we hit. */ + if (sigrenderer->processrow < channel->pat_loop_end_row) + sigrenderer->processorder = 0xFFFE; /* suspect infinite loop, so trigger loop callback */ + else + sigrenderer->processorder = 0xFFFF; /* don't trigger loop callback */ + channel->pat_loop_end_row = sigrenderer->processrow; + sigrenderer->processrow = 0xFFFF; /* special case: don't reset breakrow or pat_loop_end_row */ + } + } else { + /* IT files do this regardless of other flow control effects seen here. */ + sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */ + sigrenderer->processrow = 0xFFFE; + } + return 1; + } else if (--channel->pat_loop_count) { +#ifdef BIT_ARRAY_BULLSHIT + if (channel->played_patjump_order == sigrenderer->order) { + bit_array_set(channel->played_patjump, sigrenderer->row); + bit_array_mask(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256); + //bit_array_reset(channel->played_patjump); + } +#endif + sigrenderer->breakrow = channel->pat_loop_row; + if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) { + /* For XM files, if a loop occurs by itself, keep breakrow set for when the pattern ends - fun bug in FT2! */ + if ((sigrenderer->processrow|0xC00) < 0xFFFE) { + /* Infinite pattern loops are possible, so we check whether the pattern loop we're hitting now is earlier than the last one we hit. */ + if (sigrenderer->processrow < channel->pat_loop_end_row) + sigrenderer->processorder = 0xFFFE; /* suspect infinite loop, so trigger loop callback */ + else + sigrenderer->processorder = 0xFFFF; /* don't trigger loop callback */ + channel->pat_loop_end_row = sigrenderer->processrow; + sigrenderer->processrow = 0xFFFF; /* special case: don't reset breakrow or pat_loop_end_row */ + } + } else { + /* IT files do this regardless of other flow control effects seen here. */ + sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */ + sigrenderer->processrow = 0xFFFE; + } + return 1; + } else if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) { + channel->pat_loop_end_row = 0; + // TODO + /* Findings: + - If a pattern loop completes successfully, and then the pattern terminates, then the next pattern will start on the row corresponding to the E60. + - If a pattern loop doesn't do any loops, and then the pattern terminates, then the next pattern will start on the first row. + - If a break appears to the left of the pattern loop, it jumps into the relevant position in the next pattern, and that's it. + - If a break appears to the right of the pattern loop, it jumps to the start of the next pattern, and that's it. + - If we jump, then effect a loop using an old E60, and then the pattern ends, the next pattern starts on the row corresponding to the E60. + - Theory: breakrow is not cleared when it's a pattern loop effect! + */ + if ((sigrenderer->processrow | 0xC00) < 0xFFFE) // I have no idea if this is correct or not - FT2 is so weird :( + sigrenderer->breakrow = channel->pat_loop_row; /* emulate bug in FT2 */ + } else + channel->pat_loop_row = sigrenderer->processrow + 1; +#ifdef BIT_ARRAY_BULLSHIT + /*channel->played_patjump_order |= 0x8000;*/ + if (channel->played_patjump_order == sigrenderer->order) { + bit_array_destroy(channel->played_patjump); + channel->played_patjump = 0; + channel->played_patjump_order = 0xFFFE; + } + bit_array_clear(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row); +#endif + } + } + break; + case IT_S_PATTERN_DELAY: + sigrenderer->rowcount = 1 + (effectvalue & 15); + break; + } + } + } + } + + return 0; +} + + + +/* This function guarantees that channel->sample will always be valid if it + * is nonzero. In other words, to check if it is valid, simply check if it is + * nonzero. + */ +static void instrument_to_sample(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) +{ + if (sigdata->flags & IT_USE_INSTRUMENTS) { + if (channel->instrument >= 1 && channel->instrument <= sigdata->n_instruments) { + if (channel->note < 120) { + channel->sample = sigdata->instrument[channel->instrument-1].map_sample[channel->note]; + channel->truenote = sigdata->instrument[channel->instrument-1].map_note[channel->note]; + } else + channel->sample = 0; + } else + channel->sample = 0; + } else { + channel->sample = channel->instrument; + channel->truenote = channel->note; + } + if (!(channel->sample >= 1 && channel->sample <= sigdata->n_samples && (sigdata->sample[channel->sample-1].flags & IT_SAMPLE_EXISTS) && sigdata->sample[channel->sample-1].C5_speed)) + channel->sample = 0; +} + + + +static void fix_sample_looping(IT_PLAYING *playing) +{ + if ((playing->sample->flags & (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP)) == + (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP)) { + if (playing->resampler.dir < 0) { + playing->resampler.pos = (playing->sample->sus_loop_end << 1) - 1 - playing->resampler.pos; + playing->resampler.subpos ^= 65535; + playing->resampler.dir = 1; + } + + playing->resampler.pos += playing->time_lost; + // XXX what + playing->time_lost = 0; + } +} + + + +static void it_compatible_gxx_retrigger(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) +{ + int flags = 0; + if (channel->sample) { + if (sigdata->flags & IT_USE_INSTRUMENTS) { + if (!(channel->playing->flags & IT_PLAYING_SUSTAINOFF)) { + if (channel->playing->env_instrument->volume_envelope.flags & IT_ENVELOPE_CARRY) + flags |= 1; + if (channel->playing->env_instrument->pan_envelope.flags & IT_ENVELOPE_CARRY) + flags |= 2; + if (channel->playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_CARRY) + flags |= 4; + } + } + } + if (!(flags & 1)) { + channel->playing->volume_envelope.next_node = 0; + channel->playing->volume_envelope.tick = 0; + } + if (!(flags & 2)) { + channel->playing->pan_envelope.next_node = 0; + channel->playing->pan_envelope.tick = 0; + } + if (!(flags & 4)) { + channel->playing->pitch_envelope.next_node = 0; + channel->playing->pitch_envelope.tick = 0; + } + channel->playing->fadeoutcount = 1024; + // Should we remove IT_PLAYING_BACKGROUND? Test with sample with sustain loop... + channel->playing->flags &= ~(IT_PLAYING_BACKGROUND | IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING | IT_PLAYING_DEAD); + it_playing_update_resamplers(channel->playing); + + if (!flags && channel->sample) + if (sigdata->flags & IT_USE_INSTRUMENTS) + channel->playing->env_instrument = &sigdata->instrument[channel->instrument-1]; +} + + + +static void it_note_off(IT_PLAYING *playing) +{ + if (playing) { + playing->enabled_envelopes |= IT_ENV_VOLUME; + playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_SUSTAINOFF; + fix_sample_looping(playing); + it_playing_update_resamplers(playing); + if (playing->instrument) + if ((playing->instrument->volume_envelope.flags & (IT_ENVELOPE_ON | IT_ENVELOPE_LOOP_ON)) != IT_ENVELOPE_ON) + playing->flags |= IT_PLAYING_FADING; + } +} + + + +static void xm_note_off(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) +{ + if (channel->playing) { + if (!channel->instrument || channel->instrument > sigdata->n_instruments || + !(sigdata->instrument[channel->instrument-1].volume_envelope.flags & IT_ENVELOPE_ON)) + //if (!(entry->mask & IT_ENTRY_INSTRUMENT)) + // dunno what that was there for ... + channel->volume = 0; + channel->playing->flags |= IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING; + it_playing_update_resamplers(channel->playing); + } +} + + +static void recalculate_it_envelope_node(IT_PLAYING_ENVELOPE *pe, IT_ENVELOPE *e) +{ + int envpos = pe->tick; + unsigned int pt = e->n_nodes - 1; + unsigned int i; + for (i = 0; i < (unsigned int)(e->n_nodes - 1); ++i) + { + if (envpos <= e->node_t[i]) + { + pt = i; + break; + } + } + pe->next_node = pt; +} + + +static void recalculate_it_envelope_nodes(IT_PLAYING *playing) +{ + recalculate_it_envelope_node(&playing->volume_envelope, &playing->env_instrument->volume_envelope); + recalculate_it_envelope_node(&playing->pan_envelope, &playing->env_instrument->pitch_envelope); + recalculate_it_envelope_node(&playing->pitch_envelope, &playing->env_instrument->pitch_envelope); +} + + +static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel) +{ + int vol_env_tick = 0; + int pan_env_tick = 0; + int pitch_env_tick = 0; + + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + unsigned char nna = ~0; + int i, envelopes_copied = 0; + + if (channel->playing) { + if (channel->note == IT_NOTE_CUT) + nna = NNA_NOTE_CUT; + else if (channel->note == IT_NOTE_OFF) + nna = NNA_NOTE_OFF; + else if (channel->note > 120) + nna = NNA_NOTE_FADE; + else if (!channel->playing->instrument || (channel->playing->flags & IT_PLAYING_DEAD)) + nna = NNA_NOTE_CUT; + else if (channel->new_note_action != 0xFF) + { + nna = channel->new_note_action; + } + else + nna = channel->playing->instrument->new_note_action; + + if (!(channel->playing->flags & IT_PLAYING_SUSTAINOFF)) + { + if (nna != NNA_NOTE_CUT) + vol_env_tick = channel->playing->volume_envelope.tick; + pan_env_tick = channel->playing->pan_envelope.tick; + pitch_env_tick = channel->playing->pitch_envelope.tick; + envelopes_copied = 1; + } + + switch (nna) { + case NNA_NOTE_CUT: + channel->playing->declick_stage = 3; + break; + case NNA_NOTE_OFF: + it_note_off(channel->playing); + break; + case NNA_NOTE_FADE: + channel->playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_FADING; + break; + } + } + + channel->new_note_action = 0xFF; + + if (channel->sample == 0 || channel->note > 120) + return; + + channel->destnote = IT_NOTE_OFF; + + if (channel->playing) { + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + sigrenderer->playing[i] = channel->playing; + channel->playing = NULL; + break; + } + } + + if (sigrenderer->sigdata->flags & IT_USE_INSTRUMENTS) + { + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + IT_PLAYING * playing = sigrenderer->playing[i]; + if (playing && playing->channel == channel && playing->instrument->dup_check_type) { + int match = 1; + switch (playing->instrument->dup_check_type) + { + case DCT_NOTE: + match = (channel->truenote == playing->note); + case DCT_SAMPLE: + match = match && (channel->sample == playing->sampnum); + case DCT_INSTRUMENT: + match = match && (channel->instrument == playing->instnum); + break; + } + + if (match) + { + switch (playing->instrument->dup_check_action) + { + case DCA_NOTE_CUT: + playing->declick_stage = 3; + if (channel->playing == playing) channel->playing = NULL; + break; + case DCA_NOTE_OFF: + if (!(playing->flags & IT_PLAYING_SUSTAINOFF)) + it_note_off(playing); + break; + case DCA_NOTE_FADE: + playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_FADING; + break; + } + } + } + } + } + +/** WARNING - come up with some more heuristics for replacing old notes */ +#if 0 + if (channel->playing) { + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]->flags & IT_PLAYING_BACKGROUND) { + write_seqtime(); + sequence_c(SEQUENCE_STOP_SIGNAL); + sequence_c(i); + channel->VChannel = &module->VChannel[i]; + break; + } + } + } +#endif + } + + if (channel->playing) + free_playing(sigrenderer, channel->playing); + + channel->playing = new_playing(sigrenderer); + + if (!channel->playing) + return; + + if (!envelopes_copied && sigdata->flags & IT_USE_INSTRUMENTS) { + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + IT_PLAYING * playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + if (playing->flags & IT_PLAYING_SUSTAINOFF) continue; + if (nna != NNA_NOTE_CUT) + vol_env_tick = playing->volume_envelope.tick; + pan_env_tick = playing->pan_envelope.tick; + pitch_env_tick = playing->pitch_envelope.tick; + envelopes_copied = 1; + break; + } + } + + channel->playing->flags = 0; + channel->playing->resampling_quality = sigrenderer->resampling_quality; + channel->playing->channel = channel; + channel->playing->sample = &sigdata->sample[channel->sample-1]; + if (sigdata->flags & IT_USE_INSTRUMENTS) + channel->playing->instrument = &sigdata->instrument[channel->instrument-1]; + else + channel->playing->instrument = NULL; + channel->playing->env_instrument = channel->playing->instrument; + channel->playing->sampnum = channel->sample; + channel->playing->instnum = channel->instrument; + channel->playing->declick_stage = 0; + channel->playing->channel_volume = channel->channelvolume; + channel->playing->note = channel->truenote; + channel->playing->enabled_envelopes = 0; + channel->playing->volume_offset = 0; + channel->playing->panning_offset = 0; + //channel->playing->output = channel->output; + if (sigdata->flags & IT_USE_INSTRUMENTS) { + IT_PLAYING * playing = channel->playing; + IT_INSTRUMENT * instrument = playing->instrument; + if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_VOLUME; + if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PANNING; + if (instrument->pitch_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PITCH; + if (instrument->random_volume) playing->volume_offset = (rand() % (instrument->random_volume * 2 + 1)) - instrument->random_volume; + if (instrument->random_pan) playing->panning_offset = (rand() % (instrument->random_pan * 2 + 1)) - instrument->random_pan; + //if (instrument->output) playing->output = instrument->output; + } + channel->playing->filter_cutoff = 127; + channel->playing->filter_resonance = 0; + channel->playing->true_filter_cutoff = 127 << 8; + channel->playing->true_filter_resonance = 0; + channel->playing->vibrato_speed = 0; + channel->playing->vibrato_depth = 0; + channel->playing->vibrato_n = 0; + channel->playing->vibrato_time = 0; + channel->playing->vibrato_waveform = channel->vibrato_waveform; + channel->playing->tremolo_speed = 0; + channel->playing->tremolo_depth = 0; + channel->playing->tremolo_time = 0; + channel->playing->tremolo_waveform = channel->tremolo_waveform; + channel->playing->panbrello_speed = 0; + channel->playing->panbrello_depth = 0; + channel->playing->panbrello_time = 0; + channel->playing->panbrello_waveform = channel->panbrello_waveform; + channel->playing->panbrello_random = 0; + channel->playing->sample_vibrato_time = 0; + channel->playing->sample_vibrato_waveform = channel->playing->sample->vibrato_waveform; + channel->playing->sample_vibrato_depth = 0; + channel->playing->slide = 0; + channel->playing->finetune = channel->playing->sample->finetune; + + if (sigdata->flags & IT_USE_INSTRUMENTS) + { + if (envelopes_copied && channel->playing->env_instrument->volume_envelope.flags & IT_ENVELOPE_CARRY) { + channel->playing->volume_envelope.tick = vol_env_tick; + } else { + channel->playing->volume_envelope.tick = 0; + } + if (envelopes_copied && channel->playing->env_instrument->pan_envelope.flags & IT_ENVELOPE_CARRY) { + channel->playing->pan_envelope.tick = pan_env_tick; + } else { + channel->playing->pan_envelope.tick = 0; + } + if (envelopes_copied && channel->playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_CARRY) { + channel->playing->pitch_envelope.tick = pitch_env_tick; + } else { + channel->playing->pitch_envelope.tick = 0; + } + recalculate_it_envelope_nodes(channel->playing); + } + channel->playing->fadeoutcount = 1024; + it_reset_filter_state(&channel->playing->filter_state[0]); + it_reset_filter_state(&channel->playing->filter_state[1]); + it_playing_reset_resamplers(channel->playing, 0); + + /** WARNING - is everything initialised? */ +} + + + +static void get_default_volpan(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) +{ + if (channel->sample == 0) + return; + + channel->volume = sigdata->sample[channel->sample-1].default_volume; + + if (sigdata->flags & IT_WAS_AN_XM) { + if (!(sigdata->flags & IT_WAS_A_MOD)) + channel->truepan = 32 + sigdata->sample[channel->sample-1].default_pan*64; + return; + } + + { + int pan = sigdata->sample[channel->sample-1].default_pan; + if (pan >= 128 && pan <= 192) { + channel->pan = pan - 128; + return; + } + } + + if (sigdata->flags & IT_USE_INSTRUMENTS) { + IT_INSTRUMENT *instrument = &sigdata->instrument[channel->instrument-1]; + if (instrument->default_pan <= 64) + channel->pan = instrument->default_pan; + if (instrument->filter_cutoff >= 128) + channel->filter_cutoff = instrument->filter_cutoff - 128; + if (instrument->filter_resonance >= 128) + channel->filter_resonance = instrument->filter_resonance - 128; + } +} + + + +static void get_true_pan(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) +{ + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + + if (channel->sample && !IT_IS_SURROUND_SHIFTED(channel->truepan) && (sigdata->flags & IT_USE_INSTRUMENTS)) { + IT_INSTRUMENT *instrument = &sigdata->instrument[channel->instrument-1]; + int truepan = channel->truepan; + truepan += (channel->note - instrument->pp_centre) * instrument->pp_separation << (IT_ENVELOPE_SHIFT - 3); + channel->truepan = (unsigned short)MID(0, truepan, 64 << IT_ENVELOPE_SHIFT); + } +} + + + +static void post_process_it_volpan(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) +{ + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + + if (entry->mask & IT_ENTRY_VOLPAN) { + if (entry->volpan <= 84) { + /* Volume */ + /* Fine volume slide up */ + /* Fine volume slide down */ + } else if (entry->volpan <= 94) { + /* Volume slide up */ + unsigned char v = entry->volpan - 85; + if (v == 0) + v = channel->lastvolslide; + channel->lastvolslide = v; + /* = effect Dx0 where x == entry->volpan - 85 */ + channel->volslide += v; + } else if (entry->volpan <= 104) { + /* Volume slide down */ + unsigned char v = entry->volpan - 95; + if (v == 0) + v = channel->lastvolslide; + channel->lastvolslide = v; + /* = effect D0x where x == entry->volpan - 95 */ + channel->volslide -= v; + } else if (entry->volpan <= 114) { + /* Portamento down */ + unsigned char v = (entry->volpan - 105) << 2; + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + channel->portamento -= v << 4; + } else if (entry->volpan <= 124) { + /* Portamento up */ + unsigned char v = (entry->volpan - 115) << 2; + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + channel->portamento += v << 4; + } else if (entry->volpan <= 202) { + /* Pan */ + /* Tone Portamento */ + } else if (entry->volpan <= 212) { + /* Vibrato */ + /* This is unaffected by IT_OLD_EFFECTS. However, if v == 0, then any doubling of depth that happened before (with Hxy in the effect column) will be preserved. */ + unsigned char v = entry->volpan - 203; + if (v == 0) + v = channel->lastHdepth; + else { + v <<= 2; + channel->lastHdepth = v; + } + if (channel->playing) { + channel->playing->vibrato_speed = channel->lastHspeed; + channel->playing->vibrato_depth = v; + channel->playing->vibrato_n++; + } + } + } +} + + + +static void it_send_midi(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel, unsigned char midi_byte) +{ + if (sigrenderer->callbacks->midi) + if ((*sigrenderer->callbacks->midi)(sigrenderer->callbacks->midi_data, (int)(channel - sigrenderer->channel), midi_byte)) + return; + + switch (channel->midi_state) { + case 4: /* Ready to receive resonance parameter */ + if (midi_byte < 0x80) channel->filter_resonance = midi_byte; + channel->midi_state = 0; + break; + case 3: /* Ready to receive cutoff parameter */ + if (midi_byte < 0x80) channel->filter_cutoff = midi_byte; + channel->midi_state = 0; + break; + case 2: /* Ready for byte specifying which parameter will follow */ + if (midi_byte == 0) /* Cutoff */ + channel->midi_state = 3; + else if (midi_byte == 1) /* Resonance */ + channel->midi_state = 4; + else + channel->midi_state = 0; + break; + default: /* Counting initial F0 bytes */ + switch (midi_byte) { + case 0xF0: + channel->midi_state++; + break; + case 0xFA: + case 0xFC: + case 0xFF: + /* Reset filter parameters for all channels */ + { + int i; + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + sigrenderer->channel[i].filter_cutoff = 127; + sigrenderer->channel[i].filter_resonance = 0; + //// should we be resetting channel[i].playing->filter_* here? + } + } + /* Fall through */ + default: + channel->midi_state = 0; + break; + } + } +} + + + +static void xm_envelope_calculate_value(IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) +{ + if (pe->next_node <= 0) + pe->value = envelope->node_y[0] << IT_ENVELOPE_SHIFT; + else if (pe->next_node >= envelope->n_nodes) + pe->value = envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT; + else { + int ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT; + int ts = envelope->node_t[pe->next_node-1]; + int te = envelope->node_t[pe->next_node]; + + if (ts == te) + pe->value = ys; + else { + int ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT; + int t = pe->tick; + + pe->value = ys + (ye - ys) * (t - ts) / (te - ts); + } + } +} + + + +extern const char xm_convert_vibrato[]; + +const char mod_convert_vibrato[] = { + IT_VIBRATO_SINE, + IT_VIBRATO_RAMP_UP, /* this will be inverted by IT_OLD_EFFECTS */ + IT_VIBRATO_XM_SQUARE, + IT_VIBRATO_XM_SQUARE +}; + +/* Returns 1 if a callback caused termination of playback. */ +static int process_effects(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + IT_PLAYING *playing; + int i; + + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + + if (entry->mask & IT_ENTRY_EFFECT) { + switch (entry->effect) { +/* +Notes about effects (as compared to other module formats) + +C This is now in *HEX*. (Used to be in decimal in ST3) +E/F/G/H/U You need to check whether the song uses Amiga/Linear slides. +H/U Vibrato in Impulse Tracker is two times finer than in + any other tracker and is updated EVERY tick. + If "Old Effects" is *ON*, then the vibrato is played in the + normal manner (every non-row tick and normal depth) +E/F/G These commands ALL share the same memory. +Oxx Offsets to samples are to the 'xx00th' SAMPLE. (ie. for + 16 bit samples, the offset is xx00h*2) + Oxx past the sample end will be ignored, unless "Old Effects" + is ON, in which case the Oxx will play from the end of the + sample. +Yxy This uses a table 4 times larger (hence 4 times slower) than + vibrato or tremelo. If the waveform is set to random, then + the 'speed' part of the command is interpreted as a delay. +*/ + case IT_SET_SPEED: + if (entry->effectvalue) + { + /*if (entry->effectvalue == 255) + if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data)) + return 1;*/ + if (sigdata->flags & IT_WAS_AN_STM) { + int n = entry->effectvalue; + if (n >= 32) { + sigrenderer->tick = sigrenderer->speed = n; + } + } else { + sigrenderer->tick = sigrenderer->speed = entry->effectvalue; + } + } + else if ((sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) { +#ifdef BIT_ARRAY_BULLSHIT + bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row); +#endif + sigrenderer->speed = 0; + if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data)) + return 1; + } + break; + + case IT_BREAK_TO_ROW: + if (ignore_cxx) break; + sigrenderer->breakrow = entry->effectvalue; + /* XXX jump and break on the same row */ + if ( ( ( sigrenderer->processrow | 0xC00 ) == 0xFFFE ) && + ! ( sigrenderer->processrow & 0x400 ) ) { + sigrenderer->processrow = 0xFFFE & ~0xC00; + } else { + sigrenderer->processorder = sigrenderer->order; + sigrenderer->processrow = 0xFFFE & ~0x800; + } + break; + + case IT_VOLSLIDE_VIBRATO: + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->vibrato_speed = channel->lastHspeed; + playing->vibrato_depth = channel->lastHdepth; + playing->vibrato_n++; + } + } + /* Fall through and process volume slide. */ + case IT_VOLUME_SLIDE: + case IT_VOLSLIDE_TONEPORTA: + /* The tone portamento component is handled elsewhere. */ + { + unsigned char v = entry->effectvalue; + if (!(sigdata->flags & IT_WAS_A_MOD)) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + } + if (!(sigdata->flags & IT_WAS_AN_XM)) { + int clip = (sigdata->flags & IT_WAS_AN_S3M) ? 63 : 64; + if ((v & 0x0F) == 0x0F) { + if (!(v & 0xF0)) { + channel->volslide = -15; + channel->volume -= 15; + if (channel->volume > clip) channel->volume = 0; + } else { + channel->volume += v >> 4; + if (channel->volume > clip) channel->volume = clip; + } + } else if ((v & 0xF0) == 0xF0) { + if (!(v & 0x0F)) { + channel->volslide = 15; + channel->volume += 15; + if (channel->volume > clip) channel->volume = clip; + } else { + channel->volume -= v & 15; + if (channel->volume > clip) channel->volume = 0; + } + } else if (!(v & 0x0F)) { + channel->volslide = v >> 4; + } else { + channel->volslide = -(v & 15); + } + } else { + if ((v & 0x0F) == 0) { /* Dx0 */ + channel->volslide = v >> 4; + } else if ((v & 0xF0) == 0) { /* D0x */ + channel->volslide = -v; + } else if ((v & 0x0F) == 0x0F) { /* DxF */ + channel->volume += v >> 4; + if (channel->volume > 64) channel->volume = 64; + } else if ((v & 0xF0) == 0xF0) { /* DFx */ + channel->volume -= v & 15; + if (channel->volume > 64) channel->volume = 0; + } + } + } + break; + case IT_XM_FINE_VOLSLIDE_DOWN: + { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->xm_lastEB; + channel->xm_lastEB = v; + channel->volume -= v; + if (channel->volume > 64) channel->volume = 0; + } + break; + case IT_XM_FINE_VOLSLIDE_UP: + { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->xm_lastEA; + channel->xm_lastEA = v; + channel->volume += v; + if (channel->volume > 64) channel->volume = 64; + } + break; + case IT_PORTAMENTO_DOWN: + { + unsigned char v = entry->effectvalue; + if (sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_669)) { + if (!(sigdata->flags & IT_WAS_A_MOD)) { + if (v == 0xF0) + v |= channel->xm_lastE2; + else if (v >= 0xF0) + channel->xm_lastE2 = v & 15; + else if (v == 0xE0) + v |= channel->xm_lastX2; + else + channel->xm_lastX2 = v & 15; + } + } else if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + } else { + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + } + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + if ((v & 0xF0) == 0xF0) + playing->slide -= (v & 15) << 4; + else if ((v & 0xF0) == 0xE0) + playing->slide -= (v & 15) << 2; + else if (i < 0 && sigdata->flags & IT_WAS_A_669) + channel->portamento -= v << 3; + else if (i < 0) + channel->portamento -= v << 4; + } + } + } + break; + case IT_PORTAMENTO_UP: + { + unsigned char v = entry->effectvalue; + if (sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_669)) { + if (!(sigdata->flags & IT_WAS_A_MOD)) { + if (v == 0xF0) + v |= channel->xm_lastE1; + else if (v >= 0xF0) + channel->xm_lastE1 = v & 15; + else if (v == 0xE0) + v |= channel->xm_lastX1; + else + channel->xm_lastX1 = v & 15; + } + } else if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + } else { + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + } + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + if ((v & 0xF0) == 0xF0) + playing->slide += (v & 15) << 4; + else if ((v & 0xF0) == 0xE0) + playing->slide += (v & 15) << 2; + else if (i < 0 && sigdata->flags & IT_WAS_A_669) + channel->portamento += v << 3; + else if (i < 0) + channel->portamento += v << 4; + } + } + } + break; + case IT_XM_PORTAMENTO_DOWN: + { + unsigned char v = entry->effectvalue; + if (!(sigdata->flags & IT_WAS_A_MOD)) { + if (v == 0) + v = channel->lastJ; + channel->lastJ = v; + } + if (channel->playing) + channel->portamento -= v << 4; + } + break; + case IT_XM_PORTAMENTO_UP: + { + unsigned char v = entry->effectvalue; + if (!(sigdata->flags & IT_WAS_A_MOD)) { + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + } + if (channel->playing) + channel->portamento += v << 4; + } + break; + case IT_XM_KEY_OFF: + channel->key_off_count = entry->effectvalue; + if (!channel->key_off_count) xm_note_off(sigdata, channel); + break; + case IT_VIBRATO: + { + if (entry->effectvalue || !(sigdata->flags & IT_WAS_A_669)) { + unsigned char speed = entry->effectvalue >> 4; + unsigned char depth = entry->effectvalue & 15; + if (speed == 0) + speed = channel->lastHspeed; + channel->lastHspeed = speed; + if (depth == 0) + depth = channel->lastHdepth; + else { + if (sigdata->flags & IT_OLD_EFFECTS && !(sigdata->flags & IT_WAS_A_MOD)) + depth <<= 3; + else + depth <<= 2; + channel->lastHdepth = depth; + } + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->vibrato_speed = speed; + playing->vibrato_depth = depth; + playing->vibrato_n++; + } + } + } + } + break; + case IT_TREMOR: + { + unsigned char v = entry->effectvalue; + if (v == 0) { + if (sigdata->flags & IT_WAS_AN_S3M) + v = channel->lastDKL; + else + v = channel->lastI; + } + else if (!(sigdata->flags & IT_OLD_EFFECTS)) { + if (v & 0xF0) v -= 0x10; + if (v & 0x0F) v -= 0x01; + } + if (sigdata->flags & IT_WAS_AN_S3M) + channel->lastDKL = v; + else + channel->lastI = v; + channel->tremor_time |= 128; + } + update_tremor(channel); + break; + case IT_ARPEGGIO: + { + unsigned char v = entry->effectvalue; + /* XM files have no memory for arpeggio (000 = no effect) + * and we use lastJ for portamento down instead. + */ + if (!(sigdata->flags & IT_WAS_AN_XM)) { + if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + } else { + if (v == 0) + v = channel->lastJ; + channel->lastJ = v; + } + } + channel->arpeggio_offsets[0] = 0; + channel->arpeggio_offsets[1] = (v & 0xF0) >> 4; + channel->arpeggio_offsets[2] = (v & 0x0F); + channel->arpeggio_table = (const unsigned char *)(((sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD))==IT_WAS_AN_XM) ? &arpeggio_xm : &arpeggio_mod); + } + break; + case IT_SET_CHANNEL_VOLUME: + if (sigdata->flags & IT_WAS_AN_XM) + channel->volume = MIN(entry->effectvalue, 64); + else if (entry->effectvalue <= 64) + channel->channelvolume = entry->effectvalue; +#ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM + else + channel->channelvolume = 64; +#endif + if (channel->playing) + channel->playing->channel_volume = channel->channelvolume; + break; + case IT_CHANNEL_VOLUME_SLIDE: + { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->lastN; + channel->lastN = v; + if ((v & 0x0F) == 0) { /* Nx0 */ + channel->channelvolslide = v >> 4; + } else if ((v & 0xF0) == 0) { /* N0x */ + channel->channelvolslide = -v; + } else { + if ((v & 0x0F) == 0x0F) { /* NxF */ + channel->channelvolume += v >> 4; + if (channel->channelvolume > 64) channel->channelvolume = 64; + } else if ((v & 0xF0) == 0xF0) { /* NFx */ + channel->channelvolume -= v & 15; + if (channel->channelvolume > 64) channel->channelvolume = 0; + } else + break; + if (channel->playing) + channel->playing->channel_volume = channel->channelvolume; + } + } + break; + case IT_SET_SAMPLE_OFFSET: + { + unsigned char v = entry->effectvalue; + /*if (sigdata->flags & IT_WAS_A_MOD) { + if (v == 0) break; + } else*/ { + if (v == 0) + v = channel->lastO; + channel->lastO = v; + } + /* Note: we set the offset even if tone portamento is + * specified. Impulse Tracker does the same. + */ + if (entry->mask & IT_ENTRY_NOTE) { + if (channel->playing) { + int offset = ((int)channel->high_offset << 16) | ((int)v << 8); + IT_PLAYING *playing = channel->playing; + IT_SAMPLE *sample = playing->sample; + int end; + if ((sample->flags & IT_SAMPLE_SUS_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) + end = sample->sus_loop_end; + else if (sample->flags & IT_SAMPLE_LOOP) + end = sample->loop_end; + else { + end = sample->length; + if ( sigdata->flags & IT_WAS_PROCESSED && end > 64 ) // XXX bah damn LPC and edge case modules + end -= 64; + } + if ((sigdata->flags & IT_WAS_A_PTM) && (sample->flags & IT_SAMPLE_16BIT)) + offset >>= 1; + if (offset < end) { + it_playing_reset_resamplers(playing, offset); + playing->declick_stage = 0; + } else if (sigdata->flags & IT_OLD_EFFECTS) { + it_playing_reset_resamplers(playing, end); + playing->declick_stage = 0; + } + } + } + } + break; + case IT_PANNING_SLIDE: + /** JULIEN: guess what? the docs are wrong! (how unusual ;) + * Pxy seems to memorize its previous value... and there + * might be other mistakes like that... (sigh!) + */ + /** ENTHEH: umm... but... the docs say that Pxy memorises its + * value... don't they? :o + */ + { + unsigned char v = entry->effectvalue; + int p = channel->truepan; + if (sigdata->flags & IT_WAS_AN_XM) + { + if (IT_IS_SURROUND(channel->pan)) + { + channel->pan = 32; + p = 32 + 128 * 64; + } + p >>= 6; + } + else { + if (IT_IS_SURROUND(channel->pan)) p = 32 << 8; + p = (p + 128) >> 8; + channel->pan = p; + } + if (v == 0) + v = channel->lastP; + channel->lastP = v; + if ((v & 0x0F) == 0) { /* Px0 */ + channel->panslide = -(v >> 4); + } else if ((v & 0xF0) == 0) { /* P0x */ + channel->panslide = v; + } else if ((v & 0x0F) == 0x0F) { /* PxF */ + p -= v >> 4; + } else if ((v & 0xF0) == 0xF0) { /* PFx */ + p += v & 15; + } + if (sigdata->flags & IT_WAS_AN_XM) + channel->truepan = 32 + MID(0, p, 255) * 64; + else { + if (p < 0) p = 0; + else if (p > 64) p = 64; + channel->pan = p; + channel->truepan = p << 8; + } + } + break; + case IT_RETRIGGER_NOTE: + { + unsigned char v = entry->effectvalue; + if (sigdata->flags & IT_WAS_AN_XM) { + if ((v & 0x0F) == 0) v |= channel->lastQ & 0x0F; + if ((v & 0xF0) == 0) v |= channel->lastQ & 0xF0; + channel->lastQ = v; + } else if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + } else { + if (v == 0) + v = channel->lastQ; + channel->lastQ = v; + } + if ((v & 0x0F) == 0) v |= 0x01; + channel->retrig = v; + if (entry->mask & IT_ENTRY_NOTE) { + channel->retrig_tick = v & 0x0F; + /* Emulate a bug */ + if (sigdata->flags & IT_WAS_AN_XM) + update_retrig(sigrenderer, channel); + } else + update_retrig(sigrenderer, channel); + } + break; + case IT_XM_RETRIGGER_NOTE: + channel->retrig_tick = channel->xm_retrig = entry->effectvalue; + if (entry->effectvalue == 0) + if (channel->playing) { + it_playing_reset_resamplers(channel->playing, 0); + channel->playing->declick_stage = 0; + } + break; + case IT_TREMOLO: + { + unsigned char speed, depth; + if (sigdata->flags & IT_WAS_AN_S3M) { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + speed = v >> 4; + depth = v & 15; + } else { + speed = entry->effectvalue >> 4; + depth = entry->effectvalue & 15; + if (speed == 0) + speed = channel->lastRspeed; + channel->lastRspeed = speed; + if (depth == 0) + depth = channel->lastRdepth; + channel->lastRdepth = depth; + } + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->tremolo_speed = speed; + playing->tremolo_depth = depth; + } + } + } + break; + case IT_S: + { + /* channel->lastS was set in update_pattern_variables(). */ + unsigned char effectvalue = channel->lastS; + switch (effectvalue >> 4) { + //case IT_S_SET_FILTER: + /* Waveforms for commands S3x, S4x and S5x: + * 0: Sine wave + * 1: Ramp down + * 2: Square wave + * 3: Random wave + */ + case IT_S_SET_GLISSANDO_CONTROL: + channel->glissando = effectvalue & 15; + break; + + case IT_S_FINETUNE: + if (channel->playing) { + channel->playing->finetune = ((int)(effectvalue & 15) - 8) << 5; + } + break; + + case IT_S_SET_VIBRATO_WAVEFORM: + { + int waveform = effectvalue & 3; + if (sigdata->flags & IT_WAS_A_MOD) waveform = mod_convert_vibrato[waveform]; + else if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform]; + channel->vibrato_waveform = waveform; + if (channel->playing) { + channel->playing->vibrato_waveform = waveform; + if (!(effectvalue & 4)) + channel->playing->vibrato_time = 0; + } + } + break; + case IT_S_SET_TREMOLO_WAVEFORM: + { + int waveform = effectvalue & 3; + if (sigdata->flags & IT_WAS_A_MOD) waveform = mod_convert_vibrato[waveform]; + else if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform]; + channel->tremolo_waveform = waveform; + if (channel->playing) { + channel->playing->tremolo_waveform = waveform; + if (!(effectvalue & 4)) + channel->playing->tremolo_time = 0; + } + } + break; + case IT_S_SET_PANBRELLO_WAVEFORM: + channel->panbrello_waveform = effectvalue & 3; + if (channel->playing) { + channel->playing->panbrello_waveform = effectvalue & 3; + if (!(effectvalue & 4)) + channel->playing->panbrello_time = 0; + } + break; + + case IT_S_FINE_PATTERN_DELAY: + sigrenderer->tick += effectvalue & 15; + break; +#if 1 + case IT_S7: + { + if (sigrenderer->sigdata->flags & IT_USE_INSTRUMENTS) + { + int i; + switch (effectvalue & 15) + { + case 0: /* cut background notes */ + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + { + IT_PLAYING * playing = sigrenderer->playing[i]; + if (playing && channel == playing->channel) + { + playing->declick_stage = 3; + if (channel->playing == playing) channel->playing = NULL; + } + } + break; + case 1: /* release background notes */ + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + { + IT_PLAYING * playing = sigrenderer->playing[i]; + if (playing && channel == playing->channel && !(playing->flags & IT_PLAYING_SUSTAINOFF)) + { + it_note_off(playing); + } + } + break; + case 2: /* fade background notes */ + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + { + IT_PLAYING * playing = sigrenderer->playing[i]; + if (playing && channel == playing->channel) + { + //playing->flags &= IT_PLAYING_SUSTAINOFF; + playing->flags |= IT_PLAYING_FADING; + } + } + break; + case 3: + channel->new_note_action = NNA_NOTE_CUT; + break; + case 4: + channel->new_note_action = NNA_NOTE_CONTINUE; + break; + case 5: + channel->new_note_action = NNA_NOTE_OFF; + break; + case 6: + channel->new_note_action = NNA_NOTE_FADE; + break; + + case 7: + if (channel->playing) + channel->playing->enabled_envelopes &= ~IT_ENV_VOLUME; + break; + case 8: + if (channel->playing) + channel->playing->enabled_envelopes |= IT_ENV_VOLUME; + break; + + case 9: + if (channel->playing) + channel->playing->enabled_envelopes &= ~IT_ENV_PANNING; + break; + case 10: + if (channel->playing) + channel->playing->enabled_envelopes |= IT_ENV_PANNING; + break; + + case 11: + if (channel->playing) + channel->playing->enabled_envelopes &= ~IT_ENV_PITCH; + break; + case 12: + if (channel->playing) + channel->playing->enabled_envelopes |= IT_ENV_PITCH; + break; + } + } + } + break; +#endif + case IT_S_SET_PAN: + //ASSERT(!(sigdata->flags & IT_WAS_AN_XM)); + channel->pan = + ((effectvalue & 15) << 2) | + ((effectvalue & 15) >> 2); + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + + if (channel->playing) + channel->playing->panbrello_depth = 0; + break; + case IT_S_SET_SURROUND_SOUND: + if ((effectvalue & 15) == 15) { + if (channel->playing && channel->playing->sample && + !(channel->playing->sample->flags & (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP))) { + channel->playing->flags |= IT_PLAYING_REVERSE; + it_playing_reset_resamplers( channel->playing, channel->playing->sample->length - 1 ); + } + } else if ((effectvalue & 15) == 1) { + channel->pan = IT_SURROUND; + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + } + if (channel->playing) + channel->playing->panbrello_depth = 0; + break; + case IT_S_SET_HIGH_OFFSET: + channel->high_offset = effectvalue & 15; + break; + //case IT_S_PATTERN_LOOP: + case IT_S_DELAYED_NOTE_CUT: + channel->note_cut_count = effectvalue & 15; + if (!channel->note_cut_count) { + if (sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_PTM)) + channel->volume = 0; + else + channel->note_cut_count = 1; + } + break; + case IT_S_SET_MIDI_MACRO: + if ((sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_MOD)) == (IT_WAS_AN_XM | IT_WAS_A_MOD)) { + channel->inv_loop_speed = effectvalue & 15; + update_invert_loop(channel, channel->playing ? channel->playing->sample : NULL); + } else channel->SFmacro = effectvalue & 15; + break; + } + } + break; + case IT_SET_SONG_TEMPO: + { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->lastW; + channel->lastW = v; + if (v < 0x10) + sigrenderer->temposlide = -v; + else if (v < 0x20) + sigrenderer->temposlide = v & 15; + else + sigrenderer->tempo = v; + } + break; + case IT_FINE_VIBRATO: + { + unsigned char speed = entry->effectvalue >> 4; + unsigned char depth = entry->effectvalue & 15; + if (speed == 0) + speed = channel->lastHspeed; + channel->lastHspeed = speed; + if (depth == 0) + depth = channel->lastHdepth; + else { + if (sigdata->flags & IT_OLD_EFFECTS) + depth <<= 1; + channel->lastHdepth = depth; + } + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->vibrato_speed = speed; + playing->vibrato_depth = depth; + playing->vibrato_n++; + } + } + } + break; + case IT_SET_GLOBAL_VOLUME: + if ((sigdata->flags & IT_WAS_AN_S3M) && (entry->effectvalue > 64)) + break; + if (entry->effectvalue <= 128) + sigrenderer->globalvolume = entry->effectvalue; +#ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM + else + sigrenderer->globalvolume = 128; +#endif + break; + case IT_GLOBAL_VOLUME_SLIDE: + { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->lastW; + channel->lastW = v; + if ((v & 0x0F) == 0) { /* Wx0 */ + sigrenderer->globalvolslide = + (sigdata->flags & IT_WAS_AN_XM) ? (v >> 4)*2 : (v >> 4); + } else if ((v & 0xF0) == 0) { /* W0x */ + sigrenderer->globalvolslide = + (sigdata->flags & IT_WAS_AN_XM) ? (-v)*2 : (-v); + } else if ((v & 0x0F) == 0x0F) { /* WxF */ + sigrenderer->globalvolume += v >> 4; + if (sigrenderer->globalvolume > 128) sigrenderer->globalvolume = 128; + } else if ((v & 0xF0) == 0xF0) { /* WFx */ + sigrenderer->globalvolume -= v & 15; + if (sigrenderer->globalvolume > 128) sigrenderer->globalvolume = 0; + } + } + break; + case IT_SET_PANNING: + if (sigdata->flags & IT_WAS_AN_XM) { + channel->truepan = 32 + entry->effectvalue*64; + } else { + if (sigdata->flags & IT_WAS_AN_S3M) + channel->pan = (entry->effectvalue + 1) >> 1; + else + channel->pan = (entry->effectvalue + 2) >> 2; + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + } + if (channel->playing) + channel->playing->panbrello_depth = 0; + break; + case IT_PANBRELLO: + { + unsigned char speed = entry->effectvalue >> 4; + unsigned char depth = entry->effectvalue & 15; + if (speed == 0) + speed = channel->lastYspeed; + channel->lastYspeed = speed; + if (depth == 0) + depth = channel->lastYdepth; + channel->lastYdepth = depth; + if (channel->playing) { + channel->playing->panbrello_speed = speed; + channel->playing->panbrello_depth = depth; + } + } + break; + case IT_MIDI_MACRO: + { + const IT_MIDI *midi = sigdata->midi ? sigdata->midi : &default_midi; + if (entry->effectvalue >= 0x80) { + int n = midi->Zmacrolen[entry->effectvalue-0x80]; + int i; + for (i = 0; i < n; i++) + it_send_midi(sigrenderer, channel, midi->Zmacro[entry->effectvalue-0x80][i]); + } else { + int n = midi->SFmacrolen[channel->SFmacro]; + int i, j; + for (i = 0, j = 1; i < n; i++, j <<= 1) + it_send_midi(sigrenderer, channel, + (unsigned char)(midi->SFmacroz[channel->SFmacro] & j ? + entry->effectvalue : midi->SFmacro[channel->SFmacro][i])); + } + } + break; + case IT_XM_SET_ENVELOPE_POSITION: + if (channel->playing && channel->playing->env_instrument) { + IT_ENVELOPE *envelope = &channel->playing->env_instrument->volume_envelope; + if (envelope->flags & IT_ENVELOPE_ON) { + IT_PLAYING_ENVELOPE *pe = &channel->playing->volume_envelope; + pe->tick = entry->effectvalue; + if (pe->tick >= envelope->node_t[envelope->n_nodes-1]) + pe->tick = envelope->node_t[envelope->n_nodes-1]; + pe->next_node = 0; + while (pe->tick > envelope->node_t[pe->next_node]) pe->next_node++; + xm_envelope_calculate_value(envelope, pe); + } + } + break; + + /* uggly plain portamento for now */ + case IT_PTM_NOTE_SLIDE_DOWN: + case IT_PTM_NOTE_SLIDE_DOWN_RETRIG: + { + channel->toneslide_retrig = (entry->effect == IT_PTM_NOTE_SLIDE_DOWN_RETRIG); + + if (channel->ptm_last_toneslide) { + channel->toneslide_tick = channel->last_toneslide_tick; + + if (--channel->toneslide_tick == 0) { + channel->truenote += channel->toneslide; + if (channel->truenote >= 120) { + if (channel->toneslide < 0) channel->truenote = 0; + else channel->truenote = 119; + } + channel->note += channel->toneslide; + if (channel->note >= 120) { + if (channel->toneslide < 0) channel->note = 0; + else channel->note = 119; + } + + if (channel->playing) { + if (channel->sample) channel->playing->note = channel->truenote; + else channel->playing->note = channel->note; + it_playing_reset_resamplers(channel->playing, 0); + channel->playing->declick_stage = 0; + } + } + } + + channel->ptm_last_toneslide = 0; + + channel->toneslide = -(entry->effectvalue & 15); + channel->ptm_toneslide = (entry->effectvalue & 0xF0) >> 4; + channel->toneslide_tick += channel->ptm_toneslide; + } + break; + case IT_PTM_NOTE_SLIDE_UP: + case IT_PTM_NOTE_SLIDE_UP_RETRIG: + { + channel->toneslide_retrig = (entry->effect == IT_PTM_NOTE_SLIDE_UP_RETRIG); + + if (channel->ptm_last_toneslide) { + channel->toneslide_tick = channel->last_toneslide_tick; + + if (--channel->toneslide_tick == 0) { + channel->truenote += channel->toneslide; + if (channel->truenote >= 120) { + if (channel->toneslide < 0) channel->truenote = 0; + else channel->truenote = 119; + } + channel->note += channel->toneslide; + if (channel->note >= 120) { + if (channel->toneslide < 0) channel->note = 0; + else channel->note = 119; + } + + if (channel->playing) { + if (channel->sample) channel->playing->note = channel->truenote; + else channel->playing->note = channel->note; + it_playing_reset_resamplers(channel->playing, 0); + channel->playing->declick_stage = 0; + } + } + } + + channel->ptm_last_toneslide = 0; + + channel->toneslide = -(entry->effectvalue & 15); + channel->ptm_toneslide = (entry->effectvalue & 0xF0) >> 4; + channel->toneslide_tick += channel->ptm_toneslide; + } + break; + + case IT_OKT_NOTE_SLIDE_DOWN: + case IT_OKT_NOTE_SLIDE_DOWN_ROW: + channel->toneslide = -entry->effectvalue; + channel->okt_toneslide = (entry->effect == IT_OKT_NOTE_SLIDE_DOWN) ? 255 : 1; + break; + + case IT_OKT_NOTE_SLIDE_UP: + case IT_OKT_NOTE_SLIDE_UP_ROW: + channel->toneslide = entry->effectvalue; + channel->okt_toneslide = (entry->effect == IT_OKT_NOTE_SLIDE_UP) ? 255 : 1; + break; + + case IT_OKT_ARPEGGIO_3: + case IT_OKT_ARPEGGIO_4: + case IT_OKT_ARPEGGIO_5: + { + channel->arpeggio_offsets[0] = 0; + channel->arpeggio_offsets[1] = -(entry->effectvalue >> 4); + channel->arpeggio_offsets[2] = entry->effectvalue & 0x0F; + + switch (entry->effect) + { + case IT_OKT_ARPEGGIO_3: + channel->arpeggio_table = (const unsigned char *)&arpeggio_okt_3; + break; + + case IT_OKT_ARPEGGIO_4: + channel->arpeggio_table = (const unsigned char *)&arpeggio_okt_4; + break; + + case IT_OKT_ARPEGGIO_5: + channel->arpeggio_table = (const unsigned char *)&arpeggio_okt_5; + break; + } + } + break; + + case IT_OKT_VOLUME_SLIDE_DOWN: + if ( entry->effectvalue <= 16 ) channel->volslide = -entry->effectvalue; + else + { + channel->volume -= entry->effectvalue - 16; + if (channel->volume > 64) channel->volume = 0; + } + break; + + case IT_OKT_VOLUME_SLIDE_UP: + if ( entry->effectvalue <= 16 ) channel->volslide = entry->effectvalue; + else + { + channel->volume += entry->effectvalue - 16; + if (channel->volume > 64) channel->volume = 64; + } + break; + } + } + + if (!(sigdata->flags & IT_WAS_AN_XM)) + post_process_it_volpan(sigrenderer, entry); + + return 0; +} + + + +static int process_it_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + + // When tone portamento and instrument are specified: + // If Gxx is off: + // - same sample, do nothing but portamento + // - diff sample, retrigger all but keep current note+slide + do porta + // - if instrument is invalid, nothing; if sample is invalid, cut + // If Gxx is on: + // - same sample or new sample invalid, retrigger envelopes and initialise note value for portamento to 'seek' to + // - diff sample/inst, start using new envelopes + // When tone portamento is specified alone, sample won't change. + // TODO: consider what happens with instrument alone after all this... + + if (entry->mask & (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) { + if (entry->mask & IT_ENTRY_INSTRUMENT) + channel->instrument = entry->instrument; + instrument_to_sample(sigdata, channel); + if (channel->note <= 120) { + if ((sigdata->flags & IT_USE_INSTRUMENTS) && channel->sample == 0) + it_retrigger_note(sigrenderer, channel); /* Stop the note */ /*return 1;*/ + if (entry->mask & IT_ENTRY_INSTRUMENT) + get_default_volpan(sigdata, channel); + } else + it_retrigger_note(sigrenderer, channel); /* Stop the note */ + } + + /** WARNING: This is not ideal, since channel->playing might not get allocated owing to lack of memory... */ + if (((entry->mask & IT_ENTRY_VOLPAN) && entry->volpan >= 193 && entry->volpan <= 202) || + ((entry->mask & IT_ENTRY_EFFECT) && (entry->effect == IT_TONE_PORTAMENTO || entry->effect == IT_VOLSLIDE_TONEPORTA))) + { + if (channel->playing && (entry->mask & IT_ENTRY_INSTRUMENT)) { + if (sigdata->flags & IT_COMPATIBLE_GXX) + it_compatible_gxx_retrigger(sigdata, channel); + else if ((!(sigdata->flags & IT_USE_INSTRUMENTS) || + (channel->instrument >= 1 && channel->instrument <= sigdata->n_instruments)) && + channel->sample != channel->playing->sampnum) + { + unsigned char note = channel->playing->note; + int slide = channel->playing->slide; + it_retrigger_note(sigrenderer, channel); + if (channel->playing) { + channel->playing->note = note; + channel->playing->slide = slide; + // Should we be preserving sample_vibrato_time? depth? + } + } + } + + channel->toneporta = 0; + + if ((entry->mask & IT_ENTRY_VOLPAN) && entry->volpan >= 193 && entry->volpan <= 202) { + /* Tone Portamento in the volume column */ + static const unsigned char slidetable[] = {0, 1, 4, 8, 16, 32, 64, 96, 128, 255}; + unsigned char v = slidetable[entry->volpan - 193]; + if (sigdata->flags & IT_COMPATIBLE_GXX) { + if (v == 0) + v = channel->lastG; + channel->lastG = v; + } else { + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + } + channel->toneporta += v << 4; + } + + if ((entry->mask & IT_ENTRY_EFFECT) && (entry->effect == IT_TONE_PORTAMENTO || entry->effect == IT_VOLSLIDE_TONEPORTA)) { + /* Tone Portamento in the effect column */ + unsigned char v; + if (entry->effect == IT_TONE_PORTAMENTO) + v = entry->effectvalue; + else + v = 0; + if (sigdata->flags & IT_COMPATIBLE_GXX) { + if (v == 0) + v = channel->lastG; + channel->lastG = v; + } else { + if (v == 0 && !(sigdata->flags & IT_WAS_A_669)) + v = channel->lastEF; + channel->lastEF = v; + } + channel->toneporta += v << 4; + } + + if ((entry->mask & IT_ENTRY_NOTE) || ((sigdata->flags & IT_COMPATIBLE_GXX) && (entry->mask & IT_ENTRY_INSTRUMENT))) { + if (channel->note <= 120) { + if (channel->sample) + channel->destnote = channel->truenote; + else + channel->destnote = channel->note; + } + } + + if (channel->playing) goto skip_start_note; + } + + if ((entry->mask & IT_ENTRY_NOTE) || + ((entry->mask & IT_ENTRY_INSTRUMENT) && (!channel->playing || entry->instrument != channel->playing->instnum))) + { + if (channel->note <= 120) { + get_true_pan(sigdata, channel); + if ((entry->mask & IT_ENTRY_NOTE) || !(sigdata->flags & (IT_WAS_AN_S3M|IT_WAS_A_PTM))) + it_retrigger_note(sigrenderer, channel); + } + } + + skip_start_note: + + if (entry->mask & IT_ENTRY_VOLPAN) { + if (entry->volpan <= 64) { + /* Volume */ + channel->volume = entry->volpan; + } else if (entry->volpan <= 74) { + /* Fine volume slide up */ + unsigned char v = entry->volpan - 65; + if (v == 0) + v = channel->lastvolslide; + channel->lastvolslide = v; + /* = effect DxF where x == entry->volpan - 65 */ + channel->volume += v; + if (channel->volume > 64) channel->volume = 64; + } else if (entry->volpan <= 84) { + /* Fine volume slide down */ + unsigned char v = entry->volpan - 75; + if (v == 0) + v = channel->lastvolslide; + channel->lastvolslide = v; + /* = effect DFx where x == entry->volpan - 75 */ + channel->volume -= v; + if (channel->volume > 64) channel->volume = 0; + } else if (entry->volpan < 128) { + /* Volume slide up */ + /* Volume slide down */ + /* Portamento down */ + /* Portamento up */ + } else if (entry->volpan <= 192) { + /* Pan */ + channel->pan = entry->volpan - 128; + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + } + /* else */ + /* Tone Portamento */ + /* Vibrato */ + } + return 0; +} + + + +static void retrigger_xm_envelopes(IT_PLAYING *playing) +{ + playing->volume_envelope.next_node = 0; + playing->volume_envelope.tick = -1; + playing->pan_envelope.next_node = 0; + playing->pan_envelope.tick = -1; + playing->fadeoutcount = 1024; +} + + + +static void process_xm_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + IT_PLAYING * playing = NULL; + + if (entry->mask & IT_ENTRY_INSTRUMENT) { + int oldsample = channel->sample; + channel->inv_loop_offset = 0; + channel->instrument = entry->instrument; + instrument_to_sample(sigdata, channel); + if (channel->playing && + !((entry->mask & IT_ENTRY_NOTE) && entry->note >= 120) && + !((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0)) { + playing = dup_playing(channel->playing, channel, channel); + if (!playing) return; + if (!(sigdata->flags & IT_WAS_A_MOD)) { + /* Retrigger vol/pan envelopes if enabled, and cancel fadeout. + * Also reset vol/pan to that of _original_ instrument. + */ + channel->playing->flags &= ~(IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING); + it_playing_update_resamplers(channel->playing); + + channel->volume = channel->playing->sample->default_volume; + channel->truepan = 32 + channel->playing->sample->default_pan*64; + + retrigger_xm_envelopes(channel->playing); + } else { + /* Switch if sample changed */ + if (oldsample != channel->sample) { + int i; + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + channel->playing->declick_stage = 3; + sigrenderer->playing[i] = channel->playing; + channel->playing = NULL; + break; + } + } + + if (!channel->sample) { + if (channel->playing) + { + free_playing(sigrenderer, channel->playing); + channel->playing = NULL; + } + } else { + if (channel->playing) { + free_playing(sigrenderer, channel->playing); + } + channel->playing = playing; + playing = NULL; + channel->playing->declick_stage = 0; + channel->playing->sampnum = channel->sample; + channel->playing->sample = &sigdata->sample[channel->sample-1]; + it_playing_reset_resamplers(channel->playing, 0); + } + } + get_default_volpan(sigdata, channel); + } + } + } + + if (!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0) && + (entry->mask & IT_ENTRY_NOTE)) + { + if (!(entry->mask & IT_ENTRY_INSTRUMENT)) + instrument_to_sample(sigdata, channel); + + if (channel->note >= 120) + xm_note_off(sigdata, channel); + else if (channel->sample == 0) { + /** If we get here, one of the following is the case: + ** 1. The instrument has never been specified on this channel. + ** 2. The specified instrument is invalid. + ** 3. The instrument has no sample mapped to the selected note. + ** What should happen? + ** + ** Experimentation shows that any existing note stops and cannot + ** be brought back. A subsequent instrument change fixes that. + **/ + if (channel->playing) { + int i; + if (playing) { + free_playing(sigrenderer, channel->playing); + channel->playing = playing; + playing = NULL; + } + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + channel->playing->declick_stage = 3; + sigrenderer->playing[i] = channel->playing; + channel->playing = NULL; + break; + } + } + if (channel->playing) { + free_playing(sigrenderer, channel->playing); + channel->playing = NULL; + } + } + if (playing) free_playing(sigrenderer, playing); + return; + } else if (channel->playing && (entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) { + /* Don't retrigger note; portamento in the volume column. */ + } else if (channel->playing && + (entry->mask & IT_ENTRY_EFFECT) && + (entry->effect == IT_TONE_PORTAMENTO || + entry->effect == IT_VOLSLIDE_TONEPORTA)) { + /* Don't retrigger note; portamento in the effects column. */ + } else { + channel->destnote = IT_NOTE_OFF; + + if (!channel->playing) { + channel->playing = new_playing(sigrenderer); + if (!channel->playing) { + if (playing) free_playing(sigrenderer, playing); + return; + } + // Adding the following seems to do the trick for the case where a piece starts with an instrument alone and then some notes alone. + retrigger_xm_envelopes(channel->playing); + } + else if (playing) { + /* volume rampy stuff! move note to NNA */ + int i; + IT_PLAYING * ptemp; + if (playing->sample) ptemp = playing; + else ptemp = channel->playing; + if (!ptemp) { + if (playing) free_playing(sigrenderer, playing); + return; + } + playing = NULL; + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + ptemp->declick_stage = 3; + ptemp->flags |= IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING; + sigrenderer->playing[i] = ptemp; + ptemp = NULL; + break; + } + } + if (ptemp) free_playing(sigrenderer, ptemp); + } + + channel->playing->flags = 0; + channel->playing->resampling_quality = sigrenderer->resampling_quality; + channel->playing->channel = channel; + channel->playing->sample = &sigdata->sample[channel->sample-1]; + if (sigdata->flags & IT_USE_INSTRUMENTS) + channel->playing->instrument = &sigdata->instrument[channel->instrument-1]; + else + channel->playing->instrument = NULL; + channel->playing->env_instrument = channel->playing->instrument; + channel->playing->sampnum = channel->sample; + channel->playing->instnum = channel->instrument; + channel->playing->declick_stage = 0; + channel->playing->channel_volume = channel->channelvolume; + channel->playing->note = channel->truenote; + channel->playing->enabled_envelopes = 0; + channel->playing->volume_offset = 0; + channel->playing->panning_offset = 0; + //channel->playing->output = channel->output; + if (sigdata->flags & IT_USE_INSTRUMENTS) { + IT_PLAYING * playing = channel->playing; + IT_INSTRUMENT * instrument = playing->instrument; + if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_VOLUME; + if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PANNING; + //if (instrument->output) playing->output = instrument->output; + } + channel->playing->filter_cutoff = 127; + channel->playing->filter_resonance = 0; + channel->playing->true_filter_cutoff = 127 << 8; + channel->playing->true_filter_resonance = 0; + channel->playing->vibrato_speed = 0; + channel->playing->vibrato_depth = 0; + channel->playing->vibrato_n = 0; + channel->playing->vibrato_time = 0; + channel->playing->vibrato_waveform = 0; + channel->playing->tremolo_speed = 0; + channel->playing->tremolo_depth = 0; + channel->playing->tremolo_time = 0; + channel->playing->tremolo_waveform = 0; + channel->playing->panbrello_speed = 0; + channel->playing->panbrello_depth = 0; + channel->playing->panbrello_time = 0; + channel->playing->panbrello_waveform = 0; + channel->playing->panbrello_random = 0; + channel->playing->sample_vibrato_time = 0; + channel->playing->sample_vibrato_waveform = channel->playing->sample->vibrato_waveform; + channel->playing->sample_vibrato_depth = 0; + channel->playing->slide = 0; + channel->playing->finetune = channel->playing->sample->finetune; + it_reset_filter_state(&channel->playing->filter_state[0]); // Are these + it_reset_filter_state(&channel->playing->filter_state[1]); // necessary? + it_playing_reset_resamplers(channel->playing, 0); + + /** WARNING - is everything initialised? */ + } + } + + if (!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0) && + !((entry->mask & IT_ENTRY_NOTE) && entry->note >= 120) && + (entry->mask & (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) == (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) + { + if (channel->playing) retrigger_xm_envelopes(channel->playing); + get_default_volpan(sigdata, channel); + } + + if ((entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) { + /* Tone Portamento */ + unsigned char v = (entry->volpan & 15) << 4; + if (v == 0) + v = channel->lastG; + channel->lastG = v; + if (entry->mask & IT_ENTRY_NOTE) + if (channel->sample && channel->note < 120) + channel->destnote = channel->truenote; + channel->toneporta = v << 4; + } else if ((entry->mask & IT_ENTRY_EFFECT) && + (entry->effect == IT_TONE_PORTAMENTO || + entry->effect == IT_VOLSLIDE_TONEPORTA)) { + unsigned char v; + if (entry->effect == IT_TONE_PORTAMENTO) + v = entry->effectvalue; + else + v = 0; + if (v == 0) + v = channel->lastG; + channel->lastG = v; + if (entry->mask & IT_ENTRY_NOTE) + if (channel->sample && channel->note < 120) + channel->destnote = channel->truenote; + channel->toneporta = v << 4; + } + + if (entry->mask & IT_ENTRY_VOLPAN) { + int effect = entry->volpan >> 4; + int value = entry->volpan & 15; + switch (effect) { + case 0x6: /* Volume slide down */ + channel->xm_volslide = -value; + break; + case 0x7: /* Volume slide up */ + channel->xm_volslide = value; + break; + case 0x8: /* Fine volume slide down */ + channel->volume -= value; + if (channel->volume > 64) channel->volume = 0; + break; + case 0x9: /* Fine volume slide up */ + channel->volume += value; + if (channel->volume > 64) channel->volume = 64; + break; + case 0xA: /* Set vibrato speed */ + if (value) + channel->lastHspeed = value; + if (channel->playing) + channel->playing->vibrato_speed = channel->lastHspeed; + break; + case 0xB: /* Vibrato */ + if (value) + channel->lastHdepth = value << 2; /** WARNING: correct ? */ + if (channel->playing) { + channel->playing->vibrato_depth = channel->lastHdepth; + channel->playing->vibrato_speed = channel->lastHspeed; + channel->playing->vibrato_n++; + } + break; + case 0xC: /* Set panning */ + channel->truepan = 32 + value*(17*64); + break; + case 0xD: /* Pan slide left */ + /* -128 is a special case for emulating a 'feature' in FT2. + * As soon as effects are processed, it goes hard left. + */ + channel->panslide = value ? -value : -128; + break; + case 0xE: /* Pan slide Right */ + channel->panslide = value; + break; + case 0xF: /* Tone porta */ + break; + default: /* Volume */ + channel->volume = entry->volpan - 0x10; + break; + } + } + + if (playing) free_playing(sigrenderer, playing); +} + + + +/* This function assumes !IT_IS_END_ROW(entry). */ +static int process_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + + if (sigdata->flags & IT_WAS_AN_XM) + process_xm_note_data(sigrenderer, entry); + else + if (process_it_note_data(sigrenderer, entry)) return 0; + + return process_effects(sigrenderer, entry, ignore_cxx); +} + + + +static int process_entry(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx) +{ + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + + if (entry->mask & IT_ENTRY_NOTE) + channel->note = entry->note; + + if ((entry->mask & (IT_ENTRY_NOTE|IT_ENTRY_EFFECT)) && (sigrenderer->sigdata->flags & IT_WAS_A_669)) { + reset_channel_effects(channel); + // XXX unknown + if (channel->playing) channel->playing->finetune = 0; + } + + if ((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_S) { + /* channel->lastS was set in update_pattern_variables(). */ + unsigned char effectvalue = channel->lastS; + if (effectvalue >> 4 == IT_S_NOTE_DELAY) { + channel->note_delay_count = effectvalue & 15; + if (channel->note_delay_count == 0) + channel->note_delay_count = 1; + channel->note_delay_entry = entry; + return 0; + } + } + + return process_note_data(sigrenderer, entry, ignore_cxx); +} + + + +static void update_tick_counts(DUMB_IT_SIGRENDERER *sigrenderer) +{ + int i; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; + + if (channel->key_off_count) { + channel->key_off_count--; + if (channel->key_off_count == 0) + xm_note_off(sigrenderer->sigdata, channel); + } else if (channel->note_cut_count) { + channel->note_cut_count--; + if (channel->note_cut_count == 0) { + if (sigrenderer->sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_PTM)) + channel->volume = 0; + else if (channel->playing) { + int i; + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + channel->playing->declick_stage = 3; + sigrenderer->playing[i] = channel->playing; + channel->playing = NULL; + break; + } + } + if (channel->playing) { + free_playing(sigrenderer, channel->playing); + channel->playing = NULL; + } + } + } + } else if (channel->note_delay_count) { + channel->note_delay_count--; + if (channel->note_delay_count == 0) + process_note_data(sigrenderer, channel->note_delay_entry, 0); + /* Don't bother checking the return value; if the note + * was delayed, there can't have been a speed=0. + */ + } + } +} + + + +static int envelope_get_y(IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) +{ +#if 1 + (void)envelope; //TODO: remove the parameter + return pe->value; +#else + int ys, ye; + int ts, te; + int t; + + if (pe->next_node <= 0) + return envelope->node_y[0] << IT_ENVELOPE_SHIFT; + + if (pe->next_node >= envelope->n_nodes) + return envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT; + + ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT; + ts = envelope->node_t[pe->next_node-1]; + te = envelope->node_t[pe->next_node]; + + if (ts == te) + return ys; + + ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT; + + t = pe->tick; + + return ys + (ye - ys) * (t - ts) / (te - ts); +#endif +} + + + +#if 0 +static int it_envelope_end(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) +{ + if (pe->next_node >= envelope->n_nodes) + return 1; + + if (pe->tick < envelope->node_t[pe->next_node]) return 0; + + if ((envelope->flags & IT_ENVELOPE_LOOP_ON) && + envelope->loop_end >= pe->next_node && + envelope->node_t[envelope->loop_end] <= pe->tick) return 0; + + if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && + !(playing->flags & IT_PLAYING_SUSTAINOFF) && + envelope->sus_loop_end >= pe->next_node && + envelope->node_t[envelope->sus_loop_end] <= pe->tick) return 0; + + if (envelope->node_t[envelope->n_nodes-1] <= pe->tick) return 1; + + return 0; +} +#endif + + + +/* Returns 1 when fading should be initiated for a volume envelope. */ +static int update_it_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe, int flags) +{ + if (!(playing->enabled_envelopes & flags) || !envelope->n_nodes) + return 0; + + ASSERT(envelope->n_nodes > 0); + + if (pe->tick <= 0) + pe->value = envelope->node_y[0] << IT_ENVELOPE_SHIFT; + else if (pe->tick >= envelope->node_t[envelope->n_nodes-1]) { + pe->value = envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT; + } else { + int ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT; + int ts = envelope->node_t[pe->next_node-1]; + int te = envelope->node_t[pe->next_node]; + + if (ts == te) + pe->value = ys; + else { + int ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT; + int t = pe->tick; + + pe->value = ys + (ye - ys) * (t - ts) / (te - ts); + } + } + + pe->tick++; + + recalculate_it_envelope_node(pe, envelope); + + if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) { + if (pe->tick > envelope->node_t[envelope->sus_loop_end]) { + pe->next_node = envelope->sus_loop_start + 1; + ASSERT(pe->next_node <= envelope->n_nodes); + pe->tick = envelope->node_t[envelope->sus_loop_start]; + return 0; + } + } else if (envelope->flags & IT_ENVELOPE_LOOP_ON) { + if (pe->tick > envelope->node_t[envelope->loop_end]) { + pe->next_node = envelope->loop_start + 1; + ASSERT(pe->next_node <= envelope->n_nodes); + pe->tick = envelope->node_t[envelope->loop_start]; + return 0; + } + } + else if (pe->tick > envelope->node_t[envelope->n_nodes - 1]) + return 1; + + return 0; +} + + + +static void update_it_envelopes(IT_PLAYING *playing) +{ + IT_ENVELOPE *envelope = &playing->env_instrument->volume_envelope; + IT_PLAYING_ENVELOPE *pe = &playing->volume_envelope; + + if (update_it_envelope(playing, envelope, pe, IT_ENV_VOLUME)) { + playing->flags |= IT_PLAYING_FADING; + if (pe->value == 0) + playing->flags |= IT_PLAYING_DEAD; + } + + update_it_envelope(playing, &playing->env_instrument->pan_envelope, &playing->pan_envelope, IT_ENV_PANNING); + update_it_envelope(playing, &playing->env_instrument->pitch_envelope, &playing->pitch_envelope, IT_ENV_PITCH); +} + + + +static int xm_envelope_is_sustaining(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) +{ + if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) + if (envelope->sus_loop_start < envelope->n_nodes) + if (pe->tick == envelope->node_t[envelope->sus_loop_start]) + return 1; + return 0; +} + + + +static void update_xm_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) +{ + if (!(envelope->flags & IT_ENVELOPE_ON)) + return; + + if (xm_envelope_is_sustaining(playing, envelope, pe)) + return; + + if (pe->tick >= envelope->node_t[envelope->n_nodes-1]) + return; + + pe->tick++; + + /* pe->next_node must be kept up to date for envelope_get_y(). */ + while (pe->tick > envelope->node_t[pe->next_node]) + pe->next_node++; + + if ((envelope->flags & IT_ENVELOPE_LOOP_ON) && envelope->loop_end < envelope->n_nodes) { + if (pe->tick == envelope->node_t[envelope->loop_end]) { + pe->next_node = MID(0, envelope->loop_start, envelope->n_nodes - 1); + pe->tick = envelope->node_t[pe->next_node]; + } + } + + xm_envelope_calculate_value(envelope, pe); +} + + + +static void update_xm_envelopes(IT_PLAYING *playing) +{ + update_xm_envelope(playing, &playing->env_instrument->volume_envelope, &playing->volume_envelope); + update_xm_envelope(playing, &playing->env_instrument->pan_envelope, &playing->pan_envelope); +} + + + +static void update_fadeout(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing) +{ + if (playing->flags & IT_PLAYING_FADING) { + playing->fadeoutcount -= playing->env_instrument->fadeout; + if (playing->fadeoutcount <= 0) { + playing->fadeoutcount = 0; + if (!(sigdata->flags & IT_WAS_AN_XM)) + playing->flags |= IT_PLAYING_DEAD; + } + } +} + +static int apply_pan_envelope(IT_PLAYING *playing); +static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, double volume); + +static void playing_volume_setup(DUMB_IT_SIGRENDERER * sigrenderer, IT_PLAYING * playing, float invt2g) +{ + DUMB_IT_SIGDATA * sigdata = sigrenderer->sigdata; + int pan; + float vol, span; + float rampScale; + int ramp_style = sigrenderer->ramp_style; + + pan = apply_pan_envelope(playing); + + if ((sigrenderer->n_channels >= 2) && (sigdata->flags & IT_STEREO) && (sigrenderer->n_channels != 3 || !IT_IS_SURROUND_SHIFTED(pan))) { + if (!IT_IS_SURROUND_SHIFTED(pan)) { + span = (pan - (32<<8)) * sigdata->pan_separation * (1.0f / ((32<<8) * 128)); + vol = 0.5f * (1.0f - span); + playing->float_volume[0] = vol; + playing->float_volume[1] = 1.0f - vol; + } else { + playing->float_volume[0] = -0.5f; + playing->float_volume[1] = 0.5f; + } + } else { + playing->float_volume[0] = 1.0f; + playing->float_volume[1] = 1.0f; + } + + vol = calculate_volume(sigrenderer, playing, 1.0f); + playing->float_volume[0] *= vol; + playing->float_volume[1] *= vol; + + rampScale = 4; + + if (ramp_style > 0 && playing->declick_stage == 2) { + if ((playing->ramp_volume[0] == 0 && playing->ramp_volume[1] == 0) || vol == 0) + rampScale = 48; + } + + if (ramp_style == 0 || (ramp_style < 2 && playing->declick_stage == 2)) { + if (playing->declick_stage <= 2) { + playing->ramp_volume[0] = playing->float_volume[0]; + playing->ramp_volume[1] = playing->float_volume[1]; + playing->declick_stage = 2; + } else { + playing->float_volume[0] = 0; + playing->float_volume[1] = 0; + playing->ramp_volume[0] = 0; + playing->ramp_volume[1] = 0; + playing->declick_stage = 5; + } + playing->ramp_delta[0] = 0; + playing->ramp_delta[1] = 0; + } else { + if (playing->declick_stage == 0) { + playing->ramp_volume[0] = 0; + playing->ramp_volume[1] = 0; + rampScale = 48; + playing->declick_stage++; + } else if (playing->declick_stage == 1) { + rampScale = 48; + } else if (playing->declick_stage >= 3) { + playing->float_volume[0] = 0; + playing->float_volume[1] = 0; + if (playing->declick_stage == 3) + playing->declick_stage++; + rampScale = 48; + } + playing->ramp_delta[0] = rampScale * invt2g * (playing->float_volume[0] - playing->ramp_volume[0]); + playing->ramp_delta[1] = rampScale * invt2g * (playing->float_volume[1] - playing->ramp_volume[1]); + } +} + +static void process_playing(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float invt2g) +{ + DUMB_IT_SIGDATA * sigdata = sigrenderer->sigdata; + + if (playing->instrument) { + if (sigdata->flags & IT_WAS_AN_XM) + update_xm_envelopes(playing); + else + update_it_envelopes(playing); + update_fadeout(sigdata, playing); + } + + playing_volume_setup(sigrenderer, playing, invt2g); + + if (sigdata->flags & IT_WAS_AN_XM) { + /* 'depth' is used to store the tick number for XM files. */ + if (playing->sample_vibrato_depth < playing->sample->vibrato_rate) + playing->sample_vibrato_depth++; + } else { + playing->sample_vibrato_depth += playing->sample->vibrato_rate; + if (playing->sample_vibrato_depth > playing->sample->vibrato_depth << 8) + playing->sample_vibrato_depth = playing->sample->vibrato_depth << 8; + } + + playing->sample_vibrato_time += playing->sample->vibrato_speed; +} + +// Apparently some GCCs have problems here so renaming the function sounds like a better idea. +//#if defined(_MSC_VER) && _MSC_VER < 1800 +static double mylog2(double x) {return log(x)/log(2.0);} +//#endif + +static int delta_to_note(float delta, int base) +{ + double note; + note = mylog2(delta * 65536.f / (float)base)*12.0f+60.5f; + if (note > 119) note = 119; + else if (note < 0) note = 0; + return (int)note; +} + +#if 0 +// Period table for Protracker octaves 0-5: +static const unsigned short ProTrackerPeriodTable[6*12] = +{ + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907, + 856,808,762,720,678,640,604,570,538,508,480,453, + 428,404,381,360,339,320,302,285,269,254,240,226, + 214,202,190,180,170,160,151,143,135,127,120,113, + 107,101,95,90,85,80,75,71,67,63,60,56, + 53,50,47,45,42,40,37,35,33,31,30,28 +}; + + +static const unsigned short ProTrackerTunedPeriods[16*12] = +{ + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907, + 1700,1604,1514,1430,1348,1274,1202,1134,1070,1010,954,900, + 1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948,894, + 1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940,888, + 1664,1570,1482,1398,1320,1246,1176,1110,1048,990,934,882, + 1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926,874, + 1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920,868, + 1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914,862, + 1814,1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960, + 1800,1700,1604,1514,1430,1350,1272,1202,1134,1070,1010,954, + 1788,1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948, + 1774,1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940, + 1762,1664,1570,1482,1398,1320,1246,1176,1110,1048,988,934, + 1750,1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926, + 1736,1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920, + 1724,1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914 +}; +#endif + +static void process_all_playing(DUMB_IT_SIGRENDERER *sigrenderer) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + int i; + + float invt2g = 1.0f / ((float)TICK_TIME_DIVIDEND / (float)sigrenderer->tempo / 256.0f); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; + IT_PLAYING *playing = channel->playing; + + if (playing) { + int vibrato_shift; + switch (playing->vibrato_waveform) + { + default: + vibrato_shift = it_sine[playing->vibrato_time]; + break; + case 1: + vibrato_shift = it_sawtooth[playing->vibrato_time]; + break; + case 2: + vibrato_shift = it_squarewave[playing->vibrato_time]; + break; + case 3: + vibrato_shift = (rand() % 129) - 64; + break; + case 4: + vibrato_shift = it_xm_squarewave[playing->vibrato_time]; + break; + case 5: + vibrato_shift = it_xm_ramp[playing->vibrato_time]; + break; + case 6: + vibrato_shift = it_xm_ramp[255-playing->vibrato_time]; + break; + } + vibrato_shift *= playing->vibrato_n; + vibrato_shift *= playing->vibrato_depth; + vibrato_shift >>= 4; + + if (sigdata->flags & IT_OLD_EFFECTS) + vibrato_shift = -vibrato_shift; + + playing->volume = channel->volume; + playing->pan = channel->truepan; + + if (playing->volume_offset) { + playing->volume += (playing->volume_offset * playing->volume) >> 7; + if (playing->volume > 64) { + if (playing->volume_offset < 0) playing->volume = 0; + else playing->volume = 64; + } + } + + if (playing->panning_offset && !IT_IS_SURROUND_SHIFTED(playing->pan)) { + playing->pan += playing->panning_offset << IT_ENVELOPE_SHIFT; + if (playing->pan > 64 << IT_ENVELOPE_SHIFT) { + if (playing->panning_offset < 0) playing->pan = 0; + else playing->pan = 64 << IT_ENVELOPE_SHIFT; + } + } + + if (sigdata->flags & IT_LINEAR_SLIDES) { + int currpitch = ((playing->note - 60) << 8) + playing->slide + + vibrato_shift + + playing->finetune; + + /* We add a feature here, which is that of keeping the pitch + * within range. Otherwise it crashes. Trust me. It happened. + * The limit 32768 gives almost 11 octaves either way. + */ + if (currpitch < -32768) + currpitch = -32768; + else if (currpitch > 32767) + currpitch = 32767; + + playing->delta = (float)pow(DUMB_PITCH_BASE, currpitch); + playing->delta *= playing->sample->C5_speed * (1.f / 65536.0f); + } else { + int slide = playing->slide + vibrato_shift; + + playing->delta = (float)pow(DUMB_PITCH_BASE, ((60 - playing->note) << 8) - playing->finetune ); + /* playing->delta is 1.0 for C-5, 0.5 for C-6, etc. */ + + playing->delta *= 1.0f / playing->sample->C5_speed; + + playing->delta -= slide / AMIGA_DIVISOR; + + if (playing->delta < (1.0f / 65536.0f) / 32768.0f) { + // Should XM notes die if Amiga slides go out of range? + playing->flags |= IT_PLAYING_DEAD; + playing->delta = 1. / 32768.; + continue; + } + + playing->delta = (1.0f / 65536.0f) / playing->delta; + } + + if (playing->channel->glissando && playing->channel->toneporta && playing->channel->destnote < 120) { + playing->delta = (float)pow(DUMB_SEMITONE_BASE, delta_to_note(playing->delta, playing->sample->C5_speed) - 60) + * playing->sample->C5_speed * (1.f / 65536.f); + } + + /* + if ( channel->arpeggio ) { // another FT2 bug... + if ((sigdata->flags & (IT_LINEAR_SLIDES|IT_WAS_AN_XM|IT_WAS_A_MOD)) == (IT_WAS_AN_XM|IT_LINEAR_SLIDES) && + playing->flags & IT_PLAYING_SUSTAINOFF) + { + if ( channel->arpeggio > 0xFF ) + playing->delta = playing->sample->C5_speed * (1.f / 65536.f); + } + else*/ + { + int tick = sigrenderer->tick - 1; + if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD))!=IT_WAS_AN_XM) + tick = sigrenderer->speed - tick - 1; + else if (tick == sigrenderer->speed - 1) + tick = 0; + else + ++tick; + playing->delta *= (float)pow(DUMB_SEMITONE_BASE, channel->arpeggio_offsets[channel->arpeggio_table[tick&31]]); + } + /* + }*/ + + playing->filter_cutoff = channel->filter_cutoff; + playing->filter_resonance = channel->filter_resonance; + } + } + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing) { + process_playing(sigrenderer, sigrenderer->channel[i].playing, invt2g); + if (!(sigdata->flags & IT_WAS_AN_XM)) { + //if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) { + // This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it. + if (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD) { + free_playing(sigrenderer, sigrenderer->channel[i].playing); + sigrenderer->channel[i].playing = NULL; + } + } + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { + process_playing(sigrenderer, sigrenderer->playing[i], invt2g); + if (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD) { + free_playing(sigrenderer, sigrenderer->playing[i]); + sigrenderer->playing[i] = NULL; + } + } + } +} + + + +static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + + // Set note vol/freq to vol/freq set for each channel + + if (sigrenderer->speed && --sigrenderer->tick == 0) { + reset_tick_counts(sigrenderer); + sigrenderer->tick = sigrenderer->speed; + sigrenderer->rowcount--; + if (sigrenderer->rowcount == 0) { + sigrenderer->rowcount = 1; + +#ifdef BIT_ARRAY_BULLSHIT + if (sigrenderer->n_rows) + { +#if 1 + /* + if (bit_array_test(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row)) + { + if (sigrenderer->callbacks->loop) { + if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data)) + return 1; + bit_array_reset(sigrenderer->played); + if (sigrenderer->speed == 0) + goto speed0; // I love goto + } + } + */ +#endif + bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row); + { + int n; + for (n = 0; n < DUMB_IT_N_CHANNELS; n++) + { + IT_CHANNEL * channel = &sigrenderer->channel[n]; + if (channel->played_patjump) + { + if (channel->played_patjump_order == sigrenderer->order) + { + bit_array_set(channel->played_patjump, sigrenderer->row); + } + /* + else if ((channel->played_patjump_order & 0x7FFF) == sigrenderer->order) + { + channel->played_patjump_order |= 0x4000; + } + else if ((channel->played_patjump_order & 0x3FFF) == sigrenderer->order) + { + if ((sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) + { + // joy, was XM, pattern loop bug triggered break to row in same order + bit_array_mask(sigrenderer->played, channel->played_patjump, sigrenderer->order * 256); + } + bit_array_destroy(channel->played_patjump); + channel->played_patjump = 0; + channel->played_patjump_order = 0xFFFE; + } + */ + else + { + bit_array_destroy(channel->played_patjump); + channel->played_patjump = 0; + channel->played_patjump_order = 0xFFFE; + } + } + } + } + } +#endif + + sigrenderer->processrow++; + + if (sigrenderer->processrow >= sigrenderer->n_rows) { + IT_PATTERN *pattern; + int n; + int processorder = sigrenderer->processorder; + + if ((sigrenderer->processrow|0xC00) == 0xFFFE + 1) { /* It was incremented above! */ + sigrenderer->processrow = sigrenderer->breakrow; + sigrenderer->breakrow = 0; + for (n = 0; n < DUMB_IT_N_CHANNELS; n++) sigrenderer->channel[n].pat_loop_end_row = 0; + } else { + sigrenderer->processrow = sigrenderer->breakrow; + sigrenderer->breakrow = 0; // XXX lolwut + } + + if (sigrenderer->processorder == 0xFFFF) + sigrenderer->processorder = sigrenderer->order - 1; + + for (;;) { + sigrenderer->processorder++; + + if (sigrenderer->processorder >= sigdata->n_orders) { + sigrenderer->processorder = sigrenderer->restart_position; + if (sigrenderer->processorder >= sigdata->n_orders) { + /* Restarting beyond end. We'll loop for now. */ + sigrenderer->processorder = -1; + continue; + } + if (sigdata->flags & IT_WAS_AN_OKT) { + /* Reset some things */ + sigrenderer->speed = sigdata->speed; + sigrenderer->tempo = sigdata->tempo; + for (n = 0; n < DUMB_IT_N_CHANNELS; n++) { + xm_note_off(sigdata, &sigrenderer->channel[n]); + } + } + } + + n = sigdata->order[sigrenderer->processorder]; + + if (n < sigdata->n_patterns) + break; + +#ifdef INVALID_ORDERS_END_SONG + if (n != IT_ORDER_SKIP) +#else + if (n == IT_ORDER_END) +#endif + { + sigrenderer->processorder = sigrenderer->restart_position - 1; + } + +#ifdef BIT_ARRAY_BULLSHIT + /* Fix play tracking and timekeeping for orders containing skip commands */ + for (n = 0; n < 256; n++) { + bit_array_set(sigrenderer->played, sigrenderer->processorder * 256 + n); + } +#endif + } + + pattern = &sigdata->pattern[n]; + + n = sigrenderer->n_rows; + sigrenderer->n_rows = pattern->n_rows; + + if (sigrenderer->processrow >= sigrenderer->n_rows) + sigrenderer->processrow = 0; + +/** WARNING - everything pertaining to a new pattern initialised? */ + + sigrenderer->entry = sigrenderer->entry_start = pattern->entry; + sigrenderer->entry_end = sigrenderer->entry + pattern->n_entries; + + /* If n_rows was 0, we're only just starting. Don't do anything weird here. */ + /* added: process row check, for break to row spooniness */ + if (n && (processorder == 0xFFFF ? sigrenderer->order > sigrenderer->processorder : sigrenderer->order >= sigrenderer->processorder) +#ifdef BIT_ARRAY_BULLSHIT + && bit_array_test(sigrenderer->played, sigrenderer->processorder * 256 + sigrenderer->processrow) +#endif + ) { + if (sigrenderer->callbacks->loop) { + if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data)) + return 1; +#ifdef BIT_ARRAY_BULLSHIT + bit_array_reset(sigrenderer->played); +#endif + if (sigrenderer->speed == 0) + goto speed0; /* I love goto */ + } + } + sigrenderer->order = sigrenderer->processorder; + + n = sigrenderer->processrow; + while (n) { + while (sigrenderer->entry < sigrenderer->entry_end) { + if (IT_IS_END_ROW(sigrenderer->entry)) { + sigrenderer->entry++; + break; + } + sigrenderer->entry++; + } + n--; + } + sigrenderer->row = sigrenderer->processrow; + } else { + if (sigrenderer->entry) { + while (sigrenderer->entry < sigrenderer->entry_end) { + if (IT_IS_END_ROW(sigrenderer->entry)) { + sigrenderer->entry++; + break; + } + sigrenderer->entry++; + } + sigrenderer->row++; + } else { +#ifdef BIT_ARRAY_BULLSHIT + bit_array_clear(sigrenderer->played, sigrenderer->order * 256); +#endif + sigrenderer->entry = sigrenderer->entry_start; + sigrenderer->row = 0; + } + } + + if (!(sigdata->flags & IT_WAS_A_669)) + reset_effects(sigrenderer); + + { + IT_ENTRY *entry = sigrenderer->entry; + int ignore_cxx = 0; + + while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) + ignore_cxx |= update_pattern_variables(sigrenderer, entry++); + + entry = sigrenderer->entry; + + while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) + if (process_entry(sigrenderer, entry++, sigdata->flags & IT_WAS_AN_XM ? 0 : ignore_cxx)) + return 1; + } + + if (sigdata->flags & IT_WAS_AN_OKT) + update_effects(sigrenderer); + else if (!(sigdata->flags & IT_OLD_EFFECTS)) + update_smooth_effects(sigrenderer); + } else { + { + IT_ENTRY *entry = sigrenderer->entry; + + while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) { + if (entry->mask & IT_ENTRY_EFFECT && entry->effect != IT_SET_SAMPLE_OFFSET) + process_effects(sigrenderer, entry, 0); + /* Don't bother checking the return value; if there + * was a pattern delay, there can't be a speed=0. + */ + entry++; + } + } + + update_effects(sigrenderer); + } + } else { + if ( !(sigdata->flags & IT_WAS_AN_STM) || !(sigrenderer->tick & 15)) { + speed0: + update_effects(sigrenderer); + update_tick_counts(sigrenderer); + } + } + + if (sigrenderer->globalvolume == 0) { + if (sigrenderer->callbacks->global_volume_zero) { + LONG_LONG t = sigrenderer->gvz_sub_time + ((TICK_TIME_DIVIDEND / (sigrenderer->tempo << 8)) << 16); + sigrenderer->gvz_time += (int)(t >> 16); + sigrenderer->gvz_sub_time = (int)t & 65535; + if (sigrenderer->gvz_time >= 65536 * 12) { + if ((*sigrenderer->callbacks->global_volume_zero)(sigrenderer->callbacks->global_volume_zero_data)) + return 1; + } + } + } else { + if (sigrenderer->callbacks->global_volume_zero) { + sigrenderer->gvz_time = 0; + sigrenderer->gvz_sub_time = 0; + } + } + + process_all_playing(sigrenderer); + + { + LONG_LONG t = (TICK_TIME_DIVIDEND / (sigrenderer->tempo << 8)) << 16; + if ( sigrenderer->sigdata->flags & IT_WAS_AN_STM ) { + t /= 16; + } + t += sigrenderer->sub_time_left; + sigrenderer->time_left += (int)(t >> 16); + sigrenderer->sub_time_left = (int)t & 65535; + } + + return 0; +} + + + +int dumb_it_max_to_mix = 64; + +#if 0 +static const int aiMODVol[] = +{ + 0, + 16, 24, 32, 48, 64, 80, 96, 112, + 128, 144, 160, 176, 192, 208, 224, 240, + 256, 272, 288, 304, 320, 336, 352, 368, + 384, 400, 416, 432, 448, 464, 480, 496, + 529, 545, 561, 577, 593, 609, 625, 641, + 657, 673, 689, 705, 721, 737, 753, 769, + 785, 801, 817, 833, 849, 865, 881, 897, + 913, 929, 945, 961, 977, 993, 1009, 1024 +}; +#endif + +static const int aiPTMVolScaled[] = +{ + 0, + 31, 54, 73, 96, 111, 130, 153, 172, + 191, 206, 222, 237, 252, 275, 298, 317, + 336, 351, 370, 386, 401, 416, 428, 443, + 454, 466, 477, 489, 512, 531, 553, 573, + 592, 611, 626, 645, 660, 679, 695, 710, + 725, 740, 756, 767, 782, 798, 809, 820, + 836, 847, 859, 870, 881, 897, 908, 916, + 927, 939, 950, 962, 969, 983, 1005, 1024 +}; + +static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, double volume) +{ + if (volume != 0) { + int vol; + + if (playing->channel->flags & IT_CHANNEL_MUTED) + return 0; + + if ((playing->channel->tremor_time & 192) == 128) + return 0; + + switch (playing->tremolo_waveform) + { + default: + vol = it_sine[playing->tremolo_time]; + break; + case 1: + vol = it_sawtooth[playing->tremolo_time]; + break; + case 2: + vol = it_squarewave[playing->tremolo_time]; + break; + case 3: + vol = (rand() % 129) - 64; + break; + case 4: + vol = it_xm_squarewave[playing->tremolo_time]; + break; + case 5: + vol = it_xm_ramp[playing->tremolo_time]; + break; + case 6: + vol = it_xm_ramp[255-((sigrenderer->sigdata->flags & IT_WAS_A_MOD)?playing->vibrato_time:playing->tremolo_time)]; + break; + } + vol *= playing->tremolo_depth; + + vol = (playing->volume << 5) + vol; + + if (vol <= 0) + return 0; + + if (vol > 64 << 5) + vol = 64 << 5; + + if ( sigrenderer->sigdata->flags & IT_WAS_A_PTM ) + { + int v = aiPTMVolScaled[ vol >> 5 ]; + if ( vol < 64 << 5 ) + { + int f = vol & ( ( 1 << 5 ) - 1 ); + int f2 = ( 1 << 5 ) - f; + int v2 = aiPTMVolScaled[ ( vol >> 5 ) + 1 ]; + v = ( v * f2 + v2 * f ) >> 5; + } + vol = v << 1; + } + + volume *= vol; /* 64 << 5 */ + volume *= playing->sample->global_volume; /* 64 */ + volume *= playing->channel_volume; /* 64 */ + volume *= sigrenderer->globalvolume; /* 128 */ + volume *= sigrenderer->sigdata->mixing_volume; /* 128 */ + volume *= 1.0f / ((64 << 5) * 64.0f * 64.0f * 128.0f * 128.0f); + + if (volume && playing->instrument) { + if (playing->enabled_envelopes & IT_ENV_VOLUME && playing->env_instrument->volume_envelope.n_nodes) { + volume *= envelope_get_y(&playing->env_instrument->volume_envelope, &playing->volume_envelope); + volume *= 1.0f / (64 << IT_ENVELOPE_SHIFT); + } + volume *= playing->instrument->global_volume; /* 128 */ + volume *= playing->fadeoutcount; /* 1024 */ + volume *= 1.0f / (128.0f * 1024.0f); + } + } + + return (float)volume; +} + + + +static int apply_pan_envelope(IT_PLAYING *playing) +{ + if (playing->pan <= 64 << IT_ENVELOPE_SHIFT) { + int pan; + if (playing->panbrello_depth) { + switch (playing->panbrello_waveform) { + default: + pan = it_sine[playing->panbrello_time]; + break; + case 1: + pan = it_sawtooth[playing->panbrello_time]; + break; + case 2: + pan = it_squarewave[playing->panbrello_time]; + break; + case 3: + pan = playing->panbrello_random; + break; + } + pan *= playing->panbrello_depth << 3; + + pan += playing->pan; + if (pan < 0) pan = 0; + else if (pan > 64 << IT_ENVELOPE_SHIFT) pan = 64 << IT_ENVELOPE_SHIFT; + } else { + pan = playing->pan; + } + + if (playing->env_instrument && (playing->enabled_envelopes & IT_ENV_PANNING)) { + int p = envelope_get_y(&playing->env_instrument->pan_envelope, &playing->pan_envelope); + if (pan > 32 << IT_ENVELOPE_SHIFT) + p *= (64 << IT_ENVELOPE_SHIFT) - pan; + else + p *= pan; + pan += p >> (5 + IT_ENVELOPE_SHIFT); + } + return pan; + } + return playing->pan; +} + + +/* Note: if a click remover is provided, and store_end_sample is set, then + * the end point will be computed twice. This situation should not arise. + */ +static int32 render_playing(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, double volume, double main_delta, double delta, int32 pos, int32 size, sample_t **samples, int store_end_sample, int *left_to_mix) +{ + int bits; + + int32 size_rendered; + + DUMB_VOLUME_RAMP_INFO lvol, rvol; + + if (playing->flags & IT_PLAYING_DEAD) + return 0; + + if (*left_to_mix <= 0) + volume = 0; + + { + int quality = sigrenderer->resampling_quality; + if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality) + quality = playing->sample->max_resampling_quality; + playing->resampler.quality = quality; + resampler_set_quality(playing->resampler.fir_resampler[0], quality - DUMB_RESAMPLER_BASE); + resampler_set_quality(playing->resampler.fir_resampler[1], quality - DUMB_RESAMPLER_BASE); + } + + bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8; + + if (volume == 0) { + if (playing->sample->flags & IT_SAMPLE_STEREO) + size_rendered = dumb_resample_n_2_2(bits, &playing->resampler, NULL, size, 0, 0, delta); + else + size_rendered = dumb_resample_n_1_2(bits, &playing->resampler, NULL, size, 0, 0, delta); + } else { + lvol.volume = playing->ramp_volume [0]; + rvol.volume = playing->ramp_volume [1]; + lvol.delta = (float)(playing->ramp_delta [0] * main_delta); + rvol.delta = (float)(playing->ramp_delta [1] * main_delta); + lvol.target = playing->float_volume [0]; + rvol.target = playing->float_volume [1]; + rvol.mix = lvol.mix = (float)volume; + lvol.declick_stage = rvol.declick_stage = playing->declick_stage; + if (sigrenderer->n_channels >= 2) { + if (playing->sample->flags & IT_SAMPLE_STEREO) { + if (sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, &lvol, &rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos, click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos, click[1]); + } + size_rendered = dumb_resample_n_2_2(bits, &playing->resampler, samples[0] + pos*2, size, &lvol, &rvol, delta); + if (store_end_sample) { + sample_t click[2]; + dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, &lvol, &rvol, click); + samples[0][(pos + size_rendered) * 2] = click[0]; + samples[0][(pos + size_rendered) * 2 + 1] = click[1]; + } + if (sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, &lvol, &rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]); + } + } else { + if (sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, &lvol, &rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos, click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos, click[1]); + } + size_rendered = dumb_resample_n_1_2(bits, &playing->resampler, samples[0] + pos*2, size, &lvol, &rvol, delta); + if (store_end_sample) { + sample_t click[2]; + dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, &lvol, &rvol, click); + samples[0][(pos + size_rendered) * 2] = click[0]; + samples[0][(pos + size_rendered) * 2 + 1] = click[1]; + } + if (sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, &lvol, &rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]); + } + } + } +#if 0 // [RH] Don't need mono output + else { + if (playing->sample->flags & IT_SAMPLE_STEREO) { + if (sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, &lvol, &rvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos, click); + } + size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, samples[0] + pos, size, &lvol, &rvol, delta); + if (store_end_sample) + dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, &lvol, &rvol, &samples[0][pos + size_rendered]); + if (sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, &lvol, &rvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click); + } + } else { + if (sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, &lvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos, click); + } + size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, samples[0] + pos, size, &lvol, delta); + if (store_end_sample) + dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, &lvol, &samples[0][pos + size_rendered]); + if (sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, &lvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click); + } + } + } +#endif + playing->ramp_volume [0] = lvol.volume; + playing->ramp_volume [1] = rvol.volume; + playing->declick_stage = (lvol.declick_stage > rvol.declick_stage) ? lvol.declick_stage : rvol.declick_stage; + if (playing->declick_stage >= 4) + playing->flags |= IT_PLAYING_DEAD; + (*left_to_mix)--; + } + + if (playing->resampler.dir == 0) + playing->flags |= IT_PLAYING_DEAD; + + return size_rendered; +} + +typedef struct IT_TO_MIX +{ + IT_PLAYING *playing; + float volume; +} +IT_TO_MIX; + + + +static int CDECL it_to_mix_compare(const void *e1, const void *e2) +{ + if (((const IT_TO_MIX *)e1)->volume > ((const IT_TO_MIX *)e2)->volume) + return -1; + + if (((const IT_TO_MIX *)e1)->volume < ((const IT_TO_MIX *)e2)->volume) + return 1; + + return 0; +} + + + +static void apply_pitch_modifications(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing, double *delta, int *cutoff) +{ + { + int sample_vibrato_shift; + switch (playing->sample_vibrato_waveform) + { + default: + sample_vibrato_shift = it_sine[playing->sample_vibrato_time]; + break; + case 1: + sample_vibrato_shift = it_sawtooth[playing->sample_vibrato_time]; + break; + case 2: + sample_vibrato_shift = it_squarewave[playing->sample_vibrato_time]; + break; + case 3: + sample_vibrato_shift = (rand() % 129) - 64; + break; + case 4: + sample_vibrato_shift = it_xm_squarewave[playing->sample_vibrato_time]; + break; + case 5: + sample_vibrato_shift = it_xm_ramp[playing->sample_vibrato_time]; + break; + case 6: + sample_vibrato_shift = it_xm_ramp[255-playing->sample_vibrato_time]; + break; + } + + if (sigdata->flags & IT_WAS_AN_XM) { + int depth = playing->sample->vibrato_depth; /* True depth */ + if (playing->sample->vibrato_rate) { + depth *= playing->sample_vibrato_depth; /* Tick number */ + depth /= playing->sample->vibrato_rate; /* XM sweep */ + } + sample_vibrato_shift *= depth; + } else + sample_vibrato_shift *= playing->sample_vibrato_depth >> 8; + + sample_vibrato_shift >>= 4; + + if (sample_vibrato_shift) { + if ((sigdata->flags & IT_LINEAR_SLIDES) || !(sigdata->flags & IT_WAS_AN_XM)) + *delta *= (float)pow(DUMB_PITCH_BASE, sample_vibrato_shift); + else { + /* complicated! */ + double scale = *delta / playing->delta; + + *delta = (1.0f / 65536.0f) / playing->delta; + + *delta -= sample_vibrato_shift / AMIGA_DIVISOR; + + if (*delta < (1.0f / 65536.0f) / 32767.0f) { + *delta = (1.0f / 65536.0f) / 32767.0f; + } + + *delta = (1.0f / 65536.0f) / *delta * scale; + } + } + } + + if (playing->env_instrument && + (playing->enabled_envelopes & IT_ENV_PITCH)) + { + int p = envelope_get_y(&playing->env_instrument->pitch_envelope, &playing->pitch_envelope); + if (playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_PITCH_IS_FILTER) + *cutoff = (*cutoff * (p+(32<> (6 + IT_ENVELOPE_SHIFT); + else + *delta *= (float)pow(DUMB_PITCH_BASE, p >> (IT_ENVELOPE_SHIFT - 7)); + } +} + + + +static void render_normal(DUMB_IT_SIGRENDERER *sigrenderer, double volume, double delta, int32 pos, int32 size, sample_t **samples) +{ + int i; + + int n_to_mix = 0; + IT_TO_MIX to_mix[DUMB_IT_TOTAL_CHANNELS]; + int left_to_mix = dumb_it_max_to_mix; + + sample_t **samples_to_filter = NULL; + + //int max_output = sigrenderer->max_output; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing && !(sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) { + to_mix[n_to_mix].playing = sigrenderer->channel[i].playing; + to_mix[n_to_mix].volume = volume == 0 ? 0 : calculate_volume(sigrenderer, sigrenderer->channel[i].playing, volume); + n_to_mix++; + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { /* Won't be dead; it would have been freed. */ + to_mix[n_to_mix].playing = sigrenderer->playing[i]; + to_mix[n_to_mix].volume = volume == 0 ? 0 : calculate_volume(sigrenderer, sigrenderer->playing[i], volume); + n_to_mix++; + } + } + + if (volume != 0) + qsort(to_mix, n_to_mix, sizeof(IT_TO_MIX), &it_to_mix_compare); + + for (i = 0; i < n_to_mix; i++) { + IT_PLAYING *playing = to_mix[i].playing; + double note_delta = delta * playing->delta; + int cutoff = playing->filter_cutoff << IT_ENVELOPE_SHIFT; + //int output = min( playing->output, max_output ); + + apply_pitch_modifications(sigrenderer->sigdata, playing, ¬e_delta, &cutoff); + + if (cutoff != 127 << IT_ENVELOPE_SHIFT || playing->filter_resonance != 0) { + playing->true_filter_cutoff = cutoff; + playing->true_filter_resonance = playing->filter_resonance; + } + + if (volume && (playing->true_filter_cutoff != 127 << IT_ENVELOPE_SHIFT || playing->true_filter_resonance != 0)) { + if (!samples_to_filter) { + samples_to_filter = allocate_sample_buffer(sigrenderer->n_channels, size + 1); + if (!samples_to_filter) { + render_playing(sigrenderer, playing, 0, delta, note_delta, pos, size, NULL, 0, &left_to_mix); + continue; + } + } + { + int32 size_rendered; + DUMB_CLICK_REMOVER **cr = sigrenderer->click_remover; + dumb_silence(samples_to_filter[0], sigrenderer->n_channels * (size + 1)); + sigrenderer->click_remover = NULL; + size_rendered = render_playing(sigrenderer, playing, volume, delta, note_delta, 0, size, samples_to_filter, 1, &left_to_mix); + sigrenderer->click_remover = cr; + if (sigrenderer->n_channels == 2) { + it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0 /*output*/], pos, samples_to_filter[0], size_rendered, + 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + it_filter(cr ? cr[1] : NULL, &playing->filter_state[1], samples[0 /*output*/]+1, pos, samples_to_filter[0]+1, size_rendered, + 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + } else { + it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0 /*output*/], pos, samples_to_filter[0], size_rendered, + 1, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + } + // FIXME: filtering is not prevented by low left_to_mix! + // FIXME: change 'warning' to 'FIXME' everywhere + } + } else { + it_reset_filter_state(&playing->filter_state[0]); + it_reset_filter_state(&playing->filter_state[1]); + render_playing(sigrenderer, playing, volume, delta, note_delta, pos, size, samples /*&samples[output]*/, 0, &left_to_mix); + } + } + + destroy_sample_buffer(samples_to_filter); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing) { + //if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) { + // This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it. + if (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD) { + free_playing(sigrenderer, sigrenderer->channel[i].playing); + sigrenderer->channel[i].playing = NULL; + } + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { + if (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD) { + free_playing(sigrenderer, sigrenderer->playing[i]); + sigrenderer->playing[i] = NULL; + } + } + } +} + + + +static void render_surround(DUMB_IT_SIGRENDERER *sigrenderer, double volume, double delta, int32 pos, int32 size, sample_t **samples) +{ + int i; + + int n_to_mix = 0, n_to_mix_surround = 0; + IT_TO_MIX to_mix[DUMB_IT_TOTAL_CHANNELS]; + IT_TO_MIX to_mix_surround[DUMB_IT_TOTAL_CHANNELS]; + int left_to_mix = dumb_it_max_to_mix; + + int saved_channels = sigrenderer->n_channels; + + sample_t **samples_to_filter = NULL; + + DUMB_CLICK_REMOVER **saved_cr = sigrenderer->click_remover; + + //int max_output = sigrenderer->max_output; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing && !(sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) { + IT_PLAYING *playing = sigrenderer->channel[i].playing; + IT_TO_MIX *_to_mix = IT_IS_SURROUND_SHIFTED(playing->pan) ? to_mix_surround + n_to_mix_surround++ : to_mix + n_to_mix++; + _to_mix->playing = playing; + _to_mix->volume = volume == 0 ? 0 : calculate_volume(sigrenderer, playing, volume); + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { /* Won't be dead; it would have been freed. */ + IT_PLAYING *playing = sigrenderer->playing[i]; + IT_TO_MIX *_to_mix = IT_IS_SURROUND_SHIFTED(playing->pan) ? to_mix_surround + n_to_mix_surround++ : to_mix + n_to_mix++; + _to_mix->playing = playing; + _to_mix->volume = volume == 0 ? 0 : calculate_volume(sigrenderer, playing, volume); + } + } + + if (volume != 0) { + qsort(to_mix, n_to_mix, sizeof(IT_TO_MIX), &it_to_mix_compare); + qsort(to_mix_surround, n_to_mix_surround, sizeof(IT_TO_MIX), &it_to_mix_compare); + } + + sigrenderer->n_channels = 2; + + for (i = 0; i < n_to_mix; i++) { + IT_PLAYING *playing = to_mix[i].playing; + double note_delta = delta * playing->delta; + int cutoff = playing->filter_cutoff << IT_ENVELOPE_SHIFT; + //int output = min( playing->output, max_output ); + + apply_pitch_modifications(sigrenderer->sigdata, playing, ¬e_delta, &cutoff); + + if (cutoff != 127 << IT_ENVELOPE_SHIFT || playing->filter_resonance != 0) { + playing->true_filter_cutoff = cutoff; + playing->true_filter_resonance = playing->filter_resonance; + } + + if (volume && (playing->true_filter_cutoff != 127 << IT_ENVELOPE_SHIFT || playing->true_filter_resonance != 0)) { + if (!samples_to_filter) { + samples_to_filter = allocate_sample_buffer(sigrenderer->n_channels, size + 1); + if (!samples_to_filter) { + render_playing(sigrenderer, playing, 0, delta, note_delta, pos, size, NULL, 0, &left_to_mix); + continue; + } + } + { + long size_rendered; + DUMB_CLICK_REMOVER **cr = sigrenderer->click_remover; + dumb_silence(samples_to_filter[0], sigrenderer->n_channels * (size + 1)); + sigrenderer->click_remover = NULL; + size_rendered = render_playing(sigrenderer, playing, volume, delta, note_delta, 0, size, samples_to_filter, 1, &left_to_mix); + sigrenderer->click_remover = cr; + it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0 /*output*/], pos, samples_to_filter[0], size_rendered, + 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + it_filter(cr ? cr[1] : NULL, &playing->filter_state[1], samples[0 /*output*/]+1, pos, samples_to_filter[0]+1, size_rendered, + 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + } + } else { + it_reset_filter_state(&playing->filter_state[0]); + it_reset_filter_state(&playing->filter_state[1]); + render_playing(sigrenderer, playing, volume, delta, note_delta, pos, size, samples /*&samples[output]*/, 0, &left_to_mix); + } + } + + sigrenderer->n_channels = 1; + sigrenderer->click_remover = saved_cr ? saved_cr + 2 : 0; + + for (i = 0; i < n_to_mix_surround; i++) { + IT_PLAYING *playing = to_mix_surround[i].playing; + double note_delta = delta * playing->delta; + int cutoff = playing->filter_cutoff << IT_ENVELOPE_SHIFT; + //int output = min( playing->output, max_output ); + + apply_pitch_modifications(sigrenderer->sigdata, playing, ¬e_delta, &cutoff); + + if (cutoff != 127 << IT_ENVELOPE_SHIFT || playing->filter_resonance != 0) { + playing->true_filter_cutoff = cutoff; + playing->true_filter_resonance = playing->filter_resonance; + } + + if (volume && (playing->true_filter_cutoff != 127 << IT_ENVELOPE_SHIFT || playing->true_filter_resonance != 0)) { + if (!samples_to_filter) { + samples_to_filter = allocate_sample_buffer(sigrenderer->n_channels, size + 1); + if (!samples_to_filter) { + render_playing(sigrenderer, playing, 0, delta, note_delta, pos, size, NULL, 0, &left_to_mix); + continue; + } + } + { + long size_rendered; + DUMB_CLICK_REMOVER **cr = sigrenderer->click_remover; + dumb_silence(samples_to_filter[0], size + 1); + sigrenderer->click_remover = NULL; + size_rendered = render_playing(sigrenderer, playing, volume, delta, note_delta, 0, size, samples_to_filter, 1, &left_to_mix); + sigrenderer->click_remover = cr; + it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[1 /*output*/], pos, samples_to_filter[0], size_rendered, + 1, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + // FIXME: filtering is not prevented by low left_to_mix! + // FIXME: change 'warning' to 'FIXME' everywhere + } + } else { + it_reset_filter_state(&playing->filter_state[0]); + it_reset_filter_state(&playing->filter_state[1]); + render_playing(sigrenderer, playing, volume, delta, note_delta, pos, size, &samples[1], 0, &left_to_mix); + } + } + + sigrenderer->n_channels = saved_channels; + sigrenderer->click_remover = saved_cr; + + destroy_sample_buffer(samples_to_filter); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing) { + //if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) { + // This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it. + if (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD) { + free_playing(sigrenderer, sigrenderer->channel[i].playing); + sigrenderer->channel[i].playing = NULL; + } + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { + if (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD) { + free_playing(sigrenderer, sigrenderer->playing[i]); + sigrenderer->playing[i] = NULL; + } + } + } +} + + + +static void render(DUMB_IT_SIGRENDERER *sigrenderer, double volume, double delta, int32 pos, int32 size, sample_t **samples) +{ + if (size == 0) return; + if (sigrenderer->n_channels == 1 || sigrenderer->n_channels == 2) + render_normal(sigrenderer, volume, delta, pos, size, samples); + else if (sigrenderer->n_channels == 3) + render_surround(sigrenderer, volume, delta, pos, size, samples); +} + + + +static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_channels, int startorder, IT_CALLBACKS *callbacks, DUMB_CLICK_REMOVER **cr) +{ + DUMB_IT_SIGRENDERER *sigrenderer; + int i; + + /* [RH] Mono destination mixers are disabled. */ + if (n_channels != 2) { + return NULL; + } + + if (startorder > sigdata->n_orders) { + free(callbacks); + dumb_destroy_click_remover_array(n_channels, cr); + return NULL; + } + + sigrenderer = malloc(sizeof(*sigrenderer)); + if (!sigrenderer) { + free(callbacks); + dumb_destroy_click_remover_array(n_channels, cr); + return NULL; + } + + sigrenderer->free_playing = NULL; + sigrenderer->callbacks = callbacks; + sigrenderer->click_remover = cr; + + sigrenderer->sigdata = sigdata; + sigrenderer->n_channels = n_channels; + sigrenderer->resampling_quality = dumb_resampling_quality; + sigrenderer->ramp_style = DUMB_IT_RAMP_FULL; + sigrenderer->globalvolume = sigdata->global_volume; + sigrenderer->tempo = sigdata->tempo; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; +#if IT_CHANNEL_MUTED != 1 +#error this is wrong +#endif + channel->flags = sigdata->channel_pan[i] >> 7; + channel->volume = (sigdata->flags & IT_WAS_AN_XM) ? 0 : 64; + channel->pan = sigdata->channel_pan[i] & 0x7F; + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + channel->channelvolume = sigdata->channel_volume[i]; + channel->instrument = 0; + channel->sample = 0; + channel->note = IT_NOTE_OFF; + channel->SFmacro = 0; + channel->filter_cutoff = 127; + channel->filter_resonance = 0; + channel->new_note_action = 0xFF; + channel->xm_retrig = 0; + channel->retrig_tick = 0; + channel->tremor_time = 0; + channel->vibrato_waveform = 0; + channel->tremolo_waveform = 0; + channel->panbrello_waveform = 0; + channel->glissando = 0; + channel->toneslide = 0; + channel->ptm_toneslide = 0; + channel->ptm_last_toneslide = 0; + channel->okt_toneslide = 0; + channel->midi_state = 0; + channel->lastvolslide = 0; + channel->lastDKL = 0; + channel->lastEF = 0; + channel->lastG = 0; + channel->lastHspeed = 0; + channel->lastHdepth = 0; + channel->lastRspeed = 0; + channel->lastRdepth = 0; + channel->lastYspeed = 0; + channel->lastYdepth = 0; + channel->lastI = 0; + channel->lastJ = 0; + channel->lastN = 0; + channel->lastO = 0; + channel->high_offset = 0; + channel->lastP = 0; + channel->lastQ = 0; + channel->lastS = 0; + channel->pat_loop_row = 0; + channel->pat_loop_count = 0; + channel->pat_loop_end_row = 0; + channel->lastW = 0; + channel->xm_lastE1 = 0; + channel->xm_lastE2 = 0; + channel->xm_lastEA = 0; + channel->xm_lastEB = 0; + channel->xm_lastX1 = 0; + channel->xm_lastX2 = 0; + channel->inv_loop_delay = 0; + channel->inv_loop_speed = 0; + channel->inv_loop_offset = 0; + channel->playing = NULL; +#ifdef BIT_ARRAY_BULLSHIT + channel->played_patjump = NULL; + channel->played_patjump_order = 0xFFFE; +#endif + //channel->output = 0; + } + + if (sigdata->flags & IT_WAS_A_669) + reset_effects(sigrenderer); + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + sigrenderer->playing[i] = NULL; + + sigrenderer->speed = sigdata->speed; + + sigrenderer->processrow = 0xFFFE; + sigrenderer->n_rows = 0; + sigrenderer->breakrow = 0; + sigrenderer->rowcount = 1; + sigrenderer->order = startorder; + /* meh! + if (startorder > 0) { + int n; + for (n = startorder - 1; n >= 0; n--) { + if (sigdata->order[n] > sigdata->n_patterns) { + sigrenderer->restart_position = n + 1; + break; + } + } + } + */ + if (startorder > 0) { + sigrenderer->restart_position = startorder; + } else { + sigrenderer->restart_position = sigdata->restart_position; + } + + sigrenderer->row = 0; + sigrenderer->processorder = startorder - 1; + sigrenderer->tick = 1; + +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->played = bit_array_create(sigdata->n_orders * 256); +#endif + + { + int order; + for (order = 0; order < sigdata->n_orders; order++) { + int n = sigdata->order[order]; + if (n < sigdata->n_patterns) goto found_valid_order; +#ifdef INVALID_ORDERS_END_SONG + if (n != IT_ORDER_SKIP) +#else + if (n == IT_ORDER_END) +#endif + break; + +#ifdef BIT_ARRAY_BULLSHIT + /* Fix for played order detection for songs which have skips at the start of the orders list */ + for (n = 0; n < 256; n++) { + bit_array_set(sigrenderer->played, order * 256 + n); + } +#endif + } + /* If we get here, there were no valid orders in the song. */ + _dumb_it_end_sigrenderer(sigrenderer); + return NULL; + } + found_valid_order: + + sigrenderer->time_left = 0; + sigrenderer->sub_time_left = 0; + +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->played = bit_array_create(sigdata->n_orders * 256); +#endif + + sigrenderer->gvz_time = 0; + sigrenderer->gvz_sub_time = 0; + + //sigrenderer->max_output = 0; + + if ( !(sigdata->flags & IT_WAS_PROCESSED) ) { + dumb_it_add_lpc( sigdata ); + + sigdata->flags |= IT_WAS_PROCESSED; + } + + return sigrenderer; +} + + +void DUMBEXPORT dumb_it_set_resampling_quality(DUMB_IT_SIGRENDERER * sigrenderer, int quality) +{ + if (sigrenderer && quality >= 0 && quality < DUMB_RQ_N_LEVELS) + { + int i; + sigrenderer->resampling_quality = quality; + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing) + { + IT_PLAYING * playing = sigrenderer->channel[i].playing; + playing->resampling_quality = quality; + playing->resampler.quality = quality; + resampler_set_quality(playing->resampler.fir_resampler[0], quality - DUMB_RESAMPLER_BASE); + resampler_set_quality(playing->resampler.fir_resampler[1], quality - DUMB_RESAMPLER_BASE); + } + } + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { + IT_PLAYING * playing = sigrenderer->playing[i]; + playing->resampling_quality = quality; + playing->resampler.quality = quality; + resampler_set_quality(playing->resampler.fir_resampler[0], quality - DUMB_RESAMPLER_BASE); + resampler_set_quality(playing->resampler.fir_resampler[1], quality - DUMB_RESAMPLER_BASE); + } + } + } +} + + +void DUMBEXPORT dumb_it_set_ramp_style(DUMB_IT_SIGRENDERER * sigrenderer, int ramp_style) { + if (sigrenderer && ramp_style >= 0 && ramp_style <= 2) { + sigrenderer->ramp_style = ramp_style; + } +} + + +void DUMBEXPORT dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (DUMBCALLBACK *callback)(void *data), void *data) +{ + if (sigrenderer) { + sigrenderer->callbacks->loop = callback; + sigrenderer->callbacks->loop_data = data; + } +} + + + +void DUMBEXPORT dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (DUMBCALLBACK *callback)(void *data), void *data) +{ + if (sigrenderer) { + sigrenderer->callbacks->xm_speed_zero = callback; + sigrenderer->callbacks->xm_speed_zero_data = data; + } +} + + + +void DUMBEXPORT dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (DUMBCALLBACK *callback)(void *data, int channel, unsigned char midi_byte), void *data) +{ + if (sigrenderer) { + sigrenderer->callbacks->midi = callback; + sigrenderer->callbacks->midi_data = data; + } +} + + + +void DUMBEXPORT dumb_it_set_global_volume_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (DUMBCALLBACK *callback)(void *data), void *data) +{ + if (sigrenderer) { + sigrenderer->callbacks->global_volume_zero = callback; + sigrenderer->callbacks->global_volume_zero_data = data; + } +} + + + +static IT_CALLBACKS *create_callbacks(void) +{ + IT_CALLBACKS *callbacks = malloc(sizeof(*callbacks)); + if (!callbacks) return NULL; + callbacks->loop = NULL; + callbacks->xm_speed_zero = NULL; + callbacks->midi = NULL; + callbacks->global_volume_zero = NULL; + return callbacks; +} + + + +static DUMB_IT_SIGRENDERER *dumb_it_init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_channels, int startorder) +{ + IT_CALLBACKS *callbacks; + + if (!sigdata) return NULL; + + callbacks = create_callbacks(); + if (!callbacks) return NULL; + + return init_sigrenderer(sigdata, n_channels, startorder, callbacks, + dumb_create_click_remover_array(n_channels)); +} + + + +DUH_SIGRENDERER *DUMBEXPORT dumb_it_start_at_order(DUH *duh, int n_channels, int startorder) +{ + DUMB_IT_SIGDATA *itsd = duh_get_it_sigdata(duh); + DUMB_IT_SIGRENDERER *itsr = dumb_it_init_sigrenderer(itsd, n_channels, startorder); + /*duh->length = dumb_it_build_checkpoints(itsd, startorder);*/ + return duh_encapsulate_it_sigrenderer(itsr, n_channels, 0); +} + + + +static sigrenderer_t *it_start_sigrenderer(DUH *duh, sigdata_t *vsigdata, int n_channels, int32 pos) +{ + DUMB_IT_SIGDATA *sigdata = vsigdata; + DUMB_IT_SIGRENDERER *sigrenderer; + + (void)duh; + + { + IT_CALLBACKS *callbacks = create_callbacks(); + if (!callbacks) return NULL; + + if (sigdata->checkpoint) { + IT_CHECKPOINT *checkpoint = sigdata->checkpoint; + while (checkpoint->next && checkpoint->next->time < pos) + checkpoint = checkpoint->next; + sigrenderer = dup_sigrenderer(checkpoint->sigrenderer, n_channels, callbacks); + if (!sigrenderer) return NULL; + sigrenderer->click_remover = dumb_create_click_remover_array(n_channels); + pos -= checkpoint->time; + } else { + sigrenderer = init_sigrenderer(sigdata, n_channels, 0, callbacks, + dumb_create_click_remover_array(n_channels)); + if (!sigrenderer) return NULL; + } + } + + while (pos > 0 && pos >= sigrenderer->time_left) { + render(sigrenderer, 0, 1.0f, 0, sigrenderer->time_left, NULL); + + pos -= sigrenderer->time_left; + sigrenderer->time_left = 0; + + if (process_tick(sigrenderer)) { + _dumb_it_end_sigrenderer(sigrenderer); + return NULL; + } + } + + render(sigrenderer, 0, 1.0f, 0, pos, NULL); + sigrenderer->time_left -= pos; + + return sigrenderer; +} + + + +static int32 it_sigrenderer_get_samples( + sigrenderer_t *vsigrenderer, + double volume, double delta, + int32 size, sample_t **samples +) +{ + DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; + int32 pos; + int dt; + int32 todo; + LONG_LONG t; + + if (sigrenderer->order < 0) return 0; // problematic + + pos = 0; + dt = (int)(delta * 65536.0f + 0.5f); + + /* When samples is finally used in render_playing(), it won't be used if + * volume is 0. + */ + if (!samples) volume = 0; + + for (;;) { + todo = (long)((((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left) / dt); + + if (todo >= size) + break; + + render(sigrenderer, volume, delta, pos, todo, samples); + + pos += todo; + size -= todo; + + t = sigrenderer->sub_time_left - (LONG_LONG)todo * dt; + sigrenderer->sub_time_left = (int32)t & 65535; + sigrenderer->time_left += (int32)(t >> 16); + + if (process_tick(sigrenderer)) { + sigrenderer->order = -1; + sigrenderer->row = -1; + return pos; + } + } + + render(sigrenderer, volume, delta, pos, size, samples); + + pos += size; + + t = sigrenderer->sub_time_left - (LONG_LONG)size * dt; + sigrenderer->sub_time_left = (int32)t & 65535; + sigrenderer->time_left += (int32)(t >> 16); + + if (samples) + dumb_remove_clicks_array(sigrenderer->n_channels, sigrenderer->click_remover, samples, pos, 512.0f / delta); + + return pos; +} + + + +static void it_sigrenderer_get_current_sample(sigrenderer_t *vsigrenderer, double volume, sample_t *samples) +{ + DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; + (void)volume; // for consideration: in any of these such functions, is 'volume' going to be required? + dumb_click_remover_get_offset_array(sigrenderer->n_channels, sigrenderer->click_remover, samples); +} + + + +void _dumb_it_end_sigrenderer(sigrenderer_t *vsigrenderer) +{ + DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; + + int i; + + if (sigrenderer) { + IT_PLAYING *playing, *next; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing) + free_playing_orig(sigrenderer->channel[i].playing); +#ifdef BIT_ARRAY_BULLSHIT + bit_array_destroy(sigrenderer->channel[i].played_patjump); +#endif + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + if (sigrenderer->playing[i]) + free_playing_orig(sigrenderer->playing[i]); + + for (playing = sigrenderer->free_playing; playing != NULL; playing = next) + { + next = playing->next; + free_playing_orig(playing); + } + + dumb_destroy_click_remover_array(sigrenderer->n_channels, sigrenderer->click_remover); + + if (sigrenderer->callbacks) + free(sigrenderer->callbacks); + +#ifdef BIT_ARRAY_BULLSHIT + bit_array_destroy(sigrenderer->played); +#endif + + free(vsigrenderer); + } +} + + + +DUH_SIGTYPE_DESC _dumb_sigtype_it = { + SIGTYPE_IT, + NULL, + &it_start_sigrenderer, + NULL, + &it_sigrenderer_get_samples, + &it_sigrenderer_get_current_sample, + &_dumb_it_end_sigrenderer, + &_dumb_it_unload_sigdata +}; + + + +DUH_SIGRENDERER *DUMBEXPORT duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, int32 pos) +{ + return duh_encapsulate_raw_sigrenderer(it_sigrenderer, &_dumb_sigtype_it, n_channels, pos); +} + + + +DUMB_IT_SIGRENDERER *DUMBEXPORT duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer) +{ + return duh_get_raw_sigrenderer(sigrenderer, SIGTYPE_IT); +} + + + +/* Values of 64 or more will access NNA channels here. */ +void DUMBEXPORT dumb_it_sr_get_channel_state(DUMB_IT_SIGRENDERER *sr, int channel, DUMB_IT_CHANNEL_STATE *state) +{ + IT_PLAYING *playing; + int t; /* temporary var for holding accurate pan and filter cutoff */ + double delta; + ASSERT(channel < DUMB_IT_TOTAL_CHANNELS); + if (!sr) { state->sample = 0; return; } + if (channel >= DUMB_IT_N_CHANNELS) { + playing = sr->playing[channel - DUMB_IT_N_CHANNELS]; + if (!playing) { state->sample = 0; return; } + } else { + playing = sr->channel[channel].playing; + if (!playing) { state->sample = 0; return; } + } + + if (playing->flags & IT_PLAYING_DEAD) { state->sample = 0; return; } + + state->channel = (int)(playing->channel - sr->channel); + state->sample = playing->sampnum; + state->volume = calculate_volume(sr, playing, 1.0f); + + t = apply_pan_envelope(playing); + state->pan = (unsigned char)((t + 128) >> IT_ENVELOPE_SHIFT); + state->subpan = (signed char)t; + + delta = playing->delta * 65536.0f; + t = playing->filter_cutoff << IT_ENVELOPE_SHIFT; + apply_pitch_modifications(sr->sigdata, playing, &delta, &t); + state->freq = (int)delta; + if (t == 127 << IT_ENVELOPE_SHIFT && playing->filter_resonance == 0) { + state->filter_resonance = playing->true_filter_resonance; + t = playing->true_filter_cutoff; + } else + state->filter_resonance = playing->filter_resonance; + state->filter_cutoff = (unsigned char)(t >> 8); + state->filter_subcutoff = (unsigned char)t; +} + + + +int DUMBCALLBACK dumb_it_callback_terminate(void *data) +{ + (void)data; + return 1; +} + + + +int DUMBCALLBACK dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte) +{ + (void)data; + (void)channel; + (void)midi_byte; + return 1; +} + + + +#define IT_CHECKPOINT_INTERVAL (30 * 65536) /* Half a minute */ + +#define FUCKIT_THRESHOLD (120 * 60 * 65536) /* two hours? probably a pattern loop mess... */ + +/* Returns the length of the module, up until it first loops. */ +int32 DUMBEXPORT dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder) +{ + IT_CHECKPOINT *checkpoint; + if (!sigdata) return 0; + checkpoint = sigdata->checkpoint; + while (checkpoint) { + IT_CHECKPOINT *next = checkpoint->next; + _dumb_it_end_sigrenderer(checkpoint->sigrenderer); + free(checkpoint); + checkpoint = next; + } + sigdata->checkpoint = NULL; + checkpoint = malloc(sizeof(*checkpoint)); + if (!checkpoint) return 0; + checkpoint->time = 0; + checkpoint->sigrenderer = dumb_it_init_sigrenderer(sigdata, 0, startorder); + if (!checkpoint->sigrenderer) { + free(checkpoint); + return 0; + } + checkpoint->sigrenderer->callbacks->loop = &dumb_it_callback_terminate; + checkpoint->sigrenderer->callbacks->xm_speed_zero = &dumb_it_callback_terminate; + checkpoint->sigrenderer->callbacks->global_volume_zero = &dumb_it_callback_terminate; + + if (sigdata->checkpoint) + { + IT_CHECKPOINT *checkpoint = sigdata->checkpoint; + while (checkpoint) { + IT_CHECKPOINT *next = checkpoint->next; + _dumb_it_end_sigrenderer(checkpoint->sigrenderer); + free(checkpoint); + checkpoint = next; + } + } + + sigdata->checkpoint = checkpoint; + + for (;;) { + int32 l; + DUMB_IT_SIGRENDERER *sigrenderer = dup_sigrenderer(checkpoint->sigrenderer, 0, checkpoint->sigrenderer->callbacks); + checkpoint->sigrenderer->callbacks = NULL; + if (!sigrenderer) { + checkpoint->next = NULL; + return checkpoint->time; + } + + l = it_sigrenderer_get_samples(sigrenderer, 0, 1.0f, IT_CHECKPOINT_INTERVAL, NULL); + if (l < IT_CHECKPOINT_INTERVAL) { + _dumb_it_end_sigrenderer(sigrenderer); + checkpoint->next = NULL; + return checkpoint->time + l; + } + + checkpoint->next = malloc(sizeof(*checkpoint->next)); + if (!checkpoint->next) { + _dumb_it_end_sigrenderer(sigrenderer); + return checkpoint->time + IT_CHECKPOINT_INTERVAL; + } + + checkpoint->next->time = checkpoint->time + IT_CHECKPOINT_INTERVAL; + checkpoint = checkpoint->next; + checkpoint->sigrenderer = sigrenderer; + + if (checkpoint->time >= FUCKIT_THRESHOLD) { + checkpoint->next = NULL; + return 0; + } + } +} + + + +void DUMBEXPORT dumb_it_do_initial_runthrough(DUH *duh) +{ + if (duh) { + DUMB_IT_SIGDATA *sigdata = duh_get_it_sigdata(duh); + + if (sigdata) + duh_set_length(duh, dumb_it_build_checkpoints(sigdata, 0)); + } +} + +static int is_pattern_silent(IT_PATTERN * pattern, int order) { + int ret = 1; + IT_ENTRY * entry, * end; + if (!pattern || !pattern->n_rows || !pattern->n_entries || !pattern->entry) return 2; + + if ( pattern->n_entries == pattern->n_rows ) { + int n; + entry = pattern->entry; + for ( n = 0; n < pattern->n_entries; ++n, ++entry ) { + if ( !IT_IS_END_ROW(entry) ) break; + } + if ( n == pattern->n_entries ) return 2; + // broken? + } + + entry = pattern->entry; + end = entry + pattern->n_entries; + + while (entry < end) { + if (!IT_IS_END_ROW(entry)) { + if (entry->mask & (IT_ENTRY_INSTRUMENT | IT_ENTRY_VOLPAN)) + return 0; + if (entry->mask & IT_ENTRY_NOTE && entry->note < 120) + return 0; + if (entry->mask & IT_ENTRY_EFFECT) { + switch (entry->effect) { + case IT_SET_GLOBAL_VOLUME: + if (entry->effectvalue) return 0; + break; + + case IT_SET_SPEED: + if (entry->effectvalue > 64) ret++; + break; + + case IT_SET_SONG_TEMPO: + case IT_XM_KEY_OFF: + break; + + case IT_JUMP_TO_ORDER: + if (entry->effectvalue != order) + return 0; + break; + + case IT_S: + switch (entry->effectvalue >> 4) { + case 0: // meh bastard + if ( entry->effectvalue != 0 ) return 0; + break; + + case IT_S_FINE_PATTERN_DELAY: + case IT_S_PATTERN_LOOP: + case IT_S_PATTERN_DELAY: + ret++; + break; + + case IT_S7: + if ((entry->effectvalue & 15) > 2) + return 0; + break; + + default: + return 0; + } + break; + + // clever idiot with his S L O W crap; do nothing + case IT_VOLSLIDE_TONEPORTA: + case IT_SET_SAMPLE_OFFSET: + case IT_GLOBAL_VOLUME_SLIDE: + if ( entry->effectvalue != 0 ) return 0; + break; + + // genius also uses this instead of jump to order by mistake, meh, and it's bloody BCD + case IT_BREAK_TO_ROW: + if ( ( ( entry->effectvalue >> 4 ) * 10 + ( entry->effectvalue & 15 ) ) != order ) return 0; + break; + + default: + return 0; + } + } + } + entry++; + } + + return ret; +} + +int DUMBEXPORT dumb_it_trim_silent_patterns(DUH * duh) { + int n; + DUMB_IT_SIGDATA *sigdata; + + if (!duh) return -1; + + sigdata = duh_get_it_sigdata(duh); + + if (!sigdata || !sigdata->order || !sigdata->pattern) return -1; + + for (n = 0; n < sigdata->n_orders; n++) { + int p = sigdata->order[n]; + if (p < sigdata->n_patterns) { + IT_PATTERN * pattern = &sigdata->pattern[p]; + if (is_pattern_silent(pattern, n) > 1) { + pattern->n_rows = 1; + pattern->n_entries = 0; + if (pattern->entry) + { + free(pattern->entry); + pattern->entry = NULL; + } + } else + break; + } + } + + if (n == sigdata->n_orders) return -1; + + for (n = sigdata->n_orders - 1; n >= 0; n--) { + int p = sigdata->order[n]; + if (p < sigdata->n_patterns) { + IT_PATTERN * pattern = &sigdata->pattern[p]; + if (is_pattern_silent(pattern, n) > 1) { + pattern->n_rows = 1; + pattern->n_entries = 0; + if (pattern->entry) + { + free(pattern->entry); + pattern->entry = NULL; + } + } else + break; + } + } + + if (n < 0) return -1; + + /*duh->length = dumb_it_build_checkpoints(sigdata, 0);*/ + + return 0; +} + +int DUMBEXPORT dumb_it_scan_for_playable_orders(DUMB_IT_SIGDATA *sigdata, dumb_scan_callback callback, void * callback_data) +{ + int n; + int32 length; + void * ba_played; + DUMB_IT_SIGRENDERER * sigrenderer; + + if (!sigdata->n_orders || !sigdata->order) return -1; + + ba_played = bit_array_create(sigdata->n_orders * 256); + if (!ba_played) return -1; + + /* Skip the first order, it should always be played */ + for (n = 1; n < sigdata->n_orders; n++) { + if ((sigdata->order[n] >= sigdata->n_patterns) || + (is_pattern_silent(&sigdata->pattern[sigdata->order[n]], n) > 1)) + bit_array_set(ba_played, n * 256); + } + + for (;;) { + for (n = 0; n < sigdata->n_orders; n++) { + if (!bit_array_test_range(ba_played, n * 256, 256)) break; + } + + if (n == sigdata->n_orders) break; + + sigrenderer = dumb_it_init_sigrenderer(sigdata, 0, n); + if (!sigrenderer) { + bit_array_destroy(ba_played); + return -1; + } + sigrenderer->callbacks->loop = &dumb_it_callback_terminate; + sigrenderer->callbacks->xm_speed_zero = &dumb_it_callback_terminate; + sigrenderer->callbacks->global_volume_zero = &dumb_it_callback_terminate; + + length = 0; + + for (;;) { + int32 l; + + l = it_sigrenderer_get_samples(sigrenderer, 0, 1.0f, IT_CHECKPOINT_INTERVAL, NULL); + length += l; + if (l < IT_CHECKPOINT_INTERVAL || length >= FUCKIT_THRESHOLD) { + /* SONG OVA! */ + break; + } + } + + if ((*callback)(callback_data, n, length) < 0) return -1; + + bit_array_merge(ba_played, sigrenderer->played, 0); + + _dumb_it_end_sigrenderer(sigrenderer); + } + + bit_array_destroy(ba_played); + + return 0; +} diff --git a/thirdparty/dumb/src/it/itunload.c b/thirdparty/dumb/src/it/itunload.c new file mode 100644 index 000000000..efed192a6 --- /dev/null +++ b/thirdparty/dumb/src/it/itunload.c @@ -0,0 +1,72 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itunload.c - Code to free an Impulse Tracker / / \ \ + * module from memory. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/it.h" + + + +void _dumb_it_unload_sigdata(sigdata_t *vsigdata) +{ + if (vsigdata) { + DUMB_IT_SIGDATA *sigdata = vsigdata; + int n; + + if (sigdata->song_message) + free(sigdata->song_message); + + if (sigdata->order) + free(sigdata->order); + + if (sigdata->instrument) + free(sigdata->instrument); + + if (sigdata->sample) { + for (n = 0; n < sigdata->n_samples; n++) + if (sigdata->sample[n].data) + free(sigdata->sample[n].data); + + free(sigdata->sample); + } + + if (sigdata->pattern) { + for (n = 0; n < sigdata->n_patterns; n++) + if (sigdata->pattern[n].entry) + free(sigdata->pattern[n].entry); + free(sigdata->pattern); + } + + if (sigdata->midi) + free(sigdata->midi); + + { + IT_CHECKPOINT *checkpoint = sigdata->checkpoint; + while (checkpoint) { + IT_CHECKPOINT *next = checkpoint->next; + _dumb_it_end_sigrenderer(checkpoint->sigrenderer); + free(checkpoint); + checkpoint = next; + } + } + + free(vsigdata); + } +} diff --git a/thirdparty/dumb/src/it/load669.c b/thirdparty/dumb/src/it/load669.c new file mode 100644 index 000000000..38343be29 --- /dev/null +++ b/thirdparty/dumb/src/it/load669.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmod.c - Code to read a 669 Composer module / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_669_quick(): loads a 669 file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_669_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_669_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/load6692.c b/thirdparty/dumb/src/it/load6692.c new file mode 100644 index 000000000..1f41c7aa0 --- /dev/null +++ b/thirdparty/dumb/src/it/load6692.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmod2.c - Code to read a 669 Composer module / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_669(): loads a 669 file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_669(const char *filename) +{ + DUH *duh = dumb_load_669_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadamf.c b/thirdparty/dumb/src/it/loadamf.c new file mode 100644 index 000000000..2be50f7f5 --- /dev/null +++ b/thirdparty/dumb/src/it/loadamf.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadamf.c - Code to read a DSMI AMF module file, / / \ \ + * opening and closing it for you. | < / \_ + * | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_amf_quick(): loads a AMF file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_amf_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_amf_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadamf2.c b/thirdparty/dumb/src/it/loadamf2.c new file mode 100644 index 000000000..83ed76810 --- /dev/null +++ b/thirdparty/dumb/src/it/loadamf2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadamf2.c - Code to read a DSMI AMF module file, / / \ \ + * opening and closing it for you, and | < / \_ + * do an initial run-through. | \/ /\ / + * \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_amf(): loads a AMF file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_amf(const char *filename) +{ + DUH *duh = dumb_load_amf_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadany.c b/thirdparty/dumb/src/it/loadany.c new file mode 100644 index 000000000..910e86a77 --- /dev/null +++ b/thirdparty/dumb/src/it/loadany.c @@ -0,0 +1,38 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadany.c - Code to detect and read any of the / / \ \ + * module formats supported by DUMB, | < / \_ + * opening and closing the file for you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +DUH *DUMBEXPORT dumb_load_any_quick(const char *filename, int restrict_, int subsong) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_any_quick(f, restrict_, subsong); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadany2.c b/thirdparty/dumb/src/it/loadany2.c new file mode 100644 index 000000000..71590a0bf --- /dev/null +++ b/thirdparty/dumb/src/it/loadany2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadany2.c - Code to detect and read any of the / / \ \ + * module formats supported by DUMB, | < / \_ + * opening and closing the file for | \/ /\ / + * you, and do an initial run-through. \_ / > / + * | \ / / + * by Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_load_any(const char *filename, int restrict_, int subsong) +{ + DUH *duh = dumb_load_any_quick(filename, restrict_, subsong); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadasy.c b/thirdparty/dumb/src/it/loadasy.c new file mode 100644 index 000000000..5e9b2dd1d --- /dev/null +++ b/thirdparty/dumb/src/it/loadasy.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadasy.c - Code to read an ASYLUM Music Format / / \ \ + * module file, opening and closing it | < / \_ + * for you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_asy_quick(): loads a AMF file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_asy_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_asy_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadasy2.c b/thirdparty/dumb/src/it/loadasy2.c new file mode 100644 index 000000000..ecbc1ecbd --- /dev/null +++ b/thirdparty/dumb/src/it/loadasy2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadasy2.c - Code to read an ASYLUM Music Format / / \ \ + * module file, opening and closing it | < / \_ + * for you, and do an initial run- | \/ /\ / + * through. \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_asy(): loads a AMF file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_asy(const char *filename) +{ + DUH *duh = dumb_load_asy_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadmod.c b/thirdparty/dumb/src/it/loadmod.c new file mode 100644 index 000000000..c2239ccb2 --- /dev/null +++ b/thirdparty/dumb/src/it/loadmod.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmod.c - Code to read a good old-fashioned / / \ \ + * Amiga module file, opening and | < / \_ + * closing it for you. | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_mod_quick(): loads a MOD file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_mod_quick(const char *filename, int restrict_) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_mod_quick(f, restrict_); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadmod2.c b/thirdparty/dumb/src/it/loadmod2.c new file mode 100644 index 000000000..1051f1a8d --- /dev/null +++ b/thirdparty/dumb/src/it/loadmod2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmod2.c - Function to read a good old- / / \ \ + * fashioned Amiga module file, | < / \_ + * opening and closing it for you, | \/ /\ / + * and do an initial run-through. \_ / > / + * | \ / / + * Split off from loadmod.c by entheh. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_load_mod(const char *filename, int restrict_) +{ + DUH *duh = dumb_load_mod_quick(filename, restrict_); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadmtm.c b/thirdparty/dumb/src/it/loadmtm.c new file mode 100644 index 000000000..5ce44249b --- /dev/null +++ b/thirdparty/dumb/src/it/loadmtm.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmtm.c - Code to read a MultiTracker Module / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_mtm_quick(): loads a MTM file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_mtm_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_mtm_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadmtm2.c b/thirdparty/dumb/src/it/loadmtm2.c new file mode 100644 index 000000000..13d303203 --- /dev/null +++ b/thirdparty/dumb/src/it/loadmtm2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmtm2.c - Code to read a MultiTracker Module / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_mtm(): loads a MTM file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_mtm(const char *filename) +{ + DUH *duh = dumb_load_mtm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadokt.c b/thirdparty/dumb/src/it/loadokt.c new file mode 100644 index 000000000..b1c73b8af --- /dev/null +++ b/thirdparty/dumb/src/it/loadokt.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadokt.c - Code to read an Oktalyzer module / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_okt_quick(): loads an OKT file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_okt_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_okt_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadokt2.c b/thirdparty/dumb/src/it/loadokt2.c new file mode 100644 index 000000000..f58da163a --- /dev/null +++ b/thirdparty/dumb/src/it/loadokt2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadokt2.c - Function to read an Oktalyzer / / \ \ + * module file, opening and closing | < / \_ + * it for you, and do an initial run- | \/ /\ / + * through. \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_load_okt(const char *filename) +{ + DUH *duh = dumb_load_okt_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadoldpsm.c b/thirdparty/dumb/src/it/loadoldpsm.c new file mode 100644 index 000000000..2460d871a --- /dev/null +++ b/thirdparty/dumb/src/it/loadoldpsm.c @@ -0,0 +1,43 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadoldpsm.c - Code to read a ProTracker Studio / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_old_psm_quick(): loads an old PSM file into a DUH struct, + * returning a pointer to the DUH struct. When you have finished with it, + * you must pass the pointer to unload_duh() so that the memory can be + * freed. + */ +DUH *DUMBEXPORT dumb_load_old_psm_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_old_psm_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadoldpsm2.c b/thirdparty/dumb/src/it/loadoldpsm2.c new file mode 100644 index 000000000..edd10db56 --- /dev/null +++ b/thirdparty/dumb/src/it/loadoldpsm2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadoldpsm2.c - Code to read a ProTracker Studio / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run- | \/ /\ / + * through. \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_old_psm(): loads an old PSM file into a DUH struct, returning + * a pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_old_psm(const char *filename) +{ + DUH *duh = dumb_load_old_psm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadpsm.c b/thirdparty/dumb/src/it/loadpsm.c new file mode 100644 index 000000000..7e2405c61 --- /dev/null +++ b/thirdparty/dumb/src/it/loadpsm.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadpsm.c - Code to read a ProTracker Studio / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_psm_quick(): loads a PSM file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_psm_quick(const char *filename, int subsong) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_psm_quick(f, subsong); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadpsm2.c b/thirdparty/dumb/src/it/loadpsm2.c new file mode 100644 index 000000000..c4b5132ff --- /dev/null +++ b/thirdparty/dumb/src/it/loadpsm2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadpsm2.c - Code to read a ProTracker Studio / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_psm(): loads a PSM file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_psm(const char *filename, int subsong) +{ + DUH *duh = dumb_load_psm_quick(filename, subsong); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadptm.c b/thirdparty/dumb/src/it/loadptm.c new file mode 100644 index 000000000..1ff066b45 --- /dev/null +++ b/thirdparty/dumb/src/it/loadptm.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadptm.c - Code to read a Poly Tracker v2.03 / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_ptm_quick(): loads a PTM file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_ptm_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_ptm_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadptm2.c b/thirdparty/dumb/src/it/loadptm2.c new file mode 100644 index 000000000..3e50735d0 --- /dev/null +++ b/thirdparty/dumb/src/it/loadptm2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadptm2.c - Code to read a Poly Tracker v2.03 / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_ptm(): loads a PTM file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_ptm(const char *filename) +{ + DUH *duh = dumb_load_ptm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadriff.c b/thirdparty/dumb/src/it/loadriff.c new file mode 100644 index 000000000..84a8a4358 --- /dev/null +++ b/thirdparty/dumb/src/it/loadriff.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadriff.c - Code to read a RIFF module file / / \ \ + * opening and closing it for you. | < / \_ + * | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_riff_quick(): loads a RIFF file into a DUH struct, returning + * a pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_riff_quick( const char *filename ) +{ + DUH * duh; + DUMBFILE * f = dumbfile_open( filename ); + + if ( ! f ) + return NULL; + + duh = dumb_read_riff_quick( f ); + + dumbfile_close( f ); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadriff2.c b/thirdparty/dumb/src/it/loadriff2.c new file mode 100644 index 000000000..53466f1a5 --- /dev/null +++ b/thirdparty/dumb/src/it/loadriff2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadriff2.c - Code to read a RIFF module file / / \ \ + * opening and closing it for you, | < / \_ + * and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_load_riff(const char *filename) +{ + DUH *duh = dumb_load_riff_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loads3m.c b/thirdparty/dumb/src/it/loads3m.c new file mode 100644 index 000000000..09deb0f26 --- /dev/null +++ b/thirdparty/dumb/src/it/loads3m.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loads3m.c - Code to read a ScreamTracker 3 / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_s3m_quick(): loads an S3M file into a DUH struct, returning + * a pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_s3m_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_s3m_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loads3m2.c b/thirdparty/dumb/src/it/loads3m2.c new file mode 100644 index 000000000..7907775a8 --- /dev/null +++ b/thirdparty/dumb/src/it/loads3m2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loads3m2.c - Function to read a ScreamTracker 3 / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * Split off from loads3m.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_load_s3m(const char *filename) +{ + DUH *duh = dumb_load_s3m_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadstm.c b/thirdparty/dumb/src/it/loadstm.c new file mode 100644 index 000000000..2a533adb3 --- /dev/null +++ b/thirdparty/dumb/src/it/loadstm.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadstm.c - Code to read a ScreamTracker 2 / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_stm_quick(): loads an STM file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_stm_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_stm_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadstm2.c b/thirdparty/dumb/src/it/loadstm2.c new file mode 100644 index 000000000..491542bf3 --- /dev/null +++ b/thirdparty/dumb/src/it/loadstm2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadstm2.c - Function to read a ScreamTracker 2 / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_load_stm(const char *filename) +{ + DUH *duh = dumb_load_stm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/loadxm.c b/thirdparty/dumb/src/it/loadxm.c new file mode 100644 index 000000000..98ccd9301 --- /dev/null +++ b/thirdparty/dumb/src/it/loadxm.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadxm.c - Code to read a Fast Tracker II / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_xm_quick(): loads an XM file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_load_xm_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_xm_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/thirdparty/dumb/src/it/loadxm2.c b/thirdparty/dumb/src/it/loadxm2.c new file mode 100644 index 000000000..61459b5b8 --- /dev/null +++ b/thirdparty/dumb/src/it/loadxm2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadxm2.c - Function to read a Fast Tracker II / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * Split off from loadxm.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_load_xm(const char *filename) +{ + DUH *duh = dumb_load_xm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/ptmeffect.c b/thirdparty/dumb/src/it/ptmeffect.c new file mode 100644 index 000000000..cbc2e90cf --- /dev/null +++ b/thirdparty/dumb/src/it/ptmeffect.c @@ -0,0 +1,125 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * ptmeffect.c - Code for converting PTM / / \ \ + * effects to IT effects. | < / \_ + * | \/ /\ / + * By Chris Moeller. Based on xmeffect.c \_ / > / + * by Julien Cugniere. | \ / / + * | ' / + * \__/ + */ + + + +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +void _dumb_it_ptm_convert_effect(int effect, int value, IT_ENTRY *entry) +{ + if (effect >= PTM_N_EFFECTS) + return; + + /* Linearisation of the effect number... */ + if (effect == PTM_E) { + effect = PTM_EBASE + HIGH(value); + value = LOW(value); + } + + /* convert effect */ + entry->mask |= IT_ENTRY_EFFECT; + switch (effect) { + + case PTM_APPREGIO: effect = IT_ARPEGGIO; break; + case PTM_PORTAMENTO_UP: effect = IT_PORTAMENTO_UP; break; + case PTM_PORTAMENTO_DOWN: effect = IT_PORTAMENTO_DOWN; break; + case PTM_TONE_PORTAMENTO: effect = IT_TONE_PORTAMENTO; break; + case PTM_VIBRATO: effect = IT_VIBRATO; break; + case PTM_VOLSLIDE_TONEPORTA: effect = IT_VOLSLIDE_TONEPORTA; break; + case PTM_VOLSLIDE_VIBRATO: effect = IT_VOLSLIDE_VIBRATO; break; + case PTM_TREMOLO: effect = IT_TREMOLO; break; + case PTM_SAMPLE_OFFSET: effect = IT_SET_SAMPLE_OFFSET; break; + case PTM_VOLUME_SLIDE: effect = IT_VOLUME_SLIDE; break; + case PTM_POSITION_JUMP: effect = IT_JUMP_TO_ORDER; break; + case PTM_SET_CHANNEL_VOLUME: effect = IT_SET_CHANNEL_VOLUME; break; + case PTM_PATTERN_BREAK: effect = IT_BREAK_TO_ROW; break; + case PTM_SET_GLOBAL_VOLUME: effect = IT_SET_GLOBAL_VOLUME; break; + case PTM_RETRIGGER: effect = IT_RETRIGGER_NOTE; break; + case PTM_FINE_VIBRATO: effect = IT_FINE_VIBRATO; break; + + /* TODO properly */ + case PTM_NOTE_SLIDE_UP: effect = IT_PTM_NOTE_SLIDE_UP; break; + case PTM_NOTE_SLIDE_DOWN: effect = IT_PTM_NOTE_SLIDE_DOWN; break; + case PTM_NOTE_SLIDE_UP_RETRIG: effect = IT_PTM_NOTE_SLIDE_UP_RETRIG; break; + case PTM_NOTE_SLIDE_DOWN_RETRIG: effect = IT_PTM_NOTE_SLIDE_DOWN_RETRIG; break; + + case PTM_SET_TEMPO_BPM: + effect = (value < 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO); + break; + + case PTM_EBASE+PTM_E_SET_FINETUNE: effect = SBASE+IT_S_FINETUNE; break; /** TODO */ + case PTM_EBASE+PTM_E_SET_LOOP: effect = SBASE+IT_S_PATTERN_LOOP; break; + case PTM_EBASE+PTM_E_NOTE_CUT: effect = SBASE+IT_S_DELAYED_NOTE_CUT; break; + case PTM_EBASE+PTM_E_NOTE_DELAY: effect = SBASE+IT_S_NOTE_DELAY; break; + case PTM_EBASE+PTM_E_PATTERN_DELAY: effect = SBASE+IT_S_PATTERN_DELAY; break; + case PTM_EBASE+PTM_E_SET_PANNING: effect = SBASE+IT_S_SET_PAN; break; + + case PTM_EBASE+PTM_E_FINE_VOLSLIDE_UP: + effect = IT_VOLUME_SLIDE; + value = EFFECT_VALUE(value, 0xF); + break; + + case PTM_EBASE + PTM_E_FINE_VOLSLIDE_DOWN: + effect = IT_VOLUME_SLIDE; + value = EFFECT_VALUE(0xF, value); + break; + + case PTM_EBASE + PTM_E_FINE_PORTA_UP: + effect = IT_PORTAMENTO_UP; + value = EFFECT_VALUE(0xF, value); + break; + + case PTM_EBASE + PTM_E_FINE_PORTA_DOWN: + effect = IT_PORTAMENTO_DOWN; + value = EFFECT_VALUE(0xF, value); + break; + + case PTM_EBASE + PTM_E_RETRIG_NOTE: + effect = IT_XM_RETRIGGER_NOTE; + value = EFFECT_VALUE(0, value); + break; + + case PTM_EBASE + PTM_E_SET_VIBRATO_CONTROL: + effect = SBASE+IT_S_SET_VIBRATO_WAVEFORM; + value &= ~4; /** TODO: value&4 -> don't retrig wave */ + break; + + case PTM_EBASE + PTM_E_SET_TREMOLO_CONTROL: + effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM; + value &= ~4; /** TODO: value&4 -> don't retrig wave */ + break; + + default: + /* user effect (often used in demos for synchronisation) */ + entry->mask &= ~IT_ENTRY_EFFECT; + } + + /* Inverse linearisation... */ + if (effect >= SBASE && effect < SBASE+16) { + value = EFFECT_VALUE(effect-SBASE, value); + effect = IT_S; + } + + entry->effect = effect; + entry->effectvalue = value; +} diff --git a/thirdparty/dumb/src/it/read669.c b/thirdparty/dumb/src/it/read669.c new file mode 100644 index 000000000..53332b497 --- /dev/null +++ b/thirdparty/dumb/src/it/read669.c @@ -0,0 +1,448 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * read669.c - Code to read a 669 Composer module / / \ \ + * from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static int it_669_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int tempo, int breakpoint, unsigned char *buffer, int * used_channels) +{ + int pos; + int channel; + int row; + IT_ENTRY *entry; + + pattern->n_rows = 64; + + if (dumbfile_getnc((char *)buffer, 64 * 3 * 8, f) < 64 * 3 * 8) + return -1; + + /* compute number of entries */ + pattern->n_entries = 64 + 1; /* Account for the row end markers, speed command */ + if (breakpoint < 63) pattern->n_entries++; /* and break to row 0 */ + + pos = 0; + for (row = 0; row < 64; row++) { + for (channel = 0; channel < 8; channel++) { + if (buffer[pos+0] != 0xFF || buffer[pos+2] != 0xFF) + pattern->n_entries++; + pos += 3; + } + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + if (!pattern->entry) + return -1; + + if (breakpoint == 63) breakpoint++; + + entry = pattern->entry; + + entry->channel = 8; + entry->mask = IT_ENTRY_EFFECT; + entry->effect = IT_SET_SPEED; + entry->effectvalue = tempo; + entry++; + + pos = 0; + for (row = 0; row < 64; row++) { + + if (row == breakpoint) { + entry->channel = 8; + entry->mask = IT_ENTRY_EFFECT; + entry->effect = IT_BREAK_TO_ROW; + entry->effectvalue = 0; + entry++; + } + + for (channel = 0; channel < 8; channel++) { + if (buffer[pos+0] != 0xFF || buffer[pos+2] != 0xFF) { + entry->channel = channel; + entry->mask = 0; + + if (buffer[pos+0] < 0xFE) { + entry->mask |= IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT; + entry->note = (buffer[pos+0] >> 2) + 36; + entry->instrument = (((buffer[pos+0] << 4) | (buffer[pos+1] >> 4)) & 0x3F) + 1; + } + if (buffer[pos+0] <= 0xFE) { + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = ((buffer[pos+1] & 15) << 6) / 15; + if (*used_channels < channel + 1) *used_channels = channel + 1; + } + if (buffer[pos+2] != 0xFF) { + entry->mask |= IT_ENTRY_EFFECT; + entry->effectvalue = buffer[pos+2] & 15; + switch (buffer[pos+2] >> 4) { + case 0: + entry->effect = IT_PORTAMENTO_UP; + break; + case 1: + entry->effect = IT_PORTAMENTO_DOWN; + break; + case 2: + entry->effect = IT_TONE_PORTAMENTO; + break; + case 3: + entry->effect = IT_S; + entry->effectvalue += IT_S_FINETUNE * 16 + 8; + break; + case 4: + entry->effect = IT_VIBRATO; + // XXX speed unknown + entry->effectvalue |= 0x10; + break; + case 5: + if (entry->effectvalue) { + entry->effect = IT_SET_SPEED; + } else { + entry->mask &= ~IT_ENTRY_EFFECT; + } + break; +#if 0 + /* dunno about this, really... */ + case 6: + if (entry->effectvalue == 0) { + entry->effect = IT_PANNING_SLIDE; + entry->effectvalue = 0xFE; + } else if (entry->effectvalue == 1) { + entry->effect = IT_PANNING_SLIDE; + entry->effectvalue = 0xEF; + } else { + entry->mask &= ~IT_ENTRY_EFFECT; + } + break; +#endif + default: + entry->mask &= ~IT_ENTRY_EFFECT; + break; + } + if (*used_channels < channel + 1) *used_channels = channel + 1; + } + + entry++; + } + pos += 3; + } + IT_SET_END_ROW(entry); + entry++; + } + + return 0; +} + + + +static int it_669_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f) +{ + dumbfile_getnc((char *)sample->name, 13, f); + sample->name[13] = 0; + + sample->filename[0] = 0; + + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = dumbfile_igetl(f); + + if (dumbfile_error(f)) + return -1; + + if (sample->length <= 0) { + sample->flags = 0; + return 0; + } + + sample->flags = IT_SAMPLE_EXISTS; + + sample->global_volume = 64; + sample->default_volume = 64; + + sample->default_pan = 0; + sample->C5_speed = 8363; + // the above line might be wrong + + if ((sample->loop_end > sample->length) && !(sample->loop_start)) + sample->loop_end = 0; + + if (sample->loop_end > sample->length) + sample->loop_end = sample->length; + + if (sample->loop_end - sample->loop_start > 2) + sample->flags |= IT_SAMPLE_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->finetune = 0; + sample->max_resampling_quality = -1; + + return 0; +} + + + +static int it_669_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f) +{ + int32 i; + int32 truncated_size; + + /* let's get rid of the sample data coming after the end of the loop */ + if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) { + truncated_size = sample->length - sample->loop_end; + sample->length = sample->loop_end; + } else { + truncated_size = 0; + } + + sample->data = malloc(sample->length); + + if (!sample->data) + return -1; + + if (sample->length) + { + i = dumbfile_getnc(sample->data, sample->length, f); + + if (i < sample->length) { + //return -1; + // ficking truncated files + if (i <= 0) { + sample->flags = 0; + return 0; + } + sample->length = i; + if (sample->loop_end > i) sample->loop_end = i; + } else { + /* skip truncated data */ + dumbfile_skip(f, truncated_size); + // Should we be truncating it? + if (dumbfile_error(f)) + return -1; + } + + for (i = 0; i < sample->length; i++) + ((signed char *)sample->data)[i] ^= 0x80; + } + + return 0; +} + + +static DUMB_IT_SIGDATA *it_669_load_sigdata(DUMBFILE *f, int * ext) +{ + DUMB_IT_SIGDATA *sigdata; + int n_channels; + int i; + unsigned char tempolist[128]; + unsigned char breaklist[128]; + + i = dumbfile_igetw(f); + if (i != 0x6669 && i != 0x4E4A) return NULL; + + *ext = (i == 0x4E4A); + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) { + return NULL; + } + + if (dumbfile_getnc((char *)sigdata->name, 36, f) < 36) { + free(sigdata); + return NULL; + } + sigdata->name[36] = 0; + + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + sigdata->sample = NULL; + + sigdata->n_instruments = 0; + + sigdata->song_message = malloc(72 + 2 + 1); + if (!sigdata->song_message) { + free(sigdata); + return NULL; + } + if (dumbfile_getnc((char *)sigdata->song_message, 36, f) < 36) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sigdata->song_message[36] = 13; + sigdata->song_message[36 + 1] = 10; + if (dumbfile_getnc((char *)sigdata->song_message + 38, 36, f) < 36) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sigdata->song_message[38 + 36] = 0; + + sigdata->n_samples = dumbfile_getc(f); + sigdata->n_patterns = dumbfile_getc(f); + sigdata->restart_position = dumbfile_getc(f); + + if ((sigdata->n_samples) > 64 || (sigdata->n_patterns > 128)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->order = malloc(128); /* We may need to scan the extra ones! */ + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + if (dumbfile_getnc((char *)sigdata->order, 128, f) < 128) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (i = 0; i < 128; i++) { + if (sigdata->order[i] == 255) break; + if (sigdata->order[i] >= sigdata->n_patterns) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + if (!i) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sigdata->n_orders = i; + + if (dumbfile_getnc((char *)tempolist, 128, f) < 128) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (dumbfile_getnc((char *)breaklist, 128, f) < 128) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (i = 0; i < sigdata->n_samples; i++) + sigdata->sample[i].data = NULL; + + for (i = 0; i < sigdata->n_samples; i++) { + if (it_669_read_sample_header(&sigdata->sample[i], f)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + /* May as well try to save a tiny bit of memory. */ + if (sigdata->n_orders < 128) { + unsigned char *order = realloc(sigdata->order, sigdata->n_orders); + if (order) sigdata->order = order; + } + + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + n_channels = 0; + + /* Read in the patterns */ + { + unsigned char *buffer = malloc(64 * 3 * 8); /* 64 rows * 3 bytes * 8 channels */ + if (!buffer) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) { + if (it_669_read_pattern(&sigdata->pattern[i], f, tempolist[i], breaklist[i], buffer, &n_channels) != 0) { + free(buffer); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + free(buffer); + } + + sigdata->n_pchannels = n_channels; + + /* And finally, the sample data */ + for (i = 0; i < sigdata->n_samples; i++) { + if (it_669_read_sample_data(&sigdata->sample[i], f)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + /* Now let's initialise the remaining variables, and we're done! */ + sigdata->flags = IT_OLD_EFFECTS | IT_LINEAR_SLIDES | IT_STEREO | IT_WAS_A_669; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + sigdata->speed = 4; + sigdata->tempo = 78; + sigdata->pan_separation = 128; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i += 2) { + int sep = 32 * dumb_it_default_panning_separation / 100; + sigdata->channel_pan[i+0] = 32 + sep; + sigdata->channel_pan[i+1] = 32 - sep; + } + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *DUMBEXPORT dumb_read_669_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + int ext; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_669_load_sigdata(f, &ext); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = ext ? "669 Extended" : "669"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/read6692.c b/thirdparty/dumb/src/it/read6692.c new file mode 100644 index 000000000..a9911d3ec --- /dev/null +++ b/thirdparty/dumb/src/it/read6692.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * read6692.c - Code to read a 669 Composer module / / \ \ + * from an open file, and do an initial | < / \_ + * run-through. | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_read_669(DUMBFILE *f) +{ + DUH *duh = dumb_read_669_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/readam.c b/thirdparty/dumb/src/it/readam.c new file mode 100644 index 000000000..be99f1934 --- /dev/null +++ b/thirdparty/dumb/src/it/readam.c @@ -0,0 +1,788 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readam.c - Code to read a RIFF AM module / / \ \ + * from a parsed RIFF structure. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/it.h" +#include "internal/riff.h" + +static int it_riff_am_process_sample( IT_SAMPLE * sample, DUMBFILE * f, int len, int ver ) +{ + int header_length; + int default_pan; + int default_volume; + int flags; + int length; + int length_bytes; + int loop_start; + int loop_end; + int sample_rate; + + int32 start = dumbfile_pos( f ); + + if ( ver == 0 ) + { + if ( len < 0x38 ) + return -1; + + header_length = 0x38; + + dumbfile_getnc( (char *) sample->name, 28, f ); + sample->name[ 28 ] = 0; + + default_pan = dumbfile_getc( f ); + default_volume = dumbfile_getc( f ); + flags = dumbfile_igetw( f ); + length = dumbfile_igetl( f ); + loop_start = dumbfile_igetl( f ); + loop_end = dumbfile_igetl( f ); + sample_rate = dumbfile_igetl( f ); + } + else + { + if (len < 4) return -1; + + header_length = dumbfile_igetl( f ); + if ( header_length < 0x40 ) + return -1; + if ( header_length + 4 > len ) + return -1; + + start += 4; + len -= 4; + + dumbfile_getnc( (char *) sample->name, 32, f ); + + default_pan = dumbfile_igetw( f ); + default_volume = dumbfile_igetw( f ); + flags = dumbfile_igetw( f ); + dumbfile_skip( f, 2 ); + length = dumbfile_igetl( f ); + loop_start = dumbfile_igetl( f ); + loop_end = dumbfile_igetl( f ); + sample_rate = dumbfile_igetl( f ); + + if ( default_pan > 0x7FFF || default_volume > 0x7FFF ) + return -1; + + default_pan = default_pan * 64 / 32767; + default_volume = default_volume * 64 / 32767; + } + + if ( ! length ) { + sample->flags &= ~IT_SAMPLE_EXISTS; + return 0; + } + + if ( flags & ~( 0x8000 | 0x80 | 0x20 | 0x10 | 0x08 | 0x04 ) ) + return -1; + + length_bytes = length << ( ( flags & 0x04 ) >> 2 ); + + if ( length_bytes + header_length > len ) + return -1; + + sample->flags = 0; + + if ( flags & 0x80 ) sample->flags |= IT_SAMPLE_EXISTS; + if ( flags & 0x04 ) sample->flags |= IT_SAMPLE_16BIT; + + sample->length = length; + sample->loop_start = loop_start; + sample->loop_end = loop_end; + sample->C5_speed = sample_rate; + sample->default_volume = default_volume; + sample->default_pan = default_pan | ( ( flags & 0x20 ) << 2 ); + sample->filename[0] = 0; + sample->global_volume = 64; + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + if ( flags & 0x08 ) + { + if (((unsigned int)sample->loop_end <= (unsigned int)sample->length) && + ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end)) + { + sample->length = sample->loop_end; + sample->flags |= IT_SAMPLE_LOOP; + if ( flags & 0x10 ) sample->flags |= IT_SAMPLE_PINGPONG_LOOP; + } + } + + length_bytes = sample->length << ( ( flags & 0x04 ) >> 2 ); + + sample->data = malloc( length_bytes ); + if ( ! sample->data ) + return -1; + + if ( dumbfile_seek( f, start + header_length, DFS_SEEK_SET ) ) + return -1; + + dumbfile_getnc( sample->data, length_bytes, f ); + + return 0; +} + +static int it_riff_am_process_pattern( IT_PATTERN * pattern, DUMBFILE * f, int len, int ver ) +{ + int nrows, row; + long start, end; + unsigned flags; + int p, q, r; + IT_ENTRY * entry; + + nrows = dumbfile_getc( f ) + 1; + + pattern->n_rows = nrows; + + len -= 1; + + pattern->n_entries = 0; + + row = 0; + + start = dumbfile_pos( f ); + end = start + len; + + while ( (row < nrows) && !dumbfile_error( f ) && (dumbfile_pos( f ) < end) ) { + p = dumbfile_getc( f ); + if ( ! p ) { + ++ row; + continue; + } + + flags = p & 0xE0; + + if (flags) { + ++ pattern->n_entries; + if (flags & 0x80) dumbfile_skip( f, 2 ); + if (flags & 0x40) dumbfile_skip( f, 2 ); + if (flags & 0x20) dumbfile_skip( f, 1 ); + } + } + + if ( ! pattern->n_entries ) return 0; + + pattern->n_entries += nrows; + + pattern->entry = malloc( pattern->n_entries * sizeof( * pattern->entry ) ); + if ( ! pattern->entry ) return -1; + + entry = pattern->entry; + + row = 0; + + dumbfile_seek( f, start, DFS_SEEK_SET ); + + while ( ( row < nrows ) && !dumbfile_error( f ) && ( dumbfile_pos( f ) < end ) ) + { + p = dumbfile_getc( f ); + + if ( ! p ) + { + IT_SET_END_ROW( entry ); + ++ entry; + ++ row; + continue; + } + + flags = p; + entry->channel = flags & 0x1F; + entry->mask = 0; + + if (flags & 0xE0) + { + if ( flags & 0x80 ) + { + q = dumbfile_getc( f ); + r = dumbfile_getc( f ); + _dumb_it_xm_convert_effect( r, q, entry, 0 ); + } + + if ( flags & 0x40 ) + { + q = dumbfile_getc( f ); + r = dumbfile_getc( f ); + if ( q ) + { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = q; + } + if ( r ) + { + entry->mask |= IT_ENTRY_NOTE; + entry->note = r - 1; + } + } + + if ( flags & 0x20 ) + { + q = dumbfile_getc( f ); + entry->mask |= IT_ENTRY_VOLPAN; + if ( ver == 0 ) entry->volpan = q; + else entry->volpan = q * 64 / 127; + } + + if (entry->mask) entry++; + } + } + + while ( row < nrows ) + { + IT_SET_END_ROW( entry ); + ++ entry; + ++ row; + } + + pattern->n_entries = (int)(entry - pattern->entry); + if ( ! pattern->n_entries ) return -1; + + return 0; +} + +static DUMB_IT_SIGDATA *it_riff_amff_load_sigdata( DUMBFILE * f, struct riff * stream ) +{ + DUMB_IT_SIGDATA *sigdata; + + int n, o, p, found; + + if ( ! stream ) goto error; + + if ( stream->type != DUMB_ID( 'A', 'M', 'F', 'F' ) ) goto error; + + sigdata = malloc( sizeof( *sigdata ) ); + if ( ! sigdata ) goto error; + + sigdata->n_patterns = 0; + sigdata->n_samples = 0; + sigdata->name[0] = 0; + + found = 0; + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch( c->type ) + { + case DUMB_ID( 'M', 'A', 'I', 'N' ): + /* initialization data */ + if ( ( found & 1 ) || ( c->size < 0x48 ) ) goto error_sd; + found |= 1; + break; + + case DUMB_ID( 'O', 'R', 'D', 'R' ): + if ( ( found & 2 ) || ( c->size < 1 ) ) goto error_sd; + found |= 2; + break; + + case DUMB_ID( 'P', 'A', 'T', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_sd; + o = dumbfile_getc( f ); + if ( o >= sigdata->n_patterns ) sigdata->n_patterns = o + 1; + o = dumbfile_igetl( f ); + if ( (unsigned)o + 5 > c->size ) goto error_sd; + break; + + case DUMB_ID( 'I', 'N', 'S', 'T' ): + { + if ( c->size < 0xE1 ) goto error_sd; + if ( dumbfile_seek( f, c->offset + 1, DFS_SEEK_SET ) ) goto error_sd; + o = dumbfile_getc( f ); + if ( o >= sigdata->n_samples ) sigdata->n_samples = o + 1; + if ( c->size >= 0x121 ) + { + if ( dumbfile_seek( f, c->offset + 0xE1, DFS_SEEK_SET ) ) goto error_sd; + if ( dumbfile_mgetl( f ) == DUMB_ID('S','A','M','P') ) + { + unsigned size = dumbfile_igetl( f ); + if ( size + 0xE1 + 8 > c->size ) goto error_sd; + } + } + } + break; + } + } + + if ( found != 3 || !sigdata->n_samples || !sigdata->n_patterns ) goto error_sd; + + if ( sigdata->n_samples > 255 || sigdata->n_patterns > 255 ) goto error_sd; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + sigdata->n_instruments = 0; + sigdata->n_orders = 0; + sigdata->restart_position = 0; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { + int sep = 32 * dumb_it_default_panning_separation / 100; + sigdata->channel_pan[n ] = 32 - sep; + sigdata->channel_pan[n+1] = 32 + sep; + sigdata->channel_pan[n+2] = 32 + sep; + sigdata->channel_pan[n+3] = 32 - sep; + } + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'M', 'A', 'I', 'N' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + dumbfile_getnc( (char *) sigdata->name, 64, f ); + sigdata->name[ 64 ] = 0; + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M; + o = dumbfile_getc( f ); + if ( ! ( o & 1 ) ) sigdata->flags |= IT_LINEAR_SLIDES; + if ( ( o & ~3 ) || ! ( o & 2 ) ) goto error_usd; // unknown flags + sigdata->n_pchannels = dumbfile_getc( f ); + sigdata->speed = dumbfile_getc( f ); + sigdata->tempo = dumbfile_getc( f ); + + dumbfile_skip( f, 4 ); + + sigdata->global_volume = dumbfile_getc( f ); + + if ( c->size < 0x48 + (unsigned)sigdata->n_pchannels ) goto error_usd; + + for ( o = 0; o < sigdata->n_pchannels; ++o ) + { + p = dumbfile_getc( f ); + sigdata->channel_pan[ o ] = p; + if ( p >= 128 ) + { + sigdata->channel_volume[ o ] = 0; + } + } + break; + } + } + + sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) ); + if ( ! sigdata->pattern ) goto error_usd; + for ( n = 0; n < sigdata->n_patterns; ++n ) + sigdata->pattern[ n ].entry = NULL; + + sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) ); + if ( ! sigdata->sample ) goto error_usd; + for ( n = 0; n < sigdata->n_samples; ++n ) + { + IT_SAMPLE * sample = sigdata->sample + n; + sample->data = NULL; + sample->flags = 0; + sample->name[ 0 ] = 0; + } + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'O', 'R', 'D', 'R' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + sigdata->n_orders = dumbfile_getc( f ) + 1; + if ( (unsigned)sigdata->n_orders + 1 > c->size ) goto error_usd; + sigdata->order = malloc( sigdata->n_orders ); + if ( ! sigdata->order ) goto error_usd; + dumbfile_getnc( (char *) sigdata->order, sigdata->n_orders, f ); + break; + + case DUMB_ID( 'P', 'A', 'T', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + o = dumbfile_getc( f ); + p = dumbfile_igetl( f ); + if ( it_riff_am_process_pattern( sigdata->pattern + o, f, p, 0 ) ) goto error_usd; + break; + + case DUMB_ID( 'I', 'N', 'S', 'T' ): + { + IT_SAMPLE * sample; + if ( dumbfile_seek( f, c->offset + 1, DFS_SEEK_SET ) ) goto error_usd; + sample = sigdata->sample + dumbfile_getc( f ); + if ( c->size >= 0x121 ) + { + if ( dumbfile_seek( f, c->offset + 0xE1, DFS_SEEK_SET ) ) goto error_usd; + if ( dumbfile_mgetl( f ) == DUMB_ID('S','A','M','P') ) + { + unsigned size = dumbfile_igetl( f ); + if ( it_riff_am_process_sample( sample, f, size, 0 ) ) goto error_usd; + break; + } + } + dumbfile_seek( f, c->offset + 2, DFS_SEEK_SET ); + dumbfile_getnc( (char *) sample->name, 28, f ); + sample->name[ 28 ] = 0; + } + break; + } + } + + _dumb_it_fix_invalid_orders( sigdata ); + + return sigdata; + +error_usd: + _dumb_it_unload_sigdata( sigdata ); + goto error; +error_sd: + free( sigdata ); +error: + return NULL; +} + +static DUMB_IT_SIGDATA *it_riff_am_load_sigdata( DUMBFILE * f, struct riff * stream ) +{ + DUMB_IT_SIGDATA *sigdata; + + int n, o, p, found; + + if ( ! f || ! stream ) goto error; + + if ( stream->type != DUMB_ID( 'A', 'M', ' ', ' ' ) ) goto error; + + sigdata = malloc(sizeof(*sigdata)); + if ( ! sigdata ) goto error; + + sigdata->n_patterns = 0; + sigdata->n_samples = 0; + sigdata->name[0] = 0; + + found = 0; + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch( c->type ) + { + case DUMB_ID( 'I' ,'N' ,'I' ,'T' ): + /* initialization data */ + if ( ( found & 1 ) || ( c->size < 0x48 ) ) goto error_sd; + found |= 1; + break; + + case DUMB_ID( 'O', 'R', 'D', 'R' ): + if ( ( found & 2 ) || ( c->size < 1 ) ) goto error_sd; + found |= 2; + break; + + case DUMB_ID( 'P', 'A', 'T', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_sd; + o = dumbfile_getc( f ); + if ( o >= sigdata->n_patterns ) sigdata->n_patterns = o + 1; + o = dumbfile_igetl( f ); + if ( (unsigned)o + 5 > c->size ) goto error_sd; + break; + + case DUMB_ID( 'R', 'I', 'F', 'F' ): + { + struct riff * str = c->nested; + switch ( str->type ) + { + case DUMB_ID( 'A', 'I', ' ', ' ' ): + for ( o = 0; (unsigned)o < str->chunk_count; ++o ) + { + struct riff_chunk * chk = str->chunks + o; + switch( chk->type ) + { + case DUMB_ID( 'I', 'N', 'S', 'T' ): + { + struct riff * temp; + unsigned size; + unsigned sample_found; + if ( dumbfile_seek( f, chk->offset, DFS_SEEK_SET ) ) goto error_sd; + size = dumbfile_igetl( f ); + if ( size < 0x142 ) goto error_sd; + sample_found = 0; + dumbfile_skip( f, 1 ); + p = dumbfile_getc( f ); + if ( p >= sigdata->n_samples ) sigdata->n_samples = p + 1; + temp = riff_parse( f, chk->offset + 4 + size, chk->size - size - 4, 1 ); + if ( temp ) + { + if ( temp->type == DUMB_ID( 'A', 'S', ' ', ' ' ) ) + { + for ( p = 0; (unsigned)p < temp->chunk_count; ++p ) + { + if ( temp->chunks[ p ].type == DUMB_ID( 'S', 'A', 'M', 'P' ) ) + { + if ( sample_found ) + { + riff_free( temp ); + goto error_sd; + } + sample_found = 1; + } + } + } + riff_free( temp ); + } + } + } + } + } + } + break; + } + } + + if ( found != 3 || !sigdata->n_samples || !sigdata->n_patterns ) goto error_sd; + + if ( sigdata->n_samples > 255 || sigdata->n_patterns > 255 ) goto error_sd; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + sigdata->n_instruments = 0; + sigdata->n_orders = 0; + sigdata->restart_position = 0; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { + int sep = 32 * dumb_it_default_panning_separation / 100; + sigdata->channel_pan[n ] = 32 - sep; + sigdata->channel_pan[n+1] = 32 + sep; + sigdata->channel_pan[n+2] = 32 + sep; + sigdata->channel_pan[n+3] = 32 - sep; + } + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'I', 'N', 'I', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + dumbfile_getnc( (char *) sigdata->name, 64, f ); + sigdata->name[ 64 ] = 0; + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M; + o = dumbfile_getc( f ); + if ( ! ( o & 1 ) ) sigdata->flags |= IT_LINEAR_SLIDES; + if ( ( o & ~3 ) || ! ( o & 2 ) ) goto error_usd; // unknown flags + sigdata->n_pchannels = dumbfile_getc( f ); + sigdata->speed = dumbfile_getc( f ); + sigdata->tempo = dumbfile_getc( f ); + + dumbfile_skip( f, 4 ); + + sigdata->global_volume = dumbfile_getc( f ); + + if ( c->size < 0x48 + (unsigned)sigdata->n_pchannels ) goto error_usd; + + for ( o = 0; o < sigdata->n_pchannels; ++o ) + { + p = dumbfile_getc( f ); + if ( p <= 128 ) + { + sigdata->channel_pan[ o ] = p / 2; + } + else + { + sigdata->channel_volume[ o ] = 0; + } + } + break; + } + } + + sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) ); + if ( ! sigdata->pattern ) goto error_usd; + for ( n = 0; n < sigdata->n_patterns; ++n ) + sigdata->pattern[ n ].entry = NULL; + + sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) ); + if ( ! sigdata->sample ) goto error_usd; + for ( n = 0; n < sigdata->n_samples; ++n ) + { + IT_SAMPLE * sample = sigdata->sample + n; + sample->data = NULL; + sample->flags = 0; + sample->name[ 0 ] = 0; + } + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'O', 'R', 'D', 'R' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + sigdata->n_orders = dumbfile_getc( f ) + 1; + if ( (unsigned)sigdata->n_orders + 1 > c->size ) goto error_usd; + sigdata->order = malloc( sigdata->n_orders ); + if ( ! sigdata->order ) goto error_usd; + dumbfile_getnc( (char *) sigdata->order, sigdata->n_orders, f ); + break; + + case DUMB_ID( 'P', 'A', 'T', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + o = dumbfile_getc( f ); + p = dumbfile_igetl( f ); + if ( it_riff_am_process_pattern( sigdata->pattern + o, f, p, 1 ) ) goto error_usd; + break; + + case DUMB_ID( 'R', 'I', 'F', 'F' ): + { + struct riff * str = c->nested; + switch ( str->type ) + { + case DUMB_ID('A', 'I', ' ', ' '): + for ( o = 0; (unsigned)o < str->chunk_count; ++o ) + { + struct riff_chunk * chk = str->chunks + o; + switch( chk->type ) + { + case DUMB_ID( 'I', 'N', 'S', 'T' ): + { + struct riff * temp; + unsigned size; + unsigned sample_found; + IT_SAMPLE * sample; + if ( dumbfile_seek( f, chk->offset, DFS_SEEK_SET ) ) goto error_usd; + size = dumbfile_igetl( f ); + dumbfile_skip( f, 1 ); + p = dumbfile_getc( f ); + temp = riff_parse( f, chk->offset + 4 + size, chk->size - size - 4, 1 ); + sample_found = 0; + sample = sigdata->sample + p; + if ( temp ) + { + if ( temp->type == DUMB_ID( 'A', 'S', ' ', ' ' ) ) + { + for ( p = 0; (unsigned)p < temp->chunk_count; ++p ) + { + struct riff_chunk * c = temp->chunks + p; + if ( c->type == DUMB_ID( 'S', 'A', 'M', 'P' ) ) + { + if ( sample_found ) + { + riff_free( temp ); + goto error_usd; + } + { + riff_free( temp ); + goto error_usd; + } + if ( it_riff_am_process_sample( sample, f, c->size, 1 ) ) + { + riff_free( temp ); + goto error_usd; + } + sample_found = 1; + } + } + } + riff_free( temp ); + } + if ( ! sample_found ) + { + dumbfile_seek( f, chk->offset + 6, DFS_SEEK_SET ); + dumbfile_getnc( (char *) sample->name, 32, f ); + sample->name[ 32 ] = 0; + } + } + } + } + } + } + break; + } + } + + _dumb_it_fix_invalid_orders( sigdata ); + + return sigdata; + +error_usd: + _dumb_it_unload_sigdata( sigdata ); + goto error; +error_sd: + free( sigdata ); +error: + return NULL; +} + +DUH *dumb_read_riff_amff( DUMBFILE * f, struct riff * stream ) +{ + sigdata_t *sigdata; + long length; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_riff_amff_load_sigdata( f, stream ); + + if (!sigdata) + return NULL; + + length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/ + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "RIFF AMFF"; + return make_duh( length, 2, ( const char * const (*) [ 2 ] ) tag, 1, & descptr, & sigdata ); + } +} + +DUH *dumb_read_riff_am( DUMBFILE * f, struct riff * stream ) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_riff_am_load_sigdata( f, stream ); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "RIFF AM"; + return make_duh( -1, 2, ( const char * const (*) [ 2 ] ) tag, 1, & descptr, & sigdata ); + } +} diff --git a/thirdparty/dumb/src/it/readamf.c b/thirdparty/dumb/src/it/readamf.c new file mode 100644 index 000000000..7b72467e0 --- /dev/null +++ b/thirdparty/dumb/src/it/readamf.c @@ -0,0 +1,559 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readamf.c - Code to read a DSMI AMF module from / / \ \ + * an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static void it_amf_process_track( IT_ENTRY *entry_table, unsigned char *track, int rows, int channels ) +{ + int last_instrument = 0; + int tracksize = track[ 0 ] + ( track[ 1 ] << 8 ) + ( track[ 2 ] << 16 ); + track += 3; + while ( tracksize-- ) { + unsigned int row = track[ 0 ]; + unsigned int command = track[ 1 ]; + unsigned int argument = track[ 2 ]; + IT_ENTRY * entry = entry_table + row * channels; + if ( row >= ( unsigned int ) rows ) break; + if ( command < 0x7F ) { + entry->mask |= IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT | IT_ENTRY_VOLPAN; + entry->note = command; + if ( ! entry->instrument ) entry->instrument = last_instrument; + entry->volpan = argument; + } + else if ( command == 0x7F ) { + signed char row_delta = ( signed char ) argument; + int row_source = ( int ) row + ( int ) row_delta; + if ( row_source >= 0 && row_source < ( int ) rows ) { + *entry = entry_table[ row_source * channels ]; + } + } + else if ( command == 0x80 ) { + entry->mask |= IT_ENTRY_INSTRUMENT; + last_instrument = argument + 1; + entry->instrument = last_instrument; + } + else if ( command == 0x83 ) { + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = argument; + } + else { + unsigned int effect = command & 0x7F; + unsigned int effectvalue = argument; + switch (effect) { + case 0x01: effect = IT_SET_SPEED; break; + + case 0x02: effect = IT_VOLUME_SLIDE; + case 0x0A: if ( effect == 0x0A ) effect = IT_VOLSLIDE_TONEPORTA; + case 0x0B: if ( effect == 0x0B ) effect = IT_VOLSLIDE_VIBRATO; + if ( effectvalue & 0x80 ) effectvalue = ( -( signed char ) effectvalue ) & 0x0F; + else effectvalue = ( effectvalue & 0x0F ) << 4; + break; + + case 0x04: + if ( effectvalue & 0x80 ) { + effect = IT_PORTAMENTO_UP; + effectvalue = ( -( signed char ) effectvalue ) & 0x7F; + } + else { + effect = IT_PORTAMENTO_DOWN; + } + break; + + case 0x06: effect = IT_TONE_PORTAMENTO; break; + + case 0x07: effect = IT_TREMOR; break; + + case 0x08: effect = IT_ARPEGGIO; break; + + case 0x09: effect = IT_VIBRATO; break; + + case 0x0C: effect = IT_BREAK_TO_ROW; break; + + case 0x0D: effect = IT_JUMP_TO_ORDER; break; + + case 0x0F: effect = IT_RETRIGGER_NOTE; break; + + case 0x10: effect = IT_SET_SAMPLE_OFFSET; break; + + case 0x11: + if ( effectvalue ) { + effect = IT_VOLUME_SLIDE; + if ( effectvalue & 0x80 ) + effectvalue = 0xF0 | ( ( -( signed char ) effectvalue ) & 0x0F ); + else + effectvalue = 0x0F | ( ( effectvalue & 0x0F ) << 4 ); + } + else + effect = 0; + break; + + case 0x12: + case 0x16: + if ( effectvalue ) { + int mask = ( effect == 0x16 ) ? 0xE0 : 0xF0; + effect = ( effectvalue & 0x80 ) ? IT_PORTAMENTO_UP : IT_PORTAMENTO_DOWN; + if ( effectvalue & 0x80 ) + effectvalue = mask | ( ( -( signed char ) effectvalue ) & 0x0F ); + else + effectvalue = mask | ( effectvalue & 0x0F ); + } + else + effect = 0; + break; + + case 0x13: + effect = IT_S; + effectvalue = EFFECT_VALUE( IT_S_NOTE_DELAY, effectvalue & 0x0F ); + break; + + case 0x14: + effect = IT_S; + effectvalue = EFFECT_VALUE( IT_S_DELAYED_NOTE_CUT, effectvalue & 0x0F ); + break; + + case 0x15: effect = IT_SET_SONG_TEMPO; break; + + case 0x17: + effectvalue = ( effectvalue + 64 ) & 0x7F; + if ( entry->mask & IT_ENTRY_EFFECT ) { + if ( !( entry->mask & IT_ENTRY_VOLPAN ) ) { + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = ( effectvalue / 2 ) + 128; + } + effect = 0; + } + else { + effect = IT_SET_PANNING; + } + break; + + default: effect = effectvalue = 0; + } + if ( effect ) { + entry->mask |= IT_ENTRY_EFFECT; + entry->effect = effect; + entry->effectvalue = effectvalue; + } + } + track += 3; + } +} + +static int it_amf_process_pattern( IT_PATTERN *pattern, IT_ENTRY *entry_table, int rows, int channels ) +{ + int i, j; + int n_entries = rows; + IT_ENTRY * entry; + + pattern->n_rows = rows; + + for ( i = 0, j = channels * rows; i < j; i++ ) { + if ( entry_table[ i ].mask ) { + n_entries++; + } + } + + pattern->n_entries = n_entries; + + pattern->entry = entry = malloc( n_entries * sizeof( IT_ENTRY ) ); + if ( !entry ) { + return -1; + } + + for ( i = 0; i < rows; i++ ) { + for ( j = 0; j < channels; j++ ) { + if ( entry_table[ i * channels + j ].mask ) { + *entry = entry_table[ i * channels + j ]; + entry->channel = j; + entry++; + } + } + IT_SET_END_ROW( entry ); + entry++; + } + + return 0; +} + +static int it_amf_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f, int * offset, int ver ) +{ + int exists; + + exists = dumbfile_getc( f ); + + dumbfile_getnc( (char *) sample->name, 32, f ); + sample->name[32] = 0; + + dumbfile_getnc( (char *) sample->filename, 13, f ); + sample->filename[13] = 0; + + *offset = dumbfile_igetl( f ); + sample->length = dumbfile_igetl( f ); + sample->C5_speed = dumbfile_igetw( f ); + sample->default_volume = dumbfile_getc( f ); + sample->global_volume = 64; + if ( sample->default_volume > 64 ) sample->default_volume = 64; + + if ( ver >= 11 ) { + sample->loop_start = dumbfile_igetl( f ); + sample->loop_end = dumbfile_igetl( f ); + } else { + sample->loop_start = dumbfile_igetw( f ); + sample->loop_end = sample->length; + } + + if ( sample->length <= 0 ) { + sample->flags = 0; + return 0; + } + + sample->flags = exists == 1 ? IT_SAMPLE_EXISTS : 0; + + sample->default_pan = 0; + sample->finetune = 0; + + if ( sample->loop_end > sample->loop_start + 2 && sample->loop_end <= sample->length ) + sample->flags |= IT_SAMPLE_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + + + +static int it_amf_read_sample_data( IT_SAMPLE *sample, DUMBFILE *f ) +{ + int i, read_length = 0; + + sample->data = malloc( sample->length ); + + if ( !sample->data ) + return -1; + + if ( sample->length ) + read_length = dumbfile_getnc( sample->data, sample->length, f ); + + for ( i = 0; i < read_length; i++ ) { + ( ( char * ) sample->data )[ i ] ^= 0x80; + } + + for ( i = read_length; i < sample->length; i++ ) { + ( ( char * ) sample->data )[ i ] = 0; + } + + return 0; /* Sometimes the last sample is truncated :( */ +} + +static DUMB_IT_SIGDATA *it_amf_load_sigdata(DUMBFILE *f, int * version) +{ + DUMB_IT_SIGDATA *sigdata; + int i, j, ver, ntracks, realntracks, nchannels; + + int maxsampleseekpos = 0; + int sampleseekpos[256]; + + unsigned short *orderstotracks; + unsigned short *trackmap; + unsigned int tracksize[256]; + + unsigned char **track; + + static const char sig[] = "AMF"; + + char signature [3]; + + if ( dumbfile_getnc( signature, 3, f ) != 3 || + memcmp( signature, sig, 3 ) ) { + return NULL; + } + + *version = ver = dumbfile_getc( f ); + if ( ver < 10 || ver > 14) { + return NULL; + } + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) { + return NULL; + } + + dumbfile_getnc( (char *) sigdata->name, 32, f ); + sigdata->name[ 32 ] = 0; + sigdata->n_samples = dumbfile_getc( f ); + sigdata->n_orders = dumbfile_getc( f ); + ntracks = dumbfile_igetw( f ); + nchannels = dumbfile_getc( f ); + + if ( dumbfile_error( f ) || + sigdata->n_samples < 1 || sigdata->n_samples > 255 || + sigdata->n_orders < 1 || sigdata->n_orders > 255 || + ! ntracks || + nchannels < 1 || nchannels > 32 ) { + free( sigdata ); + return NULL; + } + + sigdata->n_pchannels = nchannels; + + memset( sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS ); + + if ( ver >= 11 ) { + int nchannels = ( ver >= 13 ) ? 32 : 16; + for ( i = 0; i < nchannels; i++ ) { + signed char panpos = dumbfile_getc( f ); + int pan = ( panpos + 64 ) / 2; + if ( pan < 0 ) pan = 0; + else if ( pan > 64 ) pan = IT_SURROUND; + sigdata->channel_pan[ i ] = pan; + } + } + else { + int sep = 32 * dumb_it_default_panning_separation / 100; + for ( i = 0; i < 16; i++ ) { + sigdata->channel_pan[ i ] = ( dumbfile_getc( f ) & 1 ) ? 32 - sep : 32 + sep; + } + } + + sigdata->tempo = 125; + sigdata->speed = 6; + if ( ver >= 13 ) { + i = dumbfile_getc( f ); + if ( i >= 32 ) sigdata->tempo = i; + i = dumbfile_getc( f ); + if ( i <= 32 ) sigdata->speed = i; + } + + sigdata->order = malloc( sigdata->n_orders ); + if ( !sigdata->order ) { + free( sigdata ); + return NULL; + } + + orderstotracks = malloc( sigdata->n_orders * nchannels * sizeof( unsigned short ) ); + if ( !orderstotracks ) { + free( sigdata->order ); + free( sigdata ); + return NULL; + } + + for ( i = 0; i < sigdata->n_orders; i++ ) { + sigdata->order[ i ] = i; + tracksize[ i ] = 64; + if ( ver >= 14 ) { + tracksize[ i ] = dumbfile_igetw( f ); + } + for ( j = 0; j < nchannels; j++ ) { + orderstotracks[ i * nchannels + j ] = dumbfile_igetw( f ); + } + } + + if ( dumbfile_error( f ) ) { + free( orderstotracks ); + free( sigdata->order ); + free( sigdata ); + return NULL; + } + + sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) ); + if ( !sigdata->sample ) { + free( orderstotracks ); + free( sigdata->order ); + free( sigdata ); + return NULL; + } + + sigdata->restart_position = 0; + + sigdata->song_message = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + for ( i = 0; i < sigdata->n_samples; ++i ) + sigdata->sample[i].data = NULL; + + for ( i = 0; i < sigdata->n_samples; ++i ) { + int offset; + if ( it_amf_read_sample_header( &sigdata->sample[i], f, &offset, ver ) ) { + goto error_ott; + } + sampleseekpos[ i ] = offset; + if ( offset > maxsampleseekpos ) maxsampleseekpos = offset; + } + + sigdata->n_patterns = sigdata->n_orders; + + sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) ); + if ( !sigdata->pattern ) { + goto error_ott; + } + for (i = 0; i < sigdata->n_patterns; ++i) + sigdata->pattern[i].entry = NULL; + + trackmap = malloc( ntracks * sizeof( unsigned short ) ); + if ( !trackmap ) { + goto error_ott; + } + + if ( dumbfile_getnc( ( char * ) trackmap, ntracks * sizeof( unsigned short ), f ) != (long)(ntracks * sizeof( unsigned short )) ) { + goto error_tm; + } + + realntracks = 0; + + for ( i = 0; i < ntracks; i++ ) { + if ( trackmap[ i ] > realntracks ) realntracks = trackmap[ i ]; + } + + track = calloc( realntracks, sizeof( unsigned char * ) ); + if ( !track ) { + goto error_tm; + } + + for ( i = 0; i < realntracks; i++ ) { + int tracksize = dumbfile_igetw( f ); + tracksize += dumbfile_getc( f ) << 16; + track[ i ] = malloc( tracksize * 3 + 3 ); + if ( !track[ i ] ) { + goto error_all; + } + track[ i ][ 0 ] = tracksize & 255; + track[ i ][ 1 ] = ( tracksize >> 8 ) & 255; + track[ i ][ 2 ] = ( tracksize >> 16 ) & 255; + if ( dumbfile_getnc( (char *) track[ i ] + 3, tracksize * 3, f ) != tracksize * 3 ) { + goto error_all; + } + } + + for ( i = 1; i <= maxsampleseekpos; i++ ) { + for ( j = 0; j < sigdata->n_samples; j++ ) { + if ( sampleseekpos[ j ] == i ) { + if ( it_amf_read_sample_data( &sigdata->sample[ j ], f ) ) { + goto error_all; + } + break; + } + } + } + + /* Process tracks into patterns */ + for ( i = 0; i < sigdata->n_patterns; i++ ) { + IT_ENTRY * entry_table = calloc( tracksize[ i ] * nchannels, sizeof( IT_ENTRY ) ); + if ( !entry_table ) { + goto error_all; + } + for ( j = 0; j < nchannels; j++ ) { + int ntrack = orderstotracks[ i * nchannels + j ]; + if ( ntrack && ntrack <= ntracks ) { + int realtrack = trackmap[ ntrack - 1 ]; + if ( realtrack ) { + realtrack--; + if ( realtrack < realntracks && track[ realtrack ] ) { + it_amf_process_track( entry_table + j, track[ realtrack ], tracksize[ i ], nchannels ); + } + } + } + } + if ( it_amf_process_pattern( &sigdata->pattern[ i ], entry_table, tracksize[ i ], nchannels ) ) { + free( entry_table ); + goto error_all; + } + free( entry_table ); + } + + /* Now let's initialise the remaining variables, and we're done! */ + sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO | IT_WAS_AN_S3M; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + _dumb_it_fix_invalid_orders(sigdata); + + for ( i = 0; i < realntracks; i++ ) { + if ( track[ i ] ) { + free( track[ i ] ); + } + } + free( track ); + free( trackmap ); + free( orderstotracks ); + + return sigdata; + +error_all: + for ( i = 0; i < realntracks; i++ ) { + if ( track[ i ] ) { + free( track[ i ] ); + } + } + free( track ); +error_tm: + free( trackmap ); +error_ott: + free( orderstotracks ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; +} + + + +DUH *DUMBEXPORT dumb_read_amf_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + int version; + + sigdata = it_amf_load_sigdata(f, &version); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + char ver_string[14]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + memcpy( ver_string, "DSMI AMF v", 10 ); + ver_string[10] = '0' + version / 10; + ver_string[11] = '.'; + ver_string[12] = '0' + version % 10; + ver_string[13] = 0; + tag[1][1] = ver_string; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/readamf2.c b/thirdparty/dumb/src/it/readamf2.c new file mode 100644 index 000000000..3c8732227 --- /dev/null +++ b/thirdparty/dumb/src/it/readamf2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readamf2.c - Function to read a DSMI AMF module / / \ \ + * from an open file and do an initial | < / \_ + * run-through. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_read_amf(DUMBFILE *f) +{ + DUH *duh = dumb_read_amf_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/readany.c b/thirdparty/dumb/src/it/readany.c new file mode 100644 index 000000000..9d90776ff --- /dev/null +++ b/thirdparty/dumb/src/it/readany.c @@ -0,0 +1,132 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readany.c - Code to detect and read any of the / / \ \ + * module formats supported by DUMB. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" + +#ifdef _MSC_VER + #define strnicmp _strnicmp +#else + #if defined(unix) || defined(__unix__) || defined(__unix) + #include + #endif + #define strnicmp strncasecmp +#endif + +enum { maximum_signature_size = 0x30 }; + +DUH *DUMBEXPORT dumb_read_any_quick(DUMBFILE *f, int restrict_, int subsong) +{ + unsigned char signature[ maximum_signature_size ]; + unsigned long signature_size; + DUH * duh = NULL; + + signature_size = dumbfile_get_size(f); + + signature_size = dumbfile_getnc( (char *)signature, maximum_signature_size, f ); + dumbfile_seek( f, 0, DFS_SEEK_SET ); + + if (signature_size >= 4 && + signature[0] == 'I' && signature[1] == 'M' && + signature[2] == 'P' && signature[3] == 'M') + { + duh = dumb_read_it_quick( f ); + } + else if (signature_size >= 17 && !memcmp(signature, "Extended Module: ", 17)) + { + duh = dumb_read_xm_quick( f ); + } + else if (signature_size >= 0x30 && + signature[0x2C] == 'S' && signature[0x2D] == 'C' && + signature[0x2E] == 'R' && signature[0x2F] == 'M') + { + duh = dumb_read_s3m_quick( f ); + } + else if (signature_size >= 30 && + /*signature[28] == 0x1A &&*/ signature[29] == 2 && + ( ! strnicmp( ( const char * ) signature + 20, "!Scream!", 8 ) || + ! strnicmp( ( const char * ) signature + 20, "BMOD2STM", 8 ) || + ! strnicmp( ( const char * ) signature + 20, "WUZAMOD!", 8 ) ) ) + { + duh = dumb_read_stm_quick( f ); + } + else if (signature_size >= 2 && + ((signature[0] == 0x69 && signature[1] == 0x66) || + (signature[0] == 0x4A && signature[1] == 0x4E))) + { + duh = dumb_read_669_quick( f ); + } + else if (signature_size >= 0x30 && + signature[0x2C] == 'P' && signature[0x2D] == 'T' && + signature[0x2E] == 'M' && signature[0x2F] == 'F') + { + duh = dumb_read_ptm_quick( f ); + } + else if (signature_size >= 4 && + signature[0] == 'P' && signature[1] == 'S' && + signature[2] == 'M' && signature[3] == ' ') + { + duh = dumb_read_psm_quick( f, subsong ); + } + else if (signature_size >= 4 && + signature[0] == 'P' && signature[1] == 'S' && + signature[2] == 'M' && signature[3] == 254) + { + duh = dumb_read_old_psm_quick( f ); + } + else if (signature_size >= 3 && + signature[0] == 'M' && signature[1] == 'T' && + signature[2] == 'M') + { + duh = dumb_read_mtm_quick( f ); + } + else if ( signature_size >= 4 && + signature[0] == 'R' && signature[1] == 'I' && + signature[2] == 'F' && signature[3] == 'F') + { + duh = dumb_read_riff_quick( f ); + } + else if ( signature_size >= 24 && + !memcmp( signature, "ASYLUM Music Format", 19 ) && + !memcmp( signature + 19, " V1.0", 5 ) ) + { + duh = dumb_read_asy_quick( f ); + } + else if ( signature_size >= 3 && + signature[0] == 'A' && signature[1] == 'M' && + signature[2] == 'F') + { + duh = dumb_read_amf_quick( f ); + } + else if ( signature_size >= 8 && + !memcmp( signature, "OKTASONG", 8 ) ) + { + duh = dumb_read_okt_quick( f ); + } + + if ( !duh ) + { + dumbfile_seek( f, 0, DFS_SEEK_SET ); + duh = dumb_read_mod_quick( f, restrict_ ); + } + + return duh; +} diff --git a/thirdparty/dumb/src/it/readany2.c b/thirdparty/dumb/src/it/readany2.c new file mode 100644 index 000000000..bd0102cab --- /dev/null +++ b/thirdparty/dumb/src/it/readany2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readany2.c - Code to detect and read any of the / / \ \ + * module formats supported by DUMB | < / \_ + * from an open file and do an initial | \/ /\ / + * run-through. \_ / > / + * | \ / / + * by Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_read_any(DUMBFILE *f, int restrict_, int subsong) +{ + DUH *duh = dumb_read_any_quick(f, restrict_, subsong); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/readasy.c b/thirdparty/dumb/src/it/readasy.c new file mode 100644 index 000000000..cc77dc39a --- /dev/null +++ b/thirdparty/dumb/src/it/readasy.c @@ -0,0 +1,339 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readasy.c - Code to read an ASYLUM Music Format / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static int it_asy_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer ) +{ + int pos; + int channel; + int row; + IT_ENTRY *entry; + + pattern->n_rows = 64; + + if ( dumbfile_getnc( (char *) buffer, 64 * 8 * 4, f ) != 64 * 8 * 4 ) + return -1; + + /* compute number of entries */ + pattern->n_entries = 64; /* Account for the row end markers */ + pos = 0; + for ( row = 0; row < 64; ++row ) { + for ( channel = 0; channel < 8; ++channel ) { + if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] ) + ++pattern->n_entries; + pos += 4; + } + } + + pattern->entry = malloc( pattern->n_entries * sizeof( *pattern->entry ) ); + if ( !pattern->entry ) + return -1; + + entry = pattern->entry; + pos = 0; + for ( row = 0; row < 64; ++row ) { + for ( channel = 0; channel < 8; ++channel ) { + if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] ) { + entry->channel = channel; + entry->mask = 0; + + if ( buffer[ pos + 0 ] && buffer[ pos + 0 ] < 96 ) { + entry->note = buffer[ pos + 0 ]; + entry->mask |= IT_ENTRY_NOTE; + } + + if ( buffer[ pos + 1 ] && buffer[ pos + 1 ] <= 64 ) { + entry->instrument = buffer[ pos + 1 ]; + entry->mask |= IT_ENTRY_INSTRUMENT; + } + + _dumb_it_xm_convert_effect( buffer[ pos + 2 ], buffer[ pos + 3 ], entry, 1 ); + + // fixup + switch ( entry->effect ) { + case IT_SET_PANNING: + entry->effectvalue <<= 1; + break; + } + + if ( entry->mask ) ++entry; + } + pos += 4; + } + IT_SET_END_ROW( entry ); + ++entry; + } + + pattern->n_entries = (int)(entry - pattern->entry); + + return 0; +} + + + +static int it_asy_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f ) +{ + int finetune, key_offset; + +/** + 21 22 Chars Sample 1 name. If the name is not a full + 22 chars in length, it will be null + terminated. + +If +the sample name begins with a '#' character (ASCII $23 (35)) then this is +assumed not to be an instrument name, and is probably a message. +*/ + dumbfile_getnc( (char *) sample->name, 22, f ); + sample->name[22] = 0; + + sample->filename[0] = 0; + +/** Each finetune step changes the note 1/8th of a semitone. */ + finetune = ( signed char ) ( dumbfile_getc( f ) << 4 ) >> 4; /* signed nibble */ + sample->default_volume = dumbfile_getc( f ); // Should we be setting global_volume to this instead? + sample->global_volume = 64; + if ( sample->default_volume > 64 ) sample->default_volume = 64; + key_offset = ( signed char ) dumbfile_getc( f ); /* base key offset */ + sample->length = dumbfile_igetl( f ); + sample->loop_start = dumbfile_igetl( f ); + sample->loop_end = sample->loop_start + dumbfile_igetl( f ); + + if ( sample->length <= 0 ) { + sample->flags = 0; + return 0; + } + + sample->flags = IT_SAMPLE_EXISTS; + + sample->default_pan = 0; + sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 * pow( DUMB_SEMITONE_BASE, key_offset ) );//( long )( 16726.0 * pow( DUMB_PITCH_BASE, finetune * 32 ) ); + sample->finetune = finetune * 32; + // the above line might be wrong + + if ( ( sample->loop_end - sample->loop_start > 2 ) && ( sample->loop_end <= sample->length ) ) + sample->flags |= IT_SAMPLE_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + + + +static int it_asy_read_sample_data( IT_SAMPLE *sample, DUMBFILE *f ) +{ + int32 truncated_size; + + /* let's get rid of the sample data coming after the end of the loop */ + if ( ( sample->flags & IT_SAMPLE_LOOP ) && sample->loop_end < sample->length ) { + truncated_size = sample->length - sample->loop_end; + sample->length = sample->loop_end; + } else { + truncated_size = 0; + } + + sample->data = malloc( sample->length ); + + if ( !sample->data ) + return -1; + + if ( sample->length ) + dumbfile_getnc( sample->data, sample->length, f ); + + dumbfile_skip( f, truncated_size ); + + return dumbfile_error( f ); +} + + + +static DUMB_IT_SIGDATA *it_asy_load_sigdata(DUMBFILE *f) +{ + DUMB_IT_SIGDATA *sigdata; + int i; + + static const char sig_part[] = "ASYLUM Music Format"; + static const char sig_rest[] = " V1.0"; /* whee, string space optimization with format type below */ + + char signature [32]; + + if ( dumbfile_getnc( signature, 32, f ) != 32 || + memcmp( signature, sig_part, 19 ) || + memcmp( signature + 19, sig_rest, 5 ) ) { + return NULL; + } + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) { + return NULL; + } + + sigdata->speed = dumbfile_getc( f ); /* XXX seems to fit the files I have */ + sigdata->tempo = dumbfile_getc( f ); /* ditto */ + sigdata->n_samples = dumbfile_getc( f ); /* ditto */ + sigdata->n_patterns = dumbfile_getc( f ); + sigdata->n_orders = dumbfile_getc( f ); + sigdata->restart_position = dumbfile_getc( f ); + + if ( dumbfile_error( f ) || !sigdata->n_samples || sigdata->n_samples > 64 || !sigdata->n_patterns || + !sigdata->n_orders ) { + free( sigdata ); + return NULL; + } + + if ( sigdata->restart_position > sigdata->n_orders ) /* XXX */ + sigdata->restart_position = 0; + + sigdata->order = malloc( sigdata->n_orders ); + if ( !sigdata->order ) { + free( sigdata ); + return NULL; + } + + if ( dumbfile_getnc( (char *) sigdata->order, sigdata->n_orders, f ) != sigdata->n_orders || + dumbfile_skip( f, 256 - sigdata->n_orders ) ) { + free( sigdata->order ); + free( sigdata ); + return NULL; + } + + sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) ); + if ( !sigdata->sample ) { + free( sigdata->order ); + free( sigdata ); + return NULL; + } + + sigdata->song_message = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + for ( i = 0; i < sigdata->n_samples; ++i ) + sigdata->sample[i].data = NULL; + + for ( i = 0; i < sigdata->n_samples; ++i ) { + if ( it_asy_read_sample_header( &sigdata->sample[i], f ) ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + + if ( dumbfile_skip( f, 37 * ( 64 - sigdata->n_samples ) ) ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + + sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) ); + if ( !sigdata->pattern ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; ++i) + sigdata->pattern[i].entry = NULL; + + /* Read in the patterns */ + { + unsigned char *buffer = malloc( 64 * 8 * 4 ); /* 64 rows * 8 channels * 4 bytes */ + if ( !buffer ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + for ( i = 0; i < sigdata->n_patterns; ++i ) { + if ( it_asy_read_pattern( &sigdata->pattern[i], f, buffer ) != 0 ) { + free( buffer ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + free( buffer ); + } + + /* And finally, the sample data */ + for ( i = 0; i < sigdata->n_samples; ++i ) { + if ( it_asy_read_sample_data( &sigdata->sample[i], f ) ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + + /* Now let's initialise the remaining variables, and we're done! */ + sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + sigdata->n_pchannels = 8; + + sigdata->name[0] = 0; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i += 4) { + int sep = 32 * dumb_it_default_panning_separation / 100; + sigdata->channel_pan[i+0] = 32 - sep; + sigdata->channel_pan[i+1] = 32 + sep; + sigdata->channel_pan[i+2] = 32 + sep; + sigdata->channel_pan[i+3] = 32 - sep; + } + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *DUMBEXPORT dumb_read_asy_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_asy_load_sigdata(f); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "ASYLUM Music Format"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/readdsmf.c b/thirdparty/dumb/src/it/readdsmf.c new file mode 100644 index 000000000..d64d87950 --- /dev/null +++ b/thirdparty/dumb/src/it/readdsmf.c @@ -0,0 +1,383 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readam.c - Code to read a RIFF DSMF module / / \ \ + * from a parsed RIFF structure. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/it.h" +#include "internal/riff.h" + +static int it_riff_dsmf_process_sample( IT_SAMPLE * sample, DUMBFILE * f, int len ) +{ + int flags; + + dumbfile_getnc( (char *) sample->filename, 13, f ); + sample->filename[ 14 ] = 0; + + flags = dumbfile_igetw( f ); + sample->default_volume = dumbfile_getc( f ); + sample->length = dumbfile_igetl( f ); + sample->loop_start = dumbfile_igetl( f ); + sample->loop_end = dumbfile_igetl( f ); + dumbfile_skip( f, 32 - 28 ); + sample->C5_speed = dumbfile_igetw( f ) * 2; + dumbfile_skip( f, 36 - 34 ); + dumbfile_getnc( (char *) sample->name, 28, f ); + sample->name[ 28 ] = 0; + + /*if ( data[ 0x38 ] || data[ 0x39 ] || data[ 0x3A ] || data[ 0x3B ] ) + return -1;*/ + + if ( ! sample->length ) { + sample->flags &= ~IT_SAMPLE_EXISTS; + return 0; + } + + /*if ( flags & ~( 2 | 1 ) ) + return -1;*/ + + if ( sample->length + 64 > len ) + return -1; + + sample->flags = IT_SAMPLE_EXISTS; + + sample->default_pan = 0; + sample->global_volume = 64; + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + if ( flags & 1 ) + { + if (((unsigned int)sample->loop_end <= (unsigned int)sample->length) && + ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end)) + { + sample->length = sample->loop_end; + sample->flags |= IT_SAMPLE_LOOP; + if ( flags & 0x10 ) sample->flags |= IT_SAMPLE_PINGPONG_LOOP; + } + } + + sample->data = malloc( sample->length ); + if ( ! sample->data ) + return -1; + + dumbfile_getnc( sample->data, sample->length, f ); + + if ( ! ( flags & 2 ) ) + { + for ( flags = 0; flags < sample->length; ++flags ) + ( ( signed char * ) sample->data ) [ flags ] ^= 0x80; + } + + return 0; +} + +static int it_riff_dsmf_process_pattern( IT_PATTERN * pattern, DUMBFILE * f, int len ) +{ + int length, row; + unsigned flags; + long start, end; + int p, q, r; + IT_ENTRY * entry; + + length = dumbfile_igetw( f ); + if ( length > len ) return -1; + + len = length - 2; + + pattern->n_rows = 64; + pattern->n_entries = 64; + + row = 0; + + start = dumbfile_pos( f ); + end = start + len; + + while ( (row < 64) && !dumbfile_error( f ) && (dumbfile_pos( f ) < end) ) { + p = dumbfile_getc( f ); + if ( ! p ) { + ++ row; + continue; + } + + flags = p & 0xF0; + + if (flags) { + ++ pattern->n_entries; + if (flags & 0x80) dumbfile_skip( f, 1 ); + if (flags & 0x40) dumbfile_skip( f, 1 ); + if (flags & 0x20) dumbfile_skip( f, 1 ); + if (flags & 0x10) dumbfile_skip( f, 2 ); + } + } + + if ( pattern->n_entries == 64 ) return 0; + + pattern->entry = malloc( pattern->n_entries * sizeof( * pattern->entry ) ); + if ( ! pattern->entry ) return -1; + + entry = pattern->entry; + + row = 0; + + if ( dumbfile_seek( f, start, DFS_SEEK_SET ) ) return -1; + + while ( ( row < 64 ) && !dumbfile_error( f ) && ( dumbfile_pos( f ) < end ) ) + { + p = dumbfile_getc( f ); + if ( ! p ) + { + IT_SET_END_ROW( entry ); + ++ entry; + ++ row; + continue; + } + + flags = p; + entry->channel = flags & 0x0F; + entry->mask = 0; + + if ( flags & 0xF0 ) + { + if ( flags & 0x80 ) + { + q = dumbfile_getc( f ); + if ( q ) + { + entry->mask |= IT_ENTRY_NOTE; + entry->note = q - 1; + } + } + + if ( flags & 0x40 ) + { + q = dumbfile_getc( f ); + if ( q ) + { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = q; + } + } + + if ( flags & 0x20 ) + { + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = dumbfile_getc( f ); + } + + if ( flags & 0x10 ) + { + q = dumbfile_getc( f ); + r = dumbfile_getc( f ); + _dumb_it_xm_convert_effect( q, r, entry, 0 ); + } + + if (entry->mask) entry++; + } + } + + while ( row < 64 ) + { + IT_SET_END_ROW( entry ); + ++ entry; + ++ row; + } + + pattern->n_entries = (int)(entry - pattern->entry); + if ( ! pattern->n_entries ) return -1; + + return 0; +} + +static DUMB_IT_SIGDATA *it_riff_dsmf_load_sigdata( DUMBFILE * f, struct riff * stream ) +{ + DUMB_IT_SIGDATA *sigdata; + + int n, o, found; + + if ( ! stream ) goto error; + + if ( stream->type != DUMB_ID( 'D', 'S', 'M', 'F' ) ) goto error; + + sigdata = malloc(sizeof(*sigdata)); + if ( ! sigdata ) goto error; + + sigdata->n_patterns = 0; + sigdata->n_samples = 0; + sigdata->name[0] = 0; + + found = 0; + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch( c->type ) + { + case DUMB_ID( 'S' ,'O' ,'N' ,'G' ): + /* initialization data */ + if ( ( found ) || ( c->size < 192 ) ) goto error_sd; + found = 1; + break; + + case DUMB_ID( 'P', 'A', 'T', 'T' ): + ++ sigdata->n_patterns; + break; + + case DUMB_ID( 'I', 'N', 'S', 'T' ): + ++ sigdata->n_samples; + break; + } + } + + if ( !found || !sigdata->n_samples || !sigdata->n_patterns ) goto error_sd; + + if ( sigdata->n_samples > 255 || sigdata->n_patterns > 255 ) goto error_sd; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + sigdata->n_instruments = 0; + sigdata->n_orders = 0; + sigdata->restart_position = 0; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { + int sep = 32 * dumb_it_default_panning_separation / 100; + sigdata->channel_pan[n ] = 32 - sep; + sigdata->channel_pan[n+1] = 32 + sep; + sigdata->channel_pan[n+2] = 32 + sep; + sigdata->channel_pan[n+3] = 32 - sep; + } + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'S', 'O', 'N', 'G' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + dumbfile_getnc( (char *) sigdata->name, 28, f ); + sigdata->name[ 28 ] = 0; + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX; + dumbfile_skip( f, 36 - 28 ); + sigdata->n_orders = dumbfile_igetw( f ); + //sigdata->n_samples = ptr[ 38 ] | ( ptr[ 39 ] << 8 ); // whatever + //sigdata->n_patterns = ptr[ 40 ] | ( ptr[ 41 ] << 8 ); + dumbfile_skip( f, 42 - 38 ); + sigdata->n_pchannels = dumbfile_igetw( f ); + sigdata->global_volume = dumbfile_getc( f ); + sigdata->mixing_volume = dumbfile_getc( f ); + sigdata->speed = dumbfile_getc( f ); + sigdata->tempo = dumbfile_getc( f ); + + for ( o = 0; o < 16; ++o ) + { + sigdata->channel_pan[ o ] = dumbfile_getc( f ) / 2; + } + + sigdata->order = malloc( 128 ); + if ( ! sigdata->order ) goto error_usd; + dumbfile_getnc( (char *) sigdata->order, 128, f ); + + break; + } + } + + sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) ); + if ( ! sigdata->pattern ) goto error_usd; + for ( n = 0; n < sigdata->n_patterns; ++n ) + sigdata->pattern[ n ].entry = NULL; + + sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) ); + if ( ! sigdata->sample ) goto error_usd; + for ( n = 0; n < sigdata->n_samples; ++n ) + { + IT_SAMPLE * sample = sigdata->sample + n; + sample->data = NULL; + } + + sigdata->n_samples = 0; + sigdata->n_patterns = 0; + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'P', 'A', 'T', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + if ( it_riff_dsmf_process_pattern( sigdata->pattern + sigdata->n_patterns, f, c->size ) ) goto error_usd; + ++ sigdata->n_patterns; + break; + + case DUMB_ID( 'I', 'N', 'S', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + if ( it_riff_dsmf_process_sample( sigdata->sample + sigdata->n_samples, f, c->size ) ) goto error_usd; + ++ sigdata->n_samples; + break; + } + } + + _dumb_it_fix_invalid_orders( sigdata ); + + return sigdata; + +error_usd: + _dumb_it_unload_sigdata( sigdata ); + goto error; +error_sd: + free( sigdata ); +error: + return NULL; +} + +DUH *dumb_read_riff_dsmf( DUMBFILE * f, struct riff * stream ) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_riff_dsmf_load_sigdata( f, stream ); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "RIFF DSMF"; + return make_duh( -1, 2, ( const char * const (*) [ 2 ] ) tag, 1, & descptr, & sigdata ); + } +} diff --git a/thirdparty/dumb/src/it/readmod.c b/thirdparty/dumb/src/it/readmod.c new file mode 100644 index 000000000..f73802798 --- /dev/null +++ b/thirdparty/dumb/src/it/readmod.c @@ -0,0 +1,633 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readmod.c - Code to read a good old-fashioned / / \ \ + * Amiga module from an open file. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static int it_mod_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer) +{ + int pos; + int channel; + int row; + IT_ENTRY *entry; + + pattern->n_rows = 64; + + if (n_channels == 0) { + /* Read the first four channels, leaving gaps for the rest. */ + for (pos = 0; pos < 64*8*4; pos += 8*4) + dumbfile_getnc((char *)buffer + pos, 4*4, f); + /* Read the other channels into the gaps we left. */ + for (pos = 4*4; pos < 64*8*4; pos += 8*4) + dumbfile_getnc((char *)buffer + pos, 4*4, f); + + n_channels = 8; + } else + dumbfile_getnc((char *)buffer, 64 * n_channels * 4, f); + + if (dumbfile_error(f)) + return -1; + + /* compute number of entries */ + pattern->n_entries = 64; /* Account for the row end markers */ + pos = 0; + for (row = 0; row < 64; row++) { + for (channel = 0; channel < n_channels; channel++) { + if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3]) + pattern->n_entries++; + pos += 4; + } + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + if (!pattern->entry) + return -1; + + entry = pattern->entry; + pos = 0; + for (row = 0; row < 64; row++) { + for (channel = 0; channel < n_channels; channel++) { + if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3]) { + unsigned char sample = (buffer[pos+0] & 0xF0) | (buffer[pos+2] >> 4); + int period = ((int)(buffer[pos+0] & 0x0F) << 8) | buffer[pos+1]; + + entry->channel = channel; + entry->mask = 0; + + if (period) { + int note; + entry->mask |= IT_ENTRY_NOTE; + + /* frequency = (AMIGA_DIVISOR / 8) / (period * 2) + * C-1: period = 214 -> frequency = 16726 + * so, set C5_speed to 16726 + * and period = 214 should translate to C5 aka 60 + * halve the period, go up an octive + * + * period = 214 / pow(DUMB_SEMITONE_BASE, note - 60) + * pow(DUMB_SEMITONE_BASE, note - 60) = 214 / period + * note - 60 = log(214/period) / log(DUMB_SEMITONE_BASE) + */ + note = (int)floor(log(214.0/period) / log(DUMB_SEMITONE_BASE) + 60.5); + entry->note = MID(0, note, 119); + // or should we preserve the period? + //entry->note = buffer[pos+0] & 0x0F; /* High nibble */ + //entry->volpan = buffer[pos+1]; /* Low byte */ + // and what about finetune? + } + + if (sample) { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = sample; + } + + _dumb_it_xm_convert_effect(buffer[pos+2] & 0x0F, buffer[pos+3], entry, 1); + + entry++; + } + pos += 4; + } + IT_SET_END_ROW(entry); + entry++; + } + + return 0; +} + + + +static int it_mod_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f, int stk) +{ + int finetune, loop_start, loop_length; + +/** + 21 22 Chars Sample 1 name. If the name is not a full + 22 chars in length, it will be null + terminated. + +If +the sample name begins with a '#' character (ASCII $23 (35)) then this is +assumed not to be an instrument name, and is probably a message. +*/ + dumbfile_getnc((char *)sample->name, 22, f); + sample->name[22] = 0; + + sample->filename[0] = 0; + + sample->length = dumbfile_mgetw(f) << 1; + finetune = (signed char)(dumbfile_getc(f) << 4) >> 4; /* signed nibble */ +/** Each finetune step changes the note 1/8th of a semitone. */ + sample->global_volume = 64; + sample->default_volume = dumbfile_getc(f); // Should we be setting global_volume to this instead? + loop_start = dumbfile_mgetw(f); + if ( !stk ) loop_start <<= 1; + loop_length = dumbfile_mgetw(f) << 1; + if ( loop_length > 2 && loop_start + loop_length > sample->length && loop_start / 2 + loop_length <= sample->length ) + loop_start /= 2; + sample->loop_start = loop_start; + sample->loop_end = loop_start + loop_length; +/** +Once this sample has been played completely from beginning +to end, if the repeat length (next field) is greater than two bytes it +will loop back to this position in the sample and continue playing. Once +it has played for the repeat length, it continues to loop back to the +repeat start offset. This means the sample continues playing until it is +told to stop. +*/ + + if (sample->length <= 0) { + sample->flags = 0; + return 0; + } + + sample->flags = IT_SAMPLE_EXISTS; + + sample->default_pan = 0; + sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 ); //(long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32)); + sample->finetune = finetune * 32; + // the above line might be wrong + + if (sample->loop_end > sample->length) + sample->loop_end = sample->length; + + if (sample->loop_end - sample->loop_start > 2) + sample->flags |= IT_SAMPLE_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + + + +static int it_mod_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f, uint32 fft) +{ + int32 i; + int32 truncated_size; + + /* let's get rid of the sample data coming after the end of the loop */ + if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) { + truncated_size = sample->length - sample->loop_end; + sample->length = sample->loop_end; + } else { + truncated_size = 0; + } + + if (sample->length) { + sample->data = malloc(sample->length); + + if (!sample->data) + return -1; + + /* Sample data are stored in "8-bit two's compliment format" (sic). */ + /* + for (i = 0; i < sample->length; i++) + ((signed char *)sample->left)[i] = dumbfile_getc(f); + */ + /* F U Olivier Lapicque */ + if (sample->length >= 5) + { + i = dumbfile_getnc(sample->data, 5, f); + if (i == 5) + { + if (!memcmp(sample->data, "ADPCM", 5)) + { + if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0) + return -1; + + return 0; + } + else + { + i += dumbfile_getnc(((char *)sample->data) + 5, sample->length - 5, f); + } + } + } + else + { + i = dumbfile_getnc(sample->data, sample->length, f); + } + if (i < sample->length) + { + if (i <= 0) + { + sample->flags = 0; + return 0; + } + sample->length = i; + if (sample->loop_end > i) sample->loop_end = i; + // holy crap! + if (sample->loop_start > i) sample->flags &= ~IT_SAMPLE_LOOP; + } + else + { + /* skip truncated data */ + int feh = dumbfile_error(f); + + if (truncated_size) dumbfile_skip(f, truncated_size); + // Should we be truncating it? + + if (feh) + return -1; + } + + if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) { + int delta = 0; + for (i = 0; i < sample->length; i++) { + delta += ((signed char *)sample->data)[i]; + ((signed char *)sample->data)[i] = delta; + } + } + } + + return 0; +} + + + + +#define MOD_FFT_OFFSET (20 + 31*(22+2+1+1+2+2) + 1 + 1 + 128) + +static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict_) +{ + DUMB_IT_SIGDATA *sigdata; + int n_channels; + int i; + uint32 fft; + + if ( dumbfile_seek(f, MOD_FFT_OFFSET, DFS_SEEK_SET) ) + return NULL; + + fft = dumbfile_mgetl(f); + if (dumbfile_error(f)) + return NULL; + + if ( dumbfile_seek(f, 0, DFS_SEEK_SET) ) + return NULL; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) { + return NULL; + } + + /** + 1 20 Chars Title of the song. If the title is not a + full 20 chars in length, it will be null- + terminated. + */ + if (dumbfile_getnc((char *)sigdata->name, 20, f) < 20) { + free(sigdata); + return NULL; + } + sigdata->name[20] = 0; + + sigdata->n_samples = 31; + + switch (fft) { + case DUMB_ID('M','.','K','.'): + case DUMB_ID('M','!','K','!'): + case DUMB_ID('M','&','K','!'): + case DUMB_ID('N','.','T','.'): + case DUMB_ID('N','S','M','S'): + case DUMB_ID('F','L','T','4'): + case DUMB_ID('M',0,0,0): + case DUMB_ID('8',0,0,0): + n_channels = 4; + break; + case DUMB_ID('F','L','T','8'): + n_channels = 0; + /* 0 indicates a special case; two four-channel patterns must be + * combined into one eight-channel pattern. Pattern indexes must + * be halved. Why oh why do they obfuscate so? + */ + /*for (i = 0; i < 128; i++) + sigdata->order[i] >>= 1;*/ + break; + case DUMB_ID('C','D','8','1'): + case DUMB_ID('O','C','T','A'): + case DUMB_ID('O','K','T','A'): + n_channels = 8; + break; + case DUMB_ID('1','6','C','N'): + n_channels = 16; + break; + case DUMB_ID('3','2','C','N'): + n_channels = 32; + break; + default: + /* If we get an illegal tag, assume 4 channels 15 samples. */ + if ((fft & 0x0000FFFFL) == DUMB_ID(0,0,'C','H')) { + if (fft >= '1' << 24 && fft < '4' << 24) { + n_channels = ((fft & 0x00FF0000L) >> 16) - '0'; + if ((unsigned int)n_channels >= 10) { + /* Rightmost character wasn't a digit. */ + n_channels = 4; + sigdata->n_samples = 15; + } else { + n_channels += (((fft & 0xFF000000L) >> 24) - '0') * 10; + /* MODs should really only go up to 32 channels, but we're lenient. */ + if ((unsigned int)(n_channels - 1) >= DUMB_IT_N_CHANNELS - 1) { + /* No channels or too many? Can't be right... */ + n_channels = 4; + sigdata->n_samples = 15; + } + } + } else { + n_channels = 4; + sigdata->n_samples = 15; + } + } else if ((fft & 0x00FFFFFFL) == DUMB_ID(0,'C','H','N')) { + n_channels = (fft >> 24) - '0'; + if ((unsigned int)(n_channels - 1) >= 9) { + /* Character was '0' or it wasn't a digit */ + n_channels = 4; + sigdata->n_samples = 15; + } + } else if ((fft & 0xFFFFFF00L) == DUMB_ID('T','D','Z',0)) { + n_channels = (fft & 0x000000FFL) - '0'; + if ((unsigned int)(n_channels - 1) >= 9) { + /* We've been very lenient, given that it should have + * been 1, 2 or 3, but this MOD has been very naughty and + * must be punished. + */ + n_channels = 4; + sigdata->n_samples = 15; + } + } else { + n_channels = 4; + sigdata->n_samples = 15; + } + } + + // moo + if ( ( restrict_ & 1 ) && sigdata->n_samples == 15 ) + { + free(sigdata); + return NULL; + } + + sigdata->n_pchannels = n_channels ? n_channels : 8; /* special case for 0, see above */ + + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + free(sigdata); + return NULL; + } + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + for (i = 0; i < sigdata->n_samples; i++) + sigdata->sample[i].data = NULL; + + for (i = 0; i < sigdata->n_samples; i++) { + if (it_mod_read_sample_header(&sigdata->sample[i], f, sigdata->n_samples == 15)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + sigdata->n_orders = dumbfile_getc(f); + sigdata->restart_position = dumbfile_getc(f); + // what if this is >= 127? what about with Fast Tracker II? + +/* if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { // is this right? + _dumb_it_unload_sigdata(sigdata); + return NULL; + }*/ + + //if (sigdata->restart_position >= sigdata->n_orders) + //sigdata->restart_position = 0; + + sigdata->order = malloc(128); /* We may need to scan the extra ones! */ + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + if (dumbfile_getnc((char *)sigdata->order, 128, f) < 128) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { // is this right? + sigdata->n_orders = 128; + //while (sigdata->n_orders > 1 && !sigdata->order[sigdata->n_orders - 1]) sigdata->n_orders--; + } + + if ( ! n_channels ) + for (i = 0; i < 128; i++) + sigdata->order[i] >>= 1; + + /* "The old NST format contains only 15 samples (instead of 31). Further + * it doesn't contain a file format tag (id). So Pattern data offset is + * at 20+15*30+1+1+128." + * - Then I shall assume the File Format Tag never exists if there are + * only 15 samples. I hope this isn't a faulty assumption... + */ + if (sigdata->n_samples == 31) + dumbfile_skip(f, 4); + + sigdata->n_patterns = -1; + + if ( ( restrict_ & 2 ) ) + { + unsigned char buffer[5]; + long sample_number; + long total_sample_size; + long offset = dumbfile_pos(f); + long remain = dumbfile_get_size(f) - offset; + if ( dumbfile_error( f ) || + dumbfile_seek( f, 0, SEEK_END ) ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sample_number = sigdata->n_samples - 1; + total_sample_size = 0; + while (dumbfile_pos(f) > offset && sample_number >= 0) { + if (sigdata->sample[sample_number].flags & IT_SAMPLE_EXISTS) { + if ( dumbfile_seek(f, -((sigdata->sample[sample_number].length + 1) / 2 + 5 + 16), DFS_SEEK_CUR) || + dumbfile_getnc((char *)buffer, 5, f) < 5 ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + if ( !memcmp( buffer, "ADPCM", 5 ) ) { /* BAH */ + total_sample_size += (sigdata->sample[sample_number].length + 1) / 2 + 5 + 16; + if ( dumbfile_seek(f, -5, DFS_SEEK_CUR) ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } else { + total_sample_size += sigdata->sample[sample_number].length; + if ( dumbfile_seek(f, -(sigdata->sample[sample_number].length - ((sigdata->sample[sample_number].length + 1) / 2 + 5 + 16) + 5), DFS_SEEK_CUR) ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + } + --sample_number; + } + + if (remain > total_sample_size) { + sigdata->n_patterns = ( remain - total_sample_size + 4 ) / ( 256 * sigdata->n_pchannels ); + if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) { + remain -= sigdata->n_patterns * 256 * sigdata->n_pchannels; + if (dumbfile_skip(f, remain - total_sample_size)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + } + } + else + { + for (i = 0; i < 128; i++) + { + if (sigdata->order[i] > sigdata->n_patterns) + sigdata->n_patterns = sigdata->order[i]; + } + sigdata->n_patterns++; + } + + if ( sigdata->n_patterns <= 0 ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + /* May as well try to save a tiny bit of memory. */ + if (sigdata->n_orders < 128) { + unsigned char *order = realloc(sigdata->order, sigdata->n_orders); + if (order) sigdata->order = order; + } + + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + /* Read in the patterns */ + { + unsigned char *buffer = malloc(256 * sigdata->n_pchannels); /* 64 rows * 4 bytes */ + if (!buffer) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) { + if (it_mod_read_pattern(&sigdata->pattern[i], f, n_channels, buffer) != 0) { + free(buffer); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + free(buffer); + } + + /* And finally, the sample data */ + for (i = 0; i < sigdata->n_samples; i++) { + if (it_mod_read_sample_data(&sigdata->sample[i], f, fft)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + /* w00t! */ + /*if ( n_channels == 4 && + ( sigdata->n_samples == 15 || + ( ( fft & 240 ) != DUMB_ID( 0, 0, 'C', 0 ) && + ( fft & 240 ) != DUMB_ID( 0, 0, 'H', 0 ) && + ( fft & 240 ) != 0 ) ) ) { + for ( i = 0; i < sigdata->n_samples; ++i ) { + IT_SAMPLE * sample = &sigdata->sample [i]; + if ( sample && ( sample->flags & IT_SAMPLE_EXISTS ) ) { + int n, o; + o = sample->length; + if ( o > 4 ) o = 4; + for ( n = 0; n < o; ++n ) + ( ( char * ) sample->data ) [n] = 0; + } + } + }*/ + + /* Now let's initialise the remaining variables, and we're done! */ + sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + /* We want 50 ticks per second; 50/6 row advances per second; + * 50*10=500 row advances per minute; 500/4=125 beats per minute. + */ + sigdata->speed = 6; + sigdata->tempo = 125; + sigdata->pan_separation = 128; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i += 4) { + int sep = 32 * dumb_it_default_panning_separation / 100; + sigdata->channel_pan[i+0] = 32 - sep; + sigdata->channel_pan[i+1] = 32 + sep; + sigdata->channel_pan[i+2] = 32 + sep; + sigdata->channel_pan[i+3] = 32 - sep; + } + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *DUMBEXPORT dumb_read_mod_quick(DUMBFILE *f, int restrict_) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_mod_load_sigdata(f, restrict_); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "MOD"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/readmod2.c b/thirdparty/dumb/src/it/readmod2.c new file mode 100644 index 000000000..e1e7a9ce0 --- /dev/null +++ b/thirdparty/dumb/src/it/readmod2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readmod2.c - Function to read a good old- / / \ \ + * fashioned Amiga module from an | < / \_ + * open file and do an initial | \/ /\ / + * run-through. \_ / > / + * | \ / / + * Split off from readmod.c by entheh. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_read_mod(DUMBFILE *f, int restrict_) +{ + DUH *duh = dumb_read_mod_quick(f, restrict_); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/readmtm.c b/thirdparty/dumb/src/it/readmtm.c new file mode 100644 index 000000000..77c4f9c76 --- /dev/null +++ b/thirdparty/dumb/src/it/readmtm.c @@ -0,0 +1,413 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readmtm.c - Code to read a MultiTracker Module / / \ \ + * from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +static size_t strlen_max(const char * ptr, size_t max) +{ + const char * end, * start; + if (ptr==0) return 0; + start = ptr; + end = ptr + max; + while(*ptr && ptr < end) ptr++; + return ptr - start; +} + +static int it_mtm_assemble_pattern(IT_PATTERN *pattern, const unsigned char * track, const unsigned short * sequence, int n_rows) +{ + int n, o, note, sample; + const unsigned char * t; + IT_ENTRY * entry; + + pattern->n_rows = n_rows; + pattern->n_entries = n_rows; + + for (n = 0; n < 32; n++) { + if (sequence[n]) { + t = &track[192 * (sequence[n] - 1)]; + for (o = 0; o < n_rows; o++) { + if (t[0] || t[1] || t[2]) pattern->n_entries++; + t += 3; + } + } + } + + entry = malloc(pattern->n_entries * sizeof(*entry)); + if (!entry) return -1; + pattern->entry = entry; + + for (n = 0; n < n_rows; n++) { + for (o = 0; o < 32; o++) { + if (sequence[o]) { + t = &track[192 * (sequence[o] - 1) + (n * 3)]; + if (t[0] || t[1] || t[2]) { + entry->channel = o; + entry->mask = 0; + note = t[0] >> 2; + sample = ((t[0] << 4) | (t[1] >> 4)) & 0x3F; + + if (note) { + entry->mask |= IT_ENTRY_NOTE; + entry->note = note + 24; + } + + if (sample) { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = sample; + } + + _dumb_it_xm_convert_effect(t[1] & 0xF, t[2], entry, 1); + + if (entry->mask) entry++; + } + } + } + IT_SET_END_ROW(entry); + entry++; + } + + pattern->n_entries = (int)(entry - pattern->entry); + + return 0; +} + +static int it_mtm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f) +{ + int finetune, flags; + + dumbfile_getnc((char *)sample->name, 22, f); + sample->name[22] = 0; + + sample->filename[0] = 0; + + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = dumbfile_igetl(f); + finetune = (signed char)(dumbfile_getc(f) << 4) >> 4; /* signed nibble */ + sample->global_volume = 64; + sample->default_volume = dumbfile_getc(f); + + flags = dumbfile_getc(f); + + if (sample->length <= 0) { + sample->flags = 0; + return 0; + } + + sample->flags = IT_SAMPLE_EXISTS; + + if (flags & 1) { + sample->flags |= IT_SAMPLE_16BIT; + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } + + sample->default_pan = 0; + sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 );//(long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32)); + sample->finetune = finetune * 32; + // the above line might be wrong + + if (sample->loop_end > sample->length) + sample->loop_end = sample->length; + + if (sample->loop_end - sample->loop_start > 2) + sample->flags |= IT_SAMPLE_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + +static int it_mtm_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f) +{ + int32 i; + int32 truncated_size; + + /* let's get rid of the sample data coming after the end of the loop */ + if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) { + truncated_size = sample->length - sample->loop_end; + sample->length = sample->loop_end; + } else { + truncated_size = 0; + } + + sample->data = malloc(sample->length); + + if (!sample->data) + return -1; + + dumbfile_getnc((char *)sample->data, sample->length, f); + dumbfile_skip(f, truncated_size); + + if (dumbfile_error(f)) + return -1; + + for (i = 0; i < sample->length; i++) + ((signed char *)sample->data)[i] ^= 0x80; + + return 0; +} + +static DUMB_IT_SIGDATA *it_mtm_load_sigdata(DUMBFILE *f, int * version) +{ + DUMB_IT_SIGDATA *sigdata; + + int n, o, n_tracks, l_comment, n_rows, n_channels; + + unsigned char * track; + + unsigned short * sequence; + + char * comment; + + if (dumbfile_getc(f) != 'M' || + dumbfile_getc(f) != 'T' || + dumbfile_getc(f) != 'M') goto error; + + *version = dumbfile_getc(f); + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) goto error; + + dumbfile_getnc((char *)sigdata->name, 20, f); + sigdata->name[20] = 0; + + n_tracks = dumbfile_igetw(f); + sigdata->n_patterns = dumbfile_getc(f) + 1; + sigdata->n_orders = dumbfile_getc(f) + 1; + l_comment = dumbfile_igetw(f); + sigdata->n_samples = dumbfile_getc(f); + //if (dumbfile_getc(f)) goto error_sd; + dumbfile_getc(f); + n_rows = dumbfile_getc(f); + n_channels = dumbfile_getc(f); + + if (dumbfile_error(f) || + (n_tracks <= 0) || + (sigdata->n_samples <= 0) || + (n_rows <= 0 || n_rows > 64) || + (n_channels <= 0 || n_channels > 32)) goto error_sd; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + if (dumbfile_getnc((char *)sigdata->channel_pan, 32, f) < 32) goto error_sd; + + for (n = 0; n < 32; n++) { + if (sigdata->channel_pan[n] <= 15) { + sigdata->channel_pan[n] -= (sigdata->channel_pan[n] & 8) >> 3; + sigdata->channel_pan[n] = (sigdata->channel_pan[n] * 32) / 7; + } else { + sigdata->channel_volume[n] = 0; + sigdata->channel_pan[n] = 7; + } + } + + for (n = 32; n < DUMB_IT_N_CHANNELS; n += 4) { + int sep = 32 * dumb_it_default_panning_separation / 100; + sigdata->channel_pan[n ] = 32 - sep; + sigdata->channel_pan[n+1] = 32 + sep; + sigdata->channel_pan[n+2] = 32 + sep; + sigdata->channel_pan[n+3] = 32 - sep; + } + + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) goto error_sd; + + sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + sigdata->speed = 6; + sigdata->tempo = 125; + sigdata->pan_separation = 128; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + sigdata->restart_position = 0; + sigdata->n_pchannels = n_channels; + + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + + for (n = 0; n < sigdata->n_samples; n++) { + if (it_mtm_read_sample_header(&sigdata->sample[n], f)) goto error_usd; + } + + sigdata->order = malloc(sigdata->n_orders); + if (!sigdata->order) goto error_usd; + + if (dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f) < sigdata->n_orders) goto error_usd; + if (sigdata->n_orders < 128) + if (dumbfile_skip(f, 128 - sigdata->n_orders)) goto error_usd; + + track = malloc(192 * n_tracks); + if (!track) goto error_usd; + + if (dumbfile_getnc((char *)track, 192 * n_tracks, f) < 192 * n_tracks) goto error_ft; + + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) goto error_ft; + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + + sequence = malloc(sigdata->n_patterns * 32 * sizeof(*sequence)); + if (!sequence) goto error_ft; + + for (n = 0; n < sigdata->n_patterns; n++) { + for (o = 0; o < 32; o++) { + sequence[(n * 32) + o] = dumbfile_igetw(f); + if (sequence[(n * 32) + o] > n_tracks) + { + //goto error_fs; + // illegal track number, silence instead of rejecting the file + sequence[(n * 32) + o] = 0; + } + } + } + + for (n = 0; n < sigdata->n_patterns; n++) { + if (it_mtm_assemble_pattern(&sigdata->pattern[n], track, &sequence[n * 32], n_rows)) goto error_fs; + } + + if (l_comment) { + comment = malloc(l_comment); + if (!comment) goto error_fs; + if (dumbfile_getnc(comment, l_comment, f) < l_comment) goto error_fc; + + /* Time for annoying "logic", yes. We want each line which has text, + * and each blank line in between all the valid lines. + */ + + /* Find last actual line. */ + for (o = -1, n = 0; n < l_comment; n += 40) { + if (comment[n]) o = n; + } + + if (o >= 0) { + + size_t l; + int m; + for (l = 0, n = 0; n <= o; n += 40) { + l += strlen_max(&comment[n], 40) + 2; + } + + l -= 1; + + sigdata->song_message = malloc(l); + if (!sigdata->song_message) goto error_fc; + + for (m = 0, n = 0; n <= o; n += 40) { + int p = (int)strlen_max(&comment[n], 40); + if (p) { + memcpy(sigdata->song_message + m, &comment[n], p); + m += p; + } + if (l - m > 1) { + sigdata->song_message[m++] = 13; + sigdata->song_message[m++] = 10; + } + } + + sigdata->song_message[m] = 0; + } + + free(comment); + } + + for (n = 0; n < sigdata->n_samples; n++) { + if (it_mtm_read_sample_data(&sigdata->sample[n], f)) goto error_fs; + } + + _dumb_it_fix_invalid_orders(sigdata); + + free(sequence); + free(track); + + return sigdata; + +error_fc: + free(comment); +error_fs: + free(sequence); +error_ft: + free(track); +error_usd: + _dumb_it_unload_sigdata(sigdata); + return NULL; + +error_sd: + free(sigdata); +error: + return NULL; +} + +static char hexdigit(int in) +{ + if (in < 10) return in + '0'; + else return in + 'A' - 10; +} + +DUH *DUMBEXPORT dumb_read_mtm_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + int ver; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_mtm_load_sigdata(f, &ver); + + if (!sigdata) + return NULL; + + { + char version[16]; + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + version[0] = 'M'; + version[1] = 'T'; + version[2] = 'M'; + version[3] = ' '; + version[4] = 'v'; + version[5] = hexdigit(ver >> 4); + version[6] = '.'; + version[7] = hexdigit(ver & 15); + version[8] = 0; + tag[1][1] = (const char *) &version; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/readokt.c b/thirdparty/dumb/src/it/readokt.c new file mode 100644 index 000000000..c1dc1ce13 --- /dev/null +++ b/thirdparty/dumb/src/it/readokt.c @@ -0,0 +1,558 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readokt.c - Code to read an Oktalyzer module / / \ \ + * from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static int it_okt_read_pattern(IT_PATTERN *pattern, const unsigned char *data, int length, int n_channels) +{ + int pos; + int channel; + int row; + int n_rows; + IT_ENTRY *entry; + + if (length < 2) return -1; + + n_rows = (data[0] << 8) | data[1]; + if (!n_rows) n_rows = 64; + + if (length < 2 + (n_rows * n_channels * 4)) return -1; + + pattern->n_rows = n_rows; + + /* compute number of entries */ + pattern->n_entries = n_rows; /* Account for the row end markers */ + pos = 2; + for (row = 0; row < pattern->n_rows; row++) { + for (channel = 0; channel < n_channels; channel++) { + if (data[pos+0] | data[pos+2]) + pattern->n_entries++; + pos += 4; + } + } + + pattern->entry = (IT_ENTRY *) malloc(pattern->n_entries * sizeof(*pattern->entry)); + if (!pattern->entry) + return -1; + + entry = pattern->entry; + pos = 2; + for (row = 0; row < n_rows; row++) { + for (channel = 0; channel < n_channels; channel++) { + if (data[pos+0] | data[pos+2]) { + entry->channel = channel; + entry->mask = 0; + + if (data[pos+0] > 0 && data[pos+0] <= 36) { + entry->mask |= IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT; + + entry->note = data[pos+0] + 35; + entry->instrument = data[pos+1] + 1; + } + + entry->effect = 0; + entry->effectvalue = data[pos+3]; + + switch (data[pos+2]) { + case 2: if (data[pos+3]) entry->effect = IT_PORTAMENTO_DOWN; break; // XXX code calls this rs_portu, but it's adding to the period, which decreases the pitch + case 13: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_DOWN; break; + case 21: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_DOWN_ROW; break; + + case 1: if (data[pos+3]) entry->effect = IT_PORTAMENTO_UP; break; // XXX same deal here, increasing the pitch + case 17: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_UP; break; + case 30: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_UP_ROW; break; + + case 10: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_3; break; + case 11: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_4; break; + case 12: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_5; break; + + case 15: entry->effect = IT_S; entry->effectvalue = EFFECT_VALUE(IT_S_SET_FILTER, data[pos+3] & 0x0F); break; + + case 25: entry->effect = IT_JUMP_TO_ORDER; break; + + case 27: entry->note = IT_NOTE_OFF; entry->mask |= IT_ENTRY_NOTE; break; + + case 28: entry->effect = IT_SET_SPEED; break; + + case 31: + if ( data[pos+3] <= 0x40 ) entry->effect = IT_SET_CHANNEL_VOLUME; + else if ( data[pos+3] <= 0x50 ) { entry->effect = IT_OKT_VOLUME_SLIDE_DOWN; entry->effectvalue = data[pos+3] - 0x40; } + else if ( data[pos+3] <= 0x60 ) { entry->effect = IT_OKT_VOLUME_SLIDE_UP; entry->effectvalue = data[pos+3] - 0x50; } + else if ( data[pos+3] <= 0x70 ) { entry->effect = IT_OKT_VOLUME_SLIDE_DOWN; entry->effectvalue = data[pos+3] - 0x50; } + else if ( data[pos+3] <= 0x80 ) { entry->effect = IT_OKT_VOLUME_SLIDE_UP; entry->effectvalue = data[pos+3] - 0x60; } + break; + } + + if ( entry->effect ) entry->mask |= IT_ENTRY_EFFECT; + + entry++; + } + pos += 4; + } + IT_SET_END_ROW(entry); + entry++; + } + + return 0; +} + + + +static void it_okt_read_sample_header(IT_SAMPLE *sample, const unsigned char * data) +{ + int loop_start, loop_length; + + memcpy(sample->name, data, 20); + sample->name[20] = 0; + + sample->filename[0] = 0; + + sample->length = (data[20] << 24) | (data[21] << 16) | (data[22] << 8) | data[23]; + sample->global_volume = 64; + sample->default_volume = data[29]; + loop_start = ((data[24] << 8) | data[25]) << 1; + loop_length = ((data[26] << 8) | data[27]) << 1; + sample->sus_loop_start = loop_start; + sample->sus_loop_end = loop_start + loop_length; + + if (sample->length <= 0) { + sample->flags = 0; + return; + } + + sample->flags = IT_SAMPLE_EXISTS; + + sample->default_pan = 0; + sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 ); //(long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32)); + sample->finetune = 0; + + if (sample->sus_loop_end > sample->length) + sample->sus_loop_end = sample->length; + + if (loop_length > 2) + sample->flags |= IT_SAMPLE_SUS_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->max_resampling_quality = -1; +} + + + +static int it_okt_read_sample_data(IT_SAMPLE *sample, const char * data, int length) +{ + if (length && sample->length) { + if (length < sample->length) { + sample->length = length; + if (length < sample->sus_loop_end) sample->sus_loop_end = length; + } + + sample->data = malloc(length); + + if (!sample->data) + return -1; + + memcpy(sample->data, data, length); + } + + return 0; +} + + + +typedef struct IFF_CHUNK IFF_CHUNK; +typedef struct IFF_CHUNKED IFF_CHUNKED; + +struct IFF_CHUNK +{ + unsigned type; + unsigned char * data; + unsigned size; +}; + +struct IFF_CHUNKED +{ + unsigned chunk_count; + IFF_CHUNK * chunks; +}; + + + +static IFF_CHUNKED *dumbfile_read_okt(DUMBFILE *f) +{ + IFF_CHUNKED *mod = (IFF_CHUNKED *) malloc(sizeof(*mod)); + if (!mod) return NULL; + + mod->chunk_count = 0; + mod->chunks = 0; + + for (;;) + { + long bytes_read; + IFF_CHUNK * chunk = ( IFF_CHUNK * ) realloc( mod->chunks, ( mod->chunk_count + 1 ) * sizeof( IFF_CHUNK ) ); + if ( !chunk ) + { + if ( mod->chunks ) free( mod->chunks ); + free( mod ); + return NULL; + } + mod->chunks = chunk; + chunk += mod->chunk_count; + + bytes_read = dumbfile_mgetl( f ); + if ( bytes_read < 0 ) break; + + chunk->type = bytes_read; + chunk->size = dumbfile_mgetl( f ); + + if ( dumbfile_error( f ) ) break; + + chunk->data = (unsigned char *) malloc( chunk->size ); + if ( !chunk->data ) + { + free( mod->chunks ); + free( mod ); + return NULL; + } + + bytes_read = dumbfile_getnc( ( char * ) chunk->data, chunk->size, f ); + if ( bytes_read < (long)chunk->size ) + { + if ( bytes_read <= 0 ) { + free( chunk->data ); + break; + } else { + chunk->size = bytes_read; + mod->chunk_count++; + break; + } + } + + mod->chunk_count++; + } + + if ( !mod->chunk_count ) { + if ( mod->chunks ) free(mod->chunks); + free(mod); + mod = NULL; + } + + return mod; +} + +void free_okt(IFF_CHUNKED * mod) +{ + unsigned i; + if (mod) + { + if (mod->chunks) + { + for (i = 0; i < mod->chunk_count; i++) + { + if (mod->chunks[i].data) free(mod->chunks[i].data); + } + free(mod->chunks); + } + free(mod); + } +} + +const IFF_CHUNK * get_chunk_by_type(IFF_CHUNKED * mod, unsigned type, unsigned offset) +{ + unsigned i; + if (mod) + { + if (mod->chunks) + { + for (i = 0; i < mod->chunk_count; i++) + { + if (mod->chunks[i].type == type) + { + if (!offset) return &mod->chunks[i]; + else offset--; + } + } + } + } + return NULL; +} + +unsigned get_chunk_count(IFF_CHUNKED *mod, unsigned type) +{ + unsigned i, count = 0; + if (mod) + { + if (mod->chunks) + { + for (i = 0; i < mod->chunk_count; i++) + { + if (mod->chunks[i].type == type) count++; + } + } + } + return count; +} + + +static DUMB_IT_SIGDATA *it_okt_load_sigdata(DUMBFILE *f) +{ + DUMB_IT_SIGDATA *sigdata; + int n_channels; + int i, j, k, l; + IFF_CHUNKED *mod; + const IFF_CHUNK *chunk; + + char signature[8]; + + if (dumbfile_getnc(signature, 8, f) < 8 || + memcmp(signature, "OKTASONG", 8)) { + return NULL; + } + + mod = dumbfile_read_okt(f); + if (!mod) + return NULL; + + sigdata = (DUMB_IT_SIGDATA *) malloc(sizeof(*sigdata)); + if (!sigdata) { + free_okt(mod); + return NULL; + } + + sigdata->name[0] = 0; + + chunk = get_chunk_by_type(mod, DUMB_ID('S','P','E','E'), 0); + if (!chunk || chunk->size < 2) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->speed = (chunk->data[0] << 8) | chunk->data[1]; + + chunk = get_chunk_by_type(mod, DUMB_ID('S','A','M','P'), 0); + if (!chunk || chunk->size < 32) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_samples = chunk->size / 32; + + chunk = get_chunk_by_type(mod, DUMB_ID('C','M','O','D'), 0); + if (!chunk || chunk->size < 8) { + free(sigdata); + free_okt(mod); + return NULL; + } + + n_channels = 0; + + for (i = 0; i < 4; i++) { + j = (chunk->data[i * 2] << 8) | chunk->data[i * 2 + 1]; + if (!j) n_channels++; + else if (j == 1) n_channels += 2; + } + + if (!n_channels) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_pchannels = n_channels; + + sigdata->sample = (IT_SAMPLE *) malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + for (i = 0; (unsigned)i < (unsigned)sigdata->n_samples; i++) + sigdata->sample[i].data = NULL; + + chunk = get_chunk_by_type(mod, DUMB_ID('S','A','M','P'), 0); + + for (i = 0; (unsigned)i < (unsigned)sigdata->n_samples; i++) { + it_okt_read_sample_header(&sigdata->sample[i], chunk->data + 32 * i); + } + + sigdata->restart_position = 0; + + chunk = get_chunk_by_type(mod, DUMB_ID('P','L','E','N'), 0); + if (!chunk || chunk->size < 2) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_orders = (chunk->data[0] << 8) | chunk->data[1]; + // what if this is > 128? + + if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + chunk = get_chunk_by_type(mod, DUMB_ID('P','A','T','T'), 0); + if (!chunk || chunk->size < (unsigned)sigdata->n_orders) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->order = (unsigned char *) malloc(sigdata->n_orders); + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + memcpy(sigdata->order, chunk->data, sigdata->n_orders); + + /* Work out how many patterns there are. */ + chunk = get_chunk_by_type(mod, DUMB_ID('S','L','E','N'), 0); + if (!chunk || chunk->size < 2) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_patterns = (chunk->data[0] << 8) | chunk->data[1]; + + j = get_chunk_count(mod, DUMB_ID('P','B','O','D')); + if (sigdata->n_patterns > (int)j) sigdata->n_patterns = (int)j; + + if (!sigdata->n_patterns) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->pattern = (IT_PATTERN *) malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + for (i = 0; (unsigned)i < (unsigned)sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + /* Read in the patterns */ + for (i = 0; (unsigned)i < (unsigned)sigdata->n_patterns; i++) { + chunk = get_chunk_by_type(mod, DUMB_ID('P','B','O','D'), i); + if (it_okt_read_pattern(&sigdata->pattern[i], chunk->data, chunk->size, n_channels) != 0) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + } + + /* And finally, the sample data */ + k = get_chunk_count(mod, DUMB_ID('S','B','O','D')); + for (i = 0, j = 0; (unsigned)i < (unsigned)sigdata->n_samples && j < k; i++) { + if (sigdata->sample[i].flags & IT_SAMPLE_EXISTS) { + chunk = get_chunk_by_type(mod, DUMB_ID('S','B','O','D'), j); + if (it_okt_read_sample_data(&sigdata->sample[i], (const char *)chunk->data, chunk->size)) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + j++; + } + } + for (; (unsigned)i < (unsigned)sigdata->n_samples; i++) { + sigdata->sample[i].flags = 0; + } + + chunk = get_chunk_by_type(mod, DUMB_ID('C','M','O','D'), 0); + + for (i = 0, j = 0; i < n_channels && j < 4; j++) { + k = (chunk->data[j * 2] << 8) | chunk->data[j * 2 + 1]; + l = (j == 1 || j == 2) ? 48 : 16; + if (k == 0) { + sigdata->channel_pan[i++] = l; + } + else if (k == 1) { + sigdata->channel_pan[i++] = l; + sigdata->channel_pan[i++] = l; + } + } + + free_okt(mod); + + /* Now let's initialise the remaining variables, and we're done! */ + sigdata->flags = IT_WAS_AN_OKT | IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + /* We want 50 ticks per second; 50/6 row advances per second; + * 50*10=500 row advances per minute; 500/4=125 beats per minute. + */ + sigdata->tempo = 125; + sigdata->pan_separation = 128; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + memset(sigdata->channel_pan + n_channels, 32, DUMB_IT_N_CHANNELS - n_channels); + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *DUMBEXPORT dumb_read_okt_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_okt_load_sigdata(f); + + if (!sigdata) + return NULL; + + { + const char *tag[1][2]; + tag[0][0] = "FORMAT"; + tag[0][1] = "Oktalyzer"; + return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/readokt2.c b/thirdparty/dumb/src/it/readokt2.c new file mode 100644 index 000000000..ef54b8d0d --- /dev/null +++ b/thirdparty/dumb/src/it/readokt2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readokt2.c - Function to read an Oktalyzer / / \ \ + * module from an open file and do | < / \_ + * an initial run-through. | \/ /\ / + * \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_read_okt(DUMBFILE *f) +{ + DUH *duh = dumb_read_okt_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/readoldpsm.c b/thirdparty/dumb/src/it/readoldpsm.c new file mode 100644 index 000000000..3946568b8 --- /dev/null +++ b/thirdparty/dumb/src/it/readoldpsm.c @@ -0,0 +1,689 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readpsm.c - Code to read an old Protracker / / \ \ + * Studio module from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +static int CDECL psm_sample_compare(const void *e1, const void *e2) +{ + const unsigned char * pa = e1; + const unsigned char * pb = e2; + int a = pa[37] | (pa[38] << 8) | (pa[39] << 16) | (pa[40] << 24); + int b = pb[37] | (pb[38] << 8) | (pb[39] << 16) | (pb[40] << 24); + return a - b; +} + +static int it_old_psm_read_samples(IT_SAMPLE ** sample, DUMBFILE * f, int * num) +{ + int n, o, count = *num, true_num, snum, offset, flags, finetune, delta; + + unsigned char * buffer; + const unsigned char * sdata; + int32 sample_bytes; + + buffer = malloc(count * 64); + if (!buffer) goto error; + + if (dumbfile_getnc((char *)buffer, count * 64, f) < count * 64) goto error_fb; + + true_num = 0; + + for (n = 0; n < count; n++) { + snum = buffer[(n * 64) + 45] | (buffer[(n * 64) + 46] << 8); + if ((snum < 1) || (snum > 255)) goto error_fb; + if (true_num < snum) true_num = snum; + } + + if (true_num > count) { + IT_SAMPLE * meh = realloc(*sample, true_num * sizeof(*meh)); + if (!meh) goto error_fb; + for (n = count; n < true_num; n++) { + meh[n].data = NULL; + } + *sample = meh; + *num = true_num; + } + + qsort(buffer, count, 64, &psm_sample_compare); + + for (n = 0; n < true_num; n++) { + (*sample)[n].flags = 0; + } + + for (n = 0; n < count; n++) { + IT_SAMPLE * s; + snum = buffer[(n * 64) + 45] | (buffer[(n * 64) + 46] << 8); + s = &((*sample)[snum - 1]); + memcpy(s->filename, buffer + (n * 64), 13); + s->filename[13] = 0; + memcpy(s->name, buffer + (n * 64) + 13, 24); + s->name[24] = 0; + offset = buffer[(n * 64) + 37] | (buffer[(n * 64) + 38] << 8) | + (buffer[(n * 64) + 39] << 16) | (buffer[(n * 64) + 40] << 24); + flags = buffer[(n * 64) + 47]; + s->length = buffer[(n * 64) + 48] | (buffer[(n * 64) + 49] << 8) | + (buffer[(n * 64) + 50] << 16) | (buffer[(n * 64) + 51] << 24); + s->loop_start = buffer[(n * 64) + 52] | (buffer[(n * 64) + 53] << 8) | + (buffer[(n * 64) + 54] << 16) | (buffer[(n * 64) + 55] << 24); + s->loop_end = buffer[(n * 64) + 56] | (buffer[(n * 64) + 57] << 8) | + (buffer[(n * 64) + 58] << 16) | (buffer[(n * 64) + 59] << 24); + + if (s->length <= 0) continue; + + finetune = buffer[(n * 64) + 60]; + s->default_volume = buffer[(n * 64) + 61]; + s->C5_speed = buffer[(n * 64) + 62] | (buffer[(n * 64) + 63] << 8); + if (finetune & 15) { + finetune &= 15; + if (finetune >= 8) finetune -= 16; + //s->C5_speed = (long)((double)s->C5_speed * pow(DUMB_PITCH_BASE, finetune*32)); + s->finetune = finetune * 32; + } + else s->finetune = 0; + + s->flags |= IT_SAMPLE_EXISTS; + if (flags & 0x41) { + s->flags &= ~IT_SAMPLE_EXISTS; + continue; + } + if (flags & 0x20) s->flags |= IT_SAMPLE_PINGPONG_LOOP; + if (flags & 4) s->flags |= IT_SAMPLE_16BIT; + + if (flags & 0x80) { + s->flags |= IT_SAMPLE_LOOP; + if ((unsigned int)s->loop_end > (unsigned int)s->length) + s->loop_end = s->length; + else if ((unsigned int)s->loop_start >= (unsigned int)s->loop_end) + s->flags &= ~IT_SAMPLE_LOOP; + else + s->length = s->loop_end; + } + + s->global_volume = 64; + + s->vibrato_speed = 0; + s->vibrato_depth = 0; + s->vibrato_rate = 0; + s->vibrato_waveform = IT_VIBRATO_SINE; + s->max_resampling_quality = -1; + + sample_bytes = s->length * ((flags & 4) ? 2 : 1); + s->data = malloc(sample_bytes); + if (!s->data) goto error_fb; + + if (dumbfile_seek(f, offset, DFS_SEEK_SET) || dumbfile_getnc(s->data, sample_bytes, f) < sample_bytes) goto error_fb; + sdata = ( const unsigned char * ) s->data; + + if (flags & 0x10) { + if (flags & 8) { + if (flags & 4) { + for (o = 0; o < s->length; o++) + ((short *)s->data)[o] = (sdata[o * 2] | (sdata[(o * 2) + 1] << 8)) ^ 0x8000; + } else { + for (o = 0; o < s->length; o++) + ((signed char *)s->data)[o] = sdata[o] ^ 0x80; + } + } else { + if (flags & 4) { + for (o = 0; o < s->length; o++) + ((short *)s->data)[o] = sdata[o * 2] | (sdata[(o * 2) + 1] << 8); + } else { + memcpy(s->data, sdata, s->length); + } + } + } else { + delta = 0; + if (flags & 8) { + /* unsigned delta? mehhh, does anything even use this? */ + if (flags & 4) { + for (o = 0; o < s->length; o++) { + delta += (short)(sdata[o * 2] | (sdata[(o * 2) + 1] << 8)); + ((short *)s->data)[o] = delta ^ 0x8000; + } + } else { + for (o = 0; o < s->length; o++) { + delta += (signed char)sdata[o]; + ((signed char *)s->data)[o] = delta ^ 0x80; + } + } + } else { + if (flags & 4) { + for (o = 0; o < s->length; o++) { + delta += (short)(sdata[o * 2] | (sdata[(o * 2) + 1] << 8)); + ((short *)s->data)[o] = delta; + } + } else { + for (o = 0; o < s->length; o++) { + delta += (signed char)sdata[o]; + ((signed char *)s->data)[o] = delta; + } + } + } + } + } + + free(buffer); + + return 0; + +error_fb: + free(buffer); +error: + return -1; +} + +static int it_old_psm_read_patterns(IT_PATTERN * pattern, DUMBFILE * f, int num, int size, int pchans) +{ + int n, offset, psize, rows, chans, row, flags, channel; + + unsigned char * buffer, * ptr, * end; + + IT_ENTRY * entry; + + buffer = malloc(size); + if (!buffer) goto error; + + if (dumbfile_getnc((char *)buffer, size, f) < size) goto error_fb; + + offset = 0; + + for (n = 0; n < num; n++) { + IT_PATTERN * p = &pattern[n]; + + if (offset >= size) goto error_fb; + + ptr = buffer + offset; + psize = ptr[0] | (ptr[1] << 8); + rows = ptr[2]; + chans = ptr[3]; + + if (!rows || !chans) { + p->n_rows = 1; + p->n_entries = 0; + continue; + } + + psize = (psize + 15) & ~15; + + end = ptr + psize; + ptr += 4; + + p->n_rows = rows; + p->n_entries = rows; + row = 0; + + while ((row < rows) && (ptr < end)) { + flags = *ptr++; + if (!flags) { + row++; + continue; + } + if (flags & 0xE0) { + p->n_entries++; + if (flags & 0x80) ptr += 2; + if (flags & 0x40) ptr++; + if (flags & 0x20) { + ptr++; + if (*ptr == 40) ptr += 3; + else ptr++; + } + } + } + + entry = malloc(p->n_entries * sizeof(*p->entry)); + if (!entry) goto error_fb; + + p->entry = entry; + + ptr = buffer + offset + 4; + row = 0; + + while ((row < rows) && (ptr < end)) { + flags = *ptr++; + if (!flags) { + IT_SET_END_ROW(entry); + entry++; + row++; + continue; + } + if (flags & 0xE0) { + entry->mask = 0; + entry->channel = channel = flags & 0x1F; + if (channel >= chans) + { + //channel = 0; + //goto error_fb; + } + if (flags & 0x80) { + if ((*ptr < 60) && (channel < pchans)) { + entry->mask |= IT_ENTRY_NOTE; + entry->note = *ptr + 35; + } + ptr++; + if (*ptr) { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = *ptr; + } + ptr++; + } + if (flags & 0x40) { + if (*ptr <= 64) { + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = *ptr; + } + ptr++; + } + if (flags & 0x20) { + entry->mask |= IT_ENTRY_EFFECT; + + switch (*ptr) { + case 1: + entry->effect = IT_XM_FINE_VOLSLIDE_UP; + entry->effectvalue = ptr[1]; + break; + + case 2: + entry->effect = IT_VOLUME_SLIDE; + entry->effectvalue = (ptr[1] << 4) & 0xF0; + break; + + case 3: + entry->effect = IT_XM_FINE_VOLSLIDE_DOWN; + entry->effectvalue = ptr[1]; + break; + + case 4: + entry->effect = IT_VOLUME_SLIDE; + entry->effectvalue = ptr[1] & 0xF; + break; + + case 10: + entry->effect = IT_PORTAMENTO_UP; + entry->effectvalue = EFFECT_VALUE(0xF, ptr[1]); + break; + + case 11: + entry->effect = IT_PORTAMENTO_UP; + entry->effectvalue = ptr[1]; + break; + + case 12: + entry->effect = IT_PORTAMENTO_DOWN; + entry->effectvalue = EFFECT_VALUE(ptr[1], 0xF); + break; + + case 13: + entry->effect = IT_PORTAMENTO_DOWN; + entry->effectvalue = ptr[1]; + break; + + case 14: + entry->effect = IT_TONE_PORTAMENTO; + entry->effectvalue = ptr[1]; + break; + + case 15: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_SET_GLISSANDO_CONTROL, ptr[1] & 15); + break; + + case 16: + entry->effect = IT_VOLSLIDE_TONEPORTA; + entry->effectvalue = ptr[1] << 4; + break; + + case 17: + entry->effect = IT_VOLSLIDE_TONEPORTA; + entry->effectvalue = ptr[1] & 0xF; + break; + + case 20: + entry->effect = IT_VIBRATO; + entry->effectvalue = ptr[1]; + break; + + case 21: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_SET_VIBRATO_WAVEFORM, ptr[1] & 11); + break; + + case 22: + entry->effect = IT_VOLSLIDE_VIBRATO; + entry->effectvalue = ptr[1] << 4; + break; + + case 23: + entry->effect = IT_VOLSLIDE_VIBRATO; + entry->effectvalue = ptr[1] & 0xF; + break; + + case 30: + entry->effect = IT_TREMOLO; + entry->effectvalue = ptr[1]; + break; + + case 31: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_SET_TREMOLO_WAVEFORM, ptr[1] & 11); + break; + + case 40: + entry->effect = IT_SET_SAMPLE_OFFSET; + entry->effectvalue = ptr[2]; + ptr += 2; + break; + + case 41: + entry->effect = IT_XM_RETRIGGER_NOTE; + entry->effectvalue = ptr[1]; + break; + + case 42: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_DELAYED_NOTE_CUT, ptr[1] & 0xF); + break; + + case 43: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_NOTE_DELAY, ptr[1] & 0xF); + break; + + case 50: + entry->effect = IT_JUMP_TO_ORDER; + entry->effectvalue = ptr[1]; + break; + + case 51: + entry->effect = IT_BREAK_TO_ROW; + entry->effectvalue = ptr[1]; + break; + + case 52: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_PATTERN_LOOP, ptr[1] & 0xF); + break; + + case 53: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_PATTERN_DELAY, ptr[1] & 0xF); + break; + + case 60: + entry->effect = IT_SET_SPEED; + entry->effectvalue = ptr[1]; + break; + + case 61: + entry->effect = IT_SET_SONG_TEMPO; + entry->effectvalue = ptr[1]; + break; + + case 70: + entry->effect = IT_ARPEGGIO; + entry->effectvalue = ptr[1]; + break; + + case 71: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_FINETUNE, ptr[1] & 0xF); + break; + + case 72: + /* "balance" ... panning? */ + entry->effect = IT_SET_PANNING; + entry->effectvalue = ((ptr[1] - ((ptr[1] & 8) >> 3)) << 5) / 7; + break; + + default: + entry->mask &= ~IT_ENTRY_EFFECT; + } + + ptr += 2; + } + if (entry->mask) entry++; + } + } + + p->n_entries = (int)(entry - p->entry); + offset += psize; + } + + free(buffer); + + return 0; + +error_fb: + free(buffer); +error: + return -1; +} + +#define PSM_COMPONENT_ORDERS 0 +#define PSM_COMPONENT_PANPOS 1 +#define PSM_COMPONENT_PATTERNS 2 +#define PSM_COMPONENT_SAMPLE_HEADERS 3 +#define PSM_COMPONENT_COMMENTS 4 + +typedef struct PSM_COMPONENT +{ + unsigned char type; + int32 offset; +} +PSM_COMPONENT; + +static int CDECL psm_component_compare(const void *e1, const void *e2) +{ + return ((const PSM_COMPONENT *)e1)->offset - + ((const PSM_COMPONENT *)e2)->offset; +} + +static DUMB_IT_SIGDATA *it_old_psm_load_sigdata(DUMBFILE *f) +{ + DUMB_IT_SIGDATA *sigdata; + + PSM_COMPONENT *component; + int n_components = 0; + + int n, flags, version, pver, n_orders, n_channels, total_pattern_size; + + if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',254)) goto error; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) goto error; + + if (dumbfile_getnc((char *)sigdata->name, 60, f) < 60 || + sigdata->name[59] != 0x1A) goto error_sd; + sigdata->name[59] = 0; + + flags = dumbfile_getc(f); + version = dumbfile_getc(f); + pver = dumbfile_getc(f); + sigdata->speed = dumbfile_getc(f); + sigdata->tempo = dumbfile_getc(f); + sigdata->mixing_volume = dumbfile_getc(f); + sigdata->n_orders = dumbfile_igetw(f); + n_orders = dumbfile_igetw(f); + sigdata->n_patterns = dumbfile_igetw(f); + sigdata->n_samples = dumbfile_igetw(f); + sigdata->n_pchannels = dumbfile_igetw(f); + n_channels = dumbfile_igetw(f); + + if (dumbfile_error(f) || + (flags & 1) || + (version != 1 && version != 0x10) || + (pver) || + (sigdata->n_orders <= 0) || + (sigdata->n_orders > 255) || + (n_orders > 255) || + (n_orders < sigdata->n_orders) || + (sigdata->n_patterns > 255) || + (sigdata->n_samples > 255) || + (sigdata->n_pchannels > DUMB_IT_N_CHANNELS) || + (sigdata->n_pchannels > n_channels) || + (n_channels > DUMB_IT_N_CHANNELS)) + goto error_sd; + + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX; + + sigdata->global_volume = 128; + sigdata->pan_separation = 128; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + sigdata->restart_position = 0; + + sigdata->order = malloc(sigdata->n_orders); + if (!sigdata->order) goto error_usd; + + if (sigdata->n_samples) { + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) goto error_usd; + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + } + + if (sigdata->n_patterns) { + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) goto error_usd; + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + } + + component = malloc(5 * sizeof(*component)); + if (!component) goto error_usd; + + for (n = 0; n < 5; n++) { + component[n_components].offset = dumbfile_igetl(f); + if (component[n_components].offset) { + component[n_components].type = n; + n_components++; + } + } + + if (!n_components) goto error_fc; + + total_pattern_size = dumbfile_igetl(f); + if (!total_pattern_size) goto error_fc; + + qsort(component, n_components, sizeof(PSM_COMPONENT), &psm_component_compare); + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { + int sep = 32 * dumb_it_default_panning_separation / 100; + sigdata->channel_pan[n ] = 32 - sep; + sigdata->channel_pan[n+1] = 32 + sep; + sigdata->channel_pan[n+2] = 32 + sep; + sigdata->channel_pan[n+3] = 32 - sep; + } + + for (n = 0; n < n_components; n++) + { + int o; + + if ( dumbfile_seek(f, component[n].offset, DFS_SEEK_SET) ) goto error_fc; + + switch (component[n].type) { + + case PSM_COMPONENT_ORDERS: + if (dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f) < sigdata->n_orders) goto error_fc; + if (n_orders > sigdata->n_orders) + if (dumbfile_skip(f, n_orders - sigdata->n_orders)) + goto error_fc; + if (dumbfile_igetw(f)) goto error_fc; + break; + + case PSM_COMPONENT_PANPOS: + if (dumbfile_getnc((char *)sigdata->channel_pan, sigdata->n_pchannels, f) < sigdata->n_pchannels) goto error_fc; + for (o = 0; o < sigdata->n_pchannels; o++) { + sigdata->channel_pan[o] -= (sigdata->channel_pan[o] & 8) >> 3; + sigdata->channel_pan[o] = ((int)sigdata->channel_pan[o] << 5) / 7; + } + break; + + case PSM_COMPONENT_PATTERNS: + if (it_old_psm_read_patterns(sigdata->pattern, f, sigdata->n_patterns, total_pattern_size, sigdata->n_pchannels)) goto error_fc; + break; + + case PSM_COMPONENT_SAMPLE_HEADERS: + if (it_old_psm_read_samples(&sigdata->sample, f, &sigdata->n_samples)) goto error_fc; + break; + + case PSM_COMPONENT_COMMENTS: + if (dumbfile_mgetl(f) == DUMB_ID('T','E','X','T')) { + o = dumbfile_igetw(f); + if (o > 0) { + sigdata->song_message = malloc(o + 1); + if (dumbfile_getnc((char *)sigdata->song_message, o, f) < o) goto error_fc; + sigdata->song_message[o] = 0; + } + } + break; + } + } + + _dumb_it_fix_invalid_orders(sigdata); + + free(component); + + return sigdata; + +error_fc: + free(component); +error_usd: + _dumb_it_unload_sigdata(sigdata); + return NULL; +error_sd: + free(sigdata); +error: + return NULL; +} + +DUH *DUMBEXPORT dumb_read_old_psm_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_old_psm_load_sigdata(f); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "PSM (old)"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/readpsm.c b/thirdparty/dumb/src/it/readpsm.c new file mode 100644 index 000000000..95545a528 --- /dev/null +++ b/thirdparty/dumb/src/it/readpsm.c @@ -0,0 +1,1292 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readpsm.c - Code to read a Protracker Studio / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifdef _MSC_VER +#define snprintf sprintf_s +#endif + +#define PSMV_OLD 940730 +#define PSMV_NEW 940902 + +typedef struct _PSMCHUNK +{ + int id; + int len; + unsigned char * data; +} PSMCHUNK; + +typedef struct _PSMEVENT +{ + int type; + unsigned char data[8]; +} PSMEVENT; + +#define PSM_EVENT_END 0 +#define PSM_EVENT_PLAY_PATTERN 1 +#define PSM_EVENT_JUMP_TO_LINE 4 +#define PSM_EVENT_SET_SPEED 7 +#define PSM_EVENT_SET_BPM 8 +#define PSM_EVENT_SAMPLE_MAP_TABLE 12 +#define PSM_EVENT_CHANGE_PAN 13 +#define PSM_EVENT_CHANGE_VOL 14 + +static int it_psm_process_sample(IT_SAMPLE * sample, const unsigned char * data, int len, int id, int version) { + int flags; + int insno = 0; + int length = 0; + int loopstart = 0; + int loopend = 0; + int panpos; + int defvol = 0; + int samplerate = 0; + + if (len < 0x60) return -1; + + flags = data[0]; + + if (version == PSMV_OLD) { + memcpy(sample->name, data + 0x0D, 34); + sample->name[34] = 0; + + insno = data[0x34] | (data[0x35] << 8); + length = data[0x36] | (data[0x37] << 8) | (data[0x38] << 16) | (data[0x39] << 24); + loopstart = data[0x3A] | (data[0x3B] << 8) | (data[0x3C] << 16) | (data[0x3D] << 24); + loopend = data[0x3E] | (data[0x3F] << 8) | (data[0x40] << 16) | (data[0x41] << 24); + panpos = data[0x43]; + defvol = data[0x44]; + samplerate = data[0x49] | (data[0x4A] << 8) | (data[0x4B] << 16) | (data[0x4C] << 24); + } else /*if (version == PSMV_NEW)*/ { + memcpy(sample->name, data + 0x11, 34); + sample->name[34] = 0; + + insno = data[0x38] | (data[0x39] << 8); + length = data[0x3A] | (data[0x3B] << 8) | (data[0x3C] << 16) | (data[0x3D] << 24); + loopstart = data[0x3E] | (data[0x3F] << 8) | (data[0x40] << 16) | (data[0x41] << 24); + loopend = data[0x42] | (data[0x43] << 8) | (data[0x44] << 16) | (data[0x45] << 24); + panpos = data[0x48]; + defvol = data[0x49]; + samplerate = data[0x4E] | (data[0x4F] << 8) | (data[0x50] << 16) | (data[0x51] << 24); + } + + if (insno != id) return -1; + + if (!length) { + sample->flags &= ~IT_SAMPLE_EXISTS; + return 0; + } + + if ((length > len - 0x60) || ((flags & 0x7F) != 0)) return -1; + + sample->flags = IT_SAMPLE_EXISTS; + sample->length = length; + sample->loop_start = loopstart; + sample->loop_end = loopend; + sample->C5_speed = samplerate; + sample->default_volume = defvol >> 1; + sample->default_pan = 0; + sample->filename[0] = 0; + sample->global_volume = 64; + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + if (flags & 0x80) { + if (((unsigned int)sample->loop_end <= (unsigned int)sample->length) && + ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end)) { + sample->length = sample->loop_end; + sample->flags |= IT_SAMPLE_LOOP; + } + } + + sample->data = malloc(sample->length); + if (!sample->data) + return -1; + + flags = 0; + data += 0x60; + + for (insno = 0; insno < sample->length; insno++) { + flags += (signed char)(*data++); + ((signed char *)sample->data)[insno] = flags; + } + + return 0; +} + +static int it_psm_process_pattern(IT_PATTERN * pattern, const unsigned char * data, int len, int speed, int bpm, const unsigned char * pan, const int * vol, int version) { + int length, nrows, row, rowlen, pos; + unsigned flags, chan; + IT_ENTRY * entry; + + length = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + if (len > length) len = length; + + if (version == PSMV_OLD) { + if (len < 10) return -1; + data += 8; + len -= 8; + } else /*if (version == PSMV_NEW)*/ { + if (len < 14) return -1; + data += 12; + len -= 12; + } + + nrows = data[0] | (data[1] << 8); + + if (!nrows) return 0; + + pattern->n_rows = nrows; + + data += 2; + len -= 2; + + pattern->n_entries = 0; + + row = 0; + pos = 2; + rowlen = data[0] | (data[1] << 8); + + while ((row < nrows) && (pos < len)) { + if (pos >= rowlen) { + row++; + rowlen += data[pos] | (data[pos+1] << 8); + pos += 2; + continue; + } + + flags = data[pos++]; + chan = data[pos++]; + + if (chan > 63) return -1; + + if (flags & 0xF0) { + pattern->n_entries++; + if (flags & 0x80) pos++; + if (flags & 0x40) pos++; + if (flags & 0x20) pos++; + if (flags & 0x10) { + switch (data[pos]) { + case 0x29: + pos++; + case 0x33: + pos++; + default: + pos += 2; + } + } + } + } + + if (!pattern->n_entries) return 0; + + pattern->n_entries += nrows; + if (speed) pattern->n_entries++; + if (bpm >= 0x20) pattern->n_entries++; + + for (pos = 0; pos < 32; pos++) { + if (!(pan[pos*2+1] & 0xF9)) pattern->n_entries++; + if (vol[pos] != -1) pattern->n_entries++; + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + if (!pattern->entry) return -1; + + entry = pattern->entry; + + if (speed) { + entry->channel = 0; + entry->mask = IT_ENTRY_EFFECT; + entry->effect = IT_SET_SPEED; + entry->effectvalue = speed; + entry++; + } + + if (bpm >= 0x20) { + entry->channel = 0; + entry->mask = IT_ENTRY_EFFECT; + entry->effect = IT_SET_SONG_TEMPO; + entry->effectvalue = bpm; + entry++; + } + + for (pos = 0; pos < 32; pos++) { + if (!(pan[pos*2+1] & 0xF9)) { + entry->channel = pos; + entry->mask = IT_ENTRY_EFFECT; + switch (pan[pos*2+1]) { + case 0: + entry->effect = IT_SET_PANNING; + entry->effectvalue = pan[pos*2] ^ 128; + break; + case 2: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_SET_SURROUND_SOUND,1); + break; + case 4: + entry->effect = IT_SET_PANNING; + entry->effectvalue = 128; + break; + } + entry++; + } + if (vol[pos] != -1) { + entry->channel = pos; + entry->mask = IT_ENTRY_EFFECT; + entry->effect = IT_SET_CHANNEL_VOLUME; + entry->effectvalue = (vol[pos] + 2) >> 2; + entry++; + } + } + + row = 0; + pos = 2; + rowlen = data[0] | (data[1] << 8); + + while ((row < nrows) && (pos < len)) { + if (pos >= rowlen) { + IT_SET_END_ROW(entry); + entry++; + row++; + rowlen += data[pos] | (data[pos+1] << 8); + pos += 2; + continue; + } + + flags = data[pos++]; + entry->channel = data[pos++]; + entry->mask = 0; + + if (flags & 0xF0) { + if (flags & 0x80) { + entry->mask |= IT_ENTRY_NOTE; + if (version == PSMV_OLD) { + if ((data[pos] < 0x80)) entry->note = (data[pos]>>4)*12+(data[pos]&0x0f)+12; + else entry->mask &= ~IT_ENTRY_NOTE; + } else /*if (version == PSMV_NEW)*/ { + if ((data[pos]) && (data[pos] < 84)) entry->note = data[pos] + 35; + else entry->mask &= ~IT_ENTRY_NOTE; + } + pos++; + } + + if (flags & 0x40) { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = data[pos++] + 1; + } + + if (flags & 0x20) { + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = (data[pos++] + 1) >> 1; + } + + if (flags & 0x10) { + entry->mask |= IT_ENTRY_EFFECT; + length = data[pos+1]; + switch (data[pos]) { + case 1: + entry->effect = IT_VOLUME_SLIDE; + if (version == PSMV_OLD) entry->effectvalue = ((length&0x1e)<<3) | 0xF; + else /*if (version == PSMV_NEW)*/ entry->effectvalue = (length<<4) | 0xF; + break; + + case 2: + entry->effect = IT_VOLUME_SLIDE; + if (version == PSMV_OLD) entry->effectvalue = (length << 3) & 0xF0; + else /*if (version == PSMV_NEW)*/ entry->effectvalue = (length << 4) & 0xF0; + break; + + case 3: + entry->effect = IT_VOLUME_SLIDE; + if (version == PSMV_OLD) entry->effectvalue = (length >> 1) | 0xF0; + else /*if (version == PSMV_NEW)*/ entry->effectvalue = length | 0xF0; + break; + + case 4: + entry->effect = IT_VOLUME_SLIDE; + if (version == PSMV_OLD) entry->effectvalue = (length >> 1) & 0xF; + else /*if (version == PSMV_NEW)*/ entry->effectvalue = length & 0xF; + break; + + case 12: + entry->effect = IT_PORTAMENTO_UP; + if (version == PSMV_OLD) { + if (length < 4) entry->effectvalue = length | 0xF0; + else entry->effectvalue = length >> 2; + } else /*if (version == PSMV_NEW)*/ { + entry->effectvalue = length; + } + break; + + case 14: + entry->effect = IT_PORTAMENTO_DOWN; + if (version == PSMV_OLD) { + if (length < 4) entry->effectvalue = length | 0xF0; + else entry->effectvalue = length >> 2; + } else /*if (version == PSMV_NEW)*/ { + entry->effectvalue = length; + } + break; + + case 15: + entry->effect = IT_TONE_PORTAMENTO; + if (version == PSMV_OLD) entry->effectvalue = length >> 2; + else /*if (version == PSMV_NEW)*/ entry->effectvalue = length; + break; + + case 0x15: + entry->effect = IT_VIBRATO; + entry->effectvalue = length; + break; + + case 0x18: + entry->effect = IT_VOLSLIDE_VIBRATO; + entry->effectvalue = length; + break; + + case 0x29: + entry->effect = IT_SET_SAMPLE_OFFSET; + entry->effectvalue = data[pos+2]; + pos += 2; + break; + + case 0x2A: + entry->effect = IT_RETRIGGER_NOTE; + entry->effectvalue = length; + break; + + case 0x33: +#if 0 + entry->effect = IT_POSITION_JUMP; + entry->effectvalue = data[pos+2]; +#else + entry->mask &= ~IT_ENTRY_EFFECT; +#endif + pos++; + break; + + case 0x34: + entry->effect = IT_BREAK_TO_ROW; + entry->effectvalue = length; + break; + + case 0x3D: + entry->effect = IT_SET_SPEED; + entry->effectvalue = length; + break; + + case 0x3E: + if (length >= 0x20) { + entry->effect = IT_SET_SONG_TEMPO; + entry->effectvalue = length; + } else { + entry->mask &= ~IT_ENTRY_EFFECT; + } + break; + + case 0x47: + entry->effect = IT_ARPEGGIO; + entry->effectvalue = length; + break; + + default: + return -1; + } + pos += 2; + } + if (entry->mask) entry++; + } + } + + while (row < nrows) { + IT_SET_END_ROW(entry); + entry++; + row++; + } + + pattern->n_entries = (int)(entry - pattern->entry); + if (!pattern->n_entries) return -1; + + return 0; +} + + +static void free_chunks(PSMCHUNK * chunk, int count) { + int n; + + for (n = 0; n < count; n++) { + if (chunk[n].data) + free(chunk[n].data); + } + + free(chunk); +} + +static void dumb_it_optimize_orders(DUMB_IT_SIGDATA * sigdata); + +static int pattcmp( const unsigned char *, const unsigned char *, size_t ); + +static DUMB_IT_SIGDATA *it_psm_load_sigdata(DUMBFILE *f, int * ver, int subsong) +{ + DUMB_IT_SIGDATA *sigdata; + + PSMCHUNK *chunk; + int n_chunks = 0; + + PSMCHUNK *songchunk; + int n_song_chunks = 0; + + PSMEVENT *event = NULL; + int n_events = 0; + + unsigned char * ptr; + + int n, length, o; + + int found; + + int n_patterns = 0; + + int first_pattern_line = -1; + int first_pattern; + + int speed, bpm; + unsigned char pan[64]; + int vol[32]; + + if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',' ')) goto error; + + length = dumbfile_igetl(f); + + if (dumbfile_mgetl(f) != DUMB_ID('F','I','L','E')) goto error; + + chunk = calloc(768, sizeof(*chunk)); + + while (length >= 8) { + chunk[n_chunks].id = dumbfile_mgetl(f); + n = dumbfile_igetl(f); + length -= 8; + if (n < 0 || n > length) + goto error_fc; + chunk[n_chunks].len = n; + if (n) { + ptr = malloc(n); + if (!ptr) goto error_fc; + if (dumbfile_getnc((char *)ptr, n, f) < n) + { + free(ptr); + goto error_fc; + } + chunk[n_chunks].data = ptr; + } + n_chunks++; + length -= n; + } + + if (!n_chunks) goto error_fc; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) goto error_fc; + + sigdata->n_patterns = 0; + sigdata->n_samples = 0; + sigdata->name[0] = 0; + + found = 0; + + for (n = 0; n < n_chunks; n++) { + PSMCHUNK * c = &chunk[n]; + switch(c->id) { + case DUMB_ID('S','D','F','T'): + /* song data format? */ + if ((found & 1) || (c->len != 8) || memcmp(c->data, "MAINSONG", 8)) goto error_sd; + found |= 1; + break; + + case DUMB_ID('S','O','N','G'): + if (/*(found & 2) ||*/ (c->len < 11) /*|| memcmp(c->data, "MAINSONG", 8)*/) goto error_sd; + found |= 2; + break; + + case DUMB_ID('D','S','M','P'): + sigdata->n_samples++; + break; + + case DUMB_ID('T','I','T','L'): + length = min((int)sizeof(sigdata->name) - 1, c->len); + memcpy(sigdata->name, c->data, length); + sigdata->name[length] = 0; + } + } + + if (found != 3 || !sigdata->n_samples) goto error_sd; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + sigdata->n_orders = 0; + + for (n = 0; n < n_chunks; n++) { + PSMCHUNK * c = &chunk[n]; + if (c->id == DUMB_ID('S','O','N','G')) { + if (subsong == 0) break; + subsong--; + } + } + + if (n == n_chunks) return NULL; + subsong = n; + + /*for (n = 0; n < n_chunks; n++) { + PSMCHUNK * c = &chunk[n]; + if (c->id == DUMB_ID('S','O','N','G')) {*/ + { + PSMCHUNK * c = &chunk[subsong]; + { + ptr = c->data; + if (ptr[10] > 32) goto error_usd; + sigdata->n_pchannels = ptr[10]; + length = c->len - 11; + ptr += 11; + songchunk = 0; + if (length >= 8) { + songchunk = malloc(128 * sizeof(*songchunk)); + if (!songchunk) goto error_usd; + while (length >= 8) { + songchunk[n_song_chunks].id = DUMB_ID(ptr[0], ptr[1], ptr[2], ptr[3]); + n = ptr[4] | (ptr[5] << 8) | (ptr[6] << 16) | (ptr[7] << 24); + length -= 8; + if (n > length) goto error_sc; + songchunk[n_song_chunks].len = n; + songchunk[n_song_chunks].data = ptr + 8; + n_song_chunks++; + length -= n; + ptr += 8 + n; + } + } + /*break;*/ + } + } + + if (!n_song_chunks) goto error_sc; + + found = 0; + + for (n = 0; n < n_song_chunks; n++) { + PSMCHUNK * c = &songchunk[n]; + + if (c->id == DUMB_ID('D','A','T','E')) { + /* date of the library build / format spec */ + if (c->len == 6) { + length = c->len; + ptr = c->data; + while (length > 0) { + if (*ptr >= '0' && *ptr <= '9') { + found = (found * 10) + (*ptr - '0'); + } else { + found = 0; + break; + } + ptr++; + length--; + } + } + break; + } + } + + /* + if (found != 940506 && + found != 940509 && + found != 940510 && + found != 940530 && + found != 940629 && + found != PSMV_OLD && + found != 941011 && + found != PSMV_NEW && + found != 940906 && + found != 940903 && + found != 940914 && + found != 941213 && + found != 800211) // WTF? + goto error_sc; + */ + + *ver = found; + + if (found == 800211 || + found == PSMV_NEW || + found == 940903 || + found == 940906 || + found == 940914 || + found == 941213) found = PSMV_NEW; + else found = PSMV_OLD; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { + int sep = 32 * dumb_it_default_panning_separation / 100; + sigdata->channel_pan[n ] = 32 - sep; + sigdata->channel_pan[n+1] = 32 + sep; + sigdata->channel_pan[n+2] = 32 + sep; + sigdata->channel_pan[n+3] = 32 - sep; + } + + for (n = 0; n < n_song_chunks; n++) { + PSMCHUNK * c = &songchunk[n]; + + switch (c->id) { + case DUMB_ID('O','P','L','H'): + if (c->len < 2) goto error_sc; + ptr = c->data; + o = ptr[0] | (ptr[1] << 8); + if (!o) goto error_sc; + event = malloc(o * sizeof(*event)); + if (!event) goto error_sc; + length = c->len - 2; + ptr += 2; + while ((length > 0) && (n_events < o)) { + event[n_events].type = *ptr; + switch (*ptr) { + case PSM_EVENT_END: + ptr++; + length--; + break; + + case PSM_EVENT_PLAY_PATTERN: + if (found == PSMV_OLD) { + if (length < 5) goto error_ev; + memcpy(event[n_events].data, ptr + 1, 4); + ptr += 5; + length -= 5; + } else /*if (found == PSMV_NEW)*/ { + if (length < 9) goto error_ev; + memcpy(event[n_events].data, ptr + 1, 8); + ptr += 9; + length -= 9; + } + break; + + case PSM_EVENT_SET_SPEED: + case PSM_EVENT_SET_BPM: + if (length < 2) goto error_ev; + event[n_events].data[0] = ptr[1]; + ptr += 2; + length -= 2; + break; + + case PSM_EVENT_JUMP_TO_LINE: + case PSM_EVENT_CHANGE_VOL: + if (length < 3) goto error_ev; + memcpy(event[n_events].data, ptr + 1, 2); + ptr += 3; + length -= 3; + break; + + case PSM_EVENT_SAMPLE_MAP_TABLE: + if (length < 7) goto error_ev; + memcpy(event[n_events].data, ptr + 1, 6); + ptr += 7; + length -= 7; + break; + + case PSM_EVENT_CHANGE_PAN: + if (length < 4) goto error_ev; + memcpy(event[n_events].data, ptr + 1, 3); + ptr += 4; + length -= 4; + break; + + default: + goto error_ev; + } + n_events++; + } + break; + + case DUMB_ID('P','P','A','N'): + length = c->len; + if (length & 1) goto error_ev; + ptr = c->data; + o = 0; + while (length > 0) { + switch (ptr[0]) { + case 0: + sigdata->channel_pan[o] = ((((int)(signed char)ptr[1]) * 32) / 127) + 32; + break; + case 2: + sigdata->channel_pan[o] = IT_SURROUND; + break; + case 4: + sigdata->channel_pan[o] = 32; + break; + } + ptr += 2; + length -= 2; + if (++o >= DUMB_IT_N_CHANNELS) break; + } + break; + + /* + case DUMB_ID('P','A','T','T'): + case DUMB_ID('D','S','A','M'): + */ + } + } + + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX; + + sigdata->global_volume = 128; + sigdata->speed = 6; + sigdata->tempo = 125; + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + speed = 0; + bpm = 0; + memset(pan, 255, sizeof(pan)); + memset(vol, 255, sizeof(vol)); + + sigdata->n_patterns = n_events; + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) goto error_ev; + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + + for (n = 0; n < n_events; n++) { + PSMEVENT * e = &event[n]; + switch (e->type) { + case PSM_EVENT_END: + n = n_events; + break; + + case PSM_EVENT_PLAY_PATTERN: + for (o = 0; o < n_chunks; o++) { + PSMCHUNK * c = &chunk[o]; + if (c->id == DUMB_ID('P','B','O','D')) { + ptr = c->data; + length = c->len; + if (found == PSMV_OLD) { + if (length < 8) goto error_ev; + if (!pattcmp(ptr + 4, e->data, 4)) { + if (it_psm_process_pattern(&sigdata->pattern[n_patterns], ptr, length, speed, bpm, pan, vol, found)) goto error_ev; + if (first_pattern_line < 0) { + first_pattern_line = n; + first_pattern = o; + } + e->data[0] = n_patterns; + e->data[1] = n_patterns >> 8; + n_patterns++; + break; + } + } else /*if (found == PSMV_NEW)*/ { + if (length < 12) goto error_ev; + if (!pattcmp(ptr + 4, e->data, 8)) { + if (it_psm_process_pattern(&sigdata->pattern[n_patterns], ptr, length, speed, bpm, pan, vol, found)) goto error_ev; + if (first_pattern_line < 0) { + first_pattern_line = n; + first_pattern = o; + } + e->data[0] = n_patterns; + e->data[1] = n_patterns >> 8; + n_patterns++; + break; + } + } + } + } + if (o == n_chunks) goto error_ev; + + speed = 0; + bpm = 0; + memset(pan, 255, sizeof(pan)); + memset(vol, 255, sizeof(vol)); + + e->type = PSM_EVENT_END; + break; + + case PSM_EVENT_JUMP_TO_LINE: + o = e->data[0] | (e->data[1] << 8); + if (o >= n_events) goto error_ev; + if (o == 0) { + /* whew! easy case! */ + sigdata->restart_position = 0; + n = n_events; + } else if (o == n) { + /* freeze */ + n = n_events; + } else if (o > n) { + /* jump ahead, setting played event numbers to zero will prevent endless looping */ + n = o - 1; + } else if (o >= first_pattern_line) { + /* another semi-easy case */ + sigdata->restart_position = event[o].data[0] | (event[o].data[1] << 8); + n = n_events; + } else { + /* crud, try to simulate rerunning all of the commands from the indicated + * line up to the first pattern, then dupe the first pattern again. + */ + /* + PSMCHUNK * c = &chunk[first_pattern]; + + for (; o < first_pattern_line; o++) { + PSMEVENT * ev = &event[o]; + switch (ev->type) { + case PSM_EVENT_SET_SPEED: + speed = ev->data[0]; + break; + case PSM_EVENT_SET_BPM: + bpm = ev->data[0]; + break; + case PSM_EVENT_CHANGE_PAN: + if (ev->data[0] > 31) goto error_ev; + pan[ev->data[0] * 2] = ev->data[1]; + pan[ev->data[0] * 2 + 1] = ev->data[2]; + break; + case PSM_EVENT_CHANGE_VOL: + if (ev->data[0] > 31) goto error_ev; + vol[ev->data[0]] = ev->data[1]; + break; + } + } + + if (it_psm_process_pattern(&sigdata->pattern[n_patterns], c->data, c->len, speed, bpm, pan, vol, found)) goto error_ev; + n_patterns++; + sigdata->restart_position = 1; + n = n_events; + + Eh, what the hell? PSM has no panning commands anyway. + */ + sigdata->restart_position = 0; + n = n_events; + } + e->type = PSM_EVENT_END; + break; + + case PSM_EVENT_SET_SPEED: + speed = e->data[0]; + break; + + case PSM_EVENT_SET_BPM: + bpm = e->data[0]; + break; + + case PSM_EVENT_CHANGE_PAN: + o = e->data[0]; + if (o > 31) goto error_ev; + pan[o * 2] = e->data[1]; + pan[o * 2 + 1] = e->data[2]; + break; + + case PSM_EVENT_CHANGE_VOL: + o = e->data[0]; + if (o > 31) goto error_ev; + vol[o] = e->data[1]; + break; + + case PSM_EVENT_SAMPLE_MAP_TABLE: + if (e->data[0] != 0 || e->data[1] != 0xFF || + e->data[2] != 0 || e->data[3] != 0 || + e->data[4] != 1 || e->data[5] != 0) + goto error_ev; + break; + } + } + + if (n_patterns > 256) goto error_ev; + + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) goto error_ev; + for (n = 0; n < sigdata->n_samples; n++) { + sigdata->sample[n].data = NULL; + sigdata->sample[n].flags = 0; + } + + o = 0; + for (n = 0; n < n_chunks; n++) { + PSMCHUNK * c = &chunk[n]; + if (c->id == DUMB_ID('D','S','M','P')) { + if (it_psm_process_sample(&sigdata->sample[o], c->data, c->len, o, found)) goto error_ev; + o++; + } + } + + sigdata->n_orders = n_patterns; + sigdata->n_patterns = n_patterns; + + sigdata->order = malloc(n_patterns); + + for (n = 0; n < n_patterns; n++) { + sigdata->order[n] = n; + } + + free(event); + free(songchunk); + free_chunks(chunk, n_chunks); + + _dumb_it_fix_invalid_orders(sigdata); + + dumb_it_optimize_orders(sigdata); + + return sigdata; + +error_ev: + free(event); +error_sc: + if (songchunk) free(songchunk); +error_usd: + _dumb_it_unload_sigdata(sigdata); + goto error_fc; +error_sd: + free(sigdata); +error_fc: + free_chunks(chunk, n_chunks); +error: + return NULL; +} + +static int CDECL it_order_compare(const void *e1, const void *e2) { + if (*((const char *)e1) < *((const char *)e2)) + return -1; + + if (*((const char *)e1) > *((const char *)e2)) + return 1; + + return 0; +} + +/* +static int it_optimize_compare(const void *e1, const void *e2) { + if (((const IT_ENTRY *)e1)->channel < ((const IT_ENTRY *)e2)->channel) + return -1; + + if (((const IT_ENTRY *)e1)->channel > ((const IT_ENTRY *)e2)->channel) + return 1; + + return 0; +} +*/ + +static int CDECL it_entry_compare(const IT_ENTRY * e1, const IT_ENTRY * e2) { + if (IT_IS_END_ROW(e1) && IT_IS_END_ROW(e2)) return 1; + if (e1->channel != e2->channel) return 0; + if (e1->mask != e2->mask) return 0; + if ((e1->mask & IT_ENTRY_NOTE) && (e1->note != e2->note)) return 0; + if ((e1->mask & IT_ENTRY_INSTRUMENT) && (e1->instrument != e2->instrument)) return 0; + if ((e1->mask & IT_ENTRY_VOLPAN) && (e1->volpan != e2->volpan)) return 0; + if ((e1->mask & IT_ENTRY_EFFECT) && ((e1->effect != e2->effect) || (e1->effectvalue != e2->effectvalue))) return 0; + return 1; +} + +/* +static void dumb_it_optimize_pattern(IT_PATTERN * pattern) { + IT_ENTRY * entry, * end; + IT_ENTRY * rowstart, * rowend; + IT_ENTRY * current; + + if (!pattern->n_entries || !pattern->entry) return; + + current = entry = pattern->entry; + end = entry + pattern->n_entries; + + while (entry < end) { + rowstart = entry; + while (!IT_IS_END_ROW(entry)) entry++; + rowend = entry; + if (rowend > rowstart + 1) + qsort(rowstart, rowend - rowstart, sizeof(IT_ENTRY), &it_optimize_compare); + entry = rowstart; + while (entry < rowend) { + if (!(entry->mask)) {} + else if (it_entry_compare(entry, current)) {} + else if (!(current->mask) || + ((entry->channel == current->channel) && + ((entry->mask | current->mask) == (entry->mask ^ current->mask)))) { + current->mask |= entry->mask; + if (entry->mask & IT_ENTRY_NOTE) current->note = entry->note; + if (entry->mask & IT_ENTRY_INSTRUMENT) current->instrument = entry->instrument; + if (entry->mask & IT_ENTRY_VOLPAN) current->volpan = entry->volpan; + if (entry->mask & IT_ENTRY_EFFECT) { + current->effect = entry->effect; + current->effectvalue = entry->effectvalue; + } + } else { + if (++current < entry) *current = *entry; + } + entry++; + } + if (++current < entry) *current = *entry; + entry++; + } + + current++; + + if (current < end) { + IT_ENTRY * opt; + pattern->n_entries = current - pattern->entry; + opt = realloc(pattern->entry, pattern->n_entries * sizeof(*pattern->entry)); + if (opt) pattern->entry = opt; + } +} +*/ + +static int it_pattern_compare(const IT_PATTERN * p1, const IT_PATTERN * p2) { + IT_ENTRY * e1, * end; + IT_ENTRY * e2; + + if (p1 == p2) return 1; + if (p1->n_entries != p2->n_entries) return 0; + + e1 = p1->entry; end = e1 + p1->n_entries; + e2 = p2->entry; + + while (e1 < end) { + if (!it_entry_compare(e1, e2)) return 0; + e1++; e2++; + } + + return 1; +} + +static void dumb_it_optimize_orders(DUMB_IT_SIGDATA * sigdata) { + int n, o, p; + + /*int last_invalid = (sigdata->flags & IT_WAS_AN_XM) ? 255 : 253;*/ + + unsigned char * order_list; + int n_patterns; + + IT_PATTERN * pattern; + + if (!sigdata->n_orders || !sigdata->n_patterns) return; + + n_patterns = 0; + order_list = malloc(sigdata->n_orders); + + if (!order_list) return; + + for (n = 0; n < sigdata->n_orders; n++) { + if (sigdata->order[n] < sigdata->n_patterns) { + for (o = 0; o < n_patterns; o++) { + if (sigdata->order[n] == order_list[o]) break; + } + if (o == n_patterns) { + order_list[n_patterns++] = sigdata->order[n]; + } + } + } + + if (!n_patterns) { + free(order_list); + return; + } + + /*for (n = 0; n < n_patterns; n++) { + dumb_it_optimize_pattern(&sigdata->pattern[order_list[n]]); + }*/ + + for (n = 0; n < n_patterns; n++) { + for (o = n + 1; o < n_patterns; o++) { + if ((order_list[n] != order_list[o]) && + it_pattern_compare(&sigdata->pattern[order_list[n]], &sigdata->pattern[order_list[o]])) { + for (p = 0; p < sigdata->n_orders; p++) { + if (sigdata->order[p] == order_list[o]) { + sigdata->order[p] = order_list[n]; + } + } + for (p = o + 1; p < n_patterns; p++) { + if (order_list[p] == order_list[o]) { + order_list[p] = order_list[n]; + } + } + order_list[o] = order_list[n]; + } + } + } + + qsort(order_list, n_patterns, sizeof(*order_list), &it_order_compare); + + for (n = 0, o = 0; n < n_patterns; n++) { + if (order_list[n] != order_list[o]) { + if (++o < n) order_list[o] = order_list[n]; + } + } + + n_patterns = o + 1; + + pattern = malloc(n_patterns * sizeof(*pattern)); + if (!pattern) { + free(order_list); + return; + } + + for (n = 0; n < n_patterns; n++) { + pattern[n] = sigdata->pattern[order_list[n]]; + } + + for (n = 0; n < sigdata->n_patterns; n++) { + for (o = 0; o < n_patterns; o++) { + if (order_list[o] == n) break; + } + if (o == n_patterns) { + if (sigdata->pattern[n].entry) + free(sigdata->pattern[n].entry); + } + } + + free(sigdata->pattern); + sigdata->pattern = pattern; + sigdata->n_patterns = n_patterns; + + for (n = 0; n < sigdata->n_orders; n++) { + for (o = 0; o < n_patterns; o++) { + if (sigdata->order[n] == order_list[o]) { + sigdata->order[n] = o; + break; + } + } + } + + free(order_list); +} + +int DUMBEXPORT dumb_get_psm_subsong_count(DUMBFILE *f) { + int length, subsongs; + int32 l; + + if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',' ')) return 0; + + length = dumbfile_igetl(f); + + if (dumbfile_mgetl(f) != DUMB_ID('F','I','L','E')) return 0; + + subsongs = 0; + + while (length >= 8 && !dumbfile_error(f)) { + if (dumbfile_mgetl(f) == DUMB_ID('S','O','N','G')) subsongs++; + l = dumbfile_igetl(f); + dumbfile_skip(f, l); + length -= l + 8; + } + + if (dumbfile_error(f)) return 0; + + return subsongs; +} + + + +/* Eww */ +int pattcmp( const unsigned char * a, const unsigned char * b, size_t l ) +{ + size_t i, j; + int na = 0, nb = 0, k; + char * p; + + k = memcmp( a, b, l ); + if ( !k ) return k; + + /* damnit */ + + for ( i = 0; i < l; ++i ) + { + if ( a [i] >= '0' && a [i] <= '9' ) break; + } + + if ( i < l ) + { + na = strtoul( (const char *)a + i, &p, 10 ); + if ( p == (const char *)a + i ) return 1; + } + + for ( j = 0; j < l; ++j ) + { + if ( b [j] >= '0' && b [j] <= '9' ) break; + } + + if ( j < l ) + { + nb = strtoul( (const char *)b + j, &p, 10 ); + if ( p == (const char *)b + j ) return -1; + } + + if ( i < j ) return -1; + else if ( j > i ) return 1; + + k = memcmp( a, b, j ); + if ( k ) return k; + + return na - nb; +} + + + +DUH *DUMBEXPORT dumb_read_psm_quick(DUMBFILE *f, int subsong) +{ + sigdata_t *sigdata; + int ver; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_psm_load_sigdata(f, &ver, subsong); + + if (!sigdata) + return NULL; + + { + int n_tags = 2; + char version[16]; + const char *tag[3][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "PSM"; + if ( ver ) + { + tag[2][0] = "FORMATVERSION"; +#if NEED_ITOA + snprintf( version, 15, "%u", ver ); + version[15] = 0; +#else + itoa(ver, version, 10); +#endif + tag[2][1] = (const char *) &version; + ++n_tags; + } + return make_duh(-1, n_tags, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/readptm.c b/thirdparty/dumb/src/it/readptm.c new file mode 100644 index 000000000..3052a4daa --- /dev/null +++ b/thirdparty/dumb/src/it/readptm.c @@ -0,0 +1,554 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readptm.c - Code to read a Poly Tracker v2.03 / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. Based on reads3m.c \_ / > / + * by entheh. | \ / / + * | ' / + * \__/ + */ + +// IT_STEREO... :o +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static int it_ptm_read_sample_header(IT_SAMPLE *sample, int32 *offset, DUMBFILE *f) +{ + int flags; + + flags = dumbfile_getc(f); + + dumbfile_getnc((char *)sample->filename, 12, f); + sample->filename[12] = 0; + + sample->default_volume = dumbfile_getc(f); + + sample->C5_speed = dumbfile_igetw(f) << 1; + + dumbfile_skip(f, 2); /* segment */ + + *offset = dumbfile_igetl(f); + + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = dumbfile_igetl(f); + + /* GUSBegin, GUSLStart, GUSLEnd, GUSLoop, reserverd */ + dumbfile_skip(f, 4+4+4+1+1); + + dumbfile_getnc((char *)sample->name, 28, f); + sample->name[28] = 0; + + /* + if (dumbfile_mgetl(f) != DUMB_ID('P','T','M','S')) + return -1; + */ + + /* BLAH! Shit likes to have broken or missing sample IDs */ + dumbfile_skip(f, 4); + + if ((flags & 3) == 0) { + /* Looks like no sample */ + sample->flags &= ~IT_SAMPLE_EXISTS; + return dumbfile_error(f); + } + + sample->global_volume = 64; + + sample->flags = IT_SAMPLE_EXISTS; + if (flags & 4) sample->flags |= IT_SAMPLE_LOOP; + if (flags & 8) sample->flags |= IT_SAMPLE_PINGPONG_LOOP; + + if (flags & 16) { + sample->flags |= IT_SAMPLE_16BIT; + + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } + + if (sample->loop_end) sample->loop_end--; + + sample->default_pan = 0; // 0 = don't use, or 160 = centre? + + if (sample->length <= 0) + sample->flags &= ~IT_SAMPLE_EXISTS; + else if (sample->flags & IT_SAMPLE_LOOP) { + if ((unsigned int)sample->loop_end > (unsigned int)sample->length) + sample->flags &= ~IT_SAMPLE_LOOP; + else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end) + sample->flags &= ~IT_SAMPLE_LOOP; + else + sample->length = sample->loop_end; + } + + + //Do we need to set all these? + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + + +static int it_ptm_read_byte(DUMBFILE *f) +{ + int meh = dumbfile_getc(f); + if (meh < 0) return 0; + return meh; +} + +static int it_ptm_read_sample_data(IT_SAMPLE *sample, int last, DUMBFILE *f) +{ + int32 n; + int s; + + sample->data = malloc(sample->length * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); + if (!sample->data) + return -1; + + s = 0; + + if (sample->flags & IT_SAMPLE_16BIT) { + unsigned char a, b; + for (n = 0; n < sample->length; n++) { + a = s += (signed char) it_ptm_read_byte(f); + b = s += (signed char) it_ptm_read_byte(f); + ((short *)sample->data)[n] = a | (b << 8); + } + } else { + for (n = 0; n < sample->length; n++) { + s += (signed char) it_ptm_read_byte(f); + ((signed char *)sample->data)[n] = s; + } + } + + if (dumbfile_error(f) && !last) + return -1; + + return 0; +} + + + +static int it_ptm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer, int length) +{ + int buflen = 0; + int bufpos = 0; + int effect, effectvalue; + + IT_ENTRY *entry; + + unsigned char channel; + + if (!length) + return -1; + + pattern->n_rows = 0; + pattern->n_entries = 0; + + /* Read in the pattern data, little by little, and work out how many + * entries we need room for. Sorry, but this is just so funny... + */ + for (;;) { + unsigned char b = buffer[buflen++] = dumbfile_getc(f); + +#if 1 + static const unsigned char used[8] = {0, 2, 2, 4, 1, 3, 3, 5}; + channel = b & 31; + b >>= 5; + pattern->n_entries++; + if (b) { + if (buflen + used[b] >= 65536) return -1; + dumbfile_getnc((char *)buffer + buflen, used[b], f); + buflen += used[b]; + } else { + /* End of row */ + if (++pattern->n_rows == 64) break; + if (buflen >= 65536) return -1; + } +#else + if (b == 0) { + /* End of row */ + pattern->n_entries++; + if (++pattern->n_rows == 64) break; + if (buflen >= 65536) return -1; + } else { + static const unsigned char used[8] = {0, 2, 2, 4, 1, 3, 3, 5}; + channel = b & 31; + b >>= 5; + if (b) { + pattern->n_entries++; + if (buflen + used[b] >= 65536) return -1; + dumbfile_getnc(buffer + buflen, used[b], f); + buflen += used[b]; + } + } +#endif + + /* We have ensured that buflen < 65536 at this point, so it is safe + * to iterate and read at least one more byte without checking. + * However, now would be a good time to check for errors reading from + * the file. + */ + + if (dumbfile_error(f)) + return -1; + + /* Great. We ran out of data, but there should be data for more rows. + * Fill the rest with null data... + */ + if (buflen >= length && pattern->n_rows < 64) + { + while (pattern->n_rows < 64) + { + if (buflen >= 65536) return -1; + buffer[buflen++] = 0; + pattern->n_entries++; + pattern->n_rows++; + } + break; + } + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + + if (!pattern->entry) + return -1; + + entry = pattern->entry; + + while (bufpos < buflen) { + unsigned char b = buffer[bufpos++]; + + if (b == 0) + { + /* End of row */ + IT_SET_END_ROW(entry); + entry++; + continue; + } + + channel = b & 31; + + if (b & 224) { + entry->mask = 0; + entry->channel = channel; + + if (b & 32) { + unsigned char n = buffer[bufpos++]; + if (n == 254 || (n >= 1 && n <= 120)) { + if (n == 254) + entry->note = IT_NOTE_CUT; + else + entry->note = n - 1; + entry->mask |= IT_ENTRY_NOTE; + } + + entry->instrument = buffer[bufpos++]; + if (entry->instrument) + entry->mask |= IT_ENTRY_INSTRUMENT; + } + + if (b & 64) { + effect = buffer[bufpos++]; + effectvalue = buffer[bufpos++]; + _dumb_it_ptm_convert_effect(effect, effectvalue, entry); + } + + if (b & 128) { + entry->volpan = buffer[bufpos++]; + if (entry->volpan <= 64) + entry->mask |= IT_ENTRY_VOLPAN; + } + + entry++; + } + } + + ASSERT(entry == pattern->entry + pattern->n_entries); + + return 0; +} + + + +/** WARNING: this is duplicated in itread.c - also bad practice to use the same struct name unless they are unified in a header */ +/* Currently we assume the sample data are stored after the sample headers in + * module files. This assumption may be unjustified; let me know if you have + * trouble. + */ + +#define PTM_COMPONENT_INSTRUMENT 1 +#define PTM_COMPONENT_PATTERN 2 +#define PTM_COMPONENT_SAMPLE 3 + +typedef struct PTM_COMPONENT +{ + unsigned char type; + unsigned char n; + int32 offset; +} +PTM_COMPONENT; + + + +static int CDECL ptm_component_compare(const void *e1, const void *e2) +{ + return ((const PTM_COMPONENT *)e1)->offset - + ((const PTM_COMPONENT *)e2)->offset; +} + + + +static DUMB_IT_SIGDATA *it_ptm_load_sigdata(DUMBFILE *f) +{ + DUMB_IT_SIGDATA *sigdata; + + PTM_COMPONENT *component; + int n_components = 0; + + int n; + + unsigned char *buffer; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) return NULL; + + /* Skip song name. */ + dumbfile_getnc((char *)sigdata->name, 28, f); + sigdata->name[28] = 0; + + if (dumbfile_getc(f) != 0x1A || dumbfile_igetw(f) != 0x203) { + free(sigdata); + return NULL; + } + + dumbfile_skip(f, 1); + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_orders = dumbfile_igetw(f); + sigdata->n_instruments = 0; + sigdata->n_samples = dumbfile_igetw(f); + sigdata->n_patterns = dumbfile_igetw(f); + + if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_samples > 255 || sigdata->n_patterns > 128) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->n_pchannels = dumbfile_igetw(f); + + if (dumbfile_igetw(f) != 0) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + dumbfile_skip(f, 2); + + if (dumbfile_mgetl(f) != DUMB_ID('P','T','M','F')) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + dumbfile_skip(f, 16); + + sigdata->order = malloc(sigdata->n_orders); + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->n_samples) { + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + } + + if (sigdata->n_patterns) { + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + } + + /** WARNING: which ones? */ + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_A_PTM; + + sigdata->global_volume = 128; + sigdata->speed = 6; + sigdata->tempo = 125; + sigdata->mixing_volume = 48; + + /* Panning positions for 32 channels */ + { + int i; + for (i = 0; i < 32; i++) { + int c = dumbfile_getc(f); + if (c <= 15) { + sigdata->channel_volume[i] = 64; + sigdata->channel_pan[i] = c; + } else { + /** WARNING: this could be improved if we support channel muting... */ + sigdata->channel_volume[i] = 0; + sigdata->channel_pan[i] = 7; + } + } + } + + /* Orders, byte each, length = sigdata->n_orders (should be even) */ + dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f); + sigdata->restart_position = 0; + + component = malloc(768*sizeof(*component)); + if (!component) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (dumbfile_seek(f, 352, DFS_SEEK_SET)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < sigdata->n_patterns; n++) { + component[n_components].type = PTM_COMPONENT_PATTERN; + component[n_components].n = n; + component[n_components].offset = dumbfile_igetw(f) << 4; + n_components++; + } + + if (dumbfile_seek(f, 608, DFS_SEEK_SET)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < sigdata->n_samples; n++) { + if (it_ptm_read_sample_header(&sigdata->sample[n], &component[n_components].offset, f)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + if (!(sigdata->sample[n].flags & IT_SAMPLE_EXISTS)) continue; + component[n_components].type = PTM_COMPONENT_SAMPLE; + component[n_components].n = n; + n_components++; + } + + qsort(component, n_components, sizeof(PTM_COMPONENT), &ptm_component_compare); + + { + int i; + for (i = 0; i < 32; i++) { + sigdata->channel_pan[i] -= (sigdata->channel_pan[i] & 8) >> 3; + sigdata->channel_pan[i] = ((int)sigdata->channel_pan[i] << 5) / 7; + if (sigdata->channel_pan[i] > 64) sigdata->channel_pan[i] = 64; + } + } + + sigdata->pan_separation = 128; + + if (dumbfile_error(f)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + buffer = malloc(65536); + if (!buffer) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < n_components; n++) { + if (dumbfile_seek(f, component[n].offset, DFS_SEEK_SET)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + switch (component[n].type) { + + case PTM_COMPONENT_PATTERN: + if (it_ptm_read_pattern(&sigdata->pattern[component[n].n], f, buffer, (n + 1 < n_components) ? (component[n+1].offset - component[n].offset) : 0)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + break; + + case PTM_COMPONENT_SAMPLE: + if (it_ptm_read_sample_data(&sigdata->sample[component[n].n], (n + 1 == n_components), f)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + } + + free(buffer); + free(component); + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + +DUH *DUMBEXPORT dumb_read_ptm_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_ptm_load_sigdata(f); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "PTM"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/readriff.c b/thirdparty/dumb/src/it/readriff.c new file mode 100644 index 000000000..4843f0527 --- /dev/null +++ b/thirdparty/dumb/src/it/readriff.c @@ -0,0 +1,57 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readriff.c - Code to read a RIFF module file / / \ \ + * from memory. | < / \_ + * | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" +#include "internal/riff.h" + + +DUH *dumb_read_riff_amff( DUMBFILE * f, struct riff * stream ); +DUH *dumb_read_riff_am( DUMBFILE * f, struct riff * stream ); +DUH *dumb_read_riff_dsmf( DUMBFILE * f, struct riff * stream ); + +/* dumb_read_riff_quick(): reads a RIFF file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must pass + * the pointer to unload_duh() so that the memory can be freed. + */ +DUH *DUMBEXPORT dumb_read_riff_quick( DUMBFILE * f ) +{ + DUH * duh; + struct riff * stream; + long size; + + size = dumbfile_get_size(f); + + stream = riff_parse( f, 0, size, 1 ); + if ( ! stream ) stream = riff_parse( f, 0, size, 0 ); + + if ( ! stream ) return 0; + + if ( stream->type == DUMB_ID( 'A', 'M', ' ', ' ' ) ) + duh = dumb_read_riff_am( f, stream ); + else if ( stream->type == DUMB_ID( 'A', 'M', 'F', 'F' ) ) + duh = dumb_read_riff_amff( f, stream ); + else if ( stream->type == DUMB_ID( 'D', 'S', 'M', 'F' ) ) + duh = dumb_read_riff_dsmf( f, stream ); + else duh = 0; + + riff_free( stream ); + + return duh; +} diff --git a/thirdparty/dumb/src/it/reads3m.c b/thirdparty/dumb/src/it/reads3m.c new file mode 100644 index 000000000..43b6128a4 --- /dev/null +++ b/thirdparty/dumb/src/it/reads3m.c @@ -0,0 +1,766 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * reads3m.c - Code to read a ScreamTracker 3 / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +// IT_STEREO... :o +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +static int it_s3m_read_sample_header(IT_SAMPLE *sample, int32 *offset, unsigned char *pack, int cwtv, DUMBFILE *f) +{ + unsigned char type; + int flags; + + type = dumbfile_getc(f); + + dumbfile_getnc((char *)sample->filename, 12, f); + sample->filename[12] = 0; + + if (type > 1) { + /** WARNING: no adlib support */ + dumbfile_skip(f, 3 + 12 + 1 + 1 + 2 + 2 + 2 + 12); + dumbfile_getnc((char *)sample->name, 28, f); + sample->name[28] = 0; + dumbfile_skip(f, 4); + sample->flags &= ~IT_SAMPLE_EXISTS; + return dumbfile_error(f); + } + + *offset = dumbfile_getc(f) << 20; + *offset += dumbfile_igetw(f) << 4; + + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = dumbfile_igetl(f); + + sample->default_volume = dumbfile_getc(f); + + dumbfile_skip(f, 1); + + flags = dumbfile_getc(f); + + if (flags < 0 || (flags != 0 && flags != 4)) + /* Sample is packed apparently (or error reading from file). We don't + * know how to read packed samples. + */ + return -1; + + *pack = flags; + + flags = dumbfile_getc(f); + + sample->C5_speed = dumbfile_igetl(f) << 1; + + /* Skip four unused bytes and three internal variables. */ + dumbfile_skip(f, 4+2+2+4); + + dumbfile_getnc((char *)sample->name, 28, f); + sample->name[28] = 0; + + if (type == 0 || sample->length <= 0) { + /* Looks like no-existy. Anyway, there's for sure no 'SCRS' ... */ + sample->flags &= ~IT_SAMPLE_EXISTS; + return dumbfile_error(f); + } + + if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','S')) + return -1; + + sample->global_volume = 64; + + sample->flags = IT_SAMPLE_EXISTS; + if (flags & 1) sample->flags |= IT_SAMPLE_LOOP; + + /* The ST3 TECH.DOC is unclear on this, but IMAGO Orpheus is not. Piece of crap. */ + + if (flags & 2) { + sample->flags |= IT_SAMPLE_STEREO; + + if ((cwtv & 0xF000) == 0x2000) { + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } + } + + if (flags & 4) { + sample->flags |= IT_SAMPLE_16BIT; + + if ((cwtv & 0xF000) == 0x2000) { + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } + } + + sample->default_pan = 0; // 0 = don't use, or 160 = centre? + + if (sample->flags & IT_SAMPLE_LOOP) { + if ((unsigned int)sample->loop_end > (unsigned int)sample->length) + /*sample->flags &= ~IT_SAMPLE_LOOP;*/ + sample->loop_end = sample->length; + else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end) + sample->flags &= ~IT_SAMPLE_LOOP; + else + /* ScreamTracker seems not to save what comes after the loop end + * point, but rather to assume it is a duplicate of what comes at + * the loop start point. I am not completely sure of this though. + * It is easy to evade; simply truncate the sample. + */ + sample->length = sample->loop_end; + } + + + //Do we need to set all these? + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + + + +static int it_s3m_read_sample_data(IT_SAMPLE *sample, int ffi, unsigned char pack, DUMBFILE *f) +{ + int32 n; + + int32 datasize = sample->length; + if (sample->flags & IT_SAMPLE_STEREO) datasize <<= 1; + + sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); + if (!sample->data) + return -1; + + if (pack == 4) { + if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0) + return -1; + } + else if (sample->flags & IT_SAMPLE_STEREO) { + if (sample->flags & IT_SAMPLE_16BIT) { + for (n = 0; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_igetw(f); + for (n = 1; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_igetw(f); + } else { + for (n = 0; n < datasize; n += 2) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + for (n = 1; n < datasize; n += 2) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + } + } else if (sample->flags & IT_SAMPLE_16BIT) + for (n = 0; n < sample->length; n++) + ((short *)sample->data)[n] = dumbfile_igetw(f); + else + for (n = 0; n < sample->length; n++) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + + if (dumbfile_error(f)) + return -1; + + if (ffi != 1) { + /* Convert to signed. */ + if (sample->flags & IT_SAMPLE_16BIT) + for (n = 0; n < datasize; n++) + ((short *)sample->data)[n] ^= 0x8000; + else + for (n = 0; n < datasize; n++) + ((signed char *)sample->data)[n] ^= 0x80; + } + + return 0; +} + + + +static int it_s3m_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer) +{ + int length; + int buflen = 0; + int bufpos = 0; + + IT_ENTRY *entry; + + unsigned char channel; + + /* Haha, this is hilarious! + * + * Well, after some experimentation, it seems that different S3M writers + * define the format in different ways. The S3M docs say that the first + * two bytes hold the "length of [the] packed pattern", and the packed + * pattern data follow. Judging by the contents of ARMANI.S3M, packaged + * with ScreamTracker itself, the measure of length _includes_ the two + * bytes used to store the length; in other words, we should read + * (length - 2) more bytes. However, aryx.s3m, packaged with ModPlug + * Tracker, excludes these two bytes, so (length) more bytes must be + * read. + * + * Call me crazy, but I just find it insanely funny that the format was + * misunderstood in this way :D + * + * Now we can't just risk reading two extra bytes, because then we + * overshoot, and DUMBFILEs don't support backward seeking (for a good + * reason). Luckily, there is a way. We can read the data little by + * little, and stop when we have 64 rows in memory. Provided we protect + * against buffer overflow, this method should work with all sensibly + * written S3M files. If you find one for which it does not work, please + * let me know at entheh@users.sf.net so I can look at it. + * + * "for a good reason" ? What's this nonsense? -kode54 + * + */ + + length = dumbfile_igetw(f); + + if (dumbfile_error(f) || !length) + return -1; + + pattern->n_rows = 0; + pattern->n_entries = 0; + + /* Read in the pattern data, little by little, and work out how many + * entries we need room for. Sorry, but this is just so funny... + */ + for (;;) { + unsigned char b = buffer[buflen++] = dumbfile_getc(f); + +#if 1 + static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5}; + channel = b & 31; + b >>= 5; + pattern->n_entries++; + if (b) { + if (buflen + used[b] >= 65536) return -1; + if (buflen + used[b] <= length) + dumbfile_getnc((char *)buffer + buflen, used[b], f); + else + memset(buffer + buflen, 0, used[b]); + buflen += used[b]; + } else { + /* End of row */ + if (++pattern->n_rows == 64) break; + if (buflen >= 65536) return -1; + } +#else + if (b == 0) { + /* End of row */ + pattern->n_entries++; + if (++pattern->n_rows == 64) break; + if (buflen >= 65536) return -1; + } else { + static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5}; + channel = b & 31; + b >>= 5; + if (b) { + pattern->n_entries++; + if (buflen + used[b] >= 65536) return -1; + dumbfile_getnc(buffer + buflen, used[b], f); + buflen += used[b]; + } + } +#endif + + /* We have ensured that buflen < 65536 at this point, so it is safe + * to iterate and read at least one more byte without checking. + * However, now would be a good time to check for errors reading from + * the file. + */ + + if (dumbfile_error(f)) + return -1; + + /* Great. We ran out of data, but there should be data for more rows. + * Fill the rest with null data... + */ + if (buflen >= length && pattern->n_rows < 64) + { + while (pattern->n_rows < 64) + { + if (buflen >= 65536) return -1; + buffer[buflen++] = 0; + pattern->n_entries++; + pattern->n_rows++; + } + break; + } + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + + if (!pattern->entry) + return -1; + + entry = pattern->entry; + + while (bufpos < buflen) { + unsigned char b = buffer[bufpos++]; + +#if 1 + if (!(b & ~31)) +#else + if (b == 0) +#endif + { + /* End of row */ + IT_SET_END_ROW(entry); + entry++; + continue; + } + + channel = b & 31; + + if (b & 224) { + entry->mask = 0; + entry->channel = channel; + + if (b & 32) { + unsigned char n = buffer[bufpos++]; + if (n != 255) { + if (n == 254) + entry->note = IT_NOTE_CUT; + else + entry->note = (n >> 4) * 12 + (n & 15); + entry->mask |= IT_ENTRY_NOTE; + } + + entry->instrument = buffer[bufpos++]; + if (entry->instrument) + entry->mask |= IT_ENTRY_INSTRUMENT; + } + + if (b & 64) { + entry->volpan = buffer[bufpos++]; + if (entry->volpan != 255) + entry->mask |= IT_ENTRY_VOLPAN; + } + + if (b & 128) { + entry->effect = buffer[bufpos++]; + entry->effectvalue = buffer[bufpos++]; + // XXX woot + if (entry->effect && entry->effect < IT_MIDI_MACRO /*!= 255*/) { + entry->mask |= IT_ENTRY_EFFECT; + switch (entry->effect) { + case IT_BREAK_TO_ROW: + entry->effectvalue -= (entry->effectvalue >> 4) * 6; + break; + + case IT_SET_CHANNEL_VOLUME: + case IT_CHANNEL_VOLUME_SLIDE: + case IT_PANNING_SLIDE: + case IT_GLOBAL_VOLUME_SLIDE: + case IT_PANBRELLO: + case IT_MIDI_MACRO: + entry->mask &= ~IT_ENTRY_EFFECT; + break; + + case IT_S: + switch (entry->effectvalue >> 4) { + case IT_S_SET_PANBRELLO_WAVEFORM: + case IT_S_FINE_PATTERN_DELAY: + case IT_S7: + case IT_S_SET_SURROUND_SOUND: + case IT_S_SET_MIDI_MACRO: + entry->mask &= ~IT_ENTRY_EFFECT; + break; + } + break; + } + } + /** WARNING: ARGH! CONVERT TEH EFFECTS!@~ */ + } + + entry++; + } + } + + ASSERT(entry == pattern->entry + pattern->n_entries); + + return 0; +} + + + +/** WARNING: this is duplicated in itread.c - also bad practice to use the same struct name unless they are unified in a header */ +/* Currently we assume the sample data are stored after the sample headers in + * module files. This assumption may be unjustified; let me know if you have + * trouble. + */ + +#define S3M_COMPONENT_INSTRUMENT 1 +#define S3M_COMPONENT_PATTERN 2 +#define S3M_COMPONENT_SAMPLE 3 + +typedef struct S3M_COMPONENT +{ + unsigned char type; + unsigned char n; + int32 offset; + short sampfirst; /* component[sampfirst] = first sample data after this */ + short sampnext; /* sampnext is used to create linked lists of sample data */ +} +S3M_COMPONENT; + + + +static int CDECL s3m_component_compare(const void *e1, const void *e2) +{ + return ((const S3M_COMPONENT *)e1)->offset - + ((const S3M_COMPONENT *)e2)->offset; +} + + + +static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f, int * cwtv) +{ + DUMB_IT_SIGDATA *sigdata; + + int flags, ffi; + int default_pan_present; + + int master_volume; + + unsigned char sample_pack[256]; + + S3M_COMPONENT *component; + int n_components = 0; + + int n; + + unsigned char *buffer; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) return NULL; + + dumbfile_getnc((char *)sigdata->name, 28, f); + sigdata->name[28] = 0; + + n = dumbfile_getc(f); + + if (n != 0x1A && n != 0) { + free(sigdata); + return NULL; + } + + if (dumbfile_getc(f) != 16) { + free(sigdata); + return NULL; + } + + dumbfile_skip(f, 2); + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_orders = dumbfile_igetw(f); + sigdata->n_instruments = 0; + sigdata->n_samples = dumbfile_igetw(f); + sigdata->n_patterns = dumbfile_igetw(f); + + if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_samples > 256 || sigdata->n_patterns > 256) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->order = malloc(sigdata->n_orders); + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->n_samples) { + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + } + + if (sigdata->n_patterns) { + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + } + + flags = dumbfile_igetw(f); + + *cwtv = dumbfile_igetw(f); + + if (*cwtv == 0x1300) { + /** WARNING: volume slides on every frame */ + } + + ffi = dumbfile_igetw(f); + + /** WARNING: which ones? */ + sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M; + + if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','M')) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->global_volume = dumbfile_getc(f); + if ( !sigdata->global_volume || sigdata->global_volume > 64 ) sigdata->global_volume = 64; + sigdata->speed = dumbfile_getc(f); + if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo? + sigdata->tempo = dumbfile_getc(f); + master_volume = dumbfile_getc(f); // 7 bits; +128 for stereo + sigdata->mixing_volume = master_volume & 127; + + if (master_volume & 128) sigdata->flags |= IT_STEREO; + + /* Skip GUS Ultra Click Removal byte. */ + dumbfile_getc(f); + + default_pan_present = dumbfile_getc(f); + + dumbfile_skip(f, 8); + + /* Skip Special Custom Data Pointer. */ + /** WARNING: investigate this? */ + dumbfile_igetw(f); + + sigdata->n_pchannels = 0; + /* Channel settings for 32 channels, 255=unused, +128=disabled */ + { + int i; + int sep = (7 * dumb_it_default_panning_separation + 50) / 100; + for (i = 0; i < 32; i++) { + int c = dumbfile_getc(f); + if (!(c & (128 | 16))) { /* +128=disabled, +16=Adlib */ + if (sigdata->n_pchannels < i + 1) sigdata->n_pchannels = i + 1; + sigdata->channel_volume[i] = 64; + sigdata->channel_pan[i] = c & 8 ? 7 + sep : 7 - sep; + /** WARNING: ah, but it should be 7 for mono... */ + } else { + /** WARNING: this could be improved if we support channel muting... */ + sigdata->channel_volume[i] = 0; + sigdata->channel_pan[i] = 7; + } + } + } + + /* Orders, byte each, length = sigdata->n_orders (should be even) */ + dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f); + sigdata->restart_position = 0; + + component = malloc(768*sizeof(*component)); + if (!component) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < sigdata->n_samples; n++) { + component[n_components].type = S3M_COMPONENT_SAMPLE; + component[n_components].n = n; + component[n_components].offset = dumbfile_igetw(f) << 4; + component[n_components].sampfirst = -1; + n_components++; + } + + for (n = 0; n < sigdata->n_patterns; n++) { + int32 offset = dumbfile_igetw(f) << 4; + if (offset) { + component[n_components].type = S3M_COMPONENT_PATTERN; + component[n_components].n = n; + component[n_components].offset = offset; + component[n_components].sampfirst = -1; + n_components++; + } else { + /** WARNING: Empty 64-row pattern ... ? (this does happen!) */ + sigdata->pattern[n].n_rows = 64; + sigdata->pattern[n].n_entries = 0; + } + } + + qsort(component, n_components, sizeof(S3M_COMPONENT), &s3m_component_compare); + + /* I found a really dumb S3M file that claimed to contain default pan + * data but didn't contain any. Programs would load it by reading part of + * the first instrument header, assuming the data to be default pan + * positions, and then rereading the instrument module. We cannot do this + * without obfuscating the file input model, so we insert an extra check + * here that we won't overrun the start of the first component. + */ + if (default_pan_present == 252 && component[0].offset >= dumbfile_pos(f) + 32) { + /* Channel default pan positions */ + int i; + for (i = 0; i < 32; i++) { + int c = dumbfile_getc(f); + if (c & 32) + sigdata->channel_pan[i] = c & 15; + } + } + + { + int i; + for (i = 0; i < 32; i++) { + sigdata->channel_pan[i] -= (sigdata->channel_pan[i] & 8) >> 3; + sigdata->channel_pan[i] = ((int)sigdata->channel_pan[i] << 5) / 7; + } + } + + sigdata->pan_separation = 128; + + if (dumbfile_error(f)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + buffer = malloc(65536); + if (!buffer) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < n_components; n++) { + int32 offset; + int m; + + offset = 0; + if (dumbfile_seek(f, component[n].offset, DFS_SEEK_SET)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + switch (component[n].type) { + + case S3M_COMPONENT_PATTERN: + if (it_s3m_read_pattern(&sigdata->pattern[component[n].n], f, buffer)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + break; + + case S3M_COMPONENT_SAMPLE: + if (it_s3m_read_sample_header(&sigdata->sample[component[n].n], &offset, &sample_pack[component[n].n], *cwtv, f)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) { + short *sample; + + for (m = n + 1; m < n_components; m++) + if (component[m].offset > offset) + break; + m--; + + sample = &component[m].sampfirst; + + while (*sample >= 0 && component[*sample].offset <= offset) + sample = &component[*sample].sampnext; + + component[n].sampnext = *sample; + *sample = n; + + component[n].offset = offset; + } + } + + m = component[n].sampfirst; + + while (m >= 0) { + // XXX + if (dumbfile_seek(f, component[m].offset, DFS_SEEK_SET)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (it_s3m_read_sample_data(&sigdata->sample[component[m].n], ffi, sample_pack[component[m].n], f)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + m = component[m].sampnext; + } + } + + free(buffer); + free(component); + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + +static char hexdigit(int in) +{ + if (in < 10) return in + '0'; + else return in + 'A' - 10; +} + +DUH *DUMBEXPORT dumb_read_s3m_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + int cwtv; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_s3m_load_sigdata(f, &cwtv); + + if (!sigdata) + return NULL; + + { + char version[8]; + const char *tag[3][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "S3M"; + tag[2][0] = "TRACKERVERSION"; + version[0] = hexdigit((cwtv >> 8) & 15); + version[1] = '.'; + version[2] = hexdigit((cwtv >> 4) & 15); + version[3] = hexdigit(cwtv & 15); + version[4] = 0; + tag[2][1] = (const char *) &version; + return make_duh(-1, 3, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/reads3m2.c b/thirdparty/dumb/src/it/reads3m2.c new file mode 100644 index 000000000..e7d34de33 --- /dev/null +++ b/thirdparty/dumb/src/it/reads3m2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * reads3m2.c - Function to read a ScreamTracker 3 / / \ \ + * module from an open file and do an | < / \_ + * initial run-through. | \/ /\ / + * \_ / > / + * Split off from reads3m.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_read_s3m(DUMBFILE *f) +{ + DUH *duh = dumb_read_s3m_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/readstm.c b/thirdparty/dumb/src/it/readstm.c new file mode 100644 index 000000000..a2ae69033 --- /dev/null +++ b/thirdparty/dumb/src/it/readstm.c @@ -0,0 +1,397 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readstm.c - Code to read a ScreamTracker 2 / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +// IT_STEREO... :o +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +#ifdef _MSC_VER + #define strnicmp _strnicmp +#else + #if defined(unix) || defined(__unix__) || defined(__unix) + #include + #endif + #define strnicmp strncasecmp +#endif + +static int it_stm_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f, unsigned short *offset ) +{ + dumbfile_getnc( (char *) sample->filename, 12, f ); + sample->filename[12] = 0; + + memcpy( sample->name, sample->filename, 13 ); + + dumbfile_skip( f, 2 ); + + *offset = dumbfile_igetw( f ); + + sample->length = dumbfile_igetw( f ); + sample->loop_start = dumbfile_igetw( f ); + sample->loop_end = dumbfile_igetw( f ); + + sample->default_volume = dumbfile_getc( f ); + + dumbfile_skip( f, 1 ); + + sample->C5_speed = dumbfile_igetw( f ) << 3; + + dumbfile_skip( f, 6 ); + + if ( sample->length < 4 || !sample->default_volume ) { + /* Looks like no-existy. */ + sample->flags &= ~IT_SAMPLE_EXISTS; + sample->length = 0; + *offset = 0; + return dumbfile_error( f ); + } + + sample->flags = IT_SAMPLE_EXISTS; + sample->global_volume = 64; + sample->default_pan = 0; // 0 = don't use, or 160 = centre? + + if ( ( sample->loop_start < sample->length ) && + ( sample->loop_end > sample->loop_start ) && + ( sample->loop_end != 0xFFFF ) ) { + sample->flags |= IT_SAMPLE_LOOP; + if ( sample->loop_end > sample->length ) sample->loop_end = sample->length; + } + + //Do we need to set all these? + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + +static int it_stm_read_sample_data( IT_SAMPLE *sample, DUMBFILE * f ) +{ + if ( ! sample->length ) return 0; + + sample->data = malloc( sample->length ); + if (!sample->data) + return -1; + + dumbfile_getnc( sample->data, sample->length, f ); + + return dumbfile_error( f ); +} + +static int it_stm_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer ) +{ + int pos; + int channel; + int row; + IT_ENTRY *entry; + + pattern->n_rows = 64; + + if ( dumbfile_getnc( (char *) buffer, 64 * 4 * 4, f ) != 64 * 4 * 4 ) + return -1; + + pattern->n_entries = 64; + pos = 0; + for ( row = 0; row < 64; ++row ) { + for ( channel = 0; channel < 4; ++channel ) { + if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] ) + ++pattern->n_entries; + pos += 4; + } + } + + pattern->entry = malloc( pattern->n_entries * sizeof( *pattern->entry ) ); + if ( !pattern->entry ) + return -1; + + entry = pattern->entry; + pos = 0; + for ( row = 0; row < 64; ++row ) { + for ( channel = 0; channel < 4; ++channel ) { + if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] ) { + unsigned note; + note = buffer[ pos + 0 ]; + entry->channel = channel; + entry->mask = 0; + entry->instrument = buffer[ pos + 1 ] >> 3; + entry->volpan = ( buffer[ pos + 1 ] & 0x07 ) + ( buffer[ pos + 2 ] >> 1 ); + entry->effect = buffer[ pos + 2 ] & 0x0F; + entry->effectvalue = buffer[ pos + 3 ]; + if ( entry->instrument && entry->instrument < 32 ) + entry->mask |= IT_ENTRY_INSTRUMENT; + if ( note < 251 ) { + entry->mask |= IT_ENTRY_NOTE; + entry->note = ( note >> 4 ) * 12 + ( note & 0x0F ); + } + if ( entry->volpan <= 64 ) + entry->mask |= IT_ENTRY_VOLPAN; + entry->mask |= IT_ENTRY_EFFECT; + switch ( entry->effect ) { + case IT_SET_SPEED: + /* taken care of in the renderer */ + break; + + case IT_BREAK_TO_ROW: + entry->effectvalue -= (entry->effectvalue >> 4) * 6; + break; + + case IT_JUMP_TO_ORDER: + case IT_VOLUME_SLIDE: + case IT_PORTAMENTO_DOWN: + case IT_PORTAMENTO_UP: + case IT_TONE_PORTAMENTO: + case IT_VIBRATO: + case IT_TREMOR: + case IT_ARPEGGIO: + case IT_VOLSLIDE_VIBRATO: + case IT_VOLSLIDE_TONEPORTA: + break; + + default: + entry->mask &= ~IT_ENTRY_EFFECT; + break; + } + if ( entry->mask ) ++entry; + } + pos += 4; + } + IT_SET_END_ROW(entry); + ++entry; + } + + pattern->n_entries = (int)(entry - pattern->entry); + + return 0; +} + + + +static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f, int * version) +{ + DUMB_IT_SIGDATA *sigdata; + + char tracker_name[ 8 ]; + + unsigned short sample_offset[ 31 ]; + + int n; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) return NULL; + + /* Skip song name. */ + dumbfile_getnc((char *)sigdata->name, 20, f); + sigdata->name[20] = 0; + + dumbfile_getnc(tracker_name, 8, f); + n = dumbfile_getc(f); + if ( n != 0x02 && n != 0x1A && n != 0x1B ) + { + free( sigdata ); + return NULL; + } + if ( dumbfile_getc(f) != 2 ) /* only support modules */ + { + free( sigdata ); + return NULL; + } + if ( strnicmp( tracker_name, "!Scream!", 8 ) && + strnicmp( tracker_name, "BMOD2STM", 8 ) && + strnicmp( tracker_name, "WUZAMOD!", 8 ) ) + { + free( sigdata ); + return NULL; + } + + *version = dumbfile_mgetw(f); + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + sigdata->n_samples = 31; + sigdata->n_pchannels = 4; + + sigdata->tempo = 125; + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + /** WARNING: which ones? */ + sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M | IT_WAS_AN_STM | IT_STEREO; + + n = dumbfile_getc(f); + if ( n < 32 ) n = 32; + sigdata->speed = n; + sigdata->n_patterns = dumbfile_getc(f); + sigdata->global_volume = dumbfile_getc(f) << 1; + if ( sigdata->global_volume > 128 ) sigdata->global_volume = 128; + + dumbfile_skip(f, 13); + + if ( dumbfile_error(f) || sigdata->n_patterns < 1 || sigdata->n_patterns > 99 ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + + if (sigdata->n_patterns) { + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + } + + memset( sigdata->channel_volume, 64, 4 ); + n = 32 * dumb_it_default_panning_separation / 100; + sigdata->channel_pan[ 0 ] = 32 + n; + sigdata->channel_pan[ 1 ] = 32 - n; + sigdata->channel_pan[ 2 ] = 32 + n; + sigdata->channel_pan[ 3 ] = 32 - n; + + for ( n = 0; n < sigdata->n_samples; ++n ) { + if ( it_stm_read_sample_header( &sigdata->sample[ n ], f, &sample_offset[ n ] ) ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + + sigdata->order = malloc( 128 ); + if ( !sigdata->order ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + + /* Orders, byte each, length = sigdata->n_orders (should be even) */ + dumbfile_getnc( (char *) sigdata->order, *version >= 0x200 ? 128 : 64, f ); + if (*version < 0x200) memset( sigdata->order + 64, 0xFF, 64 ); + sigdata->restart_position = 0; + + for ( n = 127; n >= 0; --n ) { + if ( sigdata->order[ n ] < sigdata->n_patterns ) break; + } + if ( n < 0 ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + sigdata->n_orders = n + 1; + + for ( n = 0; n < 128; ++n ) { + if ( sigdata->order[ n ] >= 99 ) sigdata->order[ n ] = 0xFF; + } + + if ( sigdata->n_patterns ) { + unsigned char * buffer = malloc( 64 * 4 * 4 ); + if ( ! buffer ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + for ( n = 0; n < sigdata->n_patterns; ++n ) { + if ( it_stm_read_pattern( &sigdata->pattern[ n ], f, buffer ) ) { + free( buffer ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + free( buffer ); + } + + for ( n = 0; n < sigdata->n_samples; ++n ) { + if ( sample_offset[ n ] ) + { + if ( dumbfile_seek( f, sample_offset[ n ] * 16, DFS_SEEK_SET ) || + it_stm_read_sample_data( &sigdata->sample[ n ], f ) ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + else + { + sigdata->sample[ n ].flags = 0; + sigdata->sample[ n ].length = 0; + } + } + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + +DUH *DUMBEXPORT dumb_read_stm_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + int ver; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_stm_load_sigdata(f , &ver); + + if (!sigdata) + return NULL; + + { + char version[16]; + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + version[0] = 'S'; + version[1] = 'T'; + version[2] = 'M'; + version[3] = ' '; + version[4] = 'v'; + version[5] = '0' + ((ver >> 8) & 15); + version[6] = '.'; + if ((ver & 255) > 99) + { + version[7] = '0' + ((ver & 255) / 100 ); + version[8] = '0' + (((ver & 255) / 10) % 10); + version[9] = '0' + ((ver & 255) % 10); + version[10] = 0; + } + else + { + version[7] = '0' + ((ver & 255) / 10); + version[8] = '0' + ((ver & 255) % 10); + version[9] = 0; + } + tag[1][1] = (const char *) &version; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/readstm2.c b/thirdparty/dumb/src/it/readstm2.c new file mode 100644 index 000000000..bd78eaf69 --- /dev/null +++ b/thirdparty/dumb/src/it/readstm2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readstm2.c - Function to read a ScreamTracker 2 / / \ \ + * module from an open file and do an | < / \_ + * initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_read_stm(DUMBFILE *f) +{ + DUH *duh = dumb_read_stm_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/readxm.c b/thirdparty/dumb/src/it/readxm.c new file mode 100644 index 000000000..e3c082e91 --- /dev/null +++ b/thirdparty/dumb/src/it/readxm.c @@ -0,0 +1,1530 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readxm.c - Code to read a Fast Tracker II / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By Julien Cugniere. Some bits of code taken \_ / > / + * from reads3m.c. | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" +#include "internal/dumbfile.h" + +#include +#include + +short * dumb_decode_vorbis(int outlen, const void *oggstream, int sizebytes); + +/** TODO: + + * XM_TREMOLO doesn't sound quite right... + * XM_SET_ENVELOPE_POSITION todo. + + * VIBRATO conversion needs to be checked (sample/effect/volume). Plus check + that effect memory is correct when using XM_VOLSLIDE_VIBRATO. + - sample vibrato (instrument vibrato) is now handled correctly. - entheh + + * XM_E_SET_VIBRATO/TREMOLO_CONTROL: effectvalue&4 -> don't retrig wave when + a new instrument is played. In retrigger_note()?. Is it worth implementing? + + * Lossy fadeout approximation. 0..31 converted to 0 --> won't fade at all. + + * Replace DUMB's sawtooth by ramp_down/ramp_up. Update XM loader. + + * A lot of things need to be reset when the end of the song is reached. + + * It seems that IT and XM don't behave the same way when dealing with + mixed loops. When IT encounters multiple SBx (x>0) commands on the same + row, it decrements the loop count for all, but only execute the loop of + the last one (highest channel). FT2 only decrements the loop count of the + last one. Not that I know of any modules using so convoluted combinations! + + * Maybe we could remove patterns that don't appear in the order table ? Or + provide a function to "optimize" a DUMB_IT_SIGDATA ? + +*/ + + + +#define XM_LINEAR_FREQUENCY 1 /* otherwise, use amiga slides */ + +#define XM_ENTRY_PACKED 128 +#define XM_ENTRY_NOTE 1 +#define XM_ENTRY_INSTRUMENT 2 +#define XM_ENTRY_VOLUME 4 +#define XM_ENTRY_EFFECT 8 +#define XM_ENTRY_EFFECTVALUE 16 + +#define XM_NOTE_OFF 97 + +#define XM_ENVELOPE_ON 1 +#define XM_ENVELOPE_SUSTAIN 2 +#define XM_ENVELOPE_LOOP 4 + +#define XM_SAMPLE_NO_LOOP 0 +#define XM_SAMPLE_FORWARD_LOOP 1 +#define XM_SAMPLE_PINGPONG_LOOP 2 +#define XM_SAMPLE_16BIT 16 +#define XM_SAMPLE_STEREO 32 + +#define XM_VIBRATO_SINE 0 +#define XM_VIBRATO_SQUARE 1 +#define XM_VIBRATO_RAMP_DOWN 2 +#define XM_VIBRATO_RAMP_UP 3 + + + +/* Probably useless :) */ +const char xm_convert_vibrato[] = { + IT_VIBRATO_SINE, + IT_VIBRATO_XM_SQUARE, + IT_VIBRATO_RAMP_DOWN, + IT_VIBRATO_RAMP_UP, + IT_VIBRATO_RANDOM +}; + + + +#define XM_MAX_SAMPLES_PER_INSTRUMENT 16 + + + +/* Extra data that doesn't fit inside IT_INSTRUMENT */ +typedef struct XM_INSTRUMENT_EXTRA +{ + int n_samples; + int vibrato_type; + int vibrato_sweep; /* 0-0xFF */ + int vibrato_depth; /* 0-0x0F */ + int vibrato_speed; /* 0-0x3F */ + int sample_header_size; +} +XM_INSTRUMENT_EXTRA; + + + +/* Trims off trailing white space, usually added by the tracker on file creation + */ +static void trim_whitespace(char *ptr, size_t size) +{ + char *p = ptr + size - 1; + while (p >= ptr && *p <= 0x20) *p-- = '\0'; +} + +/* Frees the original block if it can't resize it or if size is 0, and acts + * as malloc if ptr is NULL. + */ +static void *safe_realloc(void *ptr, size_t size) +{ + if (ptr == NULL) + return malloc(size); + + if (size == 0) { + free(ptr); + return NULL; + } else { + void *new_block = realloc(ptr, size); + if (!new_block) + free(ptr); + return new_block; + } +} + + + +/* The interpretation of the XM volume column is left to the player. Here, we + * just filter bad values. + */ +// This function is so tiny now, should we inline it? +static void it_xm_convert_volume(int volume, IT_ENTRY *entry) +{ + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = volume; + + switch (volume >> 4) { + case 0xA: /* set vibrato speed */ + case 0xB: /* vibrato */ + case 0xF: /* tone porta */ + case 0x6: /* vol slide up */ + case 0x7: /* vol slide down */ + case 0x8: /* fine vol slide up */ + case 0x9: /* fine vol slide down */ + case 0xC: /* set panning */ + case 0xD: /* pan slide left */ + case 0xE: /* pan slide right */ + case 0x1: /* set volume */ + case 0x2: /* set volume */ + case 0x3: /* set volume */ + case 0x4: /* set volume */ + break; + + case 0x5: + if (volume == 0x50) + break; /* set volume */ + /* else fall through */ + + default: + entry->mask &= ~IT_ENTRY_VOLPAN; + break; + } +} + + + +static int it_xm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer, int version) +{ + int size; + int pos; + int channel; + int row; + int effect, effectvalue; + IT_ENTRY *entry; + + /* pattern header size */ + if (dumbfile_igetl(f) != ( version == 0x0102 ? 0x08 : 0x09 ) ) { + TRACE("XM error: unexpected pattern header size\n"); + return -1; + } + + /* pattern data packing type */ + if (dumbfile_getc(f) != 0) { + TRACE("XM error: unexpected pattern packing type\n"); + return -1; + } + + if ( version == 0x0102 ) + pattern->n_rows = dumbfile_getc(f) + 1; + else + pattern->n_rows = dumbfile_igetw(f); /* 1..256 */ + size = dumbfile_igetw(f); + pattern->n_entries = 0; + + if (dumbfile_error(f)) + return -1; + + if (size == 0) + return 0; + + if (size > 1280 * n_channels) { + TRACE("XM error: pattern data size > %d bytes\n", 1280 * n_channels); + return -1; + } + + if (dumbfile_getnc((char *)buffer, size, f) < size) + return -1; + + /* compute number of entries */ + pattern->n_entries = 0; + pos = channel = row = 0; + while (pos < size) { + if (!(buffer[pos] & XM_ENTRY_PACKED) || (buffer[pos] & 31)) + pattern->n_entries++; + + channel++; + if (channel >= n_channels) { + channel = 0; + row++; + pattern->n_entries++; + } + + if (buffer[pos] & XM_ENTRY_PACKED) { + static const char offset[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5 }; + pos += 1 + offset[buffer[pos] & 31]; + } else { + pos += 5; + } + } + + if (row > pattern->n_rows) { + TRACE("XM error: wrong number of rows in pattern data\n"); + return -1; + } + + /* Whoops, looks like some modules may be short, a few channels, maybe even rows... */ + + while (row < pattern->n_rows) + { + pattern->n_entries++; + row++; + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + if (!pattern->entry) + return -1; + + /* read the entries */ + entry = pattern->entry; + pos = channel = row = 0; + while (pos < size) { + unsigned char mask; + + if (buffer[pos] & XM_ENTRY_PACKED) + mask = buffer[pos++] & 31; + else + mask = 31; + + if (mask) { + ASSERT(entry < pattern->entry + pattern->n_entries); + + entry->channel = channel; + entry->mask = 0; + + if (mask & XM_ENTRY_NOTE) { + int note = buffer[pos++]; /* 1-96 <=> C0-B7 */ + entry->note = (note == XM_NOTE_OFF) ? (IT_NOTE_OFF) : (note-1); + entry->mask |= IT_ENTRY_NOTE; + } + + if (mask & XM_ENTRY_INSTRUMENT) { + entry->instrument = buffer[pos++]; /* 1-128 */ + entry->mask |= IT_ENTRY_INSTRUMENT; + } + + if (mask & XM_ENTRY_VOLUME) + it_xm_convert_volume(buffer[pos++], entry); + + effect = effectvalue = 0; + if (mask & XM_ENTRY_EFFECT) effect = buffer[pos++]; + if (mask & XM_ENTRY_EFFECTVALUE) effectvalue = buffer[pos++]; + _dumb_it_xm_convert_effect(effect, effectvalue, entry, 0); + + entry++; + } + + channel++; + if (channel >= n_channels) { + channel = 0; + row++; + IT_SET_END_ROW(entry); + entry++; + } + } + + while (row < pattern->n_rows) + { + row++; + IT_SET_END_ROW(entry); + entry++; + } + + return 0; +} + + + +static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data, int y_offset) +{ + int i, pos, val; + + if (envelope->n_nodes > 12) { + /* XXX + TRACE("XM error: wrong number of envelope nodes (%d)\n", envelope->n_nodes); + envelope->n_nodes = 0; + return -1; */ + envelope->n_nodes = 12; + } + + if (envelope->sus_loop_start >= 12) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP; + if (envelope->loop_end >= 12) envelope->loop_end = 0; + if (envelope->loop_start >= envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON; + + pos = 0; + for (i = 0; i < envelope->n_nodes; i++) { + envelope->node_t[i] = data[pos++]; + val = data[pos++]; + if (val > 64) { + TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, val); + /* FT2 seems to simply clip the value */ + val = 64; + } + envelope->node_y[i] = (signed char)(val + y_offset); + } + + return 0; +} + + + +typedef struct LIMITED_XM LIMITED_XM; + +struct LIMITED_XM +{ + unsigned char *buffered; + long ptr, limit, allocated; + DUMBFILE *remaining; +}; + +static int DUMBCALLBACK limit_xm_resize(void *f, long n) +{ + DUMBFILE *df = f; + LIMITED_XM *lx = df->file; + if (lx->buffered || n) { + if (n > lx->allocated) { + unsigned char *buffered = realloc( lx->buffered, n ); + if ( !buffered ) return -1; + lx->buffered = buffered; + memset( buffered + lx->allocated, 0, n - lx->allocated ); + lx->allocated = n; + } + if ( dumbfile_getnc( (char *)lx->buffered, n, lx->remaining ) < n ) return -1; + } else if (!n) { + if ( lx->buffered ) free( lx->buffered ); + lx->buffered = NULL; + lx->allocated = 0; + } + lx->limit = n; + lx->ptr = 0; + return 0; +} + +static int DUMBCALLBACK limit_xm_skip_end(void *f, int32 n) +{ + DUMBFILE *df = f; + LIMITED_XM *lx = df->file; + return dumbfile_skip( lx->remaining, n ); +} + +static int DUMBCALLBACK limit_xm_skip(void *f, long n) +{ + LIMITED_XM *lx = f; + lx->ptr += n; + return 0; +} + + + +static int DUMBCALLBACK limit_xm_getc(void *f) +{ + LIMITED_XM *lx = f; + if (lx->ptr >= lx->allocated) { + return 0; + } + return lx->buffered[lx->ptr++]; +} + + + +static int32 DUMBCALLBACK limit_xm_getnc(char *ptr, int32 n, void *f) +{ + LIMITED_XM *lx = f; + int left; + left = lx->allocated - lx->ptr; + if (n > left) { + if (left > 0) { + memcpy( ptr, lx->buffered + lx->ptr, left ); + memset( ptr + left, 0, n - left ); + } else { + memset( ptr, 0, n ); + } + } else { + memcpy( ptr, lx->buffered + lx->ptr, n ); + } + lx->ptr += n; + return n; +} + + + +static void DUMBCALLBACK limit_xm_close(void *f) +{ + LIMITED_XM *lx = f; + if (lx->buffered) free(lx->buffered); + /* Do NOT close lx->remaining */ + free(f); +} + + + +/* These two can be stubs since this implementation doesn't use seeking */ +static int DUMBCALLBACK limit_xm_seek(void *f, long n) +{ + (void)f; + (void)n; + return 1; +} + + + +static long DUMBCALLBACK limit_xm_get_size(void *f) +{ + (void)f; + return 0; +} + + + +DUMBFILE_SYSTEM limit_xm_dfs = { + NULL, + &limit_xm_skip, + &limit_xm_getc, + &limit_xm_getnc, + &limit_xm_close, + &limit_xm_seek, + &limit_xm_get_size +}; + +static DUMBFILE *dumbfile_limit_xm(DUMBFILE *f) +{ + LIMITED_XM * lx = malloc(sizeof(*lx)); + lx->remaining = f; + lx->buffered = NULL; + lx->ptr = 0; + lx->limit = 0; + lx->allocated = 0; + return dumbfile_open_ex( lx, &limit_xm_dfs ); +} + +static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA *extra, DUMBFILE *f) +{ + uint32 size, bytes_read; + unsigned short vol_points[24]; + unsigned short pan_points[24]; + int i, type; + const unsigned long max_size = 4 + 22 + 1 + 2 + 4 + 96 + 48 + 48 + 1 * 14 + 2 + 2; + unsigned long skip_end = 0; + + /* Header size. Tends to be more than the actual size of the structure. + * So unread bytes must be skipped before reading the first sample + * header. + */ + + if ( limit_xm_resize( f, 4 ) < 0 ) return -1; + + size = dumbfile_igetl(f); + + if ( size == 0 ) size = max_size; + else if ( size > max_size ) + { + skip_end = size - max_size; + size = max_size; + } + + if ( limit_xm_resize( f, size - 4 ) < 0 ) return -1; + + dumbfile_getnc((char *)instrument->name, 22, f); + instrument->name[22] = 0; + trim_whitespace((char *)instrument->name, 22); + instrument->filename[0] = 0; + dumbfile_skip(f, 1); /* Instrument type. Should be 0, but seems random. */ + extra->n_samples = dumbfile_igetw(f); + + if (dumbfile_error(f) || (unsigned int)extra->n_samples > XM_MAX_SAMPLES_PER_INSTRUMENT) + return -1; + + bytes_read = 4 + 22 + 1 + 2; + + if (extra->n_samples) { + /* sample header size */ + /*i = dumbfile_igetl(f); + if (!i || i > 0x28) i = 0x28;*/ + dumbfile_skip(f, 4); + i = 0x28; + extra->sample_header_size = i; + + /* sample map */ + for (i = 0; i < 96; i++) { + instrument->map_sample[i] = dumbfile_getc(f) + 1; + instrument->map_note[i] = i; + } + + if (dumbfile_error(f)) + return 1; + + /* volume/panning envelopes */ + for (i = 0; i < 24; i++) + vol_points[i] = dumbfile_igetw(f); + for (i = 0; i < 24; i++) + pan_points[i] = dumbfile_igetw(f); + + instrument->volume_envelope.n_nodes = dumbfile_getc(f); + instrument->pan_envelope.n_nodes = dumbfile_getc(f); + + if (dumbfile_error(f)) + return -1; + + instrument->volume_envelope.sus_loop_start = dumbfile_getc(f); + instrument->volume_envelope.loop_start = dumbfile_getc(f); + instrument->volume_envelope.loop_end = dumbfile_getc(f); + + instrument->pan_envelope.sus_loop_start = dumbfile_getc(f); + instrument->pan_envelope.loop_start = dumbfile_getc(f); + instrument->pan_envelope.loop_end = dumbfile_getc(f); + + /* The envelope handler for XM files won't use sus_loop_end. */ + + type = dumbfile_getc(f); + instrument->volume_envelope.flags = 0; + if ((type & XM_ENVELOPE_ON) && instrument->volume_envelope.n_nodes) + instrument->volume_envelope.flags |= IT_ENVELOPE_ON; + if (type & XM_ENVELOPE_LOOP) instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON; +#if 1 + if (type & XM_ENVELOPE_SUSTAIN) instrument->volume_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP; +#else // This is now handled in itrender.c + /* let's avoid fading out when reaching the last envelope node */ + if (!(type & XM_ENVELOPE_LOOP)) { + instrument->volume_envelope.loop_start = instrument->volume_envelope.n_nodes-1; + instrument->volume_envelope.loop_end = instrument->volume_envelope.n_nodes-1; + } + instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON; +#endif + + type = dumbfile_getc(f); + instrument->pan_envelope.flags = 0; + if ((type & XM_ENVELOPE_ON) && instrument->pan_envelope.n_nodes) + instrument->pan_envelope.flags |= IT_ENVELOPE_ON; + if (type & XM_ENVELOPE_LOOP) instrument->pan_envelope.flags |= IT_ENVELOPE_LOOP_ON; // should this be here? + if (type & XM_ENVELOPE_SUSTAIN) instrument->pan_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP; + + if (it_xm_make_envelope(&instrument->volume_envelope, vol_points, 0) != 0) { + TRACE("XM error: volume envelope\n"); + if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) return -1; + } + + if (it_xm_make_envelope(&instrument->pan_envelope, pan_points, -32) != 0) { + TRACE("XM error: pan envelope\n"); + if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) return -1; + } + + instrument->pitch_envelope.flags = 0; + + extra->vibrato_type = dumbfile_getc(f); + extra->vibrato_sweep = dumbfile_getc(f); + extra->vibrato_depth = dumbfile_getc(f); + extra->vibrato_speed = dumbfile_getc(f); + + if (dumbfile_error(f) || extra->vibrato_type > 4) // XXX + return -1; + + /** WARNING: lossy approximation */ + instrument->fadeout = (dumbfile_igetw(f)*128 + 64)/0xFFF; + + dumbfile_skip(f, 2); /* reserved */ + + bytes_read += 4 + 96 + 48 + 48 + 14*1 + 2 + 2; + } else + for (i = 0; i < 96; i++) + instrument->map_sample[i] = 0; + + if (size > bytes_read && dumbfile_skip(f, size - bytes_read)) + return -1; + + if (skip_end && limit_xm_skip_end(f, skip_end)) + return -1; + + instrument->new_note_action = NNA_NOTE_CUT; + instrument->dup_check_type = DCT_OFF; + instrument->dup_check_action = DCA_NOTE_CUT; + instrument->pp_separation = 0; + instrument->pp_centre = 60; /* C-5 */ + instrument->global_volume = 128; + instrument->default_pan = 32; + instrument->random_volume = 0; + instrument->random_pan = 0; + instrument->filter_cutoff = 0; + instrument->filter_resonance = 0; + + return 0; +} + + + +/* I (entheh) have two XM files saved by a very naughty program. After a + * 16-bit sample, it saved a rogue byte. The length of the sample was indeed + * an odd number, incremented to include the rogue byte. + * + * In this function we are converting sample lengths and loop points so they + * are measured in samples. This means we forget about the extra bytes, and + * they don't get skipped. So we fail trying to read the next instrument. + * + * To get around this, this function returns the number of rogue bytes that + * won't be accounted for by reading sample->length samples. It returns a + * negative number on failure. + */ +static int it_xm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f) +{ + int type; + int relative_note_number; /* relative to C4 */ + int finetune; + int roguebytes; + int roguebytesmask; + int reserved; + + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = sample->loop_start + dumbfile_igetl(f); + sample->global_volume = 64; + sample->default_volume = dumbfile_getc(f); + finetune = (signed char)dumbfile_getc(f); /* -128..127 <=> -1 semitone .. +127/128 of a semitone */ + type = dumbfile_getc(f); + sample->default_pan = dumbfile_getc(f); /* 0-255 */ + relative_note_number = (signed char)dumbfile_getc(f); + + reserved = dumbfile_getc(f); + + dumbfile_getnc((char *)sample->name, 22, f); + sample->name[22] = 0; + trim_whitespace((char *)sample->name, 22); + + sample->filename[0] = 0; + + if (dumbfile_error(f)) + return -1; + + sample->C5_speed = (int32)(16726.0*pow(DUMB_SEMITONE_BASE, relative_note_number) /**pow(DUMB_PITCH_BASE, )*/ ); + sample->finetune = finetune*2; + + sample->flags = IT_SAMPLE_EXISTS; + + if (reserved == 0xAD && + (!(type & (XM_SAMPLE_16BIT | XM_SAMPLE_STEREO)))) + { + /* F U Olivier Lapicque */ + roguebytes = 4; + roguebytesmask = 4 << 2; + } + else + { + roguebytes = (int)sample->length; + roguebytesmask = 3; + } + + if (type & XM_SAMPLE_16BIT) + sample->flags |= IT_SAMPLE_16BIT; + else + roguebytesmask >>= 1; + + if (type & XM_SAMPLE_STEREO) + sample->flags |= IT_SAMPLE_STEREO; + else + roguebytesmask >>= 1; + + roguebytes &= roguebytesmask; + + if ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end) { + if (type & XM_SAMPLE_FORWARD_LOOP) sample->flags |= IT_SAMPLE_LOOP; + if (type & XM_SAMPLE_PINGPONG_LOOP) sample->flags |= IT_SAMPLE_LOOP | IT_SAMPLE_PINGPONG_LOOP; + } + + if (sample->length <= 0) + sample->flags &= ~IT_SAMPLE_EXISTS; + + return roguebytes; +} + +static void it_xm_fixup_sample_points(IT_SAMPLE *sample) +{ + if (sample->flags & IT_SAMPLE_16BIT) { + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } + if (sample->flags & IT_SAMPLE_STEREO) { + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } + + if ((unsigned int)sample->loop_end > (unsigned int)sample->length) + sample->flags &= ~IT_SAMPLE_LOOP; + else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end) + sample->flags &= ~IT_SAMPLE_LOOP; +} + +static int iswapw(int val) +{ + union + { + short sv; + char cv[2]; + } endiancheck; + /* A smart compiler will optimize this check away. */ + endiancheck.sv = 1; + if (endiancheck.cv[0] == 1) + { + return val; + } + endiancheck.sv = val; + return (unsigned char)endiancheck.cv[0] | (endiancheck.cv[1] << 8); +} + +static int it_xm_read_sample_data(IT_SAMPLE *sample, unsigned char roguebytes, DUMBFILE *f) +{ + int old; + int32 i; +// long truncated_size; + int n_channels; + int32 datasizebytes; + + if (!(sample->flags & IT_SAMPLE_EXISTS)) + return dumbfile_skip(f, roguebytes); + +#if 0 + /* let's get rid of the sample data coming after the end of the loop */ + if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length && roguebytes != 4) { + truncated_size = sample->length - sample->loop_end; + sample->length = sample->loop_end; + } else { + truncated_size = 0; + } +#endif + n_channels = sample->flags & IT_SAMPLE_STEREO ? 2 : 1; + datasizebytes = sample->length; + + sample->data = malloc(datasizebytes); + if (!sample->data) + return -1; + + if (roguebytes == 4) + { + if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0) + return -1; + return 0; + } + + dumbfile_getnc(sample->data, datasizebytes, f); + + if (dumbfile_error(f)) + return -1; + + /* FMOD extension: Samples compressed with Ogg Vorbis */ + if (!memcmp((char *)sample->data + 4, "OggS", 4) && + !memcmp((char *)sample->data + 33, "vorbis", 7)) + { + int32 outlen = ((unsigned char *)(sample->data))[0] | + (((unsigned char *)(sample->data))[1] << 8) | + (((unsigned char *)(sample->data))[2] << 16) | + (((unsigned char *)(sample->data))[3] << 24); + short *output; + + if (!(sample->flags & IT_SAMPLE_16BIT)) + { + /* Because it'll be 16-bit when we're done with it. */ + outlen <<= 1; + } + + if (sample->flags & IT_SAMPLE_STEREO) + { + /* OggMod knows nothing of stereo samples and compresses them as mono, + * screwing up the second channel. (Because for whatever reason, + * ModPlug delta encodes them independantly, even though it presents + * the sample as a double-length mono sound to other players.) + */ + sample->flags &= ~IT_SAMPLE_STEREO; + outlen >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } + output = dumb_decode_vorbis(outlen, (char *)sample->data + 4, datasizebytes - 4); + if (output != NULL) + { + free(sample->data); + sample->data = output; + sample->length = outlen; + if (!(sample->flags & IT_SAMPLE_16BIT)) + { + sample->flags |= IT_SAMPLE_16BIT; + sample->loop_start <<= 1; + sample->loop_end <<= 1; + } + + it_xm_fixup_sample_points(sample); + return 0; + } + /* Decode failed, so assume it's normal sample data that just so + * happened to look like a Vorbis stream. (Not likely to happen + * by coincidence!) */ + } + + it_xm_fixup_sample_points(sample); + + /* sample data is stored as signed delta values */ + old = 0; + if (sample->flags & IT_SAMPLE_STEREO) + { + /* Stereo samples are a ModPlug extension, so to keep compatibility with + * players that don't know about it (and FastTracker 2 itself), the two + * channels are not stored interleaved but rather, one after the other. */ + int old_r = 0; + void *ibuffer = malloc(sample->length << ((sample->flags & IT_SAMPLE_16BIT) ? 2 : 1)); + if (ibuffer == NULL) + { + /* No memory => ignore stereo bits at the end */ + sample->flags &= ~IT_SAMPLE_STEREO; + } + else if (sample->flags & IT_SAMPLE_16BIT) + { + for (i = 0; i < sample->length; i++) + { + ((short *)ibuffer)[i*2] = old += iswapw(((short *)sample->data)[i]); + ((short *)ibuffer)[i*2+1] = old_r += iswapw(((short *)sample->data)[i + sample->length]); + } + } + else + { + for (i = 0; i < sample->length; i++) + { + ((char *)ibuffer)[i*2] = old += ((char *)sample->data)[i]; + ((char *)ibuffer)[i*2+1] = old_r += ((char *)sample->data)[i + sample->length]; + } + } + if (ibuffer != NULL) + { + free(sample->data); + sample->data = ibuffer; + } + } + if (!(sample->flags & IT_SAMPLE_STEREO)) + { + if (sample->flags & IT_SAMPLE_16BIT) + { + for (i = 0; i < sample->length; i++) + ((short *)sample->data)[i] = old += iswapw(((short *)sample->data)[i]); + } + else + { + for (i = 0; i < sample->length; i++) + ((char *)sample->data)[i] = old += ((char *)sample->data)[i]; + } + } + return 0; +} + + + +/* "Real programmers don't document. If it was hard to write, + * it should be hard to understand." + * + * (Never trust the documentation provided with a tracker. + * Real files are the only truth...) + */ +static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version) +{ + DUMB_IT_SIGDATA *sigdata; + char id_text[18]; + + int header_size; + int flags; + int n_channels; + int total_samples; + int i, j; + + /* check ID text */ + if (dumbfile_getnc(id_text, 17, f) < 17) + return NULL; + id_text[17] = 0; + if (strcmp(id_text, "Extended Module: ") != 0) { + TRACE("XM error: Not an Extended Module\n"); + return NULL; + } + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) + return NULL; + + /* song name */ + if (dumbfile_getnc((char *)sigdata->name, 20, f) < 20) { + free(sigdata); + return NULL; + } + sigdata->name[20] = 0; + trim_whitespace((char *)sigdata->name, 20); + + if (dumbfile_getc(f) != 0x1A) { + TRACE("XM error: 0x1A not found\n"); + free(sigdata); + return NULL; + } + + /* tracker name */ + if (dumbfile_skip(f, 20)) { + free(sigdata); + return NULL; + } + + /* version number */ + * version = dumbfile_igetw(f); + if (* version > 0x0104 || * version < 0x0102) { + TRACE("XM error: wrong format version\n"); + free(sigdata); + return NULL; + } + + /* + ------------------ + --- Header --- + ------------------ + */ + + /* header size */ + header_size = dumbfile_igetl(f); + if (header_size < (4 + 2*8 + 1) || header_size > 0x114) { + TRACE("XM error: unexpected header size\n"); + free(sigdata); + return NULL; + } + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_samples = 0; + sigdata->n_orders = dumbfile_igetw(f); + sigdata->restart_position = dumbfile_igetw(f); + n_channels = dumbfile_igetw(f); /* max 32 but we'll be lenient */ + sigdata->n_pchannels = n_channels; + sigdata->n_patterns = dumbfile_igetw(f); + sigdata->n_instruments = dumbfile_igetw(f); /* max 128 */ /* XXX upped to 256 */ + flags = dumbfile_igetw(f); + sigdata->speed = dumbfile_igetw(f); + if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo? + sigdata->tempo = dumbfile_igetw(f); + + // FT2 always clips restart position against the song length + if (sigdata->restart_position > sigdata->n_orders) + sigdata->restart_position = sigdata->n_orders; + // And FT2 starts playback on order 0, regardless of length, + // and only checks if the next order is greater than or equal + // to this, not the current pattern. Work around this with + // DUMB's playback core by overriding a zero length with one. + if (sigdata->n_orders == 0) + sigdata->n_orders = 1; + + /* sanity checks */ + // XXX + i = header_size - 4 - 2 * 8; /* Maximum number of orders expected */ + if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_orders > i || sigdata->n_patterns > 256 || sigdata->n_instruments > 256 || n_channels > DUMB_IT_N_CHANNELS) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + //if (sigdata->restart_position >= sigdata->n_orders) + //sigdata->restart_position = 0; + + /* order table */ + sigdata->order = malloc(sigdata->n_orders*sizeof(*sigdata->order)); + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f); + dumbfile_skip(f, i - sigdata->n_orders); + + if (dumbfile_error(f)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if ( * version > 0x103 ) { + /* + -------------------- + --- Patterns --- + -------------------- + */ + + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + { + unsigned char *buffer = malloc(1280 * n_channels); /* 256 rows * 5 bytes */ + if (!buffer) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) { + if (it_xm_read_pattern(&sigdata->pattern[i], f, n_channels, buffer, * version) != 0) { + free(buffer); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + free(buffer); + } + + /* + ----------------------------------- + --- Instruments and Samples --- + ----------------------------------- + */ + + sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument)); + if (!sigdata->instrument) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + /* With XM, samples are not global, they're part of an instrument. In a + * file, each instrument is stored with its samples. Because of this, I + * don't know how to find how many samples are present in the file. Thus + * I have to do n_instruments reallocation on sigdata->sample. + * Looking at FT2, it doesn't seem possible to have more than 16 samples + * per instrument (even though n_samples is stored as 2 bytes). So maybe + * we could allocate a 128*16 array of samples, and shrink it back to the + * correct size when we know it? + * Alternatively, I could allocate samples by blocks of N (still O(n)), + * or double the number of allocated samples when I need more (O(log n)). + */ + total_samples = 0; + sigdata->sample = NULL; + + for (i = 0; i < sigdata->n_instruments; i++) { + XM_INSTRUMENT_EXTRA extra; + + DUMBFILE * lf = dumbfile_limit_xm( f ); + if ( !lf ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (it_xm_read_instrument(&sigdata->instrument[i], &extra, lf) < 0) { + // XXX + if ( ! i ) + { + TRACE("XM error: instrument %d\n", i+1); + dumbfile_close( lf ); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + else + { + dumbfile_close( lf ); + sigdata->n_instruments = i; + break; + } + } + + if (extra.n_samples) { + unsigned char roguebytes[XM_MAX_SAMPLES_PER_INSTRUMENT]; + + /* adjust instrument sample map (make indices absolute) */ + for (j = 0; j < 96; j++) + sigdata->instrument[i].map_sample[j] += total_samples; + + sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples)); + if (!sigdata->sample) { + dumbfile_close( lf ); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (j = total_samples; j < total_samples+extra.n_samples; j++) + sigdata->sample[j].data = NULL; + + if ( limit_xm_resize( lf, 0 ) < 0 ) { + dumbfile_close( lf ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + + /* read instrument's samples */ + for (j = 0; j < extra.n_samples; j++) { + IT_SAMPLE *sample = &sigdata->sample[total_samples+j]; + int b; + if ( limit_xm_resize( lf, extra.sample_header_size ) < 0 ) { + dumbfile_close( lf ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + b = it_xm_read_sample_header(sample, lf); + if (b < 0) { + dumbfile_close( lf ); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + roguebytes[j] = b; + // Any reason why these can't be set inside it_xm_read_sample_header()? + sample->vibrato_speed = extra.vibrato_speed; + sample->vibrato_depth = extra.vibrato_depth; + sample->vibrato_rate = extra.vibrato_sweep; + /* Rate and sweep don't match, but the difference is + * accounted for in itrender.c. + */ + sample->vibrato_waveform = xm_convert_vibrato[extra.vibrato_type]; + sample->max_resampling_quality = -1; + } + for (j = 0; j < extra.n_samples; j++) { + if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) { + dumbfile_close( lf ); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + total_samples += extra.n_samples; + } + + dumbfile_close( lf ); + } + + sigdata->n_samples = total_samples; + } else { + // ahboy! old layout! + // first instruments and sample headers, then patterns, then sample data! + + /* + ----------------------------------- + --- Instruments and Samples --- + ----------------------------------- + */ + + unsigned char * roguebytes = malloc( sigdata->n_instruments * XM_MAX_SAMPLES_PER_INSTRUMENT ); + if (!roguebytes) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument)); + if (!sigdata->instrument) { + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + total_samples = 0; + sigdata->sample = NULL; + + for (i = 0; i < sigdata->n_instruments; i++) { + XM_INSTRUMENT_EXTRA extra; + + DUMBFILE * lf = dumbfile_limit_xm( f ); + if ( !lf ) { + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (it_xm_read_instrument(&sigdata->instrument[i], &extra, lf) < 0) { + TRACE("XM error: instrument %d\n", i+1); + dumbfile_close(lf); + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (extra.n_samples) { + /* adjust instrument sample map (make indices absolute) */ + for (j = 0; j < 96; j++) + sigdata->instrument[i].map_sample[j] += total_samples; + + sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples)); + if (!sigdata->sample) { + dumbfile_close( lf ); + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (j = total_samples; j < total_samples+extra.n_samples; j++) + sigdata->sample[j].data = NULL; + + if ( limit_xm_resize( lf, 0 ) < 0 ) { + dumbfile_close( lf ); + free( roguebytes ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + + /* read instrument's samples */ + for (j = 0; j < extra.n_samples; j++) { + IT_SAMPLE *sample = &sigdata->sample[total_samples+j]; + int b; + if ( limit_xm_resize( lf, extra.sample_header_size ) < 0 ) { + dumbfile_close( lf ); + free( roguebytes ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + b = it_xm_read_sample_header(sample, lf); + if (b < 0) { + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + roguebytes[total_samples+j] = b; + // Any reason why these can't be set inside it_xm_read_sample_header()? + sample->vibrato_speed = extra.vibrato_speed; + sample->vibrato_depth = extra.vibrato_depth; + sample->vibrato_rate = extra.vibrato_sweep; + /* Rate and sweep don't match, but the difference is + * accounted for in itrender.c. + */ + sample->vibrato_waveform = xm_convert_vibrato[extra.vibrato_type]; + sample->max_resampling_quality = -1; + } + total_samples += extra.n_samples; + } + + dumbfile_close( lf ); + } + + sigdata->n_samples = total_samples; + + /* + -------------------- + --- Patterns --- + -------------------- + */ + + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + { + unsigned char *buffer = malloc(1280 * n_channels); /* 256 rows * 5 bytes */ + if (!buffer) { + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) { + if (it_xm_read_pattern(&sigdata->pattern[i], f, n_channels, buffer, * version) != 0) { + free(buffer); + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + free(buffer); + } + + // and now we load the sample data + for (j = 0; j < total_samples; j++) { + if (it_xm_read_sample_data(&sigdata->sample[j], roguebytes[j], f) != 0) { + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + free(roguebytes); + } + + + sigdata->flags = IT_WAS_AN_XM | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO | IT_USE_INSTRUMENTS; + // Are we OK with IT_COMPATIBLE_GXX off? + // + // When specifying note + instr + tone portamento, and an old note is still playing (even after note off): + // - If Compatible Gxx is on, the new note will be triggered only if the instrument _changes_. + // - If Compatible Gxx is off, the new note will always be triggered, provided the instrument is specified. + // - FT2 seems to do the latter (unconfirmed). + + // Err, wait. XM playback has its own code. The change made to the IT + // playbackc code didn't affect XM playback. Forget this then. There's + // still a bug in XM playback though, and it'll need some investigation... + // tomorrow... + + // UPDATE: IT_COMPATIBLE_GXX is required to be on, so that tone porta has + // separate memory from portamento. + + if (flags & XM_LINEAR_FREQUENCY) + sigdata->flags |= IT_LINEAR_SLIDES; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + memset(sigdata->channel_pan, 32, DUMB_IT_N_CHANNELS); + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +#if 0 // no fucking way, dude! + +/* The length returned is the time required to play from the beginning of the + * file to the last row of the last order (which is when the player will + * loop). Depending on the song, the sound might stop sooner. + * Due to fixed point roundoffs, I think this is only reliable to the second. + * Full precision could be achieved by using a double during the computation, + * or maybe a LONG_LONG. + */ +int32 it_compute_length(const DUMB_IT_SIGDATA *sigdata) +{ + IT_PATTERN *pattern; + int tempo, speed; + int loop_start[IT_N_CHANNELS]; + char loop_count[IT_N_CHANNELS]; + int order, entry; + int row_first_entry = 0; + int jump, jump_dest; + int delay, fine_delay; + int i; + int32 t; + + if (!sigdata) + return 0; + + tempo = sigdata->tempo; + speed = sigdata->speed; + order = entry = 0; + jump = jump_dest = 0; + t = 0; + + /* for each PATTERN */ + for (order = 0; order < sigdata->n_orders; order++) { + + if (sigdata->order[order] == IT_ORDER_END) break; + if (sigdata->order[order] == IT_ORDER_SKIP) continue; + + for (i = 0; i < IT_N_CHANNELS; i++) + loop_count[i] = -1; + + pattern = &sigdata->pattern[ sigdata->order[order] ]; + entry = 0; + if (jump == IT_BREAK_TO_ROW) { + int row = 0; + while (row < jump_dest) + if (pattern->entry[entry++].channel >= IT_N_CHANNELS) + row++; + } + + /* for each ROW */ + while (entry < pattern->n_entries) { + row_first_entry = entry; + delay = fine_delay = 0; + jump = 0; + + /* for each note NOTE */ + while (entry < pattern->n_entries && pattern->entry[entry].channel < IT_N_CHANNELS) { + int value = pattern->entry[entry].effectvalue; + int channel = pattern->entry[entry].channel; + + switch (pattern->entry[entry].effect) { + + case IT_SET_SPEED: speed = value; break; + + case IT_JUMP_TO_ORDER: + if (value <= order) /* infinite loop */ + return 0; + jump = IT_JUMP_TO_ORDER; + jump_dest = value; + break; + + case IT_BREAK_TO_ROW: + jump = IT_BREAK_TO_ROW; + jump_dest = value; + break; + + case IT_S: + switch (HIGH(value)) { + case IT_S_PATTERN_DELAY: delay = LOW(value); break; + case IT_S_FINE_PATTERN_DELAY: fine_delay = LOW(value); break; + case IT_S_PATTERN_LOOP: + if (LOW(value) == 0) { + loop_start[channel] = row_first_entry; + } else { + if (loop_count[channel] == -1) + loop_count[channel] = LOW(value); + + if (loop_count[channel]) { + jump = IT_S_PATTERN_LOOP; + jump_dest = loop_start[channel]; + } + loop_count[channel]--; + } + break; + } + break; + + case IT_SET_SONG_TEMPO: + switch (HIGH(value)) { /* slides happen every non-row frames */ + case 0: tempo = tempo - LOW(value)*(speed-1); break; + case 1: tempo = tempo + LOW(value)*(speed-1); break; + default: tempo = value; + } + tempo = MID(32, tempo, 255); + break; + } + + entry++; + } + + /* end of ROW */ + entry++; + t += TICK_TIME_DIVIDEND * (speed*(1+delay) + fine_delay) / tempo; + + if (jump == IT_JUMP_TO_ORDER) { + order = jump_dest - 1; + break; + } else if (jump == IT_BREAK_TO_ROW) + break; + else if (jump == IT_S_PATTERN_LOOP) + entry = jump_dest - 1; + } + + /* end of PATTERN */ + } + + return t; +} + +#endif /* 0 */ + + +static char hexdigit(int in) +{ + if (in < 10) return in + '0'; + else return in + 'A' - 10; +} + +DUH *DUMBEXPORT dumb_read_xm_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + int ver; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_xm_load_sigdata(f, &ver); + + if (!sigdata) + return NULL; + + { + char version[16]; + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + version[0] = 'X'; + version[1] = 'M'; + version[2] = ' '; + version[3] = 'v'; + version[4] = hexdigit( ( ver >> 8 ) & 15 ); + version[5] = '.'; + version[6] = hexdigit( ( ver >> 4 ) & 15 ); + version[7] = hexdigit( ver & 15 ); + version[8] = 0; + tag[1][1] = ( const char * ) & version; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/thirdparty/dumb/src/it/readxm2.c b/thirdparty/dumb/src/it/readxm2.c new file mode 100644 index 000000000..7a721d852 --- /dev/null +++ b/thirdparty/dumb/src/it/readxm2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readxm2.c - Function to read a Fast Tracker II / / \ \ + * module from an open file and do an | < / \_ + * initial run-through. | \/ /\ / + * \_ / > / + * Split off from readxm.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *DUMBEXPORT dumb_read_xm(DUMBFILE *f) +{ + DUH *duh = dumb_read_xm_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/thirdparty/dumb/src/it/xmeffect.c b/thirdparty/dumb/src/it/xmeffect.c new file mode 100644 index 000000000..96cf7da67 --- /dev/null +++ b/thirdparty/dumb/src/it/xmeffect.c @@ -0,0 +1,245 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * xmeffect.c - Code for converting MOD/XM / / \ \ + * effects to IT effects. | < / \_ + * | \/ /\ / + * By Julien Cugniere. Ripped out of readxm.c \_ / > / + * by entheh. | \ / / + * | ' / + * \__/ + */ + + + +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +#if 0 +unsigned char **_dumb_malloc2(int w, int h) +{ + unsigned char **line = malloc(h * sizeof(*line)); + int i; + if (!line) return NULL; + + line[0] = malloc(w * h * sizeof(*line[0])); + if (!line[0]) { + free(line); + return NULL; + } + + for (i = 1; i < h; i++) + line[i] = line[i-1] + w; + + memset(line[0], 0, w*h); + + return line; +} + + + +void _dumb_free2(unsigned char **line) +{ + if (line) { + if (line[0]) + free(line[0]); + free(line); + } +} + + + +/* Effects having a memory. 2 means that the two parts of the effectvalue + * should be handled separately. + */ +static const char xm_has_memory[] = { +/* 0 1 2 3 4 5 6 7 8 9 A B C D (E) F G H K L P R T (X) */ + 0, 1, 1, 1, 2, 1, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, + +/* E0 E1 E2 E3 E4 E5 E6 E7 E9 EA EB EC ED EE X1 X2 */ + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif + + + +/* Effects marked with 'special' are handled specifically in itrender.c */ +void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry, int mod) +{ +const int log = 0; + + if ((!effect && !value) || (effect >= XM_N_EFFECTS)) + return; + +if (log) printf("%c%02X", (effect<10)?('0'+effect):('A'+effect-10), value); + + /* Linearisation of the effect number... */ + if (effect == XM_E) { + effect = EBASE + HIGH(value); + value = LOW(value); + } else if (effect == XM_X) { + effect = XBASE + HIGH(value); + value = LOW(value); + } + +if (log) printf(" - %2d %02X", effect, value); + +#if 0 // This should be handled in itrender.c! + /* update effect memory */ + switch (xm_has_memory[effect]) { + case 1: + if (!value) + value = memory[entry->channel][effect]; + else + memory[entry->channel][effect] = value; + break; + + case 2: + if (!HIGH(value)) + SET_HIGH(value, HIGH(memory[entry->channel][effect])); + else + SET_HIGH(memory[entry->channel][effect], HIGH(value)); + + if (!LOW(value)) + SET_LOW(value, LOW(memory[entry->channel][effect])); + else + SET_LOW(memory[entry->channel][effect], LOW(value)); + break; + } +#endif + + /* convert effect */ + entry->mask |= IT_ENTRY_EFFECT; + switch (effect) { + + case XM_APPREGIO: effect = IT_ARPEGGIO; break; + case XM_VIBRATO: effect = IT_VIBRATO; break; + case XM_TONE_PORTAMENTO: effect = IT_TONE_PORTAMENTO; break; + case XM_TREMOLO: effect = IT_TREMOLO; break; + case XM_SET_PANNING: effect = IT_SET_PANNING; break; + case XM_SAMPLE_OFFSET: effect = IT_SET_SAMPLE_OFFSET; break; + case XM_POSITION_JUMP: effect = IT_JUMP_TO_ORDER; break; + case XM_MULTI_RETRIG: effect = IT_RETRIGGER_NOTE; break; + case XM_TREMOR: effect = IT_TREMOR; break; + case XM_PORTAMENTO_UP: effect = IT_XM_PORTAMENTO_UP; break; + case XM_PORTAMENTO_DOWN: effect = IT_XM_PORTAMENTO_DOWN; break; + case XM_SET_CHANNEL_VOLUME: effect = IT_SET_CHANNEL_VOLUME; break; /* special */ + case XM_VOLSLIDE_TONEPORTA: effect = IT_VOLSLIDE_TONEPORTA; break; /* special */ + case XM_VOLSLIDE_VIBRATO: effect = IT_VOLSLIDE_VIBRATO; break; /* special */ + + case XM_PATTERN_BREAK: + effect = IT_BREAK_TO_ROW; + value = BCD_TO_NORMAL(value); + if (value > 63) value = 0; /* FT2, maybe ProTracker? */ + break; + + case XM_VOLUME_SLIDE: /* special */ + effect = IT_VOLUME_SLIDE; + value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value)); + break; + + case XM_PANNING_SLIDE: + effect = IT_PANNING_SLIDE; + //value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value)); + value = HIGH(value) ? EFFECT_VALUE(0, HIGH(value)) : EFFECT_VALUE(LOW(value), 0); + break; + + case XM_GLOBAL_VOLUME_SLIDE: /* special */ + effect = IT_GLOBAL_VOLUME_SLIDE; + value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value)); + break; + + case XM_SET_TEMPO_BPM: + if (mod) effect = (value <= 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO); + else effect = (value < 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO); + break; + + case XM_SET_GLOBAL_VOLUME: + effect = IT_SET_GLOBAL_VOLUME; + value *= 2; + if (value > 128) value = 128; + break; + + case XM_KEY_OFF: + effect = IT_XM_KEY_OFF; + break; + + case XM_SET_ENVELOPE_POSITION: + effect = IT_XM_SET_ENVELOPE_POSITION; + break; + + case EBASE+XM_E_SET_FILTER: effect = SBASE+IT_S_SET_FILTER; break; + case EBASE+XM_E_SET_GLISSANDO_CONTROL: effect = SBASE+IT_S_SET_GLISSANDO_CONTROL; break; /** TODO */ + case EBASE+XM_E_SET_FINETUNE: effect = SBASE+IT_S_FINETUNE; break; + case EBASE+XM_E_SET_LOOP: effect = SBASE+IT_S_PATTERN_LOOP; break; + case EBASE+XM_E_NOTE_CUT: effect = SBASE+IT_S_DELAYED_NOTE_CUT; break; + case EBASE+XM_E_NOTE_DELAY: effect = SBASE+IT_S_NOTE_DELAY; break; + case EBASE+XM_E_PATTERN_DELAY: effect = SBASE+IT_S_PATTERN_DELAY; break; + case EBASE+XM_E_SET_PANNING: effect = SBASE+IT_S_SET_PAN; break; + case EBASE+XM_E_FINE_VOLSLIDE_UP: effect = IT_XM_FINE_VOLSLIDE_UP; break; + case EBASE+XM_E_FINE_VOLSLIDE_DOWN: effect = IT_XM_FINE_VOLSLIDE_DOWN; break; + case EBASE+XM_E_SET_MIDI_MACRO: effect = SBASE+IT_S_SET_MIDI_MACRO; break; + + case EBASE + XM_E_FINE_PORTA_UP: + effect = IT_PORTAMENTO_UP; + value = EFFECT_VALUE(0xF, value); + break; + + case EBASE + XM_E_FINE_PORTA_DOWN: + effect = IT_PORTAMENTO_DOWN; + value = EFFECT_VALUE(0xF, value); + break; + + case EBASE + XM_E_RETRIG_NOTE: + effect = IT_XM_RETRIGGER_NOTE; + value = EFFECT_VALUE(0, value); + break; + + case EBASE + XM_E_SET_VIBRATO_CONTROL: + effect = SBASE+IT_S_SET_VIBRATO_WAVEFORM; + value &= ~4; + break; + + case EBASE + XM_E_SET_TREMOLO_CONTROL: + effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM; + value &= ~4; + break; + + case XBASE + XM_X_EXTRAFINE_PORTA_UP: + effect = IT_PORTAMENTO_UP; + value = EFFECT_VALUE(0xE, value); + break; + + case XBASE + XM_X_EXTRAFINE_PORTA_DOWN: + effect = IT_PORTAMENTO_DOWN; + value = EFFECT_VALUE(0xE, value); + break; + + default: + /* user effect (often used in demos for synchronisation) */ + entry->mask &= ~IT_ENTRY_EFFECT; + } + +if (log) printf(" - %2d %02X", effect, value); + + /* Inverse linearisation... */ + if (effect >= SBASE && effect < SBASE+16) { + value = EFFECT_VALUE(effect-SBASE, value); + effect = IT_S; + } + +if (log) printf(" - %c%02X\n", 'A'+effect-1, value); + + entry->effect = effect; + entry->effectvalue = value; +} diff --git a/thirdparty/dumb/vc6/dumb/.gitignore b/thirdparty/dumb/vc6/dumb/.gitignore new file mode 100644 index 000000000..a5aab370c --- /dev/null +++ b/thirdparty/dumb/vc6/dumb/.gitignore @@ -0,0 +1,3 @@ +*.user +Debug +Release \ No newline at end of file diff --git a/thirdparty/dumb/vc6/dumb/dumb.vcxproj b/thirdparty/dumb/vc6/dumb/dumb.vcxproj new file mode 100644 index 000000000..bc10c9a67 --- /dev/null +++ b/thirdparty/dumb/vc6/dumb/dumb.vcxproj @@ -0,0 +1,216 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {612D360C-A51B-4B34-8F49-33F42A2957F5} + dumb + + + + + + + + + + + + StaticLibrary + true + v120_xp + + + StaticLibrary + v120_xp + + + + + + + + + + + + + <_ProjectFileVersion>10.0.21006.1 + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ../../include;%(AdditionalIncludeDirectories) + _USE_SSE;RESAMPLER_DECORATE=dumb;BARRAY_DECORATE=dumb;_DEBUG;WIN32;_LIB;DUMB_DECLARE_DEPRECATED;DEBUGMODE=1;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level3 + true + EditAndContinue + Default + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + + + + + MaxSpeed + AnySuitable + ../../include;%(AdditionalIncludeDirectories) + _USE_SSE;RESAMPLER_DECORATE=dumb;BARRAY_DECORATE=dumb;NDEBUG;WIN32;_LIB;DUMB_DECLARE_DEPRECATED;%(PreprocessorDefinitions) + true + MultiThreaded + true + Level3 + true + ProgramDatabase + Default + Fast + NoExtensions + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + true + true + + + Document + true + true + + + Document + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/thirdparty/dumb/vc6/dumb/dumb.vcxproj.filters b/thirdparty/dumb/vc6/dumb/dumb.vcxproj.filters new file mode 100644 index 000000000..bd096043f --- /dev/null +++ b/thirdparty/dumb/vc6/dumb/dumb.vcxproj.filters @@ -0,0 +1,326 @@ + + + + + {419c5e1f-2bf4-473a-b2e5-2e531285aa62} + + + {44b333b3-1607-4820-82bc-e4c21a40e31a} + + + {0b122556-3781-4ef3-87fe-ffa5fb50b493} + + + {e961cd19-26f6-4df0-b895-e099d3e81db9} + + + {82e35139-08ff-4e99-a3ce-2254d7427ec4} + + + {5f7fc0f6-4008-4166-83ad-e5d914718bd0} + + + {0fd0715e-5824-4419-aa5b-2d4272d222ce} + + + {b9e26fe7-6056-4580-b2c6-10e6116d4129} + + + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\it\loaders + + + src\it\loaders + + + src\it + + + src\it + + + src\it\readers + + + src\it\readers + + + src\it + + + src\it + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it + + + src\it\readers + + + src\it\readers + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\readers + + + src\it\readers + + + src\helpers + + + + + include + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + + + src\helpers + + + src\helpers + + + src\helpers + + + \ No newline at end of file diff --git a/thirdparty/fluidsynth/include/fluidsynth.h b/thirdparty/fluidsynth/include/fluidsynth.h new file mode 100644 index 000000000..ea5dd9d38 --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth.h @@ -0,0 +1,119 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_H +#define _FLUIDSYNTH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if (BUILD_SHARED_LIBS == 0) + #define FLUIDSYNTH_API // building static lib? no visibility control then +#elif defined(WIN32) + #if defined(FLUIDSYNTH_NOT_A_DLL) + #define FLUIDSYNTH_API + #elif defined(FLUIDSYNTH_DLL_EXPORTS) + #define FLUIDSYNTH_API __declspec(dllexport) + #else + #define FLUIDSYNTH_API __declspec(dllimport) + #endif + +#elif defined(MACOS9) +#define FLUIDSYNTH_API __declspec(export) + +#elif defined(__OS2__) +#define FLUIDSYNTH_API __declspec(dllexport) + +#elif defined(__GNUC__) +#define FLUIDSYNTH_API __attribute__ ((visibility ("default"))) + +#else +#define FLUIDSYNTH_API + +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define FLUID_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) && _MSC_VER > 1200 +# define FLUID_DEPRECATED __declspec(deprecated) +#else +# define FLUID_DEPRECATED +#endif + + +/** + * @file fluidsynth.h + * @brief FluidSynth is a real-time synthesizer designed for SoundFont(R) files. + * + * This is the header of the fluidsynth library and contains the + * synthesizer's public API. + * + * Depending on how you want to use or extend the synthesizer you + * will need different API functions. You probably do not need all + * of them. Here is what you might want to do: + * + * - Embedded synthesizer: create a new synthesizer and send MIDI + * events to it. The sound goes directly to the audio output of + * your system. + * + * - Plugin synthesizer: create a synthesizer and send MIDI events + * but pull the audio back into your application. + * + * - SoundFont plugin: create a new type of "SoundFont" and allow + * the synthesizer to load your type of SoundFonts. + * + * - MIDI input: Create a MIDI handler to read the MIDI input on your + * machine and send the MIDI events directly to the synthesizer. + * + * - MIDI files: Open MIDI files and send the MIDI events to the + * synthesizer. + * + * - Command lines: You can send textual commands to the synthesizer. + * + * SoundFont(R) is a registered trademark of E-mu Systems, Inc. + */ + +#include "fluidsynth/types.h" +#include "fluidsynth/settings.h" +#include "fluidsynth/synth.h" +#include "fluidsynth/shell.h" +#include "fluidsynth/sfont.h" +#include "fluidsynth/audio.h" +#include "fluidsynth/event.h" +#include "fluidsynth/midi.h" +#include "fluidsynth/seq.h" +#include "fluidsynth/seqbind.h" +#include "fluidsynth/log.h" +#include "fluidsynth/misc.h" +#include "fluidsynth/mod.h" +#include "fluidsynth/gen.h" +#include "fluidsynth/voice.h" +#include "fluidsynth/version.h" +#include "fluidsynth/ladspa.h" + + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/audio.h b/thirdparty/fluidsynth/include/fluidsynth/audio.h new file mode 100644 index 000000000..bd7612a4b --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/audio.h @@ -0,0 +1,155 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_AUDIO_H +#define _FLUIDSYNTH_AUDIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup audio_output Audio Output + * + * Functions for managing audio drivers and file renderers. + * + * The file renderer is used for fast rendering of MIDI files to + * audio files. The audio drivers are a high-level interface to + * connect the synthesizer with external audio sinks or to render + * real-time audio to files. + */ + +/** + * @defgroup audio_driver Audio Driver + * @ingroup audio_output + * + * Functions for managing audio drivers. + * + * Defines functions for creating audio driver output. Use + * new_fluid_audio_driver() to create a new audio driver for a given synth + * and configuration settings. + * + * The function new_fluid_audio_driver2() can be + * used if custom audio processing is desired before the audio is sent to the + * audio driver (although it is not as efficient). + * + * @sa @ref CreatingAudioDriver + * + * @{ + */ + +/** + * Callback function type used with new_fluid_audio_driver2() to allow for + * custom user audio processing before the audio is sent to the driver. + * + * @param data The user data parameter as passed to new_fluid_audio_driver2(). + * @param len Count of audio frames to synthesize. + * @param nfx Count of arrays in \c fx. + * @param fx Array of buffers to store effects audio to. Buffers may alias with buffers of \c out. + * @param nout Count of arrays in \c out. + * @param out Array of buffers to store (dry) audio to. Buffers may alias with buffers of \c fx. + * @return Should return #FLUID_OK on success, #FLUID_FAILED if an error occurred. + * + * This function is responsible for rendering audio to the buffers. + * The buffers passed to this function are allocated and owned by the respective + * audio driver and are only valid during that specific call (do not cache them). + * The buffers have already been zeroed-out. + * For further details please refer to fluid_synth_process(). + * + * @parblock + * @note Whereas fluid_synth_process() allows aliasing buffers, there is the guarantee that @p out + * and @p fx buffers provided by fluidsynth's audio drivers never alias. This prevents downstream + * applications from e.g. applying a custom effect accidentally to the same buffer multiple times. + * @endparblock + * + * @parblock + * @note Also note that the Jack driver is currently the only driver that has dedicated @p fx buffers + * (but only if \setting{audio_jack_multi} is true). All other drivers do not provide @p fx buffers. + * In this case, users are encouraged to mix the effects into the provided dry buffers when calling + * fluid_synth_process(). + * @code{.cpp} +int myCallback(void *, int len, int nfx, float *fx[], int nout, float *out[]) +{ + int ret; + if(nfx == 0) + { + float *fxb[4] = {out[0], out[1], out[0], out[1]}; + ret = fluid_synth_process(synth, len, sizeof(fxb) / sizeof(fxb[0]), fxb, nout, out); + } + else + { + ret = fluid_synth_process(synth, len, nfx, fx, nout, out); + } + // ... client-code ... + return ret; +} + * @endcode + * For other possible use-cases refer to \ref fluidsynth_process.c . + * @endparblock + */ +typedef int (*fluid_audio_func_t)(void *data, int len, + int nfx, float *fx[], + int nout, float *out[]); + +/** @startlifecycle{Audio Driver} */ +FLUIDSYNTH_API fluid_audio_driver_t *new_fluid_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); + +FLUIDSYNTH_API fluid_audio_driver_t *new_fluid_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, + void *data); + +FLUIDSYNTH_API void delete_fluid_audio_driver(fluid_audio_driver_t *driver); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_audio_driver_register(const char **adrivers); +/* @} */ + +/** + * @defgroup file_renderer File Renderer + * @ingroup audio_output + * + * Functions for managing file renderers and triggering the rendering. + * + * The file renderer is only used to render a MIDI file to audio as fast + * as possible. Please see \ref FileRenderer for a full example. + * + * If you are looking for a way to write audio generated + * from real-time events (for example from an external sequencer or a MIDI controller) to a file, + * please have a look at the \c file \ref audio_driver instead. + * + * + * @{ + */ + +/** @startlifecycle{File Renderer} */ +FLUIDSYNTH_API fluid_file_renderer_t *new_fluid_file_renderer(fluid_synth_t *synth); +FLUIDSYNTH_API void delete_fluid_file_renderer(fluid_file_renderer_t *dev); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_file_renderer_process_block(fluid_file_renderer_t *dev); +FLUIDSYNTH_API int fluid_file_set_encoding_quality(fluid_file_renderer_t *dev, double q); +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_AUDIO_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/event.h b/thirdparty/fluidsynth/include/fluidsynth/event.h new file mode 100644 index 000000000..643f6c524 --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/event.h @@ -0,0 +1,143 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_EVENT_H +#define _FLUIDSYNTH_EVENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup sequencer_events Sequencer Events + * @ingroup sequencer + * + * Create, modify, query and destroy sequencer events. + * + * @{ + */ + +/** + * Sequencer event type enumeration. + */ +enum fluid_seq_event_type +{ + FLUID_SEQ_NOTE = 0, /**< Note event with duration */ + FLUID_SEQ_NOTEON, /**< Note on event */ + FLUID_SEQ_NOTEOFF, /**< Note off event */ + FLUID_SEQ_ALLSOUNDSOFF, /**< All sounds off event */ + FLUID_SEQ_ALLNOTESOFF, /**< All notes off event */ + FLUID_SEQ_BANKSELECT, /**< Bank select message */ + FLUID_SEQ_PROGRAMCHANGE, /**< Program change message */ + FLUID_SEQ_PROGRAMSELECT, /**< Program select message */ + FLUID_SEQ_PITCHBEND, /**< Pitch bend message */ + FLUID_SEQ_PITCHWHEELSENS, /**< Pitch wheel sensitivity set message @since 1.1.0 was misspelled previously */ + FLUID_SEQ_MODULATION, /**< Modulation controller event */ + FLUID_SEQ_SUSTAIN, /**< Sustain controller event */ + FLUID_SEQ_CONTROLCHANGE, /**< MIDI control change event */ + FLUID_SEQ_PAN, /**< Stereo pan set event */ + FLUID_SEQ_VOLUME, /**< Volume set event */ + FLUID_SEQ_REVERBSEND, /**< Reverb send set event */ + FLUID_SEQ_CHORUSSEND, /**< Chorus send set event */ + FLUID_SEQ_TIMER, /**< Timer event (useful for giving a callback at a certain time) */ + FLUID_SEQ_CHANNELPRESSURE, /**< Channel aftertouch event @since 1.1.0 */ + FLUID_SEQ_KEYPRESSURE, /**< Polyphonic aftertouch event @since 2.0.0 */ + FLUID_SEQ_SYSTEMRESET, /**< System reset event @since 1.1.0 */ + FLUID_SEQ_UNREGISTERING, /**< Called when a sequencer client is being unregistered. @since 1.1.0 */ + FLUID_SEQ_SCALE, /**< Sets a new time scale for the sequencer @since 2.2.0 */ + FLUID_SEQ_LASTEVENT /**< @internal Defines the count of events enums @warning This symbol + is not part of the public API and ABI stability guarantee and + may change at any time! */ +}; + +/* Event alloc/free */ +/** @startlifecycle{Sequencer Event} */ +FLUIDSYNTH_API fluid_event_t *new_fluid_event(void); +FLUIDSYNTH_API void delete_fluid_event(fluid_event_t *evt); +/** @endlifecycle */ + +/* Initializing events */ +FLUIDSYNTH_API void fluid_event_set_source(fluid_event_t *evt, fluid_seq_id_t src); +FLUIDSYNTH_API void fluid_event_set_dest(fluid_event_t *evt, fluid_seq_id_t dest); + +/* Timer events */ +FLUIDSYNTH_API void fluid_event_timer(fluid_event_t *evt, void *data); + +/* Note events */ +FLUIDSYNTH_API void fluid_event_note(fluid_event_t *evt, int channel, + short key, short vel, + unsigned int duration); + +FLUIDSYNTH_API void fluid_event_noteon(fluid_event_t *evt, int channel, short key, short vel); +FLUIDSYNTH_API void fluid_event_noteoff(fluid_event_t *evt, int channel, short key); +FLUIDSYNTH_API void fluid_event_all_sounds_off(fluid_event_t *evt, int channel); +FLUIDSYNTH_API void fluid_event_all_notes_off(fluid_event_t *evt, int channel); + +/* Instrument selection */ +FLUIDSYNTH_API void fluid_event_bank_select(fluid_event_t *evt, int channel, short bank_num); +FLUIDSYNTH_API void fluid_event_program_change(fluid_event_t *evt, int channel, int preset_num); +FLUIDSYNTH_API void fluid_event_program_select(fluid_event_t *evt, int channel, unsigned int sfont_id, short bank_num, short preset_num); + +/* Real-time generic instrument controllers */ +FLUIDSYNTH_API +void fluid_event_control_change(fluid_event_t *evt, int channel, short control, int val); + +/* Real-time instrument controllers shortcuts */ +FLUIDSYNTH_API void fluid_event_pitch_bend(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_modulation(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_sustain(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_pan(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_volume(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_reverb_send(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_chorus_send(fluid_event_t *evt, int channel, int val); + +FLUIDSYNTH_API void fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, int val); +FLUIDSYNTH_API void fluid_event_channel_pressure(fluid_event_t *evt, int channel, int val); +FLUIDSYNTH_API void fluid_event_system_reset(fluid_event_t *evt); + +/* Only when unregistering clients */ +FLUIDSYNTH_API void fluid_event_unregistering(fluid_event_t *evt); + +FLUIDSYNTH_API void fluid_event_scale(fluid_event_t *evt, double new_scale); +FLUIDSYNTH_API int fluid_event_from_midi_event(fluid_event_t *, const fluid_midi_event_t *); + +/* Accessing event data */ +FLUIDSYNTH_API int fluid_event_get_type(fluid_event_t *evt); +FLUIDSYNTH_API fluid_seq_id_t fluid_event_get_source(fluid_event_t *evt); +FLUIDSYNTH_API fluid_seq_id_t fluid_event_get_dest(fluid_event_t *evt); +FLUIDSYNTH_API int fluid_event_get_channel(fluid_event_t *evt); +FLUIDSYNTH_API short fluid_event_get_key(fluid_event_t *evt); +FLUIDSYNTH_API short fluid_event_get_velocity(fluid_event_t *evt); +FLUIDSYNTH_API short fluid_event_get_control(fluid_event_t *evt); +FLUIDSYNTH_API int fluid_event_get_value(fluid_event_t *evt); +FLUIDSYNTH_API int fluid_event_get_program(fluid_event_t *evt); +FLUIDSYNTH_API void *fluid_event_get_data(fluid_event_t *evt); +FLUIDSYNTH_API unsigned int fluid_event_get_duration(fluid_event_t *evt); +FLUIDSYNTH_API short fluid_event_get_bank(fluid_event_t *evt); +FLUIDSYNTH_API int fluid_event_get_pitch(fluid_event_t *evt); +FLUIDSYNTH_API double fluid_event_get_scale(fluid_event_t *evt); +FLUIDSYNTH_API unsigned int fluid_event_get_sfont_id(fluid_event_t *evt); +/* @} */ + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_EVENT_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/gen.h b/thirdparty/fluidsynth/include/fluidsynth/gen.h new file mode 100644 index 000000000..13b07c057 --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/gen.h @@ -0,0 +1,134 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_GEN_H +#define _FLUIDSYNTH_GEN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup generators SoundFont Generators + * @ingroup soundfonts + * + * Functions and defines for SoundFont generator effects. + * + * @{ + */ + +/** + * Generator (effect) numbers (Soundfont 2.01 specifications section 8.1.3) + */ +enum fluid_gen_type +{ + GEN_STARTADDROFS, /**< Sample start address offset (0-32767) */ + GEN_ENDADDROFS, /**< Sample end address offset (-32767-0) */ + GEN_STARTLOOPADDROFS, /**< Sample loop start address offset (-32767-32767) */ + GEN_ENDLOOPADDROFS, /**< Sample loop end address offset (-32767-32767) */ + GEN_STARTADDRCOARSEOFS, /**< Sample start address coarse offset (X 32768) */ + GEN_MODLFOTOPITCH, /**< Modulation LFO to pitch */ + GEN_VIBLFOTOPITCH, /**< Vibrato LFO to pitch */ + GEN_MODENVTOPITCH, /**< Modulation envelope to pitch */ + GEN_FILTERFC, /**< Filter cutoff */ + GEN_FILTERQ, /**< Filter Q */ + GEN_MODLFOTOFILTERFC, /**< Modulation LFO to filter cutoff */ + GEN_MODENVTOFILTERFC, /**< Modulation envelope to filter cutoff */ + GEN_ENDADDRCOARSEOFS, /**< Sample end address coarse offset (X 32768) */ + GEN_MODLFOTOVOL, /**< Modulation LFO to volume */ + GEN_UNUSED1, /**< Unused */ + GEN_CHORUSSEND, /**< Chorus send amount */ + GEN_REVERBSEND, /**< Reverb send amount */ + GEN_PAN, /**< Stereo panning */ + GEN_UNUSED2, /**< Unused */ + GEN_UNUSED3, /**< Unused */ + GEN_UNUSED4, /**< Unused */ + GEN_MODLFODELAY, /**< Modulation LFO delay */ + GEN_MODLFOFREQ, /**< Modulation LFO frequency */ + GEN_VIBLFODELAY, /**< Vibrato LFO delay */ + GEN_VIBLFOFREQ, /**< Vibrato LFO frequency */ + GEN_MODENVDELAY, /**< Modulation envelope delay */ + GEN_MODENVATTACK, /**< Modulation envelope attack */ + GEN_MODENVHOLD, /**< Modulation envelope hold */ + GEN_MODENVDECAY, /**< Modulation envelope decay */ + GEN_MODENVSUSTAIN, /**< Modulation envelope sustain */ + GEN_MODENVRELEASE, /**< Modulation envelope release */ + GEN_KEYTOMODENVHOLD, /**< Key to modulation envelope hold */ + GEN_KEYTOMODENVDECAY, /**< Key to modulation envelope decay */ + GEN_VOLENVDELAY, /**< Volume envelope delay */ + GEN_VOLENVATTACK, /**< Volume envelope attack */ + GEN_VOLENVHOLD, /**< Volume envelope hold */ + GEN_VOLENVDECAY, /**< Volume envelope decay */ + GEN_VOLENVSUSTAIN, /**< Volume envelope sustain */ + GEN_VOLENVRELEASE, /**< Volume envelope release */ + GEN_KEYTOVOLENVHOLD, /**< Key to volume envelope hold */ + GEN_KEYTOVOLENVDECAY, /**< Key to volume envelope decay */ + GEN_INSTRUMENT, /**< Instrument ID (shouldn't be set by user) */ + GEN_RESERVED1, /**< Reserved */ + GEN_KEYRANGE, /**< MIDI note range */ + GEN_VELRANGE, /**< MIDI velocity range */ + GEN_STARTLOOPADDRCOARSEOFS, /**< Sample start loop address coarse offset (X 32768) */ + GEN_KEYNUM, /**< Fixed MIDI note number */ + GEN_VELOCITY, /**< Fixed MIDI velocity value */ + GEN_ATTENUATION, /**< Initial volume attenuation */ + GEN_RESERVED2, /**< Reserved */ + GEN_ENDLOOPADDRCOARSEOFS, /**< Sample end loop address coarse offset (X 32768) */ + GEN_COARSETUNE, /**< Coarse tuning */ + GEN_FINETUNE, /**< Fine tuning */ + GEN_SAMPLEID, /**< Sample ID (shouldn't be set by user) */ + GEN_SAMPLEMODE, /**< Sample mode flags */ + GEN_RESERVED3, /**< Reserved */ + GEN_SCALETUNE, /**< Scale tuning */ + GEN_EXCLUSIVECLASS, /**< Exclusive class number */ + GEN_OVERRIDEROOTKEY, /**< Sample root note override */ + + /** + * Initial Pitch + * + * @note This is not "standard" SoundFont generator, because it is not + * mentioned in the list of generators in the SF2 specifications. + * It is used by FluidSynth internally to compute the nominal pitch of + * a note on note-on event. By nature it shouldn't be allowed to be modulated, + * however the specification defines a default modulator having "Initial Pitch" + * as destination (cf. SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch). + * Thus it is impossible to cancel this default modulator, which would be required + * to let the MIDI Pitch Wheel controller modulate a different generator. + * In order to provide this flexibility, FluidSynth >= 2.1.0 uses a default modulator + * "Pitch Wheel to Fine Tune", rather than Initial Pitch. The same "compromise" can + * be found on the Audigy 2 ZS for instance. + */ + GEN_PITCH, + + GEN_CUSTOM_BALANCE, /**< Balance @note Not a real SoundFont generator */ + /* non-standard generator for an additional custom high- or low-pass filter */ + GEN_CUSTOM_FILTERFC, /**< Custom filter cutoff frequency */ + GEN_CUSTOM_FILTERQ, /**< Custom filter Q */ + + GEN_LAST /**< @internal Value defines the count of generators (#fluid_gen_type) + @warning This symbol is not part of the public API and ABI + stability guarantee and may change at any time! */ +}; +/* @} */ + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_GEN_H */ + diff --git a/thirdparty/fluidsynth/include/fluidsynth/ladspa.h b/thirdparty/fluidsynth/include/fluidsynth/ladspa.h new file mode 100644 index 000000000..05b1942d6 --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/ladspa.h @@ -0,0 +1,69 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_LADSPA_H +#define _FLUIDSYNTH_LADSPA_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup ladspa Effect - LADSPA + * @ingroup synth + * + * Functions for configuring the LADSPA effects unit + * + * This header defines useful functions for programmatically manipulating the ladspa + * effects unit of the synth that can be retrieved via fluid_synth_get_ladspa_fx(). + * + * Using any of those functions requires fluidsynth to be compiled with LADSPA support. + * Else all of those functions are useless dummies. + * + * @{ + */ +FLUIDSYNTH_API int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx); +FLUIDSYNTH_API int fluid_ladspa_activate(fluid_ladspa_fx_t *fx); +FLUIDSYNTH_API int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx); +FLUIDSYNTH_API int fluid_ladspa_reset(fluid_ladspa_fx_t *fx); +FLUIDSYNTH_API int fluid_ladspa_check(fluid_ladspa_fx_t *fx, char *err, int err_size); + +FLUIDSYNTH_API int fluid_ladspa_host_port_exists(fluid_ladspa_fx_t *fx, const char *name); + +FLUIDSYNTH_API int fluid_ladspa_add_buffer(fluid_ladspa_fx_t *fx, const char *name); +FLUIDSYNTH_API int fluid_ladspa_buffer_exists(fluid_ladspa_fx_t *fx, const char *name); + +FLUIDSYNTH_API int fluid_ladspa_add_effect(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *lib_name, const char *plugin_name); +FLUIDSYNTH_API int fluid_ladspa_effect_can_mix(fluid_ladspa_fx_t *fx, const char *name); +FLUIDSYNTH_API int fluid_ladspa_effect_set_mix(fluid_ladspa_fx_t *fx, const char *name, int mix, float gain); +FLUIDSYNTH_API int fluid_ladspa_effect_port_exists(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name); +FLUIDSYNTH_API int fluid_ladspa_effect_set_control(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *port_name, float val); +FLUIDSYNTH_API int fluid_ladspa_effect_link(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *port_name, const char *name); +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_LADSPA_H */ + diff --git a/thirdparty/fluidsynth/include/fluidsynth/log.h b/thirdparty/fluidsynth/include/fluidsynth/log.h new file mode 100644 index 000000000..ec553ba4a --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/log.h @@ -0,0 +1,97 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_LOG_H +#define _FLUIDSYNTH_LOG_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup logging Logging + * + * Logging interface + * + * The default logging function of the fluidsynth prints its messages to the + * stderr. The synthesizer uses five level of messages: #FLUID_PANIC, + * #FLUID_ERR, #FLUID_WARN, #FLUID_INFO, and #FLUID_DBG. + * + * A client application can install a new log function to handle the messages + * differently. In the following example, the application sets a callback + * function to display #FLUID_PANIC messages in a dialog, and ignores all other + * messages by setting the log function to NULL: + * + * @code + * fluid_set_log_function(FLUID_PANIC, show_dialog, (void*) root_window); + * fluid_set_log_function(FLUID_ERR, NULL, NULL); + * fluid_set_log_function(FLUID_WARN, NULL, NULL); + * fluid_set_log_function(FLUID_DBG, NULL, NULL); + * @endcode + * + * @note The logging configuration is global and not tied to a specific + * synthesizer instance. That means that all synthesizer instances created in + * the same process share the same logging configuration. + * + * @{ + */ + +/** + * FluidSynth log levels. + */ +enum fluid_log_level +{ + FLUID_PANIC, /**< The synth can't function correctly any more */ + FLUID_ERR, /**< Serious error occurred */ + FLUID_WARN, /**< Warning */ + FLUID_INFO, /**< Verbose informational messages */ + FLUID_DBG, /**< Debugging messages */ + LAST_LOG_LEVEL /**< @internal This symbol is not part of the public API and ABI + stability guarantee and may change at any time! */ +}; + +/** + * Log function handler callback type used by fluid_set_log_function(). + * + * @param level Log level (#fluid_log_level) + * @param message Log message text + * @param data User data pointer supplied to fluid_set_log_function(). + */ +typedef void (*fluid_log_function_t)(int level, const char *message, void *data); + +FLUIDSYNTH_API +fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void *data); + +FLUIDSYNTH_API void fluid_default_log_function(int level, const char *message, void *data); + +FLUIDSYNTH_API int fluid_log(int level, const char *fmt, ...) +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +__attribute__ ((format (printf, 2, 3))) +#endif +; +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_LOG_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/midi.h b/thirdparty/fluidsynth/include/fluidsynth/midi.h new file mode 100644 index 000000000..cc3e435b0 --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/midi.h @@ -0,0 +1,294 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_MIDI_H +#define _FLUIDSYNTH_MIDI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup midi_input MIDI Input + * + * MIDI Input Subsystem + * + * There are multiple ways to send MIDI events to the synthesizer. They can come + * from MIDI files, from external MIDI sequencers or raw MIDI event sources, + * can be modified via MIDI routers and also generated manually. + * + * The interface connecting all sources and sinks of MIDI events in libfluidsynth + * is \ref handle_midi_event_func_t. + * + * @{ + */ + +/** + * Generic callback function for MIDI event handler. + * + * @param data User defined data pointer + * @param event The MIDI event + * @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * This callback is used to pass MIDI events + * - from \ref midi_player, \ref midi_router or \ref midi_driver + * - to \ref midi_router via fluid_midi_router_handle_midi_event() + * - or to \ref synth via fluid_synth_handle_midi_event(). + * + * Additionally, there is a translation layer to pass MIDI events to + * a \ref sequencer via fluid_sequencer_add_midi_event_to_buffer(). + */ +typedef int (*handle_midi_event_func_t)(void *data, fluid_midi_event_t *event); + +/** + * Generic callback function fired once by MIDI tick change. + * + * @param data User defined data pointer + * @param tick The current (zero-based) tick, which triggered the callback + * @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * This callback is fired at a constant rate depending on the current BPM and PPQ. + * e.g. for PPQ = 192 and BPM = 140 the callback is fired 192 * 140 times per minute (448/sec). + * + * It can be used to sync external elements with the beat, + * or stop / loop the song on a given tick. + * Ticks being BPM-dependent, you can manipulate values such as bars or beats, + * without having to care about BPM. + * + * For example, this callback loops the song whenever it reaches the 5th bar : + * + * @code{.cpp} +int handle_tick(void *data, int tick) +{ + fluid_player_t *player = (fluid_player_t *)data; + int ppq = 192; // From MIDI header + int beatsPerBar = 4; // From the song's time signature + int loopBar = 5; + int loopTick = (loopBar - 1) * ppq * beatsPerBar; + + if (tick == loopTick) + { + return fluid_player_seek(player, 0); + } + + return FLUID_OK; +} + * @endcode + */ +typedef int (*handle_midi_tick_func_t)(void *data, int tick); +/* @} */ + +/** + * @defgroup midi_events MIDI Events + * @ingroup midi_input + * + * Functions to create, modify, query and delete MIDI events. + * + * These functions are intended to be used in MIDI routers and other filtering + * and processing functions in the MIDI event path. If you want to simply + * send MIDI messages to the synthesizer, you can use the more convenient + * \ref midi_messages interface. + * + * @{ + */ +/** @startlifecycle{MIDI Event} */ +FLUIDSYNTH_API fluid_midi_event_t *new_fluid_midi_event(void); +FLUIDSYNTH_API void delete_fluid_midi_event(fluid_midi_event_t *event); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_midi_event_set_type(fluid_midi_event_t *evt, int type); +FLUIDSYNTH_API int fluid_midi_event_get_type(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_channel(fluid_midi_event_t *evt, int chan); +FLUIDSYNTH_API int fluid_midi_event_get_channel(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_get_key(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_key(fluid_midi_event_t *evt, int key); +FLUIDSYNTH_API int fluid_midi_event_get_velocity(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_velocity(fluid_midi_event_t *evt, int vel); +FLUIDSYNTH_API int fluid_midi_event_get_control(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_control(fluid_midi_event_t *evt, int ctrl); +FLUIDSYNTH_API int fluid_midi_event_get_value(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_value(fluid_midi_event_t *evt, int val); +FLUIDSYNTH_API int fluid_midi_event_get_program(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_program(fluid_midi_event_t *evt, int val); +FLUIDSYNTH_API int fluid_midi_event_get_pitch(const fluid_midi_event_t *evt); +FLUIDSYNTH_API int fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val); +FLUIDSYNTH_API int fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, + int size, int dynamic); +FLUIDSYNTH_API int fluid_midi_event_set_text(fluid_midi_event_t *evt, + void *data, int size, int dynamic); +FLUIDSYNTH_API int fluid_midi_event_get_text(fluid_midi_event_t *evt, + void **data, int *size); +FLUIDSYNTH_API int fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, + void *data, int size, int dynamic); +FLUIDSYNTH_API int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt, + void **data, int *size); +/* @} */ + +/** + * @defgroup midi_router MIDI Router + * @ingroup midi_input + * + * Rule based transformation and filtering of MIDI events. + * + * @{ + */ + +/** + * MIDI router rule type. + * + * @since 1.1.0 + */ +typedef enum +{ + FLUID_MIDI_ROUTER_RULE_NOTE, /**< MIDI note rule */ + FLUID_MIDI_ROUTER_RULE_CC, /**< MIDI controller rule */ + FLUID_MIDI_ROUTER_RULE_PROG_CHANGE, /**< MIDI program change rule */ + FLUID_MIDI_ROUTER_RULE_PITCH_BEND, /**< MIDI pitch bend rule */ + FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE, /**< MIDI channel pressure rule */ + FLUID_MIDI_ROUTER_RULE_KEY_PRESSURE, /**< MIDI key pressure rule */ + FLUID_MIDI_ROUTER_RULE_COUNT /**< @internal Total count of rule types. This symbol + is not part of the public API and ABI stability + guarantee and may change at any time!*/ +} fluid_midi_router_rule_type; + + +/** @startlifecycle{MIDI Router} */ +FLUIDSYNTH_API fluid_midi_router_t *new_fluid_midi_router(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *event_handler_data); +FLUIDSYNTH_API void delete_fluid_midi_router(fluid_midi_router_t *handler); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_midi_router_set_default_rules(fluid_midi_router_t *router); +FLUIDSYNTH_API int fluid_midi_router_clear_rules(fluid_midi_router_t *router); +FLUIDSYNTH_API int fluid_midi_router_add_rule(fluid_midi_router_t *router, + fluid_midi_router_rule_t *rule, int type); + + +/** @startlifecycle{MIDI Router Rule} */ +FLUIDSYNTH_API fluid_midi_router_rule_t *new_fluid_midi_router_rule(void); +FLUIDSYNTH_API void delete_fluid_midi_router_rule(fluid_midi_router_rule_t *rule); +/** @endlifecycle */ + +FLUIDSYNTH_API void fluid_midi_router_rule_set_chan(fluid_midi_router_rule_t *rule, + int min, int max, float mul, int add); +FLUIDSYNTH_API void fluid_midi_router_rule_set_param1(fluid_midi_router_rule_t *rule, + int min, int max, float mul, int add); +FLUIDSYNTH_API void fluid_midi_router_rule_set_param2(fluid_midi_router_rule_t *rule, + int min, int max, float mul, int add); +FLUIDSYNTH_API int fluid_midi_router_handle_midi_event(void *data, fluid_midi_event_t *event); +FLUIDSYNTH_API int fluid_midi_dump_prerouter(void *data, fluid_midi_event_t *event); +FLUIDSYNTH_API int fluid_midi_dump_postrouter(void *data, fluid_midi_event_t *event); +/* @} */ + +/** + * @defgroup midi_driver MIDI Driver + * @ingroup midi_input + * + * Functions for managing MIDI drivers. + * + * The available MIDI drivers depend on your platform. See \ref settings_midi for all + * available configuration options. + * + * To create a MIDI driver, you need to specify a source for the MIDI events to be + * forwarded to via the \ref fluid_midi_event_t callback. Normally this will be + * either a \ref midi_router via fluid_midi_router_handle_midi_event() or the synthesizer + * via fluid_synth_handle_midi_event(). + * + * But you can also write your own handler function that preprocesses the events and + * forwards them on to the router or synthesizer instead. + * + * @{ + */ + +/** @startlifecycle{MIDI Driver} */ +FLUIDSYNTH_API +fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *event_handler_data); + +FLUIDSYNTH_API void delete_fluid_midi_driver(fluid_midi_driver_t *driver); +/** @endlifecycle */ + +/* @} */ + +/** + * @defgroup midi_player MIDI File Player + * @ingroup midi_input + * + * Parse standard MIDI files and emit MIDI events. + * + * @{ + */ + +/** + * MIDI File Player status enum. + * @since 1.1.0 + */ +enum fluid_player_status +{ + FLUID_PLAYER_READY, /**< Player is ready */ + FLUID_PLAYER_PLAYING, /**< Player is currently playing */ + FLUID_PLAYER_STOPPING, /**< Player is stopping, but hasn't finished yet (currently unused) */ + FLUID_PLAYER_DONE /**< Player is finished playing */ +}; + +/** + * MIDI File Player tempo enum. + * @since 2.2.0 + */ +enum fluid_player_set_tempo_type +{ + FLUID_PLAYER_TEMPO_INTERNAL, /**< Use midi file tempo set in midi file (120 bpm by default). Multiplied by a factor */ + FLUID_PLAYER_TEMPO_EXTERNAL_BPM, /**< Set player tempo in bpm, supersede midi file tempo */ + FLUID_PLAYER_TEMPO_EXTERNAL_MIDI, /**< Set player tempo in us per quarter note, supersede midi file tempo */ + FLUID_PLAYER_TEMPO_NBR /**< @internal Value defines the count of player tempo type (#fluid_player_set_tempo_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +/** @startlifecycle{MIDI File Player} */ +FLUIDSYNTH_API fluid_player_t *new_fluid_player(fluid_synth_t *synth); +FLUIDSYNTH_API void delete_fluid_player(fluid_player_t *player); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_player_add(fluid_player_t *player, const char *midifile); +FLUIDSYNTH_API int fluid_player_add_mem(fluid_player_t *player, const void *buffer, size_t len); +FLUIDSYNTH_API int fluid_player_play(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_stop(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_join(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_set_loop(fluid_player_t *player, int loop); +FLUIDSYNTH_API int fluid_player_set_tempo(fluid_player_t *player, int tempo_type, double tempo); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_player_set_bpm(fluid_player_t *player, int bpm); +FLUIDSYNTH_API int fluid_player_set_playback_callback(fluid_player_t *player, handle_midi_event_func_t handler, void *handler_data); +FLUIDSYNTH_API int fluid_player_set_tick_callback(fluid_player_t *player, handle_midi_tick_func_t handler, void *handler_data); + +FLUIDSYNTH_API int fluid_player_get_status(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_current_tick(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_total_ticks(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_bpm(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_get_midi_tempo(fluid_player_t *player); +FLUIDSYNTH_API int fluid_player_seek(fluid_player_t *player, int ticks); +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_MIDI_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/misc.h b/thirdparty/fluidsynth/include/fluidsynth/misc.h new file mode 100644 index 000000000..8da368d9c --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/misc.h @@ -0,0 +1,77 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_MISC_H +#define _FLUIDSYNTH_MISC_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup misc Miscellaneous + * + * Miscellaneous utility functions and defines + * + * @{ + */ + +/** + * Value that indicates success, used by most libfluidsynth functions. + * + * @note This was not publicly defined prior to libfluidsynth 1.1.0. When + * writing code which should also be compatible with older versions, something + * like the following can be used: + * + * @code + * #include + * + * #ifndef FLUID_OK + * #define FLUID_OK (0) + * #define FLUID_FAILED (-1) + * #endif + * @endcode + * + * @since 1.1.0 + */ +#define FLUID_OK (0) + +/** + * Value that indicates failure, used by most libfluidsynth functions. + * + * @note See #FLUID_OK for more details. + * + * @since 1.1.0 + */ +#define FLUID_FAILED (-1) + + +FLUIDSYNTH_API int fluid_is_soundfont(const char *filename); +FLUIDSYNTH_API int fluid_is_midifile(const char *filename); +FLUIDSYNTH_API void fluid_free(void* ptr); +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_MISC_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/mod.h b/thirdparty/fluidsynth/include/fluidsynth/mod.h new file mode 100644 index 000000000..98a00b0c5 --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/mod.h @@ -0,0 +1,105 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_MOD_H +#define _FLUIDSYNTH_MOD_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup modulators SoundFont Modulators + * @ingroup soundfonts + * + * SoundFont modulator functions and constants. + * + * @{ + */ + +/** + * Flags defining the polarity, mapping function and type of a modulator source. + * Compare with SoundFont 2.04 PDF section 8.2. + * + * Note: Bit values do not correspond to the SoundFont spec! Also note that + * #FLUID_MOD_GC and #FLUID_MOD_CC are in the flags field instead of the source field. + */ +enum fluid_mod_flags +{ + FLUID_MOD_POSITIVE = 0, /**< Mapping function is positive */ + FLUID_MOD_NEGATIVE = 1, /**< Mapping function is negative */ + FLUID_MOD_UNIPOLAR = 0, /**< Mapping function is unipolar */ + FLUID_MOD_BIPOLAR = 2, /**< Mapping function is bipolar */ + FLUID_MOD_LINEAR = 0, /**< Linear mapping function */ + FLUID_MOD_CONCAVE = 4, /**< Concave mapping function */ + FLUID_MOD_CONVEX = 8, /**< Convex mapping function */ + FLUID_MOD_SWITCH = 12, /**< Switch (on/off) mapping function */ + FLUID_MOD_GC = 0, /**< General controller source type (#fluid_mod_src) */ + FLUID_MOD_CC = 16, /**< MIDI CC controller (source will be a MIDI CC number) */ + + FLUID_MOD_SIN = 0x80, /**< Custom non-standard sinus mapping function */ +}; + +/** + * General controller (if #FLUID_MOD_GC in flags). This + * corresponds to SoundFont 2.04 PDF section 8.2.1 + */ +enum fluid_mod_src +{ + FLUID_MOD_NONE = 0, /**< No source controller */ + FLUID_MOD_VELOCITY = 2, /**< MIDI note-on velocity */ + FLUID_MOD_KEY = 3, /**< MIDI note-on note number */ + FLUID_MOD_KEYPRESSURE = 10, /**< MIDI key pressure */ + FLUID_MOD_CHANNELPRESSURE = 13, /**< MIDI channel pressure */ + FLUID_MOD_PITCHWHEEL = 14, /**< Pitch wheel */ + FLUID_MOD_PITCHWHEELSENS = 16 /**< Pitch wheel sensitivity */ +}; + +/** @startlifecycle{Modulator} */ +FLUIDSYNTH_API fluid_mod_t *new_fluid_mod(void); +FLUIDSYNTH_API void delete_fluid_mod(fluid_mod_t *mod); +/** @endlifecycle */ + +FLUIDSYNTH_API size_t fluid_mod_sizeof(void); + +FLUIDSYNTH_API void fluid_mod_set_source1(fluid_mod_t *mod, int src, int flags); +FLUIDSYNTH_API void fluid_mod_set_source2(fluid_mod_t *mod, int src, int flags); +FLUIDSYNTH_API void fluid_mod_set_dest(fluid_mod_t *mod, int dst); +FLUIDSYNTH_API void fluid_mod_set_amount(fluid_mod_t *mod, double amount); + +FLUIDSYNTH_API int fluid_mod_get_source1(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_flags1(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_source2(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_flags2(const fluid_mod_t *mod); +FLUIDSYNTH_API int fluid_mod_get_dest(const fluid_mod_t *mod); +FLUIDSYNTH_API double fluid_mod_get_amount(const fluid_mod_t *mod); + +FLUIDSYNTH_API int fluid_mod_test_identity(const fluid_mod_t *mod1, const fluid_mod_t *mod2); +FLUIDSYNTH_API int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl); +FLUIDSYNTH_API int fluid_mod_has_dest(const fluid_mod_t *mod, int gen); + +FLUIDSYNTH_API void fluid_mod_clone(fluid_mod_t *mod, const fluid_mod_t *src); +/* @} */ + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_MOD_H */ + diff --git a/thirdparty/fluidsynth/include/fluidsynth/seq.h b/thirdparty/fluidsynth/include/fluidsynth/seq.h new file mode 100644 index 000000000..2e0797549 --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/seq.h @@ -0,0 +1,92 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SEQ_H +#define _FLUIDSYNTH_SEQ_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup sequencer MIDI Sequencer + * + * MIDI event sequencer. + * + * The MIDI sequencer can be used to play MIDI events in a more flexible way than + * using the MIDI file player, which expects the events to be stored as + * Standard MIDI Files. Using the sequencer, you can provide the events one by + * one, with an optional timestamp for scheduling. + * + * @{ + */ + +/** + * Event callback prototype for destination clients. + * + * @param time Current sequencer tick value (see fluid_sequencer_get_tick()). + * @param event The event being received + * @param seq The sequencer instance + * @param data User defined data registered with the client + * + * @note @p time may not be of the same tick value as the scheduled event! In fact, depending on + * the sequencer's scale and the synth's sample-rate, @p time may be a few ticks too late. Although this + * itself is inaudible, it is important to consider, + * when you use this callback for enqueuing additional events over and over again with + * fluid_sequencer_send_at(): If you enqueue new events with a relative tick value you might introduce + * a timing error, which causes your sequence to sound e.g. slower than it's supposed to be. If this is + * your use-case, make sure to enqueue events with an absolute tick value. + */ +typedef void (*fluid_event_callback_t)(unsigned int time, fluid_event_t *event, + fluid_sequencer_t *seq, void *data); + + +/** @startlifecycle{MIDI Sequencer} */ +FLUID_DEPRECATED FLUIDSYNTH_API fluid_sequencer_t *new_fluid_sequencer(void); +FLUIDSYNTH_API fluid_sequencer_t *new_fluid_sequencer2(int use_system_timer); +FLUIDSYNTH_API void delete_fluid_sequencer(fluid_sequencer_t *seq); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_sequencer_get_use_system_timer(fluid_sequencer_t *seq); +FLUIDSYNTH_API +fluid_seq_id_t fluid_sequencer_register_client(fluid_sequencer_t *seq, const char *name, + fluid_event_callback_t callback, void *data); +FLUIDSYNTH_API void fluid_sequencer_unregister_client(fluid_sequencer_t *seq, fluid_seq_id_t id); +FLUIDSYNTH_API int fluid_sequencer_count_clients(fluid_sequencer_t *seq); +FLUIDSYNTH_API fluid_seq_id_t fluid_sequencer_get_client_id(fluid_sequencer_t *seq, int index); +FLUIDSYNTH_API char *fluid_sequencer_get_client_name(fluid_sequencer_t *seq, fluid_seq_id_t id); +FLUIDSYNTH_API int fluid_sequencer_client_is_dest(fluid_sequencer_t *seq, fluid_seq_id_t id); +FLUIDSYNTH_API void fluid_sequencer_process(fluid_sequencer_t *seq, unsigned int msec); +FLUIDSYNTH_API void fluid_sequencer_send_now(fluid_sequencer_t *seq, fluid_event_t *evt); +FLUIDSYNTH_API +int fluid_sequencer_send_at(fluid_sequencer_t *seq, fluid_event_t *evt, + unsigned int time, int absolute); +FLUIDSYNTH_API +void fluid_sequencer_remove_events(fluid_sequencer_t *seq, fluid_seq_id_t source, fluid_seq_id_t dest, int type); +FLUIDSYNTH_API unsigned int fluid_sequencer_get_tick(fluid_sequencer_t *seq); +FLUIDSYNTH_API void fluid_sequencer_set_time_scale(fluid_sequencer_t *seq, double scale); +FLUIDSYNTH_API double fluid_sequencer_get_time_scale(fluid_sequencer_t *seq); +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SEQ_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/seqbind.h b/thirdparty/fluidsynth/include/fluidsynth/seqbind.h new file mode 100644 index 000000000..b8fd1a406 --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/seqbind.h @@ -0,0 +1,45 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SEQBIND_H +#define _FLUIDSYNTH_SEQBIND_H + +#include "seq.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup sequencer + * + * @{ + */ +FLUIDSYNTH_API +fluid_seq_id_t fluid_sequencer_register_fluidsynth(fluid_sequencer_t *seq, fluid_synth_t *synth); +FLUIDSYNTH_API +int fluid_sequencer_add_midi_event_to_buffer(void *data, fluid_midi_event_t *event); +/* @} */ + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_SEQBIND_H */ + diff --git a/thirdparty/fluidsynth/include/fluidsynth/settings.h b/thirdparty/fluidsynth/include/fluidsynth/settings.h new file mode 100644 index 000000000..a8b3cb85e --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/settings.h @@ -0,0 +1,194 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SETTINGS_H +#define _FLUIDSYNTH_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup settings Settings + * + * Functions for settings management + * + * To create a synthesizer object you will have to specify its + * settings. These settings are stored in a fluid_settings_t object. + * @code + * void + * my_synthesizer () + * { + * fluid_settings_t *settings; + * fluid_synth_t *synth; + * fluid_audio_driver_t *adriver; + * + * settings = new_fluid_settings (); + * fluid_settings_setstr(settings, "audio.driver", "alsa"); + * // ... change settings ... + * synth = new_fluid_synth (settings); + * adriver = new_fluid_audio_driver (settings, synth); + * // ... + * } + * @endcode + * @sa @ref CreatingSettings + * + * @{ + */ + +/** + * Hint FLUID_HINT_BOUNDED_BELOW indicates that the LowerBound field + * of the FLUID_PortRangeHint should be considered meaningful. The + * value in this field should be considered the (inclusive) lower + * bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also + * specified then the value of LowerBound should be multiplied by the + * sample rate. + */ +#define FLUID_HINT_BOUNDED_BELOW 0x1 + +/** Hint FLUID_HINT_BOUNDED_ABOVE indicates that the UpperBound field + of the FLUID_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) upper + bound of the valid range. If FLUID_HINT_SAMPLE_RATE is also + specified then the value of UpperBound should be multiplied by the + sample rate. */ +#define FLUID_HINT_BOUNDED_ABOVE 0x2 + +/** + * Hint FLUID_HINT_TOGGLED indicates that the data item should be + * considered a Boolean toggle. Data less than or equal to zero should + * be considered `off' or `false,' and data above zero should be + * considered `on' or `true.' FLUID_HINT_TOGGLED may not be used in + * conjunction with any other hint. + */ +#define FLUID_HINT_TOGGLED 0x4 + +#define FLUID_HINT_OPTIONLIST 0x02 /**< Setting is a list of string options */ + + +/** + * Settings type + * + * Each setting has a defined type: numeric (double), integer, string or a + * set of values. The type of each setting can be retrieved using the + * function fluid_settings_get_type() + */ +enum fluid_types_enum +{ + FLUID_NO_TYPE = -1, /**< Undefined type */ + FLUID_NUM_TYPE, /**< Numeric (double) */ + FLUID_INT_TYPE, /**< Integer */ + FLUID_STR_TYPE, /**< String */ + FLUID_SET_TYPE /**< Set of values */ +}; + +/** @startlifecycle{Settings} */ +FLUIDSYNTH_API fluid_settings_t *new_fluid_settings(void); +FLUIDSYNTH_API void delete_fluid_settings(fluid_settings_t *settings); +/** @endlifecycle */ + +FLUIDSYNTH_API +int fluid_settings_get_type(fluid_settings_t *settings, const char *name); + +FLUIDSYNTH_API +int fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *val); + +FLUIDSYNTH_API +int fluid_settings_is_realtime(fluid_settings_t *settings, const char *name); + +FLUIDSYNTH_API +int fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char *str); + +FLUIDSYNTH_API +int fluid_settings_copystr(fluid_settings_t *settings, const char *name, char *str, int len); + +FLUIDSYNTH_API +int fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str); + +FLUIDSYNTH_API +int fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def); + +FLUIDSYNTH_API +int fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *value); + +FLUIDSYNTH_API +int fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val); + +FLUIDSYNTH_API +int fluid_settings_getnum(fluid_settings_t *settings, const char *name, double *val); + +FLUIDSYNTH_API +int fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val); + +FLUIDSYNTH_API +int fluid_settings_getnum_range(fluid_settings_t *settings, const char *name, + double *min, double *max); + +FLUIDSYNTH_API +int fluid_settings_setint(fluid_settings_t *settings, const char *name, int val); + +FLUIDSYNTH_API +int fluid_settings_getint(fluid_settings_t *settings, const char *name, int *val); + +FLUIDSYNTH_API +int fluid_settings_getint_default(fluid_settings_t *settings, const char *name, int *val); + +FLUIDSYNTH_API +int fluid_settings_getint_range(fluid_settings_t *settings, const char *name, + int *min, int *max); + +/** + * Callback function type used with fluid_settings_foreach_option() + * + * @param data User defined data pointer + * @param name Setting name + * @param option A string option for this setting (iterates through the list) + */ +typedef void (*fluid_settings_foreach_option_t)(void *data, const char *name, const char *option); + +FLUIDSYNTH_API +void fluid_settings_foreach_option(fluid_settings_t *settings, + const char *name, void *data, + fluid_settings_foreach_option_t func); +FLUIDSYNTH_API +int fluid_settings_option_count(fluid_settings_t *settings, const char *name); +FLUIDSYNTH_API char *fluid_settings_option_concat(fluid_settings_t *settings, + const char *name, + const char *separator); + +/** + * Callback function type used with fluid_settings_foreach() + * + * @param data User defined data pointer + * @param name Setting name + * @param type Setting type (#fluid_types_enum) + */ +typedef void (*fluid_settings_foreach_t)(void *data, const char *name, int type); + +FLUIDSYNTH_API +void fluid_settings_foreach(fluid_settings_t *settings, void *data, + fluid_settings_foreach_t func); +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SETTINGS_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/sfont.h b/thirdparty/fluidsynth/include/fluidsynth/sfont.h new file mode 100644 index 000000000..8a5e4058c --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/sfont.h @@ -0,0 +1,362 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SFONT_H +#define _FLUIDSYNTH_SFONT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup soundfonts SoundFonts + * + * SoundFont related functions + * + * This part of the API contains functions, defines and types that are mostly + * only used by internal or custom SoundFont loaders or client code that + * modifies loaded presets, SoundFonts or voices directly. + */ + +/** + * @defgroup soundfont_loader SoundFont Loader + * @ingroup soundfonts + * + * Create custom SoundFont loaders + * + * It is possible to add new SoundFont loaders to the + * synthesizer. This API allows for virtual SoundFont files to be loaded + * and synthesized, which may not actually be SoundFont files, as long as they + * can be represented by the SoundFont synthesis model. + * + * To add a new SoundFont loader to the synthesizer, call + * fluid_synth_add_sfloader() and pass a pointer to an + * #fluid_sfloader_t instance created by new_fluid_sfloader(). + * On creation, you must specify a callback function \p load + * that will be called for every file attempting to load it and + * if successful returns a #fluid_sfont_t instance, or NULL if it fails. + * + * The #fluid_sfont_t structure contains a callback to obtain the + * name of the SoundFont. It contains two functions to iterate + * though the contained presets, and one function to obtain a + * preset corresponding to a bank and preset number. This + * function should return a #fluid_preset_t instance. + * + * The #fluid_preset_t instance contains some functions to obtain + * information from the preset (name, bank, number). The most + * important callback is the noteon function. The noteon function + * is called by fluidsynth internally and + * should call fluid_synth_alloc_voice() for every sample that has + * to be played. fluid_synth_alloc_voice() expects a pointer to a + * #fluid_sample_t instance and returns a pointer to the opaque + * #fluid_voice_t structure. To set or increment the values of a + * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are + * finished initializing the voice call fluid_voice_start() to + * start playing the synthesis voice. + * + * @{ + */ + +/** + * Some notification enums for presets and samples. + */ +enum +{ + FLUID_PRESET_SELECTED, /**< Preset selected notify */ + FLUID_PRESET_UNSELECTED, /**< Preset unselected notify */ + FLUID_SAMPLE_DONE, /**< Sample no longer needed notify */ + FLUID_PRESET_PIN, /**< Request to pin preset samples to cache */ + FLUID_PRESET_UNPIN /**< Request to unpin preset samples from cache */ +}; + +/** + * Indicates the type of a sample used by the _fluid_sample_t::sampletype field. + * + * This enum corresponds to the \c SFSampleLink enum in the SoundFont spec. + * One \c flag may be bit-wise OR-ed with one \c value. + */ +enum fluid_sample_type +{ + FLUID_SAMPLETYPE_MONO = 0x1, /**< Value used for mono samples */ + FLUID_SAMPLETYPE_RIGHT = 0x2, /**< Value used for right samples of a stereo pair */ + FLUID_SAMPLETYPE_LEFT = 0x4, /**< Value used for left samples of a stereo pair */ + FLUID_SAMPLETYPE_LINKED = 0x8, /**< Value used for linked sample, which is currently not supported */ + FLUID_SAMPLETYPE_OGG_VORBIS = 0x10, /**< Flag used for Ogg Vorbis compressed samples (non-standard compliant extension) as found in the program "sftools" developed by Werner Schweer from MuseScore @since 1.1.7 */ + FLUID_SAMPLETYPE_ROM = 0x8000 /**< Flag that indicates ROM samples, causing the sample to be ignored */ +}; + + +/** + * Method to load an instrument file (does not actually need to be a real file name, + * could be another type of string identifier that the \a loader understands). + * + * @param loader SoundFont loader + * @param filename File name or other string identifier + * @return The loaded instrument file (SoundFont) or NULL if an error occurred. + */ +typedef fluid_sfont_t *(*fluid_sfloader_load_t)(fluid_sfloader_t *loader, const char *filename); + +/** + * The free method should free the memory allocated for a fluid_sfloader_t instance in + * addition to any private data. + * + * @param loader SoundFont loader + * + * Any custom user provided cleanup function must ultimately call + * delete_fluid_sfloader() to ensure proper cleanup of the #fluid_sfloader_t struct. If no private data + * needs to be freed, setting this to delete_fluid_sfloader() is sufficient. + * + */ +typedef void (*fluid_sfloader_free_t)(fluid_sfloader_t *loader); + + +/** @startlifecycle{SoundFont Loader} */ +FLUIDSYNTH_API fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free); +FLUIDSYNTH_API void delete_fluid_sfloader(fluid_sfloader_t *loader); + +FLUIDSYNTH_API fluid_sfloader_t *new_fluid_defsfloader(fluid_settings_t *settings); +/** @endlifecycle */ + +/** + * Opens the file or memory indicated by \c filename in binary read mode. + * + * @return returns a file handle on success, NULL otherwise + * + * \c filename matches the string provided during the fluid_synth_sfload() call. + */ +typedef void *(* fluid_sfloader_callback_open_t)(const char *filename); + +/** + * Reads \c count bytes to the specified buffer \c buf. + * + * @return returns #FLUID_OK if exactly \c count bytes were successfully read, else returns #FLUID_FAILED and leaves \a buf unmodified. + */ +typedef int (* fluid_sfloader_callback_read_t)(void *buf, fluid_long_long_t count, void *handle); + +/** + * Same purpose and behaviour as fseek. + * + * @param origin either \c SEEK_SET, \c SEEK_CUR or \c SEEK_END + * @return returns #FLUID_OK if the seek was successfully performed while not seeking beyond a buffer or file, #FLUID_FAILED otherwise + */ +typedef int (* fluid_sfloader_callback_seek_t)(void *handle, fluid_long_long_t offset, int origin); + +/** + * Closes the handle returned by #fluid_sfloader_callback_open_t and frees used resources. + * + * @return returns #FLUID_OK on success, #FLUID_FAILED on error + */ +typedef int (* fluid_sfloader_callback_close_t)(void *handle); + +/** @return returns current file offset or #FLUID_FAILED on error */ +typedef fluid_long_long_t (* fluid_sfloader_callback_tell_t)(void *handle); + + +FLUIDSYNTH_API int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader, + fluid_sfloader_callback_open_t open, + fluid_sfloader_callback_read_t read, + fluid_sfloader_callback_seek_t seek, + fluid_sfloader_callback_tell_t tell, + fluid_sfloader_callback_close_t close); + +FLUIDSYNTH_API int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data); +FLUIDSYNTH_API void *fluid_sfloader_get_data(fluid_sfloader_t *loader); + + + +/** + * Method to return the name of a virtual SoundFont. + * + * @param sfont Virtual SoundFont + * @return The name of the virtual SoundFont. + */ +typedef const char *(*fluid_sfont_get_name_t)(fluid_sfont_t *sfont); + +/** + * Get a virtual SoundFont preset by bank and program numbers. + * + * @param sfont Virtual SoundFont + * @param bank MIDI bank number (0-16383) + * @param prenum MIDI preset number (0-127) + * @return Should return an allocated virtual preset or NULL if it could not + * be found. + */ +typedef fluid_preset_t *(*fluid_sfont_get_preset_t)(fluid_sfont_t *sfont, int bank, int prenum); + +/** + * Start virtual SoundFont preset iteration method. + * + * @param sfont Virtual SoundFont + * + * Starts/re-starts virtual preset iteration in a SoundFont. + */ +typedef void (*fluid_sfont_iteration_start_t)(fluid_sfont_t *sfont); + +/** + * Virtual SoundFont preset iteration function. + * + * @param sfont Virtual SoundFont + * @return NULL when no more presets are available, otherwise the a pointer to the current preset + * + * Returns preset information to the caller. The returned buffer is only valid until a subsequent + * call to this function. + */ +typedef fluid_preset_t *(*fluid_sfont_iteration_next_t)(fluid_sfont_t *sfont); + +/** + * Method to free a virtual SoundFont bank. + * + * @param sfont Virtual SoundFont to free. + * @return Should return 0 when it was able to free all resources or non-zero + * if some of the samples could not be freed because they are still in use, + * in which case the free will be tried again later, until success. + * + * Any custom user provided cleanup function must ultimately call + * delete_fluid_sfont() to ensure proper cleanup of the #fluid_sfont_t struct. If no private data + * needs to be freed, setting this to delete_fluid_sfont() is sufficient. + */ +typedef int (*fluid_sfont_free_t)(fluid_sfont_t *sfont); + + +/** @startlifecycle{SoundFont} */ +FLUIDSYNTH_API fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name, + fluid_sfont_get_preset_t get_preset, + fluid_sfont_iteration_start_t iter_start, + fluid_sfont_iteration_next_t iter_next, + fluid_sfont_free_t free); + +FLUIDSYNTH_API int delete_fluid_sfont(fluid_sfont_t *sfont); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data); +FLUIDSYNTH_API void *fluid_sfont_get_data(fluid_sfont_t *sfont); + +FLUIDSYNTH_API int fluid_sfont_get_id(fluid_sfont_t *sfont); +FLUIDSYNTH_API const char *fluid_sfont_get_name(fluid_sfont_t *sfont); +FLUIDSYNTH_API fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum); +FLUIDSYNTH_API void fluid_sfont_iteration_start(fluid_sfont_t *sfont); +FLUIDSYNTH_API fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont); + +/** + * Method to get a virtual SoundFont preset name. + * + * @param preset Virtual SoundFont preset + * @return Should return the name of the preset. The returned string must be + * valid for the duration of the virtual preset (or the duration of the + * SoundFont, in the case of preset iteration). + */ +typedef const char *(*fluid_preset_get_name_t)(fluid_preset_t *preset); + +/** + * Method to get a virtual SoundFont preset MIDI bank number. + * + * @param preset Virtual SoundFont preset + * @param return The bank number of the preset + */ +typedef int (*fluid_preset_get_banknum_t)(fluid_preset_t *preset); + +/** + * Method to get a virtual SoundFont preset MIDI program number. + * + * @param preset Virtual SoundFont preset + * @param return The program number of the preset + */ +typedef int (*fluid_preset_get_num_t)(fluid_preset_t *preset); + +/** + * Method to handle a noteon event (synthesize the instrument). + * + * @param preset Virtual SoundFont preset + * @param synth Synthesizer instance + * @param chan MIDI channel number of the note on event + * @param key MIDI note number (0-127) + * @param vel MIDI velocity (0-127) + * @return #FLUID_OK on success (0) or #FLUID_FAILED (-1) otherwise + * + * This method may be called from within synthesis context and therefore + * should be as efficient as possible and not perform any operations considered + * bad for realtime audio output (memory allocations and other OS calls). + * + * Call fluid_synth_alloc_voice() for every sample that has + * to be played. fluid_synth_alloc_voice() expects a pointer to a + * #fluid_sample_t structure and returns a pointer to the opaque + * #fluid_voice_t structure. To set or increment the values of a + * generator, use fluid_voice_gen_set() or fluid_voice_gen_incr(). When you are + * finished initializing the voice call fluid_voice_start() to + * start playing the synthesis voice. Starting with FluidSynth 1.1.0 all voices + * created will be started at the same time. + */ +typedef int (*fluid_preset_noteon_t)(fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel); + +/** + * Method to free a virtual SoundFont preset. + * + * @param preset Virtual SoundFont preset + * @return Should return 0 + * + * Any custom user provided cleanup function must ultimately call + * delete_fluid_preset() to ensure proper cleanup of the #fluid_preset_t struct. If no private data + * needs to be freed, setting this to delete_fluid_preset() is sufficient. + */ +typedef void (*fluid_preset_free_t)(fluid_preset_t *preset); + +/** @startlifecycle{Preset} */ +FLUIDSYNTH_API fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont, + fluid_preset_get_name_t get_name, + fluid_preset_get_banknum_t get_bank, + fluid_preset_get_num_t get_num, + fluid_preset_noteon_t noteon, + fluid_preset_free_t free); +FLUIDSYNTH_API void delete_fluid_preset(fluid_preset_t *preset); +/** @endlifecycle */ + +FLUIDSYNTH_API int fluid_preset_set_data(fluid_preset_t *preset, void *data); +FLUIDSYNTH_API void *fluid_preset_get_data(fluid_preset_t *preset); + +FLUIDSYNTH_API const char *fluid_preset_get_name(fluid_preset_t *preset); +FLUIDSYNTH_API int fluid_preset_get_banknum(fluid_preset_t *preset); +FLUIDSYNTH_API int fluid_preset_get_num(fluid_preset_t *preset); +FLUIDSYNTH_API fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset); + +/** @startlifecycle{Sample} */ +FLUIDSYNTH_API fluid_sample_t *new_fluid_sample(void); +FLUIDSYNTH_API void delete_fluid_sample(fluid_sample_t *sample); +/** @endlifecycle */ + +FLUIDSYNTH_API size_t fluid_sample_sizeof(void); + +FLUIDSYNTH_API int fluid_sample_set_name(fluid_sample_t *sample, const char *name); +FLUIDSYNTH_API int fluid_sample_set_sound_data(fluid_sample_t *sample, + short *data, + char *data24, + unsigned int nbframes, + unsigned int sample_rate, + short copy_data); + +FLUIDSYNTH_API int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end); +FLUIDSYNTH_API int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune); + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SFONT_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/shell.h b/thirdparty/fluidsynth/include/fluidsynth/shell.h new file mode 100644 index 000000000..80787a30a --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/shell.h @@ -0,0 +1,150 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SHELL_H +#define _FLUIDSYNTH_SHELL_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup command_interface Command Interface + * + * Control and configuration interface + * + * The command interface allows you to send textual commands to + * the synthesizer, to parse a command file, or to read commands + * from the stdin or other input streams (like a TCP socket). + * + * For a full list of available commands, type \c help in the + * \ref command_shell or send the same command via a command handler. + * Further documentation can be found at + * https://github.com/FluidSynth/fluidsynth/wiki/UserManual#shell-commands + * + * @{ + */ +FLUIDSYNTH_API fluid_istream_t fluid_get_stdin(void); +FLUIDSYNTH_API fluid_ostream_t fluid_get_stdout(void); +FLUIDSYNTH_API char *fluid_get_userconf(char *buf, int len); +FLUIDSYNTH_API char *fluid_get_sysconf(char *buf, int len); +/* @} */ + + +/** + * @defgroup command_handler Command Handler + * @ingroup command_interface + * @brief Handles text commands and reading of configuration files + * + * @{ + */ + +/** @startlifecycle{Command Handler} */ +FLUIDSYNTH_API +fluid_cmd_handler_t *new_fluid_cmd_handler(fluid_synth_t *synth, fluid_midi_router_t *router); + +FLUIDSYNTH_API +fluid_cmd_handler_t *new_fluid_cmd_handler2(fluid_settings_t *settings, fluid_synth_t *synth, + fluid_midi_router_t *router, fluid_player_t *player); + +FLUIDSYNTH_API +void delete_fluid_cmd_handler(fluid_cmd_handler_t *handler); +/** @endlifecycle */ + +FLUIDSYNTH_API +void fluid_cmd_handler_set_synth(fluid_cmd_handler_t *handler, fluid_synth_t *synth); + +FLUIDSYNTH_API +int fluid_command(fluid_cmd_handler_t *handler, const char *cmd, fluid_ostream_t out); + +FLUIDSYNTH_API +int fluid_source(fluid_cmd_handler_t *handler, const char *filename); +/* @} */ + + +/** + * @defgroup command_shell Command Shell + * @ingroup command_interface + * + * Interactive shell to control and configure a synthesizer instance. + * + * If you need a platform independent way to get the standard input + * and output streams, use fluid_get_stdin() and fluid_get_stdout(). + * + * For a full list of available commands, type \c help in the shell. + * + * @{ + */ + +/** @startlifecycle{Command Shell} */ +FLUIDSYNTH_API +fluid_shell_t *new_fluid_shell(fluid_settings_t *settings, fluid_cmd_handler_t *handler, + fluid_istream_t in, fluid_ostream_t out, int thread); + +FLUIDSYNTH_API +void fluid_usershell(fluid_settings_t *settings, fluid_cmd_handler_t *handler); + +FLUIDSYNTH_API void delete_fluid_shell(fluid_shell_t *shell); +/** @endlifecycle */ + +/* @} */ + + +/** + * @defgroup command_server Command Server + * @ingroup command_interface + * + * TCP socket server for a command handler. + * + * The socket server will open the TCP port set by \ref settings_shell_port + * (default 9800) and starts a new thread and \ref command_handler for each + * incoming connection. + * + * @note The server is only available if libfluidsynth has been compiled + * with network support (enable-network). Without network support, all related + * functions will return FLUID_FAILED or NULL. + * + * @{ + */ + +/** @startlifecycle{Command Server} */ +FLUIDSYNTH_API +fluid_server_t *new_fluid_server(fluid_settings_t *settings, + fluid_synth_t *synth, fluid_midi_router_t *router); + +FLUIDSYNTH_API +fluid_server_t *new_fluid_server2(fluid_settings_t *settings, + fluid_synth_t *synth, fluid_midi_router_t *router, + fluid_player_t *player); + +FLUIDSYNTH_API void delete_fluid_server(fluid_server_t *server); + +FLUIDSYNTH_API int fluid_server_join(fluid_server_t *server); +/** @endlifecycle */ + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SHELL_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/synth.h b/thirdparty/fluidsynth/include/fluidsynth/synth.h new file mode 100644 index 000000000..0829c3e8f --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/synth.h @@ -0,0 +1,562 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_SYNTH_H +#define _FLUIDSYNTH_SYNTH_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup synth Synthesizer + * + * SoundFont synthesizer + * + * You create a new synthesizer with new_fluid_synth() and you destroy + * it with delete_fluid_synth(). Use the fluid_settings_t structure to specify + * the synthesizer characteristics. + * + * You have to load a SoundFont in order to hear any sound. For that + * you use the fluid_synth_sfload() function. + * + * You can use the audio driver functions to open + * the audio device and create a background audio thread. + * + * The API for sending MIDI events is probably what you expect: + * fluid_synth_noteon(), fluid_synth_noteoff(), ... + * + * @{ + */ + +/** @startlifecycle{Synthesizer} */ +FLUIDSYNTH_API fluid_synth_t *new_fluid_synth(fluid_settings_t *settings); +FLUIDSYNTH_API void delete_fluid_synth(fluid_synth_t *synth); +/** @endlifecycle */ + +FLUIDSYNTH_API double fluid_synth_get_cpu_load(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API const char *fluid_synth_error(fluid_synth_t *synth); +/* @} */ + +/** + * @defgroup midi_messages MIDI Channel Messages + * @ingroup synth + * + * The MIDI channel message functions are mostly directly named after their + * counterpart MIDI messages. They are a high-level interface to controlling + * the synthesizer, playing notes and changing note and channel parameters. + * + * @{ + */ +FLUIDSYNTH_API int fluid_synth_noteon(fluid_synth_t *synth, int chan, int key, int vel); +FLUIDSYNTH_API int fluid_synth_noteoff(fluid_synth_t *synth, int chan, int key); +FLUIDSYNTH_API int fluid_synth_cc(fluid_synth_t *synth, int chan, int ctrl, int val); +FLUIDSYNTH_API int fluid_synth_get_cc(fluid_synth_t *synth, int chan, int ctrl, int *pval); +FLUIDSYNTH_API int fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int *handled, int dryrun); +FLUIDSYNTH_API int fluid_synth_pitch_bend(fluid_synth_t *synth, int chan, int val); +FLUIDSYNTH_API int fluid_synth_get_pitch_bend(fluid_synth_t *synth, int chan, int *ppitch_bend); +FLUIDSYNTH_API int fluid_synth_pitch_wheel_sens(fluid_synth_t *synth, int chan, int val); +FLUIDSYNTH_API int fluid_synth_get_pitch_wheel_sens(fluid_synth_t *synth, int chan, int *pval); +FLUIDSYNTH_API int fluid_synth_program_change(fluid_synth_t *synth, int chan, int program); +FLUIDSYNTH_API int fluid_synth_channel_pressure(fluid_synth_t *synth, int chan, int val); +FLUIDSYNTH_API int fluid_synth_key_pressure(fluid_synth_t *synth, int chan, int key, int val); +FLUIDSYNTH_API int fluid_synth_bank_select(fluid_synth_t *synth, int chan, int bank); +FLUIDSYNTH_API int fluid_synth_sfont_select(fluid_synth_t *synth, int chan, int sfont_id); +FLUIDSYNTH_API +int fluid_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id, + int bank_num, int preset_num); +FLUIDSYNTH_API int +fluid_synth_program_select_by_sfont_name(fluid_synth_t *synth, int chan, + const char *sfont_name, int bank_num, + int preset_num); +FLUIDSYNTH_API +int fluid_synth_get_program(fluid_synth_t *synth, int chan, int *sfont_id, + int *bank_num, int *preset_num); +FLUIDSYNTH_API int fluid_synth_unset_program(fluid_synth_t *synth, int chan); +FLUIDSYNTH_API int fluid_synth_program_reset(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_system_reset(fluid_synth_t *synth); + +FLUIDSYNTH_API int fluid_synth_all_notes_off(fluid_synth_t *synth, int chan); +FLUIDSYNTH_API int fluid_synth_all_sounds_off(fluid_synth_t *synth, int chan); + +FLUIDSYNTH_API int fluid_synth_set_gen(fluid_synth_t *synth, int chan, + int param, float value); +FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param); +/* @} MIDI Channel Messages */ + + +/** + * @defgroup voice_control Synthesis Voice Control + * @ingroup synth + * + * Low-level access to synthesis voices. + * + * @{ + */ +FLUIDSYNTH_API int fluid_synth_start(fluid_synth_t *synth, unsigned int id, + fluid_preset_t *preset, int audio_chan, + int midi_chan, int key, int vel); +FLUIDSYNTH_API int fluid_synth_stop(fluid_synth_t *synth, unsigned int id); + +FLUIDSYNTH_API fluid_voice_t *fluid_synth_alloc_voice(fluid_synth_t *synth, + fluid_sample_t *sample, + int channum, int key, int vel); +FLUIDSYNTH_API void fluid_synth_start_voice(fluid_synth_t *synth, fluid_voice_t *voice); +FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t *synth, + fluid_voice_t *buf[], int bufsize, int ID); +/* @} Voice Control */ + + +/** + * @defgroup soundfont_management SoundFont Management + * @ingroup synth + * + * Functions to load and unload SoundFonts. + * + * @{ + */ +FLUIDSYNTH_API +int fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets); +FLUIDSYNTH_API int fluid_synth_sfreload(fluid_synth_t *synth, int id); +FLUIDSYNTH_API int fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets); +FLUIDSYNTH_API int fluid_synth_add_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont); +FLUIDSYNTH_API int fluid_synth_remove_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont); +FLUIDSYNTH_API int fluid_synth_sfcount(fluid_synth_t *synth); +FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont(fluid_synth_t *synth, unsigned int num); +FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont_by_id(fluid_synth_t *synth, int id); +FLUIDSYNTH_API fluid_sfont_t *fluid_synth_get_sfont_by_name(fluid_synth_t *synth, + const char *name); +FLUIDSYNTH_API int fluid_synth_set_bank_offset(fluid_synth_t *synth, int sfont_id, int offset); +FLUIDSYNTH_API int fluid_synth_get_bank_offset(fluid_synth_t *synth, int sfont_id); +/* @} Soundfont Management */ + + +/** + * @defgroup reverb_effect Effect - Reverb + * @ingroup synth + * + * Functions for configuring the built-in reverb effect + * + * @{ + */ +FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_reverb_on(fluid_synth_t *synth, int on); +FLUIDSYNTH_API int fluid_synth_reverb_on(fluid_synth_t *synth, int fx_group, int on); + +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, + double damping, double width, double level); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level); + +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_roomsize(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_damp(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_level(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_reverb_width(fluid_synth_t *synth); + +FLUIDSYNTH_API int fluid_synth_set_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, double roomsize); +FLUIDSYNTH_API int fluid_synth_set_reverb_group_damp(fluid_synth_t *synth, int fx_group, double damping); +FLUIDSYNTH_API int fluid_synth_set_reverb_group_width(fluid_synth_t *synth, int fx_group, double width); +FLUIDSYNTH_API int fluid_synth_set_reverb_group_level(fluid_synth_t *synth, int fx_group, double level); + +FLUIDSYNTH_API int fluid_synth_get_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, double *roomsize); +FLUIDSYNTH_API int fluid_synth_get_reverb_group_damp(fluid_synth_t *synth, int fx_group, double *damping); +FLUIDSYNTH_API int fluid_synth_get_reverb_group_width(fluid_synth_t *synth, int fx_group, double *width); +FLUIDSYNTH_API int fluid_synth_get_reverb_group_level(fluid_synth_t *synth, int fx_group, double *level); + /* @} Reverb */ + + +/** + * @defgroup chorus_effect Effect - Chorus + * @ingroup synth + * + * Functions for configuring the built-in chorus effect + * + * @{ + */ + +/** + * Chorus modulation waveform type. + */ +enum fluid_chorus_mod +{ + FLUID_CHORUS_MOD_SINE = 0, /**< Sine wave chorus modulation */ + FLUID_CHORUS_MOD_TRIANGLE = 1 /**< Triangle wave chorus modulation */ +}; + + +FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_chorus_on(fluid_synth_t *synth, int on); +FLUIDSYNTH_API int fluid_synth_chorus_on(fluid_synth_t *synth, int fx_group, int on); + +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level, + double speed, double depth_ms, int type); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type); + +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_get_chorus_nr(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_level(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_speed(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API double fluid_synth_get_chorus_depth(fluid_synth_t *synth); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_get_chorus_type(fluid_synth_t *synth); /* see fluid_chorus_mod */ + +FLUIDSYNTH_API int fluid_synth_set_chorus_group_nr(fluid_synth_t *synth, int fx_group, int nr); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_level(fluid_synth_t *synth, int fx_group, double level); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_speed(fluid_synth_t *synth, int fx_group, double speed); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_depth(fluid_synth_t *synth, int fx_group, double depth_ms); +FLUIDSYNTH_API int fluid_synth_set_chorus_group_type(fluid_synth_t *synth, int fx_group, int type); + +FLUIDSYNTH_API int fluid_synth_get_chorus_group_nr(fluid_synth_t *synth, int fx_group, int *nr); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_level(fluid_synth_t *synth, int fx_group, double *level); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_speed(fluid_synth_t *synth, int fx_group, double *speed); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_depth(fluid_synth_t *synth, int fx_group, double *depth_ms); +FLUIDSYNTH_API int fluid_synth_get_chorus_group_type(fluid_synth_t *synth, int fx_group, int *type); +/* @} Chorus */ + +/** + * @defgroup synthesis_params Synthesis Parameters + * @ingroup synth + * + * Functions to control and query synthesis parameters like gain and + * polyphony count. + * + * @{ + */ +FLUIDSYNTH_API int fluid_synth_count_midi_channels(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_audio_channels(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_audio_groups(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_effects_channels(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_count_effects_groups(fluid_synth_t *synth); + +FLUID_DEPRECATED FLUIDSYNTH_API void fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate); +FLUIDSYNTH_API void fluid_synth_set_gain(fluid_synth_t *synth, float gain); +FLUIDSYNTH_API float fluid_synth_get_gain(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_set_polyphony(fluid_synth_t *synth, int polyphony); +FLUIDSYNTH_API int fluid_synth_get_polyphony(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_get_active_voice_count(fluid_synth_t *synth); +FLUIDSYNTH_API int fluid_synth_get_internal_bufsize(fluid_synth_t *synth); + +FLUIDSYNTH_API +int fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method); + +/** + * Synthesis interpolation method. + */ +enum fluid_interp +{ + FLUID_INTERP_NONE = 0, /**< No interpolation: Fastest, but questionable audio quality */ + FLUID_INTERP_LINEAR = 1, /**< Straight-line interpolation: A bit slower, reasonable audio quality */ + FLUID_INTERP_4THORDER = 4, /**< Fourth-order interpolation, good quality, the default */ + FLUID_INTERP_7THORDER = 7, /**< Seventh-order interpolation */ + + FLUID_INTERP_DEFAULT = FLUID_INTERP_4THORDER, /**< Default interpolation method */ + FLUID_INTERP_HIGHEST = FLUID_INTERP_7THORDER, /**< Highest interpolation method */ +}; + +/** + * Enum used with fluid_synth_add_default_mod() to specify how to handle duplicate modulators. + */ +enum fluid_synth_add_mod +{ + FLUID_SYNTH_OVERWRITE, /**< Overwrite any existing matching modulator */ + FLUID_SYNTH_ADD, /**< Sum up modulator amounts */ +}; + +FLUIDSYNTH_API int fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode); +FLUIDSYNTH_API int fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod); +/* @} Synthesis Parameters */ + + +/** + * @defgroup tuning MIDI Tuning + * @ingroup synth + * + * The functions in this section implement the MIDI Tuning Standard interface. + * + * @{ + */ +FLUIDSYNTH_API +int fluid_synth_activate_key_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply); +FLUIDSYNTH_API +int fluid_synth_activate_octave_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply); +FLUIDSYNTH_API +int fluid_synth_tune_notes(fluid_synth_t *synth, int bank, int prog, + int len, const int *keys, const double *pitch, int apply); +FLUIDSYNTH_API +int fluid_synth_activate_tuning(fluid_synth_t *synth, int chan, int bank, int prog, + int apply); +FLUIDSYNTH_API +int fluid_synth_deactivate_tuning(fluid_synth_t *synth, int chan, int apply); +FLUIDSYNTH_API void fluid_synth_tuning_iteration_start(fluid_synth_t *synth); +FLUIDSYNTH_API +int fluid_synth_tuning_iteration_next(fluid_synth_t *synth, int *bank, int *prog); +FLUIDSYNTH_API int fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int prog, + char *name, int len, double *pitch); +/* @} MIDI Tuning */ + + +/** + * @defgroup audio_rendering Audio Rendering + * @ingroup synth + * + * The functions in this section can be used to render audio directly to + * memory buffers. They are used internally by the \ref audio_driver and \ref file_renderer, + * but can also be used manually for custom processing of the rendered audio. + * + * @note Please note that all following functions block during rendering. If your goal is to + * render real-time audio, ensure that you call these functions from a high-priority + * thread with little to no other duties other than calling the rendering functions. + * + * @warning + * If a concurrently running thread calls any other sound affecting synth function + * (e.g. fluid_synth_noteon(), fluid_synth_cc(), etc.) it is unspecified whether the event triggered by such a call + * will be effective in the recently synthesized audio. While this is inaudible when only requesting small chunks from the + * synth with every call (cf. fluid_synth_get_internal_bufsize()), it will become evident when requesting larger sample chunks: + * With larger sample chunks it will get harder for the synth to react on those spontaneously occurring events in time + * (like events received from a MIDI driver, or directly made synth API calls). + * In those real-time scenarios, prefer requesting smaller + * sample chunks from the synth with each call, to avoid poor quantization of your events in the synthesized audio. + * This issue is not applicable when using the MIDI player or sequencer for event dispatching. Also + * refer to the documentation of \setting{audio_period-size}. + * + * @{ + */ +FLUIDSYNTH_API int fluid_synth_write_s16(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr); +FLUIDSYNTH_API int fluid_synth_write_float(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr); +FLUID_DEPRECATED FLUIDSYNTH_API int fluid_synth_nwrite_float(fluid_synth_t *synth, int len, + float **left, float **right, + float **fx_left, float **fx_right); +FLUIDSYNTH_API int fluid_synth_process(fluid_synth_t *synth, int len, + int nfx, float *fx[], + int nout, float *out[]); +/* @} Audio Rendering */ + + +/** + * @defgroup iir_filter Effect - IIR Filter + * @ingroup synth + * + * Functions for configuring the built-in IIR filter effect + * + * @{ + */ + +/** + * Specifies the type of filter to use for the custom IIR filter + */ +enum fluid_iir_filter_type +{ + FLUID_IIR_DISABLED = 0, /**< Custom IIR filter is not operating */ + FLUID_IIR_LOWPASS, /**< Custom IIR filter is operating as low-pass filter */ + FLUID_IIR_HIGHPASS, /**< Custom IIR filter is operating as high-pass filter */ + FLUID_IIR_LAST /**< @internal Value defines the count of filter types (#fluid_iir_filter_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +/** + * Specifies optional settings to use for the custom IIR filter. Can be bitwise ORed. + */ +enum fluid_iir_filter_flags +{ + FLUID_IIR_Q_LINEAR = 1 << 0, /**< The Soundfont spec requires the filter Q to be interpreted in dB. If this flag is set the filter Q is instead assumed to be in a linear range */ + FLUID_IIR_Q_ZERO_OFF = 1 << 1, /**< If this flag the filter is switched off if Q == 0 (prior to any transformation) */ + FLUID_IIR_NO_GAIN_AMP = 1 << 2 /**< The Soundfont spec requires to correct the gain of the filter depending on the filter's Q. If this flag is set the filter gain will not be corrected. */ +}; + +FLUIDSYNTH_API int fluid_synth_set_custom_filter(fluid_synth_t *, int type, int flags); +/* @} IIR Filter */ + + + + +/** + * @defgroup channel_setup MIDI Channel Setup + * @ingroup synth + * + * The functions in this section provide interfaces to change the channel type + * and to configure basic channels, legato and portamento setups. + * + * @{ + */ + +/** @name Channel Type + * @{ + */ + +/** + * The midi channel type used by fluid_synth_set_channel_type() + */ +enum fluid_midi_channel_type +{ + CHANNEL_TYPE_MELODIC = 0, /**< Melodic midi channel */ + CHANNEL_TYPE_DRUM = 1 /**< Drum midi channel */ +}; + +FLUIDSYNTH_API int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type); +/** @} Channel Type */ + + +/** @name Basic Channel Mode + * @{ + */ + +// silence a VERY noisy warning in recent MSVC compilers +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:5287) +#endif + +/** + * Channel mode bits OR-ed together so that it matches with the midi spec: poly omnion (0), mono omnion (1), poly omnioff (2), mono omnioff (3) + */ +enum fluid_channel_mode_flags +{ + FLUID_CHANNEL_POLY_OFF = 0x01, /**< if flag is set, the basic channel is in mono on state, if not set poly is on */ + FLUID_CHANNEL_OMNI_OFF = 0x02, /**< if flag is set, the basic channel is in omni off state, if not set omni is on */ +}; + +/** + * Indicates the mode a basic channel is set to + */ +enum fluid_basic_channel_modes +{ + FLUID_CHANNEL_MODE_MASK = (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< Mask Poly and Omni bits of #fluid_channel_mode_flags, usually only used internally */ + FLUID_CHANNEL_MODE_OMNION_POLY = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 0 */ + FLUID_CHANNEL_MODE_OMNION_MONO = FLUID_CHANNEL_MODE_MASK & (~FLUID_CHANNEL_OMNI_OFF & FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 1 */ + FLUID_CHANNEL_MODE_OMNIOFF_POLY = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF & ~FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 2 */ + FLUID_CHANNEL_MODE_OMNIOFF_MONO = FLUID_CHANNEL_MODE_MASK & (FLUID_CHANNEL_OMNI_OFF | FLUID_CHANNEL_POLY_OFF), /**< corresponds to MIDI mode 3 */ + FLUID_CHANNEL_MODE_LAST /**< @internal Value defines the count of basic channel modes (#fluid_basic_channel_modes) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +FLUIDSYNTH_API int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan); + +FLUIDSYNTH_API int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan, + int *basic_chan_out, + int *mode_chan_out, + int *basic_val_out); +FLUIDSYNTH_API int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val); + +/** @} Basic Channel Mode */ + +/** @name Legato Mode + * @{ + */ + +/** + * Indicates the legato mode a channel is set to + * n1,n2,n3,.. is a legato passage. n1 is the first note, and n2,n3,n4 are played legato with previous note. */ +enum fluid_channel_legato_mode +{ + FLUID_CHANNEL_LEGATO_MODE_RETRIGGER, /**< Mode 0 - Release previous note, start a new note */ + FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER, /**< Mode 1 - On contiguous notes retrigger in attack section using current value, shape attack using current dynamic and make use of previous voices if any */ + FLUID_CHANNEL_LEGATO_MODE_LAST /**< @internal Value defines the count of legato modes (#fluid_channel_legato_mode) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */ +}; + +FLUIDSYNTH_API int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode); +FLUIDSYNTH_API int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode); +/** @} Legato Mode */ + +/** @name Portamento Mode + * @{ + */ + +/** + * Indicates the portamento mode a channel is set to + */ +enum fluid_channel_portamento_mode +{ + FLUID_CHANNEL_PORTAMENTO_MODE_EACH_NOTE, /**< Mode 0 - Portamento on each note (staccato or legato) */ + FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY, /**< Mode 1 - Portamento only on legato note */ + FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY, /**< Mode 2 - Portamento only on staccato note */ + FLUID_CHANNEL_PORTAMENTO_MODE_LAST /**< @internal Value defines the count of portamento modes + @warning This symbol is not part of the public API and ABI + stability guarantee and may change at any time! */ +}; + +FLUIDSYNTH_API int fluid_synth_set_portamento_mode(fluid_synth_t *synth, + int chan, int portamentomode); +FLUIDSYNTH_API int fluid_synth_get_portamento_mode(fluid_synth_t *synth, + int chan, int *portamentomode); +/** @} Portamento Mode */ + +/**@name Breath Mode + * @{ + */ + +/** + * Indicates the breath mode a channel is set to + */ +enum fluid_channel_breath_flags +{ + FLUID_CHANNEL_BREATH_POLY = 0x10, /**< when channel is poly, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath to initial attenuation modulator */ + FLUID_CHANNEL_BREATH_MONO = 0x20, /**< when channel is mono, this flag indicates that the default velocity to initial attenuation modulator is replaced by a breath modulator */ + FLUID_CHANNEL_BREATH_SYNC = 0x40, /**< when channel is mono, this flag indicates that the breath controller(MSB)triggers noteon/noteoff on the running note */ +}; + +FLUIDSYNTH_API int fluid_synth_set_breath_mode(fluid_synth_t *synth, + int chan, int breathmode); +FLUIDSYNTH_API int fluid_synth_get_breath_mode(fluid_synth_t *synth, + int chan, int *breathmode); +/** @} Breath Mode */ +/* @} MIDI Channel Setup */ + + +/** @ingroup settings */ +FLUIDSYNTH_API fluid_settings_t *fluid_synth_get_settings(fluid_synth_t *synth); + +/** @ingroup soundfont_loader */ +FLUIDSYNTH_API void fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader); + +/** @ingroup soundfont_loader */ +FLUIDSYNTH_API fluid_preset_t *fluid_synth_get_channel_preset(fluid_synth_t *synth, int chan); + +/** @ingroup midi_input */ +FLUIDSYNTH_API int fluid_synth_handle_midi_event(void *data, fluid_midi_event_t *event); + +/** @ingroup soundfonts */ +FLUIDSYNTH_API +int fluid_synth_pin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num); + +/** @ingroup soundfonts */ +FLUIDSYNTH_API +int fluid_synth_unpin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num); + +/** @ingroup ladspa */ +FLUIDSYNTH_API fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_SYNTH_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/types.h b/thirdparty/fluidsynth/include/fluidsynth/types.h new file mode 100644 index 000000000..4352b4c57 --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/types.h @@ -0,0 +1,85 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_TYPES_H +#define _FLUIDSYNTH_TYPES_H + + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup Types Types + * @brief Type declarations + * + * @{ + */ + +typedef struct _fluid_hashtable_t fluid_settings_t; /**< Configuration settings instance */ +typedef struct _fluid_synth_t fluid_synth_t; /**< Synthesizer instance */ +typedef struct _fluid_voice_t fluid_voice_t; /**< Synthesis voice instance */ +typedef struct _fluid_sfloader_t fluid_sfloader_t; /**< SoundFont loader plugin */ +typedef struct _fluid_sfont_t fluid_sfont_t; /**< SoundFont */ +typedef struct _fluid_preset_t fluid_preset_t; /**< SoundFont preset */ +typedef struct _fluid_sample_t fluid_sample_t; /**< SoundFont sample */ +typedef struct _fluid_mod_t fluid_mod_t; /**< SoundFont modulator */ +typedef struct _fluid_audio_driver_t fluid_audio_driver_t; /**< Audio driver instance */ +typedef struct _fluid_file_renderer_t fluid_file_renderer_t; /**< Audio file renderer instance */ +typedef struct _fluid_player_t fluid_player_t; /**< MIDI player instance */ +typedef struct _fluid_midi_event_t fluid_midi_event_t; /**< MIDI event */ +typedef struct _fluid_midi_driver_t fluid_midi_driver_t; /**< MIDI driver instance */ +typedef struct _fluid_midi_router_t fluid_midi_router_t; /**< MIDI router instance */ +typedef struct _fluid_midi_router_rule_t fluid_midi_router_rule_t; /**< MIDI router rule */ +typedef struct _fluid_hashtable_t fluid_cmd_hash_t; /**< Command handler hash table */ +typedef struct _fluid_shell_t fluid_shell_t; /**< Command shell */ +typedef struct _fluid_server_t fluid_server_t; /**< TCP/IP shell server instance */ +typedef struct _fluid_event_t fluid_event_t; /**< Sequencer event */ +typedef struct _fluid_sequencer_t fluid_sequencer_t; /**< Sequencer instance */ +typedef struct _fluid_ramsfont_t fluid_ramsfont_t; /**< RAM SoundFont */ +typedef struct _fluid_rampreset_t fluid_rampreset_t; /**< RAM SoundFont preset */ +typedef struct _fluid_cmd_handler_t fluid_cmd_handler_t; /**< Shell Command Handler */ +typedef struct _fluid_ladspa_fx_t fluid_ladspa_fx_t; /**< LADSPA effects instance */ +typedef struct _fluid_file_callbacks_t fluid_file_callbacks_t; /**< Callback struct to perform custom file loading of soundfonts */ + +typedef int fluid_istream_t; /**< Input stream descriptor */ +typedef int fluid_ostream_t; /**< Output stream descriptor */ + +typedef short fluid_seq_id_t; /**< Unique client IDs used by the sequencer and #fluid_event_t, obtained by fluid_sequencer_register_client() and fluid_sequencer_register_fluidsynth() */ + +#if defined(_MSC_VER) && (_MSC_VER < 1800) +typedef __int64 fluid_long_long_t; // even on 32bit windows +#else +/** + * A typedef for C99's type long long, which is at least 64-bit wide, as guaranteed by the C99. + * @p __int64 will be used as replacement for VisualStudio 2010 and older. + */ +typedef long long fluid_long_long_t; +#endif + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_TYPES_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/version.h b/thirdparty/fluidsynth/include/fluidsynth/version.h new file mode 100644 index 000000000..2662fcd86 --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/version.h @@ -0,0 +1,47 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_VERSION_H +#define _FLUIDSYNTH_VERSION_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup misc + * + * @{ + */ +#define FLUIDSYNTH_VERSION "2.2.8" /**< String constant of libfluidsynth version. */ +#define FLUIDSYNTH_VERSION_MAJOR 2 /**< libfluidsynth major version integer constant. */ +#define FLUIDSYNTH_VERSION_MINOR 2 /**< libfluidsynth minor version integer constant. */ +#define FLUIDSYNTH_VERSION_MICRO 8 /**< libfluidsynth micro version integer constant. */ + +FLUIDSYNTH_API void fluid_version(int *major, int *minor, int *micro); +FLUIDSYNTH_API char* fluid_version_str(void); +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_VERSION_H */ diff --git a/thirdparty/fluidsynth/include/fluidsynth/voice.h b/thirdparty/fluidsynth/include/fluidsynth/voice.h new file mode 100644 index 000000000..ead4f710e --- /dev/null +++ b/thirdparty/fluidsynth/include/fluidsynth/voice.h @@ -0,0 +1,77 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUIDSYNTH_VOICE_H +#define _FLUIDSYNTH_VOICE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup voices Voice Manipulation + * @ingroup soundfonts + * + * Synthesis voice manipulation functions. + * + * The interface to the synthesizer's voices. + * Examples on using them can be found in the source code of the default SoundFont + * loader (fluid_defsfont.c). + * + * Most of these functions should only be called from within synthesis context, + * such as the SoundFont loader's noteon method. + * + * @{ + */ + +/** + * Enum used with fluid_voice_add_mod() to specify how to handle duplicate modulators. + */ +enum fluid_voice_add_mod +{ + FLUID_VOICE_OVERWRITE, /**< Overwrite any existing matching modulator */ + FLUID_VOICE_ADD, /**< Add (sum) modulator amounts */ + FLUID_VOICE_DEFAULT /**< For default modulators only, no need to check for duplicates */ +}; + +FLUIDSYNTH_API void fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode); +FLUIDSYNTH_API float fluid_voice_gen_get(fluid_voice_t *voice, int gen); +FLUIDSYNTH_API void fluid_voice_gen_set(fluid_voice_t *voice, int gen, float val); +FLUIDSYNTH_API void fluid_voice_gen_incr(fluid_voice_t *voice, int gen, float val); + +FLUIDSYNTH_API unsigned int fluid_voice_get_id(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_channel(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_key(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_actual_key(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_velocity(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_get_actual_velocity(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_playing(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_on(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_sustained(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_is_sostenuto(const fluid_voice_t *voice); +FLUIDSYNTH_API int fluid_voice_optimize_sample(fluid_sample_t *s); +FLUIDSYNTH_API void fluid_voice_update_param(fluid_voice_t *voice, int gen); +/* @} */ + +#ifdef __cplusplus +} +#endif +#endif /* _FLUIDSYNTH_VOICE_H */ + diff --git a/thirdparty/fluidsynth/src/CMakeLists.txt b/thirdparty/fluidsynth/src/CMakeLists.txt new file mode 100644 index 000000000..efcd28fe0 --- /dev/null +++ b/thirdparty/fluidsynth/src/CMakeLists.txt @@ -0,0 +1,162 @@ +# FluidSynth - A Software Synthesizer +# +# Copyright (C) 2003-2010 Peter Hanappe and others. +# +# This library 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 library 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 library; if not, write to the Free +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307, USA + +# CMake based build system. Pedro Lopez-Cabanillas + +# ************ library ************ + +if ( LIBINSTPATCH_SUPPORT ) + set ( fluid_libinstpatch_SOURCES sfloader/fluid_instpatch.c sfloader/fluid_instpatch.h ) +endif ( LIBINSTPATCH_SUPPORT ) + + +set ( fluidsynth_SOURCES + config.h + utils/fluid_conv.c + utils/fluid_conv.h + utils/fluid_hash.c + utils/fluid_hash.h + utils/fluid_list.c + utils/fluid_list.h + utils/fluid_ringbuffer.c + utils/fluid_ringbuffer.h + utils/fluid_settings.c + utils/fluid_settings.h + utils/fluidsynth_priv.h + utils/fluid_sys.c + utils/fluid_sys.h + sfloader/fluid_defsfont.c + sfloader/fluid_defsfont.h + sfloader/fluid_sfont.h + sfloader/fluid_sfont.c + sfloader/fluid_sffile.c + sfloader/fluid_sffile.h + sfloader/fluid_samplecache.c + sfloader/fluid_samplecache.h + rvoice/fluid_adsr_env.c + rvoice/fluid_adsr_env.h + rvoice/fluid_chorus.c + rvoice/fluid_chorus.h + rvoice/fluid_iir_filter.c + rvoice/fluid_iir_filter.h + rvoice/fluid_lfo.c + rvoice/fluid_lfo.h + rvoice/fluid_rvoice.h + rvoice/fluid_rvoice.c + rvoice/fluid_rvoice_dsp.c + rvoice/fluid_rvoice_event.h + rvoice/fluid_rvoice_event.c + rvoice/fluid_rvoice_mixer.h + rvoice/fluid_rvoice_mixer.c + rvoice/fluid_phase.h + rvoice/fluid_rev.c + rvoice/fluid_rev.h + synth/fluid_chan.c + synth/fluid_chan.h + synth/fluid_event.c + synth/fluid_event.h + synth/fluid_gen.c + synth/fluid_gen.h + synth/fluid_mod.c + synth/fluid_mod.h + synth/fluid_synth.c + synth/fluid_synth.h + synth/fluid_synth_monopoly.c + synth/fluid_tuning.c + synth/fluid_tuning.h + synth/fluid_voice.c + synth/fluid_voice.h + midi/fluid_midi.c + midi/fluid_midi.h + midi/fluid_midi_router.c + midi/fluid_midi_router.h + midi/fluid_seqbind.c + midi/fluid_seqbind_notes.cpp + midi/fluid_seq.c + midi/fluid_seq_queue.cpp + drivers/fluid_adriver.c + drivers/fluid_adriver.h + drivers/fluid_mdriver.c + drivers/fluid_mdriver.h + bindings/fluid_filerenderer.c + bindings/fluid_ladspa.c + bindings/fluid_ladspa.h +) + +if ( WIN32 ) + set( fluidsynth_SOURCES + ${fluidsynth_SOURCES} + utils/win32_glibstubs.c + utils/win32_glibstubs.h + ) +endif ( WIN32 ) + +set ( public_HEADERS + ../include/fluidsynth/audio.h + ../include/fluidsynth/event.h + ../include/fluidsynth/gen.h + ../include/fluidsynth/ladspa.h + ../include/fluidsynth/log.h + ../include/fluidsynth/midi.h + ../include/fluidsynth/misc.h + ../include/fluidsynth/mod.h + ../include/fluidsynth/seq.h + ../include/fluidsynth/seqbind.h + ../include/fluidsynth/settings.h + ../include/fluidsynth/sfont.h + ../include/fluidsynth/shell.h + ../include/fluidsynth/synth.h + ../include/fluidsynth/types.h + ../include/fluidsynth/voice.h + ../include/fluidsynth/version.h +) + +set ( public_main_HEADER + ../include/fluidsynth.h +) + +add_library ( fluidsynth OBJECT + ${config_SOURCES} + ${fluidsynth_SOURCES} + ${public_HEADERS} + ${public_main_HEADER} +) + +target_include_directories ( fluidsynth PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/drivers + ${CMAKE_CURRENT_SOURCE_DIR}/synth + ${CMAKE_CURRENT_SOURCE_DIR}/rvoice + ${CMAKE_CURRENT_SOURCE_DIR}/midi + ${CMAKE_CURRENT_SOURCE_DIR}/utils + ${CMAKE_CURRENT_SOURCE_DIR}/sfloader + ${CMAKE_CURRENT_SOURCE_DIR}/bindings + ${CMAKE_CURRENT_SOURCE_DIR}/../include + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_CURRENT_SOURCE_DIR}/../../../source/decoder +) + + +if (NOT WIN32) + find_package(PkgConfig REQUIRED) + pkg_search_module(GLIB REQUIRED glib-2.0) + target_include_directories ( fluidsynth PUBLIC ${GLIB_INCLUDE_DIRS} ) + target_link_libraries ( fluidsynth PUBLIC ${pkgcfg_lib_GLIB_glib-2.0} ) + +endif() diff --git a/thirdparty/fluidsynth/src/bindings/fluid_filerenderer.c b/thirdparty/fluidsynth/src/bindings/fluid_filerenderer.c new file mode 100644 index 000000000..401daadb4 --- /dev/null +++ b/thirdparty/fluidsynth/src/bindings/fluid_filerenderer.c @@ -0,0 +1,590 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* + * Low-level routines for file output. + */ + +#include "fluid_sys.h" +#include "fluid_synth.h" +#include "fluid_settings.h" + +#if LIBSNDFILE_SUPPORT +#include +#endif + +struct _fluid_file_renderer_t +{ + fluid_synth_t *synth; + +#if LIBSNDFILE_SUPPORT + SNDFILE *sndfile; + float *buf; +#else + FILE *file; + short *buf; +#endif + + int period_size; + int buf_size; +}; + +#if LIBSNDFILE_SUPPORT + +/* Default file type used, if none specified and auto extension search fails */ +#define FLUID_FILE_RENDERER_DEFAULT_FILE_TYPE SF_FORMAT_WAV + +/* File audio format names. + * !! Keep in sync with format_ids[] */ +static const char *const format_names[] = +{ + "s8", + "s16", + "s24", + "s32", + "u8", + "float", + "double" +}; + + +/* File audio format IDs. + * !! Keep in sync with format_names[] */ +static const int format_ids[] = +{ + SF_FORMAT_PCM_S8, + SF_FORMAT_PCM_16, + SF_FORMAT_PCM_24, + SF_FORMAT_PCM_32, + SF_FORMAT_PCM_U8, + SF_FORMAT_FLOAT, + SF_FORMAT_DOUBLE +}; + +/* File endian byte order names. + * !! Keep in sync with endian_ids[] */ +static const char *const endian_names[] = +{ + "auto", + "little", + "big", + "cpu" +}; + +/* File endian byte order ids. + * !! Keep in sync with endian_names[] */ +static const int endian_ids[] = +{ + SF_ENDIAN_FILE, + SF_ENDIAN_LITTLE, + SF_ENDIAN_BIG, + SF_ENDIAN_CPU +}; + +static int fluid_file_renderer_parse_options(char *filetype, char *format, + char *endian, char *filename, SF_INFO *info); +static int fluid_file_renderer_find_file_type(char *extension, int *type); +static int fluid_file_renderer_find_valid_format(SF_INFO *info); + +#endif + + +void +fluid_file_renderer_settings(fluid_settings_t *settings) +{ +#if LIBSNDFILE_SUPPORT + SF_FORMAT_INFO finfo, cmpinfo; + int major_count; + int i, i2; + unsigned int n; + + fluid_settings_register_str(settings, "audio.file.name", "fluidsynth.wav", 0); + fluid_settings_register_str(settings, "audio.file.type", "auto", 0); + fluid_settings_register_str(settings, "audio.file.format", "s16", 0); + fluid_settings_register_str(settings, "audio.file.endian", "auto", 0); + + fluid_settings_add_option(settings, "audio.file.type", "auto"); + + sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof(int)); + + for(i = 0; i < major_count; i++) + { + finfo.format = i; + sf_command(NULL, SFC_GET_FORMAT_MAJOR, &finfo, sizeof(finfo)); + + /* Check for duplicates */ + for(i2 = 0; i2 < i; i2++) + { + cmpinfo.format = i2; + sf_command(NULL, SFC_GET_FORMAT_MAJOR, &cmpinfo, sizeof(cmpinfo)); + + if(FLUID_STRCMP(cmpinfo.extension, finfo.extension) == 0) + { + break; + } + } + + if(i2 == i) + { + fluid_settings_add_option(settings, "audio.file.type", finfo.extension); + } + } + + for(n = 0; n < FLUID_N_ELEMENTS(format_names); n++) + { + fluid_settings_add_option(settings, "audio.file.format", format_names[n]); + } + + for(n = 0; n < FLUID_N_ELEMENTS(endian_names); n++) + { + fluid_settings_add_option(settings, "audio.file.endian", endian_names[n]); + } + +#else + + fluid_settings_register_str(settings, "audio.file.name", "fluidsynth.raw", 0); + fluid_settings_register_str(settings, "audio.file.type", "raw", 0); + fluid_settings_add_option(settings, "audio.file.type", "raw"); + fluid_settings_register_str(settings, "audio.file.format", "s16", 0); + fluid_settings_add_option(settings, "audio.file.format", "s16"); + fluid_settings_register_str(settings, "audio.file.endian", "cpu", 0); + fluid_settings_add_option(settings, "audio.file.endian", "cpu"); +#endif +} + +/** + * Create a new file renderer and open the file. + * + * @param synth The synth that creates audio data. + * @return the new object, or NULL on failure + * + * @note Available file types and formats depends on if libfluidsynth was + * built with libsndfile support or not. If not then only RAW 16 bit output is + * supported. + * + * Uses the following settings from the synth object: + * - \ref settings_audio_file_name : Output filename + * - \ref settings_audio_file_type : File type, "auto" tries to determine type from filename + * extension with fallback to "wav". + * - \ref settings_audio_file_format : Audio format + * - \ref settings_audio_file_endian : Endian byte order, "auto" for file type's default byte order + * - \ref settings_audio_period-size : Size of audio blocks to process + * - \ref settings_synth_sample-rate : Sample rate to use + * + * @since 1.1.0 + */ +fluid_file_renderer_t * +new_fluid_file_renderer(fluid_synth_t *synth) +{ +#if LIBSNDFILE_SUPPORT + char *type, *format, *endian; + SF_INFO info; + double samplerate; + int retval; +#endif + int audio_channels; + char *filename = NULL; + fluid_file_renderer_t *dev; + + fluid_return_val_if_fail(synth != NULL, NULL); + fluid_return_val_if_fail(synth->settings != NULL, NULL); + + dev = FLUID_NEW(fluid_file_renderer_t); + + if(dev == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(dev, 0, sizeof(fluid_file_renderer_t)); + + dev->synth = synth; + fluid_settings_getint(synth->settings, "audio.period-size", &dev->period_size); + +#if LIBSNDFILE_SUPPORT + dev->buf_size = 2 * dev->period_size * sizeof(float); + dev->buf = FLUID_ARRAY(float, 2 * dev->period_size); +#else + dev->buf_size = 2 * dev->period_size * sizeof(short); + dev->buf = FLUID_ARRAY(short, 2 * dev->period_size); +#endif + + if(dev->buf == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + fluid_settings_dupstr(synth->settings, "audio.file.name", &filename); + fluid_settings_getint(synth->settings, "synth.audio-channels", &audio_channels); + + if(filename == NULL) + { + FLUID_LOG(FLUID_ERR, "No file name specified"); + goto error_recovery; + } + +#if LIBSNDFILE_SUPPORT + memset(&info, 0, sizeof(info)); + + info.format = FLUID_FILE_RENDERER_DEFAULT_FILE_TYPE | SF_FORMAT_PCM_16; + + fluid_settings_dupstr(synth->settings, "audio.file.type", &type); + fluid_settings_dupstr(synth->settings, "audio.file.format", &format); + fluid_settings_dupstr(synth->settings, "audio.file.endian", &endian); + + retval = fluid_file_renderer_parse_options(type, format, endian, filename, &info); + + if(type) + { + FLUID_FREE(type); + } + + if(format) + { + FLUID_FREE(format); + } + + if(endian) + { + FLUID_FREE(endian); + } + + if(!retval) + { + goto error_recovery; + } + + fluid_settings_getnum(synth->settings, "synth.sample-rate", &samplerate); + info.samplerate = samplerate + 0.5; + info.channels = 2; + + /* Search for valid format for given file type, if invalid and no format was specified. + * To handle Ogg/Vorbis and possibly future file types with new formats. + * Checking if format is SF_FORMAT_PCM_16 isn't a fool proof way to check if + * format was specified or not (if user specifies "s16" itself), but should suffice. */ + if(!sf_format_check(&info) + && ((info.format & SF_FORMAT_SUBMASK) != SF_FORMAT_PCM_16 + || !fluid_file_renderer_find_valid_format(&info))) + { + FLUID_LOG(FLUID_ERR, "Invalid or unsupported audio file format settings"); + goto error_recovery; + } + + dev->sndfile = sf_open(filename, SFM_WRITE, &info); + + if(!dev->sndfile) + { + FLUID_LOG(FLUID_ERR, "Failed to open audio file '%s' for writing", filename); + goto error_recovery; + } + + /* Turn on clipping and normalization of floats (-1.0 - 1.0) */ + sf_command(dev->sndfile, SFC_SET_CLIPPING, NULL, SF_TRUE); + sf_command(dev->sndfile, SFC_SET_NORM_FLOAT, NULL, SF_TRUE); + +#else + dev->file = FLUID_FOPEN(filename, "wb"); + + if(dev->file == NULL) + { + FLUID_LOG(FLUID_ERR, "Failed to open the file '%s'", filename); + goto error_recovery; + } + +#endif + + if(audio_channels != 1) + { + FLUID_LOG(FLUID_WARN, "The file-renderer currently only supports a single stereo channel. You have provided %d stereo channels. Audio may sound strange or incomplete.", audio_channels); + } + + FLUID_FREE(filename); + return dev; + +error_recovery: + + FLUID_FREE(filename); + delete_fluid_file_renderer(dev); + return NULL; +} + +/** + * Set vbr encoding quality (only available with libsndfile support) + * @param dev File renderer object. + * @param q The encoding quality, see libsndfile documentation of \c SFC_SET_VBR_ENCODING_QUALITY + * @return #FLUID_OK if the quality has been successfully set, #FLUID_FAILED otherwise + * @since 1.1.7 + */ +int +fluid_file_set_encoding_quality(fluid_file_renderer_t *dev, double q) +{ +#if LIBSNDFILE_SUPPORT + + if(sf_command(dev->sndfile, SFC_SET_VBR_ENCODING_QUALITY, &q, sizeof(double)) == SF_TRUE) + { + return FLUID_OK; + } + else +#endif + { + return FLUID_FAILED; + } +} + +/** + * Close file and destroy a file renderer object. + * @param dev File renderer object. + * @since 1.1.0 + */ +void delete_fluid_file_renderer(fluid_file_renderer_t *dev) +{ + fluid_return_if_fail(dev != NULL); + +#if LIBSNDFILE_SUPPORT + + if(dev->sndfile != NULL) + { + int retval = sf_close(dev->sndfile); + + if(retval != 0) + { + FLUID_LOG(FLUID_WARN, "Error closing audio file: %s", sf_error_number(retval)); + } + } + +#else + + if(dev->file != NULL) + { + fclose(dev->file); + } + +#endif + + FLUID_FREE(dev->buf); + FLUID_FREE(dev); +} + +/** + * Write period_size samples to file. + * @param dev File renderer instance + * @return #FLUID_OK or #FLUID_FAILED if an error occurred + * @since 1.1.0 + */ +int +fluid_file_renderer_process_block(fluid_file_renderer_t *dev) +{ +#if LIBSNDFILE_SUPPORT + int n; + + fluid_synth_write_float(dev->synth, dev->period_size, dev->buf, 0, 2, dev->buf, 1, 2); + + n = sf_writef_float(dev->sndfile, dev->buf, dev->period_size); + + if(n != dev->period_size) + { + FLUID_LOG(FLUID_ERR, "Audio file write error: %s", + sf_strerror(dev->sndfile)); + return FLUID_FAILED; + } + + return FLUID_OK; + +#else /* No libsndfile support */ + + size_t res, nmemb = dev->buf_size; + + fluid_synth_write_s16(dev->synth, dev->period_size, dev->buf, 0, 2, dev->buf, 1, 2); + + res = fwrite(dev->buf, 1, nmemb, dev->file); + + if(res < nmemb) + { + FLUID_LOG(FLUID_ERR, "Audio output file write error: %s", + strerror(errno)); + return FLUID_FAILED; + } + + return FLUID_OK; +#endif +} + + +#if LIBSNDFILE_SUPPORT + +/** + * Parse a colon separated format string and configure an SF_INFO structure accordingly. + * @param filetype File type string (NULL or "auto" to attempt to identify format + * by filename extension, with fallback to "wav") + * @param format File audio format string or NULL to use "s16" + * @param endian File endian string or NULL to use "auto" which uses the file type's + * default endian byte order. + * @param filename File name (used by "auto" type to determine type, based on extension) + * @param info Audio file info structure to configure + * @return TRUE on success, FALSE otherwise + */ +static int +fluid_file_renderer_parse_options(char *filetype, char *format, char *endian, + char *filename, SF_INFO *info) +{ + int type = -1; /* -1 indicates "auto" type */ + char *s; + unsigned int i; + + /* If "auto" type, then use extension to search for a match */ + if(!filetype || FLUID_STRCMP(filetype, "auto") == 0) + { + type = FLUID_FILE_RENDERER_DEFAULT_FILE_TYPE; + s = FLUID_STRRCHR(filename, '.'); + + if(s && s[1] != '\0') + { + if(!fluid_file_renderer_find_file_type(s + 1, &type)) + { + FLUID_LOG(FLUID_WARN, "Failed to determine audio file type from filename, defaulting to WAV"); + } + } + } + else if(!fluid_file_renderer_find_file_type(filetype, &type)) + { + FLUID_LOG(FLUID_ERR, "Invalid or unsupported audio file type '%s'", filetype); + return FALSE; + } + + + info->format = (info->format & ~SF_FORMAT_TYPEMASK) | type; + + /* Look for subtype */ + if(format) + { + for(i = 0; i < FLUID_N_ELEMENTS(format_names); i++) + { + if(FLUID_STRCMP(format, format_names[i]) == 0) + { + break; + } + } + + if(i >= FLUID_N_ELEMENTS(format_names)) + { + FLUID_LOG(FLUID_ERR, "Invalid or unsupported file audio format '%s'", format); + return FALSE; + } + + info->format = (info->format & ~SF_FORMAT_SUBMASK) | format_ids[i]; + } + +#if LIBSNDFILE_HASVORBIS + + /* Force subformat to vorbis as nothing else would make sense currently */ + if((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) + { + info->format = (info->format & ~SF_FORMAT_SUBMASK) | SF_FORMAT_VORBIS; + } + +#endif + + /* Look for endian */ + if(endian) + { + for(i = 0; i < FLUID_N_ELEMENTS(endian_names); i++) + { + if(FLUID_STRCMP(endian, endian_names[i]) == 0) + { + break; + } + } + + if(i >= FLUID_N_ELEMENTS(endian_names)) + { + FLUID_LOG(FLUID_ERR, "Invalid or unsupported endian byte order '%s'", endian); + return FALSE; + } + + info->format = (info->format & ~SF_FORMAT_ENDMASK) | endian_ids[i]; + } + + return TRUE; +} + +/** + * Searches for a supported libsndfile file type by extension. + * @param extension The extension string + * @param type Location to store the type (unmodified if not found) + * @return TRUE if found, FALSE otherwise + */ +static int +fluid_file_renderer_find_file_type(char *extension, int *type) +{ + SF_FORMAT_INFO finfo; + int major_count; + int i; + + sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof(int)); + + for(i = 0; i < major_count; i++) + { + finfo.format = i; + sf_command(NULL, SFC_GET_FORMAT_MAJOR, &finfo, sizeof(finfo)); + + if(FLUID_STRCMP(extension, finfo.extension) == 0) + { + break; + } + } + + if(i < major_count) + { + *type = finfo.format; + return TRUE; + } + + return FALSE; +} + +/* Search for a valid audio format for a given file type */ +static int +fluid_file_renderer_find_valid_format(SF_INFO *info) +{ + SF_FORMAT_INFO format_info; + int count, i; + + sf_command(NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof(int)); + + for(i = 0; i < count; i++) + { + format_info.format = i; + + sf_command(NULL, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof(format_info)); + + info->format = (info->format & ~SF_FORMAT_SUBMASK) | format_info.format; + + if(sf_format_check(info)) + { + return TRUE; + } + } + + return FALSE; +} + +#endif diff --git a/thirdparty/fluidsynth/src/bindings/fluid_ladspa.c b/thirdparty/fluidsynth/src/bindings/fluid_ladspa.c new file mode 100644 index 000000000..c33d1d56b --- /dev/null +++ b/thirdparty/fluidsynth/src/bindings/fluid_ladspa.c @@ -0,0 +1,1882 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* This module: 3/2002 + * Author: Markus Nentwig, nentwig@users.sourceforge.net + * + * Complete rewrite: 10/2017 + * Author: Marcus Weseloh + */ + +#include "fluid_ladspa.h" +#include "fluid_list.h" + +#if defined(LADSPA) || defined(__DOXYGEN__) + +#include +#include + + +typedef enum _fluid_ladspa_state_t +{ + FLUID_LADSPA_INACTIVE = 0, + FLUID_LADSPA_ACTIVE, + FLUID_LADSPA_RUNNING + +} fluid_ladspa_state_t; + +typedef enum _fluid_ladspa_dir_t +{ + FLUID_LADSPA_INPUT, + FLUID_LADSPA_OUTPUT, + +} fluid_ladspa_dir_t; + +typedef enum _fluid_ladspa_node_type_t +{ + FLUID_LADSPA_NODE_AUDIO = 1, + FLUID_LADSPA_NODE_CONTROL = 2, + FLUID_LADSPA_NODE_EFFECT = 4, + FLUID_LADSPA_NODE_HOST = 8, + FLUID_LADSPA_NODE_USER = 16, + +} fluid_ladspa_node_type_t; + +typedef struct _fluid_ladspa_node_t +{ + char *name; + fluid_ladspa_node_type_t type; + + /* The buffer that LADSPA effects read and/or write to. + * If FluidSynth has been compiled WITH_FLOAT, then this + * points to the host buffer, otherwise it's a separate + * buffer. */ + LADSPA_Data *effect_buffer; + + /* Only set for host nodes, points to the host buffer */ + fluid_real_t *host_buffer; + + int num_inputs; + int num_outputs; + +} fluid_ladspa_node_t; + +typedef struct _fluid_ladspa_effect_t +{ + char *name; + + /* Pointer to the library that this effect was loaded from */ + fluid_module_t *lib; + + /* The descriptor defines the plugin implementation, the + * handle points to an instance of that plugin */ + const LADSPA_Descriptor *desc; + LADSPA_Handle *handle; + + int active; + + /* Decides if we should call the run (mix = 0) or run_adding (mix = 1) + * plugin interface */ + int mix; + + /* Used to keep track of the port connection state */ + fluid_ladspa_node_t **port_nodes; + +} fluid_ladspa_effect_t; + +struct _fluid_ladspa_fx_t +{ + unsigned long sample_rate; + + /* The buffer size for all audio buffers (in samples) */ + int buffer_size; + + fluid_list_t *host_nodes; + + fluid_list_t *user_nodes; + + fluid_list_t *effects; + + fluid_rec_mutex_t api_mutex; + + fluid_atomic_int_t state; + int pending_deactivation; + + fluid_cond_mutex_t *run_finished_mutex; + fluid_cond_t *run_finished_cond; + +}; + +#define LADSPA_API_ENTER(_fx) (fluid_rec_mutex_lock((_fx)->api_mutex)) + +#define LADSPA_API_RETURN(_fx, _ret) \ + fluid_rec_mutex_unlock((_fx)->api_mutex); \ + return (_ret) + +static void clear_ladspa(fluid_ladspa_fx_t *fx); + +/* Node helpers */ +static fluid_ladspa_node_t *new_fluid_ladspa_node(fluid_ladspa_fx_t *fx, const char *name, + fluid_ladspa_node_type_t type, fluid_real_t *host_buffer); +static void delete_fluid_ladspa_node(fluid_ladspa_node_t *node); +static fluid_ladspa_node_t *get_node(fluid_ladspa_fx_t *fx, const char *name); + +/* Effect helpers */ +static fluid_ladspa_effect_t * +new_fluid_ladspa_effect(fluid_ladspa_fx_t *fx, const char *lib_name, const char *plugin_name); +static void delete_fluid_ladspa_effect(fluid_ladspa_effect_t *effect); +static void activate_effect(fluid_ladspa_effect_t *effect); +static void deactivate_effect(fluid_ladspa_effect_t *effect); +static fluid_ladspa_effect_t *get_effect(fluid_ladspa_fx_t *fx, const char *name); +static int get_effect_port_idx(const fluid_ladspa_effect_t *effect, const char *name); +static LADSPA_Data get_default_port_value(fluid_ladspa_effect_t *effect, unsigned int port_idx, + int sample_rate); +static void connect_node_to_port(fluid_ladspa_node_t *node, fluid_ladspa_dir_t dir, + fluid_ladspa_effect_t *effect, int port_idx); +static int create_control_port_nodes(fluid_ladspa_fx_t *fx, fluid_ladspa_effect_t *effect); + +/* Plugin helpers */ +static const LADSPA_Descriptor *get_plugin_descriptor(fluid_module_t *lib, const char *name); + +/* Sanity checks */ +static int check_all_ports_connected(fluid_ladspa_effect_t *effect, const char **name); +static int check_no_inplace_broken(fluid_ladspa_effect_t *effect, const char **name1, const char **name2); +static int check_host_output_used(fluid_ladspa_fx_t *fx); +static int check_all_audio_nodes_connected(fluid_ladspa_fx_t *fx, const char **name); + +#ifndef WITH_FLOAT +static FLUID_INLINE void copy_host_to_effect_buffers(fluid_ladspa_fx_t *fx, int num_samples); +static FLUID_INLINE void copy_effect_to_host_buffers(fluid_ladspa_fx_t *fx, int num_samples); +#endif + +/** + * Creates a new LADSPA effects unit. + * + * @param sample_rate sample_rate for the LADSPA effects + * @param buffer_size size of all audio buffers + * @return pointer to the new LADSPA effects unit + */ +fluid_ladspa_fx_t *new_fluid_ladspa_fx(fluid_real_t sample_rate, int buffer_size) +{ + fluid_ladspa_fx_t *fx; + + fx = FLUID_NEW(fluid_ladspa_fx_t); + + if(fx == NULL) + { + return NULL; + } + + FLUID_MEMSET(fx, 0, sizeof(fluid_ladspa_fx_t)); + + /* Setup recursive mutex to protect access to public API */ + fluid_rec_mutex_init(fx->api_mutex); + + fluid_atomic_int_set(&fx->state, FLUID_LADSPA_INACTIVE); + fx->buffer_size = buffer_size; + + /* add 0.5 to minimize overall casting error */ + fx->sample_rate = (unsigned long)(sample_rate + 0.5); + + /* Setup mutex and cond used to signal that fluid_ladspa_run has finished */ + fx->run_finished_mutex = new_fluid_cond_mutex(); + + if(fx->run_finished_mutex == NULL) + { + goto error_recovery; + } + + fx->run_finished_cond = new_fluid_cond(); + + if(fx->run_finished_cond == NULL) + { + goto error_recovery; + } + + return fx; + +error_recovery: + delete_fluid_ladspa_fx(fx); + return NULL; +} + +/** + * Destroy and free a LADSPA effects unit previously created + * with new_fluid_ladspa_fx(). + * + * @param fx LADSPA effects instance + * + * @note This function does not check the engine state for + * possible users, so make sure that you only call this + * if you are sure nobody is using the engine anymore (especially + * that nobody calls fluid_ladspa_run) + * + */ +void delete_fluid_ladspa_fx(fluid_ladspa_fx_t *fx) +{ + fluid_list_t *list; + fluid_ladspa_node_t *node; + + fluid_return_if_fail(fx != NULL); + + clear_ladspa(fx); + + /* Delete the remaining host nodes */ + for(list = fx->host_nodes; list; list = fluid_list_next(list)) + { + node = (fluid_ladspa_node_t *) fluid_list_get(list); + delete_fluid_ladspa_node(node); + } + delete_fluid_list(fx->host_nodes); + + if(fx->run_finished_cond != NULL) + { + delete_fluid_cond(fx->run_finished_cond); + } + + if(fx->run_finished_mutex != NULL) + { + delete_fluid_cond_mutex(fx->run_finished_mutex); + } + + fluid_rec_mutex_destroy(fx->api_mutex); + + FLUID_FREE(fx); +} + +/** + * Add host buffers to the LADSPA engine. + * + * @param fx LADSPA fx instance + * @param prefix common name prefix for the created nodes + * @param num_buffers number of of buffers buffer array + * @param buffers array of sample buffers + * @param buf_stride number of samples contained in one buffer + * @return FLUID_OK on success, otherwise FLUID_FAILED + * + * @note The size of the buffers pointed to by the buffers array must be + * at least as large as the buffer size given to new_fluid_ladspa_fx. + * + */ +int fluid_ladspa_add_host_ports(fluid_ladspa_fx_t *fx, const char *prefix, + int num_buffers, fluid_real_t buffers[], int buf_stride) +{ + int i; + char name[99]; + fluid_ladspa_node_t *node; + + LADSPA_API_ENTER(fx); + + if(fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + /* Create nodes for all channels */ + for(i = 0; i < num_buffers; i++) + { + /* If there is more than one buffer, then append a 1-based index to each node name */ + if(num_buffers > 1) + { + FLUID_SNPRINTF(name, sizeof(name), "%s%d", prefix, (i + 1)); + } + else + { + FLUID_STRNCPY(name, prefix, sizeof(name)); + } + + node = new_fluid_ladspa_node(fx, name, + FLUID_LADSPA_NODE_AUDIO | FLUID_LADSPA_NODE_HOST, + &buffers[i * buf_stride]); + if (node == NULL) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + fx->host_nodes = fluid_list_append(fx->host_nodes, node); + } + + LADSPA_API_RETURN(fx, FLUID_OK); +} + + +/** + * Set the sample rate of the LADSPA effects. + * + * @param fx LADSPA fx instance + * @param sample_rate new sample rate + * @return FLUID_OK on success, otherwise FLUID_FAILED + * + * Resets the LADSPA effects if the sample rate is different from the + * previous sample rate. + * + */ +int fluid_ladspa_set_sample_rate(fluid_ladspa_fx_t *fx, fluid_real_t sample_rate) +{ + unsigned long new_sample_rate; + + LADSPA_API_ENTER(fx); + + /* Add 0.5 to minimize rounding errors */ + new_sample_rate = (unsigned long)(sample_rate + 0.5); + + if(fx->sample_rate == new_sample_rate) + { + LADSPA_API_RETURN(fx, FLUID_OK); + } + + if(fluid_ladspa_is_active(fx)) + { + if(fluid_ladspa_reset(fx) != FLUID_OK) + { + FLUID_LOG(FLUID_ERR, "Failed to reset LADSPA, unable to change sample rate"); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + } + + fx->sample_rate = new_sample_rate; + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Check if the LADSPA engine is currently used to render audio + * + * @param fx LADSPA fx instance + * @return TRUE if LADSPA effects engine is active, otherwise FALSE + * + * If an engine is active, the only allowed user actions are deactivation or + * setting values of control ports on effects. Anything else, especially + * adding or removing effects, buffers or ports, is only allowed in deactivated + * state. + */ +int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx) +{ + int is_active; + + fluid_return_val_if_fail(fx != NULL, FALSE); + + LADSPA_API_ENTER(fx); + + is_active = (fluid_atomic_int_get(&fx->state) != FLUID_LADSPA_INACTIVE); + + LADSPA_API_RETURN(fx, is_active); +} + +/** + * Activate the LADSPA fx instance and each configured effect. + * + * @param fx LADSPA fx instance + * @return FLUID_OK if activation succeeded or already active, otherwise FLUID_FAILED + */ +int fluid_ladspa_activate(fluid_ladspa_fx_t *fx) +{ + fluid_list_t *list; + fluid_ladspa_effect_t *effect; + + fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); + + LADSPA_API_ENTER(fx); + + if(fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + if(fluid_ladspa_check(fx, NULL, 0) != FLUID_OK) + { + FLUID_LOG(FLUID_ERR, "LADSPA check failed, unable to activate effects"); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + for(list = fx->effects; list; list = fluid_list_next(list)) + { + effect = (fluid_ladspa_effect_t *) fluid_list_get(list); + activate_effect(effect); + } + + if(!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_INACTIVE, FLUID_LADSPA_ACTIVE)) + { + for(list = fx->effects; list; list = fluid_list_next(list)) + { + effect = (fluid_ladspa_effect_t *) fluid_list_get(list); + deactivate_effect(effect); + } + + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Deactivate a LADSPA fx instance and all configured effects. + * + * @param fx LADSPA fx instance + * @return FLUID_OK if deactivation succeeded, otherwise FLUID_FAILED + * + * @note This function may sleep. + */ +int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx) +{ + fluid_list_t *list; + fluid_ladspa_effect_t *effect; + + fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); + + LADSPA_API_ENTER(fx); + + /* If we are already inactive, then simply return success */ + if(fluid_atomic_int_get(&fx->state) == FLUID_LADSPA_INACTIVE) + { + LADSPA_API_RETURN(fx, FLUID_OK); + } + + /* Notify fluid_ladspa_run that we would like to deactivate and that it should + * send us a signal when its done if it is currently running */ + fx->pending_deactivation = 1; + + fluid_cond_mutex_lock(fx->run_finished_mutex); + + while(!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_ACTIVE, FLUID_LADSPA_INACTIVE)) + { + fluid_cond_wait(fx->run_finished_cond, fx->run_finished_mutex); + } + + fluid_cond_mutex_unlock(fx->run_finished_mutex); + + /* Now that we're inactive, deactivate all effects and return success */ + for(list = fx->effects; list; list = fluid_list_next(list)) + { + effect = (fluid_ladspa_effect_t *) fluid_list_get(list); + deactivate_effect(effect); + } + + fx->pending_deactivation = 0; + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Reset the LADSPA effects engine. + * + * @param fx LADSPA fx instance + * @return FLUID_OK on success, otherwise FLUID_FAILED + * + * Deactivate LADSPA if currently active, remove all + * effects, remove all user nodes and unload all libraries. + */ +int fluid_ladspa_reset(fluid_ladspa_fx_t *fx) +{ + fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); + + LADSPA_API_ENTER(fx); + + if(fluid_ladspa_is_active(fx)) + { + if(fluid_ladspa_deactivate(fx) != FLUID_OK) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + } + + clear_ladspa(fx); + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Processes audio data via the LADSPA effects unit. + * + * @param fx LADSPA effects instance + * @param block_count number of blocks to render + * @param block_size number of samples in a block + * + * FluidSynth calls this function during main output mixing, + * just before processing the internal reverb and chorus effects. + * + * It copies audio data from the supplied buffers, runs all effects and copies the + * resulting audio back into the same buffers. + */ +void fluid_ladspa_run(fluid_ladspa_fx_t *fx, int block_count, int block_size) +{ + int num_samples; + fluid_list_t *list; + fluid_ladspa_node_t *node; + fluid_ladspa_effect_t *effect; + + /* Somebody wants to deactivate the engine, so let's give them a chance to do that. + * And check that there is at least one effect, to avoid the overhead of the + * atomic compare and exchange on an unconfigured LADSPA engine. */ + if(fx->pending_deactivation || fx->effects == NULL) + { + return; + } + + /* Inform the engine that we are now running pluings, and bail out if it's not active */ + if(!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_ACTIVE, FLUID_LADSPA_RUNNING)) + { + return; + } + + num_samples = block_count * block_size; + +#ifndef WITH_FLOAT + copy_host_to_effect_buffers(fx, num_samples); +#endif + + for(list = fx->user_nodes; list; list = fluid_list_next(list)) + { + node = (fluid_ladspa_node_t *) fluid_list_get(list); + + FLUID_MEMSET(node->effect_buffer, 0, fx->buffer_size * sizeof(LADSPA_Data)); + } + + /* Run each effect in the order that they were added */ + for(list = fx->effects; list; list = fluid_list_next(list)) + { + effect = (fluid_ladspa_effect_t *) fluid_list_get(list); + + if(effect->mix) + { + effect->desc->run_adding(effect->handle, num_samples); + } + else + { + effect->desc->run(effect->handle, num_samples); + } + } + +#ifndef WITH_FLOAT + copy_effect_to_host_buffers(fx, num_samples); +#endif + + if(!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_RUNNING, FLUID_LADSPA_ACTIVE)) + { + FLUID_LOG(FLUID_ERR, "Unable to reset LADSPA running state!"); + } + + /* If deactivation was requested while in running state, notify that we've finished now + * and deactivation can proceed */ + if(fx->pending_deactivation) + { + fluid_cond_mutex_lock(fx->run_finished_mutex); + fluid_cond_broadcast(fx->run_finished_cond); + fluid_cond_mutex_unlock(fx->run_finished_mutex); + } +} + +/** + * Check if the effect plugin supports the run_adding and set_run_adding_gain + * interfaces necessary for output mixing + * + * @param fx LADSPA fx + * @param name the name of the effect + * @return TRUE if mix mode is supported, otherwise FALSE + */ +int fluid_ladspa_effect_can_mix(fluid_ladspa_fx_t *fx, const char *name) +{ + int can_mix; + fluid_ladspa_effect_t *effect; + + fluid_return_val_if_fail(fx != NULL, FALSE); + fluid_return_val_if_fail(name != NULL, FALSE); + + LADSPA_API_ENTER(fx); + + effect = get_effect(fx, name); + + if(effect == NULL) + { + LADSPA_API_RETURN(fx, FALSE); + } + + can_mix = (effect->desc->run_adding != NULL + && effect->desc->set_run_adding_gain != NULL); + + LADSPA_API_RETURN(fx, can_mix); +} + +/** + * Set if the effect should replace everything in the output buffers (mix = 0, default) + * or add to the buffers with a fixed gain (mix = 1). + * + * @param fx LADSPA fx instance + * @param name the name of the effect + * @param mix (boolean) if to enable mix mode + * @param gain the gain to apply to the effect output before adding to output. + * @return FLUID_OK on success, otherwise FLUID_FAILED + */ +int fluid_ladspa_effect_set_mix(fluid_ladspa_fx_t *fx, const char *name, int mix, float gain) +{ + fluid_ladspa_effect_t *effect; + + fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + + LADSPA_API_ENTER(fx); + + effect = get_effect(fx, name); + + if(effect == NULL) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + if(mix) + { + if(!fluid_ladspa_effect_can_mix(fx, name)) + { + FLUID_LOG(FLUID_ERR, "Effect '%s' does not support mix mode", name); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + effect->desc->set_run_adding_gain(effect->handle, gain); + } + + effect->mix = mix; + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +static void clear_ladspa(fluid_ladspa_fx_t *fx) +{ + fluid_list_t *list; + fluid_ladspa_node_t *node; + fluid_ladspa_effect_t *effect; + + /* Deactivate and free all effects */ + for(list = fx->effects; list; list = fluid_list_next(list)) + { + effect = (fluid_ladspa_effect_t *) fluid_list_get(list); + + deactivate_effect(effect); + delete_fluid_ladspa_effect(effect); + } + delete_fluid_list(fx->effects); + fx->effects = NULL; + + /* Delete all user nodes */ + for(list = fx->user_nodes; list; list = fluid_list_next(list)) + { + node = (fluid_ladspa_node_t *) fluid_list_get(list); + + delete_fluid_ladspa_node(node); + } + delete_fluid_list(fx->user_nodes); + fx->user_nodes = NULL; + + /* Mark all host nodes as unconnected */ + for(list = fx->host_nodes; list; list = fluid_list_next(list)) + { + node = (fluid_ladspa_node_t *) fluid_list_get(list); + + node->num_inputs = 0; + node->num_outputs = 0; + } +} + +/** + * Check if a named host port exists + * + * @param fx LADSPA fx instance + * @param name the port name + * @return TRUE if the host port exists, otherwise FALSE + */ +int fluid_ladspa_host_port_exists(fluid_ladspa_fx_t *fx, const char *name) +{ + fluid_ladspa_node_t *node; + + fluid_return_val_if_fail(fx != NULL, FALSE); + fluid_return_val_if_fail(name != NULL, FALSE); + + LADSPA_API_ENTER(fx); + + node = get_node(fx, name); + + if(node == NULL) + { + LADSPA_API_RETURN(fx, FALSE); + } + + if(node->type & FLUID_LADSPA_NODE_HOST) + { + LADSPA_API_RETURN(fx, TRUE); + } + + LADSPA_API_RETURN(fx, FALSE); +} + +/** + * Check if a named user buffer exists + * + * @param fx LADSPA fx instance + * @param name the buffer name + * @return TRUE if the buffer exists, otherwise FALSE + */ +int fluid_ladspa_buffer_exists(fluid_ladspa_fx_t *fx, const char *name) +{ + int exists; + fluid_ladspa_node_t *node; + + fluid_return_val_if_fail(fx != NULL, FALSE); + fluid_return_val_if_fail(name != NULL, FALSE); + + LADSPA_API_ENTER(fx); + + node = get_node(fx, name); + + if(node == NULL) + { + LADSPA_API_RETURN(fx, FALSE); + } + + exists = ((node->type & FLUID_LADSPA_NODE_AUDIO) && + (node->type & FLUID_LADSPA_NODE_USER)); + + LADSPA_API_RETURN(fx, exists); +} + +/** + * Check if the named port exists on an effect + * + * @param fx LADSPA fx instance + * @param effect_name name of the effect + * @param port_name the port name + * @return TRUE if port was found, otherwise FALSE + */ +int fluid_ladspa_effect_port_exists(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *port_name) +{ + fluid_ladspa_effect_t *effect; + int port_exists; + + fluid_return_val_if_fail(fx != NULL, FALSE); + fluid_return_val_if_fail(effect_name != NULL, FALSE); + fluid_return_val_if_fail(port_name != NULL, FALSE); + + LADSPA_API_ENTER(fx); + + effect = get_effect(fx, effect_name); + + if(effect == NULL) + { + LADSPA_API_RETURN(fx, FALSE); + } + + port_exists = (get_effect_port_idx(effect, port_name) != -1); + + LADSPA_API_RETURN(fx, port_exists); +} + +/** + * Create and add a new audio buffer. + * + * @param fx LADSPA effects instance + * @param name name of the new buffer + * @return FLUID_OK on success, FLUID_FAILED on error + */ +int fluid_ladspa_add_buffer(fluid_ladspa_fx_t *fx, const char *name) +{ + fluid_ladspa_node_t *node; + + fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + + LADSPA_API_ENTER(fx); + + if(fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + node = new_fluid_ladspa_node(fx, name, + FLUID_LADSPA_NODE_AUDIO | FLUID_LADSPA_NODE_USER, NULL); + + if(node == NULL) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + fx->user_nodes = fluid_list_append(fx->user_nodes, node); + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Set the value of an effect control port + * + * @param fx LADSPA fx instance + * @param effect_name name of the effect + * @param port_name name of the port + * @param val floating point value + * @return FLUID_OK on success, FLUID_FAILED on error + */ +int fluid_ladspa_effect_set_control(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *port_name, float val) +{ + fluid_ladspa_node_t *node; + fluid_ladspa_effect_t *effect; + int port_idx; + + fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); + fluid_return_val_if_fail(effect_name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(port_name != NULL, FLUID_FAILED); + + LADSPA_API_ENTER(fx); + + effect = get_effect(fx, effect_name); + + if(effect == NULL) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + port_idx = get_effect_port_idx(effect, port_name); + + if(port_idx < 0) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + if(!LADSPA_IS_PORT_CONTROL(effect->desc->PortDescriptors[port_idx])) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + node = effect->port_nodes[port_idx]; + + if(node == NULL) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + node->effect_buffer[0] = val; + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Create an instance of a LADSPA plugin as an effect + * + * @param fx LADSPA effects instance + * @param effect_name name of the effect + * @param lib_name filename of ladspa plugin library + * @param plugin_name optional, plugin name if there is more than one plugin in the library + * @return FLUID_OK on success, otherwise FLUID_FAILED + */ +int fluid_ladspa_add_effect(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *lib_name, const char *plugin_name) +{ + fluid_ladspa_effect_t *effect; + + fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); + fluid_return_val_if_fail(effect_name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(lib_name != NULL, FLUID_FAILED); + + LADSPA_API_ENTER(fx); + + if(fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + effect = new_fluid_ladspa_effect(fx, lib_name, plugin_name); + + if(effect == NULL) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + effect->name = FLUID_STRDUP(effect_name); + + if(effect->name == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + delete_fluid_ladspa_effect(effect); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + if(create_control_port_nodes(fx, effect) != FLUID_OK) + { + delete_fluid_ladspa_effect(effect); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + fx->effects = fluid_list_append(fx->effects, effect); + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Connect an effect audio port to a host port or buffer + * + * @param fx LADSPA effects instance + * @param effect_name name of the effect + * @param port_name the audio port name to connect to (case-insensitive prefix match) + * @param name the host port or buffer to connect to (case-insensitive) + * @return FLUID_OK on success, otherwise FLUID_FAILED + * + * @note There is no corresponding disconnect function. If the connections need to be changed, + * clear everything with fluid_ladspa_reset and start again from scratch. + */ +int fluid_ladspa_effect_link(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *port_name, const char *name) +{ + fluid_ladspa_effect_t *effect; + fluid_ladspa_node_t *node; + int port_idx; + int port_flags; + int dir; + + fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); + fluid_return_val_if_fail(effect_name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(port_name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + + LADSPA_API_ENTER(fx); + + if(fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + effect = get_effect(fx, effect_name); + + if(effect == NULL) + { + FLUID_LOG(FLUID_ERR, "Effect '%s' not found", effect_name); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + port_idx = get_effect_port_idx(effect, port_name); + + if(port_idx < 0) + { + FLUID_LOG(FLUID_ERR, "Port '%s' not found on effect '%s'", port_name, effect_name); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + port_flags = effect->desc->PortDescriptors[port_idx]; + + if(!LADSPA_IS_PORT_AUDIO(port_flags)) + { + FLUID_LOG(FLUID_ERR, "Only audio effect ports can be linked to buffers or host ports"); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + node = get_node(fx, name); + + if(node == NULL) + { + FLUID_LOG(FLUID_ERR, "Link target '%s' not found", name); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + if(!(node->type & FLUID_LADSPA_NODE_AUDIO)) + { + FLUID_LOG(FLUID_ERR, "Link target '%s' needs to be an audio port or buffer", name); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + if(LADSPA_IS_PORT_INPUT(port_flags)) + { + dir = FLUID_LADSPA_INPUT; + } + else + { + dir = FLUID_LADSPA_OUTPUT; + } + + connect_node_to_port(node, dir, effect, port_idx); + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Do a sanity check for problems in the LADSPA setup + * + * @param fx LADSPA fx instance + * @param err externally provided buffer for the error message. Set to NULL if you don't need an error message. + * @param err_size size of the err buffer + * @return FLUID_OK if setup is valid, otherwise FLUID_FAILED (err will contain the error message) + * + * If the check detects problems and the err pointer is not NULL, a description of the first found + * problem is written to that string (up to err_size - 1 characters). + */ +int fluid_ladspa_check(fluid_ladspa_fx_t *fx, char *err, int err_size) +{ + const char *str; + const char *str2; + fluid_list_t *list; + fluid_ladspa_effect_t *effect; + + fluid_return_val_if_fail(fx != NULL, FLUID_FAILED); + fluid_return_val_if_fail(err == NULL || err_size >= 0, FLUID_FAILED); + + LADSPA_API_ENTER(fx); + + /* Check that there is at least one effect */ + if(fx->effects == NULL) + { + FLUID_SNPRINTF(err, err_size, "No effects configured\n"); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + for(list = fx->effects; list; list = fluid_list_next(list)) + { + effect = (fluid_ladspa_effect_t *) fluid_list_get(list); + + if(check_all_ports_connected(effect, &str) == FLUID_FAILED) + { + if(err != NULL) + { + FLUID_SNPRINTF(err, err_size, "Port '%s' on effect '%s' is not connected\n", + str, effect->name); + } + + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + if(check_no_inplace_broken(effect, &str, &str2) == FLUID_FAILED) + { + if(err != NULL) + { + FLUID_SNPRINTF(err, err_size, + "effect '%s' is in-place broken, '%s' and '%s' are not allowed " + "to connect to the same node\n", effect->name, str, str2); + } + + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + } + + if(check_host_output_used(fx) == FLUID_FAILED) + { + if(err != NULL) + { + FLUID_SNPRINTF(err, err_size, "No effect outputs to one the host nodes\n"); + } + + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + if(check_all_audio_nodes_connected(fx, &str) == FLUID_FAILED) + { + if(err != NULL) + { + FLUID_SNPRINTF(err, err_size, "Audio node '%s' is not fully connected\n", str); + } + + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + LADSPA_API_RETURN(fx, FLUID_OK); +} + + +static void activate_effect(fluid_ladspa_effect_t *effect) +{ + if(!effect->active) + { + effect->active = TRUE; + + if(effect->desc->activate != NULL) + { + effect->desc->activate(effect->handle); + } + } +} + +static void deactivate_effect(fluid_ladspa_effect_t *effect) +{ + if(effect->active) + { + effect->active = FALSE; + + if(effect->desc->deactivate != NULL) + { + effect->desc->deactivate(effect->handle); + } + } +} + +/** + * Return a LADSPA node by name. Nodes are searched case insensitive. + * + * @param fx LADSPA fx instance + * @param name the node name string + * @return a fluid_ladspa_node_t pointer or NULL if not found + */ +static fluid_ladspa_node_t *get_node(fluid_ladspa_fx_t *fx, const char *name) +{ + fluid_list_t *list; + fluid_ladspa_node_t *node; + + for(list = fx->host_nodes; list; list = fluid_list_next(list)) + { + node = (fluid_ladspa_node_t *) fluid_list_get(list); + + if(FLUID_STRCASECMP(node->name, name) == 0) + { + return node; + } + } + + for(list = fx->user_nodes; list; list = fluid_list_next(list)) + { + node = (fluid_ladspa_node_t *) fluid_list_get(list); + + if(FLUID_STRCASECMP(node->name, name) == 0) + { + return node; + } + } + + return NULL; +} + +/** + * Return a LADSPA effect port index by name, using a 'fuzzy match'. + * + * @param effect pointer to fluid_ladspa_effect_t + * @param name the port name + * @return index of the port in the effect or -1 on error + * + * Returns the first effect port which matches the name. If no exact match is + * found, returns the port that starts with the specified name, but only if there is + * only one such match. + */ +static int get_effect_port_idx(const fluid_ladspa_effect_t *effect, const char *name) +{ + unsigned int i; + int port = -1; + + for(i = 0; i < effect->desc->PortCount; i++) + { + if(FLUID_STRNCASECMP(effect->desc->PortNames[i], name, FLUID_STRLEN(name)) == 0) + { + /* exact match, return immediately */ + if(FLUID_STRLEN(effect->desc->PortNames[i]) == FLUID_STRLEN(name)) + { + return i; + } + + /* more than one prefix match should be treated as not found */ + if(port != -1) + { + return -1; + } + + port = i; + } + } + + return port; +} + +/** + * Return a LADSPA descriptor structure for a plugin in a LADSPA library. + * + * @param lib pointer to the dynamically loaded library + * @param name name (LADSPA Label) of the plugin + * @return pointer to LADSPA_Descriptor, NULL on error or if not found + * + * The name is optional if the library contains only one plugin. + */ +static const LADSPA_Descriptor *get_plugin_descriptor(fluid_module_t *lib, const char *name) +{ + const LADSPA_Descriptor *desc; + const LADSPA_Descriptor *last_desc = NULL; + LADSPA_Descriptor_Function ladspa_descriptor; + int i; + + if(!fluid_module_symbol(lib, "ladspa_descriptor", (void *)&ladspa_descriptor)) + { + FLUID_LOG(FLUID_ERR, "Unable to find ladspa_descriptor in '%s'. " + "Is this really a LADSPA plugin?", fluid_module_name(lib)); + return NULL; + } + + for(i = 0; /* endless */; i++) + { + desc = ladspa_descriptor(i); + + if(desc == NULL) + { + break; + } + + if(name != NULL && FLUID_STRCMP(desc->Label, name) == 0) + { + return desc; + } + + last_desc = desc; + } + + if(name == NULL) + { + if(i == 1) + { + return last_desc; + } + + FLUID_LOG(FLUID_ERR, "Library contains more than one plugin, please specify " + "the plugin label"); + } + + return NULL; +} + +/** + * Instantiate a LADSPA plugin from a library and set up the associated + * control structures needed by the LADSPA fx engine. + * + * @param fx LADSPA fx instance + * @param lib_name file path of the plugin library + * @param plugin_name (optional) string name of the plugin (the LADSPA Label) + * @return pointer to the new ladspa_plugin_t structure or NULL on error + * + * If the library contains only one plugin, then the name is optional. + * Plugins are identified by their "Label" in the plugin descriptor structure. + */ +static fluid_ladspa_effect_t * +new_fluid_ladspa_effect(fluid_ladspa_fx_t *fx, const char *lib_name, const char *plugin_name) +{ + fluid_ladspa_effect_t *effect; + + effect = FLUID_NEW(fluid_ladspa_effect_t); + + if(effect == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(effect, 0, sizeof(fluid_ladspa_effect_t)); + + effect->lib = fluid_module_open(lib_name); + + if(effect->lib == NULL) + { + FLUID_LOG(FLUID_ERR, "Unable to load LADSPA library '%s': %s", lib_name, + fluid_module_error()); + delete_fluid_ladspa_effect(effect); + return NULL; + } + + effect->desc = get_plugin_descriptor(effect->lib, plugin_name); + + if(effect->desc == NULL) + { + delete_fluid_ladspa_effect(effect); + return NULL; + } + + effect->handle = effect->desc->instantiate(effect->desc, fx->sample_rate); + + if(effect->handle == NULL) + { + delete_fluid_ladspa_effect(effect); + FLUID_LOG(FLUID_ERR, "Unable to instantiate plugin '%s' from '%s'", plugin_name, lib_name); + return NULL; + } + + effect->port_nodes = FLUID_ARRAY(fluid_ladspa_node_t *, effect->desc->PortCount); + + if(effect->port_nodes == NULL) + { + delete_fluid_ladspa_effect(effect); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(effect->port_nodes, 0, effect->desc->PortCount * sizeof(fluid_ladspa_node_t *)); + + return effect; +} + +static void delete_fluid_ladspa_effect(fluid_ladspa_effect_t *effect) +{ + unsigned int i; + fluid_ladspa_node_t *node; + + fluid_return_if_fail(effect != NULL); + + /* Control nodes are created automatically when the effect is instantiated and + * are private to this effect, so we can safely remove them here. Nodes connected + * to audio ports might be connected to other effects as well, so we simply remove + * any pointers to them from the effect. */ + if(effect->desc != NULL) + { + for(i = 0; i < effect->desc->PortCount; i++) + { + node = (fluid_ladspa_node_t *) effect->port_nodes[i]; + + if(node && node->type & FLUID_LADSPA_NODE_CONTROL) + { + delete_fluid_ladspa_node(node); + } + } + } + + FLUID_FREE(effect->port_nodes); + + if(effect->handle != NULL && effect->desc != NULL && effect->desc->cleanup != NULL) + { + effect->desc->cleanup(effect->handle); + } + + if(effect->lib != NULL) + { + fluid_module_close(effect->lib); + } + + FLUID_FREE(effect->name); + FLUID_FREE(effect); +} + +static fluid_ladspa_node_t *new_fluid_ladspa_node(fluid_ladspa_fx_t *fx, const char *name, + fluid_ladspa_node_type_t type, fluid_real_t *host_buffer) +{ + int buffer_size; + fluid_ladspa_node_t *node; + + /* For named nodes, make sure that the name is unique */ + if(FLUID_STRLEN(name) > 0 && get_node(fx, name) != NULL) + { + return NULL; + } + + node = FLUID_NEW(fluid_ladspa_node_t); + + if(node == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(node, 0, sizeof(fluid_ladspa_node_t)); + + node->name = FLUID_STRDUP(name); + + if(node->name == NULL) + { + delete_fluid_ladspa_node(node); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = type; + node->host_buffer = host_buffer; + + /* host audio nodes need a host buffer set */ + if((type & FLUID_LADSPA_NODE_AUDIO) && (type & FLUID_LADSPA_NODE_HOST)) + { + if(node->host_buffer == NULL) + { + delete_fluid_ladspa_node(node); + return NULL; + } + +#ifdef WITH_FLOAT + /* If the host uses the same floating-point width as LADSPA, then effects + * can work in-place on the host buffer. Otherwise well need a separate + * buffer for type conversion. */ + node->effect_buffer = node->host_buffer; +#endif + } + + if(node->effect_buffer == NULL) + { + /* Control nodes only store a single floating-point value */ + buffer_size = (type & FLUID_LADSPA_NODE_CONTROL) ? 1 : fx->buffer_size; + + node->effect_buffer = FLUID_ARRAY(LADSPA_Data, buffer_size); + + if(node->effect_buffer == NULL) + { + delete_fluid_ladspa_node(node); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(node->effect_buffer, 0, buffer_size * sizeof(LADSPA_Data)); + } + + return node; +} + +static void delete_fluid_ladspa_node(fluid_ladspa_node_t *node) +{ + fluid_return_if_fail(node != NULL); + + /* If effect_buffer the same as host_buffer, then the effect_buffer has been + * provided externally, so don't free */ + if((node->effect_buffer != NULL) && ((void *)node->effect_buffer != (void *)node->host_buffer)) + { + FLUID_FREE(node->effect_buffer); + } + + FLUID_FREE(node->name); + FLUID_FREE(node); +} + +/** + * Retrieve a ladspa_effect_t instance by it's name. + * + * @param fx LADSPA effects instance + * @param name effect name + * @return pointer to effect or NULL if not found + */ +static fluid_ladspa_effect_t *get_effect(fluid_ladspa_fx_t *fx, const char *name) +{ + fluid_list_t *list; + fluid_ladspa_effect_t *effect; + + LADSPA_API_ENTER(fx); + + for(list = fx->effects; list; list = fluid_list_next(list)) + { + effect = (fluid_ladspa_effect_t *) fluid_list_get(list); + + if(FLUID_STRNCASECMP(effect->name, name, FLUID_STRLEN(name)) == 0) + { + LADSPA_API_RETURN(fx, effect); + } + } + + LADSPA_API_RETURN(fx, NULL); +} + +/** + * Set the passed in float pointer to the default value of a effect port, as specified + * by the LADSPA port hints. If no default hints are found or the port is not a control + * node, it returns 0.0f; + * + * The sample rate is needed because some LADSPA port default hints are expressed as a + * fraction of the current sample rate. + * + * @param effect pointer to effect instance + * @param port_idx index of the port in the effect + * @param sample_rate the current sample rate of the LADSPA fx + * @return default port value or 0.0f + */ +static LADSPA_Data get_default_port_value(fluid_ladspa_effect_t *effect, unsigned int port_idx, + int sample_rate) +{ + const LADSPA_PortRangeHint *hint; + LADSPA_PortRangeHintDescriptor flags; + LADSPA_Data value = 0.0; + float low_factor = 0.0; + float high_factor = 0.0; + + if(port_idx >= effect->desc->PortCount) + { + return value; + } + + hint = &effect->desc->PortRangeHints[port_idx]; + flags = hint->HintDescriptor; + + if(!LADSPA_IS_HINT_HAS_DEFAULT(flags)) + { + return value; + } + + if(LADSPA_IS_HINT_DEFAULT_0(flags)) + { + value = 0.0; + } + else if(LADSPA_IS_HINT_DEFAULT_1(flags)) + { + value = 1.0; + } + else if(LADSPA_IS_HINT_DEFAULT_100(flags)) + { + value = 100.0; + } + else if(LADSPA_IS_HINT_DEFAULT_440(flags)) + { + value = 440.0; + } + /* defaults based on lower or upper bounds must consider HINT_SAMPLE_RATE */ + else + { + if(LADSPA_IS_HINT_DEFAULT_MINIMUM(flags)) + { + low_factor = 1.0; + high_factor = 0.0; + } + else if(LADSPA_IS_HINT_DEFAULT_LOW(flags)) + { + low_factor = 0.75; + high_factor = 0.25; + } + else if(LADSPA_IS_HINT_DEFAULT_MIDDLE(flags)) + { + low_factor = 0.5; + high_factor = 0.5; + } + else if(LADSPA_IS_HINT_DEFAULT_HIGH(flags)) + { + low_factor = 0.25; + high_factor = 0.75; + } + else if(LADSPA_IS_HINT_DEFAULT_MAXIMUM(flags)) + { + low_factor = 0.0; + high_factor = 1.0; + } + + if(LADSPA_IS_HINT_LOGARITHMIC(flags) && low_factor > 0 && high_factor > 0) + { + value = exp(log(hint->LowerBound) * low_factor + log(hint->UpperBound) * high_factor); + } + else + { + value = (hint->LowerBound * low_factor + hint->UpperBound * high_factor); + } + + if(LADSPA_IS_HINT_SAMPLE_RATE(flags)) + { + value *= sample_rate; + } + } + + if(LADSPA_IS_HINT_INTEGER(flags)) + { + /* LADSPA doesn't specify which rounding method to use, so lets keep it simple... */ + value = floor(value + 0.5); + } + + return value; +} + +/** + * Create a control node for each control port on the passed in effect. The value of the + * node is taken from port hint defaults, if available. This gets run automatically after + * an effect has been added. + * + * @param fx LADSPA fx instance + * @param effect effect instance + * @return FLUID_OK on success, otherwise FLUID_FAILED + */ +static int create_control_port_nodes(fluid_ladspa_fx_t *fx, fluid_ladspa_effect_t *effect) +{ + unsigned int i; + fluid_ladspa_node_t *node; + fluid_ladspa_dir_t dir; + int port_flags; + + for(i = 0; i < effect->desc->PortCount; i++) + { + port_flags = effect->desc->PortDescriptors[i]; + + if(!LADSPA_IS_PORT_CONTROL(port_flags)) + { + continue; + } + + node = new_fluid_ladspa_node(fx, "", + FLUID_LADSPA_NODE_EFFECT | FLUID_LADSPA_NODE_CONTROL, NULL); + + if(node == NULL) + { + return FLUID_FAILED; + } + + node->effect_buffer[0] = get_default_port_value(effect, i, fx->sample_rate); + + dir = (LADSPA_IS_PORT_INPUT(port_flags)) ? FLUID_LADSPA_INPUT : FLUID_LADSPA_OUTPUT; + + connect_node_to_port(node, dir, effect, i); + } + + return FLUID_OK; +} + +static void connect_node_to_port(fluid_ladspa_node_t *node, fluid_ladspa_dir_t dir, + fluid_ladspa_effect_t *effect, int port_idx) +{ + effect->desc->connect_port(effect->handle, port_idx, node->effect_buffer); + effect->port_nodes[port_idx] = node; + + /* Mark node as connected in the respective direction */ + if(dir == FLUID_LADSPA_INPUT) + { + node->num_outputs++; + } + else + { + node->num_inputs++; + } +} + + +/** + * Check that all ports on the effect are connected to a node. + * + * @param effect LADSPA effect instance + * @param name if check fails, points to the name of first failed port + * @return FLUID_OK on successful check, otherwise FLUID_FAILED + */ +static int check_all_ports_connected(fluid_ladspa_effect_t *effect, const char **name) +{ + unsigned int i; + + for(i = 0; i < effect->desc->PortCount; i++) + { + if(effect->port_nodes[i] == NULL) + { + *name = effect->desc->PortNames[i]; + return FLUID_FAILED; + } + } + + return FLUID_OK; +} + +/** + * In-place broken plugins can't cope with input and output audio ports connected + * to the same buffer. Check for this condition in the effect. + * + * @param effect effect instance + * @param name1 if check fails, points to the first port name + * @param name2 if check fails, points to the second port name + * @return FLUID_OK on successful check, otherwise FLUID_FAILED + */ +static int check_no_inplace_broken(fluid_ladspa_effect_t *effect, const char **name1, const char **name2) +{ + unsigned int i, k; + LADSPA_PortDescriptor flags1, flags2; + + if(!LADSPA_IS_INPLACE_BROKEN(effect->desc->Properties)) + { + return FLUID_OK; + } + + for(i = 0; i < effect->desc->PortCount; i++) + { + flags1 = effect->desc->PortDescriptors[i]; + + for(k = 0; k < effect->desc->PortCount; k++) + { + flags2 = effect->desc->PortDescriptors[k]; + + if(i != k + && effect->port_nodes[i]->effect_buffer == effect->port_nodes[k]->effect_buffer + && (flags1 & 0x3) != (flags2 & 0x3) /* first two bits encode direction */ + && LADSPA_IS_PORT_AUDIO(flags1) && LADSPA_IS_PORT_AUDIO(flags2)) + { + *name1 = effect->desc->PortNames[i]; + *name2 = effect->desc->PortNames[k]; + return FLUID_FAILED; + } + } + } + + return FLUID_OK; +} + +/** + * Check that at least one host node is used by an effect + * + * @param fx LADSPA fx instance + * @return FLUID_OK on successful check, otherwise FLUID_FAILED + */ +static int check_host_output_used(fluid_ladspa_fx_t *fx) +{ + fluid_list_t *list; + fluid_ladspa_node_t *node; + + for(list = fx->host_nodes; list; list = fluid_list_next(list)) + { + node = (fluid_ladspa_node_t *) fluid_list_get(list); + + if(node->num_inputs) + { + return FLUID_OK; + } + } + + return FLUID_FAILED; +} + +/** + * Check that all user audio nodes have an input and an output + * + * @param fx LADSPA fx instance + * @param name if check fails, points to the name of first failed node + * @return FLUID_OK on successful check, otherwise FLUID_FAILED + */ +static int check_all_audio_nodes_connected(fluid_ladspa_fx_t *fx, const char **name) +{ + fluid_list_t *list; + fluid_ladspa_node_t *node; + + for(list = fx->user_nodes; list; list = fluid_list_next(list)) + { + node = (fluid_ladspa_node_t *) fluid_list_get(list); + + if(node->num_inputs == 0 || node->num_outputs == 0) + { + *name = node->name; + return FLUID_FAILED; + } + } + + return FLUID_OK; +} + +#ifndef WITH_FLOAT +/** + * Copy and type convert host buffers to effect buffers. Used only if host and LADSPA + * use different float types. + */ +static FLUID_INLINE void copy_host_to_effect_buffers(fluid_ladspa_fx_t *fx, int num_samples) +{ + int i; + fluid_list_t *list; + fluid_ladspa_node_t *node; + + for(list = fx->host_nodes; list; list = fluid_list_next(list)) + { + node = (fluid_ladspa_node_t *) fluid_list_get(list); + + /* Only copy host nodes that have at least one output or output, i.e. + * that are connected to at least one effect port. */ + if(node->num_inputs > 0 || node->num_outputs > 0) + { + for(i = 0; i < num_samples; i++) + { + node->effect_buffer[i] = (LADSPA_Data)node->host_buffer[i]; + } + } + } +} + +/** + * Copy and type convert effect buffers to host buffers. Used only if host and LADSPA + * use different float types. + */ +static FLUID_INLINE void copy_effect_to_host_buffers(fluid_ladspa_fx_t *fx, int num_samples) +{ + int i; + fluid_list_t *list; + fluid_ladspa_node_t *node; + + for(list = fx->host_nodes; list; list = fluid_list_next(list)) + { + node = (fluid_ladspa_node_t *) fluid_list_get(list); + + /* Only copy effect nodes that have at least one input, i.e. that are connected to + * at least one effect output */ + if(node->num_inputs > 0) + { + for(i = 0; i < num_samples; i++) + { + node->host_buffer[i] = (fluid_real_t)node->effect_buffer[i]; + } + } + } +} +#endif /* WITH_FLOAT */ + +#else /* ifdef LADSPA */ + +/* Dummy functions to use if LADSPA is not compiled in, to keep the + * FluidSynth library ABI stable */ + +int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx) +{ + return FALSE; +} + +int fluid_ladspa_activate(fluid_ladspa_fx_t *fx) +{ + return FLUID_FAILED; +} + +int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx) +{ + return FLUID_FAILED; +} + +int fluid_ladspa_reset(fluid_ladspa_fx_t *fx) +{ + return FLUID_FAILED; +} + +int fluid_ladspa_check(fluid_ladspa_fx_t *fx, char *err, int err_size) +{ + return FLUID_FAILED; +} + +int fluid_ladspa_host_port_exists(fluid_ladspa_fx_t *fx, const char *name) +{ + return FALSE; +} + +int fluid_ladspa_add_buffer(fluid_ladspa_fx_t *fx, const char *name) +{ + return FLUID_FAILED; +} + +int fluid_ladspa_buffer_exists(fluid_ladspa_fx_t *fx, const char *name) +{ + return FALSE; +} + +int fluid_ladspa_add_effect(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *lib_name, const char *plugin_name) +{ + return FLUID_FAILED; +} + +int fluid_ladspa_effect_can_mix(fluid_ladspa_fx_t *fx, const char *name) +{ + return FALSE; +} + +int fluid_ladspa_effect_set_mix(fluid_ladspa_fx_t *fx, const char *name, int mix, float gain) +{ + return FLUID_FAILED; +} + +int fluid_ladspa_effect_port_exists(fluid_ladspa_fx_t *fx, const char *effect_name, const char *port_name) +{ + return FALSE; +} + +int fluid_ladspa_effect_set_control(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *port_name, float val) +{ + return FLUID_FAILED; +} + +int fluid_ladspa_effect_link(fluid_ladspa_fx_t *fx, const char *effect_name, + const char *port_name, const char *name) +{ + return FLUID_FAILED; +} + +#endif /* ifdef LADSPA */ diff --git a/thirdparty/fluidsynth/src/bindings/fluid_ladspa.h b/thirdparty/fluidsynth/src/bindings/fluid_ladspa.h new file mode 100644 index 000000000..80952d51e --- /dev/null +++ b/thirdparty/fluidsynth/src/bindings/fluid_ladspa.h @@ -0,0 +1,36 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_LADSPA_H +#define _FLUID_LADSPA_H + +#include "fluid_sys.h" + +fluid_ladspa_fx_t *new_fluid_ladspa_fx(fluid_real_t sample_rate, int buffer_size); +void delete_fluid_ladspa_fx(fluid_ladspa_fx_t *fx); + +int fluid_ladspa_set_sample_rate(fluid_ladspa_fx_t *fx, fluid_real_t sample_rate); + +void fluid_ladspa_run(fluid_ladspa_fx_t *fx, int block_count, int block_size); + +int fluid_ladspa_add_host_ports(fluid_ladspa_fx_t *fx, const char *prefix, + int num_buffers, fluid_real_t buffers[], int buf_stride); + +#endif /* _FLUID_LADSPA_H */ diff --git a/thirdparty/fluidsynth/src/config.h b/thirdparty/fluidsynth/src/config.h new file mode 100644 index 000000000..3a58a2cdb --- /dev/null +++ b/thirdparty/fluidsynth/src/config.h @@ -0,0 +1,280 @@ +#ifndef CONFIG_H +#define CONFIG_H + +/* Define to enable ALSA driver */ +/* #undef ALSA_SUPPORT */ + +/* Define to activate sound output to files */ +/* #undef AUFILE_SUPPORT */ + +/* whether or not we are supporting CoreAudio */ +/* #undef COREAUDIO_SUPPORT */ + +/* whether or not we are supporting CoreMIDI */ +/* #undef COREMIDI_SUPPORT */ + +/* whether or not we are supporting DART */ +/* #undef DART_SUPPORT */ + +/* Define if building for Mac OS X Darwin */ +/* #undef DARWIN */ + +/* Define if D-Bus support is enabled */ +/* #undef DBUS_SUPPORT */ + +/* Soundfont to load automatically in some use cases */ +#define DEFAULT_SOUNDFONT "C:\\ProgramData\\soundfonts\\default.sf2" + +/* Define to enable FPE checks */ +/* #undef FPE_CHECK */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARPA_INET_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_INTTYPES_H */ + +/* Define to 1 if you have the header file. */ +#ifdef _WIN32 +#define HAVE_IO_H 1 +#endif + +/* whether or not we are supporting lash */ +/* #undef HAVE_LASH */ + +/* Define if systemd support is enabled */ +/* #undef SYSTEMD_SUPPORT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_SOUNDCARD_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MACHINE_SOUNDCARD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_IN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_TCP_H */ + +/* Define if compiling the mixer with multi-thread support */ +#define ENABLE_MIXER_THREADS 1 + +/* Define if compiling with openMP to enable parallel audio rendering */ +/* #undef HAVE_OPENMP */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PTHREAD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOUNDCARD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_TIME_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#ifndef _WIN32 +#define HAVE_UNISTD_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GETOPT_H */ + +/* Define to 1 if you have the inet_ntop() function. */ +/* #undef HAVE_INETNTOP */ + +/* Define to enable JACK driver */ +/* #undef JACK_SUPPORT */ + +/* Define to enable PipeWire driver */ +/* #undef PIPEWIRE_SUPPORT */ + +/* Include the LADSPA Fx unit */ +/* #undef LADSPA */ + +/* Define to enable IPV6 support */ +/* #undef IPV6_SUPPORT */ + +/* Define to enable network support */ +/* #undef NETWORK_SUPPORT */ + +/* Defined when fluidsynth is build in an automated environment, where no MSVC++ Runtime Debug Assertion dialogs should pop up */ +/* #undef NO_GUI */ + +/* libinstpatch for DLS and GIG */ +/* #undef LIBINSTPATCH_SUPPORT */ + +/* libsndfile has ogg vorbis support */ +/* #undef LIBSNDFILE_HASVORBIS */ + +/* Define to enable libsndfile support */ +/* #undef LIBSNDFILE_SUPPORT */ + +/* Define to enable MidiShare driver */ +/* #undef MIDISHARE_SUPPORT */ + +/* Define if using the MinGW32 environment */ +/* #undef MINGW32 */ + +/* Define to enable OSS driver */ +/* #undef OSS_SUPPORT */ + +/* Define to enable OPENSLES driver */ +/* #undef OPENSLES_SUPPORT */ + +/* Define to enable Oboe driver */ +/* #undef OBOE_SUPPORT */ + +/* Name of package */ +#define PACKAGE "fluidsynth" + +/* Define to the address where bug reports for this package should be sent. */ +/* #undef PACKAGE_BUGREPORT */ + +/* Define to the full name of this package. */ +/* #undef PACKAGE_NAME */ + +/* Define to the full name and version of this package. */ +/* #undef PACKAGE_STRING */ + +/* Define to the one symbol short name of this package. */ +/* #undef PACKAGE_TARNAME */ + +/* Define to the version of this package. */ +/* #undef PACKAGE_VERSION */ + +/* Define to enable PortAudio driver */ +/* #undef PORTAUDIO_SUPPORT */ + +/* Define to enable PulseAudio driver */ +/* #undef PULSE_SUPPORT */ + +/* Define to enable DirectSound driver */ +/* #undef DSOUND_SUPPORT */ + +/* Define to enable Windows WASAPI driver */ +/* #undef WASAPI_SUPPORT */ + +/* Define to enable Windows WaveOut driver */ +/* #undef WAVEOUT_SUPPORT */ + +/* Define to enable Windows MIDI driver */ +/* #undef WINMIDI_SUPPORT */ + +/* Define to enable SDL2 audio driver */ +/* #undef SDL2_SUPPORT */ + +/* Define to 1 if you have the ANSI C header files. */ +/* #undef STDC_HEADERS */ + +/* Soundfont to load for unit testing */ +#define TEST_SOUNDFONT "C:/Programming/Projects/fluidsynth/sf2/VintageDreamsWaves-v2.sf2" + +/* Soundfont to load for UTF-8 unit testing */ +#define TEST_SOUNDFONT_UTF8_1 "C:/Programming/Projects/fluidsynth/sf2/\xE2\x96\xA0VintageDreamsWaves-v2\xE2\x96\xA0.sf2" +#define TEST_SOUNDFONT_UTF8_2 "C:/Programming/Projects/fluidsynth/sf2/VìntàgèDrèàmsWàvès-v2.sf2" +#define TEST_MIDI_UTF8 "C:/Programming/Projects/fluidsynth/test/èmpty.mid" + +/* SF3 Soundfont to load for unit testing */ +#define TEST_SOUNDFONT_SF3 "C:/Programming/Projects/fluidsynth/sf2/VintageDreamsWaves-v2.sf3" + +/* Define to enable SIGFPE assertions */ +/* #undef TRAP_ON_FPE */ + +/* Define to do all DSP in single floating point precision */ +/* #undef WITH_FLOAT */ + +/* Define to profile the DSP code */ +/* #undef WITH_PROFILING */ + +/* Define to use the readline library for line editing */ +/* #undef WITH_READLINE */ + +/* Define if the compiler supports VLA */ +/* #undef SUPPORTS_VLA */ + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to 1 if you have the sinf() function. */ +#define HAVE_SINF 1 + +/* Define to 1 if you have the cosf() function. */ +#define HAVE_COSF 1 + +/* Define to 1 if you have the fabsf() function. */ +#define HAVE_FABSF 1 + +/* Define to 1 if you have the powf() function. */ +#define HAVE_POWF 1 + +/* Define to 1 if you have the sqrtf() function. */ +#define HAVE_SQRTF 1 + +/* Define to 1 if you have the logf() function. */ +#define HAVE_LOGF 1 + +/* Define to 1 if you have the socklen_t type. */ +#define HAVE_SOCKLEN_T 1 + +/* Define if using glib stubs instead of real glib. */ +#ifdef _WIN32 +#define WITH_GLIB_STUBS +#endif + +#endif /* CONFIG_H */ diff --git a/thirdparty/fluidsynth/src/drivers/fluid_adriver.c b/thirdparty/fluidsynth/src/drivers/fluid_adriver.c new file mode 100644 index 000000000..732532447 --- /dev/null +++ b/thirdparty/fluidsynth/src/drivers/fluid_adriver.c @@ -0,0 +1,495 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_adriver.h" +#include "fluid_sys.h" +#include "fluid_settings.h" + +/* + * fluid_adriver_definition_t + */ + +struct _fluid_audriver_definition_t +{ + const char *name; + fluid_audio_driver_t *(*new)(fluid_settings_t *settings, fluid_synth_t *synth); + fluid_audio_driver_t *(*new2)(fluid_settings_t *settings, + fluid_audio_func_t func, + void *data); + void (*free)(fluid_audio_driver_t *driver); + void (*settings)(fluid_settings_t *settings); +}; + +/* Available audio drivers, listed in order of preference */ +static const fluid_audriver_definition_t fluid_audio_drivers[] = +{ +#if ALSA_SUPPORT + { + "alsa", + new_fluid_alsa_audio_driver, + new_fluid_alsa_audio_driver2, + delete_fluid_alsa_audio_driver, + fluid_alsa_audio_driver_settings + }, +#endif + +#if JACK_SUPPORT + { + "jack", + new_fluid_jack_audio_driver, + new_fluid_jack_audio_driver2, + delete_fluid_jack_audio_driver, + fluid_jack_audio_driver_settings + }, +#endif + +#if PULSE_SUPPORT + { + "pulseaudio", + new_fluid_pulse_audio_driver, + new_fluid_pulse_audio_driver2, + delete_fluid_pulse_audio_driver, + fluid_pulse_audio_driver_settings + }, +#endif + +#if PIPEWIRE_SUPPORT + { + "pipewire", + new_fluid_pipewire_audio_driver, + new_fluid_pipewire_audio_driver2, + delete_fluid_pipewire_audio_driver, + fluid_pipewire_audio_driver_settings + }, +#endif + +#if OSS_SUPPORT + { + "oss", + new_fluid_oss_audio_driver, + new_fluid_oss_audio_driver2, + delete_fluid_oss_audio_driver, + fluid_oss_audio_driver_settings + }, +#endif + +#if OBOE_SUPPORT + { + "oboe", + new_fluid_oboe_audio_driver, + NULL, + delete_fluid_oboe_audio_driver, + fluid_oboe_audio_driver_settings + }, +#endif + +#if OPENSLES_SUPPORT + { + "opensles", + new_fluid_opensles_audio_driver, + NULL, + delete_fluid_opensles_audio_driver, + fluid_opensles_audio_driver_settings + }, +#endif + +#if COREAUDIO_SUPPORT + { + "coreaudio", + new_fluid_core_audio_driver, + new_fluid_core_audio_driver2, + delete_fluid_core_audio_driver, + fluid_core_audio_driver_settings + }, +#endif + +#if DSOUND_SUPPORT + { + "dsound", + new_fluid_dsound_audio_driver, + new_fluid_dsound_audio_driver2, + delete_fluid_dsound_audio_driver, + fluid_dsound_audio_driver_settings + }, +#endif + +#if WASAPI_SUPPORT + { + "wasapi", + new_fluid_wasapi_audio_driver, + new_fluid_wasapi_audio_driver2, + delete_fluid_wasapi_audio_driver, + fluid_wasapi_audio_driver_settings + }, +#endif + +#if WAVEOUT_SUPPORT + { + "waveout", + new_fluid_waveout_audio_driver, + new_fluid_waveout_audio_driver2, + delete_fluid_waveout_audio_driver, + fluid_waveout_audio_driver_settings + }, +#endif + +#if SNDMAN_SUPPORT + { + "sndman", + new_fluid_sndmgr_audio_driver, + new_fluid_sndmgr_audio_driver2, + delete_fluid_sndmgr_audio_driver, + NULL + }, +#endif + +#if PORTAUDIO_SUPPORT + { + "portaudio", + new_fluid_portaudio_driver, + NULL, + delete_fluid_portaudio_driver, + fluid_portaudio_driver_settings + }, +#endif + +#if DART_SUPPORT + { + "dart", + new_fluid_dart_audio_driver, + NULL, + delete_fluid_dart_audio_driver, + fluid_dart_audio_driver_settings + }, +#endif + +#if SDL2_SUPPORT + { + "sdl2", + new_fluid_sdl2_audio_driver, + NULL, + delete_fluid_sdl2_audio_driver, + fluid_sdl2_audio_driver_settings + }, +#endif + +#if AUFILE_SUPPORT + { + "file", + new_fluid_file_audio_driver, + NULL, + delete_fluid_file_audio_driver, + NULL + }, +#endif + /* NULL terminator to avoid zero size array if no driver available */ + { NULL, NULL, NULL, NULL, NULL } +}; + +#define ENABLE_AUDIO_DRIVER(_drv, _idx) \ + _drv[(_idx) / (sizeof(*(_drv))*8)] &= ~(1 << ((_idx) % (sizeof((*_drv))*8))) + +#define IS_AUDIO_DRIVER_ENABLED(_drv, _idx) \ + (!(_drv[(_idx) / (sizeof(*(_drv))*8)] & (1 << ((_idx) % (sizeof((*_drv))*8))))) + +static uint8_t fluid_adriver_disable_mask[(FLUID_N_ELEMENTS(fluid_audio_drivers) + 7) / 8] = {0}; + +void fluid_audio_driver_settings(fluid_settings_t *settings) +{ + unsigned int i; + const char *def_name = NULL; + + fluid_settings_register_str(settings, "audio.sample-format", "16bits", 0); + fluid_settings_add_option(settings, "audio.sample-format", "16bits"); + fluid_settings_add_option(settings, "audio.sample-format", "float"); + +#if defined(WIN32) + fluid_settings_register_int(settings, "audio.period-size", 512, 64, 8192, 0); + fluid_settings_register_int(settings, "audio.periods", 8, 2, 64, 0); +#elif defined(MACOS9) + fluid_settings_register_int(settings, "audio.period-size", 64, 64, 8192, 0); + fluid_settings_register_int(settings, "audio.periods", 8, 2, 64, 0); +#else + fluid_settings_register_int(settings, "audio.period-size", 64, 64, 8192, 0); + fluid_settings_register_int(settings, "audio.periods", 16, 2, 64, 0); +#endif + + fluid_settings_register_int(settings, "audio.realtime-prio", + FLUID_DEFAULT_AUDIO_RT_PRIO, 0, 99, 0); + + fluid_settings_register_str(settings, "audio.driver", "", 0); + + for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; i++) + { + /* Select the default driver */ + if (def_name == NULL) + { + def_name = fluid_audio_drivers[i].name; + } + + /* Add the driver to the list of options */ + fluid_settings_add_option(settings, "audio.driver", fluid_audio_drivers[i].name); + + if(fluid_audio_drivers[i].settings != NULL && + IS_AUDIO_DRIVER_ENABLED(fluid_adriver_disable_mask, i)) + { + fluid_audio_drivers[i].settings(settings); + } + } + + /* Set the default driver, if any */ + if(def_name != NULL) + { + fluid_settings_setstr(settings, "audio.driver", def_name); + } +} + +static const fluid_audriver_definition_t * +find_fluid_audio_driver(fluid_settings_t *settings) +{ + unsigned int i; + char *name; + char *allnames; + + for(i = 0; i < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; i++) + { + /* If this driver is de-activated, just ignore it */ + if(!IS_AUDIO_DRIVER_ENABLED(fluid_adriver_disable_mask, i)) + { + continue; + } + + if(fluid_settings_str_equal(settings, "audio.driver", fluid_audio_drivers[i].name)) + { + FLUID_LOG(FLUID_DBG, "Using '%s' audio driver", fluid_audio_drivers[i].name); + return &fluid_audio_drivers[i]; + } + } + + fluid_settings_dupstr(settings, "audio.driver", &name); /* ++ alloc name */ + FLUID_LOG(FLUID_ERR, "Couldn't find the requested audio driver '%s'.", name ? name : "NULL"); + + allnames = fluid_settings_option_concat(settings, "audio.driver", NULL); + if(allnames != NULL) + { + if(allnames[0] != '\0') + { + FLUID_LOG(FLUID_INFO, "This build of fluidsynth supports the following audio drivers: %s", allnames); + } + else + { + FLUID_LOG(FLUID_INFO, "This build of fluidsynth doesn't support any audio drivers."); + } + + FLUID_FREE(allnames); + } + + FLUID_FREE(name); + + return NULL; +} + +/** + * Create a new audio driver. + * + * @param settings Configuration settings used to select and create the audio + * driver. + * @param synth Synthesizer instance for which the audio driver is created for. + * @return The new audio driver instance or NULL on error + * + * Creates a new audio driver for a given \p synth instance with a defined set + * of configuration \p settings. The \p settings instance must be the same that + * you have passed to new_fluid_synth() when creating the \p synth instance. + * Otherwise the behaviour is undefined. + * + * @note As soon as an audio driver is created, the \p synth starts rendering audio. + * This means that all necessary initialization and sound-setup should have been + * completed before calling this function. + * Thus, of all object types in use (synth, midi player, sequencer, etc.) the audio + * driver should always be the last one to be created and the first one to be deleted! + * Also refer to the order of object creation in the code examples. + */ +fluid_audio_driver_t * +new_fluid_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) +{ + const fluid_audriver_definition_t *def = find_fluid_audio_driver(settings); + + if(def) + { + fluid_audio_driver_t *driver; + double srate, midi_event_latency; + int period_size; + + fluid_settings_getint(settings, "audio.period-size", &period_size); + fluid_settings_getnum(settings, "synth.sample-rate", &srate); + + midi_event_latency = period_size / srate; + if(midi_event_latency >= 0.05) + { + FLUID_LOG(FLUID_WARN, "You have chosen 'audio.period-size' to be %d samples. Given a sample rate of %.1f this results in a latency of %.1f ms, which will cause MIDI events to be poorly quantized (=untimed) in the synthesized audio (also known as the 'drunken-drummer' syndrome). To avoid that, you're strongly advised to increase 'audio.periods' instead, while keeping 'audio.period-size' small enough to make this warning disappear.", period_size, srate, midi_event_latency*1000.0); + } + + driver = (*def->new)(settings, synth); + + if(driver) + { + driver->define = def; + } + + return driver; + } + + return NULL; +} + +/** + * Create a new audio driver. + * + * @param settings Configuration settings used to select and create the audio + * driver. + * @param func Function called to fill audio buffers for audio playback + * @param data User defined data pointer to pass to \p func + * @return The new audio driver instance or NULL on error + * + * Like new_fluid_audio_driver() but allows for custom audio processing before + * audio is sent to audio driver. It is the responsibility of the callback + * \p func to render the audio into the buffers. If \p func uses a fluid_synth_t \p synth, + * the \p settings instance must be the same that you have passed to new_fluid_synth() + * when creating the \p synth instance. Otherwise the behaviour is undefined. + * + * @note Not as efficient as new_fluid_audio_driver(). + * + * @note As soon as an audio driver is created, a new thread is spawned starting to make + * callbacks to \p func. + * This means that all necessary sound-setup should be completed after this point, + * thus of all object types in use (synth, midi player, sequencer, etc.) the audio + * driver should always be the last one to be created and the first one to be deleted! + * Also refer to the order of object creation in the code examples. + */ +fluid_audio_driver_t * +new_fluid_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data) +{ + const fluid_audriver_definition_t *def = find_fluid_audio_driver(settings); + + if(def) + { + fluid_audio_driver_t *driver = NULL; + + if(def->new2 == NULL) + { + FLUID_LOG(FLUID_DBG, "Callback mode unsupported on '%s' audio driver", def->name); + } + else + { + driver = (*def->new2)(settings, func, data); + + if(driver) + { + driver->define = def; + } + } + + return driver; + } + + return NULL; +} + +/** + * Deletes an audio driver instance. + * + * @param driver Audio driver instance to delete + * + * Shuts down an audio driver and deletes its instance. + */ +void +delete_fluid_audio_driver(fluid_audio_driver_t *driver) +{ + fluid_return_if_fail(driver != NULL); + driver->define->free(driver); +} + + +/** + * Registers audio drivers to use + * + * @param adrivers NULL-terminated array of audio drivers to register. Pass NULL to register all available drivers. + * @return #FLUID_OK if all the audio drivers requested by the user are supported by fluidsynth and have been + * successfully registered. Otherwise #FLUID_FAILED is returned and this function has no effect. + * + * When creating a settings instance with new_fluid_settings(), all audio drivers are initialized once. + * In the past this has caused segfaults and application crashes due to buggy soundcard drivers. + * + * This function enables the user to only initialize specific audio drivers when settings instances are created. + * Therefore pass a NULL-terminated array of C-strings containing the \c names of audio drivers to register + * for the usage with fluidsynth. + * The \c names are the same as being used for the \c audio.driver setting. + * + * By default all audio drivers fluidsynth has been compiled with are registered, so calling this function is optional. + * + * @warning This function may only be called if no thread is residing in fluidsynth's API and no instances of any kind + * are alive (e.g. as it would be the case right after fluidsynth's initial creation). Else the behaviour is undefined. + * Furtermore any attempt of using audio drivers that have not been registered is undefined behaviour! + * + * @note This function is not thread safe and will never be! + * + * @since 1.1.9 + */ +int fluid_audio_driver_register(const char **adrivers) +{ + unsigned int i; + uint8_t disable_mask[FLUID_N_ELEMENTS(fluid_adriver_disable_mask)]; + + if(adrivers == NULL) + { + /* Pass NULL to register all available drivers. */ + FLUID_MEMSET(fluid_adriver_disable_mask, 0, sizeof(fluid_adriver_disable_mask)); + + return FLUID_OK; + } + + FLUID_MEMSET(disable_mask, 0xFF, sizeof(disable_mask)); + + for(i = 0; adrivers[i] != NULL; i++) + { + unsigned int j; + + /* search the requested audio driver in the template and enable it if found */ + for(j = 0; j < FLUID_N_ELEMENTS(fluid_audio_drivers) - 1; j++) + { + if(FLUID_STRCMP(adrivers[i], fluid_audio_drivers[j].name) == 0) + { + ENABLE_AUDIO_DRIVER(disable_mask, j); + break; + } + } + + if(j >= FLUID_N_ELEMENTS(fluid_audio_drivers) - 1) + { + /* requested driver not found, failure */ + return FLUID_FAILED; + } + } + + /* Update list of activated drivers */ + FLUID_MEMCPY(fluid_adriver_disable_mask, disable_mask, sizeof(disable_mask)); + + return FLUID_OK; +} diff --git a/thirdparty/fluidsynth/src/drivers/fluid_adriver.h b/thirdparty/fluidsynth/src/drivers/fluid_adriver.h new file mode 100644 index 000000000..8517f3371 --- /dev/null +++ b/thirdparty/fluidsynth/src/drivers/fluid_adriver.h @@ -0,0 +1,186 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_AUDRIVER_H +#define _FLUID_AUDRIVER_H + +#include "fluidsynth_priv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * fluid_audio_driver_t + */ + +typedef struct _fluid_audriver_definition_t fluid_audriver_definition_t; + +struct _fluid_audio_driver_t +{ + const fluid_audriver_definition_t *define; +}; + +void fluid_audio_driver_settings(fluid_settings_t *settings); + +/* Defined in fluid_filerenderer.c */ +void fluid_file_renderer_settings(fluid_settings_t *settings); + +#if PULSE_SUPPORT +fluid_audio_driver_t *new_fluid_pulse_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +fluid_audio_driver_t *new_fluid_pulse_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, void *data); +void delete_fluid_pulse_audio_driver(fluid_audio_driver_t *p); +void fluid_pulse_audio_driver_settings(fluid_settings_t *settings); +#endif + +#if ALSA_SUPPORT +fluid_audio_driver_t *new_fluid_alsa_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +fluid_audio_driver_t *new_fluid_alsa_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, void *data); +void delete_fluid_alsa_audio_driver(fluid_audio_driver_t *p); +void fluid_alsa_audio_driver_settings(fluid_settings_t *settings); +#endif + +#if OSS_SUPPORT +fluid_audio_driver_t *new_fluid_oss_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +fluid_audio_driver_t *new_fluid_oss_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, void *data); +void delete_fluid_oss_audio_driver(fluid_audio_driver_t *p); +void fluid_oss_audio_driver_settings(fluid_settings_t *settings); +#endif + +#if OPENSLES_SUPPORT +fluid_audio_driver_t* +new_fluid_opensles_audio_driver(fluid_settings_t* settings, + fluid_synth_t* synth); +void delete_fluid_opensles_audio_driver(fluid_audio_driver_t* p); +void fluid_opensles_audio_driver_settings(fluid_settings_t* settings); +#endif + +#if OBOE_SUPPORT +fluid_audio_driver_t* +new_fluid_oboe_audio_driver(fluid_settings_t* settings, + fluid_synth_t* synth); +void delete_fluid_oboe_audio_driver(fluid_audio_driver_t* p); +void fluid_oboe_audio_driver_settings(fluid_settings_t* settings); +#endif + +#if COREAUDIO_SUPPORT +fluid_audio_driver_t *new_fluid_core_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +fluid_audio_driver_t *new_fluid_core_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, + void *data); +void delete_fluid_core_audio_driver(fluid_audio_driver_t *p); +void fluid_core_audio_driver_settings(fluid_settings_t *settings); +#endif + +#if DSOUND_SUPPORT +fluid_audio_driver_t *new_fluid_dsound_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +fluid_audio_driver_t *new_fluid_dsound_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, + void *data); +void delete_fluid_dsound_audio_driver(fluid_audio_driver_t *p); +void fluid_dsound_audio_driver_settings(fluid_settings_t *settings); +#endif + +#if WASAPI_SUPPORT +fluid_audio_driver_t *new_fluid_wasapi_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, + void *data); +void delete_fluid_wasapi_audio_driver(fluid_audio_driver_t *p); +void fluid_wasapi_audio_driver_settings(fluid_settings_t *settings); +#endif + +#if WAVEOUT_SUPPORT +fluid_audio_driver_t *new_fluid_waveout_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +fluid_audio_driver_t *new_fluid_waveout_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, + void *data); +void delete_fluid_waveout_audio_driver(fluid_audio_driver_t *p); +void fluid_waveout_audio_driver_settings(fluid_settings_t *settings); +#endif + +#if PORTAUDIO_SUPPORT +void fluid_portaudio_driver_settings(fluid_settings_t *settings); +fluid_audio_driver_t *new_fluid_portaudio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +void delete_fluid_portaudio_driver(fluid_audio_driver_t *p); +#endif + +#if JACK_SUPPORT +fluid_audio_driver_t *new_fluid_jack_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth); +fluid_audio_driver_t *new_fluid_jack_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, void *data); +void delete_fluid_jack_audio_driver(fluid_audio_driver_t *p); +void fluid_jack_audio_driver_settings(fluid_settings_t *settings); +int fluid_jack_obtain_synth(fluid_settings_t *settings, fluid_synth_t **synth); +#endif + +#if PIPEWIRE_SUPPORT +fluid_audio_driver_t *new_fluid_pipewire_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth); +fluid_audio_driver_t *new_fluid_pipewire_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, void *data); +void delete_fluid_pipewire_audio_driver(fluid_audio_driver_t *p); +void fluid_pipewire_audio_driver_settings(fluid_settings_t *settings); +#endif + +#if SNDMAN_SUPPORT +fluid_audio_driver_t *new_fluid_sndmgr_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +fluid_audio_driver_t *new_fluid_sndmgr_audio_driver2(fluid_settings_t *settings, + fluid_audio_func_t func, + void *data); +void delete_fluid_sndmgr_audio_driver(fluid_audio_driver_t *p); +#endif + +#if DART_SUPPORT +fluid_audio_driver_t *new_fluid_dart_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +void delete_fluid_dart_audio_driver(fluid_audio_driver_t *p); +void fluid_dart_audio_driver_settings(fluid_settings_t *settings); +#endif + +#if SDL2_SUPPORT +fluid_audio_driver_t *new_fluid_sdl2_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +void delete_fluid_sdl2_audio_driver(fluid_audio_driver_t *p); +void fluid_sdl2_audio_driver_settings(fluid_settings_t *settings); +#endif + +#if AUFILE_SUPPORT +fluid_audio_driver_t *new_fluid_file_audio_driver(fluid_settings_t *settings, + fluid_synth_t *synth); +void delete_fluid_file_audio_driver(fluid_audio_driver_t *p); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUID_AUDRIVER_H */ diff --git a/thirdparty/fluidsynth/src/drivers/fluid_mdriver.c b/thirdparty/fluidsynth/src/drivers/fluid_mdriver.c new file mode 100644 index 000000000..973aef582 --- /dev/null +++ b/thirdparty/fluidsynth/src/drivers/fluid_mdriver.c @@ -0,0 +1,196 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_mdriver.h" +#include "fluid_settings.h" + + +/* + * fluid_mdriver_definition + */ +struct _fluid_mdriver_definition_t +{ + const char *name; + fluid_midi_driver_t *(*new)(fluid_settings_t *settings, + handle_midi_event_func_t event_handler, + void *event_handler_data); + void (*free)(fluid_midi_driver_t *p); + void (*settings)(fluid_settings_t *settings); +}; + + +static const fluid_mdriver_definition_t fluid_midi_drivers[] = +{ +#if ALSA_SUPPORT + { + "alsa_seq", + new_fluid_alsa_seq_driver, + delete_fluid_alsa_seq_driver, + fluid_alsa_seq_driver_settings + }, + { + "alsa_raw", + new_fluid_alsa_rawmidi_driver, + delete_fluid_alsa_rawmidi_driver, + fluid_alsa_rawmidi_driver_settings + }, +#endif +#if JACK_SUPPORT + { + "jack", + new_fluid_jack_midi_driver, + delete_fluid_jack_midi_driver, + fluid_jack_midi_driver_settings + }, +#endif +#if OSS_SUPPORT + { + "oss", + new_fluid_oss_midi_driver, + delete_fluid_oss_midi_driver, + fluid_oss_midi_driver_settings + }, +#endif +#if WINMIDI_SUPPORT + { + "winmidi", + new_fluid_winmidi_driver, + delete_fluid_winmidi_driver, + fluid_winmidi_midi_driver_settings + }, +#endif +#if MIDISHARE_SUPPORT + { + "midishare", + new_fluid_midishare_midi_driver, + delete_fluid_midishare_midi_driver, + NULL + }, +#endif +#if COREMIDI_SUPPORT + { + "coremidi", + new_fluid_coremidi_driver, + delete_fluid_coremidi_driver, + fluid_coremidi_driver_settings + }, +#endif + /* NULL terminator to avoid zero size array if no driver available */ + { NULL, NULL, NULL, NULL } +}; + + +void fluid_midi_driver_settings(fluid_settings_t *settings) +{ + unsigned int i; + const char *def_name = NULL; + + fluid_settings_register_int(settings, "midi.autoconnect", 0, 0, 1, FLUID_HINT_TOGGLED); + + fluid_settings_register_int(settings, "midi.realtime-prio", + FLUID_DEFAULT_MIDI_RT_PRIO, 0, 99, 0); + + fluid_settings_register_str(settings, "midi.driver", "", 0); + + for(i = 0; i < FLUID_N_ELEMENTS(fluid_midi_drivers) - 1; i++) + { + /* Select the default driver */ + if (def_name == NULL) + { + def_name = fluid_midi_drivers[i].name; + } + + /* Add the driver to the list of options */ + fluid_settings_add_option(settings, "midi.driver", fluid_midi_drivers[i].name); + + if(fluid_midi_drivers[i].settings != NULL) + { + fluid_midi_drivers[i].settings(settings); + } + } + + /* Set the default driver, if any */ + if(def_name != NULL) + { + fluid_settings_setstr(settings, "midi.driver", def_name); + } +} + +/** + * Create a new MIDI driver instance. + * + * @param settings Settings used to configure new MIDI driver. See \ref settings_midi for available options. + * @param handler MIDI handler callback (for example: fluid_midi_router_handle_midi_event() + * for MIDI router) + * @param event_handler_data Caller defined data to pass to 'handler' + * @return New MIDI driver instance or NULL on error + * + * Which MIDI driver is actually created depends on the \ref settings_midi_driver option. + */ +fluid_midi_driver_t *new_fluid_midi_driver(fluid_settings_t *settings, handle_midi_event_func_t handler, void *event_handler_data) +{ + fluid_midi_driver_t *driver = NULL; + char *allnames; + const fluid_mdriver_definition_t *def; + + for(def = fluid_midi_drivers; def->name != NULL; def++) + { + if(fluid_settings_str_equal(settings, "midi.driver", def->name)) + { + FLUID_LOG(FLUID_DBG, "Using '%s' midi driver", def->name); + driver = def->new(settings, handler, event_handler_data); + + if(driver) + { + driver->define = def; + } + + return driver; + } + } + + FLUID_LOG(FLUID_ERR, "Couldn't find the requested midi driver."); + allnames = fluid_settings_option_concat(settings, "midi.driver", NULL); + if(allnames != NULL) + { + if(allnames[0] != '\0') + { + FLUID_LOG(FLUID_INFO, "This build of fluidsynth supports the following MIDI drivers: %s", allnames); + } + else + { + FLUID_LOG(FLUID_INFO, "This build of fluidsynth doesn't support any MIDI drivers."); + } + + FLUID_FREE(allnames); + } + + return NULL; +} + +/** + * Delete a MIDI driver instance. + * @param driver MIDI driver to delete + */ +void delete_fluid_midi_driver(fluid_midi_driver_t *driver) +{ + fluid_return_if_fail(driver != NULL); + driver->define->free(driver); +} diff --git a/thirdparty/fluidsynth/src/drivers/fluid_mdriver.h b/thirdparty/fluidsynth/src/drivers/fluid_mdriver.h new file mode 100644 index 000000000..76b8b7b04 --- /dev/null +++ b/thirdparty/fluidsynth/src/drivers/fluid_mdriver.h @@ -0,0 +1,100 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_MDRIVER_H +#define _FLUID_MDRIVER_H + +#include "fluid_sys.h" + +/* + * fluid_midi_driver_t + */ + +typedef struct _fluid_mdriver_definition_t fluid_mdriver_definition_t; + +struct _fluid_midi_driver_t +{ + const fluid_mdriver_definition_t *define; + handle_midi_event_func_t handler; + void *data; +}; + +void fluid_midi_driver_settings(fluid_settings_t *settings); + +/* ALSA */ +#if ALSA_SUPPORT +fluid_midi_driver_t *new_fluid_alsa_rawmidi_driver(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *event_handler_data); +void delete_fluid_alsa_rawmidi_driver(fluid_midi_driver_t *p); +void fluid_alsa_rawmidi_driver_settings(fluid_settings_t *settings); + +fluid_midi_driver_t *new_fluid_alsa_seq_driver(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *event_handler_data); +void delete_fluid_alsa_seq_driver(fluid_midi_driver_t *p); +void fluid_alsa_seq_driver_settings(fluid_settings_t *settings); +#endif + +/* JACK */ +#if JACK_SUPPORT +void fluid_jack_midi_driver_settings(fluid_settings_t *settings); +fluid_midi_driver_t *new_fluid_jack_midi_driver(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *data); +void delete_fluid_jack_midi_driver(fluid_midi_driver_t *p); +#endif + +/* OSS */ +#if OSS_SUPPORT +fluid_midi_driver_t *new_fluid_oss_midi_driver(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *event_handler_data); +void delete_fluid_oss_midi_driver(fluid_midi_driver_t *p); +void fluid_oss_midi_driver_settings(fluid_settings_t *settings); +#endif + +/* Windows MIDI service */ +#if WINMIDI_SUPPORT +fluid_midi_driver_t *new_fluid_winmidi_driver(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *event_handler_data); +void delete_fluid_winmidi_driver(fluid_midi_driver_t *p); +void fluid_winmidi_midi_driver_settings(fluid_settings_t *settings); +#endif + +/* definitions for the MidiShare driver */ +#if MIDISHARE_SUPPORT +fluid_midi_driver_t *new_fluid_midishare_midi_driver(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *event_handler_data); +void delete_fluid_midishare_midi_driver(fluid_midi_driver_t *p); +#endif + +/* definitions for the CoreMidi driver */ +#if COREMIDI_SUPPORT +fluid_midi_driver_t *new_fluid_coremidi_driver(fluid_settings_t *settings, + handle_midi_event_func_t handler, + void *event_handler_data); +void delete_fluid_coremidi_driver(fluid_midi_driver_t *p); +void fluid_coremidi_driver_settings(fluid_settings_t *settings); +#endif + +#endif /* _FLUID_AUDRIVER_H */ diff --git a/thirdparty/fluidsynth/src/drivers/fluid_sndmgr.c b/thirdparty/fluidsynth/src/drivers/fluid_sndmgr.c new file mode 100644 index 000000000..0d83f643a --- /dev/null +++ b/thirdparty/fluidsynth/src/drivers/fluid_sndmgr.c @@ -0,0 +1,368 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/* fluid_sndmgr.c + * + * Driver for MacOS Classic + */ + +#if SNDMAN_SUPPORT + +#include "fluid_synth.h" +#include "fluid_adriver.h" +#include "fluid_settings.h" + +#include + +typedef struct +{ + fluid_audio_driver_t driver; + SndDoubleBufferHeader2 *doubleHeader; + SndDoubleBackUPP doubleCallbackProc; + SndChannelPtr channel; + int callback_is_audio_func; + void *data; + fluid_audio_func_t callback; + float *convbuffers[2]; + int bufferByteSize; + int bufferFrameSize; +} fluid_sndmgr_audio_driver_t; + +void pascal fluid_sndmgr_callback(SndChannelPtr chan, SndDoubleBufferPtr doubleBuffer); +Fixed fluid_sndmgr_double_to_fix(long double theLD); + +/* + * generic new : returns error + */ +int +start_fluid_sndmgr_audio_driver(fluid_settings_t *settings, + fluid_sndmgr_audio_driver_t *dev, + int buffer_size) +{ + int i; + SndDoubleBufferHeader2 *doubleHeader = NULL; + SndDoubleBufferPtr doubleBuffer = NULL; + OSErr err; + SndChannelPtr channel = NULL; + double sample_rate; + + fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); + + dev->doubleCallbackProc = NewSndDoubleBackProc(fluid_sndmgr_callback); + + /* the channel */ + FLUID_LOG(FLUID_DBG, "FLUID-SndManager@2"); + err = SndNewChannel(&channel, sampledSynth, initStereo, NULL); + + if((err != noErr) || (channel == NULL)) + { + FLUID_LOG(FLUID_ERR, "Failed to allocate a sound channel (error %i)", err); + return err; + } + + /* the double buffer struct */ + FLUID_LOG(FLUID_DBG, "FLUID-SndManager@3"); + doubleHeader = FLUID_NEW(SndDoubleBufferHeader2); + + if(doubleHeader == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return -1; + } + + doubleHeader->dbhBufferPtr[0] = NULL; + doubleHeader->dbhBufferPtr[1] = NULL; + doubleHeader->dbhNumChannels = 2; + doubleHeader->dbhSampleSize = 16; + doubleHeader->dbhCompressionID = 0; + doubleHeader->dbhPacketSize = 0; + doubleHeader->dbhSampleRate = fluid_sndmgr_double_to_fix((long double) sample_rate); + doubleHeader->dbhDoubleBack = dev->doubleCallbackProc; + doubleHeader->dbhFormat = 0; + + /* prepare dev */ + FLUID_LOG(FLUID_DBG, "FLUID-SndManager@4"); + dev->doubleHeader = doubleHeader; + dev->channel = channel; + dev->bufferFrameSize = buffer_size; + dev->bufferByteSize = buffer_size * 2 * 2; + + /* the 2 doublebuffers */ + FLUID_LOG(FLUID_DBG, "FLUID-SndManager@5"); + + for(i = 0; i < 2; i++) + { + doubleBuffer = (SndDoubleBufferPtr) FLUID_MALLOC(sizeof(SndDoubleBuffer) + + dev->bufferByteSize); + + if(doubleBuffer == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return -1; + } + + doubleBuffer->dbNumFrames = 0; + doubleBuffer->dbFlags = 0; + doubleBuffer->dbUserInfo[0] = (long) dev; + doubleHeader->dbhBufferPtr[i] = doubleBuffer; + CallSndDoubleBackProc(doubleHeader->dbhDoubleBack, channel, doubleBuffer); + } + + /* start */ + FLUID_LOG(FLUID_DBG, "FLUID-SndManager@6"); + + err = SndPlayDoubleBuffer(channel, (SndDoubleBufferHeader *)doubleHeader); + + if(err != noErr) + { + FLUID_LOG(FLUID_ERR, "Failed to start the sound driver (error %i)", err); + return err; + } + + FLUID_LOG(FLUID_DBG, "FLUID-SndManager@7"); + return 0; +} + +/* + * new_fluid_sndmgr_audio_driver + * This implementation used the 16bit format. + */ +fluid_audio_driver_t * +new_fluid_sndmgr_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) +{ + fluid_sndmgr_audio_driver_t *dev = NULL; + int period_size, periods, buffer_size; + + /* check the format */ + if(!fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) + { + FLUID_LOG(FLUID_ERR, "Unhandled sample format"); + return NULL; + } + + /* compute buffer size */ + fluid_settings_getint(settings, "audio.period-size", &period_size); + fluid_settings_getint(settings, "audio.periods", &periods); + buffer_size = period_size * periods; + + /* allocated dev */ + dev = FLUID_NEW(fluid_sndmgr_audio_driver_t); + + if(dev == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(dev, 0, sizeof(fluid_sndmgr_audio_driver_t)); + + dev->callback_is_audio_func = false; + dev->data = (void *)synth; + dev->callback = NULL; + + if(start_fluid_sndmgr_audio_driver(settings, dev, buffer_size) != 0) + { + delete_fluid_sndmgr_audio_driver((fluid_audio_driver_t *)dev); + return NULL; + } + + return (fluid_audio_driver_t *)dev; +} + +/* + * new_fluid_sndmgr_audio_driver2 + * + * This implementation used the audio_func float format, with + * conversion from float to 16bits in the driver. + */ +fluid_audio_driver_t * +new_fluid_sndmgr_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data) +{ + fluid_sndmgr_audio_driver_t *dev = NULL; + int period_size, periods, buffer_size; + + /* compute buffer size */ + fluid_settings_getint(settings, "audio.period-size", &period_size); + fluid_settings_getint(settings, "audio.periods", &periods); + buffer_size = period_size * periods; + + /* allocated dev */ + dev = FLUID_NEW(fluid_sndmgr_audio_driver_t); + + if(dev == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(dev, 0, sizeof(fluid_sndmgr_audio_driver_t)); + + /* allocate the conversion buffers */ + dev->convbuffers[0] = FLUID_ARRAY(float, buffer_size); + dev->convbuffers[1] = FLUID_ARRAY(float, buffer_size); + + if((dev->convbuffers[0] == NULL) || (dev->convbuffers[1] == NULL)) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + goto error_recovery; + } + + dev->callback_is_audio_func = true; + dev->data = data; + dev->callback = func; + + if(start_fluid_sndmgr_audio_driver(settings, dev, buffer_size) != 0) + { + goto error_recovery; + } + + return (fluid_audio_driver_t *)dev; + +error_recovery: + delete_fluid_sndmgr_audio_driver((fluid_audio_driver_t *)dev); + return NULL; +} + +/* + * delete_fluid_sndmgr_audio_driver + */ +void delete_fluid_sndmgr_audio_driver(fluid_audio_driver_t *p) +{ + fluid_sndmgr_audio_driver_t *dev = (fluid_sndmgr_audio_driver_t *) p; + fluid_return_if_fail(dev != NULL); + + if(dev->channel != NULL) + { + SndDisposeChannel(dev->channel, 1); + } + + if(dev->doubleCallbackProc != NULL) + { + DisposeRoutineDescriptor(dev->doubleCallbackProc); + } + + if(dev->doubleHeader != NULL) + { + FLUID_FREE(dev->doubleHeader->dbhBufferPtr[0]); + FLUID_FREE(dev->doubleHeader->dbhBufferPtr[1]); + FLUID_FREE(dev->doubleHeader); + } + + FLUID_FREE(dev->convbuffers[0]); + FLUID_FREE(dev->convbuffers[1]); + FLUID_FREE(dev); +} + +/* + * fluid_sndmgr_callback + * + */ +void pascal fluid_sndmgr_callback(SndChannelPtr chan, SndDoubleBufferPtr doubleBuffer) +{ + fluid_sndmgr_audio_driver_t *dev; + signed short *buf; + float *left; + float *right; + float v; + int i, k, buffer_size; + + dev = (fluid_sndmgr_audio_driver_t *) doubleBuffer->dbUserInfo[0]; + buf = (signed short *)doubleBuffer->dbSoundData; + buffer_size = dev->bufferFrameSize; + + if(dev->callback_is_audio_func) + { + /* float API : conversion to signed short */ + left = dev->convbuffers[0]; + right = dev->convbuffers[1]; + + FLUID_MEMSET(left, 0, buffer_size * sizeof(float)); + FLUID_MEMSET(right, 0, buffer_size * sizeof(float)); + + (*dev->callback)(dev->data, buffer_size, 0, NULL, 2, dev->convbuffers); + + for(i = 0, k = 0; i < buffer_size; i++) + { + v = 32767.0f * left[i]; + fluid_clip(v, -32768.0f, 32767.0f); + buf[k++] = (signed short) v; + + v = 32767.0f * right[i]; + fluid_clip(v, -32768.0f, 32767.0f); + buf[k++] = (signed short) v; + } + + } + else + { + /* let the synth do the conversion */ + fluid_synth_write_s16((fluid_synth_t *)dev->data, buffer_size, buf, 0, 2, buf, 1, 2); + } + + doubleBuffer->dbFlags = doubleBuffer->dbFlags | dbBufferReady; + doubleBuffer->dbNumFrames = buffer_size; +} + +/* + * fluid_sndmgr_double_to_fix + * + * A Fixed number is of the type 12345.67890. It is 32 bits in size with the + * high order bits representing the significant value (that before the point) + * and the lower 16 bits representing the fractional part of the number. + * The Sound Manager further complicates matters by using Fixed numbers, but + * needing to represent numbers larger than what the Fixed is capable of. + * To do this the Sound Manager treats the sign bit as having the value 32768 + * which will cause any number greater or equal to 32768 to look like it is + * negative. + * This routine is designed to "do the right thing" and convert any long double + * into the Fixed number it represents. + * long double is the input type because AIFF files use extended80 numbers and + * there are routines that will convert from an extended80 to a long double. + * A long double has far greater precision than a Fixed, so any number whose + * significant or fraction is larger than 65535 will not convert correctly. + */ +#define _MAX_VALUE 65535 +#define _BITS_PER_BYTE 8 +Fixed fluid_sndmgr_double_to_fix(long double theLD) +{ + unsigned long theResult = 0; + unsigned short theSignificant = 0, theFraction = 0; + + if(theLD < _MAX_VALUE) + { + theSignificant = theLD; + theFraction = theLD - theSignificant; + + if(theFraction > _MAX_VALUE) + { + /* Won't be able to convert */ + theSignificant = 0; + theFraction = 0; + } + } + + theResult |= theSignificant; + theResult = theResult << (sizeof(unsigned short) * _BITS_PER_BYTE); + theResult |= theFraction; + return theResult; +} + +#endif diff --git a/thirdparty/fluidsynth/src/fluid_conv_tables.inc.h b/thirdparty/fluidsynth/src/fluid_conv_tables.inc.h new file mode 100644 index 000000000..7660ae02d --- /dev/null +++ b/thirdparty/fluidsynth/src/fluid_conv_tables.inc.h @@ -0,0 +1,3916 @@ +/* THIS FILE HAS BEEN AUTOMATICALLY GENERATED. DO NOT EDIT. */ + +static const fluid_real_t fluid_ct2hz_tab[1200] = { + 6.875000000000000e+00, /* 0 */ + 6.878972302857565e+00, /* 1 */ + 6.882946900870038e+00, /* 2 */ + 6.886923795363534e+00, /* 3 */ + 6.890902987664938e+00, /* 4 */ + 6.894884479101899e+00, /* 5 */ + 6.898868271002832e+00, /* 6 */ + 6.902854364696922e+00, /* 7 */ + 6.906842761514120e+00, /* 8 */ + 6.910833462785147e+00, /* 9 */ + 6.914826469841493e+00, /* 10 */ + 6.918821784015415e+00, /* 11 */ + 6.922819406639943e+00, /* 12 */ + 6.926819339048873e+00, /* 13 */ + 6.930821582576776e+00, /* 14 */ + 6.934826138558993e+00, /* 15 */ + 6.938833008331636e+00, /* 16 */ + 6.942842193231585e+00, /* 17 */ + 6.946853694596501e+00, /* 18 */ + 6.950867513764812e+00, /* 19 */ + 6.954883652075718e+00, /* 20 */ + 6.958902110869197e+00, /* 21 */ + 6.962922891486000e+00, /* 22 */ + 6.966945995267650e+00, /* 23 */ + 6.970971423556450e+00, /* 24 */ + 6.974999177695476e+00, /* 25 */ + 6.979029259028577e+00, /* 26 */ + 6.983061668900381e+00, /* 27 */ + 6.987096408656299e+00, /* 28 */ + 6.991133479642508e+00, /* 29 */ + 6.995172883205968e+00, /* 30 */ + 6.999214620694422e+00, /* 31 */ + 7.003258693456385e+00, /* 32 */ + 7.007305102841154e+00, /* 33 */ + 7.011353850198803e+00, /* 34 */ + 7.015404936880191e+00, /* 35 */ + 7.019458364236955e+00, /* 36 */ + 7.023514133621508e+00, /* 37 */ + 7.027572246387055e+00, /* 38 */ + 7.031632703887573e+00, /* 39 */ + 7.035695507477828e+00, /* 40 */ + 7.039760658513362e+00, /* 41 */ + 7.043828158350510e+00, /* 42 */ + 7.047898008346381e+00, /* 43 */ + 7.051970209858872e+00, /* 44 */ + 7.056044764246665e+00, /* 45 */ + 7.060121672869229e+00, /* 46 */ + 7.064200937086813e+00, /* 47 */ + 7.068282558260457e+00, /* 48 */ + 7.072366537751985e+00, /* 49 */ + 7.076452876924008e+00, /* 50 */ + 7.080541577139924e+00, /* 51 */ + 7.084632639763921e+00, /* 52 */ + 7.088726066160973e+00, /* 53 */ + 7.092821857696843e+00, /* 54 */ + 7.096920015738083e+00, /* 55 */ + 7.101020541652036e+00, /* 56 */ + 7.105123436806832e+00, /* 57 */ + 7.109228702571396e+00, /* 58 */ + 7.113336340315441e+00, /* 59 */ + 7.117446351409471e+00, /* 60 */ + 7.121558737224782e+00, /* 61 */ + 7.125673499133463e+00, /* 62 */ + 7.129790638508400e+00, /* 63 */ + 7.133910156723263e+00, /* 64 */ + 7.138032055152522e+00, /* 65 */ + 7.142156335171443e+00, /* 66 */ + 7.146282998156079e+00, /* 67 */ + 7.150412045483284e+00, /* 68 */ + 7.154543478530708e+00, /* 69 */ + 7.158677298676794e+00, /* 70 */ + 7.162813507300781e+00, /* 71 */ + 7.166952105782710e+00, /* 72 */ + 7.171093095503412e+00, /* 73 */ + 7.175236477844522e+00, /* 74 */ + 7.179382254188470e+00, /* 75 */ + 7.183530425918486e+00, /* 76 */ + 7.187680994418600e+00, /* 77 */ + 7.191833961073637e+00, /* 78 */ + 7.195989327269231e+00, /* 79 */ + 7.200147094391808e+00, /* 80 */ + 7.204307263828601e+00, /* 81 */ + 7.208469836967637e+00, /* 82 */ + 7.212634815197753e+00, /* 83 */ + 7.216802199908588e+00, /* 84 */ + 7.220971992490576e+00, /* 85 */ + 7.225144194334963e+00, /* 86 */ + 7.229318806833797e+00, /* 87 */ + 7.233495831379924e+00, /* 88 */ + 7.237675269367005e+00, /* 89 */ + 7.241857122189496e+00, /* 90 */ + 7.246041391242668e+00, /* 91 */ + 7.250228077922588e+00, /* 92 */ + 7.254417183626143e+00, /* 93 */ + 7.258608709751013e+00, /* 94 */ + 7.262802657695695e+00, /* 95 */ + 7.266999028859489e+00, /* 96 */ + 7.271197824642509e+00, /* 97 */ + 7.275399046445672e+00, /* 98 */ + 7.279602695670708e+00, /* 99 */ + 7.283808773720155e+00, /* 100 */ + 7.288017281997363e+00, /* 101 */ + 7.292228221906492e+00, /* 102 */ + 7.296441594852512e+00, /* 103 */ + 7.300657402241208e+00, /* 104 */ + 7.304875645479175e+00, /* 105 */ + 7.309096325973821e+00, /* 106 */ + 7.313319445133367e+00, /* 107 */ + 7.317545004366848e+00, /* 108 */ + 7.321773005084116e+00, /* 109 */ + 7.326003448695829e+00, /* 110 */ + 7.330236336613470e+00, /* 111 */ + 7.334471670249334e+00, /* 112 */ + 7.338709451016527e+00, /* 113 */ + 7.342949680328980e+00, /* 114 */ + 7.347192359601435e+00, /* 115 */ + 7.351437490249451e+00, /* 116 */ + 7.355685073689411e+00, /* 117 */ + 7.359935111338512e+00, /* 118 */ + 7.364187604614767e+00, /* 119 */ + 7.368442554937015e+00, /* 120 */ + 7.372699963724910e+00, /* 121 */ + 7.376959832398929e+00, /* 122 */ + 7.381222162380365e+00, /* 123 */ + 7.385486955091339e+00, /* 124 */ + 7.389754211954788e+00, /* 125 */ + 7.394023934394474e+00, /* 126 */ + 7.398296123834983e+00, /* 127 */ + 7.402570781701721e+00, /* 128 */ + 7.406847909420918e+00, /* 129 */ + 7.411127508419629e+00, /* 130 */ + 7.415409580125734e+00, /* 131 */ + 7.419694125967937e+00, /* 132 */ + 7.423981147375768e+00, /* 133 */ + 7.428270645779582e+00, /* 134 */ + 7.432562622610564e+00, /* 135 */ + 7.436857079300720e+00, /* 136 */ + 7.441154017282889e+00, /* 137 */ + 7.445453437990733e+00, /* 138 */ + 7.449755342858746e+00, /* 139 */ + 7.454059733322252e+00, /* 140 */ + 7.458366610817398e+00, /* 141 */ + 7.462675976781167e+00, /* 142 */ + 7.466987832651371e+00, /* 143 */ + 7.471302179866648e+00, /* 144 */ + 7.475619019866477e+00, /* 145 */ + 7.479938354091157e+00, /* 146 */ + 7.484260183981829e+00, /* 147 */ + 7.488584510980459e+00, /* 148 */ + 7.492911336529853e+00, /* 149 */ + 7.497240662073646e+00, /* 150 */ + 7.501572489056309e+00, /* 151 */ + 7.505906818923147e+00, /* 152 */ + 7.510243653120299e+00, /* 153 */ + 7.514582993094742e+00, /* 154 */ + 7.518924840294287e+00, /* 155 */ + 7.523269196167583e+00, /* 156 */ + 7.527616062164117e+00, /* 157 */ + 7.531965439734210e+00, /* 158 */ + 7.536317330329022e+00, /* 159 */ + 7.540671735400553e+00, /* 160 */ + 7.545028656401644e+00, /* 161 */ + 7.549388094785967e+00, /* 162 */ + 7.553750052008046e+00, /* 163 */ + 7.558114529523234e+00, /* 164 */ + 7.562481528787732e+00, /* 165 */ + 7.566851051258580e+00, /* 166 */ + 7.571223098393661e+00, /* 167 */ + 7.575597671651698e+00, /* 168 */ + 7.579974772492261e+00, /* 169 */ + 7.584354402375757e+00, /* 170 */ + 7.588736562763442e+00, /* 171 */ + 7.593121255117417e+00, /* 172 */ + 7.597508480900623e+00, /* 173 */ + 7.601898241576849e+00, /* 174 */ + 7.606290538610730e+00, /* 175 */ + 7.610685373467746e+00, /* 176 */ + 7.615082747614226e+00, /* 177 */ + 7.619482662517344e+00, /* 178 */ + 7.623885119645124e+00, /* 179 */ + 7.628290120466435e+00, /* 180 */ + 7.632697666450997e+00, /* 181 */ + 7.637107759069377e+00, /* 182 */ + 7.641520399792996e+00, /* 183 */ + 7.645935590094122e+00, /* 184 */ + 7.650353331445872e+00, /* 185 */ + 7.654773625322218e+00, /* 186 */ + 7.659196473197983e+00, /* 187 */ + 7.663621876548839e+00, /* 188 */ + 7.668049836851313e+00, /* 189 */ + 7.672480355582786e+00, /* 190 */ + 7.676913434221488e+00, /* 191 */ + 7.681349074246513e+00, /* 192 */ + 7.685787277137797e+00, /* 193 */ + 7.690228044376140e+00, /* 194 */ + 7.694671377443195e+00, /* 195 */ + 7.699117277821469e+00, /* 196 */ + 7.703565746994330e+00, /* 197 */ + 7.708016786445999e+00, /* 198 */ + 7.712470397661556e+00, /* 199 */ + 7.716926582126939e+00, /* 200 */ + 7.721385341328947e+00, /* 201 */ + 7.725846676755232e+00, /* 202 */ + 7.730310589894314e+00, /* 203 */ + 7.734777082235564e+00, /* 204 */ + 7.739246155269221e+00, /* 205 */ + 7.743717810486381e+00, /* 206 */ + 7.748192049379003e+00, /* 207 */ + 7.752668873439905e+00, /* 208 */ + 7.757148284162772e+00, /* 209 */ + 7.761630283042152e+00, /* 210 */ + 7.766114871573452e+00, /* 211 */ + 7.770602051252947e+00, /* 212 */ + 7.775091823577775e+00, /* 213 */ + 7.779584190045939e+00, /* 214 */ + 7.784079152156307e+00, /* 215 */ + 7.788576711408615e+00, /* 216 */ + 7.793076869303465e+00, /* 217 */ + 7.797579627342325e+00, /* 218 */ + 7.802084987027529e+00, /* 219 */ + 7.806592949862281e+00, /* 220 */ + 7.811103517350658e+00, /* 221 */ + 7.815616690997596e+00, /* 222 */ + 7.820132472308910e+00, /* 223 */ + 7.824650862791279e+00, /* 224 */ + 7.829171863952254e+00, /* 225 */ + 7.833695477300261e+00, /* 226 */ + 7.838221704344591e+00, /* 227 */ + 7.842750546595412e+00, /* 228 */ + 7.847282005563763e+00, /* 229 */ + 7.851816082761554e+00, /* 230 */ + 7.856352779701573e+00, /* 231 */ + 7.860892097897477e+00, /* 232 */ + 7.865434038863802e+00, /* 233 */ + 7.869978604115957e+00, /* 234 */ + 7.874525795170227e+00, /* 235 */ + 7.879075613543772e+00, /* 236 */ + 7.883628060754630e+00, /* 237 */ + 7.888183138321716e+00, /* 238 */ + 7.892740847764821e+00, /* 239 */ + 7.897301190604616e+00, /* 240 */ + 7.901864168362650e+00, /* 241 */ + 7.906429782561352e+00, /* 242 */ + 7.910998034724028e+00, /* 243 */ + 7.915568926374868e+00, /* 244 */ + 7.920142459038940e+00, /* 245 */ + 7.924718634242192e+00, /* 246 */ + 7.929297453511457e+00, /* 247 */ + 7.933878918374448e+00, /* 248 */ + 7.938463030359761e+00, /* 249 */ + 7.943049790996876e+00, /* 250 */ + 7.947639201816158e+00, /* 251 */ + 7.952231264348851e+00, /* 252 */ + 7.956825980127090e+00, /* 253 */ + 7.961423350683890e+00, /* 254 */ + 7.966023377553156e+00, /* 255 */ + 7.970626062269677e+00, /* 256 */ + 7.975231406369129e+00, /* 257 */ + 7.979839411388076e+00, /* 258 */ + 7.984450078863969e+00, /* 259 */ + 7.989063410335148e+00, /* 260 */ + 7.993679407340840e+00, /* 261 */ + 7.998298071421166e+00, /* 262 */ + 8.002919404117131e+00, /* 263 */ + 8.007543406970633e+00, /* 264 */ + 8.012170081524465e+00, /* 265 */ + 8.016799429322301e+00, /* 266 */ + 8.021431451908718e+00, /* 267 */ + 8.026066150829182e+00, /* 268 */ + 8.030703527630045e+00, /* 269 */ + 8.035343583858563e+00, /* 270 */ + 8.039986321062880e+00, /* 271 */ + 8.044631740792035e+00, /* 272 */ + 8.049279844595961e+00, /* 273 */ + 8.053930634025493e+00, /* 274 */ + 8.058584110632355e+00, /* 275 */ + 8.063240275969166e+00, /* 276 */ + 8.067899131589453e+00, /* 277 */ + 8.072560679047628e+00, /* 278 */ + 8.077224919899010e+00, /* 279 */ + 8.081891855699810e+00, /* 280 */ + 8.086561488007144e+00, /* 281 */ + 8.091233818379026e+00, /* 282 */ + 8.095908848374366e+00, /* 283 */ + 8.100586579552983e+00, /* 284 */ + 8.105267013475586e+00, /* 285 */ + 8.109950151703799e+00, /* 286 */ + 8.114635995800137e+00, /* 287 */ + 8.119324547328020e+00, /* 288 */ + 8.124015807851782e+00, /* 289 */ + 8.128709778936644e+00, /* 290 */ + 8.133406462148743e+00, /* 291 */ + 8.138105859055118e+00, /* 292 */ + 8.142807971223712e+00, /* 293 */ + 8.147512800223378e+00, /* 294 */ + 8.152220347623867e+00, /* 295 */ + 8.156930614995849e+00, /* 296 */ + 8.161643603910889e+00, /* 297 */ + 8.166359315941468e+00, /* 298 */ + 8.171077752660976e+00, /* 299 */ + 8.175798915643707e+00, /* 300 */ + 8.180522806464868e+00, /* 301 */ + 8.185249426700576e+00, /* 302 */ + 8.189978777927859e+00, /* 303 */ + 8.194710861724653e+00, /* 304 */ + 8.199445679669809e+00, /* 305 */ + 8.204183233343089e+00, /* 306 */ + 8.208923524325167e+00, /* 307 */ + 8.213666554197633e+00, /* 308 */ + 8.218412324542989e+00, /* 309 */ + 8.223160836944650e+00, /* 310 */ + 8.227912092986951e+00, /* 311 */ + 8.232666094255134e+00, /* 312 */ + 8.237422842335366e+00, /* 313 */ + 8.242182338814722e+00, /* 314 */ + 8.246944585281202e+00, /* 315 */ + 8.251709583323715e+00, /* 316 */ + 8.256477334532098e+00, /* 317 */ + 8.261247840497099e+00, /* 318 */ + 8.266021102810386e+00, /* 319 */ + 8.270797123064552e+00, /* 320 */ + 8.275575902853102e+00, /* 321 */ + 8.280357443770470e+00, /* 322 */ + 8.285141747412004e+00, /* 323 */ + 8.289928815373978e+00, /* 324 */ + 8.294718649253587e+00, /* 325 */ + 8.299511250648951e+00, /* 326 */ + 8.304306621159110e+00, /* 327 */ + 8.309104762384031e+00, /* 328 */ + 8.313905675924602e+00, /* 329 */ + 8.318709363382636e+00, /* 330 */ + 8.323515826360879e+00, /* 331 */ + 8.328325066462995e+00, /* 332 */ + 8.333137085293574e+00, /* 333 */ + 8.337951884458139e+00, /* 334 */ + 8.342769465563139e+00, /* 335 */ + 8.347589830215949e+00, /* 336 */ + 8.352412980024869e+00, /* 337 */ + 8.357238916599142e+00, /* 338 */ + 8.362067641548924e+00, /* 339 */ + 8.366899156485314e+00, /* 340 */ + 8.371733463020334e+00, /* 341 */ + 8.376570562766940e+00, /* 342 */ + 8.381410457339022e+00, /* 343 */ + 8.386253148351402e+00, /* 344 */ + 8.391098637419834e+00, /* 345 */ + 8.395946926161001e+00, /* 346 */ + 8.400798016192528e+00, /* 347 */ + 8.405651909132970e+00, /* 348 */ + 8.410508606601821e+00, /* 349 */ + 8.415368110219505e+00, /* 350 */ + 8.420230421607386e+00, /* 351 */ + 8.425095542387764e+00, /* 352 */ + 8.429963474183879e+00, /* 353 */ + 8.434834218619903e+00, /* 354 */ + 8.439707777320951e+00, /* 355 */ + 8.444584151913077e+00, /* 356 */ + 8.449463344023272e+00, /* 357 */ + 8.454345355279468e+00, /* 358 */ + 8.459230187310540e+00, /* 359 */ + 8.464117841746299e+00, /* 360 */ + 8.469008320217505e+00, /* 361 */ + 8.473901624355852e+00, /* 362 */ + 8.478797755793982e+00, /* 363 */ + 8.483696716165481e+00, /* 364 */ + 8.488598507104875e+00, /* 365 */ + 8.493503130247637e+00, /* 366 */ + 8.498410587230186e+00, /* 367 */ + 8.503320879689882e+00, /* 368 */ + 8.508234009265037e+00, /* 369 */ + 8.513149977594903e+00, /* 370 */ + 8.518068786319684e+00, /* 371 */ + 8.522990437080534e+00, /* 372 */ + 8.527914931519545e+00, /* 373 */ + 8.532842271279769e+00, /* 374 */ + 8.537772458005202e+00, /* 375 */ + 8.542705493340792e+00, /* 376 */ + 8.547641378932433e+00, /* 377 */ + 8.552580116426974e+00, /* 378 */ + 8.557521707472214e+00, /* 379 */ + 8.562466153716908e+00, /* 380 */ + 8.567413456810757e+00, /* 381 */ + 8.572363618404420e+00, /* 382 */ + 8.577316640149505e+00, /* 383 */ + 8.582272523698581e+00, /* 384 */ + 8.587231270705169e+00, /* 385 */ + 8.592192882823742e+00, /* 386 */ + 8.597157361709733e+00, /* 387 */ + 8.602124709019529e+00, /* 388 */ + 8.607094926410479e+00, /* 389 */ + 8.612068015540880e+00, /* 390 */ + 8.617043978069995e+00, /* 391 */ + 8.622022815658045e+00, /* 392 */ + 8.627004529966211e+00, /* 393 */ + 8.631989122656625e+00, /* 394 */ + 8.636976595392392e+00, /* 395 */ + 8.641966949837570e+00, /* 396 */ + 8.646960187657180e+00, /* 397 */ + 8.651956310517205e+00, /* 398 */ + 8.656955320084593e+00, /* 399 */ + 8.661957218027252e+00, /* 400 */ + 8.666962006014057e+00, /* 401 */ + 8.671969685714840e+00, /* 402 */ + 8.676980258800407e+00, /* 403 */ + 8.681993726942528e+00, /* 404 */ + 8.687010091813930e+00, /* 405 */ + 8.692029355088316e+00, /* 406 */ + 8.697051518440354e+00, /* 407 */ + 8.702076583545674e+00, /* 408 */ + 8.707104552080883e+00, /* 409 */ + 8.712135425723552e+00, /* 410 */ + 8.717169206152221e+00, /* 411 */ + 8.722205895046399e+00, /* 412 */ + 8.727245494086567e+00, /* 413 */ + 8.732288004954178e+00, /* 414 */ + 8.737333429331656e+00, /* 415 */ + 8.742381768902396e+00, /* 416 */ + 8.747433025350762e+00, /* 417 */ + 8.752487200362102e+00, /* 418 */ + 8.757544295622727e+00, /* 419 */ + 8.762604312819928e+00, /* 420 */ + 8.767667253641967e+00, /* 421 */ + 8.772733119778085e+00, /* 422 */ + 8.777801912918500e+00, /* 423 */ + 8.782873634754402e+00, /* 424 */ + 8.787948286977960e+00, /* 425 */ + 8.793025871282323e+00, /* 426 */ + 8.798106389361616e+00, /* 427 */ + 8.803189842910941e+00, /* 428 */ + 8.808276233626385e+00, /* 429 */ + 8.813365563205011e+00, /* 430 */ + 8.818457833344862e+00, /* 431 */ + 8.823553045744967e+00, /* 432 */ + 8.828651202105329e+00, /* 433 */ + 8.833752304126937e+00, /* 434 */ + 8.838856353511765e+00, /* 435 */ + 8.843963351962772e+00, /* 436 */ + 8.849073301183893e+00, /* 437 */ + 8.854186202880049e+00, /* 438 */ + 8.859302058757157e+00, /* 439 */ + 8.864420870522107e+00, /* 440 */ + 8.869542639882779e+00, /* 441 */ + 8.874667368548046e+00, /* 442 */ + 8.879795058227756e+00, /* 443 */ + 8.884925710632759e+00, /* 444 */ + 8.890059327474884e+00, /* 445 */ + 8.895195910466947e+00, /* 446 */ + 8.900335461322765e+00, /* 447 */ + 8.905477981757135e+00, /* 448 */ + 8.910623473485851e+00, /* 449 */ + 8.915771938225692e+00, /* 450 */ + 8.920923377694436e+00, /* 451 */ + 8.926077793610846e+00, /* 452 */ + 8.931235187694687e+00, /* 453 */ + 8.936395561666711e+00, /* 454 */ + 8.941558917248665e+00, /* 455 */ + 8.946725256163294e+00, /* 456 */ + 8.951894580134333e+00, /* 457 */ + 8.957066890886521e+00, /* 458 */ + 8.962242190145584e+00, /* 459 */ + 8.967420479638253e+00, /* 460 */ + 8.972601761092253e+00, /* 461 */ + 8.977786036236310e+00, /* 462 */ + 8.982973306800140e+00, /* 463 */ + 8.988163574514472e+00, /* 464 */ + 8.993356841111027e+00, /* 465 */ + 8.998553108322524e+00, /* 466 */ + 9.003752377882689e+00, /* 467 */ + 9.008954651526247e+00, /* 468 */ + 9.014159930988928e+00, /* 469 */ + 9.019368218007461e+00, /* 470 */ + 9.024579514319580e+00, /* 471 */ + 9.029793821664024e+00, /* 472 */ + 9.035011141780535e+00, /* 473 */ + 9.040231476409863e+00, /* 474 */ + 9.045454827293758e+00, /* 475 */ + 9.050681196174985e+00, /* 476 */ + 9.055910584797308e+00, /* 477 */ + 9.061142994905504e+00, /* 478 */ + 9.066378428245352e+00, /* 479 */ + 9.071616886563648e+00, /* 480 */ + 9.076858371608189e+00, /* 481 */ + 9.082102885127791e+00, /* 482 */ + 9.087350428872268e+00, /* 483 */ + 9.092601004592458e+00, /* 484 */ + 9.097854614040202e+00, /* 485 */ + 9.103111258968356e+00, /* 486 */ + 9.108370941130790e+00, /* 487 */ + 9.113633662282384e+00, /* 488 */ + 9.118899424179034e+00, /* 489 */ + 9.124168228577656e+00, /* 490 */ + 9.129440077236170e+00, /* 491 */ + 9.134714971913517e+00, /* 492 */ + 9.139992914369659e+00, /* 493 */ + 9.145273906365567e+00, /* 494 */ + 9.150557949663234e+00, /* 495 */ + 9.155845046025672e+00, /* 496 */ + 9.161135197216907e+00, /* 497 */ + 9.166428405001989e+00, /* 498 */ + 9.171724671146988e+00, /* 499 */ + 9.177023997418987e+00, /* 500 */ + 9.182326385586096e+00, /* 501 */ + 9.187631837417451e+00, /* 502 */ + 9.192940354683200e+00, /* 503 */ + 9.198251939154520e+00, /* 504 */ + 9.203566592603613e+00, /* 505 */ + 9.208884316803697e+00, /* 506 */ + 9.214205113529024e+00, /* 507 */ + 9.219528984554866e+00, /* 508 */ + 9.224855931657519e+00, /* 509 */ + 9.230185956614312e+00, /* 510 */ + 9.235519061203593e+00, /* 511 */ + 9.240855247204744e+00, /* 512 */ + 9.246194516398171e+00, /* 513 */ + 9.251536870565312e+00, /* 514 */ + 9.256882311488628e+00, /* 515 */ + 9.262230840951620e+00, /* 516 */ + 9.267582460738812e+00, /* 517 */ + 9.272937172635757e+00, /* 518 */ + 9.278294978429049e+00, /* 519 */ + 9.283655879906307e+00, /* 520 */ + 9.289019878856182e+00, /* 521 */ + 9.294386977068365e+00, /* 522 */ + 9.299757176333575e+00, /* 523 */ + 9.305130478443569e+00, /* 524 */ + 9.310506885191137e+00, /* 525 */ + 9.315886398370107e+00, /* 526 */ + 9.321269019775343e+00, /* 527 */ + 9.326654751202744e+00, /* 528 */ + 9.332043594449249e+00, /* 529 */ + 9.337435551312833e+00, /* 530 */ + 9.342830623592516e+00, /* 531 */ + 9.348228813088346e+00, /* 532 */ + 9.353630121601423e+00, /* 533 */ + 9.359034550933879e+00, /* 534 */ + 9.364442102888892e+00, /* 535 */ + 9.369852779270683e+00, /* 536 */ + 9.375266581884510e+00, /* 537 */ + 9.380683512536677e+00, /* 538 */ + 9.386103573034534e+00, /* 539 */ + 9.391526765186470e+00, /* 540 */ + 9.396953090801922e+00, /* 541 */ + 9.402382551691376e+00, /* 542 */ + 9.407815149666359e+00, /* 543 */ + 9.413250886539442e+00, /* 544 */ + 9.418689764124254e+00, /* 545 */ + 9.424131784235461e+00, /* 546 */ + 9.429576948688783e+00, /* 547 */ + 9.435025259300987e+00, /* 548 */ + 9.440476717889890e+00, /* 549 */ + 9.445931326274362e+00, /* 550 */ + 9.451389086274322e+00, /* 551 */ + 9.456849999710739e+00, /* 552 */ + 9.462314068405634e+00, /* 553 */ + 9.467781294182085e+00, /* 554 */ + 9.473251678864221e+00, /* 555 */ + 9.478725224277222e+00, /* 556 */ + 9.484201932247325e+00, /* 557 */ + 9.489681804601824e+00, /* 558 */ + 9.495164843169070e+00, /* 559 */ + 9.500651049778460e+00, /* 560 */ + 9.506140426260462e+00, /* 561 */ + 9.511632974446592e+00, /* 562 */ + 9.517128696169429e+00, /* 563 */ + 9.522627593262607e+00, /* 564 */ + 9.528129667560824e+00, /* 565 */ + 9.533634920899834e+00, /* 566 */ + 9.539143355116456e+00, /* 567 */ + 9.544654972048564e+00, /* 568 */ + 9.550169773535101e+00, /* 569 */ + 9.555687761416067e+00, /* 570 */ + 9.561208937532529e+00, /* 571 */ + 9.566733303726613e+00, /* 572 */ + 9.572260861841514e+00, /* 573 */ + 9.577791613721493e+00, /* 574 */ + 9.583325561211870e+00, /* 575 */ + 9.588862706159038e+00, /* 576 */ + 9.594403050410451e+00, /* 577 */ + 9.599946595814636e+00, /* 578 */ + 9.605493344221186e+00, /* 579 */ + 9.611043297480759e+00, /* 580 */ + 9.616596457445088e+00, /* 581 */ + 9.622152825966971e+00, /* 582 */ + 9.627712404900283e+00, /* 583 */ + 9.633275196099962e+00, /* 584 */ + 9.638841201422023e+00, /* 585 */ + 9.644410422723555e+00, /* 586 */ + 9.649982861862712e+00, /* 587 */ + 9.655558520698731e+00, /* 588 */ + 9.661137401091917e+00, /* 589 */ + 9.666719504903652e+00, /* 590 */ + 9.672304833996394e+00, /* 591 */ + 9.677893390233677e+00, /* 592 */ + 9.683485175480111e+00, /* 593 */ + 9.689080191601384e+00, /* 594 */ + 9.694678440464260e+00, /* 595 */ + 9.700279923936582e+00, /* 596 */ + 9.705884643887279e+00, /* 597 */ + 9.711492602186349e+00, /* 598 */ + 9.717103800704876e+00, /* 599 */ + 9.722718241315029e+00, /* 600 */ + 9.728335925890050e+00, /* 601 */ + 9.733956856304269e+00, /* 602 */ + 9.739581034433099e+00, /* 603 */ + 9.745208462153036e+00, /* 604 */ + 9.750839141341658e+00, /* 605 */ + 9.756473073877629e+00, /* 606 */ + 9.762110261640702e+00, /* 607 */ + 9.767750706511709e+00, /* 608 */ + 9.773394410372575e+00, /* 609 */ + 9.779041375106310e+00, /* 610 */ + 9.784691602597011e+00, /* 611 */ + 9.790345094729869e+00, /* 612 */ + 9.796001853391154e+00, /* 613 */ + 9.801661880468235e+00, /* 614 */ + 9.807325177849568e+00, /* 615 */ + 9.812991747424702e+00, /* 616 */ + 9.818661591084274e+00, /* 617 */ + 9.824334710720015e+00, /* 618 */ + 9.830011108224751e+00, /* 619 */ + 9.835690785492401e+00, /* 620 */ + 9.841373744417977e+00, /* 621 */ + 9.847059986897586e+00, /* 622 */ + 9.852749514828432e+00, /* 623 */ + 9.858442330108813e+00, /* 624 */ + 9.864138434638127e+00, /* 625 */ + 9.869837830316865e+00, /* 626 */ + 9.875540519046620e+00, /* 627 */ + 9.881246502730082e+00, /* 628 */ + 9.886955783271043e+00, /* 629 */ + 9.892668362574389e+00, /* 630 */ + 9.898384242546111e+00, /* 631 */ + 9.904103425093300e+00, /* 632 */ + 9.909825912124155e+00, /* 633 */ + 9.915551705547966e+00, /* 634 */ + 9.921280807275133e+00, /* 635 */ + 9.927013219217162e+00, /* 636 */ + 9.932748943286656e+00, /* 637 */ + 9.938487981397332e+00, /* 638 */ + 9.944230335464004e+00, /* 639 */ + 9.949976007402601e+00, /* 640 */ + 9.955724999130149e+00, /* 641 */ + 9.961477312564792e+00, /* 642 */ + 9.967232949625778e+00, /* 643 */ + 9.972991912233457e+00, /* 644 */ + 9.978754202309304e+00, /* 645 */ + 9.984519821775889e+00, /* 646 */ + 9.990288772556898e+00, /* 647 */ + 9.996061056577135e+00, /* 648 */ + 1.000183667576251e+01, /* 649 */ + 1.000761563204004e+01, /* 650 */ + 1.001339792733787e+01, /* 651 */ + 1.001918356358524e+01, /* 652 */ + 1.002497254271253e+01, /* 653 */ + 1.003076486665121e+01, /* 654 */ + 1.003656053733387e+01, /* 655 */ + 1.004235955669425e+01, /* 656 */ + 1.004816192666716e+01, /* 657 */ + 1.005396764918855e+01, /* 658 */ + 1.005977672619549e+01, /* 659 */ + 1.006558915962617e+01, /* 660 */ + 1.007140495141990e+01, /* 661 */ + 1.007722410351709e+01, /* 662 */ + 1.008304661785931e+01, /* 663 */ + 1.008887249638921e+01, /* 664 */ + 1.009470174105059e+01, /* 665 */ + 1.010053435378838e+01, /* 666 */ + 1.010637033654859e+01, /* 667 */ + 1.011220969127841e+01, /* 668 */ + 1.011805241992611e+01, /* 669 */ + 1.012389852444111e+01, /* 670 */ + 1.012974800677396e+01, /* 671 */ + 1.013560086887632e+01, /* 672 */ + 1.014145711270099e+01, /* 673 */ + 1.014731674020188e+01, /* 674 */ + 1.015317975333406e+01, /* 675 */ + 1.015904615405370e+01, /* 676 */ + 1.016491594431812e+01, /* 677 */ + 1.017078912608576e+01, /* 678 */ + 1.017666570131619e+01, /* 679 */ + 1.018254567197013e+01, /* 680 */ + 1.018842904000941e+01, /* 681 */ + 1.019431580739701e+01, /* 682 */ + 1.020020597609703e+01, /* 683 */ + 1.020609954807471e+01, /* 684 */ + 1.021199652529644e+01, /* 685 */ + 1.021789690972974e+01, /* 686 */ + 1.022380070334324e+01, /* 687 */ + 1.022970790810674e+01, /* 688 */ + 1.023561852599116e+01, /* 689 */ + 1.024153255896858e+01, /* 690 */ + 1.024745000901219e+01, /* 691 */ + 1.025337087809634e+01, /* 692 */ + 1.025929516819652e+01, /* 693 */ + 1.026522288128936e+01, /* 694 */ + 1.027115401935261e+01, /* 695 */ + 1.027708858436520e+01, /* 696 */ + 1.028302657830718e+01, /* 697 */ + 1.028896800315975e+01, /* 698 */ + 1.029491286090526e+01, /* 699 */ + 1.030086115352719e+01, /* 700 */ + 1.030681288301017e+01, /* 701 */ + 1.031276805134000e+01, /* 702 */ + 1.031872666050360e+01, /* 703 */ + 1.032468871248905e+01, /* 704 */ + 1.033065420928557e+01, /* 705 */ + 1.033662315288354e+01, /* 706 */ + 1.034259554527449e+01, /* 707 */ + 1.034857138845109e+01, /* 708 */ + 1.035455068440717e+01, /* 709 */ + 1.036053343513771e+01, /* 710 */ + 1.036651964263884e+01, /* 711 */ + 1.037250930890785e+01, /* 712 */ + 1.037850243594318e+01, /* 713 */ + 1.038449902574443e+01, /* 714 */ + 1.039049908031233e+01, /* 715 */ + 1.039650260164880e+01, /* 716 */ + 1.040250959175691e+01, /* 717 */ + 1.040852005264086e+01, /* 718 */ + 1.041453398630604e+01, /* 719 */ + 1.042055139475899e+01, /* 720 */ + 1.042657228000740e+01, /* 721 */ + 1.043259664406012e+01, /* 722 */ + 1.043862448892718e+01, /* 723 */ + 1.044465581661975e+01, /* 724 */ + 1.045069062915016e+01, /* 725 */ + 1.045672892853194e+01, /* 726 */ + 1.046277071677973e+01, /* 727 */ + 1.046881599590938e+01, /* 728 */ + 1.047486476793787e+01, /* 729 */ + 1.048091703488336e+01, /* 730 */ + 1.048697279876519e+01, /* 731 */ + 1.049303206160384e+01, /* 732 */ + 1.049909482542098e+01, /* 733 */ + 1.050516109223943e+01, /* 734 */ + 1.051123086408320e+01, /* 735 */ + 1.051730414297744e+01, /* 736 */ + 1.052338093094850e+01, /* 737 */ + 1.052946123002388e+01, /* 738 */ + 1.053554504223227e+01, /* 739 */ + 1.054163236960351e+01, /* 740 */ + 1.054772321416862e+01, /* 741 */ + 1.055381757795981e+01, /* 742 */ + 1.055991546301045e+01, /* 743 */ + 1.056601687135509e+01, /* 744 */ + 1.057212180502944e+01, /* 745 */ + 1.057823026607040e+01, /* 746 */ + 1.058434225651606e+01, /* 747 */ + 1.059045777840566e+01, /* 748 */ + 1.059657683377963e+01, /* 749 */ + 1.060269942467959e+01, /* 750 */ + 1.060882555314833e+01, /* 751 */ + 1.061495522122981e+01, /* 752 */ + 1.062108843096918e+01, /* 753 */ + 1.062722518441279e+01, /* 754 */ + 1.063336548360814e+01, /* 755 */ + 1.063950933060393e+01, /* 756 */ + 1.064565672745005e+01, /* 757 */ + 1.065180767619755e+01, /* 758 */ + 1.065796217889870e+01, /* 759 */ + 1.066412023760692e+01, /* 760 */ + 1.067028185437685e+01, /* 761 */ + 1.067644703126430e+01, /* 762 */ + 1.068261577032625e+01, /* 763 */ + 1.068878807362090e+01, /* 764 */ + 1.069496394320763e+01, /* 765 */ + 1.070114338114699e+01, /* 766 */ + 1.070732638950076e+01, /* 767 */ + 1.071351297033187e+01, /* 768 */ + 1.071970312570447e+01, /* 769 */ + 1.072589685768389e+01, /* 770 */ + 1.073209416833664e+01, /* 771 */ + 1.073829505973047e+01, /* 772 */ + 1.074449953393427e+01, /* 773 */ + 1.075070759301816e+01, /* 774 */ + 1.075691923905345e+01, /* 775 */ + 1.076313447411263e+01, /* 776 */ + 1.076935330026941e+01, /* 777 */ + 1.077557571959869e+01, /* 778 */ + 1.078180173417656e+01, /* 779 */ + 1.078803134608032e+01, /* 780 */ + 1.079426455738847e+01, /* 781 */ + 1.080050137018071e+01, /* 782 */ + 1.080674178653793e+01, /* 783 */ + 1.081298580854224e+01, /* 784 */ + 1.081923343827694e+01, /* 785 */ + 1.082548467782655e+01, /* 786 */ + 1.083173952927677e+01, /* 787 */ + 1.083799799471452e+01, /* 788 */ + 1.084426007622793e+01, /* 789 */ + 1.085052577590632e+01, /* 790 */ + 1.085679509584024e+01, /* 791 */ + 1.086306803812144e+01, /* 792 */ + 1.086934460484285e+01, /* 793 */ + 1.087562479809866e+01, /* 794 */ + 1.088190861998423e+01, /* 795 */ + 1.088819607259615e+01, /* 796 */ + 1.089448715803220e+01, /* 797 */ + 1.090078187839141e+01, /* 798 */ + 1.090708023577399e+01, /* 799 */ + 1.091338223228137e+01, /* 800 */ + 1.091968787001621e+01, /* 801 */ + 1.092599715108235e+01, /* 802 */ + 1.093231007758490e+01, /* 803 */ + 1.093862665163013e+01, /* 804 */ + 1.094494687532556e+01, /* 805 */ + 1.095127075077993e+01, /* 806 */ + 1.095759828010317e+01, /* 807 */ + 1.096392946540646e+01, /* 808 */ + 1.097026430880218e+01, /* 809 */ + 1.097660281240394e+01, /* 810 */ + 1.098294497832656e+01, /* 811 */ + 1.098929080868611e+01, /* 812 */ + 1.099564030559985e+01, /* 813 */ + 1.100199347118627e+01, /* 814 */ + 1.100835030756511e+01, /* 815 */ + 1.101471081685730e+01, /* 816 */ + 1.102107500118502e+01, /* 817 */ + 1.102744286267166e+01, /* 818 */ + 1.103381440344184e+01, /* 819 */ + 1.104018962562143e+01, /* 820 */ + 1.104656853133749e+01, /* 821 */ + 1.105295112271833e+01, /* 822 */ + 1.105933740189350e+01, /* 823 */ + 1.106572737099377e+01, /* 824 */ + 1.107212103215112e+01, /* 825 */ + 1.107851838749881e+01, /* 826 */ + 1.108491943917128e+01, /* 827 */ + 1.109132418930423e+01, /* 828 */ + 1.109773264003461e+01, /* 829 */ + 1.110414479350058e+01, /* 830 */ + 1.111056065184153e+01, /* 831 */ + 1.111698021719810e+01, /* 832 */ + 1.112340349171218e+01, /* 833 */ + 1.112983047752687e+01, /* 834 */ + 1.113626117678652e+01, /* 835 */ + 1.114269559163672e+01, /* 836 */ + 1.114913372422430e+01, /* 837 */ + 1.115557557669733e+01, /* 838 */ + 1.116202115120513e+01, /* 839 */ + 1.116847044989824e+01, /* 840 */ + 1.117492347492846e+01, /* 841 */ + 1.118138022844883e+01, /* 842 */ + 1.118784071261362e+01, /* 843 */ + 1.119430492957838e+01, /* 844 */ + 1.120077288149987e+01, /* 845 */ + 1.120724457053610e+01, /* 846 */ + 1.121371999884635e+01, /* 847 */ + 1.122019916859113e+01, /* 848 */ + 1.122668208193219e+01, /* 849 */ + 1.123316874103256e+01, /* 850 */ + 1.123965914805649e+01, /* 851 */ + 1.124615330516949e+01, /* 852 */ + 1.125265121453833e+01, /* 853 */ + 1.125915287833101e+01, /* 854 */ + 1.126565829871680e+01, /* 855 */ + 1.127216747786624e+01, /* 856 */ + 1.127868041795107e+01, /* 857 */ + 1.128519712114435e+01, /* 858 */ + 1.129171758962035e+01, /* 859 */ + 1.129824182555462e+01, /* 860 */ + 1.130476983112394e+01, /* 861 */ + 1.131130160850638e+01, /* 862 */ + 1.131783715988125e+01, /* 863 */ + 1.132437648742913e+01, /* 864 */ + 1.133091959333184e+01, /* 865 */ + 1.133746647977249e+01, /* 866 */ + 1.134401714893542e+01, /* 867 */ + 1.135057160300625e+01, /* 868 */ + 1.135712984417187e+01, /* 869 */ + 1.136369187462041e+01, /* 870 */ + 1.137025769654129e+01, /* 871 */ + 1.137682731212518e+01, /* 872 */ + 1.138340072356401e+01, /* 873 */ + 1.138997793305099e+01, /* 874 */ + 1.139655894278060e+01, /* 875 */ + 1.140314375494857e+01, /* 876 */ + 1.140973237175192e+01, /* 877 */ + 1.141632479538892e+01, /* 878 */ + 1.142292102805911e+01, /* 879 */ + 1.142952107196333e+01, /* 880 */ + 1.143612492930366e+01, /* 881 */ + 1.144273260228346e+01, /* 882 */ + 1.144934409310737e+01, /* 883 */ + 1.145595940398131e+01, /* 884 */ + 1.146257853711245e+01, /* 885 */ + 1.146920149470925e+01, /* 886 */ + 1.147582827898146e+01, /* 887 */ + 1.148245889214008e+01, /* 888 */ + 1.148909333639740e+01, /* 889 */ + 1.149573161396701e+01, /* 890 */ + 1.150237372706373e+01, /* 891 */ + 1.150901967790369e+01, /* 892 */ + 1.151566946870432e+01, /* 893 */ + 1.152232310168429e+01, /* 894 */ + 1.152898057906358e+01, /* 895 */ + 1.153564190306344e+01, /* 896 */ + 1.154230707590640e+01, /* 897 */ + 1.154897609981630e+01, /* 898 */ + 1.155564897701822e+01, /* 899 */ + 1.156232570973857e+01, /* 900 */ + 1.156900630020503e+01, /* 901 */ + 1.157569075064656e+01, /* 902 */ + 1.158237906329340e+01, /* 903 */ + 1.158907124037712e+01, /* 904 */ + 1.159576728413052e+01, /* 905 */ + 1.160246719678775e+01, /* 906 */ + 1.160917098058420e+01, /* 907 */ + 1.161587863775658e+01, /* 908 */ + 1.162259017054289e+01, /* 909 */ + 1.162930558118242e+01, /* 910 */ + 1.163602487191574e+01, /* 911 */ + 1.164274804498475e+01, /* 912 */ + 1.164947510263260e+01, /* 913 */ + 1.165620604710378e+01, /* 914 */ + 1.166294088064403e+01, /* 915 */ + 1.166967960550044e+01, /* 916 */ + 1.167642222392135e+01, /* 917 */ + 1.168316873815644e+01, /* 918 */ + 1.168991915045666e+01, /* 919 */ + 1.169667346307427e+01, /* 920 */ + 1.170343167826283e+01, /* 921 */ + 1.171019379827721e+01, /* 922 */ + 1.171695982537358e+01, /* 923 */ + 1.172372976180941e+01, /* 924 */ + 1.173050360984346e+01, /* 925 */ + 1.173728137173583e+01, /* 926 */ + 1.174406304974791e+01, /* 927 */ + 1.175084864614237e+01, /* 928 */ + 1.175763816318322e+01, /* 929 */ + 1.176443160313578e+01, /* 930 */ + 1.177122896826666e+01, /* 931 */ + 1.177803026084377e+01, /* 932 */ + 1.178483548313637e+01, /* 933 */ + 1.179164463741501e+01, /* 934 */ + 1.179845772595153e+01, /* 935 */ + 1.180527475101911e+01, /* 936 */ + 1.181209571489225e+01, /* 937 */ + 1.181892061984674e+01, /* 938 */ + 1.182574946815969e+01, /* 939 */ + 1.183258226210954e+01, /* 940 */ + 1.183941900397603e+01, /* 941 */ + 1.184625969604024e+01, /* 942 */ + 1.185310434058453e+01, /* 943 */ + 1.185995293989262e+01, /* 944 */ + 1.186680549624953e+01, /* 945 */ + 1.187366201194158e+01, /* 946 */ + 1.188052248925647e+01, /* 947 */ + 1.188738693048315e+01, /* 948 */ + 1.189425533791194e+01, /* 949 */ + 1.190112771383447e+01, /* 950 */ + 1.190800406054369e+01, /* 951 */ + 1.191488438033388e+01, /* 952 */ + 1.192176867550066e+01, /* 953 */ + 1.192865694834093e+01, /* 954 */ + 1.193554920115298e+01, /* 955 */ + 1.194244543623637e+01, /* 956 */ + 1.194934565589204e+01, /* 957 */ + 1.195624986242221e+01, /* 958 */ + 1.196315805813046e+01, /* 959 */ + 1.197007024532171e+01, /* 960 */ + 1.197698642630218e+01, /* 961 */ + 1.198390660337945e+01, /* 962 */ + 1.199083077886241e+01, /* 963 */ + 1.199775895506131e+01, /* 964 */ + 1.200469113428772e+01, /* 965 */ + 1.201162731885455e+01, /* 966 */ + 1.201856751107603e+01, /* 967 */ + 1.202551171326775e+01, /* 968 */ + 1.203245992774663e+01, /* 969 */ + 1.203941215683092e+01, /* 970 */ + 1.204636840284023e+01, /* 971 */ + 1.205332866809548e+01, /* 972 */ + 1.206029295491897e+01, /* 973 */ + 1.206726126563430e+01, /* 974 */ + 1.207423360256643e+01, /* 975 */ + 1.208120996804169e+01, /* 976 */ + 1.208819036438771e+01, /* 977 */ + 1.209517479393349e+01, /* 978 */ + 1.210216325900937e+01, /* 979 */ + 1.210915576194704e+01, /* 980 */ + 1.211615230507953e+01, /* 981 */ + 1.212315289074123e+01, /* 982 */ + 1.213015752126786e+01, /* 983 */ + 1.213716619899650e+01, /* 984 */ + 1.214417892626560e+01, /* 985 */ + 1.215119570541492e+01, /* 986 */ + 1.215821653878560e+01, /* 987 */ + 1.216524142872013e+01, /* 988 */ + 1.217227037756235e+01, /* 989 */ + 1.217930338765746e+01, /* 990 */ + 1.218634046135199e+01, /* 991 */ + 1.219338160099387e+01, /* 992 */ + 1.220042680893234e+01, /* 993 */ + 1.220747608751803e+01, /* 994 */ + 1.221452943910292e+01, /* 995 */ + 1.222158686604034e+01, /* 996 */ + 1.222864837068499e+01, /* 997 */ + 1.223571395539292e+01, /* 998 */ + 1.224278362252155e+01, /* 999 */ + 1.224985737442966e+01, /* 1000 */ + 1.225693521347741e+01, /* 1001 */ + 1.226401714202627e+01, /* 1002 */ + 1.227110316243914e+01, /* 1003 */ + 1.227819327708026e+01, /* 1004 */ + 1.228528748831521e+01, /* 1005 */ + 1.229238579851096e+01, /* 1006 */ + 1.229948821003587e+01, /* 1007 */ + 1.230659472525962e+01, /* 1008 */ + 1.231370534655330e+01, /* 1009 */ + 1.232082007628935e+01, /* 1010 */ + 1.232793891684158e+01, /* 1011 */ + 1.233506187058518e+01, /* 1012 */ + 1.234218893989670e+01, /* 1013 */ + 1.234932012715410e+01, /* 1014 */ + 1.235645543473665e+01, /* 1015 */ + 1.236359486502506e+01, /* 1016 */ + 1.237073842040136e+01, /* 1017 */ + 1.237788610324901e+01, /* 1018 */ + 1.238503791595280e+01, /* 1019 */ + 1.239219386089892e+01, /* 1020 */ + 1.239935394047494e+01, /* 1021 */ + 1.240651815706980e+01, /* 1022 */ + 1.241368651307384e+01, /* 1023 */ + 1.242085901087876e+01, /* 1024 */ + 1.242803565287764e+01, /* 1025 */ + 1.243521644146496e+01, /* 1026 */ + 1.244240137903658e+01, /* 1027 */ + 1.244959046798973e+01, /* 1028 */ + 1.245678371072304e+01, /* 1029 */ + 1.246398110963652e+01, /* 1030 */ + 1.247118266713156e+01, /* 1031 */ + 1.247838838561096e+01, /* 1032 */ + 1.248559826747888e+01, /* 1033 */ + 1.249281231514089e+01, /* 1034 */ + 1.250003053100394e+01, /* 1035 */ + 1.250725291747637e+01, /* 1036 */ + 1.251447947696791e+01, /* 1037 */ + 1.252171021188970e+01, /* 1038 */ + 1.252894512465425e+01, /* 1039 */ + 1.253618421767548e+01, /* 1040 */ + 1.254342749336869e+01, /* 1041 */ + 1.255067495415059e+01, /* 1042 */ + 1.255792660243928e+01, /* 1043 */ + 1.256518244065426e+01, /* 1044 */ + 1.257244247121642e+01, /* 1045 */ + 1.257970669654805e+01, /* 1046 */ + 1.258697511907285e+01, /* 1047 */ + 1.259424774121592e+01, /* 1048 */ + 1.260152456540375e+01, /* 1049 */ + 1.260880559406423e+01, /* 1050 */ + 1.261609082962667e+01, /* 1051 */ + 1.262338027452177e+01, /* 1052 */ + 1.263067393118164e+01, /* 1053 */ + 1.263797180203979e+01, /* 1054 */ + 1.264527388953115e+01, /* 1055 */ + 1.265258019609203e+01, /* 1056 */ + 1.265989072416018e+01, /* 1057 */ + 1.266720547617473e+01, /* 1058 */ + 1.267452445457624e+01, /* 1059 */ + 1.268184766180666e+01, /* 1060 */ + 1.268917510030938e+01, /* 1061 */ + 1.269650677252918e+01, /* 1062 */ + 1.270384268091225e+01, /* 1063 */ + 1.271118282790620e+01, /* 1064 */ + 1.271852721596007e+01, /* 1065 */ + 1.272587584752428e+01, /* 1066 */ + 1.273322872505070e+01, /* 1067 */ + 1.274058585099260e+01, /* 1068 */ + 1.274794722780466e+01, /* 1069 */ + 1.275531285794301e+01, /* 1070 */ + 1.276268274386515e+01, /* 1071 */ + 1.277005688803004e+01, /* 1072 */ + 1.277743529289805e+01, /* 1073 */ + 1.278481796093097e+01, /* 1074 */ + 1.279220489459202e+01, /* 1075 */ + 1.279959609634581e+01, /* 1076 */ + 1.280699156865842e+01, /* 1077 */ + 1.281439131399733e+01, /* 1078 */ + 1.282179533483144e+01, /* 1079 */ + 1.282920363363110e+01, /* 1080 */ + 1.283661621286807e+01, /* 1081 */ + 1.284403307501554e+01, /* 1082 */ + 1.285145422254812e+01, /* 1083 */ + 1.285887965794188e+01, /* 1084 */ + 1.286630938367429e+01, /* 1085 */ + 1.287374340222427e+01, /* 1086 */ + 1.288118171607215e+01, /* 1087 */ + 1.288862432769973e+01, /* 1088 */ + 1.289607123959020e+01, /* 1089 */ + 1.290352245422822e+01, /* 1090 */ + 1.291097797409987e+01, /* 1091 */ + 1.291843780169266e+01, /* 1092 */ + 1.292590193949556e+01, /* 1093 */ + 1.293337038999896e+01, /* 1094 */ + 1.294084315569469e+01, /* 1095 */ + 1.294832023907602e+01, /* 1096 */ + 1.295580164263767e+01, /* 1097 */ + 1.296328736887579e+01, /* 1098 */ + 1.297077742028799e+01, /* 1099 */ + 1.297827179937329e+01, /* 1100 */ + 1.298577050863218e+01, /* 1101 */ + 1.299327355056660e+01, /* 1102 */ + 1.300078092767991e+01, /* 1103 */ + 1.300829264247694e+01, /* 1104 */ + 1.301580869746396e+01, /* 1105 */ + 1.302332909514868e+01, /* 1106 */ + 1.303085383804027e+01, /* 1107 */ + 1.303838292864934e+01, /* 1108 */ + 1.304591636948796e+01, /* 1109 */ + 1.305345416306964e+01, /* 1110 */ + 1.306099631190936e+01, /* 1111 */ + 1.306854281852353e+01, /* 1112 */ + 1.307609368543003e+01, /* 1113 */ + 1.308364891514820e+01, /* 1114 */ + 1.309120851019883e+01, /* 1115 */ + 1.309877247310414e+01, /* 1116 */ + 1.310634080638785e+01, /* 1117 */ + 1.311391351257511e+01, /* 1118 */ + 1.312149059419255e+01, /* 1119 */ + 1.312907205376823e+01, /* 1120 */ + 1.313665789383170e+01, /* 1121 */ + 1.314424811691396e+01, /* 1122 */ + 1.315184272554746e+01, /* 1123 */ + 1.315944172226614e+01, /* 1124 */ + 1.316704510960539e+01, /* 1125 */ + 1.317465289010205e+01, /* 1126 */ + 1.318226506629446e+01, /* 1127 */ + 1.318988164072239e+01, /* 1128 */ + 1.319750261592709e+01, /* 1129 */ + 1.320512799445131e+01, /* 1130 */ + 1.321275777883922e+01, /* 1131 */ + 1.322039197163648e+01, /* 1132 */ + 1.322803057539023e+01, /* 1133 */ + 1.323567359264908e+01, /* 1134 */ + 1.324332102596310e+01, /* 1135 */ + 1.325097287788384e+01, /* 1136 */ + 1.325862915096432e+01, /* 1137 */ + 1.326628984775905e+01, /* 1138 */ + 1.327395497082400e+01, /* 1139 */ + 1.328162452271663e+01, /* 1140 */ + 1.328929850599585e+01, /* 1141 */ + 1.329697692322209e+01, /* 1142 */ + 1.330465977695723e+01, /* 1143 */ + 1.331234706976464e+01, /* 1144 */ + 1.332003880420917e+01, /* 1145 */ + 1.332773498285714e+01, /* 1146 */ + 1.333543560827638e+01, /* 1147 */ + 1.334314068303618e+01, /* 1148 */ + 1.335085020970733e+01, /* 1149 */ + 1.335856419086208e+01, /* 1150 */ + 1.336628262907420e+01, /* 1151 */ + 1.337400552691893e+01, /* 1152 */ + 1.338173288697299e+01, /* 1153 */ + 1.338946471181460e+01, /* 1154 */ + 1.339720100402347e+01, /* 1155 */ + 1.340494176618080e+01, /* 1156 */ + 1.341268700086928e+01, /* 1157 */ + 1.342043671067309e+01, /* 1158 */ + 1.342819089817790e+01, /* 1159 */ + 1.343594956597088e+01, /* 1160 */ + 1.344371271664070e+01, /* 1161 */ + 1.345148035277751e+01, /* 1162 */ + 1.345925247697298e+01, /* 1163 */ + 1.346702909182024e+01, /* 1164 */ + 1.347481019991397e+01, /* 1165 */ + 1.348259580385030e+01, /* 1166 */ + 1.349038590622688e+01, /* 1167 */ + 1.349818050964288e+01, /* 1168 */ + 1.350597961669893e+01, /* 1169 */ + 1.351378322999720e+01, /* 1170 */ + 1.352159135214135e+01, /* 1171 */ + 1.352940398573654e+01, /* 1172 */ + 1.353722113338944e+01, /* 1173 */ + 1.354504279770823e+01, /* 1174 */ + 1.355286898130258e+01, /* 1175 */ + 1.356069968678369e+01, /* 1176 */ + 1.356853491676425e+01, /* 1177 */ + 1.357637467385848e+01, /* 1178 */ + 1.358421896068210e+01, /* 1179 */ + 1.359206777985232e+01, /* 1180 */ + 1.359992113398790e+01, /* 1181 */ + 1.360777902570909e+01, /* 1182 */ + 1.361564145763767e+01, /* 1183 */ + 1.362350843239690e+01, /* 1184 */ + 1.363137995261160e+01, /* 1185 */ + 1.363925602090809e+01, /* 1186 */ + 1.364713663991418e+01, /* 1187 */ + 1.365502181225924e+01, /* 1188 */ + 1.366291154057414e+01, /* 1189 */ + 1.367080582749128e+01, /* 1190 */ + 1.367870467564455e+01, /* 1191 */ + 1.368660808766940e+01, /* 1192 */ + 1.369451606620278e+01, /* 1193 */ + 1.370242861388318e+01, /* 1194 */ + 1.371034573335060e+01, /* 1195 */ + 1.371826742724657e+01, /* 1196 */ + 1.372619369821415e+01, /* 1197 */ + 1.373412454889792e+01, /* 1198 */ + 1.374205998194399e+01, /* 1199 */ +}; + +static const fluid_real_t fluid_cb2amp_tab[1441] = { + 1.000000000000000e+00, /* 0 */ + 9.885530946569389e-01, /* 1 */ + 9.772372209558107e-01, /* 2 */ + 9.660508789898133e-01, /* 3 */ + 9.549925860214360e-01, /* 4 */ + 9.440608762859234e-01, /* 5 */ + 9.332543007969910e-01, /* 6 */ + 9.225714271547631e-01, /* 7 */ + 9.120108393559098e-01, /* 8 */ + 9.015711376059569e-01, /* 9 */ + 8.912509381337456e-01, /* 10 */ + 8.810488730080140e-01, /* 11 */ + 8.709635899560807e-01, /* 12 */ + 8.609937521846006e-01, /* 13 */ + 8.511380382023764e-01, /* 14 */ + 8.413951416451951e-01, /* 15 */ + 8.317637711026710e-01, /* 16 */ + 8.222426499470711e-01, /* 17 */ + 8.128305161640993e-01, /* 18 */ + 8.035261221856173e-01, /* 19 */ + 7.943282347242815e-01, /* 20 */ + 7.852356346100718e-01, /* 21 */ + 7.762471166286917e-01, /* 22 */ + 7.673614893618189e-01, /* 23 */ + 7.585775750291838e-01, /* 24 */ + 7.498942093324559e-01, /* 25 */ + 7.413102413009175e-01, /* 26 */ + 7.328245331389041e-01, /* 27 */ + 7.244359600749900e-01, /* 28 */ + 7.161434102129021e-01, /* 29 */ + 7.079457843841379e-01, /* 30 */ + 6.998419960022735e-01, /* 31 */ + 6.918309709189365e-01, /* 32 */ + 6.839116472814293e-01, /* 33 */ + 6.760829753919817e-01, /* 34 */ + 6.683439175686147e-01, /* 35 */ + 6.606934480075960e-01, /* 36 */ + 6.531305526474723e-01, /* 37 */ + 6.456542290346555e-01, /* 38 */ + 6.382634861905486e-01, /* 39 */ + 6.309573444801932e-01, /* 40 */ + 6.237348354824193e-01, /* 41 */ + 6.165950018614822e-01, /* 42 */ + 6.095368972401691e-01, /* 43 */ + 6.025595860743578e-01, /* 44 */ + 5.956621435290105e-01, /* 45 */ + 5.888436553555889e-01, /* 46 */ + 5.821032177708715e-01, /* 47 */ + 5.754399373371569e-01, /* 48 */ + 5.688529308438415e-01, /* 49 */ + 5.623413251903491e-01, /* 50 */ + 5.559042572704036e-01, /* 51 */ + 5.495408738576245e-01, /* 52 */ + 5.432503314924332e-01, /* 53 */ + 5.370317963702527e-01, /* 54 */ + 5.308844442309884e-01, /* 55 */ + 5.248074602497725e-01, /* 56 */ + 5.188000389289611e-01, /* 57 */ + 5.128613839913648e-01, /* 58 */ + 5.069907082747044e-01, /* 59 */ + 5.011872336272722e-01, /* 60 */ + 4.954501908047902e-01, /* 61 */ + 4.897788193684462e-01, /* 62 */ + 4.841723675840993e-01, /* 63 */ + 4.786300923226383e-01, /* 64 */ + 4.731512589614805e-01, /* 65 */ + 4.677351412871982e-01, /* 66 */ + 4.623810213992603e-01, /* 67 */ + 4.570881896148750e-01, /* 68 */ + 4.518559443749224e-01, /* 69 */ + 4.466835921509631e-01, /* 70 */ + 4.415704473533125e-01, /* 71 */ + 4.365158322401660e-01, /* 72 */ + 4.315190768277652e-01, /* 73 */ + 4.265795188015927e-01, /* 74 */ + 4.216965034285822e-01, /* 75 */ + 4.168693834703354e-01, /* 76 */ + 4.120975190973302e-01, /* 77 */ + 4.073802778041127e-01, /* 78 */ + 4.027170343254591e-01, /* 79 */ + 3.981071705534972e-01, /* 80 */ + 3.935500754557774e-01, /* 81 */ + 3.890451449942806e-01, /* 82 */ + 3.845917820453536e-01, /* 83 */ + 3.801893963205612e-01, /* 84 */ + 3.758374042884442e-01, /* 85 */ + 3.715352290971726e-01, /* 86 */ + 3.672823004980847e-01, /* 87 */ + 3.630780547701014e-01, /* 88 */ + 3.589219346450052e-01, /* 89 */ + 3.548133892335755e-01, /* 90 */ + 3.507518739525680e-01, /* 91 */ + 3.467368504525316e-01, /* 92 */ + 3.427677865464503e-01, /* 93 */ + 3.388441561392025e-01, /* 94 */ + 3.349654391578277e-01, /* 95 */ + 3.311311214825911e-01, /* 96 */ + 3.273406948788382e-01, /* 97 */ + 3.235936569296283e-01, /* 98 */ + 3.198895109691398e-01, /* 99 */ + 3.162277660168379e-01, /* 100 */ + 3.126079367123955e-01, /* 101 */ + 3.090295432513591e-01, /* 102 */ + 3.054921113215513e-01, /* 103 */ + 3.019951720402016e-01, /* 104 */ + 2.985382618917959e-01, /* 105 */ + 2.951209226666385e-01, /* 106 */ + 2.917427014001167e-01, /* 107 */ + 2.884031503126606e-01, /* 108 */ + 2.851018267503909e-01, /* 109 */ + 2.818382931264454e-01, /* 110 */ + 2.786121168629770e-01, /* 111 */ + 2.754228703338166e-01, /* 112 */ + 2.722701308077913e-01, /* 113 */ + 2.691534803926916e-01, /* 114 */ + 2.660725059798810e-01, /* 115 */ + 2.630267991895382e-01, /* 116 */ + 2.600159563165272e-01, /* 117 */ + 2.570395782768864e-01, /* 118 */ + 2.540972705549305e-01, /* 119 */ + 2.511886431509580e-01, /* 120 */ + 2.483133105295570e-01, /* 121 */ + 2.454708915685030e-01, /* 122 */ + 2.426610095082415e-01, /* 123 */ + 2.398832919019490e-01, /* 124 */ + 2.371373705661655e-01, /* 125 */ + 2.344228815319922e-01, /* 126 */ + 2.317394649968479e-01, /* 127 */ + 2.290867652767773e-01, /* 128 */ + 2.264644307593059e-01, /* 129 */ + 2.238721138568339e-01, /* 130 */ + 2.213094709605637e-01, /* 131 */ + 2.187761623949552e-01, /* 132 */ + 2.162718523727020e-01, /* 133 */ + 2.137962089502232e-01, /* 134 */ + 2.113489039836647e-01, /* 135 */ + 2.089296130854039e-01, /* 136 */ + 2.065380155810529e-01, /* 137 */ + 2.041737944669530e-01, /* 138 */ + 2.018366363681561e-01, /* 139 */ + 1.995262314968880e-01, /* 140 */ + 1.972422736114854e-01, /* 141 */ + 1.949844599758045e-01, /* 142 */ + 1.927524913190936e-01, /* 143 */ + 1.905460717963247e-01, /* 144 */ + 1.883649089489801e-01, /* 145 */ + 1.862087136662867e-01, /* 146 */ + 1.840772001468956e-01, /* 147 */ + 1.819700858609984e-01, /* 148 */ + 1.798870915128788e-01, /* 149 */ + 1.778279410038923e-01, /* 150 */ + 1.757923613958693e-01, /* 151 */ + 1.737800828749375e-01, /* 152 */ + 1.717908387157588e-01, /* 153 */ + 1.698243652461744e-01, /* 154 */ + 1.678804018122560e-01, /* 155 */ + 1.659586907437560e-01, /* 156 */ + 1.640589773199539e-01, /* 157 */ + 1.621810097358930e-01, /* 158 */ + 1.603245390690041e-01, /* 159 */ + 1.584893192461113e-01, /* 160 */ + 1.566751070108149e-01, /* 161 */ + 1.548816618912481e-01, /* 162 */ + 1.531087461682030e-01, /* 163 */ + 1.513561248436208e-01, /* 164 */ + 1.496235656094433e-01, /* 165 */ + 1.479108388168208e-01, /* 166 */ + 1.462177174456718e-01, /* 167 */ + 1.445439770745928e-01, /* 168 */ + 1.428893958511103e-01, /* 169 */ + 1.412537544622754e-01, /* 170 */ + 1.396368361055938e-01, /* 171 */ + 1.380384264602885e-01, /* 172 */ + 1.364583136588925e-01, /* 173 */ + 1.348962882591654e-01, /* 174 */ + 1.333521432163324e-01, /* 175 */ + 1.318256738556407e-01, /* 176 */ + 1.303166778452299e-01, /* 177 */ + 1.288249551693134e-01, /* 178 */ + 1.273503081016662e-01, /* 179 */ + 1.258925411794167e-01, /* 180 */ + 1.244514611771385e-01, /* 181 */ + 1.230268770812381e-01, /* 182 */ + 1.216186000646368e-01, /* 183 */ + 1.202264434617413e-01, /* 184 */ + 1.188502227437018e-01, /* 185 */ + 1.174897554939529e-01, /* 186 */ + 1.161448613840343e-01, /* 187 */ + 1.148153621496883e-01, /* 188 */ + 1.135010815672315e-01, /* 189 */ + 1.122018454301964e-01, /* 190 */ + 1.109174815262401e-01, /* 191 */ + 1.096478196143185e-01, /* 192 */ + 1.083926914021204e-01, /* 193 */ + 1.071519305237607e-01, /* 194 */ + 1.059253725177289e-01, /* 195 */ + 1.047128548050900e-01, /* 196 */ + 1.035142166679344e-01, /* 197 */ + 1.023292992280754e-01, /* 198 */ + 1.011579454259899e-01, /* 199 */ + 1.000000000000000e-01, /* 200 */ + 9.885530946569390e-02, /* 201 */ + 9.772372209558107e-02, /* 202 */ + 9.660508789898135e-02, /* 203 */ + 9.549925860214359e-02, /* 204 */ + 9.440608762859236e-02, /* 205 */ + 9.332543007969910e-02, /* 206 */ + 9.225714271547633e-02, /* 207 */ + 9.120108393559097e-02, /* 208 */ + 9.015711376059571e-02, /* 209 */ + 8.912509381337455e-02, /* 210 */ + 8.810488730080142e-02, /* 211 */ + 8.709635899560805e-02, /* 212 */ + 8.609937521846008e-02, /* 213 */ + 8.511380382023763e-02, /* 214 */ + 8.413951416451952e-02, /* 215 */ + 8.317637711026708e-02, /* 216 */ + 8.222426499470713e-02, /* 217 */ + 8.128305161640990e-02, /* 218 */ + 8.035261221856173e-02, /* 219 */ + 7.943282347242814e-02, /* 220 */ + 7.852356346100718e-02, /* 221 */ + 7.762471166286916e-02, /* 222 */ + 7.673614893618190e-02, /* 223 */ + 7.585775750291836e-02, /* 224 */ + 7.498942093324558e-02, /* 225 */ + 7.413102413009177e-02, /* 226 */ + 7.328245331389041e-02, /* 227 */ + 7.244359600749903e-02, /* 228 */ + 7.161434102129020e-02, /* 229 */ + 7.079457843841380e-02, /* 230 */ + 6.998419960022735e-02, /* 231 */ + 6.918309709189366e-02, /* 232 */ + 6.839116472814293e-02, /* 233 */ + 6.760829753919818e-02, /* 234 */ + 6.683439175686146e-02, /* 235 */ + 6.606934480075961e-02, /* 236 */ + 6.531305526474723e-02, /* 237 */ + 6.456542290346556e-02, /* 238 */ + 6.382634861905485e-02, /* 239 */ + 6.309573444801933e-02, /* 240 */ + 6.237348354824192e-02, /* 241 */ + 6.165950018614822e-02, /* 242 */ + 6.095368972401691e-02, /* 243 */ + 6.025595860743578e-02, /* 244 */ + 5.956621435290103e-02, /* 245 */ + 5.888436553555890e-02, /* 246 */ + 5.821032177708713e-02, /* 247 */ + 5.754399373371569e-02, /* 248 */ + 5.688529308438413e-02, /* 249 */ + 5.623413251903491e-02, /* 250 */ + 5.559042572704037e-02, /* 251 */ + 5.495408738576246e-02, /* 252 */ + 5.432503314924333e-02, /* 253 */ + 5.370317963702527e-02, /* 254 */ + 5.308844442309885e-02, /* 255 */ + 5.248074602497726e-02, /* 256 */ + 5.188000389289612e-02, /* 257 */ + 5.128613839913648e-02, /* 258 */ + 5.069907082747045e-02, /* 259 */ + 5.011872336272722e-02, /* 260 */ + 4.954501908047903e-02, /* 261 */ + 4.897788193684462e-02, /* 262 */ + 4.841723675840994e-02, /* 263 */ + 4.786300923226382e-02, /* 264 */ + 4.731512589614805e-02, /* 265 */ + 4.677351412871981e-02, /* 266 */ + 4.623810213992603e-02, /* 267 */ + 4.570881896148749e-02, /* 268 */ + 4.518559443749224e-02, /* 269 */ + 4.466835921509630e-02, /* 270 */ + 4.415704473533125e-02, /* 271 */ + 4.365158322401658e-02, /* 272 */ + 4.315190768277653e-02, /* 273 */ + 4.265795188015926e-02, /* 274 */ + 4.216965034285822e-02, /* 275 */ + 4.168693834703355e-02, /* 276 */ + 4.120975190973302e-02, /* 277 */ + 4.073802778041128e-02, /* 278 */ + 4.027170343254591e-02, /* 279 */ + 3.981071705534973e-02, /* 280 */ + 3.935500754557775e-02, /* 281 */ + 3.890451449942807e-02, /* 282 */ + 3.845917820453535e-02, /* 283 */ + 3.801893963205612e-02, /* 284 */ + 3.758374042884442e-02, /* 285 */ + 3.715352290971726e-02, /* 286 */ + 3.672823004980846e-02, /* 287 */ + 3.630780547701014e-02, /* 288 */ + 3.589219346450052e-02, /* 289 */ + 3.548133892335755e-02, /* 290 */ + 3.507518739525679e-02, /* 291 */ + 3.467368504525317e-02, /* 292 */ + 3.427677865464503e-02, /* 293 */ + 3.388441561392026e-02, /* 294 */ + 3.349654391578276e-02, /* 295 */ + 3.311311214825911e-02, /* 296 */ + 3.273406948788381e-02, /* 297 */ + 3.235936569296283e-02, /* 298 */ + 3.198895109691397e-02, /* 299 */ + 3.162277660168379e-02, /* 300 */ + 3.126079367123956e-02, /* 301 */ + 3.090295432513590e-02, /* 302 */ + 3.054921113215514e-02, /* 303 */ + 3.019951720402016e-02, /* 304 */ + 2.985382618917960e-02, /* 305 */ + 2.951209226666385e-02, /* 306 */ + 2.917427014001167e-02, /* 307 */ + 2.884031503126606e-02, /* 308 */ + 2.851018267503910e-02, /* 309 */ + 2.818382931264454e-02, /* 310 */ + 2.786121168629771e-02, /* 311 */ + 2.754228703338166e-02, /* 312 */ + 2.722701308077913e-02, /* 313 */ + 2.691534803926915e-02, /* 314 */ + 2.660725059798810e-02, /* 315 */ + 2.630267991895381e-02, /* 316 */ + 2.600159563165272e-02, /* 317 */ + 2.570395782768863e-02, /* 318 */ + 2.540972705549305e-02, /* 319 */ + 2.511886431509579e-02, /* 320 */ + 2.483133105295570e-02, /* 321 */ + 2.454708915685030e-02, /* 322 */ + 2.426610095082415e-02, /* 323 */ + 2.398832919019490e-02, /* 324 */ + 2.371373705661655e-02, /* 325 */ + 2.344228815319923e-02, /* 326 */ + 2.317394649968478e-02, /* 327 */ + 2.290867652767773e-02, /* 328 */ + 2.264644307593059e-02, /* 329 */ + 2.238721138568340e-02, /* 330 */ + 2.213094709605638e-02, /* 331 */ + 2.187761623949553e-02, /* 332 */ + 2.162718523727020e-02, /* 333 */ + 2.137962089502233e-02, /* 334 */ + 2.113489039836646e-02, /* 335 */ + 2.089296130854040e-02, /* 336 */ + 2.065380155810529e-02, /* 337 */ + 2.041737944669529e-02, /* 338 */ + 2.018366363681561e-02, /* 339 */ + 1.995262314968880e-02, /* 340 */ + 1.972422736114854e-02, /* 341 */ + 1.949844599758046e-02, /* 342 */ + 1.927524913190936e-02, /* 343 */ + 1.905460717963247e-02, /* 344 */ + 1.883649089489800e-02, /* 345 */ + 1.862087136662868e-02, /* 346 */ + 1.840772001468955e-02, /* 347 */ + 1.819700858609984e-02, /* 348 */ + 1.798870915128787e-02, /* 349 */ + 1.778279410038923e-02, /* 350 */ + 1.757923613958693e-02, /* 351 */ + 1.737800828749375e-02, /* 352 */ + 1.717908387157589e-02, /* 353 */ + 1.698243652461744e-02, /* 354 */ + 1.678804018122561e-02, /* 355 */ + 1.659586907437561e-02, /* 356 */ + 1.640589773199540e-02, /* 357 */ + 1.621810097358930e-02, /* 358 */ + 1.603245390690042e-02, /* 359 */ + 1.584893192461113e-02, /* 360 */ + 1.566751070108149e-02, /* 361 */ + 1.548816618912481e-02, /* 362 */ + 1.531087461682030e-02, /* 363 */ + 1.513561248436208e-02, /* 364 */ + 1.496235656094434e-02, /* 365 */ + 1.479108388168207e-02, /* 366 */ + 1.462177174456718e-02, /* 367 */ + 1.445439770745927e-02, /* 368 */ + 1.428893958511103e-02, /* 369 */ + 1.412537544622754e-02, /* 370 */ + 1.396368361055938e-02, /* 371 */ + 1.380384264602885e-02, /* 372 */ + 1.364583136588924e-02, /* 373 */ + 1.348962882591653e-02, /* 374 */ + 1.333521432163324e-02, /* 375 */ + 1.318256738556407e-02, /* 376 */ + 1.303166778452299e-02, /* 377 */ + 1.288249551693134e-02, /* 378 */ + 1.273503081016662e-02, /* 379 */ + 1.258925411794167e-02, /* 380 */ + 1.244514611771385e-02, /* 381 */ + 1.230268770812382e-02, /* 382 */ + 1.216186000646368e-02, /* 383 */ + 1.202264434617413e-02, /* 384 */ + 1.188502227437018e-02, /* 385 */ + 1.174897554939530e-02, /* 386 */ + 1.161448613840343e-02, /* 387 */ + 1.148153621496883e-02, /* 388 */ + 1.135010815672315e-02, /* 389 */ + 1.122018454301964e-02, /* 390 */ + 1.109174815262401e-02, /* 391 */ + 1.096478196143185e-02, /* 392 */ + 1.083926914021203e-02, /* 393 */ + 1.071519305237606e-02, /* 394 */ + 1.059253725177289e-02, /* 395 */ + 1.047128548050900e-02, /* 396 */ + 1.035142166679344e-02, /* 397 */ + 1.023292992280754e-02, /* 398 */ + 1.011579454259898e-02, /* 399 */ + 1.000000000000000e-02, /* 400 */ + 9.885530946569391e-03, /* 401 */ + 9.772372209558112e-03, /* 402 */ + 9.660508789898131e-03, /* 403 */ + 9.549925860214359e-03, /* 404 */ + 9.440608762859235e-03, /* 405 */ + 9.332543007969915e-03, /* 406 */ + 9.225714271547629e-03, /* 407 */ + 9.120108393559097e-03, /* 408 */ + 9.015711376059570e-03, /* 409 */ + 8.912509381337459e-03, /* 410 */ + 8.810488730080137e-03, /* 411 */ + 8.709635899560805e-03, /* 412 */ + 8.609937521846007e-03, /* 413 */ + 8.511380382023767e-03, /* 414 */ + 8.413951416451947e-03, /* 415 */ + 8.317637711026709e-03, /* 416 */ + 8.222426499470713e-03, /* 417 */ + 8.128305161640995e-03, /* 418 */ + 8.035261221856169e-03, /* 419 */ + 7.943282347242814e-03, /* 420 */ + 7.852356346100719e-03, /* 421 */ + 7.762471166286920e-03, /* 422 */ + 7.673614893618186e-03, /* 423 */ + 7.585775750291836e-03, /* 424 */ + 7.498942093324558e-03, /* 425 */ + 7.413102413009177e-03, /* 426 */ + 7.328245331389044e-03, /* 427 */ + 7.244359600749898e-03, /* 428 */ + 7.161434102129020e-03, /* 429 */ + 7.079457843841380e-03, /* 430 */ + 6.998419960022738e-03, /* 431 */ + 6.918309709189363e-03, /* 432 */ + 6.839116472814293e-03, /* 433 */ + 6.760829753919818e-03, /* 434 */ + 6.683439175686149e-03, /* 435 */ + 6.606934480075957e-03, /* 436 */ + 6.531305526474723e-03, /* 437 */ + 6.456542290346556e-03, /* 438 */ + 6.382634861905490e-03, /* 439 */ + 6.309573444801930e-03, /* 440 */ + 6.237348354824191e-03, /* 441 */ + 6.165950018614822e-03, /* 442 */ + 6.095368972401693e-03, /* 443 */ + 6.025595860743574e-03, /* 444 */ + 5.956621435290103e-03, /* 445 */ + 5.888436553555890e-03, /* 446 */ + 5.821032177708716e-03, /* 447 */ + 5.754399373371567e-03, /* 448 */ + 5.688529308438413e-03, /* 449 */ + 5.623413251903491e-03, /* 450 */ + 5.559042572704037e-03, /* 451 */ + 5.495408738576248e-03, /* 452 */ + 5.432503314924331e-03, /* 453 */ + 5.370317963702527e-03, /* 454 */ + 5.308844442309885e-03, /* 455 */ + 5.248074602497728e-03, /* 456 */ + 5.188000389289609e-03, /* 457 */ + 5.128613839913648e-03, /* 458 */ + 5.069907082747045e-03, /* 459 */ + 5.011872336272725e-03, /* 460 */ + 4.954501908047901e-03, /* 461 */ + 4.897788193684461e-03, /* 462 */ + 4.841723675840994e-03, /* 463 */ + 4.786300923226385e-03, /* 464 */ + 4.731512589614803e-03, /* 465 */ + 4.677351412871981e-03, /* 466 */ + 4.623810213992603e-03, /* 467 */ + 4.570881896148752e-03, /* 468 */ + 4.518559443749222e-03, /* 469 */ + 4.466835921509630e-03, /* 470 */ + 4.415704473533125e-03, /* 471 */ + 4.365158322401661e-03, /* 472 */ + 4.315190768277650e-03, /* 473 */ + 4.265795188015926e-03, /* 474 */ + 4.216965034285823e-03, /* 475 */ + 4.168693834703355e-03, /* 476 */ + 4.120975190973304e-03, /* 477 */ + 4.073802778041126e-03, /* 478 */ + 4.027170343254591e-03, /* 479 */ + 3.981071705534973e-03, /* 480 */ + 3.935500754557776e-03, /* 481 */ + 3.890451449942805e-03, /* 482 */ + 3.845917820453535e-03, /* 483 */ + 3.801893963205613e-03, /* 484 */ + 3.758374042884443e-03, /* 485 */ + 3.715352290971724e-03, /* 486 */ + 3.672823004980846e-03, /* 487 */ + 3.630780547701014e-03, /* 488 */ + 3.589219346450053e-03, /* 489 */ + 3.548133892335753e-03, /* 490 */ + 3.507518739525680e-03, /* 491 */ + 3.467368504525317e-03, /* 492 */ + 3.427677865464505e-03, /* 493 */ + 3.388441561392024e-03, /* 494 */ + 3.349654391578276e-03, /* 495 */ + 3.311311214825911e-03, /* 496 */ + 3.273406948788383e-03, /* 497 */ + 3.235936569296281e-03, /* 498 */ + 3.198895109691397e-03, /* 499 */ + 3.162277660168379e-03, /* 500 */ + 3.126079367123956e-03, /* 501 */ + 3.090295432513592e-03, /* 502 */ + 3.054921113215512e-03, /* 503 */ + 3.019951720402016e-03, /* 504 */ + 2.985382618917960e-03, /* 505 */ + 2.951209226666387e-03, /* 506 */ + 2.917427014001166e-03, /* 507 */ + 2.884031503126605e-03, /* 508 */ + 2.851018267503910e-03, /* 509 */ + 2.818382931264455e-03, /* 510 */ + 2.786121168629769e-03, /* 511 */ + 2.754228703338166e-03, /* 512 */ + 2.722701308077913e-03, /* 513 */ + 2.691534803926917e-03, /* 514 */ + 2.660725059798808e-03, /* 515 */ + 2.630267991895381e-03, /* 516 */ + 2.600159563165272e-03, /* 517 */ + 2.570395782768865e-03, /* 518 */ + 2.540972705549304e-03, /* 519 */ + 2.511886431509579e-03, /* 520 */ + 2.483133105295570e-03, /* 521 */ + 2.454708915685031e-03, /* 522 */ + 2.426610095082414e-03, /* 523 */ + 2.398832919019490e-03, /* 524 */ + 2.371373705661655e-03, /* 525 */ + 2.344228815319923e-03, /* 526 */ + 2.317394649968480e-03, /* 527 */ + 2.290867652767772e-03, /* 528 */ + 2.264644307593060e-03, /* 529 */ + 2.238721138568340e-03, /* 530 */ + 2.213094709605639e-03, /* 531 */ + 2.187761623949552e-03, /* 532 */ + 2.162718523727020e-03, /* 533 */ + 2.137962089502233e-03, /* 534 */ + 2.113489039836648e-03, /* 535 */ + 2.089296130854039e-03, /* 536 */ + 2.065380155810529e-03, /* 537 */ + 2.041737944669530e-03, /* 538 */ + 2.018366363681562e-03, /* 539 */ + 1.995262314968879e-03, /* 540 */ + 1.972422736114853e-03, /* 541 */ + 1.949844599758046e-03, /* 542 */ + 1.927524913190937e-03, /* 543 */ + 1.905460717963246e-03, /* 544 */ + 1.883649089489800e-03, /* 545 */ + 1.862087136662868e-03, /* 546 */ + 1.840772001468956e-03, /* 547 */ + 1.819700858609983e-03, /* 548 */ + 1.798870915128787e-03, /* 549 */ + 1.778279410038923e-03, /* 550 */ + 1.757923613958693e-03, /* 551 */ + 1.737800828749376e-03, /* 552 */ + 1.717908387157588e-03, /* 553 */ + 1.698243652461744e-03, /* 554 */ + 1.678804018122561e-03, /* 555 */ + 1.659586907437561e-03, /* 556 */ + 1.640589773199539e-03, /* 557 */ + 1.621810097358930e-03, /* 558 */ + 1.603245390690042e-03, /* 559 */ + 1.584893192461114e-03, /* 560 */ + 1.566751070108149e-03, /* 561 */ + 1.548816618912481e-03, /* 562 */ + 1.531087461682030e-03, /* 563 */ + 1.513561248436209e-03, /* 564 */ + 1.496235656094433e-03, /* 565 */ + 1.479108388168207e-03, /* 566 */ + 1.462177174456718e-03, /* 567 */ + 1.445439770745928e-03, /* 568 */ + 1.428893958511102e-03, /* 569 */ + 1.412537544622754e-03, /* 570 */ + 1.396368361055938e-03, /* 571 */ + 1.380384264602885e-03, /* 572 */ + 1.364583136588924e-03, /* 573 */ + 1.348962882591653e-03, /* 574 */ + 1.333521432163324e-03, /* 575 */ + 1.318256738556408e-03, /* 576 */ + 1.303166778452300e-03, /* 577 */ + 1.288249551693134e-03, /* 578 */ + 1.273503081016662e-03, /* 579 */ + 1.258925411794167e-03, /* 580 */ + 1.244514611771386e-03, /* 581 */ + 1.230268770812381e-03, /* 582 */ + 1.216186000646368e-03, /* 583 */ + 1.202264434617413e-03, /* 584 */ + 1.188502227437019e-03, /* 585 */ + 1.174897554939529e-03, /* 586 */ + 1.161448613840343e-03, /* 587 */ + 1.148153621496883e-03, /* 588 */ + 1.135010815672315e-03, /* 589 */ + 1.122018454301963e-03, /* 590 */ + 1.109174815262401e-03, /* 591 */ + 1.096478196143185e-03, /* 592 */ + 1.083926914021204e-03, /* 593 */ + 1.071519305237606e-03, /* 594 */ + 1.059253725177289e-03, /* 595 */ + 1.047128548050900e-03, /* 596 */ + 1.035142166679344e-03, /* 597 */ + 1.023292992280754e-03, /* 598 */ + 1.011579454259898e-03, /* 599 */ + 1.000000000000000e-03, /* 600 */ + 9.885530946569391e-04, /* 601 */ + 9.772372209558111e-04, /* 602 */ + 9.660508789898131e-04, /* 603 */ + 9.549925860214359e-04, /* 604 */ + 9.440608762859235e-04, /* 605 */ + 9.332543007969915e-04, /* 606 */ + 9.225714271547629e-04, /* 607 */ + 9.120108393559096e-04, /* 608 */ + 9.015711376059570e-04, /* 609 */ + 8.912509381337459e-04, /* 610 */ + 8.810488730080138e-04, /* 611 */ + 8.709635899560805e-04, /* 612 */ + 8.609937521846007e-04, /* 613 */ + 8.511380382023768e-04, /* 614 */ + 8.413951416451947e-04, /* 615 */ + 8.317637711026709e-04, /* 616 */ + 8.222426499470713e-04, /* 617 */ + 8.128305161640995e-04, /* 618 */ + 8.035261221856169e-04, /* 619 */ + 7.943282347242813e-04, /* 620 */ + 7.852356346100719e-04, /* 621 */ + 7.762471166286919e-04, /* 622 */ + 7.673614893618186e-04, /* 623 */ + 7.585775750291836e-04, /* 624 */ + 7.498942093324559e-04, /* 625 */ + 7.413102413009177e-04, /* 626 */ + 7.328245331389044e-04, /* 627 */ + 7.244359600749898e-04, /* 628 */ + 7.161434102129020e-04, /* 629 */ + 7.079457843841380e-04, /* 630 */ + 6.998419960022738e-04, /* 631 */ + 6.918309709189362e-04, /* 632 */ + 6.839116472814292e-04, /* 633 */ + 6.760829753919819e-04, /* 634 */ + 6.683439175686149e-04, /* 635 */ + 6.606934480075957e-04, /* 636 */ + 6.531305526474723e-04, /* 637 */ + 6.456542290346556e-04, /* 638 */ + 6.382634861905489e-04, /* 639 */ + 6.309573444801930e-04, /* 640 */ + 6.237348354824192e-04, /* 641 */ + 6.165950018614823e-04, /* 642 */ + 6.095368972401694e-04, /* 643 */ + 6.025595860743575e-04, /* 644 */ + 5.956621435290103e-04, /* 645 */ + 5.888436553555889e-04, /* 646 */ + 5.821032177708716e-04, /* 647 */ + 5.754399373371566e-04, /* 648 */ + 5.688529308438414e-04, /* 649 */ + 5.623413251903491e-04, /* 650 */ + 5.559042572704037e-04, /* 651 */ + 5.495408738576248e-04, /* 652 */ + 5.432503314924330e-04, /* 653 */ + 5.370317963702527e-04, /* 654 */ + 5.308844442309885e-04, /* 655 */ + 5.248074602497728e-04, /* 656 */ + 5.188000389289610e-04, /* 657 */ + 5.128613839913648e-04, /* 658 */ + 5.069907082747044e-04, /* 659 */ + 5.011872336272725e-04, /* 660 */ + 4.954501908047901e-04, /* 661 */ + 4.897788193684461e-04, /* 662 */ + 4.841723675840994e-04, /* 663 */ + 4.786300923226385e-04, /* 664 */ + 4.731512589614803e-04, /* 665 */ + 4.677351412871981e-04, /* 666 */ + 4.623810213992603e-04, /* 667 */ + 4.570881896148752e-04, /* 668 */ + 4.518559443749222e-04, /* 669 */ + 4.466835921509630e-04, /* 670 */ + 4.415704473533125e-04, /* 671 */ + 4.365158322401661e-04, /* 672 */ + 4.315190768277650e-04, /* 673 */ + 4.265795188015926e-04, /* 674 */ + 4.216965034285822e-04, /* 675 */ + 4.168693834703355e-04, /* 676 */ + 4.120975190973304e-04, /* 677 */ + 4.073802778041126e-04, /* 678 */ + 4.027170343254591e-04, /* 679 */ + 3.981071705534974e-04, /* 680 */ + 3.935500754557776e-04, /* 681 */ + 3.890451449942805e-04, /* 682 */ + 3.845917820453535e-04, /* 683 */ + 3.801893963205612e-04, /* 684 */ + 3.758374042884443e-04, /* 685 */ + 3.715352290971724e-04, /* 686 */ + 3.672823004980846e-04, /* 687 */ + 3.630780547701014e-04, /* 688 */ + 3.589219346450053e-04, /* 689 */ + 3.548133892335753e-04, /* 690 */ + 3.507518739525679e-04, /* 691 */ + 3.467368504525317e-04, /* 692 */ + 3.427677865464505e-04, /* 693 */ + 3.388441561392024e-04, /* 694 */ + 3.349654391578276e-04, /* 695 */ + 3.311311214825911e-04, /* 696 */ + 3.273406948788383e-04, /* 697 */ + 3.235936569296281e-04, /* 698 */ + 3.198895109691397e-04, /* 699 */ + 3.162277660168379e-04, /* 700 */ + 3.126079367123956e-04, /* 701 */ + 3.090295432513592e-04, /* 702 */ + 3.054921113215512e-04, /* 703 */ + 3.019951720402016e-04, /* 704 */ + 2.985382618917960e-04, /* 705 */ + 2.951209226666387e-04, /* 706 */ + 2.917427014001166e-04, /* 707 */ + 2.884031503126605e-04, /* 708 */ + 2.851018267503910e-04, /* 709 */ + 2.818382931264455e-04, /* 710 */ + 2.786121168629769e-04, /* 711 */ + 2.754228703338166e-04, /* 712 */ + 2.722701308077913e-04, /* 713 */ + 2.691534803926917e-04, /* 714 */ + 2.660725059798808e-04, /* 715 */ + 2.630267991895381e-04, /* 716 */ + 2.600159563165272e-04, /* 717 */ + 2.570395782768865e-04, /* 718 */ + 2.540972705549304e-04, /* 719 */ + 2.511886431509580e-04, /* 720 */ + 2.483133105295570e-04, /* 721 */ + 2.454708915685031e-04, /* 722 */ + 2.426610095082414e-04, /* 723 */ + 2.398832919019490e-04, /* 724 */ + 2.371373705661655e-04, /* 725 */ + 2.344228815319923e-04, /* 726 */ + 2.317394649968480e-04, /* 727 */ + 2.290867652767772e-04, /* 728 */ + 2.264644307593059e-04, /* 729 */ + 2.238721138568340e-04, /* 730 */ + 2.213094709605639e-04, /* 731 */ + 2.187761623949552e-04, /* 732 */ + 2.162718523727020e-04, /* 733 */ + 2.137962089502232e-04, /* 734 */ + 2.113489039836648e-04, /* 735 */ + 2.089296130854039e-04, /* 736 */ + 2.065380155810529e-04, /* 737 */ + 2.041737944669530e-04, /* 738 */ + 2.018366363681562e-04, /* 739 */ + 1.995262314968879e-04, /* 740 */ + 1.972422736114853e-04, /* 741 */ + 1.949844599758046e-04, /* 742 */ + 1.927524913190937e-04, /* 743 */ + 1.905460717963246e-04, /* 744 */ + 1.883649089489800e-04, /* 745 */ + 1.862087136662868e-04, /* 746 */ + 1.840772001468956e-04, /* 747 */ + 1.819700858609983e-04, /* 748 */ + 1.798870915128787e-04, /* 749 */ + 1.778279410038923e-04, /* 750 */ + 1.757923613958693e-04, /* 751 */ + 1.737800828749376e-04, /* 752 */ + 1.717908387157588e-04, /* 753 */ + 1.698243652461744e-04, /* 754 */ + 1.678804018122561e-04, /* 755 */ + 1.659586907437562e-04, /* 756 */ + 1.640589773199539e-04, /* 757 */ + 1.621810097358930e-04, /* 758 */ + 1.603245390690042e-04, /* 759 */ + 1.584893192461114e-04, /* 760 */ + 1.566751070108148e-04, /* 761 */ + 1.548816618912481e-04, /* 762 */ + 1.531087461682030e-04, /* 763 */ + 1.513561248436209e-04, /* 764 */ + 1.496235656094433e-04, /* 765 */ + 1.479108388168207e-04, /* 766 */ + 1.462177174456718e-04, /* 767 */ + 1.445439770745928e-04, /* 768 */ + 1.428893958511102e-04, /* 769 */ + 1.412537544622754e-04, /* 770 */ + 1.396368361055938e-04, /* 771 */ + 1.380384264602885e-04, /* 772 */ + 1.364583136588924e-04, /* 773 */ + 1.348962882591653e-04, /* 774 */ + 1.333521432163324e-04, /* 775 */ + 1.318256738556407e-04, /* 776 */ + 1.303166778452300e-04, /* 777 */ + 1.288249551693134e-04, /* 778 */ + 1.273503081016662e-04, /* 779 */ + 1.258925411794167e-04, /* 780 */ + 1.244514611771386e-04, /* 781 */ + 1.230268770812381e-04, /* 782 */ + 1.216186000646368e-04, /* 783 */ + 1.202264434617413e-04, /* 784 */ + 1.188502227437019e-04, /* 785 */ + 1.174897554939529e-04, /* 786 */ + 1.161448613840343e-04, /* 787 */ + 1.148153621496883e-04, /* 788 */ + 1.135010815672315e-04, /* 789 */ + 1.122018454301963e-04, /* 790 */ + 1.109174815262401e-04, /* 791 */ + 1.096478196143185e-04, /* 792 */ + 1.083926914021204e-04, /* 793 */ + 1.071519305237606e-04, /* 794 */ + 1.059253725177289e-04, /* 795 */ + 1.047128548050900e-04, /* 796 */ + 1.035142166679344e-04, /* 797 */ + 1.023292992280754e-04, /* 798 */ + 1.011579454259898e-04, /* 799 */ + 1.000000000000000e-04, /* 800 */ + 9.885530946569390e-05, /* 801 */ + 9.772372209558111e-05, /* 802 */ + 9.660508789898140e-05, /* 803 */ + 9.549925860214369e-05, /* 804 */ + 9.440608762859227e-05, /* 805 */ + 9.332543007969905e-05, /* 806 */ + 9.225714271547629e-05, /* 807 */ + 9.120108393559096e-05, /* 808 */ + 9.015711376059571e-05, /* 809 */ + 8.912509381337459e-05, /* 810 */ + 8.810488730080147e-05, /* 811 */ + 8.709635899560814e-05, /* 812 */ + 8.609937521845998e-05, /* 813 */ + 8.511380382023759e-05, /* 814 */ + 8.413951416451947e-05, /* 815 */ + 8.317637711026709e-05, /* 816 */ + 8.222426499470712e-05, /* 817 */ + 8.128305161640995e-05, /* 818 */ + 8.035261221856177e-05, /* 819 */ + 7.943282347242822e-05, /* 820 */ + 7.852356346100711e-05, /* 821 */ + 7.762471166286911e-05, /* 822 */ + 7.673614893618186e-05, /* 823 */ + 7.585775750291836e-05, /* 824 */ + 7.498942093324559e-05, /* 825 */ + 7.413102413009176e-05, /* 826 */ + 7.328245331389045e-05, /* 827 */ + 7.244359600749906e-05, /* 828 */ + 7.161434102129028e-05, /* 829 */ + 7.079457843841373e-05, /* 830 */ + 6.998419960022731e-05, /* 831 */ + 6.918309709189363e-05, /* 832 */ + 6.839116472814293e-05, /* 833 */ + 6.760829753919819e-05, /* 834 */ + 6.683439175686149e-05, /* 835 */ + 6.606934480075965e-05, /* 836 */ + 6.531305526474729e-05, /* 837 */ + 6.456542290346549e-05, /* 838 */ + 6.382634861905483e-05, /* 839 */ + 6.309573444801929e-05, /* 840 */ + 6.237348354824191e-05, /* 841 */ + 6.165950018614822e-05, /* 842 */ + 6.095368972401694e-05, /* 843 */ + 6.025595860743581e-05, /* 844 */ + 5.956621435290110e-05, /* 845 */ + 5.888436553555884e-05, /* 846 */ + 5.821032177708710e-05, /* 847 */ + 5.754399373371566e-05, /* 848 */ + 5.688529308438413e-05, /* 849 */ + 5.623413251903491e-05, /* 850 */ + 5.559042572704037e-05, /* 851 */ + 5.495408738576248e-05, /* 852 */ + 5.432503314924336e-05, /* 853 */ + 5.370317963702532e-05, /* 854 */ + 5.308844442309879e-05, /* 855 */ + 5.248074602497723e-05, /* 856 */ + 5.188000389289609e-05, /* 857 */ + 5.128613839913648e-05, /* 858 */ + 5.069907082747045e-05, /* 859 */ + 5.011872336272725e-05, /* 860 */ + 4.954501908047905e-05, /* 861 */ + 4.897788193684466e-05, /* 862 */ + 4.841723675840989e-05, /* 863 */ + 4.786300923226380e-05, /* 864 */ + 4.731512589614803e-05, /* 865 */ + 4.677351412871981e-05, /* 866 */ + 4.623810213992603e-05, /* 867 */ + 4.570881896148752e-05, /* 868 */ + 4.518559443749226e-05, /* 869 */ + 4.466835921509635e-05, /* 870 */ + 4.415704473533121e-05, /* 871 */ + 4.365158322401657e-05, /* 872 */ + 4.315190768277650e-05, /* 873 */ + 4.265795188015926e-05, /* 874 */ + 4.216965034285822e-05, /* 875 */ + 4.168693834703355e-05, /* 876 */ + 4.120975190973304e-05, /* 877 */ + 4.073802778041130e-05, /* 878 */ + 4.027170343254595e-05, /* 879 */ + 3.981071705534969e-05, /* 880 */ + 3.935500754557772e-05, /* 881 */ + 3.890451449942805e-05, /* 882 */ + 3.845917820453536e-05, /* 883 */ + 3.801893963205612e-05, /* 884 */ + 3.758374042884443e-05, /* 885 */ + 3.715352290971728e-05, /* 886 */ + 3.672823004980850e-05, /* 887 */ + 3.630780547701010e-05, /* 888 */ + 3.589219346450050e-05, /* 889 */ + 3.548133892335753e-05, /* 890 */ + 3.507518739525680e-05, /* 891 */ + 3.467368504525317e-05, /* 892 */ + 3.427677865464505e-05, /* 893 */ + 3.388441561392028e-05, /* 894 */ + 3.349654391578279e-05, /* 895 */ + 3.311311214825908e-05, /* 896 */ + 3.273406948788379e-05, /* 897 */ + 3.235936569296281e-05, /* 898 */ + 3.198895109691397e-05, /* 899 */ + 3.162277660168380e-05, /* 900 */ + 3.126079367123956e-05, /* 901 */ + 3.090295432513592e-05, /* 902 */ + 3.054921113215516e-05, /* 903 */ + 3.019951720402019e-05, /* 904 */ + 2.985382618917957e-05, /* 905 */ + 2.951209226666384e-05, /* 906 */ + 2.917427014001166e-05, /* 907 */ + 2.884031503126606e-05, /* 908 */ + 2.851018267503910e-05, /* 909 */ + 2.818382931264455e-05, /* 910 */ + 2.786121168629772e-05, /* 911 */ + 2.754228703338169e-05, /* 912 */ + 2.722701308077910e-05, /* 913 */ + 2.691534803926914e-05, /* 914 */ + 2.660725059798808e-05, /* 915 */ + 2.630267991895381e-05, /* 916 */ + 2.600159563165272e-05, /* 917 */ + 2.570395782768864e-05, /* 918 */ + 2.540972705549306e-05, /* 919 */ + 2.511886431509582e-05, /* 920 */ + 2.483133105295568e-05, /* 921 */ + 2.454708915685029e-05, /* 922 */ + 2.426610095082414e-05, /* 923 */ + 2.398832919019490e-05, /* 924 */ + 2.371373705661655e-05, /* 925 */ + 2.344228815319923e-05, /* 926 */ + 2.317394649968480e-05, /* 927 */ + 2.290867652767775e-05, /* 928 */ + 2.264644307593062e-05, /* 929 */ + 2.238721138568338e-05, /* 930 */ + 2.213094709605637e-05, /* 931 */ + 2.187761623949552e-05, /* 932 */ + 2.162718523727020e-05, /* 933 */ + 2.137962089502232e-05, /* 934 */ + 2.113489039836648e-05, /* 935 */ + 2.089296130854041e-05, /* 936 */ + 2.065380155810531e-05, /* 937 */ + 2.041737944669527e-05, /* 938 */ + 2.018366363681560e-05, /* 939 */ + 1.995262314968879e-05, /* 940 */ + 1.972422736114853e-05, /* 941 */ + 1.949844599758046e-05, /* 942 */ + 1.927524913190937e-05, /* 943 */ + 1.905460717963248e-05, /* 944 */ + 1.883649089489802e-05, /* 945 */ + 1.862087136662866e-05, /* 946 */ + 1.840772001468954e-05, /* 947 */ + 1.819700858609983e-05, /* 948 */ + 1.798870915128787e-05, /* 949 */ + 1.778279410038923e-05, /* 950 */ + 1.757923613958693e-05, /* 951 */ + 1.737800828749376e-05, /* 952 */ + 1.717908387157589e-05, /* 953 */ + 1.698243652461746e-05, /* 954 */ + 1.678804018122559e-05, /* 955 */ + 1.659586907437560e-05, /* 956 */ + 1.640589773199539e-05, /* 957 */ + 1.621810097358930e-05, /* 958 */ + 1.603245390690042e-05, /* 959 */ + 1.584893192461114e-05, /* 960 */ + 1.566751070108150e-05, /* 961 */ + 1.548816618912483e-05, /* 962 */ + 1.531087461682029e-05, /* 963 */ + 1.513561248436207e-05, /* 964 */ + 1.496235656094433e-05, /* 965 */ + 1.479108388168207e-05, /* 966 */ + 1.462177174456718e-05, /* 967 */ + 1.445439770745928e-05, /* 968 */ + 1.428893958511104e-05, /* 969 */ + 1.412537544622755e-05, /* 970 */ + 1.396368361055936e-05, /* 971 */ + 1.380384264602884e-05, /* 972 */ + 1.364583136588924e-05, /* 973 */ + 1.348962882591653e-05, /* 974 */ + 1.333521432163324e-05, /* 975 */ + 1.318256738556407e-05, /* 976 */ + 1.303166778452300e-05, /* 977 */ + 1.288249551693135e-05, /* 978 */ + 1.273503081016663e-05, /* 979 */ + 1.258925411794166e-05, /* 980 */ + 1.244514611771384e-05, /* 981 */ + 1.230268770812381e-05, /* 982 */ + 1.216186000646368e-05, /* 983 */ + 1.202264434617413e-05, /* 984 */ + 1.188502227437019e-05, /* 985 */ + 1.174897554939530e-05, /* 986 */ + 1.161448613840344e-05, /* 987 */ + 1.148153621496882e-05, /* 988 */ + 1.135010815672314e-05, /* 989 */ + 1.122018454301963e-05, /* 990 */ + 1.109174815262401e-05, /* 991 */ + 1.096478196143185e-05, /* 992 */ + 1.083926914021204e-05, /* 993 */ + 1.071519305237607e-05, /* 994 */ + 1.059253725177290e-05, /* 995 */ + 1.047128548050899e-05, /* 996 */ + 1.035142166679343e-05, /* 997 */ + 1.023292992280754e-05, /* 998 */ + 1.011579454259898e-05, /* 999 */ + 1.000000000000000e-05, /* 1000 */ + 9.885530946569391e-06, /* 1001 */ + 9.772372209558111e-06, /* 1002 */ + 9.660508789898141e-06, /* 1003 */ + 9.549925860214369e-06, /* 1004 */ + 9.440608762859225e-06, /* 1005 */ + 9.332543007969906e-06, /* 1006 */ + 9.225714271547628e-06, /* 1007 */ + 9.120108393559096e-06, /* 1008 */ + 9.015711376059570e-06, /* 1009 */ + 8.912509381337459e-06, /* 1010 */ + 8.810488730080147e-06, /* 1011 */ + 8.709635899560814e-06, /* 1012 */ + 8.609937521845999e-06, /* 1013 */ + 8.511380382023760e-06, /* 1014 */ + 8.413951416451948e-06, /* 1015 */ + 8.317637711026709e-06, /* 1016 */ + 8.222426499470711e-06, /* 1017 */ + 8.128305161640995e-06, /* 1018 */ + 8.035261221856176e-06, /* 1019 */ + 7.943282347242822e-06, /* 1020 */ + 7.852356346100711e-06, /* 1021 */ + 7.762471166286911e-06, /* 1022 */ + 7.673614893618186e-06, /* 1023 */ + 7.585775750291836e-06, /* 1024 */ + 7.498942093324558e-06, /* 1025 */ + 7.413102413009177e-06, /* 1026 */ + 7.328245331389045e-06, /* 1027 */ + 7.244359600749906e-06, /* 1028 */ + 7.161434102129027e-06, /* 1029 */ + 7.079457843841373e-06, /* 1030 */ + 6.998419960022731e-06, /* 1031 */ + 6.918309709189363e-06, /* 1032 */ + 6.839116472814292e-06, /* 1033 */ + 6.760829753919819e-06, /* 1034 */ + 6.683439175686149e-06, /* 1035 */ + 6.606934480075964e-06, /* 1036 */ + 6.531305526474729e-06, /* 1037 */ + 6.456542290346549e-06, /* 1038 */ + 6.382634861905482e-06, /* 1039 */ + 6.309573444801930e-06, /* 1040 */ + 6.237348354824191e-06, /* 1041 */ + 6.165950018614822e-06, /* 1042 */ + 6.095368972401693e-06, /* 1043 */ + 6.025595860743581e-06, /* 1044 */ + 5.956621435290109e-06, /* 1045 */ + 5.888436553555884e-06, /* 1046 */ + 5.821032177708710e-06, /* 1047 */ + 5.754399373371567e-06, /* 1048 */ + 5.688529308438413e-06, /* 1049 */ + 5.623413251903491e-06, /* 1050 */ + 5.559042572704037e-06, /* 1051 */ + 5.495408738576248e-06, /* 1052 */ + 5.432503314924336e-06, /* 1053 */ + 5.370317963702533e-06, /* 1054 */ + 5.308844442309879e-06, /* 1055 */ + 5.248074602497723e-06, /* 1056 */ + 5.188000389289610e-06, /* 1057 */ + 5.128613839913648e-06, /* 1058 */ + 5.069907082747045e-06, /* 1059 */ + 5.011872336272725e-06, /* 1060 */ + 4.954501908047905e-06, /* 1061 */ + 4.897788193684467e-06, /* 1062 */ + 4.841723675840989e-06, /* 1063 */ + 4.786300923226380e-06, /* 1064 */ + 4.731512589614803e-06, /* 1065 */ + 4.677351412871981e-06, /* 1066 */ + 4.623810213992603e-06, /* 1067 */ + 4.570881896148752e-06, /* 1068 */ + 4.518559443749226e-06, /* 1069 */ + 4.466835921509635e-06, /* 1070 */ + 4.415704473533120e-06, /* 1071 */ + 4.365158322401657e-06, /* 1072 */ + 4.315190768277650e-06, /* 1073 */ + 4.265795188015926e-06, /* 1074 */ + 4.216965034285822e-06, /* 1075 */ + 4.168693834703355e-06, /* 1076 */ + 4.120975190973304e-06, /* 1077 */ + 4.073802778041131e-06, /* 1078 */ + 4.027170343254595e-06, /* 1079 */ + 3.981071705534969e-06, /* 1080 */ + 3.935500754557773e-06, /* 1081 */ + 3.890451449942805e-06, /* 1082 */ + 3.845917820453535e-06, /* 1083 */ + 3.801893963205613e-06, /* 1084 */ + 3.758374042884443e-06, /* 1085 */ + 3.715352290971728e-06, /* 1086 */ + 3.672823004980850e-06, /* 1087 */ + 3.630780547701010e-06, /* 1088 */ + 3.589219346450050e-06, /* 1089 */ + 3.548133892335753e-06, /* 1090 */ + 3.507518739525679e-06, /* 1091 */ + 3.467368504525317e-06, /* 1092 */ + 3.427677865464505e-06, /* 1093 */ + 3.388441561392028e-06, /* 1094 */ + 3.349654391578279e-06, /* 1095 */ + 3.311311214825908e-06, /* 1096 */ + 3.273406948788379e-06, /* 1097 */ + 3.235936569296281e-06, /* 1098 */ + 3.198895109691397e-06, /* 1099 */ + 3.162277660168379e-06, /* 1100 */ + 3.126079367123956e-06, /* 1101 */ + 3.090295432513592e-06, /* 1102 */ + 3.054921113215515e-06, /* 1103 */ + 3.019951720402019e-06, /* 1104 */ + 2.985382618917957e-06, /* 1105 */ + 2.951209226666384e-06, /* 1106 */ + 2.917427014001166e-06, /* 1107 */ + 2.884031503126606e-06, /* 1108 */ + 2.851018267503910e-06, /* 1109 */ + 2.818382931264455e-06, /* 1110 */ + 2.786121168629772e-06, /* 1111 */ + 2.754228703338169e-06, /* 1112 */ + 2.722701308077910e-06, /* 1113 */ + 2.691534803926914e-06, /* 1114 */ + 2.660725059798809e-06, /* 1115 */ + 2.630267991895382e-06, /* 1116 */ + 2.600159563165272e-06, /* 1117 */ + 2.570395782768865e-06, /* 1118 */ + 2.540972705549307e-06, /* 1119 */ + 2.511886431509582e-06, /* 1120 */ + 2.483133105295568e-06, /* 1121 */ + 2.454708915685028e-06, /* 1122 */ + 2.426610095082414e-06, /* 1123 */ + 2.398832919019490e-06, /* 1124 */ + 2.371373705661655e-06, /* 1125 */ + 2.344228815319923e-06, /* 1126 */ + 2.317394649968480e-06, /* 1127 */ + 2.290867652767775e-06, /* 1128 */ + 2.264644307593062e-06, /* 1129 */ + 2.238721138568338e-06, /* 1130 */ + 2.213094709605636e-06, /* 1131 */ + 2.187761623949552e-06, /* 1132 */ + 2.162718523727020e-06, /* 1133 */ + 2.137962089502233e-06, /* 1134 */ + 2.113489039836648e-06, /* 1135 */ + 2.089296130854041e-06, /* 1136 */ + 2.065380155810531e-06, /* 1137 */ + 2.041737944669527e-06, /* 1138 */ + 2.018366363681560e-06, /* 1139 */ + 1.995262314968879e-06, /* 1140 */ + 1.972422736114853e-06, /* 1141 */ + 1.949844599758045e-06, /* 1142 */ + 1.927524913190937e-06, /* 1143 */ + 1.905460717963248e-06, /* 1144 */ + 1.883649089489802e-06, /* 1145 */ + 1.862087136662866e-06, /* 1146 */ + 1.840772001468954e-06, /* 1147 */ + 1.819700858609983e-06, /* 1148 */ + 1.798870915128787e-06, /* 1149 */ + 1.778279410038923e-06, /* 1150 */ + 1.757923613958693e-06, /* 1151 */ + 1.737800828749376e-06, /* 1152 */ + 1.717908387157589e-06, /* 1153 */ + 1.698243652461746e-06, /* 1154 */ + 1.678804018122559e-06, /* 1155 */ + 1.659586907437560e-06, /* 1156 */ + 1.640589773199539e-06, /* 1157 */ + 1.621810097358930e-06, /* 1158 */ + 1.603245390690042e-06, /* 1159 */ + 1.584893192461114e-06, /* 1160 */ + 1.566751070108150e-06, /* 1161 */ + 1.548816618912483e-06, /* 1162 */ + 1.531087461682029e-06, /* 1163 */ + 1.513561248436207e-06, /* 1164 */ + 1.496235656094433e-06, /* 1165 */ + 1.479108388168207e-06, /* 1166 */ + 1.462177174456718e-06, /* 1167 */ + 1.445439770745928e-06, /* 1168 */ + 1.428893958511104e-06, /* 1169 */ + 1.412537544622755e-06, /* 1170 */ + 1.396368361055936e-06, /* 1171 */ + 1.380384264602884e-06, /* 1172 */ + 1.364583136588924e-06, /* 1173 */ + 1.348962882591653e-06, /* 1174 */ + 1.333521432163324e-06, /* 1175 */ + 1.318256738556407e-06, /* 1176 */ + 1.303166778452300e-06, /* 1177 */ + 1.288249551693135e-06, /* 1178 */ + 1.273503081016663e-06, /* 1179 */ + 1.258925411794166e-06, /* 1180 */ + 1.244514611771384e-06, /* 1181 */ + 1.230268770812381e-06, /* 1182 */ + 1.216186000646368e-06, /* 1183 */ + 1.202264434617413e-06, /* 1184 */ + 1.188502227437019e-06, /* 1185 */ + 1.174897554939530e-06, /* 1186 */ + 1.161448613840344e-06, /* 1187 */ + 1.148153621496882e-06, /* 1188 */ + 1.135010815672314e-06, /* 1189 */ + 1.122018454301963e-06, /* 1190 */ + 1.109174815262401e-06, /* 1191 */ + 1.096478196143185e-06, /* 1192 */ + 1.083926914021204e-06, /* 1193 */ + 1.071519305237607e-06, /* 1194 */ + 1.059253725177290e-06, /* 1195 */ + 1.047128548050899e-06, /* 1196 */ + 1.035142166679343e-06, /* 1197 */ + 1.023292992280754e-06, /* 1198 */ + 1.011579454259898e-06, /* 1199 */ + 1.000000000000000e-06, /* 1200 */ + 9.885530946569391e-07, /* 1201 */ + 9.772372209558111e-07, /* 1202 */ + 9.660508789898142e-07, /* 1203 */ + 9.549925860214369e-07, /* 1204 */ + 9.440608762859226e-07, /* 1205 */ + 9.332543007969905e-07, /* 1206 */ + 9.225714271547628e-07, /* 1207 */ + 9.120108393559096e-07, /* 1208 */ + 9.015711376059570e-07, /* 1209 */ + 8.912509381337459e-07, /* 1210 */ + 8.810488730080147e-07, /* 1211 */ + 8.709635899560814e-07, /* 1212 */ + 8.609937521845999e-07, /* 1213 */ + 8.511380382023759e-07, /* 1214 */ + 8.413951416451948e-07, /* 1215 */ + 8.317637711026709e-07, /* 1216 */ + 8.222426499470712e-07, /* 1217 */ + 8.128305161640995e-07, /* 1218 */ + 8.035261221856178e-07, /* 1219 */ + 7.943282347242822e-07, /* 1220 */ + 7.852356346100710e-07, /* 1221 */ + 7.762471166286911e-07, /* 1222 */ + 7.673614893618186e-07, /* 1223 */ + 7.585775750291836e-07, /* 1224 */ + 7.498942093324558e-07, /* 1225 */ + 7.413102413009177e-07, /* 1226 */ + 7.328245331389045e-07, /* 1227 */ + 7.244359600749906e-07, /* 1228 */ + 7.161434102129027e-07, /* 1229 */ + 7.079457843841374e-07, /* 1230 */ + 6.998419960022731e-07, /* 1231 */ + 6.918309709189363e-07, /* 1232 */ + 6.839116472814293e-07, /* 1233 */ + 6.760829753919819e-07, /* 1234 */ + 6.683439175686149e-07, /* 1235 */ + 6.606934480075964e-07, /* 1236 */ + 6.531305526474730e-07, /* 1237 */ + 6.456542290346550e-07, /* 1238 */ + 6.382634861905483e-07, /* 1239 */ + 6.309573444801930e-07, /* 1240 */ + 6.237348354824191e-07, /* 1241 */ + 6.165950018614822e-07, /* 1242 */ + 6.095368972401694e-07, /* 1243 */ + 6.025595860743581e-07, /* 1244 */ + 5.956621435290109e-07, /* 1245 */ + 5.888436553555884e-07, /* 1246 */ + 5.821032177708709e-07, /* 1247 */ + 5.754399373371567e-07, /* 1248 */ + 5.688529308438413e-07, /* 1249 */ + 5.623413251903490e-07, /* 1250 */ + 5.559042572704038e-07, /* 1251 */ + 5.495408738576248e-07, /* 1252 */ + 5.432503314924336e-07, /* 1253 */ + 5.370317963702533e-07, /* 1254 */ + 5.308844442309879e-07, /* 1255 */ + 5.248074602497723e-07, /* 1256 */ + 5.188000389289610e-07, /* 1257 */ + 5.128613839913648e-07, /* 1258 */ + 5.069907082747045e-07, /* 1259 */ + 5.011872336272725e-07, /* 1260 */ + 4.954501908047906e-07, /* 1261 */ + 4.897788193684466e-07, /* 1262 */ + 4.841723675840989e-07, /* 1263 */ + 4.786300923226380e-07, /* 1264 */ + 4.731512589614803e-07, /* 1265 */ + 4.677351412871981e-07, /* 1266 */ + 4.623810213992603e-07, /* 1267 */ + 4.570881896148752e-07, /* 1268 */ + 4.518559443749226e-07, /* 1269 */ + 4.466835921509635e-07, /* 1270 */ + 4.415704473533121e-07, /* 1271 */ + 4.365158322401657e-07, /* 1272 */ + 4.315190768277650e-07, /* 1273 */ + 4.265795188015925e-07, /* 1274 */ + 4.216965034285823e-07, /* 1275 */ + 4.168693834703355e-07, /* 1276 */ + 4.120975190973304e-07, /* 1277 */ + 4.073802778041130e-07, /* 1278 */ + 4.027170343254595e-07, /* 1279 */ + 3.981071705534969e-07, /* 1280 */ + 3.935500754557772e-07, /* 1281 */ + 3.890451449942805e-07, /* 1282 */ + 3.845917820453535e-07, /* 1283 */ + 3.801893963205613e-07, /* 1284 */ + 3.758374042884443e-07, /* 1285 */ + 3.715352290971728e-07, /* 1286 */ + 3.672823004980850e-07, /* 1287 */ + 3.630780547701010e-07, /* 1288 */ + 3.589219346450050e-07, /* 1289 */ + 3.548133892335753e-07, /* 1290 */ + 3.507518739525680e-07, /* 1291 */ + 3.467368504525316e-07, /* 1292 */ + 3.427677865464504e-07, /* 1293 */ + 3.388441561392027e-07, /* 1294 */ + 3.349654391578279e-07, /* 1295 */ + 3.311311214825908e-07, /* 1296 */ + 3.273406948788380e-07, /* 1297 */ + 3.235936569296281e-07, /* 1298 */ + 3.198895109691397e-07, /* 1299 */ + 3.162277660168379e-07, /* 1300 */ + 3.126079367123956e-07, /* 1301 */ + 3.090295432513592e-07, /* 1302 */ + 3.054921113215515e-07, /* 1303 */ + 3.019951720402019e-07, /* 1304 */ + 2.985382618917957e-07, /* 1305 */ + 2.951209226666384e-07, /* 1306 */ + 2.917427014001166e-07, /* 1307 */ + 2.884031503126606e-07, /* 1308 */ + 2.851018267503910e-07, /* 1309 */ + 2.818382931264455e-07, /* 1310 */ + 2.786121168629772e-07, /* 1311 */ + 2.754228703338169e-07, /* 1312 */ + 2.722701308077910e-07, /* 1313 */ + 2.691534803926914e-07, /* 1314 */ + 2.660725059798808e-07, /* 1315 */ + 2.630267991895382e-07, /* 1316 */ + 2.600159563165272e-07, /* 1317 */ + 2.570395782768865e-07, /* 1318 */ + 2.540972705549306e-07, /* 1319 */ + 2.511886431509582e-07, /* 1320 */ + 2.483133105295568e-07, /* 1321 */ + 2.454708915685029e-07, /* 1322 */ + 2.426610095082415e-07, /* 1323 */ + 2.398832919019490e-07, /* 1324 */ + 2.371373705661655e-07, /* 1325 */ + 2.344228815319923e-07, /* 1326 */ + 2.317394649968480e-07, /* 1327 */ + 2.290867652767775e-07, /* 1328 */ + 2.264644307593062e-07, /* 1329 */ + 2.238721138568338e-07, /* 1330 */ + 2.213094709605637e-07, /* 1331 */ + 2.187761623949552e-07, /* 1332 */ + 2.162718523727020e-07, /* 1333 */ + 2.137962089502232e-07, /* 1334 */ + 2.113489039836648e-07, /* 1335 */ + 2.089296130854041e-07, /* 1336 */ + 2.065380155810531e-07, /* 1337 */ + 2.041737944669528e-07, /* 1338 */ + 2.018366363681560e-07, /* 1339 */ + 1.995262314968879e-07, /* 1340 */ + 1.972422736114853e-07, /* 1341 */ + 1.949844599758046e-07, /* 1342 */ + 1.927524913190937e-07, /* 1343 */ + 1.905460717963248e-07, /* 1344 */ + 1.883649089489802e-07, /* 1345 */ + 1.862087136662866e-07, /* 1346 */ + 1.840772001468954e-07, /* 1347 */ + 1.819700858609982e-07, /* 1348 */ + 1.798870915128787e-07, /* 1349 */ + 1.778279410038923e-07, /* 1350 */ + 1.757923613958693e-07, /* 1351 */ + 1.737800828749376e-07, /* 1352 */ + 1.717908387157589e-07, /* 1353 */ + 1.698243652461746e-07, /* 1354 */ + 1.678804018122559e-07, /* 1355 */ + 1.659586907437560e-07, /* 1356 */ + 1.640589773199539e-07, /* 1357 */ + 1.621810097358930e-07, /* 1358 */ + 1.603245390690042e-07, /* 1359 */ + 1.584893192461114e-07, /* 1360 */ + 1.566751070108150e-07, /* 1361 */ + 1.548816618912483e-07, /* 1362 */ + 1.531087461682029e-07, /* 1363 */ + 1.513561248436207e-07, /* 1364 */ + 1.496235656094433e-07, /* 1365 */ + 1.479108388168207e-07, /* 1366 */ + 1.462177174456718e-07, /* 1367 */ + 1.445439770745928e-07, /* 1368 */ + 1.428893958511104e-07, /* 1369 */ + 1.412537544622755e-07, /* 1370 */ + 1.396368361055936e-07, /* 1371 */ + 1.380384264602884e-07, /* 1372 */ + 1.364583136588924e-07, /* 1373 */ + 1.348962882591653e-07, /* 1374 */ + 1.333521432163324e-07, /* 1375 */ + 1.318256738556407e-07, /* 1376 */ + 1.303166778452300e-07, /* 1377 */ + 1.288249551693135e-07, /* 1378 */ + 1.273503081016663e-07, /* 1379 */ + 1.258925411794166e-07, /* 1380 */ + 1.244514611771385e-07, /* 1381 */ + 1.230268770812381e-07, /* 1382 */ + 1.216186000646368e-07, /* 1383 */ + 1.202264434617413e-07, /* 1384 */ + 1.188502227437019e-07, /* 1385 */ + 1.174897554939530e-07, /* 1386 */ + 1.161448613840344e-07, /* 1387 */ + 1.148153621496882e-07, /* 1388 */ + 1.135010815672314e-07, /* 1389 */ + 1.122018454301963e-07, /* 1390 */ + 1.109174815262401e-07, /* 1391 */ + 1.096478196143185e-07, /* 1392 */ + 1.083926914021204e-07, /* 1393 */ + 1.071519305237607e-07, /* 1394 */ + 1.059253725177290e-07, /* 1395 */ + 1.047128548050899e-07, /* 1396 */ + 1.035142166679343e-07, /* 1397 */ + 1.023292992280754e-07, /* 1398 */ + 1.011579454259898e-07, /* 1399 */ + 1.000000000000000e-07, /* 1400 */ + 9.885530946569391e-08, /* 1401 */ + 9.772372209558111e-08, /* 1402 */ + 9.660508789898141e-08, /* 1403 */ + 9.549925860214369e-08, /* 1404 */ + 9.440608762859226e-08, /* 1405 */ + 9.332543007969905e-08, /* 1406 */ + 9.225714271547629e-08, /* 1407 */ + 9.120108393559096e-08, /* 1408 */ + 9.015711376059570e-08, /* 1409 */ + 8.912509381337459e-08, /* 1410 */ + 8.810488730080147e-08, /* 1411 */ + 8.709635899560814e-08, /* 1412 */ + 8.609937521845999e-08, /* 1413 */ + 8.511380382023759e-08, /* 1414 */ + 8.413951416451947e-08, /* 1415 */ + 8.317637711026709e-08, /* 1416 */ + 8.222426499470712e-08, /* 1417 */ + 8.128305161640995e-08, /* 1418 */ + 8.035261221856178e-08, /* 1419 */ + 7.943282347242822e-08, /* 1420 */ + 7.852356346100710e-08, /* 1421 */ + 7.762471166286912e-08, /* 1422 */ + 7.673614893618186e-08, /* 1423 */ + 7.585775750291836e-08, /* 1424 */ + 7.498942093324559e-08, /* 1425 */ + 7.413102413009177e-08, /* 1426 */ + 7.328245331389045e-08, /* 1427 */ + 7.244359600749906e-08, /* 1428 */ + 7.161434102129027e-08, /* 1429 */ + 7.079457843841373e-08, /* 1430 */ + 6.998419960022731e-08, /* 1431 */ + 6.918309709189362e-08, /* 1432 */ + 6.839116472814293e-08, /* 1433 */ + 6.760829753919818e-08, /* 1434 */ + 6.683439175686149e-08, /* 1435 */ + 6.606934480075964e-08, /* 1436 */ + 6.531305526474729e-08, /* 1437 */ + 6.456542290346549e-08, /* 1438 */ + 6.382634861905482e-08, /* 1439 */ + 6.309573444801930e-08, /* 1440 */ +}; + +static const fluid_real_t fluid_concave_tab[128] = { + 0.000000000000000e+00, /* 0 */ + 1.430489932664148e-03, /* 1 */ + 2.872378311625188e-03, /* 2 */ + 4.325848247384082e-03, /* 3 */ + 5.791087298566222e-03, /* 4 */ + 7.268287617170261e-03, /* 5 */ + 8.757646099794495e-03, /* 6 */ + 1.025936454513835e-02, /* 7 */ + 1.177364981809422e-02, /* 8 */ + 1.330071402076312e-02, /* 9 */ + 1.484077467074801e-02, /* 10 */ + 1.639405488709933e-02, /* 11 */ + 1.796078358431049e-02, /* 12 */ + 1.954119567478511e-02, /* 13 */ + 2.113553228022380e-02, /* 14 */ + 2.274404095240635e-02, /* 15 */ + 2.436697590387476e-02, /* 16 */ + 2.600459824905493e-02, /* 17 */ + 2.765717625638884e-02, /* 18 */ + 2.932498561208632e-02, /* 19 */ + 3.100830969614468e-02, /* 20 */ + 3.270743987132776e-02, /* 21 */ + 3.442267578584116e-02, /* 22 */ + 3.615432569049021e-02, /* 23 */ + 3.790270677116028e-02, /* 24 */ + 3.966814549751637e-02, /* 25 */ + 4.145097798888096e-02, /* 26 */ + 4.325155039831535e-02, /* 27 */ + 4.507021931600289e-02, /* 28 */ + 4.690735219310917e-02, /* 29 */ + 4.876332778738000e-02, /* 30 */ + 5.063853663182850e-02, /* 31 */ + 5.253338152796212e-02, /* 32 */ + 5.444827806510758e-02, /* 33 */ + 5.638365516750906e-02, /* 34 */ + 5.833995567100066e-02, /* 35 */ + 6.031763693119303e-02, /* 36 */ + 6.231717146526333e-02, /* 37 */ + 6.433904762960169e-02, /* 38 */ + 6.638377033574509e-02, /* 39 */ + 6.845186180722430e-02, /* 40 */ + 7.054386238016214e-02, /* 41 */ + 7.266033135069339e-02, /* 42 */ + 7.480184787253133e-02, /* 43 */ + 7.696901190828456e-02, /* 44 */ + 7.916244523843340e-02, /* 45 */ + 8.138279253221128e-02, /* 46 */ + 8.363072248500553e-02, /* 47 */ + 8.590692902729810e-02, /* 48 */ + 8.821213261061520e-02, /* 49 */ + 9.054708157644790e-02, /* 50 */ + 9.291255361465230e-02, /* 51 */ + 9.530935731844034e-02, /* 52 */ + 9.773833384374195e-02, /* 53 */ + 1.002003586814587e-01, /* 54 */ + 1.026963435519535e-01, /* 55 */ + 1.052272384320340e-01, /* 56 */ + 1.077940337257083e-01, /* 57 */ + 1.103977625911256e-01, /* 58 */ + 1.130395034373836e-01, /* 59 */ + 1.157203826063043e-01, /* 60 */ + 1.184415772558701e-01, /* 61 */ + 1.212043184637922e-01, /* 62 */ + 1.240098945716957e-01, /* 63 */ + 1.268596547926563e-01, /* 64 */ + 1.297550131073762e-01, /* 65 */ + 1.326974524771624e-01, /* 66 */ + 1.356885294051305e-01, /* 67 */ + 1.387298788807553e-01, /* 68 */ + 1.418232197470915e-01, /* 69 */ + 1.449703605347773e-01, /* 70 */ + 1.481732058123985e-01, /* 71 */ + 1.514337631090471e-01, /* 72 */ + 1.547541504720785e-01, /* 73 */ + 1.581366047313199e-01, /* 74 */ + 1.615834905504824e-01, /* 75 */ + 1.650973103575085e-01, /* 76 */ + 1.686807152583075e-01, /* 77 */ + 1.723365170531014e-01, /* 78 */ + 1.760677014918207e-01, /* 79 */ + 1.798774429250997e-01, /* 80 */ + 1.837691205309928e-01, /* 81 */ + 1.877463363252555e-01, /* 82 */ + 1.918129351957373e-01, /* 83 */ + 1.959730272401543e-01, /* 84 */ + 2.002310127325235e-01, /* 85 */ + 2.045916100984256e-01, /* 86 */ + 2.090598873449977e-01, /* 87 */ + 2.136412974706074e-01, /* 88 */ + 2.183417184746445e-01, /* 89 */ + 2.231674987037341e-01, /* 90 */ + 2.281255084119456e-01, /* 91 */ + 2.332231985857005e-01, /* 92 */ + 2.384686682973757e-01, /* 93 */ + 2.438707421158622e-01, /* 94 */ + 2.494390594316878e-01, /* 95 */ + 2.551841779673684e-01, /* 96 */ + 2.611176942651227e-01, /* 97 */ + 2.672523846070836e-01, /* 98 */ + 2.736023706723907e-01, /* 99 */ + 2.801833153320706e-01, /* 100 */ + 2.870126554104745e-01, /* 101 */ + 2.941098801182996e-01, /* 102 */ + 3.014968663518128e-01, /* 103 */ + 3.091982853909850e-01, /* 104 */ + 3.172421000557294e-01, /* 105 */ + 3.256601775925156e-01, /* 106 */ + 3.344890522049899e-01, /* 107 */ + 3.437708833346366e-01, /* 108 */ + 3.535546732719378e-01, /* 109 */ + 3.638978331573678e-01, /* 110 */ + 3.748682242916800e-01, /* 111 */ + 3.865468591251148e-01, /* 112 */ + 3.990315355323829e-01, /* 113 */ + 4.124418202704667e-01, /* 114 */ + 4.269260312118049e-01, /* 115 */ + 4.426712649157216e-01, /* 116 */ + 4.599182170649820e-01, /* 117 */ + 4.789838381319300e-01, /* 118 */ + 5.002973891516722e-01, /* 119 */ + 5.244607003923749e-01, /* 120 */ + 5.523551960717971e-01, /* 121 */ + 5.853473819249742e-01, /* 122 */ + 6.257265540116643e-01, /* 123 */ + 6.777843609317893e-01, /* 124 */ + 7.511557188716564e-01, /* 125 */ + 8.765848837316486e-01, /* 126 */ + 1.000000000000000e+00, /* 127 */ +}; + +static const fluid_real_t fluid_convex_tab[128] = { + 0.000000000000000e+00, /* 0 */ + 1.234151162683514e-01, /* 1 */ + 2.488442811283436e-01, /* 2 */ + 3.222156390682107e-01, /* 3 */ + 3.742734459883357e-01, /* 4 */ + 4.146526180750258e-01, /* 5 */ + 4.476448039282029e-01, /* 6 */ + 4.755392996076251e-01, /* 7 */ + 4.997026108483278e-01, /* 8 */ + 5.210161618680700e-01, /* 9 */ + 5.400817829350180e-01, /* 10 */ + 5.573287350842784e-01, /* 11 */ + 5.730739687881951e-01, /* 12 */ + 5.875581797295333e-01, /* 13 */ + 6.009684644676172e-01, /* 14 */ + 6.134531408748852e-01, /* 15 */ + 6.251317757083199e-01, /* 16 */ + 6.361021668426321e-01, /* 17 */ + 6.464453267280622e-01, /* 18 */ + 6.562291166653634e-01, /* 19 */ + 6.655109477950101e-01, /* 20 */ + 6.743398224074844e-01, /* 21 */ + 6.827578999442706e-01, /* 22 */ + 6.908017146090151e-01, /* 23 */ + 6.985031336481872e-01, /* 24 */ + 7.058901198817004e-01, /* 25 */ + 7.129873445895255e-01, /* 26 */ + 7.198166846679294e-01, /* 27 */ + 7.263976293276093e-01, /* 28 */ + 7.327476153929164e-01, /* 29 */ + 7.388823057348772e-01, /* 30 */ + 7.448158220326315e-01, /* 31 */ + 7.505609405683121e-01, /* 32 */ + 7.561292578841378e-01, /* 33 */ + 7.615313317026243e-01, /* 34 */ + 7.667768014142995e-01, /* 35 */ + 7.718744915880543e-01, /* 36 */ + 7.768325012962659e-01, /* 37 */ + 7.816582815253555e-01, /* 38 */ + 7.863587025293927e-01, /* 39 */ + 7.909401126550023e-01, /* 40 */ + 7.954083899015745e-01, /* 41 */ + 7.997689872674765e-01, /* 42 */ + 8.040269727598457e-01, /* 43 */ + 8.081870648042627e-01, /* 44 */ + 8.122536636747445e-01, /* 45 */ + 8.162308794690072e-01, /* 46 */ + 8.201225570749002e-01, /* 47 */ + 8.239322985081794e-01, /* 48 */ + 8.276634829468986e-01, /* 49 */ + 8.313192847416925e-01, /* 50 */ + 8.349026896424915e-01, /* 51 */ + 8.384165094495176e-01, /* 52 */ + 8.418633952686800e-01, /* 53 */ + 8.452458495279216e-01, /* 54 */ + 8.485662368909529e-01, /* 55 */ + 8.518267941876015e-01, /* 56 */ + 8.550296394652227e-01, /* 57 */ + 8.581767802529086e-01, /* 58 */ + 8.612701211192447e-01, /* 59 */ + 8.643114705948695e-01, /* 60 */ + 8.673025475228375e-01, /* 61 */ + 8.702449868926238e-01, /* 62 */ + 8.731403452073437e-01, /* 63 */ + 8.759901054283044e-01, /* 64 */ + 8.787956815362078e-01, /* 65 */ + 8.815584227441300e-01, /* 66 */ + 8.842796173936956e-01, /* 67 */ + 8.869604965626164e-01, /* 68 */ + 8.896022374088743e-01, /* 69 */ + 8.922059662742916e-01, /* 70 */ + 8.947727615679660e-01, /* 71 */ + 8.973036564480465e-01, /* 72 */ + 8.997996413185413e-01, /* 73 */ + 9.022616661562580e-01, /* 74 */ + 9.046906426815596e-01, /* 75 */ + 9.070874463853477e-01, /* 76 */ + 9.094529184235521e-01, /* 77 */ + 9.117878673893848e-01, /* 78 */ + 9.140930709727019e-01, /* 79 */ + 9.163692775149945e-01, /* 80 */ + 9.186172074677887e-01, /* 81 */ + 9.208375547615666e-01, /* 82 */ + 9.230309880917155e-01, /* 83 */ + 9.251981521274687e-01, /* 84 */ + 9.273396686493066e-01, /* 85 */ + 9.294561376198378e-01, /* 86 */ + 9.315481381927757e-01, /* 87 */ + 9.336162296642549e-01, /* 88 */ + 9.356609523703983e-01, /* 89 */ + 9.376828285347367e-01, /* 90 */ + 9.396823630688069e-01, /* 91 */ + 9.416600443289993e-01, /* 92 */ + 9.436163448324909e-01, /* 93 */ + 9.455517219348925e-01, /* 94 */ + 9.474666184720378e-01, /* 95 */ + 9.493614633681715e-01, /* 96 */ + 9.512366722126200e-01, /* 97 */ + 9.530926478068908e-01, /* 98 */ + 9.549297806839971e-01, /* 99 */ + 9.567484496016847e-01, /* 100 */ + 9.585490220111190e-01, /* 101 */ + 9.603318545024836e-01, /* 102 */ + 9.620972932288397e-01, /* 103 */ + 9.638456743095098e-01, /* 104 */ + 9.655773242141589e-01, /* 105 */ + 9.672925601286723e-01, /* 106 */ + 9.689916903038553e-01, /* 107 */ + 9.706750143879137e-01, /* 108 */ + 9.723428237436111e-01, /* 109 */ + 9.739954017509451e-01, /* 110 */ + 9.756330240961253e-01, /* 111 */ + 9.772559590475937e-01, /* 112 */ + 9.788644677197762e-01, /* 113 */ + 9.804588043252149e-01, /* 114 */ + 9.820392164156895e-01, /* 115 */ + 9.836059451129007e-01, /* 116 */ + 9.851592253292520e-01, /* 117 */ + 9.866992859792368e-01, /* 118 */ + 9.882263501819057e-01, /* 119 */ + 9.897406354548617e-01, /* 120 */ + 9.912423539002055e-01, /* 121 */ + 9.927317123828298e-01, /* 122 */ + 9.942089127014337e-01, /* 123 */ + 9.956741517526159e-01, /* 124 */ + 9.971276216883748e-01, /* 125 */ + 9.985695100673359e-01, /* 126 */ + 1.000000000000000e+00, /* 127 */ +}; + +static const fluid_real_t fluid_pan_tab[1002] = { + 0.000000000000000e+00, /* 0 */ + 1.569226455665206e-03, /* 1 */ + 3.138449047152344e-03, /* 2 */ + 4.707663910292860e-03, /* 3 */ + 6.276867180937232e-03, /* 4 */ + 7.846054994964487e-03, /* 5 */ + 9.415223488291706e-03, /* 6 */ + 1.098436879688355e-02, /* 7 */ + 1.255348705676178e-02, /* 8 */ + 1.412257440401476e-02, /* 9 */ + 1.569162697480696e-02, /* 10 */ + 1.726064090538850e-02, /* 11 */ + 1.882961233210465e-02, /* 12 */ + 2.039853739140535e-02, /* 13 */ + 2.196741221985471e-02, /* 14 */ + 2.353623295414053e-02, /* 15 */ + 2.510499573108383e-02, /* 16 */ + 2.667369668764833e-02, /* 17 */ + 2.824233196094998e-02, /* 18 */ + 2.981089768826650e-02, /* 19 */ + 3.137939000704683e-02, /* 20 */ + 3.294780505492070e-02, /* 21 */ + 3.451613896970813e-02, /* 22 */ + 3.608438788942888e-02, /* 23 */ + 3.765254795231206e-02, /* 24 */ + 3.922061529680555e-02, /* 25 */ + 4.078858606158557e-02, /* 26 */ + 4.235645638556616e-02, /* 27 */ + 4.392422240790868e-02, /* 28 */ + 4.549188026803135e-02, /* 29 */ + 4.705942610561870e-02, /* 30 */ + 4.862685606063118e-02, /* 31 */ + 5.019416627331453e-02, /* 32 */ + 5.176135288420938e-02, /* 33 */ + 5.332841203416074e-02, /* 34 */ + 5.489533986432744e-02, /* 35 */ + 5.646213251619175e-02, /* 36 */ + 5.802878613156876e-02, /* 37 */ + 5.959529685261596e-02, /* 38 */ + 6.116166082184270e-02, /* 39 */ + 6.272787418211972e-02, /* 40 */ + 6.429393307668861e-02, /* 41 */ + 6.585983364917131e-02, /* 42 */ + 6.742557204357968e-02, /* 43 */ + 6.899114440432493e-02, /* 44 */ + 7.055654687622705e-02, /* 45 */ + 7.212177560452446e-02, /* 46 */ + 7.368682673488337e-02, /* 47 */ + 7.525169641340737e-02, /* 48 */ + 7.681638078664681e-02, /* 49 */ + 7.838087600160838e-02, /* 50 */ + 7.994517820576458e-02, /* 51 */ + 8.150928354706316e-02, /* 52 */ + 8.307318817393668e-02, /* 53 */ + 8.463688823531192e-02, /* 54 */ + 8.620037988061940e-02, /* 55 */ + 8.776365925980288e-02, /* 56 */ + 8.932672252332882e-02, /* 57 */ + 9.088956582219582e-02, /* 58 */ + 9.245218530794418e-02, /* 59 */ + 9.401457713266531e-02, /* 60 */ + 9.557673744901124e-02, /* 61 */ + 9.713866241020409e-02, /* 62 */ + 9.870034817004553e-02, /* 63 */ + 1.002617908829262e-01, /* 64 */ + 1.018229867038354e-01, /* 65 */ + 1.033839317883702e-01, /* 66 */ + 1.049446222927451e-01, /* 67 */ + 1.065050543738018e-01, /* 68 */ + 1.080652241890180e-01, /* 69 */ + 1.096251278965173e-01, /* 70 */ + 1.111847616550789e-01, /* 71 */ + 1.127441216241462e-01, /* 72 */ + 1.143032039638373e-01, /* 73 */ + 1.158620048349536e-01, /* 74 */ + 1.174205203989899e-01, /* 75 */ + 1.189787468181433e-01, /* 76 */ + 1.205366802553231e-01, /* 77 */ + 1.220943168741599e-01, /* 78 */ + 1.236516528390153e-01, /* 79 */ + 1.252086843149914e-01, /* 80 */ + 1.267654074679398e-01, /* 81 */ + 1.283218184644714e-01, /* 82 */ + 1.298779134719661e-01, /* 83 */ + 1.314336886585815e-01, /* 84 */ + 1.329891401932629e-01, /* 85 */ + 1.345442642457527e-01, /* 86 */ + 1.360990569865997e-01, /* 87 */ + 1.376535145871682e-01, /* 88 */ + 1.392076332196483e-01, /* 89 */ + 1.407614090570644e-01, /* 90 */ + 1.423148382732851e-01, /* 91 */ + 1.438679170430328e-01, /* 92 */ + 1.454206415418926e-01, /* 93 */ + 1.469730079463220e-01, /* 94 */ + 1.485250124336605e-01, /* 95 */ + 1.500766511821384e-01, /* 96 */ + 1.516279203708872e-01, /* 97 */ + 1.531788161799479e-01, /* 98 */ + 1.547293347902813e-01, /* 99 */ + 1.562794723837767e-01, /* 100 */ + 1.578292251432621e-01, /* 101 */ + 1.593785892525127e-01, /* 102 */ + 1.609275608962610e-01, /* 103 */ + 1.624761362602058e-01, /* 104 */ + 1.640243115310219e-01, /* 105 */ + 1.655720828963691e-01, /* 106 */ + 1.671194465449020e-01, /* 107 */ + 1.686663986662791e-01, /* 108 */ + 1.702129354511722e-01, /* 109 */ + 1.717590530912760e-01, /* 110 */ + 1.733047477793173e-01, /* 111 */ + 1.748500157090643e-01, /* 112 */ + 1.763948530753363e-01, /* 113 */ + 1.779392560740125e-01, /* 114 */ + 1.794832209020421e-01, /* 115 */ + 1.810267437574530e-01, /* 116 */ + 1.825698208393617e-01, /* 117 */ + 1.841124483479821e-01, /* 118 */ + 1.856546224846354e-01, /* 119 */ + 1.871963394517591e-01, /* 120 */ + 1.887375954529167e-01, /* 121 */ + 1.902783866928064e-01, /* 122 */ + 1.918187093772711e-01, /* 123 */ + 1.933585597133076e-01, /* 124 */ + 1.948979339090757e-01, /* 125 */ + 1.964368281739078e-01, /* 126 */ + 1.979752387183178e-01, /* 127 */ + 1.995131617540112e-01, /* 128 */ + 2.010505934938938e-01, /* 129 */ + 2.025875301520810e-01, /* 130 */ + 2.041239679439075e-01, /* 131 */ + 2.056599030859366e-01, /* 132 */ + 2.071953317959691e-01, /* 133 */ + 2.087302502930529e-01, /* 134 */ + 2.102646547974925e-01, /* 135 */ + 2.117985415308578e-01, /* 136 */ + 2.133319067159940e-01, /* 137 */ + 2.148647465770304e-01, /* 138 */ + 2.163970573393899e-01, /* 139 */ + 2.179288352297983e-01, /* 140 */ + 2.194600764762938e-01, /* 141 */ + 2.209907773082357e-01, /* 142 */ + 2.225209339563144e-01, /* 143 */ + 2.240505426525601e-01, /* 144 */ + 2.255795996303523e-01, /* 145 */ + 2.271081011244294e-01, /* 146 */ + 2.286360433708974e-01, /* 147 */ + 2.301634226072394e-01, /* 148 */ + 2.316902350723250e-01, /* 149 */ + 2.332164770064195e-01, /* 150 */ + 2.347421446511931e-01, /* 151 */ + 2.362672342497300e-01, /* 152 */ + 2.377917420465381e-01, /* 153 */ + 2.393156642875578e-01, /* 154 */ + 2.408389972201714e-01, /* 155 */ + 2.423617370932123e-01, /* 156 */ + 2.438838801569746e-01, /* 157 */ + 2.454054226632218e-01, /* 158 */ + 2.469263608651961e-01, /* 159 */ + 2.484466910176282e-01, /* 160 */ + 2.499664093767457e-01, /* 161 */ + 2.514855122002829e-01, /* 162 */ + 2.530039957474898e-01, /* 163 */ + 2.545218562791415e-01, /* 164 */ + 2.560390900575471e-01, /* 165 */ + 2.575556933465591e-01, /* 166 */ + 2.590716624115826e-01, /* 167 */ + 2.605869935195843e-01, /* 168 */ + 2.621016829391021e-01, /* 169 */ + 2.636157269402540e-01, /* 170 */ + 2.651291217947470e-01, /* 171 */ + 2.666418637758871e-01, /* 172 */ + 2.681539491585875e-01, /* 173 */ + 2.696653742193788e-01, /* 174 */ + 2.711761352364170e-01, /* 175 */ + 2.726862284894938e-01, /* 176 */ + 2.741956502600449e-01, /* 177 */ + 2.757043968311598e-01, /* 178 */ + 2.772124644875906e-01, /* 179 */ + 2.787198495157609e-01, /* 180 */ + 2.802265482037756e-01, /* 181 */ + 2.817325568414297e-01, /* 182 */ + 2.832378717202171e-01, /* 183 */ + 2.847424891333405e-01, /* 184 */ + 2.862464053757197e-01, /* 185 */ + 2.877496167440013e-01, /* 186 */ + 2.892521195365677e-01, /* 187 */ + 2.907539100535459e-01, /* 188 */ + 2.922549845968172e-01, /* 189 */ + 2.937553394700257e-01, /* 190 */ + 2.952549709785878e-01, /* 191 */ + 2.967538754297011e-01, /* 192 */ + 2.982520491323535e-01, /* 193 */ + 2.997494883973326e-01, /* 194 */ + 3.012461895372343e-01, /* 195 */ + 3.027421488664720e-01, /* 196 */ + 3.042373627012863e-01, /* 197 */ + 3.057318273597529e-01, /* 198 */ + 3.072255391617928e-01, /* 199 */ + 3.087184944291809e-01, /* 200 */ + 3.102106894855546e-01, /* 201 */ + 3.117021206564236e-01, /* 202 */ + 3.131927842691789e-01, /* 203 */ + 3.146826766531011e-01, /* 204 */ + 3.161717941393702e-01, /* 205 */ + 3.176601330610744e-01, /* 206 */ + 3.191476897532190e-01, /* 207 */ + 3.206344605527355e-01, /* 208 */ + 3.221204417984906e-01, /* 209 */ + 3.236056298312954e-01, /* 210 */ + 3.250900209939142e-01, /* 211 */ + 3.265736116310736e-01, /* 212 */ + 3.280563980894714e-01, /* 213 */ + 3.295383767177856e-01, /* 214 */ + 3.310195438666838e-01, /* 215 */ + 3.324998958888314e-01, /* 216 */ + 3.339794291389013e-01, /* 217 */ + 3.354581399735827e-01, /* 218 */ + 3.369360247515896e-01, /* 219 */ + 3.384130798336705e-01, /* 220 */ + 3.398893015826167e-01, /* 221 */ + 3.413646863632719e-01, /* 222 */ + 3.428392305425407e-01, /* 223 */ + 3.443129304893974e-01, /* 224 */ + 3.457857825748955e-01, /* 225 */ + 3.472577831721762e-01, /* 226 */ + 3.487289286564775e-01, /* 227 */ + 3.501992154051432e-01, /* 228 */ + 3.516686397976315e-01, /* 229 */ + 3.531371982155242e-01, /* 230 */ + 3.546048870425356e-01, /* 231 */ + 3.560717026645214e-01, /* 232 */ + 3.575376414694875e-01, /* 233 */ + 3.590026998475987e-01, /* 234 */ + 3.604668741911882e-01, /* 235 */ + 3.619301608947659e-01, /* 236 */ + 3.633925563550274e-01, /* 237 */ + 3.648540569708633e-01, /* 238 */ + 3.663146591433674e-01, /* 239 */ + 3.677743592758461e-01, /* 240 */ + 3.692331537738269e-01, /* 241 */ + 3.706910390450675e-01, /* 242 */ + 3.721480114995644e-01, /* 243 */ + 3.736040675495622e-01, /* 244 */ + 3.750592036095618e-01, /* 245 */ + 3.765134160963297e-01, /* 246 */ + 3.779667014289065e-01, /* 247 */ + 3.794190560286163e-01, /* 248 */ + 3.808704763190747e-01, /* 249 */ + 3.823209587261981e-01, /* 250 */ + 3.837704996782126e-01, /* 251 */ + 3.852190956056624e-01, /* 252 */ + 3.866667429414188e-01, /* 253 */ + 3.881134381206893e-01, /* 254 */ + 3.895591775810255e-01, /* 255 */ + 3.910039577623329e-01, /* 256 */ + 3.924477751068791e-01, /* 257 */ + 3.938906260593026e-01, /* 258 */ + 3.953325070666215e-01, /* 259 */ + 3.967734145782425e-01, /* 260 */ + 3.982133450459696e-01, /* 261 */ + 3.996522949240126e-01, /* 262 */ + 4.010902606689959e-01, /* 263 */ + 4.025272387399675e-01, /* 264 */ + 4.039632255984075e-01, /* 265 */ + 4.053982177082367e-01, /* 266 */ + 4.068322115358254e-01, /* 267 */ + 4.082652035500025e-01, /* 268 */ + 4.096971902220634e-01, /* 269 */ + 4.111281680257793e-01, /* 270 */ + 4.125581334374058e-01, /* 271 */ + 4.139870829356915e-01, /* 272 */ + 4.154150130018864e-01, /* 273 */ + 4.168419201197512e-01, /* 274 */ + 4.182678007755651e-01, /* 275 */ + 4.196926514581356e-01, /* 276 */ + 4.211164686588058e-01, /* 277 */ + 4.225392488714641e-01, /* 278 */ + 4.239609885925524e-01, /* 279 */ + 4.253816843210749e-01, /* 280 */ + 4.268013325586062e-01, /* 281 */ + 4.282199298093007e-01, /* 282 */ + 4.296374725799008e-01, /* 283 */ + 4.310539573797453e-01, /* 284 */ + 4.324693807207784e-01, /* 285 */ + 4.338837391175581e-01, /* 286 */ + 4.352970290872648e-01, /* 287 */ + 4.367092471497098e-01, /* 288 */ + 4.381203898273440e-01, /* 289 */ + 4.395304536452664e-01, /* 290 */ + 4.409394351312327e-01, /* 291 */ + 4.423473308156637e-01, /* 292 */ + 4.437541372316541e-01, /* 293 */ + 4.451598509149808e-01, /* 294 */ + 4.465644684041115e-01, /* 295 */ + 4.479679862402133e-01, /* 296 */ + 4.493704009671613e-01, /* 297 */ + 4.507717091315467e-01, /* 298 */ + 4.521719072826857e-01, /* 299 */ + 4.535709919726280e-01, /* 300 */ + 4.549689597561650e-01, /* 301 */ + 4.563658071908386e-01, /* 302 */ + 4.577615308369494e-01, /* 303 */ + 4.591561272575653e-01, /* 304 */ + 4.605495930185300e-01, /* 305 */ + 4.619419246884716e-01, /* 306 */ + 4.633331188388106e-01, /* 307 */ + 4.647231720437686e-01, /* 308 */ + 4.661120808803769e-01, /* 309 */ + 4.674998419284849e-01, /* 310 */ + 4.688864517707680e-01, /* 311 */ + 4.702719069927367e-01, /* 312 */ + 4.716562041827449e-01, /* 313 */ + 4.730393399319976e-01, /* 314 */ + 4.744213108345603e-01, /* 315 */ + 4.758021134873666e-01, /* 316 */ + 4.771817444902270e-01, /* 317 */ + 4.785602004458372e-01, /* 318 */ + 4.799374779597864e-01, /* 319 */ + 4.813135736405655e-01, /* 320 */ + 4.826884840995759e-01, /* 321 */ + 4.840622059511375e-01, /* 322 */ + 4.854347358124970e-01, /* 323 */ + 4.868060703038364e-01, /* 324 */ + 4.881762060482813e-01, /* 325 */ + 4.895451396719093e-01, /* 326 */ + 4.909128678037579e-01, /* 327 */ + 4.922793870758334e-01, /* 328 */ + 4.936446941231185e-01, /* 329 */ + 4.950087855835814e-01, /* 330 */ + 4.963716580981834e-01, /* 331 */ + 4.977333083108875e-01, /* 332 */ + 4.990937328686666e-01, /* 333 */ + 5.004529284215116e-01, /* 334 */ + 5.018108916224400e-01, /* 335 */ + 5.031676191275037e-01, /* 336 */ + 5.045231075957979e-01, /* 337 */ + 5.058773536894682e-01, /* 338 */ + 5.072303540737201e-01, /* 339 */ + 5.085821054168265e-01, /* 340 */ + 5.099326043901359e-01, /* 341 */ + 5.112818476680807e-01, /* 342 */ + 5.126298319281856e-01, /* 343 */ + 5.139765538510754e-01, /* 344 */ + 5.153220101204837e-01, /* 345 */ + 5.166661974232604e-01, /* 346 */ + 5.180091124493803e-01, /* 347 */ + 5.193507518919511e-01, /* 348 */ + 5.206911124472217e-01, /* 349 */ + 5.220301908145902e-01, /* 350 */ + 5.233679836966120e-01, /* 351 */ + 5.247044877990080e-01, /* 352 */ + 5.260396998306727e-01, /* 353 */ + 5.273736165036822e-01, /* 354 */ + 5.287062345333027e-01, /* 355 */ + 5.300375506379977e-01, /* 356 */ + 5.313675615394372e-01, /* 357 */ + 5.326962639625050e-01, /* 358 */ + 5.340236546353070e-01, /* 359 */ + 5.353497302891792e-01, /* 360 */ + 5.366744876586959e-01, /* 361 */ + 5.379979234816776e-01, /* 362 */ + 5.393200344991992e-01, /* 363 */ + 5.406408174555976e-01, /* 364 */ + 5.419602690984802e-01, /* 365 */ + 5.432783861787328e-01, /* 366 */ + 5.445951654505273e-01, /* 367 */ + 5.459106036713303e-01, /* 368 */ + 5.472246976019102e-01, /* 369 */ + 5.485374440063460e-01, /* 370 */ + 5.498488396520350e-01, /* 371 */ + 5.511588813097004e-01, /* 372 */ + 5.524675657533999e-01, /* 373 */ + 5.537748897605331e-01, /* 374 */ + 5.550808501118496e-01, /* 375 */ + 5.563854435914573e-01, /* 376 */ + 5.576886669868294e-01, /* 377 */ + 5.589905170888135e-01, /* 378 */ + 5.602909906916386e-01, /* 379 */ + 5.615900845929231e-01, /* 380 */ + 5.628877955936834e-01, /* 381 */ + 5.641841204983408e-01, /* 382 */ + 5.654790561147299e-01, /* 383 */ + 5.667725992541067e-01, /* 384 */ + 5.680647467311558e-01, /* 385 */ + 5.693554953639987e-01, /* 386 */ + 5.706448419742014e-01, /* 387 */ + 5.719327833867826e-01, /* 388 */ + 5.732193164302208e-01, /* 389 */ + 5.745044379364633e-01, /* 390 */ + 5.757881447409327e-01, /* 391 */ + 5.770704336825353e-01, /* 392 */ + 5.783513016036690e-01, /* 393 */ + 5.796307453502310e-01, /* 394 */ + 5.809087617716253e-01, /* 395 */ + 5.821853477207707e-01, /* 396 */ + 5.834605000541087e-01, /* 397 */ + 5.847342156316107e-01, /* 398 */ + 5.860064913167862e-01, /* 399 */ + 5.872773239766905e-01, /* 400 */ + 5.885467104819324e-01, /* 401 */ + 5.898146477066816e-01, /* 402 */ + 5.910811325286764e-01, /* 403 */ + 5.923461618292324e-01, /* 404 */ + 5.936097324932486e-01, /* 405 */ + 5.948718414092159e-01, /* 406 */ + 5.961324854692253e-01, /* 407 */ + 5.973916615689745e-01, /* 408 */ + 5.986493666077760e-01, /* 409 */ + 5.999055974885650e-01, /* 410 */ + 6.011603511179066e-01, /* 411 */ + 6.024136244060035e-01, /* 412 */ + 6.036654142667041e-01, /* 413 */ + 6.049157176175093e-01, /* 414 */ + 6.061645313795805e-01, /* 415 */ + 6.074118524777474e-01, /* 416 */ + 6.086576778405154e-01, /* 417 */ + 6.099020044000728e-01, /* 418 */ + 6.111448290922987e-01, /* 419 */ + 6.123861488567709e-01, /* 420 */ + 6.136259606367725e-01, /* 421 */ + 6.148642613793004e-01, /* 422 */ + 6.161010480350722e-01, /* 423 */ + 6.173363175585338e-01, /* 424 */ + 6.185700669078673e-01, /* 425 */ + 6.198022930449979e-01, /* 426 */ + 6.210329929356019e-01, /* 427 */ + 6.222621635491136e-01, /* 428 */ + 6.234898018587335e-01, /* 429 */ + 6.247159048414351e-01, /* 430 */ + 6.259404694779729e-01, /* 431 */ + 6.271634927528890e-01, /* 432 */ + 6.283849716545215e-01, /* 433 */ + 6.296049031750114e-01, /* 434 */ + 6.308232843103100e-01, /* 435 */ + 6.320401120601865e-01, /* 436 */ + 6.332553834282352e-01, /* 437 */ + 6.344690954218827e-01, /* 438 */ + 6.356812450523961e-01, /* 439 */ + 6.368918293348892e-01, /* 440 */ + 6.381008452883308e-01, /* 441 */ + 6.393082899355514e-01, /* 442 */ + 6.405141603032511e-01, /* 443 */ + 6.417184534220065e-01, /* 444 */ + 6.429211663262778e-01, /* 445 */ + 6.441222960544170e-01, /* 446 */ + 6.453218396486741e-01, /* 447 */ + 6.465197941552053e-01, /* 448 */ + 6.477161566240799e-01, /* 449 */ + 6.489109241092871e-01, /* 450 */ + 6.501040936687442e-01, /* 451 */ + 6.512956623643031e-01, /* 452 */ + 6.524856272617580e-01, /* 453 */ + 6.536739854308520e-01, /* 454 */ + 6.548607339452851e-01, /* 455 */ + 6.560458698827208e-01, /* 456 */ + 6.572293903247938e-01, /* 457 */ + 6.584112923571167e-01, /* 458 */ + 6.595915730692873e-01, /* 459 */ + 6.607702295548962e-01, /* 460 */ + 6.619472589115332e-01, /* 461 */ + 6.631226582407952e-01, /* 462 */ + 6.642964246482929e-01, /* 463 */ + 6.654685552436579e-01, /* 464 */ + 6.666390471405501e-01, /* 465 */ + 6.678078974566646e-01, /* 466 */ + 6.689751033137388e-01, /* 467 */ + 6.701406618375596e-01, /* 468 */ + 6.713045701579703e-01, /* 469 */ + 6.724668254088780e-01, /* 470 */ + 6.736274247282602e-01, /* 471 */ + 6.747863652581724e-01, /* 472 */ + 6.759436441447544e-01, /* 473 */ + 6.770992585382380e-01, /* 474 */ + 6.782532055929539e-01, /* 475 */ + 6.794054824673381e-01, /* 476 */ + 6.805560863239402e-01, /* 477 */ + 6.817050143294285e-01, /* 478 */ + 6.828522636545991e-01, /* 479 */ + 6.839978314743810e-01, /* 480 */ + 6.851417149678442e-01, /* 481 */ + 6.862839113182062e-01, /* 482 */ + 6.874244177128394e-01, /* 483 */ + 6.885632313432770e-01, /* 484 */ + 6.897003494052213e-01, /* 485 */ + 6.908357690985494e-01, /* 486 */ + 6.919694876273208e-01, /* 487 */ + 6.931015021997839e-01, /* 488 */ + 6.942318100283834e-01, /* 489 */ + 6.953604083297665e-01, /* 490 */ + 6.964872943247901e-01, /* 491 */ + 6.976124652385275e-01, /* 492 */ + 6.987359183002758e-01, /* 493 */ + 6.998576507435618e-01, /* 494 */ + 7.009776598061493e-01, /* 495 */ + 7.020959427300464e-01, /* 496 */ + 7.032124967615111e-01, /* 497 */ + 7.043273191510590e-01, /* 498 */ + 7.054404071534700e-01, /* 499 */ + 7.065517580277947e-01, /* 500 */ + 7.076613690373614e-01, /* 501 */ + 7.087692374497827e-01, /* 502 */ + 7.098753605369623e-01, /* 503 */ + 7.109797355751019e-01, /* 504 */ + 7.120823598447074e-01, /* 505 */ + 7.131832306305963e-01, /* 506 */ + 7.142823452219036e-01, /* 507 */ + 7.153797009120892e-01, /* 508 */ + 7.164752949989442e-01, /* 509 */ + 7.175691247845974e-01, /* 510 */ + 7.186611875755226e-01, /* 511 */ + 7.197514806825439e-01, /* 512 */ + 7.208400014208443e-01, /* 513 */ + 7.219267471099703e-01, /* 514 */ + 7.230117150738401e-01, /* 515 */ + 7.240949026407488e-01, /* 516 */ + 7.251763071433764e-01, /* 517 */ + 7.262559259187933e-01, /* 518 */ + 7.273337563084670e-01, /* 519 */ + 7.284097956582691e-01, /* 520 */ + 7.294840413184817e-01, /* 521 */ + 7.305564906438036e-01, /* 522 */ + 7.316271409933570e-01, /* 523 */ + 7.326959897306943e-01, /* 524 */ + 7.337630342238041e-01, /* 525 */ + 7.348282718451178e-01, /* 526 */ + 7.358916999715164e-01, /* 527 */ + 7.369533159843368e-01, /* 528 */ + 7.380131172693778e-01, /* 529 */ + 7.390711012169073e-01, /* 530 */ + 7.401272652216683e-01, /* 531 */ + 7.411816066828850e-01, /* 532 */ + 7.422341230042699e-01, /* 533 */ + 7.432848115940299e-01, /* 534 */ + 7.443336698648725e-01, /* 535 */ + 7.453806952340122e-01, /* 536 */ + 7.464258851231773e-01, /* 537 */ + 7.474692369586156e-01, /* 538 */ + 7.485107481711011e-01, /* 539 */ + 7.495504161959405e-01, /* 540 */ + 7.505882384729792e-01, /* 541 */ + 7.516242124466076e-01, /* 542 */ + 7.526583355657676e-01, /* 543 */ + 7.536906052839586e-01, /* 544 */ + 7.547210190592443e-01, /* 545 */ + 7.557495743542583e-01, /* 546 */ + 7.567762686362108e-01, /* 547 */ + 7.578010993768948e-01, /* 548 */ + 7.588240640526916e-01, /* 549 */ + 7.598451601445788e-01, /* 550 */ + 7.608643851381341e-01, /* 551 */ + 7.618817365235436e-01, /* 552 */ + 7.628972117956068e-01, /* 553 */ + 7.639108084537428e-01, /* 554 */ + 7.649225240019972e-01, /* 555 */ + 7.659323559490476e-01, /* 556 */ + 7.669403018082099e-01, /* 557 */ + 7.679463590974444e-01, /* 558 */ + 7.689505253393620e-01, /* 559 */ + 7.699527980612303e-01, /* 560 */ + 7.709531747949796e-01, /* 561 */ + 7.719516530772089e-01, /* 562 */ + 7.729482304491924e-01, /* 563 */ + 7.739429044568849e-01, /* 564 */ + 7.749356726509284e-01, /* 565 */ + 7.759265325866578e-01, /* 566 */ + 7.769154818241071e-01, /* 567 */ + 7.779025179280153e-01, /* 568 */ + 7.788876384678325e-01, /* 569 */ + 7.798708410177257e-01, /* 570 */ + 7.808521231565851e-01, /* 571 */ + 7.818314824680298e-01, /* 572 */ + 7.828089165404135e-01, /* 573 */ + 7.837844229668313e-01, /* 574 */ + 7.847579993451246e-01, /* 575 */ + 7.857296432778876e-01, /* 576 */ + 7.866993523724733e-01, /* 577 */ + 7.876671242409992e-01, /* 578 */ + 7.886329565003528e-01, /* 579 */ + 7.895968467721981e-01, /* 580 */ + 7.905587926829811e-01, /* 581 */ + 7.915187918639360e-01, /* 582 */ + 7.924768419510905e-01, /* 583 */ + 7.934329405852717e-01, /* 584 */ + 7.943870854121126e-01, /* 585 */ + 7.953392740820571e-01, /* 586 */ + 7.962895042503660e-01, /* 587 */ + 7.972377735771233e-01, /* 588 */ + 7.981840797272409e-01, /* 589 */ + 7.991284203704654e-01, /* 590 */ + 8.000707931813833e-01, /* 591 */ + 8.010111958394268e-01, /* 592 */ + 8.019496260288795e-01, /* 593 */ + 8.028860814388825e-01, /* 594 */ + 8.038205597634391e-01, /* 595 */ + 8.047530587014217e-01, /* 596 */ + 8.056835759565766e-01, /* 597 */ + 8.066121092375300e-01, /* 598 */ + 8.075386562577938e-01, /* 599 */ + 8.084632147357704e-01, /* 600 */ + 8.093857823947597e-01, /* 601 */ + 8.103063569629634e-01, /* 602 */ + 8.112249361734913e-01, /* 603 */ + 8.121415177643669e-01, /* 604 */ + 8.130560994785325e-01, /* 605 */ + 8.139686790638551e-01, /* 606 */ + 8.148792542731320e-01, /* 607 */ + 8.157878228640961e-01, /* 608 */ + 8.166943825994217e-01, /* 609 */ + 8.175989312467298e-01, /* 610 */ + 8.185014665785935e-01, /* 611 */ + 8.194019863725437e-01, /* 612 */ + 8.203004884110747e-01, /* 613 */ + 8.211969704816493e-01, /* 614 */ + 8.220914303767044e-01, /* 615 */ + 8.229838658936565e-01, /* 616 */ + 8.238742748349069e-01, /* 617 */ + 8.247626550078477e-01, /* 618 */ + 8.256490042248665e-01, /* 619 */ + 8.265333203033521e-01, /* 620 */ + 8.274156010657000e-01, /* 621 */ + 8.282958443393170e-01, /* 622 */ + 8.291740479566283e-01, /* 623 */ + 8.300502097550806e-01, /* 624 */ + 8.309243275771491e-01, /* 625 */ + 8.317963992703420e-01, /* 626 */ + 8.326664226872064e-01, /* 627 */ + 8.335343956853326e-01, /* 628 */ + 8.344003161273608e-01, /* 629 */ + 8.352641818809847e-01, /* 630 */ + 8.361259908189583e-01, /* 631 */ + 8.369857408191002e-01, /* 632 */ + 8.378434297642989e-01, /* 633 */ + 8.386990555425186e-01, /* 634 */ + 8.395526160468036e-01, /* 635 */ + 8.404041091752841e-01, /* 636 */ + 8.412535328311811e-01, /* 637 */ + 8.421008849228118e-01, /* 638 */ + 8.429461633635941e-01, /* 639 */ + 8.437893660720526e-01, /* 640 */ + 8.446304909718232e-01, /* 641 */ + 8.454695359916585e-01, /* 642 */ + 8.463064990654326e-01, /* 643 */ + 8.471413781321465e-01, /* 644 */ + 8.479741711359328e-01, /* 645 */ + 8.488048760260608e-01, /* 646 */ + 8.496334907569423e-01, /* 647 */ + 8.504600132881356e-01, /* 648 */ + 8.512844415843512e-01, /* 649 */ + 8.521067736154566e-01, /* 650 */ + 8.529270073564810e-01, /* 651 */ + 8.537451407876210e-01, /* 652 */ + 8.545611718942449e-01, /* 653 */ + 8.553750986668979e-01, /* 654 */ + 8.561869191013073e-01, /* 655 */ + 8.569966311983870e-01, /* 656 */ + 8.578042329642426e-01, /* 657 */ + 8.586097224101764e-01, /* 658 */ + 8.594130975526924e-01, /* 659 */ + 8.602143564135006e-01, /* 660 */ + 8.610134970195228e-01, /* 661 */ + 8.618105174028966e-01, /* 662 */ + 8.626054156009806e-01, /* 663 */ + 8.633981896563595e-01, /* 664 */ + 8.641888376168482e-01, /* 665 */ + 8.649773575354973e-01, /* 666 */ + 8.657637474705979e-01, /* 667 */ + 8.665480054856857e-01, /* 668 */ + 8.673301296495464e-01, /* 669 */ + 8.681101180362201e-01, /* 670 */ + 8.688879687250065e-01, /* 671 */ + 8.696636798004690e-01, /* 672 */ + 8.704372493524400e-01, /* 673 */ + 8.712086754760251e-01, /* 674 */ + 8.719779562716082e-01, /* 675 */ + 8.727450898448561e-01, /* 676 */ + 8.735100743067228e-01, /* 677 */ + 8.742729077734545e-01, /* 678 */ + 8.750335883665944e-01, /* 679 */ + 8.757921142129869e-01, /* 680 */ + 8.765484834447823e-01, /* 681 */ + 8.773026941994420e-01, /* 682 */ + 8.780547446197419e-01, /* 683 */ + 8.788046328537781e-01, /* 684 */ + 8.795523570549709e-01, /* 685 */ + 8.802979153820696e-01, /* 686 */ + 8.810413059991569e-01, /* 687 */ + 8.817825270756531e-01, /* 688 */ + 8.825215767863213e-01, /* 689 */ + 8.832584533112713e-01, /* 690 */ + 8.839931548359645e-01, /* 691 */ + 8.847256795512183e-01, /* 692 */ + 8.854560256532099e-01, /* 693 */ + 8.861841913434817e-01, /* 694 */ + 8.869101748289453e-01, /* 695 */ + 8.876339743218858e-01, /* 696 */ + 8.883555880399664e-01, /* 697 */ + 8.890750142062326e-01, /* 698 */ + 8.897922510491166e-01, /* 699 */ + 8.905072968024422e-01, /* 700 */ + 8.912201497054284e-01, /* 701 */ + 8.919308080026938e-01, /* 702 */ + 8.926392699442616e-01, /* 703 */ + 8.933455337855630e-01, /* 704 */ + 8.940495977874426e-01, /* 705 */ + 8.947514602161615e-01, /* 706 */ + 8.954511193434023e-01, /* 707 */ + 8.961485734462731e-01, /* 708 */ + 8.968438208073118e-01, /* 709 */ + 8.975368597144907e-01, /* 710 */ + 8.982276884612198e-01, /* 711 */ + 8.989163053463520e-01, /* 712 */ + 8.996027086741867e-01, /* 713 */ + 9.002868967544739e-01, /* 714 */ + 9.009688679024191e-01, /* 715 */ + 9.016486204386864e-01, /* 716 */ + 9.023261526894035e-01, /* 717 */ + 9.030014629861653e-01, /* 718 */ + 9.036745496660386e-01, /* 719 */ + 9.043454110715651e-01, /* 720 */ + 9.050140455507668e-01, /* 721 */ + 9.056804514571491e-01, /* 722 */ + 9.063446271497057e-01, /* 723 */ + 9.070065709929211e-01, /* 724 */ + 9.076662813567770e-01, /* 725 */ + 9.083237566167539e-01, /* 726 */ + 9.089789951538368e-01, /* 727 */ + 9.096319953545183e-01, /* 728 */ + 9.102827556108030e-01, /* 729 */ + 9.109312743202110e-01, /* 730 */ + 9.115775498857827e-01, /* 731 */ + 9.122215807160815e-01, /* 732 */ + 9.128633652251990e-01, /* 733 */ + 9.135029018327580e-01, /* 734 */ + 9.141401889639166e-01, /* 735 */ + 9.147752250493725e-01, /* 736 */ + 9.154080085253663e-01, /* 737 */ + 9.160385378336857e-01, /* 738 */ + 9.166668114216692e-01, /* 739 */ + 9.172928277422099e-01, /* 740 */ + 9.179165852537594e-01, /* 741 */ + 9.185380824203315e-01, /* 742 */ + 9.191573177115062e-01, /* 743 */ + 9.197742896024330e-01, /* 744 */ + 9.203889965738354e-01, /* 745 */ + 9.210014371120140e-01, /* 746 */ + 9.216116097088501e-01, /* 747 */ + 9.222195128618103e-01, /* 748 */ + 9.228251450739493e-01, /* 749 */ + 9.234285048539139e-01, /* 750 */ + 9.240295907159471e-01, /* 751 */ + 9.246284011798909e-01, /* 752 */ + 9.252249347711905e-01, /* 753 */ + 9.258191900208981e-01, /* 754 */ + 9.264111654656760e-01, /* 755 */ + 9.270008596478005e-01, /* 756 */ + 9.275882711151657e-01, /* 757 */ + 9.281733984212864e-01, /* 758 */ + 9.287562401253023e-01, /* 759 */ + 9.293367947919815e-01, /* 760 */ + 9.299150609917235e-01, /* 761 */ + 9.304910373005635e-01, /* 762 */ + 9.310647223001750e-01, /* 763 */ + 9.316361145778743e-01, /* 764 */ + 9.322052127266233e-01, /* 765 */ + 9.327720153450328e-01, /* 766 */ + 9.333365210373668e-01, /* 767 */ + 9.338987284135450e-01, /* 768 */ + 9.344586360891469e-01, /* 769 */ + 9.350162426854148e-01, /* 770 */ + 9.355715468292576e-01, /* 771 */ + 9.361245471532534e-01, /* 772 */ + 9.366752422956540e-01, /* 773 */ + 9.372236309003873e-01, /* 774 */ + 9.377697116170611e-01, /* 775 */ + 9.383134831009662e-01, /* 776 */ + 9.388549440130799e-01, /* 777 */ + 9.393940930200694e-01, /* 778 */ + 9.399309287942944e-01, /* 779 */ + 9.404654500138115e-01, /* 780 */ + 9.409976553623765e-01, /* 781 */ + 9.415275435294478e-01, /* 782 */ + 9.420551132101902e-01, /* 783 */ + 9.425803631054774e-01, /* 784 */ + 9.431032919218957e-01, /* 785 */ + 9.436238983717468e-01, /* 786 */ + 9.441421811730513e-01, /* 787 */ + 9.446581390495518e-01, /* 788 */ + 9.451717707307158e-01, /* 789 */ + 9.456830749517390e-01, /* 790 */ + 9.461920504535486e-01, /* 791 */ + 9.466986959828060e-01, /* 792 */ + 9.472030102919101e-01, /* 793 */ + 9.477049921390005e-01, /* 794 */ + 9.482046402879605e-01, /* 795 */ + 9.487019535084197e-01, /* 796 */ + 9.491969305757577e-01, /* 797 */ + 9.496895702711069e-01, /* 798 */ + 9.501798713813551e-01, /* 799 */ + 9.506678326991489e-01, /* 800 */ + 9.511534530228968e-01, /* 801 */ + 9.516367311567717e-01, /* 802 */ + 9.521176659107141e-01, /* 803 */ + 9.525962561004353e-01, /* 804 */ + 9.530725005474194e-01, /* 805 */ + 9.535463980789276e-01, /* 806 */ + 9.540179475279997e-01, /* 807 */ + 9.544871477334580e-01, /* 808 */ + 9.549539975399095e-01, /* 809 */ + 9.554184957977490e-01, /* 810 */ + 9.558806413631620e-01, /* 811 */ + 9.563404330981276e-01, /* 812 */ + 9.567978698704207e-01, /* 813 */ + 9.572529505536157e-01, /* 814 */ + 9.577056740270887e-01, /* 815 */ + 9.581560391760202e-01, /* 816 */ + 9.586040448913981e-01, /* 817 */ + 9.590496900700202e-01, /* 818 */ + 9.594929736144974e-01, /* 819 */ + 9.599338944332557e-01, /* 820 */ + 9.603724514405396e-01, /* 821 */ + 9.608086435564140e-01, /* 822 */ + 9.612424697067677e-01, /* 823 */ + 9.616739288233154e-01, /* 824 */ + 9.621030198436005e-01, /* 825 */ + 9.625297417109979e-01, /* 826 */ + 9.629540933747166e-01, /* 827 */ + 9.633760737898017e-01, /* 828 */ + 9.637956819171380e-01, /* 829 */ + 9.642129167234518e-01, /* 830 */ + 9.646277771813133e-01, /* 831 */ + 9.650402622691399e-01, /* 832 */ + 9.654503709711981e-01, /* 833 */ + 9.658581022776063e-01, /* 834 */ + 9.662634551843370e-01, /* 835 */ + 9.666664286932195e-01, /* 836 */ + 9.670670218119425e-01, /* 837 */ + 9.674652335540560e-01, /* 838 */ + 9.678610629389744e-01, /* 839 */ + 9.682545089919784e-01, /* 840 */ + 9.686455707442176e-01, /* 841 */ + 9.690342472327130e-01, /* 842 */ + 9.694205375003593e-01, /* 843 */ + 9.698044405959267e-01, /* 844 */ + 9.701859555740645e-01, /* 845 */ + 9.705650814953021e-01, /* 846 */ + 9.709418174260520e-01, /* 847 */ + 9.713161624386123e-01, /* 848 */ + 9.716881156111683e-01, /* 849 */ + 9.720576760277954e-01, /* 850 */ + 9.724248427784608e-01, /* 851 */ + 9.727896149590264e-01, /* 852 */ + 9.731519916712503e-01, /* 853 */ + 9.735119720227898e-01, /* 854 */ + 9.738695551272029e-01, /* 855 */ + 9.742247401039505e-01, /* 856 */ + 9.745775260783994e-01, /* 857 */ + 9.749279121818236e-01, /* 858 */ + 9.752758975514066e-01, /* 859 */ + 9.756214813302438e-01, /* 860 */ + 9.759646626673444e-01, /* 861 */ + 9.763054407176336e-01, /* 862 */ + 9.766438146419546e-01, /* 863 */ + 9.769797836070706e-01, /* 864 */ + 9.773133467856671e-01, /* 865 */ + 9.776445033563537e-01, /* 866 */ + 9.779732525036661e-01, /* 867 */ + 9.782995934180686e-01, /* 868 */ + 9.786235252959552e-01, /* 869 */ + 9.789450473396526e-01, /* 870 */ + 9.792641587574211e-01, /* 871 */ + 9.795808587634576e-01, /* 872 */ + 9.798951465778968e-01, /* 873 */ + 9.802070214268133e-01, /* 874 */ + 9.805164825422236e-01, /* 875 */ + 9.808235291620881e-01, /* 876 */ + 9.811281605303128e-01, /* 877 */ + 9.814303758967510e-01, /* 878 */ + 9.817301745172056e-01, /* 879 */ + 9.820275556534303e-01, /* 880 */ + 9.823225185731322e-01, /* 881 */ + 9.826150625499731e-01, /* 882 */ + 9.829051868635711e-01, /* 883 */ + 9.831928907995030e-01, /* 884 */ + 9.834781736493055e-01, /* 885 */ + 9.837610347104773e-01, /* 886 */ + 9.840414732864804e-01, /* 887 */ + 9.843194886867427e-01, /* 888 */ + 9.845950802266584e-01, /* 889 */ + 9.848682472275909e-01, /* 890 */ + 9.851389890168738e-01, /* 891 */ + 9.854073049278126e-01, /* 892 */ + 9.856731942996867e-01, /* 893 */ + 9.859366564777504e-01, /* 894 */ + 9.861976908132354e-01, /* 895 */ + 9.864562966633516e-01, /* 896 */ + 9.867124733912889e-01, /* 897 */ + 9.869662203662192e-01, /* 898 */ + 9.872175369632971e-01, /* 899 */ + 9.874664225636625e-01, /* 900 */ + 9.877128765544410e-01, /* 901 */ + 9.879568983287464e-01, /* 902 */ + 9.881984872856817e-01, /* 903 */ + 9.884376428303405e-01, /* 904 */ + 9.886743643738087e-01, /* 905 */ + 9.889086513331659e-01, /* 906 */ + 9.891405031314866e-01, /* 907 */ + 9.893699191978420e-01, /* 908 */ + 9.895968989673013e-01, /* 909 */ + 9.898214418809327e-01, /* 910 */ + 9.900435473858056e-01, /* 911 */ + 9.902632149349908e-01, /* 912 */ + 9.904804439875632e-01, /* 913 */ + 9.906952340086018e-01, /* 914 */ + 9.909075844691921e-01, /* 915 */ + 9.911174948464267e-01, /* 916 */ + 9.913249646234070e-01, /* 917 */ + 9.915299932892441e-01, /* 918 */ + 9.917325803390605e-01, /* 919 */ + 9.919327252739911e-01, /* 920 */ + 9.921304276011843e-01, /* 921 */ + 9.923256868338034e-01, /* 922 */ + 9.925185024910278e-01, /* 923 */ + 9.927088740980540e-01, /* 924 */ + 9.928968011860971e-01, /* 925 */ + 9.930822832923919e-01, /* 926 */ + 9.932653199601933e-01, /* 927 */ + 9.934459107387786e-01, /* 928 */ + 9.936240551834480e-01, /* 929 */ + 9.937997528555254e-01, /* 930 */ + 9.939730033223599e-01, /* 931 */ + 9.941438061573271e-01, /* 932 */ + 9.943121609398295e-01, /* 933 */ + 9.944780672552981e-01, /* 934 */ + 9.946415246951927e-01, /* 935 */ + 9.948025328570040e-01, /* 936 */ + 9.949610913442537e-01, /* 937 */ + 9.951171997664957e-01, /* 938 */ + 9.952708577393173e-01, /* 939 */ + 9.954220648843398e-01, /* 940 */ + 9.955708208292197e-01, /* 941 */ + 9.957171252076493e-01, /* 942 */ + 9.958609776593582e-01, /* 943 */ + 9.960023778301136e-01, /* 944 */ + 9.961413253717212e-01, /* 945 */ + 9.962778199420265e-01, /* 946 */ + 9.964118612049153e-01, /* 947 */ + 9.965434488303145e-01, /* 948 */ + 9.966725824941932e-01, /* 949 */ + 9.967992618785633e-01, /* 950 */ + 9.969234866714800e-01, /* 951 */ + 9.970452565670431e-01, /* 952 */ + 9.971645712653977e-01, /* 953 */ + 9.972814304727343e-01, /* 954 */ + 9.973958339012905e-01, /* 955 */ + 9.975077812693507e-01, /* 956 */ + 9.976172723012476e-01, /* 957 */ + 9.977243067273625e-01, /* 958 */ + 9.978288842841259e-01, /* 959 */ + 9.979310047140184e-01, /* 960 */ + 9.980306677655713e-01, /* 961 */ + 9.981278731933668e-01, /* 962 */ + 9.982226207580394e-01, /* 963 */ + 9.983149102262756e-01, /* 964 */ + 9.984047413708150e-01, /* 965 */ + 9.984921139704509e-01, /* 966 */ + 9.985770278100307e-01, /* 967 */ + 9.986594826804561e-01, /* 968 */ + 9.987394783786845e-01, /* 969 */ + 9.988170147077284e-01, /* 970 */ + 9.988920914766568e-01, /* 971 */ + 9.989647085005952e-01, /* 972 */ + 9.990348656007260e-01, /* 973 */ + 9.991025626042892e-01, /* 974 */ + 9.991677993445829e-01, /* 975 */ + 9.992305756609632e-01, /* 976 */ + 9.992908913988453e-01, /* 977 */ + 9.993487464097032e-01, /* 978 */ + 9.994041405510704e-01, /* 979 */ + 9.994570736865406e-01, /* 980 */ + 9.995075456857671e-01, /* 981 */ + 9.995555564244639e-01, /* 982 */ + 9.996011057844061e-01, /* 983 */ + 9.996441936534295e-01, /* 984 */ + 9.996848199254315e-01, /* 985 */ + 9.997229845003707e-01, /* 986 */ + 9.997586872842681e-01, /* 987 */ + 9.997919281892065e-01, /* 988 */ + 9.998227071333310e-01, /* 989 */ + 9.998510240408494e-01, /* 990 */ + 9.998768788420320e-01, /* 991 */ + 9.999002714732120e-01, /* 992 */ + 9.999212018767858e-01, /* 993 */ + 9.999396700012126e-01, /* 994 */ + 9.999556758010154e-01, /* 995 */ + 9.999692192367803e-01, /* 996 */ + 9.999803002751568e-01, /* 997 */ + 9.999889188888583e-01, /* 998 */ + 9.999950750566616e-01, /* 999 */ + 9.999987687634074e-01, /* 1000 */ + 1.000000000000000e+00, /* 1001 */ +}; + diff --git a/thirdparty/fluidsynth/src/fluid_rvoice_dsp_tables.inc.h b/thirdparty/fluidsynth/src/fluid_rvoice_dsp_tables.inc.h new file mode 100644 index 000000000..f8bfa0782 --- /dev/null +++ b/thirdparty/fluidsynth/src/fluid_rvoice_dsp_tables.inc.h @@ -0,0 +1,4110 @@ +/* THIS FILE HAS BEEN AUTOMATICALLY GENERATED. DO NOT EDIT. */ + +static const fluid_real_t interp_coeff_linear[256][2] = { + { + 1.000000000000000e+00, /* 0 */ + 0.000000000000000e+00, /* 1 */ + }, { + 9.960937500000000e-01, /* 2 */ + 3.906250000000000e-03, /* 3 */ + }, { + 9.921875000000000e-01, /* 4 */ + 7.812500000000000e-03, /* 5 */ + }, { + 9.882812500000000e-01, /* 6 */ + 1.171875000000000e-02, /* 7 */ + }, { + 9.843750000000000e-01, /* 8 */ + 1.562500000000000e-02, /* 9 */ + }, { + 9.804687500000000e-01, /* 10 */ + 1.953125000000000e-02, /* 11 */ + }, { + 9.765625000000000e-01, /* 12 */ + 2.343750000000000e-02, /* 13 */ + }, { + 9.726562500000000e-01, /* 14 */ + 2.734375000000000e-02, /* 15 */ + }, { + 9.687500000000000e-01, /* 16 */ + 3.125000000000000e-02, /* 17 */ + }, { + 9.648437500000000e-01, /* 18 */ + 3.515625000000000e-02, /* 19 */ + }, { + 9.609375000000000e-01, /* 20 */ + 3.906250000000000e-02, /* 21 */ + }, { + 9.570312500000000e-01, /* 22 */ + 4.296875000000000e-02, /* 23 */ + }, { + 9.531250000000000e-01, /* 24 */ + 4.687500000000000e-02, /* 25 */ + }, { + 9.492187500000000e-01, /* 26 */ + 5.078125000000000e-02, /* 27 */ + }, { + 9.453125000000000e-01, /* 28 */ + 5.468750000000000e-02, /* 29 */ + }, { + 9.414062500000000e-01, /* 30 */ + 5.859375000000000e-02, /* 31 */ + }, { + 9.375000000000000e-01, /* 32 */ + 6.250000000000000e-02, /* 33 */ + }, { + 9.335937500000000e-01, /* 34 */ + 6.640625000000000e-02, /* 35 */ + }, { + 9.296875000000000e-01, /* 36 */ + 7.031250000000000e-02, /* 37 */ + }, { + 9.257812500000000e-01, /* 38 */ + 7.421875000000000e-02, /* 39 */ + }, { + 9.218750000000000e-01, /* 40 */ + 7.812500000000000e-02, /* 41 */ + }, { + 9.179687500000000e-01, /* 42 */ + 8.203125000000000e-02, /* 43 */ + }, { + 9.140625000000000e-01, /* 44 */ + 8.593750000000000e-02, /* 45 */ + }, { + 9.101562500000000e-01, /* 46 */ + 8.984375000000000e-02, /* 47 */ + }, { + 9.062500000000000e-01, /* 48 */ + 9.375000000000000e-02, /* 49 */ + }, { + 9.023437500000000e-01, /* 50 */ + 9.765625000000000e-02, /* 51 */ + }, { + 8.984375000000000e-01, /* 52 */ + 1.015625000000000e-01, /* 53 */ + }, { + 8.945312500000000e-01, /* 54 */ + 1.054687500000000e-01, /* 55 */ + }, { + 8.906250000000000e-01, /* 56 */ + 1.093750000000000e-01, /* 57 */ + }, { + 8.867187500000000e-01, /* 58 */ + 1.132812500000000e-01, /* 59 */ + }, { + 8.828125000000000e-01, /* 60 */ + 1.171875000000000e-01, /* 61 */ + }, { + 8.789062500000000e-01, /* 62 */ + 1.210937500000000e-01, /* 63 */ + }, { + 8.750000000000000e-01, /* 64 */ + 1.250000000000000e-01, /* 65 */ + }, { + 8.710937500000000e-01, /* 66 */ + 1.289062500000000e-01, /* 67 */ + }, { + 8.671875000000000e-01, /* 68 */ + 1.328125000000000e-01, /* 69 */ + }, { + 8.632812500000000e-01, /* 70 */ + 1.367187500000000e-01, /* 71 */ + }, { + 8.593750000000000e-01, /* 72 */ + 1.406250000000000e-01, /* 73 */ + }, { + 8.554687500000000e-01, /* 74 */ + 1.445312500000000e-01, /* 75 */ + }, { + 8.515625000000000e-01, /* 76 */ + 1.484375000000000e-01, /* 77 */ + }, { + 8.476562500000000e-01, /* 78 */ + 1.523437500000000e-01, /* 79 */ + }, { + 8.437500000000000e-01, /* 80 */ + 1.562500000000000e-01, /* 81 */ + }, { + 8.398437500000000e-01, /* 82 */ + 1.601562500000000e-01, /* 83 */ + }, { + 8.359375000000000e-01, /* 84 */ + 1.640625000000000e-01, /* 85 */ + }, { + 8.320312500000000e-01, /* 86 */ + 1.679687500000000e-01, /* 87 */ + }, { + 8.281250000000000e-01, /* 88 */ + 1.718750000000000e-01, /* 89 */ + }, { + 8.242187500000000e-01, /* 90 */ + 1.757812500000000e-01, /* 91 */ + }, { + 8.203125000000000e-01, /* 92 */ + 1.796875000000000e-01, /* 93 */ + }, { + 8.164062500000000e-01, /* 94 */ + 1.835937500000000e-01, /* 95 */ + }, { + 8.125000000000000e-01, /* 96 */ + 1.875000000000000e-01, /* 97 */ + }, { + 8.085937500000000e-01, /* 98 */ + 1.914062500000000e-01, /* 99 */ + }, { + 8.046875000000000e-01, /* 100 */ + 1.953125000000000e-01, /* 101 */ + }, { + 8.007812500000000e-01, /* 102 */ + 1.992187500000000e-01, /* 103 */ + }, { + 7.968750000000000e-01, /* 104 */ + 2.031250000000000e-01, /* 105 */ + }, { + 7.929687500000000e-01, /* 106 */ + 2.070312500000000e-01, /* 107 */ + }, { + 7.890625000000000e-01, /* 108 */ + 2.109375000000000e-01, /* 109 */ + }, { + 7.851562500000000e-01, /* 110 */ + 2.148437500000000e-01, /* 111 */ + }, { + 7.812500000000000e-01, /* 112 */ + 2.187500000000000e-01, /* 113 */ + }, { + 7.773437500000000e-01, /* 114 */ + 2.226562500000000e-01, /* 115 */ + }, { + 7.734375000000000e-01, /* 116 */ + 2.265625000000000e-01, /* 117 */ + }, { + 7.695312500000000e-01, /* 118 */ + 2.304687500000000e-01, /* 119 */ + }, { + 7.656250000000000e-01, /* 120 */ + 2.343750000000000e-01, /* 121 */ + }, { + 7.617187500000000e-01, /* 122 */ + 2.382812500000000e-01, /* 123 */ + }, { + 7.578125000000000e-01, /* 124 */ + 2.421875000000000e-01, /* 125 */ + }, { + 7.539062500000000e-01, /* 126 */ + 2.460937500000000e-01, /* 127 */ + }, { + 7.500000000000000e-01, /* 128 */ + 2.500000000000000e-01, /* 129 */ + }, { + 7.460937500000000e-01, /* 130 */ + 2.539062500000000e-01, /* 131 */ + }, { + 7.421875000000000e-01, /* 132 */ + 2.578125000000000e-01, /* 133 */ + }, { + 7.382812500000000e-01, /* 134 */ + 2.617187500000000e-01, /* 135 */ + }, { + 7.343750000000000e-01, /* 136 */ + 2.656250000000000e-01, /* 137 */ + }, { + 7.304687500000000e-01, /* 138 */ + 2.695312500000000e-01, /* 139 */ + }, { + 7.265625000000000e-01, /* 140 */ + 2.734375000000000e-01, /* 141 */ + }, { + 7.226562500000000e-01, /* 142 */ + 2.773437500000000e-01, /* 143 */ + }, { + 7.187500000000000e-01, /* 144 */ + 2.812500000000000e-01, /* 145 */ + }, { + 7.148437500000000e-01, /* 146 */ + 2.851562500000000e-01, /* 147 */ + }, { + 7.109375000000000e-01, /* 148 */ + 2.890625000000000e-01, /* 149 */ + }, { + 7.070312500000000e-01, /* 150 */ + 2.929687500000000e-01, /* 151 */ + }, { + 7.031250000000000e-01, /* 152 */ + 2.968750000000000e-01, /* 153 */ + }, { + 6.992187500000000e-01, /* 154 */ + 3.007812500000000e-01, /* 155 */ + }, { + 6.953125000000000e-01, /* 156 */ + 3.046875000000000e-01, /* 157 */ + }, { + 6.914062500000000e-01, /* 158 */ + 3.085937500000000e-01, /* 159 */ + }, { + 6.875000000000000e-01, /* 160 */ + 3.125000000000000e-01, /* 161 */ + }, { + 6.835937500000000e-01, /* 162 */ + 3.164062500000000e-01, /* 163 */ + }, { + 6.796875000000000e-01, /* 164 */ + 3.203125000000000e-01, /* 165 */ + }, { + 6.757812500000000e-01, /* 166 */ + 3.242187500000000e-01, /* 167 */ + }, { + 6.718750000000000e-01, /* 168 */ + 3.281250000000000e-01, /* 169 */ + }, { + 6.679687500000000e-01, /* 170 */ + 3.320312500000000e-01, /* 171 */ + }, { + 6.640625000000000e-01, /* 172 */ + 3.359375000000000e-01, /* 173 */ + }, { + 6.601562500000000e-01, /* 174 */ + 3.398437500000000e-01, /* 175 */ + }, { + 6.562500000000000e-01, /* 176 */ + 3.437500000000000e-01, /* 177 */ + }, { + 6.523437500000000e-01, /* 178 */ + 3.476562500000000e-01, /* 179 */ + }, { + 6.484375000000000e-01, /* 180 */ + 3.515625000000000e-01, /* 181 */ + }, { + 6.445312500000000e-01, /* 182 */ + 3.554687500000000e-01, /* 183 */ + }, { + 6.406250000000000e-01, /* 184 */ + 3.593750000000000e-01, /* 185 */ + }, { + 6.367187500000000e-01, /* 186 */ + 3.632812500000000e-01, /* 187 */ + }, { + 6.328125000000000e-01, /* 188 */ + 3.671875000000000e-01, /* 189 */ + }, { + 6.289062500000000e-01, /* 190 */ + 3.710937500000000e-01, /* 191 */ + }, { + 6.250000000000000e-01, /* 192 */ + 3.750000000000000e-01, /* 193 */ + }, { + 6.210937500000000e-01, /* 194 */ + 3.789062500000000e-01, /* 195 */ + }, { + 6.171875000000000e-01, /* 196 */ + 3.828125000000000e-01, /* 197 */ + }, { + 6.132812500000000e-01, /* 198 */ + 3.867187500000000e-01, /* 199 */ + }, { + 6.093750000000000e-01, /* 200 */ + 3.906250000000000e-01, /* 201 */ + }, { + 6.054687500000000e-01, /* 202 */ + 3.945312500000000e-01, /* 203 */ + }, { + 6.015625000000000e-01, /* 204 */ + 3.984375000000000e-01, /* 205 */ + }, { + 5.976562500000000e-01, /* 206 */ + 4.023437500000000e-01, /* 207 */ + }, { + 5.937500000000000e-01, /* 208 */ + 4.062500000000000e-01, /* 209 */ + }, { + 5.898437500000000e-01, /* 210 */ + 4.101562500000000e-01, /* 211 */ + }, { + 5.859375000000000e-01, /* 212 */ + 4.140625000000000e-01, /* 213 */ + }, { + 5.820312500000000e-01, /* 214 */ + 4.179687500000000e-01, /* 215 */ + }, { + 5.781250000000000e-01, /* 216 */ + 4.218750000000000e-01, /* 217 */ + }, { + 5.742187500000000e-01, /* 218 */ + 4.257812500000000e-01, /* 219 */ + }, { + 5.703125000000000e-01, /* 220 */ + 4.296875000000000e-01, /* 221 */ + }, { + 5.664062500000000e-01, /* 222 */ + 4.335937500000000e-01, /* 223 */ + }, { + 5.625000000000000e-01, /* 224 */ + 4.375000000000000e-01, /* 225 */ + }, { + 5.585937500000000e-01, /* 226 */ + 4.414062500000000e-01, /* 227 */ + }, { + 5.546875000000000e-01, /* 228 */ + 4.453125000000000e-01, /* 229 */ + }, { + 5.507812500000000e-01, /* 230 */ + 4.492187500000000e-01, /* 231 */ + }, { + 5.468750000000000e-01, /* 232 */ + 4.531250000000000e-01, /* 233 */ + }, { + 5.429687500000000e-01, /* 234 */ + 4.570312500000000e-01, /* 235 */ + }, { + 5.390625000000000e-01, /* 236 */ + 4.609375000000000e-01, /* 237 */ + }, { + 5.351562500000000e-01, /* 238 */ + 4.648437500000000e-01, /* 239 */ + }, { + 5.312500000000000e-01, /* 240 */ + 4.687500000000000e-01, /* 241 */ + }, { + 5.273437500000000e-01, /* 242 */ + 4.726562500000000e-01, /* 243 */ + }, { + 5.234375000000000e-01, /* 244 */ + 4.765625000000000e-01, /* 245 */ + }, { + 5.195312500000000e-01, /* 246 */ + 4.804687500000000e-01, /* 247 */ + }, { + 5.156250000000000e-01, /* 248 */ + 4.843750000000000e-01, /* 249 */ + }, { + 5.117187500000000e-01, /* 250 */ + 4.882812500000000e-01, /* 251 */ + }, { + 5.078125000000000e-01, /* 252 */ + 4.921875000000000e-01, /* 253 */ + }, { + 5.039062500000000e-01, /* 254 */ + 4.960937500000000e-01, /* 255 */ + }, { + 5.000000000000000e-01, /* 256 */ + 5.000000000000000e-01, /* 257 */ + }, { + 4.960937500000000e-01, /* 258 */ + 5.039062500000000e-01, /* 259 */ + }, { + 4.921875000000000e-01, /* 260 */ + 5.078125000000000e-01, /* 261 */ + }, { + 4.882812500000000e-01, /* 262 */ + 5.117187500000000e-01, /* 263 */ + }, { + 4.843750000000000e-01, /* 264 */ + 5.156250000000000e-01, /* 265 */ + }, { + 4.804687500000000e-01, /* 266 */ + 5.195312500000000e-01, /* 267 */ + }, { + 4.765625000000000e-01, /* 268 */ + 5.234375000000000e-01, /* 269 */ + }, { + 4.726562500000000e-01, /* 270 */ + 5.273437500000000e-01, /* 271 */ + }, { + 4.687500000000000e-01, /* 272 */ + 5.312500000000000e-01, /* 273 */ + }, { + 4.648437500000000e-01, /* 274 */ + 5.351562500000000e-01, /* 275 */ + }, { + 4.609375000000000e-01, /* 276 */ + 5.390625000000000e-01, /* 277 */ + }, { + 4.570312500000000e-01, /* 278 */ + 5.429687500000000e-01, /* 279 */ + }, { + 4.531250000000000e-01, /* 280 */ + 5.468750000000000e-01, /* 281 */ + }, { + 4.492187500000000e-01, /* 282 */ + 5.507812500000000e-01, /* 283 */ + }, { + 4.453125000000000e-01, /* 284 */ + 5.546875000000000e-01, /* 285 */ + }, { + 4.414062500000000e-01, /* 286 */ + 5.585937500000000e-01, /* 287 */ + }, { + 4.375000000000000e-01, /* 288 */ + 5.625000000000000e-01, /* 289 */ + }, { + 4.335937500000000e-01, /* 290 */ + 5.664062500000000e-01, /* 291 */ + }, { + 4.296875000000000e-01, /* 292 */ + 5.703125000000000e-01, /* 293 */ + }, { + 4.257812500000000e-01, /* 294 */ + 5.742187500000000e-01, /* 295 */ + }, { + 4.218750000000000e-01, /* 296 */ + 5.781250000000000e-01, /* 297 */ + }, { + 4.179687500000000e-01, /* 298 */ + 5.820312500000000e-01, /* 299 */ + }, { + 4.140625000000000e-01, /* 300 */ + 5.859375000000000e-01, /* 301 */ + }, { + 4.101562500000000e-01, /* 302 */ + 5.898437500000000e-01, /* 303 */ + }, { + 4.062500000000000e-01, /* 304 */ + 5.937500000000000e-01, /* 305 */ + }, { + 4.023437500000000e-01, /* 306 */ + 5.976562500000000e-01, /* 307 */ + }, { + 3.984375000000000e-01, /* 308 */ + 6.015625000000000e-01, /* 309 */ + }, { + 3.945312500000000e-01, /* 310 */ + 6.054687500000000e-01, /* 311 */ + }, { + 3.906250000000000e-01, /* 312 */ + 6.093750000000000e-01, /* 313 */ + }, { + 3.867187500000000e-01, /* 314 */ + 6.132812500000000e-01, /* 315 */ + }, { + 3.828125000000000e-01, /* 316 */ + 6.171875000000000e-01, /* 317 */ + }, { + 3.789062500000000e-01, /* 318 */ + 6.210937500000000e-01, /* 319 */ + }, { + 3.750000000000000e-01, /* 320 */ + 6.250000000000000e-01, /* 321 */ + }, { + 3.710937500000000e-01, /* 322 */ + 6.289062500000000e-01, /* 323 */ + }, { + 3.671875000000000e-01, /* 324 */ + 6.328125000000000e-01, /* 325 */ + }, { + 3.632812500000000e-01, /* 326 */ + 6.367187500000000e-01, /* 327 */ + }, { + 3.593750000000000e-01, /* 328 */ + 6.406250000000000e-01, /* 329 */ + }, { + 3.554687500000000e-01, /* 330 */ + 6.445312500000000e-01, /* 331 */ + }, { + 3.515625000000000e-01, /* 332 */ + 6.484375000000000e-01, /* 333 */ + }, { + 3.476562500000000e-01, /* 334 */ + 6.523437500000000e-01, /* 335 */ + }, { + 3.437500000000000e-01, /* 336 */ + 6.562500000000000e-01, /* 337 */ + }, { + 3.398437500000000e-01, /* 338 */ + 6.601562500000000e-01, /* 339 */ + }, { + 3.359375000000000e-01, /* 340 */ + 6.640625000000000e-01, /* 341 */ + }, { + 3.320312500000000e-01, /* 342 */ + 6.679687500000000e-01, /* 343 */ + }, { + 3.281250000000000e-01, /* 344 */ + 6.718750000000000e-01, /* 345 */ + }, { + 3.242187500000000e-01, /* 346 */ + 6.757812500000000e-01, /* 347 */ + }, { + 3.203125000000000e-01, /* 348 */ + 6.796875000000000e-01, /* 349 */ + }, { + 3.164062500000000e-01, /* 350 */ + 6.835937500000000e-01, /* 351 */ + }, { + 3.125000000000000e-01, /* 352 */ + 6.875000000000000e-01, /* 353 */ + }, { + 3.085937500000000e-01, /* 354 */ + 6.914062500000000e-01, /* 355 */ + }, { + 3.046875000000000e-01, /* 356 */ + 6.953125000000000e-01, /* 357 */ + }, { + 3.007812500000000e-01, /* 358 */ + 6.992187500000000e-01, /* 359 */ + }, { + 2.968750000000000e-01, /* 360 */ + 7.031250000000000e-01, /* 361 */ + }, { + 2.929687500000000e-01, /* 362 */ + 7.070312500000000e-01, /* 363 */ + }, { + 2.890625000000000e-01, /* 364 */ + 7.109375000000000e-01, /* 365 */ + }, { + 2.851562500000000e-01, /* 366 */ + 7.148437500000000e-01, /* 367 */ + }, { + 2.812500000000000e-01, /* 368 */ + 7.187500000000000e-01, /* 369 */ + }, { + 2.773437500000000e-01, /* 370 */ + 7.226562500000000e-01, /* 371 */ + }, { + 2.734375000000000e-01, /* 372 */ + 7.265625000000000e-01, /* 373 */ + }, { + 2.695312500000000e-01, /* 374 */ + 7.304687500000000e-01, /* 375 */ + }, { + 2.656250000000000e-01, /* 376 */ + 7.343750000000000e-01, /* 377 */ + }, { + 2.617187500000000e-01, /* 378 */ + 7.382812500000000e-01, /* 379 */ + }, { + 2.578125000000000e-01, /* 380 */ + 7.421875000000000e-01, /* 381 */ + }, { + 2.539062500000000e-01, /* 382 */ + 7.460937500000000e-01, /* 383 */ + }, { + 2.500000000000000e-01, /* 384 */ + 7.500000000000000e-01, /* 385 */ + }, { + 2.460937500000000e-01, /* 386 */ + 7.539062500000000e-01, /* 387 */ + }, { + 2.421875000000000e-01, /* 388 */ + 7.578125000000000e-01, /* 389 */ + }, { + 2.382812500000000e-01, /* 390 */ + 7.617187500000000e-01, /* 391 */ + }, { + 2.343750000000000e-01, /* 392 */ + 7.656250000000000e-01, /* 393 */ + }, { + 2.304687500000000e-01, /* 394 */ + 7.695312500000000e-01, /* 395 */ + }, { + 2.265625000000000e-01, /* 396 */ + 7.734375000000000e-01, /* 397 */ + }, { + 2.226562500000000e-01, /* 398 */ + 7.773437500000000e-01, /* 399 */ + }, { + 2.187500000000000e-01, /* 400 */ + 7.812500000000000e-01, /* 401 */ + }, { + 2.148437500000000e-01, /* 402 */ + 7.851562500000000e-01, /* 403 */ + }, { + 2.109375000000000e-01, /* 404 */ + 7.890625000000000e-01, /* 405 */ + }, { + 2.070312500000000e-01, /* 406 */ + 7.929687500000000e-01, /* 407 */ + }, { + 2.031250000000000e-01, /* 408 */ + 7.968750000000000e-01, /* 409 */ + }, { + 1.992187500000000e-01, /* 410 */ + 8.007812500000000e-01, /* 411 */ + }, { + 1.953125000000000e-01, /* 412 */ + 8.046875000000000e-01, /* 413 */ + }, { + 1.914062500000000e-01, /* 414 */ + 8.085937500000000e-01, /* 415 */ + }, { + 1.875000000000000e-01, /* 416 */ + 8.125000000000000e-01, /* 417 */ + }, { + 1.835937500000000e-01, /* 418 */ + 8.164062500000000e-01, /* 419 */ + }, { + 1.796875000000000e-01, /* 420 */ + 8.203125000000000e-01, /* 421 */ + }, { + 1.757812500000000e-01, /* 422 */ + 8.242187500000000e-01, /* 423 */ + }, { + 1.718750000000000e-01, /* 424 */ + 8.281250000000000e-01, /* 425 */ + }, { + 1.679687500000000e-01, /* 426 */ + 8.320312500000000e-01, /* 427 */ + }, { + 1.640625000000000e-01, /* 428 */ + 8.359375000000000e-01, /* 429 */ + }, { + 1.601562500000000e-01, /* 430 */ + 8.398437500000000e-01, /* 431 */ + }, { + 1.562500000000000e-01, /* 432 */ + 8.437500000000000e-01, /* 433 */ + }, { + 1.523437500000000e-01, /* 434 */ + 8.476562500000000e-01, /* 435 */ + }, { + 1.484375000000000e-01, /* 436 */ + 8.515625000000000e-01, /* 437 */ + }, { + 1.445312500000000e-01, /* 438 */ + 8.554687500000000e-01, /* 439 */ + }, { + 1.406250000000000e-01, /* 440 */ + 8.593750000000000e-01, /* 441 */ + }, { + 1.367187500000000e-01, /* 442 */ + 8.632812500000000e-01, /* 443 */ + }, { + 1.328125000000000e-01, /* 444 */ + 8.671875000000000e-01, /* 445 */ + }, { + 1.289062500000000e-01, /* 446 */ + 8.710937500000000e-01, /* 447 */ + }, { + 1.250000000000000e-01, /* 448 */ + 8.750000000000000e-01, /* 449 */ + }, { + 1.210937500000000e-01, /* 450 */ + 8.789062500000000e-01, /* 451 */ + }, { + 1.171875000000000e-01, /* 452 */ + 8.828125000000000e-01, /* 453 */ + }, { + 1.132812500000000e-01, /* 454 */ + 8.867187500000000e-01, /* 455 */ + }, { + 1.093750000000000e-01, /* 456 */ + 8.906250000000000e-01, /* 457 */ + }, { + 1.054687500000000e-01, /* 458 */ + 8.945312500000000e-01, /* 459 */ + }, { + 1.015625000000000e-01, /* 460 */ + 8.984375000000000e-01, /* 461 */ + }, { + 9.765625000000000e-02, /* 462 */ + 9.023437500000000e-01, /* 463 */ + }, { + 9.375000000000000e-02, /* 464 */ + 9.062500000000000e-01, /* 465 */ + }, { + 8.984375000000000e-02, /* 466 */ + 9.101562500000000e-01, /* 467 */ + }, { + 8.593750000000000e-02, /* 468 */ + 9.140625000000000e-01, /* 469 */ + }, { + 8.203125000000000e-02, /* 470 */ + 9.179687500000000e-01, /* 471 */ + }, { + 7.812500000000000e-02, /* 472 */ + 9.218750000000000e-01, /* 473 */ + }, { + 7.421875000000000e-02, /* 474 */ + 9.257812500000000e-01, /* 475 */ + }, { + 7.031250000000000e-02, /* 476 */ + 9.296875000000000e-01, /* 477 */ + }, { + 6.640625000000000e-02, /* 478 */ + 9.335937500000000e-01, /* 479 */ + }, { + 6.250000000000000e-02, /* 480 */ + 9.375000000000000e-01, /* 481 */ + }, { + 5.859375000000000e-02, /* 482 */ + 9.414062500000000e-01, /* 483 */ + }, { + 5.468750000000000e-02, /* 484 */ + 9.453125000000000e-01, /* 485 */ + }, { + 5.078125000000000e-02, /* 486 */ + 9.492187500000000e-01, /* 487 */ + }, { + 4.687500000000000e-02, /* 488 */ + 9.531250000000000e-01, /* 489 */ + }, { + 4.296875000000000e-02, /* 490 */ + 9.570312500000000e-01, /* 491 */ + }, { + 3.906250000000000e-02, /* 492 */ + 9.609375000000000e-01, /* 493 */ + }, { + 3.515625000000000e-02, /* 494 */ + 9.648437500000000e-01, /* 495 */ + }, { + 3.125000000000000e-02, /* 496 */ + 9.687500000000000e-01, /* 497 */ + }, { + 2.734375000000000e-02, /* 498 */ + 9.726562500000000e-01, /* 499 */ + }, { + 2.343750000000000e-02, /* 500 */ + 9.765625000000000e-01, /* 501 */ + }, { + 1.953125000000000e-02, /* 502 */ + 9.804687500000000e-01, /* 503 */ + }, { + 1.562500000000000e-02, /* 504 */ + 9.843750000000000e-01, /* 505 */ + }, { + 1.171875000000000e-02, /* 506 */ + 9.882812500000000e-01, /* 507 */ + }, { + 7.812500000000000e-03, /* 508 */ + 9.921875000000000e-01, /* 509 */ + }, { + 3.906250000000000e-03, /* 510 */ + 9.960937500000000e-01, /* 511 */ + } +}; + +static const fluid_real_t interp_coeff[256][4] = { + { + -0.000000000000000e+00, /* 0 */ + 1.000000000000000e+00, /* 1 */ + 0.000000000000000e+00, /* 2 */ + -0.000000000000000e+00, /* 3 */ + }, { + -1.937896013259888e-03, /* 4 */ + 9.999619424343109e-01, /* 5 */ + 1.983553171157837e-03, /* 6 */ + -7.599592208862305e-06, /* 7 */ + }, { + -3.845453262329102e-03, /* 8 */ + 9.998481273651123e-01, /* 9 */ + 4.027605056762695e-03, /* 10 */ + -3.027915954589844e-05, /* 11 */ + }, { + -5.722850561141968e-03, /* 12 */ + 9.996590912342072e-01, /* 13 */ + 6.131619215011597e-03, /* 14 */ + -6.785988807678223e-05, /* 15 */ + }, { + -7.570266723632812e-03, /* 16 */ + 9.993953704833984e-01, /* 17 */ + 8.295059204101562e-03, /* 18 */ + -1.201629638671875e-04, /* 19 */ + }, { + -9.387880563735962e-03, /* 20 */ + 9.990575015544891e-01, /* 21 */ + 1.051738858222961e-02, /* 22 */ + -1.870095729827881e-04, /* 23 */ + }, { + -1.117587089538574e-02, /* 24 */ + 9.986460208892822e-01, /* 25 */ + 1.279807090759277e-02, /* 26 */ + -2.682209014892578e-04, /* 27 */ + }, { + -1.293441653251648e-02, /* 28 */ + 9.981614649295807e-01, /* 29 */ + 1.513656973838806e-02, /* 30 */ + -3.636181354522705e-04, /* 31 */ + }, { + -1.466369628906250e-02, /* 32 */ + 9.976043701171875e-01, /* 33 */ + 1.753234863281250e-02, /* 34 */ + -4.730224609375000e-04, /* 35 */ + }, { + -1.636388897895813e-02, /* 36 */ + 9.969752728939056e-01, /* 37 */ + 1.998487114906311e-02, /* 38 */ + -5.962550640106201e-04, /* 39 */ + }, { + -1.803517341613770e-02, /* 40 */ + 9.962747097015381e-01, /* 41 */ + 2.249360084533691e-02, /* 42 */ + -7.331371307373047e-04, /* 43 */ + }, { + -1.967772841453552e-02, /* 44 */ + 9.955032169818878e-01, /* 45 */ + 2.505800127983093e-02, /* 46 */ + -8.834898471832275e-04, /* 47 */ + }, { + -2.129173278808594e-02, /* 48 */ + 9.946613311767578e-01, /* 49 */ + 2.767753601074219e-02, /* 50 */ + -1.047134399414062e-03, /* 51 */ + }, { + -2.287736535072327e-02, /* 52 */ + 9.937495887279510e-01, /* 53 */ + 3.035166859626770e-02, /* 54 */ + -1.223891973495483e-03, /* 55 */ + }, { + -2.443480491638184e-02, /* 56 */ + 9.927685260772705e-01, /* 57 */ + 3.307986259460449e-02, /* 58 */ + -1.413583755493164e-03, /* 59 */ + }, { + -2.596423029899597e-02, /* 60 */ + 9.917186796665192e-01, /* 61 */ + 3.586158156394958e-02, /* 62 */ + -1.616030931472778e-03, /* 63 */ + }, { + -2.746582031250000e-02, /* 64 */ + 9.906005859375000e-01, /* 65 */ + 3.869628906250000e-02, /* 66 */ + -1.831054687500000e-03, /* 67 */ + }, { + -2.893975377082825e-02, /* 68 */ + 9.894147813320160e-01, /* 69 */ + 4.158344864845276e-02, /* 70 */ + -2.058476209640503e-03, /* 71 */ + }, { + -3.038620948791504e-02, /* 72 */ + 9.881618022918701e-01, /* 73 */ + 4.452252388000488e-02, /* 74 */ + -2.298116683959961e-03, /* 75 */ + }, { + -3.180536627769470e-02, /* 76 */ + 9.868421852588654e-01, /* 77 */ + 4.751297831535339e-02, /* 78 */ + -2.549797296524048e-03, /* 79 */ + }, { + -3.319740295410156e-02, /* 80 */ + 9.854564666748047e-01, /* 81 */ + 5.055427551269531e-02, /* 82 */ + -2.813339233398438e-03, /* 83 */ + }, { + -3.456249833106995e-02, /* 84 */ + 9.840051829814911e-01, /* 85 */ + 5.364587903022766e-02, /* 86 */ + -3.088563680648804e-03, /* 87 */ + }, { + -3.590083122253418e-02, /* 88 */ + 9.824888706207275e-01, /* 89 */ + 5.678725242614746e-02, /* 90 */ + -3.375291824340820e-03, /* 91 */ + }, { + -3.721258044242859e-02, /* 92 */ + 9.809080660343170e-01, /* 93 */ + 5.997785925865173e-02, /* 94 */ + -3.673344850540161e-03, /* 95 */ + }, { + -3.849792480468750e-02, /* 96 */ + 9.792633056640625e-01, /* 97 */ + 6.321716308593750e-02, /* 98 */ + -3.982543945312500e-03, /* 99 */ + }, { + -3.975704312324524e-02, /* 100 */ + 9.775551259517670e-01, /* 101 */ + 6.650462746620178e-02, /* 102 */ + -4.302710294723511e-03, /* 103 */ + }, { + -4.099011421203613e-02, /* 104 */ + 9.757840633392334e-01, /* 105 */ + 6.983971595764160e-02, /* 106 */ + -4.633665084838867e-03, /* 107 */ + }, { + -4.219731688499451e-02, /* 108 */ + 9.739506542682648e-01, /* 109 */ + 7.322189211845398e-02, /* 110 */ + -4.975229501724243e-03, /* 111 */ + }, { + -4.337882995605469e-02, /* 112 */ + 9.720554351806641e-01, /* 113 */ + 7.665061950683594e-02, /* 114 */ + -5.327224731445312e-03, /* 115 */ + }, { + -4.453483223915100e-02, /* 116 */ + 9.700989425182343e-01, /* 117 */ + 8.012536168098450e-02, /* 118 */ + -5.689471960067749e-03, /* 119 */ + }, { + -4.566550254821777e-02, /* 120 */ + 9.680817127227783e-01, /* 121 */ + 8.364558219909668e-02, /* 122 */ + -6.061792373657227e-03, /* 123 */ + }, { + -4.677101969718933e-02, /* 124 */ + 9.660042822360992e-01, /* 125 */ + 8.721074461936951e-02, /* 126 */ + -6.444007158279419e-03, /* 127 */ + }, { + -4.785156250000000e-02, /* 128 */ + 9.638671875000000e-01, /* 129 */ + 9.082031250000000e-02, /* 130 */ + -6.835937500000000e-03, /* 131 */ + }, { + -4.890730977058411e-02, /* 132 */ + 9.616709649562836e-01, /* 133 */ + 9.447374939918518e-02, /* 134 */ + -7.237404584884644e-03, /* 135 */ + }, { + -4.993844032287598e-02, /* 136 */ + 9.594161510467529e-01, /* 137 */ + 9.817051887512207e-02, /* 138 */ + -7.648229598999023e-03, /* 139 */ + }, { + -5.094513297080994e-02, /* 140 */ + 9.571032822132111e-01, /* 141 */ + 1.019100844860077e-01, /* 142 */ + -8.068233728408813e-03, /* 143 */ + }, { + -5.192756652832031e-02, /* 144 */ + 9.547328948974609e-01, /* 145 */ + 1.056919097900391e-01, /* 146 */ + -8.497238159179688e-03, /* 147 */ + }, { + -5.288591980934143e-02, /* 148 */ + 9.523055255413055e-01, /* 149 */ + 1.095154583454132e-01, /* 150 */ + -8.935064077377319e-03, /* 151 */ + }, { + -5.382037162780762e-02, /* 152 */ + 9.498217105865479e-01, /* 153 */ + 1.133801937103271e-01, /* 154 */ + -9.381532669067383e-03, /* 155 */ + }, { + -5.473110079765320e-02, /* 156 */ + 9.472819864749908e-01, /* 157 */ + 1.172855794429779e-01, /* 158 */ + -9.836465120315552e-03, /* 159 */ + }, { + -5.561828613281250e-02, /* 160 */ + 9.446868896484375e-01, /* 161 */ + 1.212310791015625e-01, /* 162 */ + -1.029968261718750e-02, /* 163 */ + }, { + -5.648210644721985e-02, /* 164 */ + 9.420369565486908e-01, /* 165 */ + 1.252161562442780e-01, /* 166 */ + -1.077100634574890e-02, /* 167 */ + }, { + -5.732274055480957e-02, /* 168 */ + 9.393327236175537e-01, /* 169 */ + 1.292402744293213e-01, /* 170 */ + -1.125025749206543e-02, /* 171 */ + }, { + -5.814036726951599e-02, /* 172 */ + 9.365747272968292e-01, /* 173 */ + 1.333028972148895e-01, /* 174 */ + -1.173725724220276e-02, /* 175 */ + }, { + -5.893516540527344e-02, /* 176 */ + 9.337635040283203e-01, /* 177 */ + 1.374034881591797e-01, /* 178 */ + -1.223182678222656e-02, /* 179 */ + }, { + -5.970731377601624e-02, /* 180 */ + 9.308995902538300e-01, /* 181 */ + 1.415415108203888e-01, /* 182 */ + -1.273378729820251e-02, /* 183 */ + }, { + -6.045699119567871e-02, /* 184 */ + 9.279835224151611e-01, /* 185 */ + 1.457164287567139e-01, /* 186 */ + -1.324295997619629e-02, /* 187 */ + }, { + -6.118437647819519e-02, /* 188 */ + 9.250158369541168e-01, /* 189 */ + 1.499277055263519e-01, /* 190 */ + -1.375916600227356e-02, /* 191 */ + }, { + -6.188964843750000e-02, /* 192 */ + 9.219970703125000e-01, /* 193 */ + 1.541748046875000e-01, /* 194 */ + -1.428222656250000e-02, /* 195 */ + }, { + -6.257298588752747e-02, /* 196 */ + 9.189277589321136e-01, /* 197 */ + 1.584571897983551e-01, /* 198 */ + -1.481196284294128e-02, /* 199 */ + }, { + -6.323456764221191e-02, /* 200 */ + 9.158084392547607e-01, /* 201 */ + 1.627743244171143e-01, /* 202 */ + -1.534819602966309e-02, /* 203 */ + }, { + -6.387457251548767e-02, /* 204 */ + 9.126396477222443e-01, /* 205 */ + 1.671256721019745e-01, /* 206 */ + -1.589074730873108e-02, /* 207 */ + }, { + -6.449317932128906e-02, /* 208 */ + 9.094219207763672e-01, /* 209 */ + 1.715106964111328e-01, /* 210 */ + -1.643943786621094e-02, /* 211 */ + }, { + -6.509056687355042e-02, /* 212 */ + 9.061557948589325e-01, /* 213 */ + 1.759288609027863e-01, /* 214 */ + -1.699408888816833e-02, /* 215 */ + }, { + -6.566691398620605e-02, /* 216 */ + 9.028418064117432e-01, /* 217 */ + 1.803796291351318e-01, /* 218 */ + -1.755452156066895e-02, /* 219 */ + }, { + -6.622239947319031e-02, /* 220 */ + 8.994804918766022e-01, /* 221 */ + 1.848624646663666e-01, /* 222 */ + -1.812055706977844e-02, /* 223 */ + }, { + -6.675720214843750e-02, /* 224 */ + 8.960723876953125e-01, /* 225 */ + 1.893768310546875e-01, /* 226 */ + -1.869201660156250e-02, /* 227 */ + }, { + -6.727150082588196e-02, /* 228 */ + 8.926180303096771e-01, /* 229 */ + 1.939221918582916e-01, /* 230 */ + -1.926872134208679e-02, /* 231 */ + }, { + -6.776547431945801e-02, /* 232 */ + 8.891179561614990e-01, /* 233 */ + 1.984980106353760e-01, /* 234 */ + -1.985049247741699e-02, /* 235 */ + }, { + -6.823930144309998e-02, /* 236 */ + 8.855727016925812e-01, /* 237 */ + 2.031037509441376e-01, /* 238 */ + -2.043715119361877e-02, /* 239 */ + }, { + -6.869316101074219e-02, /* 240 */ + 8.819828033447266e-01, /* 241 */ + 2.077388763427734e-01, /* 242 */ + -2.102851867675781e-02, /* 243 */ + }, { + -6.912723183631897e-02, /* 244 */ + 8.783487975597382e-01, /* 245 */ + 2.124028503894806e-01, /* 246 */ + -2.162441611289978e-02, /* 247 */ + }, { + -6.954169273376465e-02, /* 248 */ + 8.746712207794189e-01, /* 249 */ + 2.170951366424561e-01, /* 250 */ + -2.222466468811035e-02, /* 251 */ + }, { + -6.993672251701355e-02, /* 252 */ + 8.709506094455719e-01, /* 253 */ + 2.218151986598969e-01, /* 254 */ + -2.282908558845520e-02, /* 255 */ + }, { + -7.031250000000000e-02, /* 256 */ + 8.671875000000000e-01, /* 257 */ + 2.265625000000000e-01, /* 258 */ + -2.343750000000000e-02, /* 259 */ + }, { + -7.066920399665833e-02, /* 260 */ + 8.633824288845062e-01, /* 261 */ + 2.313365042209625e-01, /* 262 */ + -2.404972910881042e-02, /* 263 */ + }, { + -7.100701332092285e-02, /* 264 */ + 8.595359325408936e-01, /* 265 */ + 2.361366748809814e-01, /* 266 */ + -2.466559410095215e-02, /* 267 */ + }, { + -7.132610678672791e-02, /* 268 */ + 8.556485474109650e-01, /* 269 */ + 2.409624755382538e-01, /* 270 */ + -2.528491616249084e-02, /* 271 */ + }, { + -7.162666320800781e-02, /* 272 */ + 8.517208099365234e-01, /* 273 */ + 2.458133697509766e-01, /* 274 */ + -2.590751647949219e-02, /* 275 */ + }, { + -7.190886139869690e-02, /* 276 */ + 8.477532565593719e-01, /* 277 */ + 2.506888210773468e-01, /* 278 */ + -2.653321623802185e-02, /* 279 */ + }, { + -7.217288017272949e-02, /* 280 */ + 8.437464237213135e-01, /* 281 */ + 2.555882930755615e-01, /* 282 */ + -2.716183662414551e-02, /* 283 */ + }, { + -7.241889834403992e-02, /* 284 */ + 8.397008478641510e-01, /* 285 */ + 2.605112493038177e-01, /* 286 */ + -2.779319882392883e-02, /* 287 */ + }, { + -7.264709472656250e-02, /* 288 */ + 8.356170654296875e-01, /* 289 */ + 2.654571533203125e-01, /* 290 */ + -2.842712402343750e-02, /* 291 */ + }, { + -7.285764813423157e-02, /* 292 */ + 8.314956128597260e-01, /* 293 */ + 2.704254686832428e-01, /* 294 */ + -2.906343340873718e-02, /* 295 */ + }, { + -7.305073738098145e-02, /* 296 */ + 8.273370265960693e-01, /* 297 */ + 2.754156589508057e-01, /* 298 */ + -2.970194816589355e-02, /* 299 */ + }, { + -7.322654128074646e-02, /* 300 */ + 8.231418430805206e-01, /* 301 */ + 2.804271876811981e-01, /* 302 */ + -3.034248948097229e-02, /* 303 */ + }, { + -7.338523864746094e-02, /* 304 */ + 8.189105987548828e-01, /* 305 */ + 2.854595184326172e-01, /* 306 */ + -3.098487854003906e-02, /* 307 */ + }, { + -7.352700829505920e-02, /* 308 */ + 8.146438300609589e-01, /* 309 */ + 2.905121147632599e-01, /* 310 */ + -3.162893652915955e-02, /* 311 */ + }, { + -7.365202903747559e-02, /* 312 */ + 8.103420734405518e-01, /* 313 */ + 2.955844402313232e-01, /* 314 */ + -3.227448463439941e-02, /* 315 */ + }, { + -7.376047968864441e-02, /* 316 */ + 8.060058653354645e-01, /* 317 */ + 3.006759583950043e-01, /* 318 */ + -3.292134404182434e-02, /* 319 */ + }, { + -7.385253906250000e-02, /* 320 */ + 8.016357421875000e-01, /* 321 */ + 3.057861328125000e-01, /* 322 */ + -3.356933593750000e-02, /* 323 */ + }, { + -7.392838597297668e-02, /* 324 */ + 7.972322404384613e-01, /* 325 */ + 3.109144270420074e-01, /* 326 */ + -3.421828150749207e-02, /* 327 */ + }, { + -7.398819923400879e-02, /* 328 */ + 7.927958965301514e-01, /* 329 */ + 3.160603046417236e-01, /* 330 */ + -3.486800193786621e-02, /* 331 */ + }, { + -7.403215765953064e-02, /* 332 */ + 7.883272469043732e-01, /* 333 */ + 3.212232291698456e-01, /* 334 */ + -3.551831841468811e-02, /* 335 */ + }, { + -7.406044006347656e-02, /* 336 */ + 7.838268280029297e-01, /* 337 */ + 3.264026641845703e-01, /* 338 */ + -3.616905212402344e-02, /* 339 */ + }, { + -7.407322525978088e-02, /* 340 */ + 7.792951762676239e-01, /* 341 */ + 3.315980732440948e-01, /* 342 */ + -3.682002425193787e-02, /* 343 */ + }, { + -7.407069206237793e-02, /* 344 */ + 7.747328281402588e-01, /* 345 */ + 3.368089199066162e-01, /* 346 */ + -3.747105598449707e-02, /* 347 */ + }, { + -7.405301928520203e-02, /* 348 */ + 7.701403200626373e-01, /* 349 */ + 3.420346677303314e-01, /* 350 */ + -3.812196850776672e-02, /* 351 */ + }, { + -7.402038574218750e-02, /* 352 */ + 7.655181884765625e-01, /* 353 */ + 3.472747802734375e-01, /* 354 */ + -3.877258300781250e-02, /* 355 */ + }, { + -7.397297024726868e-02, /* 356 */ + 7.608669698238373e-01, /* 357 */ + 3.525287210941315e-01, /* 358 */ + -3.942272067070007e-02, /* 359 */ + }, { + -7.391095161437988e-02, /* 360 */ + 7.561872005462646e-01, /* 361 */ + 3.577959537506104e-01, /* 362 */ + -4.007220268249512e-02, /* 363 */ + }, { + -7.383450865745544e-02, /* 364 */ + 7.514794170856476e-01, /* 365 */ + 3.630759418010712e-01, /* 366 */ + -4.072085022926331e-02, /* 367 */ + }, { + -7.374382019042969e-02, /* 368 */ + 7.467441558837891e-01, /* 369 */ + 3.683681488037109e-01, /* 370 */ + -4.136848449707031e-02, /* 371 */ + }, { + -7.363906502723694e-02, /* 372 */ + 7.419819533824921e-01, /* 373 */ + 3.736720383167267e-01, /* 374 */ + -4.201492667198181e-02, /* 375 */ + }, { + -7.352042198181152e-02, /* 376 */ + 7.371933460235596e-01, /* 377 */ + 3.789870738983154e-01, /* 378 */ + -4.265999794006348e-02, /* 379 */ + }, { + -7.338806986808777e-02, /* 380 */ + 7.323788702487946e-01, /* 381 */ + 3.843127191066742e-01, /* 382 */ + -4.330351948738098e-02, /* 383 */ + }, { + -7.324218750000000e-02, /* 384 */ + 7.275390625000000e-01, /* 385 */ + 3.896484375000000e-01, /* 386 */ + -4.394531250000000e-02, /* 387 */ + }, { + -7.308295369148254e-02, /* 388 */ + 7.226744592189789e-01, /* 389 */ + 3.949936926364899e-01, /* 390 */ + -4.458519816398621e-02, /* 391 */ + }, { + -7.291054725646973e-02, /* 392 */ + 7.177855968475342e-01, /* 393 */ + 4.003479480743408e-01, /* 394 */ + -4.522299766540527e-02, /* 395 */ + }, { + -7.272514700889587e-02, /* 396 */ + 7.128730118274689e-01, /* 397 */ + 4.057106673717499e-01, /* 398 */ + -4.585853219032288e-02, /* 399 */ + }, { + -7.252693176269531e-02, /* 400 */ + 7.079372406005859e-01, /* 401 */ + 4.110813140869141e-01, /* 402 */ + -4.649162292480469e-02, /* 403 */ + }, { + -7.231608033180237e-02, /* 404 */ + 7.029788196086884e-01, /* 405 */ + 4.164593517780304e-01, /* 406 */ + -4.712209105491638e-02, /* 407 */ + }, { + -7.209277153015137e-02, /* 408 */ + 6.979982852935791e-01, /* 409 */ + 4.218442440032959e-01, /* 410 */ + -4.774975776672363e-02, /* 411 */ + }, { + -7.185718417167664e-02, /* 412 */ + 6.929961740970612e-01, /* 413 */ + 4.272354543209076e-01, /* 414 */ + -4.837444424629211e-02, /* 415 */ + }, { + -7.160949707031250e-02, /* 416 */ + 6.879730224609375e-01, /* 417 */ + 4.326324462890625e-01, /* 418 */ + -4.899597167968750e-02, /* 419 */ + }, { + -7.134988903999329e-02, /* 420 */ + 6.829293668270111e-01, /* 421 */ + 4.380346834659576e-01, /* 422 */ + -4.961416125297546e-02, /* 423 */ + }, { + -7.107853889465332e-02, /* 424 */ + 6.778657436370850e-01, /* 425 */ + 4.434416294097900e-01, /* 426 */ + -5.022883415222168e-02, /* 427 */ + }, { + -7.079562544822693e-02, /* 428 */ + 6.727826893329620e-01, /* 429 */ + 4.488527476787567e-01, /* 430 */ + -5.083981156349182e-02, /* 431 */ + }, { + -7.050132751464844e-02, /* 432 */ + 6.676807403564453e-01, /* 433 */ + 4.542675018310547e-01, /* 434 */ + -5.144691467285156e-02, /* 435 */ + }, { + -7.019582390785217e-02, /* 436 */ + 6.625604331493378e-01, /* 437 */ + 4.596853554248810e-01, /* 438 */ + -5.204996466636658e-02, /* 439 */ + }, { + -6.987929344177246e-02, /* 440 */ + 6.574223041534424e-01, /* 441 */ + 4.651057720184326e-01, /* 442 */ + -5.264878273010254e-02, /* 443 */ + }, { + -6.955191493034363e-02, /* 444 */ + 6.522668898105621e-01, /* 445 */ + 4.705282151699066e-01, /* 446 */ + -5.324319005012512e-02, /* 447 */ + }, { + -6.921386718750000e-02, /* 448 */ + 6.470947265625000e-01, /* 449 */ + 4.759521484375000e-01, /* 450 */ + -5.383300781250000e-02, /* 451 */ + }, { + -6.886532902717590e-02, /* 452 */ + 6.419063508510590e-01, /* 453 */ + 4.813770353794098e-01, /* 454 */ + -5.441805720329285e-02, /* 455 */ + }, { + -6.850647926330566e-02, /* 456 */ + 6.367022991180420e-01, /* 457 */ + 4.868023395538330e-01, /* 458 */ + -5.499815940856934e-02, /* 459 */ + }, { + -6.813749670982361e-02, /* 460 */ + 6.314831078052521e-01, /* 461 */ + 4.922275245189667e-01, /* 462 */ + -5.557313561439514e-02, /* 463 */ + }, { + -6.775856018066406e-02, /* 464 */ + 6.262493133544922e-01, /* 465 */ + 4.976520538330078e-01, /* 466 */ + -5.614280700683594e-02, /* 467 */ + }, { + -6.736984848976135e-02, /* 468 */ + 6.210014522075653e-01, /* 469 */ + 5.030753910541534e-01, /* 470 */ + -5.670699477195740e-02, /* 471 */ + }, { + -6.697154045104980e-02, /* 472 */ + 6.157400608062744e-01, /* 473 */ + 5.084969997406006e-01, /* 474 */ + -5.726552009582520e-02, /* 475 */ + }, { + -6.656381487846375e-02, /* 476 */ + 6.104656755924225e-01, /* 477 */ + 5.139163434505463e-01, /* 478 */ + -5.781820416450500e-02, /* 479 */ + }, { + -6.614685058593750e-02, /* 480 */ + 6.051788330078125e-01, /* 481 */ + 5.193328857421875e-01, /* 482 */ + -5.836486816406250e-02, /* 483 */ + }, { + -6.572082638740540e-02, /* 484 */ + 5.998800694942474e-01, /* 485 */ + 5.247460901737213e-01, /* 486 */ + -5.890533328056335e-02, /* 487 */ + }, { + -6.528592109680176e-02, /* 488 */ + 5.945699214935303e-01, /* 489 */ + 5.301554203033447e-01, /* 490 */ + -5.943942070007324e-02, /* 491 */ + }, { + -6.484231352806091e-02, /* 492 */ + 5.892489254474640e-01, /* 493 */ + 5.355603396892548e-01, /* 494 */ + -5.996695160865784e-02, /* 495 */ + }, { + -6.439018249511719e-02, /* 496 */ + 5.839176177978516e-01, /* 497 */ + 5.409603118896484e-01, /* 498 */ + -6.048774719238281e-02, /* 499 */ + }, { + -6.392970681190491e-02, /* 500 */ + 5.785765349864960e-01, /* 501 */ + 5.463548004627228e-01, /* 502 */ + -6.100162863731384e-02, /* 503 */ + }, { + -6.346106529235840e-02, /* 504 */ + 5.732262134552002e-01, /* 505 */ + 5.517432689666748e-01, /* 506 */ + -6.150841712951660e-02, /* 507 */ + }, { + -6.298443675041199e-02, /* 508 */ + 5.678671896457672e-01, /* 509 */ + 5.571251809597015e-01, /* 510 */ + -6.200793385505676e-02, /* 511 */ + }, { + -6.250000000000000e-02, /* 512 */ + 5.625000000000000e-01, /* 513 */ + 5.625000000000000e-01, /* 514 */ + -6.250000000000000e-02, /* 515 */ + }, { + -6.200793385505676e-02, /* 516 */ + 5.571251809597015e-01, /* 517 */ + 5.678671896457672e-01, /* 518 */ + -6.298443675041199e-02, /* 519 */ + }, { + -6.150841712951660e-02, /* 520 */ + 5.517432689666748e-01, /* 521 */ + 5.732262134552002e-01, /* 522 */ + -6.346106529235840e-02, /* 523 */ + }, { + -6.100162863731384e-02, /* 524 */ + 5.463548004627228e-01, /* 525 */ + 5.785765349864960e-01, /* 526 */ + -6.392970681190491e-02, /* 527 */ + }, { + -6.048774719238281e-02, /* 528 */ + 5.409603118896484e-01, /* 529 */ + 5.839176177978516e-01, /* 530 */ + -6.439018249511719e-02, /* 531 */ + }, { + -5.996695160865784e-02, /* 532 */ + 5.355603396892548e-01, /* 533 */ + 5.892489254474640e-01, /* 534 */ + -6.484231352806091e-02, /* 535 */ + }, { + -5.943942070007324e-02, /* 536 */ + 5.301554203033447e-01, /* 537 */ + 5.945699214935303e-01, /* 538 */ + -6.528592109680176e-02, /* 539 */ + }, { + -5.890533328056335e-02, /* 540 */ + 5.247460901737213e-01, /* 541 */ + 5.998800694942474e-01, /* 542 */ + -6.572082638740540e-02, /* 543 */ + }, { + -5.836486816406250e-02, /* 544 */ + 5.193328857421875e-01, /* 545 */ + 6.051788330078125e-01, /* 546 */ + -6.614685058593750e-02, /* 547 */ + }, { + -5.781820416450500e-02, /* 548 */ + 5.139163434505463e-01, /* 549 */ + 6.104656755924225e-01, /* 550 */ + -6.656381487846375e-02, /* 551 */ + }, { + -5.726552009582520e-02, /* 552 */ + 5.084969997406006e-01, /* 553 */ + 6.157400608062744e-01, /* 554 */ + -6.697154045104980e-02, /* 555 */ + }, { + -5.670699477195740e-02, /* 556 */ + 5.030753910541534e-01, /* 557 */ + 6.210014522075653e-01, /* 558 */ + -6.736984848976135e-02, /* 559 */ + }, { + -5.614280700683594e-02, /* 560 */ + 4.976520538330078e-01, /* 561 */ + 6.262493133544922e-01, /* 562 */ + -6.775856018066406e-02, /* 563 */ + }, { + -5.557313561439514e-02, /* 564 */ + 4.922275245189667e-01, /* 565 */ + 6.314831078052521e-01, /* 566 */ + -6.813749670982361e-02, /* 567 */ + }, { + -5.499815940856934e-02, /* 568 */ + 4.868023395538330e-01, /* 569 */ + 6.367022991180420e-01, /* 570 */ + -6.850647926330566e-02, /* 571 */ + }, { + -5.441805720329285e-02, /* 572 */ + 4.813770353794098e-01, /* 573 */ + 6.419063508510590e-01, /* 574 */ + -6.886532902717590e-02, /* 575 */ + }, { + -5.383300781250000e-02, /* 576 */ + 4.759521484375000e-01, /* 577 */ + 6.470947265625000e-01, /* 578 */ + -6.921386718750000e-02, /* 579 */ + }, { + -5.324319005012512e-02, /* 580 */ + 4.705282151699066e-01, /* 581 */ + 6.522668898105621e-01, /* 582 */ + -6.955191493034363e-02, /* 583 */ + }, { + -5.264878273010254e-02, /* 584 */ + 4.651057720184326e-01, /* 585 */ + 6.574223041534424e-01, /* 586 */ + -6.987929344177246e-02, /* 587 */ + }, { + -5.204996466636658e-02, /* 588 */ + 4.596853554248810e-01, /* 589 */ + 6.625604331493378e-01, /* 590 */ + -7.019582390785217e-02, /* 591 */ + }, { + -5.144691467285156e-02, /* 592 */ + 4.542675018310547e-01, /* 593 */ + 6.676807403564453e-01, /* 594 */ + -7.050132751464844e-02, /* 595 */ + }, { + -5.083981156349182e-02, /* 596 */ + 4.488527476787567e-01, /* 597 */ + 6.727826893329620e-01, /* 598 */ + -7.079562544822693e-02, /* 599 */ + }, { + -5.022883415222168e-02, /* 600 */ + 4.434416294097900e-01, /* 601 */ + 6.778657436370850e-01, /* 602 */ + -7.107853889465332e-02, /* 603 */ + }, { + -4.961416125297546e-02, /* 604 */ + 4.380346834659576e-01, /* 605 */ + 6.829293668270111e-01, /* 606 */ + -7.134988903999329e-02, /* 607 */ + }, { + -4.899597167968750e-02, /* 608 */ + 4.326324462890625e-01, /* 609 */ + 6.879730224609375e-01, /* 610 */ + -7.160949707031250e-02, /* 611 */ + }, { + -4.837444424629211e-02, /* 612 */ + 4.272354543209076e-01, /* 613 */ + 6.929961740970612e-01, /* 614 */ + -7.185718417167664e-02, /* 615 */ + }, { + -4.774975776672363e-02, /* 616 */ + 4.218442440032959e-01, /* 617 */ + 6.979982852935791e-01, /* 618 */ + -7.209277153015137e-02, /* 619 */ + }, { + -4.712209105491638e-02, /* 620 */ + 4.164593517780304e-01, /* 621 */ + 7.029788196086884e-01, /* 622 */ + -7.231608033180237e-02, /* 623 */ + }, { + -4.649162292480469e-02, /* 624 */ + 4.110813140869141e-01, /* 625 */ + 7.079372406005859e-01, /* 626 */ + -7.252693176269531e-02, /* 627 */ + }, { + -4.585853219032288e-02, /* 628 */ + 4.057106673717499e-01, /* 629 */ + 7.128730118274689e-01, /* 630 */ + -7.272514700889587e-02, /* 631 */ + }, { + -4.522299766540527e-02, /* 632 */ + 4.003479480743408e-01, /* 633 */ + 7.177855968475342e-01, /* 634 */ + -7.291054725646973e-02, /* 635 */ + }, { + -4.458519816398621e-02, /* 636 */ + 3.949936926364899e-01, /* 637 */ + 7.226744592189789e-01, /* 638 */ + -7.308295369148254e-02, /* 639 */ + }, { + -4.394531250000000e-02, /* 640 */ + 3.896484375000000e-01, /* 641 */ + 7.275390625000000e-01, /* 642 */ + -7.324218750000000e-02, /* 643 */ + }, { + -4.330351948738098e-02, /* 644 */ + 3.843127191066742e-01, /* 645 */ + 7.323788702487946e-01, /* 646 */ + -7.338806986808777e-02, /* 647 */ + }, { + -4.265999794006348e-02, /* 648 */ + 3.789870738983154e-01, /* 649 */ + 7.371933460235596e-01, /* 650 */ + -7.352042198181152e-02, /* 651 */ + }, { + -4.201492667198181e-02, /* 652 */ + 3.736720383167267e-01, /* 653 */ + 7.419819533824921e-01, /* 654 */ + -7.363906502723694e-02, /* 655 */ + }, { + -4.136848449707031e-02, /* 656 */ + 3.683681488037109e-01, /* 657 */ + 7.467441558837891e-01, /* 658 */ + -7.374382019042969e-02, /* 659 */ + }, { + -4.072085022926331e-02, /* 660 */ + 3.630759418010712e-01, /* 661 */ + 7.514794170856476e-01, /* 662 */ + -7.383450865745544e-02, /* 663 */ + }, { + -4.007220268249512e-02, /* 664 */ + 3.577959537506104e-01, /* 665 */ + 7.561872005462646e-01, /* 666 */ + -7.391095161437988e-02, /* 667 */ + }, { + -3.942272067070007e-02, /* 668 */ + 3.525287210941315e-01, /* 669 */ + 7.608669698238373e-01, /* 670 */ + -7.397297024726868e-02, /* 671 */ + }, { + -3.877258300781250e-02, /* 672 */ + 3.472747802734375e-01, /* 673 */ + 7.655181884765625e-01, /* 674 */ + -7.402038574218750e-02, /* 675 */ + }, { + -3.812196850776672e-02, /* 676 */ + 3.420346677303314e-01, /* 677 */ + 7.701403200626373e-01, /* 678 */ + -7.405301928520203e-02, /* 679 */ + }, { + -3.747105598449707e-02, /* 680 */ + 3.368089199066162e-01, /* 681 */ + 7.747328281402588e-01, /* 682 */ + -7.407069206237793e-02, /* 683 */ + }, { + -3.682002425193787e-02, /* 684 */ + 3.315980732440948e-01, /* 685 */ + 7.792951762676239e-01, /* 686 */ + -7.407322525978088e-02, /* 687 */ + }, { + -3.616905212402344e-02, /* 688 */ + 3.264026641845703e-01, /* 689 */ + 7.838268280029297e-01, /* 690 */ + -7.406044006347656e-02, /* 691 */ + }, { + -3.551831841468811e-02, /* 692 */ + 3.212232291698456e-01, /* 693 */ + 7.883272469043732e-01, /* 694 */ + -7.403215765953064e-02, /* 695 */ + }, { + -3.486800193786621e-02, /* 696 */ + 3.160603046417236e-01, /* 697 */ + 7.927958965301514e-01, /* 698 */ + -7.398819923400879e-02, /* 699 */ + }, { + -3.421828150749207e-02, /* 700 */ + 3.109144270420074e-01, /* 701 */ + 7.972322404384613e-01, /* 702 */ + -7.392838597297668e-02, /* 703 */ + }, { + -3.356933593750000e-02, /* 704 */ + 3.057861328125000e-01, /* 705 */ + 8.016357421875000e-01, /* 706 */ + -7.385253906250000e-02, /* 707 */ + }, { + -3.292134404182434e-02, /* 708 */ + 3.006759583950043e-01, /* 709 */ + 8.060058653354645e-01, /* 710 */ + -7.376047968864441e-02, /* 711 */ + }, { + -3.227448463439941e-02, /* 712 */ + 2.955844402313232e-01, /* 713 */ + 8.103420734405518e-01, /* 714 */ + -7.365202903747559e-02, /* 715 */ + }, { + -3.162893652915955e-02, /* 716 */ + 2.905121147632599e-01, /* 717 */ + 8.146438300609589e-01, /* 718 */ + -7.352700829505920e-02, /* 719 */ + }, { + -3.098487854003906e-02, /* 720 */ + 2.854595184326172e-01, /* 721 */ + 8.189105987548828e-01, /* 722 */ + -7.338523864746094e-02, /* 723 */ + }, { + -3.034248948097229e-02, /* 724 */ + 2.804271876811981e-01, /* 725 */ + 8.231418430805206e-01, /* 726 */ + -7.322654128074646e-02, /* 727 */ + }, { + -2.970194816589355e-02, /* 728 */ + 2.754156589508057e-01, /* 729 */ + 8.273370265960693e-01, /* 730 */ + -7.305073738098145e-02, /* 731 */ + }, { + -2.906343340873718e-02, /* 732 */ + 2.704254686832428e-01, /* 733 */ + 8.314956128597260e-01, /* 734 */ + -7.285764813423157e-02, /* 735 */ + }, { + -2.842712402343750e-02, /* 736 */ + 2.654571533203125e-01, /* 737 */ + 8.356170654296875e-01, /* 738 */ + -7.264709472656250e-02, /* 739 */ + }, { + -2.779319882392883e-02, /* 740 */ + 2.605112493038177e-01, /* 741 */ + 8.397008478641510e-01, /* 742 */ + -7.241889834403992e-02, /* 743 */ + }, { + -2.716183662414551e-02, /* 744 */ + 2.555882930755615e-01, /* 745 */ + 8.437464237213135e-01, /* 746 */ + -7.217288017272949e-02, /* 747 */ + }, { + -2.653321623802185e-02, /* 748 */ + 2.506888210773468e-01, /* 749 */ + 8.477532565593719e-01, /* 750 */ + -7.190886139869690e-02, /* 751 */ + }, { + -2.590751647949219e-02, /* 752 */ + 2.458133697509766e-01, /* 753 */ + 8.517208099365234e-01, /* 754 */ + -7.162666320800781e-02, /* 755 */ + }, { + -2.528491616249084e-02, /* 756 */ + 2.409624755382538e-01, /* 757 */ + 8.556485474109650e-01, /* 758 */ + -7.132610678672791e-02, /* 759 */ + }, { + -2.466559410095215e-02, /* 760 */ + 2.361366748809814e-01, /* 761 */ + 8.595359325408936e-01, /* 762 */ + -7.100701332092285e-02, /* 763 */ + }, { + -2.404972910881042e-02, /* 764 */ + 2.313365042209625e-01, /* 765 */ + 8.633824288845062e-01, /* 766 */ + -7.066920399665833e-02, /* 767 */ + }, { + -2.343750000000000e-02, /* 768 */ + 2.265625000000000e-01, /* 769 */ + 8.671875000000000e-01, /* 770 */ + -7.031250000000000e-02, /* 771 */ + }, { + -2.282908558845520e-02, /* 772 */ + 2.218151986598969e-01, /* 773 */ + 8.709506094455719e-01, /* 774 */ + -6.993672251701355e-02, /* 775 */ + }, { + -2.222466468811035e-02, /* 776 */ + 2.170951366424561e-01, /* 777 */ + 8.746712207794189e-01, /* 778 */ + -6.954169273376465e-02, /* 779 */ + }, { + -2.162441611289978e-02, /* 780 */ + 2.124028503894806e-01, /* 781 */ + 8.783487975597382e-01, /* 782 */ + -6.912723183631897e-02, /* 783 */ + }, { + -2.102851867675781e-02, /* 784 */ + 2.077388763427734e-01, /* 785 */ + 8.819828033447266e-01, /* 786 */ + -6.869316101074219e-02, /* 787 */ + }, { + -2.043715119361877e-02, /* 788 */ + 2.031037509441376e-01, /* 789 */ + 8.855727016925812e-01, /* 790 */ + -6.823930144309998e-02, /* 791 */ + }, { + -1.985049247741699e-02, /* 792 */ + 1.984980106353760e-01, /* 793 */ + 8.891179561614990e-01, /* 794 */ + -6.776547431945801e-02, /* 795 */ + }, { + -1.926872134208679e-02, /* 796 */ + 1.939221918582916e-01, /* 797 */ + 8.926180303096771e-01, /* 798 */ + -6.727150082588196e-02, /* 799 */ + }, { + -1.869201660156250e-02, /* 800 */ + 1.893768310546875e-01, /* 801 */ + 8.960723876953125e-01, /* 802 */ + -6.675720214843750e-02, /* 803 */ + }, { + -1.812055706977844e-02, /* 804 */ + 1.848624646663666e-01, /* 805 */ + 8.994804918766022e-01, /* 806 */ + -6.622239947319031e-02, /* 807 */ + }, { + -1.755452156066895e-02, /* 808 */ + 1.803796291351318e-01, /* 809 */ + 9.028418064117432e-01, /* 810 */ + -6.566691398620605e-02, /* 811 */ + }, { + -1.699408888816833e-02, /* 812 */ + 1.759288609027863e-01, /* 813 */ + 9.061557948589325e-01, /* 814 */ + -6.509056687355042e-02, /* 815 */ + }, { + -1.643943786621094e-02, /* 816 */ + 1.715106964111328e-01, /* 817 */ + 9.094219207763672e-01, /* 818 */ + -6.449317932128906e-02, /* 819 */ + }, { + -1.589074730873108e-02, /* 820 */ + 1.671256721019745e-01, /* 821 */ + 9.126396477222443e-01, /* 822 */ + -6.387457251548767e-02, /* 823 */ + }, { + -1.534819602966309e-02, /* 824 */ + 1.627743244171143e-01, /* 825 */ + 9.158084392547607e-01, /* 826 */ + -6.323456764221191e-02, /* 827 */ + }, { + -1.481196284294128e-02, /* 828 */ + 1.584571897983551e-01, /* 829 */ + 9.189277589321136e-01, /* 830 */ + -6.257298588752747e-02, /* 831 */ + }, { + -1.428222656250000e-02, /* 832 */ + 1.541748046875000e-01, /* 833 */ + 9.219970703125000e-01, /* 834 */ + -6.188964843750000e-02, /* 835 */ + }, { + -1.375916600227356e-02, /* 836 */ + 1.499277055263519e-01, /* 837 */ + 9.250158369541168e-01, /* 838 */ + -6.118437647819519e-02, /* 839 */ + }, { + -1.324295997619629e-02, /* 840 */ + 1.457164287567139e-01, /* 841 */ + 9.279835224151611e-01, /* 842 */ + -6.045699119567871e-02, /* 843 */ + }, { + -1.273378729820251e-02, /* 844 */ + 1.415415108203888e-01, /* 845 */ + 9.308995902538300e-01, /* 846 */ + -5.970731377601624e-02, /* 847 */ + }, { + -1.223182678222656e-02, /* 848 */ + 1.374034881591797e-01, /* 849 */ + 9.337635040283203e-01, /* 850 */ + -5.893516540527344e-02, /* 851 */ + }, { + -1.173725724220276e-02, /* 852 */ + 1.333028972148895e-01, /* 853 */ + 9.365747272968292e-01, /* 854 */ + -5.814036726951599e-02, /* 855 */ + }, { + -1.125025749206543e-02, /* 856 */ + 1.292402744293213e-01, /* 857 */ + 9.393327236175537e-01, /* 858 */ + -5.732274055480957e-02, /* 859 */ + }, { + -1.077100634574890e-02, /* 860 */ + 1.252161562442780e-01, /* 861 */ + 9.420369565486908e-01, /* 862 */ + -5.648210644721985e-02, /* 863 */ + }, { + -1.029968261718750e-02, /* 864 */ + 1.212310791015625e-01, /* 865 */ + 9.446868896484375e-01, /* 866 */ + -5.561828613281250e-02, /* 867 */ + }, { + -9.836465120315552e-03, /* 868 */ + 1.172855794429779e-01, /* 869 */ + 9.472819864749908e-01, /* 870 */ + -5.473110079765320e-02, /* 871 */ + }, { + -9.381532669067383e-03, /* 872 */ + 1.133801937103271e-01, /* 873 */ + 9.498217105865479e-01, /* 874 */ + -5.382037162780762e-02, /* 875 */ + }, { + -8.935064077377319e-03, /* 876 */ + 1.095154583454132e-01, /* 877 */ + 9.523055255413055e-01, /* 878 */ + -5.288591980934143e-02, /* 879 */ + }, { + -8.497238159179688e-03, /* 880 */ + 1.056919097900391e-01, /* 881 */ + 9.547328948974609e-01, /* 882 */ + -5.192756652832031e-02, /* 883 */ + }, { + -8.068233728408813e-03, /* 884 */ + 1.019100844860077e-01, /* 885 */ + 9.571032822132111e-01, /* 886 */ + -5.094513297080994e-02, /* 887 */ + }, { + -7.648229598999023e-03, /* 888 */ + 9.817051887512207e-02, /* 889 */ + 9.594161510467529e-01, /* 890 */ + -4.993844032287598e-02, /* 891 */ + }, { + -7.237404584884644e-03, /* 892 */ + 9.447374939918518e-02, /* 893 */ + 9.616709649562836e-01, /* 894 */ + -4.890730977058411e-02, /* 895 */ + }, { + -6.835937500000000e-03, /* 896 */ + 9.082031250000000e-02, /* 897 */ + 9.638671875000000e-01, /* 898 */ + -4.785156250000000e-02, /* 899 */ + }, { + -6.444007158279419e-03, /* 900 */ + 8.721074461936951e-02, /* 901 */ + 9.660042822360992e-01, /* 902 */ + -4.677101969718933e-02, /* 903 */ + }, { + -6.061792373657227e-03, /* 904 */ + 8.364558219909668e-02, /* 905 */ + 9.680817127227783e-01, /* 906 */ + -4.566550254821777e-02, /* 907 */ + }, { + -5.689471960067749e-03, /* 908 */ + 8.012536168098450e-02, /* 909 */ + 9.700989425182343e-01, /* 910 */ + -4.453483223915100e-02, /* 911 */ + }, { + -5.327224731445312e-03, /* 912 */ + 7.665061950683594e-02, /* 913 */ + 9.720554351806641e-01, /* 914 */ + -4.337882995605469e-02, /* 915 */ + }, { + -4.975229501724243e-03, /* 916 */ + 7.322189211845398e-02, /* 917 */ + 9.739506542682648e-01, /* 918 */ + -4.219731688499451e-02, /* 919 */ + }, { + -4.633665084838867e-03, /* 920 */ + 6.983971595764160e-02, /* 921 */ + 9.757840633392334e-01, /* 922 */ + -4.099011421203613e-02, /* 923 */ + }, { + -4.302710294723511e-03, /* 924 */ + 6.650462746620178e-02, /* 925 */ + 9.775551259517670e-01, /* 926 */ + -3.975704312324524e-02, /* 927 */ + }, { + -3.982543945312500e-03, /* 928 */ + 6.321716308593750e-02, /* 929 */ + 9.792633056640625e-01, /* 930 */ + -3.849792480468750e-02, /* 931 */ + }, { + -3.673344850540161e-03, /* 932 */ + 5.997785925865173e-02, /* 933 */ + 9.809080660343170e-01, /* 934 */ + -3.721258044242859e-02, /* 935 */ + }, { + -3.375291824340820e-03, /* 936 */ + 5.678725242614746e-02, /* 937 */ + 9.824888706207275e-01, /* 938 */ + -3.590083122253418e-02, /* 939 */ + }, { + -3.088563680648804e-03, /* 940 */ + 5.364587903022766e-02, /* 941 */ + 9.840051829814911e-01, /* 942 */ + -3.456249833106995e-02, /* 943 */ + }, { + -2.813339233398438e-03, /* 944 */ + 5.055427551269531e-02, /* 945 */ + 9.854564666748047e-01, /* 946 */ + -3.319740295410156e-02, /* 947 */ + }, { + -2.549797296524048e-03, /* 948 */ + 4.751297831535339e-02, /* 949 */ + 9.868421852588654e-01, /* 950 */ + -3.180536627769470e-02, /* 951 */ + }, { + -2.298116683959961e-03, /* 952 */ + 4.452252388000488e-02, /* 953 */ + 9.881618022918701e-01, /* 954 */ + -3.038620948791504e-02, /* 955 */ + }, { + -2.058476209640503e-03, /* 956 */ + 4.158344864845276e-02, /* 957 */ + 9.894147813320160e-01, /* 958 */ + -2.893975377082825e-02, /* 959 */ + }, { + -1.831054687500000e-03, /* 960 */ + 3.869628906250000e-02, /* 961 */ + 9.906005859375000e-01, /* 962 */ + -2.746582031250000e-02, /* 963 */ + }, { + -1.616030931472778e-03, /* 964 */ + 3.586158156394958e-02, /* 965 */ + 9.917186796665192e-01, /* 966 */ + -2.596423029899597e-02, /* 967 */ + }, { + -1.413583755493164e-03, /* 968 */ + 3.307986259460449e-02, /* 969 */ + 9.927685260772705e-01, /* 970 */ + -2.443480491638184e-02, /* 971 */ + }, { + -1.223891973495483e-03, /* 972 */ + 3.035166859626770e-02, /* 973 */ + 9.937495887279510e-01, /* 974 */ + -2.287736535072327e-02, /* 975 */ + }, { + -1.047134399414062e-03, /* 976 */ + 2.767753601074219e-02, /* 977 */ + 9.946613311767578e-01, /* 978 */ + -2.129173278808594e-02, /* 979 */ + }, { + -8.834898471832275e-04, /* 980 */ + 2.505800127983093e-02, /* 981 */ + 9.955032169818878e-01, /* 982 */ + -1.967772841453552e-02, /* 983 */ + }, { + -7.331371307373047e-04, /* 984 */ + 2.249360084533691e-02, /* 985 */ + 9.962747097015381e-01, /* 986 */ + -1.803517341613770e-02, /* 987 */ + }, { + -5.962550640106201e-04, /* 988 */ + 1.998487114906311e-02, /* 989 */ + 9.969752728939056e-01, /* 990 */ + -1.636388897895813e-02, /* 991 */ + }, { + -4.730224609375000e-04, /* 992 */ + 1.753234863281250e-02, /* 993 */ + 9.976043701171875e-01, /* 994 */ + -1.466369628906250e-02, /* 995 */ + }, { + -3.636181354522705e-04, /* 996 */ + 1.513656973838806e-02, /* 997 */ + 9.981614649295807e-01, /* 998 */ + -1.293441653251648e-02, /* 999 */ + }, { + -2.682209014892578e-04, /* 1000 */ + 1.279807090759277e-02, /* 1001 */ + 9.986460208892822e-01, /* 1002 */ + -1.117587089538574e-02, /* 1003 */ + }, { + -1.870095729827881e-04, /* 1004 */ + 1.051738858222961e-02, /* 1005 */ + 9.990575015544891e-01, /* 1006 */ + -9.387880563735962e-03, /* 1007 */ + }, { + -1.201629638671875e-04, /* 1008 */ + 8.295059204101562e-03, /* 1009 */ + 9.993953704833984e-01, /* 1010 */ + -7.570266723632812e-03, /* 1011 */ + }, { + -6.785988807678223e-05, /* 1012 */ + 6.131619215011597e-03, /* 1013 */ + 9.996590912342072e-01, /* 1014 */ + -5.722850561141968e-03, /* 1015 */ + }, { + -3.027915954589844e-05, /* 1016 */ + 4.027605056762695e-03, /* 1017 */ + 9.998481273651123e-01, /* 1018 */ + -3.845453262329102e-03, /* 1019 */ + }, { + -7.599592208862305e-06, /* 1020 */ + 1.983553171157837e-03, /* 1021 */ + 9.999619424343109e-01, /* 1022 */ + -1.937896013259888e-03, /* 1023 */ + } +}; + +static const fluid_real_t sinc_table7[256][7] = { + { + 2.375620125729980e-02, /* 0 */ + -1.290049686942476e-01, /* 1 */ + 5.998790953453343e-01, /* 2 */ + 6.103020511314905e-01, /* 3 */ + -1.304058543975903e-01, /* 4 */ + 2.418010665366927e-02, /* 5 */ + -2.798064002790454e-07, /* 6 */ + }, { + 2.354065170871666e-02, /* 7 */ + -1.282805558938982e-01, /* 8 */ + 5.946483199408858e-01, /* 9 */ + 6.154931623267351e-01, /* 10 */ + -1.310817379215285e-01, /* 11 */ + 2.438827747279956e-02, /* 12 */ + -1.120220972351802e-06, /* 13 */ + }, { + 2.332282679065955e-02, /* 14 */ + -1.275405562274653e-01, /* 15 */ + 5.894053926563386e-01, /* 16 */ + 6.206699834160688e-01, /* 17 */ + -1.317408559206581e-01, /* 18 */ + 2.459380290371239e-02, /* 19 */ + -2.522356622734990e-06, /* 20 */ + }, { + 2.310281775655174e-02, /* 21 */ + -1.267852645808413e-01, /* 22 */ + 5.841508484795060e-01, /* 23 */ + 6.258319806421593e-01, /* 24 */ + -1.323829141999531e-01, /* 25 */ + 2.479658928687493e-02, /* 26 */ + -4.486817393493708e-06, /* 27 */ + }, { + 2.288071532172811e-02, /* 28 */ + -1.260149758298408e-01, /* 29 */ + 5.788852224239674e-01, /* 30 */ + 6.309786207146856e-01, /* 31 */ + -1.330076188523975e-01, /* 32 */ + 2.499654254034837e-02, /* 33 */ + -7.013696123785544e-06, /* 34 */ + }, { + 2.265660964514263e-02, /* 35 */ + -1.252299847913509e-01, /* 36 */ + 5.736090494558752e-01, /* 37 */ + 6.361093708841161e-01, /* 38 */ + -1.336146763094198e-01, /* 39 */ + 2.519356818002896e-02, /* 40 */ + -1.010257230258042e-05, /* 41 */ + }, { + 2.243059031136537e-02, /* 42 */ + -1.244305861747393e-01, /* 43 */ + 5.683228644208926e-01, /* 44 */ + 6.412236990155202e-01, /* 45 */ + -1.342037933915221e-01, /* 46 */ + 2.538757134015553e-02, /* 47 */ + -1.375251011368080e-05, /* 48 */ + }, { + 2.220274631287172e-02, /* 49 */ + -1.236170745335310e-01, /* 50 */ + 5.630272019712755e-01, /* 51 */ + 6.463210736624057e-01, /* 52 */ + -1.347746773590917e-01, /* 53 */ + 2.557845679407980e-02, /* 54 */ + -1.796205667443242e-05, /* 55 */ + }, { + 2.197316603262602e-02, /* 56 */ + -1.227897442173580e-01, /* 57 */ + 5.577225964931086e-01, /* 58 */ + 6.514009641405650e-01, /* 59 */ + -1.353270359633906e-01, /* 60 */ + 2.576612897529643e-02, /* 61 */ + -2.272924046911682e-05, /* 62 */ + }, { + 2.174193722696236e-02, /* 63 */ + -1.219488893241928e-01, /* 64 */ + 5.524095820337069e-01, /* 65 */ + 6.564628406019232e-01, /* 66 */ + -1.358605774977103e-01, /* 67 */ + 2.595049199872917e-02, /* 68 */ + -2.805156997831240e-05, /* 69 */ + }, { + 2.150914700876424e-02, /* 70 */ + -1.210948036528713e-01, /* 71 */ + 5.470886922291980e-01, /* 72 */ + 6.615061741083712e-01, /* 73 */ + -1.363750108486864e-01, /* 74 */ + 2.613144968226988e-02, /* 75 */ + -3.392603250514114e-05, /* 76 */ + }, { + 2.127488183094605e-02, /* 77 */ + -1.202277806559141e-01, /* 78 */ + 5.417604602322907e-01, /* 79 */ + 6.665304367055752e-01, /* 80 */ + -1.368700455477614e-01, /* 81 */ + 2.630890556856650e-02, /* 82 */ + -4.034909319948082e-05, /* 83 */ + }, { + 2.103922747023789e-02, /* 84 */ + -1.193481133926526e-01, /* 85 */ + 5.364254186402488e-01, /* 86 */ + 6.715351014967476e-01, /* 87 */ + -1.373453918227895e-01, /* 88 */ + 2.648276294705649e-02, /* 89 */ + -4.731669428110093e-05, /* 90 */ + }, { + 2.080226901127631e-02, /* 91 */ + -1.184560944826678e-01, /* 92 */ + 5.310840994230748e-01, /* 93 */ + 6.765196427163698e-01, /* 94 */ + -1.378007606497726e-01, /* 95 */ + 2.665292487624222e-02, /* 96 */ + -5.482425446254296e-05, /* 97 */ + }, { + 2.056409083100217e-02, /* 98 */ + -1.175520160595501e-01, /* 99 */ + 5.257370338519197e-01, /* 100 */ + 6.814835358038533e-01, /* 101 */ + -1.382358638047184e-01, /* 102 */ + 2.681929420620386e-02, /* 103 */ + -6.286666857267136e-05, /* 104 */ + }, { + 2.032477658336845e-02, /* 105 */ + -1.166361697249849e-01, /* 106 */ + 5.203847524277286e-01, /* 107 */ + 6.864262574771270e-01, /* 108 */ + -1.386504139156142e-01, /* 109 */ + 2.698177360134680e-02, /* 110 */ + -7.143830738156712e-05, /* 111 */ + }, { + 2.008440918435909e-02, /* 112 */ + -1.157088465031742e-01, /* 113 */ + 5.150277848101327e-01, /* 114 */ + 6.913472858061384e-01, /* 115 */ + -1.390441245145027e-01, /* 116 */ + 2.714026556337870e-02, /* 117 */ + -8.053301762762107e-05, /* 118 */ + }, { + 1.984307079732106e-02, /* 119 */ + -1.147703367955984e-01, /* 120 */ + 5.096666597466010e-01, /* 121 */ + 6.962461002862578e-01, /* 122 */ + -1.394167100896560e-01, /* 123 */ + 2.729467245451285e-02, /* 124 */ + -9.014412224732896e-05, /* 125 */ + }, { + 1.960084281861067e-02, /* 126 */ + -1.138209303361282e-01, /* 127 */ + 5.043019050018616e-01, /* 128 */ + 7.011221819115717e-01, /* 129 */ + -1.397678861378340e-01, /* 130 */ + 2.744489652089330e-02, /* 131 */ + -1.002644208085391e-04, /* 132 */ + }, { + 1.935780586355643e-02, /* 133 */ + -1.128609161464899e-01, /* 134 */ + 4.989340472876037e-01, /* 135 */ + 7.059750132480542e-01, /* 136 */ + -1.400973692166212e-01, /* 137 */ + 2.759083991623796e-02, /* 138 */ + -1.108861901476344e-04, /* 139 */ + }, { + 1.911403975273936e-02, /* 140 */ + -1.118905824920952e-01, /* 141 */ + 4.935636121924722e-01, /* 142 */ + 7.108040785066047e-01, /* 143 */ + -1.404048769968304e-01, /* 144 */ + 2.773240472569498e-02, /* 145 */ + -1.220011852110915e-04, /* 146 */ + }, { + 1.886962349859249e-02, /* 147 */ + -1.109102168382377e-01, /* 148 */ + 4.881911241123659e-01, /* 149 */ + 7.156088636159380e-01, /* 150 */ + -1.406901283149657e-01, /* 151 */ + 2.786949298990845e-02, /* 152 */ + -1.336006401019428e-04, /* 153 */ + }, { + 1.862463529232039e-02, /* 154 */ + -1.099201058066671e-01, /* 155 */ + 4.828171061810495e-01, /* 156 */ + 7.203888562953171e-01, /* 157 */ + -1.409528432257348e-01, /* 158 */ + 2.800200672928881e-02, /* 159 */ + -1.456752693314445e-04, /* 160 */ + }, { + 1.837915249114045e-02, /* 161 */ + -1.089205351325436e-01, /* 162 */ + 4.774420802010910e-01, /* 163 */ + 7.251435461271148e-01, /* 164 */ + -1.411927430545998e-01, /* 165 */ + 2.812984796848375e-02, /* 166 */ + -1.582152692763085e-04, /* 167 */ + }, { + 1.813325160584695e-02, /* 168 */ + -1.079117896217818e-01, /* 169 */ + 4.720665665751356e-01, /* 170 */ + 7.298724246291924e-01, /* 171 */ + -1.414095504503600e-01, /* 172 */ + 2.825291876104482e-02, /* 173 */ + -1.712103198416544e-04, /* 174 */ + }, { + 1.788700828869835e-02, /* 175 */ + -1.068941531087891e-01, /* 176 */ + 4.666910842375266e-01, /* 177 */ + 7.345749853270843e-01, /* 178 */ + -1.416029894377547e-01, /* 179 */ + 2.837112121428517e-02, /* 180 */ + -1.846495863300355e-04, /* 181 */ + }, { + 1.764049732162978e-02, /* 182 */ + -1.058679084146052e-01, /* 183 */ + 4.613161505862837e-01, /* 184 */ + 7.392507238259753e-01, /* 185 */ + -1.417727854700776e-01, /* 186 */ + 2.848435751432410e-02, /* 187 */ + -1.985217215164836e-04, /* 188 */ + }, { + 1.739379260479069e-02, /* 189 */ + -1.048333373054486e-01, /* 190 */ + 4.559422814154492e-01, /* 191 */ + 7.438991378824603e-01, /* 192 */ + -1.419186654817930e-01, /* 193 */ + 2.859252995131316e-02, /* 194 */ + -2.128148679298167e-04, /* 195 */ + }, { + 1.714696714540915e-02, /* 196 */ + -1.037907204516760e-01, /* 197 */ + 4.505699908478140e-01, /* 198 */ + 7.485197274760710e-01, /* 199 */ + -1.420403579411441e-01, /* 200 */ + 2.869554094483946e-02, /* 201 */ + -2.275166603400509e-04, /* 202 */ + }, { + 1.690009304698279e-02, /* 203 */ + -1.027403373871614e-01, /* 204 */ + 4.451997912680325e-01, /* 205 */ + 7.531119948805626e-01, /* 206 */ + -1.421375929027446e-01, /* 207 */ + 2.879329306950119e-02, /* 208 */ + -2.426142284520608e-04, /* 209 */ + }, { + 1.665324149879781e-02, /* 210 */ + -1.016824664690990e-01, /* 211 */ + 4.398321932561375e-01, /* 212 */ + 7.576754447349441e-01, /* 213 */ + -1.422101020601427e-01, /* 214 */ + 2.888568908065016e-02, /* 215 */ + -2.580941998051696e-04, /* 216 */ + }, { + 1.640648276577594e-02, /* 217 */ + -1.006173848382378e-01, /* 218 */ + 4.344677055214644e-01, /* 219 */ + 7.622095841142430e-01, /* 220 */ + -1.422576187983489e-01, /* 221 */ + 2.897263194029685e-02, /* 222 */ + -2.739427028786713e-04, /* 223 */ + }, { + 1.615988617865028e-02, /* 224 */ + -9.954536837955191e-02, /* 225 */ + 4.291068348369969e-01, /* 226 */ + 7.667139225999916e-01, /* 227 */ + -1.422798782463173e-01, /* 228 */ + 2.905402484317260e-02, /* 229 */ + -2.901453704029424e-04, /* 230 */ + }, { + 1.591352012446994e-02, /* 231 */ + -9.846669168335127e-02, /* 232 */ + 4.237500859741424e-01, /* 233 */ + 7.711879723504229e-01, /* 234 */ + -1.422766173293711e-01, /* 235 */ + 2.912977124294393e-02, /* 236 */ + -3.066873428758958e-04, /* 237 */ + }, { + 1.566745203743416e-02, /* 238 */ + -9.738162800684201e-02, /* 239 */ + 4.183979616379481e-01, /* 240 */ + 7.756312481703652e-01, /* 241 */ + -1.422475748215626e-01, /* 242 */ + 2.919977487857369e-02, /* 243 */ + -3.235532722844501e-04, /* 244 */ + }, { + 1.542174839005620e-02, /* 245 */ + -9.629044923613632e-02, /* 246 */ + 4.130509624027673e-01, /* 247 */ + 7.800432675808223e-01, /* 248 */ + -1.421924913979572e-01, /* 249 */ + 2.926393980082415e-02, /* 250 */ + -3.407273260304884e-04, /* 251 */ + }, { + 1.517647468465644e-02, /* 252 */ + -9.519342584872197e-02, /* 253 */ + 4.077095866483865e-01, /* 254 */ + 7.844235508882289e-01, /* 255 */ + -1.421111096868331e-01, /* 256 */ + 2.932217039889637e-02, /* 257 */ + -3.581931910609877e-04, /* 258 */ + }, { + 1.493169544518567e-02, /* 259 */ + -9.409082687639277e-02, /* 260 */ + 4.023743304966226e-01, /* 261 */ + 7.887716212533704e-01, /* 262 */ + -1.420031743217853e-01, /* 263 */ + 2.937437142720069e-02, /* 264 */ + -3.759340782016456e-04, /* 265 */ + }, { + 1.468747420937781e-02, /* 266 */ + -9.298291986864780e-02, /* 267 */ + 3.970456877483993e-01, /* 268 */ + 7.930870047599514e-01, /* 269 */ + -1.418684319937252e-01, /* 270 */ + 2.942044803225294e-02, /* 271 */ + -3.939327266934505e-04, /* 272 */ + }, { + 1.444387352123256e-02, /* 273 */ + -9.186997085656183e-02, /* 274 */ + 3.917241498213130e-01, /* 275 */ + 7.973692304828067e-01, /* 276 */ + -1.417066315027663e-01, /* 277 */ + 2.946030577969092e-02, /* 278 */ + -4.121714089315939e-04, /* 279 */ + }, { + 1.420095492382687e-02, /* 280 */ + -9.075224431713386e-02, /* 281 */ + 3.864102056876984e-01, /* 282 */ + 8.016178305557399e-01, /* 283 */ + -1.415175238099844e-01, /* 284 */ + 2.949385068140553e-02, /* 285 */ + -4.306319354058826e-04, /* 286 */ + }, { + 1.395877895245621e-02, /* 287 */ + -8.963000313811628e-02, /* 288 */ + 3.811043418132007e-01, /* 289 */ + 8.058323402389781e-01, /* 290 */ + -1.413008620890449e-01, /* 291 */ + 2.952098922278122e-02, /* 292 */ + -4.492956598419907e-04, /* 293 */ + }, { + 1.371740512810408e-02, /* 294 */ + -8.850350858333141e-02, /* 295 */ + 3.758070420958670e-01, /* 296 */ + 8.100122979862356e-01, /* 297 */ + -1.410564017776856e-01, /* 298 */ + 2.954162839003991e-02, /* 299 */ + -4.681434845426152e-04, /* 300 */ + }, { + 1.347689195124033e-02, /* 301 */ + -8.737302025847754e-02, /* 302 */ + 3.705187878057628e-01, /* 303 */ + 8.141572455113678e-01, /* 304 */ + -1.407839006290460e-01, /* 305 */ + 2.955567569768274e-02, /* 306 */ + -4.871558659276469e-04, /* 307 */ + }, { + 1.323729689594689e-02, /* 308 */ + -8.623879607743025e-02, /* 309 */ + 3.652400575251253e-01, /* 310 */ + 8.182667278546104e-01, /* 311 */ + -1.404831187628331e-01, /* 312 */ + 2.956303921602418e-02, /* 313 */ + -5.063128202724816e-04, /* 314 */ + }, { + 1.299867640437090e-02, /* 315 */ + -8.510109222904338e-02, /* 316 */ + 3.599713270890607e-01, /* 317 */ + 8.223402934483920e-01, /* 318 */ + -1.401538187163136e-01, /* 319 */ + 2.956362759881255e-02, /* 320 */ + -5.255939296432825e-04, /* 321 */ + }, { + 1.276108588150466e-02, /* 322 */ + -8.396016314445182e-02, /* 323 */ + 3.547130695267950e-01, /* 324 */ + 8.263774941827043e-01, /* 325 */ + -1.397957654951237e-01, /* 326 */ + 2.955735011093100e-02, /* 327 */ + -5.449783480282249e-04, /* 328 */ + }, { + 1.252457969029095e-02, /* 329 */ + -8.281626146488272e-02, /* 330 */ + 3.494657550034873e-01, /* 331 */ + 8.303778854700258e-01, /* 332 */ + -1.394087266238854e-01, /* 333 */ + 2.954411665617339e-02, /* 334 */ + -5.644448076635753e-04, /* 335 */ + }, { + 1.228921114705370e-02, /* 336 */ + -8.166963800997633e-02, /* 337 */ + 3.442298507626138e-01, /* 338 */ + 8.343410263097801e-01, /* 339 */ + -1.389924721966199e-01, /* 340 */ + 2.952383780508903e-02, /* 341 */ + -5.839716255532901e-04, /* 342 */ + }, { + 1.205503251725260e-02, /* 343 */ + -8.052054174662253e-02, /* 344 */ + 3.390058210689310e-01, /* 345 */ + 8.382664793523271e-01, /* 346 */ + -1.385467749269494e-01, /* 347 */ + 2.949642482289040e-02, /* 348 */ + -6.035367101809801e-04, /* 349 */ + }, { + 1.182209501156100e-02, /* 350 */ + -7.936921975831460e-02, /* 351 */ + 3.337941271520264e-01, /* 352 */ + 8.421538109624669e-01, /* 353 */ + -1.380714101980755e-01, /* 354 */ + 2.946178969741759e-02, /* 355 */ + -6.231175684128510e-04, /* 356 */ + }, { + 1.159044878226562e-02, /* 357 */ + -7.821591721502538e-02, /* 358 */ + 3.285952271504655e-01, /* 359 */ + 8.460025912824529e-01, /* 360 */ + -1.375661561125266e-01, /* 361 */ + 2.941984516715388e-02, /* 362 */ + -6.426913125902382e-04, /* 363 */ + }, { + 1.136014291998739e-02, /* 364 */ + -7.706087734360774e-02, /* 365 */ + 3.234095760565429e-01, /* 366 */ + 8.498123942944996e-01, /* 367 */ + -1.370307935416629e-01, /* 368 */ + 2.937050474928606e-02, /* 369 */ + -6.622346678102776e-04, /* 370 */ + }, { + 1.113122545072202e-02, /* 371 */ + -7.590434139872407e-02, /* 372 */ + 3.182376256616457e-01, /* 373 */ + 8.535827978827749e-01, /* 374 */ + -1.364651061749298e-01, /* 375 */ + 2.931368276780342e-02, /* 376 */ + -6.817239793932266e-04, /* 377 */ + }, { + 1.090374333319907e-02, /* 378 */ + -7.474654863430664e-02, /* 379 */ + 3.130798245022357e-01, /* 380 */ + 8.573133838948687e-01, /* 381 */ + -1.358688805688508e-01, /* 382 */ + 2.924929438162952e-02, /* 383 */ + -7.011352205348302e-04, /* 384 */ + }, { + 1.067774245655789e-02, /* 385 */ + -7.358773627555218e-02, /* 386 */ + 3.079366178064618e-01, /* 387 */ + 8.610037382027231e-01, /* 388 */ + -1.352419061957486e-01, /* 389 */ + 2.917725561278015e-02, /* 390 */ + -7.204440001421441e-04, /* 391 */ + }, { + 1.045326763833955e-02, /* 392 */ + -7.242813949145467e-02, /* 393 */ + 3.028084474414053e-01, /* 394 */ + 8.646534507630197e-01, /* 395 */ + -1.345839754921861e-01, /* 396 */ + 2.909748337454149e-02, /* 397 */ + -7.396255708510786e-04, /* 398 */ + }, { + 1.023036262279292e-02, /* 399 */ + -7.126799136787691e-02, /* 400 */ + 2.976957518609703e-01, /* 401 */ + 8.682621156770068e-01, /* 402 */ + -1.338948839071171e-01, /* 403 */ + 2.900989549966232e-02, /* 404 */ + -7.586548372239626e-04, /* 405 */ + }, { + 1.000907007949311e-02, /* 406 */ + -7.010752288116626e-02, /* 407 */ + 2.925989660544231e-01, /* 408 */ + 8.718293312497631e-01, /* 409 */ + -1.331744299497369e-01, /* 410 */ + 2.891441076855387e-02, /* 411 */ + -7.775063641252938e-04, /* 412 */ + }, { + 9.789431602271271e-03, /* 413 */ + -6.894696287231404e-02, /* 414 */ + 2.875185214955909e-01, /* 415 */ + 8.753547000488832e-01, /* 416 */ + -1.324224152370240e-01, /* 417 */ + 2.881094893749082e-02, /* 418 */ + -7.961543852738185e-04, /* 419 */ + }, { + 9.571487708453395e-03, /* 420 */ + -6.778653802166430e-02, /* 421 */ + 2.824548460927230e-01, /* 422 */ + 8.788378289625756e-01, /* 423 */ + -1.316386445409620e-01, /* 424 */ + 2.869943076680737e-02, /* 425 */ + -8.145728119690237e-04, /* 426 */ + }, { + 9.355277838406877e-03, /* 427 */ + -6.662647282417096e-02, /* 428 */ + 2.774083641390264e-01, /* 429 */ + 8.822783292571661e-01, /* 430 */ + -1.308229258354336e-01, /* 431 */ + 2.857977804908198e-02, /* 432 */ + -8.327352419900550e-04, /* 433 */ + }, { + 9.140840355392437e-03, /* 434 */ + -6.546698956520861e-02, /* 435 */ + 2.723794962638790e-01, /* 436 */ + 8.856758166339943e-01, /* 437 */ + -1.299750703427760e-01, /* 438 */ + 2.845191363730427e-02, /* 439 */ + -8.506149686650558e-04, /* 440 */ + }, { + 8.928212545720045e-03, /* 441 */ + -6.430830829693616e-02, /* 442 */ + 2.673686593847286e-01, /* 443 */ + 8.890299112856921e-01, /* 444 */ + -1.290948925799897e-01, /* 445 */ + 2.831576147301751e-02, /* 446 */ + -8.681849901087664e-04, /* 447 */ + }, { + 8.717430619206452e-03, /* 448 */ + -6.315064681521791e-02, /* 449 */ + 2.623762666596838e-01, /* 450 */ + 8.923402379518391e-01, /* 451 */ + -1.281822104045887e-01, /* 452 */ + 2.817124661443064e-02, /* 453 */ + -8.854180186263388e-04, /* 454 */ + }, { + 8.508529709932666e-03, /* 455 */ + -6.199422063710185e-02, /* 456 */ + 2.574027274408040e-01, /* 457 */ + 8.956064259739822e-01, /* 458 */ + -1.272368450600864e-01, /* 459 */ + 2.801829526449300e-02, /* 460 */ + -9.022864902810273e-04, /* 461 */ + }, { + 8.301543877298635e-03, /* 462 */ + -6.083924297885757e-02, /* 463 */ + 2.524484472280946e-01, /* 464 */ + 8.988281093500092e-01, /* 465 */ + -1.262586212211042e-01, /* 466 */ + 2.785683479892532e-02, /* 467 */ + -9.187625746236699e-04, /* 468 */ + }, { + 8.096506107373698e-03, /* 469 */ + -5.968592473457642e-02, /* 470 */ + 2.475138276242130e-01, /* 471 */ + 9.020049267878701e-01, /* 472 */ + -1.252473670380959e-01, /* 473 */ + 2.768679379420070e-02, /* 474 */ + -9.348181845814541e-04, /* 475 */ + }, { + 7.893448314540197e-03, /* 476 */ + -5.853447445533320e-02, /* 477 */ + 2.425992662898926e-01, /* 478 */ + 9.051365217586359e-01, /* 479 */ + -1.242029141816779e-01, /* 480 */ + 2.750810205546858e-02, /* 481 */ + -9.504249865037702e-04, /* 482 */ + }, { + 7.692401343427691e-03, /* 483 */ + -5.738509832891316e-02, /* 484 */ + 2.377051569000891e-01, /* 485 */ + 9.082225425488828e-01, /* 486 */ + -1.231250978865554e-01, /* 487 */ + 2.732069064441559e-02, /* 488 */ + -9.655544103626029e-04, /* 489 */ + }, { + 7.493394971135955e-03, /* 490 */ + -5.623800016010321e-02, /* 491 */ + 2.328318891008579e-01, /* 492 */ + 9.112626423123993e-01, /* 493 */ + -1.220137569950366e-01, /* 494 */ + 2.712449190705641e-02, /* 495 */ + -9.801776601050557e-04, /* 496 */ + }, { + 7.296457909743918e-03, /* 497 */ + -5.509338135155091e-02, /* 498 */ + 2.279798484669641e-01, /* 499 */ + 9.142564791211991e-01, /* 500 */ + -1.208687340001249e-01, /* 501 */ + 2.691943950144821e-02, /* 502 */ + -9.942657241554644e-04, /* 503 */ + }, { + 7.101617809102288e-03, /* 504 */ + -5.395144088518943e-02, /* 505 */ + 2.231494164602341e-01, /* 506 */ + 9.172037160158394e-01, /* 507 */ + -1.196898750881803e-01, /* 508 */ + 2.670546842532187e-02, /* 509 */ + -1.007789386064493e-03, /* 510 */ + }, { + 6.908901259906972e-03, /* 511 */ + -5.281237530423245e-02, /* 512 */ + 2.183409703886530e-01, /* 513 */ + 9.201040210550299e-01, /* 514 */ + -1.184770301811412e-01, /* 515 */ + 2.648251504362370e-02, /* 516 */ + -1.020719235302619e-03, /* 517 */ + }, { + 6.718333797051150e-03, /* 518 */ + -5.167637869573667e-02, /* 519 */ + 2.135548833662130e-01, /* 520 */ + 9.229570673645258e-01, /* 521 */ + -1.172300529782974e-01, /* 522 */ + 2.625051711596052e-02, /* 523 */ + -1.033025678195301e-03, /* 524 */ + }, { + 6.529939903253072e-03, /* 525 */ + -5.054364267373540e-02, /* 526 */ + 2.087915242735169e-01, /* 527 */ + 9.257625331852983e-01, /* 528 */ + -1.159488009976052e-01, /* 529 */ + 2.600941382394202e-02, /* 530 */ + -1.044678948997064e-03, /* 531 */ + }, { + 6.343743012956645e-03, /* 532 */ + -4.941435636294156e-02, /* 533 */ + 2.040512577191446e-01, /* 534 */ + 9.285201019209733e-01, /* 535 */ + -1.146331356165368e-01, /* 536 */ + 2.575914579841332e-02, /* 537 */ + -1.055649121101780e-03, /* 538 */ + }, { + 6.159765516502505e-03, /* 539 */ + -4.828870638302148e-02, /* 540 */ + 1.993344440017834e-01, /* 541 */ + 9.312294621845263e-01, /* 542 */ + -1.132829221124536e-01, /* 543 */ + 2.549965514657118e-02, /* 544 */ + -1.065906118386268e-03, /* 545 */ + }, { + 5.978028764566341e-03, /* 546 */ + -4.716687683344033e-02, /* 547 */ + 1.946414390731309e-01, /* 548 */ + 9.338903078442332e-01, /* 549 */ + -1.118980297024964e-01, /* 550 */ + 2.523088547895752e-02, /* 551 */ + -1.075419726684316e-03, /* 552 */ + }, { + 5.798553072861818e-03, /* 553 */ + -4.604904927887769e-02, /* 554 */ + 1.899725945015709e-01, /* 555 */ + 9.365023380688623e-01, /* 556 */ + -1.104783315829824e-01, /* 557 */ + 2.495278193632289e-02, /* 558 */ + -1.084159605388224e-03, /* 559 */ + }, { + 5.621357727104864e-03, /* 560 */ + -4.493540273521546e-02, /* 561 */ + 1.853282574366316e-01, /* 562 */ + 9.390652573721028e-01, /* 563 */ + -1.090237049683018e-01, /* 564 */ + 2.466529121635405e-02, /* 565 */ + -1.092095299174878e-03, /* 566 */ + }, { + 5.446460988236670e-03, /* 567 */ + -4.382611365609584e-02, /* 568 */ + 1.807087705742239e-01, /* 569 */ + 9.415787756562223e-01, /* 570 */ + -1.075340311293039e-01, /* 571 */ + 2.436836160025825e-02, /* 572 */ + -1.099196249853310e-03, /* 573 */ + }, { + 5.273880097902076e-03, /* 574 */ + -4.272135592005138e-02, /* 575 */ + 1.761144721226713e-01, /* 576 */ + 9.440426082549445e-01, /* 577 */ + -1.060091954311663e-01, /* 578 */ + 2.406194297919816e-02, /* 579 */ + -1.105431808330667e-03, /* 580 */ + }, { + 5.103631284180478e-03, /* 581 */ + -4.162130081820462e-02, /* 582 */ + 1.715456957695286e-01, /* 583 */ + 9.464564759755407e-01, /* 584 */ + -1.044490873707362e-01, /* 585 */ + 2.374598688057016e-02, /* 586 */ + -1.110771246693530e-03, /* 587 */ + }, { + 4.935729767565705e-03, /* 588 */ + -4.052611704253924e-02, /* 589 */ + 1.670027706491982e-01, /* 590 */ + 9.488201051401287e-01, /* 591 */ + -1.028536006133390e-01, /* 592 */ + 2.342044649412001e-02, /* 593 */ + -1.115183770401296e-03, /* 594 */ + }, { + 4.770189767192131e-03, /* 595 */ + -3.943597067473980e-02, /* 596 */ + 1.624860213113444e-01, /* 597 */ + 9.511332276261677e-01, /* 598 */ + -1.012226330290420e-01, /* 599 */ + 2.308527669788855e-02, /* 600 */ + -1.118638530588553e-03, /* 601 */ + }, { + 4.607024507303565e-03, /* 602 */ + -3.835102517560170e-02, /* 603 */ + 1.579957676901108e-01, /* 604 */ + 9.533955809061508e-01, /* 605 */ + -9.955608672836883e-02, /* 606 */ + 2.274043408398138e-02, /* 607 */ + -1.121104636473070e-03, /* 608 */ + }, { + 4.446246223961389e-03, /* 609 */ + -3.727144137500895e-02, /* 610 */ + 1.535323250741442e-01, /* 611 */ + 9.556069080864779e-01, /* 612 */ + -9.785386809745342e-02, /* 613 */ + 2.238587698415551e-02, /* 614 */ + -1.122551167866203e-03, /* 615 */ + }, { + 4.287866171989037e-03, /* 616 */ + -3.619737746247935e-02, /* 617 */ + 1.490960040774270e-01, /* 618 */ + 9.577669579455091e-01, /* 619 */ + -9.611588783262809e-02, /* 620 */ + 2.202156549521620e-02, /* 621 */ + -1.122947187782295e-03, /* 622 */ + }, { + 4.131894632149082e-03, /* 623 */ + -3.512898897827717e-02, /* 624 */ + 1.446871106109222e-01, /* 625 */ + 9.598754849707929e-01, /* 626 */ + -9.434206097443590e-02, /* 627 */ + 2.164746150421816e-02, /* 628 */ + -1.122261755143747e-03, /* 629 */ + }, { + 3.978340918549750e-03, /* 630 */ + -3.406642880509015e-02, /* 631 */ + 1.403059458550339e-01, /* 632 */ + 9.619322493954562e-01, /* 633 */ + -9.253230694106100e-02, /* 634 */ + 2.126352871346326e-02, /* 635 */ + -1.120463937578314e-03, /* 636 */ + }, { + 3.827213386276986e-03, /* 637 */ + -3.300984716027202e-02, /* 638 */ + 1.359528062328858e-01, /* 639 */ + 9.639370172337561e-01, /* 640 */ + -9.068654956116820e-02, /* 641 */ + 2.086973266528943e-02, /* 642 */ + -1.117522824305172e-03, /* 643 */ + }, { + 3.678519439249081e-03, /* 644 */ + -3.195939158864695e-02, /* 645 */ + 1.316279833844193e-01, /* 646 */ + 9.658895603157857e-01, /* 647 */ + -8.880471710614442e-02, /* 648 */ + 2.046604076664311e-02, /* 649 */ + -1.113407539106230e-03, /* 650 */ + }, { + 3.532265538289867e-03, /* 651 */ + -3.091520695587671e-02, /* 652 */ + 1.273317641413156e-01, /* 653 */ + 9.677896563213259e-01, /* 654 */ + -8.688674232173496e-02, /* 655 */ + 2.005242231342951e-02, /* 656 */ + -1.108087253379183e-03, /* 657 */ + }, { + 3.388457209417186e-03, /* 658 */ + -2.987743544238687e-02, /* 659 */ + 1.230644305027427e-01, /* 660 */ + 9.696370888128401e-01, /* 661 */ + -8.493256245906648e-02, /* 662 */ + 1.962884851463336e-02, /* 663 */ + -1.101531199268716e-03, /* 664 */ + }, { + 3.247099052342608e-03, /* 665 */ + -2.884621653785256e-02, /* 666 */ + 1.188262596119302e-01, /* 667 */ + 9.714316472676046e-01, /* 668 */ + -8.294211930504999e-02, /* 669 */ + 1.919529251620448e-02, /* 670 */ + -1.093708682872210e-03, /* 671 */ + }, { + 3.108194749179169e-03, /* 672 */ + -2.782168703623990e-02, /* 673 */ + 1.146175237335723e-01, /* 674 */ + 9.731731271089683e-01, /* 675 */ + -8.091535921215573e-02, /* 676 */ + 1.875172942470069e-02, /* 677 */ + -1.084589097516370e-03, /* 678 */ + }, { + 2.971747073353185e-03, /* 679 */ + -2.680398103140338e-02, /* 680 */ + 1.104384902320638e-01, /* 681 */ + 9.748613297367402e-01, /* 682 */ + -7.885223312755388e-02, /* 683 */ + 1.829813633068257e-02, /* 684 */ + -1.074141937101013e-03, /* 685 */ + }, { + 2.837757898716223e-03, /* 686 */ + -2.579322991323553e-02, /* 687 */ + 1.062894215505674e-01, /* 688 */ + 9.764960625566944e-01, /* 689 */ + -7.675269662161305e-02, /* 690 */ + 1.783449233185286e-02, /* 691 */ + -1.062336809506350e-03, /* 692 */ + }, { + 2.706228208853840e-03, /* 693 */ + -2.478956236436741e-02, /* 694 */ + 1.021705751909168e-01, /* 695 */ + 9.780771390091908e-01, /* 696 */ + -7.461670991575037e-02, /* 697 */ + 1.736077855593420e-02, /* 698 */ + -1.049143450059986e-03, /* 699 */ + }, { + 2.577158106586916e-03, /* 700 */ + -2.379310435741876e-02, /* 701 */ + 9.808220369435408e-02, /* 702 */ + 9.796043785969055e-01, /* 703 */ + -7.244423790962552e-02, /* 704 */ + 1.687697818327941e-02, /* 705 */ + -1.034531735059858e-03, /* 706 */ + }, { + 2.450546823661987e-03, /* 707 */ + -2.280397915279337e-02, /* 708 */ + 9.402455462310588e-02, /* 709 */ + 9.810776069116667e-01, /* 710 */ + -7.023525020767289e-02, /* 711 */ + 1.638307646920682e-02, /* 712 */ + -1.018471695349289e-03, /* 713 */ + }, { + 2.326392730626404e-03, /* 714 */ + -2.182230729701992e-02, /* 715 */ + 8.999787054279679e-02, /* 716 */ + 9.824966556603907e-01, /* 717 */ + -6.798972114496460e-02, /* 718 */ + 1.587906076605559e-02, /* 719 */ + -1.000933529940309e-03, /* 720 */ + }, { + 2.204693346884742e-03, /* 721 */ + -2.084820662163316e-02, /* 722 */ + 8.600238900570112e-02, /* 723 */ + 9.838613626901140e-01, /* 724 */ + -6.570762981239750e-02, /* 725 */ + 1.536492054495343e-02, /* 726 */ + -9.818876196813738e-04, /* 727 */ + }, { + 2.085445350932248e-03, /* 728 */ + -1.988179224259554e-02, /* 729 */ + 8.203834253483586e-02, /* 730 */ + 9.851715720121177e-01, /* 731 */ + -6.338896008119892e-02, /* 732 */ + 1.484064741729164e-02, /* 733 */ + -9.613045409655687e-04, /* 734 */ + }, { + 1.968644590761548e-03, /* 735 */ + -1.892317656025402e-02, /* 736 */ + 7.810595860889319e-02, /* 737 */ + 9.864271338251378e-01, /* 738 */ + -6.103370062674356e-02, /* 739 */ + 1.430623515590003e-02, /* 740 */ + -9.391550794753652e-04, /* 741 */ + }, { + 1.854286094438386e-03, /* 742 */ + -1.797246925983164e-02, /* 743 */ + 7.420545964801417e-02, /* 744 */ + 9.876279045376605e-01, /* 745 */ + -5.864184495167603e-02, /* 746 */ + 1.376167971591675e-02, /* 747 */ + -9.154102439599487e-04, /* 748 */ + }, { + 1.742364080842615e-03, /* 749 */ + -1.702977731244917e-02, /* 750 */ + 7.033706300040359e-02, /* 751 */ + 9.887737467892981e-01, /* 752 */ + -5.621339140833288e-02, /* 753 */ + 1.320697925534576e-02, /* 754 */ + -8.900412800411573e-04, /* 755 */ + }, { + 1.632871970570320e-03, /* 756 */ + -1.609520497667476e-02, /* 757 */ + 6.650098092978608e-02, /* 758 */ + 9.898645294712379e-01, /* 759 */ + -5.374834322045772e-02, /* 760 */ + 1.264213415529629e-02, /* 761 */ + -8.630196840440353e-04, /* 762 */ + }, { + 1.525802396992804e-03, /* 763 */ + -1.516885380059888e-02, /* 764 */ + 6.269742060370384e-02, /* 765 */ + 9.909001277457663e-01, /* 766 */ + -5.124670850420399e-02, /* 767 */ + 1.206714703989862e-02, /* 768 */ + -8.343172168478687e-04, /* 769 */ + }, { + 1.421147217468663e-03, /* 770 */ + -1.425082262442998e-02, /* 771 */ + 5.892658408265536e-02, /* 772 */ + 9.918804230648608e-01, /* 773 */ + -4.870850028841950e-02, /* 774 */ + 1.148202279588897e-02, /* 775 */ + -8.039059177537709e-04, /* 776 */ + }, { + 1.318897524704578e-03, /* 777 */ + -1.334120758360962e-02, /* 778 */ + 5.518866831007525e-02, /* 779 */ + 9.928053031878483e-01, /* 780 */ + -4.613373653420693e-02, /* 781 */ + 1.088676859185900e-02, /* 782 */ + -7.717581183646440e-04, /* 783 */ + }, { + 1.219043658260877e-03, /* 784 */ + -1.244010211244157e-02, /* 785 */ + 5.148386510315437e-02, /* 786 */ + 9.936746621981263e-01, /* 787 */ + -4.352244015375482e-02, /* 788 */ + 1.028139389716235e-02, /* 789 */ + -7.378464564734607e-04, /* 790 */ + }, { + 1.121575216197463e-03, /* 791 */ + -1.154759694823357e-02, /* 792 */ + 4.781236114450060e-02, /* 793 */ + 9.944884005189432e-01, /* 794 */ + -4.087463902843420e-02, /* 795 */ + 9.665910500473909e-03, /* 796 */ + -7.021438899556850e-04, /* 797 */ + }, { + 1.026481066856231e-03, /* 798 */ + -1.066378013594614e-02, /* 799 */ + 4.417433797463730e-02, /* 800 */ + 9.952464249282380e-01, /* 801 */ + -3.819036602615411e-02, /* 802 */ + 9.040332527994370e-03, /* 803 */ + -6.646237106617679e-04, /* 804 */ + }, { + 9.337493607755644e-04, /* 805 */ + -9.788737033346933e-03, /* 806 */ + 4.056997198534163e-02, /* 807 */ + 9.959486485725322e-01, /* 808 */ + -3.546965901797275e-02, /* 809 */ + 8.404676461295810e-03, /* 810 */ + -6.252595583054893e-04, /* 811 */ + }, { + 8.433675427328470e-04, /* 812 */ + -8.922550316664762e-03, /* 813 */ + 3.699943441381924e-02, /* 814 */ + 9.965949909798753e-01, /* 815 */ + -3.271256089395777e-02, /* 816 */ + 7.758961154800972e-03, /* 817 */ + -5.840254343440151e-04, /* 818 */ + }, { + 7.553223639105668e-04, /* 819 */ + -8.065299986741607e-03, /* 820 */ + 3.346289133771520e-02, /* 821 */ + 9.971853780718406e-01, /* 822 */ + -2.991911957829145e-02, /* 823 */ + 7.103207852892033e-03, /* 824 */ + -5.408957158454127e-04, /* 825 */ + }, { + 6.695998941820378e-04, /* 826 */ + -7.217063375677063e-03, /* 827 */ + 2.996050367095970e-02, /* 828 */ + 9.977197421745680e-01, /* 829 */ + -2.708938804361575e-02, /* 830 */ + 6.437440206642007e-03, /* 831 */ + -4.958451693394876e-04, /* 832 */ + }, { + 5.861855345123588e-04, /* 833 */ + -6.377915153961920e-03, /* 834 */ + 2.649242716044695e-02, /* 835 */ + 9.981980220288543e-01, /* 836 */ + -2.422342432461263e-02, /* 837 */ + 5.761684290163624e-03, /* 838 */ + -4.488489646476928e-04, /* 839 */ + }, { + 5.050640294702417e-04, /* 840 */ + -5.547927338097411e-03, /* 841 */ + 2.305881238354565e-02, /* 842 */ + 9.986201627992864e-01, /* 843 */ + -2.132129153081513e-02, /* 844 */ + 5.075968616570653e-03, /* 845 */ + -3.998826886878056e-04, /* 846 */ + }, { + 4.262194798466813e-04, /* 847 */ + -4.727169298694500e-03, /* 848 */ + 1.965980474643937e-02, /* 849 */ + 9.989861160824184e-01, /* 850 */ + -1.838305785864478e-02, /* 851 */ + 4.380324153544939e-03, /* 852 */ + -3.489223592492433e-04, /* 853 */ + }, { + 3.496353553759901e-04, /* 854 */ + -3.915707769050846e-03, /* 855 */ + 1.629554448329466e-02, /* 856 */ + 9.992958399139888e-01, /* 857 */ + -1.540879660267084e-02, /* 858 */ + 3.674784338505265e-03, /* 859 */ + -2.959444387346577e-04, /* 860 */ + }, { + 2.752945075550529e-04, /* 861 */ + -3.113606854199215e-03, /* 862 */ + 1.296616665625557e-02, /* 863 */ + 9.995492987751797e-01, /* 864 */ + -1.239858616608813e-02, /* 865 */ + 2.959385093371052e-03, /* 866 */ + -2.409258478636185e-04, /* 867 */ + }, { + 2.031791825563283e-04, /* 868 */ + -2.320928040424877e-03, /* 869 */ + 9.671801156260738e-03, /* 870 */ + 9.997464635979135e-01, /* 871 */ + -9.352510070407542e-03, /* 872 */ + 2.234164838917225e-03, /* 873 */ + -1.838439793339892e-04, /* 874 */ + }, { + 1.332710342305255e-04, /* 875 */ + -1.537730205245651e-03, /* 876 */ + 6.412572704682764e-03, /* 877 */ + 9.998873117691878e-01, /* 878 */ + -6.270656964357692e-03, /* 879 */ + 1.499164508713334e-03, /* 880 */ + -1.246767114368607e-04, /* 881 */ + }, { + 6.555113719447324e-05, /* 882 */ + -7.640696278518945e-04, /* 883 */ + 3.188600855785918e-03, /* 884 */ + 9.999718271344488e-01, /* 885 */ + -3.153120631992235e-03, /* 886 */ + 7.544275626434484e-04, /* 887 */ + -6.340242162062585e-05, /* 888 */ + }, { + 1.930201848426478e-18, /* 889 */ + -1.515373497812483e-17, /* 890 */ + 3.164321107994089e-17, /* 891 */ + 1.000000000000000e+00, /* 892 */ + 3.164321107994089e-17, /* 893 */ + -1.515373497812483e-17, /* 894 */ + 1.930201848426478e-18, /* 895 */ + }, { + -6.340242162062585e-05, /* 896 */ + 7.544275626434484e-04, /* 897 */ + -3.153120631992235e-03, /* 898 */ + 9.999718271344488e-01, /* 899 */ + 3.188600855785918e-03, /* 900 */ + -7.640696278518945e-04, /* 901 */ + 6.555113719447324e-05, /* 902 */ + }, { + -1.246767114368607e-04, /* 903 */ + 1.499164508713334e-03, /* 904 */ + -6.270656964357692e-03, /* 905 */ + 9.998873117691878e-01, /* 906 */ + 6.412572704682764e-03, /* 907 */ + -1.537730205245651e-03, /* 908 */ + 1.332710342305255e-04, /* 909 */ + }, { + -1.838439793339892e-04, /* 910 */ + 2.234164838917225e-03, /* 911 */ + -9.352510070407542e-03, /* 912 */ + 9.997464635979135e-01, /* 913 */ + 9.671801156260738e-03, /* 914 */ + -2.320928040424877e-03, /* 915 */ + 2.031791825563283e-04, /* 916 */ + }, { + -2.409258478636185e-04, /* 917 */ + 2.959385093371052e-03, /* 918 */ + -1.239858616608813e-02, /* 919 */ + 9.995492987751797e-01, /* 920 */ + 1.296616665625557e-02, /* 921 */ + -3.113606854199215e-03, /* 922 */ + 2.752945075550529e-04, /* 923 */ + }, { + -2.959444387346577e-04, /* 924 */ + 3.674784338505265e-03, /* 925 */ + -1.540879660267084e-02, /* 926 */ + 9.992958399139888e-01, /* 927 */ + 1.629554448329466e-02, /* 928 */ + -3.915707769050846e-03, /* 929 */ + 3.496353553759901e-04, /* 930 */ + }, { + -3.489223592492433e-04, /* 931 */ + 4.380324153544939e-03, /* 932 */ + -1.838305785864478e-02, /* 933 */ + 9.989861160824184e-01, /* 934 */ + 1.965980474643937e-02, /* 935 */ + -4.727169298694500e-03, /* 936 */ + 4.262194798466813e-04, /* 937 */ + }, { + -3.998826886878056e-04, /* 938 */ + 5.075968616570653e-03, /* 939 */ + -2.132129153081513e-02, /* 940 */ + 9.986201627992864e-01, /* 941 */ + 2.305881238354565e-02, /* 942 */ + -5.547927338097411e-03, /* 943 */ + 5.050640294702417e-04, /* 944 */ + }, { + -4.488489646476928e-04, /* 945 */ + 5.761684290163624e-03, /* 946 */ + -2.422342432461263e-02, /* 947 */ + 9.981980220288543e-01, /* 948 */ + 2.649242716044695e-02, /* 949 */ + -6.377915153961920e-03, /* 950 */ + 5.861855345123588e-04, /* 951 */ + }, { + -4.958451693394876e-04, /* 952 */ + 6.437440206642007e-03, /* 953 */ + -2.708938804361575e-02, /* 954 */ + 9.977197421745680e-01, /* 955 */ + 2.996050367095970e-02, /* 956 */ + -7.217063375677063e-03, /* 957 */ + 6.695998941820378e-04, /* 958 */ + }, { + -5.408957158454127e-04, /* 959 */ + 7.103207852892033e-03, /* 960 */ + -2.991911957829145e-02, /* 961 */ + 9.971853780718406e-01, /* 962 */ + 3.346289133771520e-02, /* 963 */ + -8.065299986741607e-03, /* 964 */ + 7.553223639105668e-04, /* 965 */ + }, { + -5.840254343440151e-04, /* 966 */ + 7.758961154800972e-03, /* 967 */ + -3.271256089395777e-02, /* 968 */ + 9.965949909798753e-01, /* 969 */ + 3.699943441381924e-02, /* 970 */ + -8.922550316664762e-03, /* 971 */ + 8.433675427328470e-04, /* 972 */ + }, { + -6.252595583054893e-04, /* 973 */ + 8.404676461295810e-03, /* 974 */ + -3.546965901797275e-02, /* 975 */ + 9.959486485725322e-01, /* 976 */ + 4.056997198534163e-02, /* 977 */ + -9.788737033346933e-03, /* 978 */ + 9.337493607755644e-04, /* 979 */ + }, { + -6.646237106617679e-04, /* 980 */ + 9.040332527994370e-03, /* 981 */ + -3.819036602615411e-02, /* 982 */ + 9.952464249282380e-01, /* 983 */ + 4.417433797463730e-02, /* 984 */ + -1.066378013594614e-02, /* 985 */ + 1.026481066856231e-03, /* 986 */ + }, { + -7.021438899556850e-04, /* 987 */ + 9.665910500473909e-03, /* 988 */ + -4.087463902843420e-02, /* 989 */ + 9.944884005189432e-01, /* 990 */ + 4.781236114450060e-02, /* 991 */ + -1.154759694823357e-02, /* 992 */ + 1.121575216197463e-03, /* 993 */ + }, { + -7.378464564734607e-04, /* 994 */ + 1.028139389716235e-02, /* 995 */ + -4.352244015375482e-02, /* 996 */ + 9.936746621981263e-01, /* 997 */ + 5.148386510315437e-02, /* 998 */ + -1.244010211244157e-02, /* 999 */ + 1.219043658260877e-03, /* 1000 */ + }, { + -7.717581183646440e-04, /* 1001 */ + 1.088676859185900e-02, /* 1002 */ + -4.613373653420693e-02, /* 1003 */ + 9.928053031878483e-01, /* 1004 */ + 5.518866831007525e-02, /* 1005 */ + -1.334120758360962e-02, /* 1006 */ + 1.318897524704578e-03, /* 1007 */ + }, { + -8.039059177537709e-04, /* 1008 */ + 1.148202279588897e-02, /* 1009 */ + -4.870850028841950e-02, /* 1010 */ + 9.918804230648608e-01, /* 1011 */ + 5.892658408265536e-02, /* 1012 */ + -1.425082262442998e-02, /* 1013 */ + 1.421147217468663e-03, /* 1014 */ + }, { + -8.343172168478687e-04, /* 1015 */ + 1.206714703989862e-02, /* 1016 */ + -5.124670850420399e-02, /* 1017 */ + 9.909001277457663e-01, /* 1018 */ + 6.269742060370384e-02, /* 1019 */ + -1.516885380059888e-02, /* 1020 */ + 1.525802396992804e-03, /* 1021 */ + }, { + -8.630196840440353e-04, /* 1022 */ + 1.264213415529629e-02, /* 1023 */ + -5.374834322045772e-02, /* 1024 */ + 9.898645294712379e-01, /* 1025 */ + 6.650098092978608e-02, /* 1026 */ + -1.609520497667476e-02, /* 1027 */ + 1.632871970570320e-03, /* 1028 */ + }, { + -8.900412800411573e-04, /* 1029 */ + 1.320697925534576e-02, /* 1030 */ + -5.621339140833288e-02, /* 1031 */ + 9.887737467892981e-01, /* 1032 */ + 7.033706300040359e-02, /* 1033 */ + -1.702977731244917e-02, /* 1034 */ + 1.742364080842615e-03, /* 1035 */ + }, { + -9.154102439599487e-04, /* 1036 */ + 1.376167971591675e-02, /* 1037 */ + -5.864184495167603e-02, /* 1038 */ + 9.876279045376605e-01, /* 1039 */ + 7.420545964801417e-02, /* 1040 */ + -1.797246925983164e-02, /* 1041 */ + 1.854286094438386e-03, /* 1042 */ + }, { + -9.391550794753652e-04, /* 1043 */ + 1.430623515590003e-02, /* 1044 */ + -6.103370062674356e-02, /* 1045 */ + 9.864271338251378e-01, /* 1046 */ + 7.810595860889319e-02, /* 1047 */ + -1.892317656025402e-02, /* 1048 */ + 1.968644590761548e-03, /* 1049 */ + }, { + -9.613045409655687e-04, /* 1050 */ + 1.484064741729164e-02, /* 1051 */ + -6.338896008119892e-02, /* 1052 */ + 9.851715720121177e-01, /* 1053 */ + 8.203834253483586e-02, /* 1054 */ + -1.988179224259554e-02, /* 1055 */ + 2.085445350932248e-03, /* 1056 */ + }, { + -9.818876196813738e-04, /* 1057 */ + 1.536492054495343e-02, /* 1058 */ + -6.570762981239750e-02, /* 1059 */ + 9.838613626901140e-01, /* 1060 */ + 8.600238900570112e-02, /* 1061 */ + -2.084820662163316e-02, /* 1062 */ + 2.204693346884742e-03, /* 1063 */ + }, { + -1.000933529940309e-03, /* 1064 */ + 1.587906076605559e-02, /* 1065 */ + -6.798972114496460e-02, /* 1066 */ + 9.824966556603907e-01, /* 1067 */ + 8.999787054279679e-02, /* 1068 */ + -2.182230729701992e-02, /* 1069 */ + 2.326392730626404e-03, /* 1070 */ + }, { + -1.018471695349289e-03, /* 1071 */ + 1.638307646920682e-02, /* 1072 */ + -7.023525020767289e-02, /* 1073 */ + 9.810776069116667e-01, /* 1074 */ + 9.402455462310588e-02, /* 1075 */ + -2.280397915279337e-02, /* 1076 */ + 2.450546823661987e-03, /* 1077 */ + }, { + -1.034531735059858e-03, /* 1078 */ + 1.687697818327941e-02, /* 1079 */ + -7.244423790962552e-02, /* 1080 */ + 9.796043785969055e-01, /* 1081 */ + 9.808220369435408e-02, /* 1082 */ + -2.379310435741876e-02, /* 1083 */ + 2.577158106586916e-03, /* 1084 */ + }, { + -1.049143450059986e-03, /* 1085 */ + 1.736077855593420e-02, /* 1086 */ + -7.461670991575037e-02, /* 1087 */ + 9.780771390091908e-01, /* 1088 */ + 1.021705751909168e-01, /* 1089 */ + -2.478956236436741e-02, /* 1090 */ + 2.706228208853840e-03, /* 1091 */ + }, { + -1.062336809506350e-03, /* 1092 */ + 1.783449233185286e-02, /* 1093 */ + -7.675269662161305e-02, /* 1094 */ + 9.764960625566944e-01, /* 1095 */ + 1.062894215505674e-01, /* 1096 */ + -2.579322991323553e-02, /* 1097 */ + 2.837757898716223e-03, /* 1098 */ + }, { + -1.074141937101013e-03, /* 1099 */ + 1.829813633068257e-02, /* 1100 */ + -7.885223312755388e-02, /* 1101 */ + 9.748613297367402e-01, /* 1102 */ + 1.104384902320638e-01, /* 1103 */ + -2.680398103140338e-02, /* 1104 */ + 2.971747073353185e-03, /* 1105 */ + }, { + -1.084589097516370e-03, /* 1106 */ + 1.875172942470069e-02, /* 1107 */ + -8.091535921215573e-02, /* 1108 */ + 9.731731271089683e-01, /* 1109 */ + 1.146175237335723e-01, /* 1110 */ + -2.782168703623990e-02, /* 1111 */ + 3.108194749179169e-03, /* 1112 */ + }, { + -1.093708682872210e-03, /* 1113 */ + 1.919529251620448e-02, /* 1114 */ + -8.294211930504999e-02, /* 1115 */ + 9.714316472676046e-01, /* 1116 */ + 1.188262596119302e-01, /* 1117 */ + -2.884621653785256e-02, /* 1118 */ + 3.247099052342608e-03, /* 1119 */ + }, { + -1.101531199268716e-03, /* 1120 */ + 1.962884851463336e-02, /* 1121 */ + -8.493256245906648e-02, /* 1122 */ + 9.696370888128401e-01, /* 1123 */ + 1.230644305027427e-01, /* 1124 */ + -2.987743544238687e-02, /* 1125 */ + 3.388457209417186e-03, /* 1126 */ + }, { + -1.108087253379183e-03, /* 1127 */ + 2.005242231342951e-02, /* 1128 */ + -8.688674232173496e-02, /* 1129 */ + 9.677896563213259e-01, /* 1130 */ + 1.273317641413156e-01, /* 1131 */ + -3.091520695587671e-02, /* 1132 */ + 3.532265538289867e-03, /* 1133 */ + }, { + -1.113407539106230e-03, /* 1134 */ + 2.046604076664311e-02, /* 1135 */ + -8.880471710614442e-02, /* 1136 */ + 9.658895603157857e-01, /* 1137 */ + 1.316279833844193e-01, /* 1138 */ + -3.195939158864695e-02, /* 1139 */ + 3.678519439249081e-03, /* 1140 */ + }, { + -1.117522824305172e-03, /* 1141 */ + 2.086973266528943e-02, /* 1142 */ + -9.068654956116820e-02, /* 1143 */ + 9.639370172337561e-01, /* 1144 */ + 1.359528062328858e-01, /* 1145 */ + -3.300984716027202e-02, /* 1146 */ + 3.827213386276986e-03, /* 1147 */ + }, { + -1.120463937578314e-03, /* 1148 */ + 2.126352871346326e-02, /* 1149 */ + -9.253230694106100e-02, /* 1150 */ + 9.619322493954562e-01, /* 1151 */ + 1.403059458550339e-01, /* 1152 */ + -3.406642880509015e-02, /* 1153 */ + 3.978340918549750e-03, /* 1154 */ + }, { + -1.122261755143747e-03, /* 1155 */ + 2.164746150421816e-02, /* 1156 */ + -9.434206097443590e-02, /* 1157 */ + 9.598754849707929e-01, /* 1158 */ + 1.446871106109222e-01, /* 1159 */ + -3.512898897827717e-02, /* 1160 */ + 4.131894632149082e-03, /* 1161 */ + }, { + -1.122947187782295e-03, /* 1162 */ + 2.202156549521620e-02, /* 1163 */ + -9.611588783262809e-02, /* 1164 */ + 9.577669579455091e-01, /* 1165 */ + 1.490960040774270e-01, /* 1166 */ + -3.619737746247935e-02, /* 1167 */ + 4.287866171989037e-03, /* 1168 */ + }, { + -1.122551167866203e-03, /* 1169 */ + 2.238587698415551e-02, /* 1170 */ + -9.785386809745342e-02, /* 1171 */ + 9.556069080864779e-01, /* 1172 */ + 1.535323250741442e-01, /* 1173 */ + -3.727144137500895e-02, /* 1174 */ + 4.446246223961389e-03, /* 1175 */ + }, { + -1.121104636473070e-03, /* 1176 */ + 2.274043408398138e-02, /* 1177 */ + -9.955608672836883e-02, /* 1178 */ + 9.533955809061508e-01, /* 1179 */ + 1.579957676901108e-01, /* 1180 */ + -3.835102517560170e-02, /* 1181 */ + 4.607024507303565e-03, /* 1182 */ + }, { + -1.118638530588553e-03, /* 1183 */ + 2.308527669788855e-02, /* 1184 */ + -1.012226330290420e-01, /* 1185 */ + 9.511332276261677e-01, /* 1186 */ + 1.624860213113444e-01, /* 1187 */ + -3.943597067473980e-02, /* 1188 */ + 4.770189767192131e-03, /* 1189 */ + }, { + -1.115183770401296e-03, /* 1190 */ + 2.342044649412001e-02, /* 1191 */ + -1.028536006133390e-01, /* 1192 */ + 9.488201051401287e-01, /* 1193 */ + 1.670027706491982e-01, /* 1194 */ + -4.052611704253924e-02, /* 1195 */ + 4.935729767565705e-03, /* 1196 */ + }, { + -1.110771246693530e-03, /* 1197 */ + 2.374598688057016e-02, /* 1198 */ + -1.044490873707362e-01, /* 1199 */ + 9.464564759755407e-01, /* 1200 */ + 1.715456957695286e-01, /* 1201 */ + -4.162130081820462e-02, /* 1202 */ + 5.103631284180478e-03, /* 1203 */ + }, { + -1.105431808330667e-03, /* 1204 */ + 2.406194297919816e-02, /* 1205 */ + -1.060091954311663e-01, /* 1206 */ + 9.440426082549445e-01, /* 1207 */ + 1.761144721226713e-01, /* 1208 */ + -4.272135592005138e-02, /* 1209 */ + 5.273880097902076e-03, /* 1210 */ + }, { + -1.099196249853310e-03, /* 1211 */ + 2.436836160025825e-02, /* 1212 */ + -1.075340311293039e-01, /* 1213 */ + 9.415787756562223e-01, /* 1214 */ + 1.807087705742239e-01, /* 1215 */ + -4.382611365609584e-02, /* 1216 */ + 5.446460988236670e-03, /* 1217 */ + }, { + -1.092095299174878e-03, /* 1218 */ + 2.466529121635405e-02, /* 1219 */ + -1.090237049683018e-01, /* 1220 */ + 9.390652573721028e-01, /* 1221 */ + 1.853282574366316e-01, /* 1222 */ + -4.493540273521546e-02, /* 1223 */ + 5.621357727104864e-03, /* 1224 */ + }, { + -1.084159605388224e-03, /* 1225 */ + 2.495278193632289e-02, /* 1226 */ + -1.104783315829824e-01, /* 1227 */ + 9.365023380688623e-01, /* 1228 */ + 1.899725945015709e-01, /* 1229 */ + -4.604904927887769e-02, /* 1230 */ + 5.798553072861818e-03, /* 1231 */ + }, { + -1.075419726684316e-03, /* 1232 */ + 2.523088547895752e-02, /* 1233 */ + -1.118980297024964e-01, /* 1234 */ + 9.338903078442332e-01, /* 1235 */ + 1.946414390731309e-01, /* 1236 */ + -4.716687683344033e-02, /* 1237 */ + 5.978028764566341e-03, /* 1238 */ + }, { + -1.065906118386268e-03, /* 1239 */ + 2.549965514657118e-02, /* 1240 */ + -1.132829221124536e-01, /* 1241 */ + 9.312294621845263e-01, /* 1242 */ + 1.993344440017834e-01, /* 1243 */ + -4.828870638302148e-02, /* 1244 */ + 6.159765516502505e-03, /* 1245 */ + }, { + -1.055649121101780e-03, /* 1246 */ + 2.575914579841332e-02, /* 1247 */ + -1.146331356165368e-01, /* 1248 */ + 9.285201019209733e-01, /* 1249 */ + 2.040512577191446e-01, /* 1250 */ + -4.941435636294156e-02, /* 1251 */ + 6.343743012956645e-03, /* 1252 */ + }, { + -1.044678948997064e-03, /* 1253 */ + 2.600941382394202e-02, /* 1254 */ + -1.159488009976052e-01, /* 1255 */ + 9.257625331852983e-01, /* 1256 */ + 2.087915242735169e-01, /* 1257 */ + -5.054364267373540e-02, /* 1258 */ + 6.529939903253072e-03, /* 1259 */ + }, { + -1.033025678195301e-03, /* 1260 */ + 2.625051711596052e-02, /* 1261 */ + -1.172300529782974e-01, /* 1262 */ + 9.229570673645258e-01, /* 1263 */ + 2.135548833662130e-01, /* 1264 */ + -5.167637869573667e-02, /* 1265 */ + 6.718333797051150e-03, /* 1266 */ + }, { + -1.020719235302619e-03, /* 1267 */ + 2.648251504362370e-02, /* 1268 */ + -1.184770301811412e-01, /* 1269 */ + 9.201040210550299e-01, /* 1270 */ + 2.183409703886530e-01, /* 1271 */ + -5.281237530423245e-02, /* 1272 */ + 6.908901259906972e-03, /* 1273 */ + }, { + -1.007789386064493e-03, /* 1274 */ + 2.670546842532187e-02, /* 1275 */ + -1.196898750881803e-01, /* 1276 */ + 9.172037160158394e-01, /* 1277 */ + 2.231494164602341e-01, /* 1278 */ + -5.395144088518943e-02, /* 1279 */ + 7.101617809102288e-03, /* 1280 */ + }, { + -9.942657241554644e-04, /* 1281 */ + 2.691943950144821e-02, /* 1282 */ + -1.208687340001249e-01, /* 1283 */ + 9.142564791211991e-01, /* 1284 */ + 2.279798484669641e-01, /* 1285 */ + -5.509338135155091e-02, /* 1286 */ + 7.296457909743918e-03, /* 1287 */ + }, { + -9.801776601050557e-04, /* 1288 */ + 2.712449190705641e-02, /* 1289 */ + -1.220137569950366e-01, /* 1290 */ + 9.112626423123993e-01, /* 1291 */ + 2.328318891008579e-01, /* 1292 */ + -5.623800016010321e-02, /* 1293 */ + 7.493394971135955e-03, /* 1294 */ + }, { + -9.655544103626029e-04, /* 1295 */ + 2.732069064441559e-02, /* 1296 */ + -1.231250978865554e-01, /* 1297 */ + 9.082225425488828e-01, /* 1298 */ + 2.377051569000891e-01, /* 1299 */ + -5.738509832891316e-02, /* 1300 */ + 7.692401343427691e-03, /* 1301 */ + }, { + -9.504249865037702e-04, /* 1302 */ + 2.750810205546858e-02, /* 1303 */ + -1.242029141816779e-01, /* 1304 */ + 9.051365217586359e-01, /* 1305 */ + 2.425992662898926e-01, /* 1306 */ + -5.853447445533320e-02, /* 1307 */ + 7.893448314540197e-03, /* 1308 */ + }, { + -9.348181845814541e-04, /* 1309 */ + 2.768679379420070e-02, /* 1310 */ + -1.252473670380959e-01, /* 1311 */ + 9.020049267878701e-01, /* 1312 */ + 2.475138276242130e-01, /* 1313 */ + -5.968592473457642e-02, /* 1314 */ + 8.096506107373698e-03, /* 1315 */ + }, { + -9.187625746236699e-04, /* 1316 */ + 2.785683479892532e-02, /* 1317 */ + -1.262586212211042e-01, /* 1318 */ + 8.988281093500092e-01, /* 1319 */ + 2.524484472280946e-01, /* 1320 */ + -6.083924297885757e-02, /* 1321 */ + 8.301543877298635e-03, /* 1322 */ + }, { + -9.022864902810273e-04, /* 1323 */ + 2.801829526449300e-02, /* 1324 */ + -1.272368450600864e-01, /* 1325 */ + 8.956064259739822e-01, /* 1326 */ + 2.574027274408040e-01, /* 1327 */ + -6.199422063710185e-02, /* 1328 */ + 8.508529709932666e-03, /* 1329 */ + }, { + -8.854180186263388e-04, /* 1330 */ + 2.817124661443064e-02, /* 1331 */ + -1.281822104045887e-01, /* 1332 */ + 8.923402379518391e-01, /* 1333 */ + 2.623762666596838e-01, /* 1334 */ + -6.315064681521791e-02, /* 1335 */ + 8.717430619206452e-03, /* 1336 */ + }, { + -8.681849901087664e-04, /* 1337 */ + 2.831576147301751e-02, /* 1338 */ + -1.290948925799897e-01, /* 1339 */ + 8.890299112856921e-01, /* 1340 */ + 2.673686593847286e-01, /* 1341 */ + -6.430830829693616e-02, /* 1342 */ + 8.928212545720045e-03, /* 1343 */ + }, { + -8.506149686650558e-04, /* 1344 */ + 2.845191363730427e-02, /* 1345 */ + -1.299750703427760e-01, /* 1346 */ + 8.856758166339943e-01, /* 1347 */ + 2.723794962638790e-01, /* 1348 */ + -6.546698956520861e-02, /* 1349 */ + 9.140840355392437e-03, /* 1350 */ + }, { + -8.327352419900550e-04, /* 1351 */ + 2.857977804908198e-02, /* 1352 */ + -1.308229258354336e-01, /* 1353 */ + 8.822783292571661e-01, /* 1354 */ + 2.774083641390264e-01, /* 1355 */ + -6.662647282417096e-02, /* 1356 */ + 9.355277838406877e-03, /* 1357 */ + }, { + -8.145728119690237e-04, /* 1358 */ + 2.869943076680737e-02, /* 1359 */ + -1.316386445409620e-01, /* 1360 */ + 8.788378289625756e-01, /* 1361 */ + 2.824548460927230e-01, /* 1362 */ + -6.778653802166430e-02, /* 1363 */ + 9.571487708453395e-03, /* 1364 */ + }, { + -7.961543852738185e-04, /* 1365 */ + 2.881094893749082e-02, /* 1366 */ + -1.324224152370240e-01, /* 1367 */ + 8.753547000488832e-01, /* 1368 */ + 2.875185214955909e-01, /* 1369 */ + -6.894696287231404e-02, /* 1370 */ + 9.789431602271271e-03, /* 1371 */ + }, { + -7.775063641252938e-04, /* 1372 */ + 2.891441076855387e-02, /* 1373 */ + -1.331744299497369e-01, /* 1374 */ + 8.718293312497631e-01, /* 1375 */ + 2.925989660544231e-01, /* 1376 */ + -7.010752288116626e-02, /* 1377 */ + 1.000907007949311e-02, /* 1378 */ + }, { + -7.586548372239626e-04, /* 1379 */ + 2.900989549966232e-02, /* 1380 */ + -1.338948839071171e-01, /* 1381 */ + 8.682621156770068e-01, /* 1382 */ + 2.976957518609703e-01, /* 1383 */ + -7.126799136787691e-02, /* 1384 */ + 1.023036262279292e-02, /* 1385 */ + }, { + -7.396255708510786e-04, /* 1386 */ + 2.909748337454149e-02, /* 1387 */ + -1.345839754921861e-01, /* 1388 */ + 8.646534507630197e-01, /* 1389 */ + 3.028084474414053e-01, /* 1390 */ + -7.242813949145467e-02, /* 1391 */ + 1.045326763833955e-02, /* 1392 */ + }, { + -7.204440001421441e-04, /* 1393 */ + 2.917725561278015e-02, /* 1394 */ + -1.352419061957486e-01, /* 1395 */ + 8.610037382027231e-01, /* 1396 */ + 3.079366178064618e-01, /* 1397 */ + -7.358773627555218e-02, /* 1398 */ + 1.067774245655789e-02, /* 1399 */ + }, { + -7.011352205348302e-04, /* 1400 */ + 2.924929438162952e-02, /* 1401 */ + -1.358688805688508e-01, /* 1402 */ + 8.573133838948687e-01, /* 1403 */ + 3.130798245022357e-01, /* 1404 */ + -7.474654863430664e-02, /* 1405 */ + 1.090374333319907e-02, /* 1406 */ + }, { + -6.817239793932266e-04, /* 1407 */ + 2.931368276780342e-02, /* 1408 */ + -1.364651061749298e-01, /* 1409 */ + 8.535827978827749e-01, /* 1410 */ + 3.182376256616457e-01, /* 1411 */ + -7.590434139872407e-02, /* 1412 */ + 1.113122545072202e-02, /* 1413 */ + }, { + -6.622346678102776e-04, /* 1414 */ + 2.937050474928606e-02, /* 1415 */ + -1.370307935416629e-01, /* 1416 */ + 8.498123942944996e-01, /* 1417 */ + 3.234095760565429e-01, /* 1418 */ + -7.706087734360774e-02, /* 1419 */ + 1.136014291998739e-02, /* 1420 */ + }, { + -6.426913125902382e-04, /* 1421 */ + 2.941984516715388e-02, /* 1422 */ + -1.375661561125266e-01, /* 1423 */ + 8.460025912824529e-01, /* 1424 */ + 3.285952271504655e-01, /* 1425 */ + -7.821591721502538e-02, /* 1426 */ + 1.159044878226562e-02, /* 1427 */ + }, { + -6.231175684128510e-04, /* 1428 */ + 2.946178969741759e-02, /* 1429 */ + -1.380714101980755e-01, /* 1430 */ + 8.421538109624669e-01, /* 1431 */ + 3.337941271520264e-01, /* 1432 */ + -7.936921975831460e-02, /* 1433 */ + 1.182209501156100e-02, /* 1434 */ + }, { + -6.035367101809801e-04, /* 1435 */ + 2.949642482289040e-02, /* 1436 */ + -1.385467749269494e-01, /* 1437 */ + 8.382664793523271e-01, /* 1438 */ + 3.390058210689310e-01, /* 1439 */ + -8.052054174662253e-02, /* 1440 */ + 1.205503251725260e-02, /* 1441 */ + }, { + -5.839716255532901e-04, /* 1442 */ + 2.952383780508903e-02, /* 1443 */ + -1.389924721966199e-01, /* 1444 */ + 8.343410263097801e-01, /* 1445 */ + 3.442298507626138e-01, /* 1446 */ + -8.166963800997633e-02, /* 1447 */ + 1.228921114705370e-02, /* 1448 */ + }, { + -5.644448076635753e-04, /* 1449 */ + 2.954411665617339e-02, /* 1450 */ + -1.394087266238854e-01, /* 1451 */ + 8.303778854700258e-01, /* 1452 */ + 3.494657550034873e-01, /* 1453 */ + -8.281626146488272e-02, /* 1454 */ + 1.252457969029095e-02, /* 1455 */ + }, { + -5.449783480282249e-04, /* 1456 */ + 2.955735011093100e-02, /* 1457 */ + -1.397957654951237e-01, /* 1458 */ + 8.263774941827043e-01, /* 1459 */ + 3.547130695267950e-01, /* 1460 */ + -8.396016314445182e-02, /* 1461 */ + 1.276108588150466e-02, /* 1462 */ + }, { + -5.255939296432825e-04, /* 1463 */ + 2.956362759881255e-02, /* 1464 */ + -1.401538187163136e-01, /* 1465 */ + 8.223402934483920e-01, /* 1466 */ + 3.599713270890607e-01, /* 1467 */ + -8.510109222904338e-02, /* 1468 */ + 1.299867640437090e-02, /* 1469 */ + }, { + -5.063128202724816e-04, /* 1470 */ + 2.956303921602418e-02, /* 1471 */ + -1.404831187628331e-01, /* 1472 */ + 8.182667278546104e-01, /* 1473 */ + 3.652400575251253e-01, /* 1474 */ + -8.623879607743025e-02, /* 1475 */ + 1.323729689594689e-02, /* 1476 */ + }, { + -4.871558659276469e-04, /* 1477 */ + 2.955567569768274e-02, /* 1478 */ + -1.407839006290460e-01, /* 1479 */ + 8.141572455113678e-01, /* 1480 */ + 3.705187878057628e-01, /* 1481 */ + -8.737302025847754e-02, /* 1482 */ + 1.347689195124033e-02, /* 1483 */ + }, { + -4.681434845426152e-04, /* 1484 */ + 2.954162839003991e-02, /* 1485 */ + -1.410564017776856e-01, /* 1486 */ + 8.100122979862356e-01, /* 1487 */ + 3.758070420958670e-01, /* 1488 */ + -8.850350858333141e-02, /* 1489 */ + 1.371740512810408e-02, /* 1490 */ + }, { + -4.492956598419907e-04, /* 1491 */ + 2.952098922278122e-02, /* 1492 */ + -1.413008620890449e-01, /* 1493 */ + 8.058323402389781e-01, /* 1494 */ + 3.811043418132007e-01, /* 1495 */ + -8.963000313811628e-02, /* 1496 */ + 1.395877895245621e-02, /* 1497 */ + }, { + -4.306319354058826e-04, /* 1498 */ + 2.949385068140553e-02, /* 1499 */ + -1.415175238099844e-01, /* 1500 */ + 8.016178305557399e-01, /* 1501 */ + 3.864102056876984e-01, /* 1502 */ + -9.075224431713386e-02, /* 1503 */ + 1.420095492382687e-02, /* 1504 */ + }, { + -4.121714089315939e-04, /* 1505 */ + 2.946030577969092e-02, /* 1506 */ + -1.417066315027663e-01, /* 1507 */ + 7.973692304828067e-01, /* 1508 */ + 3.917241498213130e-01, /* 1509 */ + -9.186997085656183e-02, /* 1510 */ + 1.444387352123256e-02, /* 1511 */ + }, { + -3.939327266934505e-04, /* 1512 */ + 2.942044803225294e-02, /* 1513 */ + -1.418684319937252e-01, /* 1514 */ + 7.930870047599514e-01, /* 1515 */ + 3.970456877483993e-01, /* 1516 */ + -9.298291986864780e-02, /* 1517 */ + 1.468747420937781e-02, /* 1518 */ + }, { + -3.759340782016456e-04, /* 1519 */ + 2.937437142720069e-02, /* 1520 */ + -1.420031743217853e-01, /* 1521 */ + 7.887716212533704e-01, /* 1522 */ + 4.023743304966226e-01, /* 1523 */ + -9.409082687639277e-02, /* 1524 */ + 1.493169544518567e-02, /* 1525 */ + }, { + -3.581931910609877e-04, /* 1526 */ + 2.932217039889637e-02, /* 1527 */ + -1.421111096868331e-01, /* 1528 */ + 7.844235508882289e-01, /* 1529 */ + 4.077095866483865e-01, /* 1530 */ + -9.519342584872197e-02, /* 1531 */ + 1.517647468465644e-02, /* 1532 */ + }, { + -3.407273260304884e-04, /* 1533 */ + 2.926393980082415e-02, /* 1534 */ + -1.421924913979572e-01, /* 1535 */ + 7.800432675808223e-01, /* 1536 */ + 4.130509624027673e-01, /* 1537 */ + -9.629044923613632e-02, /* 1538 */ + 1.542174839005620e-02, /* 1539 */ + }, { + -3.235532722844501e-04, /* 1540 */ + 2.919977487857369e-02, /* 1541 */ + -1.422475748215626e-01, /* 1542 */ + 7.756312481703652e-01, /* 1543 */ + 4.183979616379481e-01, /* 1544 */ + -9.738162800684201e-02, /* 1545 */ + 1.566745203743416e-02, /* 1546 */ + }, { + -3.066873428758958e-04, /* 1547 */ + 2.912977124294393e-02, /* 1548 */ + -1.422766173293711e-01, /* 1549 */ + 7.711879723504229e-01, /* 1550 */ + 4.237500859741424e-01, /* 1551 */ + -9.846669168335127e-02, /* 1552 */ + 1.591352012446994e-02, /* 1553 */ + }, { + -2.901453704029424e-04, /* 1554 */ + 2.905402484317260e-02, /* 1555 */ + -1.422798782463173e-01, /* 1556 */ + 7.667139225999916e-01, /* 1557 */ + 4.291068348369969e-01, /* 1558 */ + -9.954536837955191e-02, /* 1559 */ + 1.615988617865028e-02, /* 1560 */ + }, { + -2.739427028786713e-04, /* 1561 */ + 2.897263194029685e-02, /* 1562 */ + -1.422576187983489e-01, /* 1563 */ + 7.622095841142430e-01, /* 1564 */ + 4.344677055214644e-01, /* 1565 */ + -1.006173848382378e-01, /* 1566 */ + 1.640648276577594e-02, /* 1567 */ + }, { + -2.580941998051696e-04, /* 1568 */ + 2.888568908065016e-02, /* 1569 */ + -1.422101020601427e-01, /* 1570 */ + 7.576754447349441e-01, /* 1571 */ + 4.398321932561375e-01, /* 1572 */ + -1.016824664690990e-01, /* 1573 */ + 1.665324149879781e-02, /* 1574 */ + }, { + -2.426142284520608e-04, /* 1575 */ + 2.879329306950119e-02, /* 1576 */ + -1.421375929027446e-01, /* 1577 */ + 7.531119948805626e-01, /* 1578 */ + 4.451997912680325e-01, /* 1579 */ + -1.027403373871614e-01, /* 1580 */ + 1.690009304698279e-02, /* 1581 */ + }, { + -2.275166603400509e-04, /* 1582 */ + 2.869554094483946e-02, /* 1583 */ + -1.420403579411441e-01, /* 1584 */ + 7.485197274760710e-01, /* 1585 */ + 4.505699908478140e-01, /* 1586 */ + -1.037907204516760e-01, /* 1587 */ + 1.714696714540915e-02, /* 1588 */ + }, { + -2.128148679298167e-04, /* 1589 */ + 2.859252995131316e-02, /* 1590 */ + -1.419186654817930e-01, /* 1591 */ + 7.438991378824603e-01, /* 1592 */ + 4.559422814154492e-01, /* 1593 */ + -1.048333373054486e-01, /* 1594 */ + 1.739379260479069e-02, /* 1595 */ + }, { + -1.985217215164836e-04, /* 1596 */ + 2.848435751432410e-02, /* 1597 */ + -1.417727854700776e-01, /* 1598 */ + 7.392507238259753e-01, /* 1599 */ + 4.613161505862837e-01, /* 1600 */ + -1.058679084146052e-01, /* 1601 */ + 1.764049732162978e-02, /* 1602 */ + }, { + -1.846495863300355e-04, /* 1603 */ + 2.837112121428517e-02, /* 1604 */ + -1.416029894377547e-01, /* 1605 */ + 7.345749853270843e-01, /* 1606 */ + 4.666910842375266e-01, /* 1607 */ + -1.068941531087891e-01, /* 1608 */ + 1.788700828869835e-02, /* 1609 */ + }, { + -1.712103198416544e-04, /* 1610 */ + 2.825291876104482e-02, /* 1611 */ + -1.414095504503600e-01, /* 1612 */ + 7.298724246291924e-01, /* 1613 */ + 4.720665665751356e-01, /* 1614 */ + -1.079117896217818e-01, /* 1615 */ + 1.813325160584695e-02, /* 1616 */ + }, { + -1.582152692763085e-04, /* 1617 */ + 2.812984796848375e-02, /* 1618 */ + -1.411927430545998e-01, /* 1619 */ + 7.251435461271148e-01, /* 1620 */ + 4.774420802010910e-01, /* 1621 */ + -1.089205351325436e-01, /* 1622 */ + 1.837915249114045e-02, /* 1623 */ + }, { + -1.456752693314445e-04, /* 1624 */ + 2.800200672928881e-02, /* 1625 */ + -1.409528432257348e-01, /* 1626 */ + 7.203888562953171e-01, /* 1627 */ + 4.828171061810495e-01, /* 1628 */ + -1.099201058066671e-01, /* 1629 */ + 1.862463529232039e-02, /* 1630 */ + }, { + -1.336006401019428e-04, /* 1631 */ + 2.786949298990845e-02, /* 1632 */ + -1.406901283149657e-01, /* 1633 */ + 7.156088636159380e-01, /* 1634 */ + 4.881911241123659e-01, /* 1635 */ + -1.109102168382377e-01, /* 1636 */ + 1.886962349859249e-02, /* 1637 */ + }, { + -1.220011852110915e-04, /* 1638 */ + 2.773240472569498e-02, /* 1639 */ + -1.404048769968304e-01, /* 1640 */ + 7.108040785066047e-01, /* 1641 */ + 4.935636121924722e-01, /* 1642 */ + -1.118905824920952e-01, /* 1643 */ + 1.911403975273936e-02, /* 1644 */ + }, { + -1.108861901476344e-04, /* 1645 */ + 2.759083991623796e-02, /* 1646 */ + -1.400973692166212e-01, /* 1647 */ + 7.059750132480542e-01, /* 1648 */ + 4.989340472876037e-01, /* 1649 */ + -1.128609161464899e-01, /* 1650 */ + 1.935780586355643e-02, /* 1651 */ + }, { + -1.002644208085391e-04, /* 1652 */ + 2.744489652089330e-02, /* 1653 */ + -1.397678861378340e-01, /* 1654 */ + 7.011221819115717e-01, /* 1655 */ + 5.043019050018616e-01, /* 1656 */ + -1.138209303361282e-01, /* 1657 */ + 1.960084281861067e-02, /* 1658 */ + }, { + -9.014412224732896e-05, /* 1659 */ + 2.729467245451285e-02, /* 1660 */ + -1.394167100896560e-01, /* 1661 */ + 6.962461002862578e-01, /* 1662 */ + 5.096666597466010e-01, /* 1663 */ + -1.147703367955984e-01, /* 1664 */ + 1.984307079732106e-02, /* 1665 */ + }, { + -8.053301762762107e-05, /* 1666 */ + 2.714026556337870e-02, /* 1667 */ + -1.390441245145027e-01, /* 1668 */ + 6.913472858061384e-01, /* 1669 */ + 5.150277848101327e-01, /* 1670 */ + -1.157088465031742e-01, /* 1671 */ + 2.008440918435909e-02, /* 1672 */ + }, { + -7.143830738156712e-05, /* 1673 */ + 2.698177360134680e-02, /* 1674 */ + -1.386504139156142e-01, /* 1675 */ + 6.864262574771270e-01, /* 1676 */ + 5.203847524277286e-01, /* 1677 */ + -1.166361697249849e-01, /* 1678 */ + 2.032477658336845e-02, /* 1679 */ + }, { + -6.286666857267136e-05, /* 1680 */ + 2.681929420620386e-02, /* 1681 */ + -1.382358638047184e-01, /* 1682 */ + 6.814835358038533e-01, /* 1683 */ + 5.257370338519197e-01, /* 1684 */ + -1.175520160595501e-01, /* 1685 */ + 2.056409083100217e-02, /* 1686 */ + }, { + -5.482425446254296e-05, /* 1687 */ + 2.665292487624222e-02, /* 1688 */ + -1.378007606497726e-01, /* 1689 */ + 6.765196427163698e-01, /* 1690 */ + 5.310840994230748e-01, /* 1691 */ + -1.184560944826678e-01, /* 1692 */ + 2.080226901127631e-02, /* 1693 */ + }, { + -4.731669428110093e-05, /* 1694 */ + 2.648276294705649e-02, /* 1695 */ + -1.373453918227895e-01, /* 1696 */ + 6.715351014967476e-01, /* 1697 */ + 5.364254186402488e-01, /* 1698 */ + -1.193481133926526e-01, /* 1699 */ + 2.103922747023789e-02, /* 1700 */ + }, { + -4.034909319948082e-05, /* 1701 */ + 2.630890556856650e-02, /* 1702 */ + -1.368700455477614e-01, /* 1703 */ + 6.665304367055752e-01, /* 1704 */ + 5.417604602322907e-01, /* 1705 */ + -1.202277806559141e-01, /* 1706 */ + 2.127488183094605e-02, /* 1707 */ + }, { + -3.392603250514114e-05, /* 1708 */ + 2.613144968226988e-02, /* 1709 */ + -1.363750108486864e-01, /* 1710 */ + 6.615061741083712e-01, /* 1711 */ + 5.470886922291980e-01, /* 1712 */ + -1.210948036528713e-01, /* 1713 */ + 2.150914700876424e-02, /* 1714 */ + }, { + -2.805156997831240e-05, /* 1715 */ + 2.595049199872917e-02, /* 1716 */ + -1.358605774977103e-01, /* 1717 */ + 6.564628406019232e-01, /* 1718 */ + 5.524095820337069e-01, /* 1719 */ + -1.219488893241928e-01, /* 1720 */ + 2.174193722696236e-02, /* 1721 */ + }, { + -2.272924046911682e-05, /* 1722 */ + 2.576612897529643e-02, /* 1723 */ + -1.353270359633906e-01, /* 1724 */ + 6.514009641405650e-01, /* 1725 */ + 5.577225964931086e-01, /* 1726 */ + -1.227897442173580e-01, /* 1727 */ + 2.197316603262602e-02, /* 1728 */ + }, { + -1.796205667443242e-05, /* 1729 */ + 2.557845679407980e-02, /* 1730 */ + -1.347746773590917e-01, /* 1731 */ + 6.463210736624057e-01, /* 1732 */ + 5.630272019712755e-01, /* 1733 */ + -1.236170745335310e-01, /* 1734 */ + 2.220274631287172e-02, /* 1735 */ + }, { + -1.375251011368080e-05, /* 1736 */ + 2.538757134015553e-02, /* 1737 */ + -1.342037933915221e-01, /* 1738 */ + 6.412236990155202e-01, /* 1739 */ + 5.683228644208926e-01, /* 1740 */ + -1.244305861747393e-01, /* 1741 */ + 2.243059031136537e-02, /* 1742 */ + }, { + -1.010257230258042e-05, /* 1743 */ + 2.519356818002896e-02, /* 1744 */ + -1.336146763094198e-01, /* 1745 */ + 6.361093708841161e-01, /* 1746 */ + 5.736090494558752e-01, /* 1747 */ + -1.252299847913509e-01, /* 1748 */ + 2.265660964514263e-02, /* 1749 */ + }, { + -7.013696123785544e-06, /* 1750 */ + 2.499654254034837e-02, /* 1751 */ + -1.330076188523975e-01, /* 1752 */ + 6.309786207146856e-01, /* 1753 */ + 5.788852224239674e-01, /* 1754 */ + -1.260149758298408e-01, /* 1755 */ + 2.288071532172811e-02, /* 1756 */ + }, { + -4.486817393493708e-06, /* 1757 */ + 2.479658928687493e-02, /* 1758 */ + -1.323829141999531e-01, /* 1759 */ + 6.258319806421593e-01, /* 1760 */ + 5.841508484795060e-01, /* 1761 */ + -1.267852645808413e-01, /* 1762 */ + 2.310281775655174e-02, /* 1763 */ + }, { + -2.522356622734990e-06, /* 1764 */ + 2.459380290371239e-02, /* 1765 */ + -1.317408559206581e-01, /* 1766 */ + 6.206699834160688e-01, /* 1767 */ + 5.894053926563386e-01, /* 1768 */ + -1.275405562274653e-01, /* 1769 */ + 2.332282679065955e-02, /* 1770 */ + }, { + -1.120220972351802e-06, /* 1771 */ + 2.438827747279956e-02, /* 1772 */ + -1.310817379215285e-01, /* 1773 */ + 6.154931623267351e-01, /* 1774 */ + 5.946483199408858e-01, /* 1775 */ + -1.282805558938982e-01, /* 1776 */ + 2.354065170871666e-02, /* 1777 */ + }, { + -2.798064002790454e-07, /* 1778 */ + 2.418010665366927e-02, /* 1779 */ + -1.304058543975903e-01, /* 1780 */ + 6.103020511314905e-01, /* 1781 */ + 5.998790953453343e-01, /* 1782 */ + -1.290049686942476e-01, /* 1783 */ + 2.375620125729980e-02, /* 1784 */ + }, { + -0.000000000000000e+00, /* 1785 */ + 2.396938366347660e-02, /* 1786 */ + -1.297134997816453e-01, /* 1787 */ + 6.050971839809485e-01, /* 1788 */ + 6.050971839809485e-01, /* 1789 */ + -1.297134997816453e-01, /* 1790 */ + 2.396938366347660e-02, /* 1791 */ + } +}; + diff --git a/thirdparty/fluidsynth/src/gentables/CMakeLists.txt b/thirdparty/fluidsynth/src/gentables/CMakeLists.txt new file mode 100644 index 000000000..9cb69f2b2 --- /dev/null +++ b/thirdparty/fluidsynth/src/gentables/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.1) + +# remove $CC from the current environment and by that force cmake to look for a (working) C compiler, +# which hopefully will be the host compiler +unset(ENV{CC}) + +# also unset $CFLAGS to avoid passing any cross compilation flags to the host compiler +unset(ENV{CFLAGS}) + +# linker flags as well +unset(ENV{LDFLAGS}) + +project (gentables C) + +set ( CMAKE_BUILD_TYPE Debug ) + +# hardcode ".exe" as suffix to the binary, else in case of cross-platform cross-compiling the calling cmake will not know the suffix used here and fail to find the binary +set ( CMAKE_EXECUTABLE_SUFFIX ".exe" ) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}) + +# Add the executable that generates the table +add_executable( make_tables + make_tables.c + gen_conv.c + gen_rvoice_dsp.c) + +target_include_directories( make_tables PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../ ) + +if ( WIN32 ) + add_definitions ( -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS ) +else ( WIN32 ) + target_link_libraries (make_tables "m") +endif () diff --git a/thirdparty/fluidsynth/src/gentables/gen_conv.c b/thirdparty/fluidsynth/src/gentables/gen_conv.c new file mode 100644 index 000000000..a789e67be --- /dev/null +++ b/thirdparty/fluidsynth/src/gentables/gen_conv.c @@ -0,0 +1,84 @@ + +#include "utils/fluid_conv_tables.h" +#include "make_tables.h" + + +/* conversion tables */ +static double fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE]; +static double fluid_cb2amp_tab[FLUID_CB_AMP_SIZE]; +static double fluid_concave_tab[FLUID_VEL_CB_SIZE]; +static double fluid_convex_tab[FLUID_VEL_CB_SIZE]; +static double fluid_pan_tab[FLUID_PAN_SIZE]; + +/* + * void fluid_synth_init + * + * Does all the initialization for this module. + */ +static void fluid_conversion_config(void) +{ + int i; + double x; + + for(i = 0; i < FLUID_CENTS_HZ_SIZE; i++) + { + // 6,875 is just a factor that we already multiply into the lookup table to save + // that multiplication in fluid_ct2hz_real() + // 6.875 Hz because 440Hz / 2^6 + fluid_ct2hz_tab[i] = 6.875 * powl(2.0, (double) i / 1200.0); + } + + /* centibels to amplitude conversion + * Note: SF2.01 section 8.1.3: Initial attenuation range is + * between 0 and 144 dB. Therefore a negative attenuation is + * not allowed. + */ + for(i = 0; i < FLUID_CB_AMP_SIZE; i++) + { + fluid_cb2amp_tab[i] = powl(10.0, (double) i / -200.0); + } + + /* initialize the conversion tables (see fluid_mod.c + fluid_mod_get_value cases 4 and 8) */ + + /* concave unipolar positive transform curve */ + fluid_concave_tab[0] = 0.0; + fluid_concave_tab[FLUID_VEL_CB_SIZE - 1] = 1.0; + + /* convex unipolar positive transform curve */ + fluid_convex_tab[0] = 0; + fluid_convex_tab[FLUID_VEL_CB_SIZE - 1] = 1.0; + + /* There seems to be an error in the specs. The equations are + implemented according to the pictures on SF2.01 page 73. */ + + for(i = 1; i < FLUID_VEL_CB_SIZE - 1; i++) + { + x = (-200.0 / FLUID_PEAK_ATTENUATION) * log((double)(i * i) / ((FLUID_VEL_CB_SIZE - 1) * (FLUID_VEL_CB_SIZE - 1))) / M_LN10; + fluid_convex_tab[i] = (1.0 - x); + fluid_concave_tab[(FLUID_VEL_CB_SIZE - 1) - i] = x; + } + + /* initialize the pan conversion table */ + x = M_PI / 2.0 / (FLUID_PAN_SIZE - 1.0); + + for(i = 0; i < FLUID_PAN_SIZE; i++) + { + fluid_pan_tab[i] = sin(i * x); + } +} + + +void gen_conv_table(FILE *fp) +{ + /* Calculate the values */ + fluid_conversion_config(); + + /* fluid_ct2hz_tab */ + EMIT_ARRAY(fp, fluid_ct2hz_tab); + EMIT_ARRAY(fp, fluid_cb2amp_tab); + EMIT_ARRAY(fp, fluid_concave_tab); + EMIT_ARRAY(fp, fluid_convex_tab); + EMIT_ARRAY(fp, fluid_pan_tab); +} + diff --git a/thirdparty/fluidsynth/src/gentables/gen_rvoice_dsp.c b/thirdparty/fluidsynth/src/gentables/gen_rvoice_dsp.c new file mode 100644 index 000000000..0d326236e --- /dev/null +++ b/thirdparty/fluidsynth/src/gentables/gen_rvoice_dsp.c @@ -0,0 +1,81 @@ + +#include "rvoice/fluid_rvoice_dsp_tables.h" +#include "make_tables.h" + +/* Linear interpolation table (2 coefficients centered on 1st) */ +static double interp_coeff_linear[FLUID_INTERP_MAX][2]; + +/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */ +static double interp_coeff[FLUID_INTERP_MAX][4]; + +/* 7th order interpolation (7 coefficients centered on 3rd) */ +static double sinc_table7[FLUID_INTERP_MAX][SINC_INTERP_ORDER]; + +static double cb_interp_coeff_linear(int y, int x) { return interp_coeff_linear[y][x]; } +static double cb_interp_coeff (int y, int x) { return interp_coeff[y][x]; } +static double cb_sinc_table7 (int y, int x) { return sinc_table7[y][x]; } + +/* Initializes interpolation tables */ +void fluid_rvoice_dsp_config(void) +{ + int i, i2; + double x, v; + double i_shifted; + + /* Initialize the coefficients for the interpolation. The math comes + * from a mail, posted by Olli Niemitalo to the music-dsp mailing + * list (I found it in the music-dsp archives + * http://www.smartelectronix.com/musicdsp/). */ + + for(i = 0; i < FLUID_INTERP_MAX; i++) + { + x = (double) i / (double) FLUID_INTERP_MAX; + + interp_coeff[i][0] = (x * (-0.5 + x * (1 - 0.5 * x))); + interp_coeff[i][1] = (1.0 + x * x * (1.5 * x - 2.5)); + interp_coeff[i][2] = (x * (0.5 + x * (2.0 - 1.5 * x))); + interp_coeff[i][3] = (0.5 * x * x * (x - 1.0)); + + interp_coeff_linear[i][0] = (1.0 - x); + interp_coeff_linear[i][1] = x; + } + + /* i: Offset in terms of whole samples */ + for(i = 0; i < SINC_INTERP_ORDER; i++) + { + /* i2: Offset in terms of fractional samples ('subsamples') */ + for(i2 = 0; i2 < FLUID_INTERP_MAX; i2++) + { + /* center on middle of table */ + i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0) + + (double)i2 / (double)FLUID_INTERP_MAX; + + /* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */ + if(fabs(i_shifted) > 0.000001) + { + double arg = M_PI * i_shifted; + v = sin(arg) / (arg); + /* Hanning window */ + v *= 0.5 * (1.0 + cos(2.0 * arg / (double)SINC_INTERP_ORDER)); + } + else + { + v = 1.0; + } + + sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v; + } + } +} + + +void gen_rvoice_table_dsp (FILE *fp) +{ + /* Calculate the values */ + fluid_rvoice_dsp_config(); + + /* Emit the matrices */ + emit_matrix(fp, "interp_coeff_linear", cb_interp_coeff_linear, FLUID_INTERP_MAX, 2); + emit_matrix(fp, "interp_coeff", cb_interp_coeff, FLUID_INTERP_MAX, 4); + emit_matrix(fp, "sinc_table7", cb_sinc_table7, FLUID_INTERP_MAX, 7); +} diff --git a/thirdparty/fluidsynth/src/gentables/make_tables.c b/thirdparty/fluidsynth/src/gentables/make_tables.c new file mode 100644 index 000000000..3aee11b61 --- /dev/null +++ b/thirdparty/fluidsynth/src/gentables/make_tables.c @@ -0,0 +1,84 @@ + +#include "make_tables.h" + +static void write_value(FILE *fp, double val, int i) +{ + fprintf(fp, " %.15e%c /* %d */\n", + val, + ',', + i + ); +} + +/* Emit an array of real numbers */ +void emit_array(FILE *fp, const char *tblname, const double *tbl, int size) +{ + int i; + + fprintf(fp, "static const fluid_real_t %s[%d] = {\n", tblname, size); + + for (i = 0; i < size; i++) + { + write_value(fp, tbl[i], i); + } + fprintf(fp, "};\n\n"); +} + +/* Emit a matrix of real numbers */ +void emit_matrix(FILE *fp, const char *tblname, emit_matrix_cb tbl_cb, int sizeh, int sizel) +{ + int i, j; + + fprintf(fp, "static const fluid_real_t %s[%d][%d] = {\n {\n", tblname, sizeh, sizel); + + for (i = 0; i < sizeh; i++) + { + for (j = 0; j < sizel; j++) + { + write_value(fp, tbl_cb(i, j), i*sizel+j); + } + + + if (i < (sizeh-1)) + fprintf(fp, " }, {\n"); + else + fprintf(fp, " }\n};\n\n"); + } +} + +static void open_table(FILE**fp, const char* dir, const char* file) +{ + char buf[2048] = {0}; + + strcat(buf, dir); + strcat(buf, file); + + /* open the output file */ + *fp = fopen(buf, "w"); + if (*fp == NULL) + { + exit(-2); + } + + /* Emit warning header */ + fprintf(*fp, "/* THIS FILE HAS BEEN AUTOMATICALLY GENERATED. DO NOT EDIT. */\n\n"); +} + +int main (int argc, char *argv[]) +{ + FILE *fp; + + // make sure we have enough arguments + if (argc < 2) + return -1; + + open_table(&fp, argv[1], "fluid_conv_tables.inc.h"); + gen_conv_table(fp); + fclose(fp); + + open_table(&fp, argv[1], "fluid_rvoice_dsp_tables.inc.h"); + gen_rvoice_table_dsp(fp); + fclose(fp); + + return 0; +} diff --git a/thirdparty/fluidsynth/src/gentables/make_tables.h b/thirdparty/fluidsynth/src/gentables/make_tables.h new file mode 100644 index 000000000..0f19cf632 --- /dev/null +++ b/thirdparty/fluidsynth/src/gentables/make_tables.h @@ -0,0 +1,26 @@ + +#ifndef _FLUID_MAKE_TABLES_H +#define _FLUID_MAKE_TABLES_H + +#include +#include +#include +#include + + +#define EMIT_ARRAY(__fp__, __arr__) emit_array(__fp__, #__arr__, __arr__, sizeof(__arr__)/sizeof(*__arr__)) + +/* callback for general access to matrices */ +typedef double (*emit_matrix_cb)(int y, int x); + +/* Generators */ +void gen_rvoice_table_dsp(FILE *fp); +void gen_conv_table(FILE *fp); + +/* Emit an array of real numbers */ +void emit_array(FILE *fp, const char *tblname, const double *tbl, int size); + +/* Emit a matrix of real numbers */ +void emit_matrix(FILE *fp, const char *tblname, emit_matrix_cb tbl_cb, int sizeh, int sizel); + +#endif diff --git a/thirdparty/fluidsynth/src/midi/fluid_midi.c b/thirdparty/fluidsynth/src/midi/fluid_midi.c new file mode 100644 index 000000000..fa72327cc --- /dev/null +++ b/thirdparty/fluidsynth/src/midi/fluid_midi.c @@ -0,0 +1,2788 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_midi.h" +#include "fluid_sys.h" +#include "fluid_synth.h" +#include "fluid_settings.h" + + +static int fluid_midi_event_length(unsigned char event); +static int fluid_isasciistring(char *s); +static long fluid_getlength(const unsigned char *s); + + +/* Read the entire contents of a file into memory, allocating enough memory + * for the file, and returning the length and the buffer. + * Note: This rewinds the file to the start before reading. + * Returns NULL if there was an error reading or allocating memory. + */ +typedef FILE *fluid_file; +static char *fluid_file_read_full(fluid_file fp, size_t *length); +static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic); +static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size); +#define READ_FULL_INITIAL_BUFLEN 1024 + +static fluid_track_t *new_fluid_track(int num); +static void delete_fluid_track(fluid_track_t *track); +static int fluid_track_set_name(fluid_track_t *track, char *name); +static int fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt); +static fluid_midi_event_t *fluid_track_next_event(fluid_track_t *track); +static int fluid_track_get_duration(fluid_track_t *track); +static int fluid_track_reset(fluid_track_t *track); + +static int fluid_player_add_track(fluid_player_t *player, fluid_track_t *track); +static int fluid_player_callback(void *data, unsigned int msec); +static int fluid_player_reset(fluid_player_t *player); +static int fluid_player_load(fluid_player_t *player, fluid_playlist_item *item); +static void fluid_player_advancefile(fluid_player_t *player); +static void fluid_player_playlist_load(fluid_player_t *player, unsigned int msec); +static void fluid_player_update_tempo(fluid_player_t *player); + +static fluid_midi_file *new_fluid_midi_file(const char *buffer, size_t length); +static void delete_fluid_midi_file(fluid_midi_file *mf); +static int fluid_midi_file_read_mthd(fluid_midi_file *midifile); +static int fluid_midi_file_load_tracks(fluid_midi_file *midifile, fluid_player_t *player); +static int fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num); +static int fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track); +static int fluid_midi_file_read_varlen(fluid_midi_file *mf); +static int fluid_midi_file_getc(fluid_midi_file *mf); +static int fluid_midi_file_push(fluid_midi_file *mf, int c); +static int fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len); +static int fluid_midi_file_skip(fluid_midi_file *mf, int len); +static int fluid_midi_file_eof(fluid_midi_file *mf); +static int fluid_midi_file_read_tracklen(fluid_midi_file *mf); +static int fluid_midi_file_eot(fluid_midi_file *mf); +static int fluid_midi_file_get_division(fluid_midi_file *midifile); + + +/*************************************************************** + * + * MIDIFILE + */ + +/** + * Check if a file is a MIDI file. + * @param filename Path to the file to check + * @return TRUE if it could be a MIDI file, FALSE otherwise + * + * The current implementation only checks for the "MThd" header in the file. + * It is useful only to distinguish between SoundFont and MIDI files. + */ +int fluid_is_midifile(const char *filename) +{ + FILE *fp; + uint32_t id; + int retcode = FALSE; + + do + { + if((fp = fluid_file_open(filename, NULL)) == NULL) + { + return retcode; + } + + if(FLUID_FREAD(&id, sizeof(id), 1, fp) != 1) + { + break; + } + + retcode = (id == FLUID_FOURCC('M', 'T', 'h', 'd')); + } + while(0); + + FLUID_FCLOSE(fp); + + return retcode; +} + +/** + * Return a new MIDI file handle for parsing an already-loaded MIDI file. + * @internal + * @param buffer Pointer to full contents of MIDI file (borrows the pointer). + * The caller must not free buffer until after the fluid_midi_file is deleted. + * @param length Size of the buffer in bytes. + * @return New MIDI file handle or NULL on error. + */ +fluid_midi_file * +new_fluid_midi_file(const char *buffer, size_t length) +{ + fluid_midi_file *mf; + + if(length > INT_MAX) + { + FLUID_LOG(FLUID_ERR, "Refusing to open a MIDI file which is bigger than 2GiB"); + return NULL; + } + + mf = FLUID_NEW(fluid_midi_file); + if(mf == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file)); + + mf->c = -1; + mf->running_status = -1; + + mf->buffer = buffer; + mf->buf_len = (int)length; + mf->buf_pos = 0; + mf->eof = FALSE; + + if(fluid_midi_file_read_mthd(mf) != FLUID_OK) + { + FLUID_FREE(mf); + return NULL; + } + + return mf; +} + +static char * +fluid_file_read_full(fluid_file fp, size_t *length) +{ + size_t buflen; + char *buffer; + size_t n; + + /* Work out the length of the file in advance */ + if(FLUID_FSEEK(fp, 0, SEEK_END) != 0) + { + FLUID_LOG(FLUID_ERR, "File load: Could not seek within file"); + return NULL; + } + + buflen = ftell(fp); + + if(FLUID_FSEEK(fp, 0, SEEK_SET) != 0) + { + FLUID_LOG(FLUID_ERR, "File load: Could not seek within file"); + return NULL; + } + + FLUID_LOG(FLUID_DBG, "File load: Allocating %lu bytes", (unsigned long)buflen); + buffer = FLUID_MALLOC(buflen); + + if(buffer == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } + + n = FLUID_FREAD(buffer, 1, buflen, fp); + + if(n != buflen) + { + FLUID_LOG(FLUID_ERR, "Only read %lu bytes; expected %lu", (unsigned long)n, + (unsigned long)buflen); + FLUID_FREE(buffer); + return NULL; + }; + + *length = n; + + return buffer; +} + +/** + * Delete a MIDI file handle. + * @internal + * @param mf MIDI file handle to close and free. + */ +void +delete_fluid_midi_file(fluid_midi_file *mf) +{ + fluid_return_if_fail(mf != NULL); + + FLUID_FREE(mf); +} + +/* + * Gets the next byte in a MIDI file, taking into account previous running status. + * + * returns -1 if EOF or read error + */ +int +fluid_midi_file_getc(fluid_midi_file *mf) +{ + unsigned char c; + + if(mf->c >= 0) + { + c = mf->c; + mf->c = -1; + } + else + { + if(mf->buf_pos >= mf->buf_len) + { + mf->eof = TRUE; + return -1; + } + + c = mf->buffer[mf->buf_pos++]; + mf->trackpos++; + } + + return (int) c; +} + +/* + * Saves a byte to be returned the next time fluid_midi_file_getc() is called, + * when it is necessary according to running status. + */ +int +fluid_midi_file_push(fluid_midi_file *mf, int c) +{ + mf->c = c; + return FLUID_OK; +} + +/* + * fluid_midi_file_read + */ +int +fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len) +{ + int num = len < mf->buf_len - mf->buf_pos + ? len : mf->buf_len - mf->buf_pos; + + if(num != len) + { + mf->eof = TRUE; + } + + if(num < 0) + { + num = 0; + } + + /* Note: Read bytes, even if there aren't enough, but only increment + * trackpos if successful (emulates old behaviour of fluid_midi_file_read) + */ + FLUID_MEMCPY(buf, mf->buffer + mf->buf_pos, num); + mf->buf_pos += num; + + if(num == len) + { + mf->trackpos += num; + } + +#if DEBUG + else + { + FLUID_LOG(FLUID_DBG, "Could not read the requested number of bytes"); + } + +#endif + return (num != len) ? FLUID_FAILED : FLUID_OK; +} + +/* + * fluid_midi_file_skip + */ +int +fluid_midi_file_skip(fluid_midi_file *mf, int skip) +{ + int new_pos = mf->buf_pos + skip; + + /* Mimic the behaviour of fseek: Error to seek past the start of file, but + * OK to seek past end (this just puts it into the EOF state). */ + if(new_pos < 0) + { + FLUID_LOG(FLUID_ERR, "Failed to seek position in file"); + return FLUID_FAILED; + } + + /* Clear the EOF flag, even if moved past the end of the file (this is + * consistent with the behaviour of fseek). */ + mf->eof = FALSE; + mf->buf_pos = new_pos; + return FLUID_OK; +} + +/* + * fluid_midi_file_eof + */ +int fluid_midi_file_eof(fluid_midi_file *mf) +{ + /* Note: This does not simply test whether the file read pointer is past + * the end of the file. It mimics the behaviour of feof by actually + * testing the stateful EOF condition, which is set to TRUE if getc or + * fread have attempted to read past the end (but not if they have + * precisely reached the end), but reset to FALSE upon a successful seek. + */ + return mf->eof; +} + +/* + * fluid_midi_file_read_mthd + */ +int +fluid_midi_file_read_mthd(fluid_midi_file *mf) +{ + char mthd[14]; + + if(fluid_midi_file_read(mf, mthd, sizeof(mthd)) != FLUID_OK) + { + return FLUID_FAILED; + } + + if((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6) + || (mthd[9] > 2)) + { + FLUID_LOG(FLUID_ERR, + "Doesn't look like a MIDI file: invalid MThd header"); + return FLUID_FAILED; + } + + mf->type = mthd[9]; + mf->ntracks = (unsigned) mthd[11]; + mf->ntracks += (unsigned int)(mthd[10]) << 16; + + if((signed char)mthd[12] < 0) + { + mf->uses_smpte = 1; + mf->smpte_fps = -(signed char)mthd[12]; + mf->smpte_res = (unsigned) mthd[13]; + FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet"); + return FLUID_FAILED; + } + else + { + mf->uses_smpte = 0; + mf->division = ((unsigned)mthd[12] << 8) | ((unsigned)mthd[13] & 0xff); + FLUID_LOG(FLUID_DBG, "Division=%d", mf->division); + } + + return FLUID_OK; +} + +/* + * fluid_midi_file_load_tracks + */ +int +fluid_midi_file_load_tracks(fluid_midi_file *mf, fluid_player_t *player) +{ + int i; + + for(i = 0; i < mf->ntracks; i++) + { + if(fluid_midi_file_read_track(mf, player, i) != FLUID_OK) + { + return FLUID_FAILED; + } + } + + return FLUID_OK; +} + +/* + * fluid_isasciistring + */ +int +fluid_isasciistring(char *s) +{ + /* From ctype.h */ +#define fluid_isascii(c) (((c) & ~0x7f) == 0) + + size_t i, len = FLUID_STRLEN(s); + + for(i = 0; i < len; i++) + { + if(!fluid_isascii(s[i])) + { + return 0; + } + } + + return 1; + +#undef fluid_isascii +} + +/* + * fluid_getlength + */ +long +fluid_getlength(const unsigned char *s) +{ + long i = 0; + i = s[3] | (s[2] << 8) | (s[1] << 16) | (s[0] << 24); + return i; +} + +/* + * fluid_midi_file_read_tracklen + */ +int +fluid_midi_file_read_tracklen(fluid_midi_file *mf) +{ + unsigned char length[5]; + + if(fluid_midi_file_read(mf, length, 4) != FLUID_OK) + { + return FLUID_FAILED; + } + + mf->tracklen = fluid_getlength(length); + mf->trackpos = 0; + mf->eot = 0; + return FLUID_OK; +} + +/* + * fluid_midi_file_eot + */ +int +fluid_midi_file_eot(fluid_midi_file *mf) +{ +#if DEBUG + + if(mf->trackpos > mf->tracklen) + { + printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen); + } + +#endif + return mf->eot || (mf->trackpos >= mf->tracklen); +} + +/* + * fluid_midi_file_read_track + */ +int +fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num) +{ + fluid_track_t *track; + unsigned char id[5], length[5]; + int found_track = 0; + int skip; + + if(fluid_midi_file_read(mf, id, 4) != FLUID_OK) + { + return FLUID_FAILED; + } + + id[4] = '\0'; + mf->dtime = 0; + + while(!found_track) + { + + if(fluid_isasciistring((char *) id) == 0) + { + FLUID_LOG(FLUID_ERR, + "A non-ascii track header found, corrupt file"); + return FLUID_FAILED; + + } + else if(FLUID_STRCMP((char *) id, "MTrk") == 0) + { + + found_track = 1; + + if(fluid_midi_file_read_tracklen(mf) != FLUID_OK) + { + return FLUID_FAILED; + } + + track = new_fluid_track(num); + + if(track == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + while(!fluid_midi_file_eot(mf)) + { + if(fluid_midi_file_read_event(mf, track) != FLUID_OK) + { + delete_fluid_track(track); + return FLUID_FAILED; + } + } + + /* Skip remaining track data, if any */ + if(mf->trackpos < mf->tracklen) + { + if(fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos) != FLUID_OK) + { + delete_fluid_track(track); + return FLUID_FAILED; + } + } + + if(fluid_player_add_track(player, track) != FLUID_OK) + { + delete_fluid_track(track); + return FLUID_FAILED; + } + + } + else + { + found_track = 0; + + if(fluid_midi_file_read(mf, length, 4) != FLUID_OK) + { + return FLUID_FAILED; + } + + skip = fluid_getlength(length); + + /* fseek(mf->fp, skip, SEEK_CUR); */ + if(fluid_midi_file_skip(mf, skip) != FLUID_OK) + { + return FLUID_FAILED; + } + } + } + + if(fluid_midi_file_eof(mf)) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + return FLUID_OK; +} + +/* + * fluid_midi_file_read_varlen + */ +int +fluid_midi_file_read_varlen(fluid_midi_file *mf) +{ + int i; + int c; + mf->varlen = 0; + + for(i = 0;; i++) + { + if(i == 4) + { + FLUID_LOG(FLUID_ERR, "Invalid variable length number"); + return FLUID_FAILED; + } + + c = fluid_midi_file_getc(mf); + + if(c < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + if(c & 0x80) + { + mf->varlen |= (int)(c & 0x7F); + mf->varlen <<= 7; + } + else + { + mf->varlen += c; + break; + } + } + + return FLUID_OK; +} + +/* + * fluid_midi_file_read_event + */ +int +fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track) +{ + int status; + int type; + int tempo; + unsigned char *metadata = NULL; + unsigned char *dyn_buf = NULL; + unsigned char static_buf[256]; + int nominator, denominator, clocks, notes; + fluid_midi_event_t *evt; + int channel = 0; + int param1 = 0; + int param2 = 0; + int size; + + /* read the delta-time of the event */ + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { + return FLUID_FAILED; + } + + mf->dtime += mf->varlen; + + /* read the status byte */ + status = fluid_midi_file_getc(mf); + + if(status < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + /* not a valid status byte: use the running status instead */ + if((status & 0x80) == 0) + { + if((mf->running_status & 0x80) == 0) + { + FLUID_LOG(FLUID_ERR, "Undefined status and invalid running status"); + return FLUID_FAILED; + } + + fluid_midi_file_push(mf, status); + status = mf->running_status; + } + + /* check what message we have */ + + mf->running_status = status; + + if(status == MIDI_SYSEX) /* system exclusif */ + { + /* read the length of the message */ + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { + return FLUID_FAILED; + } + + if(mf->varlen) + { + FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, + __LINE__, mf->varlen); + metadata = FLUID_MALLOC(mf->varlen + 1); + + if(metadata == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + /* read the data of the message */ + if(fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) + { + FLUID_FREE(metadata); + return FLUID_FAILED; + } + + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(metadata); + return FLUID_FAILED; + } + + evt->dtime = mf->dtime; + size = mf->varlen; + + if(metadata[mf->varlen - 1] == MIDI_EOX) + { + size--; + } + + /* Add SYSEX event and indicate that its dynamically allocated and should be freed with event */ + fluid_midi_event_set_sysex(evt, metadata, size, TRUE); + fluid_track_add_event(track, evt); + mf->dtime = 0; + } + + return FLUID_OK; + + } + else if(status == MIDI_META_EVENT) /* meta events */ + { + + int result = FLUID_OK; + + /* get the type of the meta message */ + type = fluid_midi_file_getc(mf); + + if(type < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + /* get the length of the data part */ + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { + return FLUID_FAILED; + } + + if(mf->varlen < 255) + { + metadata = &static_buf[0]; + } + else + { + FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, + __LINE__, mf->varlen); + dyn_buf = FLUID_MALLOC(mf->varlen + 1); + + if(dyn_buf == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + metadata = dyn_buf; + } + + /* read the data */ + if(mf->varlen) + { + if(fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) + { + if(dyn_buf) + { + FLUID_FREE(dyn_buf); + } + + return FLUID_FAILED; + } + } + + /* handle meta data */ + switch(type) + { + + case MIDI_COPYRIGHT: + metadata[mf->varlen] = 0; + break; + + case MIDI_TRACK_NAME: + metadata[mf->varlen] = 0; + fluid_track_set_name(track, (char *) metadata); + break; + + case MIDI_INST_NAME: + metadata[mf->varlen] = 0; + break; + + case MIDI_LYRIC: + case MIDI_TEXT: + { + void *tmp; + int size = mf->varlen + 1; + + /* NULL terminate strings for safety */ + metadata[size - 1] = '\0'; + + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; + break; + } + + evt->dtime = mf->dtime; + + tmp = FLUID_MALLOC(size); + + if(tmp == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + delete_fluid_midi_event(evt); + evt = NULL; + result = FLUID_FAILED; + break; + } + + FLUID_MEMCPY(tmp, metadata, size); + + fluid_midi_event_set_sysex_LOCAL(evt, type, tmp, size, TRUE); + fluid_track_add_event(track, evt); + mf->dtime = 0; + } + break; + + case MIDI_MARKER: + break; + + case MIDI_CUE_POINT: + break; /* don't care much for text events */ + + case MIDI_EOT: + if(mf->varlen != 0) + { + FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event"); + result = FLUID_FAILED; + break; + } + + mf->eot = 1; + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; + break; + } + + evt->dtime = mf->dtime; + evt->type = MIDI_EOT; + fluid_track_add_event(track, evt); + mf->dtime = 0; + break; + + case MIDI_SET_TEMPO: + if(mf->varlen != 3) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for SetTempo meta event"); + result = FLUID_FAILED; + break; + } + + tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2]; + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; + break; + } + + evt->dtime = mf->dtime; + evt->type = MIDI_SET_TEMPO; + evt->channel = 0; + evt->param1 = tempo; + evt->param2 = 0; + fluid_track_add_event(track, evt); + mf->dtime = 0; + break; + + case MIDI_SMPTE_OFFSET: + if(mf->varlen != 5) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for SMPTE Offset meta event"); + result = FLUID_FAILED; + break; + } + + break; /* we don't use smtp */ + + case MIDI_TIME_SIGNATURE: + if(mf->varlen != 4) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for TimeSignature meta event"); + result = FLUID_FAILED; + break; + } + + nominator = metadata[0]; + denominator = pow(2.0, (double) metadata[1]); + clocks = metadata[2]; + notes = metadata[3]; + + FLUID_LOG(FLUID_DBG, + "signature=%d/%d, metronome=%d, 32nd-notes=%d", + nominator, denominator, clocks, notes); + + break; + + case MIDI_KEY_SIGNATURE: + if(mf->varlen != 2) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for KeySignature meta event"); + result = FLUID_FAILED; + break; + } + + /* We don't care about key signatures anyway */ + /* sf = metadata[0]; + mi = metadata[1]; */ + break; + + case MIDI_SEQUENCER_EVENT: + break; + + default: + break; + } + + if(dyn_buf) + { + FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__); + FLUID_FREE(dyn_buf); + } + + return result; + + } + else /* channel messages */ + { + + type = status & 0xf0; + channel = status & 0x0f; + + /* all channel message have at least 1 byte of associated data */ + if((param1 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + switch(type) + { + + case NOTE_ON: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + break; + + case NOTE_OFF: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + break; + + case KEY_PRESSURE: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + break; + + case CONTROL_CHANGE: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + break; + + case PROGRAM_CHANGE: + break; + + case CHANNEL_PRESSURE: + break; + + case PITCH_BEND: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } + + param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f); + param2 = 0; + break; + + default: + /* Can't possibly happen !? */ + FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event"); + return FLUID_FAILED; + } + + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + evt->dtime = mf->dtime; + evt->type = type; + evt->channel = channel; + evt->param1 = param1; + evt->param2 = param2; + fluid_track_add_event(track, evt); + mf->dtime = 0; + } + + return FLUID_OK; +} + +/* + * fluid_midi_file_get_division + */ +int +fluid_midi_file_get_division(fluid_midi_file *midifile) +{ + return midifile->division; +} + +/****************************************************** + * + * fluid_track_t + */ + +/** + * Create a MIDI event structure. + * @return New MIDI event structure or NULL when out of memory. + */ +fluid_midi_event_t * +new_fluid_midi_event() +{ + fluid_midi_event_t *evt; + evt = FLUID_NEW(fluid_midi_event_t); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + evt->dtime = 0; + evt->type = 0; + evt->channel = 0; + evt->param1 = 0; + evt->param2 = 0; + evt->next = NULL; + evt->paramptr = NULL; + return evt; +} + +/** + * Delete MIDI event structure. + * @param evt MIDI event structure + */ +void +delete_fluid_midi_event(fluid_midi_event_t *evt) +{ + fluid_midi_event_t *temp; + fluid_return_if_fail(evt != NULL); + + while(evt) + { + temp = evt->next; + + /* Dynamic SYSEX event? - free (param2 indicates if dynamic) */ + if((evt->type == MIDI_SYSEX || (evt-> type == MIDI_TEXT) || (evt->type == MIDI_LYRIC)) && + evt->paramptr && evt->param2) + { + FLUID_FREE(evt->paramptr); + } + + FLUID_FREE(evt); + evt = temp; + } +} + +/** + * Get the event type field of a MIDI event structure. + * @param evt MIDI event structure + * @return Event type field (MIDI status byte without channel) + */ +int +fluid_midi_event_get_type(const fluid_midi_event_t *evt) +{ + return evt->type; +} + +/** + * Set the event type field of a MIDI event structure. + * @param evt MIDI event structure + * @param type Event type field (MIDI status byte without channel) + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_type(fluid_midi_event_t *evt, int type) +{ + evt->type = type; + return FLUID_OK; +} + +/** + * Get the channel field of a MIDI event structure. + * @param evt MIDI event structure + * @return Channel field + */ +int +fluid_midi_event_get_channel(const fluid_midi_event_t *evt) +{ + return evt->channel; +} + +/** + * Set the channel field of a MIDI event structure. + * @param evt MIDI event structure + * @param chan MIDI channel field + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_channel(fluid_midi_event_t *evt, int chan) +{ + evt->channel = chan; + return FLUID_OK; +} + +/** + * Get the key field of a MIDI event structure. + * @param evt MIDI event structure + * @return MIDI note number (0-127) + */ +int +fluid_midi_event_get_key(const fluid_midi_event_t *evt) +{ + return evt->param1; +} + +/** + * Set the key field of a MIDI event structure. + * @param evt MIDI event structure + * @param v MIDI note number (0-127) + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_key(fluid_midi_event_t *evt, int v) +{ + evt->param1 = v; + return FLUID_OK; +} + +/** + * Get the velocity field of a MIDI event structure. + * @param evt MIDI event structure + * @return MIDI velocity number (0-127) + */ +int +fluid_midi_event_get_velocity(const fluid_midi_event_t *evt) +{ + return evt->param2; +} + +/** + * Set the velocity field of a MIDI event structure. + * @param evt MIDI event structure + * @param v MIDI velocity value + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_velocity(fluid_midi_event_t *evt, int v) +{ + evt->param2 = v; + return FLUID_OK; +} + +/** + * Get the control number of a MIDI event structure. + * @param evt MIDI event structure + * @return MIDI control number + */ +int +fluid_midi_event_get_control(const fluid_midi_event_t *evt) +{ + return evt->param1; +} + +/** + * Set the control field of a MIDI event structure. + * @param evt MIDI event structure + * @param v MIDI control number + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_control(fluid_midi_event_t *evt, int v) +{ + evt->param1 = v; + return FLUID_OK; +} + +/** + * Get the value field from a MIDI event structure. + * @param evt MIDI event structure + * @return Value field + */ +int +fluid_midi_event_get_value(const fluid_midi_event_t *evt) +{ + return evt->param2; +} + +/** + * Set the value field of a MIDI event structure. + * @param evt MIDI event structure + * @param v Value to assign + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_value(fluid_midi_event_t *evt, int v) +{ + evt->param2 = v; + return FLUID_OK; +} + +/** + * Get the program field of a MIDI event structure. + * @param evt MIDI event structure + * @return MIDI program number (0-127) + */ +int +fluid_midi_event_get_program(const fluid_midi_event_t *evt) +{ + return evt->param1; +} + +/** + * Set the program field of a MIDI event structure. + * @param evt MIDI event structure + * @param val MIDI program number (0-127) + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_program(fluid_midi_event_t *evt, int val) +{ + evt->param1 = val; + return FLUID_OK; +} + +/** + * Get the pitch field of a MIDI event structure. + * @param evt MIDI event structure + * @return Pitch value (14 bit value, 0-16383, 8192 is center) + */ +int +fluid_midi_event_get_pitch(const fluid_midi_event_t *evt) +{ + return evt->param1; +} + +/** + * Set the pitch field of a MIDI event structure. + * @param evt MIDI event structure + * @param val Pitch value (14 bit value, 0-16383, 8192 is center) + * @return Always returns FLUID_OK + */ +int +fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val) +{ + evt->param1 = val; + return FLUID_OK; +} + +/** + * Assign sysex data to a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to SYSEX data + * @param size Size of SYSEX data in bytes + * @param dynamic TRUE if the SYSEX data has been dynamically allocated and + * should be freed when the event is freed (only applies if event gets destroyed + * with delete_fluid_midi_event()) + * @return Always returns #FLUID_OK + */ +int +fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic) +{ + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_SYSEX, data, size, dynamic); + return FLUID_OK; +} + +/** + * Assign text data to a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to text data + * @param size Size of text data in bytes + * @param dynamic TRUE if the data has been dynamically allocated and + * should be freed when the event is freed via delete_fluid_midi_event() + * @return Always returns #FLUID_OK + * + * @since 2.0.0 + */ +int +fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic) +{ + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_TEXT, data, size, dynamic); + return FLUID_OK; +} + +/** + * Get the text of a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to return text data on. + * @param size Pointer to return text size on. + * @return Returns #FLUID_OK if \p data and \p size previously set by + * fluid_midi_event_set_text() have been successfully retrieved. + * Else #FLUID_FAILED is returned and \p data and \p size are not changed. + * @since 2.0.3 + */ +int fluid_midi_event_get_text(fluid_midi_event_t *evt, void **data, int *size) +{ + fluid_return_val_if_fail(evt != NULL, FLUID_FAILED); + fluid_return_val_if_fail(evt->type == MIDI_TEXT, FLUID_FAILED); + + fluid_midi_event_get_sysex_LOCAL(evt, data, size); + return FLUID_OK; +} + +/** + * Assign lyric data to a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to lyric data + * @param size Size of lyric data in bytes + * @param dynamic TRUE if the data has been dynamically allocated and + * should be freed when the event is freed via delete_fluid_midi_event() + * @return Always returns #FLUID_OK + * + * @since 2.0.0 + */ +int +fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic) +{ + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_LYRIC, data, size, dynamic); + return FLUID_OK; +} + +/** + * Get the lyric of a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to return lyric data on. + * @param size Pointer to return lyric size on. + * @return Returns #FLUID_OK if \p data and \p size previously set by + * fluid_midi_event_set_lyrics() have been successfully retrieved. + * Else #FLUID_FAILED is returned and \p data and \p size are not changed. + * @since 2.0.3 + */ +int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt, void **data, int *size) +{ + fluid_return_val_if_fail(evt != NULL, FLUID_FAILED); + fluid_return_val_if_fail(evt->type == MIDI_LYRIC, FLUID_FAILED); + + fluid_midi_event_get_sysex_LOCAL(evt, data, size); + return FLUID_OK; +} + +static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic) +{ + evt->type = type; + evt->paramptr = data; + evt->param1 = size; + evt->param2 = dynamic; +} + +static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size) +{ + if(data) + { + *data = evt->paramptr; + } + + if(size) + { + *size = evt->param1; + } +} + +/****************************************************** + * + * fluid_track_t + */ + +/* + * new_fluid_track + */ +fluid_track_t * +new_fluid_track(int num) +{ + fluid_track_t *track; + track = FLUID_NEW(fluid_track_t); + + if(track == NULL) + { + return NULL; + } + + track->name = NULL; + track->num = num; + track->first = NULL; + track->cur = NULL; + track->last = NULL; + track->ticks = 0; + return track; +} + +/* + * delete_fluid_track + */ +void +delete_fluid_track(fluid_track_t *track) +{ + fluid_return_if_fail(track != NULL); + + FLUID_FREE(track->name); + delete_fluid_midi_event(track->first); + FLUID_FREE(track); +} + +/* + * fluid_track_set_name + */ +int +fluid_track_set_name(fluid_track_t *track, char *name) +{ + size_t len; + + if(track->name != NULL) + { + FLUID_FREE(track->name); + } + + if(name == NULL) + { + track->name = NULL; + return FLUID_OK; + } + + len = FLUID_STRLEN(name); + track->name = FLUID_MALLOC(len + 1); + + if(track->name == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + FLUID_STRCPY(track->name, name); + return FLUID_OK; +} + +/* + * fluid_track_get_duration + */ +int +fluid_track_get_duration(fluid_track_t *track) +{ + int time = 0; + fluid_midi_event_t *evt = track->first; + + while(evt != NULL) + { + time += evt->dtime; + evt = evt->next; + } + + return time; +} + +/* + * fluid_track_add_event + */ +int +fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt) +{ + evt->next = NULL; + + if(track->first == NULL) + { + track->first = evt; + track->cur = evt; + track->last = evt; + } + else + { + track->last->next = evt; + track->last = evt; + } + + return FLUID_OK; +} + +/* + * fluid_track_next_event + */ +fluid_midi_event_t * +fluid_track_next_event(fluid_track_t *track) +{ + if(track->cur != NULL) + { + track->cur = track->cur->next; + } + + return track->cur; +} + +/* + * fluid_track_reset + */ +int +fluid_track_reset(fluid_track_t *track) +{ + track->ticks = 0; + track->cur = track->first; + return FLUID_OK; +} + +/* + * fluid_track_send_events + */ +static void +fluid_track_send_events(fluid_track_t *track, + fluid_synth_t *synth, + fluid_player_t *player, + unsigned int ticks, + int seek_ticks + ) +{ + fluid_midi_event_t *event; + int seeking = seek_ticks >= 0; + + if(seeking) + { + ticks = seek_ticks; /* update target ticks */ + + if(track->ticks > ticks) + { + fluid_track_reset(track); /* reset track if seeking backwards */ + } + } + + while(1) + { + + event = track->cur; + + if(event == NULL) + { + return; + } + + /* printf("track=%02d\tticks=%05u\ttrack=%05u\tdtime=%05u\tnext=%05u\n", */ + /* track->num, */ + /* ticks, */ + /* track->ticks, */ + /* event->dtime, */ + /* track->ticks + event->dtime); */ + + if(track->ticks + event->dtime > ticks) + { + return; + } + + track->ticks += event->dtime; + + if(!player || event->type == MIDI_EOT) + { + /* don't send EOT events to the callback */ + } + else if(seeking && track->ticks != ticks && (event->type == NOTE_ON || event->type == NOTE_OFF)) + { + /* skip on/off messages */ + } + else + { + if(player->playback_callback) + { + player->playback_callback(player->playback_userdata, event); + if(event->type == NOTE_ON && event->param2 != 0 && !player->channel_isplaying[event->channel]) + { + player->channel_isplaying[event->channel] = TRUE; + } + } + } + + if(event->type == MIDI_SET_TEMPO && player != NULL) + { + /* memorize the tempo change value coming from the MIDI file */ + fluid_atomic_int_set(&player->miditempo, event->param1); + fluid_player_update_tempo(player); + } + + fluid_track_next_event(track); + + } +} + +/****************************************************** + * + * fluid_player + */ +static void +fluid_player_handle_reset_synth(void *data, const char *name, int value) +{ + fluid_player_t *player = data; + fluid_return_if_fail(player != NULL); + + player->reset_synth_between_songs = value; +} + +/** + * Create a new MIDI player. + * @param synth Fluid synthesizer instance to create player for + * @return New MIDI player instance or NULL on error (out of memory) + */ +fluid_player_t * +new_fluid_player(fluid_synth_t *synth) +{ + int i; + fluid_player_t *player; + player = FLUID_NEW(fluid_player_t); + + if(player == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + fluid_atomic_int_set(&player->status, FLUID_PLAYER_READY); + fluid_atomic_int_set(&player->stopping, 0); + player->loop = 1; + player->ntracks = 0; + + for(i = 0; i < MAX_NUMBER_OF_TRACKS; i++) + { + player->track[i] = NULL; + } + + player->synth = synth; + player->system_timer = NULL; + player->sample_timer = NULL; + player->playlist = NULL; + player->currentfile = NULL; + player->division = 0; + + /* internal tempo (from MIDI file) in micro seconds per quarter note */ + player->sync_mode = 1; /* the player follows internal tempo change */ + player->miditempo = 500000; + /* external tempo in micro seconds per quarter note */ + player->exttempo = 500000; + /* tempo multiplier */ + player->multempo = 1.0F; + + player->deltatime = 4.0; + player->cur_msec = 0; + player->cur_ticks = 0; + player->last_callback_ticks = -1; + fluid_atomic_int_set(&player->seek_ticks, -1); + fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth); + fluid_player_set_tick_callback(player, NULL, NULL); + player->use_system_timer = fluid_settings_str_equal(synth->settings, + "player.timing-source", "system"); + if(player->use_system_timer) + { + player->system_timer = new_fluid_timer((int) player->deltatime, + fluid_player_callback, player, TRUE, FALSE, TRUE); + + if(player->system_timer == NULL) + { + goto err; + } + } + else + { + player->sample_timer = new_fluid_sample_timer(player->synth, + fluid_player_callback, player); + + if(player->sample_timer == NULL) + { + goto err; + } + } + + fluid_settings_getint(synth->settings, "player.reset-synth", &i); + fluid_player_handle_reset_synth(player, NULL, i); + + fluid_settings_callback_int(synth->settings, "player.reset-synth", + fluid_player_handle_reset_synth, player); + + return player; + +err: + delete_fluid_player(player); + return NULL; +} + +/** + * Delete a MIDI player instance. + * @param player MIDI player instance + * @warning Do not call when the synthesizer associated to this \p player renders audio, + * i.e. an audio driver is running or any other synthesizer thread concurrently calls + * fluid_synth_process() or fluid_synth_nwrite_float() or fluid_synth_write_*() ! + */ +void +delete_fluid_player(fluid_player_t *player) +{ + fluid_list_t *q; + fluid_playlist_item *pi; + + fluid_return_if_fail(player != NULL); + + fluid_settings_callback_int(player->synth->settings, "player.reset-synth", + NULL, NULL); + + fluid_player_stop(player); + fluid_player_reset(player); + + delete_fluid_timer(player->system_timer); + delete_fluid_sample_timer(player->synth, player->sample_timer); + + while(player->playlist != NULL) + { + q = player->playlist->next; + pi = (fluid_playlist_item *) player->playlist->data; + FLUID_FREE(pi->filename); + FLUID_FREE(pi->buffer); + FLUID_FREE(pi); + delete1_fluid_list(player->playlist); + player->playlist = q; + } + + FLUID_FREE(player); +} + +/** + * Registers settings related to the MIDI player + */ +void +fluid_player_settings(fluid_settings_t *settings) +{ + /* player.timing-source can be either "system" (use system timer) + or "sample" (use timer based on number of written samples) */ + fluid_settings_register_str(settings, "player.timing-source", "sample", 0); + fluid_settings_add_option(settings, "player.timing-source", "sample"); + fluid_settings_add_option(settings, "player.timing-source", "system"); + + /* Selects whether the player should reset the synth between songs, or not. */ + fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1, FLUID_HINT_TOGGLED); +} + + +int +fluid_player_reset(fluid_player_t *player) +{ + int i; + + for(i = 0; i < MAX_NUMBER_OF_TRACKS; i++) + { + if(player->track[i] != NULL) + { + delete_fluid_track(player->track[i]); + player->track[i] = NULL; + } + } + + for(i = 0; i < MAX_NUMBER_OF_CHANNELS; i++) + { + player->channel_isplaying[i] = FALSE; + } + + /* player->current_file = NULL; */ + /* player->status = FLUID_PLAYER_READY; */ + /* player->loop = 1; */ + player->ntracks = 0; + player->division = 0; + player->miditempo = 500000; + player->deltatime = 4.0; + return 0; +} + +/* + * fluid_player_add_track + */ +int +fluid_player_add_track(fluid_player_t *player, fluid_track_t *track) +{ + if(player->ntracks < MAX_NUMBER_OF_TRACKS) + { + player->track[player->ntracks++] = track; + return FLUID_OK; + } + else + { + return FLUID_FAILED; + } +} + +/** + * Change the MIDI callback function. + * + * @param player MIDI player instance + * @param handler Pointer to callback function + * @param handler_data Parameter sent to the callback function + * @returns FLUID_OK + * + * This is usually set to fluid_synth_handle_midi_event(), but can optionally + * be changed to a user-defined function instead, for intercepting all MIDI + * messages sent to the synth. You can also use a midi router as the callback + * function to modify the MIDI messages before sending them to the synth. + * + * @since 1.1.4 + */ +int +fluid_player_set_playback_callback(fluid_player_t *player, + handle_midi_event_func_t handler, void *handler_data) +{ + player->playback_callback = handler; + player->playback_userdata = handler_data; + return FLUID_OK; +} + +/** + * Add a listener function for every MIDI tick change. + * + * @param player MIDI player instance + * @param handler Pointer to callback function + * @param handler_data Opaque parameter to be sent to the callback function + * @returns #FLUID_OK + * + * This callback is not set by default, but can optionally + * be changed to a user-defined function for intercepting all MIDI + * tick changes and react to them with precision. + * + * @since 2.2.0 + */ +int +fluid_player_set_tick_callback(fluid_player_t *player, handle_midi_tick_func_t handler, void *handler_data) +{ + player->tick_callback = handler; + player->tick_userdata = handler_data; + return FLUID_OK; +} + +/** + * Add a MIDI file to a player queue. + * @param player MIDI player instance + * @param midifile File name of the MIDI file to add + * @return #FLUID_OK or #FLUID_FAILED + */ +int +fluid_player_add(fluid_player_t *player, const char *midifile) +{ + fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item)); + char *f = FLUID_STRDUP(midifile); + + if(!pi || !f) + { + FLUID_FREE(pi); + FLUID_FREE(f); + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + pi->filename = f; + pi->buffer = NULL; + pi->buffer_len = 0; + player->playlist = fluid_list_append(player->playlist, pi); + return FLUID_OK; +} + +/** + * Add a MIDI file to a player queue, from a buffer in memory. + * @param player MIDI player instance + * @param buffer Pointer to memory containing the bytes of a complete MIDI + * file. The data is copied, so the caller may free or modify it immediately + * without affecting the playlist. + * @param len Length of the buffer, in bytes. + * @return #FLUID_OK or #FLUID_FAILED + */ +int +fluid_player_add_mem(fluid_player_t *player, const void *buffer, size_t len) +{ + /* Take a copy of the buffer, so the caller can free immediately. */ + fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item)); + void *buf_copy = FLUID_MALLOC(len); + + if(!pi || !buf_copy) + { + FLUID_FREE(pi); + FLUID_FREE(buf_copy); + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + FLUID_MEMCPY(buf_copy, buffer, len); + pi->filename = NULL; + pi->buffer = buf_copy; + pi->buffer_len = len; + player->playlist = fluid_list_append(player->playlist, pi); + return FLUID_OK; +} + +/* + * fluid_player_load + */ +int +fluid_player_load(fluid_player_t *player, fluid_playlist_item *item) +{ + fluid_midi_file *midifile; + char *buffer; + size_t buffer_length; + int buffer_owned; + + if(item->filename != NULL) + { + fluid_file fp; + /* This file is specified by filename; load the file from disk */ + FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__, + item->filename); + /* Read the entire contents of the file into the buffer */ + fp = FLUID_FOPEN(item->filename, "rb"); + + if(fp == NULL) + { + FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file"); + return FLUID_FAILED; + } + + buffer = fluid_file_read_full(fp, &buffer_length); + + FLUID_FCLOSE(fp); + + if(buffer == NULL) + { + return FLUID_FAILED; + } + + buffer_owned = 1; + } + else + { + /* This file is specified by a pre-loaded buffer; load from memory */ + FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile from memory (%p)", + __FILE__, __LINE__, item->buffer); + buffer = (char *) item->buffer; + buffer_length = item->buffer_len; + /* Do not free the buffer (it is owned by the playlist) */ + buffer_owned = 0; + } + + midifile = new_fluid_midi_file(buffer, buffer_length); + + if(midifile == NULL) + { + if(buffer_owned) + { + FLUID_FREE(buffer); + } + + return FLUID_FAILED; + } + + player->division = fluid_midi_file_get_division(midifile); + fluid_player_update_tempo(player); // Update deltatime + /*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */ + + if(fluid_midi_file_load_tracks(midifile, player) != FLUID_OK) + { + if(buffer_owned) + { + FLUID_FREE(buffer); + } + + delete_fluid_midi_file(midifile); + return FLUID_FAILED; + } + + delete_fluid_midi_file(midifile); + + if(buffer_owned) + { + FLUID_FREE(buffer); + } + + return FLUID_OK; +} + +void +fluid_player_advancefile(fluid_player_t *player) +{ + if(player->playlist == NULL) + { + return; /* No files to play */ + } + + if(player->currentfile != NULL) + { + player->currentfile = fluid_list_next(player->currentfile); + } + + if(player->currentfile == NULL) + { + if(player->loop == 0) + { + return; /* We're done playing */ + } + + if(player->loop > 0) + { + player->loop--; + } + + player->currentfile = player->playlist; + } +} + +void +fluid_player_playlist_load(fluid_player_t *player, unsigned int msec) +{ + fluid_playlist_item *current_playitem; + int i; + + do + { + fluid_player_advancefile(player); + + if(player->currentfile == NULL) + { + /* Failed to find next song, probably since we're finished */ + fluid_atomic_int_set(&player->status, FLUID_PLAYER_DONE); + return; + } + + fluid_player_reset(player); + current_playitem = (fluid_playlist_item *) player->currentfile->data; + } + while(fluid_player_load(player, current_playitem) != FLUID_OK); + + /* Successfully loaded midi file */ + + player->begin_msec = msec; + player->start_msec = msec; + player->start_ticks = 0; + player->cur_ticks = 0; + + for(i = 0; i < player->ntracks; i++) + { + if(player->track[i] != NULL) + { + fluid_track_reset(player->track[i]); + } + } +} + +/* + * fluid_player_callback + */ +int +fluid_player_callback(void *data, unsigned int msec) +{ + int i; + int loadnextfile; + int status = FLUID_PLAYER_DONE; + fluid_midi_event_t mute_event; + fluid_player_t *player; + fluid_synth_t *synth; + player = (fluid_player_t *) data; + synth = player->synth; + + loadnextfile = player->currentfile == NULL ? 1 : 0; + + fluid_midi_event_set_type(&mute_event, CONTROL_CHANGE); + mute_event.param1 = ALL_SOUND_OFF; + mute_event.param2 = 1; + + if(fluid_player_get_status(player) != FLUID_PLAYER_PLAYING) + { + if(fluid_atomic_int_get(&player->stopping)) + { + for(i = 0; i < synth->midi_channels; i++) + { + if(player->channel_isplaying[i]) + { + fluid_midi_event_set_channel(&mute_event, i); + player->playback_callback(player->playback_userdata, &mute_event); + } + } + fluid_atomic_int_set(&player->stopping, 0); + } + return 1; + } + do + { + float deltatime; + int seek_ticks; + + if(loadnextfile) + { + loadnextfile = 0; + fluid_player_playlist_load(player, msec); + + if(player->currentfile == NULL) + { + return 0; + } + } + + player->cur_msec = msec; + deltatime = fluid_atomic_float_get(&player->deltatime); + player->cur_ticks = (player->start_ticks + + (int)((double)(player->cur_msec - player->start_msec) + / deltatime + 0.5)); /* 0.5 to average overall error when casting */ + + seek_ticks = fluid_atomic_int_get(&player->seek_ticks); + if(seek_ticks >= 0) + { + for(i = 0; i < synth->midi_channels; i++) + { + if(player->channel_isplaying[i]) + { + fluid_midi_event_set_channel(&mute_event, i); + player->playback_callback(player->playback_userdata, &mute_event); + } + } + } + + for(i = 0; i < player->ntracks; i++) + { + if(!fluid_track_eot(player->track[i])) + { + status = FLUID_PLAYER_PLAYING; + fluid_track_send_events(player->track[i], synth, player, player->cur_ticks, seek_ticks); + } + } + + if(seek_ticks >= 0) + { + player->start_ticks = seek_ticks; /* tick position of last tempo value (which is now) */ + player->cur_ticks = seek_ticks; + player->begin_msec = msec; /* only used to calculate the duration of playing */ + player->start_msec = msec; /* should be the (synth)-time of the last tempo change */ + fluid_atomic_int_set(&player->seek_ticks, -1); /* clear seek_ticks */ + } + + if(status == FLUID_PLAYER_DONE) + { + FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__, + __LINE__, (msec - player->begin_msec) / 1000.0); + + if(player->reset_synth_between_songs) + { + fluid_synth_system_reset(player->synth); + } + + loadnextfile = 1; + } + + if (player->tick_callback != NULL && player->last_callback_ticks != player->cur_ticks) { + player->tick_callback(player->tick_userdata, player->cur_ticks); + player->last_callback_ticks = player->cur_ticks; + } + } + while(loadnextfile); + + /* do not update the status if the player has been stopped already */ + fluid_atomic_int_compare_and_exchange(&player->status, FLUID_PLAYER_PLAYING, status); + + return 1; +} + +/** + * Activates play mode for a MIDI player if not already playing. + * @param player MIDI player instance + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * If the list of files added to the player has completed its requested number of loops, + * the playlist will be restarted from the beginning with a loop count of 1. + */ +int +fluid_player_play(fluid_player_t *player) +{ + if(fluid_player_get_status(player) == FLUID_PLAYER_PLAYING || + player->playlist == NULL) + { + return FLUID_OK; + } + + if(!player->use_system_timer) + { + fluid_sample_timer_reset(player->synth, player->sample_timer); + } + + /* If we're at the end of the playlist and there are no loops left, loop once */ + if(player->currentfile == NULL && player->loop == 0) + { + player->loop = 1; + } + + fluid_atomic_int_set(&player->status, FLUID_PLAYER_PLAYING); + + return FLUID_OK; +} + +/** + * Pauses the MIDI playback. + * + * @param player MIDI player instance + * @return Always returns #FLUID_OK + * + * It will not rewind to the beginning of the file, use fluid_player_seek() for this purpose. + */ +int +fluid_player_stop(fluid_player_t *player) +{ + fluid_atomic_int_set(&player->status, FLUID_PLAYER_DONE); + fluid_atomic_int_set(&player->stopping, 1); + fluid_player_seek(player, fluid_player_get_current_tick(player)); + return FLUID_OK; +} + +/** + * Get MIDI player status. + * @param player MIDI player instance + * @return Player status (#fluid_player_status) + * @since 1.1.0 + */ +int +fluid_player_get_status(fluid_player_t *player) +{ + return fluid_atomic_int_get(&player->status); +} + +/** + * Seek in the currently playing file. + * + * @param player MIDI player instance + * @param ticks the position to seek to in the current file + * @return #FLUID_FAILED if ticks is negative or after the latest tick of the file + * [or, since 2.1.3, if another seek operation is currently in progress], + * #FLUID_OK otherwise. + * + * The actual seek will be performed when the synth calls back the player (i.e. a few + * levels above the player's callback set with fluid_player_set_playback_callback()). + * If the player's status is #FLUID_PLAYER_PLAYING and a previous seek operation has + * not been completed yet, #FLUID_FAILED is returned. + * + * @since 2.0.0 + */ +int fluid_player_seek(fluid_player_t *player, int ticks) +{ + if(ticks < 0 || (fluid_player_get_status(player) != FLUID_PLAYER_READY && ticks > fluid_player_get_total_ticks(player))) + { + return FLUID_FAILED; + } + + if(fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) + { + if(fluid_atomic_int_compare_and_exchange(&player->seek_ticks, -1, ticks)) + { + // new seek position has been set, as no previous seek was in progress + return FLUID_OK; + } + } + else + { + // If the player is not currently playing, a new seek position can be set at any time. This allows + // the user to do: + // fluid_player_stop(); + // fluid_player_seek(0); // to beginning + fluid_atomic_int_set(&player->seek_ticks, ticks); + return FLUID_OK; + } + + // a previous seek is still in progress or hasn't been processed yet + return FLUID_FAILED; +} + + +/** + * Enable looping of a MIDI player + * + * @param player MIDI player instance + * @param loop Times left to loop the playlist. -1 means loop infinitely. + * @return Always returns #FLUID_OK + * + * For example, if you want to loop the playlist twice, set loop to 2 + * and call this function before you start the player. + * + * @since 1.1.0 + */ +int fluid_player_set_loop(fluid_player_t *player, int loop) +{ + player->loop = loop; + return FLUID_OK; +} + +/** + * update the MIDI player internal deltatime dependent of actual tempo. + * @param player MIDI player instance + */ +static void fluid_player_update_tempo(fluid_player_t *player) +{ + int tempo; /* tempo in micro seconds by quarter note */ + float deltatime; + + /* do nothing if the division is still unknown to avoid a div by zero */ + if(player->division == 0) + { + return; + } + + if(fluid_atomic_int_get(&player->sync_mode)) + { + /* take internal tempo from MIDI file */ + tempo = fluid_atomic_int_get(&player->miditempo); + /* compute deltattime (in ms) from current tempo and apply tempo multiplier */ + deltatime = (float)tempo / (float)player->division / (float)1000.0; + deltatime /= fluid_atomic_float_get(&player->multempo); /* multiply tempo */ + } + else + { + /* take external tempo */ + tempo = fluid_atomic_int_get(&player->exttempo); + /* compute deltattime (in ms) from current tempo */ + deltatime = (float)tempo / (float)player->division / (float)1000.0; + } + + fluid_atomic_float_set(&player->deltatime, deltatime); + + player->start_msec = player->cur_msec; + player->start_ticks = player->cur_ticks; + + FLUID_LOG(FLUID_DBG, + "tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d", + tempo, player->deltatime, player->cur_msec, player->cur_ticks); + +} + +/** + * Set the tempo of a MIDI player. + * The player can be controlled by internal tempo coming from MIDI file tempo + * change or controlled by external tempo expressed in BPM or in micro seconds + * per quarter note. + * + * @param player MIDI player instance. Must be a valid pointer. + * @param tempo_type Must a be value of #fluid_player_set_tempo_type and indicates the + * meaning of tempo value and how the player will be controlled, see below. + * @param tempo Tempo value or multiplier. + * + * #FLUID_PLAYER_TEMPO_INTERNAL, the player will be controlled by internal + * MIDI file tempo changes. If there are no tempo change in the MIDI file a default + * value of 120 bpm is used. The @c tempo parameter is used as a multiplier factor + * that must be in the range (0.001 to 1000). + * For example, if the current MIDI file tempo is 120 bpm and the multiplier + * value is 0.5 then this tempo will be slowed down to 60 bpm. + * At creation, the player is set to be controlled by internal tempo with a + * multiplier factor set to 1.0. + * + * #FLUID_PLAYER_TEMPO_EXTERNAL_BPM, the player will be controlled by the + * external tempo value provided by the tempo parameter in bpm + * (i.e in quarter notes per minute) which must be in the range (1 to 60000000). + * + * #FLUID_PLAYER_TEMPO_EXTERNAL_MIDI, similar as FLUID_PLAYER_TEMPO_EXTERNAL_BPM, + * but the tempo parameter value is in micro seconds per quarter note which + * must be in the range (1 to 60000000). + * Using micro seconds per quarter note is convenient when the tempo value is + * derived from MIDI clock realtime messages. + * + * @note When the player is controlled by an external tempo + * (#FLUID_PLAYER_TEMPO_EXTERNAL_BPM or #FLUID_PLAYER_TEMPO_EXTERNAL_MIDI) it + * continues to memorize the most recent internal tempo change coming from the + * MIDI file so that next call to fluid_player_set_tempo() with + * #FLUID_PLAYER_TEMPO_INTERNAL will set the player to follow this internal + * tempo. + * + * @warning If the function is called when no MIDI file is loaded or currently playing, it + * would have caused a division by zero in fluidsynth 2.2.7 and earlier. Starting with 2.2.8, the + * new tempo change will be stashed and applied later. + * + * @return #FLUID_OK if success or #FLUID_FAILED otherwise (incorrect parameters). + * @since 2.2.0 + */ +int fluid_player_set_tempo(fluid_player_t *player, int tempo_type, double tempo) +{ + fluid_return_val_if_fail(player != NULL, FLUID_FAILED); + fluid_return_val_if_fail(tempo_type >= FLUID_PLAYER_TEMPO_INTERNAL, FLUID_FAILED); + fluid_return_val_if_fail(tempo_type < FLUID_PLAYER_TEMPO_NBR, FLUID_FAILED); + + switch(tempo_type) + { + /* set the player to be driven by internal tempo coming from MIDI file */ + case FLUID_PLAYER_TEMPO_INTERNAL: + /* check if the multiplier is in correct range */ + fluid_return_val_if_fail(tempo >= MIN_TEMPO_MULTIPLIER, FLUID_FAILED); + fluid_return_val_if_fail(tempo <= MAX_TEMPO_MULTIPLIER, FLUID_FAILED); + + /* set the tempo multiplier */ + fluid_atomic_float_set(&player->multempo, (float)tempo); + fluid_atomic_int_set(&player->sync_mode, 1); /* set internal mode */ + break; + + /* set the player to be driven by external tempo */ + case FLUID_PLAYER_TEMPO_EXTERNAL_BPM: /* value in bpm */ + case FLUID_PLAYER_TEMPO_EXTERNAL_MIDI: /* value in us/quarter note */ + /* check if tempo is in correct range */ + fluid_return_val_if_fail(tempo >= MIN_TEMPO_VALUE, FLUID_FAILED); + fluid_return_val_if_fail(tempo <= MAX_TEMPO_VALUE, FLUID_FAILED); + + /* set the tempo value */ + if(tempo_type == FLUID_PLAYER_TEMPO_EXTERNAL_BPM) + { + tempo = 60000000L / tempo; /* convert tempo in us/quarter note */ + } + fluid_atomic_int_set(&player->exttempo, (int)tempo); + fluid_atomic_int_set(&player->sync_mode, 0); /* set external mode */ + break; + + default: /* shouldn't happen */ + break; + } + + /* update deltatime depending of current tempo */ + fluid_player_update_tempo(player); + + return FLUID_OK; +} + +/** + * Set the tempo of a MIDI player. + * @param player MIDI player instance + * @param tempo Tempo to set playback speed to (in microseconds per quarter note, as per MIDI file spec) + * @return Always returns #FLUID_OK + * @note Tempo change events contained in the MIDI file can override the specified tempo at any time! + * @deprecated Use fluid_player_set_tempo() instead. + */ +int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) +{ + player->miditempo = tempo; + + fluid_player_update_tempo(player); + return FLUID_OK; +} + +/** + * Set the tempo of a MIDI player in beats per minute. + * @param player MIDI player instance + * @param bpm Tempo in beats per minute + * @return Always returns #FLUID_OK + * @note Tempo change events contained in the MIDI file can override the specified BPM at any time! + * @deprecated Use fluid_player_set_tempo() instead. + */ +int fluid_player_set_bpm(fluid_player_t *player, int bpm) +{ + if(bpm <= 0) + { + return FLUID_FAILED; /* to avoid a division by 0 */ + } + + return fluid_player_set_midi_tempo(player, 60000000L / bpm); +} + +/** + * Wait for a MIDI player until the playback has been stopped. + * @param player MIDI player instance + * @return Always #FLUID_OK + * + * @warning The player may still be used by a concurrently running synth context. Hence it is + * not safe to immediately delete the player afterwards. Also refer to delete_fluid_player(). + */ +int +fluid_player_join(fluid_player_t *player) +{ + while(fluid_player_get_status(player) != FLUID_PLAYER_DONE) + { + fluid_msleep(10); + } + return FLUID_OK; +} + +/** + * Get the number of tempo ticks passed. + * @param player MIDI player instance + * @return The number of tempo ticks passed + * @since 1.1.7 + */ +int fluid_player_get_current_tick(fluid_player_t *player) +{ + return player->cur_ticks; +} + +/** + * Looks through all available MIDI tracks and gets the absolute tick of the very last event to play. + * @param player MIDI player instance + * @return Total tick count of the sequence + * @since 1.1.7 + */ +int fluid_player_get_total_ticks(fluid_player_t *player) +{ + int i; + int maxTicks = 0; + + for(i = 0; i < player->ntracks; i++) + { + if(player->track[i] != NULL) + { + int ticks = fluid_track_get_duration(player->track[i]); + + if(ticks > maxTicks) + { + maxTicks = ticks; + } + } + } + + return maxTicks; +} + +/** + * Get the tempo currently used by a MIDI player. + * The player can be controlled by internal tempo coming from MIDI file tempo + * change or controlled by external tempo see fluid_player_set_tempo(). + * @param player MIDI player instance. Must be a valid pointer. + * @return MIDI player tempo in BPM or FLUID_FAILED if error. + * @since 1.1.7 + */ +int fluid_player_get_bpm(fluid_player_t *player) +{ + + int midi_tempo = fluid_player_get_midi_tempo(player); + + if(midi_tempo > 0) + { + midi_tempo = 60000000L / midi_tempo; /* convert in bpm */ + } + + return midi_tempo; +} + +/** + * Get the tempo currently used by a MIDI player. + * The player can be controlled by internal tempo coming from MIDI file tempo + * change or controlled by external tempo see fluid_player_set_tempo(). + + * @param player MIDI player instance. Must be a valid pointer. + * @return Tempo of the MIDI player (in microseconds per quarter note, as per + * MIDI file spec) or FLUID_FAILED if error. + * @since 1.1.7 + */ +int fluid_player_get_midi_tempo(fluid_player_t *player) +{ + int midi_tempo; /* value to return */ + + fluid_return_val_if_fail(player != NULL, FLUID_FAILED); + + midi_tempo = fluid_atomic_int_get(&player->exttempo); + /* look if the player is internally synced */ + if(fluid_atomic_int_get(&player->sync_mode)) + { + midi_tempo = (int)((float)fluid_atomic_int_get(&player->miditempo)/ + fluid_atomic_float_get(&player->multempo)); + } + + return midi_tempo; +} + +/************************************************************************ + * MIDI PARSER + * + */ + +/* + * new_fluid_midi_parser + */ +fluid_midi_parser_t * +new_fluid_midi_parser() +{ + fluid_midi_parser_t *parser; + parser = FLUID_NEW(fluid_midi_parser_t); + + if(parser == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + parser->status = 0; /* As long as the status is 0, the parser won't do anything -> no need to initialize all the fields. */ + return parser; +} + +/* + * delete_fluid_midi_parser + */ +void +delete_fluid_midi_parser(fluid_midi_parser_t *parser) +{ + fluid_return_if_fail(parser != NULL); + + FLUID_FREE(parser); +} + +/** + * Parse a MIDI stream one character at a time. + * @param parser Parser instance + * @param c Next character in MIDI stream + * @return A parsed MIDI event or NULL if none. Event is internal and should + * not be modified or freed and is only valid until next call to this function. + * @internal Do not expose this function to the public API. It would allow downstream + * apps to abuse fluidsynth as midi parser, e.g. feeding it with rawmidi and pull out + * the needed midi information using the getter functions of fluid_midi_event_t. + * This parser however is incomplete as it e.g. only provides a limited buffer to + * store and process SYSEX data (i.e. doesn't allow arbitrary lengths) + */ +fluid_midi_event_t * +fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) +{ + fluid_midi_event_t *event; + + /* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle + * of another message. */ + if(c >= 0xF8) + { + parser->event.type = c; + parser->status = 0; /* clear the status */ + return &parser->event; + } + + /* Status byte? - If previous message not yet complete, it is discarded (re-sync). */ + if(c & 0x80) + { + /* Any status byte terminates SYSEX messages (not just 0xF7) */ + if(parser->status == MIDI_SYSEX && parser->nr_bytes > 0) + { + event = &parser->event; + fluid_midi_event_set_sysex(event, parser->data, parser->nr_bytes, + FALSE); + } + else + { + event = NULL; + } + + if(c < 0xF0) /* Voice category message? */ + { + parser->channel = c & 0x0F; + parser->status = c & 0xF0; + + /* The event consumes x bytes of data... (subtract 1 for the status byte) */ + parser->nr_bytes_total = fluid_midi_event_length(parser->status) + - 1; + + parser->nr_bytes = 0; /* 0 bytes read so far */ + } + else if(c == MIDI_SYSEX) + { + parser->status = MIDI_SYSEX; + parser->nr_bytes = 0; + } + else + { + parser->status = 0; /* Discard other system messages (0xF1-0xF7) */ + } + + return event; /* Return SYSEX event or NULL */ + } + + /* Data/parameter byte */ + + /* Discard data bytes for events we don't care about */ + if(parser->status == 0) + { + return NULL; + } + + /* Max data size exceeded? (SYSEX messages only really) */ + if(parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE) + { + parser->status = 0; /* Discard the rest of the message */ + return NULL; + } + + /* Store next byte */ + parser->data[parser->nr_bytes++] = c; + + /* Do we still need more data to get this event complete? */ + if(parser->status == MIDI_SYSEX || parser->nr_bytes < parser->nr_bytes_total) + { + return NULL; + } + + /* Event is complete, return it. + * Running status byte MIDI feature is also handled here. */ + parser->event.type = parser->status; + parser->event.channel = parser->channel; + parser->nr_bytes = 0; /* Reset data size, in case there are additional running status messages */ + + switch(parser->status) + { + case NOTE_OFF: + case NOTE_ON: + case KEY_PRESSURE: + case CONTROL_CHANGE: + case PROGRAM_CHANGE: + case CHANNEL_PRESSURE: + parser->event.param1 = parser->data[0]; /* For example key number */ + parser->event.param2 = parser->data[1]; /* For example velocity */ + break; + + case PITCH_BEND: + /* Pitch-bend is transmitted with 14-bit precision. */ + parser->event.param1 = (parser->data[1] << 7) | parser->data[0]; + break; + + default: /* Unlikely */ + return NULL; + } + + return &parser->event; +} + +/* Purpose: + * Returns the length of a MIDI message. */ +static int +fluid_midi_event_length(unsigned char event) +{ + switch(event & 0xF0) + { + case NOTE_OFF: + case NOTE_ON: + case KEY_PRESSURE: + case CONTROL_CHANGE: + case PITCH_BEND: + return 3; + + case PROGRAM_CHANGE: + case CHANNEL_PRESSURE: + return 2; + } + + switch(event) + { + case MIDI_TIME_CODE: + case MIDI_SONG_SELECT: + case 0xF4: + case 0xF5: + return 2; + + case MIDI_TUNE_REQUEST: + return 1; + + case MIDI_SONG_POSITION: + return 3; + } + + return 1; +} diff --git a/thirdparty/fluidsynth/src/midi/fluid_midi.h b/thirdparty/fluidsynth/src/midi/fluid_midi.h new file mode 100644 index 000000000..f3c7d6998 --- /dev/null +++ b/thirdparty/fluidsynth/src/midi/fluid_midi.h @@ -0,0 +1,380 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_MIDI_H +#define _FLUID_MIDI_H + +#include "fluidsynth_priv.h" +#include "fluid_sys.h" +#include "fluid_list.h" + +typedef struct _fluid_midi_parser_t fluid_midi_parser_t; + +fluid_midi_parser_t *new_fluid_midi_parser(void); +void delete_fluid_midi_parser(fluid_midi_parser_t *parser); +fluid_midi_event_t *fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c); + + +/*************************************************************** + * + * CONSTANTS & ENUM + */ + + +#define MAX_NUMBER_OF_TRACKS 128 +#define MAX_NUMBER_OF_CHANNELS 16 + +enum fluid_midi_event_type +{ + /* channel messages */ + NOTE_OFF = 0x80, + NOTE_ON = 0x90, + KEY_PRESSURE = 0xa0, + CONTROL_CHANGE = 0xb0, + PROGRAM_CHANGE = 0xc0, + CHANNEL_PRESSURE = 0xd0, + PITCH_BEND = 0xe0, + /* system exclusive */ + MIDI_SYSEX = 0xf0, + /* system common - never in midi files */ + MIDI_TIME_CODE = 0xf1, + MIDI_SONG_POSITION = 0xf2, + MIDI_SONG_SELECT = 0xf3, + MIDI_TUNE_REQUEST = 0xf6, + MIDI_EOX = 0xf7, + /* system real-time - never in midi files */ + MIDI_SYNC = 0xf8, + MIDI_TICK = 0xf9, + MIDI_START = 0xfa, + MIDI_CONTINUE = 0xfb, + MIDI_STOP = 0xfc, + MIDI_ACTIVE_SENSING = 0xfe, + MIDI_SYSTEM_RESET = 0xff, + /* meta event - for midi files only */ + MIDI_META_EVENT = 0xff +}; + +enum fluid_midi_control_change +{ + BANK_SELECT_MSB = 0x00, + MODULATION_MSB = 0x01, + BREATH_MSB = 0x02, + FOOT_MSB = 0x04, + PORTAMENTO_TIME_MSB = 0x05, + DATA_ENTRY_MSB = 0x06, + VOLUME_MSB = 0x07, + BALANCE_MSB = 0x08, + PAN_MSB = 0x0A, + EXPRESSION_MSB = 0x0B, + EFFECTS1_MSB = 0x0C, + EFFECTS2_MSB = 0x0D, + GPC1_MSB = 0x10, /* general purpose controller */ + GPC2_MSB = 0x11, + GPC3_MSB = 0x12, + GPC4_MSB = 0x13, + BANK_SELECT_LSB = 0x20, + MODULATION_WHEEL_LSB = 0x21, + BREATH_LSB = 0x22, + FOOT_LSB = 0x24, + PORTAMENTO_TIME_LSB = 0x25, + DATA_ENTRY_LSB = 0x26, + VOLUME_LSB = 0x27, + BALANCE_LSB = 0x28, + PAN_LSB = 0x2A, + EXPRESSION_LSB = 0x2B, + EFFECTS1_LSB = 0x2C, + EFFECTS2_LSB = 0x2D, + GPC1_LSB = 0x30, + GPC2_LSB = 0x31, + GPC3_LSB = 0x32, + GPC4_LSB = 0x33, + SUSTAIN_SWITCH = 0x40, + PORTAMENTO_SWITCH = 0x41, + SOSTENUTO_SWITCH = 0x42, + SOFT_PEDAL_SWITCH = 0x43, + LEGATO_SWITCH = 0x44, + HOLD2_SWITCH = 0x45, + SOUND_CTRL1 = 0x46, + SOUND_CTRL2 = 0x47, + SOUND_CTRL3 = 0x48, + SOUND_CTRL4 = 0x49, + SOUND_CTRL5 = 0x4A, + SOUND_CTRL6 = 0x4B, + SOUND_CTRL7 = 0x4C, + SOUND_CTRL8 = 0x4D, + SOUND_CTRL9 = 0x4E, + SOUND_CTRL10 = 0x4F, + GPC5 = 0x50, + GPC6 = 0x51, + GPC7 = 0x52, + GPC8 = 0x53, + PORTAMENTO_CTRL = 0x54, + EFFECTS_DEPTH1 = 0x5B, + EFFECTS_DEPTH2 = 0x5C, + EFFECTS_DEPTH3 = 0x5D, + EFFECTS_DEPTH4 = 0x5E, + EFFECTS_DEPTH5 = 0x5F, + DATA_ENTRY_INCR = 0x60, + DATA_ENTRY_DECR = 0x61, + NRPN_LSB = 0x62, + NRPN_MSB = 0x63, + RPN_LSB = 0x64, + RPN_MSB = 0x65, + ALL_SOUND_OFF = 0x78, + ALL_CTRL_OFF = 0x79, + LOCAL_CONTROL = 0x7A, + ALL_NOTES_OFF = 0x7B, + OMNI_OFF = 0x7C, + OMNI_ON = 0x7D, + POLY_OFF = 0x7E, + POLY_ON = 0x7F +}; + +/* General MIDI RPN event numbers (LSB, MSB = 0) */ +enum midi_rpn_event +{ + RPN_PITCH_BEND_RANGE = 0x00, + RPN_CHANNEL_FINE_TUNE = 0x01, + RPN_CHANNEL_COARSE_TUNE = 0x02, + RPN_TUNING_PROGRAM_CHANGE = 0x03, + RPN_TUNING_BANK_SELECT = 0x04, + RPN_MODULATION_DEPTH_RANGE = 0x05 +}; + +enum midi_meta_event +{ + MIDI_TEXT = 0x01, + MIDI_COPYRIGHT = 0x02, + MIDI_TRACK_NAME = 0x03, + MIDI_INST_NAME = 0x04, + MIDI_LYRIC = 0x05, + MIDI_MARKER = 0x06, + MIDI_CUE_POINT = 0x07, + MIDI_EOT = 0x2f, + MIDI_SET_TEMPO = 0x51, + MIDI_SMPTE_OFFSET = 0x54, + MIDI_TIME_SIGNATURE = 0x58, + MIDI_KEY_SIGNATURE = 0x59, + MIDI_SEQUENCER_EVENT = 0x7f +}; + +/* MIDI SYSEX useful manufacturer values */ +enum midi_sysex_manuf +{ + MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */ + MIDI_SYSEX_MANUF_YAMAHA = 0x43, + MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */ + MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */ +}; + +#define MIDI_SYSEX_DEVICE_ID_ALL 0x7F /**< Device ID used in SYSEX messages to indicate all devices */ + +/* SYSEX sub-ID #1 which follows device ID */ +#define MIDI_SYSEX_MIDI_TUNING_ID 0x08 /**< Sysex sub-ID #1 for MIDI tuning messages */ +#define MIDI_SYSEX_GM_ID 0x09 /**< Sysex sub-ID #1 for General MIDI messages */ +#define MIDI_SYSEX_GS_ID 0x42 /**< Model ID (GS) serving as sub-ID #1 for GS messages*/ +#define MIDI_SYSEX_XG_ID 0x4C /**< Model ID (XG) serving as sub-ID #1 for XG messages*/ + +/** + * SYSEX tuning message IDs. + */ +enum midi_sysex_tuning_msg_id +{ + MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */ + MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */ + MIDI_SYSEX_TUNING_NOTE_TUNE = 0x02, /**< Tuning note change message (realtime) */ + MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK = 0x03, /**< Bulk tuning dump request (with bank, non-realtime) */ + MIDI_SYSEX_TUNING_BULK_DUMP_BANK = 0x04, /**< Bulk tuning dump response (with bank, non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_DUMP_1BYTE = 0x05, /**< Octave tuning dump using 1 byte values (non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_DUMP_2BYTE = 0x06, /**< Octave tuning dump using 2 byte values (non-realtime) */ + MIDI_SYSEX_TUNING_NOTE_TUNE_BANK = 0x07, /**< Tuning note change message (with bank, realtime/non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE = 0x08, /**< Octave tuning message using 1 byte values (realtime/non-realtime) */ + MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */ +}; + +/* General MIDI sub-ID #2 */ +#define MIDI_SYSEX_GM_ON 0x01 /**< Enable GM mode */ +#define MIDI_SYSEX_GM_OFF 0x02 /**< Disable GM mode */ +#define MIDI_SYSEX_GM2_ON 0x03 /**< Enable GM2 mode */ +#define MIDI_SYSEX_GS_DT1 0x12 /**< GS DT1 command */ + +enum fluid_driver_status +{ + FLUID_MIDI_READY, + FLUID_MIDI_LISTENING, + FLUID_MIDI_DONE +}; + +/*************************************************************** + * + * TYPE DEFINITIONS & FUNCTION DECLARATIONS + */ + +/* + * fluid_midi_event_t + */ +struct _fluid_midi_event_t +{ + fluid_midi_event_t *next; /* Link to next event */ + void *paramptr; /* Pointer parameter (for SYSEX data), size is stored to param1, param2 indicates if pointer should be freed (dynamic if TRUE) */ + unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */ + unsigned int param1; /* First parameter */ + unsigned int param2; /* Second parameter */ + unsigned char type; /* MIDI event type */ + unsigned char channel; /* MIDI channel */ +}; + + +/* + * fluid_track_t + */ +struct _fluid_track_t +{ + char *name; + int num; + fluid_midi_event_t *first; + fluid_midi_event_t *cur; + fluid_midi_event_t *last; + unsigned int ticks; +}; + +typedef struct _fluid_track_t fluid_track_t; + +#define fluid_track_eot(track) ((track)->cur == NULL) + + +/* + * fluid_playlist_item + * Used as the `data' elements of the fluid_player.playlist. + * Represents either a filename or a pre-loaded memory buffer. + * Exactly one of `filename' and `buffer' is non-NULL. + */ +typedef struct +{ + char *filename; /** Name of file (owned); NULL if data pre-loaded */ + void *buffer; /** The MIDI file data (owned); NULL if filename */ + size_t buffer_len; /** Number of bytes in buffer; 0 if filename */ +} fluid_playlist_item; + +/* range of tempo values */ +#define MIN_TEMPO_VALUE (1.0f) +#define MAX_TEMPO_VALUE (60000000.0f) +/* range of tempo multiplier values */ +#define MIN_TEMPO_MULTIPLIER (0.001f) +#define MAX_TEMPO_MULTIPLIER (1000.0f) + +/* + * fluid_player + */ +struct _fluid_player_t +{ + fluid_atomic_int_t status; + fluid_atomic_int_t stopping; /* Flag for sending all_notes_off when player is stopped */ + int ntracks; + fluid_track_t *track[MAX_NUMBER_OF_TRACKS]; + fluid_synth_t *synth; + fluid_timer_t *system_timer; + fluid_sample_timer_t *sample_timer; + + int loop; /* -1 = loop infinitely, otherwise times left to loop the playlist */ + fluid_list_t *playlist; /* List of fluid_playlist_item* objects */ + fluid_list_t *currentfile; /* points to an item in files, or NULL if not playing */ + + char use_system_timer; /* if zero, use sample timers, otherwise use system clock timer */ + char reset_synth_between_songs; /* 1 if system reset should be sent to the synth between songs. */ + fluid_atomic_int_t seek_ticks; /* new position in tempo ticks (midi ticks) for seeking */ + int start_ticks; /* the number of tempo ticks passed at the last tempo change */ + int cur_ticks; /* the number of tempo ticks passed */ + int last_callback_ticks; /* the last tick number that was passed to player->tick_callback */ + int begin_msec; /* the time (msec) of the beginning of the file */ + int start_msec; /* the start time of the last tempo change */ + int cur_msec; /* the current time */ + /* sync mode: indicates the tempo mode the player is driven by (see fluid_player_set_tempo()): + 1, the player is driven by internal tempo (miditempo). This is the default. + 0, the player is driven by external tempo (exttempo) + */ + int sync_mode; + /* miditempo: internal tempo coming from MIDI file tempo change events + (in micro seconds per quarter note) + */ + int miditempo; /* as indicated by MIDI SetTempo: n 24th of a usec per midi-clock. bravo! */ + /* exttempo: external tempo set by fluid_player_set_tempo() (in micro seconds per quarter note) */ + int exttempo; + /* multempo: tempo multiplier set by fluid_player_set_tempo() */ + float multempo; + float deltatime; /* milliseconds per midi tick. depends on current tempo mode (see sync_mode) */ + unsigned int division; + + handle_midi_event_func_t playback_callback; /* function fired on each midi event as it is played */ + void *playback_userdata; /* pointer to user-defined data passed to playback_callback function */ + handle_midi_tick_func_t tick_callback; /* function fired on each tick change */ + void *tick_userdata; /* pointer to user-defined data passed to tick_callback function */ + + int channel_isplaying[MAX_NUMBER_OF_CHANNELS]; /* flags indicating channels on which notes have played */ +}; + +void fluid_player_settings(fluid_settings_t *settings); + + +/* + * fluid_midi_file + */ +typedef struct +{ + const char *buffer; /* Entire contents of MIDI file (borrowed) */ + int buf_len; /* Length of buffer, in bytes */ + int buf_pos; /* Current read position in contents buffer */ + int eof; /* The "end of file" condition */ + int running_status; + int c; + int type; + int ntracks; + int uses_smpte; + unsigned int smpte_fps; + unsigned int smpte_res; + unsigned int division; /* If uses_SMPTE == 0 then division is + ticks per beat (quarter-note) */ + double tempo; /* Beats per second (SI rules =) */ + int tracklen; + int trackpos; + int eot; + int varlen; + int dtime; +} fluid_midi_file; + + + +#define FLUID_MIDI_PARSER_MAX_DATA_SIZE 1024 /**< Maximum size of MIDI parameters/data (largest is SYSEX data) */ + +/* + * fluid_midi_parser_t + */ +struct _fluid_midi_parser_t +{ + unsigned char status; /* Identifies the type of event, that is currently received ('Noteon', 'Pitch Bend' etc). */ + unsigned char channel; /* The channel of the event that is received (in case of a channel event) */ + unsigned int nr_bytes; /* How many bytes have been read for the current event? */ + unsigned int nr_bytes_total; /* How many bytes does the current event type include? */ + unsigned char data[FLUID_MIDI_PARSER_MAX_DATA_SIZE]; /* The parameters or SYSEX data */ + fluid_midi_event_t event; /* The event, that is returned to the MIDI driver. */ +}; + + +#endif /* _FLUID_MIDI_H */ diff --git a/thirdparty/fluidsynth/src/midi/fluid_midi_router.c b/thirdparty/fluidsynth/src/midi/fluid_midi_router.c new file mode 100644 index 000000000..d3928274b --- /dev/null +++ b/thirdparty/fluidsynth/src/midi/fluid_midi_router.c @@ -0,0 +1,974 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* Original author: Markus Nentwig, nentwig@users.sourceforge.net + * + * Josh Green made it general purpose with a complete usable public API and + * cleaned it up a bit. + */ + +#include "fluid_midi_router.h" +#include "fluid_midi.h" +#include "fluid_synth.h" + +/* + * fluid_midi_router + */ +struct _fluid_midi_router_t +{ + fluid_mutex_t rules_mutex; + fluid_midi_router_rule_t *rules[FLUID_MIDI_ROUTER_RULE_COUNT]; /* List of rules for each rule type */ + fluid_midi_router_rule_t *free_rules; /* List of rules to free (was waiting for final events which were received) */ + + handle_midi_event_func_t event_handler; /* Callback function for generated events */ + void *event_handler_data; /* One arg for the callback */ + + int nr_midi_channels; /* For clipping the midi channel */ +}; + +struct _fluid_midi_router_rule_t +{ + int chan_min; /* Channel window, for which this rule is valid */ + int chan_max; + fluid_real_t chan_mul; /* Channel multiplier (usually 0 or 1) */ + int chan_add; /* Channel offset */ + + int par1_min; /* Parameter 1 window, for which this rule is valid */ + int par1_max; + fluid_real_t par1_mul; + int par1_add; + + int par2_min; /* Parameter 2 window, for which this rule is valid */ + int par2_max; + fluid_real_t par2_mul; + int par2_add; + + int pending_events; /* In case of noteon: How many keys are still down? */ + char keys_cc[128]; /* Flags, whether a key is down / controller is set (sustain) */ + fluid_midi_router_rule_t *next; /* next entry */ + int waiting; /* Set to TRUE when rule has been deactivated but there are still pending_events */ +}; + + +/** + * Create a new midi router. + * + * @param settings Settings used to configure MIDI router + * @param handler MIDI event callback. + * @param event_handler_data Caller defined data pointer which gets passed to 'handler' + * @return New MIDI router instance or NULL on error + * + * The new router will start with default rules and therefore pass all events unmodified. + * + * The MIDI handler callback should process the possibly filtered/modified MIDI + * events from the MIDI router and forward them on to a synthesizer for example. + * The function fluid_synth_handle_midi_event() can be used for \a handle and + * a #fluid_synth_t passed as the \a event_handler_data parameter for this purpose. + */ +fluid_midi_router_t * +new_fluid_midi_router(fluid_settings_t *settings, handle_midi_event_func_t handler, + void *event_handler_data) +{ + fluid_midi_router_t *router = NULL; + int i; + + router = FLUID_NEW(fluid_midi_router_t); + + if(router == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(router, 0, sizeof(fluid_midi_router_t)); + + /* Retrieve the number of MIDI channels for range limiting */ + fluid_settings_getint(settings, "synth.midi-channels", &router->nr_midi_channels); + + fluid_mutex_init(router->rules_mutex); + + router->event_handler = handler; + router->event_handler_data = event_handler_data; + + /* Create default routing rules which pass all events unmodified */ + for(i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) + { + router->rules[i] = new_fluid_midi_router_rule(); + + if(!router->rules[i]) + { + goto error_recovery; + } + } + + return router; + +error_recovery: + delete_fluid_midi_router(router); + return NULL; +} + +/** + * Delete a MIDI router instance. + * @param router MIDI router to delete + * @return Returns #FLUID_OK on success, #FLUID_FAILED otherwise (only if NULL + * \a router passed really) + */ +void +delete_fluid_midi_router(fluid_midi_router_t *router) +{ + fluid_midi_router_rule_t *rule; + fluid_midi_router_rule_t *next_rule; + int i; + + fluid_return_if_fail(router != NULL); + + for(i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) + { + for(rule = router->rules[i]; rule; rule = next_rule) + { + next_rule = rule->next; + FLUID_FREE(rule); + } + } + + fluid_mutex_destroy(router->rules_mutex); + FLUID_FREE(router); +} + +/** + * Set a MIDI router to use default "unity" rules. + * + * @param router Router to set to default rules. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * Such a router will pass all events unmodified. + * + * @since 1.1.0 + */ +int +fluid_midi_router_set_default_rules(fluid_midi_router_t *router) +{ + fluid_midi_router_rule_t *new_rules[FLUID_MIDI_ROUTER_RULE_COUNT]; + fluid_midi_router_rule_t *del_rules[FLUID_MIDI_ROUTER_RULE_COUNT]; + fluid_midi_router_rule_t *rule, *next_rule, *prev_rule; + int i, i2; + + fluid_return_val_if_fail(router != NULL, FLUID_FAILED); + + /* Allocate new default rules outside of lock */ + + for(i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) + { + new_rules[i] = new_fluid_midi_router_rule(); + + if(!new_rules[i]) + { + /* Free already allocated rules */ + for(i2 = 0; i2 < i; i2++) + { + delete_fluid_midi_router_rule(new_rules[i2]); + } + + return FLUID_FAILED; + } + } + + + fluid_mutex_lock(router->rules_mutex); /* ++ lock */ + + for(i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) + { + del_rules[i] = NULL; + prev_rule = NULL; + + /* Process existing rules */ + for(rule = router->rules[i]; rule; rule = next_rule) + { + next_rule = rule->next; + + if(rule->pending_events == 0) /* Rule has no pending events? */ + { + /* Remove rule from rule list */ + if(prev_rule) + { + prev_rule->next = next_rule; + } + else if(rule == router->rules[i]) + { + router->rules[i] = next_rule; + } + + /* Prepend to delete list */ + rule->next = del_rules[i]; + del_rules[i] = rule; + } + else + { + rule->waiting = TRUE; /* Pending events, mark as waiting */ + prev_rule = rule; + } + } + + /* Prepend new default rule */ + new_rules[i]->next = router->rules[i]; + router->rules[i] = new_rules[i]; + } + + fluid_mutex_unlock(router->rules_mutex); /* -- unlock */ + + + /* Free old rules outside of lock */ + + for(i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) + { + for(rule = del_rules[i]; rule; rule = next_rule) + { + next_rule = rule->next; + FLUID_FREE(rule); + } + } + + return FLUID_OK; +} + +/** + * Clear all rules in a MIDI router. + * + * @param router Router to clear all rules from + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * An empty router will drop all events until rules are added. + * + * @since 1.1.0 + */ +int +fluid_midi_router_clear_rules(fluid_midi_router_t *router) +{ + fluid_midi_router_rule_t *del_rules[FLUID_MIDI_ROUTER_RULE_COUNT]; + fluid_midi_router_rule_t *rule, *next_rule, *prev_rule; + int i; + + fluid_return_val_if_fail(router != NULL, FLUID_FAILED); + + fluid_mutex_lock(router->rules_mutex); /* ++ lock */ + + for(i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) + { + del_rules[i] = NULL; + prev_rule = NULL; + + /* Process existing rules */ + for(rule = router->rules[i]; rule; rule = next_rule) + { + next_rule = rule->next; + + if(rule->pending_events == 0) /* Rule has no pending events? */ + { + /* Remove rule from rule list */ + if(prev_rule) + { + prev_rule->next = next_rule; + } + else if(rule == router->rules[i]) + { + router->rules[i] = next_rule; + } + + /* Prepend to delete list */ + rule->next = del_rules[i]; + del_rules[i] = rule; + } + else + { + rule->waiting = TRUE; /* Pending events, mark as waiting */ + prev_rule = rule; + } + } + } + + fluid_mutex_unlock(router->rules_mutex); /* -- unlock */ + + + /* Free old rules outside of lock */ + + for(i = 0; i < FLUID_MIDI_ROUTER_RULE_COUNT; i++) + { + for(rule = del_rules[i]; rule; rule = next_rule) + { + next_rule = rule->next; + FLUID_FREE(rule); + } + } + + return FLUID_OK; +} + +/** + * Add a rule to a MIDI router. + * @param router MIDI router + * @param rule Rule to add (used directly and should not be accessed again following a + * successful call to this function). + * @param type The type of rule to add (#fluid_midi_router_rule_type) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise (invalid rule for example) + * @since 1.1.0 + */ +int +fluid_midi_router_add_rule(fluid_midi_router_t *router, fluid_midi_router_rule_t *rule, + int type) +{ + fluid_midi_router_rule_t *free_rules, *next_rule; + + fluid_return_val_if_fail(router != NULL, FLUID_FAILED); + fluid_return_val_if_fail(rule != NULL, FLUID_FAILED); + fluid_return_val_if_fail(type >= 0 && type < FLUID_MIDI_ROUTER_RULE_COUNT, FLUID_FAILED); + + + fluid_mutex_lock(router->rules_mutex); /* ++ lock */ + + /* Take over free rules list, if any (to free outside of lock) */ + free_rules = router->free_rules; + router->free_rules = NULL; + + rule->next = router->rules[type]; + router->rules[type] = rule; + + fluid_mutex_unlock(router->rules_mutex); /* -- unlock */ + + + /* Free any deactivated rules which were waiting for events and are now done */ + + for(; free_rules; free_rules = next_rule) + { + next_rule = free_rules->next; + FLUID_FREE(free_rules); + } + + return FLUID_OK; +} + +/** + * Create a new MIDI router rule. + * + * @return Newly allocated router rule or NULL if out of memory. + * + * The new rule is a "unity" rule which will accept any values and wont modify + * them. + * + * @since 1.1.0 + */ +fluid_midi_router_rule_t * +new_fluid_midi_router_rule(void) +{ + fluid_midi_router_rule_t *rule; + + rule = FLUID_NEW(fluid_midi_router_rule_t); + + if(rule == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(rule, 0, sizeof(fluid_midi_router_rule_t)); + + rule->chan_min = 0; + rule->chan_max = 999999; + rule->chan_mul = 1.0; + rule->chan_add = 0; + rule->par1_min = 0; + rule->par1_max = 999999; + rule->par1_mul = 1.0; + rule->par1_add = 0; + rule->par2_min = 0; + rule->par2_max = 999999; + rule->par2_mul = 1.0; + rule->par2_add = 0; + + return rule; +}; + +/** + * Free a MIDI router rule. + * + * @param rule Router rule to free + * + * Note that rules which have been added to a router are managed by the router, + * so this function should seldom be needed. + * + * @since 1.1.0 + */ +void +delete_fluid_midi_router_rule(fluid_midi_router_rule_t *rule) +{ + fluid_return_if_fail(rule != NULL); + FLUID_FREE(rule); +} + +/** + * Set the channel portion of a rule. + * + * @param rule MIDI router rule + * @param min Minimum value for rule match + * @param max Maximum value for rule match + * @param mul Value which is multiplied by matching event's channel value (1.0 to not modify) + * @param add Value which is added to matching event's channel value (0 to not modify) + * + * The \a min and \a max parameters define a channel range window to match + * incoming events to. If \a min is less than or equal to \a max then an event + * is matched if its channel is within the defined range (including \a min + * and \a max). If \a min is greater than \a max then rule is inverted and matches + * everything except in *between* the defined range (so \a min and \a max would match). + * + * The \a mul and \a add values are used to modify event channel values prior to + * sending the event, if the rule matches. + * + * @since 1.1.0 + */ +void +fluid_midi_router_rule_set_chan(fluid_midi_router_rule_t *rule, int min, int max, + float mul, int add) +{ + fluid_return_if_fail(rule != NULL); + rule->chan_min = min; + rule->chan_max = max; + rule->chan_mul = mul; + rule->chan_add = add; +} + +/** + * Set the first parameter portion of a rule. + * + * @param rule MIDI router rule + * @param min Minimum value for rule match + * @param max Maximum value for rule match + * @param mul Value which is multiplied by matching event's 1st parameter value (1.0 to not modify) + * @param add Value which is added to matching event's 1st parameter value (0 to not modify) + * + * The 1st parameter of an event depends on the type of event. For note events + * its the MIDI note #, for CC events its the MIDI control number, for program + * change events its the MIDI program #, for pitch bend events its the bend value, + * for channel pressure its the channel pressure value and for key pressure + * its the MIDI note number. + * + * Pitch bend values have a maximum value of 16383 (8192 is pitch bend center) and all + * other events have a max of 127. All events have a minimum value of 0. + * + * The \a min and \a max parameters define a parameter range window to match + * incoming events to. If \a min is less than or equal to \a max then an event + * is matched if its 1st parameter is within the defined range (including \a min + * and \a max). If \a min is greater than \a max then rule is inverted and matches + * everything except in *between* the defined range (so \a min and \a max would match). + * + * The \a mul and \a add values are used to modify event 1st parameter values prior to + * sending the event, if the rule matches. + * + * @since 1.1.0 + */ +void +fluid_midi_router_rule_set_param1(fluid_midi_router_rule_t *rule, int min, int max, + float mul, int add) +{ + fluid_return_if_fail(rule != NULL); + rule->par1_min = min; + rule->par1_max = max; + rule->par1_mul = mul; + rule->par1_add = add; +} + +/** + * Set the second parameter portion of a rule. + * + * @param rule MIDI router rule + * @param min Minimum value for rule match + * @param max Maximum value for rule match + * @param mul Value which is multiplied by matching event's 2nd parameter value (1.0 to not modify) + * @param add Value which is added to matching event's 2nd parameter value (0 to not modify) + * + * The 2nd parameter of an event depends on the type of event. For note events + * its the MIDI velocity, for CC events its the control value and for key pressure + * events its the key pressure value. All other types lack a 2nd parameter. + * + * All applicable 2nd parameters have the range 0-127. + * + * The \a min and \a max parameters define a parameter range window to match + * incoming events to. If \a min is less than or equal to \a max then an event + * is matched if its 2nd parameter is within the defined range (including \a min + * and \a max). If \a min is greater than \a max then rule is inverted and matches + * everything except in *between* the defined range (so \a min and \a max would match). + * + * The \a mul and \a add values are used to modify event 2nd parameter values prior to + * sending the event, if the rule matches. + * + * @since 1.1.0 + */ +void +fluid_midi_router_rule_set_param2(fluid_midi_router_rule_t *rule, int min, int max, + float mul, int add) +{ + fluid_return_if_fail(rule != NULL); + rule->par2_min = min; + rule->par2_max = max; + rule->par2_mul = mul; + rule->par2_add = add; +} + +/** + * Handle a MIDI event through a MIDI router instance. + * @param data MIDI router instance #fluid_midi_router_t, its a void * so that + * this function can be used as a callback for other subsystems + * (new_fluid_midi_driver() for example). + * @param event MIDI event to handle + * @return #FLUID_OK if all rules were applied successfully, #FLUID_FAILED if + * an error occurred while applying a rule or (since 2.2.2) the event was + * ignored because a parameter was out-of-range after the rule had been applied. + * See the note below. + * + * Purpose: The midi router is called for each event, that is received + * via the 'physical' midi input. Each event can trigger an arbitrary number + * of generated events (one for each rule that matches). + * + * In default mode, a noteon event is just forwarded to the synth's 'noteon' function, + * a 'CC' event to the synth's 'CC' function and so on. + * + * The router can be used to: + * - filter messages (for example: Pass sustain pedal CCs only to selected channels) + * - split the keyboard (noteon with notenr < x: to ch 1, >x to ch 2) + * - layer sounds (for each noteon received on ch 1, create a noteon on ch1, ch2, ch3,...) + * - velocity scaling (for each noteon event, scale the velocity by 1.27 to give DX7 users + * a chance) + * - velocity switching ("v <=100: Angel Choir; V > 100: Hell's Bells") + * - get rid of aftertouch + * - ... + * + * @note Each input event has values (ch, par1, par2) that could be changed by a rule. + * After a rule has been applied on any value and the value is out of range, the event + * can be either ignored or the value can be clamped depending on the type of the event: + * - To get full benefice of the rule the value is clamped and the event passed to the output. + * - To avoid MIDI messages conflicts at the output, the event is ignored + * (i.e not passed to the output). + * - ch out of range: event is ignored regardless of the event type. + * - par1 out of range: event is ignored for PROG_CHANGE or CONTROL_CHANGE type, + * par1 is clamped otherwise. + * - par2 out of range: par2 is clamped regardless of the event type. + */ +int +fluid_midi_router_handle_midi_event(void *data, fluid_midi_event_t *event) +{ + fluid_midi_router_t *router = (fluid_midi_router_t *)data; + fluid_midi_router_rule_t **rulep, *rule, *next_rule, *prev_rule = NULL; + int event_has_par2 = 0; /* Flag, indicates that current event needs two parameters */ + int is_par1_ignored = 0; /* Flag, indicates that current event should be + ignored/clamped when par1 is getting out of range + value after the rule had been applied:1:ignored, 0:clamped */ + int par1_max = 127; /* Range limit for par1 */ + int par2_max = 127; /* Range limit for par2 */ + int ret_val = FLUID_OK; + + int chan; /* Channel of the generated event */ + int par1; /* par1 of the generated event */ + int par2; + int event_par1; + int event_par2; + fluid_midi_event_t new_event; + + /* Some keyboards report noteoff through a noteon event with vel=0. + * Convert those to noteoff to ease processing. */ + if(event->type == NOTE_ON && event->param2 == 0) + { + event->type = NOTE_OFF; + event->param2 = 127; /* Release velocity */ + } + + fluid_mutex_lock(router->rules_mutex); /* ++ lock rules */ + + /* Depending on the event type, choose the correct list of rules. */ + switch(event->type) + { + /* For NOTE_ON event, par1(pitch) and par2(velocity) will be clamped if + they are out of range after the rule had been applied */ + case NOTE_ON: + rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_NOTE]; + event_has_par2 = 1; + break; + + /* For NOTE_OFF event, par1(pitch) and par2(velocity) will be clamped if + they are out of range after the rule had been applied */ + case NOTE_OFF: + rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_NOTE]; + event_has_par2 = 1; + break; + + /* CONTROL_CHANGE event will be ignored if par1 (ctrl num) is out + of range after the rule had been applied */ + case CONTROL_CHANGE: + rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_CC]; + event_has_par2 = 1; + is_par1_ignored = 1; + break; + + /* PROGRAM_CHANGE event will be ignored if par1 (program num) is out + of range after the rule had been applied */ + case PROGRAM_CHANGE: + rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_PROG_CHANGE]; + is_par1_ignored = 1; + break; + + /* For PITCH_BEND event, par1(bend value) will be clamped if + it is out of range after the rule had been applied */ + case PITCH_BEND: + rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_PITCH_BEND]; + par1_max = 16383; + break; + + /* For CHANNEL_PRESSURE event, par1(pressure value) will be clamped if + it is out of range after the rule had been applied */ + case CHANNEL_PRESSURE: + rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE]; + break; + + /* For KEY_PRESSURE event, par1(pitch) and par2(pressure value) will be + clamped if they are out of range after the rule had been applied */ + case KEY_PRESSURE: + rulep = &router->rules[FLUID_MIDI_ROUTER_RULE_KEY_PRESSURE]; + event_has_par2 = 1; + break; + + case MIDI_SYSTEM_RESET: + case MIDI_SYSEX: + ret_val = router->event_handler(router->event_handler_data, event); + fluid_mutex_unlock(router->rules_mutex); /* -- unlock rules */ + return ret_val; + + default: + rulep = NULL; /* Event will not be passed on */ + break; + } + + /* Loop over rules in the list, looking for matches for this event. */ + for(rule = rulep ? *rulep : NULL; rule; prev_rule = rule, rule = next_rule) + { + event_par1 = (int)event->param1; + event_par2 = (int)event->param2; + next_rule = rule->next; /* Rule may get removed from list, so get next here */ + + /* Channel window */ + if(rule->chan_min > rule->chan_max) + { + /* Inverted rule: Exclude everything between max and min (but not min/max) */ + if(event->channel > rule->chan_max && event->channel < rule->chan_min) + { + continue; + } + } + else /* Normal rule: Exclude everything < max or > min (but not min/max) */ + { + if(event->channel > rule->chan_max || event->channel < rule->chan_min) + { + continue; + } + } + + /* Par 1 window */ + if(rule->par1_min > rule->par1_max) + { + /* Inverted rule: Exclude everything between max and min (but not min/max) */ + if(event_par1 > rule->par1_max && event_par1 < rule->par1_min) + { + continue; + } + } + else /* Normal rule: Exclude everything < max or > min (but not min/max)*/ + { + if(event_par1 > rule->par1_max || event_par1 < rule->par1_min) + { + continue; + } + } + + /* Par 2 window (only applies to event types, which have 2 pars) + * For noteoff events, velocity switching doesn't make any sense. + * Velocity scaling might be useful, though. + */ + if(event_has_par2 && event->type != NOTE_OFF) + { + if(rule->par2_min > rule->par2_max) + { + /* Inverted rule: Exclude everything between max and min (but not min/max) */ + if(event_par2 > rule->par2_max && event_par2 < rule->par2_min) + { + continue; + } + } + else /* Normal rule: Exclude everything < max or > min (but not min/max)*/ + { + if(event_par2 > rule->par2_max || event_par2 < rule->par2_min) + { + continue; + } + } + } + + /* Channel scaling / offset + * Note: rule->chan_mul will probably be 0 or 1. If it's 0, input from all + * input channels is mapped to the same synth channel. + */ + chan = rule->chan_add + (int)((fluid_real_t)event->channel * rule->chan_mul + + (fluid_real_t)0.5); + + /* We ignore the event if chan is out of range */ + if((chan < 0) || (chan >= router->nr_midi_channels)) + { + ret_val = FLUID_FAILED; + continue; /* go to next rule */ + } + + /* par 1 scaling / offset */ + par1 = rule->par1_add + (int)((fluid_real_t)event_par1 * rule->par1_mul + + (fluid_real_t)0.5); + + if(is_par1_ignored) + { + /* We ignore the event if par1 is out of range */ + if((par1 < 0) || (par1 > par1_max)) + { + ret_val = FLUID_FAILED; + continue; /* go to next rule */ + } + } + else + { + /* par1 range clamping */ + if(par1 < 0) + { + par1 = 0; + } + else if(par1 > par1_max) + { + par1 = par1_max; + } + } + + /* par 2 scaling / offset, if applicable */ + if(event_has_par2) + { + par2 = rule->par2_add + (int)((fluid_real_t)event_par2 * rule->par2_mul + + (fluid_real_t)0.5); + + /* par2 range clamping */ + if(par2 < 0) + { + par2 = 0; + } + else if(par2 > par2_max) + { + par2 = par2_max; + } + } + else + { + par2 = 0; + } + + /* At this point we have to create an event of event->type on 'chan' with par1 (maybe par2). + * We keep track on the state of noteon and sustain pedal events. If the application tries + * to delete a rule, it will only be fully removed, if pending noteoff / pedal off events have + * arrived. In the meantime while waiting, it will only let through 'negative' events + * (noteoff or pedal up). + */ + if(event->type == NOTE_ON || (event->type == CONTROL_CHANGE + && par1 == SUSTAIN_SWITCH && par2 >= 64)) + { + /* Noteon or sustain pedal down event generated */ + if(rule->keys_cc[par1] == 0) + { + rule->keys_cc[par1] = 1; + rule->pending_events++; + } + } + else if(event->type == NOTE_OFF || (event->type == CONTROL_CHANGE + && par1 == SUSTAIN_SWITCH && par2 < 64)) + { + /* Noteoff or sustain pedal up event generated */ + if(rule->keys_cc[par1] > 0) + { + rule->keys_cc[par1] = 0; + rule->pending_events--; + + /* Rule is waiting for negative event to be destroyed? */ + if(rule->waiting) + { + if(rule->pending_events == 0) + { + /* Remove rule from rule list */ + if(prev_rule) + { + prev_rule->next = next_rule; + } + else + { + *rulep = next_rule; + } + + /* Add to free list */ + rule->next = router->free_rules; + router->free_rules = rule; + + rule = prev_rule; /* Set rule to previous rule, which gets assigned to the next prev_rule value (in for() statement) */ + } + + goto send_event; /* Pass the event to complete the cycle */ + } + } + } + + /* Rule is still waiting for negative event? (note off or pedal up) */ + if(rule->waiting) + { + continue; /* Skip (rule is inactive except for matching negative event) */ + } + +send_event: + + /* At this point it is decided, what is sent to the synth. + * Create a new event and make the appropriate call */ + + fluid_midi_event_set_type(&new_event, event->type); + fluid_midi_event_set_channel(&new_event, chan); + new_event.param1 = par1; + new_event.param2 = par2; + + /* On failure, continue to process events, but return failure to caller. */ + if(router->event_handler(router->event_handler_data, &new_event) != FLUID_OK) + { + ret_val = FLUID_FAILED; + } + } + + fluid_mutex_unlock(router->rules_mutex); /* -- unlock rules */ + + return ret_val; +} + +/** + * MIDI event callback function to display event information to stdout + * @param data MIDI router instance + * @param event MIDI event data + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * An implementation of the #handle_midi_event_func_t function type, used for + * displaying MIDI event information between the MIDI driver and router to + * stdout. Useful for adding into a MIDI router chain for debugging MIDI events. + */ +int fluid_midi_dump_prerouter(void *data, fluid_midi_event_t *event) +{ + switch(event->type) + { + case NOTE_ON: + fprintf(stdout, "event_pre_noteon %i %i %i\n", + event->channel, event->param1, event->param2); + break; + + case NOTE_OFF: + fprintf(stdout, "event_pre_noteoff %i %i %i\n", + event->channel, event->param1, event->param2); + break; + + case CONTROL_CHANGE: + fprintf(stdout, "event_pre_cc %i %i %i\n", + event->channel, event->param1, event->param2); + break; + + case PROGRAM_CHANGE: + fprintf(stdout, "event_pre_prog %i %i\n", event->channel, event->param1); + break; + + case PITCH_BEND: + fprintf(stdout, "event_pre_pitch %i %i\n", event->channel, event->param1); + break; + + case CHANNEL_PRESSURE: + fprintf(stdout, "event_pre_cpress %i %i\n", event->channel, event->param1); + break; + + case KEY_PRESSURE: + fprintf(stdout, "event_pre_kpress %i %i %i\n", + event->channel, event->param1, event->param2); + break; + + default: + break; + } + + return fluid_midi_router_handle_midi_event((fluid_midi_router_t *) data, event); +} + +/** + * MIDI event callback function to display event information to stdout + * @param data MIDI router instance + * @param event MIDI event data + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * An implementation of the #handle_midi_event_func_t function type, used for + * displaying MIDI event information between the MIDI driver and router to + * stdout. Useful for adding into a MIDI router chain for debugging MIDI events. + */ +int fluid_midi_dump_postrouter(void *data, fluid_midi_event_t *event) +{ + switch(event->type) + { + case NOTE_ON: + fprintf(stdout, "event_post_noteon %i %i %i\n", + event->channel, event->param1, event->param2); + break; + + case NOTE_OFF: + fprintf(stdout, "event_post_noteoff %i %i %i\n", + event->channel, event->param1, event->param2); + break; + + case CONTROL_CHANGE: + fprintf(stdout, "event_post_cc %i %i %i\n", + event->channel, event->param1, event->param2); + break; + + case PROGRAM_CHANGE: + fprintf(stdout, "event_post_prog %i %i\n", event->channel, event->param1); + break; + + case PITCH_BEND: + fprintf(stdout, "event_post_pitch %i %i\n", event->channel, event->param1); + break; + + case CHANNEL_PRESSURE: + fprintf(stdout, "event_post_cpress %i %i\n", event->channel, event->param1); + break; + + case KEY_PRESSURE: + fprintf(stdout, "event_post_kpress %i %i %i\n", + event->channel, event->param1, event->param2); + break; + + default: + break; + } + + return fluid_synth_handle_midi_event((fluid_synth_t *) data, event); +} diff --git a/thirdparty/fluidsynth/src/midi/fluid_midi_router.h b/thirdparty/fluidsynth/src/midi/fluid_midi_router.h new file mode 100644 index 000000000..5a5068d5e --- /dev/null +++ b/thirdparty/fluidsynth/src/midi/fluid_midi_router.h @@ -0,0 +1,32 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* Author: Markus Nentwig, nentwig@users.sourceforge.net + */ + +#ifndef _FLUID_MIDIROUTER_H +#define _FLUID_MIDIROUTER_H + +#include "fluidsynth_priv.h" +#include "fluid_midi.h" +#include "fluid_sys.h" + + +#endif diff --git a/thirdparty/fluidsynth/src/midi/fluid_seq.c b/thirdparty/fluidsynth/src/midi/fluid_seq.c new file mode 100644 index 000000000..81e63d220 --- /dev/null +++ b/thirdparty/fluidsynth/src/midi/fluid_seq.c @@ -0,0 +1,632 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + + +/* + 2002 : API design by Peter Hanappe and Antoine Schmitt + August 2002 : Implementation by Antoine Schmitt as@gratin.org + as part of the infiniteCD author project + http://www.infiniteCD.org/ +*/ + +#include "fluid_event.h" +#include "fluid_sys.h" // timer, threads, etc... +#include "fluid_list.h" +#include "fluid_seq_queue.h" + +/*************************************************************** + * + * SEQUENCER + */ + +#define FLUID_SEQUENCER_EVENTS_MAX 1000 + +/* Private data for SEQUENCER */ +struct _fluid_sequencer_t +{ + // A backup of currentMs when we have received the last scale change + unsigned int startMs; + + // The number of milliseconds passed since we have started the sequencer, + // as indicated by the synth's sample timer + fluid_atomic_int_t currentMs; + + // A backup of cur_ticks when we have received the last scale change + unsigned int start_ticks; + + // The tick count which we've used for the most recent event dispatching + unsigned int cur_ticks; + + int useSystemTimer; + + // The current time scale in ticks per second. + // If you think of MIDI, this is equivalent to: (PPQN / 1000) / (USPQN / 1000) + double scale; + + fluid_list_t *clients; + fluid_seq_id_t clientsID; + + // Pointer to the C++ event queue + void *queue; + fluid_rec_mutex_t mutex; +}; + +/* Private data for clients */ +typedef struct _fluid_sequencer_client_t +{ + fluid_seq_id_t id; + char *name; + fluid_event_callback_t callback; + void *data; +} fluid_sequencer_client_t; + + +/* API implementation */ + +/** + * Create a new sequencer object which uses the system timer. + * + * @return New sequencer instance + * + * Use new_fluid_sequencer2() to specify whether the system timer or + * fluid_sequencer_process() is used to advance the sequencer. + * + * @deprecated As of fluidsynth 2.1.1 the use of the system timer has been deprecated. + */ +fluid_sequencer_t * +new_fluid_sequencer(void) +{ + return new_fluid_sequencer2(TRUE); +} + +/** + * Create a new sequencer object. + * + * @param use_system_timer If TRUE, sequencer will advance at the rate of the + * system clock. If FALSE, call fluid_sequencer_process() to advance + * the sequencer. + * @return New sequencer instance + * + * @note As of fluidsynth 2.1.1 the use of the system timer has been deprecated. + * + * @since 1.1.0 + */ +fluid_sequencer_t * +new_fluid_sequencer2(int use_system_timer) +{ + fluid_sequencer_t *seq; + + if(use_system_timer) + { + FLUID_LOG(FLUID_WARN, "sequencer: Usage of the system timer has been deprecated!"); + } + + seq = FLUID_NEW(fluid_sequencer_t); + + if(seq == NULL) + { + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); + return NULL; + } + + FLUID_MEMSET(seq, 0, sizeof(fluid_sequencer_t)); + + seq->scale = 1000; // default value + seq->useSystemTimer = use_system_timer ? 1 : 0; + seq->startMs = seq->useSystemTimer ? fluid_curtime() : 0; + + fluid_rec_mutex_init(seq->mutex); + + seq->queue = new_fluid_seq_queue(FLUID_SEQUENCER_EVENTS_MAX); + if(seq->queue == NULL) + { + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); + delete_fluid_sequencer(seq); + return NULL; + } + + return(seq); +} + +/** + * Free a sequencer object. + * + * @param seq Sequencer to delete + * + * @note Before fluidsynth 2.1.1 registered sequencer clients may not be fully freed by this function. + */ +void +delete_fluid_sequencer(fluid_sequencer_t *seq) +{ + fluid_return_if_fail(seq != NULL); + + /* cleanup clients */ + while(seq->clients) + { + fluid_sequencer_client_t *client = (fluid_sequencer_client_t *)seq->clients->data; + fluid_sequencer_unregister_client(seq, client->id); + } + + fluid_rec_mutex_destroy(seq->mutex); + delete_fluid_seq_queue(seq->queue); + + FLUID_FREE(seq); +} + +/** + * Check if a sequencer is using the system timer or not. + * + * @param seq Sequencer object + * @return TRUE if system timer is being used, FALSE otherwise. + * + * @deprecated As of fluidsynth 2.1.1 the usage of the system timer has been deprecated. + * + * @since 1.1.0 + */ +int +fluid_sequencer_get_use_system_timer(fluid_sequencer_t *seq) +{ + fluid_return_val_if_fail(seq != NULL, FALSE); + + return seq->useSystemTimer; +} + + +/* clients */ + +/** + * Register a sequencer client. + * + * @param seq Sequencer object + * @param name Name of sequencer client + * @param callback Sequencer client callback or NULL for a source client. + * @param data User data to pass to the \a callback + * @return Unique sequencer ID or #FLUID_FAILED on error + * + * Clients can be sources or destinations of events. Sources don't need to + * register a callback. + * + * @note Implementations are encouraged to explicitly unregister any registered client with fluid_sequencer_unregister_client() before deleting the sequencer. + */ +fluid_seq_id_t +fluid_sequencer_register_client(fluid_sequencer_t *seq, const char *name, + fluid_event_callback_t callback, void *data) +{ + fluid_sequencer_client_t *client; + char *nameCopy; + + fluid_return_val_if_fail(seq != NULL, FLUID_FAILED); + + client = FLUID_NEW(fluid_sequencer_client_t); + + if(client == NULL) + { + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); + return FLUID_FAILED; + } + + nameCopy = FLUID_STRDUP(name); + + if(nameCopy == NULL) + { + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_FREE(client); + return FLUID_FAILED; + } + + seq->clientsID++; + + client->name = nameCopy; + client->id = seq->clientsID; + client->callback = callback; + client->data = data; + + seq->clients = fluid_list_append(seq->clients, (void *)client); + + return (client->id); +} + +/** + * Unregister a previously registered client. + * + * @param seq Sequencer object + * @param id Client ID as returned by fluid_sequencer_register_client(). + * + * The client's callback function will receive a FLUID_SEQ_UNREGISTERING event right before it is being unregistered. + */ +void +fluid_sequencer_unregister_client(fluid_sequencer_t *seq, fluid_seq_id_t id) +{ + fluid_list_t *tmp; + fluid_event_t evt; + unsigned int now = fluid_sequencer_get_tick(seq); + + fluid_return_if_fail(seq != NULL); + + fluid_event_clear(&evt); + fluid_event_unregistering(&evt); + fluid_event_set_dest(&evt, id); + fluid_event_set_time(&evt, now); + + tmp = seq->clients; + + while(tmp) + { + fluid_sequencer_client_t *client = (fluid_sequencer_client_t *)tmp->data; + + if(client->id == id) + { + // client found, remove it from the list to avoid recursive call when calling callback + seq->clients = fluid_list_remove_link(seq->clients, tmp); + + // call the callback (if any), to free underlying memory (e.g. seqbind structure) + if (client->callback != NULL) + { + (client->callback)(now, &evt, seq, client->data); + } + + if(client->name) + { + FLUID_FREE(client->name); + } + delete1_fluid_list(tmp); + FLUID_FREE(client); + return; + } + + tmp = tmp->next; + } + + return; +} + +/** + * Count a sequencers registered clients. + * + * @param seq Sequencer object + * @return Count of sequencer clients. + */ +int +fluid_sequencer_count_clients(fluid_sequencer_t *seq) +{ + if(seq == NULL || seq->clients == NULL) + { + return 0; + } + + return fluid_list_size(seq->clients); +} + +/** + * Get a client ID from its index (order in which it was registered). + * + * @param seq Sequencer object + * @param index Index of register client + * @return Client ID or #FLUID_FAILED if not found + */ +fluid_seq_id_t fluid_sequencer_get_client_id(fluid_sequencer_t *seq, int index) +{ + fluid_list_t *tmp; + + fluid_return_val_if_fail(seq != NULL, FLUID_FAILED); + fluid_return_val_if_fail(index >= 0, FLUID_FAILED); + + tmp = fluid_list_nth(seq->clients, index); + + if(tmp == NULL) + { + return FLUID_FAILED; + } + else + { + fluid_sequencer_client_t *client = (fluid_sequencer_client_t *)tmp->data; + return client->id; + } +} + +/** + * Get the name of a registered client. + * + * @param seq Sequencer object + * @param id Client ID + * @return Client name or NULL if not found. String is internal and should not + * be modified or freed. + */ +char * +fluid_sequencer_get_client_name(fluid_sequencer_t *seq, fluid_seq_id_t id) +{ + fluid_list_t *tmp; + + fluid_return_val_if_fail(seq != NULL, NULL); + + tmp = seq->clients; + + while(tmp) + { + fluid_sequencer_client_t *client = (fluid_sequencer_client_t *)tmp->data; + + if(client->id == id) + { + return client->name; + } + + tmp = tmp->next; + } + + return NULL; +} + +/** + * Check if a client is a destination client. + * + * @param seq Sequencer object + * @param id Client ID + * @return TRUE if client is a destination client, FALSE otherwise or if not found + */ +int +fluid_sequencer_client_is_dest(fluid_sequencer_t *seq, fluid_seq_id_t id) +{ + fluid_list_t *tmp; + + fluid_return_val_if_fail(seq != NULL, FALSE); + + tmp = seq->clients; + + while(tmp) + { + fluid_sequencer_client_t *client = (fluid_sequencer_client_t *)tmp->data; + + if(client->id == id) + { + return (client->callback != NULL); + } + + tmp = tmp->next; + } + + return FALSE; +} + +/** + * Send an event immediately. + * + * @param seq Sequencer object + * @param evt Event to send (not copied, used directly) + */ +void +fluid_sequencer_send_now(fluid_sequencer_t *seq, fluid_event_t *evt) +{ + fluid_seq_id_t destID; + fluid_list_t *tmp; + + fluid_return_if_fail(seq != NULL); + fluid_return_if_fail(evt != NULL); + + destID = fluid_event_get_dest(evt); + + /* find callback */ + tmp = seq->clients; + + while(tmp) + { + fluid_sequencer_client_t *dest = (fluid_sequencer_client_t *)tmp->data; + + if(dest->id == destID) + { + if(fluid_event_get_type(evt) == FLUID_SEQ_UNREGISTERING) + { + fluid_sequencer_unregister_client(seq, destID); + } + else + { + if(dest->callback) + { + (dest->callback)(fluid_sequencer_get_tick(seq), evt, seq, dest->data); + } + } + return; + } + + tmp = tmp->next; + } +} + + +/** + * Schedule an event for sending at a later time. + * + * @param seq Sequencer object + * @param evt Event to send (will be copied into internal queue) + * @param time Time value in ticks (in milliseconds with the default time scale of 1000). + * @param absolute TRUE if \a time is absolute sequencer time (time since sequencer + * creation), FALSE if relative to current time. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note The sequencer sorts events according to their timestamp \c time. For events that have + * the same timestamp, fluidsynth (as of version 2.2.0) uses the following order, according to + * which events will be dispatched to the client's callback function. + * - #FLUID_SEQ_SYSTEMRESET events precede any other event type. + * - #FLUID_SEQ_UNREGISTERING events succeed #FLUID_SEQ_SYSTEMRESET and precede other event type. + * - #FLUID_SEQ_NOTEON and #FLUID_SEQ_NOTE events succeed any other event type. + * - Otherwise the order is undefined. + * \n + * Or mathematically: #FLUID_SEQ_SYSTEMRESET < #FLUID_SEQ_UNREGISTERING < ... < (#FLUID_SEQ_NOTEON && #FLUID_SEQ_NOTE) + * + * @warning Be careful with relative ticks when sending many events! See #fluid_event_callback_t for details. + */ +int +fluid_sequencer_send_at(fluid_sequencer_t *seq, fluid_event_t *evt, + unsigned int time, int absolute) +{ + int res; + unsigned int now = fluid_sequencer_get_tick(seq); + + fluid_return_val_if_fail(seq != NULL, FLUID_FAILED); + fluid_return_val_if_fail(evt != NULL, FLUID_FAILED); + + /* set absolute */ + if(!absolute) + { + time = now + time; + } + + /* time stamp event */ + fluid_event_set_time(evt, time); + + fluid_rec_mutex_lock(seq->mutex); + res = fluid_seq_queue_push(seq->queue, evt); + fluid_rec_mutex_unlock(seq->mutex); + + return res; +} + +/** + * Remove events from the event queue. + * + * @param seq Sequencer object + * @param source Source client ID to match or -1 for wildcard + * @param dest Destination client ID to match or -1 for wildcard + * @param type Event type to match or -1 for wildcard (#fluid_seq_event_type) + */ +void +fluid_sequencer_remove_events(fluid_sequencer_t *seq, fluid_seq_id_t source, + fluid_seq_id_t dest, int type) +{ + fluid_return_if_fail(seq != NULL); + + fluid_rec_mutex_lock(seq->mutex); + fluid_seq_queue_remove(seq->queue, source, dest, type); + fluid_rec_mutex_unlock(seq->mutex); +} + + +/************************************* + time +**************************************/ + +static unsigned int +fluid_sequencer_get_tick_LOCAL(fluid_sequencer_t *seq, unsigned int cur_msec) +{ + unsigned int absMs; + double nowFloat; + unsigned int now; + + fluid_return_val_if_fail(seq != NULL, 0u); + + absMs = seq->useSystemTimer ? (unsigned int) fluid_curtime() : cur_msec; + nowFloat = ((double)(absMs - seq->startMs)) * seq->scale / 1000.0f; + now = nowFloat; + return seq->start_ticks + now; +} + +/** + * Get the current tick of the sequencer scaled by the time scale currently set. + * + * @param seq Sequencer object + * @return Current tick value + */ +unsigned int +fluid_sequencer_get_tick(fluid_sequencer_t *seq) +{ + return fluid_sequencer_get_tick_LOCAL(seq, fluid_atomic_int_get(&seq->currentMs)); +} + +/** + * Set the time scale of a sequencer. + * + * @param seq Sequencer object + * @param scale Sequencer scale value in ticks per second + * (default is 1000 for 1 tick per millisecond) + * + * If there are already scheduled events in the sequencer and the scale is changed + * the events are adjusted accordingly. + * + * @note May only be called from a sequencer callback or initially when no event dispatching happens. + * Otherwise it will mess up your event timing, because you have zero control over which events are + * affected by the scale change. + */ +void +fluid_sequencer_set_time_scale(fluid_sequencer_t *seq, double scale) +{ + fluid_return_if_fail(seq != NULL); + + if(scale != scale) + { + FLUID_LOG(FLUID_WARN, "sequencer: scale NaN\n"); + return; + } + + if(scale <= 0) + { + FLUID_LOG(FLUID_WARN, "sequencer: scale <= 0 : %f\n", scale); + return; + } + + seq->scale = scale; + seq->startMs = fluid_atomic_int_get(&seq->currentMs); + seq->start_ticks = seq->cur_ticks; +} + +/** + * Get a sequencer's time scale. + * + * @param seq Sequencer object. + * @return Time scale value in ticks per second. + */ +double +fluid_sequencer_get_time_scale(fluid_sequencer_t *seq) +{ + fluid_return_val_if_fail(seq != NULL, 0); + return seq->scale; +} + +/** + * Advance a sequencer. + * + * @param seq Sequencer object + * @param msec Time to advance sequencer to (absolute time since sequencer start). + * + * If you have registered the synthesizer as client (fluid_sequencer_register_fluidsynth()), the synth + * will take care of calling fluid_sequencer_process(). Otherwise it is up to the user to + * advance the sequencer manually. + * + * @since 1.1.0 + */ +void +fluid_sequencer_process(fluid_sequencer_t *seq, unsigned int msec) +{ + fluid_atomic_int_set(&seq->currentMs, msec); + seq->cur_ticks = fluid_sequencer_get_tick_LOCAL(seq, msec); + + fluid_rec_mutex_lock(seq->mutex); + fluid_seq_queue_process(seq->queue, seq, seq->cur_ticks); + fluid_rec_mutex_unlock(seq->mutex); +} + + +/** + * @internal + * only used privately by fluid_seqbind and only from sequencer callback, thus lock acquire is not needed. + */ +void fluid_sequencer_invalidate_note(fluid_sequencer_t *seq, fluid_seq_id_t dest, fluid_note_id_t id) +{ + fluid_seq_queue_invalidate_note_private(seq->queue, dest, id); +} diff --git a/thirdparty/fluidsynth/src/midi/fluid_seq_queue.cpp b/thirdparty/fluidsynth/src/midi/fluid_seq_queue.cpp new file mode 100644 index 000000000..4ab0062d6 --- /dev/null +++ b/thirdparty/fluidsynth/src/midi/fluid_seq_queue.cpp @@ -0,0 +1,250 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2019 Tom Moebert and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_seq_queue.h" + +#include +#include +#include + +/* + * This is an implementation of an event queue, sorted according to their timestamp. + * + * Very similar to std::priority_queue. + * + * However, we cannot use std::priority_queue, because fluid_sequencer_remove_events() requires + * access to the underlying container, which however std::priority_queue doesn't provide (without hacks). + * On the other hand, implementing a priority_queue while managing our own heap structure is very + * simple to do using std::make_heap() and friends. + */ + +// we are using a deque rather than vector, because: +// "Expansion of a deque is cheaper than the expansion of a std::vector because it does not involve +// copying of the existing elements to a new memory location." +typedef std::deque seq_queue_t; + +static bool event_compare(const fluid_event_t& left, const fluid_event_t& right) +{ + bool leftIsBeforeRight; + + unsigned int ltime = left.time, rtime = right.time; + if(ltime < rtime) + { + leftIsBeforeRight = true; + } + else if (ltime == rtime) + { + fluid_seq_event_type ltype = static_cast(left.type); + fluid_seq_event_type rtype = static_cast(right.type); + + // Both events have the same tick value. Per MIDI standard, the order is undefined. However, most implementations use a FIFO ordering here, + // which we cannot use, because heap sort is not stable. To make sure that fluidsynth behaves correctly from a user perspective, + // we do the following: + // * System reset events are always first, + // * Unregistering events are second (this gives clients the chance to reset themselves before unregistering at the same tick), + // * Bank changes must precede Prog changes (to ensure correct preset fallback AND preset selection within a certain bank), + // * NoteOn events are always last (this makes sure that all other "state-change" events have been processed and NoteOff events + // with the same key as the NoteOn have been processed (zero-length notes are not a use-case here)). + // * For any other event type, the order is undefined (because the processing order of those event types doesn't matter). + // See: + // https://lists.nongnu.org/archive/html/fluid-dev/2019-12/msg00001.html + // https://lists.nongnu.org/archive/html/fluid-dev/2017-05/msg00004.html + // + // The expression below ensures the order described above. + // This boolean expression is written in disjunctive normal form and can be obtained by using the Karnaugh map below, + // which contains + // * possible values of rtype in columns, + // * possible values of ltype in rows, + // * the boolean values to indicate whether leftIsBeforeRight, + // * X meaning any other event type, and + // * the '*' means that it could be zero, but making it 1 simplyfies the boolean expression. + // + // | ltype \ rtype | SYSR | UNREG | BANK | PROG | NOTEON | X | + // | SYSR | 1 | 1 | 1 | 1 | 1 | 1 | + // | UNREG | 0 | 1 | 1 | 1 | 1 | 1 | + // | BANK | 0 | 0 | 1 | 1 | 1 | 1* | + // | PROG | 0 | 0 | 0 | 1 | 1 | 1* | + // | NOTEON | 0 | 0 | 0 | 0 | 1 | 0 | + // | X | 0 | 0 | 0 | 0 | 1 | 1 | + // + // The values in the diagonal (i.e. comparison with itself) must be true to make them become false after leaving this + // function in order to satisfy the irreflexive requirement, i.e. assert(!(a < a)) + + leftIsBeforeRight = + // first row in table + (ltype == FLUID_SEQ_SYSTEMRESET) + // the rtype NOTEON column + || (rtype == FLUID_SEQ_NOTEON || rtype == FLUID_SEQ_NOTE) + // the second row in table + || (rtype != FLUID_SEQ_SYSTEMRESET && ltype == FLUID_SEQ_UNREGISTERING) + // the third row in table + || (rtype != FLUID_SEQ_SYSTEMRESET && rtype != FLUID_SEQ_UNREGISTERING && ltype == FLUID_SEQ_BANKSELECT) + // the fourth row in table + || (rtype != FLUID_SEQ_SYSTEMRESET && rtype != FLUID_SEQ_UNREGISTERING && rtype != FLUID_SEQ_BANKSELECT && ltype == FLUID_SEQ_PROGRAMCHANGE) + // the bottom right value, i.e. any other type compared to any other type + || (ltype != FLUID_SEQ_SYSTEMRESET && ltype != FLUID_SEQ_UNREGISTERING && ltype != FLUID_SEQ_BANKSELECT && ltype != FLUID_SEQ_PROGRAMCHANGE && ltype != FLUID_SEQ_NOTEON && ltype != FLUID_SEQ_NOTE && + rtype != FLUID_SEQ_SYSTEMRESET && rtype != FLUID_SEQ_UNREGISTERING && rtype != FLUID_SEQ_BANKSELECT && rtype != FLUID_SEQ_PROGRAMCHANGE && rtype != FLUID_SEQ_NOTEON && rtype != FLUID_SEQ_NOTE); + } + else + { + leftIsBeforeRight = false; + } + + // we must negate the return value, because we are building a max-heap, i.e. + // the smallest element is at ::front() + return !leftIsBeforeRight; +} + +int event_compare_for_test(const fluid_event_t* left, const fluid_event_t* right) +{ + return event_compare(*left, *right); +} + +void* new_fluid_seq_queue(int nb_events) +{ + try + { + // As workaround for a missing deque::reserve(), allocate a deque with a size of nb_events + seq_queue_t* queue = new seq_queue_t(nb_events); + // and clear the queue again + queue->clear(); + // C++11 introduced a deque::shrink_to_fit(), so hopefully not calling shrink_to_fit() will + // leave the previously used memory allocated, avoiding reallocation for subsequent insertions. + + return queue; + } + catch(...) + { + return 0; + } +} + +void delete_fluid_seq_queue(void *que) +{ + delete static_cast(que); +} + +int fluid_seq_queue_push(void *que, const fluid_event_t *evt) +{ + try + { + seq_queue_t& queue = *static_cast(que); + + queue.push_back(*evt); + std::push_heap(queue.begin(), queue.end(), event_compare); + + return FLUID_OK; + } + catch(...) + { + return FLUID_FAILED; + } +} + +void fluid_seq_queue_remove(void *que, fluid_seq_id_t src, fluid_seq_id_t dest, int type) +{ + seq_queue_t& queue = *static_cast(que); + + if(src == -1 && dest == -1 && type == -1) + { + // shortcut for deleting everything + queue.clear(); + } + else + { + for (seq_queue_t::iterator it = queue.begin(); it != queue.end();) + { + if((src == -1 || it->src == src) && + (dest == -1 || it->dest == dest) && + (type == -1 || it->type == type)) + { + it = queue.erase(it); + } + else + { + ++it; + } + } + + std::make_heap(queue.begin(), queue.end(), event_compare); + } +} + +void fluid_seq_queue_invalidate_note_private(void *que, fluid_seq_id_t dest, fluid_note_id_t id) +{ + seq_queue_t& queue = *static_cast(que); + + seq_queue_t::iterator event_to_invalidate = queue.end(); + unsigned int earliest_noteoff_tick = std::numeric_limits::max(); + + for (seq_queue_t::iterator it = queue.begin(); it != queue.end(); it++) + { + if((it->dest == dest) && + (it->type == FLUID_SEQ_NOTEOFF) && + (it->id == id) && + (it->time < earliest_noteoff_tick)) + { + earliest_noteoff_tick = it->time; + event_to_invalidate = it; + } + } + + if(event_to_invalidate != queue.end()) + { + // Invalidate the event, by setting invalidating its destination. + // Instead, removing the event from the queue would mess up the heap structure. We would need to + // make_heap again, which costs time, etc... + event_to_invalidate->dest = -1; + } +} + +static void fluid_seq_queue_pop(seq_queue_t &queue) +{ + std::pop_heap(queue.begin(), queue.end(), event_compare); + queue.pop_back(); +} + +void fluid_seq_queue_process(void *que, fluid_sequencer_t *seq, unsigned int cur_ticks) +{ + seq_queue_t& queue = *static_cast(que); + + while(!queue.empty()) + { + // Get the top most event. + const fluid_event_t& top = queue.front(); + if(top.time <= cur_ticks) + { + // First, copy it to a local buffer. + // This is required because the content of the queue should be read-only to the client, + // however, most client function receive a non-const fluid_event_t pointer + fluid_event_t local_evt = top; + + // Then, pop the queue, so that client-callbacks may add new events without + // messing up the heap structure while we are still processing + fluid_seq_queue_pop(queue); + fluid_sequencer_send_now(seq, &local_evt); + } + else + { + break; + } + } +} + diff --git a/thirdparty/fluidsynth/src/midi/fluid_seq_queue.h b/thirdparty/fluidsynth/src/midi/fluid_seq_queue.h new file mode 100644 index 000000000..cb58fa3a1 --- /dev/null +++ b/thirdparty/fluidsynth/src/midi/fluid_seq_queue.h @@ -0,0 +1,46 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_SEQ_QUE_H +#define _FLUID_SEQ_QUE_H + +#include "fluidsynth.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fluid_event.h" +#include "fluid_seqbind_notes.h" + +void* new_fluid_seq_queue(int nbEvents); +void delete_fluid_seq_queue(void *queue); +int fluid_seq_queue_push(void *queue, const fluid_event_t *evt); +void fluid_seq_queue_remove(void *queue, fluid_seq_id_t src, fluid_seq_id_t dest, int type); +void fluid_seq_queue_process(void *que, fluid_sequencer_t *seq, unsigned int cur_ticks); +void fluid_seq_queue_invalidate_note_private(void *que, fluid_seq_id_t dest, fluid_note_id_t id); + +int event_compare_for_test(const fluid_event_t* left, const fluid_event_t* right); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUID_SEQ_QUE_H */ diff --git a/thirdparty/fluidsynth/src/midi/fluid_seqbind.c b/thirdparty/fluidsynth/src/midi/fluid_seqbind.c new file mode 100644 index 000000000..9ec06f20c --- /dev/null +++ b/thirdparty/fluidsynth/src/midi/fluid_seqbind.c @@ -0,0 +1,386 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + + +/* + 2002 : API design by Peter Hanappe and Antoine Schmitt + August 2002 : Implementation by Antoine Schmitt as@gratin.org + as part of the infiniteCD author project + http://www.infiniteCD.org/ +*/ + +#include "fluidsynth_priv.h" +#include "fluid_synth.h" +#include "fluid_midi.h" +#include "fluid_event.h" +#include "fluid_seqbind_notes.h" + +/*************************************************************** +* +* SEQUENCER BINDING +*/ + +struct _fluid_seqbind_t +{ + fluid_synth_t *synth; + fluid_sequencer_t *seq; + fluid_sample_timer_t *sample_timer; + fluid_seq_id_t client_id; + void* note_container; +}; +typedef struct _fluid_seqbind_t fluid_seqbind_t; + +extern void fluid_sequencer_invalidate_note(fluid_sequencer_t *seq, fluid_seq_id_t dest, fluid_note_id_t id); + +int fluid_seqbind_timer_callback(void *data, unsigned int msec); +void fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data); + +/* Proper cleanup of the seqbind struct. */ +void +delete_fluid_seqbind(fluid_seqbind_t *seqbind) +{ + fluid_return_if_fail(seqbind != NULL); + + if((seqbind->client_id != -1) && (seqbind->seq != NULL)) + { + fluid_sequencer_unregister_client(seqbind->seq, seqbind->client_id); + seqbind->client_id = -1; + } + + if((seqbind->sample_timer != NULL) && (seqbind->synth != NULL)) + { + delete_fluid_sample_timer(seqbind->synth, seqbind->sample_timer); + seqbind->sample_timer = NULL; + } + + delete_fluid_note_container(seqbind->note_container); + FLUID_FREE(seqbind); +} + +/** + * Registers a synthesizer as a destination client of the given sequencer. + * + * @param seq Sequencer instance + * @param synth Synthesizer instance + * @returns Sequencer client ID, or #FLUID_FAILED on error. + * + * A convenience wrapper function around fluid_sequencer_register_client(), that allows you to + * easily process and render enqueued sequencer events with fluidsynth's synthesizer. + * The client being registered will be named @c fluidsynth. + * + * @note Implementations are encouraged to explicitly unregister this client either by calling + * fluid_sequencer_unregister_client() or by sending an unregistering event to the sequencer. Before + * fluidsynth 2.1.1 this was mandatory to avoid memory leaks. + * + * @code{.cpp} + * fluid_seq_id_t seqid = fluid_sequencer_register_fluidsynth(seq, synth); + * + * // ... do work + * + * fluid_event_t* evt = new_fluid_event(); + * fluid_event_set_source(evt, -1); + * fluid_event_set_dest(evt, seqid); + * fluid_event_unregistering(evt); + * + * // unregister the "fluidsynth" client immediately + * fluid_sequencer_send_now(seq, evt); + * delete_fluid_event(evt); + * delete_fluid_synth(synth); + * delete_fluid_sequencer(seq); + * @endcode + */ +fluid_seq_id_t +fluid_sequencer_register_fluidsynth(fluid_sequencer_t *seq, fluid_synth_t *synth) +{ + fluid_seqbind_t *seqbind; + + fluid_return_val_if_fail(seq != NULL, FLUID_FAILED); + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + seqbind = FLUID_NEW(fluid_seqbind_t); + + if(seqbind == NULL) + { + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); + return FLUID_FAILED; + } + + FLUID_MEMSET(seqbind, 0, sizeof(*seqbind)); + + seqbind->client_id = -1; + seqbind->synth = synth; + seqbind->seq = seq; + + /* set up the sample timer */ + if(!fluid_sequencer_get_use_system_timer(seq)) + { + seqbind->sample_timer = + new_fluid_sample_timer(synth, fluid_seqbind_timer_callback, (void *) seqbind); + + if(seqbind->sample_timer == NULL) + { + FLUID_LOG(FLUID_PANIC, "sequencer: Out of memory\n"); + FLUID_FREE(seqbind); + return FLUID_FAILED; + } + } + + seqbind->note_container = new_fluid_note_container(); + if(seqbind->note_container == NULL) + { + delete_fluid_sample_timer(seqbind->synth, seqbind->sample_timer); + FLUID_FREE(seqbind); + return FLUID_FAILED; + } + + /* register fluidsynth itself */ + seqbind->client_id = + fluid_sequencer_register_client(seq, "fluidsynth", fluid_seq_fluidsynth_callback, (void *)seqbind); + + if(seqbind->client_id == FLUID_FAILED) + { + delete_fluid_note_container(seqbind->note_container); + delete_fluid_sample_timer(seqbind->synth, seqbind->sample_timer); + FLUID_FREE(seqbind); + return FLUID_FAILED; + } + + return seqbind->client_id; +} + +/* Callback for sample timer */ +int +fluid_seqbind_timer_callback(void *data, unsigned int msec) +{ + fluid_seqbind_t *seqbind = (fluid_seqbind_t *) data; + fluid_sequencer_process(seqbind->seq, msec); + return 1; +} + +/* Callback for midi events */ +void +fluid_seq_fluidsynth_callback(unsigned int time, fluid_event_t *evt, fluid_sequencer_t *seq, void *data) +{ + fluid_synth_t *synth; + fluid_seqbind_t *seqbind = (fluid_seqbind_t *) data; + synth = seqbind->synth; + + switch(fluid_event_get_type(evt)) + { + case FLUID_SEQ_NOTEON: + fluid_synth_noteon(synth, fluid_event_get_channel(evt), fluid_event_get_key(evt), fluid_event_get_velocity(evt)); + break; + + case FLUID_SEQ_NOTEOFF: + { + fluid_note_id_t id = fluid_event_get_id(evt); + if(id != -1) + { + fluid_note_container_remove(seqbind->note_container, id); + } + fluid_synth_noteoff(synth, fluid_event_get_channel(evt), fluid_event_get_key(evt)); + } + break; + + case FLUID_SEQ_NOTE: + { + unsigned int dur = fluid_event_get_duration(evt); + short vel = fluid_event_get_velocity(evt); + short key = fluid_event_get_key(evt); + int chan = fluid_event_get_channel(evt); + + fluid_note_id_t id = fluid_note_compute_id(chan, key); + + int res = fluid_note_container_insert(seqbind->note_container, id); + if(res == FLUID_FAILED) + { + goto err; + } + else if(res) + { + // Note is already playing ATM, the following call to fluid_synth_noteon() will kill that note. + // Thus, we need to remove its noteoff from the queue + fluid_sequencer_invalidate_note(seqbind->seq, seqbind->client_id, id); + } + else + { + // Note not playing, all good. + } + + fluid_event_noteoff(evt, chan, key); + fluid_event_set_id(evt, id); + + res = fluid_sequencer_send_at(seq, evt, dur, 0); + if(res == FLUID_FAILED) + { + err: + FLUID_LOG(FLUID_ERR, "seqbind: Unable to process FLUID_SEQ_NOTE event, something went horribly wrong"); + return; + } + + fluid_synth_noteon(synth, chan, key, vel); + } + break; + + case FLUID_SEQ_ALLSOUNDSOFF: + fluid_note_container_clear(seqbind->note_container); + fluid_synth_all_sounds_off(synth, fluid_event_get_channel(evt)); + break; + + case FLUID_SEQ_ALLNOTESOFF: + fluid_note_container_clear(seqbind->note_container); + fluid_synth_all_notes_off(synth, fluid_event_get_channel(evt)); + break; + + case FLUID_SEQ_BANKSELECT: + fluid_synth_bank_select(synth, fluid_event_get_channel(evt), fluid_event_get_bank(evt)); + break; + + case FLUID_SEQ_PROGRAMCHANGE: + fluid_synth_program_change(synth, fluid_event_get_channel(evt), fluid_event_get_program(evt)); + break; + + case FLUID_SEQ_PROGRAMSELECT: + fluid_synth_program_select(synth, + fluid_event_get_channel(evt), + fluid_event_get_sfont_id(evt), + fluid_event_get_bank(evt), + fluid_event_get_program(evt)); + break; + + case FLUID_SEQ_PITCHBEND: + fluid_synth_pitch_bend(synth, fluid_event_get_channel(evt), fluid_event_get_pitch(evt)); + break; + + case FLUID_SEQ_PITCHWHEELSENS: + fluid_synth_pitch_wheel_sens(synth, fluid_event_get_channel(evt), fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_CONTROLCHANGE: + fluid_synth_cc(synth, fluid_event_get_channel(evt), fluid_event_get_control(evt), fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_MODULATION: + fluid_synth_cc(synth, fluid_event_get_channel(evt), MODULATION_MSB, fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_SUSTAIN: + fluid_synth_cc(synth, fluid_event_get_channel(evt), SUSTAIN_SWITCH, fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_PAN: + fluid_synth_cc(synth, fluid_event_get_channel(evt), PAN_MSB, fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_VOLUME: + fluid_synth_cc(synth, fluid_event_get_channel(evt), VOLUME_MSB, fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_REVERBSEND: + fluid_synth_cc(synth, fluid_event_get_channel(evt), EFFECTS_DEPTH1, fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_CHORUSSEND: + fluid_synth_cc(synth, fluid_event_get_channel(evt), EFFECTS_DEPTH3, fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_CHANNELPRESSURE: + fluid_synth_channel_pressure(synth, fluid_event_get_channel(evt), fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_KEYPRESSURE: + fluid_synth_key_pressure(synth, + fluid_event_get_channel(evt), + fluid_event_get_key(evt), + fluid_event_get_value(evt)); + break; + + case FLUID_SEQ_SYSTEMRESET: + fluid_synth_system_reset(synth); + break; + + case FLUID_SEQ_UNREGISTERING: /* free ourselves */ + delete_fluid_seqbind(seqbind); + break; + + case FLUID_SEQ_TIMER: + /* nothing in fluidsynth */ + break; + + case FLUID_SEQ_SCALE: + fluid_sequencer_set_time_scale(seq, fluid_event_get_scale(evt)); + break; + + default: + break; + } +} + +static fluid_seq_id_t get_fluidsynth_dest(fluid_sequencer_t *seq) +{ + int i; + fluid_seq_id_t id; + char *name; + int j = fluid_sequencer_count_clients(seq); + + for(i = 0; i < j; i++) + { + id = fluid_sequencer_get_client_id(seq, i); + name = fluid_sequencer_get_client_name(seq, id); + + if(name && (FLUID_STRCMP(name, "fluidsynth") == 0)) + { + return id; + } + } + + return -1; +} + +/** + * Transforms an incoming MIDI event (from a MIDI driver or MIDI router) to a + * sequencer event and adds it to the sequencer queue for sending as soon as possible. + * + * @param data The sequencer, must be a valid #fluid_sequencer_t + * @param event MIDI event + * @return #FLUID_OK or #FLUID_FAILED + * + * The signature of this function is of type #handle_midi_event_func_t. + * + * @since 1.1.0 + */ +int fluid_sequencer_add_midi_event_to_buffer(void *data, fluid_midi_event_t *event) +{ + fluid_event_t evt; + fluid_sequencer_t *seq; + + fluid_return_val_if_fail(data != NULL, FLUID_FAILED); + fluid_return_val_if_fail(event != NULL, FLUID_FAILED); + + seq = (fluid_sequencer_t *)data; + + fluid_event_clear(&evt); + fluid_event_from_midi_event(&evt, event); + fluid_event_set_dest(&evt, get_fluidsynth_dest(seq)); + + /* Schedule for sending at next call to fluid_sequencer_process */ + return fluid_sequencer_send_at(seq, &evt, 0, 0); +} diff --git a/thirdparty/fluidsynth/src/midi/fluid_seqbind_notes.cpp b/thirdparty/fluidsynth/src/midi/fluid_seqbind_notes.cpp new file mode 100644 index 000000000..e37720483 --- /dev/null +++ b/thirdparty/fluidsynth/src/midi/fluid_seqbind_notes.cpp @@ -0,0 +1,115 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2019 Tom Moebert and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_seqbind_notes.h" + +#include + +/* + * This is a hash container allows us to detect overlapping notes, by storing a bunch of unique integers, + * that allow us to track noteOn events. + * If an ID is part of the container, it means that we have received a noteOn on a certain channel and key. + * Once we receive a noteOff, we remove that ID again. + * + * Unfortunately, I can't think of a way to detect overlapping notes by using the synth only. One might + * think, that it's possible to use fluid_synth_get_voicelist(), in order to get a list of active voices + * and then detect overlaps. However, this doesn't reliably work, because voices may finish, before the + * noteOff is received. Think of short percussion samples spawned by long MIDI note durations. + * + * Here is an example of how it might look like. The "ticks" are equivalent to the time parameter passed + * into fluid_event_callback_t. + * +fluidsynth: debug: Tick 1728: Note on chan 15, key 44, ends at tick 1824 +fluidsynth: debug: Tick 1825: Normal NoteOFF on chan 15, key 44 +(so far, so usual) + +fluidsynth: debug: Tick 1825: Note on chan 15, key 44, dur 96, ends at tick 1921 + +oops, the voice spawned by the previous note already finished at tick 1900, but the noteOff is yet to come + +fluidsynth: debug: Tick 1920: Note on chan 15, key 44, dur 143, ends at tick 2063 +(Shit, we got another noteOn before the last noteOff. If we check for playing voices now, we won't find any, +because they have all finished.) + +fluidsynth: debug: Tick 1921: Normal NoteOFF on chan 15, key 44 +(... which means that we cannot detect an overlap, thus we cannot remove this noteoff, thus +this noteoff will immediately kill the voice that we've just started 1 tick ago) +*/ + +typedef std::set note_container_t; + +// Compute a unique ID for a given channel-key combination. Think of it as a two-dimensional array index. +fluid_note_id_t fluid_note_compute_id(int chan, short key) +{ + return 128 * chan + key; +} + +void* new_fluid_note_container() +{ + try + { + note_container_t* cont = new note_container_t; + return cont; + } + catch(...) + { + return 0; + } +} + +void delete_fluid_note_container(void *cont) +{ + delete static_cast(cont); +} + +// Returns true, if the ID was already included in the container before, false if it was just inserted and +// FLUID_FAILED in case of error. +int fluid_note_container_insert(void* cont, fluid_note_id_t id) +{ + try + { + std::pair res = static_cast(cont)->insert(id); + // res.second tells us whether the element was inserted + // by inverting it, we know whether it contained the element previously + return !res.second; + } + catch(...) + { + return FLUID_FAILED; + } +} + +void fluid_note_container_remove(void* cont, fluid_note_id_t id) +{ + try + { + static_cast(cont)->erase(id); + } + catch(...) + { + // should never happen + } +} + +// Empties the entire collection, e.g. in case of a AllNotesOff event +void fluid_note_container_clear(void* cont) +{ + static_cast(cont)->clear(); +} diff --git a/thirdparty/fluidsynth/src/midi/fluid_seqbind_notes.h b/thirdparty/fluidsynth/src/midi/fluid_seqbind_notes.h new file mode 100644 index 000000000..17180cfec --- /dev/null +++ b/thirdparty/fluidsynth/src/midi/fluid_seqbind_notes.h @@ -0,0 +1,42 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_SEQBIND_NOTE_H +#define _FLUID_SEQBIND_NOTE_H + +#include "fluidsynth.h" +#include "fluid_event.h" + +#ifdef __cplusplus +extern "C" { +#endif + +fluid_note_id_t fluid_note_compute_id(int chan, short key); +void* new_fluid_note_container(void); +void delete_fluid_note_container(void *cont); +int fluid_note_container_insert(void* cont, fluid_note_id_t id); +void fluid_note_container_remove(void* cont, fluid_note_id_t id); +void fluid_note_container_clear(void* cont); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUID_SEQBIND_NOTE_H */ diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_adsr_env.c b/thirdparty/fluidsynth/src/rvoice/fluid_adsr_env.c new file mode 100644 index 000000000..00bdd40f2 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_adsr_env.c @@ -0,0 +1,39 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_adsr_env.h" + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_adsr_env_set_data) +{ + fluid_adsr_env_t *env = obj; + fluid_adsr_env_section_t section = param[0].i; + unsigned int count = param[1].i; + fluid_real_t coeff = param[2].real; + fluid_real_t increment = param[3].real; + fluid_real_t min = param[4].real; + fluid_real_t max = param[5].real; + + env->data[section].count = count; + env->data[section].coeff = coeff; + env->data[section].increment = increment; + env->data[section].min = min; + env->data[section].max = max; +} + diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_adsr_env.h b/thirdparty/fluidsynth/src/rvoice/fluid_adsr_env.h new file mode 100644 index 000000000..5e99c6bf3 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_adsr_env.h @@ -0,0 +1,167 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_ADSR_ENVELOPE_H +#define _FLUID_ADSR_ENVELOPE_H + +#include "fluidsynth_priv.h" +#include "fluid_sys.h" + +/* + * envelope data + */ +struct _fluid_env_data_t +{ + unsigned int count; + fluid_real_t coeff; + fluid_real_t increment; + fluid_real_t min; + fluid_real_t max; +}; + +/* Indices for envelope tables */ +enum fluid_voice_envelope_index +{ + FLUID_VOICE_ENVDELAY, + FLUID_VOICE_ENVATTACK, + FLUID_VOICE_ENVHOLD, + FLUID_VOICE_ENVDECAY, + FLUID_VOICE_ENVSUSTAIN, + FLUID_VOICE_ENVRELEASE, + FLUID_VOICE_ENVFINISHED, + FLUID_VOICE_ENVLAST +}; + +typedef enum fluid_voice_envelope_index fluid_adsr_env_section_t; + +typedef struct _fluid_adsr_env_t fluid_adsr_env_t; + +struct _fluid_adsr_env_t +{ + fluid_env_data_t data[FLUID_VOICE_ENVLAST]; + unsigned int count; + fluid_real_t val; /* the current value of the envelope */ + fluid_adsr_env_section_t section; +}; + +/* For performance, all functions are inlined */ + +static FLUID_INLINE void +fluid_adsr_env_calc(fluid_adsr_env_t *env) +{ + fluid_env_data_t *env_data; + fluid_real_t x; + + env_data = &env->data[env->section]; + + /* skip to the next section of the envelope if necessary */ + while(env->count >= env_data->count) + { + // If we're switching envelope stages from decay to sustain, force the value to be the end value of the previous stage + // Hmm, should this only apply to volenv? It was so before refactoring, so keep it for now. [DH] + // No, must apply to both, otherwise some voices may sound detuned. [TM] (https://github.com/FluidSynth/fluidsynth/issues/1059) + if(env->section == FLUID_VOICE_ENVDECAY) + { + env->val = env_data->min * env_data->coeff; + } + + env_data = &env->data[++env->section]; + env->count = 0; + } + + /* calculate the envelope value and check for valid range */ + x = env_data->coeff * env->val + env_data->increment; + + if(x < env_data->min) + { + x = env_data->min; + env->section++; + env->count = 0; + } + else if(x > env_data->max) + { + x = env_data->max; + env->section++; + env->count = 0; + } + else + { + env->count++; + } + + env->val = x; +} + +/* This one cannot be inlined since it is referenced in + the event queue */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_adsr_env_set_data); + +static FLUID_INLINE void +fluid_adsr_env_reset(fluid_adsr_env_t *env) +{ + env->count = 0; + env->section = FLUID_VOICE_ENVDELAY; + env->val = 0.0f; +} + +static FLUID_INLINE fluid_real_t +fluid_adsr_env_get_val(fluid_adsr_env_t *env) +{ + return env->val; +} + +static FLUID_INLINE void +fluid_adsr_env_set_val(fluid_adsr_env_t *env, fluid_real_t val) +{ + env->val = val; +} + +static FLUID_INLINE fluid_adsr_env_section_t +fluid_adsr_env_get_section(fluid_adsr_env_t *env) +{ + return env->section; +} + +static FLUID_INLINE void +fluid_adsr_env_set_section(fluid_adsr_env_t *env, + fluid_adsr_env_section_t section) +{ + env->section = section; + env->count = 0; +} + +/* Used for determining which voice to kill. + Returns max amplitude from now, and forward in time. +*/ +static FLUID_INLINE fluid_real_t +fluid_adsr_env_get_max_val(fluid_adsr_env_t *env) +{ + if(env->section > FLUID_VOICE_ENVATTACK) + { + return env->val * 1000; + } + else + { + return env->data[FLUID_VOICE_ENVATTACK].max; + } +} + +#endif + diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_chorus.c b/thirdparty/fluidsynth/src/rvoice/fluid_chorus.c new file mode 100644 index 000000000..7a66704b4 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_chorus.c @@ -0,0 +1,1071 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe, Markus Nentwig and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* + based on a chorus implementation made by Juergen Mueller And Sundry Contributors in 1998 + + CHANGES + + - Adapted for fluidsynth, Peter Hanappe, March 2002 + + - Variable delay line implementation using bandlimited + interpolation, code reorganization: Markus Nentwig May 2002 + + - Complete rewrite using lfo computed on the fly, first order all-pass + interpolator and adding stereo unit: Jean-Jacques Ceresa, Jul 2019 + */ + + +/* + * Chorus effect. + * + * Flow diagram scheme for n delays ( 1 <= n <= MAX_CHORUS ): + * + * ________ + * direct signal (not implemented) >-->| | + * _________ | | + * mono | | | | + * input ---+---->| delay 1 |-------------------------->| Stereo |---> right + * | |_________| | | output + * | /|\ | Unit | + * : | | | + * : +-----------------+ |(width) | + * : | Delay control 1 |<-+ | | + * : +-----------------+ | | |---> left + * | _________ | | | output + * | | | | | | + * +---->| delay n |-------------------------->| | + * |_________| | | | + * /|\ | |________| + * | | +--------------+ /|\ + * +-----------------+ | |mod depth (ms)| | + * | Delay control n |<-*--|lfo speed (Hz)| gain-out + * +-----------------+ +--------------+ + * + * + * The delay i is controlled by a sine or triangle modulation i ( 1 <= i <= n). + * + * The chorus unit process a monophonic input signal and produces stereo output + * controlled by WIDTH macro. + * Actually WIDTH is fixed to maximum value. But in the future, we could add a + * setting (e.g "synth.chorus.width") allowing the user to get a gradually stereo + * effect from minimum (monophonic) to maximum stereo effect. + * + * Delays lines are implemented using only one line for all chorus blocks. + * Each chorus block has it own lfo (sinus/triangle). Each lfo are out of phase + * to produce uncorrelated signal at the output of the delay line (this simulates + * the presence of individual line for each block). Each lfo modulates the length + * of the line using a depth modulation value and lfo frequency value common to + * all lfos. + * + * LFO modulators are computed on the fly, instead of using lfo lookup table. + * The advantages are: + * - Avoiding a lost of 608272 memory bytes when lfo speed is low (0.3Hz). + * - Allows to diminish the lfo speed lower limit to 0.1Hz instead of 0.3Hz. + * A speed of 0.1 is interesting for chorus. Using a lookuptable for 0.1Hz + * would require too much memory (1824816 bytes). + * - Interpolation make use of first order all-pass interpolator instead of + * bandlimited interpolation. + * - Although lfo modulator is computed on the fly, cpu load is lower than + * using lfo lookup table with bandlimited interpolator. + */ + +#include "fluid_chorus.h" +#include "fluid_sys.h" + + +/*------------------------------------------------------------------------------------- + Private +--------------------------------------------------------------------------------------*/ +// #define DEBUG_PRINT // allows message to be printed on the console. + +#define MAX_CHORUS 99 /* number maximum of block */ +#define MAX_LEVEL 10 /* max output level */ +#define MIN_SPEED_HZ 0.1 /* min lfo frequency (Hz) */ +#define MAX_SPEED_HZ 5 /* max lfo frequency (Hz) */ + +/* WIDTH [0..10] value define a stereo separation between left and right. + When 0, the output is monophonic. When > 0 , the output is stereophonic. + Actually WIDTH is fixed to maximum value. But in the future we could add a setting to + allow a gradually stereo effect from minimum (monophonic) to maximum stereo effect. +*/ +#define WIDTH 10 + +/* SCALE_WET_WIDTH is a compensation weight factor to get an output + amplitude (wet) rather independent of the width setting. + 0: the output amplitude is fully dependent on the width setting. + >0: the output amplitude is less dependent on the width setting. + With a SCALE_WET_WIDTH of 0.2 the output amplitude is rather + independent of width setting (see fluid_chorus_set()). + */ +#define SCALE_WET_WIDTH 0.2f +#define SCALE_WET 1.0f + +#define MAX_SAMPLES 2048 /* delay length in sample (46.4 ms at sample rate: 44100Hz).*/ +#define LOW_MOD_DEPTH 176 /* low mod_depth/2 in samples */ +#define HIGH_MOD_DEPTH MAX_SAMPLES/2 /* high mod_depth in sample */ +#define RANGE_MOD_DEPTH (HIGH_MOD_DEPTH - LOW_MOD_DEPTH) + +/* Important min max values for MOD_RATE */ +/* mod rate define the rate at which the modulator is updated. Examples + 50: the modulator is updated every 50 samples (less cpu cycles expensive). + 1: the modulator is updated every sample (more cpu cycles expensive). +*/ +/* MOD_RATE acceptable for max lfo speed (5Hz) and max modulation depth (46.6 ms) */ +#define LOW_MOD_RATE 5 /* MOD_RATE acceptable for low modulation depth (8 ms) */ +#define HIGH_MOD_RATE 4 /* MOD_RATE acceptable for max modulation depth (46.6 ms) */ + /* and max lfo speed (5 Hz) */ +#define RANGE_MOD_RATE (HIGH_MOD_RATE - LOW_MOD_RATE) + +/* some chorus cpu_load measurement dependent of modulation rate: mod_rate + (number of chorus blocks: 2) + + No stero unit: + mod_rate | chorus cpu load(%) | one voice cpu load (%) + ---------------------------------------------------- + 50 | 0.204 | + 5 | 0.256 | 0.169 + 1 | 0.417 | + + With stero unit: + mod_rate | chorus cpu load(%) | one voice cpu load (%) + ---------------------------------------------------- + 50 | 0.220 | + 5 | 0.274 | 0.169 + 1 | 0.465 | + +*/ + +/* + Number of samples to add to the desired length of the delay line. This + allows to take account of rounding error interpolation when using large + modulation depth. + 1 is sufficient for max modulation depth (46.6 ms) and max lfo speed (5 Hz). +*/ +//#define INTERP_SAMPLES_NBR 0 +#define INTERP_SAMPLES_NBR 1 + + +/*----------------------------------------------------------------------------- + Sinusoidal modulator +-----------------------------------------------------------------------------*/ +/* modulator */ +typedef struct +{ + fluid_real_t a1; /* Coefficient: a1 = 2 * cos(w) */ + fluid_real_t buffer1; /* buffer1 */ + fluid_real_t buffer2; /* buffer2 */ + fluid_real_t reset_buffer2;/* reset value of buffer2 */ +} sinus_modulator; + +/*----------------------------------------------------------------------------- + Triangle modulator +-----------------------------------------------------------------------------*/ +typedef struct +{ + fluid_real_t freq; /* Osc. Frequency (in Hertz) */ + fluid_real_t val; /* internal current value */ + fluid_real_t inc; /* increment value */ +} triang_modulator; + +/*----------------------------------------------------------------------------- + modulator +-----------------------------------------------------------------------------*/ +typedef struct +{ + /*-------------*/ + int line_out; /* current line out position for this modulator */ + /*-------------*/ + sinus_modulator sinus; /* sinus lfo */ + triang_modulator triang; /* triangle lfo */ + /*-------------------------*/ + /* first order All-Pass interpolator members */ + fluid_real_t frac_pos_mod; /* fractional position part between samples */ + /* previous value used when interpolating using fractional */ + fluid_real_t buffer; +} modulator; + +/* Private data for SKEL file */ +struct _fluid_chorus_t +{ + int type; + fluid_real_t depth_ms; + fluid_real_t level; + fluid_real_t speed_Hz; + int number_blocks; + fluid_real_t sample_rate; + + /* width control: 0 monophonic, > 0 more stereophonic */ + fluid_real_t width; + fluid_real_t wet1, wet2; + + fluid_real_t *line; /* buffer line */ + int size; /* effective internal size (in samples) */ + + int line_in; /* line in position */ + + /* center output position members */ + fluid_real_t center_pos_mod; /* center output position modulated by modulator */ + int mod_depth; /* modulation depth (in samples) */ + + /* variable rate control of center output position */ + int index_rate; /* index rate to know when to update center_pos_mod */ + int mod_rate; /* rate at which center_pos_mod is updated */ + + /* modulator member */ + modulator mod[MAX_CHORUS]; /* sinus/triangle modulator */ +}; + +/*----------------------------------------------------------------------------- + Sets the frequency of sinus oscillator. + + @param mod pointer on modulator structure. + @param freq frequency of the oscillator in Hz. + @param sample_rate sample rate on audio output in Hz. + @param phase initial phase of the oscillator in degree (0 to 360). +-----------------------------------------------------------------------------*/ +static void set_sinus_frequency(sinus_modulator *mod, + float freq, float sample_rate, float phase) +{ + fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* initial angle */ + fluid_real_t a; + + mod->a1 = 2 * FLUID_COS(w); + + a = (2 * FLUID_M_PI / 360) * phase; + + mod->buffer2 = FLUID_SIN(a - w); /* y(n-1) = sin(-intial angle) */ + mod->buffer1 = FLUID_SIN(a); /* y(n) = sin(initial phase) */ + mod->reset_buffer2 = FLUID_SIN(FLUID_M_PI / 2 - w); /* reset value for PI/2 */ +} + +/*----------------------------------------------------------------------------- + Gets current value of sinus modulator: + y(n) = a1 . y(n-1) - y(n-2) + out = a1 . buffer1 - buffer2 + + @param mod pointer on modulator structure. + @return current value of the modulator sine wave. +-----------------------------------------------------------------------------*/ +static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod) +{ + fluid_real_t out; + out = mod->a1 * mod->buffer1 - mod->buffer2; + mod->buffer2 = mod->buffer1; + + if(out >= 1.0f) /* reset in case of instability near PI/2 */ + { + out = 1.0f; /* forces output to the right value */ + mod->buffer2 = mod->reset_buffer2; + } + + if(out <= -1.0f) /* reset in case of instability near -PI/2 */ + { + out = -1.0f; /* forces output to the right value */ + mod->buffer2 = - mod->reset_buffer2; + } + + mod->buffer1 = out; + return out; +} + +/*----------------------------------------------------------------------------- + Set the frequency of triangular oscillator + The frequency is converted in a slope value. + The initial value is set according to frac_phase which is a position + in the period relative to the beginning of the period. + For example: 0 is the beginning of the period, 1/4 is at 1/4 of the period + relative to the beginning. + + @param mod pointer on modulator structure. + @param freq frequency of the oscillator in Hz. + @param sample_rate sample rate on audio output in Hz. + @param frac_phase initial phase (see comment above). +-----------------------------------------------------------------------------*/ +static void set_triangle_frequency(triang_modulator *mod, float freq, + float sample_rate, float frac_phase) +{ + fluid_real_t ns_period; /* period in numbers of sample */ + + if(freq <= 0.0) + { + freq = 0.5f; + } + + mod->freq = freq; + + ns_period = sample_rate / freq; + + /* the slope of a triangular osc (0 up to +1 down to -1 up to 0....) is equivalent + to the slope of a saw osc (0 -> +4) */ + mod->inc = 4 / ns_period; /* positive slope */ + + /* The initial value and the sign of the slope depend of initial phase: + initial value = = (ns_period * frac_phase) * slope + */ + mod->val = ns_period * frac_phase * mod->inc; + + if(1.0 <= mod->val && mod->val < 3.0) + { + mod->val = 2.0 - mod->val; /* 1.0 down to -1.0 */ + mod->inc = -mod->inc; /* negative slope */ + } + else if(3.0 <= mod->val) + { + mod->val = mod->val - 4.0; /* -1.0 up to +1.0. */ + } + + /* else val < 1.0 */ +} + +/*----------------------------------------------------------------------------- + Get current value of triangular oscillator + y(n) = y(n-1) + dy + + @param mod pointer on triang_modulator structure. + @return current value. +-----------------------------------------------------------------------------*/ +static FLUID_INLINE fluid_real_t get_mod_triang(triang_modulator *mod) +{ + mod->val = mod->val + mod->inc ; + + if(mod->val >= 1.0) + { + mod->inc = -mod->inc; + return 1.0; + } + + if(mod->val <= -1.0) + { + mod->inc = -mod->inc; + return -1.0; + } + + return mod->val; +} +/*----------------------------------------------------------------------------- + Reads the sample value out of the modulated delay line. + + @param chorus pointer on chorus unit. + @param mod pointer on modulator structure. + @return current value. +-----------------------------------------------------------------------------*/ +static FLUID_INLINE fluid_real_t get_mod_delay(fluid_chorus_t *chorus, + modulator *mod) +{ + fluid_real_t out_index; /* new modulated index position */ + int int_out_index; /* integer part of out_index */ + fluid_real_t out; /* value to return */ + + /* Checks if the modulator must be updated (every mod_rate samples). */ + /* Important: center_pos_mod must be used immediately for the + first sample. So, mdl->index_rate must be initialized + to mdl->mod_rate (new_mod_delay_line()) */ + + if(chorus->index_rate >= chorus->mod_rate) + { + /* out_index = center position (center_pos_mod) + sinus waweform */ + if(chorus->type == FLUID_CHORUS_MOD_SINE) + { + out_index = chorus->center_pos_mod + + get_mod_sinus(&mod->sinus) * chorus->mod_depth; + } + else + { + out_index = chorus->center_pos_mod + + get_mod_triang(&mod->triang) * chorus->mod_depth; + } + + /* extracts integer part in int_out_index */ + if(out_index >= 0.0f) + { + int_out_index = (int)out_index; /* current integer part */ + + /* forces read index (line_out) with integer modulation value */ + /* Boundary check and circular motion as needed */ + if((mod->line_out = int_out_index) >= chorus->size) + { + mod->line_out -= chorus->size; + } + } + else /* negative */ + { + int_out_index = (int)(out_index - 1); /* previous integer part */ + /* forces read index (line_out) with integer modulation value */ + /* circular motion as needed */ + mod->line_out = int_out_index + chorus->size; + } + + /* extracts fractionnal part. (it will be used when interpolating + between line_out and line_out +1) and memorize it. + Memorizing is necessary for modulation rate above 1 */ + mod->frac_pos_mod = out_index - int_out_index; + } + + /* First order all-pass interpolation ----------------------------------*/ + /* https://ccrma.stanford.edu/~jos/pasp/First_Order_Allpass_Interpolation.html */ + /* begins interpolation: read current sample */ + out = chorus->line[mod->line_out]; + + /* updates line_out to the next sample. + Boundary check and circular motion as needed */ + if(++mod->line_out >= chorus->size) + { + mod->line_out -= chorus->size; + } + + /* Fractional interpolation between next sample (at next position) and + previous output added to current sample. + */ + out += mod->frac_pos_mod * (chorus->line[mod->line_out] - mod->buffer); + mod->buffer = out; /* memorizes current output */ + return out; +} + +/*----------------------------------------------------------------------------- + Push a sample val into the delay line + + @param dl delay line to push value into. + @param val the value to push into dl. +-----------------------------------------------------------------------------*/ +#define push_in_delay_line(dl, val) \ +{\ + dl->line[dl->line_in] = val;\ + /* Incrementation and circular motion if necessary */\ + if(++dl->line_in >= dl->size) dl->line_in -= dl->size;\ +}\ + +/*----------------------------------------------------------------------------- + Initialize : mod_rate, center_pos_mod, and index rate + + center_pos_mod is initialized so that the delay between center_pos_mod and + line_in is: mod_depth + INTERP_SAMPLES_NBR. + + @param chorus pointer on chorus unit. +-----------------------------------------------------------------------------*/ +static void set_center_position(fluid_chorus_t *chorus) +{ + int center; + + /* Sets the modulation rate. This rate defines how often + the center position (center_pos_mod ) is modulated . + The value is expressed in samples. The default value is 1 that means that + center_pos_mod is updated at every sample. + For example with a value of 2, the center position position will be + updated only one time every 2 samples only. + */ + chorus->mod_rate = LOW_MOD_RATE; /* default modulation rate */ + + /* compensate mod rate for high modulation depth */ + if(chorus->mod_depth > LOW_MOD_DEPTH) + { + int delta_mod_depth = (chorus->mod_depth - LOW_MOD_DEPTH); + chorus->mod_rate += (delta_mod_depth * RANGE_MOD_RATE) / RANGE_MOD_DEPTH; + } + + /* Initializes the modulated center position (center_pos_mod) so that: + - the delay between center_pos_mod and line_in is: + mod_depth + INTERP_SAMPLES_NBR. + */ + center = chorus->line_in - (INTERP_SAMPLES_NBR + chorus->mod_depth); + + if(center < 0) + { + center += chorus->size; + } + + chorus->center_pos_mod = (fluid_real_t)center; + + /* index rate to control when to update center_pos_mod */ + /* Important: must be set to get center_pos_mod immediately used for the + reading of first sample (see get_mod_delay()) */ + chorus->index_rate = chorus->mod_rate; +} + +/*----------------------------------------------------------------------------- + Update internal parameters dependent of sample rate. + - mod_depth. + - mod_rate, center_pos_mod, and index rate. + - modulators frequency. + + @param chorus, pointer on chorus unit. +-----------------------------------------------------------------------------*/ +static void update_parameters_from_sample_rate(fluid_chorus_t *chorus) +{ + int i; + + /* initialize modulation depth (peak to peak) (in samples) */ + /* convert modulation depth in ms to sample number */ + chorus->mod_depth = (int)(chorus->depth_ms / 1000.0 + * chorus->sample_rate); + + /* the delay line is fixed. So we reduce mod_depth (if necessary) */ + if(chorus->mod_depth > MAX_SAMPLES) + { + FLUID_LOG(FLUID_WARN, "chorus: Too high depth. Setting it to max (%d).", + MAX_SAMPLES); + chorus->mod_depth = MAX_SAMPLES; + /* set depth_ms to maximum to avoid spamming console with above warning */ + chorus->depth_ms = (chorus->mod_depth * 1000) / chorus->sample_rate; + } + + chorus->mod_depth /= 2; /* amplitude is peak to peek / 2 */ +#ifdef DEBUG_PRINT + printf("depth_ms:%f, depth_samples/2:%d\n", chorus->depth_ms, chorus->mod_depth); +#endif + + /* Initializes the modulated center position: + mod_rate, center_pos_mod, and index rate. + */ + set_center_position(chorus); /* must be called before set_xxxx_frequency() */ +#ifdef DEBUG_PRINT + printf("mod_rate:%d\n", chorus->mod_rate); +#endif + + /* initialize modulator frequency */ + for(i = 0; i < chorus->number_blocks; i++) + { + set_sinus_frequency(&chorus->mod[i].sinus, + chorus->speed_Hz * chorus->mod_rate, + chorus->sample_rate, + /* phase offset between modulators waveform */ + (float)((360.0f / (float) chorus->number_blocks) * i)); + + set_triangle_frequency(&chorus->mod[i].triang, + chorus->speed_Hz * chorus->mod_rate, + chorus->sample_rate, + /* phase offset between modulators waveform */ + (float)i / chorus->number_blocks); + } +} + +/*----------------------------------------------------------------------------- + Modulated delay line initialization. + + Sets the length line ( alloc delay samples). + Remark: the function sets the internal size according to the length delay_length. + The size is augmented by INTERP_SAMPLES_NBR to take account of interpolation. + + @param chorus, pointer on chorus unit. + @param delay_length the length of the delay line in samples. + @return FLUID_OK if success , FLUID_FAILED if memory error. + + Return FLUID_OK if success, FLUID_FAILED if memory error. +-----------------------------------------------------------------------------*/ +static int new_mod_delay_line(fluid_chorus_t *chorus, int delay_length) +{ + /*-----------------------------------------------------------------------*/ + /* checks parameter */ + if(delay_length < 1) + { + return FLUID_FAILED; + } + + chorus->mod_depth = 0; + /*----------------------------------------------------------------------- + allocates delay_line and initialize members: - line, size, line_in... + */ + /* total size of the line: size = INTERP_SAMPLES_NBR + delay_length */ + chorus->size = delay_length + INTERP_SAMPLES_NBR; + chorus->line = FLUID_ARRAY(fluid_real_t, chorus->size); + + if(! chorus->line) + { + return FLUID_FAILED; + } + + /* clears the buffer: + - delay line + - interpolator member: buffer, frac_pos_mod + */ + fluid_chorus_reset(chorus); + + /* Initializes line_in to the start of the buffer */ + chorus->line_in = 0; + /*------------------------------------------------------------------------ + Initializes modulation members: + - modulation rate (the speed at which center_pos_mod is modulated: mod_rate + - modulated center position: center_pos_mod + - index rate to know when to update center_pos_mod:index_rate + -------------------------------------------------------------------------*/ + /* Initializes the modulated center position: + mod_rate, center_pos_mod, and index rate + */ + set_center_position(chorus); + + return FLUID_OK; +} + +/*----------------------------------------------------------------------------- + API +------------------------------------------------------------------------------*/ +/** + * Create the chorus unit. Once created the chorus have no parameters set, so + * fluid_chorus_set() must be called at least one time after calling + * new_fluid_chorus(). + * + * @param sample_rate, audio sample rate in Hz. + * @return pointer on chorus unit. + */ +fluid_chorus_t * +new_fluid_chorus(fluid_real_t sample_rate) +{ + fluid_chorus_t *chorus; + + chorus = FLUID_NEW(fluid_chorus_t); + + if(chorus == NULL) + { + FLUID_LOG(FLUID_PANIC, "chorus: Out of memory"); + return NULL; + } + + FLUID_MEMSET(chorus, 0, sizeof(fluid_chorus_t)); + + chorus->sample_rate = sample_rate; + +#ifdef DEBUG_PRINT + printf("fluid_chorus_t:%d bytes\n", sizeof(fluid_chorus_t)); + printf("fluid_real_t:%d bytes\n", sizeof(fluid_real_t)); +#endif + +#ifdef DEBUG_PRINT + printf("NEW_MOD\n"); +#endif + + if(new_mod_delay_line(chorus, MAX_SAMPLES) == FLUID_FAILED) + { + delete_fluid_chorus(chorus); + return NULL; + } + + return chorus; +} + +/** + * Delete the chorus unit. + * @param chorus pointer on chorus unit returned by new_fluid_chorus(). + */ +void +delete_fluid_chorus(fluid_chorus_t *chorus) +{ + fluid_return_if_fail(chorus != NULL); + + FLUID_FREE(chorus->line); + FLUID_FREE(chorus); +} + +/** + * Clear the internal delay line and associate filter. + * @param chorus pointer on chorus unit returned by new_fluid_chorus(). + */ +void +fluid_chorus_reset(fluid_chorus_t *chorus) +{ + int i; + unsigned int u; + + /* reset delay line */ + for(i = 0; i < chorus->size; i++) + { + chorus->line[i] = 0; + } + + /* reset modulators's allpass filter */ + for(u = 0; u < FLUID_N_ELEMENTS(chorus->mod); u++) + { + /* initializes 1st order All-Pass interpolator members */ + chorus->mod[u].buffer = 0; /* previous delay sample value */ + chorus->mod[u].frac_pos_mod = 0; /* fractional position (between consecutives sample) */ + } +} + +/** + * Set one or more chorus parameters. + * + * @param chorus Chorus instance. + * @param set Flags indicating which chorus parameters to set (#fluid_chorus_set_t). + * @param nr Chorus voice count (0-99, CPU time consumption proportional to + * this value). + * @param level Chorus level (0.0-10.0). + * @param speed Chorus speed in Hz (0.1-5.0). + * @param depth_ms Chorus depth (max value depends on synth sample rate, + * 0.0-21.0 is safe for sample rate values up to 96KHz). + * @param type Chorus waveform type (#fluid_chorus_mod). + */ +void +fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, + fluid_real_t speed, fluid_real_t depth_ms, int type) +{ + if(set & FLUID_CHORUS_SET_NR) /* number of block */ + { + chorus->number_blocks = nr; + } + + if(set & FLUID_CHORUS_SET_LEVEL) /* output level */ + { + chorus->level = level; + } + + if(set & FLUID_CHORUS_SET_SPEED) /* lfo frequency (in Hz) */ + { + chorus->speed_Hz = speed; + } + + if(set & FLUID_CHORUS_SET_DEPTH) /* modulation depth (in ms) */ + { + chorus->depth_ms = depth_ms; + } + + if(set & FLUID_CHORUS_SET_TYPE) /* lfo shape (sinus, triangle) */ + { + chorus->type = type; + } + + /* check min , max parameters */ + if(chorus->number_blocks < 0) + { + FLUID_LOG(FLUID_WARN, "chorus: number blocks must be >=0! Setting value to 0."); + chorus->number_blocks = 0; + } + else if(chorus->number_blocks > MAX_CHORUS) + { + FLUID_LOG(FLUID_WARN, "chorus: number blocks larger than max. allowed! Setting value to %d.", + MAX_CHORUS); + chorus->number_blocks = MAX_CHORUS; + } + + if(chorus->speed_Hz < MIN_SPEED_HZ) + { + FLUID_LOG(FLUID_WARN, "chorus: speed is too low (min %f)! Setting value to min.", + (double) MIN_SPEED_HZ); + chorus->speed_Hz = MIN_SPEED_HZ; + } + else if(chorus->speed_Hz > MAX_SPEED_HZ) + { + FLUID_LOG(FLUID_WARN, "chorus: speed must be below %f Hz! Setting value to max.", + (double) MAX_SPEED_HZ); + chorus->speed_Hz = MAX_SPEED_HZ; + } + + if(chorus->depth_ms < 0.0) + { + FLUID_LOG(FLUID_WARN, "chorus: depth must be positive! Setting value to 0."); + chorus->depth_ms = 0.0; + } + + if(chorus->level < 0.0) + { + FLUID_LOG(FLUID_WARN, "chorus: level must be positive! Setting value to 0."); + chorus->level = 0.0; + } + else if(chorus->level > MAX_LEVEL) + { + FLUID_LOG(FLUID_WARN, "chorus: level must be < 10. A reasonable level is << 1! " + "Setting it to 0.1."); + chorus->level = 0.1; + } + + /* update parameters dependent of sample rate */ + update_parameters_from_sample_rate(chorus); + +#ifdef DEBUG_PRINT + printf("lfo type:%d\n", chorus->type); + printf("speed_Hz:%f\n", chorus->speed_Hz); +#endif + + /* Initialize the lfo waveform */ + if((chorus->type != FLUID_CHORUS_MOD_SINE) && + (chorus->type != FLUID_CHORUS_MOD_TRIANGLE)) + { + FLUID_LOG(FLUID_WARN, "chorus: Unknown modulation type. Using sinewave."); + chorus->type = FLUID_CHORUS_MOD_SINE; + } + +#ifdef DEBUG_PRINT + + if(chorus->type == FLUID_CHORUS_MOD_SINE) + { + printf("lfo: sinus\n"); + } + else + { + printf("lfo: triangle\n"); + } + + printf("nr:%d\n", chorus->number_blocks); +#endif + + /* Recalculate internal values after parameters change */ + + /* + Note: + Actually WIDTH is fixed to maximum value. But in the future we could add a setting + "synth.chorus.width" to allow a gradually stereo effect from minimum (monophonic) to + maximum stereo effect. + If this setting will be added, remove the following instruction. + */ + chorus->width = WIDTH; + { + /* The stereo amplitude equation (wet1 and wet2 below) have a + tendency to produce high amplitude with high width values ( 1 < width < 10). + This results in an unwanted noisy output clipped by the audio card. + To avoid this dependency, we divide by (1 + chorus->width * SCALE_WET_WIDTH) + Actually, with a SCALE_WET_WIDTH of 0.2, (regardless of level setting), + the output amplitude (wet) seems rather independent of width setting */ + + fluid_real_t wet = chorus->level * SCALE_WET ; + + /* wet1 and wet2 are used by the stereo effect controlled by the width setting + for producing a stereo ouptput from a monophonic chorus signal. + Please see the note above about a side effect tendency */ + + if(chorus->number_blocks > 1) + { + wet = wet / (1.0f + chorus->width * SCALE_WET_WIDTH); + chorus->wet1 = wet * (chorus->width / 2.0f + 0.5f); + chorus->wet2 = wet * ((1.0f - chorus->width) / 2.0f); +#ifdef DEBUG_PRINT + printf("width:%f\n", chorus->width); + + if(chorus->width > 0) + { + printf("nr > 1, width > 0 => out stereo\n"); + } + else + { + printf("nr > 1, width:0 =>out mono\n"); + } + +#endif + } + else + { + /* only one chorus block */ + if(chorus->width == 0.0) + { + /* wet1 and wet2 should make stereo output monomophic */ + chorus->wet1 = chorus->wet2 = wet; + } + else + { + /* for width > 0, wet1 and wet2 should make stereo output stereo + with only one block. This will only possible by inverting + the unique signal on each left and right output. + Note however that with only one block, it isn't possible to + have a graduate width effect */ + chorus->wet1 = wet; + chorus->wet2 = -wet; /* inversion */ + } + +#ifdef DEBUG_PRINT + printf("width:%f\n", chorus->width); + + if(chorus->width != 0) + { + printf("one block, width > 0 => out stereo\n"); + } + else + { + printf("one block, width:0 => out mono\n"); + } + +#endif + } + } +} + +/* +* Applies a sample rate change on the chorus. +* Note that while the chorus is used by calling any fluid_chorus_processXXX() +* function, calling fluid_chorus_samplerate_change() isn't multi task safe. +* To deal properly with this issue follow the steps: +* 1) Stop chorus processing (i.e disable calling to any fluid_chorus_processXXX(). +* chorus functions. +* 2) Change sample rate by calling fluid_chorus_samplerate_change(). +* 3) Restart chorus processing (i.e enabling calling any fluid_chorus_processXXX() +* chorus functions. +* +* Another solution is to substitute step (2): +* 2.1) delete the chorus by calling delete_fluid_chorus(). +* 2.2) create the chorus by calling new_fluid_chorus(). +* +* @param chorus pointer on the chorus. +* @param sample_rate new sample rate value. +*/ +void +fluid_chorus_samplerate_change(fluid_chorus_t *chorus, fluid_real_t sample_rate) +{ + chorus->sample_rate = sample_rate; + + /* update parameters dependent of sample rate */ + update_parameters_from_sample_rate(chorus); +} + +/** + * Process chorus by mixing the result in output buffer. + * @param chorus pointer on chorus unit returned by new_fluid_chorus(). + * @param in, pointer on monophonic input buffer of FLUID_BUFSIZE samples. + * @param left_out, right_out, pointers on stereo output buffers of + * FLUID_BUFSIZE samples. + */ +void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int sample_index; + int i; + fluid_real_t d_out[2]; /* output stereo Left and Right */ + + /* foreach sample, process output sample then input sample */ + for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) + { + fluid_real_t out; /* block output */ + + d_out[0] = d_out[1] = 0.0f; /* clear stereo unit input */ + +#if 0 + /* Debug: Listen to the chorus signal only */ + left_out[sample_index] = 0; + right_out[sample_index] = 0; +#endif + + ++chorus->index_rate; /* modulator rate */ + + /* foreach chorus block, process output sample */ + for(i = 0; i < chorus->number_blocks; i++) + { + /* get sample from the output of modulated delay line */ + out = get_mod_delay(chorus, &chorus->mod[i]); + + /* accumulate out into stereo unit input */ + d_out[i & 1] += out; + } + + /* update modulator index rate and output center position */ + if(chorus->index_rate >= chorus->mod_rate) + { + chorus->index_rate = 0; /* clear modulator index rate */ + + /* updates center position (center_pos_mod) to the next position + specified by modulation rate */ + if((chorus->center_pos_mod += chorus->mod_rate) >= chorus->size) + { + chorus->center_pos_mod -= chorus->size; + } + } + + /* Adjust stereo input level in case of number_blocks odd: + In those case, d_out[1] level is lower than d_out[0], so we need to + add out value to d_out[1] to have d_out[0] and d_out[1] balanced. + */ + if((i & 1) && i > 2) // i = 3,5,7... + { + d_out[1] += out ; + } + + /* Write the current input sample into the circular buffer. + * Note that 'in' may be aliased with 'left_out'. Hence this must be done + * before "processing stereo unit" (below). This ensures input buffer + * not being overwritten by stereo unit output. + */ + push_in_delay_line(chorus, in[sample_index]); + + /* process stereo unit */ + /* Add the chorus stereo unit d_out to left and right output */ + left_out[sample_index] += d_out[0] * chorus->wet1 + d_out[1] * chorus->wet2; + right_out[sample_index] += d_out[1] * chorus->wet1 + d_out[0] * chorus->wet2; + } +} + +/** + * Process chorus by putting the result in output buffer (no mixing). + * @param chorus pointer on chorus unit returned by new_fluid_chorus(). + * @param in, pointer on monophonic input buffer of FLUID_BUFSIZE samples. + * @param left_out, right_out, pointers on stereo output buffers of + * FLUID_BUFSIZE samples. + */ +/* Duplication of code ... (replaces sample data instead of mixing) */ +void fluid_chorus_processreplace(fluid_chorus_t *chorus, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int sample_index; + int i; + fluid_real_t d_out[2]; /* output stereo Left and Right */ + + /* foreach sample, process output sample then input sample */ + for(sample_index = 0; sample_index < FLUID_BUFSIZE; sample_index++) + { + fluid_real_t out; /* block output */ + + d_out[0] = d_out[1] = 0.0f; /* clear stereo unit input */ + +#if 0 + /* Debug: Listen to the chorus signal only */ + left_out[sample_index] = 0; + right_out[sample_index] = 0; +#endif + + ++chorus->index_rate; /* modulator rate */ + + /* foreach chorus block, process output sample */ + for(i = 0; i < chorus->number_blocks; i++) + { + /* get sample from the output of modulated delay line */ + out = get_mod_delay(chorus, &chorus->mod[i]); + + /* accumulate out into stereo unit input */ + d_out[i & 1] += out; + } + + /* update modulator index rate and output center position */ + if(chorus->index_rate >= chorus->mod_rate) + { + chorus->index_rate = 0; /* clear modulator index rate */ + + /* updates center position (center_pos_mod) to the next position + specified by modulation rate */ + if((chorus->center_pos_mod += chorus->mod_rate) >= chorus->size) + { + chorus->center_pos_mod -= chorus->size; + } + } + + /* Adjust stereo input level in case of number_blocks odd: + In those case, d_out[1] level is lower than d_out[0], so we need to + add out value to d_out[1] to have d_out[0] and d_out[1] balanced. + */ + if((i & 1) && i > 2) // i = 3,5,7... + { + d_out[1] += out ; + } + + /* Write the current input sample into the circular buffer. + * Note that 'in' may be aliased with 'left_out'. Hence this must be done + * before "processing stereo unit" (below). This ensures input buffer + * not being overwritten by stereo unit output. + */ + push_in_delay_line(chorus, in[sample_index]); + + /* process stereo unit */ + /* store the chorus stereo unit d_out to left and right output */ + left_out[sample_index] = d_out[0] * chorus->wet1 + d_out[1] * chorus->wet2; + right_out[sample_index] = d_out[1] * chorus->wet1 + d_out[0] * chorus->wet2; + } +} diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_chorus.h b/thirdparty/fluidsynth/src/rvoice/fluid_chorus.h new file mode 100644 index 000000000..c6d247fa5 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_chorus.h @@ -0,0 +1,80 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_CHORUS_H +#define _FLUID_CHORUS_H + +#include "fluidsynth_priv.h" + + +typedef struct _fluid_chorus_t fluid_chorus_t; + +/* enum describing each chorus parameter */ +enum fluid_chorus_param +{ + FLUID_CHORUS_NR, /**< number of delay line */ + FLUID_CHORUS_LEVEL, /**< output level */ + FLUID_CHORUS_SPEED, /**< lfo frequency */ + FLUID_CHORUS_DEPTH, /**< modulation depth */ + FLUID_CHORUS_TYPE, /**< type of waveform */ + FLUID_CHORUS_PARAM_LAST /* number of enum fluid_chorus_param */ +}; + +/* return a bit flag from param: 2^param */ +#define FLUID_CHORPARAM_TO_SETFLAG(param) (1 << param) + +/** Flags for fluid_chorus_set() */ +typedef enum +{ + FLUID_CHORUS_SET_NR = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_NR), + FLUID_CHORUS_SET_LEVEL = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_LEVEL), + FLUID_CHORUS_SET_SPEED = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_SPEED), + FLUID_CHORUS_SET_DEPTH = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_DEPTH), + FLUID_CHORUS_SET_TYPE = FLUID_CHORPARAM_TO_SETFLAG(FLUID_CHORUS_TYPE), + + /** Value for fluid_chorus_set() which sets all chorus parameters. */ + FLUID_CHORUS_SET_ALL = FLUID_CHORUS_SET_NR + | FLUID_CHORUS_SET_LEVEL + | FLUID_CHORUS_SET_SPEED + | FLUID_CHORUS_SET_DEPTH + | FLUID_CHORUS_SET_TYPE, +} fluid_chorus_set_t; + +/* + * chorus + */ +fluid_chorus_t *new_fluid_chorus(fluid_real_t sample_rate); +void delete_fluid_chorus(fluid_chorus_t *chorus); +void fluid_chorus_reset(fluid_chorus_t *chorus); + +void fluid_chorus_set(fluid_chorus_t *chorus, int set, int nr, fluid_real_t level, + fluid_real_t speed, fluid_real_t depth_ms, int type); +void +fluid_chorus_samplerate_change(fluid_chorus_t *chorus, fluid_real_t sample_rate); + +void fluid_chorus_processmix(fluid_chorus_t *chorus, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); +void fluid_chorus_processreplace(fluid_chorus_t *chorus, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); + + + +#endif /* _FLUID_CHORUS_H */ diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_iir_filter.c b/thirdparty/fluidsynth/src/rvoice/fluid_iir_filter.c new file mode 100644 index 000000000..0be192f1f --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_iir_filter.c @@ -0,0 +1,419 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_iir_filter.h" +#include "fluid_sys.h" +#include "fluid_conv.h" + +/** + * Applies a low- or high-pass filter with variable cutoff frequency and quality factor + * for a given biquad transfer function: + * b0 + b1*z^-1 + b2*z^-2 + * H(z) = ------------------------ + * a0 + a1*z^-1 + a2*z^-2 + * + * Also modifies filter state accordingly. + * @param iir_filter Filter parameter + * @param dsp_buf Pointer to the synthesized audio data + * @param count Count of samples in dsp_buf + */ +/* + * Variable description: + * - dsp_a1, dsp_a2: Filter coefficients for the the previously filtered output signal + * - dsp_b0, dsp_b1, dsp_b2: Filter coefficients for input signal + * - coefficients normalized to a0 + * + * A couple of variables are used internally, their results are discarded: + * - dsp_i: Index through the output buffer + * - dsp_centernode: delay line for the IIR filter + * - dsp_hist1: same + * - dsp_hist2: same + */ +void +fluid_iir_filter_apply(fluid_iir_filter_t *iir_filter, + fluid_real_t *dsp_buf, int count) +{ + if(iir_filter->type == FLUID_IIR_DISABLED || iir_filter->q_lin == 0) + { + return; + } + else + { + /* IIR filter sample history */ + fluid_real_t dsp_hist1 = iir_filter->hist1; + fluid_real_t dsp_hist2 = iir_filter->hist2; + + /* IIR filter coefficients */ + fluid_real_t dsp_a1 = iir_filter->a1; + fluid_real_t dsp_a2 = iir_filter->a2; + fluid_real_t dsp_b02 = iir_filter->b02; + fluid_real_t dsp_b1 = iir_filter->b1; + int dsp_filter_coeff_incr_count = iir_filter->filter_coeff_incr_count; + + fluid_real_t dsp_centernode; + int dsp_i; + + /* filter (implement the voice filter according to SoundFont standard) */ + + /* Check for denormal number (too close to zero). */ + if(FLUID_FABS(dsp_hist1) < 1e-20f) + { + dsp_hist1 = 0.0f; /* FIXME JMG - Is this even needed? */ + } + + /* Two versions of the filter loop. One, while the filter is + * changing towards its new setting. The other, if the filter + * doesn't change. + */ + + if(dsp_filter_coeff_incr_count > 0) + { + fluid_real_t dsp_a1_incr = iir_filter->a1_incr; + fluid_real_t dsp_a2_incr = iir_filter->a2_incr; + fluid_real_t dsp_b02_incr = iir_filter->b02_incr; + fluid_real_t dsp_b1_incr = iir_filter->b1_incr; + + + /* Increment is added to each filter coefficient filter_coeff_incr_count times. */ + for(dsp_i = 0; dsp_i < count; dsp_i++) + { + /* The filter is implemented in Direct-II form. */ + dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; + dsp_hist2 = dsp_hist1; + dsp_hist1 = dsp_centernode; + + if(dsp_filter_coeff_incr_count-- > 0) + { + fluid_real_t old_b02 = dsp_b02; + dsp_a1 += dsp_a1_incr; + dsp_a2 += dsp_a2_incr; + dsp_b02 += dsp_b02_incr; + dsp_b1 += dsp_b1_incr; + + /* Compensate history to avoid the filter going havoc with large frequency changes */ + if(iir_filter->compensate_incr && FLUID_FABS(dsp_b02) > 0.001f) + { + fluid_real_t compensate = old_b02 / dsp_b02; + dsp_hist1 *= compensate; + dsp_hist2 *= compensate; + } + } + } /* for dsp_i */ + } + else /* The filter parameters are constant. This is duplicated to save time. */ + { + for(dsp_i = 0; dsp_i < count; dsp_i++) + { + /* The filter is implemented in Direct-II form. */ + dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2; + dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1; + dsp_hist2 = dsp_hist1; + dsp_hist1 = dsp_centernode; + } + } + + iir_filter->hist1 = dsp_hist1; + iir_filter->hist2 = dsp_hist2; + iir_filter->a1 = dsp_a1; + iir_filter->a2 = dsp_a2; + iir_filter->b02 = dsp_b02; + iir_filter->b1 = dsp_b1; + iir_filter->filter_coeff_incr_count = dsp_filter_coeff_incr_count; + + fluid_check_fpe("voice_filter"); + } +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_init) +{ + fluid_iir_filter_t *iir_filter = obj; + enum fluid_iir_filter_type type = param[0].i; + enum fluid_iir_filter_flags flags = param[1].i; + + iir_filter->type = type; + iir_filter->flags = flags; + + if(type != FLUID_IIR_DISABLED) + { + fluid_iir_filter_reset(iir_filter); + } +} + +void +fluid_iir_filter_reset(fluid_iir_filter_t *iir_filter) +{ + iir_filter->hist1 = 0; + iir_filter->hist2 = 0; + iir_filter->last_fres = -1.; + iir_filter->q_lin = 0; + iir_filter->filter_startup = 1; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_fres) +{ + fluid_iir_filter_t *iir_filter = obj; + fluid_real_t fres = param[0].real; + + iir_filter->fres = fres; + iir_filter->last_fres = -1.; +} + +static fluid_real_t fluid_iir_filter_q_from_dB(fluid_real_t q_dB) +{ + /* The generator contains 'centibels' (1/10 dB) => divide by 10 to + * obtain dB */ + q_dB /= 10.0f; + + /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */ + fluid_clip(q_dB, 0.0f, 96.0f); + + /* Short version: Modify the Q definition in a way, that a Q of 0 + * dB leads to no resonance hump in the freq. response. + * + * Long version: From SF2.01, page 39, item 9 (initialFilterQ): + * "The gain at the cutoff frequency may be less than zero when + * zero is specified". Assume q_dB=0 / q_lin=1: If we would leave + * q as it is, then this results in a 3 dB hump slightly below + * fc. At fc, the gain is exactly the DC gain (0 dB). What is + * (probably) meant here is that the filter does not show a + * resonance hump for q_dB=0. In this case, the corresponding + * q_lin is 1/sqrt(2)=0.707. The filter should have 3 dB of + * attenuation at fc now. In this case Q_dB is the height of the + * resonance peak not over the DC gain, but over the frequency + * response of a non-resonant filter. This idea is implemented as + * follows: */ + q_dB -= 3.01f; + + /* The 'sound font' Q is defined in dB. The filter needs a linear + q. Convert. */ + return FLUID_POW(10.0f, q_dB / 20.0f); +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_q) +{ + fluid_iir_filter_t *iir_filter = obj; + fluid_real_t q = param[0].real; + int flags = iir_filter->flags; + + if(flags & FLUID_IIR_Q_ZERO_OFF && q <= 0.0) + { + q = 0; + } + else if(flags & FLUID_IIR_Q_LINEAR) + { + /* q is linear (only for user-defined filter) + * increase to avoid Q being somewhere between zero and one, + * which results in some strange amplified lowpass signal + */ + q++; + } + else + { + q = fluid_iir_filter_q_from_dB(q); + } + + iir_filter->q_lin = q; + iir_filter->filter_gain = 1.0; + + if(!(flags & FLUID_IIR_NO_GAIN_AMP)) + { + /* SF 2.01 page 59: + * + * The SoundFont specs ask for a gain reduction equal to half the + * height of the resonance peak (Q). For example, for a 10 dB + * resonance peak, the gain is reduced by 5 dB. This is done by + * multiplying the total gain with sqrt(1/Q). `Sqrt' divides dB + * by 2 (100 lin = 40 dB, 10 lin = 20 dB, 3.16 lin = 10 dB etc) + * The gain is later factored into the 'b' coefficients + * (numerator of the filter equation). This gain factor depends + * only on Q, so this is the right place to calculate it. + */ + iir_filter->filter_gain /= FLUID_SQRT(q); + } + + /* The synthesis loop will have to recalculate the filter coefficients. */ + iir_filter->last_fres = -1.; +} + +static FLUID_INLINE void +fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t *iir_filter, + int transition_samples, + fluid_real_t output_rate) +{ + /* FLUID_IIR_Q_LINEAR may switch the filter off by setting Q==0 */ + if(iir_filter->q_lin == 0) + { + return; + } + else + { + /* + * Those equations from Robert Bristow-Johnson's `Cookbook + * formulae for audio EQ biquad filter coefficients', obtained + * from Harmony-central.com / Computer / Programming. They are + * the result of the bilinear transform on an analogue filter + * prototype. To quote, `BLT frequency warping has been taken + * into account for both significant frequency relocation and for + * bandwidth readjustment'. */ + + fluid_real_t omega = (fluid_real_t)(2.0 * M_PI) * + (iir_filter->last_fres / output_rate); + fluid_real_t sin_coeff = FLUID_SIN(omega); + fluid_real_t cos_coeff = FLUID_COS(omega); + fluid_real_t alpha_coeff = sin_coeff / (2.0f * iir_filter->q_lin); + fluid_real_t a0_inv = 1.0f / (1.0f + alpha_coeff); + + /* Calculate the filter coefficients. All coefficients are + * normalized by a0. Think of `a1' as `a1/a0'. + * + * Here a couple of multiplications are saved by reusing common expressions. + * The original equations should be: + * iir_filter->b0=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; + * iir_filter->b1=(1.-cos_coeff)*a0_inv*iir_filter->filter_gain; + * iir_filter->b2=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; */ + + /* "a" coeffs are same for all 3 available filter types */ + fluid_real_t a1_temp = -2.0f * cos_coeff * a0_inv; + fluid_real_t a2_temp = (1.0f - alpha_coeff) * a0_inv; + + fluid_real_t b02_temp, b1_temp; + + switch(iir_filter->type) + { + case FLUID_IIR_HIGHPASS: + b1_temp = (1.0f + cos_coeff) * a0_inv * iir_filter->filter_gain; + + /* both b0 -and- b2 */ + b02_temp = b1_temp * 0.5f; + + b1_temp *= -1.0f; + break; + + case FLUID_IIR_LOWPASS: + b1_temp = (1.0f - cos_coeff) * a0_inv * iir_filter->filter_gain; + + /* both b0 -and- b2 */ + b02_temp = b1_temp * 0.5f; + break; + + default: + /* filter disabled, should never get here */ + return; + } + + iir_filter->compensate_incr = 0; + + if(iir_filter->filter_startup || (transition_samples == 0)) + { + /* The filter is calculated, because the voice was started up. + * In this case set the filter coefficients without delay. + */ + iir_filter->a1 = a1_temp; + iir_filter->a2 = a2_temp; + iir_filter->b02 = b02_temp; + iir_filter->b1 = b1_temp; + iir_filter->filter_coeff_incr_count = 0; + iir_filter->filter_startup = 0; +// printf("Setting initial filter coefficients.\n"); + } + else + { + + /* The filter frequency is changed. Calculate an increment + * factor, so that the new setting is reached after one buffer + * length. x_incr is added to the current value FLUID_BUFSIZE + * times. The length is arbitrarily chosen. Longer than one + * buffer will sacrifice some performance, though. Note: If + * the filter is still too 'grainy', then increase this number + * at will. + */ + + iir_filter->a1_incr = (a1_temp - iir_filter->a1) / transition_samples; + iir_filter->a2_incr = (a2_temp - iir_filter->a2) / transition_samples; + iir_filter->b02_incr = (b02_temp - iir_filter->b02) / transition_samples; + iir_filter->b1_incr = (b1_temp - iir_filter->b1) / transition_samples; + + if(FLUID_FABS(iir_filter->b02) > 0.0001f) + { + fluid_real_t quota = b02_temp / iir_filter->b02; + iir_filter->compensate_incr = quota < 0.5f || quota > 2.f; + } + + /* Have to add the increments filter_coeff_incr_count times. */ + iir_filter->filter_coeff_incr_count = transition_samples; + } + + fluid_check_fpe("voice_write filter calculation"); + } +} + + +void fluid_iir_filter_calc(fluid_iir_filter_t *iir_filter, + fluid_real_t output_rate, + fluid_real_t fres_mod) +{ + fluid_real_t fres; + + /* calculate the frequency of the resonant filter in Hz */ + fres = fluid_ct2hz(iir_filter->fres + fres_mod); + + /* FIXME - Still potential for a click during turn on, can we interpolate + between 20khz cutoff and 0 Q? */ + + /* I removed the optimization of turning the filter off when the + * resonance frequence is above the maximum frequency. Instead, the + * filter frequency is set to a maximum of 0.45 times the sampling + * rate. For a 44100 kHz sampling rate, this amounts to 19845 + * Hz. The reason is that there were problems with anti-aliasing when the + * synthesizer was run at lower sampling rates. Thanks to Stephan + * Tassart for pointing me to this bug. By turning the filter on and + * clipping the maximum filter frequency at 0.45*srate, the filter + * is used as an anti-aliasing filter. */ + + if(fres > 0.45f * output_rate) + { + fres = 0.45f * output_rate; + } + else if(fres < 5.f) + { + fres = 5.f; + } + + /* if filter enabled and there is a significant frequency change.. */ + if(iir_filter->type != FLUID_IIR_DISABLED && FLUID_FABS(fres - iir_filter->last_fres) > 0.01f) + { + /* The filter coefficients have to be recalculated (filter + * parameters have changed). Recalculation for various reasons is + * forced by setting last_fres to -1. The flag filter_startup + * indicates, that the DSP loop runs for the first time, in this + * case, the filter is set directly, instead of smoothly fading + * between old and new settings. */ + iir_filter->last_fres = fres; + fluid_iir_filter_calculate_coefficients(iir_filter, FLUID_BUFSIZE, + output_rate); + } + + + fluid_check_fpe("voice_write DSP coefficients"); + +} + diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_iir_filter.h b/thirdparty/fluidsynth/src/rvoice/fluid_iir_filter.h new file mode 100644 index 000000000..355d197f1 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_iir_filter.h @@ -0,0 +1,75 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_IIR_FILTER_H +#define _FLUID_IIR_FILTER_H + +#include "fluidsynth_priv.h" + +typedef struct _fluid_iir_filter_t fluid_iir_filter_t; + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_init); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_fres); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_iir_filter_set_q); + +void fluid_iir_filter_apply(fluid_iir_filter_t *iir_filter, + fluid_real_t *dsp_buf, int dsp_buf_count); + +void fluid_iir_filter_reset(fluid_iir_filter_t *iir_filter); + +void fluid_iir_filter_calc(fluid_iir_filter_t *iir_filter, + fluid_real_t output_rate, + fluid_real_t fres_mod); + +/* We can't do information hiding here, as fluid_voice_t includes the struct + without a pointer. */ +struct _fluid_iir_filter_t +{ + enum fluid_iir_filter_type type; /* specifies the type of this filter */ + enum fluid_iir_filter_flags flags; /* additional flags to customize this filter */ + + /* filter coefficients */ + /* The coefficients are normalized to a0. */ + /* b0 and b2 are identical => b02 */ + fluid_real_t b02; /* b0 / a0 */ + fluid_real_t b1; /* b1 / a0 */ + fluid_real_t a1; /* a0 / a0 */ + fluid_real_t a2; /* a1 / a0 */ + + fluid_real_t b02_incr; + fluid_real_t b1_incr; + fluid_real_t a1_incr; + fluid_real_t a2_incr; + int filter_coeff_incr_count; + int compensate_incr; /* Flag: If set, must compensate history */ + fluid_real_t hist1, hist2; /* Sample history for the IIR filter */ + int filter_startup; /* Flag: If set, the filter will be set directly. + Else it changes smoothly. */ + + fluid_real_t fres; /* the resonance frequency, in cents (not absolute cents) */ + fluid_real_t last_fres; /* Current resonance frequency of the IIR filter */ + /* Serves as a flag: A deviation between fres and last_fres */ + /* indicates, that the filter has to be recalculated. */ + fluid_real_t q_lin; /* the q-factor on a linear scale */ + fluid_real_t filter_gain; /* Gain correction factor, depends on q */ +}; + +#endif + diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_lfo.c b/thirdparty/fluidsynth/src/rvoice/fluid_lfo.c new file mode 100644 index 000000000..ae21cdd0f --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_lfo.c @@ -0,0 +1,17 @@ +#include "fluid_lfo.h" + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_incr) +{ + fluid_lfo_t *lfo = obj; + fluid_real_t increment = param[0].real; + + lfo->increment = increment; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_delay) +{ + fluid_lfo_t *lfo = obj; + unsigned int delay = param[0].i; + + lfo->delay = delay; +} diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_lfo.h b/thirdparty/fluidsynth/src/rvoice/fluid_lfo.h new file mode 100644 index 000000000..b9a9ca6ea --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_lfo.h @@ -0,0 +1,75 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_LFO_H +#define _FLUID_LFO_H + +#include "fluid_sys.h" + +typedef struct _fluid_lfo_t fluid_lfo_t; + +struct _fluid_lfo_t +{ + fluid_real_t val; /* the current value of the LFO */ + unsigned int delay; /* the delay of the lfo in samples */ + fluid_real_t increment; /* the lfo frequency is converted to a per-buffer increment */ +}; + +static FLUID_INLINE void +fluid_lfo_reset(fluid_lfo_t *lfo) +{ + lfo->val = 0.0f; +} + +// These two cannot be inlined since they're used by event_dispatch +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_incr); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_lfo_set_delay); + +static FLUID_INLINE fluid_real_t +fluid_lfo_get_val(fluid_lfo_t *lfo) +{ + return lfo->val; +} + +static FLUID_INLINE void +fluid_lfo_calc(fluid_lfo_t *lfo, unsigned int cur_delay) +{ + if(cur_delay < lfo->delay) + { + return; + } + + lfo->val += lfo->increment; + + if(lfo->val > (fluid_real_t) 1.0) + { + lfo->increment = -lfo->increment; + lfo->val = (fluid_real_t) 2.0 - lfo->val; + } + else if(lfo->val < (fluid_real_t) -1.0) + { + lfo->increment = -lfo->increment; + lfo->val = (fluid_real_t) -2.0 - lfo->val; + } + +} + +#endif + diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_phase.h b/thirdparty/fluidsynth/src/rvoice/fluid_phase.h new file mode 100644 index 000000000..44df6b249 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_phase.h @@ -0,0 +1,113 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_PHASE_H +#define _FLUID_PHASE_H + +/* + * phase + */ + +#define FLUID_INTERP_BITS 8 +#define FLUID_INTERP_BITS_MASK 0xff000000 +#define FLUID_INTERP_BITS_SHIFT 24 + + +#define FLUID_FRACT_MAX ((double)4294967296.0) + +/* fluid_phase_t +* Purpose: +* Playing pointer for voice playback +* +* When a sample is played back at a different pitch, the playing pointer in the +* source sample will not advance exactly one sample per output sample. +* This playing pointer is implemented using fluid_phase_t. +* It is a 64 bit number. The higher 32 bits contain the 'index' (number of +* the current sample), the lower 32 bits the fractional part. +*/ +typedef uint64_t fluid_phase_t; + +/* Purpose: + * Set a to b. + * a: fluid_phase_t + * b: fluid_phase_t + */ +#define fluid_phase_set(a,b) a=b; + +#define fluid_phase_set_int(a, b) ((a) = ((uint64_t)(b)) << 32) + +/* Purpose: + * Sets the phase a to a phase increment given in b. + * For example, assume b is 0.9. After setting a to it, adding a to + * the playing pointer will advance it by 0.9 samples. */ +#define fluid_phase_set_float(a, b) \ + (a) = (((uint64_t)(b)) << 32) \ + | (uint32_t) (((double)(b) - (int)(b)) * (double)FLUID_FRACT_MAX) + +/* create a fluid_phase_t from an index and a fraction value */ +#define fluid_phase_from_index_fract(index, fract) \ + ((((uint64_t)(index)) << 32) + (fract)) + +/* Purpose: + * Return the index and the fractional part, respectively. */ +#define fluid_phase_index(_x) \ + ((unsigned int)((_x) >> 32)) +#define fluid_phase_fract(_x) \ + ((uint32_t)((_x) & 0xFFFFFFFF)) + +/* Get the phase index with fractional rounding */ +#define fluid_phase_index_round(_x) \ + ((unsigned int)(((_x) + 0x80000000) >> 32)) + + +/* Purpose: + * Takes the fractional part of the argument phase and + * calculates the corresponding position in the interpolation table. + * The fractional position of the playing pointer is calculated with a quite high + * resolution (32 bits). It would be unpractical to keep a set of interpolation + * coefficients for each possible fractional part... + */ +#define fluid_phase_fract_to_tablerow(_x) \ + ((unsigned int)(fluid_phase_fract(_x) & FLUID_INTERP_BITS_MASK) >> FLUID_INTERP_BITS_SHIFT) + +#define fluid_phase_double(_x) \ + ((double)(fluid_phase_index(_x)) + ((double)fluid_phase_fract(_x) / FLUID_FRACT_MAX)) + +/* Purpose: + * Advance a by a step of b (both are fluid_phase_t). + */ +#define fluid_phase_incr(a, b) a += b + +/* Purpose: + * Subtract b from a (both are fluid_phase_t). + */ +#define fluid_phase_decr(a, b) a -= b + +/* Purpose: + * Subtract b samples from a. + */ +#define fluid_phase_sub_int(a, b) ((a) -= (uint64_t)(b) << 32) + +/* Purpose: + * Creates the expression a.index++. */ +#define fluid_phase_index_plusplus(a) (((a) += 0x100000000LL) + +#endif /* _FLUID_PHASE_H */ diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_rev.c b/thirdparty/fluidsynth/src/rvoice/fluid_rev.c new file mode 100644 index 000000000..518989477 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_rev.c @@ -0,0 +1,1523 @@ +/****************************************************************************** + * FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + * + * FDN REVERB + * + * Freeverb used by fluidsynth (v.1.1.10 and previous) is based on + * Schroeder-Moorer reverberator: + * https://ccrma.stanford.edu/~jos/pasp/Freeverb.html + * + * This FDN reverberation is based on jot FDN reverberator. + * https://ccrma.stanford.edu/~jos/Reverb/FDN_Late_Reverberation.html + * Like Freeverb it is a late reverb which is convenient for Fluidsynth. + * + * + * .-------------------. + * .-----------------| | + * | - | Feedback | + * | .--------------| Matrix | + * | | |___________________| + * | | /|\ /|\ + * \|/ | .---------. .-------. | - | .------. + * .->+ ---->| Delay 0 |-|L.P.F 0|--*-------->| |-> out + * .---------. | | |_________| |_______| | | | left + * |Tone | | | - - | |Stereo| + * In ->|corrector|--* | - - | | unit | + * mono |_________| | \|/ .---------. .-------. | | |-> out + * ---->+ ->| Delay 7 |-|L.P.F 7|--------*-->| | right + * |_________| |_______| |______| + * /|\ /|\ /|\ /|\ + * | | | | + * roomsize --/ | width --/ | + * damp ------/ level ------/ + * + * It takes a monophonic input and produces a stereo output. + * + * The parameters are the same than for Freeverb. + * Also the default response of these parameters are the same than for Freeverb: + * - roomsize (0 to 1): control the reverb time from 0.7 to 12.5 s. + * This reverberation time is ofen called T60DC. + * + * - damp (0 to 1): controls the reverb time frequency dependency. + * This controls the reverb time for the frequency sample rate/2 + * + * When 0, the reverb time for high frequencies is the same as + * for DC frequency. + * When > 0, high frequencies have less reverb time than lower frequencies. + * + * - width (0 to 100): controls the left/right output separation. + * When 0, there are no separation and the signal on left and right. + * output is the same. This sounds like a monophonic signal. + * When 100, the separation between left and right is maximum. + * + * - level (0 to 1), controls the output level reverberation. + * + * This FDN reverb produces a better quality reverberation tail than Freeverb with + * far less ringing by using modulated delay lines that help to cancel + * the building of a lot of resonances in the reverberation tail even when + * using only 8 delays lines (NBR_DELAYS = 8) (default). + * + * The frequency density (often called "modal density" is one property that + * contributes to sound quality. Although 8 lines give good result, using 12 delays + * lines brings the overall frequency density quality a bit higher. + * This quality augmentation is noticeable particularly when using long reverb time + * (roomsize = 1) on solo instrument with long release time. Of course the cpu load + * augmentation is +50% relatively to 8 lines. + * + * As a general rule the reverberation tail quality is easier to perceive by ear + * when using: + * - percussive instruments (i.e piano and others). + * - long reverb time (roomsize = 1). + * - no damping (damp = 0). + * - Using headphone. Avoid using loud speaker, you will be quickly misguided by the + * natural reverberation of the room in which you are. + * + * The cpu load for 8 lines is a bit lower than for freeverb (- 3%), + * but higher for 12 lines (+ 41%). + * + * + * The memory consumption is less than for freeverb + * (see the results table below). + * + * Two macros are usable at compiler time: + * - NBR_DELAYS: number of delay lines. 8 (default) or 12. + * - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response of + * roomsize parameter. + * When this macro is not defined (the default), roomsize has the same + * response that Freeverb, that is: + * - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s). + * + * When this macro is defined, roomsize behaves linearly: + * - roomsize (0 to 1) controls reverb time linearly (0.7 to 12.5 s). + * This linear response is convenient when using GUI controls. + * + * -------------------------------------------------------------------------- + * Compare table: + * Note: the cpu load in % are relative each to other. These values are + * given by the fluidsynth profile commands. + * -------------------------------------------------------------------------- + * reverb | NBR_DELAYS | Performances | memory size | quality + * | | (cpu_load: %) | (bytes)(see note) | + * ========================================================================== + * freeverb | 2 x 8 comb | 0.670 % | 204616 | ringing + * | 2 x 4 all-pass | | | + * ----------|--------------------------------------------------------------- + * FDN | 8 | 0.650 % | 112480 | far less + * modulated | |(feeverb - 3%) | (56% freeverb) | ringing + * |--------------------------------------------------------------- + * | 12 | 0.942 % | 168720 | best than + * | |(freeverb + 41%) | (82 %freeverb) | 8 lines + *--------------------------------------------------------------------------- + * + * Note: + * Values in this column is the memory consumption for sample rate <= 44100Hz. + * For sample rate > 44100Hz , multiply these values by (sample rate / 44100Hz). + * For example: for sample rate 96000Hz, the memory consumed is 244760 bytes + * + *---------------------------------------------------------------------------- + * 'Denormalise' method to avoid loss of performance. + * -------------------------------------------------- + * According to music-dsp thread 'Denormalise', Pentium processors + * have a hardware 'feature', that is of interest here, related to + * numeric underflow. We have a recursive filter. The output decays + * exponentially, if the input stops. So the numbers get smaller and + * smaller... At some point, they reach 'denormal' level. This will + * lead to drastic spikes in the CPU load. The effect was reproduced + * with the reverb - sometimes the average load over 10 s doubles!!. + * + * The 'undenormalise' macro fixes the problem: As soon as the number + * is close enough to denormal level, the macro forces the number to + * 0.0f. The original macro is: + * + * #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f + * + * This will zero out a number when it reaches the denormal level. + * Advantage: Maximum dynamic range Disadvantage: We'll have to check + * every sample, expensive. The alternative macro comes from a later + * mail from Jon Watte. It will zap a number before it reaches + * denormal level. Jon suggests to run it once per block instead of + * every sample. + */ + +/* Denormalising part II: + * + * Another method fixes the problem cheaper: Use a small DC-offset in + * the filter calculations. Now the signals converge not against 0, + * but against the offset. The constant offset is invisible from the + * outside world (i.e. it does not appear at the output. There is a + * very small turn-on transient response, which should not cause + * problems. + */ +#include "fluid_rev.h" +#include "fluid_sys.h" + +/*---------------------------------------------------------------------------- + Configuration macros at compiler time. + + 3 macros are usable at compiler time: + - NBR_DELAYs: number of delay lines. 8 (default) or 12. + - ROOMSIZE_RESPONSE_LINEAR: allows to choose an alternate response for + roomsize parameter. + - DENORMALISING enable denormalising handling. +-----------------------------------------------------------------------------*/ +//#define INFOS_PRINT /* allows message to be printed on the console. */ + +/* Number of delay lines (must be only 8 or 12) + 8 is the default. + 12 produces a better quality but is +50% cpu expensive. +*/ +#define NBR_DELAYS 8 /* default*/ + +/* response curve of parameter roomsize */ +/* + The default response is the same as Freeverb: + - roomsize (0 to 1) controls concave reverb time (0.7 to 12.5 s). + + when ROOMSIZE_RESPONSE_LINEAR is defined, the response is: + - roomsize (0 to 1) controls reverb time linearly (0.7 to 12.5 s). +*/ +//#define ROOMSIZE_RESPONSE_LINEAR + +/* DENORMALISING enable denormalising handling */ +#define DENORMALISING + +#ifdef DENORMALISING +#define DC_OFFSET 1e-8f +#else +#define DC_OFFSET 0.0f +#endif + +/*---------------------------------------------------------------------------- + Initial internal reverb settings (at reverb creation time) +-----------------------------------------------------------------------------*/ +/* SCALE_WET_WIDTH is a compensation weight factor to get an output + amplitude (wet) rather independent of the width setting. + 0: the output amplitude is fully dependent on the width setting. + >0: the output amplitude is less dependent on the width setting. + With a SCALE_WET_WIDTH of 0.2 the output amplitude is rather + independent of width setting (see fluid_revmodel_update()). + */ +#define SCALE_WET_WIDTH 0.2f + +/* It is best to inject the input signal less ofen. This contributes to obtain +a flatter response on comb filter. So the input gain is set to 0.1 rather 1.0. */ +#define FIXED_GAIN 0.1f /* input gain */ + +/* SCALE_WET is adjusted to 5.0 to get internal output level equivalent to freeverb */ +#define SCALE_WET 5.0f /* scale output gain */ + +/*---------------------------------------------------------------------------- + Internal FDN late reverb settings +-----------------------------------------------------------------------------*/ + +/*-- Reverberation time settings ---------------------------------- + MIN_DC_REV_TIME est defined egal to the minimum value of freeverb: + MAX_DC_REV_TIME est defined egal to the maximum value of freeverb: + T60DC is computed from gi and the longuest delay line in freeverb: L8 = 1617 + T60 = -3 * Li * T / log10(gi) + T60 = -3 * Li * / (log10(gi) * sr) + + - Li: length of comb filter delay line. + - sr: sample rate. + - gi: the feedback gain. + + The minimum value for freeverb correspond to gi = 0.7. + with Mi = 1617, sr at 44100 Hz, and gi = 0.7 => MIN_DC_REV_TIME = 0.7 s + + The maximum value for freeverb correspond to gi = 0.98. + with Mi = 1617, sr at 44100 Hz, and gi = 0.98 => MAX_DC_REV_TIME = 12.5 s +*/ + +#define MIN_DC_REV_TIME 0.7f /* minimum T60DC reverb time: seconds */ +#define MAX_DC_REV_TIME 12.5f /* maximumm T60DC time in seconds */ +#define RANGE_REV_TIME (MAX_DC_REV_TIME - MIN_DC_REV_TIME) + +/* macro to compute internal reverberation time versus roomsize parameter */ +#define GET_DC_REV_TIME(roomsize) (MIN_DC_REV_TIME + RANGE_REV_TIME * roomsize) + +/*-- Modulation related settings ----------------------------------*/ +/* For many instruments, the range for MOD_FREQ and MOD_DEPTH should be: + + MOD_DEPTH: [3..6] (in samples). + MOD_FREQ: [0.5 ..2.0] (in Hz). + + Values below the lower limits are often not sufficient to cancel unwanted + "ringing"(resonant frequency). + Values above upper limits augment the unwanted "chorus". + + With NBR_DELAYS to 8: + MOD_DEPTH must be >= 4 to cancel the unwanted "ringing".[4..6]. + With NBR_DELAYS to 12: + MOD_DEPTH to 3 is sufficient to cancel the unwanted "ringing".[3..6] +*/ +#define MOD_DEPTH 4 /* modulation depth (samples)*/ +#define MOD_RATE 50 /* modulation rate (samples)*/ +#define MOD_FREQ 1.0f /* modulation frequency (Hz) */ +/* + Number of samples to add to the desired length of a delay line. This + allow to take account of modulation interpolation. + 1 is sufficient with MOD_DEPTH equal to 4. +*/ +#define INTERP_SAMPLES_NBR 1 + +/* phase offset between modulators waveform */ +#define MOD_PHASE (360.0f/(float) NBR_DELAYS) + +#if (NBR_DELAYS == 8) + #define DELAY_L0 601 + #define DELAY_L1 691 + #define DELAY_L2 773 + #define DELAY_L3 839 + #define DELAY_L4 919 + #define DELAY_L5 997 + #define DELAY_L6 1061 + #define DELAY_L7 1129 +#elif (NBR_DELAYS == 12) + #define DELAY_L0 601 + #define DELAY_L1 691 + #define DELAY_L2 773 + #define DELAY_L3 839 + #define DELAY_L4 919 + #define DELAY_L5 997 + #define DELAY_L6 1061 + #define DELAY_L7 1093 + #define DELAY_L8 1129 + #define DELAY_L9 1151 + #define DELAY_L10 1171 + #define DELAY_L11 1187 +#endif + + +/*---------------------------------------------------------------------------*/ +/* The FDN late feed back matrix: A + T + A = P - 2 / N * u * u + N N N N + + N: the matrix dimension (i.e NBR_DELAYS). + P: permutation matrix. + u: is a colomn vector of 1. + +*/ +#define FDN_MATRIX_FACTOR (fluid_real_t)(-2.0 / NBR_DELAYS) + +/*---------------------------------------------------------------------------- + Internal FDN late structures and static functions +-----------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------------------------- + Delay absorbent low pass filter +-----------------------------------------------------------------------------*/ +typedef struct +{ + fluid_real_t buffer; + fluid_real_t b0, a1; /* filter coefficients */ +} fdn_delay_lpf; + +/*----------------------------------------------------------------------------- + Sets coefficients for delay absorbent low pass filter. + @param lpf pointer on low pass filter structure. + @param b0,a1 coefficients. +-----------------------------------------------------------------------------*/ +static void set_fdn_delay_lpf(fdn_delay_lpf *lpf, + fluid_real_t b0, fluid_real_t a1) +{ + lpf->b0 = b0; + lpf->a1 = a1; +} + +/*----------------------------------------------------------------------------- + Process delay absorbent low pass filter. + @param mod_delay modulated delay line. + @param in, input sample. + @param out output sample. +-----------------------------------------------------------------------------*/ +/* process low pass damping filter (input, output, delay) */ +#define process_damping_filter(in,out,mod_delay) \ +{\ + out = in * mod_delay->dl.damping.b0 - mod_delay->dl.damping.buffer * \ + mod_delay->dl.damping.a1;\ + mod_delay->dl.damping.buffer = out;\ +}\ + + +/*----------------------------------------------------------------------------- + Delay line : + The delay line is composed of the line plus an absorbent low pass filter + to get frequency dependent reverb time. +-----------------------------------------------------------------------------*/ +typedef struct +{ + fluid_real_t *line; /* buffer line */ + int size; /* effective internal size (in samples) */ + /*-------------*/ + int line_in; /* line in position */ + int line_out; /* line out position */ + /*-------------*/ + fdn_delay_lpf damping; /* damping low pass filter */ +} delay_line; + + +/*----------------------------------------------------------------------------- + Clears a delay line to DC_OFFSET float value. + @param dl pointer on delay line structure +-----------------------------------------------------------------------------*/ +static void clear_delay_line(delay_line *dl) +{ + int i; + + for(i = 0; i < dl->size; i++) + { + dl->line[i] = DC_OFFSET; + } +} + +/*----------------------------------------------------------------------------- + Push a sample val into the delay line +-----------------------------------------------------------------------------*/ +#define push_in_delay_line(dl, val) \ +{\ + dl->line[dl->line_in] = val;\ + /* Incrementation and circular motion if necessary */\ + if(++dl->line_in >= dl->size) dl->line_in -= dl->size;\ +}\ + +/*----------------------------------------------------------------------------- + Modulator for modulated delay line +-----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + Sinusoidal modulator +-----------------------------------------------------------------------------*/ +/* modulator are integrated in modulated delay line */ +typedef struct +{ + fluid_real_t a1; /* Coefficient: a1 = 2 * cos(w) */ + fluid_real_t buffer1; /* buffer1 */ + fluid_real_t buffer2; /* buffer2 */ + fluid_real_t reset_buffer2;/* reset value of buffer2 */ +} sinus_modulator; + +/*----------------------------------------------------------------------------- + Sets the frequency of sinus oscillator. + + @param mod pointer on modulator structure. + @param freq frequency of the oscillator in Hz. + @param sample_rate sample rate on audio output in Hz. + @param phase initial phase of the oscillator in degree (0 to 360). +-----------------------------------------------------------------------------*/ +static void set_mod_frequency(sinus_modulator *mod, + float freq, float sample_rate, float phase) +{ + fluid_real_t w = 2 * FLUID_M_PI * freq / sample_rate; /* initial angle */ + fluid_real_t a; + + mod->a1 = 2 * FLUID_COS(w); + + a = (2 * FLUID_M_PI / 360) * phase; + + mod->buffer2 = FLUID_SIN(a - w); /* y(n-1) = sin(-intial angle) */ + mod->buffer1 = FLUID_SIN(a); /* y(n) = sin(initial phase) */ + mod->reset_buffer2 = FLUID_SIN(FLUID_M_PI / 2 - w); /* reset value for PI/2 */ +} + +/*----------------------------------------------------------------------------- + Gets current value of sinus modulator: + y(n) = a1 . y(n-1) - y(n-2) + out = a1 . buffer1 - buffer2 + + @param pointer on modulator structure. + @return current value of the modulator sine wave. +-----------------------------------------------------------------------------*/ +static FLUID_INLINE fluid_real_t get_mod_sinus(sinus_modulator *mod) +{ + fluid_real_t out; + out = mod->a1 * mod->buffer1 - mod->buffer2; + mod->buffer2 = mod->buffer1; + + if(out >= 1.0f) /* reset in case of instability near PI/2 */ + { + out = 1.0f; /* forces output to the right value */ + mod->buffer2 = mod->reset_buffer2; + } + + if(out <= -1.0f) /* reset in case of instability near -PI/2 */ + { + out = -1.0f; /* forces output to the right value */ + mod->buffer2 = - mod->reset_buffer2; + } + + mod->buffer1 = out; + return out; +} + +/*----------------------------------------------------------------------------- + Modulated delay line. The line is composed of: + - the delay line with its damping low pass filter. + - the sinusoidal modulator. + - center output position modulated by the modulator. + - variable rate control of center output position. + - first order All-Pass interpolator. +-----------------------------------------------------------------------------*/ +typedef struct +{ + /* delay line with damping low pass filter member */ + delay_line dl; /* delayed line */ + /*---------------------------*/ + /* Sinusoidal modulator member */ + sinus_modulator mod; /* sinus modulator */ + /*-------------------------*/ + /* center output position members */ + fluid_real_t center_pos_mod; /* center output position modulated by modulator */ + int mod_depth; /* modulation depth (in samples) */ + /*-------------------------*/ + /* variable rate control of center output position */ + int index_rate; /* index rate to know when to update center_pos_mod */ + int mod_rate; /* rate at which center_pos_mod is updated */ + /*-------------------------*/ + /* first order All-Pass interpolator members */ + fluid_real_t frac_pos_mod; /* fractional position part between samples) */ + /* previous value used when interpolating using fractional */ + fluid_real_t buffer; +} mod_delay_line; + +/*----------------------------------------------------------------------------- + Return norminal delay length + + @param mdl, pointer on modulated delay line. +-----------------------------------------------------------------------------*/ +static int get_mod_delay_line_length(mod_delay_line *mdl) +{ + return (mdl->dl.size - mdl->mod_depth - INTERP_SAMPLES_NBR); +} + +/*----------------------------------------------------------------------------- + Reads the sample value out of the modulated delay line. + @param mdl, pointer on modulated delay line. + @return the sample value. +-----------------------------------------------------------------------------*/ +static FLUID_INLINE fluid_real_t get_mod_delay(mod_delay_line *mdl) +{ + fluid_real_t out_index; /* new modulated index position */ + int int_out_index; /* integer part of out_index */ + fluid_real_t out; /* value to return */ + + /* Checks if the modulator must be updated (every mod_rate samples). */ + /* Important: center_pos_mod must be used immediately for the + first sample. So, mdl->index_rate must be initialized + to mdl->mod_rate (set_mod_delay_line()) */ + + if(++mdl->index_rate >= mdl->mod_rate) + { + mdl->index_rate = 0; + + /* out_index = center position (center_pos_mod) + sinus waweform */ + out_index = mdl->center_pos_mod + + get_mod_sinus(&mdl->mod) * mdl->mod_depth; + + /* extracts integer part in int_out_index */ + if(out_index >= 0.0f) + { + int_out_index = (int)out_index; /* current integer part */ + + /* forces read index (line_out) with integer modulation value */ + /* Boundary check and circular motion as needed */ + if((mdl->dl.line_out = int_out_index) >= mdl->dl.size) + { + mdl->dl.line_out -= mdl->dl.size; + } + } + else /* negative */ + { + int_out_index = (int)(out_index - 1); /* previous integer part */ + /* forces read index (line_out) with integer modulation value */ + /* circular motion as needed */ + mdl->dl.line_out = int_out_index + mdl->dl.size; + } + + /* extracts fractionnal part. (it will be used when interpolating + between line_out and line_out +1) and memorize it. + Memorizing is necessary for modulation rate above 1 */ + mdl->frac_pos_mod = out_index - int_out_index; + + /* updates center position (center_pos_mod) to the next position + specified by modulation rate */ + if((mdl->center_pos_mod += mdl->mod_rate) >= mdl->dl.size) + { + mdl->center_pos_mod -= mdl->dl.size; + } + } + + /* First order all-pass interpolation ----------------------------------*/ + /* https://ccrma.stanford.edu/~jos/pasp/First_Order_Allpass_Interpolation.html */ + /* begins interpolation: read current sample */ + out = mdl->dl.line[mdl->dl.line_out]; + + /* updates line_out to the next sample. + Boundary check and circular motion as needed */ + if(++mdl->dl.line_out >= mdl->dl.size) + { + mdl->dl.line_out -= mdl->dl.size; + } + + /* Fractional interpolation between next sample (at next position) and + previous output added to current sample. + */ + out += mdl->frac_pos_mod * (mdl->dl.line[mdl->dl.line_out] - mdl->buffer); + mdl->buffer = out; /* memorizes current output */ + return out; +} + +/*----------------------------------------------------------------------------- + Late structure +-----------------------------------------------------------------------------*/ +struct _fluid_late +{ + fluid_real_t samplerate; /* sample rate */ + fluid_real_t sample_rate_max; /* sample rate maximum */ + /*----- High pass tone corrector -------------------------------------*/ + fluid_real_t tone_buffer; + fluid_real_t b1, b2; + /*----- Modulated delay lines lines ----------------------------------*/ + mod_delay_line mod_delay_lines[NBR_DELAYS]; + /*-----------------------------------------------------------------------*/ + /* Output coefficients for separate Left and right stereo outputs */ + fluid_real_t out_left_gain[NBR_DELAYS]; /* Left delay lines' output gains */ + fluid_real_t out_right_gain[NBR_DELAYS];/* Right delay lines' output gains*/ +}; + +typedef struct _fluid_late fluid_late; +/*----------------------------------------------------------------------------- + fluidsynth reverb structure +-----------------------------------------------------------------------------*/ +struct _fluid_revmodel_t +{ + /* reverb parameters */ + fluid_real_t roomsize; /* acting on reverb time */ + fluid_real_t damp; /* acting on frequency dependent reverb time */ + fluid_real_t level, wet1, wet2; /* output level */ + fluid_real_t width; /* width stereo separation */ + + /* fdn reverberation structure */ + fluid_late late; +}; + +/*----------------------------------------------------------------------------- + Updates Reverb time and absorbent filters coefficients from parameters: + + @param late pointer on late structure. + @param roomsize (0 to 1): acting on reverb time. + @param damping (0 to 1): acting on absorbent damping filter. + + Design formulas: + https://ccrma.stanford.edu/~jos/Reverb/First_Order_Delay_Filter_Design.html + https://ccrma.stanford.edu/~jos/Reverb/Tonal_Correction_Filter.html +-----------------------------------------------------------------------------*/ +static void update_rev_time_damping(fluid_late *late, + fluid_real_t roomsize, fluid_real_t damp) +{ + int i; + fluid_real_t sample_period = 1 / late->samplerate; /* Sampling period */ + int delay_length; /* delay length */ + fluid_real_t dc_rev_time; /* Reverb time at 0 Hz (in seconds) */ + + fluid_real_t alpha, alpha2; + + /*-------------------------------------------- + Computes dc_rev_time and alpha + ----------------------------------------------*/ + { + fluid_real_t gi_tmp, ai_tmp; +#ifdef ROOMSIZE_RESPONSE_LINEAR + /* roomsize parameter behave linearly: + * - roomsize (0 to 1) controls reverb time linearly (0.7 to 10 s). + * This linear response is convenient when using GUI controls. + */ + /*----------------------------------------- + Computes dc_rev_time + ------------------------------------------*/ + dc_rev_time = GET_DC_REV_TIME(roomsize); + delay_length = get_mod_delay_line_length(&late->mod_delay_lines[NBR_DELAYS - 1]); + /* computes gi_tmp from dc_rev_time using relation E2 */ + gi_tmp = FLUID_POW(10, -3 * delay_length * + sample_period / dc_rev_time); /* E2 */ +#else + /* roomsize parameters have the same response that Freeverb, that is: + * - roomsize (0 to 1) controls concave reverb time (0.7 to 10 s). + */ + { + /*----------------------------------------- + Computes dc_rev_time + ------------------------------------------*/ + fluid_real_t gi_min, gi_max; + + /* values gi_min et gi_max are computed using E2 for the line with + maximum delay */ + delay_length = get_mod_delay_line_length(&late->mod_delay_lines[NBR_DELAYS - 1]); + gi_max = FLUID_POW(10, (-3 * delay_length / MAX_DC_REV_TIME) * + sample_period); /* E2 */ + gi_min = FLUID_POW(10, (-3 * delay_length / MIN_DC_REV_TIME) * + sample_period); /* E2 */ + /* gi = f(roomsize, gi_max, gi_min) */ + gi_tmp = gi_min + roomsize * (gi_max - gi_min); + /* Computes T60DC from gi using inverse of relation E2.*/ + dc_rev_time = -3 * FLUID_M_LN10 * delay_length * sample_period / FLUID_LOGF(gi_tmp); + } +#endif /* ROOMSIZE_RESPONSE_LINEAR */ + /*-------------------------------------------- + Computes alpha + ----------------------------------------------*/ + /* Computes alpha from damp,ai_tmp,gi_tmp using relation R */ + /* - damp (0 to 1) controls concave reverb time for fs/2 frequency (T60DC to 0) */ + ai_tmp = 1.0f * damp; + + /* Preserve the square of R */ + alpha2 = 1.f / (1.f - ai_tmp / ((20.f / 80.f) * FLUID_LOGF(gi_tmp))); + + alpha = FLUID_SQRT(alpha2); /* R */ + } + + /* updates tone corrector coefficients b1,b2 from alpha */ + { + /* + Beta = (1 - alpha) / (1 + alpha) + b1 = 1/(1-beta) + b2 = beta * b1 + */ + fluid_real_t beta = (1 - alpha) / (1 + alpha); + late->b1 = 1 / (1 - beta); + late->b2 = beta * late->b1; + late->tone_buffer = 0.0f; + } + + /* updates damping coefficients of all lines (gi , ai) from dc_rev_time, alpha */ + for(i = 0; i < NBR_DELAYS; i++) + { + fluid_real_t gi, ai; + + /* delay length */ + delay_length = get_mod_delay_line_length(&late->mod_delay_lines[i]); + + /* iir low pass filter gain */ + gi = FLUID_POW(10, -3 * delay_length * sample_period / dc_rev_time); + + /* iir low pass filter feedback gain */ + ai = (20.f / 80.f) * FLUID_LOGF(gi) * (1.f - 1.f / alpha2); + + /* b0 = gi * (1 - ai), a1 = - ai */ + set_fdn_delay_lpf(&late->mod_delay_lines[i].dl.damping, + gi * (1.f - ai), -ai); + } +} + +/*----------------------------------------------------------------------------- + Updates stereo coefficients + @param late pointer on late structure + @param wet level integrated in stereo coefficients. +-----------------------------------------------------------------------------*/ +static void update_stereo_coefficient(fluid_late *late, fluid_real_t wet1) +{ + int i; + fluid_real_t wet; + + for(i = 0; i < NBR_DELAYS; i++) + { + /* delay lines output gains vectors Left and Right + + L R + 0 | 1 1| + 1 |-1 1| + 2 | 1 -1| + 3 |-1 -1| + + 4 | 1 1| + 5 |-1 1| + stereo gain = 6 | 1 -1| + 7 |-1 -1| + + 8 | 1 1| + 9 |-1 1| + 10| 1 -1| + 11|-1 -1| + */ + + /* for left line: 00, ,02, ,04, ,06, ,08, ,10, ,12,... left_gain = +1 */ + /* for left line: ,01, ,03, ,05, ,07, ,09, ,11,... left_gain = -1 */ + wet = wet1; + if(i & 1) + { + wet = -wet1; + } + late->out_left_gain[i] = wet; + + /* for right line: 00,01, ,04,05, ,08,09, ,12,13 right_gain = +1 */ + /* for right line: ,02 ,03, ,06,07, ,10,11,... right_gain = -1 */ + wet = wet1; + if(i & 2) + { + wet = -wet1; + } + late->out_right_gain[i] = wet; + } +} + +/*----------------------------------------------------------------------------- + fluid_late destructor. + @param late pointer on late structure. +-----------------------------------------------------------------------------*/ +static void delete_fluid_rev_late(fluid_late *late) +{ + int i; + fluid_return_if_fail(late != NULL); + + /* free the delay lines */ + for(i = 0; i < NBR_DELAYS; i++) + { + FLUID_FREE(late->mod_delay_lines[i].dl.line); + } +} + + +/* Nominal delay lines length table (in samples) */ +static const int nom_delay_length[NBR_DELAYS] = +{ + DELAY_L0, DELAY_L1, DELAY_L2, DELAY_L3, + DELAY_L4, DELAY_L5, DELAY_L6, DELAY_L7, +#if (NBR_DELAYS == 12) + DELAY_L8, DELAY_L9, DELAY_L10, DELAY_L11 +#endif +}; + +/* + 1)"modal density" is one property that contributes to the quality of the reverb tail. + The more is the modal density, the less are unwanted resonant frequencies + build during the decay time: modal density = total delay / sample rate. + + Delay line's length given by static table delay_length[] are nominal + to get minimum modal density of 0.15 at sample rate 44100Hz. + Here we set length_factor to 2 to multiply this nominal modal + density by 2. This leads to a default modal density of 0.15 * 2 = 0.3 for + sample rate <= 44100. + + For sample rate > 44100, length_factor is multiplied by + sample_rate / 44100. This ensures that the default modal density keeps inchanged. + (Without this compensation, the default modal density would be diminished for + new sample rate change above 44100Hz). + + 2)Modulated delay line contributes to diminish resonnant frequencies (often called "ringing"). + Modulation depth (mod_depth) is set to nominal value of MOD_DEPTH at sample rate 44100Hz. + For sample rate > 44100, mod_depth is multiplied by sample_rate / 44100. This ensures + that the effect of modulated delay line remains inchanged. +*/ +static void compensate_from_sample_rate(fluid_real_t sample_rate, + fluid_real_t *mod_depth, + fluid_real_t *length_factor) +{ + *mod_depth = MOD_DEPTH; + *length_factor = 2.0f; + if(sample_rate > 44100.0f) + { + fluid_real_t sample_rate_factor = sample_rate/44100.0f; + *length_factor *= sample_rate_factor; + *mod_depth *= sample_rate_factor; + } +} + +/*----------------------------------------------------------------------------- + Creates all modulated lines. + @param late, pointer on the fnd late reverb to initialize. + @param sample_rate_max, the maximum audio sample rate expected. + @return FLUID_OK if success, FLUID_FAILED otherwise. +-----------------------------------------------------------------------------*/ +static int create_mod_delay_lines(fluid_late *late, + fluid_real_t sample_rate_max) +{ + int i; + + fluid_real_t mod_depth, length_factor; + + /* compute mod_depth, length factor */ + compensate_from_sample_rate(sample_rate_max, &mod_depth, &length_factor); + + late->sample_rate_max = sample_rate_max; + +#ifdef INFOS_PRINT // allows message to be printed on the console. + printf("length_factor:%f, mod_depth:%f\n", length_factor, mod_depth); + /* Print: modal density and total memory bytes */ + { + int i; + int total_delay = 0; /* total delay in samples */ + for (i = 0; i < NBR_DELAYS; i++) + { + int length = (length_factor * nom_delay_length[i]) + + mod_depth + INTERP_SAMPLES_NBR; + total_delay += length; + } + + /* modal density and total memory bytes */ + printf("modal density:%f, total delay:%d, total memory:%d bytes\n", + total_delay / sample_rate_max ,total_delay , + total_delay * sizeof(fluid_real_t)); + } +#endif + + for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */ + { + int delay_length = nom_delay_length[i] * length_factor; + mod_delay_line *mdl = &late->mod_delay_lines[i]; + + /*-------------------------------------------------------------------*/ + /* checks parameter */ + if(delay_length < 1) + { + return FLUID_FAILED; + } + + /* limits mod_depth to the requested delay length */ + if(mod_depth >= delay_length) + { + FLUID_LOG(FLUID_INFO, + "fdn reverb: modulation depth has been limited"); + mod_depth = delay_length - 1; + } + + /*--------------------------------------------------------------------- + allocates delay lines + */ + + /* real size of the line in use (in samples): + size = INTERP_SAMPLES_NBR + mod_depth + delay_length */ + mdl->dl.size = delay_length + mod_depth + INTERP_SAMPLES_NBR; + mdl->dl.line = FLUID_ARRAY(fluid_real_t, mdl->dl.size); + + if(! mdl->dl.line) + { + return FLUID_FAILED; + } + } + return FLUID_OK; +} + +/*----------------------------------------------------------------------------- + Initialize all modulated lines. + @param late, pointer on the fnd late reverb to initialize. + @param sample_rate, the audio sample rate. + @return FLUID_OK if success, FLUID_FAILED otherwise. +-----------------------------------------------------------------------------*/ +static void initialize_mod_delay_lines(fluid_late *late, fluid_real_t sample_rate) +{ + int i; + fluid_real_t mod_depth, length_factor; + + /* update delay line parameter dependent of sample rate */ + late->samplerate = sample_rate; + + /* compute mod_depth, length factor */ + compensate_from_sample_rate(sample_rate, &mod_depth, &length_factor); + + for(i = 0; i < NBR_DELAYS; i++) /* for each delay line */ + { + mod_delay_line *mdl = &late->mod_delay_lines[i]; + int delay_length = nom_delay_length[i] * length_factor; + + /* limits mod_depth to the requested delay length */ + if(mod_depth >= delay_length) + { + mod_depth = delay_length - 1; + } + + mdl->mod_depth = mod_depth; + + clear_delay_line(&mdl->dl); /* clears the buffer */ + + /* Initializes line_in to the start of the buffer */ + mdl->dl.line_in = 0; + + /* Initializes line_out index INTERP_SAMPLES_NBR samples after + line_in so that the delay between line_out and line_in is: + mod_depth + delay_length + */ + mdl->dl.line_out = mdl->dl.line_in + INTERP_SAMPLES_NBR; + + /* Damping low pass filter ------------------------------------------*/ + mdl->dl.damping.buffer = 0; + + /*--------------------------------------------------------------------- + Initializes modulation members: + - modulated center position: center_pos_mod + - modulation rate (the speed at which center_pos_mod is modulated: mod_rate + - index rate to know when to update center_pos_mod:index_rate + - interpolator member: buffer, frac_pos_mod + ---------------------------------------------------------------------*/ + /* Initializes the modulated center position (center_pos_mod) so that: + - the delay between line_out and center_pos_mod is mod_depth. + - the delay between center_pos_mod and line_in is delay_length. + */ + mdl->center_pos_mod = (fluid_real_t) INTERP_SAMPLES_NBR + mod_depth; + + /* Sets the modulation rate. This rate defines how often + the center position (center_pos_mod ) is modulated . + The value is expressed in samples. The default value is 1 that means that + center_pos_mod is updated at every sample. + For example with a value of 2, the center position position will be + updated only one time every 2 samples only. + */ + if(MOD_RATE < 1 || MOD_RATE > mdl->dl.size) + { + FLUID_LOG(FLUID_INFO, "fdn reverb: modulation rate is out of range"); + mdl->mod_rate = 1; /* default modulation rate: every one sample */ + } + else + { + mdl->mod_rate = MOD_RATE; + } + + /* index rate to control when to update center_pos_mod. + Important: must be set to get center_pos_mod immediately used for + the reading of first sample (see get_mod_delay()) + */ + mdl->index_rate = mdl->mod_rate; + + /* initializes first order All-Pass interpolator members */ + mdl->buffer = 0; /* previous delay sample value */ + mdl->frac_pos_mod = 0; /* frac. position (between consecutives sample) */ + + + /* Sets local Modulators parameters: frequency and phase. + Each modulateur are shifted of MOD_PHASE degree + */ + set_mod_frequency(&mdl->mod, + MOD_FREQ * MOD_RATE, + sample_rate, + (float)(MOD_PHASE * i)); + } +} + +/* + Clears the delay lines. + + @param rev pointer on the reverb. +*/ +static void +fluid_revmodel_init(fluid_revmodel_t *rev) +{ + int i; + + /* clears all the delay lines */ + for(i = 0; i < NBR_DELAYS; i ++) + { + clear_delay_line(&rev->late.mod_delay_lines[i].dl); + } +} + + +/* + updates internal parameters. + + @param rev pointer on the reverb. +*/ +static void +fluid_revmodel_update(fluid_revmodel_t *rev) +{ + /* Recalculate internal values after parameters change */ + + /* The stereo amplitude equation (wet1 and wet2 below) have a + tendency to produce high amplitude with high width values ( 1 < width < 100). + This results in an unwanted noisy output clipped by the audio card. + To avoid this dependency, we divide by (1 + rev->width * SCALE_WET_WIDTH) + Actually, with a SCALE_WET_WIDTH of 0.2, (regardless of level setting), + the output amplitude (wet) seems rather independent of width setting */ + fluid_real_t wet = (rev->level * SCALE_WET) / + (1.0f + rev->width * SCALE_WET_WIDTH); + + /* wet1 and wet2 are used by the stereo effect controlled by the width setting + for producing a stereo ouptput from a monophonic reverb signal. + Please see the note above about a side effect tendency */ + + rev->wet1 = wet * (rev->width / 2.0f + 0.5f); + rev->wet2 = wet * ((1.0f - rev->width) / 2.0f); + + /* integrates wet1 in stereo coefficient (this will save one multiply) */ + update_stereo_coefficient(&rev->late, rev->wet1); + + if(rev->wet1 > 0.0f) + { + rev->wet2 /= rev->wet1; + } + + /* Reverberation time and damping */ + update_rev_time_damping(&rev->late, rev->roomsize, rev->damp); +} + +/*---------------------------------------------------------------------------- + Reverb API +-----------------------------------------------------------------------------*/ +/* +* Creates a reverb. Once created the reverb have no parameters set, so +* fluid_revmodel_set() must be called at least one time after calling +* new_fluid_revmodel(). +* +* @param sample_rate_max maximum sample rate expected in Hz. +* +* @param sample_rate actual sample rate needed in Hz. +* @return pointer on the new reverb or NULL if memory error. +* Reverb API. +*/ +fluid_revmodel_t * +new_fluid_revmodel(fluid_real_t sample_rate_max, fluid_real_t sample_rate) +{ + fluid_revmodel_t *rev; + + if(sample_rate <= 0) + { + return NULL; + } + + rev = FLUID_NEW(fluid_revmodel_t); + + if(rev == NULL) + { + return NULL; + } + + FLUID_MEMSET(&rev->late, 0, sizeof(fluid_late)); + + /*-------------------------------------------------------------------------- + Create fdn late reverb. + */ + + /* update minimum value for sample_rate_max */ + if(sample_rate > sample_rate_max) + { + sample_rate_max = sample_rate; + } + + /*-------------------------------------------------------------------------- + Allocate the modulated delay lines + */ + if(create_mod_delay_lines(&rev->late, sample_rate_max) == FLUID_FAILED) + { + delete_fluid_revmodel(rev); + return NULL; + } + + /*-------------------------------------------------------------------------- + Initialize the fdn reverb + */ + /* Initialize all modulated lines. */ + initialize_mod_delay_lines(&rev->late, sample_rate); + + return rev; +} + +/* +* free the reverb. +* Note that while the reverb is used by calling any fluid_revmodel_processXXX() +* function, calling delete_fluid_revmodel() isn't multi task safe because +* delay line are freed. To deal properly with this issue follow the steps: +* +* 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX(). +* reverb functions. +* 2) Delete the reverb by calling delete_fluid_revmodel(). +* +* @param rev pointer on reverb to free. +* Reverb API. +*/ +void +delete_fluid_revmodel(fluid_revmodel_t *rev) +{ + fluid_return_if_fail(rev != NULL); + delete_fluid_rev_late(&rev->late); + FLUID_FREE(rev); +} + +/* +* Sets one or more reverb parameters. Note this must be called at least one +* time after calling new_fluid_revmodel() and before any call to +* fluid_revmodel_processXXX() and fluid_revmodel_samplerate_change(). +* +* Note that while the reverb is used by calling any fluid_revmodel_processXXX() +* function, calling fluid_revmodel_set() could produce audible clics. +* If this is a problem, optionally call fluid_revmodel_reset() before calling +* fluid_revmodel_set(). +* +* @param rev Reverb instance. +* @param set One or more flags from #fluid_revmodel_set_t indicating what +* parameters to set (#FLUID_REVMODEL_SET_ALL to set all parameters). +* @param roomsize Reverb room size. +* @param damping Reverb damping. +* @param width Reverb width. +* @param level Reverb level. +* +* Reverb API. +*/ +void +fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize, + fluid_real_t damping, fluid_real_t width, fluid_real_t level) +{ + fluid_return_if_fail(rev != NULL); + + /*-----------------------------------*/ + if(set & FLUID_REVMODEL_SET_ROOMSIZE) + { + fluid_clip(roomsize, 0.0f, 1.0f); + rev->roomsize = roomsize; + } + + /*-----------------------------------*/ + if(set & FLUID_REVMODEL_SET_DAMPING) + { + fluid_clip(damping, 0.0f, 1.0f); + rev->damp = damping; + } + + /*-----------------------------------*/ + if(set & FLUID_REVMODEL_SET_WIDTH) + { + rev->width = width; + } + + /*-----------------------------------*/ + if(set & FLUID_REVMODEL_SET_LEVEL) + { + fluid_clip(level, 0.0f, 1.0f); + rev->level = level; + } + + /* updates internal parameters */ + fluid_revmodel_update(rev); +} + +/* +* Applies a sample rate change on the reverb. +* fluid_revmodel_set() must be called at least one time before calling +* this function. +* +* Note that while the reverb is used by calling any fluid_revmodel_processXXX() +* function, calling fluid_revmodel_samplerate_change() isn't multi task safe. +* To deal properly with this issue follow the steps: +* 1) Stop reverb processing (i.e disable calling of any fluid_revmodel_processXXX(). +* reverb functions. +* Optionally, call fluid_revmodel_reset() to damp the reverb. +* 2) Change sample rate by calling fluid_revmodel_samplerate_change(). +* 3) Restart reverb processing (i.e enabling calling of any fluid_revmodel_processXXX() +* reverb functions. +* +* Another solution is to substitute step (2): +* 2.1) delete the reverb by calling delete_fluid_revmodel(). +* 2.2) create the reverb by calling new_fluid_revmodel(). +* +* The best solution would be that this function be called only by the same task +* calling fluid_revmodel_processXXX(). +* +* @param rev the reverb. +* @param sample_rate new sample rate value. Must be <= sample_rate_max +* @return FLUID_OK if success, FLUID_FAILED if new sample rate is greater +* then the maximumum sample rate set at creation time. The reverb will +* continue to work but with possible lost of quality. +* If this is a problem, the caller should follow steps 2.1 and 2.2. +* Reverb API. +*/ +int +fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate) +{ + int status = FLUID_OK; + + fluid_return_val_if_fail(rev != NULL, FLUID_FAILED); + + if(sample_rate > rev->late.sample_rate_max) + { + FLUID_LOG(FLUID_WARN, + "fdn reverb: sample rate %.0f Hz is deduced to %.0f Hz\n", + sample_rate, rev->late.sample_rate_max); + + /* Reduce sample rate to the maximum value set at creation time. + The reverb will continue to work with possible lost of quality. + */ + sample_rate = rev->late.sample_rate_max; + status = FLUID_FAILED; + } + + /* Initialize all modulated lines according to sample rate change. */ + initialize_mod_delay_lines(&rev->late, sample_rate); + + /* updates damping filter coefficients according to sample rate change */ + update_rev_time_damping(&rev->late, rev->roomsize, rev->damp); + + return status; +} + +/* +* Damps the reverb by clearing the delay lines. +* @param rev the reverb. +* +* Reverb API. +*/ +void +fluid_revmodel_reset(fluid_revmodel_t *rev) +{ + fluid_return_if_fail(rev != NULL); + + fluid_revmodel_init(rev); +} + +/*----------------------------------------------------------------------------- +* fdn reverb process replace. +* @param rev pointer on reverb. +* @param in monophonic buffer input (FLUID_BUFSIZE sample). +* @param left_out stereo left processed output (FLUID_BUFSIZE sample). +* @param right_out stereo right processed output (FLUID_BUFSIZE sample). +* +* The processed reverb is replacing anything there in out. +* Reverb API. +-----------------------------------------------------------------------------*/ +void +fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int i, k; + + fluid_real_t xn; /* mono input x(n) */ + fluid_real_t out_tone_filter; /* tone corrector output */ + fluid_real_t out_left, out_right; /* output stereo Left and Right */ + fluid_real_t matrix_factor; /* partial matrix computation */ + fluid_real_t delay_out_s; /* sample */ + fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */ + + for(k = 0; k < FLUID_BUFSIZE; k++) + { + /* stereo output */ + out_left = out_right = 0; + +#ifdef DENORMALISING + /* Input is adjusted by DC_OFFSET. */ + xn = (in[k]) * FIXED_GAIN + DC_OFFSET; +#else + xn = (in[k]) * FIXED_GAIN; +#endif + + /*-------------------------------------------------------------------- + tone correction. + */ + out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer; + rev->late.tone_buffer = xn; + xn = out_tone_filter; + /*-------------------------------------------------------------------- + process feedback delayed network: + - xn is the input signal. + - before inserting in the line input we first we get the delay lines + output, filter them and compute output in delay_out[]. + - also matrix_factor is computed (to simplify further matrix product) + ---------------------------------------------------------------------*/ + /* We begin with the modulated output delay line + damping filter */ + matrix_factor = 0; + + for(i = 0; i < NBR_DELAYS; i++) + { + mod_delay_line *mdl = &rev->late.mod_delay_lines[i]; + /* get current modulated output */ + delay_out_s = get_mod_delay(mdl); + + /* process low pass damping filter + (input:delay_out_s, output:delay_out_s) */ + process_damping_filter(delay_out_s, delay_out_s, mdl); + + /* Result in delay_out[], and matrix_factor. + These will be of use later during input line process */ + delay_out[i] = delay_out_s; /* result in delay_out[] */ + matrix_factor += delay_out_s; /* result in matrix_factor */ + + /* Process stereo output */ + /* stereo left = left + out_left_gain * delay_out */ + out_left += rev->late.out_left_gain[i] * delay_out_s; + /* stereo right= right+ out_right_gain * delay_out */ + out_right += rev->late.out_right_gain[i] * delay_out_s; + } + + /* now we process the input delay line.Each input is a combination of + - xn: input signal + - delay_out[] the output of a delay line given by a permutation matrix P + - and matrix_factor. + This computes: in_delay_line = xn + (delay_out[] * matrix A) with + an algorithm equivalent but faster than using a product with matrix A. + */ + /* matrix_factor = output sum * (-2.0)/N */ + matrix_factor *= FDN_MATRIX_FACTOR; + matrix_factor += xn; /* adds reverb input signal */ + + for(i = 1; i < NBR_DELAYS; i++) + { + /* delay_in[i-1] = delay_out[i] + matrix_factor */ + delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl; + push_in_delay_line(dl, delay_out[i] + matrix_factor); + } + + /* last line input (NB_DELAY-1) */ + /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */ + { + delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl; + push_in_delay_line(dl, delay_out[0] + matrix_factor); + } + + /*-------------------------------------------------------------------*/ +#ifdef DENORMALISING + /* Removes the DC offset */ + out_left -= DC_OFFSET; + out_right -= DC_OFFSET; +#endif + + /* Calculates stereo output REPLACING anything already there: */ + /* + left_out[k] = out_left * rev->wet1 + out_right * rev->wet2; + right_out[k] = out_right * rev->wet1 + out_left * rev->wet2; + + As wet1 is integrated in stereo coefficient wet 1 is now + integrated in out_left and out_right, so we simplify previous + relation by suppression of one multiply as this: + + left_out[k] = out_left + out_right * rev->wet2; + right_out[k] = out_right + out_left * rev->wet2; + */ + left_out[k] = out_left + out_right * rev->wet2; + right_out[k] = out_right + out_left * rev->wet2; + } +} + + +/*----------------------------------------------------------------------------- +* fdn reverb process mix. +* @param rev pointer on reverb. +* @param in monophonic buffer input (FLUID_BUFSIZE samples). +* @param left_out stereo left processed output (FLUID_BUFSIZE samples). +* @param right_out stereo right processed output (FLUID_BUFSIZE samples). +* +* The processed reverb is mixed in out with samples already there in out. +* Reverb API. +-----------------------------------------------------------------------------*/ +void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out) +{ + int i, k; + + fluid_real_t xn; /* mono input x(n) */ + fluid_real_t out_tone_filter; /* tone corrector output */ + fluid_real_t out_left, out_right; /* output stereo Left and Right */ + fluid_real_t matrix_factor; /* partial matrix term */ + fluid_real_t delay_out_s; /* sample */ + fluid_real_t delay_out[NBR_DELAYS]; /* Line output + damper output */ + + for(k = 0; k < FLUID_BUFSIZE; k++) + { + /* stereo output */ + out_left = out_right = 0; +#ifdef DENORMALISING + /* Input is adjusted by DC_OFFSET. */ + xn = (in[k]) * FIXED_GAIN + DC_OFFSET; +#else + xn = (in[k]) * FIXED_GAIN; +#endif + + /*-------------------------------------------------------------------- + tone correction + */ + out_tone_filter = xn * rev->late.b1 - rev->late.b2 * rev->late.tone_buffer; + rev->late.tone_buffer = xn; + xn = out_tone_filter; + /*-------------------------------------------------------------------- + process feedback delayed network: + - xn is the input signal. + - before inserting in the line input we first we get the delay lines + output, filter them and compute output in local delay_out[]. + - also matrix_factor is computed (to simplify further matrix product). + ---------------------------------------------------------------------*/ + /* We begin with the modulated output delay line + damping filter */ + matrix_factor = 0; + + for(i = 0; i < NBR_DELAYS; i++) + { + mod_delay_line *mdl = &rev->late.mod_delay_lines[i]; + /* get current modulated output */ + delay_out_s = get_mod_delay(mdl); + + /* process low pass damping filter + (input:delay_out_s, output:delay_out_s) */ + process_damping_filter(delay_out_s, delay_out_s, mdl); + + /* Result in delay_out[], and matrix_factor. + These will be of use later during input line process */ + delay_out[i] = delay_out_s; /* result in delay_out[] */ + matrix_factor += delay_out_s; /* result in matrix_factor */ + + /* Process stereo output */ + /* stereo left = left + out_left_gain * delay_out */ + out_left += rev->late.out_left_gain[i] * delay_out_s; + /* stereo right= right+ out_right_gain * delay_out */ + out_right += rev->late.out_right_gain[i] * delay_out_s; + } + + /* now we process the input delay line. Each input is a combination of: + - xn: input signal + - delay_out[] the output of a delay line given by a permutation matrix P + - and matrix_factor. + This computes: in_delay_line = xn + (delay_out[] * matrix A) with + an algorithm equivalent but faster than using a product with matrix A. + */ + /* matrix_factor = output sum * (-2.0)/N */ + matrix_factor *= FDN_MATRIX_FACTOR; + matrix_factor += xn; /* adds reverb input signal */ + + for(i = 1; i < NBR_DELAYS; i++) + { + /* delay_in[i-1] = delay_out[i] + matrix_factor */ + delay_line *dl = &rev->late.mod_delay_lines[i - 1].dl; + push_in_delay_line(dl, delay_out[i] + matrix_factor); + } + + /* last line input (NB_DELAY-1) */ + /* delay_in[0] = delay_out[NB_DELAY -1] + matrix_factor */ + { + delay_line *dl = &rev->late.mod_delay_lines[NBR_DELAYS - 1].dl; + push_in_delay_line(dl, delay_out[0] + matrix_factor); + } + + /*-------------------------------------------------------------------*/ +#ifdef DENORMALISING + /* Removes the DC offset */ + out_left -= DC_OFFSET; + out_right -= DC_OFFSET; +#endif + /* Calculates stereo output MIXING anything already there: */ + /* + left_out[k] += out_left * rev->wet1 + out_right * rev->wet2; + right_out[k] += out_right * rev->wet1 + out_left * rev->wet2; + + As wet1 is integrated in stereo coefficient wet 1 is now + integrated in out_left and out_right, so we simplify previous + relation by suppression of one multiply as this: + + left_out[k] += out_left + out_right * rev->wet2; + right_out[k] += out_right + out_left * rev->wet2; + */ + left_out[k] += out_left + out_right * rev->wet2; + right_out[k] += out_right + out_left * rev->wet2; + } +} diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_rev.h b/thirdparty/fluidsynth/src/rvoice/fluid_rev.h new file mode 100644 index 000000000..35c9cf664 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_rev.h @@ -0,0 +1,91 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_REV_H +#define _FLUID_REV_H + +#include "fluidsynth_priv.h" + +typedef struct _fluid_revmodel_t fluid_revmodel_t; + +/* enum describing each reverb parameter */ +enum fluid_reverb_param +{ + FLUID_REVERB_ROOMSIZE, /**< reverb time */ + FLUID_REVERB_DAMP, /**< high frequency damping */ + FLUID_REVERB_WIDTH, /**< stereo width */ + FLUID_REVERB_LEVEL, /**< output level */ + FLUID_REVERB_PARAM_LAST /* number of enum fluid_reverb_param */ +}; + +/* return a bit flag from param: 2^param */ +#define FLUID_REVPARAM_TO_SETFLAG(param) (1 << param) + +/** Flags for fluid_revmodel_set() */ +typedef enum +{ + FLUID_REVMODEL_SET_ROOMSIZE = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_ROOMSIZE), + FLUID_REVMODEL_SET_DAMPING = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_DAMP), + FLUID_REVMODEL_SET_WIDTH = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_WIDTH), + FLUID_REVMODEL_SET_LEVEL = FLUID_REVPARAM_TO_SETFLAG(FLUID_REVERB_LEVEL), + + /** Value for fluid_revmodel_set() which sets all reverb parameters. */ + FLUID_REVMODEL_SET_ALL = FLUID_REVMODEL_SET_LEVEL + | FLUID_REVMODEL_SET_WIDTH + | FLUID_REVMODEL_SET_DAMPING + | FLUID_REVMODEL_SET_ROOMSIZE, +} fluid_revmodel_set_t; + +/* + * reverb preset + */ +typedef struct _fluid_revmodel_presets_t +{ + const char *name; + fluid_real_t roomsize; + fluid_real_t damp; + fluid_real_t width; + fluid_real_t level; +} fluid_revmodel_presets_t; + + +/* + * reverb + */ +fluid_revmodel_t * +new_fluid_revmodel(fluid_real_t sample_rate_max, fluid_real_t sample_rate); + +void delete_fluid_revmodel(fluid_revmodel_t *rev); + +void fluid_revmodel_processmix(fluid_revmodel_t *rev, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); + +void fluid_revmodel_processreplace(fluid_revmodel_t *rev, const fluid_real_t *in, + fluid_real_t *left_out, fluid_real_t *right_out); + +void fluid_revmodel_reset(fluid_revmodel_t *rev); + +void fluid_revmodel_set(fluid_revmodel_t *rev, int set, fluid_real_t roomsize, + fluid_real_t damping, fluid_real_t width, fluid_real_t level); + +int fluid_revmodel_samplerate_change(fluid_revmodel_t *rev, fluid_real_t sample_rate); + +#endif /* _FLUID_REV_H */ diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_rvoice.c b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice.c new file mode 100644 index 000000000..403a55587 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice.c @@ -0,0 +1,937 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_rvoice.h" +#include "fluid_conv.h" +#include "fluid_sys.h" + + +static void fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks); + +/** + * @return -1 if voice is quiet, 0 if voice has finished, 1 otherwise + */ +static FLUID_INLINE int +fluid_rvoice_calc_amp(fluid_rvoice_t *voice) +{ + fluid_real_t target_amp; /* target amplitude */ + + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVDELAY) + { + return -1; /* The volume amplitude is in hold phase. No sound is produced. */ + } + + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK) + { + /* the envelope is in the attack section: ramp linearly to max value. + * A positive modlfo_to_vol should increase volume (negative attenuation). + */ + target_amp = fluid_cb2amp(voice->dsp.attenuation) + * fluid_cb2amp(fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol) + * fluid_adsr_env_get_val(&voice->envlfo.volenv); + } + else + { + fluid_real_t amplitude_that_reaches_noise_floor; + fluid_real_t amp_max; + + target_amp = fluid_cb2amp(voice->dsp.attenuation) + * fluid_cb2amp(FLUID_PEAK_ATTENUATION * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)) + + fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol); + + /* We turn off a voice, if the volume has dropped low enough. */ + + /* A voice can be turned off, when an estimate for the volume + * (upper bound) falls below that volume, that will drop the + * sample below the noise floor. + */ + + /* If the loop amplitude is known, we can use it if the voice loop is within + * the sample loop + */ + + /* Is the playing pointer already in the loop? */ + if(voice->dsp.has_looped) + { + amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_loop; + } + else + { + amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_nonloop; + } + + /* voice->attenuation_min is a lower boundary for the attenuation + * now and in the future (possibly 0 in the worst case). Now the + * amplitude of sample and volenv cannot exceed amp_max (since + * volenv_val can only drop): + */ + + amp_max = fluid_cb2amp(voice->dsp.min_attenuation_cB) * + fluid_adsr_env_get_val(&voice->envlfo.volenv); + + /* And if amp_max is already smaller than the known amplitude, + * which will attenuate the sample below the noise floor, then we + * can safely turn off the voice. Duh. */ + if(amp_max < amplitude_that_reaches_noise_floor) + { + return 0; + } + } + + /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */ + voice->dsp.amp_incr = (target_amp - voice->dsp.amp) / FLUID_BUFSIZE; + + fluid_check_fpe("voice_write amplitude calculation"); + + /* no volume and not changing? - No need to process */ + if((voice->dsp.amp == 0.0f) && (voice->dsp.amp_incr == 0.0f)) + { + return -1; + } + + return 1; +} + + +/* these should be the absolute minimum that FluidSynth can deal with */ +#define FLUID_MIN_LOOP_SIZE 2 +#define FLUID_MIN_LOOP_PAD 0 + +#define FLUID_SAMPLESANITY_CHECK (1 << 0) +#define FLUID_SAMPLESANITY_STARTUP (1 << 1) + +/* Purpose: + * + * Make sure, that sample start / end point and loop points are in + * proper order. When starting up, calculate the initial phase. + * TODO: Investigate whether this can be moved from rvoice to voice. + */ +static void +fluid_rvoice_check_sample_sanity(fluid_rvoice_t *voice) +{ + int min_index_nonloop = (int) voice->dsp.sample->start; + int max_index_nonloop = (int) voice->dsp.sample->end; + + /* make sure we have enough samples surrounding the loop */ + int min_index_loop = (int) voice->dsp.sample->start + FLUID_MIN_LOOP_PAD; + int max_index_loop = (int) voice->dsp.sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */ + fluid_check_fpe("voice_check_sample_sanity start"); + +#if 0 + printf("Sample from %i to %i\n", voice->dsp.sample->start, voice->dsp.sample->end); + printf("Sample loop from %i %i\n", voice->dsp.sample->loopstart, voice->dsp.sample->loopend); + printf("Playback from %i to %i\n", voice->dsp.start, voice->dsp.end); + printf("Playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend); +#endif + + /* Keep the start point within the sample data */ + if(voice->dsp.start < min_index_nonloop) + { + voice->dsp.start = min_index_nonloop; + } + else if(voice->dsp.start > max_index_nonloop) + { + voice->dsp.start = max_index_nonloop; + } + + /* Keep the end point within the sample data */ + if(voice->dsp.end < min_index_nonloop) + { + voice->dsp.end = min_index_nonloop; + } + else if(voice->dsp.end > max_index_nonloop) + { + voice->dsp.end = max_index_nonloop; + } + + /* Keep start and end point in the right order */ + if(voice->dsp.start > voice->dsp.end) + { + int temp = voice->dsp.start; + voice->dsp.start = voice->dsp.end; + voice->dsp.end = temp; + /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */ + } + + /* Zero length? */ + if(voice->dsp.start == voice->dsp.end) + { + fluid_rvoice_voiceoff(voice, NULL); + return; + } + + if((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) + || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) + { + /* Keep the loop start point within the sample data */ + if(voice->dsp.loopstart < min_index_loop) + { + voice->dsp.loopstart = min_index_loop; + } + else if(voice->dsp.loopstart > max_index_loop) + { + voice->dsp.loopstart = max_index_loop; + } + + /* Keep the loop end point within the sample data */ + if(voice->dsp.loopend < min_index_loop) + { + voice->dsp.loopend = min_index_loop; + } + else if(voice->dsp.loopend > max_index_loop) + { + voice->dsp.loopend = max_index_loop; + } + + /* Keep loop start and end point in the right order */ + if(voice->dsp.loopstart > voice->dsp.loopend) + { + int temp = voice->dsp.loopstart; + voice->dsp.loopstart = voice->dsp.loopend; + voice->dsp.loopend = temp; + /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */ + } + + /* Loop too short? Then don't loop. */ + if(voice->dsp.loopend < voice->dsp.loopstart + FLUID_MIN_LOOP_SIZE) + { + voice->dsp.samplemode = FLUID_UNLOOPED; + } + + /* The loop points may have changed. Obtain a new estimate for the loop volume. */ + /* Is the voice loop within the sample loop? */ + if((int)voice->dsp.loopstart >= (int)voice->dsp.sample->loopstart + && (int)voice->dsp.loopend <= (int)voice->dsp.sample->loopend) + { + /* Is there a valid peak amplitude available for the loop, and can we use it? */ + if(voice->dsp.sample->amplitude_that_reaches_noise_floor_is_valid && voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE) + { + voice->dsp.amplitude_that_reaches_noise_floor_loop = voice->dsp.sample->amplitude_that_reaches_noise_floor / voice->dsp.synth_gain; + } + else + { + /* Worst case */ + voice->dsp.amplitude_that_reaches_noise_floor_loop = voice->dsp.amplitude_that_reaches_noise_floor_nonloop; + }; + }; + + } /* if sample mode is looped */ + + /* Run startup specific code (only once, when the voice is started) */ + if(voice->dsp.check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP) + { + if(max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE) + { + if((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) + || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) + { + voice->dsp.samplemode = FLUID_UNLOOPED; + } + } + + /* Set the initial phase of the voice (using the result from the + start offset modulators). */ + fluid_phase_set_int(voice->dsp.phase, voice->dsp.start); + } /* if startup */ + + /* Is this voice run in loop mode, or does it run straight to the + end of the waveform data? */ + if(((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) && + (fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE)) + || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) + { + /* Yes, it will loop as soon as it reaches the loop point. In + * this case we must prevent, that the playback pointer (phase) + * happens to end up beyond the 2nd loop point, because the + * point has moved. The DSP algorithm is unable to cope with + * that situation. So if the phase is beyond the 2nd loop + * point, set it to the start of the loop. No way to avoid some + * noise here. Note: If the sample pointer ends up -before the + * first loop point- instead, then the DSP loop will just play + * the sample, enter the loop and proceed as expected => no + * actions required. + */ + int index_in_sample = fluid_phase_index(voice->dsp.phase); + + if(index_in_sample >= voice->dsp.loopend) + { + /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */ + fluid_phase_set_int(voice->dsp.phase, voice->dsp.loopstart); + } + } + + /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->dsp.start, voice->dsp.end, voice->dsp.loopstart, voice->dsp.loopend); */ + + /* Sample sanity has been assured. Don't check again, until some + sample parameter is changed by modulation. */ + voice->dsp.check_sample_sanity_flag = 0; +#if 0 + printf("Sane? playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend); +#endif + fluid_check_fpe("voice_check_sample_sanity"); +} + + +/** + * Synthesize a voice to a buffer. + * + * @param voice rvoice to synthesize + * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length) + * @return Count of samples written to dsp_buf. (-1 means voice is currently + * quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.) + * + * Panning, reverb and chorus are processed separately. The dsp interpolation + * routine is in (fluid_rvoice_dsp.c). + */ +int +fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf) +{ + int ticks = voice->envlfo.ticks; + int count, is_looping; + fluid_real_t modenv_val; + + /******************* sample sanity check **********/ + + if(!voice->dsp.sample) + { + return 0; + } + + if(voice->dsp.check_sample_sanity_flag) + { + fluid_rvoice_check_sample_sanity(voice); + } + + /******************* noteoff check ****************/ + + if(voice->envlfo.noteoff_ticks != 0 && + voice->envlfo.ticks >= voice->envlfo.noteoff_ticks) + { + fluid_rvoice_noteoff_LOCAL(voice, 0); + } + + voice->envlfo.ticks += FLUID_BUFSIZE; + + /******************* vol env **********************/ + + fluid_adsr_env_calc(&voice->envlfo.volenv); + fluid_check_fpe("voice_write vol env"); + + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVFINISHED) + { + return 0; + } + + /******************* mod env **********************/ + + fluid_adsr_env_calc(&voice->envlfo.modenv); + fluid_check_fpe("voice_write mod env"); + + /******************* lfo **********************/ + + fluid_lfo_calc(&voice->envlfo.modlfo, ticks); + fluid_check_fpe("voice_write mod LFO"); + fluid_lfo_calc(&voice->envlfo.viblfo, ticks); + fluid_check_fpe("voice_write vib LFO"); + + /******************* amplitude **********************/ + + count = fluid_rvoice_calc_amp(voice); + + if(count <= 0) + { + return count; /* return -1 if voice is quiet, 0 if voice has finished */ + } + + /******************* phase **********************/ + + /* SF2.04 section 8.1.2 #26: + * attack of modEnv is convex ?!? + */ + modenv_val = (fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK) + ? fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv)) + : fluid_adsr_env_get_val(&voice->envlfo.modenv); + /* Calculate the number of samples, that the DSP loop advances + * through the original waveform with each step in the output + * buffer. It is the ratio between the frequencies of original + * waveform and output waveform.*/ + voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch + + voice->dsp.pitchoffset + + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch + + fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch + + modenv_val * voice->envlfo.modenv_to_pitch) + / voice->dsp.root_pitch_hz; + + /******************* portamento ****************/ + /* pitchoffset is updated if enabled. + Pitchoffset will be added to dsp pitch at next phase calculation time */ + + /* In most cases portamento will be disabled. Thus first verify that portamento is + * enabled before updating pitchoffset and before disabling portamento when necessary, + * in order to keep the performance loss at minimum. + * If the algorithm would first update pitchoffset and then verify if portamento + * needs to be disabled, there would be a significant performance drop on a x87 FPU + */ + if(voice->dsp.pitchinc > 0.0f) + { + /* portamento is enabled, so update pitchoffset */ + voice->dsp.pitchoffset += voice->dsp.pitchinc; + + /* when pitchoffset reaches 0.0f, portamento is disabled */ + if(voice->dsp.pitchoffset > 0.0f) + { + voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; + } + } + else if(voice->dsp.pitchinc < 0.0f) + { + /* portamento is enabled, so update pitchoffset */ + voice->dsp.pitchoffset += voice->dsp.pitchinc; + + /* when pitchoffset reaches 0.0f, portamento is disabled */ + if(voice->dsp.pitchoffset < 0.0f) + { + voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f; + } + } + + fluid_check_fpe("voice_write phase calculation"); + + /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */ + if(voice->dsp.phase_incr == 0) + { + voice->dsp.phase_incr = 1; + } + + /* voice is currently looping? */ + is_looping = voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE + || (voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE + && fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE); + + /*********************** run the dsp chain ************************ + * The sample is mixed with the output buffer. + * The buffer has to be filled from 0 to FLUID_BUFSIZE-1. + * Depending on the position in the loop and the loop size, this + * may require several runs. */ + + switch(voice->dsp.interp_method) + { + case FLUID_INTERP_NONE: + count = fluid_rvoice_dsp_interpolate_none(&voice->dsp, dsp_buf, is_looping); + break; + + case FLUID_INTERP_LINEAR: + count = fluid_rvoice_dsp_interpolate_linear(&voice->dsp, dsp_buf, is_looping); + break; + + case FLUID_INTERP_4THORDER: + default: + count = fluid_rvoice_dsp_interpolate_4th_order(&voice->dsp, dsp_buf, is_looping); + break; + + case FLUID_INTERP_7THORDER: + count = fluid_rvoice_dsp_interpolate_7th_order(&voice->dsp, dsp_buf, is_looping); + break; + } + + fluid_check_fpe("voice_write interpolation"); + + if(count == 0) + { + return count; + } + + /*************** resonant filter ******************/ + + fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate, + fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc + + modenv_val * voice->envlfo.modenv_to_fc); + + fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count); + + /* additional custom filter - only uses the fixed modulator, no lfos... */ + fluid_iir_filter_calc(&voice->resonant_custom_filter, voice->dsp.output_rate, 0); + fluid_iir_filter_apply(&voice->resonant_custom_filter, dsp_buf, count); + + return count; +} + +/** + * Initialize buffers up to (and including) bufnum + */ +static int +fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t *buffers, unsigned int bufnum) +{ + unsigned int i; + + if(bufnum < buffers->count) + { + return FLUID_OK; + } + + if(bufnum >= FLUID_RVOICE_MAX_BUFS) + { + return FLUID_FAILED; + } + + for(i = buffers->count; i <= bufnum; i++) + { + buffers->bufs[i].target_amp = 0.0f; + buffers->bufs[i].current_amp = 0.0f; + } + + buffers->count = bufnum + 1; + return FLUID_OK; +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp) +{ + fluid_rvoice_buffers_t *buffers = obj; + unsigned int bufnum = param[0].i; + fluid_real_t value = param[1].real; + + if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) + { + return; + } + + buffers->bufs[bufnum].target_amp = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping) +{ + fluid_rvoice_buffers_t *buffers = obj; + unsigned int bufnum = param[0].i; + int mapping = param[1].i; + + if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK) + { + return; + } + + buffers->bufs[bufnum].mapping = mapping; +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset) +{ + fluid_rvoice_t *voice = obj; + + voice->dsp.has_looped = 0; + voice->envlfo.ticks = 0; + voice->envlfo.noteoff_ticks = 0; + voice->dsp.amp = 0.0f; /* The last value of the volume envelope, used to + calculate the volume increment during + processing */ + + /* legato initialization */ + voice->dsp.pitchoffset = 0.0; /* portamento initialization */ + voice->dsp.pitchinc = 0.0; + + /* mod env initialization*/ + fluid_adsr_env_reset(&voice->envlfo.modenv); + + /* vol env initialization */ + fluid_adsr_env_reset(&voice->envlfo.volenv); + + /* Fixme: Retrieve from any other existing + voice on this channel to keep LFOs in + unison? */ + fluid_lfo_reset(&voice->envlfo.viblfo); + fluid_lfo_reset(&voice->envlfo.modlfo); + + /* Clear sample history in filter */ + fluid_iir_filter_reset(&voice->resonant_filter); + fluid_iir_filter_reset(&voice->resonant_custom_filter); + + /* Force setting of the phase at the first DSP loop run + * This cannot be done earlier, because it depends on modulators. + [DH] Is that comment really true? */ + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff) +{ + fluid_rvoice_t *rvoice = obj; + unsigned int min_ticks = param[0].i; + + fluid_rvoice_noteoff_LOCAL(rvoice, min_ticks); +} + +static void +fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks) +{ + if(min_ticks > voice->envlfo.ticks) + { + /* Delay noteoff */ + voice->envlfo.noteoff_ticks = min_ticks; + return; + } + + voice->envlfo.noteoff_ticks = 0; + + if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK) + { + /* A voice is turned off during the attack section of the volume + * envelope. The attack section ramps up linearly with + * amplitude. The other sections use logarithmic scaling. Calculate new + * volenv_val to achieve equivalent amplitude during the release phase + * for seamless volume transition. + */ + if(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0) + { + fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol; + fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * fluid_cb2amp(lfo); + fluid_real_t env_value = - (((-200.f / FLUID_M_LN10) * FLUID_LOGF(amp) - lfo) / FLUID_PEAK_ATTENUATION - 1); + fluid_clip(env_value, 0.0f, 1.0f); + fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); + } + } + + if(fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK) + { + /* A voice is turned off during the attack section of the modulation + * envelope. The attack section use convex scaling with pitch and filter + * frequency cutoff (see fluid_rvoice_write(): modenv_val = fluid_convex(127 * modenv.val) + * The other sections use linear scaling: modenv_val = modenv.val + * + * Calculate new modenv.val to achieve equivalent modenv_val during the release phase + * for seamless pitch and filter frequency cutoff transition. + */ + if(fluid_adsr_env_get_val(&voice->envlfo.modenv) > 0) + { + fluid_real_t env_value = fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv)); + fluid_clip(env_value, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.modenv, env_value); + } + } + + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE); + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE); +} + +/** + * skips to Attack section + * + * Updates vol and attack data + * Correction on volume val to achieve equivalent amplitude at noteOn legato + * + * @param voice the synthesis voice to be updated +*/ +static FLUID_INLINE void fluid_rvoice_local_retrigger_attack(fluid_rvoice_t *voice) +{ + /* skips to Attack section */ + /* Once in Attack section, current count must be reset, to be sure + that the section will be not be prematurely finished. */ + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVATTACK); + { + /* Correction on volume val to achieve equivalent amplitude at noteOn legato */ + fluid_env_data_t *env_data; + fluid_real_t peak = fluid_cb2amp(voice->dsp.attenuation); + fluid_real_t prev_peak = fluid_cb2amp(voice->dsp.prev_attenuation); + voice->envlfo.volenv.val = (voice->envlfo.volenv.val * prev_peak) / peak; + /* Correction on slope direction for Attack section */ + env_data = &voice->envlfo.volenv.data[FLUID_VOICE_ENVATTACK]; + + if(voice->envlfo.volenv.val <= 1.0f) + { + /* slope attack for legato note needs to be positive from val up to 1 */ + env_data->increment = 1.0f / env_data->count; + env_data->min = -1.0f; + env_data->max = 1.0f; + } + else + { + /* slope attack for legato note needs to be negative: from val down to 1 */ + env_data->increment = -voice->envlfo.volenv.val / env_data->count; + env_data->min = 1.0f; + env_data->max = voice->envlfo.volenv.val; + } + } +} + +/** + * Used by legato Mode : multi_retrigger + * see fluid_synth_noteon_mono_legato_multi_retrigger() + * @param voice the synthesis voice to be updated +*/ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack) +{ + fluid_rvoice_t *voice = obj; + int section; /* volume or modulation section */ + + /*------------------------------------------------------------------------- + Section skip for volume envelope + --------------------------------------------------------------------------*/ + section = fluid_adsr_env_get_section(&voice->envlfo.volenv); + if(section >= FLUID_VOICE_ENVHOLD) + { + /* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new + volenv_val to achieve equivalent amplitude during the attack phase + for seamless volume transition. */ + fluid_real_t amp_cb, env_value; + amp_cb = FLUID_PEAK_ATTENUATION * + (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv)); + env_value = fluid_cb2amp(amp_cb); /* a bit of optimization */ + fluid_clip(env_value, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value); + /* next, skips to Attack section */ + } + + /* skips to Attack section from any section */ + /* Update vol and attack data */ + fluid_rvoice_local_retrigger_attack(voice); + + /*------------------------------------------------------------------------- + Section skip for modulation envelope + --------------------------------------------------------------------------*/ + section = fluid_adsr_env_get_section(&voice->envlfo.modenv); + if(section >= FLUID_VOICE_ENVHOLD) + { + /* DECAY, SUSTAIN,RELEASE section use linear scaling. + Since v 2.1 , as recommended by soundfont 2.01/2.4 spec, ATTACK section + uses convex shape (see fluid_rvoice_write() - fluid_convex()). + Calculate new modenv value (new_value) for seamless attack transition. + Here we need the inverse of fluid_convex() function defined as: + new_value = pow(10, (1 - current_val) . FLUID_PEAK_ATTENUATION / -200 . 2.0) + For performance reason we use fluid_cb2amp(Val) = pow(10, val/-200) with + val = (1 - current_val) . FLUID_PEAK_ATTENUATION / 2.0 + */ + fluid_real_t new_value; /* new modenv value */ + new_value = fluid_cb2amp((1.0f - fluid_adsr_env_get_val(&voice->envlfo.modenv)) + * FLUID_PEAK_ATTENUATION / 2.0); + fluid_clip(new_value, 0.0, 1.0); + fluid_adsr_env_set_val(&voice->envlfo.modenv, new_value); + } + /* Skips from any section to ATTACK section */ + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK); +} + +/** + * sets the portamento dsp parameters: dsp.pitchoffset, dsp.pitchinc + * @param voice rvoice to set portamento. + * @param countinc increment count number. + * @param pitchoffset pitch offset to apply to voice dsp.pitch. + * + * Notes: + * 1) To get continuous portamento between consecutive noteOn (n1,n2,n3...), + * pitchoffset is accumulated in current dsp pitchoffset. + * 2) And to get constant portamento duration, dsp pitch increment is updated. +*/ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento) +{ + fluid_rvoice_t *voice = obj; + unsigned int countinc = param[0].i; + fluid_real_t pitchoffset = param[1].real; + + if(countinc) + { + voice->dsp.pitchoffset += pitchoffset; + voice->dsp.pitchinc = - voice->dsp.pitchoffset / countinc; + } + + /* Then during the voice processing (in fluid_rvoice_write()), + dsp.pitchoffset will be incremented by dsp pitchinc. */ +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.output_rate = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method) +{ + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.interp_method = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.root_pitch_hz = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.pitch = value; +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.prev_attenuation = voice->dsp.attenuation; + voice->dsp.attenuation = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.min_attenuation_cB = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.viblfo_to_pitch = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modlfo_to_pitch = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modlfo_to_vol = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modlfo_to_fc = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modenv_to_fc = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->envlfo.modenv_to_pitch = value; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain) +{ + fluid_rvoice_t *voice = obj; + fluid_real_t value = param[0].real; + + voice->dsp.synth_gain = value; + + /* For a looped sample, this value will be overwritten as soon as the + * loop parameters are initialized (they may depend on modulators). + * This value can be kept, it is a worst-case estimate. + */ + voice->dsp.amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / value; + voice->dsp.amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start) +{ + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.start = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end) +{ + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.end = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart) +{ + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.loopstart = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend) +{ + fluid_rvoice_t *voice = obj; + int value = param[0].i; + + voice->dsp.loopend = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode) +{ + fluid_rvoice_t *voice = obj; + enum fluid_loop value = param[0].i; + + voice->dsp.samplemode = value; + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK; +} + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample) +{ + fluid_rvoice_t *voice = obj; + fluid_sample_t *value = param[0].ptr; + + voice->dsp.sample = value; + + if(value) + { + voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP; + } +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff) +{ + fluid_rvoice_t *voice = obj; + + fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED); + fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED); +} + + diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_rvoice.h b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice.h new file mode 100644 index 000000000..610afd725 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice.h @@ -0,0 +1,231 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_RVOICE_H +#define _FLUID_RVOICE_H + +#include "fluidsynth_priv.h" +#include "fluid_iir_filter.h" +#include "fluid_adsr_env.h" +#include "fluid_lfo.h" +#include "fluid_phase.h" +#include "fluid_sfont.h" + +typedef struct _fluid_rvoice_envlfo_t fluid_rvoice_envlfo_t; +typedef struct _fluid_rvoice_dsp_t fluid_rvoice_dsp_t; +typedef struct _fluid_rvoice_buffers_t fluid_rvoice_buffers_t; +typedef struct _fluid_rvoice_t fluid_rvoice_t; + +/* Smallest amplitude that can be perceived (full scale is +/- 0.5) + * 16 bits => 96+4=100 dB dynamic range => 0.00001 + * 24 bits => 144-4 = 140 dB dynamic range => 1.e-7 + * 1.e-7 * 2 == 2.e-7 :) + */ +#define FLUID_NOISE_FLOOR ((fluid_real_t)2.e-7) + +enum fluid_loop +{ + FLUID_UNLOOPED = 0, + FLUID_LOOP_DURING_RELEASE = 1, + FLUID_NOTUSED = 2, + FLUID_LOOP_UNTIL_RELEASE = 3 +}; + +/* + * rvoice ticks-based parameters + * These parameters must be updated even if the voice is currently quiet. + */ +struct _fluid_rvoice_envlfo_t +{ + /* Note-off minimum length */ + unsigned int ticks; + unsigned int noteoff_ticks; + + /* vol env */ + fluid_adsr_env_t volenv; + + /* mod env */ + fluid_adsr_env_t modenv; + fluid_real_t modenv_to_fc; + fluid_real_t modenv_to_pitch; + + /* mod lfo */ + fluid_lfo_t modlfo; + fluid_real_t modlfo_to_fc; + fluid_real_t modlfo_to_pitch; + fluid_real_t modlfo_to_vol; + + /* vib lfo */ + fluid_lfo_t viblfo; + fluid_real_t viblfo_to_pitch; +}; + +/* + * rvoice parameters needed for dsp interpolation + */ +struct _fluid_rvoice_dsp_t +{ + /* interpolation method, as in fluid_interp in fluidsynth.h */ + enum fluid_interp interp_method; + enum fluid_loop samplemode; + + /* Flag that is set as soon as the first loop is completed. */ + char has_looped; + + /* Flag that initiates, that sample-related parameters have to be checked. */ + char check_sample_sanity_flag; + + fluid_sample_t *sample; + + /* sample and loop start and end points (offset in sample memory). */ + int start; + int end; + int loopstart; + int loopend; /* Note: first point following the loop (superimposed on loopstart) */ + + /* Stuff needed for portamento calculations */ + fluid_real_t pitchoffset; /* the portamento range in midicents */ + fluid_real_t pitchinc; /* the portamento increment in midicents */ + + /* Stuff needed for phase calculations */ + + fluid_real_t pitch; /* the pitch in midicents */ + fluid_real_t root_pitch_hz; + fluid_real_t output_rate; + + /* Stuff needed for amplitude calculations */ + + fluid_real_t attenuation; /* the attenuation in centibels */ + fluid_real_t prev_attenuation; /* the previous attenuation in centibels + used by fluid_rvoice_multi_retrigger_attack() */ + fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation + * during the lifetime of the voice */ + fluid_real_t amplitude_that_reaches_noise_floor_nonloop; + fluid_real_t amplitude_that_reaches_noise_floor_loop; + fluid_real_t synth_gain; /* master gain */ + + /* Dynamic input to the interpolator below */ + + fluid_real_t amp; /* current linear amplitude */ + fluid_real_t amp_incr; /* amplitude increment value for the next FLUID_BUFSIZE samples */ + + fluid_phase_t phase; /* the phase (current sample offset) of the sample wave */ + fluid_real_t phase_incr; /* the phase increment for the next FLUID_BUFSIZE samples */ +}; + +/* Currently left, right, reverb, chorus. To be changed if we + ever add surround positioning, or stereo reverb/chorus */ +#define FLUID_RVOICE_MAX_BUFS (4) + +/* + * rvoice mixer-related parameters + */ +struct _fluid_rvoice_buffers_t +{ + unsigned int count; /* Number of records in "bufs" */ + struct + { + /* the actual, linearly interpolated amplitude with which the dsp sample should be mixed into the buf */ + fluid_real_t current_amp; + + /* the desired amplitude [...] mixed into the buf (directly set by e.g. rapidly changing PAN events) */ + fluid_real_t target_amp; + + /* Mapping to mixdown buffer index */ + int mapping; + } bufs[FLUID_RVOICE_MAX_BUFS]; +}; + + +/* + * Hard real-time parameters needed to synthesize a voice + */ +struct _fluid_rvoice_t +{ + fluid_rvoice_envlfo_t envlfo; + fluid_rvoice_dsp_t dsp; + fluid_iir_filter_t resonant_filter; /* IIR resonant dsp filter */ + fluid_iir_filter_t resonant_custom_filter; /* optional custom/general-purpose IIR resonant filter */ + fluid_rvoice_buffers_t buffers; +}; + + +int fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample); + +/* defined in fluid_rvoice_dsp.c */ +void fluid_rvoice_dsp_config(void); +int fluid_rvoice_dsp_interpolate_none(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); +int fluid_rvoice_dsp_interpolate_linear(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); +int fluid_rvoice_dsp_interpolate_4th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); +int fluid_rvoice_dsp_interpolate_7th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int is_looping); + + +/* + * Combines the most significant 16 bit part of a sample with a potentially present + * least sig. 8 bit part in order to create a 24 bit sample. + */ +static FLUID_INLINE int32_t +fluid_rvoice_get_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx) +{ + /* cast sample to unsigned type, so we can safely shift and bitwise or + * without relying on undefined behaviour (should never happen anyway ofc...) */ + uint32_t msb = (uint32_t)dsp_msb[idx]; + uint8_t lsb = 0U; + + /* most soundfonts have 16 bit samples, assume that it's unlikely we + * experience 24 bit samples here */ + if(FLUID_UNLIKELY(dsp_lsb != NULL)) + { + lsb = (uint8_t)dsp_lsb[idx]; + } + + return (int32_t)((msb << 8) | lsb); +} + +#endif diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_dsp.c b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_dsp.c new file mode 100644 index 000000000..b43a0f190 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_dsp.c @@ -0,0 +1,636 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sys.h" +#include "fluid_phase.h" +#include "fluid_rvoice.h" +#include "fluid_rvoice_dsp_tables.inc.h" + +/* Purpose: + * + * Interpolates audio data (obtains values between the samples of the original + * waveform data). + * + * Variables loaded from the voice structure (assigned in fluid_rvoice_write()): + * - dsp_data: Pointer to the original waveform data + * - dsp_phase: The position in the original waveform data. + * This has an integer and a fractional part (between samples). + * - dsp_phase_incr: For each output sample, the position in the original + * waveform advances by dsp_phase_incr. This also has an integer + * part and a fractional part. + * If a sample is played at root pitch (no pitch change), + * dsp_phase_incr is integer=1 and fractional=0. + * - dsp_amp: The current amplitude envelope value. + * - dsp_amp_incr: The changing rate of the amplitude envelope. + * + * A couple of variables are used internally, their results are discarded: + * - dsp_i: Index through the output buffer + * - dsp_buf: Output buffer of floating point values (FLUID_BUFSIZE in length) + */ + +/* Interpolation (find a value between two samples of the original waveform) */ + +static FLUID_INLINE fluid_real_t +fluid_rvoice_get_float_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx) +{ + int32_t sample = fluid_rvoice_get_sample(dsp_msb, dsp_lsb, idx); + return (fluid_real_t)sample; +} + +/* No interpolation. Just take the sample, which is closest to + * the playback pointer. Questionable quality, but very + * efficient. */ +int +fluid_rvoice_dsp_interpolate_none(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int end_index; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + end_index = looping ? voice->loopend - 1 : voice->end; + + while(1) + { + dsp_phase_index = fluid_phase_index_round(dsp_phase); /* round to nearest point */ + + /* interpolate sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + dsp_buf[dsp_i] = dsp_amp * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index_round(dsp_phase); /* round to nearest point */ + dsp_amp += dsp_amp_incr; + } + + /* break out if not looping (buffer may not be full) */ + if(!looping) + { + break; + } + + /* go back to loop start */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + voice->has_looped = 1; + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + } + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +} + +/* Straight line interpolation. + * Returns number of samples processed (usually FLUID_BUFSIZE but could be + * smaller if end of sample occurs). + */ +int +fluid_rvoice_dsp_interpolate_linear(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int end_index; + fluid_real_t point; + const fluid_real_t *FLUID_RESTRICT coeffs; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + /* last index before 2nd interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 1; + + /* 2nd interpolation point to use at end of loop or sample */ + if(looping) + { + point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); /* loop start */ + } + else + { + point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end); /* duplicate end for samples no longer looping */ + } + + while(1) + { + dsp_phase_index = fluid_phase_index(dsp_phase); + + /* interpolate the sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[1] * point); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; /* increment amplitude */ + } + + if(!looping) + { + break; /* break out if not looping (end of sample) */ + } + + /* go back to loop start (if past */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + voice->has_looped = 1; + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index--; /* set end back to second to last sample point */ + } + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +} + +/* 4th order (cubic) interpolation. + * Returns number of samples processed (usually FLUID_BUFSIZE but could be + * smaller if end of sample occurs). + */ +int +fluid_rvoice_dsp_interpolate_4th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int start_index, end_index; + fluid_real_t start_point, end_point1, end_point2; + const fluid_real_t *FLUID_RESTRICT coeffs; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + /* last index before 4th interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 2; + + if(voice->has_looped) /* set start_index and start point if looped or not */ + { + start_index = voice->loopstart; + start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); /* last point in loop (wrap around) */ + } + else + { + start_index = voice->start; + start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->start); /* just duplicate the point */ + } + + /* get points off the end (loop start if looping, duplicate point if end) */ + if(looping) + { + end_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); + end_point2 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1); + } + else + { + end_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end); + end_point2 = end_point1; + } + + while(1) + { + dsp_phase_index = fluid_phase_index(dsp_phase); + + /* interpolate first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * start_point + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* interpolate the sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index++; /* we're now interpolating the 2nd to last point */ + + /* interpolate within 2nd to last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[3] * end_point1); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within the last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)]; + dsp_buf[dsp_i] = dsp_amp * + (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[2] * end_point1 + + coeffs[3] * end_point2); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + if(!looping) + { + break; /* break out if not looping (end of sample) */ + } + + /* go back to loop start */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + + if(!voice->has_looped) + { + voice->has_looped = 1; + start_index = voice->loopstart; + start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); + } + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index -= 2; /* set end back to third to last sample point */ + } + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +} + +/* 7th order interpolation. + * Returns number of samples processed (usually FLUID_BUFSIZE but could be + * smaller if end of sample occurs). + */ +int +fluid_rvoice_dsp_interpolate_7th_order(fluid_rvoice_dsp_t *voice, fluid_real_t *FLUID_RESTRICT dsp_buf, int looping) +{ + fluid_phase_t dsp_phase = voice->phase; + fluid_phase_t dsp_phase_incr; + short int *dsp_data = voice->sample->data; + char *dsp_data24 = voice->sample->data24; + fluid_real_t dsp_amp = voice->amp; + fluid_real_t dsp_amp_incr = voice->amp_incr; + unsigned int dsp_i = 0; + unsigned int dsp_phase_index; + unsigned int start_index, end_index; + fluid_real_t start_points[3], end_points[3]; + const fluid_real_t *FLUID_RESTRICT coeffs; + + /* Convert playback "speed" floating point value to phase index/fract */ + fluid_phase_set_float(dsp_phase_incr, voice->phase_incr); + + /* add 1/2 sample to dsp_phase since 7th order interpolation is centered on + * the 4th sample point */ + fluid_phase_incr(dsp_phase, (fluid_phase_t)0x80000000); + + /* last index before 7th interpolation point must be specially handled */ + end_index = (looping ? voice->loopend - 1 : voice->end) - 3; + + if(voice->has_looped) /* set start_index and start point if looped or not */ + { + start_index = voice->loopstart; + start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); + start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2); + start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 3); + } + else + { + start_index = voice->start; + start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->start); /* just duplicate the start point */ + start_points[1] = start_points[0]; + start_points[2] = start_points[0]; + } + + /* get the 3 points off the end (loop start if looping, duplicate point if end) */ + if(looping) + { + end_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); + end_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1); + end_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 2); + } + else + { + end_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end); + end_points[1] = end_points[0]; + end_points[2] = end_points[0]; + } + + while(1) + { + dsp_phase_index = fluid_phase_index(dsp_phase); + + /* interpolate first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * start_points[2] + + coeffs[1] * start_points[1] + + coeffs[2] * start_points[0] + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index++; + + /* interpolate 2nd to first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * start_points[1] + + coeffs[1] * start_points[0] + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index++; + + /* interpolate 3rd to first sample point (start or loop start) if needed */ + for(; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * start_points[0] + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + start_index -= 2; /* set back to original start index */ + + + /* interpolate the sequence of sample points */ + for(; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 3)); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + /* break out if buffer filled */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index++; /* we're now interpolating the 3rd to last point */ + + /* interpolate within 3rd to last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 2) + + coeffs[6] * end_points[0]); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the 2nd to last point */ + + /* interpolate within 2nd to last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index + 1) + + coeffs[5] * end_points[0] + + coeffs[6] * end_points[1]); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + end_index++; /* we're now interpolating the last point */ + + /* interpolate within last point */ + for(; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++) + { + coeffs = sinc_table7[fluid_phase_fract_to_tablerow(dsp_phase)]; + + dsp_buf[dsp_i] = dsp_amp + * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 3) + + coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 2) + + coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index - 1) + + coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index) + + coeffs[4] * end_points[0] + + coeffs[5] * end_points[1] + + coeffs[6] * end_points[2]); + + /* increment phase and amplitude */ + fluid_phase_incr(dsp_phase, dsp_phase_incr); + dsp_phase_index = fluid_phase_index(dsp_phase); + dsp_amp += dsp_amp_incr; + } + + if(!looping) + { + break; /* break out if not looping (end of sample) */ + } + + /* go back to loop start */ + if(dsp_phase_index > end_index) + { + fluid_phase_sub_int(dsp_phase, voice->loopend - voice->loopstart); + + if(!voice->has_looped) + { + voice->has_looped = 1; + start_index = voice->loopstart; + start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); + start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2); + start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 3); + } + } + + /* break out if filled buffer */ + if(dsp_i >= FLUID_BUFSIZE) + { + break; + } + + end_index -= 3; /* set end back to 4th to last sample point */ + } + + /* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on + * the 4th sample point (correct back to real value) */ + fluid_phase_decr(dsp_phase, (fluid_phase_t)0x80000000); + + voice->phase = dsp_phase; + voice->amp = dsp_amp; + + return (dsp_i); +} diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_dsp_tables.h b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_dsp_tables.h new file mode 100644 index 000000000..befc9faf4 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_dsp_tables.h @@ -0,0 +1,8 @@ + +#ifndef _FLUID_RVOICE_DSP_TABLES_H +#define _FLUID_RVOICE_DSP_TABLES_H + +#define FLUID_INTERP_MAX 256 +#define SINC_INTERP_ORDER 7 /* 7th order constant */ + +#endif diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_event.c b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_event.c new file mode 100644 index 000000000..e60115f36 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_event.c @@ -0,0 +1,202 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_rvoice_event.h" +#include "fluid_rvoice.h" +#include "fluid_rvoice_mixer.h" +#include "fluid_iir_filter.h" +#include "fluid_lfo.h" +#include "fluid_adsr_env.h" + +static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t *handler, const fluid_rvoice_event_t *src_event); + +static FLUID_INLINE void +fluid_rvoice_event_dispatch(fluid_rvoice_event_t *event) +{ + event->method(event->object, event->param); +} + + +/** + * In order to be able to push more than one event atomically, + * use push for all events, then use flush to commit them to the + * queue. If threadsafe is false, all events are processed immediately. */ +int +fluid_rvoice_eventhandler_push_int_real(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, int intparam, + fluid_real_t realparam) +{ + fluid_rvoice_event_t local_event; + + local_event.method = method; + local_event.object = object; + local_event.param[0].i = intparam; + local_event.param[1].real = realparam; + + return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); +} + +int +fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t *handler, fluid_rvoice_function_t method, void *object, fluid_rvoice_param_t param[MAX_EVENT_PARAMS]) +{ + fluid_rvoice_event_t local_event; + + local_event.method = method; + local_event.object = object; + FLUID_MEMCPY(&local_event.param, param, sizeof(*param) * MAX_EVENT_PARAMS); + + return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); +} + +int +fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, void *ptr) +{ + fluid_rvoice_event_t local_event; + + local_event.method = method; + local_event.object = object; + local_event.param[0].ptr = ptr; + + return fluid_rvoice_eventhandler_push_LOCAL(handler, &local_event); +} + +static int fluid_rvoice_eventhandler_push_LOCAL(fluid_rvoice_eventhandler_t *handler, const fluid_rvoice_event_t *src_event) +{ + fluid_rvoice_event_t *event; + int old_queue_stored = fluid_atomic_int_add(&handler->queue_stored, 1); + + event = fluid_ringbuffer_get_inptr(handler->queue, old_queue_stored); + + if(event == NULL) + { + fluid_atomic_int_add(&handler->queue_stored, -1); + FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing synth.polyphony!"); + return FLUID_FAILED; // Buffer full... + } + + FLUID_MEMCPY(event, src_event, sizeof(*event)); + + return FLUID_OK; +} + + +void +fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *eventhandler, fluid_rvoice_t *rvoice) +{ + fluid_rvoice_t **vptr = fluid_ringbuffer_get_inptr(eventhandler->finished_voices, 0); + + if(vptr == NULL) + { + return; // Buffer full + } + + *vptr = rvoice; + fluid_ringbuffer_next_inptr(eventhandler->finished_voices, 1); +} + +fluid_rvoice_eventhandler_t * +new_fluid_rvoice_eventhandler(int queuesize, + int finished_voices_size, int bufs, int fx_bufs, int fx_units, + fluid_real_t sample_rate_max, fluid_real_t sample_rate, + int extra_threads, int prio) +{ + fluid_rvoice_eventhandler_t *eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t); + + if(eventhandler == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + eventhandler->mixer = NULL; + eventhandler->queue = NULL; + eventhandler->finished_voices = NULL; + + fluid_atomic_int_set(&eventhandler->queue_stored, 0); + + eventhandler->finished_voices = new_fluid_ringbuffer(finished_voices_size, + sizeof(fluid_rvoice_t *)); + + if(eventhandler->finished_voices == NULL) + { + goto error_recovery; + } + + eventhandler->queue = new_fluid_ringbuffer(queuesize, sizeof(fluid_rvoice_event_t)); + + if(eventhandler->queue == NULL) + { + goto error_recovery; + } + + eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, fx_units, + sample_rate_max, sample_rate, eventhandler, extra_threads, prio); + + if(eventhandler->mixer == NULL) + { + goto error_recovery; + } + + return eventhandler; + +error_recovery: + delete_fluid_rvoice_eventhandler(eventhandler); + return NULL; +} + +int +fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t *handler) +{ + return fluid_ringbuffer_get_count(handler->queue); +} + + +/** + * Call fluid_rvoice_event_dispatch for all events in queue + * @return number of events dispatched + */ +int +fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t *handler) +{ + fluid_rvoice_event_t *event; + int result = 0; + + while(NULL != (event = fluid_ringbuffer_get_outptr(handler->queue))) + { + fluid_rvoice_event_dispatch(event); + result++; + fluid_ringbuffer_next_outptr(handler->queue); + } + + return result; +} + + +void +delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t *handler) +{ + fluid_return_if_fail(handler != NULL); + + delete_fluid_rvoice_mixer(handler->mixer); + delete_fluid_ringbuffer(handler->queue); + delete_fluid_ringbuffer(handler->finished_voices); + FLUID_FREE(handler); +} diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_event.h b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_event.h new file mode 100644 index 000000000..225e9069e --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_event.h @@ -0,0 +1,114 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_RVOICE_EVENT_H +#define _FLUID_RVOICE_EVENT_H + +#include "fluidsynth_priv.h" +#include "fluid_rvoice_mixer.h" +#include "fluid_ringbuffer.h" + +typedef struct _fluid_rvoice_event_t fluid_rvoice_event_t; + +struct _fluid_rvoice_event_t +{ + fluid_rvoice_function_t method; + void *object; + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; +}; + +/* + * Bridge between the renderer thread and the midi state thread. + * fluid_rvoice_eventhandler_fetch_all() can be called in parallel + * with fluid_rvoice_eventhandler_push/flush() + */ +struct _fluid_rvoice_eventhandler_t +{ + fluid_ringbuffer_t *queue; /**< List of fluid_rvoice_event_t */ + fluid_atomic_int_t queue_stored; /**< Extras pushed but not flushed */ + fluid_ringbuffer_t *finished_voices; /**< return queue from handler, list of fluid_rvoice_t* */ + fluid_rvoice_mixer_t *mixer; +}; + +fluid_rvoice_eventhandler_t *new_fluid_rvoice_eventhandler( + int queuesize, int finished_voices_size, int bufs, + int fx_bufs, int fx_units, fluid_real_t sample_rate_max, fluid_real_t sample_rate, int, int); + +void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t *); + +int fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t *); +int fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t *); +void fluid_rvoice_eventhandler_finished_voice_callback(fluid_rvoice_eventhandler_t *eventhandler, + fluid_rvoice_t *rvoice); + +static FLUID_INLINE void +fluid_rvoice_eventhandler_flush(fluid_rvoice_eventhandler_t *handler) +{ + int queue_stored = fluid_atomic_int_get(&handler->queue_stored); + + if(queue_stored > 0) + { + fluid_atomic_int_set(&handler->queue_stored, 0); + fluid_ringbuffer_next_inptr(handler->queue, queue_stored); + } +} + +/** + * @return next finished voice, or NULL if nothing in queue + */ +static FLUID_INLINE fluid_rvoice_t * +fluid_rvoice_eventhandler_get_finished_voice(fluid_rvoice_eventhandler_t *handler) +{ + void *result = fluid_ringbuffer_get_outptr(handler->finished_voices); + + if(result == NULL) + { + return NULL; + } + + result = * (fluid_rvoice_t **) result; + fluid_ringbuffer_next_outptr(handler->finished_voices); + return result; +} + + +int fluid_rvoice_eventhandler_push_int_real(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, int intparam, + fluid_real_t realparam); + +int fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, void *ptr); + +int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_function_t method, void *object, + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]); + +static FLUID_INLINE void +fluid_rvoice_eventhandler_add_rvoice(fluid_rvoice_eventhandler_t *handler, + fluid_rvoice_t *rvoice) +{ + fluid_rvoice_eventhandler_push_ptr(handler, fluid_rvoice_mixer_add_voice, + handler->mixer, rvoice); +} + + + +#endif diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_mixer.c b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_mixer.c new file mode 100644 index 000000000..1f30c43f8 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_mixer.c @@ -0,0 +1,1713 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_rvoice_mixer.h" +#include "fluid_rvoice.h" +#include "fluid_sys.h" +#include "fluid_rev.h" +#include "fluid_chorus.h" +#include "fluid_ladspa.h" +#include "fluid_synth.h" + + +// If less than x voices, the thread overhead is larger than the gain, +// so don't activate the thread(s). +#define VOICES_PER_THREAD 8 + +typedef struct _fluid_mixer_buffers_t fluid_mixer_buffers_t; + +struct _fluid_mixer_buffers_t +{ + fluid_rvoice_mixer_t *mixer; /**< Owner of object */ +#if ENABLE_MIXER_THREADS + fluid_thread_t *thread; /**< Thread object */ + fluid_atomic_int_t ready; /**< Atomic: buffers are ready for mixing */ +#endif + + fluid_rvoice_t **finished_voices; /* List of voices who have finished */ + int finished_voice_count; + + fluid_real_t *local_buf; + + int buf_count; + int fx_buf_count; + + /** buffer to store the left part of a stereo channel to. + * Specifically a two dimensional array, containing \c buf_count sample buffers + * (i.e. for each synth.audio-groups), of which each contains + * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples) + * @note Each sample buffer is aligned to the FLUID_DEFAULT_ALIGNMENT + * boundary provided that this pointer points to an aligned buffer. + * So make sure to access the sample buffer by first aligning this + * pointer using fluid_align_ptr() + */ + fluid_real_t *left_buf; + + /** dito, but for right part of a stereo channel */ + fluid_real_t *right_buf; + + /** buffer to store the left part of a stereo effects channel to. + * Specifically a two dimensional array, containing \c fx_buf_count buffers + * (i.e. for each synth.effects-channels), of which each buffer contains + * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples) + */ + fluid_real_t *fx_left_buf; + fluid_real_t *fx_right_buf; +}; + +typedef struct _fluid_mixer_fx_t fluid_mixer_fx_t; + +struct _fluid_mixer_fx_t +{ + fluid_revmodel_t *reverb; /**< Reverb unit */ + /* reverb shadow parameters here will be returned if queried */ + double reverb_param[FLUID_REVERB_PARAM_LAST]; + int reverb_on; /* reverb on/off */ + + fluid_chorus_t *chorus; /**< Chorus unit */ + /* chorus shadow parameters here will be returned if queried */ + double chorus_param[FLUID_CHORUS_PARAM_LAST]; + int chorus_on; /* chorus on/off */ +}; + +struct _fluid_rvoice_mixer_t +{ + fluid_mixer_fx_t *fx; + + fluid_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */ + fluid_rvoice_eventhandler_t *eventhandler; + + fluid_rvoice_t **rvoices; /**< Read-only: Voices array, sorted so that all nulls are last */ + int polyphony; /**< Read-only: Length of voices array */ + int active_voices; /**< Read-only: Number of non-null voices */ + int current_blockcount; /**< Read-only: how many blocks to process this time */ + int fx_units; + int with_reverb; /**< Should the synth use the built-in reverb unit? */ + int with_chorus; /**< Should the synth use the built-in chorus unit? */ + int mix_fx_to_out; /**< Should the effects be mixed in with the primary output? */ + +#ifdef LADSPA + fluid_ladspa_fx_t *ladspa_fx; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */ +#endif + +#if ENABLE_MIXER_THREADS +// int sleeping_threads; /**< Atomic: number of threads currently asleep */ +// int active_threads; /**< Atomic: number of threads in the thread loop */ + fluid_atomic_int_t threads_should_terminate; /**< Atomic: Set to TRUE when threads should terminate */ + fluid_atomic_int_t current_rvoice; /**< Atomic: for the threads to know next voice to */ + fluid_cond_t *wakeup_threads; /**< Signalled when the threads should wake up */ + fluid_cond_mutex_t *wakeup_threads_m; /**< wakeup_threads mutex companion */ + fluid_cond_t *thread_ready; /**< Signalled from thread, when the thread has a buffer ready for mixing */ + fluid_cond_mutex_t *thread_ready_m; /**< thread_ready mutex companion */ + + int thread_count; /**< Number of extra mixer threads for multi-core rendering */ + fluid_mixer_buffers_t *threads; /**< Array of mixer threads (thread_count in length) */ +#endif +}; + +#if ENABLE_MIXER_THREADS +static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer); +static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level); +#endif + +static FLUID_INLINE void +fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcount) +{ + const int fx_channels_per_unit = mixer->buffers.fx_buf_count / mixer->fx_units; + int i, f; + int dry_count = mixer->buffers.buf_count; /* dry buffers count */ + int mix_fx_to_out = mixer->mix_fx_to_out; /* get mix_fx_to_out mode */ + int dry_idx = 0; /* dry buffer index */ + int buf_idx; /* buffer index */ + int samp_idx; /* sample index in buffer */ + int sample_count; /* sample count to process */ + + void (*reverb_process_func)(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); + void (*chorus_process_func)(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out); + + fluid_real_t *out_rev_l, *out_rev_r, *out_ch_l, *out_ch_r; + + // all dry unprocessed mono input is stored in the left channel + fluid_real_t *in_rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *in_ch = in_rev; + + fluid_profile_ref_var(prof_ref); + +#ifdef LADSPA + + /* Run the signal through the LADSPA Fx unit. The buffers have already been + * set up in fluid_rvoice_mixer_set_ladspa. */ + if(mixer->ladspa_fx) + { + fluid_ladspa_run(mixer->ladspa_fx, current_blockcount, FLUID_BUFSIZE); + fluid_check_fpe("LADSPA"); + } + +#endif + + if(mix_fx_to_out) + { + // mix effects to first stereo channel + out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); + out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); + + reverb_process_func = fluid_revmodel_processmix; + chorus_process_func = fluid_chorus_processmix; + + } + else + { + // replace effects into respective stereo effects channel + out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + + reverb_process_func = fluid_revmodel_processreplace; + chorus_process_func = fluid_chorus_processreplace; + } + + + if(mixer->with_reverb) + { + for(f = 0; f < mixer->fx_units; f++) + { + if(!mixer->fx[f].reverb_on) + { + continue; /* this reverb unit is disabled */ + } + + buf_idx = f * fx_channels_per_unit + SYNTH_REVERB_CHANNEL; + samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + sample_count = current_blockcount * FLUID_BUFSIZE; + + /* in mix mode, map fx out_rev at index f to a dry buffer at index dry_idx */ + if(mix_fx_to_out) + { + /* dry buffer mapping, should be done more flexible in the future */ + dry_idx = (f % dry_count) * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + } + + for(i = 0; i < sample_count; i += FLUID_BUFSIZE, samp_idx += FLUID_BUFSIZE) + { + reverb_process_func(mixer->fx[f].reverb, + &in_rev[samp_idx], + mix_fx_to_out ? &out_rev_l[dry_idx + i] : &out_rev_l[samp_idx], + mix_fx_to_out ? &out_rev_r[dry_idx + i] : &out_rev_r[samp_idx]); + } + } + + fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref, 0, + current_blockcount * FLUID_BUFSIZE); + } + + if(mixer->with_chorus) + { + for(f = 0; f < mixer->fx_units; f++) + { + if(!mixer->fx[f].chorus_on) + { + continue; /* this chorus unit is disabled */ + } + + buf_idx = f * fx_channels_per_unit + SYNTH_CHORUS_CHANNEL; + samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + sample_count = current_blockcount * FLUID_BUFSIZE; + + /* in mix mode, map fx out_ch at index f to a dry buffer at index dry_idx */ + if(mix_fx_to_out) + { + /* dry buffer mapping, should be done more flexible in the future */ + dry_idx = (f % dry_count) * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE; + } + + for(i = 0; i < sample_count; i += FLUID_BUFSIZE, samp_idx += FLUID_BUFSIZE) + { + chorus_process_func(mixer->fx[f].chorus, + &in_ch [samp_idx], + mix_fx_to_out ? &out_ch_l[dry_idx + i] : &out_ch_l[samp_idx], + mix_fx_to_out ? &out_ch_r[dry_idx + i] : &out_ch_r[samp_idx]); + } + } + + fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref, 0, + current_blockcount * FLUID_BUFSIZE); + } + +} + +/** + * Glue to get fluid_rvoice_buffers_mix what it wants + * Note: Make sure outbufs has 2 * (buf_count + fx_buf_count) elements before calling + */ +static FLUID_INLINE int +fluid_mixer_buffers_prepare(fluid_mixer_buffers_t *buffers, fluid_real_t **outbufs) +{ + fluid_real_t *base_ptr; + int i; + const int fx_channels_per_unit = buffers->fx_buf_count / buffers->mixer->fx_units; + const int offset = buffers->buf_count * 2; + int with_reverb = buffers->mixer->with_reverb; + int with_chorus = buffers->mixer->with_chorus; + + /* Set up the reverb and chorus buffers only when the effect is enabled or + * when LADSPA is active. Nonexisting buffers are detected in the DSP loop. + * Not sending the effect signals saves some time in that case. */ +#ifdef LADSPA + int with_ladspa = (buffers->mixer->ladspa_fx != NULL); + with_reverb = (with_reverb | with_ladspa); + with_chorus = (with_chorus | with_ladspa); +#endif + + // all the dry, non-processed mono audio for effects is to be stored in the left buffers + base_ptr = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < buffers->mixer->fx_units; i++) + { + int fx_idx = i * fx_channels_per_unit; + + outbufs[offset + fx_idx + SYNTH_REVERB_CHANNEL] = + (with_reverb) + ? &base_ptr[(fx_idx + SYNTH_REVERB_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] + : NULL; + + outbufs[offset + fx_idx + SYNTH_CHORUS_CHANNEL] = + (with_chorus) + ? &base_ptr[(fx_idx + SYNTH_CHORUS_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT] + : NULL; + } + + /* The output associated with a MIDI channel is wrapped around + * using the number of audio groups as modulo divider. This is + * typically the number of output channels on the 'sound card', + * as long as the LADSPA Fx unit is not used. In case of LADSPA + * unit, think of it as subgroups on a mixer. + * + * For example: Assume that the number of groups is set to 2. + * Then MIDI channel 1, 3, 5, 7 etc. go to output 1, channels 2, + * 4, 6, 8 etc to output 2. Or assume 3 groups: Then MIDI + * channels 1, 4, 7, 10 etc go to output 1; 2, 5, 8, 11 etc to + * output 2, 3, 6, 9, 12 etc to output 3. + */ + base_ptr = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < buffers->buf_count; i++) + { + outbufs[i * 2] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + } + + base_ptr = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < buffers->buf_count; i++) + { + outbufs[i * 2 + 1] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + } + + return offset + buffers->fx_buf_count; +} + + +static FLUID_INLINE void +fluid_finish_rvoice(fluid_mixer_buffers_t *buffers, fluid_rvoice_t *rvoice) +{ + if(buffers->finished_voice_count < buffers->mixer->polyphony) + { + buffers->finished_voices[buffers->finished_voice_count++] = rvoice; + } + else + { + FLUID_LOG(FLUID_ERR, "Exceeded finished voices array, try increasing polyphony"); + } +} + +static void +fluid_mixer_buffer_process_finished_voices(fluid_mixer_buffers_t *buffers) +{ + int i, j; + + for(i = 0; i < buffers->finished_voice_count; i++) + { + fluid_rvoice_t *v = buffers->finished_voices[i]; + int av = buffers->mixer->active_voices; + + for(j = 0; j < av; j++) + { + if(v == buffers->mixer->rvoices[j]) + { + av--; + + /* Pack the array */ + if(j < av) + { + buffers->mixer->rvoices[j] = buffers->mixer->rvoices[av]; + } + } + } + + buffers->mixer->active_voices = av; + + fluid_rvoice_eventhandler_finished_voice_callback(buffers->mixer->eventhandler, v); + } + + buffers->finished_voice_count = 0; +} + +static FLUID_INLINE void fluid_rvoice_mixer_process_finished_voices(fluid_rvoice_mixer_t *mixer) +{ +#if ENABLE_MIXER_THREADS + int i; + + for(i = 0; i < mixer->thread_count; i++) + { + fluid_mixer_buffer_process_finished_voices(&mixer->threads[i]); + } + +#endif + fluid_mixer_buffer_process_finished_voices(&mixer->buffers); +} + + +static FLUID_INLINE fluid_real_t * +get_dest_buf(fluid_rvoice_buffers_t *buffers, int index, + fluid_real_t **dest_bufs, int dest_bufcount) +{ + int j = buffers->bufs[index].mapping; + + if(j >= dest_bufcount || j < 0) + { + return NULL; + } + + return dest_bufs[j]; +} + +/** + * Mix samples down from internal dsp_buf to output buffers + * + * @param buffers Destination buffer(s) + * @param dsp_buf Mono sample source + * @param start_block starting sample in dsp_buf + * @param sample_count number of samples to mix following \c start_block + * @param dest_bufs Array of buffers to mixdown to + * @param dest_bufcount Length of dest_bufs (i.e count of buffers) + */ +static void +fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers, + const fluid_real_t *FLUID_RESTRICT dsp_buf, + int start_block, int sample_count, + fluid_real_t **dest_bufs, int dest_bufcount) +{ + /* buffers count to mixdown to */ + int bufcount = buffers->count; + int i, dsp_i; + + /* if there is nothing to mix, return immediately */ + if(sample_count <= 0 || dest_bufcount <= 0) + { + return; + } + + FLUID_ASSERT((uintptr_t)dsp_buf % FLUID_DEFAULT_ALIGNMENT == 0); + FLUID_ASSERT((uintptr_t)(&dsp_buf[start_block * FLUID_BUFSIZE]) % FLUID_DEFAULT_ALIGNMENT == 0); + + /* mixdown for each buffer */ + for(i = 0; i < bufcount; i++) + { + fluid_real_t *FLUID_RESTRICT buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount); + fluid_real_t target_amp = buffers->bufs[i].target_amp; + fluid_real_t current_amp = buffers->bufs[i].current_amp; + fluid_real_t amp_incr; + + if(buf == NULL || (current_amp == 0.0f && target_amp == 0.0f)) + { + continue; + } + + amp_incr = (target_amp - current_amp) / FLUID_BUFSIZE; + + FLUID_ASSERT((uintptr_t)buf % FLUID_DEFAULT_ALIGNMENT == 0); + + /* Mixdown sample_count samples in the current buffer buf + * + * For the first FLUID_BUFSIZE samples, we linearly interpolate the buffers amplitude to + * avoid clicks/pops when rapidly changing the channels panning (issue 768). + * + * We could have squashed this into one single loop by using an if clause within the loop body. + * But it seems like having two separate loops is easier for compilers to understand, and therefore + * auto-vectorizing the loops. + */ + if(sample_count < FLUID_BUFSIZE) + { + // scalar loop variant, the voice will have finished afterwards + for(dsp_i = 0; dsp_i < sample_count; dsp_i++) + { + buf[start_block * FLUID_BUFSIZE + dsp_i] += current_amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; + current_amp += amp_incr; + } + } + else + { + // here goes the vectorizable loop + #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT) + for(dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i++) + { + // We cannot simply increment current_amp by amp_incr during every iteration, as this would create a dependency and prevent vectorization. + buf[start_block * FLUID_BUFSIZE + dsp_i] += (current_amp + amp_incr * dsp_i) * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; + } + + // we have reached the target_amp + if(target_amp > 0) + { + /* Note, that this loop could be unrolled by FLUID_BUFSIZE elements */ + #pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT) + for(dsp_i = FLUID_BUFSIZE; dsp_i < sample_count; dsp_i++) + { + // Index by blocks (not by samples) to let the compiler know that we always start accessing + // buf and dsp_buf at the FLUID_BUFSIZE*sizeof(fluid_real_t) byte boundary and never somewhere + // in between. + // A good compiler should understand: Aha, so I don't need to add a peel loop when vectorizing + // this loop. Great. + buf[start_block * FLUID_BUFSIZE + dsp_i] += target_amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i]; + } + } + } + + buffers->bufs[i].current_amp = target_amp; + } +} + +/** + * Synthesize one voice and add to buffer. + * NOTE: If return value is less than blockcount*FLUID_BUFSIZE, that means + * voice has been finished, removed and possibly replaced with another voice. + */ +static FLUID_INLINE void +fluid_mixer_buffers_render_one(fluid_mixer_buffers_t *buffers, + fluid_rvoice_t *rvoice, fluid_real_t **dest_bufs, + unsigned int dest_bufcount, fluid_real_t *src_buf, int blockcount) +{ + int i, total_samples = 0, last_block_mixed = 0; + + for(i = 0; i < blockcount; i++) + { + /* render one block in src_buf */ + int s = fluid_rvoice_write(rvoice, &src_buf[FLUID_BUFSIZE * i]); + + if(s == -1) + { + /* the voice is silent, mix back all the previously rendered sound */ + fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed, + total_samples - (last_block_mixed * FLUID_BUFSIZE), + dest_bufs, dest_bufcount); + + last_block_mixed = i + 1; /* future block start index to mix from */ + total_samples += FLUID_BUFSIZE; /* accumulate samples count rendered */ + } + else + { + /* the voice wasn't quiet. Some samples have been rendered [0..FLUID_BUFSIZE] */ + total_samples += s; + + if(s < FLUID_BUFSIZE) + { + /* voice has finished */ + break; + } + } + } + + /* Now mix the remaining blocks from last_block_mixed to total_sample */ + fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed, + total_samples - (last_block_mixed * FLUID_BUFSIZE), + dest_bufs, dest_bufcount); + + if(total_samples < blockcount * FLUID_BUFSIZE) + { + /* voice has finished */ + fluid_finish_rvoice(buffers, rvoice); + } +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice) +{ + int i; + fluid_rvoice_mixer_t *mixer = obj; + fluid_rvoice_t *voice = param[0].ptr; + + if(mixer->active_voices < mixer->polyphony) + { + mixer->rvoices[mixer->active_voices++] = voice; + return; // success + } + + /* See if any voices just finished, if so, take its place. + This can happen in voice overflow conditions. */ + for(i = 0; i < mixer->active_voices; i++) + { + if(mixer->rvoices[i] == voice) + { + FLUID_LOG(FLUID_ERR, "Internal error: Trying to replace an existing rvoice in fluid_rvoice_mixer_add_voice?!"); + return; + } + + if(mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED) + { + fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]); + mixer->rvoices[i] = voice; + return; // success + } + } + + /* This should never happen */ + FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice"); + return; +} + +static int +fluid_mixer_buffers_update_polyphony(fluid_mixer_buffers_t *buffers, int value) +{ + void *newptr; + + if(buffers->finished_voice_count > value) + { + return FLUID_FAILED; + } + + newptr = FLUID_REALLOC(buffers->finished_voices, value * sizeof(fluid_rvoice_t *)); + + if(newptr == NULL && value > 0) + { + return FLUID_FAILED; + } + + buffers->finished_voices = newptr; + return FLUID_OK; +} + +/** + * Update polyphony - max number of voices (NOTE: not hard real-time capable) + * @return FLUID_OK or FLUID_FAILED + */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony) +{ + void *newptr; + fluid_rvoice_mixer_t *handler = obj; + int value = param[0].i; + + if(handler->active_voices > value) + { + return /*FLUID_FAILED*/; + } + + newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t *)); + + if(newptr == NULL) + { + return /*FLUID_FAILED*/; + } + + handler->rvoices = newptr; + + if(fluid_mixer_buffers_update_polyphony(&handler->buffers, value) + == FLUID_FAILED) + { + return /*FLUID_FAILED*/; + } + +#if ENABLE_MIXER_THREADS + { + int i; + + for(i = 0; i < handler->thread_count; i++) + { + if(fluid_mixer_buffers_update_polyphony(&handler->threads[i], value) + == FLUID_FAILED) + { + return /*FLUID_FAILED*/; + } + } + } +#endif + + handler->polyphony = value; + return /*FLUID_OK*/; +} + + +static void +fluid_render_loop_singlethread(fluid_rvoice_mixer_t *mixer, int blockcount) +{ + int i; + FLUID_DECLARE_VLA(fluid_real_t *, bufs, + mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2); + int bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs); + + fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT); + + fluid_profile_ref_var(prof_ref); + + for(i = 0; i < mixer->active_voices; i++) + { + fluid_mixer_buffers_render_one(&mixer->buffers, mixer->rvoices[i], bufs, + bufcount, local_buf, blockcount); + fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1, + blockcount * FLUID_BUFSIZE); + } +} + +static FLUID_INLINE void +fluid_mixer_buffers_zero(fluid_mixer_buffers_t *buffers, int current_blockcount) +{ + int i, size = current_blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t); + + /* TODO: Optimize by only zero out the buffers we actually use later on. */ + int buf_count = buffers->buf_count, fx_buf_count = buffers->fx_buf_count; + + fluid_real_t *FLUID_RESTRICT buf_l = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *FLUID_RESTRICT buf_r = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < buf_count; i++) + { + FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + } + + buf_l = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + buf_r = fluid_align_ptr(buffers->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < fx_buf_count; i++) + { + FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size); + } +} + +static int +fluid_mixer_buffers_init(fluid_mixer_buffers_t *buffers, fluid_rvoice_mixer_t *mixer) +{ + static const int samplecount = FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT; + + buffers->mixer = mixer; + buffers->buf_count = mixer->buffers.buf_count; + buffers->fx_buf_count = mixer->buffers.fx_buf_count; + + /* Local mono voice buf */ + buffers->local_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, samplecount, FLUID_DEFAULT_ALIGNMENT); + + /* Left and right audio buffers */ + + buffers->left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); + buffers->right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); + + if((buffers->local_buf == NULL) || (buffers->left_buf == NULL) || (buffers->right_buf == NULL)) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return 0; + } + + /* Effects audio buffers */ + + buffers->fx_left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); + buffers->fx_right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT); + + if((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL)) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return 0; + } + + buffers->finished_voices = NULL; + + if(fluid_mixer_buffers_update_polyphony(buffers, mixer->polyphony) + == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return 0; + } + + return 1; +} + +/** + * Note: Not hard real-time capable (calls malloc) + */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate) +{ + fluid_rvoice_mixer_t *mixer = obj; + fluid_real_t samplerate = param[1].real; // because fluid_synth_update_mixer() puts real into arg2 + + int i; + + for(i = 0; i < mixer->fx_units; i++) + { + if(mixer->fx[i].chorus) + { + fluid_chorus_samplerate_change(mixer->fx[i].chorus, samplerate); + } + + if(mixer->fx[i].reverb) + { + fluid_revmodel_samplerate_change(mixer->fx[i].reverb, samplerate); + + /* + fluid_revmodel_samplerate_change() shouldn't fail if the reverb was created + with sample_rate_max set to the maximum sample rate indicated in the settings. + If this condition isn't respected, the reverb will continue to work but with + lost of quality. + */ + } + } + +#if LADSPA + + if(mixer->ladspa_fx != NULL) + { + fluid_ladspa_set_sample_rate(mixer->ladspa_fx, samplerate); + } + +#endif +} + + +/** + * @param buf_count number of primary stereo buffers + * @param fx_buf_count number of stereo effect buffers + */ +fluid_rvoice_mixer_t * +new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, + fluid_real_t sample_rate_max, + fluid_real_t sample_rate, + fluid_rvoice_eventhandler_t *evthandler, + int extra_threads, int prio) +{ + int i; + fluid_rvoice_mixer_t *mixer = FLUID_NEW(fluid_rvoice_mixer_t); + + if(mixer == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(mixer, 0, sizeof(fluid_rvoice_mixer_t)); + mixer->eventhandler = evthandler; + mixer->fx_units = fx_units; + mixer->buffers.buf_count = buf_count; + mixer->buffers.fx_buf_count = fx_buf_count * fx_units; + + /* allocate the reverb module */ + mixer->fx = FLUID_ARRAY(fluid_mixer_fx_t, fx_units); + + if(mixer->fx == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + FLUID_MEMSET(mixer->fx, 0, fx_units * sizeof(*mixer->fx)); + + for(i = 0; i < fx_units; i++) + { + /* create reverb and chorus units */ + mixer->fx[i].reverb = new_fluid_revmodel(sample_rate_max, sample_rate); + mixer->fx[i].chorus = new_fluid_chorus(sample_rate); + + if(mixer->fx[i].reverb == NULL || mixer->fx[i].chorus == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + } + + if(!fluid_mixer_buffers_init(&mixer->buffers, mixer)) + { + goto error_recovery; + } + +#if ENABLE_MIXER_THREADS + mixer->thread_ready = new_fluid_cond(); + mixer->wakeup_threads = new_fluid_cond(); + mixer->thread_ready_m = new_fluid_cond_mutex(); + mixer->wakeup_threads_m = new_fluid_cond_mutex(); + + if(!mixer->thread_ready || !mixer->wakeup_threads || + !mixer->thread_ready_m || !mixer->wakeup_threads_m) + { + goto error_recovery; + } + + if(fluid_rvoice_mixer_set_threads(mixer, extra_threads, prio) != FLUID_OK) + { + goto error_recovery; + } + +#endif + + return mixer; + +error_recovery: + delete_fluid_rvoice_mixer(mixer); + return NULL; +} + +static void +fluid_mixer_buffers_free(fluid_mixer_buffers_t *buffers) +{ + FLUID_FREE(buffers->finished_voices); + + /* free all the sample buffers */ + FLUID_FREE(buffers->local_buf); + FLUID_FREE(buffers->left_buf); + FLUID_FREE(buffers->right_buf); + FLUID_FREE(buffers->fx_left_buf); + FLUID_FREE(buffers->fx_right_buf); +} + +void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer) +{ + int i; + + fluid_return_if_fail(mixer != NULL); + +#if ENABLE_MIXER_THREADS + delete_rvoice_mixer_threads(mixer); + + if(mixer->thread_ready) + { + delete_fluid_cond(mixer->thread_ready); + } + + if(mixer->wakeup_threads) + { + delete_fluid_cond(mixer->wakeup_threads); + } + + if(mixer->thread_ready_m) + { + delete_fluid_cond_mutex(mixer->thread_ready_m); + } + + if(mixer->wakeup_threads_m) + { + delete_fluid_cond_mutex(mixer->wakeup_threads_m); + } + +#endif + fluid_mixer_buffers_free(&mixer->buffers); + + + for(i = 0; i < mixer->fx_units; i++) + { + if(mixer->fx[i].reverb) + { + delete_fluid_revmodel(mixer->fx[i].reverb); + } + + if(mixer->fx[i].chorus) + { + delete_fluid_chorus(mixer->fx[i].chorus); + } + } + + FLUID_FREE(mixer->fx); + FLUID_FREE(mixer->rvoices); + FLUID_FREE(mixer); +} + +#ifdef LADSPA +/** + * Set a LADSPS fx instance to be used by the mixer and assign the mixer buffers + * as LADSPA host buffers with sensible names */ +void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer, + fluid_ladspa_fx_t *ladspa_fx, int audio_groups) +{ + mixer->ladspa_fx = ladspa_fx; + + if(ladspa_fx == NULL) + { + return; + } + else + { + fluid_real_t *main_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *main_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); + + fluid_real_t *rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + fluid_real_t *chor = rev; + + rev = &rev[SYNTH_REVERB_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + chor = &chor[SYNTH_CHORUS_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]; + + fluid_ladspa_add_host_ports(ladspa_fx, "Main:L", audio_groups, + main_l, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + + fluid_ladspa_add_host_ports(ladspa_fx, "Main:R", audio_groups, + main_r, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + + fluid_ladspa_add_host_ports(ladspa_fx, "Reverb:Send", 1, + rev, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + + fluid_ladspa_add_host_ports(ladspa_fx, "Chorus:Send", 1, + chor, + FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT); + } +} +#endif + +/** + * set one or more reverb shadow parameters for one fx group. + * These parameters will be returned if queried. + * (see fluid_rvoice_mixer_reverb_get_param()) + * + * @param mixer that contains all fx units. + * @param fx_group index of the fx group to which parameters must be set. + * must be in the range [-1..mixer->fx_units[. If -1 the changes are applied to + * all fx units. + * @param set Flags indicating which parameters should be set (#fluid_revmodel_set_t) + * @param values table of parameters values. + */ +void +fluid_rvoice_mixer_set_reverb_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]) +{ + fluid_mixer_fx_t *fx = mixer->fx; + int nr_units = mixer->fx_units; + + if(fx_group >= 0) /* apply parameters to this fx group only */ + { + nr_units = fx_group + 1; + } + else /* apply parameters to all fx groups */ + { + fx_group = 0; + } + + for(; fx_group < nr_units; fx_group++) + { + int param; + + for(param = 0; param < FLUID_REVERB_PARAM_LAST; param++) + { + if(set & FLUID_REVPARAM_TO_SETFLAG(param)) + { + fx[fx_group].reverb_param[param] = values[param]; + } + } + } +} + +/** + * get one reverb shadow parameter for one fx group. + * (see fluid_rvoice_mixer_set_reverb_full()) + * + * @param mixer that contains all fx group units. + * @param fx_group index of the fx group to get parameter from. + * must be in the range [0..mixer->fx_units[. + * @param enum indicating the parameter to get. + * FLUID_REVERB_ROOMSIZE, reverb room size value. + * FLUID_REVERB_DAMP, reverb damping value. + * FLUID_REVERB_WIDTH, reverb width value. + * FLUID_REVERB_LEVEL, reverb level value. + * @return value. + */ +double +fluid_rvoice_mixer_reverb_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param) +{ + return mixer->fx[fx_group].reverb_param[param]; +} + +/** + * set one or more chorus shadow parameters for one fx group. + * These parameters will be returned if queried. + * (see fluid_rvoice_mixer_chorus_get_param()) + * + * @param mixer that contains all fx units. + * @param fx_group index of the fx group to which parameters must be set. + * must be in the range [-1..mixer->fx_units[. If -1 the changes are applied + * to all fx group. + * Keep in mind, that the needed CPU time is proportional to 'nr'. + * @param set Flags indicating which parameters to set (#fluid_chorus_set_t) + * @param values table of pararameters. + */ +void +fluid_rvoice_mixer_set_chorus_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]) +{ + fluid_mixer_fx_t *fx = mixer->fx; + int nr_units = mixer->fx_units; + + if(fx_group >= 0) /* apply parameters to this group fx only */ + { + nr_units = fx_group + 1; + } + else /* apply parameters to all fx units*/ + { + fx_group = 0; + } + + for(; fx_group < nr_units; fx_group++) + { + int param; + + for(param = 0; param < FLUID_CHORUS_PARAM_LAST; param++) + { + if(set & FLUID_CHORPARAM_TO_SETFLAG(param)) + { + fx[fx_group].chorus_param[param] = values[param]; + } + } + } +} + +/** + * get one chorus shadow parameter for one fx group. + * (see fluid_rvoice_mixer_set_chorus_full()) + * + * @param mixer that contains all fx groups units. + * @param fx_group index of the fx group to get parameter from. + * must be in the range [0..mixer->fx_units[. + * @param get Flags indicating which parameter to get (#fluid_chorus_set_t) + * @return the parameter value (0.0 is returned if error) + */ +double +fluid_rvoice_mixer_chorus_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param) +{ + return mixer->fx[fx_group].chorus_param[param]; +} + +/* @deprecated: use fluid_rvoice_mixer_reverb_enable instead */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled) +{ + fluid_rvoice_mixer_t *mixer = obj; + int on = param[0].i; + + mixer->with_reverb = on; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reverb_enable) +{ + fluid_rvoice_mixer_t *mixer = obj; + int fx_group = param[0].i; /* reverb fx group index */ + int on = param[1].i; /* on/off */ + + int nr_units = mixer->fx_units; + + /* does on/off must be applied only to fx group at index fx_group ? */ + if(fx_group >= 0) + { + mixer->fx[fx_group].reverb_on = on; + } + /* on/off must be applied to all fx groups */ + else + { + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + mixer->fx[fx_group].reverb_on = on; + } + } + + /* set with_reverb if at least one reverb unit is on */ + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + on = mixer->fx[fx_group].reverb_on; + + if(on) + { + break; + } + } + + mixer->with_reverb = on; +} + +/* @deprecated: use fluid_rvoice_mixer_chorus_enable instead */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled) +{ + fluid_rvoice_mixer_t *mixer = obj; + int on = param[0].i; + mixer->with_chorus = on; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_chorus_enable) +{ + fluid_rvoice_mixer_t *mixer = obj; + int fx_group = param[0].i; /* chorus fx group index */ + int on = param[1].i; /* on/off */ + + int nr_units = mixer->fx_units; + + /* does on/off must be applied only to fx group at index fx_group ? */ + if(fx_group >= 0) + { + mixer->fx[fx_group].chorus_on = on; + } + /* on/off must be applied to all fx groups */ + else + { + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + mixer->fx[fx_group].chorus_on = on; + } + } + + /* set with_chorus if at least one chorus unit is on */ + for(fx_group = 0; fx_group < nr_units; fx_group++) + { + on = mixer->fx[fx_group].chorus_on; + + if(on) + { + break; + } + } + + mixer->with_chorus = on; +} + +void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on) +{ + mixer->mix_fx_to_out = on; +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params) +{ + fluid_rvoice_mixer_t *mixer = obj; + int i = param[0].i; + int set = param[1].i; + int nr = param[2].i; + fluid_real_t level = param[3].real; + fluid_real_t speed = param[4].real; + fluid_real_t depth_ms = param[5].real; + int type = param[6].i; + + int nr_units = mixer->fx_units; + + /* does parameters must be applied only to fx group i ? */ + if(i >= 0) + { + nr_units = i + 1; + } + else + { + i = 0; /* parameters must be applied to all fx groups */ + } + + while(i < nr_units) + { + fluid_chorus_set(mixer->fx[i++].chorus, set, nr, level, speed, depth_ms, type); + } +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params) +{ + fluid_rvoice_mixer_t *mixer = obj; + int i = param[0].i; /* fx group index */ + int set = param[1].i; + fluid_real_t roomsize = param[2].real; + fluid_real_t damping = param[3].real; + fluid_real_t width = param[4].real; + fluid_real_t level = param[5].real; + + int nr_units = mixer->fx_units; + + /* does parameters change should be applied only to fx group i ? */ + if(i >= 0) + { + nr_units = i + 1; /* parameters change must be applied to fx groups i */ + } + else + { + i = 0; /* parameters change must be applied to all fx groups */ + } + + while(i < nr_units) + { + fluid_revmodel_set(mixer->fx[i++].reverb, set, roomsize, damping, width, level); + } +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb) +{ + fluid_rvoice_mixer_t *mixer = obj; + int i; + + for(i = 0; i < mixer->fx_units; i++) + { + fluid_revmodel_reset(mixer->fx[i].reverb); + } +} + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus) +{ + fluid_rvoice_mixer_t *mixer = obj; + int i; + + for(i = 0; i < mixer->fx_units; i++) + { + fluid_chorus_reset(mixer->fx[i].chorus); + } +} + +int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **left, fluid_real_t **right) +{ + *left = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT); + *right = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT); + return mixer->buffers.buf_count; +} + +int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **fx_left, fluid_real_t **fx_right) +{ + *fx_left = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + *fx_right = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + return mixer->buffers.fx_buf_count; +} + +int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer) +{ + return FLUID_MIXER_MAX_BUFFERS_DEFAULT; +} + +#if WITH_PROFILING +int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer) +{ + return mixer->active_voices; +} +#endif + +#if ENABLE_MIXER_THREADS + +static FLUID_INLINE fluid_rvoice_t * +fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t *mixer) +{ + int i = fluid_atomic_int_exchange_and_add(&mixer->current_rvoice, 1); + + if(i >= mixer->active_voices) + { + return NULL; + } + + return mixer->rvoices[i]; +} + +#define THREAD_BUF_PROCESSING 0 +#define THREAD_BUF_VALID 1 +#define THREAD_BUF_NODATA 2 +#define THREAD_BUF_TERMINATE 3 + +/* Core thread function (processes voices in parallel to primary synthesis thread) */ +static fluid_thread_return_t +fluid_mixer_thread_func(void *data) +{ + fluid_mixer_buffers_t *buffers = data; + fluid_rvoice_mixer_t *mixer = buffers->mixer; + int hasValidData = 0; + FLUID_DECLARE_VLA(fluid_real_t *, bufs, buffers->buf_count * 2 + buffers->fx_buf_count * 2); + int bufcount = 0; + int current_blockcount = 0; + fluid_real_t *local_buf = fluid_align_ptr(buffers->local_buf, FLUID_DEFAULT_ALIGNMENT); + + while(!fluid_atomic_int_get(&mixer->threads_should_terminate)) + { + fluid_rvoice_t *rvoice = fluid_mixer_get_mt_rvoice(mixer); + + if(rvoice == NULL) + { + // if no voices: signal rendered buffers, sleep + fluid_atomic_int_set(&buffers->ready, hasValidData ? THREAD_BUF_VALID : THREAD_BUF_NODATA); + fluid_cond_mutex_lock(mixer->thread_ready_m); + fluid_cond_signal(mixer->thread_ready); + fluid_cond_mutex_unlock(mixer->thread_ready_m); + + fluid_cond_mutex_lock(mixer->wakeup_threads_m); + + while(1) + { + int j = fluid_atomic_int_get(&buffers->ready); + + if(j == THREAD_BUF_PROCESSING || j == THREAD_BUF_TERMINATE) + { + break; + } + + fluid_cond_wait(mixer->wakeup_threads, mixer->wakeup_threads_m); + } + + fluid_cond_mutex_unlock(mixer->wakeup_threads_m); + + hasValidData = 0; + } + else + { + // else: if buffer is not zeroed, zero buffers + if(!hasValidData) + { + // blockcount may have changed, since thread was put to sleep + current_blockcount = mixer->current_blockcount; + fluid_mixer_buffers_zero(buffers, current_blockcount); + bufcount = fluid_mixer_buffers_prepare(buffers, bufs); + hasValidData = 1; + } + + // then render voice to buffers + fluid_mixer_buffers_render_one(buffers, rvoice, bufs, bufcount, local_buf, current_blockcount); + } + } + + return FLUID_THREAD_RETURN_VALUE; +} + +static void +fluid_mixer_buffers_mix(fluid_mixer_buffers_t *dst, fluid_mixer_buffers_t *src, int current_blockcount) +{ + int i, j; + int scount = current_blockcount * FLUID_BUFSIZE; + int minbuf; + fluid_real_t *FLUID_RESTRICT base_src; + fluid_real_t *FLUID_RESTRICT base_dst; + + minbuf = dst->buf_count; + + if(minbuf > src->buf_count) + { + minbuf = src->buf_count; + } + + base_src = fluid_align_ptr(src->left_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } + } + + base_src = fluid_align_ptr(src->right_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } + } + + minbuf = dst->fx_buf_count; + + if(minbuf > src->fx_buf_count) + { + minbuf = src->fx_buf_count; + } + + base_src = fluid_align_ptr(src->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->fx_left_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } + } + + base_src = fluid_align_ptr(src->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + base_dst = fluid_align_ptr(dst->fx_right_buf, FLUID_DEFAULT_ALIGNMENT); + + for(i = 0; i < minbuf; i++) + { + #pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT) + + for(j = 0; j < scount; j++) + { + int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j; + base_dst[dsp_i] += base_src[dsp_i]; + } + } +} + + +/** + * Go through all threads and see if someone is finished for mixing + */ +static int +fluid_mixer_mix_in(fluid_rvoice_mixer_t *mixer, int extra_threads, int current_blockcount) +{ + int i, result, hasmixed; + + do + { + hasmixed = 0; + result = 0; + + for(i = 0; i < extra_threads; i++) + { + int j = fluid_atomic_int_get(&mixer->threads[i].ready); + + switch(j) + { + case THREAD_BUF_PROCESSING: + result = 1; + break; + + case THREAD_BUF_VALID: + fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_NODATA); + fluid_mixer_buffers_mix(&mixer->buffers, &mixer->threads[i], current_blockcount); + hasmixed = 1; + break; + } + } + } + while(hasmixed); + + return result; +} + +static void +fluid_render_loop_multithread(fluid_rvoice_mixer_t *mixer, int current_blockcount) +{ + int i, bufcount; + fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT); + + FLUID_DECLARE_VLA(fluid_real_t *, bufs, + mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2); + // How many threads should we start this time? + int extra_threads = mixer->active_voices / VOICES_PER_THREAD; + + if(extra_threads > mixer->thread_count) + { + extra_threads = mixer->thread_count; + } + + if(extra_threads == 0) + { + // No extra threads? No thread overhead! + fluid_render_loop_singlethread(mixer, current_blockcount); + return; + } + + bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs); + + // Prepare voice list + fluid_cond_mutex_lock(mixer->wakeup_threads_m); + fluid_atomic_int_set(&mixer->current_rvoice, 0); + + for(i = 0; i < extra_threads; i++) + { + fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_PROCESSING); + } + + // Signal threads to wake up + fluid_cond_broadcast(mixer->wakeup_threads); + fluid_cond_mutex_unlock(mixer->wakeup_threads_m); + + // If thread is finished, mix it in + while(fluid_mixer_mix_in(mixer, extra_threads, current_blockcount)) + { + // Otherwise get a voice and render it + fluid_rvoice_t *rvoice = fluid_mixer_get_mt_rvoice(mixer); + + if(rvoice != NULL) + { + fluid_profile_ref_var(prof_ref); + fluid_mixer_buffers_render_one(&mixer->buffers, rvoice, bufs, bufcount, local_buf, current_blockcount); + fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1, + current_blockcount * FLUID_BUFSIZE); + //test++; + } + else + { + // If no voices, wait for mixes. Make sure one is still processing to avoid deadlock + int is_processing = 0; + //waits++; + fluid_cond_mutex_lock(mixer->thread_ready_m); + + for(i = 0; i < extra_threads; i++) + { + if(fluid_atomic_int_get(&mixer->threads[i].ready) == + THREAD_BUF_PROCESSING) + { + is_processing = 1; + } + } + + if(is_processing) + { + fluid_cond_wait(mixer->thread_ready, mixer->thread_ready_m); + } + + fluid_cond_mutex_unlock(mixer->thread_ready_m); + } + } + + //FLUID_LOG(FLUID_DBG, "Blockcount: %d, mixed %d of %d voices myself, waits = %d", + // current_blockcount, test, mixer->active_voices, waits); +} + +static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer) +{ + int i; + + // if no threads have been created yet (e.g. because a previous error prevented creation of threads + // mutexes and condition variables), skip terminating threads + if(mixer->thread_count != 0) + { + fluid_atomic_int_set(&mixer->threads_should_terminate, 1); + // Signal threads to wake up + fluid_cond_mutex_lock(mixer->wakeup_threads_m); + + for(i = 0; i < mixer->thread_count; i++) + { + fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE); + } + + fluid_cond_broadcast(mixer->wakeup_threads); + fluid_cond_mutex_unlock(mixer->wakeup_threads_m); + + for(i = 0; i < mixer->thread_count; i++) + { + if(mixer->threads[i].thread) + { + fluid_thread_join(mixer->threads[i].thread); + delete_fluid_thread(mixer->threads[i].thread); + } + + fluid_mixer_buffers_free(&mixer->threads[i]); + } + } + + FLUID_FREE(mixer->threads); + mixer->thread_count = 0; + mixer->threads = NULL; +} + +/** + * Update amount of extra mixer threads. + * @param thread_count Number of extra mixer threads for multi-core rendering + * @param prio_level real-time prio level for the extra mixer threads + */ +static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level) +{ + char name[16]; + int i; + + // Kill all existing threads first + if(mixer->thread_count) + { + delete_rvoice_mixer_threads(mixer); + } + + if(thread_count == 0) + { + return FLUID_OK; + } + + // Now prepare the new threads + fluid_atomic_int_set(&mixer->threads_should_terminate, 0); + mixer->threads = FLUID_ARRAY(fluid_mixer_buffers_t, thread_count); + + if(mixer->threads == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + FLUID_MEMSET(mixer->threads, 0, thread_count * sizeof(fluid_mixer_buffers_t)); + mixer->thread_count = thread_count; + + for(i = 0; i < thread_count; i++) + { + fluid_mixer_buffers_t *b = &mixer->threads[i]; + + if(!fluid_mixer_buffers_init(b, mixer)) + { + return FLUID_FAILED; + } + + fluid_atomic_int_set(&b->ready, THREAD_BUF_NODATA); + FLUID_SNPRINTF(name, sizeof(name), "mixer%d", i); + b->thread = new_fluid_thread(name, fluid_mixer_thread_func, b, prio_level, 0); + + if(!b->thread) + { + return FLUID_FAILED; + } + } + + return FLUID_OK; +} +#endif + +/** + * Synthesize audio into buffers + * @param blockcount number of blocks to render, each having FLUID_BUFSIZE samples + * @return number of blocks rendered + */ +int +fluid_rvoice_mixer_render(fluid_rvoice_mixer_t *mixer, int blockcount) +{ + fluid_profile_ref_var(prof_ref); + + mixer->current_blockcount = blockcount; + + // Zero buffers + fluid_mixer_buffers_zero(&mixer->buffers, blockcount); + fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref, mixer->active_voices, + blockcount * FLUID_BUFSIZE); + +#if ENABLE_MIXER_THREADS + + if(mixer->thread_count > 0) + { + fluid_render_loop_multithread(mixer, blockcount); + } + else +#endif + { + fluid_render_loop_singlethread(mixer, blockcount); + } + + fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref, mixer->active_voices, + blockcount * FLUID_BUFSIZE); + + + // Process reverb & chorus + fluid_rvoice_mixer_process_fx(mixer, blockcount); + + // Call the callback and pack active voice array + fluid_rvoice_mixer_process_finished_voices(mixer); + + return blockcount; +} diff --git a/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_mixer.h b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_mixer.h new file mode 100644 index 000000000..63a456ce1 --- /dev/null +++ b/thirdparty/fluidsynth/src/rvoice/fluid_rvoice_mixer.h @@ -0,0 +1,88 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_RVOICE_MIXER_H +#define _FLUID_RVOICE_MIXER_H + +#include "fluidsynth_priv.h" +#include "fluid_rvoice.h" +#include "fluid_ladspa.h" + +typedef struct _fluid_rvoice_mixer_t fluid_rvoice_mixer_t; + +int fluid_rvoice_mixer_render(fluid_rvoice_mixer_t *mixer, int blockcount); +int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **left, fluid_real_t **right); +int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t *mixer, + fluid_real_t **fx_left, fluid_real_t **fx_right); +int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer); +#if WITH_PROFILING +int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer); +#endif +fluid_rvoice_mixer_t *new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units, + fluid_real_t sample_rate_max, fluid_real_t sample_rate, + fluid_rvoice_eventhandler_t *, int, int); + +void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *); + +void +fluid_rvoice_mixer_set_reverb_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]); + +double +fluid_rvoice_mixer_reverb_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param); +void +fluid_rvoice_mixer_set_chorus_full(const fluid_rvoice_mixer_t *mixer, + int fx_group, int set, const double values[]); +double +fluid_rvoice_mixer_chorus_get_param(const fluid_rvoice_mixer_t *mixer, + int fx_group, int param); + + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony); + +/* @deprecated */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled); +/* @deprecated */ +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reverb_enable); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_chorus_enable); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params); + +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb); +DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus); + + + +void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on); +#ifdef LADSPA +void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer, + fluid_ladspa_fx_t *ladspa_fx, int audio_groups); +#endif + +#endif + diff --git a/thirdparty/fluidsynth/src/sfloader/fluid_defsfont.c b/thirdparty/fluidsynth/src/sfloader/fluid_defsfont.c new file mode 100644 index 000000000..ef1f0fe71 --- /dev/null +++ b/thirdparty/fluidsynth/src/sfloader/fluid_defsfont.c @@ -0,0 +1,2338 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont file loading code borrowed from Smurf SoundFont Editor + * Copyright (C) 1999-2001 Josh Green + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#include "fluid_defsfont.h" +#include "fluid_sfont.h" +#include "fluid_sys.h" +#include "fluid_synth.h" +#include "fluid_samplecache.h" +#include "fluid_chan.h" + +/* EMU8k/10k hardware applies this factor to initial attenuation generator values set at preset and + * instrument level in a soundfont. We apply this factor when loading the generator values to stay + * compatible as most existing soundfonts expect exactly this (strange, non-standard) behaviour. */ +#define EMU_ATTENUATION_FACTOR (0.4f) + +/* Dynamic sample loading functions */ +static int pin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); +static int unpin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); +static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); +static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset); +static void unload_sample(fluid_sample_t *sample); +static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan); +static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason); +static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone); +static fluid_inst_t *find_inst_by_idx(fluid_defsfont_t *defsfont, int idx); + + +/*************************************************************** + * + * SFONT LOADER + */ + +/** + * Creates a default soundfont2 loader that can be used with fluid_synth_add_sfloader(). + * By default every synth instance has an initial default soundfont loader instance. + * Calling this function is usually only necessary to load a soundfont from memory, by providing custom callback functions via fluid_sfloader_set_callbacks(). + * + * @param settings A settings instance obtained by new_fluid_settings() + * @return A default soundfont2 loader struct + */ +fluid_sfloader_t *new_fluid_defsfloader(fluid_settings_t *settings) +{ + fluid_sfloader_t *loader; + fluid_return_val_if_fail(settings != NULL, NULL); + + loader = new_fluid_sfloader(fluid_defsfloader_load, delete_fluid_sfloader); + + if(loader == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + fluid_sfloader_set_data(loader, settings); + + return loader; +} + +fluid_sfont_t *fluid_defsfloader_load(fluid_sfloader_t *loader, const char *filename) +{ + fluid_defsfont_t *defsfont; + fluid_sfont_t *sfont; + + defsfont = new_fluid_defsfont(fluid_sfloader_get_data(loader)); + + if(defsfont == NULL) + { + return NULL; + } + + sfont = new_fluid_sfont(fluid_defsfont_sfont_get_name, + fluid_defsfont_sfont_get_preset, + fluid_defsfont_sfont_iteration_start, + fluid_defsfont_sfont_iteration_next, + fluid_defsfont_sfont_delete); + + if(sfont == NULL) + { + delete_fluid_defsfont(defsfont); + return NULL; + } + + fluid_sfont_set_data(sfont, defsfont); + + defsfont->sfont = sfont; + + if(fluid_defsfont_load(defsfont, &loader->file_callbacks, filename) == FLUID_FAILED) + { + fluid_defsfont_sfont_delete(sfont); + return NULL; + } + + return sfont; +} + + + +/*************************************************************** + * + * PUBLIC INTERFACE + */ + +int fluid_defsfont_sfont_delete(fluid_sfont_t *sfont) +{ + if(delete_fluid_defsfont(fluid_sfont_get_data(sfont)) != FLUID_OK) + { + return -1; + } + + delete_fluid_sfont(sfont); + return 0; +} + +const char *fluid_defsfont_sfont_get_name(fluid_sfont_t *sfont) +{ + return fluid_defsfont_get_name(fluid_sfont_get_data(sfont)); +} + +fluid_preset_t * +fluid_defsfont_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum) +{ + return fluid_defsfont_get_preset(fluid_sfont_get_data(sfont), bank, prenum); +} + +void fluid_defsfont_sfont_iteration_start(fluid_sfont_t *sfont) +{ + fluid_defsfont_iteration_start(fluid_sfont_get_data(sfont)); +} + +fluid_preset_t *fluid_defsfont_sfont_iteration_next(fluid_sfont_t *sfont) +{ + return fluid_defsfont_iteration_next(fluid_sfont_get_data(sfont)); +} + +void fluid_defpreset_preset_delete(fluid_preset_t *preset) +{ + fluid_defsfont_t *defsfont; + fluid_defpreset_t *defpreset; + + defsfont = fluid_sfont_get_data(preset->sfont); + defpreset = fluid_preset_get_data(preset); + + if(defsfont) + { + defsfont->preset = fluid_list_remove(defsfont->preset, defpreset); + } + + delete_fluid_defpreset(defpreset); + delete_fluid_preset(preset); +} + +const char *fluid_defpreset_preset_get_name(fluid_preset_t *preset) +{ + return fluid_defpreset_get_name(fluid_preset_get_data(preset)); +} + +int fluid_defpreset_preset_get_banknum(fluid_preset_t *preset) +{ + return fluid_defpreset_get_banknum(fluid_preset_get_data(preset)); +} + +int fluid_defpreset_preset_get_num(fluid_preset_t *preset) +{ + return fluid_defpreset_get_num(fluid_preset_get_data(preset)); +} + +int fluid_defpreset_preset_noteon(fluid_preset_t *preset, fluid_synth_t *synth, + int chan, int key, int vel) +{ + return fluid_defpreset_noteon(fluid_preset_get_data(preset), synth, chan, key, vel); +} + + +/*************************************************************** + * + * SFONT + */ + +/* + * new_fluid_defsfont + */ +fluid_defsfont_t *new_fluid_defsfont(fluid_settings_t *settings) +{ + fluid_defsfont_t *defsfont; + + defsfont = FLUID_NEW(fluid_defsfont_t); + + if(defsfont == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(defsfont, 0, sizeof(*defsfont)); + + fluid_settings_getint(settings, "synth.lock-memory", &defsfont->mlock); + fluid_settings_getint(settings, "synth.dynamic-sample-loading", &defsfont->dynamic_samples); + + return defsfont; +} + +/* + * delete_fluid_defsfont + */ +int delete_fluid_defsfont(fluid_defsfont_t *defsfont) +{ + fluid_list_t *list; + fluid_preset_t *preset; + fluid_sample_t *sample; + + fluid_return_val_if_fail(defsfont != NULL, FLUID_OK); + + /* If we use dynamic sample loading, make sure we unpin any + * pinned presets before removing this soundfont */ + if(defsfont->dynamic_samples) + { + for(list = defsfont->preset; list; list = fluid_list_next(list)) + { + preset = (fluid_preset_t *)fluid_list_get(list); + unpin_preset_samples(defsfont, preset); + } + } + + /* Check that no samples are currently used */ + for(list = defsfont->sample; list; list = fluid_list_next(list)) + { + sample = (fluid_sample_t *) fluid_list_get(list); + + if(sample->refcount != 0) + { + return FLUID_FAILED; + } + } + + if(defsfont->filename != NULL) + { + FLUID_FREE(defsfont->filename); + } + + for(list = defsfont->sample; list; list = fluid_list_next(list)) + { + sample = (fluid_sample_t *) fluid_list_get(list); + + /* If the sample data pointer is different to the sampledata chunk of + * the soundfont, then the sample has been loaded individually (SF3) + * and needs to be unloaded explicitly. This is safe even if using + * dynamic sample loading, as the sample_unload mechanism sets + * sample->data to NULL after unload. */ + if ((sample->data != NULL) && (sample->data != defsfont->sampledata)) + { + fluid_samplecache_unload(sample->data); + } + delete_fluid_sample(sample); + } + + if(defsfont->sample) + { + delete_fluid_list(defsfont->sample); + } + + if(defsfont->sampledata != NULL) + { + fluid_samplecache_unload(defsfont->sampledata); + } + + for(list = defsfont->preset; list; list = fluid_list_next(list)) + { + preset = (fluid_preset_t *)fluid_list_get(list); + fluid_defpreset_preset_delete(preset); + } + + delete_fluid_list(defsfont->preset); + + for(list = defsfont->inst; list; list = fluid_list_next(list)) + { + delete_fluid_inst(fluid_list_get(list)); + } + + delete_fluid_list(defsfont->inst); + + FLUID_FREE(defsfont); + return FLUID_OK; +} + +/* + * fluid_defsfont_get_name + */ +const char *fluid_defsfont_get_name(fluid_defsfont_t *defsfont) +{ + return defsfont->filename; +} + +/* Load sample data for a single sample from the Soundfont file. + * Returns FLUID_OK on error, otherwise FLUID_FAILED + */ +int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample) +{ + int num_samples; + + num_samples = fluid_samplecache_load( + sfdata, sample->source_start, sample->source_end, sample->sampletype, + defsfont->mlock, &sample->data, &sample->data24); + + if(num_samples < 0) + { + return FLUID_FAILED; + } + + if(num_samples == 0) + { + sample->start = sample->end = 0; + sample->loopstart = sample->loopend = 0; + return FLUID_OK; + } + + /* Ogg Vorbis samples already have loop pointers relative to the individual decompressed sample, + * but SF2 samples are relative to sample chunk start, so they need to be adjusted */ + if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) + { + sample->loopstart = sample->source_loopstart - sample->source_start; + sample->loopend = sample->source_loopend - sample->source_start; + } + + /* As we've just loaded an individual sample into it's own buffer, we need to adjust the start + * and end pointers */ + sample->start = 0; + sample->end = num_samples - 1; + + return FLUID_OK; +} + +/* Loads the sample data for all samples from the Soundfont file. For SF2 files, it loads the data in + * one large block. For SF3 files, each compressed sample gets loaded individually. + * Returns FLUID_OK on success, otherwise FLUID_FAILED + */ +int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata) +{ + fluid_list_t *list; + fluid_sample_t *sample; + int sf3_file = (sfdata->version.major == 3); + int sample_parsing_result = FLUID_OK; + int invalid_loops_were_sanitized = FALSE; + + /* For SF2 files, we load the sample data in one large block */ + if(!sf3_file) + { + int read_samples; + int num_samples = sfdata->samplesize / sizeof(short); + + read_samples = fluid_samplecache_load(sfdata, 0, num_samples - 1, 0, defsfont->mlock, + &defsfont->sampledata, &defsfont->sample24data); + + if(read_samples != num_samples) + { + FLUID_LOG(FLUID_ERR, "Attempted to read %d words of sample data, but got %d instead", + num_samples, read_samples); + return FLUID_FAILED; + } + } + + #pragma omp parallel + #pragma omp single + for(list = defsfont->sample; list; list = fluid_list_next(list)) + { + sample = fluid_list_get(list); + + if(sf3_file) + { + /* SF3 samples get loaded individually, as most (or all) of them are in Ogg Vorbis format + * anyway */ + #pragma omp task firstprivate(sample,sfdata,defsfont) shared(sample_parsing_result, invalid_loops_were_sanitized) default(none) + { + if(fluid_defsfont_load_sampledata(defsfont, sfdata, sample) == FLUID_FAILED) + { + #pragma omp critical + { + FLUID_LOG(FLUID_ERR, "Failed to load sample '%s'", sample->name); + sample_parsing_result = FLUID_FAILED; + } + } + else + { + int modified = fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short)); + if(modified) + { + #pragma omp critical + { + invalid_loops_were_sanitized = TRUE; + } + } + fluid_voice_optimize_sample(sample); + } + } + } + else + { + #pragma omp task firstprivate(sample, defsfont) shared(invalid_loops_were_sanitized) default(none) + { + int modified; + /* Data pointers of SF2 samples point to large sample data block loaded above */ + sample->data = defsfont->sampledata; + sample->data24 = defsfont->sample24data; + modified = fluid_sample_sanitize_loop(sample, defsfont->samplesize); + if(modified) + { + #pragma omp critical + { + invalid_loops_were_sanitized = TRUE; + } + } + fluid_voice_optimize_sample(sample); + } + } + } + + if(invalid_loops_were_sanitized) + { + FLUID_LOG(FLUID_WARN, + "Some invalid sample loops were sanitized! If you experience audible glitches, " + "start fluidsynth in verbose mode for detailed information."); + } + + return sample_parsing_result; +} + +/* + * fluid_defsfont_load + */ +int fluid_defsfont_load(fluid_defsfont_t *defsfont, const fluid_file_callbacks_t *fcbs, const char *file) +{ + SFData *sfdata; + fluid_list_t *p; + SFPreset *sfpreset; + SFSample *sfsample; + fluid_sample_t *sample; + fluid_defpreset_t *defpreset = NULL; + + defsfont->filename = FLUID_STRDUP(file); + + if(defsfont->filename == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + defsfont->fcbs = fcbs; + + /* The actual loading is done in the sfont and sffile files */ + sfdata = fluid_sffile_open(file, fcbs); + + if(sfdata == NULL) + { + /* error message already printed */ + return FLUID_FAILED; + } + + if(fluid_sffile_parse_presets(sfdata) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Couldn't parse presets from soundfont file"); + goto err_exit; + } + + /* Keep track of the position and size of the sample data because + it's loaded separately (and might be unoaded/reloaded in future) */ + defsfont->samplepos = sfdata->samplepos; + defsfont->samplesize = sfdata->samplesize; + defsfont->sample24pos = sfdata->sample24pos; + defsfont->sample24size = sfdata->sample24size; + + /* Create all samples from sample headers */ + p = sfdata->sample; + + while(p != NULL) + { + sfsample = (SFSample *)fluid_list_get(p); + + sample = new_fluid_sample(); + + if(sample == NULL) + { + goto err_exit; + } + + if(fluid_sample_import_sfont(sample, sfsample, defsfont) == FLUID_OK) + { + fluid_defsfont_add_sample(defsfont, sample); + } + else + { + delete_fluid_sample(sample); + sample = NULL; + } + + /* Store reference to FluidSynth sample in SFSample for later IZone fixups */ + sfsample->fluid_sample = sample; + + p = fluid_list_next(p); + } + + /* If dynamic sample loading is disabled, load all samples in the Soundfont */ + if(!defsfont->dynamic_samples) + { + if(fluid_defsfont_load_all_sampledata(defsfont, sfdata) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Unable to load all sample data"); + goto err_exit; + } + } + + /* Load all the presets */ + p = sfdata->preset; + + while(p != NULL) + { + sfpreset = (SFPreset *)fluid_list_get(p); + defpreset = new_fluid_defpreset(); + + if(defpreset == NULL) + { + goto err_exit; + } + + if(fluid_defpreset_import_sfont(defpreset, sfpreset, defsfont, sfdata) != FLUID_OK) + { + goto err_exit; + } + + if(fluid_defsfont_add_preset(defsfont, defpreset) == FLUID_FAILED) + { + goto err_exit; + } + + p = fluid_list_next(p); + } + + fluid_sffile_close(sfdata); + + return FLUID_OK; + +err_exit: + fluid_sffile_close(sfdata); + delete_fluid_defpreset(defpreset); + return FLUID_FAILED; +} + +/* fluid_defsfont_add_sample + * + * Add a sample to the SoundFont + */ +int fluid_defsfont_add_sample(fluid_defsfont_t *defsfont, fluid_sample_t *sample) +{ + defsfont->sample = fluid_list_prepend(defsfont->sample, sample); + return FLUID_OK; +} + +/* fluid_defsfont_add_preset + * + * Add a preset to the SoundFont + */ +int fluid_defsfont_add_preset(fluid_defsfont_t *defsfont, fluid_defpreset_t *defpreset) +{ + fluid_preset_t *preset; + + preset = new_fluid_preset(defsfont->sfont, + fluid_defpreset_preset_get_name, + fluid_defpreset_preset_get_banknum, + fluid_defpreset_preset_get_num, + fluid_defpreset_preset_noteon, + fluid_defpreset_preset_delete); + + if(preset == NULL) + { + return FLUID_FAILED; + } + + if(defsfont->dynamic_samples) + { + preset->notify = dynamic_samples_preset_notify; + } + + fluid_preset_set_data(preset, defpreset); + + defsfont->preset = fluid_list_append(defsfont->preset, preset); + + return FLUID_OK; +} + +/* + * fluid_defsfont_get_preset + */ +fluid_preset_t *fluid_defsfont_get_preset(fluid_defsfont_t *defsfont, int bank, int num) +{ + fluid_preset_t *preset; + fluid_list_t *list; + + for(list = defsfont->preset; list != NULL; list = fluid_list_next(list)) + { + preset = (fluid_preset_t *)fluid_list_get(list); + + if((fluid_preset_get_banknum(preset) == bank) && (fluid_preset_get_num(preset) == num)) + { + return preset; + } + } + + return NULL; +} + +/* + * fluid_defsfont_iteration_start + */ +void fluid_defsfont_iteration_start(fluid_defsfont_t *defsfont) +{ + defsfont->preset_iter_cur = defsfont->preset; +} + +/* + * fluid_defsfont_iteration_next + */ +fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t *defsfont) +{ + fluid_preset_t *preset = (fluid_preset_t *)fluid_list_get(defsfont->preset_iter_cur); + + defsfont->preset_iter_cur = fluid_list_next(defsfont->preset_iter_cur); + + return preset; +} + +/*************************************************************** + * + * PRESET + */ + +/* + * new_fluid_defpreset + */ +fluid_defpreset_t * +new_fluid_defpreset(void) +{ + fluid_defpreset_t *defpreset = FLUID_NEW(fluid_defpreset_t); + + if(defpreset == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + defpreset->next = NULL; + defpreset->name[0] = 0; + defpreset->bank = 0; + defpreset->num = 0; + defpreset->global_zone = NULL; + defpreset->zone = NULL; + defpreset->pinned = FALSE; + return defpreset; +} + +/* + * delete_fluid_defpreset + */ +void +delete_fluid_defpreset(fluid_defpreset_t *defpreset) +{ + fluid_preset_zone_t *zone; + + fluid_return_if_fail(defpreset != NULL); + + delete_fluid_preset_zone(defpreset->global_zone); + defpreset->global_zone = NULL; + + zone = defpreset->zone; + + while(zone != NULL) + { + defpreset->zone = zone->next; + delete_fluid_preset_zone(zone); + zone = defpreset->zone; + } + + FLUID_FREE(defpreset); +} + +int +fluid_defpreset_get_banknum(fluid_defpreset_t *defpreset) +{ + return defpreset->bank; +} + +int +fluid_defpreset_get_num(fluid_defpreset_t *defpreset) +{ + return defpreset->num; +} + +const char * +fluid_defpreset_get_name(fluid_defpreset_t *defpreset) +{ + return defpreset->name; +} + +/* + * fluid_defpreset_next + */ +fluid_defpreset_t * +fluid_defpreset_next(fluid_defpreset_t *defpreset) +{ + return defpreset->next; +} + +/* + * Adds global and local modulators list to the voice. This is done in 2 steps: + * - Step 1: Local modulators replace identic global modulators. + * - Step 2: global + local modulators are added to the voice using mode. + * + * Instrument zone list (local/global) must be added using FLUID_VOICE_OVERWRITE. + * Preset zone list (local/global) must be added using FLUID_VOICE_ADD. + * + * @param voice voice instance. + * @param global_mod global list of modulators. + * @param local_mod local list of modulators. + * @param mode Determines how to handle an existing identical modulator. + * #FLUID_VOICE_ADD to add (offset) the modulator amounts, + * #FLUID_VOICE_OVERWRITE to replace the modulator, +*/ +static void +fluid_defpreset_noteon_add_mod_to_voice(fluid_voice_t *voice, + fluid_mod_t *global_mod, fluid_mod_t *local_mod, + int mode) +{ + fluid_mod_t *mod; + /* list for 'sorting' global/local modulators */ + fluid_mod_t *mod_list[FLUID_NUM_MOD]; + int mod_list_count, i; + + /* identity_limit_count is the modulator upper limit number to handle with + * existing identical modulators. + * When identity_limit_count is below the actual number of modulators, this + * will restrict identity check to this upper limit, + * This is useful when we know by advance that there is no duplicate with + * modulators at index above this limit. This avoid wasting cpu cycles at + * noteon. + */ + int identity_limit_count; + + /* Step 1: Local modulators replace identic global modulators. */ + + /* local (instrument zone/preset zone), modulators: Put them all into a list. */ + mod_list_count = 0; + + while(local_mod) + { + /* As modulators number in local_mod list was limited to FLUID_NUM_MOD at + soundfont loading time (fluid_limit_mod_list()), here we don't need + to check if mod_list is full. + */ + mod_list[mod_list_count++] = local_mod; + local_mod = local_mod->next; + } + + /* global (instrument zone/preset zone), modulators. + * Replace modulators with the same definition in the global list: + * (Instrument zone: SF 2.01 page 69, 'bullet' 8) + * (Preset zone: SF 2.01 page 69, second-last bullet). + * + * mod_list contains local modulators. Now we know that there + * is no global modulator identic to another global modulator (this has + * been checked at soundfont loading time). So global modulators + * are only checked against local modulators number. + */ + + /* Restrict identity check to the number of local modulators */ + identity_limit_count = mod_list_count; + + while(global_mod) + { + /* 'Identical' global modulators are ignored. + * SF2.01 section 9.5.1 + * page 69, 'bullet' 3 defines 'identical'. */ + + for(i = 0; i < identity_limit_count; i++) + { + if(fluid_mod_test_identity(global_mod, mod_list[i])) + { + break; + } + } + + /* Finally add the new modulator to the list. */ + if(i >= identity_limit_count) + { + /* Although local_mod and global_mod lists was limited to + FLUID_NUM_MOD at soundfont loading time, it is possible that + local + global modulators exceeds FLUID_NUM_MOD. + So, checks if mod_list_count reaches the limit. + */ + if(mod_list_count >= FLUID_NUM_MOD) + { + /* mod_list is full, we silently forget this modulator and + next global modulators. When mod_list will be added to the + voice, a warning will be displayed if the voice list is full. + (see fluid_voice_add_mod_local()). + */ + break; + } + + mod_list[mod_list_count++] = global_mod; + } + + global_mod = global_mod->next; + } + + /* Step 2: global + local modulators are added to the voice using mode. */ + + /* + * mod_list contains local and global modulators, we know that: + * - there is no global modulator identic to another global modulator, + * - there is no local modulator identic to another local modulator, + * So these local/global modulators are only checked against + * actual number of voice modulators. + */ + + /* Restrict identity check to the actual number of voice modulators */ + /* Actual number of voice modulators : defaults + [instruments] */ + identity_limit_count = voice->mod_count; + + for(i = 0; i < mod_list_count; i++) + { + + mod = mod_list[i]; + /* in mode FLUID_VOICE_OVERWRITE disabled instruments modulators CANNOT be skipped. */ + /* in mode FLUID_VOICE_ADD disabled preset modulators can be skipped. */ + + if((mode == FLUID_VOICE_OVERWRITE) || (mod->amount != 0)) + { + /* Instrument modulators -supersede- existing (default) modulators. + SF 2.01 page 69, 'bullet' 6 */ + + /* Preset modulators -add- to existing instrument modulators. + SF2.01 page 70 first bullet on page */ + fluid_voice_add_mod_local(voice, mod, mode, identity_limit_count); + } + } +} + +/* + * fluid_defpreset_noteon + */ +int +fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int chan, int key, int vel) +{ + fluid_preset_zone_t *preset_zone, *global_preset_zone; + fluid_inst_t *inst; + fluid_inst_zone_t *inst_zone, *global_inst_zone; + fluid_voice_zone_t *voice_zone; + fluid_list_t *list; + fluid_voice_t *voice; + int tuned_key; + int i; + + /* For detuned channels it might be better to use another key for Soundfont sample selection + * giving better approximations for the pitch than the original key. + * Example: play key 60 on 6370 Hz => use tuned key 64 for sample selection + * + * This feature is only enabled for melodic channels. + * For drum channels we always select Soundfont samples by key numbers. + */ + + if(synth->channel[chan]->channel_type == CHANNEL_TYPE_MELODIC) + { + tuned_key = (int)(fluid_channel_get_key_pitch(synth->channel[chan], key) / 100.0f + 0.5f); + } + else + { + tuned_key = key; + } + + global_preset_zone = fluid_defpreset_get_global_zone(defpreset); + + /* run thru all the zones of this preset */ + preset_zone = fluid_defpreset_get_zone(defpreset); + + while(preset_zone != NULL) + { + + /* check if the note falls into the key and velocity range of this + preset */ + if(fluid_zone_inside_range(&preset_zone->range, tuned_key, vel)) + { + + inst = fluid_preset_zone_get_inst(preset_zone); + global_inst_zone = fluid_inst_get_global_zone(inst); + + /* run thru all the zones of this instrument that could start a voice */ + for(list = preset_zone->voice_zone; list != NULL; list = fluid_list_next(list)) + { + voice_zone = fluid_list_get(list); + + /* check if the instrument zone is ignored and the note falls into + the key and velocity range of this instrument zone. + An instrument zone must be ignored when its voice is already running + played by a legato passage (see fluid_synth_noteon_monopoly_legato()) */ + if(fluid_zone_inside_range(&voice_zone->range, tuned_key, vel)) + { + + inst_zone = voice_zone->inst_zone; + + /* this is a good zone. allocate a new synthesis process and initialize it */ + voice = fluid_synth_alloc_voice_LOCAL(synth, inst_zone->sample, chan, key, vel, &voice_zone->range); + + if(voice == NULL) + { + return FLUID_FAILED; + } + + + /* Instrument level, generators */ + + for(i = 0; i < GEN_LAST; i++) + { + + /* SF 2.01 section 9.4 'bullet' 4: + * + * A generator in a local instrument zone supersedes a + * global instrument zone generator. Both cases supersede + * the default generator -> voice_gen_set */ + + if(inst_zone->gen[i].flags) + { + fluid_voice_gen_set(voice, i, inst_zone->gen[i].val); + + } + else if((global_inst_zone != NULL) && (global_inst_zone->gen[i].flags)) + { + fluid_voice_gen_set(voice, i, global_inst_zone->gen[i].val); + + } + else + { + /* The generator has not been defined in this instrument. + * Do nothing, leave it at the default. + */ + } + + } /* for all generators */ + + /* Adds instrument zone modulators (global and local) to the voice.*/ + fluid_defpreset_noteon_add_mod_to_voice(voice, + /* global instrument modulators */ + global_inst_zone ? global_inst_zone->mod : NULL, + inst_zone->mod, /* local instrument modulators */ + FLUID_VOICE_OVERWRITE); /* mode */ + + /* Preset level, generators */ + + for(i = 0; i < GEN_LAST; i++) + { + + /* SF 2.01 section 8.5 page 58: If some generators are + encountered at preset level, they should be ignored. + However this check is not necessary when the soundfont + loader has ignored invalid preset generators. + Actually load_pgen()has ignored these invalid preset + generators: + GEN_STARTADDROFS, GEN_ENDADDROFS, + GEN_STARTLOOPADDROFS, GEN_ENDLOOPADDROFS, + GEN_STARTADDRCOARSEOFS,GEN_ENDADDRCOARSEOFS, + GEN_STARTLOOPADDRCOARSEOFS, + GEN_KEYNUM, GEN_VELOCITY, + GEN_ENDLOOPADDRCOARSEOFS, + GEN_SAMPLEMODE, GEN_EXCLUSIVECLASS,GEN_OVERRIDEROOTKEY + */ + + /* SF 2.01 section 9.4 'bullet' 9: A generator in a + * local preset zone supersedes a global preset zone + * generator. The effect is -added- to the destination + * summing node -> voice_gen_incr */ + + if(preset_zone->gen[i].flags) + { + fluid_voice_gen_incr(voice, i, preset_zone->gen[i].val); + } + else if((global_preset_zone != NULL) && global_preset_zone->gen[i].flags) + { + fluid_voice_gen_incr(voice, i, global_preset_zone->gen[i].val); + } + else + { + /* The generator has not been defined in this preset + * Do nothing, leave it unchanged. + */ + } + } /* for all generators */ + + /* Adds preset zone modulators (global and local) to the voice.*/ + fluid_defpreset_noteon_add_mod_to_voice(voice, + /* global preset modulators */ + global_preset_zone ? global_preset_zone->mod : NULL, + preset_zone->mod, /* local preset modulators */ + FLUID_VOICE_ADD); /* mode */ + + /* add the synthesis process to the synthesis loop. */ + fluid_synth_start_voice(synth, voice); + + /* Store the ID of the first voice that was created by this noteon event. + * Exclusive class may only terminate older voices. + * That avoids killing voices, which have just been created. + * (a noteon event can create several voice processes with the same exclusive + * class - for example when using stereo samples) + */ + } + } + } + + preset_zone = fluid_preset_zone_next(preset_zone); + } + + return FLUID_OK; +} + +/* + * fluid_defpreset_set_global_zone + */ +int +fluid_defpreset_set_global_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone) +{ + defpreset->global_zone = zone; + return FLUID_OK; +} + +/* + * fluid_defpreset_import_sfont + */ +int +fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, + SFPreset *sfpreset, + fluid_defsfont_t *defsfont, + SFData *sfdata) +{ + fluid_list_t *p; + SFZone *sfzone; + fluid_preset_zone_t *zone; + int count; + char zone_name[256]; + + if(FLUID_STRLEN(sfpreset->name) > 0) + { + FLUID_STRCPY(defpreset->name, sfpreset->name); + } + else + { + FLUID_SNPRINTF(defpreset->name, sizeof(defpreset->name), "Bank%d,Pre%d", sfpreset->bank, sfpreset->prenum); + } + + defpreset->bank = sfpreset->bank; + defpreset->num = sfpreset->prenum; + p = sfpreset->zone; + count = 0; + + while(p != NULL) + { + sfzone = (SFZone *)fluid_list_get(p); + FLUID_SNPRINTF(zone_name, sizeof(zone_name), "pz:%s/%d", defpreset->name, count); + zone = new_fluid_preset_zone(zone_name); + + if(zone == NULL) + { + return FLUID_FAILED; + } + + if(fluid_preset_zone_import_sfont(zone, sfzone, defsfont, sfdata) != FLUID_OK) + { + delete_fluid_preset_zone(zone); + return FLUID_FAILED; + } + + if((count == 0) && (fluid_preset_zone_get_inst(zone) == NULL)) + { + fluid_defpreset_set_global_zone(defpreset, zone); + } + else if(fluid_defpreset_add_zone(defpreset, zone) != FLUID_OK) + { + return FLUID_FAILED; + } + + p = fluid_list_next(p); + count++; + } + + return FLUID_OK; +} + +/* + * fluid_defpreset_add_zone + */ +int +fluid_defpreset_add_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone) +{ + if(defpreset->zone == NULL) + { + zone->next = NULL; + defpreset->zone = zone; + } + else + { + zone->next = defpreset->zone; + defpreset->zone = zone; + } + + return FLUID_OK; +} + +/* + * fluid_defpreset_get_zone + */ +fluid_preset_zone_t * +fluid_defpreset_get_zone(fluid_defpreset_t *defpreset) +{ + return defpreset->zone; +} + +/* + * fluid_defpreset_get_global_zone + */ +fluid_preset_zone_t * +fluid_defpreset_get_global_zone(fluid_defpreset_t *defpreset) +{ + return defpreset->global_zone; +} + +/*************************************************************** + * + * PRESET_ZONE + */ + +/* + * fluid_preset_zone_next + */ +fluid_preset_zone_t * +fluid_preset_zone_next(fluid_preset_zone_t *zone) +{ + return zone->next; +} + +/* + * new_fluid_preset_zone + */ +fluid_preset_zone_t * +new_fluid_preset_zone(char *name) +{ + fluid_preset_zone_t *zone = NULL; + zone = FLUID_NEW(fluid_preset_zone_t); + + if(zone == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + zone->next = NULL; + zone->voice_zone = NULL; + zone->name = FLUID_STRDUP(name); + + if(zone->name == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(zone); + return NULL; + } + + zone->inst = NULL; + zone->range.keylo = 0; + zone->range.keyhi = 128; + zone->range.vello = 0; + zone->range.velhi = 128; + zone->range.ignore = FALSE; + + /* Flag all generators as unused (default, they will be set when they are found + * in the sound font). + * This also sets the generator values to default, but that is of no concern here.*/ + fluid_gen_init(&zone->gen[0], NULL); + zone->mod = NULL; /* list of modulators */ + return zone; +} + +/* + * delete list of modulators. + */ +void delete_fluid_list_mod(fluid_mod_t *mod) +{ + fluid_mod_t *tmp; + + while(mod) /* delete the modulators */ + { + tmp = mod; + mod = mod->next; + delete_fluid_mod(tmp); + } +} + +/* + * delete_fluid_preset_zone + */ +void +delete_fluid_preset_zone(fluid_preset_zone_t *zone) +{ + fluid_list_t *list; + + fluid_return_if_fail(zone != NULL); + + delete_fluid_list_mod(zone->mod); + + for(list = zone->voice_zone; list != NULL; list = fluid_list_next(list)) + { + FLUID_FREE(fluid_list_get(list)); + } + + delete_fluid_list(zone->voice_zone); + + FLUID_FREE(zone->name); + FLUID_FREE(zone); +} + +static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone) +{ + fluid_inst_zone_t *inst_zone; + fluid_sample_t *sample; + fluid_voice_zone_t *voice_zone; + fluid_zone_range_t *irange; + fluid_zone_range_t *prange = &preset_zone->range; + + fluid_return_val_if_fail(preset_zone->inst != NULL, FLUID_FAILED); + + inst_zone = fluid_inst_get_zone(preset_zone->inst); + + while(inst_zone != NULL) + { + + /* We only create voice ranges for zones that could actually start a voice, + * i.e. that have a sample and don't point to ROM */ + sample = fluid_inst_zone_get_sample(inst_zone); + + if((sample == NULL) || fluid_sample_in_rom(sample)) + { + inst_zone = fluid_inst_zone_next(inst_zone); + continue; + } + + voice_zone = FLUID_NEW(fluid_voice_zone_t); + + if(voice_zone == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + voice_zone->inst_zone = inst_zone; + + irange = &inst_zone->range; + + voice_zone->range.keylo = (prange->keylo > irange->keylo) ? prange->keylo : irange->keylo; + voice_zone->range.keyhi = (prange->keyhi < irange->keyhi) ? prange->keyhi : irange->keyhi; + voice_zone->range.vello = (prange->vello > irange->vello) ? prange->vello : irange->vello; + voice_zone->range.velhi = (prange->velhi < irange->velhi) ? prange->velhi : irange->velhi; + voice_zone->range.ignore = FALSE; + + preset_zone->voice_zone = fluid_list_append(preset_zone->voice_zone, voice_zone); + + inst_zone = fluid_inst_zone_next(inst_zone); + } + + return FLUID_OK; +} + +/** + * Checks if modulator mod is identic to another modulator in the list + * (specs SF 2.0X 7.4, 7.8). + * @param mod, modulator list. + * @param name, if not NULL, pointer on a string displayed as warning. + * @return TRUE if mod is identic to another modulator, FALSE otherwise. + */ +static int +fluid_zone_is_mod_identic(fluid_mod_t *mod, char *name) +{ + fluid_mod_t *next = mod->next; + + while(next) + { + /* is mod identic to next ? */ + if(fluid_mod_test_identity(mod, next)) + { + if(name) + { + FLUID_LOG(FLUID_WARN, "Ignoring identic modulator %s", name); + } + + return TRUE; + } + + next = next->next; + } + + return FALSE; +} + +/** + * Limits the number of modulators in a modulator list. + * This is appropriate to internal synthesizer modulators tables + * which have a fixed size (FLUID_NUM_MOD). + * + * @param zone_name, zone name + * @param list_mod, address of pointer on modulator list. + */ +static void fluid_limit_mod_list(char *zone_name, fluid_mod_t **list_mod) +{ + int mod_idx = 0; /* modulator index */ + fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */ + fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */ + + while(mod) + { + if((mod_idx + 1) > FLUID_NUM_MOD) + { + /* truncation of list_mod */ + if(mod_idx) + { + prev_mod->next = NULL; + } + else + { + *list_mod = NULL; + } + + delete_fluid_list_mod(mod); + FLUID_LOG(FLUID_WARN, "%s, modulators count limited to %d", zone_name, + FLUID_NUM_MOD); + break; + } + + mod_idx++; + prev_mod = mod; + mod = mod->next; + } +} + +/** + * Checks and remove invalid modulators from a zone modulators list. + * - checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1). + * - checks identic modulators in the list (specs SF 2.01 7.4, 7.8). + * @param zone_name, zone name. + * @param list_mod, address of pointer on modulators list. + */ +static void +fluid_zone_check_mod(char *zone_name, fluid_mod_t **list_mod) +{ + fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */ + fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */ + int mod_idx = 0; /* modulator index */ + + while(mod) + { + char zone_mod_name[256]; + fluid_mod_t *next = mod->next; + + /* prepare modulator name: zonename/#modulator */ + FLUID_SNPRINTF(zone_mod_name, sizeof(zone_mod_name), "%s/mod%d", zone_name, mod_idx); + + /* has mod invalid sources ? */ + if(!fluid_mod_check_sources(mod, zone_mod_name) + /* or is mod identic to any following modulator ? */ + || fluid_zone_is_mod_identic(mod, zone_mod_name)) + { + /* the modulator is useless so we remove it */ + if(prev_mod) + { + prev_mod->next = next; + } + else + { + *list_mod = next; + } + + delete_fluid_mod(mod); /* freeing */ + } + else + { + prev_mod = mod; + } + + mod = next; + mod_idx++; + } + + /* limits the size of modulators list */ + fluid_limit_mod_list(zone_name, list_mod); +} + +/* + * fluid_zone_gen_import_sfont + * Imports generators from sfzone to gen and range. + * @param gen, pointer on destination generators table. + * @param range, pointer on destination range generators. + * @param sfzone, pointer on soundfont zone generators. + */ +static void +fluid_zone_gen_import_sfont(fluid_gen_t *gen, fluid_zone_range_t *range, SFZone *sfzone) +{ + fluid_list_t *r; + SFGen *sfgen; + + for(r = sfzone->gen; r != NULL;) + { + sfgen = (SFGen *)fluid_list_get(r); + + switch(sfgen->id) + { + case GEN_KEYRANGE: + range->keylo = sfgen->amount.range.lo; + range->keyhi = sfgen->amount.range.hi; + break; + + case GEN_VELRANGE: + range->vello = sfgen->amount.range.lo; + range->velhi = sfgen->amount.range.hi; + break; + + case GEN_ATTENUATION: + /* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at + * preset and instrument level */ + gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR; + gen[sfgen->id].flags = GEN_SET; + break; + + case GEN_INSTRUMENT: + case GEN_SAMPLEID: + gen[sfgen->id].val = (fluid_real_t) sfgen->amount.uword; + gen[sfgen->id].flags = GEN_SET; + break; + + default: + gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword; + gen[sfgen->id].flags = GEN_SET; + break; + } + + r = fluid_list_next(r); + } +} + +/* + * fluid_zone_mod_source_import_sfont + * Imports source information from sf_source to src and flags. + * @param src, pointer on destination modulator source. + * @param flags, pointer on destination modulator flags. + * @param sf_source, soundfont modulator source. + * @return return TRUE if success, FALSE if source type is unknown. + */ +static int +fluid_zone_mod_source_import_sfont(unsigned char *src, unsigned char *flags, unsigned short sf_source) +{ + int type; + unsigned char flags_dest; /* destination flags */ + + /* sources */ + *src = sf_source & 127; /* index of source, seven-bit value, SF2.01 section 8.2, page 50 */ + + /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/ + flags_dest = 0; + + if(sf_source & (1 << 7)) + { + flags_dest |= FLUID_MOD_CC; + } + else + { + flags_dest |= FLUID_MOD_GC; + } + + /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/ + if(sf_source & (1 << 8)) + { + flags_dest |= FLUID_MOD_NEGATIVE; + } + else + { + flags_dest |= FLUID_MOD_POSITIVE; + } + + /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/ + if(sf_source & (1 << 9)) + { + flags_dest |= FLUID_MOD_BIPOLAR; + } + else + { + flags_dest |= FLUID_MOD_UNIPOLAR; + } + + /* modulator source types: SF2.01 section 8.2.1 page 52 */ + type = sf_source >> 10; + type &= 63; /* type is a 6-bit value */ + + if(type == 0) + { + flags_dest |= FLUID_MOD_LINEAR; + } + else if(type == 1) + { + flags_dest |= FLUID_MOD_CONCAVE; + } + else if(type == 2) + { + flags_dest |= FLUID_MOD_CONVEX; + } + else if(type == 3) + { + flags_dest |= FLUID_MOD_SWITCH; + } + else + { + *flags = flags_dest; + /* This shouldn't happen - unknown type! */ + return FALSE; + } + + *flags = flags_dest; + return TRUE; +} + +/* + * fluid_zone_mod_import_sfont + * Imports modulators from sfzone to modulators list mod. + * @param zone_name, zone name. + * @param mod, address of pointer on modulators list to return. + * @param sfzone, pointer on soundfont zone. + * @return FLUID_OK if success, FLUID_FAILED otherwise. + */ +static int +fluid_zone_mod_import_sfont(char *zone_name, fluid_mod_t **mod, SFZone *sfzone) +{ + fluid_list_t *r; + int count; + + /* Import the modulators (only SF2.1 and higher) */ + for(count = 0, r = sfzone->mod; r != NULL; count++) + { + + SFMod *mod_src = (SFMod *)fluid_list_get(r); + fluid_mod_t *mod_dest = new_fluid_mod(); + + if(mod_dest == NULL) + { + return FLUID_FAILED; + } + + mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/ + + /* *** Amount *** */ + mod_dest->amount = mod_src->amount; + + /* *** Source *** */ + if(!fluid_zone_mod_source_import_sfont(&mod_dest->src1, &mod_dest->flags1, mod_src->src)) + { + /* This shouldn't happen - unknown type! + * Deactivate the modulator by setting the amount to 0. */ + mod_dest->amount = 0; + } + + /* Note: When primary source input (src1) is set to General Controller 'No Controller', + output will be forced to 0.0 at synthesis time (see fluid_mod_get_value()). + That means that the minimum value of the modulator will be always 0.0. + We need to force amount value to 0 to ensure a correct evaluation of the minimum + value later (see fluid_voice_get_lower_boundary_for_attenuation()). + */ + if(((mod_dest->flags1 & FLUID_MOD_CC) == FLUID_MOD_GC) && + (mod_dest->src1 == FLUID_MOD_NONE)) + { + mod_dest->amount = 0; + } + + /* *** Dest *** */ + mod_dest->dest = mod_src->dest; /* index of controlled generator */ + + /* *** Amount source *** */ + if(!fluid_zone_mod_source_import_sfont(&mod_dest->src2, &mod_dest->flags2, mod_src->amtsrc)) + { + /* This shouldn't happen - unknown type! + * Deactivate the modulator by setting the amount to 0. */ + mod_dest->amount = 0; + } + /* Note: When secondary source input (src2) is set to General Controller 'No Controller', + output will be forced to +1.0 at synthesis time (see fluid_mod_get_value()). + That means that this source will behave unipolar only. We need to force the + unipolar flag to ensure to ensure a correct evaluation of the minimum + value later (see fluid_voice_get_lower_boundary_for_attenuation()). + */ + if(((mod_dest->flags2 & FLUID_MOD_CC) == FLUID_MOD_GC) && + (mod_dest->src2 == FLUID_MOD_NONE)) + { + mod_dest->flags2 &= ~FLUID_MOD_BIPOLAR; + } + + /* *** Transform *** */ + /* SF2.01 only uses the 'linear' transform (0). + * Deactivate the modulator by setting the amount to 0 in any other case. + */ + if(mod_src->trans != 0) + { + mod_dest->amount = 0; + } + + /* Store the new modulator in the zone The order of modulators + * will make a difference, at least in an instrument context: The + * second modulator overwrites the first one, if they only differ + * in amount. */ + if(count == 0) + { + *mod = mod_dest; + } + else + { + fluid_mod_t *last_mod = *mod; + + /* Find the end of the list */ + while(last_mod->next != NULL) + { + last_mod = last_mod->next; + } + + last_mod->next = mod_dest; + } + + r = fluid_list_next(r); + } /* foreach modulator */ + + /* checks and removes invalid modulators in modulators list*/ + fluid_zone_check_mod(zone_name, mod); + return FLUID_OK; +} + +/* + * fluid_preset_zone_import_sfont + */ +int +fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defsfont, SFData *sfdata) +{ + /* import the generators */ + fluid_zone_gen_import_sfont(zone->gen, &zone->range, sfzone); + + if(zone->gen[GEN_INSTRUMENT].flags == GEN_SET) + { + int inst_idx = (int) zone->gen[GEN_INSTRUMENT].val; + + zone->inst = find_inst_by_idx(defsfont, inst_idx); + + if(zone->inst == NULL) + { + zone->inst = fluid_inst_import_sfont(inst_idx, defsfont, sfdata); + } + + if(zone->inst == NULL) + { + + FLUID_LOG(FLUID_ERR, "Preset zone %s: Invalid instrument reference", + zone->name); + return FLUID_FAILED; + } + + if(fluid_preset_zone_create_voice_zones(zone) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + /* We don't need this generator anymore */ + zone->gen[GEN_INSTRUMENT].flags = GEN_UNUSED; + } + + /* Import the modulators (only SF2.1 and higher) */ + return fluid_zone_mod_import_sfont(zone->name, &zone->mod, sfzone); +} + +/* + * fluid_preset_zone_get_inst + */ +fluid_inst_t * +fluid_preset_zone_get_inst(fluid_preset_zone_t *zone) +{ + return zone->inst; +} + + +/*************************************************************** + * + * INST + */ + +/* + * new_fluid_inst + */ +fluid_inst_t * +new_fluid_inst() +{ + fluid_inst_t *inst = FLUID_NEW(fluid_inst_t); + + if(inst == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + inst->name[0] = 0; + inst->global_zone = NULL; + inst->zone = NULL; + return inst; +} + +/* + * delete_fluid_inst + */ +void +delete_fluid_inst(fluid_inst_t *inst) +{ + fluid_inst_zone_t *zone; + + fluid_return_if_fail(inst != NULL); + + delete_fluid_inst_zone(inst->global_zone); + inst->global_zone = NULL; + + zone = inst->zone; + + while(zone != NULL) + { + inst->zone = zone->next; + delete_fluid_inst_zone(zone); + zone = inst->zone; + } + + FLUID_FREE(inst); +} + +/* + * fluid_inst_set_global_zone + */ +int +fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone) +{ + inst->global_zone = zone; + return FLUID_OK; +} + +/* + * fluid_inst_import_sfont + */ +fluid_inst_t * +fluid_inst_import_sfont(int inst_idx, fluid_defsfont_t *defsfont, SFData *sfdata) +{ + fluid_list_t *p; + fluid_list_t *inst_list; + fluid_inst_t *inst; + SFZone *sfzone; + SFInst *sfinst; + fluid_inst_zone_t *inst_zone; + char zone_name[256]; + int count; + + for (inst_list = sfdata->inst; inst_list; inst_list = fluid_list_next(inst_list)) + { + sfinst = fluid_list_get(inst_list); + if (sfinst->idx == inst_idx) + { + break; + } + } + if (inst_list == NULL) + { + return NULL; + } + + inst = (fluid_inst_t *) new_fluid_inst(); + + if(inst == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + inst->source_idx = sfinst->idx; + + p = sfinst->zone; + + if(FLUID_STRLEN(sfinst->name) > 0) + { + FLUID_STRCPY(inst->name, sfinst->name); + } + else + { + FLUID_STRCPY(inst->name, ""); + } + + count = 0; + + while(p != NULL) + { + + sfzone = (SFZone *)fluid_list_get(p); + /* instrument zone name */ + FLUID_SNPRINTF(zone_name, sizeof(zone_name), "iz:%s/%d", inst->name, count); + + inst_zone = new_fluid_inst_zone(zone_name); + + if(inst_zone == NULL) + { + return NULL; + } + + if(fluid_inst_zone_import_sfont(inst_zone, sfzone, defsfont, sfdata) != FLUID_OK) + { + delete_fluid_inst_zone(inst_zone); + return NULL; + } + + if((count == 0) && (fluid_inst_zone_get_sample(inst_zone) == NULL)) + { + fluid_inst_set_global_zone(inst, inst_zone); + + } + else if(fluid_inst_add_zone(inst, inst_zone) != FLUID_OK) + { + return NULL; + } + + p = fluid_list_next(p); + count++; + } + + defsfont->inst = fluid_list_append(defsfont->inst, inst); + return inst; +} + +/* + * fluid_inst_add_zone + */ +int +fluid_inst_add_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone) +{ + if(inst->zone == NULL) + { + zone->next = NULL; + inst->zone = zone; + } + else + { + zone->next = inst->zone; + inst->zone = zone; + } + + return FLUID_OK; +} + +/* + * fluid_inst_get_zone + */ +fluid_inst_zone_t * +fluid_inst_get_zone(fluid_inst_t *inst) +{ + return inst->zone; +} + +/* + * fluid_inst_get_global_zone + */ +fluid_inst_zone_t * +fluid_inst_get_global_zone(fluid_inst_t *inst) +{ + return inst->global_zone; +} + +/*************************************************************** + * + * INST_ZONE + */ + +/* + * new_fluid_inst_zone + */ +fluid_inst_zone_t * +new_fluid_inst_zone(char *name) +{ + fluid_inst_zone_t *zone = NULL; + zone = FLUID_NEW(fluid_inst_zone_t); + + if(zone == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + zone->next = NULL; + zone->name = FLUID_STRDUP(name); + + if(zone->name == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(zone); + return NULL; + } + + zone->sample = NULL; + zone->range.keylo = 0; + zone->range.keyhi = 128; + zone->range.vello = 0; + zone->range.velhi = 128; + zone->range.ignore = FALSE; + /* Flag the generators as unused. + * This also sets the generator values to default, but they will be overwritten anyway, if used.*/ + fluid_gen_init(&zone->gen[0], NULL); + zone->mod = NULL; /* list of modulators */ + return zone; +} + +/* + * delete_fluid_inst_zone + */ +void +delete_fluid_inst_zone(fluid_inst_zone_t *zone) +{ + fluid_return_if_fail(zone != NULL); + + delete_fluid_list_mod(zone->mod); + + FLUID_FREE(zone->name); + FLUID_FREE(zone); +} + +/* + * fluid_inst_zone_next + */ +fluid_inst_zone_t * +fluid_inst_zone_next(fluid_inst_zone_t *zone) +{ + return zone->next; +} + +/* + * fluid_inst_zone_import_sfont + */ +int +fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont, + SFData *sfdata) +{ + /* import the generators */ + fluid_zone_gen_import_sfont(inst_zone->gen, &inst_zone->range, sfzone); + + /* FIXME */ + /* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */ + /* FLUID_LOG(FLUID_DBG, "ExclusiveClass=%d\n", (int) zone->gen[GEN_EXCLUSIVECLASS].val); */ + /* } */ + + if (inst_zone->gen[GEN_SAMPLEID].flags == GEN_SET) + { + fluid_list_t *list; + SFSample *sfsample; + int sample_idx = (int) inst_zone->gen[GEN_SAMPLEID].val; + + /* find the SFSample by index */ + for(list = sfdata->sample; list; list = fluid_list_next(list)) + { + sfsample = fluid_list_get(list); + if (sfsample->idx == sample_idx) + { + break; + } + } + if (list == NULL) + { + FLUID_LOG(FLUID_ERR, "Instrument zone '%s': Invalid sample reference", + inst_zone->name); + return FLUID_FAILED; + } + + inst_zone->sample = sfsample->fluid_sample; + + /* we don't need this generator anymore, mark it as unused */ + inst_zone->gen[GEN_SAMPLEID].flags = GEN_UNUSED; + } + + /* Import the modulators (only SF2.1 and higher) */ + return fluid_zone_mod_import_sfont(inst_zone->name, &inst_zone->mod, sfzone); +} + +/* + * fluid_inst_zone_get_sample + */ +fluid_sample_t * +fluid_inst_zone_get_sample(fluid_inst_zone_t *zone) +{ + return zone->sample; +} + + +int +fluid_zone_inside_range(fluid_zone_range_t *range, int key, int vel) +{ + /* ignoreInstrumentZone is set in mono legato playing */ + int ignore_zone = range->ignore; + + /* Reset the 'ignore' request */ + range->ignore = FALSE; + + return !ignore_zone && ((range->keylo <= key) && + (range->keyhi >= key) && + (range->vello <= vel) && + (range->velhi >= vel)); +} + +/*************************************************************** + * + * SAMPLE + */ + +/* + * fluid_sample_in_rom + */ +int +fluid_sample_in_rom(fluid_sample_t *sample) +{ + return (sample->sampletype & FLUID_SAMPLETYPE_ROM); +} + + +/* + * fluid_sample_import_sfont + */ +int +fluid_sample_import_sfont(fluid_sample_t *sample, SFSample *sfsample, fluid_defsfont_t *defsfont) +{ + FLUID_STRCPY(sample->name, sfsample->name); + + sample->source_start = sfsample->start; + sample->source_end = (sfsample->end > 0) ? sfsample->end - 1 : 0; /* marks last sample, contrary to SF spec. */ + sample->source_loopstart = sfsample->loopstart; + sample->source_loopend = sfsample->loopend; + + sample->start = sample->source_start; + sample->end = sample->source_end; + sample->loopstart = sample->source_loopstart; + sample->loopend = sample->source_loopend; + sample->samplerate = sfsample->samplerate; + sample->origpitch = sfsample->origpitch; + sample->pitchadj = sfsample->pitchadj; + sample->sampletype = sfsample->sampletype; + + if(defsfont->dynamic_samples) + { + sample->notify = dynamic_samples_sample_notify; + } + + if(fluid_sample_validate(sample, defsfont->samplesize) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + return FLUID_OK; +} + +/* Called if a sample is no longer used by a voice. Used by dynamic sample loading + * to unload a sample that is not used by any loaded presets anymore but couldn't + * be unloaded straight away because it was still in use by a voice. */ +static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason) +{ + if(reason == FLUID_SAMPLE_DONE && sample->preset_count == 0) + { + unload_sample(sample); + } + + return FLUID_OK; +} + +/* Called if a preset has been selected for or unselected from a channel. Used by + * dynamic sample loading to load and unload samples on demand. */ +static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan) +{ + fluid_defsfont_t *defsfont; + + if(reason == FLUID_PRESET_SELECTED) + { + FLUID_LOG(FLUID_DBG, "Selected preset '%s' on channel %d", fluid_preset_get_name(preset), chan); + defsfont = fluid_sfont_get_data(preset->sfont); + return load_preset_samples(defsfont, preset); + } + + if(reason == FLUID_PRESET_UNSELECTED) + { + FLUID_LOG(FLUID_DBG, "Deselected preset '%s' from channel %d", fluid_preset_get_name(preset), chan); + defsfont = fluid_sfont_get_data(preset->sfont); + return unload_preset_samples(defsfont, preset); + } + + if(reason == FLUID_PRESET_PIN) + { + defsfont = fluid_sfont_get_data(preset->sfont); + return pin_preset_samples(defsfont, preset); + } + + if(reason == FLUID_PRESET_UNPIN) + { + defsfont = fluid_sfont_get_data(preset->sfont); + return unpin_preset_samples(defsfont, preset); + } + + return FLUID_OK; +} + + +static int pin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) +{ + fluid_defpreset_t *defpreset; + + defpreset = fluid_preset_get_data(preset); + if (defpreset->pinned) + { + return FLUID_OK; + } + + FLUID_LOG(FLUID_DBG, "Pinning preset '%s'", fluid_preset_get_name(preset)); + + if(load_preset_samples(defsfont, preset) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + defpreset->pinned = TRUE; + + return FLUID_OK; +} + + +static int unpin_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) +{ + fluid_defpreset_t *defpreset; + + defpreset = fluid_preset_get_data(preset); + if (!defpreset->pinned) + { + return FLUID_OK; + } + + FLUID_LOG(FLUID_DBG, "Unpinning preset '%s'", fluid_preset_get_name(preset)); + + if(unload_preset_samples(defsfont, preset) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + defpreset->pinned = FALSE; + + return FLUID_OK; +} + + +/* Walk through all samples used by the passed in preset and make sure that the + * sample data is loaded for each sample. Used by dynamic sample loading. */ +static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) +{ + fluid_defpreset_t *defpreset; + fluid_preset_zone_t *preset_zone; + fluid_inst_t *inst; + fluid_inst_zone_t *inst_zone; + fluid_sample_t *sample; + SFData *sffile = NULL; + + defpreset = fluid_preset_get_data(preset); + preset_zone = fluid_defpreset_get_zone(defpreset); + + while(preset_zone != NULL) + { + inst = fluid_preset_zone_get_inst(preset_zone); + inst_zone = fluid_inst_get_zone(inst); + + while(inst_zone != NULL) + { + sample = fluid_inst_zone_get_sample(inst_zone); + + if((sample != NULL) && (sample->start != sample->end)) + { + sample->preset_count++; + + /* If this is the first time this sample has been selected, + * load the sampledata */ + if(sample->preset_count == 1) + { + /* Make sure we have an open Soundfont file. Do this here + * to avoid having to open the file if no loading is necessary + * for a preset */ + if(sffile == NULL) + { + sffile = fluid_sffile_open(defsfont->filename, defsfont->fcbs); + + if(sffile == NULL) + { + FLUID_LOG(FLUID_ERR, "Unable to open Soundfont file"); + return FLUID_FAILED; + } + } + + if(fluid_defsfont_load_sampledata(defsfont, sffile, sample) == FLUID_OK) + { + fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short)); + fluid_voice_optimize_sample(sample); + } + else + { + FLUID_LOG(FLUID_ERR, "Unable to load sample '%s', disabling", sample->name); + sample->start = sample->end = 0; + } + } + } + + inst_zone = fluid_inst_zone_next(inst_zone); + } + + preset_zone = fluid_preset_zone_next(preset_zone); + } + + if(sffile != NULL) + { + fluid_sffile_close(sffile); + } + + return FLUID_OK; +} + +/* Walk through all samples used by the passed in preset and unload the sample data + * of each sample that is not used by any selected preset anymore. Used by dynamic + * sample loading. */ +static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset) +{ + fluid_defpreset_t *defpreset; + fluid_preset_zone_t *preset_zone; + fluid_inst_t *inst; + fluid_inst_zone_t *inst_zone; + fluid_sample_t *sample; + + defpreset = fluid_preset_get_data(preset); + preset_zone = fluid_defpreset_get_zone(defpreset); + + while(preset_zone != NULL) + { + inst = fluid_preset_zone_get_inst(preset_zone); + inst_zone = fluid_inst_get_zone(inst); + + while(inst_zone != NULL) + { + sample = fluid_inst_zone_get_sample(inst_zone); + + if((sample != NULL) && (sample->preset_count > 0)) + { + sample->preset_count--; + + /* If the sample is not used by any preset or used by a + * sounding voice, unload it from the sample cache. If it's + * still in use by a voice, dynamic_samples_sample_notify will + * take care of unloading the sample as soon as the voice is + * finished with it (but only on the next API call). */ + if(sample->preset_count == 0 && sample->refcount == 0) + { + unload_sample(sample); + } + } + + inst_zone = fluid_inst_zone_next(inst_zone); + } + + preset_zone = fluid_preset_zone_next(preset_zone); + } + + return FLUID_OK; +} + +/* Unload an unused sample from the samplecache */ +static void unload_sample(fluid_sample_t *sample) +{ + fluid_return_if_fail(sample != NULL); + fluid_return_if_fail(sample->data != NULL); + fluid_return_if_fail(sample->preset_count == 0); + fluid_return_if_fail(sample->refcount == 0); + + FLUID_LOG(FLUID_DBG, "Unloading sample '%s'", sample->name); + + if(fluid_samplecache_unload(sample->data) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Unable to unload sample '%s'", sample->name); + } + else + { + sample->data = NULL; + sample->data24 = NULL; + } +} + +static fluid_inst_t *find_inst_by_idx(fluid_defsfont_t *defsfont, int idx) +{ + fluid_list_t *list; + fluid_inst_t *inst; + + for(list = defsfont->inst; list != NULL; list = fluid_list_next(list)) + { + inst = fluid_list_get(list); + + if(inst->source_idx == idx) + { + return inst; + } + } + + return NULL; +} diff --git a/thirdparty/fluidsynth/src/sfloader/fluid_defsfont.h b/thirdparty/fluidsynth/src/sfloader/fluid_defsfont.h new file mode 100644 index 000000000..b5129936a --- /dev/null +++ b/thirdparty/fluidsynth/src/sfloader/fluid_defsfont.h @@ -0,0 +1,232 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont loading code borrowed from Smurf SoundFont Editor by Josh Green + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_DEFSFONT_H +#define _FLUID_DEFSFONT_H + + +#include "fluidsynth.h" +#include "fluidsynth_priv.h" +#include "fluid_sffile.h" +#include "fluid_list.h" +#include "fluid_mod.h" +#include "fluid_gen.h" + + + +/*-----------------------------------sfont.h----------------------------*/ + +#define SF_SAMPMODES_LOOP 1 +#define SF_SAMPMODES_UNROLL 2 + +#define SF_MIN_SAMPLERATE 400 +#define SF_MAX_SAMPLERATE 50000 + +#define SF_MIN_SAMPLE_LENGTH 32 + +/*************************************************************** + * + * FORWARD DECLARATIONS + */ +typedef struct _fluid_defsfont_t fluid_defsfont_t; +typedef struct _fluid_defpreset_t fluid_defpreset_t; +typedef struct _fluid_preset_zone_t fluid_preset_zone_t; +typedef struct _fluid_inst_t fluid_inst_t; +typedef struct _fluid_inst_zone_t fluid_inst_zone_t; /**< Soundfont Instrument Zone */ +typedef struct _fluid_voice_zone_t fluid_voice_zone_t; + +/* defines the velocity and key range for a zone */ +struct _fluid_zone_range_t +{ + int keylo; + int keyhi; + int vello; + int velhi; + unsigned char ignore; /* set to TRUE for legato playing to ignore this range zone */ +}; + +/* Stored on a preset zone to keep track of the inst zones that could start a voice + * and their combined preset zone/instument zone ranges */ +struct _fluid_voice_zone_t +{ + fluid_inst_zone_t *inst_zone; + fluid_zone_range_t range; +}; + +/* + + Public interface + + */ + +fluid_sfont_t *fluid_defsfloader_load(fluid_sfloader_t *loader, const char *filename); + + +int fluid_defsfont_sfont_delete(fluid_sfont_t *sfont); +const char *fluid_defsfont_sfont_get_name(fluid_sfont_t *sfont); +fluid_preset_t *fluid_defsfont_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum); +void fluid_defsfont_sfont_iteration_start(fluid_sfont_t *sfont); +fluid_preset_t *fluid_defsfont_sfont_iteration_next(fluid_sfont_t *sfont); + + +void fluid_defpreset_preset_delete(fluid_preset_t *preset); +const char *fluid_defpreset_preset_get_name(fluid_preset_t *preset); +int fluid_defpreset_preset_get_banknum(fluid_preset_t *preset); +int fluid_defpreset_preset_get_num(fluid_preset_t *preset); +int fluid_defpreset_preset_noteon(fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel); + +int fluid_zone_inside_range(fluid_zone_range_t *zone_range, int key, int vel); + +/* + * fluid_defsfont_t + */ +struct _fluid_defsfont_t +{ + const fluid_file_callbacks_t *fcbs; /* the file callbacks used to load this Soundfont */ + char *filename; /* the filename of this soundfont */ + unsigned int samplepos; /* the position in the file at which the sample data starts */ + unsigned int samplesize; /* the size of the sample data in bytes */ + short *sampledata; /* the sample data, loaded in ram */ + + unsigned int sample24pos; /* position within sffd of the sm24 chunk, set to zero if no 24 bit sample support */ + unsigned int sample24size; /* length within sffd of the sm24 chunk */ + char *sample24data; /* if not NULL, the least significant byte of the 24bit sample data, loaded in ram */ + + fluid_sfont_t *sfont; /* pointer to parent sfont */ + fluid_list_t *sample; /* the samples in this soundfont */ + fluid_list_t *preset; /* the presets of this soundfont */ + fluid_list_t *inst; /* the instruments of this soundfont */ + int mlock; /* Should we try memlock (avoid swapping)? */ + int dynamic_samples; /* Enables dynamic sample loading if set */ + + fluid_list_t *preset_iter_cur; /* the current preset in the iteration */ +}; + + +fluid_defsfont_t *new_fluid_defsfont(fluid_settings_t *settings); +int delete_fluid_defsfont(fluid_defsfont_t *defsfont); +int fluid_defsfont_load(fluid_defsfont_t *defsfont, const fluid_file_callbacks_t *file_callbacks, const char *file); +const char *fluid_defsfont_get_name(fluid_defsfont_t *defsfont); +fluid_preset_t *fluid_defsfont_get_preset(fluid_defsfont_t *defsfont, int bank, int prenum); +void fluid_defsfont_iteration_start(fluid_defsfont_t *defsfont); +fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t *defsfont); +int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample); +int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata); + +int fluid_defsfont_add_sample(fluid_defsfont_t *defsfont, fluid_sample_t *sample); +int fluid_defsfont_add_preset(fluid_defsfont_t *defsfont, fluid_defpreset_t *defpreset); + + +/* + * fluid_preset_t + */ +struct _fluid_defpreset_t +{ + fluid_defpreset_t *next; + char name[21]; /* the name of the preset */ + unsigned int bank; /* the bank number */ + unsigned int num; /* the preset number */ + fluid_preset_zone_t *global_zone; /* the global zone of the preset */ + fluid_preset_zone_t *zone; /* the chained list of preset zones */ + int pinned; /* preset samples pinned to sample cache? */ +}; + +fluid_defpreset_t *new_fluid_defpreset(void); +void delete_fluid_defpreset(fluid_defpreset_t *defpreset); +fluid_defpreset_t *fluid_defpreset_next(fluid_defpreset_t *defpreset); +int fluid_defpreset_import_sfont(fluid_defpreset_t *defpreset, SFPreset *sfpreset, fluid_defsfont_t *defsfont, SFData *sfdata); +int fluid_defpreset_set_global_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone); +int fluid_defpreset_add_zone(fluid_defpreset_t *defpreset, fluid_preset_zone_t *zone); +fluid_preset_zone_t *fluid_defpreset_get_zone(fluid_defpreset_t *defpreset); +fluid_preset_zone_t *fluid_defpreset_get_global_zone(fluid_defpreset_t *defpreset); +int fluid_defpreset_get_banknum(fluid_defpreset_t *defpreset); +int fluid_defpreset_get_num(fluid_defpreset_t *defpreset); +const char *fluid_defpreset_get_name(fluid_defpreset_t *defpreset); +int fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int chan, int key, int vel); + +/* + * fluid_preset_zone + */ +struct _fluid_preset_zone_t +{ + fluid_preset_zone_t *next; + char *name; + fluid_inst_t *inst; + fluid_list_t *voice_zone; + fluid_zone_range_t range; + fluid_gen_t gen[GEN_LAST]; + fluid_mod_t *mod; /* List of modulators */ +}; + +fluid_preset_zone_t *new_fluid_preset_zone(char *name); +void delete_fluid_list_mod(fluid_mod_t *mod); +void delete_fluid_preset_zone(fluid_preset_zone_t *zone); +fluid_preset_zone_t *fluid_preset_zone_next(fluid_preset_zone_t *zone); +int fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_defsfont_t *defssfont, SFData *sfdata); +fluid_inst_t *fluid_preset_zone_get_inst(fluid_preset_zone_t *zone); + +/* + * fluid_inst_t + */ +struct _fluid_inst_t +{ + char name[21]; + int source_idx; /* Index of instrument in source Soundfont */ + fluid_inst_zone_t *global_zone; + fluid_inst_zone_t *zone; +}; + +fluid_inst_t *new_fluid_inst(void); +fluid_inst_t *fluid_inst_import_sfont(int inst_idx, fluid_defsfont_t *defsfont, SFData *sfdata); +void delete_fluid_inst(fluid_inst_t *inst); +int fluid_inst_set_global_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone); +int fluid_inst_add_zone(fluid_inst_t *inst, fluid_inst_zone_t *zone); +fluid_inst_zone_t *fluid_inst_get_zone(fluid_inst_t *inst); +fluid_inst_zone_t *fluid_inst_get_global_zone(fluid_inst_t *inst); + +/* + * fluid_inst_zone_t + */ +struct _fluid_inst_zone_t +{ + fluid_inst_zone_t *next; + char *name; + fluid_sample_t *sample; + fluid_zone_range_t range; + fluid_gen_t gen[GEN_LAST]; + fluid_mod_t *mod; /* List of modulators */ +}; + + +fluid_inst_zone_t *new_fluid_inst_zone(char *name); +void delete_fluid_inst_zone(fluid_inst_zone_t *zone); +fluid_inst_zone_t *fluid_inst_zone_next(fluid_inst_zone_t *zone); +int fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid_defsfont_t *defsfont, SFData *sfdata); +fluid_sample_t *fluid_inst_zone_get_sample(fluid_inst_zone_t *zone); + + +int fluid_sample_import_sfont(fluid_sample_t *sample, SFSample *sfsample, fluid_defsfont_t *defsfont); +int fluid_sample_in_rom(fluid_sample_t *sample); + + +#endif /* _FLUID_SFONT_H */ diff --git a/thirdparty/fluidsynth/src/sfloader/fluid_instpatch.c b/thirdparty/fluidsynth/src/sfloader/fluid_instpatch.c new file mode 100644 index 000000000..8539da9a2 --- /dev/null +++ b/thirdparty/fluidsynth/src/sfloader/fluid_instpatch.c @@ -0,0 +1,697 @@ + +#include "fluid_instpatch.h" +#include "fluid_list.h" +#include "fluid_sfont.h" +#include "fluid_sys.h" + +#include + +typedef struct _fluid_instpatch_font_t +{ + char name[256]; + IpatchDLS2 *dls; + + fluid_list_t *preset_list; /* the presets of this soundfont */ + fluid_list_t *preset_iter_cur; /* the current preset in the iteration */ +} fluid_instpatch_font_t; + + +typedef struct _fluid_instpatch_preset_t +{ + fluid_instpatch_font_t *parent_sfont; + IpatchSF2VoiceCache *cache; + + /* pointer to name of the preset, duplicated from item, allocated by glib */ + char *name; + int bank; + int prog; +} fluid_instpatch_preset_t; + +// private struct for storing additional data for each instpatch voice +typedef struct _instpatch_voice_user_data +{ + /* pointer to the sample store that holds the PCM */ + IpatchSampleStoreCache *sample_store; + + /* pointer to a preallocated fluid_sample_t that we can use during noteon for this voice */ + fluid_sample_t *sample; +} fluid_instpatch_voice_user_data_t; + + +/* max voices per instrument (voices exceeding this will not sound) */ +enum +{ + MAX_INST_VOICES = 128, +}; + +int fluid_instpatch_supports_multi_init(void) +{ + guint major, minor, patch; + ipatch_version(&major, &minor, &patch); + + /* libinstpatch <= 1.1.4 only supports calling ipatch_init() once */ + return FLUID_VERSION_CHECK(major, minor, patch) > FLUID_VERSION_CHECK(1, 1, 4); +} + +void fluid_instpatch_init(void) +{ + ipatch_init(); +} + +void fluid_instpatch_deinit(void) +{ + ipatch_close(); +} + +static int delete_fluid_instpatch(fluid_instpatch_font_t *pfont); + +static const char *fluid_instpatch_sfont_get_name(fluid_sfont_t *sfont); +static fluid_preset_t *fluid_instpatch_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum); + +/* sfloader callback to get the name of a preset */ +static const char * +fluid_instpatch_preset_get_name(fluid_preset_t *preset) +{ + fluid_instpatch_preset_t *preset_data = fluid_preset_get_data(preset); + return preset_data->name; +} + +/* sfloader callback to get the bank number of a preset */ +static int +fluid_instpatch_preset_get_banknum(fluid_preset_t *preset) +{ + fluid_instpatch_preset_t *preset_data = fluid_preset_get_data(preset); + return preset_data->bank; +} + +/* sfloader callback to get the preset number of a preset */ +static int +fluid_instpatch_preset_get_num(fluid_preset_t *preset) +{ + fluid_instpatch_preset_t *preset_data = fluid_preset_get_data(preset); + return preset_data->prog; +} + +static void fluid_instpatch_iteration_start(fluid_sfont_t *sfont) +{ + fluid_instpatch_font_t *pfont = fluid_sfont_get_data(sfont); + pfont->preset_iter_cur = pfont->preset_list; +} + +static fluid_preset_t *fluid_instpatch_iteration_next(fluid_sfont_t *sfont) +{ + fluid_instpatch_font_t *pfont = fluid_sfont_get_data(sfont); + fluid_preset_t *preset = fluid_list_get(pfont->preset_iter_cur); + + pfont->preset_iter_cur = fluid_list_next(pfont->preset_iter_cur); + + return preset; +} + +/* sfloader callback for a noteon event */ +static int +fluid_instpatch_preset_noteon(fluid_preset_t *preset, fluid_synth_t *synth, int chan, int key, int vel) +{ + /* voice index array */ + guint16 voice_indices[MAX_INST_VOICES]; + int sel_values[IPATCH_SF2_VOICE_CACHE_MAX_SEL_VALUES]; + fluid_mod_t *fmod = g_alloca(fluid_mod_sizeof()); + fluid_voice_t *flvoice; + + fluid_instpatch_preset_t *preset_data = fluid_preset_get_data(preset); + + int i, voice_count, voice_num, ret = FLUID_FAILED; + GSList *p; + + /* lookup the voice cache that we've created on loading */ + IpatchSF2VoiceCache *cache = preset_data->cache; + + /* loading and caching the instrument could have failed though */ + if(FLUID_UNLIKELY(cache == NULL)) + { + return FLUID_FAILED; + } + + g_object_ref(cache); + + for(i = 0; i < cache->sel_count; i++) + { + IpatchSF2VoiceSelInfo *sel_info = &cache->sel_info[i]; + + switch(sel_info->type) + { + case IPATCH_SF2_VOICE_SEL_NOTE: + sel_values[i] = key; + break; + + case IPATCH_SF2_VOICE_SEL_VELOCITY: + sel_values[i] = vel; + break; + + default: + /* match any; NOTE: according to documentation this should be IPATCH_SF2_VOICE_SEL_WILDCARD */ + sel_values[i] = -1; + break; + } + } + + voice_count = ipatch_sf2_voice_cache_select(cache, sel_values, voice_indices, MAX_INST_VOICES); + + /* loop over matching voice indexes */ + for(voice_num = 0; voice_num < voice_count; voice_num++) + { + IpatchSF2GenArray *gen_array; + fluid_sample_t *fsample; + + IpatchSF2Voice *voice = IPATCH_SF2_VOICE_CACHE_GET_VOICE(cache, voice_indices[voice_num]); + + if(!voice->sample_store) + { + /* For ROM and other non-readable samples */ + continue; + } + + fsample = ((fluid_instpatch_voice_user_data_t *)voice->user_data)->sample; + + ret = fluid_sample_set_sound_data(fsample, + ipatch_sample_store_cache_get_location(IPATCH_SAMPLE_STORE_CACHE(voice->sample_store)), + NULL, + voice->sample_size, + voice->rate, + FALSE + ); + + if(FLUID_UNLIKELY(ret == FLUID_FAILED)) + { + FLUID_LOG(FLUID_ERR, "fluid_sample_set_sound_data() failed"); + goto error_rec; + } + + ret = fluid_sample_set_loop(fsample, voice->loop_start, voice->loop_end); + + if(FLUID_UNLIKELY(ret == FLUID_FAILED)) + { + FLUID_LOG(FLUID_ERR, "fluid_sample_set_loop() failed"); + goto error_rec; + } + + ret = fluid_sample_set_pitch(fsample, voice->root_note, voice->fine_tune); + + if(FLUID_UNLIKELY(ret == FLUID_FAILED)) + { + FLUID_LOG(FLUID_ERR, "fluid_sample_set_pitch() failed"); + goto error_rec; + } + + /* allocate the FluidSynth voice */ + flvoice = fluid_synth_alloc_voice(synth, fsample, chan, key, vel); + + if(flvoice == NULL) + { + ret = FLUID_FAILED; + goto error_rec; + } + + /* set only those generator parameters that are set */ + gen_array = &voice->gen_array; + + for(i = 0; i < IPATCH_SF2_GEN_COUNT; i++) + { + if(IPATCH_SF2_GEN_ARRAY_TEST_FLAG(gen_array, i)) + { + fluid_voice_gen_set(flvoice, i, (float)(gen_array->values[i].sword)); + } + } + + p = voice->mod_list; + + while(p) + { + static const unsigned int mod_mask = + (IPATCH_SF2_MOD_MASK_DIRECTION | IPATCH_SF2_MOD_MASK_POLARITY | IPATCH_SF2_MOD_MASK_TYPE); + + IpatchSF2Mod *mod = p->data; + + fluid_mod_set_dest(fmod, mod->dest); + fluid_mod_set_source1(fmod, + mod->src & IPATCH_SF2_MOD_MASK_CONTROL, + ((mod->src & mod_mask) >> IPATCH_SF2_MOD_SHIFT_DIRECTION) + | ((mod->src & IPATCH_SF2_MOD_MASK_CC) ? FLUID_MOD_CC : 0)); + + fluid_mod_set_source2(fmod, + mod->amtsrc & IPATCH_SF2_MOD_MASK_CONTROL, + ((mod->amtsrc & mod_mask) >> IPATCH_SF2_MOD_SHIFT_DIRECTION) + | ((mod->amtsrc & IPATCH_SF2_MOD_MASK_CC) ? FLUID_MOD_CC : 0)); + + fluid_mod_set_amount(fmod, mod->amount); + fluid_voice_add_mod(flvoice, fmod, FLUID_VOICE_OVERWRITE); + + p = p->next; + } + + fluid_synth_start_voice(synth, flvoice); + + /* sample store reference taken over by fsample structure */ + } + + ret = FLUID_OK; + +error_rec: + + g_object_unref(cache); + return ret; +} + + + + +/* sfloader callback to get a patch file name */ +static const char * +fluid_instpatch_sfont_get_name(fluid_sfont_t *sfont) +{ + fluid_instpatch_font_t *sfont_data = fluid_sfont_get_data(sfont); + return sfont_data->name; +} + +static void delete_fluid_instpatch_preset(fluid_instpatch_preset_t *preset_data) +{ + fluid_return_if_fail(preset_data != NULL); + + /* -- remove voice cache reference */ + g_object_unref(preset_data->cache); + + g_free(preset_data->name); + + FLUID_FREE(preset_data); +} + +static void fluid_instpatch_preset_free(fluid_preset_t *preset) +{ + fluid_return_if_fail(preset != NULL); + + delete_fluid_instpatch_preset(fluid_preset_get_data(preset)); + delete_fluid_preset(preset); +} + +/* sfloader callback to get a preset (instrument) by bank and preset number */ +static fluid_preset_t * +fluid_instpatch_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum) +{ + fluid_instpatch_font_t *sfont_data = fluid_sfont_get_data(sfont); + fluid_preset_t *preset; + fluid_list_t *list; + + for(list = sfont_data->preset_list; list != NULL; list = fluid_list_next(list)) + { + preset = (fluid_preset_t *)fluid_list_get(list); + + if((fluid_preset_get_banknum(preset) == bank) && (fluid_preset_get_num(preset) == prenum)) + { + return preset; + } + } + + return NULL; +} + +static fluid_instpatch_voice_user_data_t *new_fluid_instpatch_voice_user_data(IpatchSampleStoreCache *sample_store) +{ + fluid_instpatch_voice_user_data_t *data = FLUID_NEW(fluid_instpatch_voice_user_data_t); + fluid_sample_t *sample = new_fluid_sample(); + + if(data == NULL || sample == NULL) + { + FLUID_FREE(data); + delete_fluid_sample(sample); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + data->sample = sample; + + /* Keep sample store cached by doing a dummy open */ + ipatch_sample_store_cache_open(sample_store); + data->sample_store = sample_store; + return data; +} + +static void fluid_instpatch_on_voice_user_data_destroy(gpointer user_data) +{ + fluid_instpatch_voice_user_data_t *data = user_data; + + delete_fluid_sample(data->sample); + ipatch_sample_store_cache_close(data->sample_store); + FLUID_FREE(data); +} + +static IpatchSF2VoiceCache *convert_dls_to_sf2_instrument(fluid_instpatch_font_t *patchfont, IpatchDLS2Inst *item, const char **err) +{ + static const char no_conv[] = "Unable to find a voice cache converter for this type"; + static const char conv_fail[] = "Failed to convert DLS inst to SF2 voices"; + static const char cache_fail[] = "Failed to cache DLS inst to SF2 voices"; + static const char oom[] = "Out of memory"; + + IpatchConverter *conv; + IpatchSF2VoiceCache *cache; + int i, count; + + /* create SF2 voice cache converter */ + conv = ipatch_create_converter(G_OBJECT_TYPE(item), IPATCH_TYPE_SF2_VOICE_CACHE); + + /* no SF2 voice cache converter for this item type? */ + if(conv == NULL) + { + *err = no_conv; + return NULL; + } + + cache = ipatch_sf2_voice_cache_new(NULL, 0); + + if(cache == NULL) + { + *err = oom; + g_object_unref(conv); + return NULL; + } + + /* do not use the default modulator list of libinstpatch, we manage our own list of default modulators */ + ipatch_sf2_voice_cache_set_default_mods(cache, NULL); + + ipatch_converter_add_input(conv, G_OBJECT(item)); + ipatch_converter_add_output(conv, G_OBJECT(cache)); + + if(!ipatch_converter_convert(conv, NULL)) + { + *err = conv_fail; + g_object_unref(cache); + g_object_unref(conv); + return NULL; + } + + g_object_unref (conv); + conv = NULL; + + /* Use voice->user_data to close open cached stores */ + cache->voice_user_data_destroy = fluid_instpatch_on_voice_user_data_destroy; + + /* loop over voices and load sample data into RAM */ + count = cache->voices->len; + + for(i = 0; i < count; i++) + { + IpatchSF2Voice *voice = &g_array_index(cache->voices, IpatchSF2Voice, i); + + if(!ipatch_sf2_voice_cache_sample_data(voice, NULL)) + { + *err = cache_fail; + g_object_unref(cache); + return NULL; + } + + if((voice->user_data = new_fluid_instpatch_voice_user_data(IPATCH_SAMPLE_STORE_CACHE(voice->sample_store))) == NULL) + { + *err = oom; + g_object_unref(cache); + return NULL; + } + } + + /* !! caller takes over cache reference */ + return cache; +} + + +fluid_instpatch_font_t *new_fluid_instpatch(fluid_sfont_t *sfont, const fluid_file_callbacks_t *fcbs, const char *filename) +{ + fluid_instpatch_font_t *patchfont = NULL; + GError *err = NULL; + IpatchDLSReader *reader = NULL; + IpatchDLSFile *file = NULL; + IpatchFileHandle *handle = NULL; + + fluid_return_val_if_fail(filename != NULL, NULL); + + if((patchfont = FLUID_NEW(fluid_instpatch_font_t)) == NULL) + { + return NULL; + } + + FLUID_MEMSET(patchfont, 0, sizeof(*patchfont)); + + FLUID_STRNCPY(&patchfont->name[0], filename, sizeof(patchfont->name)); + + /* open a file, we get a reference */ + if((file = ipatch_dls_file_new()) == NULL) + { + FLUID_FREE(patchfont); + + return NULL; + } + + /* ipatch_file_open() references the file again */ + if((handle = ipatch_file_open(IPATCH_FILE(file), filename, "r", &err)) == NULL) + { + FLUID_LOG(FLUID_ERR, "ipatch_file_open() failed with error: '%s'", ipatch_gerror_message(err)); + + g_object_unref(file); + FLUID_FREE(patchfont); + + return NULL; + } + + /* get rid of the reference we own, we dont need it any longer */ + g_object_unref(file); + file = NULL; + + /* open a reader, this gives us a reference */ + if((reader = ipatch_dls_reader_new(handle)) == NULL) + { + ipatch_file_close(handle); + FLUID_FREE(patchfont); + + return NULL; + } + + patchfont->dls = ipatch_dls_reader_load(reader, &err); + + /* unref the reader directly afterwards, not needed any longer */ + g_object_unref(reader); + reader = NULL; + + if(patchfont->dls == NULL) + { + FLUID_LOG(FLUID_ERR, "ipatch_dls_reader_new() failed with error: '%s'", ipatch_gerror_message(err)); + + // reader has already been unrefed, i.e. no need to call ipatch_file_close() + + FLUID_FREE(patchfont); + + return NULL; + } + else + { + /* at this point everything is owned by the IpatchDLS2*, no need for custom cleanups any longer */ + + IpatchIter iter; + IpatchDLS2Inst *inst; + gboolean success = ipatch_container_init_iter(IPATCH_CONTAINER(patchfont->dls), &iter, IPATCH_TYPE_DLS2_INST); + + if(success == FALSE) + { + goto bad_luck; + } + + inst = ipatch_dls2_inst_first(&iter); + + if(inst == NULL) + { + FLUID_LOG(FLUID_ERR, "A soundfont file was accepted by libinstpatch, but it doesn't contain a single instrument. Dropping the whole file."); + goto bad_luck; + } + + /* loop over instruments, convert to sf2 voices, create fluid_samples, cache all presets in a list */ + do + { + fluid_preset_t *preset; + IpatchSF2VoiceCache *cache; + int bank, prog; + const char *err = NULL; + ipatch_dls2_inst_get_midi_locale(inst, &bank, &prog); + + if((cache = convert_dls_to_sf2_instrument(patchfont, inst, &err)) == NULL) + { + FLUID_LOG(FLUID_WARN, "Unable to use DLS instrument bank %d , prog %d : %s.", bank, prog, err); + } + else + { + int is_percussion = (ipatch_item_get_flags(inst) & IPATCH_DLS2_INST_PERCUSSION) != 0; + fluid_instpatch_preset_t *preset_data = FLUID_NEW(fluid_instpatch_preset_t); + + if(preset_data == NULL) + { + g_object_unref(inst); + g_object_unref(cache); + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto bad_luck; + } + + FLUID_MEMSET(preset_data, 0, sizeof(*preset_data)); + + preset_data->parent_sfont = patchfont; + preset_data->cache = cache; + /* save name, bank and preset for quick lookup */ + preset_data->bank = is_percussion * 128 + bank; + preset_data->prog = prog; + g_object_get(inst, "name", &preset_data->name, NULL); + + preset = new_fluid_preset(sfont, + fluid_instpatch_preset_get_name, + fluid_instpatch_preset_get_banknum, + fluid_instpatch_preset_get_num, + fluid_instpatch_preset_noteon, + fluid_instpatch_preset_free); + + if(preset == NULL) + { + delete_fluid_instpatch_preset(preset_data); + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto bad_luck; + } + else + { + fluid_preset_set_data(preset, preset_data); + patchfont->preset_list = fluid_list_append(patchfont->preset_list, preset); + } + } + + inst = ipatch_dls2_inst_next(&iter); + } + while(inst); + + return patchfont; + } + +bad_luck: + delete_fluid_instpatch(patchfont); + return NULL; +} + +static int delete_fluid_instpatch(fluid_instpatch_font_t *pfont) +{ + guint16 voice_indices[MAX_INST_VOICES]; + int sel_values[IPATCH_SF2_VOICE_CACHE_MAX_SEL_VALUES]; + fluid_list_t *list; + + fluid_return_val_if_fail(pfont != NULL, FLUID_OK); + + /* loop through all fluid samples and return error if any sample is currently in use for rendering */ + for(list = pfont->preset_list; list; list = fluid_list_next(list)) + { + fluid_instpatch_preset_t *preset_data = fluid_preset_get_data((fluid_preset_t *)fluid_list_get(list)); + + int i, voice_count; + + /* lookup the voice cache that we've created on loading */ + IpatchSF2VoiceCache *cache = preset_data->cache; + + if(cache == NULL) + { + continue; + } + + g_object_ref(cache); + + for(i = 0; i < cache->sel_count; i++) + { + sel_values[i] = -1; + } + + voice_count = ipatch_sf2_voice_cache_select(cache, sel_values, voice_indices, MAX_INST_VOICES); + + for(i = 0; i < voice_count; i++) + { + IpatchSF2Voice *voice = IPATCH_SF2_VOICE_CACHE_GET_VOICE(cache, voice_indices[i]); + fluid_sample_t *fsample = ((fluid_instpatch_voice_user_data_t *)voice->user_data)->sample; + + if(fsample->refcount != 0) + { + g_object_unref(cache); + return FLUID_FAILED; + } + } + + g_object_unref(cache); + } + + for(list = pfont->preset_list; list; list = fluid_list_next(list)) + { + fluid_preset_delete_internal((fluid_preset_t *)fluid_list_get(list)); + } + + delete_fluid_list(pfont->preset_list); + + // also unrefs IpatchDLSFile and IpatchFileHandle + g_object_unref(pfont->dls); + + FLUID_FREE(pfont); + + return FLUID_OK; +} + +static int fluid_instpatch_sfont_delete(fluid_sfont_t *sfont) +{ + int ret; + fluid_return_val_if_fail(sfont != NULL, -1); + + if((ret = delete_fluid_instpatch(fluid_sfont_get_data(sfont))) == FLUID_OK) + { + delete_fluid_sfont(sfont); + } + + return ret; +} + +static fluid_sfont_t *fluid_instpatch_loader_load(fluid_sfloader_t *loader, const char *filename) +{ + fluid_instpatch_font_t *patchfont = NULL; + fluid_sfont_t *sfont = NULL; + + sfont = new_fluid_sfont(fluid_instpatch_sfont_get_name, + fluid_instpatch_sfont_get_preset, + fluid_instpatch_iteration_start, + fluid_instpatch_iteration_next, + fluid_instpatch_sfont_delete); + + if(sfont == NULL) + { + return NULL; + } + + if((patchfont = new_fluid_instpatch(sfont, &loader->file_callbacks, filename)) == NULL) + { + delete_fluid_sfont(sfont); + return NULL; + } + + fluid_sfont_set_data(sfont, patchfont); + return sfont; +} + +fluid_sfloader_t *new_fluid_instpatch_loader(fluid_settings_t *settings) +{ + fluid_sfloader_t *loader; + fluid_return_val_if_fail(settings != NULL, NULL); + + loader = new_fluid_sfloader(fluid_instpatch_loader_load, delete_fluid_sfloader); + + if(loader == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + fluid_sfloader_set_data(loader, settings); + + return loader; +} + diff --git a/thirdparty/fluidsynth/src/sfloader/fluid_instpatch.h b/thirdparty/fluidsynth/src/sfloader/fluid_instpatch.h new file mode 100644 index 000000000..c1838750a --- /dev/null +++ b/thirdparty/fluidsynth/src/sfloader/fluid_instpatch.h @@ -0,0 +1,14 @@ + +#ifndef _FLUID_INSTPATCH_H +#define _FLUID_INSTPATCH_H + +#include "fluid_sfont.h" +#include "fluid_settings.h" + +void fluid_instpatch_init(void); +void fluid_instpatch_deinit(void); +fluid_sfloader_t *new_fluid_instpatch_loader(fluid_settings_t *settings); + +int fluid_instpatch_supports_multi_init(void); + +#endif // _FLUID_INSTPATCH_H diff --git a/thirdparty/fluidsynth/src/sfloader/fluid_samplecache.c b/thirdparty/fluidsynth/src/sfloader/fluid_samplecache.c new file mode 100644 index 000000000..64e9e9e70 --- /dev/null +++ b/thirdparty/fluidsynth/src/sfloader/fluid_samplecache.c @@ -0,0 +1,313 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont file loading code borrowed from Smurf SoundFont Editor + * Copyright (C) 1999-2001 Josh Green + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* CACHED SAMPLE DATA LOADER + * + * This is a wrapper around fluid_sffile_read_sample_data that attempts to cache the read + * data across all FluidSynth instances in a global (process-wide) list. + */ + +#include "fluid_samplecache.h" +#include "fluid_sys.h" +#include "fluid_list.h" + + +typedef struct _fluid_samplecache_entry_t fluid_samplecache_entry_t; + +struct _fluid_samplecache_entry_t +{ + /* The following members all form the cache key */ + char *filename; + time_t modification_time; + unsigned int sf_samplepos; + unsigned int sf_samplesize; + unsigned int sf_sample24pos; + unsigned int sf_sample24size; + unsigned int sample_start; + unsigned int sample_end; + int sample_type; + /* End of cache key members */ + + short *sample_data; + char *sample_data24; + int sample_count; + + int num_references; + int mlocked; +}; + +static fluid_list_t *samplecache_list = NULL; +static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT; + +static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, + unsigned int sample_end, int sample_type, time_t mtime); +static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, + unsigned int sample_end, int sample_type, time_t mtime); +static void delete_samplecache_entry(fluid_samplecache_entry_t *entry); + +static int fluid_get_file_modification_time(char *filename, time_t *modification_time); + + +/* PUBLIC INTERFACE */ + +int fluid_samplecache_load(SFData *sf, + unsigned int sample_start, unsigned int sample_end, int sample_type, + int try_mlock, short **sample_data, char **sample_data24) +{ + fluid_samplecache_entry_t *entry; + int ret; + time_t mtime; + + fluid_mutex_lock(samplecache_mutex); + + if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED) + { + mtime = 0; + } + + entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime); + + if(entry == NULL) + { + fluid_mutex_unlock(samplecache_mutex); + entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime); + + if(entry == NULL) + { + ret = -1; + goto unlock_exit; + } + + fluid_mutex_lock(samplecache_mutex); + samplecache_list = fluid_list_prepend(samplecache_list, entry); + } + fluid_mutex_unlock(samplecache_mutex); + + if(try_mlock && !entry->mlocked) + { + /* Lock the memory to disable paging. It's okay if this fails. It + * probably means that the user doesn't have the required permission. */ + if(fluid_mlock(entry->sample_data, entry->sample_count * sizeof(short)) == 0) + { + if(entry->sample_data24 != NULL) + { + entry->mlocked = (fluid_mlock(entry->sample_data24, entry->sample_count) == 0); + } + else + { + entry->mlocked = TRUE; + } + + if(!entry->mlocked) + { + fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short)); + FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible."); + } + } + } + + entry->num_references++; + *sample_data = entry->sample_data; + *sample_data24 = entry->sample_data24; + ret = entry->sample_count; + +unlock_exit: + return ret; +} + +int fluid_samplecache_unload(const short *sample_data) +{ + fluid_list_t *entry_list; + fluid_samplecache_entry_t *entry; + int ret; + + fluid_mutex_lock(samplecache_mutex); + + entry_list = samplecache_list; + + while(entry_list) + { + entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list); + + if(sample_data == entry->sample_data) + { + entry->num_references--; + + if(entry->num_references == 0) + { + if(entry->mlocked) + { + fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short)); + + if(entry->sample_data24 != NULL) + { + fluid_munlock(entry->sample_data24, entry->sample_count); + } + } + + samplecache_list = fluid_list_remove(samplecache_list, entry); + delete_samplecache_entry(entry); + } + + ret = FLUID_OK; + goto unlock_exit; + } + + entry_list = fluid_list_next(entry_list); + } + + FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache."); + ret = FLUID_FAILED; + +unlock_exit: + fluid_mutex_unlock(samplecache_mutex); + return ret; +} + + +/* Private functions */ +static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, + unsigned int sample_start, + unsigned int sample_end, + int sample_type, + time_t mtime) +{ + fluid_samplecache_entry_t *entry; + + entry = FLUID_NEW(fluid_samplecache_entry_t); + + if(entry == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(entry, 0, sizeof(*entry)); + + entry->filename = FLUID_STRDUP(sf->fname); + + if(entry->filename == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + entry->sf_samplepos = sf->samplepos; + entry->sf_samplesize = sf->samplesize; + entry->sf_sample24pos = sf->sample24pos; + entry->sf_sample24size = sf->sample24size; + entry->sample_start = sample_start; + entry->sample_end = sample_end; + entry->sample_type = sample_type; + entry->modification_time = mtime; + + entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type, + &entry->sample_data, &entry->sample_data24); + + if(entry->sample_count < 0) + { + goto error_exit; + } + + return entry; + +error_exit: + delete_samplecache_entry(entry); + return NULL; +} + +static void delete_samplecache_entry(fluid_samplecache_entry_t *entry) +{ + fluid_return_if_fail(entry != NULL); + + FLUID_FREE(entry->filename); + FLUID_FREE(entry->sample_data); + FLUID_FREE(entry->sample_data24); + FLUID_FREE(entry); +} + +static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, + unsigned int sample_start, + unsigned int sample_end, + int sample_type, + time_t mtime) +{ + fluid_list_t *entry_list; + fluid_samplecache_entry_t *entry; + + entry_list = samplecache_list; + + while(entry_list) + { + entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list); + + if((FLUID_STRCMP(sf->fname, entry->filename) == 0) && + (mtime == entry->modification_time) && + (sf->samplepos == entry->sf_samplepos) && + (sf->samplesize == entry->sf_samplesize) && + (sf->sample24pos == entry->sf_sample24pos) && + (sf->sample24size == entry->sf_sample24size) && + (sample_start == entry->sample_start) && + (sample_end == entry->sample_end) && + (sample_type == entry->sample_type)) + { + return entry; + } + + entry_list = fluid_list_next(entry_list); + } + + return NULL; +} + +static int fluid_get_file_modification_time(char *filename, time_t *modification_time) +{ + fluid_stat_buf_t buf; + + if(fluid_stat(filename, &buf)) + { + return FLUID_FAILED; + } + + *modification_time = buf.st_mtime; + return FLUID_OK; +} + + +/* Only used for tests */ +int fluid_samplecache_count_entries(void) +{ + fluid_list_t *entry; + int count = 0; + + fluid_mutex_lock(samplecache_mutex); + + for(entry = samplecache_list; entry != NULL; entry = fluid_list_next(entry)) + { + count++; + } + + fluid_mutex_unlock(samplecache_mutex); + + return count; +} diff --git a/thirdparty/fluidsynth/src/sfloader/fluid_samplecache.h b/thirdparty/fluidsynth/src/sfloader/fluid_samplecache.h new file mode 100644 index 000000000..de6206ba7 --- /dev/null +++ b/thirdparty/fluidsynth/src/sfloader/fluid_samplecache.h @@ -0,0 +1,37 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_SAMPLECACHE_H +#define _FLUID_SAMPLECACHE_H + +#include "fluid_sfont.h" +#include "fluid_sffile.h" + +int fluid_samplecache_load(SFData *sf, + unsigned int sample_start, unsigned int sample_end, int sample_type, + int try_mlock, short **data, char **data24); + +int fluid_samplecache_unload(const short *sample_data); + +/* Only used for tests */ +int fluid_samplecache_count_entries(void); + +#endif /* _FLUID_SAMPLECACHE_H */ diff --git a/thirdparty/fluidsynth/src/sfloader/fluid_sffile.c b/thirdparty/fluidsynth/src/sfloader/fluid_sffile.c new file mode 100644 index 000000000..d975c77d0 --- /dev/null +++ b/thirdparty/fluidsynth/src/sfloader/fluid_sffile.c @@ -0,0 +1,2535 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont file loading code borrowed from Smurf SoundFont Editor + * Copyright (C) 1999-2001 Josh Green + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#include "fluid_sffile.h" +#include "fluid_sfont.h" +#include "fluid_sys.h" + +#define LIBSNDFILE_SUPPORT 1 +#include "sndfile.h" + + +#if LIBINSTPATCH_SUPPORT +#include +#endif + +/*=================================sfload.c======================== + Borrowed from Smurf SoundFont Editor by Josh Green + =================================================================*/ + +/* FOURCC definitions */ +#define RIFF_FCC FLUID_FOURCC('R','I','F','F') +#define LIST_FCC FLUID_FOURCC('L','I','S','T') +#define SFBK_FCC FLUID_FOURCC('s','f','b','k') +#define INFO_FCC FLUID_FOURCC('I','N','F','O') +#define SDTA_FCC FLUID_FOURCC('s','d','t','a') +#define PDTA_FCC FLUID_FOURCC('p','d','t','a') /* info/sample/preset */ + +#define IFIL_FCC FLUID_FOURCC('i','f','i','l') +#define ISNG_FCC FLUID_FOURCC('i','s','n','g') +#define INAM_FCC FLUID_FOURCC('I','N','A','M') +#define IROM_FCC FLUID_FOURCC('i','r','o','m') /* info ids (1st byte of info strings) */ +#define IVER_FCC FLUID_FOURCC('i','v','e','r') +#define ICRD_FCC FLUID_FOURCC('I','C','R','D') +#define IENG_FCC FLUID_FOURCC('I','E','N','G') +#define IPRD_FCC FLUID_FOURCC('I','P','R','D') /* more info ids */ +#define ICOP_FCC FLUID_FOURCC('I','C','O','P') +#define ICMT_FCC FLUID_FOURCC('I','C','M','T') +#define ISFT_FCC FLUID_FOURCC('I','S','F','T') /* and yet more info ids */ + +#define SNAM_FCC FLUID_FOURCC('s','n','a','m') +#define SMPL_FCC FLUID_FOURCC('s','m','p','l') /* sample ids */ +#define PHDR_FCC FLUID_FOURCC('p','h','d','r') +#define PBAG_FCC FLUID_FOURCC('p','b','a','g') +#define PMOD_FCC FLUID_FOURCC('p','m','o','d') +#define PGEN_FCC FLUID_FOURCC('p','g','e','n') /* preset ids */ +#define IHDR_FCC FLUID_FOURCC('i','n','s','t') +#define IBAG_FCC FLUID_FOURCC('i','b','a','g') +#define IMOD_FCC FLUID_FOURCC('i','m','o','d') +#define IGEN_FCC FLUID_FOURCC('i','g','e','n') /* instrument ids */ +#define SHDR_FCC FLUID_FOURCC('s','h','d','r') /* sample info */ +#define SM24_FCC FLUID_FOURCC('s','m','2','4') + +/* Set when the FCC code is unknown */ +#define UNKN_ID FLUID_N_ELEMENTS(idlist) + +/* + * This declares a uint32_t array containing the SF2 chunk identifiers. + */ +static const uint32_t idlist[] = +{ + RIFF_FCC, + LIST_FCC, + SFBK_FCC, + INFO_FCC, + SDTA_FCC, + PDTA_FCC, + + IFIL_FCC, + ISNG_FCC, + INAM_FCC, + IROM_FCC, + IVER_FCC, + ICRD_FCC, + IENG_FCC, + IPRD_FCC, + ICOP_FCC, + ICMT_FCC, + ISFT_FCC, + + SNAM_FCC, + SMPL_FCC, + PHDR_FCC, + PBAG_FCC, + PMOD_FCC, + PGEN_FCC, + IHDR_FCC, + IBAG_FCC, + IMOD_FCC, + IGEN_FCC, + SHDR_FCC, + SM24_FCC +}; + +static const unsigned short invalid_inst_gen[] = +{ + GEN_UNUSED1, + GEN_UNUSED2, + GEN_UNUSED3, + GEN_UNUSED4, + GEN_RESERVED1, + GEN_RESERVED2, + GEN_RESERVED3, + GEN_INSTRUMENT, +}; + +static const unsigned short invalid_preset_gen[] = +{ + GEN_STARTADDROFS, + GEN_ENDADDROFS, + GEN_STARTLOOPADDROFS, + GEN_ENDLOOPADDROFS, + GEN_STARTADDRCOARSEOFS, + GEN_ENDADDRCOARSEOFS, + GEN_STARTLOOPADDRCOARSEOFS, + GEN_KEYNUM, + GEN_VELOCITY, + GEN_ENDLOOPADDRCOARSEOFS, + GEN_SAMPLEMODE, + GEN_EXCLUSIVECLASS, + GEN_OVERRIDEROOTKEY, + GEN_SAMPLEID, +}; + + +/* sfont file chunk sizes */ +#define SF_PHDR_SIZE (38) +#define SF_BAG_SIZE (4) +#define SF_MOD_SIZE (10) +#define SF_GEN_SIZE (4) +#define SF_IHDR_SIZE (22) +#define SF_SHDR_SIZE (46) + + +#define READCHUNK(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(var, 8, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + ((SFChunk *)(var))->size = FLUID_LE32TOH(((SFChunk *)(var))->size); \ + } while (0) + +#define READD(sf, var) \ + do \ + { \ + uint32_t _temp; \ + if (sf->fcbs->fread(&_temp, 4, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + var = FLUID_LE32TOH(_temp); \ + } while (0) + +#define READW(sf, var) \ + do \ + { \ + uint16_t _temp; \ + if (sf->fcbs->fread(&_temp, 2, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + var = FLUID_LE16TOH(_temp); \ + } while (0) + +#define READID(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(var, 4, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +#define READSTR(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(var, 20, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + (*var)[20] = '\0'; \ + } while (0) + +#define READB(sf, var) \ + do \ + { \ + if (sf->fcbs->fread(&var, 1, sf->sffd) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +#define FSKIP(sf, size) \ + do \ + { \ + if (sf->fcbs->fseek(sf->sffd, size, SEEK_CUR) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +#define FSKIPW(sf) \ + do \ + { \ + if (sf->fcbs->fseek(sf->sffd, 2, SEEK_CUR) == FLUID_FAILED) \ + return FALSE; \ + } while (0) + +/* removes and advances a fluid_list_t pointer */ +#define SLADVREM(list, item) \ + do \ + { \ + fluid_list_t *_temp = item; \ + item = fluid_list_next(item); \ + list = fluid_list_remove_link(list, _temp); \ + delete1_fluid_list(_temp); \ + } while (0) + + +static int load_header(SFData *sf); +static int load_body(SFData *sf); +static int process_info(SFData *sf, int size); +static int process_sdta(SFData *sf, unsigned int size); +static int process_pdta(SFData *sf, int size); +static int load_phdr(SFData *sf, unsigned int size); +static int load_pbag(SFData *sf, int size); +static int load_pmod(SFData *sf, int size); +static int load_ihdr(SFData *sf, unsigned int size); +static int load_ibag(SFData *sf, int size); +static int load_imod(SFData *sf, int size); +static int load_shdr(SFData *sf, unsigned int size); + +static int chunkid(uint32_t id); +static int read_listchunk(SFData *sf, SFChunk *chunk); +static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size); +static int preset_compare_func(const void *a, const void *b); +static fluid_list_t *find_gen_by_id(int gen, fluid_list_t *genlist); +static int valid_inst_genid(unsigned short genid); +static int valid_preset_genid(unsigned short genid); + +static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data); +static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24); + +/** + * Check if a file is a SoundFont file. + * + * @param filename Path to the file to check + * @return TRUE if it could be a SF2, SF3 or DLS file, FALSE otherwise + * + * If fluidsynth was built with DLS support, this function will also identify DLS files. + * + * @note This function only checks whether header(s) in the RIFF chunk are present. + * A call to fluid_synth_sfload() might still fail. + */ +int fluid_is_soundfont(const char *filename) +{ + FILE *fp; + uint32_t fcc; + int retcode = FALSE; + const char* err_msg; + + do + { + if((fp = fluid_file_open(filename, &err_msg)) == NULL) + { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): fopen() failed: '%s'", err_msg); + return retcode; + } + + if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) + { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): failed to read RIFF chunk id."); + break; + } + + if(fcc != RIFF_FCC) + { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): expected RIFF chunk id '0x%04X' but got '0x%04X'.", (unsigned int) RIFF_FCC, (unsigned int)fcc); + break; + } + + if(FLUID_FSEEK(fp, 4, SEEK_CUR)) + { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): cannot seek +4 bytes."); + break; + } + + if(FLUID_FREAD(&fcc, sizeof(fcc), 1, fp) != 1) + { + FLUID_LOG(FLUID_ERR, "fluid_is_soundfont(): failed to read SFBK chunk id."); + break; + } + + retcode = (fcc == SFBK_FCC); + if(retcode) + { + break; // seems to be SF2, stop here + } +#ifdef LIBINSTPATCH_SUPPORT + else + { + IpatchFileHandle *fhandle = ipatch_file_identify_open(filename, NULL); + if(fhandle != NULL) + { + retcode = (ipatch_file_identify(fhandle->file, NULL) == IPATCH_TYPE_DLS_FILE); + ipatch_file_close(fhandle); + } + } +#endif + } + while(0); + + FLUID_FCLOSE(fp); + + return retcode; +} + +/* + * Open a SoundFont file and parse it's contents into a SFData structure. + * + * @param fname filename + * @param fcbs file callback structure + * @return the partially parsed SoundFont as SFData structure or NULL on error + */ +SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs) +{ + SFData *sf; + fluid_long_long_t fsize = 0; + + if(!(sf = FLUID_NEW(SFData))) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(sf, 0, sizeof(SFData)); + + fluid_rec_mutex_init(sf->mtx); + sf->fcbs = fcbs; + + if((sf->sffd = fcbs->fopen(fname)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Unable to open file '%s'", fname); + goto error_exit; + } + + sf->fname = FLUID_STRDUP(fname); + + if(sf->fname == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + /* get size of file by seeking to end */ + if(fcbs->fseek(sf->sffd, 0L, SEEK_END) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Seek to end of file failed"); + goto error_exit; + } + + if((fsize = fcbs->ftell(sf->sffd)) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Get end of file position failed"); + goto error_exit; + } + + sf->filesize = fsize; + + if(fcbs->fseek(sf->sffd, 0, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Rewind to start of file failed"); + goto error_exit; + } + + if(!load_header(sf)) + { + goto error_exit; + } + + return sf; + +error_exit: + fluid_sffile_close(sf); + return NULL; +} + +/* + * Parse all preset information from the soundfont + * + * @return FLUID_OK on success, otherwise FLUID_FAILED + */ +int fluid_sffile_parse_presets(SFData *sf) +{ + if(!load_body(sf)) + { + return FLUID_FAILED; + } + + return FLUID_OK; +} + +/* Load sample data from the soundfont file + * + * This function will always return the sample data in WAV format. If the sample_type specifies an + * Ogg Vorbis compressed sample, it will be decompressed automatically before returning. + * + * @param sf SFData instance + * @param sample_start index of first sample point in Soundfont sample chunk + * @param sample_end index of last sample point in Soundfont sample chunk + * @param sample_type type of the sample in Soundfont + * @param data pointer to sample data pointer, will point to loaded sample data on success + * @param data24 pointer to 24-bit sample data pointer if 24-bit data present, will point to loaded + * 24-bit sample data on success or NULL if no 24-bit data is present in file + * + * @return The number of sample words in returned buffers or -1 on failure + */ +int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end, + int sample_type, short **data, char **data24) +{ + int num_samples; + + if(sample_type & FLUID_SAMPLETYPE_OGG_VORBIS) + { + num_samples = fluid_sffile_read_vorbis(sf, sample_start, sample_end, data); + } + else + { + num_samples = fluid_sffile_read_wav(sf, sample_start, sample_end, data, data24); + } + + return num_samples; +} + +/* + * Close a SoundFont file and free the SFData structure. + * + * @param sf pointer to SFData structure + * @param fcbs file callback structure + */ +void fluid_sffile_close(SFData *sf) +{ + fluid_list_t *entry; + SFPreset *preset; + SFInst *inst; + + fluid_rec_mutex_destroy(sf->mtx); + if(sf->sffd) + { + sf->fcbs->fclose(sf->sffd); + } + + FLUID_FREE(sf->fname); + + entry = sf->info; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->info); + + entry = sf->preset; + + while(entry) + { + preset = (SFPreset *)fluid_list_get(entry); + delete_preset(preset); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->preset); + + entry = sf->inst; + + while(entry) + { + inst = (SFInst *)fluid_list_get(entry); + delete_inst(inst); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->inst); + + entry = sf->sample; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(sf->sample); + + FLUID_FREE(sf); +} + + +/* + * Private functions + */ + +/* sound font file load functions */ +static int chunkid(uint32_t id) +{ + unsigned int i; + + for(i = 0; i < FLUID_N_ELEMENTS(idlist); i++) + { + if(idlist[i] == id) + { + break; + } + } + + /* Return chunk id or UNKN_ID if not found */ + return i; +} + +static int load_header(SFData *sf) +{ + SFChunk chunk; + + READCHUNK(sf, &chunk); /* load RIFF chunk */ + + if(chunk.id != RIFF_FCC) + { + /* error if not RIFF */ + FLUID_LOG(FLUID_ERR, "Not a RIFF file"); + return FALSE; + } + + READID(sf, &chunk.id); /* load file ID */ + + if(chunk.id != SFBK_FCC) + { + /* error if not SFBK_ID */ + FLUID_LOG(FLUID_ERR, "Not a SoundFont file"); + return FALSE; + } + + if(chunk.size != sf->filesize - 8) + { + FLUID_LOG(FLUID_ERR, "SoundFont file size mismatch"); + return FALSE; + } + + /* Process INFO block */ + if(!read_listchunk(sf, &chunk)) + { + return FALSE; + } + + if(chunk.id != INFO_FCC) + { + FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting INFO chunk"); + return FALSE; + } + + if(!process_info(sf, chunk.size)) + { + return FALSE; + } + + /* Process sample chunk */ + if(!read_listchunk(sf, &chunk)) + { + return FALSE; + } + + if(chunk.id != SDTA_FCC) + { + FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting SAMPLE chunk"); + return FALSE; + } + + if(!process_sdta(sf, chunk.size)) + { + return FALSE; + } + + /* process HYDRA chunk */ + if(!read_listchunk(sf, &chunk)) + { + return FALSE; + } + + if(chunk.id != PDTA_FCC) + { + FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk"); + return FALSE; + } + + sf->hydrapos = sf->fcbs->ftell(sf->sffd); + sf->hydrasize = chunk.size; + + return TRUE; +} + +static int load_body(SFData *sf) +{ + if(sf->fcbs->fseek(sf->sffd, sf->hydrapos, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to seek to HYDRA position"); + return FALSE; + } + + if(!process_pdta(sf, sf->hydrasize)) + { + return FALSE; + } + + /* sort preset list by bank, preset # */ + sf->preset = fluid_list_sort(sf->preset, preset_compare_func); + + return TRUE; +} + +static int read_listchunk(SFData *sf, SFChunk *chunk) +{ + READCHUNK(sf, chunk); /* read list chunk */ + + if(chunk->id != LIST_FCC) /* error if ! list chunk */ + { + FLUID_LOG(FLUID_ERR, "Invalid chunk id in level 0 parse"); + return FALSE; + } + + READID(sf, &chunk->id); /* read id string */ + chunk->size -= 4; + return TRUE; +} + +static int process_info(SFData *sf, int size) +{ + SFChunk chunk; + union + { + char *chr; + uint32_t *fcc; + } item; + unsigned short ver; + + while(size > 0) + { + READCHUNK(sf, &chunk); + size -= 8; + + if(chunk.id == IFIL_FCC) + { + /* sound font version chunk? */ + if(chunk.size != 4) + { + FLUID_LOG(FLUID_ERR, "Sound font version info chunk has invalid size"); + return FALSE; + } + + READW(sf, ver); + sf->version.major = ver; + READW(sf, ver); + sf->version.minor = ver; + + if(sf->version.major < 2) + { + FLUID_LOG(FLUID_ERR, "Sound font version is %d.%d which is not" + " supported, convert to version 2.0x", + sf->version.major, sf->version.minor); + return FALSE; + } + + if(sf->version.major == 3) + { +#if !LIBSNDFILE_SUPPORT + FLUID_LOG(FLUID_WARN, + "Sound font version is %d.%d but fluidsynth was compiled without" + " support for (v3.x)", + sf->version.major, sf->version.minor); + return FALSE; +#endif + } + else if(sf->version.major > 2) + { + FLUID_LOG(FLUID_WARN, + "Sound font version is %d.%d which is newer than" + " what this version of fluidsynth was designed for (v2.0x)", + sf->version.major, sf->version.minor); + return FALSE; + } + } + else if(chunk.id == IVER_FCC) + { + /* ROM version chunk? */ + if(chunk.size != 4) + { + FLUID_LOG(FLUID_ERR, "ROM version info chunk has invalid size"); + return FALSE; + } + + READW(sf, ver); + sf->romver.major = ver; + READW(sf, ver); + sf->romver.minor = ver; + } + else if(chunkid(chunk.id) != UNKN_ID) + { + if((chunk.id != ICMT_FCC && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2)) + { + FLUID_LOG(FLUID_ERR, "INFO sub chunk %.4s has invalid chunk size of %d bytes", + (char*)&chunk.id, chunk.size); + return FALSE; + } + + /* alloc for chunk fcc and da chunk */ + if(!(item.fcc = FLUID_MALLOC(chunk.size + sizeof(uint32_t) + 1))) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + /* attach to INFO list, fluid_sffile_close will cleanup if FAIL occurs */ + sf->info = fluid_list_append(sf->info, item.fcc); + + /* save chunk fcc and update pointer to data value */ + *item.fcc++ = chunk.id; + + if(sf->fcbs->fread(item.chr, chunk.size, sf->sffd) == FLUID_FAILED) + { + return FALSE; + } + + /* force terminate info item */ + item.chr[chunk.size] = '\0'; + } + else + { + FLUID_LOG(FLUID_ERR, "Invalid chunk id in INFO chunk"); + return FALSE; + } + + size -= chunk.size; + } + + if(size < 0) + { + FLUID_LOG(FLUID_ERR, "INFO chunk size mismatch"); + return FALSE; + } + + return TRUE; +} + +static int process_sdta(SFData *sf, unsigned int size) +{ + SFChunk chunk; + + if(size == 0) + { + return TRUE; /* no sample data? */ + } + + /* read sub chunk */ + READCHUNK(sf, &chunk); + size -= 8; + + if(chunk.id != SMPL_FCC) + { + FLUID_LOG(FLUID_ERR, "Expected SMPL chunk found invalid id instead"); + return FALSE; + } + + /* SDTA chunk may also contain sm24 chunk for 24 bit samples + * (not yet supported), only an error if SMPL chunk size is + * greater than SDTA. */ + if(chunk.size > size) + { + FLUID_LOG(FLUID_ERR, "SDTA chunk size mismatch"); + return FALSE; + } + + /* sample data follows */ + sf->samplepos = sf->fcbs->ftell(sf->sffd); + + /* used to check validity of sample headers */ + sf->samplesize = chunk.size; + + FSKIP(sf, chunk.size); + size -= chunk.size; + + if(sf->version.major >= 2 && sf->version.minor >= 4) + { + /* any chance to find another chunk here? */ + if(size > 8) + { + /* read sub chunk */ + READCHUNK(sf, &chunk); + size -= 8; + + if(chunk.id == SM24_FCC) + { + int sm24size, sdtahalfsize; + + FLUID_LOG(FLUID_DBG, "Found SM24 chunk"); + + if(chunk.size > size) + { + FLUID_LOG(FLUID_WARN, "SM24 exceeds SDTA chunk, ignoring SM24"); + goto ret; // no error + } + + sdtahalfsize = sf->samplesize / 2; + /* + 1 byte in the case that half the size of smpl chunk is an odd value */ + sdtahalfsize += sdtahalfsize % 2; + sm24size = chunk.size; + + if(sdtahalfsize != sm24size) + { + FLUID_LOG(FLUID_WARN, "SM24 not equal to half the size of SMPL chunk (0x%X != " + "0x%X), ignoring SM24", + sm24size, sdtahalfsize); + goto ret; // no error + } + + /* sample data24 follows */ + sf->sample24pos = sf->fcbs->ftell(sf->sffd); + sf->sample24size = sm24size; + } + } + } + +ret: + FSKIP(sf, size); + + return TRUE; +} + +static int pdtahelper(SFData *sf, unsigned int expid, unsigned int reclen, SFChunk *chunk, int *size) +{ + READCHUNK(sf, chunk); + *size -= 8; + + if(chunk->id != expid) + { + FLUID_LOG(FLUID_ERR, "Expected PDTA sub-chunk '%.4s' found invalid id instead", (char*)&expid); + return FALSE; + } + + if(chunk->size % reclen) /* valid chunk size? */ + { + FLUID_LOG(FLUID_ERR, "'%.4s' chunk size is not a multiple of %d bytes", (char*)&expid, reclen); + return FALSE; + } + + if((*size -= chunk->size) < 0) + { + FLUID_LOG(FLUID_ERR, "'%.4s' chunk size exceeds remaining PDTA chunk size", (char*)&expid); + return FALSE; + } + + return TRUE; +} + +static int process_pdta(SFData *sf, int size) +{ + SFChunk chunk; + + if(!pdtahelper(sf, PHDR_FCC, SF_PHDR_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_phdr(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, PBAG_FCC, SF_BAG_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_pbag(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, PMOD_FCC, SF_MOD_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_pmod(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, PGEN_FCC, SF_GEN_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_pgen(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IHDR_FCC, SF_IHDR_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_ihdr(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IBAG_FCC, SF_BAG_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_ibag(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IMOD_FCC, SF_MOD_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_imod(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, IGEN_FCC, SF_GEN_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_igen(sf, chunk.size)) + { + return FALSE; + } + + if(!pdtahelper(sf, SHDR_FCC, SF_SHDR_SIZE, &chunk, &size)) + { + return FALSE; + } + + if(!load_shdr(sf, chunk.size)) + { + return FALSE; + } + + return TRUE; +} + +/* preset header loader */ +static int load_phdr(SFData *sf, unsigned int size) +{ + unsigned int i; + int i2; + SFPreset *preset, *prev_preset = NULL; + unsigned short pbag_idx, prev_pbag_idx = 0; + + if(size % SF_PHDR_SIZE || size == 0) + { + FLUID_LOG(FLUID_ERR, "Preset header chunk size is invalid"); + return FALSE; + } + + i = size / SF_PHDR_SIZE - 1; + + if(i == 0) + { + /* at least one preset + term record */ + FLUID_LOG(FLUID_WARN, "File contains no presets"); + FSKIP(sf, SF_PHDR_SIZE); + return TRUE; + } + + for(; i > 0; i--) + { + /* load all preset headers */ + if((preset = FLUID_NEW(SFPreset)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + sf->preset = fluid_list_append(sf->preset, preset); + preset->zone = NULL; /* In case of failure, fluid_sffile_close can cleanup */ + READSTR(sf, &preset->name); /* possible read failure ^ */ + READW(sf, preset->prenum); + READW(sf, preset->bank); + READW(sf, pbag_idx); + FSKIP(sf, 4); /* library ignored */ + FSKIP(sf, 4); /* genre ignored */ + FSKIP(sf, 4); /* morphology ignored */ + + if(prev_preset) + { + /* not first preset? */ + if(pbag_idx < prev_pbag_idx) + { + FLUID_LOG(FLUID_ERR, "Preset header indices not monotonic"); + return FALSE; + } + + i2 = pbag_idx - prev_pbag_idx; + + while(i2--) + { + prev_preset->zone = fluid_list_prepend(prev_preset->zone, NULL); + } + } + else if(pbag_idx > 0) /* 1st preset, warn if ofs >0 */ + { + FLUID_LOG(FLUID_WARN, "%d preset zones not referenced, discarding", pbag_idx); + } + + prev_preset = preset; /* update preset ptr */ + prev_pbag_idx = pbag_idx; + } + + FSKIP(sf, 24); + READW(sf, pbag_idx); /* Read terminal generator index */ + FSKIP(sf, 12); + + if(pbag_idx < prev_pbag_idx) + { + FLUID_LOG(FLUID_ERR, "Preset header indices not monotonic"); + return FALSE; + } + + i2 = pbag_idx - prev_pbag_idx; + + while(i2--) + { + prev_preset->zone = fluid_list_prepend(prev_preset->zone, NULL); + } + + return TRUE; +} + +/* preset bag loader */ +static int load_pbag(SFData *sf, int size) +{ + fluid_list_t *preset_list; + fluid_list_t *zone_list; + SFZone *z, *pz = NULL; + unsigned short genndx, modndx; + unsigned short pgenndx = 0, pmodndx = 0; + unsigned short i; + + if(size % SF_BAG_SIZE || size == 0) /* size is multiple of SF_BAG_SIZE? */ + { + FLUID_LOG(FLUID_ERR, "Preset bag chunk size is invalid"); + return FALSE; + } + + preset_list = sf->preset; + + /* traverse through presets */ + while(preset_list) + { + zone_list = ((SFPreset *)(preset_list->data))->zone; + + /* traverse preset's zones */ + while(zone_list) + { + if((size -= SF_BAG_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset bag chunk size mismatch"); + return FALSE; + } + + if((z = FLUID_NEW(SFZone)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + zone_list->data = z; + z->gen = NULL; /* Init gen and mod before possible failure, */ + z->mod = NULL; /* to ensure proper cleanup (fluid_sffile_close) */ + READW(sf, genndx); /* possible read failure ^ */ + READW(sf, modndx); + + if(pz) + { + /* if not first zone */ + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag modulator indices not monotonic"); + return FALSE; + } + + i = genndx - pgenndx; + + while(i--) + { + pz->gen = fluid_list_prepend(pz->gen, NULL); + } + + i = modndx - pmodndx; + + while(i--) + { + pz->mod = fluid_list_prepend(pz->mod, NULL); + } + } + + pz = z; /* update previous zone ptr */ + pgenndx = genndx; /* update previous zone gen index */ + pmodndx = modndx; /* update previous zone mod index */ + zone_list = fluid_list_next(zone_list); + } + + preset_list = fluid_list_next(preset_list); + } + + size -= SF_BAG_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Preset bag chunk size mismatch"); + return FALSE; + } + + READW(sf, genndx); + READW(sf, modndx); + + if(!pz) + { + if(genndx > 0) + { + FLUID_LOG(FLUID_WARN, "No preset generators and terminal index not 0"); + } + + if(modndx > 0) + { + FLUID_LOG(FLUID_WARN, "No preset modulators and terminal index not 0"); + } + + return TRUE; + } + + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Preset bag modulator indices not monotonic"); + return FALSE; + } + + i = genndx - pgenndx; + + while(i--) + { + pz->gen = fluid_list_prepend(pz->gen, NULL); + } + + i = modndx - pmodndx; + + while(i--) + { + pz->mod = fluid_list_prepend(pz->mod, NULL); + } + + return TRUE; +} + +/* preset modulator loader */ +static int load_pmod(SFData *sf, int size) +{ + fluid_list_t *preset_list; + fluid_list_t *zone_list; + fluid_list_t *mod_list; + SFMod *m; + + preset_list = sf->preset; + + while(preset_list) + { + /* traverse through all presets */ + zone_list = ((SFPreset *)(preset_list->data))->zone; + + while(zone_list) + { + /* traverse this preset's zones */ + mod_list = ((SFZone *)(zone_list->data))->mod; + + while(mod_list) + { + /* load zone's modulators */ + if((size -= SF_MOD_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset modulator chunk size mismatch"); + return FALSE; + } + + if((m = FLUID_NEW(SFMod)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + mod_list->data = m; + READW(sf, m->src); + READW(sf, m->dest); + READW(sf, m->amount); + READW(sf, m->amtsrc); + READW(sf, m->trans); + mod_list = fluid_list_next(mod_list); + } + + zone_list = fluid_list_next(zone_list); + } + + preset_list = fluid_list_next(preset_list); + } + + /* + If there isn't even a terminal record + Hmmm, the specs say there should be one, but.. + */ + if(size == 0) + { + return TRUE; + } + + size -= SF_MOD_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Preset modulator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_MOD_SIZE); /* terminal mod */ + + return TRUE; +} + +/* ------------------------------------------------------------------- + * preset generator loader + * generator (per preset) loading rules: + * Zones with no generators or modulators shall be annihilated + * Global zone must be 1st zone, discard additional ones (instrumentless zones) + * + * generator (per zone) loading rules (in order of decreasing precedence): + * KeyRange is 1st in list (if exists), else discard + * if a VelRange exists only preceded by a KeyRange, else discard + * if a generator follows an instrument discard it + * if a duplicate generator exists replace previous one + * ------------------------------------------------------------------- */ +int load_pgen(SFData *sf, int size) +{ + fluid_list_t *dup; + fluid_list_t *preset_list; + fluid_list_t *zone_list; + fluid_list_t *gen_list; + SFZone *zone; + SFGen *g; + SFPreset *preset; + SFGenAmount genval; + unsigned short genid; + int level, skip, drop, discarded; + + preset_list = sf->preset; + + while(preset_list) + { + preset = fluid_list_get(preset_list); + + /* traverse through all presets */ + discarded = FALSE; + zone_list = preset->zone; + + /* traverse preset's zones */ + while(zone_list) + { + zone = fluid_list_get(zone_list); + level = 0; + gen_list = zone->gen; + + while(gen_list) + { + /* load zone's generators */ + dup = NULL; + skip = FALSE; + drop = FALSE; + + if((size -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); + return FALSE; + } + + READW(sf, genid); + + if(genid == GEN_KEYRANGE) + { + /* nothing precedes */ + if(level == 0) + { + level = 1; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == GEN_VELRANGE) + { + /* only KeyRange precedes */ + if(level <= 1) + { + level = 2; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == GEN_INSTRUMENT) + { + /* inst is last gen */ + level = 3; + READW(sf, genval.uword); + } + else + { + level = 2; + + if(valid_preset_genid(genid)) + { + /* generator valid? */ + READW(sf, genval.sword); + dup = find_gen_by_id(genid, zone->gen); + } + else + { + skip = TRUE; + } + } + + if(!skip) + { + if(!dup) + { + /* if gen ! dup alloc new */ + if((g = FLUID_NEW(SFGen)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + gen_list->data = g; + g->id = genid; + } + else + { + g = (SFGen *)(dup->data); /* ptr to orig gen */ + drop = TRUE; + } + + g->amount = genval; + } + else + { + /* Skip this generator */ + discarded = TRUE; + drop = TRUE; + FSKIPW(sf); + } + + if(!drop) + { + gen_list = fluid_list_next(gen_list); /* next gen */ + } + else + { + SLADVREM(zone->gen, gen_list); /* drop place holder */ + } + + /* GEN_INSTRUMENT should be the last generator */ + if (level == 3) + { + break; + } + + } /* generator loop */ + + /* Anything below level 3 means it's a global zone. The global zone + * should always be the first zone in the list, so discard any + * other global zones we encounter */ + if(level < 3 && (zone_list != preset->zone)) + { + /* advance to next zone before deleting the current list element */ + zone_list = fluid_list_next(zone_list); + + FLUID_LOG(FLUID_WARN, "Preset '%s': Discarding invalid global zone", + preset->name); + preset->zone = fluid_list_remove(preset->zone, zone); + delete_zone(zone); + + /* we have already advanced the zone_list pointer, so continue with next zone */ + continue; + } + + /* All remaining generators are invalid and should be discarded + * (because they come after an instrument generator) */ + while(gen_list) + { + discarded = TRUE; + + if((size -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); + SLADVREM(zone->gen, gen_list); + } + + zone_list = fluid_list_next(zone_list); + } + + if(discarded) + { + FLUID_LOG(FLUID_WARN, + "Preset '%s': Some invalid generators were discarded", + preset->name); + } + + preset_list = fluid_list_next(preset_list); + } + + /* in case there isn't a terminal record */ + if(size == 0) + { + return TRUE; + } + + size -= SF_GEN_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Preset generator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); /* terminal gen */ + + return TRUE; +} + +/* instrument header loader */ +static int load_ihdr(SFData *sf, unsigned int size) +{ + unsigned int i; + int i2; + SFInst *inst, *prev_inst = NULL; /* ptr to current & previous instrument */ + unsigned short zndx, pzndx = 0; + + if(size % SF_IHDR_SIZE || size == 0) /* chunk size is valid? */ + { + FLUID_LOG(FLUID_ERR, "Instrument header has invalid size"); + return FALSE; + } + + size = size / SF_IHDR_SIZE - 1; + + if(size == 0) + { + /* at least one preset + term record */ + FLUID_LOG(FLUID_WARN, "File contains no instruments"); + FSKIP(sf, SF_IHDR_SIZE); + return TRUE; + } + + for(i = 0; i < size; i++) + { + /* load all instrument headers */ + if((inst = FLUID_NEW(SFInst)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + sf->inst = fluid_list_append(sf->inst, inst); + inst->zone = NULL; /* For proper cleanup if fail (fluid_sffile_close) */ + inst->idx = i; + READSTR(sf, &inst->name); /* Possible read failure ^ */ + READW(sf, zndx); + + if(prev_inst) + { + /* not first instrument? */ + if(zndx < pzndx) + { + FLUID_LOG(FLUID_ERR, "Instrument header indices not monotonic"); + return FALSE; + } + + i2 = zndx - pzndx; + + while(i2--) + { + prev_inst->zone = fluid_list_prepend(prev_inst->zone, NULL); + } + } + else if(zndx > 0) /* 1st inst, warn if ofs >0 */ + { + FLUID_LOG(FLUID_WARN, "%d instrument zones not referenced, discarding", zndx); + } + + pzndx = zndx; + prev_inst = inst; /* update instrument ptr */ + } + + FSKIP(sf, 20); + READW(sf, zndx); + + if(zndx < pzndx) + { + FLUID_LOG(FLUID_ERR, "Instrument header indices not monotonic"); + return FALSE; + } + + i2 = zndx - pzndx; + + while(i2--) + { + prev_inst->zone = fluid_list_prepend(prev_inst->zone, NULL); + } + + return TRUE; +} + +/* instrument bag loader */ +static int load_ibag(SFData *sf, int size) +{ + fluid_list_t *inst_list; + fluid_list_t *zone_list; + SFZone *z, *pz = NULL; + unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0; + int i; + + if(size % SF_BAG_SIZE || size == 0) /* size is multiple of SF_BAG_SIZE? */ + { + FLUID_LOG(FLUID_ERR, "Instrument bag chunk size is invalid"); + return FALSE; + } + + inst_list = sf->inst; + + while(inst_list) + { + /* traverse through inst */ + zone_list = ((SFInst *)(inst_list->data))->zone; + + while(zone_list) + { + /* load this inst's zones */ + if((size -= SF_BAG_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Instrument bag chunk size mismatch"); + return FALSE; + } + + if((z = FLUID_NEW(SFZone)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + zone_list->data = z; + z->gen = NULL; /* In case of failure, */ + z->mod = NULL; /* fluid_sffile_close can clean up */ + READW(sf, genndx); /* READW = possible read failure */ + READW(sf, modndx); + + if(pz) + { + /* if not first zone */ + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Instrument generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator indices not monotonic"); + return FALSE; + } + + i = genndx - pgenndx; + + while(i--) + { + pz->gen = fluid_list_prepend(pz->gen, NULL); + } + + i = modndx - pmodndx; + + while(i--) + { + pz->mod = fluid_list_prepend(pz->mod, NULL); + } + } + + pz = z; /* update previous zone ptr */ + pgenndx = genndx; + pmodndx = modndx; + zone_list = fluid_list_next(zone_list); + } + + inst_list = fluid_list_next(inst_list); + } + + size -= SF_BAG_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Instrument chunk size mismatch"); + return FALSE; + } + + READW(sf, genndx); + READW(sf, modndx); + + if(!pz) + { + /* in case that all are no zoners */ + if(genndx > 0) + { + FLUID_LOG(FLUID_WARN, "No instrument generators and terminal index not 0"); + } + + if(modndx > 0) + { + FLUID_LOG(FLUID_WARN, "No instrument modulators and terminal index not 0"); + } + + return TRUE; + } + + if(genndx < pgenndx) + { + FLUID_LOG(FLUID_ERR, "Instrument generator indices not monotonic"); + return FALSE; + } + + if(modndx < pmodndx) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator indices not monotonic"); + return FALSE; + } + + i = genndx - pgenndx; + + while(i--) + { + pz->gen = fluid_list_prepend(pz->gen, NULL); + } + + i = modndx - pmodndx; + + while(i--) + { + pz->mod = fluid_list_prepend(pz->mod, NULL); + } + + return TRUE; +} + +/* instrument modulator loader */ +static int load_imod(SFData *sf, int size) +{ + fluid_list_t *inst_list; + fluid_list_t *zone_list; + fluid_list_t *mod_list; + SFMod *m; + + inst_list = sf->inst; + + while(inst_list) + { + /* traverse through all inst */ + zone_list = ((SFInst *)(inst_list->data))->zone; + + while(zone_list) + { + /* traverse this inst's zones */ + mod_list = ((SFZone *)(zone_list->data))->mod; + + while(mod_list) + { + /* load zone's modulators */ + if((size -= SF_MOD_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator chunk size mismatch"); + return FALSE; + } + + if((m = FLUID_NEW(SFMod)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + mod_list->data = m; + READW(sf, m->src); + READW(sf, m->dest); + READW(sf, m->amount); + READW(sf, m->amtsrc); + READW(sf, m->trans); + mod_list = fluid_list_next(mod_list); + } + + zone_list = fluid_list_next(zone_list); + } + + inst_list = fluid_list_next(inst_list); + } + + /* + If there isn't even a terminal record + Hmmm, the specs say there should be one, but.. + */ + if(size == 0) + { + return TRUE; + } + + size -= SF_MOD_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "Instrument modulator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_MOD_SIZE); /* terminal mod */ + + return TRUE; +} + +/* load instrument generators (see load_pgen for loading rules) */ +int load_igen(SFData *sf, int size) +{ + fluid_list_t *dup; + fluid_list_t *inst_list; + fluid_list_t *zone_list; + fluid_list_t *gen_list; + SFZone *zone; + SFGen *g; + SFInst *inst; + SFGenAmount genval; + unsigned short genid; + int level, skip, drop, discarded; + + inst_list = sf->inst; + + /* traverse through all instruments */ + while(inst_list) + { + inst = fluid_list_get(inst_list); + + discarded = FALSE; + zone_list = inst->zone; + + /* traverse this instrument's zones */ + while(zone_list) + { + zone = fluid_list_get(zone_list); + + level = 0; + gen_list = zone->gen; + + while(gen_list) + { + /* load zone's generators */ + dup = NULL; + skip = FALSE; + drop = FALSE; + + if((size -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "IGEN chunk size mismatch"); + return FALSE; + } + + READW(sf, genid); + + if(genid == GEN_KEYRANGE) + { + /* nothing precedes */ + if(level == 0) + { + level = 1; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == GEN_VELRANGE) + { + /* only KeyRange precedes */ + if(level <= 1) + { + level = 2; + READB(sf, genval.range.lo); + READB(sf, genval.range.hi); + } + else + { + skip = TRUE; + } + } + else if(genid == GEN_SAMPLEID) + { + /* sample is last gen */ + level = 3; + READW(sf, genval.uword); + } + else + { + level = 2; + + if(valid_inst_genid(genid)) + { + /* gen valid? */ + READW(sf, genval.sword); + dup = find_gen_by_id(genid, zone->gen); + } + else + { + skip = TRUE; + } + } + + if(!skip) + { + if(!dup) + { + /* if gen ! dup alloc new */ + if((g = FLUID_NEW(SFGen)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + + gen_list->data = g; + g->id = genid; + } + else + { + g = (SFGen *)(dup->data); + drop = TRUE; + } + + g->amount = genval; + } + else + { + /* skip this generator */ + discarded = TRUE; + drop = TRUE; + FSKIPW(sf); + } + + if(!drop) + { + gen_list = fluid_list_next(gen_list); /* next gen */ + } + else + { + SLADVREM(zone->gen, gen_list); + } + + /* GEN_SAMPLEID should be last generator */ + if (level == 3) + { + break; + } + + } /* generator loop */ + + /* Anything below level 3 means it's a global zone. The global zone + * should always be the first zone in the list, so discard any + * other global zones we encounter */ + if(level < 3 && (zone_list != inst->zone)) + { + /* advance to next zone before deleting the current list element */ + zone_list = fluid_list_next(zone_list); + + FLUID_LOG(FLUID_WARN, "Instrument '%s': Discarding invalid global zone", + inst->name); + inst->zone = fluid_list_remove(inst->zone, zone); + delete_zone(zone); + + /* we have already advanced the zone_list pointer, so continue with next zone */ + continue; + } + + /* All remaining generators must be invalid and should be discarded + * (because they come after a sampleid generator) */ + while(gen_list) + { + discarded = TRUE; + + if((size -= SF_GEN_SIZE) < 0) + { + FLUID_LOG(FLUID_ERR, "Instrument generator chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); + SLADVREM(zone->gen, gen_list); + } + + zone_list = fluid_list_next(zone_list); /* next zone */ + } + + if(discarded) + { + FLUID_LOG(FLUID_WARN, + "Instrument '%s': Some invalid generators were discarded", + inst->name); + } + + inst_list = fluid_list_next(inst_list); + } + + /* for those non-terminal record cases, grr! */ + if(size == 0) + { + return TRUE; + } + + size -= SF_GEN_SIZE; + + if(size != 0) + { + FLUID_LOG(FLUID_ERR, "IGEN chunk size mismatch"); + return FALSE; + } + + FSKIP(sf, SF_GEN_SIZE); /* terminal gen */ + + return TRUE; +} + +/* sample header loader */ +static int load_shdr(SFData *sf, unsigned int size) +{ + unsigned int i; + SFSample *p; + + if(size % SF_SHDR_SIZE || size == 0) /* size is multiple of SHDR size? */ + { + FLUID_LOG(FLUID_ERR, "Sample header has invalid size"); + return FALSE; + } + + size = size / SF_SHDR_SIZE - 1; + + if(size == 0) + { + /* at least one sample + term record? */ + FLUID_LOG(FLUID_WARN, "File contains no samples"); + FSKIP(sf, SF_SHDR_SIZE); + return TRUE; + } + + /* load all sample headers */ + for(i = 0; i < size; i++) + { + if((p = FLUID_NEW(SFSample)) == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FALSE; + } + p->idx = i; + + sf->sample = fluid_list_prepend(sf->sample, p); + READSTR(sf, &p->name); + READD(sf, p->start); + READD(sf, p->end); + READD(sf, p->loopstart); + READD(sf, p->loopend); + READD(sf, p->samplerate); + READB(sf, p->origpitch); + READB(sf, p->pitchadj); + FSKIPW(sf); /* skip sample link */ + READW(sf, p->sampletype); + } + + FSKIP(sf, SF_SHDR_SIZE); /* skip terminal shdr */ + + return TRUE; +} + +void delete_preset(SFPreset *preset) +{ + fluid_list_t *entry; + SFZone *zone; + + if(!preset) + { + return; + } + + entry = preset->zone; + + while(entry) + { + zone = (SFZone *)fluid_list_get(entry); + delete_zone(zone); + entry = fluid_list_next(entry); + } + + delete_fluid_list(preset->zone); + + FLUID_FREE(preset); +} + +void delete_inst(SFInst *inst) +{ + fluid_list_t *entry; + SFZone *zone; + + if(!inst) + { + return; + } + + entry = inst->zone; + + while(entry) + { + zone = (SFZone *)fluid_list_get(entry); + delete_zone(zone); + entry = fluid_list_next(entry); + } + + delete_fluid_list(inst->zone); + + FLUID_FREE(inst); +} + + +/* Free all elements of a zone (Preset or Instrument) */ +void delete_zone(SFZone *zone) +{ + fluid_list_t *entry; + + if(!zone) + { + return; + } + + entry = zone->gen; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(zone->gen); + + entry = zone->mod; + + while(entry) + { + FLUID_FREE(fluid_list_get(entry)); + entry = fluid_list_next(entry); + } + + delete_fluid_list(zone->mod); + + FLUID_FREE(zone); +} + +/* preset sort function, first by bank, then by preset # */ +static int preset_compare_func(const void *a, const void *b) +{ + int aval, bval; + + aval = (int)(((const SFPreset *)a)->bank) << 16 | ((const SFPreset *)a)->prenum; + bval = (int)(((const SFPreset *)b)->bank) << 16 | ((const SFPreset *)b)->prenum; + + return (aval - bval); +} + +/* Find a generator by its id in the passed in list. + * + * @return pointer to SFGen if found, otherwise NULL + */ +static fluid_list_t *find_gen_by_id(int gen, fluid_list_t *genlist) +{ + /* is generator in gen list? */ + fluid_list_t *p; + + p = genlist; + + while(p) + { + if(p->data == NULL) + { + return NULL; + } + + if(gen == ((SFGen *)p->data)->id) + { + break; + } + + p = fluid_list_next(p); + } + + return p; +} + +/* check validity of instrument generator */ +static int valid_inst_genid(unsigned short genid) +{ + size_t i; + + /* OVERRIDEROOTKEY is the last official generator, everything + * following it are generators internal to FluidSynth and will + * never appear in a SoundFont file. */ + if(genid > GEN_OVERRIDEROOTKEY) + { + return FALSE; + } + + for(i = 0; i < FLUID_N_ELEMENTS(invalid_inst_gen); i++) + { + if (invalid_inst_gen[i] == genid) + { + return FALSE; + } + } + + return TRUE; +} + +/* check validity of preset generator */ +static int valid_preset_genid(unsigned short genid) +{ + size_t i; + + if(!valid_inst_genid(genid)) + { + return FALSE; + } + + for(i = 0; i < FLUID_N_ELEMENTS(invalid_preset_gen); i++) + { + if (invalid_preset_gen[i] == genid) + { + return FALSE; + } + } + + return TRUE; +} + + +static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24) +{ + short *loaded_data = NULL; + char *loaded_data24 = NULL; + unsigned int num_samples; + + fluid_return_val_if_fail((end + 1) > start , -1); + + num_samples = (end + 1) - start; + + if((start * sizeof(short) > sf->samplesize) || (end * sizeof(short) > sf->samplesize)) + { + FLUID_LOG(FLUID_ERR, "Sample offsets exceed sample data chunk"); + goto error_exit; + } + + /* Load 16-bit sample data */ + if(sf->fcbs->fseek(sf->sffd, sf->samplepos + (start * sizeof(short)), SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to seek to sample position"); + goto error_exit; + } + + loaded_data = FLUID_ARRAY(short, num_samples); + + if(loaded_data == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + if(sf->fcbs->fread(loaded_data, num_samples * sizeof(short), sf->sffd) == FLUID_FAILED) + { +#if FLUID_VERSION_CHECK(FLUIDSYNTH_VERSION_MAJOR, FLUIDSYNTH_VERSION_MINOR, FLUIDSYNTH_VERSION_MICRO) < FLUID_VERSION_CHECK(2,2,0) + if((int)(num_samples * sizeof(short)) < 0) + { + FLUID_LOG(FLUID_INFO, + "This SoundFont seems to be bigger than 2GB, which is not supported in this version of fluidsynth. " + "You need to use at least fluidsynth 2.2.0"); + } +#endif + FLUID_LOG(FLUID_ERR, "Failed to read sample data"); + goto error_exit; + } + + /* If this machine is big endian, byte swap the 16 bit samples */ + if(FLUID_IS_BIG_ENDIAN) + { + unsigned int i; + + for(i = 0; i < num_samples; i++) + { + loaded_data[i] = FLUID_LE16TOH(loaded_data[i]); + } + } + + *data = loaded_data; + + /* Optionally load additional 8 bit sample data for 24-bit support. Any failures while loading + * the 24-bit sample data will be logged as errors but won't prevent the sample reading to + * fail, as sound output is still possible with the 16-bit sample data. */ + if(sf->sample24pos) + { + if((start > sf->sample24size) || (end > sf->sample24size)) + { + FLUID_LOG(FLUID_ERR, "Sample offsets exceed 24-bit sample data chunk"); + goto error24_exit; + } + + if(sf->fcbs->fseek(sf->sffd, sf->sample24pos + start, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to seek position for 24-bit sample data in data file"); + goto error24_exit; + } + + loaded_data24 = FLUID_ARRAY(char, num_samples); + + if(loaded_data24 == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory reading 24-bit sample data"); + goto error24_exit; + } + + if(sf->fcbs->fread(loaded_data24, num_samples, sf->sffd) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to read 24-bit sample data"); + goto error24_exit; + } + } + + *data24 = loaded_data24; + + return num_samples; + +error24_exit: + FLUID_LOG(FLUID_WARN, "Ignoring 24-bit sample data, sound quality might suffer"); + FLUID_FREE(loaded_data24); + *data24 = NULL; + return num_samples; + +error_exit: + FLUID_FREE(loaded_data); + FLUID_FREE(loaded_data24); + return -1; +} + + +/* Ogg Vorbis loading and decompression */ +#if LIBSNDFILE_SUPPORT + +/* Virtual file access routines to allow loading individually compressed + * samples from the Soundfont sample data chunk using the file callbacks + * passing in during opening of the file */ +typedef struct _sfvio_data_t +{ + SFData *sffile; + sf_count_t start; /* start byte offset of compressed data */ + sf_count_t end; /* end byte offset of compressed data */ + sf_count_t offset; /* current virtual file offset from start byte offset */ + +} sfvio_data_t; + +static sf_count_t sfvio_get_filelen(void *user_data) +{ + sfvio_data_t *data = user_data; + + return (data->end + 1) - data->start; +} + +static sf_count_t sfvio_seek(sf_count_t offset, int whence, void *user_data) +{ + sfvio_data_t *data = user_data; + SFData *sf = data->sffile; + sf_count_t new_offset; + + switch(whence) + { + case SEEK_SET: + new_offset = offset; + break; + + case SEEK_CUR: + new_offset = data->offset + offset; + break; + + case SEEK_END: + new_offset = sfvio_get_filelen(user_data) + offset; + break; + + default: + goto fail; /* proper error handling not possible?? */ + } + + new_offset += data->start; + fluid_rec_mutex_lock(sf->mtx); + if (data->start <= new_offset && new_offset <= data->end && + sf->fcbs->fseek(sf->sffd, new_offset, SEEK_SET) != FLUID_FAILED) + { + data->offset = new_offset - data->start; + } + fluid_rec_mutex_unlock(sf->mtx); + +fail: + return data->offset; +} + +static sf_count_t sfvio_read(void *ptr, sf_count_t count, void *user_data) +{ + sfvio_data_t *data = user_data; + SFData *sf = data->sffile; + sf_count_t remain; + + remain = sfvio_get_filelen(user_data) - data->offset; + + if(count > remain) + { + count = remain; + } + + if(count == 0) + { + return count; + } + + fluid_rec_mutex_lock(sf->mtx); + if (sf->fcbs->fseek(sf->sffd, data->start + data->offset, SEEK_SET) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "This should never happen: fseek failed in sfvoid_read()"); + count = 0; + } + else + { + if (sf->fcbs->fread(ptr, count, sf->sffd) == FLUID_FAILED) + { + FLUID_LOG(FLUID_ERR, "Failed to read compressed sample data"); + count = 0; + } + } + fluid_rec_mutex_unlock(sf->mtx); + + data->offset += count; + + return count; +} + +static sf_count_t sfvio_tell(void *user_data) +{ + sfvio_data_t *data = user_data; + + return data->offset; +} + +int IsSndFilePresent(); + +/** + * Read Ogg Vorbis compressed data from the Soundfont and decompress it, returning the number of samples + * in the decompressed WAV. Only 16-bit mono samples are supported. + * + * Note that this function takes byte indices for start and end source data. The sample headers in SF3 + * files use byte indices, so those pointers can be passed directly to this function. + * + * This function uses a virtual file structure in order to read the Ogg Vorbis + * data from arbitrary locations in the source file. + */ +static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data) +{ + SNDFILE *sndfile; + SF_INFO sfinfo; + SF_VIRTUAL_IO sfvio = + { + sfvio_get_filelen, + sfvio_seek, + sfvio_read, + NULL, + sfvio_tell + }; + sfvio_data_t sfdata; + short *wav_data = NULL; + + if (!IsSndFilePresent()) + { + FLUID_LOG(FLUID_ERR, "Ogg Vorbis support not compiled in"); + return -1; + } + + if((start_byte > sf->samplesize) || (end_byte > sf->samplesize)) + { + FLUID_LOG(FLUID_ERR, "Ogg Vorbis data offsets exceed sample data chunk"); + return -1; + } + + // Initialize file position indicator and SF_INFO structure + sfdata.sffile = sf; + sfdata.start = sf->samplepos + start_byte; + sfdata.end = sf->samplepos + end_byte; + sfdata.offset = -1; + + /* Seek to sfdata.start, the beginning of Ogg Vorbis data in Soundfont */ + sfvio_seek(0, SEEK_SET, &sfdata); + if (sfdata.offset != 0) + { + FLUID_LOG(FLUID_ERR, "Failed to seek to compressed sample position"); + return -1; + } + + FLUID_MEMSET(&sfinfo, 0, sizeof(sfinfo)); + + // Open sample as a virtual file + sndfile = sf_open_virtual(&sfvio, SFM_READ, &sfinfo, &sfdata); + + if(!sndfile) + { + FLUID_LOG(FLUID_ERR, "sf_open_virtual(): %s", sf_strerror(sndfile)); + return -1; + } + + // Empty sample + if(sfinfo.frames <= 0 || sfinfo.channels <= 0) + { + FLUID_LOG(FLUID_DBG, "Empty decompressed sample"); + *data = NULL; + sf_close(sndfile); + return 0; + } + + // Mono sample + if(sfinfo.channels != 1) + { + FLUID_LOG(FLUID_DBG, "Unsupported channel count %d in ogg sample", sfinfo.channels); + goto error_exit; + } + + if((sfinfo.format & SF_FORMAT_OGG) == 0) + { + FLUID_LOG(FLUID_WARN, "OGG sample is not OGG compressed, this is not officially supported"); + } + + wav_data = FLUID_ARRAY(short, sfinfo.frames * sfinfo.channels); + + if(!wav_data) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_exit; + } + + /* Automatically decompresses the Ogg Vorbis data to 16-bit PCM */ + if(sf_readf_short(sndfile, wav_data, sfinfo.frames) < sfinfo.frames) + { + FLUID_LOG(FLUID_DBG, "Decompression failed!"); + FLUID_LOG(FLUID_ERR, "sf_readf_short(): %s", sf_strerror(sndfile)); + goto error_exit; + } + + sf_close(sndfile); + + *data = wav_data; + + return sfinfo.frames; + +error_exit: + FLUID_FREE(wav_data); + sf_close(sndfile); + return -1; +} +#else +static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data) +{ + return -1; +} +#endif diff --git a/thirdparty/fluidsynth/src/sfloader/fluid_sffile.h b/thirdparty/fluidsynth/src/sfloader/fluid_sffile.h new file mode 100644 index 000000000..5275c6252 --- /dev/null +++ b/thirdparty/fluidsynth/src/sfloader/fluid_sffile.h @@ -0,0 +1,194 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont loading code borrowed from Smurf SoundFont Editor by Josh Green + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_SFFILE_H +#define _FLUID_SFFILE_H + + +#include "fluid_gen.h" +#include "fluid_list.h" +#include "fluid_mod.h" +#include "fluidsynth.h" +#include "fluid_sys.h" + + +/* Sound Font structure defines */ + +/* Forward declarations */ +typedef union _SFGenAmount SFGenAmount; +typedef struct _SFVersion SFVersion; +typedef struct _SFMod SFMod; +typedef struct _SFGen SFGen; +typedef struct _SFZone SFZone; +typedef struct _SFSample SFSample; +typedef struct _SFInst SFInst; +typedef struct _SFPreset SFPreset; +typedef struct _SFData SFData; +typedef struct _SFChunk SFChunk; + + +struct _SFVersion +{ + /* version structure */ + unsigned short major; + unsigned short minor; +}; + +struct _SFMod +{ + /* Modulator structure */ + unsigned short src; /* source modulator */ + unsigned short dest; /* destination generator */ + signed short amount; /* signed, degree of modulation */ + unsigned short amtsrc; /* second source controls amnt of first */ + unsigned short trans; /* transform applied to source */ +}; + +union _SFGenAmount /* Generator amount structure */ +{ + signed short sword; /* signed 16 bit value */ + unsigned short uword; /* unsigned 16 bit value */ + struct + { + unsigned char lo; /* low value for ranges */ + unsigned char hi; /* high value for ranges */ + } range; +}; + +struct _SFGen +{ + /* Generator structure */ + unsigned short id; /* generator ID */ + SFGenAmount amount; /* generator value */ +}; + +struct _SFZone +{ + /* Sample/instrument zone structure */ + fluid_list_t *gen; /* list of generators */ + fluid_list_t *mod; /* list of modulators */ +}; + +struct _SFSample +{ + /* Sample structure */ + char name[21]; /* Name of sample */ + int idx; /* Index of this instrument in the Soundfont */ + unsigned int start; /* Offset in sample area to start of sample */ + unsigned int end; /* Offset from start to end of sample, + this is the last point of the + sample, the SF spec has this as the + 1st point after, corrected on + load/save */ + unsigned int loopstart; /* Offset from start to start of loop */ + unsigned int loopend; /* Offset from start to end of loop, + marks the first point after loop, + whose sample value is ideally + equivalent to loopstart */ + unsigned int samplerate; /* Sample rate recorded at */ + unsigned char origpitch; /* root midi key number */ + signed char pitchadj; /* pitch correction in cents */ + unsigned short sampletype; /* 1 mono,2 right,4 left,linked 8,0x8000=ROM */ + fluid_sample_t *fluid_sample; /* Imported sample (fixed up in fluid_defsfont_load) */ +}; + +struct _SFInst +{ + /* Instrument structure */ + char name[21]; /* Name of instrument */ + int idx; /* Index of this instrument in the Soundfont */ + fluid_list_t *zone; /* list of instrument zones */ +}; + +struct _SFPreset +{ + /* Preset structure */ + char name[21]; /* preset name */ + unsigned short prenum; /* preset number */ + unsigned short bank; /* bank number */ + fluid_list_t *zone; /* list of preset zones */ +}; + +/* NOTE: sffd is also used to determine if sound font is new (NULL) */ +struct _SFData +{ + /* Sound font data structure */ + SFVersion version; /* sound font version */ + SFVersion romver; /* ROM version */ + + unsigned int filesize; + + unsigned int samplepos; /* position within sffd of the sample chunk */ + unsigned int samplesize; /* length within sffd of the sample chunk */ + + unsigned int sample24pos; /* position within sffd of the sm24 chunk, set to zero if no 24 bit + sample support */ + unsigned int sample24size; /* length within sffd of the sm24 chunk */ + + unsigned int hydrapos; + unsigned int hydrasize; + + char *fname; /* file name */ + FILE *sffd; /* loaded sfont file descriptor */ + const fluid_file_callbacks_t *fcbs; /* file callbacks used to read this file */ + + fluid_rec_mutex_t mtx; /* this mutex can be used to synchronize calls to fcbs when using multiple threads (e.g. SF3 loading) */ + + fluid_list_t *info; /* linked list of info strings (1st byte is ID) */ + fluid_list_t *preset; /* linked list of preset info */ + fluid_list_t *inst; /* linked list of instrument info */ + fluid_list_t *sample; /* linked list of sample info */ +}; + +/* functions */ + + +/*-----------------------------------sffile.h----------------------------*/ +/* + File structures and routines (used to be in sffile.h) +*/ + +/* sfont file data structures */ +struct _SFChunk +{ + /* RIFF file chunk structure */ + unsigned int id; /* chunk id */ + unsigned int size; /* size of the following chunk */ +}; + +/* Public functions */ +SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs); +void fluid_sffile_close(SFData *sf); +int fluid_sffile_parse_presets(SFData *sf); +int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end, + int sample_type, short **data, char **data24); + + +/* extern only for unit test purposes */ +int load_igen(SFData *sf, int size); +int load_pgen(SFData *sf, int size); +void delete_preset(SFPreset *preset); +void delete_inst(SFInst *inst); +void delete_zone(SFZone *zone); + +#endif /* _FLUID_SFFILE_H */ diff --git a/thirdparty/fluidsynth/src/sfloader/fluid_sfont.c b/thirdparty/fluidsynth/src/sfloader/fluid_sfont.c new file mode 100644 index 000000000..26dbac650 --- /dev/null +++ b/thirdparty/fluidsynth/src/sfloader/fluid_sfont.c @@ -0,0 +1,850 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sfont.h" +#include "fluid_sys.h" + + +void *default_fopen(const char *path) +{ + const char* msg; + FILE* handle = fluid_file_open(path, &msg); + + if(handle == NULL) + { + FLUID_LOG(FLUID_ERR, "fluid_sfloader_load(): Failed to open '%s': %s", path, msg); + } + + return handle; +} + +int default_fclose(void *handle) +{ + return FLUID_FCLOSE((FILE *)handle) == 0 ? FLUID_OK : FLUID_FAILED; +} + +fluid_long_long_t default_ftell(void *handle) +{ + return FLUID_FTELL((FILE *)handle); +} + +#ifdef WIN32 + #define PRIi64 "%I64d" +#else + #define PRIi64 "%lld" +#endif + +int safe_fread(void *buf, fluid_long_long_t count, void *fd) +{ + if(FLUID_FREAD(buf, (size_t)count, 1, (FILE *)fd) != 1) + { + if(feof((FILE *)fd)) + { + FLUID_LOG(FLUID_ERR, "EOF while attempting to read " PRIi64 " bytes", count); + } + else + { + FLUID_LOG(FLUID_ERR, "File read failed"); + } + + return FLUID_FAILED; + } + + return FLUID_OK; +} + +int safe_fseek(void *fd, fluid_long_long_t ofs, int whence) +{ + if(FLUID_FSEEK((FILE *)fd, ofs, whence) != 0) + { + FLUID_LOG(FLUID_ERR, "File seek failed with offset = " PRIi64 " and whence = %d", ofs, whence); + return FLUID_FAILED; + } + + return FLUID_OK; +} + +#undef PRIi64 + +/** + * Creates a new SoundFont loader. + * + * @param load Pointer to a function that provides a #fluid_sfont_t (see #fluid_sfloader_load_t). + * @param free Pointer to a function that destroys this instance (see #fluid_sfloader_free_t). + * Unless any private data needs to be freed it is sufficient to set this to delete_fluid_sfloader(). + * + * @return the SoundFont loader instance on success, NULL otherwise. + */ +fluid_sfloader_t *new_fluid_sfloader(fluid_sfloader_load_t load, fluid_sfloader_free_t free) +{ + fluid_sfloader_t *loader; + + fluid_return_val_if_fail(load != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + loader = FLUID_NEW(fluid_sfloader_t); + + if(loader == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(loader, 0, sizeof(*loader)); + + loader->load = load; + loader->free = free; + fluid_sfloader_set_callbacks(loader, + default_fopen, + safe_fread, + safe_fseek, + default_ftell, + default_fclose); + + return loader; +} + +/** + * Frees a SoundFont loader created with new_fluid_sfloader(). + * + * @param loader The SoundFont loader instance to free. + */ +void delete_fluid_sfloader(fluid_sfloader_t *loader) +{ + fluid_return_if_fail(loader != NULL); + + FLUID_FREE(loader); +} + +/** + * Specify private data to be used by #fluid_sfloader_load_t. + * + * @param loader The SoundFont loader instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sfloader_set_data(fluid_sfloader_t *loader, void *data) +{ + fluid_return_val_if_fail(loader != NULL, FLUID_FAILED); + + loader->data = data; + return FLUID_OK; +} + +/** + * Obtain private data previously set with fluid_sfloader_set_data(). + * + * @param loader The SoundFont loader instance. + * @return The private data or NULL if none explicitly set before. + */ +void *fluid_sfloader_get_data(fluid_sfloader_t *loader) +{ + fluid_return_val_if_fail(loader != NULL, NULL); + + return loader->data; +} + +/** + * Set custom callbacks to be used upon soundfont loading. + * + * @param loader The SoundFont loader instance. + * @param open A function implementing #fluid_sfloader_callback_open_t. + * @param read A function implementing #fluid_sfloader_callback_read_t. + * @param seek A function implementing #fluid_sfloader_callback_seek_t. + * @param tell A function implementing #fluid_sfloader_callback_tell_t. + * @param close A function implementing #fluid_sfloader_callback_close_t. + * @return #FLUID_OK if the callbacks have been successfully set, #FLUID_FAILED otherwise. + * + * Useful for loading a soundfont from memory, see \a doc/fluidsynth_sfload_mem.c as an example. + * + */ +int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader, + fluid_sfloader_callback_open_t open, + fluid_sfloader_callback_read_t read, + fluid_sfloader_callback_seek_t seek, + fluid_sfloader_callback_tell_t tell, + fluid_sfloader_callback_close_t close) +{ + fluid_file_callbacks_t *cb; + + fluid_return_val_if_fail(loader != NULL, FLUID_FAILED); + fluid_return_val_if_fail(open != NULL, FLUID_FAILED); + fluid_return_val_if_fail(read != NULL, FLUID_FAILED); + fluid_return_val_if_fail(seek != NULL, FLUID_FAILED); + fluid_return_val_if_fail(tell != NULL, FLUID_FAILED); + fluid_return_val_if_fail(close != NULL, FLUID_FAILED); + + cb = &loader->file_callbacks; + + cb->fopen = open; + cb->fread = read; + cb->fseek = seek; + cb->ftell = tell; + cb->fclose = close; + + // NOTE: if we ever make the instpatch loader public, this may return FLUID_FAILED + return FLUID_OK; +} + +/** + * Creates a new virtual SoundFont instance structure. + * + * @param get_name A function implementing #fluid_sfont_get_name_t. + * @param get_preset A function implementing #fluid_sfont_get_preset_t. + * @param iter_start A function implementing #fluid_sfont_iteration_start_t, or NULL if preset iteration not needed. + * @param iter_next A function implementing #fluid_sfont_iteration_next_t, or NULL if preset iteration not needed. + * @param free A function implementing #fluid_sfont_free_t. + * @return The soundfont instance on success or NULL otherwise. + */ +fluid_sfont_t *new_fluid_sfont(fluid_sfont_get_name_t get_name, + fluid_sfont_get_preset_t get_preset, + fluid_sfont_iteration_start_t iter_start, + fluid_sfont_iteration_next_t iter_next, + fluid_sfont_free_t free) +{ + fluid_sfont_t *sfont; + + fluid_return_val_if_fail(get_name != NULL, NULL); + fluid_return_val_if_fail(get_preset != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + sfont = FLUID_NEW(fluid_sfont_t); + + if(sfont == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(sfont, 0, sizeof(*sfont)); + + sfont->get_name = get_name; + sfont->get_preset = get_preset; + sfont->iteration_start = iter_start; + sfont->iteration_next = iter_next; + sfont->free = free; + + return sfont; +} + +/** + * Set private data to use with a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sfont_set_data(fluid_sfont_t *sfont, void *data) +{ + fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); + + sfont->data = data; + return FLUID_OK; +} + +/** + * Retrieve the private data of a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @return The private data or NULL if none explicitly set before. + */ +void *fluid_sfont_get_data(fluid_sfont_t *sfont) +{ + fluid_return_val_if_fail(sfont != NULL, NULL); + + return sfont->data; +} + +/** + * Retrieve the unique ID of a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @return The SoundFont ID. + */ +int fluid_sfont_get_id(fluid_sfont_t *sfont) +{ + return sfont->id; +} + +/** + * Retrieve the name of a SoundFont instance. + * + * @param sfont The SoundFont instance. + * @return The name of the SoundFont. + */ +const char *fluid_sfont_get_name(fluid_sfont_t *sfont) +{ + return sfont->get_name(sfont); +} + +/** + * Retrieve the preset assigned the a SoundFont instance for the given bank and preset number. + * + * @param sfont The SoundFont instance. + * @param bank bank number of the preset + * @param prenum program number of the preset + * @return The preset instance or NULL if none found. + */ +fluid_preset_t *fluid_sfont_get_preset(fluid_sfont_t *sfont, int bank, int prenum) +{ + return sfont->get_preset(sfont, bank, prenum); +} + + +/** + * Starts / re-starts virtual preset iteration in a SoundFont. + * + * @param sfont Virtual SoundFont instance + */ +void fluid_sfont_iteration_start(fluid_sfont_t *sfont) +{ + fluid_return_if_fail(sfont != NULL); + fluid_return_if_fail(sfont->iteration_start != NULL); + + sfont->iteration_start(sfont); +} + +/** + * Virtual SoundFont preset iteration function. + * + * Returns preset information to the caller and advances the + * internal iteration state to the next preset for subsequent calls. + * @param sfont The SoundFont instance. + * @return NULL when no more presets are available, otherwise the a pointer to the current preset + */ +fluid_preset_t *fluid_sfont_iteration_next(fluid_sfont_t *sfont) +{ + fluid_return_val_if_fail(sfont != NULL, NULL); + fluid_return_val_if_fail(sfont->iteration_next != NULL, NULL); + + return sfont->iteration_next(sfont); +} + +/** + * Destroys a SoundFont instance created with new_fluid_sfont(). + * + * @param sfont The SoundFont instance to destroy. + * @return Always returns 0. + * + * Implements #fluid_sfont_free_t. + * + */ +int delete_fluid_sfont(fluid_sfont_t *sfont) +{ + fluid_return_val_if_fail(sfont != NULL, 0); + + FLUID_FREE(sfont); + return 0; +} + +/** + * Create a virtual SoundFont preset instance. + * + * @param parent_sfont The SoundFont instance this preset shall belong to + * @param get_name A function implementing #fluid_preset_get_name_t + * @param get_bank A function implementing #fluid_preset_get_banknum_t + * @param get_num A function implementing #fluid_preset_get_num_t + * @param noteon A function implementing #fluid_preset_noteon_t + * @param free A function implementing #fluid_preset_free_t + * @return The preset instance on success, NULL otherwise. + */ +fluid_preset_t *new_fluid_preset(fluid_sfont_t *parent_sfont, + fluid_preset_get_name_t get_name, + fluid_preset_get_banknum_t get_bank, + fluid_preset_get_num_t get_num, + fluid_preset_noteon_t noteon, + fluid_preset_free_t free) +{ + fluid_preset_t *preset; + + fluid_return_val_if_fail(parent_sfont != NULL, NULL); + fluid_return_val_if_fail(get_name != NULL, NULL); + fluid_return_val_if_fail(get_bank != NULL, NULL); + fluid_return_val_if_fail(get_num != NULL, NULL); + fluid_return_val_if_fail(noteon != NULL, NULL); + fluid_return_val_if_fail(free != NULL, NULL); + + preset = FLUID_NEW(fluid_preset_t); + + if(preset == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(preset, 0, sizeof(*preset)); + + preset->sfont = parent_sfont; + preset->get_name = get_name; + preset->get_banknum = get_bank; + preset->get_num = get_num; + preset->noteon = noteon; + preset->free = free; + + return preset; +} + +/** + * Set private data to use with a SoundFont preset instance. + * + * @param preset The SoundFont preset instance. + * @param data The private data to store. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_preset_set_data(fluid_preset_t *preset, void *data) +{ + fluid_return_val_if_fail(preset != NULL, FLUID_FAILED); + + preset->data = data; + return FLUID_OK; +} + +/** + * Retrieve the private data of a SoundFont preset instance. + * + * @param preset The SoundFont preset instance. + * @return The private data or NULL if none explicitly set before. + */ +void *fluid_preset_get_data(fluid_preset_t *preset) +{ + fluid_return_val_if_fail(preset != NULL, NULL); + + return preset->data; +} + +/** + * Retrieves the presets name by executing the \p get_name function + * provided on its creation. + * + * @param preset The SoundFont preset instance. + * @return Pointer to a NULL-terminated string containing the presets name. + */ +const char *fluid_preset_get_name(fluid_preset_t *preset) +{ + return preset->get_name(preset); +} + +/** + * Retrieves the presets bank number by executing the \p get_bank function + * provided on its creation. + * + * @param preset The SoundFont preset instance. + * @return The bank number of \p preset. + */ +int fluid_preset_get_banknum(fluid_preset_t *preset) +{ + return preset->get_banknum(preset); +} + +/** + * Retrieves the presets (instrument) number by executing the \p get_num function + * provided on its creation. + * + * @param preset The SoundFont preset instance. + * @return The number of \p preset. + */ +int fluid_preset_get_num(fluid_preset_t *preset) +{ + return preset->get_num(preset); +} + +/** + * Retrieves the presets parent SoundFont instance. + * + * @param preset The SoundFont preset instance. + * @return The parent SoundFont of \p preset. + */ +fluid_sfont_t *fluid_preset_get_sfont(fluid_preset_t *preset) +{ + return preset->sfont; +} + +/** + * Destroys a SoundFont preset instance created with new_fluid_preset(). + * + * @param preset The SoundFont preset instance to destroy. + * + * Implements #fluid_preset_free_t. + * + */ +void delete_fluid_preset(fluid_preset_t *preset) +{ + fluid_return_if_fail(preset != NULL); + + FLUID_FREE(preset); +} + +/** + * Create a new sample instance. + * + * @return The sample on success, NULL otherwise. + */ +fluid_sample_t * +new_fluid_sample() +{ + fluid_sample_t *sample = NULL; + + sample = FLUID_NEW(fluid_sample_t); + + if(sample == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(sample, 0, sizeof(*sample)); + + return sample; +} + +/** + * Destroy a sample instance previously created with new_fluid_sample(). + * + * @param sample The sample to destroy. + */ +void +delete_fluid_sample(fluid_sample_t *sample) +{ + fluid_return_if_fail(sample != NULL); + + if(sample->auto_free) + { + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + } + + FLUID_FREE(sample); +} + +/** + * Returns the size of the fluid_sample_t structure. + * + * @return Size of fluid_sample_t in bytes + * + * Useful in low latency scenarios e.g. to allocate a pool of samples. + * + * @note It is recommend to zero initialize the memory before using the object. + * + * @warning Do NOT allocate samples on the stack and assign them to a voice! + */ +size_t fluid_sample_sizeof() +{ + return sizeof(fluid_sample_t); +} + +/** + * Set the name of a SoundFont sample. + * + * @param sample SoundFont sample + * @param name Name to assign to sample (20 chars in length + zero terminator) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_sample_set_name(fluid_sample_t *sample, const char *name) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + + FLUID_STRNCPY(sample->name, name, sizeof(sample->name)); + return FLUID_OK; +} + +/** + * Assign sample data to a SoundFont sample. + * + * @param sample SoundFont sample + * @param data Buffer containing 16 bit (mono-)audio sample data + * @param data24 If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples + * @param nbframes Number of samples in \a data + * @param sample_rate Sampling rate of the sample data + * @param copy_data TRUE to copy the sample data (and automatically free it upon delete_fluid_sample()), FALSE to use it directly (and not free it) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note If \a copy_data is FALSE, data should have 8 unused frames at start + * and 8 unused frames at the end and \a nbframes should be >=48 + */ +int +fluid_sample_set_sound_data(fluid_sample_t *sample, + short *data, + char *data24, + unsigned int nbframes, + unsigned int sample_rate, + short copy_data + ) +{ + /* the number of samples before the start and after the end */ +#define SAMPLE_LOOP_MARGIN 8U + + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(data != NULL, FLUID_FAILED); + fluid_return_val_if_fail(nbframes != 0, FLUID_FAILED); + + /* in case we already have some data */ + if((sample->data != NULL || sample->data24 != NULL) && sample->auto_free) + { + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + } + + sample->data = NULL; + sample->data24 = NULL; + + if(copy_data) + { + unsigned int storedNbFrames; + + /* nbframes should be >= 48 (SoundFont specs) */ + storedNbFrames = nbframes; + + if(storedNbFrames < 48) + { + storedNbFrames = 48; + } + + storedNbFrames += 2 * SAMPLE_LOOP_MARGIN; + + sample->data = FLUID_ARRAY(short, storedNbFrames); + + if(sample->data == NULL) + { + goto error_rec; + } + + FLUID_MEMSET(sample->data, 0, storedNbFrames * sizeof(short)); + FLUID_MEMCPY(sample->data + SAMPLE_LOOP_MARGIN, data, nbframes * sizeof(short)); + + if(data24 != NULL) + { + sample->data24 = FLUID_ARRAY(char, storedNbFrames); + + if(sample->data24 == NULL) + { + goto error_rec; + } + + FLUID_MEMSET(sample->data24, 0, storedNbFrames); + FLUID_MEMCPY(sample->data24 + SAMPLE_LOOP_MARGIN, data24, nbframes * sizeof(char)); + } + + /* pointers */ + /* all from the start of data */ + sample->start = SAMPLE_LOOP_MARGIN; + sample->end = SAMPLE_LOOP_MARGIN + nbframes - 1; + } + else + { + /* we cannot assure the SAMPLE_LOOP_MARGIN */ + sample->data = data; + sample->data24 = data24; + sample->start = 0; + sample->end = nbframes - 1; + } + + sample->samplerate = sample_rate; + sample->sampletype = FLUID_SAMPLETYPE_MONO; + sample->auto_free = copy_data; + + return FLUID_OK; + +error_rec: + FLUID_LOG(FLUID_ERR, "Out of memory"); + FLUID_FREE(sample->data); + FLUID_FREE(sample->data24); + sample->data = NULL; + sample->data24 = NULL; + return FLUID_FAILED; + +#undef SAMPLE_LOOP_MARGIN +} + +/** + * Set the loop of a sample. + * + * @param sample SoundFont sample + * @param loop_start Start sample index of the loop. + * @param loop_end End index of the loop (must be a valid sample as it marks the last sample to be played). + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sample_set_loop(fluid_sample_t *sample, unsigned int loop_start, unsigned int loop_end) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + + sample->loopstart = loop_start; + sample->loopend = loop_end; + + return FLUID_OK; +} + +/** + * Set the pitch of a sample. + * + * @param sample SoundFont sample + * @param root_key Root MIDI note of sample (0-127) + * @param fine_tune Fine tune in cents + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_sample_set_pitch(fluid_sample_t *sample, int root_key, int fine_tune) +{ + fluid_return_val_if_fail(sample != NULL, FLUID_FAILED); + fluid_return_val_if_fail(0 <= root_key && root_key <= 127, FLUID_FAILED); + + sample->origpitch = root_key; + sample->pitchadj = fine_tune; + + return FLUID_OK; +} + + +/** + * Validate parameters of a sample + * + */ +int fluid_sample_validate(fluid_sample_t *sample, unsigned int buffer_size) +{ +#define EXCLUSIVE_FLAGS (FLUID_SAMPLETYPE_MONO | FLUID_SAMPLETYPE_RIGHT | FLUID_SAMPLETYPE_LEFT) + static const unsigned int supported_flags = EXCLUSIVE_FLAGS | FLUID_SAMPLETYPE_LINKED | FLUID_SAMPLETYPE_OGG_VORBIS | FLUID_SAMPLETYPE_ROM; + + /* ROM samples are unusable for us by definition */ + if(sample->sampletype & FLUID_SAMPLETYPE_ROM) + { + FLUID_LOG(FLUID_WARN, "Sample '%s': ROM sample ignored", sample->name); + return FLUID_FAILED; + } + + if(sample->sampletype & ~supported_flags) + { + FLUID_LOG(FLUID_WARN, "Sample '%s' has unknown flags, possibly using an unsupported compression; sample ignored", sample->name); + return FLUID_FAILED; + } + + if((sample->sampletype & EXCLUSIVE_FLAGS) & ((sample->sampletype & EXCLUSIVE_FLAGS) - 1)) + { + FLUID_LOG(FLUID_INFO, "Sample '%s' should be either mono or left or right; using it anyway", sample->name); + } + + if((sample->sampletype & FLUID_SAMPLETYPE_LINKED) && (sample->sampletype & EXCLUSIVE_FLAGS)) + { + FLUID_LOG(FLUID_INFO, "Linked sample '%s' should not be mono, left or right at the same time; using it anyway", sample->name); + } + + if((sample->sampletype & EXCLUSIVE_FLAGS) == 0) + { + FLUID_LOG(FLUID_INFO, "Sample '%s' has no flags set, assuming mono", sample->name); + sample->sampletype = FLUID_SAMPLETYPE_MONO; + } + + /* Ogg vorbis compressed samples in the SF3 format use byte indices for + * sample start and end pointers before decompression. Standard SF2 samples + * use sample word indices for all pointers, so use half the buffer_size + * for validation. */ + if(!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)) + { + if(buffer_size % 2) + { + FLUID_LOG(FLUID_WARN, "Sample '%s': invalid buffer size", sample->name); + return FLUID_FAILED; + } + + buffer_size /= 2; + } + + if((sample->end > buffer_size) || (sample->start >= sample->end)) + { + FLUID_LOG(FLUID_WARN, "Sample '%s': invalid start/end file positions", sample->name); + return FLUID_FAILED; + } + + return FLUID_OK; +#undef EXCLUSIVE_FLAGS +} + +/* Check the sample loop pointers and optionally convert them to something + * usable in case they are broken. Return a boolean indicating if the pointers + * have been modified, so the user can be notified of possible audio glitches. + */ +int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size) +{ + int modified = FALSE; + unsigned int max_end = buffer_size / 2; + /* In fluid_sample_t the sample end pointer points to the last sample, not + * to the data word after the last sample. FIXME: why? */ + unsigned int sample_end = sample->end + 1; + + if(sample->loopstart == sample->loopend) + { + /* Some SoundFonts disable loops by setting loopstart = loopend. While + * technically invalid, we decided to accept those samples anyway. + * Before fluidsynth 2.2.5 we've set those indices to zero, as this + * change was believed to be inaudible. This turned out to be an + * incorrect assumption, as the loop points may still be modified by + * loop offset modulators afterwards. + */ + if(sample->loopstart != sample->start) + { + // Many soundfonts set loopstart == loopend == sample->start to disabled to loop. + // Only report cases where it's not equal to the sample->start, to avoid spam. + FLUID_LOG(FLUID_DBG, "Sample '%s': zero length loop detected: loopstart == loopend == '%d', sample start '%d', using it anyway", + sample->name, sample->loopstart, sample->start); + } + } + else if(sample->loopstart > sample->loopend) + { + unsigned int tmp; + + /* If loop start and end are reversed, try to swap them around and + * continue validation */ + FLUID_LOG(FLUID_DBG, "Sample '%s': reversed loop pointers '%d' - '%d', trying to fix", + sample->name, sample->loopstart, sample->loopend); + tmp = sample->loopstart; + sample->loopstart = sample->loopend; + sample->loopend = tmp; + modified = TRUE; + } + + /* The SoundFont 2.4 spec defines the loopstart index as the first sample + * point of the loop while loopend is the first point AFTER the last sample + * of the loop. However we cannot be sure whether any of loopend or end is + * correct. Hours of thinking through this have concluded that it would be + * best practice to mangle with loops as little as necessary by only making + * sure the pointers are within sample->start to max_end. Incorrect + * soundfont shall preferably fail loudly. */ + if((sample->loopstart < sample->start) || (sample->loopstart > max_end)) + { + FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop start '%d', setting to sample start '%d'", + sample->name, sample->loopstart, sample->start); + sample->loopstart = sample->start; + modified = TRUE; + } + + if((sample->loopend < sample->start) || (sample->loopend > max_end)) + { + FLUID_LOG(FLUID_DBG, "Sample '%s': invalid loop end '%d', setting to sample end '%d'", + sample->name, sample->loopend, sample_end); + sample->loopend = sample_end; + modified = TRUE; + } + + if((sample->loopstart > sample_end) || (sample->loopend > sample_end)) + { + FLUID_LOG(FLUID_DBG, "Sample '%s': loop range '%d - %d' after sample end '%d', using it anyway", + sample->name, sample->loopstart, sample->loopend, sample_end); + } + + return modified; +} diff --git a/thirdparty/fluidsynth/src/sfloader/fluid_sfont.h b/thirdparty/fluidsynth/src/sfloader/fluid_sfont.h new file mode 100644 index 000000000..9a42c02eb --- /dev/null +++ b/thirdparty/fluidsynth/src/sfloader/fluid_sfont.h @@ -0,0 +1,189 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _PRIV_FLUID_SFONT_H +#define _PRIV_FLUID_SFONT_H + +#include "fluidsynth.h" + +int fluid_sample_validate(fluid_sample_t *sample, unsigned int max_end); +int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int max_end); + +/* + * Utility macros to access soundfonts, presets, and samples + */ + +#define fluid_sfloader_delete(_loader) { if ((_loader) && (_loader)->free) (*(_loader)->free)(_loader); } +#define fluid_sfloader_load(_loader, _filename) (*(_loader)->load)(_loader, _filename) + + +#define fluid_sfont_delete_internal(_sf) ( ((_sf) && (_sf)->free)? (*(_sf)->free)(_sf) : 0) + + +#define fluid_preset_delete_internal(_preset) \ + { if ((_preset) && (_preset)->free) { (*(_preset)->free)(_preset); }} + +#define fluid_preset_noteon(_preset,_synth,_ch,_key,_vel) \ + (*(_preset)->noteon)(_preset,_synth,_ch,_key,_vel) + +#define fluid_preset_notify(_preset,_reason,_chan) \ + ( ((_preset) && (_preset)->notify) ? (*(_preset)->notify)(_preset,_reason,_chan) : FLUID_OK ) + + +#define fluid_sample_incr_ref(_sample) { (_sample)->refcount++; } + +#define fluid_sample_decr_ref(_sample) \ + (_sample)->refcount--; \ + if (((_sample)->refcount == 0) && ((_sample)->notify)) \ + (*(_sample)->notify)(_sample, FLUID_SAMPLE_DONE); + + + +/** + * File callback structure to enable custom soundfont loading (e.g. from memory). + */ +struct _fluid_file_callbacks_t +{ + fluid_sfloader_callback_open_t fopen; + fluid_sfloader_callback_read_t fread; + fluid_sfloader_callback_seek_t fseek; + fluid_sfloader_callback_close_t fclose; + fluid_sfloader_callback_tell_t ftell; +}; + +/** + * SoundFont loader structure. + */ +struct _fluid_sfloader_t +{ + void *data; /**< User defined data pointer used by _fluid_sfloader_t::load() */ + + /** Callback structure specifying file operations used during soundfont loading to allow custom loading, such as from memory */ + fluid_file_callbacks_t file_callbacks; + + fluid_sfloader_free_t free; + + fluid_sfloader_load_t load; +}; + +/** + * Virtual SoundFont instance structure. + */ +struct _fluid_sfont_t +{ + void *data; /**< User defined data */ + int id; /**< SoundFont ID */ + int refcount; /**< SoundFont reference count (1 if no presets referencing it) */ + int bankofs; /**< Bank offset */ + + fluid_sfont_free_t free; + + fluid_sfont_get_name_t get_name; + + fluid_sfont_get_preset_t get_preset; + + fluid_sfont_iteration_start_t iteration_start; + + fluid_sfont_iteration_next_t iteration_next; +}; + +/** + * Virtual SoundFont preset. + */ +struct _fluid_preset_t +{ + void *data; /**< User supplied data */ + fluid_sfont_t *sfont; /**< Parent virtual SoundFont */ + + fluid_preset_free_t free; + + fluid_preset_get_name_t get_name; + + fluid_preset_get_banknum_t get_banknum; + + fluid_preset_get_num_t get_num; + + fluid_preset_noteon_t noteon; + + /** + * Virtual SoundFont preset notify method. + * @param preset Virtual SoundFont preset + * @param reason #FLUID_PRESET_SELECTED or #FLUID_PRESET_UNSELECTED + * @param chan MIDI channel number + * @return Should return #FLUID_OK + * + * Implement this optional method if the preset needs to be notified about + * preset select and unselect events. + * + * This method may be called from within synthesis context and therefore + * should be as efficient as possible and not perform any operations considered + * bad for realtime audio output (memory allocations and other OS calls). + */ + int (*notify)(fluid_preset_t *preset, int reason, int chan); +}; + +/** + * Virtual SoundFont sample. + */ +struct _fluid_sample_t +{ + char name[21]; /**< Sample name */ + + /* The following four sample pointers store the original pointers from the Soundfont + * file. They are never changed after loading and are used to re-create the + * actual sample pointers after a sample has been unloaded and loaded again. The + * actual sample pointers get modified during loading for SF3 (compressed) samples + * and individually loaded SF2 samples. */ + unsigned int source_start; + unsigned int source_end; + unsigned int source_loopstart; + unsigned int source_loopend; + + unsigned int start; /**< Start index */ + unsigned int end; /**< End index, index of last valid sample point (contrary to SF spec) */ + unsigned int loopstart; /**< Loop start index */ + unsigned int loopend; /**< Loop end index, first point following the loop (superimposed on loopstart) */ + + unsigned int samplerate; /**< Sample rate */ + int origpitch; /**< Original pitch (MIDI note number, 0-127) */ + int pitchadj; /**< Fine pitch adjustment (+/- 99 cents) */ + int sampletype; /**< Specifies the type of this sample as indicated by the #fluid_sample_type enum */ + int auto_free; /**< TRUE if _fluid_sample_t::data and _fluid_sample_t::data24 should be freed upon sample destruction */ + short *data; /**< Pointer to the sample's 16 bit PCM data */ + char *data24; /**< If not NULL, pointer to the least significant byte counterparts of each sample data point in order to create 24 bit audio samples */ + + int amplitude_that_reaches_noise_floor_is_valid; /**< Indicates if \a amplitude_that_reaches_noise_floor is valid (TRUE), set to FALSE initially to calculate. */ + double amplitude_that_reaches_noise_floor; /**< The amplitude at which the sample's loop will be below the noise floor. For voice off optimization, calculated automatically. */ + + unsigned int refcount; /**< Count of voices using this sample */ + int preset_count; /**< Count of selected presets using this sample (used for dynamic sample loading) */ + + /** + * Implement this function to receive notification when sample is no longer used. + * @param sample Virtual SoundFont sample + * @param reason #FLUID_SAMPLE_DONE only currently + * @return Should return #FLUID_OK + */ + int (*notify)(fluid_sample_t *sample, int reason); +}; + + +#endif /* _PRIV_FLUID_SFONT_H */ diff --git a/thirdparty/fluidsynth/src/sfloader/sndfile.h b/thirdparty/fluidsynth/src/sfloader/sndfile.h new file mode 100644 index 000000000..f5d9c1056 --- /dev/null +++ b/thirdparty/fluidsynth/src/sfloader/sndfile.h @@ -0,0 +1,882 @@ +/* +** Copyright (C) 1999-2016 Erik de Castro Lopo +** +** 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, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** sndfile.h -- system-wide definitions +** +** API documentation is in the doc/ directory of the source code tarball +** and at http://libsndfile.github.io/libsndfile/api.html. +*/ + +#ifndef SNDFILE_H +#define SNDFILE_H + +/* This is the version 1.0.X header file. */ +#define SNDFILE_1 + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* The following file types can be read and written. +** A file type would consist of a major type (ie SF_FORMAT_WAV) bitwise +** ORed with a minor type (ie SF_FORMAT_PCM). SF_FORMAT_TYPEMASK and +** SF_FORMAT_SUBMASK can be used to separate the major and minor file +** types. +*/ + +enum +{ /* Major formats. */ + SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian default). */ + SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */ + SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */ + SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */ + SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */ + SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */ + SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */ + SF_FORMAT_VOC = 0x080000, /* VOC files. */ + SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */ + SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */ + SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */ + SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */ + SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */ + SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */ + SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */ + SF_FORMAT_SDS = 0x110000, /* Midi Sample Dump Standard */ + SF_FORMAT_AVR = 0x120000, /* Audio Visual Research */ + SF_FORMAT_WAVEX = 0x130000, /* MS WAVE with WAVEFORMATEX */ + SF_FORMAT_SD2 = 0x160000, /* Sound Designer 2 */ + SF_FORMAT_FLAC = 0x170000, /* FLAC lossless file format */ + SF_FORMAT_CAF = 0x180000, /* Core Audio File format */ + SF_FORMAT_WVE = 0x190000, /* Psion WVE format */ + SF_FORMAT_OGG = 0x200000, /* Xiph OGG container */ + SF_FORMAT_MPC2K = 0x210000, /* Akai MPC 2000 sampler */ + SF_FORMAT_RF64 = 0x220000, /* RF64 WAV file */ + SF_FORMAT_MPEG = 0x230000, /* MPEG-1/2 audio stream */ + + /* Subtypes from here on. */ + + SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */ + SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */ + SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */ + SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */ + + SF_FORMAT_PCM_U8 = 0x0005, /* Unsigned 8 bit data (WAV and RAW only) */ + + SF_FORMAT_FLOAT = 0x0006, /* 32 bit float data */ + SF_FORMAT_DOUBLE = 0x0007, /* 64 bit float data */ + + SF_FORMAT_ULAW = 0x0010, /* U-Law encoded. */ + SF_FORMAT_ALAW = 0x0011, /* A-Law encoded. */ + SF_FORMAT_IMA_ADPCM = 0x0012, /* IMA ADPCM. */ + SF_FORMAT_MS_ADPCM = 0x0013, /* Microsoft ADPCM. */ + + SF_FORMAT_GSM610 = 0x0020, /* GSM 6.10 encoding. */ + SF_FORMAT_VOX_ADPCM = 0x0021, /* OKI / Dialogix ADPCM */ + + SF_FORMAT_NMS_ADPCM_16 = 0x0022, /* 16kbs NMS G721-variant encoding. */ + SF_FORMAT_NMS_ADPCM_24 = 0x0023, /* 24kbs NMS G721-variant encoding. */ + SF_FORMAT_NMS_ADPCM_32 = 0x0024, /* 32kbs NMS G721-variant encoding. */ + + SF_FORMAT_G721_32 = 0x0030, /* 32kbs G721 ADPCM encoding. */ + SF_FORMAT_G723_24 = 0x0031, /* 24kbs G723 ADPCM encoding. */ + SF_FORMAT_G723_40 = 0x0032, /* 40kbs G723 ADPCM encoding. */ + + SF_FORMAT_DWVW_12 = 0x0040, /* 12 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_16 = 0x0041, /* 16 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_24 = 0x0042, /* 24 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_N = 0x0043, /* N bit Delta Width Variable Word encoding. */ + + SF_FORMAT_DPCM_8 = 0x0050, /* 8 bit differential PCM (XI only) */ + SF_FORMAT_DPCM_16 = 0x0051, /* 16 bit differential PCM (XI only) */ + + SF_FORMAT_VORBIS = 0x0060, /* Xiph Vorbis encoding. */ + SF_FORMAT_OPUS = 0x0064, /* Xiph/Skype Opus encoding. */ + + SF_FORMAT_ALAC_16 = 0x0070, /* Apple Lossless Audio Codec (16 bit). */ + SF_FORMAT_ALAC_20 = 0x0071, /* Apple Lossless Audio Codec (20 bit). */ + SF_FORMAT_ALAC_24 = 0x0072, /* Apple Lossless Audio Codec (24 bit). */ + SF_FORMAT_ALAC_32 = 0x0073, /* Apple Lossless Audio Codec (32 bit). */ + + SF_FORMAT_MPEG_LAYER_I = 0x0080, /* MPEG-1 Audio Layer I */ + SF_FORMAT_MPEG_LAYER_II = 0x0081, /* MPEG-1 Audio Layer II */ + SF_FORMAT_MPEG_LAYER_III = 0x0082, /* MPEG-2 Audio Layer III */ + + /* Endian-ness options. */ + + SF_ENDIAN_FILE = 0x00000000, /* Default file endian-ness. */ + SF_ENDIAN_LITTLE = 0x10000000, /* Force little endian-ness. */ + SF_ENDIAN_BIG = 0x20000000, /* Force big endian-ness. */ + SF_ENDIAN_CPU = 0x30000000, /* Force CPU endian-ness. */ + + SF_FORMAT_SUBMASK = 0x0000FFFF, + SF_FORMAT_TYPEMASK = 0x0FFF0000, + SF_FORMAT_ENDMASK = 0x30000000 +} ; + +/* +** The following are the valid command numbers for the sf_command() +** interface. The use of these commands is documented in the file +** command.html in the doc directory of the source code distribution. +*/ + +enum +{ SFC_GET_LIB_VERSION = 0x1000, + SFC_GET_LOG_INFO = 0x1001, + SFC_GET_CURRENT_SF_INFO = 0x1002, + + + SFC_GET_NORM_DOUBLE = 0x1010, + SFC_GET_NORM_FLOAT = 0x1011, + SFC_SET_NORM_DOUBLE = 0x1012, + SFC_SET_NORM_FLOAT = 0x1013, + SFC_SET_SCALE_FLOAT_INT_READ = 0x1014, + SFC_SET_SCALE_INT_FLOAT_WRITE = 0x1015, + + SFC_GET_SIMPLE_FORMAT_COUNT = 0x1020, + SFC_GET_SIMPLE_FORMAT = 0x1021, + + SFC_GET_FORMAT_INFO = 0x1028, + + SFC_GET_FORMAT_MAJOR_COUNT = 0x1030, + SFC_GET_FORMAT_MAJOR = 0x1031, + SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032, + SFC_GET_FORMAT_SUBTYPE = 0x1033, + + SFC_CALC_SIGNAL_MAX = 0x1040, + SFC_CALC_NORM_SIGNAL_MAX = 0x1041, + SFC_CALC_MAX_ALL_CHANNELS = 0x1042, + SFC_CALC_NORM_MAX_ALL_CHANNELS = 0x1043, + SFC_GET_SIGNAL_MAX = 0x1044, + SFC_GET_MAX_ALL_CHANNELS = 0x1045, + + SFC_SET_ADD_PEAK_CHUNK = 0x1050, + + SFC_UPDATE_HEADER_NOW = 0x1060, + SFC_SET_UPDATE_HEADER_AUTO = 0x1061, + + SFC_FILE_TRUNCATE = 0x1080, + + SFC_SET_RAW_START_OFFSET = 0x1090, + + /* Commands reserved for dithering, which is not implemented. */ + SFC_SET_DITHER_ON_WRITE = 0x10A0, + SFC_SET_DITHER_ON_READ = 0x10A1, + + SFC_GET_DITHER_INFO_COUNT = 0x10A2, + SFC_GET_DITHER_INFO = 0x10A3, + + SFC_GET_EMBED_FILE_INFO = 0x10B0, + + SFC_SET_CLIPPING = 0x10C0, + SFC_GET_CLIPPING = 0x10C1, + + SFC_GET_CUE_COUNT = 0x10CD, + SFC_GET_CUE = 0x10CE, + SFC_SET_CUE = 0x10CF, + + SFC_GET_INSTRUMENT = 0x10D0, + SFC_SET_INSTRUMENT = 0x10D1, + + SFC_GET_LOOP_INFO = 0x10E0, + + SFC_GET_BROADCAST_INFO = 0x10F0, + SFC_SET_BROADCAST_INFO = 0x10F1, + + SFC_GET_CHANNEL_MAP_INFO = 0x1100, + SFC_SET_CHANNEL_MAP_INFO = 0x1101, + + SFC_RAW_DATA_NEEDS_ENDSWAP = 0x1110, + + /* Support for Wavex Ambisonics Format */ + SFC_WAVEX_SET_AMBISONIC = 0x1200, + SFC_WAVEX_GET_AMBISONIC = 0x1201, + + /* + ** RF64 files can be set so that on-close, writable files that have less + ** than 4GB of data in them are converted to RIFF/WAV, as per EBU + ** recommendations. + */ + SFC_RF64_AUTO_DOWNGRADE = 0x1210, + + SFC_SET_VBR_ENCODING_QUALITY = 0x1300, + SFC_SET_COMPRESSION_LEVEL = 0x1301, + SFC_SET_OGG_PAGE_LATENCY_MS = 0x1302, + SFC_SET_OGG_PAGE_LATENCY = 0x1303, + + SFC_GET_BITRATE_MODE = 0x1304, + SFC_SET_BITRATE_MODE = 0x1305, + + /* Cart Chunk support */ + SFC_SET_CART_INFO = 0x1400, + SFC_GET_CART_INFO = 0x1401, + + /* Opus files original samplerate metadata */ + SFC_SET_ORIGINAL_SAMPLERATE = 0x1500, + SFC_GET_ORIGINAL_SAMPLERATE = 0x1501, + + /* Following commands for testing only. */ + SFC_TEST_IEEE_FLOAT_REPLACE = 0x6001, + + /* + ** These SFC_SET_ADD_* values are deprecated and will disappear at some + ** time in the future. They are guaranteed to be here up to and + ** including version 1.0.8 to avoid breakage of existing software. + ** They currently do nothing and will continue to do nothing. + */ + SFC_SET_ADD_HEADER_PAD_CHUNK = 0x1051, + + SFC_SET_ADD_DITHER_ON_WRITE = 0x1070, + SFC_SET_ADD_DITHER_ON_READ = 0x1071 +} ; + + +/* +** String types that can be set and read from files. Not all file types +** support this and even the file types which support one, may not support +** all string types. +*/ + +enum +{ SF_STR_TITLE = 0x01, + SF_STR_COPYRIGHT = 0x02, + SF_STR_SOFTWARE = 0x03, + SF_STR_ARTIST = 0x04, + SF_STR_COMMENT = 0x05, + SF_STR_DATE = 0x06, + SF_STR_ALBUM = 0x07, + SF_STR_LICENSE = 0x08, + SF_STR_TRACKNUMBER = 0x09, + SF_STR_GENRE = 0x10 +} ; + +/* +** Use the following as the start and end index when doing metadata +** transcoding. +*/ + +#define SF_STR_FIRST SF_STR_TITLE +#define SF_STR_LAST SF_STR_GENRE + +enum +{ /* True and false */ + SF_FALSE = 0, + SF_TRUE = 1, + + /* Modes for opening files. */ + SFM_READ = 0x10, + SFM_WRITE = 0x20, + SFM_RDWR = 0x30, + + SF_AMBISONIC_NONE = 0x40, + SF_AMBISONIC_B_FORMAT = 0x41 +} ; + +/* Public error values. These are guaranteed to remain unchanged for the duration +** of the library major version number. +** There are also a large number of private error numbers which are internal to +** the library which can change at any time. +*/ + +enum +{ SF_ERR_NO_ERROR = 0, + SF_ERR_UNRECOGNISED_FORMAT = 1, + SF_ERR_SYSTEM = 2, + SF_ERR_MALFORMED_FILE = 3, + SF_ERR_UNSUPPORTED_ENCODING = 4 +} ; + + +/* Channel map values (used with SFC_SET/GET_CHANNEL_MAP). +*/ + +enum +{ SF_CHANNEL_MAP_INVALID = 0, + SF_CHANNEL_MAP_MONO = 1, + SF_CHANNEL_MAP_LEFT, /* Apple calls this 'Left' */ + SF_CHANNEL_MAP_RIGHT, /* Apple calls this 'Right' */ + SF_CHANNEL_MAP_CENTER, /* Apple calls this 'Center' */ + SF_CHANNEL_MAP_FRONT_LEFT, + SF_CHANNEL_MAP_FRONT_RIGHT, + SF_CHANNEL_MAP_FRONT_CENTER, + SF_CHANNEL_MAP_REAR_CENTER, /* Apple calls this 'Center Surround', Msft calls this 'Back Center' */ + SF_CHANNEL_MAP_REAR_LEFT, /* Apple calls this 'Left Surround', Msft calls this 'Back Left' */ + SF_CHANNEL_MAP_REAR_RIGHT, /* Apple calls this 'Right Surround', Msft calls this 'Back Right' */ + SF_CHANNEL_MAP_LFE, /* Apple calls this 'LFEScreen', Msft calls this 'Low Frequency' */ + SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER, /* Apple calls this 'Left Center' */ + SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER, /* Apple calls this 'Right Center */ + SF_CHANNEL_MAP_SIDE_LEFT, /* Apple calls this 'Left Surround Direct' */ + SF_CHANNEL_MAP_SIDE_RIGHT, /* Apple calls this 'Right Surround Direct' */ + SF_CHANNEL_MAP_TOP_CENTER, /* Apple calls this 'Top Center Surround' */ + SF_CHANNEL_MAP_TOP_FRONT_LEFT, /* Apple calls this 'Vertical Height Left' */ + SF_CHANNEL_MAP_TOP_FRONT_RIGHT, /* Apple calls this 'Vertical Height Right' */ + SF_CHANNEL_MAP_TOP_FRONT_CENTER, /* Apple calls this 'Vertical Height Center' */ + SF_CHANNEL_MAP_TOP_REAR_LEFT, /* Apple and MS call this 'Top Back Left' */ + SF_CHANNEL_MAP_TOP_REAR_RIGHT, /* Apple and MS call this 'Top Back Right' */ + SF_CHANNEL_MAP_TOP_REAR_CENTER, /* Apple and MS call this 'Top Back Center' */ + + SF_CHANNEL_MAP_AMBISONIC_B_W, + SF_CHANNEL_MAP_AMBISONIC_B_X, + SF_CHANNEL_MAP_AMBISONIC_B_Y, + SF_CHANNEL_MAP_AMBISONIC_B_Z, + + SF_CHANNEL_MAP_MAX +} ; + +/* Bitrate mode values (for use with SFC_GET/SET_BITRATE_MODE) +*/ +enum +{ SF_BITRATE_MODE_CONSTANT = 0, + SF_BITRATE_MODE_AVERAGE, + SF_BITRATE_MODE_VARIABLE +} ; + + +/* A SNDFILE* pointer can be passed around much like stdio.h's FILE* pointer. */ + +typedef struct sf_private_tag SNDFILE ; + +/* The following typedef is system specific and is defined when libsndfile is +** compiled. sf_count_t will be a 64 bit value when the underlying OS allows +** 64 bit file offsets. +** On windows, we need to allow the same header file to be compiler by both GCC +** and the Microsoft compiler. +*/ + +typedef int64_t sf_count_t ; +#ifndef SF_COUNT_MAX +#define SF_COUNT_MAX 0x7FFFFFFFFFFFFFFFLL +#endif + + +/* A pointer to a SF_INFO structure is passed to sf_open () and filled in. +** On write, the SF_INFO structure is filled in by the user and passed into +** sf_open (). +*/ + +struct SF_INFO +{ sf_count_t frames ; /* Used to be called samples. Changed to avoid confusion. */ + int samplerate ; + int channels ; + int format ; + int sections ; + int seekable ; +} ; + +typedef struct SF_INFO SF_INFO ; + +/* The SF_FORMAT_INFO struct is used to retrieve information about the sound +** file formats libsndfile supports using the sf_command () interface. +** +** Using this interface will allow applications to support new file formats +** and encoding types when libsndfile is upgraded, without requiring +** re-compilation of the application. +** +** Please consult the libsndfile documentation (particularly the information +** on the sf_command () interface) for examples of its use. +*/ + +typedef struct +{ int format ; + const char *name ; + const char *extension ; +} SF_FORMAT_INFO ; + +/* +** Enums and typedefs for adding dither on read and write. +** Reserved for future implementation. +*/ + +enum +{ SFD_DEFAULT_LEVEL = 0, + SFD_CUSTOM_LEVEL = 0x40000000, + + SFD_NO_DITHER = 500, + SFD_WHITE = 501, + SFD_TRIANGULAR_PDF = 502 +} ; + +typedef struct +{ int type ; + double level ; + const char *name ; +} SF_DITHER_INFO ; + +/* Struct used to retrieve information about a file embedded within a +** larger file. See SFC_GET_EMBED_FILE_INFO. +*/ + +typedef struct +{ sf_count_t offset ; + sf_count_t length ; +} SF_EMBED_FILE_INFO ; + +/* +** Struct used to retrieve cue marker information from a file +*/ + +typedef struct +{ int32_t indx ; + uint32_t position ; + int32_t fcc_chunk ; + int32_t chunk_start ; + int32_t block_start ; + uint32_t sample_offset ; + char name [256] ; +} SF_CUE_POINT ; + +#define SF_CUES_VAR(count) \ + struct \ + { uint32_t cue_count ; \ + SF_CUE_POINT cue_points [count] ; \ + } + +typedef SF_CUES_VAR (100) SF_CUES ; + +/* +** Structs used to retrieve music sample information from a file. +*/ + +enum +{ /* + ** The loop mode field in SF_INSTRUMENT will be one of the following. + */ + SF_LOOP_NONE = 800, + SF_LOOP_FORWARD, + SF_LOOP_BACKWARD, + SF_LOOP_ALTERNATING +} ; + +typedef struct +{ int gain ; + char basenote, detune ; + char velocity_lo, velocity_hi ; + char key_lo, key_hi ; + int loop_count ; + + struct + { int mode ; + uint32_t start ; + uint32_t end ; + uint32_t count ; + } loops [16] ; /* make variable in a sensible way */ +} SF_INSTRUMENT ; + + + +/* Struct used to retrieve loop information from a file.*/ +typedef struct +{ + short time_sig_num ; /* any positive integer > 0 */ + short time_sig_den ; /* any positive power of 2 > 0 */ + int loop_mode ; /* see SF_LOOP enum */ + + int num_beats ; /* this is NOT the amount of quarter notes !!!*/ + /* a full bar of 4/4 is 4 beats */ + /* a full bar of 7/8 is 7 beats */ + + float bpm ; /* suggestion, as it can be calculated using other fields:*/ + /* file's length, file's sampleRate and our time_sig_den*/ + /* -> bpms are always the amount of _quarter notes_ per minute */ + + int root_key ; /* MIDI note, or -1 for None */ + int future [6] ; +} SF_LOOP_INFO ; + + +/* Struct used to retrieve broadcast (EBU) information from a file. +** Strongly (!) based on EBU "bext" chunk format used in Broadcast WAVE. +*/ +#define SF_BROADCAST_INFO_VAR(coding_hist_size) \ + struct \ + { char description [256] ; \ + char originator [32] ; \ + char originator_reference [32] ; \ + char origination_date [10] ; \ + char origination_time [8] ; \ + uint32_t time_reference_low ; \ + uint32_t time_reference_high ; \ + short version ; \ + char umid [64] ; \ + int16_t loudness_value ; \ + int16_t loudness_range ; \ + int16_t max_true_peak_level ; \ + int16_t max_momentary_loudness ; \ + int16_t max_shortterm_loudness ; \ + char reserved [180] ; \ + uint32_t coding_history_size ; \ + char coding_history [coding_hist_size] ; \ + } + +/* SF_BROADCAST_INFO is the above struct with coding_history field of 256 bytes. */ +typedef SF_BROADCAST_INFO_VAR (256) SF_BROADCAST_INFO ; + +struct SF_CART_TIMER +{ char usage [4] ; + int32_t value ; +} ; + +typedef struct SF_CART_TIMER SF_CART_TIMER ; + +#define SF_CART_INFO_VAR(p_tag_text_size) \ + struct \ + { char version [4] ; \ + char title [64] ; \ + char artist [64] ; \ + char cut_id [64] ; \ + char client_id [64] ; \ + char category [64] ; \ + char classification [64] ; \ + char out_cue [64] ; \ + char start_date [10] ; \ + char start_time [8] ; \ + char end_date [10] ; \ + char end_time [8] ; \ + char producer_app_id [64] ; \ + char producer_app_version [64] ; \ + char user_def [64] ; \ + int32_t level_reference ; \ + SF_CART_TIMER post_timers [8] ; \ + char reserved [276] ; \ + char url [1024] ; \ + uint32_t tag_text_size ; \ + char tag_text [p_tag_text_size] ; \ + } + +typedef SF_CART_INFO_VAR (256) SF_CART_INFO ; + +/* Virtual I/O functionality. */ + +typedef sf_count_t (*sf_vio_get_filelen) (void *user_data) ; +typedef sf_count_t (*sf_vio_seek) (sf_count_t offset, int whence, void *user_data) ; +typedef sf_count_t (*sf_vio_read) (void *ptr, sf_count_t count, void *user_data) ; +typedef sf_count_t (*sf_vio_write) (const void *ptr, sf_count_t count, void *user_data) ; +typedef sf_count_t (*sf_vio_tell) (void *user_data) ; + +struct SF_VIRTUAL_IO +{ sf_vio_get_filelen get_filelen ; + sf_vio_seek seek ; + sf_vio_read read ; + sf_vio_write write ; + sf_vio_tell tell ; +} ; + +typedef struct SF_VIRTUAL_IO SF_VIRTUAL_IO ; + + +/* Open the specified file for read, write or both. On error, this will +** return a NULL pointer. To find the error number, pass a NULL SNDFILE +** to sf_strerror (). +** All calls to sf_open() should be matched with a call to sf_close(). +*/ + +SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) ; + + +/* Use the existing file descriptor to create a SNDFILE object. If close_desc +** is TRUE, the file descriptor will be closed when sf_close() is called. If +** it is FALSE, the descriptor will not be closed. +** When passed a descriptor like this, the library will assume that the start +** of file header is at the current file offset. This allows sound files within +** larger container files to be read and/or written. +** On error, this will return a NULL pointer. To find the error number, pass a +** NULL SNDFILE to sf_strerror (). +** All calls to sf_open_fd() should be matched with a call to sf_close(). + +*/ + +SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ; + +SNDFILE* sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ; + + +/* sf_error () returns a error number which can be translated to a text +** string using sf_error_number(). +*/ + +int sf_error (SNDFILE *sndfile) ; + + +/* sf_strerror () returns to the caller a pointer to the current error message for +** the given SNDFILE. +*/ + +const char* sf_strerror (SNDFILE *sndfile) ; + + +/* sf_error_number () allows the retrieval of the error string for each internal +** error number. +** +*/ + +const char* sf_error_number (int errnum) ; + + +/* The following two error functions are deprecated but they will remain in the +** library for the foreseeable future. The function sf_strerror() should be used +** in their place. +*/ + +int sf_perror (SNDFILE *sndfile) ; +int sf_error_str (SNDFILE *sndfile, char* str, size_t len) ; + + +/* Allow the caller to retrieve information from or change aspects of the +** library behaviour. +*/ + +int sf_command (SNDFILE *sndfile, int command, void *data, int datasize) ; + + +/* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ + +int sf_format_check (const SF_INFO *info) ; + + +/* Seek within the waveform data chunk of the SNDFILE. sf_seek () uses +** the same values for whence (SEEK_SET, SEEK_CUR and SEEK_END) as +** stdio.h function fseek (). +** An offset of zero with whence set to SEEK_SET will position the +** read / write pointer to the first data sample. +** On success sf_seek returns the current position in (multi-channel) +** samples from the start of the file. +** Please see the libsndfile documentation for moving the read pointer +** separately from the write pointer on files open in mode SFM_RDWR. +** On error all of these functions return -1. +*/ + +enum +{ SF_SEEK_SET = SEEK_SET, + SF_SEEK_CUR = SEEK_CUR, + SF_SEEK_END = SEEK_END +} ; + +sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence) ; + + +/* Functions for retrieving and setting string data within sound files. +** Not all file types support this features; AIFF and WAV do. For both +** functions, the str_type parameter must be one of the SF_STR_* values +** defined above. +** On error, sf_set_string() returns non-zero while sf_get_string() +** returns NULL. +*/ + +int sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ; + +const char* sf_get_string (SNDFILE *sndfile, int str_type) ; + + +/* Return the library version string. */ + +const char * sf_version_string (void) ; + +/* Return the current byterate at this point in the file. The byte rate in this +** case is the number of bytes per second of audio data. For instance, for a +** stereo, 18 bit PCM encoded file with an 16kHz sample rate, the byte rate +** would be 2 (stereo) * 2 (two bytes per sample) * 16000 => 64000 bytes/sec. +** For some file formats the returned value will be accurate and exact, for some +** it will be a close approximation, for some it will be the average bitrate for +** the whole file and for some it will be a time varying value that was accurate +** when the file was most recently read or written. +** To get the bitrate, multiple this value by 8. +** Returns -1 for unknown. +*/ +int sf_current_byterate (SNDFILE *sndfile) ; + +/* Functions for reading/writing the waveform data of a sound file. +*/ + +sf_count_t sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ; +sf_count_t sf_write_raw (SNDFILE *sndfile, const void *ptr, sf_count_t bytes) ; + + +/* Functions for reading and writing the data chunk in terms of frames. +** The number of items actually read/written = frames * number of channels. +** sf_xxxx_raw read/writes the raw data bytes from/to the file +** sf_xxxx_short passes data in the native short format +** sf_xxxx_int passes data in the native int format +** sf_xxxx_float passes data in the native float format +** sf_xxxx_double passes data in the native double format +** All of these read/write function return number of frames read/written. +*/ + +sf_count_t sf_readf_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ; +sf_count_t sf_writef_short (SNDFILE *sndfile, const short *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ; +sf_count_t sf_writef_int (SNDFILE *sndfile, const int *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ; +sf_count_t sf_writef_float (SNDFILE *sndfile, const float *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ; +sf_count_t sf_writef_double (SNDFILE *sndfile, const double *ptr, sf_count_t frames) ; + + +/* Functions for reading and writing the data chunk in terms of items. +** Otherwise similar to above. +** All of these read/write function return number of items read/written. +*/ + +sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ; +sf_count_t sf_write_short (SNDFILE *sndfile, const short *ptr, sf_count_t items) ; + +sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ; +sf_count_t sf_write_int (SNDFILE *sndfile, const int *ptr, sf_count_t items) ; + +sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ; +sf_count_t sf_write_float (SNDFILE *sndfile, const float *ptr, sf_count_t items) ; + +sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ; +sf_count_t sf_write_double (SNDFILE *sndfile, const double *ptr, sf_count_t items) ; + + +/* Close the SNDFILE and clean up all memory allocations associated with this +** file. +** Returns 0 on success, or an error number. +*/ + +int sf_close (SNDFILE *sndfile) ; + + +/* If the file is opened SFM_WRITE or SFM_RDWR, call fsync() on the file +** to force the writing of data to disk. If the file is opened SFM_READ +** no action is taken. +*/ + +void sf_write_sync (SNDFILE *sndfile) ; + + + +/* The function sf_wchar_open() is Windows Only! +** Open a file passing in a Windows Unicode filename. Otherwise, this is +** the same as sf_open(). +*/ + +#ifdef _WIN32 +SNDFILE* sf_wchar_open (const wchar_t *wpath, int mode, SF_INFO *sfinfo) ; +#endif + + + + +/* Getting and setting of chunks from within a sound file. +** +** These functions allow the getting and setting of chunks within a sound file +** (for those formats which allow it). +** +** These functions fail safely. Specifically, they will not allow you to overwrite +** existing chunks or add extra versions of format specific reserved chunks but +** should allow you to retrieve any and all chunks (may not be implemented for +** all chunks or all file formats). +*/ + +struct SF_CHUNK_INFO +{ char id [64] ; /* The chunk identifier. */ + unsigned id_size ; /* The size of the chunk identifier. */ + unsigned datalen ; /* The size of that data. */ + void *data ; /* Pointer to the data. */ +} ; + +typedef struct SF_CHUNK_INFO SF_CHUNK_INFO ; + +/* Set the specified chunk info (must be done before any audio data is written +** to the file). This will fail for format specific reserved chunks. +** The chunk_info->data pointer must be valid until the file is closed. +** Returns SF_ERR_NO_ERROR on success or non-zero on failure. +*/ +int sf_set_chunk (SNDFILE * sndfile, const SF_CHUNK_INFO * chunk_info) ; + +/* +** An opaque structure to an iterator over the all chunks of a given id +*/ +typedef struct SF_CHUNK_ITERATOR SF_CHUNK_ITERATOR ; + +/* Get an iterator for all chunks matching chunk_info. +** The iterator will point to the first chunk matching chunk_info. +** Chunks are matching, if (chunk_info->id) matches the first +** (chunk_info->id_size) bytes of a chunk found in the SNDFILE* handle. +** If chunk_info is NULL, an iterator to all chunks in the SNDFILE* handle +** is returned. +** The values of chunk_info->datalen and chunk_info->data are ignored. +** If no matching chunks are found in the sndfile, NULL is returned. +** The returned iterator will stay valid until one of the following occurs: +** a) The sndfile is closed. +** b) A new chunk is added using sf_set_chunk(). +** c) Another chunk iterator function is called on the same SNDFILE* handle +** that causes the iterator to be modified. +** The memory for the iterator belongs to the SNDFILE* handle and is freed when +** sf_close() is called. +*/ +SF_CHUNK_ITERATOR * +sf_get_chunk_iterator (SNDFILE * sndfile, const SF_CHUNK_INFO * chunk_info) ; + +/* Iterate through chunks by incrementing the iterator. +** Increments the iterator and returns a handle to the new one. +** After this call, iterator will no longer be valid, and you must use the +** newly returned handle from now on. +** The returned handle can be used to access the next chunk matching +** the criteria as defined in sf_get_chunk_iterator(). +** If iterator points to the last chunk, this will free all resources +** associated with iterator and return NULL. +** The returned iterator will stay valid until sf_get_chunk_iterator_next +** is called again, the sndfile is closed or a new chunk us added. +*/ +SF_CHUNK_ITERATOR * +sf_next_chunk_iterator (SF_CHUNK_ITERATOR * iterator) ; + + +/* Get the size of the specified chunk. +** If the specified chunk exists, the size will be returned in the +** datalen field of the SF_CHUNK_INFO struct. +** Additionally, the id of the chunk will be copied to the id +** field of the SF_CHUNK_INFO struct and it's id_size field will +** be updated accordingly. +** If the chunk doesn't exist chunk_info->datalen will be zero, and the +** id and id_size fields will be undefined. +** The function will return SF_ERR_NO_ERROR on success or non-zero on +** failure. +*/ +int +sf_get_chunk_size (const SF_CHUNK_ITERATOR * it, SF_CHUNK_INFO * chunk_info) ; + +/* Get the specified chunk data. +** If the specified chunk exists, up to chunk_info->datalen bytes of +** the chunk data will be copied into the chunk_info->data buffer +** (allocated by the caller) and the chunk_info->datalen field +** updated to reflect the size of the data. The id and id_size +** field will be updated according to the retrieved chunk +** If the chunk doesn't exist chunk_info->datalen will be zero, and the +** id and id_size fields will be undefined. +** The function will return SF_ERR_NO_ERROR on success or non-zero on +** failure. +*/ +int +sf_get_chunk_data (const SF_CHUNK_ITERATOR * it, SF_CHUNK_INFO * chunk_info) ; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* SNDFILE_H */ diff --git a/thirdparty/fluidsynth/src/synth/fluid_chan.c b/thirdparty/fluidsynth/src/synth/fluid_chan.c new file mode 100644 index 000000000..0f2eecb44 --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_chan.c @@ -0,0 +1,732 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_chan.h" +#include "fluid_mod.h" +#include "fluid_synth.h" +#include "fluid_sfont.h" + +/* Field shift amounts for sfont_bank_prog bit field integer */ +#define PROG_SHIFTVAL 0 +#define BANK_SHIFTVAL 8 +#define SFONT_SHIFTVAL 22 + +/* Field mask values for sfont_bank_prog bit field integer */ +#define PROG_MASKVAL 0x000000FF /* Bit 7 is used to indicate unset state */ +#define BANK_MASKVAL 0x003FFF00 +#define BANKLSB_MASKVAL 0x00007F00 +#define BANKMSB_MASKVAL 0x003F8000 +#define SFONT_MASKVAL 0xFFC00000 + + +static void fluid_channel_init(fluid_channel_t *chan); + + +fluid_channel_t * +new_fluid_channel(fluid_synth_t *synth, int num) +{ + fluid_channel_t *chan; + + chan = FLUID_NEW(fluid_channel_t); + + if(chan == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + chan->synth = synth; + chan->channum = num; + chan->preset = NULL; + chan->tuning = NULL; + + fluid_channel_init(chan); + fluid_channel_init_ctrl(chan, 0); + + return chan; +} + +static void +fluid_channel_init(fluid_channel_t *chan) +{ + fluid_preset_t *newpreset; + int i, prognum, banknum; + + chan->sostenuto_orderid = 0; + /*--- Init poly/mono modes variables --------------------------------------*/ + chan->mode = 0; + chan->mode_val = 0; + + /* monophonic list initialization */ + for(i = 0; i < FLUID_CHANNEL_SIZE_MONOLIST; i++) + { + chan->monolist[i].next = i + 1; + } + + chan->monolist[FLUID_CHANNEL_SIZE_MONOLIST - 1].next = 0; /* ending element chained to the 1st */ + chan->i_last = chan->n_notes = 0; /* clears the list */ + chan->i_first = chan->monolist[chan->i_last].next; /* first note index in the list */ + fluid_channel_clear_prev_note(chan); /* Mark previous note invalid */ + /*---*/ + chan->key_mono_sustained = INVALID_NOTE; /* No previous mono note sustained */ + chan->legatomode = FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER; /* Default mode */ + chan->portamentomode = FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY; /* Default mode */ + /*--- End of poly/mono initialization --------------------------------------*/ + + chan->channel_type = (chan->channum == 9) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; + prognum = 0; + banknum = (chan->channel_type == CHANNEL_TYPE_DRUM) ? DRUM_INST_BANK : 0; + + chan->sfont_bank_prog = 0 << SFONT_SHIFTVAL | banknum << BANK_SHIFTVAL + | prognum << PROG_SHIFTVAL; + + newpreset = fluid_synth_find_preset(chan->synth, banknum, prognum); + fluid_channel_set_preset(chan, newpreset); + + chan->interp_method = FLUID_INTERP_DEFAULT; + chan->tuning_bank = 0; + chan->tuning_prog = 0; + chan->nrpn_select = 0; + chan->nrpn_active = 0; + + if(chan->tuning) + { + fluid_tuning_unref(chan->tuning, 1); + chan->tuning = NULL; + } +} + +/* + @param is_all_ctrl_off if nonzero, only resets some controllers, according to + https://www.midi.org/techspecs/rp15.php +*/ +void +fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off) +{ + int i; + + chan->channel_pressure = 0; + chan->pitch_bend = 0x2000; /* Range is 0x4000, pitch bend wheel starts in centered position */ + + for(i = 0; i < GEN_LAST; i++) + { + chan->gen[i] = 0.0f; + } + + if(is_all_ctrl_off) + { + for(i = 0; i < ALL_SOUND_OFF; i++) + { + if(i >= EFFECTS_DEPTH1 && i <= EFFECTS_DEPTH5) + { + continue; + } + + if(i >= SOUND_CTRL1 && i <= SOUND_CTRL10) + { + continue; + } + + if(i == BANK_SELECT_MSB || i == BANK_SELECT_LSB || i == VOLUME_MSB || + i == VOLUME_LSB || i == PAN_MSB || i == PAN_LSB || + i == BALANCE_MSB || i == BALANCE_LSB + ) + { + continue; + } + + fluid_channel_set_cc(chan, i, 0); + } + } + else + { + for(i = 0; i < 128; i++) + { + fluid_channel_set_cc(chan, i, 0); + } + + chan->previous_cc_breath = 0;/* Reset previous breath */ + } + /* Unconditionally clear PTC receive (issue #1050) */ + fluid_channel_clear_portamento(chan); + + /* Reset polyphonic key pressure on all voices */ + for(i = 0; i < 128; i++) + { + fluid_channel_set_key_pressure(chan, i, 0); + } + + /* Set RPN controllers to NULL state */ + fluid_channel_set_cc(chan, RPN_LSB, 127); + fluid_channel_set_cc(chan, RPN_MSB, 127); + + /* Set NRPN controllers to NULL state */ + fluid_channel_set_cc(chan, NRPN_LSB, 127); + fluid_channel_set_cc(chan, NRPN_MSB, 127); + + /* Expression (MSB & LSB) */ + fluid_channel_set_cc(chan, EXPRESSION_MSB, 127); + fluid_channel_set_cc(chan, EXPRESSION_LSB, 127); + + if(!is_all_ctrl_off) + { + + chan->pitch_wheel_sensitivity = 2; /* two semi-tones */ + + /* Just like panning, a value of 64 indicates no change for sound ctrls */ + for(i = SOUND_CTRL1; i <= SOUND_CTRL10; i++) + { + fluid_channel_set_cc(chan, i, 64); + } + + /* Volume / initial attenuation (MSB & LSB) */ + fluid_channel_set_cc(chan, VOLUME_MSB, 100); + fluid_channel_set_cc(chan, VOLUME_LSB, 0); + + /* Pan (MSB & LSB) */ + fluid_channel_set_cc(chan, PAN_MSB, 64); + fluid_channel_set_cc(chan, PAN_LSB, 0); + + /* Balance (MSB & LSB) */ + fluid_channel_set_cc(chan, BALANCE_MSB, 64); + fluid_channel_set_cc(chan, BALANCE_LSB, 0); + + /* Reverb */ + /* fluid_channel_set_cc (chan, EFFECTS_DEPTH1, 40); */ + /* Note: although XG standard specifies the default amount of reverb to + be 40, most people preferred having it at zero. + See https://lists.gnu.org/archive/html/fluid-dev/2009-07/msg00016.html */ + } +} + +/* Only called by delete_fluid_synth(), so no need to queue a preset free event */ +void +delete_fluid_channel(fluid_channel_t *chan) +{ + fluid_return_if_fail(chan != NULL); + + FLUID_FREE(chan); +} + +void +fluid_channel_reset(fluid_channel_t *chan) +{ + fluid_channel_init(chan); + fluid_channel_init_ctrl(chan, 0); +} + +/* Should only be called from synthesis context */ +int +fluid_channel_set_preset(fluid_channel_t *chan, fluid_preset_t *preset) +{ + fluid_sfont_t *sfont; + + if(chan->preset == preset) + { + return FLUID_OK; + } + + if(chan->preset) + { + sfont = chan->preset->sfont; + sfont->refcount--; + } + + fluid_preset_notify(chan->preset, FLUID_PRESET_UNSELECTED, chan->channum); + + chan->preset = preset; + + if(preset) + { + sfont = preset->sfont; + sfont->refcount++; + } + + fluid_preset_notify(preset, FLUID_PRESET_SELECTED, chan->channum); + + return FLUID_OK; +} + +/* Set SoundFont ID, MIDI bank and/or program. Use -1 to use current value. */ +void +fluid_channel_set_sfont_bank_prog(fluid_channel_t *chan, int sfontnum, + int banknum, int prognum) +{ + int oldval, newval, oldmask; + + newval = ((sfontnum != -1) ? sfontnum << SFONT_SHIFTVAL : 0) + | ((banknum != -1) ? banknum << BANK_SHIFTVAL : 0) + | ((prognum != -1) ? prognum << PROG_SHIFTVAL : 0); + + oldmask = ((sfontnum != -1) ? 0 : SFONT_MASKVAL) + | ((banknum != -1) ? 0 : BANK_MASKVAL) + | ((prognum != -1) ? 0 : PROG_MASKVAL); + + oldval = chan->sfont_bank_prog; + newval = (newval & ~oldmask) | (oldval & oldmask); + chan->sfont_bank_prog = newval; +} + +/* Set bank LSB 7 bits */ +void +fluid_channel_set_bank_lsb(fluid_channel_t *chan, int banklsb) +{ + int oldval, newval, style; + + style = chan->synth->bank_select; + + if(style == FLUID_BANK_STYLE_GM || + style == FLUID_BANK_STYLE_GS) + { + return; /* ignored */ + } + + oldval = chan->sfont_bank_prog; + + if(style == FLUID_BANK_STYLE_XG) + { + newval = (oldval & ~BANK_MASKVAL) | (banklsb << BANK_SHIFTVAL); + } + else /* style == FLUID_BANK_STYLE_MMA */ + { + newval = (oldval & ~BANKLSB_MASKVAL) | (banklsb << BANK_SHIFTVAL); + } + + chan->sfont_bank_prog = newval; +} + +/* Set bank MSB 7 bits */ +void +fluid_channel_set_bank_msb(fluid_channel_t *chan, int bankmsb) +{ + int oldval, newval, style; + + style = chan->synth->bank_select; + + if(style == FLUID_BANK_STYLE_XG) + { + /* XG bank, do drum-channel auto-switch */ + /* The number "120" was based on several keyboards having drums at 120 - 127, + reference: https://lists.nongnu.org/archive/html/fluid-dev/2011-02/msg00003.html */ + chan->channel_type = (120 <= bankmsb) ? CHANNEL_TYPE_DRUM : CHANNEL_TYPE_MELODIC; + return; + } + + if(style == FLUID_BANK_STYLE_GM || + chan->channel_type == CHANNEL_TYPE_DRUM) + { + return; /* ignored */ + } + + oldval = chan->sfont_bank_prog; + + if(style == FLUID_BANK_STYLE_GS) + { + newval = (oldval & ~BANK_MASKVAL) | (bankmsb << BANK_SHIFTVAL); + } + else /* style == FLUID_BANK_STYLE_MMA */ + { + newval = (oldval & ~BANKMSB_MASKVAL) | (bankmsb << (BANK_SHIFTVAL + 7)); + } + + chan->sfont_bank_prog = newval; + +} + +/* Get SoundFont ID, MIDI bank and/or program. Use NULL to ignore a value. */ +void +fluid_channel_get_sfont_bank_prog(fluid_channel_t *chan, int *sfont, + int *bank, int *prog) +{ + int sfont_bank_prog; + + sfont_bank_prog = chan->sfont_bank_prog; + + if(sfont) + { + *sfont = (sfont_bank_prog & SFONT_MASKVAL) >> SFONT_SHIFTVAL; + } + + if(bank) + { + *bank = (sfont_bank_prog & BANK_MASKVAL) >> BANK_SHIFTVAL; + } + + if(prog) + { + *prog = (sfont_bank_prog & PROG_MASKVAL) >> PROG_SHIFTVAL; + } +} + +/** + * Compute the pitch for a key after applying Fluidsynth's tuning functionality + * and channel coarse/fine tunings. + * @param chan fluid_channel_t + * @param key MIDI note number (0-127) + * @return the pitch of the key + */ +fluid_real_t fluid_channel_get_key_pitch(fluid_channel_t *chan, int key) +{ + if(chan->tuning) + { + return fluid_tuning_get_pitch(chan->tuning, key) + + 100.0f * fluid_channel_get_gen(chan, GEN_COARSETUNE) + + fluid_channel_get_gen(chan, GEN_FINETUNE); + } + else + { + return key * 100.0f; + } +} + +/** + * Updates legato/ staccato playing state + * The function is called: + * - on noteon before adding a note into the monolist. + * - on noteoff after removing a note out of the monolist. + * @param chan fluid_channel_t. +*/ +static void +fluid_channel_update_legato_staccato_state(fluid_channel_t *chan) +{ + /* Updates legato/ staccato playing state */ + if(chan->n_notes) + { + chan->mode |= FLUID_CHANNEL_LEGATO_PLAYING; /* Legato state */ + } + else + { + chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */ + } +} + +/** + * Adds a note into the monophonic list. The function is part of the legato + * detector. fluid_channel_add_monolist() is intended to be called by + * fluid_synth_noteon_mono_LOCAL(). + * + * When a note is added at noteOn each element is use in the forward direction + * and indexed by i_last variable. + * + * @param chan fluid_channel_t. + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127, 0=noteoff). + * @param onenote. When 1 the function adds the note but the monophonic list + * keeps only one note (used on noteOn poly). + * Note: i_last index keeps a trace of the most recent note added. + * prev_note keeps a trace of the note prior i_last note. + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing state. + * + * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). +*/ +void +fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key, + unsigned char vel, unsigned char onenote) +{ + unsigned char i_last = chan->i_last; + /* Updates legato/ staccato playing state */ + fluid_channel_update_legato_staccato_state(chan); + + if(chan->n_notes) + { + /* keeps trace of the note prior last note */ + chan->prev_note = chan->monolist[i_last].note; + } + + /* moves i_last forward before writing new note */ + i_last = chan->monolist[i_last].next; + chan->i_last = i_last; /* now ilast indexes the last note */ + chan->monolist[i_last].note = key; /* we save note and velocity */ + chan->monolist[i_last].vel = vel; + + if(onenote) + { + /* clears monolist before one note addition */ + chan->i_first = i_last; + chan->n_notes = 0; + } + + if(chan->n_notes < FLUID_CHANNEL_SIZE_MONOLIST) + { + chan->n_notes++; /* updates n_notes */ + } + else + { + /* The end of buffer is reach. So circular motion for i_first */ + /* i_first index is moved forward */ + chan->i_first = chan->monolist[i_last].next; + } +} + +/** + * Searching a note in the monophonic list. The function is part of the legato + * detector. fluid_channel_search_monolist() is intended to be called by + * fluid_synth_noteoff_mono_LOCAL(). + * + * The search starts from the first note in the list indexed by i_first + + * @param chan fluid_channel_t. + * @param key MIDI note number (0-127) to search. + * @param i_prev pointer on returned index of the note prior the note to search. + * @return index of the note if find, FLUID_FAILED otherwise. + * + */ +int +fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_prev) +{ + short n = chan->n_notes; /* number of notes in monophonic list */ + short j, i = chan->i_first; /* searching starts from i_first included */ + + for(j = 0 ; j < n ; j++) + { + if(chan->monolist[i].note == key) + { + if(i == chan->i_first) + { + /* tracking index of the previous note (i_prev) */ + for(j = chan->i_last ; n < FLUID_CHANNEL_SIZE_MONOLIST; n++) + { + j = chan->monolist[j].next; + } + + * i_prev = j; /* returns index of the previous note */ + } + + return i; /* returns index of the note to search */ + } + + * i_prev = i; /* tracking index of the previous note (i_prev) */ + i = chan->monolist[i].next; /* next element */ + } + + return FLUID_FAILED; /* not found */ +} + +/** + * removes a note from the monophonic list. The function is part of + * the legato detector. + * fluid_channel_remove_monolist() is intended to be called by + * fluid_synth_noteoff_mono_LOCAL(). + * + * When a note is removed at noteOff the element concerned is fast unlinked + * and relinked after the i_last element. + * + * @param chan fluid_channel_t. + * @param + * i, index of the note to remove. If i is invalid or the list is + * empty, the function do nothing and returns FLUID_FAILED. + * @param + * On input, i_prev is a pointer on index of the note previous i. + * On output i_prev is a pointer on index of the note previous i if i is the last note + * in the list,FLUID_FAILED otherwise. When the returned index is valid it means + * a legato detection on noteoff. + * + * Note: the following variables in Channel keeps trace of the situation. + * - i_last index keeps a trace of the most recent note played even if + * the list is empty. + * - prev_note keeps a trace of the note removed if it is i_last. + * - FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing state. + * + * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). + */ +void +fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev) +{ + unsigned char i_last = chan->i_last; + + /* checks if index is valid */ + if(i < 0 || i >= FLUID_CHANNEL_SIZE_MONOLIST || !chan->n_notes) + { + * i_prev = FLUID_FAILED; + } + + /* The element is about to be removed and inserted between i_last and next */ + /* Note: when i is egal to i_last or egal to i_first, removing/inserting + isn't necessary */ + if(i == i_last) + { + /* Removing/Inserting isn't necessary */ + /* keeps trace of the note prior last note */ + chan->prev_note = chan->monolist[i_last].note; + /* moves i_last backward to the previous */ + chan->i_last = *i_prev; /* i_last index is moved backward */ + } + else + { + /* i is before i_last */ + if(i == chan->i_first) + { + /* Removing/inserting isn't necessary */ + /* i_first index is moved forward to the next element*/ + chan->i_first = chan->monolist[i].next; + } + else + { + /* i is between i_first and i_last */ + /* Unlinks element i and inserts after i_last */ + chan->monolist[* i_prev].next = chan->monolist[i].next; /* unlinks i */ + /*inserts i after i_last */ + chan->monolist[i].next = chan->monolist[i_last].next; + chan->monolist[i_last].next = i; + } + + * i_prev = FLUID_FAILED; + } + + chan->n_notes--; /* updates the number of note in the list */ + /* Updates legato/ staccato playing state */ + fluid_channel_update_legato_staccato_state(chan); +} + +/** + * On noteOff on a polyphonic channel,the monophonic list is fully flushed. + * + * @param chan fluid_channel_t. + * Note: i_last index keeps a trace of the most recent note played even if + * the list is empty. + * prev_note keeps a trace of the note i_last . + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps a trace of legato/staccato playing. + */ +void fluid_channel_clear_monolist(fluid_channel_t *chan) +{ + /* keeps trace off the most recent note played */ + chan->prev_note = chan->monolist[chan->i_last].note; + + /* flushes the monolist */ + chan->i_first = chan->monolist[chan->i_last].next; + chan->n_notes = 0; + /* Update legato/ sataccato playing state */ + chan->mode &= ~ FLUID_CHANNEL_LEGATO_PLAYING; /* Staccato state */ +} + +/** + * On noteOn on a polyphonic channel,adds the note into the monophonic list + * keeping only this note. + * @param + * chan fluid_channel_t. + * key, vel, note and velocity added in the monolist + * Note: i_last index keeps a trace of the most recent note inserted. + * prev_note keeps a trace of the note prior i_last note. + * FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing. + */ +void fluid_channel_set_onenote_monolist(fluid_channel_t *chan, unsigned char key, + unsigned char vel) +{ + fluid_channel_add_monolist(chan, key, vel, 1); +} + +/** + * The function changes the state (Valid/Invalid) of the previous note played in + * a staccato manner (fluid_channel_prev_note()). + * When potamento mode 'each note' or 'staccato only' is selected, on next + * noteOn a portamento will be started from the most recent note played + * staccato. + * It will be possible that it isn't appropriate. To give the musician the + * possibility to choose a portamento from this note , prev_note will be forced + * to invalid state on noteOff if portamento pedal is Off. + * + * The function is intended to be called when the following event occurs: + * - On noteOff (in poly or mono mode), to mark prev_note invalid. + * - On Portamento Off(in poly or mono mode), to mark prev_note invalid. + * @param chan fluid_channel_t. + */ +void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan) +{ + /* checks if the playing is staccato */ + if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + { + + /* checks if portamento pedal is off */ + if(! fluid_channel_portamento(chan)) + { + /* forces prev_note invalid */ + fluid_channel_clear_prev_note(chan); + } + } + + /* else prev_note still remains valid for next fromkey portamento */ +} + +/** + * The function handles poly/mono commutation on legato pedal On/Off. + * @param chan fluid_channel_t. + * @param value, value of the CC legato. + */ +void fluid_channel_cc_legato(fluid_channel_t *chan, int value) +{ + /* Special handling of the monophonic list */ + if(!(chan->mode & FLUID_CHANNEL_POLY_OFF) && chan->n_notes) /* The monophonic list have notes */ + { + if(value < 64) /* legato is released */ + { + /* returns from monophonic to polyphonic with notes in the monophonic list */ + + /* The monophonic list is flushed keeping last note only + Note: i_last index keeps a trace of the most recent note played. + prev_note keeps a trace of the note i_last. + FLUID_CHANNEL_LEGATO_PLAYING bit keeps trace of legato/staccato playing. + */ + chan->i_first = chan->i_last; + chan->n_notes = 1; + } + else /* legato is depressed */ + { + /* Inters in monophonic from polyphonic with note in monophonic list */ + /* Stops the running note to remain coherent with Breath Sync mode */ + if((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && !fluid_channel_breath_msb(chan)) + { + fluid_synth_noteoff_monopoly(chan->synth, chan->channum, + fluid_channel_last_note(chan), 1); + } + } + } +} + +/** + * The function handles CC Breath On/Off detection. When a channel is in + * Breath Sync mode and in monophonic playing, the breath controller allows + * to trigger noteon/noteoff note when the musician starts to breath (noteon) and + * stops to breath (noteoff). + * @param chan fluid_channel_t. + * @param value, value of the CC Breath.. + */ +void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value) +{ + if((chan->mode & FLUID_CHANNEL_BREATH_SYNC) && fluid_channel_is_playing_mono(chan) && + (chan->n_notes)) + { + /* The monophonic list isn't empty */ + if((value > 0) && (chan->previous_cc_breath == 0)) + { + /* CC Breath On detection */ + fluid_synth_noteon_mono_staccato(chan->synth, chan->channum, + fluid_channel_last_note(chan), + fluid_channel_last_vel(chan)); + } + else if((value == 0) && (chan->previous_cc_breath > 0)) + { + /* CC Breath Off detection */ + fluid_synth_noteoff_monopoly(chan->synth, chan->channum, + fluid_channel_last_note(chan), 1); + } + } + + chan->previous_cc_breath = value; +} diff --git a/thirdparty/fluidsynth/src/synth/fluid_chan.h b/thirdparty/fluidsynth/src/synth/fluid_chan.h new file mode 100644 index 000000000..96eb02e37 --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_chan.h @@ -0,0 +1,276 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_CHAN_H +#define _FLUID_CHAN_H + +#include "fluidsynth_priv.h" +#include "fluid_midi.h" +#include "fluid_tuning.h" + +/* The mononophonic list is part of the legato detector for monophonic mode */ +/* see fluid_synth_monopoly.c about a description of the legato detector device */ +/* Size of the monophonic list + - 1 is the minimum. it allows playing legato passage of any number + of notes on noteon only. + - Size above 1 allows playing legato on noteon but also on noteOff. + This allows the musician to play fast trills. + This feature is particularly usful when the MIDI input device is a keyboard. + Choosing a size of 10 is sufficient (because most musicians have only 10 + fingers when playing a monophonic instrument). +*/ +#define FLUID_CHANNEL_SIZE_MONOLIST 10 + +/* + + The monophonic list + +------------------------------------------------+ + | +----+ +----+ +----+ +----+ | + | |note| |note| |note| |note| | + +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+ + +----+ +----+ +----+ +----+ + /|\ /|\ + | | + i_first i_last + + The monophonic list is a circular buffer of FLUID_CHANNEL_SIZE_MONOLIST elements. + Each element is linked forward at initialisation time. + - when a note is added at noteOn (see fluid_channel_add_monolist()) each + element is use in the forward direction and indexed by i_last variable. + - when a note is removed at noteOff (see fluid_channel_remove_monolist()), + the element concerned is fast unlinked and relinked after the i_last element. + + The most recent note added is indexed by i_last. + The most ancient note added is the first note indexed by i_first. i_first is + moving in the forward direction in a circular manner. + +*/ +struct mononote +{ + unsigned char next; /* next note */ + unsigned char note; /* note */ + unsigned char vel; /* velocity */ +}; + +/* + * fluid_channel_t + * + * Mutual exclusion notes (as of 1.1.2): + * None - everything should have been synchronized by the synth. + */ +struct _fluid_channel_t +{ + fluid_synth_t *synth; /**< Parent synthesizer instance */ + int channum; /**< MIDI channel number */ + + /* Poly Mono variables see macro access description */ + int mode; /**< Poly Mono mode */ + int mode_val; /**< number of channel in basic channel group */ + + /* monophonic list - legato detector */ + unsigned char i_first; /**< First note index */ + unsigned char i_last; /**< most recent note index since the most recent add */ + unsigned char prev_note; /**< previous note of the most recent add/remove */ + unsigned char n_notes; /**< actual number of notes in the list */ + struct mononote monolist[FLUID_CHANNEL_SIZE_MONOLIST]; /**< monophonic list */ + + unsigned char key_mono_sustained; /**< previous sustained monophonic note */ + unsigned char previous_cc_breath; /**< Previous Breath */ + enum fluid_channel_legato_mode legatomode; /**< legato mode */ + enum fluid_channel_portamento_mode portamentomode; /**< portamento mode */ + /*- End of Poly/mono variables description */ + + unsigned char cc[128]; /**< MIDI controller values from [0;127] */ + unsigned char key_pressure[128]; /**< MIDI polyphonic key pressure from [0;127] */ + + /* Drum channel flag, CHANNEL_TYPE_MELODIC, or CHANNEL_TYPE_DRUM. */ + enum fluid_midi_channel_type channel_type; + enum fluid_interp interp_method; /**< Interpolation method (enum fluid_interp) */ + + unsigned char channel_pressure; /**< MIDI channel pressure from [0;127] */ + unsigned char pitch_wheel_sensitivity; /**< Current pitch wheel sensitivity */ + short pitch_bend; /**< Current pitch bend value */ + /* Sostenuto order id gives the order of SostenutoOn event. + * This value is useful to known when the sostenuto pedal is depressed + * (before or after a key note). We need to compare SostenutoOrderId with voice id. + */ + unsigned int sostenuto_orderid; + + int tuning_bank; /**< Current tuning bank number */ + int tuning_prog; /**< Current tuning program number */ + fluid_tuning_t *tuning; /**< Micro tuning */ + + fluid_preset_t *preset; /**< Selected preset */ + int sfont_bank_prog; /**< SoundFont ID (bit 21-31), bank (bit 7-20), program (bit 0-6) */ + + /* NRPN system */ + enum fluid_gen_type nrpn_select; /* Generator ID of SoundFont NRPN message */ + char nrpn_active; /* 1 if data entry CCs are for NRPN, 0 if RPN */ + + /* The values of the generators, set by NRPN messages, or by + * fluid_synth_set_gen(), are cached in the channel so they can be + * applied to future notes. They are copied to a voice's generators + * in fluid_voice_init(), which calls fluid_gen_init(). */ + fluid_real_t gen[GEN_LAST]; +}; + +fluid_channel_t *new_fluid_channel(fluid_synth_t *synth, int num); +void fluid_channel_init_ctrl(fluid_channel_t *chan, int is_all_ctrl_off); +void delete_fluid_channel(fluid_channel_t *chan); +void fluid_channel_reset(fluid_channel_t *chan); +int fluid_channel_set_preset(fluid_channel_t *chan, fluid_preset_t *preset); +void fluid_channel_set_sfont_bank_prog(fluid_channel_t *chan, int sfont, + int bank, int prog); +void fluid_channel_set_bank_lsb(fluid_channel_t *chan, int banklsb); +void fluid_channel_set_bank_msb(fluid_channel_t *chan, int bankmsb); +void fluid_channel_get_sfont_bank_prog(fluid_channel_t *chan, int *sfont, + int *bank, int *prog); +fluid_real_t fluid_channel_get_key_pitch(fluid_channel_t *chan, int key); + +#define fluid_channel_get_preset(chan) ((chan)->preset) +#define fluid_channel_set_cc(chan, num, val) \ + ((chan)->cc[num] = (val)) +#define fluid_channel_get_cc(chan, num) \ + ((chan)->cc[num]) +#define fluid_channel_get_key_pressure(chan, key) \ + ((chan)->key_pressure[key]) +#define fluid_channel_set_key_pressure(chan, key, val) \ + ((chan)->key_pressure[key] = (val)) +#define fluid_channel_get_channel_pressure(chan) \ + ((chan)->channel_pressure) +#define fluid_channel_set_channel_pressure(chan, val) \ + ((chan)->channel_pressure = (val)) +#define fluid_channel_get_pitch_bend(chan) \ + ((chan)->pitch_bend) +#define fluid_channel_set_pitch_bend(chan, val) \ + ((chan)->pitch_bend = (val)) +#define fluid_channel_get_pitch_wheel_sensitivity(chan) \ + ((chan)->pitch_wheel_sensitivity) +#define fluid_channel_set_pitch_wheel_sensitivity(chan, val) \ + ((chan)->pitch_wheel_sensitivity = (val)) +#define fluid_channel_get_num(chan) ((chan)->channum) +#define fluid_channel_set_interp_method(chan, new_method) \ + ((chan)->interp_method = (new_method)) +#define fluid_channel_get_interp_method(chan) \ + ((chan)->interp_method); +#define fluid_channel_set_tuning(_c, _t) { (_c)->tuning = _t; } +#define fluid_channel_has_tuning(_c) ((_c)->tuning != NULL) +#define fluid_channel_get_tuning(_c) ((_c)->tuning) +#define fluid_channel_get_tuning_bank(chan) \ + ((chan)->tuning_bank) +#define fluid_channel_set_tuning_bank(chan, bank) \ + ((chan)->tuning_bank = (bank)) +#define fluid_channel_get_tuning_prog(chan) \ + ((chan)->tuning_prog) +#define fluid_channel_set_tuning_prog(chan, prog) \ + ((chan)->tuning_prog = (prog)) +#define fluid_channel_portamentotime(_c) \ + ((_c)->cc[PORTAMENTO_TIME_MSB] * 128 + (_c)->cc[PORTAMENTO_TIME_LSB]) +#define fluid_channel_portamento(_c) ((_c)->cc[PORTAMENTO_SWITCH] >= 64) +#define fluid_channel_breath_msb(_c) ((_c)->cc[BREATH_MSB] > 0) +#define fluid_channel_clear_portamento(_c) ((_c)->cc[PORTAMENTO_CTRL] = INVALID_NOTE) +#define fluid_channel_legato(_c) ((_c)->cc[LEGATO_SWITCH] >= 64) +#define fluid_channel_sustained(_c) ((_c)->cc[SUSTAIN_SWITCH] >= 64) +#define fluid_channel_sostenuto(_c) ((_c)->cc[SOSTENUTO_SWITCH] >= 64) +#define fluid_channel_set_gen(_c, _n, _v) { (_c)->gen[_n] = _v; } +#define fluid_channel_get_gen(_c, _n) ((_c)->gen[_n]) +#define fluid_channel_get_min_note_length_ticks(chan) \ + ((chan)->synth->min_note_length_ticks) + +/* Macros interface to poly/mono mode variables */ +#define MASK_BASICCHANINFOS (FLUID_CHANNEL_MODE_MASK|FLUID_CHANNEL_BASIC|FLUID_CHANNEL_ENABLED) +/* Set the basic channel infos for a MIDI basic channel */ +#define fluid_channel_set_basic_channel_info(chan,Infos) \ + (chan->mode = (chan->mode & ~MASK_BASICCHANINFOS) | (Infos & MASK_BASICCHANINFOS)) +/* Reset the basic channel infos for a MIDI basic channel */ +#define fluid_channel_reset_basic_channel_info(chan) (chan->mode &= ~MASK_BASICCHANINFOS) + +/* Macros interface to breath variables */ +#define FLUID_CHANNEL_BREATH_MASK (FLUID_CHANNEL_BREATH_POLY|FLUID_CHANNEL_BREATH_MONO|FLUID_CHANNEL_BREATH_SYNC) +/* Set the breath infos for a MIDI channel */ +#define fluid_channel_set_breath_info(chan,BreathInfos) \ +(chan->mode = (chan->mode & ~FLUID_CHANNEL_BREATH_MASK) | (BreathInfos & FLUID_CHANNEL_BREATH_MASK)) +/* Get the breath infos for a MIDI channel */ +#define fluid_channel_get_breath_info(chan) (chan->mode & FLUID_CHANNEL_BREATH_MASK) + +/* Returns true when channel is mono or legato is on */ +#define fluid_channel_is_playing_mono(chan) ((chan->mode & FLUID_CHANNEL_POLY_OFF) ||\ + fluid_channel_legato(chan)) + +/* Macros interface to monophonic list variables */ +#define INVALID_NOTE (255) +/* Returns true when a note is a valid note */ +#define fluid_channel_is_valid_note(n) (n != INVALID_NOTE) +/* Marks prev_note as invalid. */ +#define fluid_channel_clear_prev_note(chan) (chan->prev_note = INVALID_NOTE) + +/* Returns the most recent note from i_last entry of the monophonic list */ +#define fluid_channel_last_note(chan) (chan->monolist[chan->i_last].note) + +/* Returns the most recent velocity from i_last entry of the monophonic list */ +#define fluid_channel_last_vel(chan) (chan->monolist[chan->i_last].vel) + +/* + prev_note is used to determine fromkey_portamento as well as + fromkey_legato (see fluid_synth_get_fromkey_portamento_legato()). + + prev_note is updated on noteOn/noteOff mono by the legato detector as this: + - On noteOn mono, before adding a new note into the monolist,the most + recent note in the list (i.e at i_last position) is kept in prev_note. + - Similarly, on noteOff mono , before removing a note out of the monolist, + the most recent note (i.e those at i_last position) is kept in prev_note. +*/ +#define fluid_channel_prev_note(chan) (chan->prev_note) + +/* Interface to poly/mono mode variables */ +enum fluid_channel_mode_flags_internal +{ + FLUID_CHANNEL_BASIC = 0x04, /**< if flag set the corresponding midi channel is a basic channel */ + FLUID_CHANNEL_ENABLED = 0x08, /**< if flag set the corresponding midi channel is enabled, else disabled, i.e. channel ignores any MIDI messages */ + + /* + FLUID_CHANNEL_LEGATO_PLAYING bit of channel mode keeps trace of the legato /staccato + state playing. + FLUID_CHANNEL_LEGATO_PLAYING bit is updated on noteOn/noteOff mono by the legato detector: + - On noteOn, before inserting a new note into the monolist. + - On noteOff, after removing a note out of the monolist. + + - On noteOn, this state is used by fluid_synth_noteon_mono_LOCAL() + to play the current note legato or staccato. + - On noteOff, this state is used by fluid_synth_noteoff_mono_LOCAL() + to play the current noteOff legato with the most recent note. + */ + /* bit7, 1: means legato playing , 0: means staccato playing */ + FLUID_CHANNEL_LEGATO_PLAYING = 0x80 +}; + +/* End of interface to monophonic list variables */ + +void fluid_channel_add_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel, unsigned char onenote); +int fluid_channel_search_monolist(fluid_channel_t *chan, unsigned char key, int *i_prev); +void fluid_channel_remove_monolist(fluid_channel_t *chan, int i, int *i_prev); +void fluid_channel_clear_monolist(fluid_channel_t *chan); +void fluid_channel_set_onenote_monolist(fluid_channel_t *chan, unsigned char key, unsigned char vel); +void fluid_channel_invalid_prev_note_staccato(fluid_channel_t *chan); +void fluid_channel_cc_legato(fluid_channel_t *chan, int value); +void fluid_channel_cc_breath_note_on_off(fluid_channel_t *chan, int value); + + +#endif /* _FLUID_CHAN_H */ diff --git a/thirdparty/fluidsynth/src/synth/fluid_event.c b/thirdparty/fluidsynth/src/synth/fluid_event.c new file mode 100644 index 000000000..48b781bd7 --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_event.c @@ -0,0 +1,863 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/* + 2002 : API design by Peter Hanappe and Antoine Schmitt + August 2002 : Implementation by Antoine Schmitt as@gratin.org + as part of the infiniteCD author project + http://www.infiniteCD.org/ + Oct4.2002 : AS : corrected bug in heap allocation, that caused a crash during sequencer free. +*/ + + +#include "fluid_event.h" +#include "fluidsynth_priv.h" +#include "fluid_midi.h" + +/*************************************************************** + * + * SEQUENCER EVENTS + */ + +/* Event alloc/free */ + +void +fluid_event_clear(fluid_event_t *evt) +{ + FLUID_MEMSET(evt, 0, sizeof(fluid_event_t)); + + // by default, no type + evt->dest = -1; + evt->src = -1; + evt->type = -1; + evt->id = -1; +} + +/** + * Create a new sequencer event structure. + * @return New sequencer event structure or NULL if out of memory + */ +fluid_event_t * +new_fluid_event() +{ + fluid_event_t *evt; + + evt = FLUID_NEW(fluid_event_t); + + if(evt == NULL) + { + FLUID_LOG(FLUID_PANIC, "event: Out of memory\n"); + return NULL; + } + + fluid_event_clear(evt); + + return(evt); +} + +/** + * Delete a sequencer event structure. + * @param evt Sequencer event structure created by new_fluid_event(). + */ +void +delete_fluid_event(fluid_event_t *evt) +{ + fluid_return_if_fail(evt != NULL); + + FLUID_FREE(evt); +} + +/** + * Set the time field of a sequencer event. + * @internal + * @param evt Sequencer event structure + * @param time Time value to assign + */ +void +fluid_event_set_time(fluid_event_t *evt, unsigned int time) +{ + evt->time = time; +} + +void +fluid_event_set_id(fluid_event_t *evt, fluid_note_id_t id) +{ + evt->id = id; +} + +/** + * Set source of a sequencer event. \c src must be a unique sequencer ID or -1 if not set. + * @param evt Sequencer event structure + * @param src Unique sequencer ID + */ +void +fluid_event_set_source(fluid_event_t *evt, fluid_seq_id_t src) +{ + evt->src = src; +} + +/** + * Set destination of this sequencer event, i.e. the sequencer client this event will be sent to. \c dest must be a unique sequencer ID. + * @param evt Sequencer event structure + * @param dest The destination unique sequencer ID + */ +void +fluid_event_set_dest(fluid_event_t *evt, fluid_seq_id_t dest) +{ + evt->dest = dest; +} + +/** + * Set a sequencer event to be a timer event. + * @param evt Sequencer event structure + * @param data User supplied data pointer + */ +void +fluid_event_timer(fluid_event_t *evt, void *data) +{ + evt->type = FLUID_SEQ_TIMER; + evt->data = data; +} + +/** + * Set a sequencer event to be a note on event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param key MIDI note number (0-127) + * @param vel MIDI velocity value (0-127) + * @note Since fluidsynth 2.2.2, this function will give you a #FLUID_SEQ_NOTEOFF when + * called with @p vel being zero. + */ +void +fluid_event_noteon(fluid_event_t *evt, int channel, short key, short vel) +{ + if(vel == 0) + { + fluid_event_noteoff(evt, channel, key); + return; + } + + evt->type = FLUID_SEQ_NOTEON; + evt->channel = channel; + evt->key = key; + evt->vel = vel; +} + +/** + * Set a sequencer event to be a note off event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param key MIDI note number (0-127) + */ +void +fluid_event_noteoff(fluid_event_t *evt, int channel, short key) +{ + evt->type = FLUID_SEQ_NOTEOFF; + evt->channel = channel; + evt->key = key; +} + +/** + * Set a sequencer event to be a note duration event. + * + * Before fluidsynth 2.2.0, this event type was naively implemented when used in conjunction with fluid_sequencer_register_fluidsynth(), + * because it simply enqueued a fluid_event_noteon() and fluid_event_noteoff(). + * A handling for overlapping notes was not implemented. Starting with 2.2.0, this changes: If a fluid_event_note() is already playing, + * while another fluid_event_note() arrives on the same @c channel and @c key, the earlier event will be canceled. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param key MIDI note number (0-127) + * @param vel MIDI velocity value (1-127) + * @param duration Duration of note in the time scale used by the sequencer + * + * @note The application should decide whether to use only Notes with duration, or separate NoteOn and NoteOff events. + * @warning Calling this function with @p vel or @p duration being zero results in undefined behavior! + */ +void +fluid_event_note(fluid_event_t *evt, int channel, short key, short vel, unsigned int duration) +{ + evt->type = FLUID_SEQ_NOTE; + evt->channel = channel; + evt->key = key; + evt->vel = vel; + evt->duration = duration; +} + +/** + * Set a sequencer event to be an all sounds off event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + */ +void +fluid_event_all_sounds_off(fluid_event_t *evt, int channel) +{ + evt->type = FLUID_SEQ_ALLSOUNDSOFF; + evt->channel = channel; +} + +/** + * Set a sequencer event to be a all notes off event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + */ +void +fluid_event_all_notes_off(fluid_event_t *evt, int channel) +{ + evt->type = FLUID_SEQ_ALLNOTESOFF; + evt->channel = channel; +} + +/** + * Set a sequencer event to be a bank select event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param bank_num MIDI bank number (0-16383) + */ +void +fluid_event_bank_select(fluid_event_t *evt, int channel, short bank_num) +{ + evt->type = FLUID_SEQ_BANKSELECT; + evt->channel = channel; + evt->control = bank_num; +} + +/** + * Set a sequencer event to be a program change event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val MIDI program number (0-127) + */ +void +fluid_event_program_change(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_PROGRAMCHANGE; + evt->channel = channel; + evt->value = val; +} + +/** + * Set a sequencer event to be a program select event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param sfont_id SoundFont ID number + * @param bank_num MIDI bank number (0-16383) + * @param preset_num MIDI preset number (0-127) + */ +void +fluid_event_program_select(fluid_event_t *evt, int channel, + unsigned int sfont_id, short bank_num, short preset_num) +{ + evt->type = FLUID_SEQ_PROGRAMSELECT; + evt->channel = channel; + evt->duration = sfont_id; + evt->value = preset_num; + evt->control = bank_num; +} + +/** + * Set a sequencer event to be a pitch bend event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param pitch MIDI pitch bend value (0-16383, 8192 = no bend) + */ +void +fluid_event_pitch_bend(fluid_event_t *evt, int channel, int pitch) +{ + evt->type = FLUID_SEQ_PITCHBEND; + evt->channel = channel; + + if(pitch < 0) + { + pitch = 0; + } + + if(pitch > 16383) + { + pitch = 16383; + } + + evt->pitch = pitch; +} + +/** + * Set a sequencer event to be a pitch wheel sensitivity event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param value MIDI pitch wheel sensitivity value in semitones + */ +void +fluid_event_pitch_wheelsens(fluid_event_t *evt, int channel, int value) +{ + evt->type = FLUID_SEQ_PITCHWHEELSENS; + evt->channel = channel; + evt->value = value; +} + +/** + * Set a sequencer event to be a modulation event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val MIDI modulation value (0-127) + */ +void +fluid_event_modulation(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_MODULATION; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a MIDI sustain event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val MIDI sustain value (0-127) + */ +void +fluid_event_sustain(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_SUSTAIN; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a MIDI control change event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param control MIDI control number (0-127) + * @param val MIDI control value (0-127) + */ +void +fluid_event_control_change(fluid_event_t *evt, int channel, short control, int val) +{ + evt->type = FLUID_SEQ_CONTROLCHANGE; + evt->channel = channel; + evt->control = control; + evt->value = val; +} + +/** + * Set a sequencer event to be a stereo pan event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val MIDI panning value (0-127, 0=left, 64 = middle, 127 = right) + */ +void +fluid_event_pan(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_PAN; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a volume event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val Volume value (0-127) + */ +void +fluid_event_volume(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_VOLUME; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a reverb send event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val Reverb amount (0-127) + */ +void +fluid_event_reverb_send(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_REVERBSEND; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a chorus send event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val Chorus amount (0-127) + */ +void +fluid_event_chorus_send(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_CHORUSSEND; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + + +/** + * Set a sequencer event to be an unregistering event. + * @param evt Sequencer event structure + * @since 1.1.0 + */ +void +fluid_event_unregistering(fluid_event_t *evt) +{ + evt->type = FLUID_SEQ_UNREGISTERING; +} + +/** + * Set a sequencer event to be a scale change event. + * Useful for scheduling tempo changes. + * @param evt Sequencer event structure + * @param new_scale The new time scale to apply to the sequencer, see fluid_sequencer_set_time_scale() + * @since 2.2.0 + */ +void +fluid_event_scale(fluid_event_t *evt, double new_scale) +{ + evt->type = FLUID_SEQ_SCALE; + evt->scale = new_scale; +} + +/** + * Set a sequencer event to be a channel-wide aftertouch event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param val Aftertouch amount (0-127) + * @since 1.1.0 + */ +void +fluid_event_channel_pressure(fluid_event_t *evt, int channel, int val) +{ + evt->type = FLUID_SEQ_CHANNELPRESSURE; + evt->channel = channel; + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->value = val; +} + +/** + * Set a sequencer event to be a polyphonic aftertouch event. + * @param evt Sequencer event structure + * @param channel MIDI channel number + * @param key MIDI note number (0-127) + * @param val Aftertouch amount (0-127) + * @since 2.0.0 + */ +void +fluid_event_key_pressure(fluid_event_t *evt, int channel, short key, int val) +{ + evt->type = FLUID_SEQ_KEYPRESSURE; + evt->channel = channel; + + if(key < 0) + { + key = 0; + } + + if(key > 127) + { + key = 127; + } + + if(val < 0) + { + val = 0; + } + + if(val > 127) + { + val = 127; + } + + evt->key = key; + evt->value = val; +} + +/** + * Set a sequencer event to be a midi system reset event. + * @param evt Sequencer event structure + * @since 1.1.0 + */ +void +fluid_event_system_reset(fluid_event_t *evt) +{ + evt->type = FLUID_SEQ_SYSTEMRESET; +} + +/** + * Transforms an incoming MIDI event (from a MIDI driver or MIDI router) to a + * sequencer event. + * + * @param evt Sequencer event structure + * @param event MIDI event + * @return #FLUID_OK or #FLUID_FAILED + * + * @note This function copies the fields of the MIDI event into the provided + * sequencer event. Calling applications must create the sequencer event and set + * additional fields such as the source and destination of the sequencer event. + * + * @code{.cpp} + * // ... get MIDI event, e.g. using player_callback() + * + * // Send MIDI event to sequencer to play + * fluid_event_t *evt = new_fluid_event(); + * fluid_event_set_source(evt, -1); + * fluid_event_set_dest(evt, seqid); + * fluid_event_from_midi_event(evt, event); + * fluid_sequencer_send_at(sequencer, evt, 50, 0); // relative time + * delete_fluid_event(evt); + * @endcode + * + * @since 2.2.7 + */ +int fluid_event_from_midi_event(fluid_event_t *evt, const fluid_midi_event_t *event) +{ + int chan; + fluid_return_val_if_fail(event != NULL, FLUID_FAILED); + + chan = fluid_midi_event_get_channel(event); + + switch (fluid_midi_event_get_type(event)) + { + case NOTE_OFF: + fluid_event_noteoff(evt, chan, (short)fluid_midi_event_get_key(event)); + break; + + case NOTE_ON: + fluid_event_noteon(evt, + fluid_midi_event_get_channel(event), + (short)fluid_midi_event_get_key(event), + (short)fluid_midi_event_get_velocity(event)); + break; + + case CONTROL_CHANGE: + fluid_event_control_change(evt, + chan, + (short)fluid_midi_event_get_control(event), + (short)fluid_midi_event_get_value(event)); + break; + + case PROGRAM_CHANGE: + fluid_event_program_change(evt, chan, (short)fluid_midi_event_get_program(event)); + break; + + case PITCH_BEND: + fluid_event_pitch_bend(evt, chan, fluid_midi_event_get_pitch(event)); + break; + + case CHANNEL_PRESSURE: + fluid_event_channel_pressure(evt, chan, (short)fluid_midi_event_get_program(event)); + break; + + case KEY_PRESSURE: + fluid_event_key_pressure(evt, + chan, + (short)fluid_midi_event_get_key(event), + (short)fluid_midi_event_get_value(event)); + break; + + case MIDI_SYSTEM_RESET: + fluid_event_system_reset(evt); + break; + + default: /* Not yet implemented */ + return FLUID_FAILED; + } + + return FLUID_OK; +} + +/* + * Accessing event data + */ + +/** + * Get the event type (#fluid_seq_event_type) field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Event type (#fluid_seq_event_type). + */ +int fluid_event_get_type(fluid_event_t *evt) +{ + return evt->type; +} + +/** + * @internal + * Get the time field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Time value + */ +unsigned int fluid_event_get_time(fluid_event_t *evt) +{ + return evt->time; +} + +/** + * @internal + * Get the time field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Time value + */ +fluid_note_id_t fluid_event_get_id(fluid_event_t *evt) +{ + return evt->id; +} + +/** + * Get the source sequencer client from a sequencer event structure. + * @param evt Sequencer event structure + * @return source field of the sequencer event + */ +fluid_seq_id_t fluid_event_get_source(fluid_event_t *evt) +{ + return evt->src; +} + +/** + * Get the dest sequencer client from a sequencer event structure. + * @param evt Sequencer event structure + * @return dest field of the sequencer event + */ +fluid_seq_id_t fluid_event_get_dest(fluid_event_t *evt) +{ + return evt->dest; +} + +/** + * Get the MIDI channel field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI zero-based channel number + */ +int fluid_event_get_channel(fluid_event_t *evt) +{ + return evt->channel; +} + +/** + * Get the MIDI note field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI note number (0-127) + */ +short fluid_event_get_key(fluid_event_t *evt) +{ + return evt->key; +} + +/** + * Get the MIDI velocity field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI velocity value (0-127) + */ +short fluid_event_get_velocity(fluid_event_t *evt) + +{ + return evt->vel; +} + +/** + * Get the MIDI control number field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI control number (0-127) + */ +short fluid_event_get_control(fluid_event_t *evt) +{ + return evt->control; +} + +/** + * Get the value field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Value field of event. + * + * The Value field is used by the following event types: + * #FLUID_SEQ_PROGRAMCHANGE, #FLUID_SEQ_PROGRAMSELECT (preset_num), + * #FLUID_SEQ_PITCHWHEELSENS, #FLUID_SEQ_MODULATION, #FLUID_SEQ_SUSTAIN, + * #FLUID_SEQ_CONTROLCHANGE, #FLUID_SEQ_PAN, #FLUID_SEQ_VOLUME, + * #FLUID_SEQ_REVERBSEND, #FLUID_SEQ_CHORUSSEND. + */ +int fluid_event_get_value(fluid_event_t *evt) +{ + return evt->value; +} + +/** + * Get the data field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Data field of event. + * + * Used by the #FLUID_SEQ_TIMER event type. + */ +void *fluid_event_get_data(fluid_event_t *evt) +{ + return evt->data; +} + +/** + * Get the duration field from a sequencer event structure. + * @param evt Sequencer event structure + * @return Note duration value in the time scale used by the sequencer (by default milliseconds) + * + * Used by the #FLUID_SEQ_NOTE event type. + */ +unsigned int fluid_event_get_duration(fluid_event_t *evt) +{ + return evt->duration; +} + +/** + * Get the MIDI bank field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI bank number (0-16383) + * + * Used by the #FLUID_SEQ_BANKSELECT and #FLUID_SEQ_PROGRAMSELECT + * event types. + */ +short fluid_event_get_bank(fluid_event_t *evt) +{ + return evt->control; +} + +/** + * Get the pitch field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI pitch bend pitch value (0-16383, 8192 = no bend) + * + * Used by the #FLUID_SEQ_PITCHBEND event type. + */ +int fluid_event_get_pitch(fluid_event_t *evt) +{ + return evt->pitch; +} + +/** + * Get the MIDI program field from a sequencer event structure. + * @param evt Sequencer event structure + * @return MIDI program number (0-127) + * + * Used by the #FLUID_SEQ_PROGRAMCHANGE and #FLUID_SEQ_PROGRAMSELECT + * event types. + */ +int +fluid_event_get_program(fluid_event_t *evt) +{ + return evt->value; +} + +/** + * Get the SoundFont ID field from a sequencer event structure. + * @param evt Sequencer event structure + * @return SoundFont identifier value. + * + * Used by the #FLUID_SEQ_PROGRAMSELECT event type. + */ +unsigned int +fluid_event_get_sfont_id(fluid_event_t *evt) +{ + return evt->duration; +} + +/** + * Gets time scale field from a sequencer event structure. + * @param evt Sequencer event structure + * @return SoundFont identifier value. + * + * Used by the #FLUID_SEQ_SCALE event type. + */ +double fluid_event_get_scale(fluid_event_t *evt) +{ + return evt->scale; +} diff --git a/thirdparty/fluidsynth/src/synth/fluid_event.h b/thirdparty/fluidsynth/src/synth/fluid_event.h new file mode 100644 index 000000000..4a0cca5d9 --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_event.h @@ -0,0 +1,65 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_EVENT_PRIV_H +#define _FLUID_EVENT_PRIV_H + +#include "fluidsynth.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int fluid_note_id_t; + +/* Private data for event */ +/* ?? should be optimized in size, using unions */ +struct _fluid_event_t +{ + unsigned int time; + int type; + fluid_seq_id_t src; + fluid_seq_id_t dest; + int channel; + short key; + short vel; + short control; + int value; + fluid_note_id_t id; + int pitch; + unsigned int duration; + double scale; + void *data; +}; + +unsigned int fluid_event_get_time(fluid_event_t *evt); +void fluid_event_set_time(fluid_event_t *evt, unsigned int time); + +fluid_note_id_t fluid_event_get_id(fluid_event_t *evt); +void fluid_event_set_id(fluid_event_t *evt, fluid_note_id_t id); + +void fluid_event_clear(fluid_event_t *evt); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUID_EVENT_PRIV_H */ diff --git a/thirdparty/fluidsynth/src/synth/fluid_gen.c b/thirdparty/fluidsynth/src/synth/fluid_gen.c new file mode 100644 index 000000000..2ce2b0f74 --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_gen.c @@ -0,0 +1,133 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#include "fluid_gen.h" +#include "fluid_chan.h" + + +#define _GEN(_name) GEN_ ## _name, #_name + + +/* See SFSpec21 $8.1.3 */ +static const fluid_gen_info_t fluid_gen_info[] = +{ + /* number/name init nrpn-scale min max def */ + { _GEN(STARTADDROFS), 1, 1, 0.0f, 1e10f, 0.0f }, + { _GEN(ENDADDROFS), 1, 1, -1e10f, 0.0f, 0.0f }, + { _GEN(STARTLOOPADDROFS), 1, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(ENDLOOPADDROFS), 1, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(STARTADDRCOARSEOFS), 0, 1, 0.0f, 1e10f, 0.0f }, + { _GEN(MODLFOTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(VIBLFOTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(MODENVTOPITCH), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(FILTERFC), 1, 2, 1500.0f, 13500.0f, 13500.0f }, + { _GEN(FILTERQ), 1, 1, 0.0f, 960.0f, 0.0f }, + { _GEN(MODLFOTOFILTERFC), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(MODENVTOFILTERFC), 1, 2, -12000.0f, 12000.0f, 0.0f }, + { _GEN(ENDADDRCOARSEOFS), 0, 1, -1e10f, 0.0f, 0.0f }, + { _GEN(MODLFOTOVOL), 1, 1, -960.0f, 960.0f, 0.0f }, + { _GEN(UNUSED1), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(CHORUSSEND), 1, 1, 0.0f, 1000.0f, 0.0f }, + { _GEN(REVERBSEND), 1, 1, 0.0f, 1000.0f, 0.0f }, + { _GEN(PAN), 1, 1, -500.0f, 500.0f, 0.0f }, + { _GEN(UNUSED2), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(UNUSED3), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(UNUSED4), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(MODLFODELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(MODLFOFREQ), 1, 4, -16000.0f, 4500.0f, 0.0f }, + { _GEN(VIBLFODELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(VIBLFOFREQ), 1, 4, -16000.0f, 4500.0f, 0.0f }, + { _GEN(MODENVDELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(MODENVATTACK), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(MODENVHOLD), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(MODENVDECAY), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(MODENVSUSTAIN), 0, 1, 0.0f, 1000.0f, 0.0f }, + { _GEN(MODENVRELEASE), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(KEYTOMODENVHOLD), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(KEYTOMODENVDECAY), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(VOLENVDELAY), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(VOLENVATTACK), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(VOLENVHOLD), 1, 2, -12000.0f, 5000.0f, -12000.0f }, + { _GEN(VOLENVDECAY), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(VOLENVSUSTAIN), 0, 1, 0.0f, 1440.0f, 0.0f }, + { _GEN(VOLENVRELEASE), 1, 2, -12000.0f, 8000.0f, -12000.0f }, + { _GEN(KEYTOVOLENVHOLD), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(KEYTOVOLENVDECAY), 0, 1, -1200.0f, 1200.0f, 0.0f }, + { _GEN(INSTRUMENT), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(RESERVED1), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(KEYRANGE), 0, 0, 0.0f, 127.0f, 0.0f }, + { _GEN(VELRANGE), 0, 0, 0.0f, 127.0f, 0.0f }, + { _GEN(STARTLOOPADDRCOARSEOFS), 0, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(KEYNUM), 1, 0, 0.0f, 127.0f, -1.0f }, + { _GEN(VELOCITY), 1, 1, 0.0f, 127.0f, -1.0f }, + { _GEN(ATTENUATION), 1, 1, 0.0f, 1440.0f, 0.0f }, + { _GEN(RESERVED2), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(ENDLOOPADDRCOARSEOFS), 0, 1, -1e10f, 1e10f, 0.0f }, + { _GEN(COARSETUNE), 0, 1, -120.0f, 120.0f, 0.0f }, + { _GEN(FINETUNE), 0, 1, -99.0f, 99.0f, 0.0f }, + { _GEN(SAMPLEID), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(SAMPLEMODE), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(RESERVED3), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(SCALETUNE), 0, 1, 0.0f, 1200.0f, 100.0f }, + { _GEN(EXCLUSIVECLASS), 0, 0, 0.0f, 0.0f, 0.0f }, + { _GEN(OVERRIDEROOTKEY), 1, 0, 0.0f, 127.0f, -1.0f }, + { _GEN(PITCH), 1, 0, 0.0f, 127.0f, 0.0f }, + { _GEN(CUSTOM_BALANCE), 1, 0, -960.0f, 960.0f, 0.0f }, + { _GEN(CUSTOM_FILTERFC), 1, 2, 0.0f, 22050.0f, 0.0f }, + { _GEN(CUSTOM_FILTERQ), 1, 1, 0.0f, 960.0f, 0.0f } +}; + +/* fluid_gen_init + * + * Set an array of generators to their initial value + */ +void +fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel) +{ + int i; + + for(i = 0; i < GEN_LAST; i++) + { + gen[i].flags = GEN_UNUSED; + gen[i].mod = 0.0; + gen[i].nrpn = (channel == NULL) ? 0.0 : fluid_channel_get_gen(channel, i); + gen[i].val = fluid_gen_info[i].def; + } +} + +fluid_real_t fluid_gen_scale(int gen, float value) +{ + return (fluid_gen_info[gen].min + + value * (fluid_gen_info[gen].max - fluid_gen_info[gen].min)); +} + +fluid_real_t fluid_gen_scale_nrpn(int gen, int data) +{ + data = data - 8192; + fluid_clip(data, -8192, 8192); + return (fluid_real_t)(data * fluid_gen_info[gen].nrpn_scale); +} + + +const char *fluid_gen_name(int gen) +{ + return fluid_gen_info[gen].name; +} diff --git a/thirdparty/fluidsynth/src/synth/fluid_gen.h b/thirdparty/fluidsynth/src/synth/fluid_gen.h new file mode 100644 index 000000000..b87e8d8a8 --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_gen.h @@ -0,0 +1,67 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_GEN_H +#define _FLUID_GEN_H + +#include "fluidsynth_priv.h" + +typedef struct _fluid_gen_info_t +{ + char num; /* Generator number */ + char *name; + char init; /* Does the generator need to be initialized (not used) */ + char nrpn_scale; /* The scale to convert from NRPN (cfr. fluid_gen_map_nrpn()) */ + float min; /* The minimum value */ + float max; /* The maximum value */ + float def; /* The default value (cfr. fluid_gen_init()) */ +} fluid_gen_info_t; + +/* + * SoundFont generator structure. + */ +typedef struct _fluid_gen_t +{ + unsigned char flags; /**< Is the generator set or not (#fluid_gen_flags) */ + double val; /**< The nominal value */ + double mod; /**< Change by modulators */ + double nrpn; /**< Change by NRPN messages */ +} fluid_gen_t; + +/* + * Enum value for 'flags' field of #fluid_gen_t (not really flags). + */ +enum fluid_gen_flags +{ + GEN_UNUSED, /**< Generator value is not set */ + GEN_SET, /**< Generator value is set */ +}; + +#define fluid_gen_set_mod(_gen, _val) { (_gen)->mod = (double) (_val); } +#define fluid_gen_set_nrpn(_gen, _val) { (_gen)->nrpn = (double) (_val); } + +fluid_real_t fluid_gen_scale(int gen, float value); +fluid_real_t fluid_gen_scale_nrpn(int gen, int nrpn); +void fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel); +const char *fluid_gen_name(int gen); + + +#endif /* _FLUID_GEN_H */ diff --git a/thirdparty/fluidsynth/src/synth/fluid_mod.c b/thirdparty/fluidsynth/src/synth/fluid_mod.c new file mode 100644 index 000000000..bebfb9df6 --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_mod.c @@ -0,0 +1,889 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_mod.h" +#include "fluid_chan.h" +#include "fluid_voice.h" + +/** + * Clone the modulators destination, sources, flags and amount. + * + * @param mod the modulator to store the copy to + * @param src the source modulator to retrieve the information from + * + * @note The \c next member of \c mod will be left unchanged. + */ +void +fluid_mod_clone(fluid_mod_t *mod, const fluid_mod_t *src) +{ + mod->dest = src->dest; + mod->src1 = src->src1; + mod->flags1 = src->flags1; + mod->src2 = src->src2; + mod->flags2 = src->flags2; + mod->amount = src->amount; +} + +/** + * Set a modulator's primary source controller and flags. + * + * @param mod The modulator instance + * @param src Modulator source (#fluid_mod_src or a MIDI controller number) + * @param flags Flags determining mapping function and whether the source + * controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller + * (#FLUID_MOD_CC), see #fluid_mod_flags. + */ +void +fluid_mod_set_source1(fluid_mod_t *mod, int src, int flags) +{ + mod->src1 = src; + mod->flags1 = flags; +} + +/** + * Set a modulator's secondary source controller and flags. + * + * @param mod The modulator instance + * @param src Modulator source (#fluid_mod_src or a MIDI controller number) + * @param flags Flags determining mapping function and whether the source + * controller is a general controller (#FLUID_MOD_GC) or a MIDI CC controller + * (#FLUID_MOD_CC), see #fluid_mod_flags. + */ +void +fluid_mod_set_source2(fluid_mod_t *mod, int src, int flags) +{ + mod->src2 = src; + mod->flags2 = flags; +} + +/** + * Set the destination effect of a modulator. + * + * @param mod The modulator instance + * @param dest Destination generator (#fluid_gen_type) + */ +void +fluid_mod_set_dest(fluid_mod_t *mod, int dest) +{ + mod->dest = dest; +} + +/** + * Set the scale amount of a modulator. + * + * @param mod The modulator instance + * @param amount Scale amount to assign + */ +void +fluid_mod_set_amount(fluid_mod_t *mod, double amount) +{ + mod->amount = (double) amount; +} + +/** + * Get the primary source value from a modulator. + * + * @param mod The modulator instance + * @return The primary source value (#fluid_mod_src or a MIDI CC controller value). + */ +int +fluid_mod_get_source1(const fluid_mod_t *mod) +{ + return mod->src1; +} + +/** + * Get primary source flags from a modulator. + * + * @param mod The modulator instance + * @return The primary source flags (#fluid_mod_flags). + */ +int +fluid_mod_get_flags1(const fluid_mod_t *mod) +{ + return mod->flags1; +} + +/** + * Get the secondary source value from a modulator. + * + * @param mod The modulator instance + * @return The secondary source value (#fluid_mod_src or a MIDI CC controller value). + */ +int +fluid_mod_get_source2(const fluid_mod_t *mod) +{ + return mod->src2; +} + +/** + * Get secondary source flags from a modulator. + * + * @param mod The modulator instance + * @return The secondary source flags (#fluid_mod_flags). + */ +int +fluid_mod_get_flags2(const fluid_mod_t *mod) +{ + return mod->flags2; +} + +/** + * Get destination effect from a modulator. + * + * @param mod The modulator instance + * @return Destination generator (#fluid_gen_type) + */ +int +fluid_mod_get_dest(const fluid_mod_t *mod) +{ + return mod->dest; +} + +/** + * Get the scale amount from a modulator. + * + * @param mod The modulator instance + * @return Scale amount + */ +double +fluid_mod_get_amount(const fluid_mod_t *mod) +{ + return (double) mod->amount; +} + +/* + * retrieves the initial value from the given source of the modulator + */ +static fluid_real_t +fluid_mod_get_source_value(const unsigned char mod_src, + const unsigned char mod_flags, + fluid_real_t *range, + const fluid_voice_t *voice + ) +{ + const fluid_channel_t *chan = voice->channel; + fluid_real_t val; + + if(mod_flags & FLUID_MOD_CC) + { + val = fluid_channel_get_cc(chan, mod_src); + + /* From MIDI Recommended Practice (RP-036) Default Pan Formula: + * "Since MIDI controller values range from 0 to 127, the exact center + * of the range, 63.5, cannot be represented. Therefore, the effective + * range for CC#10 is modified to be 1 to 127, and values 0 and 1 both + * pan hard left. The recommended method is to subtract 1 from the + * value of CC#10, and saturate the result to be non-negative." + * + * We treat the balance control in exactly the same way, as the same + * problem applies here as well. + */ + if(mod_src == PAN_MSB || mod_src == BALANCE_MSB) + { + *range = 126; + val -= 1; + + if(val < 0) + { + val = 0; + } + } + else if(mod_src == PORTAMENTO_CTRL) + { + // an invalid portamento fromkey should be treated as 0 when it's actually used for moulating + if(!fluid_channel_is_valid_note(val)) + { + val = 0; + } + } + } + else + { + switch(mod_src) + { + case FLUID_MOD_NONE: /* SF 2.01 8.2.1 item 0: src enum=0 => value is 1 */ + val = *range; + break; + + case FLUID_MOD_VELOCITY: + val = fluid_voice_get_actual_velocity(voice); + break; + + case FLUID_MOD_KEY: + val = fluid_voice_get_actual_key(voice); + break; + + case FLUID_MOD_KEYPRESSURE: + val = fluid_channel_get_key_pressure(chan, voice->key); + break; + + case FLUID_MOD_CHANNELPRESSURE: + val = fluid_channel_get_channel_pressure(chan); + break; + + case FLUID_MOD_PITCHWHEEL: + val = fluid_channel_get_pitch_bend(chan); + *range = 0x4000; + break; + + case FLUID_MOD_PITCHWHEELSENS: + val = fluid_channel_get_pitch_wheel_sensitivity(chan); + break; + + default: + FLUID_LOG(FLUID_ERR, "Unknown modulator source '%d', disabling modulator.", mod_src); + val = 0.0; + } + } + + return val; +} + +/** + * transforms the initial value retrieved by \c fluid_mod_get_source_value into [0.0;1.0] + */ +static fluid_real_t +fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, const fluid_real_t range) +{ + /* normalized value, i.e. usually in the range [0;1] */ + const fluid_real_t val_norm = val / range; + + /* we could also only switch case the lower nibble of mod_flags, however + * this would keep us from adding further mod types in the future + * + * instead just remove the flag(s) we already took care of + */ + mod_flags &= ~FLUID_MOD_CC; + + switch(mod_flags/* & 0x0f*/) + { + case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =0 */ + val = val_norm; + break; + + case FLUID_MOD_LINEAR | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =1 */ + val = 1.0f - val_norm; + break; + + case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =2 */ + val = -1.0f + 2.0f * val_norm; + break; + + case FLUID_MOD_LINEAR | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =3 */ + val = 1.0f - 2.0f * val_norm; + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =4 */ + val = fluid_concave(127 * (val_norm)); + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =5 */ + val = fluid_concave(127 * (1.0f - val_norm)); + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =6 */ + val = (val_norm > 0.5f) ? fluid_concave(127 * 2 * (val_norm - 0.5f)) + : -fluid_concave(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_CONCAVE | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =7 */ + val = (val_norm > 0.5f) ? -fluid_concave(127 * 2 * (val_norm - 0.5f)) + : fluid_concave(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =8 */ + val = fluid_convex(127 * (val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =9 */ + val = fluid_convex(127 * (1.0f - val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =10 */ + val = (val_norm > 0.5f) ? fluid_convex(127 * 2 * (val_norm - 0.5f)) + : -fluid_convex(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_CONVEX | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =11 */ + val = (val_norm > 0.5f) ? -fluid_convex(127 * 2 * (val_norm - 0.5f)) + : fluid_convex(127 * 2 * (0.5f - val_norm)); + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* =12 */ + val = (val_norm >= 0.5f) ? 1.0f : 0.0f; + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* =13 */ + val = (val_norm >= 0.5f) ? 0.0f : 1.0f; + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* =14 */ + val = (val_norm >= 0.5f) ? 1.0f : -1.0f; + break; + + case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =15 */ + val = (val_norm >= 0.5f) ? -1.0f : 1.0f; + break; + + /* + * MIDI CCs only have a resolution of 7 bits. The closer val_norm gets to 1, + * the less will be the resulting change of the sinus. When using this sin() + * for scaling the cutoff frequency, there will be no audible difference between + * MIDI CCs 118 to 127. To avoid this waste of CCs multiply with 0.87 + * (at least for unipolar) which makes sin() never get to 1.0 but to 0.98 which + * is close enough. + */ + case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* custom sin(x) */ + val = FLUID_SIN((FLUID_M_PI / 2.0f * 0.87f) * val_norm); + break; + + case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* custom */ + val = FLUID_SIN((FLUID_M_PI / 2.0f * 0.87f) * (1.0f - val_norm)); + break; + + case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* custom */ + val = (val_norm > 0.5f) ? FLUID_SIN(FLUID_M_PI * (val_norm - 0.5f)) + : -FLUID_SIN(FLUID_M_PI * (0.5f - val_norm)); + break; + + case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* custom */ + val = (val_norm > 0.5f) ? -FLUID_SIN(FLUID_M_PI * (val_norm - 0.5f)) + : FLUID_SIN(FLUID_M_PI * (0.5f - val_norm)); + break; + + default: + FLUID_LOG(FLUID_ERR, "Unknown modulator type '%d', disabling modulator.", mod_flags); + val = 0.0f; + break; + } + + return val; +} + +/* + * fluid_mod_get_value. + * Computes and return modulator output following SF2.01 + * (See SoundFont Modulator Controller Model Chapter 9.5). + * + * Output = Transform(Amount * Map(primary source input) * Map(secondary source input)) + * + * Notes: + * 1)fluid_mod_get_value, ignores the Transform operator. The result is: + * + * Output = Amount * Map(primary source input) * Map(secondary source input) + * + * 2)When primary source input (src1) is set to General Controller 'No Controller', + * output is forced to 0. + * + * 3)When secondary source input (src2) is set to General Controller 'No Controller', + * output is forced to +1.0 + */ +fluid_real_t +fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice) +{ + extern fluid_mod_t default_vel2filter_mod; + + fluid_real_t v1 = 0.0, v2 = 1.0; + fluid_real_t range1 = 127.0, range2 = 127.0; + + /* 'special treatment' for default controller + * + * Reference: SF2.01 section 8.4.2 + * + * The GM default controller 'vel-to-filter cut off' is not clearly + * defined: If implemented according to the specs, the filter + * frequency jumps between vel=63 and vel=64. To maintain + * compatibility with existing sound fonts, the implementation is + * 'hardcoded', it is impossible to implement using only one + * modulator otherwise. + * + * I assume here, that the 'intention' of the paragraph is one + * octave (1200 cents) filter frequency shift between vel=127 and + * vel=64. 'amount' is (-2400), at least as long as the controller + * is set to default. + * + * Further, the 'appearance' of the modulator (source enumerator, + * destination enumerator, flags etc) is different from that + * described in section 8.4.2, but it matches the definition used in + * several SF2.1 sound fonts (where it is used only to turn it off). + * */ + if(fluid_mod_test_identity(mod, &default_vel2filter_mod)) + { +// S. Christian Collins' mod, to stop forcing velocity based filtering + /* + if (voice->vel < 64){ + return (fluid_real_t) mod->amount / 2.0; + } else { + return (fluid_real_t) mod->amount * (127 - voice->vel) / 127; + } + */ + return 0; // (fluid_real_t) mod->amount / 2.0; + } + +// end S. Christian Collins' mod + + /* get the initial value of the first source */ + if(mod->src1 > 0) + { + v1 = fluid_mod_get_source_value(mod->src1, mod->flags1, &range1, voice); + + /* transform the input value */ + v1 = fluid_mod_transform_source_value(v1, mod->flags1, range1); + } + /* When primary source input (src1) is set to General Controller 'No Controller', + output is forced to 0.0 + */ + else + { + return 0.0; + } + + /* no need to go further */ + if(v1 == 0.0f) + { + return 0.0f; + } + + /* get the second input source */ + if(mod->src2 > 0) + { + v2 = fluid_mod_get_source_value(mod->src2, mod->flags2, &range2, voice); + + /* transform the second input value */ + v2 = fluid_mod_transform_source_value(v2, mod->flags2, range2); + } + /* When secondary source input (src2) is set to General Controller 'No Controller', + output is forced to +1.0 + */ + else + { + v2 = 1.0f; + } + + /* it's as simple as that: */ + return (fluid_real_t) mod->amount * v1 * v2; +} + +/** + * Create a new uninitialized modulator structure. + * + * @return New allocated modulator or NULL if out of memory + */ +fluid_mod_t * +new_fluid_mod() +{ + fluid_mod_t *mod = FLUID_NEW(fluid_mod_t); + + if(mod == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + return mod; +} + +/** + * Free a modulator structure. + * + * @param mod Modulator to free + */ +void +delete_fluid_mod(fluid_mod_t *mod) +{ + FLUID_FREE(mod); +} + +/** + * Returns the size of the fluid_mod_t structure. + * + * @return Size of fluid_mod_t in bytes + * + * Useful in low latency scenarios e.g. to allocate a modulator on the stack. + */ +size_t fluid_mod_sizeof() +{ + return sizeof(fluid_mod_t); +} + +/** + * Checks if modulator with source 1 other than CC is FLUID_MOD_NONE. + * + * @param mod, modulator. + * @return TRUE if modulator source 1 other than cc is FLUID_MOD_NONE, FALSE otherwise. + */ +static int +fluid_mod_is_src1_none(const fluid_mod_t *mod) +{ + return(((mod->flags1 & FLUID_MOD_CC) == 0) && (mod->src1 == FLUID_MOD_NONE)); +} + +/** + * Checks if modulators source other than CC source is invalid. + * + * @param mod, modulator. + * @param src1_select, source input selection to check. + * 1 to check src1 source. + * 0 to check src2 source. + * @return FALSE if selected modulator source other than cc is invalid, TRUE otherwise. + * + * (specs SF 2.01 7.4, 7.8, 8.2.1) + */ +static int +fluid_mod_check_non_cc_source(const fluid_mod_t *mod, unsigned char src1_select) +{ + unsigned char flags, src; + + if(src1_select) + { + flags = mod->flags1; + src = mod->src1; + } + else + { + flags = mod->flags2; + src = mod->src2; + } + + return(((flags & FLUID_MOD_CC) != 0) /* src is a CC */ + /* SF2.01 section 8.2.1: Constant value */ + || ((src == FLUID_MOD_NONE) + || (src == FLUID_MOD_VELOCITY) /* Note-on velocity */ + || (src == FLUID_MOD_KEY) /* Note-on key number */ + || (src == FLUID_MOD_KEYPRESSURE) /* Poly pressure */ + || (src == FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */ + || (src == FLUID_MOD_PITCHWHEEL) /* Pitch wheel */ + || (src == FLUID_MOD_PITCHWHEELSENS) /* Pitch wheel sensitivity */ + )); +} + +/** + * Checks if modulator CC source is invalid (specs SF 2.01 7.4, 7.8, 8.2.1). + * + * @param mod, modulator. + * @src1_select, source input selection: + * 1 to check src1 source or + * 0 to check src2 source. + * @return FALSE if selected modulator's source CC is invalid, TRUE otherwise. + */ +static int +fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select) +{ + unsigned char flags, src; + + if(src1_select) + { + flags = mod->flags1; + src = mod->src1; + } + else + { + flags = mod->flags2; + src = mod->src2; + } + + return(((flags & FLUID_MOD_CC) == 0) /* src is non CC */ + || ((src != BANK_SELECT_MSB) + && (src != BANK_SELECT_LSB) + && (src != DATA_ENTRY_MSB) + && (src != DATA_ENTRY_LSB) + /* is src not NRPN_LSB, NRPN_MSB, RPN_LSB, RPN_MSB */ + && ((src < NRPN_LSB) || (RPN_MSB < src)) + /* is src not ALL_SOUND_OFF, ALL_CTRL_OFF, LOCAL_CONTROL, ALL_NOTES_OFF ? */ + /* is src not OMNI_OFF, OMNI_ON, POLY_OFF, POLY_ON ? */ + && (src < ALL_SOUND_OFF) + /* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1) + However, as long fluidsynth will use only CC 7 bits resolution, + it is safe to ignore these SF recommendations on CC receive. + See explanations in fluid_synth_cc_LOCAL() */ + /* uncomment next line to forbid CC lsb */ + /* && ((src < 32) || (63 < src)) */ + )); +} + +/** + * Checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1) + * + * @param mod, modulator. + * @param name,if not NULL, pointer on a string displayed as a warning. + * @return TRUE if modulator sources src1, src2 are valid, FALSE otherwise. + */ +int fluid_mod_check_sources(const fluid_mod_t *mod, char *name) +{ + static const char invalid_non_cc_src[] = + "Invalid modulator, using non-CC source %s.src%d=%d"; + static const char invalid_cc_src[] = + "Invalid modulator, using CC source %s.src%d=%d"; + static const char src1_is_none[] = + "Modulator with source 1 none %s.src1=%d"; + + /* checks valid non cc sources */ + if(!fluid_mod_check_non_cc_source(mod, 1)) /* check src1 */ + { + if(name) + { + FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 1, mod->src1); + } + + return FALSE; + } + + /* + When src1 is non CC source FLUID_MOD_NONE, the modulator is valid but + the output of this modulator will be forced to 0 at synthesis time. + Also this modulator cannot be used to overwrite a default modulator (as + there is no default modulator with src1 source equal to FLUID_MOD_NONE). + Consequently it is useful to return FALSE to indicate this modulator + being useless. It will be removed later with others invalid modulators. + */ + if(fluid_mod_is_src1_none(mod)) + { + if(name) + { + FLUID_LOG(FLUID_WARN, src1_is_none, name, mod->src1); + } + + return FALSE; + } + + if(!fluid_mod_check_non_cc_source(mod, 0)) /* check src2 */ + { + if(name) + { + FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 2, mod->src2); + } + + return FALSE; + } + + /* checks valid cc sources */ + if(!fluid_mod_check_cc_source(mod, 1)) /* check src1 */ + { + if(name) + { + FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 1, mod->src1); + } + + return FALSE; + } + + if(!fluid_mod_check_cc_source(mod, 0)) /* check src2 */ + { + if(name) + { + FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 2, mod->src2); + } + + return FALSE; + } + + return TRUE; +} + +/** + * Checks if two modulators are identical in sources, flags and destination. + * + * @param mod1 First modulator + * @param mod2 Second modulator + * @return TRUE if identical, FALSE otherwise + * + * SF2.01 section 9.5.1 page 69, 'bullet' 3 defines 'identical'. + */ +int +fluid_mod_test_identity(const fluid_mod_t *mod1, const fluid_mod_t *mod2) +{ + return mod1->dest == mod2->dest + && mod1->src1 == mod2->src1 + && mod1->src2 == mod2->src2 + && mod1->flags1 == mod2->flags1 + && mod1->flags2 == mod2->flags2; +} + +/** + * Check if the modulator has the given source. + * + * @param mod The modulator instance + * @param cc Boolean value indicating if ctrl is a CC controller or not + * @param ctrl The source to check for (if \c cc == FALSE : a value of type #fluid_mod_src, else the value of the MIDI CC to check for) + * + * @return TRUE if the modulator has the given source, FALSE otherwise. + */ +int fluid_mod_has_source(const fluid_mod_t *mod, int cc, int ctrl) +{ + return + ( + ( + ((mod->src1 == ctrl) && ((mod->flags1 & FLUID_MOD_CC) != 0) && (cc != 0)) + || ((mod->src1 == ctrl) && ((mod->flags1 & FLUID_MOD_CC) == 0) && (cc == 0)) + ) + || + ( + ((mod->src2 == ctrl) && ((mod->flags2 & FLUID_MOD_CC) != 0) && (cc != 0)) + || ((mod->src2 == ctrl) && ((mod->flags2 & FLUID_MOD_CC) == 0) && (cc == 0)) + ) + ); +} + +/** + * Check if the modulator has the given destination. + * + * @param mod The modulator instance + * @param gen The destination generator of type #fluid_gen_type to check for + * @return TRUE if the modulator has the given destination, FALSE otherwise. + */ +int fluid_mod_has_dest(const fluid_mod_t *mod, int gen) +{ + return mod->dest == gen; +} + + +/* debug function: Prints the contents of a modulator */ +#ifdef DEBUG +void fluid_dump_modulator(fluid_mod_t *mod) +{ + int src1 = mod->src1; + int dest = mod->dest; + int src2 = mod->src2; + int flags1 = mod->flags1; + int flags2 = mod->flags2; + fluid_real_t amount = (fluid_real_t)mod->amount; + + printf("Src: "); + + if(flags1 & FLUID_MOD_CC) + { + printf("MIDI CC=%i", src1); + } + else + { + switch(src1) + { + case FLUID_MOD_NONE: + printf("None"); + break; + + case FLUID_MOD_VELOCITY: + printf("note-on velocity"); + break; + + case FLUID_MOD_KEY: + printf("Key nr"); + break; + + case FLUID_MOD_KEYPRESSURE: + printf("Poly pressure"); + break; + + case FLUID_MOD_CHANNELPRESSURE: + printf("Chan pressure"); + break; + + case FLUID_MOD_PITCHWHEEL: + printf("Pitch Wheel"); + break; + + case FLUID_MOD_PITCHWHEELSENS: + printf("Pitch Wheel sens"); + break; + + default: + printf("(unknown: %i)", src1); + }; /* switch src1 */ + }; /* if not CC */ + + if(flags1 & FLUID_MOD_NEGATIVE) + { + printf("- "); + } + else + { + printf("+ "); + }; + + if(flags1 & FLUID_MOD_BIPOLAR) + { + printf("bip "); + } + else + { + printf("unip "); + }; + + printf("-> "); + + switch(dest) + { + case GEN_FILTERQ: + printf("Q"); + break; + + case GEN_FILTERFC: + printf("fc"); + break; + + case GEN_CUSTOM_FILTERQ: + printf("custom-Q"); + break; + + case GEN_CUSTOM_FILTERFC: + printf("custom-fc"); + break; + + case GEN_VIBLFOTOPITCH: + printf("VibLFO-to-pitch"); + break; + + case GEN_MODENVTOPITCH: + printf("ModEnv-to-pitch"); + break; + + case GEN_MODLFOTOPITCH: + printf("ModLFO-to-pitch"); + break; + + case GEN_CHORUSSEND: + printf("Chorus send"); + break; + + case GEN_REVERBSEND: + printf("Reverb send"); + break; + + case GEN_PAN: + printf("pan"); + break; + + case GEN_CUSTOM_BALANCE: + printf("balance"); + break; + + case GEN_ATTENUATION: + printf("att"); + break; + + default: + printf("dest %i", dest); + }; /* switch dest */ + + printf(", amount %f flags %i src2 %i flags2 %i\n", amount, flags1, src2, flags2); +}; +#endif + diff --git a/thirdparty/fluidsynth/src/synth/fluid_mod.h b/thirdparty/fluidsynth/src/synth/fluid_mod.h new file mode 100644 index 000000000..3e7661741 --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_mod.h @@ -0,0 +1,54 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_MOD_H +#define _FLUID_MOD_H + +#include "fluidsynth_priv.h" +#include "fluid_conv.h" + +/* + * Modulator structure. See SoundFont 2.04 PDF section 8.2. + */ +struct _fluid_mod_t +{ + unsigned char dest; /**< Destination generator to control */ + unsigned char src1; /**< Source controller 1 */ + unsigned char flags1; /**< Source controller 1 flags */ + unsigned char src2; /**< Source controller 2 */ + unsigned char flags2; /**< Source controller 2 flags */ + double amount; /**< Multiplier amount */ + /* The 'next' field allows to link modulators into a list. It is + * not used in fluid_voice.c, there each voice allocates memory for a + * fixed number of modulators. Since there may be a huge number of + * different zones, this is more efficient. + */ + fluid_mod_t *next; +}; + +fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice); +int fluid_mod_check_sources(const fluid_mod_t *mod, char *name); + +#ifdef DEBUG +void fluid_dump_modulator(fluid_mod_t *mod); +#endif + + +#endif /* _FLUID_MOD_H */ diff --git a/thirdparty/fluidsynth/src/synth/fluid_synth.c b/thirdparty/fluidsynth/src/synth/fluid_synth.c new file mode 100644 index 000000000..7d058258e --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_synth.c @@ -0,0 +1,8428 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_synth.h" +#include "fluid_sys.h" +#include "fluid_chan.h" +#include "fluid_tuning.h" +#include "fluid_settings.h" +#include "fluid_sfont.h" +#include "fluid_defsfont.h" +#include "fluid_instpatch.h" + +#ifdef TRAP_ON_FPE +#define _GNU_SOURCE +#include + +/* seems to not be declared in fenv.h */ +extern int feenableexcept(int excepts); +#endif + +#define FLUID_API_RETURN(return_value) \ + do { fluid_synth_api_exit(synth); \ + return return_value; } while (0) + +#define FLUID_API_RETURN_IF_CHAN_DISABLED(return_value) \ + do { if (FLUID_LIKELY(synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED)) \ + {} \ + else \ + { FLUID_API_RETURN(return_value); } \ + } while (0) + +#define FLUID_API_ENTRY_CHAN(fail_value) \ + fluid_return_val_if_fail (synth != NULL, fail_value); \ + fluid_return_val_if_fail (chan >= 0, fail_value); \ + fluid_synth_api_enter(synth); \ + if (chan >= synth->midi_channels) { \ + FLUID_API_RETURN(fail_value); \ + } \ + +static void fluid_synth_init(void); +static void fluid_synth_api_enter(fluid_synth_t *synth); +static void fluid_synth_api_exit(fluid_synth_t *synth); + +static int fluid_synth_noteon_LOCAL(fluid_synth_t *synth, int chan, int key, + int vel); +static int fluid_synth_noteoff_LOCAL(fluid_synth_t *synth, int chan, int key); +static int fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num); +static int fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, + int len, char *response, + int *response_len, int avail_response, + int *handled, int dryrun); +static int fluid_synth_sysex_gs_dt1(fluid_synth_t *synth, const char *data, + int len, char *response, + int *response_len, int avail_response, + int *handled, int dryrun); +static int fluid_synth_sysex_xg(fluid_synth_t *synth, const char *data, + int len, char *response, + int *response_len, int avail_response, + int *handled, int dryrun); +int fluid_synth_all_notes_off_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_all_sounds_off_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_system_reset_LOCAL(fluid_synth_t *synth); +static int fluid_synth_modulate_voices_LOCAL(fluid_synth_t *synth, int chan, + int is_cc, int ctrl); +static int fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t *synth, int channum); +static int fluid_synth_update_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key); +static int fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t *synth, int chan); +static int fluid_synth_set_preset(fluid_synth_t *synth, int chan, + fluid_preset_t *preset); +static int fluid_synth_reverb_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value); +static int fluid_synth_chorus_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value); + +static fluid_preset_t * +fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum, + int banknum, int prognum); +static fluid_preset_t * +fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname, + int banknum, int prognum); + +static void fluid_synth_update_presets(fluid_synth_t *synth); +static void fluid_synth_update_gain_LOCAL(fluid_synth_t *synth); +static int fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony); +static void init_dither(void); +static FLUID_INLINE int16_t round_clip_to_i16(float x); +static int fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount); + +static fluid_voice_t *fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth); +static void fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth, + fluid_voice_t *new_voice); +static int fluid_synth_sfunload_callback(void *data, unsigned int msec); +static fluid_tuning_t *fluid_synth_get_tuning(fluid_synth_t *synth, + int bank, int prog); +static int fluid_synth_replace_tuning_LOCK(fluid_synth_t *synth, + fluid_tuning_t *tuning, + int bank, int prog, int apply); +static void fluid_synth_replace_tuning_LOCAL(fluid_synth_t *synth, + fluid_tuning_t *old_tuning, + fluid_tuning_t *new_tuning, + int apply, int unref_new); +static void fluid_synth_update_voice_tuning_LOCAL(fluid_synth_t *synth, + fluid_channel_t *channel); +static int fluid_synth_set_tuning_LOCAL(fluid_synth_t *synth, int chan, + fluid_tuning_t *tuning, int apply); +static void fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, + int param, float value); +static void fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id); + + +static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels); + + +/* Callback handlers for real-time settings */ +static void fluid_synth_handle_gain(void *data, const char *name, double value); +static void fluid_synth_handle_polyphony(void *data, const char *name, int value); +static void fluid_synth_handle_device_id(void *data, const char *name, int value); +static void fluid_synth_handle_overflow(void *data, const char *name, double value); +static void fluid_synth_handle_important_channels(void *data, const char *name, + const char *value); +static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value); +static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value); + + +static void fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan); +static int fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val); +static void fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val); + +/*************************************************************** + * + * GLOBAL + */ + +/* has the synth module been initialized? */ +/* fluid_atomic_int_t may be anything, so init with {0} to catch most cases */ +static fluid_atomic_int_t fluid_synth_initialized = {0}; + +/* default modulators + * SF2.01 page 52 ff: + * + * There is a set of predefined default modulators. They have to be + * explicitly overridden by the sound font in order to turn them off. + */ + +static fluid_mod_t default_vel2att_mod; /* SF2.01 section 8.4.1 */ +/*not static */ fluid_mod_t default_vel2filter_mod; /* SF2.01 section 8.4.2 */ +static fluid_mod_t default_at2viblfo_mod; /* SF2.01 section 8.4.3 */ +static fluid_mod_t default_mod2viblfo_mod; /* SF2.01 section 8.4.4 */ +static fluid_mod_t default_att_mod; /* SF2.01 section 8.4.5 */ +static fluid_mod_t default_pan_mod; /* SF2.01 section 8.4.6 */ +static fluid_mod_t default_expr_mod; /* SF2.01 section 8.4.7 */ +static fluid_mod_t default_reverb_mod; /* SF2.01 section 8.4.8 */ +static fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */ +static fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */ +static fluid_mod_t custom_balance_mod; /* Non-standard modulator */ + + +/* custom_breath2att_modulator is not a default modulator specified in SF +it is intended to replace default_vel2att_mod on demand using +API fluid_set_breath_mode() or command shell setbreathmode. +*/ +static fluid_mod_t custom_breath2att_mod; + +/* reverb presets */ +static const fluid_revmodel_presets_t revmodel_preset[] = +{ + /* name */ /* roomsize */ /* damp */ /* width */ /* level */ + { "Test 1", 0.2f, 0.0f, 0.5f, 0.9f }, + { "Test 2", 0.4f, 0.2f, 0.5f, 0.8f }, + { "Test 3", 0.6f, 0.4f, 0.5f, 0.7f }, + { "Test 4", 0.8f, 0.7f, 0.5f, 0.6f }, + { "Test 5", 0.8f, 1.0f, 0.5f, 0.5f }, +}; + + +/*************************************************************** + * + * INITIALIZATION & UTILITIES + */ + +void fluid_synth_settings(fluid_settings_t *settings) +{ + fluid_settings_register_int(settings, "synth.verbose", 0, 0, 1, FLUID_HINT_TOGGLED); + + fluid_settings_register_int(settings, "synth.reverb.active", 1, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_num(settings, "synth.reverb.room-size", FLUID_REVERB_DEFAULT_ROOMSIZE, 0.0f, 1.0f, 0); + fluid_settings_register_num(settings, "synth.reverb.damp", FLUID_REVERB_DEFAULT_DAMP, 0.0f, 1.0f, 0); + fluid_settings_register_num(settings, "synth.reverb.width", FLUID_REVERB_DEFAULT_WIDTH, 0.0f, 100.0f, 0); + fluid_settings_register_num(settings, "synth.reverb.level", FLUID_REVERB_DEFAULT_LEVEL, 0.0f, 1.0f, 0); + + fluid_settings_register_int(settings, "synth.chorus.active", 1, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_int(settings, "synth.chorus.nr", FLUID_CHORUS_DEFAULT_N, 0, 99, 0); + fluid_settings_register_num(settings, "synth.chorus.level", FLUID_CHORUS_DEFAULT_LEVEL, 0.0f, 10.0f, 0); + fluid_settings_register_num(settings, "synth.chorus.speed", FLUID_CHORUS_DEFAULT_SPEED, 0.1f, 5.0f, 0); + fluid_settings_register_num(settings, "synth.chorus.depth", FLUID_CHORUS_DEFAULT_DEPTH, 0.0f, 256.0f, 0); + + fluid_settings_register_int(settings, "synth.ladspa.active", 0, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_int(settings, "synth.lock-memory", 1, 0, 1, FLUID_HINT_TOGGLED); + fluid_settings_register_str(settings, "midi.portname", "", 0); + +#ifdef DEFAULT_SOUNDFONT + fluid_settings_register_str(settings, "synth.default-soundfont", DEFAULT_SOUNDFONT, 0); +#endif + + fluid_settings_register_int(settings, "synth.polyphony", 256, 1, 65535, 0); + fluid_settings_register_int(settings, "synth.midi-channels", 16, 16, 256, 0); + fluid_settings_register_num(settings, "synth.gain", 0.2f, 0.0f, 10.0f, 0); + fluid_settings_register_int(settings, "synth.audio-channels", 1, 1, 128, 0); + fluid_settings_register_int(settings, "synth.audio-groups", 1, 1, 128, 0); + fluid_settings_register_int(settings, "synth.effects-channels", 2, 2, 2, 0); + fluid_settings_register_int(settings, "synth.effects-groups", 1, 1, 128, 0); + fluid_settings_register_num(settings, "synth.sample-rate", 44100.0f, 8000.0f, 96000.0f, 0); + fluid_settings_register_int(settings, "synth.device-id", 0, 0, 126, 0); +#ifdef ENABLE_MIXER_THREADS + fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 256, 0); +#else + fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 1, 0); +#endif + + fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0); + + fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED); + + fluid_settings_register_num(settings, "synth.overflow.percussion", 4000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.sustained", -1000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.released", -2000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.age", 1000, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.volume", 500, -10000, 10000, 0); + fluid_settings_register_num(settings, "synth.overflow.important", 5000, -50000, 50000, 0); + fluid_settings_register_str(settings, "synth.overflow.important-channels", "", 0); + + fluid_settings_register_str(settings, "synth.midi-bank-select", "gs", 0); + fluid_settings_add_option(settings, "synth.midi-bank-select", "gm"); + fluid_settings_add_option(settings, "synth.midi-bank-select", "gs"); + fluid_settings_add_option(settings, "synth.midi-bank-select", "xg"); + fluid_settings_add_option(settings, "synth.midi-bank-select", "mma"); + + fluid_settings_register_int(settings, "synth.dynamic-sample-loading", 0, 0, 1, FLUID_HINT_TOGGLED); +} + +/** + * Get FluidSynth runtime version. + * @param major Location to store major number + * @param minor Location to store minor number + * @param micro Location to store micro number + */ +void fluid_version(int *major, int *minor, int *micro) +{ + *major = FLUIDSYNTH_VERSION_MAJOR; + *minor = FLUIDSYNTH_VERSION_MINOR; + *micro = FLUIDSYNTH_VERSION_MICRO; +} + +/** + * Get FluidSynth runtime version as a string. + * @return FluidSynth version string, which is internal and should not be + * modified or freed. + */ +char * +fluid_version_str(void) +{ + return FLUIDSYNTH_VERSION; +} + +/* + * void fluid_synth_init + * + * Does all the initialization for this module. + */ +static void +fluid_synth_init(void) +{ +#ifdef TRAP_ON_FPE + /* Turn on floating point exception traps */ + feenableexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_INVALID); +#endif + + init_dither(); + + /* custom_breath2att_mod is not a default modulator specified in SF2.01. + it is intended to replace default_vel2att_mod on demand using + API fluid_set_breath_mode() or command shell setbreathmode. + */ + fluid_mod_set_source1(&custom_breath2att_mod, /* The modulator we are programming here */ + BREATH_MSB, /* Source. breath MSB corresponds to 2. */ + FLUID_MOD_CC /* MIDI continuous controller */ + | FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */ + | FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */ + | FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */ + ); + fluid_mod_set_source2(&custom_breath2att_mod, 0, 0); /* No 2nd source */ + fluid_mod_set_dest(&custom_breath2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&custom_breath2att_mod, FLUID_PEAK_ATTENUATION); /* Modulation amount: 960 */ + + /* SF2.01 page 53 section 8.4.1: MIDI Note-On Velocity to Initial Attenuation */ + fluid_mod_set_source1(&default_vel2att_mod, /* The modulator we are programming here */ + FLUID_MOD_VELOCITY, /* Source. VELOCITY corresponds to 'index=2'. */ + FLUID_MOD_GC /* Not a MIDI continuous controller */ + | FLUID_MOD_CONCAVE /* Curve shape. Corresponds to 'type=1' */ + | FLUID_MOD_UNIPOLAR /* Polarity. Corresponds to 'P=0' */ + | FLUID_MOD_NEGATIVE /* Direction. Corresponds to 'D=1' */ + ); + fluid_mod_set_source2(&default_vel2att_mod, 0, 0); /* No 2nd source */ + fluid_mod_set_dest(&default_vel2att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&default_vel2att_mod, FLUID_PEAK_ATTENUATION); /* Modulation amount: 960 */ + + + + /* SF2.01 page 53 section 8.4.2: MIDI Note-On Velocity to Filter Cutoff + * Have to make a design decision here. The specs don't make any sense this way or another. + * One sound font, 'Kingston Piano', which has been praised for its quality, tries to + * override this modulator with an amount of 0 and positive polarity (instead of what + * the specs say, D=1) for the secondary source. + * So if we change the polarity to 'positive', one of the best free sound fonts works... + */ + fluid_mod_set_source1(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_NEGATIVE /* D=1 */ + ); + fluid_mod_set_source2(&default_vel2filter_mod, FLUID_MOD_VELOCITY, /* Index=2 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_SWITCH /* type=3 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + // do not remove | FLUID_MOD_NEGATIVE /* D=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_dest(&default_vel2filter_mod, GEN_FILTERFC); /* Target: Initial filter cutoff */ + fluid_mod_set_amount(&default_vel2filter_mod, -2400); + + + + /* SF2.01 page 53 section 8.4.3: MIDI Channel pressure to Vibrato LFO pitch depth */ + fluid_mod_set_source1(&default_at2viblfo_mod, FLUID_MOD_CHANNELPRESSURE, /* Index=13 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_at2viblfo_mod, 0, 0); /* no second source */ + fluid_mod_set_dest(&default_at2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */ + fluid_mod_set_amount(&default_at2viblfo_mod, 50); + + + + /* SF2.01 page 53 section 8.4.4: Mod wheel (Controller 1) to Vibrato LFO pitch depth */ + fluid_mod_set_source1(&default_mod2viblfo_mod, MODULATION_MSB, /* Index=1 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_mod2viblfo_mod, 0, 0); /* no second source */ + fluid_mod_set_dest(&default_mod2viblfo_mod, GEN_VIBLFOTOPITCH); /* Target: Vib. LFO => pitch */ + fluid_mod_set_amount(&default_mod2viblfo_mod, 50); + + + + /* SF2.01 page 55 section 8.4.5: MIDI continuous controller 7 to initial attenuation*/ + fluid_mod_set_source1(&default_att_mod, VOLUME_MSB, /* index=7 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_CONCAVE /* type=1 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_NEGATIVE /* D=1 */ + ); + fluid_mod_set_source2(&default_att_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_att_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&default_att_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ + + + + /* SF2.01 page 55 section 8.4.6 MIDI continuous controller 10 to Pan Position */ + fluid_mod_set_source1(&default_pan_mod, PAN_MSB, /* index=10 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_BIPOLAR /* P=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_pan_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_pan_mod, GEN_PAN); /* Target: pan */ + /* Amount: 500. The SF specs $8.4.6, p. 55 says: "Amount = 1000 + tenths of a percent". The center value (64) corresponds to 50%, + so it follows that amount = 50% x 1000/% = 500. */ + fluid_mod_set_amount(&default_pan_mod, 500.0); + + + /* SF2.01 page 55 section 8.4.7: MIDI continuous controller 11 to initial attenuation*/ + fluid_mod_set_source1(&default_expr_mod, EXPRESSION_MSB, /* index=11 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_CONCAVE /* type=1 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_NEGATIVE /* D=1 */ + ); + fluid_mod_set_source2(&default_expr_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_expr_mod, GEN_ATTENUATION); /* Target: Initial attenuation */ + fluid_mod_set_amount(&default_expr_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ + + + + /* SF2.01 page 55 section 8.4.8: MIDI continuous controller 91 to Reverb send */ + fluid_mod_set_source1(&default_reverb_mod, EFFECTS_DEPTH1, /* index=91 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_reverb_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_reverb_mod, GEN_REVERBSEND); /* Target: Reverb send */ + fluid_mod_set_amount(&default_reverb_mod, 200); /* Amount: 200 ('tenths of a percent') */ + + + + /* SF2.01 page 55 section 8.4.9: MIDI continuous controller 93 to Chorus send */ + fluid_mod_set_source1(&default_chorus_mod, EFFECTS_DEPTH3, /* index=93 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_chorus_mod, 0, 0); /* No second source */ + fluid_mod_set_dest(&default_chorus_mod, GEN_CHORUSSEND); /* Target: Chorus */ + fluid_mod_set_amount(&default_chorus_mod, 200); /* Amount: 200 ('tenths of a percent') */ + + + + /* SF2.01 page 57 section 8.4.10 MIDI Pitch Wheel to Initial Pitch ... */ + /* Initial Pitch is not a "standard" generator, because it isn't mentioned in the + list of generators in the SF2 specifications. That's why destination Initial Pitch + is replaced here by fine tune generator. + */ + fluid_mod_set_source1(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEEL, /* Index=14 */ + FLUID_MOD_GC /* CC =0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_BIPOLAR /* P=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&default_pitch_bend_mod, FLUID_MOD_PITCHWHEELSENS, /* Index = 16 */ + FLUID_MOD_GC /* CC=0 */ + | FLUID_MOD_LINEAR /* type=0 */ + | FLUID_MOD_UNIPOLAR /* P=0 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + /* Also see the comment in gen.h about GEN_PITCH */ + fluid_mod_set_dest(&default_pitch_bend_mod, GEN_FINETUNE); /* Destination: Fine Tune */ + fluid_mod_set_amount(&default_pitch_bend_mod, 12700.0); /* Amount: 12700 cents */ + + + /* Non-standard MIDI continuous controller 8 to channel stereo balance */ + fluid_mod_set_source1(&custom_balance_mod, BALANCE_MSB, /* Index=8 */ + FLUID_MOD_CC /* CC=1 */ + | FLUID_MOD_CONCAVE /* type=1 */ + | FLUID_MOD_BIPOLAR /* P=1 */ + | FLUID_MOD_POSITIVE /* D=0 */ + ); + fluid_mod_set_source2(&custom_balance_mod, 0, 0); + fluid_mod_set_dest(&custom_balance_mod, GEN_CUSTOM_BALANCE); /* Destination: stereo balance */ + /* Amount: 96 dB of attenuation (on the opposite channel) */ + fluid_mod_set_amount(&custom_balance_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ + +#if defined(LIBINSTPATCH_SUPPORT) + /* defer libinstpatch init to fluid_instpatch.c to avoid #include "libinstpatch.h" */ + if(!fluid_instpatch_supports_multi_init()) + { + fluid_instpatch_init(); + } +#endif +} + +static FLUID_INLINE unsigned int fluid_synth_get_ticks(fluid_synth_t *synth) +{ + return fluid_atomic_int_get(&synth->ticks_since_start); +} + +static FLUID_INLINE void fluid_synth_add_ticks(fluid_synth_t *synth, int val) +{ + fluid_atomic_int_add(&synth->ticks_since_start, val); +} + + +/*************************************************************** + * FLUID SAMPLE TIMERS + * Timers that use written audio data as timing reference + */ +struct _fluid_sample_timer_t +{ + fluid_sample_timer_t *next; /* Single linked list of timers */ + unsigned long starttick; + fluid_timer_callback_t callback; + void *data; + int isfinished; +}; + +/* + * fluid_sample_timer_process - called when synth->ticks is updated + */ +static void fluid_sample_timer_process(fluid_synth_t *synth) +{ + fluid_sample_timer_t *st; + long msec; + int cont; + unsigned int ticks = fluid_synth_get_ticks(synth); + + for(st = synth->sample_timers; st; st = st->next) + { + if(st->isfinished) + { + continue; + } + + msec = (long)(1000.0 * ((double)(ticks - st->starttick)) / synth->sample_rate); + cont = (*st->callback)(st->data, msec); + + if(cont == 0) + { + st->isfinished = 1; + } + } +} + +fluid_sample_timer_t *new_fluid_sample_timer(fluid_synth_t *synth, fluid_timer_callback_t callback, void *data) +{ + fluid_sample_timer_t *result = FLUID_NEW(fluid_sample_timer_t); + + if(result == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + fluid_sample_timer_reset(synth, result); + result->data = data; + result->callback = callback; + result->next = synth->sample_timers; + synth->sample_timers = result; + return result; +} + +void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer) +{ + fluid_sample_timer_t **ptr; + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(timer != NULL); + + ptr = &synth->sample_timers; + + while(*ptr) + { + if(*ptr == timer) + { + *ptr = timer->next; + FLUID_FREE(timer); + return; + } + + ptr = &((*ptr)->next); + } +} + +void fluid_sample_timer_reset(fluid_synth_t *synth, fluid_sample_timer_t *timer) +{ + timer->starttick = fluid_synth_get_ticks(synth); + timer->isfinished = 0; +} + +/*************************************************************** + * + * FLUID SYNTH + */ + +static FLUID_INLINE void +fluid_synth_update_mixer(fluid_synth_t *synth, fluid_rvoice_function_t method, int intparam, + fluid_real_t realparam) +{ + fluid_return_if_fail(synth != NULL && synth->eventhandler != NULL); + fluid_return_if_fail(synth->eventhandler->mixer != NULL); + fluid_rvoice_eventhandler_push_int_real(synth->eventhandler, method, + synth->eventhandler->mixer, + intparam, realparam); +} + +static FLUID_INLINE unsigned int fluid_synth_get_min_note_length_LOCAL(fluid_synth_t *synth) +{ + int i; + fluid_settings_getint(synth->settings, "synth.min-note-length", &i); + return (unsigned int)(i * synth->sample_rate / 1000.0f); +} + +/** + * Create new FluidSynth instance. + * @param settings Configuration parameters to use (used directly). + * @return New FluidSynth instance or NULL on error + * + * @note The @p settings parameter is used directly, but the synth does not take ownership of it. + * Hence, the caller is responsible for freeing it, when no longer needed. + * Further note that you may modify FluidSettings of the + * @p settings instance. However, only those FluidSettings marked as 'realtime' will + * affect the synth immediately. See the \ref fluidsettings for more details. + * + * @warning The @p settings object should only be used by a single synth at a time. I.e. creating + * multiple synth instances with a single @p settings object causes undefined behavior. Once the + * "single synth" has been deleted, you may use the @p settings object again for another synth. + */ +fluid_synth_t * +new_fluid_synth(fluid_settings_t *settings) +{ + fluid_synth_t *synth; + fluid_sfloader_t *loader; + char *important_channels; + int i, prio_level = 0; + int with_ladspa = 0; + double sample_rate_min, sample_rate_max; + + /* initialize all the conversion tables and other stuff */ + if(fluid_atomic_int_compare_and_exchange(&fluid_synth_initialized, 0, 1)) + { + fluid_synth_init(); + } + + /* allocate a new synthesizer object */ + synth = FLUID_NEW(fluid_synth_t); + + if(synth == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(synth, 0, sizeof(fluid_synth_t)); + +#if defined(LIBINSTPATCH_SUPPORT) + if(fluid_instpatch_supports_multi_init()) + { + fluid_instpatch_init(); + } +#endif + + fluid_rec_mutex_init(synth->mutex); + fluid_settings_getint(settings, "synth.threadsafe-api", &synth->use_mutex); + synth->public_api_count = 0; + + synth->settings = settings; + + fluid_settings_getint(settings, "synth.reverb.active", &synth->with_reverb); + fluid_settings_getint(settings, "synth.chorus.active", &synth->with_chorus); + fluid_settings_getint(settings, "synth.verbose", &synth->verbose); + + fluid_settings_getint(settings, "synth.polyphony", &synth->polyphony); + fluid_settings_getnum(settings, "synth.sample-rate", &synth->sample_rate); + fluid_settings_getnum_range(settings, "synth.sample-rate", &sample_rate_min, &sample_rate_max); + fluid_settings_getint(settings, "synth.midi-channels", &synth->midi_channels); + fluid_settings_getint(settings, "synth.audio-channels", &synth->audio_channels); + fluid_settings_getint(settings, "synth.audio-groups", &synth->audio_groups); + fluid_settings_getint(settings, "synth.effects-channels", &synth->effects_channels); + fluid_settings_getint(settings, "synth.effects-groups", &synth->effects_groups); + fluid_settings_getnum_float(settings, "synth.gain", &synth->gain); + fluid_settings_getint(settings, "synth.device-id", &synth->device_id); + fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores); + + fluid_settings_getnum_float(settings, "synth.overflow.percussion", &synth->overflow.percussion); + fluid_settings_getnum_float(settings, "synth.overflow.released", &synth->overflow.released); + fluid_settings_getnum_float(settings, "synth.overflow.sustained", &synth->overflow.sustained); + fluid_settings_getnum_float(settings, "synth.overflow.volume", &synth->overflow.volume); + fluid_settings_getnum_float(settings, "synth.overflow.age", &synth->overflow.age); + fluid_settings_getnum_float(settings, "synth.overflow.important", &synth->overflow.important); + + /* register the callbacks */ + fluid_settings_callback_num(settings, "synth.gain", + fluid_synth_handle_gain, synth); + fluid_settings_callback_int(settings, "synth.polyphony", + fluid_synth_handle_polyphony, synth); + fluid_settings_callback_int(settings, "synth.device-id", + fluid_synth_handle_device_id, synth); + fluid_settings_callback_num(settings, "synth.overflow.percussion", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.sustained", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.released", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.age", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.volume", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_num(settings, "synth.overflow.important", + fluid_synth_handle_overflow, synth); + fluid_settings_callback_str(settings, "synth.overflow.important-channels", + fluid_synth_handle_important_channels, synth); + fluid_settings_callback_num(settings, "synth.reverb.room-size", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.reverb.damp", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.reverb.width", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.reverb.level", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_int(settings, "synth.reverb.active", + fluid_synth_handle_reverb_chorus_int, synth); + fluid_settings_callback_int(settings, "synth.chorus.active", + fluid_synth_handle_reverb_chorus_int, synth); + fluid_settings_callback_int(settings, "synth.chorus.nr", + fluid_synth_handle_reverb_chorus_int, synth); + fluid_settings_callback_num(settings, "synth.chorus.level", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.chorus.depth", + fluid_synth_handle_reverb_chorus_num, synth); + fluid_settings_callback_num(settings, "synth.chorus.speed", + fluid_synth_handle_reverb_chorus_num, synth); + + /* do some basic sanity checking on the settings */ + + if(synth->midi_channels % 16 != 0) + { + int n = synth->midi_channels / 16; + synth->midi_channels = (n + 1) * 16; + fluid_settings_setint(settings, "synth.midi-channels", synth->midi_channels); + FLUID_LOG(FLUID_WARN, "Requested number of MIDI channels is not a multiple of 16. " + "I'll increase the number of channels to the next multiple."); + } + + if(synth->audio_channels < 1) + { + FLUID_LOG(FLUID_WARN, "Requested number of audio channels is smaller than 1. " + "Changing this setting to 1."); + synth->audio_channels = 1; + } + else if(synth->audio_channels > 128) + { + FLUID_LOG(FLUID_WARN, "Requested number of audio channels is too big (%d). " + "Limiting this setting to 128.", synth->audio_channels); + synth->audio_channels = 128; + } + + if(synth->audio_groups < 1) + { + FLUID_LOG(FLUID_WARN, "Requested number of audio groups is smaller than 1. " + "Changing this setting to 1."); + synth->audio_groups = 1; + } + else if(synth->audio_groups > 128) + { + FLUID_LOG(FLUID_WARN, "Requested number of audio groups is too big (%d). " + "Limiting this setting to 128.", synth->audio_groups); + synth->audio_groups = 128; + } + + if(synth->effects_channels < 2) + { + FLUID_LOG(FLUID_WARN, "Invalid number of effects channels (%d)." + "Setting effects channels to 2.", synth->effects_channels); + synth->effects_channels = 2; + } + + /* + number of buffers rendered by the mixer is determined by synth->audio_groups. + audio from MIDI channel is rendered, mapped and mixed in these buffers. + + Typically synth->audio_channels is only used by audio driver and should be set + to the same value that synth->audio_groups. In some situation using LADSPA, + it is best to diminish audio-channels so that the driver will be able to pass + the audio to audio devices in the case these devices have a limited number of + audio channels. + + audio-channels must not be greater then audio-groups, otherwise these + audio output above audio-groups will not be rendered by the mixeur. + */ + if(synth->audio_channels > synth->audio_groups) + { + synth->audio_channels = synth->audio_groups; + fluid_settings_setint(settings, "synth.audio-channels", synth->audio_channels); + FLUID_LOG(FLUID_WARN, "Requested audio-channels to high. " + "Limiting this setting to audio-groups."); + } + + if(fluid_settings_dupstr(settings, "synth.overflow.important-channels", + &important_channels) == FLUID_OK) + { + if(fluid_synth_set_important_channels(synth, important_channels) != FLUID_OK) + { + FLUID_LOG(FLUID_WARN, "Failed to set overflow important channels"); + } + + FLUID_FREE(important_channels); + } + + /* as soon as the synth is created it starts playing. */ + synth->state = FLUID_SYNTH_PLAYING; + + synth->fromkey_portamento = INVALID_NOTE; /* disable portamento */ + + fluid_atomic_int_set(&synth->ticks_since_start, 0); + synth->tuning = NULL; + fluid_private_init(synth->tuning_iter); + + /* Initialize multi-core variables if multiple cores enabled */ + if(synth->cores > 1) + { + fluid_settings_getint(synth->settings, "audio.realtime-prio", &prio_level); + } + + /* Allocate event queue for rvoice mixer */ + /* In an overflow situation, a new voice takes about 50 spaces in the queue! */ + synth->eventhandler = new_fluid_rvoice_eventhandler(synth->polyphony * 64, + synth->polyphony, synth->audio_groups, + synth->effects_channels, synth->effects_groups, + (fluid_real_t)sample_rate_max, synth->sample_rate, + synth->cores - 1, prio_level); + + if(synth->eventhandler == NULL) + { + goto error_recovery; + } + + /* Setup the list of default modulators. + * Needs to happen after eventhandler has been set up, as fluid_synth_enter_api is called in the process */ + synth->default_mod = NULL; + fluid_synth_add_default_mod(synth, &default_vel2att_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_vel2filter_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_at2viblfo_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_mod2viblfo_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_att_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_pan_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_expr_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_reverb_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_chorus_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &default_pitch_bend_mod, FLUID_SYNTH_ADD); + fluid_synth_add_default_mod(synth, &custom_balance_mod, FLUID_SYNTH_ADD); + + /* Create and initialize the Fx unit.*/ + fluid_settings_getint(settings, "synth.ladspa.active", &with_ladspa); + + if(with_ladspa) + { +#ifdef LADSPA + synth->ladspa_fx = new_fluid_ladspa_fx(synth->sample_rate, + FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE); + + if(synth->ladspa_fx == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + fluid_rvoice_mixer_set_ladspa(synth->eventhandler->mixer, synth->ladspa_fx, + synth->audio_groups); +#else /* LADSPA */ + FLUID_LOG(FLUID_WARN, "FluidSynth has not been compiled with LADSPA support"); +#endif /* LADSPA */ + } + + /* allocate and add the dls sfont loader */ +#ifdef LIBINSTPATCH_SUPPORT + loader = new_fluid_instpatch_loader(settings); + + if(loader == NULL) + { + FLUID_LOG(FLUID_WARN, "Failed to create the instpatch SoundFont loader"); + } + else + { + fluid_synth_add_sfloader(synth, loader); + } +#endif + + /* allocate and add the default sfont loader */ + loader = new_fluid_defsfloader(settings); + + if(loader == NULL) + { + FLUID_LOG(FLUID_WARN, "Failed to create the default SoundFont loader"); + } + else + { + fluid_synth_add_sfloader(synth, loader); + } + + /* allocate all channel objects */ + synth->channel = FLUID_ARRAY(fluid_channel_t *, synth->midi_channels); + + if(synth->channel == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + + FLUID_MEMSET(synth->channel, 0, synth->midi_channels * sizeof(*synth->channel)); + for(i = 0; i < synth->midi_channels; i++) + { + synth->channel[i] = new_fluid_channel(synth, i); + + if(synth->channel[i] == NULL) + { + goto error_recovery; + } + } + + /* allocate all synthesis processes */ + synth->nvoice = synth->polyphony; + synth->voice = FLUID_ARRAY(fluid_voice_t *, synth->nvoice); + + if(synth->voice == NULL) + { + goto error_recovery; + } + + FLUID_MEMSET(synth->voice, 0, synth->nvoice * sizeof(*synth->voice)); + for(i = 0; i < synth->nvoice; i++) + { + synth->voice[i] = new_fluid_voice(synth->eventhandler, synth->sample_rate); + + if(synth->voice[i] == NULL) + { + goto error_recovery; + } + } + + /* sets a default basic channel */ + /* Sets one basic channel: basic channel 0, mode 0 (Omni On - Poly) */ + /* (i.e all channels are polyphonic) */ + /* Must be called after channel objects allocation */ + fluid_synth_set_basic_channel_LOCAL(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, + synth->midi_channels); + + synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth); + + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, + synth->polyphony, 0.0f); + fluid_synth_reverb_on(synth, -1, synth->with_reverb); + fluid_synth_chorus_on(synth, -1, synth->with_chorus); + + synth->cur = FLUID_BUFSIZE; + synth->curmax = 0; + synth->dither_index = 0; + + { + double values[FLUID_REVERB_PARAM_LAST]; + + fluid_settings_getnum(settings, "synth.reverb.room-size", &values[FLUID_REVERB_ROOMSIZE]); + fluid_settings_getnum(settings, "synth.reverb.damp", &values[FLUID_REVERB_DAMP]); + fluid_settings_getnum(settings, "synth.reverb.width", &values[FLUID_REVERB_WIDTH]); + fluid_settings_getnum(settings, "synth.reverb.level", &values[FLUID_REVERB_LEVEL]); + + fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); + } + + { + double values[FLUID_CHORUS_PARAM_LAST]; + + fluid_settings_getint(settings, "synth.chorus.nr", &i); + values[FLUID_CHORUS_NR] = (double)i; + fluid_settings_getnum(settings, "synth.chorus.level", &values[FLUID_CHORUS_LEVEL]); + fluid_settings_getnum(settings, "synth.chorus.speed", &values[FLUID_CHORUS_SPEED]); + fluid_settings_getnum(settings, "synth.chorus.depth", &values[FLUID_CHORUS_DEPTH]); + values[FLUID_CHORUS_TYPE] = (double)FLUID_CHORUS_DEFAULT_TYPE; + + fluid_synth_set_chorus_full(synth, -1, FLUID_CHORUS_SET_ALL, values); + } + + + synth->bank_select = FLUID_BANK_STYLE_GS; + + if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gm")) + { + synth->bank_select = FLUID_BANK_STYLE_GM; + } + else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "gs")) + { + synth->bank_select = FLUID_BANK_STYLE_GS; + } + else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "xg")) + { + synth->bank_select = FLUID_BANK_STYLE_XG; + } + else if(fluid_settings_str_equal(settings, "synth.midi-bank-select", "mma")) + { + synth->bank_select = FLUID_BANK_STYLE_MMA; + } + + fluid_synth_process_event_queue(synth); + + /* FIXME */ + synth->start = fluid_curtime(); + + return synth; + +error_recovery: + delete_fluid_synth(synth); + return NULL; +} + + +/** + * Delete a FluidSynth instance. + * @param synth FluidSynth instance to delete + * + * @note Other users of a synthesizer instance, such as audio and MIDI drivers, + * should be deleted prior to freeing the FluidSynth instance. + */ +void +delete_fluid_synth(fluid_synth_t *synth) +{ + int i, k; + fluid_list_t *list; + fluid_sfont_t *sfont; + fluid_sfloader_t *loader; + + fluid_return_if_fail(synth != NULL); + + fluid_profiling_print(); + + /* unregister all real-time settings callback, to avoid a use-after-free when changing those settings after + * this synth has been deleted*/ + + fluid_settings_callback_num(synth->settings, "synth.gain", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.polyphony", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.device-id", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.percussion", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.sustained", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.released", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.age", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.volume", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.overflow.important", + NULL, NULL); + fluid_settings_callback_str(synth->settings, "synth.overflow.important-channels", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.room-size", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.damp", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.width", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.reverb.level", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.reverb.active", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.chorus.active", + NULL, NULL); + fluid_settings_callback_int(synth->settings, "synth.chorus.nr", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.level", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.depth", + NULL, NULL); + fluid_settings_callback_num(synth->settings, "synth.chorus.speed", + NULL, NULL); + + /* turn off all voices, needed to unload SoundFont data */ + if(synth->voice != NULL) + { + for(i = 0; i < synth->nvoice; i++) + { + fluid_voice_t *voice = synth->voice[i]; + + if(!voice) + { + continue; + } + + /* WARNING: A this point we must ensure that the reference counter + of any soundfont sample owned by any rvoice belonging to the voice + are correctly decremented. This is the contrary part to + to fluid_voice_init() where the sample's reference counter is + incremented. + */ + fluid_voice_unlock_rvoice(voice); + fluid_voice_overflow_rvoice_finished(voice); + + if(fluid_voice_is_playing(voice)) + { + fluid_voice_off(voice); + /* If we only use fluid_voice_off(voice) it will trigger a delayed + * fluid_voice_stop(voice) via fluid_synth_check_finished_voices(). + * But here, we are deleting the fluid_synth_t instance so + * fluid_voice_stop() will be never triggered resulting in + * SoundFont data never unloaded (i.e a serious memory leak). + * So, fluid_voice_stop() must be explicitly called to insure + * unloading SoundFont data + */ + fluid_voice_stop(voice); + } + } + } + + /* also unset all presets for clean SoundFont unload */ + if(synth->channel != NULL) + { + for(i = 0; i < synth->midi_channels; i++) + { + if(synth->channel[i] != NULL) + { + fluid_channel_set_preset(synth->channel[i], NULL); + } + } + } + + delete_fluid_rvoice_eventhandler(synth->eventhandler); + + /* delete all the SoundFonts */ + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + fluid_sfont_delete_internal(sfont); + } + + delete_fluid_list(synth->sfont); + + /* delete all the SoundFont loaders */ + + for(list = synth->loaders; list; list = fluid_list_next(list)) + { + loader = (fluid_sfloader_t *) fluid_list_get(list); + fluid_sfloader_delete(loader); + } + + delete_fluid_list(synth->loaders); + + /* wait for and delete all the lazy sfont unloading timers */ + + for(list = synth->fonts_to_be_unloaded; list; list = fluid_list_next(list)) + { + fluid_timer_t* timer = fluid_list_get(list); + // explicitly join to wait for the unload really to happen + fluid_timer_join(timer); + // delete_fluid_timer alone would stop the timer, even if it had not unloaded the soundfont yet + delete_fluid_timer(timer); + } + + delete_fluid_list(synth->fonts_to_be_unloaded); + + if(synth->channel != NULL) + { + for(i = 0; i < synth->midi_channels; i++) + { + delete_fluid_channel(synth->channel[i]); + } + + FLUID_FREE(synth->channel); + } + + if(synth->voice != NULL) + { + for(i = 0; i < synth->nvoice; i++) + { + delete_fluid_voice(synth->voice[i]); + } + + FLUID_FREE(synth->voice); + } + + + /* free the tunings, if any */ + if(synth->tuning != NULL) + { + for(i = 0; i < 128; i++) + { + if(synth->tuning[i] != NULL) + { + for(k = 0; k < 128; k++) + { + delete_fluid_tuning(synth->tuning[i][k]); + } + + FLUID_FREE(synth->tuning[i]); + } + } + + FLUID_FREE(synth->tuning); + } + + fluid_private_free(synth->tuning_iter); + +#ifdef LADSPA + /* Release the LADSPA effects unit */ + delete_fluid_ladspa_fx(synth->ladspa_fx); +#endif + + /* delete all default modulators */ + delete_fluid_list_mod(synth->default_mod); + + FLUID_FREE(synth->overflow.important_channels); + + fluid_rec_mutex_destroy(synth->mutex); + + FLUID_FREE(synth); + +#if defined(LIBINSTPATCH_SUPPORT) + if(fluid_instpatch_supports_multi_init()) + { + fluid_instpatch_deinit(); + } +#endif +} + +/** + * Get a textual representation of the last error + * @param synth FluidSynth instance + * @return Pointer to string of last error message. Valid until the same + * calling thread calls another FluidSynth function which fails. String is + * internal and should not be modified or freed. + * @deprecated This function is not thread-safe and does not work with multiple synths. + * It has been deprecated. It may return "" in a future release and will eventually be removed. + */ +const char * +fluid_synth_error(fluid_synth_t *synth) +{ + return ""; +} + +/** + * Send a note-on event to a FluidSynth object. + * + * This function will take care of proper legato playing. If a note on channel @p chan is + * already playing at the given key @p key, it will be released (even if it is sustained). + * In other words, overlapping notes are not allowed. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number (0-127) + * @param vel MIDI velocity (0-127, 0=noteoff) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_noteon(fluid_synth_t *synth, int chan, int key, int vel) +{ + int result; + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail(vel >= 0 && vel <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + result = fluid_synth_noteon_LOCAL(synth, chan, key, vel); + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of fluid_synth_noteon */ +static int +fluid_synth_noteon_LOCAL(fluid_synth_t *synth, int chan, int key, int vel) +{ + fluid_channel_t *channel ; + + /* notes with velocity zero go to noteoff */ + if(vel == 0) + { + return fluid_synth_noteoff_LOCAL(synth, chan, key); + } + + channel = synth->channel[chan]; + + /* makes sure this channel has a preset */ + if(channel->preset == NULL) + { + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d\t%s", + chan, key, vel, 0, + fluid_synth_get_ticks(synth) / 44100.0f, + (fluid_curtime() - synth->start) / 1000.0f, + 0.0f, 0, "channel has no preset"); + } + + return FLUID_FAILED; + } + + if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */ + { + /* play the noteOn in monophonic */ + return fluid_synth_noteon_mono_LOCAL(synth, chan, key, vel); + } + else + { + /* channel is poly and legato CC is Off) */ + + /* plays the noteOn in polyphonic */ + /* Sets the note at first position in monophonic list */ + /* In the case where the musician intends to inter the channel in monophonic + (by depressing the CC legato on), the next noteOn mono could be played legato + with the previous note poly (if the musician choose this). + */ + fluid_channel_set_onenote_monolist(channel, (unsigned char) key, + (unsigned char) vel); + + /* If there is another voice process on the same channel and key, + advance it to the release phase. */ + fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, key); + + /* a noteon poly is passed to fluid_synth_noteon_monopoly_legato(). + This allows an opportunity to get this note played legato with a previous + note if a CC PTC have been received before this noteon. This behavior is + a MIDI specification (see FluidPolymono-0004.pdf chapter 4.3-a ,3.4.11 + for details). + */ + return fluid_synth_noteon_monopoly_legato(synth, chan, INVALID_NOTE, key, vel); + } +} + +/** + * Sends a note-off event to a FluidSynth object. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise (may just mean that no + * voices matched the note off event) + */ +int +fluid_synth_noteoff(fluid_synth_t *synth, int chan, int key) +{ + int result; + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + result = fluid_synth_noteoff_LOCAL(synth, chan, key); + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of fluid_synth_noteoff */ +static int +fluid_synth_noteoff_LOCAL(fluid_synth_t *synth, int chan, int key) +{ + int status; + fluid_channel_t *channel = synth->channel[chan]; + + if(fluid_channel_is_playing_mono(channel)) /* channel is mono or legato CC is On) */ + { + /* play the noteOff in monophonic */ + status = fluid_synth_noteoff_mono_LOCAL(synth, chan, key); + } + else + { + /* channel is poly and legato CC is Off) */ + /* removes the note from the monophonic list */ + if(channel->n_notes && key == fluid_channel_last_note(channel)) + { + fluid_channel_clear_monolist(channel); + } + + status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); + } + + /* Changes the state (Valid/Invalid) of the most recent note played in a + staccato manner */ + fluid_channel_invalid_prev_note_staccato(channel); + return status; +} + +/* Damps voices on a channel (turn notes off), if they're sustained by + sustain pedal */ +static int +fluid_synth_damp_voices_by_sustain_LOCAL(fluid_synth_t *synth, int chan) +{ + fluid_channel_t *channel = synth->channel[chan]; + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sustained(voice)) + { + if(voice->key == channel->key_mono_sustained) + { + /* key_mono_sustained is a possible mono note sustainted + (by sustain or sostenuto pedal). It must be marked released + (INVALID_NOTE) here because it is released only by sustain pedal */ + channel->key_mono_sustained = INVALID_NOTE; + } + + fluid_voice_release(voice); + } + } + + return FLUID_OK; +} + +/* Damps voices on a channel (turn notes off), if they're sustained by + sostenuto pedal */ +static int +fluid_synth_damp_voices_by_sostenuto_LOCAL(fluid_synth_t *synth, int chan) +{ + fluid_channel_t *channel = synth->channel[chan]; + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if((fluid_voice_get_channel(voice) == chan) && fluid_voice_is_sostenuto(voice)) + { + if(voice->key == channel->key_mono_sustained) + { + /* key_mono_sustained is a possible mono note sustainted + (by sustain or sostenuto pedal). It must be marked released + (INVALID_NOTE) here because it is released only by sostenuto pedal */ + channel->key_mono_sustained = INVALID_NOTE; + } + + fluid_voice_release(voice); + } + } + + return FLUID_OK; +} + +/** + * Adds the specified modulator \c mod as default modulator to the synth. \c mod will + * take effect for any subsequently created voice. + * @param synth FluidSynth instance + * @param mod Modulator info (values copied, passed in object can be freed immediately afterwards) + * @param mode Determines how to handle an existing identical modulator (#fluid_synth_add_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note Not realtime safe (due to internal memory allocation) and therefore should not be called + * from synthesis context at the risk of stalling audio output. + */ +int +fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mode) +{ + fluid_mod_t *default_mod; + fluid_mod_t *last_mod = NULL; + fluid_mod_t *new_mod; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(mod != NULL, FLUID_FAILED); + fluid_return_val_if_fail((mode == FLUID_SYNTH_ADD) || (mode == FLUID_SYNTH_OVERWRITE) , FLUID_FAILED); + + /* Checks if modulators sources are valid */ + if(!fluid_mod_check_sources(mod, "api fluid_synth_add_default_mod mod")) + { + return FLUID_FAILED; + } + + fluid_synth_api_enter(synth); + + default_mod = synth->default_mod; + + while(default_mod != NULL) + { + if(fluid_mod_test_identity(default_mod, mod)) + { + if(mode == FLUID_SYNTH_ADD) + { + default_mod->amount += mod->amount; + } + else // mode == FLUID_SYNTH_OVERWRITE + { + default_mod->amount = mod->amount; + } + + FLUID_API_RETURN(FLUID_OK); + } + + last_mod = default_mod; + default_mod = default_mod->next; + } + + /* Add a new modulator (no existing modulator to add / overwrite). */ + new_mod = new_fluid_mod(); + + if(new_mod == NULL) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + fluid_mod_clone(new_mod, mod); + new_mod->next = NULL; + + if(last_mod == NULL) + { + synth->default_mod = new_mod; + } + else + { + last_mod->next = new_mod; + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Removes the specified modulator \c mod from the synth's default modulator list. + * fluid_mod_test_identity() will be used to test modulator matching. + * @param synth synth instance + * @param mod The modulator to remove + * @return #FLUID_OK if a matching modulator was found and successfully removed, #FLUID_FAILED otherwise + * + * @note Not realtime safe (due to internal memory freeing) and therefore should not be called + * from synthesis context at the risk of stalling audio output. + */ +int +fluid_synth_remove_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod) +{ + fluid_mod_t *default_mod; + fluid_mod_t *last_mod; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(mod != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + last_mod = default_mod = synth->default_mod; + + while(default_mod != NULL) + { + if(fluid_mod_test_identity(default_mod, mod)) + { + if(synth->default_mod == default_mod) + { + synth->default_mod = default_mod->next; + } + else + { + last_mod->next = default_mod->next; + } + + delete_fluid_mod(default_mod); + FLUID_API_RETURN(FLUID_OK); + } + + last_mod = default_mod; + default_mod = default_mod->next; + } + + FLUID_API_RETURN(FLUID_FAILED); +} + + +/** + * Send a MIDI controller event on a MIDI channel. + * + * Most CCs are 7-bits wide in FluidSynth. There are a few exceptions which may be 14-bits wide as are documented here: + * https://github.com/FluidSynth/fluidsynth/wiki/FluidFeatures#midi-control-change-implementation-chart + * + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param num MIDI controller number (0-127) + * @param val MIDI controller value (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @note This function supports MIDI Global Controllers which will be sent to + * all channels of the basic channel if this basic channel is in mode OmniOff/Mono. + * This is accomplished by sending the CC one MIDI channel below the basic + * channel of the receiver. + * Examples: let a synthesizer with 16 MIDI channels: + * - Let a basic channel 7 in mode 3 (Omni Off, Mono). If MIDI channel 6 is disabled it + * could be used as CC global for all channels belonging to basic channel 7. + * - Let a basic channel 0 in mode 3. If MIDI channel 15 is disabled it could be used + * as CC global for all channels belonging to basic channel 0. + * @warning Contrary to the MIDI Standard, this function does not clear LSB controllers, + * when MSB controllers are received. + */ +int +fluid_synth_cc(fluid_synth_t *synth, int chan, int num, int val) +{ + int result = FLUID_FAILED; + fluid_channel_t *channel; + fluid_return_val_if_fail(num >= 0 && num <= 127, FLUID_FAILED); + fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + channel = synth->channel[chan]; + + if(channel->mode & FLUID_CHANNEL_ENABLED) + { + /* chan is enabled */ + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", chan, num, val); + } + + fluid_channel_set_cc(channel, num, val); + result = fluid_synth_cc_LOCAL(synth, chan, num); + } + else /* chan is disabled so it is a candidate for global channel */ + { + /* looks for next basic channel */ + int n_chan = synth->midi_channels; /* MIDI Channels number */ + int basicchan ; + + if(chan < n_chan - 1) + { + basicchan = chan + 1; /* next channel */ + } + else + { + basicchan = 0; /* wrap to 0 */ + } + + channel = synth->channel[basicchan]; + + /* Channel must be a basicchan in mode OMNIOFF_MONO */ + if((channel->mode & FLUID_CHANNEL_BASIC) && + ((channel->mode & FLUID_CHANNEL_MODE_MASK) == FLUID_CHANNEL_MODE_OMNIOFF_MONO)) + { + /* sends cc to all channels in this basic channel */ + int i, nbr = channel->mode_val; + + for(i = basicchan; i < basicchan + nbr; i++) + { + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "cc\t%d\t%d\t%d", i, num, val); + } + + fluid_channel_set_cc(synth->channel[i], num, val); + result = fluid_synth_cc_LOCAL(synth, i, num); + } + } + /* The channel chan is not a valid 'global channel' */ + else + { + result = FLUID_FAILED; + } + } + + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of MIDI CC set function. + Most of CC are allowed to modulate but not all. A comment describes if CC num + isn't allowed to modulate. + Following explanations should help to understand both MIDI specifications and + Soundfont specifications in regard to MIDI specs. + + MIDI specs: + CC LSB (32 to 63) are LSB contributions to CC MSB (0 to 31). + It's up to the synthesizer to decide to take LSB values into account or not. + Actually Fluidsynth doesn't use CC LSB value inside fluid_voice_update_param() + (once fluid_voice_modulate() has been triggered). This is because actually + fluidsynth needs only 7 bits resolution (and not 14 bits) from these CCs. + So fluidsynth is using only 7 bit MSB (except for portamento time). + In regard to MIDI specs Fluidsynth behaves correctly. + + Soundfont specs 2.01 - 8.2.1: + To deal correctly with MIDI CC (regardless if any synth will use CC MSB alone (7 bit) + or both CCs MSB,LSB (14 bits) during synthesis), SF specs recommend not making use of + CC LSB (i.e only CC MSB) in modulator sources to trigger modulation (i.e modulators + with CC LSB connected to sources inputs should be ignored). + These specifics are particularly suited for synths that use 14 bits CCs. In this case, + the MIDI transmitter sends CC LSB first followed by CC MSB. The MIDI synth receives + both CC LSB and CC MSB but only CC MSB will trigger the modulation. + This will produce correct synthesis parameters update from a correct 14 bits CC. + If in SF specs, modulator sources with CC LSB had been accepted, both CC LSB and + CC MSB will triggers 2 modulations. This leads to incorrect synthesis parameters + update followed by correct synthesis parameters update. + + However, as long as fluidsynth will use only CC 7 bits resolution, it is safe to ignore + these SF recommendations on CC receive. +*/ +static int +fluid_synth_cc_LOCAL(fluid_synth_t *synth, int channum, int num) +{ + fluid_channel_t *chan = synth->channel[channum]; + int nrpn_select; + int value; + + value = fluid_channel_get_cc(chan, num); + + switch(num) + { + case LOCAL_CONTROL: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + break; + + /* CC omnioff, omnion, mono, poly */ + /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + case POLY_OFF: + case POLY_ON: + case OMNI_OFF: + case OMNI_ON: + + /* allowed only if channum is a basic channel */ + if(chan->mode & FLUID_CHANNEL_BASIC) + { + /* Construction of new_mode from current channel mode and this CC mode */ + int new_mode = chan->mode & FLUID_CHANNEL_MODE_MASK; + + switch(num) + { + case POLY_OFF: + new_mode |= FLUID_CHANNEL_POLY_OFF; + break; + + case POLY_ON: + new_mode &= ~FLUID_CHANNEL_POLY_OFF; + break; + + case OMNI_OFF: + new_mode |= FLUID_CHANNEL_OMNI_OFF; + break; + + case OMNI_ON: + new_mode &= ~FLUID_CHANNEL_OMNI_OFF; + break; + + default: /* should never happen */ + return FLUID_FAILED; + } + + /* MIDI specs: if value is 0 it means all channels from channum to next + basic channel minus 1 (if any) or to MIDI channel count minus 1. + However, if value is > 0 (e.g. 4), the group of channels will be be + limited to 4. + value is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY as this mode + implies a group of only one channel. + */ + /* Checks value range and changes this existing basic channel group */ + value = fluid_synth_check_next_basic_channel(synth, channum, new_mode, value); + + if(value != FLUID_FAILED) + { + /* reset the current basic channel before changing it */ + fluid_synth_reset_basic_channel_LOCAL(synth, channum, chan->mode_val); + fluid_synth_set_basic_channel_LOCAL(synth, channum, new_mode, value); + break; /* FLUID_OK */ + } + } + + return FLUID_FAILED; + + case LEGATO_SWITCH: /* not allowed to modulate */ + /* handles Poly/mono commutation on Legato pedal On/Off.*/ + fluid_channel_cc_legato(chan, value); + break; + + case PORTAMENTO_SWITCH: /* not allowed to modulate */ + /* Special handling of the monophonic list */ + /* Invalids the most recent note played in a staccato manner */ + fluid_channel_invalid_prev_note_staccato(chan); + break; + + case SUSTAIN_SWITCH: /* not allowed to modulate */ + + /* Release voices if Sustain switch is released */ + if(value < 64) /* Sustain is released */ + { + fluid_synth_damp_voices_by_sustain_LOCAL(synth, channum); + } + + break; + + case SOSTENUTO_SWITCH: /* not allowed to modulate */ + + /* Release voices if Sostetuno switch is released */ + if(value < 64) /* Sostenuto is released */ + { + fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum); + } + else /* Sostenuto is depressed */ + /* Update sostenuto order id when pedaling on Sostenuto */ + { + chan->sostenuto_orderid = synth->noteid; /* future voice id value */ + } + + break; + + case BANK_SELECT_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_channel_set_bank_msb(chan, value & 0x7F); + break; + + case BANK_SELECT_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_channel_set_bank_lsb(chan, value & 0x7F); + break; + + case ALL_NOTES_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_synth_all_notes_off_LOCAL(synth, channum); + break; + + case ALL_SOUND_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_synth_all_sounds_off_LOCAL(synth, channum); + break; + + case ALL_CTRL_OFF: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_channel_init_ctrl(chan, 1); + // the hold pedals have been reset, we maybe need to release voices + fluid_synth_damp_voices_by_sustain_LOCAL(synth, channum); + fluid_synth_damp_voices_by_sostenuto_LOCAL(synth, channum); + fluid_synth_modulate_voices_all_LOCAL(synth, channum); + break; + + case DATA_ENTRY_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + break; + + case DATA_ENTRY_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + { + int data = (value << 7) + fluid_channel_get_cc(chan, DATA_ENTRY_LSB); + + if(chan->nrpn_active) /* NRPN is active? */ + { + /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ + if((fluid_channel_get_cc(chan, NRPN_MSB) == 120) + && (fluid_channel_get_cc(chan, NRPN_LSB) < 100)) + { + nrpn_select = chan->nrpn_select; + + if(nrpn_select < GEN_LAST) + { + float val = fluid_gen_scale_nrpn(nrpn_select, data); + fluid_synth_set_gen_LOCAL(synth, channum, nrpn_select, val); + } + + chan->nrpn_select = 0; /* Reset to 0 */ + } + } + else if(fluid_channel_get_cc(chan, RPN_MSB) == 0) /* RPN is active: MSB = 0? */ + { + switch(fluid_channel_get_cc(chan, RPN_LSB)) + { + case RPN_PITCH_BEND_RANGE: /* Set bend range in semitones */ + fluid_channel_set_pitch_wheel_sensitivity(synth->channel[channum], value); + fluid_synth_update_pitch_wheel_sens_LOCAL(synth, channum); /* Update bend range */ + /* FIXME - Handle LSB? (Fine bend range in cents) */ + break; + + case RPN_CHANNEL_FINE_TUNE: /* Fine tune is 14 bit over +/-1 semitone (+/- 100 cents, 8192 = center) */ + fluid_synth_set_gen_LOCAL(synth, channum, GEN_FINETUNE, + (float)(data - 8192) * (100.0f / 8192.0f)); + break; + + case RPN_CHANNEL_COARSE_TUNE: /* Coarse tune is 7 bit and in semitones (64 is center) */ + fluid_synth_set_gen_LOCAL(synth, channum, GEN_COARSETUNE, + value - 64); + break; + + case RPN_TUNING_PROGRAM_CHANGE: + fluid_channel_set_tuning_prog(chan, value); + fluid_synth_activate_tuning(synth, channum, + fluid_channel_get_tuning_bank(chan), + value, TRUE); + break; + + case RPN_TUNING_BANK_SELECT: + fluid_channel_set_tuning_bank(chan, value); + break; + + case RPN_MODULATION_DEPTH_RANGE: + break; + } + } + + break; + } + + case NRPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + fluid_channel_set_cc(chan, NRPN_LSB, 0); + chan->nrpn_select = 0; + chan->nrpn_active = 1; + break; + + case NRPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + + /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74) */ + if(fluid_channel_get_cc(chan, NRPN_MSB) == 120) + { + if(value == 100) + { + chan->nrpn_select += 100; + } + else if(value == 101) + { + chan->nrpn_select += 1000; + } + else if(value == 102) + { + chan->nrpn_select += 10000; + } + else if(value < 100) + { + chan->nrpn_select += value; + } + } + + chan->nrpn_active = 1; + break; + + case RPN_MSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + case RPN_LSB: /* not allowed to modulate (spec SF 2.01 - 8.2.1) */ + chan->nrpn_active = 0; + break; + + case BREATH_MSB: + /* handles CC Breath On/Off noteOn/noteOff mode */ + fluid_channel_cc_breath_note_on_off(chan, value); + + /* fall-through */ + default: + /* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1) */ + /* However, as long fluidsynth will use only CC 7 bits resolution, it + is safe to ignore these SF recommendations on CC receive. See + explanations above */ + /* if (! (32 <= num && num <= 63)) */ + { + return fluid_synth_modulate_voices_LOCAL(synth, channum, 1, num); + } + } + + return FLUID_OK; +} + +/** + * Get current MIDI controller value on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param num MIDI controller number (0-127) + * @param pval Location to store MIDI controller value (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_cc(fluid_synth_t *synth, int chan, int num, int *pval) +{ + fluid_return_val_if_fail(num >= 0 && num < 128, FLUID_FAILED); + fluid_return_val_if_fail(pval != NULL, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + *pval = fluid_channel_get_cc(synth->channel[chan], num); + FLUID_API_RETURN(FLUID_OK); +} + +/* + * Handler for synth.device-id setting. + */ +static void +fluid_synth_handle_device_id(void *data, const char *name, int value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + fluid_synth_api_enter(synth); + synth->device_id = value; + fluid_synth_api_exit(synth); +} + +/** + * Process a MIDI SYSEX (system exclusive) message. + * @param synth FluidSynth instance + * @param data Buffer containing SYSEX data (not including 0xF0 and 0xF7) + * @param len Length of data in buffer + * @param response Buffer to store response to or NULL to ignore + * @param response_len IN/OUT parameter, in: size of response buffer, out: + * amount of data written to response buffer (if FLUID_FAILED is returned and + * this value is non-zero, it indicates the response buffer is too small) + * @param handled Optional location to store boolean value if message was + * recognized and handled or not (set to TRUE if it was handled) + * @param dryrun TRUE to just do a dry run but not actually execute the SYSEX + * command (useful for checking if a SYSEX message would be handled) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + */ +/* SYSEX format (0xF0 and 0xF7 not passed to this function): + * Non-realtime: 0xF0 0x7E [BODY] 0xF7 + * Realtime: 0xF0 0x7F [BODY] 0xF7 + * Tuning messages: 0xF0 0x7E/0x7F 0x08 [BODY] 0xF7 + * GS DT1 messages: 0xF0 0x41 0x42 0x12 [ADDRESS (3 bytes)] [DATA] 0xF7 + */ +int +fluid_synth_sysex(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int *handled, int dryrun) +{ + int avail_response = 0; + + if(handled) + { + *handled = FALSE; + } + + if(response_len) + { + avail_response = *response_len; + *response_len = 0; + } + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(data != NULL, FLUID_FAILED); + fluid_return_val_if_fail(len > 0, FLUID_FAILED); + fluid_return_val_if_fail(!response || response_len, FLUID_FAILED); + + if(len < 4) + { + return FLUID_OK; + } + + /* MIDI tuning SYSEX message? */ + if((data[0] == MIDI_SYSEX_UNIV_NON_REALTIME || data[0] == MIDI_SYSEX_UNIV_REALTIME) + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_MIDI_TUNING_ID) + { + int result; + fluid_synth_api_enter(synth); + result = fluid_synth_sysex_midi_tuning(synth, data, len, response, + response_len, avail_response, + handled, dryrun); + + FLUID_API_RETURN(result); + } + + /* GM or GM2 system on */ + if(data[0] == MIDI_SYSEX_UNIV_NON_REALTIME + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_GM_ID) + { + if(handled) + { + *handled = TRUE; + } + if(!dryrun && (data[3] == MIDI_SYSEX_GM_ON + || data[3] == MIDI_SYSEX_GM2_ON)) + { + int result; + synth->bank_select = FLUID_BANK_STYLE_GM; + fluid_synth_api_enter(synth); + result = fluid_synth_system_reset_LOCAL(synth); + FLUID_API_RETURN(result); + } + return FLUID_OK; + } + + /* GS DT1 message */ + if(data[0] == MIDI_SYSEX_MANUF_ROLAND + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_GS_ID + && data[3] == MIDI_SYSEX_GS_DT1) + { + int result; + fluid_synth_api_enter(synth); + result = fluid_synth_sysex_gs_dt1(synth, data, len, response, + response_len, avail_response, + handled, dryrun); + FLUID_API_RETURN(result); + } + + /* XG message */ + if(data[0] == MIDI_SYSEX_MANUF_YAMAHA + && (data[1] == synth->device_id || data[1] == MIDI_SYSEX_DEVICE_ID_ALL) + && data[2] == MIDI_SYSEX_XG_ID) + { + int result; + fluid_synth_api_enter(synth); + result = fluid_synth_sysex_xg(synth, data, len, response, + response_len, avail_response, + handled, dryrun); + FLUID_API_RETURN(result); + } + + return FLUID_OK; +} + +/* Handler for MIDI tuning SYSEX messages */ +static int +fluid_synth_sysex_midi_tuning(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int avail_response, + int *handled, int dryrun) +{ + int realtime, msgid; + int bank = 0, prog, channels; + double tunedata[128]; + int keys[128]; + char name[17]={0}; + int note, frac, frac2; + uint8_t chksum; + int i, count, index; + const char *dataptr; + char *resptr;; + + realtime = data[0] == MIDI_SYSEX_UNIV_REALTIME; + msgid = data[3]; + + switch(msgid) + { + case MIDI_SYSEX_TUNING_BULK_DUMP_REQ: + case MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK: + if(data[3] == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) + { + if(len != 5 || data[4] & 0x80 || !response) + { + return FLUID_OK; + } + + *response_len = 406; + prog = data[4]; + } + else + { + if(len != 6 || data[4] & 0x80 || data[5] & 0x80 || !response) + { + return FLUID_OK; + } + + *response_len = 407; + bank = data[4]; + prog = data[5]; + } + + if(dryrun) + { + if(handled) + { + *handled = TRUE; + } + + return FLUID_OK; + } + + if(avail_response < *response_len) + { + return FLUID_FAILED; + } + + /* Get tuning data, return if tuning not found */ + if(fluid_synth_tuning_dump(synth, bank, prog, name, 17, tunedata) == FLUID_FAILED) + { + *response_len = 0; + return FLUID_OK; + } + + resptr = response; + + *resptr++ = MIDI_SYSEX_UNIV_NON_REALTIME; + *resptr++ = synth->device_id; + *resptr++ = MIDI_SYSEX_MIDI_TUNING_ID; + *resptr++ = MIDI_SYSEX_TUNING_BULK_DUMP; + + if(msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK) + { + *resptr++ = bank; + } + + *resptr++ = prog; + /* copy 16 ASCII characters (potentially not null terminated) to the sysex buffer */ + FLUID_MEMCPY(resptr, name, 16); + resptr += 16; + + for(i = 0; i < 128; i++) + { + note = tunedata[i] / 100.0; + fluid_clip(note, 0, 127); + + frac = ((tunedata[i] - note * 100.0) * 16384.0 + 50.0) / 100.0; + fluid_clip(frac, 0, 16383); + + *resptr++ = note; + *resptr++ = frac >> 7; + *resptr++ = frac & 0x7F; + } + + if(msgid == MIDI_SYSEX_TUNING_BULK_DUMP_REQ) + { + /* NOTE: Checksum is not as straight forward as the bank based messages */ + chksum = MIDI_SYSEX_UNIV_NON_REALTIME ^ MIDI_SYSEX_MIDI_TUNING_ID + ^ MIDI_SYSEX_TUNING_BULK_DUMP ^ prog; + + for(i = 21; i < 128 * 3 + 21; i++) + { + chksum ^= response[i]; + } + } + else + { + for(i = 1, chksum = 0; i < 406; i++) + { + chksum ^= response[i]; + } + } + + *resptr++ = chksum & 0x7F; + + if(handled) + { + *handled = TRUE; + } + + break; + + case MIDI_SYSEX_TUNING_NOTE_TUNE: + case MIDI_SYSEX_TUNING_NOTE_TUNE_BANK: + dataptr = data + 4; + + if(msgid == MIDI_SYSEX_TUNING_NOTE_TUNE) + { + if(len < 10 || data[4] & 0x80 || data[5] & 0x80 || len != data[5] * 4 + 6) + { + return FLUID_OK; + } + } + else + { + if(len < 11 || data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80 + || len != data[6] * 4 + 7) + { + return FLUID_OK; + } + + bank = *dataptr++; + } + + if(dryrun) + { + if(handled) + { + *handled = TRUE; + } + + return FLUID_OK; + } + + prog = *dataptr++; + count = *dataptr++; + + for(i = 0, index = 0; i < count; i++) + { + note = *dataptr++; + + if(note & 0x80) + { + return FLUID_OK; + } + + keys[index] = note; + + note = *dataptr++; + frac = *dataptr++; + frac2 = *dataptr++; + + if(note & 0x80 || frac & 0x80 || frac2 & 0x80) + { + return FLUID_OK; + } + + frac = frac << 7 | frac2; + + /* No change pitch value? Doesn't really make sense to send that, but.. */ + if(note == 0x7F && frac == 16383) + { + continue; + } + + tunedata[index] = note * 100.0 + (frac * 100.0 / 16384.0); + index++; + } + + if(index > 0) + { + if(fluid_synth_tune_notes(synth, bank, prog, index, keys, tunedata, + realtime) == FLUID_FAILED) + { + return FLUID_FAILED; + } + } + + if(handled) + { + *handled = TRUE; + } + + break; + + case MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE: + case MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE: + if((msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE && len != 19) + || (msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE && len != 31)) + { + return FLUID_OK; + } + + if(data[4] & 0x80 || data[5] & 0x80 || data[6] & 0x80) + { + return FLUID_OK; + } + + if(dryrun) + { + if(handled) + { + *handled = TRUE; + } + + return FLUID_OK; + } + + channels = (data[4] & 0x03) << 14 | data[5] << 7 | data[6]; + + if(msgid == MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE) + { + for(i = 0; i < 12; i++) + { + frac = data[i + 7]; + + if(frac & 0x80) + { + return FLUID_OK; + } + + tunedata[i] = (int)frac - 64; + } + } + else + { + for(i = 0; i < 12; i++) + { + frac = data[i * 2 + 7]; + frac2 = data[i * 2 + 8]; + + if(frac & 0x80 || frac2 & 0x80) + { + return FLUID_OK; + } + + tunedata[i] = (((int)frac << 7 | (int)frac2) - 8192) * (200.0 / 16384.0); + } + } + + if(fluid_synth_activate_octave_tuning(synth, 0, 0, "SYSEX", + tunedata, realtime) == FLUID_FAILED) + { + return FLUID_FAILED; + } + + if(channels) + { + for(i = 0; i < 16; i++) + { + if(channels & (1 << i)) + { + fluid_synth_activate_tuning(synth, i, 0, 0, realtime); + } + } + } + + if(handled) + { + *handled = TRUE; + } + + break; + } + + return FLUID_OK; +} + +/* Handler for GS DT1 messages */ +static int +fluid_synth_sysex_gs_dt1(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int avail_response, + int *handled, int dryrun) +{ + int addr; + int len_data; + int checksum = 0, i; + + if(len < 9) // at least one byte of data should be transmitted + { + FLUID_LOG(FLUID_INFO, "SysEx DT1: message too short, dropping it."); + return FLUID_FAILED; + } + len_data = len - 8; + addr = (data[4] << 16) | (data[5] << 8) | data[6]; + + for (i = 4; i < len - 1; ++i) + { + checksum += data[i]; + } + checksum = 0x80 - (checksum & 0x7F); + if (checksum != data[len - 1]) + { + FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping message on addr 0x%x due to incorrect checksum 0x%x. Correct checksum: 0x%x", addr, (int)data[len - 1], checksum); + return FLUID_FAILED; + } + + if (addr == 0x40007F) // Mode set + { + if (len_data > 1 || (data[7] != 0 && data[7] != 0x7f)) + { + FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping invalid mode set message"); + return FLUID_FAILED; + } + if (handled) + { + *handled = TRUE; + } + if (!dryrun) + { + if (data[7] == 0) + { + synth->bank_select = FLUID_BANK_STYLE_GS; + } + else + { + synth->bank_select = FLUID_BANK_STYLE_GM; + } + return fluid_synth_system_reset_LOCAL(synth); + } + return FLUID_OK; + } + + if (synth->bank_select != FLUID_BANK_STYLE_GS) + { + return FLUID_OK; // Silently ignore all other messages + } + + if ((addr & 0xFFF0FF) == 0x401015) // Use for rhythm part + { + if (len_data > 1 || data[7] > 0x02) + { + FLUID_LOG(FLUID_INFO, "SysEx DT1: dropping invalid rhythm part message"); + return FLUID_FAILED; + } + if (handled) + { + *handled = TRUE; + } + if (!dryrun) + { + int chan = (addr >> 8) & 0x0F; + //See the Patch Part parameters section in SC-88Pro/8850 owner's manual + chan = chan >= 0x0a ? chan : (chan == 0 ? 9 : chan - 1); + synth->channel[chan]->channel_type = + data[7] == 0x00 ? CHANNEL_TYPE_MELODIC : CHANNEL_TYPE_DRUM; + + FLUID_LOG(FLUID_DBG, "SysEx DT1: setting MIDI channel %d to type %d", chan, (int)synth->channel[chan]->channel_type); + //Roland synths seem to "remember" the last instrument a channel + //used in the selected mode. This behavior is not replicated here. + fluid_synth_program_change(synth, chan, 0); + } + return FLUID_OK; + } + + //silently ignore + return FLUID_OK; +} + +/* Handler for XG messages */ +static int +fluid_synth_sysex_xg(fluid_synth_t *synth, const char *data, int len, + char *response, int *response_len, int avail_response, + int *handled, int dryrun) +{ + int addr; + int len_data; + + if(len < 7) // at least one byte of data should be transmitted + { + return FLUID_FAILED; + } + len_data = len - 6; + addr = (data[3] << 16) | (data[4] << 8) | data[5]; + + if (addr == 0x00007E // Reset + || addr == 0x00007F) // Reset to factory + { + if (len_data > 1 || data[6] != 0) + { + return FLUID_FAILED; + } + if (handled) + { + *handled = TRUE; + } + if (!dryrun) + { + synth->bank_select = FLUID_BANK_STYLE_XG; + return fluid_synth_system_reset_LOCAL(synth); + } + return FLUID_OK; + } + + /* No other messages handled yet + if (synth->bank_select != FLUID_BANK_STYLE_XG) + { + return FLUID_OK; // Silently ignore all other messages + }*/ + + //silently ignore + return FLUID_OK; +} + +/** + * Turn off all voices that are playing on the given MIDI channel, by putting them into release phase. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.4 + */ +int +fluid_synth_all_notes_off(fluid_synth_t *synth, int chan) +{ + int result; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(chan >= -1, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(chan >= synth->midi_channels) + { + result = FLUID_FAILED; + } + else + { + /* Allowed (even for channel disabled) as chan = -1 selects all channels */ + result = fluid_synth_all_notes_off_LOCAL(synth, chan); + } + + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of all notes off, (chan=-1 selects all channels) */ +//static int +int +fluid_synth_all_notes_off_LOCAL(fluid_synth_t *synth, int chan) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice)))) + { + fluid_voice_noteoff(voice); + } + } + + return FLUID_OK; +} + +/** + * Immediately stop all voices on the given MIDI channel (skips release phase). + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1), (chan=-1 selects all channels) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.4 + */ +int +fluid_synth_all_sounds_off(fluid_synth_t *synth, int chan) +{ + int result; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(chan >= -1, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(chan >= synth->midi_channels) + { + result = FLUID_FAILED; + } + else + { + /* Allowed (even for channel disabled) as chan = -1 selects all channels */ + result = fluid_synth_all_sounds_off_LOCAL(synth, chan); + } + + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of all sounds off, (chan=-1 selects all channels) */ +static int +fluid_synth_all_sounds_off_LOCAL(fluid_synth_t *synth, int chan) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice) && ((-1 == chan) || (chan == fluid_voice_get_channel(voice)))) + { + fluid_voice_off(voice); + } + } + + return FLUID_OK; +} + +/** + * Reset reverb engine + * @param synth FluidSynth instance + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_reset_reverb(fluid_synth_t *synth) +{ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f); + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Reset chorus engine + * @param synth FluidSynth instance + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_reset_chorus(fluid_synth_t *synth) +{ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f); + FLUID_API_RETURN(FLUID_OK); +} + + +/** + * Send MIDI system reset command (big red 'panic' button), turns off notes, resets + * controllers and restores initial basic channel configuration. + * @param synth FluidSynth instance + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_system_reset(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + result = fluid_synth_system_reset_LOCAL(synth); + FLUID_API_RETURN(result); +} + +/* Local variant of the system reset command */ +static int +fluid_synth_system_reset_LOCAL(fluid_synth_t *synth) +{ + int i; + + fluid_synth_all_sounds_off_LOCAL(synth, -1); + + for(i = 0; i < synth->midi_channels; i++) + { + fluid_channel_reset(synth->channel[i]); + } + + /* Basic channel 0, Mode Omni On Poly */ + fluid_synth_set_basic_channel(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, + synth->midi_channels); + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_reverb, 0, 0.0f); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_reset_chorus, 0, 0.0f); + + return FLUID_OK; +} + +/** + * Update voices on a MIDI channel after a MIDI control change. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param is_cc Boolean value indicating if ctrl is a CC controller or not + * @param ctrl MIDI controller value + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +static int +fluid_synth_modulate_voices_LOCAL(fluid_synth_t *synth, int chan, int is_cc, int ctrl) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_get_channel(voice) == chan) + { + fluid_voice_modulate(voice, is_cc, ctrl); + } + } + + return FLUID_OK; +} + +/** + * Update voices on a MIDI channel after all MIDI controllers have been changed. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +static int +fluid_synth_modulate_voices_all_LOCAL(fluid_synth_t *synth, int chan) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_get_channel(voice) == chan) + { + fluid_voice_modulate_all(voice); + } + } + + return FLUID_OK; +} + +/** + * Set the MIDI channel pressure controller value. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param val MIDI channel pressure value (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_channel_pressure(fluid_synth_t *synth, int chan, int val) +{ + int result; + fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "channelpressure\t%d\t%d", chan, val); + } + + fluid_channel_set_channel_pressure(synth->channel[chan], val); + result = fluid_synth_update_channel_pressure_LOCAL(synth, chan); + + FLUID_API_RETURN(result); +} + +/* Updates channel pressure from within synthesis thread */ +static int +fluid_synth_update_channel_pressure_LOCAL(fluid_synth_t *synth, int chan) +{ + return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_CHANNELPRESSURE); +} + +/** + * Set the MIDI polyphonic key pressure controller value. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI key number (0-127) + * @param val MIDI key pressure value (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 2.0.0 + */ +int +fluid_synth_key_pressure(fluid_synth_t *synth, int chan, int key, int val) +{ + int result; + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail(val >= 0 && val <= 127, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "keypressure\t%d\t%d\t%d", chan, key, val); + } + + fluid_channel_set_key_pressure(synth->channel[chan], key, val); + result = fluid_synth_update_key_pressure_LOCAL(synth, chan, key); + + FLUID_API_RETURN(result); +} + +/* Updates key pressure from within synthesis thread */ +static int +fluid_synth_update_key_pressure_LOCAL(fluid_synth_t *synth, int chan, int key) +{ + fluid_voice_t *voice; + int i; + int result = FLUID_OK; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(voice->chan == chan && voice->key == key) + { + result = fluid_voice_modulate(voice, 0, FLUID_MOD_KEYPRESSURE); + + if(result != FLUID_OK) + { + return result; + } + } + } + + return result; +} + +/** + * Set the MIDI pitch bend controller value on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param val MIDI pitch bend value (0-16383 with 8192 being center) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_pitch_bend(fluid_synth_t *synth, int chan, int val) +{ + int result; + fluid_return_val_if_fail(val >= 0 && val <= 16383, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "pitchb\t%d\t%d", chan, val); + } + + fluid_channel_set_pitch_bend(synth->channel[chan], val); + result = fluid_synth_update_pitch_bend_LOCAL(synth, chan); + + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of pitch bend */ +static int +fluid_synth_update_pitch_bend_LOCAL(fluid_synth_t *synth, int chan) +{ + return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEEL); +} + +/** + * Get the MIDI pitch bend controller value on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param ppitch_bend Location to store MIDI pitch bend value (0-16383 with + * 8192 being center) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_pitch_bend(fluid_synth_t *synth, int chan, int *ppitch_bend) +{ + int result; + fluid_return_val_if_fail(ppitch_bend != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + *ppitch_bend = fluid_channel_get_pitch_bend(synth->channel[chan]); + result = FLUID_OK; + + FLUID_API_RETURN(result); +} + +/** + * Set MIDI pitch wheel sensitivity on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param val Pitch wheel sensitivity value in semitones + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_pitch_wheel_sens(fluid_synth_t *synth, int chan, int val) +{ + int result; + fluid_return_val_if_fail(val >= 0 && val <= 72, FLUID_FAILED); /* 6 octaves!? Better than no limit.. */ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "pitchsens\t%d\t%d", chan, val); + } + + fluid_channel_set_pitch_wheel_sensitivity(synth->channel[chan], val); + result = fluid_synth_update_pitch_wheel_sens_LOCAL(synth, chan); + + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of set pitch wheel sensitivity */ +static int +fluid_synth_update_pitch_wheel_sens_LOCAL(fluid_synth_t *synth, int chan) +{ + return fluid_synth_modulate_voices_LOCAL(synth, chan, 0, FLUID_MOD_PITCHWHEELSENS); +} + +/** + * Get MIDI pitch wheel sensitivity on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param pval Location to store pitch wheel sensitivity value in semitones + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since Sometime AFTER v1.0 API freeze. + */ +int +fluid_synth_get_pitch_wheel_sens(fluid_synth_t *synth, int chan, int *pval) +{ + int result; + fluid_return_val_if_fail(pval != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + *pval = fluid_channel_get_pitch_wheel_sensitivity(synth->channel[chan]); + result = FLUID_OK; + + FLUID_API_RETURN(result); +} + +/** + * Assign a preset to a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param preset Preset to assign to channel or NULL to clear (ownership is taken over) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +static int +fluid_synth_set_preset(fluid_synth_t *synth, int chan, fluid_preset_t *preset) +{ + fluid_channel_t *channel; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + + channel = synth->channel[chan]; + + return fluid_channel_set_preset(channel, preset); +} + +/* Get a preset by SoundFont, bank and program numbers. + * Returns preset pointer or NULL. + */ +static fluid_preset_t * +fluid_synth_get_preset(fluid_synth_t *synth, int sfontnum, + int banknum, int prognum) +{ + fluid_sfont_t *sfont; + fluid_list_t *list; + + /* 128 indicates an "unset" operation" */ + if(prognum == FLUID_UNSET_PROGRAM) + { + return NULL; + } + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == sfontnum) + { + return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); + } + } + + return NULL; +} + +/* Get a preset by SoundFont name, bank and program. + * Returns preset pointer or NULL. + */ +static fluid_preset_t * +fluid_synth_get_preset_by_sfont_name(fluid_synth_t *synth, const char *sfontname, + int banknum, int prognum) +{ + fluid_sfont_t *sfont; + fluid_list_t *list; + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(FLUID_STRCMP(fluid_sfont_get_name(sfont), sfontname) == 0) + { + return fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); + } + } + + return NULL; +} + +/* Find a preset by bank and program numbers. + * Returns preset pointer or NULL. + */ +fluid_preset_t * +fluid_synth_find_preset(fluid_synth_t *synth, int banknum, + int prognum) +{ + fluid_preset_t *preset; + fluid_sfont_t *sfont; + fluid_list_t *list; + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + preset = fluid_sfont_get_preset(sfont, banknum - sfont->bankofs, prognum); + + if(preset) + { + return preset; + } + } + + return NULL; +} + +/** + * Send a program change event on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param prognum MIDI program number (0-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +/* As of 1.1.1 prognum can be set to 128 to unset the preset. Not documented + * since fluid_synth_unset_program() should be used instead. */ +int +fluid_synth_program_change(fluid_synth_t *synth, int chan, int prognum) +{ + fluid_preset_t *preset = NULL; + fluid_channel_t *channel; + int subst_bank, subst_prog, banknum = 0, result = FLUID_FAILED; + + fluid_return_val_if_fail(prognum >= 0 && prognum <= 128, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + channel = synth->channel[chan]; + + if(channel->channel_type == CHANNEL_TYPE_DRUM) + { + banknum = DRUM_INST_BANK; + } + else + { + fluid_channel_get_sfont_bank_prog(channel, NULL, &banknum, NULL); + } + + if(synth->verbose) + { + FLUID_LOG(FLUID_INFO, "prog\t%d\t%d\t%d", chan, banknum, prognum); + } + + /* I think this is a hack for MIDI files that do bank changes in GM mode. + * Proper way to handle this would probably be to ignore bank changes when in + * GM mode. - JG + * This is now possible by setting synth.midi-bank-select=gm, but let the hack + * stay for the time being. - DH + */ + if(prognum != FLUID_UNSET_PROGRAM) + { + subst_bank = banknum; + subst_prog = prognum; + + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + + /* Fallback to another preset if not found */ + if(!preset) + { + /* Percussion: Fallback to preset 0 in percussion bank */ + if(channel->channel_type == CHANNEL_TYPE_DRUM) + { + subst_prog = 0; + subst_bank = DRUM_INST_BANK; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + } + /* Melodic instrument */ + else + { + /* Fallback first to bank 0:prognum */ + subst_bank = 0; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + + /* Fallback to first preset in bank 0 (usually piano...) */ + if(!preset) + { + subst_prog = 0; + preset = fluid_synth_find_preset(synth, subst_bank, subst_prog); + } + } + + if(preset) + { + FLUID_LOG(FLUID_WARN, "Instrument not found on channel %d [bank=%d prog=%d], substituted [bank=%d prog=%d]", + chan, banknum, prognum, subst_bank, subst_prog); + } + else + { + FLUID_LOG(FLUID_WARN, "No preset found on channel %d [bank=%d prog=%d]", chan, banknum, prognum); + } + } + } + + /* Assign the SoundFont ID and program number to the channel */ + fluid_channel_set_sfont_bank_prog(channel, preset ? fluid_sfont_get_id(preset->sfont) : 0, + -1, prognum); + result = fluid_synth_set_preset(synth, chan, preset); + + FLUID_API_RETURN(result); +} + +/** + * Set instrument bank number on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param bank MIDI bank number + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @note This function does not change the instrument currently assigned to \c chan, + * as it is usually called prior to fluid_synth_program_change(). If you still want + * instrument changes to take effect immediately, call fluid_synth_program_reset() + * after having set up the bank configuration. + * + */ +int +fluid_synth_bank_select(fluid_synth_t *synth, int chan, int bank) +{ + int result; + fluid_return_val_if_fail(bank <= 16383, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + fluid_channel_set_sfont_bank_prog(synth->channel[chan], -1, bank, -1); + result = FLUID_OK; + + FLUID_API_RETURN(result); +} + +/** + * Set SoundFont ID on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_id ID of a loaded SoundFont + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @note This function does not change the instrument currently assigned to \c chan, + * as it is usually called prior to fluid_synth_bank_select() or fluid_synth_program_change(). + * If you still want instrument changes to take effect immediately, call fluid_synth_program_reset() + * after having selected the soundfont. + */ +int +fluid_synth_sfont_select(fluid_synth_t *synth, int chan, int sfont_id) +{ + int result; + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + fluid_channel_set_sfont_bank_prog(synth->channel[chan], sfont_id, -1, -1); + result = FLUID_OK; + + FLUID_API_RETURN(result); +} + +/** + * Set the preset of a MIDI channel to an unassigned state. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.1 + * + * @note Channel retains its SoundFont ID and bank numbers, while the program + * number is set to an "unset" state. MIDI program changes may re-assign a + * preset if one matches. + */ +int +fluid_synth_unset_program(fluid_synth_t *synth, int chan) +{ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + FLUID_API_RETURN(fluid_synth_program_change(synth, chan, FLUID_UNSET_PROGRAM)); +} + +/** + * Get current SoundFont ID, bank number and program number for a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_id Location to store SoundFont ID + * @param bank_num Location to store MIDI bank number + * @param preset_num Location to store MIDI program number + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_program(fluid_synth_t *synth, int chan, int *sfont_id, + int *bank_num, int *preset_num) +{ + int result; + fluid_channel_t *channel; + + fluid_return_val_if_fail(sfont_id != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank_num != NULL, FLUID_FAILED); + fluid_return_val_if_fail(preset_num != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + channel = synth->channel[chan]; + fluid_channel_get_sfont_bank_prog(channel, sfont_id, bank_num, preset_num); + + /* 128 indicates that the preset is unset. Set to 0 to be backwards compatible. */ + if(*preset_num == FLUID_UNSET_PROGRAM) + { + *preset_num = 0; + } + + result = FLUID_OK; + + FLUID_API_RETURN(result); +} + +/** + * Select an instrument on a MIDI channel by SoundFont ID, bank and program numbers. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_id ID of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_program_select(fluid_synth_t *synth, int chan, int sfont_id, + int bank_num, int preset_num) +{ + fluid_preset_t *preset = NULL; + fluid_channel_t *channel; + int result; + fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); + fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + channel = synth->channel[chan]; + + preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num); + + if(preset == NULL) + { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %d", + bank_num, preset_num, sfont_id); + FLUID_API_RETURN(FLUID_FAILED); + } + + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog(channel, sfont_id, bank_num, preset_num); + result = fluid_synth_set_preset(synth, chan, preset); + + FLUID_API_RETURN(result); +} + +/** + * Pins all samples of the given preset. + * + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return #FLUID_OK if the preset was found, pinned and loaded + * into memory successfully. #FLUID_FAILED otherwise. Note that #FLUID_OK + * is returned, even if synth.dynamic-sample-loading is disabled or + * the preset doesn't support dynamic-sample-loading. + * + * This function will attempt to pin all samples of the given preset and + * load them into memory, if they are currently unloaded. "To pin" in this + * context means preventing them from being unloaded by an upcoming channel + * prog change. + * + * @note This function is only useful if \ref settings_synth_dynamic-sample-loading is enabled. + * By default, dynamic-sample-loading is disabled and all samples are kept in memory. + * Furthermore, this is only useful for presets which support dynamic-sample-loading (currently, + * only preset loaded with the default soundfont loader do). + * + * @since 2.2.0 + */ +int +fluid_synth_pin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num) +{ + int ret; + fluid_preset_t *preset; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); + fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num); + + if(preset == NULL) + { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %d", + bank_num, preset_num, sfont_id); + FLUID_API_RETURN(FLUID_FAILED); + } + + ret = fluid_preset_notify(preset, FLUID_PRESET_PIN, -1); // channel unused for pinning messages + + FLUID_API_RETURN(ret); +} + +/** + * Unpin all samples of the given preset. + * + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return #FLUID_OK if preset was found, #FLUID_FAILED otherwise + * + * This function undoes the effect of fluid_synth_pin_preset(). If the preset is + * not currently used, its samples will be unloaded. + * + * @note Only useful for presets loaded with the default soundfont loader and + * only if \ref settings_synth_dynamic-sample-loading is enabled. + * + * @since 2.2.0 + */ +int +fluid_synth_unpin_preset(fluid_synth_t *synth, int sfont_id, int bank_num, int preset_num) +{ + int ret; + fluid_preset_t *preset; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank_num >= 0, FLUID_FAILED); + fluid_return_val_if_fail(preset_num >= 0, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + preset = fluid_synth_get_preset(synth, sfont_id, bank_num, preset_num); + + if(preset == NULL) + { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %d", + bank_num, preset_num, sfont_id); + FLUID_API_RETURN(FLUID_FAILED); + } + + ret = fluid_preset_notify(preset, FLUID_PRESET_UNPIN, -1); // channel unused for pinning messages + + FLUID_API_RETURN(ret); +} + +/** + * Select an instrument on a MIDI channel by SoundFont name, bank and program numbers. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param sfont_name Name of a loaded SoundFont + * @param bank_num MIDI bank number + * @param preset_num MIDI program number + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + */ +int +fluid_synth_program_select_by_sfont_name(fluid_synth_t *synth, int chan, + const char *sfont_name, int bank_num, + int preset_num) +{ + fluid_preset_t *preset = NULL; + fluid_channel_t *channel; + int result; + fluid_return_val_if_fail(sfont_name != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* Allowed only on MIDI channel enabled */ + FLUID_API_RETURN_IF_CHAN_DISABLED(FLUID_FAILED); + + channel = synth->channel[chan]; + + preset = fluid_synth_get_preset_by_sfont_name(synth, sfont_name, bank_num, + preset_num); + + if(preset == NULL) + { + FLUID_LOG(FLUID_ERR, + "There is no preset with bank number %d and preset number %d in SoundFont %s", + bank_num, preset_num, sfont_name); + FLUID_API_RETURN(FLUID_FAILED); + } + + /* Assign the new SoundFont ID, bank and program number to the channel */ + fluid_channel_set_sfont_bank_prog(channel, fluid_sfont_get_id(preset->sfont), + bank_num, preset_num); + result = fluid_synth_set_preset(synth, chan, preset); + + FLUID_API_RETURN(result); +} + +/* + * This function assures that every MIDI channel has a valid preset + * (NULL is okay). This function is called after a SoundFont is + * unloaded or reloaded. + */ +static void +fluid_synth_update_presets(fluid_synth_t *synth) +{ + fluid_channel_t *channel; + fluid_preset_t *preset; + int sfont, bank, prog; + int chan; + + for(chan = 0; chan < synth->midi_channels; chan++) + { + channel = synth->channel[chan]; + fluid_channel_get_sfont_bank_prog(channel, &sfont, &bank, &prog); + preset = fluid_synth_get_preset(synth, sfont, bank, prog); + fluid_synth_set_preset(synth, chan, preset); + } +} + +static void +fluid_synth_set_sample_rate_LOCAL(fluid_synth_t *synth, float sample_rate) +{ + int i; + fluid_clip(sample_rate, 8000.0f, 96000.0f); + synth->sample_rate = sample_rate; + + synth->min_note_length_ticks = fluid_synth_get_min_note_length_LOCAL(synth); + + for(i = 0; i < synth->polyphony; i++) + { + fluid_voice_set_output_rate(synth->voice[i], sample_rate); + } +} + +/** + * Set up an event to change the sample-rate of the synth during the next rendering call. + * @warning This function is broken-by-design! Don't use it! Instead, specify the sample-rate when creating the synth. + * @deprecated As of fluidsynth 2.1.0 this function has been deprecated. + * Changing the sample-rate is generally not considered to be a real-time use-case, as it always produces some audible artifact ("click", "pop") on the dry sound and effects (because LFOs for chorus and reverb need to be reinitialized). + * The sample-rate change may also require memory allocation deep down in the effect units. + * However, this memory allocation may fail and there is no way for the caller to know that, because the actual change of the sample-rate is executed during rendering. + * This function cannot (must not) do the sample-rate change itself, otherwise the synth needs to be locked down, causing rendering to block. + * Esp. do not use this function if this @p synth instance is used by an audio driver, because the audio driver cannot be notified by this sample-rate change. + * Long story short: don't use it. + * @code{.cpp} + fluid_synth_t* synth; // assume initialized + // [...] + // sample-rate change needed? Delete the audio driver, if any. + delete_fluid_audio_driver(adriver); + // then delete the synth + delete_fluid_synth(synth); + // update the sample-rate + fluid_settings_setnum(settings, "synth.sample-rate", 22050.0); + // and re-create objects + synth = new_fluid_synth(settings); + adriver = new_fluid_audio_driver(settings, synth); + * @endcode + * @param synth FluidSynth instance + * @param sample_rate New sample-rate (Hz) + * @since 1.1.2 + */ +void +fluid_synth_set_sample_rate(fluid_synth_t *synth, float sample_rate) +{ + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + fluid_synth_set_sample_rate_LOCAL(synth, sample_rate); + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_samplerate, + 0, synth->sample_rate); + fluid_synth_api_exit(synth); +} + +// internal sample rate change function for the jack driver +// executes immediately, therefore, make sure no rendering call is running! +void +fluid_synth_set_sample_rate_immediately(fluid_synth_t *synth, float sample_rate) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + fluid_synth_set_sample_rate_LOCAL(synth, sample_rate); + + param[0].i = 0; + param[1].real = synth->sample_rate; + fluid_rvoice_mixer_set_samplerate(synth->eventhandler->mixer, param); + + fluid_synth_api_exit(synth); +} + + +/* Handler for synth.gain setting. */ +static void +fluid_synth_handle_gain(void *data, const char *name, double value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_synth_set_gain(synth, (float) value); +} + +/** + * Set synth output gain value. + * @param synth FluidSynth instance + * @param gain Gain value (function clamps value to the range 0.0 to 10.0) + */ +void +fluid_synth_set_gain(fluid_synth_t *synth, float gain) +{ + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + fluid_clip(gain, 0.0f, 10.0f); + + synth->gain = gain; + fluid_synth_update_gain_LOCAL(synth); + fluid_synth_api_exit(synth); +} + +/* Called by synthesis thread to update the gain in all voices */ +static void +fluid_synth_update_gain_LOCAL(fluid_synth_t *synth) +{ + fluid_voice_t *voice; + float gain; + int i; + + gain = synth->gain; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice)) + { + fluid_voice_set_gain(voice, gain); + } + } +} + +/** + * Get synth output gain value. + * @param synth FluidSynth instance + * @return Synth gain value (0.0 to 10.0) + */ +float +fluid_synth_get_gain(fluid_synth_t *synth) +{ + float result; + fluid_return_val_if_fail(synth != NULL, 0.0); + fluid_synth_api_enter(synth); + + result = synth->gain; + FLUID_API_RETURN(result); +} + +/* + * Handler for synth.polyphony setting. + */ +static void +fluid_synth_handle_polyphony(void *data, const char *name, int value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_synth_set_polyphony(synth, value); +} + +/** + * Set synthesizer polyphony (max number of voices). + * @param synth FluidSynth instance + * @param polyphony Polyphony to assign + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.0.6 + */ +int +fluid_synth_set_polyphony(fluid_synth_t *synth, int polyphony) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(polyphony >= 1 && polyphony <= 65535, FLUID_FAILED); + fluid_synth_api_enter(synth); + + result = fluid_synth_update_polyphony_LOCAL(synth, polyphony); + + FLUID_API_RETURN(result); +} + +/* Called by synthesis thread to update the polyphony value */ +static int +fluid_synth_update_polyphony_LOCAL(fluid_synth_t *synth, int new_polyphony) +{ + fluid_voice_t *voice; + int i; + + if(new_polyphony > synth->nvoice) + { + /* Create more voices */ + fluid_voice_t **new_voices = FLUID_REALLOC(synth->voice, + sizeof(fluid_voice_t *) * new_polyphony); + + if(new_voices == NULL) + { + return FLUID_FAILED; + } + + synth->voice = new_voices; + + for(i = synth->nvoice; i < new_polyphony; i++) + { + synth->voice[i] = new_fluid_voice(synth->eventhandler, synth->sample_rate); + + if(synth->voice[i] == NULL) + { + return FLUID_FAILED; + } + + fluid_voice_set_custom_filter(synth->voice[i], synth->custom_filter_type, synth->custom_filter_flags); + } + + synth->nvoice = new_polyphony; + } + + synth->polyphony = new_polyphony; + + /* turn off any voices above the new limit */ + for(i = synth->polyphony; i < synth->nvoice; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice)) + { + fluid_voice_off(voice); + } + } + + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony, + synth->polyphony, 0.0f); + + return FLUID_OK; +} + +/** + * Get current synthesizer polyphony (max number of voices). + * @param synth FluidSynth instance + * @return Synth polyphony value. + * @since 1.0.6 + */ +int +fluid_synth_get_polyphony(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + result = synth->polyphony; + FLUID_API_RETURN(result); +} + +/** + * @brief Get current number of active voices. + * + * I.e. the no. of voices that have been + * started and have not yet finished. Unless called from synthesis context, + * this number does not necessarily have to be equal to the number of voices + * currently processed by the DSP loop, see below. + * @param synth FluidSynth instance + * @return Number of currently active voices. + * @since 1.1.0 + * + * @note To generate accurate continuous statistics of the voice count, caller + * should ensure this function is called synchronously with the audio synthesis + * process. This can be done in the new_fluid_audio_driver2() audio callback + * function for example. Otherwise every call to this function may return different + * voice counts as it may change after any (concurrent) call to fluid_synth_write_*() made by + * e.g. an audio driver or the applications audio rendering thread. + */ +int +fluid_synth_get_active_voice_count(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + result = synth->active_voice_count; + FLUID_API_RETURN(result); +} + +/** + * Get the internal synthesis buffer size value. + * @param synth FluidSynth instance + * @return Internal buffer size in audio frames. + * + * Audio is synthesized at this number of frames at a time. Defaults to 64 frames. I.e. the synth can only react to notes, + * control changes, and other audio affecting events after having processed 64 audio frames. + */ +int +fluid_synth_get_internal_bufsize(fluid_synth_t *synth) +{ + return FLUID_BUFSIZE; +} + +/** + * Resend a bank select and a program change for every channel and assign corresponding instruments. + * @param synth FluidSynth instance + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * This function is called mainly after a SoundFont has been loaded, + * unloaded or reloaded. + */ +int +fluid_synth_program_reset(fluid_synth_t *synth) +{ + int i, prog; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + /* try to set the correct presets */ + for(i = 0; i < synth->midi_channels; i++) + { + fluid_channel_get_sfont_bank_prog(synth->channel[i], NULL, NULL, &prog); + fluid_synth_program_change(synth, i, prog); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Synthesize a block of floating point audio to separate audio buffers (multi-channel rendering). + * + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param left Array of float buffers to store left channel of planar audio (as many as \c synth.audio-channels buffers, each of \c len in size) + * @param right Array of float buffers to store right channel of planar audio (size: dito) + * @param fx_left Since 1.1.7: If not \c NULL, array of float buffers to store left effect channels (as many as \c synth.effects-channels buffers, each of \c len in size) + * @param fx_right Since 1.1.7: If not \c NULL, array of float buffers to store right effect channels (size: dito) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * First effect channel used by reverb, second for chorus. + * + * @note Should only be called from synthesis thread. + * + * @deprecated fluid_synth_nwrite_float() is deprecated and will be removed in a future release. + * It may continue to work or it may return #FLUID_FAILED in the future. Consider using the more + * powerful and flexible fluid_synth_process(). + * + * Usage example: + * @code{.cpp} + const int FramesToRender = 64; + int channels; + // retrieve number of stereo audio channels + fluid_settings_getint(settings, "synth.audio-channels", &channels); + + // we need twice as many (mono-)buffers + channels *= 2; + + // fluid_synth_nwrite_float renders planar audio, e.g. if synth.audio-channels==16: + // each midi channel gets rendered to its own stereo buffer, rather than having + // one buffer and interleaved PCM + float** mix_buf = new float*[channels]; + for(int i = 0; i < channels; i++) + { + mix_buf[i] = new float[FramesToRender]; + } + + // retrieve number of (stereo) effect channels (internally hardcoded to reverb (first chan) + // and chrous (second chan)) + fluid_settings_getint(settings, "synth.effects-channels", &channels); + channels *= 2; + + float** fx_buf = new float*[channels]; + for(int i = 0; i < channels; i++) + { + fx_buf[i] = new float[FramesToRender]; + } + + float** mix_buf_l = mix_buf; + float** mix_buf_r = &mix_buf[channels/2]; + + float** fx_buf_l = fx_buf; + float** fx_buf_r = &fx_buf[channels/2]; + + fluid_synth_nwrite_float(synth, FramesToRender, mix_buf_l, mix_buf_r, fx_buf_l, fx_buf_r) + * @endcode + */ +int +fluid_synth_nwrite_float(fluid_synth_t *synth, int len, + float **left, float **right, + float **fx_left, float **fx_right) +{ + fluid_real_t *left_in, *fx_left_in; + fluid_real_t *right_in, *fx_right_in; + double time = fluid_utime(); + int i, num, available, count; +#ifdef WITH_FLOAT + int bytes; +#endif + float cpu_load; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(left != NULL, FLUID_FAILED); + fluid_return_val_if_fail(right != NULL, FLUID_FAILED); + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below + + /* First, take what's still available in the buffer */ + count = 0; + num = synth->cur; + + if(synth->cur < FLUID_BUFSIZE) + { + available = FLUID_BUFSIZE - synth->cur; + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); + + num = (available > len) ? len : available; +#ifdef WITH_FLOAT + bytes = num * sizeof(float); +#endif + + for(i = 0; i < synth->audio_channels; i++) + { +#ifdef WITH_FLOAT + FLUID_MEMCPY(left[i], &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); + FLUID_MEMCPY(right[i], &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); +#else //WITH_FLOAT + int j; + + for(j = 0; j < num; j++) + { + left[i][j] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + right[i][j] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + } + +#endif //WITH_FLOAT + } + + for(i = 0; i < synth->effects_channels; i++) + { +#ifdef WITH_FLOAT + + if(fx_left != NULL) + { + FLUID_MEMCPY(fx_left[i], &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); + } + + if(fx_right != NULL) + { + FLUID_MEMCPY(fx_right[i], &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + synth->cur], bytes); + } + +#else //WITH_FLOAT + int j; + + if(fx_left != NULL) + { + for(j = 0; j < num; j++) + { + fx_left[i][j] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + } + } + + if(fx_right != NULL) + { + for(j = 0; j < num; j++) + { + fx_right[i][j] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + synth->cur]; + } + } + +#endif //WITH_FLOAT + } + + count += num; + num += synth->cur; /* if we're now done, num becomes the new synth->cur below */ + } + + /* Then, run one_block() and copy till we have 'len' samples */ + while(count < len) + { + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, 0); + fluid_synth_render_blocks(synth, 1); // TODO: + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); + + num = (FLUID_BUFSIZE > len - count) ? len - count : FLUID_BUFSIZE; +#ifdef WITH_FLOAT + bytes = num * sizeof(float); +#endif + + for(i = 0; i < synth->audio_channels; i++) + { +#ifdef WITH_FLOAT + FLUID_MEMCPY(left[i] + count, &left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); + FLUID_MEMCPY(right[i] + count, &right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); +#else //WITH_FLOAT + int j; + + for(j = 0; j < num; j++) + { + left[i][j + count] = (float) left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + right[i][j + count] = (float) right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + } + +#endif //WITH_FLOAT + } + + for(i = 0; i < synth->effects_channels; i++) + { +#ifdef WITH_FLOAT + + if(fx_left != NULL) + { + FLUID_MEMCPY(fx_left[i] + count, &fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); + } + + if(fx_right != NULL) + { + FLUID_MEMCPY(fx_right[i] + count, &fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT], bytes); + } + +#else //WITH_FLOAT + int j; + + if(fx_left != NULL) + { + for(j = 0; j < num; j++) + { + fx_left[i][j + count] = (float) fx_left_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + } + } + + if(fx_right != NULL) + { + for(j = 0; j < num; j++) + { + fx_right[i][j + count] = (float) fx_right_in[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j]; + } + } + +#endif //WITH_FLOAT + } + + count += num; + } + + synth->cur = num; + + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); + + return FLUID_OK; +} + +/** + * mixes the samples of \p in to \p out + * + * @param out the output sample buffer to mix to + * @param ooff sample offset in \p out + * @param in the rvoice_mixer input sample buffer to mix from + * @param ioff sample offset in \p in + * @param buf_idx the sample buffer index of \p in to mix from + * @param num number of samples to mix + */ +static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out, + int ooff, + const fluid_real_t *FLUID_RESTRICT in, + int ioff, + int buf_idx, + int num) +{ + if(out != NULL) + { + int j; + + for(j = 0; j < num; j++) + { + out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff]; + } + } +} + +/** + * Synthesize floating point audio to stereo audio channels + * (implements the default interface #fluid_audio_func_t). + * + * @param synth FluidSynth instance + * + * @param len Count of audio frames to synthesize and store in every single buffer provided by \p out and \p fx. + * Zero value is permitted, the function does nothing and return FLUID_OK. + * + * @param nfx Count of arrays in \c fx. Must be a multiple of 2 (because of stereo) + * and in the range 0 <= nfx/2 <= (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups()). + * Note that zero value is valid and allows to skip mixing effects in all fx output buffers. + * + * @param fx Array of buffers to store effects audio to. Buffers may + * alias with buffers of \c out. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer. + * + * @param nout Count of arrays in \c out. Must be a multiple of 2 + * (because of stereo) and in the range 0 <= nout/2 <= fluid_synth_count_audio_channels(). + * Note that zero value is valid and allows to skip mixing dry audio in all out output buffers. + * + * @param out Array of buffers to store (dry) audio to. Buffers may + * alias with buffers of \c fx. Individual NULL buffers are permitted and will cause to skip mixing any audio into that buffer. + * + * @return #FLUID_OK on success, + * #FLUID_FAILED otherwise, + * - fx == NULL while nfx > 0, or out == NULL while nout > 0. + * - \c nfx or \c nout not multiple of 2. + * - len < 0. + * - \c nfx or \c nout exceed the range explained above. + * + * Synthesize and mix audio to a given number of planar audio buffers. + * Therefore pass nout = N*2 float buffers to \p out in order to render + * the synthesized audio to \p N stereo channels. Each float buffer must be + * able to hold \p len elements. + * + * \p out contains an array of planar buffers for normal, dry, stereo + * audio (alternating left and right). Like: +@code{.cpp} +out[0] = left_buffer_audio_channel_0 +out[1] = right_buffer_audio_channel_0 +out[2] = left_buffer_audio_channel_1 +out[3] = right_buffer_audio_channel_1 +... +out[ (i * 2 + 0) % nout ] = left_buffer_audio_channel_i +out[ (i * 2 + 1) % nout ] = right_buffer_audio_channel_i +@endcode + * + * for zero-based channel index \p i. + * The buffer layout of \p fx used for storing effects + * like reverb and chorus looks similar: +@code{.cpp} +fx[0] = left_buffer_channel_of_reverb_unit_0 +fx[1] = right_buffer_channel_of_reverb_unit_0 +fx[2] = left_buffer_channel_of_chorus_unit_0 +fx[3] = right_buffer_channel_of_chorus_unit_0 +fx[4] = left_buffer_channel_of_reverb_unit_1 +fx[5] = right_buffer_channel_of_reverb_unit_1 +fx[6] = left_buffer_channel_of_chorus_unit_1 +fx[7] = right_buffer_channel_of_chorus_unit_1 +fx[8] = left_buffer_channel_of_reverb_unit_2 +... +fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 0) % nfx ] = left_buffer_for_effect_channel_j_of_unit_k +fx[ ((k * fluid_synth_count_effects_channels() + j) * 2 + 1) % nfx ] = right_buffer_for_effect_channel_j_of_unit_k +@endcode + * where 0 <= k < fluid_synth_count_effects_groups() is a zero-based index denoting the effects unit and + * 0 <= j < fluid_synth_count_effects_channels() is a zero-based index denoting the effect channel within + * unit \p k. + * + * Any playing voice is assigned to audio channels based on the MIDI channel it's playing on: Let \p chan be the + * zero-based MIDI channel index an arbitrary voice is playing on. To determine the audio channel and effects unit it is + * going to be rendered to use: + * + * i = chan % fluid_synth_count_audio_groups() + * + * k = chan % fluid_synth_count_effects_groups() + * + * @parblock + * @note The owner of the sample buffers must zero them out before calling this + * function, because any synthesized audio is mixed (i.e. added) to the buffers. + * E.g. if fluid_synth_process() is called from a custom audio driver process function + * (see new_fluid_audio_driver2()), the audio driver takes care of zeroing the buffers. + * @endparblock + * + * @parblock + * @note No matter how many buffers you pass in, fluid_synth_process() + * will always render all audio channels to the + * buffers in \c out and all effects channels to the + * buffers in \c fx, provided that nout > 0 and nfx > 0 respectively. If + * nout/2 < fluid_synth_count_audio_channels() it will wrap around. Same + * is true for effects audio if nfx/2 < (fluid_synth_count_effects_channels() * fluid_synth_count_effects_groups()). + * See usage examples below. + * @endparblock + * + * @parblock + * @note Should only be called from synthesis thread. + * @endparblock + */ +int +fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[], + int nout, float *out[]) +{ + return fluid_synth_process_LOCAL(synth, len, nfx, fx, nout, out, fluid_synth_render_blocks); +} + +/* declared public (instead of static) for testing purpose */ +int +fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], + int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int)) +{ + fluid_real_t *left_in, *fx_left_in; + fluid_real_t *right_in, *fx_right_in; + int nfxchan, nfxunits, naudchan; + + double time = fluid_utime(); + int i, f, num, count, buffered_blocks; + + float cpu_load; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + /* fx NULL while nfx > 0 is invalid */ + fluid_return_val_if_fail((fx != NULL) || (nfx == 0), FLUID_FAILED); + /* nfx must be multiple of 2. Note that 0 value is valid and + allows to skip mixing in fx output buffers + */ + fluid_return_val_if_fail(nfx % 2 == 0, FLUID_FAILED); + + /* out NULL while nout > 0 is invalid */ + fluid_return_val_if_fail((out != NULL) || (nout == 0), FLUID_FAILED); + /* nout must be multiple of 2. Note that 0 value is valid and + allows to skip mixing in out output buffers + */ + fluid_return_val_if_fail(nout % 2 == 0, FLUID_FAILED); + + /* check len value. Note that 0 value is valid, the function does nothing and returns FLUID_OK. + */ + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below + + nfxchan = synth->effects_channels; + nfxunits = synth->effects_groups; + naudchan = synth->audio_channels; + + fluid_return_val_if_fail(0 <= nfx / 2 && nfx / 2 <= nfxchan * nfxunits, FLUID_FAILED); + fluid_return_val_if_fail(0 <= nout / 2 && nout / 2 <= naudchan, FLUID_FAILED); + + /* get internal mixer audio dry buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + /* get internal mixer audio effect buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_fx_bufs(synth->eventhandler->mixer, &fx_left_in, &fx_right_in); + + /* Conversely to fluid_synth_write_float(),fluid_synth_write_s16() (which handle only one + stereo output) we don't want rendered audio effect mixed in internal audio dry buffers. + FALSE instructs the mixer that internal audio effects will be mixed in respective internal + audio effects buffers. + */ + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, FALSE); + + + /* First, take what's still available in the buffer */ + count = 0; + /* synth->cur indicates if available samples are still in internal mixer buffer */ + num = synth->cur; + + buffered_blocks = (synth->cur + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + if(synth->cur < buffered_blocks * FLUID_BUFSIZE) + { + /* yes, available sample are in internal mixer buffer */ + int available = (buffered_blocks * FLUID_BUFSIZE) - synth->cur; + num = (available > len) ? len : available; + + /* mixing dry samples (or skip if requested by the caller) */ + if(nout != 0) + { + for(i = 0; i < naudchan; i++) + { + /* mix num left samples from input mixer buffer (left_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ + float *out_buf = out[(i * 2) % nout]; + fluid_synth_mix_single_buffer(out_buf, 0, left_in, synth->cur, i, num); + + /* mix num right samples from input mixer buffer (right_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ + out_buf = out[(i * 2 + 1) % nout]; + fluid_synth_mix_single_buffer(out_buf, 0, right_in, synth->cur, i, num); + } + } + + /* mixing effects samples (or skip if requested by the caller) */ + if(nfx != 0) + { + // loop over all effects units + for(f = 0; f < nfxunits; f++) + { + // write out all effects (i.e. reverb and chorus) + for(i = 0; i < nfxchan; i++) + { + int buf_idx = f * nfxchan + i; + + /* mix num left samples from input mixer buffer (fx_left_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ + float *out_buf = fx[(buf_idx * 2) % nfx]; + fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num); + + /* mix num right samples from input mixer buffer (fx_right_in) at input offset + synth->cur to output buffer (out_buf) at offset 0 */ + out_buf = fx[(buf_idx * 2 + 1) % nfx]; + fluid_synth_mix_single_buffer(out_buf, 0, fx_right_in, synth->cur, buf_idx, num); + } + } + } + + count += num; + num += synth->cur; /* if we're now done, num becomes the new synth->cur below */ + } + + /* Then, render blocks and copy till we have 'len' samples */ + while(count < len) + { + /* always render full bloc multiple of FLUID_BUFSIZE */ + int blocksleft = (len - count + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + /* render audio (dry and effect) to respective internal dry and effect buffers */ + int blockcount = block_render_func(synth, blocksleft); + + num = (blockcount * FLUID_BUFSIZE > len - count) ? len - count : blockcount * FLUID_BUFSIZE; + + /* mixing dry samples (or skip if requested by the caller) */ + if(nout != 0) + { + for(i = 0; i < naudchan; i++) + { + /* mix num left samples from input mixer buffer (left_in) at input offset + 0 to output buffer (out_buf) at offset count */ + float *out_buf = out[(i * 2) % nout]; + fluid_synth_mix_single_buffer(out_buf, count, left_in, 0, i, num); + + /* mix num right samples from input mixer buffer (right_in) at input offset + 0 to output buffer (out_buf) at offset count */ + out_buf = out[(i * 2 + 1) % nout]; + fluid_synth_mix_single_buffer(out_buf, count, right_in, 0, i, num); + } + } + + /* mixing effects samples (or skip if requested by the caller) */ + if(nfx != 0) + { + // loop over all effects units + for(f = 0; f < nfxunits; f++) + { + // write out all effects (i.e. reverb and chorus) + for(i = 0; i < nfxchan; i++) + { + int buf_idx = f * nfxchan + i; + + /* mix num left samples from input mixer buffer (fx_left_in) at input offset + 0 to output buffer (out_buf) at offset count */ + float *out_buf = fx[(buf_idx * 2) % nfx]; + fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num); + + /* mix num right samples from input mixer buffer (fx_right_in) at input offset + 0 to output buffer (out_buf) at offset count */ + out_buf = fx[(buf_idx * 2 + 1) % nfx]; + fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num); + } + } + } + + count += num; + } + + synth->cur = num; + + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); + + return FLUID_OK; +} + + +/** + * Synthesize a block of floating point audio samples to audio buffers. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param lout Array of floats to store left channel of audio + * @param loff Offset index in 'lout' for first sample + * @param lincr Increment between samples stored to 'lout' + * @param rout Array of floats to store right channel of audio + * @param roff Offset index in 'rout' for first sample + * @param rincr Increment between samples stored to 'rout' + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1, + * lincr = 2, rincr = 2). + * + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. + */ +int +fluid_synth_write_float(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr) +{ + void *channels_out[2] = {lout, rout}; + int channels_off[2] = {loff, roff }; + int channels_incr[2] = {lincr, rincr }; + + return fluid_synth_write_float_channels(synth, len, 2, channels_out, + channels_off, channels_incr); +} + +/** + * Synthesize a block of float audio samples channels to audio buffers. + * The function is convenient for audio driver to render multiple stereo + * channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels). + * + * @param synth FluidSynth instance. + * @param len Count of audio frames to synthesize. + * @param channels_count Count of channels in a frame. + * must be multiple of 2 and channel_count/2 must not exceed the number + * of internal mixer buffers (synth->audio_groups) + * @param channels_out Array of channels_count pointers on 16 bit words to + * store sample channels. Modified on return. + * @param channels_off Array of channels_count offset index to add to respective pointer + * in channels_out for first sample. + * @param channels_incr Array of channels_count increment between consecutive + * samples channels. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + * + * Useful for storing: + * - interleaved channels in a unique buffer. + * - non interleaved channels in an unique buffer (or in distinct buffers). + * + * Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn) + * in a unique buffer: + * { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4,... + * sn:c1, sn:c2, sn:c3, sn:c4 }. + * + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. + */ +int +fluid_synth_write_float_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]) +{ + return fluid_synth_write_float_channels_LOCAL(synth, len, channels_count, + channels_out, channels_off, channels_incr, + fluid_synth_render_blocks); +} + +int +fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[], + int (*block_render_func)(fluid_synth_t *, int)) +{ + float **chan_out = (float **)channels_out; + int n, cur, size; + + /* pointers on first input mixer buffer */ + fluid_real_t *left_in; + fluid_real_t *right_in; + int bufs_in_count; /* number of stereo input buffers */ + int i; + + /* start average cpu load probe */ + double time = fluid_utime(); + float cpu_load; + + /* start profiling duration probe (if profiling is enabled) */ + fluid_profile_ref_var(prof_ref); + + /* check parameters */ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below + + /* check for valid channel_count: must be multiple of 2 and + channel_count/2 must not exceed the number of internal mixer buffers + (synth->audio_groups) + */ + fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */ + && channels_count >= 2, FLUID_FAILED); + + bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */ + fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED); + + fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED); + + /* initialize output channels buffers on first sample position */ + i = channels_count; + do + { + i--; + chan_out[i] += channels_off[i]; + } + while(i); + + /* Conversely to fluid_synth_process(), + we want rendered audio effect mixed in internal audio dry buffers. + TRUE instructs the mixer that internal audio effects will be mixed in internal + audio dry buffers. + */ + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE); + + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + + size = len; + + /* synth->cur indicates if available samples are still in internal mixer buffer */ + cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */ + + do + { + /* fill up the buffers as needed */ + if(cur >= synth->curmax) + { + /* render audio (dry and effect) to internal dry buffers */ + /* always render full blocs multiple of FLUID_BUFSIZE */ + int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + synth->curmax = FLUID_BUFSIZE * block_render_func(synth, blocksleft); + + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + cur = 0; + } + + /* calculate amount of available samples */ + n = synth->curmax - cur; + + /* keep track of emitted samples */ + if(n > size) + { + n = size; + } + + size -= n; + + /* update pointers to current position */ + left_in += cur + n; + right_in += cur + n; + + /* set final cursor position */ + cur += n; + + /* reverse index */ + n = 0 - n; + + do + { + i = bufs_in_count; + do + { + /* input sample index in stereo buffer i */ + int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n; + int c = i << 1; /* channel index c to write */ + + /* write left input sample to channel sample */ + *chan_out[c] = (float) left_in[in_idx]; + + /* write right input sample to next channel sample */ + *chan_out[c+1] = (float) right_in[in_idx]; + + /* advance output pointers */ + chan_out[c] += channels_incr[c]; + chan_out[c+1] += channels_incr[c+1]; + } + while(i); + } + while(++n < 0); + } + while(size); + + synth->cur = cur; /* save current sample position. It will be used on next call */ + + /* save average cpu load, use by API for real time cpu load meter */ + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); + + /* stop duration probe and save performance measurement (if profiling is enabled) */ + fluid_profile_write(FLUID_PROF_WRITE, prof_ref, + fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), + len); + return FLUID_OK; +} + +/* for testing purpose */ +int +fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr, + int (*block_render_func)(fluid_synth_t *, int) + ) +{ + void *channels_out[2] = {lout, rout}; + int channels_off[2] = {loff, roff }; + int channels_incr[2] = {lincr, rincr }; + + return fluid_synth_write_float_channels_LOCAL(synth, len, 2, channels_out, + channels_off, channels_incr, + block_render_func); +} + + +#define DITHER_SIZE 48000 +#define DITHER_CHANNELS 2 + +static float rand_table[DITHER_CHANNELS][DITHER_SIZE]; + +/* Init dither table */ +static void +init_dither(void) +{ + float d, dp; + int c, i; + + for(c = 0; c < DITHER_CHANNELS; c++) + { + dp = 0; + + for(i = 0; i < DITHER_SIZE - 1; i++) + { + d = rand() / (float)RAND_MAX - 0.5f; + rand_table[c][i] = d - dp; + dp = d; + } + + rand_table[c][DITHER_SIZE - 1] = 0 - dp; + } +} + +/* A portable replacement for roundf(), seems it may actually be faster too! */ +static FLUID_INLINE int16_t +round_clip_to_i16(float x) +{ + long i; + + if(x >= 0.0f) + { + i = (long)(x + 0.5f); + + if(FLUID_UNLIKELY(i > 32767)) + { + i = 32767; + } + } + else + { + i = (long)(x - 0.5f); + + if(FLUID_UNLIKELY(i < -32768)) + { + i = -32768; + } + } + + return (int16_t)i; +} + +/** + * Synthesize a block of 16 bit audio samples to audio buffers. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param lout Array of 16 bit words to store left channel of audio + * @param loff Offset index in 'lout' for first sample + * @param lincr Increment between samples stored to 'lout' + * @param rout Array of 16 bit words to store right channel of audio + * @param roff Offset index in 'rout' for first sample + * @param rincr Increment between samples stored to 'rout' + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * Useful for storing interleaved stereo (lout = rout, loff = 0, roff = 1, + * lincr = 2, rincr = 2). + * + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. + * @note Dithering is performed when converting from internal floating point to + * 16 bit audio. + */ +int +fluid_synth_write_s16(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr) +{ + void *channels_out[2] = {lout, rout}; + int channels_off[2] = {loff, roff }; + int channels_incr[2] = {lincr, rincr }; + + return fluid_synth_write_s16_channels(synth, len, 2, channels_out, + channels_off, channels_incr); +} + +/** + * Synthesize a block of 16 bit audio samples channels to audio buffers. + * The function is convenient for audio driver to render multiple stereo + * channels pairs on multi channels audio cards (i.e 2, 4, 6, 8,.. channels). + * + * @param synth FluidSynth instance. + * @param len Count of audio frames to synthesize. + * @param channels_count Count of channels in a frame. + * must be multiple of 2 and channel_count/2 must not exceed the number + * of internal mixer buffers (synth->audio_groups) + * @param channels_out Array of channels_count pointers on 16 bit words to + * store sample channels. Modified on return. + * @param channels_off Array of channels_count offset index to add to respective pointer + * in channels_out for first sample. + * @param channels_incr Array of channels_count increment between consecutive + * samples channels. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + * + * Useful for storing: + * - interleaved channels in a unique buffer. + * - non interleaved channels in an unique buffer (or in distinct buffers). + * + * Example for interleaved 4 channels (c1, c2, c3, c4) and n samples (s1, s2,..sn) + * in a unique buffer: + * { s1:c1, s1:c2, s1:c3, s1:c4, s2:c1, s2:c2, s2:c3, s2:c4, .... + * sn:c1, sn:c2, sn:c3, sn:c4 }. + * + * @note Should only be called from synthesis thread. + * @note Reverb and Chorus are mixed to \c lout resp. \c rout. + * @note Dithering is performed when converting from internal floating point to + * 16 bit audio. + */ +int +fluid_synth_write_s16_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]) +{ + int16_t **chan_out = (int16_t **)channels_out; + int di, n, cur, size; + + /* pointers on first input mixer buffer */ + fluid_real_t *left_in; + fluid_real_t *right_in; + int bufs_in_count; /* number of stereo input buffers */ + int i; + + /* start average cpu load probe */ + double time = fluid_utime(); + float cpu_load; + + /* start profiling duration probe (if profiling is enabled) */ + fluid_profile_ref_var(prof_ref); + + /* check parameters */ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + fluid_return_val_if_fail(len >= 0, FLUID_FAILED); + fluid_return_val_if_fail(len != 0, FLUID_OK); // to avoid raising FE_DIVBYZERO below + + /* check for valid channel_count: must be multiple of 2 and + channel_count/2 must not exceed the number of internal mixer buffers + (synth->audio_groups) + */ + fluid_return_val_if_fail(!(channels_count & 1) /* must be multiple of 2 */ + && channels_count >= 2, FLUID_FAILED); + + bufs_in_count = (unsigned int)channels_count >> 1; /* channels_count/2 */ + fluid_return_val_if_fail(bufs_in_count <= synth->audio_groups, FLUID_FAILED); + + fluid_return_val_if_fail(channels_out != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_off != NULL, FLUID_FAILED); + fluid_return_val_if_fail(channels_incr != NULL, FLUID_FAILED); + + /* initialize output channels buffers on first sample position */ + i = channels_count; + do + { + i--; + chan_out[i] += channels_off[i]; + } + while(i); + + /* Conversely to fluid_synth_process(), + we want rendered audio effect mixed in internal audio dry buffers. + TRUE instructs the mixer that internal audio effects will be mixed in internal + audio dry buffers. + */ + fluid_rvoice_mixer_set_mix_fx(synth->eventhandler->mixer, TRUE); + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + + size = len; + /* synth->cur indicates if available samples are still in internal mixer buffer */ + cur = synth->cur; /* get previous sample position in internal buffer (due to prvious call) */ + di = synth->dither_index; + + do + { + /* fill up the buffers as needed */ + if(cur >= synth->curmax) + { + /* render audio (dry and effect) to internal dry buffers */ + /* always render full blocs multiple of FLUID_BUFSIZE */ + int blocksleft = (size + FLUID_BUFSIZE - 1) / FLUID_BUFSIZE; + synth->curmax = FLUID_BUFSIZE * fluid_synth_render_blocks(synth, blocksleft); + + /* get first internal mixer audio dry buffer's pointer (left and right channel) */ + fluid_rvoice_mixer_get_bufs(synth->eventhandler->mixer, &left_in, &right_in); + cur = 0; + } + + /* calculate amount of available samples */ + n = synth->curmax - cur; + + /* keep track of emitted samples */ + if(n > size) + { + n = size; + } + + size -= n; + + /* update pointers to current position */ + left_in += cur + n; + right_in += cur + n; + + /* set final cursor position */ + cur += n; + + /* reverse index */ + n = 0 - n; + + do + { + i = bufs_in_count; + do + { + /* input sample index in stereo buffer i */ + int in_idx = --i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + n; + int c = i << 1; /* channel index c to write */ + + /* write left input sample to channel sample */ + *chan_out[c] = round_clip_to_i16(left_in[in_idx] * 32766.0f + + rand_table[0][di]); + + /* write right input sample to next channel sample */ + *chan_out[c+1] = round_clip_to_i16(right_in[in_idx] * 32766.0f + + rand_table[1][di]); + /* advance output pointers */ + chan_out[c] += channels_incr[c]; + chan_out[c+1] += channels_incr[c+1]; + } + while(i); + + if(++di >= DITHER_SIZE) + { + di = 0; + } + } + while(++n < 0); + } + while(size); + + synth->cur = cur; /* save current sample position. It will be used on next call */ + synth->dither_index = di; /* keep dither buffer continuous */ + + /* save average cpu load, used by API for real time cpu load meter */ + time = fluid_utime() - time; + cpu_load = 0.5 * (fluid_atomic_float_get(&synth->cpu_load) + time * synth->sample_rate / len / 10000.0); + fluid_atomic_float_set(&synth->cpu_load, cpu_load); + + /* stop duration probe and save performance measurement (if profiling is enabled) */ + fluid_profile_write(FLUID_PROF_WRITE, prof_ref, + fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), + len); + return FLUID_OK; +} + +/** + * Converts stereo floating point sample data to signed 16 bit data with dithering. + * @param dither_index Pointer to an integer which should be initialized to 0 + * before the first call and passed unmodified to additional calls which are + * part of the same synthesis output. + * @param len Length in frames to convert + * @param lin Buffer of left audio samples to convert from + * @param rin Buffer of right audio samples to convert from + * @param lout Array of 16 bit words to store left channel of audio + * @param loff Offset index in 'lout' for first sample + * @param lincr Increment between samples stored to 'lout' + * @param rout Array of 16 bit words to store right channel of audio + * @param roff Offset index in 'rout' for first sample + * @param rincr Increment between samples stored to 'rout' + * + * @note Currently private to libfluidsynth. + */ +void +fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const float *rin, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr) +{ + int i, j, k; + int16_t *left_out = lout; + int16_t *right_out = rout; + int di = *dither_index; + fluid_profile_ref_var(prof_ref); + + for(i = 0, j = loff, k = roff; i < len; i++, j += lincr, k += rincr) + { + left_out[j] = round_clip_to_i16(lin[i] * 32766.0f + rand_table[0][di]); + right_out[k] = round_clip_to_i16(rin[i] * 32766.0f + rand_table[1][di]); + + if(++di >= DITHER_SIZE) + { + di = 0; + } + } + + *dither_index = di; /* keep dither buffer continuous */ + + fluid_profile(FLUID_PROF_WRITE, prof_ref, 0, len); +} + +static void +fluid_synth_check_finished_voices(fluid_synth_t *synth) +{ + int j; + fluid_rvoice_t *fv; + + while(NULL != (fv = fluid_rvoice_eventhandler_get_finished_voice(synth->eventhandler))) + { + for(j = 0; j < synth->polyphony; j++) + { + if(synth->voice[j]->rvoice == fv) + { + fluid_voice_unlock_rvoice(synth->voice[j]); + fluid_voice_stop(synth->voice[j]); + break; + } + else if(synth->voice[j]->overflow_rvoice == fv) + { + /* Unlock the overflow_rvoice of the voice. + Decrement the reference count of the sample owned by this + rvoice. + */ + fluid_voice_overflow_rvoice_finished(synth->voice[j]); + + /* Decrement synth active voice count. Must not be incorporated + in fluid_voice_overflow_rvoice_finished() because + fluid_voice_overflow_rvoice_finished() is called also + at synth destruction and in this case the variable should be + accessed via voice->channel->synth->active_voice_count. + And for certain voices which are not playing, the field + voice->channel is NULL. + */ + synth->active_voice_count--; + break; + } + } + } +} + +/** + * Process all waiting events in the rvoice queue. + * Make sure no (other) rendering is running in parallel when + * you call this function! + */ +void fluid_synth_process_event_queue(fluid_synth_t *synth) +{ + fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler); +} + + +/** + * Process blocks (FLUID_BUFSIZE) of audio. + * Must be called from renderer thread only! + * @return number of blocks rendered. Might (often) return less than requested + */ +static int +fluid_synth_render_blocks(fluid_synth_t *synth, int blockcount) +{ + int i, maxblocks; + fluid_profile_ref_var(prof_ref); + + /* Assign ID of synthesis thread */ +// synth->synth_thread_id = fluid_thread_get_id (); + + fluid_check_fpe("??? Just starting up ???"); + + fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler); + + /* do not render more blocks than we can store internally */ + maxblocks = fluid_rvoice_mixer_get_bufcount(synth->eventhandler->mixer); + + if(blockcount > maxblocks) + { + blockcount = maxblocks; + } + + for(i = 0; i < blockcount; i++) + { + fluid_sample_timer_process(synth); + fluid_synth_add_ticks(synth, FLUID_BUFSIZE); + + /* If events have been queued waiting for fluid_rvoice_eventhandler_dispatch_all() + * (should only happen with parallel render) stop processing and go for rendering + */ + if(fluid_rvoice_eventhandler_dispatch_count(synth->eventhandler)) + { + // Something has happened, we can't process more + blockcount = i + 1; + break; + } + } + + fluid_check_fpe("fluid_sample_timer_process"); + + blockcount = fluid_rvoice_mixer_render(synth->eventhandler->mixer, blockcount); + + /* Testcase, that provokes a denormal floating point error */ +#if 0 + { + float num = 1; + + while(num != 0) + { + num *= 0.5; + }; + }; +#endif + fluid_check_fpe("??? Remainder of synth_one_block ???"); + fluid_profile(FLUID_PROF_ONE_BLOCK, prof_ref, + fluid_rvoice_mixer_get_active_voices(synth->eventhandler->mixer), + blockcount * FLUID_BUFSIZE); + return blockcount; +} + +/* + * Handler for synth.reverb.* and synth.chorus.* double settings. + */ +static void fluid_synth_handle_reverb_chorus_num(void *data, const char *name, double value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + if(FLUID_STRCMP(name, "synth.reverb.room-size") == 0) + { + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, value); + } + else if(FLUID_STRCMP(name, "synth.reverb.damp") == 0) + { + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, value); + } + else if(FLUID_STRCMP(name, "synth.reverb.width") == 0) + { + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, value); + } + else if(FLUID_STRCMP(name, "synth.reverb.level") == 0) + { + fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.depth") == 0) + { + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.speed") == 0) + { + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.level") == 0) + { + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, value); + } +} + +/* + * Handler for synth.reverb.* and synth.chorus.* integer settings. + */ +static void fluid_synth_handle_reverb_chorus_int(void *data, const char *name, int value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + if(FLUID_STRCMP(name, "synth.reverb.active") == 0) + { + fluid_synth_reverb_on(synth, -1, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.active") == 0) + { + fluid_synth_chorus_on(synth, -1, value); + } + else if(FLUID_STRCMP(name, "synth.chorus.nr") == 0) + { + fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, (double)value); + } +} + +/* + * Handler for synth.overflow.* settings. + */ +static void fluid_synth_handle_overflow(void *data, const char *name, double value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + fluid_return_if_fail(synth != NULL); + + fluid_synth_api_enter(synth); + + if(FLUID_STRCMP(name, "synth.overflow.percussion") == 0) + { + synth->overflow.percussion = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.released") == 0) + { + synth->overflow.released = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.sustained") == 0) + { + synth->overflow.sustained = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.volume") == 0) + { + synth->overflow.volume = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.age") == 0) + { + synth->overflow.age = value; + } + else if(FLUID_STRCMP(name, "synth.overflow.important") == 0) + { + synth->overflow.important = value; + } + + fluid_synth_api_exit(synth); +} + +/* Selects a voice for killing. */ +static fluid_voice_t * +fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t *synth) +{ + int i; + float best_prio = OVERFLOW_PRIO_CANNOT_KILL - 1; + float this_voice_prio; + fluid_voice_t *voice; + int best_voice_index = -1; + unsigned int ticks = fluid_synth_get_ticks(synth); + + for(i = 0; i < synth->polyphony; i++) + { + + voice = synth->voice[i]; + + /* safeguard against an available voice. */ + if(_AVAILABLE(voice)) + { + return voice; + } + + this_voice_prio = fluid_voice_get_overflow_prio(voice, &synth->overflow, + ticks); + + /* check if this voice has less priority than the previous candidate. */ + if(this_voice_prio < best_prio) + { + best_voice_index = i; + best_prio = this_voice_prio; + } + } + + if(best_voice_index < 0) + { + return NULL; + } + + voice = synth->voice[best_voice_index]; + FLUID_LOG(FLUID_DBG, "Killing voice %d, index %d, chan %d, key %d ", + fluid_voice_get_id(voice), best_voice_index, fluid_voice_get_channel(voice), fluid_voice_get_key(voice)); + fluid_voice_off(voice); + + return voice; +} + + +/** + * Allocate a synthesis voice. + * @param synth FluidSynth instance + * @param sample Sample to assign to the voice + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number for the voice (0-127) + * @param vel MIDI velocity for the voice (0-127) + * @return Allocated synthesis voice or NULL on error + * + * This function is called by a SoundFont's preset in response to a noteon event. + * The returned voice comes with default modulators and generators. + * A single noteon event may create any number of voices, when the preset is layered. + * + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon method. + */ +fluid_voice_t * +fluid_synth_alloc_voice(fluid_synth_t *synth, fluid_sample_t *sample, + int chan, int key, int vel) +{ + fluid_return_val_if_fail(sample != NULL, NULL); + fluid_return_val_if_fail(sample->data != NULL, NULL); + FLUID_API_ENTRY_CHAN(NULL); + FLUID_API_RETURN(fluid_synth_alloc_voice_LOCAL(synth, sample, chan, key, vel, NULL)); + +} + +fluid_voice_t * +fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range) +{ + int i, k; + fluid_voice_t *voice = NULL; + fluid_channel_t *channel = NULL; + unsigned int ticks; + + /* check if there's an available synthesis process */ + for(i = 0; i < synth->polyphony; i++) + { + if(_AVAILABLE(synth->voice[i])) + { + voice = synth->voice[i]; + break; + } + } + + /* No success yet? Then stop a running voice. */ + if(voice == NULL) + { + FLUID_LOG(FLUID_DBG, "Polyphony exceeded, trying to kill a voice"); + voice = fluid_synth_free_voice_by_kill_LOCAL(synth); + } + + if(voice == NULL) + { + FLUID_LOG(FLUID_WARN, "Failed to allocate a synthesis process. (chan=%d,key=%d)", chan, key); + return NULL; + } + + ticks = fluid_synth_get_ticks(synth); + + if(synth->verbose) + { + k = 0; + + for(i = 0; i < synth->polyphony; i++) + { + if(!_AVAILABLE(synth->voice[i])) + { + k++; + } + } + + FLUID_LOG(FLUID_INFO, "noteon\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d", + chan, key, vel, synth->storeid, + (float) ticks / 44100.0f, + (fluid_curtime() - synth->start) / 1000.0f, + 0.0f, + k); + } + + channel = synth->channel[chan]; + + if(fluid_voice_init(voice, sample, zone_range, channel, key, vel, + synth->storeid, ticks, synth->gain) != FLUID_OK) + { + FLUID_LOG(FLUID_WARN, "Failed to initialize voice"); + return NULL; + } + + /* add the default modulators to the synthesis process. */ + /* custom_breath2att_modulator is not a default modulator specified in SF + it is intended to replace default_vel2att_mod for this channel on demand using + API fluid_synth_set_breath_mode() or shell command setbreathmode for this channel. + */ + { + int mono = fluid_channel_is_playing_mono(channel); + fluid_mod_t *default_mod = synth->default_mod; + + while(default_mod != NULL) + { + if( + /* See if default_mod is the velocity_to_attenuation modulator */ + fluid_mod_test_identity(default_mod, &default_vel2att_mod) && + // See if a replacement by custom_breath2att_modulator has been demanded + // for this channel + ((!mono && (channel->mode & FLUID_CHANNEL_BREATH_POLY)) || + (mono && (channel->mode & FLUID_CHANNEL_BREATH_MONO))) + ) + { + // Replacement of default_vel2att modulator by custom_breath2att_modulator + fluid_voice_add_mod_local(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT, 0); + } + else + { + fluid_voice_add_mod_local(voice, default_mod, FLUID_VOICE_DEFAULT, 0); + } + + // Next default modulator to add to the voice + default_mod = default_mod->next; + } + } + + return voice; +} + +/* Kill all voices on a given channel, which have the same exclusive class + * generator as new_voice. + */ +static void +fluid_synth_kill_by_exclusive_class_LOCAL(fluid_synth_t *synth, + fluid_voice_t *new_voice) +{ + int excl_class = fluid_voice_gen_value(new_voice, GEN_EXCLUSIVECLASS); + int i; + + /* Excl. class 0: No exclusive class */ + if(excl_class == 0) + { + return; + } + + /* Kill all notes on the same channel with the same exclusive class */ + for(i = 0; i < synth->polyphony; i++) + { + fluid_voice_t *existing_voice = synth->voice[i]; + + /* If voice is playing, on the same channel, has same exclusive + * class and is not part of the same noteon event (voice group), then kill it */ + + if(fluid_voice_is_playing(existing_voice) + && fluid_voice_get_channel(existing_voice) == fluid_voice_get_channel(new_voice) + && fluid_voice_gen_value(existing_voice, GEN_EXCLUSIVECLASS) == excl_class + && fluid_voice_get_id(existing_voice) != fluid_voice_get_id(new_voice)) + { + fluid_voice_kill_excl(existing_voice); + } + } +} + +/** + * Activate a voice previously allocated with fluid_synth_alloc_voice(). + * @param synth FluidSynth instance + * @param voice Voice to activate + * + * This function is called by a SoundFont's preset in response to a noteon + * event. Exclusive classes are processed here. + * + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon method. + */ +void +fluid_synth_start_voice(fluid_synth_t *synth, fluid_voice_t *voice) +{ + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(voice != NULL); +// fluid_return_if_fail (fluid_synth_is_synth_thread (synth)); + fluid_synth_api_enter(synth); + + /* Find the exclusive class of this voice. If set, kill all voices + * that match the exclusive class and are younger than the first + * voice process created by this noteon event. */ + fluid_synth_kill_by_exclusive_class_LOCAL(synth, voice); + + fluid_voice_start(voice); /* Start the new voice */ + fluid_voice_lock_rvoice(voice); + fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice); + fluid_synth_api_exit(synth); +} + +/** + * Add a SoundFont loader to the synth. This function takes ownership of \c loader + * and frees it automatically upon \c synth destruction. + * @param synth FluidSynth instance + * @param loader Loader API structure + * + * SoundFont loaders are used to add custom instrument loading to FluidSynth. + * The caller supplied functions for loading files, allocating presets, + * retrieving information on them and synthesizing note-on events. Using this + * method even non SoundFont instruments can be synthesized, although limited + * to the SoundFont synthesis model. + * + * @note Should only be called before any SoundFont files are loaded. + */ +void +fluid_synth_add_sfloader(fluid_synth_t *synth, fluid_sfloader_t *loader) +{ + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(loader != NULL); + fluid_synth_api_enter(synth); + + /* Test if sfont is already loaded */ + if(synth->sfont == NULL) + { + synth->loaders = fluid_list_prepend(synth->loaders, loader); + } + + fluid_synth_api_exit(synth); +} + +/** + * Load a SoundFont file (filename is interpreted by SoundFont loaders). + * The newly loaded SoundFont will be put on top of the SoundFont + * stack. Presets are searched starting from the SoundFont on the + * top of the stack, working the way down the stack until a preset is found. + * + * @param synth FluidSynth instance + * @param filename File to load + * @param reset_presets TRUE to re-assign presets for all MIDI channels (equivalent to calling fluid_synth_program_reset()) + * @return SoundFont ID on success, #FLUID_FAILED on error + * + * @note Since FluidSynth 2.2.0 @c filename is treated as an UTF8 encoded string on Windows. FluidSynth will convert it + * to wide-char internally and then pass it to _wfopen(). Before FluidSynth 2.2.0, @c filename was treated as ANSI string + * on Windows. All other platforms directly pass it to fopen() without any conversion (usually, UTF8 is accepted). + */ +int +fluid_synth_sfload(fluid_synth_t *synth, const char *filename, int reset_presets) +{ + fluid_sfont_t *sfont; + fluid_list_t *list; + fluid_sfloader_t *loader; + int sfont_id; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(filename != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + sfont_id = synth->sfont_id; + + if(++sfont_id != FLUID_FAILED) + { + /* MT NOTE: Loaders list should not change. */ + + for(list = synth->loaders; list; list = fluid_list_next(list)) + { + loader = (fluid_sfloader_t *) fluid_list_get(list); + + sfont = fluid_sfloader_load(loader, filename); + + if(sfont != NULL) + { + sfont->refcount++; + synth->sfont_id = sfont->id = sfont_id; + + synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */ + + /* reset the presets for all channels if requested */ + if(reset_presets) + { + fluid_synth_program_reset(synth); + } + + FLUID_API_RETURN(sfont_id); + } + } + } + + FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); + FLUID_API_RETURN(FLUID_FAILED); +} + +/** + * Schedule a SoundFont for unloading. + * + * If the SoundFont isn't used anymore by any playing voices, it will be unloaded immediately. + * + * If any samples of the given SoundFont are still required by active voices, + * the SoundFont will be unloaded in a lazy manner, once those voices have finished synthesizing. + * If you call delete_fluid_synth(), all voices will be destroyed and the SoundFont + * will be unloaded in any case. + * Once this function returned, fluid_synth_sfcount() and similar functions will behave as if + * the SoundFont has already been unloaded, even though the lazy-unloading is still pending. + * + * @note This lazy-unloading mechanism was broken between FluidSynth 1.1.4 and 2.1.5 . As a + * consequence, SoundFonts scheduled for lazy-unloading may be never freed under certain + * conditions. Calling delete_fluid_synth() does not recover this situation either. + * + * @param synth FluidSynth instance + * @param id ID of SoundFont to unload + * @param reset_presets TRUE to re-assign presets for all MIDI channels + * @return #FLUID_OK if the given @p id was found, #FLUID_FAILED otherwise. + */ +int +fluid_synth_sfunload(fluid_synth_t *synth, int id, int reset_presets) +{ + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + /* remove the SoundFont from the list */ + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == id) + { + synth->sfont = fluid_list_remove(synth->sfont, sfont); + break; + } + } + + if(!list) + { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); + FLUID_API_RETURN(FLUID_FAILED); + } + + /* reset the presets for all channels (SoundFont will be freed when there are no more references) */ + if(reset_presets) + { + fluid_synth_program_reset(synth); + } + else + { + fluid_synth_update_presets(synth); + } + + /* -- Remove synth->sfont list's reference to SoundFont */ + fluid_synth_sfont_unref(synth, sfont); + + FLUID_API_RETURN(FLUID_OK); +} + +/* Unref a SoundFont and destroy if no more references */ +void +fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont) +{ + fluid_return_if_fail(sfont != NULL); /* Shouldn't happen, programming error if so */ + + sfont->refcount--; /* -- Remove the sfont list's reference */ + + if(sfont->refcount == 0) /* No more references? - Attempt delete */ + { + if(fluid_sfont_delete_internal(sfont) == 0) /* SoundFont loader can block SoundFont unload */ + { + FLUID_LOG(FLUID_DBG, "Unloaded SoundFont"); + } /* spin off a timer thread to unload the sfont later (SoundFont loader blocked unload) */ + else + { + fluid_timer_t* timer = new_fluid_timer(100, fluid_synth_sfunload_callback, sfont, TRUE, FALSE, FALSE); + synth->fonts_to_be_unloaded = fluid_list_prepend(synth->fonts_to_be_unloaded, timer); + } + } +} + +/* Callback to continually attempt to unload a SoundFont, + * only if a SoundFont loader blocked the unload operation */ +static int +fluid_synth_sfunload_callback(void *data, unsigned int msec) +{ + fluid_sfont_t *sfont = data; + + if(fluid_sfont_delete_internal(sfont) == 0) + { + FLUID_LOG(FLUID_DBG, "Unloaded SoundFont"); + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * Reload a SoundFont. The SoundFont retains its ID and index on the SoundFont stack. + * @param synth FluidSynth instance + * @param id ID of SoundFont to reload + * @return SoundFont ID on success, #FLUID_FAILED on error + */ +int +fluid_synth_sfreload(fluid_synth_t *synth, int id) +{ + char *filename = NULL; + fluid_sfont_t *sfont; + fluid_sfloader_t *loader; + fluid_list_t *list; + int index, ret = FLUID_FAILED; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + /* Search for SoundFont and get its index */ + for(list = synth->sfont, index = 0; list; list = fluid_list_next(list), index++) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == id) + { + break; + } + } + + if(!list) + { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", id); + goto exit; + } + + /* keep a copy of the SoundFont's filename */ + filename = FLUID_STRDUP(fluid_sfont_get_name(sfont)); + + if(filename == NULL || fluid_synth_sfunload(synth, id, FALSE) != FLUID_OK) + { + goto exit; + } + + /* MT Note: SoundFont loader list will not change */ + + for(list = synth->loaders; list; list = fluid_list_next(list)) + { + loader = (fluid_sfloader_t *) fluid_list_get(list); + + sfont = fluid_sfloader_load(loader, filename); + + if(sfont != NULL) + { + sfont->id = id; + sfont->refcount++; + + synth->sfont = fluid_list_insert_at(synth->sfont, index, sfont); /* insert the sfont at the same index */ + + /* reset the presets for all channels */ + fluid_synth_update_presets(synth); + ret = id; + goto exit; + } + } + + FLUID_LOG(FLUID_ERR, "Failed to load SoundFont \"%s\"", filename); + +exit: + FLUID_FREE(filename); + FLUID_API_RETURN(ret); +} + +/** + * Add a SoundFont. The SoundFont will be added to the top of the SoundFont stack. + * @param synth FluidSynth instance + * @param sfont SoundFont to add + * @return New assigned SoundFont ID or #FLUID_FAILED on error + */ +int +fluid_synth_add_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont) +{ + int sfont_id; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + sfont_id = synth->sfont_id; + + if(++sfont_id != FLUID_FAILED) + { + synth->sfont_id = sfont->id = sfont_id; + synth->sfont = fluid_list_prepend(synth->sfont, sfont); /* prepend to list */ + + /* reset the presets for all channels */ + fluid_synth_program_reset(synth); + } + + FLUID_API_RETURN(sfont_id); +} + +/** + * Remove a SoundFont from the SoundFont stack without deleting it. + * @param synth FluidSynth instance + * @param sfont SoundFont to remove + * @return #FLUID_OK if \c sfont successfully removed, #FLUID_FAILED otherwise + * + * SoundFont is not freed and is left as the responsibility of the caller. + * + * @note The SoundFont should only be freed after there are no presets + * referencing it. This can only be ensured by the SoundFont loader and + * therefore this function should not normally be used. + */ +int +fluid_synth_remove_sfont(fluid_synth_t *synth, fluid_sfont_t *sfont) +{ + fluid_sfont_t *sfont_tmp; + fluid_list_t *list; + int ret = FLUID_FAILED; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(sfont != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + /* remove the SoundFont from the list */ + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont_tmp = fluid_list_get(list); + + if(sfont_tmp == sfont) + { + synth->sfont = fluid_list_remove(synth->sfont, sfont_tmp); + ret = FLUID_OK; + break; + } + } + + /* reset the presets for all channels */ + fluid_synth_program_reset(synth); + + FLUID_API_RETURN(ret); +} + +/** + * Count number of loaded SoundFont files. + * @param synth FluidSynth instance + * @return Count of loaded SoundFont files. + */ +int +fluid_synth_sfcount(fluid_synth_t *synth) +{ + int count; + + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + count = fluid_list_size(synth->sfont); + FLUID_API_RETURN(count); +} + +/** + * Get SoundFont by index. + * @param synth FluidSynth instance + * @param num SoundFont index on the stack (starting from 0 for top of stack). + * @return SoundFont instance or NULL if invalid index + * + * @note Caller should be certain that SoundFont is not deleted (unloaded) for + * the duration of use of the returned pointer. + */ +fluid_sfont_t * +fluid_synth_get_sfont(fluid_synth_t *synth, unsigned int num) +{ + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, NULL); + fluid_synth_api_enter(synth); + list = fluid_list_nth(synth->sfont, num); + + if(list) + { + sfont = fluid_list_get(list); + } + + FLUID_API_RETURN(sfont); +} + +/** + * Get SoundFont by ID. + * @param synth FluidSynth instance + * @param id SoundFont ID + * @return SoundFont instance or NULL if invalid ID + * + * @note Caller should be certain that SoundFont is not deleted (unloaded) for + * the duration of use of the returned pointer. + */ +fluid_sfont_t * +fluid_synth_get_sfont_by_id(fluid_synth_t *synth, int id) +{ + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, NULL); + fluid_synth_api_enter(synth); + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == id) + { + break; + } + } + + FLUID_API_RETURN(list ? sfont : NULL); +} + +/** + * Get SoundFont by name. + * @param synth FluidSynth instance + * @param name Name of SoundFont + * @return SoundFont instance or NULL if invalid name + * @since 1.1.0 + * + * @note Caller should be certain that SoundFont is not deleted (unloaded) for + * the duration of use of the returned pointer. + */ +fluid_sfont_t * +fluid_synth_get_sfont_by_name(fluid_synth_t *synth, const char *name) +{ + fluid_sfont_t *sfont = NULL; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, NULL); + fluid_return_val_if_fail(name != NULL, NULL); + fluid_synth_api_enter(synth); + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(FLUID_STRCMP(fluid_sfont_get_name(sfont), name) == 0) + { + break; + } + } + + FLUID_API_RETURN(list ? sfont : NULL); +} + +/** + * Get active preset on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @return Preset or NULL if no preset active on \c chan + * + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon methods. Not thread safe otherwise. + */ +fluid_preset_t * +fluid_synth_get_channel_preset(fluid_synth_t *synth, int chan) +{ + fluid_preset_t *result; + fluid_channel_t *channel; + FLUID_API_ENTRY_CHAN(NULL); + + channel = synth->channel[chan]; + result = channel->preset; + fluid_synth_api_exit(synth); + return result; +} + +/** + * Get list of currently playing voices. + * @param synth FluidSynth instance + * @param buf Array to store voices to (NULL terminated if not filled completely) + * @param bufsize Count of indexes in buf + * @param id Voice ID to search for or < 0 to return list of all playing voices + * + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon methods. Voices are only guaranteed to remain + * unchanged until next synthesis process iteration. + */ +void +fluid_synth_get_voicelist(fluid_synth_t *synth, fluid_voice_t *buf[], int bufsize, + int id) +{ + int count = 0; + int i; + + fluid_return_if_fail(synth != NULL); + fluid_return_if_fail(buf != NULL); + fluid_synth_api_enter(synth); + + for(i = 0; i < synth->polyphony && count < bufsize; i++) + { + fluid_voice_t *voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice) && (id < 0 || (int)voice->id == id)) + { + buf[count++] = voice; + } + } + + if(count < bufsize) + { + buf[count] = NULL; + } + + fluid_synth_api_exit(synth); +} + +/** + * Enable or disable reverb effect. + * @param synth FluidSynth instance + * @param on TRUE to enable chorus, FALSE to disable + * @deprecated Use fluid_synth_reverb_on() instead. + */ +void +fluid_synth_set_reverb_on(fluid_synth_t *synth, int on) +{ + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + synth->with_reverb = (on != 0); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_reverb_enabled, + on != 0, 0.0f); + fluid_synth_api_exit(synth); +} + +/** + * Enable or disable reverb on one fx group unit. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param on TRUE to enable reverb, FALSE to disable + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_reverb_on(fluid_synth_t *synth, int fx_group, int on) +{ + int ret; + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if(fx_group < 0 ) + { + synth->with_reverb = (on != 0); + } + + param[0].i = fx_group; + param[1].i = on; + ret = fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_reverb_enable, + synth->eventhandler->mixer, + param); + + FLUID_API_RETURN(ret); +} + +/** + * Activate a reverb preset. + * @param synth FluidSynth instance + * @param num Reverb preset number + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note Currently private to libfluidsynth. + */ +int +fluid_synth_set_reverb_preset(fluid_synth_t *synth, unsigned int num) +{ + double values[FLUID_REVERB_PARAM_LAST]; + + fluid_return_val_if_fail( + num < FLUID_N_ELEMENTS(revmodel_preset), + FLUID_FAILED + ); + + values[FLUID_REVERB_ROOMSIZE] = revmodel_preset[num].roomsize; + values[FLUID_REVERB_DAMP] = revmodel_preset[num].damp; + values[FLUID_REVERB_WIDTH] = revmodel_preset[num].width; + values[FLUID_REVERB_LEVEL] = revmodel_preset[num].level; + fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); + return FLUID_OK; +} + +/** + * Set reverb parameters to all groups. + * + * @param synth FluidSynth instance + * @param roomsize Reverb room size value (0.0-1.0) + * @param damping Reverb damping value (0.0-1.0) + * @param width Reverb width value (0.0-100.0) + * @param level Reverb level value (0.0-1.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use the individual reverb setter functions in new code instead. + */ +int +fluid_synth_set_reverb(fluid_synth_t *synth, double roomsize, double damping, + double width, double level) +{ + double values[FLUID_REVERB_PARAM_LAST]; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + values[FLUID_REVERB_ROOMSIZE] = roomsize; + values[FLUID_REVERB_DAMP] = damping; + values[FLUID_REVERB_WIDTH] = width; + values[FLUID_REVERB_LEVEL] = level; + return fluid_synth_set_reverb_full(synth, -1, FLUID_REVMODEL_SET_ALL, values); +} + +/** + * Set reverb roomsize of all groups. + * + * @param synth FluidSynth instance + * @param roomsize Reverb room size value (0.0-1.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_roomsize() in new code instead. + */ +int fluid_synth_set_reverb_roomsize(fluid_synth_t *synth, double roomsize) +{ + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_ROOMSIZE, roomsize); +} + +/** + * Set reverb damping of all groups. + * + * @param synth FluidSynth instance + * @param damping Reverb damping value (0.0-1.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_damp() in new code instead. + */ +int fluid_synth_set_reverb_damp(fluid_synth_t *synth, double damping) +{ + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_DAMP, damping); +} + +/** + * Set reverb width of all groups. + * + * @param synth FluidSynth instance + * @param width Reverb width value (0.0-100.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_width() in new code instead. + */ +int fluid_synth_set_reverb_width(fluid_synth_t *synth, double width) +{ + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_WIDTH, width); +} + +/** + * Set reverb level of all groups. + * + * @param synth FluidSynth instance + * @param level Reverb level value (0.0-1.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_reverb_group_level() in new code instead. + */ +int fluid_synth_set_reverb_level(fluid_synth_t *synth, double level) +{ + return fluid_synth_reverb_set_param(synth, -1, FLUID_REVERB_LEVEL, level); +} + +/** + * Set reverb roomsize to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param roomsize roomsize value to set. Must be in the range indicated by + * synth.reverb.room-size setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, + double roomsize) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize); +} + +/** + * Set reverb damp to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param damping damping value to set. Must be in the range indicated by + * synth.reverb.damp setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_set_reverb_group_damp(fluid_synth_t *synth, int fx_group, + double damping) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_DAMP, damping); +} + +/** + * Set reverb width to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param width width value to set. Must be in the range indicated by + * synth.reverb.width setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_set_reverb_group_width(fluid_synth_t *synth, int fx_group, + double width) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_WIDTH, width); +} + +/** + * Set reverb level to one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param level output level to set. Must be in the range indicated by + * synth.reverb.level setting. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_set_reverb_group_level(fluid_synth_t *synth, int fx_group, + double level) +{ + return fluid_synth_reverb_set_param(synth, fx_group, FLUID_REVERB_LEVEL, level); +} + +/** + * Set one reverb parameter to one fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param enum indicating the parameter to set (#fluid_reverb_param). + * FLUID_REVERB_ROOMSIZE, roomsize Reverb room size value (0.0-1.0) + * FLUID_REVERB_DAMP, reverb damping value (0.0-1.0) + * FLUID_REVERB_WIDTH, reverb width value (0.0-100.0) + * FLUID_REVERB_LEVEL, reverb level value (0.0-1.0) + * @param value, parameter value + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_reverb_set_param(fluid_synth_t *synth, int fx_group, + int param, double value) +{ + int ret; + double values[FLUID_REVERB_PARAM_LAST] = {0.0}; + static const char *name[FLUID_REVERB_PARAM_LAST] = + { + "synth.reverb.room-size", "synth.reverb.damp", + "synth.reverb.width", "synth.reverb.level" + }; + + double min; /* minimum value */ + double max; /* maximum value */ + + /* check parameters */ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* check if reverb value is in max min range */ + fluid_settings_getnum_range(synth->settings, name[param], &min, &max); + if(value < min || value > max) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* set the value */ + values[param] = value; + ret = fluid_synth_set_reverb_full(synth, fx_group, FLUID_REVPARAM_TO_SETFLAG(param), values); + FLUID_API_RETURN(ret); +} + +int +fluid_synth_set_reverb_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + /* if non of the flags is set, fail */ + fluid_return_val_if_fail(set & FLUID_REVMODEL_SET_ALL, FLUID_FAILED); + + /* fx group shadow values are set here so that they will be returned if queried */ + fluid_rvoice_mixer_set_reverb_full(synth->eventhandler->mixer, fx_group, set, + values); + + /* Synth shadow values are set here so that they will be returned if queried */ + if (fx_group < 0) + { + int i; + for(i = 0; i < FLUID_REVERB_PARAM_LAST; i++) + { + if(set & FLUID_REVPARAM_TO_SETFLAG(i)) + { + synth->reverb_param[i] = values[i]; + } + } + } + + param[0].i = fx_group; + param[1].i = set; + param[2].real = values[FLUID_REVERB_ROOMSIZE]; + param[3].real = values[FLUID_REVERB_DAMP]; + param[4].real = values[FLUID_REVERB_WIDTH]; + param[5].real = values[FLUID_REVERB_LEVEL]; + /* finally enqueue an rvoice event to the mixer to actual update reverb */ + return fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_set_reverb_params, + synth->eventhandler->mixer, + param); +} + +/** + * Get reverb room size of all fx groups. + * @param synth FluidSynth instance + * @return Reverb room size (0.0-1.2) + * @deprecated Use fluid_synth_get_reverb_group_roomsize() in new code instead. + */ +double +fluid_synth_get_reverb_roomsize(fluid_synth_t *synth) +{ + double roomsize = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_ROOMSIZE, &roomsize); + return roomsize; +} + +/** + * Get reverb damping of all fx groups. + * @param synth FluidSynth instance + * @return Reverb damping value (0.0-1.0) + * @deprecated Use fluid_synth_get_reverb_group_damp() in new code instead. + */ +double +fluid_synth_get_reverb_damp(fluid_synth_t *synth) +{ + double damp = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_DAMP, &damp); + return damp; +} + +/** + * Get reverb level of all fx groups. + * @param synth FluidSynth instance + * @return Reverb level value (0.0-1.0) + * @deprecated Use fluid_synth_get_reverb_group_level() in new code instead. + */ +double +fluid_synth_get_reverb_level(fluid_synth_t *synth) +{ + double level = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_LEVEL, &level); + return level; +} + +/** + * Get reverb width of all fx groups. + * @param synth FluidSynth instance + * @return Reverb width value (0.0-100.0) + * @deprecated Use fluid_synth_get_reverb_group_width() in new code instead. + */ +double +fluid_synth_get_reverb_width(fluid_synth_t *synth) +{ + double width = 0.0; + fluid_synth_reverb_get_param(synth, -1, FLUID_REVERB_WIDTH, &width); + return width; +} + +/** + * get reverb roomsize of one or all groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param roomsize valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_get_reverb_group_roomsize(fluid_synth_t *synth, int fx_group, + double *roomsize) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_ROOMSIZE, roomsize); +} + +/** + * get reverb damp of one or all groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param damping valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_get_reverb_group_damp(fluid_synth_t *synth, int fx_group, + double *damping) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_DAMP, damping); +} + +/** + * get reverb width of one or all groups + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param width valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_get_reverb_group_width(fluid_synth_t *synth, int fx_group, + double *width) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_WIDTH, width); +} + +/** + * get reverb level of one or all groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param level valid pointer on the value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int fluid_synth_get_reverb_group_level(fluid_synth_t *synth, int fx_group, + double *level) +{ + return fluid_synth_reverb_get_param(synth, fx_group, FLUID_REVERB_LEVEL, level); +} + + +/** + * Get one reverb parameter value of one fx groups. + * @param synth FluidSynth instance + * @param fx_group index of the fx group to get parameter value from. + * Must be in the range -1 to synth->effects_groups-1. If -1 get the + * parameter common to all fx groups. + * @param enum indicating the parameter to get (#fluid_reverb_param). + * FLUID_REVERB_ROOMSIZE, reverb room size value. + * FLUID_REVERB_DAMP, reverb damping value. + * FLUID_REVERB_WIDTH, reverb width value. + * FLUID_REVERB_LEVEL, reverb level value. + * @param value pointer on the value to return. + * @return FLUID_OK if success, FLUID_FAILED otherwise. + */ +static int fluid_synth_reverb_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value) +{ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail((param >= 0) && (param < FLUID_REVERB_PARAM_LAST), FLUID_FAILED); + fluid_return_val_if_fail(value != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if (fx_group < 0) + { + /* return reverb param common to all fx groups */ + *value = synth->reverb_param[param]; + } + else + { + /* return reverb param of fx group at index fx_group */ + *value = fluid_rvoice_mixer_reverb_get_param(synth->eventhandler->mixer, + fx_group, param); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Enable or disable all chorus groups. + * @param synth FluidSynth instance + * @param on TRUE to enable chorus, FALSE to disable + * @deprecated Use fluid_synth_chorus_on() in new code instead. + */ +void +fluid_synth_set_chorus_on(fluid_synth_t *synth, int on) +{ + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + + synth->with_chorus = (on != 0); + fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_chorus_enabled, + on != 0, 0.0f); + fluid_synth_api_exit(synth); +} + +/** + * Enable or disable chorus on one or all groups. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all fx groups. + * @param on TRUE to enable chorus, FALSE to disable + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_chorus_on(fluid_synth_t *synth, int fx_group, int on) +{ + int ret; + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if(fx_group < 0 ) + { + synth->with_chorus = (on != 0); + } + + param[0].i = fx_group; + param[1].i = on; + ret = fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_chorus_enable, + synth->eventhandler->mixer, + param); + + FLUID_API_RETURN(ret); +} + +/** + * Set chorus parameters to all fx groups. + * Keep in mind, that the needed CPU time is proportional to 'nr'. + * @param synth FluidSynth instance + * @param nr Chorus voice count (0-99, CPU time consumption proportional to + * this value) + * @param level Chorus level (0.0-10.0) + * @param speed Chorus speed in Hz (0.1-5.0) + * @param depth_ms Chorus depth (max value depends on synth sample-rate, + * 0.0-21.0 is safe for sample-rate values up to 96KHz) + * @param type Chorus waveform type (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use the individual chorus setter functions in new code instead. + * + * Keep in mind, that the needed CPU time is proportional to 'nr'. + */ +int fluid_synth_set_chorus(fluid_synth_t *synth, int nr, double level, + double speed, double depth_ms, int type) +{ + double values[FLUID_CHORUS_PARAM_LAST]; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + values[FLUID_CHORUS_NR] = nr; + values[FLUID_CHORUS_LEVEL] = level; + values[FLUID_CHORUS_SPEED] = speed; + values[FLUID_CHORUS_DEPTH] = depth_ms; + values[FLUID_CHORUS_TYPE] = type; + return fluid_synth_set_chorus_full(synth, -1, FLUID_CHORUS_SET_ALL, values); +} + +/** + * Set the chorus voice count of all groups. + * + * @param synth FluidSynth instance + * @param nr Chorus voice count (0-99, CPU time consumption proportional to + * this value) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_nr() in new code instead. + */ +int fluid_synth_set_chorus_nr(fluid_synth_t *synth, int nr) +{ + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_NR, nr); +} + +/** + * Set the chorus level of all groups. + * + * @param synth FluidSynth instance + * @param level Chorus level (0.0-10.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_level() in new code instead. + */ +int fluid_synth_set_chorus_level(fluid_synth_t *synth, double level) +{ + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_LEVEL, level); +} + +/** + * Set the chorus speed of all groups. + * + * @param synth FluidSynth instance + * @param speed Chorus speed in Hz (0.1-5.0) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_level() in new code instead. + */ +int fluid_synth_set_chorus_speed(fluid_synth_t *synth, double speed) +{ + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_SPEED, speed); +} + +/** + * Set the chorus depth of all groups. + * + * @param synth FluidSynth instance + * @param depth_ms Chorus depth (max value depends on synth sample-rate, + * 0.0-21.0 is safe for sample-rate values up to 96KHz) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_depth() in new code instead. + */ +int fluid_synth_set_chorus_depth(fluid_synth_t *synth, double depth_ms) +{ + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_DEPTH, depth_ms); +} + +/** + * Set the chorus type of all groups. + * + * @param synth FluidSynth instance + * @param type Chorus waveform type (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @deprecated Use fluid_synth_set_chorus_group_type() in new code instead. + */ +int fluid_synth_set_chorus_type(fluid_synth_t *synth, int type) +{ + return fluid_synth_chorus_set_param(synth, -1, FLUID_CHORUS_TYPE, type); +} + +/** + * Set chorus voice count nr to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param nr Voice count to set. Must be in the range indicated by \setting{synth_chorus_nr} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_nr(fluid_synth_t *synth, int fx_group, int nr) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_NR, (double)nr); +} + +/** + * Set chorus output level to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param level Output level to set. Must be in the range indicated by \setting{synth_chorus_level} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_level(fluid_synth_t *synth, int fx_group, double level) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_LEVEL, level); +} + +/** + * Set chorus lfo speed to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param speed Lfo speed to set. Must be in the range indicated by \setting{synth_chorus_speed} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_speed(fluid_synth_t *synth, int fx_group, double speed) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_SPEED, speed); +} + +/** + * Set chorus lfo depth to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param depth_ms lfo depth to set. Must be in the range indicated by \setting{synth_chorus_depth} + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_depth(fluid_synth_t *synth, int fx_group, double depth_ms) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms); +} + +/** + * Set chorus lfo waveform type to one or all chorus groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param type Lfo waveform type to set. (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_set_chorus_group_type(fluid_synth_t *synth, int fx_group, int type) +{ + return fluid_synth_chorus_set_param(synth, fx_group, FLUID_CHORUS_TYPE, (double)type); +} + +/** + * Set one chorus parameter to one fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter will be applied to all groups. + * @param enum indicating the parameter to set (#fluid_chorus_param). + * FLUID_CHORUS_NR, chorus voice count (0-99, CPU time consumption proportional to + * this value). + * FLUID_CHORUS_LEVEL, chorus level (0.0-10.0). + * FLUID_CHORUS_SPEED, chorus speed in Hz (0.1-5.0). + * FLUID_CHORUS_DEPTH, chorus depth (max value depends on synth sample-rate, + * 0.0-21.0 is safe for sample-rate values up to 96KHz). + * FLUID_CHORUS_TYPE, chorus waveform type (#fluid_chorus_mod) + * @param value, parameter value + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_chorus_set_param(fluid_synth_t *synth, int fx_group, int param, + double value) +{ + int ret; + double values[FLUID_CHORUS_PARAM_LAST] = {0.0}; + + /* setting name (except lfo waveform type) */ + static const char *name[FLUID_CHORUS_PARAM_LAST-1] = + { + "synth.chorus.nr", "synth.chorus.level", + "synth.chorus.speed", "synth.chorus.depth" + }; + + /* check parameters */ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* check if chorus value is in max min range */ + if(param == FLUID_CHORUS_TYPE || param == FLUID_CHORUS_NR) /* integer value */ + { + int min = FLUID_CHORUS_MOD_SINE; + int max = FLUID_CHORUS_MOD_TRIANGLE; + if(param == FLUID_CHORUS_NR) + { + fluid_settings_getint_range(synth->settings, name[param], &min, &max); + } + if((int)value < min || (int)value > max) + { + FLUID_API_RETURN(FLUID_FAILED); + } + } + else /* float value */ + { + double min; + double max; + fluid_settings_getnum_range(synth->settings, name[param], &min, &max); + if(value < min || value > max) + { + FLUID_API_RETURN(FLUID_FAILED); + } + } + + /* set the value */ + values[param] = value; + ret = fluid_synth_set_chorus_full(synth, fx_group, + FLUID_CHORPARAM_TO_SETFLAG(param), values); + FLUID_API_RETURN(ret); +} + +int +fluid_synth_set_chorus_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + /* if non of the flags is set, fail */ + fluid_return_val_if_fail(set & FLUID_CHORUS_SET_ALL, FLUID_FAILED); + + /* fx group shadow values are set here so that they will be returned if queried */ + fluid_rvoice_mixer_set_chorus_full(synth->eventhandler->mixer, fx_group, + set, values); + + /* Synth shadow values are set here so that they will be returned if queried */ + if (fx_group < 0) + { + int i; + for(i = 0; i < FLUID_CHORUS_PARAM_LAST; i++) + { + if(set & FLUID_CHORPARAM_TO_SETFLAG(i)) + { + synth->chorus_param[i] = values[i]; + } + } + } + + param[0].i = fx_group; + param[1].i = set; + param[2].i = (int)values[FLUID_CHORUS_NR]; + param[3].real = values[FLUID_CHORUS_LEVEL]; + param[4].real = values[FLUID_CHORUS_SPEED]; + param[5].real = values[FLUID_CHORUS_DEPTH]; + param[6].i = (int)values[FLUID_CHORUS_TYPE]; + return fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_set_chorus_params, + synth->eventhandler->mixer, + param); +} + +/** + * Get chorus voice number (delay line count) value of all fx groups. + * @param synth FluidSynth instance + * @return Chorus voice count + * @deprecated Use fluid_synth_get_chorus_group_nr() in new code instead. + */ +int +fluid_synth_get_chorus_nr(fluid_synth_t *synth) +{ + double nr = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_NR, &nr); + return (int)nr; +} + +/** + * Get chorus level of all fx groups. + * @param synth FluidSynth instance + * @return Chorus level value + * @deprecated Use fluid_synth_get_chorus_group_level() in new code instead. + */ +double +fluid_synth_get_chorus_level(fluid_synth_t *synth) +{ + double level = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_LEVEL, &level); + return level; +} + +/** + * Get chorus speed in Hz of all fx groups. + * @param synth FluidSynth instance + * @return Chorus speed in Hz + * @deprecated Use fluid_synth_get_chorus_group_speed() in new code instead. + */ +double +fluid_synth_get_chorus_speed(fluid_synth_t *synth) +{ + double speed = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_SPEED, &speed); + return speed; +} + +/** + * Get chorus depth of all fx groups. + * @param synth FluidSynth instance + * @return Chorus depth + * @deprecated Use fluid_synth_get_chorus_group_depth() in new code instead. + */ +double +fluid_synth_get_chorus_depth(fluid_synth_t *synth) +{ + double depth = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_DEPTH, &depth); + return depth; +} + +/** + * Get chorus waveform type of all fx groups. + * @param synth FluidSynth instance + * @return Chorus waveform type (#fluid_chorus_mod) + * @deprecated Use fluid_synth_get_chorus_group_type() in new code instead. + */ +int +fluid_synth_get_chorus_type(fluid_synth_t *synth) +{ + double type = 0.0; + fluid_synth_chorus_get_param(synth, -1, FLUID_CHORUS_TYPE, &type); + return (int)type; +} + +/** + * Get chorus count nr of one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group from which to fetch the chorus voice count. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param nr valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_get_chorus_group_nr(fluid_synth_t *synth, int fx_group, int *nr) +{ + double num_nr = 0.0; + int status; + status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_NR, &num_nr); + *nr = (int)num_nr; + return status; +} + +/** + * Get chorus output level of one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group from which chorus level to fetch. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param level valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_get_chorus_group_level(fluid_synth_t *synth, int fx_group, double *level) +{ + return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_LEVEL, level); +} + +/** + * Get chorus waveform lfo speed of one or all fx groups. + * @param synth FluidSynth instance. + * @param fx_group Index of the fx group from which lfo speed to fetch. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param speed valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise. + */ +int +fluid_synth_get_chorus_group_speed(fluid_synth_t *synth, int fx_group, double *speed) +{ + return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_SPEED, speed); +} + +/** + * Get chorus lfo depth of one or all fx groups. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group from which lfo depth to fetch. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param depth_ms valid pointer on value to return. + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_chorus_group_depth(fluid_synth_t *synth, int fx_group, double *depth_ms) +{ + return fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_DEPTH, depth_ms); +} + +/** + * Get chorus waveform type of one or all fx groups. + * @param synth FluidSynth instance + * @param fx_group Index of the fx group from which to fetch the waveform type. + * Must be in the range -1 to (fluid_synth_count_effects_groups()-1). If -1 the + * parameter common to all fx groups is fetched. + * @param type valid pointer on waveform type to return (#fluid_chorus_mod) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_get_chorus_group_type(fluid_synth_t *synth, int fx_group, int *type) +{ + double num_type = 0.0; + int status; + status = fluid_synth_chorus_get_param(synth, fx_group, FLUID_CHORUS_TYPE, &num_type); + *type = (int)num_type; + return status; +} + +/** + * Get chorus parameter value of one or all fx groups. + * @param synth FluidSynth instance + * @param fx_group index of the fx group + * @param enum indicating the parameter to get. + * FLUID_CHORUS_NR, chorus voice count. + * FLUID_CHORUS_LEVEL, chorus level. + * FLUID_CHORUS_SPEED, chorus speed. + * FLUID_CHORUS_DEPTH, chorus depth. + * FLUID_CHORUS_TYPE, chorus waveform type. + * @param value pointer on the value to return. + * @return FLUID_OK if success, FLUID_FAILED otherwise. + */ +static int fluid_synth_chorus_get_param(fluid_synth_t *synth, int fx_group, + int param, double *value) +{ + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail((param >= 0) && (param < FLUID_CHORUS_PARAM_LAST), FLUID_FAILED); + fluid_return_val_if_fail(value != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(fx_group < -1 || fx_group >= synth->effects_groups) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if (fx_group < 0) + { + /* return chorus param common to all fx groups */ + *value = synth->chorus_param[param]; + } + else + { + /* return chorus param of fx group at index group */ + *value = fluid_rvoice_mixer_chorus_get_param(synth->eventhandler->mixer, + fx_group, param); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/* + * If the same note is hit twice on the same channel, then the older + * voice process is advanced to the release stage. Using a mechanical + * MIDI controller, the only way this can happen is when the sustain + * pedal is held. In this case the behaviour implemented here is + * natural for many instruments. Note: One noteon event can trigger + * several voice processes, for example a stereo sample. Don't + * release those... + */ +void +fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan, + int key) +{ + int i; + fluid_voice_t *voice; + + /* storeid is a parameter for fluid_voice_init() */ + synth->storeid = synth->noteid++; + + /* for "monophonic playing" key is the previous sustained note + if it exists (0 to 127) or INVALID_NOTE otherwise */ + if(key == INVALID_NOTE) + { + return; + } + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_playing(voice) + && (fluid_voice_get_channel(voice) == chan) + && (fluid_voice_get_key(voice) == key) + && (fluid_voice_get_id(voice) != synth->noteid)) + { + /* Id of voices that was sustained by sostenuto */ + if(fluid_voice_is_sostenuto(voice)) + { + synth->storeid = fluid_voice_get_id(voice); + } + + /* Force the voice into release stage except if pedaling + (sostenuto or sustain) is active */ + fluid_voice_noteoff(voice); + } + } +} + +/** + * Set synthesis interpolation method on one or all MIDI channels. + * @param synth FluidSynth instance + * @param chan MIDI channel to set interpolation method on or -1 for all channels + * @param interp_method Interpolation method (#fluid_interp) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_set_interp_method(fluid_synth_t *synth, int chan, int interp_method) +{ + int i; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + if(chan < -1 || chan >= synth->midi_channels) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + if(synth->channel[0] == NULL) + { + FLUID_LOG(FLUID_ERR, "Channels don't exist (yet)!"); + FLUID_API_RETURN(FLUID_FAILED); + } + + for(i = 0; i < synth->midi_channels; i++) + { + if(chan < 0 || fluid_channel_get_num(synth->channel[i]) == chan) + { + fluid_channel_set_interp_method(synth->channel[i], interp_method); + } + } + + FLUID_API_RETURN(FLUID_OK); +}; + +/** + * Get the total count of MIDI channels. + * @param synth FluidSynth instance + * @return Count of MIDI channels + */ +int +fluid_synth_count_midi_channels(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + result = synth->midi_channels; + FLUID_API_RETURN(result); +} + +/** + * Get the total count of audio channels. + * @param synth FluidSynth instance + * @return Count of audio channel stereo pairs (1 = 2 channels, 2 = 4, etc) + */ +int +fluid_synth_count_audio_channels(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + result = synth->audio_channels; + FLUID_API_RETURN(result); +} + +/** + * Get the total number of allocated audio channels. Usually identical to the + * number of audio channels. Can be employed by LADSPA effects subsystem. + * + * @param synth FluidSynth instance + * @return Count of audio group stereo pairs (1 = 2 channels, 2 = 4, etc) + */ +int +fluid_synth_count_audio_groups(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + result = synth->audio_groups; + FLUID_API_RETURN(result); +} + +/** + * Get the total number of allocated effects channels. + * @param synth FluidSynth instance + * @return Count of allocated effects channels + */ +int +fluid_synth_count_effects_channels(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + result = synth->effects_channels; + FLUID_API_RETURN(result); +} + +/** + * Get the total number of allocated effects units. + * + * This is the same number as initially provided by the setting \setting{synth_effects-groups}. + * @param synth FluidSynth instance + * @return Count of allocated effects units + */ +int +fluid_synth_count_effects_groups(fluid_synth_t *synth) +{ + int result; + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + result = synth->effects_groups; + FLUID_API_RETURN(result); +} + +/** + * Get the synth CPU load value. + * @param synth FluidSynth instance + * @return Estimated CPU load value in percent (0-100) + */ +double +fluid_synth_get_cpu_load(fluid_synth_t *synth) +{ + fluid_return_val_if_fail(synth != NULL, 0); + return fluid_atomic_float_get(&synth->cpu_load); +} + +/* Get tuning for a given bank:program */ +static fluid_tuning_t * +fluid_synth_get_tuning(fluid_synth_t *synth, int bank, int prog) +{ + + if((synth->tuning == NULL) || + (synth->tuning[bank] == NULL) || + (synth->tuning[bank][prog] == NULL)) + { + return NULL; + } + + return synth->tuning[bank][prog]; +} + +/* Replace tuning on a given bank:program (need not already exist). + * Synth mutex should already be locked by caller. */ +static int +fluid_synth_replace_tuning_LOCK(fluid_synth_t *synth, fluid_tuning_t *tuning, + int bank, int prog, int apply) +{ + fluid_tuning_t *old_tuning; + + if(synth->tuning == NULL) + { + synth->tuning = FLUID_ARRAY(fluid_tuning_t **, 128); + + if(synth->tuning == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + FLUID_MEMSET(synth->tuning, 0, 128 * sizeof(fluid_tuning_t **)); + } + + if(synth->tuning[bank] == NULL) + { + synth->tuning[bank] = FLUID_ARRAY(fluid_tuning_t *, 128); + + if(synth->tuning[bank] == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return FLUID_FAILED; + } + + FLUID_MEMSET(synth->tuning[bank], 0, 128 * sizeof(fluid_tuning_t *)); + } + + old_tuning = synth->tuning[bank][prog]; + synth->tuning[bank][prog] = tuning; + + if(old_tuning) + { + if(!fluid_tuning_unref(old_tuning, 1)) /* -- unref old tuning */ + { + /* Replace old tuning if present */ + fluid_synth_replace_tuning_LOCAL(synth, old_tuning, tuning, apply, FALSE); + } + } + + return FLUID_OK; +} + +/* Replace a tuning with a new one in all MIDI channels. new_tuning can be + * NULL, in which case channels are reset to default equal tempered scale. */ +static void +fluid_synth_replace_tuning_LOCAL(fluid_synth_t *synth, fluid_tuning_t *old_tuning, + fluid_tuning_t *new_tuning, int apply, int unref_new) +{ + fluid_channel_t *channel; + int old_tuning_unref = 0; + int i; + + for(i = 0; i < synth->midi_channels; i++) + { + channel = synth->channel[i]; + + if(fluid_channel_get_tuning(channel) == old_tuning) + { + old_tuning_unref++; + + if(new_tuning) + { + fluid_tuning_ref(new_tuning); /* ++ ref new tuning for channel */ + } + + fluid_channel_set_tuning(channel, new_tuning); + + if(apply) + { + fluid_synth_update_voice_tuning_LOCAL(synth, channel); + } + } + } + + /* Send unref old tuning event if any unrefs */ + if(old_tuning && old_tuning_unref) + { + fluid_tuning_unref(old_tuning, old_tuning_unref); + } + + if(!unref_new || !new_tuning) + { + return; + } + + fluid_tuning_unref(new_tuning, 1); +} + +/* Update voice tunings in realtime */ +static void +fluid_synth_update_voice_tuning_LOCAL(fluid_synth_t *synth, fluid_channel_t *channel) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_on(voice) && (voice->channel == channel)) + { + fluid_voice_calculate_gen_pitch(voice); + fluid_voice_update_param(voice, GEN_PITCH); + } + } +} + +/** + * Set the tuning of the entire MIDI note scale. + * @param synth FluidSynth instance + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program + * @param name Label name for this tuning + * @param pitch Array of pitch values (length of 128, each value is number of + * cents, for example normally note 0 is 0.0, 1 is 100.0, 60 is 6000.0, etc). + * Pass NULL to create a equal tempered (normal) scale. + * @param apply TRUE to apply new tuning in realtime to existing notes which + * are using the replaced tuning (if any), FALSE otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + */ +int +fluid_synth_activate_key_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply) +{ + fluid_tuning_t *tuning; + int retval = FLUID_OK; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + tuning = new_fluid_tuning(name, bank, prog); + + if(tuning) + { + if(pitch) + { + fluid_tuning_set_all(tuning, pitch); + } + + retval = fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, apply); + + if(retval == FLUID_FAILED) + { + fluid_tuning_unref(tuning, 1); + } + } + else + { + retval = FLUID_FAILED; + } + + FLUID_API_RETURN(retval); +} + +/** + * Activate an octave tuning on every octave in the MIDI note scale. + * @param synth FluidSynth instance + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program + * @param name Label name for this tuning + * @param pitch Array of pitch values (length of 12 for each note of an octave + * starting at note C, values are number of offset cents to add to the normal + * tuning amount) + * @param apply TRUE to apply new tuning in realtime to existing notes which + * are using the replaced tuning (if any), FALSE otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + */ +int +fluid_synth_activate_octave_tuning(fluid_synth_t *synth, int bank, int prog, + const char *name, const double *pitch, int apply) +{ + fluid_tuning_t *tuning; + int retval = FLUID_OK; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(pitch != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + tuning = new_fluid_tuning(name, bank, prog); + + if(tuning) + { + fluid_tuning_set_octave(tuning, pitch); + retval = fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, apply); + + if(retval == FLUID_FAILED) + { + fluid_tuning_unref(tuning, 1); + } + } + else + { + retval = FLUID_FAILED; + } + + FLUID_API_RETURN(retval); +} + +/** + * Set tuning values for one or more MIDI notes for an existing tuning. + * @param synth FluidSynth instance + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program + * @param len Number of MIDI notes to assign + * @param key Array of MIDI key numbers (length of 'len', values 0-127) + * @param pitch Array of pitch values (length of 'len', values are number of + * cents from MIDI note 0) + * @param apply TRUE to apply tuning change in realtime to existing notes using + * the specified tuning, FALSE otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note Prior to version 1.1.0 it was an error to specify a tuning that didn't + * already exist. Starting with 1.1.0, the default equal tempered scale will be + * used as a basis, if no tuning exists for the given bank and prog. + */ +int +fluid_synth_tune_notes(fluid_synth_t *synth, int bank, int prog, + int len, const int *key, const double *pitch, int apply) +{ + fluid_tuning_t *old_tuning, *new_tuning; + int retval = FLUID_OK; + int i; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); + fluid_return_val_if_fail(len > 0, FLUID_FAILED); + fluid_return_val_if_fail(key != NULL, FLUID_FAILED); + fluid_return_val_if_fail(pitch != NULL, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + old_tuning = fluid_synth_get_tuning(synth, bank, prog); + + if(old_tuning) + { + new_tuning = fluid_tuning_duplicate(old_tuning); + } + else + { + new_tuning = new_fluid_tuning("Unnamed", bank, prog); + } + + if(new_tuning) + { + for(i = 0; i < len; i++) + { + fluid_tuning_set_pitch(new_tuning, key[i], pitch[i]); + } + + retval = fluid_synth_replace_tuning_LOCK(synth, new_tuning, bank, prog, apply); + + if(retval == FLUID_FAILED) + { + fluid_tuning_unref(new_tuning, 1); + } + } + else + { + retval = FLUID_FAILED; + } + + FLUID_API_RETURN(retval); +} + +/** + * Activate a tuning scale on a MIDI channel. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param bank Tuning bank number (0-127), not related to MIDI instrument bank + * @param prog Tuning preset number (0-127), not related to MIDI instrument program + * @param apply TRUE to apply tuning change to active notes, FALSE otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + * + * @note A default equal tempered scale will be created, if no tuning exists + * on the given bank and prog. + */ +int +fluid_synth_activate_tuning(fluid_synth_t *synth, int chan, int bank, int prog, + int apply) +{ + fluid_tuning_t *tuning; + int retval = FLUID_OK; + + //fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + //fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, FLUID_FAILED); + fluid_return_val_if_fail(bank >= 0 && bank < 128, FLUID_FAILED); + fluid_return_val_if_fail(prog >= 0 && prog < 128, FLUID_FAILED); + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + tuning = fluid_synth_get_tuning(synth, bank, prog); + + /* If no tuning exists, create a new default tuning. We do this, so that + * it can be replaced later, if any changes are made. */ + if(!tuning) + { + tuning = new_fluid_tuning("Unnamed", bank, prog); + + if(tuning) + { + fluid_synth_replace_tuning_LOCK(synth, tuning, bank, prog, FALSE); + } + } + + if(tuning) + { + fluid_tuning_ref(tuning); /* ++ ref for outside of lock */ + } + + if(!tuning) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + fluid_tuning_ref(tuning); /* ++ ref new tuning for following function */ + retval = fluid_synth_set_tuning_LOCAL(synth, chan, tuning, apply); + + fluid_tuning_unref(tuning, 1); /* -- unref for outside of lock */ + + FLUID_API_RETURN(retval); +} + +/* Local synthesis thread set tuning function (takes over tuning reference) */ +static int +fluid_synth_set_tuning_LOCAL(fluid_synth_t *synth, int chan, + fluid_tuning_t *tuning, int apply) +{ + fluid_tuning_t *old_tuning; + fluid_channel_t *channel; + + channel = synth->channel[chan]; + + old_tuning = fluid_channel_get_tuning(channel); + fluid_channel_set_tuning(channel, tuning); /* !! Takes over callers reference */ + + if(apply) + { + fluid_synth_update_voice_tuning_LOCAL(synth, channel); + } + + /* Send unref old tuning event */ + if(old_tuning) + { + fluid_tuning_unref(old_tuning, 1); + } + + + return FLUID_OK; +} + +/** + * Clear tuning scale on a MIDI channel (use default equal tempered scale). + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param apply TRUE to apply tuning change to active notes, FALSE otherwise + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.0 + */ +int +fluid_synth_deactivate_tuning(fluid_synth_t *synth, int chan, int apply) +{ + int retval = FLUID_OK; + + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + retval = fluid_synth_set_tuning_LOCAL(synth, chan, NULL, apply); + + FLUID_API_RETURN(retval); +} + +/** + * Start tuning iteration. + * @param synth FluidSynth instance + */ +void +fluid_synth_tuning_iteration_start(fluid_synth_t *synth) +{ + fluid_return_if_fail(synth != NULL); + fluid_synth_api_enter(synth); + fluid_private_set(synth->tuning_iter, FLUID_INT_TO_POINTER(0)); + fluid_synth_api_exit(synth); +} + +/** + * Advance to next tuning. + * @param synth FluidSynth instance + * @param bank Location to store MIDI bank number of next tuning scale + * @param prog Location to store MIDI program number of next tuning scale + * @return 1 if tuning iteration advanced, 0 if no more tunings + */ +int +fluid_synth_tuning_iteration_next(fluid_synth_t *synth, int *bank, int *prog) +{ + void *pval; + int b = 0, p = 0; + + fluid_return_val_if_fail(synth != NULL, 0); + fluid_return_val_if_fail(bank != NULL, 0); + fluid_return_val_if_fail(prog != NULL, 0); + fluid_synth_api_enter(synth); + + /* Current tuning iteration stored as: bank << 8 | program */ + pval = fluid_private_get(synth->tuning_iter); + p = FLUID_POINTER_TO_INT(pval); + b = (p >> 8) & 0xFF; + p &= 0xFF; + + if(!synth->tuning) + { + FLUID_API_RETURN(0); + } + + for(; b < 128; b++, p = 0) + { + if(synth->tuning[b] == NULL) + { + continue; + } + + for(; p < 128; p++) + { + if(synth->tuning[b][p] == NULL) + { + continue; + } + + *bank = b; + *prog = p; + + if(p < 127) + { + fluid_private_set(synth->tuning_iter, + FLUID_INT_TO_POINTER(b << 8 | (p + 1))); + } + else + { + fluid_private_set(synth->tuning_iter, FLUID_INT_TO_POINTER((b + 1) << 8)); + } + + FLUID_API_RETURN(1); + } + } + + FLUID_API_RETURN(0); +} + +/** + * Get the entire note tuning for a given MIDI bank and program. + * @param synth FluidSynth instance + * @param bank MIDI bank number of tuning + * @param prog MIDI program number of tuning + * @param name Location to store tuning name or NULL to ignore + * @param len Maximum number of chars to store to 'name' (including NULL byte) + * @param pitch Array to store tuning scale to or NULL to ignore (len of 128) + * @return #FLUID_OK if matching tuning was found, #FLUID_FAILED otherwise + */ +int +fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int prog, + char *name, int len, double *pitch) +{ + fluid_tuning_t *tuning; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + tuning = fluid_synth_get_tuning(synth, bank, prog); + + if(tuning) + { + if(name) + { + FLUID_SNPRINTF(name, len - 1, "%s", fluid_tuning_get_name(tuning)); + name[len - 1] = 0; /* make sure the string is null terminated */ + } + + if(pitch) + { + FLUID_MEMCPY(pitch, fluid_tuning_get_all(tuning), 128 * sizeof(double)); + } + } + + FLUID_API_RETURN(tuning ? FLUID_OK : FLUID_FAILED); +} + +/** + * Get settings assigned to a synth. + * @param synth FluidSynth instance + * @return FluidSynth settings which are assigned to the synth + */ +fluid_settings_t * +fluid_synth_get_settings(fluid_synth_t *synth) +{ + fluid_return_val_if_fail(synth != NULL, NULL); + + return synth->settings; +} + +/** + * Apply an offset to a SoundFont generator on a MIDI channel. + * + * This function allows to set an offset for the specified destination generator in real-time. + * The offset will be applied immediately to all voices that are currently and subsequently playing + * on the given MIDI channel. This functionality works equivalent to using NRPN MIDI messages to + * manipulate synthesis parameters. See SoundFont spec, paragraph 8.1.3, for details on SoundFont + * generator parameters and valid ranges, as well as paragraph 9.6 for details on NRPN messages. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param SoundFont generator ID (#fluid_gen_type) + * @param value Offset value (in native units of the generator) to assign to the MIDI channel + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int fluid_synth_set_gen(fluid_synth_t *synth, int chan, int param, float value) +{ + fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + fluid_synth_set_gen_LOCAL(synth, chan, param, value); + + FLUID_API_RETURN(FLUID_OK); +} + +/* Synthesis thread local set gen function */ +static void +fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, int param, float value) +{ + fluid_voice_t *voice; + int i; + + fluid_channel_set_gen(synth->channel[chan], param, value); + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_get_channel(voice) == chan) + { + fluid_voice_set_param(voice, param, value); + } + } +} + +/** + * Retrieve the generator NRPN offset assigned to a MIDI channel. + * + * The value returned is in native units of the generator. By default, the offset is zero. + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param param SoundFont generator ID (#fluid_gen_type) + * @return Current NRPN generator offset value assigned to the MIDI channel + */ +float +fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param) +{ + float result; + fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + result = fluid_channel_get_gen(synth->channel[chan], param); + FLUID_API_RETURN(result); +} + +/** + * Handle MIDI event from MIDI router, used as a callback function. + * @param data FluidSynth instance + * @param event MIDI event to handle + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + */ +int +fluid_synth_handle_midi_event(void *data, fluid_midi_event_t *event) +{ + fluid_synth_t *synth = (fluid_synth_t *) data; + int type = fluid_midi_event_get_type(event); + int chan = fluid_midi_event_get_channel(event); + + switch(type) + { + case NOTE_ON: + return fluid_synth_noteon(synth, chan, + fluid_midi_event_get_key(event), + fluid_midi_event_get_velocity(event)); + + case NOTE_OFF: + return fluid_synth_noteoff(synth, chan, fluid_midi_event_get_key(event)); + + case CONTROL_CHANGE: + return fluid_synth_cc(synth, chan, + fluid_midi_event_get_control(event), + fluid_midi_event_get_value(event)); + + case PROGRAM_CHANGE: + return fluid_synth_program_change(synth, chan, fluid_midi_event_get_program(event)); + + case CHANNEL_PRESSURE: + return fluid_synth_channel_pressure(synth, chan, fluid_midi_event_get_program(event)); + + case KEY_PRESSURE: + return fluid_synth_key_pressure(synth, chan, + fluid_midi_event_get_key(event), + fluid_midi_event_get_value(event)); + + case PITCH_BEND: + return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event)); + + case MIDI_SYSTEM_RESET: + return fluid_synth_system_reset(synth); + + case MIDI_SYSEX: + return fluid_synth_sysex(synth, event->paramptr, event->param1, NULL, NULL, NULL, FALSE); + + case MIDI_TEXT: + case MIDI_LYRIC: + case MIDI_SET_TEMPO: + return FLUID_OK; + } + + return FLUID_FAILED; +} + +/** + * Create and start voices using an arbitrary preset and a MIDI note on event. + * + * Using this function is only supported when the setting @c synth.dynamic-sample-loading is false! + * @param synth FluidSynth instance + * @param id Voice group ID to use (can be used with fluid_synth_stop()). + * @param preset Preset to synthesize + * @param audio_chan Unused currently, set to 0 + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param key MIDI note number (0-127) + * @param vel MIDI velocity number (1-127) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note Should only be called from within synthesis thread, which includes + * SoundFont loader preset noteon method. + */ +int +fluid_synth_start(fluid_synth_t *synth, unsigned int id, fluid_preset_t *preset, + int audio_chan, int chan, int key, int vel) +{ + int result, dynamic_samples; + fluid_return_val_if_fail(preset != NULL, FLUID_FAILED); + fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED); + fluid_return_val_if_fail(vel >= 1 && vel <= 127, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + fluid_settings_getint(fluid_synth_get_settings(synth), "synth.dynamic-sample-loading", &dynamic_samples); + if(dynamic_samples) + { + // The preset might not be currently used, thus its sample data may not be loaded. + // This guard is to avoid a NULL deref in rvoice_write(). + FLUID_LOG(FLUID_ERR, "Calling fluid_synth_start() while synth.dynamic-sample-loading is enabled is not supported."); + // Although we would be able to select the preset (and load it's samples) we have no way to + // unselect the preset again in fluid_synth_stop(). Also dynamic sample loading was intended + // to be used only when presets have been selected on a MIDI channel. + // Note that even if the preset is currently selected on a channel, it could be unselected at + // any time. And we would end up with a NULL sample->data again, because we are not referencing + // the preset here. Thus failure is our only option. + result = FLUID_FAILED; + } + else + { + synth->storeid = id; + result = fluid_preset_noteon(preset, synth, chan, key, vel); + } + + FLUID_API_RETURN(result); +} + +/** + * Stop notes for a given note event voice ID. + * @param synth FluidSynth instance + * @param id Voice note event ID + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * @note In FluidSynth versions prior to 1.1.0 #FLUID_FAILED would be returned + * if no matching voice note event ID was found. Versions after 1.1.0 only + * return #FLUID_FAILED if an error occurs. + */ +int +fluid_synth_stop(fluid_synth_t *synth, unsigned int id) +{ + int result; + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + fluid_synth_stop_LOCAL(synth, id); + result = FLUID_OK; + FLUID_API_RETURN(result); +} + +/* Local synthesis thread variant of fluid_synth_stop */ +static void +fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id) +{ + fluid_voice_t *voice; + int i; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_on(voice) && (fluid_voice_get_id(voice) == id)) + { + fluid_voice_noteoff(voice); + } + } +} + +/** + * Offset the bank numbers of a loaded SoundFont, i.e.\ subtract + * \c offset from any bank number when assigning instruments. + * + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @param offset Bank offset value to apply to all instruments + * @return #FLUID_OK if the offset was set successfully, #FLUID_FAILED otherwise + */ +int +fluid_synth_set_bank_offset(fluid_synth_t *synth, int sfont_id, int offset) +{ + fluid_sfont_t *sfont; + fluid_list_t *list; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == sfont_id) + { + sfont->bankofs = offset; + break; + } + } + + if(!list) + { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id); + FLUID_API_RETURN(FLUID_FAILED); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Get bank offset of a loaded SoundFont. + * @param synth FluidSynth instance + * @param sfont_id ID of a loaded SoundFont + * @return SoundFont bank offset value + */ +int +fluid_synth_get_bank_offset(fluid_synth_t *synth, int sfont_id) +{ + fluid_sfont_t *sfont; + fluid_list_t *list; + int offset = 0; + + fluid_return_val_if_fail(synth != NULL, 0); + fluid_synth_api_enter(synth); + + for(list = synth->sfont; list; list = fluid_list_next(list)) + { + sfont = fluid_list_get(list); + + if(fluid_sfont_get_id(sfont) == sfont_id) + { + offset = sfont->bankofs; + break; + } + } + + if(!list) + { + FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id); + FLUID_API_RETURN(0); + } + + FLUID_API_RETURN(offset); +} + +void +fluid_synth_api_enter(fluid_synth_t *synth) +{ + if(synth->use_mutex) + { + fluid_rec_mutex_lock(synth->mutex); + } + + if(!synth->public_api_count) + { + fluid_synth_check_finished_voices(synth); + } + + synth->public_api_count++; +} + +void fluid_synth_api_exit(fluid_synth_t *synth) +{ + synth->public_api_count--; + + if(!synth->public_api_count) + { + fluid_rvoice_eventhandler_flush(synth->eventhandler); + } + + if(synth->use_mutex) + { + fluid_rec_mutex_unlock(synth->mutex); + } + +} + +/** + * Set midi channel type + * @param synth FluidSynth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param type MIDI channel type (#fluid_midi_channel_type) + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * @since 1.1.4 + */ +int fluid_synth_set_channel_type(fluid_synth_t *synth, int chan, int type) +{ + fluid_return_val_if_fail((type >= CHANNEL_TYPE_MELODIC) && (type <= CHANNEL_TYPE_DRUM), FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + synth->channel[chan]->channel_type = type; + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Return the LADSPA effects instance used by FluidSynth + * + * @param synth FluidSynth instance + * @return pointer to LADSPA fx or NULL if compiled without LADSPA support or LADSPA is not active + */ +fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth) +{ + fluid_return_val_if_fail(synth != NULL, NULL); + + return synth->ladspa_fx; +} + +/** + * Configure a general-purpose IIR biquad filter. + * + * @param synth FluidSynth instance + * @param type Type of the IIR filter to use (see #fluid_iir_filter_type) + * @param flags Additional flags to customize this filter or zero to stay with the default (see #fluid_iir_filter_flags) + * @return #FLUID_OK if the settings have been successfully applied, otherwise #FLUID_FAILED + * + * This is an optional, additional filter that operates independently from the default low-pass filter required by the Soundfont2 standard. + * By default this filter is off (#FLUID_IIR_DISABLED). + */ +int fluid_synth_set_custom_filter(fluid_synth_t *synth, int type, int flags) +{ + int i; + fluid_voice_t *voice; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_return_val_if_fail(type >= FLUID_IIR_DISABLED && type < FLUID_IIR_LAST, FLUID_FAILED); + + fluid_synth_api_enter(synth); + + synth->custom_filter_type = type; + synth->custom_filter_flags = flags; + + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + fluid_voice_set_custom_filter(voice, type, flags); + } + + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Set the important channels for voice overflow priority calculation. + * + * @param synth FluidSynth instance + * @param channels comma-separated list of channel numbers + * @return #FLUID_OK on success, otherwise #FLUID_FAILED + */ +static int fluid_synth_set_important_channels(fluid_synth_t *synth, const char *channels) +{ + int i; + int retval = FLUID_FAILED; + int *values = NULL; + int num_values; + fluid_overflow_prio_t *scores; + + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + + scores = &synth->overflow; + + if(scores->num_important_channels < synth->midi_channels) + { + scores->important_channels = FLUID_REALLOC(scores->important_channels, + sizeof(*scores->important_channels) * synth->midi_channels); + + if(scores->important_channels == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto exit; + } + + scores->num_important_channels = synth->midi_channels; + } + + FLUID_MEMSET(scores->important_channels, FALSE, + sizeof(*scores->important_channels) * scores->num_important_channels); + + if(channels != NULL) + { + values = FLUID_ARRAY(int, synth->midi_channels); + + if(values == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto exit; + } + + /* Every channel given in the comma-separated list of channel numbers + * is set to TRUE, i.e. flagging it as "important". Channel numbers are + * 1-based. */ + num_values = fluid_settings_split_csv(channels, values, synth->midi_channels); + + for(i = 0; i < num_values; i++) + { + if(values[i] > 0 && values[i] <= synth->midi_channels) + { + scores->important_channels[values[i] - 1] = TRUE; + } + } + } + + retval = FLUID_OK; + +exit: + FLUID_FREE(values); + return retval; +} + +/* + * Handler for synth.overflow.important-channels setting. + */ +static void fluid_synth_handle_important_channels(void *data, const char *name, + const char *value) +{ + fluid_synth_t *synth = (fluid_synth_t *)data; + + fluid_synth_api_enter(synth); + fluid_synth_set_important_channels(synth, value); + fluid_synth_api_exit(synth); +} + + +/* API legato mode *********************************************************/ + +/** + * Sets the legato mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a legatomode is invalid. + */ +int fluid_synth_set_legato_mode(fluid_synth_t *synth, int chan, int legatomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(legatomode >= 0, FLUID_FAILED); + fluid_return_val_if_fail(legatomode < FLUID_CHANNEL_LEGATO_MODE_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + synth->channel[chan]->legatomode = legatomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Gets the legato mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param legatomode The legato mode as indicated by #fluid_channel_legato_mode. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a legatomode is NULL. + */ +int fluid_synth_get_legato_mode(fluid_synth_t *synth, int chan, int *legatomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(legatomode != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * legatomode = synth->channel[chan]->legatomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/* API portamento mode *********************************************************/ + +/** + * Sets the portamento mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param portamentomode The portamento mode as indicated by #fluid_channel_portamento_mode. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a portamentomode is invalid. + */ +int fluid_synth_set_portamento_mode(fluid_synth_t *synth, int chan, + int portamentomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(portamentomode >= 0, FLUID_FAILED); + fluid_return_val_if_fail(portamentomode < FLUID_CHANNEL_PORTAMENTO_MODE_LAST, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + synth->channel[chan]->portamentomode = portamentomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Gets the portamento mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param portamentomode Pointer to the portamento mode as indicated by #fluid_channel_portamento_mode. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a portamentomode is NULL. + */ +int fluid_synth_get_portamento_mode(fluid_synth_t *synth, int chan, + int *portamentomode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(portamentomode != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * portamentomode = synth->channel[chan]->portamentomode; + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/* API breath mode *********************************************************/ + +/** + * Sets the breath mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param breathmode The breath mode as indicated by #fluid_channel_breath_flags. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + */ +int fluid_synth_set_breath_mode(fluid_synth_t *synth, int chan, int breathmode) +{ + /* checks parameters first */ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + fluid_channel_set_breath_info(synth->channel[chan], breathmode); + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Gets the breath mode of a channel. + * + * @param synth the synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param breathmode Pointer to the returned breath mode as indicated by #fluid_channel_breath_flags. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a breathmode is NULL. + */ +int fluid_synth_get_breath_mode(fluid_synth_t *synth, int chan, int *breathmode) +{ + /* checks parameters first */ + fluid_return_val_if_fail(breathmode != NULL, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + /**/ + * breathmode = fluid_channel_get_breath_info(synth->channel[chan]); + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/** API Poly/mono mode ******************************************************/ + +/* + * Resets a basic channel group of MIDI channels. + * @param synth the synth instance. + * @param chan the beginning channel of the group. + * @param nbr_chan the number of channel in the group. +*/ +static void +fluid_synth_reset_basic_channel_LOCAL(fluid_synth_t *synth, int chan, int nbr_chan) +{ + int i; + + for(i = chan; i < chan + nbr_chan; i++) + { + fluid_channel_reset_basic_channel_info(synth->channel[i]); + synth->channel[i]->mode_val = 0; + } +} + +/** + * Disables and unassigns all channels from a basic channel group. + * + * @param synth The synth instance. + * @param chan The basic channel of the group to reset or -1 to reset all channels. + * @note By default (i.e. on creation after new_fluid_synth() and after fluid_synth_system_reset()) + * a synth instance has one basic channel at channel 0 in mode #FLUID_CHANNEL_MODE_OMNION_POLY. + * All other channels belong to this basic channel group. Make sure to call this function before + * setting any custom basic channel setup. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a chan isn't a basic channel. + */ +int fluid_synth_reset_basic_channel(fluid_synth_t *synth, int chan) +{ + int nbr_chan; + + /* checks parameters first */ + if(chan < 0) + { + fluid_return_val_if_fail(synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + /* The range is all MIDI channels from 0 to MIDI channel count -1 */ + chan = 0; /* beginning chan */ + nbr_chan = synth->midi_channels; /* MIDI Channels number */ + } + else + { + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /* checks if chan is a basic channel */ + if(!(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC)) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* The range is all MIDI channels in the group from chan */ + nbr_chan = synth->channel[chan]->mode_val; /* nbr of channels in the group */ + } + + /* resets the range of MIDI channels */ + fluid_synth_reset_basic_channel_LOCAL(synth, chan, nbr_chan); + FLUID_API_RETURN(FLUID_OK); +} + +/** + * Checks if a new basic channel group overlaps the next basic channel group. + * + * On success the function returns the possible number of channel for this + * new basic channel group. + * The function fails if the new group overlaps the next basic channel group. + * + * @param see fluid_synth_set_basic_channel. + * @return + * - On success, the effective number of channels for this new basic channel group, + * #FLUID_FAILED otherwise. + * - #FLUID_FAILED + * - \a val has a number of channels overlapping next basic channel group or been + * above MIDI channel count. + */ +static int +fluid_synth_check_next_basic_channel(fluid_synth_t *synth, int basicchan, int mode, int val) +{ + int i, n_chan = synth->midi_channels; /* MIDI Channels count */ + int real_val = val; /* real number of channels in the group */ + + /* adjusts val range */ + if(mode == FLUID_CHANNEL_MODE_OMNIOFF_POLY) + { + real_val = 1; /* mode poly omnioff implies a group of only one channel.*/ + } + else if(val == 0) + { + /* mode poly omnion (0), mono omnion (1), mono omni off (3) */ + /* value 0 means all possible channels from basicchan to MIDI channel count -1.*/ + real_val = n_chan - basicchan; + } + /* checks if val range is above MIDI channel count */ + else if(basicchan + val > n_chan) + { + return FLUID_FAILED; + } + + /* checks if this basic channel group overlaps next basic channel group */ + for(i = basicchan + 1; i < basicchan + real_val; i++) + { + if(synth->channel[i]->mode & FLUID_CHANNEL_BASIC) + { + /* A value of 0 for val means all possible channels from basicchan to + to the next basic channel -1 (if any). + When i reaches the next basic channel group, real_val will be + limited if it is possible */ + if(val == 0) + { + /* limitation of real_val */ + real_val = i - basicchan; + break; + } + + /* overlap with the next basic channel group */ + return FLUID_FAILED; + } + } + + return real_val; +} + +/** + * Sets a new basic channel group only. The function doesn't allow to change an + * existing basic channel. + * + * The function fails if any channel overlaps any existing basic channel group. + * To make room if necessary, basic channel groups can be cleared using + * fluid_synth_reset_basic_channel(). + * + * @param synth the synth instance. + * @param chan the basic Channel number (0 to MIDI channel count-1). + * @param mode the MIDI mode to use for chan (see #fluid_basic_channel_modes). + * @param val number of channels in the group. + * @note \a val is only relevant for mode #FLUID_CHANNEL_MODE_OMNION_POLY, + * #FLUID_CHANNEL_MODE_OMNION_MONO and #FLUID_CHANNEL_MODE_OMNIOFF_MONO. A value + * of 0 means all possible channels from \a chan to to next basic channel minus 1 (if any) + * or to MIDI channel count minus 1. Val is ignored for #FLUID_CHANNEL_MODE_OMNIOFF_POLY + * as this mode implies a group of only one channel. + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + * - \a mode is invalid. + * - \a val has a number of channels overlapping another basic channel group or been + * above MIDI channel count. + * - When the function fails, any existing basic channels aren't modified. + */ +int fluid_synth_set_basic_channel(fluid_synth_t *synth, int chan, int mode, int val) +{ + /* check parameters */ + fluid_return_val_if_fail(mode >= 0, FLUID_FAILED); + fluid_return_val_if_fail(mode < FLUID_CHANNEL_MODE_LAST, FLUID_FAILED); + fluid_return_val_if_fail(val >= 0, FLUID_FAILED); + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + /**/ + if(val > 0 && chan + val > synth->midi_channels) + { + FLUID_API_RETURN(FLUID_FAILED); + } + + /* Checks if there is an overlap with the next basic channel */ + val = fluid_synth_check_next_basic_channel(synth, chan, mode, val); + + if(val == FLUID_FAILED || synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) + { + /* overlap with the next or previous channel group */ + FLUID_LOG(FLUID_INFO, "basic channel %d overlaps another group", chan); + FLUID_API_RETURN(FLUID_FAILED); + } + + /* sets a new basic channel group */ + fluid_synth_set_basic_channel_LOCAL(synth, chan, mode, val); + /**/ + FLUID_API_RETURN(FLUID_OK); +} + +/* + * Local version of fluid_synth_set_basic_channel(), called internally: + * - by fluid_synth_set_basic_channel() to set a new basic channel group. + * - during creation new_fluid_synth() or on CC reset to set a default basic channel group. + * - on CC ominoff, CC omnion, CC poly , CC mono to change an existing basic channel group. + * + * @param see fluid_synth_set_basic_channel() +*/ +static void +fluid_synth_set_basic_channel_LOCAL(fluid_synth_t *synth, int basicchan, int mode, int val) +{ + int i; + + /* sets the basic channel group */ + for(i = basicchan; i < basicchan + val; i++) + { + int new_mode = mode; /* OMNI_OFF/ON, MONO/POLY ,others bits are zero */ + int new_val; + /* MIDI specs: when mode is changed, channel must receive ALL_NOTES_OFF */ + fluid_synth_all_notes_off_LOCAL(synth, i); + + if(i == basicchan) + { + new_mode |= FLUID_CHANNEL_BASIC; /* First channel in the group */ + new_val = val; /* number of channels in the group */ + } + else + { + new_val = 0; /* val is 0 for other channel than basic channel */ + } + + /* Channel is enabled */ + new_mode |= FLUID_CHANNEL_ENABLED; + /* Now new_mode is OMNI OFF/ON,MONO/POLY, BASIC_CHANNEL or not and enabled */ + fluid_channel_set_basic_channel_info(synth->channel[i], new_mode); + synth->channel[i]->mode_val = new_val; + } +} + +/** + * Searches a previous basic channel starting from chan. + * + * @param synth the synth instance. + * @param chan starting index of the search (including chan). + * @return index of the basic channel if found , FLUID_FAILED otherwise. + */ +static int fluid_synth_get_previous_basic_channel(fluid_synth_t *synth, int chan) +{ + for(; chan >= 0; chan--) + { + /* searches previous basic channel */ + if(synth->channel[chan]->mode & FLUID_CHANNEL_BASIC) + { + /* chan is the previous basic channel */ + return chan; + } + } + + return FLUID_FAILED; +} + +/** + * Returns poly mono mode information of any MIDI channel. + * + * @param synth the synth instance + * @param chan MIDI channel number (0 to MIDI channel count - 1) + * @param basic_chan_out Buffer to store the basic channel \a chan belongs to or #FLUID_FAILED if \a chan is disabled. + * @param mode_out Buffer to store the mode of \a chan (see #fluid_basic_channel_modes) or #FLUID_FAILED if \a chan is disabled. + * @param val_out Buffer to store the total number of channels in this basic channel group or #FLUID_FAILED if \a chan is disabled. + * @note If any of \a basic_chan_out, \a mode_out, \a val_out pointer is NULL + * the corresponding information isn't returned. + * + * @return + * - #FLUID_OK on success. + * - #FLUID_FAILED + * - \a synth is NULL. + * - \a chan is outside MIDI channel count. + */ +int fluid_synth_get_basic_channel(fluid_synth_t *synth, int chan, + int *basic_chan_out, + int *mode_out, + int *val_out) +{ + int basic_chan = FLUID_FAILED; + int mode = FLUID_FAILED; + int val = FLUID_FAILED; + + /* checks parameters first */ + FLUID_API_ENTRY_CHAN(FLUID_FAILED); + + if((synth->channel[chan]->mode & FLUID_CHANNEL_ENABLED) && + /* chan is enabled , we search the basic channel chan belongs to */ + (basic_chan = fluid_synth_get_previous_basic_channel(synth, chan)) != FLUID_FAILED) + { + mode = synth->channel[chan]->mode & FLUID_CHANNEL_MODE_MASK; + val = synth->channel[basic_chan]->mode_val; + } + + /* returns the information if they are requested */ + if(basic_chan_out) + { + * basic_chan_out = basic_chan; + } + + if(mode_out) + { + * mode_out = mode; + } + + if(val_out) + { + * val_out = val; + } + + FLUID_API_RETURN(FLUID_OK); +} diff --git a/thirdparty/fluidsynth/src/synth/fluid_synth.h b/thirdparty/fluidsynth/src/synth/fluid_synth.h new file mode 100644 index 000000000..132a98dda --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_synth.h @@ -0,0 +1,267 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_SYNTH_H +#define _FLUID_SYNTH_H + + +/*************************************************************** + * + * INCLUDES + */ + +#include "fluid_sys.h" +#include "fluid_list.h" +#include "fluid_rev.h" +#include "fluid_voice.h" +#include "fluid_chorus.h" +#include "fluid_ladspa.h" +#include "fluid_midi_router.h" +#include "fluid_rvoice_event.h" + +/*************************************************************** + * + * DEFINES + */ +#define FLUID_NUM_PROGRAMS 128 +#define DRUM_INST_BANK 128 + +#define FLUID_UNSET_PROGRAM 128 /* Program number used to unset a preset */ + +#define FLUID_REVERB_DEFAULT_ROOMSIZE 0.2f /**< Default reverb room size */ +#define FLUID_REVERB_DEFAULT_DAMP 0.0f /**< Default reverb damping */ +#define FLUID_REVERB_DEFAULT_WIDTH 0.5f /**< Default reverb width */ +#define FLUID_REVERB_DEFAULT_LEVEL 0.9f /**< Default reverb level */ + +#define FLUID_CHORUS_DEFAULT_N 3 /**< Default chorus voice count */ +#define FLUID_CHORUS_DEFAULT_LEVEL 2.0f /**< Default chorus level */ +#define FLUID_CHORUS_DEFAULT_SPEED 0.3f /**< Default chorus speed */ +#define FLUID_CHORUS_DEFAULT_DEPTH 8.0f /**< Default chorus depth */ +#define FLUID_CHORUS_DEFAULT_TYPE FLUID_CHORUS_MOD_SINE /**< Default chorus waveform type */ + +/*************************************************************** + * + * ENUM + */ + +/** + * Bank Select MIDI message styles. Default style is GS. + */ +enum fluid_midi_bank_select +{ + FLUID_BANK_STYLE_GM, /**< GM style, bank = 0 always (CC0/MSB and CC32/LSB ignored) */ + FLUID_BANK_STYLE_GS, /**< GS style, bank = CC0/MSB (CC32/LSB ignored) */ + FLUID_BANK_STYLE_XG, /**< XG style, bank = CC32/LSB (CC0/MSB ignored) */ + FLUID_BANK_STYLE_MMA /**< MMA style bank = 128*MSB+LSB */ +}; + +enum fluid_synth_status +{ + FLUID_SYNTH_CLEAN, + FLUID_SYNTH_PLAYING, + FLUID_SYNTH_QUIET, + FLUID_SYNTH_STOPPED +}; + +#define SYNTH_REVERB_CHANNEL 0 +#define SYNTH_CHORUS_CHANNEL 1 + +/* + * fluid_synth_t + * + * Mutual exclusion notes (as of 1.1.2): + * + * All variables are considered belongning to the "public API" thread, + * which processes all MIDI, except for: + * + * ticks_since_start - atomic, set by rendering thread only + * cpu_load - atomic, set by rendering thread only + * cur, curmax, dither_index - used by rendering thread only + * ladspa_fx - same instance copied in rendering thread. Synchronising handled internally. + * + */ + +struct _fluid_synth_t +{ + fluid_rec_mutex_t mutex; /**< Lock for public API */ + int use_mutex; /**< Use mutex for all public API functions? */ + int public_api_count; /**< How many times the mutex is currently locked */ + + fluid_settings_t *settings; /**< the synthesizer settings */ + int device_id; /**< Device ID used for SYSEX messages */ + int polyphony; /**< Maximum polyphony */ + int with_reverb; /**< Should the synth use the built-in reverb unit? */ + int with_chorus; /**< Should the synth use the built-in chorus unit? */ + int verbose; /**< Turn verbose mode on? */ + double sample_rate; /**< The sample rate */ + int midi_channels; /**< the number of MIDI channels (>= 16) */ + int bank_select; /**< the style of Bank Select MIDI messages */ + int audio_channels; /**< the number of audio channels (1 channel=left+right) */ + int audio_groups; /**< the number of (stereo) 'sub'groups from the synth. + Typically equal to audio_channels. */ + int effects_channels; /**< the number of effects channels (>= 2) */ + int effects_groups; /**< the number of effects units (>= 1) */ + int state; /**< the synthesizer state */ + fluid_atomic_uint_t ticks_since_start; /**< the number of audio samples since the start */ + unsigned int start; /**< the start in msec, as returned by system clock */ + fluid_overflow_prio_t overflow; /**< parameters for overflow priority (aka voice-stealing) */ + + fluid_list_t *loaders; /**< the SoundFont loaders */ + fluid_list_t *sfont; /**< List of fluid_sfont_info_t for each loaded SoundFont (remains until SoundFont is unloaded) */ + int sfont_id; /**< Incrementing ID assigned to each loaded SoundFont */ + fluid_list_t *fonts_to_be_unloaded; /**< list of timers that try to unload a soundfont */ + + float gain; /**< master gain */ + fluid_channel_t **channel; /**< the channels */ + int nvoice; /**< the length of the synthesis process array (max polyphony allowed) */ + fluid_voice_t **voice; /**< the synthesis voices */ + int active_voice_count; /**< count of active voices */ + unsigned int noteid; /**< the id is incremented for every new note. it's used for noteoff's */ + unsigned int storeid; + int fromkey_portamento; /**< fromkey portamento */ + fluid_rvoice_eventhandler_t *eventhandler; + + /**< Shadow of reverb parameter: roomsize, damping, width, level */ + double reverb_param[FLUID_REVERB_PARAM_LAST]; + + /**< Shadow of chorus parameter: chorus number, level, speed, depth, type */ + double chorus_param[FLUID_CHORUS_PARAM_LAST]; + + int cur; /**< the current sample in the audio buffers to be output */ + int curmax; /**< current amount of samples present in the audio buffers */ + int dither_index; /**< current index in random dither value buffer: fluid_synth_(write_s16|dither_s16) */ + + fluid_atomic_float_t cpu_load; /**< CPU load in percent (CPU time required / audio synthesized time * 100) */ + + fluid_tuning_t ***tuning; /**< 128 banks of 128 programs for the tunings */ + fluid_private_t tuning_iter; /**< Tuning iterators per each thread */ + + fluid_sample_timer_t *sample_timers; /**< List of timers triggered before a block is processed */ + unsigned int min_note_length_ticks; /**< If note-offs are triggered just after a note-on, they will be delayed */ + + int cores; /**< Number of CPU cores (1 by default) */ + + fluid_mod_t *default_mod; /**< the (dynamic) list of default modulators */ + + fluid_ladspa_fx_t *ladspa_fx; /**< Effects unit for LADSPA support */ + enum fluid_iir_filter_type custom_filter_type; /**< filter type of the user-defined filter currently used for all voices */ + enum fluid_iir_filter_flags custom_filter_flags; /**< filter type of the user-defined filter currently used for all voices */ +}; + +/** + * Type definition of the synthesizer's audio callback function. + * @param synth FluidSynth instance + * @param len Count of audio frames to synthesize + * @param out1 Array to store left channel of audio to + * @param loff Offset index in 'out1' for first sample + * @param lincr Increment between samples stored to 'out1' + * @param out2 Array to store right channel of audio to + * @param roff Offset index in 'out2' for first sample + * @param rincr Increment between samples stored to 'out2' + */ +typedef int (*fluid_audio_callback_t)(fluid_synth_t *synth, int len, + void *out1, int loff, int lincr, + void *out2, int roff, int rincr); + +typedef int (*fluid_audio_channels_callback_t)(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]); + +int +fluid_synth_write_float_channels_LOCAL(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[], + int (*block_render_func)(fluid_synth_t *, int)); + +int +fluid_synth_write_s16_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]); +int +fluid_synth_write_float_channels(fluid_synth_t *synth, int len, + int channels_count, + void *channels_out[], int channels_off[], + int channels_incr[]); + +fluid_preset_t *fluid_synth_find_preset(fluid_synth_t *synth, + int banknum, + int prognum); +void fluid_synth_sfont_unref(fluid_synth_t *synth, fluid_sfont_t *sfont); + +void fluid_synth_dither_s16(int *dither_index, int len, const float *lin, const float *rin, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr); + +int fluid_synth_reset_reverb(fluid_synth_t *synth); +int fluid_synth_set_reverb_preset(fluid_synth_t *synth, unsigned int num); +int fluid_synth_reverb_set_param(fluid_synth_t *synth, int fx_group, + int param, + double value); +int fluid_synth_set_reverb_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]); + +int fluid_synth_reset_chorus(fluid_synth_t *synth); +int fluid_synth_chorus_set_param(fluid_synth_t *synth, int fx_group, + int param, double value); +int fluid_synth_set_chorus_full(fluid_synth_t *synth, int fx_group, int set, + const double values[]); + +fluid_sample_timer_t *new_fluid_sample_timer(fluid_synth_t *synth, fluid_timer_callback_t callback, void *data); +void delete_fluid_sample_timer(fluid_synth_t *synth, fluid_sample_timer_t *timer); +void fluid_sample_timer_reset(fluid_synth_t *synth, fluid_sample_timer_t *timer); + +void fluid_synth_process_event_queue(fluid_synth_t *synth); + +int fluid_synth_set_gen2(fluid_synth_t *synth, int chan, + int param, float value, + int absolute, int normalized); + +int +fluid_synth_process_LOCAL(fluid_synth_t *synth, int len, int nfx, float *fx[], + int nout, float *out[], int (*block_render_func)(fluid_synth_t *, int)); +int +fluid_synth_write_float_LOCAL(fluid_synth_t *synth, int len, + void *lout, int loff, int lincr, + void *rout, int roff, int rincr, + int (*block_render_func)(fluid_synth_t *, int)); +/* + * misc + */ +void fluid_synth_settings(fluid_settings_t *settings); +void fluid_synth_set_sample_rate_immediately(fluid_synth_t *synth, float sample_rate); + + +/* extern declared in fluid_synth_monopoly.c */ + +int fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int vel); +int fluid_synth_noteon_mono_LOCAL(fluid_synth_t *synth, int chan, int key, int vel); +int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t *synth, int chan, int key); +int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, int fromkey, int tokey, int vel); +int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, char Mono); + +fluid_voice_t * +fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int chan, int key, int vel, fluid_zone_range_t *zone_range); + +void fluid_synth_release_voice_on_same_note_LOCAL(fluid_synth_t *synth, int chan, int key); +#endif /* _FLUID_SYNTH_H */ diff --git a/thirdparty/fluidsynth/src/synth/fluid_synth_monopoly.c b/thirdparty/fluidsynth/src/synth/fluid_synth_monopoly.c new file mode 100644 index 000000000..d1de13196 --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_synth_monopoly.c @@ -0,0 +1,722 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_synth.h" +#include "fluid_chan.h" +#include "fluid_defsfont.h" + + +/****************************************************************************** + The legato detector is composed as this, + variables: + - monolist: monophonic list variable. + - prev_note: to store the most recent note before adding on noteon or before + removing on noteoff. + - FLUID_CHANNEL_LEGATO_PLAYING: legato/staccato state bit that informs on + legato or staccato playing. + functions: + - fluid_channel_add_monolist(), for inserting a new note. + - fluid_channel_search_monolist(), for searching the position of a note + into the list. + - fluid_channel_remove_monolist(), for removing a note from the list. + + The monophonic list + +------------------------------------------------+ + | +----+ +----+ +----+ +----+ | + | |note| |note| |note| |note| | + +--->|vel |-->|vel |-->....-->|vel |-->|vel |----+ + +----+ +----+ +----+ +----+ + /|\ /|\ + | | + i_first i_last + + The list allows an easy automatic detection of a legato passage when it is + played on a MIDI keyboard input device. + It is useful also when the input device is an ewi (electronic wind instrument) + or evi (electronic valve instrument) and these instruments are unable to send + MIDI CC legato on/off. + + The list memorizes the notes in playing order. + - (a) On noteOn n2, if a previous note n1 exists, there is a legato + detection with n1 (with or without portamento from n1 to n2 See note below). + - (b) On noteOff of the running note n2, if a previous note n1 exists, + there is a legato detection from n2 to n1, allowing fast trills playing + (with or without portamento from n2 to n1. See note below). + + Notes in the list are inserted to the end of the list that works like a + circular buffer.The features are: + + 1) It is always possible to play an infinite legato passage in + direct order (n1_On,n2_On,n3_On,....). + + 2) Playing legato in the reverse order (n10_Off, n9_Off,,...) helps in + fast trills playing as the list memorizes 10 most recent notes. + + 3) Playing an infinite lagato passage in ascendant or descendant order, + without playing trills is always possible using the usual way like this: + First we begin with an ascendant passage, + n1On, (n2On,n1Off), (n3On,n2Off) , (n4On,n3Off), then + we continue with a descendant passage + (n3On,n4off), (n2On,n3off), (n1On,n2off), n1Off...and so on + + Each MIDI channel have a legato detector. + + Note: + Portamento is a feature independent of the legato detector. So + portamento isn't part of the lagato detector. However portamento + (when enabled) is triggered at noteOn (like legato). Like in legato + situation it is usual to have a portamento from a note 'fromkey' to another + note 'tokey'. Portamento fromkey note choice is determined at noteOn by + fluid_synth_get_fromkey_portamento_legato() (see below). + + More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). +******************************************************************************/ + + +/***************************************************************************** + Portamento related functions in Poly or Mono mode +******************************************************************************/ + +/** + * fluid_synth_get_fromkey_portamento_legato returns two information: + * - fromkey note for portamento. + * - fromkey note for legato. + * +-----> fromkey_portamento + * ______|________ + * portamento modes >------->| | + * | get_fromkey | + * Porta.on/off >------------------------->|_______________| + * (PTC) | + * +-----> fromkey_legato + * + * The functions is intended to be call on noteOn mono + * see fluid_synth_noteon_mono_staccato(), fluid_synth_noteon_monopoly_legato() + * ------- + * 1)The function determines if a portamento must occur on next noteOn. + * The value returned is 'fromkey portamento' which is the pitchstart key + * of a portamento, as function of PTC or (default_fromkey, prev_note) both + * if Portamento On. By order of precedence the result is: + * 1.1) PTC have precedence over Portamento On. + * If CC PTC has been received, its value supersedes and any + * portamento pedal On, default_fromkey,prev_note or portamento mode. + * 1.2) Otherwise ,when Portamento On the function takes the following value: + * - default_fromkey if valid + * - otherwise prev_note(prev_note is the note prior the most recent + * note played). + * Then portamento mode is applied to validate the value chosen. + * Where portamento mode is: + * - each note, a portamento occurs on each note. + * - legato only, portamento only on notes played legato. + * - staccato only, portamento only on notes played staccato. + * 1.3) Otherwise, portamento is off,INVALID_NOTE is returned (portamento is disabled). + * ------ + * 2)The function determines if a legato playing must occur on next noteOn. + * 'fromkey legato note' is returned as a function of default_fromkey, PTC, + * current mono/poly mode,actual 'staccato/legato' playing state and prev_note. + * By order of precedence the result is: + * 2.1) If valid, default_fromkey have precedence over any others values. + * 2.2) Otherwise if CC PTC has been received its value is returned. + * 2.3) Otherwise fromkey legato is determined from the mono/poly mode, + * the actual 'staccato/legato' playing state (FLUID_CHANNEL_LEGATO_PLAYING) and prev_note + * as this: + * - in (poly/Mono) staccato , INVALID_NOTE is returned. + * - in poly legato , actually we don't want playing legato. So + * INVALID_NOTE is returned. + * - in mono legato , prev_note is returned. + * + * On input + * @param chan fluid_channel_t. + * @param defaultFromkey, the default 'fromkey portamento' note or 'fromkey legato' + * note (see description above). + * + * @return + * 1)'fromkey portamento' is returned in fluid_synth_t.fromkey_portamento. + * If valid,it means that portamento is enabled . + * + * 2)The 'fromkey legato' note is returned. + * + * Notes about usage: + * The function is intended to be called when the following event occurs: + * - On noteOn (Poly or Mono) after insertion in the monophonic list. + * - On noteOff(mono legato playing). In this case, default_fromkey must be valid. + * + * Typical calling usage: + * - In poly, default_fromkey must be INVALID_NOTE. + * - In mono staccato playing,default_fromkey must be INVALID_NOTE. + * - In mono legato playing,default_fromkey must be valid. + */ +static char fluid_synth_get_fromkey_portamento_legato(fluid_channel_t *chan, + int default_fromkey) +{ + unsigned char ptc = fluid_channel_get_cc(chan, PORTAMENTO_CTRL); + + if(fluid_channel_is_valid_note(ptc)) + { + /* CC PTC has been received */ + fluid_channel_clear_portamento(chan); /* clears the CC PTC receive */ + chan->synth->fromkey_portamento = ptc;/* returns fromkey portamento */ + + /* returns fromkey legato */ + if(!fluid_channel_is_valid_note(default_fromkey)) + { + default_fromkey = ptc; + } + } + else + { + /* determines and returns fromkey portamento */ + unsigned char fromkey_portamento = INVALID_NOTE; + + if(fluid_channel_portamento(chan)) + { + /* Portamento when Portamento pedal is On */ + /* 'fromkey portamento'is determined from the portamento mode + and the most recent note played (prev_note)*/ + enum fluid_channel_portamento_mode portamentomode = chan->portamentomode; + + if(fluid_channel_is_valid_note(default_fromkey)) + { + fromkey_portamento = default_fromkey; /* on each note */ + } + else + { + fromkey_portamento = fluid_channel_prev_note(chan); /* on each note */ + } + + if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_LEGATO_ONLY) + { + /* Mode portamento:legato only */ + if(!(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + { + fromkey_portamento = INVALID_NOTE; + } + } + else if(portamentomode == FLUID_CHANNEL_PORTAMENTO_MODE_STACCATO_ONLY) + { + /* Mode portamento:staccato only */ + if(chan->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { + fromkey_portamento = INVALID_NOTE; + } + } + + /* else Mode portamento: on each note (staccato/legato) */ + } + + /* Returns fromkey portamento */ + chan->synth->fromkey_portamento = fromkey_portamento; + + /* Determines and returns fromkey legato */ + if(!fluid_channel_is_valid_note(default_fromkey)) + { + /* in staccato (poly/Mono) returns INVALID_NOTE */ + /* In mono mode legato playing returns the note prior most + recent note played */ + if(fluid_channel_is_playing_mono(chan) && (chan->mode & FLUID_CHANNEL_LEGATO_PLAYING)) + { + default_fromkey = fluid_channel_prev_note(chan); /* note prior last note */ + } + + /* In poly mode legato playing, actually we don't want playing legato. + So returns INVALID_NOTE */ + } + } + + return default_fromkey; /* Returns legato fromkey */ +} + +/***************************************************************************** + noteon - noteoff functions in Mono mode +******************************************************************************/ +/* + * noteon - noteoff on a channel in "monophonic playing". + * + * A channel needs to be played monophonic if this channel has been set in + * monophonic mode by basic channel API.(see fluid_synth_polymono.c). + * A channel needs also to be played monophonic if it has been set in + * polyphonic mode and legato pedal is On during the playing. + * When a channel is in "monophonic playing" state, only one note at a time can be + * played in a staccato or legato manner (with or without portamento). + * More information in FluidPolyMono-0004.pdf chapter 4 (Appendices). + * _______________ + * ________________ | noteon | + * | legato detector| O-->| mono_staccato |--*-> preset_noteon + * noteon_mono ->| (add_monolist) |--O-- |_______________| | (with or without) + * LOCAL |________________| O /|\ | (portamento) + * /|\ set_onenote | | fromkey | + * | | | portamento| + * noteOn poly >---*------------------* | | + * | | | + * | _____ |________ | + * portamento modes >--- | ->| | | + * | | get_fromkey | | + * Porta.on/off >--------------------- | ->|_______________| | + * (PTC) | | | + * | fromkey | fromkey | + * | legato | portamento| + * | _____\|/_______ | + * *-->| noteon |--/ + * | | monopoly | + * | | legato |----> voices + * legato modes >------- | ->|_______________| triggering + * | (with or without) + * | (portamento) + * | + * | + * noteOff poly >---*----------------- | ---------+ + * | clear | | + * _\|/_____________ | | + * | legato detector | O | + * noteoff_mono->|(search_monolist)|-O-- _____\|/_______ + * LOCAL |(remove_monolist)| O-->| noteoff | + * |_________________| | monopoly |----> noteoff + * Sust.on/off >------------------------->|_______________| + * Sost.on/off +------------------------------------------------------------------------------*/ + +/** + * Plays a noteon event for a Synth instance in "monophonic playing" state. + * Please see the description above about "monophonic playing". + * _______________ + * ________________ | noteon | + * | legato detector| O-->| mono_staccato |--->preset_noteon + * noteon_mono ->| (add_monolist) |--O-- |_______________| + * LOCAL |________________| O + * | + * | + * | + * | + * | + * | + * | + * | + * | + * | _______________ + * | | noteon | + * +-->| monopoly | + * | legato |---> voices + * |_______________| triggering + * + * The function uses the legato detector (see above) to determine if the note must + * be played staccato or legato. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int fluid_synth_noteon_mono_LOCAL(fluid_synth_t *synth, int chan, + int key, int vel) +{ + fluid_channel_t *channel = synth->channel[chan]; + + /* Adds the note into the monophonic list */ + fluid_channel_add_monolist(channel, key, vel, 0); + + /* in Breath Sync mode, the noteon triggering is postponed + until the musician starts blowing in the breath controller */ + if(!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) || + fluid_channel_breath_msb(channel)) + { + /* legato/staccato playing detection */ + if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { + /* legato playing */ + /* legato from prev_note to key */ + /* the voices from prev_note key number are to be used to play key number */ + /* fromkey must be valid */ + return fluid_synth_noteon_monopoly_legato(synth, chan, + fluid_channel_prev_note(channel), key, vel); + } + else + { + /* staccato playing */ + return fluid_synth_noteon_mono_staccato(synth, chan, key, vel); + } + } + else + { + return FLUID_OK; + } +} + +/** + * Plays a noteoff event for a Synth instance in "monophonic playing" state. + * Please see the description above about "monophonic playing" + * + * _______________ + * | noteon | + * +-->| monopoly | + * | | legato |----> voices + * | |_______________| triggering + * | (with or without) + * | (portamento) + * | + * | + * | + * | + * | + * | + * _________________ | + * | legato detector | O + * noteoff_mono->|(search_monolist)|-O-- _______________ + * LOCAL |(remove_monolist)| O-->| noteoff | + * |_________________| | monopoly |----> noteoff + * |_______________| + * + * The function uses the legato detector (see above) to determine if the noteoff must + * be played staccato or legato. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int fluid_synth_noteoff_mono_LOCAL(fluid_synth_t *synth, int chan, int key) +{ + int status; + int i, i_prev; + fluid_channel_t *channel = synth->channel[chan]; + /* searching the note in the monophonic list */ + i = fluid_channel_search_monolist(channel, key, &i_prev); + + if(i >= 0) + { + /* the note is in the monophonic list */ + /* Removes the note from the monophonic list */ + fluid_channel_remove_monolist(channel, i, &i_prev); + + /* in Breath Sync mode, the noteoff triggering is done + if the musician is blowing in the breath controller */ + if(!(channel->mode & FLUID_CHANNEL_BREATH_SYNC) || + fluid_channel_breath_msb(channel)) + { + /* legato playing detection */ + if(channel->mode & FLUID_CHANNEL_LEGATO_PLAYING) + { + /* the list contains others notes */ + if(i_prev >= 0) + { + /* legato playing detection on noteoff */ + /* legato from key to i_prev key */ + /* the voices from key number are to be used to + play i_prev key number. */ + status = fluid_synth_noteon_monopoly_legato(synth, chan, + key, channel->monolist[i_prev].note, + channel->monolist[i_prev].vel); + } + /* else the note doesn't need to be played off */ + else + { + status = FLUID_OK; + } + } + else + { + /* the monophonic list is empty */ + /* plays the monophonic note noteoff and eventually held + by sustain/sostenuto */ + status = fluid_synth_noteoff_monopoly(synth, chan, key, 1); + } + } + else + { + status = FLUID_OK; + } + } + else + { + /* the note is not found in the list so the note was + played On when the channel was in polyphonic playing */ + /* plays the noteoff as for polyphonic */ + status = fluid_synth_noteoff_monopoly(synth, chan, key, 0); + } + + return status; +} + +/*---------------------------------------------------------------------------- + staccato playing +-----------------------------------------------------------------------------*/ +/** + * Plays noteon for a monophonic note in staccato manner. + * Please see the description above about "monophonic playing". + * _______________ + * | noteon | + * noteon_mono >------------------------>| mono_staccato |----> preset_noteon + * |_______________| (with or without) + * LOCAL /|\ (portamento) + * | fromkey + * | portamento + * | + * | + * ______|________ + * portamento modes >----->| | + * | get_fromkey | + * Porta.on/off >----------------------->|_______________| + * Portamento + * (PTC) + * + * We are in staccato situation (where no previous note have been depressed). + * Before the note been passed to fluid_preset_noteon(), the function must determine + * the from_key_portamento parameter used by fluid_preset_noteon(). + * + * from_key_portamento is returned by fluid_synth_get_fromkey_portamento_legato() function. + * fromkey_portamento is set to valid/invalid key value depending of the portamento + * modes (see portamento mode API) , CC portamento On/Off , and CC portamento control + * (PTC). + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + */ +int +fluid_synth_noteon_mono_staccato(fluid_synth_t *synth, int chan, int key, int vel) +{ + fluid_channel_t *channel = synth->channel[chan]; + + /* Before playing a new note, if a previous monophonic note is currently + sustained it needs to be released */ + fluid_synth_release_voice_on_same_note_LOCAL(synth, chan, channel->key_mono_sustained); + /* Get possible 'fromkey portamento' */ + fluid_synth_get_fromkey_portamento_legato(channel, INVALID_NOTE); + /* The note needs to be played by voices allocation */ + return fluid_preset_noteon(channel->preset, synth, chan, key, vel); +} + +/** + * Plays noteoff for a polyphonic or monophonic note + * Please see the description above about "monophonic playing". + * + * + * noteOff poly >---------------------------------+ + * | + * | + * | + * noteoff_mono _____\|/_______ + * LOCAL >------------------------->| noteoff | + * | monopoly |----> noteoff + * Sust.on/off >------------------------->|_______________| + * Sost.on/off + * + * The function has the same behaviour when the noteoff is poly of mono, except + * that for mono noteoff, if any pedal (sustain or sostenuto ) is depressed, the + * key is memorized. This is necessary when the next mono note will be played + * staccato, as any current mono note currently sustained will need to be released + * (see fluid_synth_noteon_mono_staccato()). + * Note also that for a monophonic legato passage, the function is called only when + * the last noteoff of the passage occurs. That means that if sustain or sostenuto + * is depressed, only the last note of a legato passage will be sustained. + * + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param key MIDI note number (0-127). + * @param Mono, 1 noteoff on monophonic note. + * 0 noteoff on polyphonic note. + * @return FLUID_OK on success, FLUID_FAILED otherwise. + * + * Note: On return, on monophonic, possible sustained note is memorized in + * key_mono_sustained. Memorization is done here on noteOff. + */ +int fluid_synth_noteoff_monopoly(fluid_synth_t *synth, int chan, int key, + char Mono) +{ + int status = FLUID_FAILED; + fluid_voice_t *voice; + int i; + fluid_channel_t *channel = synth->channel[chan]; + + /* Key_sustained is prepared to return no note sustained (INVALID_NOTE) */ + if(Mono) + { + channel->key_mono_sustained = INVALID_NOTE; /* no mono note sustained */ + } + + /* noteoff for all voices with same chan and same key */ + for(i = 0; i < synth->polyphony; i++) + { + voice = synth->voice[i]; + + if(fluid_voice_is_on(voice) && + fluid_voice_get_channel(voice) == chan && + fluid_voice_get_key(voice) == key) + { + if(synth->verbose) + { + int used_voices = 0; + int k; + + for(k = 0; k < synth->polyphony; k++) + { + if(!_AVAILABLE(synth->voice[k])) + { + used_voices++; + } + } + + FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%d", + fluid_voice_get_channel(voice), fluid_voice_get_key(voice), 0, + fluid_voice_get_id(voice), + (fluid_curtime() - synth->start) / 1000.0f, + used_voices); + } /* if verbose */ + + fluid_voice_noteoff(voice); + + /* noteoff on monophonic note */ + /* Key memorization if the note is sustained */ + if(Mono && + (fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice))) + { + channel->key_mono_sustained = key; + } + + status = FLUID_OK; + } /* if voice on */ + } /* for all voices */ + + return status; +} + +/*---------------------------------------------------------------------------- + legato playing +-----------------------------------------------------------------------------*/ +/** + * Plays noteon for a monophonic note played legato. + * Please see the description above about "monophonic playing". + * + * + * _______________ + * portamento modes >----->| | + * | get_fromkey | + * Porta.on/off >----------------------->|_______________| + * Portamento | + * (PTC) | +-->preset_noteon + * fromkey | fromkey | (with or without) + * legato | portamento| (portamento) + * _____\|/_______ | + * | noteon |--+ + * noteon_mono >------------------------>| monopoly | + * LOCAL | legato |----->voices + * |_______________| triggering + * /|\ (with or without) + * | (portamento) + * legato modes >-----------------+ + * + * We are in legato situation (where a previous note has been depressed). + * The function must determine the from_key_portamento and from_key_legato parameters + * used respectively by fluid_preset_noteon() function and voices triggering functions. + * + * from_key_portamento and from_key_legato are returned by + * fluid_synth_get_fromkey_portamento_legato() function. + * fromkey_portamento is set to valid/invalid key value depending of the portamento + * modes (see portamento mode API), CC portamento On/Off, and CC portamento control + * (PTC). + * Then, depending of the legato modes (see legato mode API), the function will call + * the appropriate triggering functions. + * @param synth instance. + * @param chan MIDI channel number (0 to MIDI channel count - 1). + * @param fromkey MIDI note number (0-127). + * @param tokey MIDI note number (0-127). + * @param vel MIDI velocity (0-127). + * @return FLUID_OK on success, FLUID_FAILED otherwise. + * + * Note: The voices with key 'fromkey' are to be used to play key 'tokey'. + * The function is able to play legato through Preset Zone(s) (PZ) and + * Instrument Zone(s) (IZ) as far as possible. + * When key tokey is outside the current Instrument Zone, Preset Zone, + * current 'fromkey' voices are released. If necessary new voices + * are restarted when tokey enters inside new Instrument(s) Zones,Preset Zone(s). + * More information in FluidPolyMono-0004.pdf chapter 4.7 (Appendices). + */ +int fluid_synth_noteon_monopoly_legato(fluid_synth_t *synth, int chan, + int fromkey, int tokey, int vel) +{ + fluid_channel_t *channel = synth->channel[chan]; + enum fluid_channel_legato_mode legatomode = channel->legatomode; + fluid_voice_t *voice; + int i ; + /* Gets possible 'fromkey portamento' and possible 'fromkey legato' note */ + fromkey = fluid_synth_get_fromkey_portamento_legato(channel, fromkey); + + if(fluid_channel_is_valid_note(fromkey)) + { + for(i = 0; i < synth->polyphony; i++) + { + /* searching fromkey voices: only those who don't have 'note off' */ + voice = synth->voice[i]; + + if(fluid_voice_is_on(voice) && + fluid_voice_get_channel(voice) == chan && + fluid_voice_get_key(voice) == fromkey) + { + fluid_zone_range_t *zone_range = voice->zone_range; + + /* Ignores voice when there is no instrument zone (i.e no zone_range). Otherwise + checks if tokey is inside the range of the running voice */ + if(zone_range && fluid_zone_inside_range(zone_range, tokey, vel)) + { + switch(legatomode) + { + case FLUID_CHANNEL_LEGATO_MODE_RETRIGGER: /* mode 0 */ + fluid_voice_release(voice); /* normal release */ + break; + + case FLUID_CHANNEL_LEGATO_MODE_MULTI_RETRIGGER: /* mode 1 */ + /* Skip in attack section */ + fluid_voice_update_multi_retrigger_attack(voice, tokey, vel); + + /* Starts portamento if enabled */ + if(fluid_channel_is_valid_note(synth->fromkey_portamento)) + { + /* Sends portamento parameters to the voice dsp */ + fluid_voice_update_portamento(voice, + synth->fromkey_portamento, + tokey); + } + + /* The voice is now used to play tokey in legato manner */ + /* Marks this Instrument Zone to be ignored during next + fluid_preset_noteon() */ + zone_range->ignore = TRUE; + break; + + default: /* Invalid mode: this should never happen */ + FLUID_LOG(FLUID_WARN, "Failed to execute legato mode: %d", + legatomode); + return FLUID_FAILED; + } + } + else + { + /* tokey note is outside the voice range, so the voice is released */ + fluid_voice_release(voice); + } + } + } + } + + /* May be,tokey will enter in new others Insrument Zone(s),Preset Zone(s), in + this case it needs to be played by voices allocation */ + return fluid_preset_noteon(channel->preset, synth, chan, tokey, vel); +} diff --git a/thirdparty/fluidsynth/src/synth/fluid_tuning.c b/thirdparty/fluidsynth/src/synth/fluid_tuning.c new file mode 100644 index 000000000..0df248b7b --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_tuning.c @@ -0,0 +1,190 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#include "fluid_tuning.h" +#include "fluid_sys.h" + + +fluid_tuning_t *new_fluid_tuning(const char *name, int bank, int prog) +{ + fluid_tuning_t *tuning; + int i; + + tuning = FLUID_NEW(fluid_tuning_t); + + if(tuning == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(tuning, 0, sizeof(fluid_tuning_t)); + + if(fluid_tuning_set_name(tuning, name) != FLUID_OK) + { + delete_fluid_tuning(tuning); + return NULL; + } + + tuning->bank = bank; + tuning->prog = prog; + + for(i = 0; i < 128; i++) + { + tuning->pitch[i] = i * 100.0; + } + + fluid_atomic_int_set(&tuning->refcount, 1); /* Start with a refcount of 1 */ + + return tuning; +} + +/* Duplicate a tuning */ +fluid_tuning_t * +fluid_tuning_duplicate(fluid_tuning_t *tuning) +{ + fluid_tuning_t *new_tuning; + int i; + + new_tuning = FLUID_NEW(fluid_tuning_t); + + if(!new_tuning) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(new_tuning, 0, sizeof(fluid_tuning_t)); + + if(fluid_tuning_set_name(new_tuning, tuning->name) != FLUID_OK) + { + delete_fluid_tuning(new_tuning); + return NULL; + } + + new_tuning->bank = tuning->bank; + new_tuning->prog = tuning->prog; + + for(i = 0; i < 128; i++) + { + new_tuning->pitch[i] = tuning->pitch[i]; + } + + fluid_atomic_int_set(&new_tuning->refcount, 1); /* Start with a refcount of 1 */ + + return new_tuning; +} + +void +delete_fluid_tuning(fluid_tuning_t *tuning) +{ + fluid_return_if_fail(tuning != NULL); + + FLUID_FREE(tuning->name); + FLUID_FREE(tuning); +} + +/* Add a reference to a tuning object */ +void +fluid_tuning_ref(fluid_tuning_t *tuning) +{ + fluid_return_if_fail(tuning != NULL); + + fluid_atomic_int_inc(&tuning->refcount); +} + +/* Unref a tuning object, when it reaches 0 it is deleted, returns TRUE if deleted */ +int +fluid_tuning_unref(fluid_tuning_t *tuning, int count) +{ + fluid_return_val_if_fail(tuning != NULL, FALSE); + + /* Add and compare are separate, but that is OK, since refcount will only + * reach 0 when there are no references and therefore no possibility of + * another thread adding a reference in between */ + fluid_atomic_int_add(&tuning->refcount, -count); + + /* Delete when refcount reaches 0 */ + if(!fluid_atomic_int_get(&tuning->refcount)) + { + delete_fluid_tuning(tuning); + return TRUE; + } + else + { + return FALSE; + } +} + +int fluid_tuning_set_name(fluid_tuning_t *tuning, const char *name) +{ + if(tuning->name != NULL) + { + FLUID_FREE(tuning->name); + tuning->name = NULL; + } + + if(name != NULL) + { + tuning->name = FLUID_STRDUP(name); + + if(tuning->name == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + } + + return FLUID_OK; +} + +char *fluid_tuning_get_name(fluid_tuning_t *tuning) +{ + return tuning->name; +} + +void fluid_tuning_set_octave(fluid_tuning_t *tuning, const double *pitch_deriv) +{ + int i; + + for(i = 0; i < 128; i++) + { + tuning->pitch[i] = i * 100.0 + pitch_deriv[i % 12]; + } +} + +void fluid_tuning_set_all(fluid_tuning_t *tuning, const double *pitch) +{ + int i; + + for(i = 0; i < 128; i++) + { + tuning->pitch[i] = pitch[i]; + } +} + +void fluid_tuning_set_pitch(fluid_tuning_t *tuning, int key, double pitch) +{ + if((key >= 0) && (key < 128)) + { + tuning->pitch[key] = pitch; + } +} diff --git a/thirdparty/fluidsynth/src/synth/fluid_tuning.h b/thirdparty/fluidsynth/src/synth/fluid_tuning.h new file mode 100644 index 000000000..542d2ced6 --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_tuning.h @@ -0,0 +1,69 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/* + + More information about micro tuning can be found at: + + https://www.midi.org/about-midi/tuning.htm + https://www.midi.org/about-midi/tuning-scale.htm + https://www.midi.org/about-midi/tuning_extens.htm + +*/ + +#ifndef _FLUID_TUNING_H +#define _FLUID_TUNING_H + +#include "fluidsynth_priv.h" + +struct _fluid_tuning_t +{ + char *name; + int bank; + int prog; + double pitch[128]; /* the pitch of every key, in cents */ + fluid_atomic_int_t refcount; /* Tuning reference count */ +}; + +fluid_tuning_t *new_fluid_tuning(const char *name, int bank, int prog); +void delete_fluid_tuning(fluid_tuning_t *tuning); +fluid_tuning_t *fluid_tuning_duplicate(fluid_tuning_t *tuning); +void fluid_tuning_ref(fluid_tuning_t *tuning); +int fluid_tuning_unref(fluid_tuning_t *tuning, int count); + +int fluid_tuning_set_name(fluid_tuning_t *tuning, const char *name); +char *fluid_tuning_get_name(fluid_tuning_t *tuning); + +#define fluid_tuning_get_bank(_t) ((_t)->bank) +#define fluid_tuning_get_prog(_t) ((_t)->prog) + +void fluid_tuning_set_pitch(fluid_tuning_t *tuning, int key, double pitch); +#define fluid_tuning_get_pitch(_t, _key) ((_t)->pitch[_key]) + +void fluid_tuning_set_octave(fluid_tuning_t *tuning, const double *pitch_deriv); + +void fluid_tuning_set_all(fluid_tuning_t *tuning, const double *pitch); +#define fluid_tuning_get_all(_t) (&(_t)->pitch[0]) + + + + +#endif /* _FLUID_TUNING_H */ diff --git a/thirdparty/fluidsynth/src/synth/fluid_voice.c b/thirdparty/fluidsynth/src/synth/fluid_voice.c new file mode 100644 index 000000000..e827f9ff2 --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_voice.c @@ -0,0 +1,2052 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sys.h" +#include "fluid_voice.h" +#include "fluid_mod.h" +#include "fluid_chan.h" +#include "fluid_conv.h" +#include "fluid_synth.h" +#include "fluid_sys.h" +#include "fluid_sfont.h" +#include "fluid_rvoice_event.h" +#include "fluid_defsfont.h" + +/* used for filter turn off optimization - if filter cutoff is above the + specified value and filter q is below the other value, turn filter off */ +#define FLUID_MAX_AUDIBLE_FILTER_FC 19000.0f +#define FLUID_MIN_AUDIBLE_FILTER_Q 1.2f + +/* min vol envelope release (to stop clicks) in SoundFont timecents */ +#define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */ + + +static const int32_t INT24_MAX = (1 << (16 + 8 - 1)); + +static int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t *voice); +static int calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base, + int gen_key2base, int is_decay); +static fluid_real_t +fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice); + +#define UPDATE_RVOICE0(proc) \ + do { \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, voice->rvoice, param); \ + } while (0) + +#define UPDATE_RVOICE_GENERIC_R1(proc, obj, rarg) \ + do { \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].real = rarg; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ + } while (0) + +#define UPDATE_RVOICE_GENERIC_I1(proc, obj, iarg) \ + do { \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].i = iarg; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ + } while (0) + +#define UPDATE_RVOICE_GENERIC_I2(proc, obj, iarg1, iarg2) \ + do { \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].i = iarg1; \ + param[1].i = iarg2; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ + } while (0) + +#define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \ + do { \ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; \ + param[0].i = iarg; \ + param[1].real = rarg; \ + fluid_rvoice_eventhandler_push(voice->eventhandler, proc, obj, param); \ + } while (0) + + +#define UPDATE_RVOICE_R1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, voice->rvoice, arg1) +#define UPDATE_RVOICE_I1(proc, arg1) UPDATE_RVOICE_GENERIC_I1(proc, voice->rvoice, arg1) + +#define UPDATE_RVOICE_BUFFERS_AMP(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->buffers, iarg, rarg) +#define UPDATE_RVOICE_ENVLFO_R1(proc, envp, rarg) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->envlfo.envp, rarg) +#define UPDATE_RVOICE_ENVLFO_I1(proc, envp, iarg) UPDATE_RVOICE_GENERIC_I1(proc, &voice->rvoice->envlfo.envp, iarg) + +static FLUID_INLINE void +fluid_voice_update_volenv(fluid_voice_t *voice, + int enqueue, + fluid_adsr_env_section_t section, + unsigned int count, + fluid_real_t coeff, + fluid_real_t increment, + fluid_real_t min, + fluid_real_t max) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + param[0].i = section; + param[1].i = count; + param[2].real = coeff; + param[3].real = increment; + param[4].real = min; + param[5].real = max; + + if(enqueue) + { + fluid_rvoice_eventhandler_push(voice->eventhandler, + fluid_adsr_env_set_data, + &voice->rvoice->envlfo.volenv, + param); + } + else + { + fluid_adsr_env_set_data(&voice->rvoice->envlfo.volenv, param); + } +} + +static FLUID_INLINE void +fluid_voice_update_modenv(fluid_voice_t *voice, + int enqueue, + fluid_adsr_env_section_t section, + unsigned int count, + fluid_real_t coeff, + fluid_real_t increment, + fluid_real_t min, + fluid_real_t max) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + param[0].i = section; + param[1].i = count; + param[2].real = coeff; + param[3].real = increment; + param[4].real = min; + param[5].real = max; + + if(enqueue) + { + fluid_rvoice_eventhandler_push(voice->eventhandler, + fluid_adsr_env_set_data, + &voice->rvoice->envlfo.modenv, + param); + } + else + { + fluid_adsr_env_set_data(&voice->rvoice->envlfo.modenv, param); + } +} + +static FLUID_INLINE void fluid_voice_sample_unref(fluid_sample_t **sample) +{ + if(*sample != NULL) + { + fluid_sample_decr_ref(*sample); + *sample = NULL; + } +} + +/* + * Swaps the current rvoice with the current overflow_rvoice + */ +static void fluid_voice_swap_rvoice(fluid_voice_t *voice) +{ + fluid_rvoice_t *rtemp = voice->rvoice; + int ctemp = voice->can_access_rvoice; + voice->rvoice = voice->overflow_rvoice; + voice->can_access_rvoice = voice->can_access_overflow_rvoice; + voice->overflow_rvoice = rtemp; + voice->can_access_overflow_rvoice = ctemp; + voice->overflow_sample = voice->sample; +} + +static void fluid_voice_initialize_rvoice(fluid_voice_t *voice, fluid_real_t output_rate) +{ + fluid_rvoice_param_t param[MAX_EVENT_PARAMS]; + + FLUID_MEMSET(voice->rvoice, 0, sizeof(fluid_rvoice_t)); + + /* The 'sustain' and 'finished' segments of the volume / modulation + * envelope are constant. They are never affected by any modulator + * or generator. Therefore it is enough to initialize them once + * during the lifetime of the synth. + */ + fluid_voice_update_volenv(voice, FALSE, FLUID_VOICE_ENVSUSTAIN, + 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); + fluid_voice_update_volenv(voice, FALSE, FLUID_VOICE_ENVFINISHED, + 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); + fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVSUSTAIN, + 0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f); + fluid_voice_update_modenv(voice, FALSE, FLUID_VOICE_ENVFINISHED, + 0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f); + + param[0].i = FLUID_IIR_LOWPASS; + param[1].i = 0; + fluid_iir_filter_init(&voice->rvoice->resonant_filter, param); + + param[0].i = FLUID_IIR_DISABLED; + fluid_iir_filter_init(&voice->rvoice->resonant_custom_filter, param); + + param[0].real = output_rate; + fluid_rvoice_set_output_rate(voice->rvoice, param); +} + +/* + * new_fluid_voice + */ +fluid_voice_t * +new_fluid_voice(fluid_rvoice_eventhandler_t *handler, fluid_real_t output_rate) +{ + fluid_voice_t *voice; + voice = FLUID_NEW(fluid_voice_t); + + if(voice == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + voice->can_access_rvoice = TRUE; + voice->can_access_overflow_rvoice = TRUE; + + voice->rvoice = FLUID_NEW(fluid_rvoice_t); + voice->overflow_rvoice = FLUID_NEW(fluid_rvoice_t); + + if(voice->rvoice == NULL || voice->overflow_rvoice == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + delete_fluid_voice(voice); + return NULL; + } + + voice->status = FLUID_VOICE_CLEAN; + voice->chan = NO_CHANNEL; + voice->key = 0; + voice->vel = 0; + voice->eventhandler = handler; + voice->channel = NULL; + voice->sample = NULL; + voice->overflow_sample = NULL; + voice->output_rate = output_rate; + + /* Initialize both the rvoice and overflow_rvoice */ + fluid_voice_initialize_rvoice(voice, output_rate); + fluid_voice_swap_rvoice(voice); + fluid_voice_initialize_rvoice(voice, output_rate); + + return voice; +} + +/* + * delete_fluid_voice + */ +void +delete_fluid_voice(fluid_voice_t *voice) +{ + fluid_return_if_fail(voice != NULL); + + if(!voice->can_access_rvoice || !voice->can_access_overflow_rvoice) + { + FLUID_LOG(FLUID_WARN, "Deleting voice %u which has locked rvoices!", voice->id); + } + + FLUID_FREE(voice->overflow_rvoice); + FLUID_FREE(voice->rvoice); + FLUID_FREE(voice); +} + +/* fluid_voice_init + * + * Initialize the synthesis process + * inst_zone, the Instrument Zone contains the sample, Keyrange,Velrange + * of the voice. + * When playing legato (n1,n2) in mono mode, n2 will use n1 voices + * as far as n2 still enters in Keyrange,Velrange of n1. + */ +int +fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample, + fluid_zone_range_t *inst_zone_range, + fluid_channel_t *channel, int key, int vel, unsigned int id, + unsigned int start_time, fluid_real_t gain) +{ + /* Note: The voice parameters will be initialized later, when the + * generators have been retrieved from the sound font. Here, only + * the 'working memory' of the voice (position in envelopes, history + * of IIR filters, position in sample etc) is initialized. */ + int i; + + if(!voice->can_access_rvoice) + { + if(voice->can_access_overflow_rvoice) + { + fluid_voice_swap_rvoice(voice); + } + else + { + FLUID_LOG(FLUID_ERR, "Internal error: Cannot access an rvoice in fluid_voice_init!"); + return FLUID_FAILED; + } + } + + /* We are now guaranteed to have access to the rvoice */ + + if(voice->sample) + { + fluid_voice_off(voice); + } + + voice->zone_range = inst_zone_range; /* Instrument zone range for legato */ + voice->id = id; + voice->chan = fluid_channel_get_num(channel); + voice->key = (unsigned char) key; + voice->vel = (unsigned char) vel; + voice->channel = channel; + voice->mod_count = 0; + voice->start_time = start_time; + voice->has_noteoff = 0; + UPDATE_RVOICE0(fluid_rvoice_reset); + + /* + We increment the reference count of the sample to indicate that this + sample is about to be owned by the rvoice. This will prevent the + unloading of the soundfont while this rvoice is playing. + */ + fluid_sample_incr_ref(sample); + fluid_rvoice_eventhandler_push_ptr(voice->eventhandler, fluid_rvoice_set_sample, voice->rvoice, sample); + voice->sample = sample; + + i = fluid_channel_get_interp_method(channel); + UPDATE_RVOICE_I1(fluid_rvoice_set_interp_method, i); + + /* Set all the generators to their default value, according to SF + * 2.01 section 8.1.3 (page 48). The value of NRPN messages are + * copied from the channel to the voice's generators. The sound font + * loader overwrites them. The generator values are later converted + * into voice parameters in + * fluid_voice_calculate_runtime_synthesis_parameters. */ + fluid_gen_init(&voice->gen[0], channel); + UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, _SAMPLEMODE(voice)); + + voice->synth_gain = gain; + + /* avoid division by zero later*/ + if(voice->synth_gain < 0.0000001f) + { + voice->synth_gain = 0.0000001f; + } + + UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain); + + /* Set up buffer mapping, should be done more flexible in the future. */ + i = 2 * channel->synth->audio_groups; + i += (voice->chan % channel->synth->effects_groups) * channel->synth->effects_channels; + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 2, i + SYNTH_REVERB_CHANNEL); + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 3, i + SYNTH_CHORUS_CHANNEL); + + i = 2 * (voice->chan % channel->synth->audio_groups); + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 0, i); + UPDATE_RVOICE_GENERIC_I2(fluid_rvoice_buffers_set_mapping, &voice->rvoice->buffers, 1, i + 1); + + return FLUID_OK; +} + + +/** + * Update sample rate. + * + * @note If the voice is active, it will be turned off. + */ +void +fluid_voice_set_output_rate(fluid_voice_t *voice, fluid_real_t value) +{ + if(fluid_voice_is_playing(voice)) + { + fluid_voice_off(voice); + } + + voice->output_rate = value; + UPDATE_RVOICE_GENERIC_R1(fluid_rvoice_set_output_rate, voice->rvoice, value); + UPDATE_RVOICE_GENERIC_R1(fluid_rvoice_set_output_rate, voice->overflow_rvoice, value); +} + + +/** + * Set the value of a generator. + * + * @param voice Voice instance + * @param i Generator ID (#fluid_gen_type) + * @param val Generator value + */ +void +fluid_voice_gen_set(fluid_voice_t *voice, int i, float val) +{ + voice->gen[i].val = val; + voice->gen[i].flags = GEN_SET; + + if(i == GEN_SAMPLEMODE) + { + UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, (int) val); + } +} + +/** + * Offset the value of a generator. + * + * @param voice Voice instance + * @param i Generator ID (#fluid_gen_type) + * @param val Value to add to the existing value + */ +void +fluid_voice_gen_incr(fluid_voice_t *voice, int i, float val) +{ + voice->gen[i].val += val; + voice->gen[i].flags = GEN_SET; +} + +/** + * Get the value of a generator. + * + * @param voice Voice instance + * @param gen Generator ID (#fluid_gen_type) + * @return Current generator value + */ +float +fluid_voice_gen_get(fluid_voice_t *voice, int gen) +{ + return voice->gen[gen].val; +} + +fluid_real_t fluid_voice_gen_value(const fluid_voice_t *voice, int num) +{ + return (fluid_real_t)(voice->gen[num].val + voice->gen[num].mod + voice->gen[num].nrpn); +} + +/* + * fluid_voice_start + */ +void fluid_voice_start(fluid_voice_t *voice) +{ + /* The maximum volume of the loop is calculated and cached once for each + * sample with its nominal loop settings. This happens, when the sample is used + * for the first time.*/ + + fluid_voice_calculate_runtime_synthesis_parameters(voice); + +#ifdef WITH_PROFILING + voice->ref = fluid_profile_ref(); +#endif + + voice->status = FLUID_VOICE_ON; + + /* Increment voice count */ + voice->channel->synth->active_voice_count++; +} + +/** + * Calculate the amplitude of a voice. + * + * @param gain The gain value in the range [0.0 ; 1.0] + * @return An amplitude used by rvoice_mixer's buffers + */ +static FLUID_INLINE fluid_real_t +fluid_voice_calculate_gain_amplitude(const fluid_voice_t *voice, fluid_real_t gain) +{ + /* we use 24bit samples in fluid_rvoice_dsp. in order to normalize float + * samples to [0.0;1.0] divide samples by the max. value of an int24 and + * amplify them with the gain */ + return gain * voice->synth_gain / (INT24_MAX * 1.0f); +} + +/* Useful to return the nominal pitch of a key */ +/* The nominal pitch is dependent of voice->root_pitch,tuning, and + GEN_SCALETUNE generator. + This is useful to set the value of GEN_PITCH generator on noteOn. + This is useful to get the beginning/ending pitch for portamento. +*/ +fluid_real_t fluid_voice_calculate_pitch(fluid_voice_t *voice, int key) +{ + fluid_tuning_t *tuning; + fluid_real_t x, pitch; + + /* Now the nominal pitch of the key is returned. + * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a + * non-realtime parameter. So we don't allow modulation (as opposed + * to fluid_voice_gen_value(voice, GEN_SCALETUNE) When the scale tuning is varied, + * one key remains fixed. Here C3 (MIDI number 60) is used. + */ + if(fluid_channel_has_tuning(voice->channel)) + { + tuning = fluid_channel_get_tuning(voice->channel); + x = fluid_tuning_get_pitch(tuning, (int)(voice->root_pitch / 100.0f)); + pitch = voice->gen[GEN_SCALETUNE].val / 100.0f * + (fluid_tuning_get_pitch(tuning, key) - x) + x; + } + else + { + pitch = voice->gen[GEN_SCALETUNE].val + * (key - voice->root_pitch / 100.0f) + voice->root_pitch; + } + + return pitch; +} + +void +fluid_voice_calculate_gen_pitch(fluid_voice_t *voice) +{ + voice->gen[GEN_PITCH].val = fluid_voice_calculate_pitch(voice, fluid_voice_get_actual_key(voice)); +} + +/* + * fluid_voice_calculate_runtime_synthesis_parameters + * + * in this function we calculate the values of all the parameters. the + * parameters are converted to their most useful unit for the DSP + * algorithm, for example, number of samples instead of + * timecents. Some parameters keep their "perceptual" unit and + * conversion will be done in the DSP function. This is the case, for + * example, for the pitch since it is modulated by the controllers in + * cents. */ +static int +fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t *voice) +{ + int i; + unsigned int n; + + static int const list_of_generators_to_initialize[] = + { + GEN_STARTADDROFS, /* SF2.01 page 48 #0 */ + GEN_ENDADDROFS, /* #1 */ + GEN_STARTLOOPADDROFS, /* #2 */ + GEN_ENDLOOPADDROFS, /* #3 */ + /* GEN_STARTADDRCOARSEOFS see comment below [1] #4 */ + GEN_MODLFOTOPITCH, /* #5 */ + GEN_VIBLFOTOPITCH, /* #6 */ + GEN_MODENVTOPITCH, /* #7 */ + GEN_FILTERFC, /* #8 */ + GEN_FILTERQ, /* #9 */ + GEN_MODLFOTOFILTERFC, /* #10 */ + GEN_MODENVTOFILTERFC, /* #11 */ + /* GEN_ENDADDRCOARSEOFS [1] #12 */ + GEN_MODLFOTOVOL, /* #13 */ + /* not defined #14 */ + GEN_CHORUSSEND, /* #15 */ + GEN_REVERBSEND, /* #16 */ + GEN_PAN, /* #17 */ + /* not defined #18 */ + /* not defined #19 */ + /* not defined #20 */ + GEN_MODLFODELAY, /* #21 */ + GEN_MODLFOFREQ, /* #22 */ + GEN_VIBLFODELAY, /* #23 */ + GEN_VIBLFOFREQ, /* #24 */ + GEN_MODENVDELAY, /* #25 */ + GEN_MODENVATTACK, /* #26 */ + GEN_MODENVHOLD, /* #27 */ + GEN_MODENVDECAY, /* #28 */ + /* GEN_MODENVSUSTAIN [1] #29 */ + GEN_MODENVRELEASE, /* #30 */ + /* GEN_KEYTOMODENVHOLD [1] #31 */ + /* GEN_KEYTOMODENVDECAY [1] #32 */ + GEN_VOLENVDELAY, /* #33 */ + GEN_VOLENVATTACK, /* #34 */ + GEN_VOLENVHOLD, /* #35 */ + GEN_VOLENVDECAY, /* #36 */ + /* GEN_VOLENVSUSTAIN [1] #37 */ + GEN_VOLENVRELEASE, /* #38 */ + /* GEN_KEYTOVOLENVHOLD [1] #39 */ + /* GEN_KEYTOVOLENVDECAY [1] #40 */ + /* GEN_STARTLOOPADDRCOARSEOFS [1] #45 */ + GEN_KEYNUM, /* #46 */ + GEN_VELOCITY, /* #47 */ + GEN_ATTENUATION, /* #48 */ + /* GEN_ENDLOOPADDRCOARSEOFS [1] #50 */ + /* GEN_COARSETUNE [1] #51 */ + /* GEN_FINETUNE [1] #52 */ + GEN_OVERRIDEROOTKEY, /* #58 */ + GEN_PITCH, /* --- */ + GEN_CUSTOM_BALANCE, /* --- */ + GEN_CUSTOM_FILTERFC, /* --- */ + GEN_CUSTOM_FILTERQ /* --- */ + }; + + /* When the voice is made ready for the synthesis process, a lot of + * voice-internal parameters have to be calculated. + * + * At this point, the sound font has already set the -nominal- value + * for all generators (excluding GEN_PITCH). Most generators can be + * modulated - they include a nominal value and an offset (which + * changes with velocity, note number, channel parameters like + * aftertouch, mod wheel...) Now this offset will be calculated as + * follows: + * + * - Process each modulator once. + * - Calculate its output value. + * - Find the target generator. + * - Add the output value to the modulation value of the generator. + * + * Note: The generators have been initialized with + * fluid_gen_init(). + */ + + for(i = 0; i < voice->mod_count; i++) + { + fluid_mod_t *mod = &voice->mod[i]; + fluid_real_t modval = fluid_mod_get_value(mod, voice); + int dest_gen_index = mod->dest; + fluid_gen_t *dest_gen = &voice->gen[dest_gen_index]; + dest_gen->mod += modval; + /* fluid_dump_modulator(mod); */ + } + + /* Now the generators are initialized, nominal and modulation value. + * The voice parameters (which depend on generators) are calculated + * with fluid_voice_update_param. Processing the list of generator + * changes will calculate each voice parameter once. + * + * Note [1]: Some voice parameters depend on several generators. For + * example, the pitch depends on GEN_COARSETUNE, GEN_FINETUNE and + * GEN_PITCH. voice->pitch. Unnecessary recalculation is avoided + * by removing all but one generator from the list of voice + * parameters. Same with GEN_XXX and GEN_XXXCOARSE: the + * initialisation list contains only GEN_XXX. + */ + + /* Calculate the voice parameter(s) dependent on each generator. */ + for(n = 0; n < FLUID_N_ELEMENTS(list_of_generators_to_initialize); n++) + { + fluid_voice_update_param(voice, list_of_generators_to_initialize[n]); + } + + /* Start portamento if enabled */ + { + /* fromkey note comes from "GetFromKeyPortamentoLegato()" detector. + When fromkey is set to ValidNote , portamento is started */ + /* Return fromkey portamento */ + int fromkey = voice->channel->synth->fromkey_portamento; + + if(fluid_channel_is_valid_note(fromkey)) + { + /* Send portamento parameters to the voice dsp */ + fluid_voice_update_portamento(voice, fromkey, fluid_voice_get_actual_key(voice)); + } + } + + /* Make an estimate on how loud this voice can get at any time (attenuation). */ + UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB, + fluid_voice_get_lower_boundary_for_attenuation(voice)); + return FLUID_OK; +} + +/* + * calculate_hold_decay_buffers + */ +static int +calculate_hold_decay_buffers(fluid_voice_t *voice, int gen_base, + int gen_key2base, int is_decay) +{ + /* Purpose: + * + * Returns the number of DSP loops, that correspond to the hold + * (is_decay=0) or decay (is_decay=1) time. + * gen_base=GEN_VOLENVHOLD, GEN_VOLENVDECAY, GEN_MODENVHOLD, + * GEN_MODENVDECAY gen_key2base=GEN_KEYTOVOLENVHOLD, + * GEN_KEYTOVOLENVDECAY, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY + */ + + fluid_real_t keysteps; + fluid_real_t timecents; + fluid_real_t seconds; + int buffers; + + /* SF2.01 section 8.4.3 # 31, 32, 39, 40 + * GEN_KEYTOxxxENVxxx uses key 60 as 'origin'. + * The unit of the generator is timecents per key number. + * If KEYTOxxxENVxxx is 100, a key one octave over key 60 (72) + * will cause (60-72)*100=-1200 timecents of time variation. + * The time is cut in half. + */ + + keysteps = 60.0f - fluid_channel_get_key_pitch(voice->channel, fluid_voice_get_actual_key(voice)) / 100.0f; + timecents = fluid_voice_gen_value(voice, gen_base) + fluid_voice_gen_value(voice, gen_key2base) * keysteps; + + /* Range checking */ + if(is_decay) + { + /* SF 2.01 section 8.1.3 # 28, 36 */ + if(timecents > 8000.f) + { + timecents = 8000.f; + } + } + else + { + /* SF 2.01 section 8.1.3 # 27, 35 */ + if(timecents > 5000.f) + { + timecents = 5000.f; + } + + /* SF 2.01 section 8.1.2 # 27, 35: + * The most negative number indicates no hold time + */ + if(timecents <= -32768.f) + { + return 0; + } + } + + /* SF 2.01 section 8.1.3 # 27, 28, 35, 36 */ + if(timecents < -12000.f) + { + timecents = -12000.f; + } + + seconds = fluid_tc2sec(timecents); + /* Each DSP loop processes FLUID_BUFSIZE samples. */ + + /* round to next full number of buffers */ + buffers = (int)(((fluid_real_t)voice->output_rate * seconds) + / (fluid_real_t)FLUID_BUFSIZE + + 0.5f); + + return buffers; +} + +/* + * The value of a generator (gen) has changed. (The different + * generators are listed in fluidsynth.h, or in SF2.01 page 48-49) + * Now the dependent 'voice' parameters are calculated. + * + * fluid_voice_update_param can be called during the setup of the + * voice (to calculate the initial value for a voice parameter), or + * during its operation (a generator has been changed due to + * real-time parameter modifications like pitch-bend). + * + * Note: The generator holds three values: The base value .val, an + * offset caused by modulators .mod, and an offset caused by the + * NRPN system. fluid_voice_gen_value(voice, generator_enumerator) returns the sum + * of all three. + */ + +/** + * Update all the synthesis parameters which depend on generator \a gen. + * + * @param voice Voice instance + * @param gen Generator id (#fluid_gen_type) + * + * Calling this function is only necessary after changing a generator of an already playing voice. + */ +void +fluid_voice_update_param(fluid_voice_t *voice, int gen) +{ + unsigned int count, z; + fluid_real_t x = fluid_voice_gen_value(voice, gen); + + switch(gen) + { + + case GEN_PAN: + case GEN_CUSTOM_BALANCE: + /* range checking is done in the fluid_pan and fluid_balance functions */ + voice->pan = fluid_voice_gen_value(voice, GEN_PAN); + voice->balance = fluid_voice_gen_value(voice, GEN_CUSTOM_BALANCE); + + /* left amp */ + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0, + fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1))); + + /* right amp */ + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1, + fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0))); + break; + + case GEN_ATTENUATION: + voice->attenuation = x; + + /* Range: SF2.01 section 8.1.3 # 48 + * Motivation for range checking: + * OHPiano.SF2 sets initial attenuation to a whooping -96 dB */ + fluid_clip(voice->attenuation, 0.f, 1440.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_attenuation, voice->attenuation); + break; + + /* The pitch is calculated from three different generators. + * Read comment in fluidsynth.h about GEN_PITCH. + */ + case GEN_PITCH: + case GEN_COARSETUNE: + case GEN_FINETUNE: + /* The testing for allowed range is done in 'fluid_ct2hz' */ + voice->pitch = (fluid_voice_gen_value(voice, GEN_PITCH) + + 100.0f * fluid_voice_gen_value(voice, GEN_COARSETUNE) + + fluid_voice_gen_value(voice, GEN_FINETUNE)); + UPDATE_RVOICE_R1(fluid_rvoice_set_pitch, voice->pitch); + break; + + case GEN_REVERBSEND: + /* The generator unit is 'tenths of a percent'. */ + voice->reverb_send = x / 1000.0f; + fluid_clip(voice->reverb_send, 0.f, 1.f); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send)); + break; + + case GEN_CHORUSSEND: + /* The generator unit is 'tenths of a percent'. */ + voice->chorus_send = x / 1000.0f; + fluid_clip(voice->chorus_send, 0.f, 1.f); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send)); + break; + + case GEN_OVERRIDEROOTKEY: + + /* This is a non-realtime parameter. Therefore the .mod part of the generator + * can be neglected. + * NOTE: origpitch sets MIDI root note while pitchadj is a fine tuning amount + * which offsets the original rate. This means that the fine tuning is + * inverted with respect to the root note (so subtract it, not add). + */ + if(voice->sample != NULL) + { + if(voice->gen[GEN_OVERRIDEROOTKEY].val > -1) //FIXME: use flag instead of -1 + { + voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f + - voice->sample->pitchadj; + } + else + { + voice->root_pitch = voice->sample->origpitch * 100.0f - voice->sample->pitchadj; + } + + x = (fluid_ct2hz_real(voice->root_pitch) * ((fluid_real_t) voice->output_rate / voice->sample->samplerate)); + } + else + { + if(voice->gen[GEN_OVERRIDEROOTKEY].val > -1) //FIXME: use flag instead of -1 + { + voice->root_pitch = voice->gen[GEN_OVERRIDEROOTKEY].val * 100.0f; + } + else + { + voice->root_pitch = 0; + } + + x = fluid_ct2hz_real(voice->root_pitch); + } + + /* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */ + fluid_voice_calculate_gen_pitch(voice); + UPDATE_RVOICE_R1(fluid_rvoice_set_root_pitch_hz, x); + + break; + + case GEN_FILTERFC: + /* The resonance frequency is converted from absolute cents to + * midicents .val and .mod are both used, this permits real-time + * modulation. The allowed range is tested in the 'fluid_ct2hz' + * function [PH,20021214] + */ + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_filter, x); + break; + + case GEN_FILTERQ: + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_filter, x); + break; + + /* same as the two above, only for the custom filter */ + case GEN_CUSTOM_FILTERFC: + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_fres, &voice->rvoice->resonant_custom_filter, x); + break; + + case GEN_CUSTOM_FILTERQ: + UPDATE_RVOICE_GENERIC_R1(fluid_iir_filter_set_q, &voice->rvoice->resonant_custom_filter, x); + break; + + case GEN_MODLFOTOPITCH: + fluid_clip(x, -12000.f, 12000.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x); + break; + + case GEN_MODLFOTOVOL: + fluid_clip(x, -960.f, 960.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x); + break; + + case GEN_MODLFOTOFILTERFC: + fluid_clip(x, -12000.f, 12000.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x); + break; + + case GEN_MODLFODELAY: + fluid_clip(x, -12000.0f, 5000.0f); + z = (unsigned int)(voice->output_rate * fluid_tc2sec_delay(x)); + UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, modlfo, z); + break; + + case GEN_MODLFOFREQ: + /* - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples + * - the delay into a sample delay + */ + fluid_clip(x, -16000.0f, 4500.0f); + x = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate); + UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, modlfo, x); + break; + + case GEN_VIBLFOFREQ: + /* vib lfo + * + * - the frequency is converted into a delta value, per buffer of FLUID_BUFSIZE samples + * - the delay into a sample delay + */ + fluid_clip(x, -16000.0f, 4500.0f); + x = 4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate; + UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, viblfo, x); + break; + + case GEN_VIBLFODELAY: + fluid_clip(x, -12000.0f, 5000.0f); + z = (unsigned int)(voice->output_rate * fluid_tc2sec_delay(x)); + UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, viblfo, z); + break; + + case GEN_VIBLFOTOPITCH: + fluid_clip(x, -12000.f, 12000.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_viblfo_to_pitch, x); + break; + + case GEN_KEYNUM: + /* GEN_KEYNUM: SF2.01 page 46, item 46 + * + * If this generator is active, it forces the key number to its + * value. Non-realtime controller. + * + * There is a flag, which should indicate, whether a generator is + * enabled or not. But here we rely on the default value of -1. + */ + + /* 2017-09-02: do not change the voice's key here, otherwise it will + * never be released on a noteoff event + */ +#if 0 + x = fluid_voice_gen_value(voice, GEN_KEYNUM); + + if(x >= 0) + { + voice->key = x; + } + +#endif + break; + + case GEN_VELOCITY: + /* GEN_VELOCITY: SF2.01 page 46, item 47 + * + * If this generator is active, it forces the velocity to its + * value. Non-realtime controller. + * + * There is a flag, which should indicate, whether a generator is + * enabled or not. But here we rely on the default value of -1. + */ + /* 2017-09-02: do not change the voice's velocity here, use + * fluid_voice_get_actual_velocity() to get the value of this generator + * if active. + */ +#if 0 + x = fluid_voice_gen_value(voice, GEN_VELOCITY); + + if(x > 0) + { + voice->vel = x; + } + +#endif + break; + + case GEN_MODENVTOPITCH: + fluid_clip(x, -12000.f, 12000.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x); + break; + + case GEN_MODENVTOFILTERFC: + /* Range: SF2.01 section 8.1.3 # 1 + * Motivation for range checking: + * Filter is reported to make funny noises now and then + */ + fluid_clip(x, -12000.f, 12000.f); + UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_fc, x); + break; + + + /* sample start and ends points + * + * Range checking is initiated via the + * voice->check_sample_sanity flag, + * because it is impossible to check here: + * During the voice setup, all modulators are processed, while + * the voice is inactive. Therefore, illegal settings may + * occur during the setup (for example: First move the loop + * end point ahead of the loop start point => invalid, then + * move the loop start point forward => valid again. + */ + case GEN_STARTADDROFS: /* SF2.01 section 8.1.3 # 0 */ + case GEN_STARTADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 4 */ + if(voice->sample != NULL) + { + fluid_real_t start_fine = fluid_voice_gen_value(voice, GEN_STARTADDROFS); + fluid_real_t start_coar = fluid_voice_gen_value(voice, GEN_STARTADDRCOARSEOFS); + + z = voice->sample->start + (int)start_fine + 32768 * (int)start_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_start, z); + } + + break; + + case GEN_ENDADDROFS: /* SF2.01 section 8.1.3 # 1 */ + case GEN_ENDADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 12 */ + if(voice->sample != NULL) + { + fluid_real_t end_fine = fluid_voice_gen_value(voice, GEN_ENDADDROFS); + fluid_real_t end_coar = fluid_voice_gen_value(voice, GEN_ENDADDRCOARSEOFS); + + z = voice->sample->end + (int)end_fine + 32768 * (int)end_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_end, z); + } + + break; + + case GEN_STARTLOOPADDROFS: /* SF2.01 section 8.1.3 # 2 */ + case GEN_STARTLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 45 */ + if(voice->sample != NULL) + { + fluid_real_t lstart_fine = fluid_voice_gen_value(voice, GEN_STARTLOOPADDROFS); + fluid_real_t lstart_coar = fluid_voice_gen_value(voice, GEN_STARTLOOPADDRCOARSEOFS); + + z = voice->sample->loopstart + (int)lstart_fine + 32768 * (int)lstart_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_loopstart, z); + } + + break; + + case GEN_ENDLOOPADDROFS: /* SF2.01 section 8.1.3 # 3 */ + case GEN_ENDLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 50 */ + if(voice->sample != NULL) + { + fluid_real_t lend_fine = fluid_voice_gen_value(voice, GEN_ENDLOOPADDROFS); + fluid_real_t lend_coar = fluid_voice_gen_value(voice, GEN_ENDLOOPADDRCOARSEOFS); + + z = voice->sample->loopend + (int)lend_fine + 32768 * (int)lend_coar; + UPDATE_RVOICE_I1(fluid_rvoice_set_loopend, z); + } + + break; + + /* Conversion functions differ in range limit */ +#define NUM_BUFFERS_DELAY(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_delay(_v) / FLUID_BUFSIZE) +#define NUM_BUFFERS_ATTACK(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_attack(_v) / FLUID_BUFSIZE) +#define NUM_BUFFERS_RELEASE(_v) (unsigned int) (voice->output_rate * fluid_tc2sec_release(_v) / FLUID_BUFSIZE) + + /* volume envelope + * + * - delay and hold times are converted to absolute number of samples + * - sustain is converted to its absolute value + * - attack, decay and release are converted to their increment per sample + */ + case GEN_VOLENVDELAY: /* SF2.01 section 8.1.3 # 33 */ + fluid_clip(x, -12000.0f, 5000.0f); + count = NUM_BUFFERS_DELAY(x); + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDELAY, + count, 0.0f, 0.0f, -1.0f, 1.0f); + break; + + case GEN_VOLENVATTACK: /* SF2.01 section 8.1.3 # 34 */ + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_ATTACK(x); + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVATTACK, + count, 1.0f, 1.0f / count, -1.0f, 1.0f); + break; + + case GEN_VOLENVHOLD: /* SF2.01 section 8.1.3 # 35 */ + case GEN_KEYTOVOLENVHOLD: /* SF2.01 section 8.1.3 # 39 */ + count = calculate_hold_decay_buffers(voice, GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0); /* 0 means: hold */ + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVHOLD, + count, 1.0f, 0.0f, -1.0f, 2.0f); + break; + + case GEN_VOLENVDECAY: /* SF2.01 section 8.1.3 # 36 */ + case GEN_VOLENVSUSTAIN: /* SF2.01 section 8.1.3 # 37 */ + case GEN_KEYTOVOLENVDECAY: /* SF2.01 section 8.1.3 # 40 */ + x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_VOLENVSUSTAIN); + fluid_clip(x, 0.0f, 1.0f); + count = calculate_hold_decay_buffers(voice, GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */ + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVDECAY, + count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f); + break; + + case GEN_VOLENVRELEASE: /* SF2.01 section 8.1.3 # 38 */ + fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f); + count = 1 + NUM_BUFFERS_RELEASE(x); + fluid_voice_update_volenv(voice, TRUE, FLUID_VOICE_ENVRELEASE, + count, 1.0f, -1.0f / count, 0.0f, 1.0f); + break; + + /* Modulation envelope */ + case GEN_MODENVDELAY: /* SF2.01 section 8.1.3 # 25 */ + fluid_clip(x, -12000.0f, 5000.0f); + count = NUM_BUFFERS_DELAY(x); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVDELAY, + count, 0.0f, 0.0f, -1.0f, 1.0f); + break; + + case GEN_MODENVATTACK: /* SF2.01 section 8.1.3 # 26 */ + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_ATTACK(x); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVATTACK, + count, 1.0f, 1.0f / count, -1.0f, 1.0f); + break; + + case GEN_MODENVHOLD: /* SF2.01 section 8.1.3 # 27 */ + case GEN_KEYTOMODENVHOLD: /* SF2.01 section 8.1.3 # 31 */ + count = calculate_hold_decay_buffers(voice, GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0); /* 1 means: hold */ + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVHOLD, + count, 1.0f, 0.0f, -1.0f, 2.0f); + break; + + case GEN_MODENVDECAY: /* SF 2.01 section 8.1.3 # 28 */ + case GEN_MODENVSUSTAIN: /* SF 2.01 section 8.1.3 # 29 */ + case GEN_KEYTOMODENVDECAY: /* SF 2.01 section 8.1.3 # 32 */ + count = calculate_hold_decay_buffers(voice, GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1); /* 1 for decay */ + x = 1.0f - 0.001f * fluid_voice_gen_value(voice, GEN_MODENVSUSTAIN); + fluid_clip(x, 0.0f, 1.0f); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVDECAY, + count, 1.0f, count ? -1.0f / count : 0.0f, x, 2.0f); + break; + + case GEN_MODENVRELEASE: /* SF 2.01 section 8.1.3 # 30 */ + fluid_clip(x, -12000.0f, 8000.0f); + count = 1 + NUM_BUFFERS_RELEASE(x); + fluid_voice_update_modenv(voice, TRUE, FLUID_VOICE_ENVRELEASE, + count, 1.0f, -1.0f / count, 0.0f, 2.0f); + + break; + + } /* switch gen */ +} + +/** + * Recalculate voice parameters for a given control. + * + * @param voice the synthesis voice + * @param cc flag to distinguish between a continuous control and a channel control (pitch bend, ...) + * @param ctrl the control number: + * when >=0, only modulators's destination having ctrl as source are updated. + * when -1, all modulators's destination are updated (regardless of ctrl). + * + * In this implementation, I want to make sure that all controllers + * are event based: the parameter values of the DSP algorithm should + * only be updates when a controller event arrived and not at every + * iteration of the audio cycle (which would probably be feasible if + * the synth was made in silicon). + * + * The update is done in two steps: + * + * - step 1: first, we look for all the modulators that have the changed + * controller as a source. This will yield a generator that will be changed + * because of the controller event. + * + * - step 2: For this generator, calculate its new value. This is the + * sum of its original value plus the values of all the attached modulators. + * The generator flag is set to indicate the parameters must be updated. + * This avoid the risk to call 'fluid_voice_update_param' several + * times for the same generator if several modulators have that generator as + * destination. So every changed generators are updated only once. + */ + + /* bit table for each generator being updated. The bits are packed in variables + Each variable have NBR_BIT_BY_VAR bits represented by NBR_BIT_BY_VAR_LN2. + The size of the table is the number of variables: SIZE_UPDATED_GEN_BIT. + + Note: In this implementation NBR_BIT_BY_VAR_LN2 is set to 5 (convenient for 32 bits cpu) + but this could be set to 6 for 64 bits cpu. + */ + +#define NBR_BIT_BY_VAR_LN2 5 /* for 32 bits variables */ +#define NBR_BIT_BY_VAR (1 << NBR_BIT_BY_VAR_LN2) +#define NBR_BIT_BY_VAR_ANDMASK (NBR_BIT_BY_VAR - 1) +#define SIZE_UPDATED_GEN_BIT ((GEN_LAST + NBR_BIT_BY_VAR_ANDMASK) / NBR_BIT_BY_VAR) + +#define is_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] & (1 << (gen & NBR_BIT_BY_VAR_ANDMASK))) +#define set_gen_updated(bit,gen) (bit[gen >> NBR_BIT_BY_VAR_LN2] |= (1 << (gen & NBR_BIT_BY_VAR_ANDMASK))) + +int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl) +{ + int i, k; + fluid_mod_t *mod; + uint32_t gen; + fluid_real_t modval; + + /* Clears registered bits table of updated generators */ + uint32_t updated_gen_bit[SIZE_UPDATED_GEN_BIT] = {0}; + + /* printf("Chan=%d, CC=%d, Src=%d, Val=%d\n", voice->channel->channum, cc, ctrl, val); */ + + for(i = 0; i < voice->mod_count; i++) + { + mod = &voice->mod[i]; + + /* step 1: find all the modulators that have the changed controller + as input source. When ctrl is -1 all modulators destination + are updated */ + if(ctrl < 0 || fluid_mod_has_source(mod, cc, ctrl)) + { + gen = fluid_mod_get_dest(mod); + + /* Skip if this generator has already been updated */ + if(!is_gen_updated(updated_gen_bit, gen)) + { + modval = 0.0; + + /* step 2: for every attached modulator, calculate the modulation + * value for the generator gen */ + for(k = 0; k < voice->mod_count; k++) + { + if(fluid_mod_has_dest(&voice->mod[k], gen)) + { + modval += fluid_mod_get_value(&voice->mod[k], voice); + } + } + + fluid_gen_set_mod(&voice->gen[gen], modval); + + /* now recalculate the parameter values that are derived from the + generator */ + fluid_voice_update_param(voice, gen); + + /* set the bit that indicates this generator is updated */ + set_gen_updated(updated_gen_bit, gen); + } + } + } + + return FLUID_OK; +} + +/** + * Update all the modulators. + * + * This function is called after a ALL_CTRL_OFF MIDI message has been received (CC 121). + * All destinations of all modulators will be updated. + */ +int fluid_voice_modulate_all(fluid_voice_t *voice) +{ + return fluid_voice_modulate(voice, 0, -1); +} + +/* legato update functions --------------------------------------------------*/ + +/* Updates voice portamento parameters + * + * @voice voice the synthesis voice + * @fromkey the beginning pitch of portamento. + * @tokey the ending pitch of portamento. + * + * The function calculates pitch offset and increment, then these parameters + * are send to the dsp. +*/ +void fluid_voice_update_portamento(fluid_voice_t *voice, int fromkey, int tokey) + +{ + fluid_channel_t *channel = voice->channel; + + /* calculates pitch offset */ + fluid_real_t PitchBeg = fluid_voice_calculate_pitch(voice, fromkey); + fluid_real_t PitchEnd = fluid_voice_calculate_pitch(voice, tokey); + fluid_real_t pitchoffset = PitchBeg - PitchEnd; + + /* Calculates increment countinc */ + /* Increment is function of PortamentoTime (ms)*/ + unsigned int countinc = (unsigned int)(((fluid_real_t)voice->output_rate * + 0.001f * + (fluid_real_t)fluid_channel_portamentotime(channel)) / + (fluid_real_t)FLUID_BUFSIZE + 0.5f); + + /* Send portamento parameters to the voice dsp */ + UPDATE_RVOICE_GENERIC_IR(fluid_rvoice_set_portamento, voice->rvoice, countinc, pitchoffset); +} + +/*---------------------------------------------------------------*/ +/*legato mode 1: multi_retrigger + * + * Modulates all generators dependent of key,vel. + * Forces the voice envelopes in the attack section (legato mode 1). + * + * @voice voice the synthesis voice + * @tokey the new key to be applied to this voice. + * @vel the new velocity to be applied to this voice. + */ +void fluid_voice_update_multi_retrigger_attack(fluid_voice_t *voice, + int tokey, int vel) +{ + voice->key = tokey; /* new note */ + voice->vel = vel; /* new velocity */ + /* Updates generators dependent of velocity */ + /* Modulates GEN_ATTENUATION (and others ) before calling + fluid_rvoice_multi_retrigger_attack().*/ + fluid_voice_modulate(voice, FALSE, FLUID_MOD_VELOCITY); + + /* Updates generator dependent of voice->key */ + fluid_voice_update_param(voice, GEN_KEYTOMODENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOMODENVDECAY); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVHOLD); + fluid_voice_update_param(voice, GEN_KEYTOVOLENVDECAY); + + /* Updates pitch generator */ + fluid_voice_calculate_gen_pitch(voice); + fluid_voice_update_param(voice, GEN_PITCH); + + /* updates adsr generator */ + UPDATE_RVOICE0(fluid_rvoice_multi_retrigger_attack); +} +/** end of legato update functions */ + +/* + Force the voice into release stage. Useful anywhere a voice + needs to be damped even if pedals (sustain sostenuto) are depressed. + See fluid_synth_damp_voices_by_sustain_LOCAL(), + fluid_synth_damp_voices_by_sostenuto_LOCAL, + fluid_voice_noteoff(). +*/ +void +fluid_voice_release(fluid_voice_t *voice) +{ + unsigned int at_tick = fluid_channel_get_min_note_length_ticks(voice->channel); + UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick); + voice->has_noteoff = 1; // voice is marked as noteoff occurred +} + +/* + * fluid_voice_noteoff + * + * Sending a noteoff event will advance the envelopes to section 5 (release). + * The function is convenient for polyphonic or monophonic note + */ +void +fluid_voice_noteoff(fluid_voice_t *voice) +{ + fluid_channel_t *channel; + + fluid_profile(FLUID_PROF_VOICE_NOTE, voice->ref, 0, 0); + + channel = voice->channel; + + /* Sustain a note under Sostenuto pedal */ + if(fluid_channel_sostenuto(channel) && + channel->sostenuto_orderid > voice->id) + { + // Sostenuto depressed after note + voice->status = FLUID_VOICE_HELD_BY_SOSTENUTO; + } + /* Or sustain a note under Sustain pedal */ + else if(fluid_channel_sustained(channel)) + { + voice->status = FLUID_VOICE_SUSTAINED; + } + /* Or force the voice to release stage */ + else + { + fluid_voice_release(voice); + } +} + +/* + * fluid_voice_kill_excl + * + * Percussion sounds can be mutually exclusive: for example, a 'closed + * hihat' sound will terminate an 'open hihat' sound ringing at the + * same time. This behaviour is modeled using 'exclusive classes', + * turning on a voice with an exclusive class other than 0 will kill + * all other voices having that exclusive class within the same preset + * or channel. fluid_voice_kill_excl gets called, when 'voice' is to + * be killed for that reason. + */ + +int +fluid_voice_kill_excl(fluid_voice_t *voice) +{ + + unsigned int at_tick; + + if(!fluid_voice_is_playing(voice)) + { + return FLUID_OK; + } + + /* Turn off the exclusive class information for this voice, + so that it doesn't get killed twice + */ + fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, 0); + + /* Speed up the volume envelope */ + /* The value was found through listening tests with hi-hat samples. */ + fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, -200); + fluid_voice_update_param(voice, GEN_VOLENVRELEASE); + + /* Speed up the modulation envelope */ + fluid_voice_gen_set(voice, GEN_MODENVRELEASE, -200); + fluid_voice_update_param(voice, GEN_MODENVRELEASE); + + at_tick = fluid_channel_get_min_note_length_ticks(voice->channel); + UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick); + + + return FLUID_OK; +} + +/* + * Unlock the overflow rvoice of the voice. + * Decrement the reference count of the sample owned by this rvoice. + * + * Called by fluid_synth when the overflow rvoice has finished by itself. + * Must be called also explicitly at synth destruction to ensure that + * the soundfont be unloaded successfully. + */ +void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice) +{ + voice->can_access_overflow_rvoice = 1; + + /* Decrement the reference count of the sample to indicate + that this sample isn't owned by the rvoice anymore */ + fluid_voice_sample_unref(&voice->overflow_sample); +} + +/* + * fluid_voice_off + * + * Force the voice into finished stage. Useful anywhere a voice + * needs to be cancelled from MIDI API. + */ +void fluid_voice_off(fluid_voice_t *voice) +{ + UPDATE_RVOICE0(fluid_rvoice_voiceoff); /* request to finish the voice */ +} + +/* + * fluid_voice_stop + * + * Purpose: + * Turns off a voice, meaning that it is not processed anymore by the + * DSP loop, i.e. contrary part to fluid_voice_start(). + */ +void +fluid_voice_stop(fluid_voice_t *voice) +{ + fluid_profile(FLUID_PROF_VOICE_RELEASE, voice->ref, 0, 0); + + voice->chan = NO_CHANNEL; + + /* Decrement the reference count of the sample, to indicate + that this sample isn't owned by the rvoice anymore. + */ + fluid_voice_sample_unref(&voice->sample); + + voice->status = FLUID_VOICE_OFF; + voice->has_noteoff = 1; + + /* Decrement voice count */ + voice->channel->synth->active_voice_count--; +} + +/** + * Adds a modulator to the voice if the modulator has valid sources. + * + * @param voice Voice instance. + * @param mod Modulator info (copied). + * @param mode Determines how to handle an existing identical modulator. + * #FLUID_VOICE_ADD to add (offset) the modulator amounts, + * #FLUID_VOICE_OVERWRITE to replace the modulator, + * #FLUID_VOICE_DEFAULT when adding a default modulator - no duplicate should + * exist so don't check. + */ +void +fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode) +{ + /* Ignore the modulator if its sources inputs are invalid */ + if(fluid_mod_check_sources(mod, "api fluid_voice_add_mod mod")) + { + fluid_voice_add_mod_local(voice, mod, mode, FLUID_NUM_MOD); + } +} + +/** + * Adds a modulator to the voice. + * local version of fluid_voice_add_mod function. Called at noteon time. + * @param voice, mod, mode, same as for fluid_voice_add_mod() (see above). + * @param check_limit_count is the modulator number limit to handle with existing + * identical modulator(i.e mode FLUID_VOICE_OVERWRITE, FLUID_VOICE_ADD). + * - When FLUID_NUM_MOD, all the voices modulators (since the previous call) + * are checked for identity. + * - When check_count_limit is below the actual number of voices modulators + * (voice->mod_count), this will restrict identity check to this number, + * This is useful when we know by advance that there is no duplicate with + * modulators at index above this limit. This avoid wasting cpu cycles at noteon. + */ +void +fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count) +{ + int i; + + /* check_limit_count cannot be above voice->mod_count */ + if(check_limit_count > voice->mod_count) + { + check_limit_count = voice->mod_count; + } + + if(mode == FLUID_VOICE_ADD) + { + + /* if identical modulator exists, add them */ + for(i = 0; i < check_limit_count; i++) + { + if(fluid_mod_test_identity(&voice->mod[i], mod)) + { + // printf("Adding modulator...\n"); + voice->mod[i].amount += mod->amount; + return; + } + } + + } + else if(mode == FLUID_VOICE_OVERWRITE) + { + + /* if identical modulator exists, replace it (only the amount has to be changed) */ + for(i = 0; i < check_limit_count; i++) + { + if(fluid_mod_test_identity(&voice->mod[i], mod)) + { + // printf("Replacing modulator...amount is %f\n",mod->amount); + voice->mod[i].amount = mod->amount; + return; + } + } + } + + /* Add a new modulator (No existing modulator to add / overwrite). + Also, default modulators (FLUID_VOICE_DEFAULT) are added without + checking, if the same modulator already exists. */ + if(voice->mod_count < FLUID_NUM_MOD) + { + fluid_mod_clone(&voice->mod[voice->mod_count++], mod); + } + else + { + FLUID_LOG(FLUID_WARN, "Voice %i has more modulators than supported, ignoring.", voice->id); + } +} + +/** + * Get the unique ID of the noteon-event. + * + * @param voice Voice instance + * @return Note on unique ID + * + * A SoundFont loader may store pointers to voices it has created for + * real-time control during the operation of a voice (for example: parameter + * changes in SoundFont editor). The synth uses a pool of voices internally which are + * 'recycled' and never deallocated. + * + * However, before modifying an existing voice, check + * - that its state is still 'playing' + * - that the ID is still the same + * + * Otherwise the voice has finished playing. + */ +unsigned int fluid_voice_get_id(const fluid_voice_t *voice) +{ + return voice->id; +} + +/** + * Check if a voice is producing sound. + * + * Like fluid_voice_is_on() this will return TRUE once a call to + * fluid_synth_start_voice() has been made. Contrary to fluid_voice_is_on(), + * this might also return TRUE after the voice received a noteoff event, as it may + * still be playing in release phase, or because it has been sustained or + * sostenuto'ed. + * + * @param voice Voice instance + * @return TRUE if playing, FALSE otherwise + */ +int fluid_voice_is_playing(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_ON) + || fluid_voice_is_sustained(voice) + || fluid_voice_is_sostenuto(voice); + +} + +/** + * Check if a voice is ON. + * + * A voice is in ON state as soon as a call to fluid_synth_start_voice() has been made + * (which is typically done in a fluid_preset_t's noteon function). + * A voice stays ON as long as it has not received a noteoff event. + * + * @param voice Voice instance + * @return TRUE if on, FALSE otherwise + * + * @since 1.1.7 + */ +int fluid_voice_is_on(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_ON && !voice->has_noteoff); +} + +/** + * Check if a voice keeps playing after it has received a noteoff due to being held by sustain. + * + * @param voice Voice instance + * @return TRUE if sustained, FALSE otherwise + * + * @since 1.1.7 + */ +int fluid_voice_is_sustained(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_SUSTAINED); +} + +/** + * Check if a voice keeps playing after it has received a noteoff due to being held by sostenuto. + * + * @param voice Voice instance + * @return TRUE if sostenuto, FALSE otherwise + * + * @since 1.1.7 + */ +int fluid_voice_is_sostenuto(const fluid_voice_t *voice) +{ + return (voice->status == FLUID_VOICE_HELD_BY_SOSTENUTO); +} + +/** + * Return the MIDI channel the voice is playing on. + * + * @param voice Voice instance + * @return The channel assigned to this voice + * + * @note The result of this function is only valid if the voice is playing. + * + * @since 1.1.7 + */ +int fluid_voice_get_channel(const fluid_voice_t *voice) +{ + return voice->chan; +} + +/** + * Return the effective MIDI key of the playing voice. + * + * @param voice Voice instance + * @return The MIDI key this voice is playing at + * + * If the voice was started from an instrument which uses a fixed key generator, it returns that. + * Otherwise returns the same value as \c fluid_voice_get_key. + * + * @note The result of this function is only valid if the voice is playing. + * + * @since 1.1.7 + */ +int fluid_voice_get_actual_key(const fluid_voice_t *voice) +{ + fluid_real_t x = fluid_voice_gen_value(voice, GEN_KEYNUM); + + if(x >= 0) + { + return (int)x; + } + else + { + return fluid_voice_get_key(voice); + } +} + +/** + * Return the MIDI key from the starting noteon event. + * + * @param voice Voice instance + * @return The MIDI key of the noteon event that originally turned on this voice + * + * @note The result of this function is only valid if the voice is playing. + * + * @since 1.1.7 + */ +int fluid_voice_get_key(const fluid_voice_t *voice) +{ + return voice->key; +} + +/** + * Return the effective MIDI velocity of the playing voice. + * + * @param voice Voice instance + * @return The MIDI velocity this voice is playing at + * + * If the voice was started from an instrument which uses a fixed velocity generator, it returns that. + * Otherwise it returns the same value as \c fluid_voice_get_velocity. + * + * @note The result of this function is only valid if the voice is playing. + * + * @since 1.1.7 + */ +int fluid_voice_get_actual_velocity(const fluid_voice_t *voice) +{ + fluid_real_t x = fluid_voice_gen_value(voice, GEN_VELOCITY); + + if(x > 0) + { + return (int)x; + } + else + { + return fluid_voice_get_velocity(voice); + } +} + +/** + * Return the MIDI velocity from the starting noteon event. + * + * @param voice Voice instance + * @return The MIDI velocity which originally turned on this voice + * + * @note The result of this function is only valid if the voice is playing. + * + * @since 1.1.7 + */ +int fluid_voice_get_velocity(const fluid_voice_t *voice) +{ + return voice->vel; +} + +/* + * fluid_voice_get_lower_boundary_for_attenuation + * + * Purpose: + * + * A lower boundary for the attenuation (as in 'the minimum + * attenuation of this voice, with volume pedals, modulators + * etc. resulting in minimum attenuation, cannot fall below x cB) is + * calculated. This has to be called during fluid_voice_start, after + * all modulators have been run on the voice once. Also, + * voice->attenuation has to be initialized. + * (see fluid_voice_calculate_runtime_synthesis_parameters()) + */ +static fluid_real_t +fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t *voice) +{ + int i; + fluid_mod_t *mod; + fluid_real_t possible_att_reduction_cB = 0; + fluid_real_t lower_bound; + + for(i = 0; i < voice->mod_count; i++) + { + mod = &voice->mod[i]; + + /* Modulator has attenuation as target and can change over time? */ + if((mod->dest == GEN_ATTENUATION) + && ((mod->flags1 & FLUID_MOD_CC) + || (mod->flags2 & FLUID_MOD_CC) + || (mod->src1 == FLUID_MOD_CHANNELPRESSURE) + || (mod->src1 == FLUID_MOD_KEYPRESSURE) + || (mod->src1 == FLUID_MOD_PITCHWHEEL) + || (mod->src2 == FLUID_MOD_CHANNELPRESSURE) + || (mod->src2 == FLUID_MOD_KEYPRESSURE) + || (mod->src2 == FLUID_MOD_PITCHWHEEL))) + { + + fluid_real_t current_val = fluid_mod_get_value(mod, voice); + /* min_val is the possible minimum value for this modulator. + it depends of 3 things : + 1)the minimum values of src1,src2 (i.e -1 if mapping is bipolar + or 0 if mapping is unipolar). + 2)the sign of amount. + 3)absolute value of amount. + + When at least one source mapping is bipolar: + min_val is -|amount| regardless the sign of amount. + When both sources mapping are unipolar: + min_val is -|amount|, if amount is negative. + min_val is 0, if amount is positive + */ + fluid_real_t min_val = fabs(mod->amount); + + /* Can this modulator produce a negative contribution? */ + if((mod->flags1 & FLUID_MOD_BIPOLAR) + || (mod->flags2 & FLUID_MOD_BIPOLAR) + || (mod->amount < 0)) + { + min_val = -min_val; /* min_val = - |amount|*/ + } + else + { + /* No negative value possible. But still, the minimum contribution is 0. */ + min_val = 0; + } + + /* For example: + * - current_val=100 + * - min_val=-4000 + * - possible reduction contribution of this modulator = current_val - min_val = 4100 + */ + if(current_val > min_val) + { + possible_att_reduction_cB += (current_val - min_val); + } + } + } + + lower_bound = voice->attenuation - possible_att_reduction_cB; + + /* SF2.01 specs do not allow negative attenuation */ + if(lower_bound < 0) + { + lower_bound = 0; + } + + return lower_bound; +} + + + + +int fluid_voice_set_param(fluid_voice_t *voice, int gen, fluid_real_t nrpn_value) +{ + voice->gen[gen].nrpn = nrpn_value; + voice->gen[gen].flags = GEN_SET; + fluid_voice_update_param(voice, gen); + return FLUID_OK; +} + +int fluid_voice_set_gain(fluid_voice_t *voice, fluid_real_t gain) +{ + fluid_real_t left, right, reverb, chorus; + + /* avoid division by zero*/ + if(gain < 0.0000001f) + { + gain = 0.0000001f; + } + + voice->synth_gain = gain; + left = fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 1) * fluid_balance(voice->balance, 1)); + right = fluid_voice_calculate_gain_amplitude(voice, + fluid_pan(voice->pan, 0) * fluid_balance(voice->balance, 0)); + reverb = fluid_voice_calculate_gain_amplitude(voice, voice->reverb_send); + chorus = fluid_voice_calculate_gain_amplitude(voice, voice->chorus_send); + + UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 0, left); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 1, right); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 2, reverb); + UPDATE_RVOICE_BUFFERS_AMP(fluid_rvoice_buffers_set_amp, 3, chorus); + + return FLUID_OK; +} + +/* - Scan the loop + * - determine the peak level + * - Calculate, what factor will make the loop inaudible + * - Store in sample + */ + +/** + * Calculate the peak volume of a sample for voice off optimization. + * + * @param s Sample to optimize + * @return #FLUID_OK on success, #FLUID_FAILED otherwise + * + * If the peak volume during the loop is known, then the voice can + * be released earlier during the release phase. Otherwise, the + * voice will operate (inaudibly), until the envelope is at the + * nominal turnoff point. So it's a good idea to call + * fluid_voice_optimize_sample() on each sample once. + */ +int +fluid_voice_optimize_sample(fluid_sample_t *s) +{ + int32_t peak_max = 0; + int32_t peak_min = 0; + int32_t peak; + fluid_real_t normalized_amplitude_during_loop; + double result; + unsigned int i; + + /* ignore disabled samples */ + if(s->start == s->end) + { + return (FLUID_OK); + } + + if(!s->amplitude_that_reaches_noise_floor_is_valid) /* Only once */ + { + /* Scan the loop */ + for(i = s->loopstart; i < s->loopend; i++) + { + int32_t val = fluid_rvoice_get_sample(s->data, s->data24, i); + + if(val > peak_max) + { + peak_max = val; + } + else if(val < peak_min) + { + peak_min = val; + } + } + + /* Determine the peak level */ + if(peak_max > -peak_min) + { + peak = peak_max; + } + else + { + peak = -peak_min; + } + + if(peak == 0) + { + /* Avoid division by zero */ + peak = 1; + } + + /* Calculate what factor will make the loop inaudible + * For example: Take a peak of 3277 (10 % of 32768). The + * normalized amplitude is 0.1 (10 % of 32768). An amplitude + * factor of 0.0001 (as opposed to the default 0.00001) will + * drop this sample to the noise floor. + */ + + /* 16 bits => 96+4=100 dB dynamic range => 0.00001 */ + normalized_amplitude_during_loop = ((fluid_real_t)peak) / (INT24_MAX * 1.0f); + result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop; + + /* Store in sample */ + s->amplitude_that_reaches_noise_floor = (double)result; + s->amplitude_that_reaches_noise_floor_is_valid = 1; +#if 0 + printf("Sample peak detection: factor %f\n", (double)result); +#endif + } + + return FLUID_OK; +} + +float +fluid_voice_get_overflow_prio(fluid_voice_t *voice, + fluid_overflow_prio_t *score, + unsigned int cur_time) +{ + float this_voice_prio = 0; + int channel; + + /* Are we already overflowing? */ + if(!voice->can_access_overflow_rvoice) + { + return OVERFLOW_PRIO_CANNOT_KILL; + } + + /* Is this voice on the drum channel? + * Then it is very important. + * Also skip the released and sustained scores. + */ + if(voice->channel->channel_type == CHANNEL_TYPE_DRUM) + { + this_voice_prio += score->percussion; + } + else if(voice->has_noteoff) + { + /* Noteoff has */ + this_voice_prio += score->released; + } + else if(fluid_voice_is_sustained(voice) || fluid_voice_is_sostenuto(voice)) + { + /* This voice is still active, since the sustain pedal is held down. + * Consider it less important than non-sustained channels. + * This decision is somehow subjective. But usually the sustain pedal + * is used to play 'more-voices-than-fingers', so it shouldn't hurt + * if we kill one voice. + */ + this_voice_prio += score->sustained; + } + + /* We are not enthusiastic about releasing voices, which have just been started. + * Otherwise hitting a chord may result in killing notes belonging to that very same + * chord. So give newer voices a higher score. */ + if(score->age) + { + cur_time -= voice->start_time; + + if(cur_time < 1) + { + cur_time = 1; // Avoid div by zero + } + + this_voice_prio += (score->age * voice->output_rate) / cur_time; + } + + /* take a rough estimate of loudness into account. Louder voices are more important. */ + if(score->volume) + { + fluid_real_t a = voice->attenuation; + + if(voice->has_noteoff) + { + // FIXME: Should take into account where on the envelope we are...? + } + + if(a < 0.1f) + { + a = 0.1f; // Avoid div by zero + } + + this_voice_prio += score->volume / a; + } + + /* Check if this voice is on an important channel. If so, then add the + * score for important channels */ + channel = fluid_voice_get_channel(voice); + + if(channel < score->num_important_channels && score->important_channels[channel]) + { + this_voice_prio += score->important; + } + + return this_voice_prio; +} + + +void fluid_voice_set_custom_filter(fluid_voice_t *voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags) +{ + UPDATE_RVOICE_GENERIC_I2(fluid_iir_filter_init, &voice->rvoice->resonant_custom_filter, type, flags); +} + diff --git a/thirdparty/fluidsynth/src/synth/fluid_voice.h b/thirdparty/fluidsynth/src/synth/fluid_voice.h new file mode 100644 index 000000000..4ce6c2b7a --- /dev/null +++ b/thirdparty/fluidsynth/src/synth/fluid_voice.h @@ -0,0 +1,198 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_VOICE_H +#define _FLUID_VOICE_H + +#include "fluid_phase.h" +#include "fluid_gen.h" +#include "fluid_mod.h" +#include "fluid_iir_filter.h" +#include "fluid_adsr_env.h" +#include "fluid_lfo.h" +#include "fluid_rvoice.h" +#include "fluid_rvoice_event.h" + +#define NO_CHANNEL 0xff + +typedef struct _fluid_overflow_prio_t fluid_overflow_prio_t; + +struct _fluid_overflow_prio_t +{ + float percussion; /**< Is this voice on the drum channel? Then add this score */ + float released; /**< Is this voice in release stage? Then add this score (usually negative) */ + float sustained; /**< Is this voice sustained? Then add this score (usually negative) */ + float volume; /**< Multiply current (or future) volume (a value between 0 and 1) */ + float age; /**< This score will be divided by the number of seconds the voice has lasted */ + float important; /**< This score will be added to all important channels */ + char *important_channels; /**< "important" flags indexed by MIDI channel number */ + int num_important_channels; /**< Number of elements in the important_channels array */ +}; + +enum fluid_voice_status +{ + FLUID_VOICE_CLEAN, + FLUID_VOICE_ON, + FLUID_VOICE_SUSTAINED, /* Sustained by Sustain pedal */ + FLUID_VOICE_HELD_BY_SOSTENUTO, /* Sustained by Sostenuto pedal */ + FLUID_VOICE_OFF +}; + + +/* + * fluid_voice_t + */ +struct _fluid_voice_t +{ + unsigned int id; /* the id is incremented for every new noteon. + it's used for noteoff's */ + unsigned char status; + unsigned char chan; /* the channel number, quick access for channel messages */ + unsigned char key; /* the key of the noteon event, quick access for noteoff */ + unsigned char vel; /* the velocity of the noteon event */ + fluid_channel_t *channel; + fluid_rvoice_eventhandler_t *eventhandler; + fluid_zone_range_t *zone_range; /* instrument zone range*/ + fluid_sample_t *sample; /* Pointer to sample (dupe in rvoice) */ + fluid_sample_t *overflow_sample; /* Pointer to sample (dupe in overflow_rvoice) */ + + unsigned int start_time; + int mod_count; + fluid_mod_t mod[FLUID_NUM_MOD]; + fluid_gen_t gen[GEN_LAST]; + + /* basic parameters */ + fluid_real_t output_rate; /* the sample rate of the synthesizer (dupe in rvoice) */ + + /* basic parameters */ + fluid_real_t pitch; /* the pitch in midicents (dupe in rvoice) */ + fluid_real_t attenuation; /* the attenuation in centibels (dupe in rvoice) */ + fluid_real_t root_pitch; + + /* master gain (dupe in rvoice) */ + fluid_real_t synth_gain; + + /* pan */ + fluid_real_t pan; + + /* balance */ + fluid_real_t balance; + + /* reverb */ + fluid_real_t reverb_send; + + /* chorus */ + fluid_real_t chorus_send; + + /* rvoice control */ + fluid_rvoice_t *rvoice; + fluid_rvoice_t *overflow_rvoice; /* Used temporarily and only in overflow situations */ + char can_access_rvoice; /* False if rvoice is being rendered in separate thread */ + char can_access_overflow_rvoice; /* False if overflow_rvoice is being rendered in separate thread */ + char has_noteoff; /* Flag set when noteoff has been sent */ + +#ifdef WITH_PROFILING + /* for debugging */ + double ref; +#endif +}; + + +fluid_voice_t *new_fluid_voice(fluid_rvoice_eventhandler_t *handler, fluid_real_t output_rate); +void delete_fluid_voice(fluid_voice_t *voice); + +void fluid_voice_start(fluid_voice_t *voice); +void fluid_voice_calculate_gen_pitch(fluid_voice_t *voice); + +int fluid_voice_init(fluid_voice_t *voice, fluid_sample_t *sample, + fluid_zone_range_t *inst_zone_range, + fluid_channel_t *channel, int key, int vel, + unsigned int id, unsigned int time, fluid_real_t gain); + +int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl); +int fluid_voice_modulate_all(fluid_voice_t *voice); + +/** Set the NRPN value of a generator. */ +int fluid_voice_set_param(fluid_voice_t *voice, int gen, fluid_real_t value); + + +/** Set the gain. */ +int fluid_voice_set_gain(fluid_voice_t *voice, fluid_real_t gain); + +void fluid_voice_set_output_rate(fluid_voice_t *voice, fluid_real_t value); + + +/** Update all the synthesis parameters, which depend on generator + 'gen'. This is only necessary after changing a generator of an + already operating voice. Most applications will not need this + function.*/ +void fluid_voice_update_param(fluid_voice_t *voice, int gen); + +/** legato modes */ +/* force in the attack section for legato mode multi_retrigger: 1 */ +void fluid_voice_update_multi_retrigger_attack(fluid_voice_t *voice, int tokey, int vel); +/* Update portamento parameter */ +void fluid_voice_update_portamento(fluid_voice_t *voice, int fromkey, int tokey); + + +void fluid_voice_release(fluid_voice_t *voice); +void fluid_voice_noteoff(fluid_voice_t *voice); +void fluid_voice_off(fluid_voice_t *voice); +void fluid_voice_stop(fluid_voice_t *voice); +void fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count); +void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice); + +int fluid_voice_kill_excl(fluid_voice_t *voice); +float fluid_voice_get_overflow_prio(fluid_voice_t *voice, + fluid_overflow_prio_t *score, + unsigned int cur_time); + +#define OVERFLOW_PRIO_CANNOT_KILL 999999. + +/** + * Locks the rvoice for rendering, so it can't be modified directly + */ +static FLUID_INLINE void +fluid_voice_lock_rvoice(fluid_voice_t *voice) +{ + voice->can_access_rvoice = 0; +} + +/** + * Unlocks the rvoice for rendering, so it can be modified directly + */ +static FLUID_INLINE void +fluid_voice_unlock_rvoice(fluid_voice_t *voice) +{ + voice->can_access_rvoice = 1; +} + +#define _AVAILABLE(voice) ((voice)->can_access_rvoice && \ + (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF))) +//#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL) +#define _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val) + + +fluid_real_t fluid_voice_gen_value(const fluid_voice_t *voice, int num); +void fluid_voice_set_custom_filter(fluid_voice_t *voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags); + + +#endif /* _FLUID_VOICE_H */ diff --git a/thirdparty/fluidsynth/src/utils/fluid_conv.c b/thirdparty/fluidsynth/src/utils/fluid_conv.c new file mode 100644 index 000000000..65dcde309 --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_conv.c @@ -0,0 +1,330 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_conv.h" +#include "fluid_sys.h" +#include "fluid_conv_tables.inc.h" + +/* + * Converts absolute cents to Hertz + * + * As per sfspec section 9.3: + * + * ABSOLUTE CENTS - An absolute logarithmic measure of frequency based on a + * reference of MIDI key number scaled by 100. + * A cent is 1/1200 of an octave [which is the twelve hundredth root of two], + * and value 6900 is 440 Hz (A-440). + * + * Implemented below basically is the following: + * 440 * 2^((cents-6900)/1200) + * = 440 * 2^((int)((cents-6900)/1200)) * 2^(((int)cents-6900)%1200)) + * = 2^((int)((cents-6900)/1200)) * (440 * 2^(((int)cents-6900)%1200))) + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * This second factor is stored in the lookup table. + * + * The first factor can be implemented with a fast shift when the exponent + * is always an int. This is the case when using 440/2^6 Hz rather than 440Hz + * reference. + */ +fluid_real_t +fluid_ct2hz_real(fluid_real_t cents) +{ + if(FLUID_UNLIKELY(cents < 0)) + { + return (fluid_real_t) 1.0; + } + else + { + unsigned int mult, fac, rem; + unsigned int icents = (unsigned int)cents; + icents += 300u; + + // don't use stdlib div() here, it turned out have poor performance + fac = icents / 1200u; + rem = icents % 1200u; + + // Think of "mult" as the factor that we multiply (440/2^6)Hz with, + // or in other words mult is the "first factor" of the above + // functions comment. + // + // Assuming sizeof(uint)==4 this will give us a maximum range of + // 32 * 1200cents - 300cents == 38100 cents == 29,527,900,160 Hz + // which is much more than ever needed. For bigger values, just + // safely wrap around (the & is just a replacement for the quick + // modulo operation % 32). + mult = 1u << (fac & (sizeof(mult)*8u - 1u)); + + // don't use ldexp() either (poor performance) + return mult * fluid_ct2hz_tab[rem]; + } +} + +/* + * fluid_ct2hz + */ +fluid_real_t +fluid_ct2hz(fluid_real_t cents) +{ + /* Filter fc limit: SF2.01 page 48 # 8 */ + if(cents >= 13500) + { + cents = 13500; /* 20 kHz */ + } + else if(cents < 1500) + { + cents = 1500; /* 20 Hz */ + } + + return fluid_ct2hz_real(cents); +} + +/* + * fluid_cb2amp + * + * in: a value between 0 and 1440, 0 is no attenuation + * out: a value between 1 and 0 + */ +fluid_real_t +fluid_cb2amp(fluid_real_t cb) +{ + /* + * cb: an attenuation in 'centibels' (1/10 dB) + * SF2.01 page 49 # 48 limits it to 144 dB. + * 96 dB is reasonable for 16 bit systems, 144 would make sense for 24 bit. + */ + + /* minimum attenuation: 0 dB */ + if(cb < 0) + { + return 1.0; + } + + if(cb >= FLUID_CB_AMP_SIZE) + { + return 0.0; + } + + return fluid_cb2amp_tab[(int) cb]; +} + +/* + * fluid_tc2sec_delay + */ +fluid_real_t +fluid_tc2sec_delay(fluid_real_t tc) +{ + /* SF2.01 section 8.1.2 items 21, 23, 25, 33 + * SF2.01 section 8.1.3 items 21, 23, 25, 33 + * + * The most negative number indicates a delay of 0. Range is limited + * from -12000 to 5000 */ + if(tc <= -32768.0f) + { + return (fluid_real_t) 0.0f; + }; + + if(tc < -12000.f) + { + tc = (fluid_real_t) -12000.0f; + } + + if(tc > 5000.0f) + { + tc = (fluid_real_t) 5000.0f; + } + + return FLUID_POW(2.f, tc / 1200.f); +} + +/* + * fluid_tc2sec_attack + */ +fluid_real_t +fluid_tc2sec_attack(fluid_real_t tc) +{ + /* SF2.01 section 8.1.2 items 26, 34 + * SF2.01 section 8.1.3 items 26, 34 + * The most negative number indicates a delay of 0 + * Range is limited from -12000 to 8000 */ + if(tc <= -32768.f) + { + return (fluid_real_t) 0.f; + }; + + if(tc < -12000.f) + { + tc = (fluid_real_t) -12000.f; + }; + + if(tc > 8000.f) + { + tc = (fluid_real_t) 8000.f; + }; + + return FLUID_POW(2.f, tc / 1200.f); +} + +/* + * fluid_tc2sec + */ +fluid_real_t +fluid_tc2sec(fluid_real_t tc) +{ + /* No range checking here! */ + return FLUID_POW(2.f, tc / 1200.f); +} + +/* + * fluid_tc2sec_release + */ +fluid_real_t +fluid_tc2sec_release(fluid_real_t tc) +{ + /* SF2.01 section 8.1.2 items 30, 38 + * SF2.01 section 8.1.3 items 30, 38 + * No 'most negative number' rule here! + * Range is limited from -12000 to 8000 */ + if(tc <= -32768.f) + { + return (fluid_real_t) 0.f; + }; + + if(tc < -12000.f) + { + tc = (fluid_real_t) -12000.f; + }; + + if(tc > 8000.f) + { + tc = (fluid_real_t) 8000.f; + }; + + return FLUID_POW(2.f, tc / 1200.f); +} + +/* + * fluid_act2hz + * + * Convert from absolute cents to Hertz + * + * The inverse operation, converting from Hertz to cents, was unused and implemented as + * +fluid_hz2ct(fluid_real_t f) +{ + return 6900.f + (1200.f / FLUID_M_LN2) * FLUID_LOGF(f / 440.0f)); +} + */ +fluid_real_t +fluid_act2hz(fluid_real_t c) +{ + return 8.176f * FLUID_POW(2.f, c / 1200.f); +} + +/* + * fluid_pan + */ +fluid_real_t +fluid_pan(fluid_real_t c, int left) +{ + if(left) + { + c = -c; + } + + if(c <= -500.f) + { + return (fluid_real_t) 0.f; + } + else if(c >= 500.f) + { + return (fluid_real_t) 1.f; + } + else + { + return fluid_pan_tab[(int)(c) + 500]; + } +} + +/* + * Return the amount of attenuation based on the balance for the specified + * channel. If balance is negative (turned toward left channel, only the right + * channel is attenuated. If balance is positive, only the left channel is + * attenuated. + * + * @params balance left/right balance, range [-960;960] in absolute centibels + * @return amount of attenuation [0.0;1.0] + */ +fluid_real_t fluid_balance(fluid_real_t balance, int left) +{ + /* This is the most common case */ + if(balance == 0.f) + { + return 1.0f; + } + + if((left && balance < 0.f) || (!left && balance > 0.f)) + { + return 1.0f; + } + + if(balance < 0.f) + { + balance = -balance; + } + + return fluid_cb2amp(balance); +} + +/* + * fluid_concave + */ +fluid_real_t +fluid_concave(fluid_real_t val) +{ + if(val < 0.f) + { + return 0.f; + } + else if(val >= (fluid_real_t)FLUID_VEL_CB_SIZE) + { + return 1.f; + } + + return fluid_concave_tab[(int) val]; +} + +/* + * fluid_convex + */ +fluid_real_t +fluid_convex(fluid_real_t val) +{ + if(val < 0.f) + { + return 0.f; + } + else if(val >= (fluid_real_t)FLUID_VEL_CB_SIZE) + { + return 1.f; + } + + return fluid_convex_tab[(int) val]; +} + diff --git a/thirdparty/fluidsynth/src/utils/fluid_conv.h b/thirdparty/fluidsynth/src/utils/fluid_conv.h new file mode 100644 index 000000000..60f441c49 --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_conv.h @@ -0,0 +1,40 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_CONV_H +#define _FLUID_CONV_H + +#include "fluidsynth_priv.h" +#include "utils/fluid_conv_tables.h" + +fluid_real_t fluid_ct2hz_real(fluid_real_t cents); +fluid_real_t fluid_ct2hz(fluid_real_t cents); +fluid_real_t fluid_cb2amp(fluid_real_t cb); +fluid_real_t fluid_tc2sec(fluid_real_t tc); +fluid_real_t fluid_tc2sec_delay(fluid_real_t tc); +fluid_real_t fluid_tc2sec_attack(fluid_real_t tc); +fluid_real_t fluid_tc2sec_release(fluid_real_t tc); +fluid_real_t fluid_act2hz(fluid_real_t c); +fluid_real_t fluid_pan(fluid_real_t c, int left); +fluid_real_t fluid_balance(fluid_real_t balance, int left); +fluid_real_t fluid_concave(fluid_real_t val); +fluid_real_t fluid_convex(fluid_real_t val); + +#endif /* _FLUID_CONV_H */ diff --git a/thirdparty/fluidsynth/src/utils/fluid_conv_tables.h b/thirdparty/fluidsynth/src/utils/fluid_conv_tables.h new file mode 100644 index 000000000..8d1ae7154 --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_conv_tables.h @@ -0,0 +1,41 @@ + +#ifndef _FLUID_CONV_TABLES_H +#define _FLUID_CONV_TABLES_H + +/* + Attenuation range in centibels. + Attenuation range is the dynamic range of the volume envelope generator + from 0 to the end of attack segment. + fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation. + However the spec makes no distinction between 16 or 24 bit synths, so use + 96 dB here. + + Note about usefulness of 24 bits: + 1)Even fluidsynth is a 24 bit synth, this format is only relevant if + the sample format coming from the soundfont is 24 bits and the audio sample format + chosen by the application (audio.sample.format) is not 16 bits. + + 2)When the sample soundfont is 16 bits, the internal 24 bits number have + 16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of + this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db) + even if this sample is produced by the audio driver using an audio sample format + compatible for a 24 bit DAC. + + 3)When the audio sample format settings is 16 bits (audio.sample.format), the + audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB + even if the initial sample comes from a 24 bits soundfont. + + In both cases (2) or (3), the real dynamic range is only 96 dB. + + Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3): + - for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB). + - for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB). + */ +#define FLUID_PEAK_ATTENUATION 960.0f + +#define FLUID_CENTS_HZ_SIZE 1200 +#define FLUID_VEL_CB_SIZE 128 +#define FLUID_CB_AMP_SIZE 1441 +#define FLUID_PAN_SIZE 1002 + +#endif diff --git a/thirdparty/fluidsynth/src/utils/fluid_hash.c b/thirdparty/fluidsynth/src/utils/fluid_hash.c new file mode 100644 index 000000000..7efd0dedd --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_hash.c @@ -0,0 +1,1407 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + * + * Adapted for FluidSynth use by Josh Green + * September 8, 2009 from glib 2.18.4 + */ + +/* + * MT safe + */ + +#include "fluid_sys.h" +#include "fluid_hash.h" +#include "fluid_list.h" + + +#define HASH_TABLE_MIN_SIZE 11 +#define HASH_TABLE_MAX_SIZE 13845163 + + +typedef struct +{ + fluid_hashtable_t *hashtable; + fluid_hashnode_t *prev_node; + fluid_hashnode_t *node; + int position; + int pre_advanced; // Boolean + int version; +} RealIter; + + +/* Excerpt from glib gprimes.c */ + +static const unsigned int primes[] = +{ + 11, + 19, + 37, + 73, + 109, + 163, + 251, + 367, + 557, + 823, + 1237, + 1861, + 2777, + 4177, + 6247, + 9371, + 14057, + 21089, + 31627, + 47431, + 71143, + 106721, + 160073, + 240101, + 360163, + 540217, + 810343, + 1215497, + 1823231, + 2734867, + 4102283, + 6153409, + 9230113, + 13845163, +}; + +static const unsigned int nprimes = FLUID_N_ELEMENTS(primes); + +static unsigned int +spaced_primes_closest(unsigned int num) +{ + unsigned int i; + + for(i = 0; i < nprimes; i++) + { + if(primes[i] > num) + { + return primes[i]; + } + } + + return primes[nprimes - 1]; +} + +/* End excerpt from glib gprimes.c */ + + +/* + * @hashtable: our #fluid_hashtable_t + * @key: the key to lookup against + * @hash_return: optional key hash return location + * Return value: a pointer to the described #fluid_hashnode_t pointer + * + * Performs a lookup in the hash table. Virtually all hash operations + * will use this function internally. + * + * This function first computes the hash value of the key using the + * user's hash function. + * + * If an entry in the table matching @key is found then this function + * returns a pointer to the pointer to that entry in the table. In + * the case that the entry is at the head of a chain, this pointer + * will be an item in the nodes[] array. In the case that the entry + * is not at the head of a chain, this pointer will be the ->next + * pointer on the node that precedes it. + * + * In the case that no matching entry exists in the table, a pointer + * to a %NULL pointer will be returned. To insert a item, this %NULL + * pointer should be updated to point to the new #fluid_hashnode_t. + * + * If @hash_return is a pass-by-reference parameter. If it is + * non-%NULL then the computed hash value is returned. This is to + * save insertions from having to compute the hash record again for + * the new record. + */ +static FLUID_INLINE fluid_hashnode_t ** +fluid_hashtable_lookup_node(fluid_hashtable_t *hashtable, const void *key, + unsigned int *hash_return) +{ + fluid_hashnode_t **node_ptr, *node; + unsigned int hash_value; + + hash_value = (* hashtable->hash_func)(key); + node_ptr = &hashtable->nodes[hash_value % hashtable->size]; + + if(hash_return) + { + *hash_return = hash_value; + } + + /* Hash table lookup needs to be fast. + * We therefore remove the extra conditional of testing + * whether to call the key_equal_func or not from + * the inner loop. + * + * Additional optimisation: first check if our full hash + * values are equal so we can avoid calling the full-blown + * key equality function in most cases. + */ + if(hashtable->key_equal_func) + { + while((node = *node_ptr)) + { + if(node->key_hash == hash_value && + hashtable->key_equal_func(node->key, key)) + { + break; + } + + node_ptr = &(*node_ptr)->next; + } + } + else + { + while((node = *node_ptr)) + { + if(node->key == key) + { + break; + } + + node_ptr = &(*node_ptr)->next; + } + } + + return node_ptr; +} + +/* + * @hashtable: our #fluid_hashtable_t + * @node_ptr_ptr: a pointer to the return value from + * fluid_hashtable_lookup_node() + * @notify: %TRUE if the destroy notify handlers are to be called + * + * Removes a node from the hash table and updates the node count. The + * node is freed. No table resize is performed. + * + * If @notify is %TRUE then the destroy notify functions are called + * for the key and value of the hash node. + * + * @node_ptr_ptr is a pass-by-reference in/out parameter. When the + * function is called, it should point to the pointer to the node to + * remove. This level of indirection is required so that the pointer + * may be updated appropriately once the node has been removed. + * + * Before the function returns, the pointer at @node_ptr_ptr will be + * updated to point to the position in the table that contains the + * pointer to the "next" node in the chain. This makes this function + * convenient to use from functions that iterate over the entire + * table. If there is no further item in the chain then the + * #fluid_hashnode_t pointer will be %NULL (ie: **node_ptr_ptr == %NULL). + * + * Since the pointer in the table to the removed node is replaced with + * either a pointer to the next node or a %NULL pointer as + * appropriate, the pointer at the end of @node_ptr_ptr will never be + * modified at all. Stay tuned. :) + */ +static void +fluid_hashtable_remove_node(fluid_hashtable_t *hashtable, + fluid_hashnode_t ***node_ptr_ptr, int notify) +{ + fluid_hashnode_t **node_ptr, *node; + + node_ptr = *node_ptr_ptr; + node = *node_ptr; + + *node_ptr = node->next; + + if(notify && hashtable->key_destroy_func) + { + hashtable->key_destroy_func(node->key); + } + + if(notify && hashtable->value_destroy_func) + { + hashtable->value_destroy_func(node->value); + } + + FLUID_FREE(node); + + hashtable->nnodes--; +} + +/* + * fluid_hashtable_remove_all_nodes: + * @hashtable: our #fluid_hashtable_t + * @notify: %TRUE if the destroy notify handlers are to be called + * + * Removes all nodes from the table. Since this may be a precursor to + * freeing the table entirely, no resize is performed. + * + * If @notify is %TRUE then the destroy notify functions are called + * for the key and value of the hash node. + */ +static void +fluid_hashtable_remove_all_nodes(fluid_hashtable_t *hashtable, int notify) +{ + fluid_hashnode_t **node_ptr; + int i; + + for(i = 0; i < hashtable->size; i++) + { + for(node_ptr = &hashtable->nodes[i]; *node_ptr != NULL;) + { + fluid_hashtable_remove_node(hashtable, &node_ptr, notify); + } + } + + hashtable->nnodes = 0; +} + +/* + * fluid_hashtable_resize: + * @hashtable: our #fluid_hashtable_t + * + * Resizes the hash table to the optimal size based on the number of + * nodes currently held. If you call this function then a resize will + * occur, even if one does not need to occur. Use + * fluid_hashtable_maybe_resize() instead. + */ +static void +fluid_hashtable_resize(fluid_hashtable_t *hashtable) +{ + fluid_hashnode_t **new_nodes; + fluid_hashnode_t *node; + fluid_hashnode_t *next; + unsigned int hash_val; + int new_size; + int i; + + new_size = spaced_primes_closest(hashtable->nnodes); + new_size = (new_size < HASH_TABLE_MIN_SIZE) ? HASH_TABLE_MIN_SIZE : + ((new_size > HASH_TABLE_MAX_SIZE) ? HASH_TABLE_MAX_SIZE : new_size); + + new_nodes = FLUID_ARRAY(fluid_hashnode_t *, new_size); + + if(!new_nodes) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return; + } + + FLUID_MEMSET(new_nodes, 0, new_size * sizeof(fluid_hashnode_t *)); + + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = next) + { + next = node->next; + + hash_val = node->key_hash % new_size; + + node->next = new_nodes[hash_val]; + new_nodes[hash_val] = node; + } + } + + FLUID_FREE(hashtable->nodes); + hashtable->nodes = new_nodes; + hashtable->size = new_size; +} + +/* + * fluid_hashtable_maybe_resize: + * @hashtable: our #fluid_hashtable_t + * + * Resizes the hash table, if needed. + * + * Essentially, calls fluid_hashtable_resize() if the table has strayed + * too far from its ideal size for its number of nodes. + */ +static FLUID_INLINE void +fluid_hashtable_maybe_resize(fluid_hashtable_t *hashtable) +{ + int nnodes = hashtable->nnodes; + int size = hashtable->size; + + if((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) || + (3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE)) + { + fluid_hashtable_resize(hashtable); + } +} + +/** + * new_fluid_hashtable: + * @hash_func: a function to create a hash value from a key. + * Hash values are used to determine where keys are stored within the + * #fluid_hashtable_t data structure. The fluid_direct_hash(), fluid_int_hash() and + * fluid_str_hash() functions are provided for some common types of keys. + * If hash_func is %NULL, fluid_direct_hash() is used. + * @key_equal_func: a function to check two keys for equality. This is + * used when looking up keys in the #fluid_hashtable_t. The fluid_direct_equal(), + * fluid_int_equal() and fluid_str_equal() functions are provided for the most + * common types of keys. If @key_equal_func is %NULL, keys are compared + * directly in a similar fashion to fluid_direct_equal(), but without the + * overhead of a function call. + * + * Creates a new #fluid_hashtable_t with a reference count of 1. + * + * Return value: a new #fluid_hashtable_t. + **/ +fluid_hashtable_t * +new_fluid_hashtable(fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func) +{ + return new_fluid_hashtable_full(hash_func, key_equal_func, NULL, NULL); +} + + +/** + * new_fluid_hashtable_full: + * @hash_func: a function to create a hash value from a key. + * @key_equal_func: a function to check two keys for equality. + * @key_destroy_func: a function to free the memory allocated for the key + * used when removing the entry from the #fluid_hashtable_t or %NULL if you + * don't want to supply such a function. + * @value_destroy_func: a function to free the memory allocated for the + * value used when removing the entry from the #fluid_hashtable_t or %NULL if + * you don't want to supply such a function. + * + * Creates a new #fluid_hashtable_t like fluid_hashtable_new() with a reference count + * of 1 and allows to specify functions to free the memory allocated for the + * key and value that get called when removing the entry from the #fluid_hashtable_t. + * + * Return value: a new #fluid_hashtable_t. + **/ +fluid_hashtable_t * +new_fluid_hashtable_full(fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func, + fluid_destroy_notify_t key_destroy_func, + fluid_destroy_notify_t value_destroy_func) +{ + fluid_hashtable_t *hashtable; + + hashtable = FLUID_NEW(fluid_hashtable_t); + + if(!hashtable) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + hashtable->size = HASH_TABLE_MIN_SIZE; + hashtable->nnodes = 0; + hashtable->hash_func = hash_func ? hash_func : fluid_direct_hash; + hashtable->key_equal_func = key_equal_func; + fluid_atomic_int_set(&hashtable->ref_count, 1); + hashtable->key_destroy_func = key_destroy_func; + hashtable->value_destroy_func = value_destroy_func; + hashtable->nodes = FLUID_ARRAY(fluid_hashnode_t *, hashtable->size); + if(hashtable->nodes == NULL) + { + delete_fluid_hashtable(hashtable); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + FLUID_MEMSET(hashtable->nodes, 0, hashtable->size * sizeof(*hashtable->nodes)); + + return hashtable; +} + +/** + * fluid_hashtable_iter_init: + * @iter: an uninitialized #fluid_hashtable_iter_t. + * @hashtable: a #fluid_hashtable_t. + * + * Initializes a key/value pair iterator and associates it with + * @hashtable. Modifying the hash table after calling this function + * invalidates the returned iterator. + * |[ + * fluid_hashtable_iter_t iter; + * gpointer key, value; + * + * fluid_hashtable_iter_init (&iter, hashtable); + * while (fluid_hashtable_iter_next (&iter, &key, &value)) + * { + * /* do something with key and value */ + * } + * ]| + * + * Since: 2.16 + **/ +void +fluid_hashtable_iter_init(fluid_hashtable_iter_t *iter, + fluid_hashtable_t *hashtable) +{ + RealIter *ri = (RealIter *) iter; + + fluid_return_if_fail(iter != NULL); + fluid_return_if_fail(hashtable != NULL); + + ri->hashtable = hashtable; + ri->prev_node = NULL; + ri->node = NULL; + ri->position = -1; + ri->pre_advanced = FALSE; +} + +/** + * fluid_hashtable_iter_next: + * @iter: an initialized #fluid_hashtable_iter_t. + * @key: a location to store the key, or %NULL. + * @value: a location to store the value, or %NULL. + * + * Advances @iter and retrieves the key and/or value that are now + * pointed to as a result of this advancement. If %FALSE is returned, + * @key and @value are not set, and the iterator becomes invalid. + * + * Return value: %FALSE if the end of the #fluid_hashtable_t has been reached. + * + * Since: 2.16 + **/ +int +fluid_hashtable_iter_next(fluid_hashtable_iter_t *iter, void **key, + void **value) +{ + RealIter *ri = (RealIter *) iter; + + fluid_return_val_if_fail(iter != NULL, FALSE); + + if(ri->pre_advanced) + { + ri->pre_advanced = FALSE; + + if(ri->node == NULL) + { + return FALSE; + } + } + else + { + if(ri->node != NULL) + { + ri->prev_node = ri->node; + ri->node = ri->node->next; + } + + while(ri->node == NULL) + { + ri->position++; + + if(ri->position >= ri->hashtable->size) + { + return FALSE; + } + + ri->prev_node = NULL; + ri->node = ri->hashtable->nodes[ri->position]; + } + } + + if(key != NULL) + { + *key = ri->node->key; + } + + if(value != NULL) + { + *value = ri->node->value; + } + + return TRUE; +} + +/** + * fluid_hashtable_iter_get_hash_table: + * @iter: an initialized #fluid_hashtable_iter_t. + * + * Returns the #fluid_hashtable_t associated with @iter. + * + * Return value: the #fluid_hashtable_t associated with @iter. + * + * Since: 2.16 + **/ +fluid_hashtable_t * +fluid_hashtable_iter_get_hash_table(fluid_hashtable_iter_t *iter) +{ + fluid_return_val_if_fail(iter != NULL, NULL); + + return ((RealIter *) iter)->hashtable; +} + +static void +iter_remove_or_steal(RealIter *ri, int notify) +{ + fluid_hashnode_t *prev; + fluid_hashnode_t *node; + int position; + + fluid_return_if_fail(ri != NULL); + fluid_return_if_fail(ri->node != NULL); + + prev = ri->prev_node; + node = ri->node; + position = ri->position; + + /* pre-advance the iterator since we will remove the node */ + + ri->node = ri->node->next; + /* ri->prev_node is still the correct previous node */ + + while(ri->node == NULL) + { + ri->position++; + + if(ri->position >= ri->hashtable->size) + { + break; + } + + ri->prev_node = NULL; + ri->node = ri->hashtable->nodes[ri->position]; + } + + ri->pre_advanced = TRUE; + + /* remove the node */ + + if(prev != NULL) + { + prev->next = node->next; + } + else + { + ri->hashtable->nodes[position] = node->next; + } + + if(notify) + { + if(ri->hashtable->key_destroy_func) + { + ri->hashtable->key_destroy_func(node->key); + } + + if(ri->hashtable->value_destroy_func) + { + ri->hashtable->value_destroy_func(node->value); + } + } + + FLUID_FREE(node); + + ri->hashtable->nnodes--; +} + +/** + * fluid_hashtable_iter_remove(): + * @iter: an initialized #fluid_hashtable_iter_t. + * + * Removes the key/value pair currently pointed to by the iterator + * from its associated #fluid_hashtable_t. Can only be called after + * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more + * than once for the same key/value pair. + * + * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the + * key and value are freed using the supplied destroy functions, otherwise + * you have to make sure that any dynamically allocated values are freed + * yourself. + * + * Since: 2.16 + **/ +void +fluid_hashtable_iter_remove(fluid_hashtable_iter_t *iter) +{ + iter_remove_or_steal((RealIter *) iter, TRUE); +} + +/** + * fluid_hashtable_iter_steal(): + * @iter: an initialized #fluid_hashtable_iter_t. + * + * Removes the key/value pair currently pointed to by the iterator + * from its associated #fluid_hashtable_t, without calling the key and value + * destroy functions. Can only be called after + * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more + * than once for the same key/value pair. + * + * Since: 2.16 + **/ +void +fluid_hashtable_iter_steal(fluid_hashtable_iter_t *iter) +{ + iter_remove_or_steal((RealIter *) iter, FALSE); +} + + +/** + * fluid_hashtable_ref: + * @hashtable: a valid #fluid_hashtable_t. + * + * Atomically increments the reference count of @hashtable by one. + * This function is MT-safe and may be called from any thread. + * + * Return value: the passed in #fluid_hashtable_t. + * + * Since: 2.10 + **/ +fluid_hashtable_t * +fluid_hashtable_ref(fluid_hashtable_t *hashtable) +{ + fluid_return_val_if_fail(hashtable != NULL, NULL); + fluid_return_val_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0, hashtable); + + fluid_atomic_int_add(&hashtable->ref_count, 1); + return hashtable; +} + +/** + * fluid_hashtable_unref: + * @hashtable: a valid #fluid_hashtable_t. + * + * Atomically decrements the reference count of @hashtable by one. + * If the reference count drops to 0, all keys and values will be + * destroyed, and all memory allocated by the hash table is released. + * This function is MT-safe and may be called from any thread. + * + * Since: 2.10 + **/ +void +fluid_hashtable_unref(fluid_hashtable_t *hashtable) +{ + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0); + + if(fluid_atomic_int_exchange_and_add(&hashtable->ref_count, -1) - 1 == 0) + { + fluid_hashtable_remove_all_nodes(hashtable, TRUE); + FLUID_FREE(hashtable->nodes); + FLUID_FREE(hashtable); + } +} + +/** + * delete_fluid_hashtable: + * @hashtable: a #fluid_hashtable_t. + * + * Destroys all keys and values in the #fluid_hashtable_t and decrements its + * reference count by 1. If keys and/or values are dynamically allocated, + * you should either free them first or create the #fluid_hashtable_t with destroy + * notifiers using fluid_hashtable_new_full(). In the latter case the destroy + * functions you supplied will be called on all keys and values during the + * destruction phase. + **/ +void +delete_fluid_hashtable(fluid_hashtable_t *hashtable) +{ + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0); + + fluid_hashtable_remove_all(hashtable); + fluid_hashtable_unref(hashtable); +} + +/** + * fluid_hashtable_lookup: + * @hashtable: a #fluid_hashtable_t. + * @key: the key to look up. + * + * Looks up a key in a #fluid_hashtable_t. Note that this function cannot + * distinguish between a key that is not present and one which is present + * and has the value %NULL. If you need this distinction, use + * fluid_hashtable_lookup_extended(). + * + * Return value: the associated value, or %NULL if the key is not found. + **/ +void * +fluid_hashtable_lookup(fluid_hashtable_t *hashtable, const void *key) +{ + fluid_hashnode_t *node; + + fluid_return_val_if_fail(hashtable != NULL, NULL); + + node = *fluid_hashtable_lookup_node(hashtable, key, NULL); + + return node ? node->value : NULL; +} + +/** + * fluid_hashtable_lookup_extended: + * @hashtable: a #fluid_hashtable_t. + * @lookup_key: the key to look up. + * @orig_key: returns the original key. + * @value: returns the value associated with the key. + * + * Looks up a key in the #fluid_hashtable_t, returning the original key and the + * associated value and a #gboolean which is %TRUE if the key was found. This + * is useful if you need to free the memory allocated for the original key, + * for example before calling fluid_hashtable_remove(). + * + * Return value: %TRUE if the key was found in the #fluid_hashtable_t. + **/ +int +fluid_hashtable_lookup_extended(fluid_hashtable_t *hashtable, + const void *lookup_key, + void **orig_key, void **value) +{ + fluid_hashnode_t *node; + + fluid_return_val_if_fail(hashtable != NULL, FALSE); + + node = *fluid_hashtable_lookup_node(hashtable, lookup_key, NULL); + + if(node == NULL) + { + return FALSE; + } + + if(orig_key) + { + *orig_key = node->key; + } + + if(value) + { + *value = node->value; + } + + return TRUE; +} + +/* + * fluid_hashtable_insert_internal: + * @hashtable: our #fluid_hashtable_t + * @key: the key to insert + * @value: the value to insert + * @keep_new_key: if %TRUE and this key already exists in the table + * then call the destroy notify function on the old key. If %FALSE + * then call the destroy notify function on the new key. + * + * Implements the common logic for the fluid_hashtable_insert() and + * fluid_hashtable_replace() functions. + * + * Do a lookup of @key. If it is found, replace it with the new + * @value (and perhaps the new @key). If it is not found, create a + * new node. + */ +static void +fluid_hashtable_insert_internal(fluid_hashtable_t *hashtable, void *key, + void *value, int keep_new_key) +{ + fluid_hashnode_t **node_ptr, *node; + unsigned int key_hash; + + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0); + + node_ptr = fluid_hashtable_lookup_node(hashtable, key, &key_hash); + + if((node = *node_ptr)) + { + if(keep_new_key) + { + if(hashtable->key_destroy_func) + { + hashtable->key_destroy_func(node->key); + } + + node->key = key; + } + else + { + if(hashtable->key_destroy_func) + { + hashtable->key_destroy_func(key); + } + } + + if(hashtable->value_destroy_func) + { + hashtable->value_destroy_func(node->value); + } + + node->value = value; + } + else + { + node = FLUID_NEW(fluid_hashnode_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return; + } + + node->key = key; + node->value = value; + node->key_hash = key_hash; + node->next = NULL; + + *node_ptr = node; + hashtable->nnodes++; + fluid_hashtable_maybe_resize(hashtable); + } +} + +/** + * fluid_hashtable_insert: + * @hashtable: a #fluid_hashtable_t. + * @key: a key to insert. + * @value: the value to associate with the key. + * + * Inserts a new key and value into a #fluid_hashtable_t. + * + * If the key already exists in the #fluid_hashtable_t its current value is replaced + * with the new value. If you supplied a @value_destroy_func when creating the + * #fluid_hashtable_t, the old value is freed using that function. If you supplied + * a @key_destroy_func when creating the #fluid_hashtable_t, the passed key is freed + * using that function. + **/ +void +fluid_hashtable_insert(fluid_hashtable_t *hashtable, void *key, void *value) +{ + fluid_hashtable_insert_internal(hashtable, key, value, FALSE); +} + +/** + * fluid_hashtable_replace: + * @hashtable: a #fluid_hashtable_t. + * @key: a key to insert. + * @value: the value to associate with the key. + * + * Inserts a new key and value into a #fluid_hashtable_t similar to + * fluid_hashtable_insert(). The difference is that if the key already exists + * in the #fluid_hashtable_t, it gets replaced by the new key. If you supplied a + * @value_destroy_func when creating the #fluid_hashtable_t, the old value is freed + * using that function. If you supplied a @key_destroy_func when creating the + * #fluid_hashtable_t, the old key is freed using that function. + **/ +void +fluid_hashtable_replace(fluid_hashtable_t *hashtable, void *key, void *value) +{ + fluid_hashtable_insert_internal(hashtable, key, value, TRUE); +} + +/* + * fluid_hashtable_remove_internal: + * @hashtable: our #fluid_hashtable_t + * @key: the key to remove + * @notify: %TRUE if the destroy notify handlers are to be called + * Return value: %TRUE if a node was found and removed, else %FALSE + * + * Implements the common logic for the fluid_hashtable_remove() and + * fluid_hashtable_steal() functions. + * + * Do a lookup of @key and remove it if it is found, calling the + * destroy notify handlers only if @notify is %TRUE. + */ +static int +fluid_hashtable_remove_internal(fluid_hashtable_t *hashtable, const void *key, + int notify) +{ + fluid_hashnode_t **node_ptr; + + fluid_return_val_if_fail(hashtable != NULL, FALSE); + + node_ptr = fluid_hashtable_lookup_node(hashtable, key, NULL); + + if(*node_ptr == NULL) + { + return FALSE; + } + + fluid_hashtable_remove_node(hashtable, &node_ptr, notify); + fluid_hashtable_maybe_resize(hashtable); + + return TRUE; +} + +/** + * fluid_hashtable_remove: + * @hashtable: a #fluid_hashtable_t. + * @key: the key to remove. + * + * Removes a key and its associated value from a #fluid_hashtable_t. + * + * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the + * key and value are freed using the supplied destroy functions, otherwise + * you have to make sure that any dynamically allocated values are freed + * yourself. + * + * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. + **/ +int +fluid_hashtable_remove(fluid_hashtable_t *hashtable, const void *key) +{ + return fluid_hashtable_remove_internal(hashtable, key, TRUE); +} + +/** + * fluid_hashtable_steal: + * @hashtable: a #fluid_hashtable_t. + * @key: the key to remove. + * + * Removes a key and its associated value from a #fluid_hashtable_t without + * calling the key and value destroy functions. + * + * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t. + **/ +int +fluid_hashtable_steal(fluid_hashtable_t *hashtable, const void *key) +{ + return fluid_hashtable_remove_internal(hashtable, key, FALSE); +} + +/** + * fluid_hashtable_remove_all: + * @hashtable: a #fluid_hashtable_t + * + * Removes all keys and their associated values from a #fluid_hashtable_t. + * + * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the keys + * and values are freed using the supplied destroy functions, otherwise you + * have to make sure that any dynamically allocated values are freed + * yourself. + * + * Since: 2.12 + **/ +void +fluid_hashtable_remove_all(fluid_hashtable_t *hashtable) +{ + fluid_return_if_fail(hashtable != NULL); + + fluid_hashtable_remove_all_nodes(hashtable, TRUE); + fluid_hashtable_maybe_resize(hashtable); +} + +/** + * fluid_hashtable_steal_all: + * @hashtable: a #fluid_hashtable_t. + * + * Removes all keys and their associated values from a #fluid_hashtable_t + * without calling the key and value destroy functions. + * + * Since: 2.12 + **/ +void +fluid_hashtable_steal_all(fluid_hashtable_t *hashtable) +{ + fluid_return_if_fail(hashtable != NULL); + + fluid_hashtable_remove_all_nodes(hashtable, FALSE); + fluid_hashtable_maybe_resize(hashtable); +} + +/* + * fluid_hashtable_foreach_remove_or_steal: + * @hashtable: our #fluid_hashtable_t + * @func: the user's callback function + * @user_data: data for @func + * @notify: %TRUE if the destroy notify handlers are to be called + * + * Implements the common logic for fluid_hashtable_foreach_remove() and + * fluid_hashtable_foreach_steal(). + * + * Iterates over every node in the table, calling @func with the key + * and value of the node (and @user_data). If @func returns %TRUE the + * node is removed from the table. + * + * If @notify is true then the destroy notify handlers will be called + * for each removed node. + */ +static unsigned int +fluid_hashtable_foreach_remove_or_steal(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data, + int notify) +{ + fluid_hashnode_t *node, **node_ptr; + unsigned int deleted = 0; + int i; + + for(i = 0; i < hashtable->size; i++) + { + for(node_ptr = &hashtable->nodes[i]; (node = *node_ptr) != NULL;) + { + if((* func)(node->key, node->value, user_data)) + { + fluid_hashtable_remove_node(hashtable, &node_ptr, notify); + deleted++; + } + else + { + node_ptr = &node->next; + } + } + } + + fluid_hashtable_maybe_resize(hashtable); + + return deleted; +} + +#if 0 +/** + * fluid_hashtable_foreach_remove: + * @hashtable: a #fluid_hashtable_t. + * @func: the function to call for each key/value pair. + * @user_data: user data to pass to the function. + * + * Calls the given function for each key/value pair in the #fluid_hashtable_t. + * If the function returns %TRUE, then the key/value pair is removed from the + * #fluid_hashtable_t. If you supplied key or value destroy functions when creating + * the #fluid_hashtable_t, they are used to free the memory allocated for the removed + * keys and values. + * + * See #fluid_hashtable_iter_t for an alternative way to loop over the + * key/value pairs in the hash table. + * + * Return value: the number of key/value pairs removed. + **/ +static unsigned int +fluid_hashtable_foreach_remove(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data) +{ + fluid_return_val_if_fail(hashtable != NULL, 0); + fluid_return_val_if_fail(func != NULL, 0); + + return fluid_hashtable_foreach_remove_or_steal(hashtable, func, user_data, TRUE); +} +#endif + +/** + * fluid_hashtable_foreach_steal: + * @hashtable: a #fluid_hashtable_t. + * @func: the function to call for each key/value pair. + * @user_data: user data to pass to the function. + * + * Calls the given function for each key/value pair in the #fluid_hashtable_t. + * If the function returns %TRUE, then the key/value pair is removed from the + * #fluid_hashtable_t, but no key or value destroy functions are called. + * + * See #fluid_hashtable_iter_t for an alternative way to loop over the + * key/value pairs in the hash table. + * + * Return value: the number of key/value pairs removed. + **/ +unsigned int +fluid_hashtable_foreach_steal(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data) +{ + fluid_return_val_if_fail(hashtable != NULL, 0); + fluid_return_val_if_fail(func != NULL, 0); + + return fluid_hashtable_foreach_remove_or_steal(hashtable, func, user_data, FALSE); +} + +/** + * fluid_hashtable_foreach: + * @hashtable: a #fluid_hashtable_t. + * @func: the function to call for each key/value pair. + * @user_data: user data to pass to the function. + * + * Calls the given function for each of the key/value pairs in the + * #fluid_hashtable_t. The function is passed the key and value of each + * pair, and the given @user_data parameter. The hash table may not + * be modified while iterating over it (you can't add/remove + * items). To remove all items matching a predicate, use + * fluid_hashtable_foreach_remove(). + * + * See fluid_hashtable_find() for performance caveats for linear + * order searches in contrast to fluid_hashtable_lookup(). + **/ +void +fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hr_func_t func, + void *user_data) +{ + fluid_hashnode_t *node; + int i; + + fluid_return_if_fail(hashtable != NULL); + fluid_return_if_fail(func != NULL); + + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + (* func)(node->key, node->value, user_data); + } + } +} + +/** + * fluid_hashtable_find: + * @hashtable: a #fluid_hashtable_t. + * @predicate: function to test the key/value pairs for a certain property. + * @user_data: user data to pass to the function. + * + * Calls the given function for key/value pairs in the #fluid_hashtable_t until + * @predicate returns %TRUE. The function is passed the key and value of + * each pair, and the given @user_data parameter. The hash table may not + * be modified while iterating over it (you can't add/remove items). + * + * Note, that hash tables are really only optimized for forward lookups, + * i.e. fluid_hashtable_lookup(). + * So code that frequently issues fluid_hashtable_find() or + * fluid_hashtable_foreach() (e.g. in the order of once per every entry in a + * hash table) should probably be reworked to use additional or different + * data structures for reverse lookups (keep in mind that an O(n) find/foreach + * operation issued for all n values in a hash table ends up needing O(n*n) + * operations). + * + * Return value: The value of the first key/value pair is returned, for which + * func evaluates to %TRUE. If no pair with the requested property is found, + * %NULL is returned. + * + * Since: 2.4 + **/ +void * +fluid_hashtable_find(fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, + void *user_data) +{ + fluid_hashnode_t *node; + int i; + + fluid_return_val_if_fail(hashtable != NULL, NULL); + fluid_return_val_if_fail(predicate != NULL, NULL); + + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + if(predicate(node->key, node->value, user_data)) + { + return node->value; + } + } + } + + return NULL; +} + +/** + * fluid_hashtable_size: + * @hashtable: a #fluid_hashtable_t. + * + * Returns the number of elements contained in the #fluid_hashtable_t. + * + * Return value: the number of key/value pairs in the #fluid_hashtable_t. + **/ +unsigned int +fluid_hashtable_size(fluid_hashtable_t *hashtable) +{ + fluid_return_val_if_fail(hashtable != NULL, 0); + + return hashtable->nnodes; +} + +/** + * fluid_hashtable_get_keys: + * @hashtable: a #fluid_hashtable_t + * + * Retrieves every key inside @hashtable. The returned data is valid + * until @hashtable is modified. + * + * Return value: a #GList containing all the keys inside the hash + * table. The content of the list is owned by the hash table and + * should not be modified or freed. Use delete_fluid_list() when done + * using the list. + * + * Since: 2.14 + */ +fluid_list_t * +fluid_hashtable_get_keys(fluid_hashtable_t *hashtable) +{ + fluid_hashnode_t *node; + int i; + fluid_list_t *retval; + + fluid_return_val_if_fail(hashtable != NULL, NULL); + + retval = NULL; + + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + retval = fluid_list_prepend(retval, node->key); + } + } + + return retval; +} + +/** + * fluid_hashtable_get_values: + * @hashtable: a #fluid_hashtable_t + * + * Retrieves every value inside @hashtable. The returned data is + * valid until @hashtable is modified. + * + * Return value: a #GList containing all the values inside the hash + * table. The content of the list is owned by the hash table and + * should not be modified or freed. Use delete_fluid_list() when done + * using the list. + * + * Since: 2.14 + */ +fluid_list_t * +fluid_hashtable_get_values(fluid_hashtable_t *hashtable) +{ + fluid_hashnode_t *node; + int i; + fluid_list_t *retval; + + fluid_return_val_if_fail(hashtable != NULL, NULL); + + retval = NULL; + + for(i = 0; i < hashtable->size; i++) + { + for(node = hashtable->nodes[i]; node; node = node->next) + { + retval = fluid_list_prepend(retval, node->value); + } + } + + return retval; +} + + +/* Extracted from glib/gstring.c */ + + +/** + * fluid_str_equal: + * @v1: a key + * @v2: a key to compare with @v1 + * + * Compares two strings for byte-by-byte equality and returns %TRUE + * if they are equal. It can be passed to new_fluid_hashtable() as the + * @key_equal_func parameter, when using strings as keys in a #Ghashtable. + * + * Returns: %TRUE if the two keys match + */ +int +fluid_str_equal(const void *v1, const void *v2) +{ + const char *string1 = v1; + const char *string2 = v2; + + return FLUID_STRCMP(string1, string2) == 0; +} + +/** + * fluid_str_hash: + * @v: a string key + * + * Converts a string to a hash value. + * It can be passed to new_fluid_hashtable() as the @hash_func + * parameter, when using strings as keys in a #fluid_hashtable_t. + * + * Returns: a hash value corresponding to the key + */ +unsigned int +fluid_str_hash(const void *v) +{ + /* 31 bit hash function */ + const signed char *p = v; + uint32_t h = *p; + + if(h) + { + for(p += 1; *p != '\0'; p++) + { + h = (h << 5) - h + *p; + } + } + + return h; +} + + +/* Extracted from glib/gutils.c */ + + +/** + * fluid_direct_equal: + * @v1: a key. + * @v2: a key to compare with @v1. + * + * Compares two #gpointer arguments and returns %TRUE if they are equal. + * It can be passed to new_fluid_hashtable() as the @key_equal_func + * parameter, when using pointers as keys in a #fluid_hashtable_t. + * + * Returns: %TRUE if the two keys match. + */ +int +fluid_direct_equal(const void *v1, const void *v2) +{ + return v1 == v2; +} + +/** + * fluid_direct_hash: + * @v: a void * key + * + * Converts a gpointer to a hash value. + * It can be passed to g_hashtable_new() as the @hash_func parameter, + * when using pointers as keys in a #fluid_hashtable_t. + * + * Returns: a hash value corresponding to the key. + */ +unsigned int +fluid_direct_hash(const void *v) +{ + return FLUID_POINTER_TO_UINT(v); +} + +/** + * fluid_int_equal: + * @v1: a pointer to a int key. + * @v2: a pointer to a int key to compare with @v1. + * + * Compares the two #gint values being pointed to and returns + * %TRUE if they are equal. + * It can be passed to g_hashtable_new() as the @key_equal_func + * parameter, when using pointers to integers as keys in a #fluid_hashtable_t. + * + * Returns: %TRUE if the two keys match. + */ +int +fluid_int_equal(const void *v1, const void *v2) +{ + return *((const int *) v1) == *((const int *) v2); +} + +/** + * fluid_int_hash: + * @v: a pointer to a int key + * + * Converts a pointer to a #gint to a hash value. + * It can be passed to g_hashtable_new() as the @hash_func parameter, + * when using pointers to integers values as keys in a #fluid_hashtable_t. + * + * Returns: a hash value corresponding to the key. + */ +unsigned int +fluid_int_hash(const void *v) +{ + return *(const int *) v; +} diff --git a/thirdparty/fluidsynth/src/utils/fluid_hash.h b/thirdparty/fluidsynth/src/utils/fluid_hash.h new file mode 100644 index 000000000..96b2471be --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_hash.h @@ -0,0 +1,131 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/* + * Adapted for FluidSynth use by Josh Green + * September 8, 2009 from glib 2.18.4 + * + * - Self contained (no dependencies on glib) + * - changed names to fluid_hashtable_... + */ + +#ifndef _FLUID_HASH_H +#define _FLUID_HASH_H + +#include "fluidsynth_priv.h" +#include "fluid_list.h" +#include "fluid_sys.h" + +/* Extracted from gtypes.h */ +typedef void (*fluid_destroy_notify_t)(void *data); +typedef unsigned int (*fluid_hash_func_t)(const void *key); +typedef int (*fluid_equal_func_t)(const void *a, const void *b); +/* End gtypes.h extraction */ + +typedef int (*fluid_hr_func_t)(void *key, void *value, void *user_data); +typedef struct _fluid_hashtable_iter_t fluid_hashtable_iter_t; + +typedef struct _fluid_hashnode_t fluid_hashnode_t; + +struct _fluid_hashnode_t +{ + void *key; + void *value; + fluid_hashnode_t *next; + unsigned int key_hash; +}; + +struct _fluid_hashtable_t +{ + int size; + int nnodes; + fluid_hashnode_t **nodes; + fluid_hash_func_t hash_func; + fluid_equal_func_t key_equal_func; + fluid_atomic_int_t ref_count; + fluid_destroy_notify_t key_destroy_func; + fluid_destroy_notify_t value_destroy_func; + fluid_rec_mutex_t mutex; // Optionally used in other modules (fluid_settings.c for example) +}; + +struct _fluid_hashtable_iter_t +{ + /*< private >*/ + void *dummy1; + void *dummy2; + void *dummy3; + int dummy4; + int dummy5; // Bool + void *dummy6; +}; + +fluid_hashtable_t *new_fluid_hashtable(fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func); +fluid_hashtable_t *new_fluid_hashtable_full(fluid_hash_func_t hash_func, + fluid_equal_func_t key_equal_func, + fluid_destroy_notify_t key_destroy_func, + fluid_destroy_notify_t value_destroy_func); +void delete_fluid_hashtable(fluid_hashtable_t *hashtable); + +void fluid_hashtable_iter_init(fluid_hashtable_iter_t *iter, fluid_hashtable_t *hashtable); +int fluid_hashtable_iter_next(fluid_hashtable_iter_t *iter, void **key, void **value); +fluid_hashtable_t *fluid_hashtable_iter_get_hash_table(fluid_hashtable_iter_t *iter); +void fluid_hashtable_iter_remove(fluid_hashtable_iter_t *iter); +void fluid_hashtable_iter_steal(fluid_hashtable_iter_t *iter); + +fluid_hashtable_t *fluid_hashtable_ref(fluid_hashtable_t *hashtable); +void fluid_hashtable_unref(fluid_hashtable_t *hashtable); + +void *fluid_hashtable_lookup(fluid_hashtable_t *hashtable, const void *key); +int fluid_hashtable_lookup_extended(fluid_hashtable_t *hashtable, const void *lookup_key, + void **orig_key, void **value); + +void fluid_hashtable_insert(fluid_hashtable_t *hashtable, void *key, void *value); +void fluid_hashtable_replace(fluid_hashtable_t *hashtable, void *key, void *value); + +int fluid_hashtable_remove(fluid_hashtable_t *hashtable, const void *key); +int fluid_hashtable_steal(fluid_hashtable_t *hashtable, const void *key); +void fluid_hashtable_remove_all(fluid_hashtable_t *hashtable); +void fluid_hashtable_steal_all(fluid_hashtable_t *hashtable); +unsigned int fluid_hashtable_foreach_steal(fluid_hashtable_t *hashtable, + fluid_hr_func_t func, void *user_data); +void fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hr_func_t func, + void *user_data); +void *fluid_hashtable_find(fluid_hashtable_t *hashtable, fluid_hr_func_t predicate, + void *user_data); +unsigned int fluid_hashtable_size(fluid_hashtable_t *hashtable); +fluid_list_t *fluid_hashtable_get_keys(fluid_hashtable_t *hashtable); +fluid_list_t *fluid_hashtable_get_values(fluid_hashtable_t *hashtable); + +int fluid_str_equal(const void *v1, const void *v2); +unsigned int fluid_str_hash(const void *v); +int fluid_direct_equal(const void *v1, const void *v2); +unsigned int fluid_direct_hash(const void *v); +int fluid_int_equal(const void *v1, const void *v2); +unsigned int fluid_int_hash(const void *v); + +#endif /* _FLUID_HASH_H */ + diff --git a/thirdparty/fluidsynth/src/utils/fluid_list.c b/thirdparty/fluidsynth/src/utils/fluid_list.c new file mode 100644 index 000000000..c88e2aec0 --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_list.c @@ -0,0 +1,337 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + */ + +/* + * Modified by the GLib Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + + + +#include "fluid_sys.h" +#include "fluid_list.h" + + +fluid_list_t * +new_fluid_list(void) +{ + fluid_list_t *list; + list = (fluid_list_t *) FLUID_MALLOC(sizeof(fluid_list_t)); + list->data = NULL; + list->next = NULL; + return list; +} + +void +delete_fluid_list(fluid_list_t *list) +{ + fluid_list_t *next; + fluid_return_if_fail(list != NULL); + + while(list) + { + next = list->next; + FLUID_FREE(list); + list = next; + } +} + +void +delete1_fluid_list(fluid_list_t *list) +{ + FLUID_FREE(list); +} + +fluid_list_t * +fluid_list_append(fluid_list_t *list, void *data) +{ + fluid_list_t *new_list; + fluid_list_t *last; + + new_list = new_fluid_list(); + new_list->data = data; + + if(list) + { + last = fluid_list_last(list); + /* g_assert (last != NULL); */ + last->next = new_list; + + return list; + } + else + { + return new_list; + } +} + +fluid_list_t * +fluid_list_prepend(fluid_list_t *list, void *data) +{ + fluid_list_t *new_list; + + new_list = new_fluid_list(); + new_list->data = data; + new_list->next = list; + + return new_list; +} + +fluid_list_t * +fluid_list_nth(fluid_list_t *list, int n) +{ + while((n-- > 0) && list) + { + list = list->next; + } + + return list; +} + +fluid_list_t * +fluid_list_remove(fluid_list_t *list, void *data) +{ + fluid_list_t *tmp; + fluid_list_t *prev; + + prev = NULL; + tmp = list; + + while(tmp) + { + if(tmp->data == data) + { + if(prev) + { + prev->next = tmp->next; + } + + if(list == tmp) + { + list = list->next; + } + + tmp->next = NULL; + delete_fluid_list(tmp); + + break; + } + + prev = tmp; + tmp = tmp->next; + } + + return list; +} + +fluid_list_t * +fluid_list_remove_link(fluid_list_t *list, fluid_list_t *link) +{ + fluid_list_t *tmp; + fluid_list_t *prev; + + prev = NULL; + tmp = list; + + while(tmp) + { + if(tmp == link) + { + if(prev) + { + prev->next = tmp->next; + } + + if(list == tmp) + { + list = list->next; + } + + tmp->next = NULL; + break; + } + + prev = tmp; + tmp = tmp->next; + } + + return list; +} + +static fluid_list_t * +fluid_list_sort_merge(fluid_list_t *l1, fluid_list_t *l2, fluid_compare_func_t compare_func) +{ + fluid_list_t list, *l; + + l = &list; + + while(l1 && l2) + { + if(compare_func(l1->data, l2->data) < 0) + { + l = l->next = l1; + l1 = l1->next; + } + else + { + l = l->next = l2; + l2 = l2->next; + } + } + + l->next = l1 ? l1 : l2; + + return list.next; +} + +fluid_list_t * +fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func) +{ + fluid_list_t *l1, *l2; + + if(!list) + { + return NULL; + } + + if(!list->next) + { + return list; + } + + l1 = list; + l2 = list->next; + + while((l2 = l2->next) != NULL) + { + if((l2 = l2->next) == NULL) + { + break; + } + + l1 = l1->next; + } + + l2 = l1->next; + l1->next = NULL; + + return fluid_list_sort_merge(fluid_list_sort(list, compare_func), + fluid_list_sort(l2, compare_func), + compare_func); +} + + +fluid_list_t * +fluid_list_last(fluid_list_t *list) +{ + if(list) + { + while(list->next) + { + list = list->next; + } + } + + return list; +} + +int +fluid_list_size(fluid_list_t *list) +{ + int n = 0; + + while(list) + { + n++; + list = list->next; + } + + return n; +} + +fluid_list_t *fluid_list_insert_at(fluid_list_t *list, int n, void *data) +{ + fluid_list_t *new_list; + fluid_list_t *cur; + fluid_list_t *prev = NULL; + + new_list = new_fluid_list(); + new_list->data = data; + + cur = list; + + while((n-- > 0) && cur) + { + prev = cur; + cur = cur->next; + } + + new_list->next = cur; + + if(prev) + { + prev->next = new_list; + return list; + } + else + { + return new_list; + } +} + +/* Compare function to sort strings alphabetically, + * for use with fluid_list_sort(). */ +int +fluid_list_str_compare_func(const void *a, const void *b) +{ + if(a && b) + { + return FLUID_STRCMP(a, b); + } + + if(!a && !b) + { + return 0; + } + + if(a) + { + return -1; + } + + return 1; +} + +int fluid_list_idx(fluid_list_t *list, void *data) +{ + int i = 0; + + while(list) + { + if (list->data == data) + { + return i; + } + list = list->next; + } + + return -1; +} diff --git a/thirdparty/fluidsynth/src/utils/fluid_list.h b/thirdparty/fluidsynth/src/utils/fluid_list.h new file mode 100644 index 000000000..a290135ce --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_list.h @@ -0,0 +1,63 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _FLUID_LIST_H +#define _FLUID_LIST_H + +#include "fluidsynth_priv.h" + +/* + * + * Lists + * + * A sound font loader has to pack the data from the .SF2 file into + * list structures of this type. + * + */ + +typedef struct _fluid_list_t fluid_list_t; + +typedef int (*fluid_compare_func_t)(const void *a, const void *b); + +struct _fluid_list_t +{ + void *data; + fluid_list_t *next; +}; + +fluid_list_t *new_fluid_list(void); +void delete_fluid_list(fluid_list_t *list); +void delete1_fluid_list(fluid_list_t *list); +fluid_list_t *fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func); +fluid_list_t *fluid_list_append(fluid_list_t *list, void *data); +fluid_list_t *fluid_list_prepend(fluid_list_t *list, void *data); +fluid_list_t *fluid_list_remove(fluid_list_t *list, void *data); +fluid_list_t *fluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink); +fluid_list_t *fluid_list_nth(fluid_list_t *list, int n); +fluid_list_t *fluid_list_last(fluid_list_t *list); +fluid_list_t *fluid_list_insert_at(fluid_list_t *list, int n, void *data); +int fluid_list_idx(fluid_list_t *list, void *data); +int fluid_list_size(fluid_list_t *list); + +#define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL) +#define fluid_list_get(slist) ((slist) ? ((slist)->data) : NULL) + +int fluid_list_str_compare_func(const void *a, const void *b); + +#endif /* _FLUID_LIST_H */ diff --git a/thirdparty/fluidsynth/src/utils/fluid_ringbuffer.c b/thirdparty/fluidsynth/src/utils/fluid_ringbuffer.c new file mode 100644 index 000000000..e9fc4ddd3 --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_ringbuffer.c @@ -0,0 +1,90 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* + * Josh Green + * 2009-05-28 + */ + +#include "fluid_ringbuffer.h" +#include "fluid_sys.h" + + +/** + * Create a lock free queue with a fixed maximum count and size of elements. + * @param count Count of elements in queue (fixed max number of queued elements) + * @return New lock free queue or NULL if out of memory (error message logged) + * + * Lockless FIFO queues don't use any locking mechanisms and can therefore be + * advantageous in certain situations, such as passing data between a lower + * priority thread and a higher "real time" thread, without potential lock + * contention which could stall the high priority thread. Note that there may + * only be one producer thread and one consumer thread. + */ +fluid_ringbuffer_t * +new_fluid_ringbuffer(int count, size_t elementsize) +{ + fluid_ringbuffer_t *queue; + + fluid_return_val_if_fail(count > 0, NULL); + + queue = FLUID_NEW(fluid_ringbuffer_t); + + if(!queue) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + queue->array = FLUID_MALLOC(elementsize * count); + + if(!queue->array) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + delete_fluid_ringbuffer(queue); + return NULL; + } + + /* Clear array, in case dynamic pointer reclaiming is being done */ + FLUID_MEMSET(queue->array, 0, elementsize * count); + + queue->totalcount = count; + queue->elementsize = elementsize; + fluid_atomic_int_set(&queue->count, 0); + queue->in = 0; + queue->out = 0; + + return (queue); +} + +/** + * Free an event queue. + * @param queue Lockless queue instance + * + * Care must be taken when freeing a queue, to ensure that the consumer and + * producer threads will no longer access it. + */ +void +delete_fluid_ringbuffer(fluid_ringbuffer_t *queue) +{ + fluid_return_if_fail(queue != NULL); + FLUID_FREE(queue->array); + FLUID_FREE(queue); +} diff --git a/thirdparty/fluidsynth/src/utils/fluid_ringbuffer.h b/thirdparty/fluidsynth/src/utils/fluid_ringbuffer.h new file mode 100644 index 000000000..6b0a2df37 --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_ringbuffer.h @@ -0,0 +1,133 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _FLUID_RINGBUFFER_H +#define _FLUID_RINGBUFFER_H + +#include "fluid_sys.h" + +/* + * Lockless event queue instance. + */ +struct _fluid_ringbuffer_t +{ + char *array; /**< Queue array of arbitrary size elements */ + int totalcount; /**< Total count of elements in array */ + fluid_atomic_int_t count; /**< Current count of elements */ + int in; /**< Index in queue to store next pushed element */ + int out; /**< Index in queue of next popped element */ + size_t elementsize; /**< Size of each element */ + void *userdata; +}; + +typedef struct _fluid_ringbuffer_t fluid_ringbuffer_t; + + +fluid_ringbuffer_t *new_fluid_ringbuffer(int count, size_t elementsize); +void delete_fluid_ringbuffer(fluid_ringbuffer_t *queue); + +/** + * Get pointer to next input array element in queue. + * @param queue Lockless queue instance + * @param offset Normally zero, or more if you need to push several items at once + * @return Pointer to array element in queue to store data to or NULL if queue is full + * + * This function along with fluid_ringbuffer_next_inptr() form a queue "push" + * operation and is split into 2 functions to avoid an element copy. Note that + * the returned array element pointer may contain the data of a previous element + * if the queue has wrapped around. This can be used to reclaim pointers to + * allocated memory, etc. + */ +static FLUID_INLINE void * +fluid_ringbuffer_get_inptr(fluid_ringbuffer_t *queue, int offset) +{ + return fluid_atomic_int_get(&queue->count) + offset >= queue->totalcount ? NULL + : queue->array + queue->elementsize * ((queue->in + offset) % queue->totalcount); +} + +/** + * Advance the input queue index to complete a "push" operation. + * @param queue Lockless queue instance + * @param count Normally one, or more if you need to push several items at once + * + * This function along with fluid_ringbuffer_get_inptr() form a queue "push" + * operation and is split into 2 functions to avoid element copy. + */ +static FLUID_INLINE void +fluid_ringbuffer_next_inptr(fluid_ringbuffer_t *queue, int count) +{ + fluid_atomic_int_add(&queue->count, count); + + queue->in += count; + + if(queue->in >= queue->totalcount) + { + queue->in -= queue->totalcount; + } +} + +/** + * Get amount of items currently in queue + * @param queue Lockless queue instance + * @return amount of items currently in queue + */ +static FLUID_INLINE int +fluid_ringbuffer_get_count(fluid_ringbuffer_t *queue) +{ + return fluid_atomic_int_get(&queue->count); +} + + +/** + * Get pointer to next output array element in queue. + * @param queue Lockless queue instance + * @return Pointer to array element data in the queue or NULL if empty, can only + * be used up until fluid_ringbuffer_next_outptr() is called. + * + * This function along with fluid_ringbuffer_next_outptr() form a queue "pop" + * operation and is split into 2 functions to avoid an element copy. + */ +static FLUID_INLINE void * +fluid_ringbuffer_get_outptr(fluid_ringbuffer_t *queue) +{ + return fluid_ringbuffer_get_count(queue) == 0 ? NULL + : queue->array + queue->elementsize * queue->out; +} + + +/** + * Advance the output queue index to complete a "pop" operation. + * @param queue Lockless queue instance + * + * This function along with fluid_ringbuffer_get_outptr() form a queue "pop" + * operation and is split into 2 functions to avoid an element copy. + */ +static FLUID_INLINE void +fluid_ringbuffer_next_outptr(fluid_ringbuffer_t *queue) +{ + fluid_atomic_int_add(&queue->count, -1); + + if(++queue->out == queue->totalcount) + { + queue->out = 0; + } +} + +#endif /* _FLUID_ringbuffer_H */ diff --git a/thirdparty/fluidsynth/src/utils/fluid_settings.c b/thirdparty/fluidsynth/src/utils/fluid_settings.c new file mode 100644 index 000000000..ed9db4f71 --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_settings.c @@ -0,0 +1,1999 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sys.h" +#include "fluid_hash.h" +#include "fluid_synth.h" +#include "fluid_adriver.h" +#include "fluid_mdriver.h" +#include "fluid_settings.h" +#include "fluid_midi.h" + +/* maximum allowed components of a settings variable (separated by '.') */ +#define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */ +#define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */ + +static void fluid_settings_init(fluid_settings_t *settings); +static void fluid_settings_key_destroy_func(void *value); +static void fluid_settings_value_destroy_func(void *value); +static int fluid_settings_tokenize(const char *s, char *buf, char **ptr); + +/* Common structure to all settings nodes */ +typedef struct +{ + char *value; + char *def; + int hints; + fluid_list_t *options; + fluid_str_update_t update; + void *data; +} fluid_str_setting_t; + +typedef struct +{ + double value; + double def; + double min; + double max; + int hints; + fluid_num_update_t update; + void *data; +} fluid_num_setting_t; + +typedef struct +{ + int value; + int def; + int min; + int max; + int hints; + fluid_int_update_t update; + void *data; +} fluid_int_setting_t; + +typedef struct +{ + fluid_hashtable_t *hashtable; +} fluid_set_setting_t; + +typedef struct +{ + int type; /**< fluid_types_enum */ + + union + { + fluid_str_setting_t str; + fluid_num_setting_t num; + fluid_int_setting_t i; + fluid_set_setting_t set; + }; +} fluid_setting_node_t; + +static fluid_setting_node_t * +new_fluid_str_setting(const char *value, const char *def, int hints) +{ + fluid_setting_node_t *node; + fluid_str_setting_t *str; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_STR_TYPE; + + str = &node->str; + str->value = value ? FLUID_STRDUP(value) : NULL; + str->def = def ? FLUID_STRDUP(def) : NULL; + str->hints = hints; + str->options = NULL; + str->update = NULL; + str->data = NULL; + return node; +} + +static void +delete_fluid_str_setting(fluid_setting_node_t *node) +{ + fluid_return_if_fail(node != NULL); + + FLUID_ASSERT(node->type == FLUID_STR_TYPE); + + FLUID_FREE(node->str.value); + FLUID_FREE(node->str.def); + + if(node->str.options) + { + fluid_list_t *list = node->str.options; + + while(list) + { + FLUID_FREE(list->data); + list = fluid_list_next(list); + } + + delete_fluid_list(node->str.options); + } + + FLUID_FREE(node); +} + + +static fluid_setting_node_t * +new_fluid_num_setting(double min, double max, double def, int hints) +{ + fluid_setting_node_t *node; + fluid_num_setting_t *num; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_NUM_TYPE; + + num = &node->num; + num->value = def; + num->def = def; + num->min = min; + num->max = max; + num->hints = hints; + num->update = NULL; + num->data = NULL; + + return node; +} + +static void +delete_fluid_num_setting(fluid_setting_node_t *node) +{ + fluid_return_if_fail(node != NULL); + + FLUID_ASSERT(node->type == FLUID_NUM_TYPE); + FLUID_FREE(node); +} + +static fluid_setting_node_t * +new_fluid_int_setting(int min, int max, int def, int hints) +{ + fluid_setting_node_t *node; + fluid_int_setting_t *i; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_INT_TYPE; + + i = &node->i; + i->value = def; + i->def = def; + i->min = min; + i->max = max; + i->hints = hints; + i->update = NULL; + i->data = NULL; + return node; +} + +static void +delete_fluid_int_setting(fluid_setting_node_t *node) +{ + fluid_return_if_fail(node != NULL); + + FLUID_ASSERT(node->type == FLUID_INT_TYPE); + FLUID_FREE(node); +} + +static fluid_setting_node_t * +new_fluid_set_setting(void) +{ + fluid_setting_node_t *node; + fluid_set_setting_t *set; + + node = FLUID_NEW(fluid_setting_node_t); + + if(!node) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = FLUID_SET_TYPE; + set = &node->set; + + set->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, + fluid_settings_key_destroy_func, + fluid_settings_value_destroy_func); + + if(!set->hashtable) + { + FLUID_FREE(node); + return NULL; + } + + return node; +} + +static void +delete_fluid_set_setting(fluid_setting_node_t *node) +{ + fluid_return_if_fail(node != NULL); + + FLUID_ASSERT(node->type == FLUID_SET_TYPE); + delete_fluid_hashtable(node->set.hashtable); + FLUID_FREE(node); +} + +/** + * Create a new settings object + * + * @return the pointer to the settings object + */ +fluid_settings_t * +new_fluid_settings(void) +{ + fluid_settings_t *settings; + + settings = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal, + fluid_settings_key_destroy_func, + fluid_settings_value_destroy_func); + + if(settings == NULL) + { + return NULL; + } + + fluid_rec_mutex_init(settings->mutex); + fluid_settings_init(settings); + return settings; +} + +/** + * Delete the provided settings object + * + * @param settings a settings object + */ +void +delete_fluid_settings(fluid_settings_t *settings) +{ + fluid_return_if_fail(settings != NULL); + + fluid_rec_mutex_destroy(settings->mutex); + delete_fluid_hashtable(settings); +} + +/* Settings hash key destroy function */ +static void +fluid_settings_key_destroy_func(void *value) +{ + FLUID_FREE(value); /* Free the string key value */ +} + +/* Settings hash value destroy function */ +static void +fluid_settings_value_destroy_func(void *value) +{ + fluid_setting_node_t *node = value; + + switch(node->type) + { + case FLUID_NUM_TYPE: + delete_fluid_num_setting(node); + break; + + case FLUID_INT_TYPE: + delete_fluid_int_setting(node); + break; + + case FLUID_STR_TYPE: + delete_fluid_str_setting(node); + break; + + case FLUID_SET_TYPE: + delete_fluid_set_setting(node); + break; + } +} + +void +fluid_settings_init(fluid_settings_t *settings) +{ + fluid_return_if_fail(settings != NULL); + + fluid_synth_settings(settings); + //fluid_shell_settings(settings); + fluid_player_settings(settings); + fluid_file_renderer_settings(settings); + fluid_audio_driver_settings(settings); + fluid_midi_driver_settings(settings); +} + +static int +fluid_settings_tokenize(const char *s, char *buf, char **ptr) +{ + char *tokstr, *tok; + int n = 0; + + if(FLUID_STRLEN(s) > MAX_SETTINGS_LABEL) + { + FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars", + MAX_SETTINGS_LABEL); + return 0; + } + + FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */ + tokstr = buf; + + while((tok = fluid_strtok(&tokstr, "."))) + { + if(n >= MAX_SETTINGS_TOKENS) + { + FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d", + MAX_SETTINGS_TOKENS); + return 0; + } + else + { + ptr[n++] = tok; + } + } + + return n; +} + +/** + * Get a setting name, value and type + * + * @param settings a settings object + * @param name Settings name + * @param value Location to store setting node if found + * @return #FLUID_OK if the node exists, #FLUID_FAILED otherwise + */ +static int +fluid_settings_get(fluid_settings_t *settings, const char *name, + fluid_setting_node_t **value) +{ + fluid_hashtable_t *table = settings; + fluid_setting_node_t *node = NULL; + char *tokens[MAX_SETTINGS_TOKENS]; + char buf[MAX_SETTINGS_LABEL + 1]; + int ntokens; + int n; + + ntokens = fluid_settings_tokenize(name, buf, tokens); + + if(table == NULL || ntokens <= 0) + { + return FLUID_FAILED; + } + + for(n = 0; n < ntokens; n++) + { + + node = fluid_hashtable_lookup(table, tokens[n]); + + if(!node) + { + return FLUID_FAILED; + } + + table = (node->type == FLUID_SET_TYPE) ? node->set.hashtable : NULL; + } + + if(value) + { + *value = node; + } + + return FLUID_OK; +} + +/** + * Set a setting name, value and type, replacing it if already exists + * + * @param settings a settings object + * @param name Settings name + * @param value Node instance to assign (used directly) + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise + */ +static int +fluid_settings_set(fluid_settings_t *settings, const char *name, fluid_setting_node_t *value) +{ + fluid_hashtable_t *table = settings; + fluid_setting_node_t *node; + char *tokens[MAX_SETTINGS_TOKENS]; + char buf[MAX_SETTINGS_LABEL + 1]; + int n, num; + char *dupname; + + num = fluid_settings_tokenize(name, buf, tokens); + + if(num == 0) + { + return FLUID_FAILED; + } + + num--; + + for(n = 0; n < num; n++) + { + + node = fluid_hashtable_lookup(table, tokens[n]); + + if(node) + { + + if(node->type == FLUID_SET_TYPE) + { + table = node->set.hashtable; + } + else + { + /* path ends prematurely */ + FLUID_LOG(FLUID_ERR, "'%s' is not a node. Name of the setting was '%s'", tokens[n], name); + return FLUID_FAILED; + } + + } + else + { + /* create a new node */ + fluid_setting_node_t *setnode; + + dupname = FLUID_STRDUP(tokens[n]); + setnode = new_fluid_set_setting(); + + if(!dupname || !setnode) + { + if(dupname) + { + FLUID_FREE(dupname); + } + else + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + + if(setnode) + { + delete_fluid_set_setting(setnode); + } + + return FLUID_FAILED; + } + + fluid_hashtable_insert(table, dupname, setnode); + table = setnode->set.hashtable; + } + } + + dupname = FLUID_STRDUP(tokens[num]); + + if(!dupname) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return FLUID_FAILED; + } + + fluid_hashtable_insert(table, dupname, value); + + return FLUID_OK; +} + +/** + * Registers a new string value for the specified setting. + * + * @param settings a settings object + * @param name the setting's name + * @param def the default value for the setting + * @param hints the hints for the setting + * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise + */ +int +fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) != FLUID_OK) + { + node = new_fluid_str_setting(def, def, hints); + retval = fluid_settings_set(settings, name, node); + + if(retval != FLUID_OK) + { + delete_fluid_str_setting(node); + } + } + else + { + /* if variable already exists, don't change its value. */ + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + FLUID_FREE(setting->def); + setting->def = def ? FLUID_STRDUP(def) : NULL; + setting->hints = hints; + retval = FLUID_OK; + } + else + { + FLUID_LOG(FLUID_ERR, "Failed to register string setting '%s' as it already exists with a different type", name); + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Registers a new float value for the specified setting. + * + * @param settings a settings object + * @param name the setting's name + * @param def the default value for the setting + * @param min the smallest allowed value for the setting + * @param max the largest allowed value for the setting + * @param hints the hints for the setting + * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise + */ +int +fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def, + double min, double max, int hints) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + + /* For now, all floating point settings are bounded below and above */ + hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) != FLUID_OK) + { + /* insert a new setting */ + node = new_fluid_num_setting(min, max, def, hints); + retval = fluid_settings_set(settings, name, node); + + if(retval != FLUID_OK) + { + delete_fluid_num_setting(node); + } + } + else + { + if(node->type == FLUID_NUM_TYPE) + { + /* update the existing setting but don't change its value */ + fluid_num_setting_t *setting = &node->num; + setting->min = min; + setting->max = max; + setting->def = def; + setting->hints = hints; + retval = FLUID_OK; + } + else + { + /* type mismatch */ + FLUID_LOG(FLUID_ERR, "Failed to register numeric setting '%s' as it already exists with a different type", name); + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Registers a new integer value for the specified setting. + * + * @param settings a settings object + * @param name the setting's name + * @param def the default value for the setting + * @param min the smallest allowed value for the setting + * @param max the largest allowed value for the setting + * @param hints the hints for the setting + * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise + */ +int +fluid_settings_register_int(fluid_settings_t *settings, const char *name, int def, + int min, int max, int hints) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + + /* For now, all integer settings are bounded below and above */ + hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE; + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) != FLUID_OK) + { + /* insert a new setting */ + node = new_fluid_int_setting(min, max, def, hints); + retval = fluid_settings_set(settings, name, node); + + if(retval != FLUID_OK) + { + delete_fluid_int_setting(node); + } + } + else + { + if(node->type == FLUID_INT_TYPE) + { + /* update the existing setting but don't change its value */ + fluid_int_setting_t *setting = &node->i; + setting->min = min; + setting->max = max; + setting->def = def; + setting->hints = hints; + retval = FLUID_OK; + } + else + { + /* type mismatch */ + FLUID_LOG(FLUID_ERR, "Failed to register int setting '%s' as it already exists with a different type", name); + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Registers a callback for the specified string setting. + * + * @param settings a settings object + * @param name the setting's name + * @param callback an update function for the setting + * @param data user supplied data passed to the update function + * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise + */ +int fluid_settings_callback_str(fluid_settings_t *settings, const char *name, + fluid_str_update_t callback, void *data) +{ + fluid_setting_node_t *node; + fluid_str_setting_t *setting; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || node->type != FLUID_STR_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; + } + + setting = &node->str; + setting->update = callback; + setting->data = data; + + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_OK; +} + +/** + * Registers a callback for the specified numeric setting. + * + * @param settings a settings object + * @param name the setting's name + * @param callback an update function for the setting + * @param data user supplied data passed to the update function + * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise + */ +int fluid_settings_callback_num(fluid_settings_t *settings, const char *name, + fluid_num_update_t callback, void *data) +{ + fluid_setting_node_t *node; + fluid_num_setting_t *setting; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || node->type != FLUID_NUM_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; + } + + setting = &node->num; + setting->update = callback; + setting->data = data; + + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_OK; +} + +/** + * Registers a callback for the specified int setting. + * + * @param settings a settings object + * @param name the setting's name + * @param callback an update function for the setting + * @param data user supplied data passed to the update function + * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise + */ +int fluid_settings_callback_int(fluid_settings_t *settings, const char *name, + fluid_int_update_t callback, void *data) +{ + fluid_setting_node_t *node; + fluid_int_setting_t *setting; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || node->type != FLUID_INT_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; + } + + setting = &node->i; + setting->update = callback; + setting->data = data; + + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_OK; +} + +void* fluid_settings_get_user_data(fluid_settings_t * settings, const char *name) +{ + fluid_setting_node_t *node; + void* retval = NULL; + + fluid_return_val_if_fail(settings != NULL, NULL); + fluid_return_val_if_fail(name != NULL, NULL); + fluid_return_val_if_fail(name[0] != '\0', NULL); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_NUM_TYPE) + { + fluid_num_setting_t *setting = &node->num; + retval = setting->data; + } + else if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + retval = setting->data; + } + else if(node->type == FLUID_INT_TYPE) + { + fluid_int_setting_t *setting = &node->i; + retval = setting->data; + } + } + + fluid_rec_mutex_unlock(settings->mutex); + return retval; +} + +/** + * Get the type of the setting with the given name + * + * @param settings a settings object + * @param name a setting's name + * @return the type for the named setting (see #fluid_types_enum), or #FLUID_NO_TYPE when it does not exist + */ +int +fluid_settings_get_type(fluid_settings_t *settings, const char *name) +{ + fluid_setting_node_t *node; + int type = FLUID_NO_TYPE; + + fluid_return_val_if_fail(settings != NULL, type); + fluid_return_val_if_fail(name != NULL, type); + fluid_return_val_if_fail(name[0] != '\0', type); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + type = node->type; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return type; +} + +/** + * Get the hints for the named setting as an integer bitmap + * + * @param settings a settings object + * @param name a setting's name + * @param hints set to the hints associated to the setting if it exists + * @return #FLUID_OK if hints associated to the named setting exist, #FLUID_FAILED otherwise + */ +int +fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *hints) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_NUM_TYPE) + { + fluid_num_setting_t *setting = &node->num; + *hints = setting->hints; + retval = FLUID_OK; + } + else if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + *hints = setting->hints; + retval = FLUID_OK; + } + else if(node->type == FLUID_INT_TYPE) + { + fluid_int_setting_t *setting = &node->i; + *hints = setting->hints; + retval = FLUID_OK; + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Ask whether the setting is changeable in real-time. + * + * @param settings a settings object + * @param name a setting's name + * @return TRUE if the setting is changeable in real-time, FALSE otherwise + * + * @note Before using this function, make sure the @p settings object has already been used to create + * a synthesizer, a MIDI driver, an audio driver, a MIDI player, or a command handler (depending on + * which settings you want to query). + */ +int +fluid_settings_is_realtime(fluid_settings_t *settings, const char *name) +{ + fluid_setting_node_t *node; + int isrealtime = FALSE; + + fluid_return_val_if_fail(settings != NULL, 0); + fluid_return_val_if_fail(name != NULL, 0); + fluid_return_val_if_fail(name[0] != '\0', 0); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_NUM_TYPE) + { + fluid_num_setting_t *setting = &node->num; + isrealtime = setting->update != NULL; + } + else if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + isrealtime = setting->update != NULL; + } + else if(node->type == FLUID_INT_TYPE) + { + fluid_int_setting_t *setting = &node->i; + isrealtime = setting->update != NULL; + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return isrealtime; +} + +/** + * Set a string value for a named setting + * + * @param settings a settings object + * @param name a setting's name + * @param str new string value + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise + */ +int +fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char *str) +{ + fluid_setting_node_t *node; + fluid_str_setting_t *setting; + char *new_value = NULL; + fluid_str_update_t callback = NULL; + void *data = NULL; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || (node->type != FLUID_STR_TYPE)) + { + FLUID_LOG(FLUID_ERR, "Unknown string setting '%s'", name); + goto error_recovery; + } + + setting = &node->str; + + if(setting->value) + { + FLUID_FREE(setting->value); + } + + if(str) + { + new_value = FLUID_STRDUP(str); + + if(new_value == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + goto error_recovery; + } + } + + setting->value = new_value; + + callback = setting->update; + data = setting->data; + + /* Release the mutex before calling the update callback, to avoid + * possible deadlocks with FluidSynths API lock */ + fluid_rec_mutex_unlock(settings->mutex); + + if(callback) + { + (*callback)(data, name, new_value); + } + + return FLUID_OK; + +error_recovery: + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; +} + +/** + * Copy the value of a string setting into the provided buffer (thread safe) + * + * @param settings a settings object + * @param name a setting's name + * @param str Caller supplied buffer to copy string value to + * @param len Size of 'str' buffer (no more than len bytes will be written, which + * will always include a zero terminator) + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise + * + * @note A size of 256 should be more than sufficient for the string buffer. + * + * @since 1.1.0 + */ +int +fluid_settings_copystr(fluid_settings_t *settings, const char *name, + char *str, int len) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(str != NULL, retval); + fluid_return_val_if_fail(len > 0, retval); + + str[0] = 0; + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + + if(setting->value) + { + FLUID_STRNCPY(str, setting->value, len); + } + + retval = FLUID_OK; + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; + + if(setting->hints & FLUID_HINT_TOGGLED) + { + FLUID_STRNCPY(str, setting->value ? "yes" : "no", len); + + retval = FLUID_OK; + } + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Duplicate the value of a string setting + * + * @param settings a settings object + * @param name a setting's name + * @param str Location to store pointer to allocated duplicate string + * @return #FLUID_OK if the value exists and was successfully duplicated, #FLUID_FAILED otherwise + * + * Like fluid_settings_copystr() but allocates a new copy of the string. Caller + * owns the string and should free it with fluid_free() when done using it. + * + * @since 1.1.0 + */ +int +fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(str != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + + if(setting->value) + { + *str = FLUID_STRDUP(setting->value); + + if(!*str) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + } + + if(!setting->value || *str) + { + retval = FLUID_OK; /* Don't set to FLUID_OK if out of memory */ + } + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; + + if(setting->hints & FLUID_HINT_TOGGLED) + { + *str = FLUID_STRDUP(setting->value ? "yes" : "no"); + + if(!*str) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + + if(!setting->value || *str) + { + retval = FLUID_OK; /* Don't set to FLUID_OK if out of memory */ + } + } + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + + +/** + * Test a string setting for some value. + * + * @param settings a settings object + * @param name a setting's name + * @param s a string to be tested + * @return TRUE if the value exists and is equal to \c s, FALSE otherwise + */ +int +fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *s) +{ + fluid_setting_node_t *node; + int retval = FALSE; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(s != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + + if(setting->value) + { + retval = FLUID_STRCMP(setting->value, s) == 0; + } + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; + + if(setting->hints & FLUID_HINT_TOGGLED) + { + retval = FLUID_STRCMP(setting->value ? "yes" : "no", s) == 0; + } + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Get the default value of a string setting. + * + * @param settings a settings object + * @param name a setting's name + * @param def the default string value of the setting if it exists + * @return FLUID_OK if a default value exists, FLUID_FAILED otherwise + * + * @note The returned string is not owned by the caller and should not be modified or freed. + */ +int +fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def) +{ + fluid_setting_node_t *node; + char *retval = NULL; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK) + { + if(node->type == FLUID_STR_TYPE) + { + fluid_str_setting_t *setting = &node->str; + retval = setting->def; + } + else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */ + { + fluid_int_setting_t *setting = &node->i; + + if(setting->hints & FLUID_HINT_TOGGLED) + { + retval = setting->def ? "yes" : "no"; + } + } + } + + *def = retval; + fluid_rec_mutex_unlock(settings->mutex); + + return retval != NULL ? FLUID_OK : FLUID_FAILED; +} + +/** + * Add an option to a string setting (like an enumeration value). + * + * @param settings a settings object + * @param name a setting's name + * @param s option string to add + * @return #FLUID_OK if the setting exists and option was added, #FLUID_FAILED otherwise + * + * Causes the setting's #FLUID_HINT_OPTIONLIST hint to be set. + */ +int +fluid_settings_add_option(fluid_settings_t *settings, const char *name, const char *s) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(s != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_STR_TYPE)) + { + fluid_str_setting_t *setting = &node->str; + char *copy = FLUID_STRDUP(s); + setting->options = fluid_list_append(setting->options, copy); + setting->hints |= FLUID_HINT_OPTIONLIST; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Remove an option previously assigned by fluid_settings_add_option(). + * + * @param settings a settings object + * @param name a setting's name + * @param s option string to remove + * @return #FLUID_OK if the setting exists and option was removed, #FLUID_FAILED otherwise + */ +int +fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(s != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_STR_TYPE)) + { + + fluid_str_setting_t *setting = &node->str; + fluid_list_t *list = setting->options; + + while(list) + { + char *option = (char *) fluid_list_get(list); + + if(FLUID_STRCMP(s, option) == 0) + { + FLUID_FREE(option); + setting->options = fluid_list_remove_link(setting->options, list); + retval = FLUID_OK; + break; + } + + list = fluid_list_next(list); + } + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Set a numeric value for a named setting. + * + * @param settings a settings object + * @param name a setting's name + * @param val new setting's value + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise + */ +int +fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val) +{ + fluid_setting_node_t *node; + fluid_num_setting_t *setting; + fluid_num_update_t callback = NULL; + void *data = NULL; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || (node->type != FLUID_NUM_TYPE)) + { + FLUID_LOG(FLUID_ERR, "Unknown numeric setting '%s'", name); + goto error_recovery; + } + + setting = &node->num; + + if(val < setting->min || val > setting->max) + { + FLUID_LOG(FLUID_ERR, "requested set value for '%s' out of range", name); + goto error_recovery; + } + + setting->value = val; + + callback = setting->update; + data = setting->data; + + /* Release the mutex before calling the update callback, to avoid + * possible deadlocks with FluidSynths API lock */ + fluid_rec_mutex_unlock(settings->mutex); + + if(callback) + { + (*callback)(data, name, val); + } + + return FLUID_OK; + +error_recovery: + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; +} + +/** + * Get the numeric value of a named setting + * + * @param settings a settings object + * @param name a setting's name + * @param val variable pointer to receive the setting's numeric value + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise + */ +int +fluid_settings_getnum(fluid_settings_t *settings, const char *name, double *val) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_NUM_TYPE)) + { + fluid_num_setting_t *setting = &node->num; + *val = setting->value; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * float-typed wrapper for fluid_settings_getnum + * + * @param settings a settings object + * @param name a setting's name + * @param val variable pointer to receive the setting's float value + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise + */ +int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val) +{ + double tmp; + + if(fluid_settings_getnum(settings, name, &tmp) == FLUID_OK) + { + *val = tmp; + return FLUID_OK; + } + + return FLUID_FAILED; +} + +/** + * Get the range of values of a numeric setting + * + * @param settings a settings object + * @param name a setting's name + * @param min setting's range lower limit + * @param max setting's range upper limit + * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise + */ +int +fluid_settings_getnum_range(fluid_settings_t *settings, const char *name, + double *min, double *max) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(min != NULL, retval); + fluid_return_val_if_fail(max != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_NUM_TYPE)) + { + fluid_num_setting_t *setting = &node->num; + *min = setting->min; + *max = setting->max; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Get the default value of a named numeric (double) setting + * + * @param settings a settings object + * @param name a setting's name + * @param val set to the default value if the named setting exists + * @return #FLUID_OK if the default value of the named setting exists, #FLUID_FAILED otherwise + */ +int +fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_NUM_TYPE)) + { + fluid_num_setting_t *setting = &node->num; + *val = setting->def; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Set an integer value for a setting + * + * @param settings a settings object + * @param name a setting's name + * @param val new setting's integer value + * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise + */ +int +fluid_settings_setint(fluid_settings_t *settings, const char *name, int val) +{ + fluid_setting_node_t *node; + fluid_int_setting_t *setting; + fluid_int_update_t callback = NULL; + void *data = NULL; + + fluid_return_val_if_fail(settings != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name != NULL, FLUID_FAILED); + fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED); + + fluid_rec_mutex_lock(settings->mutex); + + if((fluid_settings_get(settings, name, &node) != FLUID_OK) + || (node->type != FLUID_INT_TYPE)) + { + FLUID_LOG(FLUID_ERR, "Unknown integer parameter '%s'", name); + goto error_recovery; + } + + setting = &node->i; + + if(val < setting->min || val > setting->max) + { + FLUID_LOG(FLUID_ERR, "requested set value for setting '%s' out of range", name); + goto error_recovery; + } + + setting->value = val; + + callback = setting->update; + data = setting->data; + + /* Release the mutex before calling the update callback, to avoid + * possible deadlocks with FluidSynths API lock */ + fluid_rec_mutex_unlock(settings->mutex); + + if(callback) + { + (*callback)(data, name, val); + } + + return FLUID_OK; + +error_recovery: + fluid_rec_mutex_unlock(settings->mutex); + return FLUID_FAILED; +} + +/** + * Get an integer value setting. + * + * @param settings a settings object + * @param name a setting's name + * @param val pointer to a variable to receive the setting's integer value + * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise + */ +int +fluid_settings_getint(fluid_settings_t *settings, const char *name, int *val) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_INT_TYPE)) + { + fluid_int_setting_t *setting = &node->i; + *val = setting->value; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Get the range of values of an integer setting + * + * @param settings a settings object + * @param name a setting's name + * @param min setting's range lower limit + * @param max setting's range upper limit + * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise + */ +int +fluid_settings_getint_range(fluid_settings_t *settings, const char *name, + int *min, int *max) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(min != NULL, retval); + fluid_return_val_if_fail(max != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_INT_TYPE)) + { + fluid_int_setting_t *setting = &node->i; + *min = setting->min; + *max = setting->max; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Get the default value of an integer setting. + * + * @param settings a settings object + * @param name a setting's name + * @param val set to the setting's default integer value if it exists + * @return #FLUID_OK if the setting's default integer value exists, #FLUID_FAILED otherwise + */ +int fluid_settings_getint_default(fluid_settings_t *settings, const char *name, int *val) +{ + fluid_setting_node_t *node; + int retval = FLUID_FAILED; + + fluid_return_val_if_fail(settings != NULL, retval); + fluid_return_val_if_fail(name != NULL, retval); + fluid_return_val_if_fail(name[0] != '\0', retval); + fluid_return_val_if_fail(val != NULL, retval); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && (node->type == FLUID_INT_TYPE)) + { + fluid_int_setting_t *setting = &node->i; + *val = setting->def; + retval = FLUID_OK; + } + + fluid_rec_mutex_unlock(settings->mutex); + + return retval; +} + +/** + * Iterate the available options for a named string setting, calling the provided + * callback function for each existing option. + * + * @param settings a settings object + * @param name a setting's name + * @param data any user provided pointer + * @param func callback function to be called on each iteration + * + * @note Starting with FluidSynth 1.1.0 the \p func callback is called for each + * option in alphabetical order. Sort order was undefined in previous versions. + */ +void +fluid_settings_foreach_option(fluid_settings_t *settings, const char *name, + void *data, fluid_settings_foreach_option_t func) +{ + fluid_setting_node_t *node; + fluid_str_setting_t *setting; + fluid_list_t *p, *newlist = NULL; + + fluid_return_if_fail(settings != NULL); + fluid_return_if_fail(name != NULL); + fluid_return_if_fail(name[0] != '\0'); + fluid_return_if_fail(func != NULL); + + fluid_rec_mutex_lock(settings->mutex); /* ++ lock */ + + if(fluid_settings_get(settings, name, &node) != FLUID_OK + || node->type != FLUID_STR_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ + return; + } + + setting = &node->str; + + /* Duplicate option list */ + for(p = setting->options; p; p = p->next) + { + newlist = fluid_list_append(newlist, fluid_list_get(p)); + } + + /* Sort by name */ + newlist = fluid_list_sort(newlist, fluid_list_str_compare_func); + + for(p = newlist; p; p = p->next) + { + (*func)(data, name, (const char *)fluid_list_get(p)); + } + + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ + + delete_fluid_list(newlist); +} + +/** + * Count option string values for a string setting. + * + * @param settings a settings object + * @param name Name of setting + * @return Count of options for this string setting (0 if none, -1 if not found + * or not a string setting) + * + * @since 1.1.0 + */ +int +fluid_settings_option_count(fluid_settings_t *settings, const char *name) +{ + fluid_setting_node_t *node; + int count = -1; + + fluid_return_val_if_fail(settings != NULL, -1); + fluid_return_val_if_fail(name != NULL, -1); + fluid_return_val_if_fail(name[0] != '\0', -1); + + fluid_rec_mutex_lock(settings->mutex); + + if(fluid_settings_get(settings, name, &node) == FLUID_OK + && node->type == FLUID_STR_TYPE) + { + count = fluid_list_size(node->str.options); + } + + fluid_rec_mutex_unlock(settings->mutex); + + return (count); +} + +/** + * Concatenate options for a string setting together with a separator between. + * + * @param settings Settings object + * @param name Settings name + * @param separator String to use between options (NULL to use ", ") + * @return Newly allocated string or NULL on error (out of memory, not a valid + * setting \p name or not a string setting). Free the string when finished with it by using fluid_free(). + * + * @since 1.1.0 + */ +char * +fluid_settings_option_concat(fluid_settings_t *settings, const char *name, + const char *separator) +{ + fluid_setting_node_t *node; + fluid_list_t *p, *newlist = NULL; + size_t count, len; + char *str, *option; + + fluid_return_val_if_fail(settings != NULL, NULL); + fluid_return_val_if_fail(name != NULL, NULL); + fluid_return_val_if_fail(name[0] != '\0', NULL); + + if(!separator) + { + separator = ", "; + } + + fluid_rec_mutex_lock(settings->mutex); /* ++ lock */ + + if(fluid_settings_get(settings, name, &node) != FLUID_OK + || node->type != FLUID_STR_TYPE) + { + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ + return (NULL); + } + + /* Duplicate option list, count options and get total string length */ + for(p = node->str.options, count = 0, len = 0; p; p = p->next) + { + option = fluid_list_get(p); + + if(option) + { + newlist = fluid_list_append(newlist, option); + len += FLUID_STRLEN(option); + count++; + } + } + + if(count > 1) + { + len += (count - 1) * FLUID_STRLEN(separator); + } + + len++; /* For terminator */ + + /* Sort by name */ + newlist = fluid_list_sort(newlist, fluid_list_str_compare_func); + + str = FLUID_MALLOC(len); + + if(str) + { + str[0] = 0; + + for(p = newlist; p; p = p->next) + { + option = fluid_list_get(p); + strcat(str, option); + + if(p->next) + { + strcat(str, separator); + } + } + } + + fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */ + + delete_fluid_list(newlist); + + if(!str) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + } + + return (str); +} + +/* Structure passed to fluid_settings_foreach_iter recursive function */ +typedef struct +{ + char path[MAX_SETTINGS_LABEL + 1]; /* Maximum settings label length */ + fluid_list_t *names; /* For fluid_settings_foreach() */ +} fluid_settings_foreach_bag_t; + +static int +fluid_settings_foreach_iter(void *key, void *value, void *data) +{ + fluid_settings_foreach_bag_t *bag = data; + char *keystr = key; + fluid_setting_node_t *node = value; + size_t pathlen; + char *s; + + pathlen = FLUID_STRLEN(bag->path); + + if(pathlen > 0) + { + bag->path[pathlen] = '.'; + bag->path[pathlen + 1] = 0; + } + + strcat(bag->path, keystr); + + switch(node->type) + { + case FLUID_NUM_TYPE: + case FLUID_INT_TYPE: + case FLUID_STR_TYPE: + s = FLUID_STRDUP(bag->path); + + if(s) + { + bag->names = fluid_list_append(bag->names, s); + } + + break; + + case FLUID_SET_TYPE: + fluid_hashtable_foreach(node->set.hashtable, + fluid_settings_foreach_iter, bag); + break; + } + + bag->path[pathlen] = 0; + + return 0; +} + +/** + * Iterate the existing settings defined in a settings object, calling the + * provided callback function for each setting. + * + * @param settings a settings object + * @param data any user provided pointer + * @param func callback function to be called on each iteration + * + * @note Starting with FluidSynth 1.1.0 the \p func callback is called for each + * setting in alphabetical order. Sort order was undefined in previous versions. + */ +void +fluid_settings_foreach(fluid_settings_t *settings, void *data, + fluid_settings_foreach_t func) +{ + fluid_settings_foreach_bag_t bag; + fluid_setting_node_t *node; + fluid_list_t *p; + + fluid_return_if_fail(settings != NULL); + fluid_return_if_fail(func != NULL); + + bag.path[0] = 0; + bag.names = NULL; + + fluid_rec_mutex_lock(settings->mutex); + + /* Add all node names to the bag.names list */ + fluid_hashtable_foreach(settings, fluid_settings_foreach_iter, &bag); + + /* Sort names */ + bag.names = fluid_list_sort(bag.names, fluid_list_str_compare_func); + + /* Loop over names and call the callback */ + for(p = bag.names; p; p = p->next) + { + if(fluid_settings_get(settings, (const char *)(p->data), &node) == FLUID_OK + && node) + { + (*func)(data, (const char *)(p->data), node->type); + } + + FLUID_FREE(p->data); /* -- Free name */ + } + + fluid_rec_mutex_unlock(settings->mutex); + + delete_fluid_list(bag.names); /* -- Free names list */ +} + +/** + * Split a comma-separated list of integers and fill the passed + * in buffer with the parsed values. + * + * @param str the comma-separated string to split + * @param buf user-supplied buffer to hold the parsed numbers + * @param buf_len length of user-supplied buffer + * @return number of parsed values or -1 on failure + */ +int fluid_settings_split_csv(const char *str, int *buf, int buf_len) +{ + char *s; + char *tok; + char *tokstr; + int n = 0; + + s = tokstr = FLUID_STRDUP(str); + + if(s == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return -1; + } + + while((tok = fluid_strtok(&tokstr, ",")) && n < buf_len) + { + buf[n++] = atoi(tok); + } + + FLUID_FREE(s); + + return n; +} diff --git a/thirdparty/fluidsynth/src/utils/fluid_settings.h b/thirdparty/fluidsynth/src/utils/fluid_settings.h new file mode 100644 index 000000000..73a63e86d --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_settings.h @@ -0,0 +1,65 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef _FLUID_SETTINGS_H +#define _FLUID_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +int fluid_settings_add_option(fluid_settings_t *settings, const char *name, const char *s); +int fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s); + + +typedef void (*fluid_str_update_t)(void *data, const char *name, const char *value); + +int fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints); +int fluid_settings_callback_str(fluid_settings_t *settings, const char *name, + fluid_str_update_t fun, void *data); + + +typedef void (*fluid_num_update_t)(void *data, const char *name, double value); + +int fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def, + double min, double max, int hints); +int fluid_settings_callback_num(fluid_settings_t *settings, const char *name, + fluid_num_update_t fun, void *data); + +/* Type specific wrapper for fluid_settings_getnum */ +int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val); + + +typedef void (*fluid_int_update_t)(void *data, const char *name, int value); +int fluid_settings_register_int(fluid_settings_t *settings, const char *name, int def, + int min, int max, int hints); +int fluid_settings_callback_int(fluid_settings_t *settings, const char *name, + fluid_int_update_t fun, void *data); + +int fluid_settings_split_csv(const char *str, int *buf, int buf_len); + +void* fluid_settings_get_user_data(fluid_settings_t * settings, const char *name); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUID_SETTINGS_H */ diff --git a/thirdparty/fluidsynth/src/utils/fluid_sys.c b/thirdparty/fluidsynth/src/utils/fluid_sys.c new file mode 100644 index 000000000..91b257636 --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_sys.c @@ -0,0 +1,1803 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "fluid_sys.h" + + +#if WITH_READLINE +#include +#include +#endif + +#ifdef DBUS_SUPPORT +#include "fluid_rtkit.h" +#endif + +#if HAVE_PTHREAD_H && !defined(WIN32) +// Do not include pthread on windows. It includes winsock.h, which collides with ws2tcpip.h from fluid_sys.h +// It isn't need on Windows anyway. +#include +#endif + +/* WIN32 HACK - Flag used to differentiate between a file descriptor and a socket. + * Should work, so long as no SOCKET or file descriptor ends up with this bit set. - JG */ +#ifdef _WIN32 +#define FLUID_SOCKET_FLAG 0x40000000 +#else +#define FLUID_SOCKET_FLAG 0x00000000 +#define SOCKET_ERROR -1 +#define INVALID_SOCKET -1 +#endif + +/* SCHED_FIFO priority for high priority timer threads */ +#define FLUID_SYS_TIMER_HIGH_PRIO_LEVEL 10 + + +typedef struct +{ + fluid_thread_func_t func; + void *data; + int prio_level; +} fluid_thread_info_t; + +struct _fluid_timer_t +{ + long msec; + + // Pointer to a function to be executed by the timer. + // This field is set to NULL once the timer is finished to indicate completion. + // This allows for timed waits, rather than waiting forever as fluid_timer_join() does. + fluid_timer_callback_t callback; + void *data; + fluid_thread_t *thread; + int cont; + int auto_destroy; +}; + +struct _fluid_server_socket_t +{ + fluid_socket_t socket; + fluid_thread_t *thread; + int cont; + fluid_server_func_t func; + void *data; +}; + + +static int fluid_istream_gets(fluid_istream_t in, char *buf, int len); + +static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL] = +{ + fluid_default_log_function, + fluid_default_log_function, + fluid_default_log_function, + fluid_default_log_function, +#ifdef DEBUG + fluid_default_log_function +#else + NULL +#endif +}; +static void *fluid_log_user_data[LAST_LOG_LEVEL] = { NULL }; + +static const char fluid_libname[] = "fluidsynth"; + +/** + * Installs a new log function for a specified log level. + * @param level Log level to install handler for. + * @param fun Callback function handler to call for logged messages + * @param data User supplied data pointer to pass to log function + * @return The previously installed function. + */ +fluid_log_function_t +fluid_set_log_function(int level, fluid_log_function_t fun, void *data) +{ + fluid_log_function_t old = NULL; + + if((level >= 0) && (level < LAST_LOG_LEVEL)) + { + old = fluid_log_function[level]; + fluid_log_function[level] = fun; + fluid_log_user_data[level] = data; + } + + return old; +} + +/** + * Default log function which prints to the stderr. + * @param level Log level + * @param message Log message + * @param data User supplied data (not used) + */ +void +fluid_default_log_function(int level, const char *message, void *data) +{ + FILE *out; + +#if defined(WIN32) + out = stdout; +#else + out = stderr; +#endif + + switch(level) + { + case FLUID_PANIC: + FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message); + break; + + case FLUID_ERR: + FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message); + break; + + case FLUID_WARN: + FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message); + break; + + case FLUID_INFO: + FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); + break; + + case FLUID_DBG: + FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message); + break; + + default: + FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); + break; + } + + fflush(out); +} + +/** + * Print a message to the log. + * @param level Log level (#fluid_log_level). + * @param fmt Printf style format string for log message + * @param ... Arguments for printf 'fmt' message string + * @return Always returns #FLUID_FAILED + */ +int +fluid_log(int level, const char *fmt, ...) +{ + if((level >= 0) && (level < LAST_LOG_LEVEL)) + { + fluid_log_function_t fun = fluid_log_function[level]; + + if(fun != NULL) + { + char errbuf[1024]; + + va_list args; + va_start(args, fmt); + FLUID_VSNPRINTF(errbuf, sizeof(errbuf), fmt, args); + va_end(args); + + (*fun)(level, errbuf, fluid_log_user_data[level]); + } + } + + return FLUID_FAILED; +} + +void* fluid_alloc(size_t len) +{ + void* ptr = malloc(len); + +#if defined(DEBUG) && !defined(_MSC_VER) + // garbage initialize allocated memory for debug builds to ease reproducing + // bugs like 44453ff23281b3318abbe432fda90888c373022b . + // + // MSVC++ already garbage initializes allocated memory by itself (debug-heap). + // + // 0xCC because + // * it makes pointers reliably crash when dereferencing them, + // * floating points are still some valid but insanely huge negative number, and + // * if for whatever reason this allocated memory is executed, it'll trigger + // INT3 (...at least on x86) + if(ptr != NULL) + { + memset(ptr, 0xCC, len); + } +#endif + return ptr; +} + +/** + * Open a file with a UTF-8 string, even in Windows + * @param filename The name of the file to open + * @param mode The mode to open the file in + */ +FILE *fluid_fopen(const char *filename, const char *mode) +{ +#if defined(WIN32) + wchar_t *wpath = NULL, *wmode = NULL; + FILE *file = NULL; + int length; + if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, NULL, 0)) == 0) + { + FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for filename '%s'. Error was: '%s'", filename, fluid_get_windows_error()); + errno = EINVAL; + goto error_recovery; + } + + wpath = FLUID_MALLOC(length * sizeof(wchar_t)); + if (wpath == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory."); + errno = EINVAL; + goto error_recovery; + } + + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, wpath, length); + + if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, NULL, 0)) == 0) + { + FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for mode '%s'. Error was: '%s'", mode, fluid_get_windows_error()); + errno = EINVAL; + goto error_recovery; + } + + wmode = FLUID_MALLOC(length * sizeof(wchar_t)); + if (wmode == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory."); + errno = EINVAL; + goto error_recovery; + } + + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, wmode, length); + + file = _wfopen(wpath, wmode); + +error_recovery: + FLUID_FREE(wpath); + FLUID_FREE(wmode); + + return file; +#else + return fopen(filename, mode); +#endif +} + +/** + * Wrapper for free() that satisfies at least C90 requirements. + * + * @param ptr Pointer to memory region that should be freed + * + * @note Only use this function when the API documentation explicitly says so. Otherwise use + * adequate \c delete_fluid_* functions. + * + * @warning Calling ::free() on memory that is advised to be freed with fluid_free() results in undefined behaviour! + * (cf.: "Potential Errors Passing CRT Objects Across DLL Boundaries" found in MS Docs) + * + * @since 2.0.7 + */ +void fluid_free(void* ptr) +{ + free(ptr); +} + +/** + * An improved strtok, still trashes the input string, but is portable and + * thread safe. Also skips token chars at beginning of token string and never + * returns an empty token (will return NULL if source ends in token chars though). + * NOTE: NOT part of public API + * @internal + * @param str Pointer to a string pointer of source to tokenize. Pointer gets + * updated on each invocation to point to beginning of next token. Note that + * token char gets overwritten with a 0 byte. String pointer is set to NULL + * when final token is returned. + * @param delim String of delimiter chars. + * @return Pointer to the next token or NULL if no more tokens. + */ +char *fluid_strtok(char **str, char *delim) +{ + char *s, *d, *token; + char c; + + if(str == NULL || delim == NULL || !*delim) + { + FLUID_LOG(FLUID_ERR, "Null pointer"); + return NULL; + } + + s = *str; + + if(!s) + { + return NULL; /* str points to a NULL pointer? (tokenize already ended) */ + } + + /* skip delimiter chars at beginning of token */ + do + { + c = *s; + + if(!c) /* end of source string? */ + { + *str = NULL; + return NULL; + } + + for(d = delim; *d; d++) /* is source char a token char? */ + { + if(c == *d) /* token char match? */ + { + s++; /* advance to next source char */ + break; + } + } + } + while(*d); /* while token char match */ + + token = s; /* start of token found */ + + /* search for next token char or end of source string */ + for(s = s + 1; *s; s++) + { + c = *s; + + for(d = delim; *d; d++) /* is source char a token char? */ + { + if(c == *d) /* token char match? */ + { + *s = '\0'; /* overwrite token char with zero byte to terminate token */ + *str = s + 1; /* update str to point to beginning of next token */ + return token; + } + } + } + + /* we get here only if source string ended */ + *str = NULL; + return token; +} + +/** + * Suspend the execution of the current thread for the specified amount of time. + * @param milliseconds to wait. + */ +void fluid_msleep(unsigned int msecs) +{ + g_usleep(msecs * 1000); +} + +/** + * Get time in milliseconds to be used in relative timing operations. + * @return Monotonic time in milliseconds. + */ +unsigned int fluid_curtime(void) +{ + double now; + static double initial_time = 0; + + if(initial_time == 0) + { + initial_time = fluid_utime(); + } + + now = fluid_utime(); + + return (unsigned int)((now - initial_time) / 1000.0); +} + +/** + * Get time in microseconds to be used in relative timing operations. + * @return time in microseconds. + * Note: When used for profiling we need high precision clock given + * by g_get_monotonic_time()if available (glib version >= 2.53.3). + * If glib version is too old and in the case of Windows the function + * uses high precision performance counter instead of g_getmonotic_time(). + */ +double +fluid_utime(void) +{ + double utime; + +#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 28 + /* use high precision monotonic clock if available (g_monotonic_time(). + * For Windows, if this clock is actually implemented as low prec. clock + * (i.e. in case glib is too old), high precision performance counter are + * used instead. + * see: https://bugzilla.gnome.org/show_bug.cgi?id=783340 + */ +#if defined(WITH_PROFILING) && defined(WIN32) &&\ + /* glib < 2.53.3 */\ + (GLIB_MINOR_VERSION <= 53 && (GLIB_MINOR_VERSION < 53 || GLIB_MICRO_VERSION < 3)) + /* use high precision performance counter. */ + static LARGE_INTEGER freq_cache = {0, 0}; /* Performance Frequency */ + LARGE_INTEGER perf_cpt; + + if(! freq_cache.QuadPart) + { + QueryPerformanceFrequency(&freq_cache); /* Frequency value */ + } + + QueryPerformanceCounter(&perf_cpt); /* Counter value */ + utime = perf_cpt.QuadPart * 1000000.0 / freq_cache.QuadPart; /* time in micros */ +#else + utime = g_get_monotonic_time(); +#endif +#else + /* fallback to less precise clock */ + GTimeVal timeval; + g_get_current_time(&timeval); + utime = (timeval.tv_sec * 1000000.0 + timeval.tv_usec); +#endif + + return utime; +} + + + +#if defined(WIN32) /* Windoze specific stuff */ + +void +fluid_thread_self_set_prio(int prio_level) +{ + if(prio_level > 0) + { + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); + } +} + + +#elif defined(__OS2__) /* OS/2 specific stuff */ + +void +fluid_thread_self_set_prio(int prio_level) +{ + if(prio_level > 0) + { + DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0); + } +} + +#else /* POSIX stuff.. Nice POSIX.. Good POSIX. */ + +void +fluid_thread_self_set_prio(int prio_level) +{ + struct sched_param priority; + + if(prio_level > 0) + { + + memset(&priority, 0, sizeof(priority)); + priority.sched_priority = prio_level; + + if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &priority) == 0) + { + return; + } + +#ifdef DBUS_SUPPORT + /* Try to gain high priority via rtkit */ + + if(fluid_rtkit_make_realtime(0, prio_level) == 0) + { + return; + } + +#endif + FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority"); + } +} + +#ifdef FPE_CHECK + +/*************************************************************** + * + * Floating point exceptions + * + * The floating point exception functions were taken from Ircam's + * jMax source code. https://www.ircam.fr/jmax + * + * FIXME: check in config for i386 machine + * + * Currently not used. I leave the code here in case we want to pick + * this up again some time later. + */ + +/* Exception flags */ +#define _FPU_STATUS_IE 0x001 /* Invalid Operation */ +#define _FPU_STATUS_DE 0x002 /* Denormalized Operand */ +#define _FPU_STATUS_ZE 0x004 /* Zero Divide */ +#define _FPU_STATUS_OE 0x008 /* Overflow */ +#define _FPU_STATUS_UE 0x010 /* Underflow */ +#define _FPU_STATUS_PE 0x020 /* Precision */ +#define _FPU_STATUS_SF 0x040 /* Stack Fault */ +#define _FPU_STATUS_ES 0x080 /* Error Summary Status */ + +/* Macros for accessing the FPU status word. */ + +/* get the FPU status */ +#define _FPU_GET_SW(sw) __asm__ ("fnstsw %0" : "=m" (*&sw)) + +/* clear the FPU status */ +#define _FPU_CLR_SW() __asm__ ("fnclex" : : ) + +/* Purpose: + * Checks, if the floating point unit has produced an exception, print a message + * if so and clear the exception. + */ +unsigned int fluid_check_fpe_i386(char *explanation) +{ + unsigned int s; + + _FPU_GET_SW(s); + _FPU_CLR_SW(); + + s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE; + + if(s) + { + FLUID_LOG(FLUID_WARN, "FPE exception (before or in %s): %s%s%s%s%s", explanation, + (s & _FPU_STATUS_IE) ? "Invalid operation " : "", + (s & _FPU_STATUS_DE) ? "Denormal number " : "", + (s & _FPU_STATUS_ZE) ? "Zero divide " : "", + (s & _FPU_STATUS_OE) ? "Overflow " : "", + (s & _FPU_STATUS_UE) ? "Underflow " : ""); + } + + return s; +} + +/* Purpose: + * Clear floating point exception. + */ +void fluid_clear_fpe_i386(void) +{ + _FPU_CLR_SW(); +} + +#endif // ifdef FPE_CHECK + + +#endif // #else (its POSIX) + + +/*************************************************************** + * + * Profiling (Linux, i586 only) + * + */ + +#if WITH_PROFILING +/* Profiling interface between profiling command shell and audio rendering API + (FluidProfile_0004.pdf- 3.2.2). + Macros are in defined in fluid_sys.h. +*/ + +/* + ----------------------------------------------------------------------------- + Shell task side | Profiling interface | Audio task side + ----------------------------------------------------------------------------- + profiling | Internal | | | Audio + command <---> |<-- profling -->| Data |<--macros -->| <--> rendering + shell | API | | | API + +*/ +/* default parameters for shell command "prof_start" in fluid_sys.c */ +unsigned short fluid_profile_notes = 0; /* number of generated notes */ +/* preset bank:0 prog:16 (organ) */ +unsigned char fluid_profile_bank = FLUID_PROFILE_DEFAULT_BANK; +unsigned char fluid_profile_prog = FLUID_PROFILE_DEFAULT_PROG; + +/* print mode */ +unsigned char fluid_profile_print = FLUID_PROFILE_DEFAULT_PRINT; +/* number of measures */ +unsigned short fluid_profile_n_prof = FLUID_PROFILE_DEFAULT_N_PROF; +/* measure duration in ms */ +unsigned short fluid_profile_dur = FLUID_PROFILE_DEFAULT_DURATION; +/* lock between multiple-shell */ +fluid_atomic_int_t fluid_profile_lock = 0; +/**/ + +/*---------------------------------------------- + Profiling Data +-----------------------------------------------*/ +unsigned char fluid_profile_status = PROFILE_STOP; /* command and status */ +unsigned int fluid_profile_end_ticks = 0; /* ending position (in ticks) */ +fluid_profile_data_t fluid_profile_data[] = /* Data duration */ +{ + {"synth_write_* ------------>", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block ---------->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:clear ---->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:one voice->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:all voices>", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:reverb --->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"synth_one_block:chorus --->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"voice:note --------------->", 1e10, 0.0, 0.0, 0, 0, 0}, + {"voice:release ------------>", 1e10, 0.0, 0.0, 0, 0, 0} +}; + + +/*---------------------------------------------- + Internal profiling API +-----------------------------------------------*/ +/* logging profiling data (used on synthesizer instance deletion) */ +void fluid_profiling_print(void) +{ + int i; + + printf("fluid_profiling_print\n"); + + FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)"); + + for(i = 0; i < FLUID_PROFILE_NBR; i++) + { + if(fluid_profile_data[i].count > 0) + { + FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f", + fluid_profile_data[i].description, + fluid_profile_data[i].min, + fluid_profile_data[i].total / fluid_profile_data[i].count, + fluid_profile_data[i].max); + } + else + { + FLUID_LOG(FLUID_DBG, "%s: no profiling available", + fluid_profile_data[i].description); + } + } +} + +/* Macro that returns cpu load in percent (%) + * @dur: duration (micro second). + * @sample_rate: sample_rate used in audio driver (Hz). + * @n_amples: number of samples collected during 'dur' duration. +*/ +#define fluid_profile_load(dur,sample_rate,n_samples) \ + (dur * sample_rate / n_samples / 10000.0) + + +/* prints cpu loads only +* +* @param sample_rate the sample rate of audio output. +* @param out output stream device. +* +* ------------------------------------------------------------------------------ +* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices +* ------------------------------------------------------------------------------ +* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices +* -------|---------|---------|----------|---------|---------|------------------- +* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612 +*/ +static void fluid_profiling_print_load(double sample_rate, fluid_ostream_t out) +{ + unsigned int n_voices; /* voices number */ + static const char max_voices_not_available[] = " not available"; + const char *pmax_voices; + char max_voices_available[20]; + + /* First computes data to be printed */ + double total, voices, reverb, chorus, all_voices, voice; + /* voices number */ + n_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ? + fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_voices / + fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count : 0; + + /* total load (%) */ + total = fluid_profile_data[FLUID_PROF_WRITE].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_WRITE].total, sample_rate, + fluid_profile_data[FLUID_PROF_WRITE].n_samples) : 0; + + /* reverb load (%) */ + reverb = fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].total, + sample_rate, + fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].n_samples) : 0; + + /* chorus load (%) */ + chorus = fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].total, + sample_rate, + fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].n_samples) : 0; + + /* total voices load: total - reverb - chorus (%) */ + voices = total - reverb - chorus; + + /* One voice load (%): all_voices / n_voices. */ + all_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ? + fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].total, + sample_rate, + fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_samples) : 0; + + voice = n_voices ? all_voices / n_voices : 0; + + /* estimated maximum voices number */ + if(voice > 0) + { + FLUID_SNPRINTF(max_voices_available, sizeof(max_voices_available), + "%17d", (unsigned int)((100.0 - reverb - chorus) / voice)); + pmax_voices = max_voices_available; + } + else + { + pmax_voices = max_voices_not_available; + } + + /* Now prints data */ + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " Cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond) and maximum voices\n", + sample_rate, 1000000.0 / sample_rate); + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " nVoices| total(%%)|voices(%%)| reverb(%%)|chorus(%%)| voice(%%)|estimated maxVoices\n"); + fluid_ostream_printf(out, + " -------|---------|---------|----------|---------|---------|-------------------\n"); + fluid_ostream_printf(out, + "%8d|%9.3f|%9.3f|%10.3f|%9.3f|%9.3f|%s\n", n_voices, total, voices, + reverb, chorus, voice, pmax_voices); +} + +/* +* prints profiling data (used by profile shell command: prof_start). +* The function is an internal profiling API between the "profile" command +* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). +* +* @param sample_rate the sample rate of audio output. +* @param out output stream device. +* +* When print mode is 1, the function prints all the information (see below). +* When print mode is 0, the function prints only the cpu loads. +* +* ------------------------------------------------------------------------------ +* Duration(microsecond) and cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) +* ------------------------------------------------------------------------------ +* Code under profiling |Voices| Duration (microsecond) | Load(%) +* | nbr| min| avg| max| +* ---------------------------|------|--------------------------------|---------- +* synth_write_* ------------>| 250| 3.91| 2188.82| 3275.00| 41.544 +* synth_one_block ---------->| 250| 1150.70| 2273.56| 3241.47| 41.100 +* synth_one_block:clear ---->| 250| 3.07| 4.62| 61.18| 0.084 +* synth_one_block:one voice->| 1| 4.19| 9.02| 1044.27| 0.163 +* synth_one_block:all voices>| 250| 1138.41| 2259.11| 3217.73| 40.839 +* synth_one_block:reverb --->| no profiling available +* synth_one_block:chorus --->| no profiling available +* voice:note --------------->| no profiling available +* voice:release ------------>| no profiling available +* ------------------------------------------------------------------------------ +* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices +* ------------------------------------------------------------------------------ +* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices +* -------|---------|---------|----------|---------|---------|------------------- +* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612 +*/ +void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out) +{ + int i; + + if(fluid_profile_print) + { + /* print all details: Duration(microsecond) and cpu loads(%) */ + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " Duration(microsecond) and cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond)\n", + sample_rate, 1000000.0 / sample_rate); + fluid_ostream_printf(out, + " ------------------------------------------------------------------------------\n"); + fluid_ostream_printf(out, + " Code under profiling |Voices| Duration (microsecond) | Load(%%)\n"); + fluid_ostream_printf(out, + " | nbr| min| avg| max|\n"); + fluid_ostream_printf(out, + " ---------------------------|------|--------------------------------|----------\n"); + + for(i = 0; i < FLUID_PROFILE_NBR; i++) + { + unsigned int count = fluid_profile_data[i].count; + + if(count > 0) + { + /* data are available */ + + if(FLUID_PROF_WRITE <= i && i <= FLUID_PROF_ONE_BLOCK_CHORUS) + { + double load = fluid_profile_load(fluid_profile_data[i].total, sample_rate, + fluid_profile_data[i].n_samples); + fluid_ostream_printf(out, " %s|%6d|%10.2f|%10.2f|%10.2f|%8.3f\n", + fluid_profile_data[i].description, /* code under profiling */ + fluid_profile_data[i].n_voices / count, /* voices number */ + fluid_profile_data[i].min, /* minimum duration */ + fluid_profile_data[i].total / count, /* average duration */ + fluid_profile_data[i].max, /* maximum duration */ + load); /* cpu load */ + } + else + { + /* note and release duration */ + fluid_ostream_printf(out, " %s|%6d|%10.0f|%10.0f|%10.0f|\n", + fluid_profile_data[i].description, /* code under profiling */ + fluid_profile_data[i].n_voices / count, + fluid_profile_data[i].min, /* minimum duration */ + fluid_profile_data[i].total / count, /* average duration */ + fluid_profile_data[i].max); /* maximum duration */ + } + } + else + { + /* data aren't available */ + fluid_ostream_printf(out, + " %s| no profiling available\n", fluid_profile_data[i].description); + } + } + } + + /* prints cpu loads only */ + fluid_profiling_print_load(sample_rate, out);/* prints cpu loads */ +} + +/* + Returns true if the user cancels the current profiling measurement. + Actually this is implemented using the key. To add this functionality: + 1) Adds #define FLUID_PROFILE_CANCEL in fluid_sys.h. + 2) Adds the necessary code inside fluid_profile_is_cancel(). + + When FLUID_PROFILE_CANCEL is not defined, the function return FALSE. +*/ +int fluid_profile_is_cancel_req(void) +{ +#ifdef FLUID_PROFILE_CANCEL + +#if defined(WIN32) /* Windows specific stuff */ + /* Profile cancellation is supported for Windows */ + /* returns TRUE if key is depressed */ + return(GetAsyncKeyState(VK_RETURN) & 0x1); + +#elif defined(__OS2__) /* OS/2 specific stuff */ + /* Profile cancellation isn't yet supported for OS2 */ + /* For OS2, replaces the following line with the function that returns + true when the keyboard key is depressed */ + return FALSE; /* default value */ + +#else /* POSIX stuff */ + /* Profile cancellation is supported for Linux */ + /* returns true is is depressed */ + { + /* Here select() is used to poll the standard input to see if an input + is ready. As the standard input is usually buffered, the user + needs to depress to set the input to a "ready" state. + */ + struct timeval tv; + fd_set fds; /* just one fds need to be polled */ + tv.tv_sec = 0; /* Setting both values to 0, means a 0 timeout */ + tv.tv_usec = 0; + FD_ZERO(&fds); /* reset fds */ + FD_SET(STDIN_FILENO, &fds); /* sets fds to poll standard input only */ + select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); /* polling */ + return (FD_ISSET(0, &fds)); /* returns true if standard input is ready */ + } +#endif /* OS stuff */ + +#else /* FLUID_PROFILE_CANCEL not defined */ + return FALSE; /* default value */ +#endif /* FLUID_PROFILE_CANCEL */ +} + +/** +* Returns status used in shell command "prof_start". +* The function is an internal profiling API between the "profile" command +* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). +* +* @return status +* - PROFILE_READY profiling data are ready. +* - PROFILE_RUNNING, profiling data are still under acquisition. +* - PROFILE_CANCELED, acquisition has been cancelled by the user. +* - PROFILE_STOP, no acquisition in progress. +* +* When status is PROFILE_RUNNING, the caller can do passive waiting, or other +* work before recalling the function later. +*/ +int fluid_profile_get_status(void) +{ + /* Checks if user has requested to cancel the current measurement */ + /* Cancellation must have precedence over other status */ + if(fluid_profile_is_cancel_req()) + { + fluid_profile_start_stop(0, 0); /* stops the measurement */ + return PROFILE_CANCELED; + } + + switch(fluid_profile_status) + { + case PROFILE_READY: + return PROFILE_READY; /* profiling data are ready */ + + case PROFILE_START: + return PROFILE_RUNNING;/* profiling data are under acquisition */ + + default: + return PROFILE_STOP; + } +} + +/** +* Starts or stops profiling measurement. +* The function is an internal profiling API between the "profile" command +* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2). +* +* @param end_tick end position of the measure (in ticks). +* - If end_tick is greater then 0, the function starts a measure if a measure +* isn't running. If a measure is already running, the function does nothing +* and returns. +* - If end_tick is 0, the function stops a measure. +* @param clear_data, +* - If clear_data is 0, the function clears fluid_profile_data before starting +* a measure, otherwise, the data from the started measure will be accumulated +* within fluid_profile_data. +*/ +void fluid_profile_start_stop(unsigned int end_ticks, short clear_data) +{ + if(end_ticks) /* This is a "start" request */ + { + /* Checks if a measure is already running */ + if(fluid_profile_status != PROFILE_START) + { + short i; + fluid_profile_end_ticks = end_ticks; + + /* Clears profile data */ + if(clear_data == 0) + { + for(i = 0; i < FLUID_PROFILE_NBR; i++) + { + fluid_profile_data[i].min = 1e10;/* min sets to max value */ + fluid_profile_data[i].max = 0; /* maximum sets to min value */ + fluid_profile_data[i].total = 0; /* total duration microsecond */ + fluid_profile_data[i].count = 0; /* data count */ + fluid_profile_data[i].n_voices = 0; /* voices number */ + fluid_profile_data[i].n_samples = 0;/* audio samples number */ + } + } + + fluid_profile_status = PROFILE_START; /* starts profiling */ + } + + /* else do nothing when profiling is already started */ + } + else /* This is a "stop" request */ + { + /* forces the current running profile (if any) to stop */ + fluid_profile_status = PROFILE_STOP; /* stops profiling */ + } +} + +#endif /* WITH_PROFILING */ + +/*************************************************************** + * + * Threads + * + */ + +#if OLD_GLIB_THREAD_API + +/* Rather than inline this one, we just declare it as a function, to prevent + * GCC warning about inline failure. */ +fluid_cond_t * +new_fluid_cond(void) +{ + if(!g_thread_supported()) + { + g_thread_init(NULL); + } + + return g_cond_new(); +} + +#endif + +static gpointer +fluid_thread_high_prio(gpointer data) +{ + fluid_thread_info_t *info = data; + + fluid_thread_self_set_prio(info->prio_level); + + info->func(info->data); + FLUID_FREE(info); + + return NULL; +} + +/** + * Create a new thread. + * @param func Function to execute in new thread context + * @param data User defined data to pass to func + * @param prio_level Priority level. If greater than 0 then high priority scheduling will + * be used, with the given priority level (used by pthreads only). 0 uses normal scheduling. + * @param detach If TRUE, 'join' does not work and the thread destroys itself when finished. + * @return New thread pointer or NULL on error + */ +fluid_thread_t * +new_fluid_thread(const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach) +{ + GThread *thread; + fluid_thread_info_t *info = NULL; + GError *err = NULL; + + g_return_val_if_fail(func != NULL, NULL); + +#if OLD_GLIB_THREAD_API + + /* Make sure g_thread_init has been called. + * Probably not a good idea in a shared library, + * but what can we do *and* remain backwards compatible? */ + if(!g_thread_supported()) + { + g_thread_init(NULL); + } + +#endif + + if(prio_level > 0) + { + info = FLUID_NEW(fluid_thread_info_t); + + if(!info) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + info->func = func; + info->data = data; + info->prio_level = prio_level; +#if NEW_GLIB_THREAD_API + thread = g_thread_try_new(name, fluid_thread_high_prio, info, &err); +#else + thread = g_thread_create(fluid_thread_high_prio, info, detach == FALSE, &err); +#endif + } + + else + { +#if NEW_GLIB_THREAD_API + thread = g_thread_try_new(name, (GThreadFunc)func, data, &err); +#else + thread = g_thread_create((GThreadFunc)func, data, detach == FALSE, &err); +#endif + } + + if(!thread) + { + FLUID_LOG(FLUID_ERR, "Failed to create the thread: %s", + fluid_gerror_message(err)); + g_clear_error(&err); + FLUID_FREE(info); + return NULL; + } + +#if NEW_GLIB_THREAD_API + + if(detach) + { + g_thread_unref(thread); // Release thread reference, if caller wants to detach + } + +#endif + + return thread; +} + +/** + * Frees data associated with a thread (does not actually stop thread). + * @param thread Thread to free + */ +void +delete_fluid_thread(fluid_thread_t *thread) +{ + /* Threads free themselves when they quit, nothing to do */ +} + +/** + * Join a thread (wait for it to terminate). + * @param thread Thread to join + * @return FLUID_OK + */ +int +fluid_thread_join(fluid_thread_t *thread) +{ + g_thread_join(thread); + + return FLUID_OK; +} + + +static fluid_thread_return_t +fluid_timer_run(void *data) +{ + fluid_timer_t *timer; + int count = 0; + int cont; + long start; + long delay; + + timer = (fluid_timer_t *)data; + + /* keep track of the start time for absolute positioning */ + start = fluid_curtime(); + + while(timer->cont) + { + cont = (*timer->callback)(timer->data, fluid_curtime() - start); + + count++; + + if(!cont) + { + break; + } + + /* to avoid incremental time errors, calculate the delay between + two callbacks bringing in the "absolute" time (count * + timer->msec) */ + delay = (count * timer->msec) - (fluid_curtime() - start); + + if(delay > 0) + { + fluid_msleep(delay); + } + } + + FLUID_LOG(FLUID_DBG, "Timer thread finished"); + timer->callback = NULL; + + if(timer->auto_destroy) + { + FLUID_FREE(timer); + } + + return FLUID_THREAD_RETURN_VALUE; +} + +fluid_timer_t * +new_fluid_timer(int msec, fluid_timer_callback_t callback, void *data, + int new_thread, int auto_destroy, int high_priority) +{ + fluid_timer_t *timer; + + timer = FLUID_NEW(fluid_timer_t); + + if(timer == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + timer->msec = msec; + timer->callback = callback; + timer->data = data; + timer->cont = TRUE ; + timer->thread = NULL; + timer->auto_destroy = auto_destroy; + + if(new_thread) + { + timer->thread = new_fluid_thread("timer", fluid_timer_run, timer, high_priority + ? FLUID_SYS_TIMER_HIGH_PRIO_LEVEL : 0, FALSE); + + if(!timer->thread) + { + FLUID_FREE(timer); + return NULL; + } + } + else + { + fluid_timer_run(timer); /* Run directly, instead of as a separate thread */ + + if(auto_destroy) + { + /* do NOT return freed memory */ + return NULL; + } + } + + return timer; +} + +void +delete_fluid_timer(fluid_timer_t *timer) +{ + int auto_destroy; + fluid_return_if_fail(timer != NULL); + + auto_destroy = timer->auto_destroy; + + timer->cont = 0; + fluid_timer_join(timer); + + /* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */ + + if(!auto_destroy) + { + FLUID_FREE(timer); + } +} + +int +fluid_timer_join(fluid_timer_t *timer) +{ + int auto_destroy; + + if(timer->thread) + { + auto_destroy = timer->auto_destroy; + fluid_thread_join(timer->thread); + + if(!auto_destroy) + { + timer->thread = NULL; + } + } + + return FLUID_OK; +} + +int +fluid_timer_is_running(const fluid_timer_t *timer) +{ + // for unit test usage only + return timer->callback != NULL; +} + +long fluid_timer_get_interval(const fluid_timer_t * timer) +{ + // for unit test usage only + return timer->msec; +} + + +/*************************************************************** + * + * Sockets and I/O + * + */ + +/** + * Get standard in stream handle. + * @return Standard in stream. + */ +fluid_istream_t +fluid_get_stdin(void) +{ + return STDIN_FILENO; +} + +/** + * Get standard output stream handle. + * @return Standard out stream. + */ +fluid_ostream_t +fluid_get_stdout(void) +{ + return STDOUT_FILENO; +} + +/** + * Read a line from an input stream. + * @return 0 if end-of-stream, -1 if error, non zero otherwise + */ +int +fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt, + char *buf, int len) +{ +#if WITH_READLINE + + if(in == fluid_get_stdin()) + { + char *line; + + line = readline(prompt); + + if(line == NULL) + { + return -1; + } + + FLUID_SNPRINTF(buf, len, "%s", line); + buf[len - 1] = 0; + + if(buf[0] != '\0') + { + add_history(buf); + } + + free(line); + return 1; + } + else +#endif + { + fluid_ostream_printf(out, "%s", prompt); + return fluid_istream_gets(in, buf, len); + } +} + +/** + * Reads a line from an input stream (socket). + * @param in The input socket + * @param buf Buffer to store data to + * @param len Maximum length to store to buf + * @return 1 if a line was read, 0 on end of stream, -1 on error + */ +static int +fluid_istream_gets(fluid_istream_t in, char *buf, int len) +{ + char c; + int n; + + buf[len - 1] = 0; + + while(--len > 0) + { +#ifndef WIN32 + n = read(in, &c, 1); + + if(n == -1) + { + return -1; + } + +#else + + /* Handle read differently depending on if its a socket or file descriptor */ + if(!(in & FLUID_SOCKET_FLAG)) + { + // usually read() is supposed to return '\n' as last valid character of the user input + // when compiled with compatibility for WinXP however, read() may return 0 (EOF) rather than '\n' + // this would cause the shell to exit early + n = read(in, &c, 1); + + if(n == -1) + { + return -1; + } + } + else + { +#ifdef NETWORK_SUPPORT + n = recv(in & ~FLUID_SOCKET_FLAG, &c, 1, 0); + if(n == SOCKET_ERROR) +#endif + { + return -1; + } + } + +#endif + + if(n == 0) + { + *buf = 0; + // return 1 if read from stdin, else 0, to fix early exit of shell + return (in == STDIN_FILENO); + } + + if(c == '\n') + { + *buf = 0; + return 1; + } + + /* Store all characters excluding CR */ + if(c != '\r') + { + *buf++ = c; + } + } + + return -1; +} + +/** + * Send a printf style string with arguments to an output stream (socket). + * @param out Output stream + * @param format printf style format string + * @param ... Arguments for the printf format string + * @return Number of bytes written or -1 on error + */ +int +fluid_ostream_printf(fluid_ostream_t out, const char *format, ...) +{ + char buf[4096]; + va_list args; + int len; + + va_start(args, format); + len = FLUID_VSNPRINTF(buf, 4095, format, args); + va_end(args); + + if(len == 0) + { + return 0; + } + + if(len < 0) + { + printf("fluid_ostream_printf: buffer overflow"); + return -1; + } + + buf[4095] = 0; + +#ifndef WIN32 + return write(out, buf, FLUID_STRLEN(buf)); +#else + { + int retval; + + /* Handle write differently depending on if its a socket or file descriptor */ + if(!(out & FLUID_SOCKET_FLAG)) + { + return write(out, buf, (unsigned int)FLUID_STRLEN(buf)); + } + +#ifdef NETWORK_SUPPORT + /* Socket */ + retval = send(out & ~FLUID_SOCKET_FLAG, buf, (int)FLUID_STRLEN(buf), 0); + return retval != SOCKET_ERROR ? retval : -1; +#else + return -1; +#endif + } +#endif +} + +#ifdef NETWORK_SUPPORT + +int fluid_server_socket_join(fluid_server_socket_t *server_socket) +{ + return fluid_thread_join(server_socket->thread); +} + +static int fluid_socket_init(void) +{ +#ifdef _WIN32 + WSADATA wsaData; + int res = WSAStartup(MAKEWORD(2, 2), &wsaData); + + if(res != 0) + { + FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", res); + return FLUID_FAILED; + } + +#endif + + return FLUID_OK; +} + +static void fluid_socket_cleanup(void) +{ +#ifdef _WIN32 + WSACleanup(); +#endif +} + +static int fluid_socket_get_error(void) +{ +#ifdef _WIN32 + return (int)WSAGetLastError(); +#else + return errno; +#endif +} + +fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock) +{ + return sock | FLUID_SOCKET_FLAG; +} + +fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock) +{ + return sock | FLUID_SOCKET_FLAG; +} + +void fluid_socket_close(fluid_socket_t sock) +{ + if(sock != INVALID_SOCKET) + { +#ifdef _WIN32 + closesocket(sock); + +#else + close(sock); +#endif + } +} + +static fluid_thread_return_t fluid_server_socket_run(void *data) +{ + fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data; + fluid_socket_t client_socket; +#ifdef IPV6_SUPPORT + struct sockaddr_in6 addr; +#else + struct sockaddr_in addr; +#endif + +#ifdef HAVE_INETNTOP +#ifdef IPV6_SUPPORT + char straddr[INET6_ADDRSTRLEN]; +#else + char straddr[INET_ADDRSTRLEN]; +#endif /* IPV6_SUPPORT */ +#endif /* HAVE_INETNTOP */ + + socklen_t addrlen = sizeof(addr); + int r; + FLUID_MEMSET((char *)&addr, 0, sizeof(addr)); + + FLUID_LOG(FLUID_DBG, "Server listening for connections"); + + while(server_socket->cont) + { + client_socket = accept(server_socket->socket, (struct sockaddr *)&addr, &addrlen); + + FLUID_LOG(FLUID_DBG, "New client connection"); + + if(client_socket == INVALID_SOCKET) + { + if(server_socket->cont) + { + FLUID_LOG(FLUID_ERR, "Failed to accept connection: %d", fluid_socket_get_error()); + } + + server_socket->cont = 0; + return FLUID_THREAD_RETURN_VALUE; + } + else + { +#ifdef HAVE_INETNTOP + +#ifdef IPV6_SUPPORT + inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr)); +#else + inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr)); +#endif + + r = server_socket->func(server_socket->data, client_socket, + straddr); +#else + r = server_socket->func(server_socket->data, client_socket, + inet_ntoa(addr.sin_addr)); +#endif + + if(r != 0) + { + fluid_socket_close(client_socket); + } + } + } + + FLUID_LOG(FLUID_DBG, "Server closing"); + + return FLUID_THREAD_RETURN_VALUE; +} + +fluid_server_socket_t * +new_fluid_server_socket(int port, fluid_server_func_t func, void *data) +{ + fluid_server_socket_t *server_socket; +#ifdef IPV6_SUPPORT + struct sockaddr_in6 addr; +#else + struct sockaddr_in addr; +#endif + + fluid_socket_t sock; + + fluid_return_val_if_fail(func != NULL, NULL); + + if(fluid_socket_init() != FLUID_OK) + { + return NULL; + } + +#ifdef IPV6_SUPPORT + sock = socket(AF_INET6, SOCK_STREAM, 0); + + if(sock == INVALID_SOCKET) + { + FLUID_LOG(FLUID_ERR, "Failed to create server socket: %d", fluid_socket_get_error()); + fluid_socket_cleanup(); + return NULL; + } + + FLUID_MEMSET(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = htons((uint16_t)port); + addr.sin6_addr = in6addr_any; +#else + + sock = socket(AF_INET, SOCK_STREAM, 0); + + if(sock == INVALID_SOCKET) + { + FLUID_LOG(FLUID_ERR, "Failed to create server socket: %d", fluid_socket_get_error()); + fluid_socket_cleanup(); + return NULL; + } + + FLUID_MEMSET(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons((uint16_t)port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); +#endif + + if(bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) + { + FLUID_LOG(FLUID_ERR, "Failed to bind server socket: %d", fluid_socket_get_error()); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } + + if(listen(sock, SOMAXCONN) == SOCKET_ERROR) + { + FLUID_LOG(FLUID_ERR, "Failed to listen on server socket: %d", fluid_socket_get_error()); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } + + server_socket = FLUID_NEW(fluid_server_socket_t); + + if(server_socket == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } + + server_socket->socket = sock; + server_socket->func = func; + server_socket->data = data; + server_socket->cont = 1; + + server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket, + 0, FALSE); + + if(server_socket->thread == NULL) + { + FLUID_FREE(server_socket); + fluid_socket_close(sock); + fluid_socket_cleanup(); + return NULL; + } + + return server_socket; +} + +void delete_fluid_server_socket(fluid_server_socket_t *server_socket) +{ + fluid_return_if_fail(server_socket != NULL); + + server_socket->cont = 0; + + if(server_socket->socket != INVALID_SOCKET) + { + fluid_socket_close(server_socket->socket); + } + + if(server_socket->thread) + { + fluid_thread_join(server_socket->thread); + delete_fluid_thread(server_socket->thread); + } + + FLUID_FREE(server_socket); + + // Should be called the same number of times as fluid_socket_init() + fluid_socket_cleanup(); +} + +#endif // NETWORK_SUPPORT + +FILE* fluid_file_open(const char* path, const char** errMsg) +{ + static const char ErrExist[] = "File does not exist."; + static const char ErrRegular[] = "File is not regular, refusing to open it."; + static const char ErrNull[] = "File does not exists or insufficient permissions to open it."; + + FILE* handle = NULL; + + if(!fluid_file_test(path, FLUID_FILE_TEST_EXISTS)) + { + if(errMsg != NULL) + { + *errMsg = ErrExist; + } + } + else if(!fluid_file_test(path, FLUID_FILE_TEST_IS_REGULAR)) + { + if(errMsg != NULL) + { + *errMsg = ErrRegular; + } + } + else if((handle = FLUID_FOPEN(path, "rb")) == NULL) + { + if(errMsg != NULL) + { + *errMsg = ErrNull; + } + } + + return handle; +} + +fluid_long_long_t fluid_file_tell(FILE* f) +{ +#ifdef WIN32 + // On Windows, long is only a 32 bit integer. Thus ftell() does not support to handle files >2GiB. + // We should use _ftelli64() in this case, however its availability depends on MS CRT and might not be + // available on WindowsXP, Win98, etc. + // + // The web recommends to fallback to _telli64() in this case. However, it's return value differs from + // _ftelli64() on Win10: https://github.com/FluidSynth/fluidsynth/pull/629#issuecomment-602238436 + // + // Thus, we use fgetpos(). + fpos_t pos; + if(fgetpos(f, &pos) != 0) + { + return (fluid_long_long_t)-1L; + } + return pos; +#else + return ftell(f); +#endif +} + +#ifdef WIN32 +// not thread-safe! +char* fluid_get_windows_error(void) +{ + static TCHAR err[1024]; + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + err, + sizeof(err)/sizeof(err[0]), + NULL); + +#ifdef _UNICODE + static char ascii_err[sizeof(err)]; + + WideCharToMultiByte(CP_UTF8, 0, err, -1, ascii_err, sizeof(ascii_err)/sizeof(ascii_err[0]), 0, 0); + return ascii_err; +#else + return err; +#endif +} +#endif diff --git a/thirdparty/fluidsynth/src/utils/fluid_sys.h b/thirdparty/fluidsynth/src/utils/fluid_sys.h new file mode 100644 index 000000000..de7018bf9 --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluid_sys.h @@ -0,0 +1,775 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/* + * @file fluid_sys.h + * + * This header contains a bunch of (mostly) system and machine + * dependent functions: + * + * - timers + * - current time in milliseconds and microseconds + * - debug logging + * - profiling + * - memory locking + * - checking for floating point exceptions + * + * fluidsynth's wrapper OSAL so to say; include it in .c files, be careful to include + * it in fluidsynth's private header files (see comment in fluid_coreaudio.c) + */ + +#ifndef _FLUID_SYS_H +#define _FLUID_SYS_H + +#include "fluidsynth_priv.h" + +#if HAVE_MATH_H +#include +#endif + +#if HAVE_ERRNO_H +#include +#endif + +#if HAVE_STDARG_H +#include +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_FCNTL_H +#include +#endif + +#if HAVE_SYS_MMAN_H +#include +#endif + +#if HAVE_SYS_TYPES_H +#include +#endif + +#if HAVE_SYS_STAT_H +#include +#endif + +#if HAVE_SYS_TIME_H +#include +#endif + +#if HAVE_SYS_SOCKET_H +#include +#endif + +#if HAVE_NETINET_IN_H +#include +#endif + +#if HAVE_NETINET_TCP_H +#include +#endif + +#if HAVE_ARPA_INET_H +#include +#endif + +#if HAVE_LIMITS_H +#include +#endif + +#if HAVE_OPENMP +#include +#endif + +#if HAVE_IO_H +#include // _open(), _close(), read(), write() on windows +#endif + +#if HAVE_SIGNAL_H +#include +#endif + +/** Integer types */ +#if HAVE_STDINT_H +#include + +#else + +/* Assume GLIB types */ +typedef gint8 int8_t; +typedef guint8 uint8_t; +typedef gint16 int16_t; +typedef guint16 uint16_t; +typedef gint32 int32_t; +typedef guint32 uint32_t; +typedef gint64 int64_t; +typedef guint64 uint64_t; +typedef guintptr uintptr_t; +typedef gintptr intptr_t; + +#endif + +#if defined(WIN32) && HAVE_WINDOWS_H +#include +#include /* Provides also socklen_t */ +#ifdef WITH_GLIB_STUBS +#include "win32_glibstubs.h" +#endif + +/* WIN32 special defines */ +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +#ifdef _MSC_VER +#pragma warning(disable : 4244) +#pragma warning(disable : 4101) +#pragma warning(disable : 4305) +#pragma warning(disable : 4996) +#endif + +#endif + +/* Darwin special defines (taken from config_macosx.h) */ +#ifdef DARWIN +# define MACINTOSH +# define __Types__ +#endif + +#ifdef LADSPA +#include +#endif + +#ifndef WIN32 +#include +#endif + +/** + * Macro used for safely accessing a message from a GError and using a default + * message if it is NULL. + * @param err Pointer to a GError to access the message field of. + * @return Message string + */ +#define fluid_gerror_message(err) ((err) ? err->message : "No error details") + +#ifdef WIN32 +char* fluid_get_windows_error(void); +#endif + +#define FLUID_INLINE inline + +#define FLUID_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) + +/* Integer<->pointer conversion */ +#define FLUID_POINTER_TO_UINT(x) ((unsigned int)(uintptr_t)(x)) +#define FLUID_UINT_TO_POINTER(x) ((void *)(uintptr_t)(x)) +#define FLUID_POINTER_TO_INT(x) ((signed int)(intptr_t)(x)) +#define FLUID_INT_TO_POINTER(x) ((void *)(intptr_t)(x)) + +/* Endian detection */ +#define FLUID_IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN) + +#define FLUID_LE32TOH(x) GINT32_FROM_LE(x) +#define FLUID_LE16TOH(x) GINT16_FROM_LE(x) + +#if FLUID_IS_BIG_ENDIAN +#define FLUID_FOURCC(_a, _b, _c, _d) \ + (uint32_t)(((uint32_t)(_a) << 24) | ((uint32_t)(_b) << 16) | ((uint32_t)(_c) << 8) | (uint32_t)(_d)) +#else +#define FLUID_FOURCC(_a, _b, _c, _d) \ + (uint32_t)(((uint32_t)(_d) << 24) | ((uint32_t)(_c) << 16) | ((uint32_t)(_b) << 8) | (uint32_t)(_a)) +#endif + +/* + * Utility functions + */ +char *fluid_strtok(char **str, char *delim); + +#define FLUID_FILE_TEST_EXISTS G_FILE_TEST_EXISTS +#define FLUID_FILE_TEST_IS_REGULAR G_FILE_TEST_IS_REGULAR +#define fluid_file_test(path, flags) g_file_test(path, flags) + +#define fluid_strfreev g_strfreev + +#if defined(__OS2__) +#define INCL_DOS +#include + +/* Define socklen_t if not provided */ +#if !HAVE_SOCKLEN_T +typedef int socklen_t; +#endif +#endif + +/** + Time functions + + */ + +unsigned int fluid_curtime(void); +double fluid_utime(void); + + +/** + Timers + + */ + +/* if the callback function returns 1 the timer will continue; if it + returns 0 it will stop */ +typedef int (*fluid_timer_callback_t)(void *data, unsigned int msec); + +typedef struct _fluid_timer_t fluid_timer_t; + +fluid_timer_t *new_fluid_timer(int msec, fluid_timer_callback_t callback, + void *data, int new_thread, int auto_destroy, + int high_priority); + +void delete_fluid_timer(fluid_timer_t *timer); +int fluid_timer_join(fluid_timer_t *timer); +int fluid_timer_stop(fluid_timer_t *timer); +int fluid_timer_is_running(const fluid_timer_t *timer); +long fluid_timer_get_interval(const fluid_timer_t * timer); + +// Macros to use for pre-processor if statements to test which Glib thread API we have (pre or post 2.32) +#define NEW_GLIB_THREAD_API GLIB_CHECK_VERSION(2,32,0) +#define OLD_GLIB_THREAD_API !GLIB_CHECK_VERSION(2,32,0) + +/* Muteces */ + +#if NEW_GLIB_THREAD_API + +/* glib 2.32 and newer */ + +/* Regular mutex */ +typedef GMutex fluid_mutex_t; +#define FLUID_MUTEX_INIT { 0 } +#define fluid_mutex_init(_m) g_mutex_init (&(_m)) +#define fluid_mutex_destroy(_m) g_mutex_clear (&(_m)) +#define fluid_mutex_lock(_m) g_mutex_lock(&(_m)) +#define fluid_mutex_unlock(_m) g_mutex_unlock(&(_m)) + +/* Recursive lock capable mutex */ +typedef GRecMutex fluid_rec_mutex_t; +#define fluid_rec_mutex_init(_m) g_rec_mutex_init(&(_m)) +#define fluid_rec_mutex_destroy(_m) g_rec_mutex_clear(&(_m)) +#define fluid_rec_mutex_lock(_m) g_rec_mutex_lock(&(_m)) +#define fluid_rec_mutex_unlock(_m) g_rec_mutex_unlock(&(_m)) + +/* Dynamically allocated mutex suitable for fluid_cond_t use */ +typedef GMutex fluid_cond_mutex_t; +#define fluid_cond_mutex_lock(m) g_mutex_lock(m) +#define fluid_cond_mutex_unlock(m) g_mutex_unlock(m) + +static FLUID_INLINE fluid_cond_mutex_t * +new_fluid_cond_mutex(void) +{ + GMutex *mutex; + mutex = g_new(GMutex, 1); + g_mutex_init(mutex); + return (mutex); +} + +static FLUID_INLINE void +delete_fluid_cond_mutex(fluid_cond_mutex_t *m) +{ + fluid_return_if_fail(m != NULL); + g_mutex_clear(m); + g_free(m); +} + +/* Thread condition signaling */ +typedef GCond fluid_cond_t; +#define fluid_cond_signal(cond) g_cond_signal(cond) +#define fluid_cond_broadcast(cond) g_cond_broadcast(cond) +#define fluid_cond_wait(cond, mutex) g_cond_wait(cond, mutex) + +static FLUID_INLINE fluid_cond_t * +new_fluid_cond(void) +{ + GCond *cond; + cond = g_new(GCond, 1); + g_cond_init(cond); + return (cond); +} + +static FLUID_INLINE void +delete_fluid_cond(fluid_cond_t *cond) +{ + fluid_return_if_fail(cond != NULL); + g_cond_clear(cond); + g_free(cond); +} + +/* Thread private data */ + +typedef GPrivate fluid_private_t; +#define fluid_private_init(_priv) memset (&_priv, 0, sizeof (_priv)) +#define fluid_private_free(_priv) +#define fluid_private_get(_priv) g_private_get(&(_priv)) +#define fluid_private_set(_priv, _data) g_private_set(&(_priv), _data) + +#else + +/* glib prior to 2.32 */ + +/* Regular mutex */ +typedef GStaticMutex fluid_mutex_t; +#define FLUID_MUTEX_INIT G_STATIC_MUTEX_INIT +#define fluid_mutex_destroy(_m) g_static_mutex_free(&(_m)) +#define fluid_mutex_lock(_m) g_static_mutex_lock(&(_m)) +#define fluid_mutex_unlock(_m) g_static_mutex_unlock(&(_m)) + +#define fluid_mutex_init(_m) do { \ + if (!g_thread_supported ()) g_thread_init (NULL); \ + g_static_mutex_init (&(_m)); \ +} while(0) + +/* Recursive lock capable mutex */ +typedef GStaticRecMutex fluid_rec_mutex_t; +#define fluid_rec_mutex_destroy(_m) g_static_rec_mutex_free(&(_m)) +#define fluid_rec_mutex_lock(_m) g_static_rec_mutex_lock(&(_m)) +#define fluid_rec_mutex_unlock(_m) g_static_rec_mutex_unlock(&(_m)) + +#define fluid_rec_mutex_init(_m) do { \ + if (!g_thread_supported ()) g_thread_init (NULL); \ + g_static_rec_mutex_init (&(_m)); \ +} while(0) + +/* Dynamically allocated mutex suitable for fluid_cond_t use */ +typedef GMutex fluid_cond_mutex_t; +#define delete_fluid_cond_mutex(m) g_mutex_free(m) +#define fluid_cond_mutex_lock(m) g_mutex_lock(m) +#define fluid_cond_mutex_unlock(m) g_mutex_unlock(m) + +static FLUID_INLINE fluid_cond_mutex_t * +new_fluid_cond_mutex(void) +{ + if(!g_thread_supported()) + { + g_thread_init(NULL); + } + + return g_mutex_new(); +} + +/* Thread condition signaling */ +typedef GCond fluid_cond_t; +fluid_cond_t *new_fluid_cond(void); +#define delete_fluid_cond(cond) g_cond_free(cond) +#define fluid_cond_signal(cond) g_cond_signal(cond) +#define fluid_cond_broadcast(cond) g_cond_broadcast(cond) +#define fluid_cond_wait(cond, mutex) g_cond_wait(cond, mutex) + +/* Thread private data */ +typedef GStaticPrivate fluid_private_t; +#define fluid_private_get(_priv) g_static_private_get(&(_priv)) +#define fluid_private_set(_priv, _data) g_static_private_set(&(_priv), _data, NULL) +#define fluid_private_free(_priv) g_static_private_free(&(_priv)) + +#define fluid_private_init(_priv) do { \ + if (!g_thread_supported ()) g_thread_init (NULL); \ + g_static_private_init (&(_priv)); \ +} while(0) + +#endif + + +/* Atomic operations */ + +#define fluid_atomic_int_inc(_pi) g_atomic_int_inc(_pi) +#define fluid_atomic_int_get(_pi) g_atomic_int_get(_pi) +#define fluid_atomic_int_set(_pi, _val) g_atomic_int_set(_pi, _val) +#define fluid_atomic_int_dec_and_test(_pi) g_atomic_int_dec_and_test(_pi) +#define fluid_atomic_int_compare_and_exchange(_pi, _old, _new) \ + g_atomic_int_compare_and_exchange(_pi, _old, _new) + +#if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 30) +#define fluid_atomic_int_exchange_and_add(_pi, _add) \ + g_atomic_int_add(_pi, _add) +#define fluid_atomic_int_add(_pi, _add) \ + g_atomic_int_add(_pi, _add) +#else +#define fluid_atomic_int_exchange_and_add(_pi, _add) \ + g_atomic_int_exchange_and_add(_pi, _add) +#define fluid_atomic_int_add(_pi, _add) \ + g_atomic_int_exchange_and_add(_pi, _add) +#endif + +#define fluid_atomic_pointer_get(_pp) g_atomic_pointer_get(_pp) +#define fluid_atomic_pointer_set(_pp, val) g_atomic_pointer_set(_pp, val) +#define fluid_atomic_pointer_compare_and_exchange(_pp, _old, _new) \ + g_atomic_pointer_compare_and_exchange(_pp, _old, _new) + +static FLUID_INLINE void +fluid_atomic_float_set(fluid_atomic_float_t *fptr, float val) +{ + int32_t ival; + memcpy(&ival, &val, 4); + fluid_atomic_int_set((fluid_atomic_int_t *)fptr, ival); +} + +static FLUID_INLINE float +fluid_atomic_float_get(fluid_atomic_float_t *fptr) +{ + int32_t ival; + float fval; + ival = fluid_atomic_int_get((fluid_atomic_int_t *)fptr); + memcpy(&fval, &ival, 4); + return fval; +} + + +/* Threads */ + +/* other thread implementations might change this for their needs */ +typedef void *fluid_thread_return_t; +/* static return value for thread functions which requires a return value */ +#define FLUID_THREAD_RETURN_VALUE (NULL) + +typedef GThread fluid_thread_t; +typedef fluid_thread_return_t (*fluid_thread_func_t)(void *data); + +#define FLUID_THREAD_ID_NULL NULL /* A NULL "ID" value */ +#define fluid_thread_id_t GThread * /* Data type for a thread ID */ +#define fluid_thread_get_id() g_thread_self() /* Get unique "ID" for current thread */ + +fluid_thread_t *new_fluid_thread(const char *name, fluid_thread_func_t func, void *data, + int prio_level, int detach); +void delete_fluid_thread(fluid_thread_t *thread); +void fluid_thread_self_set_prio(int prio_level); +int fluid_thread_join(fluid_thread_t *thread); + +/* Dynamic Module Loading, currently only used by LADSPA subsystem */ +#ifdef LADSPA + +typedef GModule fluid_module_t; + +#define fluid_module_open(_name) g_module_open((_name), G_MODULE_BIND_LOCAL) +#define fluid_module_close(_mod) g_module_close(_mod) +#define fluid_module_error() g_module_error() +#define fluid_module_name(_mod) g_module_name(_mod) +#define fluid_module_symbol(_mod, _name, _ptr) g_module_symbol((_mod), (_name), (_ptr)) + +#endif /* LADSPA */ + +/* Sockets and I/O */ + +int fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt, char *buf, int len); +int fluid_ostream_printf(fluid_ostream_t out, const char *format, ...); + +#if defined(WIN32) +typedef SOCKET fluid_socket_t; +#else +typedef int fluid_socket_t; +#endif + +/* The function should return 0 if no error occurred, non-zero + otherwise. If the function return non-zero, the socket will be + closed by the server. */ +typedef int (*fluid_server_func_t)(void *data, fluid_socket_t client_socket, char *addr); + +fluid_server_socket_t *new_fluid_server_socket(int port, fluid_server_func_t func, void *data); +void delete_fluid_server_socket(fluid_server_socket_t *sock); +int fluid_server_socket_join(fluid_server_socket_t *sock); +void fluid_socket_close(fluid_socket_t sock); +fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock); +fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock); + +/* File access */ +#define fluid_stat(_filename, _statbuf) g_stat((_filename), (_statbuf)) +#if !GLIB_CHECK_VERSION(2, 26, 0) + /* GStatBuf has not been introduced yet, manually typedef to what they had at that time: + * https://github.com/GNOME/glib/blob/e7763678b56e3be073cc55d707a6e92fc2055ee0/glib/gstdio.h#L98-L115 + */ + #if defined(WIN32) || HAVE_WINDOWS_H // somehow reliably mock G_OS_WIN32?? + // Any effort from our side to reliably mock GStatBuf on Windows is in vain. E.g. glib-2.16 is broken as it uses struct stat rather than struct _stat32 on Win x86. + // Disable it (the user has been warned by cmake). + #undef fluid_stat + #define fluid_stat(_filename, _statbuf) (-1) + typedef struct _fluid_stat_buf_t{int st_mtime;} fluid_stat_buf_t; + #else + /* posix, OS/2, etc. */ + typedef struct stat fluid_stat_buf_t; + #endif +#else +typedef GStatBuf fluid_stat_buf_t; +#endif + +FILE* fluid_file_open(const char* filename, const char** errMsg); +fluid_long_long_t fluid_file_tell(FILE* f); + + +/* Profiling */ +#if WITH_PROFILING +/** profiling interface between Profiling command shell and Audio + rendering API (FluidProfile_0004.pdf- 3.2.2) +*/ + +/* + ----------------------------------------------------------------------------- + Shell task side | Profiling interface | Audio task side + ----------------------------------------------------------------------------- + profiling | Internal | | | Audio + command <---> |<-- profling -->| Data |<--macros -->| <--> rendering + shell | API | | | API + +*/ + +/* default parameters for shell command "prof_start" in fluid_sys.c */ +#define FLUID_PROFILE_DEFAULT_BANK 0 /* default bank */ +#define FLUID_PROFILE_DEFAULT_PROG 16 /* default prog (organ) */ +#define FLUID_PROFILE_FIRST_KEY 12 /* first key generated */ +#define FLUID_PROFILE_LAST_KEY 108 /* last key generated */ +#define FLUID_PROFILE_DEFAULT_VEL 64 /* default note velocity */ +#define FLUID_PROFILE_VOICE_ATTEN -0.04f /* gain attenuation per voice (dB) */ + + +#define FLUID_PROFILE_DEFAULT_PRINT 0 /* default print mode */ +#define FLUID_PROFILE_DEFAULT_N_PROF 1 /* default number of measures */ +#define FLUID_PROFILE_DEFAULT_DURATION 500 /* default duration (ms) */ + + +extern unsigned short fluid_profile_notes; /* number of generated notes */ +extern unsigned char fluid_profile_bank; /* bank,prog preset used by */ +extern unsigned char fluid_profile_prog; /* generated notes */ +extern unsigned char fluid_profile_print; /* print mode */ + +extern unsigned short fluid_profile_n_prof;/* number of measures */ +extern unsigned short fluid_profile_dur; /* measure duration in ms */ +extern fluid_atomic_int_t fluid_profile_lock ; /* lock between multiple shell */ +/**/ + +/*---------------------------------------------- + Internal profiling API (in fluid_sys.c) +-----------------------------------------------*/ +/* Starts a profiling measure used in shell command "prof_start" */ +void fluid_profile_start_stop(unsigned int end_ticks, short clear_data); + +/* Returns status used in shell command "prof_start" */ +int fluid_profile_get_status(void); + +/* Prints profiling data used in shell command "prof_start" */ +void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out); + +/* Returns True if profiling cancellation has been requested */ +int fluid_profile_is_cancel_req(void); + +/* For OS that implement key for profile cancellation: + 1) Adds #define FLUID_PROFILE_CANCEL + 2) Adds the necessary code inside fluid_profile_is_cancel() see fluid_sys.c +*/ +#if defined(WIN32) /* Profile cancellation is supported for Windows */ +#define FLUID_PROFILE_CANCEL + +#elif defined(__OS2__) /* OS/2 specific stuff */ +/* Profile cancellation isn't yet supported for OS2 */ + +#else /* POSIX stuff */ +#define FLUID_PROFILE_CANCEL /* Profile cancellation is supported for linux */ +#include /* STDIN_FILENO */ +#include /* select() */ +#endif /* posix */ + +/* logging profiling data (used on synthesizer instance deletion) */ +void fluid_profiling_print(void); + +/*---------------------------------------------- + Profiling Data (in fluid_sys.c) +-----------------------------------------------*/ +/** Profiling data. Keep track of min/avg/max values to profile a + piece of code. */ +typedef struct _fluid_profile_data_t +{ + const char *description; /* name of the piece of code under profiling */ + double min, max, total; /* duration (microsecond) */ + unsigned int count; /* total count */ + unsigned int n_voices; /* voices number */ + unsigned int n_samples; /* audio samples number */ +} fluid_profile_data_t; + +enum +{ + /* commands/status (profiling interface) */ + PROFILE_STOP, /* command to stop a profiling measure */ + PROFILE_START, /* command to start a profile measure */ + PROFILE_READY, /* status to signal that a profiling measure has finished + and ready to be printed */ + /*- State returned by fluid_profile_get_status() -*/ + /* between profiling commands and internal profiling API */ + PROFILE_RUNNING, /* a profiling measure is running */ + PROFILE_CANCELED,/* a profiling measure has been canceled */ +}; + +/* Data interface */ +extern unsigned char fluid_profile_status ; /* command and status */ +extern unsigned int fluid_profile_end_ticks; /* ending position (in ticks) */ +extern fluid_profile_data_t fluid_profile_data[]; /* Profiling data */ + +/*---------------------------------------------- + Probes macros +-----------------------------------------------*/ +/** Macro to obtain a time reference used for the profiling */ +#define fluid_profile_ref() fluid_utime() + +/** Macro to create a variable and assign the current reference time for profiling. + * So we don't get unused variable warnings when profiling is disabled. */ +#define fluid_profile_ref_var(name) double name = fluid_utime() + +/** + * Profile identifier numbers. List all the pieces of code you want to profile + * here. Be sure to add an entry in the fluid_profile_data table in + * fluid_sys.c + */ +enum +{ + FLUID_PROF_WRITE, + FLUID_PROF_ONE_BLOCK, + FLUID_PROF_ONE_BLOCK_CLEAR, + FLUID_PROF_ONE_BLOCK_VOICE, + FLUID_PROF_ONE_BLOCK_VOICES, + FLUID_PROF_ONE_BLOCK_REVERB, + FLUID_PROF_ONE_BLOCK_CHORUS, + FLUID_PROF_VOICE_NOTE, + FLUID_PROF_VOICE_RELEASE, + FLUID_PROFILE_NBR /* number of profile probes */ +}; +/** Those macros are used to calculate the min/avg/max. Needs a profile number, a + time reference, the voices and samples number. */ + +/* local macro : acquiere data */ +#define fluid_profile_data(_num, _ref, voices, samples)\ +{\ + double _now = fluid_utime();\ + double _delta = _now - _ref;\ + fluid_profile_data[_num].min = _delta < fluid_profile_data[_num].min ?\ + _delta : fluid_profile_data[_num].min; \ + fluid_profile_data[_num].max = _delta > fluid_profile_data[_num].max ?\ + _delta : fluid_profile_data[_num].max;\ + fluid_profile_data[_num].total += _delta;\ + fluid_profile_data[_num].count++;\ + fluid_profile_data[_num].n_voices += voices;\ + fluid_profile_data[_num].n_samples += samples;\ + _ref = _now;\ +} + +/** Macro to collect data, called from inner functions inside audio + rendering API */ +#define fluid_profile(_num, _ref, voices, samples)\ +{\ + if ( fluid_profile_status == PROFILE_START)\ + { /* acquires data */\ + fluid_profile_data(_num, _ref, voices, samples)\ + }\ +} + +/** Macro to collect data, called from audio rendering API (fluid_write_xxxx()). + This macro control profiling ending position (in ticks). +*/ +#define fluid_profile_write(_num, _ref, voices, samples)\ +{\ + if (fluid_profile_status == PROFILE_START)\ + {\ + /* acquires data first: must be done before checking that profile is + finished to ensure at least one valid data sample. + */\ + fluid_profile_data(_num, _ref, voices, samples)\ + if (fluid_synth_get_ticks(synth) >= fluid_profile_end_ticks)\ + {\ + /* profiling is finished */\ + fluid_profile_status = PROFILE_READY;\ + }\ + }\ +} + +#else + +/* No profiling */ +#define fluid_profiling_print() +#define fluid_profile_ref() 0 +#define fluid_profile_ref_var(name) +#define fluid_profile(_num,_ref,voices, samples) +#define fluid_profile_write(_num,_ref, voices, samples) +#endif /* WITH_PROFILING */ + +/** + + Memory locking + + Memory locking is used to avoid swapping of the large block of + sample data. + */ + +#if defined(HAVE_SYS_MMAN_H) && !defined(__OS2__) +#define fluid_mlock(_p,_n) mlock(_p, _n) +#define fluid_munlock(_p,_n) munlock(_p,_n) +#else +#define fluid_mlock(_p,_n) 0 +#define fluid_munlock(_p,_n) +#endif + + +/** + + Floating point exceptions + + fluid_check_fpe() checks for "unnormalized numbers" and other + exceptions of the floating point processsor. +*/ +#ifdef FPE_CHECK +#define fluid_check_fpe(expl) fluid_check_fpe_i386(expl) +#define fluid_clear_fpe() fluid_clear_fpe_i386() +unsigned int fluid_check_fpe_i386(char *explanation_in_case_of_fpe); +void fluid_clear_fpe_i386(void); +#else +#define fluid_check_fpe(expl) +#define fluid_clear_fpe() +#endif + + +/* System control */ +void fluid_msleep(unsigned int msecs); + +/** + * Advances the given \c ptr to the next \c alignment byte boundary. + * Make sure you've allocated an extra of \c alignment bytes to avoid a buffer overflow. + * + * @note \c alignment must be a power of two + * @return Returned pointer is guaranteed to be aligned to \c alignment boundary and in range \f[ ptr <= returned_ptr < ptr + alignment \f]. + */ +static FLUID_INLINE void *fluid_align_ptr(const void *ptr, unsigned int alignment) +{ + uintptr_t ptr_int = (uintptr_t)ptr; + unsigned int offset = ptr_int & (alignment - 1); + unsigned int add = (alignment - offset) & (alignment - 1); // advance the pointer to the next alignment boundary + ptr_int += add; + + /* assert alignment is power of two */ + FLUID_ASSERT(!(alignment == 0) && !(alignment & (alignment - 1))); + + return (void *)ptr_int; +} + +#define FLUID_DEFAULT_ALIGNMENT (64U) + +#endif /* _FLUID_SYS_H */ diff --git a/thirdparty/fluidsynth/src/utils/fluidsynth_priv.h b/thirdparty/fluidsynth/src/utils/fluidsynth_priv.h new file mode 100644 index 000000000..452b98467 --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/fluidsynth_priv.h @@ -0,0 +1,335 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* + * @file fluidsynth_priv.h + * + * lightweight part of fluid_sys.h, containing forward declarations of fluidsynth's private types and private macros + * + * include this one file in fluidsynth's private header files + */ + +#ifndef _FLUIDSYNTH_PRIV_H +#define _FLUIDSYNTH_PRIV_H + +#include "config.h" + +#ifndef WITH_GLIB_STUBS +#include +#endif + +#if HAVE_STDLIB_H +#include // malloc, free +#endif + +#if HAVE_STDIO_H +#include // printf +#endif + +#if HAVE_STRING_H +#include +#endif + + +#include "fluidsynth.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************************************** + * + * BASIC TYPES + */ + +#if defined(WITH_FLOAT) +typedef float fluid_real_t; +#else +typedef double fluid_real_t; +#endif + +#if defined(SUPPORTS_VLA) +# define FLUID_DECLARE_VLA(_type, _name, _len) \ + _type _name[_len] +#else +# define FLUID_DECLARE_VLA(_type, _name, _len) \ + _type* _name = g_newa(_type, (_len)) +#endif + + +/** Atomic types */ +#if defined(WIN32) && defined(WITH_GLIB_STUBS) +typedef long fluid_atomic_int_t; +typedef unsigned long fluid_atomic_uint_t; +#else +typedef int fluid_atomic_int_t; +typedef unsigned int fluid_atomic_uint_t; +#endif +typedef float fluid_atomic_float_t; + + +/*************************************************************** + * + * FORWARD DECLARATIONS + */ +typedef struct _fluid_env_data_t fluid_env_data_t; +typedef struct _fluid_adriver_definition_t fluid_adriver_definition_t; +typedef struct _fluid_channel_t fluid_channel_t; +typedef struct _fluid_tuning_t fluid_tuning_t; +typedef struct _fluid_hashtable_t fluid_hashtable_t; +typedef struct _fluid_client_t fluid_client_t; +typedef struct _fluid_server_socket_t fluid_server_socket_t; +typedef struct _fluid_sample_timer_t fluid_sample_timer_t; +typedef struct _fluid_zone_range_t fluid_zone_range_t; +typedef struct _fluid_rvoice_eventhandler_t fluid_rvoice_eventhandler_t; + +/* Declare rvoice related typedefs here instead of fluid_rvoice.h, as it's needed + * in fluid_lfo.c and fluid_adsr.c as well */ +typedef union _fluid_rvoice_param_t +{ + void *ptr; + int i; + fluid_real_t real; +} fluid_rvoice_param_t; +enum { MAX_EVENT_PARAMS = 7 }; /**< Maximum number of #fluid_rvoice_param_t to be passed to an #fluid_rvoice_function_t */ +typedef void (*fluid_rvoice_function_t)(void *obj, const fluid_rvoice_param_t param[MAX_EVENT_PARAMS]); + +/* Macro for declaring an rvoice event function (#fluid_rvoice_function_t). The functions may only access + * those params that were previously set in fluid_voice.c + */ +#define DECLARE_FLUID_RVOICE_FUNCTION(name) void name(void* obj, const fluid_rvoice_param_t param[MAX_EVENT_PARAMS]) + + +/*************************************************************** + * + * CONSTANTS + */ + +#define FLUID_BUFSIZE 64 /**< FluidSynth internal buffer size (in samples) */ +#define FLUID_MIXER_MAX_BUFFERS_DEFAULT (8192/FLUID_BUFSIZE) /**< Number of buffers that can be processed in one rendering run */ +#define FLUID_MAX_EVENTS_PER_BUFSIZE 1024 /**< Maximum queued MIDI events per #FLUID_BUFSIZE */ +#define FLUID_MAX_RETURN_EVENTS 1024 /**< Maximum queued synthesis thread return events */ +#define FLUID_MAX_EVENT_QUEUES 16 /**< Maximum number of unique threads queuing events */ +#define FLUID_DEFAULT_AUDIO_RT_PRIO 60 /**< Default setting for audio.realtime-prio */ +#define FLUID_DEFAULT_MIDI_RT_PRIO 50 /**< Default setting for midi.realtime-prio */ +#define FLUID_NUM_MOD 64 /**< Maximum number of modulators in a voice */ + +/*************************************************************** + * + * SYSTEM INTERFACE + */ + +/* Math constants */ +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795 +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530941723212145818 +#endif + +#ifndef M_LN10 +#define M_LN10 2.3025850929940456840179914546844 +#endif + +#define FLUID_M_PI ((fluid_real_t)M_PI) +#define FLUID_M_LN2 ((fluid_real_t)M_LN2) +#define FLUID_M_LN10 ((fluid_real_t)M_LN10) + +/* Math functions */ +#if defined WITH_FLOAT && defined HAVE_SINF +#define FLUID_SIN sinf +#else +#define FLUID_SIN (fluid_real_t)sin +#endif + +#if defined WITH_FLOAT && defined HAVE_COSF +#define FLUID_COS cosf +#else +#define FLUID_COS (fluid_real_t)cos +#endif + +#if defined WITH_FLOAT && defined HAVE_FABSF +#define FLUID_FABS fabsf +#else +#define FLUID_FABS (fluid_real_t)fabs +#endif + +#if defined WITH_FLOAT && defined HAVE_POWF +#define FLUID_POW powf +#else +#define FLUID_POW (fluid_real_t)pow +#endif + +#if defined WITH_FLOAT && defined HAVE_SQRTF +#define FLUID_SQRT sqrtf +#else +#define FLUID_SQRT (fluid_real_t)sqrt +#endif + +#if defined WITH_FLOAT && defined HAVE_LOGF +#define FLUID_LOGF logf +#else +#define FLUID_LOGF (fluid_real_t)log +#endif + +/* Memory allocation */ +#define FLUID_MALLOC(_n) fluid_alloc(_n) +#define FLUID_REALLOC(_p,_n) realloc(_p,_n) +#define FLUID_FREE(_p) fluid_free(_p) +#define FLUID_NEW(_t) (_t*)FLUID_MALLOC(sizeof(_t)) +#define FLUID_ARRAY_ALIGNED(_t,_n,_a) (_t*)FLUID_MALLOC((_n)*sizeof(_t) + ((unsigned int)_a - 1u)) +#define FLUID_ARRAY(_t,_n) FLUID_ARRAY_ALIGNED(_t,_n,1u) + +void* fluid_alloc(size_t len); + +/* File access */ +#define FLUID_FOPEN(_f,_m) fluid_fopen(_f,_m) +#define FLUID_FCLOSE(_f) fclose(_f) +#define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f) + +FILE *fluid_fopen(const char *filename, const char *mode); + +#ifdef WIN32 +#define FLUID_FSEEK(_f,_n,_set) _fseeki64(_f,_n,_set) +#else +#define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set) +#endif + +#define FLUID_FTELL(_f) fluid_file_tell(_f) + +/* Memory functions */ +#define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n) +#define FLUID_MEMSET(_s,_c,_n) memset(_s,_c,_n) + +/* String functions */ +#define FLUID_STRLEN(_s) strlen(_s) +#define FLUID_STRCMP(_s,_t) strcmp(_s,_t) +#define FLUID_STRNCMP(_s,_t,_n) strncmp(_s,_t,_n) +#define FLUID_STRCPY(_dst,_src) strcpy(_dst,_src) +#define FLUID_STRTOL(_s,_e,_b) strtol(_s,_e,_b) + +#define FLUID_STRNCPY(_dst,_src,_n) \ +do { strncpy(_dst,_src,_n-1); \ + (_dst)[(_n)-1]='\0'; \ +}while(0) + +#define FLUID_STRCHR(_s,_c) strchr(_s,_c) +#define FLUID_STRRCHR(_s,_c) strrchr(_s,_c) + +#ifdef strdup +#define FLUID_STRDUP(s) strdup(s) +#else +#define FLUID_STRDUP(s) FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s) +#endif + +#define FLUID_SPRINTF sprintf +#define FLUID_FPRINTF fprintf + +#if (defined(WIN32) && _MSC_VER < 1900) || defined(MINGW32) +/* need to make sure we use a C99 compliant implementation of (v)snprintf(), + * i.e. not microsofts non compliant extension _snprintf() as it doesn't + * reliably null-terminate the buffer + */ +#define FLUID_SNPRINTF g_snprintf +#else +#define FLUID_SNPRINTF snprintf +#endif + +#if (defined(WIN32) && _MSC_VER < 1500) || defined(MINGW32) +#define FLUID_VSNPRINTF g_vsnprintf +#else +#define FLUID_VSNPRINTF vsnprintf +#endif + +#if defined(WIN32) && !defined(MINGW32) +#define FLUID_STRCASECMP _stricmp +#else +#define FLUID_STRCASECMP strcasecmp +#endif + +#if defined(WIN32) && !defined(MINGW32) +#define FLUID_STRNCASECMP _strnicmp +#else +#define FLUID_STRNCASECMP strncasecmp +#endif + + +#define fluid_clip(_val, _min, _max) \ +{ (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); } + +#if WITH_FTS +#define FLUID_PRINTF post +#define FLUID_FLUSH() +#else +#define FLUID_PRINTF printf +#define FLUID_FLUSH() fflush(stdout) +#endif + +/* People who want to reduce the size of the may do this by entirely + * removing the logging system. This will cause all log messages to + * be discarded at compile time, allowing to save about 80 KiB for + * the compiled binary. + */ +#if 0 +#define FLUID_LOG (void)sizeof +#else +#define FLUID_LOG fluid_log +#endif + +#if defined(DEBUG) && !defined(NDEBUG) +#define FLUID_ASSERT(a) g_assert(a) +#else +#define FLUID_ASSERT(a) +#endif + +#define FLUID_LIKELY G_LIKELY +#define FLUID_UNLIKELY G_UNLIKELY + +/* Misc */ +#if defined(__INTEL_COMPILER) +#define FLUID_RESTRICT restrict +#elif defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#define FLUID_RESTRICT __restrict__ +#elif defined(_MSC_VER) && _MSC_VER >= 1400 +#define FLUID_RESTRICT __restrict +#else +#warning "Dont know how this compiler handles restrict pointers, refuse to use them." +#define FLUID_RESTRICT +#endif + +#define FLUID_N_ELEMENTS(struct) (sizeof (struct) / sizeof (struct[0])) +#define FLUID_MEMBER_SIZE(struct, member) ( sizeof (((struct *)0)->member) ) + + +#define fluid_return_if_fail(cond) \ +if(cond) \ + ; \ +else \ + return + +#define fluid_return_val_if_fail(cond, val) \ + fluid_return_if_fail(cond) (val) + +#ifdef __cplusplus +} +#endif + +#endif /* _FLUIDSYNTH_PRIV_H */ diff --git a/thirdparty/fluidsynth/src/utils/win32_glibstubs.c b/thirdparty/fluidsynth/src/utils/win32_glibstubs.c new file mode 100644 index 000000000..ecd82a9bb --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/win32_glibstubs.c @@ -0,0 +1,144 @@ +#ifdef WIN32 + +#define _CRT_SECURE_NO_WARNINGS + +#include "fluidsynth_priv.h" +#include "fluid_sys.h" +#include "win32_glibstubs.h" + +#include + +static wchar_t *utf8_to_wc(const char *str) +{ + if (str == NULL) + { + return NULL; + } + wchar_t *wstr = NULL; + int length; + + if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, NULL, 0)) == 0) + { + return NULL; + } + wstr = malloc(length * sizeof(wchar_t)); + if (wstr == NULL) + { + return NULL; + } + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, wstr, length); + return wstr; +} + +BOOL fluid_g_file_test(const char *pathA, int flags) +{ + wchar_t *path = utf8_to_wc(pathA); + if (path == NULL) + { + return FALSE; + } + DWORD attributes = GetFileAttributesW(path); + FLUID_FREE(path); + if (attributes == INVALID_FILE_ATTRIBUTES) + { + return FALSE; + } + if (flags & G_FILE_TEST_EXISTS) + { + return TRUE; + } + if (flags & G_FILE_TEST_IS_REGULAR) + { + return (attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0; + } + return FALSE; +} + +double fluid_g_get_monotonic_time(void) +{ + static LARGE_INTEGER freq_cache = { 0, 0 }; /* Performance Frequency */ + LARGE_INTEGER perf_cpt; + + if (!freq_cache.QuadPart) + { + QueryPerformanceFrequency(&freq_cache); /* Frequency value */ + } + + QueryPerformanceCounter(&perf_cpt); /* Counter value */ + return perf_cpt.QuadPart * 1000000.0 / freq_cache.QuadPart; /* time in micros */ +} + +/* Thread support */ +static unsigned __stdcall g_thread_wrapper(void *info_) +{ + GThread *info = (GThread *)info_; + info->func(info->data); + /* Free the "GThread" now if it was detached. Otherwise, it's freed in fluid_g_thread_join. */ + if (info->handle == NULL) + { + free(info); + } + _endthreadex(0); + return 0; +} + +GThread *fluid_g_thread_create(GThreadFunc func, void *data, BOOL joinable, GError **error) +{ + static GError error_container; + + g_return_val_if_fail(func != NULL, NULL); + + GThread *info = (GThread *)malloc(sizeof(GThread)); + if (info == NULL) + { + return NULL; + } + + info->func = func; + info->data = data; + + HANDLE thread = (HANDLE)_beginthreadex(NULL, 0, g_thread_wrapper, info, CREATE_SUSPENDED, NULL); + + if (error != NULL) + { + error_container.code = thread ? 0 : errno; + if (errno != 0) + { + error_container.message = strerror(errno); + } + *error = &error_container; + } + + if (thread == NULL) + { + free(info); + info = NULL; + } + else + { + if (!joinable) + { + /* Release thread reference, if caller doesn't want to join */ + CloseHandle(thread); + info->handle = NULL; + } + else + { + info->handle = thread; + } + ResumeThread(thread); + } + return info; +} + +void fluid_g_thread_join(GThread *thread) +{ + if (thread != NULL && thread->handle != NULL) + { + WaitForSingleObject(thread->handle, INFINITE); + CloseHandle(thread->handle); + free(thread); + } +} + +#endif \ No newline at end of file diff --git a/thirdparty/fluidsynth/src/utils/win32_glibstubs.h b/thirdparty/fluidsynth/src/utils/win32_glibstubs.h new file mode 100644 index 000000000..92380d8a8 --- /dev/null +++ b/thirdparty/fluidsynth/src/utils/win32_glibstubs.h @@ -0,0 +1,141 @@ +#ifndef _GLIBSTUBS_H +#define _GLIBSTUBS_H + +#ifdef WIN32 +#include +#include +#include + +/* Miscellaneous stubs */ +#define GLIB_CHECK_VERSION(x, y, z) 0 /* Evaluate to 0 to get FluidSynth to use the "old" thread API */ +#define GLIB_MAJOR_VERSION 2 +#define GLIB_MINOR_VERSION 29 + +typedef struct +{ + int code; + const char *message; +} GError; +typedef void *gpointer; + +#define g_new(s, c) FLUID_ARRAY(s, c) +#define g_free(p) FLUID_FREE(p) +#define g_strfreev FLUID_FREE +#define g_newa(_type, _len) (_type *)_alloca(sizeof(_type) * (_len)) +#define g_assert(a) assert(a) +#define G_LIKELY(expr) (expr) +#define G_UNLIKELY(expr) (expr) +#endif + +#define g_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a) +#define g_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__) + +#define g_return_val_if_fail(expr, val) if (expr) {} else { return val; } +#define g_clear_error(err) do {} while (0) + +#define G_FILE_TEST_EXISTS 1 +#define G_FILE_TEST_IS_REGULAR 2 + +#define g_file_test fluid_g_file_test +#define g_shell_parse_argv fluid_g_shell_parse_argv +BOOL fluid_g_file_test(const char *path, int flags); +BOOL fluid_g_shell_parse_argv(const char *command_line, int *argcp, char ***argvp, void *dummy); + +#define g_get_monotonic_time fluid_g_get_monotonic_time +double fluid_g_get_monotonic_time(void); + +/* Byte ordering */ +#ifdef __BYTE_ORDER__ +#define G_BYTE_ORDER __BYTE_ORDER__ +#define G_BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#else +// If __BYTE_ORDER__ isn't defined, assume little endian +#define G_BYTE_ORDER 1234 +#define G_BIG_ENDIAN 4321 +#endif + +#if G_BYTE_ORDER == G_BIG_ENDIAN +#define GINT16_FROM_LE(x) (int16_t)(((uint16_t)(x) >> 8) | ((uint16_t)(x) << 8)) +#define GINT32_FROM_LE(x) (int32_t)((FLUID_LE16TOH(x) << 16) | (FLUID16_LE16TOH(x >> 16))) +#else +#define GINT32_FROM_LE(x) (x) +#define GINT16_FROM_LE(x) (x) + +/* Thread support */ +#define g_thread_supported() 1 +#define g_thread_init(_) do {} while (0) +#define g_usleep(usecs) Sleep((usecs) / 1000) + +typedef gpointer (*GThreadFunc)(void *data); +typedef struct +{ + GThreadFunc func; + void *data; + HANDLE handle; +} GThread; + +#define g_thread_create fluid_g_thread_create +#define g_thread_join fluid_g_thread_join +GThread *fluid_g_thread_create(GThreadFunc func, void *data, BOOL joinable, GError **error); +void fluid_g_thread_join(GThread *thread); + +/* Regular mutex */ +typedef SRWLOCK GStaticMutex; +#define G_STATIC_MUTEX_INIT SRWLOCK_INIT +#define g_static_mutex_init(_m) InitializeSRWLock(_m) +#define g_static_mutex_free(_m) do {} while (0) +#define g_static_mutex_lock(_m) AcquireSRWLockExclusive(_m) +#define g_static_mutex_unlock(_m) ReleaseSRWLockExclusive(_m) + +/* Recursive lock capable mutex */ +typedef CRITICAL_SECTION GStaticRecMutex; +#define g_static_rec_mutex_init(_m) InitializeCriticalSection(_m) +#define g_static_rec_mutex_free(_m) DeleteCriticalSection(_m) +#define g_static_rec_mutex_lock(_m) EnterCriticalSection(_m) +#define g_static_rec_mutex_unlock(_m) LeaveCriticalSection(_m) + +/* Dynamically allocated mutex suitable for fluid_cond_t use */ +typedef SRWLOCK GMutex; +#define g_mutex_free(m) do { if (m != NULL) g_free(m); } while(0) +#define g_mutex_lock(m) AcquireSRWLockExclusive(m) +#define g_mutex_unlock(m) ReleaseSRWLockExclusive(m) + +static inline GMutex *g_mutex_new(void) +{ + GMutex *mutex = g_new(GMutex, 1); + InitializeSRWLock(mutex); + return mutex; +} + +/* Thread condition signaling */ +typedef CONDITION_VARIABLE GCond; +#define g_cond_free(cond) do { if (cond != NULL) g_free(cond); } while (0) +#define g_cond_signal(cond) WakeConditionVariable(cond) +#define g_cond_broadcast(cond) WakeAllConditionVariable(cond) +#define g_cond_wait(cond, mutex) SleepConditionVariableSRW(cond, mutex, INFINITE, 0) + +static inline GCond *g_cond_new(void) +{ + GCond *cond = g_new(GCond, 1); + InitializeConditionVariable(cond); + return cond; +} + +/* Thread private data */ +typedef DWORD GStaticPrivate; +#define g_static_private_init(_priv) do { *_priv = TlsAlloc(); } while (0) +#define g_static_private_get(_priv) TlsGetValue(*_priv) +#define g_static_private_set(_priv, _data, _) TlsSetValue(*_priv, _data) +#define g_static_private_free(_priv) TlsFree(*_priv) + +/* Atomic operations */ +#define g_atomic_int_inc(_pi) InterlockedIncrement(_pi) +#define g_atomic_int_get(_pi) (MemoryBarrier(), *_pi) +#define g_atomic_int_set(_pi, _val) do { MemoryBarrier(); *_pi = _val; } while (0) +#define g_atomic_int_dec_and_test(_pi) (InterlockedDecrement(_pi) == 0) +#define g_atomic_int_compare_and_exchange(_pi, _old, _new) (InterlockedCompareExchange(_pi, _new, _old) == _old) +#define g_atomic_int_exchange_and_add(_pi, _add) InterlockedExchangeAdd(_pi, _add) + +#endif + +#endif diff --git a/thirdparty/game-music-emu/CMakeLists.txt b/thirdparty/game-music-emu/CMakeLists.txt new file mode 100644 index 000000000..b164481f3 --- /dev/null +++ b/thirdparty/game-music-emu/CMakeLists.txt @@ -0,0 +1,133 @@ +cmake_minimum_required(VERSION 2.8...3.19) + +# CMake project definition file. +project(libgme) + +include (CheckCXXCompilerFlag) + +# When version is changed, also change the one in gme/gme.h to match +set(GME_VERSION 0.6.2 CACHE INTERNAL "libgme Version") + +if(COMPILER_IS_GNUCXX_COMPATIBLE) + add_compile_options(-Wall -Wextra) + if(NOT PROFILE) + add_compile_options(-fomit-frame-pointer) + endif() + check_cxx_compiler_flag(-Wno-array-bounds HAVE_NO_ARRAY_BOUNDS) + if(HAVE_NO_ARRAY_BOUNDS) + add_compile_options(-Wno-array-bounds) + endif() +endif() + +#[ZMusic] Disable most of bogus and annoying MSVC warnings +if(MSVC) + add_compile_options(/wd4101 /wd4800 /wd4702 /wd4706 /wd4805 /wd4310 /wd4244 /wd4456 /wd4459 /wd4146 /wd4127 /wd4458 /wd4267 /wd4804) +endif() + +# Default emulators to build (all of them! ;) +# [ZMusic] No options, enable all of them by default. + +#if (NOT DEFINED USE_GME_AY) + SET(USE_GME_AY 1 CACHE BOOL "Enable support for Spectrum ZX music emulation") +#endif() + +#if (NOT DEFINED USE_GME_GBS) + SET(USE_GME_GBS 1 CACHE BOOL "Enable support for Game Boy music emulation") +#endif() + +#if (NOT DEFINED USE_GME_GYM) + SET(USE_GME_GYM 1 CACHE BOOL "Enable Sega MegaDrive/Genesis music emulation") +#endif() + +#if (NOT DEFINED USE_GME_HES) + SET(USE_GME_HES 1 CACHE BOOL "Enable PC Engine/TurboGrafx-16 music emulation") +#endif() + +#if (NOT DEFINED USE_GME_KSS) + SET(USE_GME_KSS 1 CACHE BOOL "Enable MSX or other Z80 systems music emulation") +#endif() + +#if (NOT DEFINED USE_GME_NSF) + SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation") +#endif() + +#if (NOT DEFINED USE_GME_NSFE) + SET(USE_GME_NSFE 1 CACHE BOOL "Enable NES NSFE and NSF music emulation") +#endif() + +#if (NOT DEFINED USE_GME_SAP) + SET(USE_GME_SAP 1 CACHE BOOL "Enable Atari SAP music emulation") +#endif() + +#if (NOT DEFINED USE_GME_SPC) + SET(USE_GME_SPC 1 CACHE BOOL "Enable SNES SPC music emulation") +#endif() + +#if (NOT DEFINED USE_GME_VGM) + SET(USE_GME_VGM 1 CACHE BOOL "Enable Sega VGM/VGZ music emulation") +#endif() + +#if (NOT DEFINED GME_YM2612_EMU) + SET(GME_YM2612_EMU "Nuked" CACHE STRING "Which YM2612 emulator to use: \"Nuked\" (LGPLv2.1+), \"MAME\" (GPLv2+), or \"GENS\" (LGPLv2.1+)") +#endif() + +#if (USE_GME_NSFE AND NOT USE_GME_NSF) + #MESSAGE(" -- NSFE support requires NSF, enabling NSF support. --") + SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE) +#endif() + +set(ENABLE_UBSAN OFF) + +# Check for GCC/Clang "visibility" support. +if (COMPILER_IS_GNUCXX_COMPATIBLE) + + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -W -Wextra") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + + # Assume we have visibility support on any compiler that supports C++11 + add_definitions (-DLIBGME_VISIBILITY) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden") + + # Try to protect against undefined behavior from signed integer overflow + # This has caused miscompilation of code already and there are other + # potential uses; see https://bitbucket.org/mpyne/game-music-emu/issues/18/ + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fwrapv") + + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if (NOT DEFINED LIBGME_SWITCH_FALLTHROUGH) + check_cxx_compiler_flag (-Wimplicit-fallthrough __LIBGME_SWITCH_FALLTHROUGH_WARNINGS) + set (LIBGME_SWITCH_FALLTHROUGH ${__LIBGME_SWITCH_FALLTHROUGH_WARNINGS} + CACHE BOOL "Set if the compiler will complain about implicit switch fallthrough" + ) + endif() + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override -Wno-unused-const-variable") + endif() + + if (ENABLE_UBSAN) + # GCC needs -static-libubsan + if (NOT BUILD_SHARED_LIBS AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -static-libubsan") + else() + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") + endif() + endif() +endif () + +if(LIBGME_SWITCH_FALLTHROUGH) + # Avoid warning spam about switch fallthroughs, which are numerous in + # the codebase. + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wimplicit-fallthrough=0") +endif() + +# Shared library defined here +add_subdirectory(gme) + +# EXCLUDE_FROM_ALL adds build rules but keeps it out of default build +# [ZMusic] Not needed. +if( FALSE ) +add_subdirectory(player EXCLUDE_FROM_ALL) +add_subdirectory(demo EXCLUDE_FROM_ALL) +endif() + +use_fast_math( gme ) diff --git a/thirdparty/game-music-emu/changes.txt b/thirdparty/game-music-emu/changes.txt new file mode 100644 index 000000000..034ba4821 --- /dev/null +++ b/thirdparty/game-music-emu/changes.txt @@ -0,0 +1,5 @@ +Game_Music_Emu Change Log +------------------------- + +Please see the git version history (e.g. git shortlog tags/0.6.0..tags/0.6.1) +for the accurate change log. diff --git a/thirdparty/game-music-emu/design.txt b/thirdparty/game-music-emu/design.txt new file mode 100644 index 000000000..d79c860f7 --- /dev/null +++ b/thirdparty/game-music-emu/design.txt @@ -0,0 +1,194 @@ +Game_Music_Emu 0.6.0 Design +--------------------------- +This might be slightly out-of-date at times, but will be a big help in +understanding the library implementation. + + +Architecture +------------ +The library is essentially a bunch of independent game music file +emulators unified with a common interface. + +Gme_File and Music_Emu provide a common interface to the emulators. The +virtual functions are protected rather than public to allow pre- and +post-processing of arguments and data in one place. This allows the +emulator classes to assume that everything is set up properly when +starting a track and playing samples. + +All file input is done with the Data_Reader interface. Many derived +classes are present, for the usual disk-based file and block of memory, +to specialized adaptors for things like reading a subset of data or +combining a block of memory with a Data_Reader to the remaining data. +This makes the library much more flexible with regard to the source of +game music file data. I still added a specialized load_mem() function to +have the emulator keep a pointer to data already read in memory, for +those formats whose files can be absolutely huge (GYM, some VGMs). This +is important if for some reason the caller must load the data ahead of +time, but doesn't want the emulator needlessly making a copy. + +Since silence checking and fading are relatively complex, they are kept +separate from basic file loading and track information, which are +handled in the base class Gme_File. My original intent was to use +Gme_File as the common base class for full emulators and track +information-only readers, but implementing the C interface was much +simpler if both derived from Music_Emu. User C++ code can still benefit +from static checking by using Gme_File where only track information will +be accessed. + +Each emulator generally has three components: main emulator, CPU +emulator, and sound chip emulator(s). Each component has minimal +coupling, so use in a full emulator or stand alone is fairly easy. This +modularity really helps reduce complexity. Blip_Buffer helps a lot with +simplifying the APU interfaces and implementation. + +The "classic" emulators derive from Classic_Emu, which handles +Blip_Buffer filling and multiple channels. It uses Multi_Buffer for +output, allowing you to derive a custom buffer that could output each +voice to a separate sound channel and do different processing on each. +At some point I'm going to implement a better Effects_Buffer that allows +individual control of every channel. + +In implementing the C interface, I wanted a way to specify an emulator +type that didn't require linking in all the emulators. For each emulator +type there is a global object with pointers to functions to create the +emulator or a track information reader. The emulator type is thus a +pointer to this, which conveniently allows for a NULL value. The user +referencing this emulator type object is what ultimately links the +emulator in (unless new Foo_Emu is used in C++, of course). This type +also serves as a useful substitute for RTTI on older C++ compilers. + +Addendum: I have since added gme_type_list(), which causes all listed +emulators to be linked in. To avoid this, I make the list itself +editable in blargg_config.h. Having a built-in list allows +gme_load_file() to take a path and give back an emulator with the file +loaded, which is extremely useful for new users. + + +Interface conventions +---------------------- +If a function retains a pointer to or replaces the value of an object +passed, it takes a pointer so that it will be clear in the caller's +source code that care is required. + +Multi-word names have an underscore '_' separator between individual +words. + +Functions are named with lowercase words. Functions which perform an +action with side-effects are named with a verb phrase (i.e. load, move, +run). Functions which return the value of a piece of state are named +using a noun phrase (i.e. loaded, moved, running). + +Classes are named with capitalized words. Only the first letter of an +acronym is capitalized. Class names are nouns, sometimes suggestive of +what they do (i.e. File_Scanner). + +Structure, enumeration, and typedefs to these and built-in types are +named using lowercase words with a _t suffix. + +Macros are named with all-uppercase words. + +Internal names which can't be hidden due to technical reasons have an +underscore '_' suffix. + + +Managing Complexity +------------------- +Complexity has been a factor in most library decisions. Many features +have been passed by due to the complexity they would add. Once +complexity goes past a certain level, it mentally grasping the library +in its entirety, at which point more defects will occur and be hard to +find. + +I chose 16-bit signed samples because it seems to be the most common +format. Supporting multiple formats would add too much complexity to be +worth it. Other formats can be obtained via conversion. + +I've kept interfaces fairly lean, leaving many possible features +untapped but easy to add if necessary. For example the classic emulators +could have volume and frequency equalization adjusted separately for +each channel, since they each have an associated Blip_Synth. + +Source files of 400 lines or less seem to be the best size to limit +complexity. In a few cases there is no reasonable way to split longer +files, or there is benefit from having the source together in one file. + + +Preventing Bugs +--------------- +I've done many things to reduce the opportunity for defects. A general +principle is to write code so that defects will be as visible as +possible. I've used several techniques to achieve this. + +I put assertions at key points where defects seem likely or where +corruption due to a defect is likely to be visible. I've also put +assertions where violations of the interface are likely. In emulators +where I am unsure of exact hardware operation in a particular case, I +output a debug-only message noting that this has occurred; many times I +haven't implemented a hardware feature because nothing uses it. I've +made code brittle where there is no clear reason flexibility; code +written to handle every possibility sacrifices quality and reliability +to handle vaguely defined situations. + + +Flexibility through indirection +------------------------------- +I've tried to allow the most flexibility of modules by using indirection +to allow extension by the user. This keeps each module simpler and more +focused on its unique task. + +The classic emulators use Multi_Buffer, which potentially allows a +separate Blip_Buffer for each channel. This keeps emulators free of +typical code to allow output in mono, stereo, panning, etc. + +All emulators use a reader object to access file data, allowing it to be +stored in a regular file, compressed archive, memory, or generated +on-the-fly. Again, the library can be kept free of the particulars of +file access and changes required to support new formats. + + +Emulators in general +-------------------- +When I wrote the first NES sound emulator, I stored most of the state in +an emulator-specific format, with significant redundancy. In the +register write function I decoded everything into named variables. I +became tired of the verbosity and wanted to more closely model the +hardware, so I moved to a style of storing the last written value to +each register, along with as little other state as possible, mostly the +internal hardware registers. While this involves slightly more +recalculation, in most cases the emulation code is of comparable size. +It also makes state save/restore (for use in a full emulator) much +simpler. Finally, it makes debugging easier since the hardware registers +used in emulation are obvious. + + +CPU Cores +--------- +I've spent lots of time coming up with techniques to optimize the CPU +cores. Some of the most important: execute multiple instructions during +an emulation call, keep state in local variables to allow register +assignment, optimize state representation for most common instructions, +defer status flag calculation until actually needed, read program code +directly without a call to the memory read function, always pre-fetch +the operand byte before decoding instruction, and emulate instructions +using common blocks of code. + +I've successfully used Nes_Cpu in a fairly complete NES emulator, and +I'd like to make all the CPU emulators suitable for use in emulators. It +seems a waste for them to be used only for the small amount of emulation +necessary for game music files. + +I debugged the CPU cores by writing a test shell that ran them in +parallel with other CPU cores and compared all memory accesses and +processor states at each step. This provided good value at little cost. + +The CPU mapping page size is adjustable to allow the best tradeoff +between memory/cache usage and handler granularity. The interface allows +code to be somewhat independent of the page size. + +I optimize program memory accesses to direct reads rather than calls to +the memory read function. My assumption is that it would be difficult to +get useful code out of hardware I/O addresses, so no software will +intentionally execute out of I/O space. Since the page size can be +changed easily, most program memory mapping schemes can be accommodated. +This greatly reduces memory access function calls. + diff --git a/thirdparty/game-music-emu/gme.txt b/thirdparty/game-music-emu/gme.txt new file mode 100644 index 000000000..5a7d2f560 --- /dev/null +++ b/thirdparty/game-music-emu/gme.txt @@ -0,0 +1,376 @@ +Game_Music_Emu 0.6.2 +-------------------- +Author : Shay Green +Maintainer : Michael Pyne +Website : https://bitbucket.org/mpyne/game-music-emu/ +Source : https://bitbucket.org/mpyne/game-music-emu/ +License : GNU Lesser General Public License (LGPL), see LICENSE.txt + +Contents +-------- +* Overview +* Error handling +* Emulator types +* M3U playlist support +* Information fields +* Track length +* Loading file data +* Sound parameters +* VGM/GYM YM2413 & YM2612 FM sound +* Modular construction +* Obscure features +* Solving problems +* Thanks + + +Overview +-------- +This library can open game music files, play tracks, and read game and +track information tags. To play a game music file, do the following: + +* Open the file with gme_open_file() +* Start a track with gme_start_track(); +* Generate samples as needed with gme_play() +* Play samples through speaker using your operating system +* Delete emulator when done with gme_delete() + +Your code must arrange for the generated samples to be played through +the computer's speaker using whatever method your operating system +requires. + +There are many additional features available; you can: + +* Determine of the type of a music file without opening it with +gme_identify_*() +* Load just the file's information tags with gme_info_only +* Load from a block of memory rather than a file with gme_load_data() +* Arrange for a fade-out at a particular time with gme_set_fade +* Find when a track has ended with gme_track_ended() +* Seek to a new time in the track with gme_seek() +* Load an extended m3u playlist with gme_load_m3u() +* Get a list of the voices (channels) and mute them individually with +gme_voice_names() and gme_mute_voice() +* Change the playback tempo without affecting pitch with gme_set_tempo() +* Adjust treble/bass equalization with gme_set_equalizer() +* Associate your own data with an emulator and later get it back with +gme_set_user_data() +* Register a function of yours to be called back when the emulator is +deleted with gme_set_user_cleanup() + +Refer to gme.h for a comprehensive summary of features. + + +Error handling +-------------- +Functions which can fail have a return type of gme_err_t, which is a +pointer to an error string (const char*). If a function is successful it +returns NULL. Errors that you can easily avoid are checked with debug +assertions; gme_err_t return values are only used for genuine run-time +errors that can't be easily predicted in advance (out of memory, I/O +errors, incompatible file data). Your code should check all error +values. + +When loading a music file in the wrong emulator or trying to load a +non-music file, gme_wrong_file_type is returned. You can check for this +error in C++ like this: + + gme_err_t err = gme_open_file( path, &emu ); + if ( err == gme_wrong_file_type ) + ... + +To check for minor problems, call gme_warning() to get a string +describing the last warning. Your player should allow the user some way +of knowing when this is the case, since these minor errors could affect +playback. Without this information the user can't solve problems as +well. When playing a track, gme_warning() returns minor playback-related +problems (major playback problems end the track immediately and set the +warning string). + + +Emulator types +-------------- +The library includes several game music emulators that each support a +different file type. Each is identified by a gme_type_t constant defined +in gme.h, for example gme_nsf_emu is for the NSF emulator. If you use +gme_open_file() or gme_open_data(), the library does the work of +determining the file type and creating an appropriate emulator. If you +want more control over this process, read on. + +There are two basic ways to identify a game music file's type: look at +its file extension, or read the header data. The library includes +functions to help with both methods. The first is preferable because it +is fast and the most common way to identify files. Sometimes the +extension is lost or wrong, so the header must be read. + +Use gme_identify_extension() to find the correct game music type based +on a filename. To identify a file based on its extension and header +contents, use gme_identify_file(). If you read the header data yourself, +use gme_identify_header(). + +If you want to remove support for some music types to reduce your +executable size, edit GME_TYPE_LIST in blargg_config.h. For example, to +support just NSF and GBS, use this: + + #define GME_TYPE_LIST \ + gme_nsf_type,\ + gme_gbs_type + + +M3U playlist support +-------------------- +The library supports playlists in an extended m3u format with +gme_load_m3u() to give track names and times to multi-song formats: AY, +GBS, HES, KSS, NSF, NSFE, and SAP. Some aspects of the file format +itself is not well-defined so some m3u files won't work properly +(particularly those provided with KSS files). Only m3u files referencing +a single file are supported; your code must handle m3u files covering +more than one game music file, though it can use the built-in m3u +parsing provided by the library. + + +Information fields +------------------ +Support is provided for the various text fields and length information +in a file with gme_track_info(). If you just need track information for +a file (for example, building a playlist), use gme_new_info() in place +of gme_new_emu(), load the file normally, then you can access the track +count and info, but nothing else. + + M3U VGM GYM SPC SAP NSFE NSF AY GBS HES KSS + ------------------------------------------------------- +Track Count | * * * * * * * * * + | +System | * * * * * * * * * * + | +Game | * * * * * * * + | +Song | * * * * * * * + | +Author | * * * * * * * * + | +Copyright | * * * * * * * * + | +Comment | * * * * + | +Dumper | * * * * + | +Length | * * * * * * + | +Intro Length| * * * + | +Loop Length | * * * + +As listed above, the HES and KSS file formats don't include a track +count, and tracks are often scattered over the 0-255 range, so an m3u +playlist for these is a must. + +Unavailable text fields are set to an empty string and times to -1. Your +code should be prepared for any combination of available and unavailable +fields, as a particular music file might not use all of the supported +fields listed above. + +Currently text fields are truncated to 255 characters. Obscure fields of +some formats are not currently decoded; contact me if you want one +added. + + +Track length +------------ +The library leaves it up to you as to when to stop playing a track. You +can ask for available length information and then tell the library what +time it should start fading the track with gme_set_fade(). By default it +also continually checks for 6 or more seconds of silence to mark the end +of a track. Here is a reasonable algorithm you can use to decide how +long to play a track: + +* If the track length is > 0, use it +* If the loop length > 0, play for intro + loop * 2 +* Otherwise, default to 2.5 minutes (150000 msec) + +If you want to play a track longer than normal, be sure the loop length +isn't zero. See Music_Player.cpp around line 145 for example code. + +By default, the library skips silence at the beginning of a track. It +also continually checks for the end of a non-looping track by watching +for 6 seconds of unbroken silence. When doing this is scans *ahead* by +several seconds so it can report the end of the track after only one +second of silence has actually played. This feature can be disabled with +gme_ignore_silence(). + + +Loading file data +----------------- +The library allows file data to be loaded in many different ways. All +load functions return an error which you should check. The following +examples assume these variables: + + Music_Emu* emu; + gme_err_t error; + +If you're letting the library determine a file's type, you can use +either gme_open_file() or gme_open_data(): + + error = gme_open_file( pathname, &emu ); + error = gme_open_data( pointer, size, &emu ); + +If you're manually determining file type and using used gme_new_emu() to +create an emulator, you can use the following methods of loading: + +* From a block of memory: + + error = gme_load_data( emu, pointer, size ); + +* Have library call your function to read data: + + gme_err_t my_read( void* my_data, void* out, long count ) + { + // code that reads 'count' bytes into 'out' buffer + // and return 0 if no error + } + + error = gme_load_custom( emu, my_read, file_size, my_data ); + + +Sound parameters +---------------- +All emulators support an arbitrary output sampling rate. A rate of 44100 +Hz should work well on most systems. Since band-limited synthesis is +used, a sampling rate above 48000 Hz is not necessary and will actually +reduce sound quality and performance. + +All emulators also support adjustable gain, mainly for the purpose of +getting consistent volume between different music formats and avoiding +excessive modulation. The gain can only be set *before* setting the +emulator's sampling rate, so it's not useful as a general volume +control. The default gains of emulators are set so that they give +generally similar volumes, though some soundtracks are significantly +louder or quieter than normal. + +Some emulators support adjustable treble and bass frequency equalization +(AY, GBS, HES, KSS, NSF, NSFE, SAP, VGM) using set_equalizer(). +Parameters are specified using gme_equalizer_t eq = { treble_dB, +bass_freq }. Treble_dB sets the treble level (in dB), where 0.0 dB gives +normal treble; -200.0 dB is quite muffled, and 5.0 dB emphasizes treble +for an extra crisp sound. Bass_freq sets the frequency where bass +response starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass, +and 15000 Hz removes all bass. For example, the following makes the +sound extra-crisp but lacking bass: + + gme_equalizer_t eq = { 5.0, 1000 }; + gme_set_equalizer( music_emu, &eq ); + +Each emulator's equalization defaults to approximate the particular +console's sound quality; this default can be determined by calling +equalizer() just after creating the emulator. The Music_Emu::tv_eq +profile gives sound as if coming from a TV speaker, and some emulators +include other profiles for different versions of the system. For +example, to use Famicom sound equalization with the NSF emulator, do the +following: + + music_emu->set_equalizer( Nsf_Emu::famicom_eq ); + + +VGM/GYM YM2413 & YM2612 FM sound +-------------------------------- +The library plays Sega Genesis/Mega Drive music using a YM2612 FM sound +chip emulator based on the Gens project. Because this has some +inaccuracies, other YM2612 emulators can be used in its place by +re-implementing the interface in YM2612_Emu.h. Available on my website +is a modified version of MAME's YM2612 emulator, which sounds better in +some ways and whose author is still making improvements. + +VGM music files using the YM2413 FM sound chip are also supported, but a +YM2413 emulator isn't included with the library due to technical +reasons. I have put one of the available YM2413 emulators on my website +that can be used directly. + + +Modular construction +-------------------- +The library is made of many fairly independent modules. If you're using +only one music file emulator, you can eliminate many of the library +sources from your program. Refer to the files list in readme.txt to get +a general idea of what can be removed, and be sure to edit GME_TYPE_LIST +(see "Emulator types" above). Post to the forum if you'd like me to put +together a smaller version for a particular use, as this only takes me a +few minutes to do. + +If you want to use one of the individual sound chip emulators (or CPU +cores) in your own console emulator, first check the libraries page on +my website since I have released several of them as stand alone +libraries with included documentation and examples on their use. If you +don't find it as a standalone library, contact me and I'll consider +separating it. + +The "classic" sound chips use my Blip_Buffer library, which greatly +simplifies their implementation and efficiently handles band-limited +synthesis. It is also available as a stand alone library with +documentation and many examples. + + +Obscure features +---------------- +The library's flexibility allows many possibilities. Contact me if you +want help implementing ideas or removing limitations. + +* Uses no global/static variables, allowing multiple instances of any +emulator. This is useful in a music player if you want to allow +simultaneous recording or scanning of other tracks while one is already +playing. This will also be useful if your platform disallows global +data. + +* Emulators that support a custom sound buffer can have *every* voice +routed to a different Blip_Buffer, allowing custom processing on each +voice. For example you could record a Game Boy track as a 4-channel +sound file. + +* Defining BLIP_BUFFER_FAST uses lower quality, less-multiply-intensive +synthesis on "classic" emulators, which might help on some really old +processors. This significantly lowers sound quality and prevents treble +equalization. Try this if your platform's processor isn't fast enough +for normal quality. Even on my ten-year-old 400 MHz Mac, this reduces +processor usage at most by about 0.6% (from 4% to 3.4%), hardly worth +the quality loss. + + +Solving problems +---------------- +If you're having problems, try the following: + +* If you're getting garbled sound, try this simple siren generator in +place of your call to play(). This will quickly tell whether the problem +is in the library or in your code. + + static void play_siren( long count, short* out ) + { + static double a, a2; + while ( count-- ) + *out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) ); + } + +* Enable debugging support in your environment. This enables assertions +and other run-time checks. + +* Turn the compiler's optimizer is off. Sometimes an optimizer generates +bad code. + +* If multiple threads are being used, ensure that only one at a time is +accessing a given set of objects from the library. This library is not +in general thread-safe, though independent objects can be used in +separate threads. + +* If all else fails, see if the demos work. + + +Thanks +------ +Big thanks to Chris Moeller (kode54) for help with library testing and +feedback, for maintaining the Foobar2000 plugin foo_gep based on it, and +for original work on openspc++ that was used when developing Spc_Emu. +Brad Martin's excellent OpenSPC SNES DSP emulator worked well from the +start. Also thanks to Richard Bannister, Mahendra Tallur, Shazz, +nenolod, theHobbit, Johan Samuelsson, and nes6502 for testing, using, +and giving feedback for the library in their respective game music +players. More recently, Lucas Paul and Michael Pyne have helped nudge the +library into a public repository and get its interface more stable for use +in shared libraries. diff --git a/thirdparty/game-music-emu/gme/Ay_Apu.cpp b/thirdparty/game-music-emu/gme/Ay_Apu.cpp new file mode 100644 index 000000000..d132c42f9 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ay_Apu.cpp @@ -0,0 +1,395 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Ay_Apu.h" + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Emulation inaccuracies: +// * Noise isn't run when not in use +// * Changes to envelope and noise periods are delayed until next reload +// * Super-sonic tone should attenuate output to about 60%, not 50% + +// Tones above this frequency are treated as disabled tone at half volume. +// Power of two is more efficient (avoids division). +unsigned const inaudible_freq = 16384; + +int const period_factor = 16; + +static byte const amp_table [16] = +{ +#define ENTRY( n ) byte (n * Ay_Apu::amp_range + 0.5) + // With channels tied together and 1K resistor to ground (as datasheet recommends), + // output nearly matches logarithmic curve as claimed. Approx. 1.5 dB per step. + ENTRY(0.000000),ENTRY(0.007813),ENTRY(0.011049),ENTRY(0.015625), + ENTRY(0.022097),ENTRY(0.031250),ENTRY(0.044194),ENTRY(0.062500), + ENTRY(0.088388),ENTRY(0.125000),ENTRY(0.176777),ENTRY(0.250000), + ENTRY(0.353553),ENTRY(0.500000),ENTRY(0.707107),ENTRY(1.000000), + + /* + // Measured from an AY-3-8910A chip with date code 8611. + + // Direct voltages without any load (very linear) + ENTRY(0.000000),ENTRY(0.046237),ENTRY(0.064516),ENTRY(0.089785), + ENTRY(0.124731),ENTRY(0.173118),ENTRY(0.225806),ENTRY(0.329032), + ENTRY(0.360215),ENTRY(0.494624),ENTRY(0.594624),ENTRY(0.672043), + ENTRY(0.766129),ENTRY(0.841935),ENTRY(0.926882),ENTRY(1.000000), + // With only some load + ENTRY(0.000000),ENTRY(0.011940),ENTRY(0.017413),ENTRY(0.024876), + ENTRY(0.036318),ENTRY(0.054229),ENTRY(0.072637),ENTRY(0.122388), + ENTRY(0.174129),ENTRY(0.239303),ENTRY(0.323881),ENTRY(0.410945), + ENTRY(0.527363),ENTRY(0.651741),ENTRY(0.832338),ENTRY(1.000000), + */ +#undef ENTRY +}; + +static byte const modes [8] = +{ +#define MODE( a0,a1, b0,b1, c0,c1 ) \ + (a0 | a1<<1 | b0<<2 | b1<<3 | c0<<4 | c1<<5) + MODE( 1,0, 1,0, 1,0 ), + MODE( 1,0, 0,0, 0,0 ), + MODE( 1,0, 0,1, 1,0 ), + MODE( 1,0, 1,1, 1,1 ), + MODE( 0,1, 0,1, 0,1 ), + MODE( 0,1, 1,1, 1,1 ), + MODE( 0,1, 1,0, 0,1 ), + MODE( 0,1, 0,0, 0,0 ), +}; + +Ay_Apu::Ay_Apu() +{ + // build full table of the upper 8 envelope waveforms + for ( int m = 8; m--; ) + { + byte* out = env.modes [m]; + int flags = modes [m]; + for ( int x = 3; --x >= 0; ) + { + int amp = flags & 1; + int end = flags >> 1 & 1; + int step = end - amp; + amp *= 15; + for ( int y = 16; --y >= 0; ) + { + *out++ = amp_table [amp]; + amp += step; + } + flags >>= 2; + } + } + + output( 0 ); + volume( 1.0 ); + reset(); +} + +void Ay_Apu::reset() +{ + last_time = 0; + noise.delay = 0; + noise.lfsr = 1; + + osc_t* osc = &oscs [osc_count]; + do + { + osc--; + osc->period = period_factor; + osc->delay = 0; + osc->last_amp = 0; + osc->phase = 0; + } + while ( osc != oscs ); + + for ( int i = sizeof regs; --i >= 0; ) + regs [i] = 0; + regs [7] = 0xFF; + write_data_( 13, 0 ); +} + +void Ay_Apu::write_data_( int addr, int data ) +{ + assert( (unsigned) addr < reg_count ); + + if ( (unsigned) addr >= 14 ) + { + #ifdef debug_printf + debug_printf( "Wrote to I/O port %02X\n", (int) addr ); + #endif + } + + // envelope mode + if ( addr == 13 ) + { + if ( !(data & 8) ) // convert modes 0-7 to proper equivalents + data = (data & 4) ? 15 : 9; + env.wave = env.modes [data - 7]; + env.pos = -48; + env.delay = 0; // will get set to envelope period in run_until() + } + regs [addr] = data; + + // handle period changes accurately + int i = addr >> 1; + if ( i < osc_count ) + { + blip_time_t period = (regs [i * 2 + 1] & 0x0F) * (0x100L * period_factor) + + regs [i * 2] * period_factor; + if ( !period ) + period = period_factor; + + // adjust time of next timer expiration based on change in period + osc_t& osc = oscs [i]; + if ( (osc.delay += period - osc.period) < 0 ) + osc.delay = 0; + osc.period = period; + } + + // TODO: same as above for envelope timer, and it also has a divide by two after it +} + +int const noise_off = 0x08; +int const tone_off = 0x01; + +void Ay_Apu::run_until( blip_time_t final_end_time ) +{ + require( final_end_time >= last_time ); + + // noise period and initial values + blip_time_t const noise_period_factor = period_factor * 2; // verified + blip_time_t noise_period = (regs [6] & 0x1F) * noise_period_factor; + if ( !noise_period ) + noise_period = noise_period_factor; + blip_time_t const old_noise_delay = noise.delay; + blargg_ulong const old_noise_lfsr = noise.lfsr; + + // envelope period + blip_time_t const env_period_factor = period_factor * 2; // verified + blip_time_t env_period = (regs [12] * 0x100L + regs [11]) * env_period_factor; + if ( !env_period ) + env_period = env_period_factor; // same as period 1 on my AY chip + if ( !env.delay ) + env.delay = env_period; + + // run each osc separately + for ( int index = 0; index < osc_count; index++ ) + { + osc_t* const osc = &oscs [index]; + int osc_mode = regs [7] >> index; + + // output + Blip_Buffer* const osc_output = osc->output; + if ( !osc_output ) + continue; + osc_output->set_modified(); + + // period + int half_vol = 0; + blip_time_t inaudible_period = (blargg_ulong) (osc_output->clock_rate() + + inaudible_freq) / (inaudible_freq * 2); + if ( osc->period <= inaudible_period && !(osc_mode & tone_off) ) + { + half_vol = 1; // Actually around 60%, but 50% is close enough + osc_mode |= tone_off; + } + + // envelope + blip_time_t start_time = last_time; + blip_time_t end_time = final_end_time; + int const vol_mode = regs [0x08 + index]; + int volume = amp_table [vol_mode & 0x0F] >> half_vol; + int osc_env_pos = env.pos; + if ( vol_mode & 0x10 ) + { + volume = env.wave [osc_env_pos] >> half_vol; + // use envelope only if it's a repeating wave or a ramp that hasn't finished + if ( !(regs [13] & 1) || osc_env_pos < -32 ) + { + end_time = start_time + env.delay; + if ( end_time >= final_end_time ) + end_time = final_end_time; + + //if ( !(regs [12] | regs [11]) ) + // debug_printf( "Used envelope period 0\n" ); + } + else if ( !volume ) + { + osc_mode = noise_off | tone_off; + } + } + else if ( !volume ) + { + osc_mode = noise_off | tone_off; + } + + // tone time + blip_time_t const period = osc->period; + blip_time_t time = start_time + osc->delay; + if ( osc_mode & tone_off ) // maintain tone's phase when off + { + blargg_long count = (final_end_time - time + period - 1) / period; + time += count * period; + osc->phase ^= count & 1; + } + + // noise time + blip_time_t ntime = final_end_time; + blargg_ulong noise_lfsr = 1; + if ( !(osc_mode & noise_off) ) + { + ntime = start_time + old_noise_delay; + noise_lfsr = old_noise_lfsr; + //if ( (regs [6] & 0x1F) == 0 ) + // debug_printf( "Used noise period 0\n" ); + } + + // The following efficiently handles several cases (least demanding first): + // * Tone, noise, and envelope disabled, where channel acts as 4-bit DAC + // * Just tone or just noise, envelope disabled + // * Envelope controlling tone and/or noise + // * Tone and noise disabled, envelope enabled with high frequency + // * Tone and noise together + // * Tone and noise together with envelope + + // This loop only runs one iteration if envelope is disabled. If envelope + // is being used as a waveform (tone and noise disabled), this loop will + // still be reasonably efficient since the bulk of it will be skipped. + while ( 1 ) + { + // current amplitude + int amp = 0; + if ( (osc_mode | osc->phase) & 1 & (osc_mode >> 3 | noise_lfsr) ) + amp = volume; + { + int delta = amp - osc->last_amp; + if ( delta ) + { + osc->last_amp = amp; + synth_.offset( start_time, delta, osc_output ); + } + } + + // Run wave and noise interleved with each catching up to the other. + // If one or both are disabled, their "current time" will be past end time, + // so there will be no significant performance hit. + if ( ntime < end_time || time < end_time ) + { + // Since amplitude was updated above, delta will always be +/- volume, + // so we can avoid using last_amp every time to calculate the delta. + int delta = amp * 2 - volume; + int delta_non_zero = delta != 0; + int phase = osc->phase | (osc_mode & tone_off); assert( tone_off == 0x01 ); + do + { + // run noise + blip_time_t end = end_time; + if ( end_time > time ) end = time; + if ( phase & delta_non_zero ) + { + while ( ntime <= end ) // must advance *past* time to avoid hang + { + int changed = noise_lfsr + 1; + noise_lfsr = (-(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1); + if ( changed & 2 ) + { + delta = -delta; + synth_.offset( ntime, delta, osc_output ); + } + ntime += noise_period; + } + } + else + { + // 20 or more noise periods on average for some music + blargg_long remain = end - ntime; + blargg_long count = remain / noise_period; + if ( remain >= 0 ) + ntime += noise_period + count * noise_period; + } + + // run tone + end = end_time; + if ( end_time > ntime ) end = ntime; + if ( noise_lfsr & delta_non_zero ) + { + while ( time < end ) + { + delta = -delta; + synth_.offset( time, delta, osc_output ); + time += period; + //phase ^= 1; + } + //assert( phase == (delta > 0) ); + phase = unsigned (-delta) >> (CHAR_BIT * sizeof (unsigned) - 1); + // (delta > 0) + } + else + { + // loop usually runs less than once + //SUB_CASE_COUNTER( (time < end) * (end - time + period - 1) / period ); + + while ( time < end ) + { + time += period; + phase ^= 1; + } + } + } + while ( time < end_time || ntime < end_time ); + + osc->last_amp = (delta + volume) >> 1; + if ( !(osc_mode & tone_off) ) + osc->phase = phase; + } + + if ( end_time >= final_end_time ) + break; // breaks first time when envelope is disabled + + // next envelope step + if ( ++osc_env_pos >= 0 ) + osc_env_pos -= 32; + volume = env.wave [osc_env_pos] >> half_vol; + + start_time = end_time; + end_time += env_period; + if ( end_time > final_end_time ) + end_time = final_end_time; + } + osc->delay = time - final_end_time; + + if ( !(osc_mode & noise_off) ) + { + noise.delay = ntime - final_end_time; + noise.lfsr = noise_lfsr; + } + } + + // TODO: optimized saw wave envelope? + + // maintain envelope phase + blip_time_t remain = final_end_time - last_time - env.delay; + if ( remain >= 0 ) + { + blargg_long count = (remain + env_period) / env_period; + env.pos += count; + if ( env.pos >= 0 ) + env.pos = (env.pos & 31) - 32; + remain -= count * env_period; + assert( -remain <= env_period ); + } + env.delay = -remain; + assert( env.delay > 0 ); + assert( env.pos < 0 ); + + last_time = final_end_time; +} diff --git a/thirdparty/game-music-emu/gme/Ay_Apu.h b/thirdparty/game-music-emu/gme/Ay_Apu.h new file mode 100644 index 000000000..ad2d83692 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ay_Apu.h @@ -0,0 +1,106 @@ +// AY-3-8910 sound chip emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef AY_APU_H +#define AY_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Ay_Apu { +public: + // Set buffer to generate all sound into, or disable sound if NULL + void output( Blip_Buffer* ); + + // Reset sound chip + void reset(); + + // Write to register at specified time + enum { reg_count = 16 }; + void write( blip_time_t time, int addr, int data ); + + // Run sound to specified time, end current time frame, then start a new + // time frame at time 0. Time frames have no effect on emulation and each + // can be whatever length is convenient. + void end_frame( blip_time_t length ); + +// Additional features + + // Set sound output of specific oscillator to buffer, where index is + // 0, 1, or 2. If buffer is NULL, the specified oscillator is muted. + enum { osc_count = 3 }; + void osc_output( int index, Blip_Buffer* ); + + // Set overall volume (default is 1.0) + void volume( double ); + + // Set treble equalization (see documentation) + void treble_eq( blip_eq_t const& ); + +public: + Ay_Apu(); + typedef unsigned char byte; +private: + struct osc_t + { + blip_time_t period; + blip_time_t delay; + short last_amp; + short phase; + Blip_Buffer* output; + } oscs [osc_count]; + blip_time_t last_time; + byte regs [reg_count]; + + struct { + blip_time_t delay; + blargg_ulong lfsr; + } noise; + + struct { + blip_time_t delay; + byte const* wave; + int pos; + byte modes [8] [48]; // values already passed through volume table + } env; + + void run_until( blip_time_t ); + void write_data_( int addr, int data ); +public: + enum { amp_range = 255 }; + Blip_Synth synth_; +}; + +inline void Ay_Apu::volume( double v ) { synth_.volume( 0.7 / osc_count / amp_range * v ); } + +inline void Ay_Apu::treble_eq( blip_eq_t const& eq ) { synth_.treble_eq( eq ); } + +inline void Ay_Apu::write( blip_time_t time, int addr, int data ) +{ + run_until( time ); + write_data_( addr, data ); +} + +inline void Ay_Apu::osc_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Ay_Apu::output( Blip_Buffer* buf ) +{ + osc_output( 0, buf ); + osc_output( 1, buf ); + osc_output( 2, buf ); +} + +inline void Ay_Apu::end_frame( blip_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Ay_Cpu.cpp b/thirdparty/game-music-emu/gme/Ay_Cpu.cpp new file mode 100644 index 000000000..31c912568 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ay_Cpu.cpp @@ -0,0 +1,1659 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +/* +Last validated with zexall 2006.11.21 5:26 PM +* Doesn't implement the R register or immediate interrupt after EI. +* Address wrap-around isn't completely correct, but is prevented from crashing emulator. +*/ + +#include "Ay_Cpu.h" + +#include "blargg_endian.h" +#include + +//#include "z80_cpu_log.h" + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#define SYNC_TIME() (void) (s.time = s_time) +#define RELOAD_TIME() (void) (s_time = s.time) + +// Callbacks to emulator + +#define CPU_OUT( cpu, addr, data, TIME )\ + ay_cpu_out( cpu, TIME, addr, data ) + +#define CPU_IN( cpu, addr, TIME )\ + ay_cpu_in( cpu, addr ) + +#include "blargg_source.h" + +// flags, named with hex value for clarity +int const S80 = 0x80; +int const Z40 = 0x40; +int const F20 = 0x20; +int const H10 = 0x10; +int const F08 = 0x08; +int const V04 = 0x04; +int const P04 = 0x04; +int const N02 = 0x02; +int const C01 = 0x01; + +#define SZ28P( n ) szpc [n] +#define SZ28PC( n ) szpc [n] +#define SZ28C( n ) (szpc [n] & ~P04) +#define SZ28( n ) SZ28C( n ) + +#define SET_R( n ) (void) (r.r = n) +#define GET_R() (r.r) + +Ay_Cpu::Ay_Cpu() +{ + state = &state_; + for ( int i = 0x100; --i >= 0; ) + { + int even = 1; + for ( int p = i; p; p >>= 1 ) + even ^= p; + int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04); + szpc [i] = n; + szpc [i + 0x100] = n | C01; + } + szpc [0x000] |= Z40; + szpc [0x100] |= Z40; +} + +void Ay_Cpu::reset( void* m ) +{ + mem = (uint8_t*) m; + + check( state == &state_ ); + state = &state_; + state_.time = 0; + state_.base = 0; + end_time_ = 0; + + memset( &r, 0, sizeof r ); +} + +#define TIME (s_time + s.base) +#define READ_PROG( addr ) (mem [addr]) +#define INSTR( offset ) READ_PROG( pc + (offset) ) +#define GET_ADDR() GET_LE16( &READ_PROG( pc ) ) +#define READ( addr ) READ_PROG( addr ) +#define WRITE( addr, data ) (void) (READ_PROG( addr ) = data) +#define READ_WORD( addr ) GET_LE16( &READ_PROG( addr ) ) +#define WRITE_WORD( addr, data ) SET_LE16( &READ_PROG( addr ), data ) +#define IN( addr ) CPU_IN( this, addr, TIME ) +#define OUT( addr, data ) CPU_OUT( this, addr, data, TIME ) + +#if BLARGG_BIG_ENDIAN + #define R8( n, offset ) ((r8_ - offset) [n]) +#elif BLARGG_LITTLE_ENDIAN + #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) +#else + #error "Byte order of CPU must be known" +#endif + +//#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)]) + +// help compiler see that it can just adjust stack offset, saving an extra instruction +#define R16( n, shift, offset )\ + (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) + +#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e +#define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f +#define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g +#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h + +// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8 +static byte const ed_dd_timing [0x100] = { +//0 1 2 3 4 5 6 7 8 9 A B C D E F +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00, +0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0, +0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, +0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00, +0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +bool Ay_Cpu::run( cpu_time_t end_time ) +{ + set_end_time( end_time ); + state_t s = this->state_; + this->state = &s; + bool warning = false; + + union { + regs_t rg; + pairs_t rp; + uint8_t r8_ [8]; // indexed + uint16_t r16_ [4]; + }; + rg = this->r.b; + + cpu_time_t s_time = s.time; + uint8_t* const mem = this->mem; // cache + uint16_t pc = r.pc; + uint16_t sp = r.sp; + uint16_t ix = r.ix; // TODO: keep in memory for direct access? + uint16_t iy = r.iy; + int flags = r.b.flags; + + goto loop; +jr_not_taken: + s_time -= 5; + goto loop; +call_not_taken: + s_time -= 7; +jp_not_taken: + pc += 2; +loop: + + check( (unsigned long) pc < 0x10000 ); + check( (unsigned long) sp < 0x10000 ); + check( (unsigned) flags < 0x100 ); + check( (unsigned) ix < 0x10000 ); + check( (unsigned) iy < 0x10000 ); + + uint8_t opcode; + opcode = READ_PROG( pc ); + pc++; + + static byte const base_timing [0x100] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0 + 13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1 + 12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2 + 12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6 + 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B + 11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C + 11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D + 11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E + 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F + }; + + uint16_t data; + data = base_timing [opcode]; + if ( (s_time += data) >= 0 ) + goto possibly_out_of_time; +almost_out_of_time: + + data = READ_PROG( pc ); + + #ifdef Z80_CPU_LOG_H + //log_opcode( opcode, READ_PROG( pc ) ); + z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy ); + z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ), + READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) ); + #endif + + switch ( opcode ) + { +possibly_out_of_time: + if ( s_time < (int) data ) + goto almost_out_of_time; + s_time -= data; + goto out_of_time; + +// Common + + case 0x00: // NOP + CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc. + goto loop; + + case 0x08:{// EX AF,AF' + int temp = r.alt.b.a; + r.alt.b.a = rg.a; + rg.a = temp; + + temp = r.alt.b.flags; + r.alt.b.flags = flags; + flags = temp; + goto loop; + } + + case 0xD3: // OUT (imm),A + pc++; + OUT( data + rg.a * 0x100, rg.a ); + goto loop; + + case 0x2E: // LD L,imm + pc++; + rg.l = data; + goto loop; + + case 0x3E: // LD A,imm + pc++; + rg.a = data; + goto loop; + + case 0x3A:{// LD A,(addr) + uint16_t addr = GET_ADDR(); + pc += 2; + rg.a = READ( addr ); + goto loop; + } + +// Conditional + +#define ZERO (flags & Z40) +#define CARRY (flags & C01) +#define EVEN (flags & P04) +#define MINUS (flags & S80) + +// JR +#define JR( cond ) {\ + int disp = (int8_t) data;\ + pc++;\ + if ( !(cond) )\ + goto jr_not_taken;\ + pc += disp;\ + goto loop;\ +} + + case 0x20: JR( !ZERO ) // JR NZ,disp + case 0x28: JR( ZERO ) // JR Z,disp + case 0x30: JR( !CARRY ) // JR NC,disp + case 0x38: JR( CARRY ) // JR C,disp + case 0x18: JR( true ) // JR disp + + case 0x10:{// DJNZ disp + int temp = rg.b - 1; + rg.b = temp; + JR( temp ) + } + +// JP +#define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop; + + case 0xC2: JP( !ZERO ) // JP NZ,addr + case 0xCA: JP( ZERO ) // JP Z,addr + case 0xD2: JP( !CARRY ) // JP NC,addr + case 0xDA: JP( CARRY ) // JP C,addr + case 0xE2: JP( !EVEN ) // JP PO,addr + case 0xEA: JP( EVEN ) // JP PE,addr + case 0xF2: JP( !MINUS ) // JP P,addr + case 0xFA: JP( MINUS ) // JP M,addr + + case 0xC3: // JP addr + pc = GET_ADDR(); + goto loop; + + case 0xE9: // JP HL + pc = rp.hl; + goto loop; + +// RET +#define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop; + + case 0xC0: RET( !ZERO ) // RET NZ + case 0xC8: RET( ZERO ) // RET Z + case 0xD0: RET( !CARRY ) // RET NC + case 0xD8: RET( CARRY ) // RET C + case 0xE0: RET( !EVEN ) // RET PO + case 0xE8: RET( EVEN ) // RET PE + case 0xF0: RET( !MINUS ) // RET P + case 0xF8: RET( MINUS ) // RET M + + case 0xC9: // RET + ret_taken: + pc = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto loop; + +// CALL +#define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken; + + case 0xC4: CALL( !ZERO ) // CALL NZ,addr + case 0xCC: CALL( ZERO ) // CALL Z,addr + case 0xD4: CALL( !CARRY ) // CALL NC,addr + case 0xDC: CALL( CARRY ) // CALL C,addr + case 0xE4: CALL( !EVEN ) // CALL PO,addr + case 0xEC: CALL( EVEN ) // CALL PE,addr + case 0xF4: CALL( !MINUS ) // CALL P,addr + case 0xFC: CALL( MINUS ) // CALL M,addr + + case 0xCD:{// CALL addr + call_taken: + uint16_t addr = pc + 2; + pc = GET_ADDR(); + sp = uint16_t (sp - 2); + WRITE_WORD( sp, addr ); + goto loop; + } + + case 0xFF: // RST + if ( (pc - 1) > 0xFFFF ) + { + pc = uint16_t (pc - 1); + s_time -= 11; + goto loop; + } + CASE7( C7, CF, D7, DF, E7, EF, F7 ): + data = pc; + pc = opcode & 0x38; + goto push_data; + +// PUSH/POP + case 0xF5: // PUSH AF + data = rg.a * 0x100u + flags; + goto push_data; + + case 0xC5: // PUSH BC + case 0xD5: // PUSH DE + case 0xE5: // PUSH HL + data = R16( opcode, 4, 0xC5 ); + push_data: + sp = uint16_t (sp - 2); + WRITE_WORD( sp, data ); + goto loop; + + case 0xF1: // POP AF + flags = READ( sp ); + rg.a = READ( sp + 1 ); + sp = uint16_t (sp + 2); + goto loop; + + case 0xC1: // POP BC + case 0xD1: // POP DE + case 0xE1: // POP HL + R16( opcode, 4, 0xC1 ) = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto loop; + +// ADC/ADD/SBC/SUB + case 0x96: // SUB (HL) + case 0x86: // ADD (HL) + flags &= ~C01; + case 0x9E: // SBC (HL) + case 0x8E: // ADC (HL) + data = READ( rp.hl ); + goto adc_data; + + case 0xD6: // SUB A,imm + case 0xC6: // ADD imm + flags &= ~C01; + case 0xDE: // SBC A,imm + case 0xCE: // ADC imm + pc++; + goto adc_data; + + CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r + CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r + flags &= ~C01; + CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r + CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r + data = R8( opcode & 7, 0 ); + adc_data: { + int result = data + (flags & C01); + data ^= rg.a; + flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes + if ( flags ) + result = -result; + result += rg.a; + data ^= result; + flags |=(data & H10) | + ((data - -0x80) >> 6 & V04) | + SZ28C( result & 0x1FF ); + rg.a = result; + goto loop; + } + +// CP + case 0xBE: // CP (HL) + data = READ( rp.hl ); + goto cp_data; + + case 0xFE: // CP imm + pc++; + goto cp_data; + + CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r + data = R8( opcode, 0xB8 ); + cp_data: { + int result = rg.a - data; + flags = N02 | (data & (F20 | F08)) | (result >> 8 & C01); + data ^= rg.a; + flags |=(((result ^ rg.a) & data) >> 5 & V04) | + (((data & H10) ^ result) & (S80 | H10)); + if ( (uint8_t) result ) + goto loop; + flags |= Z40; + goto loop; + } + +// ADD HL,rp + + case 0x39: // ADD HL,SP + data = sp; + goto add_hl_data; + + case 0x09: // ADD HL,BC + case 0x19: // ADD HL,DE + case 0x29: // ADD HL,HL + data = R16( opcode, 4, 0x09 ); + add_hl_data: { + blargg_ulong sum = rp.hl + data; + data ^= rp.hl; + rp.hl = sum; + flags = (flags & (S80 | Z40 | V04)) | + (sum >> 16) | + (sum >> 8 & (F20 | F08)) | + ((data ^ sum) >> 8 & H10); + goto loop; + } + + case 0x27:{// DAA + int a = rg.a; + if ( a > 0x99 ) + flags |= C01; + + int adjust = 0x60 & -(flags & C01); + + if ( flags & H10 || (a & 0x0F) > 9 ) + adjust |= 0x06; + + if ( flags & N02 ) + adjust = -adjust; + a += adjust; + + flags = (flags & (C01 | N02)) | + ((rg.a ^ a) & H10) | + SZ28P( (uint8_t) a ); + rg.a = a; + goto loop; + } + /* + case 0x27:{// DAA + // more optimized, but probably not worth the obscurity + int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags + int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0 + + if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9 + adjust |= 0x06; + + if ( f & N02 ) + adjust = -adjust; + int a = rg.a + adjust; + + flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); + rg.a = a; + goto loop; + } + */ + +// INC/DEC + case 0x34: // INC (HL) + data = READ( rp.hl ) + 1; + WRITE( rp.hl, data ); + goto inc_set_flags; + + CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r + data = ++R8( opcode >> 3, 0 ); + inc_set_flags: + flags = (flags & C01) | + (((data & 0x0F) - 1) & H10) | + SZ28( (uint8_t) data ); + if ( data != 0x80 ) + goto loop; + flags |= V04; + goto loop; + + case 0x35: // DEC (HL) + data = READ( rp.hl ) - 1; + WRITE( rp.hl, data ); + goto dec_set_flags; + + CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r + data = --R8( opcode >> 3, 0 ); + dec_set_flags: + flags = (flags & C01) | N02 | + (((data & 0x0F) + 1) & H10) | + SZ28( (uint8_t) data ); + if ( data != 0x7F ) + goto loop; + flags |= V04; + goto loop; + + case 0x03: // INC BC + case 0x13: // INC DE + case 0x23: // INC HL + R16( opcode, 4, 0x03 )++; + goto loop; + + case 0x33: // INC SP + sp = uint16_t (sp + 1); + goto loop; + + case 0x0B: // DEC BC + case 0x1B: // DEC DE + case 0x2B: // DEC HL + R16( opcode, 4, 0x0B )--; + goto loop; + + case 0x3B: // DEC SP + sp = uint16_t (sp - 1); + goto loop; + +// AND + case 0xA6: // AND (HL) + data = READ( rp.hl ); + goto and_data; + + case 0xE6: // AND imm + pc++; + goto and_data; + + CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r + data = R8( opcode, 0xA0 ); + and_data: + rg.a &= data; + flags = SZ28P( rg.a ) | H10; + goto loop; + +// OR + case 0xB6: // OR (HL) + data = READ( rp.hl ); + goto or_data; + + case 0xF6: // OR imm + pc++; + goto or_data; + + CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r + data = R8( opcode, 0xB0 ); + or_data: + rg.a |= data; + flags = SZ28P( rg.a ); + goto loop; + +// XOR + case 0xAE: // XOR (HL) + data = READ( rp.hl ); + goto xor_data; + + case 0xEE: // XOR imm + pc++; + goto xor_data; + + CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r + data = R8( opcode, 0xA8 ); + xor_data: + rg.a ^= data; + flags = SZ28P( rg.a ); + goto loop; + +// LD + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r + WRITE( rp.hl, R8( opcode, 0x70 ) ); + goto loop; + + CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r + CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r + CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r + CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r + CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r + CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r + CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r + R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 ); + goto loop; + + CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm + R8( opcode >> 3, 0 ) = data; + pc++; + goto loop; + + case 0x36: // LD (HL),imm + pc++; + WRITE( rp.hl, data ); + goto loop; + + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL) + R8( opcode >> 3, 8 ) = READ( rp.hl ); + goto loop; + + case 0x01: // LD rp,imm + case 0x11: + case 0x21: + R16( opcode, 4, 0x01 ) = GET_ADDR(); + pc += 2; + goto loop; + + case 0x31: // LD sp,imm + sp = GET_ADDR(); + pc += 2; + goto loop; + + case 0x2A:{// LD HL,(addr) + uint16_t addr = GET_ADDR(); + pc += 2; + rp.hl = READ_WORD( addr ); + goto loop; + } + + case 0x32:{// LD (addr),A + uint16_t addr = GET_ADDR(); + pc += 2; + WRITE( addr, rg.a ); + goto loop; + } + + case 0x22:{// LD (addr),HL + uint16_t addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, rp.hl ); + goto loop; + } + + case 0x02: // LD (BC),A + case 0x12: // LD (DE),A + WRITE( R16( opcode, 4, 0x02 ), rg.a ); + goto loop; + + case 0x0A: // LD A,(BC) + case 0x1A: // LD A,(DE) + rg.a = READ( R16( opcode, 4, 0x0A ) ); + goto loop; + + case 0xF9: // LD SP,HL + sp = rp.hl; + goto loop; + +// Rotate + + case 0x07:{// RLCA + uint16_t temp = rg.a; + temp = (temp << 1) | (temp >> 7); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08 | C01)); + rg.a = temp; + goto loop; + } + + case 0x0F:{// RRCA + uint16_t temp = rg.a; + flags = (flags & (S80 | Z40 | P04)) | + (temp & C01); + temp = (temp << 7) | (temp >> 1); + flags |= temp & (F20 | F08); + rg.a = temp; + goto loop; + } + + case 0x17:{// RLA + blargg_ulong temp = (rg.a << 1) | (flags & C01); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08)) | + (temp >> 8); + rg.a = temp; + goto loop; + } + + case 0x1F:{// RRA + uint16_t temp = (flags << 7) | (rg.a >> 1); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08)) | + (rg.a & C01); + rg.a = temp; + goto loop; + } + +// Misc + case 0x2F:{// CPL + uint16_t temp = ~rg.a; + flags = (flags & (S80 | Z40 | P04 | C01)) | + (temp & (F20 | F08)) | + (H10 | N02); + rg.a = temp; + goto loop; + } + + case 0x3F:{// CCF + flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) | + (flags << 4 & H10) | + (rg.a & (F20 | F08)); + goto loop; + } + + case 0x37: // SCF + flags = (flags & (S80 | Z40 | P04)) | C01 | + (rg.a & (F20 | F08)); + goto loop; + + case 0xDB: // IN A,(imm) + pc++; + rg.a = IN( data + rg.a * 0x100 ); + goto loop; + + case 0xE3:{// EX (SP),HL + uint16_t temp = READ_WORD( sp ); + WRITE_WORD( sp, rp.hl ); + rp.hl = temp; + goto loop; + } + + case 0xEB:{// EX DE,HL + uint16_t temp = rp.hl; + rp.hl = rp.de; + rp.de = temp; + goto loop; + } + + case 0xD9:{// EXX DE,HL + uint16_t temp = r.alt.w.bc; + r.alt.w.bc = rp.bc; + rp.bc = temp; + + temp = r.alt.w.de; + r.alt.w.de = rp.de; + rp.de = temp; + + temp = r.alt.w.hl; + r.alt.w.hl = rp.hl; + rp.hl = temp; + goto loop; + } + + case 0xF3: // DI + r.iff1 = 0; + r.iff2 = 0; + goto loop; + + case 0xFB: // EI + r.iff1 = 1; + r.iff2 = 1; + // TODO: delayed effect + goto loop; + + case 0x76: // HALT + goto halt; + +//////////////////////////////////////// CB prefix + { + case 0xCB: + unsigned data2; + data2 = INSTR( 1 ); + (void) data2; // TODO is this the same as data in all cases? + pc++; + switch ( data ) + { + + // Rotate left + + #define RLC( read, write ) {\ + uint8_t result = read;\ + result = uint8_t (result << 1) | (result >> 7);\ + flags = SZ28P( result ) | (result & C01);\ + write;\ + goto loop;\ + } + + case 0x06: // RLC (HL) + s_time += 7; + data = rp.hl; + rlc_data_addr: + RLC( READ( data ), WRITE( data, result ) ) + + CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r + uint8_t& reg = R8( data, 0 ); + RLC( reg, reg = result ) + } + + #define RL( read, write ) {\ + uint16_t result = (read << 1) | (flags & C01);\ + flags = SZ28PC( result );\ + write;\ + goto loop;\ + } + + case 0x16: // RL (HL) + s_time += 7; + data = rp.hl; + rl_data_addr: + RL( READ( data ), WRITE( data, result ) ) + + CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r + uint8_t& reg = R8( data, 0x10 ); + RL( reg, reg = result ) + } + + #define SLA( read, add, write ) {\ + uint16_t result = (read << 1) | add;\ + flags = SZ28PC( result );\ + write;\ + goto loop;\ + } + + case 0x26: // SLA (HL) + s_time += 7; + data = rp.hl; + sla_data_addr: + SLA( READ( data ), 0, WRITE( data, result ) ) + + CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r + uint8_t& reg = R8( data, 0x20 ); + SLA( reg, 0, reg = result ) + } + + case 0x36: // SLL (HL) + s_time += 7; + data = rp.hl; + sll_data_addr: + SLA( READ( data ), 1, WRITE( data, result ) ) + + CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r + uint8_t& reg = R8( data, 0x30 ); + SLA( reg, 1, reg = result ) + } + + // Rotate right + + #define RRC( read, write ) {\ + uint8_t result = read;\ + flags = result & C01;\ + result = uint8_t (result << 7) | (result >> 1);\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x0E: // RRC (HL) + s_time += 7; + data = rp.hl; + rrc_data_addr: + RRC( READ( data ), WRITE( data, result ) ) + + CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r + uint8_t& reg = R8( data, 0x08 ); + RRC( reg, reg = result ) + } + + #define RR( read, write ) {\ + uint8_t result = read;\ + uint8_t temp = result & C01;\ + result = uint8_t (flags << 7) | (result >> 1);\ + flags = SZ28P( result ) | temp;\ + write;\ + goto loop;\ + } + + case 0x1E: // RR (HL) + s_time += 7; + data = rp.hl; + rr_data_addr: + RR( READ( data ), WRITE( data, result ) ) + + CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r + uint8_t& reg = R8( data, 0x18 ); + RR( reg, reg = result ) + } + + #define SRA( read, write ) {\ + uint8_t result = read;\ + flags = result & C01;\ + result = (result & 0x80) | (result >> 1);\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x2E: // SRA (HL) + data = rp.hl; + s_time += 7; + sra_data_addr: + SRA( READ( data ), WRITE( data, result ) ) + + CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r + uint8_t& reg = R8( data, 0x28 ); + SRA( reg, reg = result ) + } + + #define SRL( read, write ) {\ + uint8_t result = read;\ + flags = result & C01;\ + result >>= 1;\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x3E: // SRL (HL) + s_time += 7; + data = rp.hl; + srl_data_addr: + SRL( READ( data ), WRITE( data, result ) ) + + CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r + uint8_t& reg = R8( data, 0x38 ); + SRL( reg, reg = result ) + } + + // BIT + { + unsigned temp; + CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL) + s_time += 4; + temp = READ( rp.hl ); + flags &= C01; + goto bit_temp; + CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r + CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r + CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r + CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r + CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r + CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r + CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r + temp = R8( data & 7, 0 ); + flags = (flags & C01) | (temp & (F20 | F08)); + bit_temp: + int masked = temp & 1 << (data >> 3 & 7); + flags |=(masked & S80) | H10 | + ((masked - 1) >> 8 & (Z40 | P04)); + goto loop; + } + + // SET/RES + CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL) + CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL) + s_time += 7; + int temp = READ( rp.hl ); + int bit = 1 << (data >> 3 & 7); + temp |= bit; // SET + if ( !(data & 0x40) ) + temp ^= bit; // RES + WRITE( rp.hl, temp ); + goto loop; + } + + CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r + CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r + CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r + CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r + CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r + CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r + CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r + CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r + R8( data & 7, 0 ) |= 1 << (data >> 3 & 7); + goto loop; + + CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r + CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r + CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r + CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r + CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r + CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r + CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r + CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r + R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7)); + goto loop; + } + assert( false ); + } + +//////////////////////////////////////// ED prefix + { + case 0xED: + pc++; + s_time += ed_dd_timing [data] >> 4; + switch ( data ) + { + { + blargg_ulong temp; + case 0x72: // SBC HL,SP + case 0x7A: // ADC HL,SP + temp = sp; + if ( 0 ) + case 0x42: // SBC HL,BC + case 0x52: // SBC HL,DE + case 0x62: // SBC HL,HL + case 0x4A: // ADC HL,BC + case 0x5A: // ADC HL,DE + case 0x6A: // ADC HL,HL + temp = R16( data >> 3 & 6, 1, 0 ); + blargg_ulong sum = temp + (flags & C01); + flags = ~data >> 2 & N02; + if ( flags ) + sum = -sum; + sum += rp.hl; + temp ^= rp.hl; + temp ^= sum; + flags |=(sum >> 16 & C01) | + (temp >> 8 & H10) | + (sum >> 8 & (S80 | F20 | F08)) | + ((temp - -0x8000) >> 14 & V04); + rp.hl = sum; + if ( (uint16_t) sum ) + goto loop; + flags |= Z40; + goto loop; + } + + CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C) + int temp = IN( rp.bc ); + R8( data >> 3, 8 ) = temp; + flags = (flags & C01) | SZ28P( temp ); + goto loop; + } + + case 0x71: // OUT (C),0 + rg.flags = 0; + CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r + OUT( rp.bc, R8( data >> 3, 8 ) ); + goto loop; + + { + unsigned temp; + case 0x73: // LD (ADDR),SP + temp = sp; + if ( 0 ) + case 0x43: // LD (ADDR),BC + case 0x53: // LD (ADDR),DE + temp = R16( data, 4, 0x43 ); + uint16_t addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, temp ); + goto loop; + } + + case 0x4B: // LD BC,(ADDR) + case 0x5B:{// LD DE,(ADDR) + uint16_t addr = GET_ADDR(); + pc += 2; + R16( data, 4, 0x4B ) = READ_WORD( addr ); + goto loop; + } + + case 0x7B:{// LD SP,(ADDR) + uint16_t addr = GET_ADDR(); + pc += 2; + sp = READ_WORD( addr ); + goto loop; + } + + case 0x67:{// RRD + uint8_t temp = READ( rp.hl ); + WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); + temp = (rg.a & 0xF0) | (temp & 0x0F); + flags = (flags & C01) | SZ28P( temp ); + rg.a = temp; + goto loop; + } + + case 0x6F:{// RLD + uint8_t temp = READ( rp.hl ); + WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); + temp = (rg.a & 0xF0) | (temp >> 4); + flags = (flags & C01) | SZ28P( temp ); + rg.a = temp; + goto loop; + } + + CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG + opcode = 0x10; // flag to do SBC instead of ADC + flags &= ~C01; + data = rg.a; + rg.a = 0; + goto adc_data; + + { + int inc; + case 0xA9: // CPD + case 0xB9: // CPDR + inc = -1; + if ( 0 ) + case 0xA1: // CPI + case 0xB1: // CPIR + inc = +1; + uint16_t addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + int result = rg.a - temp; + flags = (flags & C01) | N02 | + ((((temp ^ rg.a) & H10) ^ result) & (S80 | H10)); + + if ( !(uint8_t) result ) flags |= Z40; + result -= (flags & H10) >> 4; + flags |= result & F08; + flags |= result << 4 & F20; + if ( !--rp.bc ) + goto loop; + + flags |= V04; + if ( flags & Z40 || data < 0xB0 ) + goto loop; + + pc -= 2; + s_time += 5; + goto loop; + } + + { + int inc; + case 0xA8: // LDD + case 0xB8: // LDDR + inc = -1; + if ( 0 ) + case 0xA0: // LDI + case 0xB0: // LDIR + inc = +1; + uint16_t addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + addr = rp.de; + rp.de = addr + inc; + WRITE( addr, temp ); + + temp += rg.a; + flags = (flags & (S80 | Z40 | C01)) | + (temp & F08) | (temp << 4 & F20); + if ( !--rp.bc ) + goto loop; + + flags |= V04; + if ( data < 0xB0 ) + goto loop; + + pc -= 2; + s_time += 5; + goto loop; + } + + { + int inc; + case 0xAB: // OUTD + case 0xBB: // OTDR + inc = -1; + if ( 0 ) + case 0xA3: // OUTI + case 0xB3: // OTIR + inc = +1; + uint16_t addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + int b = --rg.b; + flags = (temp >> 6 & N02) | SZ28( b ); + if ( b && data >= 0xB0 ) + { + pc -= 2; + s_time += 5; + } + + OUT( rp.bc, temp ); + goto loop; + } + + { + int inc; + case 0xAA: // IND + case 0xBA: // INDR + inc = -1; + if ( 0 ) + case 0xA2: // INI + case 0xB2: // INIR + inc = +1; + + uint16_t addr = rp.hl; + rp.hl = addr + inc; + + int temp = IN( rp.bc ); + + int b = --rg.b; + flags = (temp >> 6 & N02) | SZ28( b ); + if ( b && data >= 0xB0 ) + { + pc -= 2; + s_time += 5; + } + + WRITE( addr, temp ); + goto loop; + } + + case 0x47: // LD I,A + r.i = rg.a; + goto loop; + + case 0x4F: // LD R,A + SET_R( rg.a ); + debug_printf( "LD R,A not supported\n" ); + warning = true; + goto loop; + + case 0x57: // LD A,I + rg.a = r.i; + goto ld_ai_common; + + case 0x5F: // LD A,R + rg.a = GET_R(); + debug_printf( "LD A,R not supported\n" ); + warning = true; + ld_ai_common: + flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04); + goto loop; + + CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN + r.iff1 = r.iff2; + goto ret_taken; + + case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0 + r.im = 0; + goto loop; + + case 0x56: case 0x76: // IM 1 + r.im = 1; + goto loop; + + case 0x5E: case 0x7E: // IM 2 + r.im = 2; + goto loop; + + default: + debug_printf( "Opcode $ED $%02X not supported\n", data ); + warning = true; + goto loop; + } + assert( false ); + } + +//////////////////////////////////////// DD/FD prefix + { + uint16_t ixy; + case 0xDD: + ixy = ix; + goto ix_prefix; + case 0xFD: + ixy = iy; + ix_prefix: + pc++; + unsigned data2 = READ_PROG( pc ); + s_time += ed_dd_timing [data] & 0x0F; + switch ( data ) + { + // TODO: more efficient way of avoid negative address + #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) + + #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; + + // ADD/ADC/SUB/SBC + + case 0x96: // SUB (IXY+disp) + case 0x86: // ADD (IXY+disp) + flags &= ~C01; + case 0x9E: // SBC (IXY+disp) + case 0x8E: // ADC (IXY+disp) + pc++; + opcode = data; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto adc_data; + + case 0x94: // SUB HXY + case 0x84: // ADD HXY + flags &= ~C01; + case 0x9C: // SBC HXY + case 0x8C: // ADC HXY + opcode = data; + data = ixy >> 8; + goto adc_data; + + case 0x95: // SUB LXY + case 0x85: // ADD LXY + flags &= ~C01; + case 0x9D: // SBC LXY + case 0x8D: // ADC LXY + opcode = data; + data = (uint8_t) ixy; + goto adc_data; + + { + unsigned temp; + case 0x39: // ADD IXY,SP + temp = sp; + goto add_ixy_data; + + case 0x29: // ADD IXY,HL + temp = ixy; + goto add_ixy_data; + + case 0x09: // ADD IXY,BC + case 0x19: // ADD IXY,DE + temp = R16( data, 4, 0x09 ); + add_ixy_data: { + blargg_ulong sum = ixy + temp; + temp ^= ixy; + ixy = (uint16_t) sum; + flags = (flags & (S80 | Z40 | V04)) | + (sum >> 16) | + (sum >> 8 & (F20 | F08)) | + ((temp ^ sum) >> 8 & H10); + goto set_ixy; + } + } + + // AND + case 0xA6: // AND (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto and_data; + + case 0xA4: // AND HXY + data = ixy >> 8; + goto and_data; + + case 0xA5: // AND LXY + data = (uint8_t) ixy; + goto and_data; + + // OR + case 0xB6: // OR (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto or_data; + + case 0xB4: // OR HXY + data = ixy >> 8; + goto or_data; + + case 0xB5: // OR LXY + data = (uint8_t) ixy; + goto or_data; + + // XOR + case 0xAE: // XOR (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto xor_data; + + case 0xAC: // XOR HXY + data = ixy >> 8; + goto xor_data; + + case 0xAD: // XOR LXY + data = (uint8_t) ixy; + goto xor_data; + + // CP + case 0xBE: // CP (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto cp_data; + + case 0xBC: // CP HXY + data = ixy >> 8; + goto cp_data; + + case 0xBD: // CP LXY + data = (uint8_t) ixy; + goto cp_data; + + // LD + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r + data = R8( data, 0x70 ); + if ( 0 ) + case 0x36: // LD (IXY+disp),imm + pc++, data = READ_PROG( pc ); + pc++; + WRITE( IXY_DISP( ixy, (int8_t) data2 ), data ); + goto loop; + + CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY + R8( data >> 3, 8 ) = ixy >> 8; + goto loop; + + case 0x64: // LD HXY,HXY + case 0x6D: // LD LXY,LXY + goto loop; + + CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY + R8( data >> 3, 8 ) = ixy; + goto loop; + + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) + pc++; + R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto loop; + + case 0x26: // LD HXY,imm + pc++; + goto ld_hxy_data; + + case 0x65: // LD HXY,LXY + data2 = (uint8_t) ixy; + goto ld_hxy_data; + + CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r + data2 = R8( data, 0x60 ); + ld_hxy_data: + ixy = (uint8_t) ixy | (data2 << 8); + goto set_ixy; + + case 0x2E: // LD LXY,imm + pc++; + goto ld_lxy_data; + + case 0x6C: // LD LXY,HXY + data2 = ixy >> 8; + goto ld_lxy_data; + + CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r + data2 = R8( data, 0x68 ); + ld_lxy_data: + ixy = (ixy & 0xFF00) | data2; + set_ixy: + if ( opcode == 0xDD ) + { + ix = ixy; + goto loop; + } + iy = ixy; + goto loop; + + case 0xF9: // LD SP,IXY + sp = ixy; + goto loop; + + case 0x22:{// LD (ADDR),IXY + uint16_t addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, ixy ); + goto loop; + } + + case 0x21: // LD IXY,imm + ixy = GET_ADDR(); + pc += 2; + goto set_ixy; + + case 0x2A:{// LD IXY,(addr) + uint16_t addr = GET_ADDR(); + ixy = READ_WORD( addr ); + pc += 2; + goto set_ixy; + } + + // DD/FD CB prefix + case 0xCB: { + data = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data2 = READ_PROG( pc ); + pc++; + switch ( data2 ) + { + case 0x06: goto rlc_data_addr; // RLC (IXY) + case 0x16: goto rl_data_addr; // RL (IXY) + case 0x26: goto sla_data_addr; // SLA (IXY) + case 0x36: goto sll_data_addr; // SLL (IXY) + case 0x0E: goto rrc_data_addr; // RRC (IXY) + case 0x1E: goto rr_data_addr; // RR (IXY) + case 0x2E: goto sra_data_addr; // SRA (IXY) + case 0x3E: goto srl_data_addr; // SRL (IXY) + + CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) + uint8_t temp = READ( data ); + int masked = temp & 1 << (data2 >> 3 & 7); + flags = (flags & C01) | H10 | + (masked & S80) | + ((masked - 1) >> 8 & (Z40 | P04)); + goto loop; + } + + CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp) + CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp) + int temp = READ( data ); + int bit = 1 << (data2 >> 3 & 7); + temp |= bit; // SET + if ( !(data2 & 0x40) ) + temp ^= bit; // RES + WRITE( data, temp ); + goto loop; + } + + default: + debug_printf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 ); + warning = true; + goto loop; + } + assert( false ); + } + + // INC/DEC + case 0x23: // INC IXY + ixy = uint16_t (ixy + 1); + goto set_ixy; + + case 0x2B: // DEC IXY + ixy = uint16_t (ixy - 1); + goto set_ixy; + + case 0x34: // INC (IXY+disp) + ixy = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data = READ( ixy ) + 1; + WRITE( ixy, data ); + goto inc_set_flags; + + case 0x35: // DEC (IXY+disp) + ixy = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data = READ( ixy ) - 1; + WRITE( ixy, data ); + goto dec_set_flags; + + case 0x24: // INC HXY + ixy = uint16_t (ixy + 0x100); + data = ixy >> 8; + goto inc_xy_common; + + case 0x2C: // INC LXY + data = uint8_t (ixy + 1); + ixy = (ixy & 0xFF00) | data; + inc_xy_common: + if ( opcode == 0xDD ) + { + ix = ixy; + goto inc_set_flags; + } + iy = ixy; + goto inc_set_flags; + + case 0x25: // DEC HXY + ixy = uint16_t (ixy - 0x100); + data = ixy >> 8; + goto dec_xy_common; + + case 0x2D: // DEC LXY + data = uint8_t (ixy - 1); + ixy = (ixy & 0xFF00) | data; + dec_xy_common: + if ( opcode == 0xDD ) + { + ix = ixy; + goto dec_set_flags; + } + iy = ixy; + goto dec_set_flags; + + // PUSH/POP + case 0xE5: // PUSH IXY + data = ixy; + goto push_data; + + case 0xE1:{// POP IXY + ixy = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto set_ixy; + } + + // Misc + + case 0xE9: // JP (IXY) + pc = ixy; + goto loop; + + case 0xE3:{// EX (SP),IXY + uint16_t temp = READ_WORD( sp ); + WRITE_WORD( sp, ixy ); + ixy = temp; + goto set_ixy; + } + + default: + debug_printf( "Unnecessary DD/FD prefix encountered\n" ); + warning = true; + pc--; + goto loop; + } + assert( false ); + } + + } + debug_printf( "Unhandled main opcode: $%02X\n", opcode ); + assert( false ); + +halt: + s_time &= 3; // increment by multiple of 4 +out_of_time: + pc--; + + s.time = s_time; + rg.flags = flags; + r.ix = ix; + r.iy = iy; + r.sp = sp; + r.pc = pc; + this->r.b = rg; + this->state_ = s; + this->state = &this->state_; + + return warning; +} diff --git a/thirdparty/game-music-emu/gme/Ay_Cpu.h b/thirdparty/game-music-emu/gme/Ay_Cpu.h new file mode 100644 index 000000000..6984b42dc --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ay_Cpu.h @@ -0,0 +1,89 @@ +// Z80 CPU emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef AY_CPU_H +#define AY_CPU_H + +#include "blargg_endian.h" + +typedef blargg_long cpu_time_t; + +// must be defined by caller +void ay_cpu_out( class Ay_Cpu*, cpu_time_t, unsigned addr, int data ); +int ay_cpu_in( class Ay_Cpu*, unsigned addr ); + +class Ay_Cpu { +public: + // Clear all registers and keep pointer to 64K memory passed in + void reset( void* mem_64k ); + + // Run until specified time is reached. Returns true if suspicious/unsupported + // instruction was encountered at any point during run. + bool run( cpu_time_t end_time ); + + // Time of beginning of next instruction + cpu_time_t time() const { return state->time + state->base; } + + // Alter current time. Not supported during run() call. + void set_time( cpu_time_t t ) { state->time = t - state->base; } + void adjust_time( int delta ) { state->time += delta; } + + #if BLARGG_BIG_ENDIAN + struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; + #else + struct regs_t { uint8_t c, b, e, d, l, h, a, flags; }; + #endif + BOOST_STATIC_ASSERT( sizeof (regs_t) == 8 ); + + struct pairs_t { uint16_t bc, de, hl, fa; }; + + // Registers are not updated until run() returns + struct registers_t { + uint16_t pc; + uint16_t sp; + uint16_t ix; + uint16_t iy; + union { + regs_t b; // b.b, b.c, b.d, b.e, b.h, b.l, b.flags, b.a + pairs_t w; // w.bc, w.de, w.hl. w.fa + }; + union { + regs_t b; + pairs_t w; + } alt; + uint8_t iff1; + uint8_t iff2; + uint8_t r; + uint8_t i; + uint8_t im; + }; + //registers_t r; (below for efficiency) + + // can read this far past end of memory + enum { cpu_padding = 0x100 }; + +public: + Ay_Cpu(); +private: + uint8_t szpc [0x200]; + uint8_t* mem; + cpu_time_t end_time_; + struct state_t { + cpu_time_t base; + cpu_time_t time; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + void set_end_time( cpu_time_t t ); +public: + registers_t r; +}; + +inline void Ay_Cpu::set_end_time( cpu_time_t t ) +{ + cpu_time_t delta = state->base - t; + state->base = t; + state->time += delta; +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Ay_Emu.cpp b/thirdparty/game-music-emu/gme/Ay_Emu.cpp new file mode 100644 index 000000000..a973ba0f1 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ay_Emu.cpp @@ -0,0 +1,405 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Ay_Emu.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +long const spectrum_clock = 3546900; +long const cpc_clock = 2000000; + +unsigned const ram_start = 0x4000; +int const osc_count = Ay_Apu::osc_count + 1; + +Ay_Emu::Ay_Emu() +{ + beeper_output = 0; + set_type( gme_ay_type ); + + static const char* const names [osc_count] = { + "Wave 1", "Wave 2", "Wave 3", "Beeper" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type | 0, wave_type | 1, wave_type | 2, mixed_type | 0 + }; + set_voice_types( types ); + set_silence_lookahead( 6 ); +} + +Ay_Emu::~Ay_Emu() { } + +// Track info + +static byte const* get_data( Ay_Emu::file_t const& file, byte const* ptr, int min_size ) +{ + long pos = ptr - (byte const*) file.header; + long file_size = file.end - (byte const*) file.header; + assert( (unsigned long) pos <= (unsigned long) file_size - 2 ); + int offset = (int16_t) get_be16( ptr ); + if ( !offset || blargg_ulong (pos + offset) > blargg_ulong (file_size - min_size) ) + return 0; + return ptr + offset; +} + +static blargg_err_t parse_header( byte const* in, long size, Ay_Emu::file_t* out ) +{ + typedef Ay_Emu::header_t header_t; + out->header = (header_t const*) in; + out->end = in + size; + + if ( size < Ay_Emu::header_size ) + return gme_wrong_file_type; + + header_t const& h = *(header_t const*) in; + if ( memcmp( h.tag, "ZXAYEMUL", 8 ) ) + return gme_wrong_file_type; + + out->tracks = get_data( *out, h.track_info, (h.max_track + 1) * 4 ); + if ( !out->tracks ) + return "Missing track data"; + + return 0; +} + +static void copy_ay_fields( Ay_Emu::file_t const& file, track_info_t* out, int track ) +{ + Gme_File::copy_field_( out->song, (char const*) get_data( file, file.tracks + track * 4, 1 ) ); + byte const* track_info = get_data( file, file.tracks + track * 4 + 2, 6 ); + if ( track_info ) + out->length = get_be16( track_info + 4 ) * (1000L / 50); // frames to msec + + Gme_File::copy_field_( out->author, (char const*) get_data( file, file.header->author, 1 ) ); + Gme_File::copy_field_( out->comment, (char const*) get_data( file, file.header->comment, 1 ) ); +} + +blargg_err_t Ay_Emu::track_info_( track_info_t* out, int track ) const +{ + copy_ay_fields( file, out, track ); + return 0; +} + +struct Ay_File : Gme_Info_ +{ + Ay_Emu::file_t file; + + Ay_File() { set_type( gme_ay_type ); } + + blargg_err_t load_mem_( byte const* begin, long size ) + { + RETURN_ERR( parse_header( begin, size, &file ) ); + set_track_count( file.header->max_track + 1 ); + return 0; + } + + blargg_err_t track_info_( track_info_t* out, int track ) const + { + copy_ay_fields( file, out, track ); + return 0; + } +}; + +static Music_Emu* new_ay_emu () { return BLARGG_NEW Ay_Emu ; } +static Music_Emu* new_ay_file() { return BLARGG_NEW Ay_File; } + +static gme_type_t_ const gme_ay_type_ = { "ZX Spectrum", 0, &new_ay_emu, &new_ay_file, "AY", 1 }; +BLARGG_EXPORT extern gme_type_t const gme_ay_type = &gme_ay_type_; + +// Setup + +blargg_err_t Ay_Emu::load_mem_( byte const* in, long size ) +{ + assert( offsetof (header_t,track_info [2]) == header_size ); + + RETURN_ERR( parse_header( in, size, &file ) ); + set_track_count( file.header->max_track + 1 ); + + if ( file.header->vers > 2 ) + set_warning( "Unknown file version" ); + + set_voice_count( osc_count ); + apu.volume( gain() ); + + return setup_buffer( spectrum_clock ); +} + +void Ay_Emu::update_eq( blip_eq_t const& eq ) +{ + apu.treble_eq( eq ); +} + +void Ay_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer*, Blip_Buffer* ) +{ + if ( i >= Ay_Apu::osc_count ) + beeper_output = center; + else + apu.osc_output( i, center ); +} + +// Emulation + +void Ay_Emu::set_tempo_( double t ) +{ + play_period = blip_time_t (clock_rate() / 50 / t); +} + +blargg_err_t Ay_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + memset( mem.ram + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET + memset( mem.ram + 0x0100, 0xFF, 0x4000 - 0x100 ); + memset( mem.ram + ram_start, 0x00, sizeof mem.ram - ram_start ); + memset( mem.padding1, 0xFF, sizeof mem.padding1 ); + memset( mem.ram + 0x10000, 0xFF, sizeof mem.ram - 0x10000 ); + + // locate data blocks + byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 ); + if ( !data ) return "File data missing"; + + byte const* const more_data = get_data( file, data + 10, 6 ); + if ( !more_data ) return "File data missing"; + + byte const* blocks = get_data( file, data + 12, 8 ); + if ( !blocks ) return "File data missing"; + + // initial addresses + cpu::reset( mem.ram ); + r.sp = get_be16( more_data ); + r.b.a = r.b.b = r.b.d = r.b.h = data [8]; + r.b.flags = r.b.c = r.b.e = r.b.l = data [9]; + r.alt.w = r.w; + r.ix = r.iy = r.w.hl; + + unsigned addr = get_be16( blocks ); + if ( !addr ) return "File data missing"; + + unsigned init = get_be16( more_data + 2 ); + if ( !init ) + init = addr; + + // copy blocks into memory + do + { + blocks += 2; + unsigned len = get_be16( blocks ); blocks += 2; + if ( addr + len > 0x10000 ) + { + set_warning( "Bad data block size" ); + len = 0x10000 - addr; + } + check( len ); + byte const* in = get_data( file, blocks, 0 ); blocks += 2; + if ( len > blargg_ulong (file.end - in) ) + { + set_warning( "Missing file data" ); + len = file.end - in; + } + //debug_printf( "addr: $%04X, len: $%04X\n", addr, len ); + if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data + debug_printf( "Block addr in ROM\n" ); + memcpy( mem.ram + addr, in, len ); + + if ( file.end - blocks < 8 ) + { + set_warning( "Missing file data" ); + break; + } + } + while ( (addr = get_be16( blocks )) != 0 ); + + // copy and configure driver + static byte const passive [] = { + 0xF3, // DI + 0xCD, 0, 0, // CALL init + 0xED, 0x5E, // LOOP: IM 2 + 0xFB, // EI + 0x76, // HALT + 0x18, 0xFA // JR LOOP + }; + static byte const active [] = { + 0xF3, // DI + 0xCD, 0, 0, // CALL init + 0xED, 0x56, // LOOP: IM 1 + 0xFB, // EI + 0x76, // HALT + 0xCD, 0, 0, // CALL play + 0x18, 0xF7 // JR LOOP + }; + memcpy( mem.ram, passive, sizeof passive ); + unsigned play_addr = get_be16( more_data + 4 ); + //debug_printf( "Play: $%04X\n", play_addr ); + if ( play_addr ) + { + memcpy( mem.ram, active, sizeof active ); + mem.ram [ 9] = play_addr; + mem.ram [10] = play_addr >> 8; + } + mem.ram [2] = init; + mem.ram [3] = init >> 8; + + mem.ram [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET) + + memcpy( mem.ram + 0x10000, mem.ram, 0x80 ); // some code wraps around (ugh) + + beeper_delta = int (apu.amp_range * 0.65); + last_beeper = 0; + apu.reset(); + next_play = play_period; + + // start at spectrum speed + change_clock_rate( spectrum_clock ); + set_tempo( tempo() ); + + spectrum_mode = false; + cpc_mode = false; + cpc_latch = 0; + + return 0; +} + +// Emulation + +void Ay_Emu::cpu_out_misc( cpu_time_t time, unsigned addr, int data ) +{ + if ( !cpc_mode ) + { + switch ( addr & 0xFEFF ) + { + case 0xFEFD: + spectrum_mode = true; + apu_addr = data & 0x0F; + return; + + case 0xBEFD: + spectrum_mode = true; + apu.write( time, apu_addr, data ); + return; + } + } + + if ( !spectrum_mode ) + { + switch ( addr >> 8 ) + { + case 0xF6: + switch ( data & 0xC0 ) + { + case 0xC0: + apu_addr = cpc_latch & 0x0F; + goto enable_cpc; + + case 0x80: + apu.write( time, apu_addr, cpc_latch ); + goto enable_cpc; + } + break; + + case 0xF4: + cpc_latch = data; + goto enable_cpc; + } + } + + debug_printf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); + return; + +enable_cpc: + if ( !cpc_mode ) + { + cpc_mode = true; + change_clock_rate( cpc_clock ); + set_tempo( tempo() ); + } +} + +void ay_cpu_out( Ay_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) +{ + Ay_Emu& emu = STATIC_CAST(Ay_Emu&,*cpu); + + if ( (addr & 0xFF) == 0xFE && !emu.cpc_mode ) + { + int delta = emu.beeper_delta; + data &= 0x10; + if ( emu.last_beeper != data ) + { + emu.last_beeper = data; + emu.beeper_delta = -delta; + emu.spectrum_mode = true; + if ( emu.beeper_output ) + emu.apu.synth_.offset( time, delta, emu.beeper_output ); + } + } + else + { + emu.cpu_out_misc( time, addr, data ); + } +} + +int ay_cpu_in( Ay_Cpu*, unsigned addr ) +{ + // keyboard read and other things + if ( (addr & 0xFF) == 0xFE ) + return 0xFF; // other values break some beeper tunes + + debug_printf( "Unmapped IN : $%04X\n", addr ); + return 0xFF; +} + +blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int ) +{ + set_time( 0 ); + if ( !(spectrum_mode | cpc_mode) ) + duration /= 2; // until mode is set, leave room for halved clock rate + + while ( time() < duration ) + { + cpu::run( min( duration, (blip_time_t) next_play ) ); + + if ( time() >= next_play ) + { + next_play += play_period; + + if ( r.iff1 ) + { + if ( mem.ram [r.pc] == 0x76 ) + r.pc++; + + r.iff1 = r.iff2 = 0; + + mem.ram [--r.sp] = uint8_t (r.pc >> 8); + mem.ram [--r.sp] = uint8_t (r.pc); + r.pc = 0x38; + cpu::adjust_time( 12 ); + if ( r.im == 2 ) + { + cpu::adjust_time( 6 ); + unsigned addr = r.i * 0x100u + 0xFF; + r.pc = mem.ram [(addr + 1) & 0xFFFF] * 0x100u + mem.ram [addr]; + } + } + } + } + duration = time(); + next_play -= duration; + check( next_play >= 0 ); + adjust_time( -duration ); + + apu.end_frame( duration ); + + return 0; +} diff --git a/thirdparty/game-music-emu/gme/Ay_Emu.h b/thirdparty/game-music-emu/gme/Ay_Emu.h new file mode 100644 index 000000000..6726f0157 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ay_Emu.h @@ -0,0 +1,69 @@ +// Sinclair Spectrum AY music file emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef AY_EMU_H +#define AY_EMU_H + +#include "Classic_Emu.h" +#include "Ay_Apu.h" +#include "Ay_Cpu.h" + +class Ay_Emu : private Ay_Cpu, public Classic_Emu { + typedef Ay_Cpu cpu; +public: + // AY file header + enum { header_size = 0x14 }; + struct header_t + { + byte tag [8]; + byte vers; + byte player; + byte unused [2]; + byte author [2]; + byte comment [2]; + byte max_track; + byte first_track; + byte track_info [2]; + }; + + static gme_type_t static_type() { return gme_ay_type; } +public: + Ay_Emu(); + ~Ay_Emu(); + struct file_t { + header_t const* header; + byte const* end; + byte const* tracks; + }; +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_mem_( byte const*, long ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); +private: + file_t file; + + cpu_time_t play_period; + cpu_time_t next_play; + Blip_Buffer* beeper_output; + int beeper_delta; + int last_beeper; + int apu_addr; + int cpc_latch; + bool spectrum_mode; + bool cpc_mode; + + // large items + struct { + byte padding1 [0x100]; + byte ram [0x10000 + 0x100]; + } mem; + Ay_Apu apu; + friend void ay_cpu_out( Ay_Cpu*, cpu_time_t, unsigned addr, int data ); + void cpu_out_misc( cpu_time_t, unsigned addr, int data ); +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Blip_Buffer.cpp b/thirdparty/game-music-emu/gme/Blip_Buffer.cpp new file mode 100644 index 000000000..2b88cd4f8 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Blip_Buffer.cpp @@ -0,0 +1,460 @@ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ + +#include "Blip_Buffer.h" + +#include +#include +#include +#include +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +int const silent_buf_size = 1; // size used for Silent_Blip_Buffer + +Blip_Buffer::Blip_Buffer() +{ + factor_ = (blip_ulong)-1 / 2; + offset_ = 0; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + reader_accum_ = 0; + bass_shift_ = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; + + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + buf_t_ i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); + + // casting to short truncates to 16 bits and sign-extends + i = 0x18000; + assert( (short) i == -0x8000 ); + #endif +} + +Blip_Buffer::~Blip_Buffer() +{ + if ( buffer_size_ != silent_buf_size ) + free( buffer_ ); +} + +Silent_Blip_Buffer::Silent_Blip_Buffer() +{ + factor_ = 0; + buffer_ = buf; + buffer_size_ = silent_buf_size; + memset( buf, 0, sizeof buf ); // in case machine takes exception for signed overflow +} + +void Blip_Buffer::clear( int entire_buffer ) +{ + offset_ = 0; + reader_accum_ = 0; + modified_ = 0; + if ( buffer_ ) + { + long count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) ); + } +} + +Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return "Internal (tried to resize Silent_Blip_Buffer)"; + } + + // start with maximum length that resampled time can represent + long new_size = (UINT_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; + if ( msec != blip_max_length ) + { + long s = (new_rate * (msec + 1) + 999) / 1000; + if ( s < new_size ) + new_size = s; + else + assert( 0 ); // fails if requested buffer length exceeds limit + } + + if ( buffer_size_ != new_size ) + { + void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + buffer_ = (buf_t_*) p; + } + + buffer_size_ = new_size; + assert( buffer_size_ != silent_buf_size ); + + // update things based on the sample rate + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( msec ) + assert( length_ == msec ); // ensure length is same as that passed in + if ( clock_rate_ ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + + clear(); + + return 0; // success +} + +blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const +{ + double ratio = (double) sample_rate_ / rate; + blip_long factor = (blip_long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); + assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large + return (blip_resampled_time_t) factor; +} + +void Blip_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + int shift = 31; + if ( freq > 0 ) + { + shift = 13; + long f = (freq << 16) / sample_rate_; + while ( (f >>= 1) && --shift ) { } + } + bass_shift_ = shift; +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length +} + +void Blip_Buffer::remove_silence( long count ) +{ + assert( count <= samples_avail() ); // tried to remove more samples than available + offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; +} + +long Blip_Buffer::count_samples( blip_time_t t ) const +{ + unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return (long) (last_sample - first_sample); +} + +blip_time_t Blip_Buffer::count_clocks( long count ) const +{ + if ( !factor_ ) + { + assert( 0 ); // sample rate and clock rates must be set first + return 0; + } + + if ( count > buffer_size_ ) + count = buffer_size_; + blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; + return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); +} + +void Blip_Buffer::remove_samples( long count ) +{ + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + long remain = samples_avail() + blip_buffer_extra_; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +// Blip_Synth_ + +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +void Blip_Synth_Fast_::volume_unit( double new_unit ) +{ + delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); +} + +#if !BLIP_BUFFER_FAST + +Blip_Synth_::Blip_Synth_( short* p, int w ) : + impulses( p ), + width( w ) +{ + volume_unit_ = 0.0; + kernel_unit = 0; + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) +{ + if ( cutoff >= 0.999 ) + cutoff = 0.999; + + if ( treble < -300.0 ) + treble = -300.0; + if ( treble > 5.0 ) + treble = 5.0; + + double const maxh = 4096.0; + double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); + double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); + double const to_angle = PI / 2 / maxh / oversample; + for ( int i = 0; i < count; i++ ) + { + double angle = ((i - count) * 2 + 1) * to_angle; + double angle_maxh = angle * maxh; + double angle_maxh_mid = angle_maxh * cutoff; + + double y = maxh; + + // 0 to Fs/2*cutoff, flat + if ( angle_maxh_mid ) // unstable at t=0 + y *= sin( angle_maxh_mid ) / angle_maxh_mid; + + // Fs/2*cutoff to Fs/2, logarithmic rolloff + double cosa = cos( angle ); + double den = 1 + rolloff * (rolloff - cosa - cosa); + + // Becomes unstable when rolloff is near 1.0 and t is near 0, + // which is the only time den becomes small + if ( den > 1e-13 ) + { + double num = + (cos( angle_maxh - angle ) * rolloff - cos( angle_maxh )) * pow_a_n - + cos( angle_maxh_mid - angle ) * rolloff + cos( angle_maxh_mid ); + + y = y * cutoff + num / den; + } + + out [i] = (float) y; + } +} + +void blip_eq_t::generate( float* out, int count ) const +{ + // lower cutoff freq for narrow kernels with their wider transition band + // (8 points->1.49, 16 points->1.15) + double oversample = blip_res * 2.25 / count + 0.85; + double half_rate = sample_rate * 0.5; + if ( cutoff_freq ) + oversample = half_rate / cutoff_freq; + double cutoff = rolloff_freq * oversample / half_rate; + + gen_sinc( out, count, blip_res * oversample, treble, cutoff ); + + // apply (half of) hamming window + double to_fraction = PI / (count - 1); + for ( int i = count; i--; ) + out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction ); +} + +void Blip_Synth_::adjust_impulse() +{ + // sum pairs for each phase and add error correction to end of first half + int const size = impulses_size(); + for ( int p = blip_res; p-- >= blip_res / 2; ) + { + int p2 = blip_res - 2 - p; + long error = kernel_unit; + for ( int i = 1; i < size; i += blip_res ) + { + error -= impulses [i + p ]; + error -= impulses [i + p2]; + } + if ( p == p2 ) + error /= 2; // phase = 0.5 impulse uses same half for both sides + impulses [size - blip_res + p] += (short) error; + //printf( "error: %ld\n", error ); + } + + //for ( int i = blip_res; i--; printf( "\n" ) ) + // for ( int j = 0; j < width / 2; j++ ) + // printf( "%5ld,", impulses [j * blip_res + i + 1] ); +} + +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) +{ + float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; + + int const half_size = blip_res / 2 * (width - 1); + eq.generate( &fimpulse [blip_res], half_size ); + + int i; + + // need mirror slightly past center for calculation + for ( i = blip_res; i--; ) + fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; + + // starts at 0 + for ( i = 0; i < blip_res; i++ ) + fimpulse [i] = 0.0f; + + // find rescale factor + double total = 0.0; + for ( i = 0; i < half_size; i++ ) + total += fimpulse [blip_res + i]; + + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB + //double const base_unit = 37888.0; // allows treble to +5 dB + double const base_unit = 32768.0; // necessary for blip_unscaled to work + double rescale = base_unit / 2 / total; + kernel_unit = (long) base_unit; + + // integrate, first difference, rescale, convert to int + double sum = 0.0; + double next = 0.0; + int const impulses_size = this->impulses_size(); + for ( i = 0; i < impulses_size; i++ ) + { + impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); + sum += fimpulse [i]; + next += fimpulse [i + blip_res]; + } + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) + { + volume_unit_ = 0.0; + volume_unit( vol ); + } +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( new_unit != volume_unit_ ) + { + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + volume_unit_ = new_unit; + double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; + + if ( factor > 0.0 ) + { + int shift = 0; + + // if unit is really small, might need to attenuate kernel + while ( factor < 2.0 ) + { + shift++; + factor *= 2.0; + } + + if ( shift ) + { + kernel_unit >>= shift; + assert( kernel_unit > 0 ); // fails if volume unit is too low + + // keep values positive to avoid round-towards-zero of sign-preserving + // right shift for negative values + long offset = 0x8000 + (1 << (shift - 1)); + long offset2 = 0x8000 >> shift; + for ( int i = impulses_size(); i--; ) + impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); + adjust_impulse(); + } + } + delta_factor = (int) floor( factor + 0.5 ); + //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); + } +} +#endif + +long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo ) +{ + long count = samples_avail(); + if ( count > max_samples ) + count = max_samples; + + if ( count ) + { + int const bass = BLIP_READER_BASS( *this ); + BLIP_READER_BEGIN( reader, *this ); + + if ( !stereo ) + { + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out++ = (blip_sample_t) s; + BLIP_READER_NEXT( reader, bass ); + } + } + else + { + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out = (blip_sample_t) s; + out += 2; + BLIP_READER_NEXT( reader, bass ); + } + } + BLIP_READER_END( reader, *this ); + + remove_samples( count ); + } + return count; +} + +void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return; + } + + buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; + + int const sample_shift = blip_sample_bits - 16; + int prev = 0; + while ( count-- ) + { + blip_long s = (blip_long) *in++ << sample_shift; + *out += s - prev; + prev = s; + ++out; + } + *out -= prev; +} + diff --git a/thirdparty/game-music-emu/gme/Blip_Buffer.h b/thirdparty/game-music-emu/gme/Blip_Buffer.h new file mode 100644 index 000000000..e6facc820 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Blip_Buffer.h @@ -0,0 +1,490 @@ +// Band-limited sound synthesis buffer + +// Blip_Buffer 0.4.1 +#ifndef BLIP_BUFFER_H +#define BLIP_BUFFER_H + + // internal + #include + #if INT_MAX < 0x7FFFFFFF + #error "int must be at least 32 bits" + #endif + + typedef int blip_long; + typedef unsigned blip_ulong; + +// Time unit at source clock rate +typedef blip_long blip_time_t; + +// Output samples are 16-bit signed, with a range of -32768 to 32767 +typedef short blip_sample_t; +enum { blip_sample_max = 32767 }; + +class Blip_Buffer { +public: + typedef const char* blargg_err_t; + + // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults + // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there + // isn't enough memory, returns error without affecting current buffer setup. + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); + + // Set number of source time units per second + void clock_rate( long ); + + // End current time frame of specified duration and make its samples available + // (along with any still-unread samples) for reading with read_samples(). Begins + // a new time frame at the end of the current frame. + void end_frame( blip_time_t time ); + + // Read at most 'max_samples' out of buffer into 'dest', removing them from from + // the buffer. Returns number of samples actually read and removed. If stereo is + // true, increments 'dest' one extra time after writing each sample, to allow + // easy interleving of two channels into a stereo output buffer. + long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); + +// Additional optional features + + // Current output sample rate + long sample_rate() const; + + // Length of buffer, in milliseconds + int length() const; + + // Number of source time units per second + long clock_rate() const; + + // Set frequency high-pass filter frequency, where higher values reduce bass more + void bass_freq( int frequency ); + + // Number of samples delay from synthesis to samples read out + int output_latency() const; + + // Remove all available samples and clear buffer to silence. If 'entire_buffer' is + // false, just clears out any samples waiting rather than the entire buffer. + void clear( int entire_buffer = 1 ); + + // Number of samples available for reading with read_samples() + long samples_avail() const; + + // Remove 'count' samples from those waiting to be read + void remove_samples( long count ); + +// Experimental features + + // Count number of clocks needed until 'count' samples will be available. + // If buffer can't even hold 'count' samples, returns number of clocks until + // buffer becomes full. + blip_time_t count_clocks( long count ) const; + + // Number of raw samples that can be mixed within frame of specified duration. + long count_samples( blip_time_t duration ) const; + + // Mix 'count' samples from 'buf' into buffer. + void mix_samples( blip_sample_t const* buf, long count ); + + // not documented yet + void set_modified() { modified_ = 1; } + int clear_modified() { int b = modified_; modified_ = 0; return b; } + typedef blip_ulong blip_resampled_time_t; + void remove_silence( long count ); + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } + blip_resampled_time_t clock_rate_factor( long clock_rate ) const; +public: + Blip_Buffer(); + ~Blip_Buffer(); + + Blip_Buffer(Blip_Buffer &&) = default; + + // Deprecated + typedef blip_resampled_time_t resampled_time_t; + blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } + blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } +private: + // noncopyable + Blip_Buffer( const Blip_Buffer& ); + Blip_Buffer& operator = ( const Blip_Buffer& ); +public: + typedef blip_time_t buf_t_; + blip_ulong factor_; + blip_resampled_time_t offset_; + buf_t_* buffer_; + blip_long buffer_size_; + blip_long reader_accum_; + int bass_shift_; +private: + long sample_rate_; + long clock_rate_; + int bass_freq_; + int length_; + int modified_; + friend class Blip_Reader; +}; + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +// Number of bits in resample ratio fraction. Higher values give a more accurate ratio +// but reduce maximum buffer size. +#ifndef BLIP_BUFFER_ACCURACY + #define BLIP_BUFFER_ACCURACY 16 +#endif + +// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in +// noticeable broadband noise when synthesizing high frequency square waves. +// Affects size of Blip_Synth objects since they store the waveform directly. +#ifndef BLIP_PHASE_BITS + #if BLIP_BUFFER_FAST + #define BLIP_PHASE_BITS 8 + #else + #define BLIP_PHASE_BITS 6 + #endif +#endif + + // Internal + typedef blip_ulong blip_resampled_time_t; + int const blip_widest_impulse_ = 16; + int const blip_buffer_extra_ = blip_widest_impulse_ + 2; + int const blip_res = 1 << BLIP_PHASE_BITS; + class blip_eq_t; + + class Blip_Synth_Fast_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_Fast_(); + void treble_eq( blip_eq_t const& ) { } + }; + + class Blip_Synth_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_( short* impulses, int width ); + void treble_eq( blip_eq_t const& ); + private: + double volume_unit_; + short* const impulses; + int const width; + blip_long kernel_unit; + int impulses_size() const { return blip_res / 2 * width + 1; } + void adjust_impulse(); + }; + +// Quality level. Start with blip_good_quality. +const int blip_med_quality = 8; +const int blip_good_quality = 12; +const int blip_high_quality = 16; + +// Range specifies the greatest expected change in amplitude. Calculate it +// by finding the difference between the maximum and minimum expected +// amplitudes (max - min). +template +class Blip_Synth { +public: + // Set overall volume of waveform + void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } + + // Configure low-pass filter (see blip_buffer.txt) + void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } + + // Get/set Blip_Buffer used for output + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Update amplitude of waveform at given time. Using this requires a separate + // Blip_Synth for each waveform. + void update( blip_time_t time, int amplitude ); + +// Low-level interface + + // Add an amplitude transition of specified delta, optionally into specified buffer + // rather than the one set with output(). Delta can be positive or negative. + // The actual change in amplitude is delta * (volume / range) + void offset( blip_time_t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Contact author for more info. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + + // Same as offset(), except code is inlined for higher performance + void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( blip_time_t t, int delta ) const { + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); + } + +private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; +#else + Blip_Synth_ impl; + typedef short imp_t; + imp_t impulses [blip_res * (quality / 2) + 1]; +public: + Blip_Synth() : impl( impulses, quality ) { } +#endif +}; + +// Low-pass equalization parameters +class blip_eq_t { +public: + // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce + // treble, small positive values (0 to 5.0) increase treble. + blip_eq_t( double treble_db = 0 ); + + // See blip_buffer.txt + blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); + +private: + double treble; + long rolloff_freq; + long sample_rate; + long cutoff_freq; + void generate( float* out, int count ) const; + friend class Blip_Synth_; +}; + +int const blip_sample_bits = 30; + +// Dummy Blip_Buffer to direct sound output to, for easy muting without +// having to stop sound code. +class Silent_Blip_Buffer : public Blip_Buffer { + buf_t_ buf [blip_buffer_extra_ + 1]; +public: + // The following cannot be used (an assertion will fail if attempted): + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); + blip_time_t count_clocks( long count ) const; + void mix_samples( blip_sample_t const* buf, long count ); + + Silent_Blip_Buffer(); +}; + + #if defined (__GNUC__) || _MSC_VER >= 1100 + #define BLIP_RESTRICT __restrict + #else + #define BLIP_RESTRICT + #endif + +// Optimized reading from Blip_Buffer, for use in custom sample output + +// Begin reading from buffer. Name should be unique to the current block. +#define BLIP_READER_BEGIN( name, blip_buffer ) \ + const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\ + blip_long name##_reader_accum = (blip_buffer).reader_accum_ + +// Get value to pass to BLIP_READER_NEXT() +#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_) + +// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal +// code at the cost of having no bass control +int const blip_reader_default_bass = 9; + +// Current sample +#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16)) + +// Current raw sample in full internal resolution +#define BLIP_READER_READ_RAW( name ) (name##_reader_accum) + +// Advance to next sample +#define BLIP_READER_NEXT( name, bass ) \ + (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) + +// End reading samples from buffer. The number of samples read must now be removed +// using Blip_Buffer::remove_samples(). +#define BLIP_READER_END( name, blip_buffer ) \ + (void) ((blip_buffer).reader_accum_ = name##_reader_accum) + + +// Compatibility with older version +const long blip_unscaled = 65535; +const int blip_low_quality = blip_med_quality; +const int blip_best_quality = blip_high_quality; + +// Deprecated; use BLIP_READER macros as follows: +// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf ); +// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf ); +// r.read() -> BLIP_READER_READ( r ) +// r.read_raw() -> BLIP_READER_READ_RAW( r ) +// r.next( bass ) -> BLIP_READER_NEXT( r, bass ) +// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass ) +// r.end( buf ) -> BLIP_READER_END( r, buf ) +class Blip_Reader { +public: + int begin( Blip_Buffer& ); + blip_long read() const { return accum >> (blip_sample_bits - 16); } + blip_long read_raw() const { return accum; } + void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } + void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } + +private: + const Blip_Buffer::buf_t_* buf; + blip_long accum; +}; + +// End of public interface + +#include + +template +inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, + int delta, Blip_Buffer* blip_buf ) const +{ + // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the + // need for a longer buffer as set by set_sample_rate(). + assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); + delta *= impl.delta_factor; + blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); + +#if BLIP_BUFFER_FAST + blip_long left = buf [0] + delta; + + // Kind of crappy, but doing shift after multiply results in overflow. + // Alternate way of delaying multiply by delta_factor results in worse + // sub-sample resolution. + blip_long right = (delta >> BLIP_PHASE_BITS) * phase; + left -= right; + right += buf [1]; + + buf [0] = left; + buf [1] = right; +#else + + int const fwd = (blip_widest_impulse_ - quality) / 2; + int const rev = fwd + quality - 2; + int const mid = quality / 2 - 1; + + imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; + + #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + + // straight forward implementation resulted in better code on GCC for x86 + + #define ADD_IMP( out, in ) \ + buf [out] += (blip_long) imp [blip_res * (in)] * delta + + #define BLIP_FWD( i ) {\ + ADD_IMP( fwd + i, i );\ + ADD_IMP( fwd + 1 + i, i + 1 );\ + } + #define BLIP_REV( r ) {\ + ADD_IMP( rev - r, r + 1 );\ + ADD_IMP( rev + 1 - r, r );\ + } + + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + ADD_IMP( fwd + mid - 1, mid - 1 ); + ADD_IMP( fwd + mid , mid ); + imp = impulses + phase; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + ADD_IMP( rev , 1 ); + ADD_IMP( rev + 1, 0 ); + + #else + + // for RISC processors, help compiler by reading ahead of writes + + #define BLIP_FWD( i ) {\ + blip_long t0 = i0 * delta + buf [fwd + i];\ + blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ + i0 = imp [blip_res * (i + 2)];\ + buf [fwd + i] = t0;\ + buf [fwd + 1 + i] = t1;\ + } + #define BLIP_REV( r ) {\ + blip_long t0 = i0 * delta + buf [rev - r];\ + blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\ + i0 = imp [blip_res * (r - 1)];\ + buf [rev - r] = t0;\ + buf [rev + 1 - r] = t1;\ + } + + blip_long i0 = *imp; + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + blip_long t0 = i0 * delta + buf [fwd + mid - 1]; + blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ]; + imp = impulses + phase; + i0 = imp [blip_res * mid]; + buf [fwd + mid - 1] = t0; + buf [fwd + mid ] = t1; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + blip_long t0 = i0 * delta + buf [rev ]; + blip_long t1 = *imp * delta + buf [rev + 1]; + buf [rev ] = t0; + buf [rev + 1] = t1; + #endif + +#endif +} + +#undef BLIP_FWD +#undef BLIP_REV + +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const +{ + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); +} + +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::update( blip_time_t t, int amp ) +{ + int delta = amp - impl.last_amp; + impl.last_amp = amp; + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); +} + +inline blip_eq_t::blip_eq_t( double t ) : + treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } +inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : + treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } + +inline int Blip_Buffer::length() const { return length_; } +inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } +inline long Blip_Buffer::sample_rate() const { return sample_rate_; } +inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } +inline long Blip_Buffer::clock_rate() const { return clock_rate_; } +inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } + +inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) +{ + buf = blip_buf.buffer_; + accum = blip_buf.reader_accum_; + return blip_buf.bass_shift_; +} + +int const blip_max_length = 0; +int const blip_default_length = 250; + +#endif diff --git a/thirdparty/game-music-emu/gme/CMakeLists.txt b/thirdparty/game-music-emu/gme/CMakeLists.txt new file mode 100644 index 000000000..6449be0b3 --- /dev/null +++ b/thirdparty/game-music-emu/gme/CMakeLists.txt @@ -0,0 +1,211 @@ +# List of source files required by libgme and any emulators +# This is not 100% accurate (Fir_Resampler for instance) but +# you'll be OK. +set(libgme_SRCS Blip_Buffer.cpp + Classic_Emu.cpp + Data_Reader.cpp + Dual_Resampler.cpp + Effects_Buffer.cpp + Fir_Resampler.cpp + gme.cpp + Gme_File.cpp + M3u_Playlist.cpp + Multi_Buffer.cpp + Music_Emu.cpp + ) + +# static builds need to find static zlib (and static forms of other needed +# libraries. Ensure CMake looks only for static libs if we're doing a static +# build. See https://stackoverflow.com/a/44738756 +if(NOT BUILD_SHARED_LIBS) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") +endif() + +# [ZMusic] We use miniz now. +# find_package(ZLIB QUIET) + +# Ay_Apu is very popular around here +if (USE_GME_AY OR USE_GME_KSS) + set(libgme_SRCS ${libgme_SRCS} + Ay_Apu.cpp + ) +endif() + +# so is Ym2612_Emu +if (USE_GME_VGM OR USE_GME_GYM) + if(GME_YM2612_EMU STREQUAL "Nuked") + add_definitions(-DVGM_YM2612_NUKED) + set(libgme_SRCS ${libgme_SRCS} + Ym2612_Nuked.cpp + ) + message("VGM/GYM: Nuked OPN2 emulator will be used") + elseif(GME_YM2612_EMU STREQUAL "MAME") + add_definitions(-DVGM_YM2612_MAME) + set(libgme_SRCS ${libgme_SRCS} + Ym2612_MAME.cpp + ) + message("VGM/GYM: MAME YM2612 emulator will be used") + else() + add_definitions(-DVGM_YM2612_GENS) + set(libgme_SRCS ${libgme_SRCS} + Ym2612_GENS.cpp + ) + message("VGM/GYM: GENS 2.10 emulator will be used") + endif() +endif() + +# But none are as popular as Sms_Apu +if (USE_GME_VGM OR USE_GME_GYM OR USE_GME_KSS) + set(libgme_SRCS ${libgme_SRCS} + Sms_Apu.cpp + ) +endif() + +if (USE_GME_AY) + set(libgme_SRCS ${libgme_SRCS} + # Ay_Apu.cpp included earlier + Ay_Cpu.cpp + Ay_Emu.cpp + ) +endif() + +if (USE_GME_GBS) + set(libgme_SRCS ${libgme_SRCS} + Gb_Apu.cpp + Gb_Cpu.cpp + Gb_Oscs.cpp + Gbs_Emu.cpp + ) +endif() + +if (USE_GME_GYM) + set(libgme_SRCS ${libgme_SRCS} + # Sms_Apu.cpp included earlier + # Ym2612_Emu.cpp included earlier + Gym_Emu.cpp + ) +endif() + +if (USE_GME_HES) + set(libgme_SRCS ${libgme_SRCS} + Hes_Apu.cpp + Hes_Cpu.cpp + Hes_Emu.cpp + ) +endif() + +if (USE_GME_KSS) + set(libgme_SRCS ${libgme_SRCS} + # Ay_Apu.cpp included earlier + # Sms_Apu.cpp included earlier + Kss_Cpu.cpp + Kss_Emu.cpp + Kss_Scc_Apu.cpp + ) +endif() + +if (USE_GME_NSF OR USE_GME_NSFE) + set(libgme_SRCS ${libgme_SRCS} + Nes_Apu.cpp + Nes_Cpu.cpp + Nes_Fme7_Apu.cpp + Nes_Namco_Apu.cpp + Nes_Oscs.cpp + Nes_Vrc6_Apu.cpp + Nsf_Emu.cpp + ) +endif() + +if (USE_GME_NSFE) + set(libgme_SRCS ${libgme_SRCS} + Nsfe_Emu.cpp + ) +endif() + +if (USE_GME_SAP) + set(libgme_SRCS ${libgme_SRCS} + Sap_Apu.cpp + Sap_Cpu.cpp + Sap_Emu.cpp + ) +endif() + +if (USE_GME_SPC) + set(libgme_SRCS ${libgme_SRCS} + Snes_Spc.cpp + Spc_Cpu.cpp + Spc_Dsp.cpp + Spc_Emu.cpp + Spc_Filter.cpp + ) +endif() + +if (USE_GME_VGM) + set(libgme_SRCS ${libgme_SRCS} + # Sms_Apu.cpp included earlier + # Ym2612_Emu.cpp included earlier + Vgm_Emu.cpp + Vgm_Emu_Impl.cpp + Ym2413_Emu.cpp + ) +endif() + +# These headers are part of the generic gme interface. +set (EXPORTED_HEADERS gme.h) + +# On some platforms we may need to change headers or whatnot based on whether +# we're building the library or merely using the library. The following is +# only defined when building the library to allow us to tell which is which. + +#[ZMusic] Not needed +#add_definitions(-DBLARGG_BUILD_DLL) + +# For the gme_types.h +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +# Add library to be compiled. +add_library(gme OBJECT ${libgme_SRCS}) + +# Commented our since we use miniz +#if(ZLIB_FOUND) +# message(" ** ZLib library located, compressed file formats will be supported") + target_compile_definitions(gme PRIVATE -DHAVE_ZLIB_H) + target_link_libraries(gme PRIVATE miniz) + + # [ZMusic] Switch to modern target for better transitive handling. + # target_link_libraries(gme PRIVATE ZLIB::ZLIB) + # Is not to be installed though + +# set(PKG_CONFIG_ZLIB -lz) # evaluated in libgme.pc.in +#else() +# message("ZLib library not found, disabling support for compressed formats such as VGZ") +#endif() + +# [ZMusic] Interface include directories +target_include_directories(gme INTERFACE ..) + +# [ZMusic] Not needed. +if( FALSE ) +# The version is the release. The "soversion" is the API version. As long +# as only build fixes are performed (i.e. no backwards-incompatible changes +# to the API), the SOVERSION should be the same even when bumping up VERSION. +# The way gme.h is designed, SOVERSION should very rarely be bumped, if ever. +# Hopefully the API can stay compatible with old versions. +set_target_properties(gme + PROPERTIES VERSION ${GME_VERSION} + SOVERSION 0) + +install(TARGETS gme LIBRARY DESTINATION lib${LIB_SUFFIX} + RUNTIME DESTINATION bin # DLL platforms + ARCHIVE DESTINATION lib) # DLL platforms + +# Run during cmake phase, so this is available during make +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gme_types.h.in + ${CMAKE_CURRENT_BINARY_DIR}/gme_types.h) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libgme.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc @ONLY) + +install(FILES ${EXPORTED_HEADERS} DESTINATION include/gme) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig) +endif() diff --git a/thirdparty/game-music-emu/gme/Classic_Emu.cpp b/thirdparty/game-music-emu/gme/Classic_Emu.cpp new file mode 100644 index 000000000..c572d9b5c --- /dev/null +++ b/thirdparty/game-music-emu/gme/Classic_Emu.cpp @@ -0,0 +1,190 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Classic_Emu.h" + +#include "Multi_Buffer.h" +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Classic_Emu::Classic_Emu() +{ + buf = 0; + stereo_buffer = 0; + voice_types = 0; + + // avoid inconsistency in our duplicated constants + assert( (int) wave_type == (int) Multi_Buffer::wave_type ); + assert( (int) noise_type == (int) Multi_Buffer::noise_type ); + assert( (int) mixed_type == (int) Multi_Buffer::mixed_type ); +} + +Classic_Emu::~Classic_Emu() +{ + delete stereo_buffer; +} + +void Classic_Emu::set_equalizer_( equalizer_t const& eq ) +{ + Music_Emu::set_equalizer_( eq ); + update_eq( eq.treble ); + if ( buf ) + buf->bass_freq( (int) equalizer().bass ); +} + +blargg_err_t Classic_Emu::set_sample_rate_( long rate ) +{ + if ( !buf ) + { + if ( !stereo_buffer ) + CHECK_ALLOC( stereo_buffer = BLARGG_NEW Stereo_Buffer ); + buf = stereo_buffer; + } + return buf->set_sample_rate( rate, 1000 / 20 ); +} + +blargg_err_t Classic_Emu::set_multi_channel ( bool is_enabled ) +{ + RETURN_ERR( Music_Emu::set_multi_channel_( is_enabled ) ); + return 0; +} + +void Classic_Emu::mute_voices_( int mask ) +{ + Music_Emu::mute_voices_( mask ); + for ( int i = voice_count(); i--; ) + { + if ( mask & (1 << i) ) + { + set_voice( i, 0, 0, 0 ); + } + else + { + Multi_Buffer::channel_t ch = buf->channel( i, (voice_types ? voice_types [i] : 0) ); + assert( (ch.center && ch.left && ch.right) || + (!ch.center && !ch.left && !ch.right) ); // all or nothing + set_voice( i, ch.center, ch.left, ch.right ); + } + } +} + +void Classic_Emu::change_clock_rate( long rate ) +{ + clock_rate_ = rate; + buf->clock_rate( rate ); +} + +blargg_err_t Classic_Emu::setup_buffer( long rate ) +{ + change_clock_rate( rate ); + RETURN_ERR( buf->set_channel_count( voice_count() ) ); + set_equalizer( equalizer() ); + buf_changed_count = buf->channels_changed_count(); + return 0; +} + +blargg_err_t Classic_Emu::start_track_( int track ) +{ + RETURN_ERR( Music_Emu::start_track_( track ) ); + buf->clear(); + return 0; +} + +blargg_err_t Classic_Emu::play_( long count, sample_t* out ) +{ + long remain = count; + while ( remain ) + { + remain -= buf->read_samples( &out [count - remain], remain ); + if ( remain ) + { + if ( buf_changed_count != buf->channels_changed_count() ) + { + buf_changed_count = buf->channels_changed_count(); + remute_voices(); + } + int msec = buf->length(); + blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000; + RETURN_ERR( run_clocks( clocks_emulated, msec ) ); + assert( clocks_emulated ); + buf->end_frame( clocks_emulated ); + } + } + return 0; +} + +// Rom_Data + +blargg_err_t Rom_Data_::load_rom_data_( Data_Reader& in, + int header_size, void* header_out, int fill, long pad_size ) +{ + long file_offset = pad_size - header_size; + + rom_addr = 0; + mask = 0; + size_ = 0; + rom.clear(); + + file_size_ = in.remain(); + if ( file_size_ <= header_size ) // <= because there must be data after header + return gme_wrong_file_type; + blargg_err_t err = rom.resize( file_offset + file_size_ + pad_size ); + if ( !err ) + err = in.read( rom.begin() + file_offset, file_size_ ); + if ( err ) + { + rom.clear(); + return err; + } + + file_size_ -= header_size; + memcpy( header_out, &rom [file_offset], header_size ); + + memset( rom.begin() , fill, pad_size ); + memset( rom.end() - pad_size, fill, pad_size ); + + return 0; +} + +void Rom_Data_::set_addr_( long addr, int unit ) +{ + rom_addr = addr - unit - pad_extra; + + long rounded = (addr + file_size_ + unit - 1) / unit * unit; + if ( rounded <= 0 ) + { + rounded = 0; + } + else + { + int shift = 0; + unsigned long max_addr = (unsigned long) (rounded - 1); + while ( max_addr >> shift ) + shift++; + mask = (1L << shift) - 1; + } + + if ( addr < 0 ) + addr = 0; + size_ = rounded; + if ( rom.resize( rounded - rom_addr + pad_extra ) ) { } // OK if shrink fails + + if ( 0 ) + { + debug_printf( "addr: %X\n", addr ); + debug_printf( "file_size: %d\n", file_size_ ); + debug_printf( "rounded: %d\n", rounded ); + debug_printf( "mask: $%X\n", mask ); + } +} diff --git a/thirdparty/game-music-emu/gme/Classic_Emu.h b/thirdparty/game-music-emu/gme/Classic_Emu.h new file mode 100644 index 000000000..57cdd5c32 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Classic_Emu.h @@ -0,0 +1,128 @@ +// Common aspects of emulators which use Blip_Buffer for sound output + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef CLASSIC_EMU_H +#define CLASSIC_EMU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" +#include "Music_Emu.h" + +class Classic_Emu : public Music_Emu { +public: + Classic_Emu(); + ~Classic_Emu(); + void set_buffer( Multi_Buffer* ); + blargg_err_t set_multi_channel( bool is_enabled ) override; +protected: + // Services + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + void set_voice_types( int const* t ) { voice_types = t; } + blargg_err_t setup_buffer( long clock_rate ); + long clock_rate() const { return clock_rate_; } + void change_clock_rate( long ); // experimental + + // Overridable + virtual void set_voice( int index, Blip_Buffer* center, + Blip_Buffer* left, Blip_Buffer* right ) = 0; + virtual void update_eq( blip_eq_t const& ) = 0; + virtual blargg_err_t start_track_( int track ) = 0; + virtual blargg_err_t run_clocks( blip_time_t& time_io, int msec ) = 0; +protected: + blargg_err_t set_sample_rate_( long sample_rate ); + void mute_voices_( int ); + void set_equalizer_( equalizer_t const& ); + blargg_err_t play_( long, sample_t* ); +private: + Multi_Buffer* buf; + Multi_Buffer* stereo_buffer; // NULL if using custom buffer + long clock_rate_; + unsigned buf_changed_count; + int const* voice_types; +}; + +inline void Classic_Emu::set_buffer( Multi_Buffer* new_buf ) +{ + assert( !buf && new_buf ); + buf = new_buf; +} + +// ROM data handler, used by several Classic_Emu derivitives. Loads file data +// with padding on both sides, allowing direct use in bank mapping. The main purpose +// is to allow all file data to be loaded with only one read() call (for efficiency). + +class Rom_Data_ { +public: + typedef unsigned char byte; +protected: + enum { pad_extra = 8 }; + blargg_vector rom; + long file_size_; + blargg_long rom_addr; + blargg_long mask; + blargg_long size_; // TODO: eliminate + + blargg_err_t load_rom_data_( Data_Reader& in, int header_size, void* header_out, + int fill, long pad_size ); + void set_addr_( long addr, int unit ); +}; + +template +class Rom_Data : public Rom_Data_ { + enum { pad_size = unit + pad_extra }; +public: + // Load file data, using already-loaded header 'h' if not NULL. Copy header + // from loaded file data into *out and fill unmapped bytes with 'fill'. + blargg_err_t load( Data_Reader& in, int header_size, void* header_out, int fill ) + { + return load_rom_data_( in, header_size, header_out, fill, pad_size ); + } + + // Size of file data read in (excluding header) + long file_size() const { return file_size_; } + + // Pointer to beginning of file data + byte* begin() const { return rom.begin() + pad_size; } + + // Set address that file data should start at + void set_addr( long addr ) { set_addr_( addr, unit ); } + + // Free data + void clear() { rom.clear(); } + + // Size of data + start addr, rounded to a multiple of unit + long size() const { return size_; } + + // Pointer to unmapped page filled with same value + byte* unmapped() { return rom.begin(); } + + // Mask address to nearest power of two greater than size() + blargg_long mask_addr( blargg_long addr ) const + { + #ifdef check + check( addr <= mask ); + #endif + return addr & mask; + } + + // Pointer to page starting at addr. Returns unmapped() if outside data. + byte* at_addr( blargg_long addr ) + { + blargg_ulong offset = mask_addr( addr ) - rom_addr; + if ( offset > blargg_ulong (rom.size() - pad_size) ) + offset = 0; // unmapped + return &rom [offset]; + } +}; + +#ifndef GME_APU_HOOK + #define GME_APU_HOOK( emu, addr, data ) ((void) 0) +#endif + +#ifndef GME_FRAME_HOOK + #define GME_FRAME_HOOK( emu ) ((void) 0) +#else + #define GME_FRAME_HOOK_DEFINED 1 +#endif + +#endif diff --git a/thirdparty/game-music-emu/gme/Data_Reader.cpp b/thirdparty/game-music-emu/gme/Data_Reader.cpp new file mode 100644 index 000000000..bbd982051 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Data_Reader.cpp @@ -0,0 +1,450 @@ +// File_Extractor 0.4.0. http://www.slack.net/~ant/ + +#include "Data_Reader.h" + +#include "blargg_endian.h" +#include +#include +#include + +/* Copyright (C) 2005-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef HAVE_ZLIB_H +// [ZMusic] Use miniz. +#include +#include +#include +static const unsigned char gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ +#endif /* HAVE_ZLIB_H */ + +const char Data_Reader::eof_error [] = "Unexpected end of file"; + +#define RETURN_VALIDITY_CHECK( cond ) \ + do { if ( unlikely( !(cond) ) ) return "Corrupt file"; } while(0) + +blargg_err_t Data_Reader::read( void* p, long s ) +{ + RETURN_VALIDITY_CHECK( s > 0 ); + + long result = read_avail( p, s ); + if ( result != s ) + { + if ( result >= 0 && result < s ) + return eof_error; + + return "Read error"; + } + + return 0; +} + +blargg_err_t Data_Reader::skip( long count ) +{ + RETURN_VALIDITY_CHECK( count >= 0 ); + + char buf [512]; + while ( count ) + { + long n = sizeof buf; + if ( n > count ) + n = count; + count -= n; + RETURN_ERR( read( buf, n ) ); + } + return 0; +} + +long File_Reader::remain() const { return size() - tell(); } + +blargg_err_t File_Reader::skip( long n ) +{ + RETURN_VALIDITY_CHECK( n >= 0 ); + + if ( !n ) + return 0; + return seek( tell() + n ); +} + +// Subset_Reader + +Subset_Reader::Subset_Reader( Data_Reader* dr, long size ) +{ + in = dr; + remain_ = dr->remain(); + if ( remain_ > size ) + remain_ = max( 0l, size ); +} + +long Subset_Reader::remain() const { return remain_; } + +long Subset_Reader::read_avail( void* p, long s ) +{ + s = max( 0l, s ); + if ( s > remain_ ) + s = remain_; + remain_ -= s; + return in->read_avail( p, s ); +} + +// Remaining_Reader + +Remaining_Reader::Remaining_Reader( void const* h, long size, Data_Reader* r ) +{ + header = (char const*) h; + header_end = header + max( 0l, size ); + in = r; +} + +long Remaining_Reader::remain() const { return header_end - header + in->remain(); } + +long Remaining_Reader::read_first( void* out, long count ) +{ + count = max( 0l, count ); + long first = header_end - header; + if ( first ) + { + if ( first > count || first < 0 ) + first = count; + void const* old = header; + header += first; + memcpy( out, old, (size_t) first ); + } + return first; +} + +long Remaining_Reader::read_avail( void* out, long count ) +{ + count = max( 0l, count ); + long first = read_first( out, count ); + long second = max( 0l, count - first ); + if ( second ) + { + second = in->read_avail( (char*) out + first, second ); + if ( second <= 0 ) + return second; + } + return first + second; +} + +blargg_err_t Remaining_Reader::read( void* out, long count ) +{ + count = max( 0l, count ); + long first = read_first( out, count ); + long second = max( 0l, count - first ); + if ( !second ) + return 0; + return in->read( (char*) out + first, second ); +} + +// Mem_File_Reader + +Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : + m_begin( (const char*) p ), + m_size( max( 0l, s ) ), + m_pos( 0l ) +{ +#ifdef HAVE_ZLIB_H + if( !m_begin ) + return; + + if ( gz_decompress() ) + { + debug_printf( "Loaded compressed data\n" ); + m_ownedPtr = true; + } +#endif /* HAVE_ZLIB_H */ +} + +#ifdef HAVE_ZLIB_H +Mem_File_Reader::~Mem_File_Reader() +{ + if ( m_ownedPtr ) + free( const_cast( m_begin ) ); // see gz_compress for the malloc +} +#endif + +long Mem_File_Reader::size() const { return m_size; } + +long Mem_File_Reader::read_avail( void* p, long s ) +{ + long r = remain(); + if ( s > r || s < 0 ) + s = r; + memcpy( p, m_begin + m_pos, static_cast(s) ); + m_pos += s; + return s; +} + +long Mem_File_Reader::tell() const { return m_pos; } + +blargg_err_t Mem_File_Reader::seek( long n ) +{ + RETURN_VALIDITY_CHECK( n >= 0 ); + if ( n > m_size ) + return eof_error; + m_pos = n; + return 0; +} + +#ifdef HAVE_ZLIB_H + +bool Mem_File_Reader::gz_decompress() +{ + if ( m_size >= 2 && memcmp(m_begin, gz_magic, 2) != 0 ) + { + /* Don't try to decompress non-GZ files, just assign input pointer */ + return false; + } + + using vec_size = size_t; + const vec_size full_length = static_cast( m_size ); + const vec_size half_length = static_cast( m_size / 2 ); + + // We use malloc/friends here so we can realloc to grow buffer if needed + char *raw_data = reinterpret_cast ( malloc( full_length ) ); + size_t raw_data_size = full_length; + if ( !raw_data ) + return false; + + z_stream strm; + strm.next_in = const_cast( reinterpret_cast( m_begin ) ); + strm.avail_in = static_cast( m_size ); + strm.total_out = 0; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + + bool done = false; + + // Adding 16 sets bit 4, which enables zlib to auto-detect the + // header. + if ( inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK ) + { + free( raw_data ); + return false; + } + + while ( !done ) + { + /* If our output buffer is too small */ + if ( strm.total_out >= raw_data_size ) + { + raw_data_size += half_length; + raw_data = reinterpret_cast( realloc( raw_data, raw_data_size ) ); + if ( !raw_data ) { + return false; + } + } + + strm.next_out = reinterpret_cast( raw_data + strm.total_out ); + strm.avail_out = static_cast( static_cast( raw_data_size ) - strm.total_out ); + + /* Inflate another chunk. */ + int err = inflate( &strm, Z_SYNC_FLUSH ); + if ( err == Z_STREAM_END ) + done = true; + else if ( err != Z_OK ) + break; + } + + if ( inflateEnd(&strm) != Z_OK ) + { + free( raw_data ); + return false; + } + + m_begin = raw_data; + m_size = static_cast( strm.total_out ); + + return true; +} + +#endif /* HAVE_ZLIB_H */ + + +// Callback_Reader + +Callback_Reader::Callback_Reader( callback_t c, long size, void* d ) : + callback( c ), + data( d ) +{ + remain_ = max( 0l, size ); +} + +long Callback_Reader::remain() const { return remain_; } + +long Callback_Reader::read_avail( void* out, long count ) +{ + if ( count > remain_ ) + count = remain_; + if ( count < 0 || Callback_Reader::read( out, count ) ) + count = -1; + return count; +} + +blargg_err_t Callback_Reader::read( void* out, long count ) +{ + RETURN_VALIDITY_CHECK( count >= 0 ); + if ( count > remain_ ) + return eof_error; + return callback( data, out, (int) count ); +} + +// Std_File_Reader + +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + +static const char* get_gzip_eof( const char* path, long* eof ) +{ + FILE* file = fopen( path, "rb" ); + if ( !file ) + return "Couldn't open file"; + + unsigned char buf [4]; + bool found_eof = false; + if ( fread( buf, 2, 1, file ) > 0 && buf [0] == 0x1F && buf [1] == 0x8B ) + { + fseek( file, -4, SEEK_END ); + if ( fread( buf, 4, 1, file ) > 0 ) { + *eof = get_le32( buf ); + found_eof = true; + } + } + if ( !found_eof ) + { + fseek( file, 0, SEEK_END ); + *eof = ftell( file ); + } + const char* err = (ferror( file ) || feof( file )) ? "Couldn't get file size" : nullptr; + fclose( file ); + return err; +} +#endif + + +Std_File_Reader::Std_File_Reader() : + file_( nullptr ) +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + , size_( 0 ) +#endif +{ } + +Std_File_Reader::~Std_File_Reader() { close(); } + +blargg_err_t Std_File_Reader::open( const char* path ) +{ +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + // zlib transparently handles uncompressed data if magic header + // not present but we still need to grab size + RETURN_ERR( get_gzip_eof( path, &size_ ) ); + file_ = gzopen( path, "rb" ); +#else + file_ = fopen( path, "rb" ); +#endif + + if ( !file_ ) + return "Couldn't open file"; + return nullptr; +} + +long Std_File_Reader::size() const +{ +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + if ( file_ ) + return size_; // Set for both compressed and uncompressed modes +#endif + long pos = tell(); + fseek( (FILE*) file_, 0, SEEK_END ); + long result = tell(); + fseek( (FILE*) file_, pos, SEEK_SET ); + return result; +} + +long Std_File_Reader::read_avail( void* p, long s ) +{ +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + if ( file_ && s > 0 && s <= UINT_MAX ) { + return gzread( reinterpret_cast(file_), + p, static_cast(s) ); + } + return 0l; +#else + const size_t readLength = static_cast( max( 0l, s ) ); + const auto result = fread( p, 1, readLength, reinterpret_cast(file_) ); + return static_cast( result ); +#endif /* HAVE_ZLIB_H */ +} + +blargg_err_t Std_File_Reader::read( void* p, long s ) +{ + RETURN_VALIDITY_CHECK( s > 0 && s <= UINT_MAX ); +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + if ( file_ ) + { + const auto &gzfile = reinterpret_cast( file_ ); + if ( s == gzread( gzfile, p, static_cast( s ) ) ) + return nullptr; + if ( gzeof( gzfile ) ) + return eof_error; + return "Couldn't read from GZ file"; + } +#endif + const auto &file = reinterpret_cast( file_ ); + if ( s == static_cast( fread( p, 1, static_cast(s), file ) ) ) + return 0; + if ( feof( file ) ) + return eof_error; + return "Couldn't read from file"; +} + +long Std_File_Reader::tell() const +{ +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + if ( file_ ) + return gztell( reinterpret_cast( file_ ) ); +#endif + return ftell( reinterpret_cast( file_ ) ); +} + +blargg_err_t Std_File_Reader::seek( long n ) +{ +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + if ( file_ ) + { + if ( gzseek( reinterpret_cast( file_ ), n, SEEK_SET ) >= 0 ) + return nullptr; + if ( n > size_ ) + return eof_error; + return "Error seeking in GZ file"; + } +#endif + if ( !fseek( reinterpret_cast( file_ ), n, SEEK_SET ) ) + return nullptr; + if ( n > size() ) + return eof_error; + return "Error seeking in file"; +} + +void Std_File_Reader::close() +{ + if ( file_ ) + { +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + gzclose( reinterpret_cast( file_ ) ); +#else + fclose( reinterpret_cast( file_ ) ); +#endif + file_ = nullptr; + } +} + diff --git a/thirdparty/game-music-emu/gme/Data_Reader.h b/thirdparty/game-music-emu/gme/Data_Reader.h new file mode 100644 index 000000000..8ff82f3a9 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Data_Reader.h @@ -0,0 +1,150 @@ +// Data reader interface for uniform access + +// File_Extractor 0.4.0 +#ifndef DATA_READER_H +#define DATA_READER_H + +#include "blargg_common.h" + +#ifdef HAVE_ZLIB_H +// [ZMusic] Use miniz. +#include +#endif + +// Supports reading and finding out how many bytes are remaining +class Data_Reader { +public: + virtual ~Data_Reader() { } + + static const char eof_error []; // returned by read() when request goes beyond end + + // Read at most count bytes and return number actually read, or <= 0 if error + virtual long read_avail( void*, long n ) = 0; + + // Read exactly count bytes and return error if they couldn't be read + virtual blargg_err_t read( void*, long count ); + + // Number of bytes remaining until end of file + virtual long remain() const = 0; + + // Read and discard count bytes + virtual blargg_err_t skip( long count ); + +public: + Data_Reader() { } + typedef blargg_err_t error_t; // deprecated +private: + // noncopyable + Data_Reader( const Data_Reader& ); + Data_Reader& operator = ( const Data_Reader& ); +}; + +// Supports seeking in addition to Data_Reader operations +class File_Reader : public Data_Reader { +public: + // Size of file + virtual long size() const = 0; + + // Current position in file + virtual long tell() const = 0; + + // Go to new position + virtual blargg_err_t seek( long ) = 0; + + long remain() const; + blargg_err_t skip( long n ); +}; + +// Disk file reader +class Std_File_Reader : public File_Reader { +public: + blargg_err_t open( const char* path ); + void close(); + +public: + Std_File_Reader(); + ~Std_File_Reader(); + long size() const; + blargg_err_t read( void*, long ); + long read_avail( void*, long ); + long tell() const; + blargg_err_t seek( long ); +private: + void* file_; // Either FILE* or zlib's gzFile +#if 0//[ZDOOM:unneeded] def HAVE_ZLIB_H + long size_; // TODO: Fix ABI compat +#endif /* HAVE_ZLIB_H */ +}; + +// Treats range of memory as a file +class Mem_File_Reader : public File_Reader { +public: + Mem_File_Reader( const void*, long size ); +#ifdef HAVE_ZLIB_H + ~Mem_File_Reader( ); +#endif /* HAVE_ZLIB_H */ + +public: + long size() const; + long read_avail( void*, long ); + long tell() const; + blargg_err_t seek( long ); +private: +#ifdef HAVE_ZLIB_H + bool gz_decompress(); +#endif /* HAVE_ZLIB_H */ + + const char* m_begin; + long m_size; + long m_pos; +#ifdef HAVE_ZLIB_H + bool m_ownedPtr = false; // set if we must free m_begin +#endif /* HAVE_ZLIB_H */ +}; + + +// Makes it look like there are only count bytes remaining +class Subset_Reader : public Data_Reader { +public: + Subset_Reader( Data_Reader*, long count ); + +public: + long remain() const; + long read_avail( void*, long ); +private: + Data_Reader* in; + long remain_; +}; + +// Joins already-read header and remaining data into original file (to avoid seeking) +class Remaining_Reader : public Data_Reader { +public: + Remaining_Reader( void const* header, long size, Data_Reader* ); + +public: + long remain() const; + long read_avail( void*, long ); + blargg_err_t read( void*, long ); +private: + char const* header; + char const* header_end; + Data_Reader* in; + long read_first( void* out, long count ); +}; + +// Invokes callback function to read data. Size of data must be specified in advance. +class Callback_Reader : public Data_Reader { +public: + typedef const char* (*callback_t)( void* data, void* out, int count ); + Callback_Reader( callback_t, long size, void* data = 0 ); +public: + long read_avail( void*, long ); + blargg_err_t read( void*, long ); + long remain() const; +private: + callback_t const callback; + void* const data; + long remain_; +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Dual_Resampler.cpp b/thirdparty/game-music-emu/gme/Dual_Resampler.cpp new file mode 100644 index 000000000..e774d85f8 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Dual_Resampler.cpp @@ -0,0 +1,141 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Dual_Resampler.h" + +#include +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +unsigned const resampler_extra = 256; + +Dual_Resampler::Dual_Resampler() : + sample_buf_size(0), + oversamples_per_frame(-1), + buf_pos(-1), + resampler_size(0) +{ +} + +Dual_Resampler::~Dual_Resampler() { } + +blargg_err_t Dual_Resampler::reset( int pairs ) +{ + // expand allocations a bit + RETURN_ERR( sample_buf.resize( (pairs + (pairs >> 2)) * 2 ) ); + resize( pairs ); + resampler_size = oversamples_per_frame + (oversamples_per_frame >> 2); + return resampler.buffer_size( resampler_size ); +} + +void Dual_Resampler::resize( int pairs ) +{ + int new_sample_buf_size = pairs * 2; + if ( sample_buf_size != new_sample_buf_size ) + { + if ( (unsigned) new_sample_buf_size > sample_buf.size() ) + { + check( false ); + return; + } + sample_buf_size = new_sample_buf_size; + oversamples_per_frame = int (pairs * resampler.ratio()) * 2 + 2; + clear(); + } +} + +void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, dsample_t* out ) +{ + long pair_count = sample_buf_size >> 1; + blip_time_t blip_time = blip_buf.count_clocks( pair_count ); + int sample_count = oversamples_per_frame - resampler.written(); + + int new_count = play_frame( blip_time, sample_count, resampler.buffer() ); + assert( new_count < resampler_size ); + + blip_buf.end_frame( blip_time ); + assert( blip_buf.samples_avail() == pair_count ); + + resampler.write( new_count ); + +#ifdef NDEBUG // Avoid warning when asserts are disabled + resampler.read( sample_buf.begin(), sample_buf_size ); +#else + long count = resampler.read( sample_buf.begin(), sample_buf_size ); + assert( count == (long) sample_buf_size ); +#endif + + mix_samples( blip_buf, out ); + blip_buf.remove_samples( pair_count ); +} + +void Dual_Resampler::dual_play( long count, dsample_t* out, Blip_Buffer& blip_buf ) +{ + // empty extra buffer + long remain = sample_buf_size - buf_pos; + if ( remain ) + { + if ( remain > count ) + remain = count; + count -= remain; + memcpy( out, &sample_buf [buf_pos], remain * sizeof *out ); + out += remain; + buf_pos += remain; + } + + // entire frames + while ( count >= (long) sample_buf_size ) + { + play_frame_( blip_buf, out ); + out += sample_buf_size; + count -= sample_buf_size; + } + + // extra + if ( count ) + { + play_frame_( blip_buf, sample_buf.begin() ); + buf_pos = count; + memcpy( out, sample_buf.begin(), count * sizeof *out ); + out += count; + } +} + +void Dual_Resampler::mix_samples( Blip_Buffer& blip_buf, dsample_t* out ) +{ + Blip_Reader sn; + int bass = sn.begin( blip_buf ); + const dsample_t* in = sample_buf.begin(); + + for ( int n = sample_buf_size >> 1; n--; ) + { + int s = sn.read(); + blargg_long l = (blargg_long) in [0] * 2 + s; + if ( (int16_t) l != l ) + l = 0x7FFF - (l >> 24); + + sn.next( bass ); + blargg_long r = (blargg_long) in [1] * 2 + s; + if ( (int16_t) r != r ) + r = 0x7FFF - (r >> 24); + + in += 2; + out [0] = l; + out [1] = r; + out += 2; + } + + sn.end( blip_buf ); +} + diff --git a/thirdparty/game-music-emu/gme/Dual_Resampler.h b/thirdparty/game-music-emu/gme/Dual_Resampler.h new file mode 100644 index 000000000..512fd97d0 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Dual_Resampler.h @@ -0,0 +1,50 @@ +// Combination of Fir_Resampler and Blip_Buffer mixing. Used by Sega FM emulators. + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef DUAL_RESAMPLER_H +#define DUAL_RESAMPLER_H + +#include "Fir_Resampler.h" +#include "Blip_Buffer.h" + +class Dual_Resampler { +public: + Dual_Resampler(); + virtual ~Dual_Resampler(); + + typedef short dsample_t; + + double setup( double oversample, double rolloff, double gain ); + blargg_err_t reset( int max_pairs ); + void resize( int pairs_per_frame ); + void clear(); + + void dual_play( long count, dsample_t* out, Blip_Buffer& ); + +protected: + virtual int play_frame( blip_time_t, int pcm_count, dsample_t* pcm_out ) = 0; +private: + + blargg_vector sample_buf; + int sample_buf_size; + int oversamples_per_frame; + int buf_pos; + int resampler_size; + + Fir_Resampler<12> resampler; + void mix_samples( Blip_Buffer&, dsample_t* ); + void play_frame_( Blip_Buffer&, dsample_t* ); +}; + +inline double Dual_Resampler::setup( double oversample, double rolloff, double gain ) +{ + return resampler.time_ratio( oversample, rolloff, gain * 0.5 ); +} + +inline void Dual_Resampler::clear() +{ + buf_pos = sample_buf_size; + resampler.clear(); +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Effects_Buffer.cpp b/thirdparty/game-music-emu/gme/Effects_Buffer.cpp new file mode 100644 index 000000000..56b0c5b5c --- /dev/null +++ b/thirdparty/game-music-emu/gme/Effects_Buffer.cpp @@ -0,0 +1,595 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Effects_Buffer.h" + +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +typedef blargg_long fixed_t; + +#define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5) +#define FMUL( x, y ) (((x) * (y)) >> 15) + +const unsigned echo_size = 4096; +const unsigned echo_mask = echo_size - 1; +BOOST_STATIC_ASSERT( (echo_size & echo_mask) == 0 ); // must be power of 2 + +const unsigned reverb_size = 8192 * 2; +const unsigned reverb_mask = reverb_size - 1; +BOOST_STATIC_ASSERT( (reverb_size & reverb_mask) == 0 ); // must be power of 2 + +Effects_Buffer::config_t::config_t() +{ + pan_1 = -0.15f; + pan_2 = 0.15f; + reverb_delay = 88.0f; + reverb_level = 0.12f; + echo_delay = 61.0f; + echo_level = 0.10f; + delay_variance = 18.0f; + effects_enabled = false; +} + +void Effects_Buffer::set_depth( double d ) +{ + float f = (float) d; + config_t c; + c.pan_1 = -0.6f * f; + c.pan_2 = 0.6f * f; + c.reverb_delay = 880 * 0.1f; + c.echo_delay = 610 * 0.1f; + if ( f > 0.5 ) + f = 0.5; // TODO: more linear reduction of extreme reverb/echo + c.reverb_level = 0.5f * f; + c.echo_level = 0.30f * f; + c.delay_variance = 180 * 0.1f; + c.effects_enabled = (d > 0.0f); + config( c ); +} + +Effects_Buffer::Effects_Buffer( int num_voices, bool center_only ) + : Multi_Buffer( 2*num_voices ) + , max_voices(num_voices) + , bufs(max_voices * (center_only ? (max_buf_count - 4) : max_buf_count)) + , chan_types(max_voices * chan_types_count) + , stereo_remain(0) + , effect_remain(0) + // TODO: Reorder buf_count to be initialized before bufs to factor out channel sizing + , buf_count(max_voices * (center_only ? (max_buf_count - 4) : max_buf_count)) + , effects_enabled(false) + , reverb_buf(max_voices, std::vector(reverb_size)) + , echo_buf(max_voices, std::vector(echo_size)) + , reverb_pos(max_voices) + , echo_pos(max_voices) +{ + set_depth( 0 ); +} + +Effects_Buffer::~Effects_Buffer() +{} + +blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) +{ + try + { + for(int i=0; i max ) + return max; + return n; +} + +void Effects_Buffer::config( const config_t& cfg ) +{ + channels_changed(); + + // clear echo and reverb buffers + // ensure the echo/reverb buffers have already been allocated, so this method can be + // called before set_sample_rate is called + if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf[0].size() ) + { + for(int i=0; i chan_types_count-1 ) + out = chan_types_count-1; + } + else if ( !(type & noise_type) && (type & type_index_mask) % 3 != 0 ) + { + out = type & 1; + } + return chan_types [(i%max_voices)*chan_types_count+out]; +} + +void Effects_Buffer::end_frame( blip_time_t clock_count ) +{ + int bufs_used = 0; + int stereo_mask = (config_.effects_enabled ? 0x78 : 0x06); + + const int buf_count_per_voice = buf_count/max_voices; + for ( int v = 0; v < max_voices; v++ ) // foreach voice + { + for ( int i = 0; i < buf_count_per_voice; i++) // foreach buffer of that voice + { + bufs_used |= bufs [v*buf_count_per_voice + i].clear_modified() << i; + bufs [v*buf_count_per_voice + i].end_frame( clock_count ); + + if ( (bufs_used & stereo_mask) && buf_count == max_voices*max_buf_count ) + stereo_remain = max(stereo_remain, bufs [v*buf_count_per_voice + i].samples_avail() + bufs [v*buf_count_per_voice + i].output_latency()); + if ( effects_enabled || config_.effects_enabled ) + effect_remain = max(effect_remain, bufs [v*buf_count_per_voice + i].samples_avail() + bufs [v*buf_count_per_voice + i].output_latency()); + } + bufs_used = 0; + } + + effects_enabled = config_.effects_enabled; +} + +long Effects_Buffer::samples_avail() const +{ + return bufs [0].samples_avail() * 2; +} + +long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples ) +{ + const int n_channels = max_voices * 2; + const int buf_count_per_voice = buf_count/max_voices; + + require( total_samples % n_channels == 0 ); // as many items needed to fill at least one frame + + long remain = bufs [0].samples_avail(); + total_samples = remain = min( remain, total_samples/n_channels ); + + while ( remain ) + { + int active_bufs = buf_count_per_voice; + long count = remain; + + // optimizing mixing to skip any channels which had nothing added + if ( effect_remain ) + { + if ( count > effect_remain ) + count = effect_remain; + + if ( stereo_remain ) + { + mix_enhanced( out, count ); + } + else + { + mix_mono_enhanced( out, count ); + active_bufs = 3; + } + } + else if ( stereo_remain ) + { + mix_stereo( out, count ); + active_bufs = 3; + } + else + { + mix_mono( out, count ); + active_bufs = 1; + } + + out += count * n_channels; + remain -= count; + + stereo_remain -= count; + if ( stereo_remain < 0 ) + stereo_remain = 0; + + effect_remain -= count; + if ( effect_remain < 0 ) + effect_remain = 0; + + // skip the output from any buffers that didn't contribute to the sound output + // during this frame (e.g. if we only render mono then only the very first buf + // is 'active') + for ( int v = 0; v < max_voices; v++ ) // foreach voice + { + for ( int i = 0; i < buf_count_per_voice; i++) // foreach buffer of that voice + { + if ( i < active_bufs ) + bufs [v*buf_count_per_voice + i].remove_samples( count ); + else // keep time synchronized + bufs [v*buf_count_per_voice + i].remove_silence( count ); + } + } + } + + return total_samples * n_channels; +} + +void Effects_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) +{ + for(int i=0; i> 1; n; --n ) + { + blargg_long cs0 = BLIP_READER_READ( c ); + BLIP_READER_NEXT( c, bass ); + + blargg_long cs1 = BLIP_READER_READ( c ); + BLIP_READER_NEXT( c, bass ); + + if ( (int16_t) cs0 != cs0 ) + cs0 = 0x7FFF - (cs0 >> 24); + ((uint32_t*) out) [i*2+0] = ((uint16_t) cs0) | (uint16_t(cs0) << 16); + + if ( (int16_t) cs1 != cs1 ) + cs1 = 0x7FFF - (cs1 >> 24); + ((uint32_t*) out) [i*2+1] = ((uint16_t) cs1) | (uint16_t(cs1) << 16); + out += max_voices*4; + } + + if ( count & 1 ) + { + int s = BLIP_READER_READ( c ); + BLIP_READER_NEXT( c, bass ); + out [i*2+0] = s; + out [i*2+1] = s; + if ( (int16_t) s != s ) + { + s = 0x7FFF - (s >> 24); + out [i*2+0] = s; + out [i*2+1] = s; + } + } + + BLIP_READER_END( c, bufs [i*max_buf_count+0] ); + } +} + +void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long frames ) +{ + for(int i=0; i> 24); + + if ( (int16_t) right != right ) + right = 0x7FFF - (right >> 24); + + out [i*2+0] = left; + out [i*2+1] = right; + + out += max_voices*2; + + } + + BLIP_READER_END( r, bufs [i*max_buf_count+2] ); + BLIP_READER_END( l, bufs [i*max_buf_count+1] ); + BLIP_READER_END( c, bufs [i*max_buf_count+0] ); + } +} + +void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long frames ) +{ + for(int i=0; ireverb_buf[i][0]; + blip_sample_t* const echo_buf = &this->echo_buf[i][0]; + int echo_pos = this->echo_pos[i]; + int reverb_pos = this->reverb_pos[i]; + + int count = frames; + while ( count-- ) + { + int sum1_s = BLIP_READER_READ( sq1 ); + int sum2_s = BLIP_READER_READ( sq2 ); + + BLIP_READER_NEXT( sq1, bass ); + BLIP_READER_NEXT( sq2, bass ); + + int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + + FMUL( sum2_s, chans.pan_2_levels [0] ) + + reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; + + int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + + FMUL( sum2_s, chans.pan_2_levels [1] ) + + reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; + + fixed_t reverb_level = chans.reverb_level; + reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); + reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); + reverb_pos = (reverb_pos + 2) & reverb_mask; + + int sum3_s = BLIP_READER_READ( center ); + BLIP_READER_NEXT( center, bass ); + + int left = new_reverb_l + sum3_s + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); + int right = new_reverb_r + sum3_s + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); + + echo_buf [echo_pos] = sum3_s; + echo_pos = (echo_pos + 1) & echo_mask; + + if ( (int16_t) left != left ) + left = 0x7FFF - (left >> 24); + + if ( (int16_t) right != right ) + right = 0x7FFF - (right >> 24); + + out [i*2+0] = left; + out [i*2+1] = right; + out += max_voices*2; + } + this->reverb_pos[i] = reverb_pos; + this->echo_pos[i] = echo_pos; + + BLIP_READER_END( sq1, bufs [i*max_buf_count+0] ); + BLIP_READER_END( sq2, bufs [i*max_buf_count+1] ); + BLIP_READER_END( center, bufs [i*max_buf_count+2] ); + } +} + +void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long frames ) +{ + for(int i=0; ireverb_buf[i][0]; + blip_sample_t* const echo_buf = &this->echo_buf[i][0]; + int echo_pos = this->echo_pos[i]; + int reverb_pos = this->reverb_pos[i]; + + int count = frames; + while ( count-- ) + { + int sum1_s = BLIP_READER_READ( sq1 ); + int sum2_s = BLIP_READER_READ( sq2 ); + + BLIP_READER_NEXT( sq1, bass ); + BLIP_READER_NEXT( sq2, bass ); + + int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + + FMUL( sum2_s, chans.pan_2_levels [0] ) + BLIP_READER_READ( l1 ) + + reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; + + int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + + FMUL( sum2_s, chans.pan_2_levels [1] ) + BLIP_READER_READ( r1 ) + + reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; + + BLIP_READER_NEXT( l1, bass ); + BLIP_READER_NEXT( r1, bass ); + + fixed_t reverb_level = chans.reverb_level; + reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); + reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); + reverb_pos = (reverb_pos + 2) & reverb_mask; + + int sum3_s = BLIP_READER_READ( center ); + BLIP_READER_NEXT( center, bass ); + + int left = new_reverb_l + sum3_s + BLIP_READER_READ( l2 ) + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); + int right = new_reverb_r + sum3_s + BLIP_READER_READ( r2 ) + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); + + BLIP_READER_NEXT( l2, bass ); + BLIP_READER_NEXT( r2, bass ); + + echo_buf [echo_pos] = sum3_s; + echo_pos = (echo_pos + 1) & echo_mask; + + if ( (int16_t) left != left ) + left = 0x7FFF - (left >> 24); + + if ( (int16_t) right != right ) + right = 0x7FFF - (right >> 24); + + out [i*2+0] = left; + out [i*2+1] = right; + + out += max_voices*2; + } + this->reverb_pos[i] = reverb_pos; + this->echo_pos[i] = echo_pos; + + BLIP_READER_END( l1, bufs [i*max_buf_count+3] ); + BLIP_READER_END( r1, bufs [i*max_buf_count+4] ); + BLIP_READER_END( l2, bufs [i*max_buf_count+5] ); + BLIP_READER_END( r2, bufs [i*max_buf_count+6] ); + BLIP_READER_END( sq1, bufs [i*max_buf_count+0] ); + BLIP_READER_END( sq2, bufs [i*max_buf_count+1] ); + BLIP_READER_END( center, bufs [i*max_buf_count+2] ); + } +} + diff --git a/thirdparty/game-music-emu/gme/Effects_Buffer.h b/thirdparty/game-music-emu/gme/Effects_Buffer.h new file mode 100644 index 000000000..ec634d622 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Effects_Buffer.h @@ -0,0 +1,90 @@ +// Multi-channel effects buffer with panning, echo and reverb + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef EFFECTS_BUFFER_H +#define EFFECTS_BUFFER_H + +#include "Multi_Buffer.h" + +#include + +// Effects_Buffer uses several buffers and outputs stereo sample pairs. +class Effects_Buffer : public Multi_Buffer { +public: + // nVoices indicates the number of voices for which buffers will be allocated + // to make Effects_Buffer work as "mix everything to one", nVoices will be 1 + // If center_only is true, only center buffers are created and + // less memory is used. + Effects_Buffer( int nVoices = 1, bool center_only = false ); + + // Channel Effect Center Pan + // --------------------------------- + // 0,5 reverb pan_1 + // 1,6 reverb pan_2 + // 2,7 echo - + // 3 echo - + // 4 echo - + + // Channel configuration + struct config_t { + double pan_1; // -1.0 = left, 0.0 = center, 1.0 = right + double pan_2; + double echo_delay; // msec + double echo_level; // 0.0 to 1.0 + double reverb_delay; // msec + double delay_variance; // difference between left/right delays (msec) + double reverb_level; // 0.0 to 1.0 + bool effects_enabled; // if false, use optimized simple mixer + config_t(); + }; + + // Set configuration of buffer + virtual void config( const config_t& ); + void set_depth( double ); + +public: + ~Effects_Buffer(); + blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length ); + void clock_rate( long ); + void bass_freq( int ); + void clear(); + channel_t channel( int, int ); + void end_frame( blip_time_t ); + long read_samples( blip_sample_t*, long ); + long samples_avail() const; +private: + typedef long fixed_t; + int max_voices; + enum { max_buf_count = 7 }; + std::vector bufs; + enum { chan_types_count = 3 }; + std::vector chan_types; + config_t config_; + long stereo_remain; + long effect_remain; + int buf_count; + bool effects_enabled; + + std::vector > reverb_buf; + std::vector > echo_buf; + std::vector reverb_pos; + std::vector echo_pos; + + struct { + fixed_t pan_1_levels [2]; + fixed_t pan_2_levels [2]; + int echo_delay_l; + int echo_delay_r; + fixed_t echo_level; + int reverb_delay_l; + int reverb_delay_r; + fixed_t reverb_level; + } chans; + + void mix_mono( blip_sample_t*, blargg_long ); + void mix_stereo( blip_sample_t*, blargg_long ); + void mix_enhanced( blip_sample_t*, blargg_long ); + void mix_mono_enhanced( blip_sample_t*, blargg_long ); +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Fir_Resampler.cpp b/thirdparty/game-music-emu/gme/Fir_Resampler.cpp new file mode 100644 index 000000000..d8dd6837c --- /dev/null +++ b/thirdparty/game-music-emu/gme/Fir_Resampler.cpp @@ -0,0 +1,199 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Fir_Resampler.h" + +#include +#include +#include +#include + +/* Copyright (C) 2004-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( double rolloff, int width, double offset, double spacing, double scale, + int count, short* out ) +{ + double const maxh = 256; + double const step = PI / maxh * spacing; + double const to_w = maxh * 2 / width; + double const pow_a_n = pow( rolloff, maxh ); + scale /= maxh * 2; + + double angle = (count / 2 - 1 + offset) * -step; + while ( count-- ) + { + *out++ = 0; + double w = angle * to_w; + if ( fabs( w ) < PI ) + { + double rolloff_cos_a = rolloff * cos( angle ); + double num = 1 - rolloff_cos_a - + pow_a_n * cos( maxh * angle ) + + pow_a_n * rolloff * cos( (maxh - 1) * angle ); + double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + double sinc = scale * num / den - scale; + + out [-1] = (short) (cos( w ) * sinc + sinc); + } + angle += step; + } +} + +Fir_Resampler_::Fir_Resampler_( int width, sample_t* impulses_ ) : + width_( width ), + write_offset( width * stereo - stereo ), + impulses( impulses_ ) +{ + write_pos = 0; + res = 1; + imp_phase = 0; + skip_bits = 0; + step = stereo; + ratio_ = 1.0; +} + +Fir_Resampler_::~Fir_Resampler_() { } + +void Fir_Resampler_::clear() +{ + imp_phase = 0; + if ( buf.size() ) + { + write_pos = &buf [write_offset]; + memset( buf.begin(), 0, write_offset * sizeof buf [0] ); + } +} + +blargg_err_t Fir_Resampler_::buffer_size( int new_size ) +{ + RETURN_ERR( buf.resize( new_size + write_offset ) ); + clear(); + return 0; +} + +double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain ) +{ + ratio_ = new_factor; + + double fstep = 0.0; + { + double least_error = 2; + double pos = 0; + res = -1; + for ( int r = 1; r <= max_res; r++ ) + { + pos += ratio_; + double nearest = floor( pos + 0.5 ); + double error = fabs( pos - nearest ); + if ( error < least_error ) + { + res = r; + fstep = nearest / res; + least_error = error; + } + } + } + + skip_bits = 0; + + step = stereo * (int) floor( fstep ); + + ratio_ = fstep; + fstep = fmod( fstep, 1.0 ); + + double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; + double pos = 0.0; + input_per_cycle = 0; + for ( int i = 0; i < res; i++ ) + { + gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter, + double (0x7FFF * gain * filter), + (int) width_, impulses + i * width_ ); + + pos += fstep; + input_per_cycle += step; + if ( pos >= 0.9999999 ) + { + pos -= 1.0; + skip_bits |= 1 << i; + input_per_cycle++; + } + } + + clear(); + + return ratio_; +} + +int Fir_Resampler_::input_needed( blargg_long output_count ) const +{ + blargg_long input_count = 0; + + unsigned long skip = skip_bits >> imp_phase; + int remain = res - imp_phase; + while ( (output_count -= 2) > 0 ) + { + input_count += step + (skip & 1) * stereo; + skip >>= 1; + if ( !--remain ) + { + skip = skip_bits; + remain = res; + } + output_count -= 2; + } + + long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]); + if ( input_extra < 0 ) + input_extra = 0; + return input_extra; +} + +int Fir_Resampler_::avail_( blargg_long input_count ) const +{ + int cycle_count = input_count / input_per_cycle; + int output_count = cycle_count * res * stereo; + input_count -= cycle_count * input_per_cycle; + + blargg_ulong skip = skip_bits >> imp_phase; + int remain = res - imp_phase; + while ( input_count >= 0 ) + { + input_count -= step + (skip & 1) * stereo; + skip >>= 1; + if ( !--remain ) + { + skip = skip_bits; + remain = res; + } + output_count += 2; + } + return output_count; +} + +int Fir_Resampler_::skip_input( long count ) +{ + int remain = write_pos - buf.begin(); + int max_count = remain - width_ * stereo; + if ( count > max_count ) + count = max_count; + + remain -= count; + write_pos = &buf [remain]; + memmove( buf.begin(), &buf [count], remain * sizeof buf [0] ); + + return count; +} diff --git a/thirdparty/game-music-emu/gme/Fir_Resampler.h b/thirdparty/game-music-emu/gme/Fir_Resampler.h new file mode 100644 index 000000000..d637ec41c --- /dev/null +++ b/thirdparty/game-music-emu/gme/Fir_Resampler.h @@ -0,0 +1,171 @@ +// Finite impulse response (FIR) resampler with adjustable FIR size + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef FIR_RESAMPLER_H +#define FIR_RESAMPLER_H + +#include "blargg_common.h" +#include + +class Fir_Resampler_ { +public: + + // Use Fir_Resampler (below) + + // Set input/output resampling ratio and optionally low-pass rolloff and gain. + // Returns actual ratio used (rounded to internal precision). + double time_ratio( double factor, double rolloff = 0.999, double gain = 1.0 ); + + // Current input/output ratio + double ratio() const { return ratio_; } + +// Input + + typedef short sample_t; + + // Resize and clear input buffer + blargg_err_t buffer_size( int ); + + // Clear input buffer. At least two output samples will be available after + // two input samples are written. + void clear(); + + // Number of input samples that can be written + int max_write() const { return buf.end() - write_pos; } + + // Pointer to place to write input samples + sample_t* buffer() { return write_pos; } + + // Notify resampler that 'count' input samples have been written + void write( long count ); + + // Number of input samples in buffer + int written() const { return write_pos - &buf [write_offset]; } + + // Skip 'count' input samples. Returns number of samples actually skipped. + int skip_input( long count ); + +// Output + + // Number of extra input samples needed until 'count' output samples are available + int input_needed( blargg_long count ) const; + + // Number of output samples available + int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); } + +public: + ~Fir_Resampler_(); +protected: + enum { stereo = 2 }; + enum { max_res = 32 }; + blargg_vector buf; + sample_t* write_pos; + int res; + int imp_phase; + int const width_; + int const write_offset; + blargg_ulong skip_bits; + int step; + int input_per_cycle; + double ratio_; + sample_t* impulses; + + Fir_Resampler_( int width, sample_t* ); + int avail_( blargg_long input_count ) const; +}; + +// Width is number of points in FIR. Must be even and 4 or more. More points give +// better quality and rolloff effectiveness, and take longer to calculate. +template +class Fir_Resampler : public Fir_Resampler_ { + BOOST_STATIC_ASSERT( width >= 4 && width % 2 == 0 ); + short impulses [max_res] [width]; +public: + Fir_Resampler() : Fir_Resampler_( width, impulses [0] ) { } + + // Read at most 'count' samples. Returns number of samples actually read. + typedef short sample_t; + int read( sample_t* out, blargg_long count ); +}; + +// End of public interface + +inline void Fir_Resampler_::write( long count ) +{ + write_pos += count; + assert( write_pos <= buf.end() ); +} + +template +int Fir_Resampler::read( sample_t* out_begin, blargg_long count ) +{ + sample_t* out = out_begin; + const sample_t* in = buf.begin(); + sample_t* end_pos = write_pos; + blargg_ulong skip = skip_bits >> imp_phase; + sample_t const* imp = impulses [imp_phase]; + int remain = res - imp_phase; + int const step = this->step; + + count >>= 1; + + if ( end_pos - in >= width * stereo ) + { + end_pos -= width * stereo; + do + { + count--; + + // accumulate in extended precision + blargg_long l = 0; + blargg_long r = 0; + + const sample_t* i = in; + if ( count < 0 ) + break; + + for ( int n = width / 2; n; --n ) + { + int pt0 = imp [0]; + l += pt0 * i [0]; + r += pt0 * i [1]; + int pt1 = imp [1]; + imp += 2; + l += pt1 * i [2]; + r += pt1 * i [3]; + i += 4; + } + + remain--; + + l >>= 15; + r >>= 15; + + in += (skip * stereo) & stereo; + skip >>= 1; + in += step; + + if ( !remain ) + { + imp = impulses [0]; + skip = skip_bits; + remain = res; + } + + out [0] = (sample_t) l; + out [1] = (sample_t) r; + out += 2; + } + while ( in <= end_pos ); + } + + imp_phase = res - remain; + + int left = write_pos - in; + write_pos = &buf [left]; + memmove( buf.begin(), in, left * sizeof *in ); + + return out - out_begin; +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Gb_Apu.cpp b/thirdparty/game-music-emu/gme/Gb_Apu.cpp new file mode 100644 index 000000000..82a9cc1b6 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gb_Apu.cpp @@ -0,0 +1,306 @@ +// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +unsigned const vol_reg = 0xFF24; +unsigned const status_reg = 0xFF26; + +Gb_Apu::Gb_Apu() +{ + square1.synth = &square_synth; + square2.synth = &square_synth; + wave.synth = &other_synth; + noise.synth = &other_synth; + + oscs [0] = &square1; + oscs [1] = &square2; + oscs [2] = &wave; + oscs [3] = &noise; + + for ( int i = 0; i < osc_count; i++ ) + { + Gb_Osc& osc = *oscs [i]; + osc.regs = ®s [i * 5]; + osc.output = 0; + osc.outputs [0] = 0; + osc.outputs [1] = 0; + osc.outputs [2] = 0; + osc.outputs [3] = 0; + } + + set_tempo( 1.0 ); + volume( 1.0 ); + reset(); +} + +void Gb_Apu::treble_eq( const blip_eq_t& eq ) +{ + square_synth.treble_eq( eq ); + other_synth.treble_eq( eq ); +} + +void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + require( (unsigned) index < osc_count ); + require( (center && left && right) || (!center && !left && !right) ); + Gb_Osc& osc = *oscs [index]; + osc.outputs [1] = right; + osc.outputs [2] = left; + osc.outputs [3] = center; + osc.output = osc.outputs [osc.output_select]; +} + +void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, center, left, right ); +} + +void Gb_Apu::update_volume() +{ + // TODO: doesn't handle differing left/right global volume (support would + // require modification to all oscillator code) + int data = regs [vol_reg - start_addr]; + double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit; + square_synth.volume( vol ); + other_synth.volume( vol ); +} + +static unsigned char const powerup_regs [0x20] = { + 0x80,0x3F,0x00,0xFF,0xBF, // square 1 + 0xFF,0x3F,0x00,0xFF,0xBF, // square 2 + 0x7F,0xFF,0x9F,0xFF,0xBF, // wave + 0xFF,0xFF,0x00,0x00,0xBF, // noise + 0x00, // left/right enables + 0x77, // master volume + 0x80, // power + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +void Gb_Apu::set_tempo( double t ) +{ + frame_period = 4194304 / 256; // 256 Hz + if ( t != 1.0 ) + frame_period = blip_time_t (frame_period / t); +} + +void Gb_Apu::reset() +{ + next_frame_time = 0; + last_time = 0; + frame_count = 0; + + square1.reset(); + square2.reset(); + wave.reset(); + noise.reset(); + noise.bits = 1; + wave.wave_pos = 0; + + // avoid click at beginning + regs [vol_reg - start_addr] = 0x77; + update_volume(); + + regs [status_reg - start_addr] = 0x01; // force power + write_register( 0, status_reg, 0x00 ); + + static unsigned char const initial_wave [] = { + 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table + 0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA + }; + memcpy( wave.wave, initial_wave, sizeof initial_wave ); +} + +void Gb_Apu::run_until( blip_time_t end_time ) +{ + require( end_time >= last_time ); // end_time must not be before previous time + if ( end_time == last_time ) + return; + + while ( true ) + { + blip_time_t time = next_frame_time; + if ( time > end_time ) + time = end_time; + + // run oscillators + for ( int i = 0; i < osc_count; ++i ) + { + Gb_Osc& osc = *oscs [i]; + if ( osc.output ) + { + osc.output->set_modified(); // TODO: misses optimization opportunities? + int playing = false; + if ( osc.enabled && osc.volume && + (!(osc.regs [4] & osc.len_enabled_mask) || osc.length) ) + playing = -1; + switch ( i ) + { + case 0: square1.run( last_time, time, playing ); break; + case 1: square2.run( last_time, time, playing ); break; + case 2: wave .run( last_time, time, playing ); break; + case 3: noise .run( last_time, time, playing ); break; + } + } + } + last_time = time; + + if ( time == end_time ) + break; + + next_frame_time += frame_period; + + // 256 Hz actions + square1.clock_length(); + square2.clock_length(); + wave.clock_length(); + noise.clock_length(); + + frame_count = (frame_count + 1) & 3; + if ( frame_count == 0 ) + { + // 64 Hz actions + square1.clock_envelope(); + square2.clock_envelope(); + noise.clock_envelope(); + } + + if ( frame_count & 1 ) + square1.clock_sweep(); // 128 Hz action + } +} + +void Gb_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + + assert( next_frame_time >= end_time ); + next_frame_time -= end_time; + + assert( last_time >= end_time ); + last_time -= end_time; +} + +void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) +{ + require( (unsigned) data < 0x100 ); + + int reg = addr - start_addr; + if ( (unsigned) reg >= register_count ) + return; + + run_until( time ); + + int old_reg = regs [reg]; + regs [reg] = data; + + if ( addr < vol_reg ) + { + write_osc( reg / 5, reg, data ); + } + else if ( addr == vol_reg && data != old_reg ) // global volume + { + // return all oscs to 0 + for ( int i = 0; i < osc_count; i++ ) + { + Gb_Osc& osc = *oscs [i]; + int amp = osc.last_amp; + osc.last_amp = 0; + if ( amp && osc.enabled && osc.output ) + other_synth.offset( time, -amp, osc.output ); + } + + if ( wave.outputs [3] ) + other_synth.offset( time, 30, wave.outputs [3] ); + + update_volume(); + + if ( wave.outputs [3] ) + other_synth.offset( time, -30, wave.outputs [3] ); + + // oscs will update with new amplitude when next run + } + else if ( addr == 0xFF25 || addr == status_reg ) + { + int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0; + int flags = regs [0xFF25 - start_addr] & mask; + + // left/right assignments + for ( int i = 0; i < osc_count; i++ ) + { + Gb_Osc& osc = *oscs [i]; + osc.enabled &= mask; + int bits = flags >> i; + Blip_Buffer* old_output = osc.output; + osc.output_select = (bits >> 3 & 2) | (bits & 1); + osc.output = osc.outputs [osc.output_select]; + if ( osc.output != old_output ) + { + int amp = osc.last_amp; + osc.last_amp = 0; + if ( amp && old_output ) + other_synth.offset( time, -amp, old_output ); + } + } + + if ( addr == status_reg && data != old_reg ) + { + if ( !(data & 0x80) ) + { + for ( unsigned i = 0; i < sizeof powerup_regs; i++ ) + { + if ( i != status_reg - start_addr ) + write_register( time, i + start_addr, powerup_regs [i] ); + } + } + else + { + //debug_printf( "APU powered on\n" ); + } + } + } + else if ( addr >= 0xFF30 ) + { + int index = (addr & 0x0F) * 2; + wave.wave [index] = data >> 4; + wave.wave [index + 1] = data & 0x0F; + } +} + +int Gb_Apu::read_register( blip_time_t time, unsigned addr ) +{ + run_until( time ); + + int index = addr - start_addr; + require( (unsigned) index < register_count ); + int data = regs [index]; + + if ( addr == status_reg ) + { + data = (data & 0x80) | 0x70; + for ( int i = 0; i < osc_count; i++ ) + { + const Gb_Osc& osc = *oscs [i]; + if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) ) + data |= 1 << i; + } + } + + return data; +} diff --git a/thirdparty/game-music-emu/gme/Gb_Apu.h b/thirdparty/game-music-emu/gme/Gb_Apu.h new file mode 100644 index 000000000..9b251262f --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gb_Apu.h @@ -0,0 +1,90 @@ +// Nintendo Game Boy PAPU sound chip emulator + +// Gb_Snd_Emu 0.1.5 +#ifndef GB_APU_H +#define GB_APU_H + +#include "Gb_Oscs.h" + +class Gb_Apu { +public: + + // Set overall volume of all oscillators, where 1.0 is full volume + void volume( double ); + + // Set treble equalization + void treble_eq( const blip_eq_t& ); + + // Outputs can be assigned to a single buffer for mono output, or to three + // buffers for stereo output (using Stereo_Buffer to do the mixing). + + // Assign all oscillator outputs to specified buffer(s). If buffer + // is NULL, silences all oscillators. + void output( Blip_Buffer* mono ); + void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + + // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, + // which refer to Square 1, Square 2, Wave, and Noise. If buffer is NULL, + // silences oscillator. + enum { osc_count = 4 }; + void osc_output( int index, Blip_Buffer* mono ); + void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + + // Reset oscillators and internal state + void reset(); + + // Reads and writes at addr must satisfy start_addr <= addr <= end_addr + enum { start_addr = 0xFF10 }; + enum { end_addr = 0xFF3F }; + enum { register_count = end_addr - start_addr + 1 }; + + // Write 'data' to address at specified time + void write_register( blip_time_t, unsigned addr, int data ); + + // Read from address at specified time + int read_register( blip_time_t, unsigned addr ); + + // Run all oscillators up to specified time, end current time frame, then + // start a new frame at time 0. + void end_frame( blip_time_t ); + + void set_tempo( double ); + +public: + Gb_Apu(); +private: + // noncopyable + Gb_Apu( const Gb_Apu& ); + Gb_Apu& operator = ( const Gb_Apu& ); + + Gb_Osc* oscs [osc_count]; + blip_time_t next_frame_time; + blip_time_t last_time; + blip_time_t frame_period; + double volume_unit; + int frame_count; + + Gb_Square square1; + Gb_Square square2; + Gb_Wave wave; + Gb_Noise noise; + uint8_t regs [register_count]; + Gb_Square::Synth square_synth; // used by squares + Gb_Wave::Synth other_synth; // used by wave and noise + + void update_volume(); + void run_until( blip_time_t ); + void write_osc( int index, int reg, int data ); +}; + +inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } + +inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } + +inline void Gb_Apu::volume( double vol ) +{ + volume_unit = 0.60 / osc_count / 15 /*steps*/ / 2 /*?*/ / 8 /*master vol range*/ * vol; + update_volume(); +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Gb_Cpu.cpp b/thirdparty/game-music-emu/gme/Gb_Cpu.cpp new file mode 100644 index 000000000..db1abee58 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gb_Cpu.cpp @@ -0,0 +1,1054 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Gb_Cpu.h" + +#include + +//#include "gb_cpu_log.h" + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "gb_cpu_io.h" + +#include "blargg_source.h" + +// Common instructions: +// +// 365880 FA LD A,IND16 +// 355863 20 JR NZ +// 313655 21 LD HL,IMM +// 274580 28 JR Z +// 252878 FE CMP IMM +// 230541 7E LD A,(HL) +// 226209 2A LD A,(HL+) +// 217467 CD CALL +// 212034 C9 RET +// 208376 CB CB prefix +// +// 27486 CB 7E BIT 7,(HL) +// 15925 CB 76 BIT 6,(HL) +// 13035 CB 19 RR C +// 11557 CB 7F BIT 7,A +// 10898 CB 37 SWAP A +// 10208 CB 66 BIT 4,(HL) + +#if BLARGG_NONPORTABLE + #define PAGE_OFFSET( addr ) (addr) +#else + #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) +#endif + +inline void Gb_Cpu::set_code_page( int i, uint8_t* p ) +{ + state->code_map [i] = p - PAGE_OFFSET( i * (blargg_long) page_size ); +} + +void Gb_Cpu::reset( void* unmapped ) +{ + check( state == &state_ ); + state = &state_; + + state_.remain = 0; + + for ( int i = 0; i < page_count + 1; i++ ) + set_code_page( i, (uint8_t*) unmapped ); + + memset( &r, 0, sizeof r ); + //interrupts_enabled = false; + + blargg_verify_byte_order(); +} + +void Gb_Cpu::map_code( gb_addr_t start, unsigned size, void* data ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + + unsigned first_page = start / page_size; + for ( unsigned i = size / page_size; i--; ) + set_code_page( first_page + i, (uint8_t*) data + i * page_size ); +} + +#define READ( addr ) CPU_READ( this, (addr), s.remain ) +#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), s.remain );} +#define READ_FAST( addr, out ) CPU_READ_FAST( this, (addr), s.remain, out ) +#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )]) + +unsigned const z_flag = 0x80; +unsigned const n_flag = 0x40; +unsigned const h_flag = 0x20; +unsigned const c_flag = 0x10; + +bool Gb_Cpu::run( blargg_long cycle_count ) +{ + state_.remain = blargg_ulong (cycle_count + clocks_per_instr) / clocks_per_instr; + state_t s; + this->state = &s; + memcpy( &s, &this->state_, sizeof s ); + +#if BLARGG_BIG_ENDIAN + #define R8( n ) (r8_ [n]) +#elif BLARGG_LITTLE_ENDIAN + #define R8( n ) (r8_ [(n) ^ 1]) +#else + #error "Byte order of CPU must be known" +#endif + + union { + core_regs_t rg; // individual registers + + struct { + uint16_t bc, de, hl, unused; // pairs + } rp; + + uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence) + uint16_t r16 [4]; // indexed pairs + }; + BOOST_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 ); + + rg = r; + unsigned pc = r.pc; + unsigned sp = r.sp; + unsigned flags = r.flags; + +loop: + + check( (unsigned long) pc < 0x10000 ); + check( (unsigned long) sp < 0x10000 ); + check( (flags & ~0xF0) == 0 ); + + uint8_t const* instr = s.code_map [pc >> page_shift]; + unsigned op; + + // TODO: eliminate this special case + #if BLARGG_NONPORTABLE + op = instr [pc]; + pc++; + instr += pc; + #else + instr += PAGE_OFFSET( pc ); + op = *instr++; + pc++; + #endif + +#define GET_ADDR() GET_LE16( instr ) + + if ( !--s.remain ) + goto stop; + + unsigned data; + data = *instr; + + #ifdef GB_CPU_LOG_H + gb_cpu_log( "new", pc - 1, op, data, instr [1] ); + #endif + + switch ( op ) + { + +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH( cond )\ +{\ + pc++;\ + int offset = (int8_t) data;\ + if ( !(cond) ) goto loop;\ + pc = uint16_t (pc + offset);\ + goto loop;\ +} + +// Most Common + + case 0x20: // JR NZ + BRANCH( !(flags & z_flag) ) + + case 0x21: // LD HL,IMM (common) + rp.hl = GET_ADDR(); + pc += 2; + goto loop; + + case 0x28: // JR Z + BRANCH( flags & z_flag ) + + { + unsigned temp; + case 0xF0: // LD A,(0xFF00+imm) + temp = data | 0xFF00; + pc++; + goto ld_a_ind_comm; + + case 0xF2: // LD A,(0xFF00+C) + temp = rg.c | 0xFF00; + goto ld_a_ind_comm; + + case 0x0A: // LD A,(BC) + temp = rp.bc; + goto ld_a_ind_comm; + + case 0x3A: // LD A,(HL-) + temp = rp.hl; + rp.hl = temp - 1; + goto ld_a_ind_comm; + + case 0x1A: // LD A,(DE) + temp = rp.de; + goto ld_a_ind_comm; + + case 0x2A: // LD A,(HL+) (common) + temp = rp.hl; + rp.hl = temp + 1; + goto ld_a_ind_comm; + + case 0xFA: // LD A,IND16 (common) + temp = GET_ADDR(); + pc += 2; + ld_a_ind_comm: + READ_FAST( temp, rg.a ); + goto loop; + } + + case 0xBE: // CMP (HL) + data = READ( rp.hl ); + goto cmp_comm; + + case 0xB8: // CMP B + case 0xB9: // CMP C + case 0xBA: // CMP D + case 0xBB: // CMP E + case 0xBC: // CMP H + case 0xBD: // CMP L + data = R8( op & 7 ); + goto cmp_comm; + + case 0xFE: // CMP IMM + pc++; + cmp_comm: + op = rg.a; + data = op - data; + sub_set_flags: + flags = ((op & 15) - (data & 15)) & h_flag; + flags |= (data >> 4) & c_flag; + flags |= n_flag; + if ( data & 0xFF ) + goto loop; + flags |= z_flag; + goto loop; + + case 0x46: // LD B,(HL) + case 0x4E: // LD C,(HL) + case 0x56: // LD D,(HL) + case 0x5E: // LD E,(HL) + case 0x66: // LD H,(HL) + case 0x6E: // LD L,(HL) + case 0x7E:{// LD A,(HL) + unsigned addr = rp.hl; + READ_FAST( addr, R8( (op >> 3) & 7 ) ); + goto loop; + } + + case 0xC4: // CNZ (next-most-common) + pc += 2; + if ( flags & z_flag ) + goto loop; + call: + pc -= 2; + case 0xCD: // CALL (most-common) + data = pc + 2; + pc = GET_ADDR(); + push: + sp = (sp - 1) & 0xFFFF; + WRITE( sp, data >> 8 ); + sp = (sp - 1) & 0xFFFF; + WRITE( sp, data & 0xFF ); + goto loop; + + case 0xC8: // RNZ (next-most-common) + if ( !(flags & z_flag) ) + goto loop; + case 0xC9: // RET (most common) + ret: + pc = READ( sp ); + pc += 0x100 * READ( sp + 1 ); + sp = (sp + 2) & 0xFFFF; + goto loop; + + case 0x00: // NOP + case 0x40: // LD B,B + case 0x49: // LD C,C + case 0x52: // LD D,D + case 0x5B: // LD E,E + case 0x64: // LD H,H + case 0x6D: // LD L,L + case 0x7F: // LD A,A + goto loop; + +// CB Instructions + + case 0xCB: + pc++; + // now data is the opcode + switch ( data ) { + + { + int temp; + case 0x46: // BIT b,(HL) + case 0x4E: + case 0x56: + case 0x5E: + case 0x66: + case 0x6E: + case 0x76: + case 0x7E: + { + unsigned addr = rp.hl; + READ_FAST( addr, temp ); + goto bit_comm; + } + + case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r + case 0x44: case 0x45: case 0x47: case 0x48: + case 0x49: case 0x4A: case 0x4B: case 0x4C: + case 0x4D: case 0x4F: case 0x50: case 0x51: + case 0x52: case 0x53: case 0x54: case 0x55: + case 0x57: case 0x58: case 0x59: case 0x5A: + case 0x5B: case 0x5C: case 0x5D: case 0x5F: + case 0x60: case 0x61: case 0x62: case 0x63: + case 0x64: case 0x65: case 0x67: case 0x68: + case 0x69: case 0x6A: case 0x6B: case 0x6C: + case 0x6D: case 0x6F: case 0x70: case 0x71: + case 0x72: case 0x73: case 0x74: case 0x75: + case 0x77: case 0x78: case 0x79: case 0x7A: + case 0x7B: case 0x7C: case 0x7D: case 0x7F: + temp = R8( data & 7 ); + bit_comm: + int bit = (~data >> 3) & 7; + flags &= ~n_flag; + flags |= h_flag | z_flag; + flags ^= (temp << bit) & z_flag; + goto loop; + } + + case 0x86: // RES b,(HL) + case 0x8E: + case 0x96: + case 0x9E: + case 0xA6: + case 0xAE: + case 0xB6: + case 0xBE: + case 0xC6: // SET b,(HL) + case 0xCE: + case 0xD6: + case 0xDE: + case 0xE6: + case 0xEE: + case 0xF6: + case 0xFE: { + int temp = READ( rp.hl ); + int bit = 1 << ((data >> 3) & 7); + temp &= ~bit; + if ( !(data & 0x40) ) + bit = 0; + WRITE( rp.hl, temp | bit ); + goto loop; + } + + case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r + case 0xC4: case 0xC5: case 0xC7: case 0xC8: + case 0xC9: case 0xCA: case 0xCB: case 0xCC: + case 0xCD: case 0xCF: case 0xD0: case 0xD1: + case 0xD2: case 0xD3: case 0xD4: case 0xD5: + case 0xD7: case 0xD8: case 0xD9: case 0xDA: + case 0xDB: case 0xDC: case 0xDD: case 0xDF: + case 0xE0: case 0xE1: case 0xE2: case 0xE3: + case 0xE4: case 0xE5: case 0xE7: case 0xE8: + case 0xE9: case 0xEA: case 0xEB: case 0xEC: + case 0xED: case 0xEF: case 0xF0: case 0xF1: + case 0xF2: case 0xF3: case 0xF4: case 0xF5: + case 0xF7: case 0xF8: case 0xF9: case 0xFA: + case 0xFB: case 0xFC: case 0xFD: case 0xFF: + R8( data & 7 ) |= 1 << ((data >> 3) & 7); + goto loop; + + case 0x80: case 0x81: case 0x82: case 0x83: // RES b,r + case 0x84: case 0x85: case 0x87: case 0x88: + case 0x89: case 0x8A: case 0x8B: case 0x8C: + case 0x8D: case 0x8F: case 0x90: case 0x91: + case 0x92: case 0x93: case 0x94: case 0x95: + case 0x97: case 0x98: case 0x99: case 0x9A: + case 0x9B: case 0x9C: case 0x9D: case 0x9F: + case 0xA0: case 0xA1: case 0xA2: case 0xA3: + case 0xA4: case 0xA5: case 0xA7: case 0xA8: + case 0xA9: case 0xAA: case 0xAB: case 0xAC: + case 0xAD: case 0xAF: case 0xB0: case 0xB1: + case 0xB2: case 0xB3: case 0xB4: case 0xB5: + case 0xB7: case 0xB8: case 0xB9: case 0xBA: + case 0xBB: case 0xBC: case 0xBD: case 0xBF: + R8( data & 7 ) &= ~(1 << ((data >> 3) & 7)); + goto loop; + + { + int temp; + case 0x36: // SWAP (HL) + temp = READ( rp.hl ); + goto swap_comm; + + case 0x30: // SWAP B + case 0x31: // SWAP C + case 0x32: // SWAP D + case 0x33: // SWAP E + case 0x34: // SWAP H + case 0x35: // SWAP L + case 0x37: // SWAP A + temp = R8( data & 7 ); + swap_comm: + op = (temp >> 4) | (temp << 4); + flags = 0; + goto shift_comm; + } + +// Shift/Rotate + + case 0x06: // RLC (HL) + case 0x16: // RL (HL) + case 0x26: // SLA (HL) + op = READ( rp.hl ); + goto rl_comm; + + case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA A + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC A + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL A + op = R8( data & 7 ); + goto rl_comm; + + case 0x3E: // SRL (HL) + data += 0x10; // bump up to 0x4n to avoid preserving sign bit + case 0x1E: // RR (HL) + case 0x0E: // RRC (HL) + case 0x2E: // SRA (HL) + op = READ( rp.hl ); + goto rr_comm; + + case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL A + data += 0x10; // bump up to 0x4n + case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR A + case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC A + case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA A + op = R8( data & 7 ); + goto rr_comm; + + } // CB op + assert( false ); // unhandled CB op + + case 0x07: // RLCA + case 0x17: // RLA + data = op; + op = rg.a; + rl_comm: + op <<= 1; + op |= ((data & flags) >> 4) & 1; // RL and carry is set + flags = (op >> 4) & c_flag; // C = bit shifted out + if ( data < 0x10 ) // RLC + op |= op >> 8; + // SLA doesn't fill lower bit + goto shift_comm; + + case 0x0F: // RRCA + case 0x1F: // RRA + data = op; + op = rg.a; + rr_comm: + op |= (data & flags) << 4; // RR and carry is set + flags = (op << 4) & c_flag; // C = bit shifted out + if ( data < 0x10 ) // RRC + op |= op << 8; + op >>= 1; + if ( data & 0x20 ) // SRA propagates sign bit + op |= (op << 1) & 0x80; + shift_comm: + data &= 7; + if ( !(op & 0xFF) ) + flags |= z_flag; + if ( data == 6 ) + goto write_hl_op_ff; + R8( data ) = op; + goto loop; + +// Load + + case 0x70: // LD (HL),B + case 0x71: // LD (HL),C + case 0x72: // LD (HL),D + case 0x73: // LD (HL),E + case 0x74: // LD (HL),H + case 0x75: // LD (HL),L + case 0x77: // LD (HL),A + op = R8( op & 7 ); + write_hl_op_ff: + WRITE( rp.hl, op & 0xFF ); + goto loop; + + case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x47: // LD r,r + case 0x48: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4F: + case 0x50: case 0x51: case 0x53: case 0x54: case 0x55: case 0x57: + case 0x58: case 0x59: case 0x5A: case 0x5C: case 0x5D: case 0x5F: + case 0x60: case 0x61: case 0x62: case 0x63: case 0x65: case 0x67: + case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6F: + case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: + R8( (op >> 3) & 7 ) = R8( op & 7 ); + goto loop; + + case 0x08: // LD IND16,SP + data = GET_ADDR(); + pc += 2; + WRITE( data, sp&0xFF ); + data++; + WRITE( data, sp >> 8 ); + goto loop; + + case 0xF9: // LD SP,HL + sp = rp.hl; + goto loop; + + case 0x31: // LD SP,IMM + sp = GET_ADDR(); + pc += 2; + goto loop; + + case 0x01: // LD BC,IMM + case 0x11: // LD DE,IMM + r16 [op >> 4] = GET_ADDR(); + pc += 2; + goto loop; + + { + unsigned temp; + case 0xE0: // LD (0xFF00+imm),A + temp = data | 0xFF00; + pc++; + goto write_data_rg_a; + + case 0xE2: // LD (0xFF00+C),A + temp = rg.c | 0xFF00; + goto write_data_rg_a; + + case 0x32: // LD (HL-),A + temp = rp.hl; + rp.hl = temp - 1; + goto write_data_rg_a; + + case 0x02: // LD (BC),A + temp = rp.bc; + goto write_data_rg_a; + + case 0x12: // LD (DE),A + temp = rp.de; + goto write_data_rg_a; + + case 0x22: // LD (HL+),A + temp = rp.hl; + rp.hl = temp + 1; + goto write_data_rg_a; + + case 0xEA: // LD IND16,A (common) + temp = GET_ADDR(); + pc += 2; + write_data_rg_a: + WRITE( temp, rg.a ); + goto loop; + } + + case 0x06: // LD B,IMM + rg.b = data; + pc++; + goto loop; + + case 0x0E: // LD C,IMM + rg.c = data; + pc++; + goto loop; + + case 0x16: // LD D,IMM + rg.d = data; + pc++; + goto loop; + + case 0x1E: // LD E,IMM + rg.e = data; + pc++; + goto loop; + + case 0x26: // LD H,IMM + rg.h = data; + pc++; + goto loop; + + case 0x2E: // LD L,IMM + rg.l = data; + pc++; + goto loop; + + case 0x36: // LD (HL),IMM + WRITE( rp.hl, data ); + pc++; + goto loop; + + case 0x3E: // LD A,IMM + rg.a = data; + pc++; + goto loop; + +// Increment/Decrement + + case 0x03: // INC BC + case 0x13: // INC DE + case 0x23: // INC HL + r16 [op >> 4]++; + goto loop; + + case 0x33: // INC SP + sp = (sp + 1) & 0xFFFF; + goto loop; + + case 0x0B: // DEC BC + case 0x1B: // DEC DE + case 0x2B: // DEC HL + r16 [op >> 4]--; + goto loop; + + case 0x3B: // DEC SP + sp = (sp - 1) & 0xFFFF; + goto loop; + + case 0x34: // INC (HL) + op = rp.hl; + data = READ( op ); + data++; + WRITE( op, data & 0xFF ); + goto inc_comm; + + case 0x04: // INC B + case 0x0C: // INC C (common) + case 0x14: // INC D + case 0x1C: // INC E + case 0x24: // INC H + case 0x2C: // INC L + case 0x3C: // INC A + op = (op >> 3) & 7; + R8( op ) = data = R8( op ) + 1; + inc_comm: + flags = (flags & c_flag) | (((data & 15) - 1) & h_flag) | ((data >> 1) & z_flag); + goto loop; + + case 0x35: // DEC (HL) + op = rp.hl; + data = READ( op ); + data--; + WRITE( op, data & 0xFF ); + goto dec_comm; + + case 0x05: // DEC B + case 0x0D: // DEC C + case 0x15: // DEC D + case 0x1D: // DEC E + case 0x25: // DEC H + case 0x2D: // DEC L + case 0x3D: // DEC A + op = (op >> 3) & 7; + data = R8( op ) - 1; + R8( op ) = data; + dec_comm: + flags = (flags & c_flag) | n_flag | (((data & 15) + 0x31) & h_flag); + if ( data & 0xFF ) + goto loop; + flags |= z_flag; + goto loop; + +// Add 16-bit + + { + blargg_ulong temp; // need more than 16 bits for carry + unsigned prev; + + case 0xF8: // LD HL,SP+imm + temp = int8_t (data); // sign-extend to 16 bits + pc++; + flags = 0; + temp += sp; + prev = sp; + goto add_16_hl; + + case 0xE8: // ADD SP,IMM + temp = int8_t (data); // sign-extend to 16 bits + pc++; + flags = 0; + temp += sp; + prev = sp; + sp = temp & 0xFFFF; + goto add_16_comm; + + case 0x39: // ADD HL,SP + temp = sp; + goto add_hl_comm; + + case 0x09: // ADD HL,BC + case 0x19: // ADD HL,DE + case 0x29: // ADD HL,HL + temp = r16 [op >> 4]; + add_hl_comm: + prev = rp.hl; + temp += prev; + flags &= z_flag; + add_16_hl: + rp.hl = temp; + add_16_comm: + flags |= (temp >> 12) & c_flag; + flags |= (((temp & 0x0FFF) - (prev & 0x0FFF)) >> 7) & h_flag; + goto loop; + } + + case 0x86: // ADD (HL) + data = READ( rp.hl ); + goto add_comm; + + case 0x80: // ADD B + case 0x81: // ADD C + case 0x82: // ADD D + case 0x83: // ADD E + case 0x84: // ADD H + case 0x85: // ADD L + case 0x87: // ADD A + data = R8( op & 7 ); + goto add_comm; + + case 0xC6: // ADD IMM + pc++; + add_comm: + flags = rg.a; + data += flags; + flags = ((data & 15) - (flags & 15)) & h_flag; + flags |= (data >> 4) & c_flag; + rg.a = data; + if ( data & 0xFF ) + goto loop; + flags |= z_flag; + goto loop; + +// Add/Subtract + + case 0x8E: // ADC (HL) + data = READ( rp.hl ); + goto adc_comm; + + case 0x88: // ADC B + case 0x89: // ADC C + case 0x8A: // ADC D + case 0x8B: // ADC E + case 0x8C: // ADC H + case 0x8D: // ADC L + case 0x8F: // ADC A + data = R8( op & 7 ); + goto adc_comm; + + case 0xCE: // ADC IMM + pc++; + adc_comm: + data += (flags >> 4) & 1; + data &= 0xFF; // to do: does carry get set when sum + carry = 0x100? + goto add_comm; + + case 0x96: // SUB (HL) + data = READ( rp.hl ); + goto sub_comm; + + case 0x90: // SUB B + case 0x91: // SUB C + case 0x92: // SUB D + case 0x93: // SUB E + case 0x94: // SUB H + case 0x95: // SUB L + case 0x97: // SUB A + data = R8( op & 7 ); + goto sub_comm; + + case 0xD6: // SUB IMM + pc++; + sub_comm: + op = rg.a; + data = op - data; + rg.a = data; + goto sub_set_flags; + + case 0x9E: // SBC (HL) + data = READ( rp.hl ); + goto sbc_comm; + + case 0x98: // SBC B + case 0x99: // SBC C + case 0x9A: // SBC D + case 0x9B: // SBC E + case 0x9C: // SBC H + case 0x9D: // SBC L + case 0x9F: // SBC A + data = R8( op & 7 ); + goto sbc_comm; + + case 0xDE: // SBC IMM + pc++; + sbc_comm: + data += (flags >> 4) & 1; + data &= 0xFF; // to do: does carry get set when sum + carry = 0x100? + goto sub_comm; + +// Logical + + case 0xA0: // AND B + case 0xA1: // AND C + case 0xA2: // AND D + case 0xA3: // AND E + case 0xA4: // AND H + case 0xA5: // AND L + data = R8( op & 7 ); + goto and_comm; + + case 0xA6: // AND (HL) + data = READ( rp.hl ); + pc--; + case 0xE6: // AND IMM + pc++; + and_comm: + rg.a &= data; + case 0xA7: // AND A + flags = h_flag | (((rg.a - 1) >> 1) & z_flag); + goto loop; + + case 0xB0: // OR B + case 0xB1: // OR C + case 0xB2: // OR D + case 0xB3: // OR E + case 0xB4: // OR H + case 0xB5: // OR L + data = R8( op & 7 ); + goto or_comm; + + case 0xB6: // OR (HL) + data = READ( rp.hl ); + pc--; + case 0xF6: // OR IMM + pc++; + or_comm: + rg.a |= data; + case 0xB7: // OR A + flags = ((rg.a - 1) >> 1) & z_flag; + goto loop; + + case 0xA8: // XOR B + case 0xA9: // XOR C + case 0xAA: // XOR D + case 0xAB: // XOR E + case 0xAC: // XOR H + case 0xAD: // XOR L + data = R8( op & 7 ); + goto xor_comm; + + case 0xAE: // XOR (HL) + data = READ( rp.hl ); + pc--; + case 0xEE: // XOR IMM + pc++; + xor_comm: + data ^= rg.a; + rg.a = data; + data--; + flags = (data >> 1) & z_flag; + goto loop; + + case 0xAF: // XOR A + rg.a = 0; + flags = z_flag; + goto loop; + +// Stack + + case 0xF1: // POP FA + case 0xC1: // POP BC + case 0xD1: // POP DE + case 0xE1: // POP HL (common) + data = READ( sp ); + r16 [(op >> 4) & 3] = data + 0x100 * READ( sp + 1 ); + sp = (sp + 2) & 0xFFFF; + if ( op != 0xF1 ) + goto loop; + flags = rg.flags & 0xF0; + goto loop; + + case 0xC5: // PUSH BC + data = rp.bc; + goto push; + + case 0xD5: // PUSH DE + data = rp.de; + goto push; + + case 0xE5: // PUSH HL + data = rp.hl; + goto push; + + case 0xF5: // PUSH FA + data = (flags << 8) | rg.a; + goto push; + +// Flow control + + case 0xFF: + if ( pc == idle_addr + 1 ) + goto stop; + case 0xC7: case 0xCF: case 0xD7: case 0xDF: // RST + case 0xE7: case 0xEF: case 0xF7: + data = pc; + pc = (op & 0x38) + rst_base; + goto push; + + case 0xCC: // CZ + pc += 2; + if ( flags & z_flag ) + goto call; + goto loop; + + case 0xD4: // CNC + pc += 2; + if ( !(flags & c_flag) ) + goto call; + goto loop; + + case 0xDC: // CC + pc += 2; + if ( flags & c_flag ) + goto call; + goto loop; + + case 0xD9: // RETI + //interrupts_enabled = 1; + goto ret; + + case 0xC0: // RZ + if ( !(flags & z_flag) ) + goto ret; + goto loop; + + case 0xD0: // RNC + if ( !(flags & c_flag) ) + goto ret; + goto loop; + + case 0xD8: // RC + if ( flags & c_flag ) + goto ret; + goto loop; + + case 0x18: // JR + BRANCH( true ) + + case 0x30: // JR NC + BRANCH( !(flags & c_flag) ) + + case 0x38: // JR C + BRANCH( flags & c_flag ) + + case 0xE9: // JP_HL + pc = rp.hl; + goto loop; + + case 0xC3: // JP (next-most-common) + pc = GET_ADDR(); + goto loop; + + case 0xC2: // JP NZ + pc += 2; + if ( !(flags & z_flag) ) + goto jp_taken; + goto loop; + + case 0xCA: // JP Z (most common) + pc += 2; + if ( !(flags & z_flag) ) + goto loop; + jp_taken: + pc -= 2; + pc = GET_ADDR(); + goto loop; + + case 0xD2: // JP NC + pc += 2; + if ( !(flags & c_flag) ) + goto jp_taken; + goto loop; + + case 0xDA: // JP C + pc += 2; + if ( flags & c_flag ) + goto jp_taken; + goto loop; + +// Flags + + case 0x2F: // CPL + rg.a = ~rg.a; + flags |= n_flag | h_flag; + goto loop; + + case 0x3F: // CCF + flags = (flags ^ c_flag) & ~(n_flag | h_flag); + goto loop; + + case 0x37: // SCF + flags = (flags | c_flag) & ~(n_flag | h_flag); + goto loop; + + case 0xF3: // DI + //interrupts_enabled = 0; + goto loop; + + case 0xFB: // EI + //interrupts_enabled = 1; + goto loop; + +// Special + + case 0xDD: case 0xD3: case 0xDB: case 0xE3: case 0xE4: // ? + case 0xEB: case 0xEC: case 0xF4: case 0xFD: case 0xFC: + case 0x10: // STOP + case 0x27: // DAA (I'll have to implement this eventually...) + case 0xBF: + case 0xED: // Z80 prefix + case 0x76: // HALT + s.remain++; + goto stop; + } + + // If this fails then the case above is missing an opcode + assert( false ); + +stop: + pc--; + + // copy state back + STATIC_CAST(core_regs_t&,r) = rg; + r.pc = pc; + r.sp = sp; + r.flags = flags; + + this->state = &state_; + memcpy( &this->state_, &s, sizeof this->state_ ); + + return s.remain > 0; +} diff --git a/thirdparty/game-music-emu/gme/Gb_Cpu.h b/thirdparty/game-music-emu/gme/Gb_Cpu.h new file mode 100644 index 000000000..d3df30cac --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gb_Cpu.h @@ -0,0 +1,91 @@ +// Nintendo Game Boy CPU emulator +// Treats every instruction as taking 4 cycles + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef GB_CPU_H +#define GB_CPU_H + +#include "blargg_common.h" +#include "blargg_endian.h" + +typedef unsigned gb_addr_t; // 16-bit CPU address + +class Gb_Cpu { + enum { clocks_per_instr = 4 }; +public: + // Clear registers and map all pages to unmapped + void reset( void* unmapped = 0 ); + + // Map code memory (memory accessed via the program counter). Start and size + // must be multiple of page_size. + enum { page_size = 0x2000 }; + void map_code( gb_addr_t start, unsigned size, void* code ); + + uint8_t* get_code( gb_addr_t ); + + // Push a byte on the stack + void push_byte( int ); + + // Game Boy Z80 registers. *Not* kept updated during a call to run(). + struct core_regs_t { + #if BLARGG_BIG_ENDIAN + uint8_t b, c, d, e, h, l, flags, a; + #else + uint8_t c, b, e, d, l, h, a, flags; + #endif + }; + + struct registers_t : core_regs_t { + long pc; // more than 16 bits to allow overflow detection + uint16_t sp; + }; + registers_t r; + + // Interrupt enable flag set by EI and cleared by DI + //bool interrupts_enabled; // unused + + // Base address for RST vectors (normally 0) + gb_addr_t rst_base; + + // If CPU executes opcode 0xFF at this address, it treats as illegal instruction + enum { idle_addr = 0xF00D }; + + // Run CPU for at least 'count' cycles and return false, or return true if + // illegal instruction is encountered. + bool run( blargg_long count ); + + // Number of clock cycles remaining for most recent run() call + blargg_long remain() const { return state->remain * clocks_per_instr; } + + // Can read this many bytes past end of a page + enum { cpu_padding = 8 }; + +public: + Gb_Cpu() : rst_base( 0 ) { state = &state_; } + enum { page_shift = 13 }; + enum { page_count = 0x10000 >> page_shift }; +private: + // noncopyable + Gb_Cpu( const Gb_Cpu& ); + Gb_Cpu& operator = ( const Gb_Cpu& ); + + struct state_t { + uint8_t* code_map [page_count + 1]; + blargg_long remain; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + + void set_code_page( int, uint8_t* ); +}; + +inline uint8_t* Gb_Cpu::get_code( gb_addr_t addr ) +{ + return state->code_map [addr >> page_shift] + addr + #if !BLARGG_NONPORTABLE + % (unsigned) page_size + #endif + ; +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Gb_Oscs.cpp b/thirdparty/game-music-emu/gme/Gb_Oscs.cpp new file mode 100644 index 000000000..735653fa9 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gb_Oscs.cpp @@ -0,0 +1,336 @@ +// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Gb_Osc + +void Gb_Osc::reset() +{ + delay = 0; + last_amp = 0; + length = 0; + output_select = 3; + output = outputs [output_select]; +} + +void Gb_Osc::clock_length() +{ + if ( (regs [4] & len_enabled_mask) && length ) + length--; +} + +// Gb_Env + +void Gb_Env::clock_envelope() +{ + if ( env_delay && !--env_delay ) + { + env_delay = regs [2] & 7; + int v = volume - 1 + (regs [2] >> 2 & 2); + if ( (unsigned) v < 15 ) + volume = v; + } +} + +bool Gb_Env::write_register( int reg, int data ) +{ + switch ( reg ) + { + case 1: + length = 64 - (regs [1] & 0x3F); + break; + + case 2: + if ( !(data >> 4) ) + enabled = false; + break; + + case 4: + if ( data & trigger ) + { + env_delay = regs [2] & 7; + volume = regs [2] >> 4; + enabled = true; + if ( length == 0 ) + length = 64; + return true; + } + } + return false; +} + +// Gb_Square + +void Gb_Square::reset() +{ + phase = 0; + sweep_freq = 0; + sweep_delay = 0; + Gb_Env::reset(); +} + +void Gb_Square::clock_sweep() +{ + int sweep_period = (regs [0] & period_mask) >> 4; + if ( sweep_period && sweep_delay && !--sweep_delay ) + { + sweep_delay = sweep_period; + regs [3] = sweep_freq & 0xFF; + regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07); + + int offset = sweep_freq >> (regs [0] & shift_mask); + if ( regs [0] & 0x08 ) + offset = -offset; + sweep_freq += offset; + + if ( sweep_freq < 0 ) + { + sweep_freq = 0; + } + else if ( sweep_freq >= 2048 ) + { + sweep_delay = 0; // don't modify channel frequency any further + sweep_freq = 2048; // silence sound immediately + } + } +} + +void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing ) +{ + if ( sweep_freq == 2048 ) + playing = false; + + static unsigned char const table [4] = { 1, 2, 4, 6 }; + int const duty = table [regs [1] >> 6]; + int amp = volume & playing; + if ( phase >= duty ) + amp = -amp; + + int frequency = this->frequency(); + if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041 + { + // really high frequency results in DC at half volume + amp = volume >> 1; + playing = false; + } + + { + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } + } + + time += delay; + if ( !playing ) + time = end_time; + + if ( time < end_time ) + { + int const period = (2048 - frequency) * 4; + Blip_Buffer* const output = this->output; + int phase = this->phase; + int delta = amp * 2; + do + { + phase = (phase + 1) & 7; + if ( phase == 0 || phase == duty ) + { + delta = -delta; + synth->offset_inline( time, delta, output ); + } + time += period; + } + while ( time < end_time ); + + this->phase = phase; + last_amp = delta >> 1; + } + delay = time - end_time; +} + +// Gb_Noise + +void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing ) +{ + int amp = volume & playing; + int tap = 13 - (regs [3] & 8); + if ( bits >> tap & 2 ) + amp = -amp; + + { + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } + } + + time += delay; + if ( !playing ) + time = end_time; + + if ( time < end_time ) + { + static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 }; + int period = table [regs [3] & 7] << (regs [3] >> 4); + + // keep parallel resampled time to eliminate time conversion in the loop + Blip_Buffer* const output = this->output; + const blip_resampled_time_t resampled_period = + output->resampled_duration( period ); + blip_resampled_time_t resampled_time = output->resampled_time( time ); + unsigned bits = this->bits; + int delta = amp * 2; + + do + { + unsigned changed = (bits >> tap) + 1; + time += period; + bits <<= 1; + if ( changed & 2 ) + { + delta = -delta; + bits |= 1; + synth->offset_resampled( resampled_time, delta, output ); + } + resampled_time += resampled_period; + } + while ( time < end_time ); + + this->bits = bits; + last_amp = delta >> 1; + } + delay = time - end_time; +} + +// Gb_Wave + +inline void Gb_Wave::write_register( int reg, int data ) +{ + switch ( reg ) + { + case 0: + if ( !(data & 0x80) ) + enabled = false; + break; + + case 1: + length = 256 - regs [1]; + break; + + case 2: + volume = data >> 5 & 3; + break; + + case 4: + if ( data & trigger & regs [0] ) + { + wave_pos = 0; + enabled = true; + if ( length == 0 ) + length = 256; + } + } +} + +void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing ) +{ + int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7 + int frequency; + { + int amp = (wave [wave_pos] >> volume_shift & playing) * 2; + + frequency = this->frequency(); + if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045 + { + amp = 30 >> volume_shift & playing; + playing = false; + } + + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } + } + + time += delay; + if ( !playing ) + time = end_time; + + if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + int const period = (2048 - frequency) * 2; + int wave_pos = (this->wave_pos + 1) & (wave_size - 1); + + do + { + int amp = (wave [wave_pos] >> volume_shift) * 2; + wave_pos = (wave_pos + 1) & (wave_size - 1); + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset_inline( time, delta, output ); + } + time += period; + } + while ( time < end_time ); + + this->wave_pos = (wave_pos - 1) & (wave_size - 1); + } + delay = time - end_time; +} + +// Gb_Apu::write_osc + +void Gb_Apu::write_osc( int index, int reg, int data ) +{ + reg -= index * 5; + Gb_Square* sq = &square2; + switch ( index ) + { + case 0: + sq = &square1; + case 1: + if ( sq->write_register( reg, data ) && index == 0 ) + { + square1.sweep_freq = square1.frequency(); + if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) ) + { + square1.sweep_delay = 1; // cause sweep to recalculate now + square1.clock_sweep(); + } + } + break; + + case 2: + wave.write_register( reg, data ); + break; + + case 3: + if ( noise.write_register( reg, data ) ) + noise.bits = 0x7FFF; + } +} diff --git a/thirdparty/game-music-emu/gme/Gb_Oscs.h b/thirdparty/game-music-emu/gme/Gb_Oscs.h new file mode 100644 index 000000000..8cb026c3e --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gb_Oscs.h @@ -0,0 +1,83 @@ +// Private oscillators used by Gb_Apu + +// Gb_Snd_Emu 0.1.5 +#ifndef GB_OSCS_H +#define GB_OSCS_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct Gb_Osc +{ + enum { trigger = 0x80 }; + enum { len_enabled_mask = 0x40 }; + + Blip_Buffer* outputs [4]; // NULL, right, left, center + Blip_Buffer* output; + int output_select; + uint8_t* regs; // osc's 5 registers + + int delay; + int last_amp; + int volume; + int length; + int enabled; + + void reset(); + void clock_length(); + int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } +}; + +struct Gb_Env : Gb_Osc +{ + int env_delay; + + void reset(); + void clock_envelope(); + bool write_register( int, int ); +}; + +struct Gb_Square : Gb_Env +{ + enum { period_mask = 0x70 }; + enum { shift_mask = 0x07 }; + + typedef Blip_Synth Synth; + Synth const* synth; + int sweep_delay; + int sweep_freq; + int phase; + + void reset(); + void clock_sweep(); + void run( blip_time_t, blip_time_t, int playing ); +}; + +struct Gb_Noise : Gb_Env +{ + typedef Blip_Synth Synth; + Synth const* synth; + unsigned bits; + + void run( blip_time_t, blip_time_t, int playing ); +}; + +struct Gb_Wave : Gb_Osc +{ + typedef Blip_Synth Synth; + Synth const* synth; + int wave_pos; + enum { wave_size = 32 }; + uint8_t wave [wave_size]; + + void write_register( int, int ); + void run( blip_time_t, blip_time_t, int playing ); +}; + +inline void Gb_Env::reset() +{ + env_delay = 0; + Gb_Osc::reset(); +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Gbs_Emu.cpp b/thirdparty/game-music-emu/gme/Gbs_Emu.cpp new file mode 100644 index 000000000..6c5def339 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gbs_Emu.cpp @@ -0,0 +1,290 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Gbs_Emu.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = + Music_Emu::make_equalizer( -47.0, 2000 ); +Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = + Music_Emu::make_equalizer( 0.0, 300 ); + +Gbs_Emu::Gbs_Emu() +{ + set_type( gme_gbs_type ); + + static const char* const names [Gb_Apu::osc_count] = { + "Square 1", "Square 2", "Wave", "Noise" + }; + set_voice_names( names ); + + static int const types [Gb_Apu::osc_count] = { + wave_type | 1, wave_type | 2, wave_type | 0, mixed_type | 0 + }; + set_voice_types( types ); + + set_silence_lookahead( 6 ); + set_max_initial_silence( 21 ); + set_gain( 1.2 ); + + set_equalizer( make_equalizer( -1.0, 120 ) ); +} + +Gbs_Emu::~Gbs_Emu() { } + +void Gbs_Emu::unload() +{ + rom.clear(); + Music_Emu::unload(); +} + +// Track info + +static void copy_gbs_fields( Gbs_Emu::header_t const& h, track_info_t* out ) +{ + GME_COPY_FIELD( h, out, game ); + GME_COPY_FIELD( h, out, author ); + GME_COPY_FIELD( h, out, copyright ); +} + +blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const +{ + copy_gbs_fields( header_, out ); + return 0; +} + +static blargg_err_t check_gbs_header( void const* header ) +{ + if ( memcmp( header, "GBS", 3 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Gbs_File : Gme_Info_ +{ + Gbs_Emu::header_t h; + + Gbs_File() { set_type( gme_gbs_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + blargg_err_t err = in.read( &h, Gbs_Emu::header_size ); + if ( err ) + return (err == in.eof_error ? gme_wrong_file_type : err); + + set_track_count( h.track_count ); + return check_gbs_header( &h ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_gbs_fields( h, out ); + return 0; + } +}; + +static Music_Emu* new_gbs_emu () { return BLARGG_NEW Gbs_Emu ; } +static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; } + +static gme_type_t_ const gme_gbs_type_ = { "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }; +BLARGG_EXPORT extern gme_type_t const gme_gbs_type = &gme_gbs_type_; + +// Setup + +blargg_err_t Gbs_Emu::load_( Data_Reader& in ) +{ + assert( offsetof (header_t,copyright [32]) == header_size ); + RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); + + set_track_count( header_.track_count ); + RETURN_ERR( check_gbs_header( &header_ ) ); + + if ( header_.vers != 1 ) + set_warning( "Unknown file version" ); + + if ( header_.timer_mode & 0x78 ) + set_warning( "Invalid timer mode" ); + + unsigned load_addr = get_le16( header_.load_addr ); + if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F || + load_addr < 0x400 ) + set_warning( "Invalid load/init/play address" ); + + set_voice_count( Gb_Apu::osc_count ); + + apu.volume( gain() ); + + return setup_buffer( 4194304 ); +} + +void Gbs_Emu::update_eq( blip_eq_t const& eq ) +{ + apu.treble_eq( eq ); +} + +void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + apu.osc_output( i, c, l, r ); +} + +// Emulation + +// see gb_cpu_io.h for read/write functions + +void Gbs_Emu::set_bank( int n ) +{ + blargg_long addr = rom.mask_addr( n * (blargg_long) bank_size ); + if ( addr == 0 && rom.size() > bank_size ) + { + // TODO: what is the correct behavior? Current Game & Watch Gallery + // rip requires that this have no effect or set to bank 1. + //debug_printf( "Selected ROM bank 0\n" ); + return; + //n = 1; + } + cpu::map_code( bank_size, bank_size, rom.at_addr( addr ) ); +} + +void Gbs_Emu::update_timer() +{ + if ( header_.timer_mode & 0x04 ) + { + static byte const rates [4] = { 10, 4, 6, 8 }; + int shift = rates [ram [hi_page + 7] & 3] - (header_.timer_mode >> 7); + play_period = (256L - ram [hi_page + 6]) << shift; + } + else + { + play_period = 70224; // 59.73 Hz + } + if ( tempo() != 1.0 ) + play_period = blip_time_t (play_period / tempo()); +} + +static uint8_t const sound_data [Gb_Apu::register_count] = { + 0x80, 0xBF, 0x00, 0x00, 0xBF, // square 1 + 0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2 + 0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave + 0x00, 0xFF, 0x00, 0x00, 0xBF, // noise + 0x77, 0xF3, 0xF1, // vin/volume, status, power mode + 0, 0, 0, 0, 0, 0, 0, 0, 0, // unused + 0xAC, 0xDD, 0xDA, 0x48, 0x36, 0x02, 0xCF, 0x16, // waveform data + 0x2C, 0x04, 0xE5, 0x2C, 0xAC, 0xDD, 0xDA, 0x48 +}; + +void Gbs_Emu::cpu_jsr( gb_addr_t addr ) +{ + check( cpu::r.sp == get_le16( header_.stack_ptr ) ); + cpu::r.pc = addr; + cpu_write( --cpu::r.sp, idle_addr >> 8 ); + cpu_write( --cpu::r.sp, idle_addr&0xFF ); +} + +void Gbs_Emu::set_tempo_( double t ) +{ + apu.set_tempo( t ); + update_timer(); +} + +blargg_err_t Gbs_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + memset( ram, 0, 0x4000 ); + memset( ram + 0x4000, 0xFF, 0x1F80 ); + memset( ram + 0x5F80, 0, sizeof ram - 0x5F80 ); + ram [hi_page] = 0; // joypad reads back as 0 + + apu.reset(); + for ( int i = 0; i < (int) sizeof sound_data; i++ ) + apu.write_register( 0, i + apu.start_addr, sound_data [i] ); + + unsigned load_addr = get_le16( header_.load_addr ); + rom.set_addr( load_addr ); + cpu::rst_base = load_addr; + + cpu::reset( rom.unmapped() ); + + cpu::map_code( ram_addr, 0x10000 - ram_addr, ram ); + cpu::map_code( 0, bank_size, rom.at_addr( 0 ) ); + set_bank( rom.size() > bank_size ); + + ram [hi_page + 6] = header_.timer_modulo; + ram [hi_page + 7] = header_.timer_mode; + update_timer(); + next_play = play_period; + + cpu::r.a = track; + cpu::r.pc = idle_addr; + cpu::r.sp = get_le16( header_.stack_ptr ); + cpu_time = 0; + cpu_jsr( get_le16( header_.init_addr ) ); + + return 0; +} + +blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) +{ + cpu_time = 0; + while ( cpu_time < duration ) + { + long count = duration - cpu_time; + cpu_time = duration; + bool result = cpu::run( count ); + cpu_time -= cpu::remain(); + + if ( result ) + { + if ( cpu::r.pc == idle_addr ) + { + if ( next_play > duration ) + { + cpu_time = duration; + break; + } + + if ( cpu_time < next_play ) + cpu_time = next_play; + next_play += play_period; + cpu_jsr( get_le16( header_.play_addr ) ); + GME_FRAME_HOOK( this ); + // TODO: handle timer rates different than 60 Hz + } + else if ( cpu::r.pc > 0xFFFF ) + { + debug_printf( "PC wrapped around\n" ); + cpu::r.pc &= 0xFFFF; + } + else + { + set_warning( "Emulation error (illegal/unsupported instruction)" ); + debug_printf( "Bad opcode $%.2x at $%.4x\n", + (int) *cpu::get_code( cpu::r.pc ), (int) cpu::r.pc ); + cpu::r.pc = (cpu::r.pc + 1) & 0xFFFF; + cpu_time += 6; + } + } + } + + duration = cpu_time; + next_play -= cpu_time; + if ( next_play < 0 ) // could go negative if routine is taking too long to return + next_play = 0; + apu.end_frame( cpu_time ); + + return 0; +} diff --git a/thirdparty/game-music-emu/gme/Gbs_Emu.h b/thirdparty/game-music-emu/gme/Gbs_Emu.h new file mode 100644 index 000000000..580f395c6 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gbs_Emu.h @@ -0,0 +1,88 @@ +// Nintendo Game Boy GBS music file emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef GBS_EMU_H +#define GBS_EMU_H + +#include "Classic_Emu.h" +#include "Gb_Apu.h" +#include "Gb_Cpu.h" + +class Gbs_Emu : private Gb_Cpu, public Classic_Emu { + typedef Gb_Cpu cpu; +public: + // Equalizer profiles for Game Boy Color speaker and headphones + static equalizer_t const handheld_eq; + static equalizer_t const headphones_eq; + + // GBS file header + enum { header_size = 112 }; + struct header_t + { + char tag [3]; + byte vers; + byte track_count; + byte first_track; + byte load_addr [2]; + byte init_addr [2]; + byte play_addr [2]; + byte stack_ptr [2]; + byte timer_modulo; + byte timer_mode; + char game [32]; + char author [32]; + char copyright [32]; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + static gme_type_t static_type() { return gme_gbs_type; } + +public: + // deprecated + using Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + +public: + Gbs_Emu(); + ~Gbs_Emu(); +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_( Data_Reader& ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); + void unload(); +private: + // rom + enum { bank_size = 0x4000 }; + Rom_Data rom; + void set_bank( int ); + + // timer + blip_time_t cpu_time; + blip_time_t play_period; + blip_time_t next_play; + void update_timer(); + + header_t header_; + void cpu_jsr( gb_addr_t ); + +public: private: friend class Gb_Cpu; + blip_time_t clock() const { return cpu_time - cpu::remain(); } + + enum { joypad_addr = 0xFF00 }; + enum { ram_addr = 0xA000 }; + enum { hi_page = 0xFF00 - ram_addr }; + byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding]; + Gb_Apu apu; + + int cpu_read( gb_addr_t ); + void cpu_write( gb_addr_t, int ); +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Gme_File.cpp b/thirdparty/game-music-emu/gme/Gme_File.cpp new file mode 100644 index 000000000..a5e4516d6 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gme_File.cpp @@ -0,0 +1,216 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Gme_File.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +const char* const gme_wrong_file_type = "Wrong file type for this emulator"; + +void Gme_File::clear_playlist() +{ + playlist.clear(); + clear_playlist_(); + track_count_ = raw_track_count_; +} + +void Gme_File::unload() +{ + clear_playlist(); // *before* clearing track count + track_count_ = 0; + raw_track_count_ = 0; + file_data.clear(); +} + +Gme_File::Gme_File() +{ + type_ = 0; + user_data_ = 0; + user_cleanup_ = 0; + unload(); // clears fields + blargg_verify_byte_order(); // used by most emulator types, so save them the trouble +} + +Gme_File::~Gme_File() +{ + if ( user_cleanup_ ) + user_cleanup_( user_data_ ); +} + +blargg_err_t Gme_File::load_mem_( byte const* data, long size ) +{ + require( data != file_data.begin() ); // load_mem_() or load_() must be overridden + Mem_File_Reader in( data, size ); + return load_( in ); +} + +blargg_err_t Gme_File::load_( Data_Reader& in ) +{ + RETURN_ERR( file_data.resize( in.remain() ) ); + RETURN_ERR( in.read( file_data.begin(), file_data.size() ) ); + return load_mem_( file_data.begin(), file_data.size() ); +} + +// public load functions call this at beginning +void Gme_File::pre_load() { unload(); } + +void Gme_File::post_load_() { } + +// public load functions call this at end +blargg_err_t Gme_File::post_load( blargg_err_t err ) +{ + if ( !track_count() ) + set_track_count( type()->track_count ); + if ( !err ) + post_load_(); + else + unload(); + + return err; +} + +// Public load functions + +blargg_err_t Gme_File::load_mem( void const* in, long size ) +{ + pre_load(); + return post_load( load_mem_( (byte const*) in, size ) ); +} + +blargg_err_t Gme_File::load( Data_Reader& in ) +{ + pre_load(); + return post_load( load_( in ) ); +} + +blargg_err_t Gme_File::load_file( const char* path ) +{ + pre_load(); + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + return post_load( load_( in ) ); +} + +blargg_err_t Gme_File::load_remaining_( void const* h, long s, Data_Reader& in ) +{ + Remaining_Reader rem( h, s, &in ); + return load( rem ); +} + +// Track info + +void Gme_File::copy_field_( char* out, const char* in, int in_size ) +{ + if ( !in || !*in ) + return; + + // remove spaces/junk from beginning + while ( in_size && unsigned (*in - 1) <= ' ' - 1 ) + { + in++; + in_size--; + } + + // truncate + if ( in_size > max_field_ ) + in_size = max_field_; + + // find terminator + int len = 0; + while ( len < in_size && in [len] ) + len++; + + // remove spaces/junk from end + while ( len && unsigned (in [len - 1]) <= ' ' ) + len--; + + // copy + out [len] = 0; + memcpy( out, in, len ); + + // strip out stupid fields that should have been left blank + if ( !strcmp( out, "?" ) || !strcmp( out, "" ) || !strcmp( out, "< ? >" ) ) + out [0] = 0; +} + +void Gme_File::copy_field_( char* out, const char* in ) +{ + copy_field_( out, in, max_field_ ); +} + +blargg_err_t Gme_File::remap_track_( int* track_io ) const +{ + if ( (unsigned) *track_io >= (unsigned) track_count() ) + return "Invalid track"; + + if ( (unsigned) *track_io < (unsigned) playlist.size() ) + { + M3u_Playlist::entry_t const& e = playlist [*track_io]; + *track_io = 0; + if ( e.track >= 0 ) + { + *track_io = e.track; + if ( !(type_->flags_ & 0x02) ) + *track_io -= e.decimal_track; + } + if ( *track_io >= raw_track_count_ ) + return "Invalid track in m3u playlist"; + } + else + { + check( !playlist.size() ); + } + return 0; +} + +blargg_err_t Gme_File::track_info( track_info_t* out, int track ) const +{ + out->track_count = track_count(); + out->length = -1; + out->loop_length = -1; + out->intro_length = -1; + out->song [0] = 0; + + out->game [0] = 0; + out->author [0] = 0; + out->copyright [0] = 0; + out->comment [0] = 0; + out->dumper [0] = 0; + out->system [0] = 0; + + copy_field_( out->system, type()->system ); + + int remapped = track; + RETURN_ERR( remap_track_( &remapped ) ); + RETURN_ERR( track_info_( out, remapped ) ); + + // override with m3u info + if ( playlist.size() ) + { + M3u_Playlist::info_t const& i = playlist.info(); + copy_field_( out->game , i.title ); + copy_field_( out->author, i.engineer ); + copy_field_( out->author, i.composer ); + copy_field_( out->dumper, i.ripping ); + + M3u_Playlist::entry_t const& e = playlist [track]; + copy_field_( out->song, e.name ); + if ( e.length >= 0 ) out->length = e.length * 1000L; + if ( e.intro >= 0 ) out->intro_length = e.intro * 1000L; + if ( e.loop >= 0 ) out->loop_length = e.loop * 1000L; + } + return 0; +} diff --git a/thirdparty/game-music-emu/gme/Gme_File.h b/thirdparty/game-music-emu/gme/Gme_File.h new file mode 100644 index 000000000..3ec36bc8e --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gme_File.h @@ -0,0 +1,173 @@ +// Common interface to game music file loading and information + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef GME_FILE_H +#define GME_FILE_H + +#include "gme.h" +#include "blargg_common.h" +#include "Data_Reader.h" +#include "M3u_Playlist.h" + +// Error returned if file is wrong type +//extern const char gme_wrong_file_type []; // declared in gme.h + +struct gme_type_t_ +{ + const char* system; /* name of system this music file type is generally for */ + int track_count; /* non-zero for formats with a fixed number of tracks */ + Music_Emu* (*new_emu)(); /* Create new emulator for this type (useful in C++ only) */ + Music_Emu* (*new_info)(); /* Create new info reader for this type */ + + /* internal */ + const char* extension_; + int flags_; +}; + +struct track_info_t +{ + long track_count; + + /* times in milliseconds; -1 if unknown */ + long length; + long intro_length; + long loop_length; + + /* empty string if not available */ + char system [256]; + char game [256]; + char song [256]; + char author [256]; + char copyright [256]; + char comment [256]; + char dumper [256]; +}; +enum { gme_max_field = 255 }; + +struct Gme_File { +public: +// File loading + + // Each loads game music data from a file and returns an error if + // file is wrong type or is seriously corrupt. They also set warning + // string for minor problems. + + // Load from file on disk + blargg_err_t load_file( const char* path ); + + // Load from custom data source (see Data_Reader.h) + blargg_err_t load( Data_Reader& ); + + // Load from file already read into memory. Keeps pointer to data, so you + // must not free it until you're done with the file. + blargg_err_t load_mem( void const* data, long size ); + + // Load an m3u playlist. Must be done after loading main music file. + blargg_err_t load_m3u( const char* path ); + blargg_err_t load_m3u( Data_Reader& in ); + + // Clears any loaded m3u playlist and any internal playlist that the music + // format supports (NSFE for example). + void clear_playlist(); + +// Informational + + // Type of emulator. For example if this returns gme_nsfe_type, this object + // is an NSFE emulator, and you can cast to an Nsfe_Emu* if necessary. + gme_type_t type() const; + + // Most recent warning string, or NULL if none. Clears current warning after + // returning. + const char* warning(); + + // Number of tracks or 0 if no file has been loaded + int track_count() const; + + // Get information for a track (length, name, author, etc.) + // See gme.h for definition of struct track_info_t. + blargg_err_t track_info( track_info_t* out, int track ) const; + +// User data/cleanup + + // Set/get pointer to data you want to associate with this emulator. + // You can use this for whatever you want. + void set_user_data( void* p ) { user_data_ = p; } + void* user_data() const { return user_data_; } + + // Register cleanup function to be called when deleting emulator, or NULL to + // clear it. Passes user_data to cleanup function. + void set_user_cleanup( gme_user_cleanup_t func ) { user_cleanup_ = func; } + +public: + // deprecated + int error_count() const; // use warning() +public: + Gme_File(); + virtual ~Gme_File(); + BLARGG_DISABLE_NOTHROW + typedef uint8_t byte; +protected: + // Services + void set_track_count( int n ) { track_count_ = raw_track_count_ = n; } + void set_warning( const char* s ) { warning_ = s; } + void set_type( gme_type_t t ) { type_ = t; } + blargg_err_t load_remaining_( void const* header, long header_size, Data_Reader& remaining ); + + // Overridable + virtual void unload(); // called before loading file and if loading fails + virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_() + virtual blargg_err_t load_mem_( byte const* data, long size ); // use data in memory + virtual blargg_err_t track_info_( track_info_t* out, int track ) const = 0; + virtual void pre_load(); + virtual void post_load_(); + virtual void clear_playlist_() { } + +public: + blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu +private: + // noncopyable + Gme_File( const Gme_File& ); + Gme_File& operator = ( const Gme_File& ); + + gme_type_t type_; + int track_count_; + int raw_track_count_; + const char* warning_; + void* user_data_; + gme_user_cleanup_t user_cleanup_; + M3u_Playlist playlist; + char playlist_warning [64]; + blargg_vector file_data; // only if loaded into memory using default load + + blargg_err_t load_m3u_( blargg_err_t ); + blargg_err_t post_load( blargg_err_t err ); +public: + // track_info field copying + enum { max_field_ = 255 }; + static void copy_field_( char* out, const char* in ); + static void copy_field_( char* out, const char* in, int len ); +}; + +Music_Emu* gme_new_( Music_Emu*, long sample_rate ); + +#define GME_COPY_FIELD( in, out, name ) \ + { Gme_File::copy_field_( out->name, in.name, sizeof in.name ); } + +#ifndef GME_FILE_READER + #define GME_FILE_READER Std_File_Reader +#elif defined (GME_FILE_READER_INCLUDE) + #include GME_FILE_READER_INCLUDE +#endif + +inline gme_type_t Gme_File::type() const { return type_; } +inline int Gme_File::error_count() const { return warning_ != 0; } +inline int Gme_File::track_count() const { return track_count_; } + +inline const char* Gme_File::warning() +{ + const char* s = warning_; + warning_ = 0; + return s; +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Gym_Emu.cpp b/thirdparty/game-music-emu/gme/Gym_Emu.cpp new file mode 100644 index 000000000..bb99ff033 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gym_Emu.cpp @@ -0,0 +1,380 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Gym_Emu.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +double const min_tempo = 0.25; +double const oversample_factor = 5 / 3.0; +double const fm_gain = 3.0; + +const long base_clock = 53700300; +const long clock_rate = base_clock / 15; + +Gym_Emu::Gym_Emu() +{ + data = 0; + pos = 0; + set_type( gme_gym_type ); + + static const char* const names [] = { + "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" + }; + set_voice_names( names ); + set_silence_lookahead( 1 ); // tracks should already be trimmed +} + +Gym_Emu::~Gym_Emu() { } + +// Track info + +static void get_gym_info( Gym_Emu::header_t const& h, long length, track_info_t* out ) +{ + if ( !memcmp( h.tag, "GYMX", 4 ) ) + { + length = length * 50 / 3; // 1000 / 60 + long loop = get_le32( h.loop_start ); + if ( loop ) + { + out->intro_length = loop * 50 / 3; + out->loop_length = length - out->intro_length; + } + else + { + out->length = length; + out->intro_length = length; // make it clear that track is no longer than length + out->loop_length = 0; + } + + // more stupidity where the field should have been left + if ( strcmp( h.song, "Unknown Song" ) ) + GME_COPY_FIELD( h, out, song ); + + if ( strcmp( h.game, "Unknown Game" ) ) + GME_COPY_FIELD( h, out, game ); + + if ( strcmp( h.copyright, "Unknown Publisher" ) ) + GME_COPY_FIELD( h, out, copyright ); + + if ( strcmp( h.dumper, "Unknown Person" ) ) + GME_COPY_FIELD( h, out, dumper ); + + if ( strcmp( h.comment, "Header added by YMAMP" ) ) + GME_COPY_FIELD( h, out, comment ); + } +} + +blargg_err_t Gym_Emu::track_info_( track_info_t* out, int ) const +{ + get_gym_info( header_, track_length(), out ); + return 0; +} + +static long gym_track_length( byte const* p, byte const* end ) +{ + long time = 0; + while ( p < end ) + { + switch ( *p++ ) + { + case 0: + time++; + break; + + case 1: + case 2: + p += 2; + break; + + case 3: + p += 1; + break; + } + } + return time; +} + +long Gym_Emu::track_length() const { return gym_track_length( data, data_end ); } + +static blargg_err_t check_header( byte const* in, long size, int* data_offset = 0 ) +{ + if ( size < 4 ) + return gme_wrong_file_type; + + if ( memcmp( in, "GYMX", 4 ) == 0 ) + { + if ( size < Gym_Emu::header_size + 1 ) + return gme_wrong_file_type; + + if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 ) + return "Packed GYM file not supported"; + + if ( data_offset ) + *data_offset = Gym_Emu::header_size; + } + else if ( *in > 3 ) + { + return gme_wrong_file_type; + } + + return 0; +} + +struct Gym_File : Gme_Info_ +{ + byte const* file_begin; + byte const* file_end; + int data_offset; + + Gym_File() { set_type( gme_gym_type ); } + + blargg_err_t load_mem_( byte const* in, long size ) + { + file_begin = in; + file_end = in + size; + data_offset = 0; + return check_header( in, size, &data_offset ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + long length = gym_track_length( &file_begin [data_offset], file_end ); + get_gym_info( *(Gym_Emu::header_t const*) file_begin, length, out ); + return 0; + } +}; + +static Music_Emu* new_gym_emu () { return BLARGG_NEW Gym_Emu ; } +static Music_Emu* new_gym_file() { return BLARGG_NEW Gym_File; } + +static gme_type_t_ const gme_gym_type_ = { "Sega Genesis", 1, &new_gym_emu, &new_gym_file, "GYM", 0 }; +BLARGG_EXPORT extern gme_type_t const gme_gym_type = &gme_gym_type_; + +// Setup + +blargg_err_t Gym_Emu::set_sample_rate_( long sample_rate ) +{ + blip_eq_t eq( -32, 8000, sample_rate ); + apu.treble_eq( eq ); + dac_synth.treble_eq( eq ); + apu.volume( 0.135 * fm_gain * gain() ); + dac_synth.volume( 0.125 / 256 * fm_gain * gain() ); + double factor = Dual_Resampler::setup( oversample_factor, 0.990, fm_gain * gain() ); + fm_sample_rate = sample_rate * factor; + + RETURN_ERR( blip_buf.set_sample_rate( sample_rate, int (1000 / 60.0 / min_tempo) ) ); + blip_buf.clock_rate( clock_rate ); + + RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) ); + RETURN_ERR( Dual_Resampler::reset( long (1.0 / 60 / min_tempo * sample_rate) ) ); + + return 0; +} + +void Gym_Emu::set_tempo_( double t ) +{ + if ( t < min_tempo ) + { + set_tempo( min_tempo ); + return; + } + + if ( blip_buf.sample_rate() ) + { + clocks_per_frame = long (clock_rate / 60 / tempo()); + Dual_Resampler::resize( long (sample_rate() / (60.0 * tempo())) ); + } +} + +void Gym_Emu::mute_voices_( int mask ) +{ + Music_Emu::mute_voices_( mask ); + fm.mute_voices( mask ); + dac_muted = (mask & 0x40) != 0; + apu.output( (mask & 0x80) ? 0 : &blip_buf ); +} + +blargg_err_t Gym_Emu::load_mem_( byte const* in, long size ) +{ + assert( offsetof (header_t,packed [4]) == header_size ); + int offset = 0; + RETURN_ERR( check_header( in, size, &offset ) ); + set_voice_count( 8 ); + + data = in + offset; + data_end = in + size; + loop_begin = 0; + + if ( offset ) + header_ = *(header_t const*) in; + else + memset( &header_, 0, sizeof header_ ); + + return 0; +} + +// Emulation + +blargg_err_t Gym_Emu::start_track_( int track ) +{ + RETURN_ERR( Music_Emu::start_track_( track ) ); + + pos = data; + loop_remain = get_le32( header_.loop_start ); + + prev_dac_count = 0; + dac_enabled = false; + dac_amp = -1; + + fm.reset(); + apu.reset(); + blip_buf.clear(); + Dual_Resampler::clear(); + return 0; +} + +void Gym_Emu::run_dac( int dac_count ) +{ + // Guess beginning and end of sample and adjust rate and buffer position accordingly. + + // count dac samples in next frame + int next_dac_count = 0; + const byte* p = this->pos; + int cmd; + while ( (cmd = *p++) != 0 ) + { + int data = *p++; + if ( cmd <= 2 ) + ++p; + if ( cmd == 1 && data == 0x2A ) + next_dac_count++; + } + + // detect beginning and end of sample + int rate_count = dac_count; + int start = 0; + if ( !prev_dac_count && next_dac_count && dac_count < next_dac_count ) + { + rate_count = next_dac_count; + start = next_dac_count - dac_count; + } + else if ( prev_dac_count && !next_dac_count && dac_count < prev_dac_count ) + { + rate_count = prev_dac_count; + } + + // Evenly space samples within buffer section being used + blip_resampled_time_t period = blip_buf.resampled_duration( clocks_per_frame ) / rate_count; + + blip_resampled_time_t time = blip_buf.resampled_time( 0 ) + + period * start + (period >> 1); + + int dac_amp = this->dac_amp; + if ( dac_amp < 0 ) + dac_amp = dac_buf [0]; + + for ( int i = 0; i < dac_count; i++ ) + { + int delta = dac_buf [i] - dac_amp; + dac_amp += delta; + dac_synth.offset_resampled( time, delta, &blip_buf ); + time += period; + } + this->dac_amp = dac_amp; +} + +void Gym_Emu::parse_frame() +{ + int dac_count = 0; + const byte* pos = this->pos; + + if ( loop_remain && !--loop_remain ) + loop_begin = pos; // find loop on first time through sequence + + int cmd; + while ( (cmd = *pos++) != 0 ) + { + int data = *pos++; + if ( cmd == 1 ) + { + int data2 = *pos++; + if ( data != 0x2A ) + { + if ( data == 0x2B ) + dac_enabled = (data2 & 0x80) != 0; + + fm.write0( data, data2 ); + } + else if ( dac_count < (int) sizeof dac_buf ) + { + dac_buf [dac_count] = data2; + dac_count += dac_enabled; + } + } + else if ( cmd == 2 ) + { + fm.write1( data, *pos++ ); + } + else if ( cmd == 3 ) + { + apu.write_data( 0, data ); + } + else + { + // to do: many GYM streams are full of errors, and error count should + // reflect cases where music is really having problems + //log_error(); + --pos; // put data back + } + } + + // loop + if ( pos >= data_end ) + { + check( pos == data_end ); + + if ( loop_begin ) + pos = loop_begin; + else + set_track_ended(); + } + this->pos = pos; + + // dac + if ( dac_count && !dac_muted ) + run_dac( dac_count ); + prev_dac_count = dac_count; +} + +int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ) +{ + if ( !track_ended() ) + parse_frame(); + + apu.end_frame( blip_time ); + + memset( buf, 0, sample_count * sizeof *buf ); + fm.run( sample_count >> 1, buf ); + + return sample_count; +} + +blargg_err_t Gym_Emu::play_( long count, sample_t* out ) +{ + Dual_Resampler::dual_play( count, out, blip_buf ); + return 0; +} diff --git a/thirdparty/game-music-emu/gme/Gym_Emu.h b/thirdparty/game-music-emu/gme/Gym_Emu.h new file mode 100644 index 000000000..290f57f5c --- /dev/null +++ b/thirdparty/game-music-emu/gme/Gym_Emu.h @@ -0,0 +1,82 @@ +// Sega Genesis/Mega Drive GYM music file emulator +// Includes with PCM timing recovery to improve sample quality. + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef GYM_EMU_H +#define GYM_EMU_H + +#include "Dual_Resampler.h" +#include "Ym2612_Emu.h" +#include "Music_Emu.h" +#include "Sms_Apu.h" + +class Gym_Emu : public Music_Emu, private Dual_Resampler { +public: + // GYM file header + enum { header_size = 428 }; + struct header_t + { + char tag [4]; + char song [32]; + char game [32]; + char copyright [32]; + char emulator [32]; + char dumper [32]; + char comment [256]; + byte loop_start [4]; // in 1/60 seconds, 0 if not looped + byte packed [4]; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + static gme_type_t static_type() { return gme_gym_type; } + +public: + // deprecated + using Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + enum { gym_rate = 60 }; + long track_length() const; // use track_info() + +public: + Gym_Emu(); + ~Gym_Emu(); +protected: + blargg_err_t load_mem_( byte const*, long ); + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t set_sample_rate_( long sample_rate ); + blargg_err_t start_track_( int ); + blargg_err_t play_( long count, sample_t* ); + void mute_voices_( int ); + void set_tempo_( double ); + int play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ); +private: + // sequence data begin, loop begin, current position, end + const byte* data; + const byte* loop_begin; + const byte* pos; + const byte* data_end; + blargg_long loop_remain; // frames remaining until loop beginning has been located + header_t header_; + double fm_sample_rate; + blargg_long clocks_per_frame; + void parse_frame(); + + // dac (pcm) + int dac_amp; + int prev_dac_count; + bool dac_enabled; + bool dac_muted; + void run_dac( int ); + + // sound + Blip_Buffer blip_buf; + Ym2612_Emu fm; + Blip_Synth dac_synth; + Sms_Apu apu; + byte dac_buf [1024]; +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Hes_Apu.cpp b/thirdparty/game-music-emu/gme/Hes_Apu.cpp new file mode 100644 index 000000000..1df811592 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Hes_Apu.cpp @@ -0,0 +1,315 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Hes_Apu.h" + +#include + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +bool const center_waves = true; // reduces asymmetry and clamping when starting notes + +Hes_Apu::Hes_Apu() +{ + Hes_Osc* osc = &oscs [osc_count]; + do + { + osc--; + osc->outputs [0] = 0; + osc->outputs [1] = 0; + osc->chans [0] = 0; + osc->chans [1] = 0; + osc->chans [2] = 0; + } + while ( osc != oscs ); + + reset(); +} + +void Hes_Apu::reset() +{ + latch = 0; + balance = 0xFF; + + Hes_Osc* osc = &oscs [osc_count]; + do + { + osc--; + memset( osc, 0, offsetof (Hes_Osc,outputs) ); + osc->noise_lfsr = 1; + osc->control = 0x40; + osc->balance = 0xFF; + } + while ( osc != oscs ); +} + +void Hes_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + require( (unsigned) index < osc_count ); + oscs [index].chans [0] = center; + oscs [index].chans [1] = left; + oscs [index].chans [2] = right; + + Hes_Osc* osc = &oscs [osc_count]; + do + { + osc--; + balance_changed( *osc ); + } + while ( osc != oscs ); +} + +void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) +{ + Blip_Buffer* const osc_outputs_0 = outputs [0]; // cache often-used values + if ( osc_outputs_0 && control & 0x80 ) + { + int dac = this->dac; + + int const volume_0 = volume [0]; + { + int delta = dac * volume_0 - last_amp [0]; + if ( delta ) + synth_.offset( last_time, delta, osc_outputs_0 ); + osc_outputs_0->set_modified(); + } + + Blip_Buffer* const osc_outputs_1 = outputs [1]; + int const volume_1 = volume [1]; + if ( osc_outputs_1 ) + { + int delta = dac * volume_1 - last_amp [1]; + if ( delta ) + synth_.offset( last_time, delta, osc_outputs_1 ); + osc_outputs_1->set_modified(); + } + + blip_time_t time = last_time + delay; + if ( time < end_time ) + { + if ( noise & 0x80 ) + { + if ( volume_0 | volume_1 ) + { + // noise + int const period = (32 - (noise & 0x1F)) * 64; // TODO: correct? + unsigned noise_lfsr = this->noise_lfsr; + do + { + int new_dac = 0x1F & -(noise_lfsr >> 1 & 1); + // Implemented using "Galios configuration" + // TODO: find correct LFSR algorithm + noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1)); + //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1)); + int delta = new_dac - dac; + if ( delta ) + { + dac = new_dac; + synth_.offset( time, delta * volume_0, osc_outputs_0 ); + if ( osc_outputs_1 ) + synth_.offset( time, delta * volume_1, osc_outputs_1 ); + } + time += period; + } + while ( time < end_time ); + + this->noise_lfsr = noise_lfsr; + assert( noise_lfsr ); + } + } + else if ( !(control & 0x40) ) + { + // wave + int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop + int period = this->period * 2; + if ( period >= 14 && (volume_0 | volume_1) ) + { + do + { + int new_dac = wave [phase]; + phase = (phase + 1) & 0x1F; + int delta = new_dac - dac; + if ( delta ) + { + dac = new_dac; + synth_.offset( time, delta * volume_0, osc_outputs_0 ); + if ( osc_outputs_1 ) + synth_.offset( time, delta * volume_1, osc_outputs_1 ); + } + time += period; + } + while ( time < end_time ); + } + else + { + if ( !period ) + { + // TODO: Gekisha Boy assumes that period = 0 silences wave + //period = 0x1000 * 2; + period = 1; + //if ( !(volume_0 | volume_1) ) + // debug_printf( "Used period 0\n" ); + } + + // maintain phase when silent + blargg_long count = (end_time - time + period - 1) / period; + phase += count; // phase will be masked below + time += count * period; + } + this->phase = (phase - 1) & 0x1F; // undo pre-advance + } + } + time -= end_time; + if ( time < 0 ) + time = 0; + delay = time; + + this->dac = dac; + last_amp [0] = dac * volume_0; + last_amp [1] = dac * volume_1; + } + last_time = end_time; +} + +void Hes_Apu::balance_changed( Hes_Osc& osc ) +{ + static short const log_table [32] = { // ~1.5 db per step + #define ENTRY( factor ) short (factor * Hes_Osc::amp_range / 31.0 + 0.5) + ENTRY( 0.000000 ),ENTRY( 0.005524 ),ENTRY( 0.006570 ),ENTRY( 0.007813 ), + ENTRY( 0.009291 ),ENTRY( 0.011049 ),ENTRY( 0.013139 ),ENTRY( 0.015625 ), + ENTRY( 0.018581 ),ENTRY( 0.022097 ),ENTRY( 0.026278 ),ENTRY( 0.031250 ), + ENTRY( 0.037163 ),ENTRY( 0.044194 ),ENTRY( 0.052556 ),ENTRY( 0.062500 ), + ENTRY( 0.074325 ),ENTRY( 0.088388 ),ENTRY( 0.105112 ),ENTRY( 0.125000 ), + ENTRY( 0.148651 ),ENTRY( 0.176777 ),ENTRY( 0.210224 ),ENTRY( 0.250000 ), + ENTRY( 0.297302 ),ENTRY( 0.353553 ),ENTRY( 0.420448 ),ENTRY( 0.500000 ), + ENTRY( 0.594604 ),ENTRY( 0.707107 ),ENTRY( 0.840896 ),ENTRY( 1.000000 ), + #undef ENTRY + }; + + int vol = (osc.control & 0x1F) - 0x1E * 2; + + int left = vol + (osc.balance >> 3 & 0x1E) + (balance >> 3 & 0x1E); + if ( left < 0 ) left = 0; + + int right = vol + (osc.balance << 1 & 0x1E) + (balance << 1 & 0x1E); + if ( right < 0 ) right = 0; + + left = log_table [left ]; + right = log_table [right]; + + // optimizing for the common case of being centered also allows easy + // panning using Effects_Buffer + osc.outputs [0] = osc.chans [0]; // center + osc.outputs [1] = 0; + if ( left != right ) + { + osc.outputs [0] = osc.chans [1]; // left + osc.outputs [1] = osc.chans [2]; // right + } + + if ( center_waves ) + { + osc.last_amp [0] += (left - osc.volume [0]) * 16; + osc.last_amp [1] += (right - osc.volume [1]) * 16; + } + + osc.volume [0] = left; + osc.volume [1] = right; +} + +void Hes_Apu::write_data( blip_time_t time, int addr, int data ) +{ + if ( addr == 0x800 ) + { + latch = data & 7; + } + else if ( addr == 0x801 ) + { + if ( balance != data ) + { + balance = data; + + Hes_Osc* osc = &oscs [osc_count]; + do + { + osc--; + osc->run_until( synth, time ); + balance_changed( *oscs ); + } + while ( osc != oscs ); + } + } + else if ( latch < osc_count ) + { + Hes_Osc& osc = oscs [latch]; + osc.run_until( synth, time ); + switch ( addr ) + { + case 0x802: + osc.period = (osc.period & 0xF00) | data; + break; + + case 0x803: + osc.period = (osc.period & 0x0FF) | ((data & 0x0F) << 8); + break; + + case 0x804: + if ( osc.control & 0x40 & ~data ) + osc.phase = 0; + osc.control = data; + balance_changed( osc ); + break; + + case 0x805: + osc.balance = data; + balance_changed( osc ); + break; + + case 0x806: + data &= 0x1F; + if ( !(osc.control & 0x40) ) + { + osc.wave [osc.phase] = data; + osc.phase = (osc.phase + 1) & 0x1F; + } + else if ( osc.control & 0x80 ) + { + osc.dac = data; + } + break; + + case 0x807: + if ( &osc >= &oscs [4] ) + osc.noise = data; + break; + + case 0x809: + if ( !(data & 0x80) && (data & 0x03) != 0 ) + debug_printf( "HES LFO not supported\n" ); + } + } +} + +void Hes_Apu::end_frame( blip_time_t end_time ) +{ + Hes_Osc* osc = &oscs [osc_count]; + do + { + osc--; + if ( end_time > osc->last_time ) + osc->run_until( synth, end_time ); + assert( osc->last_time >= end_time ); + osc->last_time -= end_time; + } + while ( osc != oscs ); +} diff --git a/thirdparty/game-music-emu/gme/Hes_Apu.h b/thirdparty/game-music-emu/gme/Hes_Apu.h new file mode 100644 index 000000000..1efc0a064 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Hes_Apu.h @@ -0,0 +1,66 @@ +// Turbo Grafx 16 (PC Engine) PSG sound chip emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef HES_APU_H +#define HES_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct Hes_Osc +{ + unsigned char wave [32]; + short volume [2]; + int last_amp [2]; + int delay; + int period; + unsigned char noise; + unsigned char phase; + unsigned char balance; + unsigned char dac; + blip_time_t last_time; + + Blip_Buffer* outputs [2]; + Blip_Buffer* chans [3]; + unsigned noise_lfsr; + unsigned char control; + + enum { amp_range = 0x8000 }; + typedef Blip_Synth synth_t; + + void run_until( synth_t& synth, blip_time_t ); +}; + +class Hes_Apu { +public: + void treble_eq( blip_eq_t const& ); + void volume( double ); + + enum { osc_count = 6 }; + void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + + void reset(); + + enum { start_addr = 0x0800 }; + enum { end_addr = 0x0809 }; + void write_data( blip_time_t, int addr, int data ); + + void end_frame( blip_time_t ); + +public: + Hes_Apu(); +private: + Hes_Osc oscs [osc_count]; + int latch; + int balance; + Hes_Osc::synth_t synth; + + void balance_changed( Hes_Osc& ); + void recalc_chans(); +}; + +inline void Hes_Apu::volume( double v ) { synth.volume( 1.8 / osc_count / Hes_Osc::amp_range * v ); } + +inline void Hes_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + +#endif diff --git a/thirdparty/game-music-emu/gme/Hes_Cpu.cpp b/thirdparty/game-music-emu/gme/Hes_Cpu.cpp new file mode 100644 index 000000000..095a1851a --- /dev/null +++ b/thirdparty/game-music-emu/gme/Hes_Cpu.cpp @@ -0,0 +1,1295 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Hes_Cpu.h" + +#include "blargg_endian.h" + +//#include "hes_cpu_log.h" + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +// TODO: support T flag, including clearing it at appropriate times? + +// all zero-page should really use whatever is at page 1, but that would +// reduce efficiency quite a bit +int const ram_addr = 0x2000; + +#define FLUSH_TIME() (void) (s.time = s_time) +#define CACHE_TIME() (void) (s_time = s.time) + +#include "hes_cpu_io.h" + +#include "blargg_source.h" + +#if BLARGG_NONPORTABLE + #define PAGE_OFFSET( addr ) (addr) +#else + #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) +#endif + +// status flags +int const st_n = 0x80; +int const st_v = 0x40; +int const st_t = 0x20; +int const st_b = 0x10; +int const st_d = 0x08; +int const st_i = 0x04; +int const st_z = 0x02; +int const st_c = 0x01; + +void Hes_Cpu::reset() +{ + check( state == &state_ ); + state = &state_; + + state_.time = 0; + state_.base = 0; + irq_time_ = future_hes_time; + end_time_ = future_hes_time; + + r.status = st_i; + r.sp = 0; + r.pc = 0; + r.a = 0; + r.x = 0; + r.y = 0; + + blargg_verify_byte_order(); +} + +void Hes_Cpu::set_mmr( int reg, int bank ) +{ + assert( (unsigned) reg <= page_count ); // allow page past end to be set + assert( (unsigned) bank < 0x100 ); + mmr [reg] = bank; + uint8_t const* code = CPU_SET_MMR( this, reg, bank ); + state->code_map [reg] = code - PAGE_OFFSET( reg << page_shift ); +} + +#define TIME (s_time + s.base) + +#define READ( addr ) CPU_READ( this, (addr), TIME ) +#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} +#define READ_LOW( addr ) (ram [int (addr)]) +#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) +#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )]) + +#define SET_SP( v ) (sp = ((v) + 1) | 0x100) +#define GET_SP() ((sp - 1) & 0xFF) +#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) + +bool Hes_Cpu::run( hes_time_t end_time ) +{ + bool illegal_encountered = false; + set_end_time( end_time ); + state_t s = this->state_; + this->state = &s; + // even on x86, using s.time in place of s_time was slower + blargg_long s_time = s.time; + + // registers + uint_fast16_t pc = r.pc; + uint_fast8_t a = r.a; + uint_fast8_t x = r.x; + uint_fast8_t y = r.y; + uint_fast16_t sp; + SET_SP( r.sp ); + + #define IS_NEG (nz & 0x8080) + + #define CALC_STATUS( out ) do {\ + out = status & (st_v | st_d | st_i);\ + out |= ((nz >> 8) | nz) & st_n;\ + out |= c >> 8 & st_c;\ + if ( !(nz & 0xFF) ) out |= st_z;\ + } while ( 0 ) + + #define SET_STATUS( in ) do {\ + status = in & (st_v | st_d | st_i);\ + nz = in << 8;\ + c = nz;\ + nz |= ~in & st_z;\ + } while ( 0 ) + + uint_fast8_t status; + uint_fast16_t c; // carry set if (c & 0x100) != 0 + uint_fast16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + { + uint_fast8_t temp = r.status; + SET_STATUS( temp ); + } + + goto loop; +branch_not_taken: + s_time -= 2; +loop: + + #ifndef NDEBUG + { + hes_time_t correct = end_time_; + if ( !(status & st_i) && correct > irq_time_ ) + correct = irq_time_; + check( s.base == correct ); + /* + static long count; + if ( count == 1844 ) Debugger(); + if ( s.base != correct ) debug_printf( "%ld\n", count ); + count++; + */ + } + #endif + + check( (unsigned) GET_SP() < 0x100 ); + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + + uint8_t const* instr = s.code_map [pc >> page_shift]; + uint_fast8_t opcode; + + // TODO: eliminate this special case + #if BLARGG_NONPORTABLE + opcode = instr [pc]; + pc++; + instr += pc; + #else + instr += PAGE_OFFSET( pc ); + opcode = *instr++; + pc++; + #endif + + // TODO: each reference lists slightly different timing values, ugh + static uint8_t const clock_table [256] = + {// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,6,// 0 + 4,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,6,// 1 + 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6,// 2 + 4,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,6,// 3 + 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,6,// 4 + 4,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,6,// 5 + 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,6,// 6 + 4,7,7,17,4,4,6,7,2,5,4,2,7,5,7,6,// 7 + 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// 8 + 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// 9 + 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// A + 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// B + 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// C + 4,7,7,17,2,4,6,7,2,5,3,2,2,5,7,6,// D + 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E + 4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F + }; // 0x00 was 8 + + uint_fast16_t data; + data = clock_table [opcode]; + if ( (s_time += data) >= 0 ) + goto possibly_out_of_time; +almost_out_of_time: + + data = *instr; + + #ifdef HES_CPU_LOG_H + log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2], + instr [3], instr [4], instr [5] ); + //log_opcode( opcode ); + #endif + + switch ( opcode ) + { +possibly_out_of_time: + if ( s_time < (int) data ) + goto almost_out_of_time; + s_time -= data; + goto out_of_time; + +// Macros + +#define GET_MSB() (instr [1]) +#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB()); +#define GET_ADDR() GET_LE16( instr ) + +// TODO: is the penalty really always added? the original 6502 was much better +//#define PAGE_CROSS_PENALTY( lsb ) (void) (s_time += (lsb) >> 8) +#define PAGE_CROSS_PENALTY( lsb ) + +// Branch + +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH( cond )\ +{\ + int_fast16_t offset = (int8_t) data;\ + pc++;\ + if ( !(cond) ) goto branch_not_taken;\ + pc = uint16_t (pc + offset);\ + goto loop;\ +} + + case 0xF0: // BEQ + BRANCH( !((uint8_t) nz) ); + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ); + + case 0x10: // BPL + BRANCH( !IS_NEG ); + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + + case 0x30: // BMI + BRANCH( IS_NEG ) + + case 0x50: // BVC + BRANCH( !(status & st_v) ) + + case 0x70: // BVS + BRANCH( status & st_v ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x80: // BRA + branch_taken: + BRANCH( true ); + + case 0xFF: + if ( pc == idle_addr + 1 ) + goto idle_done; + case 0x0F: // BBRn + case 0x1F: + case 0x2F: + case 0x3F: + case 0x4F: + case 0x5F: + case 0x6F: + case 0x7F: + case 0x8F: // BBSn + case 0x9F: + case 0xAF: + case 0xBF: + case 0xCF: + case 0xDF: + case 0xEF: { + uint_fast16_t t = 0x101 * READ_LOW( data ); + t ^= 0xFF; + pc++; + data = GET_MSB(); + BRANCH( t & (1 << (opcode >> 4)) ) + } + + case 0x4C: // JMP abs + pc = GET_ADDR(); + goto loop; + + case 0x7C: // JMP (ind+X) + data += x; + case 0x6C:{// JMP (ind) + data += 0x100 * GET_MSB(); + pc = GET_LE16( &READ_PROG( data ) ); + goto loop; + } + +// Subroutine + + case 0x44: // BSR + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); + sp = (sp - 2) | 0x100; + WRITE_LOW( sp, pc ); + goto branch_taken; + + case 0x20: { // JSR + uint_fast16_t temp = pc + 1; + pc = GET_ADDR(); + WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); + sp = (sp - 2) | 0x100; + WRITE_LOW( sp, temp ); + goto loop; + } + + case 0x60: // RTS + pc = 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); + pc += 1 + READ_LOW( sp ); + sp = (sp - 0xFE) | 0x100; + goto loop; + + case 0x00: // BRK + goto handle_brk; + +// Common + + case 0xBD:{// LDA abs,X + PAGE_CROSS_PENALTY( data + x ); + uint_fast16_t addr = GET_ADDR() + x; + pc += 2; + CPU_READ_FAST( this, addr, TIME, nz ); + a = nz; + goto loop; + } + + case 0x9D:{// STA abs,X + uint_fast16_t addr = GET_ADDR() + x; + pc += 2; + CPU_WRITE_FAST( this, addr, a, TIME ); + goto loop; + } + + case 0x95: // STA zp,x + data = uint8_t (data + x); + case 0x85: // STA zp + pc++; + WRITE_LOW( data, a ); + goto loop; + + case 0xAE:{// LDX abs + uint_fast16_t addr = GET_ADDR(); + pc += 2; + CPU_READ_FAST( this, addr, TIME, nz ); + x = nz; + goto loop; + } + + case 0xA5: // LDA zp + a = nz = READ_LOW( data ); + pc++; + goto loop; + +// Load/store + + { + uint_fast16_t addr; + case 0x91: // STA (ind),Y + addr = 0x100 * READ_LOW( uint8_t (data + 1) ); + addr += READ_LOW( data ) + y; + pc++; + goto sta_ptr; + + case 0x81: // STA (ind,X) + data = uint8_t (data + x); + case 0x92: // STA (ind) + addr = 0x100 * READ_LOW( uint8_t (data + 1) ); + addr += READ_LOW( data ); + pc++; + goto sta_ptr; + + case 0x99: // STA abs,Y + data += y; + case 0x8D: // STA abs + addr = data + 0x100 * GET_MSB(); + pc += 2; + sta_ptr: + CPU_WRITE_FAST( this, addr, a, TIME ); + goto loop; + } + + { + uint_fast16_t addr; + case 0xA1: // LDA (ind,X) + data = uint8_t (data + x); + case 0xB2: // LDA (ind) + addr = 0x100 * READ_LOW( uint8_t (data + 1) ); + addr += READ_LOW( data ); + pc++; + goto a_nz_read_addr; + + case 0xB1:// LDA (ind),Y + addr = READ_LOW( data ) + y; + PAGE_CROSS_PENALTY( addr ); + addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); + pc++; + goto a_nz_read_addr; + + case 0xB9: // LDA abs,Y + data += y; + PAGE_CROSS_PENALTY( data ); + case 0xAD: // LDA abs + addr = data + 0x100 * GET_MSB(); + pc += 2; + a_nz_read_addr: + CPU_READ_FAST( this, addr, TIME, nz ); + a = nz; + goto loop; + } + + case 0xBE:{// LDX abs,y + PAGE_CROSS_PENALTY( data + y ); + uint_fast16_t addr = GET_ADDR() + y; + pc += 2; + FLUSH_TIME(); + x = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + case 0xB5: // LDA zp,x + a = nz = READ_LOW( uint8_t (data + x) ); + pc++; + goto loop; + + case 0xA9: // LDA #imm + pc++; + a = data; + nz = data; + goto loop; + +// Bit operations + + case 0x3C: // BIT abs,x + data += x; + case 0x2C:{// BIT abs + uint_fast16_t addr; + ADD_PAGE( addr ); + FLUSH_TIME(); + nz = READ( addr ); + CACHE_TIME(); + goto bit_common; + } + case 0x34: // BIT zp,x + data = uint8_t (data + x); + case 0x24: // BIT zp + data = READ_LOW( data ); + case 0x89: // BIT imm + nz = data; + bit_common: + pc++; + status &= ~st_v; + status |= nz & st_v; + if ( nz & a ) + goto loop; // Z should be clear, and nz must be non-zero if nz & a is + nz <<= 8; // set Z flag without affecting N flag + goto loop; + + { + uint_fast16_t addr; + + case 0xB3: // TST abs,x + addr = GET_MSB() + x; + goto tst_abs; + + case 0x93: // TST abs + addr = GET_MSB(); + tst_abs: + addr += 0x100 * instr [2]; + pc++; + FLUSH_TIME(); + nz = READ( addr ); + CACHE_TIME(); + goto tst_common; + } + + case 0xA3: // TST zp,x + nz = READ_LOW( uint8_t (GET_MSB() + x) ); + goto tst_common; + + case 0x83: // TST zp + nz = READ_LOW( GET_MSB() ); + tst_common: + pc += 2; + status &= ~st_v; + status |= nz & st_v; + if ( nz & data ) + goto loop; // Z should be clear, and nz must be non-zero if nz & data is + nz <<= 8; // set Z flag without affecting N flag + goto loop; + + { + uint_fast16_t addr; + case 0x0C: // TSB abs + case 0x1C: // TRB abs + addr = GET_ADDR(); + pc++; + goto txb_addr; + + // TODO: everyone lists different behaviors for the status flags, ugh + case 0x04: // TSB zp + case 0x14: // TRB zp + addr = data + ram_addr; + txb_addr: + FLUSH_TIME(); + nz = a | READ( addr ); + if ( opcode & 0x10 ) + nz ^= a; // bits from a will already be set, so this clears them + status &= ~st_v; + status |= nz & st_v; + pc++; + WRITE( addr, nz ); + CACHE_TIME(); + goto loop; + } + + case 0x07: // RMBn + case 0x17: + case 0x27: + case 0x37: + case 0x47: + case 0x57: + case 0x67: + case 0x77: + pc++; + READ_LOW( data ) &= ~(1 << (opcode >> 4)); + goto loop; + + case 0x87: // SMBn + case 0x97: + case 0xA7: + case 0xB7: + case 0xC7: + case 0xD7: + case 0xE7: + case 0xF7: + pc++; + READ_LOW( data ) |= 1 << ((opcode >> 4) - 8); + goto loop; + +// Load/store + + case 0x9E: // STZ abs,x + data += x; + case 0x9C: // STZ abs + ADD_PAGE( data ); + pc++; + FLUSH_TIME(); + WRITE( data, 0 ); + CACHE_TIME(); + goto loop; + + case 0x74: // STZ zp,x + data = uint8_t (data + x); + case 0x64: // STZ zp + pc++; + WRITE_LOW( data, 0 ); + goto loop; + + case 0x94: // STY zp,x + data = uint8_t (data + x); + case 0x84: // STY zp + pc++; + WRITE_LOW( data, y ); + goto loop; + + case 0x96: // STX zp,y + data = uint8_t (data + y); + case 0x86: // STX zp + pc++; + WRITE_LOW( data, x ); + goto loop; + + case 0xB6: // LDX zp,y + data = uint8_t (data + y); + case 0xA6: // LDX zp + data = READ_LOW( data ); + case 0xA2: // LDX #imm + pc++; + x = data; + nz = data; + goto loop; + + case 0xB4: // LDY zp,x + data = uint8_t (data + x); + case 0xA4: // LDY zp + data = READ_LOW( data ); + case 0xA0: // LDY #imm + pc++; + y = data; + nz = data; + goto loop; + + case 0xBC: // LDY abs,X + data += x; + PAGE_CROSS_PENALTY( data ); + case 0xAC:{// LDY abs + uint_fast16_t addr = data + 0x100 * GET_MSB(); + pc += 2; + FLUSH_TIME(); + y = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + { + uint_fast8_t temp; + case 0x8C: // STY abs + temp = y; + goto store_abs; + + case 0x8E: // STX abs + temp = x; + store_abs: + uint_fast16_t addr = GET_ADDR(); + pc += 2; + FLUSH_TIME(); + WRITE( addr, temp ); + CACHE_TIME(); + goto loop; + } + +// Compare + + case 0xEC:{// CPX abs + uint_fast16_t addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( addr ); + CACHE_TIME(); + goto cpx_data; + } + + case 0xE4: // CPX zp + data = READ_LOW( data ); + case 0xE0: // CPX #imm + cpx_data: + nz = x - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0xCC:{// CPY abs + uint_fast16_t addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( addr ); + CACHE_TIME(); + goto cpy_data; + } + + case 0xC4: // CPY zp + data = READ_LOW( data ); + case 0xC0: // CPY #imm + cpy_data: + nz = y - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + +// Logical + +#define ARITH_ADDR_MODES( op )\ + case op - 0x04: /* (ind,x) */\ + data = uint8_t (data + x);\ + case op + 0x0D: /* (ind) */\ + data = 0x100 * READ_LOW( uint8_t (data + 1) ) + READ_LOW( data );\ + goto ptr##op;\ + case op + 0x0C:{/* (ind),y */\ + uint_fast16_t temp = READ_LOW( data ) + y;\ + PAGE_CROSS_PENALTY( temp );\ + data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ + goto ptr##op;\ + }\ + case op + 0x10: /* zp,X */\ + data = uint8_t (data + x);\ + case op + 0x00: /* zp */\ + data = READ_LOW( data );\ + goto imm##op;\ + case op + 0x14: /* abs,Y */\ + data += y;\ + goto ind##op;\ + case op + 0x18: /* abs,X */\ + data += x;\ + ind##op:\ + PAGE_CROSS_PENALTY( data );\ + case op + 0x08: /* abs */\ + ADD_PAGE( data );\ + ptr##op:\ + FLUSH_TIME();\ + data = READ( data );\ + CACHE_TIME();\ + case op + 0x04: /* imm */\ + imm##op: + + ARITH_ADDR_MODES( 0xC5 ) // CMP + nz = a - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + + ARITH_ADDR_MODES( 0x25 ) // AND + nz = (a &= data); + pc++; + goto loop; + + ARITH_ADDR_MODES( 0x45 ) // EOR + nz = (a ^= data); + pc++; + goto loop; + + ARITH_ADDR_MODES( 0x05 ) // ORA + nz = (a |= data); + pc++; + goto loop; + +// Add/subtract + + ARITH_ADDR_MODES( 0xE5 ) // SBC + data ^= 0xFF; + goto adc_imm; + + ARITH_ADDR_MODES( 0x65 ) // ADC + adc_imm: { + if ( status & st_d ) + debug_printf( "Decimal mode not supported\n" ); + int_fast16_t carry = c >> 8 & 1; + int_fast16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend + status &= ~st_v; + status |= ov >> 2 & 0x40; + c = nz = a + data + carry; + pc++; + a = (uint8_t) nz; + goto loop; + } + +// Shift/rotate + + case 0x4A: // LSR A + c = 0; + case 0x6A: // ROR A + nz = c >> 1 & 0x80; + c = a << 8; + nz |= a >> 1; + a = nz; + goto loop; + + case 0x0A: // ASL A + nz = a << 1; + c = nz; + a = (uint8_t) nz; + goto loop; + + case 0x2A: { // ROL A + nz = a << 1; + int_fast16_t temp = c >> 8 & 1; + c = nz; + nz |= temp; + a = (uint8_t) nz; + goto loop; + } + + case 0x5E: // LSR abs,X + data += x; + case 0x4E: // LSR abs + c = 0; + case 0x6E: // ROR abs + ror_abs: { + ADD_PAGE( data ); + FLUSH_TIME(); + int temp = READ( data ); + nz = (c >> 1 & 0x80) | (temp >> 1); + c = temp << 8; + goto rotate_common; + } + + case 0x3E: // ROL abs,X + data += x; + goto rol_abs; + + case 0x1E: // ASL abs,X + data += x; + case 0x0E: // ASL abs + c = 0; + case 0x2E: // ROL abs + rol_abs: + ADD_PAGE( data ); + nz = c >> 8 & 1; + FLUSH_TIME(); + nz |= (c = READ( data ) << 1); + rotate_common: + pc++; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + + case 0x7E: // ROR abs,X + data += x; + goto ror_abs; + + case 0x76: // ROR zp,x + data = uint8_t (data + x); + goto ror_zp; + + case 0x56: // LSR zp,x + data = uint8_t (data + x); + case 0x46: // LSR zp + c = 0; + case 0x66: // ROR zp + ror_zp: { + int temp = READ_LOW( data ); + nz = (c >> 1 & 0x80) | (temp >> 1); + c = temp << 8; + goto write_nz_zp; + } + + case 0x36: // ROL zp,x + data = uint8_t (data + x); + goto rol_zp; + + case 0x16: // ASL zp,x + data = uint8_t (data + x); + case 0x06: // ASL zp + c = 0; + case 0x26: // ROL zp + rol_zp: + nz = c >> 8 & 1; + nz |= (c = READ_LOW( data ) << 1); + goto write_nz_zp; + +// Increment/decrement + +#define INC_DEC_AXY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; + + case 0x1A: // INA + INC_DEC_AXY( a, +1 ) + + case 0xE8: // INX + INC_DEC_AXY( x, +1 ) + + case 0xC8: // INY + INC_DEC_AXY( y, +1 ) + + case 0x3A: // DEA + INC_DEC_AXY( a, -1 ) + + case 0xCA: // DEX + INC_DEC_AXY( x, -1 ) + + case 0x88: // DEY + INC_DEC_AXY( y, -1 ) + + case 0xF6: // INC zp,x + data = uint8_t (data + x); + case 0xE6: // INC zp + nz = 1; + goto add_nz_zp; + + case 0xD6: // DEC zp,x + data = uint8_t (data + x); + case 0xC6: // DEC zp + nz = (unsigned) -1; + add_nz_zp: + nz += READ_LOW( data ); + write_nz_zp: + pc++; + WRITE_LOW( data, nz ); + goto loop; + + case 0xFE: // INC abs,x + data = x + GET_ADDR(); + goto inc_ptr; + + case 0xEE: // INC abs + data = GET_ADDR(); + inc_ptr: + nz = 1; + goto inc_common; + + case 0xDE: // DEC abs,x + data = x + GET_ADDR(); + goto dec_ptr; + + case 0xCE: // DEC abs + data = GET_ADDR(); + dec_ptr: + nz = (unsigned) -1; + inc_common: + FLUSH_TIME(); + nz += READ( data ); + pc += 2; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + +// Transfer + + case 0xA8: // TAY + y = a; + nz = a; + goto loop; + + case 0x98: // TYA + a = y; + nz = y; + goto loop; + + case 0xAA: // TAX + x = a; + nz = a; + goto loop; + + case 0x8A: // TXA + a = x; + nz = x; + goto loop; + + case 0x9A: // TXS + SET_SP( x ); // verified (no flag change) + goto loop; + + case 0xBA: // TSX + x = nz = GET_SP(); + goto loop; + + #define SWAP_REGS( r1, r2 ) {\ + uint_fast8_t t = r1;\ + r1 = r2;\ + r2 = t;\ + goto loop;\ + } + + case 0x02: // SXY + SWAP_REGS( x, y ); + + case 0x22: // SAX + SWAP_REGS( a, x ); + + case 0x42: // SAY + SWAP_REGS( a, y ); + + case 0x62: // CLA + a = 0; + goto loop; + + case 0x82: // CLX + x = 0; + goto loop; + + case 0xC2: // CLY + y = 0; + goto loop; + +// Stack + + case 0x48: // PHA + PUSH( a ); + goto loop; + + case 0xDA: // PHX + PUSH( x ); + goto loop; + + case 0x5A: // PHY + PUSH( y ); + goto loop; + + case 0x40:{// RTI + uint_fast8_t temp = READ_LOW( sp ); + pc = READ_LOW( 0x100 | (sp - 0xFF) ); + pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; + sp = (sp - 0xFD) | 0x100; + data = status; + SET_STATUS( temp ); + this->r.status = status; // update externally-visible I flag + if ( (data ^ status) & st_i ) + { + hes_time_t new_time = end_time_; + if ( !(status & st_i) && new_time > irq_time_ ) + new_time = irq_time_; + blargg_long delta = s.base - new_time; + s.base = new_time; + s_time += delta; + } + goto loop; + } + + #define POP() READ_LOW( sp ); sp = (sp - 0xFF) | 0x100 + + case 0x68: // PLA + a = nz = POP(); + goto loop; + + case 0xFA: // PLX + x = nz = POP(); + goto loop; + + case 0x7A: // PLY + y = nz = POP(); + goto loop; + + case 0x28:{// PLP + uint_fast8_t temp = POP(); + uint_fast8_t changed = status ^ temp; + SET_STATUS( temp ); + if ( !(changed & st_i) ) + goto loop; // I flag didn't change + if ( status & st_i ) + goto handle_sei; + goto handle_cli; + } + #undef POP + + case 0x08: { // PHP + uint_fast8_t temp; + CALC_STATUS( temp ); + PUSH( temp | st_b ); + goto loop; + } + +// Flags + + case 0x38: // SEC + c = (unsigned) ~0; + goto loop; + + case 0x18: // CLC + c = 0; + goto loop; + + case 0xB8: // CLV + status &= ~st_v; + goto loop; + + case 0xD8: // CLD + status &= ~st_d; + goto loop; + + case 0xF8: // SED + status |= st_d; + goto loop; + + case 0x58: // CLI + if ( !(status & st_i) ) + goto loop; + status &= ~st_i; + handle_cli: { + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - irq_time_; + if ( delta <= 0 ) + { + if ( TIME < irq_time_ ) + goto loop; + goto delayed_cli; + } + s.base = irq_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + + if ( delta >= s_time + 1 ) + { + // delayed irq until after next instruction + s.base += s_time + 1; + s_time = -1; + irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop + goto loop; + } + delayed_cli: + debug_printf( "Delayed CLI not supported\n" ); // TODO: implement + goto loop; + } + + case 0x78: // SEI + if ( status & st_i ) + goto loop; + status |= st_i; + handle_sei: { + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - end_time_; + s.base = end_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + debug_printf( "Delayed SEI not supported\n" ); // TODO: implement + goto loop; + } + +// Special + + case 0x53:{// TAM + uint_fast8_t const bits = data; // avoid using data across function call + pc++; + for ( int i = 0; i < 8; i++ ) + if ( bits & (1 << i) ) + set_mmr( i, a ); + goto loop; + } + + case 0x43:{// TMA + pc++; + byte const* in = mmr; + do + { + if ( data & 1 ) + a = *in; + in++; + } + while ( (data >>= 1) != 0 ); + goto loop; + } + + case 0x03: // ST0 + case 0x13: // ST1 + case 0x23:{// ST2 + uint_fast16_t addr = opcode >> 4; + if ( addr ) + addr++; + pc++; + FLUSH_TIME(); + CPU_WRITE_VDP( this, addr, data, TIME ); + CACHE_TIME(); + goto loop; + } + + case 0xEA: // NOP + goto loop; + + case 0x54: // CSL + debug_printf( "CSL not supported\n" ); + illegal_encountered = true; + goto loop; + + case 0xD4: // CSH + goto loop; + + case 0xF4: { // SET + //fuint16 operand = GET_MSB(); + debug_printf( "SET not handled\n" ); + //switch ( data ) + //{ + //} + illegal_encountered = true; + goto loop; + } + +// Block transfer + + { + uint_fast16_t in_alt; + int_fast16_t in_inc; + uint_fast16_t out_alt; + int_fast16_t out_inc; + + case 0xE3: // TIA + in_alt = 0; + goto bxfer_alt; + + case 0xF3: // TAI + in_alt = 1; + bxfer_alt: + in_inc = in_alt ^ 1; + out_alt = in_inc; + out_inc = in_alt; + goto bxfer; + + case 0xD3: // TIN + in_inc = 1; + out_inc = 0; + goto bxfer_no_alt; + + case 0xC3: // TDD + in_inc = -1; + out_inc = -1; + goto bxfer_no_alt; + + case 0x73: // TII + in_inc = 1; + out_inc = 1; + bxfer_no_alt: + in_alt = 0; + out_alt = 0; + bxfer: + uint_fast16_t in = GET_LE16( instr + 0 ); + uint_fast16_t out = GET_LE16( instr + 2 ); + int count = GET_LE16( instr + 4 ); + if ( !count ) + count = 0x10000; + pc += 6; + WRITE_LOW( 0x100 | (sp - 1), y ); + WRITE_LOW( 0x100 | (sp - 2), a ); + WRITE_LOW( 0x100 | (sp - 3), x ); + FLUSH_TIME(); + do + { + // TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O + uint_fast8_t t = READ( in ); + in += in_inc; + in &= 0xFFFF; + s.time += 6; + if ( in_alt ) + in_inc = -in_inc; + WRITE( out, t ); + out += out_inc; + out &= 0xFFFF; + if ( out_alt ) + out_inc = -out_inc; + } + while ( --count ); + CACHE_TIME(); + goto loop; + } + +// Illegal + + default: + debug_printf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 ); + illegal_encountered = true; + goto loop; + } + assert( false ); + + int result_; +handle_brk: + pc++; + result_ = 6; + +interrupt: + { + s_time += 7; + + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); + WRITE_LOW( 0x100 | (sp - 2), pc ); + pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ ); + + sp = (sp - 3) | 0x100; + uint_fast8_t temp; + CALC_STATUS( temp ); + if ( result_ == 6 ) + temp |= st_b; + WRITE_LOW( sp, temp ); + + status &= ~st_d; + status |= st_i; + this->r.status = status; // update externally-visible I flag + + blargg_long delta = s.base - end_time_; + s.base = end_time_; + s_time += delta; + goto loop; + } + +idle_done: + s_time = 0; +out_of_time: + pc--; + FLUSH_TIME(); + CPU_DONE( this, TIME, result_ ); + CACHE_TIME(); + if ( result_ > 0 ) + goto interrupt; + if ( s_time < 0 ) + goto loop; + + s.time = s_time; + + r.pc = pc; + r.sp = GET_SP(); + r.a = a; + r.x = x; + r.y = y; + + { + uint_fast8_t temp; + CALC_STATUS( temp ); + r.status = temp; + } + + this->state_ = s; + this->state = &this->state_; + + return illegal_encountered; +} diff --git a/thirdparty/game-music-emu/gme/Hes_Cpu.h b/thirdparty/game-music-emu/gme/Hes_Cpu.h new file mode 100644 index 000000000..cec46fa9e --- /dev/null +++ b/thirdparty/game-music-emu/gme/Hes_Cpu.h @@ -0,0 +1,122 @@ +// PC Engine CPU emulator for use with HES music files + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef HES_CPU_H +#define HES_CPU_H + +#include "blargg_common.h" + +typedef blargg_long hes_time_t; // clock cycle count +typedef unsigned hes_addr_t; // 16-bit address +enum { future_hes_time = INT_MAX / 2 + 1 }; + +class Hes_Cpu { +public: + void reset(); + + enum { page_size = 0x2000 }; + enum { page_shift = 13 }; + enum { page_count = 8 }; + void set_mmr( int reg, int bank ); + + uint8_t const* get_code( hes_addr_t ); + + uint8_t ram [page_size]; + + // not kept updated during a call to run() + struct registers_t { + uint16_t pc; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t status; + uint8_t sp; + }; + registers_t r; + + // page mapping registers + uint8_t mmr [page_count + 1]; + + // Set end_time and run CPU from current time. Returns true if any illegal + // instructions were encountered. + bool run( hes_time_t end_time ); + + // Time of beginning of next instruction to be executed + hes_time_t time() const { return state->time + state->base; } + void set_time( hes_time_t t ) { state->time = t - state->base; } + void adjust_time( int delta ) { state->time += delta; } + + hes_time_t irq_time() const { return irq_time_; } + void set_irq_time( hes_time_t ); + + hes_time_t end_time() const { return end_time_; } + void set_end_time( hes_time_t ); + + void end_frame( hes_time_t ); + + // Attempt to execute instruction here results in CPU advancing time to + // lesser of irq_time() and end_time() (or end_time() if IRQs are + // disabled) + enum { idle_addr = 0x1FFF }; + + // Can read this many bytes past end of a page + enum { cpu_padding = 8 }; + +public: + Hes_Cpu() { state = &state_; } + enum { irq_inhibit = 0x04 }; +private: + // noncopyable + Hes_Cpu( const Hes_Cpu& ); + Hes_Cpu& operator = ( const Hes_Cpu& ); + + struct state_t { + uint8_t const* code_map [page_count + 1]; + hes_time_t base; + blargg_long time; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + hes_time_t irq_time_; + hes_time_t end_time_; + + void set_code_page( int, void const* ); + inline int update_end_time( hes_time_t end, hes_time_t irq ); +}; + +inline uint8_t const* Hes_Cpu::get_code( hes_addr_t addr ) +{ + return state->code_map [addr >> page_shift] + addr + #if !BLARGG_NONPORTABLE + % (unsigned) page_size + #endif + ; +} + +inline int Hes_Cpu::update_end_time( hes_time_t t, hes_time_t irq ) +{ + if ( irq < t && !(r.status & irq_inhibit) ) t = irq; + int delta = state->base - t; + state->base = t; + return delta; +} + +inline void Hes_Cpu::set_irq_time( hes_time_t t ) +{ + state->time += update_end_time( end_time_, (irq_time_ = t) ); +} + +inline void Hes_Cpu::set_end_time( hes_time_t t ) +{ + state->time += update_end_time( (end_time_ = t), irq_time_ ); +} + +inline void Hes_Cpu::end_frame( hes_time_t t ) +{ + assert( state == &state_ ); + state_.base -= t; + if ( irq_time_ < future_hes_time ) irq_time_ -= t; + if ( end_time_ < future_hes_time ) end_time_ -= t; +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Hes_Emu.cpp b/thirdparty/game-music-emu/gme/Hes_Emu.cpp new file mode 100644 index 000000000..818691fdc --- /dev/null +++ b/thirdparty/game-music-emu/gme/Hes_Emu.cpp @@ -0,0 +1,531 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Hes_Emu.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const timer_mask = 0x04; +int const vdp_mask = 0x02; +int const i_flag_mask = 0x04; +int const unmapped = 0xFF; + +long const period_60hz = 262 * 455L; // scanlines * clocks per scanline + +Hes_Emu::Hes_Emu() +{ + timer.raw_load = 0; + set_type( gme_hes_type ); + + static const char* const names [Hes_Apu::osc_count] = { + "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Multi 1", "Multi 2" + }; + set_voice_names( names ); + + static int const types [Hes_Apu::osc_count] = { + wave_type | 0, wave_type | 1, wave_type | 2, wave_type | 3, + mixed_type | 0, mixed_type | 1 + }; + set_voice_types( types ); + set_silence_lookahead( 6 ); + set_gain( 1.11 ); +} + +Hes_Emu::~Hes_Emu() { } + +void Hes_Emu::unload() +{ + rom.clear(); + Music_Emu::unload(); +} + +// Track info + +static byte const* copy_field( byte const* in, char* out ) +{ + if ( in ) + { + int len = 0x20; + if ( in [0x1F] && !in [0x2F] ) + len = 0x30; // fields are sometimes 16 bytes longer (ugh) + + // since text fields are where any data could be, detect non-text + // and fields with data after zero byte terminator + + int i = 0; + for ( i = 0; i < len && in [i]; i++ ) + if ( ((in [i] + 1) & 0xFF) < ' ' + 1 ) // also treat 0xFF as non-text + return 0; // non-ASCII found + + for ( ; i < len; i++ ) + if ( in [i] ) + return 0; // data after terminator + + Gme_File::copy_field_( out, (char const*) in, len ); + in += len; + } + return in; +} + +static void copy_hes_fields( byte const* in, track_info_t* out ) +{ + if ( *in >= ' ' ) + { + in = copy_field( in, out->game ); + in = copy_field( in, out->author ); + in = copy_field( in, out->copyright ); + } +} + +blargg_err_t Hes_Emu::track_info_( track_info_t* out, int ) const +{ + copy_hes_fields( rom.begin() + 0x20, out ); + return 0; +} + +static blargg_err_t check_hes_header( void const* header ) +{ + if ( memcmp( header, "HESM", 4 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Hes_File : Gme_Info_ +{ + struct header_t { + char header [Hes_Emu::header_size]; + char unused [0x20]; + byte fields [0x30 * 3]; + } h; + + Hes_File() { set_type( gme_hes_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + assert( offsetof (header_t,fields) == Hes_Emu::header_size + 0x20 ); + blargg_err_t err = in.read( &h, sizeof h ); + if ( err ) + return (err == in.eof_error ? gme_wrong_file_type : err); + return check_hes_header( &h ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_hes_fields( h.fields, out ); + return 0; + } +}; + +static Music_Emu* new_hes_emu () { return BLARGG_NEW Hes_Emu ; } +static Music_Emu* new_hes_file() { return BLARGG_NEW Hes_File; } + +static gme_type_t_ const gme_hes_type_ = { "PC Engine", 256, &new_hes_emu, &new_hes_file, "HES", 1 }; +BLARGG_EXPORT extern gme_type_t const gme_hes_type = &gme_hes_type_; + + +// Setup + +blargg_err_t Hes_Emu::load_( Data_Reader& in ) +{ + assert( offsetof (header_t,unused [4]) == header_size ); + RETURN_ERR( rom.load( in, header_size, &header_, unmapped ) ); + + RETURN_ERR( check_hes_header( header_.tag ) ); + + if ( header_.vers != 0 ) + set_warning( "Unknown file version" ); + + if ( memcmp( header_.data_tag, "DATA", 4 ) ) + set_warning( "Data header missing" ); + + if ( memcmp( header_.unused, "\0\0\0\0", 4 ) ) + set_warning( "Unknown header data" ); + + // File spec supports multiple blocks, but I haven't found any, and + // many files have bad sizes in the only block, so it's simpler to + // just try to load the damn data as best as possible. + + long addr = get_le32( header_.addr ); + long size = get_le32( header_.size ); + long const rom_max = 0x100000; + if ( addr & ~(rom_max - 1) ) + { + set_warning( "Invalid address" ); + addr &= rom_max - 1; + } + if ( (unsigned long) (addr + size) > (unsigned long) rom_max ) + set_warning( "Invalid size" ); + + if ( size != rom.file_size() ) + { + if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) ) + set_warning( "Multiple DATA not supported" ); + else if ( size < rom.file_size() ) + set_warning( "Extra file data" ); + else + set_warning( "Missing file data" ); + } + + rom.set_addr( addr ); + + set_voice_count( apu.osc_count ); + + apu.volume( gain() ); + + return setup_buffer( 7159091 ); +} + +void Hes_Emu::update_eq( blip_eq_t const& eq ) +{ + apu.treble_eq( eq ); +} + +void Hes_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + apu.osc_output( i, center, left, right ); +} + +// Emulation + +void Hes_Emu::recalc_timer_load() +{ + timer.load = timer.raw_load * timer_base + 1; +} + +void Hes_Emu::set_tempo_( double t ) +{ + play_period = hes_time_t (period_60hz / t); + timer_base = int (1024 / t); + recalc_timer_load(); +} + +blargg_err_t Hes_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + memset( ram, 0, sizeof ram ); // some HES music relies on zero fill + memset( sgx, 0, sizeof sgx ); + + apu.reset(); + cpu::reset(); + + for ( unsigned i = 0; i < sizeof header_.banks; i++ ) + set_mmr( i, header_.banks [i] ); + set_mmr( page_count, 0xFF ); // unmapped beyond end of address space + + irq.disables = timer_mask | vdp_mask; + irq.timer = future_hes_time; + irq.vdp = future_hes_time; + + timer.enabled = false; + timer.raw_load= 0x80; + timer.count = timer.load; + timer.fired = false; + timer.last_time = 0; + + vdp.latch = 0; + vdp.control = 0; + vdp.next_vbl = 0; + + ram [0x1FF] = (idle_addr - 1) >> 8; + ram [0x1FE] = (idle_addr - 1) & 0xFF; + r.sp = 0xFD; + r.pc = get_le16( header_.init_addr ); + r.a = track; + + recalc_timer_load(); + last_frame_hook = 0; + + return 0; +} + +// Hardware + +void Hes_Emu::cpu_write_vdp( int addr, int data ) +{ + switch ( addr ) + { + case 0: + vdp.latch = data & 0x1F; + break; + + case 2: + if ( vdp.latch == 5 ) + { + if ( data & 0x04 ) + set_warning( "Scanline interrupt unsupported" ); + run_until( time() ); + vdp.control = data; + irq_changed(); + } + else + { + debug_printf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data ); + } + break; + + case 3: + debug_printf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data ); + break; + } +} + +void Hes_Emu::cpu_write_( hes_addr_t addr, int data ) +{ + if ( unsigned (addr - apu.start_addr) <= apu.end_addr - apu.start_addr ) + { + GME_APU_HOOK( this, addr - apu.start_addr, data ); + // avoid going way past end when a long block xfer is writing to I/O space + hes_time_t t = min( time(), end_time() + 8 ); + apu.write_data( t, addr, data ); + return; + } + + hes_time_t time = this->time(); + switch ( addr ) + { + case 0x0000: + case 0x0002: + case 0x0003: + cpu_write_vdp( addr, data ); + return; + + case 0x0C00: { + run_until( time ); + timer.raw_load = (data & 0x7F) + 1; + recalc_timer_load(); + timer.count = timer.load; + break; + } + + case 0x0C01: + data &= 1; + if ( timer.enabled == data ) + return; + run_until( time ); + timer.enabled = data; + if ( data ) + timer.count = timer.load; + break; + + case 0x1402: + run_until( time ); + irq.disables = data; + if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values + debug_printf( "Int mask: $%02X\n", data ); + break; + + case 0x1403: + run_until( time ); + if ( timer.enabled ) + timer.count = timer.load; + timer.fired = false; + break; + +#ifndef NDEBUG + case 0x1000: // I/O port + case 0x0402: // palette + case 0x0403: + case 0x0404: + case 0x0405: + return; + + default: + debug_printf( "unmapped write $%04X <- $%02X\n", addr, data ); + return; +#endif + } + + irq_changed(); +} + +int Hes_Emu::cpu_read_( hes_addr_t addr ) +{ + hes_time_t time = this->time(); + addr &= page_size - 1; + switch ( addr ) + { + case 0x0000: + if ( irq.vdp > time ) + return 0; + irq.vdp = future_hes_time; + run_until( time ); + irq_changed(); + return 0x20; + + case 0x0002: + case 0x0003: + debug_printf( "VDP read not supported: %d\n", addr ); + return 0; + + case 0x0C01: + //return timer.enabled; // TODO: remove? + case 0x0C00: + run_until( time ); + debug_printf( "Timer count read\n" ); + return (unsigned) (timer.count - 1) / timer_base; + + case 0x1402: + return irq.disables; + + case 0x1403: + { + int status = 0; + if ( irq.timer <= time ) status |= timer_mask; + if ( irq.vdp <= time ) status |= vdp_mask; + return status; + } + + #ifndef NDEBUG + case 0x1000: // I/O port + case 0x180C: // CD-ROM + case 0x180D: + break; + + default: + debug_printf( "unmapped read $%04X\n", addr ); + #endif + } + + return unmapped; +} + +// see hes_cpu_io.h for core read/write functions + +// Emulation + +void Hes_Emu::run_until( hes_time_t present ) +{ + while ( vdp.next_vbl < present ) + vdp.next_vbl += play_period; + + hes_time_t elapsed = present - timer.last_time; + if ( elapsed > 0 ) + { + if ( timer.enabled ) + { + timer.count -= elapsed; + if ( timer.count <= 0 ) + timer.count += timer.load; + } + timer.last_time = present; + } +} + +void Hes_Emu::irq_changed() +{ + hes_time_t present = time(); + + if ( irq.timer > present ) + { + irq.timer = future_hes_time; + if ( timer.enabled && !timer.fired ) + irq.timer = present + timer.count; + } + + if ( irq.vdp > present ) + { + irq.vdp = future_hes_time; + if ( vdp.control & 0x08 ) + irq.vdp = vdp.next_vbl; + } + + hes_time_t time = future_hes_time; + if ( !(irq.disables & timer_mask) ) time = irq.timer; + if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp ); + + set_irq_time( time ); +} + +int Hes_Emu::cpu_done() +{ + check( time() >= end_time() || + (!(r.status & i_flag_mask) && time() >= irq_time()) ); + + if ( !(r.status & i_flag_mask) ) + { + hes_time_t present = time(); + + if ( irq.timer <= present && !(irq.disables & timer_mask) ) + { + timer.fired = true; + irq.timer = future_hes_time; + irq_changed(); // overkill, but not worth writing custom code + #if GME_FRAME_HOOK_DEFINED + { + unsigned const threshold = period_60hz / 30; + unsigned long elapsed = present - last_frame_hook; + if ( elapsed - period_60hz + threshold / 2 < threshold ) + { + last_frame_hook = present; + GME_FRAME_HOOK( this ); + } + } + #endif + return 0x0A; + } + + if ( irq.vdp <= present && !(irq.disables & vdp_mask) ) + { + // work around for bugs with music not acknowledging VDP + //run_until( present ); + //irq.vdp = future_hes_time; + //irq_changed(); + #if GME_FRAME_HOOK_DEFINED + last_frame_hook = present; + GME_FRAME_HOOK( this ); + #endif + return 0x08; + } + } + return 0; +} + +static void adjust_time( blargg_long& time, hes_time_t delta ) +{ + if ( time < future_hes_time ) + { + time -= delta; + if ( time < 0 ) + time = 0; + } +} + +blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int ) +{ + blip_time_t const duration = duration_; // cache + + if ( cpu::run( duration ) ) + set_warning( "Emulation error (illegal instruction)" ); + + check( time() >= duration ); + //check( time() - duration < 20 ); // Txx instruction could cause going way over + + run_until( duration ); + + // end time frame + timer.last_time -= duration; + vdp.next_vbl -= duration; + #if GME_FRAME_HOOK_DEFINED + last_frame_hook -= duration; + #endif + cpu::end_frame( duration ); + ::adjust_time( irq.timer, duration ); + ::adjust_time( irq.vdp, duration ); + apu.end_frame( duration ); + + return 0; +} diff --git a/thirdparty/game-music-emu/gme/Hes_Emu.h b/thirdparty/game-music-emu/gme/Hes_Emu.h new file mode 100644 index 000000000..08c1370d4 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Hes_Emu.h @@ -0,0 +1,94 @@ +// TurboGrafx-16/PC Engine HES music file emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef HES_EMU_H +#define HES_EMU_H + +#include "Classic_Emu.h" +#include "Hes_Apu.h" +#include "Hes_Cpu.h" + +class Hes_Emu : private Hes_Cpu, public Classic_Emu { + typedef Hes_Cpu cpu; +public: + // HES file header + enum { header_size = 0x20 }; + struct header_t + { + byte tag [4]; + byte vers; + byte first_track; + byte init_addr [2]; + byte banks [8]; + byte data_tag [4]; + byte size [4]; + byte addr [4]; + byte unused [4]; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + static gme_type_t static_type() { return gme_hes_type; } + +public: + Hes_Emu(); + ~Hes_Emu(); +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_( Data_Reader& ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); + void unload(); +public: private: friend class Hes_Cpu; + byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space + + int cpu_read_( hes_addr_t ); + int cpu_read( hes_addr_t ); + void cpu_write_( hes_addr_t, int data ); + void cpu_write( hes_addr_t, int ); + void cpu_write_vdp( int addr, int data ); + byte const* cpu_set_mmr( int page, int bank ); + int cpu_done(); +private: + Rom_Data rom; + header_t header_; + hes_time_t play_period; + hes_time_t last_frame_hook; + int timer_base; + + struct { + hes_time_t last_time; + blargg_long count; + blargg_long load; + int raw_load; + byte enabled; + byte fired; + } timer; + + struct { + hes_time_t next_vbl; + byte latch; + byte control; + } vdp; + + struct { + hes_time_t timer; + hes_time_t vdp; + byte disables; + } irq; + + void recalc_timer_load(); + + // large items + Hes_Apu apu; + byte sgx [3 * page_size + cpu_padding]; + + void irq_changed(); + void run_until( hes_time_t ); +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Kss_Cpu.cpp b/thirdparty/game-music-emu/gme/Kss_Cpu.cpp new file mode 100644 index 000000000..f3857680f --- /dev/null +++ b/thirdparty/game-music-emu/gme/Kss_Cpu.cpp @@ -0,0 +1,1700 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +/* +Last validated with zexall 2006.11.14 2:19 PM +* Doesn't implement the R register or immediate interrupt after EI. +* Address wrap-around isn't completely correct, but is prevented from crashing emulator. +*/ + +#include "Kss_Cpu.h" + +#include "blargg_endian.h" +#include + +//#include "z80_cpu_log.h" + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#define SYNC_TIME() (void) (s.time = s_time) +#define RELOAD_TIME() (void) (s_time = s.time) + +// Callbacks to emulator + +#define CPU_OUT( cpu, addr, data, time )\ + kss_cpu_out( this, time, addr, data ) + +#define CPU_IN( cpu, addr, time )\ + kss_cpu_in( this, time, addr ) + +#define CPU_WRITE( cpu, addr, data, time )\ + (SYNC_TIME(), kss_cpu_write( this, addr, data )) + +#include "blargg_source.h" + +// flags, named with hex value for clarity +int const S80 = 0x80; +int const Z40 = 0x40; +int const F20 = 0x20; +int const H10 = 0x10; +int const F08 = 0x08; +int const V04 = 0x04; +int const P04 = 0x04; +int const N02 = 0x02; +int const C01 = 0x01; + +#define SZ28P( n ) szpc [n] +#define SZ28PC( n ) szpc [n] +#define SZ28C( n ) (szpc [n] & ~P04) +#define SZ28( n ) SZ28C( n ) + +#define SET_R( n ) (void) (r.r = n) +#define GET_R() (r.r) + +Kss_Cpu::Kss_Cpu() +{ + state = &state_; + + for ( int i = 0x100; --i >= 0; ) + { + int even = 1; + for ( int p = i; p; p >>= 1 ) + even ^= p; + int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04); + szpc [i] = n; + szpc [i + 0x100] = n | C01; + } + szpc [0x000] |= Z40; + szpc [0x100] |= Z40; +} + +inline void Kss_Cpu::set_page( int i, void* write, void const* read ) +{ + blargg_long offset = KSS_CPU_PAGE_OFFSET( i * (blargg_long) page_size ); + state->write [i] = (byte *) write - offset; + state->read [i] = (byte const*) read - offset; +} + +void Kss_Cpu::reset( void* unmapped_write, void const* unmapped_read ) +{ + check( state == &state_ ); + state = &state_; + state_.time = 0; + state_.base = 0; + end_time_ = 0; + + for ( int i = 0; i < page_count + 1; i++ ) + set_page( i, unmapped_write, unmapped_read ); + + memset( &r, 0, sizeof r ); +} + +void Kss_Cpu::map_mem( unsigned addr, blargg_ulong size, void* write, void const* read ) +{ + // address range must begin and end on page boundaries + require( addr % page_size == 0 ); + require( size % page_size == 0 ); + + unsigned first_page = addr / page_size; + for ( unsigned i = size / page_size; i--; ) + { + blargg_long offset = i * (blargg_long) page_size; + set_page( first_page + i, (byte*) write + offset, (byte const*) read + offset ); + } +} + +#define TIME (s_time + s.base) +#define RW_MEM( addr, rw ) (s.rw [(addr) >> page_shift] [KSS_CPU_PAGE_OFFSET( addr )]) +#define READ_PROG( addr ) RW_MEM( addr, read ) +#define READ( addr ) READ_PROG( addr ) +//#define WRITE( addr, data ) (void) (RW_MEM( addr, write ) = data) +#define WRITE( addr, data ) CPU_WRITE( this, addr, data, TIME ) +#define READ_WORD( addr ) GET_LE16( &READ( addr ) ) +#define WRITE_WORD( addr, data ) SET_LE16( &RW_MEM( addr, write ), data ) +#define IN( addr ) CPU_IN( this, addr, TIME ) +#define OUT( addr, data ) CPU_OUT( this, addr, data, TIME ) + +#if BLARGG_BIG_ENDIAN + #define R8( n, offset ) ((r8_ - offset) [n]) +#elif BLARGG_LITTLE_ENDIAN + #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) +#else + #error "Byte order of CPU must be known" +#endif + +//#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)]) + +// help compiler see that it can just adjust stack offset, saving an extra instruction +#define R16( n, shift, offset )\ + (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) + +#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e +#define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f +#define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g +#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h + +// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8 +static byte const ed_dd_timing [0x100] = { +//0 1 2 3 4 5 6 7 8 9 A B C D E F +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00, +0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0, +0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, +0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00, +0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +bool Kss_Cpu::run( cpu_time_t end_time ) +{ + set_end_time( end_time ); + state_t s = this->state_; + this->state = &s; + bool warning = false; + + union { + regs_t rg; + pairs_t rp; + uint8_t r8_ [8]; // indexed + uint16_t r16_ [4]; + }; + rg = this->r.b; + + cpu_time_t s_time = s.time; + uint_fast32_t pc = r.pc; + uint_fast32_t sp = r.sp; + uint_fast32_t ix = r.ix; // TODO: keep in memory for direct access? + uint_fast32_t iy = r.iy; + int flags = r.b.flags; + + goto loop; +jr_not_taken: + s_time -= 5; + goto loop; +call_not_taken: + s_time -= 7; +jp_not_taken: + pc += 2; +loop: + + check( (unsigned long) pc < 0x10000 ); + check( (unsigned long) sp < 0x10000 ); + check( (unsigned) flags < 0x100 ); + check( (unsigned) ix < 0x10000 ); + check( (unsigned) iy < 0x10000 ); + + uint8_t const* instr = s.read [pc >> page_shift]; +#define GET_ADDR() GET_LE16( instr ) + + uint_fast8_t opcode; + + // TODO: eliminate this special case + #if BLARGG_NONPORTABLE + opcode = instr [pc]; + pc++; + instr += pc; + #else + instr += KSS_CPU_PAGE_OFFSET( pc ); + opcode = *instr++; + pc++; + #endif + + static byte const base_timing [0x100] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0 + 13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1 + 12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2 + 12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6 + 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B + 11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C + 11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D + 11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E + 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F + }; + + uint_fast16_t data; + data = base_timing [opcode]; + if ( (s_time += data) >= 0 ) + goto possibly_out_of_time; +almost_out_of_time: + + data = READ_PROG( pc ); + + #ifdef Z80_CPU_LOG_H + //log_opcode( opcode, READ_PROG( pc ) ); + z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy ); + z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ), + READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) ); + #endif + + switch ( opcode ) + { +possibly_out_of_time: + if ( s_time < (int) data ) + goto almost_out_of_time; + s_time -= data; + goto out_of_time; + +// Common + + case 0x00: // NOP + CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc. + goto loop; + + case 0x08:{// EX AF,AF' + int temp = r.alt.b.a; + r.alt.b.a = rg.a; + rg.a = temp; + + temp = r.alt.b.flags; + r.alt.b.flags = flags; + flags = temp; + goto loop; + } + + case 0xD3: // OUT (imm),A + pc++; + OUT( data + rg.a * 0x100, rg.a ); + goto loop; + + case 0x2E: // LD L,imm + pc++; + rg.l = data; + goto loop; + + case 0x3E: // LD A,imm + pc++; + rg.a = data; + goto loop; + + case 0x3A:{// LD A,(addr) + uint_fast16_t addr = GET_ADDR(); + pc += 2; + rg.a = READ( addr ); + goto loop; + } + +// Conditional + +#define ZERO (flags & Z40) +#define CARRY (flags & C01) +#define EVEN (flags & P04) +#define MINUS (flags & S80) + +// JR +// TODO: more efficient way to handle negative branch that wraps PC around +#define JR( cond ) {\ + int offset = (int8_t) data;\ + pc++;\ + if ( !(cond) )\ + goto jr_not_taken;\ + pc = uint16_t (pc + offset);\ + goto loop;\ +} + + case 0x20: JR( !ZERO ) // JR NZ,disp + case 0x28: JR( ZERO ) // JR Z,disp + case 0x30: JR( !CARRY ) // JR NC,disp + case 0x38: JR( CARRY ) // JR C,disp + case 0x18: JR( true ) // JR disp + + case 0x10:{// DJNZ disp + int temp = rg.b - 1; + rg.b = temp; + JR( temp ) + } + +// JP +#define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop; + + case 0xC2: JP( !ZERO ) // JP NZ,addr + case 0xCA: JP( ZERO ) // JP Z,addr + case 0xD2: JP( !CARRY ) // JP NC,addr + case 0xDA: JP( CARRY ) // JP C,addr + case 0xE2: JP( !EVEN ) // JP PO,addr + case 0xEA: JP( EVEN ) // JP PE,addr + case 0xF2: JP( !MINUS ) // JP P,addr + case 0xFA: JP( MINUS ) // JP M,addr + + case 0xC3: // JP addr + pc = GET_ADDR(); + goto loop; + + case 0xE9: // JP HL + pc = rp.hl; + goto loop; + +// RET +#define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop; + + case 0xC0: RET( !ZERO ) // RET NZ + case 0xC8: RET( ZERO ) // RET Z + case 0xD0: RET( !CARRY ) // RET NC + case 0xD8: RET( CARRY ) // RET C + case 0xE0: RET( !EVEN ) // RET PO + case 0xE8: RET( EVEN ) // RET PE + case 0xF0: RET( !MINUS ) // RET P + case 0xF8: RET( MINUS ) // RET M + + case 0xC9: // RET + ret_taken: + pc = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto loop; + +// CALL +#define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken; + + case 0xC4: CALL( !ZERO ) // CALL NZ,addr + case 0xCC: CALL( ZERO ) // CALL Z,addr + case 0xD4: CALL( !CARRY ) // CALL NC,addr + case 0xDC: CALL( CARRY ) // CALL C,addr + case 0xE4: CALL( !EVEN ) // CALL PO,addr + case 0xEC: CALL( EVEN ) // CALL PE,addr + case 0xF4: CALL( !MINUS ) // CALL P,addr + case 0xFC: CALL( MINUS ) // CALL M,addr + + case 0xCD:{// CALL addr + call_taken: + uint_fast16_t addr = pc + 2; + pc = GET_ADDR(); + sp = uint16_t (sp - 2); + WRITE_WORD( sp, addr ); + goto loop; + } + + case 0xFF: // RST + if ( pc > idle_addr ) + goto hit_idle_addr; + CASE7( C7, CF, D7, DF, E7, EF, F7 ): + data = pc; + pc = opcode & 0x38; + goto push_data; + +// PUSH/POP + case 0xF5: // PUSH AF + data = rg.a * 0x100u + flags; + goto push_data; + + case 0xC5: // PUSH BC + case 0xD5: // PUSH DE + case 0xE5: // PUSH HL + data = R16( opcode, 4, 0xC5 ); + push_data: + sp = uint16_t (sp - 2); + WRITE_WORD( sp, data ); + goto loop; + + case 0xF1: // POP AF + flags = READ( sp ); + rg.a = READ( sp + 1 ); + sp = uint16_t (sp + 2); + goto loop; + + case 0xC1: // POP BC + case 0xD1: // POP DE + case 0xE1: // POP HL + R16( opcode, 4, 0xC1 ) = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto loop; + +// ADC/ADD/SBC/SUB + case 0x96: // SUB (HL) + case 0x86: // ADD (HL) + flags &= ~C01; + case 0x9E: // SBC (HL) + case 0x8E: // ADC (HL) + data = READ( rp.hl ); + goto adc_data; + + case 0xD6: // SUB A,imm + case 0xC6: // ADD imm + flags &= ~C01; + case 0xDE: // SBC A,imm + case 0xCE: // ADC imm + pc++; + goto adc_data; + + CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r + CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r + flags &= ~C01; + CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r + CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r + data = R8( opcode & 7, 0 ); + adc_data: { + int result = data + (flags & C01); + data ^= rg.a; + flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes + if ( flags ) + result = -result; + result += rg.a; + data ^= result; + flags |=(data & H10) | + ((data - -0x80) >> 6 & V04) | + SZ28C( result & 0x1FF ); + rg.a = result; + goto loop; + } + +// CP + case 0xBE: // CP (HL) + data = READ( rp.hl ); + goto cp_data; + + case 0xFE: // CP imm + pc++; + goto cp_data; + + CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r + data = R8( opcode, 0xB8 ); + cp_data: { + int result = rg.a - data; + flags = N02 | (data & (F20 | F08)) | (result >> 8 & C01); + data ^= rg.a; + flags |=(((result ^ rg.a) & data) >> 5 & V04) | + (((data & H10) ^ result) & (S80 | H10)); + if ( (uint8_t) result ) + goto loop; + flags |= Z40; + goto loop; + } + +// ADD HL,rp + + case 0x39: // ADD HL,SP + data = sp; + goto add_hl_data; + + case 0x09: // ADD HL,BC + case 0x19: // ADD HL,DE + case 0x29: // ADD HL,HL + data = R16( opcode, 4, 0x09 ); + add_hl_data: { + blargg_ulong sum = rp.hl + data; + data ^= rp.hl; + rp.hl = sum; + flags = (flags & (S80 | Z40 | V04)) | + (sum >> 16) | + (sum >> 8 & (F20 | F08)) | + ((data ^ sum) >> 8 & H10); + goto loop; + } + + case 0x27:{// DAA + int a = rg.a; + if ( a > 0x99 ) + flags |= C01; + + int adjust = 0x60 & -(flags & C01); + + if ( flags & H10 || (a & 0x0F) > 9 ) + adjust |= 0x06; + + if ( flags & N02 ) + adjust = -adjust; + a += adjust; + + flags = (flags & (C01 | N02)) | + ((rg.a ^ a) & H10) | + SZ28P( (uint8_t) a ); + rg.a = a; + goto loop; + } + /* + case 0x27:{// DAA + // more optimized, but probably not worth the obscurity + int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags + int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0 + + if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9 + adjust |= 0x06; + + if ( f & N02 ) + adjust = -adjust; + int a = rg.a + adjust; + + flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); + rg.a = a; + goto loop; + } + */ + +// INC/DEC + case 0x34: // INC (HL) + data = READ( rp.hl ) + 1; + WRITE( rp.hl, data ); + goto inc_set_flags; + + CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r + data = ++R8( opcode >> 3, 0 ); + inc_set_flags: + flags = (flags & C01) | + (((data & 0x0F) - 1) & H10) | + SZ28( (uint8_t) data ); + if ( data != 0x80 ) + goto loop; + flags |= V04; + goto loop; + + case 0x35: // DEC (HL) + data = READ( rp.hl ) - 1; + WRITE( rp.hl, data ); + goto dec_set_flags; + + CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r + data = --R8( opcode >> 3, 0 ); + dec_set_flags: + flags = (flags & C01) | N02 | + (((data & 0x0F) + 1) & H10) | + SZ28( (uint8_t) data ); + if ( data != 0x7F ) + goto loop; + flags |= V04; + goto loop; + + case 0x03: // INC BC + case 0x13: // INC DE + case 0x23: // INC HL + R16( opcode, 4, 0x03 )++; + goto loop; + + case 0x33: // INC SP + sp = uint16_t (sp + 1); + goto loop; + + case 0x0B: // DEC BC + case 0x1B: // DEC DE + case 0x2B: // DEC HL + R16( opcode, 4, 0x0B )--; + goto loop; + + case 0x3B: // DEC SP + sp = uint16_t (sp - 1); + goto loop; + +// AND + case 0xA6: // AND (HL) + data = READ( rp.hl ); + goto and_data; + + case 0xE6: // AND imm + pc++; + goto and_data; + + CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r + data = R8( opcode, 0xA0 ); + and_data: + rg.a &= data; + flags = SZ28P( rg.a ) | H10; + goto loop; + +// OR + case 0xB6: // OR (HL) + data = READ( rp.hl ); + goto or_data; + + case 0xF6: // OR imm + pc++; + goto or_data; + + CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r + data = R8( opcode, 0xB0 ); + or_data: + rg.a |= data; + flags = SZ28P( rg.a ); + goto loop; + +// XOR + case 0xAE: // XOR (HL) + data = READ( rp.hl ); + goto xor_data; + + case 0xEE: // XOR imm + pc++; + goto xor_data; + + CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r + data = R8( opcode, 0xA8 ); + xor_data: + rg.a ^= data; + flags = SZ28P( rg.a ); + goto loop; + +// LD + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r + WRITE( rp.hl, R8( opcode, 0x70 ) ); + goto loop; + + CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r + CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r + CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r + CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r + CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r + CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r + CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r + R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 ); + goto loop; + + CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm + R8( opcode >> 3, 0 ) = data; + pc++; + goto loop; + + case 0x36: // LD (HL),imm + pc++; + WRITE( rp.hl, data ); + goto loop; + + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL) + R8( opcode >> 3, 8 ) = READ( rp.hl ); + goto loop; + + case 0x01: // LD rp,imm + case 0x11: + case 0x21: + R16( opcode, 4, 0x01 ) = GET_ADDR(); + pc += 2; + goto loop; + + case 0x31: // LD sp,imm + sp = GET_ADDR(); + pc += 2; + goto loop; + + case 0x2A:{// LD HL,(addr) + uint_fast16_t addr = GET_ADDR(); + pc += 2; + rp.hl = READ_WORD( addr ); + goto loop; + } + + case 0x32:{// LD (addr),A + uint_fast16_t addr = GET_ADDR(); + pc += 2; + WRITE( addr, rg.a ); + goto loop; + } + + case 0x22:{// LD (addr),HL + uint_fast16_t addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, rp.hl ); + goto loop; + } + + case 0x02: // LD (BC),A + case 0x12: // LD (DE),A + WRITE( R16( opcode, 4, 0x02 ), rg.a ); + goto loop; + + case 0x0A: // LD A,(BC) + case 0x1A: // LD A,(DE) + rg.a = READ( R16( opcode, 4, 0x0A ) ); + goto loop; + + case 0xF9: // LD SP,HL + sp = rp.hl; + goto loop; + +// Rotate + + case 0x07:{// RLCA + uint_fast16_t temp = rg.a; + temp = (temp << 1) | (temp >> 7); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08 | C01)); + rg.a = temp; + goto loop; + } + + case 0x0F:{// RRCA + uint_fast16_t temp = rg.a; + flags = (flags & (S80 | Z40 | P04)) | + (temp & C01); + temp = (temp << 7) | (temp >> 1); + flags |= temp & (F20 | F08); + rg.a = temp; + goto loop; + } + + case 0x17:{// RLA + blargg_ulong temp = (rg.a << 1) | (flags & C01); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08)) | + (temp >> 8); + rg.a = (uint8_t)temp; + goto loop; + } + + case 0x1F:{// RRA + uint_fast16_t temp = (flags << 7) | (rg.a >> 1); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08)) | + (rg.a & C01); + rg.a = temp; + goto loop; + } + +// Misc + case 0x2F:{// CPL + uint_fast16_t temp = ~rg.a; + flags = (flags & (S80 | Z40 | P04 | C01)) | + (temp & (F20 | F08)) | + (H10 | N02); + rg.a = temp; + goto loop; + } + + case 0x3F:{// CCF + flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) | + (flags << 4 & H10) | + (rg.a & (F20 | F08)); + goto loop; + } + + case 0x37: // SCF + flags = (flags & (S80 | Z40 | P04)) | C01 | + (rg.a & (F20 | F08)); + goto loop; + + case 0xDB: // IN A,(imm) + pc++; + rg.a = IN( data + rg.a * 0x100 ); + goto loop; + + case 0xE3:{// EX (SP),HL + uint_fast16_t temp = READ_WORD( sp ); + WRITE_WORD( sp, rp.hl ); + rp.hl = temp; + goto loop; + } + + case 0xEB:{// EX DE,HL + uint_fast16_t temp = rp.hl; + rp.hl = rp.de; + rp.de = temp; + goto loop; + } + + case 0xD9:{// EXX DE,HL + uint_fast16_t temp = r.alt.w.bc; + r.alt.w.bc = rp.bc; + rp.bc = temp; + + temp = r.alt.w.de; + r.alt.w.de = rp.de; + rp.de = temp; + + temp = r.alt.w.hl; + r.alt.w.hl = rp.hl; + rp.hl = temp; + goto loop; + } + + case 0xF3: // DI + r.iff1 = 0; + r.iff2 = 0; + goto loop; + + case 0xFB: // EI + r.iff1 = 1; + r.iff2 = 1; + // TODO: delayed effect + goto loop; + + case 0x76: // HALT + goto halt; + +//////////////////////////////////////// CB prefix + { + case 0xCB: + unsigned data2; + data2 = instr [1]; + (void) data2; // TODO is this the same as data in all cases? + pc++; + switch ( data ) + { + + // Rotate left + + #define RLC( read, write ) {\ + uint_fast8_t result = read;\ + result = uint8_t (result << 1) | (result >> 7);\ + flags = SZ28P( result ) | (result & C01);\ + write;\ + goto loop;\ + } + + case 0x06: // RLC (HL) + s_time += 7; + data = rp.hl; + rlc_data_addr: + RLC( READ( data ), WRITE( data, result ) ) + + CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r + uint8_t& reg = R8( data, 0 ); + RLC( reg, reg = result ) + } + + #define RL( read, write ) {\ + uint_fast16_t result = (read << 1) | (flags & C01);\ + flags = SZ28PC( result );\ + write;\ + goto loop;\ + } + + case 0x16: // RL (HL) + s_time += 7; + data = rp.hl; + rl_data_addr: + RL( READ( data ), WRITE( data, result ) ) + + CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r + uint8_t& reg = R8( data, 0x10 ); + RL( reg, reg = result ) + } + + #define SLA( read, add, write ) {\ + uint_fast16_t result = (read << 1) | add;\ + flags = SZ28PC( result );\ + write;\ + goto loop;\ + } + + case 0x26: // SLA (HL) + s_time += 7; + data = rp.hl; + sla_data_addr: + SLA( READ( data ), 0, WRITE( data, result ) ) + + CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r + uint8_t& reg = R8( data, 0x20 ); + SLA( reg, 0, reg = result ) + } + + case 0x36: // SLL (HL) + s_time += 7; + data = rp.hl; + sll_data_addr: + SLA( READ( data ), 1, WRITE( data, result ) ) + + CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r + uint8_t& reg = R8( data, 0x30 ); + SLA( reg, 1, reg = result ) + } + + // Rotate right + + #define RRC( read, write ) {\ + uint_fast8_t result = read;\ + flags = result & C01;\ + result = uint8_t (result << 7) | (result >> 1);\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x0E: // RRC (HL) + s_time += 7; + data = rp.hl; + rrc_data_addr: + RRC( READ( data ), WRITE( data, result ) ) + + CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r + uint8_t& reg = R8( data, 0x08 ); + RRC( reg, reg = result ) + } + + #define RR( read, write ) {\ + uint_fast8_t result = read;\ + uint_fast8_t temp = result & C01;\ + result = uint8_t (flags << 7) | (result >> 1);\ + flags = SZ28P( result ) | temp;\ + write;\ + goto loop;\ + } + + case 0x1E: // RR (HL) + s_time += 7; + data = rp.hl; + rr_data_addr: + RR( READ( data ), WRITE( data, result ) ) + + CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r + uint8_t& reg = R8( data, 0x18 ); + RR( reg, reg = result ) + } + + #define SRA( read, write ) {\ + uint_fast8_t result = read;\ + flags = result & C01;\ + result = (result & 0x80) | (result >> 1);\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x2E: // SRA (HL) + data = rp.hl; + s_time += 7; + sra_data_addr: + SRA( READ( data ), WRITE( data, result ) ) + + CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r + uint8_t& reg = R8( data, 0x28 ); + SRA( reg, reg = result ) + } + + #define SRL( read, write ) {\ + uint_fast8_t result = read;\ + flags = result & C01;\ + result >>= 1;\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x3E: // SRL (HL) + s_time += 7; + data = rp.hl; + srl_data_addr: + SRL( READ( data ), WRITE( data, result ) ) + + CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r + uint8_t& reg = R8( data, 0x38 ); + SRL( reg, reg = result ) + } + + // BIT + { + unsigned temp; + CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL) + s_time += 4; + temp = READ( rp.hl ); + flags &= C01; + goto bit_temp; + CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r + CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r + CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r + CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r + CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r + CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r + CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r + temp = R8( data & 7, 0 ); + flags = (flags & C01) | (temp & (F20 | F08)); + bit_temp: + int masked = temp & 1 << (data >> 3 & 7); + flags |=(masked & S80) | H10 | + ((masked - 1) >> 8 & (Z40 | P04)); + goto loop; + } + + // SET/RES + CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL) + CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL) + s_time += 7; + int temp = READ( rp.hl ); + int bit = 1 << (data >> 3 & 7); + temp |= bit; // SET + if ( !(data & 0x40) ) + temp ^= bit; // RES + WRITE( rp.hl, temp ); + goto loop; + } + + CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r + CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r + CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r + CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r + CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r + CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r + CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r + CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r + R8( data & 7, 0 ) |= 1 << (data >> 3 & 7); + goto loop; + + CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r + CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r + CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r + CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r + CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r + CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r + CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r + CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r + R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7)); + goto loop; + } + assert( false ); + } + +#undef GET_ADDR +#define GET_ADDR() GET_LE16( instr + 1 ) + +//////////////////////////////////////// ED prefix + { + case 0xED: + pc++; + s_time += ed_dd_timing [data] >> 4; + switch ( data ) + { + { + blargg_ulong temp; + case 0x72: // SBC HL,SP + case 0x7A: // ADC HL,SP + temp = sp; + if ( 0 ) + case 0x42: // SBC HL,BC + case 0x52: // SBC HL,DE + case 0x62: // SBC HL,HL + case 0x4A: // ADC HL,BC + case 0x5A: // ADC HL,DE + case 0x6A: // ADC HL,HL + temp = R16( data >> 3 & 6, 1, 0 ); + blargg_ulong sum = temp + (flags & C01); + flags = ~data >> 2 & N02; + if ( flags ) + sum = -sum; + sum += rp.hl; + temp ^= rp.hl; + temp ^= sum; + flags |=(sum >> 16 & C01) | + (temp >> 8 & H10) | + (sum >> 8 & (S80 | F20 | F08)) | + ((temp - -0x8000) >> 14 & V04); + rp.hl = sum; + if ( (uint16_t) sum ) + goto loop; + flags |= Z40; + goto loop; + } + + CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C) + int temp = IN( rp.bc ); + R8( data >> 3, 8 ) = temp; + flags = (flags & C01) | SZ28P( temp ); + goto loop; + } + + case 0x71: // OUT (C),0 + rg.flags = 0; + CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r + OUT( rp.bc, R8( data >> 3, 8 ) ); + goto loop; + + { + unsigned temp; + case 0x73: // LD (ADDR),SP + temp = sp; + if ( 0 ) + case 0x43: // LD (ADDR),BC + case 0x53: // LD (ADDR),DE + temp = R16( data, 4, 0x43 ); + uint_fast16_t addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, temp ); + goto loop; + } + + case 0x4B: // LD BC,(ADDR) + case 0x5B:{// LD DE,(ADDR) + uint_fast16_t addr = GET_ADDR(); + pc += 2; + R16( data, 4, 0x4B ) = READ_WORD( addr ); + goto loop; + } + + case 0x7B:{// LD SP,(ADDR) + uint_fast16_t addr = GET_ADDR(); + pc += 2; + sp = READ_WORD( addr ); + goto loop; + } + + case 0x67:{// RRD + uint_fast8_t temp = READ( rp.hl ); + WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); + temp = (rg.a & 0xF0) | (temp & 0x0F); + flags = (flags & C01) | SZ28P( temp ); + rg.a = temp; + goto loop; + } + + case 0x6F:{// RLD + uint_fast8_t temp = READ( rp.hl ); + WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); + temp = (rg.a & 0xF0) | (temp >> 4); + flags = (flags & C01) | SZ28P( temp ); + rg.a = temp; + goto loop; + } + + CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG + opcode = 0x10; // flag to do SBC instead of ADC + flags &= ~C01; + data = rg.a; + rg.a = 0; + goto adc_data; + + { + int inc; + case 0xA9: // CPD + case 0xB9: // CPDR + inc = -1; + if ( 0 ) + case 0xA1: // CPI + case 0xB1: // CPIR + inc = +1; + uint_fast16_t addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + int result = rg.a - temp; + flags = (flags & C01) | N02 | + ((((temp ^ rg.a) & H10) ^ result) & (S80 | H10)); + + if ( !(uint8_t) result ) flags |= Z40; + result -= (flags & H10) >> 4; + flags |= result & F08; + flags |= result << 4 & F20; + if ( !--rp.bc ) + goto loop; + + flags |= V04; + if ( flags & Z40 || data < 0xB0 ) + goto loop; + + pc -= 2; + s_time += 5; + goto loop; + } + + { + int inc; + case 0xA8: // LDD + case 0xB8: // LDDR + inc = -1; + if ( 0 ) + case 0xA0: // LDI + case 0xB0: // LDIR + inc = +1; + uint_fast16_t addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + addr = rp.de; + rp.de = addr + inc; + WRITE( addr, temp ); + + temp += rg.a; + flags = (flags & (S80 | Z40 | C01)) | + (temp & F08) | (temp << 4 & F20); + if ( !--rp.bc ) + goto loop; + + flags |= V04; + if ( data < 0xB0 ) + goto loop; + + pc -= 2; + s_time += 5; + goto loop; + } + + { + int inc; + case 0xAB: // OUTD + case 0xBB: // OTDR + inc = -1; + if ( 0 ) + case 0xA3: // OUTI + case 0xB3: // OTIR + inc = +1; + uint_fast16_t addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + int b = --rg.b; + flags = (temp >> 6 & N02) | SZ28( b ); + if ( b && data >= 0xB0 ) + { + pc -= 2; + s_time += 5; + } + + OUT( rp.bc, temp ); + goto loop; + } + + { + int inc; + case 0xAA: // IND + case 0xBA: // INDR + inc = -1; + if ( 0 ) + case 0xA2: // INI + case 0xB2: // INIR + inc = +1; + + uint_fast16_t addr = rp.hl; + rp.hl = addr + inc; + + int temp = IN( rp.bc ); + + int b = --rg.b; + flags = (temp >> 6 & N02) | SZ28( b ); + if ( b && data >= 0xB0 ) + { + pc -= 2; + s_time += 5; + } + + WRITE( addr, temp ); + goto loop; + } + + case 0x47: // LD I,A + r.i = rg.a; + goto loop; + + case 0x4F: // LD R,A + SET_R( rg.a ); + debug_printf( "LD R,A not supported\n" ); + warning = true; + goto loop; + + case 0x57: // LD A,I + rg.a = r.i; + goto ld_ai_common; + + case 0x5F: // LD A,R + rg.a = GET_R(); + debug_printf( "LD A,R not supported\n" ); + warning = true; + ld_ai_common: + flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04); + goto loop; + + CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN + r.iff1 = r.iff2; + goto ret_taken; + + case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0 + r.im = 0; + goto loop; + + case 0x56: case 0x76: // IM 1 + r.im = 1; + goto loop; + + case 0x5E: case 0x7E: // IM 2 + r.im = 2; + goto loop; + + default: + debug_printf( "Opcode $ED $%02X not supported\n", data ); + warning = true; + goto loop; + } + assert( false ); + } + +//////////////////////////////////////// DD/FD prefix + { + uint_fast16_t ixy; + case 0xDD: + ixy = ix; + goto ix_prefix; + case 0xFD: + ixy = iy; + ix_prefix: + pc++; + unsigned data2 = READ_PROG( pc ); + s_time += ed_dd_timing [data] & 0x0F; + switch ( data ) + { + // TODO: more efficient way of avoid negative address + // TODO: avoid using this as argument to READ() since it is evaluated twice + #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) + + #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; + + // ADD/ADC/SUB/SBC + + case 0x96: // SUB (IXY+disp) + case 0x86: // ADD (IXY+disp) + flags &= ~C01; + case 0x9E: // SBC (IXY+disp) + case 0x8E: // ADC (IXY+disp) + pc++; + opcode = data; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto adc_data; + + case 0x94: // SUB HXY + case 0x84: // ADD HXY + flags &= ~C01; + case 0x9C: // SBC HXY + case 0x8C: // ADC HXY + opcode = data; + data = ixy >> 8; + goto adc_data; + + case 0x95: // SUB LXY + case 0x85: // ADD LXY + flags &= ~C01; + case 0x9D: // SBC LXY + case 0x8D: // ADC LXY + opcode = data; + data = (uint8_t) ixy; + goto adc_data; + + { + unsigned temp; + case 0x39: // ADD IXY,SP + temp = sp; + goto add_ixy_data; + + case 0x29: // ADD IXY,HL + temp = ixy; + goto add_ixy_data; + + case 0x09: // ADD IXY,BC + case 0x19: // ADD IXY,DE + temp = R16( data, 4, 0x09 ); + add_ixy_data: { + blargg_ulong sum = ixy + temp; + temp ^= ixy; + ixy = (uint16_t) sum; + flags = (flags & (S80 | Z40 | V04)) | + (sum >> 16) | + (sum >> 8 & (F20 | F08)) | + ((temp ^ sum) >> 8 & H10); + goto set_ixy; + } + } + + // AND + case 0xA6: // AND (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto and_data; + + case 0xA4: // AND HXY + data = ixy >> 8; + goto and_data; + + case 0xA5: // AND LXY + data = (uint8_t) ixy; + goto and_data; + + // OR + case 0xB6: // OR (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto or_data; + + case 0xB4: // OR HXY + data = ixy >> 8; + goto or_data; + + case 0xB5: // OR LXY + data = (uint8_t) ixy; + goto or_data; + + // XOR + case 0xAE: // XOR (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto xor_data; + + case 0xAC: // XOR HXY + data = ixy >> 8; + goto xor_data; + + case 0xAD: // XOR LXY + data = (uint8_t) ixy; + goto xor_data; + + // CP + case 0xBE: // CP (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto cp_data; + + case 0xBC: // CP HXY + data = ixy >> 8; + goto cp_data; + + case 0xBD: // CP LXY + data = (uint8_t) ixy; + goto cp_data; + + // LD + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r + data = R8( data, 0x70 ); + if ( 0 ) + case 0x36: // LD (IXY+disp),imm + pc++, data = READ_PROG( pc ); + pc++; + WRITE( IXY_DISP( ixy, (int8_t) data2 ), data ); + goto loop; + + CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY + R8( data >> 3, 8 ) = ixy >> 8; + goto loop; + + case 0x64: // LD HXY,HXY + case 0x6D: // LD LXY,LXY + goto loop; + + CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY + R8( data >> 3, 8 ) = ixy; + goto loop; + + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) + pc++; + R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto loop; + + case 0x26: // LD HXY,imm + pc++; + goto ld_hxy_data; + + case 0x65: // LD HXY,LXY + data2 = (uint8_t) ixy; + goto ld_hxy_data; + + CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r + data2 = R8( data, 0x60 ); + ld_hxy_data: + ixy = (uint8_t) ixy | (data2 << 8); + goto set_ixy; + + case 0x2E: // LD LXY,imm + pc++; + goto ld_lxy_data; + + case 0x6C: // LD LXY,HXY + data2 = ixy >> 8; + goto ld_lxy_data; + + CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r + data2 = R8( data, 0x68 ); + ld_lxy_data: + ixy = (ixy & 0xFF00) | data2; + set_ixy: + if ( opcode == 0xDD ) + { + ix = ixy; + goto loop; + } + iy = ixy; + goto loop; + + case 0xF9: // LD SP,IXY + sp = ixy; + goto loop; + + case 0x22:{// LD (ADDR),IXY + uint_fast16_t addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, ixy ); + goto loop; + } + + case 0x21: // LD IXY,imm + ixy = GET_ADDR(); + pc += 2; + goto set_ixy; + + case 0x2A:{// LD IXY,(addr) + uint_fast16_t addr = GET_ADDR(); + ixy = READ_WORD( addr ); + pc += 2; + goto set_ixy; + } + + // DD/FD CB prefix + case 0xCB: { + data = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data2 = READ_PROG( pc ); + pc++; + switch ( data2 ) + { + case 0x06: goto rlc_data_addr; // RLC (IXY) + case 0x16: goto rl_data_addr; // RL (IXY) + case 0x26: goto sla_data_addr; // SLA (IXY) + case 0x36: goto sll_data_addr; // SLL (IXY) + case 0x0E: goto rrc_data_addr; // RRC (IXY) + case 0x1E: goto rr_data_addr; // RR (IXY) + case 0x2E: goto sra_data_addr; // SRA (IXY) + case 0x3E: goto srl_data_addr; // SRL (IXY) + + CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) + uint_fast8_t temp = READ( data ); + int masked = temp & 1 << (data2 >> 3 & 7); + flags = (flags & C01) | H10 | + (masked & S80) | + ((masked - 1) >> 8 & (Z40 | P04)); + goto loop; + } + + CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp) + CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp) + int temp = READ( data ); + int bit = 1 << (data2 >> 3 & 7); + temp |= bit; // SET + if ( !(data2 & 0x40) ) + temp ^= bit; // RES + WRITE( data, temp ); + goto loop; + } + + default: + debug_printf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 ); + warning = true; + goto loop; + } + assert( false ); + } + + // INC/DEC + case 0x23: // INC IXY + ixy = uint16_t (ixy + 1); + goto set_ixy; + + case 0x2B: // DEC IXY + ixy = uint16_t (ixy - 1); + goto set_ixy; + + case 0x34: // INC (IXY+disp) + ixy = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data = READ( ixy ) + 1; + WRITE( ixy, data ); + goto inc_set_flags; + + case 0x35: // DEC (IXY+disp) + ixy = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data = READ( ixy ) - 1; + WRITE( ixy, data ); + goto dec_set_flags; + + case 0x24: // INC HXY + ixy = uint16_t (ixy + 0x100); + data = ixy >> 8; + goto inc_xy_common; + + case 0x2C: // INC LXY + data = uint8_t (ixy + 1); + ixy = (ixy & 0xFF00) | data; + inc_xy_common: + if ( opcode == 0xDD ) + { + ix = ixy; + goto inc_set_flags; + } + iy = ixy; + goto inc_set_flags; + + case 0x25: // DEC HXY + ixy = uint16_t (ixy - 0x100); + data = ixy >> 8; + goto dec_xy_common; + + case 0x2D: // DEC LXY + data = uint8_t (ixy - 1); + ixy = (ixy & 0xFF00) | data; + dec_xy_common: + if ( opcode == 0xDD ) + { + ix = ixy; + goto dec_set_flags; + } + iy = ixy; + goto dec_set_flags; + + // PUSH/POP + case 0xE5: // PUSH IXY + data = ixy; + goto push_data; + + case 0xE1:{// POP IXY + ixy = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto set_ixy; + } + + // Misc + + case 0xE9: // JP (IXY) + pc = ixy; + goto loop; + + case 0xE3:{// EX (SP),IXY + uint_fast16_t temp = READ_WORD( sp ); + WRITE_WORD( sp, ixy ); + ixy = temp; + goto set_ixy; + } + + default: + debug_printf( "Unnecessary DD/FD prefix encountered\n" ); + warning = true; + pc--; + goto loop; + } + assert( false ); + } + + } + debug_printf( "Unhandled main opcode: $%02X\n", opcode ); + assert( false ); + +hit_idle_addr: + s_time -= 11; + goto out_of_time; +halt: + s_time &= 3; // increment by multiple of 4 +out_of_time: + pc--; + + s.time = s_time; + rg.flags = flags; + r.ix = ix; + r.iy = iy; + r.sp = sp; + r.pc = pc; + this->r.b = rg; + this->state_ = s; + this->state = &this->state_; + + return warning; +} diff --git a/thirdparty/game-music-emu/gme/Kss_Cpu.h b/thirdparty/game-music-emu/gme/Kss_Cpu.h new file mode 100644 index 000000000..d31864cd3 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Kss_Cpu.h @@ -0,0 +1,120 @@ +// Z80 CPU emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef KSS_CPU_H +#define KSS_CPU_H + +#include "blargg_endian.h" + +typedef blargg_long cpu_time_t; + +// must be defined by caller +void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data ); +int kss_cpu_in( class Kss_Cpu*, cpu_time_t, unsigned addr ); +void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data ); + +class Kss_Cpu { +public: + // Clear registers and map all pages to unmapped + void reset( void* unmapped_write, void const* unmapped_read ); + + // Map memory. Start and size must be multiple of page_size. + enum { page_size = 0x2000 }; + void map_mem( unsigned addr, blargg_ulong size, void* write, void const* read ); + + // Map address to page + uint8_t* write( unsigned addr ); + uint8_t const* read( unsigned addr ); + + // Run until specified time is reached. Returns true if suspicious/unsupported + // instruction was encountered at any point during run. + bool run( cpu_time_t end_time ); + + // Time of beginning of next instruction + cpu_time_t time() const { return state->time + state->base; } + + // Alter current time. Not supported during run() call. + void set_time( cpu_time_t t ) { state->time = t - state->base; } + void adjust_time( int delta ) { state->time += delta; } + + #if BLARGG_BIG_ENDIAN + struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; + #else + struct regs_t { uint8_t c, b, e, d, l, h, a, flags; }; + #endif + BOOST_STATIC_ASSERT( sizeof (regs_t) == 8 ); + + struct pairs_t { uint16_t bc, de, hl, fa; }; + + // Registers are not updated until run() returns + struct registers_t { + uint16_t pc; + uint16_t sp; + uint16_t ix; + uint16_t iy; + union { + regs_t b; // b.b, b.c, b.d, b.e, b.h, b.l, b.flags, b.a + pairs_t w; // w.bc, w.de, w.hl. w.fa + }; + union { + regs_t b; + pairs_t w; + } alt; + uint8_t iff1; + uint8_t iff2; + uint8_t r; + uint8_t i; + uint8_t im; + }; + //registers_t r; (below for efficiency) + + enum { idle_addr = 0xFFFF }; + + // can read this far past end of a page + enum { cpu_padding = 0x100 }; + +public: + Kss_Cpu(); + enum { page_shift = 13 }; + enum { page_count = 0x10000 >> page_shift }; +private: + uint8_t szpc [0x200]; + cpu_time_t end_time_; + struct state_t { + uint8_t const* read [page_count + 1]; + uint8_t * write [page_count + 1]; + cpu_time_t base; + cpu_time_t time; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + void set_end_time( cpu_time_t t ); + void set_page( int i, void* write, void const* read ); +public: + registers_t r; +}; + +#if BLARGG_NONPORTABLE + #define KSS_CPU_PAGE_OFFSET( addr ) (addr) +#else + #define KSS_CPU_PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) +#endif + +inline uint8_t* Kss_Cpu::write( unsigned addr ) +{ + return state->write [addr >> page_shift] + KSS_CPU_PAGE_OFFSET( addr ); +} + +inline uint8_t const* Kss_Cpu::read( unsigned addr ) +{ + return state->read [addr >> page_shift] + KSS_CPU_PAGE_OFFSET( addr ); +} + +inline void Kss_Cpu::set_end_time( cpu_time_t t ) +{ + cpu_time_t delta = state->base - t; + state->base = t; + state->time += delta; +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Kss_Emu.cpp b/thirdparty/game-music-emu/gme/Kss_Emu.cpp new file mode 100644 index 000000000..fd4905ce3 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Kss_Emu.cpp @@ -0,0 +1,416 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Kss_Emu.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +long const clock_rate = 3579545; +int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; + +Kss_Emu::Kss_Emu() +{ + sn = 0; + set_type( gme_kss_type ); + set_silence_lookahead( 6 ); + static const char* const names [osc_count] = { + "Square 1", "Square 2", "Square 3", + "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type | 0, wave_type | 1, wave_type | 2, + wave_type | 3, wave_type | 4, wave_type | 5, wave_type | 6, wave_type | 7 + }; + set_voice_types( types ); + + memset( unmapped_read, 0xFF, sizeof unmapped_read ); +} + +Kss_Emu::~Kss_Emu() { unload(); } + +void Kss_Emu::unload() +{ + delete sn; + sn = 0; + Classic_Emu::unload(); +} + +// Track info + +static void copy_kss_fields( Kss_Emu::header_t const& h, track_info_t* out ) +{ + const char* system = "MSX"; + if ( h.device_flags & 0x02 ) + { + system = "Sega Master System"; + if ( h.device_flags & 0x04 ) + system = "Game Gear"; + } + Gme_File::copy_field_( out->system, system ); +} + +blargg_err_t Kss_Emu::track_info_( track_info_t* out, int ) const +{ + copy_kss_fields( header_, out ); + return 0; +} + +static blargg_err_t check_kss_header( void const* header ) +{ + if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Kss_File : Gme_Info_ +{ + Kss_Emu::header_t header_; + + Kss_File() { set_type( gme_kss_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + blargg_err_t err = in.read( &header_, Kss_Emu::header_size ); + if ( err ) + return (err == in.eof_error ? gme_wrong_file_type : err); + return check_kss_header( &header_ ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_kss_fields( header_, out ); + return 0; + } +}; + +static Music_Emu* new_kss_emu () { return BLARGG_NEW Kss_Emu ; } +static Music_Emu* new_kss_file() { return BLARGG_NEW Kss_File; } + +static gme_type_t_ const gme_kss_type_ = { "MSX", 256, &new_kss_emu, &new_kss_file, "KSS", 0x03 }; +BLARGG_EXPORT extern gme_type_t const gme_kss_type = &gme_kss_type_; + + +// Setup + +void Kss_Emu::update_gain() +{ + double g = gain() * 1.4; + if ( scc_accessed ) + g *= 1.5; + ay.volume( g ); + scc.volume( g ); + if ( sn ) + sn->volume( g ); +} + +blargg_err_t Kss_Emu::load_( Data_Reader& in ) +{ + memset( &header_, 0, sizeof header_ ); + assert( offsetof (header_t,device_flags) == header_size - 1 ); + assert( offsetof (ext_header_t,msx_audio_vol) == ext_header_size - 1 ); + RETURN_ERR( rom.load( in, header_size, STATIC_CAST(header_t*,&header_), 0 ) ); + + RETURN_ERR( check_kss_header( header_.tag ) ); + + if ( header_.tag [3] == 'C' ) + { + if ( header_.extra_header ) + { + header_.extra_header = 0; + set_warning( "Unknown data in header" ); + } + if ( header_.device_flags & ~0x0F ) + { + header_.device_flags &= 0x0F; + set_warning( "Unknown data in header" ); + } + } + else + { + ext_header_t& ext = header_; + memcpy( &ext, rom.begin(), min( (int) ext_header_size, (int) header_.extra_header ) ); + if ( header_.extra_header > 0x10 ) + set_warning( "Unknown data in header" ); + } + + if ( header_.device_flags & 0x09 ) + set_warning( "FM sound not supported" ); + + scc_enabled = 0xC000; + if ( header_.device_flags & 0x04 ) + scc_enabled = 0; + + if ( header_.device_flags & 0x02 && !sn ) + CHECK_ALLOC( sn = BLARGG_NEW( Sms_Apu ) ); + + set_voice_count( osc_count ); + + return setup_buffer( ::clock_rate ); +} + +void Kss_Emu::update_eq( blip_eq_t const& eq ) +{ + ay.treble_eq( eq ); + scc.treble_eq( eq ); + if ( sn ) + sn->treble_eq( eq ); +} + +void Kss_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + int i2 = i - ay.osc_count; + if ( i2 >= 0 ) + scc.osc_output( i2, center ); + else + ay.osc_output( i, center ); + if ( sn && i < sn->osc_count ) + sn->osc_output( i, center, left, right ); +} + +// Emulation + +void Kss_Emu::set_tempo_( double t ) +{ + blip_time_t period = + (header_.device_flags & 0x40 ? ::clock_rate / 50 : ::clock_rate / 60); + play_period = blip_time_t (period / t); +} + +blargg_err_t Kss_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + memset( ram, 0xC9, 0x4000 ); + memset( ram + 0x4000, 0, sizeof ram - 0x4000 ); + + // copy driver code to lo RAM + static byte const bios [] = { + 0xD3, 0xA0, 0xF5, 0x7B, 0xD3, 0xA1, 0xF1, 0xC9, // $0001: WRTPSG + 0xD3, 0xA0, 0xDB, 0xA2, 0xC9 // $0009: RDPSG + }; + static byte const vectors [] = { + 0xC3, 0x01, 0x00, // $0093: WRTPSG vector + 0xC3, 0x09, 0x00, // $0096: RDPSG vector + }; + memcpy( ram + 0x01, bios, sizeof bios ); + memcpy( ram + 0x93, vectors, sizeof vectors ); + + // copy non-banked data into RAM + unsigned load_addr = get_le16( header_.load_addr ); + long orig_load_size = get_le16( header_.load_size ); + long load_size = min( orig_load_size, rom.file_size() ); + load_size = min( load_size, long (mem_size - load_addr) ); + if ( load_size != orig_load_size ) + set_warning( "Excessive data size" ); + memcpy( ram + load_addr, rom.begin() + header_.extra_header, load_size ); + + rom.set_addr( -load_size - header_.extra_header ); + + // check available bank data + blargg_long const bank_size = this->bank_size(); + int max_banks = (rom.file_size() - load_size + bank_size - 1) / bank_size; + bank_count = header_.bank_mode & 0x7F; + if ( bank_count > max_banks ) + { + bank_count = max_banks; + set_warning( "Bank data missing" ); + } + //debug_printf( "load_size : $%X\n", load_size ); + //debug_printf( "bank_size : $%X\n", bank_size ); + //debug_printf( "bank_count: %d (%d claimed)\n", bank_count, header_.bank_mode & 0x7F ); + + ram [idle_addr] = 0xFF; + cpu::reset( unmapped_write, unmapped_read ); + cpu::map_mem( 0, mem_size, ram, ram ); + + ay.reset(); + scc.reset(); + if ( sn ) + sn->reset(); + r.sp = 0xF380; + ram [--r.sp] = idle_addr >> 8; + ram [--r.sp] = idle_addr & 0xFF; + r.b.a = track; + r.pc = get_le16( header_.init_addr ); + next_play = play_period; + scc_accessed = false; + gain_updated = false; + update_gain(); + ay_latch = 0; + + return 0; +} + +void Kss_Emu::set_bank( int logical, int physical ) +{ + unsigned const bank_size = this->bank_size(); + + unsigned addr = 0x8000; + if ( logical && bank_size == 8 * 1024 ) + addr = 0xA000; + + physical -= header_.first_bank; + if ( (unsigned) physical >= (unsigned) bank_count ) + { + byte* data = ram + addr; + cpu::map_mem( addr, bank_size, data, data ); + } + else + { + long phys = physical * (blargg_long) bank_size; + for ( unsigned offset = 0; offset < bank_size; offset += page_size ) + cpu::map_mem( addr + offset, page_size, + unmapped_write, rom.at_addr( phys + offset ) ); + } +} + +void Kss_Emu::cpu_write( unsigned addr, int data ) +{ + data &= 0xFF; + switch ( addr ) + { + case 0x9000: + set_bank( 0, data ); + return; + + case 0xB000: + set_bank( 1, data ); + return; + } + + int scc_addr = (addr & 0xDFFF) ^ 0x9800; + if ( scc_addr < scc.reg_count ) + { + scc_accessed = true; + scc.write( time(), scc_addr, data ); + return; + } + + debug_printf( "LD ($%04X),$%02X\n", addr, data ); +} + +void kss_cpu_write( Kss_Cpu* cpu, unsigned addr, int data ) +{ + *cpu->write( addr ) = data; + if ( (addr & STATIC_CAST(Kss_Emu&,*cpu).scc_enabled) == 0x8000 ) + STATIC_CAST(Kss_Emu&,*cpu).cpu_write( addr, data ); +} + +void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) +{ + data &= 0xFF; + Kss_Emu& emu = STATIC_CAST(Kss_Emu&,*cpu); + switch ( addr & 0xFF ) + { + case 0xA0: + emu.ay_latch = data & 0x0F; + return; + + case 0xA1: + GME_APU_HOOK( &emu, emu.ay_latch, data ); + emu.ay.write( time, emu.ay_latch, data ); + return; + + case 0x06: + if ( emu.sn && (emu.header_.device_flags & 0x04) ) + { + emu.sn->write_ggstereo( time, data ); + return; + } + break; + + case 0x7E: + case 0x7F: + if ( emu.sn ) + { + GME_APU_HOOK( &emu, 16, data ); + emu.sn->write_data( time, data ); + return; + } + break; + + case 0xFE: + emu.set_bank( 0, data ); + return; + + #ifndef NDEBUG + case 0xF1: // FM data + if ( data ) + break; // trap non-zero data + case 0xF0: // FM addr + case 0xA8: // PPI + return; + #endif + } + + debug_printf( "OUT $%04X,$%02X\n", addr, data ); +} + +int kss_cpu_in( Kss_Cpu*, cpu_time_t, unsigned addr ) +{ + //Kss_Emu& emu = STATIC_CAST(Kss_Emu&,*cpu); + //switch ( addr & 0xFF ) + //{ + //} + + debug_printf( "IN $%04X\n", addr ); + return 0; +} + +// Emulation + +blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int ) +{ + while ( time() < duration ) + { + blip_time_t end = min( duration, next_play ); + cpu::run( min( duration, next_play ) ); + if ( r.pc == idle_addr ) + set_time( end ); + + if ( time() >= next_play ) + { + next_play += play_period; + if ( r.pc == idle_addr ) + { + if ( !gain_updated ) + { + gain_updated = true; + if ( scc_accessed ) + update_gain(); + } + + ram [--r.sp] = idle_addr >> 8; + ram [--r.sp] = idle_addr & 0xFF; + r.pc = get_le16( header_.play_addr ); + GME_FRAME_HOOK( this ); + } + } + } + + duration = time(); + next_play -= duration; + check( next_play >= 0 ); + adjust_time( -duration ); + ay.end_frame( duration ); + scc.end_frame( duration ); + if ( sn ) + sn->end_frame( duration ); + + return 0; +} diff --git a/thirdparty/game-music-emu/gme/Kss_Emu.h b/thirdparty/game-music-emu/gme/Kss_Emu.h new file mode 100644 index 000000000..467b28abd --- /dev/null +++ b/thirdparty/game-music-emu/gme/Kss_Emu.h @@ -0,0 +1,95 @@ +// MSX computer KSS music file emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef KSS_EMU_H +#define KSS_EMU_H + +#include "Classic_Emu.h" +#include "Kss_Scc_Apu.h" +#include "Kss_Cpu.h" +#include "Sms_Apu.h" +#include "Ay_Apu.h" + +class Kss_Emu : private Kss_Cpu, public Classic_Emu { + typedef Kss_Cpu cpu; +public: + // KSS file header + enum { header_size = 0x10 }; + struct header_t + { + byte tag [4]; + byte load_addr [2]; + byte load_size [2]; + byte init_addr [2]; + byte play_addr [2]; + byte first_bank; + byte bank_mode; + byte extra_header; + byte device_flags; + }; + + enum { ext_header_size = 0x10 }; + struct ext_header_t + { + byte data_size [4]; + byte unused [4]; + byte first_track [2]; + byte last_tack [2]; + byte psg_vol; + byte scc_vol; + byte msx_music_vol; + byte msx_audio_vol; + }; + + struct composite_header_t : header_t, ext_header_t { }; + + // Header for currently loaded file + composite_header_t const& header() const { return header_; } + + static gme_type_t static_type() { return gme_kss_type; } +public: + Kss_Emu(); + ~Kss_Emu(); +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_( Data_Reader& ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); + void unload(); +private: + Rom_Data rom; + composite_header_t header_; + + bool scc_accessed; + bool gain_updated; + void update_gain(); + + unsigned scc_enabled; // 0 or 0xC000 + int bank_count; + void set_bank( int logical, int physical ); + blargg_long bank_size() const { return (16 * 1024L) >> (header_.bank_mode >> 7 & 1); } + + blip_time_t play_period; + blip_time_t next_play; + int ay_latch; + + friend void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data ); + friend int kss_cpu_in( class Kss_Cpu*, cpu_time_t, unsigned addr ); + void cpu_write( unsigned addr, int data ); + friend void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data ); + + // large items + enum { mem_size = 0x10000 }; + byte ram [mem_size + cpu_padding]; + + Ay_Apu ay; + Scc_Apu scc; + Sms_Apu* sn; + byte unmapped_read [0x100]; + byte unmapped_write [page_size]; +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Kss_Scc_Apu.cpp b/thirdparty/game-music-emu/gme/Kss_Scc_Apu.cpp new file mode 100644 index 000000000..bb84b3250 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Kss_Scc_Apu.cpp @@ -0,0 +1,97 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Kss_Scc_Apu.h" + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Tones above this frequency are treated as disabled tone at half volume. +// Power of two is more efficient (avoids division). +unsigned const inaudible_freq = 16384; + +int const wave_size = 0x20; + +void Scc_Apu::run_until( blip_time_t end_time ) +{ + for ( int index = 0; index < osc_count; index++ ) + { + osc_t& osc = oscs [index]; + + Blip_Buffer* const output = osc.output; + if ( !output ) + continue; + output->set_modified(); + + blip_time_t period = (regs [0x80 + index * 2 + 1] & 0x0F) * 0x100 + + regs [0x80 + index * 2] + 1; + int volume = 0; + if ( regs [0x8F] & (1 << index) ) + { + blip_time_t inaudible_period = (blargg_ulong) (output->clock_rate() + + inaudible_freq * 32) / (inaudible_freq * 16); + if ( period > inaudible_period ) + volume = (regs [0x8A + index] & 0x0F) * (amp_range / 256 / 15); + } + + int8_t const* wave = (int8_t*) regs + index * wave_size; + if ( index == osc_count - 1 ) + wave -= wave_size; // last two oscs share wave + { + int amp = wave [osc.phase] * volume; + int delta = amp - osc.last_amp; + if ( delta ) + { + osc.last_amp = amp; + synth.offset( last_time, delta, output ); + } + } + + blip_time_t time = last_time + osc.delay; + if ( time < end_time ) + { + if ( !volume ) + { + // maintain phase + blargg_long count = (end_time - time + period - 1) / period; + osc.phase = (osc.phase + count) & (wave_size - 1); + time += count * period; + } + else + { + + int phase = osc.phase; + int last_wave = wave [phase]; + phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop + + do + { + int amp = wave [phase]; + phase = (phase + 1) & (wave_size - 1); + int delta = amp - last_wave; + if ( delta ) + { + last_wave = amp; + synth.offset( time, delta * volume, output ); + } + time += period; + } + while ( time < end_time ); + + osc.phase = phase = (phase - 1) & (wave_size - 1); // undo pre-advance + osc.last_amp = wave [phase] * volume; + } + } + osc.delay = time - end_time; + } + last_time = end_time; +} diff --git a/thirdparty/game-music-emu/gme/Kss_Scc_Apu.h b/thirdparty/game-music-emu/gme/Kss_Scc_Apu.h new file mode 100644 index 000000000..eda5747fe --- /dev/null +++ b/thirdparty/game-music-emu/gme/Kss_Scc_Apu.h @@ -0,0 +1,106 @@ +// Konami SCC sound chip emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef KSS_SCC_APU_H +#define KSS_SCC_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" +#include + +class Scc_Apu { +public: + // Set buffer to generate all sound into, or disable sound if NULL + void output( Blip_Buffer* ); + + // Reset sound chip + void reset(); + + // Write to register at specified time + enum { reg_count = 0x90 }; + void write( blip_time_t time, int reg, int data ); + + // Run sound to specified time, end current time frame, then start a new + // time frame at time 0. Time frames have no effect on emulation and each + // can be whatever length is convenient. + void end_frame( blip_time_t length ); + +// Additional features + + // Set sound output of specific oscillator to buffer, where index is + // 0 to 4. If buffer is NULL, the specified oscillator is muted. + enum { osc_count = 5 }; + void osc_output( int index, Blip_Buffer* ); + + // Set overall volume (default is 1.0) + void volume( double ); + + // Set treble equalization (see documentation) + void treble_eq( blip_eq_t const& ); + +public: + Scc_Apu(); +private: + enum { amp_range = 0x8000 }; + struct osc_t + { + int delay; + int phase; + int last_amp; + Blip_Buffer* output; + }; + osc_t oscs [osc_count]; + blip_time_t last_time; + unsigned char regs [reg_count]; + Blip_Synth synth; + + void run_until( blip_time_t ); +}; + +inline void Scc_Apu::volume( double v ) { synth.volume( 0.43 / osc_count / amp_range * v ); } + +inline void Scc_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + +inline void Scc_Apu::osc_output( int index, Blip_Buffer* b ) +{ + assert( (unsigned) index < osc_count ); + oscs [index].output = b; +} + +inline void Scc_Apu::write( blip_time_t time, int addr, int data ) +{ + assert( (unsigned) addr < reg_count ); + run_until( time ); + regs [addr] = data; +} + +inline void Scc_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + last_time -= end_time; + assert( last_time >= 0 ); +} + +inline void Scc_Apu::output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; i++ ) + oscs [i].output = buf; +} + +inline Scc_Apu::Scc_Apu() +{ + output( 0 ); +} + +inline void Scc_Apu::reset() +{ + last_time = 0; + + for ( int i = 0; i < osc_count; i++ ) + memset( &oscs [i], 0, offsetof (osc_t,output) ); + + memset( regs, 0, sizeof regs ); +} + +#endif diff --git a/thirdparty/game-music-emu/gme/M3u_Playlist.cpp b/thirdparty/game-music-emu/gme/M3u_Playlist.cpp new file mode 100644 index 000000000..e751d4cc8 --- /dev/null +++ b/thirdparty/game-music-emu/gme/M3u_Playlist.cpp @@ -0,0 +1,426 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "M3u_Playlist.h" +#include "Music_Emu.h" + +#include + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// gme functions defined here to avoid linking in m3u code unless it's used + +blargg_err_t Gme_File::load_m3u_( blargg_err_t err ) +{ + require( raw_track_count_ ); // file must be loaded first + + if ( !err ) + { + if ( playlist.size() ) + track_count_ = playlist.size(); + + int line = playlist.first_error(); + if ( line ) + { + // avoid using bloated printf() + char* out = &playlist_warning [sizeof playlist_warning]; + *--out = 0; + do { + *--out = line % 10 + '0'; + } while ( (line /= 10) > 0 ); + + static const char str [] = "Problem in m3u at line "; + out -= sizeof str - 1; + memcpy( out, str, sizeof str - 1 ); + set_warning( out ); + } + } + return err; +} + +blargg_err_t Gme_File::load_m3u( const char* path ) { return load_m3u_( playlist.load( path ) ); } + +blargg_err_t Gme_File::load_m3u( Data_Reader& in ) { return load_m3u_( playlist.load( in ) ); } + +BLARGG_EXPORT gme_err_t gme_load_m3u( Music_Emu* me, const char* path ) { return me->load_m3u( path ); } + +BLARGG_EXPORT gme_err_t gme_load_m3u_data( Music_Emu* me, const void* data, long size ) +{ + Mem_File_Reader in( data, size ); + return me->load_m3u( in ); +} + + + +static char* skip_white( char* in ) +{ + while ( *in == ' ' ) + in++; + return in; +} + +inline unsigned from_dec( unsigned n ) { return n - '0'; } + +static char* parse_filename( char* in, M3u_Playlist::entry_t& entry ) +{ + entry.file = in; + entry.type = ""; + char* out = in; + while ( 1 ) + { + int c = *in; + if ( !c ) break; + in++; + + if ( c == ',' ) // commas in filename + { + char* p = skip_white( in ); + if ( *p == '$' || from_dec( *p ) <= 9 ) + { + in = p; + break; + } + } + + if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix + { + entry.type = ++in; + while ( (c = *in) != 0 && c != ',' ) + in++; + if ( c == ',' ) + { + *in++ = 0; // terminate type + in = skip_white( in ); + } + break; + } + + if ( c == '\\' ) // \ prefix for special characters + { + c = *in; + if ( !c ) break; + in++; + } + *out++ = (char) c; + } + *out = 0; // terminate string + return in; +} + +static char* next_field( char* in, int* result ) +{ + while ( 1 ) + { + in = skip_white( in ); + + if ( !*in ) + break; + + if ( *in == ',' ) + { + in++; + break; + } + + *result = 1; + in++; + } + return skip_white( in ); +} + +static char* parse_int_( char* in, int* out ) +{ + int n = 0; + while ( 1 ) + { + unsigned d = from_dec( *in ); + if ( d > 9 ) + break; + in++; + n = n * 10 + d; + *out = n; + } + return in; +} + +static char* parse_int( char* in, int* out, int* result ) +{ + return next_field( parse_int_( in, out ), result ); +} + +// Returns 16 or greater if not hex +inline int from_hex_char( int h ) +{ + h -= 0x30; + if ( (unsigned) h > 9 ) + h = ((h - 0x11) & 0xDF) + 10; + return h; +} + +static char* parse_track( char* in, M3u_Playlist::entry_t& entry, int* result ) +{ + if ( *in == '$' ) + { + in++; + int n = 0; + while ( 1 ) + { + int h = from_hex_char( *in ); + if ( h > 15 ) + break; + in++; + n = n * 16 + h; + entry.track = n; + } + } + else + { + in = parse_int_( in, &entry.track ); + if ( entry.track >= 0 ) + entry.decimal_track = 1; + } + return next_field( in, result ); +} + +static char* parse_time_( char* in, int* out ) +{ + *out = -1; + int n = -1; + in = parse_int_( in, &n ); + if ( n >= 0 ) + { + *out = n; + if ( *in == ':' ) + { + n = -1; + in = parse_int_( in + 1, &n ); + if ( n >= 0 ) + *out = *out * 60 + n; + } + } + return in; +} + +static char* parse_time( char* in, int* out, int* result ) +{ + return next_field( parse_time_( in, out ), result ); +} + +static char* parse_name( char* in ) +{ + char* out = in; + while ( 1 ) + { + int c = *in; + if ( !c ) break; + in++; + + if ( c == ',' ) // commas in string + { + char* p = skip_white( in ); + if ( *p == ',' || *p == '-' || from_dec( *p ) <= 9 ) + { + in = p; + break; + } + } + + if ( c == '\\' ) // \ prefix for special characters + { + c = *in; + if ( !c ) break; + in++; + } + *out++ = (char) c; + } + *out = 0; // terminate string + return in; +} + +static int parse_line( char* in, M3u_Playlist::entry_t& entry ) +{ + int result = 0; + + // file + entry.file = in; + entry.type = ""; + in = parse_filename( in, entry ); + + // track + entry.track = -1; + entry.decimal_track = 0; + in = parse_track( in, entry, &result ); + + // name + entry.name = in; + in = parse_name( in ); + + // time + entry.length = -1; + in = parse_time( in, &entry.length, &result ); + + // loop + entry.intro = -1; + entry.loop = -1; + if ( *in == '-' ) + { + entry.loop = entry.length; + in++; + } + else + { + in = parse_time_( in, &entry.loop ); + if ( entry.loop >= 0 ) + { + entry.intro = 0; + if ( *in == '-' ) // trailing '-' means that intro length was specified + { + in++; + entry.intro = entry.loop; + entry.loop = entry.length - entry.intro; + } + } + } + in = next_field( in, &result ); + + // fade + entry.fade = -1; + in = parse_time( in, &entry.fade, &result ); + + // repeat + entry.repeat = -1; + in = parse_int( in, &entry.repeat, &result ); + + return result; +} + +static void parse_comment( char* in, M3u_Playlist::info_t& info, bool first ) +{ + in = skip_white( in + 1 ); + const char* field = in; + while ( *in && *in != ':' ) + in++; + + if ( *in == ':' ) + { + const char* text = skip_white( in + 1 ); + if ( *text ) + { + *in = 0; + if ( !strcmp( "Composer", field ) ) info.composer = text; + else if ( !strcmp( "Engineer", field ) ) info.engineer = text; + else if ( !strcmp( "Ripping" , field ) ) info.ripping = text; + else if ( !strcmp( "Tagging" , field ) ) info.tagging = text; + else + text = 0; + if ( text ) + return; + *in = ':'; + } + } + + if ( first ) + info.title = field; +} + +blargg_err_t M3u_Playlist::parse_() +{ + info_.title = ""; + info_.composer = ""; + info_.engineer = ""; + info_.ripping = ""; + info_.tagging = ""; + + int const CR = 13; + int const LF = 10; + + data.end() [-1] = LF; // terminate input + + first_error_ = 0; + bool first_comment = true; + int line = 0; + int count = 0; + char* in = data.begin(); + while ( in < data.end() ) + { + // find end of line and terminate it + line++; + char* begin = in; + while ( *in != CR && *in != LF ) + { + if ( !*in ) + return "Not an m3u playlist"; + in++; + } + if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line + *in++ = 0; + *in++ = 0; + + // parse line + if ( *begin == '#' ) + { + parse_comment( begin, info_, first_comment ); + first_comment = false; + } + else if ( *begin ) + { + if ( (int) entries.size() <= count ) + RETURN_ERR( entries.resize( count * 2 + 64 ) ); + + if ( !parse_line( begin, entries [count] ) ) + count++; + else if ( !first_error_ ) + first_error_ = line; + first_comment = false; + } + } + if ( count <= 0 ) + return "Not an m3u playlist"; + + if ( !(info_.composer [0] | info_.engineer [0] | info_.ripping [0] | info_.tagging [0]) ) + info_.title = ""; + + return entries.resize( count ); +} + +blargg_err_t M3u_Playlist::parse() +{ + blargg_err_t err = parse_(); + if ( err ) + { + entries.clear(); + data.clear(); + } + return err; +} + +blargg_err_t M3u_Playlist::load( Data_Reader& in ) +{ + RETURN_ERR( data.resize( in.remain() + 1 ) ); + RETURN_ERR( in.read( data.begin(), data.size() - 1 ) ); + return parse(); +} + +blargg_err_t M3u_Playlist::load( const char* path ) +{ + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + return load( in ); +} + +blargg_err_t M3u_Playlist::load( void const* in, long size ) +{ + RETURN_ERR( data.resize( size + 1 ) ); + memcpy( data.begin(), in, size ); + return parse(); +} diff --git a/thirdparty/game-music-emu/gme/M3u_Playlist.h b/thirdparty/game-music-emu/gme/M3u_Playlist.h new file mode 100644 index 000000000..6757b7cfb --- /dev/null +++ b/thirdparty/game-music-emu/gme/M3u_Playlist.h @@ -0,0 +1,67 @@ +// M3U playlist file parser, with support for subtrack information + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef M3U_PLAYLIST_H +#define M3U_PLAYLIST_H + +#include "blargg_common.h" +#include "Data_Reader.h" + +class M3u_Playlist { +public: + // Load playlist data + blargg_err_t load( const char* path ); + blargg_err_t load( Data_Reader& in ); + blargg_err_t load( void const* data, long size ); + + // Line number of first parse error, 0 if no error. Any lines with parse + // errors are ignored. + int first_error() const { return first_error_; } + + struct info_t + { + const char* title; + const char* composer; + const char* engineer; + const char* ripping; + const char* tagging; + }; + info_t const& info() const { return info_; } + + struct entry_t + { + const char* file; // filename without stupid ::TYPE suffix + const char* type; // if filename has ::TYPE suffix, this will be "TYPE". "" if none. + const char* name; + bool decimal_track; // true if track was specified in hex + // integers are -1 if not present + int track; // 1-based + int length; // seconds + int intro; + int loop; + int fade; + int repeat; // count + }; + entry_t const& operator [] ( int i ) const { return entries [i]; } + int size() const { return entries.size(); } + + void clear(); + +private: + blargg_vector entries; + blargg_vector data; + int first_error_; + info_t info_; + + blargg_err_t parse(); + blargg_err_t parse_(); +}; + +inline void M3u_Playlist::clear() +{ + first_error_ = 0; + entries.clear(); + data.clear(); +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Multi_Buffer.cpp b/thirdparty/game-music-emu/gme/Multi_Buffer.cpp new file mode 100644 index 000000000..5f000ceeb --- /dev/null +++ b/thirdparty/game-music-emu/gme/Multi_Buffer.cpp @@ -0,0 +1,232 @@ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ + +#include "Multi_Buffer.h" + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) +{ + length_ = 0; + sample_rate_ = 0; + channels_changed_count_ = 1; +} + +blargg_err_t Multi_Buffer::set_channel_count( int ) { return 0; } + +// Silent_Buffer + +Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse +{ + // TODO: better to use empty Blip_Buffer so caller never has to check for NULL? + chan.left = 0; + chan.center = 0; + chan.right = 0; +} + +// Mono_Buffer + +Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) +{ + chan.center = &buf; + chan.left = &buf; + chan.right = &buf; +} + +Mono_Buffer::~Mono_Buffer() { } + +blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) +{ + RETURN_ERR( buf.set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); +} + +// Stereo_Buffer + +Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) +{ + chan.center = &bufs [0]; + chan.left = &bufs [1]; + chan.right = &bufs [2]; +} + +Stereo_Buffer::~Stereo_Buffer() { } + +blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec ) +{ + for ( int i = 0; i < buf_count; i++ ) + RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); +} + +void Stereo_Buffer::clock_rate( long rate ) +{ + for ( int i = 0; i < buf_count; i++ ) + bufs [i].clock_rate( rate ); +} + +void Stereo_Buffer::bass_freq( int bass ) +{ + for ( int i = 0; i < buf_count; i++ ) + bufs [i].bass_freq( bass ); +} + +void Stereo_Buffer::clear() +{ + stereo_added = 0; + was_stereo = false; + for ( int i = 0; i < buf_count; i++ ) + bufs [i].clear(); +} + +void Stereo_Buffer::end_frame( blip_time_t clock_count ) +{ + stereo_added = 0; + for ( int i = 0; i < buf_count; i++ ) + { + stereo_added |= bufs [i].clear_modified() << i; + bufs [i].end_frame( clock_count ); + } +} + +long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) +{ + require( !(count & 1) ); // count must be even + count = (unsigned) count / 2; + + long avail = bufs [0].samples_avail(); + if ( count > avail ) + count = avail; + if ( count ) + { + int bufs_used = stereo_added | was_stereo; + //debug_printf( "%X\n", bufs_used ); + if ( bufs_used <= 1 ) + { + mix_mono( out, count ); + bufs [0].remove_samples( count ); + bufs [1].remove_silence( count ); + bufs [2].remove_silence( count ); + } + else if ( bufs_used & 1 ) + { + mix_stereo( out, count ); + bufs [0].remove_samples( count ); + bufs [1].remove_samples( count ); + bufs [2].remove_samples( count ); + } + else + { + mix_stereo_no_center( out, count ); + bufs [0].remove_silence( count ); + bufs [1].remove_samples( count ); + bufs [2].remove_samples( count ); + } + + // to do: this might miss opportunities for optimization + if ( !bufs [0].samples_avail() ) + { + was_stereo = stereo_added; + stereo_added = 0; + } + } + + return count * 2; +} + +void Stereo_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [1] ); + BLIP_READER_BEGIN( left, bufs [1] ); + BLIP_READER_BEGIN( right, bufs [2] ); + BLIP_READER_BEGIN( center, bufs [0] ); + + for ( ; count; --count ) + { + int c = BLIP_READER_READ( center ); + blargg_long l = c + BLIP_READER_READ( left ); + blargg_long r = c + BLIP_READER_READ( right ); + if ( (int16_t) l != l ) + l = 0x7FFF - (l >> 24); + + BLIP_READER_NEXT( center, bass ); + if ( (int16_t) r != r ) + r = 0x7FFF - (r >> 24); + + BLIP_READER_NEXT( left, bass ); + BLIP_READER_NEXT( right, bass ); + + out [0] = l; + out [1] = r; + out += 2; + } + + BLIP_READER_END( center, bufs [0] ); + BLIP_READER_END( right, bufs [2] ); + BLIP_READER_END( left, bufs [1] ); +} + +void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [1] ); + BLIP_READER_BEGIN( left, bufs [1] ); + BLIP_READER_BEGIN( right, bufs [2] ); + + for ( ; count; --count ) + { + blargg_long l = BLIP_READER_READ( left ); + if ( (int16_t) l != l ) + l = 0x7FFF - (l >> 24); + + blargg_long r = BLIP_READER_READ( right ); + if ( (int16_t) r != r ) + r = 0x7FFF - (r >> 24); + + BLIP_READER_NEXT( left, bass ); + BLIP_READER_NEXT( right, bass ); + + out [0] = l; + out [1] = r; + out += 2; + } + + BLIP_READER_END( right, bufs [2] ); + BLIP_READER_END( left, bufs [1] ); +} + +void Stereo_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [0] ); + BLIP_READER_BEGIN( center, bufs [0] ); + + for ( ; count; --count ) + { + blargg_long s = BLIP_READER_READ( center ); + if ( (int16_t) s != s ) + s = 0x7FFF - (s >> 24); + + BLIP_READER_NEXT( center, bass ); + out [0] = s; + out [1] = s; + out += 2; + } + + BLIP_READER_END( center, bufs [0] ); +} diff --git a/thirdparty/game-music-emu/gme/Multi_Buffer.h b/thirdparty/game-music-emu/gme/Multi_Buffer.h new file mode 100644 index 000000000..82c8b3ab5 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Multi_Buffer.h @@ -0,0 +1,158 @@ +// Multi-channel sound buffer interface, and basic mono and stereo buffers + +// Blip_Buffer 0.4.1 +#ifndef MULTI_BUFFER_H +#define MULTI_BUFFER_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +// Interface to one or more Blip_Buffers mapped to one or more channels +// consisting of left, center, and right buffers. +class Multi_Buffer { +public: + Multi_Buffer( int samples_per_frame ); + virtual ~Multi_Buffer() { } + + // Set the number of channels available + virtual blargg_err_t set_channel_count( int ); + + // Get indexed channel, from 0 to channel count - 1 + struct channel_t { + Blip_Buffer* center; + Blip_Buffer* left; + Blip_Buffer* right; + }; + enum { type_index_mask = 0xFF }; + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + virtual channel_t channel( int index, int type ) = 0; + + // See Blip_Buffer.h + virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0; + virtual void clock_rate( long ) = 0; + virtual void bass_freq( int ) = 0; + virtual void clear() = 0; + long sample_rate() const; + + // Length of buffer, in milliseconds + int length() const; + + // See Blip_Buffer.h + virtual void end_frame( blip_time_t ) = 0; + + // Number of samples per output frame (1 = mono, 2 = stereo) + int samples_per_frame() const; + + // Count of changes to channel configuration. Incremented whenever + // a change is made to any of the Blip_Buffers for any channel. + unsigned channels_changed_count() { return channels_changed_count_; } + + // See Blip_Buffer.h + virtual long read_samples( blip_sample_t*, long ) = 0; + virtual long samples_avail() const = 0; + +public: + BLARGG_DISABLE_NOTHROW +protected: + void channels_changed() { channels_changed_count_++; } +private: + // noncopyable + Multi_Buffer( const Multi_Buffer& ); + Multi_Buffer& operator = ( const Multi_Buffer& ); + + unsigned channels_changed_count_; + long sample_rate_; + int length_; + int const samples_per_frame_; +}; + +// Uses a single buffer and outputs mono samples. +class Mono_Buffer : public Multi_Buffer { + Blip_Buffer buf; + channel_t chan; +public: + // Buffer used for all channels + Blip_Buffer* center() { return &buf; } + +public: + Mono_Buffer(); + ~Mono_Buffer(); + blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); + void clock_rate( long rate ) { buf.clock_rate( rate ); } + void bass_freq( int freq ) { buf.bass_freq( freq ); } + void clear() { buf.clear(); } + long samples_avail() const { return buf.samples_avail(); } + long read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t t ) { buf.end_frame( t ); } +}; + +// Uses three buffers (one for center) and outputs stereo sample pairs. +class Stereo_Buffer : public Multi_Buffer { +public: + + // Buffers used for all channels + Blip_Buffer* center() { return &bufs [0]; } + Blip_Buffer* left() { return &bufs [1]; } + Blip_Buffer* right() { return &bufs [2]; } + +public: + Stereo_Buffer(); + ~Stereo_Buffer(); + blargg_err_t set_sample_rate( long, int msec = blip_default_length ); + void clock_rate( long ); + void bass_freq( int ); + void clear(); + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t ); + + long samples_avail() const { return bufs [0].samples_avail() * 2; } + long read_samples( blip_sample_t*, long ); + +private: + enum { buf_count = 3 }; + Blip_Buffer bufs [buf_count]; + channel_t chan; + int stereo_added; + int was_stereo; + + void mix_stereo_no_center( blip_sample_t*, blargg_long ); + void mix_stereo( blip_sample_t*, blargg_long ); + void mix_mono( blip_sample_t*, blargg_long ); +}; + +// Silent_Buffer generates no samples, useful where no sound is wanted +class Silent_Buffer : public Multi_Buffer { + channel_t chan; +public: + Silent_Buffer(); + blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); + void clock_rate( long ) { } + void bass_freq( int ) { } + void clear() { } + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t ) { } + long samples_avail() const { return 0; } + long read_samples( blip_sample_t*, long ) { return 0; } +}; + + +inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) +{ + sample_rate_ = rate; + length_ = msec; + return 0; +} + +inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec ) +{ + return Multi_Buffer::set_sample_rate( rate, msec ); +} + +inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } + +inline long Multi_Buffer::sample_rate() const { return sample_rate_; } + +inline int Multi_Buffer::length() const { return length_; } + +#endif diff --git a/thirdparty/game-music-emu/gme/Music_Emu.cpp b/thirdparty/game-music-emu/gme/Music_Emu.cpp new file mode 100644 index 000000000..e60e7ca5d --- /dev/null +++ b/thirdparty/game-music-emu/gme/Music_Emu.cpp @@ -0,0 +1,451 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Music_Emu.h" + +#include "Multi_Buffer.h" +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const silence_max = 6; // seconds +int const silence_threshold = 0x10; +long const fade_block_size = 512; +int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) + +Music_Emu::equalizer_t const Music_Emu::tv_eq = + Music_Emu::make_equalizer( -8.0, 180 ); + +void Music_Emu::clear_track_vars() +{ + current_track_ = -1; + out_time = 0; + emu_time = 0; + emu_track_ended_ = true; + track_ended_ = true; + fade_start = INT_MAX / 2 + 1; + fade_step = 1; + silence_time = 0; + silence_count = 0; + buf_remain = 0; + warning(); // clear warning +} + +void Music_Emu::unload() +{ + voice_count_ = 0; + clear_track_vars(); + Gme_File::unload(); +} + +Music_Emu::Music_Emu() +{ + effects_buffer = 0; + multi_channel_ = false; + sample_rate_ = 0; + mute_mask_ = 0; + tempo_ = 1.0; + gain_ = 1.0; + + // defaults + max_initial_silence = 2; + silence_lookahead = 3; + ignore_silence_ = false; + equalizer_.treble = -1.0; + equalizer_.bass = 60; + + emu_autoload_playback_limit_ = true; + + static const char* const names [] = { + "Voice 1", "Voice 2", "Voice 3", "Voice 4", + "Voice 5", "Voice 6", "Voice 7", "Voice 8" + }; + set_voice_names( names ); + Music_Emu::unload(); // non-virtual +} + +Music_Emu::~Music_Emu() { delete effects_buffer; } + +blargg_err_t Music_Emu::set_sample_rate( long rate ) +{ + require( !sample_rate() ); // sample rate can't be changed once set + RETURN_ERR( set_sample_rate_( rate ) ); + RETURN_ERR( buf.resize( buf_size ) ); + sample_rate_ = rate; + return 0; +} + +void Music_Emu::pre_load() +{ + require( sample_rate() ); // set_sample_rate() must be called before loading a file + Gme_File::pre_load(); +} + +void Music_Emu::set_equalizer( equalizer_t const& eq ) +{ + equalizer_ = eq; + set_equalizer_( eq ); +} + +bool Music_Emu::multi_channel() const +{ + return this->multi_channel_; +} + +blargg_err_t Music_Emu::set_multi_channel( bool ) +{ + // by default not supported, derived may override this + return "unsupported for this emulator type"; +} + +blargg_err_t Music_Emu::set_multi_channel_( bool isEnabled ) +{ + // multi channel support must be set at the very beginning + require( !sample_rate() ); + multi_channel_ = isEnabled; + return 0; +} + +void Music_Emu::mute_voice( int index, bool mute ) +{ + require( (unsigned) index < (unsigned) voice_count() ); + int bit = 1 << index; + int mask = mute_mask_ | bit; + if ( !mute ) + mask ^= bit; + mute_voices( mask ); +} + +void Music_Emu::mute_voices( int mask ) +{ + require( sample_rate() ); // sample rate must be set first + mute_mask_ = mask; + mute_voices_( mask ); +} + +void Music_Emu::set_tempo( double t ) +{ + require( sample_rate() ); // sample rate must be set first + double const min = 0.02; + double const max = 4.00; + if ( t < min ) t = min; + if ( t > max ) t = max; + tempo_ = t; + set_tempo_( t ); +} + +void Music_Emu::post_load_() +{ + set_tempo( tempo_ ); + remute_voices(); +} + +blargg_err_t Music_Emu::start_track( int track ) +{ + clear_track_vars(); + + int remapped = track; + RETURN_ERR( remap_track_( &remapped ) ); + current_track_ = track; + RETURN_ERR( start_track_( remapped ) ); + + emu_track_ended_ = false; + track_ended_ = false; + + if ( !ignore_silence_ ) + { + // play until non-silence or end of track + for ( long end = max_initial_silence * out_channels() * sample_rate(); emu_time < end; ) + { + fill_buf(); + if ( buf_remain | (int) emu_track_ended_ ) + break; + } + + emu_time = buf_remain; + out_time = 0; + silence_time = 0; + silence_count = 0; + } + return track_ended() ? warning() : 0; +} + +void Music_Emu::end_track_if_error( blargg_err_t err ) +{ + if ( err ) + { + emu_track_ended_ = true; + set_warning( err ); + } +} + +bool Music_Emu::autoload_playback_limit() const +{ + return emu_autoload_playback_limit_; +} + +void Music_Emu::set_autoload_playback_limit( bool do_autoload_limit ) +{ + emu_autoload_playback_limit_ = do_autoload_limit; +} + +// Tell/Seek + +blargg_long Music_Emu::msec_to_samples( blargg_long msec ) const +{ + blargg_long sec = msec / 1000; + msec -= sec * 1000; + return (sec * sample_rate() + msec * sample_rate() / 1000) * out_channels(); +} + +long Music_Emu::tell_samples() const +{ + return out_time; +} + +long Music_Emu::tell() const +{ + blargg_long rate = sample_rate() * out_channels(); + blargg_long sec = out_time / rate; + return sec * 1000 + (out_time - sec * rate) * 1000 / rate; +} + +blargg_err_t Music_Emu::seek_samples( long time ) +{ + if ( time < out_time ) + RETURN_ERR( start_track( current_track_ ) ); + return skip( time - out_time ); +} + +blargg_err_t Music_Emu::seek( long msec ) +{ + return seek_samples( msec_to_samples( msec ) ); +} + +blargg_err_t Music_Emu::skip( long count ) +{ + require( current_track() >= 0 ); // start_track() must have been called already + out_time += count; + + // remove from silence and buf first + { + long n = min( count, silence_count ); + silence_count -= n; + count -= n; + + n = min( count, buf_remain ); + buf_remain -= n; + count -= n; + } + + if ( count && !emu_track_ended_ ) + { + emu_time += count; + end_track_if_error( skip_( count ) ); + } + + if ( !(silence_count | buf_remain) ) // caught up to emulator, so update track ended + track_ended_ |= emu_track_ended_; + + return 0; +} + +blargg_err_t Music_Emu::skip_( long count ) +{ + // for long skip, mute sound + const long threshold = 30000; + if ( count > threshold ) + { + int saved_mute = mute_mask_; + mute_voices( ~0 ); + + while ( count > threshold / 2 && !emu_track_ended_ ) + { + RETURN_ERR( play_( buf_size, buf.begin() ) ); + count -= buf_size; + } + + mute_voices( saved_mute ); + } + + while ( count && !emu_track_ended_ ) + { + long n = buf_size; + if ( n > count ) + n = count; + count -= n; + RETURN_ERR( play_( n, buf.begin() ) ); + } + return 0; +} + +// Fading + +void Music_Emu::set_fade( long start_msec, long length_msec ) +{ + fade_step = sample_rate() * length_msec / (fade_block_size * fade_shift * 1000 / out_channels()); + fade_start = msec_to_samples( start_msec ); +} + +// unit / pow( 2.0, (double) x / step ) +static int int_log( blargg_long x, int step, int unit ) +{ + int shift = x / step; + int fraction = (x - shift * step) * unit / step; + return ((unit - fraction) + (fraction >> 1)) >> shift; +} + +void Music_Emu::handle_fade( long out_count, sample_t* out ) +{ + for ( int i = 0; i < out_count; i += fade_block_size ) + { + int const shift = 14; + int const unit = 1 << shift; + int gain = int_log( (out_time + i - fade_start) / fade_block_size, + fade_step, unit ); + if ( gain < (unit >> fade_shift) ) + track_ended_ = emu_track_ended_ = true; + + sample_t* io = &out [i]; + for ( int count = min( fade_block_size, out_count - i ); count; --count ) + { + *io = sample_t ((*io * gain) >> shift); + ++io; + } + } +} + +// Silence detection + +void Music_Emu::emu_play( long count, sample_t* out ) +{ + check( current_track_ >= 0 ); + emu_time += count; + if ( current_track_ >= 0 && !emu_track_ended_ ) + end_track_if_error( play_( count, out ) ); + else + memset( out, 0, count * sizeof *out ); +} + +// number of consecutive silent samples at end +static long count_silence( Music_Emu::sample_t* begin, long size ) +{ + Music_Emu::sample_t first = *begin; + *begin = silence_threshold; // sentinel + Music_Emu::sample_t* p = begin + size; + while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { } + *begin = first; + return size - (p - begin); +} + +// fill internal buffer and check it for silence +void Music_Emu::fill_buf() +{ + assert( !buf_remain ); + if ( !emu_track_ended_ ) + { + emu_play( buf_size, buf.begin() ); + long silence = count_silence( buf.begin(), buf_size ); + if ( silence < buf_size ) + { + silence_time = emu_time - silence; + buf_remain = buf_size; + return; + } + } + silence_count += buf_size; +} + +blargg_err_t Music_Emu::play( long out_count, sample_t* out ) +{ + if ( track_ended_ ) + { + memset( out, 0, out_count * sizeof *out ); + } + else + { + require( current_track() >= 0 ); + require( out_count % out_channels() == 0 ); + + assert( emu_time >= out_time ); + + // prints nifty graph of how far ahead we are when searching for silence + //debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" ); + + long pos = 0; + if ( silence_count ) + { + // during a run of silence, run emulator at >=2x speed so it gets ahead + long ahead_time = silence_lookahead * (out_time + out_count - silence_time) + silence_time; + while ( emu_time < ahead_time && !(buf_remain | emu_track_ended_) ) + fill_buf(); + + // fill with silence + pos = min( silence_count, out_count ); + memset( out, 0, pos * sizeof *out ); + silence_count -= pos; + + if ( emu_time - silence_time > silence_max * out_channels() * sample_rate() ) + { + track_ended_ = emu_track_ended_ = true; + silence_count = 0; + buf_remain = 0; + } + } + + if ( buf_remain ) + { + // empty silence buf + long n = min( buf_remain, out_count - pos ); + memcpy( &out [pos], buf.begin() + (buf_size - buf_remain), n * sizeof *out ); + buf_remain -= n; + pos += n; + } + + // generate remaining samples normally + long remain = out_count - pos; + if ( remain ) + { + emu_play( remain, out + pos ); + track_ended_ |= emu_track_ended_; + + if ( !ignore_silence_ || out_time > fade_start ) + { + // check end for a new run of silence + long silence = count_silence( out + pos, remain ); + if ( silence < remain ) + silence_time = emu_time - silence; + + if ( emu_time - silence_time >= buf_size ) + fill_buf(); // cause silence detection on next play() + } + } + + if ( fade_start >= 0 && out_time > fade_start ) + handle_fade( out_count, out ); + } + out_time += out_count; + return 0; +} + +// Gme_Info_ + +blargg_err_t Gme_Info_::set_sample_rate_( long ) { return 0; } +void Gme_Info_::pre_load() { Gme_File::pre_load(); } // skip Music_Emu +void Gme_Info_::post_load_() { Gme_File::post_load_(); } // skip Music_Emu +void Gme_Info_::set_equalizer_( equalizer_t const& ){ check( false ); } +void Gme_Info_::enable_accuracy_( bool ) { check( false ); } +void Gme_Info_::mute_voices_( int ) { check( false ); } +void Gme_Info_::set_tempo_( double ) { } +blargg_err_t Gme_Info_::start_track_( int ) { return "Use full emulator for playback"; } +blargg_err_t Gme_Info_::play_( long, sample_t* ) { return "Use full emulator for playback"; } diff --git a/thirdparty/game-music-emu/gme/Music_Emu.h b/thirdparty/game-music-emu/gme/Music_Emu.h new file mode 100644 index 000000000..3aafa5ec1 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Music_Emu.h @@ -0,0 +1,252 @@ +// Common interface to game music file emulators + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef MUSIC_EMU_H +#define MUSIC_EMU_H + +#include "Gme_File.h" +class Multi_Buffer; + +struct Music_Emu : public Gme_File { +public: +// Basic functionality (see Gme_File.h for file loading/track info functions) + + // Set output sample rate. Must be called only once before loading file. + blargg_err_t set_sample_rate( long sample_rate ); + + // specifies if all 8 voices get rendered to their own stereo channel + // default implementation of Music_Emu always returns not supported error (i.e. no multichannel support by default) + // derived emus must override this if they support multichannel rendering + virtual blargg_err_t set_multi_channel( bool is_enabled ); + + // Start a track, where 0 is the first track. Also clears warning string. + blargg_err_t start_track( int ); + + // Generate 'count' samples info 'buf'. Output is in stereo. Any emulation + // errors set warning string, and major errors also end track. + typedef short sample_t; + blargg_err_t play( long count, sample_t* buf ); + +// Informational + + // Sample rate sound is generated at + long sample_rate() const; + + // Index of current track or -1 if one hasn't been started + int current_track() const; + + // Number of voices used by currently loaded file + int voice_count() const; + + // Names of voices + const char** voice_names() const; + + bool multi_channel() const; + +// Track status/control + + // Number of milliseconds (1000 msec = 1 second) played since beginning of track + long tell() const; + + // Number of samples generated since beginning of track + long tell_samples() const; + + // Seek to new time in track. Seeking backwards or far forward can take a while. + blargg_err_t seek( long msec ); + + // Equivalent to restarting track then skipping n samples + blargg_err_t seek_samples( long n ); + + // Skip n samples + blargg_err_t skip( long n ); + + // True if a track has reached its end + bool track_ended() const; + + // Set start time and length of track fade out. Once fade ends track_ended() returns + // true. Fade time can be changed while track is playing. + void set_fade( long start_msec, long length_msec = 8000 ); + + // Controls whether or not to automatically load and obey track length + // metadata for supported emulators. + // + // @since 0.6.2. + bool autoload_playback_limit() const; + void set_autoload_playback_limit( bool do_autoload_limit ); + + // Disable automatic end-of-track detection and skipping of silence at beginning + void ignore_silence( bool disable = true ); + + // Info for current track + using Gme_File::track_info; + blargg_err_t track_info( track_info_t* out ) const; + +// Sound customization + + // Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. + // Track length as returned by track_info() assumes a tempo of 1.0. + void set_tempo( double ); + + // Mute/unmute voice i, where voice 0 is first voice + void mute_voice( int index, bool mute = true ); + + // Set muting state of all voices at once using a bit mask, where -1 mutes them all, + // 0 unmutes them all, 0x01 mutes just the first voice, etc. + void mute_voices( int mask ); + + // Change overall output amplitude, where 1.0 results in minimal clamping. + // Must be called before set_sample_rate(). + void set_gain( double ); + + // Request use of custom multichannel buffer. Only supported by "classic" emulators; + // on others this has no effect. Should be called only once *before* set_sample_rate(). + virtual void set_buffer( Multi_Buffer* ) { } + + // Enables/disables accurate emulation options, if any are supported. Might change + // equalizer settings. + void enable_accuracy( bool enable = true ); + +// Sound equalization (treble/bass) + + // Frequency equalizer parameters (see gme.txt) + // See gme.h for definition of struct gme_equalizer_t. + typedef gme_equalizer_t equalizer_t; + + // Current frequency equalizater parameters + equalizer_t const& equalizer() const; + + // Set frequency equalizer parameters + void set_equalizer( equalizer_t const& ); + + // Construct equalizer of given treble/bass settings + static const equalizer_t make_equalizer( double treble, double bass ) + { + const Music_Emu::equalizer_t e = { treble, bass, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + return e; + } + + // Equalizer settings for TV speaker + static equalizer_t const tv_eq; + +public: + Music_Emu(); + ~Music_Emu(); +protected: + void set_max_initial_silence( int n ) { max_initial_silence = n; } + void set_silence_lookahead( int n ) { silence_lookahead = n; } + void set_voice_count( int n ) { voice_count_ = n; } + void set_voice_names( const char* const* names ); + void set_track_ended() { emu_track_ended_ = true; } + double gain() const { return gain_; } + double tempo() const { return tempo_; } + void remute_voices(); + blargg_err_t set_multi_channel_( bool is_enabled ); + + virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0; + virtual void set_equalizer_( equalizer_t const& ) { } + virtual void enable_accuracy_( bool /* enable */ ) { } + virtual void mute_voices_( int mask ) = 0; + virtual void set_tempo_( double ) = 0; + virtual blargg_err_t start_track_( int ) = 0; // tempo is set before this + virtual blargg_err_t play_( long count, sample_t* out ) = 0; + virtual blargg_err_t skip_( long count ); +protected: + virtual void unload(); + virtual void pre_load(); + virtual void post_load_(); +private: + // general + equalizer_t equalizer_; + int max_initial_silence; + const char** voice_names_; + int voice_count_; + int mute_mask_; + double tempo_; + double gain_; + bool multi_channel_; + + // returns the number of output channels, i.e. usually 2 for stereo, unlesss multi_channel_ == true + int out_channels() const { return this->multi_channel() ? 2*8 : 2; } + + long sample_rate_; + blargg_long msec_to_samples( blargg_long msec ) const; + + // track-specific + int current_track_; + blargg_long out_time; // number of samples played since start of track + blargg_long emu_time; // number of samples emulator has generated since start of track + bool emu_track_ended_; // emulator has reached end of track + bool emu_autoload_playback_limit_; // whether to load and obey track length by default + volatile bool track_ended_; + void clear_track_vars(); + void end_track_if_error( blargg_err_t ); + + // fading + blargg_long fade_start; + int fade_step; + void handle_fade( long count, sample_t* out ); + + // silence detection + int silence_lookahead; // speed to run emulator when looking ahead for silence + bool ignore_silence_; + long silence_time; // number of samples where most recent silence began + long silence_count; // number of samples of silence to play before using buf + long buf_remain; // number of samples left in silence buffer + enum { buf_size = 2048 }; + blargg_vector buf; + void fill_buf(); + void emu_play( long count, sample_t* out ); + + Multi_Buffer* effects_buffer; + friend Music_Emu* gme_internal_new_emu_( gme_type_t, int, bool ); + friend void gme_set_stereo_depth( Music_Emu*, double ); +}; + +// base class for info-only derivations +struct Gme_Info_ : Music_Emu +{ + virtual blargg_err_t set_sample_rate_( long sample_rate ); + virtual void set_equalizer_( equalizer_t const& ); + virtual void enable_accuracy_( bool ); + virtual void mute_voices_( int mask ); + virtual void set_tempo_( double ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t play_( long count, sample_t* out ); + virtual void pre_load(); + virtual void post_load_(); +}; + +inline blargg_err_t Music_Emu::track_info( track_info_t* out ) const +{ + return track_info( out, current_track_ ); +} + +inline long Music_Emu::sample_rate() const { return sample_rate_; } +inline const char** Music_Emu::voice_names() const { return voice_names_; } +inline int Music_Emu::voice_count() const { return voice_count_; } +inline int Music_Emu::current_track() const { return current_track_; } +inline bool Music_Emu::track_ended() const { return track_ended_; } +inline const Music_Emu::equalizer_t& Music_Emu::equalizer() const { return equalizer_; } + +inline void Music_Emu::enable_accuracy( bool b ) { enable_accuracy_( b ); } +inline void Music_Emu::set_tempo_( double t ) { tempo_ = t; } +inline void Music_Emu::remute_voices() { mute_voices( mute_mask_ ); } +inline void Music_Emu::ignore_silence( bool b ) { ignore_silence_ = b; } +inline blargg_err_t Music_Emu::start_track_( int ) { return 0; } + +inline void Music_Emu::set_voice_names( const char* const* names ) +{ + // Intentional removal of const, so users don't have to remember obscure const in middle + voice_names_ = const_cast (names); +} + +inline void Music_Emu::mute_voices_( int ) { } + +inline void Music_Emu::set_gain( double g ) +{ + assert( !sample_rate() ); // you must set gain before setting sample rate + gain_ = g; +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Nes_Apu.cpp b/thirdparty/game-music-emu/gme/Nes_Apu.cpp new file mode 100644 index 000000000..68edb446d --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Apu.cpp @@ -0,0 +1,391 @@ +// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ + +#include "Nes_Apu.h" + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const amp_range = 15; + +Nes_Apu::Nes_Apu() : + square1( &square_synth ), + square2( &square_synth ) +{ + tempo_ = 1.0; + dmc.apu = this; + dmc.prg_reader = NULL; + irq_notifier_ = NULL; + + oscs [0] = &square1; + oscs [1] = &square2; + oscs [2] = ▵ + oscs [3] = &noise; + oscs [4] = &dmc; + + output( NULL ); + volume( 1.0 ); + reset( false ); +} + +void Nes_Apu::treble_eq( const blip_eq_t& eq ) +{ + square_synth.treble_eq( eq ); + triangle.synth.treble_eq( eq ); + noise.synth.treble_eq( eq ); + dmc.synth.treble_eq( eq ); +} + +void Nes_Apu::enable_nonlinear( double v ) +{ + dmc.nonlinear = true; + square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v ); + + const double tnd = 0.48 / 202 * nonlinear_tnd_gain(); + triangle.synth.volume( 3.0 * tnd ); + noise.synth.volume( 2.0 * tnd ); + dmc.synth.volume( tnd ); + + square1 .last_amp = 0; + square2 .last_amp = 0; + triangle.last_amp = 0; + noise .last_amp = 0; + dmc .last_amp = 0; +} + +void Nes_Apu::volume( double v ) +{ + dmc.nonlinear = false; + square_synth.volume( 0.1128 / amp_range * v ); + triangle.synth.volume( 0.12765 / amp_range * v ); + noise.synth.volume( 0.0741 / amp_range * v ); + dmc.synth.volume( 0.42545 / 127 * v ); +} + +void Nes_Apu::output( Blip_Buffer* buffer ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, buffer ); +} + +void Nes_Apu::set_tempo( double t ) +{ + tempo_ = t; + frame_period = (dmc.pal_mode ? 8314 : 7458); + if ( t != 1.0 ) + frame_period = (int) (frame_period / t) & ~1; // must be even +} + +void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) +{ + dmc.pal_mode = pal_mode; + set_tempo( tempo_ ); + + square1.reset(); + square2.reset(); + triangle.reset(); + noise.reset(); + dmc.reset(); + + last_time = 0; + last_dmc_time = 0; + osc_enables = 0; + irq_flag = false; + earliest_irq_ = no_irq; + frame_delay = 1; + write_register( 0, 0x4017, 0x00 ); + write_register( 0, 0x4015, 0x00 ); + + for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ ) + write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); + + dmc.dac = initial_dmc_dac; + if ( !dmc.nonlinear ) + triangle.last_amp = 15; + if ( !dmc.nonlinear ) // TODO: remove? + dmc.last_amp = initial_dmc_dac; // prevent output transition +} + +void Nes_Apu::irq_changed() +{ + nes_time_t new_irq = dmc.next_irq; + if ( dmc.irq_flag | irq_flag ) { + new_irq = 0; + } + else if ( new_irq > next_irq ) { + new_irq = next_irq; + } + + if ( new_irq != earliest_irq_ ) { + earliest_irq_ = new_irq; + if ( irq_notifier_ ) + irq_notifier_( irq_data ); + } +} + +// frames + +void Nes_Apu::run_until( nes_time_t end_time ) +{ + require( end_time >= last_dmc_time ); + if ( end_time > next_dmc_read_time() ) + { + nes_time_t start = last_dmc_time; + last_dmc_time = end_time; + dmc.run( start, end_time ); + } +} + +void Nes_Apu::run_until_( nes_time_t end_time ) +{ + require( end_time >= last_time ); + + if ( end_time == last_time ) + return; + + if ( last_dmc_time < end_time ) + { + nes_time_t start = last_dmc_time; + last_dmc_time = end_time; + dmc.run( start, end_time ); + } + + while ( true ) + { + // earlier of next frame time or end time + nes_time_t time = last_time + frame_delay; + if ( time > end_time ) + time = end_time; + frame_delay -= time - last_time; + + // run oscs to present + square1.run( last_time, time ); + square2.run( last_time, time ); + triangle.run( last_time, time ); + noise.run( last_time, time ); + last_time = time; + + if ( time == end_time ) + break; // no more frames to run + + // take frame-specific actions + frame_delay = frame_period; + switch ( frame++ ) + { + case 0: + if ( !(frame_mode & 0xC0) ) { + next_irq = time + frame_period * 4 + 2; + irq_flag = true; + } + // fall through + case 2: + // clock length and sweep on frames 0 and 2 + square1.clock_length( 0x20 ); + square2.clock_length( 0x20 ); + noise.clock_length( 0x20 ); + triangle.clock_length( 0x80 ); // different bit for halt flag on triangle + + square1.clock_sweep( -1 ); + square2.clock_sweep( 0 ); + + // frame 2 is slightly shorter in mode 1 + if ( dmc.pal_mode && frame == 3 ) + frame_delay -= 2; + break; + + case 1: + // frame 1 is slightly shorter in mode 0 + if ( !dmc.pal_mode ) + frame_delay -= 2; + break; + + case 3: + frame = 0; + + // frame 3 is almost twice as long in mode 1 + if ( frame_mode & 0x80 ) + frame_delay += frame_period - (dmc.pal_mode ? 2 : 6); + break; + } + + // clock envelopes and linear counter every frame + triangle.clock_linear_counter(); + square1.clock_envelope(); + square2.clock_envelope(); + noise.clock_envelope(); + } +} + +template +inline void zero_apu_osc( T* osc, nes_time_t time ) +{ + Blip_Buffer* output = osc->output; + int last_amp = osc->last_amp; + osc->last_amp = 0; + if ( output && last_amp ) + osc->synth.offset( time, -last_amp, output ); +} + +void Nes_Apu::end_frame( nes_time_t end_time ) +{ + if ( end_time > last_time ) + run_until_( end_time ); + + if ( dmc.nonlinear ) + { + zero_apu_osc( &square1, last_time ); + zero_apu_osc( &square2, last_time ); + zero_apu_osc( &triangle, last_time ); + zero_apu_osc( &noise, last_time ); + zero_apu_osc( &dmc, last_time ); + } + + // make times relative to new frame + last_time -= end_time; + require( last_time >= 0 ); + + last_dmc_time -= end_time; + require( last_dmc_time >= 0 ); + + if ( next_irq != no_irq ) { + next_irq -= end_time; + check( next_irq >= 0 ); + } + if ( dmc.next_irq != no_irq ) { + dmc.next_irq -= end_time; + check( dmc.next_irq >= 0 ); + } + if ( earliest_irq_ != no_irq ) { + earliest_irq_ -= end_time; + if ( earliest_irq_ < 0 ) + earliest_irq_ = 0; + } +} + +// registers + +static const unsigned char length_table [0x20] = { + 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, + 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, + 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, + 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E +}; + +void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) +{ + require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx) + require( (unsigned) data <= 0xFF ); + + // Ignore addresses outside range + if ( unsigned (addr - start_addr) > end_addr - start_addr ) + return; + + run_until_( time ); + + if ( addr < 0x4014 ) + { + // Write to channel + int osc_index = (addr - start_addr) >> 2; + Nes_Osc* osc = oscs [osc_index]; + + int reg = addr & 3; + osc->regs [reg] = data; + osc->reg_written [reg] = true; + + if ( osc_index == 4 ) + { + // handle DMC specially + dmc.write_register( reg, data ); + } + else if ( reg == 3 ) + { + // load length counter + if ( (osc_enables >> osc_index) & 1 ) + osc->length_counter = length_table [(data >> 3) & 0x1F]; + + // reset square phase + if ( osc_index < 2 ) + ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1; + } + } + else if ( addr == 0x4015 ) + { + // Channel enables + for ( int i = osc_count; i--; ) + if ( !((data >> i) & 1) ) + oscs [i]->length_counter = 0; + + bool recalc_irq = dmc.irq_flag; + dmc.irq_flag = false; + + int old_enables = osc_enables; + osc_enables = data; + if ( !(data & 0x10) ) { + dmc.next_irq = no_irq; + recalc_irq = true; + } + else if ( !(old_enables & 0x10) ) { + dmc.start(); // dmc just enabled + } + + if ( recalc_irq ) + irq_changed(); + } + else if ( addr == 0x4017 ) + { + // Frame mode + frame_mode = data; + + bool irq_enabled = !(data & 0x40); + irq_flag &= irq_enabled; + next_irq = no_irq; + + // mode 1 + frame_delay = (frame_delay & 1); + frame = 0; + + if ( !(data & 0x80) ) + { + // mode 0 + frame = 1; + frame_delay += frame_period; + if ( irq_enabled ) + next_irq = time + frame_delay + frame_period * 3 + 1; + } + + irq_changed(); + } +} + +int Nes_Apu::read_status( nes_time_t time ) +{ + run_until_( time - 1 ); + + int result = (dmc.irq_flag << 7) | (irq_flag << 6); + + for ( int i = 0; i < osc_count; i++ ) + if ( oscs [i]->length_counter ) + result |= 1 << i; + + run_until_( time ); + + if ( irq_flag ) + { + result |= 0x40; + irq_flag = false; + irq_changed(); + } + + //debug_printf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result ); + + return result; +} diff --git a/thirdparty/game-music-emu/gme/Nes_Apu.h b/thirdparty/game-music-emu/gme/Nes_Apu.h new file mode 100644 index 000000000..5e722248f --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Apu.h @@ -0,0 +1,179 @@ +// NES 2A03 APU sound chip emulator + +// Nes_Snd_Emu 0.1.8 +#ifndef NES_APU_H +#define NES_APU_H + +#include "blargg_common.h" + +typedef blargg_long nes_time_t; // CPU clock cycle count +typedef unsigned nes_addr_t; // 16-bit memory address + +#include "Nes_Oscs.h" + +struct apu_state_t; +class Nes_Buffer; + +class Nes_Apu { +public: + // Set buffer to generate all sound into, or disable sound if NULL + void output( Blip_Buffer* ); + + // Set memory reader callback used by DMC oscillator to fetch samples. + // When callback is invoked, 'user_data' is passed unchanged as the + // first parameter. + void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL ); + + // All time values are the number of CPU clock cycles relative to the + // beginning of the current time frame. Before resetting the CPU clock + // count, call end_frame( last_cpu_time ). + + // Write to register (0x4000-0x4017, except 0x4014 and 0x4016) + enum { start_addr = 0x4000 }; + enum { end_addr = 0x4017 }; + void write_register( nes_time_t, nes_addr_t, int data ); + + // Read from status register at 0x4015 + enum { status_addr = 0x4015 }; + int read_status( nes_time_t ); + + // Run all oscillators up to specified time, end current time frame, then + // start a new time frame at time 0. Time frames have no effect on emulation + // and each can be whatever length is convenient. + void end_frame( nes_time_t ); + +// Additional optional features (can be ignored without any problem) + + // Reset internal frame counter, registers, and all oscillators. + // Use PAL timing if pal_timing is true, otherwise use NTSC timing. + // Set the DMC oscillator's initial DAC value to initial_dmc_dac without + // any audible click. + void reset( bool pal_mode = false, int initial_dmc_dac = 0 ); + + // Adjust frame period + void set_tempo( double ); + + // Save/load exact emulation state + void save_state( apu_state_t* out ) const; + void load_state( apu_state_t const& ); + + // Set overall volume (default is 1.0) + void volume( double ); + + // Set treble equalization (see notes.txt) + void treble_eq( const blip_eq_t& ); + + // Set sound output of specific oscillator to buffer. If buffer is NULL, + // the specified oscillator is muted and emulation accuracy is reduced. + // The oscillators are indexed as follows: 0) Square 1, 1) Square 2, + // 2) Triangle, 3) Noise, 4) DMC. + enum { osc_count = 5 }; + void osc_output( int index, Blip_Buffer* buffer ); + + // Set IRQ time callback that is invoked when the time of earliest IRQ + // may have changed, or NULL to disable. When callback is invoked, + // 'user_data' is passed unchanged as the first parameter. + void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); + + // Get time that APU-generated IRQ will occur if no further register reads + // or writes occur. If IRQ is already pending, returns irq_waiting. If no + // IRQ will occur, returns no_irq. + enum { no_irq = INT_MAX / 2 + 1 }; + enum { irq_waiting = 0 }; + nes_time_t earliest_irq( nes_time_t ) const; + + // Count number of DMC reads that would occur if 'run_until( t )' were executed. + // If last_read is not NULL, set *last_read to the earliest time that + // 'count_dmc_reads( time )' would result in the same result. + int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const; + + // Time when next DMC memory read will occur + nes_time_t next_dmc_read_time() const; + + // Run DMC until specified time, so that any DMC memory reads can be + // accounted for (i.e. inserting CPU wait states). + void run_until( nes_time_t ); + +public: + Nes_Apu(); + BLARGG_DISABLE_NOTHROW +private: + friend class Nes_Nonlinearizer; + void enable_nonlinear( double volume ); + static double nonlinear_tnd_gain() { return 0.75; } +private: + friend struct Nes_Dmc; + + // noncopyable + Nes_Apu( const Nes_Apu& ); + Nes_Apu& operator = ( const Nes_Apu& ); + + Nes_Osc* oscs [osc_count]; + Nes_Square square1; + Nes_Square square2; + Nes_Noise noise; + Nes_Triangle triangle; + Nes_Dmc dmc; + + double tempo_; + nes_time_t last_time; // has been run until this time in current frame + nes_time_t last_dmc_time; + nes_time_t earliest_irq_; + nes_time_t next_irq; + int frame_period; + int frame_delay; // cycles until frame counter runs next + int frame; // current frame (0-3) + int osc_enables; + int frame_mode; + bool irq_flag; + void (*irq_notifier_)( void* user_data ); + void* irq_data; + Nes_Square::Synth square_synth; // shared by squares + + void irq_changed(); + void state_restored(); + void run_until_( nes_time_t ); + + // TODO: remove + friend class Nes_Core; +}; + +inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf ) +{ + assert( (unsigned) osc < osc_count ); + oscs [osc]->output = buf; +} + +inline nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const +{ + return earliest_irq_; +} + +inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data ) +{ + dmc.prg_reader_data = user_data; + dmc.prg_reader = func; +} + +inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data ) +{ + irq_notifier_ = func; + irq_data = user_data; +} + +inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const +{ + return dmc.count_reads( time, last_read ); +} + +inline nes_time_t Nes_Dmc::next_read_time() const +{ + if ( length_counter == 0 ) + return Nes_Apu::no_irq; // not reading + + return apu->last_dmc_time + delay + long (bits_remain - 1) * period; +} + +inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } + +#endif diff --git a/thirdparty/game-music-emu/gme/Nes_Cpu.cpp b/thirdparty/game-music-emu/gme/Nes_Cpu.cpp new file mode 100644 index 000000000..5eb0862a3 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Cpu.cpp @@ -0,0 +1,1073 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Nes_Cpu.h" + +#include "blargg_endian.h" +#include + +#define BLARGG_CPU_X86 1 + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +#define FLUSH_TIME() (void) (s.time = s_time) +#define CACHE_TIME() (void) (s_time = s.time) + +#include "nes_cpu_io.h" + +#include "blargg_source.h" + +#ifndef CPU_DONE + #define CPU_DONE( cpu, time, result_out ) { result_out = -1; } +#endif + +#ifndef CPU_READ_PPU + #define CPU_READ_PPU( cpu, addr, out, time )\ + {\ + FLUSH_TIME();\ + out = CPU_READ( cpu, addr, time );\ + CACHE_TIME();\ + } +#endif + +#if BLARGG_NONPORTABLE + #define PAGE_OFFSET( addr ) (addr) +#else + #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) +#endif + +inline void Nes_Cpu::set_code_page( int i, void const* p ) +{ + state->code_map [i] = (uint8_t const*) p - PAGE_OFFSET( i * page_size ); +} + +int const st_n = 0x80; +int const st_v = 0x40; +int const st_r = 0x20; +int const st_b = 0x10; +int const st_d = 0x08; +int const st_i = 0x04; +int const st_z = 0x02; +int const st_c = 0x01; + +void Nes_Cpu::reset( void const* unmapped_page ) +{ + check( state == &state_ ); + state = &state_; + r.status = st_i; + r.sp = 0xFF; + r.pc = 0; + r.a = 0; + r.x = 0; + r.y = 0; + state_.time = 0; + state_.base = 0; + irq_time_ = future_nes_time; + end_time_ = future_nes_time; + error_count_ = 0; + + assert( page_size == 0x800 ); // assumes this + set_code_page( page_count, unmapped_page ); + map_code( 0x2000, 0xE000, unmapped_page, true ); + map_code( 0x0000, 0x2000, low_mem, true ); + + blargg_verify_byte_order(); +} + +void Nes_Cpu::map_code( nes_addr_t start, unsigned size, void const* data, bool mirror ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= 0x10000 ); + + unsigned page = start / page_size; + for ( unsigned n = size / page_size; n; --n ) + { + set_code_page( page++, data ); + if ( !mirror ) + data = (char const*) data + page_size; + } +} + +#define TIME (s_time + s.base) +#define READ_LIKELY_PPU( addr, out ) {CPU_READ_PPU( this, (addr), out, TIME );} +#define READ( addr ) CPU_READ( this, (addr), TIME ) +#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} +#define READ_LOW( addr ) (low_mem [int (addr)]) +#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) +#define READ_PROG( addr ) (s.code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )]) + +#define SET_SP( v ) (sp = ((v) + 1) | 0x100) +#define GET_SP() ((sp - 1) & 0xFF) +#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) + +bool Nes_Cpu::run( nes_time_t end_time ) +{ + set_end_time( end_time ); + state_t s = this->state_; + this->state = &s; + // even on x86, using s.time in place of s_time was slower + int16_t s_time = s.time; + + // registers + uint16_t pc = r.pc; + uint8_t a = r.a; + uint8_t x = r.x; + uint8_t y = r.y; + uint16_t sp; + SET_SP( r.sp ); + + // status flags + #define IS_NEG (nz & 0x8080) + + #define CALC_STATUS( out ) do {\ + out = status & (st_v | st_d | st_i);\ + out |= ((nz >> 8) | nz) & st_n;\ + out |= c >> 8 & st_c;\ + if ( !(nz & 0xFF) ) out |= st_z;\ + } while ( 0 ) + + #define SET_STATUS( in ) do {\ + status = in & (st_v | st_d | st_i);\ + nz = in << 8;\ + c = nz;\ + nz |= ~in & st_z;\ + } while ( 0 ) + + uint8_t status; + uint16_t c; // carry set if (c & 0x100) != 0 + uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + { + uint8_t temp = r.status; + SET_STATUS( temp ); + } + + goto loop; +dec_clock_loop: + s_time--; +loop: + + check( (unsigned) GET_SP() < 0x100 ); + check( (unsigned) pc < 0x10000 ); + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + check( (unsigned) y < 0x100 ); + check( -32768 <= s_time && s_time < 32767 ); + + uint8_t const* instr = s.code_map [pc >> page_bits]; + uint8_t opcode; + + // TODO: eliminate this special case + #if BLARGG_NONPORTABLE + opcode = instr [pc]; + pc++; + instr += pc; + #else + instr += PAGE_OFFSET( pc ); + opcode = *instr++; + pc++; + #endif + + static uint8_t const clock_table [256] = + {// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 + 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 + 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 + 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 + 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A + 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E + 3,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 // F + }; // 0x00 was 7 and 0xF2 was 2 + + uint16_t data; + +#if !BLARGG_CPU_X86 + if ( s_time >= 0 ) + goto out_of_time; + s_time += clock_table [opcode]; + + data = *instr; + + switch ( opcode ) + { +#else + + data = clock_table [opcode]; + if ( (s_time += data) >= 0 ) + goto possibly_out_of_time; +almost_out_of_time: + + data = *instr; + + switch ( opcode ) + { +possibly_out_of_time: + if ( s_time < (int) data ) + goto almost_out_of_time; + s_time -= data; + goto out_of_time; +#endif + +// Macros + +#define GET_MSB() (instr [1]) +#define ADD_PAGE() (pc++, data += 0x100 * GET_MSB()) +#define GET_ADDR() GET_LE16( instr ) + +#define NO_PAGE_CROSSING( lsb ) +#define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8; + +#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; + +#define IND_Y( cross, out ) {\ + uint16_t temp = READ_LOW( data ) + y;\ + out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ + cross( temp );\ + } + +#define IND_X( out ) {\ + uint16_t temp = data + x;\ + out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ + } + +#define ARITH_ADDR_MODES( op )\ +case op - 0x04: /* (ind,x) */\ + IND_X( data )\ + goto ptr##op;\ +case op + 0x0C: /* (ind),y */\ + IND_Y( HANDLE_PAGE_CROSSING, data )\ + goto ptr##op;\ +case op + 0x10: /* zp,X */\ + data = uint8_t (data + x);\ +case op + 0x00: /* zp */\ + data = READ_LOW( data );\ + goto imm##op;\ +case op + 0x14: /* abs,Y */\ + data += y;\ + goto ind##op;\ +case op + 0x18: /* abs,X */\ + data += x;\ +ind##op:\ + HANDLE_PAGE_CROSSING( data );\ +case op + 0x08: /* abs */\ + ADD_PAGE();\ +ptr##op:\ + FLUSH_TIME();\ + data = READ( data );\ + CACHE_TIME();\ +case op + 0x04: /* imm */\ +imm##op: + +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH( cond )\ +{\ + int16_t offset = (int8_t) data;\ + uint16_t extra_clock = (++pc & 0xFF) + offset;\ + if ( !(cond) ) goto dec_clock_loop;\ + pc = uint16_t (pc + offset);\ + s_time += extra_clock >> 8 & 1;\ + goto loop;\ +} + +// Often-Used + + case 0xB5: // LDA zp,x + a = nz = READ_LOW( uint8_t (data + x) ); + pc++; + goto loop; + + case 0xA5: // LDA zp + a = nz = READ_LOW( data ); + pc++; + goto loop; + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ); + + case 0x20: { // JSR + uint16_t temp = pc + 1; + pc = GET_ADDR(); + WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); + sp = (sp - 2) | 0x100; + WRITE_LOW( sp, temp ); + goto loop; + } + + case 0x4C: // JMP abs + pc = GET_ADDR(); + goto loop; + + case 0xE8: // INX + INC_DEC_XY( x, 1 ) + + case 0x10: // BPL + BRANCH( !IS_NEG ) + + ARITH_ADDR_MODES( 0xC5 ) // CMP + nz = a - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0x30: // BMI + BRANCH( IS_NEG ) + + case 0xF0: // BEQ + BRANCH( !(uint8_t) nz ); + + case 0x95: // STA zp,x + data = uint8_t (data + x); + case 0x85: // STA zp + pc++; + WRITE_LOW( data, a ); + goto loop; + + case 0xC8: // INY + INC_DEC_XY( y, 1 ) + + case 0xA8: // TAY + y = a; + nz = a; + goto loop; + + case 0x98: // TYA + a = y; + nz = y; + goto loop; + + case 0xAD:{// LDA abs + unsigned addr = GET_ADDR(); + pc += 2; + READ_LIKELY_PPU( addr, nz ); + a = nz; + goto loop; + } + + case 0x60: // RTS + pc = 1 + READ_LOW( sp ); + pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); + sp = (sp - 0xFE) | 0x100; + goto loop; + + { + uint16_t addr; + + case 0x99: // STA abs,Y + addr = y + GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + goto sta_ptr; + + case 0x8D: // STA abs + addr = GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + goto sta_ptr; + + case 0x9D: // STA abs,X (slightly more common than STA abs) + addr = x + GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + sta_ptr: + FLUSH_TIME(); + WRITE( addr, a ); + CACHE_TIME(); + goto loop; + + case 0x91: // STA (ind),Y + IND_Y( NO_PAGE_CROSSING, addr ) + pc++; + goto sta_ptr; + + case 0x81: // STA (ind,X) + IND_X( addr ) + pc++; + goto sta_ptr; + + } + + case 0xA9: // LDA #imm + pc++; + a = data; + nz = data; + goto loop; + + // common read instructions + { + uint16_t addr; + + case 0xA1: // LDA (ind,X) + IND_X( addr ) + pc++; + goto a_nz_read_addr; + + case 0xB1:// LDA (ind),Y + addr = READ_LOW( data ) + y; + HANDLE_PAGE_CROSSING( addr ); + addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); + pc++; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + goto a_nz_read_addr; + + case 0xB9: // LDA abs,Y + HANDLE_PAGE_CROSSING( data + y ); + addr = GET_ADDR() + y; + pc += 2; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + goto a_nz_read_addr; + + case 0xBD: // LDA abs,X + HANDLE_PAGE_CROSSING( data + x ); + addr = GET_ADDR() + x; + pc += 2; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + a_nz_read_addr: + FLUSH_TIME(); + a = nz = READ( addr ); + CACHE_TIME(); + goto loop; + + } + +// Branch + + case 0x50: // BVC + BRANCH( !(status & st_v) ) + + case 0x70: // BVS + BRANCH( status & st_v ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + +// Load/store + + case 0x94: // STY zp,x + data = uint8_t (data + x); + case 0x84: // STY zp + pc++; + WRITE_LOW( data, y ); + goto loop; + + case 0x96: // STX zp,y + data = uint8_t (data + y); + case 0x86: // STX zp + pc++; + WRITE_LOW( data, x ); + goto loop; + + case 0xB6: // LDX zp,y + data = uint8_t (data + y); + case 0xA6: // LDX zp + data = READ_LOW( data ); + case 0xA2: // LDX #imm + pc++; + x = data; + nz = data; + goto loop; + + case 0xB4: // LDY zp,x + data = uint8_t (data + x); + case 0xA4: // LDY zp + data = READ_LOW( data ); + case 0xA0: // LDY #imm + pc++; + y = data; + nz = data; + goto loop; + + case 0xBC: // LDY abs,X + data += x; + HANDLE_PAGE_CROSSING( data ); + case 0xAC:{// LDY abs + unsigned addr = data + 0x100 * GET_MSB(); + pc += 2; + FLUSH_TIME(); + y = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + case 0xBE: // LDX abs,y + data += y; + HANDLE_PAGE_CROSSING( data ); + case 0xAE:{// LDX abs + unsigned addr = data + 0x100 * GET_MSB(); + pc += 2; + FLUSH_TIME(); + x = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + { + uint8_t temp; + case 0x8C: // STY abs + temp = y; + goto store_abs; + + case 0x8E: // STX abs + temp = x; + store_abs: + unsigned addr = GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, temp ); + goto loop; + } + FLUSH_TIME(); + WRITE( addr, temp ); + CACHE_TIME(); + goto loop; + } + +// Compare + + case 0xEC:{// CPX abs + unsigned addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( addr ); + CACHE_TIME(); + goto cpx_data; + } + + case 0xE4: // CPX zp + data = READ_LOW( data ); + case 0xE0: // CPX #imm + cpx_data: + nz = x - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0xCC:{// CPY abs + unsigned addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( addr ); + CACHE_TIME(); + goto cpy_data; + } + + case 0xC4: // CPY zp + data = READ_LOW( data ); + case 0xC0: // CPY #imm + cpy_data: + nz = y - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + +// Logical + + ARITH_ADDR_MODES( 0x25 ) // AND + nz = (a &= data); + pc++; + goto loop; + + ARITH_ADDR_MODES( 0x45 ) // EOR + nz = (a ^= data); + pc++; + goto loop; + + ARITH_ADDR_MODES( 0x05 ) // ORA + nz = (a |= data); + pc++; + goto loop; + + case 0x2C:{// BIT abs + unsigned addr = GET_ADDR(); + pc += 2; + status &= ~st_v; + READ_LIKELY_PPU( addr, nz ); + status |= nz & st_v; + if ( a & nz ) + goto loop; + nz <<= 8; // result must be zero, even if N bit is set + goto loop; + } + + case 0x24: // BIT zp + nz = READ_LOW( data ); + pc++; + status &= ~st_v; + status |= nz & st_v; + if ( a & nz ) + goto loop; + nz <<= 8; // result must be zero, even if N bit is set + goto loop; + +// Add/subtract + + ARITH_ADDR_MODES( 0xE5 ) // SBC + case 0xEB: // unofficial equivalent + data ^= 0xFF; + goto adc_imm; + + ARITH_ADDR_MODES( 0x65 ) // ADC + adc_imm: { + int16_t carry = c >> 8 & 1; + int16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend + status &= ~st_v; + status |= ov >> 2 & 0x40; + c = nz = a + data + carry; + pc++; + a = (uint8_t) nz; + goto loop; + } + +// Shift/rotate + + case 0x4A: // LSR A + c = 0; + case 0x6A: // ROR A + nz = c >> 1 & 0x80; + c = a << 8; + nz |= a >> 1; + a = nz; + goto loop; + + case 0x0A: // ASL A + nz = a << 1; + c = nz; + a = (uint8_t) nz; + goto loop; + + case 0x2A: { // ROL A + nz = a << 1; + int16_t temp = c >> 8 & 1; + c = nz; + nz |= temp; + a = (uint8_t) nz; + goto loop; + } + + case 0x5E: // LSR abs,X + data += x; + case 0x4E: // LSR abs + c = 0; + case 0x6E: // ROR abs + ror_abs: { + ADD_PAGE(); + FLUSH_TIME(); + int temp = READ( data ); + nz = (c >> 1 & 0x80) | (temp >> 1); + c = temp << 8; + goto rotate_common; + } + + case 0x3E: // ROL abs,X + data += x; + goto rol_abs; + + case 0x1E: // ASL abs,X + data += x; + case 0x0E: // ASL abs + c = 0; + case 0x2E: // ROL abs + rol_abs: + ADD_PAGE(); + nz = c >> 8 & 1; + FLUSH_TIME(); + nz |= (c = READ( data ) << 1); + rotate_common: + pc++; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + + case 0x7E: // ROR abs,X + data += x; + goto ror_abs; + + case 0x76: // ROR zp,x + data = uint8_t (data + x); + goto ror_zp; + + case 0x56: // LSR zp,x + data = uint8_t (data + x); + case 0x46: // LSR zp + c = 0; + case 0x66: // ROR zp + ror_zp: { + int temp = READ_LOW( data ); + nz = (c >> 1 & 0x80) | (temp >> 1); + c = temp << 8; + goto write_nz_zp; + } + + case 0x36: // ROL zp,x + data = uint8_t (data + x); + goto rol_zp; + + case 0x16: // ASL zp,x + data = uint8_t (data + x); + case 0x06: // ASL zp + c = 0; + case 0x26: // ROL zp + rol_zp: + nz = c >> 8 & 1; + nz |= (c = READ_LOW( data ) << 1); + goto write_nz_zp; + +// Increment/decrement + + case 0xCA: // DEX + INC_DEC_XY( x, -1 ) + + case 0x88: // DEY + INC_DEC_XY( y, -1 ) + + case 0xF6: // INC zp,x + data = uint8_t (data + x); + case 0xE6: // INC zp + nz = 1; + goto add_nz_zp; + + case 0xD6: // DEC zp,x + data = uint8_t (data + x); + case 0xC6: // DEC zp + nz = (uint16_t) -1; + add_nz_zp: + nz += READ_LOW( data ); + write_nz_zp: + pc++; + WRITE_LOW( data, nz ); + goto loop; + + case 0xFE: // INC abs,x + data = x + GET_ADDR(); + goto inc_ptr; + + case 0xEE: // INC abs + data = GET_ADDR(); + inc_ptr: + nz = 1; + goto inc_common; + + case 0xDE: // DEC abs,x + data = x + GET_ADDR(); + goto dec_ptr; + + case 0xCE: // DEC abs + data = GET_ADDR(); + dec_ptr: + nz = (uint16_t) -1; + inc_common: + FLUSH_TIME(); + nz += READ( data ); + pc += 2; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + +// Transfer + + case 0xAA: // TAX + x = a; + nz = a; + goto loop; + + case 0x8A: // TXA + a = x; + nz = x; + goto loop; + + case 0x9A: // TXS + SET_SP( x ); // verified (no flag change) + goto loop; + + case 0xBA: // TSX + x = nz = GET_SP(); + goto loop; + +// Stack + + case 0x48: // PHA + PUSH( a ); // verified + goto loop; + + case 0x68: // PLA + a = nz = READ_LOW( sp ); + sp = (sp - 0xFF) | 0x100; + goto loop; + + case 0x40:{// RTI + uint8_t temp = READ_LOW( sp ); + pc = READ_LOW( 0x100 | (sp - 0xFF) ); + pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; + sp = (sp - 0xFD) | 0x100; + data = status; + SET_STATUS( temp ); + if ( !((data ^ status) & st_i) ) goto loop; // I flag didn't change + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - irq_time_; + if ( delta <= 0 ) goto loop; + if ( status & st_i ) goto loop; + s_time += delta; + s.base = irq_time_; + goto loop; + } + + case 0x28:{// PLP + uint8_t temp = READ_LOW( sp ); + sp = (sp - 0xFF) | 0x100; + uint8_t changed = status ^ temp; + SET_STATUS( temp ); + if ( !(changed & st_i) ) + goto loop; // I flag didn't change + if ( status & st_i ) + goto handle_sei; + goto handle_cli; + } + + case 0x08: { // PHP + uint8_t temp; + CALC_STATUS( temp ); + PUSH( temp | (st_b | st_r) ); + goto loop; + } + + case 0x6C:{// JMP (ind) + data = GET_ADDR(); + check( unsigned (data - 0x2000) >= 0x4000 ); // ensure it's outside I/O space + uint8_t const* page = s.code_map [data >> page_bits]; + pc = page [PAGE_OFFSET( data )]; + data = (data & 0xFF00) | ((data + 1) & 0xFF); + pc |= page [PAGE_OFFSET( data )] << 8; + goto loop; + } + + case 0x00: // BRK + goto handle_brk; + +// Flags + + case 0x38: // SEC + c = (uint16_t) ~0; + goto loop; + + case 0x18: // CLC + c = 0; + goto loop; + + case 0xB8: // CLV + status &= ~st_v; + goto loop; + + case 0xD8: // CLD + status &= ~st_d; + goto loop; + + case 0xF8: // SED + status |= st_d; + goto loop; + + case 0x58: // CLI + if ( !(status & st_i) ) + goto loop; + status &= ~st_i; + handle_cli: { + //debug_printf( "CLI at %d\n", TIME ); + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - irq_time_; + if ( delta <= 0 ) + { + if ( TIME < irq_time_ ) + goto loop; + goto delayed_cli; + } + s.base = irq_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + + if ( delta >= s_time + 1 ) + { + s.base += s_time + 1; + s_time = -1; + goto loop; + } + + // TODO: implement + delayed_cli: + debug_printf( "Delayed CLI not emulated\n" ); + goto loop; + } + + case 0x78: // SEI + if ( status & st_i ) + goto loop; + status |= st_i; + handle_sei: { + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - end_time_; + s.base = end_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + + debug_printf( "Delayed SEI not emulated\n" ); + goto loop; + } + +// Unofficial + + // SKW - Skip word + case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: + HANDLE_PAGE_CROSSING( data + x ); + case 0x0C: + pc++; + // SKB - Skip byte + case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: + case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: + pc++; + goto loop; + + // NOP + case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: + goto loop; + + case bad_opcode: // HLT + pc--; + case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: + case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: + goto stop; + +// Unimplemented + + case 0xFF: // force 256-entry jump table for optimization purposes + c |= 1; + default: + check( (unsigned) opcode <= 0xFF ); + // skip over proper number of bytes + static unsigned char const illop_lens [8] = { + 0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0 + }; + uint8_t opcode = instr [-1]; + int16_t len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3; + if ( opcode == 0x9C ) + len = 2; + pc += len; + error_count_++; + + if ( (opcode >> 4) == 0x0B ) + { + if ( opcode == 0xB3 ) + data = READ_LOW( data ); + if ( opcode != 0xB7 ) + HANDLE_PAGE_CROSSING( data + y ); + } + goto loop; + } + assert( false ); + + int result_; +handle_brk: + pc++; + result_ = 4; + +interrupt: + { + s_time += 7; + + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); + WRITE_LOW( 0x100 | (sp - 2), pc ); + pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); + + sp = (sp - 3) | 0x100; + uint8_t temp; + CALC_STATUS( temp ); + temp |= st_r; + if ( result_ ) + temp |= st_b; // TODO: incorrectly sets B flag for IRQ + WRITE_LOW( sp, temp ); + + this->r.status = status |= st_i; + blargg_long delta = s.base - end_time_; + if ( delta >= 0 ) goto loop; + s_time += delta; + s.base = end_time_; + goto loop; + } + +out_of_time: + pc--; + FLUSH_TIME(); + CPU_DONE( this, TIME, result_ ); + CACHE_TIME(); + if ( result_ >= 0 ) + goto interrupt; + if ( s_time < 0 ) + goto loop; + +stop: + + s.time = s_time; + + r.pc = pc; + r.sp = GET_SP(); + r.a = a; + r.x = x; + r.y = y; + + { + uint8_t temp; + CALC_STATUS( temp ); + r.status = temp; + } + + this->state_ = s; + this->state = &this->state_; + + return s_time < 0; +} + diff --git a/thirdparty/game-music-emu/gme/Nes_Cpu.h b/thirdparty/game-music-emu/gme/Nes_Cpu.h new file mode 100644 index 000000000..878b5ba5c --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Cpu.h @@ -0,0 +1,112 @@ +// NES 6502 CPU emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef NES_CPU_H +#define NES_CPU_H + +#include "blargg_common.h" + +typedef blargg_long nes_time_t; // clock cycle count +typedef unsigned nes_addr_t; // 16-bit address +enum { future_nes_time = INT_MAX / 2 + 1 }; + +class Nes_Cpu { +public: + // Clear registers, map low memory and its three mirrors to address 0, + // and mirror unmapped_page in remaining memory + void reset( void const* unmapped_page = 0 ); + + // Map code memory (memory accessed via the program counter). Start and size + // must be multiple of page_size. If mirror is true, repeats code page + // throughout address range. + enum { page_size = 0x800 }; + void map_code( nes_addr_t start, unsigned size, void const* code, bool mirror = false ); + + // Access emulated memory as CPU does + uint8_t const* get_code( nes_addr_t ); + + // 2KB of RAM at address 0 + uint8_t low_mem [0x800]; + + // NES 6502 registers. Not kept updated during a call to run(). + struct registers_t { + uint16_t pc; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t status; + uint8_t sp; + }; + registers_t r; + + // Set end_time and run CPU from current time. Returns true if execution + // stopped due to encountering bad_opcode. + bool run( nes_time_t end_time ); + + // Time of beginning of next instruction to be executed + nes_time_t time() const { return state->time + state->base; } + void set_time( nes_time_t t ) { state->time = t - state->base; } + void adjust_time( int delta ) { state->time += delta; } + + nes_time_t irq_time() const { return irq_time_; } + void set_irq_time( nes_time_t ); + + nes_time_t end_time() const { return end_time_; } + void set_end_time( nes_time_t ); + + // Number of undefined instructions encountered and skipped + void clear_error_count() { error_count_ = 0; } + unsigned long error_count() const { return error_count_; } + + // CPU invokes bad opcode handler if it encounters this + enum { bad_opcode = 0xF2 }; + +public: + Nes_Cpu() { state = &state_; } + enum { page_bits = 11 }; + enum { page_count = 0x10000 >> page_bits }; + enum { irq_inhibit = 0x04 }; +private: + struct state_t { + uint8_t const* code_map [page_count + 1]; + nes_time_t base; + int time; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + nes_time_t irq_time_; + nes_time_t end_time_; + unsigned long error_count_; + + void set_code_page( int, void const* ); + inline int update_end_time( nes_time_t end, nes_time_t irq ); +}; + +inline uint8_t const* Nes_Cpu::get_code( nes_addr_t addr ) +{ + return state->code_map [addr >> page_bits] + addr + #if !BLARGG_NONPORTABLE + % (unsigned) page_size + #endif + ; +} + +inline int Nes_Cpu::update_end_time( nes_time_t t, nes_time_t irq ) +{ + if ( irq < t && !(r.status & irq_inhibit) ) t = irq; + int delta = state->base - t; + state->base = t; + return delta; +} + +inline void Nes_Cpu::set_irq_time( nes_time_t t ) +{ + state->time += update_end_time( end_time_, (irq_time_ = t) ); +} + +inline void Nes_Cpu::set_end_time( nes_time_t t ) +{ + state->time += update_end_time( (end_time_ = t), irq_time_ ); +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Nes_Fme7_Apu.cpp b/thirdparty/game-music-emu/gme/Nes_Fme7_Apu.cpp new file mode 100644 index 000000000..93973e409 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Fme7_Apu.cpp @@ -0,0 +1,121 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Nes_Fme7_Apu.h" + +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void Nes_Fme7_Apu::reset() +{ + last_time = 0; + + for ( int i = 0; i < osc_count; i++ ) + oscs [i].last_amp = 0; + + fme7_apu_state_t* state = this; + memset( state, 0, sizeof *state ); +} + +unsigned char const Nes_Fme7_Apu::amp_table [16] = +{ + #define ENTRY( n ) (unsigned char) (n * amp_range + 0.5) + ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156), + ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624), + ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498), + ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000) + #undef ENTRY +}; + +void Nes_Fme7_Apu::run_until( blip_time_t end_time ) +{ + require( end_time >= last_time ); + + for ( int index = 0; index < osc_count; index++ ) + { + int mode = regs [7] >> index; + int vol_mode = regs [010 + index]; + int volume = amp_table [vol_mode & 0x0F]; + + Blip_Buffer* const osc_output = oscs [index].output; + if ( !osc_output ) + continue; + osc_output->set_modified(); + + // check for unsupported mode + #ifndef NDEBUG + if ( (mode & 011) <= 001 && vol_mode & 0x1F ) + debug_printf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n", + mode, vol_mode & 0x1F ); + #endif + + if ( (mode & 001) | (vol_mode & 0x10) ) + volume = 0; // noise and envelope aren't supported + + // period + int const period_factor = 16; + unsigned period = (regs [index * 2 + 1] & 0x0F) * 0x100 * period_factor + + regs [index * 2] * period_factor; + if ( period < 50 ) // around 22 kHz + { + volume = 0; + if ( !period ) // on my AY-3-8910A, period doesn't have extra one added + period = period_factor; + } + + // current amplitude + int amp = volume; + if ( !phases [index] ) + amp = 0; + { + int delta = amp - oscs [index].last_amp; + if ( delta ) + { + oscs [index].last_amp = amp; + synth.offset( last_time, delta, osc_output ); + } + } + + blip_time_t time = last_time + delays [index]; + if ( time < end_time ) + { + int delta = amp * 2 - volume; + if ( volume ) + { + do + { + delta = -delta; + synth.offset_inline( time, delta, osc_output ); + time += period; + } + while ( time < end_time ); + + oscs [index].last_amp = (delta + volume) >> 1; + phases [index] = (delta > 0); + } + else + { + // maintain phase when silent + int count = (end_time - time + period - 1) / period; + phases [index] ^= count & 1; + time += (blargg_long) count * period; + } + } + + delays [index] = time - end_time; + } + + last_time = end_time; +} + diff --git a/thirdparty/game-music-emu/gme/Nes_Fme7_Apu.h b/thirdparty/game-music-emu/gme/Nes_Fme7_Apu.h new file mode 100644 index 000000000..b79ed6f5e --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Fme7_Apu.h @@ -0,0 +1,131 @@ +// Sunsoft FME-7 sound emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef NES_FME7_APU_H +#define NES_FME7_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct fme7_apu_state_t +{ + enum { reg_count = 14 }; + uint8_t regs [reg_count]; + uint8_t phases [3]; // 0 or 1 + uint8_t latch; + uint16_t delays [3]; // a, b, c +}; + +class Nes_Fme7_Apu : private fme7_apu_state_t { +public: + // See Nes_Apu.h for reference + void reset(); + void volume( double ); + void treble_eq( blip_eq_t const& ); + void output( Blip_Buffer* ); + enum { osc_count = 3 }; + void osc_output( int index, Blip_Buffer* ); + void end_frame( blip_time_t ); + void save_state( fme7_apu_state_t* ) const; + void load_state( fme7_apu_state_t const& ); + + // Mask and addresses of registers + enum { addr_mask = 0xE000 }; + enum { data_addr = 0xE000 }; + enum { latch_addr = 0xC000 }; + + // (addr & addr_mask) == latch_addr + void write_latch( int ); + + // (addr & addr_mask) == data_addr + void write_data( blip_time_t, int data ); + +public: + Nes_Fme7_Apu(); + BLARGG_DISABLE_NOTHROW +private: + // noncopyable + Nes_Fme7_Apu( const Nes_Fme7_Apu& ); + Nes_Fme7_Apu& operator = ( const Nes_Fme7_Apu& ); + + static unsigned char const amp_table [16]; + + struct { + Blip_Buffer* output; + int last_amp; + } oscs [osc_count]; + blip_time_t last_time; + + enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff + Blip_Synth synth; + + void run_until( blip_time_t ); +}; + +inline void Nes_Fme7_Apu::volume( double v ) +{ + synth.volume( 0.38 / amp_range * v ); // to do: fine-tune +} + +inline void Nes_Fme7_Apu::treble_eq( blip_eq_t const& eq ) +{ + synth.treble_eq( eq ); +} + +inline void Nes_Fme7_Apu::osc_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Nes_Fme7_Apu::output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, buf ); +} + +inline Nes_Fme7_Apu::Nes_Fme7_Apu() +{ + output( NULL ); + volume( 1.0 ); + reset(); +} + +inline void Nes_Fme7_Apu::write_latch( int data ) { latch = data; } + +inline void Nes_Fme7_Apu::write_data( blip_time_t time, int data ) +{ + if ( (unsigned) latch >= reg_count ) + { + #ifdef debug_printf + debug_printf( "FME7 write to %02X (past end of sound registers)\n", (int) latch ); + #endif + return; + } + + run_until( time ); + regs [latch] = data; +} + +inline void Nes_Fme7_Apu::end_frame( blip_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +inline void Nes_Fme7_Apu::save_state( fme7_apu_state_t* out ) const +{ + *out = *this; +} + +inline void Nes_Fme7_Apu::load_state( fme7_apu_state_t const& in ) +{ + reset(); + fme7_apu_state_t* state = this; + *state = in; +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Nes_Namco_Apu.cpp b/thirdparty/game-music-emu/gme/Nes_Namco_Apu.cpp new file mode 100644 index 000000000..3e5fc1491 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Namco_Apu.cpp @@ -0,0 +1,145 @@ +// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ + +#include "Nes_Namco_Apu.h" + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Nes_Namco_Apu::Nes_Namco_Apu() +{ + output( NULL ); + volume( 1.0 ); + reset(); +} + +void Nes_Namco_Apu::reset() +{ + last_time = 0; + addr_reg = 0; + + int i; + for ( i = 0; i < reg_count; i++ ) + reg [i] = 0; + + for ( i = 0; i < osc_count; i++ ) + { + Namco_Osc& osc = oscs [i]; + osc.delay = 0; + osc.last_amp = 0; + osc.wave_pos = 0; + } +} + +void Nes_Namco_Apu::output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, buf ); +} + +/* +void Nes_Namco_Apu::reflect_state( Tagged_Data& data ) +{ + reflect_int16( data, BLARGG_4CHAR('A','D','D','R'), &addr_reg ); + + static const char hex [17] = "0123456789ABCDEF"; + int i; + for ( i = 0; i < reg_count; i++ ) + reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], ® [i] ); + + for ( i = 0; i < osc_count; i++ ) + { + reflect_int32( data, BLARGG_4CHAR('D','L','Y','0') + i, &oscs [i].delay ); + reflect_int16( data, BLARGG_4CHAR('P','O','S','0') + i, &oscs [i].wave_pos ); + } +} +*/ + +void Nes_Namco_Apu::end_frame( blip_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +void Nes_Namco_Apu::run_until( blip_time_t nes_end_time ) +{ + int active_oscs = (reg [0x7F] >> 4 & 7) + 1; + for ( int i = osc_count - active_oscs; i < osc_count; i++ ) + { + Namco_Osc& osc = oscs [i]; + Blip_Buffer* output = osc.output; + if ( !output ) + continue; + output->set_modified(); + + blip_resampled_time_t time = + output->resampled_time( last_time ) + osc.delay; + blip_resampled_time_t end_time = output->resampled_time( nes_end_time ); + osc.delay = 0; + if ( time < end_time ) + { + const uint8_t* osc_reg = ® [i * 8 + 0x40]; + if ( !(osc_reg [4] & 0xE0) ) + continue; + + int volume = osc_reg [7] & 15; + if ( !volume ) + continue; + + blargg_long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0]; + if ( freq < 64 * active_oscs ) + continue; // prevent low frequencies from excessively delaying freq changes + blip_resampled_time_t period = + output->resampled_duration( 983040 ) / freq * active_oscs; + + int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4; + if ( !wave_size ) + continue; + + int last_amp = osc.last_amp; + int wave_pos = osc.wave_pos; + + do + { + // read wave sample + int addr = wave_pos + osc_reg [6]; + int sample = reg [addr >> 1] >> (addr << 2 & 4); + wave_pos++; + sample = (sample & 15) * volume; + + // output impulse if amplitude changed + int delta = sample - last_amp; + if ( delta ) + { + last_amp = sample; + synth.offset_resampled( time, delta, output ); + } + + // next sample + time += period; + if ( wave_pos >= wave_size ) + wave_pos = 0; + } + while ( time < end_time ); + + osc.wave_pos = wave_pos; + osc.last_amp = last_amp; + } + osc.delay = time - end_time; + } + + last_time = nes_end_time; +} + diff --git a/thirdparty/game-music-emu/gme/Nes_Namco_Apu.h b/thirdparty/game-music-emu/gme/Nes_Namco_Apu.h new file mode 100644 index 000000000..876d85e0a --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Namco_Apu.h @@ -0,0 +1,102 @@ +// Namco 106 sound chip emulator + +// Nes_Snd_Emu 0.1.8 +#ifndef NES_NAMCO_APU_H +#define NES_NAMCO_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct namco_state_t; + +class Nes_Namco_Apu { +public: + // See Nes_Apu.h for reference. + void volume( double ); + void treble_eq( const blip_eq_t& ); + void output( Blip_Buffer* ); + enum { osc_count = 8 }; + void osc_output( int index, Blip_Buffer* ); + void reset(); + void end_frame( blip_time_t ); + + // Read/write data register is at 0x4800 + enum { data_reg_addr = 0x4800 }; + void write_data( blip_time_t, int ); + int read_data(); + + // Write-only address register is at 0xF800 + enum { addr_reg_addr = 0xF800 }; + void write_addr( int ); + + // to do: implement save/restore + void save_state( namco_state_t* out ) const; + void load_state( namco_state_t const& ); + +public: + Nes_Namco_Apu(); + BLARGG_DISABLE_NOTHROW +private: + // noncopyable + Nes_Namco_Apu( const Nes_Namco_Apu& ); + Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& ); + + struct Namco_Osc { + blargg_long delay; + Blip_Buffer* output; + short last_amp; + short wave_pos; + }; + + Namco_Osc oscs [osc_count]; + + blip_time_t last_time; + int addr_reg; + + enum { reg_count = 0x80 }; + uint8_t reg [reg_count]; + Blip_Synth synth; + + uint8_t& access(); + void run_until( blip_time_t ); +}; +/* +struct namco_state_t +{ + uint8_t regs [0x80]; + uint8_t addr; + uint8_t unused; + uint8_t positions [8]; + uint32_t delays [8]; +}; +*/ + +inline uint8_t& Nes_Namco_Apu::access() +{ + int addr = addr_reg & 0x7F; + if ( addr_reg & 0x80 ) + addr_reg = (addr + 1) | 0x80; + return reg [addr]; +} + +inline void Nes_Namco_Apu::volume( double v ) { synth.volume( 0.10 / osc_count * v ); } + +inline void Nes_Namco_Apu::treble_eq( const blip_eq_t& eq ) { synth.treble_eq( eq ); } + +inline void Nes_Namco_Apu::write_addr( int v ) { addr_reg = v; } + +inline int Nes_Namco_Apu::read_data() { return access(); } + +inline void Nes_Namco_Apu::osc_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Nes_Namco_Apu::write_data( blip_time_t time, int data ) +{ + run_until( time ); + access() = data; +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Nes_Oscs.cpp b/thirdparty/game-music-emu/gme/Nes_Oscs.cpp new file mode 100644 index 000000000..1ad3f59c0 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Oscs.cpp @@ -0,0 +1,551 @@ +// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ + +#include "Nes_Apu.h" + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Nes_Osc + +void Nes_Osc::clock_length( int halt_mask ) +{ + if ( length_counter && !(regs [0] & halt_mask) ) + length_counter--; +} + +void Nes_Envelope::clock_envelope() +{ + int period = regs [0] & 15; + if ( reg_written [3] ) { + reg_written [3] = false; + env_delay = period; + envelope = 15; + } + else if ( --env_delay < 0 ) { + env_delay = period; + if ( envelope | (regs [0] & 0x20) ) + envelope = (envelope - 1) & 15; + } +} + +int Nes_Envelope::volume() const +{ + return length_counter == 0 ? 0 : (regs [0] & 0x10) ? (regs [0] & 15) : envelope; +} + +// Nes_Square + +void Nes_Square::clock_sweep( int negative_adjust ) +{ + int sweep = regs [1]; + + if ( --sweep_delay < 0 ) + { + reg_written [1] = true; + + int period = this->period(); + int shift = sweep & shift_mask; + if ( shift && (sweep & 0x80) && period >= 8 ) + { + int offset = period >> shift; + + if ( sweep & negate_flag ) + offset = negative_adjust - offset; + + if ( period + offset < 0x800 ) + { + period += offset; + // rewrite period + regs [2] = period & 0xFF; + regs [3] = (regs [3] & ~7) | ((period >> 8) & 7); + } + } + } + + if ( reg_written [1] ) { + reg_written [1] = false; + sweep_delay = (sweep >> 4) & 7; + } +} + +// TODO: clean up +inline nes_time_t Nes_Square::maintain_phase( nes_time_t time, nes_time_t end_time, + nes_time_t timer_period ) +{ + nes_time_t remain = end_time - time; + if ( remain > 0 ) + { + int count = (remain + timer_period - 1) / timer_period; + phase = (phase + count) & (phase_range - 1); + time += (blargg_long) count * timer_period; + } + return time; +} + +void Nes_Square::run( nes_time_t time, nes_time_t end_time ) +{ + const int period = this->period(); + const int timer_period = (period + 1) * 2; + + if ( !output ) + { + delay = maintain_phase( time + delay, end_time, timer_period ) - end_time; + return; + } + + output->set_modified(); + + int offset = period >> (regs [1] & shift_mask); + if ( regs [1] & negate_flag ) + offset = 0; + + const int volume = this->volume(); + if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) + { + if ( last_amp ) { + synth.offset( time, -last_amp, output ); + last_amp = 0; + } + + time += delay; + time = maintain_phase( time, end_time, timer_period ); + } + else + { + // handle duty select + int duty_select = (regs [0] >> 6) & 3; + int duty = 1 << duty_select; // 1, 2, 4, 2 + int amp = 0; + if ( duty_select == 3 ) { + duty = 2; // negated 25% + amp = volume; + } + if ( phase < duty ) + amp ^= volume; + + { + int delta = update_amp( amp ); + if ( delta ) + synth.offset( time, delta, output ); + } + + time += delay; + if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + const Synth& synth = this->synth; + int delta = amp * 2 - volume; + int phase = this->phase; + + do { + phase = (phase + 1) & (phase_range - 1); + if ( phase == 0 || phase == duty ) { + delta = -delta; + synth.offset_inline( time, delta, output ); + } + time += timer_period; + } + while ( time < end_time ); + + last_amp = (delta + volume) >> 1; + this->phase = phase; + } + } + + delay = time - end_time; +} + +// Nes_Triangle + +void Nes_Triangle::clock_linear_counter() +{ + if ( reg_written [3] ) + linear_counter = regs [0] & 0x7F; + else if ( linear_counter ) + linear_counter--; + + if ( !(regs [0] & 0x80) ) + reg_written [3] = false; +} + +inline int Nes_Triangle::calc_amp() const +{ + int amp = phase_range - phase; + if ( amp < 0 ) + amp = phase - (phase_range + 1); + return amp; +} + +// TODO: clean up +inline nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_time, + nes_time_t timer_period ) +{ + nes_time_t remain = end_time - time; + if ( remain > 0 ) + { + int count = (remain + timer_period - 1) / timer_period; + phase = ((unsigned) phase + 1 - count) & (phase_range * 2 - 1); + phase++; + time += (blargg_long) count * timer_period; + } + return time; +} + +void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) +{ + const int timer_period = period() + 1; + if ( !output ) + { + time += delay; + delay = 0; + if ( length_counter && linear_counter && timer_period >= 3 ) + delay = maintain_phase( time, end_time, timer_period ) - end_time; + return; + } + + output->set_modified(); + + // to do: track phase when period < 3 + // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks. + + int delta = update_amp( calc_amp() ); + if ( delta ) + synth.offset( time, delta, output ); + + time += delay; + if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) + { + time = end_time; + } + else if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + + int phase = this->phase; + int volume = 1; + if ( phase > phase_range ) { + phase -= phase_range; + volume = -volume; + } + + do { + if ( --phase == 0 ) { + phase = phase_range; + volume = -volume; + } + else { + synth.offset_inline( time, volume, output ); + } + + time += timer_period; + } + while ( time < end_time ); + + if ( volume < 0 ) + phase += phase_range; + this->phase = phase; + last_amp = calc_amp(); + } + delay = time - end_time; +} + +// Nes_Dmc + +void Nes_Dmc::reset() +{ + address = 0; + dac = 0; + buf = 0; + bits_remain = 1; + bits = 0; + buf_full = false; + silence = true; + next_irq = Nes_Apu::no_irq; + irq_flag = false; + irq_enabled = false; + + Nes_Osc::reset(); + period = 0x1AC; +} + +void Nes_Dmc::recalc_irq() +{ + nes_time_t irq = Nes_Apu::no_irq; + if ( irq_enabled && length_counter ) + irq = apu->last_dmc_time + delay + + ((length_counter - 1) * 8 + bits_remain - 1) * nes_time_t (period) + 1; + if ( irq != next_irq ) { + next_irq = irq; + apu->irq_changed(); + } +} + +int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const +{ + if ( last_read ) + *last_read = time; + + if ( length_counter == 0 ) + return 0; // not reading + + nes_time_t first_read = next_read_time(); + nes_time_t avail = time - first_read; + if ( avail <= 0 ) + return 0; + + int count = (avail - 1) / (period * 8) + 1; + if ( !(regs [0] & loop_flag) && count > length_counter ) + count = length_counter; + + if ( last_read ) + { + *last_read = first_read + (count - 1) * (period * 8) + 1; + check( *last_read <= time ); + check( count == count_reads( *last_read, NULL ) ); + check( count - 1 == count_reads( *last_read - 1, NULL ) ); + } + + return count; +} + +static short const dmc_period_table [2] [16] = { + {428, 380, 340, 320, 286, 254, 226, 214, // NTSC + 190, 160, 142, 128, 106, 84, 72, 54}, + + {398, 354, 316, 298, 276, 236, 210, 198, // PAL + 176, 148, 132, 118, 98, 78, 66, 50} +}; + +inline void Nes_Dmc::reload_sample() +{ + address = 0x4000 + regs [2] * 0x40; + length_counter = regs [3] * 0x10 + 1; +} + +static byte const dac_table [128] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14, + 15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27, + 27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38, + 39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49, + 50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59, + 59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67, + 68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75, + 76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83, +}; + +void Nes_Dmc::write_register( int addr, int data ) +{ + if ( addr == 0 ) + { + period = dmc_period_table [pal_mode] [data & 15]; + irq_enabled = (data & 0xC0) == 0x80; // enabled only if loop disabled + irq_flag &= irq_enabled; + recalc_irq(); + } + else if ( addr == 1 ) + { + int old_dac = dac; + dac = data & 0x7F; + + // adjust last_amp so that "pop" amplitude will be properly non-linear + // with respect to change in dac + int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]); + if ( !nonlinear ) + last_amp = faked_nonlinear; + } +} + +void Nes_Dmc::start() +{ + reload_sample(); + fill_buffer(); + recalc_irq(); +} + +void Nes_Dmc::fill_buffer() +{ + if ( !buf_full && length_counter ) + { + require( prg_reader ); // prg_reader must be set + buf = prg_reader( prg_reader_data, 0x8000u + address ); + address = (address + 1) & 0x7FFF; + buf_full = true; + if ( --length_counter == 0 ) + { + if ( regs [0] & loop_flag ) { + reload_sample(); + } + else { + apu->osc_enables &= ~0x10; + irq_flag = irq_enabled; + next_irq = Nes_Apu::no_irq; + apu->irq_changed(); + } + } + } +} + +void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) +{ + int delta = update_amp( dac ); + if ( !output ) + { + silence = true; + } + else + { + output->set_modified(); + if ( delta ) + synth.offset( time, delta, output ); + } + + time += delay; + if ( time < end_time ) + { + int bits_remain = this->bits_remain; + if ( silence && !buf_full ) + { + int count = (end_time - time + period - 1) / period; + bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1; + time += count * period; + } + else + { + Blip_Buffer* const output = this->output; + const int period = this->period; + int bits = this->bits; + int dac = this->dac; + + do + { + if ( !silence ) + { + int step = (bits & 1) * 4 - 2; + bits >>= 1; + if ( unsigned (dac + step) <= 0x7F ) { + dac += step; + synth.offset_inline( time, step, output ); + } + } + + time += period; + + if ( --bits_remain == 0 ) + { + bits_remain = 8; + if ( !buf_full ) { + silence = true; + } + else { + silence = false; + bits = buf; + buf_full = false; + if ( !output ) + silence = true; + fill_buffer(); + } + } + } + while ( time < end_time ); + + this->dac = dac; + this->last_amp = dac; + this->bits = bits; + } + this->bits_remain = bits_remain; + } + delay = time - end_time; +} + +// Nes_Noise + +static short const noise_period_table [16] = { + 0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0, + 0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4 +}; + +void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) +{ + int period = noise_period_table [regs [2] & 15]; + + if ( !output ) + { + // TODO: clean up + time += delay; + delay = time + (end_time - time + period - 1) / period * period - end_time; + return; + } + + output->set_modified(); + + const int volume = this->volume(); + int amp = (noise & 1) ? volume : 0; + { + int delta = update_amp( amp ); + if ( delta ) + synth.offset( time, delta, output ); + } + + time += delay; + if ( time < end_time ) + { + const int mode_flag = 0x80; + + if ( !volume ) + { + // round to next multiple of period + time += (end_time - time + period - 1) / period * period; + + // approximate noise cycling while muted, by shuffling up noise register + // to do: precise muted noise cycling? + if ( !(regs [2] & mode_flag) ) { + int feedback = (noise << 13) ^ (noise << 14); + noise = (feedback & 0x4000) | (noise >> 1); + } + } + else + { + Blip_Buffer* const output = this->output; + + // using resampled time avoids conversion in synth.offset() + blip_resampled_time_t rperiod = output->resampled_duration( period ); + blip_resampled_time_t rtime = output->resampled_time( time ); + + int noise = this->noise; + int delta = amp * 2 - volume; + const int tap = (regs [2] & mode_flag ? 8 : 13); + + do { + int feedback = (noise << tap) ^ (noise << 14); + time += period; + + if ( (noise + 1) & 2 ) { + // bits 0 and 1 of noise differ + delta = -delta; + synth.offset_resampled( rtime, delta, output ); + } + + rtime += rperiod; + noise = (feedback & 0x4000) | (noise >> 1); + } + while ( time < end_time ); + + last_amp = (delta + volume) >> 1; + this->noise = noise; + } + } + + delay = time - end_time; +} + diff --git a/thirdparty/game-music-emu/gme/Nes_Oscs.h b/thirdparty/game-music-emu/gme/Nes_Oscs.h new file mode 100644 index 000000000..b675bfb47 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Oscs.h @@ -0,0 +1,147 @@ +// Private oscillators used by Nes_Apu + +// Nes_Snd_Emu 0.1.8 +#ifndef NES_OSCS_H +#define NES_OSCS_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Nes_Apu; + +struct Nes_Osc +{ + unsigned char regs [4]; + bool reg_written [4]; + Blip_Buffer* output; + int length_counter;// length counter (0 if unused by oscillator) + int delay; // delay until next (potential) transition + int last_amp; // last amplitude oscillator was outputting + + void clock_length( int halt_mask ); + int period() const { + return (regs [3] & 7) * 0x100 + (regs [2] & 0xFF); + } + void reset() { + delay = 0; + last_amp = 0; + } + int update_amp( int amp ) { + int delta = amp - last_amp; + last_amp = amp; + return delta; + } +}; + +struct Nes_Envelope : Nes_Osc +{ + int envelope; + int env_delay; + + void clock_envelope(); + int volume() const; + void reset() { + envelope = 0; + env_delay = 0; + Nes_Osc::reset(); + } +}; + +// Nes_Square +struct Nes_Square : Nes_Envelope +{ + enum { negate_flag = 0x08 }; + enum { shift_mask = 0x07 }; + enum { phase_range = 8 }; + int phase; + int sweep_delay; + + typedef Blip_Synth Synth; + Synth const& synth; // shared between squares + + Nes_Square( Synth const* s ) : synth( *s ) { } + + void clock_sweep( int adjust ); + void run( nes_time_t, nes_time_t ); + void reset() { + sweep_delay = 0; + Nes_Envelope::reset(); + } + nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time, + nes_time_t timer_period ); +}; + +// Nes_Triangle +struct Nes_Triangle : Nes_Osc +{ + enum { phase_range = 16 }; + int phase; + int linear_counter; + Blip_Synth synth; + + int calc_amp() const; + void run( nes_time_t, nes_time_t ); + void clock_linear_counter(); + void reset() { + linear_counter = 0; + phase = 1; + Nes_Osc::reset(); + } + nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time, + nes_time_t timer_period ); +}; + +// Nes_Noise +struct Nes_Noise : Nes_Envelope +{ + int noise; + Blip_Synth synth; + + void run( nes_time_t, nes_time_t ); + void reset() { + noise = 1 << 14; + Nes_Envelope::reset(); + } +}; + +// Nes_Dmc +struct Nes_Dmc : Nes_Osc +{ + int address; // address of next byte to read + int period; + //int length_counter; // bytes remaining to play (already defined in Nes_Osc) + int buf; + int bits_remain; + int bits; + bool buf_full; + bool silence; + + enum { loop_flag = 0x40 }; + + int dac; + + nes_time_t next_irq; + bool irq_enabled; + bool irq_flag; + bool pal_mode; + bool nonlinear; + + int (*prg_reader)( void*, nes_addr_t ); // needs to be initialized to prg read function + void* prg_reader_data; + + Nes_Apu* apu; + + Blip_Synth synth; + + void start(); + void write_register( int, int ); + void run( nes_time_t, nes_time_t ); + void recalc_irq(); + void fill_buffer(); + void reload_sample(); + void reset(); + int count_reads( nes_time_t, nes_time_t* ) const; + nes_time_t next_read_time() const; +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Nes_Vrc6_Apu.cpp b/thirdparty/game-music-emu/gme/Nes_Vrc6_Apu.cpp new file mode 100644 index 000000000..d178407c3 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Vrc6_Apu.cpp @@ -0,0 +1,215 @@ +// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ + +#include "Nes_Vrc6_Apu.h" + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Nes_Vrc6_Apu::Nes_Vrc6_Apu() +{ + output( NULL ); + volume( 1.0 ); + reset(); +} + +void Nes_Vrc6_Apu::reset() +{ + last_time = 0; + for ( int i = 0; i < osc_count; i++ ) + { + Vrc6_Osc& osc = oscs [i]; + for ( int j = 0; j < reg_count; j++ ) + osc.regs [j] = 0; + osc.delay = 0; + osc.last_amp = 0; + osc.phase = 1; + osc.amp = 0; + } +} + +void Nes_Vrc6_Apu::output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, buf ); +} + +void Nes_Vrc6_Apu::run_until( blip_time_t time ) +{ + require( time >= last_time ); + run_square( oscs [0], time ); + run_square( oscs [1], time ); + run_saw( time ); + last_time = time; +} + +void Nes_Vrc6_Apu::write_osc( blip_time_t time, int osc_index, int reg, int data ) +{ + require( (unsigned) osc_index < osc_count ); + require( (unsigned) reg < reg_count ); + + run_until( time ); + oscs [osc_index].regs [reg] = data; +} + +void Nes_Vrc6_Apu::end_frame( blip_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +void Nes_Vrc6_Apu::save_state( vrc6_apu_state_t* out ) const +{ + assert( sizeof (vrc6_apu_state_t) == 20 ); + out->saw_amp = oscs [2].amp; + for ( int i = 0; i < osc_count; i++ ) + { + Vrc6_Osc const& osc = oscs [i]; + for ( int r = 0; r < reg_count; r++ ) + out->regs [i] [r] = osc.regs [r]; + + out->delays [i] = osc.delay; + out->phases [i] = osc.phase; + } +} + +void Nes_Vrc6_Apu::load_state( vrc6_apu_state_t const& in ) +{ + reset(); + oscs [2].amp = in.saw_amp; + for ( int i = 0; i < osc_count; i++ ) + { + Vrc6_Osc& osc = oscs [i]; + for ( int r = 0; r < reg_count; r++ ) + osc.regs [r] = in.regs [i] [r]; + + osc.delay = in.delays [i]; + osc.phase = in.phases [i]; + } + if ( !oscs [2].phase ) + oscs [2].phase = 1; +} + +void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) +{ + Blip_Buffer* output = osc.output; + if ( !output ) + return; + output->set_modified(); + + int volume = osc.regs [0] & 15; + if ( !(osc.regs [2] & 0x80) ) + volume = 0; + + int gate = osc.regs [0] & 0x80; + int duty = ((osc.regs [0] >> 4) & 7) + 1; + int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp; + blip_time_t time = last_time; + if ( delta ) + { + osc.last_amp += delta; + square_synth.offset( time, delta, output ); + } + + time += osc.delay; + osc.delay = 0; + int period = osc.period(); + if ( volume && !gate && period > 4 ) + { + if ( time < end_time ) + { + int phase = osc.phase; + + do + { + phase++; + if ( phase == 16 ) + { + phase = 0; + osc.last_amp = volume; + square_synth.offset( time, volume, output ); + } + if ( phase == duty ) + { + osc.last_amp = 0; + square_synth.offset( time, -volume, output ); + } + time += period; + } + while ( time < end_time ); + + osc.phase = phase; + } + osc.delay = time - end_time; + } +} + +void Nes_Vrc6_Apu::run_saw( blip_time_t end_time ) +{ + Vrc6_Osc& osc = oscs [2]; + Blip_Buffer* output = osc.output; + if ( !output ) + return; + output->set_modified(); + + int amp = osc.amp; + int amp_step = osc.regs [0] & 0x3F; + blip_time_t time = last_time; + int last_amp = osc.last_amp; + if ( !(osc.regs [2] & 0x80) || !(amp_step | amp) ) + { + osc.delay = 0; + int delta = (amp >> 3) - last_amp; + last_amp = amp >> 3; + saw_synth.offset( time, delta, output ); + } + else + { + time += osc.delay; + if ( time < end_time ) + { + int period = osc.period() * 2; + int phase = osc.phase; + + do + { + if ( --phase == 0 ) + { + phase = 7; + amp = 0; + } + + int delta = (amp >> 3) - last_amp; + if ( delta ) + { + last_amp = amp >> 3; + saw_synth.offset( time, delta, output ); + } + + time += period; + amp = (amp + amp_step) & 0xFF; + } + while ( time < end_time ); + + osc.phase = phase; + osc.amp = amp; + } + + osc.delay = time - end_time; + } + + osc.last_amp = last_amp; +} + diff --git a/thirdparty/game-music-emu/gme/Nes_Vrc6_Apu.h b/thirdparty/game-music-emu/gme/Nes_Vrc6_Apu.h new file mode 100644 index 000000000..23a6519fc --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nes_Vrc6_Apu.h @@ -0,0 +1,95 @@ +// Konami VRC6 sound chip emulator + +// Nes_Snd_Emu 0.1.8 +#ifndef NES_VRC6_APU_H +#define NES_VRC6_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct vrc6_apu_state_t; + +class Nes_Vrc6_Apu { +public: + // See Nes_Apu.h for reference + void reset(); + void volume( double ); + void treble_eq( blip_eq_t const& ); + void output( Blip_Buffer* ); + enum { osc_count = 3 }; + void osc_output( int index, Blip_Buffer* ); + void end_frame( blip_time_t ); + void save_state( vrc6_apu_state_t* ) const; + void load_state( vrc6_apu_state_t const& ); + + // Oscillator 0 write-only registers are at $9000-$9002 + // Oscillator 1 write-only registers are at $A000-$A002 + // Oscillator 2 write-only registers are at $B000-$B002 + enum { reg_count = 3 }; + enum { base_addr = 0x9000 }; + enum { addr_step = 0x1000 }; + void write_osc( blip_time_t, int osc, int reg, int data ); + +public: + Nes_Vrc6_Apu(); + BLARGG_DISABLE_NOTHROW +private: + // noncopyable + Nes_Vrc6_Apu( const Nes_Vrc6_Apu& ); + Nes_Vrc6_Apu& operator = ( const Nes_Vrc6_Apu& ); + + struct Vrc6_Osc + { + uint8_t regs [3]; + Blip_Buffer* output; + int delay; + int last_amp; + int phase; + int amp; // only used by saw + + int period() const + { + return (regs [2] & 0x0F) * 0x100L + regs [1] + 1; + } + }; + + Vrc6_Osc oscs [osc_count]; + blip_time_t last_time; + + Blip_Synth saw_synth; + Blip_Synth square_synth; + + void run_until( blip_time_t ); + void run_square( Vrc6_Osc& osc, blip_time_t ); + void run_saw( blip_time_t ); +}; + +struct vrc6_apu_state_t +{ + uint8_t regs [3] [3]; + uint8_t saw_amp; + uint16_t delays [3]; + uint8_t phases [3]; + uint8_t unused; +}; + +inline void Nes_Vrc6_Apu::osc_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Nes_Vrc6_Apu::volume( double v ) +{ + double const factor = 0.0967 * 2; + saw_synth.volume( factor / 31 * v ); + square_synth.volume( factor * 0.5 / 15 * v ); +} + +inline void Nes_Vrc6_Apu::treble_eq( blip_eq_t const& eq ) +{ + saw_synth.treble_eq( eq ); + square_synth.treble_eq( eq ); +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Nsf_Emu.cpp b/thirdparty/game-music-emu/gme/Nsf_Emu.cpp new file mode 100644 index 000000000..74d76850e --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nsf_Emu.cpp @@ -0,0 +1,561 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Nsf_Emu.h" + +#include "blargg_endian.h" +#include +#include + +#if !NSF_EMU_APU_ONLY + #include "Nes_Namco_Apu.h" + #include "Nes_Vrc6_Apu.h" + #include "Nes_Fme7_Apu.h" +#endif + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const vrc6_flag = 0x01; +int const namco_flag = 0x10; +int const fme7_flag = 0x20; + +long const clock_divisor = 12; + +Nsf_Emu::equalizer_t const Nsf_Emu::nes_eq = + Music_Emu::make_equalizer( -1.0, 80 ); +Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = + Music_Emu::make_equalizer( -15.0, 80 ); + +int Nsf_Emu::pcm_read( void* emu, nes_addr_t addr ) +{ + return *((Nsf_Emu*) emu)->cpu::get_code( addr ); +} + +Nsf_Emu::Nsf_Emu() +{ + vrc6 = 0; + namco = 0; + fme7 = 0; + + set_type( gme_nsf_type ); + set_silence_lookahead( 6 ); + apu.dmc_reader( pcm_read, this ); + Music_Emu::set_equalizer( nes_eq ); + set_gain( 1.4 ); + memset( unmapped_code, Nes_Cpu::bad_opcode, sizeof unmapped_code ); +} + +Nsf_Emu::~Nsf_Emu() { unload(); } + +void Nsf_Emu::unload() +{ + #if !NSF_EMU_APU_ONLY + { + delete vrc6; + vrc6 = 0; + + delete namco; + namco = 0; + + delete fme7; + fme7 = 0; + } + #endif + + rom.clear(); + Music_Emu::unload(); +} + +// Track info + +static void copy_nsf_fields( Nsf_Emu::header_t const& h, track_info_t* out ) +{ + GME_COPY_FIELD( h, out, game ); + GME_COPY_FIELD( h, out, author ); + GME_COPY_FIELD( h, out, copyright ); + if ( h.chip_flags ) + Gme_File::copy_field_( out->system, "Famicom" ); +} + +blargg_err_t Nsf_Emu::track_info_( track_info_t* out, int ) const +{ + copy_nsf_fields( header_, out ); + return 0; +} + +static blargg_err_t check_nsf_header( void const* header ) +{ + if ( memcmp( header, "NESM\x1A", 5 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Nsf_File : Gme_Info_ +{ + Nsf_Emu::header_t h; + + Nsf_File() { set_type( gme_nsf_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + blargg_err_t err = in.read( &h, Nsf_Emu::header_size ); + if ( err ) + return (err == in.eof_error ? gme_wrong_file_type : err); + + if ( h.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag) ) + set_warning( "Uses unsupported audio expansion hardware" ); + + set_track_count( h.track_count ); + return check_nsf_header( &h ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_nsf_fields( h, out ); + return 0; + } +}; + +static Music_Emu* new_nsf_emu () { return BLARGG_NEW Nsf_Emu ; } +static Music_Emu* new_nsf_file() { return BLARGG_NEW Nsf_File; } + +static gme_type_t_ const gme_nsf_type_ = { "Nintendo NES", 0, &new_nsf_emu, &new_nsf_file, "NSF", 1 }; +BLARGG_EXPORT extern gme_type_t const gme_nsf_type = &gme_nsf_type_; + + +// Setup + +void Nsf_Emu::set_tempo_( double t ) +{ + unsigned playback_rate = get_le16( header_.ntsc_speed ); + unsigned standard_rate = 0x411A; + clock_rate_ = 1789772.72727; + play_period = 262 * 341L * 4 - 2; // two fewer PPU clocks every four frames + + if ( pal_only ) + { + play_period = 33247 * clock_divisor; + clock_rate_ = 1662607.125; + standard_rate = 0x4E20; + playback_rate = get_le16( header_.pal_speed ); + } + + if ( !playback_rate ) + playback_rate = standard_rate; + + if ( playback_rate != standard_rate || t != 1.0 ) + play_period = long (playback_rate * clock_rate_ / (1000000.0 / clock_divisor * t)); + + apu.set_tempo( t ); +} + +blargg_err_t Nsf_Emu::init_sound() +{ + if ( header_.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag) ) + set_warning( "Uses unsupported audio expansion hardware" ); + + { + #define APU_NAMES "Square 1", "Square 2", "Triangle", "Noise", "DMC" + + int const count = Nes_Apu::osc_count; + static const char* const apu_names [count] = { APU_NAMES }; + set_voice_count( count ); + set_voice_names( apu_names ); + + } + + static int const types [] = { + wave_type | 1, wave_type | 2, wave_type | 0, + noise_type | 0, mixed_type | 1, + wave_type | 3, wave_type | 4, wave_type | 5, + wave_type | 6, wave_type | 7, wave_type | 8, wave_type | 9, + wave_type |10, wave_type |11, wave_type |12, wave_type |13 + }; + set_voice_types( types ); // common to all sound chip configurations + + double adjusted_gain = gain(); + + #if NSF_EMU_APU_ONLY + { + if ( header_.chip_flags ) + set_warning( "Uses unsupported audio expansion hardware" ); + } + #else + { + if ( header_.chip_flags & (namco_flag | vrc6_flag | fme7_flag) ) + set_voice_count( Nes_Apu::osc_count + 3 ); + + if ( header_.chip_flags & namco_flag ) + { + namco = BLARGG_NEW Nes_Namco_Apu; + CHECK_ALLOC( namco ); + adjusted_gain *= 0.75; + + int const count = Nes_Apu::osc_count + Nes_Namco_Apu::osc_count; + static const char* const names [count] = { + APU_NAMES, + "Wave 1", "Wave 2", "Wave 3", "Wave 4", + "Wave 5", "Wave 6", "Wave 7", "Wave 8" + }; + set_voice_count( count ); + set_voice_names( names ); + } + + if ( header_.chip_flags & vrc6_flag ) + { + vrc6 = BLARGG_NEW Nes_Vrc6_Apu; + CHECK_ALLOC( vrc6 ); + adjusted_gain *= 0.75; + + { + int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count; + static const char* const names [count] = { + APU_NAMES, + "Saw Wave", "Square 3", "Square 4" + }; + set_voice_count( count ); + set_voice_names( names ); + } + + if ( header_.chip_flags & namco_flag ) + { + int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count + + Nes_Namco_Apu::osc_count; + static const char* const names [count] = { + APU_NAMES, + "Saw Wave", "Square 3", "Square 4", + "Wave 1", "Wave 2", "Wave 3", "Wave 4", + "Wave 5", "Wave 6", "Wave 7", "Wave 8" + }; + set_voice_count( count ); + set_voice_names( names ); + } + } + + if ( header_.chip_flags & fme7_flag ) + { + fme7 = BLARGG_NEW Nes_Fme7_Apu; + CHECK_ALLOC( fme7 ); + adjusted_gain *= 0.75; + + int const count = Nes_Apu::osc_count + Nes_Fme7_Apu::osc_count; + static const char* const names [count] = { + APU_NAMES, + "Square 3", "Square 4", "Square 5" + }; + set_voice_count( count ); + set_voice_names( names ); + } + + if ( namco ) namco->volume( adjusted_gain ); + if ( vrc6 ) vrc6 ->volume( adjusted_gain ); + if ( fme7 ) fme7 ->volume( adjusted_gain ); + } + #endif + + apu.volume( adjusted_gain ); + + return 0; +} + +blargg_err_t Nsf_Emu::load_( Data_Reader& in ) +{ + assert( offsetof (header_t,unused [4]) == header_size ); + RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); + + set_track_count( header_.track_count ); + RETURN_ERR( check_nsf_header( &header_ ) ); + + if ( header_.vers != 1 ) + set_warning( "Unknown file version" ); + + // sound and memory + blargg_err_t err = init_sound(); + if ( err ) + return err; + + // set up data + nes_addr_t load_addr = get_le16( header_.load_addr ); + init_addr = get_le16( header_.init_addr ); + play_addr = get_le16( header_.play_addr ); + if ( !load_addr ) load_addr = rom_begin; + if ( !init_addr ) init_addr = rom_begin; + if ( !play_addr ) play_addr = rom_begin; + if ( load_addr < rom_begin || init_addr < rom_begin ) + { + const char* w = warning(); + if ( !w ) + w = "Corrupt file (invalid load/init/play address)"; + return w; + } + + rom.set_addr( load_addr % bank_size ); + int total_banks = rom.size() / bank_size; + + // bank switching + int first_bank = (load_addr - rom_begin) / bank_size; + for ( int i = 0; i < bank_count; i++ ) + { + unsigned bank = i - first_bank; + if ( bank >= (unsigned) total_banks ) + bank = 0; + initial_banks [i] = bank; + + if ( header_.banks [i] ) + { + // bank-switched + memcpy( initial_banks, header_.banks, sizeof initial_banks ); + break; + } + } + + pal_only = (header_.speed_flags & 3) == 1; + + #if !NSF_EMU_EXTRA_FLAGS + header_.speed_flags = 0; + #endif + + set_tempo( tempo() ); + + return setup_buffer( (long) (clock_rate_ + 0.5) ); +} + +void Nsf_Emu::update_eq( blip_eq_t const& eq ) +{ + apu.treble_eq( eq ); + + #if !NSF_EMU_APU_ONLY + { + if ( namco ) namco->treble_eq( eq ); + if ( vrc6 ) vrc6 ->treble_eq( eq ); + if ( fme7 ) fme7 ->treble_eq( eq ); + } + #endif +} + +void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* ) +{ + if ( i < Nes_Apu::osc_count ) + { + apu.osc_output( i, buf ); + return; + } + i -= Nes_Apu::osc_count; + + #if !NSF_EMU_APU_ONLY + { + if ( fme7 && i < Nes_Fme7_Apu::osc_count ) + { + fme7->osc_output( i, buf ); + return; + } + + if ( vrc6 ) + { + if ( i < Nes_Vrc6_Apu::osc_count ) + { + // put saw first + if ( --i < 0 ) + i = 2; + vrc6->osc_output( i, buf ); + return; + } + i -= Nes_Vrc6_Apu::osc_count; + } + + if ( namco && i < Nes_Namco_Apu::osc_count ) + { + namco->osc_output( i, buf ); + return; + } + } + #endif +} + +// Emulation + +// see nes_cpu_io.h for read/write functions + +void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) +{ + #if !NSF_EMU_APU_ONLY + { + if ( namco ) + { + switch ( addr ) + { + case Nes_Namco_Apu::data_reg_addr: + namco->write_data( time(), data ); + return; + + case Nes_Namco_Apu::addr_reg_addr: + namco->write_addr( data ); + return; + } + } + + if ( addr >= Nes_Fme7_Apu::latch_addr && fme7 ) + { + switch ( addr & Nes_Fme7_Apu::addr_mask ) + { + case Nes_Fme7_Apu::latch_addr: + fme7->write_latch( data ); + return; + + case Nes_Fme7_Apu::data_addr: + fme7->write_data( time(), data ); + return; + } + } + + if ( vrc6 ) + { + unsigned reg = addr & (Nes_Vrc6_Apu::addr_step - 1); + unsigned osc = unsigned (addr - Nes_Vrc6_Apu::base_addr) / Nes_Vrc6_Apu::addr_step; + if ( osc < Nes_Vrc6_Apu::osc_count && reg < Nes_Vrc6_Apu::reg_count ) + { + vrc6->write_osc( time(), osc, reg, data ); + return; + } + } + } + #endif + + // unmapped write + + #ifndef NDEBUG + { + // some games write to $8000 and $8001 repeatedly + if ( addr == 0x8000 || addr == 0x8001 ) return; + + // probably namco sound mistakenly turned on in mck + if ( addr == 0x4800 || addr == 0xF800 ) return; + + // memory mapper? + if ( addr == 0xFFF8 ) return; + + debug_printf( "write_unmapped( 0x%04X, 0x%02X )\n", (unsigned) addr, (unsigned) data ); + } + #endif +} + +blargg_err_t Nsf_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + memset( low_mem, 0, sizeof low_mem ); + memset( sram, 0, sizeof sram ); + + cpu::reset( unmapped_code ); // also maps low_mem + cpu::map_code( sram_addr, sizeof sram, sram ); + for ( int i = 0; i < bank_count; ++i ) + cpu_write( bank_select_addr + i, initial_banks [i] ); + + apu.reset( pal_only, (header_.speed_flags & 0x20) ? 0x3F : 0 ); + apu.write_register( 0, 0x4015, 0x0F ); + apu.write_register( 0, 0x4017, (header_.speed_flags & 0x10) ? 0x80 : 0 ); + #if !NSF_EMU_APU_ONLY + { + if ( namco ) namco->reset(); + if ( vrc6 ) vrc6 ->reset(); + if ( fme7 ) fme7 ->reset(); + } + #endif + + play_ready = 4; + play_extra = 0; + next_play = play_period / clock_divisor; + + saved_state.pc = badop_addr; + low_mem [0x1FF] = (badop_addr - 1) >> 8; + low_mem [0x1FE] = (badop_addr - 1) & 0xFF; + r.sp = 0xFD; + r.pc = init_addr; + r.a = track; + r.x = pal_only; + + return 0; +} + +blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int ) +{ + set_time( 0 ); + while ( time() < duration ) + { + nes_time_t end = min( (blip_time_t) next_play, duration ); + end = min( end, time() + 32767 ); // allows CPU to use 16-bit time delta + if ( cpu::run( end ) ) + { + if ( r.pc != badop_addr ) + { + set_warning( "Emulation error (illegal instruction)" ); + r.pc++; + } + else + { + play_ready = 1; + if ( saved_state.pc != badop_addr ) + { + cpu::r = saved_state; + saved_state.pc = badop_addr; + } + else + { + set_time( end ); + } + } + } + + if ( time() >= next_play ) + { + nes_time_t period = (play_period + play_extra) / clock_divisor; + play_extra = play_period - period * clock_divisor; + next_play += period; + if ( play_ready && !--play_ready ) + { + check( saved_state.pc == badop_addr ); + if ( r.pc != badop_addr ) + saved_state = cpu::r; + + r.pc = play_addr; + low_mem [0x100 + r.sp--] = (badop_addr - 1) >> 8; + low_mem [0x100 + r.sp--] = (badop_addr - 1) & 0xFF; + GME_FRAME_HOOK( this ); + } + } + } + + if ( cpu::error_count() ) + { + cpu::clear_error_count(); + set_warning( "Emulation error (illegal instruction)" ); + } + + duration = time(); + next_play -= duration; + check( next_play >= 0 ); + if ( next_play < 0 ) + next_play = 0; + + apu.end_frame( duration ); + + #if !NSF_EMU_APU_ONLY + { + if ( namco ) namco->end_frame( duration ); + if ( vrc6 ) vrc6 ->end_frame( duration ); + if ( fme7 ) fme7 ->end_frame( duration ); + } + #endif + + return 0; +} diff --git a/thirdparty/game-music-emu/gme/Nsf_Emu.h b/thirdparty/game-music-emu/gme/Nsf_Emu.h new file mode 100644 index 000000000..e538b1b30 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nsf_Emu.h @@ -0,0 +1,106 @@ +// Nintendo NES/Famicom NSF music file emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef NSF_EMU_H +#define NSF_EMU_H + +#include "Classic_Emu.h" +#include "Nes_Apu.h" +#include "Nes_Cpu.h" + +class Nsf_Emu : private Nes_Cpu, public Classic_Emu { + typedef Nes_Cpu cpu; +public: + // Equalizer profiles for US NES and Japanese Famicom + static equalizer_t const nes_eq; + static equalizer_t const famicom_eq; + + // NSF file header + enum { header_size = 0x80 }; + struct header_t + { + char tag [5]; + byte vers; + byte track_count; + byte first_track; + byte load_addr [2]; + byte init_addr [2]; + byte play_addr [2]; + char game [32]; + char author [32]; + char copyright [32]; + byte ntsc_speed [2]; + byte banks [8]; + byte pal_speed [2]; + byte speed_flags; + byte chip_flags; + byte unused [4]; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + static gme_type_t static_type() { return gme_nsf_type; } + +public: + // deprecated + using Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + +public: + Nsf_Emu(); + ~Nsf_Emu(); + Nes_Apu* apu_() { return &apu; } +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_( Data_Reader& ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); + void unload(); +protected: + enum { bank_count = 8 }; + byte initial_banks [bank_count]; + nes_addr_t init_addr; + nes_addr_t play_addr; + double clock_rate_; + bool pal_only; + + // timing + Nes_Cpu::registers_t saved_state; + nes_time_t next_play; + nes_time_t play_period; + int play_extra; + int play_ready; + + enum { rom_begin = 0x8000 }; + enum { bank_select_addr = 0x5FF8 }; + enum { bank_size = 0x1000 }; + Rom_Data rom; + +public: private: friend class Nes_Cpu; + void cpu_jsr( nes_addr_t ); + int cpu_read( nes_addr_t ); + void cpu_write( nes_addr_t, int ); + void cpu_write_misc( nes_addr_t, int ); + enum { badop_addr = bank_select_addr }; + +private: + class Nes_Namco_Apu* namco; + class Nes_Vrc6_Apu* vrc6; + class Nes_Fme7_Apu* fme7; + Nes_Apu apu; + static int pcm_read( void*, nes_addr_t ); + blargg_err_t init_sound(); + + header_t header_; + + enum { sram_addr = 0x6000 }; + byte sram [0x2000]; + byte unmapped_code [Nes_Cpu::page_size + 8]; +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Nsfe_Emu.cpp b/thirdparty/game-music-emu/gme/Nsfe_Emu.cpp new file mode 100644 index 000000000..035f99dee --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nsfe_Emu.cpp @@ -0,0 +1,335 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Nsfe_Emu.h" + +#include "blargg_endian.h" +#include +#include + +/* Copyright (C) 2005-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Nsfe_Info::Nsfe_Info() { playlist_disabled = false; } + +Nsfe_Info::~Nsfe_Info() { } + +inline void Nsfe_Info::unload() +{ + track_name_data.clear(); + track_names.clear(); + playlist.clear(); + track_times.clear(); +} + +// TODO: if no playlist, treat as if there is a playlist that is just 1,2,3,4,5... ? +void Nsfe_Info::disable_playlist( bool b ) +{ + playlist_disabled = b; + info.track_count = playlist.size(); + if ( !info.track_count || playlist_disabled ) + info.track_count = actual_track_count_; +} + +int Nsfe_Info::remap_track( int track ) const +{ + if ( !playlist_disabled && (unsigned) track < playlist.size() ) + track = playlist [track]; + return track; +} + +// Read multiple strings and separate into individual strings +static blargg_err_t read_strs( Data_Reader& in, long size, blargg_vector& chars, + blargg_vector& strs ) +{ + RETURN_ERR( chars.resize( size + 1 ) ); + chars [size] = 0; // in case last string doesn't have terminator + RETURN_ERR( in.read( &chars [0], size ) ); + + RETURN_ERR( strs.resize( 128 ) ); + int count = 0; + for ( int i = 0; i < size; i++ ) + { + if ( (int) strs.size() <= count ) + RETURN_ERR( strs.resize( count * 2 ) ); + strs [count++] = &chars [i]; + while ( i < size && chars [i] ) + i++; + } + + return strs.resize( count ); +} + +// Copy in to out, where out has out_max characters allocated. Truncate to +// out_max - 1 characters. +static void copy_str( const char* in, char* out, int out_max ) +{ + out [out_max - 1] = 0; + strncpy( out, in, out_max - 1 ); +} + +struct nsfe_info_t +{ + byte load_addr [2]; + byte init_addr [2]; + byte play_addr [2]; + byte speed_flags; + byte chip_flags; + byte track_count; + byte first_track; + byte unused [6]; +}; + +blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) +{ + int const nsfe_info_size = 16; + assert( offsetof (nsfe_info_t,unused [6]) == nsfe_info_size ); + + // check header + byte signature [4]; + blargg_err_t err = in.read( signature, sizeof signature ); + if ( err ) + return (err == in.eof_error ? gme_wrong_file_type : err); + if ( memcmp( signature, "NSFE", 4 ) ) + return gme_wrong_file_type; + + // free previous info + track_name_data.clear(); + track_names.clear(); + playlist.clear(); + track_times.clear(); + + // default nsf header + static const Nsf_Emu::header_t base_header = + { + {'N','E','S','M','\x1A'},// tag + 1, // version + 1, 1, // track count, first track + {0,0},{0,0},{0,0}, // addresses + "","","", // strings + {0x1A, 0x41}, // NTSC rate + {0,0,0,0,0,0,0,0}, // banks + {0x20, 0x4E}, // PAL rate + 0, 0, // flags + {0,0,0,0} // unused + }; + Nsf_Emu::header_t& header = info; + header = base_header; + + // parse tags + int phase = 0; + while ( phase != 3 ) + { + // read size and tag + byte block_header [2] [4]; + RETURN_ERR( in.read( block_header, sizeof block_header ) ); + blargg_long size = get_le32( block_header [0] ); + blargg_long tag = get_le32( block_header [1] ); + + if ( size < 0 ) + return "Corrupt file"; + + //debug_printf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) ); + + switch ( tag ) + { + case BLARGG_4CHAR('O','F','N','I'): { + check( phase == 0 ); + if ( size < 8 ) + return "Corrupt file"; + + nsfe_info_t finfo; + finfo.track_count = 1; + finfo.first_track = 0; + + RETURN_ERR( in.read( &finfo, min( size, (blargg_long) nsfe_info_size ) ) ); + if ( size > nsfe_info_size ) + RETURN_ERR( in.skip( size - nsfe_info_size ) ); + phase = 1; + info.speed_flags = finfo.speed_flags; + info.chip_flags = finfo.chip_flags; + info.track_count = finfo.track_count; + this->actual_track_count_ = finfo.track_count; + info.first_track = finfo.first_track; + memcpy( info.load_addr, finfo.load_addr, 2 * 3 ); + break; + } + + case BLARGG_4CHAR('K','N','A','B'): + if ( size > (int) sizeof info.banks ) + return "Corrupt file"; + RETURN_ERR( in.read( info.banks, size ) ); + break; + + case BLARGG_4CHAR('h','t','u','a'): { + blargg_vector chars; + blargg_vector strs; + RETURN_ERR( read_strs( in, size, chars, strs ) ); + int n = strs.size(); + + if ( n > 3 ) + copy_str( strs [3], info.dumper, sizeof info.dumper ); + + if ( n > 2 ) + copy_str( strs [2], info.copyright, sizeof info.copyright ); + + if ( n > 1 ) + copy_str( strs [1], info.author, sizeof info.author ); + + if ( n > 0 ) + copy_str( strs [0], info.game, sizeof info.game ); + + break; + } + + case BLARGG_4CHAR('e','m','i','t'): + RETURN_ERR( track_times.resize( size / 4 ) ); + RETURN_ERR( in.read( track_times.begin(), track_times.size() * 4 ) ); + break; + + case BLARGG_4CHAR('l','b','l','t'): + RETURN_ERR( read_strs( in, size, track_name_data, track_names ) ); + break; + + case BLARGG_4CHAR('t','s','l','p'): + RETURN_ERR( playlist.resize( size ) ); + RETURN_ERR( in.read( &playlist [0], size ) ); + break; + + case BLARGG_4CHAR('A','T','A','D'): { + check( phase == 1 ); + phase = 2; + if ( !nsf_emu ) + { + RETURN_ERR( in.skip( size ) ); + } + else + { + Subset_Reader sub( &in, size ); // limit emu to nsf data + Remaining_Reader rem( &header, Nsf_Emu::header_size, &sub ); + RETURN_ERR( nsf_emu->load( rem ) ); + check( rem.remain() == 0 ); + } + break; + } + + case BLARGG_4CHAR('D','N','E','N'): + check( phase == 2 ); + phase = 3; + break; + + default: + // tags that can be skipped start with a lowercase character + check( islower( (tag >> 24) & 0xFF ) ); + RETURN_ERR( in.skip( size ) ); + break; + } + } + + return 0; +} + +blargg_err_t Nsfe_Info::track_info_( track_info_t* out, int track ) const +{ + int remapped = remap_track( track ); + if ( (unsigned) remapped < track_times.size() ) + { + long length = (int32_t) get_le32( track_times [remapped] ); + if ( length > 0 ) + out->length = length; + } + if ( (unsigned) remapped < track_names.size() ) + Gme_File::copy_field_( out->song, track_names [remapped] ); + + GME_COPY_FIELD( info, out, game ); + GME_COPY_FIELD( info, out, author ); + GME_COPY_FIELD( info, out, copyright ); + GME_COPY_FIELD( info, out, dumper ); + return 0; +} + +Nsfe_Emu::Nsfe_Emu() +{ + loading = false; + set_type( gme_nsfe_type ); +} + +Nsfe_Emu::~Nsfe_Emu() { } + +void Nsfe_Emu::unload() +{ + if ( !loading ) + info.unload(); // TODO: extremely hacky! + Nsf_Emu::unload(); +} + +blargg_err_t Nsfe_Emu::track_info_( track_info_t* out, int track ) const +{ + return info.track_info_( out, track ); +} + +struct Nsfe_File : Gme_Info_ +{ + Nsfe_Info info; + + Nsfe_File() { set_type( gme_nsfe_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + RETURN_ERR( info.load( in, 0 ) ); + info.disable_playlist( false ); + set_track_count( info.info.track_count ); + return 0; + } + + blargg_err_t track_info_( track_info_t* out, int track ) const + { + return info.track_info_( out, track ); + } +}; + +static Music_Emu* new_nsfe_emu () { return BLARGG_NEW Nsfe_Emu ; } +static Music_Emu* new_nsfe_file() { return BLARGG_NEW Nsfe_File; } + +static gme_type_t_ const gme_nsfe_type_ = { "Nintendo NES", 0, &new_nsfe_emu, &new_nsfe_file, "NSFE", 1 }; +BLARGG_EXPORT extern gme_type_t const gme_nsfe_type = &gme_nsfe_type_; + + +blargg_err_t Nsfe_Emu::load_( Data_Reader& in ) +{ + if ( loading ) + return Nsf_Emu::load_( in ); + + // TODO: this hacky recursion-avoidance could have subtle problems + loading = true; + blargg_err_t err = info.load( in, this ); + loading = false; + disable_playlist( false ); + return err; +} + +void Nsfe_Emu::disable_playlist( bool b ) +{ + info.disable_playlist( b ); + set_track_count( info.info.track_count ); +} + +void Nsfe_Emu::clear_playlist_() +{ + disable_playlist(); + Nsf_Emu::clear_playlist_(); +} + +blargg_err_t Nsfe_Emu::start_track_( int track ) +{ + return Nsf_Emu::start_track_( info.remap_track( track ) ); +} diff --git a/thirdparty/game-music-emu/gme/Nsfe_Emu.h b/thirdparty/game-music-emu/gme/Nsfe_Emu.h new file mode 100644 index 000000000..fd65f0af8 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Nsfe_Emu.h @@ -0,0 +1,68 @@ +// Nintendo NES/Famicom NSFE music file emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef NSFE_EMU_H +#define NSFE_EMU_H + +#include "blargg_common.h" +#include "Nsf_Emu.h" + +// Allows reading info from NSFE file without creating emulator +class Nsfe_Info { +public: + blargg_err_t load( Data_Reader&, Nsf_Emu* ); + + struct info_t : Nsf_Emu::header_t + { + char game [256]; + char author [256]; + char copyright [256]; + char dumper [256]; + } info; + + void disable_playlist( bool = true ); + + blargg_err_t track_info_( track_info_t* out, int track ) const; + + int remap_track( int i ) const; + + void unload(); + + Nsfe_Info(); + ~Nsfe_Info(); +private: + blargg_vector track_name_data; + blargg_vector track_names; + blargg_vector playlist; + blargg_vector track_times; + int actual_track_count_; + bool playlist_disabled; +}; + +class Nsfe_Emu : public Nsf_Emu { +public: + static gme_type_t static_type() { return gme_nsfe_type; } + +public: + // deprecated + struct header_t { char tag [4]; }; + using Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + void disable_playlist( bool = true ); // use clear_playlist() + +public: + Nsfe_Emu(); + ~Nsfe_Emu(); +protected: + blargg_err_t load_( Data_Reader& ); + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t start_track_( int ); + void unload(); + void clear_playlist_(); +private: + Nsfe_Info info; + bool loading; +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Sap_Apu.cpp b/thirdparty/game-music-emu/gme/Sap_Apu.cpp new file mode 100644 index 000000000..26fa2d13f --- /dev/null +++ b/thirdparty/game-music-emu/gme/Sap_Apu.cpp @@ -0,0 +1,334 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Sap_Apu.h" + +#include + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const max_frequency = 12000; // pure waves above this frequency are silenced + +static void gen_poly( blargg_ulong mask, int count, byte* out ) +{ + blargg_ulong n = 1; + do + { + int bits = 0; + int b = 0; + do + { + // implemented using "Galios configuration" + bits |= (n & 1) << b; + n = (n >> 1) ^ (mask & -(n & 1)); + } + while ( b++ < 7 ); + *out++ = bits; + } + while ( --count ); +} + +// poly5 +int const poly5_len = (1 << 5) - 1; +blargg_ulong const poly5_mask = (1UL << poly5_len) - 1; +blargg_ulong const poly5 = 0x167C6EA1; + +inline blargg_ulong run_poly5( blargg_ulong in, int shift ) +{ + return (in << shift & poly5_mask) | (in >> (poly5_len - shift)); +} + +#define POLY_MASK( width, tap1, tap2 ) \ + ((1UL << (width - 1 - tap1)) | (1UL << (width - 1 - tap2))) + +Sap_Apu_Impl::Sap_Apu_Impl() +{ + gen_poly( POLY_MASK( 4, 1, 0 ), sizeof poly4, poly4 ); + gen_poly( POLY_MASK( 9, 5, 0 ), sizeof poly9, poly9 ); + gen_poly( POLY_MASK( 17, 5, 0 ), sizeof poly17, poly17 ); + + if ( 0 ) // comment out to recauculate poly5 constant + { + byte poly5 [4]; + gen_poly( POLY_MASK( 5, 2, 0 ), sizeof poly5, poly5 ); + blargg_ulong n = poly5 [3] * 0x1000000L + poly5 [2] * 0x10000L + + poly5 [1] * 0x100L + poly5 [0]; + blargg_ulong rev = n & 1; + for ( int i = 1; i < poly5_len; i++ ) + rev |= (n >> i & 1) << (poly5_len - i); + debug_printf( "poly5: 0x%08lX\n", rev ); + } +} + +Sap_Apu::Sap_Apu() +{ + impl = 0; + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, 0 ); +} + +void Sap_Apu::reset( Sap_Apu_Impl* new_impl ) +{ + impl = new_impl; + last_time = 0; + poly5_pos = 0; + poly4_pos = 0; + polym_pos = 0; + control = 0; + + for ( int i = 0; i < osc_count; i++ ) + memset( &oscs [i], 0, offsetof (osc_t,output) ); +} + +inline void Sap_Apu::calc_periods() +{ + // 15/64 kHz clock + int divider = 28; + if ( this->control & 1 ) + divider = 114; + + for ( int i = 0; i < osc_count; i++ ) + { + osc_t* const osc = &oscs [i]; + + int const osc_reload = osc->regs [0]; // cache + blargg_long period = (osc_reload + 1) * divider; + static byte const fast_bits [osc_count] = { 1 << 6, 1 << 4, 1 << 5, 1 << 3 }; + if ( this->control & fast_bits [i] ) + { + period = osc_reload + 4; + if ( i & 1 ) + { + period = osc_reload * 0x100L + osc [-1].regs [0] + 7; + if ( !(this->control & fast_bits [i - 1]) ) + period = (period - 6) * divider; + + if ( (osc [-1].regs [1] & 0x1F) > 0x10 ) + debug_printf( "Use of slave channel in 16-bit mode not supported\n" ); + } + } + osc->period = period; + } +} + +void Sap_Apu::run_until( blip_time_t end_time ) +{ + calc_periods(); + Sap_Apu_Impl* const impl = this->impl; // cache + + // 17/9-bit poly selection + byte const* polym = impl->poly17; + int polym_len = poly17_len; + if ( this->control & 0x80 ) + { + polym_len = poly9_len; + polym = impl->poly9; + } + polym_pos %= polym_len; + + for ( int i = 0; i < osc_count; i++ ) + { + osc_t* const osc = &oscs [i]; + blip_time_t time = last_time + osc->delay; + blip_time_t const period = osc->period; + + // output + Blip_Buffer* output = osc->output; + if ( output ) + { + output->set_modified(); + + int const osc_control = osc->regs [1]; // cache + int volume = (osc_control & 0x0F) * 2; + if ( !volume || osc_control & 0x10 || // silent, DAC mode, or inaudible frequency + ((osc_control & 0xA0) == 0xA0 && period < 1789773 / 2 / max_frequency) ) + { + if ( !(osc_control & 0x10) ) + volume >>= 1; // inaudible frequency = half volume + + int delta = volume - osc->last_amp; + if ( delta ) + { + osc->last_amp = volume; + impl->synth.offset( last_time, delta, output ); + } + + // TODO: doesn't maintain high pass flip-flop (very minor issue) + } + else + { + // high pass + static byte const hipass_bits [osc_count] = { 1 << 2, 1 << 1, 0, 0 }; + blip_time_t period2 = 0; // unused if no high pass + blip_time_t time2 = end_time; + if ( this->control & hipass_bits [i] ) + { + period2 = osc [2].period; + time2 = last_time + osc [2].delay; + if ( osc->invert ) + { + // trick inner wave loop into inverting output + osc->last_amp -= volume; + volume = -volume; + } + } + + if ( time < end_time || time2 < end_time ) + { + // poly source + static byte const poly1 [] = { 0x55, 0x55 }; // square wave + byte const* poly = poly1; + int poly_len = 8 * sizeof poly1; // can be just 2 bits, but this is faster + int poly_pos = osc->phase & 1; + int poly_inc = 1; + if ( !(osc_control & 0x20) ) + { + poly = polym; + poly_len = polym_len; + poly_pos = polym_pos; + if ( osc_control & 0x40 ) + { + poly = impl->poly4; + poly_len = poly4_len; + poly_pos = poly4_pos; + } + poly_inc = period % poly_len; + poly_pos = (poly_pos + osc->delay) % poly_len; + } + poly_inc -= poly_len; // allows more optimized inner loop below + + // square/poly5 wave + blargg_ulong wave = poly5; + check( poly5 & 1 ); // low bit is set for pure wave + int poly5_inc = 0; + if ( !(osc_control & 0x80) ) + { + wave = run_poly5( wave, (osc->delay + poly5_pos) % poly5_len ); + poly5_inc = period % poly5_len; + } + + // Run wave and high pass interleved with each catching up to the other. + // Disabled high pass has no performance effect since inner wave loop + // makes no compromise for high pass, and only runs once in that case. + int osc_last_amp = osc->last_amp; + do + { + // run high pass + if ( time2 < time ) + { + int delta = -osc_last_amp; + if ( volume < 0 ) + delta += volume; + if ( delta ) + { + osc_last_amp += delta - volume; + volume = -volume; + impl->synth.offset( time2, delta, output ); + } + } + while ( time2 <= time ) // must advance *past* time to avoid hang + time2 += period2; + + // run wave + blip_time_t end = end_time; + if ( end > time2 ) + end = time2; + while ( time < end ) + { + if ( wave & 1 ) + { + int amp = volume & -(poly [poly_pos >> 3] >> (poly_pos & 7) & 1); + if ( (poly_pos += poly_inc) < 0 ) + poly_pos += poly_len; + int delta = amp - osc_last_amp; + if ( delta ) + { + osc_last_amp = amp; + impl->synth.offset( time, delta, output ); + } + } + wave = run_poly5( wave, poly5_inc ); + time += period; + } + } + while ( time < end_time || time2 < end_time ); + + osc->phase = poly_pos; + osc->last_amp = osc_last_amp; + } + + osc->invert = 0; + if ( volume < 0 ) + { + // undo inversion trickery + osc->last_amp -= volume; + osc->invert = 1; + } + } + } + + // maintain divider + blip_time_t remain = end_time - time; + if ( remain > 0 ) + { + blargg_long count = (remain + period - 1) / period; + osc->phase ^= count; + time += count * period; + } + osc->delay = time - end_time; + } + + // advance polies + blip_time_t duration = end_time - last_time; + last_time = end_time; + poly4_pos = (poly4_pos + duration) % poly4_len; + poly5_pos = (poly5_pos + duration) % poly5_len; + polym_pos += duration; // will get %'d on next call +} + +void Sap_Apu::write_data( blip_time_t time, unsigned addr, int data ) +{ + run_until( time ); + int i = (addr ^ 0xD200) >> 1; + if ( i < osc_count ) + { + oscs [i].regs [addr & 1] = data; + } + else if ( addr == 0xD208 ) + { + control = data; + } + else if ( addr == 0xD209 ) + { + oscs [0].delay = 0; + oscs [1].delay = 0; + oscs [2].delay = 0; + oscs [3].delay = 0; + } + /* + // TODO: are polynomials reset in this case? + else if ( addr == 0xD20F ) + { + if ( (data & 3) == 0 ) + polym_pos = 0; + } + */ +} + +void Sap_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + + last_time -= end_time; +} diff --git a/thirdparty/game-music-emu/gme/Sap_Apu.h b/thirdparty/game-music-emu/gme/Sap_Apu.h new file mode 100644 index 000000000..1b67571bc --- /dev/null +++ b/thirdparty/game-music-emu/gme/Sap_Apu.h @@ -0,0 +1,77 @@ +// Atari POKEY sound chip emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef SAP_APU_H +#define SAP_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Sap_Apu_Impl; + +class Sap_Apu { +public: + enum { osc_count = 4 }; + void osc_output( int index, Blip_Buffer* ); + + void reset( Sap_Apu_Impl* ); + + enum { start_addr = 0xD200 }; + enum { end_addr = 0xD209 }; + void write_data( blip_time_t, unsigned addr, int data ); + + void end_frame( blip_time_t ); + +public: + Sap_Apu(); +private: + struct osc_t + { + unsigned char regs [2]; + unsigned char phase; + unsigned char invert; + int last_amp; + blip_time_t delay; + blip_time_t period; // always recalculated before use; here for convenience + Blip_Buffer* output; + }; + osc_t oscs [osc_count]; + Sap_Apu_Impl* impl; + blip_time_t last_time; + int poly5_pos; + int poly4_pos; + int polym_pos; + int control; + + void calc_periods(); + void run_until( blip_time_t ); + + enum { poly4_len = (1L << 4) - 1 }; + enum { poly9_len = (1L << 9) - 1 }; + enum { poly17_len = (1L << 17) - 1 }; + friend class Sap_Apu_Impl; +}; + +// Common tables and Blip_Synth that can be shared among multiple Sap_Apu objects +class Sap_Apu_Impl { +public: + Blip_Synth synth; + + Sap_Apu_Impl(); + void volume( double d ) { synth.volume( 1.0 / Sap_Apu::osc_count / 30 * d ); } + +private: + typedef unsigned char byte; + byte poly4 [Sap_Apu::poly4_len / 8 + 1]; + byte poly9 [Sap_Apu::poly9_len / 8 + 1]; + byte poly17 [Sap_Apu::poly17_len / 8 + 1]; + friend class Sap_Apu; +}; + +inline void Sap_Apu::osc_output( int i, Blip_Buffer* b ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = b; +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Sap_Cpu.cpp b/thirdparty/game-music-emu/gme/Sap_Cpu.cpp new file mode 100644 index 000000000..76ae277ad --- /dev/null +++ b/thirdparty/game-music-emu/gme/Sap_Cpu.cpp @@ -0,0 +1,1004 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Sap_Cpu.h" + +#include +#include "blargg_endian.h" + +//#include "nes_cpu_log.h" + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#define FLUSH_TIME() (void) (s.time = s_time) +#define CACHE_TIME() (void) (s_time = s.time) + +#include "sap_cpu_io.h" + +#ifndef CPU_DONE + #define CPU_DONE( cpu, time, result_out ) { result_out = -1; } +#endif + +#include "blargg_source.h" + +int const st_n = 0x80; +int const st_v = 0x40; +int const st_r = 0x20; +int const st_b = 0x10; +int const st_d = 0x08; +int const st_i = 0x04; +int const st_z = 0x02; +int const st_c = 0x01; + +void Sap_Cpu::reset( void* new_mem ) +{ + check( state == &state_ ); + state = &state_; + mem = (uint8_t*) new_mem; + r.status = st_i; + r.sp = 0xFF; + r.pc = 0; + r.a = 0; + r.x = 0; + r.y = 0; + state_.time = 0; + state_.base = 0; + irq_time_ = future_sap_time; + end_time_ = future_sap_time; + + blargg_verify_byte_order(); +} + +#define TIME (s_time + s.base) +#define READ( addr ) CPU_READ( this, (addr), TIME ) +#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} +#define READ_LOW( addr ) (mem [int (addr)]) +#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) +#define READ_PROG( addr ) (READ_LOW( addr )) + +#define SET_SP( v ) (sp = ((v) + 1) | 0x100) +#define GET_SP() ((sp - 1) & 0xFF) +#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) + +bool Sap_Cpu::run( sap_time_t end_time ) +{ + bool illegal_encountered = false; + set_end_time( end_time ); + state_t s = this->state_; + this->state = &s; + int32_t s_time = s.time; + uint8_t* const mem = this->mem; // cache + + // registers + uint16_t pc = r.pc; + uint8_t a = r.a; + uint8_t x = r.x; + uint8_t y = r.y; + uint16_t sp; + SET_SP( r.sp ); + + // status flags + #define IS_NEG (nz & 0x8080) + + #define CALC_STATUS( out ) do {\ + out = status & (st_v | st_d | st_i);\ + out |= ((nz >> 8) | nz) & st_n;\ + out |= c >> 8 & st_c;\ + if ( !(nz & 0xFF) ) out |= st_z;\ + } while ( 0 ) + + #define SET_STATUS( in ) do {\ + status = in & (st_v | st_d | st_i);\ + nz = in << 8;\ + c = nz;\ + nz |= ~in & st_z;\ + } while ( 0 ) + + uint8_t status; + uint16_t c; // carry set if (c & 0x100) != 0 + uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + { + uint8_t temp = r.status; + SET_STATUS( temp ); + } + + goto loop; +dec_clock_loop: + s_time--; +loop: + + #ifndef NDEBUG + { + sap_time_t correct = end_time_; + if ( !(status & st_i) && correct > irq_time_ ) + correct = irq_time_; + check( s.base == correct ); + } + #endif + + check( (unsigned) GET_SP() < 0x100 ); + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + check( (unsigned) y < 0x100 ); + + uint8_t opcode = mem [pc]; + pc++; + uint8_t const* instr = mem + pc; + + static uint8_t const clock_table [256] = + {// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 + 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 + 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 + 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 + 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A + 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F + }; // 0x00 was 7 + + uint16_t data; + data = clock_table [opcode]; + if ( (s_time += data) >= 0 ) + goto possibly_out_of_time; +almost_out_of_time: + + data = *instr; + + #ifdef NES_CPU_LOG_H + nes_cpu_log( "cpu_log", pc - 1, opcode, instr [0], instr [1] ); + #endif + + switch ( opcode ) + { +possibly_out_of_time: + if ( s_time < (int) data ) + goto almost_out_of_time; + s_time -= data; + goto out_of_time; + +// Macros + +#define GET_MSB() (instr [1]) +#define ADD_PAGE() (pc++, data += 0x100 * GET_MSB()) +#define GET_ADDR() GET_LE16( instr ) + +#define NO_PAGE_CROSSING( lsb ) +#define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8; + +#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; + +#define IND_Y( cross, out ) {\ + uint16_t temp = READ_LOW( data ) + y;\ + out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ + cross( temp );\ + } + +#define IND_X( out ) {\ + uint16_t temp = data + x;\ + out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ + } + +#define ARITH_ADDR_MODES( op )\ +case op - 0x04: /* (ind,x) */\ + IND_X( data )\ + goto ptr##op;\ +case op + 0x0C: /* (ind),y */\ + IND_Y( HANDLE_PAGE_CROSSING, data )\ + goto ptr##op;\ +case op + 0x10: /* zp,X */\ + data = uint8_t (data + x);\ +case op + 0x00: /* zp */\ + data = READ_LOW( data );\ + goto imm##op;\ +case op + 0x14: /* abs,Y */\ + data += y;\ + goto ind##op;\ +case op + 0x18: /* abs,X */\ + data += x;\ +ind##op:\ + HANDLE_PAGE_CROSSING( data );\ +case op + 0x08: /* abs */\ + ADD_PAGE();\ +ptr##op:\ + FLUSH_TIME();\ + data = READ( data );\ + CACHE_TIME();\ +case op + 0x04: /* imm */\ +imm##op: + +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH( cond )\ +{\ + int16_t offset = (int8_t) data;\ + uint16_t extra_clock = (++pc & 0xFF) + offset;\ + if ( !(cond) ) goto dec_clock_loop;\ + pc += offset;\ + s_time += extra_clock >> 8 & 1;\ + goto loop;\ +} + +// Often-Used + + case 0xB5: // LDA zp,x + a = nz = READ_LOW( uint8_t (data + x) ); + pc++; + goto loop; + + case 0xA5: // LDA zp + a = nz = READ_LOW( data ); + pc++; + goto loop; + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ); + + case 0x20: { // JSR + uint16_t temp = pc + 1; + pc = GET_ADDR(); + WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); + sp = (sp - 2) | 0x100; + WRITE_LOW( sp, temp ); + goto loop; + } + + case 0x4C: // JMP abs + pc = GET_ADDR(); + goto loop; + + case 0xE8: // INX + INC_DEC_XY( x, 1 ) + + case 0x10: // BPL + BRANCH( !IS_NEG ) + + ARITH_ADDR_MODES( 0xC5 ) // CMP + nz = a - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0x30: // BMI + BRANCH( IS_NEG ) + + case 0xF0: // BEQ + BRANCH( !(uint8_t) nz ); + + case 0x95: // STA zp,x + data = uint8_t (data + x); + case 0x85: // STA zp + pc++; + WRITE_LOW( data, a ); + goto loop; + + case 0xC8: // INY + INC_DEC_XY( y, 1 ) + + case 0xA8: // TAY + y = a; + nz = a; + goto loop; + + case 0x98: // TYA + a = y; + nz = y; + goto loop; + + case 0xAD:{// LDA abs + unsigned addr = GET_ADDR(); + pc += 2; + nz = READ( addr ); + a = nz; + goto loop; + } + + case 0x60: // RTS + pc = 1 + READ_LOW( sp ); + pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); + sp = (sp - 0xFE) | 0x100; + goto loop; + + { + uint16_t addr; + + case 0x99: // STA abs,Y + addr = y + GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + goto sta_ptr; + + case 0x8D: // STA abs + addr = GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + goto sta_ptr; + + case 0x9D: // STA abs,X (slightly more common than STA abs) + addr = x + GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + sta_ptr: + FLUSH_TIME(); + WRITE( addr, a ); + CACHE_TIME(); + goto loop; + + case 0x91: // STA (ind),Y + IND_Y( NO_PAGE_CROSSING, addr ) + pc++; + goto sta_ptr; + + case 0x81: // STA (ind,X) + IND_X( addr ) + pc++; + goto sta_ptr; + + } + + case 0xA9: // LDA #imm + pc++; + a = data; + nz = data; + goto loop; + + // common read instructions + { + uint16_t addr; + + case 0xA1: // LDA (ind,X) + IND_X( addr ) + pc++; + goto a_nz_read_addr; + + case 0xB1:// LDA (ind),Y + addr = READ_LOW( data ) + y; + HANDLE_PAGE_CROSSING( addr ); + addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); + pc++; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + goto a_nz_read_addr; + + case 0xB9: // LDA abs,Y + HANDLE_PAGE_CROSSING( data + y ); + addr = GET_ADDR() + y; + pc += 2; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + goto a_nz_read_addr; + + case 0xBD: // LDA abs,X + HANDLE_PAGE_CROSSING( data + x ); + addr = GET_ADDR() + x; + pc += 2; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + a_nz_read_addr: + FLUSH_TIME(); + a = nz = READ( addr ); + CACHE_TIME(); + goto loop; + + } + +// Branch + + case 0x50: // BVC + BRANCH( !(status & st_v) ) + + case 0x70: // BVS + BRANCH( status & st_v ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + +// Load/store + + case 0x94: // STY zp,x + data = uint8_t (data + x); + case 0x84: // STY zp + pc++; + WRITE_LOW( data, y ); + goto loop; + + case 0x96: // STX zp,y + data = uint8_t (data + y); + case 0x86: // STX zp + pc++; + WRITE_LOW( data, x ); + goto loop; + + case 0xB6: // LDX zp,y + data = uint8_t (data + y); + case 0xA6: // LDX zp + data = READ_LOW( data ); + case 0xA2: // LDX #imm + pc++; + x = data; + nz = data; + goto loop; + + case 0xB4: // LDY zp,x + data = uint8_t (data + x); + case 0xA4: // LDY zp + data = READ_LOW( data ); + case 0xA0: // LDY #imm + pc++; + y = data; + nz = data; + goto loop; + + case 0xBC: // LDY abs,X + data += x; + HANDLE_PAGE_CROSSING( data ); + case 0xAC:{// LDY abs + unsigned addr = data + 0x100 * GET_MSB(); + pc += 2; + FLUSH_TIME(); + y = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + case 0xBE: // LDX abs,y + data += y; + HANDLE_PAGE_CROSSING( data ); + case 0xAE:{// LDX abs + unsigned addr = data + 0x100 * GET_MSB(); + pc += 2; + FLUSH_TIME(); + x = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + { + uint8_t temp; + case 0x8C: // STY abs + temp = y; + goto store_abs; + + case 0x8E: // STX abs + temp = x; + store_abs: + unsigned addr = GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, temp ); + goto loop; + } + FLUSH_TIME(); + WRITE( addr, temp ); + CACHE_TIME(); + goto loop; + } + +// Compare + + case 0xEC:{// CPX abs + unsigned addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( addr ); + CACHE_TIME(); + goto cpx_data; + } + + case 0xE4: // CPX zp + data = READ_LOW( data ); + case 0xE0: // CPX #imm + cpx_data: + nz = x - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0xCC:{// CPY abs + unsigned addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( addr ); + CACHE_TIME(); + goto cpy_data; + } + + case 0xC4: // CPY zp + data = READ_LOW( data ); + case 0xC0: // CPY #imm + cpy_data: + nz = y - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + +// Logical + + ARITH_ADDR_MODES( 0x25 ) // AND + nz = (a &= data); + pc++; + goto loop; + + ARITH_ADDR_MODES( 0x45 ) // EOR + nz = (a ^= data); + pc++; + goto loop; + + ARITH_ADDR_MODES( 0x05 ) // ORA + nz = (a |= data); + pc++; + goto loop; + + case 0x2C:{// BIT abs + unsigned addr = GET_ADDR(); + pc += 2; + status &= ~st_v; + nz = READ( addr ); + status |= nz & st_v; + if ( a & nz ) + goto loop; + nz <<= 8; // result must be zero, even if N bit is set + goto loop; + } + + case 0x24: // BIT zp + nz = READ_LOW( data ); + pc++; + status &= ~st_v; + status |= nz & st_v; + if ( a & nz ) + goto loop; + nz <<= 8; // result must be zero, even if N bit is set + goto loop; + +// Add/subtract + + ARITH_ADDR_MODES( 0xE5 ) // SBC + case 0xEB: // unofficial equivalent + data ^= 0xFF; + goto adc_imm; + + ARITH_ADDR_MODES( 0x65 ) // ADC + adc_imm: { + check( !(status & st_d) ); + int16_t carry = c >> 8 & 1; + int16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend + status &= ~st_v; + status |= ov >> 2 & 0x40; + c = nz = a + data + carry; + pc++; + a = (uint8_t) nz; + goto loop; + } + +// Shift/rotate + + case 0x4A: // LSR A + c = 0; + case 0x6A: // ROR A + nz = c >> 1 & 0x80; + c = a << 8; + nz |= a >> 1; + a = nz; + goto loop; + + case 0x0A: // ASL A + nz = a << 1; + c = nz; + a = (uint8_t) nz; + goto loop; + + case 0x2A: { // ROL A + nz = a << 1; + int16_t temp = c >> 8 & 1; + c = nz; + nz |= temp; + a = (uint8_t) nz; + goto loop; + } + + case 0x5E: // LSR abs,X + data += x; + case 0x4E: // LSR abs + c = 0; + case 0x6E: // ROR abs + ror_abs: { + ADD_PAGE(); + FLUSH_TIME(); + int temp = READ( data ); + nz = (c >> 1 & 0x80) | (temp >> 1); + c = temp << 8; + goto rotate_common; + } + + case 0x3E: // ROL abs,X + data += x; + goto rol_abs; + + case 0x1E: // ASL abs,X + data += x; + case 0x0E: // ASL abs + c = 0; + case 0x2E: // ROL abs + rol_abs: + ADD_PAGE(); + nz = c >> 8 & 1; + FLUSH_TIME(); + nz |= (c = READ( data ) << 1); + rotate_common: + pc++; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + + case 0x7E: // ROR abs,X + data += x; + goto ror_abs; + + case 0x76: // ROR zp,x + data = uint8_t (data + x); + goto ror_zp; + + case 0x56: // LSR zp,x + data = uint8_t (data + x); + case 0x46: // LSR zp + c = 0; + case 0x66: // ROR zp + ror_zp: { + int temp = READ_LOW( data ); + nz = (c >> 1 & 0x80) | (temp >> 1); + c = temp << 8; + goto write_nz_zp; + } + + case 0x36: // ROL zp,x + data = uint8_t (data + x); + goto rol_zp; + + case 0x16: // ASL zp,x + data = uint8_t (data + x); + case 0x06: // ASL zp + c = 0; + case 0x26: // ROL zp + rol_zp: + nz = c >> 8 & 1; + nz |= (c = READ_LOW( data ) << 1); + goto write_nz_zp; + +// Increment/decrement + + case 0xCA: // DEX + INC_DEC_XY( x, -1 ) + + case 0x88: // DEY + INC_DEC_XY( y, -1 ) + + case 0xF6: // INC zp,x + data = uint8_t (data + x); + case 0xE6: // INC zp + nz = 1; + goto add_nz_zp; + + case 0xD6: // DEC zp,x + data = uint8_t (data + x); + case 0xC6: // DEC zp + nz = (uint16_t) -1; + add_nz_zp: + nz += READ_LOW( data ); + write_nz_zp: + pc++; + WRITE_LOW( data, nz ); + goto loop; + + case 0xFE: // INC abs,x + data = x + GET_ADDR(); + goto inc_ptr; + + case 0xEE: // INC abs + data = GET_ADDR(); + inc_ptr: + nz = 1; + goto inc_common; + + case 0xDE: // DEC abs,x + data = x + GET_ADDR(); + goto dec_ptr; + + case 0xCE: // DEC abs + data = GET_ADDR(); + dec_ptr: + nz = (uint16_t) -1; + inc_common: + FLUSH_TIME(); + nz += READ( data ); + pc += 2; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + +// Transfer + + case 0xAA: // TAX + x = a; + nz = a; + goto loop; + + case 0x8A: // TXA + a = x; + nz = x; + goto loop; + + case 0x9A: // TXS + SET_SP( x ); // verified (no flag change) + goto loop; + + case 0xBA: // TSX + x = nz = GET_SP(); + goto loop; + +// Stack + + case 0x48: // PHA + PUSH( a ); // verified + goto loop; + + case 0x68: // PLA + a = nz = READ_LOW( sp ); + sp = (sp - 0xFF) | 0x100; + goto loop; + + case 0x40:{// RTI + uint8_t temp = READ_LOW( sp ); + pc = READ_LOW( 0x100 | (sp - 0xFF) ); + pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; + sp = (sp - 0xFD) | 0x100; + data = status; + SET_STATUS( temp ); + this->r.status = status; // update externally-visible I flag + if ( (data ^ status) & st_i ) + { + sap_time_t new_time = end_time_; + if ( !(status & st_i) && new_time > irq_time_ ) + new_time = irq_time_; + blargg_long delta = s.base - new_time; + s.base = new_time; + s_time += delta; + } + goto loop; + } + + case 0x28:{// PLP + uint8_t temp = READ_LOW( sp ); + sp = (sp - 0xFF) | 0x100; + uint8_t changed = status ^ temp; + SET_STATUS( temp ); + if ( !(changed & st_i) ) + goto loop; // I flag didn't change + if ( status & st_i ) + goto handle_sei; + goto handle_cli; + } + + case 0x08: { // PHP + uint8_t temp; + CALC_STATUS( temp ); + PUSH( temp | (st_b | st_r) ); + goto loop; + } + + case 0x6C:{// JMP (ind) + data = GET_ADDR(); + pc = READ_PROG( data ); + data = (data & 0xFF00) | ((data + 1) & 0xFF); + pc |= 0x100 * READ_PROG( data ); + goto loop; + } + + case 0x00: // BRK + goto handle_brk; + +// Flags + + case 0x38: // SEC + c = (uint16_t) ~0; + goto loop; + + case 0x18: // CLC + c = 0; + goto loop; + + case 0xB8: // CLV + status &= ~st_v; + goto loop; + + case 0xD8: // CLD + status &= ~st_d; + goto loop; + + case 0xF8: // SED + status |= st_d; + goto loop; + + case 0x58: // CLI + if ( !(status & st_i) ) + goto loop; + status &= ~st_i; + handle_cli: { + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - irq_time_; + if ( delta <= 0 ) + { + if ( TIME < irq_time_ ) + goto loop; + goto delayed_cli; + } + s.base = irq_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + + if ( delta >= s_time + 1 ) + { + // delayed irq until after next instruction + s.base += s_time + 1; + s_time = -1; + irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop + goto loop; + } + delayed_cli: + debug_printf( "Delayed CLI not emulated\n" ); + goto loop; + } + + case 0x78: // SEI + if ( status & st_i ) + goto loop; + status |= st_i; + handle_sei: { + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - end_time_; + s.base = end_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + debug_printf( "Delayed SEI not emulated\n" ); + goto loop; + } + +// Unofficial + + // SKW - Skip word + case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: + HANDLE_PAGE_CROSSING( data + x ); + case 0x0C: + pc++; + // SKB - Skip byte + case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: + case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: + pc++; + goto loop; + + // NOP + case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: + goto loop; + +// Unimplemented + + // halt + //case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: + //case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2: + + default: + illegal_encountered = true; + pc--; + goto stop; + } + assert( false ); + + int result_; +handle_brk: + if ( (pc - 1) >= idle_addr ) + goto idle_done; + pc++; + result_ = 4; + debug_printf( "BRK executed\n" ); + +interrupt: + { + s_time += 7; + + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); + WRITE_LOW( 0x100 | (sp - 2), pc ); + pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); + + sp = (sp - 3) | 0x100; + uint8_t temp; + CALC_STATUS( temp ); + temp |= st_r; + if ( result_ ) + temp |= st_b; // TODO: incorrectly sets B flag for IRQ + WRITE_LOW( sp, temp ); + + status &= ~st_d; + status |= st_i; + this->r.status = status; // update externally-visible I flag + + blargg_long delta = s.base - end_time_; + s.base = end_time_; + s_time += delta; + goto loop; + } + +idle_done: + //s_time = 0; + pc--; + goto stop; +out_of_time: + pc--; + FLUSH_TIME(); + CPU_DONE( this, TIME, result_ ); + CACHE_TIME(); + if ( result_ >= 0 ) + goto interrupt; + if ( s_time < 0 ) + goto loop; + +stop: + + s.time = s_time; + + r.pc = pc; + r.sp = GET_SP(); + r.a = a; + r.x = x; + r.y = y; + + { + uint8_t temp; + CALC_STATUS( temp ); + r.status = temp; + } + + this->state_ = s; + this->state = &this->state_; + + return illegal_encountered; +} + diff --git a/thirdparty/game-music-emu/gme/Sap_Cpu.h b/thirdparty/game-music-emu/gme/Sap_Cpu.h new file mode 100644 index 000000000..fdfb9a310 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Sap_Cpu.h @@ -0,0 +1,81 @@ +// Atari 6502 CPU emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef SAP_CPU_H +#define SAP_CPU_H + +#include "blargg_common.h" + +typedef blargg_long sap_time_t; // clock cycle count +typedef unsigned sap_addr_t; // 16-bit address +enum { future_sap_time = INT_MAX / 2 + 1 }; + +class Sap_Cpu { +public: + // Clear all registers and keep pointer to 64K memory passed in + void reset( void* mem_64k ); + + // Run until specified time is reached. Returns true if suspicious/unsupported + // instruction was encountered at any point during run. + bool run( sap_time_t end_time ); + + // Registers are not updated until run() returns (except I flag in status) + struct registers_t { + uint16_t pc; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t status; + uint8_t sp; + }; + registers_t r; + + enum { idle_addr = 0xFEFF }; + + // Time of beginning of next instruction to be executed + sap_time_t time() const { return state->time + state->base; } + void set_time( sap_time_t t ) { state->time = t - state->base; } + void adjust_time( int delta ) { state->time += delta; } + + sap_time_t irq_time() const { return irq_time_; } + void set_irq_time( sap_time_t ); + + sap_time_t end_time() const { return end_time_; } + void set_end_time( sap_time_t ); + +public: + Sap_Cpu() { state = &state_; } + enum { irq_inhibit = 0x04 }; +private: + struct state_t { + sap_time_t base; + sap_time_t time; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + sap_time_t irq_time_; + sap_time_t end_time_; + uint8_t* mem; + + inline sap_time_t update_end_time( sap_time_t end, sap_time_t irq ); +}; + +inline sap_time_t Sap_Cpu::update_end_time( sap_time_t t, sap_time_t irq ) +{ + if ( irq < t && !(r.status & irq_inhibit) ) t = irq; + sap_time_t delta = state->base - t; + state->base = t; + return delta; +} + +inline void Sap_Cpu::set_irq_time( sap_time_t t ) +{ + state->time += update_end_time( end_time_, (irq_time_ = t) ); +} + +inline void Sap_Cpu::set_end_time( sap_time_t t ) +{ + state->time += update_end_time( (end_time_ = t), irq_time_ ); +} + +#endif diff --git a/thirdparty/game-music-emu/gme/Sap_Emu.cpp b/thirdparty/game-music-emu/gme/Sap_Emu.cpp new file mode 100644 index 000000000..dc5d666d6 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Sap_Emu.cpp @@ -0,0 +1,443 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Sap_Emu.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +long const base_scanline_period = 114; + +Sap_Emu::Sap_Emu() +{ + set_type( gme_sap_type ); + + static const char* const names [Sap_Apu::osc_count * 2] = { + "Wave 1", "Wave 2", "Wave 3", "Wave 4", + "Wave 5", "Wave 6", "Wave 7", "Wave 8", + }; + set_voice_names( names ); + + static int const types [Sap_Apu::osc_count * 2] = { + wave_type | 1, wave_type | 2, wave_type | 3, wave_type | 0, + wave_type | 5, wave_type | 6, wave_type | 7, wave_type | 4, + }; + set_voice_types( types ); + set_silence_lookahead( 6 ); +} + +Sap_Emu::~Sap_Emu() { } + +// Track info + +// Returns 16 or greater if not hex +inline int from_hex_char( int h ) +{ + h -= 0x30; + if ( (unsigned) h > 9 ) + h = ((h - 0x11) & 0xDF) + 10; + return h; +} + +static long from_hex( byte const* in ) +{ + unsigned result = 0; + for ( int n = 4; n--; ) + { + int h = from_hex_char( *in++ ); + if ( h > 15 ) + return -1; + result = result * 0x10 + h; + } + return result; +} + +static int from_dec( byte const* in, byte const* end ) +{ + if ( in >= end ) + return -1; + + int n = 0; + while ( in < end ) + { + int dig = *in++ - '0'; + if ( (unsigned) dig > 9 ) + return -1; + n = n * 10 + dig; + } + return n; +} + +static void parse_string( byte const* in, byte const* end, int len, char* out ) +{ + byte const* start = in; + if ( *in++ == '\"' ) + { + start++; + while ( in < end && *in != '\"' ) + in++; + } + else + { + in = end; + } + len = min( len - 1, int (in - start) ); + out [len] = 0; + memcpy( out, start, len ); +} + +static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out ) +{ + out->track_count = 1; + out->author [0] = 0; + out->name [0] = 0; + out->copyright [0] = 0; + + if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) ) + return gme_wrong_file_type; + + byte const* file_end = in + size - 5; + in += 5; + while ( in < file_end && (in [0] != 0xFF || in [1] != 0xFF) ) + { + byte const* line_end = in; + while ( line_end < file_end && *line_end != 0x0D ) + line_end++; + + char const* tag = (char const*) in; + while ( in < line_end && *in > ' ' ) + in++; + int tag_len = (char const*) in - tag; + + while ( in < line_end && *in <= ' ' ) in++; + + if ( tag_len <= 0 ) + { + // skip line + } + else if ( !strncmp( "INIT", tag, tag_len ) ) + { + out->init_addr = from_hex( in ); + if ( (unsigned long) out->init_addr > 0xFFFF ) + return "Invalid init address"; + } + else if ( !strncmp( "PLAYER", tag, tag_len ) ) + { + out->play_addr = from_hex( in ); + if ( (unsigned long) out->play_addr > 0xFFFF ) + return "Invalid play address"; + } + else if ( !strncmp( "MUSIC", tag, tag_len ) ) + { + out->music_addr = from_hex( in ); + if ( (unsigned long) out->music_addr > 0xFFFF ) + return "Invalid music address"; + } + else if ( !strncmp( "SONGS", tag, tag_len ) ) + { + out->track_count = from_dec( in, line_end ); + if ( out->track_count <= 0 ) + return "Invalid track count"; + } + else if ( !strncmp( "TYPE", tag, tag_len ) ) + { + switch ( out->type = *in ) + { + case 'C': + case 'B': + break; + + case 'D': + return "Digimusic not supported"; + + default: + return "Unsupported player type"; + } + } + else if ( !strncmp( "STEREO", tag, tag_len ) ) + { + out->stereo = true; + } + else if ( !strncmp( "FASTPLAY", tag, tag_len ) ) + { + out->fastplay = from_dec( in, line_end ); + if ( out->fastplay <= 0 ) + return "Invalid fastplay value"; + } + else if ( !strncmp( "AUTHOR", tag, tag_len ) ) + { + parse_string( in, line_end, sizeof out->author, out->author ); + } + else if ( !strncmp( "NAME", tag, tag_len ) ) + { + parse_string( in, line_end, sizeof out->name, out->name ); + } + else if ( !strncmp( "DATE", tag, tag_len ) ) + { + parse_string( in, line_end, sizeof out->copyright, out->copyright ); + } + + in = line_end + 2; + } + + if ( in [0] != 0xFF || in [1] != 0xFF ) + return "ROM data missing"; + out->rom_data = in + 2; + + return 0; +} + +static void copy_sap_fields( Sap_Emu::info_t const& in, track_info_t* out ) +{ + Gme_File::copy_field_( out->game, in.name ); + Gme_File::copy_field_( out->author, in.author ); + Gme_File::copy_field_( out->copyright, in.copyright ); +} + +blargg_err_t Sap_Emu::track_info_( track_info_t* out, int ) const +{ + copy_sap_fields( info, out ); + return 0; +} + +struct Sap_File : Gme_Info_ +{ + Sap_Emu::info_t info; + + Sap_File() { set_type( gme_sap_type ); } + + blargg_err_t load_mem_( byte const* begin, long size ) + { + RETURN_ERR( parse_info( begin, size, &info ) ); + set_track_count( info.track_count ); + return 0; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_sap_fields( info, out ); + return 0; + } +}; + +static Music_Emu* new_sap_emu () { return BLARGG_NEW Sap_Emu ; } +static Music_Emu* new_sap_file() { return BLARGG_NEW Sap_File; } + +static gme_type_t_ const gme_sap_type_ = { "Atari XL", 0, &new_sap_emu, &new_sap_file, "SAP", 1 }; +BLARGG_EXPORT extern gme_type_t const gme_sap_type = &gme_sap_type_; + +// Setup + +blargg_err_t Sap_Emu::load_mem_( byte const* in, long size ) +{ + file_end = in + size; + + info.warning = 0; + info.type = 'B'; + info.stereo = false; + info.init_addr = -1; + info.play_addr = -1; + info.music_addr = -1; + info.fastplay = 312; + RETURN_ERR( parse_info( in, size, &info ) ); + + set_warning( info.warning ); + set_track_count( info.track_count ); + set_voice_count( Sap_Apu::osc_count << info.stereo ); + apu_impl.volume( gain() ); + + return setup_buffer( 1773447 ); +} + +void Sap_Emu::update_eq( blip_eq_t const& eq ) +{ + apu_impl.synth.treble_eq( eq ); +} + +void Sap_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + int i2 = i - Sap_Apu::osc_count; + if ( i2 >= 0 ) + apu2.osc_output( i2, right ); + else + apu.osc_output( i, (info.stereo ? left : center) ); +} + +// Emulation + +void Sap_Emu::set_tempo_( double t ) +{ + scanline_period = sap_time_t (base_scanline_period / t); +} + +inline sap_time_t Sap_Emu::play_period() const { return info.fastplay * scanline_period; } + +void Sap_Emu::cpu_jsr( sap_addr_t addr ) +{ + check( r.sp >= 0xFE ); // catch anything trying to leave data on stack + r.pc = addr; + int high_byte = (idle_addr - 1) >> 8; + if ( r.sp == 0xFE && mem.ram [0x1FF] == high_byte ) + r.sp = 0xFF; // pop extra byte off + mem.ram [0x100 + r.sp--] = high_byte; // some routines use RTI to return + mem.ram [0x100 + r.sp--] = high_byte; + mem.ram [0x100 + r.sp--] = (idle_addr - 1) & 0xFF; +} + +void Sap_Emu::run_routine( sap_addr_t addr ) +{ + cpu_jsr( addr ); + cpu::run( 312 * base_scanline_period * 60 ); + check( r.pc == idle_addr ); +} + +inline void Sap_Emu::call_init( int track ) +{ + switch ( info.type ) + { + case 'B': + r.a = track; + run_routine( info.init_addr ); + break; + + case 'C': + r.a = 0x70; + r.x = info.music_addr&0xFF; + r.y = info.music_addr >> 8; + run_routine( info.play_addr + 3 ); + r.a = 0; + r.x = track; + run_routine( info.play_addr + 3 ); + break; + } +} + +blargg_err_t Sap_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + memset( &mem, 0, sizeof mem ); + + byte const* in = info.rom_data; + while ( file_end - in >= 5 ) + { + unsigned start = get_le16( in ); + unsigned end = get_le16( in + 2 ); + //debug_printf( "Block $%04X-$%04X\n", start, end ); + in += 4; + if ( end < start ) + { + set_warning( "Invalid file data block" ); + break; + } + long len = end - start + 1; + if ( len > file_end - in ) + { + set_warning( "Invalid file data block" ); + break; + } + + memcpy( mem.ram + start, in, len ); + in += len; + if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF ) + in += 2; + } + + apu.reset( &apu_impl ); + apu2.reset( &apu_impl ); + cpu::reset( mem.ram ); + time_mask = 0; // disables sound during init + call_init( track ); + time_mask = -1; + + next_play = play_period(); + + return 0; +} + +// Emulation + +// see sap_cpu_io.h for read/write functions + +void Sap_Emu::cpu_write_( sap_addr_t addr, int data ) +{ + if ( (addr ^ Sap_Apu::start_addr) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) ) + { + GME_APU_HOOK( this, addr - Sap_Apu::start_addr, data ); + apu.write_data( time() & time_mask, addr, data ); + return; + } + + if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) && + info.stereo ) + { + GME_APU_HOOK( this, addr - 0x10 - Sap_Apu::start_addr + 10, data ); + apu2.write_data( time() & time_mask, addr ^ 0x10, data ); + return; + } + + if ( (addr & ~0x0010) != 0xD20F || data != 0x03 ) + debug_printf( "Unmapped write $%04X <- $%02X\n", addr, data ); +} + +inline void Sap_Emu::call_play() +{ + switch ( info.type ) + { + case 'B': + cpu_jsr( info.play_addr ); + break; + + case 'C': + cpu_jsr( info.play_addr + 6 ); + break; + } +} + +blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int ) +{ + set_time( 0 ); + while ( time() < duration ) + { + if ( cpu::run( duration ) || r.pc > idle_addr ) + return "Emulation error (illegal instruction)"; + + if ( r.pc == idle_addr ) + { + if ( next_play <= duration ) + { + set_time( next_play ); + next_play += play_period(); + call_play(); + GME_FRAME_HOOK( this ); + } + else + { + set_time( duration ); + } + } + } + + duration = time(); + next_play -= duration; + check( next_play >= 0 ); + if ( next_play < 0 ) + next_play = 0; + apu.end_frame( duration ); + if ( info.stereo ) + apu2.end_frame( duration ); + + return 0; +} diff --git a/thirdparty/game-music-emu/gme/Sap_Emu.h b/thirdparty/game-music-emu/gme/Sap_Emu.h new file mode 100644 index 000000000..f75312713 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Sap_Emu.h @@ -0,0 +1,68 @@ +// Atari XL/XE SAP music file emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef SAP_EMU_H +#define SAP_EMU_H + +#include "Classic_Emu.h" +#include "Sap_Apu.h" +#include "Sap_Cpu.h" + +class Sap_Emu : private Sap_Cpu, public Classic_Emu { + typedef Sap_Cpu cpu; +public: + static gme_type_t static_type() { return gme_sap_type; } +public: + Sap_Emu(); + ~Sap_Emu(); + struct info_t { + byte const* rom_data; + const char* warning; + long init_addr; + long play_addr; + long music_addr; + int type; + int track_count; + int fastplay; + bool stereo; + char author [256]; + char name [256]; + char copyright [ 32]; + }; +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_mem_( byte const*, long ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); +public: private: friend class Sap_Cpu; + int cpu_read( sap_addr_t ); + void cpu_write( sap_addr_t, int ); + void cpu_write_( sap_addr_t, int ); +private: + info_t info; + + byte const* file_end; + sap_time_t scanline_period; + sap_time_t next_play; + sap_time_t time_mask; + Sap_Apu apu; + Sap_Apu apu2; + + // large items + struct { + byte padding1 [0x100]; + byte ram [0x10000 + 0x100]; + } mem; + Sap_Apu_Impl apu_impl; + + sap_time_t play_period() const; + void call_play(); + void cpu_jsr( sap_addr_t ); + void call_init( int track ); + void run_routine( sap_addr_t ); +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Sms_Apu.cpp b/thirdparty/game-music-emu/gme/Sms_Apu.cpp new file mode 100644 index 000000000..b41fdec41 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Sms_Apu.cpp @@ -0,0 +1,330 @@ +// Sms_Snd_Emu 0.1.4. http://www.slack.net/~ant/ + +#include "Sms_Apu.h" + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Sms_Osc + +Sms_Osc::Sms_Osc() +{ + output = 0; + outputs [0] = 0; // always stays NULL + outputs [1] = 0; + outputs [2] = 0; + outputs [3] = 0; +} + +void Sms_Osc::reset() +{ + delay = 0; + last_amp = 0; + volume = 0; + output_select = 3; + output = outputs [3]; +} + +// Sms_Square + +inline void Sms_Square::reset() +{ + period = 0; + phase = 0; + Sms_Osc::reset(); +} + +void Sms_Square::run( blip_time_t time, blip_time_t end_time ) +{ + if ( !volume || period <= 128 ) + { + // ignore 16kHz and higher + if ( last_amp ) + { + synth->offset( time, -last_amp, output ); + last_amp = 0; + } + time += delay; + if ( !period ) + { + time = end_time; + } + else if ( time < end_time ) + { + // keep calculating phase + int count = (end_time - time + period - 1) / period; + phase = (phase + count) & 1; + time += count * period; + } + } + else + { + int amp = phase ? volume : -volume; + { + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } + } + + time += delay; + if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + int delta = amp * 2; + do + { + delta = -delta; + synth->offset_inline( time, delta, output ); + time += period; + phase ^= 1; + } + while ( time < end_time ); + this->last_amp = phase ? volume : -volume; + } + } + delay = time - end_time; +} + +// Sms_Noise + +static int const noise_periods [3] = { 0x100, 0x200, 0x400 }; + +inline void Sms_Noise::reset() +{ + period = &noise_periods [0]; + shifter = 0x8000; + feedback = 0x9000; + Sms_Osc::reset(); +} + +void Sms_Noise::run( blip_time_t time, blip_time_t end_time ) +{ + int amp = volume; + if ( shifter & 1 ) + amp = -amp; + + { + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth.offset( time, delta, output ); + } + } + + time += delay; + if ( !volume ) + time = end_time; + + if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + unsigned shifter = this->shifter; + int delta = amp * 2; + int period = *this->period * 2; + if ( !period ) + period = 16; + + do + { + int changed = shifter + 1; + shifter = (feedback & -(shifter & 1)) ^ (shifter >> 1); + if ( changed & 2 ) // true if bits 0 and 1 differ + { + delta = -delta; + synth.offset_inline( time, delta, output ); + } + time += period; + } + while ( time < end_time ); + + this->shifter = shifter; + this->last_amp = delta >> 1; + } + delay = time - end_time; +} + +// Sms_Apu + +Sms_Apu::Sms_Apu() +{ + for ( int i = 0; i < 3; i++ ) + { + squares [i].synth = &square_synth; + oscs [i] = &squares [i]; + } + oscs [3] = &noise; + + volume( 1.0 ); + reset(); +} + +Sms_Apu::~Sms_Apu() +{ +} + +void Sms_Apu::volume( double vol ) +{ + vol *= 0.85 / (osc_count * 64 * 2); + square_synth.volume( vol ); + noise.synth.volume( vol ); +} + +void Sms_Apu::treble_eq( const blip_eq_t& eq ) +{ + square_synth.treble_eq( eq ); + noise.synth.treble_eq( eq ); +} + +void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + require( (unsigned) index < osc_count ); + require( (center && left && right) || (!center && !left && !right) ); + Sms_Osc& osc = *oscs [index]; + osc.outputs [1] = right; + osc.outputs [2] = left; + osc.outputs [3] = center; + osc.output = osc.outputs [osc.output_select]; +} + +void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, center, left, right ); +} + +void Sms_Apu::reset( unsigned feedback, int noise_width ) +{ + last_time = 0; + latch = 0; + + if ( !feedback || !noise_width ) + { + feedback = 0x0009; + noise_width = 16; + } + // convert to "Galios configuration" + looped_feedback = 1 << (noise_width - 1); + noise_feedback = 0; + while ( noise_width-- ) + { + noise_feedback = (noise_feedback << 1) | (feedback & 1); + feedback >>= 1; + } + + squares [0].reset(); + squares [1].reset(); + squares [2].reset(); + noise.reset(); +} + +void Sms_Apu::run_until( blip_time_t end_time ) +{ + require( end_time >= last_time ); // end_time must not be before previous time + + if ( end_time > last_time ) + { + // run oscillators + for ( int i = 0; i < osc_count; ++i ) + { + Sms_Osc& osc = *oscs [i]; + if ( osc.output ) + { + osc.output->set_modified(); + if ( i < 3 ) + squares [i].run( last_time, end_time ); + else + noise.run( last_time, end_time ); + } + } + + last_time = end_time; + } +} + +void Sms_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + + assert( last_time >= end_time ); + last_time -= end_time; +} + +void Sms_Apu::write_ggstereo( blip_time_t time, int data ) +{ + require( (unsigned) data <= 0xFF ); + + run_until( time ); + + for ( int i = 0; i < osc_count; i++ ) + { + Sms_Osc& osc = *oscs [i]; + int flags = data >> i; + Blip_Buffer* old_output = osc.output; + osc.output_select = (flags >> 3 & 2) | (flags & 1); + osc.output = osc.outputs [osc.output_select]; + if ( osc.output != old_output && osc.last_amp ) + { + if ( old_output ) + { + old_output->set_modified(); + square_synth.offset( time, -osc.last_amp, old_output ); + } + osc.last_amp = 0; + } + } +} + +// volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 ) +static unsigned char const volumes [16] = { + 64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0 +}; + +void Sms_Apu::write_data( blip_time_t time, int data ) +{ + require( (unsigned) data <= 0xFF ); + + run_until( time ); + + if ( data & 0x80 ) + latch = data; + + int index = (latch >> 5) & 3; + if ( latch & 0x10 ) + { + oscs [index]->volume = volumes [data & 15]; + } + else if ( index < 3 ) + { + Sms_Square& sq = squares [index]; + if ( data & 0x80 ) + sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF); + else + sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00); + } + else + { + int select = data & 3; + if ( select < 3 ) + noise.period = &noise_periods [select]; + else + noise.period = &squares [2].period; + + noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback; + noise.shifter = 0x8000; + } +} diff --git a/thirdparty/game-music-emu/gme/Sms_Apu.h b/thirdparty/game-music-emu/gme/Sms_Apu.h new file mode 100644 index 000000000..3c11a9c3c --- /dev/null +++ b/thirdparty/game-music-emu/gme/Sms_Apu.h @@ -0,0 +1,75 @@ +// Sega Master System SN76489 PSG sound chip emulator + +// Sms_Snd_Emu 0.1.4 +#ifndef SMS_APU_H +#define SMS_APU_H + +#include "Sms_Oscs.h" + +class Sms_Apu { +public: + // Set overall volume of all oscillators, where 1.0 is full volume + void volume( double ); + + // Set treble equalization + void treble_eq( const blip_eq_t& ); + + // Outputs can be assigned to a single buffer for mono output, or to three + // buffers for stereo output (using Stereo_Buffer to do the mixing). + + // Assign all oscillator outputs to specified buffer(s). If buffer + // is NULL, silences all oscillators. + void output( Blip_Buffer* mono ); + void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + + // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, + // which refer to Square 1, Square 2, Square 3, and Noise. If buffer is NULL, + // silences oscillator. + enum { osc_count = 4 }; + void osc_output( int index, Blip_Buffer* mono ); + void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + + // Reset oscillators and internal state + void reset( unsigned noise_feedback = 0, int noise_width = 0 ); + + // Write GameGear left/right assignment byte + void write_ggstereo( blip_time_t, int ); + + // Write to data port + void write_data( blip_time_t, int ); + + // Run all oscillators up to specified time, end current frame, then + // start a new frame at time 0. + void end_frame( blip_time_t ); + +public: + Sms_Apu(); + ~Sms_Apu(); +private: + // noncopyable + Sms_Apu( const Sms_Apu& ); + Sms_Apu& operator = ( const Sms_Apu& ); + + Sms_Osc* oscs [osc_count]; + Sms_Square squares [3]; + Sms_Square::Synth square_synth; // used by squares + blip_time_t last_time; + int latch; + Sms_Noise noise; + unsigned noise_feedback; + unsigned looped_feedback; + + void run_until( blip_time_t ); +}; + +struct sms_apu_state_t +{ + unsigned char regs [8] [2]; + unsigned char latch; +}; + +inline void Sms_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } + +inline void Sms_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } + +#endif diff --git a/thirdparty/game-music-emu/gme/Sms_Oscs.h b/thirdparty/game-music-emu/gme/Sms_Oscs.h new file mode 100644 index 000000000..2a896fef3 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Sms_Oscs.h @@ -0,0 +1,49 @@ +// Private oscillators used by Sms_Apu + +// Sms_Snd_Emu 0.1.4 +#ifndef SMS_OSCS_H +#define SMS_OSCS_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct Sms_Osc +{ + Blip_Buffer* outputs [4]; // NULL, right, left, center + Blip_Buffer* output; + int output_select; + + int delay; + int last_amp; + int volume; + + Sms_Osc(); + void reset(); +}; + +struct Sms_Square : Sms_Osc +{ + int period; + int phase; + + typedef Blip_Synth Synth; + const Synth* synth; + + void reset(); + void run( blip_time_t, blip_time_t ); +}; + +struct Sms_Noise : Sms_Osc +{ + const int* period; + unsigned shifter; + unsigned feedback; + + typedef Blip_Synth Synth; + Synth synth; + + void reset(); + void run( blip_time_t, blip_time_t ); +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Snes_Spc.cpp b/thirdparty/game-music-emu/gme/Snes_Spc.cpp new file mode 100644 index 000000000..0b2077d8c --- /dev/null +++ b/thirdparty/game-music-emu/gme/Snes_Spc.cpp @@ -0,0 +1,380 @@ +// SPC emulation support: init, sample buffering, reset, SPC loading + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Snes_Spc.h" + +#include + +/* Copyright (C) 2004-2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#define RAM (m.ram.ram) +#define REGS (m.smp_regs [0]) +#define REGS_IN (m.smp_regs [1]) + +// (n ? n : 256) +#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) + + +//// Init + +blargg_err_t Snes_Spc::init() +{ + memset( &m, 0, sizeof m ); + dsp.init( RAM ); + + m.tempo = tempo_unit; + + // Most SPC music doesn't need ROM, and almost all the rest only rely + // on these two bytes + m.rom [0x3E] = 0xFF; + m.rom [0x3F] = 0xC0; + + static unsigned char const cycle_table [128] = + {// 01 23 45 67 89 AB CD EF + 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0 + 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x46, // 1 + 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x74, // 2 + 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x38, // 3 + 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x66, // 4 + 0x48,0x47,0x45,0x56,0x55,0x45,0x22,0x43, // 5 + 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x75, // 6 + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x36, // 7 + 0x28,0x47,0x34,0x36,0x26,0x54,0x52,0x45, // 8 + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0xC5, // 9 + 0x38,0x47,0x34,0x36,0x26,0x44,0x52,0x44, // A + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x34, // B + 0x38,0x47,0x45,0x47,0x25,0x64,0x52,0x49, // C + 0x48,0x47,0x56,0x67,0x45,0x55,0x22,0x83, // D + 0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E + 0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F + }; + + // unpack cycle table + for ( int i = 0; i < 128; i++ ) + { + int n = cycle_table [i]; + m.cycle_table [i * 2 + 0] = n >> 4; + m.cycle_table [i * 2 + 1] = n & 0x0F; + } + + #if SPC_LESS_ACCURATE + memcpy( reg_times, reg_times_, sizeof reg_times ); + #endif + + reset(); + return 0; +} + +void Snes_Spc::init_rom( uint8_t const in [rom_size] ) +{ + memcpy( m.rom, in, sizeof m.rom ); +} + +void Snes_Spc::set_tempo( int t ) +{ + m.tempo = t; + int const timer2_shift = 4; // 64 kHz + int const other_shift = 3; // 8 kHz + + #if SPC_DISABLE_TEMPO + m.timers [2].prescaler = timer2_shift; + m.timers [1].prescaler = timer2_shift + other_shift; + m.timers [0].prescaler = timer2_shift + other_shift; + #else + if ( !t ) + t = 1; + int const timer2_rate = 1 << timer2_shift; + int rate = (timer2_rate * tempo_unit + (t >> 1)) / t; + if ( rate < timer2_rate / 4 ) + rate = timer2_rate / 4; // max 4x tempo + m.timers [2].prescaler = rate; + m.timers [1].prescaler = rate << other_shift; + m.timers [0].prescaler = rate << other_shift; + #endif +} + +// Timer registers have been loaded. Applies these to the timers. Does not +// reset timer prescalers or dividers. +void Snes_Spc::timers_loaded() +{ + int i; + for ( i = 0; i < timer_count; i++ ) + { + Timer* t = &m.timers [i]; + t->period = IF_0_THEN_256( REGS [r_t0target + i] ); + t->enabled = REGS [r_control] >> i & 1; + t->counter = REGS_IN [r_t0out + i] & 0x0F; + } + + set_tempo( m.tempo ); +} + +// Loads registers from unified 16-byte format +void Snes_Spc::load_regs( uint8_t const in [reg_count] ) +{ + memcpy( REGS, in, reg_count ); + memcpy( REGS_IN, REGS, reg_count ); + + // These always read back as 0 + REGS_IN [r_test ] = 0; + REGS_IN [r_control ] = 0; + REGS_IN [r_t0target] = 0; + REGS_IN [r_t1target] = 0; + REGS_IN [r_t2target] = 0; +} + +// RAM was just loaded from SPC, with $F0-$FF containing SMP registers +// and timer counts. Copies these to proper registers. +void Snes_Spc::ram_loaded() +{ + m.rom_enabled = 0; + load_regs( &RAM [0xF0] ); + + // Put STOP instruction around memory to catch PC underflow/overflow + memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); + memset( m.ram.ram + 0x10000, cpu_pad_fill, sizeof m.ram.padding1 ); +} + +// Registers were just loaded. Applies these new values. +void Snes_Spc::regs_loaded() +{ + enable_rom( REGS [r_control] & 0x80 ); + timers_loaded(); +} + +void Snes_Spc::reset_time_regs() +{ + m.cpu_error = 0; + m.echo_accessed = 0; + m.spc_time = 0; + m.dsp_time = 0; + #if SPC_LESS_ACCURATE + m.dsp_time = clocks_per_sample + 1; + #endif + + for ( int i = 0; i < timer_count; i++ ) + { + Timer* t = &m.timers [i]; + t->next_time = 1; + t->divider = 0; + } + + regs_loaded(); + + m.extra_clocks = 0; + reset_buf(); +} + +void Snes_Spc::reset_common( int timer_counter_init ) +{ + int i; + for ( i = 0; i < timer_count; i++ ) + REGS_IN [r_t0out + i] = timer_counter_init; + + // Run IPL ROM + memset( &m.cpu_regs, 0, sizeof m.cpu_regs ); + m.cpu_regs.pc = rom_addr; + + REGS [r_test ] = 0x0A; + REGS [r_control] = 0xB0; // ROM enabled, clear ports + for ( i = 0; i < port_count; i++ ) + REGS_IN [r_cpuio0 + i] = 0; + + reset_time_regs(); +} + +void Snes_Spc::soft_reset() +{ + reset_common( 0 ); + dsp.soft_reset(); +} + +void Snes_Spc::reset() +{ + memset( RAM, 0xFF, 0x10000 ); + ram_loaded(); + reset_common( 0x0F ); + dsp.reset(); +} + +char const Snes_Spc::signature [signature_size + 1] = + "SNES-SPC700 Sound File Data v0.30\x1A\x1A"; + +blargg_err_t Snes_Spc::load_spc( void const* data, long size ) +{ + spc_file_t const* const spc = (spc_file_t const*) data; + + // be sure compiler didn't insert any padding into fle_t + assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 ); + + // Check signature and file size + if ( size < signature_size || memcmp( spc, signature, 27 ) ) + return "Not an SPC file"; + + if ( size < spc_min_file_size ) + return "Corrupt SPC file"; + + // CPU registers + m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl; + m.cpu_regs.a = spc->a; + m.cpu_regs.x = spc->x; + m.cpu_regs.y = spc->y; + m.cpu_regs.psw = spc->psw; + m.cpu_regs.sp = spc->sp; + + // RAM and registers + memcpy( RAM, spc->ram, 0x10000 ); + ram_loaded(); + + // DSP registers + dsp.load( spc->dsp ); + + reset_time_regs(); + + return 0; +} + +void Snes_Spc::clear_echo() +{ + if ( !(dsp.read( Spc_Dsp::r_flg ) & 0x20) ) + { + int addr = 0x100 * dsp.read( Spc_Dsp::r_esa ); + int end = addr + 0x800 * (dsp.read( Spc_Dsp::r_edl ) & 0x0F); + if ( end > 0x10000 ) + end = 0x10000; + memset( &RAM [addr], 0xFF, end - addr ); + } +} + + +//// Sample output + +void Snes_Spc::reset_buf() +{ + // Start with half extra buffer of silence + sample_t* out = m.extra_buf; + while ( out < &m.extra_buf [extra_size / 2] ) + *out++ = 0; + + m.extra_pos = out; + m.buf_begin = 0; + + dsp.set_output( 0, 0 ); +} + +void Snes_Spc::set_output( sample_t* out, int size ) +{ + require( (size & 1) == 0 ); // size must be even + + m.extra_clocks &= clocks_per_sample - 1; + if ( out ) + { + sample_t const* out_end = out + size; + m.buf_begin = out; + m.buf_end = out_end; + + // Copy extra to output + sample_t const* in = m.extra_buf; + while ( in < m.extra_pos && out < out_end ) + *out++ = *in++; + + // Handle output being full already + if ( out >= out_end ) + { + // Have DSP write to remaining extra space + out = dsp.extra(); + out_end = &dsp.extra() [extra_size]; + + // Copy any remaining extra samples as if DSP wrote them + while ( in < m.extra_pos ) + *out++ = *in++; + assert( out <= out_end ); + } + + dsp.set_output( out, out_end - out ); + } + else + { + reset_buf(); + } +} + +void Snes_Spc::save_extra() +{ + // Get end pointers + sample_t const* main_end = m.buf_end; // end of data written to buf + sample_t const* dsp_end = dsp.out_pos(); // end of data written to dsp.extra() + if ( m.buf_begin <= dsp_end && dsp_end <= main_end ) + { + main_end = dsp_end; + dsp_end = dsp.extra(); // nothing in DSP's extra + } + + // Copy any extra samples at these ends into extra_buf + sample_t* out = m.extra_buf; + sample_t const* in; + for ( in = m.buf_begin + sample_count(); in < main_end; in++ ) + *out++ = *in; + for ( in = dsp.extra(); in < dsp_end ; in++ ) + *out++ = *in; + + m.extra_pos = out; + assert( out <= &m.extra_buf [extra_size] ); +} + +blargg_err_t Snes_Spc::play( int count, sample_t* out ) +{ + require( (count & 1) == 0 ); // must be even + if ( count ) + { + set_output( out, count ); + end_frame( count * (clocks_per_sample / 2) ); + } + + const char* err = m.cpu_error; + m.cpu_error = 0; + return err; +} + +blargg_err_t Snes_Spc::skip( int count ) +{ + #if SPC_LESS_ACCURATE + if ( count > 2 * sample_rate * 2 ) + { + set_output( 0, 0 ); + + // Skip a multiple of 4 samples + time_t end = count; + count = (count & 3) + 1 * sample_rate * 2; + end = (end - count) * (clocks_per_sample / 2); + + m.skipped_kon = 0; + m.skipped_koff = 0; + + // Preserve DSP and timer synchronization + // TODO: verify that this really preserves it + int old_dsp_time = m.dsp_time + m.spc_time; + m.dsp_time = end - m.spc_time + skipping_time; + end_frame( end ); + m.dsp_time = m.dsp_time - skipping_time + old_dsp_time; + + dsp.write( Spc_Dsp::r_koff, m.skipped_koff & ~m.skipped_kon ); + dsp.write( Spc_Dsp::r_kon , m.skipped_kon ); + clear_echo(); + } + #endif + + return play( count, 0 ); +} diff --git a/thirdparty/game-music-emu/gme/Snes_Spc.h b/thirdparty/game-music-emu/gme/Snes_Spc.h new file mode 100644 index 000000000..68c780ab7 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Snes_Spc.h @@ -0,0 +1,283 @@ +// SNES SPC-700 APU emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef SNES_SPC_H +#define SNES_SPC_H + +#include "Spc_Dsp.h" +#include "blargg_endian.h" + +#include + +struct Snes_Spc { +public: + // Must be called once before using + blargg_err_t init(); + + // Sample pairs generated per second + enum { sample_rate = 32000 }; + +// Emulator use + + // Sets IPL ROM data. Library does not include ROM data. Most SPC music files + // don't need ROM, but a full emulator must provide this. + enum { rom_size = 0x40 }; + void init_rom( uint8_t const rom [rom_size] ); + + // Sets destination for output samples + typedef short sample_t; + void set_output( sample_t* out, int out_size ); + + // Number of samples written to output since last set + int sample_count() const; + + // Resets SPC to power-on state. This resets your output buffer, so you must + // call set_output() after this. + void reset(); + + // Emulates pressing reset switch on SNES. This resets your output buffer, so + // you must call set_output() after this. + void soft_reset(); + + // 1024000 SPC clocks per second, sample pair every 32 clocks + typedef int time_t; + enum { clock_rate = 1024000 }; + enum { clocks_per_sample = 32 }; + + // Emulated port read/write at specified time + enum { port_count = 4 }; + int read_port ( time_t, int port ); + void write_port( time_t, int port, int data ); + + // Runs SPC to end_time and starts a new time frame at 0 + void end_frame( time_t end_time ); + +// Sound control + + // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). + // Reduces emulation accuracy. + enum { voice_count = 8 }; + void mute_voices( int mask ); + + // If true, prevents channels and global volumes from being phase-negated. + // Only supported by fast DSP. + void disable_surround( bool disable = true ); + + // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc. + enum { tempo_unit = 0x100 }; + void set_tempo( int ); + +// SPC music files + + // Loads SPC data into emulator + enum { spc_min_file_size = 0x10180 }; + enum { spc_file_size = 0x10200 }; + blargg_err_t load_spc( void const* in, long size ); + + // Clears echo region. Useful after loading an SPC as many have garbage in echo. + void clear_echo(); + + // Plays for count samples and write samples to out. Discards samples if out + // is NULL. Count must be a multiple of 2 since output is stereo. + blargg_err_t play( int count, sample_t* out ); + + // Skips count samples. Several times faster than play() when using fast DSP. + blargg_err_t skip( int count ); + +// State save/load (only available with accurate DSP) + +#if !SPC_NO_COPY_STATE_FUNCS + // Saves/loads state + enum { state_size = 67 * 1024L }; // maximum space needed when saving + typedef Spc_Dsp::copy_func_t copy_func_t; + void copy_state( unsigned char** io, copy_func_t ); + + // Writes minimal header to spc_out + static void init_header( void* spc_out ); + + // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out. + // Does not set up SPC header; use init_header() for that. + void save_spc( void* spc_out ); + + // Returns true if new key-on events occurred since last check. Useful for + // trimming silence while saving an SPC. + bool check_kon(); +#endif + +public: + // TODO: document + struct regs_t + { + uint16_t pc; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t psw; + uint8_t sp; + }; + regs_t& smp_regs() { return m.cpu_regs; } + + uint8_t* smp_ram() { return m.ram.ram; } + + void run_until( time_t t ) { run_until_( t ); } +public: + BLARGG_DISABLE_NOTHROW + + // Time relative to m_spc_time. Speeds up code a bit by eliminating need to + // constantly add m_spc_time to time from CPU. CPU uses time that ends at + // 0 to eliminate reloading end time every instruction. It pays off. + typedef int rel_time_t; + + struct Timer + { + rel_time_t next_time; // time of next event + int prescaler; + int period; + int divider; + int enabled; + int counter; + }; + enum { reg_count = 0x10 }; + enum { timer_count = 3 }; + enum { extra_size = Spc_Dsp::extra_size }; + + enum { signature_size = 35 }; + +private: + Spc_Dsp dsp; + + #if SPC_LESS_ACCURATE + static signed char const reg_times_ [256]; + signed char reg_times [256]; + #endif + + struct state_t + { + Timer timers [timer_count]; + + uint8_t smp_regs [2] [reg_count]; + + regs_t cpu_regs; + + rel_time_t dsp_time; + time_t spc_time; + bool echo_accessed; + + int tempo; + int skipped_kon; + int skipped_koff; + const char* cpu_error; + + int extra_clocks; + sample_t* buf_begin; + sample_t const* buf_end; + sample_t* extra_pos; + sample_t extra_buf [extra_size]; + + int rom_enabled; + uint8_t rom [rom_size]; + uint8_t hi_ram [rom_size]; + + unsigned char cycle_table [256]; + + struct + { + // padding to neutralize address overflow -- but this is + // still undefined behavior! TODO: remove and instead properly + // guard usage of emulated memory + uint8_t padding1 [0x100]; + alignas(uint16_t) uint8_t ram [0x10000 + 0x100]; + } ram; + }; + state_t m; + + enum { rom_addr = 0xFFC0 }; + + enum { skipping_time = 127 }; + + // Value that padding should be filled with + enum { cpu_pad_fill = 0xFF }; + + enum { + r_test = 0x0, r_control = 0x1, + r_dspaddr = 0x2, r_dspdata = 0x3, + r_cpuio0 = 0x4, r_cpuio1 = 0x5, + r_cpuio2 = 0x6, r_cpuio3 = 0x7, + r_f8 = 0x8, r_f9 = 0x9, + r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC, + r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF + }; + + void timers_loaded(); + void enable_rom( int enable ); + void reset_buf(); + void save_extra(); + void load_regs( uint8_t const in [reg_count] ); + void ram_loaded(); + void regs_loaded(); + void reset_time_regs(); + void reset_common( int timer_counter_init ); + + Timer* run_timer_ ( Timer* t, rel_time_t ); + Timer* run_timer ( Timer* t, rel_time_t ); + int dsp_read ( rel_time_t ); + void dsp_write ( int data, rel_time_t ); + void cpu_write_smp_reg_( int data, rel_time_t, uint16_t addr ); + void cpu_write_smp_reg ( int data, rel_time_t, uint16_t addr ); + void cpu_write_high ( int data, uint8_t i ); + void cpu_write ( int data, uint16_t addr, rel_time_t ); + int cpu_read_smp_reg ( int i, rel_time_t ); + int cpu_read ( uint16_t addr, rel_time_t ); + unsigned CPU_mem_bit ( uint16_t pc, rel_time_t ); + + bool check_echo_access ( int addr ); + uint8_t* run_until_( time_t end_time ); + + struct spc_file_t + { + char signature [signature_size]; + uint8_t has_id666; + uint8_t version; + uint8_t pcl, pch; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t psw; + uint8_t sp; + char text [212]; + uint8_t ram [0x10000]; + uint8_t dsp [128]; + uint8_t unused [0x40]; + uint8_t ipl_rom [0x40]; + }; + + static char const signature [signature_size + 1]; + + void save_regs( uint8_t out [reg_count] ); +}; + +#include + +inline int Snes_Spc::sample_count() const { return (m.extra_clocks >> 5) * 2; } + +inline int Snes_Spc::read_port( time_t t, int port ) +{ + assert( (unsigned) port < port_count ); + return run_until_( t ) [port]; +} + +inline void Snes_Spc::write_port( time_t t, int port, int data ) +{ + assert( (unsigned) port < port_count ); + run_until_( t ) [0x10 + port] = data; +} + +inline void Snes_Spc::mute_voices( int mask ) { dsp.mute_voices( mask ); } + +inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); } + +#if !SPC_NO_COPY_STATE_FUNCS +inline bool Snes_Spc::check_kon() { return dsp.check_kon(); } +#endif + +#endif diff --git a/thirdparty/game-music-emu/gme/Spc_Cpu.cpp b/thirdparty/game-music-emu/gme/Spc_Cpu.cpp new file mode 100644 index 000000000..998fe121b --- /dev/null +++ b/thirdparty/game-music-emu/gme/Spc_Cpu.cpp @@ -0,0 +1,549 @@ +// Core SPC emulation: CPU, timers, SMP registers, memory + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Snes_Spc.h" + +#include + +/* Copyright (C) 2004-2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#define RAM (m.ram.ram) +#define REGS (m.smp_regs [0]) +#define REGS_IN (m.smp_regs [1]) + +// (n ? n : 256) +#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) + +// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which +// do crazy echo buffer accesses. +#ifndef SPC_MORE_ACCURACY + #define SPC_MORE_ACCURACY 0 +#endif + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + + +//// Timers + +#if SPC_DISABLE_TEMPO + #define TIMER_DIV( t, n ) ((n) >> t->prescaler) + #define TIMER_MUL( t, n ) ((n) << t->prescaler) +#else + #define TIMER_DIV( t, n ) ((n) / t->prescaler) + #define TIMER_MUL( t, n ) ((n) * t->prescaler) +#endif + +Snes_Spc::Timer* Snes_Spc::run_timer_( Timer* t, rel_time_t time ) +{ + int elapsed = TIMER_DIV( t, time - t->next_time ) + 1; + t->next_time += TIMER_MUL( t, elapsed ); + + if ( t->enabled ) + { + int remain = IF_0_THEN_256( t->period - t->divider ); + int divider = t->divider + elapsed; + int over = elapsed - remain; + if ( over >= 0 ) + { + int n = over / t->period; + t->counter = (t->counter + 1 + n) & 0x0F; + divider = over - n * t->period; + } + t->divider = (uint8_t) divider; + } + return t; +} + +inline Snes_Spc::Timer* Snes_Spc::run_timer( Timer* t, rel_time_t time ) +{ + if ( time >= t->next_time ) + t = run_timer_( t, time ); + return t; +} + + +//// ROM + +void Snes_Spc::enable_rom( int enable ) +{ + if ( m.rom_enabled != enable ) + { + m.rom_enabled = enable; + if ( enable ) + memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram ); + memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size ); + // TODO: ROM can still get overwritten when DSP writes to echo buffer + } +} + + +//// DSP + +#if SPC_LESS_ACCURATE + int const max_reg_time = 29; + + signed char const Snes_Spc::reg_times_ [256] = + { + -1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22, + 2, 3, 0, 1,-12, 0, 1, 1, 7, 6, 14, 14, 27, 14, 14, 23, + 5, 6, 3, 4, -1, 3, 4, 4, 10, 9, 14, 14, 26, -5, 14, 23, + 8, 9, 6, 7, 2, 6, 7, 7, 13, 12, 14, 14, 27, -4, 14, 24, + 11, 12, 9, 10, 5, 9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24, + 14, 15, 12, 13, 8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24, + 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25, + 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25, + + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + }; + + #define RUN_DSP( time, offset ) \ + int count = (time) - (offset) - m.dsp_time;\ + if ( count >= 0 )\ + {\ + int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\ + m.dsp_time += clock_count;\ + dsp.run( clock_count );\ + } +#else + #define RUN_DSP( time, offset ) \ + {\ + int count = (time) - m.dsp_time;\ + if ( !SPC_MORE_ACCURACY || count )\ + {\ + assert( count > 0 );\ + m.dsp_time = (time);\ + dsp.run( count );\ + }\ + } +#endif + +int Snes_Spc::dsp_read( rel_time_t time ) +{ + RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] ); + + int result = dsp.read( REGS [r_dspaddr] & 0x7F ); + + #ifdef SPC_DSP_READ_HOOK + SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result ); + #endif + + return result; +} + +inline void Snes_Spc::dsp_write( int data, rel_time_t time ) +{ + RUN_DSP( time, reg_times [REGS [r_dspaddr]] ) + #if SPC_LESS_ACCURATE + else if ( m.dsp_time == skipping_time ) + { + int r = REGS [r_dspaddr]; + if ( r == Spc_Dsp::r_kon ) + m.skipped_kon |= data & ~dsp.read( Spc_Dsp::r_koff ); + + if ( r == Spc_Dsp::r_koff ) + { + m.skipped_koff |= data; + m.skipped_kon &= ~data; + } + } + #endif + + #ifdef SPC_DSP_WRITE_HOOK + SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data ); + #endif + + if ( REGS [r_dspaddr] <= 0x7F ) + dsp.write( REGS [r_dspaddr], data ); + else if ( !SPC_MORE_ACCURACY ) + debug_printf( "SPC wrote to DSP register > $7F\n" ); +} + + +//// Memory access extras + +#if SPC_MORE_ACCURACY + #define MEM_ACCESS( time, addr ) \ + {\ + if ( time >= m.dsp_time )\ + {\ + RUN_DSP( time, max_reg_time );\ + }\ + } +#elif !defined (NDEBUG) + // Debug-only check for read/write within echo buffer, since this might result in + // inaccurate emulation due to the DSP not being caught up to the present. + + bool Snes_Spc::check_echo_access( int addr ) + { + if ( !(dsp.read( Spc_Dsp::r_flg ) & 0x20) ) + { + int start = 0x100 * dsp.read( Spc_Dsp::r_esa ); + int size = 0x800 * (dsp.read( Spc_Dsp::r_edl ) & 0x0F); + int end = start + (size ? size : 4); + if ( start <= addr && addr < end ) + { + if ( !m.echo_accessed ) + { + m.echo_accessed = 1; + return true; + } + } + } + return false; + } + + #define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) ); +#else + #define MEM_ACCESS( time, addr ) +#endif + + +//// CPU write + +#if SPC_MORE_ACCURACY +static unsigned char const glitch_probs [3] [256] = +{ + 0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B, + 0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08, + 0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09, + 0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01, + 0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05, + 0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07, + 0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07, + 0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01, + 0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09, + 0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08, + 0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03, + 0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03, + 0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07, + 0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02, + 0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02, + 0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01, + + 0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07, + 0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06, + 0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09, + 0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03, + 0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07, + 0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03, + 0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06, + 0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03, + 0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05, + 0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04, + 0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05, + 0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01, + 0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05, + 0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01, + 0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03, + 0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01, + + 0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A, + 0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A, + 0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A, + 0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09, + 0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09, + 0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02, + 0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07, + 0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04, + 0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A, + 0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07, + 0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04, + 0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02, + 0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06, + 0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03, + 0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02, + 0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03, +}; +#endif + +// Read/write handlers are divided into multiple functions to keep rarely-used +// functionality separate so often-used functionality can be optimized better +// by compiler. + +// If write isn't preceded by read, data has this added to it +int const no_read_before_write = 0x2000; + +void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr ) +{ + switch ( addr ) + { + case r_t0target: + case r_t1target: + case r_t2target: { + Timer* t = &m.timers [addr - r_t0target]; + int period = IF_0_THEN_256( data ); + if ( t->period != period ) + { + t = run_timer( t, time ); + #if SPC_MORE_ACCURACY + // Insane behavior when target is written just after counter is + // clocked and counter matches new period and new period isn't 1, 2, 4, or 8 + if ( t->divider == (period & 0xFF) && + t->next_time == time + TIMER_MUL( t, 1 ) && + ((period - 1) | ~0x0F) & period ) + { + //debug_printf( "SPC pathological timer target write\n" ); + + // If the period is 3, 5, or 9, there's a probability this behavior won't occur, + // based on the previous period + int prob = 0xFF; + int old_period = t->period & 0xFF; + if ( period == 3 ) prob = glitch_probs [0] [old_period]; + if ( period == 5 ) prob = glitch_probs [1] [old_period]; + if ( period == 9 ) prob = glitch_probs [2] [old_period]; + + // The glitch suppresses incrementing of one of the counter bits, based on + // the lowest set bit in the new period + int b = 1; + while ( !(period & b) ) + b <<= 1; + + if ( (rand() >> 4 & 0xFF) <= prob ) + t->divider = (t->divider - b) & 0xFF; + } + #endif + t->period = period; + } + break; + } + + case r_t0out: + case r_t1out: + case r_t2out: + if ( !SPC_MORE_ACCURACY ) + debug_printf( "SPC wrote to counter %d\n", (int) addr - r_t0out ); + + if ( data < no_read_before_write / 2 ) + run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0; + break; + + // Registers that act like RAM + case 0x8: + case 0x9: + REGS_IN [addr] = (uint8_t) data; + break; + + case r_test: + if ( (uint8_t) data != 0x0A ) + debug_printf( "SPC wrote to test register\n" ); + break; + + case r_control: + // port clears + if ( data & 0x10 ) + { + REGS_IN [r_cpuio0] = 0; + REGS_IN [r_cpuio1] = 0; + } + if ( data & 0x20 ) + { + REGS_IN [r_cpuio2] = 0; + REGS_IN [r_cpuio3] = 0; + } + + // timers + { + for ( int i = 0; i < timer_count; i++ ) + { + Timer* t = &m.timers [i]; + int enabled = data >> i & 1; + if ( t->enabled != enabled ) + { + t = run_timer( t, time ); + t->enabled = enabled; + if ( enabled ) + { + t->divider = 0; + t->counter = 0; + } + } + } + } + enable_rom( data & 0x80 ); + break; + } +} + +void Snes_Spc::cpu_write_smp_reg( int data, rel_time_t time, uint16_t addr ) +{ + if ( addr == r_dspdata ) // 99% + dsp_write( data, time ); + else + cpu_write_smp_reg_( data, time, addr ); +} + +void Snes_Spc::cpu_write_high( int data, uint8_t i ) +{ + assert ( i < rom_size ); + m.hi_ram [i] = (uint8_t) data; + if ( m.rom_enabled ) + RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM +} + +void Snes_Spc::cpu_write( int data, uint16_t addr, rel_time_t time ) +{ + MEM_ACCESS( time, addr ) + + // RAM + RAM [addr] = (uint8_t) data; + if ( addr >= 0xF0 ) // 64% + { + const uint16_t reg = addr - 0xF0; + // $F0-$FF + if ( reg < reg_count ) // 87% + { + REGS [reg] = (uint8_t) data; + + // Ports + #ifdef SPC_PORT_WRITE_HOOK + if ( (unsigned) (reg - r_cpuio0) < port_count ) + SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0), + (uint8_t) data, ®S [r_cpuio0] ); + #endif + + // Registers other than $F2 and $F4-$F7 + if ( reg != 2 && (reg < 4 || reg > 7) ) // 36% + cpu_write_smp_reg( data, time, reg ); + } + // High mem/address wrap-around + else if ( addr >= rom_addr ) // 1% in IPL ROM area or address wrapped around + cpu_write_high( data, addr - rom_addr ); + } +} + + +//// CPU read + +inline int Snes_Spc::cpu_read_smp_reg( int reg, rel_time_t time ) +{ + int result = REGS_IN [reg]; + reg -= r_dspaddr; + // DSP addr and data + if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3 + { + result = REGS [r_dspaddr]; + if ( (unsigned) reg == 1 ) + result = dsp_read( time ); // 0xF3 + } + return result; +} + +int Snes_Spc::cpu_read( uint16_t addr, rel_time_t time ) +{ + MEM_ACCESS( time, addr ) + + // RAM + int result = RAM [addr]; + int reg = addr - 0xF0; + if ( reg >= 0 ) // 40% + { + reg -= 0x10; + if ( (unsigned) reg >= 0xFF00 ) // 21% + { + reg += 0x10 - r_t0out; + + // Timers + if ( (unsigned) reg < timer_count ) // 90% + { + Timer* t = &m.timers [reg]; + if ( time >= t->next_time ) + t = run_timer_( t, time ); + result = t->counter; + t->counter = 0; + } + // Other registers + else if ( reg < 0 ) // 10% + { + result = cpu_read_smp_reg( reg + r_t0out, time ); + } + else // 1% + { + assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 ); + result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time ); + } + } + } + + return result; +} + + +//// Run + +// Prefix and suffix for CPU emulator function +#define SPC_CPU_RUN_FUNC \ +uint8_t* Snes_Spc::run_until_( time_t end_time )\ +{\ + rel_time_t rel_time = m.spc_time - end_time;\ + assert( rel_time <= 0 );\ + m.spc_time = end_time;\ + m.dsp_time += rel_time;\ + m.timers [0].next_time += rel_time;\ + m.timers [1].next_time += rel_time;\ + m.timers [2].next_time += rel_time; + +#define SPC_CPU_RUN_FUNC_END \ + m.spc_time += rel_time;\ + m.dsp_time -= rel_time;\ + m.timers [0].next_time -= rel_time;\ + m.timers [1].next_time -= rel_time;\ + m.timers [2].next_time -= rel_time;\ + assert( m.spc_time <= end_time );\ + return ®S [r_cpuio0];\ +} + +int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks + +void Snes_Spc::end_frame( time_t end_time ) +{ + // Catch CPU up to as close to end as possible. If final instruction + // would exceed end, does NOT execute it and leaves m.spc_time < end. + if ( end_time > m.spc_time ) + run_until_( end_time ); + + m.spc_time -= end_time; + m.extra_clocks += end_time; + + // Greatest number of clocks early that emulation can stop early due to + // not being able to execute current instruction without going over + // allowed time. + assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 ); + + // Catch timers up to CPU + for ( int i = 0; i < timer_count; i++ ) + run_timer( &m.timers [i], 0 ); + + // Catch DSP up to CPU + if ( m.dsp_time < 0 ) + { + RUN_DSP( 0, max_reg_time ); + } + + // Save any extra samples beyond what should be generated + if ( m.buf_begin ) + save_extra(); +} + +// Inclusion here allows static memory access functions and better optimization +#include "Spc_Cpu.h" diff --git a/thirdparty/game-music-emu/gme/Spc_Cpu.h b/thirdparty/game-music-emu/gme/Spc_Cpu.h new file mode 100644 index 000000000..2dd3e63c2 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Spc_Cpu.h @@ -0,0 +1,1182 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +/* Copyright (C) 2004-2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +//// Memory access + +#if SPC_MORE_ACCURACY + #define SUSPICIOUS_OPCODE( name ) ((void) 0) +#else + #define SUSPICIOUS_OPCODE( name ) debug_printf( "SPC: suspicious opcode: " name "\n" ) +#endif + +#define CPU_READ( time, offset, addr )\ + cpu_read( addr, time + offset ) + +#define CPU_WRITE( time, offset, addr, data )\ + cpu_write( data, addr, time + offset ) + +#if SPC_MORE_ACCURACY + #define CPU_READ_TIMER( time, offset, addr, out )\ + { out = CPU_READ( time, offset, addr ); } + +#else + // timers are by far the most common thing read from dp + #define CPU_READ_TIMER( time, offset, addr_, out )\ + {\ + rel_time_t adj_time = time + offset;\ + int dp_addr = addr_;\ + int ti = dp_addr - (r_t0out + 0xF0);\ + if ( (unsigned) ti < timer_count )\ + {\ + Timer* t = &m.timers [ti];\ + if ( adj_time >= t->next_time )\ + t = run_timer_( t, adj_time );\ + out = t->counter;\ + t->counter = 0;\ + }\ + else\ + {\ + out = ram [dp_addr];\ + int i = dp_addr - 0xF0;\ + if ( (unsigned) i < 0x10 )\ + out = cpu_read_smp_reg( i, adj_time );\ + }\ + } +#endif + +#define TIME_ADJ( n ) (n) + +#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out ) +#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) ) +#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) ) + +#define DP_ADDR( addr ) (dp + (addr)) + +#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out ) +#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) ) +#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data ) + +#define READ_PROG16( addr ) (RAM [(addr) & 0xffff] | (RAM [((addr) + 1) & 0xffff] << 8)) + +#define SET_PC( n ) (pc = n) +#define GET_PC() (pc) +#define READ_PC( pc ) (ram [pc]) +#define READ_PC16( pc ) READ_PROG16( pc ) + +#define SET_SP( v ) (sp = v) +#define GET_SP() ((uint8_t) (sp)) + +#define PUSH16( data )\ +{\ + PUSH( (data & 0xff00) >> 8 );\ + PUSH( data & 0xff );\ +} + +#define PUSH( data )\ +{\ + ram [0x100 + sp] = (uint8_t) (data);\ + --sp;\ +} + +#define POP( out )\ +{\ + ++sp;\ + out = ram [0x100 + sp];\ +} + +#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) + +unsigned Snes_Spc::CPU_mem_bit( uint16_t pc, rel_time_t rel_time ) +{ + unsigned addr = READ_PC16( pc ); + unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); + return t << 8 & 0x100; +} + +//// Status flag handling + +// Hex value in name to clarify code and bit shifting. +// Flag stored in indicated variable during emulation +int const n80 = 0x80; // nz +int const v40 = 0x40; // psw +int const p20 = 0x20; // dp +int const b10 = 0x10; // psw +int const h08 = 0x08; // psw +int const i04 = 0x04; // psw +int const z02 = 0x02; // nz +int const c01 = 0x01; // c + +int const nz_neg_mask = 0x880; // either bit set indicates N flag set + +#define GET_PSW( out )\ +{\ + out = psw & ~(n80 | p20 | z02 | c01);\ + out |= c >> 8 & c01;\ + out |= dp >> 3 & p20;\ + out |= ((nz >> 4) | nz) & n80;\ + if ( !(uint8_t) nz ) out |= z02;\ +} + +#define SET_PSW( in )\ +{\ + psw = in;\ + c = in << 8;\ + dp = in << 3 & 0x100;\ + nz = (in << 4 & 0x800) | (~in & z02);\ +} + +SPC_CPU_RUN_FUNC +{ + uint8_t* const ram = RAM; + uint8_t a = m.cpu_regs.a; + uint8_t x = m.cpu_regs.x; + uint8_t y = m.cpu_regs.y; + uint16_t pc; + uint8_t sp; + int psw; + int c; + int nz; + int dp; + + SET_PC( m.cpu_regs.pc ); + SET_SP( m.cpu_regs.sp ); + SET_PSW( m.cpu_regs.psw ); + + goto loop; + + + // Main loop + +cbranch_taken_loop: + pc += (int8_t) ram [pc]; +inc_pc_loop: + pc++; +loop: +{ + unsigned opcode; + unsigned data; + + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + check( (unsigned) y < 0x100 ); + + opcode = ram [pc]; + if ( (rel_time += m.cycle_table [opcode]) > 0 ) + goto out_of_time; + + #ifdef SPC_CPU_OPCODE_HOOK + SPC_CPU_OPCODE_HOOK( GET_PC(), opcode ); + #endif + /* + //SUB_CASE_COUNTER( 1 ); + #define PROFILE_TIMER_LOOP( op, addr, len )\ + if ( opcode == op )\ + {\ + int cond = (unsigned) ((addr) - 0xFD) < 3 &&\ + pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\ + SUB_CASE_COUNTER( op && cond );\ + } + + PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 ); + PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 ); + PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 ); + */ + + // TODO: if PC is at end of memory, this will get wrong operand (very obscure) + pc++; + data = ram [pc]; + switch ( opcode ) + { + +// Common instructions + +#define BRANCH( cond )\ +{\ + pc++;\ + pc += (int8_t) data;\ + if ( cond )\ + goto loop;\ + pc -= (int8_t) data;\ + rel_time -= 2;\ + goto loop;\ +} + + case 0xF0: // BEQ + BRANCH( !(uint8_t) nz ) // 89% taken + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ) + + case 0x3F:{// CALL + int old_addr = GET_PC() + 2; + SET_PC( READ_PC16( pc ) ); + PUSH16( old_addr ); + goto loop; + } + + case 0x6F:// RET + { + uint8_t l, h; + POP( l ); + POP( h ); + SET_PC( l | (h << 8) ); + } + goto loop; + + case 0xE4: // MOV a,dp + ++pc; + // 80% from timer + READ_DP_TIMER( 0, data, a = nz ); + goto loop; + + case 0xFA:{// MOV dp,dp + int temp; + READ_DP_TIMER( -2, data, temp ); + data = temp + no_read_before_write ; + } + // fall through + case 0x8F:{// MOV dp,#imm + int temp = READ_PC( pc + 1 ); + pc += 2; + + #if !SPC_MORE_ACCURACY + { + int i = dp + temp; + ram [i] = (uint8_t) data; + i -= 0xF0; + if ( (unsigned) i < 0x10 ) // 76% + { + REGS [i] = (uint8_t) data; + + // Registers other than $F2 and $F4-$F7 + if ( i != 2 && (i < 4 || i > 7)) // 12% + cpu_write_smp_reg( data, rel_time, i ); + } + } + #else + WRITE_DP( 0, temp, data ); + #endif + goto loop; + } + + case 0xC4: // MOV dp,a + ++pc; + #if !SPC_MORE_ACCURACY + { + int i = dp + data; + ram [i] = (uint8_t) a; + i -= 0xF0; + if ( (unsigned) i < 0x10 ) // 39% + { + unsigned sel = i - 2; + REGS [i] = (uint8_t) a; + + if ( sel == 1 ) // 51% $F3 + dsp_write( a, rel_time ); + else if ( sel > 1 ) // 1% not $F2 or $F3 + cpu_write_smp_reg_( a, rel_time, i ); + } + } + #else + WRITE_DP( 0, data, a ); + #endif + goto loop; + +#define CASE( n ) case n: + +// Define common address modes based on opcode for immediate mode. Execution +// ends with data set to the address of the operand. +#define ADDR_MODES_( op )\ + CASE( op - 0x02 ) /* (X) */\ + data = x + dp;\ + pc--;\ + goto end_##op;\ + CASE( op + 0x0F ) /* (dp)+Y */\ + data = READ_PROG16( data + dp ) + y;\ + goto end_##op;\ + CASE( op - 0x01 ) /* (dp+X) */\ + data = READ_PROG16( ((uint8_t) (data + x)) + dp );\ + goto end_##op;\ + CASE( op + 0x0E ) /* abs+Y */\ + data += y;\ + goto abs_##op;\ + CASE( op + 0x0D ) /* abs+X */\ + data += x;\ + CASE( op - 0x03 ) /* abs */\ + abs_##op:\ + data += 0x100 * READ_PC( ++pc );\ + goto end_##op;\ + CASE( op + 0x0C ) /* dp+X */\ + data = (uint8_t) (data + x); + +#define ADDR_MODES_NO_DP( op )\ + ADDR_MODES_( op )\ + data += dp;\ + end_##op: + +#define ADDR_MODES( op )\ + ADDR_MODES_( op )\ + CASE( op - 0x04 ) /* dp */\ + data += dp;\ + end_##op: + +// 1. 8-bit Data Transmission Commands. Group I + + ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr + a = nz = READ( 0, data ); + goto inc_pc_loop; + + case 0xBF:{// MOV A,(X)+ + int temp = x + dp; + x = (uint8_t) (x + 1); + a = nz = READ( -1, temp ); + goto loop; + } + + case 0xE8: // MOV A,imm + a = data; + nz = data; + goto inc_pc_loop; + + case 0xF9: // MOV X,dp+Y + data = (uint8_t) (data + y); + case 0xF8: // MOV X,dp + READ_DP_TIMER( 0, data, x = nz ); + goto inc_pc_loop; + + case 0xE9: // MOV X,abs + data = READ_PC16( pc ); + ++pc; + data = READ( 0, data ); + case 0xCD: // MOV X,imm + x = data; + nz = data; + goto inc_pc_loop; + + case 0xFB: // MOV Y,dp+X + data = (uint8_t) (data + x); + case 0xEB: // MOV Y,dp + // 70% from timer + pc++; + READ_DP_TIMER( 0, data, y = nz ); + goto loop; + + case 0xEC:{// MOV Y,abs + int temp = READ_PC16( pc ); + pc += 2; + READ_TIMER( 0, temp, y = nz ); + //y = nz = READ( 0, temp ); + goto loop; + } + + case 0x8D: // MOV Y,imm + y = data; + nz = data; + goto inc_pc_loop; + +// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 + + ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A + WRITE( 0, data, a ); + goto inc_pc_loop; + + { + int temp; + case 0xCC: // MOV abs,Y + temp = y; + goto mov_abs_temp; + case 0xC9: // MOV abs,X + temp = x; + mov_abs_temp: + WRITE( 0, READ_PC16( pc ), temp ); + pc += 2; + goto loop; + } + + case 0xD9: // MOV dp+Y,X + data = (uint8_t) (data + y); + case 0xD8: // MOV dp,X + WRITE( 0, data + dp, x ); + goto inc_pc_loop; + + case 0xDB: // MOV dp+X,Y + data = (uint8_t) (data + x); + case 0xCB: // MOV dp,Y + WRITE( 0, data + dp, y ); + goto inc_pc_loop; + +// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. + + case 0x7D: // MOV A,X + a = x; + nz = x; + goto loop; + + case 0xDD: // MOV A,Y + a = y; + nz = y; + goto loop; + + case 0x5D: // MOV X,A + x = a; + nz = a; + goto loop; + + case 0xFD: // MOV Y,A + y = a; + nz = a; + goto loop; + + case 0x9D: // MOV X,SP + x = nz = GET_SP(); + goto loop; + + case 0xBD: // MOV SP,X + SET_SP( x ); + goto loop; + + //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) + + case 0xAF: // MOV (X)+,A + WRITE_DP( 0, x, a + no_read_before_write ); + x = (uint8_t) (x + 1); + goto loop; + +// 5. 8-BIT LOGIC OPERATION COMMANDS + +#define LOGICAL_OP( op, func )\ + ADDR_MODES( op ) /* addr */\ + data = READ( 0, data );\ + case op: /* imm */\ + nz = a func##= data;\ + goto inc_pc_loop;\ + { unsigned addr;\ + case op + 0x11: /* X,Y */\ + data = READ_DP( -2, y );\ + addr = x + dp;\ + goto addr_##op;\ + case op + 0x01: /* dp,dp */\ + data = READ_DP( -3, data );\ + case op + 0x10:{/*dp,imm*/\ + uint16_t addr2 = pc + 1;\ + pc += 2;\ + addr = READ_PC( addr2 ) + dp;\ + }\ + addr_##op:\ + nz = data func READ( -1, addr );\ + WRITE( 0, addr, nz );\ + goto loop;\ + } + + LOGICAL_OP( 0x28, & ); // AND + + LOGICAL_OP( 0x08, | ); // OR + + LOGICAL_OP( 0x48, ^ ); // EOR + +// 4. 8-BIT ARITHMETIC OPERATION COMMANDS + + ADDR_MODES( 0x68 ) // CMP addr + data = READ( 0, data ); + case 0x68: // CMP imm + nz = a - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x79: // CMP (X),(Y) + data = READ_DP( -2, y ); + nz = READ_DP( -1, x ) - data; + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0x69: // CMP dp,dp + data = READ_DP( -3, data ); + case 0x78: // CMP dp,imm + nz = READ_DP( -1, READ_PC( ++pc ) ) - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x3E: // CMP X,dp + data += dp; + goto cmp_x_addr; + case 0x1E: // CMP X,abs + data = READ_PC16( pc ); + pc++; + cmp_x_addr: + data = READ( 0, data ); + case 0xC8: // CMP X,imm + nz = x - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x7E: // CMP Y,dp + data += dp; + goto cmp_y_addr; + case 0x5E: // CMP Y,abs + data = READ_PC16( pc ); + pc++; + cmp_y_addr: + data = READ( 0, data ); + case 0xAD: // CMP Y,imm + nz = y - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + { + int addr; + case 0xB9: // SBC (x),(y) + case 0x99: // ADC (x),(y) + pc--; // compensate for inc later + data = READ_DP( -2, y ); + addr = x + dp; + goto adc_addr; + case 0xA9: // SBC dp,dp + case 0x89: // ADC dp,dp + data = READ_DP( -3, data ); + case 0xB8: // SBC dp,imm + case 0x98: // ADC dp,imm + addr = READ_PC( ++pc ) + dp; + adc_addr: + nz = READ( -1, addr ); + goto adc_data; + +// catch ADC and SBC together, then decode later based on operand +#undef CASE +#define CASE( n ) case n: case (n) + 0x20: + ADDR_MODES( 0x88 ) // ADC/SBC addr + data = READ( 0, data ); + case 0xA8: // SBC imm + case 0x88: // ADC imm + addr = -1; // A + nz = a; + adc_data: { + int flags; + if ( opcode >= 0xA0 ) // SBC + data ^= 0xFF; + + flags = data ^ nz; + nz += data + (c >> 8 & 1); + flags ^= nz; + + psw = (psw & ~(v40 | h08)) | + (flags >> 1 & h08) | + ((flags + 0x80) >> 2 & v40); + c = nz; + if ( addr < 0 ) + { + a = (uint8_t) nz; + goto inc_pc_loop; + } + WRITE( 0, addr, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + } + + } + +// 6. ADDITION & SUBTRACTION COMMANDS + +#define INC_DEC_REG( reg, op )\ + nz = reg op;\ + reg = (uint8_t) nz;\ + goto loop; + + case 0xBC: INC_DEC_REG( a, + 1 ) // INC A + case 0x3D: INC_DEC_REG( x, + 1 ) // INC X + case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y + + case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A + case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X + case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y + + case 0x9B: // DEC dp+X + case 0xBB: // INC dp+X + data = (uint8_t) (data + x); + case 0x8B: // DEC dp + case 0xAB: // INC dp + data += dp; + goto inc_abs; + case 0x8C: // DEC abs + case 0xAC: // INC abs + data = READ_PC16( pc ); + pc++; + inc_abs: + nz = (opcode >> 4 & 2) - 1; + nz += READ( -1, data ); + WRITE( 0, data, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + +// 7. SHIFT, ROTATION COMMANDS + + case 0x5C: // LSR A + c = 0; + case 0x7C:{// ROR A + nz = (c >> 1 & 0x80) | (a >> 1); + c = a << 8; + a = nz; + goto loop; + } + + case 0x1C: // ASL A + c = 0; + case 0x3C:{// ROL A + int temp = c >> 8 & 1; + c = a << 1; + nz = c | temp; + a = (uint8_t) nz; + goto loop; + } + + case 0x0B: // ASL dp + c = 0; + data += dp; + goto rol_mem; + case 0x1B: // ASL dp+X + c = 0; + case 0x3B: // ROL dp+X + data = (uint8_t) (data + x); + case 0x2B: // ROL dp + data += dp; + goto rol_mem; + case 0x0C: // ASL abs + c = 0; + case 0x2C: // ROL abs + data = READ_PC16( pc ); + pc++; + rol_mem: + nz = c >> 8 & 1; + nz |= (c = READ( -1, data ) << 1); + WRITE( 0, data, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + + case 0x4B: // LSR dp + c = 0; + data += dp; + goto ror_mem; + case 0x5B: // LSR dp+X + c = 0; + case 0x7B: // ROR dp+X + data = (uint8_t) (data + x); + case 0x6B: // ROR dp + data += dp; + goto ror_mem; + case 0x4C: // LSR abs + c = 0; + case 0x6C: // ROR abs + data = READ_PC16( pc ); + pc++; + ror_mem: { + int temp = READ( -1, data ); + nz = (c >> 1 & 0x80) | (temp >> 1); + c = temp << 8; + WRITE( 0, data, nz ); + goto inc_pc_loop; + } + + case 0x9F: // XCN + nz = a = (a >> 4) | (uint8_t) (a << 4); + goto loop; + +// 8. 16-BIT TRANSMISION COMMANDS + + case 0xBA: // MOVW YA,dp + a = READ_DP( -2, data ); + nz = (a & 0x7F) | (a >> 1); + y = READ_DP( 0, (uint8_t) (data + 1) ); + nz |= y; + goto inc_pc_loop; + + case 0xDA: // MOVW dp,YA + WRITE_DP( -1, data, a ); + WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write ); + goto inc_pc_loop; + +// 9. 16-BIT OPERATION COMMANDS + + case 0x3A: // INCW dp + case 0x1A:{// DECW dp + int temp; + // low byte + data += dp; + temp = READ( -3, data ); + temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW + nz = ((temp >> 1) | temp) & 0x7F; + WRITE( -2, data, /*(uint8_t)*/ temp ); + + // high byte + data = (uint8_t) (data + 1) + dp; + temp = (uint8_t) ((temp >> 8) + READ( -1, data )); + nz |= temp; + WRITE( 0, data, temp ); + + goto inc_pc_loop; + } + + case 0x7A: // ADDW YA,dp + case 0x9A:{// SUBW YA,dp + int lo = READ_DP( -2, data ); + int hi = READ_DP( 0, (uint8_t) (data + 1) ); + int result; + int flags; + + if ( opcode == 0x9A ) // SUBW + { + lo = (lo ^ 0xFF) + 1; + hi ^= 0xFF; + } + + lo += a; + result = y + hi + (lo >> 8); + flags = hi ^ y ^ result; + + psw = (psw & ~(v40 | h08)) | + (flags >> 1 & h08) | + ((flags + 0x80) >> 2 & v40); + c = result; + a = (uint8_t) lo; + result = (uint8_t) result; + y = result; + nz = (((lo >> 1) | lo) & 0x7F) | result; + + goto inc_pc_loop; + } + + case 0x5A: { // CMPW YA,dp + int temp = a - READ_DP( -1, data ); + nz = ((temp >> 1) | temp) & 0x7F; + temp = y + (temp >> 8); + temp -= READ_DP( 0, (uint8_t) (data + 1) ); + nz |= temp; + c = ~temp; + nz &= 0xFF; + goto inc_pc_loop; + } + +// 10. MULTIPLICATION & DIVISON COMMANDS + + case 0xCF: { // MUL YA + unsigned temp = y * a; + a = (uint8_t) temp; + nz = ((temp >> 1) | temp) & 0x7F; + y = (uint8_t) (temp >> 8); + nz |= y; + goto loop; + } + + case 0x9E: // DIV YA,X + { + unsigned ya = y * 0x100 + a; + + psw &= ~(h08 | v40); + + if ( y >= x ) + psw |= v40; + + if ( (y & 15) >= (x & 15) ) + psw |= h08; + + if ( y < x * 2 ) + { + a = ya / x; + y = ya - a * x; + } + else + { + a = 255 - (ya - x * 0x200) / (256 - x); + y = x + (ya - x * 0x200) % (256 - x); + } + + nz = (uint8_t) a; + a = (uint8_t) a; + y = (uint8_t) y; + + goto loop; + } + +// 11. DECIMAL COMPENSATION COMMANDS + + case 0xDF: // DAA + SUSPICIOUS_OPCODE( "DAA" ); + if ( a > 0x99 || c & 0x100 ) + { + a += 0x60; + c = 0x100; + } + + if ( (a & 0x0F) > 9 || psw & h08 ) + a += 0x06; + + nz = a; + a = (uint8_t) a; + goto loop; + + case 0xBE: // DAS + SUSPICIOUS_OPCODE( "DAS" ); + if ( a > 0x99 || !(c & 0x100) ) + { + a -= 0x60; + c = 0; + } + + if ( (a & 0x0F) > 9 || !(psw & h08) ) + a -= 0x06; + + nz = a; + a = (uint8_t) a; + goto loop; + +// 12. BRANCHING COMMANDS + + case 0x2F: // BRA rel + pc += (int8_t) data; + goto inc_pc_loop; + + case 0x30: // BMI + BRANCH( (nz & nz_neg_mask) ) + + case 0x10: // BPL + BRANCH( !(nz & nz_neg_mask) ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + + case 0x70: // BVS + BRANCH( psw & v40 ) + + case 0x50: // BVC + BRANCH( !(psw & v40) ) + + #define CBRANCH( cond )\ + {\ + pc++;\ + if ( cond )\ + goto cbranch_taken_loop;\ + rel_time -= 2;\ + goto inc_pc_loop;\ + } + + case 0x03: // BBS dp.bit,rel + case 0x23: + case 0x43: + case 0x63: + case 0x83: + case 0xA3: + case 0xC3: + case 0xE3: + CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 ) + + case 0x13: // BBC dp.bit,rel + case 0x33: + case 0x53: + case 0x73: + case 0x93: + case 0xB3: + case 0xD3: + case 0xF3: + CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) ) + + case 0xDE: // CBNE dp+X,rel + data = (uint8_t) (data + x); + // fall through + case 0x2E:{// CBNE dp,rel + int temp; + // 61% from timer + READ_DP_TIMER( -4, data, temp ); + CBRANCH( temp != a ) + } + + case 0x6E: { // DBNZ dp,rel + unsigned temp = READ_DP( -4, data ) - 1; + WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write ); + CBRANCH( temp ) + } + + case 0xFE: // DBNZ Y,rel + y = (uint8_t) (y - 1); + BRANCH( y ) + + case 0x1F: // JMP [abs+X] + SET_PC( READ_PC16( pc ) + x ); + // fall through + case 0x5F: // JMP abs + SET_PC( READ_PC16( pc ) ); + goto loop; + +// 13. SUB-ROUTINE CALL RETURN COMMANDS + + case 0x0F:{// BRK + int temp; + int ret_addr = GET_PC(); + SUSPICIOUS_OPCODE( "BRK" ); + SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified + PUSH16( ret_addr ); + GET_PSW( temp ); + psw = (psw | b10) & ~i04; + PUSH( temp ); + goto loop; + } + + case 0x4F:{// PCALL offset + int ret_addr = GET_PC() + 1; + SET_PC( 0xFF00 | data ); + PUSH16( ret_addr ); + goto loop; + } + + case 0x01: // TCALL n + case 0x11: + case 0x21: + case 0x31: + case 0x41: + case 0x51: + case 0x61: + case 0x71: + case 0x81: + case 0x91: + case 0xA1: + case 0xB1: + case 0xC1: + case 0xD1: + case 0xE1: + case 0xF1: { + int ret_addr = GET_PC(); + SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) ); + PUSH16( ret_addr ); + goto loop; + } + +// 14. STACK OPERATION COMMANDS + + { + int temp; + uint8_t l, h; + case 0x7F: // RET1 + POP (temp); + POP (l); + POP (h); + SET_PC( l | (h << 8) ); + goto set_psw; + case 0x8E: // POP PSW + POP( temp ); + set_psw: + SET_PSW( temp ); + goto loop; + } + + case 0x0D: { // PUSH PSW + int temp; + GET_PSW( temp ); + PUSH( temp ); + goto loop; + } + + case 0x2D: // PUSH A + PUSH( a ); + goto loop; + + case 0x4D: // PUSH X + PUSH( x ); + goto loop; + + case 0x6D: // PUSH Y + PUSH( y ); + goto loop; + + case 0xAE: // POP A + POP( a ); + goto loop; + + case 0xCE: // POP X + POP( x ); + goto loop; + + case 0xEE: // POP Y + POP( y ); + goto loop; + +// 15. BIT OPERATION COMMANDS + + case 0x02: // SET1 + case 0x22: + case 0x42: + case 0x62: + case 0x82: + case 0xA2: + case 0xC2: + case 0xE2: + case 0x12: // CLR1 + case 0x32: + case 0x52: + case 0x72: + case 0x92: + case 0xB2: + case 0xD2: + case 0xF2: { + int bit = 1 << (opcode >> 5); + int mask = ~bit; + if ( opcode & 0x10 ) + bit = 0; + data += dp; + WRITE( 0, data, (READ( -1, data ) & mask) | bit ); + goto inc_pc_loop; + } + + case 0x0E: // TSET1 abs + case 0x4E: // TCLR1 abs + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -2, data ); + nz = (uint8_t) (a - temp); + temp &= ~a; + if ( opcode == 0x0E ) + temp |= a; + WRITE( 0, data, temp ); + } + goto loop; + + case 0x4A: // AND1 C,mem.bit + c &= MEM_BIT( 0 ); + pc += 2; + goto loop; + + case 0x6A: // AND1 C,/mem.bit + c &= ~MEM_BIT( 0 ); + pc += 2; + goto loop; + + case 0x0A: // OR1 C,mem.bit + c |= MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0x2A: // OR1 C,/mem.bit + c |= ~MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0x8A: // EOR1 C,mem.bit + c ^= MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0xEA: // NOT1 mem.bit + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -1, data & 0x1FFF ); + temp ^= 1 << (data >> 13); + WRITE( 0, data & 0x1FFF, temp ); + } + goto loop; + + case 0xCA: // MOV1 mem.bit,C + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -2, data & 0x1FFF ); + unsigned bit = data >> 13; + temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit); + WRITE( 0, data & 0x1FFF, temp + no_read_before_write ); + } + goto loop; + + case 0xAA: // MOV1 C,mem.bit + c = MEM_BIT( 0 ); + pc += 2; + goto loop; + +// 16. PROGRAM PSW FLAG OPERATION COMMANDS + + case 0x60: // CLRC + c = 0; + goto loop; + + case 0x80: // SETC + c = ~0; + goto loop; + + case 0xED: // NOTC + c ^= 0x100; + goto loop; + + case 0xE0: // CLRV + psw &= ~(v40 | h08); + goto loop; + + case 0x20: // CLRP + dp = 0; + goto loop; + + case 0x40: // SETP + dp = 0x100; + goto loop; + + case 0xA0: // EI + SUSPICIOUS_OPCODE( "EI" ); + psw |= i04; + goto loop; + + case 0xC0: // DI + SUSPICIOUS_OPCODE( "DI" ); + psw &= ~i04; + goto loop; + +// 17. OTHER COMMANDS + + case 0x00: // NOP + goto loop; + + case 0xFF:{// STOP + // handle PC wrap-around + if ( pc == 0x0000 ) + { + debug_printf( "SPC: PC wrapped around\n" ); + goto loop; + } + } + // fall through + case 0xEF: // SLEEP + SUSPICIOUS_OPCODE( "STOP/SLEEP" ); + --pc; + rel_time = 0; + m.cpu_error = "SPC emulation error"; + goto stop; + } // switch + + assert( 0 ); // catch any unhandled instructions +} +out_of_time: + rel_time -= m.cycle_table [ ram [pc] ]; // undo partial execution of opcode +stop: + + // Uncache registers + m.cpu_regs.pc = (uint16_t) GET_PC(); + m.cpu_regs.sp = ( uint8_t) GET_SP(); + m.cpu_regs.a = ( uint8_t) a; + m.cpu_regs.x = ( uint8_t) x; + m.cpu_regs.y = ( uint8_t) y; + { + int temp; + GET_PSW( temp ); + m.cpu_regs.psw = (uint8_t) temp; + } +} +SPC_CPU_RUN_FUNC_END diff --git a/thirdparty/game-music-emu/gme/Spc_Dsp.cpp b/thirdparty/game-music-emu/gme/Spc_Dsp.cpp new file mode 100644 index 000000000..51556434d --- /dev/null +++ b/thirdparty/game-music-emu/gme/Spc_Dsp.cpp @@ -0,0 +1,704 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Spc_Dsp.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +#if INT_MAX < 0x7FFFFFFF + #error "Requires that int type have at least 32 bits" +#endif + + +// TODO: add to blargg_endian.h +#define GET_LE16SA( addr ) ((int16_t) GET_LE16( addr )) +#define GET_LE16A( addr ) GET_LE16( addr ) +#define SET_LE16A( addr, data ) SET_LE16( addr, data ) + +static uint8_t const initial_regs [Spc_Dsp::register_count] = +{ + 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, + 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, + 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, + 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, + 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, + 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, + 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, + 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF +}; + +// if ( io < -32768 ) io = -32768; +// if ( io > 32767 ) io = 32767; +#define CLAMP16( io )\ +{\ + if ( (int16_t) io != io )\ + io = (io >> 31) ^ 0x7FFF;\ +} + +// Access global DSP register +#define REG(n) m.regs [r_##n] + +// Access voice DSP register +#define VREG(r,n) r [v_##n] + +#define WRITE_SAMPLES( l, r, out ) \ +{\ + out [0] = l;\ + out [1] = r;\ + out += 2;\ + if ( out >= m.out_end )\ + {\ + check( out == m.out_end );\ + check( m.out_end != &m.extra [extra_size] || \ + (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ + out = m.extra;\ + m.out_end = &m.extra [extra_size];\ + }\ +}\ + +void Spc_Dsp::set_output( sample_t* out, int size ) +{ + require( (size & 1) == 0 ); // must be even + if ( !out ) + { + out = m.extra; + size = extra_size; + } + m.out_begin = out; + m.out = out; + m.out_end = out + size; +} + +// Volume registers and efb are signed! Easy to forget int8_t cast. +// Prefixes are to avoid accidental use of locals with same names. + +// Interleved gauss table (to improve cache coherency) +// interleved_gauss [i] = gauss [(i & 1) * 256 + 255 - (i >> 1 & 0xFF)] +static short const interleved_gauss [512] = +{ + 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303, + 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299, + 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292, + 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282, + 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269, + 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253, + 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234, + 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213, + 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190, + 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164, + 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136, + 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106, + 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074, + 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040, + 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005, + 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969, + 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932, + 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894, + 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855, + 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816, + 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777, + 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737, + 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698, + 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659, + 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620, + 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582, + 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545, + 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508, + 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473, + 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439, + 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405, + 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374, +}; + + +//// Counters + +#define RATE( rate, div )\ + (rate >= div ? rate / div * 8 - 1 : rate - 1) + +static unsigned const counter_mask [32] = +{ + RATE( 2,2), RATE(2048,4), RATE(1536,3), + RATE(1280,5), RATE(1024,4), RATE( 768,3), + RATE( 640,5), RATE( 512,4), RATE( 384,3), + RATE( 320,5), RATE( 256,4), RATE( 192,3), + RATE( 160,5), RATE( 128,4), RATE( 96,3), + RATE( 80,5), RATE( 64,4), RATE( 48,3), + RATE( 40,5), RATE( 32,4), RATE( 24,3), + RATE( 20,5), RATE( 16,4), RATE( 12,3), + RATE( 10,5), RATE( 8,4), RATE( 6,3), + RATE( 5,5), RATE( 4,4), RATE( 3,3), + RATE( 2,4), + RATE( 1,4) +}; +#undef RATE + +inline void Spc_Dsp::init_counter() +{ + // counters start out with this synchronization + m.counters [0] = 1; + m.counters [1] = 0; + m.counters [2] = -0x20u; + m.counters [3] = 0x0B; + + int n = 2; + for ( int i = 1; i < 32; i++ ) + { + m.counter_select [i] = &m.counters [n]; + if ( !--n ) + n = 3; + } + m.counter_select [ 0] = &m.counters [0]; + m.counter_select [30] = &m.counters [2]; +} + +inline void Spc_Dsp::run_counter( int i ) +{ + int n = m.counters [i]; + if ( !(n-- & 7) ) + n -= 6 - i; + m.counters [i] = n; +} + +#define READ_COUNTER( rate )\ + (*m.counter_select [rate] & counter_mask [rate]) + + +//// Emulation + +void Spc_Dsp::run( int clock_count ) +{ + int new_phase = m.phase + clock_count; + int count = new_phase >> 5; + m.phase = new_phase & 31; + if ( !count ) + return; + + uint8_t* const ram = m.ram; + uint8_t const* const dir = &ram [REG(dir) * 0x100]; + int const slow_gaussian = (REG(pmon) >> 1) | REG(non); + int const noise_rate = REG(flg) & 0x1F; + + // Global volume + int mvoll = (int8_t) REG(mvoll); + int mvolr = (int8_t) REG(mvolr); + if ( mvoll * mvolr < m.surround_threshold ) + mvoll = -mvoll; // eliminate surround + + do + { + // KON/KOFF reading + if ( (m.every_other_sample ^= 1) != 0 ) + { + m.new_kon &= ~m.kon; + m.kon = m.new_kon; + m.t_koff = REG(koff); + } + + run_counter( 1 ); + run_counter( 2 ); + run_counter( 3 ); + + // Noise + if ( !READ_COUNTER( noise_rate ) ) + { + int feedback = (m.noise << 13) ^ (m.noise << 14); + m.noise = (feedback & 0x4000) ^ (m.noise >> 1); + } + + // Voices + int pmon_input = 0; + int main_out_l = 0; + int main_out_r = 0; + int echo_out_l = 0; + int echo_out_r = 0; + voice_t* v = m.voices; + uint8_t* v_regs = m.regs; + int vbit = 1; + do + { + #define SAMPLE_PTR(i) GET_LE16A( &dir [VREG(v_regs,srcn) * 4 + i * 2] ) + + int brr_header = ram [v->brr_addr]; + int kon_delay = v->kon_delay; + + // Pitch + int pitch = GET_LE16A( &VREG(v_regs,pitchl) ) & 0x3FFF; + if ( REG(pmon) & vbit ) + pitch += ((pmon_input >> 5) * pitch) >> 10; + + // KON phases + if ( --kon_delay >= 0 ) + { + v->kon_delay = kon_delay; + + // Get ready to start BRR decoding on next sample + if ( kon_delay == 4 ) + { + v->brr_addr = SAMPLE_PTR( 0 ); + v->brr_offset = 1; + v->buf_pos = v->buf; + brr_header = 0; // header is ignored on this sample + } + + // Envelope is never run during KON + v->env = 0; + v->hidden_env = 0; + + // Disable BRR decoding until last three samples + v->interp_pos = (kon_delay & 3 ? 0x4000 : 0); + + // Pitch is never added during KON + pitch = 0; + } + + int env = v->env; + + // Gaussian interpolation + { + int output = 0; + VREG(v_regs,envx) = (uint8_t) (env >> 4); + if ( env ) + { + // Make pointers into gaussian based on fractional position between samples + int offset = (unsigned) v->interp_pos >> 3 & 0x1FE; + short const* fwd = interleved_gauss + offset; + short const* rev = interleved_gauss + 510 - offset; // mirror left half of gaussian + + int const* in = &v->buf_pos [(unsigned) v->interp_pos >> 12]; + + if ( !(slow_gaussian & vbit) ) // 99% + { + // Faster approximation when exact sample value isn't necessary for pitch mod + output = (fwd [0] * in [0] + + fwd [1] * in [1] + + rev [1] * in [2] + + rev [0] * in [3]) >> 11; + output = (output * env) >> 11; + } + else + { + output = (int16_t) (m.noise * 2); + if ( !(REG(non) & vbit) ) + { + output = (fwd [0] * in [0]) >> 11; + output += (fwd [1] * in [1]) >> 11; + output += (rev [1] * in [2]) >> 11; + output = (int16_t) output; + output += (rev [0] * in [3]) >> 11; + + CLAMP16( output ); + output &= ~1; + } + output = (output * env) >> 11 & ~1; + } + + // Output + int l = output * v->volume [0]; + int r = output * v->volume [1]; + + main_out_l += l; + main_out_r += r; + + if ( REG(eon) & vbit ) + { + echo_out_l += l; + echo_out_r += r; + } + } + + pmon_input = output; + VREG(v_regs,outx) = (uint8_t) (output >> 8); + } + + // Soft reset or end of sample + if ( REG(flg) & 0x80 || (brr_header & 3) == 1 ) + { + v->env_mode = env_release; + env = 0; + } + + if ( m.every_other_sample ) + { + // KOFF + if ( m.t_koff & vbit ) + v->env_mode = env_release; + + // KON + if ( m.kon & vbit ) + { + v->kon_delay = 5; + v->env_mode = env_attack; + REG(endx) &= ~vbit; + } + } + + // Envelope + if ( !v->kon_delay ) + { + if ( v->env_mode == env_release ) // 97% + { + env -= 0x8; + v->env = env; + if ( env <= 0 ) + { + v->env = 0; + goto skip_brr; // no BRR decoding for you! + } + } + else // 3% + { + int rate; + int const adsr0 = VREG(v_regs,adsr0); + int env_data = VREG(v_regs,adsr1); + if ( adsr0 >= 0x80 ) // 97% ADSR + { + if ( v->env_mode > env_decay ) // 89% + { + env--; + env -= env >> 8; + rate = env_data & 0x1F; + + // optimized handling + v->hidden_env = env; + if ( READ_COUNTER( rate ) ) + goto exit_env; + v->env = env; + goto exit_env; + } + else if ( v->env_mode == env_decay ) + { + env--; + env -= env >> 8; + rate = (adsr0 >> 3 & 0x0E) + 0x10; + } + else // env_attack + { + rate = (adsr0 & 0x0F) * 2 + 1; + env += rate < 31 ? 0x20 : 0x400; + } + } + else // GAIN + { + int mode; + env_data = VREG(v_regs,gain); + mode = env_data >> 5; + if ( mode < 4 ) // direct + { + env = env_data * 0x10; + rate = 31; + } + else + { + rate = env_data & 0x1F; + if ( mode == 4 ) // 4: linear decrease + { + env -= 0x20; + } + else if ( mode < 6 ) // 5: exponential decrease + { + env--; + env -= env >> 8; + } + else // 6,7: linear increase + { + env += 0x20; + if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) + env += 0x8 - 0x20; // 7: two-slope linear increase + } + } + } + + // Sustain level + if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) + v->env_mode = env_sustain; + + v->hidden_env = env; + + // unsigned cast because linear decrease going negative also triggers this + if ( (unsigned) env > 0x7FF ) + { + env = (env < 0 ? 0 : 0x7FF); + if ( v->env_mode == env_attack ) + v->env_mode = env_decay; + } + + if ( !READ_COUNTER( rate ) ) + v->env = env; // nothing else is controlled by the counter + } + } + exit_env: + + { + // Apply pitch + int old_pos = v->interp_pos; + int interp_pos = (old_pos & 0x3FFF) + pitch; + if ( interp_pos > 0x7FFF ) + interp_pos = 0x7FFF; + v->interp_pos = interp_pos; + + // BRR decode if necessary + if ( old_pos >= 0x4000 ) + { + // Arrange the four input nybbles in 0xABCD order for easy decoding + int nybbles = ram [(v->brr_addr + v->brr_offset) & 0xFFFF] * 0x100 + + ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; + + // Advance read position + int const brr_block_size = 9; + int brr_offset = v->brr_offset; + if ( (brr_offset += 2) >= brr_block_size ) + { + // Next BRR block + int brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; + assert( brr_offset == brr_block_size ); + if ( brr_header & 1 ) + { + brr_addr = SAMPLE_PTR( 1 ); + if ( !v->kon_delay ) + REG(endx) |= vbit; + } + v->brr_addr = brr_addr; + brr_offset = 1; + } + v->brr_offset = brr_offset; + + // Decode + + // 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11 + static unsigned char const shifts [16 * 2] = { + 13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11 + }; + int const scale = brr_header >> 4; + int const right_shift = shifts [scale]; + int const left_shift = shifts [scale + 16]; + + // Write to next four samples in circular buffer + int* pos = v->buf_pos; + int* end; + + // Decode four samples + for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) + { + // Extract upper nybble and scale appropriately. Every cast is + // necessary to maintain correctness and avoid undef behavior + int s = int16_t(uint16_t((int16_t) nybbles >> right_shift) << left_shift); + + // Apply IIR filter (8 is the most commonly used) + int const filter = brr_header & 0x0C; + int const p1 = pos [brr_buf_size - 1]; + int const p2 = pos [brr_buf_size - 2] >> 1; + if ( filter >= 8 ) + { + s += p1; + s -= p2; + if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 + { + s += p2 >> 4; + s += (p1 * -3) >> 6; + } + else // s += p1 * 0.8984375 - p2 * 0.40625 + { + s += (p1 * -13) >> 7; + s += (p2 * 3) >> 4; + } + } + else if ( filter ) // s += p1 * 0.46875 + { + s += p1 >> 1; + s += (-p1) >> 5; + } + + // Adjust and write sample + CLAMP16( s ); + s = (int16_t) (s * 2); + pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around + } + + if ( pos >= &v->buf [brr_buf_size] ) + pos = v->buf; + v->buf_pos = pos; + } + } +skip_brr: + // Next voice + vbit <<= 1; + v_regs += 0x10; + v++; + } + while ( vbit < 0x100 ); + + // Echo position + int echo_offset = m.echo_offset; + uint8_t* const echo_ptr = &ram [(REG(esa) * 0x100 + echo_offset) & 0xFFFF]; + if ( !echo_offset ) + m.echo_length = (REG(edl) & 0x0F) * 0x800; + echo_offset += 4; + if ( echo_offset >= m.echo_length ) + echo_offset = 0; + m.echo_offset = echo_offset; + + // FIR + int echo_in_l = GET_LE16SA( echo_ptr + 0 ); + int echo_in_r = GET_LE16SA( echo_ptr + 2 ); + + int (*echo_hist_pos) [2] = m.echo_hist_pos; + if ( ++echo_hist_pos >= &m.echo_hist [echo_hist_size] ) + echo_hist_pos = m.echo_hist; + m.echo_hist_pos = echo_hist_pos; + + echo_hist_pos [0] [0] = echo_hist_pos [8] [0] = echo_in_l; + echo_hist_pos [0] [1] = echo_hist_pos [8] [1] = echo_in_r; + + #define CALC_FIR_( i, in ) ((in) * (int8_t) REG(fir + i * 0x10)) + echo_in_l = CALC_FIR_( 7, echo_in_l ); + echo_in_r = CALC_FIR_( 7, echo_in_r ); + + #define CALC_FIR( i, ch ) CALC_FIR_( i, echo_hist_pos [i + 1] [ch] ) + #define DO_FIR( i )\ + echo_in_l += CALC_FIR( i, 0 );\ + echo_in_r += CALC_FIR( i, 1 ); + DO_FIR( 0 ); + DO_FIR( 1 ); + DO_FIR( 2 ); + #if defined (__MWERKS__) && __MWERKS__ < 0x3200 + __eieio(); // keeps compiler from stupidly "caching" things in memory + #endif + DO_FIR( 3 ); + DO_FIR( 4 ); + DO_FIR( 5 ); + DO_FIR( 6 ); + + // Echo out + if ( !(REG(flg) & 0x20) ) + { + int l = (echo_out_l >> 7) + ((echo_in_l * (int8_t) REG(efb)) >> 14); + int r = (echo_out_r >> 7) + ((echo_in_r * (int8_t) REG(efb)) >> 14); + + // just to help pass more validation tests + #if SPC_MORE_ACCURACY + l &= ~1; + r &= ~1; + #endif + + CLAMP16( l ); + CLAMP16( r ); + + SET_LE16A( echo_ptr + 0, l ); + SET_LE16A( echo_ptr + 2, r ); + } + + // Sound out + int l = (main_out_l * mvoll + echo_in_l * (int8_t) REG(evoll)) >> 14; + int r = (main_out_r * mvolr + echo_in_r * (int8_t) REG(evolr)) >> 14; + + CLAMP16( l ); + CLAMP16( r ); + + if ( (REG(flg) & 0x40) ) + { + l = 0; + r = 0; + } + + sample_t* out = m.out; + WRITE_SAMPLES( l, r, out ); + m.out = out; + } + while ( --count ); +} + + +//// Setup + +void Spc_Dsp::mute_voices( int mask ) +{ + m.mute_mask = mask; + for ( int i = 0; i < voice_count; i++ ) + { + m.voices [i].enabled = (mask >> i & 1) - 1; + update_voice_vol( i * 0x10 ); + } +} + +void Spc_Dsp::init( void* ram_64k ) +{ + m.ram = (uint8_t*) ram_64k; + mute_voices( 0 ); + disable_surround( false ); + set_output( 0, 0 ); + reset(); + + #ifndef NDEBUG + // be sure this sign-extends + assert( (int16_t) 0x8000 == -0x8000 ); + + // be sure right shift preserves sign + assert( (-1 >> 1) == -1 ); + + // check clamp macro + int i; + i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); + i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); + + blargg_verify_byte_order(); + #endif +} + +void Spc_Dsp::soft_reset_common() +{ + require( m.ram ); // init() must have been called already + + m.noise = 0x4000; + m.echo_hist_pos = m.echo_hist; + m.every_other_sample = 1; + m.echo_offset = 0; + m.phase = 0; + + init_counter(); +} + +void Spc_Dsp::soft_reset() +{ + REG(flg) = 0xE0; + soft_reset_common(); +} + +void Spc_Dsp::load( uint8_t const regs [register_count] ) +{ + memcpy( m.regs, regs, sizeof m.regs ); + memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); + + // Internal state + int i; + for ( i = voice_count; --i >= 0; ) + { + voice_t& v = m.voices [i]; + v.brr_offset = 1; + v.buf_pos = v.buf; + } + m.new_kon = REG(kon); + + mute_voices( m.mute_mask ); + soft_reset_common(); +} + +void Spc_Dsp::reset() { load( initial_regs ); } diff --git a/thirdparty/game-music-emu/gme/Spc_Dsp.h b/thirdparty/game-music-emu/gme/Spc_Dsp.h new file mode 100644 index 000000000..b364f0845 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Spc_Dsp.h @@ -0,0 +1,207 @@ +// Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one) + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef SPC_DSP_H +#define SPC_DSP_H + +#include "blargg_common.h" + +struct Spc_Dsp { +public: +// Setup + + // Initializes DSP and has it use the 64K RAM provided + void init( void* ram_64k ); + + // Sets destination for output samples. If out is NULL or out_size is 0, + // doesn't generate any. + typedef short sample_t; + void set_output( sample_t* out, int out_size ); + + // Number of samples written to output since it was last set, always + // a multiple of 2. Undefined if more samples were generated than + // output buffer could hold. + int sample_count() const; + +// Emulation + + // Resets DSP to power-on state + void reset(); + + // Emulates pressing reset switch on SNES + void soft_reset(); + + // Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp() + // to catch the DSP up to present. + int read ( int addr ) const; + void write( int addr, int data ); + + // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks + // a pair of samples is be generated. + void run( int clock_count ); + +// Sound control + + // Mutes voices corresponding to non-zero bits in mask (overrides VxVOL with 0). + // Reduces emulation accuracy. + enum { voice_count = 8 }; + void mute_voices( int mask ); + + // If true, prevents channels and global volumes from being phase-negated + void disable_surround( bool disable = true ); + +// State + + // Resets DSP and uses supplied values to initialize registers + enum { register_count = 128 }; + void load( uint8_t const regs [register_count] ); + +// DSP register addresses + + // Global registers + enum { + r_mvoll = 0x0C, r_mvolr = 0x1C, + r_evoll = 0x2C, r_evolr = 0x3C, + r_kon = 0x4C, r_koff = 0x5C, + r_flg = 0x6C, r_endx = 0x7C, + r_efb = 0x0D, r_pmon = 0x2D, + r_non = 0x3D, r_eon = 0x4D, + r_dir = 0x5D, r_esa = 0x6D, + r_edl = 0x7D, + r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F + }; + + // Voice registers + enum { + v_voll = 0x00, v_volr = 0x01, + v_pitchl = 0x02, v_pitchh = 0x03, + v_srcn = 0x04, v_adsr0 = 0x05, + v_adsr1 = 0x06, v_gain = 0x07, + v_envx = 0x08, v_outx = 0x09 + }; + +public: + enum { extra_size = 16 }; + sample_t* extra() { return m.extra; } + sample_t const* out_pos() const { return m.out; } +public: + BLARGG_DISABLE_NOTHROW + + enum { echo_hist_size = 8 }; + + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; + enum { brr_buf_size = 12 }; + struct voice_t + { + int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) + int* buf_pos; // place in buffer where next samples will be decoded + int interp_pos; // relative fractional position in sample (0x1000 = 1.0) + int brr_addr; // address of current BRR block + int brr_offset; // current decoding offset in BRR block + int kon_delay; // KON delay/current setup phase + env_mode_t env_mode; + int env; // current envelope level + int hidden_env; // used by GAIN mode 7, very obscure quirk + int volume [2]; // copy of volume from DSP registers, with surround disabled + int enabled; // -1 if enabled, 0 if muted + }; +private: + struct state_t + { + uint8_t regs [register_count]; + + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) + int echo_hist [echo_hist_size * 2] [2]; + int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] + + int every_other_sample; // toggles every sample + int kon; // KON value when last checked + int noise; + int echo_offset; // offset from ESA in echo buffer + int echo_length; // number of bytes that echo_offset will stop at + int phase; // next clock cycle to run (0-31) + unsigned counters [4]; + + int new_kon; + int t_koff; + + voice_t voices [voice_count]; + + unsigned* counter_select [32]; + + // non-emulation state + uint8_t* ram; // 64K shared RAM between DSP and SMP + int mute_mask; + int surround_threshold; + sample_t* out; + sample_t* out_end; + sample_t* out_begin; + sample_t extra [extra_size]; + }; + state_t m; + + void init_counter(); + void run_counter( int ); + void soft_reset_common(); + void write_outline( int addr, int data ); + void update_voice_vol( int addr ); +}; + +#include + +inline int Spc_Dsp::sample_count() const { return m.out - m.out_begin; } + +inline int Spc_Dsp::read( int addr ) const +{ + assert( (unsigned) addr < register_count ); + return m.regs [addr]; +} + +inline void Spc_Dsp::update_voice_vol( int addr ) +{ + int l = (int8_t) m.regs [addr + v_voll]; + int r = (int8_t) m.regs [addr + v_volr]; + + if ( l * r < m.surround_threshold ) + { + // signs differ, so negate those that are negative + l ^= l >> 7; + r ^= r >> 7; + } + + voice_t& v = m.voices [addr >> 4]; + int enabled = v.enabled; + v.volume [0] = l & enabled; + v.volume [1] = r & enabled; +} + +inline void Spc_Dsp::write( int addr, int data ) +{ + assert( (unsigned) addr < register_count ); + + m.regs [addr] = (uint8_t) data; + int low = addr & 0x0F; + if ( low < 0x2 ) // voice volumes + { + update_voice_vol( low ^ addr ); + } + else if ( low == 0xC ) + { + if ( addr == r_kon ) + m.new_kon = (uint8_t) data; + + if ( addr == r_endx ) // always cleared, regardless of data written + m.regs [r_endx] = 0; + } +} + +inline void Spc_Dsp::disable_surround( bool disable ) +{ + m.surround_threshold = disable ? 0 : -0x4000; +} + +#define SPC_NO_COPY_STATE_FUNCS 1 + +#define SPC_LESS_ACCURATE 1 + +#endif diff --git a/thirdparty/game-music-emu/gme/Spc_Emu.cpp b/thirdparty/game-music-emu/gme/Spc_Emu.cpp new file mode 100644 index 000000000..0f45d8739 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Spc_Emu.cpp @@ -0,0 +1,358 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Spc_Emu.h" + +#include "blargg_endian.h" +#include +#include + +/* Copyright (C) 2004-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// TODO: support Spc_Filter's bass + +Spc_Emu::Spc_Emu() +{ + set_type( gme_spc_type ); + + static const char* const names [Snes_Spc::voice_count] = { + "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8" + }; + set_voice_names( names ); + + set_gain( 1.4 ); +} + +Spc_Emu::~Spc_Emu() { } + +// Track info + +long const trailer_offset = 0x10200; + +byte const* Spc_Emu::trailer() const { return &file_data [min( file_size, trailer_offset )]; } + +long Spc_Emu::trailer_size() const { return max( 0L, file_size - trailer_offset ); } + +static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) +{ + // header + byte const* end = begin + size; + if ( size < 8 || memcmp( begin, "xid6", 4 ) ) + { + check( false ); + return; + } + long info_size = get_le32( begin + 4 ); + byte const* in = begin + 8; + if ( end - in > info_size ) + { + debug_printf( "Extra data after SPC xid6 info\n" ); + end = in + info_size; + } + + int year = 0; + char copyright [256 + 5]; + int copyright_len = 0; + int const year_len = 5; + + while ( end - in >= 4 ) + { + // header + int id = in [0]; + int data = in [3] * 0x100 + in [2]; + int type = in [1]; + int len = type ? data : 0; + in += 4; + if ( len > end - in ) + { + check( false ); + break; // block goes past end of data + } + + // handle specific block types + char* field = 0; + switch ( id ) + { + case 0x01: field = out->song; break; + case 0x02: field = out->game; break; + case 0x03: field = out->author; break; + case 0x04: field = out->dumper; break; + case 0x07: field = out->comment; break; + case 0x14: year = data; break; + + //case 0x30: // intro length + // Many SPCs have intro length set wrong for looped tracks, making it useless + /* + case 0x30: + check( len == 4 ); + if ( len >= 4 ) + { + out->intro_length = get_le32( in ) / 64; + if ( out->length > 0 ) + { + long loop = out->length - out->intro_length; + if ( loop >= 2000 ) + out->loop_length = loop; + } + } + break; + */ + + case 0x13: + copyright_len = min( len, (int) sizeof copyright - year_len ); + memcpy( ©right [year_len], in, copyright_len ); + break; + + default: + if ( id < 0x01 || (id > 0x07 && id < 0x10) || + (id > 0x14 && id < 0x30) || id > 0x36 ) + debug_printf( "Unknown SPC xid6 block: %X\n", (int) id ); + break; + } + if ( field ) + { + check( type == 1 ); + Gme_File::copy_field_( field, (char const*) in, len ); + } + + // skip to next block + in += len; + + // blocks are supposed to be 4-byte aligned with zero-padding... + byte const* unaligned = in; + while ( (in - begin) & 3 && in < end ) + { + if ( *in++ != 0 ) + { + // ...but some files have no padding + in = unaligned; + debug_printf( "SPC info tag wasn't properly padded to align\n" ); + break; + } + } + } + + char* p = ©right [year_len]; + if ( year ) + { + *--p = ' '; + for ( int n = 4; n--; ) + { + *--p = char (year % 10 + '0'); + year /= 10; + } + copyright_len += year_len; + } + if ( copyright_len ) + Gme_File::copy_field_( out->copyright, p, copyright_len ); + + check( in == end ); +} + +static void get_spc_info( Spc_Emu::header_t const& h, byte const* xid6, long xid6_size, + track_info_t* out ) +{ + // decode length (can be in text or binary format, sometimes ambiguous ugh) + long len_secs = 0; + for ( int i = 0; i < 3; i++ ) + { + unsigned n = h.len_secs [i] - '0'; + if ( n > 9 ) + { + // ignore single-digit text lengths + // (except if author field is present and begins at offset 1, ugh) + if ( i == 1 && (h.author [0] || !h.author [1]) ) + len_secs = 0; + break; + } + len_secs *= 10; + len_secs += n; + } + if ( !len_secs || len_secs > 0x1FFF ) + len_secs = get_le16( h.len_secs ); + if ( len_secs < 0x1FFF ) + out->length = len_secs * 1000; + + int offset = (h.author [0] < ' ' || unsigned (h.author [0] - '0') <= 9); + Gme_File::copy_field_( out->author, &h.author [offset], sizeof h.author - offset ); + + GME_COPY_FIELD( h, out, song ); + GME_COPY_FIELD( h, out, game ); + GME_COPY_FIELD( h, out, dumper ); + GME_COPY_FIELD( h, out, comment ); + + if ( xid6_size ) + get_spc_xid6( xid6, xid6_size, out ); +} + +blargg_err_t Spc_Emu::track_info_( track_info_t* out, int ) const +{ + get_spc_info( header(), trailer(), trailer_size(), out ); + return 0; +} + +static blargg_err_t check_spc_header( void const* header ) +{ + if ( memcmp( header, "SNES-SPC700 Sound File Data", 27 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Spc_File : Gme_Info_ +{ + Spc_Emu::header_t header; + blargg_vector xid6; + + Spc_File() { set_type( gme_spc_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + long file_size = in.remain(); + if ( file_size < Snes_Spc::spc_min_file_size ) + return gme_wrong_file_type; + RETURN_ERR( in.read( &header, Spc_Emu::header_size ) ); + RETURN_ERR( check_spc_header( header.tag ) ); + long const xid6_offset = 0x10200; + long xid6_size = file_size - xid6_offset; + if ( xid6_size > 0 ) + { + RETURN_ERR( xid6.resize( xid6_size ) ); + RETURN_ERR( in.skip( xid6_offset - Spc_Emu::header_size ) ); + RETURN_ERR( in.read( xid6.begin(), xid6.size() ) ); + } + return 0; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + get_spc_info( header, xid6.begin(), xid6.size(), out ); + return 0; + } +}; + +static Music_Emu* new_spc_emu () { return BLARGG_NEW Spc_Emu ; } +static Music_Emu* new_spc_file() { return BLARGG_NEW Spc_File; } + +static gme_type_t_ const gme_spc_type_ = { "Super Nintendo", 1, &new_spc_emu, &new_spc_file, "SPC", 0 }; +BLARGG_EXPORT extern gme_type_t const gme_spc_type = &gme_spc_type_; + + +// Setup + +blargg_err_t Spc_Emu::set_sample_rate_( long sample_rate ) +{ + RETURN_ERR( apu.init() ); + enable_accuracy( false ); + if ( sample_rate != native_sample_rate ) + { + RETURN_ERR( resampler.buffer_size( native_sample_rate / 20 * 2 ) ); + resampler.time_ratio( (double) native_sample_rate / sample_rate, 0.9965 ); + } + return 0; +} + +void Spc_Emu::enable_accuracy_( bool b ) +{ + Music_Emu::enable_accuracy_( b ); + filter.enable( b ); +} + +void Spc_Emu::mute_voices_( int m ) +{ + Music_Emu::mute_voices_( m ); + apu.mute_voices( m ); +} + +blargg_err_t Spc_Emu::load_mem_( byte const* in, long size ) +{ + assert( offsetof (header_t,unused2 [46]) == header_size ); + file_data = in; + file_size = size; + set_voice_count( Snes_Spc::voice_count ); + if ( size < Snes_Spc::spc_min_file_size ) + return gme_wrong_file_type; + return check_spc_header( in ); +} + +// Emulation + +void Spc_Emu::set_tempo_( double t ) +{ + apu.set_tempo( (int) (t * apu.tempo_unit) ); +} + +blargg_err_t Spc_Emu::start_track_( int track ) +{ + RETURN_ERR( Music_Emu::start_track_( track ) ); + resampler.clear(); + filter.clear(); + RETURN_ERR( apu.load_spc( file_data, file_size ) ); + filter.set_gain( (int) (gain() * SPC_Filter::gain_unit) ); + apu.clear_echo(); + track_info_t spc_info; + RETURN_ERR( track_info_( &spc_info, track ) ); + + // Set a default track length, need a non-zero fadeout + if ( autoload_playback_limit() && ( spc_info.length > 0 ) ) + set_fade ( spc_info.length, 50 ); + return 0; +} + +blargg_err_t Spc_Emu::play_and_filter( long count, sample_t out [] ) +{ + RETURN_ERR( apu.play( count, out ) ); + filter.run( out, count ); + return 0; +} + +blargg_err_t Spc_Emu::skip_( long count ) +{ + if ( sample_rate() != native_sample_rate ) + { + count = long (count * resampler.ratio()) & ~1; + count -= resampler.skip_input( count ); + } + + // TODO: shouldn't skip be adjusted for the 64 samples read afterwards? + + if ( count > 0 ) + { + RETURN_ERR( apu.skip( count ) ); + filter.clear(); + } + + // eliminate pop due to resampler + const int resampler_latency = 64; + sample_t buf [resampler_latency]; + return play_( resampler_latency, buf ); +} + +blargg_err_t Spc_Emu::play_( long count, sample_t* out ) +{ + if ( sample_rate() == native_sample_rate ) + return play_and_filter( count, out ); + + long remain = count; + while ( remain > 0 ) + { + remain -= resampler.read( &out [count - remain], remain ); + if ( remain > 0 ) + { + long n = resampler.max_write(); + RETURN_ERR( play_and_filter( n, resampler.buffer() ) ); + resampler.write( n ); + } + } + check( remain == 0 ); + return 0; +} diff --git a/thirdparty/game-music-emu/gme/Spc_Emu.h b/thirdparty/game-music-emu/gme/Spc_Emu.h new file mode 100644 index 000000000..76e1ac63d --- /dev/null +++ b/thirdparty/game-music-emu/gme/Spc_Emu.h @@ -0,0 +1,82 @@ +// Super Nintendo SPC music file emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef SPC_EMU_H +#define SPC_EMU_H + +#include "Fir_Resampler.h" +#include "Music_Emu.h" +#include "Snes_Spc.h" +#include "Spc_Filter.h" + +class Spc_Emu : public Music_Emu { +public: + // The Super Nintendo hardware samples at 32kHz. Other sample rates are + // handled by resampling the 32kHz output; emulation accuracy is not affected. + enum { native_sample_rate = 32000 }; + + // SPC file header + enum { header_size = 0x100 }; + struct header_t + { + char tag [35]; + byte format; + byte version; + byte pc [2]; + byte a, x, y, psw, sp; + byte unused [2]; + char song [32]; + char game [32]; + char dumper [16]; + char comment [32]; + byte date [11]; + byte len_secs [3]; + byte fade_msec [4]; + char author [32]; // sometimes first char should be skipped (see official SPC spec) + byte mute_mask; + byte emulator; + byte unused2 [46]; + }; + + // Header for currently loaded file + header_t const& header() const { return *(header_t const*) file_data; } + + // Prevents channels and global volumes from being phase-negated + void disable_surround( bool disable = true ); + + static gme_type_t static_type() { return gme_spc_type; } + +public: + // deprecated + using Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + byte const* trailer() const; // use track_info() + long trailer_size() const; + +public: + Spc_Emu(); + ~Spc_Emu(); +protected: + blargg_err_t load_mem_( byte const*, long ); + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t set_sample_rate_( long ); + blargg_err_t start_track_( int ); + blargg_err_t play_( long, sample_t* ); + blargg_err_t skip_( long ); + void mute_voices_( int ); + void set_tempo_( double ); + void enable_accuracy_( bool ); +private: + byte const* file_data; + long file_size; + Fir_Resampler<24> resampler; + SPC_Filter filter; + Snes_Spc apu; + + blargg_err_t play_and_filter( long count, sample_t out [] ); +}; + +inline void Spc_Emu::disable_surround( bool b ) { apu.disable_surround( b ); } + +#endif diff --git a/thirdparty/game-music-emu/gme/Spc_Filter.cpp b/thirdparty/game-music-emu/gme/Spc_Filter.cpp new file mode 100644 index 000000000..2cc77fc93 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Spc_Filter.cpp @@ -0,0 +1,83 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Spc_Filter.h" + +#include + +/* Copyright (C) 2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void SPC_Filter::clear() { memset( ch, 0, sizeof ch ); } + +SPC_Filter::SPC_Filter() +{ + enabled = true; + gain = gain_unit; + bass = bass_norm; + clear(); +} + +void SPC_Filter::run( short* io, int count ) +{ + require( (count & 1) == 0 ); // must be even + + int const gain = this->gain; + if ( enabled ) + { + int const bass = this->bass; + chan_t* c = &ch [2]; + do + { + // cache in registers + int sum = (--c)->sum; + int pp1 = c->pp1; + int p1 = c->p1; + + for ( int i = 0; i < count; i += 2 ) + { + // Low-pass filter (two point FIR with coeffs 0.25, 0.75) + int f = io [i] + p1; + p1 = io [i] * 3; + + // High-pass filter ("leaky integrator") + int delta = f - pp1; + pp1 = f; + int s = sum >> (gain_bits + 2); + sum += (delta * gain) - (sum >> bass); + + // Clamp to 16 bits + if ( (short) s != s ) + s = (s >> 31) ^ 0x7FFF; + + io [i] = (short) s; + } + + c->p1 = p1; + c->pp1 = pp1; + c->sum = sum; + ++io; + } + while ( c != ch ); + } + else if ( gain != gain_unit ) + { + short* const end = io + count; + while ( io < end ) + { + int s = (*io * gain) >> gain_bits; + if ( (short) s != s ) + s = (s >> 31) ^ 0x7FFF; + *io++ = (short) s; + } + } +} diff --git a/thirdparty/game-music-emu/gme/Spc_Filter.h b/thirdparty/game-music-emu/gme/Spc_Filter.h new file mode 100644 index 000000000..d9994af5f --- /dev/null +++ b/thirdparty/game-music-emu/gme/Spc_Filter.h @@ -0,0 +1,53 @@ +// Simple low-pass and high-pass filter to better match sound output of a SNES + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef SPC_FILTER_H +#define SPC_FILTER_H + +#include "blargg_common.h" + +struct SPC_Filter { +public: + + // Filters count samples of stereo sound in place. Count must be a multiple of 2. + typedef short sample_t; + void run( sample_t* io, int count ); + +// Optional features + + // Clears filter to silence + void clear(); + + // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit + // are fine, since output is clamped to 16-bit sample range. + enum { gain_unit = 0x100 }; + void set_gain( int gain ); + + // Enables/disables filtering (when disabled, gain is still applied) + void enable( bool b ); + + // Sets amount of bass (logarithmic scale) + enum { bass_none = 0 }; + enum { bass_norm = 8 }; // normal amount + enum { bass_max = 31 }; + void set_bass( int bass ); + +public: + SPC_Filter(); + BLARGG_DISABLE_NOTHROW +private: + enum { gain_bits = 8 }; + int gain; + int bass; + bool enabled; + struct chan_t { int p1, pp1, sum; }; + chan_t ch [2]; +}; + +inline void SPC_Filter::enable( bool b ) { enabled = b; } + +inline void SPC_Filter::set_gain( int g ) { gain = g; } + +inline void SPC_Filter::set_bass( int b ) { bass = b; } + +#endif diff --git a/thirdparty/game-music-emu/gme/Vgm_Emu.cpp b/thirdparty/game-music-emu/gme/Vgm_Emu.cpp new file mode 100644 index 000000000..8f19b7de5 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Vgm_Emu.cpp @@ -0,0 +1,434 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Vgm_Emu.h" + +#include "blargg_endian.h" +#include +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow +double const rolloff = 0.990; +double const oversample_factor = 1.5; + +Vgm_Emu::Vgm_Emu() +{ + disable_oversampling_ = false; + psg_rate = 0; + set_type( gme_vgm_type ); + + static int const types [8] = { + wave_type | 1, wave_type | 0, wave_type | 2, noise_type | 0 + }; + set_voice_types( types ); + + set_silence_lookahead( 1 ); // tracks should already be trimmed + + set_equalizer( make_equalizer( -14.0, 80 ) ); +} + +Vgm_Emu::~Vgm_Emu() { } + +// Track info + +static byte const* skip_gd3_str( byte const* in, byte const* end ) +{ + while ( end - in >= 2 ) + { + in += 2; + if ( !(in [-2] | in [-1]) ) + break; + } + return in; +} + +static byte const* get_gd3_str( byte const* in, byte const* end, char* field ) +{ + byte const* mid = skip_gd3_str( in, end ); + int len = (mid - in) / 2 - 1; + if ( len > 0 ) + { + len = min( len, (int) Gme_File::max_field_ ); + field [len] = 0; + for ( int i = 0; i < len; i++ ) + field [i] = (in [i * 2 + 1] ? '?' : in [i * 2]); // TODO: convert to utf-8 + } + return mid; +} + +static byte const* get_gd3_pair( byte const* in, byte const* end, char* field ) +{ + return skip_gd3_str( get_gd3_str( in, end, field ), end ); +} + +static void parse_gd3( byte const* in, byte const* end, track_info_t* out ) +{ + in = get_gd3_pair( in, end, out->song ); + in = get_gd3_pair( in, end, out->game ); + in = get_gd3_pair( in, end, out->system ); + in = get_gd3_pair( in, end, out->author ); + in = get_gd3_str ( in, end, out->copyright ); + in = get_gd3_pair( in, end, out->dumper ); + in = get_gd3_str ( in, end, out->comment ); +} + +int const gd3_header_size = 12; + +static long check_gd3_header( byte const* h, long remain ) +{ + if ( remain < gd3_header_size ) return 0; + if ( memcmp( h, "Gd3 ", 4 ) ) return 0; + if ( get_le32( h + 4 ) >= 0x200 ) return 0; + + long gd3_size = get_le32( h + 8 ); + if ( gd3_size > remain - gd3_header_size ) return 0; + + return gd3_size; +} + +byte const* Vgm_Emu::gd3_data( int* size ) const +{ + if ( size ) + *size = 0; + + long gd3_offset = get_le32( header().gd3_offset ) - 0x2C; + if ( gd3_offset < 0 ) + return 0; + + byte const* gd3 = data + header_size + gd3_offset; + long gd3_size = check_gd3_header( gd3, data_end - gd3 ); + if ( !gd3_size ) + return 0; + + if ( size ) + *size = gd3_size + gd3_header_size; + + return gd3; +} + +static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) +{ + long length = get_le32( h.track_duration ) * 10 / 441; + if ( length > 0 ) + { + long loop = get_le32( h.loop_duration ); + if ( loop > 0 && get_le32( h.loop_offset ) ) + { + out->loop_length = loop * 10 / 441; + out->intro_length = length - out->loop_length; + } + else + { + out->length = length; // 1000 / 44100 (VGM files used 44100 as timebase) + out->intro_length = length; // make it clear that track is no longer than length + out->loop_length = 0; + } + } +} + +blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const +{ + get_vgm_length( header(), out ); + + int size; + byte const* gd3 = gd3_data( &size ); + if ( gd3 ) + parse_gd3( gd3 + gd3_header_size, gd3 + size, out ); + + return 0; +} + +static blargg_err_t check_vgm_header( Vgm_Emu::header_t const& h ) +{ + if ( memcmp( h.tag, "Vgm ", 4 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Vgm_File : Gme_Info_ +{ + Vgm_Emu::header_t h; + blargg_vector gd3; + + Vgm_File() { set_type( gme_vgm_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + long file_size = in.remain(); + if ( file_size <= Vgm_Emu::header_size ) + return gme_wrong_file_type; + + RETURN_ERR( in.read( &h, Vgm_Emu::header_size ) ); + RETURN_ERR( check_vgm_header( h ) ); + + long gd3_offset = get_le32( h.gd3_offset ) - 0x2C; + long remain = file_size - Vgm_Emu::header_size - gd3_offset; + byte gd3_h [gd3_header_size]; + if ( gd3_offset > 0 && remain >= gd3_header_size ) + { + RETURN_ERR( in.skip( gd3_offset ) ); + RETURN_ERR( in.read( gd3_h, sizeof gd3_h ) ); + long gd3_size = check_gd3_header( gd3_h, remain ); + if ( gd3_size ) + { + RETURN_ERR( gd3.resize( gd3_size ) ); + RETURN_ERR( in.read( gd3.begin(), gd3.size() ) ); + } + } + return 0; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + get_vgm_length( h, out ); + if ( gd3.size() ) + parse_gd3( gd3.begin(), gd3.end(), out ); + return 0; + } +}; + +static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; } +static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; } + +static gme_type_t_ const gme_vgm_type_ = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 }; +BLARGG_EXPORT extern gme_type_t const gme_vgm_type = &gme_vgm_type_; + +static gme_type_t_ const gme_vgz_type_ = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 }; +BLARGG_EXPORT extern gme_type_t const gme_vgz_type = &gme_vgz_type_; + + +// Setup + +void Vgm_Emu::set_tempo_( double t ) +{ + if ( psg_rate ) + { + vgm_rate = (long) (44100 * t + 0.5); + blip_time_factor = (long) floor( double (1L << blip_time_bits) / vgm_rate * psg_rate + 0.5 ); + //debug_printf( "blip_time_factor: %ld\n", blip_time_factor ); + //debug_printf( "vgm_rate: %ld\n", vgm_rate ); + // TODO: remove? calculates vgm_rate more accurately (above differs at most by one Hz only) + //blip_time_factor = (long) floor( double (1L << blip_time_bits) * psg_rate / 44100 / t + 0.5 ); + //vgm_rate = (long) floor( double (1L << blip_time_bits) * psg_rate / blip_time_factor + 0.5 ); + + fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 ); + } +} + +blargg_err_t Vgm_Emu::set_sample_rate_( long sample_rate ) +{ + RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) ); + return Classic_Emu::set_sample_rate_( sample_rate ); +} + +blargg_err_t Vgm_Emu::set_multi_channel ( bool is_enabled ) +{ + // we acutally should check here whether this is classic emu or not + // however set_multi_channel() is called before setup_fm() resulting in uninited is_classic_emu() + // hard code it to unsupported +#if 0 + if ( is_classic_emu() ) + { + RETURN_ERR( Music_Emu::set_multi_channel_( is_enabled ) ); + return 0; + } + else +#endif + { + (void) is_enabled; + return "multichannel rendering not supported for YM2*** FM sound chip emulators"; + } +} + +void Vgm_Emu::update_eq( blip_eq_t const& eq ) +{ + psg.treble_eq( eq ); + dac_synth.treble_eq( eq ); +} + +void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + if ( i < psg.osc_count ) + psg.osc_output( i, c, l, r ); +} + +void Vgm_Emu::mute_voices_( int mask ) +{ + Classic_Emu::mute_voices_( mask ); + dac_synth.output( &blip_buf ); + if ( uses_fm ) + { + psg.output( (mask & 0x80) ? 0 : &blip_buf ); + if ( ym2612.enabled() ) + { + dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() ); + ym2612.mute_voices( mask ); + } + + if ( ym2413.enabled() ) + { + int m = mask & 0x3F; + if ( mask & 0x20 ) + m |= 0x01E0; // channels 5-8 + if ( mask & 0x40 ) + m |= 0x3E00; + ym2413.mute_voices( m ); + } + } +} + +blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size ) +{ + assert( offsetof (header_t,unused2 [8]) == header_size ); + + if ( new_size <= header_size ) + return gme_wrong_file_type; + + header_t const& h = *(header_t const*) new_data; + + RETURN_ERR( check_vgm_header( h ) ); + + check( get_le32( h.version ) <= 0x150 ); + + // psg rate + psg_rate = get_le32( h.psg_rate ); + if ( !psg_rate ) + psg_rate = 3579545; + blip_buf.clock_rate( psg_rate ); + + data = new_data; + data_end = new_data + new_size; + + // get loop + loop_begin = data_end; + if ( get_le32( h.loop_offset ) ) + loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)]; + + set_voice_count( psg.osc_count ); + + RETURN_ERR( setup_fm() ); + + static const char* const fm_names [] = { + "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" + }; + static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" }; + set_voice_names( uses_fm ? fm_names : psg_names ); + + // do after FM in case output buffer is changed + return Classic_Emu::setup_buffer( psg_rate ); +} + +blargg_err_t Vgm_Emu::setup_fm() +{ + long ym2612_rate = get_le32( header().ym2612_rate ); + long ym2413_rate = get_le32( header().ym2413_rate ); + if ( ym2413_rate && get_le32( header().version ) < 0x110 ) + update_fm_rates( &ym2413_rate, &ym2612_rate ); + + uses_fm = false; + + fm_rate = blip_buf.sample_rate() * oversample_factor; + + if ( ym2612_rate ) + { + uses_fm = true; + if ( disable_oversampling_ ) + fm_rate = ym2612_rate / 144.0; + Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() ); + RETURN_ERR( ym2612.set_rate( fm_rate, ym2612_rate ) ); + ym2612.enable( true ); + set_voice_count( 8 ); + } + + if ( !uses_fm && ym2413_rate ) + { + uses_fm = true; + if ( disable_oversampling_ ) + fm_rate = ym2413_rate / 72.0; + Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() ); + int result = ym2413.set_rate( fm_rate, ym2413_rate ); + if ( result == 2 ) + return "YM2413 FM sound isn't supported"; + CHECK_ALLOC( !result ); + ym2413.enable( true ); + set_voice_count( 8 ); + } + + if ( uses_fm ) + { + RETURN_ERR( Dual_Resampler::reset( blip_buf.length() * blip_buf.sample_rate() / 1000 ) ); + psg.volume( 0.135 * fm_gain * gain() ); + } + else + { + ym2612.enable( false ); + ym2413.enable( false ); + psg.volume( gain() ); + } + + return 0; +} + +// Emulation + +blargg_err_t Vgm_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + psg.reset( get_le16( header().noise_feedback ), header().noise_width ); + + dac_disabled = -1; + pos = data + header_size; + pcm_data = pos; + pcm_pos = pos; + dac_amp = -1; + vgm_time = 0; + if ( get_le32( header().version ) >= 0x150 ) + { + long data_offset = get_le32( header().data_offset ); + check( data_offset ); + if ( data_offset ) + pos += data_offset + offsetof (header_t,data_offset) - 0x40; + } + + if ( uses_fm ) + { + if ( ym2413.enabled() ) + ym2413.reset(); + + if ( ym2612.enabled() ) + ym2612.reset(); + + fm_time_offset = 0; + blip_buf.clear(); + Dual_Resampler::clear(); + } + return 0; +} + +blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec ) +{ + time_io = run_commands( msec * vgm_rate / 1000 ); + psg.end_frame( time_io ); + return 0; +} + +blargg_err_t Vgm_Emu::play_( long count, sample_t* out ) +{ + if ( !uses_fm ) + return Classic_Emu::play_( count, out ); + + Dual_Resampler::dual_play( count, out, blip_buf ); + return 0; +} diff --git a/thirdparty/game-music-emu/gme/Vgm_Emu.h b/thirdparty/game-music-emu/gme/Vgm_Emu.h new file mode 100644 index 000000000..40cfb7102 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Vgm_Emu.h @@ -0,0 +1,86 @@ +// Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef VGM_EMU_H +#define VGM_EMU_H + +#include "Vgm_Emu_Impl.h" + +// Emulates VGM music using SN76489/SN76496 PSG, YM2612, and YM2413 FM sound chips. +// Supports custom sound buffer and frequency equalization when VGM uses just the PSG. +// FM sound chips can be run at their proper rates, or slightly higher to reduce +// aliasing on high notes. Currently YM2413 support requires that you supply a +// YM2413 sound chip emulator. I can provide one I've modified to work with the library. +class Vgm_Emu : public Vgm_Emu_Impl { +public: + // True if custom buffer and custom equalization are supported + // TODO: move into Music_Emu and rename to something like supports_custom_buffer() + bool is_classic_emu() const { return !uses_fm; } + + blargg_err_t set_multi_channel ( bool is_enabled ) override; + + // Disable running FM chips at higher than normal rate. Will result in slightly + // more aliasing of high notes. + void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; } + + // VGM header format + enum { header_size = 0x40 }; + struct header_t + { + char tag [4]; + byte data_size [4]; + byte version [4]; + byte psg_rate [4]; + byte ym2413_rate [4]; + byte gd3_offset [4]; + byte track_duration [4]; + byte loop_offset [4]; + byte loop_duration [4]; + byte frame_rate [4]; + byte noise_feedback [2]; + byte noise_width; + byte unused1; + byte ym2612_rate [4]; + byte ym2151_rate [4]; + byte data_offset [4]; + byte unused2 [8]; + }; + + // Header for currently loaded file + header_t const& header() const { return *(header_t const*) data; } + + static gme_type_t static_type() { return gme_vgm_type; } + +public: + // deprecated + using Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + byte const* gd3_data( int* size_out = 0 ) const; // use track_info() + +public: + Vgm_Emu(); + ~Vgm_Emu(); +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_mem_( byte const*, long ); + blargg_err_t set_sample_rate_( long sample_rate ); + blargg_err_t start_track_( int ); + blargg_err_t play_( long count, sample_t* ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void mute_voices_( int mask ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); +private: + // removed; use disable_oversampling() and set_tempo() instead + Vgm_Emu( bool oversample, double tempo = 1.0 ); + double fm_rate; + long psg_rate; + long vgm_rate; + bool disable_oversampling_; + bool uses_fm; + blargg_err_t setup_fm(); +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Vgm_Emu_Impl.cpp b/thirdparty/game-music-emu/gme/Vgm_Emu_Impl.cpp new file mode 100644 index 000000000..0d400254d --- /dev/null +++ b/thirdparty/game-music-emu/gme/Vgm_Emu_Impl.cpp @@ -0,0 +1,314 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Vgm_Emu.h" + +#include +#include +#include "blargg_endian.h" + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +enum { + cmd_gg_stereo = 0x4F, + cmd_psg = 0x50, + cmd_ym2413 = 0x51, + cmd_ym2612_port0 = 0x52, + cmd_ym2612_port1 = 0x53, + cmd_ym2151 = 0x54, + cmd_delay = 0x61, + cmd_delay_735 = 0x62, + cmd_delay_882 = 0x63, + cmd_byte_delay = 0x64, + cmd_end = 0x66, + cmd_data_block = 0x67, + cmd_short_delay = 0x70, + cmd_pcm_delay = 0x80, + cmd_pcm_seek = 0xE0, + + pcm_block_type = 0x00, + ym2612_dac_port = 0x2A +}; + +inline int command_len( int command ) +{ + switch ( command >> 4 ) + { + case 0x03: + case 0x04: + return 2; + + case 0x05: + case 0x0A: + case 0x0B: + return 3; + + case 0x0C: + case 0x0D: + return 4; + + case 0x0E: + case 0x0F: + return 5; + } + + check( false ); + return 1; +} + +template +inline void Ym_Emu::begin_frame( short* p ) +{ + require( enabled() ); + out = p; + last_time = 0; +} + +template +inline int Ym_Emu::run_until( int time ) +{ + int count = time - last_time; + if ( count > 0 ) + { + if ( last_time < 0 ) + return false; + last_time = time; + short* p = out; + out += count * Emu::out_chan_count; + Emu::run( count, p ); + } + return true; +} + +inline Vgm_Emu_Impl::fm_time_t Vgm_Emu_Impl::to_fm_time( vgm_time_t t ) const +{ + return (t * fm_time_factor + fm_time_offset) >> fm_time_bits; +} + +inline blip_time_t Vgm_Emu_Impl::to_blip_time( vgm_time_t t ) const +{ + return (t * blip_time_factor) >> blip_time_bits; +} + +void Vgm_Emu_Impl::write_pcm( vgm_time_t vgm_time, int amp ) +{ + blip_time_t blip_time = to_blip_time( vgm_time ); + int old = dac_amp; + int delta = amp - old; + dac_amp = amp; + if ( old >= 0 ) + dac_synth.offset_inline( blip_time, delta, &blip_buf ); + else + dac_amp |= dac_disabled; +} + +blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) +{ + vgm_time_t vgm_time = this->vgm_time; + byte const* pos = this->pos; + if ( pos >= data_end ) + { + set_track_ended(); + if ( pos > data_end ) + set_warning( "Stream lacked end event" ); + } + + while ( vgm_time < end_time && pos < data_end ) + { + // TODO: be sure there are enough bytes left in stream for particular command + // so we don't read past end + switch ( *pos++ ) + { + case cmd_end: + pos = loop_begin; // if not looped, loop_begin == data_end + break; + + case cmd_delay_735: + vgm_time += 735; + break; + + case cmd_delay_882: + vgm_time += 882; + break; + + case cmd_gg_stereo: + psg.write_ggstereo( to_blip_time( vgm_time ), *pos++ ); + break; + + case cmd_psg: + psg.write_data( to_blip_time( vgm_time ), *pos++ ); + break; + + case cmd_delay: + vgm_time += pos [1] * 0x100L + pos [0]; + pos += 2; + break; + + case cmd_byte_delay: + vgm_time += *pos++; + break; + + case cmd_ym2413: + if ( ym2413.run_until( to_fm_time( vgm_time ) ) ) + ym2413.write( pos [0], pos [1] ); + pos += 2; + break; + + case cmd_ym2612_port0: + if ( pos [0] == ym2612_dac_port ) + { + write_pcm( vgm_time, pos [1] ); + } + else if ( ym2612.run_until( to_fm_time( vgm_time ) ) ) + { + if ( pos [0] == 0x2B ) + { + dac_disabled = (pos [1] >> 7 & 1) - 1; + dac_amp |= dac_disabled; + } + ym2612.write0( pos [0], pos [1] ); + } + pos += 2; + break; + + case cmd_ym2612_port1: + if ( ym2612.run_until( to_fm_time( vgm_time ) ) ) + ym2612.write1( pos [0], pos [1] ); + pos += 2; + break; + + case cmd_data_block: { + check( *pos == cmd_end ); + int type = pos [1]; + long size = get_le32( pos + 2 ); + pos += 6; + if ( type == pcm_block_type ) + pcm_data = pos; + pos += size; + break; + } + + case cmd_pcm_seek: + pcm_pos = pcm_data + pos [3] * 0x1000000L + pos [2] * 0x10000L + + pos [1] * 0x100L + pos [0]; + pos += 4; + break; + + default: + int cmd = pos [-1]; + switch ( cmd & 0xF0 ) + { + case cmd_pcm_delay: + write_pcm( vgm_time, *pcm_pos++ ); + vgm_time += cmd & 0x0F; + break; + + case cmd_short_delay: + vgm_time += (cmd & 0x0F) + 1; + break; + + case 0x50: + pos += 2; + break; + + default: + pos += command_len( cmd ) - 1; + set_warning( "Unknown stream event" ); + } + } + } + vgm_time -= end_time; + this->pos = pos; + this->vgm_time = vgm_time; + + return to_blip_time( end_time ); +} + +int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ) +{ + // to do: timing is working mostly by luck + + int min_pairs = sample_count >> 1; + int vgm_time = ((long) min_pairs << fm_time_bits) / fm_time_factor - 1; + assert( to_fm_time( vgm_time ) <= min_pairs ); + int pairs = min_pairs; + while ( (pairs = to_fm_time( vgm_time )) < min_pairs ) + vgm_time++; + //debug_printf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs ); + + if ( ym2612.enabled() ) + { + ym2612.begin_frame( buf ); + memset( buf, 0, pairs * stereo * sizeof *buf ); + } + else if ( ym2413.enabled() ) + { + ym2413.begin_frame( buf ); + } + + run_commands( vgm_time ); + ym2612.run_until( pairs ); + ym2413.run_until( pairs ); + + fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) - + ((long) pairs << fm_time_bits); + + psg.end_frame( blip_time ); + + return pairs * stereo; +} + +// Update pre-1.10 header FM rates by scanning commands +void Vgm_Emu_Impl::update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const +{ + byte const* p = data + 0x40; + while ( p < data_end ) + { + switch ( *p ) + { + case cmd_end: + return; + + case cmd_psg: + case cmd_byte_delay: + p += 2; + break; + + case cmd_delay: + p += 3; + break; + + case cmd_data_block: + p += 7 + get_le32( p + 3 ); + break; + + case cmd_ym2413: + *ym2612_rate = 0; + return; + + case cmd_ym2612_port0: + case cmd_ym2612_port1: + *ym2612_rate = *ym2413_rate; + *ym2413_rate = 0; + return; + + case cmd_ym2151: + *ym2413_rate = 0; + *ym2612_rate = 0; + return; + + default: + p += command_len( *p ); + } + } +} diff --git a/thirdparty/game-music-emu/gme/Vgm_Emu_Impl.h b/thirdparty/game-music-emu/gme/Vgm_Emu_Impl.h new file mode 100644 index 000000000..dadbb9207 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Vgm_Emu_Impl.h @@ -0,0 +1,71 @@ +// Low-level parts of Vgm_Emu + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef VGM_EMU_IMPL_H +#define VGM_EMU_IMPL_H + +#include "Dual_Resampler.h" +#include "Classic_Emu.h" +#include "Ym2413_Emu.h" +#include "Ym2612_Emu.h" +#include "Sms_Apu.h" + +template +class Ym_Emu : public Emu { +protected: + int last_time; + short* out; + enum { disabled_time = -1 }; +public: + Ym_Emu() : last_time( disabled_time ), out( NULL ) { } + void enable( bool b ) { last_time = b ? 0 : disabled_time; } + bool enabled() const { return last_time != disabled_time; } + void begin_frame( short* p ); + int run_until( int time ); +}; + +class Vgm_Emu_Impl : public Classic_Emu, private Dual_Resampler { +public: + typedef Classic_Emu::sample_t sample_t; +protected: + enum { stereo = 2 }; + + typedef int vgm_time_t; + + enum { fm_time_bits = 12 }; + typedef int fm_time_t; + long fm_time_offset; + int fm_time_factor; + fm_time_t to_fm_time( vgm_time_t ) const; + + enum { blip_time_bits = 12 }; + int blip_time_factor; + blip_time_t to_blip_time( vgm_time_t ) const; + + byte const* data; + byte const* loop_begin; + byte const* data_end; + void update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const; + + vgm_time_t vgm_time; + byte const* pos; + blip_time_t run_commands( vgm_time_t ); + int play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ); + + byte const* pcm_data; + byte const* pcm_pos; + int dac_amp; + int dac_disabled; // -1 if disabled + void write_pcm( vgm_time_t, int amp ); + + Ym_Emu ym2612; + Ym_Emu ym2413; + + Blip_Buffer blip_buf; + Sms_Apu psg; + Blip_Synth dac_synth; + + friend class Vgm_Emu; +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Ym2413_Emu.cpp b/thirdparty/game-music-emu/gme/Ym2413_Emu.cpp new file mode 100644 index 000000000..01e796d95 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ym2413_Emu.cpp @@ -0,0 +1,21 @@ + +// Use in place of Ym2413_Emu.cpp and ym2413.c to disable support for this chip + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Ym2413_Emu.h" + +Ym2413_Emu::Ym2413_Emu() { } + +Ym2413_Emu::~Ym2413_Emu() { } + +int Ym2413_Emu::set_rate( double, double ) { return 2; } + +void Ym2413_Emu::reset() { } + +void Ym2413_Emu::write( int, int ) { } + +void Ym2413_Emu::mute_voices( int ) { } + +void Ym2413_Emu::run( int, sample_t* ) { } + diff --git a/thirdparty/game-music-emu/gme/Ym2413_Emu.h b/thirdparty/game-music-emu/gme/Ym2413_Emu.h new file mode 100644 index 000000000..ed4fd11df --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ym2413_Emu.h @@ -0,0 +1,33 @@ +// YM2413 FM sound chip emulator interface + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef YM2413_EMU_H +#define YM2413_EMU_H + +class Ym2413_Emu { + struct OPLL* opll; +public: + Ym2413_Emu(); + ~Ym2413_Emu(); + + // Set output sample rate and chip clock rates, in Hz. Returns non-zero + // if error. + int set_rate( double sample_rate, double clock_rate ); + + // Reset to power-up state + void reset(); + + // Mute voice n if bit n (1 << n) of mask is set + enum { channel_count = 14 }; + void mute_voices( int mask ); + + // Write 'data' to 'addr' + void write( int addr, int data ); + + // Run and write pair_count samples to output + typedef short sample_t; + enum { out_chan_count = 2 }; // stereo + void run( int pair_count, sample_t* out ); +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Ym2612_Emu.h b/thirdparty/game-music-emu/gme/Ym2612_Emu.h new file mode 100644 index 000000000..f62209a07 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ym2612_Emu.h @@ -0,0 +1,19 @@ +// YM2612 FM sound chip emulator interface + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#ifdef VGM_YM2612_GENS // LGPL v2.1+ license +#include "Ym2612_GENS.h" +typedef Ym2612_GENS_Emu Ym2612_Emu; +#endif + +#ifdef VGM_YM2612_NUKED // LGPL v2.1+ license +#include "Ym2612_Nuked.h" +typedef Ym2612_Nuked_Emu Ym2612_Emu; +#endif + +#ifdef VGM_YM2612_MAME // GPL v2+ license +#include "Ym2612_MAME.h" +typedef Ym2612_MAME_Emu Ym2612_Emu; +#endif + diff --git a/thirdparty/game-music-emu/gme/Ym2612_GENS.cpp b/thirdparty/game-music-emu/gme/Ym2612_GENS.cpp new file mode 100644 index 000000000..d9930d62b --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ym2612_GENS.cpp @@ -0,0 +1,1319 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +// Based on Gens 2.10 ym2612.c + +#include "Ym2612_GENS.h" + +#include +#include +#include +#include +#include +#include + +/* Copyright (C) 2002 Stéphane Dallongeville (gens AT consolemul.com) */ +/* Copyright (C) 2004-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +// This is mostly the original source in its C style and all. +// +// Somewhat optimized and simplified. Uses a template to generate the many +// variants of Update_Chan. Rewrote header file. In need of full rewrite by +// someone more familiar with FM sound and the YM2612. Has some inaccuracies +// compared to the Sega Genesis sound, particularly being mixed at such a +// high sample accuracy (the Genesis sounds like it has only 8 bit samples). +// - Shay + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +const int output_bits = 14; + +struct slot_t +{ + const int *DT; // parametre detune + int MUL; // parametre "multiple de frequence" + int TL; // Total Level = volume lorsque l'enveloppe est au plus haut + int TLL; // Total Level ajusted + int SLL; // Sustin Level (ajusted) = volume où l'enveloppe termine sa premiere phase de regression + int KSR_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe + int KSR; // Key Scale Rate = cette valeur est calculee par rapport à la frequence actuelle, elle va influer + // sur les differents parametres de l'enveloppe comme l'attaque, le decay ... comme dans la realite ! + int SEG; // Type enveloppe SSG + int env_xor; + int env_max; + + const int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR]) + const int *DR; // Decay Rate (table pointeur) = Taux pour la regression (DR[KSR]) + const int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR]) + const int *RR; // Release Rate (table pointeur) = Taux pour le rel'chement (RR[KSR]) + int Fcnt; // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN[Finc >> 16]) + int Finc; // frequency step = pas d'incrementation du compteur-frequence + // plus le pas est grand, plus la frequence est aïgu (ou haute) + int Ecurp; // Envelope current phase = cette variable permet de savoir dans quelle phase + // de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ... + // en fonction de la valeur de cette variable, on va appeler une fonction permettant + // de mettre à jour l'enveloppe courante. + int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe + int Einc; // Envelope step courant + int Ecmp; // Envelope counter limite pour la prochaine phase + int EincA; // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque + // cette valeur est egal à AR[KSR] + int EincD; // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression + // cette valeur est egal à DR[KSR] + int EincS; // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue + // cette valeur est egal à SR[KSR] + int EincR; // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement + // cette valeur est egal à RR[KSR] + int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entree + // d'un autre ou carrement à la sortie de la voie + int INd; // input data of the slot = donnees en entree du slot + int ChgEnM; // Change envelop mask. + int AMS; // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO + int AMSon; // AMS enable flag = drapeau d'activation de l'AMS +}; + +struct channel_t +{ + int S0_OUT[4]; // anciennes sorties slot 0 (pour le feed back) + int LEFT; // LEFT enable flag + int RIGHT; // RIGHT enable flag + int ALGO; // Algorythm = determine les connections entre les operateurs + int FB; // shift count of self feed back = degre de "Feed-Back" du SLOT 1 (il est son unique entree) + int FMS; // Frequency Modulation Sensitivity of channel = degre de modulation de la frequence sur la voie par le LFO + int AMS; // Amplitude Modulation Sensitivity of channel = degre de modulation de l'amplitude sur la voie par le LFO + int FNUM[4]; // hauteur frequence de la voie (+ 3 pour le mode special) + int FOCT[4]; // octave de la voie (+ 3 pour le mode special) + int KC[4]; // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S) + slot_t SLOT[4]; // four slot.operators = les 4 slots de la voie + int FFlag; // Frequency step recalculation flag +}; + +struct state_t +{ + int TimerBase; // TimerBase calculation + int Status; // YM2612 Status (timer overflow) + int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter + int TimerAL; + int TimerAcnt; // timerA counter = valeur courante du Timer A + int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter + int TimerBL; + int TimerBcnt; // timerB counter = valeur courante du Timer B + int Mode; // Mode actuel des voie 3 et 6 (normal / special) + int DAC; // DAC enabled flag + channel_t CHANNEL[Ym2612_GENS_Emu::channel_count]; // Les 6 voies du YM2612 + int REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif + // cela nous rend le debuggage plus facile +}; + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +#define ATTACK 0 +#define DECAY 1 +#define SUBSTAIN 2 +#define RELEASE 3 + +// SIN_LBITS <= 16 +// LFO_HBITS <= 16 +// (SIN_LBITS + SIN_HBITS) <= 26 +// (ENV_LBITS + ENV_HBITS) <= 28 +// (LFO_LBITS + LFO_HBITS) <= 28 + +#define SIN_HBITS 12 // Sinus phase counter int part +#define SIN_LBITS (26 - SIN_HBITS) // Sinus phase counter float part (best setting) + +#if (SIN_LBITS > 16) +#define SIN_LBITS 16 // Can't be greater than 16 bits +#endif + +#define ENV_HBITS 12 // Env phase counter int part +#define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting) + +#define LFO_HBITS 10 // LFO phase counter int part +#define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting) + +#define SIN_LENGHT (1 << SIN_HBITS) +#define ENV_LENGHT (1 << ENV_HBITS) +#define LFO_LENGHT (1 << LFO_HBITS) + +#define TL_LENGHT (ENV_LENGHT * 3) // Env + TL scaling + LFO + +#define SIN_MASK (SIN_LENGHT - 1) +#define ENV_MASK (ENV_LENGHT - 1) +#define LFO_MASK (LFO_LENGHT - 1) + +#define ENV_STEP (96.0 / ENV_LENGHT) // ENV_MAX = 96 dB + +#define ENV_ATTACK ((ENV_LENGHT * 0) << ENV_LBITS) +#define ENV_DECAY ((ENV_LENGHT * 1) << ENV_LBITS) +#define ENV_END ((ENV_LENGHT * 2) << ENV_LBITS) + +#define MAX_OUT_BITS (SIN_HBITS + SIN_LBITS + 2) // Modulation = -4 <--> +4 +#define MAX_OUT ((1 << MAX_OUT_BITS) - 1) + +#define PG_CUT_OFF ((int) (78.0 / ENV_STEP)) +#define ENV_CUT_OFF ((int) (68.0 / ENV_STEP)) + +#define AR_RATE 399128 +#define DR_RATE 5514396 + +//#define AR_RATE 426136 +//#define DR_RATE (AR_RATE * 12) + +#define LFO_FMS_LBITS 9 // FIXED (LFO_FMS_BASE gives somethink as 1) +#define LFO_FMS_BASE ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS))) + +#define S0 0 // Stupid typo of the YM2612 +#define S1 2 +#define S2 1 +#define S3 3 + +inline void set_seg( slot_t& s, int seg ) +{ + s.env_xor = 0; + s.env_max = INT_MAX; + s.SEG = seg; + if ( seg & 4 ) + { + s.env_xor = ENV_MASK; + s.env_max = ENV_MASK; + } +} + +struct tables_t +{ + short SIN_TAB [SIN_LENGHT]; // SINUS TABLE (offset into TL TABLE) + int LFOcnt; // LFO counter = compteur-frequence pour le LFO + int LFOinc; // LFO step counter = pas d'incrementation du compteur-frequence du LFO + // plus le pas est grand, plus la frequence est grande + unsigned int AR_TAB [128]; // Attack rate table + unsigned int DR_TAB [96]; // Decay rate table + unsigned int DT_TAB [8] [32]; // Detune table + unsigned int SL_TAB [16]; // Substain level table + unsigned int NULL_RATE [32]; // Table for NULL rate + int LFO_INC_TAB [8]; // LFO step table + + short ENV_TAB [2 * ENV_LENGHT + 8]; // ENV CURVE TABLE (attack & decay) + + short LFO_ENV_TAB [LFO_LENGHT]; // LFO AMS TABLE (adjusted for 11.8 dB) + short LFO_FREQ_TAB [LFO_LENGHT]; // LFO FMS TABLE + int TL_TAB [TL_LENGHT * 2]; // TOTAL LEVEL TABLE (positif and minus) + unsigned int DECAY_TO_ATTACK [ENV_LENGHT]; // Conversion from decay to attack phase + unsigned int FINC_TAB [2048]; // Frequency step table +}; + +static const unsigned char DT_DEF_TAB [4 * 32] = +{ +// FD = 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + +// FD = 1 + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, + +// FD = 2 + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16, + +// FD = 3 + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22 +}; + +static const unsigned char FKEY_TAB [16] = +{ + 0, 0, 0, 0, + 0, 0, 0, 1, + 2, 3, 3, 3, + 3, 3, 3, 3 +}; + +static const unsigned char LFO_AMS_TAB [4] = +{ + 31, 4, 1, 0 +}; + +static const unsigned char LFO_FMS_TAB [8] = +{ + LFO_FMS_BASE * 0, LFO_FMS_BASE * 1, + LFO_FMS_BASE * 2, LFO_FMS_BASE * 3, + LFO_FMS_BASE * 4, LFO_FMS_BASE * 6, + LFO_FMS_BASE * 12, LFO_FMS_BASE * 24 +}; + +inline void YM2612_Special_Update() { } + +struct Ym2612_GENS_Impl +{ + enum { channel_count = Ym2612_GENS_Emu::channel_count }; + + state_t YM2612; + int mute_mask; + tables_t g; + + void KEY_ON( channel_t&, int ); + void KEY_OFF( channel_t&, int ); + int SLOT_SET( int, int ); + int CHANNEL_SET( int, int ); + int YM_SET( int, int ); + + void set_rate( double sample_rate, double clock_factor ); + void reset(); + void write0( int addr, int data ); + void write1( int addr, int data ); + void run_timer( int ); + void run( int pair_count, Ym2612_GENS_Emu::sample_t* ); +}; + +void Ym2612_GENS_Impl::KEY_ON( channel_t& ch, int nsl) +{ + slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot + + if (SL->Ecurp == RELEASE) // la touche est-elle rel'chee ? + { + SL->Fcnt = 0; + + // Fix Ecco 2 splash sound + + SL->Ecnt = (g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM; + SL->ChgEnM = ~0; + +// SL->Ecnt = g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK; +// SL->Ecnt = 0; + + SL->Einc = SL->EincA; + SL->Ecmp = ENV_DECAY; + SL->Ecurp = ATTACK; + } +} + + +void Ym2612_GENS_Impl::KEY_OFF(channel_t& ch, int nsl) +{ + slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot + + if (SL->Ecurp != RELEASE) // la touche est-elle appuyee ? + { + if (SL->Ecnt < ENV_DECAY) // attack phase ? + { + SL->Ecnt = (g.ENV_TAB [SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY; + } + + SL->Einc = SL->EincR; + SL->Ecmp = ENV_END; + SL->Ecurp = RELEASE; + } +} + + +int Ym2612_GENS_Impl::SLOT_SET( int Adr, int data ) +{ + int nch = Adr & 3; + if ( nch == 3 ) + return 1; + + channel_t& ch = YM2612.CHANNEL [nch + (Adr & 0x100 ? 3 : 0)]; + slot_t& sl = ch.SLOT [(Adr >> 2) & 3]; + + switch ( Adr & 0xF0 ) + { + case 0x30: + if ( (sl.MUL = (data & 0x0F)) != 0 ) sl.MUL <<= 1; + else sl.MUL = 1; + + sl.DT = (int*) g.DT_TAB [(data >> 4) & 7]; + + ch.SLOT [0].Finc = -1; + + break; + + case 0x40: + sl.TL = data & 0x7F; + + // SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound... + YM2612_Special_Update(); + +#if ((ENV_HBITS - 7) < 0) + sl.TLL = sl.TL >> (7 - ENV_HBITS); +#else + sl.TLL = sl.TL << (ENV_HBITS - 7); +#endif + + break; + + case 0x50: + sl.KSR_S = 3 - (data >> 6); + + ch.SLOT [0].Finc = -1; + + if (data &= 0x1F) sl.AR = (int*) &g.AR_TAB [data << 1]; + else sl.AR = (int*) &g.NULL_RATE [0]; + + sl.EincA = sl.AR [sl.KSR]; + if (sl.Ecurp == ATTACK) sl.Einc = sl.EincA; + break; + + case 0x60: + if ( (sl.AMSon = (data & 0x80)) != 0 ) sl.AMS = ch.AMS; + else sl.AMS = 31; + + if (data &= 0x1F) sl.DR = (int*) &g.DR_TAB [data << 1]; + else sl.DR = (int*) &g.NULL_RATE [0]; + + sl.EincD = sl.DR [sl.KSR]; + if (sl.Ecurp == DECAY) sl.Einc = sl.EincD; + break; + + case 0x70: + if (data &= 0x1F) sl.SR = (int*) &g.DR_TAB [data << 1]; + else sl.SR = (int*) &g.NULL_RATE [0]; + + sl.EincS = sl.SR [sl.KSR]; + if ((sl.Ecurp == SUBSTAIN) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincS; + break; + + case 0x80: + sl.SLL = g.SL_TAB [data >> 4]; + + sl.RR = (int*) &g.DR_TAB [((data & 0xF) << 2) + 2]; + + sl.EincR = sl.RR [sl.KSR]; + if ((sl.Ecurp == RELEASE) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincR; + break; + + case 0x90: + // SSG-EG envelope shapes : + /* + E At Al H + + 1 0 0 0 \\\\ + 1 0 0 1 \___ + 1 0 1 0 \/\/ + 1 0 1 1 \ + 1 1 0 0 //// + 1 1 0 1 / + 1 1 1 0 /\/\ + 1 1 1 1 /___ + + E = SSG-EG enable + At = Start negate + Al = Altern + H = Hold */ + + set_seg( sl, (data & 8) ? (data & 0x0F) : 0 ); + break; + } + + return 0; +} + + +int Ym2612_GENS_Impl::CHANNEL_SET( int Adr, int data ) +{ + int num = Adr & 3; + if ( num == 3 ) + return 1; + + channel_t& ch = YM2612.CHANNEL [num + (Adr & 0x100 ? 3 : 0)]; + + switch ( Adr & 0xFC ) + { + case 0xA0: + YM2612_Special_Update(); + + ch.FNUM [0] = (ch.FNUM [0] & 0x700) + data; + ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; + + ch.SLOT [0].Finc = -1; + break; + + case 0xA4: + YM2612_Special_Update(); + + ch.FNUM [0] = (ch.FNUM [0] & 0x0FF) + ((data & 0x07) << 8); + ch.FOCT [0] = (data & 0x38) >> 3; + ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; + + ch.SLOT [0].Finc = -1; + break; + + case 0xA8: + if ( Adr < 0x100 ) + { + num++; + + YM2612_Special_Update(); + + YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x700) + data; + YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | + FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; + + YM2612.CHANNEL [2].SLOT [0].Finc = -1; + } + break; + + case 0xAC: + if ( Adr < 0x100 ) + { + num++; + + YM2612_Special_Update(); + + YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x0FF) + ((data & 0x07) << 8); + YM2612.CHANNEL [2].FOCT [num] = (data & 0x38) >> 3; + YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | + FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; + + YM2612.CHANNEL [2].SLOT [0].Finc = -1; + } + break; + + case 0xB0: + if ( ch.ALGO != (data & 7) ) + { + // Fix VectorMan 2 heli sound (level 1) + YM2612_Special_Update(); + + ch.ALGO = data & 7; + + ch.SLOT [0].ChgEnM = 0; + ch.SLOT [1].ChgEnM = 0; + ch.SLOT [2].ChgEnM = 0; + ch.SLOT [3].ChgEnM = 0; + } + + ch.FB = 9 - ((data >> 3) & 7); // Real thing ? + +// if (ch.FB = ((data >> 3) & 7)) ch.FB = 9 - ch.FB; // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound... +// else ch.FB = 31; + break; + + case 0xB4: { + YM2612_Special_Update(); + + ch.LEFT = 0 - ((data >> 7) & 1); + ch.RIGHT = 0 - ((data >> 6) & 1); + + ch.AMS = LFO_AMS_TAB [(data >> 4) & 3]; + ch.FMS = LFO_FMS_TAB [data & 7]; + + for ( int i = 0; i < 4; i++ ) + { + slot_t& sl = ch.SLOT [i]; + sl.AMS = (sl.AMSon ? ch.AMS : 31); + } + break; + } + } + + return 0; +} + + +int Ym2612_GENS_Impl::YM_SET(int Adr, int data) +{ + switch ( Adr ) + { + case 0x22: + if (data & 8) // LFO enable + { + // Cool Spot music 1, LFO modified severals time which + // distord the sound, have to check that on a real genesis... + + g.LFOinc = g.LFO_INC_TAB [data & 7]; + } + else + { + g.LFOinc = g.LFOcnt = 0; + } + break; + + case 0x24: + YM2612.TimerA = (YM2612.TimerA & 0x003) | (((int) data) << 2); + + if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) + { + YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; + } + break; + + case 0x25: + YM2612.TimerA = (YM2612.TimerA & 0x3FC) | (data & 3); + + if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) + { + YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; + } + break; + + case 0x26: + YM2612.TimerB = data; + + if (YM2612.TimerBL != (256 - YM2612.TimerB) << (4 + 12)) + { + YM2612.TimerBcnt = YM2612.TimerBL = (256 - YM2612.TimerB) << (4 + 12); + } + break; + + case 0x27: + // Parametre divers + // b7 = CSM MODE + // b6 = 3 slot mode + // b5 = reset b + // b4 = reset a + // b3 = timer enable b + // b2 = timer enable a + // b1 = load b + // b0 = load a + + if ((data ^ YM2612.Mode) & 0x40) + { + // We changed the channel 2 mode, so recalculate phase step + // This fix the punch sound in Street of Rage 2 + + YM2612_Special_Update(); + + YM2612.CHANNEL [2].SLOT [0].Finc = -1; // recalculate phase step + } + +// if ((data & 2) && (YM2612.Status & 2)) YM2612.TimerBcnt = YM2612.TimerBL; +// if ((data & 1) && (YM2612.Status & 1)) YM2612.TimerAcnt = YM2612.TimerAL; + +// YM2612.Status &= (~data >> 4); // Reset du Status au cas ou c'est demande + YM2612.Status &= (~data >> 4) & (data >> 2); // Reset Status + + YM2612.Mode = data; + break; + + case 0x28: { + int nch = data & 3; + if ( nch == 3 ) + return 1; + if ( data & 4 ) + nch += 3; + channel_t& ch = YM2612.CHANNEL [nch]; + + YM2612_Special_Update(); + + if (data & 0x10) KEY_ON(ch, S0); // On appuie sur la touche pour le slot 1 + else KEY_OFF(ch, S0); // On rel'che la touche pour le slot 1 + if (data & 0x20) KEY_ON(ch, S1); // On appuie sur la touche pour le slot 3 + else KEY_OFF(ch, S1); // On rel'che la touche pour le slot 3 + if (data & 0x40) KEY_ON(ch, S2); // On appuie sur la touche pour le slot 2 + else KEY_OFF(ch, S2); // On rel'che la touche pour le slot 2 + if (data & 0x80) KEY_ON(ch, S3); // On appuie sur la touche pour le slot 4 + else KEY_OFF(ch, S3); // On rel'che la touche pour le slot 4 + break; + } + + case 0x2B: + if (YM2612.DAC ^ (data & 0x80)) YM2612_Special_Update(); + + YM2612.DAC = data & 0x80; // activation/desactivation du DAC + break; + } + + return 0; +} + +void Ym2612_GENS_Impl::set_rate( double sample_rate, double clock_rate ) +{ + assert( sample_rate ); + assert( clock_rate > sample_rate ); + + int i; + + // 144 = 12 * (prescale * 2) = 12 * 6 * 2 + // prescale set to 6 by default + + double Frequence = clock_rate / sample_rate / 144.0; + if ( fabs( Frequence - 1.0 ) < 0.0000001 ) + Frequence = 1.0; + YM2612.TimerBase = int (Frequence * 4096.0); + + // Tableau TL : + // [0 - 4095] = +output [4095 - ...] = +output overflow (fill with 0) + // [12288 - 16383] = -output [16384 - ...] = -output overflow (fill with 0) + + for(i = 0; i < TL_LENGHT; i++) + { + if (i >= PG_CUT_OFF) // YM2612 cut off sound after 78 dB (14 bits output ?) + { + g.TL_TAB [TL_LENGHT + i] = g.TL_TAB [i] = 0; + } + else + { + double x = MAX_OUT; // Max output + x /= pow( 10.0, (ENV_STEP * i) / 20.0 ); // Decibel -> Voltage + + g.TL_TAB [i] = (int) x; + g.TL_TAB [TL_LENGHT + i] = -g.TL_TAB [i]; + } + } + + // Tableau SIN : + // g.SIN_TAB [x] [y] = sin(x) * y; + // x = phase and y = volume + + g.SIN_TAB [0] = g.SIN_TAB [SIN_LENGHT / 2] = PG_CUT_OFF; + + for(i = 1; i <= SIN_LENGHT / 4; i++) + { + double x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT)); // Sinus + x = 20 * log10(1 / x); // convert to dB + + int j = (int) (x / ENV_STEP); // Get TL range + + if (j > PG_CUT_OFF) j = (int) PG_CUT_OFF; + + g.SIN_TAB [i] = g.SIN_TAB [(SIN_LENGHT / 2) - i] = j; + g.SIN_TAB [(SIN_LENGHT / 2) + i] = g.SIN_TAB [SIN_LENGHT - i] = TL_LENGHT + j; + } + + // Tableau LFO (LFO wav) : + + for(i = 0; i < LFO_LENGHT; i++) + { + double x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus + x += 1.0; + x /= 2.0; // positive only + x *= 11.8 / ENV_STEP; // ajusted to MAX enveloppe modulation + + g.LFO_ENV_TAB [i] = (int) x; + + x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus + x *= (double) ((1 << (LFO_HBITS - 1)) - 1); + + g.LFO_FREQ_TAB [i] = (int) x; + + } + + // Tableau Enveloppe : + // g.ENV_TAB [0] -> g.ENV_TAB [ENV_LENGHT - 1] = attack curve + // g.ENV_TAB [ENV_LENGHT] -> g.ENV_TAB [2 * ENV_LENGHT - 1] = decay curve + + for(i = 0; i < ENV_LENGHT; i++) + { + // Attack curve (x^8 - music level 2 Vectorman 2) + double x = pow(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8); + x *= ENV_LENGHT; + + g.ENV_TAB [i] = (int) x; + + // Decay curve (just linear) + x = pow(((double) (i) / (double) (ENV_LENGHT)), 1); + x *= ENV_LENGHT; + + g.ENV_TAB [ENV_LENGHT + i] = (int) x; + } + for ( i = 0; i < 8; i++ ) + g.ENV_TAB [i + ENV_LENGHT * 2] = 0; + + g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1; // for the stopped state + + // Tableau pour la conversion Attack -> Decay and Decay -> Attack + + int j = ENV_LENGHT - 1; + for ( i = 0; i < ENV_LENGHT; i++ ) + { + while ( j && g.ENV_TAB [j] < i ) + j--; + + g.DECAY_TO_ATTACK [i] = j << ENV_LBITS; + } + + // Tableau pour le Substain Level + + for(i = 0; i < 15; i++) + { + double x = i * 3; // 3 and not 6 (Mickey Mania first music for test) + x /= ENV_STEP; + + g.SL_TAB [i] = ((int) x << ENV_LBITS) + ENV_DECAY; + } + + g.SL_TAB [15] = ((ENV_LENGHT - 1) << ENV_LBITS) + ENV_DECAY; // special case : volume off + + // Tableau Frequency Step + + for(i = 0; i < 2048; i++) + { + double x = (double) (i) * Frequence; + +#if ((SIN_LBITS + SIN_HBITS - (21 - 7)) < 0) + x /= (double) (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS)); +#else + x *= (double) (1 << (SIN_LBITS + SIN_HBITS - (21 - 7))); +#endif + + x /= 2.0; // because MUL = value * 2 + + g.FINC_TAB [i] = (unsigned int) x; + } + + // Tableaux Attack & Decay Rate + + for(i = 0; i < 4; i++) + { + g.AR_TAB [i] = 0; + g.DR_TAB [i] = 0; + } + + for(i = 0; i < 60; i++) + { + double x = Frequence; + + x *= 1.0 + ((i & 3) * 0.25); // bits 0-1 : x1.00, x1.25, x1.50, x1.75 + x *= (double) (1 << ((i >> 2))); // bits 2-5 : shift bits (x2^0 - x2^15) + x *= (double) (ENV_LENGHT << ENV_LBITS); // on ajuste pour le tableau g.ENV_TAB + + g.AR_TAB [i + 4] = (unsigned int) (x / AR_RATE); + g.DR_TAB [i + 4] = (unsigned int) (x / DR_RATE); + } + + for(i = 64; i < 96; i++) + { + g.AR_TAB [i] = g.AR_TAB [63]; + g.DR_TAB [i] = g.DR_TAB [63]; + + g.NULL_RATE [i - 64] = 0; + } + + for ( i = 96; i < 128; i++ ) + g.AR_TAB [i] = 0; + + // Tableau Detune + + for(i = 0; i < 4; i++) + { + for (int j = 0; j < 32; j++) + { +#if ((SIN_LBITS + SIN_HBITS - 21) < 0) + double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS)); +#else + double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21)); +#endif + + g.DT_TAB [i + 0] [j] = (int) y; + g.DT_TAB [i + 4] [j] = (int) -y; + } + } + + // Tableau LFO + g.LFO_INC_TAB [0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + + reset(); +} + +const char* Ym2612_GENS_Emu::set_rate( double sample_rate, double clock_rate ) +{ + if ( !impl ) + { + impl = (Ym2612_GENS_Impl*) malloc( sizeof *impl ); + if ( !impl ) + return "Out of memory"; + impl->mute_mask = 0; + } + memset( &impl->YM2612, 0, sizeof impl->YM2612 ); + + impl->set_rate( sample_rate, clock_rate ); + + return 0; +} + +Ym2612_GENS_Emu::~Ym2612_GENS_Emu() +{ + free( impl ); +} + +inline void Ym2612_GENS_Impl::write0( int opn_addr, int data ) +{ + assert( (unsigned) data <= 0xFF ); + + if ( opn_addr < 0x30 ) + { + YM2612.REG [0] [opn_addr] = data; + YM_SET( opn_addr, data ); + } + else if ( YM2612.REG [0] [opn_addr] != data ) + { + YM2612.REG [0] [opn_addr] = data; + + if ( opn_addr < 0xA0 ) + SLOT_SET( opn_addr, data ); + else + CHANNEL_SET( opn_addr, data ); + } +} + +inline void Ym2612_GENS_Impl::write1( int opn_addr, int data ) +{ + assert( (unsigned) data <= 0xFF ); + + if ( opn_addr >= 0x30 && YM2612.REG [1] [opn_addr] != data ) + { + YM2612.REG [1] [opn_addr] = data; + + if ( opn_addr < 0xA0 ) + SLOT_SET( opn_addr + 0x100, data ); + else + CHANNEL_SET( opn_addr + 0x100, data ); + } +} + +void Ym2612_GENS_Emu::reset() +{ + impl->reset(); +} + +void Ym2612_GENS_Impl::reset() +{ + g.LFOcnt = 0; + YM2612.TimerA = 0; + YM2612.TimerAL = 0; + YM2612.TimerAcnt = 0; + YM2612.TimerB = 0; + YM2612.TimerBL = 0; + YM2612.TimerBcnt = 0; + YM2612.DAC = 0; + + YM2612.Status = 0; + + int i; + for ( i = 0; i < channel_count; i++ ) + { + channel_t& ch = YM2612.CHANNEL [i]; + + ch.LEFT = ~0; + ch.RIGHT = ~0; + ch.ALGO = 0; + ch.FB = 31; + ch.FMS = 0; + ch.AMS = 0; + + for ( int j = 0 ;j < 4 ; j++ ) + { + ch.S0_OUT [j] = 0; + ch.FNUM [j] = 0; + ch.FOCT [j] = 0; + ch.KC [j] = 0; + + ch.SLOT [j].Fcnt = 0; + ch.SLOT [j].Finc = 0; + ch.SLOT [j].Ecnt = ENV_END; // Put it at the end of Decay phase... + ch.SLOT [j].Einc = 0; + ch.SLOT [j].Ecmp = 0; + ch.SLOT [j].Ecurp = RELEASE; + + ch.SLOT [j].ChgEnM = 0; + } + } + + for ( i = 0; i < 0x100; i++ ) + { + YM2612.REG [0] [i] = -1; + YM2612.REG [1] [i] = -1; + } + + for ( i = 0xB6; i >= 0xB4; i-- ) + { + write0( i, 0xC0 ); + write1( i, 0xC0 ); + } + + for ( i = 0xB2; i >= 0x22; i-- ) + { + write0( i, 0 ); + write1( i, 0 ); + } + + write0( 0x2A, 0x80 ); +} + +void Ym2612_GENS_Emu::write0( int addr, int data ) +{ + impl->write0( addr, data ); +} + +void Ym2612_GENS_Emu::write1( int addr, int data ) +{ + impl->write1( addr, data ); +} + +void Ym2612_GENS_Emu::mute_voices( int mask ) { impl->mute_mask = mask; } + +static void update_envelope_( slot_t* sl ) +{ + switch ( sl->Ecurp ) + { + case 0: + // Env_Attack_Next + + // Verified with Gynoug even in HQ (explode SFX) + sl->Ecnt = ENV_DECAY; + + sl->Einc = sl->EincD; + sl->Ecmp = sl->SLL; + sl->Ecurp = DECAY; + break; + + case 1: + // Env_Decay_Next + + // Verified with Gynoug even in HQ (explode SFX) + sl->Ecnt = sl->SLL; + + sl->Einc = sl->EincS; + sl->Ecmp = ENV_END; + sl->Ecurp = SUBSTAIN; + break; + + case 2: + // Env_Substain_Next(slot_t *SL) + if (sl->SEG & 8) // SSG envelope type + { + int release = sl->SEG & 1; + + if ( !release ) + { + // re KEY ON + + // sl->Fcnt = 0; + // sl->ChgEnM = ~0; + + sl->Ecnt = 0; + sl->Einc = sl->EincA; + sl->Ecmp = ENV_DECAY; + sl->Ecurp = ATTACK; + } + + set_seg( *sl, (sl->SEG << 1) & 4 ); + + if ( !release ) + break; + } + // fall through + + case 3: + // Env_Release_Next + sl->Ecnt = ENV_END; + sl->Einc = 0; + sl->Ecmp = ENV_END + 1; + break; + + // default: no op + } +} + +inline void update_envelope( slot_t& sl ) +{ + int ecmp = sl.Ecmp; + if ( (sl.Ecnt += sl.Einc) >= ecmp ) + update_envelope_( &sl ); +} + +template +struct ym2612_update_chan { + static void func( tables_t&, channel_t&, Ym2612_GENS_Emu::sample_t*, int ); +}; + +typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_GENS_Emu::sample_t*, int ); + +template +void ym2612_update_chan::func( tables_t& g, channel_t& ch, + Ym2612_GENS_Emu::sample_t* buf, int length ) +{ + int not_end = ch.SLOT [S3].Ecnt - ENV_END; + + // algo is a compile-time constant, so all conditions based on it are resolved + // during compilation + + // special cases + if ( algo == 7 ) + not_end |= ch.SLOT [S0].Ecnt - ENV_END; + + if ( algo >= 5 ) + not_end |= ch.SLOT [S2].Ecnt - ENV_END; + + if ( algo >= 4 ) + not_end |= ch.SLOT [S1].Ecnt - ENV_END; + + int CH_S0_OUT_1 = ch.S0_OUT [1]; + + int in0 = ch.SLOT [S0].Fcnt; + int in1 = ch.SLOT [S1].Fcnt; + int in2 = ch.SLOT [S2].Fcnt; + int in3 = ch.SLOT [S3].Fcnt; + + int YM2612_LFOinc = g.LFOinc; + int YM2612_LFOcnt = g.LFOcnt + YM2612_LFOinc; + + if ( !not_end ) + return; + + do + { + // envelope + int const env_LFO = g.LFO_ENV_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK]; + + short const* const ENV_TAB = g.ENV_TAB; + + #define CALC_EN( x ) \ + int temp##x = ENV_TAB [ch.SLOT [S##x].Ecnt >> ENV_LBITS] + ch.SLOT [S##x].TLL; \ + int en##x = ((temp##x ^ ch.SLOT [S##x].env_xor) + (env_LFO >> ch.SLOT [S##x].AMS)) & \ + ((temp##x - ch.SLOT [S##x].env_max) >> 31); + + CALC_EN( 0 ) + CALC_EN( 1 ) + CALC_EN( 2 ) + CALC_EN( 3 ) + + int const* const TL_TAB = g.TL_TAB; + + #define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)]) + + // feedback + int CH_S0_OUT_0 = ch.S0_OUT [0]; + { + int temp = in0 + ((CH_S0_OUT_0 + CH_S0_OUT_1) >> ch.FB); + CH_S0_OUT_1 = CH_S0_OUT_0; + CH_S0_OUT_0 = SINT( (temp >> SIN_LBITS) & SIN_MASK, en0 ); + } + + int CH_OUTd; + if ( algo == 0 ) + { + int temp = in1 + CH_S0_OUT_1; + temp = in2 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ); + temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 1 ) + { + int temp = in2 + CH_S0_OUT_1 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); + temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 2 ) + { + int temp = in2 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); + temp = in3 + CH_S0_OUT_1 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 3 ) + { + int temp = in1 + CH_S0_OUT_1; + temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 4 ) + { + int temp = in3 + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ); + //DO_LIMIT + } + else if ( algo == 5 ) + { + int temp = CH_S0_OUT_1; + CH_OUTd = SINT( ((in3 + temp) >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( ((in1 + temp) >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( ((in2 + temp) >> SIN_LBITS) & SIN_MASK, en2 ); + //DO_LIMIT + } + else if ( algo == 6 ) + { + CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); + //DO_LIMIT + } + else if ( algo == 7 ) + { + CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ) + CH_S0_OUT_1; + //DO_LIMIT + } + + CH_OUTd >>= MAX_OUT_BITS - output_bits + 2; + + // update phase + unsigned freq_LFO = ((g.LFO_FREQ_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK] * + ch.FMS) >> (LFO_HBITS - 1 + 1)) + (1L << (LFO_FMS_LBITS - 1)); + YM2612_LFOcnt += YM2612_LFOinc; + in0 += (ch.SLOT [S0].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + in1 += (ch.SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + in2 += (ch.SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + in3 += (ch.SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + + int t0 = buf [0] + (CH_OUTd & ch.LEFT); + int t1 = buf [1] + (CH_OUTd & ch.RIGHT); + + update_envelope( ch.SLOT [0] ); + update_envelope( ch.SLOT [1] ); + update_envelope( ch.SLOT [2] ); + update_envelope( ch.SLOT [3] ); + + ch.S0_OUT [0] = CH_S0_OUT_0; + buf [0] = t0; + buf [1] = t1; + buf += 2; + } + while ( --length ); + + ch.S0_OUT [1] = CH_S0_OUT_1; + + ch.SLOT [S0].Fcnt = in0; + ch.SLOT [S1].Fcnt = in1; + ch.SLOT [S2].Fcnt = in2; + ch.SLOT [S3].Fcnt = in3; +} + +static const ym2612_update_chan_t UPDATE_CHAN [8] = { + &ym2612_update_chan<0>::func, + &ym2612_update_chan<1>::func, + &ym2612_update_chan<2>::func, + &ym2612_update_chan<3>::func, + &ym2612_update_chan<4>::func, + &ym2612_update_chan<5>::func, + &ym2612_update_chan<6>::func, + &ym2612_update_chan<7>::func +}; + +void Ym2612_GENS_Impl::run_timer( int length ) +{ + int const step = 6; + int remain = length; + do + { + int n = step; + if ( n > remain ) + n = remain; + remain -= n; + + long i = n * YM2612.TimerBase; + if (YM2612.Mode & 1) // Timer A ON ? + { + // if ((YM2612.TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) + if ((YM2612.TimerAcnt -= i) <= 0) + { + // timer a overflow + + YM2612.Status |= (YM2612.Mode & 0x04) >> 2; + YM2612.TimerAcnt += YM2612.TimerAL; + + if (YM2612.Mode & 0x80) + { + KEY_ON( YM2612.CHANNEL [2], 0 ); + KEY_ON( YM2612.CHANNEL [2], 1 ); + KEY_ON( YM2612.CHANNEL [2], 2 ); + KEY_ON( YM2612.CHANNEL [2], 3 ); + } + } + } + + if (YM2612.Mode & 2) // Timer B ON ? + { + // if ((YM2612.TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) + if ((YM2612.TimerBcnt -= i) <= 0) + { + // timer b overflow + YM2612.Status |= (YM2612.Mode & 0x08) >> 2; + YM2612.TimerBcnt += YM2612.TimerBL; + } + } + } + while ( remain > 0 ); +} + +void Ym2612_GENS_Impl::run( int pair_count, Ym2612_GENS_Emu::sample_t* out ) +{ + if ( pair_count <= 0 ) + return; + + if ( YM2612.Mode & 3 ) + run_timer( pair_count ); + + // Mise à jour des pas des compteurs-frequences s'ils ont ete modifies + + for ( int chi = 0; chi < channel_count; chi++ ) + { + channel_t& ch = YM2612.CHANNEL [chi]; + if ( ch.SLOT [0].Finc != -1 ) + continue; + + int i2 = 0; + if ( chi == 2 && (YM2612.Mode & 0x40) ) + i2 = 2; + + for ( int i = 0; i < 4; i++ ) + { + // static int seq [4] = { 2, 1, 3, 0 }; + // if ( i2 ) i2 = seq [i]; + + slot_t& sl = ch.SLOT [i]; + int finc = g.FINC_TAB [ch.FNUM [i2]] >> (7 - ch.FOCT [i2]); + int ksr = ch.KC [i2] >> sl.KSR_S; // keycode attenuation + sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL; + if (sl.KSR != ksr) // si le KSR a change alors + { // les differents taux pour l'enveloppe sont mis à jour + sl.KSR = ksr; + + sl.EincA = sl.AR [ksr]; + sl.EincD = sl.DR [ksr]; + sl.EincS = sl.SR [ksr]; + sl.EincR = sl.RR [ksr]; + + if (sl.Ecurp == ATTACK) + { + sl.Einc = sl.EincA; + } + else if (sl.Ecurp == DECAY) + { + sl.Einc = sl.EincD; + } + else if (sl.Ecnt < ENV_END) + { + if (sl.Ecurp == SUBSTAIN) + sl.Einc = sl.EincS; + else if (sl.Ecurp == RELEASE) + sl.Einc = sl.EincR; + } + } + + if ( i2 ) + i2 = (i2 ^ 2) ^ (i2 >> 1); + } + } + + for ( int i = 0; i < channel_count; i++ ) + { + if ( !(mute_mask & (1 << i)) && (i != 5 || !YM2612.DAC) ) + UPDATE_CHAN [YM2612.CHANNEL [i].ALGO]( g, YM2612.CHANNEL [i], out, pair_count ); + } + + g.LFOcnt += g.LFOinc * pair_count; +} + +void Ym2612_GENS_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); } diff --git a/thirdparty/game-music-emu/gme/Ym2612_GENS.h b/thirdparty/game-music-emu/gme/Ym2612_GENS.h new file mode 100644 index 000000000..4cb2e8ae3 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ym2612_GENS.h @@ -0,0 +1,38 @@ +// YM2612 FM sound chip emulator interface + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef YM2612_EMU_H +#define YM2612_EMU_H + +struct Ym2612_GENS_Impl; + +class Ym2612_GENS_Emu { + Ym2612_GENS_Impl* impl; +public: + Ym2612_GENS_Emu() { impl = 0; } + ~Ym2612_GENS_Emu(); + + // Set output sample rate and chip clock rates, in Hz. Returns non-zero + // if error. + const char* set_rate( double sample_rate, double clock_rate ); + + // Reset to power-up state + void reset(); + + // Mute voice n if bit n (1 << n) of mask is set + enum { channel_count = 6 }; + void mute_voices( int mask ); + + // Write addr to register 0 then data to register 1 + void write0( int addr, int data ); + + // Write addr to register 2 then data to register 3 + void write1( int addr, int data ); + + // Run and add pair_count samples into current output buffer contents + typedef short sample_t; + enum { out_chan_count = 2 }; // stereo + void run( int pair_count, sample_t* out ); +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Ym2612_MAME.cpp b/thirdparty/game-music-emu/gme/Ym2612_MAME.cpp new file mode 100644 index 000000000..524dab55a --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ym2612_MAME.cpp @@ -0,0 +1,3108 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +// Based on Mame YM2612 ym2612.c + +#include "Ym2612_MAME.h" + +/* +** +** File: fm2612.c -- software implementation of Yamaha YM2612 FM sound generator +** Split from fm.c to keep 2612 fixes from infecting other OPN chips +** +** Copyright Jarek Burczynski (bujar at mame dot net) +** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 1.5.1 (Genesis Plus GX ym2612.c rev. 368) +** +*/ + +/* +** History: +** +** 2006~2012 Eke-Eke (Genesis Plus GX): +** Huge thanks to Nemesis, lot of those fixes came from his tests on Sega Genesis hardware +** More informations at http://gendev.spritesmind.net/forum/viewtopic.php?t=386 +** +** TODO: +** +** - core documentation +** - BUSY flag support +** +** CHANGELOG: +** +** 26-09-2017 Eke-Eke (Genesis Plus GX): +** - fixed EG counter loopback behavior (verified on YM3438 die) +** - reverted changes to EG rates 2-7 increment values +** +** xx-xx-xxxx +** - fixed LFO implementation: +** .added support for CH3 special mode: fixes various sound effects (birds in Warlock, bug sound in Aladdin...) +** .inverted LFO AM waveform: fixes Spider-Man & Venom : Separation Anxiety (intro), California Games (surfing event) +** .improved LFO timing accuracy: now updated AFTER sample output, like EG/PG updates, and without any precision loss anymore. +** - improved internal timers emulation +** - adjusted lowest EG rates increment values +** - fixed Attack Rate not being updated in some specific cases (Batman & Robin intro) +** - fixed EG behavior when Attack Rate is maximal +** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation +** - implemented EG output immediate changes on register writes +** - fixed YM2612 initial values (after the reset): fixes missing intro in B.O.B +** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many other games using GEMS sound engine) +** - implemented accurate CSM mode emulation +** - implemented accurate SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Stix & many other games) +** - implemented accurate address/data ports behavior +** +** 06-23-2007 Zsolt Vasvari: +** - changed the timing not to require the use of floating point calculations +** +** 03-08-2003 Jarek Burczynski: +** - fixed YM2608 initial values (after the reset) +** - fixed flag and irqmask handling (YM2608) +** - fixed BUFRDY flag handling (YM2608) +** +** 14-06-2003 Jarek Burczynski: +** - implemented all of the YM2608 status register flags +** - implemented support for external memory read/write via YM2608 +** - implemented support for deltat memory limit register in YM2608 emulation +** +** 22-05-2003 Jarek Burczynski: +** - fixed LFO PM calculations (copy&paste bugfix) +** +** 08-05-2003 Jarek Burczynski: +** - fixed SSG support +** +** 22-04-2003 Jarek Burczynski: +** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608) +** +** 15-04-2003 Jarek Burczynski: +** - added support for YM2608's register 0x110 - status mask +** +** 01-12-2002 Jarek Burczynski: +** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608) +** The addressing patch used for early Neo-Geo games can be removed now. +** +** 26-11-2002 Jarek Burczynski, Nicola Salmoria: +** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to: +** - added emulation of YM2608 drums. +** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608) +** +** 16-08-2002 Jarek Burczynski: +** - binary exact Envelope Generator (verified on real YM2203); +** identical to YM2151 +** - corrected 'off by one' error in feedback calculations (when feedback is off) +** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610) +** +** 18-12-2001 Jarek Burczynski: +** - added SSG-EG support (verified on real YM2203) +** +** 12-08-2001 Jarek Burczynski: +** - corrected sin_tab and tl_tab data (verified on real chip) +** - corrected feedback calculations (verified on real chip) +** - corrected phase generator calculations (verified on real chip) +** - corrected envelope generator calculations (verified on real chip) +** - corrected FM volume level (YM2610 and YM2610B). +** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) : +** this was needed to calculate YM2610 FM channels output correctly. +** (Each FM channel is calculated as in other chips, but the output of the channel +** gets shifted right by one *before* sending to accumulator. That was impossible to do +** with previous implementation). +** +** 23-07-2001 Jarek Burczynski, Nicola Salmoria: +** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip) +** +** 11-06-2001 Jarek Burczynski: +** - corrected end of sample bug in ADPCMA_calc_cha(). +** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits). +** +** 08-12-98 hiro-shi: +** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA +** move ROM limit check.(CALC_CH? -> 2610Write1/2) +** test program (ADPCMB_TEST) +** move ADPCM A/B end check. +** ADPCMB repeat flag(no check) +** change ADPCM volume rate (8->16) (32->48). +** +** 09-12-98 hiro-shi: +** change ADPCM volume. (8->16, 48->64) +** replace ym2610 ch0/3 (YM-2610B) +** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff. +** add ADPCM_SHIFT_MASK +** change ADPCMA_DECODE_MIN/MAX. +*/ + +/************************************************************************/ +/* comment of hiro-shi(Hiromitsu Shioya) */ +/* YM2610(B) = OPN-B */ +/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/************************************************************************/ + +#include +#include /* for memset */ +#include /* for NULL */ +#include +#include + +namespace Ym2612_MameImpl +{ + +/* ---- mamedef - begin ---- */ +/* typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h) */ +/* 8-bit values */ +typedef unsigned char UINT8; +typedef signed char INT8; + +/* 16-bit values */ +typedef unsigned short UINT16; +typedef signed short INT16; + +/* 32-bit values */ +#ifndef _WINDOWS_H +typedef unsigned int UINT32; +typedef signed int INT32; +#endif + +/* 64-bit values */ +#ifndef _WINDOWS_H +#ifdef _MSC_VER +typedef signed __int64 INT64; +typedef unsigned __int64 UINT64; +#else +__extension__ typedef unsigned long long UINT64; +__extension__ typedef signed long long INT64; +#endif +#endif + +/* offsets and addresses are 32-bit (for now...) */ +typedef UINT32 offs_t; + +/* stream_sample_t is used to represent a single sample in a sound stream */ +typedef INT16 stream_sample_t; + +#if defined(VGM_BIG_ENDIAN) +#define BYTE_XOR_BE(x) (x) +#elif defined(VGM_LITTLE_ENDIAN) +#define BYTE_XOR_BE(x) ((x) ^ 0x01) +#else +/* don't define BYTE_XOR_BE so that it throws an error when compiling */ +#endif + +#if defined(_MSC_VER) +//#define INLINE static __forceinline +#define INLINE static __inline +#elif defined(__GNUC__) +#define INLINE static __inline__ +#else +#define INLINE static inline +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifdef _DEBUG +#define logerror printf +#else +#define logerror +#endif + +typedef void (*SRATE_CALLBACK)(void*, UINT32); +/* ---- mamedef - end ---- */ + +/* --- select emulation chips --- */ +/* +#define BUILD_YM2203 (HAS_YM2203) // build YM2203(OPN) emulator +#define BUILD_YM2608 (HAS_YM2608) // build YM2608(OPNA) emulator +#define BUILD_YM2610 (HAS_YM2610) // build YM2610(OPNB) emulator +#define BUILD_YM2610B (HAS_YM2610B) // build YM2610B(OPNB?)emulator +#define BUILD_YM2612 (HAS_YM2612) // build YM2612(OPN2) emulator +#define BUILD_YM3438 (HAS_YM3438) // build YM3438(OPN) emulator +*/ +#define BUILD_YM2203 0 +#define BUILD_YM2608 0 +#define BUILD_YM2610 0 +#define BUILD_YM2610B 0 +#define BUILD_YM2612 1 +#define BUILD_YM3438 0 + +#define FM_BUSY_FLAG_SUPPORT 0 + +/* select bit size of output : 8 or 16 */ +#define FM_SAMPLE_BITS 16 + +/* select timer system internal or external */ +#define FM_INTERNAL_TIMER 1 + +/* --- speedup optimize --- */ +/* busy flag enulation , The definition of FM_GET_TIME_NOW() is necessary. */ +/* #define FM_BUSY_FLAG_SUPPORT 1 */ + +/* --- external SSG(YM2149/AY-3-8910)emulator interface port */ +/* used by YM2203,YM2608,and YM2610 */ +typedef struct _ssg_callbacks ssg_callbacks; +struct _ssg_callbacks +{ + void (*set_clock)(void *param, int clock); + void (*write)(void *param, int address, int data); + int (*read)(void *param); + void (*reset)(void *param); +}; + +/* --- external callback funstions for realtime update --- */ + +#if FM_BUSY_FLAG_SUPPORT +#define TIME_TYPE attotime +#define UNDEFINED_TIME attotime_zero +#define FM_GET_TIME_NOW(machine) timer_get_time(machine) +#define ADD_TIMES(t1, t2) attotime_add((t1), (t2)) +#define COMPARE_TIMES(t1, t2) attotime_compare((t1), (t2)) +#define MULTIPLY_TIME_BY_INT(t,i) attotime_mul(t, i) +#endif + +/* compiler dependence */ +#if 0 +#ifndef OSD_CPU_H +#define OSD_CPU_H +typedef unsigned char UINT8; /* unsigned 8bit */ +typedef unsigned short UINT16; /* unsigned 16bit */ +typedef unsigned int UINT32; /* unsigned 32bit */ +typedef signed char INT8; /* signed 8bit */ +typedef signed short INT16; /* signed 16bit */ +typedef signed int INT32; /* signed 32bit */ +#endif /* OSD_CPU_H */ +#endif + +typedef stream_sample_t FMSAMPLE; +/* +#if (FM_SAMPLE_BITS==16) +typedef INT16 FMSAMPLE; +#endif +#if (FM_SAMPLE_BITS==8) +typedef unsigned char FMSAMPLE; +#endif +*/ + +typedef void (*FM_TIMERHANDLER)(void *param,int c,int cnt,int clock); +typedef void (*FM_IRQHANDLER)(void *param,int irq); +/* FM_TIMERHANDLER : Stop or Start timer */ +/* int n = chip number */ +/* int c = Channel 0=TimerA,1=TimerB */ +/* int count = timer count (0=stop) */ +/* doube stepTime = step time of one count (sec.)*/ + +/* FM_IRQHHANDLER : IRQ level changing sense */ +/* int n = chip number */ +/* int irq = IRQ level 0=OFF,1=ON */ + +/** + * @brief Initialize chip and return the instance + * @param param Unused, keep NULL + * @param baseclock YM2612 clock + * @param rate Output sample rate + * @param TimerHandler Keep NULL + * @param IRQHandler Keep NULL + * @return Chip instance or NULL on any error + */ +static void * ym2612_init(void *param, int baseclock, int rate, + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler); +/** + * @brief Free chip instance + * @param chip Chip instance + */ +static void ym2612_shutdown(void *chip); +/** + * @brief Reset state of the chip + * @param chip Chip instance + */ +static void ym2612_reset_chip(void *chip); +/** + * @brief Generate stereo output of specified length + * @param chip Chip instance + * @param buffer Output sound buffer + * @param frames Output buffer size in frames (one frame - two array entries of the buffer) + * @param mix 0 - override buffer data, 1 - mix output data with a content of the buffer + */ +static void ym2612_generate(void *chip, FMSAMPLE *buffer, int frames, int mix); +#define ym2612_update_one(chip, buffer, length) ym2612_generate(chip, buffer, length, 0) + +/** + * @brief Single-Sample generation prepare + * @param chip Chip instance + */ +static void ym2612_pre_generate(void *chip); +/** + * @brief Generate single stereo PCM frame. Will be used native sample rate of 53267 Hz + * @param chip Chip instance + * @param buffer One stereo PCM frame + */ +static void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[2]); + +/* void ym2612_post_generate(void *chip, int length); */ + +static int ym2612_write(void *chip, int a,unsigned char v); +#if 0 +static unsigned char ym2612_read(void *chip,int a); +static int ym2612_timer_over(void *chip, int c ); +#endif + +#ifdef __STATE_H__ +static void ym2612_postload(void *chip); +#endif + +static void ym2612_set_mutemask(void *chip, UINT32 MuteMask); +#if 0 +static void ym2612_setoptions(UINT8 Flags); +#endif + + +static stream_sample_t *DUMMYBUF = NULL; + +/* shared function building option */ +#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B||BUILD_YM2612||BUILD_YM3438) +#define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608) + +#define RSM_ENABLE 0 +#define RSM_FRAC 10 + +/* globals */ +#define TYPE_SSG 0x01 /* SSG support */ +#define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */ +#define TYPE_6CH 0x04 /* FM 6CH / 3CH */ +#define TYPE_DAC 0x08 /* YM2612's DAC device */ +#define TYPE_ADPCM 0x10 /* two ADPCM units */ +#define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */ + + +#define TYPE_YM2203 (TYPE_SSG) +#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM) +#define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610) +#define TYPE_YM2612 (TYPE_DAC |TYPE_LFOPAN |TYPE_6CH) + + +/* globals */ +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<>3) + +/* sin waveform table in 'decibel' scale */ +static unsigned int sin_tab[SIN_LEN]; + +/* sustain level table (3dB per step) */ +/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ +/* 1, 2, 4, 8, 16, 32, 64 (value)*/ +/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ + +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +/* attenuation value (10 bits) = (SL << 2) << 3 */ +#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) ) +static const UINT32 sl_table[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const UINT8 eg_inc[19*RATE_STEPS]={ + +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ +/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ +/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ +/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ + +/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ +/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ +/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(17) in this table - it's directly in the code */ +static const UINT8 eg_rate_select2612[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates (same as Rate 0) */ +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), + +/* rates 00-11 */ +/* +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +*/ +O(18),O(18),O( 2),O( 3), /* from Nemesis's tests on real YM2612 hardware */ +O( 0),O( 1),O( 2),O( 2), /* Nemesis's tests */ + +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 12 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 13 */ +O( 8),O( 9),O(10),O(11), + +/* rate 14 */ +O(12),O(13),O(14),O(15), + +/* rate 15 */ +O(16),O(16),O(16),O(16), + +/* 32 dummy rates (same as 15 3) */ +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ +/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ +/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates */ +/* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */ + +/* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */ +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), + +/* rates 00-11 */ +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 12 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 32 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) + +}; +#undef O + +static const UINT8 dt_tab[4 * 32]={ +/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ +/* FD=0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* FD=1 */ + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, +/* FD=2 */ + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, +/* FD=3 */ + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 +}; + + +/* OPN key frequency number -> key code follow table */ +/* fnum higher 4bit -> keycode lower 2bit */ +static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; + + +/* 8 LFO speed parameters */ +/* each value represents number of samples that one LFO level will last for */ +static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; + + + +/*There are 4 different LFO AM depths available, they are: + 0 dB, 1.4 dB, 5.9 dB, 11.8 dB + Here is how it is generated (in EG steps): + + 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 + 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 + 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 + + (1.4 dB is losing precision as you can see) + + It's implemented as generator from 0..126 with step 2 then a shift + right N times, where N is: + 8 for 0 dB + 3 for 1.4 dB + 1 for 5.9 dB + 0 for 11.8 dB +*/ +static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0}; + + + +/*There are 8 different LFO PM depths available, they are: + 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) + + Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 + (bits 8,9,10 = FNUM MSB from OCT/FNUM register) + + Here we store only first quarter (positive one) of full waveform. + Full table (lfo_pm_table) containing all 128 waveforms is build + at run (init) time. + + One value in table below represents 4 (four) basic LFO steps + (1 PM step = 4 AM steps). + + For example: + at LFO SPEED=0 (which is 108 samples per basic LFO step) + one value from "lfo_pm_output" table lasts for 432 consecutive + samples (4*108=432) and one full LFO waveform cycle lasts for 13824 + samples (32*432=13824; 32 because we store only a quarter of whole + waveform in the table below) +*/ +static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ +/* FNUM BIT 4: 000 0001xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1}, + +/* FNUM BIT 5: 000 0010xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3}, + +/* FNUM BIT 6: 000 0100xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1}, +/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6}, + +/* FNUM BIT 7: 000 1000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1}, +/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2}, +/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, + +/* FNUM BIT 8: 001 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2}, +/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3}, +/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4}, +/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, + +/* FNUM BIT 9: 010 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2}, +/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4}, +/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6}, +/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8}, +/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, + +/* FNUM BIT10: 100 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4}, +/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8}, +/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc}, +/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10}, +/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, +/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}, + +}; + +/* all 128 LFO PM waveforms */ +static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ + +/* register number to channel number , slot offset */ +#define OPN_CHAN(N) (N&3) +#define OPN_SLOT(N) ((N>>2)&3) + +/* slot number */ +#define SLOT1 0 +#define SLOT2 2 +#define SLOT3 1 +#define SLOT4 3 + +/* bit0 = Right enable , bit1 = Left enable */ +#define OUTD_RIGHT 1 +#define OUTD_LEFT 2 +#define OUTD_CENTER 3 + + +/* save output as raw 16-bit sample */ +/* #define SAVE_SAMPLE */ + +#ifdef SAVE_SAMPLE +static FILE *sample[1]; + #if 1 /*save to MONO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = rt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + + +/* struct describing a single operator (SLOT) */ +typedef struct +{ + INT32 *DT; /* detune :dt_tab[DT] */ + UINT8 KSR; /* key scale rate :3-KSR */ + UINT32 ar; /* attack rate */ + UINT32 d1r; /* decay rate */ + UINT32 d2r; /* sustain rate */ + UINT32 rr; /* release rate */ + UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ + UINT32 mul; /* multiple :ML_TABLE[ML] */ + + /* Phase Generator */ + UINT32 phase; /* phase counter */ + INT32 Incr; /* phase step */ + + /* Envelope Generator */ + UINT8 state; /* phase type */ + UINT32 tl; /* total level: TL << 3 */ + INT32 volume; /* envelope counter */ + UINT32 sl; /* sustain level:sl_table[SL] */ + UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */ + + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT8 eg_sh_d1r; /* (decay state) */ + UINT8 eg_sel_d1r; /* (decay state) */ + UINT8 eg_sh_d2r; /* (sustain state) */ + UINT8 eg_sel_d2r; /* (sustain state) */ + UINT8 eg_sh_rr; /* (release state) */ + UINT8 eg_sel_rr; /* (release state) */ + + UINT8 ssg; /* SSG-EG waveform */ + UINT8 ssgn; /* SSG-EG negated output */ + + UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */ + + /* LFO */ + UINT32 AMmask; /* AM enable flag */ + +} FM_SLOT; + +typedef struct +{ + FM_SLOT SLOT[4]; /* four SLOTs (operators) */ + + UINT8 ALGO; /* algorithm */ + UINT8 FB; /* feedback shift */ + INT32 op1_out[2]; /* op1 output for feedback */ + + INT32 *connect1; /* SLOT1 output pointer */ + INT32 *connect3; /* SLOT3 output pointer */ + INT32 *connect2; /* SLOT2 output pointer */ + INT32 *connect4; /* SLOT4 output pointer */ + + INT32 *mem_connect;/* where to put the delayed sample (MEM) */ + INT32 mem_value; /* delayed sample (MEM) value */ + + INT32 pms; /* channel PMS */ + UINT8 ams; /* channel AMS */ + + UINT32 fc; /* fnum,blk:adjusted to sample rate */ + UINT8 kcode; /* key code: */ + UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + UINT8 Muted; +} FM_CH; + + +typedef struct +{ + /* running_device *device; */ + void * param; /* this chip parameter */ + double freqbase; /* frequency base */ + int timer_prescaler; /* timer prescaler */ + UINT8 irq; /* interrupt level */ + UINT8 irqmask; /* irq mask */ +#if FM_BUSY_FLAG_SUPPORT + TIME_TYPE busy_expiry_time; /* expiry time of the busy status */ +#endif + UINT32 clock; /* master clock (Hz) */ + UINT32 rate; /* internal sampling rate (Hz) */ +#if RSM_ENABLE + INT32 rateratio; /* resampling ratio */ + INT32 framecnt; /* resampling frames count*/ + FMSAMPLE cur_sample[2]; /* previous sample */ + FMSAMPLE prev_sample[2]; /* previous sample */ +#endif + UINT8 address; /* address register */ + UINT8 status; /* status flag */ + UINT32 mode; /* mode CSM / 3SLOT */ + UINT8 fn_h; /* freq latch */ + UINT8 prescaler_sel; /* prescaler selector */ + INT32 TA; /* timer a */ + INT32 TAC; /* timer a counter */ + UINT8 TB; /* timer b */ + INT32 TBC; /* timer b counter */ + /* local time tables */ + INT32 dt_tab[8][32]; /* DeTune table */ + /* Extention Timer and IRQ handler */ + FM_TIMERHANDLER timer_handler; + FM_IRQHANDLER IRQ_Handler; + const ssg_callbacks *SSG; +} FM_ST; + + + +/***********************************************************/ +/* OPN unit */ +/***********************************************************/ + +/* OPN 3slot struct */ +typedef struct +{ + UINT32 fc[3]; /* fnum3,blk3: calculated */ + UINT8 fn_h; /* freq3 latch */ + UINT8 kcode[3]; /* key code */ + UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + UINT8 key_csm; /* CSM mode Key-ON flag */ +} FM_3SLOT; + +/* OPN/A/B common state */ +typedef struct +{ + UINT8 type; /* chip type */ + FM_ST ST; /* general state */ + FM_3SLOT SL3; /* 3 slot mode state */ + FM_CH *P_CH; /* pointer of CH */ + unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */ + + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */ + UINT32 eg_timer_add; /* step of eg_timer */ + UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */ + + + /* there are 2048 FNUMs that can be generated using FNUM/BLK registers + but LFO works with one more bit of a precision so we really need 4096 elements */ + UINT32 fn_table[4096]; /* fnumber->increment counter */ + UINT32 fn_max; /* maximal phase increment (used for phase overflow) */ + + /* LFO */ + UINT8 lfo_cnt; /* current LFO phase (out of 128) */ + UINT32 lfo_timer; /* current LFO phase runs at LFO frequency */ + UINT32 lfo_timer_add; /* step of lfo_timer */ + UINT32 lfo_timer_overflow; /* LFO timer overflows every N samples (depends on LFO frequency) */ + UINT32 LFO_AM; /* current LFO AM step */ + UINT32 LFO_PM; /* current LFO PM step */ + + INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ + INT32 mem; /* one sample delay memory */ + INT32 out_fm[6]; /* outputs of working channels */ + +} FM_OPN; + +/* here's the virtual YM2612 */ +typedef struct +{ + UINT8 REGS[512]; /* registers */ + FM_OPN OPN; /* OPN state */ + FM_CH CH[6]; /* channel state */ + UINT8 addr_A1; /* address line A1 */ + + /* dac output (YM2612) */ + /* int dacen; */ + UINT8 dacen; + UINT8 dac_test; + INT32 dacout; + UINT8 MuteDAC; + + UINT8 WaveOutMode; + INT32 WaveL; + INT32 WaveR; +} YM2612; + +/* log output level */ +#define LOG_ERR 3 /* ERROR */ +#define LOG_WAR 2 /* WARNING */ +#define LOG_INF 1 /* INFORMATION */ +#define LOG_LEVEL LOG_INF + +#ifndef __RAINE__ +#define LOG(n,x) do { if( (n)>=LOG_LEVEL ) logerror x; } while (0) +#endif + +/* limitter */ +#define Limit(val, max,min) { \ + if ( val > max ) val = max; \ + else if ( val < min ) val = min; \ +} + +#if 0 +#define USE_VGM_INIT_SWITCH +static UINT8 IsVGMInit = 0; +#endif +static UINT8 PseudoSt = 0x00; +/*#include +static FILE* hFile; +static UINT32 FileSample;*/ + +/* status set and IRQ handling */ +INLINE void FM_STATUS_SET(FM_ST *ST,int flag) +{ + /* set status flag */ + ST->status |= flag; + if ( !(ST->irq) && (ST->status & ST->irqmask) ) + { + ST->irq = 1; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,1); + } +} + +/* status reset and IRQ handling */ +INLINE void FM_STATUS_RESET(FM_ST *ST,int flag) +{ + /* reset status flag */ + ST->status &=~flag; + if ( (ST->irq) && !(ST->status & ST->irqmask) ) + { + ST->irq = 0; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,0); + } +} + +/* IRQ mask set */ +INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag) +{ + ST->irqmask = flag; + /* IRQ handling check */ + FM_STATUS_SET(ST,0); + FM_STATUS_RESET(ST,0); +} + +INLINE void FM_KEYON(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + /* Note by Valley Bell: + I assume that the CSM mode shouldn't affect channels + other than FM3, so I added a check for it here.*/ + if( !SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + SLOT->key = 1; +} + +INLINE void FM_KEYOFF(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if (SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) + { +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) /* workaround for VGMs trimmed with VGMTool */ + { + SLOT->state = EG_OFF; + SLOT->volume = MAX_ATT_INDEX; + SLOT->vol_out= MAX_ATT_INDEX; + } + else +#endif + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } + + SLOT->key = 0; +} + +INLINE void FM_KEYON_CSM(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if( !SLOT->key && !OPN->SL3.key_csm) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } +} + +INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + if (!SLOT->key) + { +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + { + SLOT->state = EG_OFF; + SLOT->volume = MAX_ATT_INDEX; + SLOT->vol_out= MAX_ATT_INDEX; + } + else +#endif + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } +} + +/* OPN Mode Register Write */ +INLINE void set_timers( FM_OPN *OPN, FM_ST *ST, void *n, int v ) +{ + /* b7 = CSM MODE */ + /* b6 = 3 slot mode */ + /* b5 = reset b */ + /* b4 = reset a */ + /* b3 = timer enable b */ + /* b2 = timer enable a */ + /* b1 = load b */ + /* b0 = load a */ + + if ((OPN->ST.mode ^ v) & 0xC0) + { + /* phase increment need to be recalculated */ + OPN->P_CH[2].SLOT[SLOT1].Incr=-1; + + /* CSM mode disabled and CSM key ON active*/ + if (((v & 0xC0) != 0x80) && OPN->SL3.key_csm) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT1); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT2); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT3); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT4); + OPN->SL3.key_csm = 0; + } + } + + /* reset Timer b flag */ + if( v & 0x20 ) + FM_STATUS_RESET(ST,0x02); + /* reset Timer a flag */ + if( v & 0x10 ) + FM_STATUS_RESET(ST,0x01); + /* load b */ + if ((v&2) && !(ST->mode&2)) + { + ST->TBC = ( 256-ST->TB)<<4; + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,(int)ST->clock); + } + /* load a */ + if ((v&1) && !(ST->mode&1)) + { + ST->TAC = (1024-ST->TA); + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,(int)ST->clock); + ST->TAC *= 4096; + } + + ST->mode = (UINT32)v; +} + + +/* Timer A Overflow */ +INLINE void TimerAOver(FM_ST *ST) +{ + /* set status (if enabled) */ + if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01); + /* clear or reload the counter */ + ST->TAC = (1024-ST->TA); + if (ST->timer_handler) (ST->timer_handler)(ST->param,0,ST->TAC * ST->timer_prescaler,(int)ST->clock); + ST->TAC *= 4096; +} +/* Timer B Overflow */ +INLINE void TimerBOver(FM_ST *ST) +{ + /* set status (if enabled) */ + if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02); + /* clear or reload the counter */ + ST->TBC = ( 256-ST->TB)<<4; + if (ST->timer_handler) (ST->timer_handler)(ST->param,1,ST->TBC * ST->timer_prescaler,(int)ST->clock); +} + + +#if FM_INTERNAL_TIMER +/* ----- internal timer mode , update timer */ +/* Valley Bell: defines fixed */ + +/* ---------- calculate timer A ---------- */ + #define INTERNAL_TIMER_A(ST,CSM_CH) \ + { \ + if( (ST)->TAC && ((ST)->timer_handler==0) ) \ + if( ((ST)->TAC -= (int)((ST)->freqbase*4096)) <= 0 ) \ + { \ + TimerAOver( ST ); \ + /* CSM mode total level latch and auto key on */ \ + if( (ST)->mode & 0x80 ) \ + CSMKeyControll( OPN, CSM_CH ); \ + } \ + } +/* ---------- calculate timer B ---------- */ + #define INTERNAL_TIMER_B(ST,step) \ + { \ + if( (ST)->TBC && ((ST)->timer_handler==0) ) \ + if( ((ST)->TBC -= (int)((ST)->freqbase*4096*step)) <= 0 ) \ + TimerBOver( ST ); \ + } +#else /* FM_INTERNAL_TIMER */ +/* external timer mode */ +#define INTERNAL_TIMER_A(ST,CSM_CH) +#define INTERNAL_TIMER_B(ST,step) +#endif /* FM_INTERNAL_TIMER */ + + + +#if FM_BUSY_FLAG_SUPPORT +#define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME) +INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST) +{ + if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 ) + { + if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(ST->device->machine)) > 0) + return ST->status | 0x80; /* with busy */ + /* expire */ + FM_BUSY_CLEAR(ST); + } + return ST->status; +} +INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock ) +{ + TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(ATTOTIME_IN_HZ(ST->clock), busyclock * ST->timer_prescaler); + ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(ST->device->machine), expiry_period); +} +#else +#define FM_STATUS_FLAG(ST) ((ST)->status) +#define FM_BUSY_SET(ST,bclock) {} +#define FM_BUSY_CLEAR(ST) {} +#endif + + +/* set algorithm connection */ +INLINE void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch ) +{ + INT32 *carrier = &OPN->out_fm[ch]; + + INT32 **om1 = &CH->connect1; + INT32 **om2 = &CH->connect3; + INT32 **oc1 = &CH->connect2; + + INT32 **memc = &CH->mem_connect; + + switch( CH->ALGO ) + { + case 0: + /* M1---C1---MEM---M2---C2---OUT */ + *om1 = &OPN->c1; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 1: + /* M1------+-MEM---M2---C2---OUT */ + /* C1-+ */ + *om1 = &OPN->mem; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 2: + /* M1-----------------+-C2---OUT */ + /* C1---MEM---M2-+ */ + *om1 = &OPN->c2; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 3: + /* M1---C1---MEM------+-C2---OUT */ + /* M2-+ */ + *om1 = &OPN->c1; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->c2; + break; + case 4: + /* M1---C1-+-OUT */ + /* M2---C2-+ */ + /* MEM: not used */ + *om1 = &OPN->c1; + *oc1 = carrier; + *om2 = &OPN->c2; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + case 5: + /* +----C1----+ */ + /* M1-+-MEM---M2-+-OUT */ + /* +----C2----+ */ + *om1 = 0; /* special mark */ + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->m2; + break; + case 6: + /* M1---C1-+ */ + /* M2-+-OUT */ + /* C2-+ */ + /* MEM: not used */ + *om1 = &OPN->c1; + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + case 7: + /* M1-+ */ + /* C1-+-OUT */ + /* M2-+ */ + /* C2-+ */ + /* MEM: not used*/ + *om1 = carrier; + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + } + + CH->connect4 = carrier; +} + +/* set detune & multiple */ +INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v) +{ + SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1; + SLOT->DT = ST->dt_tab[(v>>4)&7]; + CH->SLOT[SLOT1].Incr=-1; +} + +/* set total level */ +INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v) +{ + SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ + (void)CH; + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; +} + +/* set attack rate & key scale */ +INLINE void set_ar_ksr(UINT8 type, FM_CH *CH,FM_SLOT *SLOT,int v) +{ + UINT8 old_KSR = SLOT->KSR; + (void)type; + + SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->KSR = 3-(v>>6); + if (SLOT->KSR != old_KSR) + { + CH->SLOT[SLOT1].Incr=-1; + } + + /* Even if it seems unnecessary, in some odd case, KSR and KC are both modified */ + /* and could result in SLOT->kc remaining unchanged. */ + /* In such case, AR values would not be recalculated despite SLOT->ar has changed */ + /* This fixes the introduction music of Batman & Robin (Eke-Eke) */ + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware */ + } +} + +/* set decay rate */ +INLINE void set_dr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr]; +} + +/* set sustain rate */ +INLINE void set_sr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr]; +} + +/* set release rate */ +INLINE void set_sl_rr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->sl = sl_table[ v>>4 ]; + + /* check EG state changes */ + if ((SLOT->state == EG_DEC) && (SLOT->volume >= (INT32)(SLOT->sl))) + SLOT->state = EG_SUS; + + SLOT->rr = 34 + ((v&0x0f)<<2); + + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr]; +} + +/* advance LFO to next sample */ +INLINE void advance_lfo(FM_OPN *OPN) +{ + if (OPN->lfo_timer_overflow) /* LFO enabled ? */ + { + /* increment LFO timer */ + OPN->lfo_timer += OPN->lfo_timer_add; + + /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */ + while (OPN->lfo_timer >= OPN->lfo_timer_overflow) + { + OPN->lfo_timer -= OPN->lfo_timer_overflow; + + /* There are 128 LFO steps */ + OPN->lfo_cnt = ( OPN->lfo_cnt + 1 ) & 127; + + /* Valley Bell: Replaced old code (non-inverted triangle) with + the one from Genesis Plus GX 1.71. */ + /* triangle (inverted) */ + /* AM: from 126 to 0 step -2, 0 to 126 step +2 */ + if (OPN->lfo_cnt<64) + OPN->LFO_AM = (UINT32)(OPN->lfo_cnt ^ 63) << 1; + else + OPN->LFO_AM = (UINT32)(OPN->lfo_cnt & 63) << 1; + + /* PM works with 4 times slower clock */ + OPN->LFO_PM = OPN->lfo_cnt >> 2; + } + } +} + +INLINE void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT) +{ + /* unsigned int out; */ + unsigned int i = 4; /* four operators per channel */ + + do + { + switch(SLOT->state) + { + case EG_ATT: /* attack phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_ar)-1))) + { + /* update attenuation level */ + SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4; + + /* check phase transition*/ + if (SLOT->volume <= MIN_ATT_INDEX) + { + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */ + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + break; + + case EG_DEC: /* decay phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_d1r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* check phase transition*/ + if (SLOT->volume >= (INT32)(SLOT->sl)) + SLOT->state = EG_SUS; + } + break; + + case EG_SUS: /* sustain phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_d2r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* check phase transition*/ + if ( SLOT->volume >= MAX_ATT_INDEX ) + SLOT->volume = MAX_ATT_INDEX; + /* do not change SLOT->state (verified on real chip) */ + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + break; + + case EG_REL: /* release phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_rr)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; + /* check phase transition */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; + + /* check phase transition*/ + if (SLOT->volume >= MAX_ATT_INDEX) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + + } + break; + } + + /* Valley Bell: These few lines are missing in Genesis Plus GX' ym2612 core file. + Disabling them fixes the SSG-EG. + Additional Note: Asterix and the Great Rescue: Level 1 sounds "better" with these lines, + but less accurate. */ + #if 0 + out = ((UINT32)SLOT->volume); + + /* negate output (changes come from alternate bit, init comes from attack bit) */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL)) + out ^= MAX_ATT_INDEX; + + /* we need to store the result here because we are going to change ssgn + in next instruction */ + SLOT->vol_out = out + SLOT->tl; + #endif + + SLOT++; + i--; + } while (i); + +} + +/* SSG-EG update process */ +/* The behavior is based upon Nemesis tests on real hardware */ +/* This is actually executed before each samples */ +INLINE void update_ssg_eg_channel(FM_SLOT *SLOT) +{ + unsigned int i = 4; /* four operators per channel */ + + do + { + /* detect SSG-EG transition */ + /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */ + /* if an Attack Phase is programmed, inversion can occur on each sample */ + if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL)) + { + if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */ + { + /* set inversion flag */ + if (SLOT->ssg & 0x02) + SLOT->ssgn = 4; + + /* force attenuation level during decay phases */ + if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04))) + SLOT->volume = MAX_ATT_INDEX; + } + else /* loop SSG-EG */ + { + /* toggle output inversion flag or reset Phase Generator */ + if (SLOT->ssg & 0x02) + SLOT->ssgn ^= 4; + else + SLOT->phase = 0; + + /* same as Key ON */ + if (SLOT->state != EG_ATT) + { + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* Attack Rate is maximal: directly switch to Decay or Substain */ + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + } + } + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* next slot */ + SLOT++; + i--; + } while (i); +} + + +INLINE void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum) +{ + UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; + INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ]; + + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + UINT8 blk = (block_fnum&0x7000) >> 12; + UINT32 fn = block_fnum & 0xfff; + + /* recalculate keyscale code */ + /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/ + /* This really stupid bug caused a read outside of the + array [size 0x10] and returned invalid values. + This caused an annoying vibrato for some notes. + (Note: seems to be a copy-and-paste from OPNWriteReg -> case 0xA0) + Why are MAME cores always SOO buggy ?! */ + /* Oh, and before I forget: it's correct in fm.c */ + int kc = (blk<<2) | opn_fktable[fn >> 8]; + /* Thanks to Blargg - his patch that helped me to find this bug */ + + /* recalculate (frequency) phase increment counter */ + int fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc]; + + /* (frequency) phase overflow (credits to Nemesis) */ + if (fc < 0) fc += OPN->fn_max; + + /* update phase */ + SLOT->phase += (fc * SLOT->mul) >> 1; + } + else /* LFO phase modulation = zero */ + { + SLOT->phase += SLOT->Incr; + } +} + +INLINE void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH) +{ + UINT32 block_fnum = CH->block_fnum; + + UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; + INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ]; + + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + UINT8 blk = (block_fnum&0x7000) >> 12; + UINT32 fn = block_fnum & 0xfff; + + /* recalculate keyscale code */ + /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/ + /* the same stupid bug as above */ + int kc = (blk<<2) | opn_fktable[fn >> 8]; + + /* recalculate (frequency) phase increment counter */ + int fc = (OPN->fn_table[fn]>>(7-blk)); + + /* (frequency) phase overflow (credits to Nemesis) */ + int finc = fc + CH->SLOT[SLOT1].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1; + + finc = fc + CH->SLOT[SLOT2].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1; + + finc = fc + CH->SLOT[SLOT3].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1; + + finc = fc + CH->SLOT[SLOT4].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1; + } + else /* LFO phase modulation = zero */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +/* update phase increment and envelope generator */ +INLINE void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc ) +{ + int ksr = kc >> SLOT->KSR; + + fc += SLOT->DT[kc]; + + /* detects frequency overflow (credits to Nemesis) */ + if (fc < 0) fc += OPN->fn_max; + + /* (frequency) phase increment counter */ + SLOT->Incr = (fc * SLOT->mul) >> 1; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 32+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware (Attack phase is blocked) */ + } + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + + SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr]; + } +} + +/* update phase increment counters */ +INLINE void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH ) +{ + if( CH->SLOT[SLOT1].Incr==-1) + { + int fc = CH->fc; + int kc = CH->kcode; + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc ); + } +} + +#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) + +INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm) +{ + UINT32 p; + + p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm) +{ + UINT32 p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ]; + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE void chan_calc(YM2612 *F2612, FM_OPN *OPN, FM_CH *CH) +{ + UINT32 AM = OPN->LFO_AM >> CH->ams; + unsigned int eg_out; + + if (CH->Muted) + return; + + OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0; + + *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ + + eg_out = volume_calc(&CH->SLOT[SLOT1]); + { + INT32 out = CH->op1_out[0] + CH->op1_out[1]; + CH->op1_out[0] = CH->op1_out[1]; + + if( !CH->connect1 ) + { + /* algorithm 5 */ + OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0]; + } + else + { + /* other algorithms */ + *CH->connect1 += CH->op1_out[0]; + } + + + CH->op1_out[1] = 0; + if( eg_out < ENV_QUIET ) /* SLOT 1 */ + { + if (!CH->FB) + out=0; + + CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<FB) ); + } + } + + eg_out = volume_calc(&CH->SLOT[SLOT3]); + if( eg_out < ENV_QUIET ) /* SLOT 3 */ + *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2); + + eg_out = volume_calc(&CH->SLOT[SLOT2]); + if( eg_out < ENV_QUIET ) /* SLOT 2 */ + *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1); + + eg_out = volume_calc(&CH->SLOT[SLOT4]); + if( eg_out < ENV_QUIET ) /* SLOT 4 */ + *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2); + + + /* store current MEM */ + CH->mem_value = OPN->mem; + + /* update phase counters AFTER output calculations */ + if(CH->pms) + { + /* add support for 3 slot mode */ + if ((OPN->ST.mode & 0xC0) && (CH == &F2612->CH[2])) + { + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum); + } + else update_phase_lfo_channel(OPN, CH); + } + else /* no LFO phase modulation */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +static void FMCloseTable( void ) +{ +#ifdef SAVE_SAMPLE + fclose(sample[0]); +#endif + return; +} + + +/* CSM Key Controll */ +INLINE void CSMKeyControll(FM_OPN *OPN, FM_CH *CH) +{ + /* all key ON (verified by Nemesis on real hardware) */ + FM_KEYON_CSM(OPN,CH,SLOT1); + FM_KEYON_CSM(OPN,CH,SLOT2); + FM_KEYON_CSM(OPN,CH,SLOT3); + FM_KEYON_CSM(OPN,CH,SLOT4); + OPN->SL3.key_csm = 1; +} + +#ifdef __STATE_H__ +/* FM channel save , internal state only */ +static void FMsave_state_channel(running_device *device,FM_CH *CH,int num_ch) +{ + int slot , ch; + + for(ch=0;chop1_out); + state_save_register_device_item(device, ch, CH->fc); + /* slots */ + for(slot=0;slot<4;slot++) + { + FM_SLOT *SLOT = &CH->SLOT[slot]; + state_save_register_device_item(device, ch * 4 + slot, SLOT->phase); + state_save_register_device_item(device, ch * 4 + slot, SLOT->state); + state_save_register_device_item(device, ch * 4 + slot, SLOT->volume); + } + } +} + +static void FMsave_state_st(running_device *device,FM_ST *ST) +{ +#if FM_BUSY_FLAG_SUPPORT + state_save_register_device_item(device, 0, ST->busy_expiry_time.seconds ); + state_save_register_device_item(device, 0, ST->busy_expiry_time.attoseconds ); +#endif + state_save_register_device_item(device, 0, ST->address ); + state_save_register_device_item(device, 0, ST->irq ); + state_save_register_device_item(device, 0, ST->irqmask ); + state_save_register_device_item(device, 0, ST->status ); + state_save_register_device_item(device, 0, ST->mode ); + state_save_register_device_item(device, 0, ST->prescaler_sel ); + state_save_register_device_item(device, 0, ST->fn_h ); + state_save_register_device_item(device, 0, ST->TA ); + state_save_register_device_item(device, 0, ST->TAC ); + state_save_register_device_item(device, 0, ST->TB ); + state_save_register_device_item(device, 0, ST->TBC ); +} +#endif /* _STATE_H */ + +#if BUILD_OPN +/* write a OPN mode register 0x20-0x2f */ +static void OPNWriteMode(FM_OPN *OPN, int r, int v) +{ + UINT8 c; + FM_CH *CH; + + switch(r) + { + case 0x21: /* Test */ + break; + case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */ + if (v&8) /* LFO enabled ? */ + { + #if 0 + if (!OPN->lfo_timer_overflow) + { + /* restart LFO */ + OPN->lfo_cnt = 0; + OPN->lfo_timer = 0; + OPN->LFO_AM = 0; + OPN->LFO_PM = 0; + } + #endif + + OPN->lfo_timer_overflow = lfo_samples_per_step[v&7] << LFO_SH; + } + else + { + /* Valley Bell: Ported from Genesis Plus GX 1.71 + hold LFO waveform in reset state */ + OPN->lfo_timer_overflow = 0; + OPN->lfo_timer = 0; + OPN->lfo_cnt = 0; + + + OPN->LFO_PM = 0; + OPN->LFO_AM = 126; + /* OPN->lfo_timer_overflow = 0; */ + } + break; + case 0x24: /* timer A High 8*/ + OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2); + break; + case 0x25: /* timer A Low 2*/ + OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3); + break; + case 0x26: /* timer B */ + OPN->ST.TB = (UINT8)v; + break; + case 0x27: /* mode, timer control */ + set_timers( OPN, &(OPN->ST),OPN->ST.param,v ); + break; + case 0x28: /* key on / off */ + c = v & 0x03; + if( c == 3 ) break; + if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3; + CH = OPN->P_CH; + CH = &CH[c]; + if(v&0x10) FM_KEYON(OPN,CH,SLOT1); else FM_KEYOFF(OPN,CH,SLOT1); + if(v&0x20) FM_KEYON(OPN,CH,SLOT2); else FM_KEYOFF(OPN,CH,SLOT2); + if(v&0x40) FM_KEYON(OPN,CH,SLOT3); else FM_KEYOFF(OPN,CH,SLOT3); + if(v&0x80) FM_KEYON(OPN,CH,SLOT4); else FM_KEYOFF(OPN,CH,SLOT4); + break; + } +} + +/* write a OPN register (0x30-0xff) */ +static void OPNWriteReg(FM_OPN *OPN, int r, int v) +{ + FM_CH *CH; + FM_SLOT *SLOT; + + UINT8 c = OPN_CHAN(r); + + if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */ + + if (r >= 0x100) c+=3; + + CH = OPN->P_CH; + CH = &CH[c]; + + SLOT = &(CH->SLOT[OPN_SLOT(r)]); + + switch( r & 0xf0 ) { + case 0x30: /* DET , MUL */ + set_det_mul(&OPN->ST,CH,SLOT,v); + break; + + case 0x40: /* TL */ + set_tl(CH,SLOT,v); + break; + + case 0x50: /* KS, AR */ + set_ar_ksr(OPN->type,CH,SLOT,v); + break; + + case 0x60: /* bit7 = AM ENABLE, DR */ + set_dr(OPN->type, SLOT,v); + + if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */ + { + SLOT->AMmask = (v&0x80) ? ~0 : 0; + } + break; + + case 0x70: /* SR */ + set_sr(OPN->type,SLOT,v); + break; + + case 0x80: /* SL, RR */ + set_sl_rr(OPN->type,SLOT,v); + break; + + case 0x90: /* SSG-EG */ + SLOT->ssg = v&0x0f; + + /* recalculate EG output */ + if (SLOT->state > EG_REL) + { + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* SSG-EG envelope shapes : + + E AtAlH + 1 0 0 0 \\\\ + + 1 0 0 1 \___ + + 1 0 1 0 \/\/ + ___ + 1 0 1 1 \ + + 1 1 0 0 //// + ___ + 1 1 0 1 / + + 1 1 1 0 /\/\ + + 1 1 1 1 /___ + + + E = SSG-EG enable + + + The shapes are generated using Attack, Decay and Sustain phases. + + Each single character in the diagrams above represents this whole + sequence: + + - when KEY-ON = 1, normal Attack phase is generated (*without* any + difference when compared to normal mode), + + - later, when envelope level reaches minimum level (max volume), + the EG switches to Decay phase (which works with bigger steps + when compared to normal mode - see below), + + - later when envelope level passes the SL level, + the EG swithes to Sustain phase (which works with bigger steps + when compared to normal mode - see below), + + - finally when envelope level reaches maximum level (min volume), + the EG switches to Attack phase again (depends on actual waveform). + + Important is that when switch to Attack phase occurs, the phase counter + of that operator will be zeroed-out (as in normal KEY-ON) but not always. + (I havent found the rule for that - perhaps only when the output level is low) + + The difference (when compared to normal Envelope Generator mode) is + that the resolution in Decay and Sustain phases is 4 times lower; + this results in only 256 steps instead of normal 1024. + In other words: + when SSG-EG is disabled, the step inside of the EG is one, + when SSG-EG is enabled, the step is four (in Decay and Sustain phases). + + Times between the level changes are the same in both modes. + + + Important: + Decay 1 Level (so called SL) is compared to actual SSG-EG output, so + it is the same in both SSG and no-SSG modes, with this exception: + + when the SSG-EG is enabled and is generating raising levels + (when the EG output is inverted) the SL will be found at wrong level !!! + For example, when SL=02: + 0 -6 = -6dB in non-inverted EG output + 96-6 = -90dB in inverted EG output + Which means that EG compares its level to SL as usual, and that the + output is simply inverted afterall. + + + The Yamaha's manuals say that AR should be set to 0x1f (max speed). + That is not necessary, but then EG will be generating Attack phase. + + */ + + + break; + + case 0xa0: + switch( OPN_SLOT(r) ) + { + case 0: /* 0xa0-0xa2 : FNUM1 */ +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->ST.fn_h = CH->block_fnum >> 8; +#endif + { + UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v; + UINT8 blk = OPN->ST.fn_h>>3; + /* keyscale code */ + CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + CH->fc = OPN->fn_table[fn*2]>>(7-blk); + + /* store fnum in clear form for LFO PM calculations */ + CH->block_fnum = (blk<<11) | fn; + + CH->SLOT[SLOT1].Incr=-1; + } + break; + case 1: /* 0xa4-0xa6 : FNUM2,BLK */ + OPN->ST.fn_h = v&0x3f; +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) // workaround for stupid Kega Fusion init block + CH->block_fnum = (OPN->ST.fn_h << 8) | (CH->block_fnum & 0xFF); +#endif + break; + case 2: /* 0xa8-0xaa : 3CH FNUM1 */ +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->SL3.fn_h = OPN->SL3.block_fnum[c] >> 8; +#endif + if(r < 0x100) + { + UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v; + UINT8 blk = OPN->SL3.fn_h>>3; + /* keyscale code */ + OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk); + OPN->SL3.block_fnum[c] = (blk<<11) | fn; + (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1; + } + break; + case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ + if(r < 0x100) + { + OPN->SL3.fn_h = v&0x3f; +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->SL3.block_fnum[c] = (OPN->SL3.fn_h << 8) | (OPN->SL3.block_fnum[c] & 0xFF); +#endif + } + break; + } + break; + + case 0xb0: + switch( OPN_SLOT(r) ) + { + case 0: /* 0xb0-0xb2 : FB,ALGO */ + { + unsigned char feedback = ((v>>3)&7); + CH->ALGO = v&7; + CH->FB = feedback ? feedback + 6 : 0; + setup_connection( OPN, CH, c ); + } + break; + case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */ + if( OPN->type & TYPE_LFOPAN) + { + /* b0-2 PMS */ + CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ + + /* b4-5 AMS */ + CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03]; + + /* PAN : b7 = L, b6 = R */ + OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0; + OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0; + + } + break; + } + break; + } +} + +/* initialize time tables */ +static void init_timetables(FM_OPN *OPN, double freqbase) +{ + int i,d; + double rate; + + /* DeTune table */ + for (d = 0;d <= 3;d++) + { + for (i = 0;i <= 31;i++) + { + rate = ((double)dt_tab[d*32 + i]) * freqbase * (1<<(FREQ_SH-10)); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ + OPN->ST.dt_tab[d][i] = (INT32) rate; + OPN->ST.dt_tab[d+4][i] = -OPN->ST.dt_tab[d][i]; + } + } + + /* there are 2048 FNUMs that can be generated using FNUM/BLK registers + but LFO works with one more bit of a precision so we really need 4096 elements */ + /* calculate fnumber -> increment counter table */ + for(i = 0; i < 4096; i++) + { + /* freq table for octave 7 */ + /* OPN phase increment counter = 20bit */ + /* the correct formula is : F-Number = (144 * fnote * 2^20 / M) / 2^(B-1) */ + /* where sample clock is M/144 */ + /* this means the increment value for one clock sample is FNUM * 2^(B-1) = FNUM * 64 for octave 7 */ + /* we also need to handle the ratio between the chip frequency and the emulated frequency (can be 1.0) */ + OPN->fn_table[i] = (UINT32)( (double)i * 32 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ + } + + /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */ + OPN->fn_max = (UINT32)( (double)0x20000 * freqbase * (1<<(FREQ_SH-10)) ); +} + +/* prescaler set (and make time tables) */ +static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres) +{ + /* frequency base */ + OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0; + + /* EG is updated every 3 samples */ + OPN->eg_timer_add = (UINT32)((1<ST.freqbase); + OPN->eg_timer_overflow = ( 3 ) * (1<lfo_timer_add = (UINT32)((1<ST.freqbase); + + /* Timer base time */ + OPN->ST.timer_prescaler = timer_prescaler; + + /* SSG part prescaler set */ + if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.param, OPN->ST.clock * 2 / SSGpres ); + + /* make time tables */ + init_timetables(OPN, OPN->ST.freqbase); +} + +static void reset_channels( FM_ST *ST , FM_CH *CH , int num ) +{ + int c,s; + (void)ST; + + for( c = 0 ; c < num ; c++ ) + { + /* memset(&CH[c], 0x00, sizeof(FM_CH)); */ + CH[c].mem_value = 0; + CH[c].op1_out[0] = 0; + CH[c].op1_out[1] = 0; + CH[c].fc = 0; + for(s = 0 ; s < 4 ; s++ ) + { + /* memset(&CH[c].SLOT[s], 0x00, sizeof(FM_SLOT)); */ + CH[c].SLOT[s].Incr = -1; + CH[c].SLOT[s].key = 0; + CH[c].SLOT[s].phase = 0; + CH[c].SLOT[s].ssg = 0; + CH[c].SLOT[s].ssgn = 0; + CH[c].SLOT[s].state= EG_OFF; + CH[c].SLOT[s].volume = MAX_ATT_INDEX; + CH[c].SLOT[s].vol_out= MAX_ATT_INDEX; + } + } +} + +/* initialize generic tables */ +static void init_tables(void) +{ + signed int i,x; + signed int n; + double o,m; + + /* build Linear Power Table */ + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 2; /* 13 bits here (as in real chip) */ + + + /* 14 bits (with sign bit) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + /* one entry in the 'Power' table use the following format, xxxxxyyyyyyyys with: */ + /* s = sign bit */ + /* yyyyyyyy = 8-bits decimal part (0-TL_RES_LEN) */ + /* xxxxx = 5-bits integer 'shift' value (0-31) but, since Power table output is 13 bits, */ + /* any value above 13 (included) would be discarded. */ + for (i=1; i<13; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + } + + /* build Logarithmic Sinus table */ + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + /* 13-bits (8.5) value is formatted for above 'Power' table */ + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + } + + /* build LFO PM modulation table */ + for(i = 0; i < 8; i++) /* 8 PM depths */ + { + UINT8 fnum; + for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */ + { + UINT8 value; + UINT8 step; + UINT32 offset_depth = i; + UINT32 offset_fnum_bit; + UINT32 bit_tmp; + + for (step=0; step<8; step++) + { + value = 0; + for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */ + { + if (fnum & (1<CH; + FMSAMPLE *bufOut = buffer; + int i; +#if !RSM_ENABLE + FMSAMPLE bufTmp[2]; +#endif + + ym2612_pre_generate(chip); + + if (!frames) + { + update_ssg_eg_channel(&cch[0].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[1].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[2].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[3].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[4].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[5].SLOT[SLOT1]); + } + + /* buffering */ + for(i=0 ; i < frames ; i++) + { +#if RSM_ENABLE + while(F2612->OPN.ST.framecnt >= F2612->OPN.ST.rateratio)/* Copy-Pasta from Nuked */ + { + /* Copy-Pasta from Nuked */ + F2612->OPN.ST.prev_sample[0] = F2612->OPN.ST.cur_sample[0]; + F2612->OPN.ST.prev_sample[1] = F2612->OPN.ST.cur_sample[1]; + ym2612_generate_one_native(chip, F2612->OPN.ST.cur_sample); + F2612->OPN.ST.framecnt -= F2612->OPN.ST.rateratio; + /* Copy-Pasta from Nuked */ + } + if (mix) + { + *bufOut++ += (FMSAMPLE)((F2612->OPN.ST.prev_sample[0] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[0] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + *bufOut++ += (FMSAMPLE)((F2612->OPN.ST.prev_sample[1] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[1] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + } else { + *bufOut++ = (FMSAMPLE)((F2612->OPN.ST.prev_sample[0] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[0] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + *bufOut++ = (FMSAMPLE)((F2612->OPN.ST.prev_sample[1] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[1] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + } + F2612->OPN.ST.framecnt += 1 << RSM_FRAC; +#else + if (mix) + { + ym2612_generate_one_native(chip, bufTmp); + bufOut[0] += bufTmp[0]; + bufOut[1] += bufTmp[1]; + } + else + { + ym2612_generate_one_native(chip, bufOut); + } + bufOut += 2; +#endif + } + /* ym2612_post_generate(chip, frames); */ +} + +void ym2612_pre_generate(void *chip) +{ + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + FM_CH *cch = F2612->CH; + + /* refresh PG and EG */ + refresh_fc_eg_chan( OPN, &cch[0] ); + refresh_fc_eg_chan( OPN, &cch[1] ); + if( (OPN->ST.mode & 0xc0) ) + { + /* 3SLOT MODE */ + if( cch[2].SLOT[SLOT1].Incr==-1) + { + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT4] , cch[2].fc , cch[2].kcode ); + } + } else + refresh_fc_eg_chan( OPN, &cch[2] ); + refresh_fc_eg_chan( OPN, &cch[3] ); + refresh_fc_eg_chan( OPN, &cch[4] ); + refresh_fc_eg_chan( OPN, &cch[5] ); +} + +void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[]) +{ + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + INT32 *out_fm = OPN->out_fm; + FM_CH *cch = F2612->CH; + INT32 dacout; + int lt,rt; + + if (! F2612->MuteDAC) + dacout = F2612->dacout; + else + dacout = 0; + + /* clear outputs */ + out_fm[0] = 0; + out_fm[1] = 0; + out_fm[2] = 0; + out_fm[3] = 0; + out_fm[4] = 0; + out_fm[5] = 0; + + /* update SSG-EG output */ + update_ssg_eg_channel(&cch[0].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[1].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[2].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[3].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[4].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[5].SLOT[SLOT1]); + + /* calculate FM */ + if (! F2612->dac_test) + { + chan_calc(F2612, OPN, &cch[0]); + chan_calc(F2612, OPN, &cch[1]); + chan_calc(F2612, OPN, &cch[2]); + chan_calc(F2612, OPN, &cch[3]); + chan_calc(F2612, OPN, &cch[4]); + if( F2612->dacen ) + cch[5].connect4 += dacout; + else + chan_calc(F2612, OPN, &cch[5]); + } + else + { + out_fm[0] = out_fm[1] = dacout; + out_fm[2] = out_fm[3] = dacout; + out_fm[5] = dacout; + } + + /* advance LFO */ + advance_lfo(OPN); + + /* advance envelope generator */ + OPN->eg_timer += OPN->eg_timer_add; + while (OPN->eg_timer >= OPN->eg_timer_overflow) + { + /* reset EG timer */ + OPN->eg_timer -= OPN->eg_timer_overflow; + /* increment EG counter */ + OPN->eg_cnt++; + /* EG counter is 12-bit only and zero value is skipped (verified on real hardware) */ + if (OPN->eg_cnt == 4096) + OPN->eg_cnt = 1; + + /* advance envelope generator */ + advance_eg_channel(OPN, &cch[0].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[1].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[2].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[3].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[4].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[5].SLOT[SLOT1]); + } + + /*fprintf(hFile, "%u", FileSample, out_fm[0]); + for (lt = 0; lt < 6; lt ++) + fprintf(hFile, "\t%d", out_fm[lt]); + fprintf(hFile, "\n"); + FileSample ++;*/ + + if (out_fm[0] > 8192) out_fm[0] = 8192; + else if (out_fm[0] < -8192) out_fm[0] = -8192; + if (out_fm[1] > 8192) out_fm[1] = 8192; + else if (out_fm[1] < -8192) out_fm[1] = -8192; + if (out_fm[2] > 8192) out_fm[2] = 8192; + else if (out_fm[2] < -8192) out_fm[2] = -8192; + if (out_fm[3] > 8192) out_fm[3] = 8192; + else if (out_fm[3] < -8192) out_fm[3] = -8192; + if (out_fm[4] > 8192) out_fm[4] = 8192; + else if (out_fm[4] < -8192) out_fm[4] = -8192; + if (out_fm[5] > 8192) out_fm[5] = 8192; + else if (out_fm[5] < -8192) out_fm[5] = -8192; + + /* 6-channels mixing */ + lt = ((out_fm[0]>>0) & OPN->pan[0]); + rt = ((out_fm[0]>>0) & OPN->pan[1]); + lt += ((out_fm[1]>>0) & OPN->pan[2]); + rt += ((out_fm[1]>>0) & OPN->pan[3]); + lt += ((out_fm[2]>>0) & OPN->pan[4]); + rt += ((out_fm[2]>>0) & OPN->pan[5]); + lt += ((out_fm[3]>>0) & OPN->pan[6]); + rt += ((out_fm[3]>>0) & OPN->pan[7]); + if (! F2612->dac_test) + { + lt += ((out_fm[4]>>0) & OPN->pan[8]); + rt += ((out_fm[4]>>0) & OPN->pan[9]); + } + else + { + lt += dacout; + lt += dacout; + } + lt += ((out_fm[5]>>0) & OPN->pan[10]); + rt += ((out_fm[5]>>0) & OPN->pan[11]); + + /* Limit( lt, MAXOUT, MINOUT ); */ + /* Limit( rt, MAXOUT, MINOUT ); */ + + #ifdef SAVE_SAMPLE + SAVE_ALL_CHANNELS + #endif + + /* buffering */ + if (F2612->WaveOutMode & 0x01) + F2612->WaveL = lt; + if (F2612->WaveOutMode & 0x02) + F2612->WaveR = rt; + if (F2612->WaveOutMode ^ 0x03) + F2612->WaveOutMode ^= 0x03; + + buffer[0] = (FMSAMPLE)(F2612->WaveL / 2); + buffer[1] = (FMSAMPLE)(F2612->WaveR / 2); + + /* CSM mode: if CSM Key ON has occured, CSM Key OFF need to be sent */ + /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */ + OPN->SL3.key_csm <<= 1; + + /* timer A control */ + /* INTERNAL_TIMER_A( &OPN->ST , cch[2] ) */ + { + if( OPN->ST.TAC && (OPN->ST.timer_handler==0) ) + if( (OPN->ST.TAC -= (int)(OPN->ST.freqbase*4096)) <= 0 ) + { + TimerAOver( &OPN->ST ); + /* CSM mode total level latch and auto key on */ + if( OPN->ST.mode & 0x80 ) + CSMKeyControll( OPN, &cch[2] ); + } + } + + /* CSM Mode Key ON still disabled */ + if (OPN->SL3.key_csm & 2) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&cch[2],SLOT1); + FM_KEYOFF_CSM(&cch[2],SLOT2); + FM_KEYOFF_CSM(&cch[2],SLOT3); + FM_KEYOFF_CSM(&cch[2],SLOT4); + OPN->SL3.key_csm = 0; + } +} + +#if 0 +void ym2612_post_generate(void *chip, int length) +{ + YM2612 *F2612 = (YM2612 *)chip; + /* timer B control */ + INTERNAL_TIMER_B(&F2612->OPN.ST, length); +} +#endif + +#ifdef __STATE_H__ +void ym2612_postload(void *chip) +{ + if (chip) + { + YM2612 *F2612 = (YM2612 *)chip; + int r; + + /* DAC data & port */ + F2612->dacout = ((int)F2612->REGS[0x2a] - 0x80) << 6; /* level unknown */ + F2612->dacen = F2612->REGS[0x2d] & 0x80; + /* OPN registers */ + /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ + for(r=0x30;r<0x9e;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); + OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); + } + /* FB / CONNECT , L / R / AMS / PMS */ + for(r=0xb0;r<0xb6;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); + OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); + } + /* channels */ + /*FM_channel_postload(F2612->CH,6);*/ + } +} + +static void YM2612_save_state(YM2612 *F2612, running_device *device) +{ + state_save_register_device_item_array(device, 0, F2612->REGS); + FMsave_state_st(device,&F2612->OPN.ST); + FMsave_state_channel(device,F2612->CH,6); + /* 3slots */ + state_save_register_device_item_array(device, 0, F2612->OPN.SL3.fc); + state_save_register_device_item(device, 0, F2612->OPN.SL3.fn_h); + state_save_register_device_item_array(device, 0, F2612->OPN.SL3.kcode); + /* address register1 */ + state_save_register_device_item(device, 0, F2612->addr_A1); +} +#endif /* _STATE_H */ + +/* initialize YM2612 emulator(s) */ +static void * ym2612_init(void *param, int clock, int rate, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler) +{ + YM2612 *F2612; + + if (clock <= 0 || rate <= 0) + return NULL; /* Forbid zero clock and sample rate */ + + /* allocate extend state space */ + /* F2612 = auto_alloc_clear(device->machine, YM2612); */ + F2612 = (YM2612 *)malloc(sizeof(YM2612)); + if (F2612 == NULL) + return NULL; + memset(F2612, 0x00, sizeof(YM2612)); + /* allocate total level table (128kb space) */ + init_tables(); + + F2612->OPN.ST.param = param; + F2612->OPN.type = TYPE_YM2612; + F2612->OPN.P_CH = F2612->CH; + /* F2612->OPN.ST.device = device; */ + F2612->OPN.ST.clock = clock; +#if RSM_ENABLE + F2612->OPN.ST.rate = 53267; + F2612->OPN.ST.rateratio = (INT32)(UINT32)((((UINT64)144 * rate) << RSM_FRAC) / clock); + F2612->OPN.ST.framecnt = 1 << RSM_FRAC; + memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2); + memset(&(F2612->OPN.ST.prev_sample), 0x00, sizeof(FMSAMPLE) * 2); +#else + F2612->OPN.ST.rate = rate; +#endif + /* F2612->OPN.ST.irq = 0; */ + /* F2612->OPN.ST.status = 0; */ + /* Extend handler */ + F2612->OPN.ST.timer_handler = timer_handler; + F2612->OPN.ST.IRQ_Handler = IRQHandler; + + if (PseudoSt) + F2612->WaveOutMode = 0x01; + else + F2612->WaveOutMode = 0x03; + /*hFile = fopen("YM2612.log", "wt"); + fprintf(hFile, "Clock: %d, Sample Rate: %d\n", clock, rate); + fprintf(hFile, "Sample\tCh 0\tCh 1\tCh 2\tCh 3\tCh 4\tCh 5\n"); + FileSample = 0;*/ + +#ifdef __STATE_H__ + YM2612_save_state(F2612, device); +#endif + return F2612; +} + +/* shut down emulator */ +static void ym2612_shutdown(void *chip) +{ + YM2612 *F2612 = (YM2612 *)chip; + /* fclose(hFile); */ + + FMCloseTable(); + /* auto_free(F2612->OPN.ST.device->machine, F2612); */ + free(F2612); +} + +/* reset one of chip */ +static void ym2612_reset_chip(void *chip) +{ + int i; + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + + OPNSetPres( OPN, 6*24, 6*24, 0); + /* status clear */ + FM_IRQMASK_SET(&OPN->ST,0x03); + FM_BUSY_CLEAR(&OPN->ST); + /* OPNWriteMode(OPN,0x27,0x30); */ /* mode 0 , timer reset */ + +#if RSM_ENABLE + /* Resampler's state */ + F2612->OPN.ST.framecnt = 1 << RSM_FRAC; + memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2); + memset(&(F2612->OPN.ST.prev_sample), 0x00, sizeof(FMSAMPLE) * 2); +#endif + + OPN->eg_timer = 0; + OPN->eg_cnt = 0; + + OPN->lfo_timer = 0; + OPN->lfo_cnt = 0; + OPN->LFO_AM = 126; + OPN->LFO_PM = 0; + + OPN->ST.TAC = 0; + OPN->ST.TBC = 0; + + OPN->SL3.key_csm = 0; + + OPN->ST.status = 0; + OPN->ST.mode = 0; + + memset(F2612->REGS, 0x00, sizeof(UINT8) * 512); + + OPNWriteMode(OPN,0x22,0x00); + + OPNWriteMode(OPN,0x27,0x30); + OPNWriteMode(OPN,0x26,0x00); + OPNWriteMode(OPN,0x25,0x00); + OPNWriteMode(OPN,0x24,0x00); + + reset_channels( &OPN->ST , &F2612->CH[0] , 6 ); + + for(i = 0xb6 ; i >= 0xb4 ; i-- ) + { + OPNWriteReg(OPN,i ,0xc0); + OPNWriteReg(OPN,i|0x100,0xc0); + } + for(i = 0xb2 ; i >= 0x30 ; i-- ) + { + OPNWriteReg(OPN,i ,0); + OPNWriteReg(OPN,i|0x100,0); + } + + /* DAC mode clear */ + F2612->dacen = 0; + F2612->dac_test = 0; + F2612->dacout = 0; + + if (F2612->WaveOutMode == 0x02) + F2612->WaveOutMode >>= 1; +} + +/* YM2612 write */ +/* n = number */ +/* a = address */ +/* v = value */ +static int ym2612_write(void *chip, int a, UINT8 v) +{ + YM2612 *F2612 = (YM2612 *)chip; + int addr; + + v &= 0xff; /* adjust to 8 bit bus */ + + switch( a&3) + { + case 0: /* address port 0 */ + F2612->OPN.ST.address = v; + F2612->addr_A1 = 0; + break; + + case 1: /* data port 0 */ + if (F2612->addr_A1 != 0) + break; /* verified on real YM2608 */ + + addr = F2612->OPN.ST.address; + F2612->REGS[addr] = v; + switch( addr & 0xf0 ) + { + case 0x20: /* 0x20-0x2f Mode */ + switch( addr ) + { + case 0x2a: /* DAC data (YM2612) */ + ym2612_update_one(chip, DUMMYBUF, 0); + F2612->dacout = ((int)v - 0x80) << 6; /* level unknown */ + break; + case 0x2b: /* DAC Sel (YM2612) */ + /* b7 = dac enable */ + F2612->dacen = v & 0x80; + break; + case 0x2C: /* undocumented: DAC Test Reg */ + /* b5 = volume enable */ + F2612->dac_test = v & 0x20; + break; + default: /* OPN section */ + /* ym2612_update_req(F2612->OPN.ST.param); */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* write register */ + OPNWriteMode(&(F2612->OPN),addr,v); + } + break; + default: /* 0x30-0xff OPN section */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* write register */ + OPNWriteReg(&(F2612->OPN),addr,v); + } + break; + + case 2: /* address port 1 */ + F2612->OPN.ST.address = v; + F2612->addr_A1 = 1; + break; + + case 3: /* data port 1 */ + if (F2612->addr_A1 != 1) + break; /* verified on real YM2608 */ + + addr = F2612->OPN.ST.address; + F2612->REGS[addr | 0x100] = v; + ym2612_update_one(chip, DUMMYBUF, 0); + OPNWriteReg(&(F2612->OPN),addr | 0x100,v); + break; + } + return F2612->OPN.ST.irq; +} + +#if 0 +static UINT8 ym2612_read(void *chip,int a) +{ + YM2612 *F2612 = (YM2612 *)chip; + + switch( a&3) + { + case 0: /* status 0 */ + return FM_STATUS_FLAG(&F2612->OPN.ST); + case 1: + case 2: + case 3: + /* LOG(LOG_WAR,("YM2612 #%p:A=%d read unmapped area\n",F2612->OPN.ST.param,a)); */ + return FM_STATUS_FLAG(&F2612->OPN.ST); + } + return 0; +} + +static int ym2612_timer_over(void *chip,int c) +{ + YM2612 *F2612 = (YM2612 *)chip; + + if( c ) + { /* Timer B */ + TimerBOver( &(F2612->OPN.ST) ); + } + else + { /* Timer A */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* timer update */ + TimerAOver( &(F2612->OPN.ST) ); + /* CSM mode key,TL controll */ + if ((F2612->OPN.ST.mode & 0xc0) == 0x80) + { /* CSM mode total level latch and auto key on */ + CSMKeyControll( &F2612->OPN, &(F2612->CH[2]) ); + } + } + return F2612->OPN.ST.irq; +} +#endif + +static void ym2612_set_mutemask(void *chip, UINT32 MuteMask) +{ + YM2612 *F2612 = (YM2612 *)chip; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 6; CurChn ++) + F2612->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + F2612->MuteDAC = (MuteMask >> 6) & 0x01; + + return; +} +#if 0 +static void ym2612_setoptions(UINT8 Flags) +{ + PseudoSt = (Flags >> 2) & 0x01; + + return; +} +#endif + +} // Ym2612_MameImpl + + +Ym2612_MAME_Emu::Ym2612_MAME_Emu() { impl = 0; } + +Ym2612_MAME_Emu::~Ym2612_MAME_Emu() +{ + if ( impl ) Ym2612_MameImpl::ym2612_shutdown( impl ); +} + +const char *Ym2612_MAME_Emu::set_rate(double sample_rate, double clock_rate) +{ + if ( impl ) Ym2612_MameImpl::ym2612_shutdown( impl ); + impl = Ym2612_MameImpl::ym2612_init( NULL, static_cast(clock_rate), static_cast(sample_rate), NULL, NULL ); + if ( !impl ) + return "Out of memory"; + return 0; +} + +void Ym2612_MAME_Emu::reset() +{ + if ( impl ) Ym2612_MameImpl::ym2612_reset_chip( impl ); +} + +void Ym2612_MAME_Emu::mute_voices(int mask) +{ + if ( impl ) Ym2612_MameImpl::ym2612_set_mutemask( impl, mask ); +} + +void Ym2612_MAME_Emu::write0(int addr, int data) +{ + if ( !impl ) return; + Ym2612_MameImpl::ym2612_write( impl, 0, static_cast(addr) ); + Ym2612_MameImpl::ym2612_write( impl, 1, static_cast(data) ); +} + +void Ym2612_MAME_Emu::write1(int addr, int data) +{ + if ( !impl ) return; + Ym2612_MameImpl::ym2612_write( impl, 0 + 2, static_cast(addr) ); + Ym2612_MameImpl::ym2612_write( impl, 1 + 2, static_cast(data) ); +} + +void Ym2612_MAME_Emu::run(int pair_count, Ym2612_MAME_Emu::sample_t *out) +{ + if ( impl ) Ym2612_MameImpl::ym2612_generate( impl, out, pair_count, 0); +} diff --git a/thirdparty/game-music-emu/gme/Ym2612_MAME.h b/thirdparty/game-music-emu/gme/Ym2612_MAME.h new file mode 100644 index 000000000..03831065a --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ym2612_MAME.h @@ -0,0 +1,38 @@ +// YM2612 FM sound chip emulator interface + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef YM2612_EMU_H +#define YM2612_EMU_H + +typedef void Ym2612_MAME_Impl; + +class Ym2612_MAME_Emu { + Ym2612_MAME_Impl* impl; +public: + Ym2612_MAME_Emu(); + ~Ym2612_MAME_Emu(); + + // Set output sample rate and chip clock rates, in Hz. Returns non-zero + // if error. + const char* set_rate( double sample_rate, double clock_rate ); + + // Reset to power-up state + void reset(); + + // Mute voice n if bit n (1 << n) of mask is set + enum { channel_count = 6 }; + void mute_voices( int mask ); + + // Write addr to register 0 then data to register 1 + void write0( int addr, int data ); + + // Write addr to register 2 then data to register 3 + void write1( int addr, int data ); + + // Run and add pair_count samples into current output buffer contents + typedef short sample_t; + enum { out_chan_count = 2 }; // stereo + void run( int pair_count, sample_t* out ); +}; + +#endif diff --git a/thirdparty/game-music-emu/gme/Ym2612_Nuked.cpp b/thirdparty/game-music-emu/gme/Ym2612_Nuked.cpp new file mode 100644 index 000000000..fc49ac690 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ym2612_Nuked.cpp @@ -0,0 +1,1872 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +// Based on Nuked OPN2 ym3438.c and ym3438.h + +#include "Ym2612_Nuked.h" + +/* + * Copyright (C) 2017 Alexey Khokholov (Nuke.YKT) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * + * Nuked OPN2(Yamaha YM3438) emulator. + * Thanks: + * Silicon Pr0n: + * Yamaha YM3438 decap and die shot(digshadow). + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.0.7 + */ + + +#include +#include + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +namespace Ym2612_NukedImpl +{ + +/*EXTRA*/ +#define RSM_FRAC 10 +#define OPN_WRITEBUF_SIZE 2048 +#define OPN_WRITEBUF_DELAY 15 + +enum { + ym3438_type_discrete = 0, /* Discrete YM3438 (Teradrive) */ + ym3438_type_asic = 1, /* ASIC YM3438 (MD1 VA7, MD2, MD3, etc) */ + ym3438_type_ym2612 = 2 /* YM2612 (MD1, MD2 VA2) */ +}; + +/*EXTRA*/ +typedef struct _opn2_writebuf { + Bit64u time; + Bit8u port; + Bit8u data; + Bit8u reserved[6]; +} opn2_writebuf; + +typedef struct +{ + Bit32u cycles; + Bit32u channel; + Bit16s mol, mor; + /* IO */ + Bit16u write_data; + Bit8u write_a; + Bit8u write_d; + Bit8u write_a_en; + Bit8u write_d_en; + Bit8u write_busy; + Bit8u write_busy_cnt; + Bit8u write_fm_address; + Bit8u write_fm_data; + Bit8u write_fm_mode_a; + Bit16u address; + Bit8u data; + Bit8u pin_test_in; + Bit8u pin_irq; + Bit8u busy; + /* LFO */ + Bit8u lfo_en; + Bit8u lfo_freq; + Bit8u lfo_pm; + Bit8u lfo_am; + Bit8u lfo_cnt; + Bit8u lfo_inc; + Bit8u lfo_quotient; + /* Phase generator */ + Bit16u pg_fnum; + Bit8u pg_block; + Bit8u pg_kcode; + Bit32u pg_inc[24]; + Bit32u pg_phase[24]; + Bit8u pg_reset[24]; + Bit32u pg_read; + /* Envelope generator */ + Bit8u eg_cycle; + Bit8u eg_cycle_stop; + Bit8u eg_shift; + Bit8u eg_shift_lock; + Bit8u eg_timer_low_lock; + Bit16u eg_timer; + Bit8u eg_timer_inc; + Bit16u eg_quotient; + Bit8u eg_custom_timer; + Bit8u eg_rate; + Bit8u eg_ksv; + Bit8u eg_inc; + Bit8u eg_ratemax; + Bit8u eg_sl[2]; + Bit8u eg_lfo_am; + Bit8u eg_tl[2]; + Bit8u eg_state[24]; + Bit16u eg_level[24]; + Bit16u eg_out[24]; + Bit8u eg_kon[24]; + Bit8u eg_kon_csm[24]; + Bit8u eg_kon_latch[24]; + Bit8u eg_csm_mode[24]; + Bit8u eg_ssg_enable[24]; + Bit8u eg_ssg_pgrst_latch[24]; + Bit8u eg_ssg_repeat_latch[24]; + Bit8u eg_ssg_hold_up_latch[24]; + Bit8u eg_ssg_dir[24]; + Bit8u eg_ssg_inv[24]; + Bit32u eg_read[2]; + Bit8u eg_read_inc; + /* FM */ + Bit16s fm_op1[6][2]; + Bit16s fm_op2[6]; + Bit16s fm_out[24]; + Bit16u fm_mod[24]; + /* Channel */ + Bit16s ch_acc[6]; + Bit16s ch_out[6]; + Bit16s ch_lock; + Bit8u ch_lock_l; + Bit8u ch_lock_r; + Bit16s ch_read; + /* Timer */ + Bit16u timer_a_cnt; + Bit16u timer_a_reg; + Bit8u timer_a_load_lock; + Bit8u timer_a_load; + Bit8u timer_a_enable; + Bit8u timer_a_reset; + Bit8u timer_a_load_latch; + Bit8u timer_a_overflow_flag; + Bit8u timer_a_overflow; + + Bit16u timer_b_cnt; + Bit8u timer_b_subcnt; + Bit16u timer_b_reg; + Bit8u timer_b_load_lock; + Bit8u timer_b_load; + Bit8u timer_b_enable; + Bit8u timer_b_reset; + Bit8u timer_b_load_latch; + Bit8u timer_b_overflow_flag; + Bit8u timer_b_overflow; + + /* Register set */ + Bit8u mode_test_21[8]; + Bit8u mode_test_2c[8]; + Bit8u mode_ch3; + Bit8u mode_kon_channel; + Bit8u mode_kon_operator[4]; + Bit8u mode_kon[24]; + Bit8u mode_csm; + Bit8u mode_kon_csm; + Bit8u dacen; + Bit16s dacdata; + + Bit8u ks[24]; + Bit8u ar[24]; + Bit8u sr[24]; + Bit8u dt[24]; + Bit8u multi[24]; + Bit8u sl[24]; + Bit8u rr[24]; + Bit8u dr[24]; + Bit8u am[24]; + Bit8u tl[24]; + Bit8u ssg_eg[24]; + + Bit16u fnum[6]; + Bit8u block[6]; + Bit8u kcode[6]; + Bit16u fnum_3ch[6]; + Bit8u block_3ch[6]; + Bit8u kcode_3ch[6]; + Bit8u reg_a4; + Bit8u reg_ac; + Bit8u connect[6]; + Bit8u fb[6]; + Bit8u pan_l[6], pan_r[6]; + Bit8u ams[6]; + Bit8u pms[6]; + + /*EXTRA*/ + Bit32u mute[7]; + Bit32s rateratio; + Bit32s samplecnt; + Bit32s oldsamples[2]; + Bit32s samples[2]; + + Bit64u writebuf_samplecnt; + Bit32u writebuf_cur; + Bit32u writebuf_last; + Bit64u writebuf_lasttime; + opn2_writebuf writebuf[OPN_WRITEBUF_SIZE]; +} ym3438_t; + +/* EXTRA, original was "void OPN2_Reset(ym3438_t *chip)" */ +void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock); +void OPN2_SetChipType(Bit32u type); +void OPN2_Clock(ym3438_t *chip, Bit16s *buffer); +void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data); +void OPN2_SetTestPin(ym3438_t *chip, Bit32u value); +Bit32u OPN2_ReadTestPin(ym3438_t *chip); +Bit32u OPN2_ReadIRQPin(ym3438_t *chip); +Bit8u OPN2_Read(ym3438_t *chip, Bit32u port); + +/*EXTRA*/ +void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data); +void OPN2_Generate(ym3438_t *chip, Bit16s *buf); +void OPN2_GenerateResampled(ym3438_t *chip, Bit16s *buf); +void OPN2_GenerateStream(ym3438_t *chip, Bit16s *output, Bit32u numsamples); +void OPN2_GenerateStreamMix(ym3438_t *chip, Bit16s *output, Bit32u numsamples); +void OPN2_SetOptions(Bit8u flags); +void OPN2_SetMute(ym3438_t *chip, Bit32u mute); + + + + + +enum { + eg_num_attack = 0, + eg_num_decay = 1, + eg_num_sustain = 2, + eg_num_release = 3 +}; + +/* logsin table */ +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +/* exp table */ +static const Bit16u exprom[256] = { + 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, + 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, + 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, + 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, + 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, + 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, + 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, + 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, + 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, + 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, + 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, + 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, + 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, + 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, + 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, + 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, + 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, + 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, + 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, + 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, + 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, + 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, + 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, + 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, + 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, + 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, + 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, + 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, + 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, + 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa +}; + +/* Note table */ +static const Bit32u fn_note[16] = { + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3 +}; + +/* Envelope generator */ +static const Bit32u eg_stephi[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +static const Bit8u eg_am_shift[4] = { + 7, 3, 1, 0 +}; + +/* Phase generator */ +static const Bit32u pg_detune[8] = { 16, 17, 19, 20, 22, 24, 27, 29 }; + +static const Bit32u pg_lfo_sh1[8][8] = { + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 7, 7, 1, 1 }, + { 7, 7, 7, 7, 1, 1, 1, 1 }, + { 7, 7, 7, 1, 1, 1, 1, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 } +}; + +static const Bit32u pg_lfo_sh2[8][8] = { + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 2, 2, 2, 2 }, + { 7, 7, 7, 2, 2, 2, 7, 7 }, + { 7, 7, 2, 2, 7, 7, 2, 2 }, + { 7, 7, 2, 7, 7, 7, 2, 7 }, + { 7, 7, 7, 2, 7, 7, 2, 1 }, + { 7, 7, 7, 2, 7, 7, 2, 1 }, + { 7, 7, 7, 2, 7, 7, 2, 1 } +}; + +/* Address decoder */ +static const Bit32u op_offset[12] = { + 0x000, /* Ch1 OP1/OP2 */ + 0x001, /* Ch2 OP1/OP2 */ + 0x002, /* Ch3 OP1/OP2 */ + 0x100, /* Ch4 OP1/OP2 */ + 0x101, /* Ch5 OP1/OP2 */ + 0x102, /* Ch6 OP1/OP2 */ + 0x004, /* Ch1 OP3/OP4 */ + 0x005, /* Ch2 OP3/OP4 */ + 0x006, /* Ch3 OP3/OP4 */ + 0x104, /* Ch4 OP3/OP4 */ + 0x105, /* Ch5 OP3/OP4 */ + 0x106 /* Ch6 OP3/OP4 */ +}; + +static const Bit32u ch_offset[6] = { + 0x000, /* Ch1 */ + 0x001, /* Ch2 */ + 0x002, /* Ch3 */ + 0x100, /* Ch4 */ + 0x101, /* Ch5 */ + 0x102 /* Ch6 */ +}; + +/* LFO */ +static const Bit32u lfo_cycles[8] = { + 108, 77, 71, 67, 62, 44, 8, 5 +}; + +/* FM algorithm */ +static const Bit32u fm_algorithm[4][6][8] = { + { + { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_0 */ + { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_1 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 1 } /* Out */ + }, + { + { 0, 1, 0, 0, 0, 1, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 1, 1, 1, 0, 0, 0, 0, 0 }, /* OP2 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 1, 1, 1 } /* Out */ + }, + { + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ + { 1, 0, 0, 1, 1, 1, 1, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 1, 1, 1, 1 } /* Out */ + }, + { + { 0, 0, 1, 0, 0, 1, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 0, 0, 0, 1, 0, 0, 0, 0 }, /* OP2 */ + { 1, 1, 0, 1, 1, 0, 0, 0 }, /* Last operator */ + { 0, 0, 1, 0, 0, 0, 0, 0 }, /* Last operator */ + { 1, 1, 1, 1, 1, 1, 1, 1 } /* Out */ + } +}; + +static Bit32u chip_type = ym3438_type_discrete; + +void OPN2_DoIO(ym3438_t *chip) +{ + /* Write signal check */ + chip->write_a_en = (chip->write_a & 0x03) == 0x01; + chip->write_d_en = (chip->write_d & 0x03) == 0x01; + chip->write_a <<= 1; + chip->write_d <<= 1; + /* Busy counter */ + chip->busy = chip->write_busy; + chip->write_busy_cnt += chip->write_busy; + chip->write_busy = (chip->write_busy && !(chip->write_busy_cnt >> 5)) || chip->write_d_en; + chip->write_busy_cnt &= 0x1f; +} + +void OPN2_DoRegWrite(ym3438_t *chip) +{ + Bit32u i; + Bit32u slot = chip->cycles % 12; + Bit32u address; + Bit32u channel = chip->channel; + /* Update registers */ + if (chip->write_fm_data) + { + /* Slot */ + if (op_offset[slot] == (chip->address & 0x107)) + { + if (chip->address & 0x08) + { + /* OP2, OP4 */ + slot += 12; + } + address = chip->address & 0xf0; + switch (address) + { + case 0x30: /* DT, MULTI */ + chip->multi[slot] = chip->data & 0x0f; + if (!chip->multi[slot]) + { + chip->multi[slot] = 1; + } + else + { + chip->multi[slot] <<= 1; + } + chip->dt[slot] = (chip->data >> 4) & 0x07; + break; + case 0x40: /* TL */ + chip->tl[slot] = chip->data & 0x7f; + break; + case 0x50: /* KS, AR */ + chip->ar[slot] = chip->data & 0x1f; + chip->ks[slot] = (chip->data >> 6) & 0x03; + break; + case 0x60: /* AM, DR */ + chip->dr[slot] = chip->data & 0x1f; + chip->am[slot] = (chip->data >> 7) & 0x01; + break; + case 0x70: /* SR */ + chip->sr[slot] = chip->data & 0x1f; + break; + case 0x80: /* SL, RR */ + chip->rr[slot] = chip->data & 0x0f; + chip->sl[slot] = (chip->data >> 4) & 0x0f; + chip->sl[slot] |= (chip->sl[slot] + 1) & 0x10; + break; + case 0x90: /* SSG-EG */ + chip->ssg_eg[slot] = chip->data & 0x0f; + break; + default: + break; + } + } + + /* Channel */ + if (ch_offset[channel] == (chip->address & 0x103)) + { + address = chip->address & 0xfc; + switch (address) + { + case 0xa0: + chip->fnum[channel] = (chip->data & 0xff) | ((chip->reg_a4 & 0x07) << 8); + chip->block[channel] = (chip->reg_a4 >> 3) & 0x07; + chip->kcode[channel] = (chip->block[channel] << 2) | fn_note[chip->fnum[channel] >> 7]; + break; + case 0xa4: + chip->reg_a4 = chip->data & 0xff; + break; + case 0xa8: + chip->fnum_3ch[channel] = (chip->data & 0xff) | ((chip->reg_ac & 0x07) << 8); + chip->block_3ch[channel] = (chip->reg_ac >> 3) & 0x07; + chip->kcode_3ch[channel] = (chip->block_3ch[channel] << 2) | fn_note[chip->fnum_3ch[channel] >> 7]; + break; + case 0xac: + chip->reg_ac = chip->data & 0xff; + break; + case 0xb0: + chip->connect[channel] = chip->data & 0x07; + chip->fb[channel] = (chip->data >> 3) & 0x07; + break; + case 0xb4: + chip->pms[channel] = chip->data & 0x07; + chip->ams[channel] = (chip->data >> 4) & 0x03; + chip->pan_l[channel] = (chip->data >> 7) & 0x01; + chip->pan_r[channel] = (chip->data >> 6) & 0x01; + break; + default: + break; + } + } + } + + if (chip->write_a_en || chip->write_d_en) + { + /* Data */ + if (chip->write_a_en) + { + chip->write_fm_data = 0; + } + + if (chip->write_fm_address && chip->write_d_en) + { + chip->write_fm_data = 1; + } + + /* Address */ + if (chip->write_a_en) + { + if ((chip->write_data & 0xf0) != 0x00) + { + /* FM Write */ + chip->address = chip->write_data; + chip->write_fm_address = 1; + } + else + { + /* SSG write */ + chip->write_fm_address = 0; + } + } + + /* FM Mode */ + /* Data */ + if (chip->write_d_en && (chip->write_data & 0x100) == 0) + { + switch (chip->address) + { + case 0x21: /* LSI test 1 */ + for (i = 0; i < 8; i++) + { + chip->mode_test_21[i] = (chip->write_data >> i) & 0x01; + } + break; + case 0x22: /* LFO control */ + if ((chip->write_data >> 3) & 0x01) + { + chip->lfo_en = 0x7f; + } + else + { + chip->lfo_en = 0; + } + chip->lfo_freq = chip->write_data & 0x07; + break; + case 0x24: /* Timer A */ + chip->timer_a_reg &= 0x03; + chip->timer_a_reg |= (chip->write_data & 0xff) << 2; + break; + case 0x25: + chip->timer_a_reg &= 0x3fc; + chip->timer_a_reg |= chip->write_data & 0x03; + break; + case 0x26: /* Timer B */ + chip->timer_b_reg = chip->write_data & 0xff; + break; + case 0x27: /* CSM, Timer control */ + chip->mode_ch3 = (chip->write_data & 0xc0) >> 6; + chip->mode_csm = chip->mode_ch3 == 2; + chip->timer_a_load = chip->write_data & 0x01; + chip->timer_a_enable = (chip->write_data >> 2) & 0x01; + chip->timer_a_reset = (chip->write_data >> 4) & 0x01; + chip->timer_b_load = (chip->write_data >> 1) & 0x01; + chip->timer_b_enable = (chip->write_data >> 3) & 0x01; + chip->timer_b_reset = (chip->write_data >> 5) & 0x01; + break; + case 0x28: /* Key on/off */ + for (i = 0; i < 4; i++) + { + chip->mode_kon_operator[i] = (chip->write_data >> (4 + i)) & 0x01; + } + if ((chip->write_data & 0x03) == 0x03) + { + /* Invalid address */ + chip->mode_kon_channel = 0xff; + } + else + { + chip->mode_kon_channel = (chip->write_data & 0x03) + ((chip->write_data >> 2) & 1) * 3; + } + break; + case 0x2a: /* DAC data */ + chip->dacdata &= 0x01; + chip->dacdata |= (chip->write_data ^ 0x80) << 1; + break; + case 0x2b: /* DAC enable */ + chip->dacen = chip->write_data >> 7; + break; + case 0x2c: /* LSI test 2 */ + for (i = 0; i < 8; i++) + { + chip->mode_test_2c[i] = (chip->write_data >> i) & 0x01; + } + chip->dacdata &= 0x1fe; + chip->dacdata |= chip->mode_test_2c[3]; + chip->eg_custom_timer = !chip->mode_test_2c[7] && chip->mode_test_2c[6]; + break; + default: + break; + } + } + + /* Address */ + if (chip->write_a_en) + { + chip->write_fm_mode_a = chip->write_data & 0xff; + } + } + + if (chip->write_fm_data) + { + chip->data = chip->write_data & 0xff; + } +} + +void OPN2_PhaseCalcIncrement(ym3438_t *chip) +{ + Bit32u chan = chip->channel; + Bit32u slot = chip->cycles; + Bit32u fnum = chip->pg_fnum; + Bit32u fnum_h = fnum >> 4; + Bit32u fm; + Bit32u basefreq; + Bit8u lfo = chip->lfo_pm; + Bit8u lfo_l = lfo & 0x0f; + Bit8u pms = chip->pms[chan]; + Bit8u dt = chip->dt[slot]; + Bit8u dt_l = dt & 0x03; + Bit8u detune = 0; + Bit8u block, note; + Bit8u sum, sum_h, sum_l; + Bit8u kcode = chip->pg_kcode; + + fnum <<= 1; + /* Apply LFO */ + if (lfo_l & 0x08) + { + lfo_l ^= 0x0f; + } + fm = (fnum_h >> pg_lfo_sh1[pms][lfo_l]) + (fnum_h >> pg_lfo_sh2[pms][lfo_l]); + if (pms > 5) + { + fm <<= pms - 5; + } + fm >>= 2; + if (lfo & 0x10) + { + fnum -= fm; + } + else + { + fnum += fm; + } + fnum &= 0xfff; + + basefreq = (fnum << chip->pg_block) >> 2; + + /* Apply detune */ + if (dt_l) + { + if (kcode > 0x1c) + { + kcode = 0x1c; + } + block = kcode >> 2; + note = kcode & 0x03; + sum = block + 9 + ((dt_l == 3) | (dt_l & 0x02)); + sum_h = sum >> 1; + sum_l = sum & 0x01; + detune = pg_detune[(sum_l << 2) | note] >> (9 - sum_h); + } + if (dt & 0x04) + { + basefreq -= detune; + } + else + { + basefreq += detune; + } + basefreq &= 0x1ffff; + chip->pg_inc[slot] = (basefreq * chip->multi[slot]) >> 1; + chip->pg_inc[slot] &= 0xfffff; +} + +void OPN2_PhaseGenerate(ym3438_t *chip) +{ + Bit32u slot; + /* Mask increment */ + slot = (chip->cycles + 20) % 24; + if (chip->pg_reset[slot]) + { + chip->pg_inc[slot] = 0; + } + /* Phase step */ + slot = (chip->cycles + 19) % 24; + chip->pg_phase[slot] += chip->pg_inc[slot]; + chip->pg_phase[slot] &= 0xfffff; + if (chip->pg_reset[slot] || chip->mode_test_21[3]) + { + chip->pg_phase[slot] = 0; + } +} + +void OPN2_EnvelopeSSGEG(ym3438_t *chip) +{ + Bit32u slot = chip->cycles; + Bit8u direction = 0; + chip->eg_ssg_pgrst_latch[slot] = 0; + chip->eg_ssg_repeat_latch[slot] = 0; + chip->eg_ssg_hold_up_latch[slot] = 0; + chip->eg_ssg_inv[slot] = 0; + if (chip->ssg_eg[slot] & 0x08) + { + direction = chip->eg_ssg_dir[slot]; + if (chip->eg_level[slot] & 0x200) + { + /* Reset */ + if ((chip->ssg_eg[slot] & 0x03) == 0x00) + { + chip->eg_ssg_pgrst_latch[slot] = 1; + } + /* Repeat */ + if ((chip->ssg_eg[slot] & 0x01) == 0x00) + { + chip->eg_ssg_repeat_latch[slot] = 1; + } + /* Inverse */ + if ((chip->ssg_eg[slot] & 0x03) == 0x02) + { + direction ^= 1; + } + if ((chip->ssg_eg[slot] & 0x03) == 0x03) + { + direction = 1; + } + } + /* Hold up */ + if (chip->eg_kon_latch[slot] + && ((chip->ssg_eg[slot] & 0x07) == 0x05 || (chip->ssg_eg[slot] & 0x07) == 0x03)) + { + chip->eg_ssg_hold_up_latch[slot] = 1; + } + direction &= chip->eg_kon[slot]; + chip->eg_ssg_inv[slot] = (chip->eg_ssg_dir[slot] ^ ((chip->ssg_eg[slot] >> 2) & 0x01)) + & chip->eg_kon[slot]; + } + chip->eg_ssg_dir[slot] = direction; + chip->eg_ssg_enable[slot] = (chip->ssg_eg[slot] >> 3) & 0x01; +} + +void OPN2_EnvelopeADSR(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 22) % 24; + + Bit8u nkon = chip->eg_kon_latch[slot]; + Bit8u okon = chip->eg_kon[slot]; + Bit8u kon_event; + Bit8u koff_event; + Bit8u eg_off; + Bit16s level; + Bit16s nextlevel = 0; + Bit16s ssg_level; + Bit8u nextstate = chip->eg_state[slot]; + Bit16s inc = 0; + chip->eg_read[0] = chip->eg_read_inc; + chip->eg_read_inc = chip->eg_inc > 0; + + /* Reset phase generator */ + chip->pg_reset[slot] = (nkon && !okon) || chip->eg_ssg_pgrst_latch[slot]; + + /* KeyOn/Off */ + kon_event = (nkon && !okon) || (okon && chip->eg_ssg_repeat_latch[slot]); + koff_event = okon && !nkon; + + ssg_level = level = (Bit16s)chip->eg_level[slot]; + + if (chip->eg_ssg_inv[slot]) + { + /* Inverse */ + ssg_level = 512 - level; + ssg_level &= 0x3ff; + } + if (koff_event) + { + level = ssg_level; + } + if (chip->eg_ssg_enable[slot]) + { + eg_off = level >> 9; + } + else + { + eg_off = (level & 0x3f0) == 0x3f0; + } + nextlevel = level; + if (kon_event) + { + nextstate = eg_num_attack; + /* Instant attack */ + if (chip->eg_ratemax) + { + nextlevel = 0; + } + else if (chip->eg_state[slot] == eg_num_attack && level != 0 && chip->eg_inc && nkon) + { + inc = (~level << chip->eg_inc) >> 5; + } + } + else + { + switch (chip->eg_state[slot]) + { + case eg_num_attack: + if (level == 0) + { + nextstate = eg_num_decay; + } + else if(chip->eg_inc && !chip->eg_ratemax && nkon) + { + inc = (~level << chip->eg_inc) >> 5; + } + break; + case eg_num_decay: + if ((level >> 5) == chip->eg_sl[1]) + { + nextstate = eg_num_sustain; + } + else if (!eg_off && chip->eg_inc) + { + inc = 1 << (chip->eg_inc - 1); + if (chip->eg_ssg_enable[slot]) + { + inc <<= 2; + } + } + break; + case eg_num_sustain: + case eg_num_release: + if (!eg_off && chip->eg_inc) + { + inc = 1 << (chip->eg_inc - 1); + if (chip->eg_ssg_enable[slot]) + { + inc <<= 2; + } + } + break; + default: + break; + } + if (!nkon) + { + nextstate = eg_num_release; + } + } + if (chip->eg_kon_csm[slot]) + { + nextlevel |= chip->eg_tl[1] << 3; + } + + /* Envelope off */ + if (!kon_event && !chip->eg_ssg_hold_up_latch[slot] && chip->eg_state[slot] != eg_num_attack && eg_off) + { + nextstate = eg_num_release; + nextlevel = 0x3ff; + } + + nextlevel += inc; + + chip->eg_kon[slot] = chip->eg_kon_latch[slot]; + chip->eg_level[slot] = (Bit16u)nextlevel & 0x3ff; + chip->eg_state[slot] = nextstate; +} + +void OPN2_EnvelopePrepare(ym3438_t *chip) +{ + Bit8u rate; + Bit8u sum; + Bit8u inc = 0; + Bit32u slot = chip->cycles; + Bit8u rate_sel; + + /* Prepare increment */ + rate = (chip->eg_rate << 1) + chip->eg_ksv; + + if (rate > 0x3f) + { + rate = 0x3f; + } + + sum = ((rate >> 2) + chip->eg_shift_lock) & 0x0f; + if (chip->eg_rate != 0 && chip->eg_quotient == 2) + { + if (rate < 48) + { + switch (sum) + { + case 12: + inc = 1; + break; + case 13: + inc = (rate >> 1) & 0x01; + break; + case 14: + inc = rate & 0x01; + break; + default: + break; + } + } + else + { + inc = eg_stephi[rate & 0x03][chip->eg_timer_low_lock] + (rate >> 2) - 11; + if (inc > 4) + { + inc = 4; + } + } + } + chip->eg_inc = inc; + chip->eg_ratemax = (rate >> 1) == 0x1f; + + /* Prepare rate & ksv */ + rate_sel = chip->eg_state[slot]; + if ((chip->eg_kon[slot] && chip->eg_ssg_repeat_latch[slot]) + || (!chip->eg_kon[slot] && chip->eg_kon_latch[slot])) + { + rate_sel = eg_num_attack; + } + switch (rate_sel) + { + case eg_num_attack: + chip->eg_rate = chip->ar[slot]; + break; + case eg_num_decay: + chip->eg_rate = chip->dr[slot]; + break; + case eg_num_sustain: + chip->eg_rate = chip->sr[slot]; + break; + case eg_num_release: + chip->eg_rate = (chip->rr[slot] << 1) | 0x01; + break; + default: + break; + } + chip->eg_ksv = chip->pg_kcode >> (chip->ks[slot] ^ 0x03); + if (chip->am[slot]) + { + chip->eg_lfo_am = chip->lfo_am >> eg_am_shift[chip->ams[chip->channel]]; + } + else + { + chip->eg_lfo_am = 0; + } + /* Delay TL & SL value */ + chip->eg_tl[1] = chip->eg_tl[0]; + chip->eg_tl[0] = chip->tl[slot]; + chip->eg_sl[1] = chip->eg_sl[0]; + chip->eg_sl[0] = chip->sl[slot]; +} + +void OPN2_EnvelopeGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 23) % 24; + Bit16u level; + + level = chip->eg_level[slot]; + + if (chip->eg_ssg_inv[slot]) + { + /* Inverse */ + level = 512 - level; + } + if (chip->mode_test_21[5]) + { + level = 0; + } + level &= 0x3ff; + + /* Apply AM LFO */ + level += chip->eg_lfo_am; + + /* Apply TL */ + if (!(chip->mode_csm && chip->channel == 2 + 1)) + { + level += chip->eg_tl[0] << 3; + } + if (level > 0x3ff) + { + level = 0x3ff; + } + chip->eg_out[slot] = level; +} + +void OPN2_UpdateLFO(ym3438_t *chip) +{ + if ((chip->lfo_quotient & lfo_cycles[chip->lfo_freq]) == lfo_cycles[chip->lfo_freq]) + { + chip->lfo_quotient = 0; + chip->lfo_cnt++; + } + else + { + chip->lfo_quotient += chip->lfo_inc; + } + chip->lfo_cnt &= chip->lfo_en; +} + +void OPN2_FMPrepare(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 6) % 24; + Bit32u channel = chip->channel; + Bit16s mod, mod1, mod2; + Bit32u op = slot / 6; + Bit8u connect = chip->connect[channel]; + Bit32u prevslot = (chip->cycles + 18) % 24; + + /* Calculate modulation */ + mod1 = mod2 = 0; + + if (fm_algorithm[op][0][connect]) + { + mod2 |= chip->fm_op1[channel][0]; + } + if (fm_algorithm[op][1][connect]) + { + mod1 |= chip->fm_op1[channel][1]; + } + if (fm_algorithm[op][2][connect]) + { + mod1 |= chip->fm_op2[channel]; + } + if (fm_algorithm[op][3][connect]) + { + mod2 |= chip->fm_out[prevslot]; + } + if (fm_algorithm[op][4][connect]) + { + mod1 |= chip->fm_out[prevslot]; + } + mod = mod1 + mod2; + if (op == 0) + { + /* Feedback */ + mod = mod >> (10 - chip->fb[channel]); + if (!chip->fb[channel]) + { + mod = 0; + } + } + else + { + mod >>= 1; + } + chip->fm_mod[slot] = mod; + + slot = (chip->cycles + 18) % 24; + /* OP1 */ + if (slot / 6 == 0) + { + chip->fm_op1[channel][1] = chip->fm_op1[channel][0]; + chip->fm_op1[channel][0] = chip->fm_out[slot]; + } + /* OP2 */ + if (slot / 6 == 2) + { + chip->fm_op2[channel] = chip->fm_out[slot]; + } +} + +void OPN2_ChGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 18) % 24; + Bit32u channel = chip->channel; + Bit32u op = slot / 6; + Bit32u test_dac = chip->mode_test_2c[5]; + Bit16s acc = chip->ch_acc[channel]; + Bit16s add = test_dac; + Bit16s sum = 0; + if (op == 0 && !test_dac) + { + acc = 0; + } + if (fm_algorithm[op][5][chip->connect[channel]] && !test_dac) + { + add += chip->fm_out[slot] >> 5; + } + sum = acc + add; + /* Clamp */ + if (sum > 255) + { + sum = 255; + } + else if(sum < -256) + { + sum = -256; + } + + if (op == 0 || test_dac) + { + chip->ch_out[channel] = chip->ch_acc[channel]; + } + chip->ch_acc[channel] = sum; +} + +void OPN2_ChOutput(ym3438_t *chip) +{ + Bit32u cycles = chip->cycles; + Bit32u slot = chip->cycles; + Bit32u channel = chip->channel; + Bit32u test_dac = chip->mode_test_2c[5]; + Bit16s out; + Bit16s sign; + Bit32u out_en; + chip->ch_read = chip->ch_lock; + if (slot < 12) + { + /* Ch 4,5,6 */ + channel++; + } + if ((cycles & 3) == 0) + { + if (!test_dac) + { + /* Lock value */ + chip->ch_lock = chip->ch_out[channel]; + } + chip->ch_lock_l = chip->pan_l[channel]; + chip->ch_lock_r = chip->pan_r[channel]; + } + /* Ch 6 */ + if (((cycles >> 2) == 1 && chip->dacen) || test_dac) + { + out = (Bit16s)chip->dacdata; + out <<= 7; + out >>= 7; + } + else + { + out = chip->ch_lock; + } + chip->mol = 0; + chip->mor = 0; + + if (chip_type == ym3438_type_ym2612) + { + out_en = ((cycles & 3) == 3) || test_dac; + /* YM2612 DAC emulation(not verified) */ + sign = out >> 8; + if (out >= 0) + { + out++; + sign++; + } + if (chip->ch_lock_l && out_en) + { + chip->mol = out; + } + else + { + chip->mol = sign; + } + if (chip->ch_lock_r && out_en) + { + chip->mor = out; + } + else + { + chip->mor = sign; + } + /* Amplify signal */ + chip->mol *= 3; + chip->mor *= 3; + } + else + { + out_en = ((cycles & 3) != 0) || test_dac; + /* Discrete YM3438 seems has the ladder effect too */ + if (out >= 0 && chip_type == ym3438_type_discrete) + { + out++; + } + if (chip->ch_lock_l && out_en) + { + chip->mol = out; + } + if (chip->ch_lock_r && out_en) + { + chip->mor = out; + } + } +} + +void OPN2_FMGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 19) % 24; + /* Calculate phase */ + Bit16u phase = (chip->fm_mod[slot] + (chip->pg_phase[slot] >> 10)) & 0x3ff; + Bit16u quarter; + Bit16u level; + Bit16s output; + if (phase & 0x100) + { + quarter = (phase ^ 0xff) & 0xff; + } + else + { + quarter = phase & 0xff; + } + level = logsinrom[quarter]; + /* Apply envelope */ + level += chip->eg_out[slot] << 2; + /* Transform */ + if (level > 0x1fff) + { + level = 0x1fff; + } + output = ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 2) >> (level >> 8); + if (phase & 0x200) + { + output = ((~output) ^ (chip->mode_test_21[4] << 13)) + 1; + } + else + { + output = output ^ (chip->mode_test_21[4] << 13); + } + output <<= 2; + output >>= 2; + chip->fm_out[slot] = output; +} + +void OPN2_DoTimerA(ym3438_t *chip) +{ + Bit16u time; + Bit8u load; + load = chip->timer_a_overflow; + if (chip->cycles == 2) + { + /* Lock load value */ + load |= (!chip->timer_a_load_lock && chip->timer_a_load); + chip->timer_a_load_lock = chip->timer_a_load; + if (chip->mode_csm) + { + /* CSM KeyOn */ + chip->mode_kon_csm = load; + } + else + { + chip->mode_kon_csm = 0; + } + } + /* Load counter */ + if (chip->timer_a_load_latch) + { + time = chip->timer_a_reg; + } + else + { + time = chip->timer_a_cnt; + } + chip->timer_a_load_latch = load; + /* Increase counter */ + if ((chip->cycles == 1 && chip->timer_a_load_lock) || chip->mode_test_21[2]) + { + time++; + } + /* Set overflow flag */ + if (chip->timer_a_reset) + { + chip->timer_a_reset = 0; + chip->timer_a_overflow_flag = 0; + } + else + { + chip->timer_a_overflow_flag |= chip->timer_a_overflow & chip->timer_a_enable; + } + chip->timer_a_overflow = (time >> 10); + chip->timer_a_cnt = time & 0x3ff; +} + +void OPN2_DoTimerB(ym3438_t *chip) +{ + Bit16u time; + Bit8u load; + load = chip->timer_b_overflow; + if (chip->cycles == 2) + { + /* Lock load value */ + load |= (!chip->timer_b_load_lock && chip->timer_b_load); + chip->timer_b_load_lock = chip->timer_b_load; + } + /* Load counter */ + if (chip->timer_b_load_latch) + { + time = chip->timer_b_reg; + } + else + { + time = chip->timer_b_cnt; + } + chip->timer_b_load_latch = load; + /* Increase counter */ + if (chip->cycles == 1) + { + chip->timer_b_subcnt++; + } + if ((chip->timer_b_subcnt == 0x10 && chip->timer_b_load_lock) || chip->mode_test_21[2]) + { + time++; + } + chip->timer_b_subcnt &= 0x0f; + /* Set overflow flag */ + if (chip->timer_b_reset) + { + chip->timer_b_reset = 0; + chip->timer_b_overflow_flag = 0; + } + else + { + chip->timer_b_overflow_flag |= chip->timer_b_overflow & chip->timer_b_enable; + } + chip->timer_b_overflow = (time >> 8); + chip->timer_b_cnt = time & 0xff; +} + +void OPN2_KeyOn(ym3438_t*chip) +{ + Bit32u slot = chip->cycles; + Bit32u chan = chip->channel; + /* Key On */ + chip->eg_kon_latch[slot] = chip->mode_kon[slot]; + chip->eg_kon_csm[slot] = 0; + if (chip->channel == 2 && chip->mode_kon_csm) + { + /* CSM Key On */ + chip->eg_kon_latch[slot] = 1; + chip->eg_kon_csm[slot] = 1; + } + if (chip->cycles == chip->mode_kon_channel) + { + /* OP1 */ + chip->mode_kon[chan] = chip->mode_kon_operator[0]; + /* OP2 */ + chip->mode_kon[chan + 12] = chip->mode_kon_operator[1]; + /* OP3 */ + chip->mode_kon[chan + 6] = chip->mode_kon_operator[2]; + /* OP4 */ + chip->mode_kon[chan + 18] = chip->mode_kon_operator[3]; + } +} + +void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock) +{ + Bit32u i, rateratio; + rateratio = (Bit32u)chip->rateratio; + memset(chip, 0, sizeof(ym3438_t)); + for (i = 0; i < 24; i++) + { + chip->eg_out[i] = 0x3ff; + chip->eg_level[i] = 0x3ff; + chip->eg_state[i] = eg_num_release; + chip->multi[i] = 1; + } + for (i = 0; i < 6; i++) + { + chip->pan_l[i] = 1; + chip->pan_r[i] = 1; + } + + if (rate != 0) + { + chip->rateratio = (Bit32s)(Bit32u)((((Bit64u)144 * rate) << RSM_FRAC) / clock); + } + else + { + chip->rateratio = (Bit32s)rateratio; + } +} + +void OPN2_SetChipType(Bit32u type) +{ + chip_type = type; +} + +void OPN2_Clock(ym3438_t *chip, Bit16s *buffer) +{ + Bit32u slot = chip->cycles; + chip->lfo_inc = chip->mode_test_21[1]; + chip->pg_read >>= 1; + chip->eg_read[1] >>= 1; + chip->eg_cycle++; + /* Lock envelope generator timer value */ + if (chip->cycles == 1 && chip->eg_quotient == 2) + { + if (chip->eg_cycle_stop) + { + chip->eg_shift_lock = 0; + } + else + { + chip->eg_shift_lock = chip->eg_shift + 1; + } + chip->eg_timer_low_lock = chip->eg_timer & 0x03; + } + /* Cycle specific functions */ + switch (chip->cycles) + { + case 0: + chip->lfo_pm = chip->lfo_cnt >> 2; + if (chip->lfo_cnt & 0x40) + { + chip->lfo_am = chip->lfo_cnt & 0x3f; + } + else + { + chip->lfo_am = chip->lfo_cnt ^ 0x3f; + } + chip->lfo_am <<= 1; + break; + case 1: + chip->eg_quotient++; + chip->eg_quotient %= 3; + chip->eg_cycle = 0; + chip->eg_cycle_stop = 1; + chip->eg_shift = 0; + chip->eg_timer_inc |= chip->eg_quotient >> 1; + chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; + chip->eg_timer_inc = chip->eg_timer >> 12; + chip->eg_timer &= 0xfff; + break; + case 2: + chip->pg_read = chip->pg_phase[21] & 0x3ff; + chip->eg_read[1] = chip->eg_out[0]; + break; + case 13: + chip->eg_cycle = 0; + chip->eg_cycle_stop = 1; + chip->eg_shift = 0; + chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; + chip->eg_timer_inc = chip->eg_timer >> 12; + chip->eg_timer &= 0xfff; + break; + case 23: + chip->lfo_inc |= 1; + break; + } + chip->eg_timer &= ~(chip->mode_test_21[5] << chip->eg_cycle); + if (((chip->eg_timer >> chip->eg_cycle) | (chip->pin_test_in & chip->eg_custom_timer)) & chip->eg_cycle_stop) + { + chip->eg_shift = chip->eg_cycle; + chip->eg_cycle_stop = 0; + } + + OPN2_DoIO(chip); + + OPN2_DoTimerA(chip); + OPN2_DoTimerB(chip); + OPN2_KeyOn(chip); + + OPN2_ChOutput(chip); + OPN2_ChGenerate(chip); + + OPN2_FMPrepare(chip); + OPN2_FMGenerate(chip); + + OPN2_PhaseGenerate(chip); + OPN2_PhaseCalcIncrement(chip); + + OPN2_EnvelopeADSR(chip); + OPN2_EnvelopeGenerate(chip); + OPN2_EnvelopeSSGEG(chip); + OPN2_EnvelopePrepare(chip); + + /* Prepare fnum & block */ + if (chip->mode_ch3) + { + /* Channel 3 special mode */ + switch (slot) + { + case 1: /* OP1 */ + chip->pg_fnum = chip->fnum_3ch[1]; + chip->pg_block = chip->block_3ch[1]; + chip->pg_kcode = chip->kcode_3ch[1]; + break; + case 7: /* OP3 */ + chip->pg_fnum = chip->fnum_3ch[0]; + chip->pg_block = chip->block_3ch[0]; + chip->pg_kcode = chip->kcode_3ch[0]; + break; + case 13: /* OP2 */ + chip->pg_fnum = chip->fnum_3ch[2]; + chip->pg_block = chip->block_3ch[2]; + chip->pg_kcode = chip->kcode_3ch[2]; + break; + case 19: /* OP4 */ + default: + chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; + chip->pg_block = chip->block[(chip->channel + 1) % 6]; + chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; + break; + } + } + else + { + chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; + chip->pg_block = chip->block[(chip->channel + 1) % 6]; + chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; + } + + OPN2_UpdateLFO(chip); + OPN2_DoRegWrite(chip); + chip->cycles = (chip->cycles + 1) % 24; + chip->channel = chip->cycles % 6; + + buffer[0] = chip->mol; + buffer[1] = chip->mor; +} + +void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data) +{ + port &= 3; + chip->write_data = ((port << 7) & 0x100) | data; + if (port & 1) + { + /* Data */ + chip->write_d |= 1; + } + else + { + /* Address */ + chip->write_a |= 1; + } +} + +void OPN2_SetTestPin(ym3438_t *chip, Bit32u value) +{ + chip->pin_test_in = value & 1; +} + +Bit32u OPN2_ReadTestPin(ym3438_t *chip) +{ + if (!chip->mode_test_2c[7]) + { + return 0; + } + return chip->cycles == 23; +} + +Bit32u OPN2_ReadIRQPin(ym3438_t *chip) +{ + return chip->timer_a_overflow_flag | chip->timer_b_overflow_flag; +} + +Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) +{ + if ((port & 3) == 0 || chip_type == ym3438_type_asic) + { + if (chip->mode_test_21[6]) + { + /* Read test data */ + Bit32u slot = (chip->cycles + 18) % 24; + Bit16u testdata = ((chip->pg_read & 0x01) << 15) + | ((chip->eg_read[chip->mode_test_21[0]] & 0x01) << 14); + if (chip->mode_test_2c[4]) + { + testdata |= chip->ch_read & 0x1ff; + } + else + { + testdata |= chip->fm_out[slot] & 0x3fff; + } + if (chip->mode_test_21[7]) + { + return testdata & 0xff; + } + else + { + return testdata >> 8; + } + } + else + { + return (Bit8u)(chip->busy << 7) | (Bit8u)(chip->timer_b_overflow_flag << 1) + | (Bit8u)chip->timer_a_overflow_flag; + } + } + return 0; +} + +void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data) +{ + Bit64u time1, time2; + Bit16s buffer[2]; + Bit64u skip; + + if (chip->writebuf[chip->writebuf_last].port & 0x04) + { + OPN2_Write(chip, chip->writebuf[chip->writebuf_last].port & 0X03, + chip->writebuf[chip->writebuf_last].data); + + chip->writebuf_cur = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; + skip = chip->writebuf[chip->writebuf_last].time - chip->writebuf_samplecnt; + chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + while (skip--) + { + OPN2_Clock(chip, buffer); + } + } + + chip->writebuf[chip->writebuf_last].port = (port & 0x03) | 0x04; + chip->writebuf[chip->writebuf_last].data = data; + time1 = chip->writebuf_lasttime + OPN_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + chip->writebuf[chip->writebuf_last].time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; +} + +void OPN2_Generate(ym3438_t *chip, Bit16s *buf) +{ + Bit32u i; + Bit16s buffer[2]; + Bit32u mute; + + buf[0] = 0; + buf[1] = 0; + + for (i = 0; i < 24; i++) + { + switch (chip->cycles >> 2) + { + case 0: /* Ch 2 */ + mute = chip->mute[1]; + break; + case 1: /* Ch 6, DAC */ + mute = chip->mute[5 + chip->dacen]; + break; + case 2: /* Ch 4 */ + mute = chip->mute[3]; + break; + case 3: /* Ch 1 */ + mute = chip->mute[0]; + break; + case 4: /* Ch 5 */ + mute = chip->mute[4]; + break; + case 5: /* Ch 3 */ + mute = chip->mute[2]; + break; + default: + mute = 0; + break; + } + OPN2_Clock(chip, buffer); + if (!mute) + { + buf[0] += buffer[0]; + buf[1] += buffer[1]; + } + + while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + { + if (!(chip->writebuf[chip->writebuf_cur].port & 0x04)) + { + break; + } + chip->writebuf[chip->writebuf_cur].port &= 0x03; + OPN2_Write(chip, chip->writebuf[chip->writebuf_cur].port, + chip->writebuf[chip->writebuf_cur].data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPN_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; + } +} + +void OPN2_GenerateResampled(ym3438_t *chip, Bit16s *buf) +{ + Bit16s buffer[2]; + + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPN2_Generate(chip, buffer); + chip->samples[0] = buffer[0] * 11; + chip->samples[1] = buffer[1] * 11; + chip->samplecnt -= chip->rateratio; + } + buf[0] = (Bit16s)(((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio)>>1); + buf[1] = (Bit16s)(((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio)>>1); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPN2_GenerateStream(ym3438_t *chip, Bit16s *output, Bit32u numsamples) +{ + Bit32u i; + Bit16s buffer[2]; + + for (i = 0; i < numsamples; i++) + { + OPN2_GenerateResampled(chip, buffer); + *output++ = buffer[0]; + *output++ = buffer[1]; + } +} + +void OPN2_GenerateStreamMix(ym3438_t *chip, Bit16s *output, Bit32u numsamples) +{ + Bit32u i; + Bit16s buffer[2]; + + for (i = 0; i < numsamples; i++) + { + OPN2_GenerateResampled(chip, buffer); + *output++ += buffer[0]; + *output++ += buffer[1]; + } +} + + +void OPN2_SetOptions(Bit8u flags) +{ + switch ((flags >> 3) & 0x03) + { + case 0x00: /* YM2612 */ + default: + OPN2_SetChipType(ym3438_type_ym2612); + break; + case 0x01: /* ASIC YM3438 */ + OPN2_SetChipType(ym3438_type_asic); + break; + case 0x02: /* Discrete YM3438 */ + OPN2_SetChipType(ym3438_type_discrete); + break; + } +} + +void OPN2_SetMute(ym3438_t *chip, Bit32u mute) +{ + Bit32u i; + for (i = 0; i < 7; i++) + { + chip->mute[i] = (mute >> i) & 0x01; + } +} + + +} // Ym2612_NukedImpl + + +Ym2612_Nuked_Emu::Ym2612_Nuked_Emu() +{ + Ym2612_NukedImpl::OPN2_SetChipType( Ym2612_NukedImpl::ym3438_type_asic ); + impl = new Ym2612_NukedImpl::ym3438_t; +} + +Ym2612_Nuked_Emu::~Ym2612_Nuked_Emu() +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( chip_r ) delete chip_r; +} + +const char *Ym2612_Nuked_Emu::set_rate(double sample_rate, double clock_rate) +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( !chip_r ) + return "Out of memory"; + prev_sample_rate = sample_rate; + prev_clock_rate = clock_rate; + Ym2612_NukedImpl::OPN2_Reset( chip_r, static_cast(sample_rate), static_cast(clock_rate) ); + return 0; +} + +void Ym2612_Nuked_Emu::reset() +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( !chip_r ) Ym2612_NukedImpl::OPN2_Reset( chip_r, static_cast(prev_sample_rate), static_cast(prev_clock_rate) ); +} + +void Ym2612_Nuked_Emu::mute_voices(int mask) +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( chip_r ) Ym2612_NukedImpl::OPN2_SetMute( chip_r, mask ); +} + +void Ym2612_Nuked_Emu::write0(int addr, int data) +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( !chip_r ) return; + Ym2612_NukedImpl::OPN2_WriteBuffered( chip_r, 0, static_cast(addr) ); + Ym2612_NukedImpl::OPN2_WriteBuffered( chip_r, 1, static_cast(data) ); +} + +void Ym2612_Nuked_Emu::write1(int addr, int data) +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( !chip_r ) return; + Ym2612_NukedImpl::OPN2_WriteBuffered( chip_r, 0 + 2, static_cast(addr) ); + Ym2612_NukedImpl::OPN2_WriteBuffered( chip_r, 1 + 2, static_cast(data) ); +} + +void Ym2612_Nuked_Emu::run(int pair_count, Ym2612_Nuked_Emu::sample_t *out) +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( !chip_r ) return; + Ym2612_NukedImpl::OPN2_GenerateStream(chip_r, out, pair_count); +} diff --git a/thirdparty/game-music-emu/gme/Ym2612_Nuked.h b/thirdparty/game-music-emu/gme/Ym2612_Nuked.h new file mode 100644 index 000000000..6c265b138 --- /dev/null +++ b/thirdparty/game-music-emu/gme/Ym2612_Nuked.h @@ -0,0 +1,41 @@ +// YM2612 FM sound chip emulator interface + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef YM2612_EMU_H +#define YM2612_EMU_H + +typedef void Ym2612_Nuked_Impl; + +class Ym2612_Nuked_Emu { + Ym2612_Nuked_Impl* impl; + double prev_sample_rate; + double prev_clock_rate; +public: + Ym2612_Nuked_Emu(); + ~Ym2612_Nuked_Emu(); + + // Set output sample rate and chip clock rates, in Hz. Returns non-zero + // if error. + const char* set_rate( double sample_rate, double clock_rate ); + + // Reset to power-up state + void reset(); + + // Mute voice n if bit n (1 << n) of mask is set + enum { channel_count = 6 }; + void mute_voices( int mask ); + + // Write addr to register 0 then data to register 1 + void write0( int addr, int data ); + + // Write addr to register 2 then data to register 3 + void write1( int addr, int data ); + + // Run and add pair_count samples into current output buffer contents + typedef short sample_t; + enum { out_chan_count = 2 }; // stereo + void run( int pair_count, sample_t* out ); +}; + +#endif + diff --git a/thirdparty/game-music-emu/gme/blargg_common.h b/thirdparty/game-music-emu/gme/blargg_common.h new file mode 100644 index 000000000..13cc2417e --- /dev/null +++ b/thirdparty/game-music-emu/gme/blargg_common.h @@ -0,0 +1,160 @@ +// Sets up common environment for Shay Green's libraries. +// To change configuration options, modify blargg_config.h, not this file. + +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +#include +#include +#include +#include + +#undef BLARGG_COMMON_H +// allow blargg_config.h to #include blargg_common.h +#include "blargg_config.h" +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +// BLARGG_RESTRICT: equivalent to restrict, where supported +#if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT +#endif + +// STATIC_CAST(T,expr): Used in place of static_cast (expr) +#ifndef STATIC_CAST + #define STATIC_CAST(T,expr) ((T) (expr)) +#endif + +// blargg_err_t (0 on success, otherwise error string) +#ifndef blargg_err_t + typedef const char* blargg_err_t; +#endif + +// blargg_vector - very lightweight vector of POD types (no constructor/destructor) +template +class blargg_vector { + T* begin_; + size_t size_; +public: + blargg_vector() : begin_( 0 ), size_( 0 ) { } + ~blargg_vector() { free( begin_ ); } + size_t size() const { return size_; } + T* begin() const { return begin_; } + T* end() const { return begin_ + size_; } + blargg_err_t resize( size_t n ) + { + void* p = realloc( begin_, n * sizeof (T) ); + if ( !p && n ) + return "Out of memory"; + begin_ = (T*) p; + size_ = n; + return 0; + } + void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } + T& operator [] ( size_t n ) const + { + assert( n <= size_ ); // <= to allow past-the-end value + return begin_ [n]; + } +}; + +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if operator new can return NULL + #if __cplusplus >= 199711 || __GNUC__ >= 3 + #define BLARGG_THROWS( spec ) throw spec + #else + #define BLARGG_THROWS( spec ) + #endif + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ + void operator delete ( void* p ) { free( p ); } + #define BLARGG_NEW new +#else + #include + #define BLARGG_NEW new (std::nothrow) +#endif + +// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant) +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) + +#define BLARGG_2CHAR( a, b ) \ + ((a&0xFF)*0x100L + (b&0xFF)) + +// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +#ifndef BOOST_STATIC_ASSERT + #ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) + #else + // Some other compilers fail when declaring same function multiple times in class, + // so differentiate them by line + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) + #endif +#endif + +// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, +// compiler is assumed to support bool. If undefined, availability is determined. +#ifndef BLARGG_COMPILER_HAS_BOOL + #if defined (__MWERKS__) + #if !__option(bool) + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (_MSC_VER) + #if _MSC_VER < 1100 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (__GNUC__) + // supports bool + #elif __cplusplus < 199711 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif +#endif +#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL + // If you get errors here, modify your blargg_config.h file + typedef int bool; + const bool true = 1; + const bool false = 0; +#endif + +// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough + +#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF + typedef long blargg_long; +#else + typedef int blargg_long; +#endif + +#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF + typedef unsigned long blargg_ulong; +#else + typedef unsigned blargg_ulong; +#endif + +// int8_t etc. + +// TODO: Add CMake check for this, although I'd likely just point affected +// persons to a real compiler... +#if 1 || defined (HAVE_STDINT_H) + #include +#endif + +#if __GNUC__ >= 3 + #define BLARGG_DEPRECATED __attribute__ ((deprecated)) +#else + #define BLARGG_DEPRECATED +#endif + +// Use in place of "= 0;" for a pure virtual, since these cause calls to std C++ lib. +// During development, BLARGG_PURE( x ) expands to = 0; +// virtual int func() BLARGG_PURE( { return 0; } ) +#ifndef BLARGG_PURE + #define BLARGG_PURE( def ) def +#endif + +#endif +#endif diff --git a/thirdparty/game-music-emu/gme/blargg_config.h b/thirdparty/game-music-emu/gme/blargg_config.h new file mode 100644 index 000000000..377dd2d8c --- /dev/null +++ b/thirdparty/game-music-emu/gme/blargg_config.h @@ -0,0 +1,43 @@ +// Library configuration. Modify this file as necessary. + +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment to use zlib for transparent decompression of gzipped files +//#define HAVE_ZLIB_H + +// Uncomment and edit list to support only the listed game music types, +// so that the others don't get linked in at all. +/* +#define GME_TYPE_LIST \ + gme_ay_type,\ + gme_gbs_type,\ + gme_gym_type,\ + gme_hes_type,\ + gme_kss_type,\ + gme_nsf_type,\ + gme_nsfe_type,\ + gme_sap_type,\ + gme_spc_type,\ + gme_vgm_type,\ + gme_vgz_type +*/ + +// Uncomment to enable platform-specific optimizations +//#define BLARGG_NONPORTABLE 1 + +// Uncomment to use faster, lower quality sound synthesis +//#define BLIP_BUFFER_FAST 1 + +// Uncomment if automatic byte-order determination doesn't work +//#define BLARGG_BIG_ENDIAN 1 + +// Uncomment if you get errors in the bool section of blargg_common.h +//#define BLARGG_COMPILER_HAS_BOOL 1 + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/thirdparty/game-music-emu/gme/blargg_endian.h b/thirdparty/game-music-emu/gme/blargg_endian.h new file mode 100644 index 000000000..46e58e2f0 --- /dev/null +++ b/thirdparty/game-music-emu/gme/blargg_endian.h @@ -0,0 +1,184 @@ +// CPU Byte Order Utilities + +#ifndef BLARGG_ENDIAN +#define BLARGG_ENDIAN + +#include "blargg_common.h" + +// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) +#if defined (__i386__) || defined (__x86_64__) || defined (_M_IX86) || defined (_M_X64) + #define BLARGG_CPU_X86 1 + #define BLARGG_CPU_CISC 1 +#endif + +#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) || \ + defined (__POWERPC__) || defined (__powerc) + #define BLARGG_CPU_POWERPC 1 + #define BLARGG_CPU_RISC 1 +#endif + +// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only +// one may be #defined to 1. Only needed if something actually depends on byte order. +#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) +#ifdef __GLIBC__ + // GCC handles this for us + #include + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define BLARGG_LITTLE_ENDIAN 1 + #elif __BYTE_ORDER == __BIG_ENDIAN + #define BLARGG_BIG_ENDIAN 1 + #endif +#else + +#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ + (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) + #define BLARGG_LITTLE_ENDIAN 1 +#endif + +#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ + defined (__sparc__) || BLARGG_CPU_POWERPC || \ + (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) + #define BLARGG_BIG_ENDIAN 1 +#elif !defined (__mips__) + // No endian specified; assume little-endian, since it's most common + #define BLARGG_LITTLE_ENDIAN 1 +#endif +#endif +#endif + +#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN + #undef BLARGG_LITTLE_ENDIAN + #undef BLARGG_BIG_ENDIAN +#endif + +inline void blargg_verify_byte_order() +{ + #ifndef NDEBUG + #if BLARGG_BIG_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i == 0 ); + #elif BLARGG_LITTLE_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i != 0 ); + #endif + #endif +} + +inline unsigned get_le16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 8 | + (unsigned) ((unsigned char const*) p) [1]; +} + +inline blargg_ulong get_le32( void const* p ) +{ + return (blargg_ulong) ((unsigned char const*) p) [3] << 24 | + (blargg_ulong) ((unsigned char const*) p) [2] << 16 | + (blargg_ulong) ((unsigned char const*) p) [1] << 8 | + (blargg_ulong) ((unsigned char const*) p) [0]; +} + +inline blargg_ulong get_be32( void const* p ) +{ + return (blargg_ulong) ((unsigned char const*) p) [0] << 24 | + (blargg_ulong) ((unsigned char const*) p) [1] << 16 | + (blargg_ulong) ((unsigned char const*) p) [2] << 8 | + (blargg_ulong) ((unsigned char const*) p) [3]; +} + +inline void set_le16( void* p, unsigned n ) +{ + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [0] = (unsigned char) n; +} + +inline void set_be16( void* p, unsigned n ) +{ + ((unsigned char*) p) [0] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) n; +} + +inline void set_le32( void* p, blargg_ulong n ) +{ + ((unsigned char*) p) [0] = (unsigned char) n; + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [2] = (unsigned char) (n >> 16); + ((unsigned char*) p) [3] = (unsigned char) (n >> 24); +} + +inline void set_be32( void* p, blargg_ulong n ) +{ + ((unsigned char*) p) [3] = (unsigned char) n; + ((unsigned char*) p) [2] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) (n >> 16); + ((unsigned char*) p) [0] = (unsigned char) (n >> 24); +} + +#if BLARGG_NONPORTABLE + // Optimized implementation if byte order is known + #if BLARGG_LITTLE_ENDIAN + #define GET_LE16( addr ) (*(uint16_t*) (addr)) + #define GET_LE32( addr ) (*(uint32_t*) (addr)) + #define SET_LE16( addr, data ) (void) (*(uint16_t*) (addr) = (data)) + #define SET_LE32( addr, data ) (void) (*(uint32_t*) (addr) = (data)) + #elif BLARGG_BIG_ENDIAN + #define GET_BE16( addr ) (*(uint16_t*) (addr)) + #define GET_BE32( addr ) (*(uint32_t*) (addr)) + #define SET_BE16( addr, data ) (void) (*(uint16_t*) (addr) = (data)) + #define SET_BE32( addr, data ) (void) (*(uint32_t*) (addr) = (data)) + + #if BLARGG_CPU_POWERPC + // PowerPC has special byte-reversed instructions + #if defined (__MWERKS__) + #define GET_LE16( addr ) (__lhbrx( addr, 0 )) + #define GET_LE32( addr ) (__lwbrx( addr, 0 )) + #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 )) + #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) + #elif defined (__GNUC__) + #define GET_LE16( addr ) ({unsigned short ppc_lhbrx_; __asm__ volatile( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr) : "memory" ); ppc_lhbrx_;}) + #define GET_LE32( addr ) ({unsigned short ppc_lwbrx_; __asm__ volatile( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr) : "memory" ); ppc_lwbrx_;}) + #define SET_LE16( addr, in ) ({__asm__ volatile( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) + #define SET_LE32( addr, in ) ({__asm__ volatile( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) + #endif + #endif + #endif +#endif + +#ifndef GET_LE16 + #define GET_LE16( addr ) get_le16( addr ) + #define SET_LE16( addr, data ) set_le16( addr, data ) +#endif + +#ifndef GET_LE32 + #define GET_LE32( addr ) get_le32( addr ) + #define SET_LE32( addr, data ) set_le32( addr, data ) +#endif + +#ifndef GET_BE16 + #define GET_BE16( addr ) get_be16( addr ) + #define SET_BE16( addr, data ) set_be16( addr, data ) +#endif + +#ifndef GET_BE32 + #define GET_BE32( addr ) get_be32( addr ) + #define SET_BE32( addr, data ) set_be32( addr, data ) +#endif + +// auto-selecting versions + +inline void set_le( uint16_t* p, unsigned n ) { SET_LE16( p, n ); } +inline void set_le( uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } +inline void set_be( uint16_t* p, unsigned n ) { SET_BE16( p, n ); } +inline void set_be( uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } +inline unsigned get_le( uint16_t* p ) { return GET_LE16( p ); } +inline blargg_ulong get_le( uint32_t* p ) { return GET_LE32( p ); } +inline unsigned get_be( uint16_t* p ) { return GET_BE16( p ); } +inline blargg_ulong get_be( uint32_t* p ) { return GET_BE32( p ); } + +#endif diff --git a/thirdparty/game-music-emu/gme/blargg_source.h b/thirdparty/game-music-emu/gme/blargg_source.h new file mode 100644 index 000000000..b65afd30b --- /dev/null +++ b/thirdparty/game-music-emu/gme/blargg_source.h @@ -0,0 +1,123 @@ +/* Included at the beginning of library source files, after all other #include lines. +Sets up helpful macros and services used in my source code. They don't need +module an annoying module prefix on their names since they are defined after +all other #include lines. */ + +#ifndef BLARGG_SOURCE_H +#define BLARGG_SOURCE_H + +// If debugging is enabled, abort program if expr is false. Meant for checking +// internal state and consistency. A failed assertion indicates a bug in the module. +// void assert( bool expr ); +#include + +// If debugging is enabled and expr is false, abort program. Meant for checking +// caller-supplied parameters and operations that are outside the control of the +// module. A failed requirement indicates a bug outside the module. +// void require( bool expr ); +#undef require +#define require( expr ) assert( expr ) + +// Use to provide hints to compiler for optimized code layout in situations where we +// can almost always expect a conditional to go one way or the other. Should only be +// used in situations where an unexpected branch is truly exceptional though! +#undef likely +#undef unlikely +#ifdef __GNUC__ + #define likely( x ) __builtin_expect(x, 1) + #define unlikely( x ) __builtin_expect(x, 0) +#else + #define likely( x ) (x) + #define unlikely( x ) (x) +#endif + +// Like printf() except output goes to debug log file. Might be defined to do +// nothing (not even evaluate its arguments). +// void debug_printf( const char* format, ... ); +static inline void blargg_dprintf_( const char*, ... ) { } +#undef debug_printf +#define debug_printf (1) ? (void) 0 : blargg_dprintf_ + +// If enabled, evaluate expr and if false, make debug log entry with source file +// and line. Meant for finding situations that should be examined further, but that +// don't indicate a problem. In all cases, execution continues normally. +#undef check +#define check( expr ) ((void) 0) + +// If expr yields error string, return it from current function, otherwise continue. +#undef RETURN_ERR +#define RETURN_ERR( expr ) do { \ + blargg_err_t blargg_return_err_ = (expr); \ + if ( blargg_return_err_ ) return blargg_return_err_; \ + } while ( 0 ) + +// If ptr is 0, return out of memory error string. +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) + +// Avoid any macros which evaluate their arguments multiple times +#undef min +#undef max + +#define DEF_MIN_MAX( type ) \ + static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\ + static inline type max( type x, type y ) { if ( y < x ) return x; return y; } + +DEF_MIN_MAX( int ) +DEF_MIN_MAX( unsigned ) +DEF_MIN_MAX( long ) +DEF_MIN_MAX( unsigned long ) +DEF_MIN_MAX( float ) +DEF_MIN_MAX( double ) + +#undef DEF_MIN_MAX + +/* +// using const references generates crappy code, and I am currenly only using these +// for built-in types, so they take arguments by value + +// TODO: remove +inline int min( int x, int y ) +template +inline T min( T x, T y ) +{ + if ( x < y ) + return x; + return y; +} + +template +inline T max( T x, T y ) +{ + if ( x < y ) + return y; + return x; +} +*/ + +// TODO: good idea? bad idea? +#undef byte +#define byte byte_ +typedef unsigned char byte; + +// Setup compiler defines useful for exporting required public API symbols in gme.cpp +#ifndef BLARGG_EXPORT + #if defined (_WIN32) && defined(BLARGG_BUILD_DLL) + #define BLARGG_EXPORT __declspec(dllexport) + #elif defined (LIBGME_VISIBILITY) + #define BLARGG_EXPORT __attribute__((visibility ("default"))) + #else + #define BLARGG_EXPORT + #endif +#endif + +// deprecated +#define BLARGG_CHECK_ALLOC CHECK_ALLOC +#define BLARGG_RETURN_ERR RETURN_ERR + +// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of debug_printf and check +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif diff --git a/thirdparty/game-music-emu/gme/gb_cpu_io.h b/thirdparty/game-music-emu/gme/gb_cpu_io.h new file mode 100644 index 000000000..8bd69aa2d --- /dev/null +++ b/thirdparty/game-music-emu/gme/gb_cpu_io.h @@ -0,0 +1,72 @@ + +#include "Gbs_Emu.h" + +#include "blargg_source.h" + +int Gbs_Emu::cpu_read( gb_addr_t addr ) +{ + int result = *cpu::get_code( addr ); + if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count ) + result = apu.read_register( clock(), addr ); +#ifndef NDEBUG + else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 ) + debug_printf( "Read from unmapped memory $%.4x\n", (unsigned) addr ); + else if ( unsigned (addr - 0xFF01) < 0xFF80 - 0xFF01 ) + debug_printf( "Unhandled I/O read 0x%4X\n", (unsigned) addr ); +#endif + return result; +} + +void Gbs_Emu::cpu_write( gb_addr_t addr, int data ) +{ + unsigned offset = addr - ram_addr; + if ( offset <= 0xFFFF - ram_addr ) + { + ram [offset] = data; + if ( (addr ^ 0xE000) <= 0x1F80 - 1 ) + { + if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count ) + { + GME_APU_HOOK( this, addr - Gb_Apu::start_addr, data ); + apu.write_register( clock(), addr, data ); + } + else if ( (addr ^ 0xFF06) < 2 ) + update_timer(); + else if ( addr == joypad_addr ) + ram [offset] = 0; // keep joypad return value 0 + else + ram [offset] = 0xFF; + + //if ( addr == 0xFFFF ) + // debug_printf( "Wrote interrupt mask\n" ); + } + } + else if ( (addr ^ 0x2000) <= 0x2000 - 1 ) + { + set_bank( data ); + } +#ifndef NDEBUG + else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 ) + { + debug_printf( "Wrote to unmapped memory $%.4x\n", (unsigned) addr ); + } +#endif +} + +#define CPU_READ_FAST( cpu, addr, time, out ) \ + CPU_READ_FAST_( STATIC_CAST(Gbs_Emu*,cpu), addr, time, out ) + +#define CPU_READ_FAST_( emu, addr, time, out ) \ +{\ + out = READ_PROG( addr );\ + if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count )\ + out = emu->apu.read_register( emu->cpu_time - time * clocks_per_instr, addr );\ + else\ + check( out == emu->cpu_read( addr ) );\ +} + +#define CPU_READ( cpu, addr, time ) \ + STATIC_CAST(Gbs_Emu*,cpu)->cpu_read( addr ) + +#define CPU_WRITE( cpu, addr, data, time ) \ + STATIC_CAST(Gbs_Emu*,cpu)->cpu_write( addr, data ) diff --git a/thirdparty/game-music-emu/gme/gme.cpp b/thirdparty/game-music-emu/gme/gme.cpp new file mode 100644 index 000000000..8558e0904 --- /dev/null +++ b/thirdparty/game-music-emu/gme/gme.cpp @@ -0,0 +1,420 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Music_Emu.h" + +#include "gme_types.h" +#if !GME_DISABLE_STEREO_DEPTH +#include "Effects_Buffer.h" +#endif +#include "blargg_endian.h" +#include +#include + +/* Copyright (C) 2003-2006 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +BLARGG_EXPORT gme_type_t const* gme_type_list() +{ + static gme_type_t const gme_type_list_ [] = { +#ifdef GME_TYPE_LIST + GME_TYPE_LIST, +#else + #ifdef USE_GME_AY + gme_ay_type, + #endif + #ifdef USE_GME_GBS + gme_gbs_type, + #endif + #ifdef USE_GME_GYM + gme_gym_type, + #endif + #ifdef USE_GME_HES + gme_hes_type, + #endif + #ifdef USE_GME_KSS + gme_kss_type, + #endif + #ifdef USE_GME_NSF + gme_nsf_type, + #endif + #ifdef USE_GME_NSFE + gme_nsfe_type, + #endif + #ifdef USE_GME_SAP + gme_sap_type, + #endif + #ifdef USE_GME_SPC + gme_spc_type, + #endif + #ifdef USE_GME_VGM + gme_vgm_type, + gme_vgz_type, + #endif +#endif + 0 + }; + + return gme_type_list_; +} + +BLARGG_EXPORT const char* gme_identify_header( void const* header ) +{ + switch ( get_be32( header ) ) + { + case BLARGG_4CHAR('Z','X','A','Y'): return "AY"; + case BLARGG_4CHAR('G','B','S',0x01): return "GBS"; + case BLARGG_4CHAR('G','Y','M','X'): return "GYM"; + case BLARGG_4CHAR('H','E','S','M'): return "HES"; + case BLARGG_4CHAR('K','S','C','C'): + case BLARGG_4CHAR('K','S','S','X'): return "KSS"; + case BLARGG_4CHAR('N','E','S','M'): return "NSF"; + case BLARGG_4CHAR('N','S','F','E'): return "NSFE"; + case BLARGG_4CHAR('S','A','P',0x0D): return "SAP"; + case BLARGG_4CHAR('S','N','E','S'): return "SPC"; + case BLARGG_4CHAR('V','g','m',' '): return "VGM"; + } + if (get_be16(header) == BLARGG_2CHAR(0x1F, 0x8B)) + return "VGZ"; + return ""; +} + +static void to_uppercase( const char* in, int len, char* out ) +{ + for ( int i = 0; i < len; i++ ) + { + if ( !(out [i] = toupper( in [i] )) ) + return; + } + *out = 0; // extension too long +} + +BLARGG_EXPORT gme_type_t gme_identify_extension( const char* extension_ ) +{ + char const* end = strrchr( extension_, '.' ); + if ( end ) + extension_ = end + 1; + + char extension [6]; + to_uppercase( extension_, sizeof extension, extension ); + + for ( gme_type_t const* types = gme_type_list(); *types; types++ ) + if ( !strcmp( extension, (*types)->extension_ ) ) + return *types; + return 0; +} + +BLARGG_EXPORT const char *gme_type_extension( gme_type_t music_type ) +{ + const gme_type_t_ *const music_typeinfo = static_cast( music_type ); + if ( music_type ) + return music_typeinfo->extension_; + return ""; +} + +BLARGG_EXPORT gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ) +{ + *type_out = gme_identify_extension( path ); + // TODO: don't examine header if file has extension? + if ( !*type_out ) + { + char header [4]; + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + RETURN_ERR( in.read( header, sizeof header ) ); + *type_out = gme_identify_extension( gme_identify_header( header ) ); + } + return 0; +} + +BLARGG_EXPORT gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, int sample_rate ) +{ + require( (data || !size) && out ); + *out = 0; + + gme_type_t file_type = 0; + if ( size >= 4 ) + file_type = gme_identify_extension( gme_identify_header( data ) ); + if ( !file_type ) + return gme_wrong_file_type; + + Music_Emu* emu = gme_new_emu( file_type, sample_rate ); + CHECK_ALLOC( emu ); + + gme_err_t err = gme_load_data( emu, data, size ); + + if ( err ) + delete emu; + else + *out = emu; + + return err; +} + +BLARGG_EXPORT gme_err_t gme_open_file( const char* path, Music_Emu** out, int sample_rate ) +{ + require( path && out ); + *out = 0; + + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + + char header [4]; + int header_size = 0; + + gme_type_t file_type = gme_identify_extension( path ); + if ( !file_type ) + { + header_size = sizeof header; + RETURN_ERR( in.read( header, sizeof header ) ); + file_type = gme_identify_extension( gme_identify_header( header ) ); + } + if ( !file_type ) + return gme_wrong_file_type; + + Music_Emu* emu = gme_new_emu( file_type, sample_rate ); + CHECK_ALLOC( emu ); + + // optimization: avoids seeking/re-reading header + Remaining_Reader rem( header, header_size, &in ); + gme_err_t err = emu->load( rem ); + in.close(); + + if ( err ) + delete emu; + else + *out = emu; + + return err; +} + +BLARGG_EXPORT void gme_set_autoload_playback_limit( Music_Emu *emu, int do_autoload_limit ) +{ + emu->set_autoload_playback_limit( do_autoload_limit != 0 ); +} + +BLARGG_EXPORT int gme_autoload_playback_limit( Music_Emu *const emu ) +{ + return emu->autoload_playback_limit(); +} + +// Used to implement gme_new_emu and gme_new_emu_multi_channel +Music_Emu* gme_internal_new_emu_( gme_type_t type, int rate, bool multi_channel ) +{ + if ( type ) + { + if ( rate == gme_info_only ) + return type->new_info(); + + Music_Emu* me = type->new_emu(); + if ( me ) + { + #if !GME_DISABLE_STEREO_DEPTH + me->set_multi_channel( multi_channel ); + + if ( type->flags_ & 1 ) + { + if ( me->multi_channel() ) + { + me->effects_buffer = BLARGG_NEW Effects_Buffer(8); + } + else + { + me->effects_buffer = BLARGG_NEW Effects_Buffer(1); + } + if ( me->effects_buffer ) + me->set_buffer( me->effects_buffer ); + } + + if ( !(type->flags_ & 1) || me->effects_buffer ) + #endif + { + if ( !me->set_sample_rate( rate ) ) + { + check( me->type() == type ); + return me; + } + } + delete me; + } + } + return 0; +} + +BLARGG_EXPORT Music_Emu* gme_new_emu( gme_type_t type, int rate ) +{ + return gme_internal_new_emu_( type, rate, false /* no multichannel */); +} + +BLARGG_EXPORT Music_Emu* gme_new_emu_multi_channel( gme_type_t type, int rate ) +{ + // multi-channel emulator (if possible, not all emu types support multi-channel) + return gme_internal_new_emu_( type, rate, true /* multichannel */); +} + +BLARGG_EXPORT gme_err_t gme_load_file( Music_Emu* me, const char* path ) { return me->load_file( path ); } + +BLARGG_EXPORT gme_err_t gme_load_data( Music_Emu* me, void const* data, long size ) +{ + Mem_File_Reader in( data, size ); + return me->load( in ); +} + +BLARGG_EXPORT gme_err_t gme_load_custom( Music_Emu* me, gme_reader_t func, long size, void* data ) +{ + Callback_Reader in( func, size, data ); + return me->load( in ); +} + +BLARGG_EXPORT void gme_delete( Music_Emu* me ) { delete me; } + +BLARGG_EXPORT gme_type_t gme_type( Music_Emu const* me ) { return me->type(); } + +BLARGG_EXPORT const char* gme_warning( Music_Emu* me ) { return me->warning(); } + +BLARGG_EXPORT int gme_track_count( Music_Emu const* me ) { return me->track_count(); } + +struct gme_info_t_ : gme_info_t +{ + track_info_t info; + + BLARGG_DISABLE_NOTHROW +}; + +BLARGG_EXPORT gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, int track ) +{ + *out = NULL; + + gme_info_t_* info = BLARGG_NEW gme_info_t_; + CHECK_ALLOC( info ); + + gme_err_t err = me->track_info( &info->info, track ); + if ( err ) + { + gme_free_info( info ); + return err; + } + + #define COPY(name) info->name = info->info.name; + + COPY( length ); + COPY( intro_length ); + COPY( loop_length ); + + info->i4 = -1; + info->i5 = -1; + info->i6 = -1; + info->i7 = -1; + info->i8 = -1; + info->i9 = -1; + info->i10 = -1; + info->i11 = -1; + info->i12 = -1; + info->i13 = -1; + info->i14 = -1; + info->i15 = -1; + + info->s7 = ""; + info->s8 = ""; + info->s9 = ""; + info->s10 = ""; + info->s11 = ""; + info->s12 = ""; + info->s13 = ""; + info->s14 = ""; + info->s15 = ""; + + COPY( system ); + COPY( game ); + COPY( song ); + COPY( author ); + COPY( copyright ); + COPY( comment ); + COPY( dumper ); + + #undef COPY + + info->play_length = info->length; + if ( info->play_length <= 0 ) + { + info->play_length = info->intro_length + 2 * info->loop_length; // intro + 2 loops + if ( info->play_length <= 0 ) + info->play_length = 150 * 1000; // 2.5 minutes + } + + *out = info; + + return 0; +} + +BLARGG_EXPORT void gme_free_info( gme_info_t* info ) +{ + delete STATIC_CAST(gme_info_t_*,info); +} + +BLARGG_EXPORT void gme_set_stereo_depth( Music_Emu* me, double depth ) +{ +#if !GME_DISABLE_STEREO_DEPTH + if ( me->effects_buffer ) + STATIC_CAST(Effects_Buffer*,me->effects_buffer)->set_depth( depth ); +#endif +} + +BLARGG_EXPORT void* gme_user_data ( Music_Emu const* me ) { return me->user_data(); } +BLARGG_EXPORT void gme_set_user_data ( Music_Emu* me, void* new_user_data ) { me->set_user_data( new_user_data ); } +BLARGG_EXPORT void gme_set_user_cleanup(Music_Emu* me, gme_user_cleanup_t func ) { me->set_user_cleanup( func ); } + +BLARGG_EXPORT gme_err_t gme_start_track ( Music_Emu* me, int index ) { return me->start_track( index ); } +BLARGG_EXPORT gme_err_t gme_play ( Music_Emu* me, int n, short* p ) { return me->play( n, p ); } +BLARGG_EXPORT void gme_set_fade ( Music_Emu* me, int start_msec ) { me->set_fade( start_msec ); } +BLARGG_EXPORT int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); } +BLARGG_EXPORT int gme_tell ( Music_Emu const* me ) { return me->tell(); } +BLARGG_EXPORT int gme_tell_samples ( Music_Emu const* me ) { return me->tell_samples(); } +BLARGG_EXPORT gme_err_t gme_seek ( Music_Emu* me, int msec ) { return me->seek( msec ); } +BLARGG_EXPORT gme_err_t gme_seek_samples ( Music_Emu* me, int n ) { return me->seek_samples( n ); } +BLARGG_EXPORT int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); } +BLARGG_EXPORT void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); } +BLARGG_EXPORT void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); } +BLARGG_EXPORT void gme_mute_voice ( Music_Emu* me, int index, int mute ) { me->mute_voice( index, mute != 0 ); } +BLARGG_EXPORT void gme_mute_voices ( Music_Emu* me, int mask ) { me->mute_voices( mask ); } +BLARGG_EXPORT void gme_enable_accuracy( Music_Emu* me, int enabled ) { me->enable_accuracy( enabled ); } +BLARGG_EXPORT void gme_clear_playlist ( Music_Emu* me ) { me->clear_playlist(); } +BLARGG_EXPORT int gme_type_multitrack( gme_type_t t ) { return t->track_count != 1; } +BLARGG_EXPORT int gme_multi_channel ( Music_Emu const* me ) { return me->multi_channel(); } + +BLARGG_EXPORT void gme_set_equalizer ( Music_Emu* me, gme_equalizer_t const* eq ) +{ + Music_Emu::equalizer_t e = me->equalizer(); + e.treble = eq->treble; + e.bass = eq->bass; + me->set_equalizer( e ); +} + +BLARGG_EXPORT void gme_equalizer( Music_Emu const* me, gme_equalizer_t* out ) +{ + gme_equalizer_t e = gme_equalizer_t(); // Default-init all fields to 0.0f + e.treble = me->equalizer().treble; + e.bass = me->equalizer().bass; + *out = e; +} + +BLARGG_EXPORT const char* gme_voice_name( Music_Emu const* me, int i ) +{ + assert( (unsigned) i < (unsigned) me->voice_count() ); + return me->voice_names() [i]; +} + +BLARGG_EXPORT const char* gme_type_system( gme_type_t type ) +{ + assert( type ); + return type->system; +} diff --git a/thirdparty/game-music-emu/gme/gme.h b/thirdparty/game-music-emu/gme/gme.h new file mode 100644 index 000000000..36c098343 --- /dev/null +++ b/thirdparty/game-music-emu/gme/gme.h @@ -0,0 +1,286 @@ +/* Game music emulator library C interface (also usable from C++) */ + +/* Game_Music_Emu 0.6.2 */ +#ifndef GME_H +#define GME_H + +#ifdef __cplusplus + extern "C" { +#endif + +#define GME_VERSION 0x000602 /* 1 byte major, 1 byte minor, 1 byte patch-level */ + +/* Error string returned by library functions, or NULL if no error (success) */ +typedef const char* gme_err_t; + +/* First parameter of most gme_ functions is a pointer to the Music_Emu */ +typedef struct Music_Emu Music_Emu; + + +/******** Basic operations ********/ + +/* Create emulator and load game music file/data into it. Sets *out to new emulator. */ +gme_err_t gme_open_file( const char path [], Music_Emu** out, int sample_rate ); + +/* Number of tracks available */ +int gme_track_count( Music_Emu const* ); + +/* Start a track, where 0 is the first track */ +gme_err_t gme_start_track( Music_Emu*, int index ); + +/* Generate 'count' 16-bit signed samples info 'out'. Output is in stereo. */ +gme_err_t gme_play( Music_Emu*, int count, short out [] ); + +/* Finish using emulator and free memory */ +void gme_delete( Music_Emu* ); + + +/******** Track position/length ********/ + +/* Set time to start fading track out. Once fade ends track_ended() returns true. +Fade time can be changed while track is playing. */ +void gme_set_fade( Music_Emu*, int start_msec ); + +/** + * If do_autoload_limit is nonzero, then automatically load track length + * metadata (if present) and terminate playback once the track length has been + * reached. Otherwise playback will continue for an arbitrary period of time + * until a prolonged period of silence is detected. + * + * Not all individual emulators support this setting. + * + * By default, playback limits are loaded and applied. + * + * @since 0.6.2 + */ +void gme_set_autoload_playback_limit( Music_Emu *, int do_autoload_limit ); + +/** See gme_set_autoload_playback_limit. + * @since 0.6.2 + */ +int gme_autoload_playback_limit( Music_Emu const* ); + +/* True if a track has reached its end */ +int gme_track_ended( Music_Emu const* ); + +/* Number of milliseconds (1000 = one second) played since beginning of track */ +int gme_tell( Music_Emu const* ); + +/* Number of samples generated since beginning of track */ +int gme_tell_samples( Music_Emu const* ); + +/* Seek to new time in track. Seeking backwards or far forward can take a while. */ +gme_err_t gme_seek( Music_Emu*, int msec ); + +/* Equivalent to restarting track then skipping n samples */ +gme_err_t gme_seek_samples( Music_Emu*, int n ); + + +/******** Informational ********/ + +/* If you only need track information from a music file, pass gme_info_only for +sample_rate to open/load. */ +enum { gme_info_only = -1 }; + +/* Most recent warning string, or NULL if none. Clears current warning after returning. +Warning is also cleared when loading a file and starting a track. */ +const char* gme_warning( Music_Emu* ); + +/* Load m3u playlist file (must be done after loading music) */ +gme_err_t gme_load_m3u( Music_Emu*, const char path [] ); + +/* Clear any loaded m3u playlist and any internal playlist that the music format +supports (NSFE for example). */ +void gme_clear_playlist( Music_Emu* ); + +/* Gets information for a particular track (length, name, author, etc.). +Must be freed after use. */ +typedef struct gme_info_t gme_info_t; +gme_err_t gme_track_info( Music_Emu const*, gme_info_t** out, int track ); + +/* Frees track information */ +void gme_free_info( gme_info_t* ); + +struct gme_info_t +{ + /* times in milliseconds; -1 if unknown */ + int length; /* total length, if file specifies it */ + int intro_length; /* length of song up to looping section */ + int loop_length; /* length of looping section */ + + /* Length if available, otherwise intro_length+loop_length*2 if available, + otherwise a default of 150000 (2.5 minutes). */ + int play_length; + + int i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15; /* reserved */ + + /* empty string ("") if not available */ + const char* system; + const char* game; + const char* song; + const char* author; + const char* copyright; + const char* comment; + const char* dumper; + + const char *s7,*s8,*s9,*s10,*s11,*s12,*s13,*s14,*s15; /* reserved */ +}; + + +/******** Advanced playback ********/ + +/* Adjust stereo echo depth, where 0.0 = off and 1.0 = maximum. Has no effect for +GYM, SPC, and Sega Genesis VGM music */ +void gme_set_stereo_depth( Music_Emu*, double depth ); + +/* Disable automatic end-of-track detection and skipping of silence at beginning +if ignore is true */ +void gme_ignore_silence( Music_Emu*, int ignore ); + +/* Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. +Track length as returned by track_info() assumes a tempo of 1.0. */ +void gme_set_tempo( Music_Emu*, double tempo ); + +/* Number of voices used by currently loaded file */ +int gme_voice_count( Music_Emu const* ); + +/* Name of voice i, from 0 to gme_voice_count() - 1 */ +const char* gme_voice_name( Music_Emu const*, int i ); + +/* Mute/unmute voice i, where voice 0 is first voice */ +void gme_mute_voice( Music_Emu*, int index, int mute ); + +/* Set muting state of all voices at once using a bit mask, where -1 mutes all +voices, 0 unmutes them all, 0x01 mutes just the first voice, etc. */ +void gme_mute_voices( Music_Emu*, int muting_mask ); + +/* Frequency equalizer parameters (see gme.txt) */ +/* Implementers: If modified, also adjust Music_Emu::make_equalizer as needed */ +typedef struct gme_equalizer_t +{ + double treble; /* -50.0 = muffled, 0 = flat, +5.0 = extra-crisp */ + double bass; /* 1 = full bass, 90 = average, 16000 = almost no bass */ + + double d2,d3,d4,d5,d6,d7,d8,d9; /* reserved */ +} gme_equalizer_t; + +/* Get current frequency equalizater parameters */ +void gme_equalizer( Music_Emu const*, gme_equalizer_t* out ); + +/* Change frequency equalizer parameters */ +void gme_set_equalizer( Music_Emu*, gme_equalizer_t const* eq ); + +/* Enables/disables most accurate sound emulation options */ +void gme_enable_accuracy( Music_Emu*, int enabled ); + + +/******** Game music types ********/ + +/* Music file type identifier. Can also hold NULL. */ +typedef const struct gme_type_t_* gme_type_t; + +/* Emulator type constants for each supported file type */ +extern const gme_type_t + gme_ay_type, + gme_gbs_type, + gme_gym_type, + gme_hes_type, + gme_kss_type, + gme_nsf_type, + gme_nsfe_type, + gme_sap_type, + gme_spc_type, + gme_vgm_type, + gme_vgz_type; + +/* Type of this emulator */ +gme_type_t gme_type( Music_Emu const* ); + +/* Pointer to array of all music types, with NULL entry at end. Allows a player linked +to this library to support new music types without having to be updated. */ +gme_type_t const* gme_type_list(); + +/* Name of game system for this music file type */ +const char* gme_type_system( gme_type_t ); + +/* True if this music file type supports multiple tracks */ +int gme_type_multitrack( gme_type_t ); + +/* whether the pcm output retrieved by gme_play() will have all 8 voices rendered to their + * individual stereo channel or (if false) these voices get mixed into one single stereo channel + * @since 0.6.2 */ +int gme_multi_channel( Music_Emu const* ); + +/******** Advanced file loading ********/ + +/* Error returned if file type is not supported */ +extern const char* const gme_wrong_file_type; + +/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. + * The resulting Music_Emu object will be set to single channel mode. */ +gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, int sample_rate ); + +/* Determine likely game music type based on first four bytes of file. Returns +string containing proper file suffix (i.e. "NSF", "SPC", etc.) or "" if +file header is not recognized. */ +const char* gme_identify_header( void const* header ); + +/* Get corresponding music type for file path or extension passed in. */ +gme_type_t gme_identify_extension( const char path_or_extension [] ); + +/** + * Get typical file extension for a given music type. This is not a replacement + * for a file content identification library (but see gme_identify_header). + * + * @since 0.6.2 + */ +const char* gme_type_extension( gme_type_t music_type ); + +/* Determine file type based on file's extension or header (if extension isn't recognized). +Sets *type_out to type, or 0 if unrecognized or error. */ +gme_err_t gme_identify_file( const char path [], gme_type_t* type_out ); + +/* Create new emulator and set sample rate. Returns NULL if out of memory. If you only need +track information, pass gme_info_only for sample_rate. */ +Music_Emu* gme_new_emu( gme_type_t, int sample_rate ); + +/* Create new multichannel emulator and set sample rate. Returns NULL if out of memory. + * If you only need track information, pass gme_info_only for sample_rate. + * (see gme_multi_channel for more information on multichannel support) + * @since 0.6.2 + */ +Music_Emu* gme_new_emu_multi_channel( gme_type_t, int sample_rate ); + +/* Load music file into emulator */ +gme_err_t gme_load_file( Music_Emu*, const char path [] ); + +/* Load music file from memory into emulator. Makes a copy of data passed. */ +gme_err_t gme_load_data( Music_Emu*, void const* data, long size ); + +/* Load music file using custom data reader function that will be called to +read file data. Most emulators load the entire file in one read call. */ +typedef gme_err_t (*gme_reader_t)( void* your_data, void* out, int count ); +gme_err_t gme_load_custom( Music_Emu*, gme_reader_t, long file_size, void* your_data ); + +/* Load m3u playlist file from memory (must be done after loading music) */ +gme_err_t gme_load_m3u_data( Music_Emu*, void const* data, long size ); + + +/******** User data ********/ + +/* Set/get pointer to data you want to associate with this emulator. +You can use this for whatever you want. */ +void gme_set_user_data( Music_Emu*, void* new_user_data ); +void* gme_user_data( Music_Emu const* ); + +/* Register cleanup function to be called when deleting emulator, or NULL to +clear it. Passes user_data to cleanup function. */ +typedef void (*gme_user_cleanup_t)( void* user_data ); +void gme_set_user_cleanup( Music_Emu*, gme_user_cleanup_t func ); + + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/thirdparty/game-music-emu/gme/gme_types.h b/thirdparty/game-music-emu/gme/gme_types.h new file mode 100644 index 000000000..06226f4aa --- /dev/null +++ b/thirdparty/game-music-emu/gme/gme_types.h @@ -0,0 +1,21 @@ +#ifndef GME_TYPES_H +#define GME_TYPES_H + +/* + * This is a default gme_types.h for use when *not* using + * CMake. If CMake is in use gme_types.h.in will be + * processed instead. + */ +#define USE_GME_AY +#define USE_GME_GBS +#define USE_GME_GYM +#define USE_GME_HES +#define USE_GME_KSS +#define USE_GME_NSF +#define USE_GME_NSFE +#define USE_GME_SAP +#define USE_GME_SPC +/* VGM and VGZ are a package deal */ +#define USE_GME_VGM + +#endif /* GME_TYPES_H */ diff --git a/thirdparty/game-music-emu/gme/gme_types.h.in b/thirdparty/game-music-emu/gme/gme_types.h.in new file mode 100644 index 000000000..4829b3e16 --- /dev/null +++ b/thirdparty/game-music-emu/gme/gme_types.h.in @@ -0,0 +1,23 @@ +#ifndef GME_TYPES_H +#define GME_TYPES_H + +/* CMake will either define the following to 1, or #undef it, + * depending on the options passed to CMake. This is used to + * conditionally compile in the various emulator types. + * + * See gme_type_list() in gme.cpp + */ + +#cmakedefine USE_GME_AY +#cmakedefine USE_GME_GBS +#cmakedefine USE_GME_GYM +#cmakedefine USE_GME_HES +#cmakedefine USE_GME_KSS +#cmakedefine USE_GME_NSF +#cmakedefine USE_GME_NSFE +#cmakedefine USE_GME_SAP +#cmakedefine USE_GME_SPC +/* VGM and VGZ are a package deal */ +#cmakedefine USE_GME_VGM + +#endif /* GME_TYPES_H */ diff --git a/thirdparty/game-music-emu/gme/hes_cpu_io.h b/thirdparty/game-music-emu/gme/hes_cpu_io.h new file mode 100644 index 000000000..ce60ce8ef --- /dev/null +++ b/thirdparty/game-music-emu/gme/hes_cpu_io.h @@ -0,0 +1,101 @@ + +#include "Hes_Emu.h" + +#include "blargg_source.h" + +int Hes_Emu::cpu_read( hes_addr_t addr ) +{ + check( addr <= 0xFFFF ); + int result = *cpu::get_code( addr ); + if ( mmr [addr >> page_shift] == 0xFF ) + result = cpu_read_( addr ); + return result; +} + +void Hes_Emu::cpu_write( hes_addr_t addr, int data ) +{ + check( addr <= 0xFFFF ); + byte* out = write_pages [addr >> page_shift]; + addr &= page_size - 1; + if ( out ) + out [addr] = data; + else if ( mmr [addr >> page_shift] == 0xFF ) + cpu_write_( addr, data ); +} + +inline byte const* Hes_Emu::cpu_set_mmr( int page, int bank ) +{ + write_pages [page] = 0; + if ( bank < 0x80 ) + return rom.at_addr( bank * (blargg_long) page_size ); + + byte* data = 0; + switch ( bank ) + { + case 0xF8: + data = cpu::ram; + break; + + case 0xF9: + case 0xFA: + case 0xFB: + data = &sgx [(bank - 0xF9) * page_size]; + break; + + default: + if ( bank != 0xFF ) + debug_printf( "Unmapped bank $%02X\n", bank ); + return rom.unmapped(); + } + + write_pages [page] = data; + return data; +} + +#define CPU_READ_FAST( cpu, addr, time, out ) \ + CPU_READ_FAST_( STATIC_CAST(Hes_Emu*,cpu), addr, time, out ) + +#define CPU_READ_FAST_( cpu, addr, time, out ) \ +{\ + out = READ_PROG( addr );\ + if ( mmr [addr >> page_shift] == 0xFF )\ + {\ + FLUSH_TIME();\ + out = cpu->cpu_read_( addr );\ + CACHE_TIME();\ + }\ +} + +#define CPU_WRITE_FAST( cpu, addr, data, time ) \ + CPU_WRITE_FAST_( STATIC_CAST(Hes_Emu*,cpu), addr, data, time ) + +#define CPU_WRITE_FAST_( cpu, addr, data, time ) \ +{\ + byte* out = cpu->write_pages [addr >> page_shift];\ + addr &= page_size - 1;\ + if ( out )\ + {\ + out [addr] = data;\ + }\ + else if ( mmr [addr >> page_shift] == 0xFF )\ + {\ + FLUSH_TIME();\ + cpu->cpu_write_( addr, data );\ + CACHE_TIME();\ + }\ +} + +#define CPU_READ( cpu, addr, time ) \ + STATIC_CAST(Hes_Emu*,cpu)->cpu_read( addr ) + +#define CPU_WRITE( cpu, addr, data, time ) \ + STATIC_CAST(Hes_Emu*,cpu)->cpu_write( addr, data ) + +#define CPU_WRITE_VDP( cpu, addr, data, time ) \ + STATIC_CAST(Hes_Emu*,cpu)->cpu_write_vdp( addr, data ) + +#define CPU_SET_MMR( cpu, page, bank ) \ + STATIC_CAST(Hes_Emu*,cpu)->cpu_set_mmr( page, bank ) + +#define CPU_DONE( cpu, time, result_out ) \ + result_out = STATIC_CAST(Hes_Emu*,cpu)->cpu_done() diff --git a/thirdparty/game-music-emu/gme/libgme.pc.in b/thirdparty/game-music-emu/gme/libgme.pc.in new file mode 100644 index 000000000..f057ce17c --- /dev/null +++ b/thirdparty/game-music-emu/gme/libgme.pc.in @@ -0,0 +1,16 @@ +# entries grouped with CMake are expanded by CMake +# ${foo} entries are left alone by CMake and much +# later are used by pkg-config. +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +lib_suffix=@LIB_SUFFIX@ +libdir=${exec_prefix}/lib${lib_suffix} +includedir=${prefix}/include + +Name: Game_Music_Emu +Description: A video game emulation library for music. +URL: https://bitbucket.org/mpyne/game-music-emu/wiki/Home +Version: @GME_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lgme +Libs.private: -lstdc++ @PKG_CONFIG_ZLIB@ diff --git a/thirdparty/game-music-emu/gme/nes_cpu_io.h b/thirdparty/game-music-emu/gme/nes_cpu_io.h new file mode 100644 index 000000000..68ce9b6ff --- /dev/null +++ b/thirdparty/game-music-emu/gme/nes_cpu_io.h @@ -0,0 +1,83 @@ + +#include "Nsf_Emu.h" + +#if !NSF_EMU_APU_ONLY + #include "Nes_Namco_Apu.h" +#endif + +#include "blargg_source.h" + +int Nsf_Emu::cpu_read( nes_addr_t addr ) +{ + int result; + + result = cpu::low_mem [addr & 0x7FF]; + if ( !(addr & 0xE000) ) + goto exit; + + result = *cpu::get_code( addr ); + if ( addr > 0x7FFF ) + goto exit; + + result = sram [addr & (sizeof sram - 1)]; + if ( addr > 0x5FFF ) + goto exit; + + if ( addr == Nes_Apu::status_addr ) + return apu.read_status( cpu::time() ); + + #if !NSF_EMU_APU_ONLY + if ( addr == Nes_Namco_Apu::data_reg_addr && namco ) + return namco->read_data(); + #endif + + result = addr >> 8; // simulate open bus + + if ( addr != 0x2002 ) + debug_printf( "Read unmapped $%.4X\n", (unsigned) addr ); + +exit: + return result; +} + +void Nsf_Emu::cpu_write( nes_addr_t addr, int data ) +{ + { + nes_addr_t offset = addr ^ sram_addr; + if ( offset < sizeof sram ) + { + sram [offset] = data; + return; + } + } + { + int temp = addr & 0x7FF; + if ( !(addr & 0xE000) ) + { + cpu::low_mem [temp] = data; + return; + } + } + + if ( unsigned (addr - Nes_Apu::start_addr) <= Nes_Apu::end_addr - Nes_Apu::start_addr ) + { + GME_APU_HOOK( this, addr - Nes_Apu::start_addr, data ); + apu.write_register( cpu::time(), addr, data ); + return; + } + + unsigned bank = addr - bank_select_addr; + if ( bank < bank_count ) + { + blargg_long offset = rom.mask_addr( data * (blargg_long) bank_size ); + if ( offset >= rom.size() ) + set_warning( "Invalid bank" ); + cpu::map_code( (bank + 8) * bank_size, bank_size, rom.at_addr( offset ) ); + return; + } + + cpu_write_misc( addr, data ); +} + +#define CPU_READ( cpu, addr, time ) STATIC_CAST(Nsf_Emu&,*cpu).cpu_read( addr ) +#define CPU_WRITE( cpu, addr, data, time ) STATIC_CAST(Nsf_Emu&,*cpu).cpu_write( addr, data ) diff --git a/thirdparty/game-music-emu/gme/sap_cpu_io.h b/thirdparty/game-music-emu/gme/sap_cpu_io.h new file mode 100644 index 000000000..d009d0d9b --- /dev/null +++ b/thirdparty/game-music-emu/gme/sap_cpu_io.h @@ -0,0 +1,26 @@ + +#include "Sap_Emu.h" + +#include "blargg_source.h" + +#define CPU_WRITE( cpu, addr, data, time ) STATIC_CAST(Sap_Emu&,*cpu).cpu_write( addr, data ) + +void Sap_Emu::cpu_write( sap_addr_t addr, int data ) +{ + mem.ram [addr] = data; + if ( (addr >> 8) == 0xD2 ) + cpu_write_( addr, data ); +} + +#ifdef NDEBUG + #define CPU_READ( cpu, addr, time ) READ_LOW( addr ) +#else + #define CPU_READ( cpu, addr, time ) STATIC_CAST(Sap_Emu&,*cpu).cpu_read( addr ) + + int Sap_Emu::cpu_read( sap_addr_t addr ) + { + if ( (addr & 0xF900) == 0xD000 ) + debug_printf( "Unmapped read $%04X\n", addr ); + return mem.ram [addr]; + } +#endif diff --git a/thirdparty/game-music-emu/license.txt b/thirdparty/game-music-emu/license.txt new file mode 100644 index 000000000..5ab7695ab --- /dev/null +++ b/thirdparty/game-music-emu/license.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/thirdparty/game-music-emu/readme.txt b/thirdparty/game-music-emu/readme.txt new file mode 100644 index 000000000..22cc20aad --- /dev/null +++ b/thirdparty/game-music-emu/readme.txt @@ -0,0 +1,241 @@ +Game_Music_Emu 0.6.2: Game Music Emulators +------------------------------------------ +Game_Music_Emu is a collection of video game music file emulators that +support the following formats and systems: + +AY ZX Spectrum/Amstrad CPC +GBS Nintendo Game Boy +GYM Sega Genesis/Mega Drive +HES NEC TurboGrafx-16/PC Engine +KSS MSX Home Computer/other Z80 systems (doesn't support FM sound) +NSF/NSFE Nintendo NES/Famicom (with VRC 6, Namco 106, and FME-7 sound) +SAP Atari systems using POKEY sound chip +SPC Super Nintendo/Super Famicom +VGM/VGZ Sega Master System/Mark III, Sega Genesis/Mega Drive,BBC Micro + +Features: +* C interface for use in C, C++, and other compatible languages +* High emphasis has been placed on making the library very easy to use +* One set of common functions work with all emulators the same way +* Several code examples, including music player using SDL +* Portable code for use on any system with modern or older C++ compilers +* Adjustable output sample rate using quality band-limited resampling +* Uniform access to text information fields and track timing information +* End-of-track fading and automatic look ahead silence detection +* Treble/bass and stereo echo for AY/GBS/HES/KSS/NSF/NSFE/SAP/VGM +* Tempo can be adjusted and individual voices can be muted while playing +* Can read music data from file, memory, or custom reader function/class +* Can access track information without having to load into full emulator +* M3U track listing support for multi-track formats +* Modular design allows elimination of unneeded emulators/features + +This library has been used in game music players for Windows, Linux on +several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable, +GP2X, and Nintendo DS. + +Author : Shay Green +Website: https://bitbucket.org/mpyne/game-music-emu/wiki/Home +License: GNU Lesser General Public License (LGPL) + +Note: When you will use MAME YM2612 emulator, the license of library +will be GNU General Public License (GPL) v2.0+! + +Current Maintainer: Michael Pyne + +Getting Started +--------------- +Build a program consisting of demo/basics.c, demo/Wave_Writer.cpp, and +all source files in gme/. + +Or, if you have CMake 2.6 or later, execute at a command prompt (from the +extracted source directory): + + mkdir build + cd build + cmake ../ # <-- Pass any needed CMake flags here + make # To build the library + cd demo + make # To build the demo itself + +Be sure "test.nsf" is in the same directory as the demo program. Running it +should generate the recording "out.wav". + +You can use "make install" to install the library. To choose where to install +the library to, use the CMake argument "-DCMAKE_INSTALL_PREFIX=/usr/local" +(and replace /usr/local with the base path you wish to use). Alternately, you +can specify the base path to install to when you run "make install" by passing +'DESTDIR=/usr/local' on the make install command line (again, replace +/usr/local as appropriate). + +To build a static library instead of shared (the default), pass +-DBUILD_SHARED_LIBS=OFF to the cmake command when running cmake. + +A slightly more extensive demo application is available in the player/ +directory. It requires SDL to build. + +Read gme.txt for more information. Post to the discussion forum for +assistance. + +Files +----- +gme.txt General notes about the library +changes.txt Changes made since previous releases +design.txt Library design notes +license.txt GNU Lesser General Public License +CMakeLists.txt CMake build rules + +test.nsf Test file for NSF emulator +test.m3u Test m3u playlist for features.c demo + +demo/ + basics.c Records NSF file to wave sound file + features.c Demonstrates many additional features + Wave_Writer.h WAVE sound file writer used for demo output + Wave_Writer.cpp + CMakeLists.txt CMake build rules + +player/ Player using the SDL multimedia library + player.cpp Simple music player with waveform display + Music_Player.cpp Stand alone player for background music + Music_Player.h + Audio_Scope.cpp Audio waveform scope + Audio_Scope.h + CMakeLists.txt CMake build rules + +gme/ + blargg_config.h Library configuration (modify this file as needed) + + gme.h Library interface header file + gme.cpp + + Ay_Emu.h ZX Spectrum AY emulator + Ay_Emu.cpp + Ay_Apu.cpp + Ay_Apu.h + Ay_Cpu.cpp + Ay_Cpu.h + + Gbs_Emu.h Nintendo Game Boy GBS emulator + Gbs_Emu.cpp + Gb_Apu.cpp + Gb_Apu.h + Gb_Cpu.cpp + Gb_Cpu.h + gb_cpu_io.h + Gb_Oscs.cpp + Gb_Oscs.h + + Hes_Emu.h TurboGrafx-16/PC Engine HES emulator + Hes_Apu.cpp + Hes_Apu.h + Hes_Cpu.cpp + Hes_Cpu.h + hes_cpu_io.h + Hes_Emu.cpp + + Kss_Emu.h MSX Home Computer/other Z80 systems KSS emulator + Kss_Emu.cpp + Kss_Cpu.cpp + Kss_Cpu.h + Kss_Scc_Apu.cpp + Kss_Scc_Apu.h + Ay_Apu.h + Ay_Apu.cpp + Sms_Apu.h + Sms_Apu.cpp + Sms_Oscs.h + + Nsf_Emu.h Nintendo NES NSF/NSFE emulator + Nsf_Emu.cpp + Nes_Apu.cpp + Nes_Apu.h + Nes_Cpu.cpp + Nes_Cpu.h + nes_cpu_io.h + Nes_Oscs.cpp + Nes_Oscs.h + Nes_Fme7_Apu.cpp + Nes_Fme7_Apu.h + Nes_Namco_Apu.cpp + Nes_Namco_Apu.h + Nes_Vrc6_Apu.cpp + Nes_Vrc6_Apu.h + Nsfe_Emu.h NSFE support + Nsfe_Emu.cpp + + Spc_Emu.h Super Nintendo SPC emulator + Spc_Emu.cpp + Snes_Spc.cpp + Snes_Spc.h + Spc_Cpu.cpp + Spc_Cpu.h + Spc_Dsp.cpp + Spc_Dsp.h + Fir_Resampler.cpp + Fir_Resampler.h + + Sap_Emu.h Atari SAP emulator + Sap_Emu.cpp + Sap_Apu.cpp + Sap_Apu.h + Sap_Cpu.cpp + Sap_Cpu.h + sap_cpu_io.h + + Vgm_Emu.h Sega VGM emulator + Vgm_Emu_Impl.cpp + Vgm_Emu_Impl.h + Vgm_Emu.cpp + Ym2413_Emu.cpp + Ym2413_Emu.h + Gym_Emu.h Sega Genesis GYM emulator + Gym_Emu.cpp + Sms_Apu.cpp Common Sega emulator files + Sms_Apu.h + Sms_Oscs.h + Ym2612_Emu.h + Ym2612_GENS.cpp GENS 2.10 YM2612 emulator (LGPLv2.1+ license) + Ym2612_GENS.h + Ym2612_MAME.cpp MAME YM2612 emulator (GPLv2.0+ license) + Ym2612_MAME.h + Ym2612_Nuked.cpp Nuked OPN2 emulator (LGPLv2.1+ license) + Ym2612_Nuked.h + Dual_Resampler.cpp + Dual_Resampler.h + Fir_Resampler.cpp + Fir_Resampler.h + + M3u_Playlist.h M3U playlist support + M3u_Playlist.cpp + + Effects_Buffer.h Sound buffer with stereo echo and panning + Effects_Buffer.cpp + + blargg_common.h Common files needed by all emulators + blargg_endian.h + blargg_source.h + Blip_Buffer.cpp + Blip_Buffer.h + Gme_File.h + Gme_File.cpp + Music_Emu.h + Music_Emu.cpp + Classic_Emu.h + Classic_Emu.cpp + Multi_Buffer.h + Multi_Buffer.cpp + Data_Reader.h + Data_Reader.cpp + + CMakeLists.txt CMake build rules + + +Legal +----- +Game_Music_Emu library copyright (C) 2003-2009 Shay Green. +Sega Genesis YM2612 emulator copyright (C) 2002 Stephane Dallongeville. +MAME YM2612 emulator copyright (C) 2003 Jarek Burczynski, Tatsuyuki Satoh +Nuked OPN2 emulator copyright (C) 2017 Alexey Khokholov (Nuke.YKT) + +-- +Shay Green diff --git a/thirdparty/libxmp/CMakeLists.txt b/thirdparty/libxmp/CMakeLists.txt new file mode 100644 index 000000000..1567c3f37 --- /dev/null +++ b/thirdparty/libxmp/CMakeLists.txt @@ -0,0 +1,102 @@ +make_release_only() + +add_library(libxmp OBJECT + src/virtual.c + src/format.c + src/period.c + src/player.c + src/read_event.c + src/dataio.c + src/misc.c + src/mkstemp.c + src/md5.c + src/lfo.c + src/scan.c + src/control.c + src/far_extras.c + src/med_extras.c + src/filter.c + src/effects.c + src/mixer.c + src/mix_all.c + src/load_helpers.c + src/load.c + src/hio.c + src/hmn_extras.c + src/extras.c + src/smix.c + src/filetype.c + src/memio.c + src/tempfile.c + src/mix_paula.c + src/win32.c + src/loaders/common.c + src/loaders/iff.c + src/loaders/itsex.c + src/loaders/lzw.c + src/loaders/voltable.c + src/loaders/sample.c + src/loaders/vorbis.c + src/loaders/xm_load.c + src/loaders/mod_load.c + src/loaders/s3m_load.c + src/loaders/stm_load.c + src/loaders/669_load.c + src/loaders/far_load.c + src/loaders/mtm_load.c + src/loaders/ptm_load.c + src/loaders/okt_load.c + src/loaders/ult_load.c + src/loaders/mdl_load.c + src/loaders/it_load.c + src/loaders/stx_load.c + src/loaders/pt3_load.c + src/loaders/sfx_load.c + src/loaders/flt_load.c + src/loaders/st_load.c + src/loaders/emod_load.c + src/loaders/imf_load.c + src/loaders/digi_load.c + src/loaders/fnk_load.c + src/loaders/ice_load.c + src/loaders/liq_load.c + src/loaders/ims_load.c + src/loaders/masi_load.c + src/loaders/masi16_load.c + src/loaders/amf_load.c + src/loaders/stim_load.c + src/loaders/mmd_common.c + src/loaders/mmd1_load.c + src/loaders/mmd3_load.c + src/loaders/rtm_load.c + src/loaders/dt_load.c + src/loaders/no_load.c + src/loaders/arch_load.c + src/loaders/sym_load.c + src/loaders/med2_load.c + src/loaders/med3_load.c + src/loaders/med4_load.c + src/loaders/dbm_load.c + src/loaders/umx_load.c + src/loaders/gdm_load.c + src/loaders/pw_load.c + src/loaders/gal5_load.c + src/loaders/gal4_load.c + src/loaders/mfp_load.c + src/loaders/asylum_load.c + src/loaders/muse_load.c + src/loaders/hmn_load.c + src/loaders/mgt_load.c + src/loaders/chip_load.c + src/loaders/abk_load.c + src/loaders/coco_load.c + src/loaders/xmf_load.c +) + +use_fast_math(libxmp) + +target_include_directories(libxmp PRIVATE include ../miniz/) +target_compile_definitions(libxmp PRIVATE -DHAVE_POWF=1 -DLIBXMP_STATIC=1 -DLIBXMP_NO_PROWIZARD=1 -DLIBXMP_NO_DEPACKERS=1) +if (NOT WIN32) + target_compile_definitions(libxmp PRIVATE -DHAVE_DIRENT=1 -DHAVE_UNISTD=1) +endif () diff --git a/thirdparty/libxmp/include/xmp.h b/thirdparty/libxmp/include/xmp.h new file mode 100644 index 000000000..22db2a3da --- /dev/null +++ b/thirdparty/libxmp/include/xmp.h @@ -0,0 +1,407 @@ +#ifndef XMP_H +#define XMP_H + +#if defined(EMSCRIPTEN) +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define XMP_VERSION "4.6.0" +#define XMP_VERCODE 0x040600 +#define XMP_VER_MAJOR 4 +#define XMP_VER_MINOR 6 +#define XMP_VER_RELEASE 0 + +#if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(LIBXMP_STATIC) +# define LIBXMP_EXPORT +# elif defined(BUILDING_DLL) +# define LIBXMP_EXPORT __declspec(dllexport) +# else +# define LIBXMP_EXPORT __declspec(dllimport) +# endif +#elif defined(__OS2__) && defined(__WATCOMC__) +# if defined(LIBXMP_STATIC) +# define LIBXMP_EXPORT +# elif defined(BUILDING_DLL) +# define LIBXMP_EXPORT __declspec(dllexport) +# else +# define LIBXMP_EXPORT +# endif +#elif (defined(__GNUC__) || defined(__clang__) || defined(__HP_cc)) && defined(XMP_SYM_VISIBILITY) +# if defined(LIBXMP_STATIC) +# define LIBXMP_EXPORT +# else +# define LIBXMP_EXPORT __attribute__((visibility("default"))) +# endif +#elif defined(__SUNPRO_C) && defined(XMP_LDSCOPE_GLOBAL) +# if defined(LIBXMP_STATIC) +# define LIBXMP_EXPORT +# else +# define LIBXMP_EXPORT __global +# endif +#elif defined(EMSCRIPTEN) +# define LIBXMP_EXPORT EMSCRIPTEN_KEEPALIVE +# define LIBXMP_EXPORT_VAR +#else +# define LIBXMP_EXPORT +#endif + +#if !defined(LIBXMP_EXPORT_VAR) +# define LIBXMP_EXPORT_VAR LIBXMP_EXPORT +#endif + +#define XMP_NAME_SIZE 64 /* Size of module name and type */ + +#define XMP_KEY_OFF 0x81 /* Note number for key off event */ +#define XMP_KEY_CUT 0x82 /* Note number for key cut event */ +#define XMP_KEY_FADE 0x83 /* Note number for fade event */ + +/* mixer parameter macros */ + +/* sample format flags */ +#define XMP_FORMAT_8BIT (1 << 0) /* Mix to 8-bit instead of 16 */ +#define XMP_FORMAT_UNSIGNED (1 << 1) /* Mix to unsigned samples */ +#define XMP_FORMAT_MONO (1 << 2) /* Mix to mono instead of stereo */ + +/* player parameters */ +#define XMP_PLAYER_AMP 0 /* Amplification factor */ +#define XMP_PLAYER_MIX 1 /* Stereo mixing */ +#define XMP_PLAYER_INTERP 2 /* Interpolation type */ +#define XMP_PLAYER_DSP 3 /* DSP effect flags */ +#define XMP_PLAYER_FLAGS 4 /* Player flags */ +#define XMP_PLAYER_CFLAGS 5 /* Player flags for current module */ +#define XMP_PLAYER_SMPCTL 6 /* Sample control flags */ +#define XMP_PLAYER_VOLUME 7 /* Player module volume */ +#define XMP_PLAYER_STATE 8 /* Internal player state (read only) */ +#define XMP_PLAYER_SMIX_VOLUME 9 /* SMIX volume */ +#define XMP_PLAYER_DEFPAN 10 /* Default pan setting */ +#define XMP_PLAYER_MODE 11 /* Player personality */ +#define XMP_PLAYER_MIXER_TYPE 12 /* Current mixer (read only) */ +#define XMP_PLAYER_VOICES 13 /* Maximum number of mixer voices */ + +/* interpolation types */ +#define XMP_INTERP_NEAREST 0 /* Nearest neighbor */ +#define XMP_INTERP_LINEAR 1 /* Linear (default) */ +#define XMP_INTERP_SPLINE 2 /* Cubic spline */ + +/* dsp effect types */ +#define XMP_DSP_LOWPASS (1 << 0) /* Lowpass filter effect */ +#define XMP_DSP_ALL (XMP_DSP_LOWPASS) + +/* player state */ +#define XMP_STATE_UNLOADED 0 /* Context created */ +#define XMP_STATE_LOADED 1 /* Module loaded */ +#define XMP_STATE_PLAYING 2 /* Module playing */ + +/* player flags */ +#define XMP_FLAGS_VBLANK (1 << 0) /* Use vblank timing */ +#define XMP_FLAGS_FX9BUG (1 << 1) /* Emulate FX9 bug */ +#define XMP_FLAGS_FIXLOOP (1 << 2) /* Emulate sample loop bug */ +#define XMP_FLAGS_A500 (1 << 3) /* Use Paula mixer in Amiga modules */ + +/* player modes */ +#define XMP_MODE_AUTO 0 /* Autodetect mode (default) */ +#define XMP_MODE_MOD 1 /* Play as a generic MOD player */ +#define XMP_MODE_NOISETRACKER 2 /* Play using Noisetracker quirks */ +#define XMP_MODE_PROTRACKER 3 /* Play using Protracker quirks */ +#define XMP_MODE_S3M 4 /* Play as a generic S3M player */ +#define XMP_MODE_ST3 5 /* Play using ST3 bug emulation */ +#define XMP_MODE_ST3GUS 6 /* Play using ST3+GUS quirks */ +#define XMP_MODE_XM 7 /* Play as a generic XM player */ +#define XMP_MODE_FT2 8 /* Play using FT2 bug emulation */ +#define XMP_MODE_IT 9 /* Play using IT quirks */ +#define XMP_MODE_ITSMP 10 /* Play using IT sample mode quirks */ + +/* mixer types */ +#define XMP_MIXER_STANDARD 0 /* Standard mixer */ +#define XMP_MIXER_A500 1 /* Amiga 500 */ +#define XMP_MIXER_A500F 2 /* Amiga 500 with led filter */ + +/* sample flags */ +#define XMP_SMPCTL_SKIP (1 << 0) /* Don't load samples */ + +/* limits */ +#define XMP_MAX_KEYS 121 /* Number of valid keys */ +#define XMP_MAX_ENV_POINTS 32 /* Max number of envelope points */ +#define XMP_MAX_MOD_LENGTH 256 /* Max number of patterns in module */ +#define XMP_MAX_CHANNELS 64 /* Max number of channels in module */ +#define XMP_MAX_SRATE 49170 /* max sampling rate (Hz) */ +#define XMP_MIN_SRATE 4000 /* min sampling rate (Hz) */ +#define XMP_MIN_BPM 20 /* min BPM */ +/* frame rate = (50 * bpm / 125) Hz */ +/* frame size = (sampling rate * channels * size) / frame rate */ +#define XMP_MAX_FRAMESIZE (5 * XMP_MAX_SRATE * 2 / XMP_MIN_BPM) + +/* error codes */ +#define XMP_END 1 +#define XMP_ERROR_INTERNAL 2 /* Internal error */ +#define XMP_ERROR_FORMAT 3 /* Unsupported module format */ +#define XMP_ERROR_LOAD 4 /* Error loading file */ +#define XMP_ERROR_DEPACK 5 /* Error depacking file */ +#define XMP_ERROR_SYSTEM 6 /* System error */ +#define XMP_ERROR_INVALID 7 /* Invalid parameter */ +#define XMP_ERROR_STATE 8 /* Invalid player state */ + +struct xmp_channel { + int pan; /* Channel pan (0x80 is center) */ + int vol; /* Channel volume */ +#define XMP_CHANNEL_SYNTH (1 << 0) /* Channel is synthesized */ +#define XMP_CHANNEL_MUTE (1 << 1) /* Channel is muted */ +#define XMP_CHANNEL_SPLIT (1 << 2) /* Split Amiga channel in bits 5-4 */ +#define XMP_CHANNEL_SURROUND (1 << 4) /* Surround channel */ + int flg; /* Channel flags */ +}; + +struct xmp_pattern { + int rows; /* Number of rows */ + int index[1]; /* Track index */ +}; + +struct xmp_event { + unsigned char note; /* Note number (0 means no note) */ + unsigned char ins; /* Patch number */ + unsigned char vol; /* Volume (0 to basevol) */ + unsigned char fxt; /* Effect type */ + unsigned char fxp; /* Effect parameter */ + unsigned char f2t; /* Secondary effect type */ + unsigned char f2p; /* Secondary effect parameter */ + unsigned char _flag; /* Internal (reserved) flags */ +}; + +struct xmp_track { + int rows; /* Number of rows */ + struct xmp_event event[1]; /* Event data */ +}; + +struct xmp_envelope { +#define XMP_ENVELOPE_ON (1 << 0) /* Envelope is enabled */ +#define XMP_ENVELOPE_SUS (1 << 1) /* Envelope has sustain point */ +#define XMP_ENVELOPE_LOOP (1 << 2) /* Envelope has loop */ +#define XMP_ENVELOPE_FLT (1 << 3) /* Envelope is used for filter */ +#define XMP_ENVELOPE_SLOOP (1 << 4) /* Envelope has sustain loop */ +#define XMP_ENVELOPE_CARRY (1 << 5) /* Don't reset envelope position */ + int flg; /* Flags */ + int npt; /* Number of envelope points */ + int scl; /* Envelope scaling */ + int sus; /* Sustain start point */ + int sue; /* Sustain end point */ + int lps; /* Loop start point */ + int lpe; /* Loop end point */ + short data[XMP_MAX_ENV_POINTS * 2]; +}; + +struct xmp_subinstrument { + int vol; /* Default volume */ + int gvl; /* Global volume */ + int pan; /* Pan */ + int xpo; /* Transpose */ + int fin; /* Finetune */ + int vwf; /* Vibrato waveform */ + int vde; /* Vibrato depth */ + int vra; /* Vibrato rate */ + int vsw; /* Vibrato sweep */ + int rvv; /* Random volume/pan variation (IT) */ + int sid; /* Sample number */ +#define XMP_INST_NNA_CUT 0x00 +#define XMP_INST_NNA_CONT 0x01 +#define XMP_INST_NNA_OFF 0x02 +#define XMP_INST_NNA_FADE 0x03 + int nna; /* New note action */ +#define XMP_INST_DCT_OFF 0x00 +#define XMP_INST_DCT_NOTE 0x01 +#define XMP_INST_DCT_SMP 0x02 +#define XMP_INST_DCT_INST 0x03 + int dct; /* Duplicate check type */ +#define XMP_INST_DCA_CUT XMP_INST_NNA_CUT +#define XMP_INST_DCA_OFF XMP_INST_NNA_OFF +#define XMP_INST_DCA_FADE XMP_INST_NNA_FADE + int dca; /* Duplicate check action */ + int ifc; /* Initial filter cutoff */ + int ifr; /* Initial filter resonance */ +}; + +struct xmp_instrument { + char name[32]; /* Instrument name */ + int vol; /* Instrument volume */ + int nsm; /* Number of samples */ + int rls; /* Release (fadeout) */ + struct xmp_envelope aei; /* Amplitude envelope info */ + struct xmp_envelope pei; /* Pan envelope info */ + struct xmp_envelope fei; /* Frequency envelope info */ + + struct { + unsigned char ins; /* Instrument number for each key */ + signed char xpo; /* Instrument transpose for each key */ + } map[XMP_MAX_KEYS]; + + struct xmp_subinstrument *sub; + + void *extra; /* Extra fields */ +}; + +struct xmp_sample { + char name[32]; /* Sample name */ + int len; /* Sample length */ + int lps; /* Loop start */ + int lpe; /* Loop end */ +#define XMP_SAMPLE_16BIT (1 << 0) /* 16bit sample */ +#define XMP_SAMPLE_LOOP (1 << 1) /* Sample is looped */ +#define XMP_SAMPLE_LOOP_BIDIR (1 << 2) /* Bidirectional sample loop */ +#define XMP_SAMPLE_LOOP_REVERSE (1 << 3) /* Backwards sample loop */ +#define XMP_SAMPLE_LOOP_FULL (1 << 4) /* Play full sample before looping */ +#define XMP_SAMPLE_SLOOP (1 << 5) /* Sample has sustain loop */ +#define XMP_SAMPLE_SLOOP_BIDIR (1 << 6) /* Bidirectional sustain loop */ +#define XMP_SAMPLE_STEREO (1 << 7) /* Interlaced stereo sample */ +#define XMP_SAMPLE_SYNTH (1 << 15) /* Data contains synth patch */ + int flg; /* Flags */ + unsigned char *data; /* Sample data */ +}; + +struct xmp_sequence { + int entry_point; + int duration; +}; + +struct xmp_module { + char name[XMP_NAME_SIZE]; /* Module title */ + char type[XMP_NAME_SIZE]; /* Module format */ + int pat; /* Number of patterns */ + int trk; /* Number of tracks */ + int chn; /* Tracks per pattern */ + int ins; /* Number of instruments */ + int smp; /* Number of samples */ + int spd; /* Initial speed */ + int bpm; /* Initial BPM */ + int len; /* Module length in patterns */ + int rst; /* Restart position */ + int gvl; /* Global volume */ + + struct xmp_pattern **xxp; /* Patterns */ + struct xmp_track **xxt; /* Tracks */ + struct xmp_instrument *xxi; /* Instruments */ + struct xmp_sample *xxs; /* Samples */ + struct xmp_channel xxc[XMP_MAX_CHANNELS]; /* Channel info */ + unsigned char xxo[XMP_MAX_MOD_LENGTH]; /* Orders */ +}; + +struct xmp_test_info { + char name[XMP_NAME_SIZE]; /* Module title */ + char type[XMP_NAME_SIZE]; /* Module format */ +}; + +struct xmp_module_info { + unsigned char md5[16]; /* MD5 message digest */ + int vol_base; /* Volume scale */ + struct xmp_module *mod; /* Pointer to module data */ + char *comment; /* Comment text, if any */ + int num_sequences; /* Number of valid sequences */ + struct xmp_sequence *seq_data; /* Pointer to sequence data */ +}; + +struct xmp_channel_info { + unsigned int period; /* Sample period (* 4096) */ + unsigned int position; /* Sample position */ + short pitchbend; /* Linear bend from base note*/ + unsigned char note; /* Current base note number */ + unsigned char instrument; /* Current instrument number */ + unsigned char sample; /* Current sample number */ + unsigned char volume; /* Current volume */ + unsigned char pan; /* Current stereo pan */ + unsigned char reserved; /* Reserved */ + struct xmp_event event; /* Current track event */ +}; + +struct xmp_frame_info { /* Current frame information */ + int pos; /* Current position */ + int pattern; /* Current pattern */ + int row; /* Current row in pattern */ + int num_rows; /* Number of rows in current pattern */ + int frame; /* Current frame */ + int speed; /* Current replay speed */ + int bpm; /* Current bpm */ + int time; /* Current module time in ms */ + int total_time; /* Estimated replay time in ms*/ + int frame_time; /* Frame replay time in us */ + void *buffer; /* Pointer to sound buffer */ + int buffer_size; /* Used buffer size */ + int total_size; /* Total buffer size */ + int volume; /* Current master volume */ + int loop_count; /* Loop counter */ + int virt_channels; /* Number of virtual channels */ + int virt_used; /* Used virtual channels */ + int sequence; /* Current sequence */ + + struct xmp_channel_info channel_info[XMP_MAX_CHANNELS]; /* Current channel information */ +}; + +struct xmp_callbacks { + unsigned long (*read_func)(void *dest, unsigned long len, + unsigned long nmemb, void *priv); + int (*seek_func)(void *priv, long offset, int whence); + long (*tell_func)(void *priv); + int (*close_func)(void *priv); +}; + +typedef char *xmp_context; + +LIBXMP_EXPORT_VAR extern const char *xmp_version; +LIBXMP_EXPORT_VAR extern const unsigned int xmp_vercode; + +LIBXMP_EXPORT int xmp_syserrno (void); + +LIBXMP_EXPORT xmp_context xmp_create_context (void); +LIBXMP_EXPORT void xmp_free_context (xmp_context); + +LIBXMP_EXPORT int xmp_load_module (xmp_context, const char *); +LIBXMP_EXPORT int xmp_load_module_from_memory (xmp_context, const void *, long); +LIBXMP_EXPORT int xmp_load_module_from_file (xmp_context, void *, long); +LIBXMP_EXPORT int xmp_load_module_from_callbacks (xmp_context, void *, struct xmp_callbacks); + +LIBXMP_EXPORT int xmp_test_module (const char *, struct xmp_test_info *); +LIBXMP_EXPORT int xmp_test_module_from_memory (const void *, long, struct xmp_test_info *); +LIBXMP_EXPORT int xmp_test_module_from_file (void *, struct xmp_test_info *); +LIBXMP_EXPORT int xmp_test_module_from_callbacks (void *, struct xmp_callbacks, struct xmp_test_info *); + +LIBXMP_EXPORT void xmp_scan_module (xmp_context); +LIBXMP_EXPORT void xmp_release_module (xmp_context); + +LIBXMP_EXPORT int xmp_start_player (xmp_context, int, int); +LIBXMP_EXPORT int xmp_play_frame (xmp_context); +LIBXMP_EXPORT int xmp_play_buffer (xmp_context, void *, int, int); +LIBXMP_EXPORT void xmp_get_frame_info (xmp_context, struct xmp_frame_info *); +LIBXMP_EXPORT void xmp_end_player (xmp_context); +LIBXMP_EXPORT void xmp_inject_event (xmp_context, int, struct xmp_event *); +LIBXMP_EXPORT void xmp_get_module_info (xmp_context, struct xmp_module_info *); +LIBXMP_EXPORT const char *const *xmp_get_format_list (void); +LIBXMP_EXPORT int xmp_next_position (xmp_context); +LIBXMP_EXPORT int xmp_prev_position (xmp_context); +LIBXMP_EXPORT int xmp_set_position (xmp_context, int); +LIBXMP_EXPORT int xmp_set_row (xmp_context, int); +LIBXMP_EXPORT int xmp_set_tempo_factor(xmp_context, double); +LIBXMP_EXPORT void xmp_stop_module (xmp_context); +LIBXMP_EXPORT void xmp_restart_module (xmp_context); +LIBXMP_EXPORT int xmp_seek_time (xmp_context, int); +LIBXMP_EXPORT int xmp_channel_mute (xmp_context, int, int); +LIBXMP_EXPORT int xmp_channel_vol (xmp_context, int, int); +LIBXMP_EXPORT int xmp_set_player (xmp_context, int, int); +LIBXMP_EXPORT int xmp_get_player (xmp_context, int); +LIBXMP_EXPORT int xmp_set_instrument_path (xmp_context, const char *); + +/* External sample mixer API */ +LIBXMP_EXPORT int xmp_start_smix (xmp_context, int, int); +LIBXMP_EXPORT void xmp_end_smix (xmp_context); +LIBXMP_EXPORT int xmp_smix_play_instrument(xmp_context, int, int, int, int); +LIBXMP_EXPORT int xmp_smix_play_sample (xmp_context, int, int, int, int); +LIBXMP_EXPORT int xmp_smix_channel_pan (xmp_context, int, int); +LIBXMP_EXPORT int xmp_smix_load_sample (xmp_context, int, const char *); +LIBXMP_EXPORT int xmp_smix_release_sample (xmp_context, int); + +#ifdef __cplusplus +} +#endif + +#endif /* XMP_H */ diff --git a/thirdparty/libxmp/src/callbackio.h b/thirdparty/libxmp/src/callbackio.h new file mode 100644 index 000000000..de46eb497 --- /dev/null +++ b/thirdparty/libxmp/src/callbackio.h @@ -0,0 +1,187 @@ +#ifndef LIBXMP_CALLBACKIO_H +#define LIBXMP_CALLBACKIO_H + +#include +#include "common.h" + +typedef struct { + void *priv; + struct xmp_callbacks callbacks; + int eof; +} CBFILE; + +LIBXMP_BEGIN_DECLS + +static inline uint8 cbread8(CBFILE *f, int *err) +{ + uint8 x = 0xff; + size_t r = f->callbacks.read_func(&x, 1, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (err) *err = f->eof; + + return x; +} + +static inline int8 cbread8s(CBFILE *f, int *err) +{ + return (int8)cbread8(f, err); +} + +static inline uint16 cbread16l(CBFILE *f, int *err) +{ + uint8 buf[2]; + uint16 x = 0xffff; + size_t r = f->callbacks.read_func(buf, 2, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem16l(buf); + if (err) *err = f->eof; + + return x; +} + +static inline uint16 cbread16b(CBFILE *f, int *err) +{ + uint8 buf[2]; + uint16 x = 0xffff; + size_t r = f->callbacks.read_func(buf, 2, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem16b(buf); + if (err) *err = f->eof; + + return x; +} + +static inline uint32 cbread24l(CBFILE *f, int *err) +{ + uint8 buf[3]; + uint32 x = 0xffffffff; + size_t r = f->callbacks.read_func(buf, 3, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem24l(buf); + if (err) *err = f->eof; + + return x; +} + +static inline uint32 cbread24b(CBFILE *f, int *err) +{ + uint8 buf[3]; + uint32 x = 0xffffffff; + size_t r = f->callbacks.read_func(buf, 3, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem24b(buf); + if (err) *err = f->eof; + + return x; +} + +static inline uint32 cbread32l(CBFILE *f, int *err) +{ + uint8 buf[4]; + uint32 x = 0xffffffff; + size_t r = f->callbacks.read_func(buf, 4, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem32l(buf); + if (err) *err = f->eof; + + return x; +} + +static inline uint32 cbread32b(CBFILE *f, int *err) +{ + uint8 buf[4]; + uint32 x = 0xffffffff; + size_t r = f->callbacks.read_func(buf, 4, 1, f->priv); + f->eof = (r == 1) ? 0 : EOF; + + if (r) x = readmem32b(buf); + if (err) *err = f->eof; + + return x; +} + +static inline size_t cbread(void *dest, size_t len, size_t nmemb, CBFILE *f) +{ + size_t r = f->callbacks.read_func(dest, (unsigned long)len, + (unsigned long)nmemb, f->priv); + f->eof = (r < nmemb) ? EOF : 0; + return r; +} + +static inline int cbseek(CBFILE *f, long offset, int whence) +{ + f->eof = 0; + return f->callbacks.seek_func(f->priv, offset, whence); +} + +static inline long cbtell(CBFILE *f) +{ + return f->callbacks.tell_func(f->priv); +} + +static inline int cbeof(CBFILE *f) +{ + return f->eof; +} + +static inline long cbfilelength(CBFILE *f) +{ + long pos = f->callbacks.tell_func(f->priv); + long length; + int r; + + if (pos < 0) + return EOF; + + r = f->callbacks.seek_func(f->priv, 0, SEEK_END); + if (r < 0) + return EOF; + + length = f->callbacks.tell_func(f->priv); + r = f->callbacks.seek_func(f->priv, pos, SEEK_SET); + + return length; +} + +static inline CBFILE *cbopen(void *priv, struct xmp_callbacks callbacks) +{ + CBFILE *f; + if (priv == NULL || callbacks.read_func == NULL || + callbacks.seek_func == NULL || callbacks.tell_func == NULL) + goto err; + + f = (CBFILE *)calloc(1, sizeof(CBFILE)); + if (f == NULL) + goto err; + + f->priv = priv; + f->callbacks = callbacks; + f->eof = 0; + return f; + + err: + if (priv && callbacks.close_func) + callbacks.close_func(priv); + + return NULL; +} + +static inline int cbclose(CBFILE *f) +{ + int r = 0; + if (f->callbacks.close_func != NULL) + r = f->callbacks.close_func(f->priv); + + free(f); + return r; +} + +LIBXMP_END_DECLS + +#endif /* LIBXMP_CALLBACKIO_H */ diff --git a/thirdparty/libxmp/src/common.h b/thirdparty/libxmp/src/common.h new file mode 100644 index 000000000..0f62151ae --- /dev/null +++ b/thirdparty/libxmp/src/common.h @@ -0,0 +1,574 @@ +#ifndef LIBXMP_COMMON_H +#define LIBXMP_COMMON_H + +#ifdef LIBXMP_CORE_PLAYER +#ifndef LIBXMP_NO_PROWIZARD +#define LIBXMP_NO_PROWIZARD +#endif +#ifndef LIBXMP_NO_DEPACKERS +#define LIBXMP_NO_DEPACKERS +#endif +#else +#undef LIBXMP_CORE_DISABLE_IT +#endif + +#include +#include +#include +#include +#include +#include "xmp.h" + +#undef LIBXMP_EXPORT_VAR +#if defined(EMSCRIPTEN) +#include +#define LIBXMP_EXPORT_VAR EMSCRIPTEN_KEEPALIVE +#else +#define LIBXMP_EXPORT_VAR +#endif + +#ifndef __cplusplus +#define LIBXMP_BEGIN_DECLS +#define LIBXMP_END_DECLS +#else +#define LIBXMP_BEGIN_DECLS extern "C" { +#define LIBXMP_END_DECLS } +#endif + +#if defined(_MSC_VER) && !defined(__cplusplus) +#define inline __inline +#endif + +#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\ + (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \ + (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus)) +#define LIBXMP_RESTRICT __restrict +#else +#define LIBXMP_RESTRICT +#endif + +#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__EMX__) +#define XMP_MAXPATH _MAX_PATH +#elif defined(PATH_MAX) +#define XMP_MAXPATH PATH_MAX +#else +#define XMP_MAXPATH 1024 +#endif + +#if defined(__MORPHOS__) || defined(__AROS__) || defined(__AMIGA__) \ + || defined(__amigaos__) || defined(__amigaos4__) || defined(AMIGA) +#define LIBXMP_AMIGA 1 +#endif + +#if defined(_MSC_VER) && defined(__has_include) +#if __has_include() +#define HAVE_WINAPIFAMILY_H 1 +#else +#define HAVE_WINAPIFAMILY_H 0 +#endif +#endif + +#if HAVE_WINAPIFAMILY_H +#include +#define LIBXMP_UWP (!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)) +#else +#define LIBXMP_UWP 0 +#endif + +#ifdef HAVE_EXTERNAL_VISIBILITY +#define LIBXMP_EXPORT_VERSIONED __attribute__((visibility("default"),externally_visible)) +#else +#define LIBXMP_EXPORT_VERSIONED __attribute__((visibility("default"))) +#endif +#ifdef HAVE_ATTRIBUTE_SYMVER +#define LIBXMP_ATTRIB_SYMVER(_sym) __attribute__((__symver__(_sym))) +#else +#define LIBXMP_ATTRIB_SYMVER(_sym) +#endif + +/* AmigaOS fixes by Chris Young , Nov 25, 2007 + */ +#if defined B_BEOS_VERSION +# include +#elif defined __amigaos4__ +# include +#elif defined _arch_dreamcast /* KallistiOS */ +# include +#else +typedef signed char int8; +typedef signed short int int16; +typedef signed int int32; +typedef unsigned char uint8; +typedef unsigned short int uint16; +typedef unsigned int uint32; +#endif + +#ifdef _MSC_VER /* MSVC6 has no long long */ +typedef signed __int64 int64; +typedef unsigned __int64 uint64; +#elif !(defined(B_BEOS_VERSION) || defined(__amigaos4__)) +typedef unsigned long long uint64; +typedef signed long long int64; +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4100) /* unreferenced formal parameter */ +#endif + +#ifndef LIBXMP_CORE_PLAYER +#define LIBXMP_PAULA_SIMULATOR +#endif + +/* Constants */ +#define PAL_RATE 250.0 /* 1 / (50Hz * 80us) */ +#define NTSC_RATE 208.0 /* 1 / (60Hz * 80us) */ +#define C4_PAL_RATE 8287 /* 7093789.2 / period (C4) * 2 */ +#define C4_NTSC_RATE 8363 /* 7159090.5 / period (C4) * 2 */ + +/* [Amiga] PAL color carrier frequency (PCCF) = 4.43361825 MHz */ +/* [Amiga] CPU clock = 1.6 * PCCF = 7.0937892 MHz */ + +#define DEFAULT_AMPLIFY 1 +#define DEFAULT_MIX 100 + +#define MSN(x) (((x)&0xf0)>>4) +#define LSN(x) ((x)&0x0f) +#define SET_FLAG(a,b) ((a)|=(b)) +#define RESET_FLAG(a,b) ((a)&=~(b)) +#define TEST_FLAG(a,b) !!((a)&(b)) + +/* libxmp_get_filetype() return values */ +#define XMP_FILETYPE_NONE 0 +#define XMP_FILETYPE_DIR (1 << 0) +#define XMP_FILETYPE_FILE (1 << 1) + +#define CLAMP(x,a,b) do { \ + if ((x) < (a)) (x) = (a); \ + else if ((x) > (b)) (x) = (b); \ +} while (0) +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +#define TRACK_NUM(a,c) m->mod.xxp[a]->index[c] +#define EVENT(a,c,r) m->mod.xxt[TRACK_NUM((a),(c))]->event[r] + +#ifdef _MSC_VER +#define D_CRIT " Error: " +#define D_WARN "Warning: " +#define D_INFO " Info: " +#ifdef DEBUG +#define D_ libxmp_msvc_dbgprint /* in win32.c */ +void libxmp_msvc_dbgprint(const char *text, ...); +#else +/* VS prior to VC7.1 does not support variadic macros. + * VC8.0 does not optimize unused parameters passing. */ +#if _MSC_VER < 1400 +static void __inline D_(const char *text, ...) { + do { } while (0); +} +#else +#define D_(...) do {} while (0) +#endif +#endif + +#elif defined __ANDROID__ + +#ifdef DEBUG +#include +#define D_CRIT " Error: " +#define D_WARN "Warning: " +#define D_INFO " Info: " +#define D_(...) do { \ + __android_log_print(ANDROID_LOG_DEBUG, "libxmp", __VA_ARGS__); \ + } while (0) +#else +#define D_(...) do {} while (0) +#endif + +#else + +#ifdef DEBUG +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define LIBXMP_FUNC __func__ +#else +#define LIBXMP_FUNC __FUNCTION__ +#endif +#define D_INFO "\x1b[33m" +#define D_CRIT "\x1b[31m" +#define D_WARN "\x1b[36m" +#define D_(...) do { \ + printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, LIBXMP_FUNC, \ + __FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \ + } while (0) +#else +#define D_(...) do {} while (0) +#endif + +#endif /* !_MSC_VER */ + +#ifdef _MSC_VER +#define dup _dup +#define fileno _fileno +#define strnicmp _strnicmp +#define fdopen _fdopen +#define open _open +#define close _close +#define unlink _unlink +#define S_ISDIR(x) (((x)&_S_IFDIR) != 0) +#endif +#if defined(_WIN32) || defined(__WATCOMC__) /* in win32.c */ +#define USE_LIBXMP_SNPRINTF +/* MSVC 2015+ has C99 compliant snprintf and vsnprintf implementations. + * If __USE_MINGW_ANSI_STDIO is defined for MinGW (which it is by default), + * compliant implementations will be used instead of the broken MSVCRT + * functions. Additionally, GCC may optimize some calls to those functions. */ +#if defined(_MSC_VER) && _MSC_VER >= 1900 +#undef USE_LIBXMP_SNPRINTF +#endif +#if defined(__MINGW32__) && defined(__USE_MINGW_ANSI_STDIO) && (__USE_MINGW_ANSI_STDIO != 0) +#undef USE_LIBXMP_SNPRINTF +#endif +#ifdef USE_LIBXMP_SNPRINTF +#if defined(__GNUC__) || defined(__clang__) +#define LIBXMP_ATTRIB_PRINTF(x,y) __attribute__((__format__(__printf__,x,y))) +#else +#define LIBXMP_ATTRIB_PRINTF(x,y) +#endif +int libxmp_vsnprintf(char *, size_t, const char *, va_list) LIBXMP_ATTRIB_PRINTF(3,0); +int libxmp_snprintf (char *, size_t, const char *, ...) LIBXMP_ATTRIB_PRINTF(3,4); +#define snprintf libxmp_snprintf +#define vsnprintf libxmp_vsnprintf +#endif +#endif + +/* Output file size limit for files unpacked from unarchivers into RAM. Most + * general archive compression formats can't nicely bound the output size + * from their input filesize, and a cap is needed for a few reasons: + * + * - Linux is too dumb for its own good and its malloc/realloc will return + * pointers to RAM that doesn't exist instead of NULL. When these are used, + * it will kill the application instead of allowing it to fail gracefully. + * - libFuzzer and the clang sanitizers have malloc/realloc interceptors that + * terminate with an error instead of returning NULL. + * + * Depackers that have better ways of bounding the output size can ignore this. + * This value is fairly arbitrary and can be changed if needed. + */ +#define LIBXMP_DEPACK_LIMIT (512 << 20) + +/* Quirks */ +#define QUIRK_S3MLOOP (1 << 0) /* S3M loop mode */ +#define QUIRK_ENVFADE (1 << 1) /* Fade at end of envelope */ +#define QUIRK_PROTRACK (1 << 2) /* Use Protracker-specific quirks */ +#define QUIRK_ST3BUGS (1 << 4) /* Scream Tracker 3 bug compatibility */ +#define QUIRK_FINEFX (1 << 5) /* Enable 0xf/0xe for fine effects */ +#define QUIRK_VSALL (1 << 6) /* Volume slides in all frames */ +#define QUIRK_PBALL (1 << 7) /* Pitch bending in all frames */ +#define QUIRK_PERPAT (1 << 8) /* Cancel persistent fx at pat start */ +#define QUIRK_VOLPDN (1 << 9) /* Set priority to volume slide down */ +#define QUIRK_UNISLD (1 << 10) /* Unified pitch slide/portamento */ +#define QUIRK_ITVPOR (1 << 11) /* Disable fine bends in IT vol fx */ +#define QUIRK_FTMOD (1 << 12) /* Flag for multichannel mods */ +#define QUIRK_INVLOOP (1 << 13) /* Enable invert loop */ +/*#define QUIRK_MODRNG (1 << 13)*/ /* Limit periods to MOD range */ +#define QUIRK_INSVOL (1 << 14) /* Use instrument volume */ +#define QUIRK_VIRTUAL (1 << 15) /* Enable virtual channels */ +#define QUIRK_FILTER (1 << 16) /* Enable filter */ +#define QUIRK_IGSTPOR (1 << 17) /* Ignore stray tone portamento */ +#define QUIRK_KEYOFF (1 << 18) /* Keyoff doesn't reset fadeout */ +#define QUIRK_VIBHALF (1 << 19) /* Vibrato is half as deep */ +#define QUIRK_VIBALL (1 << 20) /* Vibrato in all frames */ +#define QUIRK_VIBINV (1 << 21) /* Vibrato has inverse waveform */ +#define QUIRK_PRENV (1 << 22) /* Portamento resets envelope & fade */ +#define QUIRK_ITOLDFX (1 << 23) /* IT old effects mode */ +#define QUIRK_S3MRTG (1 << 24) /* S3M-style retrig when count == 0 */ +#define QUIRK_RTDELAY (1 << 25) /* Delay effect retrigs instrument */ +#define QUIRK_FT2BUGS (1 << 26) /* FT2 bug compatibility */ +#define QUIRK_MARKER (1 << 27) /* Patterns 0xfe and 0xff reserved */ +#define QUIRK_NOBPM (1 << 28) /* Adjust speed only, no BPM */ +#define QUIRK_ARPMEM (1 << 29) /* Arpeggio has memory (S3M_ARPEGGIO) */ +#define QUIRK_RSTCHN (1 << 30) /* Reset channel on sample end */ + +#define HAS_QUIRK(x) (m->quirk & (x)) + + +/* Format quirks */ +#define QUIRKS_ST3 (QUIRK_S3MLOOP | QUIRK_VOLPDN | QUIRK_FINEFX | \ + QUIRK_S3MRTG | QUIRK_MARKER | QUIRK_RSTCHN ) +#define QUIRKS_FT2 (QUIRK_RTDELAY | QUIRK_FINEFX ) +#define QUIRKS_IT (QUIRK_S3MLOOP | QUIRK_FINEFX | QUIRK_VIBALL | \ + QUIRK_ENVFADE | QUIRK_ITVPOR | QUIRK_KEYOFF | \ + QUIRK_VIRTUAL | QUIRK_FILTER | QUIRK_RSTCHN | \ + QUIRK_IGSTPOR | QUIRK_S3MRTG | QUIRK_MARKER ) + +/* DSP effects */ +#define DSP_EFFECT_CUTOFF 0x02 +#define DSP_EFFECT_RESONANCE 0x03 +#define DSP_EFFECT_FILTER_A0 0xb0 +#define DSP_EFFECT_FILTER_B0 0xb1 +#define DSP_EFFECT_FILTER_B1 0xb2 + +/* Time factor */ +#define DEFAULT_TIME_FACTOR 10.0 +#define MED_TIME_FACTOR 2.64 +#define FAR_TIME_FACTOR 4.01373 /* See far_extras.c */ + +#define MAX_SEQUENCES 255 +#define MAX_SAMPLE_SIZE 0x10000000 +#define MAX_SAMPLES 1024 +#define MAX_INSTRUMENTS 255 +#define MAX_PATTERNS 256 + +#define IS_PLAYER_MODE_MOD() (m->read_event_type == READ_EVENT_MOD) +#define IS_PLAYER_MODE_FT2() (m->read_event_type == READ_EVENT_FT2) +#define IS_PLAYER_MODE_ST3() (m->read_event_type == READ_EVENT_ST3) +#define IS_PLAYER_MODE_IT() (m->read_event_type == READ_EVENT_IT) +#define IS_PLAYER_MODE_MED() (m->read_event_type == READ_EVENT_MED) +#define IS_PERIOD_MODRNG() (m->period_type == PERIOD_MODRNG) +#define IS_PERIOD_LINEAR() (m->period_type == PERIOD_LINEAR) +#define IS_PERIOD_CSPD() (m->period_type == PERIOD_CSPD) + +#define IS_AMIGA_MOD() (IS_PLAYER_MODE_MOD() && IS_PERIOD_MODRNG()) + +struct ord_data { + int speed; + int bpm; + int gvl; + int time; + int start_row; +#ifndef LIBXMP_CORE_PLAYER + int st26_speed; +#endif +}; + + +/* Context */ + +struct smix_data { + int chn; + int ins; + int smp; + struct xmp_instrument *xxi; + struct xmp_sample *xxs; +}; + +/* This will be added to the sample structure in the next API revision */ +struct extra_sample_data { + double c5spd; + int sus; + int sue; +}; + +struct midi_macro { + char data[32]; +}; + +struct midi_macro_data { + struct midi_macro param[16]; + struct midi_macro fixed[128]; +}; + +struct module_data { + struct xmp_module mod; + + char *dirname; /* file dirname */ + char *basename; /* file basename */ + const char *filename; /* Module file name */ + char *comment; /* Comments, if any */ + uint8 md5[16]; /* MD5 message digest */ + int size; /* File size */ + double rrate; /* Replay rate */ + double time_factor; /* Time conversion constant */ + int c4rate; /* C4 replay rate */ + int volbase; /* Volume base */ + int gvolbase; /* Global volume base */ + int gvol; /* Global volume */ + int mvolbase; /* Mix volume base (S3M/IT) */ + int mvol; /* Mix volume (S3M/IT) */ + const int *vol_table; /* Volume translation table */ + int quirk; /* player quirks */ +#define READ_EVENT_MOD 0 +#define READ_EVENT_FT2 1 +#define READ_EVENT_ST3 2 +#define READ_EVENT_IT 3 +#define READ_EVENT_MED 4 + int read_event_type; +#define PERIOD_AMIGA 0 +#define PERIOD_MODRNG 1 +#define PERIOD_LINEAR 2 +#define PERIOD_CSPD 3 + int period_type; + int smpctl; /* sample control flags */ + int defpan; /* default pan setting */ + struct ord_data xxo_info[XMP_MAX_MOD_LENGTH]; + int num_sequences; + struct xmp_sequence seq_data[MAX_SEQUENCES]; + char *instrument_path; + void *extra; /* format-specific extra fields */ + uint8 **scan_cnt; /* scan counters */ + struct extra_sample_data *xtra; + struct midi_macro_data *midi; + int compare_vblank; +}; + + +struct pattern_loop { + int start; + int count; +}; + +struct flow_control { + int pbreak; + int jump; + int delay; + int jumpline; + int loop_chn; +#ifndef LIBXMP_CORE_PLAYER + int jump_in_pat; +#endif + + struct pattern_loop *loop; + + int num_rows; + int end_point; +#define ROWDELAY_ON (1 << 0) +#define ROWDELAY_FIRST_FRAME (1 << 1) + int rowdelay; /* For IT pattern row delay */ + int rowdelay_set; +}; + +struct virt_channel { + int count; + int map; +}; + +struct scan_data { + int time; /* replay time in ms */ + int row; + int ord; + int num; +}; + +struct player_data { + int ord; + int pos; + int row; + int frame; + int speed; + int bpm; + int mode; + int player_flags; + int flags; + + double current_time; + double frame_time; + + int loop_count; + int sequence; + unsigned char sequence_control[XMP_MAX_MOD_LENGTH]; + + int smix_vol; /* SFX volume */ + int master_vol; /* Music volume */ + int gvol; + + struct flow_control flow; + + struct scan_data *scan; + + struct channel_data *xc_data; + + int channel_vol[XMP_MAX_CHANNELS]; + char channel_mute[XMP_MAX_CHANNELS]; + + struct virt_control { + int num_tracks; /* Number of tracks */ + int virt_channels; /* Number of virtual channels */ + int virt_used; /* Number of voices currently in use */ + int maxvoc; /* Number of sound card voices */ + + struct virt_channel *virt_channel; + + struct mixer_voice *voice_array; + } virt; + + struct xmp_event inject_event[XMP_MAX_CHANNELS]; + + struct { + int consumed; + int in_size; + char *in_buffer; + } buffer_data; + +#ifndef LIBXMP_CORE_PLAYER + int st26_speed; /* For IceTracker speed effect */ +#endif + int filter; /* Amiga led filter */ +}; + +struct mixer_data { + int freq; /* sampling rate */ + int format; /* sample format */ + int amplify; /* amplification multiplier */ + int mix; /* percentage of channel separation */ + int interp; /* interpolation type */ + int dsp; /* dsp effect flags */ + char *buffer; /* output buffer */ + int32 *buf32; /* temporary buffer for 32 bit samples */ + int numvoc; /* default softmixer voices number */ + int ticksize; + int dtright; /* anticlick control, right channel */ + int dtleft; /* anticlick control, left channel */ + int bidir_adjust; /* adjustment for IT bidirectional loops */ + double pbase; /* period base */ +}; + +struct context_data { + struct player_data p; + struct mixer_data s; + struct module_data m; + struct smix_data smix; + int state; +}; + + +/* Prototypes */ + +char *libxmp_adjust_string (char *); +int libxmp_prepare_scan (struct context_data *); +void libxmp_free_scan (struct context_data *); +int libxmp_scan_sequences (struct context_data *); +int libxmp_get_sequence (struct context_data *, int); +int libxmp_set_player_mode (struct context_data *); +void libxmp_reset_flow (struct context_data *); + +int8 read8s (FILE *, int *err); +uint8 read8 (FILE *, int *err); +uint16 read16l (FILE *, int *err); +uint16 read16b (FILE *, int *err); +uint32 read24l (FILE *, int *err); +uint32 read24b (FILE *, int *err); +uint32 read32l (FILE *, int *err); +uint32 read32b (FILE *, int *err); +static inline void write8 (FILE *f, uint8 b) { + fputc(b, f); +} +void write16l (FILE *, uint16); +void write16b (FILE *, uint16); +void write32l (FILE *, uint32); +void write32b (FILE *, uint32); + +uint16 readmem16l (const uint8 *); +uint16 readmem16b (const uint8 *); +uint32 readmem24l (const uint8 *); +uint32 readmem24b (const uint8 *); +uint32 readmem32l (const uint8 *); +uint32 readmem32b (const uint8 *); + +struct xmp_instrument *libxmp_get_instrument(struct context_data *, int); +struct xmp_sample *libxmp_get_sample(struct context_data *, int); + +char *libxmp_strdup(const char *); +int libxmp_get_filetype (const char *); + +#endif /* LIBXMP_COMMON_H */ diff --git a/thirdparty/libxmp/src/control.c b/thirdparty/libxmp/src/control.c new file mode 100644 index 000000000..1e56f4833 --- /dev/null +++ b/thirdparty/libxmp/src/control.c @@ -0,0 +1,590 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "format.h" +#include "virtual.h" +#include "mixer.h" + +const char *xmp_version LIBXMP_EXPORT_VAR = XMP_VERSION; +const unsigned int xmp_vercode LIBXMP_EXPORT_VAR = XMP_VERCODE; + +xmp_context xmp_create_context(void) +{ + struct context_data *ctx; + + ctx = (struct context_data *) calloc(1, sizeof(struct context_data)); + if (ctx == NULL) { + return NULL; + } + + ctx->state = XMP_STATE_UNLOADED; + ctx->m.defpan = 100; + ctx->s.numvoc = SMIX_NUMVOC; + + return (xmp_context)ctx; +} + +void xmp_free_context(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + + if (ctx->state > XMP_STATE_UNLOADED) + xmp_release_module(opaque); + + free(m->instrument_path); + free(opaque); +} + +static void set_position(struct context_data *ctx, int pos, int dir) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct flow_control *f = &p->flow; + int seq; + int has_marker; + + /* If dir is 0, we can jump to a different sequence */ + if (dir == 0) { + seq = libxmp_get_sequence(ctx, pos); + } else { + seq = p->sequence; + } + + if (seq == 0xff) { + return; + } + + has_marker = HAS_QUIRK(QUIRK_MARKER); + + if (seq >= 0) { + int start = m->seq_data[seq].entry_point; + + p->sequence = seq; + + if (pos >= 0) { + int pat; + + while (has_marker && mod->xxo[pos] == 0xfe) { + if (dir < 0) { + if (pos > start) { + pos--; + } + } else { + pos++; + } + } + pat = mod->xxo[pos]; + + if (pat < mod->pat) { + if (has_marker && pat == 0xff) { + return; + } + + if (pos > p->scan[seq].ord) { + f->end_point = 0; + } else { + f->num_rows = mod->xxp[pat]->rows; + f->end_point = p->scan[seq].num; + f->jumpline = 0; + } + } + } + + if (pos < mod->len) { + if (pos == 0) { + p->pos = -1; + } else { + p->pos = pos; + } + /* Clear flow vars to prevent old pattern jumps and + * other junk from executing in the new position. */ + libxmp_reset_flow(ctx); + } + } +} + +int xmp_next_position(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (p->pos < m->mod.len) + set_position(ctx, p->pos + 1, 1); + + return p->pos; +} + +int xmp_prev_position(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (p->pos == m->seq_data[p->sequence].entry_point) { + set_position(ctx, -1, -1); + } else if (p->pos > m->seq_data[p->sequence].entry_point) { + set_position(ctx, p->pos - 1, -1); + } + return p->pos < 0 ? 0 : p->pos; +} + +int xmp_set_position(xmp_context opaque, int pos) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (pos >= m->mod.len) + return -XMP_ERROR_INVALID; + + set_position(ctx, pos, 0); + + return p->pos; +} + +int xmp_set_row(xmp_context opaque, int row) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct flow_control *f = &p->flow; + int pos = p->pos; + int pattern; + + if (pos < 0 || pos >= mod->len) { + pos = 0; + } + pattern = mod->xxo[pos]; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (pattern >= mod->pat || row >= mod->xxp[pattern]->rows) + return -XMP_ERROR_INVALID; + + /* See set_position. */ + if (p->pos < 0) + p->pos = 0; + p->ord = p->pos; + p->row = row; + p->frame = -1; + f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows; + + return row; +} + +void xmp_stop_module(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + + if (ctx->state < XMP_STATE_PLAYING) + return; + + p->pos = -2; +} + +void xmp_restart_module(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + + if (ctx->state < XMP_STATE_PLAYING) + return; + + p->loop_count = 0; + p->pos = -1; +} + +int xmp_seek_time(xmp_context opaque, int time) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + int i, t; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + for (i = m->mod.len - 1; i >= 0; i--) { + int pat = m->mod.xxo[i]; + if (pat >= m->mod.pat) { + continue; + } + if (libxmp_get_sequence(ctx, i) != p->sequence) { + continue; + } + t = m->xxo_info[i].time; + if (time >= t) { + set_position(ctx, i, 1); + break; + } + } + if (i < 0) { + xmp_set_position(opaque, 0); + } + + return p->pos < 0 ? 0 : p->pos; +} + +int xmp_channel_mute(xmp_context opaque, int chn, int status) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + int ret; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (chn < 0 || chn >= XMP_MAX_CHANNELS) { + return -XMP_ERROR_INVALID; + } + + ret = p->channel_mute[chn]; + + if (status >= 2) { + p->channel_mute[chn] = !p->channel_mute[chn]; + } else if (status >= 0) { + p->channel_mute[chn] = status; + } + + return ret; +} + +int xmp_channel_vol(xmp_context opaque, int chn, int vol) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + int ret; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (chn < 0 || chn >= XMP_MAX_CHANNELS) { + return -XMP_ERROR_INVALID; + } + + ret = p->channel_vol[chn]; + + if (vol >= 0 && vol <= 100) { + p->channel_vol[chn] = vol; + } + + return ret; +} + +#ifdef USE_VERSIONED_SYMBOLS +LIBXMP_BEGIN_DECLS /* no name-mangling */ +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v40__(xmp_context, int, int) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.0"); +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v41__(xmp_context, int, int) + __attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.1"); +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v43__(xmp_context, int, int) + __attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.3"); +LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v44__(xmp_context, int, int) + __attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@@XMP_4.4"); + +#ifndef HAVE_ATTRIBUTE_SYMVER +asm(".symver xmp_set_player_v40__, xmp_set_player@XMP_4.0"); +asm(".symver xmp_set_player_v41__, xmp_set_player@XMP_4.1"); +asm(".symver xmp_set_player_v43__, xmp_set_player@XMP_4.3"); +asm(".symver xmp_set_player_v44__, xmp_set_player@@XMP_4.4"); +#endif +LIBXMP_END_DECLS + +#define xmp_set_player__ xmp_set_player_v40__ +#else +#define xmp_set_player__ xmp_set_player +#endif + +int xmp_set_player__(xmp_context opaque, int parm, int val) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_data *s = &ctx->s; + int ret = -XMP_ERROR_INVALID; + + + if (parm == XMP_PLAYER_SMPCTL || parm == XMP_PLAYER_DEFPAN) { + /* these should be set before loading the module */ + if (ctx->state >= XMP_STATE_LOADED) { + return -XMP_ERROR_STATE; + } + } else if (parm == XMP_PLAYER_VOICES) { + /* these should be set before start playing */ + if (ctx->state >= XMP_STATE_PLAYING) { + return -XMP_ERROR_STATE; + } + } else if (ctx->state < XMP_STATE_PLAYING) { + return -XMP_ERROR_STATE; + } + + switch (parm) { + case XMP_PLAYER_AMP: + if (val >= 0 && val <= 3) { + s->amplify = val; + ret = 0; + } + break; + case XMP_PLAYER_MIX: + if (val >= -100 && val <= 100) { + s->mix = val; + ret = 0; + } + break; + case XMP_PLAYER_INTERP: + if (val >= XMP_INTERP_NEAREST && val <= XMP_INTERP_SPLINE) { + s->interp = val; + ret = 0; + } + break; + case XMP_PLAYER_DSP: + s->dsp = val; + ret = 0; + break; + case XMP_PLAYER_FLAGS: { + p->player_flags = val; + ret = 0; + break; } + + /* 4.1 */ + case XMP_PLAYER_CFLAGS: { + int vblank = p->flags & XMP_FLAGS_VBLANK; + p->flags = val; + if (vblank != (p->flags & XMP_FLAGS_VBLANK)) + libxmp_scan_sequences(ctx); + ret = 0; + break; } + case XMP_PLAYER_SMPCTL: + m->smpctl = val; + ret = 0; + break; + case XMP_PLAYER_VOLUME: + if (val >= 0 && val <= 200) { + p->master_vol = val; + ret = 0; + } + break; + case XMP_PLAYER_SMIX_VOLUME: + if (val >= 0 && val <= 200) { + p->smix_vol = val; + ret = 0; + } + break; + + /* 4.3 */ + case XMP_PLAYER_DEFPAN: + if (val >= 0 && val <= 100) { + m->defpan = val; + ret = 0; + } + break; + + /* 4.4 */ + case XMP_PLAYER_MODE: + p->mode = val; + libxmp_set_player_mode(ctx); + libxmp_scan_sequences(ctx); + ret = 0; + break; + case XMP_PLAYER_VOICES: + s->numvoc = val; + break; + } + + return ret; +} + +#ifdef USE_VERSIONED_SYMBOLS +LIBXMP_BEGIN_DECLS /* no name-mangling */ +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v40__(xmp_context, int) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.0"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v41__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.1"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v42__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.2"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v43__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.3"); +LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v44__(xmp_context, int) + __attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@@XMP_4.4"); + +#ifndef HAVE_ATTRIBUTE_SYMVER +asm(".symver xmp_get_player_v40__, xmp_get_player@XMP_4.0"); +asm(".symver xmp_get_player_v41__, xmp_get_player@XMP_4.1"); +asm(".symver xmp_get_player_v42__, xmp_get_player@XMP_4.2"); +asm(".symver xmp_get_player_v43__, xmp_get_player@XMP_4.3"); +asm(".symver xmp_get_player_v44__, xmp_get_player@@XMP_4.4"); +#endif +LIBXMP_END_DECLS + +#define xmp_get_player__ xmp_get_player_v40__ +#else +#define xmp_get_player__ xmp_get_player +#endif + +int xmp_get_player__(xmp_context opaque, int parm) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_data *s = &ctx->s; + int ret = -XMP_ERROR_INVALID; + + if (parm == XMP_PLAYER_SMPCTL || parm == XMP_PLAYER_DEFPAN) { + // can read these at any time + } else if (parm != XMP_PLAYER_STATE && ctx->state < XMP_STATE_PLAYING) { + return -XMP_ERROR_STATE; + } + + switch (parm) { + case XMP_PLAYER_AMP: + ret = s->amplify; + break; + case XMP_PLAYER_MIX: + ret = s->mix; + break; + case XMP_PLAYER_INTERP: + ret = s->interp; + break; + case XMP_PLAYER_DSP: + ret = s->dsp; + break; + case XMP_PLAYER_FLAGS: + ret = p->player_flags; + break; + + /* 4.1 */ + case XMP_PLAYER_CFLAGS: + ret = p->flags; + break; + case XMP_PLAYER_SMPCTL: + ret = m->smpctl; + break; + case XMP_PLAYER_VOLUME: + ret = p->master_vol; + break; + case XMP_PLAYER_SMIX_VOLUME: + ret = p->smix_vol; + break; + + /* 4.2 */ + case XMP_PLAYER_STATE: + ret = ctx->state; + break; + + /* 4.3 */ + case XMP_PLAYER_DEFPAN: + ret = m->defpan; + break; + + /* 4.4 */ + case XMP_PLAYER_MODE: + ret = p->mode; + break; + case XMP_PLAYER_MIXER_TYPE: + ret = XMP_MIXER_STANDARD; + if (p->flags & XMP_FLAGS_A500) { + if (IS_AMIGA_MOD()) { +#ifdef LIBXMP_PAULA_SIMULATOR + if (p->filter) { + ret = XMP_MIXER_A500F; + } else { + ret = XMP_MIXER_A500; + } +#endif + } + } + break; + case XMP_PLAYER_VOICES: + ret = s->numvoc; + break; + } + + return ret; +} + +const char *const *xmp_get_format_list(void) +{ + return format_list(); +} + +void xmp_inject_event(xmp_context opaque, int channel, struct xmp_event *e) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + + if (ctx->state < XMP_STATE_PLAYING) + return; + + memcpy(&p->inject_event[channel], e, sizeof(struct xmp_event)); + p->inject_event[channel]._flag = 1; +} + +int xmp_set_instrument_path(xmp_context opaque, const char *path) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + + if (m->instrument_path != NULL) + free(m->instrument_path); + + m->instrument_path = libxmp_strdup(path); + if (m->instrument_path == NULL) { + return -XMP_ERROR_SYSTEM; + } + + return 0; +} + +int xmp_set_tempo_factor(xmp_context opaque, double val) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_data *s = &ctx->s; + int ticksize; + + if (val <= 0.0) { + return -1; + } + + val *= 10; + ticksize = s->freq * val * m->rrate / p->bpm / 1000 * sizeof(int); + if (ticksize > XMP_MAX_FRAMESIZE) { + return -1; + } + m->time_factor = val; + + return 0; +} diff --git a/thirdparty/libxmp/src/dataio.c b/thirdparty/libxmp/src/dataio.c new file mode 100644 index 000000000..3612a3631 --- /dev/null +++ b/thirdparty/libxmp/src/dataio.c @@ -0,0 +1,254 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "common.h" + + +#define read_byte(x) do { \ + (x) = fgetc(f); \ + if ((x) < 0) goto error; \ +} while (0) + +#define set_error(x) do { \ + if (err != NULL) *err = (x); \ +} while (0) + +uint8 read8(FILE *f, int *err) +{ + int a; + + read_byte(a); + set_error(0); + return a; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xff; +} + +int8 read8s(FILE *f, int *err) +{ + int a; + + read_byte(a); + set_error(0); + return (int8)a; + + error: + set_error(ferror(f) ? errno : EOF); + return 0; +} + +uint16 read16l(FILE *f, int *err) +{ + int a, b; + + read_byte(a); + read_byte(b); + + set_error(0); + return ((uint16)b << 8) | a; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffff; +} + +uint16 read16b(FILE *f, int *err) +{ + int a, b; + + read_byte(a); + read_byte(b); + + set_error(0); + return (a << 8) | b; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffff; +} + +uint32 read24l(FILE *f, int *err) +{ + int a, b, c; + + read_byte(a); + read_byte(b); + read_byte(c); + + set_error(0); + return (c << 16) | (b << 8) | a; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffffffff; +} + +uint32 read24b(FILE *f, int *err) +{ + int a, b, c; + + read_byte(a); + read_byte(b); + read_byte(c); + + set_error(0); + return (a << 16) | (b << 8) | c; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffffffff; +} + +uint32 read32l(FILE *f, int *err) +{ + int a, b, c, d; + + read_byte(a); + read_byte(b); + read_byte(c); + read_byte(d); + + set_error(0); + return (d << 24) | (c << 16) | (b << 8) | a; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffffffff; +} + +uint32 read32b(FILE *f, int *err) +{ + int a, b, c, d; + + read_byte(a); + read_byte(b); + read_byte(c); + read_byte(d); + + set_error(0); + return (a << 24) | (b << 16) | (c << 8) | d; + + error: + set_error(ferror(f) ? errno : EOF); + return 0xffffffff; +} + +uint16 readmem16l(const uint8 *m) +{ + uint32 a, b; + + a = m[0]; + b = m[1]; + + return (b << 8) | a; +} + +uint16 readmem16b(const uint8 *m) +{ + uint32 a, b; + + a = m[0]; + b = m[1]; + + return (a << 8) | b; +} + +uint32 readmem24l(const uint8 *m) +{ + uint32 a, b, c; + + a = m[0]; + b = m[1]; + c = m[2]; + + return (c << 16) | (b << 8) | a; +} + +uint32 readmem24b(const uint8 *m) +{ + uint32 a, b, c; + + a = m[0]; + b = m[1]; + c = m[2]; + + return (a << 16) | (b << 8) | c; +} + +uint32 readmem32l(const uint8 *m) +{ + uint32 a, b, c, d; + + a = m[0]; + b = m[1]; + c = m[2]; + d = m[3]; + + return (d << 24) | (c << 16) | (b << 8) | a; +} + +uint32 readmem32b(const uint8 *m) +{ + uint32 a, b, c, d; + + a = m[0]; + b = m[1]; + c = m[2]; + d = m[3]; + + return (a << 24) | (b << 16) | (c << 8) | d; +} + +#ifndef LIBXMP_CORE_PLAYER + +void write16l(FILE *f, uint16 w) +{ + write8(f, w & 0x00ff); + write8(f, (w & 0xff00) >> 8); +} + +void write16b(FILE *f, uint16 w) +{ + write8(f, (w & 0xff00) >> 8); + write8(f, w & 0x00ff); +} + +void write32l(FILE *f, uint32 w) +{ + write8(f, w & 0x000000ff); + write8(f, (w & 0x0000ff00) >> 8); + write8(f, (w & 0x00ff0000) >> 16); + write8(f, (w & 0xff000000) >> 24); +} + +void write32b(FILE *f, uint32 w) +{ + write8(f, (w & 0xff000000) >> 24); + write8(f, (w & 0x00ff0000) >> 16); + write8(f, (w & 0x0000ff00) >> 8); + write8(f, w & 0x000000ff); +} + +#endif diff --git a/thirdparty/libxmp/src/effects.c b/thirdparty/libxmp/src/effects.c new file mode 100644 index 000000000..5c5049cf7 --- /dev/null +++ b/thirdparty/libxmp/src/effects.c @@ -0,0 +1,1133 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "effects.h" +#include "period.h" +#include "virtual.h" +#include "mixer.h" +#ifndef LIBXMP_CORE_PLAYER +#include "extras.h" +#endif + +#define NOT_IMPLEMENTED +#define HAS_QUIRK(x) (m->quirk & (x)) + +#define SET_LFO_NOTZERO(lfo, depth, rate) do { \ + if ((depth) != 0) libxmp_lfo_set_depth(lfo, depth); \ + if ((rate) != 0) libxmp_lfo_set_rate(lfo, rate); \ +} while (0) + +#define EFFECT_MEMORY__(p, m) do { \ + if ((p) == 0) { (p) = (m); } else { (m) = (p); } \ +} while (0) + +/* ST3 effect memory is not a bug, but it's a weird implementation and it's + * unlikely to be supported in anything other than ST3 (or OpenMPT). + */ +#define EFFECT_MEMORY(p, m) do { \ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { \ + EFFECT_MEMORY__((p), xc->vol.memory); \ + } else { \ + EFFECT_MEMORY__((p), (m)); \ + } \ +} while (0) + +#define EFFECT_MEMORY_SETONLY(p, m) do { \ + EFFECT_MEMORY__((p), (m)); \ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { \ + if ((p) != 0) { xc->vol.memory = (p); } \ + } \ +} while (0) + +#define EFFECT_MEMORY_S3M(p) do { \ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { \ + EFFECT_MEMORY__((p), xc->vol.memory); \ + } \ +} while (0) + + +static void do_toneporta(struct context_data *ctx, + struct channel_data *xc, int note) +{ + struct module_data *m = &ctx->m; + struct xmp_instrument *instrument = &m->mod.xxi[xc->ins]; + struct xmp_subinstrument *sub; + int mapped_xpo = 0; + int mapped = 0; + + if (IS_VALID_NOTE(xc->key)) { + mapped = instrument->map[xc->key].ins; + } + + if (mapped >= instrument->nsm) { + mapped = 0; + } + + sub = &instrument->sub[mapped]; + + if (IS_VALID_NOTE(note - 1) && (uint32)xc->ins < m->mod.ins) { + note--; + if (IS_VALID_NOTE(xc->key_porta)) { + mapped_xpo = instrument->map[xc->key_porta].xpo; + } + xc->porta.target = libxmp_note_to_period(ctx, note + sub->xpo + + mapped_xpo, xc->finetune, xc->per_adj); + } + xc->porta.dir = xc->period < xc->porta.target ? 1 : -1; +} + +void libxmp_process_fx(struct context_data *ctx, struct channel_data *xc, int chn, + struct xmp_event *e, int fnum) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct flow_control *f = &p->flow; + uint8 note, fxp, fxt; + int h, l; + + /* key_porta is IT only */ + if (m->read_event_type != READ_EVENT_IT) { + xc->key_porta = xc->key; + } + + note = e->note; + if (fnum == 0) { + fxt = e->fxt; + fxp = e->fxp; + } else { + fxt = e->f2t; + fxp = e->f2p; + } + + switch (fxt) { + case FX_ARPEGGIO: + fx_arpeggio: + if (!HAS_QUIRK(QUIRK_ARPMEM) || fxp != 0) { + xc->arpeggio.val[0] = 0; + xc->arpeggio.val[1] = MSN(fxp); + xc->arpeggio.val[2] = LSN(fxp); + xc->arpeggio.size = 3; + } + break; + case FX_S3M_ARPEGGIO: + EFFECT_MEMORY(fxp, xc->arpeggio.memory); + goto fx_arpeggio; + +#ifndef LIBXMP_CORE_PLAYER + case FX_OKT_ARP3: + if (fxp != 0) { + xc->arpeggio.val[0] = -MSN(fxp); + xc->arpeggio.val[1] = 0; + xc->arpeggio.val[2] = LSN(fxp); + xc->arpeggio.size = 3; + } + break; + case FX_OKT_ARP4: + if (fxp != 0) { + xc->arpeggio.val[0] = 0; + xc->arpeggio.val[1] = LSN(fxp); + xc->arpeggio.val[2] = 0; + xc->arpeggio.val[3] = -MSN(fxp); + xc->arpeggio.size = 4; + } + break; + case FX_OKT_ARP5: + if (fxp != 0) { + xc->arpeggio.val[0] = LSN(fxp); + xc->arpeggio.val[1] = LSN(fxp); + xc->arpeggio.val[2] = 0; + xc->arpeggio.size = 3; + } + break; +#endif + case FX_PORTA_UP: /* Portamento up */ + EFFECT_MEMORY(fxp, xc->freq.memory); + + if (HAS_QUIRK(QUIRK_FINEFX) + && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) { + switch (MSN(fxp)) { + case 0xf: + fxp &= 0x0f; + goto fx_f_porta_up; + case 0xe: + fxp &= 0x0f; + fxp |= 0x10; + goto fx_xf_porta; + } + } + + SET(PITCHBEND); + + if (fxp != 0) { + xc->freq.slide = -fxp; + if (HAS_QUIRK(QUIRK_UNISLD)) + xc->porta.memory = fxp; + } else if (xc->freq.slide > 0) { + xc->freq.slide *= -1; + } + break; + case FX_PORTA_DN: /* Portamento down */ + EFFECT_MEMORY(fxp, xc->freq.memory); + + if (HAS_QUIRK(QUIRK_FINEFX) + && (fnum == 0 || !HAS_QUIRK(QUIRK_ITVPOR))) { + switch (MSN(fxp)) { + case 0xf: + fxp &= 0x0f; + goto fx_f_porta_dn; + case 0xe: + fxp &= 0x0f; + fxp |= 0x20; + goto fx_xf_porta; + } + } + + SET(PITCHBEND); + + if (fxp != 0) { + xc->freq.slide = fxp; + if (HAS_QUIRK(QUIRK_UNISLD)) + xc->porta.memory = fxp; + } else if (xc->freq.slide < 0) { + xc->freq.slide *= -1; + } + break; + case FX_TONEPORTA: /* Tone portamento */ + EFFECT_MEMORY_SETONLY(fxp, xc->porta.memory); + + if (fxp != 0) { + if (HAS_QUIRK(QUIRK_UNISLD)) /* IT compatible Gxx off */ + xc->freq.memory = fxp; + xc->porta.slide = fxp; + } + + if (HAS_QUIRK(QUIRK_IGSTPOR)) { + if (note == 0 && xc->porta.dir == 0) + break; + } + + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + + do_toneporta(ctx, xc, note); + + SET(TONEPORTA); + break; + + case FX_VIBRATO: /* Vibrato */ + EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory); + + SET(VIBRATO); + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp)); + break; + case FX_FINE_VIBRATO: /* Fine vibrato */ + EFFECT_MEMORY_SETONLY(fxp, xc->vibrato.memory); + + SET(VIBRATO); + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp), MSN(fxp)); + break; + + case FX_TONE_VSLIDE: /* Toneporta + vol slide */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + do_toneporta(ctx, xc, note); + SET(TONEPORTA); + goto fx_volslide; + case FX_VIBRA_VSLIDE: /* Vibrato + vol slide */ + SET(VIBRATO); + goto fx_volslide; + + case FX_TREMOLO: /* Tremolo */ + EFFECT_MEMORY(fxp, xc->tremolo.memory); + SET(TREMOLO); + SET_LFO_NOTZERO(&xc->tremolo.lfo, LSN(fxp), MSN(fxp)); + break; + + case FX_SETPAN: /* Set pan */ + if (HAS_QUIRK(QUIRK_PROTRACK)) { + break; + } + fx_setpan: + /* From OpenMPT PanOff.xm: + * "Another chapter of weird FT2 bugs: Note-Off + Note Delay + * + Volume Column Panning = Panning effect is ignored." + */ + if (!HAS_QUIRK(QUIRK_FT2BUGS) /* If not FT2 */ + || fnum == 0 /* or not vol column */ + || e->note != XMP_KEY_OFF /* or not keyoff */ + || e->fxt != FX_EXTENDED /* or not delay */ + || MSN(e->fxp) != EX_DELAY) { + xc->pan.val = fxp; + xc->pan.surround = 0; + } + xc->rpv = 0; /* storlek_20: set pan overrides random pan */ + xc->pan.surround = 0; + break; + case FX_OFFSET: /* Set sample offset */ + EFFECT_MEMORY(fxp, xc->offset.memory); + SET(OFFSET); + if (note) { + xc->offset.val &= xc->offset.val & ~0xffff; + xc->offset.val |= fxp << 8; + xc->offset.val2 = fxp << 8; + } + if (e->ins) { + xc->offset.val2 = fxp << 8; + } + break; + case FX_VOLSLIDE: /* Volume slide */ + fx_volslide: + /* S3M file volume slide note: + * DFy Fine volume down by y (...) If y is 0, the command will + * be treated as a volume slide up with a value of f (15). + * If a DFF command is specified, the volume will be slid + * up. + */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (l == 0xf && h != 0) { + xc->vol.memory = fxp; + fxp >>= 4; + goto fx_f_vslide_up; + } else if (h == 0xf && l != 0) { + xc->vol.memory = fxp; + fxp &= 0x0f; + goto fx_f_vslide_dn; + } + } + + /* recover memory */ + if (fxp == 0x00) { + if ((fxp = xc->vol.memory) != 0) + goto fx_volslide; + } + + SET(VOL_SLIDE); + + /* Skaven's 2nd reality (S3M) has volslide parameter D7 => pri + * down. Other trackers only compute volumes if the other + * parameter is 0, Fall from sky.xm has 2C => do nothing. + * Also don't assign xc->vol.memory if fxp is 0, see Guild + * of Sounds.xm + */ + if (fxp) { + xc->vol.memory = fxp; + h = MSN(fxp); + l = LSN(fxp); + if (fxp) { + if (HAS_QUIRK(QUIRK_VOLPDN)) { + xc->vol.slide = l ? -l : h; + } else { + xc->vol.slide = h ? h : -l; + } + } + } + + /* Mirko reports that a S3M with D0F effects created with ST321 + * should process volume slides in all frames like ST300. I + * suspect ST3/IT could be handling D0F effects like this. + */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + if (MSN(xc->vol.memory) == 0xf + || LSN(xc->vol.memory) == 0xf) { + SET(FINE_VOLS); + xc->vol.fslide = xc->vol.slide; + } + } + break; + case FX_VOLSLIDE_2: /* Secondary volume slide */ + SET(VOL_SLIDE_2); + if (fxp) { + h = MSN(fxp); + l = LSN(fxp); + xc->vol.slide2 = h ? h : -l; + } + break; + case FX_JUMP: /* Order jump */ + p->flow.pbreak = 1; + p->flow.jump = fxp; + /* effect B resets effect D in lower channels */ + p->flow.jumpline = 0; + break; + case FX_VOLSET: /* Volume set */ + SET(NEW_VOL); + xc->volume = fxp; + if (xc->split) { + p->xc_data[xc->pair].volume = xc->volume; + } + break; + case FX_BREAK: /* Pattern break */ + p->flow.pbreak = 1; + p->flow.jumpline = 10 * MSN(fxp) + LSN(fxp); + break; + case FX_EXTENDED: /* Extended effect */ + EFFECT_MEMORY_S3M(fxp); + fxt = fxp >> 4; + fxp &= 0x0f; + switch (fxt) { + case EX_FILTER: /* Amiga led filter */ + if (IS_AMIGA_MOD()) { + p->filter = !(fxp & 1); + } + break; + case EX_F_PORTA_UP: /* Fine portamento up */ + EFFECT_MEMORY(fxp, xc->fine_porta.up_memory); + goto fx_f_porta_up; + case EX_F_PORTA_DN: /* Fine portamento down */ + EFFECT_MEMORY(fxp, xc->fine_porta.down_memory); + goto fx_f_porta_dn; + case EX_GLISS: /* Glissando toggle */ + if (fxp) { + SET_NOTE(NOTE_GLISSANDO); + } else { + RESET_NOTE(NOTE_GLISSANDO); + } + break; + case EX_VIBRATO_WF: /* Set vibrato waveform */ + fxp &= 3; + libxmp_lfo_set_waveform(&xc->vibrato.lfo, fxp); + break; + case EX_FINETUNE: /* Set finetune */ + if (!HAS_QUIRK(QUIRK_FT2BUGS) || note > 0) { + xc->finetune = (int8)(fxp << 4); + } + break; + case EX_PATTERN_LOOP: /* Loop pattern */ + if (fxp == 0) { + /* mark start of loop */ + f->loop[chn].start = p->row; + if (HAS_QUIRK(QUIRK_FT2BUGS)) + p->flow.jumpline = p->row; + } else { + /* end of loop */ + if (f->loop[chn].count) { + if (--f->loop[chn].count) { + /* **** H:FIXME **** */ + f->loop_chn = ++chn; + } else { + if (HAS_QUIRK(QUIRK_S3MLOOP)) + f->loop[chn].start = + p->row + 1; + } + } else { + f->loop[chn].count = fxp; + f->loop_chn = ++chn; + } + } + break; + case EX_TREMOLO_WF: /* Set tremolo waveform */ + libxmp_lfo_set_waveform(&xc->tremolo.lfo, fxp & 3); + break; + case EX_SETPAN: + fxp <<= 4; + goto fx_setpan; + case EX_RETRIG: /* Retrig note */ + SET(RETRIG); + xc->retrig.val = fxp; + xc->retrig.count = LSN(xc->retrig.val) + 1; + xc->retrig.type = 0; + xc->retrig.limit = 0; + break; + case EX_F_VSLIDE_UP: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->fine_vol.up_memory); + goto fx_f_vslide_up; + case EX_F_VSLIDE_DN: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->fine_vol.down_memory); + goto fx_f_vslide_dn; + case EX_CUT: /* Cut note */ + SET(RETRIG); + SET_NOTE(NOTE_CUT); /* for IT cut-carry */ + xc->retrig.val = fxp + 1; + xc->retrig.count = xc->retrig.val; + xc->retrig.type = 0x10; + break; + case EX_DELAY: /* Note delay */ + /* computed at frame loop */ + break; + case EX_PATT_DELAY: /* Pattern delay */ + goto fx_patt_delay; + case EX_INVLOOP: /* Invert loop / funk repeat */ + xc->invloop.speed = fxp; + break; + } + break; + case FX_SPEED: /* Set speed */ + if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK) { + goto fx_s3m_speed; + } + + /* speedup.xm needs BPM = 20 */ + if (fxp < 0x20) { + goto fx_s3m_speed; + } + goto fx_s3m_bpm; + + case FX_FINETUNE: + xc->finetune = (int16) (fxp - 0x80); + break; + + case FX_F_VSLIDE_UP: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->fine_vol.up_memory); + fx_f_vslide_up: + SET(FINE_VOLS); + xc->vol.fslide = fxp; + break; + case FX_F_VSLIDE_DN: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->fine_vol.up_memory); + fx_f_vslide_dn: + SET(FINE_VOLS); + xc->vol.fslide = -fxp; + break; + + case FX_F_PORTA_UP: /* Fine portamento up */ + fx_f_porta_up: + if (fxp) { + SET(FINE_BEND); + xc->freq.fslide = -fxp; + } + break; + case FX_F_PORTA_DN: /* Fine portamento down */ + fx_f_porta_dn: + if (fxp) { + SET(FINE_BEND); + xc->freq.fslide = fxp; + } + break; + case FX_PATT_DELAY: + fx_patt_delay: + if (m->read_event_type != READ_EVENT_ST3 || !p->flow.delay) { + p->flow.delay = fxp; + } + break; + + case FX_S3M_SPEED: /* Set S3M speed */ + EFFECT_MEMORY_S3M(fxp); + fx_s3m_speed: + if (fxp) { + p->speed = fxp; +#ifndef LIBXMP_CORE_PLAYER + p->st26_speed = 0; +#endif + } + break; + case FX_S3M_BPM: /* Set S3M BPM */ + fx_s3m_bpm: { + /* Lower time factor in MED allows lower BPM values */ + int min_bpm = (int)(0.5 + m->time_factor * XMP_MIN_BPM / 10); + if (fxp < min_bpm) + fxp = min_bpm; + p->bpm = fxp; + p->frame_time = m->time_factor * m->rrate / p->bpm; + break; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + case FX_IT_BPM: /* Set IT BPM */ + if (MSN(fxp) == 0) { + SET(TEMPO_SLIDE); + if (LSN(fxp)) /* T0x - Tempo slide down by x */ + xc->tempo.slide = -LSN(fxp); + /* T00 - Repeat previous slide */ + } else if (MSN(fxp) == 1) { /* T1x - Tempo slide up by x */ + SET(TEMPO_SLIDE); + xc->tempo.slide = LSN(fxp); + } else { + if (fxp < XMP_MIN_BPM) + fxp = XMP_MIN_BPM; + p->bpm = fxp; + } + p->frame_time = m->time_factor * m->rrate / p->bpm; + break; + case FX_IT_ROWDELAY: + if (!f->rowdelay_set) { + f->rowdelay = fxp; + f->rowdelay_set = ROWDELAY_ON | ROWDELAY_FIRST_FRAME; + } + break; + + /* From the OpenMPT VolColMemory.it test case: + * "Volume column commands a, b, c and d (volume slide) share one + * effect memory, but it should not be shared with Dxy in the effect + * column. + */ + case FX_VSLIDE_UP_2: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(VOL_SLIDE_2); + xc->vol.slide2 = fxp; + break; + case FX_VSLIDE_DN_2: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(VOL_SLIDE_2); + xc->vol.slide2 = -fxp; + break; + case FX_F_VSLIDE_UP_2: /* Fine volume slide up */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(FINE_VOLS_2); + xc->vol.fslide2 = fxp; + break; + case FX_F_VSLIDE_DN_2: /* Fine volume slide down */ + EFFECT_MEMORY(fxp, xc->vol.memory2); + SET(FINE_VOLS_2); + xc->vol.fslide2 = -fxp; + break; + case FX_IT_BREAK: /* Pattern break with hex parameter */ + if (!f->loop_chn) + { + p->flow.pbreak = 1; + p->flow.jumpline = fxp; + } + break; + +#endif + + case FX_GLOBALVOL: /* Set global volume */ + if (fxp > m->gvolbase) { + p->gvol = m->gvolbase; + } else { + p->gvol = fxp; + } + break; + case FX_GVOL_SLIDE: /* Global volume slide */ + fx_gvolslide: + if (fxp) { + SET(GVOL_SLIDE); + xc->gvol.memory = fxp; + h = MSN(fxp); + l = LSN(fxp); + + if (HAS_QUIRK(QUIRK_FINEFX)) { + if (l == 0xf && h != 0) { + xc->gvol.slide = 0; + xc->gvol.fslide = h; + } else if (h == 0xf && l != 0) { + xc->gvol.slide = 0; + xc->gvol.fslide = -l; + } else { + xc->gvol.slide = h ? h : -l; + xc->gvol.fslide = 0; + } + } else { + xc->gvol.slide = h ? h : -l; + xc->gvol.fslide = 0; + } + } else { + if ((fxp = xc->gvol.memory) != 0) { + goto fx_gvolslide; + } + } + break; + case FX_KEYOFF: /* Key off */ + xc->keyoff = fxp + 1; + break; + case FX_ENVPOS: /* Set envelope position */ + /* From OpenMPT SetEnvPos.xm: + * "When using the Lxx effect, Fasttracker 2 only sets the + * panning envelope position if the volume envelope’s sustain + * flag is set. + */ + if (HAS_QUIRK(QUIRK_FT2BUGS)) { + struct xmp_instrument *instrument; + instrument = libxmp_get_instrument(ctx, xc->ins); + if (instrument != NULL) { + if (instrument->aei.flg & XMP_ENVELOPE_SUS) { + xc->p_idx = fxp; + } + } + } else { + xc->p_idx = fxp; + } + xc->v_idx = fxp; + xc->f_idx = fxp; + break; + case FX_PANSLIDE: /* Pan slide (XM) */ + EFFECT_MEMORY(fxp, xc->pan.memory); + SET(PAN_SLIDE); + xc->pan.slide = LSN(fxp) - MSN(fxp); + break; + case FX_PANSL_NOMEM: /* Pan slide (XM volume column) */ + SET(PAN_SLIDE); + xc->pan.slide = LSN(fxp) - MSN(fxp); + break; + +#ifndef LIBXMP_CORE_DISABLE_IT + case FX_IT_PANSLIDE: /* Pan slide w/ fine pan (IT) */ + SET(PAN_SLIDE); + if (fxp) { + if (MSN(fxp) == 0xf) { + xc->pan.slide = 0; + xc->pan.fslide = LSN(fxp); + } else if (LSN(fxp) == 0xf) { + xc->pan.slide = 0; + xc->pan.fslide = -MSN(fxp); + } else { + SET(PAN_SLIDE); + xc->pan.slide = LSN(fxp) - MSN(fxp); + xc->pan.fslide = 0; + } + } + break; +#endif + + case FX_MULTI_RETRIG: /* Multi retrig */ + EFFECT_MEMORY_S3M(fxp); + if (fxp) { + xc->retrig.val = fxp; + xc->retrig.type = MSN(xc->retrig.val); + } + if (note) { + xc->retrig.count = LSN(xc->retrig.val) + 1; + } + xc->retrig.limit = 0; + SET(RETRIG); + break; + case FX_TREMOR: /* Tremor */ + EFFECT_MEMORY(fxp, xc->tremor.memory); + xc->tremor.up = MSN(fxp); + xc->tremor.down = LSN(fxp); + if (IS_PLAYER_MODE_FT2()) { + xc->tremor.count |= 0x80; + } else { + if (xc->tremor.up == 0) { + xc->tremor.up++; + } + if (xc->tremor.down == 0) { + xc->tremor.down++; + } + } + SET(TREMOR); + break; + case FX_XF_PORTA: /* Extra fine portamento */ + fx_xf_porta: + SET(FINE_BEND); + switch (MSN(fxp)) { + case 1: + xc->freq.fslide = -0.25 * LSN(fxp); + break; + case 2: + xc->freq.fslide = 0.25 * LSN(fxp); + break; + } + break; + case FX_SURROUND: + xc->pan.surround = fxp; + break; + case FX_REVERSE: /* Play forward/backward */ + libxmp_virt_reverse(ctx, chn, fxp); + break; + +#ifndef LIBXMP_CORE_DISABLE_IT + case FX_TRK_VOL: /* Track volume setting */ + if (fxp <= m->volbase) { + xc->mastervol = fxp; + } + break; + case FX_TRK_VSLIDE: /* Track volume slide */ + if (fxp == 0) { + if ((fxp = xc->trackvol.memory) == 0) + break; + } + + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (h == 0xf && l != 0) { + xc->trackvol.memory = fxp; + fxp &= 0x0f; + goto fx_trk_fvslide; + } else if (l == 0xf && h != 0) { + xc->trackvol.memory = fxp; + fxp &= 0xf0; + goto fx_trk_fvslide; + } + } + + SET(TRK_VSLIDE); + if (fxp) { + h = MSN(fxp); + l = LSN(fxp); + + xc->trackvol.memory = fxp; + if (HAS_QUIRK(QUIRK_VOLPDN)) { + xc->trackvol.slide = l ? -l : h; + } else { + xc->trackvol.slide = h ? h : -l; + } + } + + break; + case FX_TRK_FVSLIDE: /* Track fine volume slide */ + fx_trk_fvslide: + SET(TRK_FVSLIDE); + if (fxp) { + xc->trackvol.fslide = MSN(fxp) - LSN(fxp); + } + break; + + case FX_IT_INSTFUNC: + switch (fxp) { + case 0: /* Past note cut */ + libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_CUT); + break; + case 1: /* Past note off */ + libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_OFF); + break; + case 2: /* Past note fade */ + libxmp_virt_pastnote(ctx, chn, VIRT_ACTION_FADE); + break; + case 3: /* Set NNA to note cut */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CUT); + break; + case 4: /* Set NNA to continue */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CONT); + break; + case 5: /* Set NNA to note off */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_OFF); + break; + case 6: /* Set NNA to note fade */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_FADE); + break; + case 7: /* Turn off volume envelope */ + SET_PER(VENV_PAUSE); + break; + case 8: /* Turn on volume envelope */ + RESET_PER(VENV_PAUSE); + break; + case 9: /* Turn off pan envelope */ + SET_PER(PENV_PAUSE); + break; + case 0xa: /* Turn on pan envelope */ + RESET_PER(PENV_PAUSE); + break; + case 0xb: /* Turn off pitch envelope */ + SET_PER(FENV_PAUSE); + break; + case 0xc: /* Turn on pitch envelope */ + RESET_PER(FENV_PAUSE); + break; + } + break; + case FX_FLT_CUTOFF: + xc->filter.cutoff = fxp; + break; + case FX_FLT_RESN: + xc->filter.resonance = fxp; + break; + case FX_MACRO_SET: + xc->macro.active = LSN(fxp); + break; + case FX_MACRO: + SET(MIDI_MACRO); + xc->macro.val = fxp; + xc->macro.slide = 0; + break; + case FX_MACROSMOOTH: + if (ctx->p.speed && xc->macro.val < 0x80) { + SET(MIDI_MACRO); + xc->macro.target = fxp; + xc->macro.slide = ((float)fxp - xc->macro.val) / ctx->p.speed; + } + break; + case FX_PANBRELLO: /* Panbrello */ + SET(PANBRELLO); + SET_LFO_NOTZERO(&xc->panbrello.lfo, LSN(fxp) << 4, MSN(fxp)); + break; + case FX_PANBRELLO_WF: /* Panbrello waveform */ + libxmp_lfo_set_waveform(&xc->panbrello.lfo, fxp & 3); + break; + case FX_HIOFFSET: /* High offset */ + xc->offset.val &= 0xffff; + xc->offset.val |= fxp << 16; + break; +#endif + +#ifndef LIBXMP_CORE_PLAYER + + /* SFX effects */ + case FX_VOL_ADD: + if (!IS_VALID_INSTRUMENT(xc->ins)) { + break; + } + SET(NEW_VOL); + xc->volume = m->mod.xxi[xc->ins].sub[0].vol + fxp; + if (xc->volume > m->volbase) { + xc->volume = m->volbase; + } + break; + case FX_VOL_SUB: + if (!IS_VALID_INSTRUMENT(xc->ins)) { + break; + } + SET(NEW_VOL); + xc->volume = m->mod.xxi[xc->ins].sub[0].vol - fxp; + if (xc->volume < 0) { + xc->volume =0; + } + break; + case FX_PITCH_ADD: + SET_PER(TONEPORTA); + xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, 0) + + fxp; + xc->porta.slide = 2; + xc->porta.dir = 1; + break; + case FX_PITCH_SUB: + SET_PER(TONEPORTA); + xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, 0) + - fxp; + xc->porta.slide = 2; + xc->porta.dir = -1; + break; + + /* Saga Musix says: + * + * "When both nibbles of an Fxx command are set, SoundTracker 2.6 + * applies the both values alternatingly, first the high nibble, + * then the low nibble on the next row, then the high nibble again... + * If only the high nibble is set, it should act like if only the low + * nibble is set (i.e. F30 is the same as F03). + */ + case FX_ICE_SPEED: + if (fxp) { + if (LSN(fxp)) { + p->st26_speed = (MSN(fxp) << 8) | LSN(fxp); + } else { + p->st26_speed = MSN(fxp); + } + } + break; + + case FX_VOLSLIDE_UP: /* Vol slide with uint8 arg */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (h == 0xf && l != 0) { + fxp &= 0x0f; + goto fx_f_vslide_up; + } + } + + if (fxp) + xc->vol.slide = fxp; + SET(VOL_SLIDE); + break; + case FX_VOLSLIDE_DN: /* Vol slide with uint8 arg */ + if (HAS_QUIRK(QUIRK_FINEFX)) { + h = MSN(fxp); + l = LSN(fxp); + if (h == 0xf && l != 0) { + fxp &= 0x0f; + goto fx_f_vslide_dn; + } + } + + if (fxp) + xc->vol.slide = -fxp; + SET(VOL_SLIDE); + break; + case FX_F_VSLIDE: /* Fine volume slide */ + SET(FINE_VOLS); + if (fxp) { + h = MSN(fxp); + l = LSN(fxp); + xc->vol.fslide = h ? h : -l; + } + break; + case FX_NSLIDE_DN: + case FX_NSLIDE_UP: + case FX_NSLIDE_R_DN: + case FX_NSLIDE_R_UP: + if (fxp != 0) { + if (fxt == FX_NSLIDE_R_DN || fxt == FX_NSLIDE_R_UP) { + xc->retrig.val = MSN(fxp); + xc->retrig.count = MSN(fxp) + 1; + xc->retrig.type = 0; + xc->retrig.limit = 0; + } + + if (fxt == FX_NSLIDE_UP || fxt == FX_NSLIDE_R_UP) + xc->noteslide.slide = LSN(fxp); + else + xc->noteslide.slide = -LSN(fxp); + + xc->noteslide.count = xc->noteslide.speed = MSN(fxp); + } + if (fxt == FX_NSLIDE_R_DN || fxt == FX_NSLIDE_R_UP) + SET(RETRIG); + SET(NOTE_SLIDE); + break; + case FX_NSLIDE2_DN: + SET(NOTE_SLIDE); + xc->noteslide.slide = -fxp; + xc->noteslide.count = xc->noteslide.speed = 1; + break; + case FX_NSLIDE2_UP: + SET(NOTE_SLIDE); + xc->noteslide.slide = fxp; + xc->noteslide.count = xc->noteslide.speed = 1; + break; + case FX_F_NSLIDE_DN: + SET(FINE_NSLIDE); + xc->noteslide.fslide = -fxp; + break; + case FX_F_NSLIDE_UP: + SET(FINE_NSLIDE); + xc->noteslide.fslide = fxp; + break; + + case FX_PER_VIBRATO: /* Persistent vibrato */ + if (LSN(fxp) != 0) { + SET_PER(VIBRATO); + } else { + RESET_PER(VIBRATO); + } + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 2, MSN(fxp)); + break; + case FX_PER_PORTA_UP: /* Persistent portamento up */ + SET_PER(PITCHBEND); + xc->freq.slide = -fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_PER_PORTA_DN: /* Persistent portamento down */ + SET_PER(PITCHBEND); + xc->freq.slide = fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_PER_TPORTA: /* Persistent tone portamento */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + SET_PER(TONEPORTA); + do_toneporta(ctx, xc, note); + xc->porta.slide = fxp; + if (fxp == 0) + RESET_PER(TONEPORTA); + break; + case FX_PER_VSLD_UP: /* Persistent volslide up */ + SET_PER(VOL_SLIDE); + xc->vol.slide = fxp; + if (fxp == 0) + RESET_PER(VOL_SLIDE); + break; + case FX_PER_VSLD_DN: /* Persistent volslide down */ + SET_PER(VOL_SLIDE); + xc->vol.slide = -fxp; + if (fxp == 0) + RESET_PER(VOL_SLIDE); + break; + case FX_VIBRATO2: /* Deep vibrato (2x) */ + SET(VIBRATO); + SET_LFO_NOTZERO(&xc->vibrato.lfo, LSN(fxp) << 3, MSN(fxp)); + break; + case FX_SPEED_CP: /* Set speed and ... */ + if (fxp) { + p->speed = fxp; + p->st26_speed = 0; + } + /* fall through */ + case FX_PER_CANCEL: /* Cancel persistent effects */ + xc->per_flags = 0; + break; + + /* 669 effects */ + + case FX_669_PORTA_UP: /* 669 portamento up */ + SET_PER(PITCHBEND); + xc->freq.slide = 80 * fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_669_PORTA_DN: /* 669 portamento down */ + SET_PER(PITCHBEND); + xc->freq.slide = -80 * fxp; + if ((xc->freq.memory = fxp) == 0) + RESET_PER(PITCHBEND); + break; + case FX_669_TPORTA: /* 669 tone portamento */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + SET_PER(TONEPORTA); + do_toneporta(ctx, xc, note); + xc->porta.slide = 40 * fxp; + if (fxp == 0) + RESET_PER(TONEPORTA); + break; + case FX_669_FINETUNE: /* 669 finetune */ + xc->finetune = 80 * (int8)fxp; + break; + case FX_669_VIBRATO: /* 669 vibrato */ + if (LSN(fxp) != 0) { + libxmp_lfo_set_waveform(&xc->vibrato.lfo, 669); + SET_PER(VIBRATO); + } else { + RESET_PER(VIBRATO); + } + SET_LFO_NOTZERO(&xc->vibrato.lfo, 669, 1); + break; + + /* ULT effects */ + + case FX_ULT_TPORTA: /* ULT tone portamento */ + /* Like normal persistent tone portamento, except: + * + * 1) Despite the documentation claiming 300 cancels tone + * portamento, it actually reuses the last parameter. + * + * 2) A 3xx without a note will reuse the last target note. + */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + SET_PER(TONEPORTA); + EFFECT_MEMORY(fxp, xc->porta.memory); + EFFECT_MEMORY(note, xc->porta.note_memory); + do_toneporta(ctx, xc, note); + xc->porta.slide = fxp; + if (fxp == 0) + RESET_PER(TONEPORTA); + break; + + /* Archimedes (!Tracker, Digital Symphony, et al.) effects */ + + case FX_LINE_JUMP: /* !Tracker and Digital Symphony "Line Jump" */ + /* Jump to a line within the current order. In Digital Symphony + * this can be combined with position jump (like pattern break) + * and overrides the pattern break line in lower channels. */ + if (p->flow.pbreak == 0) { + p->flow.pbreak = 1; + p->flow.jump = p->ord; + } + p->flow.jumpline = fxp; + p->flow.jump_in_pat = p->ord; + break; +#endif + + default: +#ifndef LIBXMP_CORE_PLAYER + libxmp_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum); +#endif + break; + } +} diff --git a/thirdparty/libxmp/src/effects.h b/thirdparty/libxmp/src/effects.h new file mode 100644 index 000000000..fb48b495d --- /dev/null +++ b/thirdparty/libxmp/src/effects.h @@ -0,0 +1,163 @@ +#ifndef LIBXMP_EFFECTS_H +#define LIBXMP_EFFECTS_H + +/* Protracker effects */ +#define FX_ARPEGGIO 0x00 +#define FX_PORTA_UP 0x01 +#define FX_PORTA_DN 0x02 +#define FX_TONEPORTA 0x03 +#define FX_VIBRATO 0x04 +#define FX_TONE_VSLIDE 0x05 +#define FX_VIBRA_VSLIDE 0x06 +#define FX_TREMOLO 0x07 +#define FX_OFFSET 0x09 +#define FX_VOLSLIDE 0x0a +#define FX_JUMP 0x0b +#define FX_VOLSET 0x0c +#define FX_BREAK 0x0d +#define FX_EXTENDED 0x0e +#define FX_SPEED 0x0f + +/* Fast tracker effects */ +#define FX_SETPAN 0x08 + +/* Fast Tracker II effects */ +#define FX_GLOBALVOL 0x10 +#define FX_GVOL_SLIDE 0x11 +#define FX_KEYOFF 0x14 +#define FX_ENVPOS 0x15 +#define FX_PANSLIDE 0x19 +#define FX_MULTI_RETRIG 0x1b +#define FX_TREMOR 0x1d +#define FX_XF_PORTA 0x21 + +/* Protracker extended effects */ +#define EX_FILTER 0x00 +#define EX_F_PORTA_UP 0x01 +#define EX_F_PORTA_DN 0x02 +#define EX_GLISS 0x03 +#define EX_VIBRATO_WF 0x04 +#define EX_FINETUNE 0x05 +#define EX_PATTERN_LOOP 0x06 +#define EX_TREMOLO_WF 0x07 +#define EX_SETPAN 0x08 +#define EX_RETRIG 0x09 +#define EX_F_VSLIDE_UP 0x0a +#define EX_F_VSLIDE_DN 0x0b +#define EX_CUT 0x0c +#define EX_DELAY 0x0d +#define EX_PATT_DELAY 0x0e +#define EX_INVLOOP 0x0f + +#ifndef LIBXMP_CORE_PLAYER +/* Oktalyzer effects */ +#define FX_OKT_ARP3 0x70 +#define FX_OKT_ARP4 0x71 +#define FX_OKT_ARP5 0x72 +#define FX_NSLIDE2_DN 0x73 +#define FX_NSLIDE2_UP 0x74 +#define FX_F_NSLIDE_DN 0x75 +#define FX_F_NSLIDE_UP 0x76 + +/* Persistent effects -- for FNK */ +#define FX_PER_PORTA_DN 0x78 +#define FX_PER_PORTA_UP 0x79 +#define FX_PER_TPORTA 0x7a +#define FX_PER_VIBRATO 0x7b +#define FX_PER_VSLD_UP 0x7c +#define FX_PER_VSLD_DN 0x7d +#define FX_SPEED_CP 0x7e +#define FX_PER_CANCEL 0x7f + +/* 669 frequency based effects */ +#define FX_669_PORTA_UP 0x60 +#define FX_669_PORTA_DN 0x61 +#define FX_669_TPORTA 0x62 +#define FX_669_FINETUNE 0x63 +#define FX_669_VIBRATO 0x64 + +/* FAR effects */ +#define FX_FAR_PORTA_UP 0x65 /* FAR pitch offset up */ +#define FX_FAR_PORTA_DN 0x66 /* FAR pitch offset down */ +#define FX_FAR_TPORTA 0x67 /* FAR persistent tone portamento */ +#define FX_FAR_TEMPO 0x68 /* FAR coarse tempo and tempo mode */ +#define FX_FAR_F_TEMPO 0x69 /* FAR fine tempo slide up/down */ +#define FX_FAR_VIBDEPTH 0x6a /* FAR set vibrato depth */ +#define FX_FAR_VIBRATO 0x6b /* FAR persistent vibrato */ +#define FX_FAR_SLIDEVOL 0x6c /* FAR persistent slide-to-volume */ +#define FX_FAR_RETRIG 0x6d /* FAR retrigger */ +#define FX_FAR_DELAY 0x6e /* FAR note offset */ + +/* Other frequency based effects (ULT, etc) */ +#define FX_ULT_TPORTA 0x6f +#endif + +#ifndef LIBXMP_CORE_DISABLE_IT +/* IT effects */ +#define FX_TRK_VOL 0x80 +#define FX_TRK_VSLIDE 0x81 +#define FX_TRK_FVSLIDE 0x82 +#define FX_IT_INSTFUNC 0x83 +#define FX_FLT_CUTOFF 0x84 +#define FX_FLT_RESN 0x85 +#define FX_IT_BPM 0x87 +#define FX_IT_ROWDELAY 0x88 +#define FX_IT_PANSLIDE 0x89 +#define FX_PANBRELLO 0x8a +#define FX_PANBRELLO_WF 0x8b +#define FX_HIOFFSET 0x8c +#define FX_IT_BREAK 0x8e /* like FX_BREAK with hex parameter */ +#define FX_MACRO_SET 0xbd /* Set active IT parametered MIDI macro */ +#define FX_MACRO 0xbe /* Execute IT MIDI macro */ +#define FX_MACROSMOOTH 0xbf /* Execute IT MIDI macro slide */ +#endif + +#ifndef LIBXMP_CORE_PLAYER +/* MED effects */ +#define FX_HOLD_DECAY 0x90 +#define FX_SETPITCH 0x91 +#define FX_VIBRATO2 0x92 + +/* PTM effects */ +#define FX_NSLIDE_DN 0x9c /* IMF/PTM note slide down */ +#define FX_NSLIDE_UP 0x9d /* IMF/PTM note slide up */ +#define FX_NSLIDE_R_UP 0x9e /* PTM note slide down with retrigger */ +#define FX_NSLIDE_R_DN 0x9f /* PTM note slide up with retrigger */ + +/* Extra effects */ +#define FX_VOLSLIDE_UP 0xa0 /* SFX, MDL */ +#define FX_VOLSLIDE_DN 0xa1 +#define FX_F_VSLIDE 0xa5 /* IMF/MDL */ +#define FX_CHORUS 0xa9 /* IMF */ +#define FX_ICE_SPEED 0xa2 +#define FX_REVERB 0xaa /* IMF */ +#define FX_MED_HOLD 0xb1 /* MMD hold/decay */ +#define FX_MEGAARP 0xb2 /* Smaksak effect 7: MegaArp */ +#define FX_VOL_ADD 0xb6 /* SFX change volume up */ +#define FX_VOL_SUB 0xb7 /* SFX change volume down */ +#define FX_PITCH_ADD 0xb8 /* SFX add steps to current note */ +#define FX_PITCH_SUB 0xb9 /* SFX add steps to current note */ +#define FX_LINE_JUMP 0xba /* Archimedes jump to line in current order */ +#endif + +#define FX_SURROUND 0x8d /* S3M/IT */ +#define FX_REVERSE 0x8f /* XM/IT/others: play forward/reverse */ +#define FX_S3M_SPEED 0xa3 /* S3M */ +#define FX_VOLSLIDE_2 0xa4 +#define FX_FINETUNE 0xa6 +#define FX_S3M_BPM 0xab /* S3M */ +#define FX_FINE_VIBRATO 0xac /* S3M/PTM/IMF/LIQ */ +#define FX_F_VSLIDE_UP 0xad /* MMD */ +#define FX_F_VSLIDE_DN 0xae /* MMD */ +#define FX_F_PORTA_UP 0xaf /* MMD */ +#define FX_F_PORTA_DN 0xb0 /* MMD */ +#define FX_PATT_DELAY 0xb3 /* MMD */ +#define FX_S3M_ARPEGGIO 0xb4 +#define FX_PANSL_NOMEM 0xb5 /* XM volume column */ + +#define FX_VSLIDE_UP_2 0xc0 /* IT volume column volume slide */ +#define FX_VSLIDE_DN_2 0xc1 +#define FX_F_VSLIDE_UP_2 0xc2 +#define FX_F_VSLIDE_DN_2 0xc3 + +#endif /* LIBXMP_EFFECTS_H */ diff --git a/thirdparty/libxmp/src/extras.c b/thirdparty/libxmp/src/extras.c new file mode 100644 index 000000000..f56b7b125 --- /dev/null +++ b/thirdparty/libxmp/src/extras.c @@ -0,0 +1,163 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "extras.h" +#include "med_extras.h" +#include "hmn_extras.h" +#include "far_extras.h" + +/* + * Module extras + */ + +void libxmp_release_module_extras(struct context_data *ctx) +{ + struct module_data *m = &ctx->m; + + if (HAS_MED_MODULE_EXTRAS(*m)) + libxmp_med_release_module_extras(m); + else if (HAS_HMN_MODULE_EXTRAS(*m)) + libxmp_hmn_release_module_extras(m); + else if (HAS_FAR_MODULE_EXTRAS(*m)) + libxmp_far_release_module_extras(m); +} + +/* + * Channel extras + */ + +int libxmp_new_channel_extras(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + + if (HAS_MED_MODULE_EXTRAS(*m)) { + if (libxmp_med_new_channel_extras(xc) < 0) + return -1; + } else if (HAS_HMN_MODULE_EXTRAS(*m)) { + if (libxmp_hmn_new_channel_extras(xc) < 0) + return -1; + } else if (HAS_FAR_MODULE_EXTRAS(*m)) { + if (libxmp_far_new_channel_extras(xc) < 0) + return -1; + } + + return 0; +} + +void libxmp_release_channel_extras(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + + if (HAS_MED_CHANNEL_EXTRAS(*m)) + libxmp_med_release_channel_extras(xc); + else if (HAS_HMN_CHANNEL_EXTRAS(*m)) + libxmp_hmn_release_channel_extras(xc); + else if (HAS_FAR_CHANNEL_EXTRAS(*m)) + libxmp_far_release_channel_extras(xc); +} + +void libxmp_reset_channel_extras(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + + if (HAS_MED_CHANNEL_EXTRAS(*m)) + libxmp_med_reset_channel_extras(xc); + else if (HAS_HMN_CHANNEL_EXTRAS(*m)) + libxmp_hmn_reset_channel_extras(xc); + else if (HAS_FAR_CHANNEL_EXTRAS(*m)) + libxmp_far_reset_channel_extras(xc); +} + +/* + * Player extras + */ + +void libxmp_play_extras(struct context_data *ctx, struct channel_data *xc, int chn) +{ + struct module_data *m = &ctx->m; + + if (HAS_FAR_CHANNEL_EXTRAS(*xc)) + libxmp_far_play_extras(ctx, xc, chn); + + if (xc->ins >= m->mod.ins) /* SFX instruments have no extras */ + return; + + if (HAS_MED_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])) + libxmp_med_play_extras(ctx, xc, chn); + else if (HAS_HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])) + libxmp_hmn_play_extras(ctx, xc, chn); +} + +int libxmp_extras_get_volume(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + int vol; + + if (xc->ins >= m->mod.ins) + vol = xc->volume; + else if (HAS_MED_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])) + vol = MED_CHANNEL_EXTRAS(*xc)->volume * xc->volume / 64; + else if (HAS_HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])) + vol = HMN_CHANNEL_EXTRAS(*xc)->volume * xc->volume / 64; + else + vol = xc->volume; + + return vol; +} + +int libxmp_extras_get_period(struct context_data *ctx, struct channel_data *xc) +{ + int period; + + if (HAS_MED_CHANNEL_EXTRAS(*xc)) + period = libxmp_med_change_period(ctx, xc); + else period = 0; + + return period; +} + +int libxmp_extras_get_linear_bend(struct context_data *ctx, struct channel_data *xc) +{ + int linear_bend; + + if (HAS_MED_CHANNEL_EXTRAS(*xc)) + linear_bend = libxmp_med_linear_bend(ctx, xc); + else if (HAS_HMN_CHANNEL_EXTRAS(*xc)) + linear_bend = libxmp_hmn_linear_bend(ctx, xc); + else + linear_bend = 0; + + return linear_bend; +} + +void libxmp_extras_process_fx(struct context_data *ctx, struct channel_data *xc, + int chn, uint8 note, uint8 fxt, uint8 fxp, int fnum) +{ + if (HAS_MED_CHANNEL_EXTRAS(*xc)) + libxmp_med_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum); + else if (HAS_HMN_CHANNEL_EXTRAS(*xc)) + libxmp_hmn_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum); + else if (HAS_FAR_CHANNEL_EXTRAS(*xc)) + libxmp_far_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum); +} diff --git a/thirdparty/libxmp/src/extras.h b/thirdparty/libxmp/src/extras.h new file mode 100644 index 000000000..b8ef7b237 --- /dev/null +++ b/thirdparty/libxmp/src/extras.h @@ -0,0 +1,18 @@ +#ifndef LIBXMP_EXTRAS_H +#define LIBXMP_EXTRAS_H + +void libxmp_release_module_extras(struct context_data *); +int libxmp_new_channel_extras(struct context_data *, struct channel_data *); +void libxmp_release_channel_extras(struct context_data *, struct channel_data *); +void libxmp_reset_channel_extras(struct context_data *, struct channel_data *); +void libxmp_play_extras(struct context_data *, struct channel_data *, int); +int libxmp_extras_get_volume(struct context_data *, struct channel_data *); +int libxmp_extras_get_period(struct context_data *, struct channel_data *); +int libxmp_extras_get_linear_bend(struct context_data *, struct channel_data *); +void libxmp_extras_process_fx(struct context_data *, struct channel_data *, int, uint8, uint8, uint8, int); + + +/* FIXME */ +void libxmp_med_hold_hack(struct context_data *ctx, int, int, int); + +#endif diff --git a/thirdparty/libxmp/src/far_extras.c b/thirdparty/libxmp/src/far_extras.c new file mode 100644 index 000000000..d2f53ca08 --- /dev/null +++ b/thirdparty/libxmp/src/far_extras.c @@ -0,0 +1,405 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "lfo.h" +#include "effects.h" +#include "period.h" +#include "far_extras.h" + +#define FAR_GUS_CHANNELS 17 +#define FAR_OLD_TEMPO_SHIFT 2 /* Power of multiplier for old tempo mode. */ + +/** + * The time factor needed to directly use FAR tempos is a little unintuitive. + * + * Generally: FAR tries to run 32/[coarse tempo] rows per second, which + * (usually, but not always) are subdivided into 4 "ticks". To achieve + * this, it measures tempos in the number of ticks that should play per second + * (see far_tempos below). Fine tempo is added or subtracted from this number. + * To time these ticks, FAR uses the programmable interval timer (PIT) to run a + * player interrupt. + * + * libxmp effectively uses a calculation of 10.0 * 0.25 / BPM to get the tick + * duration in seconds. A base time factor of 4.0 makes this 1 / BPM, turning + * BPM into the ticks/sec measure that FAR uses. This isn't completely + * accurate to FAR, though. + * + * The x86 PIT runs at a rate of 1193182 Hz, but FAR does something strange + * when calculating PIT divisors and uses a constant of 1197255 Hz instead. + * This means FAR tempo is slightly slower by a factor of around: + * + * floor(1197255 / 32) / floor(1193182 / 32) ~= 1.003439 + * + * This still isn't perfect, but it gets the playback rate fairly close. + */ + +/* tempo[0] = 256; tempo[i] = floor(128 / i). */ +static const int far_tempos[16] = +{ + 256, 128, 64, 42, 32, 25, 21, 18, 16, 14, 12, 11, 10, 9, 9, 8 +}; + +/** + * FAR tempo has some unusual requirements that don't really match any other + * format: + * + * 1) The coarse tempo is roughly equivalent to speed, but a value of 0 is + * supported, and FAR doesn't actually have a concept of ticks: it translates + * this value to tempo. + * + * 2) There is some very bizarre clamping behavior involving fine tempo slides + * that needs to be emulated. + * + * 3) Tempos can range from 1 to 356(!). FAR uses a fixed row subdivision size + * of 16, so just shift the tempo by 4 and hope libxmp doesn't change it. + * + * 4) There are two tempo modes, and they can be switched between arbitrarily... + */ +int libxmp_far_translate_tempo(int mode, int fine_change, int coarse, + int *fine, int *_speed, int *_bpm) +{ + int speed, bpm; + + if (coarse < 0 || coarse > 15 || mode < 0 || mode > 1) + return -1; + + /* Compatibility for FAR's broken fine tempo "clamping". */ + if (fine_change < 0 && far_tempos[coarse] + *fine <= 0) { + *fine = 0; + } else if (fine_change > 0 && far_tempos[coarse] + *fine >= 100) { + *fine = 100; + } + + if (mode == 1) { + /* "New" FAR tempo + * Note that negative values are possible in Farandole Composer + * via changing fine tempo and then slowing coarse tempo. + * These result in very slow final tempos due to signed to + * unsigned conversion. Zero should just be ignored entirely. */ + int tempo = far_tempos[coarse] + *fine; + uint32 divisor; + if (tempo == 0) + return -1; + + divisor = 1197255 / tempo; + + /* Coincidentally(?), the "new" FAR tempo algorithm actually + * prevents the BPM from dropping too far under XMP_MIN_BPM, + * which is what libxmp needs anyway. */ + speed = 0; + while (divisor > 0xffff) { + divisor >>= 1; + tempo <<= 1; + speed++; + } + if (speed >= 2) + speed++; + speed += 3; + /* Add an extra tick because the FAR replayer checks the tick + * remaining count before decrementing it but after handling + * each tick, i.e. a count of "3" executes 4 ticks. */ + speed++; + bpm = tempo; + } else { + /* "Old" FAR tempo + * This runs into the XMP_MIN_BPM limit, but nothing uses it anyway. + * Old tempo mode in the original FAR replayer has 32 ticks, + * but ignores all except every 8th. */ + speed = 4 << FAR_OLD_TEMPO_SHIFT; + bpm = (far_tempos[coarse] + *fine * 2) << FAR_OLD_TEMPO_SHIFT; + } + + if (bpm < XMP_MIN_BPM) + bpm = XMP_MIN_BPM; + + *_speed = speed; + *_bpm = bpm; + return 0; +} + +static void libxmp_far_update_tempo(struct context_data *ctx, int fine_change) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct far_module_extras *me = (struct far_module_extras *)m->extra; + int speed, bpm; + + if (libxmp_far_translate_tempo(me->tempo_mode, fine_change, + me->coarse_tempo, &me->fine_tempo, &speed, &bpm) == 0) { + p->speed = speed; + p->bpm = bpm; + p->frame_time = m->time_factor * m->rrate / p->bpm; + } +} + +static void libxmp_far_update_vibrato(struct lfo *lfo, int rate, int depth) +{ + libxmp_lfo_set_depth(lfo, libxmp_gus_frequency_steps(depth << 1, FAR_GUS_CHANNELS)); + libxmp_lfo_set_rate(lfo, rate * 3); +} + +/* Convoluted algorithm for delay times for retrigger and note offset effects. */ +static int libxmp_far_retrigger_delay(struct far_module_extras *me, int param) +{ + int delay; + if (me->coarse_tempo < 0 || me->coarse_tempo > 15 || param < 1) + return -1; + + delay = (far_tempos[me->coarse_tempo] + me->fine_tempo) / param; + + if (me->tempo_mode) { + /* Effects divide by 4, timer increments by 2 (round up). */ + return ((delay >> 2) + 1) >> 1; + } else { + /* Effects divide by 2, timer increments by 2 (round up). + * Old tempo mode handles every 8th tick (<< FAR_OLD_TEMPO_SHIFT). + * Delay values >4 result in no retrigger. */ + delay = (((delay >> 1) + 1) >> 1) << FAR_OLD_TEMPO_SHIFT; + if (delay >= 16) + return -1; + if (delay < (1 << FAR_OLD_TEMPO_SHIFT)) + return (1 << FAR_OLD_TEMPO_SHIFT); + return delay; + } +} + + +void libxmp_far_play_extras(struct context_data *ctx, struct channel_data *xc, int chn) +{ + struct far_module_extras *me = FAR_MODULE_EXTRAS(ctx->m); + struct far_channel_extras *ce = FAR_CHANNEL_EXTRAS(*xc); + + /* FAR vibrato depth is global, even though rate isn't. This might have + * been changed by a different channel, so make sure it's applied. */ + if (TEST(VIBRATO) || TEST_PER(VIBRATO)) + libxmp_far_update_vibrato(&xc->vibrato.lfo, ce->vib_rate, me->vib_depth); +} + +int libxmp_far_new_channel_extras(struct channel_data *xc) +{ + xc->extra = calloc(1, sizeof(struct far_channel_extras)); + if (xc->extra == NULL) + return -1; + FAR_CHANNEL_EXTRAS(*xc)->magic = FAR_EXTRAS_MAGIC; + return 0; +} + +void libxmp_far_reset_channel_extras(struct channel_data *xc) +{ + memset((char *)xc->extra + 4, 0, sizeof(struct far_channel_extras) - 4); +} + +void libxmp_far_release_channel_extras(struct channel_data *xc) +{ + free(xc->extra); + xc->extra = NULL; +} + +int libxmp_far_new_module_extras(struct module_data *m) +{ + m->extra = calloc(1, sizeof(struct far_module_extras)); + if (m->extra == NULL) + return -1; + FAR_MODULE_EXTRAS(*m)->magic = FAR_EXTRAS_MAGIC; + FAR_MODULE_EXTRAS(*m)->vib_depth = 4; + return 0; +} + +void libxmp_far_release_module_extras(struct module_data *m) +{ + free(m->extra); + m->extra = NULL; +} + +void libxmp_far_extras_process_fx(struct context_data *ctx, struct channel_data *xc, + int chn, uint8 note, uint8 fxt, uint8 fxp, int fnum) +{ + struct xmp_module *mod = &ctx->m.mod; + struct far_module_extras *me = FAR_MODULE_EXTRAS(ctx->m); + struct far_channel_extras *ce = FAR_CHANNEL_EXTRAS(*xc); + int update_tempo = 0; + int update_vibrato = 0; + int fine_change = 0; + int delay, target, tempo; + int32 diff, step; + + /* Tempo effects and vibrato are multiplexed to reduce the effects count. + * + * Misc. notes: FAR pitch offset effects can overflow/underflow GUS + * frequency, which isn't supported by libxmp (Haj/before.far). + */ + switch (fxt) { + case FX_FAR_PORTA_UP: /* FAR pitch offset up */ + SET(FINE_BEND); + RESET_PER(TONEPORTA); + xc->freq.fslide = libxmp_gus_frequency_steps(fxp << 2, FAR_GUS_CHANNELS); + break; + + case FX_FAR_PORTA_DN: /* FAR pitch offset down */ + SET(FINE_BEND); + RESET_PER(TONEPORTA); + xc->freq.fslide = -libxmp_gus_frequency_steps(fxp << 2, FAR_GUS_CHANNELS); + break; + + /* Despite some claims, this effect scales with tempo and only + * corresponds to (param) rows at tempo 4. See FORMATS.DOC. + */ + case FX_FAR_TPORTA: /* FAR persistent tone portamento */ + if (!IS_VALID_INSTRUMENT(xc->ins)) + break; + + tempo = far_tempos[me->coarse_tempo] + me->fine_tempo; + + SET_PER(TONEPORTA); + if (IS_VALID_NOTE(note - 1)) { + xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, xc->per_adj); + } + xc->porta.dir = xc->period < xc->porta.target ? 1 : -1; + + /* Parameter of 0 is equivalent to 1. */ + if (fxp < 1) + fxp = 1; + /* Tempos <=0 cause crashes and other weird behavior + * here in Farandole Composer, don't emulate that. */ + if (tempo < 1) + tempo = 1; + + diff = xc->porta.target - xc->period; + step = (diff > 0 ? diff : -diff) * 8 / (tempo * fxp); + + xc->porta.slide = (step > 0) ? step : 1; + break; + + + /* Despite some claims, this effect scales with tempo and only + * corresponds to (param/2) rows at tempo 4. See FORMATS.DOC. + */ + case FX_FAR_SLIDEVOL: /* FAR persistent slide-to-volume */ + tempo = far_tempos[me->coarse_tempo] + me->fine_tempo; + target = MSN(fxp) << 4; + fxp = LSN(fxp); + + /* Parameter of 0 is equivalent to 1. */ + if (fxp < 1) + fxp = 1; + /* Tempos <=0 cause crashes and other weird behavior + * here in Farandole Composer, don't emulate that. */ + if (tempo < 1) + tempo = 1; + + diff = target - xc->volume; + step = diff * 16 / (tempo * fxp); + if (step == 0) + step = (diff > 0) ? 1 : -1; + + SET_PER(VOL_SLIDE); + xc->vol.slide = step; + xc->vol.target = target + 1; + break; + + case FX_FAR_VIBDEPTH: /* FAR set vibrato depth */ + me->vib_depth = LSN(fxp); + update_vibrato = 1; + break; + + case FX_FAR_VIBRATO: /* FAR vibrato and sustained vibrato */ + if (ce->vib_sustain == 0) { + /* With sustain, regular vibrato only sets the rate. */ + ce->vib_sustain = MSN(fxp); + if (ce->vib_sustain == 0) + SET(VIBRATO); + } + ce->vib_rate = LSN(fxp); + update_vibrato = 1; + break; + + /* Retrigger note param times at intervals that roughly evently + * divide the row. A param of 0 crashes Farandole Composer. + */ + case FX_FAR_RETRIG: /* FAR retrigger */ + delay = libxmp_far_retrigger_delay(me, fxp); + if (note && fxp > 1 && delay >= 0 && delay <= ctx->p.speed) { + SET(RETRIG); + xc->retrig.val = delay ? delay : 1; + xc->retrig.count = delay + 1; + xc->retrig.type = 0; + xc->retrig.limit = fxp - 1; + } + break; + + /* A better effect name would probably be "retrigger once". + * The description/intent seems to be that this is a delay + * effect, but an initial note always plays as well. The second + * note always plays on the (param)th tick due to player quirks, + * but it's supposed to be derived similar to retrigger. + * A param of zero works like effect 4F (bug?). + */ + case FX_FAR_DELAY: /* FAR note offset */ + if (note) { + delay = me->tempo_mode ? fxp : fxp << FAR_OLD_TEMPO_SHIFT; + SET(RETRIG); + xc->retrig.val = delay ? delay : 1; + xc->retrig.count = delay + 1; + xc->retrig.type = 0; + xc->retrig.limit = fxp ? 1 : 0; + } + break; + + case FX_FAR_TEMPO: /* FAR coarse tempo and tempo mode */ + if (MSN(fxp)) { + me->tempo_mode = MSN(fxp) - 1; + } else { + me->coarse_tempo = LSN(fxp); + } + update_tempo = 1; + break; + + case FX_FAR_F_TEMPO: /* FAR fine tempo slide up/down */ + if (MSN(fxp)) { + me->fine_tempo += MSN(fxp); + fine_change = MSN(fxp); + } else if (LSN(fxp)) { + me->fine_tempo -= LSN(fxp); + fine_change = -LSN(fxp); + } else { + me->fine_tempo = 0; + } + update_tempo = 1; + break; + } + + if (update_vibrato) { + if (ce->vib_rate != 0) { + if (ce->vib_sustain) + SET_PER(VIBRATO); + } else { + RESET_PER(VIBRATO); + ce->vib_sustain = 0; + } + libxmp_far_update_vibrato(&xc->vibrato.lfo, ce->vib_rate, me->vib_depth); + } + + if (update_tempo) + libxmp_far_update_tempo(ctx, fine_change); +} diff --git a/thirdparty/libxmp/src/far_extras.h b/thirdparty/libxmp/src/far_extras.h new file mode 100644 index 000000000..b1b3ae0d2 --- /dev/null +++ b/thirdparty/libxmp/src/far_extras.h @@ -0,0 +1,55 @@ +#ifndef XMP_FAR_EXTRAS_H +#define XMP_FAR_EXTRAS_H + +#include "common.h" + +#define FAR_EXTRAS_MAGIC 0x7b12a83f + +/* +struct far_instrument_extras { + uint32 magic; +}; +*/ +struct far_channel_extras { + uint32 magic; + int vib_sustain; /* Is vibrato persistent? */ + int vib_rate; /* Vibrato rate. */ +}; + +struct far_module_extras { + uint32 magic; + int coarse_tempo; + int fine_tempo; + int tempo_mode; + int vib_depth; /* Vibrato depth for all channels. */ +}; + +/* +#define FAR_INSTRUMENT_EXTRAS(x) ((struct far_instrument_extras *)(x).extra) +#define HAS_FAR_INSTRUMENT_EXTRAS(x) \ + (FAR_INSTRUMENT_EXTRAS(x) != NULL && \ + FAR_INSTRUMENT_EXTRAS(x)->magic == FAR_EXTRAS_MAGIC) +*/ +#define FAR_CHANNEL_EXTRAS(x) ((struct far_channel_extras *)(x).extra) +#define HAS_FAR_CHANNEL_EXTRAS(x) \ + (FAR_CHANNEL_EXTRAS(x) != NULL && \ + FAR_CHANNEL_EXTRAS(x)->magic == FAR_EXTRAS_MAGIC) + +#define FAR_MODULE_EXTRAS(x) ((struct far_module_extras *)(x).extra) +#define HAS_FAR_MODULE_EXTRAS(x) \ + (FAR_MODULE_EXTRAS(x) != NULL && \ + FAR_MODULE_EXTRAS(x)->magic == FAR_EXTRAS_MAGIC) + +int libxmp_far_translate_tempo(int, int, int, int *, int *, int *); + +void libxmp_far_play_extras(struct context_data *, struct channel_data *, int); +int libxmp_far_linear_bend(struct context_data *, struct channel_data *); +int libxmp_far_new_channel_extras(struct channel_data *); +void libxmp_far_reset_channel_extras(struct channel_data *); +void libxmp_far_release_channel_extras(struct channel_data *); +int libxmp_far_new_module_extras(struct module_data *); +void libxmp_far_release_module_extras(struct module_data *); +void libxmp_far_extras_process_fx(struct context_data *, struct channel_data *, int, uint8, uint8, uint8, int); + +#endif + diff --git a/thirdparty/libxmp/src/filetype.c b/thirdparty/libxmp/src/filetype.c new file mode 100644 index 000000000..363150c10 --- /dev/null +++ b/thirdparty/libxmp/src/filetype.c @@ -0,0 +1,145 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include + +#if defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +int libxmp_get_filetype (const char *path) +{ + DWORD result = GetFileAttributesA(path); + if (result == (DWORD)(-1)) { + errno = ENOENT; + return XMP_FILETYPE_NONE; + } + return (result & FILE_ATTRIBUTE_DIRECTORY) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__OS2__) || defined(__EMX__) + +#define INCL_DOSFILEMGR +#include + +int libxmp_get_filetype (const char *path) +{ + FILESTATUS3 fs; + if (DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) != 0) { + errno = ENOENT; + return XMP_FILETYPE_NONE; + } + return (fs.attrFile & FILE_DIRECTORY) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__DJGPP__) + +#include +#include + +int libxmp_get_filetype (const char *path) +{ + int attr = _chmod(path, 0); + /* Root directories on some non-local drives (e.g. CD-ROM), as well as + * devices may fail _chmod, but we are not interested in such cases. */ + if (attr < 0) return XMP_FILETYPE_NONE; + /* we shouldn't hit _A_VOLID ! */ + return (attr & (_A_SUBDIR|_A_VOLID)) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__WATCOMC__) && defined(_DOS) + +#include +#include + +int libxmp_get_filetype (const char *path) +{ + unsigned int attr; + if (_dos_getfileattr(path, &attr)) return XMP_FILETYPE_NONE; + return (attr & (_A_SUBDIR|_A_VOLID)) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE; +} + +#elif defined(__amigaos4__) + +#define __USE_INLINE__ +#include + +int libxmp_get_filetype (const char *path) +{ + int typ = XMP_FILETYPE_NONE; + struct ExamineData *data = ExamineObjectTags(EX_StringNameInput, path, TAG_END); + if (data) { + if (EXD_IS_FILE(data)) { + typ = XMP_FILETYPE_FILE; + } else + if (EXD_IS_DIRECTORY(data)) { + typ = XMP_FILETYPE_DIR; + } + FreeDosObject(DOS_EXAMINEDATA, data); + } + if (typ == XMP_FILETYPE_NONE) errno = ENOENT; + return typ; +} + +#elif defined(LIBXMP_AMIGA) + +#include + +int libxmp_get_filetype (const char *path) +{ + int typ = XMP_FILETYPE_NONE; + BPTR lock = Lock((const STRPTR)path, ACCESS_READ); + if (lock) { + struct FileInfoBlock *fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB,NULL); + if (fib) { + if (Examine(lock, fib)) { + typ = (fib->fib_DirEntryType < 0) ? XMP_FILETYPE_FILE : XMP_FILETYPE_DIR; + } + FreeDosObject(DOS_FIB, fib); + } + UnLock(lock); + } + if (typ == XMP_FILETYPE_NONE) errno = ENOENT; + return typ; +} + +#else /* unix (ish): */ + +#include +#include + +int libxmp_get_filetype (const char *path) +{ + struct stat st; + memset(&st, 0, sizeof(st)); /* silence sanitizers.. */ + if (stat(path, &st) < 0) return XMP_FILETYPE_NONE; + if (S_ISDIR(st.st_mode)) return XMP_FILETYPE_DIR; + if (S_ISREG(st.st_mode)) return XMP_FILETYPE_FILE; + return XMP_FILETYPE_NONE; +} + +#endif + diff --git a/thirdparty/libxmp/src/filter.c b/thirdparty/libxmp/src/filter.c new file mode 100644 index 000000000..a8a34b672 --- /dev/null +++ b/thirdparty/libxmp/src/filter.c @@ -0,0 +1,109 @@ +/* + * Based on the public domain version by Olivier Lapicque + * Rewritten for libxmp by Claudio Matsuoka + * + * Copyright (C) 2012 Claudio Matsuoka + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LIBXMP_CORE_DISABLE_IT +#include +#include "xmp.h" +#include "common.h" +#include "mixer.h" + + +/* LUT for 2 * damping factor */ +static const float resonance_table[128] = { + 1.0000000000000000f, 0.9786446094512940f, 0.9577452540397644f, 0.9372922182083130f, + 0.9172759056091309f, 0.8976871371269226f, 0.8785166740417481f, 0.8597555756568909f, + 0.8413951396942139f, 0.8234267830848694f, 0.8058421611785889f, 0.7886331081390381f, + 0.7717915177345276f, 0.7553095817565918f, 0.7391796708106995f, 0.7233941555023193f, + 0.7079457640647888f, 0.6928272843360901f, 0.6780316829681397f, 0.6635520458221436f, + 0.6493816375732422f, 0.6355138421058655f, 0.6219421625137329f, 0.6086603403091431f, + 0.5956621170043945f, 0.5829415321350098f, 0.5704925656318665f, 0.5583094954490662f, + 0.5463865399360657f, 0.5347182154655457f, 0.5232990980148315f, 0.5121238231658936f, + 0.5011872053146362f, 0.4904841780662537f, 0.4800096750259399f, 0.4697588682174683f, + 0.4597269892692566f, 0.4499093294143677f, 0.4403013288974762f, 0.4308985173702240f, + 0.4216965138912201f, 0.4126909971237183f, 0.4038778245449066f, 0.3952528536319733f, + 0.3868120610713959f, 0.3785515129566193f, 0.3704673945903778f, 0.3625559210777283f, + 0.3548133969306946f, 0.3472362160682678f, 0.3398208320140839f, 0.3325638175010681f, + 0.3254617750644684f, 0.3185114264488220f, 0.3117094635963440f, 0.3050527870655060f, + 0.2985382676124573f, 0.2921628654003143f, 0.2859236001968384f, 0.2798175811767578f, + 0.2738419771194458f, 0.2679939568042755f, 0.2622708380222321f, 0.2566699385643005f, + 0.2511886358261108f, 0.2458244115114212f, 0.2405747324228287f, 0.2354371547698975f, + 0.2304092943668366f, 0.2254888117313385f, 0.2206734120845795f, 0.2159608304500580f, + 0.2113489061594009f, 0.2068354636430740f, 0.2024184018373489f, 0.1980956792831421f, + 0.1938652694225311f, 0.1897251904010773f, 0.1856735348701477f, 0.1817083954811096f, + 0.1778279393911362f, 0.1740303486585617f, 0.1703138649463654f, 0.1666767448186874f, + 0.1631172895431519f, 0.1596338599920273f, 0.1562248021364212f, 0.1528885662555695f, + 0.1496235728263855f, 0.1464282870292664f, 0.1433012634515762f, 0.1402409970760346f, + 0.1372461020946503f, 0.1343151479959488f, 0.1314467936754227f, 0.1286396980285645f, + 0.1258925348520279f, 0.1232040524482727f, 0.1205729842185974f, 0.1179980933666229f, + 0.1154781952500343f, 0.1130121126770973f, 0.1105986908078194f, 0.1082368120551109f, + 0.1059253737330437f, 0.1036632955074310f, 0.1014495193958283f, 0.0992830246686935f, + 0.0971627980470657f, 0.0950878411531448f, 0.0930572077631950f, 0.0910699293017387f, + 0.0891250967979431f, 0.0872217938303947f, 0.0853591337800026f, 0.0835362523794174f, + 0.0817523002624512f, 0.0800064504146576f, 0.0782978758215904f, 0.0766257941722870f, + 0.0749894231557846f, 0.0733879879117012f, 0.0718207582831383f, 0.0702869966626167f, + 0.0687859877943993f, 0.0673170387744904f, 0.0658794566988945f, 0.0644725710153580f, +}; + + +#if !defined(HAVE_POWF) || defined(__DJGPP__) || defined(__WATCOMC__) +/* Watcom doesn't have powf. DJGPP have a C-only implementation in libm. */ +#undef powf +#define powf(f1_,f2_) (float)pow((f1_),(f2_)) +#endif + + +/* + * Simple 2-poles resonant filter + */ +#define FREQ_PARAM_MULT (128.0f / (24.0f * 256.0f)) +void libxmp_filter_setup(int srate, int cutoff, int res, int *a0, int *b0, int *b1) +{ + float fc, fs = (float)srate; + float fg, fb0, fb1; + float r, d, e; + + /* [0-255] => [100Hz-8000Hz] */ + CLAMP(cutoff, 0, 255); + CLAMP(res, 0, 255); + + fc = 110.0f * powf(2.0f, (float)cutoff * FREQ_PARAM_MULT + 0.25f); + if (fc > fs / 2.0f) { + fc = fs / 2.0f; + } + + r = fs / (2.0 * 3.14159265358979f * fc); + d = resonance_table[res >> 1] * (r + 1.0) - 1.0; + e = r * r; + + fg = 1.0 / (1.0 + d + e); + fb0 = (d + e + e) / (1.0 + d + e); + fb1 = -e / (1.0 + d + e); + + *a0 = (int)(fg * (1 << FILTER_SHIFT)); + *b0 = (int)(fb0 * (1 << FILTER_SHIFT)); + *b1 = (int)(fb1 * (1 << FILTER_SHIFT)); +} + +#endif diff --git a/thirdparty/libxmp/src/format.c b/thirdparty/libxmp/src/format.c new file mode 100644 index 000000000..593ace13e --- /dev/null +++ b/thirdparty/libxmp/src/format.c @@ -0,0 +1,127 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "format.h" +#ifndef LIBXMP_NO_PROWIZARD +#include "loaders/prowizard/prowiz.h" +#endif + +const struct format_loader *const format_loaders[NUM_FORMATS + 2] = { + &libxmp_loader_xm, + &libxmp_loader_mod, +#ifndef LIBXMP_CORE_DISABLE_IT + &libxmp_loader_it, +#endif + &libxmp_loader_s3m, +#ifndef LIBXMP_CORE_PLAYER + &libxmp_loader_flt, + &libxmp_loader_st, + &libxmp_loader_stm, + &libxmp_loader_stx, + &libxmp_loader_mtm, + &libxmp_loader_ice, + &libxmp_loader_imf, + &libxmp_loader_ptm, + &libxmp_loader_mdl, + &libxmp_loader_ult, + &libxmp_loader_liq, + &libxmp_loader_no, + &libxmp_loader_masi, + &libxmp_loader_masi16, + &libxmp_loader_muse, + &libxmp_loader_gal5, + &libxmp_loader_gal4, + &libxmp_loader_amf, + &libxmp_loader_asylum, + &libxmp_loader_gdm, + &libxmp_loader_mmd1, + &libxmp_loader_mmd3, + &libxmp_loader_med2, + &libxmp_loader_med3, + &libxmp_loader_med4, + /* &libxmp_loader_dmf, */ + &libxmp_loader_chip, + &libxmp_loader_rtm, + &libxmp_loader_pt3, + /* &libxmp_loader_tcb, */ + &libxmp_loader_dt, + /* &libxmp_loader_gtk, */ + /* &libxmp_loader_dtt, */ + &libxmp_loader_mgt, + &libxmp_loader_arch, + &libxmp_loader_sym, + &libxmp_loader_digi, + &libxmp_loader_dbm, + &libxmp_loader_emod, + &libxmp_loader_okt, + &libxmp_loader_sfx, + &libxmp_loader_far, + &libxmp_loader_umx, + &libxmp_loader_hmn, + &libxmp_loader_stim, + &libxmp_loader_coco, + /* &libxmp_loader_mtp, */ + &libxmp_loader_ims, + &libxmp_loader_669, + &libxmp_loader_fnk, + /* &libxmp_loader_amd, */ + /* &libxmp_loader_rad, */ + /* &libxmp_loader_hsc, */ + &libxmp_loader_mfp, + &libxmp_loader_abk, + /* &libxmp_loader_alm, */ + /* &libxmp_loader_polly, */ + /* &libxmp_loader_stc, */ + &libxmp_loader_xmf, +#ifndef LIBXMP_NO_PROWIZARD + &libxmp_loader_pw, +#endif +#endif /* LIBXMP_CORE_PLAYER */ + NULL /* list teminator */ +}; + +static const char *_farray[NUM_FORMATS + NUM_PW_FORMATS + 1] = { NULL }; + +const char *const *format_list(void) +{ + int count, i; + + if (_farray[0] == NULL) { + for (count = i = 0; format_loaders[i] != NULL; i++) { +#ifndef LIBXMP_NO_PROWIZARD + if (strcmp(format_loaders[i]->name, "prowizard") == 0) { + int j; + + for (j = 0; pw_formats[j] != NULL; j++) { + _farray[count++] = pw_formats[j]->name; + } + continue; + } +#endif + _farray[count++] = format_loaders[i]->name; + } + + _farray[count] = NULL; + } + + return _farray; +} diff --git a/thirdparty/libxmp/src/format.h b/thirdparty/libxmp/src/format.h new file mode 100644 index 000000000..a4cfd5bfc --- /dev/null +++ b/thirdparty/libxmp/src/format.h @@ -0,0 +1,103 @@ +#ifndef LIBXMP_FORMAT_H +#define LIBXMP_FORMAT_H + +#include "common.h" +#include "hio.h" + +struct format_loader { + const char *name; + int (*test)(HIO_HANDLE *, char *, const int); + int (*loader)(struct module_data *, HIO_HANDLE *, const int); +}; + +extern const struct format_loader *const format_loaders[]; + +const char *const *format_list(void); + +extern const struct format_loader libxmp_loader_xm; +extern const struct format_loader libxmp_loader_mod; +extern const struct format_loader libxmp_loader_it; +extern const struct format_loader libxmp_loader_s3m; + +#ifndef LIBXMP_CORE_PLAYER +extern const struct format_loader libxmp_loader_flt; +extern const struct format_loader libxmp_loader_st; +extern const struct format_loader libxmp_loader_stm; +extern const struct format_loader libxmp_loader_stx; +extern const struct format_loader libxmp_loader_mtm; +extern const struct format_loader libxmp_loader_ice; +extern const struct format_loader libxmp_loader_imf; +extern const struct format_loader libxmp_loader_ptm; +extern const struct format_loader libxmp_loader_mdl; +extern const struct format_loader libxmp_loader_ult; +extern const struct format_loader libxmp_loader_liq; +extern const struct format_loader libxmp_loader_no; +extern const struct format_loader libxmp_loader_masi; +extern const struct format_loader libxmp_loader_masi16; +extern const struct format_loader libxmp_loader_muse; +extern const struct format_loader libxmp_loader_gal5; +extern const struct format_loader libxmp_loader_gal4; +extern const struct format_loader libxmp_loader_amf; +extern const struct format_loader libxmp_loader_asylum; +extern const struct format_loader libxmp_loader_gdm; +extern const struct format_loader libxmp_loader_mmd1; +extern const struct format_loader libxmp_loader_mmd3; +extern const struct format_loader libxmp_loader_med2; +extern const struct format_loader libxmp_loader_med3; +extern const struct format_loader libxmp_loader_med4; +extern const struct format_loader libxmp_loader_rtm; +extern const struct format_loader libxmp_loader_pt3; +extern const struct format_loader libxmp_loader_dt; +extern const struct format_loader libxmp_loader_mgt; +extern const struct format_loader libxmp_loader_arch; +extern const struct format_loader libxmp_loader_sym; +extern const struct format_loader libxmp_loader_digi; +extern const struct format_loader libxmp_loader_dbm; +extern const struct format_loader libxmp_loader_emod; +extern const struct format_loader libxmp_loader_okt; +extern const struct format_loader libxmp_loader_sfx; +extern const struct format_loader libxmp_loader_far; +extern const struct format_loader libxmp_loader_umx; +extern const struct format_loader libxmp_loader_stim; +extern const struct format_loader libxmp_loader_coco; +extern const struct format_loader libxmp_loader_ims; +extern const struct format_loader libxmp_loader_669; +extern const struct format_loader libxmp_loader_fnk; +extern const struct format_loader libxmp_loader_mfp; +extern const struct format_loader libxmp_loader_pw; +extern const struct format_loader libxmp_loader_hmn; +extern const struct format_loader libxmp_loader_chip; +extern const struct format_loader libxmp_loader_abk; +extern const struct format_loader libxmp_loader_xmf; +#if 0 /* broken / unused, yet. */ +extern const struct format_loader libxmp_loader_dmf; +extern const struct format_loader libxmp_loader_tcb; +extern const struct format_loader libxmp_loader_gtk; +extern const struct format_loader libxmp_loader_dtt; +extern const struct format_loader libxmp_loader_mtp; +extern const struct format_loader libxmp_loader_amd; +extern const struct format_loader libxmp_loader_rad; +extern const struct format_loader libxmp_loader_hsc; +extern const struct format_loader libxmp_loader_alm; +extern const struct format_loader libxmp_loader_polly; +extern const struct format_loader libxmp_loader_stc; +#endif +#endif /* LIBXMP_CORE_PLAYER */ + +#ifndef LIBXMP_CORE_PLAYER +#define NUM_FORMATS 52 +#elif !defined(LIBXMP_CORE_DISABLE_IT) +#define NUM_FORMATS 4 +#else +#define NUM_FORMATS 3 +#endif + +#ifndef LIBXMP_NO_PROWIZARD +#define NUM_PW_FORMATS 43 +extern const struct pw_format *const pw_formats[]; +int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *); +#else +#define NUM_PW_FORMATS 0 +#endif + +#endif /* LIBXMP_FORMAT_H */ diff --git a/thirdparty/libxmp/src/hio.c b/thirdparty/libxmp/src/hio.c new file mode 100644 index 000000000..bee385172 --- /dev/null +++ b/thirdparty/libxmp/src/hio.c @@ -0,0 +1,544 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "common.h" +#include "hio.h" +#include "callbackio.h" +#include "mdataio.h" + +static long get_size(FILE *f) +{ + long size, pos; + + pos = ftell(f); + if (pos >= 0) { + if (fseek(f, 0, SEEK_END) < 0) { + return -1; + } + size = ftell(f); + if (fseek(f, pos, SEEK_SET) < 0) { + return -1; + } + return size; + } else { + return pos; + } +} + +int8 hio_read8s(HIO_HANDLE *h) +{ + int err; + int8 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read8s(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread8s(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread8s(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint8 hio_read8(HIO_HANDLE *h) +{ + int err; + uint8 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read8(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread8(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread8(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint16 hio_read16l(HIO_HANDLE *h) +{ + int err; + uint16 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read16l(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread16l(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread16l(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint16 hio_read16b(HIO_HANDLE *h) +{ + int err; + uint16 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read16b(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread16b(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread16b(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint32 hio_read24l(HIO_HANDLE *h) +{ + int err; + uint32 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read24l(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread24l(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread24l(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint32 hio_read24b(HIO_HANDLE *h) +{ + int err; + uint32 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read24b(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread24b(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread24b(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint32 hio_read32l(HIO_HANDLE *h) +{ + int err; + uint32 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read32l(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread32l(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread32l(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +uint32 hio_read32b(HIO_HANDLE *h) +{ + int err; + uint32 ret; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = read32b(h->handle.file, &err); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread32b(h->handle.mem, &err); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread32b(h->handle.cbfile, &err); + break; + default: + return 0; + } + + if (err != 0) { + h->error = err; + } + return ret; +} + +size_t hio_read(void *buf, size_t size, size_t num, HIO_HANDLE *h) +{ + size_t ret = 0; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = fread(buf, size, num, h->handle.file); + if (ret != num) { + if (ferror(h->handle.file)) { + h->error = errno; + } else { + h->error = feof(h->handle.file) ? EOF : -2; + } + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mread(buf, size, num, h->handle.mem); + if (ret != num) { + h->error = EOF; + } + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbread(buf, size, num, h->handle.cbfile); + if (ret != num) { + h->error = EOF; + } + break; + } + + return ret; +} + +int hio_seek(HIO_HANDLE *h, long offset, int whence) +{ + int ret = -1; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = fseek(h->handle.file, offset, whence); + if (ret < 0) { + h->error = errno; + } + else if (h->error == EOF) { + h->error = 0; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mseek(h->handle.mem, offset, whence); + if (ret < 0) { + h->error = EINVAL; + } + else if (h->error == EOF) { + h->error = 0; + } + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbseek(h->handle.cbfile, offset, whence); + if (ret < 0) { + h->error = EINVAL; + } + else if (h->error == EOF) { + h->error = 0; + } + break; + } + + return ret; +} + +long hio_tell(HIO_HANDLE *h) +{ + long ret = -1; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = ftell(h->handle.file); + if (ret < 0) { + h->error = errno; + } + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mtell(h->handle.mem); + if (ret < 0) { + /* should _not_ happen! */ + h->error = EINVAL; + } + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbtell(h->handle.cbfile); + if (ret < 0) { + h->error = EINVAL; + } + break; + } + + return ret; +} + +int hio_eof(HIO_HANDLE *h) +{ + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + return feof(h->handle.file); + case HIO_HANDLE_TYPE_MEMORY: + return meof(h->handle.mem); + case HIO_HANDLE_TYPE_CBFILE: + return cbeof(h->handle.cbfile); + } + return EOF; +} + +int hio_error(HIO_HANDLE *h) +{ + int error = h->error; + h->error = 0; + return error; +} + +HIO_HANDLE *hio_open(const char *path, const char *mode) +{ + HIO_HANDLE *h; + + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); + if (h == NULL) + goto err; + + h->type = HIO_HANDLE_TYPE_FILE; + h->handle.file = fopen(path, mode); + if (h->handle.file == NULL) + goto err2; + + h->size = get_size(h->handle.file); + if (h->size < 0) + goto err3; + + return h; + + err3: + fclose(h->handle.file); + err2: + free(h); + err: + return NULL; +} + +HIO_HANDLE *hio_open_mem(const void *ptr, long size, int free_after_use) +{ + HIO_HANDLE *h; + + if (size <= 0) return NULL; + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); + if (h == NULL) + return NULL; + + h->type = HIO_HANDLE_TYPE_MEMORY; + h->handle.mem = mopen(ptr, size, free_after_use); + h->size = size; + + if (!h->handle.mem) { + free(h); + h = NULL; + } + + return h; +} + +HIO_HANDLE *hio_open_file(FILE *f) +{ + HIO_HANDLE *h; + + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); + if (h == NULL) + return NULL; + + h->noclose = 1; + h->type = HIO_HANDLE_TYPE_FILE; + h->handle.file = f; + h->size = get_size(f); + if (h->size < 0) { + free(h); + return NULL; + } + + return h; +} + +HIO_HANDLE *hio_open_file2(FILE *f) +{ + HIO_HANDLE *h = hio_open_file(f); + if (h != NULL) { + h->noclose = 0; + } + else { + fclose(f); + } + return h; +} + +HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks) +{ + HIO_HANDLE *h; + CBFILE *f = cbopen(priv, callbacks); + if (!f) + return NULL; + + h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE)); + if (h == NULL) { + cbclose(f); + return NULL; + } + + h->type = HIO_HANDLE_TYPE_CBFILE; + h->handle.cbfile = f; + h->size = cbfilelength(f); + if (h->size < 0) { + cbclose(f); + free(h); + return NULL; + } + return h; +} + +static int hio_close_internal(HIO_HANDLE *h) +{ + int ret = -1; + + switch (HIO_HANDLE_TYPE(h)) { + case HIO_HANDLE_TYPE_FILE: + ret = (h->noclose)? 0 : fclose(h->handle.file); + break; + case HIO_HANDLE_TYPE_MEMORY: + ret = mclose(h->handle.mem); + break; + case HIO_HANDLE_TYPE_CBFILE: + ret = cbclose(h->handle.cbfile); + break; + } + return ret; +} + +/* hio_close + hio_open_mem. Reuses the same HIO_HANDLE. */ +int hio_reopen_mem(const void *ptr, long size, int free_after_use, HIO_HANDLE *h) +{ + MFILE *m; + int ret; + if (size <= 0) return -1; + + m = mopen(ptr, size, free_after_use); + if (m == NULL) { + return -1; + } + + ret = hio_close_internal(h); + if (ret < 0) { + m->free_after_use = 0; + mclose(m); + return ret; + } + + h->type = HIO_HANDLE_TYPE_MEMORY; + h->handle.mem = m; + h->size = size; + return 0; +} + +/* hio_close + hio_open_file. Reuses the same HIO_HANDLE. */ +int hio_reopen_file(FILE *f, int close_after_use, HIO_HANDLE *h) +{ + long size = get_size(f); + int ret; + if (size < 0) { + return -1; + } + + ret = hio_close_internal(h); + if (ret < 0) { + return -1; + } + + h->noclose = !close_after_use; + h->type = HIO_HANDLE_TYPE_FILE; + h->handle.file = f; + h->size = size; + return 0; +} + +int hio_close(HIO_HANDLE *h) +{ + int ret = hio_close_internal(h); + free(h); + return ret; +} + +long hio_size(HIO_HANDLE *h) +{ + return h->size; +} diff --git a/thirdparty/libxmp/src/hio.h b/thirdparty/libxmp/src/hio.h new file mode 100644 index 000000000..3925a82d5 --- /dev/null +++ b/thirdparty/libxmp/src/hio.h @@ -0,0 +1,50 @@ +#ifndef XMP_HIO_H +#define XMP_HIO_H + +#include "callbackio.h" +#include "memio.h" + +#define HIO_HANDLE_TYPE(x) ((x)->type) + +enum hio_type { + HIO_HANDLE_TYPE_FILE, + HIO_HANDLE_TYPE_MEMORY, + HIO_HANDLE_TYPE_CBFILE +}; + +typedef struct { + enum hio_type type; + long size; + union { + FILE *file; + MFILE *mem; + CBFILE *cbfile; + } handle; + int error; + int noclose; +} HIO_HANDLE; + +int8 hio_read8s (HIO_HANDLE *); +uint8 hio_read8 (HIO_HANDLE *); +uint16 hio_read16l (HIO_HANDLE *); +uint16 hio_read16b (HIO_HANDLE *); +uint32 hio_read24l (HIO_HANDLE *); +uint32 hio_read24b (HIO_HANDLE *); +uint32 hio_read32l (HIO_HANDLE *); +uint32 hio_read32b (HIO_HANDLE *); +size_t hio_read (void *, size_t, size_t, HIO_HANDLE *); +int hio_seek (HIO_HANDLE *, long, int); +long hio_tell (HIO_HANDLE *); +int hio_eof (HIO_HANDLE *); +int hio_error (HIO_HANDLE *); +HIO_HANDLE *hio_open (const char *, const char *); +HIO_HANDLE *hio_open_mem (const void *, long, int); +HIO_HANDLE *hio_open_file (FILE *); +HIO_HANDLE *hio_open_file2 (FILE *);/* allows fclose()ing the file by libxmp */ +HIO_HANDLE *hio_open_callbacks (void *, struct xmp_callbacks); +int hio_reopen_mem (const void *, long, int, HIO_HANDLE *); +int hio_reopen_file (FILE *, int, HIO_HANDLE *); +int hio_close (HIO_HANDLE *); +long hio_size (HIO_HANDLE *); + +#endif diff --git a/thirdparty/libxmp/src/hmn_extras.c b/thirdparty/libxmp/src/hmn_extras.c new file mode 100644 index 000000000..7339ef1ca --- /dev/null +++ b/thirdparty/libxmp/src/hmn_extras.c @@ -0,0 +1,141 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "virtual.h" +#include "effects.h" +#include "hmn_extras.h" + +static uint8 megaarp[16][16] = { + { 0, 3, 7, 12, 15, 12, 7, 3, 0, 3, 7, 12, 15, 12, 7, 3 }, + { 0, 4, 7, 12, 16, 12, 7, 4, 0, 4, 7, 12, 16, 12, 7, 4 }, + { 0, 3, 8, 12, 15, 12, 8, 3, 0, 3, 8, 12, 15, 12, 8, 3 }, + { 0, 4, 8, 12, 16, 12, 8, 4, 0, 4, 8, 12, 16, 12, 8, 4 }, + { 0, 5, 8, 12, 17, 12, 8, 5, 0, 5, 8, 12, 17, 12, 8, 5 }, + { 0, 5, 9, 12, 17, 12, 9, 5, 0, 5, 9, 12, 17, 12, 9, 5 }, + { 12, 0, 7, 0, 3, 0, 7, 0, 12, 0, 7, 0, 3, 0, 7, 0 }, + { 12, 0, 7, 0, 4, 0, 7, 0, 12, 0, 7, 0, 4, 0, 7, 0 }, + + { 0, 3, 7, 3, 7, 12, 7, 12, 15, 12, 7, 12, 7, 3, 7, 3 }, + { 0, 4, 7, 4, 7, 12, 7, 12, 16, 12, 7, 12, 7, 4, 7, 4 }, + { 31, 27, 24, 19, 15, 12, 7, 3, 0, 3, 7, 12, 15, 19, 24, 27 }, + { 31, 28, 24, 19, 16, 12, 7, 4, 0, 4, 7, 12, 16, 19, 24, 28 }, + { 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12 }, + { 0, 12, 24, 12, 0, 12, 24, 12, 0, 12, 24, 12, 0, 12, 24, 12 }, + { 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3 }, + { 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4 } +}; + + +int libxmp_hmn_linear_bend(struct context_data *ctx, struct channel_data *xc) +{ + return 0; +} + +void libxmp_hmn_play_extras(struct context_data *ctx, struct channel_data *xc, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct hmn_channel_extras *ce = (struct hmn_channel_extras *)xc->extra; + struct xmp_instrument *xxi; + int pos, waveform, volume; + + if (p->frame == 0 && TEST(NEW_NOTE|NEW_INS)) { + ce->datapos = 0; + } + + xxi = &m->mod.xxi[xc->ins]; + pos = ce->datapos; + waveform = HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->data[pos]; + volume = HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->progvolume[pos] & 0x7f; + + if (waveform < xxi->nsm && xxi->sub[waveform].sid != xc->smp) { + xc->smp = xxi->sub[waveform].sid; + libxmp_virt_setsmp(ctx, chn, xc->smp); + } + + pos++; + if (pos > HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->dataloopend) + pos = HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->dataloopstart; + + ce->datapos = pos; + ce->volume = volume; +} + +int libxmp_hmn_new_instrument_extras(struct xmp_instrument *xxi) +{ + xxi->extra = calloc(1, sizeof(struct hmn_instrument_extras)); + if (xxi->extra == NULL) + return -1; + HMN_INSTRUMENT_EXTRAS((*xxi))->magic = HMN_EXTRAS_MAGIC; + return 0; +} + +int libxmp_hmn_new_channel_extras(struct channel_data *xc) +{ + xc->extra = calloc(1, sizeof(struct hmn_channel_extras)); + if (xc->extra == NULL) + return -1; + HMN_CHANNEL_EXTRAS((*xc))->magic = HMN_EXTRAS_MAGIC; + return 0; +} + +void libxmp_hmn_reset_channel_extras(struct channel_data *xc) +{ + memset((char *)xc->extra + 4, 0, sizeof(struct hmn_channel_extras) - 4); +} + +void libxmp_hmn_release_channel_extras(struct channel_data *xc) +{ + free(xc->extra); + xc->extra = NULL; +} + +int libxmp_hmn_new_module_extras(struct module_data *m) +{ + m->extra = calloc(1, sizeof(struct hmn_module_extras)); + if (m->extra == NULL) + return -1; + HMN_MODULE_EXTRAS((*m))->magic = HMN_EXTRAS_MAGIC; + return 0; +} + +void libxmp_hmn_release_module_extras(struct module_data *m) +{ + free(m->extra); + m->extra = NULL; +} + +void libxmp_hmn_extras_process_fx(struct context_data *ctx, struct channel_data *xc, + int chn, uint8 note, uint8 fxt, uint8 fxp, int fnum) +{ + switch (fxt) { + case FX_MEGAARP: + /* Not sure if this is correct... */ + fxp = LSN(fxp); + + memcpy(xc->arpeggio.val, megaarp[fxp], 16); + xc->arpeggio.size = 16; + break; + } +} diff --git a/thirdparty/libxmp/src/hmn_extras.h b/thirdparty/libxmp/src/hmn_extras.h new file mode 100644 index 000000000..ab739d2ed --- /dev/null +++ b/thirdparty/libxmp/src/hmn_extras.h @@ -0,0 +1,51 @@ +#ifndef XMP_HMN_EXTRAS_H +#define XMP_HMN_EXTRAS_H + +#define HMN_EXTRAS_MAGIC 0x041bc81a + +struct hmn_instrument_extras { + uint32 magic; + int dataloopstart; + int dataloopend; + uint8 data[64]; + uint8 progvolume[64]; +}; + +struct hmn_channel_extras { + uint32 magic; + int datapos; /* HMN waveform table pointer */ + int volume; /* HMN synth volume */ +}; + +struct hmn_module_extras { + uint32 magic; +}; + +#define HMN_INSTRUMENT_EXTRAS(x) ((struct hmn_instrument_extras *)(x).extra) +#define HAS_HMN_INSTRUMENT_EXTRAS(x) \ + (HMN_INSTRUMENT_EXTRAS(x) != NULL && \ + HMN_INSTRUMENT_EXTRAS(x)->magic == HMN_EXTRAS_MAGIC) + +#define HMN_CHANNEL_EXTRAS(x) ((struct hmn_channel_extras *)(x).extra) +#define HAS_HMN_CHANNEL_EXTRAS(x) \ + (HMN_CHANNEL_EXTRAS(x) != NULL && \ + HMN_CHANNEL_EXTRAS(x)->magic == HMN_EXTRAS_MAGIC) + +#define HMN_MODULE_EXTRAS(x) ((struct hmn_module_extras *)(x).extra) +#define HAS_HMN_MODULE_EXTRAS(x) \ + (HMN_MODULE_EXTRAS(x) != NULL && \ + HMN_MODULE_EXTRAS(x)->magic == HMN_EXTRAS_MAGIC) + +void libxmp_hmn_play_extras(struct context_data *, struct channel_data *, int); +void libxmp_hmn_set_arpeggio(struct channel_data *, int); +int libxmp_hmn_linear_bend(struct context_data *, struct channel_data *); +int libxmp_hmn_new_instrument_extras(struct xmp_instrument *); +int libxmp_hmn_new_channel_extras(struct channel_data *); +void libxmp_hmn_reset_channel_extras(struct channel_data *); +void libxmp_hmn_release_channel_extras(struct channel_data *); +int libxmp_hmn_new_module_extras(struct module_data *); +void libxmp_hmn_release_module_extras(struct module_data *); +void libxmp_hmn_extras_process_fx(struct context_data *, struct channel_data *, int, uint8, uint8, uint8, int); + +#endif + diff --git a/thirdparty/libxmp/src/lfo.c b/thirdparty/libxmp/src/lfo.c new file mode 100644 index 000000000..a393b9664 --- /dev/null +++ b/thirdparty/libxmp/src/lfo.c @@ -0,0 +1,163 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "lfo.h" + +#define WAVEFORM_SIZE 64 + +static const int sine_wave[WAVEFORM_SIZE] = { + 0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, + 235, 244, 250, 253, 255, 253, 250, 244, 235, 224, 212, 197, + 180, 161, 141, 120, 97, 74, 49, 24, 0, -24, -49, -74, + -97,-120,-141,-161,-180,-197,-212,-224,-235,-244,-250,-253, + -255,-253,-250,-244,-235,-224,-212,-197,-180,-161,-141,-120, + -97, -74, -49, -24 +}; + +/* LFO */ + +static int get_lfo_mod(struct lfo *lfo) +{ + int val; + + if (lfo->rate == 0) + return 0; + + switch (lfo->type) { + case 0: /* sine */ + val = sine_wave[lfo->phase]; + break; + case 1: /* ramp down */ + val = 255 - (lfo->phase << 3); + break; + case 2: /* square */ + val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : -255; + break; + case 3: /* random */ + val = ((rand() & 0x1ff) - 256); + break; +#ifndef LIBXMP_CORE_PLAYER + case 669: /* 669 vibrato */ + val = lfo->phase & 1; + break; +#endif + default: + return 0; + } + + return val * lfo->depth; +} + +static int get_lfo_st3(struct lfo *lfo) +{ + if (lfo->rate == 0) { + return 0; + } + + /* S3M square */ + if (lfo->type == 2) { + int val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : 0; + return val * lfo->depth; + } + + return get_lfo_mod(lfo); +} + +/* From OpenMPT VibratoWaveforms.xm: + * "Generally the vibrato and tremolo tables are identical to those that + * ProTracker uses, but the vibrato’s “ramp down†table is upside down." + */ +static int get_lfo_ft2(struct lfo *lfo) +{ + if (lfo->rate == 0) + return 0; + + /* FT2 ramp */ + if (lfo->type == 1) { + int phase = (lfo->phase + (WAVEFORM_SIZE >> 1)) % WAVEFORM_SIZE; + int val = (phase << 3) - 255; + return val * lfo->depth; + } + + return get_lfo_mod(lfo); +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +static int get_lfo_it(struct lfo *lfo) +{ + if (lfo->rate == 0) + return 0; + + return get_lfo_st3(lfo); +} + +#endif + +int libxmp_lfo_get(struct context_data *ctx, struct lfo *lfo, int is_vibrato) +{ + struct module_data *m = &ctx->m; + + switch (m->read_event_type) { + case READ_EVENT_ST3: + return get_lfo_st3(lfo); + case READ_EVENT_FT2: + if (is_vibrato) { + return get_lfo_ft2(lfo); + } else { + return get_lfo_mod(lfo); + } +#ifndef LIBXMP_CORE_DISABLE_IT + case READ_EVENT_IT: + return get_lfo_it(lfo); +#endif + default: + return get_lfo_mod(lfo); + } +} + + +void libxmp_lfo_update(struct lfo *lfo) +{ + lfo->phase += lfo->rate; + lfo->phase %= WAVEFORM_SIZE; +} + +void libxmp_lfo_set_phase(struct lfo *lfo, int phase) +{ + lfo->phase = phase; +} + +void libxmp_lfo_set_depth(struct lfo *lfo, int depth) +{ + lfo->depth = depth; +} + +void libxmp_lfo_set_rate(struct lfo *lfo, int rate) +{ + lfo->rate = rate; +} + +void libxmp_lfo_set_waveform(struct lfo *lfo, int type) +{ + lfo->type = type; +} diff --git a/thirdparty/libxmp/src/lfo.h b/thirdparty/libxmp/src/lfo.h new file mode 100644 index 000000000..a269ac3db --- /dev/null +++ b/thirdparty/libxmp/src/lfo.h @@ -0,0 +1,20 @@ +#ifndef LIBXMP_LFO_H +#define LIBXMP_LFO_H + +#include "common.h" + +struct lfo { + int type; + int rate; + int depth; + int phase; +}; + +int libxmp_lfo_get(struct context_data *, struct lfo *, int); +void libxmp_lfo_update(struct lfo *); +void libxmp_lfo_set_phase(struct lfo *, int); +void libxmp_lfo_set_depth(struct lfo *, int); +void libxmp_lfo_set_rate(struct lfo *, int); +void libxmp_lfo_set_waveform(struct lfo *, int); + +#endif diff --git a/thirdparty/libxmp/src/list.h b/thirdparty/libxmp/src/list.h new file mode 100644 index 000000000..2d7926655 --- /dev/null +++ b/thirdparty/libxmp/src/list.h @@ -0,0 +1,141 @@ +#ifndef LIBXMP_LIST_H +#define LIBXMP_LIST_H + +#include /* offsetof */ + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *_new, + struct list_head * prev, + struct list_head * next) +{ + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +/** + * list_add - add a new entry + * @_new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *_new, struct list_head *head) +{ + __list_add(_new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @_new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *_new, struct list_head *head) +{ + __list_add(_new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#endif /* LIBXMP_LIST_H */ diff --git a/thirdparty/libxmp/src/load.c b/thirdparty/libxmp/src/load.c new file mode 100644 index 000000000..13454ce99 --- /dev/null +++ b/thirdparty/libxmp/src/load.c @@ -0,0 +1,580 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "format.h" +#include "list.h" +#include "hio.h" +#include "loaders/loader.h" + +#ifndef LIBXMP_NO_DEPACKERS +#include "tempfile.h" +#include "depackers/depacker.h" +#endif + +#ifndef LIBXMP_CORE_PLAYER +#include "md5.h" +#include "extras.h" +#endif + + +void libxmp_load_prologue(struct context_data *); +void libxmp_load_epilogue(struct context_data *); +int libxmp_prepare_scan(struct context_data *); + +#ifndef LIBXMP_CORE_PLAYER +#define BUFLEN 16384 + +static void set_md5sum(HIO_HANDLE *f, unsigned char *digest) +{ + unsigned char buf[BUFLEN]; + MD5_CTX ctx; + int bytes_read; + + hio_seek(f, 0, SEEK_SET); + + MD5Init(&ctx); + while ((bytes_read = hio_read(buf, 1, BUFLEN, f)) > 0) { + MD5Update(&ctx, buf, bytes_read); + } + MD5Final(digest, &ctx); +} + +static char *get_dirname(const char *name) +{ + char *dirname; + const char *p; + ptrdiff_t len; + + if ((p = strrchr(name, '/')) != NULL) { + len = p - name + 1; + dirname = (char *) malloc(len + 1); + if (dirname != NULL) { + memcpy(dirname, name, len); + dirname[len] = 0; + } + } else { + dirname = libxmp_strdup(""); + } + + return dirname; +} + +static char *get_basename(const char *name) +{ + const char *p; + char *basename; + + if ((p = strrchr(name, '/')) != NULL) { + basename = libxmp_strdup(p + 1); + } else { + basename = libxmp_strdup(name); + } + + return basename; +} +#endif /* LIBXMP_CORE_PLAYER */ + +static int test_module(struct xmp_test_info *info, HIO_HANDLE *h) +{ + char buf[XMP_NAME_SIZE]; + int i; + + if (info != NULL) { + *info->name = 0; /* reset name prior to testing */ + *info->type = 0; /* reset type prior to testing */ + } + + for (i = 0; format_loaders[i] != NULL; i++) { + hio_seek(h, 0, SEEK_SET); + if (format_loaders[i]->test(h, buf, 0) == 0) { + int is_prowizard = 0; + +#ifndef LIBXMP_NO_PROWIZARD + if (strcmp(format_loaders[i]->name, "prowizard") == 0) { + hio_seek(h, 0, SEEK_SET); + pw_test_format(h, buf, 0, info); + is_prowizard = 1; + } +#endif + + if (info != NULL && !is_prowizard) { + strncpy(info->name, buf, XMP_NAME_SIZE - 1); + info->name[XMP_NAME_SIZE - 1] = '\0'; + + strncpy(info->type, format_loaders[i]->name, + XMP_NAME_SIZE - 1); + info->type[XMP_NAME_SIZE - 1] = '\0'; + } + return 0; + } + } + return -XMP_ERROR_FORMAT; +} + +int xmp_test_module(const char *path, struct xmp_test_info *info) +{ + HIO_HANDLE *h; +#ifndef LIBXMP_NO_DEPACKERS + char *temp = NULL; +#endif + int ret; + + ret = libxmp_get_filetype(path); + + if (ret == XMP_FILETYPE_NONE) { + return -XMP_ERROR_SYSTEM; + } + if (ret & XMP_FILETYPE_DIR) { + errno = EISDIR; + return -XMP_ERROR_SYSTEM; + } + + if ((h = hio_open(path, "rb")) == NULL) + return -XMP_ERROR_SYSTEM; + +#ifndef LIBXMP_NO_DEPACKERS + if (libxmp_decrunch(h, path, &temp) < 0) { + ret = -XMP_ERROR_DEPACK; + goto err; + } +#endif + + ret = test_module(info, h); + +#ifndef LIBXMP_NO_DEPACKERS + err: + hio_close(h); + unlink_temp_file(temp); +#else + hio_close(h); +#endif + return ret; +} + +int xmp_test_module_from_memory(const void *mem, long size, struct xmp_test_info *info) +{ + HIO_HANDLE *h; + int ret; + + if (size <= 0) { + return -XMP_ERROR_INVALID; + } + + if ((h = hio_open_mem(mem, size, 0)) == NULL) + return -XMP_ERROR_SYSTEM; + + ret = test_module(info, h); + + hio_close(h); + return ret; +} + +int xmp_test_module_from_file(void *file, struct xmp_test_info *info) +{ + HIO_HANDLE *h; + int ret; +#ifndef LIBXMP_NO_DEPACKERS + char *temp = NULL; +#endif + + if ((h = hio_open_file((FILE *)file)) == NULL) + return -XMP_ERROR_SYSTEM; + +#ifndef LIBXMP_NO_DEPACKERS + if (libxmp_decrunch(h, NULL, &temp) < 0) { + ret = -XMP_ERROR_DEPACK; + goto err; + } +#endif + + ret = test_module(info, h); + +#ifndef LIBXMP_NO_DEPACKERS + err: + hio_close(h); + unlink_temp_file(temp); +#else + hio_close(h); +#endif + return ret; +} + +int xmp_test_module_from_callbacks(void *priv, struct xmp_callbacks callbacks, + struct xmp_test_info *info) +{ + HIO_HANDLE *h; + int ret; + + if ((h = hio_open_callbacks(priv, callbacks)) == NULL) + return -XMP_ERROR_SYSTEM; + + ret = test_module(info, h); + + hio_close(h); + return ret; +} + +static int load_module(xmp_context opaque, HIO_HANDLE *h) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int i, j, ret; + int test_result, load_result; + + libxmp_load_prologue(ctx); + + D_(D_WARN "load"); + test_result = load_result = -1; + for (i = 0; format_loaders[i] != NULL; i++) { + hio_seek(h, 0, SEEK_SET); + + D_(D_WARN "test %s", format_loaders[i]->name); + test_result = format_loaders[i]->test(h, NULL, 0); + if (test_result == 0) { + hio_seek(h, 0, SEEK_SET); + D_(D_WARN "load format: %s", format_loaders[i]->name); + load_result = format_loaders[i]->loader(m, h, 0); + break; + } + } + + if (test_result < 0) { + xmp_release_module(opaque); + return -XMP_ERROR_FORMAT; + } + + if (load_result < 0) { + goto err_load; + } + + /* Sanity check: number of channels, module length */ + if (mod->chn > XMP_MAX_CHANNELS || mod->len > XMP_MAX_MOD_LENGTH) { + goto err_load; + } + + /* Sanity check: channel pan */ + for (i = 0; i < mod->chn; i++) { + if (mod->xxc[i].vol < 0 || mod->xxc[i].vol > 0xff) { + goto err_load; + } + if (mod->xxc[i].pan < 0 || mod->xxc[i].pan > 0xff) { + goto err_load; + } + } + + /* Sanity check: patterns */ + if (mod->xxp == NULL) { + goto err_load; + } + for (i = 0; i < mod->pat; i++) { + if (mod->xxp[i] == NULL) { + goto err_load; + } + for (j = 0; j < mod->chn; j++) { + int t = mod->xxp[i]->index[j]; + if (t < 0 || t >= mod->trk || mod->xxt[t] == NULL) { + goto err_load; + } + } + } + + libxmp_adjust_string(mod->name); + for (i = 0; i < mod->ins; i++) { + libxmp_adjust_string(mod->xxi[i].name); + } + for (i = 0; i < mod->smp; i++) { + libxmp_adjust_string(mod->xxs[i].name); + } + +#ifndef LIBXMP_CORE_PLAYER + if (test_result == 0 && load_result == 0) + set_md5sum(h, m->md5); +#endif + + libxmp_load_epilogue(ctx); + + ret = libxmp_prepare_scan(ctx); + if (ret < 0) { + xmp_release_module(opaque); + return ret; + } + + ret = libxmp_scan_sequences(ctx); + if (ret < 0) { + xmp_release_module(opaque); + return -XMP_ERROR_LOAD; + } + + ctx->state = XMP_STATE_LOADED; + + return 0; + + err_load: + xmp_release_module(opaque); + return -XMP_ERROR_LOAD; +} + +int xmp_load_module(xmp_context opaque, const char *path) +{ + struct context_data *ctx = (struct context_data *)opaque; +#ifndef LIBXMP_CORE_PLAYER + struct module_data *m = &ctx->m; +#endif +#ifndef LIBXMP_NO_DEPACKERS + char *temp_name; +#endif + HIO_HANDLE *h; + int ret; + + D_(D_WARN "path = %s", path); + + ret = libxmp_get_filetype(path); + + if (ret == XMP_FILETYPE_NONE) { + return -XMP_ERROR_SYSTEM; + } + if (ret & XMP_FILETYPE_DIR) { + errno = EISDIR; + return -XMP_ERROR_SYSTEM; + } + + if ((h = hio_open(path, "rb")) == NULL) { + return -XMP_ERROR_SYSTEM; + } + +#ifndef LIBXMP_NO_DEPACKERS + D_(D_INFO "decrunch"); + if (libxmp_decrunch(h, path, &temp_name) < 0) { + ret = -XMP_ERROR_DEPACK; + goto err; + } +#endif + + if (ctx->state > XMP_STATE_UNLOADED) + xmp_release_module(opaque); + +#ifndef LIBXMP_CORE_PLAYER + m->dirname = get_dirname(path); + if (m->dirname == NULL) { + ret = -XMP_ERROR_SYSTEM; + goto err; + } + + m->basename = get_basename(path); + if (m->basename == NULL) { + ret = -XMP_ERROR_SYSTEM; + goto err; + } + + m->filename = path; /* For ALM, SSMT, etc */ + m->size = hio_size(h); +#else + ctx->m.filename = NULL; + ctx->m.dirname = NULL; + ctx->m.basename = NULL; +#endif + + ret = load_module(opaque, h); + hio_close(h); + +#ifndef LIBXMP_NO_DEPACKERS + unlink_temp_file(temp_name); +#endif + + return ret; + +#ifndef LIBXMP_CORE_PLAYER + err: + hio_close(h); +#ifndef LIBXMP_NO_DEPACKERS + unlink_temp_file(temp_name); +#endif + return ret; +#endif +} + +int xmp_load_module_from_memory(xmp_context opaque, const void *mem, long size) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + HIO_HANDLE *h; + int ret; + + if (size <= 0) { + return -XMP_ERROR_INVALID; + } + + if ((h = hio_open_mem(mem, size, 0)) == NULL) + return -XMP_ERROR_SYSTEM; + + if (ctx->state > XMP_STATE_UNLOADED) + xmp_release_module(opaque); + + m->filename = NULL; + m->basename = NULL; + m->dirname = NULL; + m->size = size; + + ret = load_module(opaque, h); + + hio_close(h); + + return ret; +} + +int xmp_load_module_from_file(xmp_context opaque, void *file, long size) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + HIO_HANDLE *h; + int ret; + + if ((h = hio_open_file((FILE *)file)) == NULL) + return -XMP_ERROR_SYSTEM; + + if (ctx->state > XMP_STATE_UNLOADED) + xmp_release_module(opaque); + + m->filename = NULL; + m->basename = NULL; + m->dirname = NULL; + m->size = hio_size(h); + + ret = load_module(opaque, h); + + hio_close(h); + + return ret; +} + +int xmp_load_module_from_callbacks(xmp_context opaque, void *priv, + struct xmp_callbacks callbacks) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + HIO_HANDLE *h; + int ret; + + if ((h = hio_open_callbacks(priv, callbacks)) == NULL) + return -XMP_ERROR_SYSTEM; + + if (ctx->state > XMP_STATE_UNLOADED) + xmp_release_module(opaque); + + m->filename = NULL; + m->basename = NULL; + m->dirname = NULL; + m->size = hio_size(h); + + ret = load_module(opaque, h); + + hio_close(h); + + return ret; +} + +void xmp_release_module(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int i; + + /* can't test this here, we must call release_module to clean up + * load errors + if (ctx->state < XMP_STATE_LOADED) + return; + */ + + if (ctx->state > XMP_STATE_LOADED) + xmp_end_player(opaque); + + ctx->state = XMP_STATE_UNLOADED; + + D_(D_INFO "Freeing memory"); + +#ifndef LIBXMP_CORE_PLAYER + libxmp_release_module_extras(ctx); +#endif + + if (mod->xxt != NULL) { + for (i = 0; i < mod->trk; i++) { + free(mod->xxt[i]); + } + free(mod->xxt); + mod->xxt = NULL; + } + + if (mod->xxp != NULL) { + for (i = 0; i < mod->pat; i++) { + free(mod->xxp[i]); + } + free(mod->xxp); + mod->xxp = NULL; + } + + if (mod->xxi != NULL) { + for (i = 0; i < mod->ins; i++) { + free(mod->xxi[i].sub); + free(mod->xxi[i].extra); + } + free(mod->xxi); + mod->xxi = NULL; + } + + if (mod->xxs != NULL) { + for (i = 0; i < mod->smp; i++) { + libxmp_free_sample(&mod->xxs[i]); + } + free(mod->xxs); + mod->xxs = NULL; + } + + free(m->xtra); + free(m->midi); + m->xtra = NULL; + m->midi = NULL; + + libxmp_free_scan(ctx); + + free(m->comment); + m->comment = NULL; + + D_("free dirname/basename"); + free(m->dirname); + free(m->basename); + m->basename = NULL; + m->dirname = NULL; +} + +void xmp_scan_module(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + + if (ctx->state < XMP_STATE_LOADED) + return; + + libxmp_scan_sequences(ctx); +} diff --git a/thirdparty/libxmp/src/load_helpers.c b/thirdparty/libxmp/src/load_helpers.c new file mode 100644 index 000000000..c0c5e8826 --- /dev/null +++ b/thirdparty/libxmp/src/load_helpers.c @@ -0,0 +1,560 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "common.h" +#include "loaders/loader.h" + + +#ifndef LIBXMP_CORE_PLAYER + +/* + * Handle special "module quirks" that can't be detected automatically + * such as Protracker 2.x compatibility, vblank timing, etc. + */ + +struct module_quirk { + uint8 md5[16]; + int flags; + int mode; +}; + +const struct module_quirk mq[] = { + /* "No Mercy" by Alf/VTL (added by Martin Willers) */ + { + { 0x36, 0x6e, 0xc0, 0xfa, 0x96, 0x2a, 0xeb, 0xee, + 0x03, 0x4a, 0xa2, 0xdb, 0xaa, 0x49, 0xaa, 0xea }, + 0, XMP_MODE_PROTRACKER + }, + + /* mod.souvenir of china */ + { + { 0x93, 0xf1, 0x46, 0xae, 0xb7, 0x58, 0xc3, 0x9d, + 0x8b, 0x5f, 0xbc, 0x98, 0xbf, 0x23, 0x7a, 0x43 }, + XMP_FLAGS_FIXLOOP, XMP_MODE_AUTO + }, + +#if 0 + /* "siedler ii" (added by Daniel Ã…kerud) */ + /* Timing fixed by vblank scan compare. CIA: 32m10s VBlank: 12m32s */ + { + { 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73, + 0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, +#endif + + /* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */ + { + { 0xe9, 0x98, 0x01, 0x2c, 0x70, 0x0e, 0xb4, 0x3a, + 0xf0, 0x32, 0x17, 0x11, 0x30, 0x58, 0x29, 0xb2 }, + 0, XMP_MODE_NOISETRACKER + }, + +#if 0 + /* -- Already covered by Noisetracker fingerprinting -- */ + + /* Another version of Klisje paa klisje sent by Steve Fernandez */ + { + { 0x12, 0x19, 0x1c, 0x90, 0x41, 0xe3, 0xfd, 0x70, + 0xb7, 0xe6, 0xb3, 0x94, 0x8b, 0x21, 0x07, 0x63 }, + XMP_FLAGS_VBLANK + }, +#endif + + /* "((((( nebulos )))))" sent by Tero Auvinen (AMP version) */ + { + { 0x51, 0x6e, 0x8d, 0xcc, 0x35, 0x7d, 0x50, 0xde, + 0xa9, 0x85, 0xbe, 0xbf, 0x90, 0x2e, 0x42, 0xdc }, + 0, XMP_MODE_NOISETRACKER + }, + + /* Purple Motion's Sundance.mod, Music Channel BBS edit */ + { + { 0x5d, 0x3e, 0x1e, 0x08, 0x28, 0x52, 0x12, 0xc7, + 0x17, 0x64, 0x95, 0x75, 0x98, 0xe6, 0x95, 0xc1 }, + 0, XMP_MODE_ST3 + }, + + /* Asle's Ode to Protracker */ + { + { 0x97, 0xa3, 0x7d, 0x30, 0xd7, 0xae, 0x6d, 0x50, + 0xc9, 0x62, 0xe9, 0xd8, 0x87, 0x1b, 0x7e, 0x8a }, + 0, XMP_MODE_PROTRACKER + }, + + /* grooving3.mod */ + /* length 150778 crc32c 0xfdcf9aadU */ + { + { 0xdb, 0x61, 0x22, 0x44, 0x39, 0x85, 0x74, 0xe9, + 0xfa, 0x11, 0xb8, 0xfb, 0x87, 0xe8, 0xde, 0xc5, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* mod.Rundgren */ + /* length 195078 crc32c 0x8fa827a4U */ + { + { 0x9a, 0xdb, 0xb2, 0x09, 0x07, 0x1c, 0x44, 0x82, + 0xc5, 0xdf, 0x83, 0x52, 0xcc, 0x73, 0x9f, 0x20, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* dance feeling by Audiomonster */ + /* length 169734 crc32c 0x79fa2c9bU */ + { + { 0x31, 0x2c, 0x3d, 0xaa, 0x5f, 0x1a, 0x54, 0x44, + 0x9d, 0xf7, 0xc4, 0x41, 0x8a, 0xc5, 0x01, 0x02, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* knights melody by Audiomonster */ + /* length 77798 crc32c 0x7bf19c5bU */ + { + { 0x31, 0xc3, 0x0e, 0x32, 0xfc, 0x99, 0x95, 0xd2, + 0x97, 0x20, 0xb3, 0x77, 0x50, 0x05, 0xfe, 0xa5, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* hcomme by Bouffon */ + /* length 71346 crc32c 0x4ad49cb3U */ + { + { 0x6e, 0xf9, 0x78, 0xc1, 0x80, 0xae, 0x51, 0x06, + 0x05, 0x7c, 0x6e, 0xd0, 0x26, 0x7e, 0xfe, 0x3d, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* ((((aquapool)))) by Dolphin */ + /* length 62932 crc32c 0x05b103fcU */ + { + { 0xff, 0x0b, 0xe0, 0x26, 0xc6, 0x31, 0xb5, 0x9b, + 0x94, 0x83, 0x94, 0x99, 0x7e, 0x24, 0x7c, 0xdd, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* 100yarddash by Dr. Awesome */ + /* length 104666 crc32c 0xd2b0e4a6U */ + { + { 0x5b, 0xff, 0x2f, 0xb8, 0xef, 0x3c, 0xbe, 0x55, + 0xa8, 0xe2, 0xa7, 0xcf, 0x5c, 0xbd, 0xdd, 0xb2, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* jazz-reggae-funk by Droid */ + /* length 115564 crc32c 0x41ff635fU */ + { + { 0xe5, 0x6e, 0x31, 0x2f, 0x62, 0x80, 0xc1, 0x9d, + 0x2f, 0x24, 0x54, 0xf3, 0x89, 0x3f, 0x94, 0x6c, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* hard and heavy by Fish */ + /* length 69814 crc32c 0x1f09d3d5U */ + { + { 0x6b, 0xce, 0x39, 0x94, 0x75, 0x42, 0x06, 0x74, + 0xd2, 0x83, 0xbc, 0x5e, 0x7b, 0x42, 0x1f, 0xa0, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* crazy valley by Julius and Droid */ + /* length 97496 crc32c 0xb8eec40eU */ + { + { 0x23, 0x77, 0x18, 0x1d, 0x21, 0x9b, 0x41, 0x8f, + 0xc1, 0xb4, 0xf4, 0xf8, 0x22, 0xdd, 0xd8, 0xb6, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* THE ILLOGICAL ONE by Rhino */ + /* length 173432 crc32c 0xcb4e2987U */ + { + { 0xd8, 0xc2, 0xbb, 0xe6, 0x11, 0xd0, 0x5c, 0x02, + 0x8e, 0x3b, 0xcb, 0x7c, 0x4a, 0x7d, 0x43, 0xa0, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* sounds of holiday by Spacebrain */ + /* length 309520 crc32c 0x28804a57U */ + { + { 0x36, 0x18, 0x19, 0xa4, 0x9d, 0xa2, 0xa2, 0x6f, + 0x58, 0x60, 0xc4, 0xd9, 0x0d, 0xa2, 0x9f, 0x49, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* sunisinus by Speed-Head */ + /* length 175706 crc32c 0x2e56451bU */ + { + { 0x7e, 0x69, 0x44, 0xb6, 0x38, 0x0d, 0x27, 0x14, + 0x70, 0x5d, 0x44, 0xce, 0xce, 0xdd, 0x37, 0x31, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* eat the fulcrum bop by The Assassin */ + /* length 160286 crc32c 0x583a4683U */ + { + { 0x11, 0xe9, 0x6f, 0x62, 0xe1, 0xc3, 0xc5, 0xcc, + 0x3b, 0xaf, 0xea, 0x69, 0x4b, 0xce, 0x5f, 0xec, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* obvious disaster by Tip */ + /* length 221086 crc32c 0x51c6d489U */ + { + { 0x06, 0x8e, 0x69, 0x01, 0x49, 0x8f, 0xbd, 0x0f, + 0xfc, 0xb7, 0x8f, 0x2a, 0x91, 0xe1, 0x8b, 0xe8, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* alien nation by Turtle */ + /* length 167548 crc32c 0xc9ec1674U */ + { + { 0x71, 0xdf, 0x11, 0xac, 0x5d, 0xec, 0x07, 0xf8, + 0x10, 0x6f, 0x28, 0x8d, 0x47, 0x59, 0x54, 0x9b, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + /* illusions!2 by Zuhl */ + /* length 289770 crc32c 0x6bf5fbcfU */ + { + { 0xca, 0x37, 0x8c, 0x0e, 0x87, 0x4f, 0x1e, 0xcd, + 0xa3, 0xe9, 0x8b, 0xdd, 0x11, 0x46, 0x8d, 0x69, }, + XMP_FLAGS_VBLANK, XMP_MODE_AUTO + }, + + { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + 0, 0 + } +}; + +static void module_quirks(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + int i; + + for (i = 0; mq[i].flags != 0 || mq[i].mode != 0; i++) { + if (!memcmp(m->md5, mq[i].md5, 16)) { + p->flags |= mq[i].flags; + p->mode = mq[i].mode; + } + } +} + +#endif /* LIBXMP_CORE_PLAYER */ + +char *libxmp_adjust_string(char *s) +{ + int i; + + for (i = 0; i < strlen(s); i++) { + if (!isprint((unsigned char)s[i]) || ((uint8) s[i] > 127)) + s[i] = ' '; + } + + while (*s && (s[strlen(s) - 1] == ' ')) { + s[strlen(s) - 1] = 0; + } + + return s; +} + +static void check_envelope(struct xmp_envelope *env) +{ + /* Disable envelope if invalid number of points */ + if (env->npt <= 0 || env->npt > XMP_MAX_ENV_POINTS) { + env->flg &= ~XMP_ENVELOPE_ON; + } + + /* Disable envelope loop if invalid loop parameters */ + if (env->lps >= env->npt || env->lpe >= env->npt) { + env->flg &= ~XMP_ENVELOPE_LOOP; + } + + /* Disable envelope sustain if invalid sustain */ + if (env->sus >= env->npt || env->sue >= env->npt) { + env->flg &= ~XMP_ENVELOPE_SUS; + } +} + +static void clamp_volume_envelope(struct module_data *m, struct xmp_envelope *env) +{ + /* Clamp broken values in the volume envelope to the expected range. */ + if (env->flg & XMP_ENVELOPE_ON) { + int i; + for (i = 0; i < env->npt; i++) { + int16 *data = &env->data[i * 2 + 1]; + CLAMP(*data, 0, m->volbase); + } + } +} + +void libxmp_load_prologue(struct context_data *ctx) +{ + struct module_data *m = &ctx->m; + int i; + + /* Reset variables */ + memset(&m->mod, 0, sizeof (struct xmp_module)); + m->rrate = PAL_RATE; + m->c4rate = C4_PAL_RATE; + m->volbase = 0x40; + m->gvol = m->gvolbase = 0x40; + m->vol_table = NULL; + m->quirk = 0; + m->read_event_type = READ_EVENT_MOD; + m->period_type = PERIOD_AMIGA; + m->compare_vblank = 0; + m->comment = NULL; + m->scan_cnt = NULL; + m->midi = NULL; + + /* Set defaults */ + m->mod.pat = 0; + m->mod.trk = 0; + m->mod.chn = 4; + m->mod.ins = 0; + m->mod.smp = 0; + m->mod.spd = 6; + m->mod.bpm = 125; + m->mod.len = 0; + m->mod.rst = 0; + +#ifndef LIBXMP_CORE_PLAYER + m->extra = NULL; +#endif + + m->time_factor = DEFAULT_TIME_FACTOR; + + for (i = 0; i < 64; i++) { + int pan = (((i + 1) / 2) % 2) * 0xff; + m->mod.xxc[i].pan = 0x80 + (pan - 0x80) * m->defpan / 100; + m->mod.xxc[i].vol = 0x40; + m->mod.xxc[i].flg = 0; + } +} + +void libxmp_load_epilogue(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int i, j; + + mod->gvl = m->gvol; + + /* Sanity check for module parameters */ + CLAMP(mod->len, 0, XMP_MAX_MOD_LENGTH); + CLAMP(mod->pat, 0, 257); /* some formats have an extra pattern */ + CLAMP(mod->ins, 0, 255); + CLAMP(mod->smp, 0, MAX_SAMPLES); + CLAMP(mod->chn, 0, XMP_MAX_CHANNELS); + + /* Fix cases where the restart value is invalid e.g. kc_fall8.xm + * from http://aminet.net/mods/mvp/mvp_0002.lha (reported by + * Ralf Hoffmann ) + */ + if (mod->rst >= mod->len) { + mod->rst = 0; + } + + /* Sanity check for tempo and BPM */ + if (mod->spd <= 0 || mod->spd > 255) { + mod->spd = 6; + } + CLAMP(mod->bpm, XMP_MIN_BPM, 1000); + + /* Set appropriate values for instrument volumes and subinstrument + * global volumes when QUIRK_INSVOL is not set, to keep volume values + * consistent if the user inspects struct xmp_module. We can later + * set volumes in the loaders and eliminate the quirk. + */ + for (i = 0; i < mod->ins; i++) { + if (~m->quirk & QUIRK_INSVOL) { + mod->xxi[i].vol = m->volbase; + } + for (j = 0; j < mod->xxi[i].nsm; j++) { + if (~m->quirk & QUIRK_INSVOL) { + mod->xxi[i].sub[j].gvl = m->volbase; + } + } + } + + /* Sanity check for envelopes + */ + for (i = 0; i < mod->ins; i++) { + check_envelope(&mod->xxi[i].aei); + check_envelope(&mod->xxi[i].fei); + check_envelope(&mod->xxi[i].pei); + clamp_volume_envelope(m, &mod->xxi[i].aei); + } + +#ifndef LIBXMP_CORE_DISABLE_IT + /* TODO: there's no unintrusive and clean way to get this struct into + * libxmp_load_sample currently, so bound these fields here for now. */ + for (i = 0; i < mod->smp; i++) { + struct xmp_sample *xxs = &mod->xxs[i]; + struct extra_sample_data *xtra = &m->xtra[i]; + if (xtra->sus < 0) { + xtra->sus = 0; + } + if (xtra->sue > xxs->len) { + xtra->sue = xxs->len; + } + if (xtra->sus >= xxs->len || xtra->sus >= xtra->sue) { + xtra->sus = xtra->sue = 0; + xxs->flg &= ~(XMP_SAMPLE_SLOOP | XMP_SAMPLE_SLOOP_BIDIR); + } + } +#endif + + p->filter = 0; + p->mode = XMP_MODE_AUTO; + p->flags = p->player_flags; +#ifndef LIBXMP_CORE_PLAYER + module_quirks(ctx); +#endif + libxmp_set_player_mode(ctx); +} + +int libxmp_prepare_scan(struct context_data *ctx) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int i, ord; + + if (mod->xxp == NULL || mod->xxt == NULL) + return -XMP_ERROR_LOAD; + ord = 0; + while (ord < mod->len && mod->xxo[ord] >= mod->pat) { + ord++; + } + + if (ord >= mod->len) { + mod->len = 0; + return 0; + } + + m->scan_cnt = (uint8 **) calloc(mod->len, sizeof(uint8 *)); + if (m->scan_cnt == NULL) + return -XMP_ERROR_SYSTEM; + + for (i = 0; i < mod->len; i++) { + int pat_idx = mod->xxo[i]; + struct xmp_pattern *pat; + + /* Add pattern if referenced in orders */ + if (pat_idx < mod->pat && !mod->xxp[pat_idx]) { + if (libxmp_alloc_pattern(mod, pat_idx) < 0) { + return -XMP_ERROR_SYSTEM; + } + } + + pat = pat_idx >= mod->pat ? NULL : mod->xxp[pat_idx]; + m->scan_cnt[i] = (uint8 *) calloc(1, (pat && pat->rows)? pat->rows : 1); + if (m->scan_cnt[i] == NULL) + return -XMP_ERROR_SYSTEM; + } + + return 0; +} + +void libxmp_free_scan(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int i; + + if (m->scan_cnt) { + for (i = 0; i < mod->len; i++) + free(m->scan_cnt[i]); + + free(m->scan_cnt); + m->scan_cnt = NULL; + } + + free(p->scan); + p->scan = NULL; +} + +/* Process player personality flags */ +int libxmp_set_player_mode(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + int q; + + switch (p->mode) { + case XMP_MODE_AUTO: + break; + case XMP_MODE_MOD: + m->c4rate = C4_PAL_RATE; + m->quirk = 0; + m->read_event_type = READ_EVENT_MOD; + m->period_type = PERIOD_AMIGA; + break; + case XMP_MODE_NOISETRACKER: + m->c4rate = C4_PAL_RATE; + m->quirk = QUIRK_NOBPM; + m->read_event_type = READ_EVENT_MOD; + m->period_type = PERIOD_MODRNG; + break; + case XMP_MODE_PROTRACKER: + m->c4rate = C4_PAL_RATE; + m->quirk = QUIRK_PROTRACK; + m->read_event_type = READ_EVENT_MOD; + m->period_type = PERIOD_MODRNG; + break; + case XMP_MODE_S3M: + q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM); + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_ST3 | q; + m->read_event_type = READ_EVENT_ST3; + break; + case XMP_MODE_ST3: + q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM); + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q; + m->read_event_type = READ_EVENT_ST3; + break; + case XMP_MODE_ST3GUS: + q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM); + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q; + m->quirk &= ~QUIRK_RSTCHN; + m->read_event_type = READ_EVENT_ST3; + break; + case XMP_MODE_XM: + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_FT2; + m->read_event_type = READ_EVENT_FT2; + break; + case XMP_MODE_FT2: + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_FT2 | QUIRK_FT2BUGS; + m->read_event_type = READ_EVENT_FT2; + break; + case XMP_MODE_IT: + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV; + m->read_event_type = READ_EVENT_IT; + break; + case XMP_MODE_ITSMP: + m->c4rate = C4_NTSC_RATE; + m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV; + m->quirk &= ~(QUIRK_VIRTUAL | QUIRK_RSTCHN); + m->read_event_type = READ_EVENT_IT; + break; + default: + return -1; + } + + if (p->mode != XMP_MODE_AUTO) + m->compare_vblank = 0; + + return 0; +} + diff --git a/thirdparty/libxmp/src/loaders/669_load.c b/thirdparty/libxmp/src/loaders/669_load.c new file mode 100644 index 000000000..fc3f27a7c --- /dev/null +++ b/thirdparty/libxmp/src/loaders/669_load.c @@ -0,0 +1,262 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" + + +static int c669_test (HIO_HANDLE *, char *, const int); +static int c669_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_669 = { + "Composer 669", + c669_test, + c669_load +}; + +static int c669_test(HIO_HANDLE *f, char *t, const int start) +{ + uint16 id; + + id = hio_read16b(f); + if (id != 0x6966 && id != 0x4a4e) + return -1; + + hio_seek(f, 110, SEEK_SET); + if (hio_read8(f) > 64) + return -1; + if (hio_read8(f) > 128) + return -1; + + hio_seek(f, 240, SEEK_SET); + if (hio_read8(f) != 0xff) + return -1; + + hio_seek(f, start + 2, SEEK_SET); + libxmp_read_title(f, t, 36); + + return 0; +} + + +struct c669_file_header { + uint8 marker[2]; /* 'if'=standard, 'JN'=extended */ + uint8 message[108]; /* Song message */ + uint8 nos; /* Number of samples (0-64) */ + uint8 nop; /* Number of patterns (0-128) */ + uint8 loop; /* Loop order number */ + uint8 order[128]; /* Order list */ + uint8 speed[128]; /* Tempo list for patterns */ + uint8 pbrk[128]; /* Break list for patterns */ +}; + +struct c669_instrument_header { + uint8 name[13]; /* ASCIIZ instrument name */ + uint32 length; /* Instrument length */ + uint32 loop_start; /* Instrument loop start */ + uint32 loopend; /* Instrument loop end */ +}; + + +#define NONE 0xff + +/* Effects bug fixed by Miod Vallat */ + +static const uint8 fx[6] = { + FX_669_PORTA_UP, + FX_669_PORTA_DN, + FX_669_TPORTA, + FX_669_FINETUNE, + FX_669_VIBRATO, + FX_SPEED_CP +}; + + +static int c669_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct c669_file_header sfh; + struct c669_instrument_header sih; + uint8 ev[3]; + + LOAD_INIT(); + + hio_read(sfh.marker, 2, 1, f); /* 'if'=standard, 'JN'=extended */ + hio_read(sfh.message, 108, 1, f); /* Song message */ + sfh.nos = hio_read8(f); /* Number of samples (0-64) */ + sfh.nop = hio_read8(f); /* Number of patterns (0-128) */ + + /* Sanity check */ + if (sfh.nos > 64 || sfh.nop > 128) + return -1; + + sfh.loop = hio_read8(f); /* Loop order number */ + if (hio_read(sfh.order, 1, 128, f) != 128) /* Order list */ + return -1; + if (hio_read(sfh.speed, 1, 128, f) != 128) /* Tempo list for patterns */ + return -1; + if (hio_read(sfh.pbrk, 1, 128, f) != 128) /* Break list for patterns */ + return -1; + + mod->chn = 8; + mod->ins = sfh.nos; + mod->pat = sfh.nop; + mod->trk = mod->chn * mod->pat; + for (i = 0; i < 128; i++) { + if (sfh.order[i] > sfh.nop) + break; + } + mod->len = i; + memcpy (mod->xxo, sfh.order, mod->len); + mod->spd = 6; + mod->bpm = 78; + mod->smp = mod->ins; + + m->period_type = PERIOD_CSPD; + m->c4rate = C4_NTSC_RATE; + + libxmp_copy_adjust(mod->name, sfh.message, 36); + libxmp_set_type(m, strncmp((char *)sfh.marker, "if", 2) ? + "UNIS 669" : "Composer 669"); + + MODULE_INFO(); + + m->comment = (char *) malloc(109); + memcpy(m->comment, sfh.message, 108); + m->comment[108] = 0; + + /* Read and convert instruments and samples */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + D_(D_INFO "Instruments: %d", mod->pat); + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + hio_read (sih.name, 13, 1, f); /* ASCIIZ instrument name */ + sih.length = hio_read32l(f); /* Instrument size */ + sih.loop_start = hio_read32l(f); /* Instrument loop start */ + sih.loopend = hio_read32l(f); /* Instrument loop end */ + + /* Sanity check */ + if (sih.length > MAX_SAMPLE_SIZE) + return -1; + + xxs->len = sih.length; + xxs->lps = sih.loop_start; + xxs->lpe = sih.loopend >= 0xfffff ? 0 : sih.loopend; + xxs->flg = xxs->lpe ? XMP_SAMPLE_LOOP : 0; /* 1 == Forward loop */ + + sub->vol = 0x40; + sub->pan = 0x80; + sub->sid = i; + + if (xxs->len > 0) + xxi->nsm = 1; + + libxmp_instrument_name(mod, i, sih.name, 13); + + D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c", i, + xxi->name, xxs->len, xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' '); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + for (i = 0; i < mod->pat; i++) { + int pbrk; + + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + event = &EVENT(i, 0, 0); + event->f2t = FX_SPEED_CP; + event->f2p = sfh.speed[i]; + + pbrk = sfh.pbrk[i]; + if (pbrk >= 64) + return -1; + + event = &EVENT(i, 1, pbrk); + event->f2t = FX_BREAK; + event->f2p = 0; + + for (j = 0; j < 64 * 8; j++) { + event = &EVENT(i, j % 8, j / 8); + if(hio_read(ev, 1, 3, f) < 3) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + + if ((ev[0] & 0xfe) != 0xfe) { + event->note = 1 + 36 + (ev[0] >> 2); + event->ins = 1 + MSN(ev[1]) + ((ev[0] & 0x03) << 4); + } + + if (ev[0] != 0xff) + event->vol = (LSN(ev[1]) << 2) + 1; + + if (ev[2] != 0xff) { + if (MSN(ev[2]) >= ARRAY_SIZE(fx)) + continue; + + event->fxt = fx[MSN(ev[2])]; + event->fxp = LSN(ev[2]); + + if (event->fxt == FX_SPEED_CP) { + event->f2t = FX_PER_CANCEL; + } + } + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len <= 2) + continue; + if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) + return -1; + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = DEFPAN((i % 2) * 0xff); + } + + m->quirk |= QUIRK_PBALL|QUIRK_PERPAT; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/abk_load.c b/thirdparty/libxmp/src/loaders/abk_load.c new file mode 100644 index 000000000..16c3bac58 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/abk_load.c @@ -0,0 +1,655 @@ +/* Extended Module Player + * AMOS/STOS Music Bank Loader + * Copyright (C) 2014 Stephen J Leary and Claudio Matsuoka + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../effects.h" +#include "../period.h" + +#define AMOS_BANK 0x416d426b +#define AMOS_MUSIC_TYPE 0x0003 +#define AMOS_MAIN_HEADER 0x14L +#define AMOS_STRING_LEN 0x10 +#define AMOS_BASE_FREQ 8192 +#define AMOS_ABK_CHANNELS 4 +#define ABK_HEADER_SECTION_COUNT 3 + +static int abk_test (HIO_HANDLE *, char *, const int); +static int abk_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_abk = +{ + "AMOS Music Bank", + abk_test, + abk_load +}; + +/** + * @class abk_header + * @brief represents the main ABK header. + */ +struct abk_header +{ + uint32 instruments_offset; + uint32 songs_offset; + uint32 patterns_offset; +}; + +/** + * @class abk_song + * @brief represents a song in an ABK module. + */ +struct abk_song +{ + uint32 playlist_offset[AMOS_ABK_CHANNELS]; + uint16 tempo; + char song_name[AMOS_STRING_LEN]; +}; + +/** + * @class abk_playlist + * @brief represents an ABK playlist. + */ +struct abk_playlist +{ + uint16 length; + uint16 *pattern; +}; + +/** + * @class abk_instrument + * @brief represents an ABK instrument. + */ +struct abk_instrument +{ + uint32 sample_offset; + uint32 sample_length; + + uint32 repeat_offset; + uint16 repeat_end; + + uint16 sample_volume; + + char sample_name[AMOS_STRING_LEN]; +}; + + +/** + * @brief read the ABK playlist out from the file stream. This method malloc's some memory for the playlist + * and can realloc if the playlist is very long. + * @param f the stream to read from + * @param playlist_offset the offset to the playlist sections. + * @param playlist this structure is populated with the result. + */ +static void read_abk_playlist(HIO_HANDLE *f, uint32 playlist_offset, struct abk_playlist *playlist) +{ + uint16 playdata; + int arraysize; + + arraysize = 64; + + /* clear the length */ + playlist->length = 0; + + /* move to the start of the songs data section. */ + hio_seek(f, playlist_offset, SEEK_SET); + + playlist->pattern = (uint16 *) malloc(arraysize * sizeof(uint16)); + + playdata = hio_read16b(f); + + while((playdata != 0xFFFF) && (playdata != 0xFFFE)) + { + /* i hate doing a realloc in a loop + but i'd rather not traverse the list twice.*/ + + if (playlist->length >= arraysize) + { + arraysize *= 2; + playlist->pattern = (uint16 *) realloc(playlist->pattern , arraysize * sizeof(uint16)); + } + + playlist->pattern[playlist->length++] = playdata; + playdata = hio_read16b(f); + }; +} + +static int read_abk_song(HIO_HANDLE *f, struct abk_song *song, uint32 songs_section_offset) +{ + int i; + uint32 song_section; + + /* move to the start of the songs data section */ + hio_seek(f, songs_section_offset, SEEK_SET); + + if (hio_read16b(f) != 1) + { + /* we only support a single song. + * in a an abk file for now */ + return -1; + } + + song_section = hio_read32b(f); + + if (hio_seek(f, songs_section_offset + song_section, SEEK_SET) < 0) { + return -1; + } + + for (i=0; iplaylist_offset[i] = hio_read16b(f) + songs_section_offset + song_section; + } + + song->tempo = hio_read16b(f); + + /* unused. just progress the file pointer forward */ + (void) hio_read16b(f); + + if (hio_read(song->song_name, 1, AMOS_STRING_LEN, f) != AMOS_STRING_LEN) { + return -1; + } + + return 0; +} + +/** + * @brief reads an ABK pattern into a xmp_event structure. + * @param f stream to read data from. + * @param events events object to populate. + * @param pattern_offset_abs the absolute file offset to the start of the patter to read. + * @return returns the size of the pattern. + */ +static int read_abk_pattern(HIO_HANDLE *f, struct xmp_event *events, uint32 pattern_offset_abs) +{ + uint8 position; + uint8 command; + uint8 param; + uint8 inst = 0; + uint8 jumped = 0; + uint8 per_command = 0; + uint8 per_param = 0; + + uint16 delay; + uint16 patdata; + + int storepos; + if ((storepos = hio_tell(f)) < 0) { + return -1; + } + + /* count how many abk positions are used in this pattern */ + position = 0; + + hio_seek(f, pattern_offset_abs, SEEK_SET); + + /* read the first bit of pattern data */ + patdata = hio_read16b(f); + + while ((patdata != 0x8000) && (jumped == 0)) + { + if (patdata == 0x9100) + { + break; + } + + if (patdata & 0x8000) + { + command = (patdata >> 8) & 0x7F; + param = patdata & 0x007F; + + if (command != 0x03 && command != 0x09 && command != 0x0b && command != 0x0c && command != 0x0d && command < 0x10) { + per_command = 0; + per_param = 0; + } + + switch (command) + { + case 0x01: /* portamento up */ + case 0x0e: + events[position].fxt = FX_PORTA_UP; + events[position].fxp = param; + break; + case 0x02: /* portamento down */ + case 0x0f: + events[position].fxt = FX_PORTA_DN; + events[position].fxp = param; + break; + case 0x03: /* set volume */ + events[position].fxt = FX_VOLSET; + events[position].fxp = param; + break; + case 0x04: /* stop effect */ + break; + case 0x05: /* repeat */ + events[position].fxt = FX_EXTENDED; + if (param == 0) { + events[position].fxp = 0x50; + } else { + events[position].fxp = 0x60 | (param & 0x0f); + } + break; + case 0x06: /* lowpass filter off */ + events[position].fxt = FX_EXTENDED; + events[position].fxp = 0x00; + break; + case 0x07: /* lowpass filter on */ + events[position].fxt = FX_EXTENDED; + events[position].fxp = 0x01; + break; + case 0x08: /* set tempo */ + if (param > 0) { + events[position].fxt = FX_SPEED; + events[position].fxp = 100/param; + } + break; + case 0x09: /* set instrument */ + inst = param + 1; + break; + case 0x0a: /* arpeggio */ + per_command = FX_ARPEGGIO; + per_param = param; + break; + case 0x0b: /* tone portamento */ + per_command = FX_TONEPORTA; + per_param = param; + break; + case 0x0c: /* vibrato */ + per_command = FX_VIBRATO; + per_param = param; + break; + case 0x0d: /* volume slide */ + if (param != 0) { + per_command = FX_VOLSLIDE; + per_param = param; + } else { + per_command = 0; + per_param = 0; + } + break; + case 0x10: /* delay */ + if (per_command != 0 || per_param != 0) { + int i; + for (i = 0; i < param && position < 64; i++) { + events[position].fxt = per_command; + events[position].fxp = per_param; + position++; + } + } else { + position += param; + } + if (position >= 64) { + jumped = 1; + } + break; + case 0x11: /* position jump */ + events[position].fxt = FX_JUMP; + events[position].fxp = param; + /* break out of the loop because we've jumped.*/ + jumped = 1; + break; + default: +#if 0 + /* write out an error for any unprocessed commands.*/ + D_(D_WARN "ABK UNPROCESSED COMMAND: %x,%x\n", command, param); + break; +#else + return -1; +#endif + } + } + else + { + if (patdata & 0x4000) + { + /* old note format.*/ + /* old note format is 2 x 2 byte words with bit 14 set on the first word */ + /* WORD 1: 0x4XDD | X = dont care, D = delay to apply after note. (Usually in 7FDD form). + * WORD 2: 0xXPPP | PPP = Amiga Period */ + + delay = patdata & 0xff; + patdata = hio_read16b(f); + + if (patdata == 0 && delay == 0) + { + /* a zero note, with zero delay ends the pattern */ + break; + } + + if (patdata != 0) + { + /* convert the note from amiga period format to xmp's internal format.*/ + events[position].note = libxmp_period_to_note(patdata & 0x0fff); + events[position].ins = inst; + } + + /* now add on the delay */ + position += delay; + if (position >= 64) { + break; + } + } + else /* new note format */ + { + /* convert the note from amiga period format to xmp's internal format.*/ + events[position].note = libxmp_period_to_note(patdata & 0x0fff); + events[position].ins = inst; + } + } + + /* read the data for the next pass round the loop */ + patdata = hio_read16b(f); + + /* check for an EOF while reading */ + if (hio_eof(f)) + { + break; + } + } + if (position >= 1 && position < 64) { + events[position - 1].f2t = FX_BREAK; + } + + hio_seek(f, storepos, SEEK_SET); + + return position; +} + +static struct abk_instrument* read_abk_insts(HIO_HANDLE *f, uint32 inst_section_size, int count) +{ + uint16 i; + struct abk_instrument *inst; + + if (count < 1) + return NULL; + + inst = (struct abk_instrument*) malloc(count * sizeof(struct abk_instrument)); + memset(inst, 0, count * sizeof(struct abk_instrument)); + + for (i = 0; i < count; i++) + { + uint32 sampleLength; + + inst[i].sample_offset = hio_read32b(f); + inst[i].repeat_offset = hio_read32b(f); + inst[i].sample_length = hio_read16b(f); + inst[i].repeat_end = hio_read16b(f); + inst[i].sample_volume = hio_read16b(f); + sampleLength = hio_read16b(f); + + /* detect a potential bug where the sample length is not specified (and we might already know the length) */ + if (sampleLength > 4) + { + inst[i].sample_length = sampleLength; + } + + if (hio_read(inst[i].sample_name, 1, 16, f) != 16) { + free(inst); + return NULL; + } + } + + return inst; +} + +static int abk_test(HIO_HANDLE *f, char *t, const int start) +{ + struct abk_song song; + char music[8]; + uint32 song_section_offset; + + if (hio_read32b(f) != AMOS_BANK) + { + return -1; + } + + if (hio_read16b(f) != AMOS_MUSIC_TYPE) + { + return -1; + } + + /* skip over length and chip/fastmem.*/ + hio_seek(f, 6, SEEK_CUR); + + if (hio_read(music, 1, 8, f) != 8) /* get the "Music " */ + return -1; + + if (memcmp(music, "Music ", 8)) + { + return -1; + } + + /* Attempt to read title. */ + hio_read32b(f); /* instruments_offset */ + song_section_offset = hio_read32b(f); + + if (t != NULL && read_abk_song(f, &song, AMOS_MAIN_HEADER + song_section_offset) == 0) + { + libxmp_copy_adjust(t, (uint8 *)song.song_name, AMOS_STRING_LEN); + } + + return 0; +} + +static int abk_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + int i,j,k; + uint16 pattern; + uint32 first_sample_offset; + uint32 inst_section_size; + + struct xmp_module *mod = &m->mod; + + struct abk_header main_header; + struct abk_instrument *ci; + struct abk_song song; + struct abk_playlist playlist; + + hio_seek(f, AMOS_MAIN_HEADER, SEEK_SET); + + main_header.instruments_offset = hio_read32b(f); + main_header.songs_offset = hio_read32b(f); + main_header.patterns_offset = hio_read32b(f); + + /* Sanity check */ + if (main_header.instruments_offset > 0x00100000 || + main_header.songs_offset > 0x00100000 || + main_header.patterns_offset > 0x00100000) { + return -1; + } + + inst_section_size = main_header.instruments_offset; + D_(D_INFO "Sample Bytes: %d", inst_section_size); + + LOAD_INIT(); + + libxmp_set_type(m, "AMOS Music Bank"); + + if (read_abk_song(f, &song, AMOS_MAIN_HEADER + main_header.songs_offset) < 0) + { + return -1; + } + + libxmp_copy_adjust(mod->name, (uint8*) song.song_name, AMOS_STRING_LEN); + + MODULE_INFO(); + + hio_seek(f, AMOS_MAIN_HEADER + main_header.patterns_offset, SEEK_SET); + + mod->chn = AMOS_ABK_CHANNELS; + mod->pat = hio_read16b(f); + + /* Sanity check */ + if (mod->pat > 256) { + return -1; + } + + mod->trk = mod->chn * mod->pat; + + /* move to the start of the instruments section. */ + hio_seek(f, AMOS_MAIN_HEADER + main_header.instruments_offset, SEEK_SET); + mod->ins = hio_read16b(f); + + /* Sanity check */ + if (mod->ins > 255) { + return -1; + } + + mod->smp = mod->ins; + + /* Read and convert instruments and samples */ + + if (libxmp_init_instrument(m) < 0) + { + return -1; + } + + D_(D_INFO "Instruments: %d", mod->ins); + + /* read all the instruments in */ + ci = read_abk_insts(f, inst_section_size, mod->ins); + if (ci == NULL) { + return -1; + } + + /* store the location of the first sample so we can read them later. */ + first_sample_offset = AMOS_MAIN_HEADER + main_header.instruments_offset + ci[0].sample_offset; + + for (i = 0; i < mod->ins; i++) + { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + { + free(ci); + return -1; + } + + mod->xxs[i].len = ci[i].sample_length << 1; + + if (mod->xxs[i].len > 0) + { + mod->xxi[i].nsm = 1; + } + + /* the repeating stuff. */ + if (ci[i].repeat_offset > ci[i].sample_offset) + { + mod->xxs[i].lps = (ci[i].repeat_offset - ci[i].sample_offset) << 1; + } + else + { + mod->xxs[i].lps = 0; + } + mod->xxs[i].lpe = ci[i].repeat_end; + if (mod->xxs[i].lpe > 2) { + mod->xxs[i].lpe <<= 1; + mod->xxs[i].flg = XMP_SAMPLE_LOOP; + } +/*printf("%02x lps=%04x lpe=%04x\n", i, mod->xxs[i].lps, mod->xxs[i].lpe);*/ + + mod->xxi[i].sub[0].vol = ci[i].sample_volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + libxmp_instrument_name(mod, i, (uint8*)ci[i].sample_name, 16); + + D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c", i, + mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' '); + } + + free(ci); + + if (libxmp_init_pattern(mod) < 0) + { + return -1; + } + + /* figure out the playlist order. + * TODO: if the 4 channels arent in the same order then + * we need to fail here. */ + + read_abk_playlist(f, song.playlist_offset[0], &playlist); + + /* move to the start of the instruments section */ + /* then convert the patterns one at a time. there is a pattern for each channel.*/ + hio_seek(f, AMOS_MAIN_HEADER + main_header.patterns_offset + 2, SEEK_SET); + + mod->len = 0; + + i = 0; + for (j = 0; j < mod->pat; j++) + { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + { + free(playlist.pattern); + return -1; + } + + for (k = 0; k < mod->chn; k++) + { + pattern = hio_read16b(f); + if (read_abk_pattern(f, mod->xxt[(i*mod->chn)+k]->event, AMOS_MAIN_HEADER + main_header.patterns_offset + pattern) < 0) { + free(playlist.pattern); + return -1; + } + } + + i++; + } + + /* Sanity check */ + if (playlist.length > 256) { + free(playlist.pattern); + return -1; + } + + mod->len = playlist.length; + + /* now push all the patterns into the module and set the length */ + for (i = 0; i < playlist.length; i++) + { + mod->xxo[i] = playlist.pattern[i]; + } + + /* free up some memory here */ + free(playlist.pattern); + + D_(D_INFO "Stored patterns: %d", mod->pat); + D_(D_INFO "Stored tracks: %d", mod->trk); + + /* Read samples */ + hio_seek(f, first_sample_offset, SEEK_SET); + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) + { + if (mod->xxs[i].len <= 2) + continue; + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + { + return -1; + } + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/amf_load.c b/thirdparty/libxmp/src/loaders/amf_load.c new file mode 100644 index 000000000..db1ad7b4c --- /dev/null +++ b/thirdparty/libxmp/src/loaders/amf_load.c @@ -0,0 +1,592 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* AMF loader written based on the format specs by Miodrag Vallat with + * fixes by Andre Timmermans. + * + * The AMF format is the internal format used by DSMI, the DOS Sound and Music + * Interface, which is the engine of DMP. As DMP was able to play more and more + * module formats, the format evolved to support more features. There were 5 + * official formats, numbered from 10 (AMF 1.0) to 14 (AMF 1.4). + */ + +#include "loader.h" +#include "../period.h" + + +static int amf_test(HIO_HANDLE *, char *, const int); +static int amf_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_amf = { + "DSMI Advanced Module Format", + amf_test, + amf_load +}; + +static int amf_test(HIO_HANDLE * f, char *t, const int start) +{ + char buf[4]; + int ver; + + if (hio_read(buf, 1, 3, f) < 3) + return -1; + + if (buf[0] != 'A' || buf[1] != 'M' || buf[2] != 'F') + return -1; + + ver = hio_read8(f); + if ((ver != 0x01 && ver < 0x08) || ver > 0x0e) + return -1; + + libxmp_read_title(f, t, 32); + + return 0; +} + + +static int amf_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + uint8 buf[1024]; + int *trkmap, newtrk; + int no_loopend = 0; + int ver; + + LOAD_INIT(); + + hio_read(buf, 1, 3, f); + ver = hio_read8(f); + + if (hio_read(buf, 1, 32, f) != 32) + return -1; + + memcpy(mod->name, buf, 32); + mod->name[32] = '\0'; + libxmp_set_type(m, "DSMI %d.%d AMF", ver / 10, ver % 10); + + mod->ins = hio_read8(f); + mod->len = hio_read8(f); + mod->trk = hio_read16l(f); + mod->chn = 4; + + if (ver >= 0x09) { + mod->chn = hio_read8(f); + } + + /* Sanity check */ + if (mod->ins == 0 || mod->len == 0 || mod->trk == 0 + || mod->chn == 0 || mod->chn > XMP_MAX_CHANNELS) { + return -1; + } + + mod->smp = mod->ins; + mod->pat = mod->len; + + if (ver == 0x09 || ver == 0x0a) + hio_read(buf, 1, 16, f); /* channel remap table */ + + if (ver >= 0x0d) { + if (hio_read(buf, 1, 32, f) != 32) /* panning table */ + return -1; + + for (i = 0; i < 32; i++) { + mod->xxc->pan = 0x80 + 2 * (int8)buf[i]; + } + mod->bpm = hio_read8(f); + mod->spd = hio_read8(f); + } else if (ver >= 0x0b) { + hio_read(buf, 1, 16, f); + } + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + + /* Orders */ + + /* + * Andre Timmermans says: + * + * Order table: track numbers in this table are not explained, + * but as you noticed you have to perform -1 to obtain the index + * in the track table. For value 0, found in some files, I think + * it means an empty track. + * + * 2021 note: this is misleading. Do not subtract 1 from the logical + * track values found in the order table; load the mapping table to + * index 1 instead. + */ + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = i; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *)); + if (mod->xxp == NULL) + return -1; + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + + mod->xxp[i]->rows = ver >= 0x0e ? hio_read16l(f) : 64; + + if (mod->xxp[i]->rows > 256) + return -1; + + for (j = 0; j < mod->chn; j++) { + uint16 t = hio_read16l(f); + mod->xxp[i]->index[j] = t; + } + } + + /* Instruments */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Probe for 2-byte loop start 1.0 format + * in facing_n.amf and sweetdrm.amf have only the sample + * loop start specified in 2 bytes + * + * These modules are an early variant of the AMF 1.0 format. Since + * normal AMF 1.0 files have 32-bit lengths/loop start/loop end, + * this is possibly caused by these fields having been expanded for + * the 1.0 format, but M2AMF 1.3 writing instrument structs with + * the old length (which would explain the missing 6 bytes). + */ + if (ver == 0x0a) { + uint8 b; + uint32 len, val; + long pos = hio_tell(f); + if (pos < 0) { + return -1; + } + for (i = 0; i < mod->ins; i++) { + b = hio_read8(f); + if (b != 0 && b != 1) { + no_loopend = 1; + break; + } + hio_seek(f, 32 + 13, SEEK_CUR); + if (hio_read32l(f) > (uint32)mod->ins) { /* check index */ + no_loopend = 1; + break; + } + len = hio_read32l(f); + if (len > 0x100000) { /* check len */ + no_loopend = 1; + break; + } + if (hio_read16l(f) == 0x0000) { /* check c2spd */ + no_loopend = 1; + break; + } + if (hio_read8(f) > 0x40) { /* check volume */ + no_loopend = 1; + break; + } + val = hio_read32l(f); /* check loop start */ + if (val > len) { + no_loopend = 1; + break; + } + val = hio_read32l(f); /* check loop end */ + if (val > len) { + no_loopend = 1; + break; + } + } + hio_seek(f, pos, SEEK_SET); + } + + if (no_loopend) { + D_(D_INFO "Detected AMF 1.0 truncated instruments."); + } + + for (i = 0; i < mod->ins; i++) { + int c2spd; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + hio_read8(f); + + hio_read(buf, 1, 32, f); + libxmp_instrument_name(mod, i, buf, 32); + + hio_read(buf, 1, 13, f); /* sample name */ + hio_read32l(f); /* sample index */ + + mod->xxi[i].nsm = 1; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].sub[0].pan = 0x80; + + if (ver >= 0x0a) { + mod->xxs[i].len = hio_read32l(f); + } else { + mod->xxs[i].len = hio_read16l(f); + } + c2spd = hio_read16l(f); + libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + mod->xxi[i].sub[0].vol = hio_read8(f); + + /* + * Andre Timmermans says: + * + * [Miodrag Vallat's] doc tells that in version 1.0 only + * sample loop start is present (2 bytes) but the files I + * have tells both start and end are present (2*4 bytes). + * Maybe it should be read as version < 1.0. + * + * CM: confirmed with Maelcum's "The tribal zone" + */ + + if (no_loopend != 0) { + mod->xxs[i].lps = hio_read16l(f); + mod->xxs[i].lpe = mod->xxs[i].len; + } else if (ver >= 0x0a) { + mod->xxs[i].lps = hio_read32l(f); + mod->xxs[i].lpe = hio_read32l(f); + } else { + /* Non-looping samples are stored with lpe=-1, not 0. */ + mod->xxs[i].lps = hio_read16l(f); + mod->xxs[i].lpe = hio_read16l(f); + + if (mod->xxs[i].lpe == 0xffff) + mod->xxs[i].lpe = 0; + } + + if (no_loopend != 0) { + mod->xxs[i].flg = mod->xxs[i].lps > 0 ? XMP_SAMPLE_LOOP : 0; + } else { + mod->xxs[i].flg = mod->xxs[i].lpe > mod->xxs[i].lps ? + XMP_SAMPLE_LOOP : 0; + } + + D_(D_INFO "[%2X] %-32.32s %05x %05x %05x %c V%02x %5d", + i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, mod->xxs[i].flg & XMP_SAMPLE_LOOP ? + 'L' : ' ', mod->xxi[i].sub[0].vol, c2spd); + } + + if (hio_error(f)) + return -1; + + + /* Tracks */ + + /* Index 0 is a blank track that isn't stored in the file. To keep + * things simple, load the mapping table to index 1 so the table + * index is the same as the logical track value. Older versions + * attempted to remap it to index 0 and subtract 1 from the index, + * breaking modules that directly reference the empty track in the + * order table (see "cosmos st.amf"). + */ + trkmap = (int *) calloc(mod->trk + 1, sizeof(int)); + if (trkmap == NULL) + return -1; + newtrk = 0; + + for (i = 1; i <= mod->trk; i++) { /* read track table */ + uint16 t; + t = hio_read16l(f); + trkmap[i] = t; + if (t > newtrk) newtrk = t; + } + + for (i = 0; i < mod->pat; i++) { /* read track table */ + for (j = 0; j < mod->chn; j++) { + uint16 k = mod->xxp[i]->index[j]; + + /* Use empty track if an invalid track is requested + * (such as in Lasse Makkonen "faster and louder") + */ + if (k > mod->trk) + k = 0; + mod->xxp[i]->index[j] = trkmap[k]; + } + } + + mod->trk = newtrk + 1; /* + empty track */ + free(trkmap); + + if (hio_error(f)) + return -1; + + D_(D_INFO "Stored tracks: %d", mod->trk - 1); + + mod->xxt = (struct xmp_track **) calloc(mod->trk, sizeof(struct xmp_track *)); + if (mod->xxt == NULL) + return -1; + + /* Alloc track 0 as empty track */ + if (libxmp_alloc_track(mod, 0, 64) < 0) + return -1; + + /* Alloc rest of the tracks */ + for (i = 1; i < mod->trk; i++) { + uint8 t1, t2, t3; + int size; + + if (libxmp_alloc_track(mod, i, 64) < 0) /* FIXME! */ + return -1; + + /* Previous versions loaded this as a 24-bit value, but it's + * just a word. The purpose of the third byte is unknown, and + * DSMI just ignores it. + */ + size = hio_read16l(f); + hio_read8(f); + + if (hio_error(f)) + return -1; + + /* Version 0.1 AMFs do not count the end-of-track marker in + * the event count, so add 1. This hasn't been verified yet. */ + if (ver == 0x01 && size != 0) + size++; + + for (j = 0; j < size; j++) { + t1 = hio_read8(f); /* row */ + t2 = hio_read8(f); /* type */ + t3 = hio_read8(f); /* parameter */ + + if (t1 == 0xff && t2 == 0xff && t3 == 0xff) + break; + + /* If an event is encountered past the end of the + * track, treat it the same as the track end. This is + * encountered in "Avoid.amf". + */ + if (t1 >= mod->xxt[i]->rows) { + if (hio_seek(f, (size - j - 1) * 3, SEEK_CUR)) + return -1; + + break; + } + + event = &mod->xxt[i]->event[t1]; + + if (t2 < 0x7f) { /* note */ + if (t2 > 0) + event->note = t2 + 1; + /* A volume value of 0xff indicates that + * the old volume should be reused. Prior + * libxmp versions also forgot to add 1 here. + */ + event->vol = (t3 != 0xff) ? (t3 + 1) : 0; + } else if (t2 == 0x7f) { /* note retrigger */ + + /* AMF.TXT claims that this duplicates the + * previous event, which is a lie. M2AMF emits + * this for MODs when an instrument change + * occurs with no note, indicating it should + * retrigger (like in PT 2.3). Ignore this. + * + * See: "aladdin - aladd.pc.amf", "eye.amf". + */ + } else if (t2 == 0x80) { /* instrument */ + event->ins = t3 + 1; + } else { /* effects */ + uint8 fxp, fxt; + + fxp = fxt = 0; + + switch (t2) { + case 0x81: + fxt = FX_S3M_SPEED; + fxp = t3; + break; + case 0x82: + if ((int8)t3 > 0) { + fxt = FX_VOLSLIDE; + fxp = t3 << 4; + } else { + fxt = FX_VOLSLIDE; + fxp = -(int8)t3 & 0x0f; + } + break; + case 0x83: + /* See volume notes above. Previous + * releases forgot to add 1 here. */ + event->vol = (t3 + 1); + break; + case 0x84: + /* AT: Not explained for 0x84, pitch + * slide, value 0x00 corresponds to + * S3M E00 and 0x80 stands for S3M F00 + * (I checked with M2AMF) + */ + if ((int8)t3 >= 0) { + fxt = FX_PORTA_DN; + fxp = t3; + } else if (t3 == 0x80) { + fxt = FX_PORTA_UP; + fxp = 0; + } else { + fxt = FX_PORTA_UP; + fxp = -(int8)t3; + } + break; + case 0x85: + /* porta abs -- unknown */ + break; + case 0x86: + fxt = FX_TONEPORTA; + fxp = t3; + break; + + /* AT: M2AMF maps both tremolo and tremor to + * 0x87. Since tremor is only found in certain + * formats, maybe it would be better to + * consider it is a tremolo. + */ + case 0x87: + fxt = FX_TREMOLO; + fxp = t3; + break; + case 0x88: + fxt = FX_ARPEGGIO; + fxp = t3; + break; + case 0x89: + fxt = FX_VIBRATO; + fxp = t3; + break; + case 0x8a: + if ((int8)t3 > 0) { + fxt = FX_TONE_VSLIDE; + fxp = t3 << 4; + } else { + fxt = FX_TONE_VSLIDE; + fxp = -(int8)t3 & 0x0f; + } + break; + case 0x8b: + if ((int8)t3 > 0) { + fxt = FX_VIBRA_VSLIDE; + fxp = t3 << 4; + } else { + fxt = FX_VIBRA_VSLIDE; + fxp = -(int8)t3 & 0x0f; + } + break; + case 0x8c: + fxt = FX_BREAK; + fxp = t3; + break; + case 0x8d: + fxt = FX_JUMP; + fxp = t3; + break; + case 0x8e: + /* sync -- unknown */ + break; + case 0x8f: + fxt = FX_EXTENDED; + fxp = (EX_RETRIG << 4) | (t3 & 0x0f); + break; + case 0x90: + fxt = FX_OFFSET; + fxp = t3; + break; + case 0x91: + if ((int8)t3 > 0) { + fxt = FX_EXTENDED; + fxp = (EX_F_VSLIDE_UP << 4) | + (t3 & 0x0f); + } else { + fxt = FX_EXTENDED; + fxp = (EX_F_VSLIDE_DN << 4) | + (t3 & 0x0f); + } + break; + case 0x92: + if ((int8)t3 > 0) { + fxt = FX_PORTA_DN; + fxp = 0xf0 | (fxp & 0x0f); + } else { + fxt = FX_PORTA_UP; + fxp = 0xf0 | (fxp & 0x0f); + } + break; + case 0x93: + fxt = FX_EXTENDED; + fxp = (EX_DELAY << 4) | (t3 & 0x0f); + break; + case 0x94: + fxt = FX_EXTENDED; + fxp = (EX_CUT << 4) | (t3 & 0x0f); + break; + case 0x95: + fxt = FX_SPEED; + if (t3 < 0x21) + t3 = 0x21; + fxp = t3; + break; + case 0x96: + if ((int8)t3 > 0) { + fxt = FX_PORTA_DN; + fxp = 0xe0 | (fxp & 0x0f); + } else { + fxt = FX_PORTA_UP; + fxp = 0xe0 | (fxp & 0x0f); + } + break; + case 0x97: + /* Same as S3M pan, but param is offset by -0x40. */ + if (t3 == 0x64) { /* 0xA4 - 0x40 */ + fxt = FX_SURROUND; + fxp = 1; + } else if (t3 >= 0xC0 || t3 <= 0x40) { + int pan = ((int8)t3 << 1) + 0x80; + fxt = FX_SETPAN; + fxp = MIN(0xff, pan); + } + break; + } + + event->fxt = fxt; + event->fxp = fxp; + } + } + } + + + /* Samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->quirk |= QUIRK_FINEFX; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/arch_load.c b/thirdparty/libxmp/src/loaders/arch_load.c new file mode 100644 index 000000000..07b9a7dd7 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/arch_load.c @@ -0,0 +1,510 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "iff.h" + +#define MAGIC_MUSX MAGIC4('M','U','S','X') +#define MAGIC_MNAM MAGIC4('M','N','A','M') +#define MAGIC_SNAM MAGIC4('S','N','A','M') +#define MAGIC_SVOL MAGIC4('S','V','O','L') +#define MAGIC_SLEN MAGIC4('S','L','E','N') +#define MAGIC_ROFS MAGIC4('R','O','F','S') +#define MAGIC_RLEN MAGIC4('R','L','E','N') +#define MAGIC_SDAT MAGIC4('S','D','A','T') + + +static int arch_test (HIO_HANDLE *, char *, const int); +static int arch_load (struct module_data *, HIO_HANDLE *, const int); + + +const struct format_loader libxmp_loader_arch = { + "Archimedes Tracker", + arch_test, + arch_load +}; + +/* + * Linear (0 to 0x40) to logarithmic volume conversion. + * This is only used for the Protracker-compatible "linear volume" effect in + * Andy Southgate's StasisMod. In this implementation linear and logarithmic + * volumes can be freely intermixed. + */ +static const uint8 lin_table[65]={ + 0x00, 0x48, 0x64, 0x74, 0x82, 0x8a, 0x92, 0x9a, + 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xea, 0xbe, + 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, + 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, 0xe0, + 0xe2, 0xe2, 0xe4, 0xe4, 0xe6, 0xe6, 0xe8, 0xe8, + 0xea, 0xea, 0xec, 0xec, 0xee, 0xee, 0xf0, 0xf0, + 0xf2, 0xf2, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, + 0xfa, 0xfa, 0xfc, 0xfc, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe +}; + +#if 0 +static uint8 convert_vol(uint8 vol) { +/* return pow(2,6.0-(255.0-vol)/32)+.5; */ + return vol_table[vol]; +} +#endif + +static int arch_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_MUSX) { + return -1; + } + + hio_read32l(f); + + while (!hio_eof(f)) { + uint32 id = hio_read32b(f); + uint32 len = hio_read32l(f); + + /* Sanity check */ + if (len > 0x100000) { + return -1; + } + + if (id == MAGIC_MNAM) { + libxmp_read_title(f, t, 32); + return 0; + } + + hio_seek(f, len, SEEK_CUR); + } + + libxmp_read_title(f, t, 0); + + return 0; +} + + +struct local_data { + int year, month, day; + int pflag, sflag, max_ins, max_pat; + int has_mvox; + int has_pnum; + uint8 ster[8], rows[64]; +}; + +static void fix_effect(struct xmp_event *e) +{ +#if 0 + /* for debugging */ + printf ("%c%02x ", e->fxt["0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"], e->fxp); +#endif + switch (e->fxt) { + case 0x00: /* 00 xy Normal play or Arpeggio */ + e->fxt = FX_ARPEGGIO; + /* x: first halfnote to add + y: second halftone to subtract */ + break; + case 0x01: /* 01 xx Slide Up */ + e->fxt = FX_PORTA_UP; + break; + case 0x02: /* 02 xx Slide Down */ + e->fxt = FX_PORTA_DN; + break; + case 0x03: /* 03 xx Tone Portamento */ + e->fxt = FX_TONEPORTA; + break; + case 0x0b: /* 0B xx Break Pattern */ + e->fxt = FX_BREAK; + break; + case 0x0c: + /* Set linear volume */ + if (e->fxp <= 64) { + e->fxt = FX_VOLSET; + e->fxp = lin_table[e->fxp]; + } else { + e->fxp = e->fxt = 0; + } + break; + case 0x0e: /* 0E xy Set Stereo */ + case 0x19: /* StasisMod's non-standard set panning effect */ + /* y: stereo position (1-7,ignored). 1=left 4=center 7=right */ + if (e->fxp>0 && e->fxp<8) { + e->fxt = FX_SETPAN; + e->fxp = 42*e->fxp-40; + } else + e->fxt = e->fxp = 0; + break; + case 0x10: /* 10 xx Volume Slide Up */ + e->fxt = FX_VOLSLIDE_UP; + break; + case 0x11: /* 11 xx Volume Slide Down */ + e->fxt = FX_VOLSLIDE_DN; + break; + case 0x13: /* 13 xx Position Jump */ + e->fxt = FX_JUMP; + break; + case 0x15: /* 15 xy Line Jump. (not in manual) */ + /* Jump to line 10*x+y in same pattern. (10*x+y>63 ignored) */ + if (MSN(e->fxp) * 10 + LSN(e->fxp) < 64) { + e->fxt = FX_LINE_JUMP; + e->fxp = MSN(e->fxp) * 10 + LSN(e->fxp); + } else { + e->fxt = e->fxp = 0; + } + break; + case 0x1c: /* 1C xy Set Speed */ + e->fxt = FX_SPEED; + break; + case 0x1f: /* 1F xx Set Volume */ + e->fxt = FX_VOLSET; + /* all volumes are logarithmic */ + /* e->fxp = convert_vol (e->fxp); */ + break; + default: + e->fxt = e->fxp = 0; + } +} + +static int get_tinf(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + int x; + + x = hio_read8(f); + data->year = ((x & 0xf0) >> 4) * 10 + (x & 0x0f); + x = hio_read8(f); + data->year += ((x & 0xf0) >> 4) * 1000 + (x & 0x0f) * 100; + + x = hio_read8(f); + data->month = ((x & 0xf0) >> 4) * 10 + (x & 0x0f); + + x = hio_read8(f); + data->day = ((x & 0xf0) >> 4) * 10 + (x & 0x0f); + + return 0; +} + +static int get_mvox(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + uint32 chn; + + chn = hio_read32l(f); + + /* Sanity check */ + if (chn < 1 || chn > 8 || data->has_mvox) { + return -1; + } + + mod->chn = chn; + data->has_mvox = 1; + return 0; +} + +static int get_ster(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + + if (hio_read(data->ster, 1, 8, f) != 8) { + return -1; + } + + for (i = 0; i < mod->chn; i++) { + if (data->ster[i] > 0 && data->ster[i] < 8) { + mod->xxc[i].pan = 42 * data->ster[i] - 40; + } + } + + return 0; +} + +static int get_mnam(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + if (hio_read(mod->name, 1, 32, f) != 32) + return -1; + + return 0; +} + +static int get_anam(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + /*hio_read(m->author, 1, 32, f); */ + + return 0; +} + +static int get_mlen(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + uint32 len; + + len = hio_read32l(f); + + /* Sanity check */ + if (len > 0xff) + return -1; + + mod->len = len; + return 0; +} + +static int get_pnum(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + uint32 pat; + + pat = hio_read32l(f); + + /* Sanity check */ + if (pat < 1 || pat > 64 || data->has_pnum) + return -1; + + mod->pat = pat; + data->has_pnum = 1; + return 0; +} + +static int get_plen(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + + if (hio_read(data->rows, 1, 64, f) != 64) + return -1; + + return 0; +} + +static int get_sequ(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + hio_read(mod->xxo, 1, 128, f); + libxmp_set_type(m, "Archimedes Tracker"); + MODULE_INFO(); + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j, k; + struct xmp_event *event; + + /* Sanity check */ + if (!data->has_mvox || !data->has_pnum) { + return -1; + } + + if (!data->pflag) { + D_(D_INFO "Stored patterns: %d", mod->pat); + data->pflag = 1; + data->max_pat = 0; + mod->trk = mod->pat * mod->chn; + + if (libxmp_init_pattern(mod) < 0) + return -1; + } + + /* Sanity check */ + if (data->max_pat >= mod->pat || data->max_pat >= 64) + return -1; + + i = data->max_pat; + + if (libxmp_alloc_pattern_tracks(mod, i, data->rows[i]) < 0) + return -1; + + for (j = 0; j < data->rows[i]; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + + event->fxp = hio_read8(f); + event->fxt = hio_read8(f); + event->ins = hio_read8(f); + event->note = hio_read8(f); + + if (event->note) + event->note += 48; + + fix_effect(event); + } + } + + data->max_pat++; + + return 0; +} + +static int get_samp(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + + if (!data->sflag) { + mod->smp = mod->ins = 36; + if (libxmp_init_instrument(m) < 0) + return -1; + + D_(D_INFO "Instruments: %d", mod->ins); + + data->sflag = 1; + data->max_ins = 0; + } + + /* FIXME: More than 36 sample slots used. Unfortunately we + * have no way to handle this without two passes, and it's + * officially supposed to be 36, so ignore the rest. + */ + if (data->max_ins >= 36) + return 0; + + i = data->max_ins; + + mod->xxi[i].nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + if (hio_read32b(f) != MAGIC_SNAM) /* SNAM */ + return -1; + + { + /* should usually be 0x14 but zero is not unknown */ + int name_len = hio_read32l(f); + + /* Sanity check */ + if (name_len < 0 || name_len > 32) + return -1; + + hio_read(mod->xxi[i].name, 1, name_len, f); + } + + if (hio_read32b(f) != MAGIC_SVOL) /* SVOL */ + return -1; + hio_read32l(f); + /* mod->xxi[i].sub[0].vol = convert_vol(hio_read32l(f)); */ + mod->xxi[i].sub[0].vol = hio_read32l(f) & 0xff; + + if (hio_read32b(f) != MAGIC_SLEN) /* SLEN */ + return -1; + hio_read32l(f); + mod->xxs[i].len = hio_read32l(f); + + if (hio_read32b(f) != MAGIC_ROFS) /* ROFS */ + return -1; + hio_read32l(f); + mod->xxs[i].lps = hio_read32l(f); + + if (hio_read32b(f) != MAGIC_RLEN) /* RLEN */ + return -1; + hio_read32l(f); + mod->xxs[i].lpe = hio_read32l(f); + + if (hio_read32b(f) != MAGIC_SDAT) /* SDAT */ + return -1; + hio_read32l(f); + hio_read32l(f); /* 0x00000000 */ + + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].sub[0].pan = 0x80; + + m->vol_table = libxmp_arch_vol_table; + m->volbase = 0xff; + + if (mod->xxs[i].lpe > 2) { + mod->xxs[i].flg = XMP_SAMPLE_LOOP; + mod->xxs[i].lpe = mod->xxs[i].lps + mod->xxs[i].lpe; + } else if (mod->xxs[i].lpe == 2 && mod->xxs[i].lps > 0) { + /* non-zero repeat offset and repeat length of 2 + * means loop to end of sample */ + mod->xxs[i].flg = XMP_SAMPLE_LOOP; + mod->xxs[i].lpe = mod->xxs[i].len; + } + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_VIDC, &mod->xxs[i], NULL) < 0) + return -1; + + D_(D_INFO "[%2X] %-20.20s %05x %05x %05x %c V%02x", + i, mod->xxi[i].name, + mod->xxs[i].len, + mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol); + + data->max_ins++; + + return 0; +} + +static int arch_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + int i; + struct local_data data; + + LOAD_INIT(); + + hio_read32b(f); /* MUSX */ + hio_read32b(f); + + memset(&data, 0, sizeof(struct local_data)); + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFF chunk IDs */ + libxmp_iff_register(handle, "TINF", get_tinf); + libxmp_iff_register(handle, "MVOX", get_mvox); + libxmp_iff_register(handle, "STER", get_ster); + libxmp_iff_register(handle, "MNAM", get_mnam); + libxmp_iff_register(handle, "ANAM", get_anam); + libxmp_iff_register(handle, "MLEN", get_mlen); + libxmp_iff_register(handle, "PNUM", get_pnum); + libxmp_iff_register(handle, "PLEN", get_plen); + libxmp_iff_register(handle, "SEQU", get_sequ); + libxmp_iff_register(handle, "PATT", get_patt); + libxmp_iff_register(handle, "SAMP", get_samp); + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = DEFPAN((((i + 3) / 2) % 2) * 0xff); + } + + return 0; +} + diff --git a/thirdparty/libxmp/src/loaders/asylum_load.c b/thirdparty/libxmp/src/loaders/asylum_load.c new file mode 100644 index 000000000..151295799 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/asylum_load.c @@ -0,0 +1,185 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Based on AMF->MOD converter written by Mr. P / Powersource, 1995 + */ + +#include "loader.h" +#include "../period.h" + +static int asylum_test(HIO_HANDLE *, char *, const int); +static int asylum_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_asylum = { + "Asylum Music Format v1.0", + asylum_test, + asylum_load +}; + +static int asylum_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[32]; + + if (hio_read(buf, 1, 32, f) < 32) + return -1; + + if (memcmp(buf, "ASYLUM Music Format V1.0\0\0\0\0\0\0\0\0", 32)) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +static int asylum_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + uint8 buf[2048]; + int i, j; + + LOAD_INIT(); + + hio_seek(f, 32, SEEK_CUR); /* skip magic */ + mod->spd = hio_read8(f); /* initial speed */ + mod->bpm = hio_read8(f); /* initial BPM */ + mod->ins = hio_read8(f); /* number of instruments */ + mod->pat = hio_read8(f); /* number of patterns */ + mod->len = hio_read8(f); /* module length */ + mod->rst = hio_read8(f); /* restart byte */ + + /* Sanity check - this format only stores 64 sample structures. */ + if (mod->ins > 64) { + D_(D_CRIT "invalid sample count %d", mod->ins); + return -1; + } + + hio_read(mod->xxo, 1, mod->len, f); /* read orders */ + hio_seek(f, start + 294, SEEK_SET); + + mod->chn = 8; + mod->smp = mod->ins; + mod->trk = mod->pat * mod->chn; + + snprintf(mod->type, XMP_NAME_SIZE, "Asylum Music Format v1.0"); + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments and samples */ + for (i = 0; i < mod->ins; i++) { + uint8 insbuf[37]; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) { + return -1; + } + + if (hio_read(insbuf, 1, 37, f) != 37) { + return -1; + } + + libxmp_instrument_name(mod, i, insbuf, 22); + mod->xxi[i].sub[0].fin = (int8)(insbuf[22] << 4); + mod->xxi[i].sub[0].vol = insbuf[23]; + mod->xxi[i].sub[0].xpo = (int8)insbuf[24]; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + mod->xxs[i].len = readmem32l(insbuf + 25); + mod->xxs[i].lps = readmem32l(insbuf + 29); + mod->xxs[i].lpe = mod->xxs[i].lps + readmem32l(insbuf + 33); + + /* Sanity check - ASYLUM modules are converted from MODs. */ + if ((uint32)mod->xxs[i].len >= 0x20000) { + D_(D_CRIT "invalid sample %d length %d", i, mod->xxs[i].len); + return -1; + } + + mod->xxs[i].flg = mod->xxs[i].lpe > 2 ? XMP_SAMPLE_LOOP : 0; + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %d", i, + mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].fin); + } + + hio_seek(f, 37 * (64 - mod->ins), SEEK_CUR); + + D_(D_INFO "Module length: %d", mod->len); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + uint8 *pos; + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + if (hio_read(buf, 1, 2048, f) < 2048) { + D_(D_CRIT "read error at pattern %d", i); + return -1; + } + + pos = buf; + for (j = 0; j < 64 * 8; j++) { + uint8 note; + + event = &EVENT(i, j % 8, j / 8); + memset(event, 0, sizeof(struct xmp_event)); + note = *pos++; + + if (note != 0) { + event->note = note + 13; + } + + event->ins = *pos++; + event->fxt = *pos++; + event->fxp = *pos++; + + /* TODO: m07.amf and m12.amf from Crusader: No Remorse + * use 0x1b for what looks *plausibly* like retrigger. + * No other ASYLUM modules use effects over 16. */ + if (event->fxt >= 0x10 && event->fxt != FX_MULTI_RETRIG) + event->fxt = event->fxp = 0; + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len > 1) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + mod->xxi[i].nsm = 1; + } + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/chip_load.c b/thirdparty/libxmp/src/loaders/chip_load.c new file mode 100644 index 000000000..bda7c470e --- /dev/null +++ b/thirdparty/libxmp/src/loaders/chip_load.c @@ -0,0 +1,203 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "mod.h" +#include "../period.h" + +static int chip_test(HIO_HANDLE *, char *, const int); +static int chip_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_chip = { + "Chiptracker", + chip_test, + chip_load +}; + +static int chip_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[4]; + + hio_seek(f, start + 952, SEEK_SET); + if (hio_read(buf, 1, 4, f) < 4) + return -1; + + /* Also RASP? */ + if (memcmp(buf, "KRIS", 4) != 0) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + +static int chip_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct mod_header mh; + uint8 *tidx; + int i, j, tnum; + + LOAD_INIT(); + + tidx = (uint8 *) calloc(1, 1024); + if (tidx == NULL) { + goto err; + } + + hio_read(mh.name, 20, 1, f); + hio_read16b(f); + + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + + hio_read(mh.magic, 4, 1, f); + mh.len = hio_read8(f); + + /* Sanity check */ + if (mh.len > 128) { + goto err2; + } + + mh.restart = hio_read8(f); + hio_read(tidx, 1024, 1, f); + hio_read16b(f); + + mod->chn = 4; + mod->ins = 31; + mod->smp = mod->ins; + mod->len = mh.len; + mod->pat = mh.len; + mod->rst = mh.restart; + + tnum = 0; + for (i = 0; i < mod->len; i++) { + mod->xxo[i] = i; + + for (j = 0; j < 4; j++) { + int t = tidx[2 * (4 * i + j)]; + if (t > tnum) + tnum = t; + } + } + + mod->trk = tnum + 1; + + strncpy(mod->name, (char *)mh.name, 20); + libxmp_set_type(m, "Chiptracker"); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + goto err2; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + goto err2; + + sub = &xxi->sub[0]; + + xxs->len = 2 * mh.ins[i].size; + xxs->lps = mh.ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size; + xxs->flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + sub->fin = (int8) (mh.ins[i].finetune << 4); + sub->vol = mh.ins[i].volume; + sub->pan = 0x80; + sub->sid = i; + + if (xxs->len > 0) + xxi->nsm = 1; + + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + } + + if (libxmp_init_pattern(mod) < 0) + goto err2; + + for (i = 0; i < mod->len; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + goto err2; + mod->xxp[i]->rows = 64; + + for (j = 0; j < 4; j++) { + int t = tidx[2 * (4 * i + j)]; + mod->xxp[i]->index[j] = t; + } + } + + /* Load and convert tracks */ + D_(D_INFO "Stored tracks: %d", mod->trk); + + for (i = 0; i < mod->trk; i++) { + if (libxmp_alloc_track(mod, i, 64) < 0) + goto err2; + + for (j = 0; j < 64; j++) { + struct xmp_event *event = &mod->xxt[i]->event[j]; + uint8 e[4]; + + if (hio_read(e, 1, 4, f) < 4) { + D_(D_CRIT "read error in track %d", i); + goto err2; + } + if (e[0] && e[0] != 0xa8) + event->note = 13 + e[0] / 2; + event->ins = e[1]; + event->fxt = e[2] & 0x0f; + event->fxp = e[3]; + } + } + + m->period_type = PERIOD_MODRNG; + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (mod->xxs[i].len == 0) + continue; + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_FULLREP, &mod->xxs[i], NULL) < 0) + goto err2; + } + + free(tidx); + + return 0; + + err2: + free(tidx); + err: + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/coco_load.c b/thirdparty/libxmp/src/loaders/coco_load.c new file mode 100644 index 000000000..caca6f29e --- /dev/null +++ b/thirdparty/libxmp/src/loaders/coco_load.c @@ -0,0 +1,318 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" + +static int coco_test (HIO_HANDLE *, char *, const int); +static int coco_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_coco = { + "Coconizer", + coco_test, + coco_load +}; + +static int check_cr(uint8 *s, int n) +{ + while (n--) { + if (*s++ == 0x0d) + return 0; + } + + return -1; +} + +static int coco_test(HIO_HANDLE *f, char *t, const int start) +{ + uint8 x, buf[20]; + uint32 y; + int n, i; + + x = hio_read8(f); + + /* check number of channels */ + if (x != 0x84 && x != 0x88) + return -1; + + if (hio_read(buf, 1, 20, f) != 20) /* read title */ + return -1; + if (check_cr(buf, 20) != 0) + return -1; + + n = hio_read8(f); /* instruments */ + if (n <= 0 || n > 100) + return -1; + + hio_read8(f); /* sequences */ + hio_read8(f); /* patterns */ + + y = hio_read32l(f); + if (y < 64 || y > 0x00100000) /* offset of sequence table */ + return -1; + + y = hio_read32l(f); /* offset of patterns */ + if (y < 64 || y > 0x00100000) + return -1; + + for (i = 0; i < n; i++) { + int ofs = hio_read32l(f); + int len = hio_read32l(f); + int vol = hio_read32l(f); + int lps = hio_read32l(f); + int lsz = hio_read32l(f); + + if (ofs < 64 || ofs > 0x00100000) + return -1; + + if (vol < 0 || vol > 0xff) + return -1; + + if (len < 0 || lps < 0 || lsz < 0) + return -1; + if (len > 0x00100000 || lps > 0x00100000 || lsz > 0x00100000) + return -1; + + if (lps > 0 && lps + lsz - 1 > len) + return -1; + + hio_read(buf, 1, 11, f); + hio_read8(f); /* unused */ + } + + hio_seek(f, start + 1, SEEK_SET); + libxmp_read_title(f, t, 20); + +#if 0 + for (i = 0; i < 20; i++) { + if (t[i] == 0x0d) + t[i] = 0; + } +#endif + + return 0; +} + + +static void fix_effect(struct xmp_event *e) +{ + switch (e->fxt) { + case 0x00: /* 00 xy Normal play or Arpeggio */ + e->fxt = FX_ARPEGGIO; + /* x: first halfnote to add + y: second halftone to subtract */ + break; + case 0x01: /* 01 xx Slide Pitch Up (until Amis Max), Frequency+InfoByte*64*/ + case 0x05: /* 05 xx Slide Pitch Up (no limit), Frequency+InfoByte*16 */ + e->fxt = FX_PORTA_UP; + break; + case 0x02: /* 02 xx Slide Pitch Down (until Amis Min), Frequency-InfoByte*64*/ + case 0x06: /* 06 xx Slide Pitch Down (0 limit), Frequency-InfoByte*16 */ + e->fxt = FX_PORTA_DN; + break; + case 0x03: /* 03 xx Fine Volume Up */ + e->fxt = FX_F_VSLIDE_UP; + break; + case 0x04: /* 04 xx Fine Volume Down */ + e->fxt = FX_F_VSLIDE_DN; + break; + case 0x07: /* 07 xy Set Stereo Position */ + /* y: stereo position (1-7,ignored). 1=left 4=center 7=right */ + if (e->fxp>0 && e->fxp<8) { + e->fxt = FX_SETPAN; + e->fxp = 42*e->fxp-40; + } else + e->fxt = e->fxp = 0; + break; + case 0x08: /* 08 xx Start Auto Fine Volume Up */ + case 0x09: /* 09 xx Start Auto Fine Volume Down */ + case 0x0a: /* 0A xx Start Auto Pitch Up */ + case 0x0b: /* 0B xx Start Auto Pitch Down */ + e->fxt = e->fxp = 0; /* FIXME */ + break; + case 0x0c: /* 0C xx Set Volume */ + e->fxt = FX_VOLSET; + e->fxp = 0xff - e->fxp; + break; + case 0x0d: /* 0D xy Pattern Break */ + e->fxt = FX_BREAK; + break; + case 0x0e: /* 0E xx Position Jump */ + e->fxt = FX_JUMP; + break; + case 0x0f: /* 0F xx Set Speed */ + e->fxt = FX_SPEED; + break; + case 0x10: /* 10 xx Unused */ + e->fxt = e->fxp = 0; + break; + case 0x11: /* 11 xx Fine Slide Pitch Up */ + case 0x12: /* 12 xx Fine Slide Pitch Down */ + e->fxt = e->fxp = 0; /* FIXME */ + break; + case 0x13: /* 13 xx Volume Up */ + e->fxt = FX_VOLSLIDE_UP; + break; + case 0x14: /* 14 xx Volume Down */ + e->fxt = FX_VOLSLIDE_DN; + break; + default: + e->fxt = e->fxp = 0; + } +} + +static int coco_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + int i, j, k; + int seq_ptr, pat_ptr, smp_ptr[100]; + + LOAD_INIT(); + + mod->chn = hio_read8(f) & 0x3f; + libxmp_read_title(f, mod->name, 20); + + for (i = 0; i < 20; i++) { + if (mod->name[i] == 0x0d) + mod->name[i] = 0; + } + + libxmp_set_type(m, "Coconizer"); + + mod->ins = mod->smp = hio_read8(f); + mod->len = hio_read8(f); + mod->pat = hio_read8(f); + mod->trk = mod->pat * mod->chn; + + seq_ptr = hio_read32l(f); + pat_ptr = hio_read32l(f); + + if (hio_error(f)) { + return -1; + } + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + m->vol_table = libxmp_arch_vol_table; + m->volbase = 0xff; + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + smp_ptr[i] = hio_read32l(f); + mod->xxs[i].len = hio_read32l(f); + mod->xxi[i].sub[0].vol = 0xff - hio_read32l(f); + mod->xxi[i].sub[0].pan = 0x80; + mod->xxs[i].lps = hio_read32l(f); + mod->xxs[i].lpe = mod->xxs[i].lps + hio_read32l(f); + if (mod->xxs[i].lpe) + mod->xxs[i].lpe -= 1; + mod->xxs[i].flg = mod->xxs[i].lps > 0 ? XMP_SAMPLE_LOOP : 0; + hio_read(mod->xxi[i].name, 1, 11, f); + for (j = 0; j < 11; j++) { + if (mod->xxi[i].name[j] == 0x0d) + mod->xxi[i].name[j] = 0; + } + hio_read8(f); /* unused */ + mod->xxi[i].sub[0].sid = i; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + if (hio_error(f)) { + return -1; + } + + D_(D_INFO "[%2X] %-10.10s %05x %05x %05x %c V%02x", + i, mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol); + } + + /* Sequence */ + + hio_seek(f, start + seq_ptr, SEEK_SET); + for (i = 0; ; i++) { + uint8 x = hio_read8(f); + if (x == 0xff) + break; + if (i < mod->len) + mod->xxo[i] = x; + } + + /* Patterns */ + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + if (hio_seek(f, start + pat_ptr, SEEK_SET) < 0) + return -1; + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < 64; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + event->fxp = hio_read8(f); + event->fxt = hio_read8(f); + event->ins = hio_read8(f); + event->note = hio_read8(f); + if (event->note) + event->note += 12; + + if (hio_error(f)) { + return -1; + } + + fix_effect(event); + } + } + } + + /* Read samples */ + + D_(D_INFO "Stored samples : %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxi[i].nsm == 0) + continue; + + hio_seek(f, start + smp_ptr[i], SEEK_SET); + if (libxmp_load_sample(m, f, SAMPLE_FLAG_VIDC, &mod->xxs[i], NULL) < 0) + return -1; + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = DEFPAN((((i + 3) / 2) % 2) * 0xff); + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/common.c b/thirdparty/libxmp/src/loaders/common.c new file mode 100644 index 000000000..f84a9d952 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/common.c @@ -0,0 +1,594 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#if defined(HAVE_DIRENT) +#include +#include +#endif + +#include "../common.h" + +#include "xmp.h" +#include "../period.h" +#include "loader.h" + +int libxmp_init_instrument(struct module_data *m) +{ + struct xmp_module *mod = &m->mod; + + if (mod->ins > 0) { + mod->xxi = (struct xmp_instrument *) calloc(mod->ins, sizeof(struct xmp_instrument)); + if (mod->xxi == NULL) + return -1; + } + + if (mod->smp > 0) { + int i; + /* Sanity check */ + if (mod->smp > MAX_SAMPLES) { + D_(D_CRIT "sample count %d exceeds maximum (%d)", + mod->smp, MAX_SAMPLES); + return -1; + } + + mod->xxs = (struct xmp_sample *) calloc(mod->smp, sizeof(struct xmp_sample)); + if (mod->xxs == NULL) + return -1; + m->xtra = (struct extra_sample_data *) calloc(mod->smp, sizeof(struct extra_sample_data)); + if (m->xtra == NULL) + return -1; + + for (i = 0; i < mod->smp; i++) { + m->xtra[i].c5spd = m->c4rate; + } + } + + return 0; +} + +/* Sample number adjustment (originally by Vitamin/CAIG). + * Only use this AFTER a previous usage of libxmp_init_instrument, + * and don't use this to free samples that have already been loaded. */ +int libxmp_realloc_samples(struct module_data *m, int new_size) +{ + struct xmp_module *mod = &m->mod; + struct xmp_sample *xxs; + struct extra_sample_data *xtra; + + /* Sanity check */ + if (new_size < 0) + return -1; + + if (new_size == 0) { + /* Don't rely on implementation-defined realloc(x,0) behavior. */ + mod->smp = 0; + free(mod->xxs); + mod->xxs = NULL; + free(m->xtra); + m->xtra = NULL; + return 0; + } + + xxs = (struct xmp_sample *) realloc(mod->xxs, sizeof(struct xmp_sample) * new_size); + if (xxs == NULL) + return -1; + mod->xxs = xxs; + + xtra = (struct extra_sample_data *) realloc(m->xtra, sizeof(struct extra_sample_data) * new_size); + if (xtra == NULL) + return -1; + m->xtra = xtra; + + if (new_size > mod->smp) { + int clear_size = new_size - mod->smp; + int i; + + memset(xxs + mod->smp, 0, sizeof(struct xmp_sample) * clear_size); + memset(xtra + mod->smp, 0, sizeof(struct extra_sample_data) * clear_size); + + for (i = mod->smp; i < new_size; i++) { + m->xtra[i].c5spd = m->c4rate; + } + } + mod->smp = new_size; + return 0; +} + +int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num) +{ + if (num == 0) + return 0; + + mod->xxi[i].sub = (struct xmp_subinstrument *) calloc(num, sizeof(struct xmp_subinstrument)); + if (mod->xxi[i].sub == NULL) + return -1; + + return 0; +} + +int libxmp_init_pattern(struct xmp_module *mod) +{ + mod->xxt = (struct xmp_track **) calloc(mod->trk, sizeof(struct xmp_track *)); + if (mod->xxt == NULL) + return -1; + + mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *)); + if (mod->xxp == NULL) + return -1; + + return 0; +} + +int libxmp_alloc_pattern(struct xmp_module *mod, int num) +{ + /* Sanity check */ + if (num < 0 || num >= mod->pat || mod->xxp[num] != NULL) + return -1; + + mod->xxp[num] = (struct xmp_pattern *) calloc(1, sizeof(struct xmp_pattern) + + sizeof(int) * (mod->chn - 1)); + if (mod->xxp[num] == NULL) + return -1; + + return 0; +} + +int libxmp_alloc_track(struct xmp_module *mod, int num, int rows) +{ + /* Sanity check */ + if (num < 0 || num >= mod->trk || mod->xxt[num] != NULL || rows <= 0) + return -1; + + mod->xxt[num] = (struct xmp_track *) calloc(1, sizeof(struct xmp_track) + + sizeof(struct xmp_event) * (rows - 1)); + if (mod->xxt[num] == NULL) + return -1; + + mod->xxt[num]->rows = rows; + + return 0; +} + +int libxmp_alloc_tracks_in_pattern(struct xmp_module *mod, int num) +{ + int i; + + D_(D_INFO "Alloc %d tracks of %d rows", mod->chn, mod->xxp[num]->rows); + for (i = 0; i < mod->chn; i++) { + int t = num * mod->chn + i; + int rows = mod->xxp[num]->rows; + + if (libxmp_alloc_track(mod, t, rows) < 0) + return -1; + + mod->xxp[num]->index[i] = t; + } + + return 0; +} + +int libxmp_alloc_pattern_tracks(struct xmp_module *mod, int num, int rows) +{ + /* Sanity check */ + if (rows <= 0 || rows > 256) + return -1; + + if (libxmp_alloc_pattern(mod, num) < 0) + return -1; + + mod->xxp[num]->rows = rows; + + if (libxmp_alloc_tracks_in_pattern(mod, num) < 0) + return -1; + + return 0; +} + +#ifndef LIBXMP_CORE_PLAYER +/* Some formats explicitly allow more than 256 rows (e.g. OctaMED). This function + * allows those formats to work without disrupting the sanity check for other formats. + */ +int libxmp_alloc_pattern_tracks_long(struct xmp_module *mod, int num, int rows) +{ + /* Sanity check */ + if (rows <= 0 || rows > 32768) + return -1; + + if (libxmp_alloc_pattern(mod, num) < 0) + return -1; + + mod->xxp[num]->rows = rows; + + if (libxmp_alloc_tracks_in_pattern(mod, num) < 0) + return -1; + + return 0; +} +#endif + +char *libxmp_instrument_name(struct xmp_module *mod, int i, uint8 *r, int n) +{ + CLAMP(n, 0, 31); + + return libxmp_copy_adjust(mod->xxi[i].name, r, n); +} + +char *libxmp_copy_adjust(char *s, uint8 *r, int n) +{ + int i; + + memset(s, 0, n + 1); + strncpy(s, (char *)r, n); + + for (i = 0; s[i] && i < n; i++) { + if (!isprint((unsigned char)s[i]) || ((uint8)s[i] > 127)) + s[i] = '.'; + } + + while (*s && (s[strlen(s) - 1] == ' ')) + s[strlen(s) - 1] = 0; + + return s; +} + +void libxmp_read_title(HIO_HANDLE *f, char *t, int s) +{ + uint8 buf[XMP_NAME_SIZE]; + + if (t == NULL || s < 0) + return; + + if (s >= XMP_NAME_SIZE) + s = XMP_NAME_SIZE -1; + + memset(t, 0, s + 1); + + s = hio_read(buf, 1, s, f); + buf[s] = 0; + libxmp_copy_adjust(t, buf, s); +} + +#ifndef LIBXMP_CORE_PLAYER + +int libxmp_test_name(const uint8 *s, int n, int flags) +{ + int i; + + for (i = 0; i < n; i++) { + if (s[i] == '\0' && (flags & TEST_NAME_IGNORE_AFTER_0)) + break; + if (s[i] == '\r' && (flags & TEST_NAME_IGNORE_AFTER_CR)) + break; + if (s[i] > 0x7f) + return -1; + /* ACS_Team2.mod has a backspace in instrument name */ + /* Numerous ST modules from Music Channel BBS have char 14. */ + if (s[i] > 0 && s[i] < 32 && s[i] != 0x08 && s[i] != 0x0e) + return -1; + } + + return 0; +} + +int libxmp_copy_name_for_fopen(char *dest, const char *name, int n) +{ + int converted_colon = 0; + int i; + + /* libxmp_copy_adjust, but make sure the filename won't do anything + * malicious when given to fopen. This should only be used on song files. + */ + if (!strcmp(name, ".") || strstr(name, "..") || + name[0] == '\\' || name[0] == '/' || name[0] == ':') + return -1; + + for (i = 0; i < n - 1; i++) { + uint8 t = name[i]; + if (!t) + break; + + /* Reject non-ASCII symbols as they have poorly defined behavior. + */ + if (t < 32 || t >= 0x7f) + return -1; + + /* Reject anything resembling a Windows-style root path. Allow + * converting a single : to / so things like ST-01:samplename + * work. (Leave the : as-is on Amiga.) + */ + if (i > 0 && t == ':' && !converted_colon) { + uint8 t2 = name[i + 1]; + if (!t2 || t2 == '/' || t2 == '\\') + return -1; + + converted_colon = 1; +#ifndef LIBXMP_AMIGA + dest[i] = '/'; + continue; +#endif + } + + if (t == '\\') { + dest[i] = '/'; + continue; + } + + dest[i] = t; + } + dest[i] = '\0'; + return 0; +} + +/* + * Honor Noisetracker effects: + * + * 0 - arpeggio + * 1 - portamento up + * 2 - portamento down + * 3 - Tone-portamento + * 4 - Vibrato + * A - Slide volume + * B - Position jump + * C - Set volume + * D - Pattern break + * E - Set filter (keep the led off, please!) + * F - Set speed (now up to $1F) + * + * Pex Tufvesson's notes from http://www.livet.se/mahoney/: + * + * Note that some of the modules will have bugs in the playback with all + * known PC module players. This is due to that in many demos where I synced + * events in the demo with the music, I used commands that these newer PC + * module players erroneously interpret as "newer-version-trackers commands". + * Which they aren't. + */ +void libxmp_decode_noisetracker_event(struct xmp_event *event, const uint8 *mod_event) +{ + int fxt; + + memset(event, 0, sizeof (struct xmp_event)); + event->note = libxmp_period_to_note((LSN(mod_event[0]) << 8) + mod_event[1]); + event->ins = ((MSN(mod_event[0]) << 4) | MSN(mod_event[2])); + fxt = LSN(mod_event[2]); + + if (fxt <= 0x06 || (fxt >= 0x0a && fxt != 0x0e)) { + event->fxt = fxt; + event->fxp = mod_event[3]; + } + + libxmp_disable_continue_fx(event); +} +#endif + +void libxmp_decode_protracker_event(struct xmp_event *event, const uint8 *mod_event) +{ + int fxt = LSN(mod_event[2]); + + memset(event, 0, sizeof (struct xmp_event)); + event->note = libxmp_period_to_note((LSN(mod_event[0]) << 8) + mod_event[1]); + event->ins = ((MSN(mod_event[0]) << 4) | MSN(mod_event[2])); + + if (fxt != 0x08) { + event->fxt = fxt; + event->fxp = mod_event[3]; + } + + libxmp_disable_continue_fx(event); +} + +void libxmp_disable_continue_fx(struct xmp_event *event) +{ + if (event->fxp == 0) { + switch (event->fxt) { + case 0x05: + event->fxt = 0x03; + break; + case 0x06: + event->fxt = 0x04; + break; + case 0x01: + case 0x02: + case 0x0a: + event->fxt = 0x00; + } + } else if (event->fxt == 0x0e) { + if (event->fxp == 0xa0 || event->fxp == 0xb0) { + event->fxt = event->fxp = 0; + } + } +} + +#ifndef LIBXMP_CORE_PLAYER +/* libxmp_check_filename_case(): */ +/* Given a directory, see if file exists there, ignoring case */ + +#if defined(_WIN32) || defined(__DJGPP__) || \ + defined(__OS2__) || defined(__EMX__) || \ + defined(_DOS) || defined(LIBXMP_AMIGA) || \ + defined(__riscos__) || \ + /* case-insensitive file system: directly probe the file */\ + \ + !defined(HAVE_DIRENT) /* or, target does not have dirent. */ +int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size) +{ + char path[XMP_MAXPATH]; + snprintf(path, sizeof(path), "%s/%s", dir, name); + if (! (libxmp_get_filetype(path) & XMP_FILETYPE_FILE)) + return 0; + strncpy(new_name, name, size); + return 1; +} +#else /* target has dirent */ +int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size) +{ + int found = 0; + DIR *dirp; + struct dirent *d; + + dirp = opendir(dir); + if (dirp == NULL) + return 0; + + while ((d = readdir(dirp)) != NULL) { + if (!strcasecmp(d->d_name, name)) { + found = 1; + strncpy(new_name, d->d_name, size); + break; + } + } + + closedir(dirp); + + return found; +} +#endif + +void libxmp_get_instrument_path(struct module_data *m, char *path, int size) +{ + if (m->instrument_path) { + strncpy(path, m->instrument_path, size); + } else if (getenv("XMP_INSTRUMENT_PATH")) { + strncpy(path, getenv("XMP_INSTRUMENT_PATH"), size); + } else { + strncpy(path, ".", size); + } +} +#endif /* LIBXMP_CORE_PLAYER */ + +void libxmp_set_type(struct module_data *m, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + vsnprintf(m->mod.type, XMP_NAME_SIZE, fmt, ap); + va_end(ap); +} + +#ifndef LIBXMP_CORE_PLAYER +static int schism_tracker_date(int year, int month, int day) +{ + int mm = (month + 9) % 12; + int yy = year - mm / 10; + + yy = yy * 365 + (yy / 4) - (yy / 100) + (yy / 400); + mm = (mm * 306 + 5) / 10; + + return yy + mm + (day - 1); +} + +/* Generate a Schism Tracker version string. + * Schism Tracker versions are stored as follows: + * + * s_ver <= 0x50: 0.s_ver + * s_ver > 0x50, < 0xfff: days from epoch=(s_ver - 0x50) + * s_ver = 0xfff: days from epoch=l_ver + */ +void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver) +{ + if (s_ver >= 0x50) { + /* time_t epoch_sec = 1256947200; */ + int64 t = schism_tracker_date(2009, 10, 31); + int year, month, day, dayofyear; + + if (s_ver == 0xfff) { + t += l_ver; + } else + t += s_ver - 0x50; + + /* Date algorithm reimplemented from OpenMPT. + */ + year = (int)((t * 10000L + 14780) / 3652425); + dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400)); + if (dayofyear < 0) { + year--; + dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400)); + } + month = (100 * dayofyear + 52) / 3060; + day = dayofyear - (month * 306 + 5) / 10 + 1; + + year += (month + 2) / 12; + month = (month + 2) % 12 + 1; + + snprintf(buf, size, "Schism Tracker %04d-%02d-%02d", + year, month, day); + } else { + snprintf(buf, size, "Schism Tracker 0.%x", s_ver); + } +} + +/* Old MPT modules (from MPT <=1.16, older versions of OpenMPT) rely on a + * pre-amp routine that scales mix volume down. This is based on the module's + * channel count and a tracker pre-amp setting that isn't saved in the module. + * This setting defaults to 128. When fixed to 128, it can be optimized out. + * + * In OpenMPT, this pre-amp routine is only available in the MPT and OpenMPT + * 1.17 RC1 and RC2 mix modes. Changing a module to the compatible or 1.17 RC3 + * mix modes will permanently disable it for that module. OpenMPT applies the + * old mix modes to MPT <=1.16 modules, "IT 8.88", and in old OpenMPT-made + * modules that specify one of these mix modes in their extended properties. + * + * Set mod->chn and m->mvol first! + */ +void libxmp_apply_mpt_preamp(struct module_data *m) +{ + /* OpenMPT uses a slightly different table. */ + static const uint8 preamp_table[16] = + { + 0x60, 0x60, 0x60, 0x70, /* 0-7 */ + 0x80, 0x88, 0x90, 0x98, /* 8-15 */ + 0xA0, 0xA4, 0xA8, 0xB0, /* 16-23 */ + 0xB4, 0xB8, 0xBC, 0xC0, /* 24-31 */ + }; + + int chn = m->mod.chn; + CLAMP(chn, 1, 31); + + m->mvol = (m->mvol * 96) / preamp_table[chn >> 1]; + + /* Pre-amp is applied like this in the mixers of libmodplug/libopenmpt + * (still vastly simplified). + + int preamp = 128; + + if (preamp > 128) { + preamp = 128 + ((preamp - 128) * (chn + 4)) / 16; + } + preamp = preamp * m->mvol / 64; + preamp = (preamp << 7) / preamp_table[chn >> 1]; + + ... + + channel_volume_16bit = (channel_volume_16bit * preamp) >> 7; + */ +} +#endif + +char *libxmp_strdup(const char *src) +{ + size_t len = strlen(src) + 1; + char *buf = (char *) malloc(len); + if (buf) { + memcpy(buf, src, len); + } + return buf; +} diff --git a/thirdparty/libxmp/src/loaders/dbm_load.c b/thirdparty/libxmp/src/loaders/dbm_load.c new file mode 100644 index 000000000..fd965204d --- /dev/null +++ b/thirdparty/libxmp/src/loaders/dbm_load.c @@ -0,0 +1,582 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Based on DigiBooster_E.guide from the DigiBoosterPro 2.20 package. + * DigiBooster Pro written by Tomasz & Waldemar Piasta + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +#define MAGIC_DBM0 MAGIC4('D','B','M','0') + + +static int dbm_test(HIO_HANDLE *, char *, const int); +static int dbm_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_dbm = { + "DigiBooster Pro", + dbm_test, + dbm_load +}; + +static int dbm_test(HIO_HANDLE * f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_DBM0) + return -1; + + hio_seek(f, 12, SEEK_CUR); + libxmp_read_title(f, t, 44); + + return 0; +} + + +struct local_data { + int have_info; + int have_song; + int have_patt; + int have_smpl; + int have_inst; + int have_venv; + int have_penv; + int maj_version; + int min_version; +}; + +struct dbm_envelope { + int ins; + int flg; + int npt; + int sus; + int lps; + int lpe; + int sus2; + struct dbm_envelope_node { + uint16 position; + int16 value; + } nodes[32]; +}; + + +static void dbm_translate_effect(struct xmp_event *event, uint8 *fxt, uint8 *fxp) +{ + switch (*fxt) { + case 0x0e: + switch (MSN(*fxp)) { + case 0x3: /* Play from backward */ + /* TODO: this is supposed to play the sample in + * reverse only once, then forward. */ + if (event->note) { + *fxt = FX_REVERSE; + *fxp = 1; + } else { + *fxt = *fxp = 0; + } + break; + case 0x4: /* Turn off sound in channel */ + *fxt = FX_EXTENDED; + *fxp = (EX_CUT << 4); + break; + case 0x5: /* Turn on/off channel */ + /* In DigiBooster Pro, this is tied to + * the channel mute toggle in the UI. */ + *fxt = FX_TRK_VOL; + *fxp = *fxp ? 0x40 : 0x00; + break; + } + break; + + case 0x1c: /* Set Real BPM */ + *fxt = FX_S3M_BPM; + break; + + default: + if (*fxt > 0x1c) + *fxt = *fxp = 0; + } +} + +static int get_info(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int val; + + /* Sanity check */ + if (data->have_info || size < 10) { + return -1; + } + data->have_info = 1; + + val = hio_read16b(f); + if (val < 0 || val > 255) { + D_(D_CRIT "Invalid number of instruments: %d", val); + goto err; + } + mod->ins = val; + + val = hio_read16b(f); + if (val < 0) { + D_(D_CRIT "Invalid number of samples: %d", val); + goto err2; + } + mod->smp = val; + + hio_read16b(f); /* Songs */ + + val = hio_read16b(f); + if (val < 0 || val > 256) { + D_(D_CRIT "Invalid number of patterns: %d", val); + goto err3; + } + mod->pat = val; + + val = hio_read16b(f); + if (val < 0 || val > XMP_MAX_CHANNELS) { + D_(D_CRIT "Invalid number of channels: %d", val); + goto err4; + } + mod->chn = val; + + mod->trk = mod->pat * mod->chn; + + if (libxmp_init_instrument(m) < 0) + return -1; + + return 0; + + err4: + mod->pat = 0; + err3: + mod->smp = 0; + err2: + mod->ins = 0; + err: + return -1; +} + +static int get_song(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + char buffer[50]; + + /* Sanity check */ + if (data->have_song || size < 46) { + return 0; + } + data->have_song = 1; + + hio_read(buffer, 44, 1, f); + D_(D_INFO "Song name: %.44s", buffer); + + mod->len = hio_read16b(f); + D_(D_INFO "Song length: %d patterns", mod->len); + + /* Sanity check */ + if (mod->len > 256) { + return -1; + } + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = hio_read16b(f); + + return 0; +} + +static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + int c2spd, flags, snum; + uint8 buffer[50]; + + /* Sanity check */ + if (data->have_inst || size < 50 * mod->ins) { + return -1; + } + data->have_inst = 1; + + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + mod->xxi[i].nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + if (hio_read(buffer, 30, 1, f) == 0) + return -1; + + libxmp_instrument_name(mod, i, buffer, 30); + snum = hio_read16b(f); + if (snum == 0 || snum > mod->smp) { + /* Skip remaining data for this instrument. */ + hio_seek(f, 18, SEEK_CUR); + continue; + } + + mod->xxi[i].sub[0].sid = --snum; + mod->xxi[i].sub[0].vol = hio_read16b(f); + c2spd = hio_read32b(f); + mod->xxs[snum].lps = hio_read32b(f); + mod->xxs[snum].lpe = mod->xxs[snum].lps + hio_read32b(f); + mod->xxi[i].sub[0].pan = 0x80 + (int16)hio_read16b(f); + if (mod->xxi[i].sub[0].pan > 0xff) + mod->xxi[i].sub[0].pan = 0xff; + flags = hio_read16b(f); + mod->xxs[snum].flg = flags & 0x03 ? XMP_SAMPLE_LOOP : 0; + mod->xxs[snum].flg |= flags & 0x02 ? XMP_SAMPLE_LOOP_BIDIR : 0; + + libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + + D_(D_INFO "[%2X] %-30.30s #%02X V%02x P%02x %5d", + i, mod->xxi[i].name, snum, + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].pan, c2spd); + } + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, c, r, n, sz; + struct xmp_event *event, dummy; + uint8 x; + + /* Sanity check */ + if (data->have_patt || !data->have_info) { + return -1; + } + data->have_patt = 1; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d ", mod->pat); + + /* + * Note: channel and flag bytes are inverted in the format + * description document + */ + + for (i = 0; i < mod->pat; i++) { + int rows = hio_read16b(f); + if (hio_error(f)) + return -1; + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + sz = hio_read32b(f); + //printf("rows = %d, size = %d\n", mod->xxp[i]->rows, sz); + + r = 0; + /*c = -1;*/ + + while (sz > 0) { + //printf(" offset=%x, sz = %d, ", hio_tell(f), sz); + c = hio_read8(f); + if (hio_error(f)) + return -1; + + if (--sz <= 0) break; + //printf("c = %02x\n", c); + + if (c == 0) { + r++; + continue; + } + c--; + + n = hio_read8(f); + if (--sz <= 0) break; + //printf(" n = %d\n", n); + + if (c >= mod->chn || r >= mod->xxp[i]->rows) { + event = &dummy; + } else { + event = &EVENT(i, c, r); + } + + memset(event, 0, sizeof (struct xmp_event)); + + if (n & 0x01) { + x = hio_read8(f); + event->note = 13 + MSN(x) * 12 + LSN(x); + if (--sz <= 0) break; + } + if (n & 0x02) { + event->ins = hio_read8(f); + if (--sz <= 0) break; + } + if (n & 0x04) { + event->fxt = hio_read8(f); + if (--sz <= 0) break; + } + if (n & 0x08) { + event->fxp = hio_read8(f); + if (--sz <= 0) break; + } + if (n & 0x10) { + event->f2t = hio_read8(f); + if (--sz <= 0) break; + } + if (n & 0x20) { + event->f2p = hio_read8(f); + if (--sz <= 0) break; + } + + dbm_translate_effect(event, &event->fxt, &event->fxp); + dbm_translate_effect(event, &event->f2t, &event->f2p); + } + } + + return 0; +} + +static int get_smpl(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, flags; + + /* Sanity check */ + if (data->have_smpl || !data->have_info) { + return -1; + } + data->have_smpl = 1; + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + flags = hio_read32b(f); + mod->xxs[i].len = hio_read32b(f); + + if (flags & 0x02) { + mod->xxs[i].flg |= XMP_SAMPLE_16BIT; + } + + if (flags & 0x04) { /* Skip 32-bit samples */ + mod->xxs[i].len <<= 2; + hio_seek(f, mod->xxs[i].len, SEEK_CUR); + continue; + } + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_BIGEND, &mod->xxs[i], NULL) < 0) + return -1; + + if (mod->xxs[i].len == 0) + continue; + + D_(D_INFO "[%2X] %08x %05x%c%05x %05x %c", + i, flags, mod->xxs[i].len, + mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? + (mod->xxs[i].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : 'L') : ' '); + + } + + return 0; +} + +static int read_envelope(struct xmp_module *mod, struct dbm_envelope *env, HIO_HANDLE *f) +{ + int i; + + env->ins = (int)hio_read16b(f) - 1; + env->flg = hio_read8(f) & 0x7; + env->npt = (int)hio_read8(f) + 1; /* DBM counts sections, not points. */ + env->sus = hio_read8(f); + env->lps = hio_read8(f); + env->lpe = hio_read8(f); + env->sus2 = hio_read8(f); + + /* The format document claims there should be a reserved byte here but + * no DigiBooster Pro module actually has this. The revised document + * on the DigiBooster 3 website is corrected. + */ + + /* Sanity check */ + if (env->ins < 0 || env->ins >= mod->ins || env->npt > 32 || + env->sus >= 32 || env->lps >= 32 || env->lpe >= 32) + return -1; + + for (i = 0; i < 32; i++) { + env->nodes[i].position = hio_read16b(f); + env->nodes[i].value = (int16)hio_read16b(f); + } + + if (hio_error(f)) + return -1; + + return 0; +} + +static int get_venv(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + struct dbm_envelope env; + int i, j, nenv, ins; + + /* Sanity check */ + if (data->have_venv || !data->have_info) { + return -1; + } + data->have_venv = 1; + + nenv = hio_read16b(f); + + D_(D_INFO "Vol envelopes : %d ", nenv); + + for (i = 0; i < nenv; i++) { + if (read_envelope(mod, &env, f) != 0) + return -1; + + ins = env.ins; + mod->xxi[ins].aei.flg = env.flg; + mod->xxi[ins].aei.npt = env.npt; + mod->xxi[ins].aei.sus = env.sus; + mod->xxi[ins].aei.lps = env.lps; + mod->xxi[ins].aei.lpe = env.lpe; + + for (j = 0; j < 32; j++) { + mod->xxi[ins].aei.data[j * 2 + 0] = env.nodes[j].position; + mod->xxi[ins].aei.data[j * 2 + 1] = env.nodes[j].value; + } + } + + return 0; +} + +static int get_penv(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + struct dbm_envelope env; + int i, j, nenv, ins; + + /* Sanity check */ + if (data->have_penv || !data->have_info) { + return -1; + } + data->have_penv = 1; + + nenv = hio_read16b(f); + + D_(D_INFO "Pan envelopes : %d ", nenv); + + for (i = 0; i < nenv; i++) { + if (read_envelope(mod, &env, f) != 0) + return -1; + + ins = env.ins; + mod->xxi[ins].pei.flg = env.flg; + mod->xxi[ins].pei.npt = env.npt; + mod->xxi[ins].pei.sus = env.sus; + mod->xxi[ins].pei.lps = env.lps; + mod->xxi[ins].pei.lpe = env.lpe; + + for (j = 0; j < 32; j++) { + /* DigiBooster Pro 2 stores the pan value between 0 and 64. + * DigiBooster 3 stores it from -128 to 128 (Krashan - M2.dbm). + */ + if (data->maj_version >= 3) { + env.nodes[j].value = env.nodes[j].value / 4 + 32; + } + + mod->xxi[ins].pei.data[j * 2 + 0] = env.nodes[j].position; + mod->xxi[ins].pei.data[j * 2 + 1] = env.nodes[j].value; + } + } + + return 0; +} + +static int dbm_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + char name[XMP_NAME_SIZE]; + uint16 version; + int i, ret; + struct local_data data; + + LOAD_INIT(); + + hio_read32b(f); /* DBM0 */ + + memset(&data, 0, sizeof(struct local_data)); + version = hio_read16b(f); + data.maj_version = version >> 8; + data.min_version = version & 0xFF; + + hio_seek(f, 10, SEEK_CUR); + if (hio_read(name, 1, 44, f) < 44) + return -1; + name[44] = '\0'; + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + m->c4rate = C4_NTSC_RATE; + m->quirk |= QUIRK_FINEFX; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "INFO", get_info); + ret |= libxmp_iff_register(handle, "SONG", get_song); + ret |= libxmp_iff_register(handle, "INST", get_inst); + ret |= libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "SMPL", get_smpl); + ret |= libxmp_iff_register(handle, "VENV", get_venv); + ret |= libxmp_iff_register(handle, "PENV", get_penv); + + if (ret != 0) + return -1; + + strncpy(mod->name, name, XMP_NAME_SIZE); + snprintf(mod->type, XMP_NAME_SIZE, "DigiBooster Pro %d.%02x DBM0", + data.maj_version, data.min_version); + + MODULE_INFO(); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = 0x80; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/digi_load.c b/thirdparty/libxmp/src/loaders/digi_load.c new file mode 100644 index 000000000..e36d4658a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/digi_load.c @@ -0,0 +1,252 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Based on the DIGI Booster player v1.6 by Tap (Tomasz Piasta), with the + * help of Louise Heimann . The following + * DIGI Booster effects are _NOT_ recognized by this player: + * + * 8xx robot + * e00 filter off + * e01 filter on + * e30 backwd play sample + * e31 backwd play sample+loop + * e50 channel off + * e51 channel on + * e8x sample offset 2 + * e9x retrace + */ + +#include "loader.h" + + +static int digi_test (HIO_HANDLE *, char *, const int); +static int digi_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_digi = { + "DIGI Booster", + digi_test, + digi_load +}; + +static int digi_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[20]; + + if (hio_read(buf, 1, 20, f) < 20) + return -1; + + if (memcmp(buf, "DIGI Booster module", 19)) + return -1; + + hio_seek(f, 156, SEEK_CUR); + hio_seek(f, 3 * 4 * 32, SEEK_CUR); + hio_seek(f, 2 * 1 * 32, SEEK_CUR); + + libxmp_read_title(f, t, 32); + + return 0; +} + + +struct digi_header { + uint8 id[20]; /* ID: "DIGI Booster module\0" */ + uint8 vstr[4]; /* Version string: "Vx.y" */ + uint8 ver; /* Version hi-nibble.lo-nibble */ + uint8 chn; /* Number of channels */ + uint8 pack; /* PackEnable */ + uint8 unknown[19]; /* ?! */ + uint8 pat; /* Number of patterns */ + uint8 len; /* Song length */ + uint8 ord[128]; /* Orders */ + uint32 slen[31]; /* Sample length for 31 samples */ + uint32 sloop[31]; /* Sample loop start for 31 samples */ + uint32 sllen[31]; /* Sample loop length for 31 samples */ + uint8 vol[31]; /* Instrument volumes */ + int8 fin[31]; /* Finetunes */ + uint8 title[32]; /* Song name */ + uint8 insname[31][30]; /* Instrument names */ +}; + + +static int digi_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event = 0; + struct digi_header dh; + uint8 digi_event[4], chn_table[64]; + uint16 w; + int i, j, k, c; + + LOAD_INIT(); + + hio_read(dh.id, 20, 1, f); + + hio_read(dh.vstr, 4, 1, f); + dh.ver = hio_read8(f); + dh.chn = hio_read8(f); + dh.pack = hio_read8(f); + hio_read(dh.unknown, 19, 1, f); + dh.pat = hio_read8(f); + dh.len = hio_read8(f); + + /* Sanity check */ + if (dh.len > 127) { + return -1; + } + + hio_read(dh.ord, 128, 1, f); + + for (i = 0; i < 31; i++) + dh.slen[i] = hio_read32b(f); + for (i = 0; i < 31; i++) + dh.sloop[i] = hio_read32b(f); + for (i = 0; i < 31; i++) + dh.sllen[i] = hio_read32b(f); + for (i = 0; i < 31; i++) + dh.vol[i] = hio_read8(f); + for (i = 0; i < 31; i++) + dh.fin[i] = hio_read8s(f); + + if (hio_read(dh.title, 1, 32, f) < 32) { + D_(D_CRIT "read error at title"); + return -1; + } + + for (i = 0; i < 31; i++) { + if (hio_read(dh.insname[i], 1, 30, f) < 30) { + D_(D_CRIT "read error at instrument name %d", i); + return -1; + } + } + + mod->ins = 31; + mod->smp = mod->ins; + mod->pat = dh.pat + 1; + mod->chn = dh.chn; + mod->trk = mod->pat * mod->chn; + mod->len = dh.len + 1; + + m->period_type = PERIOD_MODRNG; + + libxmp_copy_adjust(mod->name, dh.title, 32); + libxmp_set_type(m, "DIGI Booster %-4.4s", dh.vstr); + + MODULE_INFO(); + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = dh.ord[i]; + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments and samples */ + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = dh.slen[i]; + mod->xxs[i].lps = dh.sloop[i]; + mod->xxs[i].lpe = dh.sloop[i] + dh.sllen[i]; + mod->xxs[i].flg = mod->xxs[i].lpe > 0 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].vol = dh.vol[i]; + mod->xxi[i].sub[0].fin = dh.fin[i]; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + libxmp_instrument_name(mod, i, dh.insname[i], 30); + + D_(D_INFO "[%2X] %-30.30s %04x %04x %04x %c V%02x", i, + mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', mod->xxi[i].sub[0].vol); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + if (dh.pack) { + w = (hio_read16b(f) - 64) >> 2; + if (hio_read(chn_table, 1, 64, f) < 64) { + D_(D_CRIT "read error at channel table %d", i); + return -1; + } + } else { + w = 64 * mod->chn; + memset(chn_table, 0xff, sizeof(chn_table)); + } + + for (j = 0; j < 64; j++) { + for (c = 0, k = 0x80; c < mod->chn; c++, k >>= 1) { + if (chn_table[j] & k) { + if (hio_read(digi_event, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + event = &EVENT (i, c, j); + libxmp_decode_protracker_event(event, digi_event); + switch (event->fxt) { + case 0x08: /* Robot */ + event->fxt = event->fxp = 0; + break; + case 0x0e: + switch (MSN (event->fxp)) { + case 0x00: + case 0x03: + case 0x08: + case 0x09: + event->fxt = event->fxp = 0; + break; + case 0x04: + event->fxt = 0x0c; + event->fxp = 0x00; + break; + } + } + w--; + } + } + } + + if (w) { + D_(D_CRIT "Corrupted file (w = %d)", w); + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/dt_load.c b/thirdparty/libxmp/src/loaders/dt_load.c new file mode 100644 index 000000000..175dd7074 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/dt_load.c @@ -0,0 +1,354 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +#define MAGIC_D_T_ MAGIC4('D','.','T','.') + + +static int dt_test(HIO_HANDLE *, char *, const int); +static int dt_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_dt = { + "Digital Tracker", + dt_test, + dt_load +}; + +static int dt_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_D_T_) + return -1; + + hio_read32b(f); /* chunk size */ + hio_read16b(f); /* type */ + hio_read16b(f); /* 0xff then mono */ + hio_read16b(f); /* reserved */ + hio_read16b(f); /* tempo */ + hio_read16b(f); /* bpm */ + hio_read32b(f); /* undocumented */ + + libxmp_read_title(f, t, 32); + + return 0; +} + + +struct local_data { + int pflag, sflag; + int realpat; + int last_pat; + int insnum; +}; + + +static int get_d_t_(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int b; + + hio_read16b(f); /* type */ + hio_read16b(f); /* 0xff then mono */ + hio_read16b(f); /* reserved */ + mod->spd = hio_read16b(f); + if ((b = hio_read16b(f)) > 0) /* RAMBO.DTM has bpm 0 */ + mod->bpm = b; /* Not clamped by Digital Tracker. */ + hio_read32b(f); /* undocumented */ + + hio_read(mod->name, 32, 1, f); + libxmp_set_type(m, "Digital Tracker DTM"); + + MODULE_INFO(); + + return 0; +} + +static int get_s_q_(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i, maxpat; + + /* Sanity check */ + if (mod->pat != 0) { + return -1; + } + + mod->len = hio_read16b(f); + mod->rst = hio_read16b(f); + + /* Sanity check */ + if (mod->len > 256 || mod->rst > 255) { + return -1; + } + + hio_read32b(f); /* reserved */ + + for (maxpat = i = 0; i < 128; i++) { + mod->xxo[i] = hio_read8(f); + if (mod->xxo[i] > maxpat) + maxpat = mod->xxo[i]; + } + mod->pat = maxpat + 1; + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + + /* Sanity check */ + if (data->pflag) { + return -1; + } + + mod->chn = hio_read16b(f); + data->realpat = hio_read16b(f); + mod->trk = mod->chn * mod->pat; + + /* Sanity check */ + if (mod->chn > XMP_MAX_CHANNELS) { + return -1; + } + + return 0; +} + +static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i, c2spd; + uint8 name[30]; + + /* Sanity check */ + if (mod->ins != 0) { + return -1; + } + + mod->ins = mod->smp = hio_read16b(f); + + /* Sanity check */ + if (mod->ins > MAX_INSTRUMENTS) { + return -1; + } + + D_(D_INFO "Instruments : %d ", mod->ins); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + uint32 repstart, replen; + int fine, flag; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + hio_read32b(f); /* reserved */ + mod->xxs[i].len = hio_read32b(f); + mod->xxi[i].nsm = !!mod->xxs[i].len; + fine = hio_read8s(f); /* finetune */ + mod->xxi[i].sub[0].vol = hio_read8(f); + mod->xxi[i].sub[0].pan = 0x80; + repstart = hio_read32b(f); + replen = hio_read32b(f); + mod->xxs[i].lps = repstart; + mod->xxs[i].lpe = repstart + replen - 1; + mod->xxs[i].flg = replen > 2 ? XMP_SAMPLE_LOOP : 0; + + if (hio_read(name, 22, 1, f) == 0) + return -1; + + libxmp_instrument_name(mod, i, name, 22); + + flag = hio_read16b(f); /* bit 0-7:resol 8:stereo */ + if ((flag & 0xff) > 8) { + mod->xxs[i].flg |= XMP_SAMPLE_16BIT; + mod->xxs[i].len >>= 1; + mod->xxs[i].lps >>= 1; + mod->xxs[i].lpe >>= 1; + } + + hio_read32b(f); /* midi note (0x00300000) */ + c2spd = hio_read32b(f); /* frequency */ + libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + + /* It's strange that we have both c2spd and finetune */ + mod->xxi[i].sub[0].fin += fine; + + mod->xxi[i].sub[0].sid = i; + + D_(D_INFO "[%2X] %-22.22s %05x%c%05x %05x %c%c %2db V%02x F%+03d %5d", + i, mod->xxi[i].name, + mod->xxs[i].len, + mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + repstart, + replen, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + flag & 0x100 ? 'S' : ' ', + flag & 0xff, + mod->xxi[i].sub[0].vol, + fine, + c2spd); + } + + return 0; +} + +static int get_dapt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int pat, i, j, k; + struct xmp_event *event; + int rows; + + if (!data->pflag) { + D_(D_INFO "Stored patterns: %d", mod->pat); + data->pflag = 1; + data->last_pat = 0; + + if (libxmp_init_pattern(mod) < 0) + return -1; + } + + hio_read32b(f); /* 0xffffffff */ + pat = hio_read16b(f); + rows = hio_read16b(f); + + /* Sanity check */ + if (pat < 0 || pat >= mod->pat || rows < 0 || rows > 256) { + return -1; + } + if (pat < data->last_pat) { + return -1; + } + + for (i = data->last_pat; i <= pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + } + data->last_pat = pat + 1; + + for (j = 0; j < rows; j++) { + for (k = 0; k < mod->chn; k++) { + uint8 a, b, c, d; + + event = &EVENT(pat, k, j); + a = hio_read8(f); + b = hio_read8(f); + c = hio_read8(f); + d = hio_read8(f); + if (a) { + a--; + event->note = 12 * (a >> 4) + (a & 0x0f) + 12; + } + event->vol = (b & 0xfc) >> 2; + event->ins = ((b & 0x03) << 4) + (c >> 4); + event->fxt = c & 0xf; + event->fxp = d; + } + } + + return 0; +} + +static int get_dait(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + + if (!data->sflag) { + D_(D_INFO "Stored samples : %d ", mod->smp); + data->sflag = 1; + data->insnum = 0; + } + + if (size > 2) { + int ret; + + /* Sanity check */ + if (data->insnum >= mod->ins) { + return -1; + } + + ret = libxmp_load_sample(m, f, SAMPLE_FLAG_BIGEND, + &mod->xxs[mod->xxi[data->insnum].sub[0].sid], NULL); + + if (ret < 0) + return -1; + } + + data->insnum++; + + return 0; +} + +static int dt_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + iff_handle handle; + struct local_data data; + struct xmp_module *mod = &m->mod; + int ret, i; + + LOAD_INIT(); + + memset(&data, 0, sizeof (struct local_data)); + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + m->c4rate = C4_NTSC_RATE; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "D.T.", get_d_t_); + ret |= libxmp_iff_register(handle, "S.Q.", get_s_q_); + ret |= libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "INST", get_inst); + ret |= libxmp_iff_register(handle, "DAPT", get_dapt); + ret |= libxmp_iff_register(handle, "DAIT", get_dait); + + if (ret != 0) + return -1; + + /* Load IFF chunks */ + ret = libxmp_iff_load(handle, m, f , &data); + libxmp_iff_release(handle); + if (ret < 0) + return -1; + + /* alloc remaining patterns */ + if (mod->xxp != NULL) { + for (i = data.last_pat; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) { + return -1; + } + } + } + + return 0; +} + diff --git a/thirdparty/libxmp/src/loaders/emod_load.c b/thirdparty/libxmp/src/loaders/emod_load.c new file mode 100644 index 000000000..590c802c6 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/emod_load.c @@ -0,0 +1,262 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "iff.h" + +#define MAGIC_FORM MAGIC4('F','O','R','M') +#define MAGIC_EMOD MAGIC4('E','M','O','D') +#define MAGIC_EMIC MAGIC4('E','M','I','C') + +static int emod_test(HIO_HANDLE *, char *, const int); +static int emod_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_emod = { + "Quadra Composer", + emod_test, + emod_load +}; + +static int emod_test(HIO_HANDLE * f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_FORM) + return -1; + + hio_read32b(f); + + if (hio_read32b(f) != MAGIC_EMOD) + return -1; + + if (hio_read32b(f) == MAGIC_EMIC) { + hio_read32b(f); /* skip size */ + hio_read16b(f); /* skip version */ + libxmp_read_title(f, t, 20); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + +struct local_data { + int has_emic; + int has_patt; + int has_8smp; +}; + +static int get_emic(struct module_data *m, int size, HIO_HANDLE * f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, ver; + uint8 reorder[256]; + + /* Sanity check */ + if (data->has_emic) { + return -1; + } + data->has_emic = 1; + + ver = hio_read16b(f); + hio_read(mod->name, 1, 20, f); + hio_seek(f, 20, SEEK_CUR); + mod->bpm = hio_read8(f); + mod->ins = hio_read8(f); + mod->smp = mod->ins; + + m->period_type = PERIOD_MODRNG; + + snprintf(mod->type, XMP_NAME_SIZE, "Quadra Composer EMOD v%d", ver); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + uint8 name[20]; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + hio_read8(f); /* num */ + sub->vol = hio_read8(f); + xxs->len = 2 * hio_read16b(f); + if (hio_read(name, 1, 20, f) < 20) + return -1; + libxmp_instrument_name(mod, i, name, 20); + xxs->flg = hio_read8(f) & 1 ? XMP_SAMPLE_LOOP : 0; + sub->fin = hio_read8s(f) << 4; + xxs->lps = 2 * hio_read16b(f); + xxs->lpe = xxs->lps + 2 * hio_read16b(f); + hio_read32b(f); /* ptr */ + + xxi->nsm = 1; + sub->pan = 0x80; + sub->sid = i; + + D_(D_INFO "[%2X] %-20.20s %05x %05x %05x %c V%02x %+d", + i, xxi->name, xxs->len, xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, sub->fin >> 4); + } + + hio_read8(f); /* pad */ + mod->pat = hio_read8(f); + mod->trk = mod->pat * mod->chn; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + memset(reorder, 0, sizeof(reorder)); + + for (i = 0; i < mod->pat; i++) { + reorder[hio_read8(f)] = i; + + if (libxmp_alloc_pattern_tracks(mod, i, hio_read8(f) + 1) < 0) + return -1; + + hio_seek(f, 20, SEEK_CUR); /* skip name */ + hio_read32b(f); /* ptr */ + } + + mod->len = hio_read8(f); + + D_(D_INFO "Module length: %d", mod->len); + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = reorder[hio_read8(f)]; + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE * f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + struct xmp_event *event; + int i, j, k; + uint8 x; + + /* Sanity check */ + if (data->has_patt || !data->has_emic) { + return -1; + } + data->has_patt = 1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + event->ins = hio_read8(f); + event->note = hio_read8(f) + 1; + if (event->note != 0) + event->note += 48; + event->fxt = hio_read8(f) & 0x0f; + event->fxp = hio_read8(f); + + /* Fix effects */ + switch (event->fxt) { + case 0x04: + x = event->fxp; + event->fxp = + (x & 0xf0) | ((x << 1) & 0x0f); + break; + case 0x09: + event->fxt <<= 1; + break; + case 0x0b: + x = event->fxt; + event->fxt = 16 * (x / 10) + x % 10; + break; + } + } + } + } + + return 0; +} + +static int get_8smp(struct module_data *m, int size, HIO_HANDLE * f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->has_8smp || !data->has_emic) { + return -1; + } + data->has_8smp = 1; + + D_(D_INFO "Stored samples : %d ", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} + +static int emod_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + iff_handle handle; + struct local_data data; + int ret; + + LOAD_INIT(); + + memset(&data, 0, sizeof(struct local_data)); + + hio_read32b(f); /* FORM */ + hio_read32b(f); + hio_read32b(f); /* EMOD */ + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "EMIC", get_emic); + ret |= libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "8SMP", get_8smp); + + if (ret != 0) + return -1; + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/far_load.c b/thirdparty/libxmp/src/loaders/far_load.c new file mode 100644 index 000000000..1a6e129dc --- /dev/null +++ b/thirdparty/libxmp/src/loaders/far_load.c @@ -0,0 +1,472 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Based on the Farandole Composer format specifications by Daniel Potter. + * + * "(...) this format is for EDITING purposes (storing EVERYTHING you're + * working on) so it may include information not completely neccessary." + */ + +#include "loader.h" +#include "../far_extras.h" + +struct far_header { + uint32 magic; /* File magic: 'FAR\xfe' */ + uint8 name[40]; /* Song name */ + uint8 crlf[3]; /* 0x0d 0x0a 0x1A */ + uint16 headersize; /* Remaining header size in bytes */ + uint8 version; /* Version MSN=major, LSN=minor */ + uint8 ch_on[16]; /* Channel on/off switches */ + uint8 rsvd1[9]; /* Current editing values */ + uint8 tempo; /* Default tempo */ + uint8 pan[16]; /* Channel pan definitions */ + uint8 rsvd2[4]; /* Grid, mode (for editor) */ + uint16 textlen; /* Length of embedded text */ +}; + +struct far_header2 { + uint8 order[256]; /* Orders */ + uint8 patterns; /* Number of stored patterns (?) */ + uint8 songlen; /* Song length in patterns */ + uint8 restart; /* Restart pos */ + uint16 patsize[256]; /* Size of each pattern in bytes */ +}; + +struct far_instrument { + uint8 name[32]; /* Instrument name */ + uint32 length; /* Length of sample (up to 64Kb) */ + uint8 finetune; /* Finetune (unsuported) */ + uint8 volume; /* Volume (unsuported?) */ + uint32 loop_start; /* Loop start */ + uint32 loopend; /* Loop end */ + uint8 sampletype; /* 1=16 bit sample */ + uint8 loopmode; +}; + +struct far_event { + uint8 note; + uint8 instrument; + uint8 volume; /* In reverse nibble order? */ + uint8 effect; +}; + + +#define MAGIC_FAR MAGIC4('F','A','R',0xfe) + + +static int far_test (HIO_HANDLE *, char *, const int); +static int far_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_far = { + "Farandole Composer", + far_test, + far_load +}; + +static int far_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_FAR) + return -1; + + libxmp_read_title(f, t, 40); + + return 0; +} + + +static void far_translate_effect(struct xmp_event *event, int fx, int param, int vol) +{ + switch (fx) { + case 0x0: /* 0x0? Global funct */ + switch (param) { + case 0x1: /* 0x01 Ramp delay on */ + case 0x2: /* 0x02 Ramp delay off */ + /* These control volume ramping and can be ignored. */ + break; + case 0x3: /* 0x03 Fulfill loop */ + /* This is intended to be sustain release, but the + * effect is buggy and just cuts most of the time. */ + event->fxt = FX_KEYOFF; + break; + case 0x4: /* 0x04 Old FAR tempo */ + event->fxt = FX_FAR_TEMPO; + event->fxp = 0x10; + break; + case 0x5: /* 0x05 New FAR tempo */ + event->fxt = FX_FAR_TEMPO; + event->fxp = 0x20; + break; + } + break; + case 0x1: /* 0x1? Pitch offset up */ + event->fxt = FX_FAR_PORTA_UP; + event->fxp = param; + break; + case 0x2: /* 0x2? Pitch offset down */ + event->fxt = FX_FAR_PORTA_DN; + event->fxp = param; + break; + case 0x3: /* 0x3? Note-port */ + event->fxt = FX_FAR_TPORTA; + event->fxp = param; + break; + case 0x4: /* 0x4? Retrigger */ + event->fxt = FX_FAR_RETRIG; + event->fxp = param; + break; + case 0x5: /* 0x5? Set Vibrato depth */ + event->fxt = FX_FAR_VIBDEPTH; + event->fxp = param; + break; + case 0x6: /* 0x6? Vibrato note */ + event->fxt = FX_FAR_VIBRATO; + event->fxp = param; + break; + case 0x7: /* 0x7? Vol Sld Up */ + event->fxt = FX_F_VSLIDE_UP; + event->fxp = (param << 4); + break; + case 0x8: /* 0x8? Vol Sld Dn */ + event->fxt = FX_F_VSLIDE_DN; + event->fxp = (param << 4); + break; + case 0x9: /* 0x9? Sustained vibrato */ + event->fxt = FX_FAR_VIBRATO; + event->fxp = 0x10 /* Vibrato sustain flag */ | param; + break; + case 0xa: /* 0xa? Slide-to-vol */ + if (vol >= 0x01 && vol <= 0x10) { + event->fxt = FX_FAR_SLIDEVOL; + event->fxp = ((vol - 1) << 4) | param; + event->vol = 0; + } + break; + case 0xb: /* 0xb? Balance */ + event->fxt = FX_SETPAN; + event->fxp = (param << 4) | param; + break; + case 0xc: /* 0xc? Note Offset */ + event->fxt = FX_FAR_DELAY; + event->fxp = param; + break; + case 0xd: /* 0xd? Fine tempo down */ + event->fxt = FX_FAR_F_TEMPO; + event->fxp = param; + break; + case 0xe: /* 0xe? Fine tempo up */ + event->fxt = FX_FAR_F_TEMPO; + event->fxp = param << 4; + break; + case 0xf: /* 0xf? Set tempo */ + event->fxt = FX_FAR_TEMPO; + event->fxp = param; + break; + } +} + +#define COMMENT_MAXLINES 44 + +static void far_read_text(char *dest, size_t textlen, HIO_HANDLE *f) +{ + /* FAR module text uses 132-char lines with no line breaks... */ + size_t end, lastchar, i; + + if (textlen > COMMENT_MAXLINES * 132) + textlen = COMMENT_MAXLINES * 132; + + while (textlen) { + end = MIN(textlen, 132); + textlen -= end; + end = hio_read(dest, 1, end, f); + + lastchar = 0; + for (i = 0; i < end; i++) { + /* Nulls in the text area are equivalent to spaces. */ + if (dest[i] == '\0') + dest[i] = ' '; + else if (dest[i] != ' ') + lastchar = i; + } + dest += lastchar + 1; + *dest++ = '\n'; + } + *dest = '\0'; +} + +static int far_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct far_module_extras *me; + int i, j, k; + struct xmp_event *event; + struct far_header ffh; + struct far_header2 ffh2; + struct far_instrument fih; + uint8 *patbuf = NULL; + uint8 sample_map[8]; + + LOAD_INIT(); + + hio_read32b(f); /* File magic: 'FAR\xfe' */ + hio_read(ffh.name, 40, 1, f); /* Song name */ + hio_read(ffh.crlf, 3, 1, f); /* 0x0d 0x0a 0x1A */ + ffh.headersize = hio_read16l(f); /* Remaining header size in bytes */ + ffh.version = hio_read8(f); /* Version MSN=major, LSN=minor */ + hio_read(ffh.ch_on, 16, 1, f); /* Channel on/off switches */ + hio_seek(f, 9, SEEK_CUR); /* Current editing values */ + ffh.tempo = hio_read8(f); /* Default tempo */ + hio_read(ffh.pan, 16, 1, f); /* Channel pan definitions */ + hio_read32l(f); /* Grid, mode (for editor) */ + ffh.textlen = hio_read16l(f); /* Length of embedded text */ + + /* Sanity check */ + if (ffh.tempo >= 16) { + return -1; + } + + if ((m->comment = (char *)malloc(ffh.textlen + COMMENT_MAXLINES + 1)) != NULL) { + far_read_text(m->comment, ffh.textlen, f); + } else { + hio_seek(f, ffh.textlen, SEEK_CUR); /* Skip song text */ + } + + hio_read(ffh2.order, 256, 1, f); /* Orders */ + ffh2.patterns = hio_read8(f); /* Number of stored patterns (?) */ + ffh2.songlen = hio_read8(f); /* Song length in patterns */ + ffh2.restart = hio_read8(f); /* Restart pos */ + for (i = 0; i < 256; i++) { + ffh2.patsize[i] = hio_read16l(f); /* Size of each pattern in bytes */ + } + + if (hio_error(f)) { + return -1; + } + + /* Skip unsupported header extension if it exists. The documentation claims + * this field is the "remaining" header size, but it's the total size. */ + if (ffh.headersize > 869 + ffh.textlen) { + if (hio_seek(f, ffh.headersize, SEEK_SET)) + return -1; + } + + mod->chn = 16; + /*mod->pat=ffh2.patterns; (Error in specs? --claudio) */ + mod->len = ffh2.songlen; + mod->rst = ffh2.restart; + memcpy (mod->xxo, ffh2.order, mod->len); + + for (mod->pat = i = 0; i < 256; i++) { + if (ffh2.patsize[i]) + mod->pat = i + 1; + } + /* Make sure referenced zero-sized patterns are also counted. */ + for (i = 0; i < mod->len; i++) { + if (mod->pat <= mod->xxo[i]) + mod->pat = mod->xxo[i] + 1; + } + + mod->trk = mod->chn * mod->pat; + + if (libxmp_far_new_module_extras(m) != 0) + return -1; + + me = FAR_MODULE_EXTRAS(*m); + me->coarse_tempo = ffh.tempo; + me->fine_tempo = 0; + me->tempo_mode = 1; + m->time_factor = FAR_TIME_FACTOR; + libxmp_far_translate_tempo(1, 0, me->coarse_tempo, &me->fine_tempo, &mod->spd, &mod->bpm); + + m->period_type = PERIOD_CSPD; + m->c4rate = C4_NTSC_RATE; + + m->quirk |= QUIRK_VSALL | QUIRK_PBALL | QUIRK_VIBALL; + + strncpy(mod->name, (char *)ffh.name, 40); + libxmp_set_type(m, "Farandole Composer %d.%d", MSN(ffh.version), LSN(ffh.version)); + + MODULE_INFO(); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Comment bytes : %d", ffh.textlen); + D_(D_INFO "Stored patterns: %d", mod->pat); + + if ((patbuf = (uint8 *)malloc(256 * 16 * 4)) == NULL) + return -1; + + for (i = 0; i < mod->pat; i++) { + uint8 brk, note, ins, vol, fxb; + uint8 *pos; + int rows; + + if (libxmp_alloc_pattern(mod, i) < 0) + goto err; + + if (!ffh2.patsize[i]) + continue; + + rows = (ffh2.patsize[i] - 2) / 64; + + /* Sanity check */ + if (rows <= 0 || rows > 256) { + goto err; + } + + mod->xxp[i]->rows = rows; + + if (libxmp_alloc_tracks_in_pattern(mod, i) < 0) + goto err; + + brk = hio_read8(f) + 1; + hio_read8(f); + + if (hio_read(patbuf, rows * 64, 1, f) < 1) { + D_(D_CRIT "read error at pat %d", i); + goto err; + } + + pos = patbuf; + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + + if (k == 0 && j == brk) + event->f2t = FX_BREAK; + + note = *pos++; + ins = *pos++; + vol = *pos++; + fxb = *pos++; + + if (note) + event->note = note + 48; + if (event->note || ins) + event->ins = ins + 1; + + if (vol >= 0x01 && vol <= 0x10) + event->vol = (vol - 1) * 16 + 1; + + far_translate_effect(event, MSN(fxb), LSN(fxb), vol); + } + } + } + free(patbuf); + + /* Allocate tracks for any patterns referenced with a size of 0. These + * use the configured pattern break position, which is 64 by default. */ + for (i = 0; i < mod->len; i++) { + int pat = mod->xxo[i]; + if (mod->xxp[pat]->rows == 0) { + mod->xxp[pat]->rows = 64; + if (libxmp_alloc_tracks_in_pattern(mod, pat) < 0) + return -1; + } + } + + mod->ins = -1; + if (hio_read(sample_map, 1, 8, f) < 8) { + D_(D_CRIT "read error at sample map"); + return -1; + } + for (i = 0; i < 64; i++) { + if (sample_map[i / 8] & (1 << (i % 8))) + mod->ins = i; + } + mod->ins++; + + mod->smp = mod->ins; + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments and samples */ + + for (i = 0; i < mod->ins; i++) { + if (!(sample_map[i / 8] & (1 << (i % 8)))) + continue; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + hio_read(fih.name, 32, 1, f); /* Instrument name */ + fih.length = hio_read32l(f); /* Length of sample (up to 64Kb) */ + fih.finetune = hio_read8(f); /* Finetune (unsuported) */ + fih.volume = hio_read8(f); /* Volume (unsuported?) */ + fih.loop_start = hio_read32l(f);/* Loop start */ + fih.loopend = hio_read32l(f); /* Loop end */ + fih.sampletype = hio_read8(f); /* 1=16 bit sample */ + fih.loopmode = hio_read8(f); + + /* Sanity check */ + if (fih.length > 0x10000 || fih.loop_start > 0x10000 || + fih.loopend > 0x10000) { + return -1; + } + + mod->xxs[i].len = fih.length; + mod->xxs[i].lps = fih.loop_start; + mod->xxs[i].lpe = fih.loopend; + mod->xxs[i].flg = 0; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + if (fih.sampletype != 0) { + mod->xxs[i].flg |= XMP_SAMPLE_16BIT; + mod->xxs[i].len >>= 1; + mod->xxs[i].lps >>= 1; + mod->xxs[i].lpe >>= 1; + } + + mod->xxs[i].flg |= fih.loopmode ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].vol = 0xff; /* fih.volume; */ + mod->xxi[i].sub[0].sid = i; + + libxmp_instrument_name(mod, i, fih.name, 32); + + D_(D_INFO "[%2X] %-32.32s %04x %04x %04x %c V%02x", + i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, fih.loopmode ? 'L' : ' ', mod->xxi[i].sub[0].vol); + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + /* Panning map */ + for (i = 0; i < 16; i++) { + if (ffh.ch_on[i] == 0) + mod->xxc[i].flg |= XMP_CHANNEL_MUTE; + if (ffh.pan[i] < 0x10) + mod->xxc[i].pan = (ffh.pan[i] << 4) | ffh.pan[i]; + } + + m->volbase = 0xf0; + + return 0; + + err: + free(patbuf); + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/flt_load.c b/thirdparty/libxmp/src/loaders/flt_load.c new file mode 100644 index 000000000..a2d493c2c --- /dev/null +++ b/thirdparty/libxmp/src/loaders/flt_load.c @@ -0,0 +1,491 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "mod.h" +#include "../period.h" + +static int flt_test(HIO_HANDLE *, char *, const int); +static int flt_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_flt = { + "Startrekker", + flt_test, + flt_load +}; + +static int flt_test(HIO_HANDLE * f, char *t, const int start) +{ + char buf[4]; + + hio_seek(f, start + 1080, SEEK_SET); + if (hio_read(buf, 1, 4, f) < 4) + return -1; + + /* Also RASP? */ + if (memcmp(buf, "FLT", 3) && memcmp(buf, "EXO", 3)) + return -1; + + if (buf[3] != '4' && buf[3] != '8' && buf[3] != 'M') + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + +/* Waveforms from the Startrekker 1.2 AM synth replayer code */ + +static const int8 am_waveform[3][32] = { + { 0, 25, 49, 71, 90, 106, 117, 125, /* Sine */ + 127, 125, 117, 106, 90, 71, 49, 25, + 0, -25, -49, -71, -90, -106, -117, -125, + -127, -125, -117, -106, -90, -71, -49, -25 + }, + + { -128, -120, -112, -104, -96, -88, -80, -72, /* Ramp */ + -64, -56, -48, -40, -32, -24, -16, -8, + 0, 8, 16, 24, 32, 40, 48, 56, + 64, 72, 80, 88, 96, 104, 112, 120 + }, + + { -128, -128, -128, -128, -128, -128, -128, -128, /* Square */ + -128, -128, -128, -128, -128, -128, -128, -128, + 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127 + } +}; + +struct am_instrument { + int16 l0; /* start amplitude */ + int16 a1l; /* attack level */ + int16 a1s; /* attack speed */ + int16 a2l; /* secondary attack level */ + int16 a2s; /* secondary attack speed */ + int16 sl; /* sustain level */ + int16 ds; /* decay speed */ + int16 st; /* sustain time */ + int16 rs; /* release speed */ + int16 wf; /* waveform */ + int16 p_fall; /* ? */ + int16 v_amp; /* vibrato amplitude */ + int16 v_spd; /* vibrato speed */ + int16 fq; /* base frequency */ +}; + +static int is_am_instrument(HIO_HANDLE *nt, int i) +{ + char buf[2]; + int16 wf; + + hio_seek(nt, 144 + i * 120, SEEK_SET); + hio_read(buf, 1, 2, nt); + if (memcmp(buf, "AM", 2)) + return 0; + hio_seek(nt, 24, SEEK_CUR); + wf = hio_read16b(nt); + if (hio_error(nt) || wf < 0 || wf > 3) + return 0; + + return 1; +} + +static int read_am_instrument(struct module_data *m, HIO_HANDLE *nt, int i) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_envelope *vol_env = &xxi->aei; + struct xmp_envelope *freq_env = &xxi->fei; + struct am_instrument am; + char *wave; + int a, b; + int8 am_noise[1024]; + + hio_seek(nt, 144 + i * 120 + 2 + 4, SEEK_SET); + am.l0 = hio_read16b(nt); + am.a1l = hio_read16b(nt); + am.a1s = hio_read16b(nt); + am.a2l = hio_read16b(nt); + am.a2s = hio_read16b(nt); + am.sl = hio_read16b(nt); + am.ds = hio_read16b(nt); + am.st = hio_read16b(nt); + hio_read16b(nt); + am.rs = hio_read16b(nt); + am.wf = hio_read16b(nt); + am.p_fall = -(int16) hio_read16b(nt); + am.v_amp = hio_read16b(nt); + am.v_spd = hio_read16b(nt); + am.fq = hio_read16b(nt); + + if (hio_error(nt)) { + return -1; + } + +#if 0 + printf + ("L0=%d A1L=%d A1S=%d A2L=%d A2S=%d SL=%d DS=%d ST=%d RS=%d WF=%d\n", + am.l0, am.a1l, am.a1s, am.a2l, am.a2s, am.sl, am.ds, am.st, am.rs, + am.wf); +#endif + + if (am.wf < 3) { + xxs->len = 32; + xxs->lps = 0; + xxs->lpe = 32; + wave = (char *)&am_waveform[am.wf][0]; + } else { + int j; + + xxs->len = 1024; + xxs->lps = 0; + xxs->lpe = 1024; + + for (j = 0; j < 1024; j++) + am_noise[j] = rand() % 256; + + wave = (char *)&am_noise[0]; + } + + xxs->flg = XMP_SAMPLE_LOOP; + xxi->sub[0].vol = 0x40; /* prelude.mod has 0 in instrument */ + xxi->nsm = 1; + xxi->sub[0].xpo = -12 * am.fq; + xxi->sub[0].vwf = 0; + xxi->sub[0].vde = am.v_amp << 2; + xxi->sub[0].vra = am.v_spd; + + /* + * AM synth envelope parameters based on the Startrekker 1.2 docs + * + * L0 Start amplitude for the envelope + * A1L Attack level + * A1S The speed that the amplitude changes to the attack level, $1 + * is slow and $40 is fast. + * A2L Secondary attack level, for those who likes envelopes... + * A2S Secondary attack speed. + * DS The speed that the amplitude decays down to the: + * SL Sustain level. There is remains for the time set by the + * ST Sustain time. + * RS Release speed. The speed that the amplitude falls from ST to 0. + */ + if (am.a1s == 0) + am.a1s = 1; + if (am.a2s == 0) + am.a2s = 1; + if (am.ds == 0) + am.ds = 1; + if (am.rs == 0) + am.rs = 1; + + vol_env->npt = 6; + vol_env->flg = XMP_ENVELOPE_ON; + + vol_env->data[0] = 0; + vol_env->data[1] = am.l0 / 4; + + /* + * Startrekker increments/decrements the envelope by the stage speed + * until it reaches the next stage level. + * + * ^ + * | + * 100 +.........o + * | /: + * A2L +.......o : x = 256 * (A2L - A1L) / (256 - A1L) + * | /: : + * | / : : + * A1L +....o..:.: + * | : : : + * | :x : : + * +----+--+-+-----> + * | | + * |256/| + * A2S + */ + + if (am.a1l > am.l0) { + a = am.a1l - am.l0; + b = 256 - am.l0; + } else { + a = am.l0 - am.a1l; + b = am.l0; + } + if (b == 0) + b = 1; + + vol_env->data[2] = vol_env->data[0] + (256 * a) / (am.a1s * b); + vol_env->data[3] = am.a1l / 4; + + if (am.a2l > am.a1l) { + a = am.a2l - am.a1l; + b = 256 - am.a1l; + } else { + a = am.a1l - am.a2l; + b = am.a1l; + } + if (b == 0) + b = 1; + + vol_env->data[4] = vol_env->data[2] + (256 * a) / (am.a2s * b); + vol_env->data[5] = am.a2l / 4; + + if (am.sl > am.a2l) { + a = am.sl - am.a2l; + b = 256 - am.a2l; + } else { + a = am.a2l - am.sl; + b = am.a2l; + } + if (b == 0) + b = 1; + + vol_env->data[6] = vol_env->data[4] + (256 * a) / (am.ds * b); + vol_env->data[7] = am.sl / 4; + vol_env->data[8] = vol_env->data[6] + am.st; + vol_env->data[9] = am.sl / 4; + vol_env->data[10] = vol_env->data[8] + (256 / am.rs); + vol_env->data[11] = 0; + + /* + * Implement P.FALL using pitch envelope + */ + + if (am.p_fall) { + freq_env->npt = 2; + freq_env->flg = XMP_ENVELOPE_ON; + freq_env->data[0] = 0; + freq_env->data[1] = 0; + freq_env->data[2] = 1024 / abs(am.p_fall); + freq_env->data[3] = 10 * (am.p_fall < 0 ? -256 : 256); + } + + if (libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD, xxs, wave)) + return -1; + + return 0; +} + +static int flt_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct mod_header mh; + uint8 mod_event[4]; + const char *tracker; + char filename[1024]; + char buf[16]; + HIO_HANDLE *nt; + int am_synth; + + LOAD_INIT(); + + /* See if we have the synth parameters file */ + am_synth = 0; + snprintf(filename, 1024, "%s%s.NT", m->dirname, m->basename); + if ((nt = hio_open(filename, "rb")) == NULL) { + snprintf(filename, 1024, "%s%s.nt", m->dirname, m->basename); + if ((nt = hio_open(filename, "rb")) == NULL) { + snprintf(filename, 1024, "%s%s.AS", m->dirname, + m->basename); + if ((nt = hio_open(filename, "rb")) == NULL) { + snprintf(filename, 1024, "%s%s.as", m->dirname, + m->basename); + nt = hio_open(filename, "rb"); + } + } + } + + tracker = "Startrekker"; + + if (nt) { + if (hio_read(buf, 1, 16, nt) != 16) { + goto err; + } + if (memcmp(buf, "ST1.2 ModuleINFO", 16) == 0) { + am_synth = 1; + tracker = "Startrekker 1.2"; + } else if (memcmp(buf, "ST1.3 ModuleINFO", 16) == 0) { + am_synth = 1; + tracker = "Startrekker 1.3"; + } else if (memcmp(buf, "AudioSculpture10", 16) == 0) { + am_synth = 1; + tracker = "AudioSculpture 1.0"; + } + } + + hio_read(mh.name, 20, 1, f); + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 128, 1, f); + hio_read(mh.magic, 4, 1, f); + + if (mh.magic[3] == '4') { + mod->chn = 4; + } else { + mod->chn = 8; + } + + mod->ins = 31; + mod->smp = mod->ins; + mod->len = mh.len; + mod->rst = mh.restart; + memcpy(mod->xxo, mh.order, 128); + + for (i = 0; i < 128; i++) { + if (mod->chn > 4) + mod->xxo[i] >>= 1; + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + } + + mod->pat++; + + mod->trk = mod->chn * mod->pat; + + strncpy(mod->name, (char *)mh.name, 20); + libxmp_set_type(m, "%s %4.4s", tracker, mh.magic); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + goto err; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + goto err; + + sub = &xxi->sub[0]; + + xxs->len = 2 * mh.ins[i].size; + xxs->lps = 2 * mh.ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size; + xxs->flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + sub->fin = (int8) (mh.ins[i].finetune << 4); + sub->vol = mh.ins[i].volume; + sub->pan = 0x80; + sub->sid = i; + xxi->rls = 0xfff; + + if (xxs->len > 0) + xxi->nsm = 1; + + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + } + + if (libxmp_init_pattern(mod) < 0) + goto err; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + /* "The format you are looking for is FLT8, and the ONLY two + * differences are: It says FLT8 instead of FLT4 or M.K., AND, the + * patterns are PAIRED. I thought this was the easiest 8 track + * format possible, since it can be loaded in a normal 4 channel + * tracker if you should want to rip sounds or patterns. So, in a + * 8 track FLT8 module, patterns 00 and 01 is "really" pattern 00. + * Patterns 02 and 03 together is "really" pattern 01. Thats it. + * Oh well, I didnt have the time to implement all effect commands + * either, so some FLT8 modules would play back badly (I think + * especially the portamento command uses a different "scale" than + * the normal portamento command, that would be hard to patch). + */ + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + goto err; + + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, j % 4, j / 4); + if (hio_read(mod_event, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + goto err; + } + libxmp_decode_noisetracker_event(event, mod_event); + } + if (mod->chn > 4) { + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, (j % 4) + 4, j / 4); + if (hio_read(mod_event, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + goto err; + } + libxmp_decode_noisetracker_event(event, mod_event); + + /* no macros */ + if (event->fxt == 0x0e) + event->fxt = event->fxp = 0; + } + } + } + + /* no such limit for synth instruments + * mod->flg |= XXM_FLG_MODRNG; + */ + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (mod->xxs[i].len == 0) { + if (am_synth && is_am_instrument(nt, i)) { + if (read_am_instrument(m, nt, i) < 0) { + D_(D_CRIT "Missing nt file"); + goto err; + } + } + continue; + } + if (libxmp_load_sample(m, f, SAMPLE_FLAG_FULLREP, &mod->xxs[i], NULL) < + 0) { + goto err; + } + } + + if (nt) { + hio_close(nt); + } + + return 0; + + err: + if (nt) { + hio_close(nt); + } + + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/fnk_load.c b/thirdparty/libxmp/src/loaders/fnk_load.c new file mode 100644 index 000000000..8eaff912a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/fnk_load.c @@ -0,0 +1,342 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" + +#define MAGIC_Funk MAGIC4('F','u','n','k') + + +static int fnk_test (HIO_HANDLE *, char *, const int); +static int fnk_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_fnk = { + "Funktracker", + fnk_test, + fnk_load +}; + +static int fnk_test(HIO_HANDLE *f, char *t, const int start) +{ + uint8 a, b; + int size; + + if (hio_read32b(f) != MAGIC_Funk) + return -1; + + hio_read8(f); + a = hio_read8(f); + b = hio_read8(f); + hio_read8(f); + + if ((a >> 1) < 10) /* creation year (-1980) */ + return -1; + + if (MSN(b) > 7 || LSN(b) > 9) /* CPU and card */ + return -1; + + size = hio_read32l(f); + if (size < 1024) + return -1; + + if (hio_size(f) != size) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + + +struct fnk_instrument { + uint8 name[19]; /* ASCIIZ instrument name */ + uint32 loop_start; /* Instrument loop start */ + uint32 length; /* Instrument length */ + uint8 volume; /* Volume (0-255) */ + uint8 pan; /* Pan (0-255) */ + uint8 shifter; /* Portamento and offset shift */ + uint8 waveform; /* Vibrato and tremolo waveforms */ + uint8 retrig; /* Retrig and arpeggio speed */ +}; + +struct fnk_header { + uint8 marker[4]; /* 'Funk' */ + uint8 info[4]; /* */ + uint32 filesize; /* File size */ + uint8 fmt[4]; /* F2xx, Fkxx or Fvxx */ + uint8 loop; /* Loop order number */ + uint8 order[256]; /* Order list */ + uint8 pbrk[128]; /* Break list for patterns */ + struct fnk_instrument fih[64]; /* Instruments */ +}; + + +static void fnk_translate_event(struct xmp_event *event, const uint8 ev[3], + const struct fnk_header *ffh) +{ + switch (ev[0] >> 2) { + case 0x3f: + case 0x3e: + case 0x3d: + break; + default: + event->note = 37 + (ev[0] >> 2); + event->ins = 1 + MSN(ev[1]) + ((ev[0] & 0x03) << 4); + event->vol = ffh->fih[event->ins - 1].volume; + } + + switch (LSN(ev[1])) { + case 0x00: + event->fxt = FX_PER_PORTA_UP; + event->fxp = ev[2]; + break; + case 0x01: + event->fxt = FX_PER_PORTA_DN; + event->fxp = ev[2]; + break; + case 0x02: + event->fxt = FX_PER_TPORTA; + event->fxp = ev[2]; + break; + case 0x03: + event->fxt = FX_PER_VIBRATO; + event->fxp = ev[2]; + break; + case 0x06: + event->fxt = FX_PER_VSLD_UP; + event->fxp = ev[2] << 1; + break; + case 0x07: + event->fxt = FX_PER_VSLD_DN; + event->fxp = ev[2] << 1; + break; + case 0x0b: + event->fxt = FX_ARPEGGIO; + event->fxp = ev[2]; + break; + case 0x0d: + event->fxt = FX_VOLSET; + event->fxp = ev[2]; + break; + case 0x0e: + if (ev[2] == 0x0a || ev[2] == 0x0b || ev[2] == 0x0c) { + event->fxt = FX_PER_CANCEL; + break; + } + + switch (MSN(ev[2])) { + case 0x1: + event->fxt = FX_EXTENDED; + event->fxp = (EX_CUT << 4) | LSN(ev[2]); + break; + case 0x2: + event->fxt = FX_EXTENDED; + event->fxp = (EX_DELAY << 4) | LSN(ev[2]); + break; + case 0xd: + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | LSN(ev[2]); + break; + case 0xe: + event->fxt = FX_SETPAN; + event->fxp = 8 + (LSN(ev[2]) << 4); + break; + case 0xf: + event->fxt = FX_SPEED; + event->fxp = LSN(ev[2]); + break; + } + } +} + +static int fnk_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + /* int day, month, year; */ + struct xmp_event *event; + struct fnk_header ffh; + uint8 ev[3]; + + LOAD_INIT(); + + hio_read(ffh.marker, 4, 1, f); + hio_read(ffh.info, 4, 1, f); + ffh.filesize = hio_read32l(f); + hio_read(ffh.fmt, 4, 1, f); + ffh.loop = hio_read8(f); + hio_read(ffh.order, 256, 1, f); + hio_read(ffh.pbrk, 128, 1, f); + + for (i = 0; i < 128; i++) { + if (ffh.pbrk[i] >= 64) { + return -1; + } + } + + for (i = 0; i < 64; i++) { + hio_read(ffh.fih[i].name, 19, 1, f); + ffh.fih[i].loop_start = hio_read32l(f); + ffh.fih[i].length = hio_read32l(f); + ffh.fih[i].volume = hio_read8(f); + ffh.fih[i].pan = hio_read8(f); + ffh.fih[i].shifter = hio_read8(f); + ffh.fih[i].waveform = hio_read8(f); + ffh.fih[i].retrig = hio_read8(f); + /* Sanity check */ + if (ffh.fih[i].length >= ffh.filesize) { + return -1; + } + } + + /* day = ffh.info[0] & 0x1f; + month = ((ffh.info[1] & 0x01) << 3) | ((ffh.info[0] & 0xe0) >> 5); + year = 1980 + ((ffh.info[1] & 0xfe) >> 1); */ + + mod->smp = mod->ins = 64; + + for (i = 0; i < 256 && ffh.order[i] != 0xff; i++) { + if (ffh.order[i] > mod->pat) + mod->pat = ffh.order[i]; + } + mod->pat++; + + /* Sanity check */ + if (mod->pat > 128) { + return -1; + } + + mod->len = i; + memcpy (mod->xxo, ffh.order, mod->len); + + mod->spd = 4; + mod->bpm = 125; + mod->chn = 0; + + /* + * If an R1 fmt (funktype = Fk** or Fv**), then ignore byte 3. It's + * unreliable. It used to store the (GUS) sample memory requirement. + */ + if (ffh.fmt[0] == 'F' && ffh.fmt[1] == '2') { + if (((int8)ffh.info[3] >> 1) & 0x40) + mod->bpm -= (ffh.info[3] >> 1) & 0x3f; + else + mod->bpm += (ffh.info[3] >> 1) & 0x3f; + + libxmp_set_type(m, "FunktrackerGOLD"); + } else if (ffh.fmt[0] == 'F' && (ffh.fmt[1] == 'v' || ffh.fmt[1] == 'k')) { + libxmp_set_type(m, "Funktracker"); + } else { + mod->chn = 8; + libxmp_set_type(m, "Funktracker DOS32"); + } + + if (mod->chn == 0) { + mod->chn = (ffh.fmt[2] < '0') || (ffh.fmt[2] > '9') || + (ffh.fmt[3] < '0') || (ffh.fmt[3] > '9') ? 8 : + (ffh.fmt[2] - '0') * 10 + ffh.fmt[3] - '0'; + + /* Sanity check */ + if (mod->chn <= 0 || mod->chn > XMP_MAX_CHANNELS) + return -1; + } + + mod->bpm = 4 * mod->bpm / 5; + mod->trk = mod->chn * mod->pat; + + /* FNK allows mode per instrument but we don't, so use linear for all */ + m->period_type = PERIOD_LINEAR; + + MODULE_INFO(); + /* D_(D_INFO "Creation date: %02d/%02d/%04d", day, month, year); */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Convert instruments */ + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = ffh.fih[i].length; + mod->xxs[i].lps = ffh.fih[i].loop_start; + if (mod->xxs[i].lps == -1) + mod->xxs[i].lps = 0; + mod->xxs[i].lpe = ffh.fih[i].length; + mod->xxs[i].flg = ffh.fih[i].loop_start != -1 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].vol = ffh.fih[i].volume; + mod->xxi[i].sub[0].pan = ffh.fih[i].pan; + mod->xxi[i].sub[0].sid = i; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + libxmp_instrument_name(mod, i, ffh.fih[i].name, 19); + + D_(D_INFO "[%2X] %-20.20s %04x %04x %04x %c V%02x P%02x", i, + mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].pan); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + EVENT(i, 0, ffh.pbrk[i]).f2t = FX_BREAK; + + for (j = 0; j < 64; j++) { + for(k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + if (hio_read(ev, 1, 3, f) < 3) + return -1; + + fnk_translate_event(event, ev, &ffh); + } + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len <= 2) + continue; + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = 0x80; + + m->volbase = 0xff; + m->quirk = QUIRK_VSALL; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/gal4_load.c b/thirdparty/libxmp/src/loaders/gal4_load.c new file mode 100644 index 000000000..1e11b8056 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/gal4_load.c @@ -0,0 +1,481 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +/* Galaxy Music System 4.0 module file loader + * + * Based on modules converted using mod2j2b.exe + */ + +static int gal4_test(HIO_HANDLE *, char *, const int); +static int gal4_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_gal4 = { + "Galaxy Music System 4.0", + gal4_test, + gal4_load +}; + +static int gal4_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC4('R', 'I', 'F', 'F')) + return -1; + + hio_read32b(f); + + if (hio_read32b(f) != MAGIC4('A', 'M', 'F', 'F')) + return -1; + + if (hio_read32b(f) != MAGIC4('M', 'A', 'I', 'N')) + return -1; + + hio_read32b(f); /* skip size */ + libxmp_read_title(f, t, 64); + + return 0; +} + +struct local_data { + int snum; +}; + +static int get_main(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + char buf[64]; + int flags; + + if (hio_read(buf, 1, 64, f) < 64) + return -1; + strncpy(mod->name, buf, 63); /* ensure string terminator */ + mod->name[63] = '\0'; + libxmp_set_type(m, "Galaxy Music System 4.0"); + + flags = hio_read8(f); + if (~flags & 0x01) + m->period_type = PERIOD_LINEAR; + mod->chn = hio_read8(f); + mod->spd = hio_read8(f); + mod->bpm = hio_read8(f); + hio_read16l(f); /* unknown - 0x01c5 */ + hio_read16l(f); /* unknown - 0xff00 */ + hio_read8(f); /* unknown - 0x80 */ + + /* Sanity check */ + if (mod->chn > 32) { + return -1; + } + + return 0; +} + +static int get_ordr(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + mod->len = hio_read8(f) + 1; + if (hio_error(f)) { + return -1; + } + + for (i = 0; i < mod->len; i++) { + mod->xxo[i] = hio_read8(f); + } + + return 0; +} + +static int get_patt_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + i = hio_read8(f) + 1; /* pattern number */ + + if (i > mod->pat) + mod->pat = i; + + return 0; +} + +static int get_inst_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + hio_read8(f); /* 00 */ + i = hio_read8(f) + 1; /* instrument number */ + + /* Sanity check */ + if (i > MAX_INSTRUMENTS) + return -1; + + if (i > mod->ins) + mod->ins = i; + + hio_seek(f, 28, SEEK_CUR); /* skip name */ + + mod->smp += hio_read8(f); + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event, dummy; + int i, len, chan; + int rows, r; + uint8 flag; + + i = hio_read8(f); /* pattern number */ + len = hio_read32l(f); + + /* Sanity check */ + if (i >= mod->pat || len <= 0 || mod->xxp[i]) { + return -1; + } + + rows = hio_read8(f) + 1; + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + for (r = 0; r < rows; ) { + if ((flag = hio_read8(f)) == 0) { + r++; + continue; + } + if (hio_error(f)) { + return -1; + } + + chan = flag & 0x1f; + + event = chan < mod->chn ? &EVENT(i, chan, r) : &dummy; + + if (flag & 0x80) { + uint8 fxp = hio_read8(f); + uint8 fxt = hio_read8(f); + + switch (fxt) { + case 0x14: /* speed */ + fxt = FX_S3M_SPEED; + break; + default: + if (fxt > 0x0f) { + D_(D_CRIT "p%d r%d c%d unknown effect %02x %02x", i, r, chan, fxt, fxp); + fxt = fxp = 0; + } + } + + event->fxt = fxt; + event->fxp = fxp; + } + + if (flag & 0x40) { + event->ins = hio_read8(f); + event->note = hio_read8(f); + + if (event->note == 128) { + event->note = XMP_KEY_OFF; + } + } + + if (flag & 0x20) { + event->vol = 1 + hio_read8(f) / 2; + } + } + + return 9; +} + +static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j; + int srate, finetune, flags; + int val, vwf, vra, vde, vsw /*, fade*/; + uint8 buf[30]; + + hio_read8(f); /* 00 */ + i = hio_read8(f); /* instrument number */ + + /* Sanity check */ + if (i >= mod->ins || mod->xxi[i].nsm) { + return -1; + } + + hio_read(mod->xxi[i].name, 1, 28, f); + mod->xxi[i].nsm = hio_read8(f); + + for (j = 0; j < 108; j++) { + mod->xxi[i].map[j].ins = hio_read8(f); + } + + hio_seek(f, 11, SEEK_CUR); /* unknown */ + vwf = hio_read8(f); /* vibrato waveform */ + vsw = hio_read8(f); /* vibrato sweep */ + hio_read8(f); /* unknown */ + hio_read8(f); /* unknown */ + vde = hio_read8(f); /* vibrato depth */ + vra = hio_read16l(f) / 16; /* vibrato speed */ + hio_read8(f); /* unknown */ + + val = hio_read8(f); /* PV envelopes flags */ + if (LSN(val) & 0x01) + mod->xxi[i].aei.flg |= XMP_ENVELOPE_ON; + if (LSN(val) & 0x02) + mod->xxi[i].aei.flg |= XMP_ENVELOPE_SUS; + if (LSN(val) & 0x04) + mod->xxi[i].aei.flg |= XMP_ENVELOPE_LOOP; + if (MSN(val) & 0x01) + mod->xxi[i].pei.flg |= XMP_ENVELOPE_ON; + if (MSN(val) & 0x02) + mod->xxi[i].pei.flg |= XMP_ENVELOPE_SUS; + if (MSN(val) & 0x04) + mod->xxi[i].pei.flg |= XMP_ENVELOPE_LOOP; + + val = hio_read8(f); /* PV envelopes points */ + mod->xxi[i].aei.npt = LSN(val) + 1; + mod->xxi[i].pei.npt = MSN(val) + 1; + + val = hio_read8(f); /* PV envelopes sustain point */ + mod->xxi[i].aei.sus = LSN(val); + mod->xxi[i].pei.sus = MSN(val); + + val = hio_read8(f); /* PV envelopes loop start */ + mod->xxi[i].aei.lps = LSN(val); + mod->xxi[i].pei.lps = MSN(val); + + hio_read8(f); /* PV envelopes loop end */ + mod->xxi[i].aei.lpe = LSN(val); + mod->xxi[i].pei.lpe = MSN(val); + + if (mod->xxi[i].aei.npt <= 0 || mod->xxi[i].aei.npt > MIN(10, XMP_MAX_ENV_POINTS)) + mod->xxi[i].aei.flg &= ~XMP_ENVELOPE_ON; + + if (mod->xxi[i].pei.npt <= 0 || mod->xxi[i].pei.npt > MIN(10, XMP_MAX_ENV_POINTS)) + mod->xxi[i].pei.flg &= ~XMP_ENVELOPE_ON; + + if (hio_read(buf, 1, 30, f) < 30) { /* volume envelope points */ + D_(D_CRIT "read error at vol env %d", i); + return -1; + } + for (j = 0; j < mod->xxi[i].aei.npt; j++) { + if (j >= 10) { + break; + } + mod->xxi[i].aei.data[j * 2] = readmem16l(buf + j * 3) / 16; + mod->xxi[i].aei.data[j * 2 + 1] = buf[j * 3 + 2]; + } + + if (hio_read(buf, 1, 30, f) < 30) { /* pan envelope points */ + D_(D_CRIT "read error at pan env %d", i); + return -1; + } + for (j = 0; j < mod->xxi[i].pei.npt; j++) { + if (j >= 10) { + break; + } + mod->xxi[i].pei.data[j * 2] = readmem16l(buf + j * 3) / 16; + mod->xxi[i].pei.data[j * 2 + 1] = buf[j * 3 + 2]; + } + + /*fade =*/ hio_read8(f); /* fadeout - 0x80->0x02 0x310->0x0c */ + hio_read8(f); /* unknown */ + + D_(D_INFO "[%2X] %-28.28s %2d ", i, mod->xxi[i].name, mod->xxi[i].nsm); + + if (mod->xxi[i].nsm == 0) + return 0; + + if (libxmp_alloc_subinstrument(mod, i, mod->xxi[i].nsm) < 0) + return -1; + + for (j = 0; j < mod->xxi[i].nsm; j++, data->snum++) { + hio_read32b(f); /* SAMP */ + hio_read32b(f); /* size */ + + hio_read(mod->xxs[data->snum].name, 1, 28, f); + + mod->xxi[i].sub[j].pan = hio_read8(f) * 4; + if (mod->xxi[i].sub[j].pan == 0) /* not sure about this */ + mod->xxi[i].sub[j].pan = 0x80; + + mod->xxi[i].sub[j].vol = hio_read8(f); + flags = hio_read8(f); + hio_read8(f); /* unknown - 0x80 */ + + mod->xxi[i].sub[j].vwf = vwf; + mod->xxi[i].sub[j].vde = vde; + mod->xxi[i].sub[j].vra = vra; + mod->xxi[i].sub[j].vsw = vsw; + mod->xxi[i].sub[j].sid = data->snum; + + mod->xxs[data->snum].len = hio_read32l(f); + mod->xxs[data->snum].lps = hio_read32l(f); + mod->xxs[data->snum].lpe = hio_read32l(f); + + mod->xxs[data->snum].flg = 0; + if (flags & 0x04) + mod->xxs[data->snum].flg |= XMP_SAMPLE_16BIT; + if (flags & 0x08) + mod->xxs[data->snum].flg |= XMP_SAMPLE_LOOP; + if (flags & 0x10) + mod->xxs[data->snum].flg |= XMP_SAMPLE_LOOP_BIDIR; + /* if (flags & 0x80) + mod->xxs[data->snum].flg |= ? */ + + srate = hio_read32l(f); + finetune = 0; + libxmp_c2spd_to_note(srate, &mod->xxi[i].sub[j].xpo, &mod->xxi[i].sub[j].fin); + mod->xxi[i].sub[j].fin += finetune; + + hio_read32l(f); /* 0x00000000 */ + hio_read32l(f); /* unknown */ + + D_(D_INFO " %X: %05x%c%05x %05x %c V%02x P%02x %5d", + j, mod->xxs[data->snum].len, + mod->xxs[data->snum].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + mod->xxs[data->snum].lps, + mod->xxs[data->snum].lpe, + mod->xxs[data->snum].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : + mod->xxs[data->snum].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[j].vol, + mod->xxi[i].sub[j].pan, + srate); + + if (mod->xxs[data->snum].len > 1) { + int snum = data->snum; + if (libxmp_load_sample(m, f, 0, &mod->xxs[snum], NULL) < 0) + return -1; + } + } + + return 0; +} + +static int gal4_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + int i, ret, offset; + struct local_data data; + + LOAD_INIT(); + + hio_read32b(f); /* Skip RIFF */ + hio_read32b(f); /* Skip size */ + hio_read32b(f); /* Skip AM */ + + offset = hio_tell(f); + + mod->smp = mod->ins = 0; + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + m->c4rate = C4_NTSC_RATE; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "MAIN", get_main); + ret |= libxmp_iff_register(handle, "ORDR", get_ordr); + ret |= libxmp_iff_register(handle, "PATT", get_patt_cnt); + ret |= libxmp_iff_register(handle, "INST", get_inst_cnt); + + if (ret != 0) + return -1; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + libxmp_iff_set_quirk(handle, IFF_CHUNK_TRUNC4); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + mod->trk = mod->pat * mod->chn; + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d\n", mod->pat); + D_(D_INFO "Stored samples : %d ", mod->smp); + + hio_seek(f, start + offset, SEEK_SET); + data.snum = 0; + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "INST", get_inst); + + if (ret != 0) + return -1; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + libxmp_iff_set_quirk(handle, IFF_CHUNK_TRUNC4); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + /* Alloc missing patterns */ + for (i = 0; i < mod->pat; i++) { + if (mod->xxp[i] == NULL) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) { + return -1; + } + } + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = 0x80; + } + + m->quirk |= QUIRKS_FT2; + m->read_event_type = READ_EVENT_FT2; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/gal5_load.c b/thirdparty/libxmp/src/loaders/gal5_load.c new file mode 100644 index 000000000..3b1b6df1b --- /dev/null +++ b/thirdparty/libxmp/src/loaders/gal5_load.c @@ -0,0 +1,404 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +/* Galaxy Music System 5.0 module file loader + * + * Based on the format description by Dr.Eggman + * (http://www.jazz2online.com/J2Ov2/articles/view.php?articleID=288) + * and Jazz Jackrabbit modules by Alexander Brandon from Lori Central + * (http://www.loricentral.com/jj2music.html) + */ + +static int gal5_test(HIO_HANDLE *, char *, const int); +static int gal5_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_gal5 = { + "Galaxy Music System 5.0 (J2B)", + gal5_test, + gal5_load +}; + + +struct local_data { + uint8 chn_pan[64]; +}; + +static int gal5_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC4('R', 'I', 'F', 'F')) + return -1; + + hio_read32b(f); + + if (hio_read32b(f) != MAGIC4('A', 'M', ' ', ' ')) + return -1; + + if (hio_read32b(f) != MAGIC4('I', 'N', 'I', 'T')) + return -1; + + hio_read32b(f); /* skip size */ + libxmp_read_title(f, t, 64); + + return 0; +} + +static int get_init(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + char buf[64]; + int flags; + + if (hio_read(buf, 1, 64, f) < 64) + return -1; + strncpy(mod->name, buf, 63); /* ensure string terminator */ + mod->name[63] = '\0'; + libxmp_set_type(m, "Galaxy Music System 5.0"); + flags = hio_read8(f); /* bit 0: Amiga period */ + if (~flags & 0x01) + m->period_type = PERIOD_LINEAR; + mod->chn = hio_read8(f); + mod->spd = hio_read8(f); + mod->bpm = hio_read8(f); + hio_read16l(f); /* unknown - 0x01c5 */ + hio_read16l(f); /* unknown - 0xff00 */ + hio_read8(f); /* unknown - 0x80 */ + + if (hio_read(data->chn_pan, 1, 64, f) != 64) { + D_(D_CRIT "error reading INIT"); + return -1; + } + + /* Sanity check */ + if (mod->chn > XMP_MAX_CHANNELS) + return -1; + + return 0; +} + +static int get_ordr(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + mod->len = hio_read8(f) + 1; + /* Don't follow Dr.Eggman's specs here */ + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = hio_read8(f); + + return 0; +} + +static int get_patt_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + i = hio_read8(f) + 1; /* pattern number */ + + if (i > mod->pat) + mod->pat = i; + + return 0; +} + +static int get_inst_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + hio_read32b(f); /* 42 01 00 00 */ + hio_read8(f); /* 00 */ + i = hio_read8(f) + 1; /* instrument number */ + + /* Sanity check */ + if (i > MAX_INSTRUMENTS) + return -1; + + if (i > mod->ins) + mod->ins = i; + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event, dummy; + int i, len, chan; + int rows, r; + uint8 flag; + + i = hio_read8(f); /* pattern number */ + len = hio_read32l(f); + + rows = hio_read8(f) + 1; + + /* Sanity check - don't allow duplicate patterns. */ + if (len < 0 || mod->xxp[i] != NULL) + return -1; + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + for (r = 0; r < rows; ) { + if ((flag = hio_read8(f)) == 0) { + r++; + continue; + } + if (hio_error(f)) { + return -1; + } + + chan = flag & 0x1f; + + event = chan < mod->chn ? &EVENT(i, chan, r) : &dummy; + + if (flag & 0x80) { + uint8 fxp = hio_read8(f); + uint8 fxt = hio_read8(f); + + switch (fxt) { + case 0x14: /* speed */ + fxt = FX_S3M_SPEED; + break; + default: + if (fxt > 0x0f) { + D_(D_CRIT "p%d r%d c%d unknown effect %02x %02x", i, r, chan, fxt, fxp); + fxt = fxp = 0; + } + } + + event->fxt = fxt; + event->fxp = fxp; + } + + if (flag & 0x40) { + event->ins = hio_read8(f); + event->note = hio_read8(f); + + if (event->note == 128) { + event->note = XMP_KEY_OFF; + } + } + + if (flag & 0x20) { + event->vol = 1 + hio_read8(f) / 2; + } + } + + return 0; +} + +static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i, srate, finetune, flags; + int has_unsigned_sample; + + hio_read32b(f); /* 42 01 00 00 */ + hio_read8(f); /* 00 */ + i = hio_read8(f); /* instrument number */ + + /* Sanity check - don't allow duplicate instruments. */ + if (mod->xxi[i].nsm != 0) + return -1; + + hio_read(mod->xxi[i].name, 1, 28, f); + hio_seek(f, 290, SEEK_CUR); /* Sample/note map, envelopes */ + mod->xxi[i].nsm = hio_read16l(f); + + D_(D_INFO "[%2X] %-28.28s %2d ", i, mod->xxi[i].name, mod->xxi[i].nsm); + + if (mod->xxi[i].nsm == 0) + return 0; + + if (libxmp_alloc_subinstrument(mod, i, mod->xxi[i].nsm) < 0) + return -1; + + /* FIXME: Currently reading only the first sample */ + + hio_read32b(f); /* RIFF */ + hio_read32b(f); /* size */ + hio_read32b(f); /* AS */ + hio_read32b(f); /* SAMP */ + hio_read32b(f); /* size */ + hio_read32b(f); /* unknown - usually 0x40000000 */ + + hio_read(mod->xxs[i].name, 1, 28, f); + + hio_read32b(f); /* unknown - 0x0000 */ + hio_read8(f); /* unknown - 0x00 */ + + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].vol = hio_read8(f); + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].vol = (hio_read16l(f) + 1) / 512; + flags = hio_read16l(f); + hio_read16l(f); /* unknown - 0x0080 */ + mod->xxs[i].len = hio_read32l(f); + mod->xxs[i].lps = hio_read32l(f); + mod->xxs[i].lpe = hio_read32l(f); + + mod->xxs[i].flg = 0; + has_unsigned_sample = 0; + if (flags & 0x04) + mod->xxs[i].flg |= XMP_SAMPLE_16BIT; + if (flags & 0x08) + mod->xxs[i].flg |= XMP_SAMPLE_LOOP; + if (flags & 0x10) + mod->xxs[i].flg |= XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR; + if (~flags & 0x80) + has_unsigned_sample = 1; + + srate = hio_read32l(f); + finetune = 0; + libxmp_c2spd_to_note(srate, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + mod->xxi[i].sub[0].fin += finetune; + + hio_read32l(f); /* 0x00000000 */ + hio_read32l(f); /* unknown */ + + D_(D_INFO " %x: %05x%c%05x %05x %c V%02x %04x %5d", + 0, mod->xxs[i].len, + mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, flags, srate); + + if (mod->xxs[i].len > 1) { + if (libxmp_load_sample(m, f, has_unsigned_sample ? + SAMPLE_FLAG_UNS : 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} + +static int gal5_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + int i, ret, offset; + struct local_data data; + + LOAD_INIT(); + + hio_read32b(f); /* Skip RIFF */ + hio_read32b(f); /* Skip size */ + hio_read32b(f); /* Skip AM */ + + offset = hio_tell(f); + + mod->smp = mod->ins = 0; + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + m->c4rate = C4_NTSC_RATE; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "INIT", get_init); /* Galaxy 5.0 */ + ret |= libxmp_iff_register(handle, "ORDR", get_ordr); + ret |= libxmp_iff_register(handle, "PATT", get_patt_cnt); + ret |= libxmp_iff_register(handle, "INST", get_inst_cnt); + + if (ret != 0) + return -1; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + libxmp_iff_set_quirk(handle, IFF_SKIP_EMBEDDED); + libxmp_iff_set_quirk(handle, IFF_CHUNK_ALIGN2); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + mod->trk = mod->pat * mod->chn; + mod->smp = mod->ins; + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + D_(D_INFO "Stored samples: %d ", mod->smp); + + hio_seek(f, start + offset, SEEK_SET); + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "INST", get_inst); + + if (ret != 0) + return -1; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + libxmp_iff_set_quirk(handle, IFF_SKIP_EMBEDDED); + libxmp_iff_set_quirk(handle, IFF_CHUNK_ALIGN2); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + /* Alloc missing patterns */ + for (i = 0; i < mod->pat; i++) { + if (mod->xxp[i] == NULL) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) { + return -1; + } + } + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = data.chn_pan[i] * 2; + } + + m->quirk |= QUIRKS_FT2; + m->read_event_type = READ_EVENT_FT2; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/gdm_load.c b/thirdparty/libxmp/src/loaders/gdm_load.c new file mode 100644 index 000000000..5139ef88a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/gdm_load.c @@ -0,0 +1,449 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Based on the GDM (General Digital Music) version 1.0 File Format + * Specification - Revision 2 by MenTaLguY + */ + +#include "loader.h" +#include "../period.h" + +#define MAGIC_GDM MAGIC4('G','D','M',0xfe) +#define MAGIC_GMFS MAGIC4('G','M','F','S') + + +static int gdm_test(HIO_HANDLE *, char *, const int); +static int gdm_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_gdm = { + "General Digital Music", + gdm_test, + gdm_load +}; + +static int gdm_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_GDM) + return -1; + + hio_seek(f, start + 0x47, SEEK_SET); + if (hio_read32b(f) != MAGIC_GMFS) + return -1; + + hio_seek(f, start + 4, SEEK_SET); + libxmp_read_title(f, t, 32); + + return 0; +} + + + +void fix_effect(uint8 *fxt, uint8 *fxp) +{ + int h, l; + switch (*fxt) { + case 0x00: /* no effect */ + *fxp = 0; + break; + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: /* same as protracker */ + break; + case 0x08: + *fxt = FX_TREMOR; + break; + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: /* same as protracker */ + break; + case 0x0e: + /* Convert some extended effects to their S3M equivalents. This is + * necessary because the continue effects were left as the original + * effect (e.g. FX_VOLSLIDE for the fine volume slides) by 2GDM! + * Otherwise, these should be the same as protracker. + */ + h = MSN(*fxp); + l = LSN(*fxp); + switch(h) { + case EX_F_PORTA_UP: + *fxt = FX_PORTA_UP; + *fxp = l | 0xF0; + break; + case EX_F_PORTA_DN: + *fxt = FX_PORTA_DN; + *fxp = l | 0xF0; + break; + case 0x8: /* extra fine portamento up */ + *fxt = FX_PORTA_UP; + *fxp = l | 0xE0; + break; + case 0x9: /* extra fine portamento down */ + *fxt = FX_PORTA_DN; + *fxp = l | 0xE0; + break; + case EX_F_VSLIDE_UP: + /* Don't convert 0 as it would turn into volume slide down... */ + if (l) { + *fxt = FX_VOLSLIDE; + *fxp = (l << 4) | 0xF; + } + break; + case EX_F_VSLIDE_DN: + /* Don't convert 0 as it would turn into volume slide up... */ + if (l) { + *fxt = FX_VOLSLIDE; + *fxp = l | 0xF0; + } + break; + } + break; + case 0x0f: /* set speed */ + *fxt = FX_S3M_SPEED; + break; + case 0x10: /* arpeggio */ + *fxt = FX_S3M_ARPEGGIO; + break; + case 0x11: /* set internal flag */ + *fxt = *fxp = 0; + break; + case 0x12: + *fxt = FX_MULTI_RETRIG; + break; + case 0x13: + *fxt = FX_GLOBALVOL; + break; + case 0x14: + *fxt = FX_FINE_VIBRATO; + break; + case 0x1e: /* special misc */ + switch (MSN(*fxp)) { + case 0x0: /* sample control */ + if (LSN(*fxp) == 1) { /* enable surround */ + /* This is the only sample control effect + * that 2GDM emits. BWSB ignores it, + * but supporting it is harmless. */ + *fxt = FX_SURROUND; + *fxp = 1; + } else { + *fxt = *fxp = 0; + } + break; + case 0x8: /* set pan position */ + *fxt = FX_EXTENDED; + break; + default: + *fxt = *fxp = 0; + break; + } + break; + case 0x1f: + *fxt = FX_S3M_BPM; + break; + default: + *fxt = *fxp = 0; + } +} + + +static int gdm_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + int vermaj, vermin, tvmaj, tvmin, tracker; + int /*origfmt,*/ ord_ofs, pat_ofs, ins_ofs, smp_ofs; + uint8 buffer[32], panmap[32]; + int i; + + LOAD_INIT(); + + hio_read32b(f); /* skip magic */ + hio_read(mod->name, 1, 32, f); + hio_seek(f, 32, SEEK_CUR); /* skip author */ + + hio_seek(f, 7, SEEK_CUR); + + vermaj = hio_read8(f); + vermin = hio_read8(f); + tracker = hio_read16l(f); + tvmaj = hio_read8(f); + tvmin = hio_read8(f); + + if (tracker == 0) { + libxmp_set_type(m, "GDM %d.%02d (2GDM %d.%02d)", + vermaj, vermin, tvmaj, tvmin); + } else { + libxmp_set_type(m, "GDM %d.%02d (unknown tracker %d.%02d)", + vermaj, vermin, tvmaj, tvmin); + } + + if (hio_read(panmap, 32, 1, f) == 0) { + D_(D_CRIT "error reading header"); + return -1; + } + for (i = 0; i < 32; i++) { + if (panmap[i] == 255) { + panmap[i] = 8; + mod->xxc[i].vol = 0; + mod->xxc[i].flg |= XMP_CHANNEL_MUTE; + } else if (panmap[i] == 16) { + panmap[i] = 8; + } + mod->xxc[i].pan = 0x80 + (panmap[i] - 8) * 16; + } + + mod->gvl = hio_read8(f); + mod->spd = hio_read8(f); + mod->bpm = hio_read8(f); + /*origfmt =*/ hio_read16l(f); + ord_ofs = hio_read32l(f); + mod->len = hio_read8(f) + 1; + pat_ofs = hio_read32l(f); + mod->pat = hio_read8(f) + 1; + ins_ofs = hio_read32l(f); + smp_ofs = hio_read32l(f); + mod->ins = mod->smp = hio_read8(f) + 1; + + /* Sanity check */ + if (mod->ins > MAX_INSTRUMENTS) + return -1; + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + hio_seek(f, start + ord_ofs, SEEK_SET); + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = hio_read8(f); + + /* Read instrument data */ + + hio_seek(f, start + ins_ofs, SEEK_SET); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + int flg, c4spd, vol, pan; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + if (hio_read(buffer, 1, 32, f) != 32) + return -1; + + libxmp_instrument_name(mod, i, buffer, 32); + hio_seek(f, 12, SEEK_CUR); /* skip filename */ + hio_read8(f); /* skip EMS handle */ + mod->xxs[i].len = hio_read32l(f); + mod->xxs[i].lps = hio_read32l(f); + mod->xxs[i].lpe = hio_read32l(f); + flg = hio_read8(f); + c4spd = hio_read16l(f); + vol = hio_read8(f); + pan = hio_read8(f); + + mod->xxi[i].sub[0].vol = vol > 0x40 ? 0x40 : vol; + mod->xxi[i].sub[0].pan = pan > 15 ? 0x80 : 0x80 + (pan - 8) * 16; + libxmp_c2spd_to_note(c4spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + + mod->xxi[i].sub[0].sid = i; + mod->xxs[i].flg = 0; + + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + if (flg & 0x01) { + mod->xxs[i].flg |= XMP_SAMPLE_LOOP; + } + if (flg & 0x02) { + mod->xxs[i].flg |= XMP_SAMPLE_16BIT; + mod->xxs[i].len >>= 1; + mod->xxs[i].lps >>= 1; + mod->xxs[i].lpe >>= 1; + } + + D_(D_INFO "[%2X] %-32.32s %05x%c%05x %05x %c V%02x P%02x %5d", + i, mod->xxi[i].name, + mod->xxs[i].len, + mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, + mod->xxi[i].sub[0].pan, + c4spd); + } + + /* Read and convert patterns */ + + hio_seek(f, start + pat_ofs, SEEK_SET); + + /* Effects in muted channels are processed, so scan patterns first to + * see the real number of channels + */ + mod->chn = 0; + for (i = 0; i < mod->pat; i++) { + int len, c, r, k; + + len = hio_read16l(f); + len -= 2; + + for (r = 0; len > 0; ) { + c = hio_read8(f); + if (hio_error(f)) + return -1; + len--; + + if (c == 0) { + r++; + + /* Sanity check */ + if (len == 0) { + if (r > 64) + return -1; + } else { + if (r >= 64) + return -1; + } + + continue; + } + + if (mod->chn <= (c & 0x1f)) + mod->chn = (c & 0x1f) + 1; + + if (c & 0x20) { /* note and sample follows */ + hio_read8(f); + hio_read8(f); + len -= 2; + } + + if (c & 0x40) { /* effect(s) follow */ + do { + k = hio_read8(f); + if (hio_error(f)) + return -1; + len--; + if ((k & 0xc0) != 0xc0) { + hio_read8(f); + len--; + } + } while (k & 0x20); + } + } + } + + mod->trk = mod->pat * mod->chn; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + hio_seek(f, start + pat_ofs, SEEK_SET); + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + int len, c, r, k; + + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + len = hio_read16l(f); + len -= 2; + + for (r = 0; len > 0; ) { + c = hio_read8(f); + if (hio_error(f)) + return -1; + len--; + + if (c == 0) { + r++; + continue; + } + + /* Sanity check */ + if ((c & 0x1f) >= mod->chn || r >= 64) { + return -1; + } + + event = &EVENT(i, c & 0x1f, r); + + if (c & 0x20) { /* note and sample follows */ + k = hio_read8(f); + /* 0 is empty note */ + event->note = k ? 12 + 12 * MSN(k & 0x7f) + LSN(k) : 0; + event->ins = hio_read8(f); + len -= 2; + } + + if (c & 0x40) { /* effect(s) follow */ + do { + k = hio_read8(f); + if (hio_error(f)) + return -1; + len--; + switch ((k & 0xc0) >> 6) { + case 0: + event->fxt = k & 0x1f; + event->fxp = hio_read8(f); + len--; + fix_effect(&event->fxt, &event->fxp); + break; + case 1: + event->f2t = k & 0x1f; + event->f2p = hio_read8(f); + len--; + fix_effect(&event->f2t, &event->f2p); + break; + case 2: + hio_read8(f); + len--; + } + } while (k & 0x20); + } + } + } + + /* Read samples */ + + hio_seek(f, start + smp_ofs, SEEK_SET); + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->quirk |= QUIRK_ARPMEM | QUIRK_FINEFX; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/hmn_load.c b/thirdparty/libxmp/src/loaders/hmn_load.c new file mode 100644 index 000000000..281d3ff0a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/hmn_load.c @@ -0,0 +1,324 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "mod.h" +#include "../period.h" +#include "../hmn_extras.h" + +/* + * From http://www.livet.se/mahoney/: + * + * Most modules from His Master's Noise uses special chip-sounds or + * fine-tuning of samples that never was a part of the standard NoiseTracker + * v2.0 command set. So if you want to listen to them correctly use an Amiga + * emulator and run the demo! DeliPlayer does a good job of playing them + * (there are some occasional error mostly concerning vibrato and portamento + * effects, but I can live with that!), and it can be downloaded from + * http://www.deliplayer.com + */ + +/* + * From http://www.cactus.jawnet.pl/attitude/index.php?action=readtext&issue=12&which=12 + * + * [Bepp] For your final Amiga release, the music disk His Master's Noise, + * you developed a special version of NoiseTracker. Could you tell us a + * little about this project? + * + * [Mahoney] I wanted to make a music disk with loads of songs, without being + * too repetitive or boring. So all of my "experimental features" that did not + * belong to NoiseTracker v2.0 were put into a separate version that would + * feature wavetable sounds, chord calculations, off-line filter calculations, + * mixing, reversing, sample accurate delays, resampling, fades - calculations + * that would be done on a standard setup of sounds instead of on individual + * modules. This "compression technique" lead to some 100 songs fitting on two + * standard 3.5" disks, written by 22 different composers. I'd say that writing + * a music program does give you loads of talented friends - you should try + * that yourself someday! + */ + +/* + * From: Pex Tufvesson + * To: Claudio Matsuoka + * Date: Sat, Jun 1, 2013 at 4:16 AM + * Subject: Re: A question about (very) old stuff + * + * (...) + * If I remember correctly, these chip sounds were done with several short + * waveforms, and an index table that was loopable that would choose which + * waveform to play each frame. And, you didn't have to "draw" every + * waveform in the instrument - you would choose which waveforms to draw + * and the replayer would (at startup) interpolate the waveforms that you + * didn't draw. + * + * In the special noisetracker, you could draw all of these waveforms, draw + * the index table, and the instrument would be stored in one of the + * "patterns" of the song. + */ + +static int hmn_test(HIO_HANDLE *, char *, const int); +static int hmn_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_hmn = { + "His Master's Noise", + hmn_test, + hmn_load +}; + +/* His Master's Noise M&K! will fail in regular Noisetracker loading + * due to invalid finetune values. + */ +#define MAGIC_FEST MAGIC4('F', 'E', 'S', 'T') +#define MAGIC_MK MAGIC4('M', '&', 'K', '!') + +static int hmn_test(HIO_HANDLE * f, char *t, const int start) +{ + int magic; + + hio_seek(f, start + 1080, SEEK_SET); + magic = hio_read32b(f); + + if (magic != MAGIC_FEST && magic != MAGIC_MK) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + +struct mupp { + uint8 prgon; + uint8 pattno; + uint8 dataloopstart; + uint8 dataloopend; +}; + +static int hmn_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct mod_header mh; + struct mupp mupp[31]; + uint8 mod_event[4]; + int mupp_index, num_mupp; + + LOAD_INIT(); + + /* + * clr.b $1c(a6) ;prog on/off + * CMP.L #'Mupp',-$16(a3,d4.l) + * bne.s noprgo + * move.l a0,-(a7) + * move.b #1,$1c(a6) ;prog on + * move.l l697,a0 + * lea $43c(a0),a0 + * moveq #0,d2 + * move.b -$16+$4(a3,d4.l),d2 ;pattno + * mulu #$400,d2 + * lea (a0,d2.l),a0 + * move.l a0,4(a6) ;proginstr data-start + * moveq #0,d2 + * MOVE.B $3C0(A0),$12(A6) + * AND.B #$7F,$12(A6) + * move.b $380(a0),d2 + * mulu #$20,d2 + * lea (a0,d2.w),a0 + * move.l a0,$a(a6) ;loopstartmempoi = startmempoi + * move.B $3(a3,d4.l),$13(a6) ;volume + * move.b -$16+$5(a3,d4.l),8(a6) ;dataloopstart + * move.b -$16+$6(a3,d4.l),9(a6) ;dataloopend + * move.w #$10,$e(a6) ;looplen + * move.l (a7)+,a0 + * MOVE.W $12(A6),(A2) + * AND.W #$FF,(A2) + * BRA.S L505_LQ + */ + + /* + * Wavetable structure is 22 * 32 byte waveforms and 32 byte + * wave control data with looping. + */ + memset(mupp, 0, 31 * sizeof (struct mupp)); + + hio_read(mh.name, 20, 1, f); + num_mupp = 0; + + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); /* Instrument name */ + if (memcmp(mh.ins[i].name, "Mupp", 4) == 0) { + mupp[i].prgon = 1; + mupp[i].pattno = mh.ins[i].name[4]; + mupp[i].dataloopstart = mh.ins[i].name[5]; + mupp[i].dataloopend = mh.ins[i].name[6]; + num_mupp++; + } + + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 128, 1, f); + hio_read(mh.magic, 4, 1, f); + + mod->chn = 4; + mod->ins = 31; + mod->smp = mod->ins + 28 * num_mupp; + mod->len = mh.len; + mod->rst = mh.restart; + memcpy(mod->xxo, mh.order, 128); + + for (i = 0; i < 128; i++) { + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + } + + mod->pat++; + mod->trk = mod->chn * mod->pat; + + if (libxmp_hmn_new_module_extras(m) != 0) + return -1; + + strncpy(mod->name, (char *)mh.name, 20); + libxmp_set_type(m, "%s (%4.4s)", "His Master's Noise", mh.magic); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + if (mupp[i].prgon) { + mod->xxi[i].nsm = 28; + snprintf(mod->xxi[i].name, 32, + "Mupp %02x %02x %02x", mupp[i].pattno, + mupp[i].dataloopstart, mupp[i].dataloopend); + if (libxmp_hmn_new_instrument_extras(&mod->xxi[i]) != 0) + return -1; + } else { + mod->xxi[i].nsm = 1; + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + + mod->xxs[i].len = 2 * mh.ins[i].size; + mod->xxs[i].lps = 2 * mh.ins[i].loop_start; + mod->xxs[i].lpe = mod->xxs[i].lps + + 2 * mh.ins[i].loop_size; + mod->xxs[i].flg = mh.ins[i].loop_size > 1 ? + XMP_SAMPLE_LOOP : 0; + } + + if (libxmp_alloc_subinstrument(mod, i, mod->xxi[i].nsm) < 0) + return -1; + + for (j = 0; j < mod->xxi[i].nsm; j++) { + mod->xxi[i].sub[j].fin = + -(int8)(mh.ins[i].finetune << 3); + mod->xxi[i].sub[j].vol = mh.ins[i].volume; + mod->xxi[i].sub[j].pan = 0x80; + mod->xxi[i].sub[j].sid = i; + } + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, j % 4, j / 4); + if (hio_read(mod_event, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + libxmp_decode_protracker_event(event, mod_event); + + switch (event->fxt) { + case 0x07: + event->fxt = FX_MEGAARP; + break; + case 0x08: + case 0x09: + case 0x0e: + event->fxt = event->fxp = 0; + break; + } + } + } + + /* Noisetracker does not support CIA timing (Glue Master/muppenkorva.mod) */ + m->quirk |= QUIRK_NOBPM; + m->period_type = PERIOD_MODRNG; + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < 31; i++) { + if (libxmp_load_sample(m, f, SAMPLE_FLAG_FULLREP, + &mod->xxs[i], NULL) < 0) { + return -1; + } + } + + + /* Load Mupp samples */ + + mupp_index = 0; + for (i = 0; i < 31; i ++) { + struct hmn_instrument_extras *extra = + (struct hmn_instrument_extras *)mod->xxi[i].extra; + + if (!mupp[i].prgon) + continue; + + hio_seek(f, start + 1084 + 1024 * mupp[i].pattno, SEEK_SET); + for (j = 0; j < 28; j++) { + int k = 31 + 28 * mupp_index + j; + mod->xxi[i].sub[j].sid = k; + mod->xxs[k].len = 32; + mod->xxs[k].lps = 0; + mod->xxs[k].lpe = 32; + mod->xxs[k].flg = XMP_SAMPLE_LOOP; + if (libxmp_load_sample(m, f, 0, &mod->xxs[k], NULL) < 0) + return -1; + } + + extra->dataloopstart = mupp[i].dataloopstart; + extra->dataloopend = mupp[i].dataloopend; + + hio_read(extra->data, 1, 64, f); + hio_read(extra->progvolume, 1, 64, f); + + mupp_index++; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/ice_load.c b/thirdparty/libxmp/src/loaders/ice_load.c new file mode 100644 index 000000000..55e1d767c --- /dev/null +++ b/thirdparty/libxmp/src/loaders/ice_load.c @@ -0,0 +1,206 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Loader for Soundtracker 2.6/Ice Tracker modules */ + +#include "loader.h" + +#define MAGIC_MTN_ MAGIC4('M','T','N',0) +#define MAGIC_IT10 MAGIC4('I','T','1','0') + +static int ice_test(HIO_HANDLE *, char *, const int); +static int ice_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_ice = { + "Soundtracker 2.6/Ice Tracker", + ice_test, + ice_load +}; + +static int ice_test(HIO_HANDLE * f, char *t, const int start) +{ + uint32 magic; + + hio_seek(f, start + 1464, SEEK_SET); + magic = hio_read32b(f); + if (magic != MAGIC_MTN_ && magic != MAGIC_IT10) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 28); + + return 0; +} + +struct ice_ins { + char name[22]; /* Instrument name */ + uint16 len; /* Sample length / 2 */ + uint8 finetune; /* Finetune */ + uint8 volume; /* Volume (0-63) */ + uint16 loop_start; /* Sample loop start in file */ + uint16 loop_size; /* Loop size / 2 */ +}; + +struct ice_header { + char title[20]; + struct ice_ins ins[31]; /* Instruments */ + uint8 len; /* Size of the pattern list */ + uint8 trk; /* Number of tracks */ + uint8 ord[128][4]; + uint32 magic; /* 'MTN\0', 'IT10' */ +}; + +static int ice_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct ice_header ih; + uint8 ev[4]; + + LOAD_INIT(); + + hio_read(ih.title, 20, 1, f); + for (i = 0; i < 31; i++) { + hio_read(ih.ins[i].name, 22, 1, f); + ih.ins[i].len = hio_read16b(f); + ih.ins[i].finetune = hio_read8(f); + ih.ins[i].volume = hio_read8(f); + ih.ins[i].loop_start = hio_read16b(f); + ih.ins[i].loop_size = hio_read16b(f); + } + ih.len = hio_read8(f); + ih.trk = hio_read8(f); + hio_read(ih.ord, 128 * 4, 1, f); + ih.magic = hio_read32b(f); + + /* Sanity check */ + if (ih.len > 128) { + return -1; + } + for (i = 0; i < ih.len; i++) { + for (j = 0; j < 4; j++) { + if (ih.ord[i][j] >= ih.trk) + return -1; + } + } + + if (ih.magic == MAGIC_IT10) + libxmp_set_type(m, "Ice Tracker"); + else if (ih.magic == MAGIC_MTN_) + libxmp_set_type(m, "Soundtracker 2.6"); + else + return -1; + + mod->ins = 31; + mod->smp = mod->ins; + mod->pat = ih.len; + mod->len = ih.len; + mod->trk = ih.trk; + + strncpy(mod->name, (char *)ih.title, 20); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi; + struct xmp_sample *xxs; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + xxi = &mod->xxi[i]; + xxs = &mod->xxs[i]; + + xxs->len = 2 * ih.ins[i].len; + xxs->lps = 2 * ih.ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * ih.ins[i].loop_size; + xxs->flg = ih.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + xxi->sub[0].vol = ih.ins[i].volume; + /* xxi->sub[0].fin = (int8)(ih.ins[i].finetune << 4); */ + xxi->sub[0].pan = 0x80; + xxi->sub[0].sid = i; + + if (xxs->len > 0) + xxi->nsm = 1; + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c %02x %01x", + i, ih.ins[i].name, xxs->len, xxs->lps, + xxs->lpe, xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + xxi->sub[0].vol, xxi->sub[0].fin >> 4); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + mod->xxp[i]->rows = 64; + + for (j = 0; j < mod->chn; j++) { + mod->xxp[i]->index[j] = ih.ord[i][j]; + } + mod->xxo[i] = i; + } + + D_(D_INFO "Stored tracks: %d", mod->trk); + + for (i = 0; i < mod->trk; i++) { + if (libxmp_alloc_track(mod, i, 64) < 0) + return -1; + + for (j = 0; j < mod->xxt[i]->rows; j++) { + event = &mod->xxt[i]->event[j]; + if (hio_read(ev, 1, 4, f) < 4) { + D_(D_CRIT "read error at track %d", i); + return -1; + } + libxmp_decode_protracker_event(event, ev); + + if (event->fxt == FX_SPEED) { + if (MSN(event->fxp) && LSN(event->fxp)) { + event->fxt = FX_ICE_SPEED; + } + } + } + } + + m->period_type = PERIOD_MODRNG; + + /* Read samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len <= 4) + continue; + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/iff.c b/thirdparty/libxmp/src/loaders/iff.c new file mode 100644 index 000000000..0214b872d --- /dev/null +++ b/thirdparty/libxmp/src/loaders/iff.c @@ -0,0 +1,215 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "../common.h" +#include "../list.h" +#include "iff.h" + +#include "loader.h" + +struct iff_data { + struct list_head iff_list; + unsigned id_size; + unsigned flags; +}; + +static int iff_process(iff_handle opaque, struct module_data *m, char *id, long size, + HIO_HANDLE *f, void *parm) +{ + struct iff_data *data = (struct iff_data *)opaque; + struct list_head *tmp; + struct iff_info *i; + int pos; + + pos = hio_tell(f); + + list_for_each(tmp, &data->iff_list) { + i = list_entry(tmp, struct iff_info, list); + if (id && !memcmp(id, i->id, data->id_size)) { + D_(D_WARN "Load IFF chunk %s (%ld) @%d", id, size, pos); + if (size > IFF_MAX_CHUNK_SIZE) { + return -1; + } + if (i->loader(m, size, f, parm) < 0) { + return -1; + } + break; + } + } + + if (hio_seek(f, pos + size, SEEK_SET) < 0) { + return -1; + } + + return 0; +} + +static int iff_chunk(iff_handle opaque, struct module_data *m, HIO_HANDLE *f, void *parm) +{ + struct iff_data *data = (struct iff_data *)opaque; + unsigned size; + char id[17] = ""; + + D_(D_INFO "chunk id size: %d", data->id_size); + if (hio_read(id, 1, data->id_size, f) != data->id_size) { + (void)hio_error(f); /* clear error flag */ + return 1; + } + D_(D_INFO "chunk id: [%s]", id); + + if (data->flags & IFF_SKIP_EMBEDDED) { + /* embedded RIFF hack */ + if (!strncmp(id, "RIFF", 4)) { + hio_read32b(f); + hio_read32b(f); + /* read first chunk ID instead */ + if (hio_read(id, 1, data->id_size, f) != data->id_size){ + return 1; + } + } + } + + if (data->flags & IFF_LITTLE_ENDIAN) { + size = hio_read32l(f); + } else { + size = hio_read32b(f); + } + D_(D_INFO "size: %d", size); + + if (hio_error(f)) { + return -1; + } + + if (data->flags & IFF_CHUNK_ALIGN2) { + /* Sanity check */ + if (size > 0xfffffffe) { + return -1; + } + size = (size + 1) & ~1; + } + + if (data->flags & IFF_CHUNK_ALIGN4) { + /* Sanity check */ + if (size > 0xfffffffc) { + return -1; + } + size = (size + 3) & ~3; + } + + /* PT 3.6 hack: this does not seem to ever apply to "PTDT". + * This broke several modules (city lights.pt36, acid phase.pt36) */ + if ((data->flags & IFF_FULL_CHUNK_SIZE) && memcmp(id, "PTDT", 4)) { + if (size < data->id_size + 4) + return -1; + size -= data->id_size + 4; + } + + return iff_process(opaque, m, id, size, f, parm); +} + +iff_handle libxmp_iff_new() +{ + struct iff_data *data; + + data = (struct iff_data *) malloc(sizeof(struct iff_data)); + if (data == NULL) { + return NULL; + } + + INIT_LIST_HEAD(&data->iff_list); + data->id_size = 4; + data->flags = 0; + + return (iff_handle)data; +} + +int libxmp_iff_load(iff_handle opaque, struct module_data *m, HIO_HANDLE *f, void *parm) +{ + int ret; + + while (!hio_eof(f)) { + ret = iff_chunk(opaque, m, f, parm); + if (ret > 0) + break; + if (ret < 0) + return -1; + } + + return 0; +} + +int libxmp_iff_register(iff_handle opaque, const char *id, + int (*loader)(struct module_data *, int, HIO_HANDLE *, void *)) +{ + struct iff_data *data = (struct iff_data *)opaque; + struct iff_info *f; + int i = 0; + + f = (struct iff_info *) malloc(sizeof(struct iff_info)); + if (f == NULL) + return -1; + + /* Note: previously was an strncpy */ + for (; i < 4 && id && id[i]; i++) + f->id[i] = id[i]; + for (; i < 4; i++) + f->id[i] = '\0'; + + f->loader = loader; + + list_add_tail(&f->list, &data->iff_list); + + return 0; +} + +void libxmp_iff_release(iff_handle opaque) +{ + struct iff_data *data = (struct iff_data *)opaque; + struct list_head *tmp; + struct iff_info *i; + + /* can't use list_for_each, we free the node before incrementing */ + for (tmp = (&data->iff_list)->next; tmp != (&data->iff_list);) { + i = list_entry(tmp, struct iff_info, list); + list_del(&i->list); + tmp = tmp->next; + free(i); + } + + free(data); +} + +/* Functions to tune IFF mutations */ + +void libxmp_iff_id_size(iff_handle opaque, int n) +{ + struct iff_data *data = (struct iff_data *)opaque; + + data->id_size = n; +} + +void libxmp_iff_set_quirk(iff_handle opaque, int i) +{ + struct iff_data *data = (struct iff_data *)opaque; + + data->flags |= i; +} diff --git a/thirdparty/libxmp/src/loaders/iff.h b/thirdparty/libxmp/src/loaders/iff.h new file mode 100644 index 000000000..df4c807b0 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/iff.h @@ -0,0 +1,43 @@ +#ifndef LIBXMP_IFF_H +#define LIBXMP_IFF_H + +#include "../hio.h" +#include "../list.h" + +#define IFF_NOBUFFER 0x0001 + +#define IFF_LITTLE_ENDIAN 0x01 +#define IFF_FULL_CHUNK_SIZE 0x02 +#define IFF_CHUNK_ALIGN2 0x04 +#define IFF_CHUNK_ALIGN4 0x08 +#define IFF_SKIP_EMBEDDED 0x10 +#define IFF_CHUNK_TRUNC4 0x20 + +#define IFF_MAX_CHUNK_SIZE 0x800000 + +typedef void *iff_handle; + +struct iff_header { + char form[4]; /* FORM */ + int len; /* File length */ + char id[4]; /* IFF type identifier */ +}; + +struct iff_info { + char id[4]; + int (*loader)(struct module_data *, int, HIO_HANDLE *, void *); + struct list_head list; +}; + +iff_handle libxmp_iff_new(void); +int libxmp_iff_load(iff_handle, struct module_data *, HIO_HANDLE *, void *); +/* int libxmp_iff_chunk(iff_handle, struct module_data *, HIO_HANDLE *, void *); */ +int libxmp_iff_register(iff_handle, const char *, + int (*loader)(struct module_data *, int, HIO_HANDLE *, void *)); +void libxmp_iff_id_size(iff_handle, int); +void libxmp_iff_set_quirk(iff_handle, int); +void libxmp_iff_release(iff_handle); +/* int libxmp_iff_process(iff_handle, struct module_data *, char *, long, + HIO_HANDLE *, void *); */ + +#endif /* LIBXMP_IFF_H */ diff --git a/thirdparty/libxmp/src/loaders/imf_load.c b/thirdparty/libxmp/src/loaders/imf_load.c new file mode 100644 index 000000000..a692dd689 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/imf_load.c @@ -0,0 +1,541 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Loader for Imago Orpheus modules based on the format description + * written by Lutz Roeder. + */ + +#include "loader.h" +#include "../period.h" + + +#define IMF_EOR 0x00 +#define IMF_CH_MASK 0x1f +#define IMF_NI_FOLLOW 0x20 +#define IMF_FX_FOLLOWS 0x80 +#define IMF_F2_FOLLOWS 0x40 + +struct imf_channel { + char name[12]; /* Channelname (ASCIIZ-String, max 11 chars) */ + uint8 status; /* Channel status */ + uint8 pan; /* Pan positions */ + uint8 chorus; /* Default chorus */ + uint8 reverb; /* Default reverb */ +}; + +struct imf_header { + char name[32]; /* Songname (ASCIIZ-String, max. 31 chars) */ + uint16 len; /* Number of orders saved */ + uint16 pat; /* Number of patterns saved */ + uint16 ins; /* Number of instruments saved */ + uint16 flg; /* Module flags */ + uint8 unused1[8]; + uint8 tpo; /* Default tempo (1..255) */ + uint8 bpm; /* Default beats per minute (BPM) (32..255) */ + uint8 vol; /* Default mastervolume (0..64) */ + uint8 amp; /* Amplification factor (4..127) */ + uint8 unused2[8]; + uint32 magic; /* 'IM10' */ + struct imf_channel chn[32]; /* Channel settings */ + uint8 pos[256]; /* Order list */ +}; + +struct imf_env { + uint8 npt; /* Number of envelope points */ + uint8 sus; /* Envelope sustain point */ + uint8 lps; /* Envelope loop start point */ + uint8 lpe; /* Envelope loop end point */ + uint8 flg; /* Envelope flags */ + uint8 unused[3]; +}; + +struct imf_instrument { + char name[32]; /* Inst. name (ASCIIZ-String, max. 31 chars) */ + uint8 map[120]; /* Multisample settings */ + uint8 unused[8]; + uint16 vol_env[32]; /* Volume envelope settings */ + uint16 pan_env[32]; /* Pan envelope settings */ + uint16 pitch_env[32]; /* Pitch envelope settings */ + struct imf_env env[3]; + uint16 fadeout; /* Fadeout rate (0...0FFFH) */ + uint16 nsm; /* Number of samples in instrument */ + uint32 magic; /* 'II10' */ +}; + +struct imf_sample { + char name[13]; /* Sample filename (12345678.ABC) */ + uint8 unused1[3]; + uint32 len; /* Length */ + uint32 lps; /* Loop start */ + uint32 lpe; /* Loop end */ + uint32 rate; /* Samplerate */ + uint8 vol; /* Default volume (0..64) */ + uint8 pan; /* Default pan (00h = Left / 80h = Middle) */ + uint8 unused2[14]; + uint8 flg; /* Sample flags */ + uint8 unused3[5]; + uint16 ems; /* Reserved for internal usage */ + uint32 dram; /* Reserved for internal usage */ + uint32 magic; /* 'IS10' */ +}; + + +#define MAGIC_IM10 MAGIC4('I','M','1','0') +#define MAGIC_II10 MAGIC4('I','I','1','0') + +static int imf_test (HIO_HANDLE *, char *, const int); +static int imf_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_imf = { + "Imago Orpheus v1.0", + imf_test, + imf_load +}; + +static int imf_test(HIO_HANDLE *f, char *t, const int start) +{ + hio_seek(f, start + 60, SEEK_SET); + if (hio_read32b(f) != MAGIC_IM10) + return -1; + + hio_seek(f, start, SEEK_SET); + libxmp_read_title(f, t, 32); + + return 0; +} + +#define NONE 0xff +#define FX_IMF_FPORTA_UP 0xfe +#define FX_IMF_FPORTA_DN 0xfd + + +/* Effect conversion table */ +static const uint8 fx[36] = { + NONE, + FX_S3M_SPEED, + FX_S3M_BPM, + FX_TONEPORTA, + FX_TONE_VSLIDE, + FX_VIBRATO, + FX_VIBRA_VSLIDE, + FX_FINE_VIBRATO, + FX_TREMOLO, + FX_S3M_ARPEGGIO, + FX_SETPAN, + FX_PANSLIDE, + FX_VOLSET, + FX_VOLSLIDE, + FX_F_VSLIDE, + FX_FINETUNE, + FX_NSLIDE_UP, + FX_NSLIDE_DN, + FX_PORTA_UP, + FX_PORTA_DN, + FX_IMF_FPORTA_UP, + FX_IMF_FPORTA_DN, + FX_FLT_CUTOFF, + FX_FLT_RESN, + FX_OFFSET, + NONE /* fine offset */, + FX_KEYOFF, + FX_MULTI_RETRIG, + FX_TREMOR, + FX_JUMP, + FX_BREAK, + FX_GLOBALVOL, + FX_GVOL_SLIDE, + FX_EXTENDED, + FX_CHORUS, + FX_REVERB +}; + + +/* Effect translation */ +static void xlat_fx (int c, uint8 *fxt, uint8 *fxp) +{ + uint8 h = MSN (*fxp), l = LSN (*fxp); + + if (*fxt >= ARRAY_SIZE(fx)) { + D_(D_WARN "invalid effect %#02x", *fxt); + *fxt = *fxp = 0; + return; + } + + switch (*fxt = fx[*fxt]) { + case FX_IMF_FPORTA_UP: + *fxt = FX_PORTA_UP; + if (*fxp < 0x30) + *fxp = LSN (*fxp >> 2) | 0xe0; + else + *fxp = LSN (*fxp >> 4) | 0xf0; + break; + case FX_IMF_FPORTA_DN: + *fxt = FX_PORTA_DN; + if (*fxp < 0x30) + *fxp = LSN (*fxp >> 2) | 0xe0; + else + *fxp = LSN (*fxp >> 4) | 0xf0; + break; + case FX_EXTENDED: /* Extended effects */ + switch (h) { + case 0x1: /* Set filter */ + case 0x2: /* Undefined */ + case 0x4: /* Undefined */ + case 0x6: /* Undefined */ + case 0x7: /* Undefined */ + case 0x9: /* Undefined */ + case 0xe: /* Ignore envelope */ + case 0xf: /* Invert loop */ + *fxp = *fxt = 0; + break; + case 0x3: /* Glissando */ + *fxp = l | (EX_GLISS << 4); + break; + case 0x5: /* Vibrato waveform */ + *fxp = l | (EX_VIBRATO_WF << 4); + break; + case 0x8: /* Tremolo waveform */ + *fxp = l | (EX_TREMOLO_WF << 4); + break; + case 0xa: /* Pattern loop */ + *fxp = l | (EX_PATTERN_LOOP << 4); + break; + case 0xb: /* Pattern delay */ + *fxp = l | (EX_PATT_DELAY << 4); + break; + case 0xc: + if (l == 0) + *fxt = *fxp = 0; + } + break; + case NONE: /* No effect */ + *fxt = *fxp = 0; + break; + } +} + + +static int imf_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, r, i, j; + struct xmp_event *event = 0, dummy; + struct imf_header ih; + struct imf_instrument ii; + struct imf_sample is; + int pat_len, smp_num; + uint8 n, b; + + LOAD_INIT(); + + /* Load and convert header */ + hio_read(ih.name, 32, 1, f); + ih.len = hio_read16l(f); + ih.pat = hio_read16l(f); + ih.ins = hio_read16l(f); + ih.flg = hio_read16l(f); + hio_read(ih.unused1, 8, 1, f); + ih.tpo = hio_read8(f); + ih.bpm = hio_read8(f); + ih.vol = hio_read8(f); + ih.amp = hio_read8(f); + hio_read(ih.unused2, 8, 1, f); + ih.magic = hio_read32b(f); + + /* Sanity check */ + if (ih.len > 256 || ih.pat > 256 || ih.ins > 255) { + return -1; + } + + for (i = 0; i < 32; i++) { + hio_read(ih.chn[i].name, 12, 1, f); + ih.chn[i].chorus = hio_read8(f); + ih.chn[i].reverb = hio_read8(f); + ih.chn[i].pan = hio_read8(f); + ih.chn[i].status = hio_read8(f); + } + + if (hio_read(ih.pos, 256, 1, f) < 1) { + D_(D_CRIT "read error at order list"); + return -1; + } + + if (ih.magic != MAGIC_IM10) { + return -1; + } + + libxmp_copy_adjust(mod->name, (uint8 *)ih.name, 32); + + mod->len = ih.len; + mod->ins = ih.ins; + mod->smp = 1024; + mod->pat = ih.pat; + + if (ih.flg & 0x01) + m->period_type = PERIOD_LINEAR; + + mod->spd = ih.tpo; + mod->bpm = ih.bpm; + + libxmp_set_type(m, "Imago Orpheus 1.0 IMF"); + + MODULE_INFO(); + + mod->chn = 0; + for (i = 0; i < 32; i++) { + /* 0=enabled; 1=muted, but still processed; 2=disabled.*/ + if (ih.chn[i].status >= 2) + continue; + + mod->chn = i + 1; + mod->xxc[i].pan = ih.chn[i].pan; +#if 0 + /* FIXME */ + mod->xxc[i].cho = ih.chn[i].chorus; + mod->xxc[i].rvb = ih.chn[i].reverb; + mod->xxc[i].flg |= XMP_CHANNEL_FX; +#endif + } + + mod->trk = mod->pat * mod->chn; + + memcpy(mod->xxo, ih.pos, mod->len); + for (i = 0; i < mod->len; i++) { + if (mod->xxo[i] == 0xff) + mod->xxo[i]--; + } + + m->c4rate = C4_NTSC_RATE; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read patterns */ + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + int rows; + + pat_len = hio_read16l(f) - 4; + + rows = hio_read16l(f); + + /* Sanity check */ + if (rows > 256) { + return -1; + } + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + r = 0; + + while (--pat_len >= 0) { + b = hio_read8(f); + + if (b == IMF_EOR) { + r++; + continue; + } + + /* Sanity check */ + if (r >= rows) { + return -1; + } + + c = b & IMF_CH_MASK; + event = c >= mod->chn ? &dummy : &EVENT(i, c, r); + + if (b & IMF_NI_FOLLOW) { + n = hio_read8(f); + switch (n) { + case 255: + case 160: /* ?! */ + n = XMP_KEY_OFF; + break; /* Key off */ + default: + n = 13 + 12 * MSN (n) + LSN (n); + } + + event->note = n; + event->ins = hio_read8(f); + pat_len -= 2; + } + if (b & IMF_FX_FOLLOWS) { + event->fxt = hio_read8(f); + event->fxp = hio_read8(f); + xlat_fx(c, &event->fxt, &event->fxp); + pat_len -= 2; + } + if (b & IMF_F2_FOLLOWS) { + event->f2t = hio_read8(f); + event->f2p = hio_read8(f); + xlat_fx(c, &event->f2t, &event->f2p); + pat_len -= 2; + } + } + } + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments and samples */ + + D_(D_INFO "Instruments: %d", mod->ins); + + for (smp_num = i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + + hio_read(ii.name, 32, 1, f); + ii.name[31] = 0; + hio_read(ii.map, 120, 1, f); + hio_read(ii.unused, 8, 1, f); + for (j = 0; j < 32; j++) + ii.vol_env[j] = hio_read16l(f); + for (j = 0; j < 32; j++) + ii.pan_env[j] = hio_read16l(f); + for (j = 0; j < 32; j++) + ii.pitch_env[j] = hio_read16l(f); + for (j = 0; j < 3; j++) { + ii.env[j].npt = hio_read8(f); + ii.env[j].sus = hio_read8(f); + ii.env[j].lps = hio_read8(f); + ii.env[j].lpe = hio_read8(f); + ii.env[j].flg = hio_read8(f); + hio_read(ii.env[j].unused, 3, 1, f); + } + ii.fadeout = hio_read16l(f); + ii.nsm = hio_read16l(f); + ii.magic = hio_read32b(f); + + /* Sanity check */ + if (ii.nsm > 255) + return -1; + + if (ii.magic != MAGIC_II10) + return -2; + + xxi->nsm = ii.nsm; + + if (xxi->nsm > 0) { + if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0) + return -1; + } + + strncpy((char *)xxi->name, ii.name, 31); + xxi->name[31] = '\0'; + + for (j = 0; j < 108; j++) { + xxi->map[j + 12].ins = ii.map[j]; + } + + D_(D_INFO "[%2X] %-31.31s %2d %4x %c", i, ii.name, ii.nsm, + ii.fadeout, ii.env[0].flg & 0x01 ? 'V' : '-'); + + xxi->aei.npt = ii.env[0].npt; + xxi->aei.sus = ii.env[0].sus; + xxi->aei.lps = ii.env[0].lps; + xxi->aei.lpe = ii.env[0].lpe; + xxi->aei.flg = ii.env[0].flg & 0x01 ? XMP_ENVELOPE_ON : 0; + xxi->aei.flg |= ii.env[0].flg & 0x02 ? XMP_ENVELOPE_SUS : 0; + xxi->aei.flg |= ii.env[0].flg & 0x04 ? XMP_ENVELOPE_LOOP : 0; + + /* Sanity check */ + if (xxi->aei.npt > 16) { + return -1; + } + + for (j = 0; j < xxi->aei.npt; j++) { + xxi->aei.data[j * 2] = ii.vol_env[j * 2]; + xxi->aei.data[j * 2 + 1] = ii.vol_env[j * 2 + 1]; + } + + for (j = 0; j < ii.nsm; j++, smp_num++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs = &mod->xxs[smp_num]; + int sid; + + hio_read(is.name, 13, 1, f); + hio_read(is.unused1, 3, 1, f); + is.len = hio_read32l(f); + is.lps = hio_read32l(f); + is.lpe = hio_read32l(f); + is.rate = hio_read32l(f); + is.vol = hio_read8(f); + is.pan = hio_read8(f); + hio_read(is.unused2, 14, 1, f); + is.flg = hio_read8(f); + hio_read(is.unused3, 5, 1, f); + is.ems = hio_read16l(f); + is.dram = hio_read32l(f); + is.magic = hio_read32b(f); + + /* Sanity check */ + if (is.len > 0x100000 || is.lps > 0x100000 || is.lpe > 0x100000) + return -1; + + sub->sid = smp_num; + sub->vol = is.vol; + sub->pan = is.pan; + xxs->len = is.len; + xxs->lps = is.lps; + xxs->lpe = is.lpe; + xxs->flg = is.flg & 1 ? XMP_SAMPLE_LOOP : 0; + + if (is.flg & 4) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + D_(D_INFO " %02x: %05x %05x %05x %5d", + j, is.len, is.lps, is.lpe, is.rate); + + libxmp_c2spd_to_note(is.rate, &sub->xpo, &sub->fin); + + if (xxs->len <= 0) + continue; + + sid = sub->sid; + if (libxmp_load_sample(m, f, 0, &mod->xxs[sid], NULL) < 0) + return -1; + } + } + + mod->smp = smp_num; + mod->xxs = (struct xmp_sample *) realloc(mod->xxs, sizeof(struct xmp_sample) * mod->smp); + if (mod->xxs == NULL) { + return -1; + } + m->xtra = (struct extra_sample_data *) realloc(m->xtra, sizeof(struct extra_sample_data) * mod->smp); + if (m->xtra == NULL) { + return -1; + } + + m->c4rate = C4_NTSC_RATE; + m->quirk |= QUIRK_FILTER | QUIRKS_ST3 | QUIRK_ARPMEM; + m->read_event_type = READ_EVENT_ST3; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/ims_load.c b/thirdparty/libxmp/src/loaders/ims_load.c new file mode 100644 index 000000000..1942eb012 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/ims_load.c @@ -0,0 +1,293 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Loader for Images Music System modules based on the EP replayer. + * + * Date: Thu, 19 Apr 2001 19:13:06 +0200 + * From: Michael Doering + * + * I just "stumbled" upon something about the Unic.3C format when I was + * testing replayers for the upcoming UADE 0.21 that might be also + * interesting to you for xmp. The "Beastbusters" tune is not a UNIC file :) + * It's actually a different Format, although obviously related, called + * "Images Music System". + * + * I was testing the replayer from the Wanted Team with one of their test + * tunes, among them also the beastbuster music. When I first listened to + * it, I knew I have heard it somewhere, a bit different but it was alike. + * This one had more/richer percussions and there was no strange beep in + * the bg. ;) After some searching on my HD I found it among the xmp test + * tunes as a UNIC file. + */ + +#include "loader.h" +#include "../period.h" + +struct ims_instrument { + uint8 name[20]; + int16 finetune; /* Causes squeaks in beast-busters1! */ + uint16 size; + uint8 unknown; + uint8 volume; + uint16 loop_start; + uint16 loop_size; +}; + +struct ims_header { + uint8 title[20]; + struct ims_instrument ins[31]; + uint8 len; + uint8 zero; + uint8 orders[128]; + uint8 magic[4]; +}; + + +static int ims_test (HIO_HANDLE *, char *, const int); +static int ims_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_ims = { + "Images Music System", + ims_test, + ims_load +}; + +static int ims_test(HIO_HANDLE *f, char *t, const int start) +{ + int i; + int smp_size, pat; + struct ims_header ih; + + smp_size = 0; + + hio_read(ih.title, 20, 1, f); + + for (i = 0; i < 31; i++) { + if (hio_read(ih.ins[i].name, 1, 20, f) < 20) + return -1; + + ih.ins[i].finetune = (int16)hio_read16b(f); + ih.ins[i].size = hio_read16b(f); + ih.ins[i].unknown = hio_read8(f); + ih.ins[i].volume = hio_read8(f); + ih.ins[i].loop_start = hio_read16b(f); + ih.ins[i].loop_size = hio_read16b(f); + + smp_size += ih.ins[i].size * 2; + + if (libxmp_test_name(ih.ins[i].name, 20, 0) < 0) + return -1; + + if (ih.ins[i].volume > 0x40) + return -1; + + if (ih.ins[i].size > 0x8000) + return -1; + + if (ih.ins[i].loop_start > ih.ins[i].size) + return -1; + + if (ih.ins[i].size && ih.ins[i].loop_size > 2 * ih.ins[i].size) + return -1; + } + + if (smp_size < 8) + return -1; + + ih.len = hio_read8(f); + ih.zero = hio_read8(f); + hio_read(ih.orders, 128, 1, f); + if (hio_read(ih.magic, 4, 1, f) == 0) + return -1; + + if (ih.zero > 1) /* not sure what this is */ + return -1; + + if (ih.magic[3] != 0x3c) + return -1; + + if (ih.len > 0x7f) + return -1; + + for (pat = i = 0; i < ih.len; i++) + if (ih.orders[i] > pat) + pat = ih.orders[i]; + pat++; + + if (pat > 0x7f || ih.len == 0 || ih.len > 0x7f) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + + +static int ims_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct ims_header ih; + uint8 ims_event[3]; + int xpo = 21; /* Tuned against UADE */ + + LOAD_INIT(); + + mod->chn = 4; + mod->ins = 31; + mod->smp = mod->ins; + + hio_read (ih.title, 20, 1, f); + + for (i = 0; i < 31; i++) { + hio_read (ih.ins[i].name, 20, 1, f); + ih.ins[i].finetune = (int16)hio_read16b(f); + ih.ins[i].size = hio_read16b(f); + ih.ins[i].unknown = hio_read8(f); + ih.ins[i].volume = hio_read8(f); + ih.ins[i].loop_start = hio_read16b(f); + ih.ins[i].loop_size = hio_read16b(f); + } + + ih.len = hio_read8(f); + if (ih.len > 128) { + return -1; + } + ih.zero = hio_read8(f); + hio_read (ih.orders, 128, 1, f); + hio_read (ih.magic, 4, 1, f); + + mod->len = ih.len; + memcpy (mod->xxo, ih.orders, mod->len); + + for (i = 0; i < mod->len; i++) + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + + mod->pat++; + mod->trk = mod->chn * mod->pat; + + strncpy(mod->name, (char *)ih.title, 20); + libxmp_set_type(m, "Images Music System"); + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + xxi = &mod->xxi[i]; + sub = &xxi->sub[0]; + xxs = &mod->xxs[i]; + + xxs->len = 2 * ih.ins[i].size; + xxs->lps = 2 * ih.ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * ih.ins[i].loop_size; + xxs->flg = ih.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + sub->fin = 0; /* ih.ins[i].finetune; */ + sub->vol = ih.ins[i].volume; + sub->pan = 0x80; + sub->sid = i; + //mod->xxi[i].rls = 0xfff; + + if (xxs->len > 0) { + xxi->nsm = 1; + } + + libxmp_instrument_name(mod, i, ih.ins[i].name, 20); + + D_(D_INFO "[%2X] %-20.20s %04x %04x %04x %c V%02x %+d", + i, xxi->name, xxs->len, xxs->lps, xxs->lpe, + ih.ins[i].loop_size > 1 ? 'L' : ' ', sub->vol, sub->fin >> 4); + } + + if (libxmp_init_pattern(mod) < 0) { + return -1; + } + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < 0x100; j++) { + event = &EVENT (i, j & 0x3, j >> 2); + hio_read(ims_event, 1, 3, f); + + /* Event format: + * + * 0000 0000 0000 0000 0000 0000 + * |\ / \ / \ / \ / + * | note ins fx parameter + * ins + * + * 0x3f is a blank note. + */ + event->note = ims_event[0] & 0x3f; + if (event->note != 0x00 && event->note != 0x3f) + event->note += xpo + 12; + else + event->note = 0; + event->ins = ((ims_event[0] & 0x40) >> 2) | MSN(ims_event[1]); + event->fxt = LSN(ims_event[1]); + event->fxp = ims_event[2]; + + libxmp_disable_continue_fx (event); + + /* According to Asle: + * ``Just note that pattern break effect command (D**) uses + * HEX value in UNIC format (while it is DEC values in PTK). + * Thus, it has to be converted!'' + * + * Is this valid for IMS as well? --claudio + */ + if (event->fxt == 0x0d) + event->fxp = (event->fxp / 10) << 4 | (event->fxp % 10); + } + } + + m->period_type = PERIOD_MODRNG; + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (!mod->xxs[i].len) + continue; + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/it.h b/thirdparty/libxmp/src/loaders/it.h new file mode 100644 index 000000000..6df35b414 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/it.h @@ -0,0 +1,196 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LIBXMP_LOADERS_IT_H +#define LIBXMP_LOADERS_IT_H + +#include "loader.h" + +/* IT flags */ +#define IT_STEREO 0x01 +#define IT_VOL_OPT 0x02 /* Not recognized */ +#define IT_USE_INST 0x04 +#define IT_LINEAR_FREQ 0x08 +#define IT_OLD_FX 0x10 +#define IT_LINK_GXX 0x20 +#define IT_MIDI_WHEEL 0x40 +#define IT_MIDI_CONFIG 0x80 + +/* IT special */ +#define IT_HAS_MSG 0x01 +#define IT_EDIT_HISTORY 0x02 +#define IT_HIGHLIGHTS 0x04 +#define IT_SPEC_MIDICFG 0x08 + +/* IT instrument flags */ +#define IT_INST_SAMPLE 0x01 +#define IT_INST_16BIT 0x02 +#define IT_INST_STEREO 0x04 +#define IT_INST_LOOP 0x10 +#define IT_INST_SLOOP 0x20 +#define IT_INST_BLOOP 0x40 +#define IT_INST_BSLOOP 0x80 + +/* IT sample flags */ +#define IT_SMP_SAMPLE 0x01 +#define IT_SMP_16BIT 0x02 +#define IT_SMP_STEREO 0x04 /* unsupported */ +#define IT_SMP_COMP 0x08 /* unsupported */ +#define IT_SMP_LOOP 0x10 +#define IT_SMP_SLOOP 0x20 +#define IT_SMP_BLOOP 0x40 +#define IT_SMP_BSLOOP 0x80 + +/* IT sample conversion flags */ +#define IT_CVT_SIGNED 0x01 +#define IT_CVT_BIGEND 0x02 /* 'safe to ignore' according to ittech.txt */ +#define IT_CVT_DIFF 0x04 /* Compressed sample flag */ +#define IT_CVT_BYTEDIFF 0x08 /* 'safe to ignore' according to ittech.txt */ +#define IT_CVT_12BIT 0x10 /* 'safe to ignore' according to ittech.txt */ +#define IT_CVT_ADPCM 0xff /* Special: always indicates Modplug ADPCM4 */ + +/* IT envelope flags */ +#define IT_ENV_ON 0x01 +#define IT_ENV_LOOP 0x02 +#define IT_ENV_SLOOP 0x04 +#define IT_ENV_CARRY 0x08 +#define IT_ENV_FILTER 0x80 + + +struct it_file_header { + uint32 magic; /* 'IMPM' */ + uint8 name[26]; /* ASCIIZ Song name */ + uint8 hilite_min; /* Pattern editor highlight */ + uint8 hilite_maj; /* Pattern editor highlight */ + uint16 ordnum; /* Number of orders (must be even) */ + uint16 insnum; /* Number of instruments */ + uint16 smpnum; /* Number of samples */ + uint16 patnum; /* Number of patterns */ + uint16 cwt; /* Tracker ID and version */ + uint16 cmwt; /* Format version */ + uint16 flags; /* Flags */ + uint16 special; /* More flags */ + uint8 gv; /* Global volume */ + uint8 mv; /* Master volume */ + uint8 is; /* Initial speed */ + uint8 it; /* Initial tempo */ + uint8 sep; /* Panning separation */ + uint8 pwd; /* Pitch wheel depth */ + uint16 msglen; /* Message length */ + uint32 msgofs; /* Message offset */ + uint32 rsvd; /* Reserved */ + uint8 chpan[64]; /* Channel pan settings */ + uint8 chvol[64]; /* Channel volume settings */ +}; + +struct it_instrument1_header { + uint32 magic; /* 'IMPI' */ + uint8 dosname[12]; /* DOS filename */ + uint8 zero; /* Always zero */ + uint8 flags; /* Instrument flags */ + uint8 vls; /* Volume loop start */ + uint8 vle; /* Volume loop end */ + uint8 sls; /* Sustain loop start */ + uint8 sle; /* Sustain loop end */ + uint16 rsvd1; /* Reserved */ + uint16 fadeout; /* Fadeout (release) */ + uint8 nna; /* New note action */ + uint8 dnc; /* Duplicate note check */ + uint16 trkvers; /* Tracker version */ + uint8 nos; /* Number of samples */ + uint8 rsvd2; /* Reserved */ + uint8 name[26]; /* ASCIIZ Instrument name */ + uint8 rsvd3[6]; /* Reserved */ + uint8 keys[240]; + uint8 epoint[200]; + uint8 enode[50]; +}; + +struct it_instrument2_header { + uint32 magic; /* 'IMPI' */ + uint8 dosname[12]; /* DOS filename */ + uint8 zero; /* Always zero */ + uint8 nna; /* New Note Action */ + uint8 dct; /* Duplicate Check Type */ + uint8 dca; /* Duplicate Check Action */ + uint16 fadeout; + uint8 pps; /* Pitch-Pan Separation */ + uint8 ppc; /* Pitch-Pan Center */ + uint8 gbv; /* Global Volume */ + uint8 dfp; /* Default pan */ + uint8 rv; /* Random volume variation */ + uint8 rp; /* Random pan variation */ + uint16 trkvers; /* Not used: tracked version */ + uint8 nos; /* Not used: number of samples */ + uint8 rsvd1; /* Reserved */ + uint8 name[26]; /* ASCIIZ Instrument name */ + uint8 ifc; /* Initial filter cutoff */ + uint8 ifr; /* Initial filter resonance */ + uint8 mch; /* MIDI channel */ + uint8 mpr; /* MIDI program */ + uint16 mbnk; /* MIDI bank */ + uint8 keys[240]; +}; + +struct it_envelope_node { + int8 y; + uint16 x; +}; + +struct it_envelope { + uint8 flg; /* Flags */ + uint8 num; /* Number of node points */ + uint8 lpb; /* Loop beginning */ + uint8 lpe; /* Loop end */ + uint8 slb; /* Sustain loop beginning */ + uint8 sle; /* Sustain loop end */ + struct it_envelope_node node[25]; + uint8 unused; +}; + +struct it_sample_header { + uint32 magic; /* 'IMPS' */ + uint8 dosname[12]; /* DOS filename */ + uint8 zero; /* Always zero */ + uint8 gvl; /* Global volume for instrument */ + uint8 flags; /* Sample flags */ + uint8 vol; /* Volume */ + uint8 name[26]; /* ASCIIZ sample name */ + uint8 convert; /* Sample flags */ + uint8 dfp; /* Default pan */ + uint32 length; /* Length */ + uint32 loopbeg; /* Loop begin */ + uint32 loopend; /* Loop end */ + uint32 c5spd; /* C 5 speed */ + uint32 sloopbeg; /* SusLoop begin */ + uint32 sloopend; /* SusLoop end */ + uint32 sample_ptr; /* Sample pointer */ + uint8 vis; /* Vibrato speed */ + uint8 vid; /* Vibrato depth */ + uint8 vir; /* Vibrato rate */ + uint8 vit; /* Vibrato waveform */ +}; + +int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215); +int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215); + +#endif /* LIBXMP_LOADERS_IT_H */ diff --git a/thirdparty/libxmp/src/loaders/it_load.c b/thirdparty/libxmp/src/loaders/it_load.c new file mode 100644 index 000000000..f3cc8c0a3 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/it_load.c @@ -0,0 +1,1465 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LIBXMP_CORE_DISABLE_IT + +#include "loader.h" +#include "it.h" +#include "../period.h" + +#define MAGIC_IMPM MAGIC4('I','M','P','M') +#define MAGIC_IMPI MAGIC4('I','M','P','I') +#define MAGIC_IMPS MAGIC4('I','M','P','S') + +static int it_test(HIO_HANDLE *, char *, const int); +static int it_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_it = { + "Impulse Tracker", + it_test, + it_load +}; + +static int it_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_IMPM) + return -1; + + libxmp_read_title(f, t, 26); + + return 0; +} + + +#define FX_NONE 0xff +#define FX_XTND 0xfe +#define L_CHANNELS 64 + +static const uint8 fx[32] = { + /* */ FX_NONE, + /* A */ FX_S3M_SPEED, + /* B */ FX_JUMP, + /* C */ FX_IT_BREAK, + /* D */ FX_VOLSLIDE, + /* E */ FX_PORTA_DN, + /* F */ FX_PORTA_UP, + /* G */ FX_TONEPORTA, + /* H */ FX_VIBRATO, + /* I */ FX_TREMOR, + /* J */ FX_S3M_ARPEGGIO, + /* K */ FX_VIBRA_VSLIDE, + /* L */ FX_TONE_VSLIDE, + /* M */ FX_TRK_VOL, + /* N */ FX_TRK_VSLIDE, + /* O */ FX_OFFSET, + /* P */ FX_IT_PANSLIDE, + /* Q */ FX_MULTI_RETRIG, + /* R */ FX_TREMOLO, + /* S */ FX_XTND, + /* T */ FX_IT_BPM, + /* U */ FX_FINE_VIBRATO, + /* V */ FX_GLOBALVOL, + /* W */ FX_GVOL_SLIDE, + /* X */ FX_SETPAN, + /* Y */ FX_PANBRELLO, + /* Z */ FX_MACRO, + /* ? */ FX_NONE, + /* / */ FX_MACROSMOOTH, + /* ? */ FX_NONE, + /* ? */ FX_NONE, + /* ? */ FX_NONE +}; + +static void xlat_fx(int c, struct xmp_event *e, uint8 *last_fxp, int new_fx) +{ + uint8 h = MSN(e->fxp), l = LSN(e->fxp); + + switch (e->fxt = fx[e->fxt]) { + case FX_XTND: /* Extended effect */ + e->fxt = FX_EXTENDED; + + if (h == 0 && e->fxp == 0) { + e->fxp = last_fxp[c]; + h = MSN(e->fxp); + l = LSN(e->fxp); + } else { + last_fxp[c] = e->fxp; + } + + switch (h) { + case 0x1: /* Glissando */ + e->fxp = 0x30 | l; + break; + case 0x2: /* Finetune -- not supported */ + e->fxt = e->fxp = 0; + break; + case 0x3: /* Vibrato wave */ + e->fxp = 0x40 | l; + break; + case 0x4: /* Tremolo wave */ + e->fxp = 0x70 | l; + break; + case 0x5: /* Panbrello wave */ + if (l <= 3) { + e->fxt = FX_PANBRELLO_WF; + e->fxp = l; + } else { + e->fxt = e->fxp = 0; + } + break; + case 0x6: /* Pattern delay */ + e->fxp = 0xe0 | l; + break; + case 0x7: /* Instrument functions */ + e->fxt = FX_IT_INSTFUNC; + e->fxp &= 0x0f; + break; + case 0x8: /* Set pan position */ + e->fxt = FX_SETPAN; + e->fxp = l << 4; + break; + case 0x9: + if (l == 0 || l == 1) { + /* 0x91 = set surround */ + e->fxt = FX_SURROUND; + e->fxp = l; + } else if (l == 0xe || l == 0xf) { + /* 0x9f Play reverse (MPT) */ + e->fxt = FX_REVERSE; + e->fxp = l - 0xe; + } + break; + case 0xa: /* High offset */ + e->fxt = FX_HIOFFSET; + e->fxp = l; + break; + case 0xb: /* Pattern loop */ + e->fxp = 0x60 | l; + break; + case 0xc: /* Note cut */ + case 0xd: /* Note delay */ + if ((e->fxp = l) == 0) + e->fxp++; /* SD0 and SC0 become SD1 and SC1 */ + e->fxp |= h << 4; + break; + case 0xe: /* Pattern row delay */ + e->fxt = FX_IT_ROWDELAY; + e->fxp = l; + break; + case 0xf: /* Set parametered macro */ + e->fxt = FX_MACRO_SET; + e->fxp = l; + break; + default: + e->fxt = e->fxp = 0; + } + break; + case FX_TREMOR: + if (!new_fx && e->fxp != 0) { + e->fxp = ((MSN(e->fxp) + 1) << 4) | (LSN(e->fxp) + 1); + } + break; + case FX_GLOBALVOL: + if (e->fxp > 0x80) { /* See storlek test 16 */ + e->fxt = e->fxp = 0; + } + break; + case FX_NONE: /* No effect */ + e->fxt = e->fxp = 0; + break; + } +} + + +static void xlat_volfx(struct xmp_event *event) +{ + int b; + + b = event->vol; + event->vol = 0; + + if (b <= 0x40) { + event->vol = b + 1; + } else if (b >= 65 && b <= 74) { /* A */ + event->f2t = FX_F_VSLIDE_UP_2; + event->f2p = b - 65; + } else if (b >= 75 && b <= 84) { /* B */ + event->f2t = FX_F_VSLIDE_DN_2; + event->f2p = b - 75; + } else if (b >= 85 && b <= 94) { /* C */ + event->f2t = FX_VSLIDE_UP_2; + event->f2p = b - 85; + } else if (b >= 95 && b <= 104) { /* D */ + event->f2t = FX_VSLIDE_DN_2; + event->f2p = b - 95; + } else if (b >= 105 && b <= 114) { /* E */ + event->f2t = FX_PORTA_DN; + event->f2p = (b - 105) << 2; + } else if (b >= 115 && b <= 124) { /* F */ + event->f2t = FX_PORTA_UP; + event->f2p = (b - 115) << 2; + } else if (b >= 128 && b <= 192) { /* pan */ + if (b == 192) { + event->f2p = 0xff; + } else { + event->f2p = (b - 128) << 2; + } + event->f2t = FX_SETPAN; + } else if (b >= 193 && b <= 202) { /* G */ + uint8 val[10] = { + 0x00, 0x01, 0x04, 0x08, 0x10, + 0x20, 0x40, 0x60, 0x80, 0xff + }; + event->f2t = FX_TONEPORTA; + event->f2p = val[b - 193]; + } else if (b >= 203 && b <= 212) { /* H */ + event->f2t = FX_VIBRATO; + event->f2p = b - 203; + } +} + + +static void fix_name(uint8 *s, int l) +{ + int i; + + /* IT names can have 0 at start of data, replace with space */ + for (l--, i = 0; i < l; i++) { + if (s[i] == 0) + s[i] = ' '; + } + for (i--; i >= 0 && s[i] == ' '; i--) { + if (s[i] == ' ') + s[i] = 0; + } +} + + +static int load_it_midi_config(struct module_data *m, HIO_HANDLE *f) +{ + int i; + + m->midi = (struct midi_macro_data *) calloc(1, sizeof(struct midi_macro_data)); + if (m->midi == NULL) + return -1; + + /* Skip global MIDI macros */ + if (hio_seek(f, 9 * 32, SEEK_CUR) < 0) + return -1; + + /* SFx macros */ + for (i = 0; i < 16; i++) { + if (hio_read(m->midi->param[i].data, 1, 32, f) < 32) + return -1; + m->midi->param[i].data[31] = '\0'; + } + /* Zxx macros */ + for (i = 0; i < 128; i++) { + if (hio_read(m->midi->fixed[i].data, 1, 32, f) < 32) + return -1; + m->midi->fixed[i].data[31] = '\0'; + } + return 0; +} + + +static int read_envelope(struct xmp_envelope *ei, struct it_envelope *env, + HIO_HANDLE *f) +{ + int i; + uint8 buf[82]; + + if (hio_read(buf, 1, 82, f) != 82) { + return -1; + } + + env->flg = buf[0]; + env->num = MIN(buf[1], 25); /* Clamp to IT max */ + + env->lpb = buf[2]; + env->lpe = buf[3]; + env->slb = buf[4]; + env->sle = buf[5]; + + for (i = 0; i < 25; i++) { + env->node[i].y = buf[6 + i * 3]; + env->node[i].x = readmem16l(buf + 7 + i * 3); + } + + ei->flg = env->flg & IT_ENV_ON ? XMP_ENVELOPE_ON : 0; + + if (env->flg & IT_ENV_LOOP) { + ei->flg |= XMP_ENVELOPE_LOOP; + } + + if (env->flg & IT_ENV_SLOOP) { + ei->flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_SLOOP; + } + + if (env->flg & IT_ENV_CARRY) { + ei->flg |= XMP_ENVELOPE_CARRY; + } + + ei->npt = env->num; + ei->sus = env->slb; + ei->sue = env->sle; + ei->lps = env->lpb; + ei->lpe = env->lpe; + + if (ei->npt > 0 && ei->npt <= 25 /* XMP_MAX_ENV_POINTS */) { + for (i = 0; i < ei->npt; i++) { + ei->data[i * 2] = env->node[i].x; + ei->data[i * 2 + 1] = env->node[i].y; + } + } else { + ei->flg &= ~XMP_ENVELOPE_ON; + } + + return 0; +} + +static void identify_tracker(struct module_data *m, struct it_file_header *ifh, + int pat_before_smp, int *is_mpt_116) +{ +#ifndef LIBXMP_CORE_PLAYER + char tracker_name[40]; + int sample_mode = ~ifh->flags & IT_USE_INST; + + switch (ifh->cwt >> 8) { + case 0x00: + strcpy(tracker_name, "unmo3"); + break; + case 0x01: + case 0x02: /* test from Schism Tracker sources */ + if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0214 + && ifh->flags == 9 && ifh->special == 0 + && ifh->hilite_maj == 0 && ifh->hilite_min == 0 + && ifh->insnum == 0 && ifh->patnum + 1 == ifh->ordnum + && ifh->gv == 128 && ifh->mv == 100 && ifh->is == 1 + && ifh->sep == 128 && ifh->pwd == 0 + && ifh->msglen == 0 && ifh->msgofs == 0 && ifh->rsvd == 0) { + strcpy(tracker_name, "OpenSPC conversion"); + } else if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0217) { + strcpy(tracker_name, "ModPlug Tracker 1.16"); + /* ModPlug Tracker files aren't really IMPM 2.00 */ + ifh->cmwt = sample_mode ? 0x100 : 0x214; + *is_mpt_116 = 1; + } else if (ifh->cmwt == 0x0200 && ifh->cwt == 0x0202 && pat_before_smp) { + /* ModPlug Tracker ITs from pre-alpha 4 use tracker + * 0x0202 and format 0x0200. Unfortunately, ITs from + * Impulse Tracker may *also* use this. These MPT ITs + * can be detected because they write patterns before + * samples/instruments. */ + strcpy(tracker_name, "ModPlug Tracker 1.0 pre-alpha"); + ifh->cmwt = sample_mode ? 0x100 : 0x200; + *is_mpt_116 = 1; + } else if (ifh->cwt == 0x0216) { + strcpy(tracker_name, "Impulse Tracker 2.14v3"); + } else if (ifh->cwt == 0x0217) { + strcpy(tracker_name, "Impulse Tracker 2.14v5"); + } else if (ifh->cwt == 0x0214 && !memcmp(&ifh->rsvd, "CHBI", 4)) { + strcpy(tracker_name, "Chibi Tracker"); + } else { + snprintf(tracker_name, 40, "Impulse Tracker %d.%02x", + (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff); + } + break; + case 0x08: + case 0x7f: + if (ifh->cwt == 0x0888) { + strcpy(tracker_name, "OpenMPT 1.17"); + *is_mpt_116 = 1; + } else if (ifh->cwt == 0x7fff) { + strcpy(tracker_name, "munch.py"); + } else { + snprintf(tracker_name, 40, "unknown (%04x)", ifh->cwt); + } + break; + default: + switch (ifh->cwt >> 12) { + case 0x1: + libxmp_schism_tracker_string(tracker_name, 40, + (ifh->cwt & 0x0fff), ifh->rsvd); + break; + case 0x5: + snprintf(tracker_name, 40, "OpenMPT %d.%02x", + (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff); + if (memcmp(&ifh->rsvd, "OMPT", 4)) + strncat(tracker_name, " (compat.)", 39); + break; + case 0x06: + snprintf(tracker_name, 40, "BeRoTracker %d.%02x", + (ifh->cwt & 0x0f00) >> 8, ifh->cwt & 0xff); + break; + default: + snprintf(tracker_name, 40, "unknown (%04x)", ifh->cwt); + } + } + + libxmp_set_type(m, "%s IT %d.%02x", tracker_name, ifh->cmwt >> 8, + ifh->cmwt & 0xff); +#else + libxmp_set_type(m, "Impulse Tracker"); +#endif +} + +static int load_old_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f) +{ + int inst_map[120], inst_rmap[XMP_MAX_KEYS]; + struct it_instrument1_header i1h; + int c, k, j; + uint8 buf[64]; + + if (hio_read(buf, 1, 64, f) != 64) { + return -1; + } + + i1h.magic = readmem32b(buf); + if (i1h.magic != MAGIC_IMPI) { + D_(D_CRIT "bad instrument magic"); + return -1; + } + memcpy(i1h.dosname, buf + 4, 12); + i1h.zero = buf[16]; + i1h.flags = buf[17]; + i1h.vls = buf[18]; + i1h.vle = buf[19]; + i1h.sls = buf[20]; + i1h.sle = buf[21]; + i1h.fadeout = readmem16l(buf + 24); + i1h.nna = buf[26]; + i1h.dnc = buf[27]; + i1h.trkvers = readmem16l(buf + 28); + i1h.nos = buf[30]; + + memcpy(i1h.name, buf + 32, 26); + fix_name(i1h.name, 26); + + if (hio_read(i1h.keys, 1, 240, f) != 240) { + return -1; + } + if (hio_read(i1h.epoint, 1, 200, f) != 200) { + return -1; + } + if (hio_read(i1h.enode, 1, 50, f) != 50) { + return -1; + } + + libxmp_copy_adjust(xxi->name, i1h.name, 25); + + xxi->rls = i1h.fadeout << 7; + + xxi->aei.flg = 0; + if (i1h.flags & IT_ENV_ON) { + xxi->aei.flg |= XMP_ENVELOPE_ON; + } + if (i1h.flags & IT_ENV_LOOP) { + xxi->aei.flg |= XMP_ENVELOPE_LOOP; + } + if (i1h.flags & IT_ENV_SLOOP) { + xxi->aei.flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_SLOOP; + } + if (i1h.flags & IT_ENV_CARRY) { + xxi->aei.flg |= XMP_ENVELOPE_SUS | XMP_ENVELOPE_CARRY; + } + xxi->aei.lps = i1h.vls; + xxi->aei.lpe = i1h.vle; + xxi->aei.sus = i1h.sls; + xxi->aei.sue = i1h.sle; + + for (k = 0; k < 25 && i1h.enode[k * 2] != 0xff; k++) ; + + /* Sanity check */ + if (k >= 25 || i1h.enode[k * 2] != 0xff) { + return -1; + } + + for (xxi->aei.npt = k; k--;) { + xxi->aei.data[k * 2] = i1h.enode[k * 2]; + xxi->aei.data[k * 2 + 1] = i1h.enode[k * 2 + 1]; + } + + /* See how many different instruments we have */ + for (j = 0; j < 120; j++) + inst_map[j] = -1; + + for (k = j = 0; j < XMP_MAX_KEYS; j++) { + c = j < 120 ? i1h.keys[j * 2 + 1] - 1 : -1; + if (c < 0 || c >= 120) { + xxi->map[j].ins = 0; + xxi->map[j].xpo = 0; + continue; + } + if (inst_map[c] == -1) { + inst_map[c] = k; + inst_rmap[k] = c; + k++; + } + xxi->map[j].ins = inst_map[c]; + xxi->map[j].xpo = i1h.keys[j * 2] - j; + } + + xxi->nsm = k; + xxi->vol = 0x40; + + if (k) { + xxi->sub = (struct xmp_subinstrument *) calloc(k, sizeof(struct xmp_subinstrument)); + if (xxi->sub == NULL) { + return -1; + } + + for (j = 0; j < k; j++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + + sub->sid = inst_rmap[j]; + sub->nna = i1h.nna; + sub->dct = + i1h.dnc ? XMP_INST_DCT_NOTE : XMP_INST_DCT_OFF; + sub->dca = XMP_INST_DCA_CUT; + sub->pan = -1; + } + } + + D_(D_INFO "[ ] %-26.26s %d %-4.4s %4d %2d %c%c%c %3d", + /*i,*/ i1h.name, + i1h.nna, + i1h.dnc ? "on" : "off", + i1h.fadeout, + xxi->aei.npt, + xxi->aei.flg & XMP_ENVELOPE_ON ? 'V' : '-', + xxi->aei.flg & XMP_ENVELOPE_LOOP ? 'L' : '-', + xxi->aei.flg & XMP_ENVELOPE_SUS ? 'S' : '-', xxi->nsm); + + return 0; +} + +static int load_new_it_instrument(struct xmp_instrument *xxi, HIO_HANDLE *f) +{ + int inst_map[120], inst_rmap[XMP_MAX_KEYS]; + struct it_instrument2_header i2h; + struct it_envelope env; + int dca2nna[] = { 0, 2, 3, 3 /* Northern Sky (cj-north.it) has this... */ }; + int c, k, j; + uint8 buf[64]; + + if (hio_read(buf, 1, 64, f) != 64) { + return -1; + } + + i2h.magic = readmem32b(buf); + if (i2h.magic != MAGIC_IMPI) { + D_(D_CRIT "bad instrument magic"); + return -1; + } + memcpy(i2h.dosname, buf + 4, 12); + i2h.zero = buf[16]; + i2h.nna = buf[17]; + i2h.dct = buf[18]; + i2h.dca = buf[19]; + + /* Sanity check */ + if (i2h.dca > 3) { + /* Northern Sky has an instrument with DCA 3 */ + D_(D_WARN "bad instrument dca: %d", i2h.dca); + i2h.dca = 0; + } + + i2h.fadeout = readmem16l(buf + 20); + i2h.pps = buf[22]; + i2h.ppc = buf[23]; + i2h.gbv = buf[24]; + i2h.dfp = buf[25]; + i2h.rv = buf[26]; + i2h.rp = buf[27]; + i2h.trkvers = readmem16l(buf + 28); + i2h.nos = buf[30]; + + memcpy(i2h.name, buf + 32, 26); + fix_name(i2h.name, 26); + + i2h.ifc = buf[58]; + i2h.ifr = buf[59]; + i2h.mch = buf[60]; + i2h.mpr = buf[61]; + i2h.mbnk = readmem16l(buf + 62); + + if (hio_read(i2h.keys, 1, 240, f) != 240) { + D_(D_CRIT "key map read error"); + return -1; + } + + libxmp_copy_adjust(xxi->name, i2h.name, 25); + xxi->rls = i2h.fadeout << 6; + + /* Envelopes */ + + if (read_envelope(&xxi->aei, &env, f) < 0) { + return -1; + } + if (read_envelope(&xxi->pei, &env, f) < 0) { + return -1; + } + if (read_envelope(&xxi->fei, &env, f) < 0) { + return -1; + } + + if (xxi->pei.flg & XMP_ENVELOPE_ON) { + for (j = 0; j < xxi->pei.npt; j++) + xxi->pei.data[j * 2 + 1] += 32; + } + + if (xxi->aei.flg & XMP_ENVELOPE_ON && xxi->aei.npt == 0) { + xxi->aei.npt = 1; + } + if (xxi->pei.flg & XMP_ENVELOPE_ON && xxi->pei.npt == 0) { + xxi->pei.npt = 1; + } + if (xxi->fei.flg & XMP_ENVELOPE_ON && xxi->fei.npt == 0) { + xxi->fei.npt = 1; + } + + if (env.flg & IT_ENV_FILTER) { + xxi->fei.flg |= XMP_ENVELOPE_FLT; + for (j = 0; j < env.num; j++) { + xxi->fei.data[j * 2 + 1] += 32; + xxi->fei.data[j * 2 + 1] *= 4; + } + } else { + /* Pitch envelope is *50 to get fine interpolation */ + for (j = 0; j < env.num; j++) + xxi->fei.data[j * 2 + 1] *= 50; + } + + /* See how many different instruments we have */ + for (j = 0; j < 120; j++) + inst_map[j] = -1; + + for (k = j = 0; j < 120; j++) { + c = i2h.keys[j * 2 + 1] - 1; + if (c < 0 || c >= 120) { + xxi->map[j].ins = 0xff; /* No sample */ + xxi->map[j].xpo = 0; + continue; + } + if (inst_map[c] == -1) { + inst_map[c] = k; + inst_rmap[k] = c; + k++; + } + xxi->map[j].ins = inst_map[c]; + xxi->map[j].xpo = i2h.keys[j * 2] - j; + } + + xxi->nsm = k; + xxi->vol = i2h.gbv >> 1; + + if (k) { + xxi->sub = (struct xmp_subinstrument *) calloc(k, sizeof(struct xmp_subinstrument)); + if (xxi->sub == NULL) + return -1; + + for (j = 0; j < k; j++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + + sub->sid = inst_rmap[j]; + sub->nna = i2h.nna; + sub->dct = i2h.dct; + sub->dca = dca2nna[i2h.dca]; + sub->pan = i2h.dfp & 0x80 ? -1 : i2h.dfp * 4; + sub->ifc = i2h.ifc; + sub->ifr = i2h.ifr; + sub->rvv = ((int)i2h.rp << 8) | i2h.rv; + } + } + + D_(D_INFO "[ ] %-26.26s %d %d %d %4d %4d %2x " + "%02x %c%c%c %3d %02x %02x", + /*i,*/ i2h.name, + i2h.nna, i2h.dct, i2h.dca, + i2h.fadeout, + i2h.gbv, + i2h.dfp & 0x80 ? 0x80 : i2h.dfp * 4, + i2h.rv, + xxi->aei.flg & XMP_ENVELOPE_ON ? 'V' : '-', + xxi->pei.flg & XMP_ENVELOPE_ON ? 'P' : '-', + env.flg & 0x01 ? env.flg & 0x80 ? 'F' : 'P' : '-', + xxi->nsm, i2h.ifc, i2h.ifr); + + return 0; +} + +static void force_sample_length(struct xmp_sample *xxs, struct extra_sample_data *xtra, int len) +{ + xxs->len = len; + + if (xxs->lpe > xxs->len) + xxs->lpe = xxs->len; + + if (xxs->lps >= xxs->len) + xxs->flg &= ~XMP_SAMPLE_LOOP; + + if (xtra) { + if (xtra->sue > xxs->len) + xtra->sue = xxs->len; + + if(xtra->sus >= xxs->len) + xxs->flg &= ~(XMP_SAMPLE_SLOOP | XMP_SAMPLE_SLOOP_BIDIR); + } +} + +static int load_it_sample(struct module_data *m, int i, int start, + int sample_mode, HIO_HANDLE *f) +{ + struct it_sample_header ish; + struct xmp_module *mod = &m->mod; + struct extra_sample_data *xtra; + struct xmp_sample *xxs; + int j, k; + uint8 buf[80]; + + if (sample_mode) { + mod->xxi[i].sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument)); + if (mod->xxi[i].sub == NULL) { + return -1; + } + } + + if (hio_read(buf, 1, 80, f) != 80) { + return -1; + } + + ish.magic = readmem32b(buf); + /* Changed to continue to allow use-brdg.it and use-funk.it to + * load correctly (both IT 2.04) + */ + if (ish.magic != MAGIC_IMPS) { + return 0; + } + + xxs = &mod->xxs[i]; + xtra = &m->xtra[i]; + + memcpy(ish.dosname, buf + 4, 12); + ish.zero = buf[16]; + ish.gvl = buf[17]; + ish.flags = buf[18]; + ish.vol = buf[19]; + + memcpy(ish.name, buf + 20, 26); + fix_name(ish.name, 26); + + ish.convert = buf[46]; + ish.dfp = buf[47]; + ish.length = readmem32l(buf + 48); + ish.loopbeg = readmem32l(buf + 52); + ish.loopend = readmem32l(buf + 56); + ish.c5spd = readmem32l(buf + 60); + ish.sloopbeg = readmem32l(buf + 64); + ish.sloopend = readmem32l(buf + 68); + ish.sample_ptr = readmem32l(buf + 72); + ish.vis = buf[76]; + ish.vid = buf[77]; + ish.vir = buf[78]; + ish.vit = buf[79]; + + if (ish.flags & IT_SMP_16BIT) { + xxs->flg = XMP_SAMPLE_16BIT; + } + xxs->len = ish.length; + + xxs->lps = ish.loopbeg; + xxs->lpe = ish.loopend; + xxs->flg |= ish.flags & IT_SMP_LOOP ? XMP_SAMPLE_LOOP : 0; + xxs->flg |= ish.flags & IT_SMP_BLOOP ? XMP_SAMPLE_LOOP_BIDIR : 0; + xxs->flg |= ish.flags & IT_SMP_SLOOP ? XMP_SAMPLE_SLOOP : 0; + xxs->flg |= ish.flags & IT_SMP_BSLOOP ? XMP_SAMPLE_SLOOP_BIDIR : 0; + + if (ish.flags & IT_SMP_SLOOP) { + xtra->sus = ish.sloopbeg; + xtra->sue = ish.sloopend; + } + + if (sample_mode) { + /* Create an instrument for each sample */ + mod->xxi[i].vol = 64; + mod->xxi[i].sub[0].vol = ish.vol; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].nsm = !!(xxs->len); + libxmp_instrument_name(mod, i, ish.name, 25); + } else { + libxmp_copy_adjust(xxs->name, ish.name, 25); + } + + D_(D_INFO "\n[%2X] %-26.26s %05x%c%05x %05x %05x %05x " + "%02x%02x %02x%02x %5d ", + i, sample_mode ? xxs->name : mod->xxs[i].name, + xxs->len, + ish.flags & IT_SMP_16BIT ? '+' : ' ', + MIN(xxs->lps, 0xfffff), MIN(xxs->lpe, 0xfffff), + MIN(ish.sloopbeg, 0xfffff), MIN(ish.sloopend, 0xfffff), + ish.flags, ish.convert, ish.vol, ish.gvl, ish.c5spd); + + /* Convert C5SPD to relnote/finetune + * + * In IT we can have a sample associated with two or more + * instruments, but c5spd is a sample attribute -- so we must + * scan all xmp instruments to set the correct transposition + */ + + for (j = 0; j < mod->ins; j++) { + for (k = 0; k < mod->xxi[j].nsm; k++) { + struct xmp_subinstrument *sub = &mod->xxi[j].sub[k]; + if (sub->sid == i) { + sub->vol = ish.vol; + sub->gvl = ish.gvl; + sub->vra = ish.vis; /* sample to sub-instrument vibrato */ + sub->vde = ish.vid << 1; + sub->vwf = ish.vit; + sub->vsw = (0xff - ish.vir) >> 1; + + libxmp_c2spd_to_note(ish.c5spd, + &mod->xxi[j].sub[k].xpo, + &mod->xxi[j].sub[k].fin); + + /* Set sample pan (overrides subinstrument) */ + if (ish.dfp & 0x80) { + sub->pan = (ish.dfp & 0x7f) * 4; + } else if (sample_mode) { + sub->pan = -1; + } + } + } + } + + if (ish.flags & IT_SMP_SAMPLE && xxs->len > 1) { + int cvt = 0; + + /* Sanity check - some modules may have invalid sizes on + * unused samples so only check this if the sample flag is set. */ + if (xxs->len > MAX_SAMPLE_SIZE) { + return -1; + } + + if (0 != hio_seek(f, start + ish.sample_ptr, SEEK_SET)) + return -1; + + if (xxs->lpe > xxs->len || xxs->lps >= xxs->lpe) + xxs->flg &= ~XMP_SAMPLE_LOOP; + + if (ish.convert == IT_CVT_ADPCM) + cvt |= SAMPLE_FLAG_ADPCM; + + if (~ish.convert & IT_CVT_SIGNED) + cvt |= SAMPLE_FLAG_UNS; + + /* compressed samples */ + if (ish.flags & IT_SMP_COMP) { + long min_size, file_len, left; + void *decbuf; + int ret; + + /* Sanity check - the lower bound on IT compressed + * sample size (in bytes) is a little over 1/8th of the + * number of SAMPLES in the sample. + */ + file_len = hio_size(f); + min_size = xxs->len >> 3; + left = file_len - (long)ish.sample_ptr; + /* No data to read at all? Just skip it... */ + if (left <= 0) + return 0; + + if ((file_len > 0) && (left < min_size)) { + D_(D_WARN "sample %X failed minimum size check " + "(len=%d, needs >=%ld bytes, %ld available): " + "resizing to %ld", + i, xxs->len, min_size, left, left << 3); + + force_sample_length(xxs, xtra, left << 3); + } + + decbuf = (uint8 *) calloc(1, xxs->len * 2); + if (decbuf == NULL) + return -1; + + if (ish.flags & IT_SMP_16BIT) { + itsex_decompress16(f, (int16 *)decbuf, xxs->len, + ish.convert & IT_CVT_DIFF); + +#ifdef WORDS_BIGENDIAN + /* decompression generates native-endian + * samples, but we want little-endian + */ + cvt |= SAMPLE_FLAG_BIGEND; +#endif + } else { + itsex_decompress8(f, (uint8 *)decbuf, xxs->len, + ish.convert & IT_CVT_DIFF); + } + + ret = libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD | cvt, + &mod->xxs[i], decbuf); + if (ret < 0) { + free(decbuf); + return -1; + } + + free(decbuf); + } else { + if (libxmp_load_sample(m, f, cvt, &mod->xxs[i], NULL) < 0) + return -1; + } + } + + return 0; +} + +static int load_it_pattern(struct module_data *m, int i, int new_fx, + HIO_HANDLE *f) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event, dummy, lastevent[L_CHANNELS]; + uint8 mask[L_CHANNELS]; + uint8 last_fxp[64]; + + int r, c, pat_len, num_rows; + uint8 b; + + r = 0; + + memset(last_fxp, 0, sizeof(last_fxp)); + memset(lastevent, 0, L_CHANNELS * sizeof(struct xmp_event)); + memset(&dummy, 0, sizeof(struct xmp_event)); + + pat_len = hio_read16l(f) /* - 4 */ ; + mod->xxp[i]->rows = num_rows = hio_read16l(f); + + if (libxmp_alloc_tracks_in_pattern(mod, i) < 0) { + return -1; + } + + memset(mask, 0, L_CHANNELS); + hio_read16l(f); + hio_read16l(f); + + while (r < num_rows && --pat_len >= 0) { + b = hio_read8(f); + if (hio_error(f)) { + return -1; + } + if (!b) { + r++; + continue; + } + c = (b - 1) & 63; + + if (b & 0x80) { + mask[c] = hio_read8(f); + pat_len--; + } + /* + * WARNING: we IGNORE events in disabled channels. Disabled + * channels should be muted only, but we don't know the + * real number of channels before loading the patterns and + * we don't want to set it to 64 channels. + */ + if (c >= mod->chn) { + event = &dummy; + } else { + event = &EVENT(i, c, r); + } + + if (mask[c] & 0x01) { + b = hio_read8(f); + + /* From ittech.txt: + * Note ranges from 0->119 (C-0 -> B-9) + * 255 = note off, 254 = notecut + * Others = note fade (already programmed into IT's player + * but not available in the editor) + */ + switch (b) { + case 0xff: /* key off */ + b = XMP_KEY_OFF; + break; + case 0xfe: /* cut */ + b = XMP_KEY_CUT; + break; + default: + if (b > 119) { /* fade */ + b = XMP_KEY_FADE; + } else { + b++; /* note */ + } + } + lastevent[c].note = event->note = b; + pat_len--; + } + if (mask[c] & 0x02) { + b = hio_read8(f); + lastevent[c].ins = event->ins = b; + pat_len--; + } + if (mask[c] & 0x04) { + b = hio_read8(f); + lastevent[c].vol = event->vol = b; + xlat_volfx(event); + pat_len--; + } + if (mask[c] & 0x08) { + b = hio_read8(f); + if (b >= ARRAY_SIZE(fx)) { + D_(D_WARN "invalid effect %#02x", b); + hio_read8(f); + + } else { + event->fxt = b; + event->fxp = hio_read8(f); + + xlat_fx(c, event, last_fxp, new_fx); + lastevent[c].fxt = event->fxt; + lastevent[c].fxp = event->fxp; + } + pat_len -= 2; + } + if (mask[c] & 0x10) { + event->note = lastevent[c].note; + } + if (mask[c] & 0x20) { + event->ins = lastevent[c].ins; + } + if (mask[c] & 0x40) { + event->vol = lastevent[c].vol; + xlat_volfx(event); + } + if (mask[c] & 0x80) { + event->fxt = lastevent[c].fxt; + event->fxp = lastevent[c].fxp; + } + } + + return 0; +} + +static int it_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, i, j; + struct it_file_header ifh; + int max_ch; + uint32 *pp_ins; /* Pointers to instruments */ + uint32 *pp_smp; /* Pointers to samples */ + uint32 *pp_pat; /* Pointers to patterns */ + int new_fx, sample_mode; + int pat_before_smp = 0; + int is_mpt_116 = 0; + + LOAD_INIT(); + + /* Load and convert header */ + ifh.magic = hio_read32b(f); + if (ifh.magic != MAGIC_IMPM) { + return -1; + } + + hio_read(ifh.name, 26, 1, f); + ifh.hilite_min = hio_read8(f); + ifh.hilite_maj = hio_read8(f); + + ifh.ordnum = hio_read16l(f); + ifh.insnum = hio_read16l(f); + ifh.smpnum = hio_read16l(f); + ifh.patnum = hio_read16l(f); + + ifh.cwt = hio_read16l(f); + ifh.cmwt = hio_read16l(f); + ifh.flags = hio_read16l(f); + ifh.special = hio_read16l(f); + + ifh.gv = hio_read8(f); + ifh.mv = hio_read8(f); + ifh.is = hio_read8(f); + ifh.it = hio_read8(f); + ifh.sep = hio_read8(f); + ifh.pwd = hio_read8(f); + + /* Sanity check */ + if (ifh.gv > 0x80) { + D_(D_CRIT "invalid gv (%u)", ifh.gv); + goto err; + } + + ifh.msglen = hio_read16l(f); + ifh.msgofs = hio_read32l(f); + ifh.rsvd = hio_read32l(f); + + hio_read(ifh.chpan, 64, 1, f); + hio_read(ifh.chvol, 64, 1, f); + + if (hio_error(f)) { + D_(D_CRIT "error reading IT header"); + goto err; + } + + memcpy(mod->name, ifh.name, sizeof(ifh.name)); + /* sizeof(ifh.name) == 26, sizeof(mod->name) == 64. */ + mod->name[sizeof(ifh.name)] = '\0'; + mod->len = ifh.ordnum; + mod->ins = ifh.insnum; + mod->smp = ifh.smpnum; + mod->pat = ifh.patnum; + + /* Sanity check */ + if (mod->ins > 255 || mod->smp > 255 || mod->pat > 255) { + D_(D_CRIT "invalid ins (%u), smp (%u), or pat (%u)", + mod->ins, mod->smp, mod->pat); + goto err; + } + + if (mod->ins) { + pp_ins = (uint32 *) calloc(4, mod->ins); + if (pp_ins == NULL) + goto err; + } else { + pp_ins = NULL; + } + + pp_smp = (uint32 *) calloc(4, mod->smp); + if (pp_smp == NULL) + goto err2; + + pp_pat = (uint32 *) calloc(4, mod->pat); + if (pp_pat == NULL) + goto err3; + + mod->spd = ifh.is; + mod->bpm = ifh.it; + + sample_mode = ~ifh.flags & IT_USE_INST; + + if (ifh.flags & IT_LINEAR_FREQ) { + m->period_type = PERIOD_LINEAR; + } + + for (i = 0; i < 64; i++) { + struct xmp_channel *xxc = &mod->xxc[i]; + + if (ifh.chpan[i] == 100) { /* Surround -> center */ + xxc->flg |= XMP_CHANNEL_SURROUND; + } + + if (ifh.chpan[i] & 0x80) { /* Channel mute */ + xxc->flg |= XMP_CHANNEL_MUTE; + } + + if (ifh.flags & IT_STEREO) { + xxc->pan = (int)ifh.chpan[i] * 0x80 >> 5; + if (xxc->pan > 0xff) + xxc->pan = 0xff; + } else { + xxc->pan = 0x80; + } + + xxc->vol = ifh.chvol[i]; + } + + if (mod->len <= XMP_MAX_MOD_LENGTH) { + hio_read(mod->xxo, 1, mod->len, f); + } else { + hio_read(mod->xxo, 1, XMP_MAX_MOD_LENGTH, f); + hio_seek(f, mod->len - XMP_MAX_MOD_LENGTH, SEEK_CUR); + mod->len = XMP_MAX_MOD_LENGTH; + } + + new_fx = ifh.flags & IT_OLD_FX ? 0 : 1; + + for (i = 0; i < mod->ins; i++) + pp_ins[i] = hio_read32l(f); + for (i = 0; i < mod->smp; i++) + pp_smp[i] = hio_read32l(f); + for (i = 0; i < mod->pat; i++) + pp_pat[i] = hio_read32l(f); + + if ((ifh.flags & IT_MIDI_CONFIG) || (ifh.special & IT_SPEC_MIDICFG)) { + /* Skip edit history if it exists. */ + if (ifh.special & IT_EDIT_HISTORY) { + int skip = hio_read16l(f) * 8; + if (hio_error(f) || (skip && hio_seek(f, skip, SEEK_CUR) < 0)) + goto err4; + } + if (load_it_midi_config(m, f) < 0) + goto err4; + } + if (mod->smp && mod->pat && pp_pat[0] != 0 && pp_pat[0] < pp_smp[0]) + pat_before_smp = 1; + + m->c4rate = C4_NTSC_RATE; + + identify_tracker(m, &ifh, pat_before_smp, &is_mpt_116); + + MODULE_INFO(); + + D_(D_INFO "Instrument/FX mode: %s/%s", + sample_mode ? "sample" : ifh.cmwt >= 0x200 ? + "new" : "old", ifh.flags & IT_OLD_FX ? "old" : "IT"); + + if (sample_mode) + mod->ins = mod->smp; + + if (libxmp_init_instrument(m) < 0) + goto err4; + + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + /* + * IT files can have three different instrument types: 'New' + * instruments, 'old' instruments or just samples. We need a + * different loader for each of them. + */ + + struct xmp_instrument *xxi = &mod->xxi[i]; + + if (!sample_mode && ifh.cmwt >= 0x200) { + /* New instrument format */ + if (hio_seek(f, start + pp_ins[i], SEEK_SET) < 0) { + goto err4; + } + + if (load_new_it_instrument(xxi, f) < 0) { + goto err4; + } + + } else if (!sample_mode) { + /* Old instrument format */ + if (hio_seek(f, start + pp_ins[i], SEEK_SET) < 0) { + goto err4; + } + + if (load_old_it_instrument(xxi, f) < 0) { + goto err4; + } + } + } + + D_(D_INFO "Stored Samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + + if (hio_seek(f, start + pp_smp[i], SEEK_SET) < 0) { + goto err4; + } + + if (load_it_sample(m, i, start, sample_mode, f) < 0) { + goto err4; + } + } + /* Reset any error status set by truncated samples. */ + hio_error(f); + + D_(D_INFO "Stored patterns: %d", mod->pat); + + /* Effects in muted channels are processed, so scan patterns first to + * see the real number of channels + */ + max_ch = 0; + for (i = 0; i < mod->pat; i++) { + uint8 mask[L_CHANNELS]; + int pat_len, num_rows, row; + + /* If the offset to a pattern is 0, the pattern is empty */ + if (pp_pat[i] == 0) + continue; + + hio_seek(f, start + pp_pat[i], SEEK_SET); + pat_len = hio_read16l(f) /* - 4 */ ; + num_rows = hio_read16l(f); + memset(mask, 0, L_CHANNELS); + hio_read16l(f); + hio_read16l(f); + + /* Sanity check: + * - Impulse Tracker and Schism Tracker allow up to 200 rows. + * - ModPlug Tracker 1.16 allows 256 rows. + * - OpenMPT allows 1024 rows. + */ + if (num_rows > 1024) { + D_(D_WARN "skipping pattern %d (%d rows)", i, num_rows); + pp_pat[i] = 0; + continue; + } + + row = 0; + while (row < num_rows && --pat_len >= 0) { + int b = hio_read8(f); + if (hio_error(f)) { + D_(D_CRIT "error scanning pattern %d", i); + goto err4; + } + if (b == 0) { + row++; + continue; + } + + c = (b - 1) & 63; + + if (c > max_ch) + max_ch = c; + + if (b & 0x80) { + mask[c] = hio_read8(f); + pat_len--; + } + + if (mask[c] & 0x01) { + hio_read8(f); + pat_len--; + } + if (mask[c] & 0x02) { + hio_read8(f); + pat_len--; + } + if (mask[c] & 0x04) { + hio_read8(f); + pat_len--; + } + if (mask[c] & 0x08) { + hio_read8(f); + hio_read8(f); + pat_len -= 2; + } + } + } + + /* Set the number of channels actually used + */ + mod->chn = max_ch + 1; + mod->trk = mod->pat * mod->chn; + + if (libxmp_init_pattern(mod) < 0) { + goto err4; + } + + /* Read patterns */ + for (i = 0; i < mod->pat; i++) { + + if (libxmp_alloc_pattern(mod, i) < 0) { + goto err4; + } + + /* If the offset to a pattern is 0, the pattern is empty */ + if (pp_pat[i] == 0) { + mod->xxp[i]->rows = 64; + for (j = 0; j < mod->chn; j++) { + int tnum = i * mod->chn + j; + if (libxmp_alloc_track(mod, tnum, 64) < 0) + goto err4; + mod->xxp[i]->index[j] = tnum; + } + continue; + } + + if (hio_seek(f, start + pp_pat[i], SEEK_SET) < 0) { + D_(D_CRIT "error seeking to %d", start + pp_pat[i]); + goto err4; + } + + if (load_it_pattern(m, i, new_fx, f) < 0) { + D_(D_CRIT "error loading pattern %d", i); + goto err4; + } + } + + free(pp_pat); + free(pp_smp); + free(pp_ins); + + /* Song message */ + if (ifh.special & IT_HAS_MSG) { + if ((m->comment = (char *)malloc(ifh.msglen)) != NULL) { + hio_seek(f, start + ifh.msgofs, SEEK_SET); + + D_(D_INFO "Message length : %d", ifh.msglen); + + for (j = 0; j + 1 < ifh.msglen; j++) { + int b = hio_read8(f); + if (b == '\r') { + b = '\n'; + } else if ((b < 32 || b > 127) && b != '\n' + && b != '\t') { + b = '.'; + } + m->comment[j] = b; + } + + if (ifh.msglen > 0) { + m->comment[j] = 0; + } + } + } + + /* Format quirks */ + + m->quirk |= QUIRKS_IT | QUIRK_ARPMEM | QUIRK_INSVOL; + + if (ifh.flags & IT_LINK_GXX) { + m->quirk |= QUIRK_PRENV; + } else { + m->quirk |= QUIRK_UNISLD; + } + + if (new_fx) { + m->quirk |= QUIRK_VIBHALF | QUIRK_VIBINV; + } else { + m->quirk &= ~QUIRK_VIBALL; + m->quirk |= QUIRK_ITOLDFX; + } + + if (sample_mode) { + m->quirk &= ~(QUIRK_VIRTUAL | QUIRK_RSTCHN); + } + + m->gvolbase = 0x80; + m->gvol = ifh.gv; + m->mvolbase = 48; + m->mvol = ifh.mv; + m->read_event_type = READ_EVENT_IT; + +#ifndef LIBXMP_CORE_PLAYER + if (is_mpt_116) + libxmp_apply_mpt_preamp(m); +#endif + + return 0; + +err4: + free(pp_pat); +err3: + free(pp_smp); +err2: + free(pp_ins); +err: + return -1; +} + +#endif /* LIBXMP_CORE_DISABLE_IT */ diff --git a/thirdparty/libxmp/src/loaders/itsex.c b/thirdparty/libxmp/src/loaders/itsex.c new file mode 100644 index 000000000..a56dd4fa0 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/itsex.c @@ -0,0 +1,243 @@ +#ifndef LIBXMP_CORE_DISABLE_IT + +/* Public domain IT sample decompressor by Olivier Lapicque */ + +#include "loader.h" +#include "it.h" + +static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, int n, int *err) +{ + uint32 retval = 0; + int i = n; + int bnum = *bitnum; + uint32 bbuf = *bitbuf; + + if (n > 0 && n <= 32) { + do { + if (bnum == 0) { + if (hio_eof(ibuf)) { + *err = EOF; + return 0; + } + bbuf = hio_read8(ibuf); + bnum = 8; + } + retval >>= 1; + retval |= bbuf << 31; + bbuf >>= 1; + bnum--; + i--; + } while (i != 0); + + i = n; + + *bitnum = bnum; + *bitbuf = bbuf; + } else { + /* Invalid shift value. */ + *err = -2; + return 0; + } + + return (retval >> (32 - i)); +} + + +int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215) +{ + /* uint32 size = 0; */ + uint32 block_count = 0; + uint32 bitbuf = 0; + int bitnum = 0; + uint8 left = 0, temp = 0, temp2 = 0; + uint32 d, pos; + int err = 0; + + while (len) { + if (!block_count) { + block_count = 0x8000; + /*size =*/ hio_read16l(src); + left = 9; + temp = temp2 = 0; + bitbuf = bitnum = 0; + } + + d = block_count; + if (d > len) + d = len; + + /* Unpacking */ + pos = 0; + do { + uint16 bits = read_bits(src, &bitbuf, &bitnum, left, &err); + if (err != 0) + return -1; + + if (left < 7) { + uint32 i = 1 << (left - 1); + uint32 j = bits & 0xffff; + if (i != j) + goto unpack_byte; + bits = (read_bits(src, &bitbuf, &bitnum, 3, &err) + + 1) & 0xff; + if (err != 0) + return -1; + + left = ((uint8)bits < left) ? (uint8)bits : + (uint8)((bits + 1) & 0xff); + goto next; + } + + if (left < 9) { + uint16 i = (0xff >> (9 - left)) + 4; + uint16 j = i - 8; + + if ((bits <= j) || (bits > i)) + goto unpack_byte; + + bits -= j; + left = ((uint8)(bits & 0xff) < left) ? + (uint8)(bits & 0xff) : + (uint8)((bits + 1) & 0xff); + goto next; + } + + if (left >= 10) + goto skip_byte; + + if (bits >= 256) { + left = (uint8) (bits + 1) & 0xff; + goto next; + } + + unpack_byte: + if (left < 8) { + uint8 shift = 8 - left; + signed char c = (signed char)(bits << shift); + c >>= shift; + bits = (uint16) c; + } + bits += temp; + temp = (uint8)bits; + temp2 += temp; + dst[pos] = it215 ? temp2 : temp; + + skip_byte: + pos++; + + next: + /* if (slen <= 0) + return -1 */; + } while (pos < d); + + /* Move On */ + block_count -= d; + len -= d; + dst += d; + } + + return 0; +} + +int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215) +{ + /* uint32 size = 0; */ + uint32 block_count = 0; + uint32 bitbuf = 0; + int bitnum = 0; + uint8 left = 0; + int16 temp = 0, temp2 = 0; + uint32 d, pos; + int err = 0; + + while (len) { + if (!block_count) { + block_count = 0x4000; + /*size =*/ hio_read16l(src); + left = 17; + temp = temp2 = 0; + bitbuf = bitnum = 0; + } + + d = block_count; + if (d > len) + d = len; + + /* Unpacking */ + pos = 0; + do { + uint32 bits = read_bits(src, &bitbuf, &bitnum, left, &err); + if (err != 0) + return -1; + + if (left < 7) { + uint32 i = 1 << (left - 1); + uint32 j = bits; + + if (i != j) + goto unpack_byte; + + bits = read_bits(src, &bitbuf, &bitnum, 4, &err) + 1; + if (err != 0) + return -1; + + left = ((uint8)(bits & 0xff) < left) ? + (uint8)(bits & 0xff) : + (uint8)((bits + 1) & 0xff); + goto next; + } + + if (left < 17) { + uint32 i = (0xffff >> (17 - left)) + 8; + uint32 j = (i - 16) & 0xffff; + + if ((bits <= j) || (bits > (i & 0xffff))) + goto unpack_byte; + + bits -= j; + left = ((uint8)(bits & 0xff) < left) ? + (uint8)(bits & 0xff) : + (uint8)((bits + 1) & 0xff); + goto next; + } + + if (left >= 18) + goto skip_byte; + + if (bits >= 0x10000) { + left = (uint8)(bits + 1) & 0xff; + goto next; + } + + unpack_byte: + if (left < 16) { + uint8 shift = 16 - left; + int16 c = (int16)(bits << shift); + c >>= shift; + bits = (uint32) c; + } + bits += temp; + temp = (int16)bits; + temp2 += temp; + dst[pos] = (it215) ? temp2 : temp; + + skip_byte: + pos++; + + next: + /* if (slen <= 0) + return -1 */; + } while (pos < d); + + /* Move On */ + block_count -= d; + len -= d; + dst += d; + if (len <= 0) + break; + } + + return 0; +} + +#endif /* LIBXMP_CORE_DISABLE_IT */ diff --git a/thirdparty/libxmp/src/loaders/liq_load.c b/thirdparty/libxmp/src/loaders/liq_load.c new file mode 100644 index 000000000..5c2cf235a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/liq_load.c @@ -0,0 +1,657 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Liquid Tracker module loader based on the format description written + * by Nir Oren. Tested with Shell.liq sent by Adi Sapir. + */ + +#include "loader.h" +#include "../period.h" + + +struct liq_header { + uint8 magic[14]; /* "Liquid Module:" */ + uint8 name[30]; /* ASCIIZ module name */ + uint8 author[20]; /* Author name */ + uint8 _0x1a; /* 0x1a */ + uint8 tracker[20]; /* Tracker name */ + uint16 version; /* Format version */ + uint16 speed; /* Initial speed */ + uint16 bpm; /* Initial bpm */ + uint16 low; /* Lowest note (Amiga Period*4) */ + uint16 high; /* Uppest note (Amiga Period*4) */ + uint16 chn; /* Number of channels */ + uint32 flags; /* Module flags */ + uint16 pat; /* Number of patterns saved */ + uint16 ins; /* Number of instruments */ + uint16 len; /* Module length */ + uint16 hdrsz; /* Header size */ +}; + +struct liq_instrument { +#if 0 + uint8 magic[4]; /* 'L', 'D', 'S', 'S' */ +#endif + uint16 version; /* LDSS header version */ + uint8 name[30]; /* Instrument name */ + uint8 editor[20]; /* Generator name */ + uint8 author[20]; /* Author name */ + uint8 hw_id; /* Hardware used to record the sample */ + uint32 length; /* Sample length */ + uint32 loopstart; /* Sample loop start */ + uint32 loopend; /* Sample loop end */ + uint32 c2spd; /* C2SPD */ + uint8 vol; /* Volume */ + uint8 flags; /* Flags */ + uint8 pan; /* Pan */ + uint8 midi_ins; /* General MIDI instrument */ + uint8 gvl; /* Global volume */ + uint8 chord; /* Chord type */ + uint16 hdrsz; /* LDSS header size */ + uint16 comp; /* Compression algorithm */ + uint32 crc; /* CRC */ + uint8 midi_ch; /* MIDI channel */ + uint8 rsvd[11]; /* Reserved */ + uint8 filename[25]; /* DOS file name */ +}; + +struct liq_pattern { +#if 0 + uint8 magic[4]; /* 'L', 'P', 0, 0 */ +#endif + uint8 name[30]; /* ASCIIZ pattern name */ + uint16 rows; /* Number of rows */ + uint32 size; /* Size of packed pattern */ + uint32 reserved; /* Reserved */ +}; + + +static int liq_test (HIO_HANDLE *, char *, const int); +static int liq_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_liq = { + "Liquid Tracker", + liq_test, + liq_load +}; + +static int liq_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[15]; + + if (hio_read(buf, 1, 14, f) < 14) + return -1; + + if (memcmp(buf, "Liquid Module:", 14)) + return -1; + + libxmp_read_title(f, t, 30); + + return 0; +} + + +#define NONE 0xff + + +static const uint8 fx[25] = { + FX_ARPEGGIO, + FX_S3M_BPM, + FX_BREAK, + FX_PORTA_DN, + NONE, + FX_FINE_VIBRATO, + NONE, + NONE, + NONE, + FX_JUMP, + NONE, + FX_VOLSLIDE, + FX_EXTENDED, + FX_TONEPORTA, + FX_OFFSET, + NONE, /* FIXME: Pan */ + NONE, + NONE, /*FX_MULTI_RETRIG,*/ + FX_S3M_SPEED, + FX_TREMOLO, + FX_PORTA_UP, + FX_VIBRATO, + NONE, + FX_TONE_VSLIDE, + FX_VIBRA_VSLIDE +}; + + +/* Effect translation */ +static void xlat_fx(int c, struct xmp_event *e) +{ + uint8 h = MSN (e->fxp), l = LSN (e->fxp); + + if (e->fxt >= ARRAY_SIZE(fx)) { + D_(D_WARN "invalid effect %#02x", e->fxt); + e->fxt = e->fxp = 0; + return; + } + + switch (e->fxt = fx[e->fxt]) { + case FX_EXTENDED: /* Extended effects */ + switch (h) { + case 0x3: /* Glissando */ + e->fxp = l | (EX_GLISS << 4); + break; + case 0x4: /* Vibrato wave */ + if (l == 3) + l++; + e->fxp = l | (EX_VIBRATO_WF << 4); + break; + case 0x5: /* Finetune */ + e->fxp = l | (EX_FINETUNE << 4); + break; + case 0x6: /* Pattern loop */ + e->fxp = l | (EX_PATTERN_LOOP << 4); + break; + case 0x7: /* Tremolo wave */ + if (l == 3) + l++; + e->fxp = l | (EX_TREMOLO_WF << 4); + break; + case 0xc: /* Cut */ + e->fxp = l | (EX_CUT << 4); + break; + case 0xd: /* Delay */ + e->fxp = l | (EX_DELAY << 4); + break; + case 0xe: /* Pattern delay */ + e->fxp = l | (EX_PATT_DELAY << 4); + break; + default: /* Ignore */ + e->fxt = e->fxp = 0; + break; + } + break; + case NONE: /* No effect */ + e->fxt = e->fxp = 0; + break; + } +} + + +static int decode_event(uint8 x1, struct xmp_event *event, HIO_HANDLE *f) +{ + uint8 x2; + + memset (event, 0, sizeof (struct xmp_event)); + + if (x1 & 0x01) { + x2 = hio_read8(f); + if (x2 == 0xfe) + event->note = XMP_KEY_OFF; + else + event->note = x2 + 1 + 36; + } + + if (x1 & 0x02) + event->ins = hio_read8(f) + 1; + + if (x1 & 0x04) + event->vol = hio_read8(f); + + if (x1 & 0x08) + event->fxt = hio_read8(f) - 'A'; + + if (x1 & 0x10) + event->fxp = hio_read8(f); + + D_(D_INFO " event: %02x %02x %02x %02x %02x", + event->note, event->ins, event->vol, event->fxt, event->fxp); + + /* Sanity check */ + if (event->note > 107 && event->note != XMP_KEY_OFF) + return -1; + + if (event->ins > 100 || event->vol > 64 || event->fxt > 26) + return -1; + + return 0; +} + +static int liq_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i; + struct xmp_event *event = NULL; + struct liq_header lh; + struct liq_instrument li; + struct liq_pattern lp; + uint8 x1, x2; + uint32 pmag; + char tracker_name[21]; + + LOAD_INIT(); + + hio_read(lh.magic, 14, 1, f); + hio_read(lh.name, 30, 1, f); + hio_read(lh.author, 20, 1, f); + hio_read8(f); + hio_read(lh.tracker, 20, 1, f); + + lh.version = hio_read16l(f); + lh.speed = hio_read16l(f); + lh.bpm = hio_read16l(f); + lh.low = hio_read16l(f); + lh.high = hio_read16l(f); + lh.chn = hio_read16l(f); + lh.flags = hio_read32l(f); + lh.pat = hio_read16l(f); + lh.ins = hio_read16l(f); + lh.len = hio_read16l(f); + lh.hdrsz = hio_read16l(f); + + /* Sanity check */ + if (lh.chn > XMP_MAX_CHANNELS || lh.pat > 256 || lh.ins > 256) { + return -1; + } + + if ((lh.version >> 8) == 0) { + lh.hdrsz = lh.len; + lh.len = 0; + hio_seek(f, -2, SEEK_CUR); + } + + if (lh.len > 256) { + return -1; + } + + mod->spd = lh.speed; + mod->bpm = MIN(lh.bpm, 255); + mod->chn = lh.chn; + mod->pat = lh.pat; + mod->ins = mod->smp = lh.ins; + mod->len = lh.len; + mod->trk = mod->chn * mod->pat; + + m->quirk |= QUIRK_INSVOL; + + strncpy(mod->name, (char *)lh.name, 30); + strncpy(tracker_name, (char *)lh.tracker, 20); + /* strncpy(m->author, (char *)lh.author, 20); */ + tracker_name[20] = 0; + for (i = 20; i >= 0; i--) { + if (tracker_name[i] == 0x20) + tracker_name[i] = 0; + if (tracker_name[i]) + break; + } + snprintf(mod->type, XMP_NAME_SIZE, "%s LIQ %d.%02d", + tracker_name, lh.version >> 8, lh.version & 0x00ff); + + if (lh.version > 0) { + for (i = 0; i < mod->chn; i++) { + uint8 pan = hio_read8(f); + + if (pan >= 64) { + if (pan == 64) { + pan = 63; + } else if (pan == 66) { + pan = 31; + mod->xxc[i].flg |= XMP_CHANNEL_SURROUND; + } else { + /* Sanity check */ + return -1; + } + } + + mod->xxc[i].pan = pan << 2; + } + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].vol = hio_read8(f); + + hio_read(mod->xxo, 1, mod->len, f); + + /* Skip 1.01 echo pools */ + hio_seek(f, lh.hdrsz - (0x6d + mod->chn * 2 + mod->len), SEEK_CUR); + } else { + hio_seek(f, start + 0xf0, SEEK_SET); + hio_read (mod->xxo, 1, 256, f); + hio_seek(f, start + lh.hdrsz, SEEK_SET); + + for (i = 0; i < 256; i++) { + if (mod->xxo[i] == 0xff) + break; + } + mod->len = i; + } + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + + D_(D_INFO "Stored patterns: %d", mod->pat); + + x1 = x2 = 0; + for (i = 0; i < mod->pat; i++) { + int row, channel, count; + + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + + pmag = hio_read32b(f); + if (pmag == 0x21212121) /* !!!! */ + continue; + if (pmag != 0x4c500000) /* LP\0\0 */ + return -1; + + hio_read(lp.name, 30, 1, f); + lp.rows = hio_read16l(f); + lp.size = hio_read32l(f); + lp.reserved = hio_read32l(f); + + /* Sanity check */ + if (lp.rows > 256) { + return -1; + } + + D_(D_INFO "rows: %d size: %d\n", lp.rows, lp.size); + + mod->xxp[i]->rows = lp.rows; + libxmp_alloc_tracks_in_pattern(mod, i); + + row = 0; + channel = 0; + count = hio_tell(f); + +/* + * Packed pattern data is stored full Track after full Track from the left to + * the right (all Intervals in Track and then going Track right). You should + * expect 0C0h on any pattern end, and then your Unpacked Patterndata Pointer + * should be equal to the value in offset [24h]; if it's not, you should exit + * with an error. + */ + +read_event: + /* Sanity check */ + if (i >= mod->pat || channel >= mod->chn || row >= mod->xxp[i]->rows) + return -1; + + event = &EVENT(i, channel, row); + + if (x2) { + if (decode_event(x1, event, f) < 0) + return -1; + xlat_fx (channel, event); + x2--; + goto next_row; + } + + x1 = hio_read8(f); + +test_event: + /* Sanity check */ + if (i >= mod->pat || channel >= mod->chn || row >= mod->xxp[i]->rows) + return -1; + + event = &EVENT(i, channel, row); + D_(D_INFO "* count=%ld chan=%d row=%d event=%02x", + hio_tell(f) - count, channel, row, x1); + + switch (x1) { + case 0xc0: /* end of pattern */ + D_(D_WARN "- end of pattern"); + if (hio_tell(f) - count != lp.size) + return -1; + goto next_pattern; + case 0xe1: /* skip channels */ + x1 = hio_read8(f); + channel += x1; + D_(D_INFO " [skip %d channels]", x1); + /* fall thru */ + case 0xa0: /* next channel */ + D_(D_INFO " [next channel]"); + channel++; + if (channel >= mod->chn) { + D_(D_CRIT "uh-oh! bad channel number!"); + channel--; + } + row = -1; + goto next_row; + case 0xe0: /* skip rows */ + x1 = hio_read8(f); + D_(D_INFO " [skip %d rows]", x1); + row += x1; + /* fall thru */ + case 0x80: /* next row */ + D_(D_INFO " [next row]"); + goto next_row; + } + + if (x1 > 0xc0 && x1 < 0xe0) { /* packed data */ + D_(D_INFO " [packed data]"); + if (decode_event(x1, event, f) < 0) + return -1; + xlat_fx (channel, event); + goto next_row; + } + + if (x1 > 0xa0 && x1 < 0xc0) { /* packed data repeat */ + x2 = hio_read8(f); + D_(D_INFO " [packed data - repeat %d times]", x2); + if (decode_event(x1, event, f) < 0) + return -1; + xlat_fx (channel, event); + goto next_row; + } + + if (x1 > 0x80 && x1 < 0xa0) { /* packed data repeat, keep note */ + x2 = hio_read8(f); + D_(D_INFO " [packed data - repeat %d times, keep note]", x2); + if (decode_event(x1, event, f) < 0) + return -1; + xlat_fx (channel, event); + while (x2) { + row++; + + /* Sanity check */ + if (row >= lp.rows) + return -1; + + memcpy(&EVENT(i, channel, row), event, sizeof (struct xmp_event)); + x2--; + } + goto next_row; + } + + /* unpacked data */ + D_ (D_INFO " [unpacked data]"); + if (x1 < 0xfe) + event->note = 1 + 36 + x1; + else if (x1 == 0xfe) + event->note = XMP_KEY_OFF; + + x1 = hio_read8(f); + if (x1 > 100) { + row++; + goto test_event; + } + if (x1 != 0xff) + event->ins = x1 + 1; + + x1 = hio_read8(f); + if (x1 != 0xff) + event->vol = x1; + + x1 = hio_read8(f); + if (x1 != 0xff) + event->fxt = x1 - 'A'; + + x1 = hio_read8(f); + event->fxp = x1; + + /* Sanity check */ + if (event->fxt > 24) { + return -1; + } + + xlat_fx(channel, event); + + D_(D_INFO " event: %02x %02x %02x %02x %02x\n", + event->note, event->ins, event->vol, event->fxt, event->fxp); + + /* Sanity check */ + if (event->note > 119 && event->note != XMP_KEY_OFF) + return -1; + + if (event->ins > 100 || event->vol > 65) + return -1; + +next_row: + row++; + if (row >= mod->xxp[i]->rows) { + row = 0; + x2 = 0; + channel++; + } + + /* Sanity check */ + if (channel >= mod->chn) { + channel = 0; + } + + goto read_event; + +next_pattern: + ; + } + + /* Read and convert instruments */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs = &mod->xxs[i]; + unsigned char b[4]; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + if (hio_read(b, 1, 4, f) < 4) + return -1; + + if (b[0] == '?' && b[1] == '?' && b[2] == '?' && b[3] == '?') + continue; + if (b[0] != 'L' || b[1] != 'D' || b[2] != 'S' || b[3] != 'S') + return -1; + + li.version = hio_read16l(f); + hio_read(li.name, 30, 1, f); + hio_read(li.editor, 20, 1, f); + hio_read(li.author, 20, 1, f); + li.hw_id = hio_read8(f); + + li.length = hio_read32l(f); + li.loopstart = hio_read32l(f); + li.loopend = hio_read32l(f); + li.c2spd = hio_read32l(f); + + li.vol = hio_read8(f); + li.flags = hio_read8(f); + li.pan = hio_read8(f); + li.midi_ins = hio_read8(f); + li.gvl = hio_read8(f); + li.chord = hio_read8(f); + + li.hdrsz = hio_read16l(f); + li.comp = hio_read16l(f); + li.crc = hio_read32l(f); + + li.midi_ch = hio_read8(f); + hio_read(li.rsvd, 11, 1, f); + hio_read(li.filename, 25, 1, f); + + /* Sanity check */ + if (hio_error(f)) { + return -1; + } + + xxi->nsm = !!(li.length); + xxi->vol = 0x40; + + xxs->len = li.length; + xxs->lps = li.loopstart; + xxs->lpe = li.loopend; + + if (li.flags & 0x01) { + xxs->flg = XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + if (li.loopend > 0) + xxs->flg = XMP_SAMPLE_LOOP; + + /* FIXME: LDSS 1.0 have global vol == 0 ? */ + /* if (li.gvl == 0) */ + li.gvl = 0x40; + + sub->vol = li.vol; + sub->gvl = li.gvl; + sub->pan = li.pan; + sub->sid = i; + + libxmp_instrument_name(mod, i, li.name, 31); + + D_(D_INFO "[%2X] %-30.30s %05x%c%05x %05x %c %02x %02x %2d.%02d %5d", + i, mod->xxi[i].name, mod->xxs[i].len, + xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', xxs->lps, xxs->lpe, + xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', sub->vol, sub->gvl, + li.version >> 8, li.version & 0xff, li.c2spd); + + libxmp_c2spd_to_note(li.c2spd, &sub->xpo, &sub->fin); + hio_seek(f, li.hdrsz - 0x90, SEEK_CUR); + + if (xxs->len == 0) + continue; + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + } + + m->quirk |= QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + + return 0; +} + diff --git a/thirdparty/libxmp/src/loaders/loader.h b/thirdparty/libxmp/src/loaders/loader.h new file mode 100644 index 000000000..b5088fce9 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/loader.h @@ -0,0 +1,74 @@ +#ifndef XMP_LOADER_H +#define XMP_LOADER_H + +#include "../common.h" +#include "../effects.h" +#include "../format.h" +#include "../hio.h" + +/* Sample flags */ +#define SAMPLE_FLAG_DIFF 0x0001 /* Differential */ +#define SAMPLE_FLAG_UNS 0x0002 /* Unsigned */ +#define SAMPLE_FLAG_8BDIFF 0x0004 +#define SAMPLE_FLAG_7BIT 0x0008 +#define SAMPLE_FLAG_NOLOAD 0x0010 /* Get from buffer, don't load */ +#define SAMPLE_FLAG_BIGEND 0x0040 /* Big-endian */ +#define SAMPLE_FLAG_VIDC 0x0080 /* Archimedes VIDC logarithmic */ +/*#define SAMPLE_FLAG_STEREO 0x0100 Interleaved stereo sample */ +#define SAMPLE_FLAG_FULLREP 0x0200 /* Play full sample before looping */ +#define SAMPLE_FLAG_ADLIB 0x1000 /* Adlib synth instrument */ +#define SAMPLE_FLAG_HSC 0x2000 /* HSC Adlib synth instrument */ +#define SAMPLE_FLAG_ADPCM 0x4000 /* ADPCM4 encoded samples */ + +/* libxmp_test_name flags */ +#define TEST_NAME_IGNORE_AFTER_0 0x0001 +#define TEST_NAME_IGNORE_AFTER_CR 0x0002 + +#define DEFPAN(x) (0x80 + ((x) - 0x80) * m->defpan / 100) + +int libxmp_init_instrument (struct module_data *); +int libxmp_realloc_samples (struct module_data *, int); +int libxmp_alloc_subinstrument (struct xmp_module *, int, int); +int libxmp_init_pattern (struct xmp_module *); +int libxmp_alloc_pattern (struct xmp_module *, int); +int libxmp_alloc_track (struct xmp_module *, int, int); +int libxmp_alloc_tracks_in_pattern (struct xmp_module *, int); +int libxmp_alloc_pattern_tracks (struct xmp_module *, int, int); +#ifndef LIBXMP_CORE_PLAYER +int libxmp_alloc_pattern_tracks_long(struct xmp_module *, int, int); +#endif +char *libxmp_instrument_name (struct xmp_module *, int, uint8 *, int); + +char *libxmp_copy_adjust (char *, uint8 *, int); +int libxmp_copy_name_for_fopen (char *, const char *, int); +int libxmp_test_name (const uint8 *, int, int); +void libxmp_read_title (HIO_HANDLE *, char *, int); +void libxmp_set_xxh_defaults (struct xmp_module *); +void libxmp_decode_protracker_event (struct xmp_event *, const uint8 *); +void libxmp_decode_noisetracker_event(struct xmp_event *, const uint8 *); +void libxmp_disable_continue_fx (struct xmp_event *); +int libxmp_check_filename_case (const char *, const char *, char *, int); +void libxmp_get_instrument_path (struct module_data *, char *, int); +void libxmp_set_type (struct module_data *, const char *, ...); +int libxmp_load_sample (struct module_data *, HIO_HANDLE *, int, + struct xmp_sample *, const void *); +void libxmp_free_sample (struct xmp_sample *); +#ifndef LIBXMP_CORE_PLAYER +void libxmp_schism_tracker_string (char *, size_t, int, int); +void libxmp_apply_mpt_preamp (struct module_data *m); +#endif + +extern uint8 libxmp_ord_xlat[]; +extern const int libxmp_arch_vol_table[]; + +#define MAGIC4(a,b,c,d) \ + (((uint32)(a)<<24)|((uint32)(b)<<16)|((uint32)(c)<<8)|(d)) + +#define LOAD_INIT() + +#define MODULE_INFO() do { \ + D_(D_WARN "Module title: \"%s\"", m->mod.name); \ + D_(D_WARN "Module type: %s", m->mod.type); \ +} while (0) + +#endif diff --git a/thirdparty/libxmp/src/loaders/lzw.c b/thirdparty/libxmp/src/loaders/lzw.c new file mode 100644 index 000000000..6e1bd828d --- /dev/null +++ b/thirdparty/libxmp/src/loaders/lzw.c @@ -0,0 +1,439 @@ +/* Extended Module Player + * Copyright (C) 2021-2023 Alice Rowan + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Simple LZW decoder for Digital Symphony. + * This does not handle the hacks required for ARC or UnShrink. */ + +#include "lzw.h" +#include + +/*#define LZW_DEBUG*/ + +#define LZW_NO_CODE ((uint16)-1) +#define LZW_CODE_CLEAR 256 +#define LZW_CODE_SYM_EOF 257 + +struct bitstream +{ + uint32 buf; + size_t num_read; + size_t max_read; + int bits; +}; + +struct LZW_code +{ + uint16 prev; + uint16 length; + uint8 value; +}; + +struct LZW_tree +{ + struct LZW_code *codes; + unsigned int bits; + unsigned int length; + unsigned int maxlength; + unsigned int defaultlength; + unsigned int alloclength; + unsigned int previous_code; + int new_inc; + int flags; + uint8 previous_first_char; +}; + +static void bs_init(struct bitstream *bs, size_t max_read) +{ + bs->buf = 0; + bs->num_read = 0; + bs->max_read = max_read; + bs->bits = 0; +} + +static int bs_read(struct bitstream *bs, HIO_HANDLE *f, int bits) +{ + uint8 byte; + int ret; + + if (bs->bits < bits) { + while (bs->bits < bits) { + if (bs->num_read >= bs->max_read) + return -1; + + byte = hio_read8(f); + bs->buf |= byte << bs->bits; + bs->bits += 8; + bs->num_read++; + } + if (hio_error(f)) + return -1; + } + + ret = bs->buf & ((1 << bits) - 1); + bs->buf >>= bits; + bs->bits -= bits; + return ret; +} + +static int LZW_init_tree(struct LZW_tree *lzw, int flags) +{ + unsigned int maxbits = LZW_FLAG_MAXBITS(flags); + unsigned int i; + + lzw->bits = 9; + if (maxbits < lzw->bits || maxbits > 16) + return -1; + + lzw->defaultlength = 258; /* 256 chars + clear + EOF. */ + lzw->maxlength = 1 << lzw->bits; + lzw->alloclength = 1 << maxbits; + + lzw->codes = (struct LZW_code *)calloc(lzw->alloclength, sizeof(struct LZW_code)); + if (lzw->codes == NULL) + return -1; + + lzw->length = lzw->defaultlength; + lzw->previous_code = LZW_NO_CODE; + lzw->new_inc = 0; + lzw->flags = flags; + lzw->previous_first_char = 0; + + for (i = 0; i < 256; i++) { + lzw->codes[i].length = 1; + lzw->codes[i].value = i; + lzw->codes[i].prev = LZW_NO_CODE; + } + return 0; +} + +static void LZW_free(struct LZW_tree *lzw) +{ + free(lzw->codes); + return; +} + +static void LZW_add(struct LZW_tree *lzw) +{ + struct LZW_code *current; + uint16 prev_length; + + if (lzw->length >= lzw->alloclength) + return; + + current = &(lzw->codes[lzw->length++]); + + /* Increase bitwidth if the NEXT code would be maxlength. */ + if (lzw->length >= lzw->maxlength && lzw->length < lzw->alloclength) { + lzw->maxlength <<= 1; + lzw->bits++; + lzw->new_inc = 1; + #ifdef LZW_DEBUG + printf("I: bitwidth increased to %d\n", lzw->bits); + #endif + } + + current->prev = lzw->previous_code; + current->value = lzw->previous_first_char; + + /* NOTE: when the length cache deadcode below is enabled, this may + * intentionally be set to or overflow to 0, in which case the length + * will be computed as-needed by iterating the tree. */ + prev_length = lzw->codes[lzw->previous_code].length; + current->length = prev_length ? prev_length + 1 : 0; +} + +/** + * Reset the LZW tree length. + */ +static void LZW_clear(struct LZW_tree *lzw) +{ + lzw->bits = 9; + lzw->maxlength = (1 << lzw->bits); + lzw->length = lzw->defaultlength; + lzw->previous_code = LZW_NO_CODE; +#if 0 +{ + int i; + for (i = lzw->defaultlength; i < lzw->alloclength; i++) + lzw->codes[i].length = 0; +} +#endif +} + +/** + * Get the length of an LZW code, or compute it if it isn't currently stored. + * This happens when one or mode codes in the sequence are marked for reuse. + */ +static uint16 LZW_get_length(const struct LZW_tree *lzw, const struct LZW_code *c) +{ +#if 0 + uint16 code; + uint16 length = 1; + + if (c->length) + return c->length; + + do { + /* Shouldn't happen, but... */ + if(length >= lzw->maxlength) + return 0; + + length++; + code = c->prev; + c = &(lzw->codes[code]); + } + while (code >= lzw->defaultlength); + return length; +#endif + return c->length; +} + +/** + * Output an LZW code. + */ +static int LZW_output(struct LZW_tree *lzw, uint16 code, uint8 **_pos, size_t *left) +{ + uint8 *pos = *_pos; + + struct LZW_code *codes = lzw->codes; + struct LZW_code *current = &(codes[code]); + unsigned int length = LZW_get_length(lzw, current); + unsigned int i; + + if (length == 0 || length > *left) + return -1; + + for (i = length - 1; i > 0; i--) { + pos[i] = current->value; + code = current->prev; + current = &(codes[code]); + } + *pos = code; + *_pos += length; + *left -= length; + + lzw->previous_first_char = code; + return 0; +} + +/** + * Decode an LZW code and create the next code from known data. + */ +static int LZW_decode(struct LZW_tree *lzw, uint16 code, uint8 **_pos, size_t *left) +{ + int kwkwk = 0; + int result; + + /* Digital Symphony LZW never seems to reference cleared codes, + * which allows some assumptions to be made (like never clearing the + * cached code lengths). If this decoder needs to support those, the + * cached length handling deadcode above needs to be uncommented. */ + if (code > lzw->length) + return -1; + + /* This is a special case--the current code is the previous code with the + * first character of the previous code appended, and needs to be added + * before the output occurs (instead of after). */ + if (code == lzw->length) { + if (lzw->previous_code == LZW_NO_CODE) + return -1; + + LZW_add(lzw); + lzw->previous_code = code; + kwkwk = 1; + } + + /* Otherwise, output first, and then add a new code, which is the previous + * code with the first character of the current code appended. */ + result = LZW_output(lzw, code, _pos, left); + if (result == 0 && !kwkwk) { + if (lzw->previous_code != LZW_NO_CODE) + LZW_add(lzw); + + lzw->previous_code = code; + } + return result; +} + +int libxmp_read_lzw(void *dest, size_t dest_len, size_t max_read_len, + int flags, HIO_HANDLE *f) +{ + struct LZW_tree lzw; + struct bitstream bs; + + uint8 *start = (uint8 *)dest; + uint8 *pos = start; + size_t left = dest_len; + int result; + int code; + + bs_init(&bs, max_read_len); + if (LZW_init_tree(&lzw, flags) != 0) + return -1; + + #ifdef LZW_DEBUG + printf("S: %zu\n", dest_len); + #endif + + while (left > 0) { + code = bs_read(&bs, f, lzw.bits); + #ifdef LZW_DEBUG + printf(" : %x\n", code); + #endif + if (code < 0) + break; + + if (code == LZW_CODE_CLEAR) { + #ifdef LZW_DEBUG + printf(" : >>> CLEAR <<<\n"); + #endif + LZW_clear(&lzw); + continue; + } else if ((flags & LZW_FLAG_SYMQUIRKS) && code == LZW_CODE_SYM_EOF) { + break; + } + + lzw.new_inc = 0; + result = LZW_decode(&lzw, code, &pos, &left); + if (result) + break; + } + + if (left > 0) { + D_(D_WARN "encountered error in stream or early EOF"); + memset(pos, 0, left); + } else if (flags & LZW_FLAG_SYMQUIRKS) { + /* Digital Symphony - read final EOF code. */ + if (lzw.new_inc) { + /* If the final code prior to EOF should have increased + * the bitwidth, read the EOF with the old bitwidth + * instead of the new one. + * + * This anomaly exists in FULLEFFECT, NARCOSIS and + * NEWDANCE. In NEWDANCE (libxmp's test file for this), + * it occurs specifically in the LZW-compressed sequence. + * https://github.com/libxmp/libxmp/issues/347 + */ + lzw.bits--; + } + + code = bs_read(&bs, f, lzw.bits); + #ifdef LZW_DEBUG + printf("E: %x\n", code); + #endif + if (code < 0) { + D_(D_WARN "missing LZW EOF code!"); + } else if (code != LZW_CODE_SYM_EOF) { + D_(D_WARN "LZW stream is longer than the provided buffer!"); + } + + } + + if (flags & LZW_FLAG_SYMQUIRKS) { + /* Digital Symphony LZW compressed stream size is 4 aligned. */ + size_t num_read = bs.num_read; + while (num_read & 3) { + #ifdef LZW_DEBUG + printf("A: align byte\n"); + #endif + hio_read8(f); + num_read++; + } + } + #ifdef LZW_DEBUG + printf("I: stream end position: %ld\n", hio_tell(f)); + #endif + + LZW_free(&lzw); + return 0; +} + +/* Decode Digital Symphony sigma-delta compressed samples. + * This isn't really LZW but it uses the same bitstream and alignment hacks. + * + * Based on the sigma-delta unpacker from OpenMPT by Saga Musix. + */ +int libxmp_read_sigma_delta(void *dest, size_t dest_len, size_t max_read_len, + HIO_HANDLE *f) +{ + struct bitstream bs; + uint8 *pos = (uint8 *)dest; + uint8 *end = pos + dest_len; + int max_runlength; + int runlength = 0; + int bits = 8; + uint8 accumulator; + + if (!dest_len) + return 0; + + bs_init(&bs, max_read_len); + + /* DOESN'T count towards alignment. */ + max_runlength = hio_read8(f); + /* DOES count. */ + accumulator = bs_read(&bs, f, bits); + *(pos++) = accumulator; + + while (pos < end) { + int value = bs_read(&bs, f, bits); + if (value < 0) + return -1; + + /* Expand bitwidth. */ + if (!value) { + if (bits >= 9) + return -1; + + bits++; + runlength = 0; + continue; + } + + if (value & 1) accumulator -= (value >> 1); + else accumulator += (value >> 1); + + *(pos++) = accumulator; + + /* High bit set resets the run length. */ + if (value >> (bits - 1)) { + runlength = 0; + continue; + } + /* Reduce bitwidth. */ + if (++runlength >= max_runlength) { + if (bits > 1) + bits--; + runlength = 0; + } + } + + /* Digital Symphony aligns bitstreams to lengths of 4. */ + if (bs.num_read & 3) { + size_t total = bs.num_read; + while (total & 3) { + hio_read8(f); + total++; + } + } + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/lzw.h b/thirdparty/libxmp/src/loaders/lzw.h new file mode 100644 index 000000000..12ecd4494 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/lzw.h @@ -0,0 +1,17 @@ +#ifndef LIBXMP_LOADER_LZW_H +#define LIBXMP_LOADER_LZW_H + +#include "loader.h" + +#define LZW_FLAG_MAXBITS(x) ((x) & 15) +#define LZW_FLAG_SYMQUIRKS 0x100 + +#define LZW_FLAGS_SYM LZW_FLAG_MAXBITS(13) | LZW_FLAG_SYMQUIRKS + +int libxmp_read_lzw(void *dest, size_t dest_len, size_t max_read_len, + int flags, HIO_HANDLE *f); + +int libxmp_read_sigma_delta(void *dest, size_t dest_len, size_t max_read_len, + HIO_HANDLE *f); + +#endif /* LIBXMP_LOADER_LZW_H */ diff --git a/thirdparty/libxmp/src/loaders/masi16_load.c b/thirdparty/libxmp/src/loaders/masi16_load.c new file mode 100644 index 000000000..c33d10db4 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/masi16_load.c @@ -0,0 +1,437 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +#define MAGIC_PSM_ MAGIC4('P','S','M',0xfe) + + +static int masi16_test (HIO_HANDLE *, char *, const int); +static int masi16_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_masi16 = { + "Epic MegaGames MASI 16", + masi16_test, + masi16_load +}; + +static int masi16_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_PSM_) + return -1; + + libxmp_read_title(f, t, 60); + + return 0; +} + + +static void masi16_translate_effect(struct xmp_event *event, uint8 effect, + uint8 param, uint8 param2, uint8 param3) +{ + switch (effect) { + case 1: /* Fine Volume Slide Up */ + event->fxt = FX_F_VSLIDE_UP; + event->fxp = param; + break; + case 2: /* Volume Slide Up */ + event->fxt = FX_VOLSLIDE_UP; + event->fxp = param; + break; + case 3: /* Fine Volume Slide Down */ + event->fxt = FX_F_VSLIDE_DN; + event->fxp = param; + break; + case 4: /* Volume Slide Down */ + event->fxt = FX_VOLSLIDE_DN; + event->fxp = param; + break; + + case 10: /* Fine Porta Up */ + event->fxt = FX_F_PORTA_UP; + event->fxp = param; + break; + case 11: /* Portamento Up */ + event->fxt = FX_PORTA_UP; + event->fxp = param; + break; + case 12: /* Fine Porta Down */ + event->fxt = FX_F_PORTA_DN; + event->fxp = param; + break; + case 13: /* Portamento Down */ + event->fxt = FX_PORTA_DN; + event->fxp = param; + break; + case 14: /* Tone Portamento */ + event->fxt = FX_TONEPORTA; + event->fxp = param; + break; + case 15: /* Glissando control */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_GLISS << 4) | (param & 0x0f); + break; + case 16: /* Tone Portamento + Volslide Up */ + event->fxt = FX_TONEPORTA; + event->fxp = 0; + event->f2t = FX_VOLSLIDE_UP; + event->f2p = param; + break; + case 17: /* Tone Portamento + Volslide Down */ + event->fxt = FX_TONEPORTA; + event->fxp = 0; + event->f2t = FX_VOLSLIDE_DN; + event->f2p = param; + break; + + case 20: /* Vibrato */ + event->fxt = FX_VIBRATO; + event->fxp = param; + break; + case 21: /* Vibrato waveform */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_VIBRATO_WF << 4) | (param & 0x0f); + break; + case 22: /* Vibrato + Volume Slide Up */ + event->fxt = FX_VIBRATO; + event->fxp = 0; + event->f2t = FX_VOLSLIDE_UP; + event->f2p = param; + break; + case 23: /* Vibrato + Volume Slide Down */ + event->fxt = FX_VIBRATO; + event->fxp = 0; + event->f2t = FX_VOLSLIDE_DN; + event->f2p = param; + break; + + case 30: /* Tremolo */ + event->fxt = FX_TREMOLO; + event->fxp = param; + break; + case 31: /* Tremolo waveform */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_TREMOLO_WF << 4) | (param & 0x0f); + break; + + case 40: /* Sample Offset */ + /* TODO: param and param3 are the fine and high offsets. */ + event->fxt = FX_OFFSET; + event->fxp = param2; + break; + case 41: /* Retrigger Note */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | (param & 0x0f); + break; + case 42: /* Note Cut */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_CUT << 4) | (param & 0x0f); + break; + case 43: /* Note Delay */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_DELAY << 4) | (param & 0x0f); + break; + + case 50: /* Position Jump */ + event->fxt = FX_JUMP; + event->fxp = param; + break; + case 51: /* Pattern Break */ + event->fxt = FX_BREAK; + event->fxp = param; + break; + case 52: /* Jump Loop */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_PATTERN_LOOP << 4) | (param & 0x0f); + break; + case 53: /* Pattern Delay */ + event->fxt = FX_PATT_DELAY; + event->fxp = param; + break; + + case 60: /* Set Speed */ + event->fxt = FX_S3M_SPEED; + event->fxp = param; + break; + case 61: /* Set BPM */ + event->fxt = FX_S3M_BPM; + event->fxp = param; + break; + + case 70: /* Arpeggio */ + event->fxt = FX_ARPEGGIO; + event->fxp = param; + break; + case 71: /* Set Finetune */ + event->fxt = FX_FINETUNE; + event->fxp = param; + break; + case 72: /* Set Balance */ + event->fxt = FX_SETPAN; + event->fxp = (param & 0x0f) | ((param & 0x0f) << 4); + break; + + default: + event->fxt = event->fxp = 0; + break; + } +} + +static int masi16_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, r, i; + struct xmp_event *event; + uint8 buf[1024]; + uint32 p_ord, p_chn, p_pat, p_ins; + uint32 p_smp[256]; + uint8 sample_map[256]; + int type, ver /*, mode*/; + int stored_ins; + + LOAD_INIT(); + + hio_read32b(f); + + hio_read(buf, 1, 60, f); + memcpy(mod->name, (char *)buf, 59); + mod->name[59] = '\0'; + + type = hio_read8(f); /* song type */ + ver = hio_read8(f); /* song version */ + /*mode =*/ hio_read8(f); /* pattern version */ + + if (type & 0x01) /* song mode not supported */ + return -1; + + libxmp_set_type(m, "Epic MegaGames MASI 16 PSM %d.%02d", MSN(ver), LSN(ver)); + + mod->spd = hio_read8(f); + mod->bpm = hio_read8(f); + hio_read8(f); /* master volume */ + hio_read16l(f); /* song length */ + mod->len = hio_read16l(f); + mod->pat = hio_read16l(f); + stored_ins = hio_read16l(f); + hio_read16l(f); /* ignore channels to play */ + mod->chn = hio_read16l(f); /* use channels to proceed */ + + /* Sanity check */ + if (mod->len > 256 || mod->pat > 256 || stored_ins > 255 || + mod->chn > XMP_MAX_CHANNELS) { + return -1; + } + mod->trk = mod->pat * mod->chn; + + p_ord = hio_read32l(f); + p_chn = hio_read32l(f); + p_pat = hio_read32l(f); + p_ins = hio_read32l(f); + + /* should be this way but fails with Silverball song 6 */ + //mod->flg |= ~type & 0x02 ? XXM_FLG_MODRNG : 0; + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + hio_seek(f, start + p_ord, SEEK_SET); + hio_read(mod->xxo, 1, mod->len, f); + + memset(buf, 0, mod->chn); + hio_seek(f, start + p_chn, SEEK_SET); + hio_read(buf, 1, 16, f); + + for (i = 0; i < mod->chn; i++) { + if (buf[i] < 16) { + mod->xxc[i].pan = buf[i] | (buf[i] << 4); + } + } + + /* Get the actual instruments count... */ + mod->ins = 0; + for (i = 0; i < stored_ins; i++) { + hio_seek(f, start + p_ins + 64 * i + 45, SEEK_SET); + sample_map[i] = hio_read16l(f) - 1; + mod->ins = MAX(mod->ins, sample_map[i] + 1); + } + if (mod->ins > 255 || hio_error(f)) + return -1; + + mod->smp = mod->ins; + + if (libxmp_init_instrument(m) < 0) + return -1; + + memset(p_smp, 0, sizeof(p_smp)); + + hio_seek(f, start + p_ins, SEEK_SET); + for (i = 0; i < stored_ins; i++) { + struct xmp_instrument *xxi; + struct xmp_sample *xxs; + struct xmp_subinstrument *sub; + uint16 flags, c2spd; + int finetune; + int num = sample_map[i]; + + if (hio_read(buf, 1, 64, f) < 64) + return -1; + + xxi = &mod->xxi[num]; + xxs = &mod->xxs[num]; + + /* Don't load duplicate instruments */ + if (xxi->sub) + continue; + + if (libxmp_alloc_subinstrument(mod, num, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + /*hio_read(buf, 1, 13, f);*/ /* sample filename */ + /*hio_read(buf, 1, 24, f);*/ /* sample description */ + memcpy(xxi->name, buf + 13, 24); + xxi->name[24] = '\0'; + p_smp[i] = readmem32l(buf + 37); + /*hio_read32l(f);*/ /* memory location */ + /*hio_read16l(f);*/ /* sample number */ + flags = buf[47]; /* sample type */ + xxs->len = readmem32l(buf + 48); + xxs->lps = readmem32l(buf + 52); + xxs->lpe = readmem32l(buf + 56); + finetune = buf[60]; + sub->vol = buf[61]; + c2spd = readmem16l(buf + 62); + sub->pan = 0x80; + sub->sid = num; + xxs->flg = flags & 0x80 ? XMP_SAMPLE_LOOP : 0; + xxs->flg |= flags & 0x20 ? XMP_SAMPLE_LOOP_BIDIR : 0; + + libxmp_c2spd_to_note(c2spd, &sub->xpo, &sub->fin); + sub->fin += (int8)((finetune & 0x0f) << 4); + sub->xpo += (finetune >> 4) - 7; + + /* The documentation claims samples shouldn't exceed 64k. The + * PS16 modules from Silverball and Epic Pinball confirm this. + * Later Protracker Studio Modules (MASI) allow up to 1MB. + */ + if ((uint32)xxs->len > 64 * 1024) { + D_(D_CRIT "invalid sample %d length %d", num, xxs->len); + return -1; + } + + if (xxs->len > 0) + xxi->nsm = 1; + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %5d", + num, xxi->name, xxs->len, xxs->lps, + xxs->lpe, xxs->flg & XMP_SAMPLE_LOOP ? + 'L' : ' ', sub->vol, c2spd); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + hio_seek(f, start + p_pat, SEEK_SET); + for (i = 0; i < mod->pat; i++) { + int len; + uint8 b, rows, chan; + + len = hio_read16l(f) - 4; + rows = hio_read8(f); + if (rows > 64) { + return -1; + } + chan = hio_read8(f); + if (chan > 32) { + return -1; + } + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + for (r = 0; r < rows; r++) { + while (len > 0) { + b = hio_read8(f); + len--; + + if (b == 0) + break; + + c = b & 0x0f; + if (c >= mod->chn) + return -1; + event = &EVENT(i, c, r); + + if (b & 0x80) { + event->note = hio_read8(f) + 36; + event->ins = hio_read8(f); + len -= 2; + } + + if (b & 0x40) { + event->vol = hio_read8(f) + 1; + len--; + } + + if (b & 0x20) { + uint8 effect = hio_read8(f); + uint8 param = hio_read8(f); + uint8 param2 = 0; + uint8 param3 = 0; + + if (effect == 40) { /* Sample Offset */ + param2 = hio_read8(f); + param3 = hio_read8(f); + } + masi16_translate_effect(event, effect, param, param2, param3); + len -= 2; + } + } + } + + if (len > 0) + hio_seek(f, len, SEEK_CUR); + } + + /* Read samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < stored_ins; i++) { + struct xmp_sample *xxs = &mod->xxs[sample_map[i]]; + + /* Don't load duplicate sample data */ + if (xxs->data) + continue; + + hio_seek(f, start + p_smp[i], SEEK_SET); + if (libxmp_load_sample(m, f, SAMPLE_FLAG_DIFF, xxs, NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/masi_load.c b/thirdparty/libxmp/src/loaders/masi_load.c new file mode 100644 index 000000000..8484939cd --- /dev/null +++ b/thirdparty/libxmp/src/loaders/masi_load.c @@ -0,0 +1,835 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Originally based on the PSM loader from Modplug by Olivier Lapicque and + * fixed comparing the One Must Fall! PSMs with Kenny Chou's MTM files. + */ + +/* + * From EPICTEST Readme.1st: + * + * The Music And Sound Interface, MASI, is the basis behind all new Epic + * games. MASI uses its own proprietary file format, PSM, for storing + * its music. + */ + +/* + * kode54's comment on Sinaria PSMs in the foo_dumb hydrogenaudio forum: + * + * "The Sinaria variant uses eight character pattern and instrument IDs, + * the sample headers are laid out slightly different, and the patterns + * use a different format for the note values, and also different effect + * scales for certain commands. + * + * [Epic] PSM uses high nibble for octave and low nibble for note, for + * a valid range up to 0x7F, for a range of D-1 through D#9 compared to + * IT. (...) Sinaria PSM uses plain note values, from 1 - 83, for a + * range of C-3 through B-9. + * + * [Epic] PSM also uses an effect scale for portamento, volume slides, + * and vibrato that is about four times as sensitive as the IT equivalents. + * Sinaria does not. This seems to coincide with the MOD/S3M to PSM + * converter that Joshua Jensen released in the EPICTEST.ZIP file which + * can still be found on a few FTP sites. It converted effects literally, + * even though the bundled players behaved as the libraries used with + * Epic's games did and made the effects sound too strong." + */ + +/* + * Claudio's note: Sinaria seems to have a finetune byte just before + * volume and some kind of (stereo?) interleaved sample, with 16-byte + * frames (see Sinaria songs 5 and 8). Sinaria song 10 still sounds + * ugly, maybe caused by finetune issues? + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +#define MAGIC_PSM_ MAGIC4('P','S','M',' ') +#define MAGIC_FILE MAGIC4('F','I','L','E') +#define MAGIC_TITL MAGIC4('T','I','T','L') +#define MAGIC_OPLH MAGIC4('O','P','L','H') +#define MAGIC_PPAN MAGIC4('P','P','A','N') + + +static int masi_test (HIO_HANDLE *, char *, const int); +static int masi_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_masi = { + "Epic MegaGames MASI", + masi_test, + masi_load +}; + +static int masi_test(HIO_HANDLE *f, char *t, const int start) +{ + int val; + + if (hio_read32b(f) != MAGIC_PSM_) + return -1; + + hio_read8(f); + hio_read8(f); + hio_read8(f); + if (hio_read8(f) != 0) + return -1; + + if (hio_read32b(f) != MAGIC_FILE) + return -1; + + hio_read32b(f); + val = hio_read32l(f); + hio_seek(f, val, SEEK_CUR); + + if (hio_read32b(f) == MAGIC_TITL) { + val = hio_read32l(f); + libxmp_read_title(f, t, val); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + +struct local_data { + int sinaria; + int cur_pat; + int cur_ins; + uint8 *pnam; + uint8 *pord; +}; + +static int get_sdft(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + return 0; +} + +static int get_titl(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + char buf[XMP_NAME_SIZE]; + + size = size > XMP_NAME_SIZE - 1 ? XMP_NAME_SIZE - 1 : size; + size = hio_read(buf, 1, size, f); + strncpy(mod->name, buf, size); + mod->name[size] = '\0'; + + return 0; +} + +static int get_dsmp_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + mod->ins++; + mod->smp = mod->ins; + + return 0; +} + +static int get_pbod_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + char buf[20]; + + mod->pat++; + if (hio_read(buf, 1, 20, f) < 20) { + D_(D_CRIT "read error at pat %d", mod->pat - 1); + return -1; + } + if (buf[9] != 0 && buf[13] == 0) + data->sinaria = 1; + + return 0; +} + + +static int get_dsmp(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + struct local_data *data = (struct local_data *)parm; + int i, srate, flags; + int finetune; + + flags = hio_read8(f); /* flags */ + hio_seek(f, 8, SEEK_CUR); /* songname */ + hio_seek(f, data->sinaria ? 8 : 4, SEEK_CUR); /* smpid */ + + i = data->cur_ins; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + xxi = &mod->xxi[i]; + sub = &xxi->sub[0]; + xxs = &mod->xxs[i]; + + hio_read(xxi->name, 1, 31, f); + hio_seek(f, 8, SEEK_CUR); + hio_read8(f); /* insno */ + hio_read8(f); + xxs->len = hio_read32l(f); + xxs->lps = hio_read32l(f); + xxs->lpe = hio_read32l(f); + xxs->flg = flags & 0x80 ? XMP_SAMPLE_LOOP : 0; + hio_read16l(f); + + if ((int32)xxs->lpe < 0) + xxs->lpe = 0; + + if (xxs->len > 0) + xxi->nsm = 1; + + finetune = 0; + if (data->sinaria) { + finetune = (int8)(hio_read8s(f) << 4); + } + + sub->vol = hio_read8(f) / 2 + 1; + hio_read32l(f); + sub->pan = 0x80; + sub->sid = i; + srate = hio_read16l(f); + + D_(D_INFO "[%2X] %-32.32s %05x %05x %05x %c V%02x %+04d %5d", i, + xxi->name, xxs->len, xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, finetune, srate); + + libxmp_c2spd_to_note(srate, &sub->xpo, &sub->fin); + sub->fin += finetune; + + hio_seek(f, 16, SEEK_CUR); + if (libxmp_load_sample(m, f, SAMPLE_FLAG_8BDIFF, xxs, NULL) < 0) + return -1; + + data->cur_ins++; + + return 0; +} + + +static uint8 convert_porta(uint8 param, int sinaria) +{ + if (sinaria) { + return param; + } + + if (param < 4) { + return param | 0xf0; + } else { + return param >> 2; + } +} + +static int get_pbod(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, r; + struct xmp_event *event, dummy; + uint8 flag, chan; + /* uint32 len; */ + int rows, rowlen; + + i = data->cur_pat; + + /*len =*/ hio_read32l(f); + hio_read(data->pnam + i * 8, 1, data->sinaria ? 8 : 4, f); + + rows = hio_read16l(f); + if (hio_error(f)) { + return -1; + } + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + r = 0; + + do { + rowlen = hio_read16l(f) - 2; + if (hio_error(f)) { + return -1; + } + while (rowlen > 0) { + flag = hio_read8(f); + + if (rowlen == 1) + break; + + chan = hio_read8(f); + rowlen -= 2; + + event = chan < mod->chn ? &EVENT(i, chan, r) : &dummy; + + if (flag & 0x80) { + uint8 note = hio_read8(f); + rowlen--; + if (data->sinaria) + note += 36; + else + note = (note >> 4) * 12 + (note & 0x0f) + 1 + 12; + event->note = note; + } + + if (flag & 0x40) { + event->ins = hio_read8(f) + 1; + rowlen--; + } + + if (flag & 0x20) { + event->vol = hio_read8(f) / 2 + 1; + rowlen--; + } + + if (flag & 0x10) { + uint8 fxt = hio_read8(f); + uint8 fxp = hio_read8(f); + rowlen -= 2; + +#if 0 + /* compressed events */ + if (fxt >= 0x40) { + switch (fxp >> 4) { + case 0x0: { + uint8 note; + note = (fxt>>4)*12 + + (fxt & 0x0f) + 1; + event->note = note; + fxt = FX_TONEPORTA; + fxp = (fxp + 1) * 2; + break; } + default: +D_(D_CRIT "p%d r%d c%d: compressed event %02x %02x\n", i, r, chan, fxt, fxp); + return -1; + } + } else +#endif + + switch (fxt) { + + /* Volume slide */ + case 0x01: /* fine volslide up */ + fxt = FX_EXTENDED; + fxp = (EX_F_VSLIDE_UP << 4) | + ((fxp / 2) & 0x0f); + break; + case 0x02: /* volslide up */ + fxt = FX_VOLSLIDE; + fxp = (fxp / 2) << 4; + break; + case 0x03: /* fine volslide down */ + fxt = FX_EXTENDED; + fxp = (EX_F_VSLIDE_DN << 4) | + ((fxp / 2) & 0x0f); + break; + case 0x04: /* volslide down */ + fxt = FX_VOLSLIDE; + fxp /= 2; + break; + + /* Portamento */ + case 0x0b: /* fine portamento up */ + fxt = FX_PORTA_UP; + fxp = (EX_F_PORTA_UP << 4) | + convert_porta(fxp, data->sinaria); + break; + case 0x0c: /* portamento up */ + fxt = FX_PORTA_UP; + fxp = convert_porta(fxp, data->sinaria); + break; + case 0x0d: /* fine portamento up */ + fxt = FX_PORTA_DN; + fxp = (EX_F_PORTA_DN << 4) | + convert_porta(fxp, data->sinaria); + break; + case 0x0e: /* portamento down */ + fxt = FX_PORTA_DN; + fxp = convert_porta(fxp, data->sinaria); + break; + case 0x0f: /* tone portamento */ + fxt = FX_TONEPORTA; + fxp >>= 2; + break; + case 0x10: /* toneporta + vslide up */ + fxt = FX_TONE_VSLIDE; + fxp = fxt & 0xf0; + break; + case 0x11: /* glissando */ + fxt = FX_EXTENDED; + fxp = (EX_GLISS << 4) | (fxp & 0x0f); + break; + case 0x12: /* toneporta + vslide down */ + fxt = FX_TONE_VSLIDE; + fxp >>= 4; + break; + + /* 0x13: S3M S: crashes MASI */ + + /* Vibrato */ + case 0x15: /* vibrato */ + fxt = data->sinaria ? + FX_VIBRATO : FX_FINE_VIBRATO; + /* fxp remains the same */ + break; + case 0x16: /* vibrato waveform */ + fxt = FX_EXTENDED; + fxp = (EX_VIBRATO_WF << 4) | (fxp & 0x0f); + break; + case 0x17: /* vibrato + vslide up */ + fxt = FX_VIBRA_VSLIDE; + fxp >>= 4; + break; + case 0x18: /* vibrato + vslide down */ + fxt = FX_VIBRA_VSLIDE; + fxp = fxp & 0x0f; + break; + + /* Tremolo */ + case 0x1f: /* tremolo */ + fxt = FX_TREMOLO; + /* fxp remains the same */ + break; + case 0x20: /* tremolo waveform */ + fxt = FX_EXTENDED; + fxp = (EX_TREMOLO_WF << 4) | (fxp & 0x0f); + break; + + /* Sample commands */ + case 0x29: /* 3-byte offset */ + fxt = FX_OFFSET; + /* use only the middle byte */ + fxp = hio_read8(f); + hio_read8(f); + rowlen -= 2; + break; + case 0x2a: /* retrig note */ + fxt = FX_EXTENDED; + fxp = (EX_RETRIG << 4) | (fxp & 0x0f); + break; + case 0x2b: /* note cut */ + fxt = FX_EXTENDED; + fxp = (EX_CUT << 4) | (fxp & 0x0f); + break; + case 0x2c: /* note delay */ + fxt = FX_EXTENDED; + fxp = (EX_DELAY << 4) | (fxp & 0x0f); + break; + + /* Position change */ + case 0x33: /* position jump */ + /* not used in MASI */ + fxt = FX_JUMP; + fxp >>= 1; + hio_read8(f); + rowlen--; + break; + case 0x34: /* pattern break */ + /* not used in MASI */ + fxt = FX_BREAK; + break; + case 0x35: /* pattern loop */ + fxt = FX_EXTENDED; + fxp = (EX_PATTERN_LOOP << 4) | (fxp & 0x0f); + break; + case 0x36: /* pattern delay */ + fxt = FX_EXTENDED; + fxp = (EX_PATT_DELAY << 4) | (fxp & 0x0f); + break; + + /* Speed change */ + case 0x3d: /* speed */ + fxt = FX_SPEED; + break; + case 0x3e: /* tempo */ + fxt = FX_SPEED; + break; + + /* Other */ + case 0x47: /* arpeggio */ + fxt = FX_S3M_ARPEGGIO; + break; + case 0x48: /* set finetune */ + fxt = FX_EXTENDED; + fxp = (EX_FINETUNE << 4) | (fxp & 0x0f); + break; + case 0x49: /* set pan */ + fxt = FX_SETPAN; + fxp <<= 4; + break; + + default: +D_(D_CRIT "p%d r%d c%d: unknown effect %02x %02x\n", i, r, chan, fxt, fxp); + fxt = fxp = 0; + } + + event->fxt = fxt; + event->fxp = fxp; + } + } + r++; + } while (r < rows); + + data->cur_pat++; + + return 0; +} + +static int get_song(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + hio_seek(f, 10, SEEK_CUR); + mod->chn = hio_read8(f); + + return 0; +} + +static int subchunk_oplh(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int first_order_chunk = INT_MAX; + int num_chunk, i; + + /* First two bytes = Number of chunks that follow */ + num_chunk = hio_read16l(f); + + /* Sub sub chunks */ + for (i = 0; i < num_chunk && size > 0; i++) { + int opcode = hio_read8(f); + + size--; + + if (opcode == 0) { /* last sub sub chunk */ + break; + } + + /* Saga Musix's note in OpenMPT: + * + * "This is more like a playlist than a collection of global + * values. In theory, a tempo item inbetween two order items + * should modify the tempo when switching patterns. No module + * uses this feature in practice though, so we can keep our + * loader simple. Unimplemented opcodes do nothing or freeze + * MASI." + */ + switch (opcode) { + case 0x01: /* Play order list item */ + if (mod->len >= XMP_MAX_MOD_LENGTH) { + return -1; + } + hio_read(data->pord + mod->len * 8, 1, data->sinaria ? 8 : 4, f); + size -= data->sinaria ? 8 : 4; + mod->len++; + if (first_order_chunk == INT_MAX) { + first_order_chunk = i; + } + break; + + /* 0x02: Play range */ + /* 0x03: Jump loop */ + + case 0x04: { /* Jump line (restart position) */ + int restart_chunk = hio_read16l(f); + size -= 2; + + /* This jumps to the command line, but since we're converting + * play order list items to our order list, only change the + * restart position if it's after the first order chunk. + */ + + if (restart_chunk >= first_order_chunk) { + mod->rst = restart_chunk - first_order_chunk; + } + + break; } + + /* 0x05: Channel flip */ + /* 0x06: Transpose */ + + case 0x07: /* Default speed */ + mod->spd = hio_read8(f); + size--; + break; + case 0x08: /* Default tempo */ + mod->bpm = hio_read8(f); + size--; + break; + case 0x0c: /* Sample map table */ + hio_read16l(f); + hio_read16l(f); + hio_read16l(f); + size -= 6; + break; + case 0x0d: { /* Channel panning table */ + int chn = hio_read8(f); + int pan = hio_read8(f); + int type = hio_read8(f); + struct xmp_channel *xxc; + + if (chn >= XMP_MAX_CHANNELS) { + break; + } + + xxc = &mod->xxc[chn]; + + size -= 3; + + switch (type) { + case 0: /* use panning */ + xxc->pan = pan ^ 0x80; + break; + case 2: /* surround */ + xxc->pan = 0x80; + xxc->flg |= XMP_CHANNEL_SURROUND; + break; + case 4: /* center */ + xxc->pan = 0x80; + break; + } + break; } + case 0x0e: { /* Channel volume table */ + int chn = hio_read8(f); + int vol = hio_read8(f); + struct xmp_channel *xxc; + + if (chn >= XMP_MAX_CHANNELS) { + break; + } + + xxc = &mod->xxc[chn]; + + size -= 2; + + xxc->vol = (vol >> 2) + 1; + break; } + default: + /*printf("channel %d: %02x %02x\n", i, c, hio_read8(f));*/ + return -1; + } + } + + return 0; +} + +/* Sinaria channel panning table */ +static int subchunk_ppan(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + int i; + + for (i = 0; i < XMP_MAX_CHANNELS && size > 0; i++) { + struct xmp_channel *xxc = &mod->xxc[i]; + int type = hio_read8(f); + int pan = hio_read8(f); + + size -= 2; + + switch (type) { + case 0: /* use panning */ + xxc->pan = pan ^ 0x80; + break; + case 2: /* surround */ + xxc->pan = 0x80; + xxc->flg |= XMP_CHANNEL_SURROUND; + break; + case 4: /* center */ + xxc->pan = 0x80; + break; + } + } + + return 0; +} + +/* Subchunk loader based on OpenMPT LoadPSM.cpp */ +static int get_song_2(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + uint32 magic; + char buf[20]; + + hio_read(buf, 1, 9, f); + hio_read16l(f); + size -= 11; + + D_(D_INFO "Subsong title: %-9.9s", buf); + + /* Iterate over subchunks. We want OPLH and PPAN */ + while (size > 0) { + int subchunk_size; + + magic = hio_read32b(f); + subchunk_size = hio_read32l(f); + if (subchunk_size <= 0 || hio_error(f)) { + return -1; + } + + size -= subchunk_size; + + switch (magic) { + case MAGIC_OPLH: + if (subchunk_oplh(m, size, f, parm) < 0) { + return -1; + } + break; + + case MAGIC_PPAN: + if (subchunk_ppan(m, size, f, parm) < 0) { + return -1; + } + break; + + default: + hio_seek(f, subchunk_size, SEEK_CUR); + } + } + + return 0; +} + +static int masi_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + int ret, offset; + int i, j; + struct local_data data; + + LOAD_INIT(); + + hio_read32b(f); + + data.sinaria = 0; + mod->name[0] = 0; + + hio_seek(f, 8, SEEK_CUR); /* skip file size and FILE */ + mod->smp = mod->ins = 0; + data.cur_pat = 0; + data.cur_ins = 0; + offset = hio_tell(f); + + handle = libxmp_iff_new(); + if (handle == NULL) + goto err; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "TITL", get_titl); + ret |= libxmp_iff_register(handle, "SDFT", get_sdft); + ret |= libxmp_iff_register(handle, "SONG", get_song); + ret |= libxmp_iff_register(handle, "DSMP", get_dsmp_cnt); + ret |= libxmp_iff_register(handle, "PBOD", get_pbod_cnt); + + if (ret != 0) + goto err; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + goto err; + } + + libxmp_iff_release(handle); + + mod->trk = mod->pat * mod->chn; + data.pnam = (uint8 *) malloc(mod->pat * 8); /* pattern names */ + if (data.pnam == NULL) + goto err; + + data.pord = (uint8 *) malloc(XMP_MAX_MOD_LENGTH * 8); /* pattern orders */ + if (data.pord == NULL) + goto err2; + + libxmp_set_type(m, data.sinaria ? + "Sinaria PSM" : "Epic MegaGames MASI PSM"); + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + goto err3; + + if (libxmp_init_pattern(mod) < 0) + goto err3; + + D_(D_INFO "Stored patterns: %d", mod->pat); + D_(D_INFO "Stored samples : %d", mod->smp); + + hio_seek(f, start + offset, SEEK_SET); + + mod->len = 0; + + handle = libxmp_iff_new(); + if (handle == NULL) + goto err3; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "SONG", get_song_2); + ret |= libxmp_iff_register(handle, "DSMP", get_dsmp); + ret |= libxmp_iff_register(handle, "PBOD", get_pbod); + + if (ret != 0) + goto err3; + + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + goto err3; + } + + libxmp_iff_release(handle); + + for (i = 0; i < mod->len; i++) { + for (j = 0; j < mod->pat; j++) { + if (!memcmp(data.pord + i * 8, data.pnam + j * 8, data.sinaria ? 8 : 4)) { + mod->xxo[i] = j; + break; + } + } + + if (j == mod->pat) + break; + } + + free(data.pord); + free(data.pnam); + + return 0; + + err3: + free(data.pord); + err2: + free(data.pnam); + err: + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/mdl_load.c b/thirdparty/libxmp/src/loaders/mdl_load.c new file mode 100644 index 000000000..5413857a4 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mdl_load.c @@ -0,0 +1,1252 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Note: envelope switching (effect 9) and sample status change (effect 8) + * not supported. + */ + +#include "loader.h" +#include "iff.h" +#include "../period.h" + +#define MAGIC_DMDL MAGIC4('D','M','D','L') + + +static int mdl_test (HIO_HANDLE *, char *, const int); +static int mdl_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mdl = { + "Digitrakker", + mdl_test, + mdl_load +}; + +static int mdl_test(HIO_HANDLE *f, char *t, const int start) +{ + uint16 id; + + if (hio_read32b(f) != MAGIC_DMDL) + return -1; + + hio_read8(f); /* version */ + id = hio_read16b(f); + + if (id == 0x494e) { /* IN */ + hio_read32b(f); + libxmp_read_title(f, t, 32); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + + +#define MDL_NOTE_FOLLOWS 0x04 +#define MDL_INSTRUMENT_FOLLOWS 0x08 +#define MDL_VOLUME_FOLLOWS 0x10 +#define MDL_EFFECT_FOLLOWS 0x20 +#define MDL_PARAMETER1_FOLLOWS 0x40 +#define MDL_PARAMETER2_FOLLOWS 0x80 + + +struct mdl_envelope { + uint8 num; + uint8 data[30]; + uint8 sus; + uint8 loop; +}; + +struct local_data { + int *i_index; + int *s_index; + int *v_index; /* volume envelope */ + int *p_index; /* pan envelope */ + int *f_index; /* pitch envelope */ + int *packinfo; + int has_in; + int has_pa; + int has_tr; + int has_ii; + int has_is; + int has_sa; + int v_envnum; + int p_envnum; + int f_envnum; + struct mdl_envelope *v_env; + struct mdl_envelope *p_env; + struct mdl_envelope *f_env; +}; + + +static void fix_env(int i, struct xmp_envelope *ei, struct mdl_envelope *env, + int *idx, int envnum) +{ + int j, k, lastx; + + if (idx[i] >= 0) { + ei->flg = XMP_ENVELOPE_ON; + ei->npt = 15; + + for (j = 0; j < envnum; j++) { + if (idx[i] == env[j].num) { + ei->flg |= env[j].sus & 0x10 ? XMP_ENVELOPE_SUS : 0; + ei->flg |= env[j].sus & 0x20 ? XMP_ENVELOPE_LOOP : 0; + ei->sus = env[j].sus & 0x0f; + ei->lps = env[j].loop & 0x0f; + ei->lpe = env[j].loop & 0xf0; + + lastx = -1; + + for (k = 0; k < ei->npt; k++) { + int x = env[j].data[k * 2]; + + if (x == 0) + break; + ei->data[k * 2] = lastx + x; + ei->data[k * 2 + 1] = env[j].data[k * 2 + 1]; + + lastx = ei->data[k * 2]; + } + + ei->npt = k; + break; + } + } + } +} + + +/* Effects 1-6 (note effects) can only be entered in the first effect + * column, G-L (volume-effects) only in the second column. + */ + +static void xlat_fx_common(uint8 *t, uint8 *p) +{ + switch (*t) { + case 0x07: /* 7 - Set BPM */ + *t = FX_S3M_BPM; + break; + case 0x08: /* 8 - Set pan */ + case 0x09: /* 9 - Set envelope -- not supported */ + case 0x0a: /* A - Not used */ + *t = *p = 0x00; + break; + case 0x0b: /* B - Position jump */ + case 0x0c: /* C - Set volume */ + case 0x0d: /* D - Pattern break */ + /* Like protracker */ + break; + case 0x0e: /* E - Extended */ + switch (MSN (*p)) { + case 0x0: /* E0 - not used */ + case 0x3: /* E3 - not used */ + case 0x8: /* Set sample status -- unsupported */ + *t = *p = 0x00; + break; + case 0x1: /* Pan slide left */ + *t = FX_PANSLIDE; + *p <<= 4; + break; + case 0x2: /* Pan slide right */ + *t = FX_PANSLIDE; + *p &= 0x0f; + break; + } + break; + case 0x0f: + *t = FX_S3M_SPEED; + break; + } +} + +static void xlat_fx1(uint8 *t, uint8 *p) +{ + switch (*t) { + case 0x00: /* - - No effect */ + *p = 0; + break; + case 0x05: /* 5 - Arpeggio */ + *t = FX_ARPEGGIO; + break; + case 0x06: /* 6 - Not used */ + *t = *p = 0x00; + break; + } + + xlat_fx_common(t, p); +} + + +static void xlat_fx2(uint8 *t, uint8 *p) +{ + switch (*t) { + case 0x00: /* - - No effect */ + *p = 0; + break; + case 0x01: /* G - Volume slide up */ + *t = FX_VOLSLIDE_UP; + break; + case 0x02: /* H - Volume slide down */ + *t = FX_VOLSLIDE_DN; + break; + case 0x03: /* I - Multi-retrig */ + *t = FX_MULTI_RETRIG; + break; + case 0x04: /* J - Tremolo */ + *t = FX_TREMOLO; + break; + case 0x05: /* K - Tremor */ + *t = FX_TREMOR; + break; + case 0x06: /* L - Not used */ + *t = *p = 0x00; + break; + } + + xlat_fx_common(t, p); +} + +struct bits { + uint32 b, n; +}; + +static unsigned int get_bits(char i, uint8 **buf, int *len, struct bits *bits) +{ + unsigned int x; + + if (i == 0) { + bits->b = readmem32l(*buf); + *buf += 4; *len -= 4; + bits->n = 32; + } + + x = bits->b & ((1 << i) - 1); /* get i bits */ + bits->b >>= i; + if ((bits->n -= i) <= 24) { + if (*len <= 0) /* FIXME: last few bits can't be consumed */ + return x; + bits->b |= readmem32l((*buf)++) << bits->n; + bits->n += 8; + (*len)--; + } + + return x; +} + +/* From the Digitrakker docs: + * + * The description of the sample-packmethode (1) [8bit packing]:... + * ---------------------------------------------------------------- + * + * The method is based on the Huffman algorithm. It's easy and very fast + * and effective on samples. The packed sample is a bit stream: + * + * Byte 0 Byte 1 Byte 2 Byte 3 + * Bit 76543210 fedcba98 nmlkjihg ....rqpo + * + * A packed byte is stored in the following form: + * + * xxxx10..0s => byte = + (number of <0> bits between + * s and 1) * 16 - 8; + * if s==1 then byte = byte xor 255 + * + * If there are no <0> bits between the first bit (sign) and the <1> bit, + * you have the following form: + * + * xxx1s => byte = ; if s=1 then byte = byte xor 255 + */ + +static int unpack_sample8(uint8 *t, uint8 *f, int len, int l) +{ + int i, s; + uint8 b, d; + struct bits bits; + + D_(D_INFO "unpack sample 8bit, len=%d", len); + get_bits(0, &f, &len, &bits); + + for (i = b = d = 0; i < l; i++) { + + /* Sanity check */ + if (len < 0) + return -1; + + s = get_bits(1, &f, &len, &bits); + if (get_bits(1, &f, &len, &bits)) { + b = get_bits(3, &f, &len, &bits); + } else { + b = 8; + while (len >= 0 && get_bits(1, &f, &len, &bits) == 0) { + /* Sanity check */ + if (b >= 240) { return -1; } + b += 16; + } + b += get_bits(4, &f, &len, &bits); + } + + if (s) { + b ^= 0xff; + } + + d += b; + *t++ = d; + } + + return 0; +} + +/* + * The description of the sample-packmethode (2) [16bit packing]:... + * ---------------------------------------------------------------- + * + * It works as methode (1) but it only crunches every 2nd byte (the high- + * bytes of 16 bit samples). So when you depack 16 bit samples, you have to + * read 8 bits from the data-stream first. They present the lowbyte of the + * sample-word. Then depack the highbyte in the descripted way (methode [1]). + * Only the highbytes are delta-values. So take the lowbytes as they are. + * Go on this way for the whole sample! + */ + +static int unpack_sample16(uint8 *t, uint8 *f, int len, int l) +{ + int i, lo, s; + uint8 b, d; + struct bits bits; + + D_(D_INFO "unpack sample 16bit, len=%d", len); + get_bits(0, &f, &len, &bits); + + for (i = lo = b = d = 0; i < l; i++) { + /* Sanity check */ + if (len < 0) + return -1; + + lo = get_bits(8, &f, &len, &bits); + s = get_bits(1, &f, &len, &bits); + if (get_bits(1, &f, &len, &bits)) { + b = get_bits(3, &f, &len, &bits); + } else { + b = 8; + while (len >= 0 && get_bits(1, &f, &len, &bits) == 0) { + /* Sanity check */ + if (b >= 240) { return -1; } + b += 16; + } + b += get_bits(4, &f, &len, &bits); + } + + if (s) + b ^= 0xff; + d += b; + + *t++ = lo; + *t++ = d; + } + + return 0; +} + + +/* + * IFF chunk handlers + */ + +static int get_chunk_in(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->has_in) { + D_(D_CRIT "duplicate IN chunk"); + return -1; + } + data->has_in = 1; + + hio_read(mod->name, 1, 32, f); + mod->name[32] = '\0'; + hio_seek(f, 20, SEEK_CUR); + + mod->len = hio_read16l(f); + mod->rst = hio_read16l(f); + hio_read8(f); /* gvol */ + mod->spd = hio_read8(f); + mod->bpm = hio_read8(f); + + /* Sanity check */ + if (mod->len > 256 || mod->rst > 255) { + return -1; + } + + for (i = 0; i < 32; i++) { + uint8 chinfo = hio_read8(f); + if (chinfo & 0x80) + break; + mod->xxc[i].pan = chinfo << 1; + } + mod->chn = i; + hio_seek(f, 32 - i - 1, SEEK_CUR); + + if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) { + D_(D_CRIT "read error at order list"); + return -1; + } + + MODULE_INFO(); + + return 0; +} + +static int get_chunk_pa(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j, chn; + int x; + + /* Sanity check */ + if (data->has_pa || !data->has_in) { + D_(D_CRIT "duplicate PA chunk or missing IN chunk"); + return -1; + } + data->has_pa = 1; + + mod->pat = hio_read8(f); + + mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *)); + if (mod->xxp == NULL) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + + chn = hio_read8(f); + mod->xxp[i]->rows = (int)hio_read8(f) + 1; + + hio_seek(f, 16, SEEK_CUR); /* Skip pattern name */ + for (j = 0; j < chn; j++) { + x = hio_read16l(f); + + if (j < mod->chn) + mod->xxp[i]->index[j] = x; + } + } + + return 0; +} + +static int get_chunk_p0(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j; + uint16 x; + + /* Sanity check */ + if (data->has_pa || !data->has_in) { + D_(D_CRIT "duplicate PA (0.0) chunk or missing IN chunk"); + return -1; + } + data->has_pa = 1; + + mod->pat = hio_read8(f); + + mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *)); + if (mod->xxp == NULL) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + mod->xxp[i]->rows = 64; + + for (j = 0; j < 32; j++) { + x = hio_read16l(f); + + if (j < mod->chn) + mod->xxp[i]->index[j] = x; + } + } + + return 0; +} + +static int get_chunk_tr(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j, k, row, len, max_trk; + struct xmp_track *track; + + /* Sanity check */ + if (data->has_tr || !data->has_pa) { + D_(D_CRIT "duplicate TR chunk or missing PA chunk"); + return -1; + } + data->has_tr = 1; + + mod->trk = hio_read16l(f) + 1; + + /* Sanity check */ + max_trk = 0; + for (i = 0; i < mod->pat; i++) { + for (j = 0; j < mod->chn; j++) { + if (max_trk < mod->xxp[i]->index[j]) + max_trk = mod->xxp[i]->index[j]; + } + } + if (max_trk >= mod->trk) { + return -1; + } + + mod->xxt = (struct xmp_track **) calloc(mod->trk, sizeof(struct xmp_track *)); + if (mod->xxt == NULL) + return -1; + + D_(D_INFO "Stored tracks: %d", mod->trk); + + track = (struct xmp_track *) calloc(1, sizeof(struct xmp_track) + + sizeof(struct xmp_event) * 255); + if (track == NULL) + goto err; + + /* Empty track 0 is not stored in the file */ + if (libxmp_alloc_track(mod, 0, 256) < 0) + goto err2; + + for (i = 1; i < mod->trk; i++) { + /* Length of the track in bytes */ + len = hio_read16l(f); + + memset(track, 0, sizeof(struct xmp_track) + + sizeof(struct xmp_event) * 255); + + for (row = 0; len;) { + struct xmp_event *ev; + + /* Sanity check */ + if (row > 255) { + goto err2; + } + + ev = &track->event[row]; + + j = hio_read8(f); + + len--; + switch (j & 0x03) { + case 0: + row += j >> 2; + break; + case 1: + /* Sanity check */ + if (row < 1 || row + (j >> 2) > 255) + goto err2; + + for (k = 0; k <= (j >> 2); k++) + memcpy(&ev[k], &ev[-1], sizeof (struct xmp_event)); + row += k - 1; + break; + case 2: + /* Sanity check */ + if ((j >> 2) == row) { + goto err2; + } + memcpy(ev, &track->event[j >> 2], sizeof (struct xmp_event)); + break; + case 3: + if (j & MDL_NOTE_FOLLOWS) { + uint8 b = hio_read8(f); + len--; + ev->note = b == 0xff ? XMP_KEY_OFF : b + 12; + } + if (j & MDL_INSTRUMENT_FOLLOWS) + len--, ev->ins = hio_read8(f); + if (j & MDL_VOLUME_FOLLOWS) + len--, ev->vol = hio_read8(f); + if (j & MDL_EFFECT_FOLLOWS) { + len--, k = hio_read8(f); + ev->fxt = LSN(k); + ev->f2t = MSN(k); + } + if (j & MDL_PARAMETER1_FOLLOWS) + len--, ev->fxp = hio_read8(f); + if (j & MDL_PARAMETER2_FOLLOWS) + len--, ev->f2p = hio_read8(f); + break; + } + + row++; + } + + if (row <= 64) + row = 64; + else if (row <= 128) + row = 128; + else row = 256; + + if (libxmp_alloc_track(mod, i, row) < 0) + goto err2; + + memcpy(mod->xxt[i], track, sizeof (struct xmp_track) + + sizeof (struct xmp_event) * (row - 1)); + + mod->xxt[i]->rows = row; + + /* Translate effects */ + for (j = 0; j < row; j++) { + struct xmp_event *ev = &mod->xxt[i]->event[j]; + xlat_fx1(&ev->fxt, &ev->fxp); + xlat_fx2(&ev->f2t, &ev->f2p); + } + } + + free(track); + + return 0; + + err2: + free(track); + err: + return -1; +} + +static int get_chunk_ii(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j, k; + int map, last_map; + uint8 buf[40]; + + /* Sanity check */ + if (data->has_ii) { + D_(D_CRIT "duplicate II chunk"); + return -1; + } + data->has_ii = 1; + + mod->ins = hio_read8(f); + D_(D_INFO "Instruments: %d", mod->ins); + + mod->xxi = (struct xmp_instrument *) calloc(mod->ins, sizeof(struct xmp_instrument)); + if (mod->xxi == NULL) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + + data->i_index[i] = hio_read8(f); + xxi->nsm = hio_read8(f); + if (hio_read(buf, 1, 32, f) < 32) { + D_(D_CRIT "read error at instrument %d", i); + return -1; + } + buf[32] = 0; + libxmp_instrument_name(mod, i, buf, 32); + + D_(D_INFO "[%2X] %-32.32s %2d", data->i_index[i], xxi->name, xxi->nsm); + + if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0) + return -1; + + for (j = 0; j < XMP_MAX_KEYS; j++) + xxi->map[j].ins = 0xff; + + for (last_map = j = 0; j < mod->xxi[i].nsm; j++) { + int x; + struct xmp_subinstrument *sub = &xxi->sub[j]; + + sub->sid = hio_read8(f); + map = hio_read8(f) + 12; + sub->vol = hio_read8(f); + for (k = last_map; k <= map; k++) { + if (k < XMP_MAX_KEYS) + xxi->map[k].ins = j; + } + last_map = map + 1; + + x = hio_read8(f); /* Volume envelope */ + if (j == 0) + data->v_index[i] = x & 0x80 ? x & 0x3f : -1; + if (~x & 0x40) + sub->vol = 0xff; + + mod->xxi[i].sub[j].pan = hio_read8(f) << 1; + + x = hio_read8(f); /* Pan envelope */ + if (j == 0) + data->p_index[i] = x & 0x80 ? x & 0x3f : -1; + if (~x & 0x40) + sub->pan = 0x80; + + x = hio_read16l(f); + if (j == 0) + xxi->rls = x; + + sub->vra = hio_read8(f); /* vibrato rate */ + sub->vde = hio_read8(f) << 1; /* vibrato depth */ + sub->vsw = hio_read8(f); /* vibrato sweep */ + sub->vwf = hio_read8(f); /* vibrato waveform */ + hio_read8(f); /* Reserved */ + + x = hio_read8(f); /* Pitch envelope */ + if (j == 0) + data->f_index[i] = x & 0x80 ? x & 0x3f : -1; + + D_(D_INFO " %2x: V%02x S%02x v%02x p%02x f%02x", + j, sub->vol, sub->sid, data->v_index[i], + data->p_index[i], data->f_index[i]); + } + } + + return 0; +} + +static int get_chunk_is(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + uint8 buf[64]; + uint8 x; + + /* Sanity check */ + if (data->has_is) { + D_(D_CRIT "duplicate IS chunk"); + return -1; + } + data->has_is = 1; + + mod->smp = hio_read8(f); + mod->xxs = (struct xmp_sample *) calloc(mod->smp, sizeof(struct xmp_sample)); + if (mod->xxs == NULL) + return -1; + m->xtra = (struct extra_sample_data *) calloc(mod->smp, sizeof(struct extra_sample_data)); + if (m->xtra == NULL) + return -1; + + data->packinfo = (int *) calloc(mod->smp, sizeof(int)); + if (data->packinfo == NULL) + return -1; + + D_(D_INFO "Sample infos: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + struct xmp_sample *xxs = &mod->xxs[i]; + int c5spd; + + data->s_index[i] = hio_read8(f); /* Sample number */ + if (hio_read(buf, 1, 32, f) < 32) { + D_(D_CRIT "read error at sample %d", i); + return -1; + } + buf[32] = 0; + libxmp_copy_adjust(xxs->name, buf, 31); + + hio_seek(f, 8, SEEK_CUR); /* Sample filename */ + + c5spd = hio_read32l(f); + + xxs->len = hio_read32l(f); + xxs->lps = hio_read32l(f); + xxs->lpe = hio_read32l(f); + + /* Sanity check */ + if (xxs->len < 0 || xxs->lps < 0 || + xxs->lps > xxs->len || xxs->lpe > (xxs->len - xxs->lps)) { + D_(D_CRIT "invalid sample %d - len:%d s:%d l:%d", + i, xxs->len, xxs->lps, xxs->lpe); + return -1; + } + + xxs->flg = xxs->lpe > 0 ? XMP_SAMPLE_LOOP : 0; + xxs->lpe = xxs->lps + xxs->lpe; + + m->xtra[i].c5spd = (double)c5spd; + + hio_read8(f); /* Volume in DMDL 0.0 */ + x = hio_read8(f); + if (x & 0x01) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + xxs->flg |= (x & 0x02) ? XMP_SAMPLE_LOOP_BIDIR : 0; + data->packinfo[i] = (x & 0x0c) >> 2; + + D_(D_INFO "[%2X] %-32.32s %05x%c %05x %05x %c %6d %d", + data->s_index[i], xxs->name, xxs->len, + xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', + xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + c5spd, data->packinfo[i]); + } + + return 0; +} + +static int get_chunk_i0(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + uint8 buf[64]; + uint8 x; + + /* Sanity check */ + if (data->has_ii || data->has_is) { + D_(D_CRIT "duplicate IS (0.0) chunk"); + return -1; + } + data->has_ii = 1; + data->has_is = 1; + + mod->ins = mod->smp = hio_read8(f); + + D_(D_INFO "Instruments (0.0): %d", mod->ins); + + if (libxmp_init_instrument(m) < 0) + return -1; + + data->packinfo = (int *) calloc(mod->smp, sizeof(int)); + if (data->packinfo == NULL) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs = &mod->xxs[i]; + int c5spd; + + xxi->nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + sub->sid = data->i_index[i] = data->s_index[i] = hio_read8(f); + + if (hio_read(buf, 1, 32, f) < 32) { + D_(D_CRIT "read error at instrument %d", i); + return -1; + } + buf[32] = 0; + hio_seek(f, 8, SEEK_CUR); /* Sample filename */ + libxmp_instrument_name(mod, i, buf, 32); + + c5spd = hio_read16l(f); + + xxs->len = hio_read32l(f); + xxs->lps = hio_read32l(f); + xxs->lpe = hio_read32l(f); + + /* Sanity check */ + if (xxs->len < 0 || xxs->lps < 0 || + xxs->lps > xxs->len || xxs->lpe > (xxs->len - xxs->lps)) { + D_(D_CRIT "invalid sample %d - len:%d s:%d l:%d", + i, xxs->len, xxs->lps, xxs->lpe); + return -1; + } + + xxs->flg = xxs->lpe > 0 ? XMP_SAMPLE_LOOP : 0; + xxs->lpe = xxs->lps + xxs->lpe; + + sub->vol = hio_read8(f); /* Volume */ + sub->pan = 0x80; + + m->xtra[i].c5spd = (double)c5spd; + + x = hio_read8(f); + if (x & 0x01) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + xxs->flg |= (x & 0x02) ? XMP_SAMPLE_LOOP_BIDIR : 0; + data->packinfo[i] = (x & 0x0c) >> 2; + + D_(D_INFO "[%2X] %-32.32s %5d V%02x %05x%c %05x %05x %d", + data->i_index[i], xxi->name, c5spd, sub->vol, + xxs->len, xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', + xxs->lps, xxs->lpe, data->packinfo[i]); + } + + return 0; +} + +static int get_chunk_sa(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, len, size_bound; + uint8 *smpbuf = NULL, *buf; + int smpbuf_alloc = -1; + int left = hio_size(f) - hio_tell(f); + + /* Sanity check */ + if (data->has_sa || !data->has_is || data->packinfo == NULL) { + D_(D_CRIT "duplicate SA chunk or missing IS chunk"); + return -1; + } + data->has_sa = 1; + + if (size < left) + left = size; + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + struct xmp_sample *xxs = &mod->xxs[i]; + + len = xxs->len; + if (xxs->flg & XMP_SAMPLE_16BIT) + len <<= 1; + + /* Bound the packed sample data size before trying to allocate RAM for it... */ + switch (data->packinfo[i]) { + case 0: + size_bound = len; + break; + case 1: + /* See unpack_sample8: each byte packs to 5 bits minimum. */ + size_bound = (len >> 3) * 5; + break; + case 2: + /* See unpack_sample16: each upper byte packs to 5 bits minimum, lower bytes are not packed. */ + size_bound = (len >> 4) * 13; + break; + default: + /* Sanity check */ + D_(D_CRIT "sample %d invalid pack %d", i, data->packinfo[i]); + goto err2; + } + + /* Sanity check */ + if (left < size_bound) { + D_(D_CRIT "sample %d (pack=%d) requested >=%d bytes, only %d available", + i, data->packinfo[i], size_bound, left); + goto err2; + } + + if (len > smpbuf_alloc) { + uint8 *tmp = (uint8 *) realloc(smpbuf, len); + if (!tmp) + goto err2; + + smpbuf = tmp; + smpbuf_alloc = len; + } + + switch (data->packinfo[i]) { + case 0: + if (hio_read(smpbuf, 1, len, f) < len) { + D_(D_CRIT "sample %d read error (no pack)", i); + goto err2; + } + left -= len; + break; + case 1: + len = hio_read32l(f); + /* Sanity check */ + if (xxs->flg & XMP_SAMPLE_16BIT) + goto err2; + if (len <= 0 || len > 0x80000) /* Max compressed sample size */ + goto err2; + if ((buf = (uint8 *)malloc(len + 4)) == NULL) + goto err2; + if (hio_read(buf, 1, len, f) != len) { + D_(D_CRIT "sample %d read error (8-bit)", i); + goto err3; + } + /* The unpack function may read slightly beyond the end. */ + buf[len] = buf[len + 1] = buf[len + 2] = buf[len + 3] = 0; + if (unpack_sample8(smpbuf, buf, len, xxs->len) < 0) { + D_(D_CRIT "sample %d unpack error (8-bit)", i); + goto err3; + } + free(buf); + left -= len + 4; + break; + case 2: + len = hio_read32l(f); + /* Sanity check */ + if (~xxs->flg & XMP_SAMPLE_16BIT) + goto err2; + if (len <= 0 || len > MAX_SAMPLE_SIZE) + goto err2; + if ((buf = (uint8 *)malloc(len + 4)) == NULL) + goto err2; + if (hio_read(buf, 1, len, f) != len) { + D_(D_CRIT "sample %d read error (16-bit)", i); + goto err3; + } + /* The unpack function may read slightly beyond the end. */ + buf[len] = buf[len + 1] = buf[len + 2] = buf[len + 3] = 0; + if (unpack_sample16(smpbuf, buf, len, xxs->len) < 0) { + D_(D_CRIT "sample %d unpack error (16-bit)", i); + goto err3; + } + free(buf); + left -= len + 4; + break; + } + + if (libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD, xxs, (char *)smpbuf) < 0) + goto err2; + } + + free(smpbuf); + return 0; + err3: + free(buf); + err2: + free(smpbuf); + return -1; +} + +static int get_chunk_ve(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->v_env) { + D_(D_CRIT "duplicate VE chunk"); + return -1; + } + + if ((data->v_envnum = hio_read8(f)) == 0) + return 0; + + D_(D_INFO "Vol envelopes: %d", data->v_envnum); + + data->v_env = (struct mdl_envelope *) calloc(data->v_envnum, sizeof(struct mdl_envelope)); + if (data->v_env == NULL) { + return -1; + } + + for (i = 0; i < data->v_envnum; i++) { + data->v_env[i].num = hio_read8(f); + hio_read(data->v_env[i].data, 1, 30, f); + data->v_env[i].sus = hio_read8(f); + data->v_env[i].loop = hio_read8(f); + } + + return 0; +} + +static int get_chunk_pe(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->p_env) { + D_(D_CRIT "duplicate PE chunk"); + return -1; + } + + if ((data->p_envnum = hio_read8(f)) == 0) + return 0; + + D_(D_INFO "Pan envelopes: %d", data->p_envnum); + + data->p_env = (struct mdl_envelope *) calloc(data->p_envnum, sizeof(struct mdl_envelope)); + if (data->p_env == NULL) { + return -1; + } + + for (i = 0; i < data->p_envnum; i++) { + data->p_env[i].num = hio_read8(f); + hio_read(data->p_env[i].data, 1, 30, f); + data->p_env[i].sus = hio_read8(f); + data->p_env[i].loop = hio_read8(f); + } + + return 0; +} + +static int get_chunk_fe(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->f_env) { + D_(D_CRIT "duplicate FE chunk"); + return -1; + } + + if ((data->f_envnum = hio_read8(f)) == 0) + return 0; + + D_(D_INFO "Pitch envelopes: %d", data->f_envnum); + + data->f_env = (struct mdl_envelope *) calloc(data->f_envnum, sizeof(struct mdl_envelope)); + if (data->f_env == NULL) { + return -1; + } + + for (i = 0; i < data->f_envnum; i++) { + data->f_env[i].num = hio_read8(f); + hio_read(data->f_env[i].data, 1, 30, f); + data->f_env[i].sus = hio_read8(f); + data->f_env[i].loop = hio_read8(f); + } + + return 0; +} + + +static int mdl_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + iff_handle handle; + int i, j, k, l; + char buf[8]; + struct local_data data; + int retval = 0; + + LOAD_INIT(); + + memset(&data, 0, sizeof (struct local_data)); + + /* Check magic and get version */ + hio_read32b(f); + if (hio_read(buf, 1, 1, f) < 1) + return -1; + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFFoid chunk IDs */ + libxmp_iff_register(handle, "IN", get_chunk_in); /* Module info */ + libxmp_iff_register(handle, "TR", get_chunk_tr); /* Tracks */ + libxmp_iff_register(handle, "SA", get_chunk_sa); /* Sampled data */ + libxmp_iff_register(handle, "VE", get_chunk_ve); /* Volume envelopes */ + libxmp_iff_register(handle, "PE", get_chunk_pe); /* Pan envelopes */ + libxmp_iff_register(handle, "FE", get_chunk_fe); /* Pitch envelopes */ + + if (MSN(*buf)) { + libxmp_iff_register(handle, "II", get_chunk_ii); /* Instruments */ + libxmp_iff_register(handle, "PA", get_chunk_pa); /* Patterns */ + libxmp_iff_register(handle, "IS", get_chunk_is); /* Sample info */ + } else { + libxmp_iff_register(handle, "PA", get_chunk_p0); /* Old 0.0 patterns */ + libxmp_iff_register(handle, "IS", get_chunk_i0); /* Old 0.0 Sample info */ + } + + /* MDL uses a IFF-style file format with 16 bit IDs and little endian + * 32 bit chunk size. There's only one chunk per data type (i.e. one + * big chunk for all samples). + */ + libxmp_iff_id_size(handle, 2); + libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN); + + libxmp_set_type(m, "Digitrakker MDL %d.%d", MSN(*buf), LSN(*buf)); + + m->volbase = 0xff; + m->c4rate = C4_NTSC_RATE; + + data.v_envnum = data.p_envnum = data.f_envnum = 0; + data.s_index = (int *) calloc(256, sizeof(int)); + data.i_index = (int *) calloc(256, sizeof(int)); + data.v_index = (int *) malloc(256 * sizeof(int)); + data.p_index = (int *) malloc(256 * sizeof(int)); + data.f_index = (int *) malloc(256 * sizeof(int)); + if (!data.s_index || !data.i_index || !data.v_index || !data.p_index || !data.f_index) { + goto err; + } + + for (i = 0; i < 256; i++) { + data.v_index[i] = data.p_index[i] = data.f_index[i] = -1; + } + + /* Load IFFoid chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + retval = -1; + goto err; + } + + libxmp_iff_release(handle); + + /* Reindex instruments */ + for (i = 0; i < mod->trk; i++) { + for (j = 0; j < mod->xxt[i]->rows; j++) { + struct xmp_event *e = &mod->xxt[i]->event[j]; + + for (l = 0; l < mod->ins; l++) { + if (e->ins && e->ins == data.i_index[l]) { + e->ins = l + 1; + break; + } + } + } + } + + /* Reindex envelopes, etc. */ + for (i = 0; i < mod->ins; i++) { + fix_env(i, &mod->xxi[i].aei, data.v_env, data.v_index, data.v_envnum); + fix_env(i, &mod->xxi[i].pei, data.p_env, data.p_index, data.p_envnum); + fix_env(i, &mod->xxi[i].fei, data.f_env, data.f_index, data.f_envnum); + + for (j = 0; j < mod->xxi[i].nsm; j++) { + for (k = 0; k < mod->smp; k++) { + if (mod->xxi[i].sub[j].sid == data.s_index[k]) { + mod->xxi[i].sub[j].sid = k; + /*libxmp_c2spd_to_note(data.c2spd[k], + &mod->xxi[i].sub[j].xpo, &mod->xxi[i].sub[j].fin);*/ + break; + } + } + } + } + + err: + free(data.f_index); + free(data.p_index); + free(data.v_index); + free(data.i_index); + free(data.s_index); + + free(data.v_env); + free(data.p_env); + free(data.f_env); + + free(data.packinfo); + + m->quirk |= QUIRKS_FT2 | QUIRK_KEYOFF; + m->read_event_type = READ_EVENT_FT2; + + return retval; +} diff --git a/thirdparty/libxmp/src/loaders/med.h b/thirdparty/libxmp/src/loaders/med.h new file mode 100644 index 000000000..93a77aab3 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/med.h @@ -0,0 +1,334 @@ +#ifndef LIBXMP_MED_H +#define LIBXMP_MED_H + +#include "../common.h" +#include "../hio.h" + +#define MMD_INST_TYPES 9 + +#ifdef DEBUG +extern const char *const mmd_inst_type[]; +#endif + +/* Structures as defined in the MED/OctaMED MMD0 and MMD1 file formats, + * revision 1, described by Teijo Kinnunen in Apr 25 1992 + */ + +struct PlaySeq { + char name[32]; /* (0) 31 chars + \0 */ + uint32 reserved[2]; /* (32) for possible extensions */ + uint16 length; /* (40) # of entries */ + uint16 seq[1]; /* (42) block numbers.. */ +}; + + +struct MMD0sample { + uint16 rep, replen; /* offs: 0(s), 2(s) */ + uint8 midich; /* offs: 4(s) */ + uint8 midipreset; /* offs: 5(s) */ + uint8 svol; /* offs: 6(s) */ + int8 strans; /* offs: 7(s) */ +}; + + +struct MMD0song { + struct MMD0sample sample[63]; /* 63 * 8 bytes = 504 bytes */ + uint16 numblocks; /* offs: 504 */ + uint16 songlen; /* offs: 506 */ + uint8 playseq[256]; /* offs: 508 */ + uint16 deftempo; /* offs: 764 */ + int8 playtransp; /* offs: 766 */ +#define FLAG_FILTERON 0x1 /* the hardware audio filter is on */ +#define FLAG_JUMPINGON 0x2 /* mouse pointer jumping on */ +#define FLAG_JUMP8TH 0x4 /* ump every 8th line (not in OctaMED Pro) */ +#define FLAG_INSTRSATT 0x8 /* sng+samples indicator (not useful in MMDs) */ +#define FLAG_VOLHEX 0x10 /* volumes are HEX */ +#define FLAG_STSLIDE 0x20 /* use ST/NT/PT compatible sliding */ +#define FLAG_8CHANNEL 0x40 /* this is OctaMED 5-8 channel song */ +#define FLAG_SLOWHQ 0X80 /* HQ V2-4 compatibility mode */ + uint8 flags; /* offs: 767 */ +#define FLAG2_BMASK 0x1F /* (bits 0-4) BPM beat length (in lines) */ +#define FLAG2_BPM 0x20 /* BPM mode on */ +#define FLAG2_MIX 0x80 /* Module uses mixing */ + uint8 flags2; /* offs: 768 */ + uint8 tempo2; /* offs: 769 */ + uint8 trkvol[16]; /* offs: 770 */ + uint8 mastervol; /* offs: 786 */ + uint8 numsamples; /* offs: 787 */ +}; /* length = 788 bytes */ + + +/* This structure is exactly as long as the MMDsong structure. Common fields + * are located at same offsets. You can also see, that there's a lot of room + * for expansion in this structure. + */ +struct MMD2song { + struct MMD0sample sample[63]; + uint16 numblocks; + uint16 songlen; /* NOTE: number of sections in MMD2 */ + struct PlaySeq **playseqtable; + uint16 *sectiontable; /* UWORD section numbers */ + uint8 *trackvols; /* UBYTE track volumes */ + uint16 numtracks; /* max. number of tracks in the song + * (also the number of entries in + * 'trackvols' table) */ + uint16 numpseqs; /* number of PlaySeqs in 'playseqtable' */ + int8 *trackpans; /* NULL means 'all centered */ +#define FLAG3_STEREO 0x1 /* Mixing in stereo */ +#define FLAG3_FREEPAN 0x2 /* Mixing flag: free pan */ + uint32 flags3; /* see defs below */ + uint16 voladj; /* volume adjust (%), 0 means 100 */ + uint16 channels; /* mixing channels, 0 means 4 */ + uint8 mix_echotype; /* 0 = nothing, 1 = normal, 2 = cross */ + uint8 mix_echodepth; /* 1 - 6, 0 = default */ + uint16 mix_echolen; /* echo length in milliseconds */ + int8 mix_stereosep; /* stereo separation */ + uint8 pad0[223]; /* reserved for future expansion */ +/* Fields below are MMD0/MMD1-compatible (except pad1[]) */ + uint16 deftempo; + int8 playtransp; + uint8 flags; + uint8 flags2; + uint8 tempo2; + uint8 pad1[16]; /* used to be trackvols, in MMD2 reserved */ + uint8 mastervol; + uint8 numsamples; +}; /* length = 788 bytes */ + + +struct MMD0 { + uint32 id; + uint32 modlen; + struct MMD0song *song; + uint16 psecnum; /* MMD2 only */ + uint16 pseq; /* MMD2 only */ + struct MMD0Block **blockarr; +#define MMD_LOADTOFASTMEM 0x1 + uint8 mmdflags; /* MMD2 only */ + uint8 reserved[3]; + struct InstrHdr **smplarr; + uint32 reserved2; + struct MMD0exp *expdata; + uint32 reserved3; + uint16 pstate; /* some data for the player routine */ + uint16 pblock; + uint16 pline; + uint16 pseqnum; + int16 actplayline; + uint8 counter; + uint8 extra_songs; /* number of songs - 1 */ +}; /* length = 52 bytes */ + + +struct MMD0Block { + uint8 numtracks, lines; +}; + + +struct BlockCmdPageTable { + uint16 num_pages; + uint16 reserved; + uint16 *page[1]; +}; + + +struct BlockInfo { + uint32 *hlmask; + uint8 *blockname; + uint32 blocknamelen; + struct BlockCmdPageTable *pagetable; + uint32 reserved[5]; +}; + + +struct MMD1Block { + uint16 numtracks; + uint16 lines; + struct BlockInfo *info; +}; + + +struct InstrHdr { + uint32 length; +#define S_16 0x10 /* 16-bit sample */ +#define MD16 0x18 /* 16-bit sample (Aura) */ +#define STEREO 0x20 /* Stereo sample, not interleaved */ + int16 type; + /* Followed by actual data */ +}; + + +struct SynthWF { + uint16 length; /* length in words */ + int8 wfdata[1]; /* the waveform */ +}; + + +struct SynthInstr { + uint32 length; /* length of this struct */ + int16 type; /* -1 or -2 (offs: 4) */ + uint8 defaultdecay; + uint8 reserved[3]; + uint16 rep; + uint16 replen; + uint16 voltbllen; /* offs: 14 */ + uint16 wftbllen; /* offs: 16 */ + uint8 volspeed; /* offs: 18 */ + uint8 wfspeed; /* offs: 19 */ + uint16 wforms; /* offs: 20 */ + uint8 voltbl[128]; /* offs: 22 */ + uint8 wftbl[128]; /* offs: 150 */ + uint32 wf[64]; /* offs: 278 */ +}; + + +/* OctaMED SoundStudio 1 and prior use the InstrExt default_pitch field as a + * default note value for the default note key 'F'. Pressing 'F' will insert a + * note event with this note value. + * + * MED Soundstudio 2 in mix mode treats note 0x01 as a default note event, + * which is emitted by the default note key instead of a regular note event. + * It also makes this more complicated, despite not having changed the file + * format: the user must enter a frequency in Hz instead of a note number, + * where 8363 Hz corresponds to the event C-2. This frequency is converted to a + * note number upon saving the module. Multi-octave instruments do not support + * this feature as they are not supported by MED Soundstudio 2. + * + * This editor-only behavior would be irrelevant, except when default_pitch + * is zero, the player uses the default frequency 22050 Hz instead. This + * results in a note between E-3 and F-3. Since this feature is currently + * implemented in the instrument map, use the mix mode note for F-3 instead. + */ +#define MMD3_DEFAULT_NOTE 53 + +struct InstrExt { + uint8 hold; + uint8 decay; + uint8 suppress_midi_off; + int8 finetune; + /* Below fields saved by >= V5 */ + uint8 default_pitch; +#define SSFLG_LOOP 0x01 /* Loop On/Off */ +#define SSFLG_EXTPSET 0x02 /* Ext. Preset */ +#define SSFLG_DISABLED 0x04 /* Disabled */ +#define SSFLG_PINGPONG 0x08 /* Ping-pong looping */ + uint8 instr_flags; + uint16 long_midi_preset; + /* Below fields saved by >= V5.02 */ + uint8 output_device; + uint8 reserved; + /* Below fields saved by >= V7 */ + uint32 long_repeat; + uint32 long_replen; +}; + + +struct MMDInfo { + struct MMDInfo *next; /* next MMDInfo structure */ + uint16 reserved; + uint16 type; /* data type (1 = ASCII) */ + uint32 length; /* data length in bytes */ + /* data follows... */ +}; + + +struct MMDARexxTrigCmd { + struct MMDARexxTrigCmd *next; /* the next command, or NULL */ + uint8 cmdnum; /* command number (01..FF) */ + uint8 pad; + int16 cmdtype; /* command type (OMACTION_...) */ + char *cmd; /* command, or NULL */ + char *port; /* port, or NULL */ + uint16 cmd_len; /* length of 'cmd' string (without + * term. 0) */ + uint16 port_len; /* length of 'port' string (without + * term. 0) */ +}; /* current (V7) structure size: 20 */ + + +struct MMDARexx { + uint16 res; /* reserved, must be zero! */ + uint16 trigcmdlen; /* size of trigcmd entries + * (MUST be used!!) */ + struct MMDARexxTrigCmd *trigcmd; /* chain of MMDARexxTrigCmds or NULL */ +}; + + +struct MMDMIDICmd3x { + uint8 struct_vers; /* current version = 0 */ + uint8 pad; + uint16 num_of_settings; /* number of Cmd3x settings + * (currently set to 15) */ + uint8 *ctrlr_types; /* controller types */ + uint16 *ctrlr_numbers; /* controller numbers */ +}; + + +struct MMDInstrInfo { + uint8 name[40]; +}; + +struct MMD0exp { + struct MMD0 *nextmod; /* pointer to the next module */ + struct InstrExt *exp_smp; /* pointer to InstrExt */ + uint16 s_ext_entries; /* size of InstrExt structure array */ + uint16 s_ext_entrsz; /* size of each InstrExt structure */ + uint8 *annotxt; /* pointer to the annotation text */ + uint32 annolen; /* length of 'annotxt' */ + struct MMDInstrInfo *iinfo; /* pointer to MMDInstrInfo */ + uint16 i_ext_entries; /* size of MMDInstrInfo struct array */ + uint16 i_ext_entrsz; /* size of each MMDInstrInfo struct */ + uint32 jumpmask; /* mouse pointer jump control */ + uint16 *rgbtable; /* screen colors */ + uint8 channelsplit[4]; /* channel splitting control */ + struct NotationInfo *n_info; /* info for the notation editor */ + uint8 *songname; /* song name of the current song */ + uint32 songnamelen; /* song name length */ + struct MMDDumpData *dumps; /* MIDI dump data */ + struct MMDInfo *mmdinfo; /* more information about the song */ + struct MMDARexx *mmdrexx; /* embedded ARexx commands */ + struct MMDMIDICmd3x *mmdcmd3x; /* settings for command 3x */ + uint32 reserved2[3]; /* future expansion fields */ + uint32 tag_end; +}; + + +struct NotationInfo { + uint8 n_of_sharps; /* number of sharps or flats */ +#define NFLG_FLAT 1 +#define NFLG_3_4 2 + uint8 flags; + int16 trksel[5]; /* number of the selected track */ + uint8 trkshow[16]; /* tracks shown */ + uint8 trkghost[16]; /* tracks ghosted */ + int8 notetr[63]; /* note transpose for each instrument */ + uint8 pad; +}; + + +struct MMDDumpData { + uint16 numdumps; + uint16 reserved[3]; +}; + + +struct MMDDump { + uint32 length; /* length of the MIDI message dump */ + uint8 *data; /* pointer to MIDI dump data */ + uint16 ext_len; /* MMDDump struct extension length */ + /* if ext_len >= 20: */ + uint8 name[20]; /* name of the dump */ +}; + +extern const int mmd_num_oct[6]; + +void mmd_xlat_fx(struct xmp_event *, int, int, int, int); +int mmd_alloc_tables(struct module_data *, int, struct SynthInstr *); + +int mmd_load_instrument(HIO_HANDLE *, struct module_data *, int, int, + struct MMD0exp *, struct InstrExt *, struct MMD0sample *, int); + +void mmd_set_bpm(struct module_data *, int, int, int, int); +void mmd_info_text(HIO_HANDLE *, struct module_data *, int); + +#endif /* LIBXMP_MED_H */ diff --git a/thirdparty/libxmp/src/loaders/med2_load.c b/thirdparty/libxmp/src/loaders/med2_load.c new file mode 100644 index 000000000..706e42d3a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/med2_load.c @@ -0,0 +1,243 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * MED 1.12 is in Fish disk #255 + */ + +#include "loader.h" +#include "../period.h" + +#define MAGIC_MED2 MAGIC4('M','E','D',2) + +static int med2_test(HIO_HANDLE *, char *, const int); +static int med2_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_med2 = { + "MED 1.12 MED2", + med2_test, + med2_load +}; + + +static int med2_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_MED2) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +static int med2_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + int sliding; + struct xmp_event *event; + uint8 buf[40]; + + LOAD_INIT(); + + if (hio_read32b(f) != MAGIC_MED2) + return -1; + + libxmp_set_type(m, "MED 1.12 MED2"); + + mod->ins = mod->smp = 32; + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* read instrument names */ + hio_read(buf, 1, 40, f); /* skip 0 */ + for (i = 0; i < 31; i++) { + if (hio_read(buf, 1, 40, f) != 40) + return -1; + + libxmp_instrument_name(mod, i, buf, 40); + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + } + + /* read instrument volumes */ + hio_read8(f); /* skip 0 */ + for (i = 0; i < 31; i++) { + mod->xxi[i].sub[0].vol = hio_read8(f); + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].fin = 0; + mod->xxi[i].sub[0].sid = i; + } + + /* read instrument loops */ + hio_read16b(f); /* skip 0 */ + for (i = 0; i < 31; i++) { + mod->xxs[i].lps = hio_read16b(f); + } + + /* read instrument loop length */ + hio_read16b(f); /* skip 0 */ + for (i = 0; i < 31; i++) { + uint32 lsiz = hio_read16b(f); + mod->xxs[i].lpe = mod->xxs[i].lps + lsiz; + mod->xxs[i].flg = lsiz > 1 ? XMP_SAMPLE_LOOP : 0; + } + + mod->chn = 4; + mod->pat = hio_read16b(f); + mod->trk = mod->chn * mod->pat; + + if (hio_read(mod->xxo, 1, 100, f) != 100) + return -1; + + mod->len = hio_read16b(f); + + /* Sanity check */ + if (mod->pat > 256 || mod->len > 100) + return -1; + + k = hio_read16b(f); + if (k < 1) { + return -1; + } + + mod->spd = 6; + mod->bpm = k; + m->time_factor = MED_TIME_FACTOR; + + hio_read16b(f); /* flags */ + sliding = hio_read16b(f); /* sliding */ + hio_read32b(f); /* jumping mask */ + hio_seek(f, 16, SEEK_CUR); /* rgb */ + + MODULE_INFO(); + + D_(D_INFO "Sliding: %d", sliding); + + if (sliding == 6) + m->quirk |= QUIRK_VSALL | QUIRK_PBALL; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + hio_read32b(f); + + for (j = 0; j < 64; j++) { + for (k = 0; k < 4; k++) { + uint8 x; + event = &EVENT(i, k, j); + event->note = libxmp_period_to_note(hio_read16b(f)); + x = hio_read8(f); + event->ins = x >> 4; + event->fxt = x & 0x0f; + event->fxp = hio_read8(f); + + switch (event->fxt) { + case 0x00: /* arpeggio */ + case 0x01: /* slide up */ + case 0x02: /* slide down */ + case 0x03: /* portamento */ + case 0x04: /* vibrato? */ + case 0x0c: /* volume */ + break; /* ...like protracker */ + case 0x0d: /* volslide */ + case 0x0e: /* volslide */ + event->fxt = FX_VOLSLIDE; + break; + case 0x0f: + event->fxt = FX_S3M_BPM; + break; + } + } + } + } + + /* Load samples */ + + D_(D_INFO "Instruments : %d ", mod->ins); + + for (i = 0; i < 31; i++) { + char path[XMP_MAXPATH]; + char ins_path[256]; + char ins_name[32]; + char name[256]; + HIO_HANDLE *s = NULL; + int found = 0; + + if (libxmp_copy_name_for_fopen(ins_name, mod->xxi[i].name, 32) != 0) + continue; + + libxmp_get_instrument_path(m, ins_path, 256); + if (libxmp_check_filename_case(ins_path, ins_name, name, 256)) { + snprintf(path, XMP_MAXPATH, "%s/%s", ins_path, name); + found = 1; + } + + /* Try the module dir if the instrument path didn't work. */ + if (!found && m->dirname != NULL && + libxmp_check_filename_case(m->dirname, ins_name, name, 256)) { + snprintf(path, XMP_MAXPATH, "%s%s", m->dirname, name); + found = 1; + } + + if (found) { + if ((s = hio_open(path,"rb")) != NULL) { + mod->xxs[i].len = hio_size(s); + } + } + + if (mod->xxs[i].len > 0) { + mod->xxi[i].nsm = 1; + } + + if (!strlen(mod->xxi[i].name) && !mod->xxs[i].len) { + if (s != NULL) { + hio_close(s); + } + continue; + } + + D_(D_INFO "[%2X] %-32.32s %04x %04x %04x %c V%02x", + i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol); + + if (s != NULL) { + int ret = libxmp_load_sample(m, s, 0, &mod->xxs[i], NULL); + hio_close(s); + if (ret < 0) { + return -1; + } + } + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/med3_load.c b/thirdparty/libxmp/src/loaders/med3_load.c new file mode 100644 index 000000000..d2b30549f --- /dev/null +++ b/thirdparty/libxmp/src/loaders/med3_load.c @@ -0,0 +1,439 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * MED 2.00 is in Fish disk #349 and has a couple of demo modules, get it + * from ftp://ftp.funet.fi/pub/amiga/fish/301-400/ff349 + */ + +#include "loader.h" + +#define MAGIC_MED3 MAGIC4('M','E','D',3) + + +static int med3_test(HIO_HANDLE *, char *, const int); +static int med3_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_med3 = { + "MED 2.00 MED3", + med3_test, + med3_load +}; + +static int med3_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_MED3) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + + +#define MASK 0x80000000 + +#define M0F_LINEMSK0F 0x01 +#define M0F_LINEMSK1F 0x02 +#define M0F_FXMSK0F 0x04 +#define M0F_FXMSK1F 0x08 +#define M0F_LINEMSK00 0x10 +#define M0F_LINEMSK10 0x20 +#define M0F_FXMSK00 0x40 +#define M0F_FXMSK10 0x80 + + +/* + * From the MED 2.00 file loading/saving routines by Teijo Kinnunen, 1990 + */ + +static uint8 get_nibble(uint8 *mem, uint16 *nbnum) +{ + uint8 *mloc = mem + (*nbnum / 2), res; + + if(*nbnum & 0x1) + res = *mloc & 0x0f; + else + res = *mloc >> 4; + (*nbnum)++; + + return res; +} + +static uint16 get_nibbles(uint8 *mem, uint16 *nbnum, uint8 nbs) +{ + uint16 res = 0; + + while (nbs--) { + res <<= 4; + res |= get_nibble(mem, nbnum); + } + + return res; +} + +static int unpack_block(struct module_data *m, uint16 bnum, uint8 *from, uint16 convsz) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + uint32 linemsk0 = *((uint32 *)from), linemsk1 = *((uint32 *)from + 1); + uint32 fxmsk0 = *((uint32 *)from + 2), fxmsk1 = *((uint32 *)from + 3); + uint32 *lmptr = &linemsk0, *fxptr = &fxmsk0; + uint16 fromn = 0, lmsk; + uint8 *fromst = from + 16, bcnt, *tmpto; + uint8 *patbuf, *to; + uint32 nibs_left = convsz * 2; + int i, j, trkn = mod->chn; + + /*from += 16;*/ + patbuf = to = (uint8 *) calloc(3, 4 * 64); + if (to == NULL) { + goto err; + } + + for (i = 0; i < 64; i++) { + if (i == 32) { + lmptr = &linemsk1; + fxptr = &fxmsk1; + } + + if (*lmptr & MASK) { + if (trkn / 4 > nibs_left) { + goto err2; + } + nibs_left -= trkn / 4; + + lmsk = get_nibbles(fromst, &fromn, (uint8)(trkn / 4)); + lmsk <<= (16 - trkn); + tmpto = to; + + for (bcnt = 0; bcnt < trkn; bcnt++) { + if (lmsk & 0x8000) { + if (nibs_left < 3) { + goto err2; + } + nibs_left -= 3; + *tmpto = (uint8)get_nibbles(fromst, + &fromn,2); + *(tmpto + 1) = (get_nibble(fromst, + &fromn) << 4); + } + lmsk <<= 1; + tmpto += 3; + } + } + + if (*fxptr & MASK) { + if (trkn / 4 > nibs_left) { + goto err2; + } + nibs_left -= trkn / 4; + + lmsk = get_nibbles(fromst,&fromn,(uint8)(trkn / 4)); + lmsk <<= (16 - trkn); + tmpto = to; + + for (bcnt = 0; bcnt < trkn; bcnt++) { + if (lmsk & 0x8000) { + if (nibs_left < 3) { + goto err2; + } + nibs_left -= 3; + *(tmpto+1) |= get_nibble(fromst, + &fromn); + *(tmpto+2) = (uint8)get_nibbles(fromst, + &fromn,2); + } + lmsk <<= 1; + tmpto += 3; + } + } + to += 3 * trkn; + *lmptr <<= 1; + *fxptr <<= 1; + } + + for (i = 0; i < 64; i++) { + for (j = 0; j < 4; j++) { + event = &EVENT(bnum, j, i); + + event->note = patbuf[i * 12 + j * 3 + 0]; + if (event->note) + event->note += 48; + event->ins = patbuf[i * 12 + j * 3 + 1] >> 4; + if (event->ins) + event->ins++; + event->fxt = patbuf[i * 12 + j * 3 + 1] & 0x0f; + event->fxp = patbuf[i * 12 + j * 3 + 2]; + + switch (event->fxt) { + case 0x00: /* arpeggio */ + case 0x01: /* slide up */ + case 0x02: /* slide down */ + case 0x03: /* portamento */ + case 0x04: /* vibrato? */ + break; + case 0x0c: /* set volume (BCD) */ + event->fxp = MSN(event->fxp) * 10 + + LSN(event->fxp); + break; + case 0x0d: /* volume slides */ + event->fxt = FX_VOLSLIDE; + break; + case 0x0f: /* tempo/break */ + if (event->fxp == 0) + event->fxt = FX_BREAK; + if (event->fxp == 0xff) { + event->fxp = event->fxt = 0; + event->vol = 1; + } else if (event->fxp == 0xfe) { + event->fxp = event->fxt = 0; + } else if (event->fxp == 0xf1) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | 3; + } else if (event->fxp == 0xf2) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_CUT << 4) | 3; + } else if (event->fxp == 0xf3) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_DELAY << 4) | 3; + } else if (event->fxp > 10) { + event->fxt = FX_S3M_BPM; + event->fxp = 125 * event->fxp / 33; + } + break; + default: + event->fxp = event->fxt = 0; + } + } + } + + free(patbuf); + + return 0; + + err2: + free(patbuf); + err: + return -1; +} + + +static int med3_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + uint32 mask; + int transp, sliding; + + LOAD_INIT(); + + hio_read32b(f); + + libxmp_set_type(m, "MED 2.00 MED3"); + + mod->ins = mod->smp = 32; + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* read instrument names */ + for (i = 0; i < 32; i++) { + uint8 c, buf[40]; + for (j = 0; j < 40; j++) { + c = hio_read8(f); + buf[j] = c; + if (c == 0) + break; + } + libxmp_instrument_name(mod, i, buf, 32); + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + } + + /* read instrument volumes */ + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + mod->xxi[i].sub[0].vol = mask & MASK ? hio_read8(f) : 0; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].fin = 0; + mod->xxi[i].sub[0].sid = i; + } + + /* read instrument loops */ + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + mod->xxs[i].lps = mask & MASK ? hio_read16b(f) : 0; + } + + /* read instrument loop length */ + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + uint32 lsiz = mask & MASK ? hio_read16b(f) : 0; + mod->xxs[i].len = mod->xxs[i].lps + lsiz; + mod->xxs[i].lpe = mod->xxs[i].lps + lsiz; + mod->xxs[i].flg = lsiz > 1 ? XMP_SAMPLE_LOOP : 0; + } + + mod->chn = 4; + mod->pat = hio_read16b(f); + mod->trk = mod->chn * mod->pat; + + mod->len = hio_read16b(f); + + /* Sanity check */ + if (mod->len > 256 || mod->pat > 256) + return -1; + + hio_read(mod->xxo, 1, mod->len, f); + mod->spd = hio_read16b(f); + if (mod->spd > 10) { + mod->bpm = 125 * mod->spd / 33; + mod->spd = 6; + } + transp = hio_read8s(f); + hio_read8(f); /* flags */ + sliding = hio_read16b(f); /* sliding */ + hio_read32b(f); /* jumping mask */ + hio_seek(f, 16, SEEK_CUR); /* rgb */ + + /* read midi channels */ + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + if (mask & MASK) + hio_read8(f); + } + + /* read midi programs */ + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + if (mask & MASK) + hio_read8(f); + } + + MODULE_INFO(); + + D_(D_INFO "Sliding: %d", sliding); + D_(D_INFO "Play transpose: %d", transp); + + if (sliding == 6) + m->quirk |= QUIRK_VSALL | QUIRK_PBALL; + + for (i = 0; i < 32; i++) + mod->xxi[i].sub[0].xpo = transp; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + uint32 *conv; + uint8 b; + /*uint8 tracks;*/ + uint16 convsz; + + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + /* TODO: not clear if this should be respected. Later MED + * formats are capable of having different track counts. */ + /*tracks =*/ hio_read8(f); + + b = hio_read8(f); + convsz = hio_read16b(f); + conv = (uint32 *) calloc(1, convsz + 16); + if (conv == NULL) + return -1; + + if (b & M0F_LINEMSK00) + *conv = 0L; + else if (b & M0F_LINEMSK0F) + *conv = 0xffffffff; + else + *conv = hio_read32b(f); + + if (b & M0F_LINEMSK10) + *(conv + 1) = 0L; + else if (b & M0F_LINEMSK1F) + *(conv + 1) = 0xffffffff; + else + *(conv + 1) = hio_read32b(f); + + if (b & M0F_FXMSK00) + *(conv + 2) = 0L; + else if (b & M0F_FXMSK0F) + *(conv + 2) = 0xffffffff; + else + *(conv + 2) = hio_read32b(f); + + if (b & M0F_FXMSK10) + *(conv + 3) = 0L; + else if (b & M0F_FXMSK1F) + *(conv + 3) = 0xffffffff; + else + *(conv + 3) = hio_read32b(f); + + if (hio_read(conv + 4, 1, convsz, f) != convsz) { + free(conv); + return -1; + } + + if (unpack_block(m, i, (uint8 *)conv, convsz) < 0) { + free(conv); + return -1; + } + + free(conv); + } + + /* Load samples */ + + D_(D_INFO "Instruments: %d", mod->ins); + + mask = hio_read32b(f); + for (i = 0; i < 32; i++, mask <<= 1) { + if (~mask & MASK) + continue; + + mod->xxi[i].nsm = 1; + mod->xxs[i].len = hio_read32b(f); + + if (mod->xxs[i].len == 0) + mod->xxi[i].nsm = 0; + + if (hio_read16b(f)) /* type */ + continue; + + D_(D_INFO "[%2X] %-32.32s %04x %04x %04x %c V%02x ", + i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol); + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/med4_load.c b/thirdparty/libxmp/src/loaders/med4_load.c new file mode 100644 index 000000000..63e4ea044 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/med4_load.c @@ -0,0 +1,863 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * MED 2.13 is in Fish disk #424 and has a couple of demo modules, get it + * from ftp://ftp.funet.fi/pub/amiga/fish/401-500/ff424. Alex Van Starrex's + * HappySong MED4 is in ff401. MED 3.00 is in ff476. + */ + +#include "med.h" +#include "loader.h" +#include "../med_extras.h" + +#define MAGIC_MED4 MAGIC4('M','E','D',4) +#undef MED4_DEBUG + +static int med4_test(HIO_HANDLE *, char *, const int); +static int med4_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_med4 = { + "MED 2.10 MED4", + med4_test, + med4_load +}; + +static int med4_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_MED4) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +static void fix_effect(struct xmp_event *event, int hexvol) +{ + switch (event->fxt) { + case 0x00: /* arpeggio */ + case 0x01: /* slide up */ + case 0x02: /* slide down */ + case 0x03: /* portamento */ + case 0x04: /* vibrato? */ + break; + case 0x0c: /* set volume (BCD) */ + if (!hexvol) { + event->fxp = MSN(event->fxp) * 10 + LSN(event->fxp); + } + break; + case 0x0d: /* volume slides */ + event->fxt = FX_VOLSLIDE; + break; + case 0x0f: /* tempo/break */ + if (event->fxp == 0) + event->fxt = FX_BREAK; + if (event->fxp == 0xff) { + event->fxp = event->fxt = 0; + event->vol = 1; + } else if (event->fxp == 0xf1) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | 3; + } else if (event->fxp == 0xf2) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_CUT << 4) | 3; + } else if (event->fxp == 0xf3) { + event->fxt = FX_EXTENDED; + event->fxp = (EX_DELAY << 4) | 3; + } else if (event->fxp > 0xf0) { + event->fxp = event->fxt = 0; + } else if (event->fxp > 10) { + event->fxt = FX_S3M_BPM; + event->fxp = 125 * event->fxp / 33; + } + break; + default: + event->fxp = event->fxt = 0; + } +} + +struct stream { + HIO_HANDLE* f; + int has_nibble; + uint8 value; +}; + +static inline void stream_init(HIO_HANDLE* f, struct stream* s) +{ + s->f = f; + s->has_nibble = s->value = 0; +} + +static inline unsigned stream_read4(struct stream* s) +{ + s->has_nibble = !s->has_nibble; + if (!s->has_nibble) { + return s->value & 0x0f; + } else { + s->value = hio_read8(s->f); + return s->value >> 4; + } +} + +static inline unsigned stream_read8(struct stream* s) +{ + unsigned a = stream_read4(s); + unsigned b = stream_read4(s); + return (a << 4) | b; +} + +static inline unsigned stream_read12(struct stream* s) +{ + unsigned a = stream_read4(s); + unsigned b = stream_read4(s); + unsigned c = stream_read4(s); + return (a << 8) | (b << 4) | c; +} + +static inline uint16 stream_read16(struct stream* s) +{ + unsigned a = stream_read4(s); + unsigned b = stream_read4(s); + unsigned c = stream_read4(s); + unsigned d = stream_read4(s); + return (a << 12) | (b << 8) | (c << 4) | d; +} + +static inline uint16 stream_read_aligned16(struct stream* s, int bits) +{ + if (bits <= 4) { + return stream_read4(s) << 12; + } + if (bits <= 8) { + return stream_read8(s) << 8; + } + if (bits <= 12) { + return stream_read12(s) << 4; + } + return stream_read16(s); +} + +struct temp_inst { + char name[32]; + int loop_start; + int loop_end; + int volume; + int transpose; +}; + +static int med4_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k, y; + uint8 m0; + uint64 mask; + int transp, masksz; + int32 pos; + int vermaj, vermin; + uint8 trkvol[16], buf[1024]; + struct xmp_event *event; + int flags, hexvol = 0; + int num_ins, num_smp; + int smp_idx; + int tempo; + struct temp_inst temp_inst[64]; + + LOAD_INIT(); + + hio_read32b(f); /* Skip magic */ + + vermaj = 2; + vermin = 10; + + /* + * Check if we have a MEDV chunk at the end of the file + */ + if ((pos = hio_tell(f)) < 0) { + return -1; + } + + hio_seek(f, 0, SEEK_END); + if (hio_tell(f) > 2000) { + hio_seek(f, -1024, SEEK_CUR); + hio_read(buf, 1, 1024, f); + for (i = 0; i < 1013; i++) { + if (!memcmp(buf + i, "MEDV\000\000\000\004", 8)) { + vermaj = *(buf + i + 10); + vermin = *(buf + i + 11); + break; + } + } + } + hio_seek(f, start + pos, SEEK_SET); + + snprintf(mod->type, XMP_NAME_SIZE, "MED %d.%02d MED4", vermaj, vermin); + + m0 = hio_read8(f); + + mask = masksz = 0; + for (i = 0; m0 != 0 && i < 8; i++, m0 <<= 1) { + if (m0 & 0x80) { + mask <<= 8; + mask |= hio_read8(f); + masksz++; + } + } + + /* CID 128662 (#1 of 1): Bad bit shift operation (BAD_SHIFT) + * large_shift: left shifting by more than 63 bits has undefined + * behavior. + */ + if (masksz > 0) { + mask <<= 8 * (sizeof(mask) - masksz); + } + /*printf("m0=%x mask=%x\n", m0, mask);*/ + + /* read instrument names in temporary space */ + + num_ins = 0; + memset(temp_inst, 0, sizeof(temp_inst)); + for (i = 0; mask != 0 && i < 64; i++, mask <<= 1) { + uint8 c, size; + uint16 loop_len = 0; + + if ((int64)mask > 0) + continue; + + num_ins = i + 1; + + /* read flags */ + c = hio_read8(f); + + /* read instrument name */ + size = hio_read8(f); + for (j = 0; j < size; j++) + buf[j] = hio_read8(f); + buf[j] = 0; +#ifdef MED4_DEBUG + printf("%02x %02x %2d [%s]\n", i, c, size, buf); +#endif + + temp_inst[i].volume = 0x40; + + if ((c & 0x01) == 0) + temp_inst[i].loop_start = hio_read16b(f) << 1; + if ((c & 0x02) == 0) + loop_len = hio_read16b(f) << 1; + if ((c & 0x04) == 0) /* ? Tanko2 (MED 3.00 demo) */ + hio_read8(f); + if ((c & 0x08) == 0) /* Tim Newsham's "span" */ + hio_read8(f); + if ((c & 0x30) == 0) + temp_inst[i].volume = hio_read8(f); + if ((c & 0x40) == 0) + temp_inst[i].transpose = hio_read8s(f); + + temp_inst[i].loop_end = temp_inst[i].loop_start + loop_len; + + libxmp_copy_adjust(temp_inst[i].name, buf, 32); + } + + mod->pat = hio_read16b(f); + mod->len = hio_read16b(f); + + if (hio_error(f)) { + return -1; + } + +#ifdef MED4_DEBUG + printf("pat=%x len=%x\n", mod->pat, mod->len); +#endif + if (mod->pat > 256 || mod->len > XMP_MAX_MOD_LENGTH) + return -1; + hio_read(mod->xxo, 1, mod->len, f); + + /* From MED V3.00 docs: + * + * The left proportional gadget controls the primary tempo. It canbe + * 1 - 240. The bigger the number, the faster the speed. Note that + * tempos 1 - 10 are Tracker-compatible (but obsolete, because + * secondary tempo can be used now). + */ + tempo = hio_read16b(f); + if (tempo <= 10) { + mod->spd = tempo; + mod->bpm = 125; + } else { + mod->bpm = 125 * tempo / 33; + } + transp = hio_read8s(f); + flags = hio_read8s(f); + mod->spd = hio_read16b(f); + + if (~flags & 0x20) /* sliding */ + m->quirk |= QUIRK_VSALL | QUIRK_PBALL; + + if (flags & 0x10) /* dec/hex volumes */ + hexvol = 1; + + /* This is just a guess... */ + if (vermaj == 2) /* Happy.med has tempo 5 but loads as 6 */ + mod->spd = flags & 0x20 ? 5 : 6; + + hio_seek(f, 20, SEEK_CUR); + + hio_read(trkvol, 1, 16, f); + hio_read8(f); /* master vol */ + + MODULE_INFO(); + + D_(D_INFO "Play transpose: %d", transp); + + for (i = 0; i < 64; i++) + temp_inst[i].transpose += transp; + + /* Scan patterns to determine number of channels */ + mod->chn = 0; + if ((pos = hio_tell(f)) < 0) { + return -1; + } + + for (i = 0; i < mod->pat; i++) { + int size, plen, chn; + + size = hio_read8(f); /* pattern control block */ + chn = hio_read8(f); + if (chn > mod->chn) + mod->chn = chn; + hio_read8(f); /* skip number of rows */ + plen = hio_read16b(f); + + hio_seek(f, size + plen - 4, SEEK_CUR); + } + + /* Sanity check */ + if (mod->chn > 16) { + return -1; + } + + mod->trk = mod->chn * mod->pat; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + hio_seek(f, pos, SEEK_SET); + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + int size, plen, rows; + uint8 ctl[4], chn; + unsigned chmsk; + uint32 linemask[8], fxmask[8], x; + int num_masks; + struct stream stream; + +#ifdef MED4_DEBUG + printf("\n===== PATTERN %d =====\n", i); + printf("offset = %lx\n", hio_tell(f)); +#endif + + size = hio_read8(f); /* pattern control block */ + if ((pos = hio_tell(f)) < 0) { + return -1; + } + chn = hio_read8(f); + if (chn > mod->chn) { + return -1; + } + rows = (int)hio_read8(f) + 1; + plen = hio_read16b(f); +#ifdef MED4_DEBUG + printf("size = %02x\n", size); + printf("chn = %01x\n", chn); + printf("rows = %01x\n", rows); + printf("plen = %04x\n", plen); +#endif + /* read control byte */ + for (j = 0; j < 4; j++) { + if (rows > j * 64) + ctl[j] = hio_read8(f); + else + break; +#ifdef MED4_DEBUG + printf("ctl[%d] = %02x\n", j, ctl[j]); + +#endif + } + + if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0) + return -1; + + /* initialize masks */ + for (y = 0; y < 8; y++) { + linemask[y] = 0; + fxmask[y] = 0; + } + + /* read masks */ + num_masks = 0; + for (y = 0; y < 8; y++) { + if (rows > y * 32) { + int c = ctl[y / 2]; + int s = 4 * (y % 2); + linemask[y] = c & (0x80 >> s) ? ~0 : + c & (0x40 >> s) ? 0 : hio_read32b(f); + fxmask[y] = c & (0x20 >> s) ? ~0 : + c & (0x10 >> s) ? 0 : hio_read32b(f); + num_masks++; +#ifdef MED4_DEBUG + printf("linemask[%d] = %08x\n", y, linemask[y]); + printf("fxmask[%d] = %08x\n", y, fxmask[y]); +#endif + } else { + break; + } + } + + hio_seek(f, pos + size, SEEK_SET); + stream_init(f, &stream); + + for (y = 0; y < num_masks; y++) { + + for (j = 0; j < 32; j++) { + int line = y * 32 + j; + + if (line >= rows) + break; + + if (linemask[y] & 0x80000000) { + chmsk = stream_read_aligned16(&stream, chn); + for (k = 0; k < chn; k++, chmsk <<= 1) { + event = &EVENT(i, k, line); + + if (chmsk & 0x8000) { + x = stream_read12(&stream); + event->note = x >> 4; + if (event->note) + event->note += 48; + event->ins = x & 0x0f; + } + } + } + + if (fxmask[y] & 0x80000000) { + chmsk = stream_read_aligned16(&stream, chn); + for (k = 0; k < chn; k++, chmsk <<= 1) { + event = &EVENT(i, k, line); + + if (chmsk & 0x8000) { + x = stream_read12(&stream); + event->fxt = x >> 8; + event->fxp = x & 0xff; + fix_effect(event, hexvol); + } + } + } + +#ifdef MED4_DEBUG + printf("%03d ", line); + for (k = 0; k < 4; k++) { + event = &EVENT(i, k, line); + if (event->note) + printf("%03d", event->note); + else + printf("---"); + printf(" %1x%1x%02x ", + event->ins, event->fxt, event->fxp); + } + printf("\n"); +#endif + + linemask[y] <<= 1; + fxmask[y] <<= 1; + } + + } + hio_seek(f, pos + size + plen, SEEK_SET); + } + + mod->ins = num_ins; + + if (libxmp_med_new_module_extras(m) != 0) + return -1; + + /* + * Load samples + */ + mask = hio_read32b(f); + if (mask == MAGIC4('M','E','D','V')) { + mod->smp = 0; + + if (libxmp_init_instrument(m) < 0) + return -1; + hio_seek(f, -4, SEEK_CUR); + goto parse_iff; + } + mask <<= 32; + mask |= hio_read32b(f); + + mask <<= 1; /* no instrument #0 */ + + /* obtain number of samples */ + if ((pos = hio_tell(f)) < 0) { + return -1; + } + num_smp = 0; + { + int _len, _type; + uint64 _mask = mask; + for (i = 0; _mask != 0 && i < 64; i++, _mask <<= 1) { + if ((int64)_mask > 0) + continue; + + _len = hio_read32b(f); + _type = (int16)hio_read16b(f); + + if (_type == 0 || _type == -2) { + num_smp++; + } else if (_type == -1) { + if (_len < 22) { + D_(D_CRIT "invalid synth %d length", i); + return -1; + } + hio_seek(f, 20, SEEK_CUR); + num_smp += hio_read16b(f); + _len -= 22; + } + + if (_len < 0) { + D_(D_CRIT "invalid sample %d length", i); + return -1; + } + hio_seek(f, _len, SEEK_CUR); + } + } + hio_seek(f, pos, SEEK_SET); + + mod->smp = num_smp; + + if (libxmp_init_instrument(m) < 0) { + return -1; + } + + D_(D_INFO "Instruments: %d", mod->ins); + + smp_idx = 0; + for (i = 0; mask != 0 && i < num_ins; i++, mask <<= 1) { + int length, type; + struct SynthInstr synth; + struct xmp_instrument *xxi; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + struct med_instrument_extras *ie; + + if ((int64)mask > 0) { + continue; + } + + xxi = &mod->xxi[i]; + + length = hio_read32b(f); + type = (int16)hio_read16b(f); /* instrument type */ + + strncpy((char *)xxi->name, temp_inst[i].name, 32); + xxi->name[31] = '\0'; + + D_(D_INFO "\n[%2X] %-32.32s %d", i, xxi->name, type); + + /* This is very similar to MMD1 synth/hybrid instruments, + * but just different enough to be reimplemented here. + */ + + if (type == -2) { /* Hybrid */ + pos = hio_tell(f); + if (pos < 0) { + return -1; + } + + hio_read32b(f); /* ? - MSH 00 */ + hio_read16b(f); /* ? - ffff */ + hio_read16b(f); /* ? - 0000 */ + hio_read16b(f); /* ? - 0000 */ + synth.rep = hio_read16b(f); /* ? */ + synth.replen = hio_read16b(f); /* ? */ + synth.voltbllen = hio_read16b(f); + synth.wftbllen = hio_read16b(f); + synth.volspeed = hio_read8(f); + synth.wfspeed = hio_read8(f); + synth.wforms = hio_read16b(f); + + /* Sanity check */ + if (synth.voltbllen > 128 || + synth.wftbllen > 128 || + synth.wforms > 256) { + return -1; + } + + hio_read(synth.voltbl, 1, synth.voltbllen, f); + hio_read(synth.wftbl, 1, synth.wftbllen, f); + if (hio_error(f)) + return -1; + + hio_seek(f, pos + hio_read32b(f), SEEK_SET); + length = hio_read32b(f); + type = hio_read16b(f); + + if (libxmp_med_new_instrument_extras(xxi) != 0) + return -1; + + xxi->nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + ie = MED_INSTRUMENT_EXTRAS(*xxi); + ie->vts = synth.volspeed; + ie->wts = synth.wfspeed; + ie->vtlen = synth.voltbllen; + ie->wtlen = synth.wftbllen; + + sub->pan = 0x80; + sub->vol = temp_inst[i].volume; + sub->xpo = temp_inst[i].transpose; + sub->sid = smp_idx; + sub->fin = 0 /*exp_smp.finetune*/; + + xxs = &mod->xxs[smp_idx]; + + xxs->len = length; + xxs->lps = temp_inst[i].loop_start; + xxs->lpe = temp_inst[i].loop_end; + xxs->flg = temp_inst[i].loop_end > 2 ? + XMP_SAMPLE_LOOP : 0; + + D_(D_INFO " %05x %05x %05x %02x %+03d", + xxs->len, xxs->lps, xxs->lpe, + sub->vol, sub->xpo /*, sub->fin >> 4*/); + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + + smp_idx++; + + if (mmd_alloc_tables(m, i, &synth) != 0) + return -1; + + continue; + } + + if (type == -1) { /* Synthetic */ + pos = hio_tell(f); + if (pos < 0) { + return -1; + } + + hio_read32b(f); /* ? - MSH 00 */ + hio_read16b(f); /* ? - ffff */ + hio_read16b(f); /* ? - 0000 */ + hio_read16b(f); /* ? - 0000 */ + synth.rep = hio_read16b(f); /* ? */ + synth.replen = hio_read16b(f); /* ? */ + synth.voltbllen = hio_read16b(f); + synth.wftbllen = hio_read16b(f); + synth.volspeed = hio_read8(f); + synth.wfspeed = hio_read8(f); + synth.wforms = hio_read16b(f); + + if (synth.wforms == 0xffff) + continue; + + /* Sanity check */ + if (synth.voltbllen > 128 || + synth.wftbllen > 128 || + synth.wforms > 64) { + return -1; + } + + hio_read(synth.voltbl, 1, synth.voltbllen, f); + hio_read(synth.wftbl, 1, synth.wftbllen, f); + + for (j = 0; j < synth.wforms; j++) + synth.wf[j] = hio_read32b(f); + + if (hio_error(f)) + return -1; + + D_(D_INFO " VS:%02x WS:%02x WF:%02x %02x %+03d", + synth.volspeed, synth.wfspeed, + synth.wforms & 0xff, + temp_inst[i].volume, + temp_inst[i].transpose /*, + exp_smp.finetune*/); + + if (libxmp_med_new_instrument_extras(&mod->xxi[i]) != 0) + return -1; + + mod->xxi[i].nsm = synth.wforms; + if (libxmp_alloc_subinstrument(mod, i, synth.wforms) < 0) + return -1; + + ie = MED_INSTRUMENT_EXTRAS(*xxi); + ie->vts = synth.volspeed; + ie->wts = synth.wfspeed; + ie->vtlen = synth.voltbllen; + ie->wtlen = synth.wftbllen; + + for (j = 0; j < synth.wforms; j++) { + /* Sanity check */ + if (smp_idx >= num_smp) { + return -1; + } + + sub = &xxi->sub[j]; + + sub->pan = 0x80; + sub->vol = 64; + sub->xpo = -24; + sub->sid = smp_idx; + sub->fin = 0 /*exp_smp.finetune*/; + + hio_seek(f, pos + synth.wf[j], SEEK_SET); + + xxs = &mod->xxs[smp_idx]; + + xxs->len = hio_read16b(f) * 2; + xxs->lps = 0; + xxs->lpe = xxs->len; + xxs->flg = XMP_SAMPLE_LOOP; + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) { + return -1; + } + + smp_idx++; + } + + if (mmd_alloc_tables(m, i, &synth) != 0) + return -1; + + hio_seek(f, pos + length, SEEK_SET); + continue; + } + + if (type != 0) { + hio_seek(f, length, SEEK_CUR); + continue; + } + + /* instr type is sample */ + xxi->nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + sub->vol = temp_inst[i].volume; + sub->pan = 0x80; + sub->xpo = temp_inst[i].transpose; + sub->sid = smp_idx; + + /* Sanity check */ + if (smp_idx >= mod->smp) + return -1; + + xxs = &mod->xxs[smp_idx]; + + xxs->len = length; + xxs->lps = temp_inst[i].loop_start; + xxs->lpe = temp_inst[i].loop_end; + xxs->flg = temp_inst[i].loop_end > 2 ? XMP_SAMPLE_LOOP : 0; + + D_(D_INFO " %04x %04x %04x %c V%02x %+03d", + xxs->len, mod->xxs[smp_idx].lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, sub->xpo); + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + + /* Limit range to 3 octave (see MED.El toro) */ + for (j = 0; j < 9; j++) { + for (k = 0; k < 12; k++) { + int xpo = 0; + + if (j < 4) + xpo = 12 * (4 - j); + else if (j > 6) + xpo = -12 * (j - 6); + + xxi->map[12 * j + k].xpo = xpo; + } + } + + smp_idx++; + } + + /* Not sure what this was supposed to be, but it isn't present in + * Synth-a-sysmic.med or any other MED4 module on ModLand. */ + /*hio_read16b(f);*/ /* unknown */ + + /* IFF-like section */ +parse_iff: + while (!hio_eof(f)) { + int32 id, size, s2, ver; + + if ((id = hio_read32b(f)) <= 0) + break; + + if ((size = hio_read32b(f)) <= 0) + break; + + switch (id) { + case MAGIC4('M','E','D','V'): + ver = hio_read32b(f); + size -= 4; + vermaj = (ver & 0xff00) >> 8; + vermin = (ver & 0xff); + D_(D_INFO "MED Version: %d.%0d", vermaj, vermin); + break; + case MAGIC4('A','N','N','O'): + /* annotation */ + s2 = size < 1023 ? size : 1023; + if ((m->comment = (char *) malloc(s2 + 1)) != NULL) { + int read_len = hio_read(m->comment, 1, s2, f); + m->comment[read_len] = '\0'; + + D_(D_INFO "Annotation: %s", m->comment); + size -= s2; + } + break; + case MAGIC4('H','L','D','C'): + /* hold & decay */ + break; + } + + hio_seek(f, size, SEEK_CUR); + } + + m->read_event_type = READ_EVENT_MED; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/mfp_load.c b/thirdparty/libxmp/src/loaders/mfp_load.c new file mode 100644 index 000000000..7aa2c6a48 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mfp_load.c @@ -0,0 +1,253 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * A module packer created by Shaun Southern. Samples are stored in a + * separate file. File prefixes are mfp for song and smp for samples. For + * more information see http://www.exotica.org.uk/wiki/Magnetic_Fields_Packer + */ + +#include "loader.h" + +static int mfp_test(HIO_HANDLE *, char *, const int); +static int mfp_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mfp = { + "Magnetic Fields Packer", + mfp_test, + mfp_load +}; + +static int mfp_test(HIO_HANDLE *f, char *t, const int start) +{ + uint8 buf[384]; + int i, len, lps, lsz; + + if (HIO_HANDLE_TYPE(f) != HIO_HANDLE_TYPE_FILE) + return -1; + + if (hio_read(buf, 1, 384, f) < 384) + return -1; + + /* check restart byte */ + if (buf[249] != 0x7f) + return -1; + + for (i = 0; i < 31; i++) { + /* check size */ + len = readmem16b(buf + i * 8); + if (len > 0x7fff) + return -1; + + /* check finetune */ + if (buf[i * 8 + 2] & 0xf0) + return -1; + + /* check volume */ + if (buf[i * 8 + 3] > 0x40) + return -1; + + /* check loop start */ + lps = readmem16b(buf + i * 8 + 4); + if (lps > len) + return -1; + + /* check loop size */ + lsz = readmem16b(buf + i * 8 + 6); + if (lps + lsz - 1 > len) + return -1; + + if (len > 0 && lsz == 0) + return -1; + } + + if (buf[248] != readmem16b(buf + 378)) + return -1; + + if (readmem16b(buf + 378) != readmem16b(buf + 380)) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +static int mfp_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k, x, y; + struct xmp_event *event; + char smp_filename[XMP_MAXPATH]; + HIO_HANDLE *s; + int size1 /*, size2*/; + int pat_addr, pat_table[128][4]; + uint8 buf[1024], mod_event[4]; + int row; + + LOAD_INIT(); + + libxmp_set_type(m, "Magnetic Fields Packer"); + MODULE_INFO(); + + mod->chn = 4; + mod->ins = mod->smp = 31; + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < 31; i++) { + int loop_size; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = 2 * hio_read16b(f); + mod->xxi[i].sub[0].fin = (int8)(hio_read8(f) << 4); + mod->xxi[i].sub[0].vol = hio_read8(f); + mod->xxs[i].lps = 2 * hio_read16b(f); + loop_size = hio_read16b(f); + + mod->xxs[i].lpe = mod->xxs[i].lps + 2 * loop_size; + mod->xxs[i].flg = loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].rls = 0xfff; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + D_(D_INFO "[%2X] %04x %04x %04x %c V%02x %+d", + i, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + loop_size > 1 ? 'L' : ' ', + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].fin >> 4); + } + + mod->len = mod->pat = hio_read8(f); + hio_read8(f); /* restart */ + + for (i = 0; i < 128; i++) { + mod->xxo[i] = hio_read8(f); + } + + if (hio_error(f)) { + return -1; + } + + mod->trk = mod->pat * mod->chn; + + /* Read and convert patterns */ + + if (libxmp_init_pattern(mod) < 0) + return -1; + + size1 = hio_read16b(f); + /* size2 = */ hio_read16b(f); + + for (i = 0; i < size1; i++) { /* Read pattern table */ + for (j = 0; j < 4; j++) { + pat_table[i][j] = hio_read16b(f); + } + } + + D_(D_INFO "Stored patterns: %d ", mod->pat); + + pat_addr = hio_tell(f); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < 4; j++) { + size_t len; + hio_seek(f, pat_addr + pat_table[i][j], SEEK_SET); + + len = hio_read(buf, 1, 1024, f); + + for (row = k = 0; k < 4; k++) { + for (x = 0; x < 4; x++) { + for (y = 0; y < 4; y++, row++) { + event = &EVENT(i, j, row); + + if (k >= len || + buf[k] + x >= len || + buf[buf[k] + x] + y >= len || + buf[buf[buf[k] + x] + y] * 2 + 4 > len) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + memcpy(mod_event, &buf[buf[buf[buf[k] + x] + y] * 2], 4); + libxmp_decode_protracker_event(event, mod_event); + } + } + } + } + } + + /* Read samples */ + D_(D_INFO "Loading samples: %d", mod->ins); + + /* first check smp.filename */ + if (strlen(m->basename) < 5 || m->basename[3] != '.') { + D_(D_CRIT "invalid filename %s", m->basename); + goto err; + } + + m->basename[0] = 's'; + m->basename[1] = 'm'; + m->basename[2] = 'p'; + snprintf(smp_filename, XMP_MAXPATH, "%s%s", m->dirname, m->basename); + if ((s = hio_open(smp_filename, "rb")) == NULL) { + /* handle .set filenames like in Kid Chaos*/ + if (strchr(m->basename, '-')) { + char *p = strrchr(smp_filename, '-'); + if (p != NULL) + strcpy(p, ".set"); + } + if ((s = hio_open(smp_filename, "rb")) == NULL) { + D_(D_CRIT "can't open sample file %s", smp_filename); + goto err; + } + } + + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, s, SAMPLE_FLAG_FULLREP, + &mod->xxs[mod->xxi[i].sub[0].sid], NULL) < 0) { + free(s); + return -1; + } + } + + hio_close(s); + + m->period_type = PERIOD_MODRNG; + + return 0; + + err: + for (i = 0; i < mod->ins; i++) { + mod->xxi[i].nsm = 0; + memset(&mod->xxs[i], 0, sizeof(struct xmp_sample)); + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/mgt_load.c b/thirdparty/libxmp/src/loaders/mgt_load.c new file mode 100644 index 000000000..98900ccf5 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mgt_load.c @@ -0,0 +1,382 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +#define MAGIC_MGT MAGIC4(0x00,'M','G','T') +#define MAGIC_MCS MAGIC4(0xbd,'M','C','S') + + +static int mgt_test (HIO_HANDLE *, char *, const int); +static int mgt_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mgt = { + "Megatracker", + mgt_test, + mgt_load +}; + +static int mgt_test(HIO_HANDLE *f, char *t, const int start) +{ + int sng_ptr; + + if (hio_read24b(f) != MAGIC_MGT) + return -1; + hio_read8(f); + if (hio_read32b(f) != MAGIC_MCS) + return -1; + + hio_seek(f, 18, SEEK_CUR); + sng_ptr = hio_read32b(f); + hio_seek(f, start + sng_ptr, SEEK_SET); + + libxmp_read_title(f, t, 32); + + return 0; +} + +static int mgt_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + int i, j; + int ver; + int sng_ptr, seq_ptr, ins_ptr, pat_ptr, trk_ptr; + int sdata[64]; + + LOAD_INIT(); + + hio_read24b(f); /* MGT */ + ver = hio_read8(f); + hio_read32b(f); /* MCS */ + + libxmp_set_type(m, "Megatracker MGT v%d.%d", MSN(ver), LSN(ver)); + + mod->chn = hio_read16b(f); + hio_read16b(f); /* number of songs */ + mod->len = hio_read16b(f); + mod->pat = hio_read16b(f); + mod->trk = hio_read16b(f); + mod->ins = mod->smp = hio_read16b(f); + hio_read16b(f); /* reserved */ + hio_read32b(f); /* reserved */ + + /* Sanity check */ + if (mod->chn > XMP_MAX_CHANNELS || mod->pat > MAX_PATTERNS || mod->ins > 64) { + return -1; + } + + sng_ptr = hio_read32b(f); + seq_ptr = hio_read32b(f); + ins_ptr = hio_read32b(f); + pat_ptr = hio_read32b(f); + trk_ptr = hio_read32b(f); + hio_read32b(f); /* sample offset */ + hio_read32b(f); /* total smp len */ + hio_read32b(f); /* unpacked trk size */ + + hio_seek(f, start + sng_ptr, SEEK_SET); + + hio_read(mod->name, 1, 32, f); + seq_ptr = hio_read32b(f); + mod->len = hio_read16b(f); + mod->rst = hio_read16b(f); + mod->bpm = hio_read8(f); + mod->spd = hio_read8(f); + hio_read16b(f); /* global volume */ + hio_read8(f); /* master L */ + hio_read8(f); /* master R */ + + /* Sanity check */ + if (mod->len > XMP_MAX_MOD_LENGTH || mod->rst > 255) { + return -1; + } + + for (i = 0; i < mod->chn; i++) { + hio_read16b(f); /* pan */ + } + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + /* Sequence */ + + hio_seek(f, start + seq_ptr, SEEK_SET); + for (i = 0; i < mod->len; i++) { + int pos = hio_read16b(f); + + /* Sanity check */ + if (pos >= mod->pat) { + return -1; + } + mod->xxo[i] = pos; + } + + /* Instruments */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + hio_seek(f, start + ins_ptr, SEEK_SET); + + for (i = 0; i < mod->ins; i++) { + int c2spd, flags; + + if (libxmp_alloc_subinstrument(mod, i , 1) < 0) + return -1; + + hio_read(mod->xxi[i].name, 1, 32, f); + sdata[i] = hio_read32b(f); + mod->xxs[i].len = hio_read32b(f); + + /* Sanity check */ + if (mod->xxs[i].len > MAX_SAMPLE_SIZE) { + return -1; + } + + mod->xxs[i].lps = hio_read32b(f); + mod->xxs[i].lpe = mod->xxs[i].lps + hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + c2spd = hio_read32b(f); + libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + mod->xxi[i].sub[0].vol = hio_read16b(f) >> 4; + hio_read8(f); /* vol L */ + hio_read8(f); /* vol R */ + mod->xxi[i].sub[0].pan = 0x80; + flags = hio_read8(f); + mod->xxs[i].flg = flags & 0x03 ? XMP_SAMPLE_LOOP : 0; + mod->xxs[i].flg |= flags & 0x02 ? XMP_SAMPLE_LOOP_BIDIR : 0; + mod->xxi[i].sub[0].fin += 0 * hio_read8(f); // FIXME + hio_read8(f); /* unused */ + hio_read8(f); + hio_read8(f); + hio_read8(f); + hio_read16b(f); + hio_read32b(f); + hio_read32b(f); + + mod->xxi[i].nsm = !!mod->xxs[i].len; + mod->xxi[i].sub[0].sid = i; + + D_(D_INFO "[%2X] %-32.32s %04x %04x %04x %c V%02x %5d\n", + i, mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, c2spd); + } + + /* PATTERN_INIT - alloc extra track*/ + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored tracks: %d", mod->trk); + + /* Tracks */ + + for (i = 1; i < mod->trk; i++) { + int offset, rows; + uint8 b; + + hio_seek(f, start + trk_ptr + i * 4, SEEK_SET); + offset = hio_read32b(f); + hio_seek(f, start + offset, SEEK_SET); + + rows = hio_read16b(f); + + /* Sanity check */ + if (rows > 255) + return -1; + + if (libxmp_alloc_track(mod, i, rows) < 0) + return -1; + + //printf("\n=== Track %d ===\n\n", i); + for (j = 0; j < rows; j++) { + uint8 note; + /* TODO libxmp can't really support the wide effect + * params Megatracker uses right now, but less bad + * conversions of certain effects could be attempted. */ + /* uint8 f2p ;*/ + + b = hio_read8(f); + j += b & 0x03; + + /* Sanity check */ + if (j >= rows) + return -1; + + note = 0; + event = &mod->xxt[i]->event[j]; + if (b & 0x04) + note = hio_read8(f); + if (b & 0x08) + event->ins = hio_read8(f); + if (b & 0x10) + event->vol = hio_read8(f); + if (b & 0x20) + event->fxt = hio_read8(f); + if (b & 0x40) + event->fxp = hio_read8(f); + if (b & 0x80) + /*f2p =*/ hio_read8(f); + + if (note == 1) + event->note = XMP_KEY_OFF; + else if (note > 11) /* adjusted to play codeine.mgt */ + event->note = note + 1; + + /* effects */ + if (event->fxt < 0x10) + /* like amiga */ ; + else switch (event->fxt) { + case 0x13: + case 0x14: + case 0x15: + case 0x17: + case 0x1c: + case 0x1d: + case 0x1e: + event->fxt = FX_EXTENDED; + event->fxp = ((event->fxt & 0x0f) << 4) | + (event->fxp & 0x0f); + break; + default: + event->fxt = event->fxp = 0; + } + + /* volume and volume column effects */ + if ((event->vol >= 0x10) && (event->vol <= 0x50)) { + event->vol -= 0x0f; + continue; + } + + switch (event->vol >> 4) { + case 0x06: /* Volume slide down */ + event->f2t = FX_VOLSLIDE_2; + event->f2p = event->vol - 0x60; + break; + case 0x07: /* Volume slide up */ + event->f2t = FX_VOLSLIDE_2; + event->f2p = (event->vol - 0x70) << 4; + break; + case 0x08: /* Fine volume slide down */ + event->f2t = FX_EXTENDED; + event->f2p = (EX_F_VSLIDE_DN << 4) | + (event->vol - 0x80); + break; + case 0x09: /* Fine volume slide up */ + event->f2t = FX_EXTENDED; + event->f2p = (EX_F_VSLIDE_UP << 4) | + (event->vol - 0x90); + break; + case 0x0a: /* Set vibrato speed */ + event->f2t = FX_VIBRATO; + event->f2p = (event->vol - 0xa0) << 4; + break; + case 0x0b: /* Vibrato */ + event->f2t = FX_VIBRATO; + event->f2p = event->vol - 0xb0; + break; + case 0x0c: /* Set panning */ + event->f2t = FX_SETPAN; + event->f2p = ((event->vol - 0xc0) << 4) + 8; + break; + case 0x0d: /* Pan slide left */ + event->f2t = FX_PANSLIDE; + event->f2p = (event->vol - 0xd0) << 4; + break; + case 0x0e: /* Pan slide right */ + event->f2t = FX_PANSLIDE; + event->f2p = event->vol - 0xe0; + break; + case 0x0f: /* Tone portamento */ + event->f2t = FX_TONEPORTA; + event->f2p = (event->vol - 0xf0) << 4; + break; + } + + event->vol = 0; + + /*printf("%02x %02x %02x %02x %02x %02x\n", + j, event->note, event->ins, event->vol, + event->fxt, event->fxp);*/ + } + } + + /* Extra track */ + if (mod->trk > 0) { + mod->xxt[0] = (struct xmp_track *) calloc(1, sizeof(struct xmp_track) + + sizeof(struct xmp_event) * 64 - 1); + mod->xxt[0]->rows = 64; + } + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + hio_seek(f, start + pat_ptr, SEEK_SET); + + for (i = 0; i < mod->pat; i++) { + int rows; + + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + + rows = hio_read16b(f); + + /* Sanity check */ + if (rows > 256) { + return -1; + } + + mod->xxp[i]->rows = rows; + + for (j = 0; j < mod->chn; j++) { + int track = hio_read16b(f) - 1; + + /* Sanity check */ + if (track >= mod->trk) { + return -1; + } + + mod->xxp[i]->index[j] = track; + } + } + + /* Read samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxi[i].nsm == 0) + continue; + + hio_seek(f, start + sdata[i], SEEK_SET); + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/mmd1_load.c b/thirdparty/libxmp/src/loaders/mmd1_load.c new file mode 100644 index 000000000..ab4b081cc --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mmd1_load.c @@ -0,0 +1,629 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * OctaMED v1.00b: ftp://ftp.funet.fi/pub/amiga/fish/501-600/ff579 + */ + +#include "med.h" +#include "loader.h" +#include "../med_extras.h" + +static int mmd1_test(HIO_HANDLE *, char *, const int); +static int mmd1_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mmd1 = { + "MED 2.10/OctaMED", + mmd1_test, + mmd1_load +}; + +static int mmd1_test(HIO_HANDLE *f, char *t, const int start) +{ + char id[4]; + uint32 offset, len; + + if (hio_read(id, 1, 4, f) < 4) + return -1; + + if (memcmp(id, "MMD0", 4) && memcmp(id, "MMD1", 4) && memcmp(id, "MMDC", 4)) + return -1; + + hio_seek(f, 28, SEEK_CUR); + offset = hio_read32b(f); /* expdata_offset */ + + if (offset) { + hio_seek(f, start + offset + 44, SEEK_SET); + offset = hio_read32b(f); + len = hio_read32b(f); + hio_seek(f, start + offset, SEEK_SET); + libxmp_read_title(f, t, len); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + + +static int mmd1_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + struct MMD0 header; + struct MMD0song song; + struct MMD1Block block; + struct InstrExt *exp_smp = NULL; + struct MMD0exp expdata; + struct xmp_event *event; + uint32 *blockarr = NULL; + uint32 *smplarr = NULL; + uint8 *patbuf = NULL; + int ver = 0; + int mmdc = 0; + int smp_idx = 0; + int song_offset; + int blockarr_offset; + int smplarr_offset; + int expdata_offset; + int expsmp_offset; + int songname_offset; + int iinfo_offset; + int annotxt_offset; + int bpm_on, bpmlen, med_8ch, hexvol; + int max_lines; + int retval = -1; + + LOAD_INIT(); + + hio_read(&header.id, 4, 1, f); + + ver = *((char *)&header.id + 3) - '1' + 1; + if (ver > 1) { + ver = 0; + mmdc = 1; + } + + D_(D_WARN "load header"); + header.modlen = hio_read32b(f); + song_offset = hio_read32b(f); + D_(D_INFO "song_offset = 0x%08x", song_offset); + hio_read16b(f); + hio_read16b(f); + blockarr_offset = hio_read32b(f); + D_(D_INFO "blockarr_offset = 0x%08x", blockarr_offset); + hio_read32b(f); + smplarr_offset = hio_read32b(f); + D_(D_INFO "smplarr_offset = 0x%08x", smplarr_offset); + hio_read32b(f); + expdata_offset = hio_read32b(f); + D_(D_INFO "expdata_offset = 0x%08x", expdata_offset); + hio_read32b(f); + header.pstate = hio_read16b(f); + header.pblock = hio_read16b(f); + header.pline = hio_read16b(f); + header.pseqnum = hio_read16b(f); + header.actplayline = hio_read16b(f); + header.counter = hio_read8(f); + header.extra_songs = hio_read8(f); + + /* + * song structure + */ + D_(D_WARN "load song"); + if (hio_seek(f, start + song_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at song"); + return -1; + } + for (i = 0; i < 63; i++) { + song.sample[i].rep = hio_read16b(f); + song.sample[i].replen = hio_read16b(f); + song.sample[i].midich = hio_read8(f); + song.sample[i].midipreset = hio_read8(f); + song.sample[i].svol = hio_read8(f); + song.sample[i].strans = hio_read8s(f); + } + song.numblocks = hio_read16b(f); + song.songlen = hio_read16b(f); + + /* Sanity check */ + if (song.numblocks > 255 || song.songlen > 256) { + D_(D_CRIT "unsupported block count (%d) or song length (%d)", + song.numblocks, song.songlen); + return -1; + } + + D_(D_INFO "song.songlen = %d", song.songlen); + for (i = 0; i < 256; i++) + song.playseq[i] = hio_read8(f); + song.deftempo = hio_read16b(f); + song.playtransp = hio_read8(f); + song.flags = hio_read8(f); + song.flags2 = hio_read8(f); + song.tempo2 = hio_read8(f); + for (i = 0; i < 16; i++) + song.trkvol[i] = hio_read8(f); + song.mastervol = hio_read8(f); + song.numsamples = hio_read8(f); + + /* Sanity check */ + if (song.numsamples > 63) { + D_(D_CRIT "invalid instrument count %d", song.numsamples); + return -1; + } + + /* + * convert header + */ + m->c4rate = C4_NTSC_RATE; + m->quirk |= song.flags & FLAG_STSLIDE ? 0 : QUIRK_VSALL | QUIRK_PBALL; + hexvol = song.flags & FLAG_VOLHEX; + med_8ch = song.flags & FLAG_8CHANNEL; + bpm_on = song.flags2 & FLAG2_BPM; + bpmlen = 1 + (song.flags2 & FLAG2_BMASK); + m->time_factor = MED_TIME_FACTOR; + + mmd_set_bpm(m, med_8ch, song.deftempo, bpm_on, bpmlen); + + mod->spd = song.tempo2; + mod->pat = song.numblocks; + mod->ins = song.numsamples; + mod->len = song.songlen; + mod->rst = 0; + mod->chn = 0; + memcpy(mod->xxo, song.playseq, mod->len); + mod->name[0] = 0; + + /* + * Read smplarr + */ + D_(D_WARN "read smplarr"); + smplarr = (uint32 *) malloc(mod->ins * sizeof(uint32)); + if (smplarr == NULL) { + return -1; + } + if (hio_seek(f, start + smplarr_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at smplarr"); + goto err_cleanup; + } + for (i = 0; i < mod->ins; i++) { + smplarr[i] = hio_read32b(f); + if (hio_eof(f)) { + D_(D_CRIT "read error at smplarr pos %d", i); + goto err_cleanup; + } + } + + /* + * Obtain number of samples from each instrument + */ + mod->smp = 0; + for (i = 0; i < mod->ins; i++) { + int16 type; + if (smplarr[i] == 0) + continue; + if (hio_seek(f, start + smplarr[i], SEEK_SET) != 0) { + D_(D_CRIT "seek error at instrument %d", i); + goto err_cleanup; + } + + hio_read32b(f); /* length */ + type = hio_read16b(f); + if (type == -1 || type == -2) { /* type is synth? */ + int wforms; + hio_seek(f, 14, SEEK_CUR); + wforms = hio_read16b(f); + + /* Sanity check */ + if (wforms > 256) { + D_(D_CRIT "invalid wform count at instrument %d", i); + goto err_cleanup; + } + + mod->smp += wforms; + } else if (type >= 1 && type <= 6) { + mod->smp += mmd_num_oct[type - 1]; + } else { + mod->smp++; + } + } + + /* + * expdata + */ + D_(D_WARN "load expdata"); + expdata.s_ext_entries = 0; + expdata.s_ext_entrsz = 0; + expdata.i_ext_entries = 0; + expdata.i_ext_entrsz = 0; + expsmp_offset = 0; + iinfo_offset = 0; + if (expdata_offset) { + if (hio_seek(f, start + expdata_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at expdata"); + goto err_cleanup; + } + hio_read32b(f); + expsmp_offset = hio_read32b(f); + D_(D_INFO "expsmp_offset = 0x%08x", expsmp_offset); + expdata.s_ext_entries = hio_read16b(f); + expdata.s_ext_entrsz = hio_read16b(f); + annotxt_offset = hio_read32b(f); + expdata.annolen = hio_read32b(f); + iinfo_offset = hio_read32b(f); + D_(D_INFO "iinfo_offset = 0x%08x", iinfo_offset); + expdata.i_ext_entries = hio_read16b(f); + expdata.i_ext_entrsz = hio_read16b(f); + + /* Sanity check */ + if (expsmp_offset < 0 || + annotxt_offset < 0 || + expdata.annolen > 0x10000 || + iinfo_offset < 0) { + D_(D_CRIT "invalid expdata (annotxt=0x%08x annolen=0x%08x)", + annotxt_offset, expdata.annolen); + goto err_cleanup; + } + + hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + songname_offset = hio_read32b(f); + expdata.songnamelen = hio_read32b(f); + D_(D_INFO "songname_offset = 0x%08x", songname_offset); + D_(D_INFO "expdata.songnamelen = %d", expdata.songnamelen); + + hio_seek(f, start + songname_offset, SEEK_SET); + for (i = 0; i < expdata.songnamelen; i++) { + if (i >= XMP_NAME_SIZE) + break; + mod->name[i] = hio_read8(f); + } + + /* Read annotation */ + if (annotxt_offset != 0 && expdata.annolen != 0) { + D_(D_INFO "annotxt_offset = 0x%08x", annotxt_offset); + m->comment = (char *) malloc(expdata.annolen + 1); + if (m->comment != NULL) { + hio_seek(f, start + annotxt_offset, SEEK_SET); + hio_read(m->comment, 1, expdata.annolen, f); + m->comment[expdata.annolen] = 0; + } + } + } + + /* + * Read blockarr. + */ + D_(D_WARN "read blockarr"); + blockarr = (uint32 *) malloc(mod->pat * sizeof(uint32)); + if (blockarr == NULL) { + goto err_cleanup; + } + if (hio_seek(f, start + blockarr_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at blockarr"); + goto err_cleanup; + } + for (i = 0; i < mod->pat; i++) { + blockarr[i] = hio_read32b(f); + if (hio_error(f)) { + D_(D_CRIT "read error at blockarr pos %d", i); + goto err_cleanup; + } + } + + /* + * Quickly scan patterns to check the number of channels + */ + D_(D_WARN "find number of channels"); + + max_lines = 1; + for (i = 0; i < mod->pat; i++) { + D_(D_INFO "block %d block_offset = 0x%08x", i, blockarr[i]); + if (blockarr[i] == 0) + continue; + + if (hio_seek(f, start + blockarr[i], SEEK_SET) != 0) { + D_(D_CRIT "seek error at block %d", i); + goto err_cleanup; + } + + if (ver > 0) { + block.numtracks = hio_read16b(f); + block.lines = hio_read16b(f); + } else { + block.numtracks = hio_read8(f); + block.lines = hio_read8(f); + } + + /* Sanity check--Amiga OctaMED files have an upper bound of 3200 lines per block. */ + if (block.lines + 1 > 3200) { + D_(D_CRIT "invalid line count %d in block %d", block.lines + 1, i); + goto err_cleanup; + } + + if (block.numtracks > mod->chn) { + mod->chn = block.numtracks; + } + if (block.lines + 1 > max_lines) { + max_lines = block.lines + 1; + } + } + + /* Sanity check */ + /* MMD0/MMD1 can't have more than 16 channels... */ + if (mod->chn > MIN(16, XMP_MAX_CHANNELS)) { + D_(D_CRIT "invalid channel count %d", mod->chn); + goto err_cleanup; + } + + mod->trk = mod->pat * mod->chn; + + libxmp_set_type(m, ver == 0 ? mmdc ? "MED Packer MMDC" : + mod->chn > 4 ? "OctaMED 2.00 MMD0" : + "MED 2.10 MMD0" : "OctaMED 4.00 MMD1"); + + MODULE_INFO(); + + D_(D_INFO "BPM mode: %s (length = %d)", bpm_on ? "on" : "off", bpmlen); + D_(D_INFO "Song transpose: %d", song.playtransp); + D_(D_INFO "Stored patterns: %d", mod->pat); + + /* + * Read and convert patterns + */ + D_(D_WARN "read patterns"); + if (libxmp_init_pattern(mod) < 0) + goto err_cleanup; + + if ((patbuf = (uint8 *)malloc(mod->chn * max_lines * 4)) == NULL) { + goto err_cleanup; + } + + for (i = 0; i < mod->pat; i++) { + uint8 *pos; + size_t size; + + if (blockarr[i] == 0) + continue; + + if (hio_seek(f, start + blockarr[i], SEEK_SET) != 0) { + D_(D_CRIT "seek error at block %d", i); + goto err_cleanup; + } + + if (ver > 0) { + block.numtracks = hio_read16b(f); + block.lines = hio_read16b(f); + hio_read32b(f); + } else { + block.numtracks = hio_read8(f); + block.lines = hio_read8(f); + } + + size = block.numtracks * (block.lines + 1) * (ver ? 4 : 3); + + if (mmdc) { + /* MMDC is just MMD0 with simple pattern packing. */ + memset(patbuf, 0, size); + for (j = 0; j < size;) { + unsigned pack = hio_read8(f); + if (hio_error(f)) { + D_(D_CRIT "read error in block %d", i); + goto err_cleanup; + } + + if (pack & 0x80) { + /* Run of 0 */ + j += 256 - pack; + continue; + } + /* Uncompressed block */ + pack++; + if (pack > size - j) + pack = size - j; + + if (hio_read(patbuf + j, 1, pack, f) < pack) { + D_(D_CRIT "read error in block %d", i); + goto err_cleanup; + } + j += pack; + } + } else { + if (hio_read(patbuf, 1, size, f) < size) { + D_(D_CRIT "read error in block %d", i); + goto err_cleanup; + } + } + + if (libxmp_alloc_pattern_tracks_long(mod, i, block.lines + 1) < 0) + goto err_cleanup; + + pos = patbuf; + if (ver > 0) { /* MMD1 */ + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < block.numtracks; k++) { + event = &EVENT(i, k, j); + event->note = pos[0] & 0x7f; + if (event->note) + event->note += + 12 + song.playtransp; + + if (event->note >= XMP_MAX_KEYS) + event->note = 0; + + event->ins = pos[1] & 0x3f; + + /* Decay */ + if (event->ins && !event->note) { + event->f2t = FX_MED_HOLD; + } + + event->fxt = pos[2]; + event->fxp = pos[3]; + mmd_xlat_fx(event, bpm_on, bpmlen, + med_8ch, hexvol); + pos += 4; + } + } + } else { /* MMD0 */ + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < block.numtracks; k++) { + event = &EVENT(i, k, j); + event->note = pos[0] & 0x3f; + if (event->note) + event->note += 12 + song.playtransp; + + if (event->note >= XMP_MAX_KEYS) + event->note = 0; + + event->ins = + (pos[1] >> 4) | ((pos[0] & 0x80) >> 3) + | ((pos[0] & 0x40) >> 1); + + /* Decay */ + if (event->ins && !event->note) { + event->f2t = FX_MED_HOLD; + } + + event->fxt = pos[1] & 0x0f; + event->fxp = pos[2]; + mmd_xlat_fx(event, bpm_on, bpmlen, + med_8ch, hexvol); + pos += 3; + } + } + } + } + free(patbuf); + patbuf = NULL; + + if (libxmp_med_new_module_extras(m)) + goto err_cleanup; + + /* + * Read and convert instruments and samples + */ + D_(D_WARN "read instruments"); + if (libxmp_init_instrument(m) < 0) + goto err_cleanup; + + D_(D_INFO "Instruments: %d", mod->ins); + + /* Instrument extras */ + exp_smp = (struct InstrExt *) calloc(mod->ins, sizeof(struct InstrExt)); + if (exp_smp == NULL) { + goto err_cleanup; + } + + if (expsmp_offset) { + if (hio_seek(f, start + expsmp_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at expsmp"); + goto err_cleanup; + } + + for (i = 0; i < mod->ins && i < expdata.s_ext_entries; i++) { + int skip = expdata.s_ext_entrsz - 4; + + D_(D_INFO "sample %d expsmp_offset = 0x%08lx", i, hio_tell(f)); + + exp_smp[i].hold = hio_read8(f); + exp_smp[i].decay = hio_read8(f); + exp_smp[i].suppress_midi_off = hio_read8(f); + exp_smp[i].finetune = hio_read8(f); + + if (hio_error(f)) { + D_(D_CRIT "read error at expsmp"); + goto err_cleanup; + } + + if (skip && hio_seek(f, skip, SEEK_CUR) != 0) { + D_(D_CRIT "seek error at expsmp"); + goto err_cleanup; + } + } + } + + /* Instrument names */ + if (iinfo_offset) { + uint8 name[40]; + + if (hio_seek(f, start + iinfo_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at iinfo"); + goto err_cleanup; + } + + for (i = 0; i < mod->ins && i < expdata.i_ext_entries; i++) { + int skip = expdata.i_ext_entrsz - 40; + + D_(D_INFO "sample %d iinfo_offset = 0x%08lx", i, hio_tell(f)); + + if (hio_read(name, 40, 1, f) < 1) { + D_(D_CRIT "read error at iinfo %d", i); + goto err_cleanup; + } + libxmp_instrument_name(mod, i, name, 40); + + if (skip && hio_seek(f, skip, SEEK_CUR) != 0) { + D_(D_CRIT "seek error at iinfo %d", i); + goto err_cleanup; + } + } + } + + /* Sample data */ + for (smp_idx = i = 0; i < mod->ins; i++) { + D_(D_INFO "sample %d smpl_offset = 0x%08x", i, smplarr[i]); + if (smplarr[i] == 0) { + continue; + } + + if (hio_seek(f, start + smplarr[i], SEEK_SET) < 0) { + D_(D_CRIT "seek error at instrument %d", i); + goto err_cleanup; + } + + smp_idx = mmd_load_instrument(f, m, i, smp_idx, &expdata, + &exp_smp[i], &song.sample[i], ver); + + if (smp_idx < 0) { + goto err_cleanup; + } + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].vol = song.trkvol[i]; + mod->xxc[i].pan = DEFPAN((((i + 1) / 2) % 2) * 0xff); + } + + m->read_event_type = READ_EVENT_MED; + retval = 0; + + err_cleanup: + free(exp_smp); + free(blockarr); + free(smplarr); + free(patbuf); + + return retval; +} diff --git a/thirdparty/libxmp/src/loaders/mmd3_load.c b/thirdparty/libxmp/src/loaders/mmd3_load.c new file mode 100644 index 000000000..3ab74f4d0 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mmd3_load.c @@ -0,0 +1,596 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "med.h" +#include "loader.h" +#include "../med_extras.h" + +static int mmd3_test (HIO_HANDLE *, char *, const int); +static int mmd3_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mmd3 = { + "OctaMED", + mmd3_test, + mmd3_load +}; + +static int mmd3_test(HIO_HANDLE *f, char *t, const int start) +{ + char id[4]; + uint32 offset, len; + + if (hio_read(id, 1, 4, f) < 4) + return -1; + + if (memcmp(id, "MMD2", 4) && memcmp(id, "MMD3", 4)) + return -1; + + hio_seek(f, 28, SEEK_CUR); + offset = hio_read32b(f); /* expdata_offset */ + + if (offset) { + hio_seek(f, start + offset + 44, SEEK_SET); + offset = hio_read32b(f); + len = hio_read32b(f); + hio_seek(f, start + offset, SEEK_SET); + libxmp_read_title(f, t, len); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + + +static int mmd3_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + struct MMD0 header; + struct MMD2song song; + struct MMD1Block block; + struct InstrExt *exp_smp = NULL; + struct MMD0exp expdata; + struct xmp_event *event; + uint32 *blockarr = NULL; + uint32 *smplarr = NULL; + uint8 *patbuf = NULL; + int ver = 0; + int smp_idx = 0; + int song_offset; + int seqtable_offset; + int trackvols_offset; + int trackpans_offset; + int blockarr_offset; + int smplarr_offset; + int expdata_offset; + int expsmp_offset; + int songname_offset; + int iinfo_offset; + int mmdinfo_offset; + int playseq_offset; + int bpm_on, bpmlen, med_8ch, hexvol; + int max_lines; + int retval = -1; + + LOAD_INIT(); + + hio_read(&header.id, 4, 1, f); + + ver = *((char *)&header.id + 3) - '1' + 1; + + D_(D_WARN "load header"); + header.modlen = hio_read32b(f); + song_offset = hio_read32b(f); + D_(D_INFO "song_offset = 0x%08x", song_offset); + hio_read16b(f); + hio_read16b(f); + blockarr_offset = hio_read32b(f); + D_(D_INFO "blockarr_offset = 0x%08x", blockarr_offset); + hio_read32b(f); + smplarr_offset = hio_read32b(f); + D_(D_INFO "smplarr_offset = 0x%08x", smplarr_offset); + hio_read32b(f); + expdata_offset = hio_read32b(f); + D_(D_INFO "expdata_offset = 0x%08x", expdata_offset); + hio_read32b(f); + header.pstate = hio_read16b(f); + header.pblock = hio_read16b(f); + header.pline = hio_read16b(f); + header.pseqnum = hio_read16b(f); + header.actplayline = hio_read16b(f); + header.counter = hio_read8(f); + header.extra_songs = hio_read8(f); + + /* + * song structure + */ + D_(D_WARN "load song"); + hio_seek(f, start + song_offset, SEEK_SET); + for (i = 0; i < 63; i++) { + song.sample[i].rep = hio_read16b(f); + song.sample[i].replen = hio_read16b(f); + song.sample[i].midich = hio_read8(f); + song.sample[i].midipreset = hio_read8(f); + song.sample[i].svol = hio_read8(f); + song.sample[i].strans = hio_read8s(f); + } + song.numblocks = hio_read16b(f); + song.songlen = hio_read16b(f); + D_(D_INFO "song.songlen = %d", song.songlen); + seqtable_offset = hio_read32b(f); + hio_read32b(f); + trackvols_offset = hio_read32b(f); + song.numtracks = hio_read16b(f); + song.numpseqs = hio_read16b(f); + trackpans_offset = hio_read32b(f); + song.flags3 = hio_read32b(f); + song.voladj = hio_read16b(f); + song.channels = hio_read16b(f); + song.mix_echotype = hio_read8(f); + song.mix_echodepth = hio_read8(f); + song.mix_echolen = hio_read16b(f); + song.mix_stereosep = hio_read8(f); + + hio_seek(f, 223, SEEK_CUR); + + song.deftempo = hio_read16b(f); + song.playtransp = hio_read8(f); + song.flags = hio_read8(f); + song.flags2 = hio_read8(f); + song.tempo2 = hio_read8(f); + for (i = 0; i < 16; i++) + hio_read8(f); /* reserved */ + song.mastervol = hio_read8(f); + song.numsamples = hio_read8(f); + + /* Sanity check */ + if (song.numsamples > 63) { + D_(D_CRIT "invalid instrument count %d", song.numsamples); + return -1; + } + + /* + * read sequence + */ + hio_seek(f, start + seqtable_offset, SEEK_SET); + playseq_offset = hio_read32b(f); + hio_seek(f, start + playseq_offset, SEEK_SET); + hio_seek(f, 32, SEEK_CUR); /* skip name */ + hio_read32b(f); + hio_read32b(f); + mod->len = hio_read16b(f); + + /* Sanity check */ + if (mod->len > 255) { + D_(D_CRIT "unsupported song length %d", mod->len); + return -1; + } + + for (i = 0; i < mod->len; i++) { + mod->xxo[i] = hio_read16b(f); + } + + /* + * convert header + */ + m->c4rate = C4_NTSC_RATE; + m->quirk |= song.flags & FLAG_STSLIDE ? 0 : QUIRK_VSALL | QUIRK_PBALL; + hexvol = song.flags & FLAG_VOLHEX; + med_8ch = song.flags & FLAG_8CHANNEL; + bpm_on = song.flags2 & FLAG2_BPM; + bpmlen = 1 + (song.flags2 & FLAG2_BMASK); + m->time_factor = MED_TIME_FACTOR; + + mmd_set_bpm(m, med_8ch, song.deftempo, bpm_on, bpmlen); + + mod->spd = song.tempo2; + mod->pat = song.numblocks; + mod->ins = song.numsamples; + mod->rst = 0; + mod->chn = 0; + mod->name[0] = 0; + + /* + * Read smplarr + */ + D_(D_WARN "read smplarr"); + smplarr = (uint32 *) malloc(mod->ins * sizeof(uint32)); + if (smplarr == NULL) { + return -1; + } + if (hio_seek(f, start + smplarr_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at smplarr"); + goto err_cleanup; + } + for (i = 0; i < mod->ins; i++) { + smplarr[i] = hio_read32b(f); + if (hio_eof(f)) { + D_(D_CRIT "read error at smplarr pos %d", i); + goto err_cleanup; + } + } + + /* + * Obtain number of samples from each instrument + */ + mod->smp = 0; + for (i = 0; i < mod->ins; i++) { + int16 type; + if (smplarr[i] == 0) + continue; + hio_seek(f, start + smplarr[i], SEEK_SET); + hio_read32b(f); /* length */ + type = hio_read16b(f); + if (type == -1 || type == -2) { /* type is synth? */ + hio_seek(f, 14, SEEK_CUR); + mod->smp += hio_read16b(f); /* wforms */ + } else if (type >= 1 && type <= 6) { /* octave samples */ + mod->smp += mmd_num_oct[type - 1]; + } else { + mod->smp++; + } + if (hio_error(f)) { + D_(D_CRIT "read error at sample %d", i); + goto err_cleanup; + } + } + + /* + * expdata + */ + D_(D_WARN "load expdata"); + expdata.s_ext_entries = 0; + expdata.s_ext_entrsz = 0; + expdata.i_ext_entries = 0; + expdata.i_ext_entrsz = 0; + expsmp_offset = 0; + iinfo_offset = 0; + + if (expdata_offset) { + if (hio_seek(f, start + expdata_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at expdata"); + goto err_cleanup; + } + hio_read32b(f); + expsmp_offset = hio_read32b(f); + D_(D_INFO "expsmp_offset = 0x%08x", expsmp_offset); + expdata.s_ext_entries = hio_read16b(f); + expdata.s_ext_entrsz = hio_read16b(f); + hio_read32b(f); /* annotxt */ + hio_read32b(f); /* annolen */ + iinfo_offset = hio_read32b(f); + D_(D_INFO "iinfo_offset = 0x%08x", iinfo_offset); + expdata.i_ext_entries = hio_read16b(f); + expdata.i_ext_entrsz = hio_read16b(f); + + /* Sanity check */ + if (expsmp_offset < 0 || iinfo_offset < 0) { + D_(D_CRIT "invalid expdata"); + goto err_cleanup; + } + + hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + songname_offset = hio_read32b(f); + D_(D_INFO "songname_offset = 0x%08x", songname_offset); + expdata.songnamelen = hio_read32b(f); + hio_read32b(f); /* dumps */ + mmdinfo_offset = hio_read32b(f); + + if (hio_error(f)) { + D_(D_CRIT "read error in expdata"); + goto err_cleanup; + } + + hio_seek(f, start + songname_offset, SEEK_SET); + D_(D_INFO "expdata.songnamelen = %d", expdata.songnamelen); + for (i = 0; i < expdata.songnamelen; i++) { + if (i >= XMP_NAME_SIZE) + break; + mod->name[i] = hio_read8(f); + } + + if (mmdinfo_offset != 0) { + D_(D_INFO "mmdinfo_offset = 0x%08x", mmdinfo_offset); + hio_seek(f, start + mmdinfo_offset, SEEK_SET); + mmd_info_text(f, m, mmdinfo_offset); + } + } + + /* + * Read blockarr. + */ + D_(D_WARN "read blockarr"); + blockarr = (uint32 *) malloc(mod->pat * sizeof(uint32)); + if (blockarr == NULL) { + goto err_cleanup; + } + if (hio_seek(f, start + blockarr_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at blockarr"); + goto err_cleanup; + } + for (i = 0; i < mod->pat; i++) { + blockarr[i] = hio_read32b(f); + if (hio_error(f)) { + D_(D_CRIT "read error at blockarr pos %d", i); + goto err_cleanup; + } + } + + /* + * Quickly scan patterns to check the number of channels + */ + D_(D_WARN "find number of channels"); + + max_lines = 1; + for (i = 0; i < mod->pat; i++) { + D_(D_INFO "block %d block_offset = 0x%08x", i, blockarr[i]); + if (blockarr[i] == 0) + continue; + + hio_seek(f, start + blockarr[i], SEEK_SET); + + block.numtracks = hio_read16b(f); + block.lines = hio_read16b(f); + if (hio_error(f)) { + D_(D_CRIT "read error at block %d", i); + goto err_cleanup; + } + + /* Sanity check--Amiga OctaMED files have an upper bound of 3200 lines per block, + * but MED Soundstudio for Windows allows up to 9999 lines. + */ + if (block.lines + 1 > 9999) { + D_(D_CRIT "invalid line count %d in block %d", block.lines + 1, i); + goto err_cleanup; + } + + if (block.numtracks > mod->chn) { + mod->chn = block.numtracks; + } + if (block.lines + 1 > max_lines) { + max_lines = block.lines + 1; + } + } + + /* Sanity check */ + if (mod->chn <= 0 || mod->chn > XMP_MAX_CHANNELS) { + D_(D_CRIT "invalid channel count %d", mod->chn); + goto err_cleanup; + } + + mod->trk = mod->pat * mod->chn; + + if (ver == 2) + libxmp_set_type(m, "OctaMED v5 MMD2"); + else + libxmp_set_type(m, "OctaMED Soundstudio MMD%c", '0' + ver); + + MODULE_INFO(); + + D_(D_INFO "BPM mode: %s (length = %d)", bpm_on ? "on" : "off", bpmlen); + D_(D_INFO "Song transpose : %d", song.playtransp); + D_(D_INFO "Stored patterns: %d", mod->pat); + + /* + * Read and convert patterns + */ + D_(D_WARN "read patterns"); + + if (libxmp_init_pattern(mod) < 0) + goto err_cleanup; + + if ((patbuf = (uint8 *)malloc(mod->chn * max_lines * 4)) == NULL) { + goto err_cleanup; + } + + for (i = 0; i < mod->pat; i++) { + uint8 *pos; + size_t size; + + if (blockarr[i] == 0) + continue; + + hio_seek(f, start + blockarr[i], SEEK_SET); + + block.numtracks = hio_read16b(f); + block.lines = hio_read16b(f); + hio_read32b(f); /* FIXME: should try to load extra command pages when they exist. */ + + size = block.numtracks * (block.lines + 1) * 4; + if (hio_read(patbuf, 1, size, f) < size) { + D_(D_CRIT "read error in block %d", i); + goto err_cleanup; + } + + if (libxmp_alloc_pattern_tracks_long(mod, i, block.lines + 1) < 0) + goto err_cleanup; + + pos = patbuf; + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < block.numtracks; k++) { + event = &EVENT(i, k, j); + event->note = pos[0] & 0x7f; + if (event->note) { + event->note += 12 + song.playtransp; + } + + if (event->note >= XMP_MAX_KEYS) + event->note = 0; + + event->ins = pos[1] & 0x3f; + + /* Decay */ + if (event->ins && !event->note) { + event->f2t = FX_MED_HOLD; + } + + event->fxt = pos[2]; + event->fxp = pos[3]; + mmd_xlat_fx(event, bpm_on, bpmlen, + med_8ch, hexvol); + pos += 4; + } + } + } + free(patbuf); + patbuf = NULL; + + if (libxmp_med_new_module_extras(m) != 0) + goto err_cleanup; + + /* + * Read and convert instruments and samples + */ + D_(D_WARN "read instruments"); + + if (libxmp_init_instrument(m) < 0) + goto err_cleanup; + + D_(D_INFO "Instruments: %d", mod->ins); + + /* Instrument extras */ + exp_smp = (struct InstrExt *) calloc(mod->ins, sizeof(struct InstrExt)); + if (exp_smp == NULL) { + goto err_cleanup; + } + + if (expsmp_offset) { + if (hio_seek(f, start + expsmp_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at expsmp"); + goto err_cleanup; + } + + for (i = 0; i < mod->ins && i < expdata.s_ext_entries; i++) { + int skip = expdata.s_ext_entrsz - 4; + + D_(D_INFO "sample %d expsmp_offset = 0x%08lx", i, hio_tell(f)); + + exp_smp[i].hold = hio_read8(f); + exp_smp[i].decay = hio_read8(f); + exp_smp[i].suppress_midi_off = hio_read8(f); + exp_smp[i].finetune = hio_read8(f); + + if (expdata.s_ext_entrsz >= 8) { /* Octamed V5 */ + exp_smp[i].default_pitch = hio_read8(f); + exp_smp[i].instr_flags = hio_read8(f); + hio_read16b(f); + skip -= 4; + } + if (expdata.s_ext_entrsz >= 10) { /* OctaMED V5.02 */ + hio_read16b(f); + skip -= 2; + } + if (expdata.s_ext_entrsz >= 18) { /* OctaMED V7 */ + exp_smp[i].long_repeat = hio_read32b(f); + exp_smp[i].long_replen = hio_read32b(f); + skip -= 8; + } + + if (hio_error(f)) { + D_(D_CRIT "read error at expsmp"); + goto err_cleanup; + } + + if (skip && hio_seek(f, skip, SEEK_CUR) != 0) { + D_(D_CRIT "seek error at expsmp"); + goto err_cleanup; + } + } + } + + /* Instrument names */ + if (iinfo_offset) { + uint8 name[40]; + + if (hio_seek(f, start + iinfo_offset, SEEK_SET) != 0) { + D_(D_CRIT "seek error at iinfo"); + goto err_cleanup; + } + + for (i = 0; i < mod->ins && i < expdata.i_ext_entries; i++) { + int skip = expdata.i_ext_entrsz - 40; + + D_(D_INFO "sample %d iinfo_offset = 0x%08lx", i, hio_tell(f)); + + if (hio_read(name, 40, 1, f) < 1) { + D_(D_CRIT "read error at iinfo %d", i); + goto err_cleanup; + } + libxmp_instrument_name(mod, i, name, 40); + + if (skip && hio_seek(f, skip, SEEK_CUR) != 0) { + D_(D_CRIT "seek error at iinfo %d", i); + goto err_cleanup; + } + } + } + + /* Sample data */ + for (smp_idx = i = 0; i < mod->ins; i++) { + D_(D_INFO "sample %d smpl_offset = 0x%08x", i, smplarr[i]); + if (smplarr[i] == 0) { + continue; + } + + if (hio_seek(f, start + smplarr[i], SEEK_SET) < 0) { + D_(D_CRIT "seek error at instrument %d", i); + goto err_cleanup; + } + + smp_idx = mmd_load_instrument(f, m, i, smp_idx, &expdata, + &exp_smp[i], &song.sample[i], ver); + + if (smp_idx < 0) { + goto err_cleanup; + } + } + + hio_seek(f, start + trackvols_offset, SEEK_SET); + for (i = 0; i < mod->chn; i++) + mod->xxc[i].vol = hio_read8(f);; + + if (trackpans_offset) { + hio_seek(f, start + trackpans_offset, SEEK_SET); + for (i = 0; i < mod->chn; i++) { + int p = 8 * hio_read8s(f); + mod->xxc[i].pan = 0x80 + (p > 127 ? 127 : p); + } + } else { + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = 0x80; + } + + m->read_event_type = READ_EVENT_MED; + retval = 0; + + err_cleanup: + free(exp_smp); + free(blockarr); + free(smplarr); + free(patbuf); + + return retval; +} diff --git a/thirdparty/libxmp/src/loaders/mmd_common.c b/thirdparty/libxmp/src/loaders/mmd_common.c new file mode 100644 index 000000000..2a019727a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mmd_common.c @@ -0,0 +1,1028 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Common functions for MMD0/1 and MMD2/3 loaders + * Tempo fixed by Francis Russell + */ + +#include "med.h" +#include "loader.h" +#include "../med_extras.h" + +#ifdef DEBUG +const char *const mmd_inst_type[] = { + "HYB", /* -2 */ + "SYN", /* -1 */ + "SMP", /* 0 */ + "I5O", /* 1 */ + "I3O", /* 2 */ + "I2O", /* 3 */ + "I4O", /* 4 */ + "I6O", /* 5 */ + "I7O", /* 6 */ + "EXT", /* 7 */ +}; +#endif + +static int mmd_convert_tempo(int tempo, int bpm_on, int med_8ch) +{ + const int tempos_compat[10] = { + 195, 97, 65, 49, 39, 32, 28, 24, 22, 20 + }; + const int tempos_8ch[10] = { + 179, 164, 152, 141, 131, 123, 116, 110, 104, 99 + }; + + if (tempo > 0) { + /* From the OctaMEDv4 documentation: + * + * In 8-channel mode, you can control the playing speed more + * accurately (to techies: by changing the size of the mix buffer). + * This can be done with the left tempo gadget (values 1-10; the + * lower, the faster). Values 11-240 are equivalent to 10. + * + * NOTE: the tempos used for this are directly from OctaMED + * Soundstudio 2, but in older versions the playback speeds + * differed slightly between NTSC and PAL. This table seems to + * have been intended to be a compromise between the two. + */ + if (med_8ch) { + tempo = tempo > 10 ? 10 : tempo; + return tempos_8ch[tempo-1]; + } + /* Tempos 1-10 in tempo mode are compatibility tempos that + * approximate Soundtracker speeds. + */ + if (tempo <= 10 && !bpm_on) { + return tempos_compat[tempo-1]; + } + } + return tempo; +} + +void mmd_xlat_fx(struct xmp_event *event, int bpm_on, int bpmlen, int med_8ch, + int hexvol) +{ + switch (event->fxt) { + case 0x00: + /* ARPEGGIO 00 + * Changes the pitch six times between three different + * pitches during the duration of the note. It can create a + * chord sound or other special effect. Arpeggio works better + * with some instruments than others. + */ + break; + case 0x01: + /* SLIDE UP 01 + * This slides the pitch of the current track up. It decreases + * the period of the note by the amount of the argument on each + * timing pulse. OctaMED-Pro can create slides automatically, + * but you may want to use this function for special effects. + * Note: a param of 0 does nothing and should be ignored. + */ + if (!event->fxp) + event->fxt = 0; + break; + case 0x02: + /* SLIDE DOWN 02 + * The same as SLIDE UP, but it slides down. + * Note: a param of 0 does nothing and should be ignored. + */ + if (!event->fxp) + event->fxt = 0; + break; + case 0x03: + /* PORTAMENTO 03 + * Makes precise sliding easy. + */ + break; + case 0x04: + /* VIBRATO 04 + * The left half of the argument is the vibrato speed, the + * right half is the depth. If the numbers are zeros, the + * previous speed and depth are used. + */ + /* Note: this is twice as deep as the Protracker vibrato */ + event->fxt = FX_VIBRATO2; + break; + case 0x05: + /* SLIDE + FADE 05 + * ProTracker compatible. This command is the combination of + * commands 0300 and 0Dxx. The argument is the fade speed. + * The slide will continue during this command. + */ + /* fall-through */ + case 0x06: + /* VIBRATO + FADE 06 + * ProTracker compatible. Combines commands 0400 and 0Dxx. + * The argument is the fade speed. The vibrato will continue + * during this command. + */ + /* fall-through */ + case 0x07: + /* TREMOLO 07 + * ProTracker compatible. + * This command is a kind of "volume vibrato". The left + * number is the speed of the tremolo, and the right one is + * the depth. The depth must be quite high before the effect + * is audible. + */ + break; + case 0x08: + /* HOLD and DECAY 08 + * This command must be on the same line as the note. The + * left half of the argument determines the decay and the + * right half the hold. + */ + event->fxt = event->fxp = 0; + break; + case 0x09: + /* SECONDARY TEMPO 09 + * This sets the secondary tempo (the number of timing + * pulses per note). The argument must be from 01 to 20 (hex). + */ + if (event->fxp >= 0x01 && event->fxp <= 0x20) { + event->fxt = FX_SPEED; + } else { + event->fxt = event->fxp = 0; + } + break; + case 0x0a: + /* 0A not mentioned but it's Protracker-compatible */ + /* fall-through */ + case 0x0b: + /* POSITION JUMP 0B + * The song plays up to this command and then jumps to + * another position in the play sequence. The song then + * loops from the point jumped to, to the end of the song + * forever. The purpose is to allow for introductions that + * play only once. + */ + /* fall-through */ + case 0x0c: + /* SET VOLUME 0C + * Overrides the default volume of an instrument. + */ + if (!hexvol) { + int p = event->fxp; + event->fxp = (p >> 8) * 10 + (p & 0xff); + } + break; + case 0x0d: + /* VOLUME SLIDE 0D + * Smoothly slides the volume up or down. The left half of + * the argument increases the volume. The right decreases it. + */ + event->fxt = FX_VOLSLIDE; + break; + case 0x0e: + /* SYNTH JUMP 0E + * When used with synthetic or hybrid instruments, it + * triggers a jump in the Waveform Sequence List. The argument + * is the jump destination (line no). + */ + event->fxt = event->fxp = 0; + break; + case 0x0f: + /* MISCELLANEOUS 0F + * The effect depends upon the value of the argument. + */ + if (event->fxp == 0x00) { /* Jump to next block */ + event->fxt = FX_BREAK; + break; + } else if (event->fxp <= 0xf0) { + event->fxt = FX_S3M_BPM; + event->fxp = mmd_convert_tempo(event->fxp, bpm_on, med_8ch); + break; + } else switch (event->fxp) { + case 0xf1: /* Play note twice */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | 3; + break; + case 0xf2: /* Delay note */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_DELAY << 4) | 3; + break; + case 0xf3: /* Play note three times */ + event->fxt = FX_EXTENDED; + event->fxp = (EX_RETRIG << 4) | 2; + break; + case 0xf8: /* Turn filter off */ + case 0xf9: /* Turn filter on */ + case 0xfa: /* MIDI pedal on */ + case 0xfb: /* MIDI pedal off */ + case 0xfd: /* Set pitch */ + case 0xfe: /* End of song */ + event->fxt = event->fxp = 0; + break; + case 0xff: /* Turn note off */ + event->fxt = event->fxp = 0; + event->note = XMP_KEY_CUT; + break; + default: + event->fxt = event->fxp = 0; + } + break; + case 0x11: + /* SLIDE PITCH UP (only once) 11 + * Equivalent to ProTracker command E1x. + * Lets you control the pitch with great accuracy. This + * command changes only this occurrence of the note. + * Note: a param of 0 does nothing and should be ignored. + */ + event->fxt = FX_F_PORTA_UP; + break; + case 0x12: + /* SLIDE DOWN (only once) 12 + * Equivalent to ProTracker command E2x. + * Note: a param of 0 does nothing and should be ignored. + */ + event->fxt = FX_F_PORTA_DN; + break; + case 0x14: + /* VIBRATO 14 + * ProTracker compatible. This is similar to command 04 + * except the depth is halved, to give greater accuracy. + */ + event->fxt = FX_VIBRATO; + break; + case 0x15: + /* SET FINETUNE 15 + * Set a finetune value for a note, overrides the default + * fine tune value of the instrument. Negative numbers must + * be entered as follows: + * -1 => FF -3 => FD -5 => FB -7 => F9 + * -2 => FE -4 => FC -6 => FA -8 => F8 + */ + event->fxt = FX_FINETUNE; + event->fxp = (event->fxp + 8) << 4; + break; + case 0x16: + /* LOOP 16 + * Creates a loop within a block. 1600 marks the beginning + * of the loop. The next occurrence of the 16 command + * designates the number of loops. Same as ProTracker E6x. + */ + event->fxt = FX_EXTENDED; + if (event->fxp > 0x0f) + event->fxp = 0x0f; + event->fxp |= 0x60; + break; + case 0x18: + /* STOP NOTE 18 + * Cuts the note by zeroing the volume at the pulse specified + * in the argument value. This is the same as ProTracker + * command ECx. + */ + event->fxt = FX_EXTENDED; + if (event->fxp > 0x0f) + event->fxp = 0x0f; + event->fxp |= 0xc0; + break; + case 0x19: + /* SET SAMPLE START OFFSET + * Same as ProTracker command 9. + * When playing a sample, this command sets the starting + * offset (at steps of $100 = 256 bytes). Useful for speech + * samples. + */ + event->fxt = FX_OFFSET; + break; + case 0x1a: + /* SLIDE VOLUME UP ONCE + * Only once ProTracker command EAx. Lets volume slide + * slowly once per line. + * Note: a param of 0 does nothing and should be ignored. + */ + event->fxt = event->fxp ? FX_F_VSLIDE_UP : 0; + break; + case 0x1b: + /* SLIDE VOLUME DOWN ONCE + * Only once ProTracker command EBx. + * Note: a param of 0 does nothing and should be ignored. + */ + event->fxt = event->fxp ? FX_F_VSLIDE_DN : 0; + break; + case 0x1d: + /* JUMP TO NEXT BLOCK 1D + * Jumps to the next line in the PLAY SEQUENCE LIST at the + * specified line. ProTracker command D. This command is + * like F00, except that you can specify the line number of + * the first line to be played. The line number must be + * specified in HEX. + */ + event->fxt = FX_BREAK; + break; + case 0x1e: + /* PLAY LINE x TIMES 1E + * Plays only commands, notes not replayed. ProTracker + * pattern delay. + */ + event->fxt = FX_PATT_DELAY; + break; + case 0x1f: + /* Command 1F: NOTE DELAY AND RETRIGGER + * (Protracker commands EC and ED) + * Gives you accurate control over note playing. You can + * delay the note any number of ticks, and initiate fast + * retrigger. Level 1 = note delay value, level 2 = retrigger + * value. + */ + if (MSN(event->fxp)) { + /* delay */ + event->fxt = FX_EXTENDED; + event->fxp = 0xd0 | (event->fxp >> 4); + } else if (LSN(event->fxp)) { + /* retrig */ + event->fxt = FX_EXTENDED; + event->fxp = 0x90 | (event->fxp & 0x0f); + } + break; + case 0x20: + /* Command 20: REVERSE SAMPLE / RELATIVE SAMPLE OFFSET + * With command level $00, the sample is reversed. With other + * levels, it changes the sample offset, just like command 19, + * except the command level is the new offset relative to the + * current sample position being played. + * Note: 20 00 only works on the same line as a new note. + */ + if (event->fxp == 0 && event->note != 0) { + event->fxt = FX_REVERSE; + event->fxp = 1; + } else { + event->fxt = event->fxp = 0; + } + break; + case 0x2e: + /* Command 2E: SET TRACK PANNING + * Allows track panning to be changed during play. The track + * on which the player command appears is the track affected. + * The command level is in signed hex: $F0 to $10 = -16 to 16 + * decimal. + */ + if (event->fxp >= 0xf0 || event->fxp <= 0x10) { + int fxp = (signed char)event->fxp + 16; + fxp <<= 3; + if (fxp == 0x100) + fxp--; + event->fxt = FX_SETPAN; + event->fxp = fxp; + } + break; + default: + event->fxt = event->fxp = 0; + break; + } +} + + +struct mmd_instrument_info +{ + uint32 length; + uint32 rep; + uint32 replen; + int sampletrans; + int synthtrans; + int flg; + int enable; +}; + +/* Interpret loop/flag parameters for sampled instruments (sample, hybrid, IFF). + * This is common code to avoid replicating this mess in each loader. */ +static void mmd_load_instrument_common( + struct mmd_instrument_info *info, struct InstrHdr *instr, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + info->enable = 1; + info->flg = 0; + if (ver >= 2 && expdata->s_ext_entrsz >= 8) { /* MMD2+ instrument flags */ + uint8 instr_flags = exp_smp->instr_flags; + + if (instr_flags & SSFLG_LOOP) { + info->flg |= XMP_SAMPLE_LOOP; + } + if (instr_flags & SSFLG_PINGPONG) { + info->flg |= XMP_SAMPLE_LOOP_BIDIR; + } + if (instr_flags & SSFLG_DISABLED) { + info->enable = 0; + } + } else { + if (sample->replen > 1) { + info->flg |= XMP_SAMPLE_LOOP; + } + } + + info->sampletrans = 36 + sample->strans; + info->synthtrans = 12 + sample->strans; + + if (instr) { + int sample_type = instr->type & ~(S_16|MD16|STEREO); + + if ((ver >= 3 && sample_type == 0) || sample_type == 7) { + /* Mix mode transposes samples down two octaves. + * This does not apply to octave samples or synths. + * ExtSamples (7) are transposed regardless. */ + info->sampletrans -= 24; + } + + info->length = instr->length; + + if (ver >= 2 && expdata->s_ext_entrsz >= 18) { /* MMD2+ long repeat */ + info->rep = exp_smp->long_repeat; + info->replen = exp_smp->long_replen; + } else { + info->rep = sample->rep << 1; + info->replen = sample->replen << 1; + } + + if (instr->type & S_16) { + info->flg |= XMP_SAMPLE_16BIT; + info->length >>= 1; + info->rep >>= 1; + info->replen >>= 1; + } + + /* STEREO means that this is a stereo sample. The sample + * is not interleaved. The left channel comes first, + * followed by the right channel. Important: Length + * specifies the size of one channel only! The actual memory + * usage for both samples is length * 2 bytes. + */ + if (instr->type & STEREO) { + D_(D_WARN "stereo sample unsupported"); + /* TODO: implement stereo sample support. + info.flg |= XMP_SAMPLE_STEREO; + */ + } + } +} + +/* Compatibility for MED Soundstudio v2 default pitch events. + * For single-octave samples and synthetics, MED mix mode note 0x01 + * plays the note number stored in the InstrExt default pitch field. + * Mix mode events are currently transposed up an octave and are offset + * down by 1 for the instrument map, hence index 12. + * + * See med.h for more info. + */ +static void mmd_set_default_pitch_note(struct xmp_instrument *xxi, + struct InstrExt *exp_smp, int ver) +{ + if (ver >= 3) { + int note = MMD3_DEFAULT_NOTE; + + if (exp_smp->default_pitch) + note = exp_smp->default_pitch - 1; + + if (note >= 0 && note < XMP_MAX_KEYS) + xxi->map[12].xpo = note; + } +} + +int mmd_alloc_tables(struct module_data *m, int i, struct SynthInstr *synth) +{ + struct med_module_extras *me = (struct med_module_extras *)m->extra; + + me->vol_table[i] = (uint8 *) calloc(1, synth->voltbllen); + if (me->vol_table[i] == NULL) + goto err; + memcpy(me->vol_table[i], synth->voltbl, synth->voltbllen); + + me->wav_table[i] = (uint8 *) calloc(1, synth->wftbllen); + if (me->wav_table[i] == NULL) + goto err1; + memcpy(me->wav_table[i], synth->wftbl, synth->wftbllen); + + return 0; + + err1: + free(me->vol_table[i]); + err: + return -1; +} + +static int mmd_load_hybrid_instrument(HIO_HANDLE *f, struct module_data *m, int i, + int smp_idx, struct SynthInstr *synth, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + struct med_instrument_extras *ie; + struct mmd_instrument_info info; + struct InstrHdr instr; + int pos = hio_tell(f); + int j; + + /* Sanity check */ + if (smp_idx >= mod->smp) { + return -1; + } + + synth->defaultdecay = hio_read8(f); + hio_seek(f, 3, SEEK_CUR); + synth->rep = hio_read16b(f); + synth->replen = hio_read16b(f); + synth->voltbllen = hio_read16b(f); + synth->wftbllen = hio_read16b(f); + synth->volspeed = hio_read8(f); + synth->wfspeed = hio_read8(f); + synth->wforms = hio_read16b(f); + hio_read(synth->voltbl, 1, 128, f); + hio_read(synth->wftbl, 1, 128, f); + + /* Sanity check */ + if (synth->voltbllen > 128 || synth->wftbllen > 128 || + synth->wforms < 1 || synth->wforms > 64) { + return -1; + } + + for (j = 0; j < synth->wforms; j++) + synth->wf[j] = hio_read32b(f); + + if (hio_error(f)) + return -1; + + hio_seek(f, pos - 6 + synth->wf[0], SEEK_SET); + instr.length = hio_read32b(f); + instr.type = hio_read16b(f); + + /* Hybrids using IFFOCT/ext samples as their sample don't seem to + * exist. If one is found, this should be fixed. OctaMED SS 1.03 + * converts 16-bit samples to 8-bit when changed to hybrid. */ + if (instr.type != 0) { + D_(D_CRIT "unsupported sample type %d for hybrid", instr.type); + return -1; + } + + if (libxmp_med_new_instrument_extras(xxi) != 0) + return -1; + + xxi->nsm = synth->wforms; + if (libxmp_alloc_subinstrument(mod, i, synth->wforms) < 0) + return -1; + + ie = MED_INSTRUMENT_EXTRAS(*xxi); + ie->vts = synth->volspeed; + ie->wts = synth->wfspeed; + ie->vtlen = synth->voltbllen; + ie->wtlen = synth->wftbllen; + + mmd_load_instrument_common(&info, &instr, expdata, exp_smp, sample, ver); + mmd_set_default_pitch_note(xxi, exp_smp, ver); + sub = &xxi->sub[0]; + + sub->pan = 0x80; + sub->vol = info.enable ? sample->svol : 0; + sub->xpo = info.sampletrans; + sub->sid = smp_idx; + sub->fin = exp_smp->finetune; + + xxs = &mod->xxs[smp_idx]; + + xxs->len = info.length; + xxs->lps = info.rep; + xxs->lpe = info.rep + info.replen; + xxs->flg = info.flg; + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + + smp_idx++; + + for (j = 1; j < synth->wforms; j++) { + sub = &xxi->sub[j]; + xxs = &mod->xxs[smp_idx]; + + /* Sanity check */ + if (j >= xxi->nsm || smp_idx >= mod->smp) + return -1; + + sub->pan = 0x80; + sub->vol = info.enable ? 64 : 0; + sub->xpo = info.synthtrans; + sub->sid = smp_idx; + sub->fin = exp_smp->finetune; + + hio_seek(f, pos - 6 + synth->wf[j], SEEK_SET); + + xxs->len = hio_read16b(f) * 2; + xxs->lps = 0; + xxs->lpe = xxs->len; + xxs->flg = XMP_SAMPLE_LOOP; + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + + smp_idx++; + } + return 0; +} + +static int mmd_load_synth_instrument(HIO_HANDLE *f, struct module_data *m, int i, + int smp_idx, struct SynthInstr *synth, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct med_instrument_extras *ie; + struct mmd_instrument_info info; + int pos = hio_tell(f); + int j; + + mmd_load_instrument_common(&info, NULL, expdata, exp_smp, sample, ver); + mmd_set_default_pitch_note(xxi, exp_smp, ver); + + synth->defaultdecay = hio_read8(f); + hio_seek(f, 3, SEEK_CUR); + synth->rep = hio_read16b(f); + synth->replen = hio_read16b(f); + synth->voltbllen = hio_read16b(f); + synth->wftbllen = hio_read16b(f); + synth->volspeed = hio_read8(f); + synth->wfspeed = hio_read8(f); + synth->wforms = hio_read16b(f); + hio_read(synth->voltbl, 1, 128, f); + hio_read(synth->wftbl, 1, 128, f); + + if (synth->wforms == 0xffff) { + xxi->nsm = 0; + return 1; + } + if (synth->voltbllen > 128 || + synth->wftbllen > 128 || + synth->wforms > 64) { + return -1; + } + + for (j = 0; j < synth->wforms; j++) + synth->wf[j] = hio_read32b(f); + + if (hio_error(f)) + return -1; + + D_(D_INFO " VS:%02x WS:%02x WF:%02x %02x %+3d %+1d", + synth->volspeed, synth->wfspeed, + synth->wforms & 0xff, + sample->svol, + sample->strans, + exp_smp->finetune); + + if (libxmp_med_new_instrument_extras(&mod->xxi[i]) != 0) + return -1; + + xxi->nsm = synth->wforms; + if (libxmp_alloc_subinstrument(mod, i, synth->wforms) < 0) + return -1; + + ie = MED_INSTRUMENT_EXTRAS(*xxi); + ie->vts = synth->volspeed; + ie->wts = synth->wfspeed; + ie->vtlen = synth->voltbllen; + ie->wtlen = synth->wftbllen; + + for (j = 0; j < synth->wforms; j++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs = &mod->xxs[smp_idx]; + + /* Sanity check */ + if (j >= xxi->nsm || smp_idx >= mod->smp) + return -1; + + sub->pan = 0x80; + sub->vol = info.enable ? 64 : 0; + sub->xpo = info.synthtrans; + sub->sid = smp_idx; + sub->fin = exp_smp->finetune; + + hio_seek(f, pos - 6 + synth->wf[j], SEEK_SET); + + xxs->len = hio_read16b(f) * 2; + xxs->lps = 0; + xxs->lpe = xxs->len; + xxs->flg = XMP_SAMPLE_LOOP; + + if (libxmp_load_sample(m, f, 0, xxs, NULL) < 0) + return -1; + + smp_idx++; + } + + return 0; +} + +static int mmd_load_sampled_instrument(HIO_HANDLE *f, struct module_data *m, int i, + int smp_idx, struct InstrHdr *instr, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + struct mmd_instrument_info info; + int j, k; + + /* Sanity check */ + if (smp_idx >= mod->smp) + return -1; + + /* hold & decay support */ + if (libxmp_med_new_instrument_extras(xxi) != 0) + return -1; + MED_INSTRUMENT_EXTRAS(*xxi)->hold = exp_smp->hold; + xxi->rls = 0xfff - (exp_smp->decay << 4); + + xxi->nsm = 1; + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mmd_load_instrument_common(&info, instr, expdata, exp_smp, sample, ver); + mmd_set_default_pitch_note(xxi, exp_smp, ver); + sub = &xxi->sub[0]; + + sub->vol = info.enable ? sample->svol : 0; + sub->pan = 0x80; + sub->xpo = info.sampletrans; + sub->sid = smp_idx; + sub->fin = exp_smp->finetune << 4; + + xxs = &mod->xxs[smp_idx]; + + xxs->len = info.length; + xxs->lps = info.rep; + xxs->lpe = info.rep + info.replen; + xxs->flg = info.flg; + + /* Restrict sampled instruments to 3 octave range except for MMD3. + * Checked in MMD0 with med.egypian/med.medieval from Lemmings 2 + * and MED.ParasolStars, MMD1 with med.Lemmings2 + */ + + if (ver < 3) { + /* ExtSamples have two extra octaves. */ + int octaves = (instr->type & 7) == 7 ? 5 : 3; + for (j = 0; j < 9; j++) { + for (k = 0; k < 12; k++) { + int xpo = 0; + + if (j < 1) + xpo = 12 * (1 - j); + else if (j > octaves) + xpo = -12 * (j - octaves); + + xxi->map[12 * j + k].xpo = xpo; + } + } + } + + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_BIGEND, xxs, NULL) < 0) { + return -1; + } + + return 0; +} + +static const char iffoct_insmap[6][9] = { + /* 2 */ { 1, 1, 1, 0, 0, 0, 0, 0, 0 }, + /* 3 */ { 2, 2, 2, 2, 2, 2, 1, 1, 0 }, + /* 4 */ { 3, 3, 3, 2, 2, 2, 1, 1, 0 }, + /* 5 */ { 4, 4, 4, 3, 2, 2, 1, 1, 0 }, + /* 6 */ { 5, 5, 5, 5, 4, 3, 2, 1, 0 }, + /* 7 */ { 6, 6, 6, 6, 5, 4, 3, 2, 1 } +}; + +static const char iffoct_xpomap[6][9] = { + /* 2 */ { 12, 12, 12, 0, 0, 0, 0, 0, 0 }, + /* 3 */ { 12, 12, 12, 12, 12, 12, 0, 0,-12 }, + /* 4 */ { 12, 12, 12, 0, 0, 0,-12,-12,-24 }, + /* 5 */ { 24, 24, 24, 12, 0, 0,-12,-24,-36 }, + /* 6 */ { 12, 12, 12, 12, 0,-12,-24,-36,-48 }, + /* 7 */ { 12, 12, 12, 12, 0,-12,-24,-36,-48 }, +}; + +static int mmd_load_iffoct_instrument(HIO_HANDLE *f, struct module_data *m, int i, + int smp_idx, struct InstrHdr *instr, int num_oct, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + struct mmd_instrument_info info; + int size, j, k; + + if (num_oct < 2 || num_oct > 7) + return -1; + + /* Sanity check */ + if (smp_idx + num_oct > mod->smp) + return -1; + + /* Sanity check - ignore absurdly large IFFOCT instruments. */ + if ((int)instr->length < 0) + return -1; + + /* hold & decay support */ + if (libxmp_med_new_instrument_extras(xxi) != 0) + return -1; + + MED_INSTRUMENT_EXTRAS(*xxi)->hold = exp_smp->hold; + xxi->rls = 0xfff - (exp_smp->decay << 4); + + xxi->nsm = num_oct; + if (libxmp_alloc_subinstrument(mod, i, num_oct) < 0) + return -1; + + /* base octave size */ + size = instr->length / ((1 << num_oct) - 1); + mmd_load_instrument_common(&info, instr, expdata, exp_smp, sample, ver); + + for (j = 0; j < num_oct; j++) { + sub = &xxi->sub[j]; + + sub->vol = info.enable ? sample->svol : 0; + sub->pan = 0x80; + sub->xpo = info.sampletrans - 12; + sub->sid = smp_idx; + sub->fin = exp_smp->finetune << 4; + + xxs = &mod->xxs[smp_idx]; + + xxs->len = size; + xxs->lps = info.rep; + xxs->lpe = info.rep + info.replen; + xxs->flg = info.flg; + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_BIGEND, xxs, NULL) < 0) { + return -1; + } + + smp_idx++; + size <<= 1; + info.rep <<= 1; + info.replen <<= 1; + } + + /* instrument mapping */ + + for (j = 0; j < 9; j++) { + for (k = 0; k < 12; k++) { + xxi->map[12 * j + k].ins = iffoct_insmap[num_oct - 2][j]; + xxi->map[12 * j + k].xpo = iffoct_xpomap[num_oct - 2][j]; + } + } + + return 0; +} + +/* Number of octaves in IFFOCT samples */ +const int mmd_num_oct[6] = { 5, 3, 2, 4, 6, 7 }; + +int mmd_load_instrument(HIO_HANDLE *f, struct module_data *m, int i, int smp_idx, + struct MMD0exp *expdata, struct InstrExt *exp_smp, + struct MMD0sample *sample, int ver) +{ + struct InstrHdr instr; + struct SynthInstr synth; + int sample_type; + + instr.length = hio_read32b(f); + instr.type = (int16)hio_read16b(f); + sample_type = instr.type & ~(S_16|MD16|STEREO); + + D_(D_INFO "[%2x] %-40.40s %d", i, m->mod.xxi[i].name, instr.type); + + if (instr.type == -2) { /* Hybrid */ + int ret = mmd_load_hybrid_instrument(f, m, i, smp_idx, + &synth, expdata, exp_smp, sample, ver); + + if (ret < 0) { + D_(D_CRIT "error loading hybrid instrument %d", i); + return -1; + } + + smp_idx += synth.wforms; + + if (mmd_alloc_tables(m, i, &synth) != 0) + return -1; + + } else if (instr.type == -1) { /* Synthetic */ + int ret = mmd_load_synth_instrument(f, m, i, smp_idx, + &synth, expdata, exp_smp, sample, ver); + + if (ret > 0) + return smp_idx; + + if (ret < 0) { + D_(D_CRIT "error loading synthetic instrument %d", i); + return -1; + } + + smp_idx += synth.wforms; + + if (mmd_alloc_tables(m, i, &synth) != 0) + return -1; + + } else if (instr.type >= 1 && instr.type <= 6) { /* IFFOCT */ + int ret; + const int oct = mmd_num_oct[instr.type - 1]; + + ret = mmd_load_iffoct_instrument(f, m, i, smp_idx, + &instr, oct, expdata, exp_smp, sample, ver); + + if (ret < 0) { + D_(D_CRIT "error loading IFFOCT instrument %d", i); + return -1; + } + + smp_idx += oct; + + } else if (sample_type == 0 || sample_type == 7) { /* Sample */ + int ret; + + ret = mmd_load_sampled_instrument(f, m, i, smp_idx, + &instr, expdata, exp_smp, sample, ver); + + if (ret < 0) { + D_(D_CRIT "error loading sample %d", i); + return -1; + } + + smp_idx++; + + } else { + /* Invalid instrument type */ + D_(D_CRIT "invalid instrument type: %d", instr.type); + return -1; + } + return smp_idx; +} + + +void mmd_set_bpm(struct module_data *m, int med_8ch, int deftempo, + int bpm_on, int bpmlen) +{ + struct xmp_module *mod = &m->mod; + + mod->bpm = mmd_convert_tempo(deftempo, bpm_on, med_8ch); + + /* 8-channel mode completely overrides regular timing. + * See mmd_convert_tempo for more info. + */ + if (med_8ch) { + m->time_factor = DEFAULT_TIME_FACTOR; + } else if (bpm_on) { + m->time_factor = DEFAULT_TIME_FACTOR * 4 / bpmlen; + } +} + + +void mmd_info_text(HIO_HANDLE *f, struct module_data *m, int offset) +{ + int type, len; + + /* Copy song info text */ + hio_read32b(f); /* skip next */ + hio_read16b(f); /* skip reserved */ + type = hio_read16b(f); + D_(D_INFO "mmdinfo type=%d", type); + if (type == 1) { /* 1 = ASCII */ + len = hio_read32b(f); + D_(D_INFO "mmdinfo length=%d", len); + if (len > 0 && len < hio_size(f)) { + m->comment = (char *) malloc(len + 1); + if (m->comment == NULL) + return; + hio_read(m->comment, 1, len, f); + m->comment[len] = 0; + } + } +} diff --git a/thirdparty/libxmp/src/loaders/mod.h b/thirdparty/libxmp/src/loaders/mod.h new file mode 100644 index 000000000..682aef022 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mod.h @@ -0,0 +1,59 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LIBXMP_LOADERS_MOD_H +#define LIBXMP_LOADERS_MOD_H + +struct mod_instrument { + uint8 name[22]; /* Instrument name */ + uint16 size; /* Sample length in 16-bit words */ + int8 finetune; /* Finetune (signed nibble) */ + int8 volume; /* Linear playback volume */ + uint16 loop_start; /* Loop start in 16-bit words */ + uint16 loop_size; /* Loop length in 16-bit words */ +}; + +struct mod_header { + uint8 name[20]; + struct mod_instrument ins[31]; + uint8 len; + uint8 restart; /* Number of patterns in Soundtracker, + * Restart in Noisetracker/Startrekker, + * 0x7F in Protracker + */ + uint8 order[128]; + uint8 magic[4]; +}; + +#ifndef LIBXMP_CORE_PLAYER +/* Soundtracker 15-instrument module header */ + +struct st_header { + uint8 name[20]; + struct mod_instrument ins[15]; + uint8 len; + uint8 restart; + uint8 order[128]; +}; +#endif + +#endif /* LIBXMP_LOADERS_MOD_H */ diff --git a/thirdparty/libxmp/src/loaders/mod_load.c b/thirdparty/libxmp/src/loaders/mod_load.c new file mode 100644 index 000000000..425604c15 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mod_load.c @@ -0,0 +1,1010 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* This loader recognizes the following variants of the Protracker + * module format: + * + * - Protracker M.K. and M!K! + * - Protracker songs + * - Noisetracker N.T. and M&K! (not tested) + * - Fast Tracker 6CHN and 8CHN + * - Fasttracker II/Take Tracker ?CHN and ??CH + * - Mod's Grave M.K. w/ 8 channels (WOW) + * - Atari Octalyser CD61 and CD81 + * - Digital Tracker FA04, FA06 and FA08 + * - TakeTracker TDZ1, TDZ2, TDZ3, and TDZ4 + * - (unknown) NSMS, LARD + * + * The 'lite' version only recognizes Protracker M.K. and + * Fasttracker ?CHN and ??CH formats. + */ + +#include +#include "loader.h" +#include "mod.h" + +#ifndef LIBXMP_CORE_PLAYER +struct mod_magic { + const char *magic; + int flag; + int id; + int ch; +}; + +#define TRACKER_PROTRACKER 0 +#define TRACKER_NOISETRACKER 1 +#define TRACKER_SOUNDTRACKER 2 +#define TRACKER_FASTTRACKER 3 +#define TRACKER_FASTTRACKER2 4 +#define TRACKER_OCTALYSER 5 +#define TRACKER_TAKETRACKER 6 +#define TRACKER_DIGITALTRACKER 7 +#define TRACKER_FLEXTRAX 8 +#define TRACKER_MODSGRAVE 9 +#define TRACKER_SCREAMTRACKER3 10 +#define TRACKER_OPENMPT 11 +#define TRACKER_UNKNOWN_CONV 95 +#define TRACKER_CONVERTEDST 96 +#define TRACKER_CONVERTED 97 +#define TRACKER_CLONE 98 +#define TRACKER_UNKNOWN 99 + +#define TRACKER_PROBABLY_NOISETRACKER 20 + +const struct mod_magic mod_magic[] = { + {"M.K.", 0, TRACKER_PROTRACKER, 4}, + {"M!K!", 1, TRACKER_PROTRACKER, 4}, + {"M&K!", 1, TRACKER_NOISETRACKER, 4}, + {"N.T.", 1, TRACKER_NOISETRACKER, 4}, + {"6CHN", 0, TRACKER_FASTTRACKER, 6}, + {"8CHN", 0, TRACKER_FASTTRACKER, 8}, + {"CD61", 1, TRACKER_OCTALYSER, 6}, /* Atari STe/Falcon */ + {"CD81", 1, TRACKER_OCTALYSER, 8}, /* Atari STe/Falcon */ + {"TDZ1", 1, TRACKER_TAKETRACKER, 1}, /* TakeTracker 1ch */ + {"TDZ2", 1, TRACKER_TAKETRACKER, 2}, /* TakeTracker 2ch */ + {"TDZ3", 1, TRACKER_TAKETRACKER, 3}, /* TakeTracker 3ch */ + {"TDZ4", 1, TRACKER_TAKETRACKER, 4}, /* see XModule SaveTracker.c */ + {"FA04", 1, TRACKER_DIGITALTRACKER, 4}, /* Atari Falcon */ + {"FA06", 1, TRACKER_DIGITALTRACKER, 6}, /* Atari Falcon */ + {"FA08", 1, TRACKER_DIGITALTRACKER, 8}, /* Atari Falcon */ + {"LARD", 1, TRACKER_UNKNOWN, 4}, /* in judgement_day_gvine.mod */ + {"NSMS", 1, TRACKER_UNKNOWN, 4}, /* in Kingdom.mod */ +}; + +/* Returns non-zero if the given tracker ONLY supports VBlank timing. This + * should be used only when the tracker is known for sure, e.g. magic match. */ +static int tracker_is_vblank(int id) +{ + switch (id) { + case TRACKER_NOISETRACKER: + case TRACKER_SOUNDTRACKER: + return 1; + default: + return 0; + } +} +#endif /* LIBXMP_CORE_PLAYER */ + +static int mod_test(HIO_HANDLE *, char *, const int); +static int mod_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mod = { + #ifdef LIBXMP_CORE_PLAYER + "Protracker", + #else + "Amiga Protracker/Compatible", + #endif + mod_test, + mod_load +}; + +#ifndef LIBXMP_CORE_PLAYER +static int validate_pattern(uint8 *buf) +{ + int i, j; + + for (i = 0; i < 64; i++) { + for (j = 0; j < 4; j++) { + uint8 *d = buf + (i * 4 + j) * 4; + if ((d[0] >> 4) > 1) { + D_(D_CRIT "invalid pattern data: row %d ch %d: %02x", i, j, d[0]); + return -1; + } + } + } + + return 0; +} +#endif + +static int mod_test(HIO_HANDLE * f, char *t, const int start) +{ + int i; + char buf[4]; + #ifndef LIBXMP_CORE_PLAYER + uint8 pat_buf[1024]; + int smp_size, num_pat; + long size; + int count; + int detected; + #endif + + hio_seek(f, start + 1080, SEEK_SET); + if (hio_read(buf, 1, 4, f) < 4) { + return -1; + } + + if (!strncmp(buf + 2, "CH", 2) && + isdigit((unsigned char)buf[0]) && isdigit((unsigned char)buf[1])) { + i = (buf[0] - '0') * 10 + buf[1] - '0'; + if (i > 0 && i <= 32) { + goto found; + } + } + + if (!strncmp(buf + 1, "CHN", 3) && isdigit((unsigned char)*buf)) { + if (*buf - '0') { + goto found; + } + } + +#ifdef LIBXMP_CORE_PLAYER + if (memcmp(buf, "M.K.", 4)) + return -1; +#else + for (i = 0; i < ARRAY_SIZE(mod_magic); i++) { + if (!memcmp(buf, mod_magic[i].magic, 4)) + break; + } + if (i >= ARRAY_SIZE(mod_magic)) { + return -1; + } + + detected = mod_magic[i].flag; + + /* + * Sanity check to prevent loading NoiseRunner and other module + * formats with valid magic at offset 1080 (e.g. His Master's Noise) + */ + + hio_seek(f, start + 20, SEEK_SET); + for (i = 0; i < 31; i++) { + uint8 x; + + hio_seek(f, 22, SEEK_CUR); /* Instrument name */ + + /* OpenMPT can create mods with large samples */ + hio_read16b(f); /* sample size */ + + /* Chris Spiegel tells me that sandman.mod has 0x20 in finetune */ + x = hio_read8(f); + if (x & 0xf0 && x != 0x20) /* test finetune */ + return -1; + if (hio_read8(f) > 0x40) /* test volume */ + return -1; + hio_read16b(f); /* loop start */ + hio_read16b(f); /* loop size */ + } + + /* The following checks are only relevant for filtering out atypical + * M.K. variants. If the magic is from a recognizable source, skip them. */ + if (detected) + goto found; + + /* Test for UNIC tracker modules + * + * From Gryzor's Pro-Wizard PW_FORMATS-Engl.guide: + * ``The UNIC format is very similar to Protracker... At least in the + * heading... same length : 1084 bytes. Even the "M.K." is present, + * sometimes !! Maybe to disturb the rippers.. hehe but Pro-Wizard + * doesn't test this only!'' + */ + + /* get file size */ + size = hio_size(f); + smp_size = 0; + hio_seek(f, start + 20, SEEK_SET); + + /* get samples size */ + for (i = 0; i < 31; i++) { + hio_seek(f, 22, SEEK_CUR); + smp_size += 2 * hio_read16b(f); /* Length in 16-bit words */ + hio_seek(f, 6, SEEK_CUR); + } + + /* get number of patterns */ + num_pat = 0; + hio_seek(f, start + 952, SEEK_SET); + for (i = 0; i < 128; i++) { + uint8 x = hio_read8(f); + if (x > 0x7f) + break; + if (x > num_pat) + num_pat = x; + } + num_pat++; + + /* see if module size matches UNIC */ + if (start + 1084 + num_pat * 0x300 + smp_size == size) { + D_(D_CRIT "module size matches UNIC"); + return -1; + } + + /* validate pattern data in an attempt to catch UNICs with MOD size */ + for (count = i = 0; i < num_pat; i++) { + hio_seek(f, start + 1084 + 1024 * i, SEEK_SET); + if (!hio_read(pat_buf, 1024, 1, f)) { + D_(D_WARN "pattern %d: failed to read pattern data", i); + return -1; + } + if (validate_pattern(pat_buf) < 0) { + D_(D_WARN "pattern %d: error in pattern data", i); + /* Allow a few errors, "lexstacy" has 0x52 */ + count++; + } + } + if (count > 2) { + return -1; + } +#endif /* LIBXMP_CORE_PLAYER */ + +found: + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + + +#ifndef LIBXMP_CORE_PLAYER +static int is_st_ins(const char *s) +{ + if (s[0] != 's' && s[0] != 'S') + return 0; + if (s[1] != 't' && s[1] != 'T') + return 0; + if (s[2] != '-' || s[5] != ':') + return 0; + if (!isdigit((unsigned char)s[3]) || !isdigit((unsigned char)s[4])) + return 0; + + return 1; +} + +static int get_tracker_id(struct module_data *m, struct mod_header *mh, int id) +{ + struct xmp_module *mod = &m->mod; + int has_loop_0 = 0; + int has_vol_in_empty_ins = 0; + int i; + + /* Check if has instruments with loop size 0 */ + for (i = 0; i < 31; i++) { + if (mh->ins[i].loop_size == 0) { + has_loop_0 = 1; + break; + } + } + + /* Check if has instruments with size 0 and volume > 0 */ + for (i = 0; i < 31; i++) { + if (mh->ins[i].size == 0 && mh->ins[i].volume > 0) { + has_vol_in_empty_ins = 1; + break; + } + } + + /* + * Test Protracker-like files + */ + if (mh->restart == mod->pat) { + if (mod->chn == 4) { + id = TRACKER_SOUNDTRACKER; + } else { + id = TRACKER_UNKNOWN; + } + } else if (mh->restart == 0x78) { + if (mod->chn == 4) { + /* Can't trust this for Noisetracker, MOD.Data City Remix + * has Protracker effects and Noisetracker restart byte + */ + id = TRACKER_PROBABLY_NOISETRACKER; + } else { + id = TRACKER_UNKNOWN; + } + return id; + } else if (mh->restart < 0x7f) { + if (mod->chn == 4 && !has_vol_in_empty_ins) { + id = TRACKER_NOISETRACKER; + } else { + id = TRACKER_UNKNOWN; /* ? */ + } + mod->rst = mh->restart; + } else if (mh->restart == 0x7f) { + if (mod->chn == 4) { + if (has_loop_0) { + id = TRACKER_CLONE; + } + } else { + id = TRACKER_SCREAMTRACKER3; + } + return id; + } else if (mh->restart > 0x7f) { + id = TRACKER_UNKNOWN; /* ? */ + return id; + } + + if (!has_loop_0) { /* All loops are size 2 or greater */ + + for (i = 0; i < 31; i++) { + if (mh->ins[i].size == 1 && mh->ins[i].volume == 0) { + return TRACKER_CONVERTED; + } + } + + for (i = 0; i < 31; i++) { + if (is_st_ins((char *)mh->ins[i].name)) + break; + } + if (i == 31) { /* No st- instruments */ + for (i = 0; i < 31; i++) { + if (mh->ins[i].size != 0 + || mh->ins[i].loop_size != 1) { + continue; + } + + switch (mod->chn) { + case 4: + if (has_vol_in_empty_ins) { + id = TRACKER_OPENMPT; + } else { + id = TRACKER_NOISETRACKER; + /* or Octalyser */ + } + break; + case 6: + case 8: + id = TRACKER_OCTALYSER; + break; + default: + id = TRACKER_UNKNOWN; + } + return id; + } + + if (mod->chn == 4) { + id = TRACKER_PROTRACKER; + } else if (mod->chn == 6 || mod->chn == 8) { + /* FastTracker 1.01? */ + id = TRACKER_FASTTRACKER; + } else { + id = TRACKER_UNKNOWN; + } + } + } else { /* Has loops with size 0 */ + for (i = 15; i < 31; i++) { + /* Is the name or size set? */ + if (mh->ins[i].name[0] || mh->ins[i].size > 0) + break; + } + if (i == 31 && is_st_ins((char *)mh->ins[14].name)) { + return TRACKER_CONVERTEDST; + } + + /* Assume that Fast Tracker modules won't have ST- instruments */ + for (i = 0; i < 31; i++) { + if (is_st_ins((char *)mh->ins[i].name)) + break; + } + if (i < 31) { + return TRACKER_UNKNOWN_CONV; + } + + if (mod->chn == 4 || mod->chn == 6 || mod->chn == 8) { + return TRACKER_FASTTRACKER; + } + + id = TRACKER_UNKNOWN; /* ?! */ + } + + return id; +} +#endif /* LIBXMP_CORE_PLAYER */ + +static int mod_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + struct xmp_event *event; + struct mod_header mh; + char magic[8]; + uint8 *patbuf; +#ifndef LIBXMP_CORE_PLAYER + uint8 pat_high_fxx[256]; + const char *tracker = ""; + int detected = 0; + int tracker_id = TRACKER_PROTRACKER; + int out_of_range = 0; + int maybe_wow = 1; + int smp_size, ptsong = 0; + int needs_timing_detection = 0; + int samerow_fxx = 0; /* speed + BPM set on same row */ + int high_fxx = 0; /* high Fxx is used anywhere */ +#endif + int ptkloop = 0; /* Protracker loop */ + + LOAD_INIT(); + + mod->ins = 31; + mod->smp = mod->ins; + mod->chn = 0; + #ifndef LIBXMP_CORE_PLAYER + smp_size = 0; + #else + m->quirk |= QUIRK_PROTRACK; + #endif + m->period_type = PERIOD_MODRNG; + + hio_read(mh.name, 20, 1, f); + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); /* Instrument name */ + mh.ins[i].size = hio_read16b(f); /* Length in 16-bit words */ + mh.ins[i].finetune = hio_read8(f); /* Finetune (signed nibble) */ + mh.ins[i].volume = hio_read8(f); /* Linear playback volume */ + mh.ins[i].loop_start = hio_read16b(f); /* Loop start in 16-bit words */ + mh.ins[i].loop_size = hio_read16b(f); /* Loop size in 16-bit words */ + + #ifndef LIBXMP_CORE_PLAYER + /* Mod's Grave WOW files are converted from 669s and have default + * finetune and volume. + */ + if (mh.ins[i].size && (mh.ins[i].finetune != 0 || mh.ins[i].volume != 64)) + maybe_wow = 0; + + smp_size += 2 * mh.ins[i].size; + #endif + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 128, 1, f); + memset(magic, 0, sizeof(magic)); + hio_read(magic, 1, 4, f); + if (hio_error(f)) { + return -1; + } + +#ifndef LIBXMP_CORE_PLAYER + /* Mod's Grave WOW files always have a 0 restart byte; 6692WOW implements + * 669 repeating by inserting a pattern jump and ignores this byte. + */ + if (mh.restart != 0) + maybe_wow = 0; + + for (i = 0; i < ARRAY_SIZE(mod_magic); i++) { + if (!(strncmp (magic, mod_magic[i].magic, 4))) { + mod->chn = mod_magic[i].ch; + tracker_id = mod_magic[i].id; + detected = mod_magic[i].flag; + break; + } + } + + /* Enable timing detection for M.K. and M!K! modules. */ + if (tracker_id == TRACKER_PROTRACKER) + needs_timing_detection = 1; + + /* Digital Tracker MODs have an extra four bytes after the magic. + * These are always 00h 40h 00h 00h and can probably be ignored. */ + if (tracker_id == TRACKER_DIGITALTRACKER) { + hio_read32b(f); + } +#endif + + if (mod->chn == 0) { + #ifdef LIBXMP_CORE_PLAYER + if (!memcmp(magic, "M.K.", 4)) { + mod->chn = 4; + } else + #endif + if (!strncmp(magic + 2, "CH", 2) && + isdigit((unsigned char)magic[0]) && isdigit((unsigned char)magic[1])) { + mod->chn = (*magic - '0') * 10 + magic[1] - '0'; + } else if (!strncmp(magic + 1, "CHN", 3) && isdigit((unsigned char)*magic)) { + mod->chn = *magic - '0'; + } else { + return -1; + } + #ifndef LIBXMP_CORE_PLAYER + tracker_id = mod->chn & 1 ? TRACKER_TAKETRACKER : TRACKER_FASTTRACKER2; + detected = 1; + #endif + } + + strncpy(mod->name, (char *) mh.name, 20); + + mod->len = mh.len; + /* mod->rst = mh.restart; */ + + if (mod->rst >= mod->len) + mod->rst = 0; + memcpy(mod->xxo, mh.order, 128); + + for (i = 0; i < 128; i++) { + /* This fixes dragnet.mod (garbage in the order list) */ + if (mod->xxo[i] > 0x7f) + break; + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + } + mod->pat++; + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + +#ifndef LIBXMP_CORE_PLAYER + if (mh.ins[i].size >= 0x8000) { + tracker_id = TRACKER_OPENMPT; + needs_timing_detection = 0; + detected = 1; + } +#endif + + xxi = &mod->xxi[i]; + sub = &xxi->sub[0]; + xxs = &mod->xxs[i]; + + xxs->len = 2 * mh.ins[i].size; + xxs->lps = 2 * mh.ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size; + if (xxs->lpe > xxs->len) { + xxs->lpe = xxs->len; + } + xxs->flg = (mh.ins[i].loop_size > 1 && xxs->lpe >= 4) ? + XMP_SAMPLE_LOOP : 0; + sub->fin = (int8)(mh.ins[i].finetune << 4); + sub->vol = mh.ins[i].volume; + sub->pan = 0x80; + sub->sid = i; + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + + if (xxs->len > 0) { + xxi->nsm = 1; + } + } + +#ifndef LIBXMP_CORE_PLAYER + /* Experimental tracker-detection routine */ + + if (detected) + goto skip_test; + + /* Test for Flextrax modules + * + * FlexTrax is a soundtracker for Atari Falcon030 compatible computers. + * FlexTrax supports the standard MOD file format (up to eight channels) + * for compatibility reasons but also features a new enhanced module + * format FLX. The FLX format is an extended version of the standard + * MOD file format with support for real-time sound effects like reverb + * and delay. + */ + + if (0x43c + mod->pat * 4 * mod->chn * 0x40 + smp_size < m->size) { + char idbuffer[4]; + int pos = hio_tell(f); + int num_read; + if (pos < 0) { + return -1; + } + hio_seek(f, start + 0x43c + mod->pat * 4 * mod->chn * 0x40 + smp_size, SEEK_SET); + num_read = hio_read(idbuffer, 1, 4, f); + hio_seek(f, start + pos, SEEK_SET); + + if (num_read == 4 && !memcmp(idbuffer, "FLEX", 4)) { + tracker_id = TRACKER_FLEXTRAX; + needs_timing_detection = 0; + goto skip_test; + } + } + + /* Test for Mod's Grave WOW modules + * + * Stefan Danes said: + * This weird format is identical to '8CHN' but still uses the 'M.K.' ID. + * You can only test for WOW by calculating the size of the module for 8 + * channels and comparing this to the actual module length. If it's equal, + * the module is an 8 channel WOW. + * + * Addendum: very rarely, WOWs will have an odd length due to an extra byte, + * so round the filesize down in this check. False positive WOWs can be ruled + * out by checking the restart byte and sample volume (see above). + * + * Worst case if there are still issues with this, OpenMPT validates later + * patterns in potential WOW files (where sample data would be located in a + * regular M.K. MOD) to rule out false positives. + */ + + if (!strncmp(magic, "M.K.", 4) && maybe_wow && + (0x43c + mod->pat * 32 * 0x40 + smp_size) == (m->size & ~1)) { + mod->chn = 8; + tracker_id = TRACKER_MODSGRAVE; + needs_timing_detection = 0; + } else { + /* Test for Protracker song files */ + ptsong = !strncmp((char *)magic, "M.K.", 4) && + (0x43c + mod->pat * 0x400 == m->size); + if (ptsong) { + tracker_id = TRACKER_PROTRACKER; + goto skip_test; + } else { + /* something else */ + tracker_id = get_tracker_id(m, &mh, tracker_id); + } + } + +skip_test: +#endif + + if (mod->chn >= XMP_MAX_CHANNELS) { + return -1; + } + + mod->trk = mod->chn * mod->pat; + + for (i = 0; i < mod->ins; i++) { + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d %c", + i, mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + (mh.ins[i].loop_size > 1 && mod->xxs[i].lpe > 8) ? + 'L' : ' ', mod->xxi[i].sub[0].vol, + mod->xxi[i].sub[0].fin >> 4, + ptkloop && mod->xxs[i].lps == 0 && mh.ins[i].loop_size > 1 && + mod->xxs[i].len > mod->xxs[i].lpe ? '!' : ' '); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + if ((patbuf = (uint8 *) malloc(64 * 4 * mod->chn)) == NULL) { + return -1; + } + +#ifndef LIBXMP_CORE_PLAYER + memset(pat_high_fxx, 0, sizeof(pat_high_fxx)); +#endif + + for (i = 0; i < mod->pat; i++) { + uint8 *mod_event; + + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) { + free(patbuf); + return -1; + } + + if (hio_read(patbuf, 64 * 4 * mod->chn, 1, f) < 1) { + free(patbuf); + return -1; + } + +#ifndef LIBXMP_CORE_PLAYER + mod_event = patbuf; + for (j = 0; j < 64; j++) { + int speed_row = 0; + int bpm_row = 0; + for (k = 0; k < mod->chn; k++) { + int period; + + period = ((int)(LSN(mod_event[0])) << 8) | mod_event[1]; + if (period != 0 && (period < 108 || period > 907)) { + out_of_range = 1; + } + + /* Filter noisetracker events */ + if (tracker_id == TRACKER_PROBABLY_NOISETRACKER) { + unsigned char fxt = LSN(mod_event[2]); + unsigned char fxp = LSN(mod_event[3]); + + if ((fxt > 0x06 && fxt < 0x0a) || (fxt == 0x0e && fxp > 1)) { + tracker_id = TRACKER_UNKNOWN; + } + } + /* Needs CIA/VBlank detection? */ + if (LSN(mod_event[2]) == 0x0f) { + if (mod_event[3] >= 0x20) { + pat_high_fxx[i] = mod_event[3]; + m->compare_vblank = 1; + high_fxx = 1; + bpm_row = 1; + } else { + speed_row = 1; + } + } + mod_event += 4; + } + if (bpm_row && speed_row) { + samerow_fxx = 1; + } + } + + if (out_of_range) { + if (tracker_id == TRACKER_UNKNOWN && mh.restart == 0x7f) { + tracker_id = TRACKER_SCREAMTRACKER3; + } + + /* Check out-of-range notes in Amiga trackers */ + if (tracker_id == TRACKER_PROTRACKER || + tracker_id == TRACKER_NOISETRACKER || + tracker_id == TRACKER_PROBABLY_NOISETRACKER || + tracker_id == TRACKER_SOUNDTRACKER) { /* note > B-3 */ + + tracker_id = TRACKER_UNKNOWN; + } + } +#endif + + mod_event = patbuf; + for (j = 0; j < 64; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); +#ifdef LIBXMP_CORE_PLAYER + libxmp_decode_protracker_event(event, mod_event); +#else + switch (tracker_id) { + case TRACKER_PROBABLY_NOISETRACKER: + case TRACKER_NOISETRACKER: + libxmp_decode_noisetracker_event(event, mod_event); + break; + default: + libxmp_decode_protracker_event(event, mod_event); + } +#endif + mod_event += 4; + } + } + } + free(patbuf); + +#ifndef LIBXMP_CORE_PLAYER + /* VBlank detection routine. + * Despite VBlank being dependent on the tracker used, VBlank detection + * is complex and uses heuristics mostly independent from tracker ID. + * See also: the scan.c comparison code enabled by m->compare_vblank + */ + if (!needs_timing_detection) { + /* Noisetracker and some other trackers do not support CIA timing. The + * only known MOD in the wild that relies on this is muppenkorva.mod + * by Glue Master (loaded by the His Master's Noise loader). + */ + if (tracker_is_vblank(tracker_id)) { + m->quirk |= QUIRK_NOBPM; + } + m->compare_vblank = 0; + + } else if (samerow_fxx) { + /* If low Fxx and high Fxx are on the same row, there's a high chance + * this is from a CIA-based tracker. There are some exceptions. + */ + if (tracker_id == TRACKER_NOISETRACKER || + tracker_id == TRACKER_PROBABLY_NOISETRACKER || + tracker_id == TRACKER_SOUNDTRACKER) { + + tracker_id = TRACKER_UNKNOWN; + } + m->compare_vblank = 0; + + } else if (high_fxx && mod->len >= 8) { + /* Test for high Fxx at the end only--this is typically VBlank, + * and is used to add silence to the end of modules. + * + * Exception: if the final high Fxx is F7D, this module is either CIA + * or is VBlank that was modified to play as CIA, so do nothing. + * + * TODO: MPT resets modules on the end loop, so some of the very long + * silent sections in modules affected by this probably expect CIA. It + * should eventually be possible to detect those. + */ + const int threshold = mod->len - 2; + + for (i = 0; i < threshold; i++) { + if (pat_high_fxx[mod->xxo[i]]) + break; + } + if (i == threshold) { + for (i = mod->len - 1; i >= threshold; i--) { + uint8 fxx = pat_high_fxx[mod->xxo[i]]; + if (fxx == 0x00) + continue; + if (fxx == 0x7d) + break; + + m->compare_vblank = 0; + m->quirk |= QUIRK_NOBPM; + break; + } + } + } + + switch (tracker_id) { + case TRACKER_PROTRACKER: + tracker = "Protracker"; + ptkloop = 1; + break; + case TRACKER_PROBABLY_NOISETRACKER: + case TRACKER_NOISETRACKER: + tracker = "Noisetracker"; + break; + case TRACKER_SOUNDTRACKER: + tracker = "Soundtracker"; + break; + case TRACKER_FASTTRACKER: + case TRACKER_FASTTRACKER2: + tracker = "Fast Tracker"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_TAKETRACKER: + tracker = "Take Tracker"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_OCTALYSER: + tracker = "Octalyser"; + break; + case TRACKER_DIGITALTRACKER: + tracker = "Digital Tracker"; + break; + case TRACKER_FLEXTRAX: + tracker = "Flextrax"; + break; + case TRACKER_MODSGRAVE: + tracker = "Mod's Grave"; + break; + case TRACKER_SCREAMTRACKER3: + tracker = "Scream Tracker"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_CONVERTEDST: + case TRACKER_CONVERTED: + tracker = "Converted"; + break; + case TRACKER_CLONE: + tracker = "Protracker clone"; + m->period_type = PERIOD_AMIGA; + break; + case TRACKER_OPENMPT: + tracker = "OpenMPT"; + ptkloop = 1; + break; + default: + case TRACKER_UNKNOWN_CONV: + case TRACKER_UNKNOWN: + tracker = "Unknown tracker"; + m->period_type = PERIOD_AMIGA; + break; + } + + if (out_of_range) { + m->period_type = PERIOD_AMIGA; + } + + if (tracker_id == TRACKER_MODSGRAVE) { + snprintf(mod->type, XMP_NAME_SIZE, "%s", tracker); + } else { + snprintf(mod->type, XMP_NAME_SIZE, "%s %s", tracker, magic); + } +#else + libxmp_set_type(m, (mod->chn == 4) ? "Protracker" : "Fasttracker"); +#endif + + MODULE_INFO(); + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + int flags; + + if (!mod->xxs[i].len) + continue; + + flags = (ptkloop && mod->xxs[i].lps == 0) ? SAMPLE_FLAG_FULLREP : 0; + + #ifdef LIBXMP_CORE_PLAYER + if (libxmp_load_sample(m, f, flags, &mod->xxs[i], NULL) < 0) + return -1; + #else + if (ptsong) { + HIO_HANDLE *s; + char sn[XMP_MAXPATH]; + char tmpname[32]; + const char *instname = mod->xxi[i].name; + + if (!instname[0] || !m->dirname) + continue; + + if (libxmp_copy_name_for_fopen(tmpname, instname, 32)) + continue; + + snprintf(sn, XMP_MAXPATH, "%s%s", m->dirname, tmpname); + + if ((s = hio_open(sn, "rb")) != NULL) { + if (libxmp_load_sample(m, s, flags, &mod->xxs[i], NULL) < 0) { + hio_close(s); + return -1; + } + hio_close(s); + } + } else { + uint8 buf[5]; + long pos; + int num; + + if ((pos = hio_tell(f)) < 0) { + return -1; + } + num = hio_read(buf, 1, 5, f); + + if (num == 5 && !memcmp(buf, "ADPCM", 5)) { + flags |= SAMPLE_FLAG_ADPCM; + } else { + hio_seek(f, pos, SEEK_SET); + } + + if (libxmp_load_sample(m, f, flags, &mod->xxs[i], NULL) < 0) + return -1; + } + #endif + } + + #ifdef LIBXMP_CORE_PLAYER + if (mod->chn > 4) { + m->quirk &= ~QUIRK_PROTRACK; + m->quirk |= QUIRKS_FT2 | QUIRK_FTMOD; + m->read_event_type = READ_EVENT_FT2; + m->period_type = PERIOD_AMIGA; + } + #else + if (tracker_id == TRACKER_PROTRACKER || tracker_id == TRACKER_OPENMPT) { + m->quirk |= QUIRK_PROTRACK; + } else if (tracker_id == TRACKER_SCREAMTRACKER3) { + m->c4rate = C4_NTSC_RATE; + m->quirk |= QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + } else if (tracker_id == TRACKER_FASTTRACKER || tracker_id == TRACKER_FASTTRACKER2 || tracker_id == TRACKER_TAKETRACKER || tracker_id == TRACKER_MODSGRAVE || mod->chn > 4) { + m->c4rate = C4_NTSC_RATE; + m->quirk |= QUIRKS_FT2 | QUIRK_FTMOD; + m->read_event_type = READ_EVENT_FT2; + m->period_type = PERIOD_AMIGA; + } + #endif + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/mtm_load.c b/thirdparty/libxmp/src/loaders/mtm_load.c new file mode 100644 index 000000000..60ee8a6d7 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/mtm_load.c @@ -0,0 +1,344 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" + +struct mtm_file_header { + uint8 magic[3]; /* "MTM" */ + uint8 version; /* MSN=major, LSN=minor */ + uint8 name[20]; /* ASCIIZ Module name */ + uint16 tracks; /* Number of tracks saved */ + uint8 patterns; /* Number of patterns saved */ + uint8 modlen; /* Module length */ + uint16 extralen; /* Length of the comment field */ + uint8 samples; /* Number of samples */ + uint8 attr; /* Always zero */ + uint8 rows; /* Number rows per track */ + uint8 channels; /* Number of tracks per pattern */ + uint8 pan[32]; /* Pan positions for each channel */ +}; + +struct mtm_instrument_header { + uint8 name[22]; /* Instrument name */ + uint32 length; /* Instrument length in bytes */ + uint32 loop_start; /* Sample loop start */ + uint32 loopend; /* Sample loop end */ + uint8 finetune; /* Finetune */ + uint8 volume; /* Playback volume */ + uint8 attr; /* &0x01: 16bit sample */ +}; + +static int mtm_test(HIO_HANDLE *, char *, const int); +static int mtm_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_mtm = { + "Multitracker", + mtm_test, + mtm_load +}; + +static int mtm_test(HIO_HANDLE *f, char *t, const int start) +{ + uint8 buf[4]; + + if (hio_read(buf, 1, 4, f) < 4) + return -1; + if (memcmp(buf, "MTM", 3)) + return -1; + if (buf[3] != 0x10) + return -1; + + libxmp_read_title(f, t, 20); + + return 0; +} + +static int mtm_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + struct mtm_file_header mfh; + struct mtm_instrument_header mih; + uint8 mt[192]; + int fxx[2]; + + LOAD_INIT(); + + hio_read(mfh.magic, 3, 1, f); /* "MTM" */ + mfh.version = hio_read8(f); /* MSN=major, LSN=minor */ + hio_read(mfh.name, 20, 1, f); /* ASCIIZ Module name */ + mfh.tracks = hio_read16l(f); /* Number of tracks saved */ + mfh.patterns = hio_read8(f); /* Number of patterns saved */ + mfh.modlen = hio_read8(f); /* Module length */ + mfh.extralen = hio_read16l(f); /* Length of the comment field */ + + mfh.samples = hio_read8(f); /* Number of samples */ + if (mfh.samples > 63) { + return -1; + } + + mfh.attr = hio_read8(f); /* Always zero */ + + mfh.rows = hio_read8(f); /* Number rows per track */ + if (mfh.rows != 64) + return -1; + + mfh.channels = hio_read8(f); /* Number of tracks per pattern */ + if (mfh.channels > MIN(32, XMP_MAX_CHANNELS)) { + return -1; + } + + hio_read(mfh.pan, 32, 1, f); /* Pan positions for each channel */ + + if (hio_error(f)) { + return -1; + } + +#if 0 + if (strncmp((char *)mfh.magic, "MTM", 3)) + return -1; +#endif + + mod->trk = mfh.tracks + 1; + mod->pat = mfh.patterns + 1; + mod->len = mfh.modlen + 1; + mod->ins = mfh.samples; + mod->smp = mod->ins; + mod->chn = mfh.channels; + mod->spd = 6; + mod->bpm = 125; + + strncpy(mod->name, (char *)mfh.name, 20); + libxmp_set_type(m, "MultiTracker %d.%02d MTM", MSN(mfh.version), + LSN(mfh.version)); + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments */ + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + hio_read(mih.name, 22, 1, f); /* Instrument name */ + mih.length = hio_read32l(f); /* Instrument length in bytes */ + + if (mih.length > MAX_SAMPLE_SIZE) + return -1; + + mih.loop_start = hio_read32l(f); /* Sample loop start */ + mih.loopend = hio_read32l(f); /* Sample loop end */ + mih.finetune = hio_read8(f); /* Finetune */ + mih.volume = hio_read8(f); /* Playback volume */ + mih.attr = hio_read8(f); /* &0x01: 16bit sample */ + + xxs->len = mih.length; + xxs->lps = mih.loop_start; + xxs->lpe = mih.loopend; + xxs->flg = (xxs->lpe > 2) ? XMP_SAMPLE_LOOP : 0; /* 1 == Forward loop */ + if (mfh.attr & 1) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + sub->vol = mih.volume; + sub->fin = mih.finetune; + sub->pan = 0x80; + sub->sid = i; + + libxmp_instrument_name(mod, i, mih.name, 22); + + if (xxs->len > 0) + mod->xxi[i].nsm = 1; + + D_(D_INFO "[%2X] %-22.22s %04x%c%04x %04x %c V%02x F%+03d\n", i, + xxi->name, xxs->len, xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', + xxs->lps, xxs->lpe, xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, sub->fin - 0x80); + } + + hio_read(mod->xxo, 1, 128, f); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored tracks: %d", mod->trk - 1); + + fxx[0] = fxx[1] = 0; + for (i = 0; i < mod->trk; i++) { + + if (libxmp_alloc_track(mod, i, mfh.rows) < 0) + return -1; + + if (i == 0) + continue; + + if (hio_read(mt, 3, 64, f) != 64) + return -1; + + for (j = 0; j < 64; j++) { + struct xmp_event *e = &mod->xxt[i]->event[j]; + uint8 *d = mt + j * 3; + + e->note = d[0] >> 2; + if (e->note) { + e->note += 37; + } + e->ins = ((d[0] & 0x3) << 4) + MSN(d[1]); + e->fxt = LSN(d[1]); + e->fxp = d[2]; + if (e->fxt > FX_SPEED) { + e->fxt = e->fxp = 0; + } + + /* See tempo mode detection below. */ + if (e->fxt == FX_SPEED) { + fxx[e->fxp >= 0x20] = 1; + } + + /* Set pan effect translation */ + if (e->fxt == FX_EXTENDED && MSN(e->fxp) == 0x8) { + e->fxt = FX_SETPAN; + e->fxp <<= 4; + } + } + } + + /* Read patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat - 1); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern(mod, i) < 0) + return -1; + + mod->xxp[i]->rows = 64; + for (j = 0; j < 32; j++) { + int track = hio_read16l(f); + + if (track >= mod->trk) { + track = 0; + } + + if (j < mod->chn) { + mod->xxp[i]->index[j] = track; + } + } + } + + /* Tempo mode detection. + * + * The MTM tempo effect has an unusual property: when speed is set, the + * tempo resets to 125, and when tempo is set, the speed resets to 6. + * Modules that use both speed and tempo effects need to emulate this. + * See: Absolve the Ambience by Sybaris, Soma by Ranger Rick. + * + * Dual Module Player and other DOS players did not know about this and + * did not implement support for it, and instead used Protracker Fxx. + * Many MTM authors created modules that rely on this which are various + * degrees of broken in the tracker they were made with! Several MTMs + * by Phoenix and Silent Mode expect this. The majority of them can be + * detected by checking for high Fxx and low Fxx on the same row. + */ + if (fxx[0] && fxx[1]) { + /* Both used, check patterns. */ + D_(D_INFO "checking patterns for MT or DMP Fxx effect usage"); + for (i = 0; i < mod->pat; i++) { + for (j = 0; j < mfh.rows; j++) { + fxx[0] = fxx[1] = 0; + for (k = 0; k < mod->chn; k++) { + struct xmp_event *e = &EVENT(i, k, j); + if (e->fxt == FX_SPEED) { + fxx[e->fxp >= 0x20] = 1; + } + } + if (fxx[0] && fxx[1]) { + /* Same row, no change required */ + D_(D_INFO "probably DMP (%d:%d)", i, j); + goto probably_dmp; + } + } + } + D_(D_INFO "probably MT; injecting speed/BPM reset effects"); + for (i = 0; i < mod->pat; i++) { + for (j = 0; j < mfh.rows; j++) { + for (k = 0; k < mod->chn; k++) { + struct xmp_event *e = &EVENT(i, k, j); + if (e->fxt == FX_SPEED) { + e->f2t = FX_SPEED; + e->f2p = (e->fxp < 0x20) ? 125 : 6; + } + } + } + } + } + probably_dmp: + + /* Comments */ + if (mfh.extralen) { + m->comment = (char *)malloc(mfh.extralen + 1); + if (m->comment) { + /* Comments are stored in 40 byte ASCIIZ lines. */ + int len = hio_read(m->comment, 1, mfh.extralen, f); + int line, last_line = 0; + + for (i = 0; i + 40 <= len; i += 40) { + if (m->comment[i] != '\0') + last_line = i + 40; + } + for (j = 0, line = 0; line < last_line; line += 40) { + char *pos = m->comment + line; + for (i = 0; i < 39; i++) { + if (pos[i] == '\0') + break; + m->comment[j++] = pos[i]; + } + m->comment[j++] = '\n'; + } + m->comment[j] = '\0'; + } else { + hio_seek(f, mfh.extralen, SEEK_CUR); + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) + return -1; + } + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = mfh.pan[i] << 4; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/muse_load.c b/thirdparty/libxmp/src/loaders/muse_load.c new file mode 100644 index 000000000..d0111308f --- /dev/null +++ b/thirdparty/libxmp/src/loaders/muse_load.c @@ -0,0 +1,110 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../miniz.h" + +static int muse_test(HIO_HANDLE *, char *, const int); +static int muse_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_muse = { + "MUSE container", + muse_test, + muse_load +}; + +static int muse_test(HIO_HANDLE * f, char *t, const int start) +{ + uint8 in[8]; + uint32 r; + + if (hio_read(in, 1, 8, f) != 8) { + return -1; + } + if (memcmp(in, "MUSE", 4) != 0) { + return -1; + } + r = readmem32b(in + 4); + if (r != 0xdeadbeaf && r != 0xdeadbabe) { + return -1; + } + if (t) { + *t = '\0'; /* FIXME */ + } + return 0; +} + +static int muse_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + void *in, *out; + long inlen; + size_t outlen; + int err; + + inlen = hio_size(f); + if (inlen < 24 || inlen >= LIBXMP_DEPACK_LIMIT) { + D_(D_CRIT "bad file size"); + return -1; + } + if (hio_seek(f, 24, SEEK_SET) < 0) { + D_(D_CRIT "hio_seek() failed"); + return -1; + } + + inlen -= 24; + in = (uint8 *)malloc(inlen); + if (!in) { + D_(D_CRIT "Out of memory"); + return -1; + } + if (hio_read(in, 1, inlen, f) != inlen) { + D_(D_CRIT "Failed reading input file"); + free(in); + return -1; + } + + out = tinfl_decompress_mem_to_heap(in, inlen, &outlen, TINFL_FLAG_PARSE_ZLIB_HEADER); + if (!out) { + free(in); + D_(D_CRIT "tinfl_decompress_mem_to_heap() failed"); + return -1; + } + free(in); + + if (hio_reopen_mem(out, outlen, 1, f) < 0) { + free(out); + return -1; + } + err = libxmp_loader_gal5.test(f, NULL, 0); + hio_seek(f, 0, SEEK_SET); + if (err == 0) { + err = libxmp_loader_gal5.loader(m, f, 0); + } else { + err = libxmp_loader_gal4.test(f, NULL, 0); + hio_seek(f, 0, SEEK_SET); + if (err == 0) { + err = libxmp_loader_gal4.loader(m, f, 0); + } + } + + return err; +} diff --git a/thirdparty/libxmp/src/loaders/no_load.c b/thirdparty/libxmp/src/loaders/no_load.c new file mode 100644 index 000000000..7846ba3b2 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/no_load.c @@ -0,0 +1,257 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +/* Nir Oren's Liquid Tracker old "NO" format. I have only one NO module, + * Moti Radomski's "Time after time" from ftp.modland.com. + */ + + +static int no_test (HIO_HANDLE *, char *, const int); +static int no_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_no = { + "Liquid Tracker NO", + no_test, + no_load +}; + +static int no_test(HIO_HANDLE *f, char *t, const int start) +{ + int nsize, pat, chn; + int i; + + hio_seek(f, start, SEEK_CUR); + + if (hio_read32b(f) != 0x4e4f0000) /* NO 0x00 0x00 */ + return -1; + + nsize = hio_read8(f); + if (nsize != 20) + return -1; + + /* test title */ + for (i = 0; i < nsize; i++) { + if (hio_read8(f) == 0) + return -1; + } + + hio_seek(f, 9, SEEK_CUR); + + /* test number of patterns */ + pat = hio_read8(f); + if (pat == 0) + return -1; + + hio_read8(f); + + /* test number of channels */ + chn = hio_read8(f); + if (chn <= 0 || chn > 16) + return -1; + + hio_seek(f, start + 5, SEEK_SET); + + libxmp_read_title(f, t, nsize); + + return 0; +} + + +static const uint8 fx[15] = { + FX_ARPEGGIO, + 0, + FX_BREAK, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; + + +static int no_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + int i, j, k; + int nsize; + + LOAD_INIT(); + + hio_read32b(f); /* NO 0x00 0x00 */ + + libxmp_set_type(m, "Liquid Tracker"); + + nsize = hio_read8(f); + for (i = 0; i < nsize; i++) { + uint8 x = hio_read8(f); + if (i < XMP_NAME_SIZE) + mod->name[i] = x; + } + + hio_read16l(f); + hio_read16l(f); + hio_read16l(f); + hio_read16l(f); + hio_read8(f); + mod->pat = hio_read8(f); + hio_read8(f); + mod->chn = hio_read8(f); + mod->trk = mod->pat * mod->chn; + hio_read8(f); + hio_read16l(f); + hio_read16l(f); + hio_read8(f); + mod->ins = mod->smp = 63; + + for (i = 0; i < 256; i++) { + uint8 x = hio_read8(f); + if (x == 0xff) + break; + mod->xxo[i] = x; + } + hio_seek(f, 255 - i, SEEK_CUR); + mod->len = i; + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read instrument names */ + for (i = 0; i < mod->ins; i++) { + int hasname, c2spd; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + nsize = hio_read8(f); + if (hio_error(f)) { + return -1; + } + + hasname = 0; + for (j = 0; j < nsize; j++) { + uint8 x = hio_read8(f); + if (x != 0x20) + hasname = 1; + if (j < 32) + mod->xxi[i].name[j] = x; + } + if (!hasname) + mod->xxi[i].name[0] = 0; + + hio_read32l(f); + hio_read32l(f); + mod->xxi[i].sub[0].vol = hio_read8(f); + c2spd = hio_read16l(f); + mod->xxs[i].len = hio_read16l(f); + mod->xxs[i].lps = hio_read16l(f); + mod->xxs[i].lpe = hio_read16l(f); + hio_read32l(f); + hio_read16l(f); + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + /* + mod->xxs[i].lps = 0; + mod->xxs[i].lpe = 0; + */ + mod->xxs[i].flg = mod->xxs[i].lpe > 0 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].fin = 0; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %5d", + i, mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, c2spd); + + libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d ", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < mod->xxp[i]->rows; j++) { + for (k = 0; k < mod->chn; k++) { + uint32 x, note, ins, vol, fxt, fxp; + + event = &EVENT (i, k, j); + + x = hio_read32l(f); + note = x & 0x0000003f; + ins = (x & 0x00001fc0) >> 6; + vol = (x & 0x000fe000) >> 13; + fxt = (x & 0x00f00000) >> 20; + fxp = (x & 0xff000000) >> 24; + + if (note != 0x3f) + event->note = 36 + note; + if (ins != 0x7f) + event->ins = 1 + ins; + if (vol != 0x7f) + event->vol = vol; + if (fxt != 0x0f) { + event->fxt = fx[fxt]; + event->fxp = fxp; + } + } + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len == 0) + continue; + if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->quirk |= QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/okt_load.c b/thirdparty/libxmp/src/loaders/okt_load.c new file mode 100644 index 000000000..65a9ab45a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/okt_load.c @@ -0,0 +1,389 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Based on the format description written by Harald Zappe. + * Additional information about Oktalyzer modules from Bernardo + * Innocenti's XModule 3.4 sources. + */ + +#include "loader.h" +#include "iff.h" + +static int okt_test(HIO_HANDLE *, char *, const int); +static int okt_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_okt = { + "Oktalyzer", + okt_test, + okt_load +}; + +static int okt_test(HIO_HANDLE *f, char *t, const int start) +{ + char magic[8]; + + if (hio_read(magic, 1, 8, f) < 8) + return -1; + + if (strncmp(magic, "OKTASONG", 8)) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +#define OKT_MODE8 0x00 /* 7 bit samples */ +#define OKT_MODE4 0x01 /* 8 bit samples */ +#define OKT_MODEB 0x02 /* Both */ + +#define NONE 0xff + +struct local_data { + int mode[36]; + int idx[36]; + int pattern; + int sample; + int samples; + int has_cmod; + int has_samp; + int has_slen; +}; + +static const int fx[32] = { + NONE, + FX_PORTA_UP, /* 1 */ + FX_PORTA_DN, /* 2 */ + NONE, + NONE, + NONE, + NONE, + NONE, + NONE, + NONE, + FX_OKT_ARP3, /* 10 */ + FX_OKT_ARP4, /* 11 */ + FX_OKT_ARP5, /* 12 */ + FX_NSLIDE2_DN, /* 13 */ + NONE, + NONE, /* 15 - filter */ + NONE, + FX_NSLIDE2_UP, /* 17 */ + NONE, + NONE, + NONE, + FX_NSLIDE_DN, /* 21 */ + NONE, + NONE, + NONE, + FX_JUMP, /* 25 */ + NONE, + NONE, /* 27 - release */ + FX_SPEED, /* 28 */ + NONE, + FX_NSLIDE_UP, /* 30 */ + FX_VOLSET /* 31 */ +}; + +static int get_cmod(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i; + + /* Sanity check */ + if (data->has_cmod || size < 8) { + return -1; + } + data->has_cmod = 1; + + mod->chn = 0; + for (i = 0; i < 4; i++) { + int pan = (((i + 1) / 2) % 2) * 0xff; + int p = 0x80 + (pan - 0x80) * m->defpan / 100; + + if (hio_read16b(f) == 0) { + mod->xxc[mod->chn++].pan = p; + } else { + mod->xxc[mod->chn].flg |= XMP_CHANNEL_SPLIT | (i << 4); + mod->xxc[mod->chn++].pan = p; + mod->xxc[mod->chn].flg |= XMP_CHANNEL_SPLIT | (i << 4); + mod->xxc[mod->chn++].pan = p; + } + + } + + return 0; +} + +static int get_samp(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int i, j; + int looplen; + + /* Sanity check */ + if (data->has_samp || size != 36 * 32) { + return -1; + } + data->has_samp = 1; + + /* Should be always 36 */ + mod->ins = size / 32; /* sizeof(struct okt_instrument_header); */ + mod->smp = mod->ins; + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (j = i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[j]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + hio_read(xxi->name, 1, 20, f); + + /* Sample size is always rounded down */ + xxs->len = hio_read32b(f) & ~1; + xxs->lps = hio_read16b(f) << 1; + looplen = hio_read16b(f) << 1; + xxs->lpe = xxs->lps + looplen; + xxs->flg = looplen > 2 ? XMP_SAMPLE_LOOP : 0; + + sub->vol = hio_read16b(f); + data->mode[i] = hio_read16b(f); + + sub->pan = 0x80; + sub->sid = j; + + data->idx[j] = i; + + if (xxs->len > 0) { + xxi->nsm = 1; + j++; + } + } + data->samples = j; + + return 0; +} + +static int get_spee(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + mod->spd = hio_read16b(f); + mod->bpm = 125; + + return 0; +} + +static int get_slen(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + + /* Sanity check */ + if (data->has_slen || !data->has_cmod || size < 2) { + return -1; + } + data->has_slen = 1; + + mod->pat = hio_read16b(f); + mod->trk = mod->pat * mod->chn; + + return 0; +} + +static int get_plen(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + mod->len = hio_read16b(f); + + /* Sanity check */ + if (mod->len > 256) + return -1; + + D_(D_INFO "Module length: %d", mod->len); + + return 0; +} + +static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + + if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) + return -1; + + return 0; +} + +static int get_pbod(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + struct xmp_event *e; + uint16 rows; + int j; + + /* Sanity check */ + if (!data->has_slen || !data->has_cmod) { + return -1; + } + + if (data->pattern >= mod->pat) + return 0; + + if (!data->pattern) { + if (libxmp_init_pattern(mod) < 0) + return -1; + D_(D_INFO "Stored patterns: %d", mod->pat); + } + + rows = hio_read16b(f); + + if (libxmp_alloc_pattern_tracks(mod, data->pattern, rows) < 0) + return -1; + + for (j = 0; j < rows * mod->chn; j++) { + uint8 note, ins, fxt; + + e = &EVENT(data->pattern, j % mod->chn, j / mod->chn); + memset(e, 0, sizeof(struct xmp_event)); + + note = hio_read8(f); + ins = hio_read8(f); + + if (note) { + e->note = 48 + note; + e->ins = 1 + ins; + } + + fxt = hio_read8(f); + if (fxt >= ARRAY_SIZE(fx)) { + return -1; + } + e->fxt = fx[fxt]; + e->fxp = hio_read8(f); + + if ((e->fxt == FX_VOLSET) && (e->fxp > 0x40)) { + if (e->fxp <= 0x50) { + e->fxt = FX_VOLSLIDE; + e->fxp -= 0x40; + } else if (e->fxp <= 0x60) { + e->fxt = FX_VOLSLIDE; + e->fxp = (e->fxp - 0x50) << 4; + } else if (e->fxp <= 0x70) { + e->fxt = FX_F_VSLIDE_DN; + e->fxp = e->fxp - 0x60; + } else if (e->fxp <= 0x80) { + e->fxt = FX_F_VSLIDE_UP; + e->fxp = e->fxp - 0x70; + } + } + if (e->fxt == FX_ARPEGGIO) /* Arpeggio fixup */ + e->fxp = (((24 - MSN(e->fxp)) % 12) << 4) | LSN(e->fxp); + if (e->fxt == NONE) + e->fxt = e->fxp = 0; + } + data->pattern++; + + return 0; +} + +static int get_sbod(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + int flags = 0; + int i, sid; + + if (data->sample >= data->samples) + return 0; + + D_(D_INFO "Stored samples: %d", mod->smp); + + i = data->idx[data->sample]; + if (data->mode[i] == OKT_MODE8 || data->mode[i] == OKT_MODEB) + flags = SAMPLE_FLAG_7BIT; + + sid = mod->xxi[i].sub[0].sid; + if (libxmp_load_sample(m, f, flags, &mod->xxs[sid], NULL) < 0) + return -1; + + data->sample++; + + return 0; +} + +static int okt_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + iff_handle handle; + struct local_data data; + int ret; + + LOAD_INIT(); + + hio_seek(f, 8, SEEK_CUR); /* OKTASONG */ + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + memset(&data, 0, sizeof(struct local_data)); + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "CMOD", get_cmod); + ret |= libxmp_iff_register(handle, "SAMP", get_samp); + ret |= libxmp_iff_register(handle, "SPEE", get_spee); + ret |= libxmp_iff_register(handle, "SLEN", get_slen); + ret |= libxmp_iff_register(handle, "PLEN", get_plen); + ret |= libxmp_iff_register(handle, "PATT", get_patt); + ret |= libxmp_iff_register(handle, "PBOD", get_pbod); + ret |= libxmp_iff_register(handle, "SBOD", get_sbod); + + if (ret != 0) + return -1; + + libxmp_set_type(m, "Oktalyzer"); + + MODULE_INFO(); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + m->period_type = PERIOD_MODRNG; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/pt3_load.c b/thirdparty/libxmp/src/loaders/pt3_load.c new file mode 100644 index 000000000..8c62560bc --- /dev/null +++ b/thirdparty/libxmp/src/loaders/pt3_load.c @@ -0,0 +1,305 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "mod.h" +#include "iff.h" + +#define MAGIC_FORM MAGIC4('F','O','R','M') +#define MAGIC_MODL MAGIC4('M','O','D','L') +#define MAGIC_VERS MAGIC4('V','E','R','S') +#define MAGIC_INFO MAGIC4('I','N','F','O') + +static int pt3_test(HIO_HANDLE *, char *, const int); +static int pt3_load(struct module_data *, HIO_HANDLE *, const int); +static int ptdt_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_pt3 = { + "Protracker 3", + pt3_test, + pt3_load +}; + +static int pt3_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_FORM) + return -1; + + hio_read32b(f); /* skip size */ + + if (hio_read32b(f) != MAGIC_MODL) + return -1; + + if (hio_read32b(f) != MAGIC_VERS) + return -1; + + hio_read32b(f); /* skip size */ + + hio_seek(f, 10, SEEK_CUR); + + if (hio_read32b(f) == MAGIC_INFO) { + hio_read32b(f); /* skip size */ + libxmp_read_title(f, t, 32); + } else { + libxmp_read_title(f, t, 0); + } + + return 0; +} + +#define PT3_FLAG_CIA 0x0001 /* VBlank if not set */ +#define PT3_FLAG_FILTER 0x0002 /* Filter status */ +#define PT3_FLAG_SONG 0x0004 /* Modules have this bit unset */ +#define PT3_FLAG_IRQ 0x0008 /* Soft IRQ */ +#define PT3_FLAG_VARPAT 0x0010 /* Variable pattern length */ +#define PT3_FLAG_8VOICE 0x0020 /* 4 voices if not set */ +#define PT3_FLAG_16BIT 0x0040 /* 8 bit samples if not set */ +#define PT3_FLAG_RAWPAT 0x0080 /* Packed patterns if not set */ + +struct local_data { + int has_ptdt; +}; + +static int get_info(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct xmp_module *mod = &m->mod; + struct local_data *data = (struct local_data *)parm; + /* int flags; */ + /* int day, month, year, hour, min, sec; + int dhour, dmin, dsec; */ + + /* Sanity check */ + if(data->has_ptdt) { + return -1; + } + + hio_read(mod->name, 1, 32, f); + mod->ins = hio_read16b(f); + mod->len = hio_read16b(f); + mod->pat = hio_read16b(f); + mod->gvl = hio_read16b(f); + mod->bpm = hio_read16b(f); /* Not clamped by Protracker 3.6 */ + /*flags =*/ hio_read16b(f); + /*day =*/ hio_read16b(f); + /*month =*/ hio_read16b(f); + /*year =*/ hio_read16b(f); + /*hour =*/ hio_read16b(f); + /*min =*/ hio_read16b(f); + /*sec =*/ hio_read16b(f); + /*dhour =*/ hio_read16b(f); + /*dmin =*/ hio_read16b(f); + /*dsec =*/ hio_read16b(f); + + /* Sanity check */ + if (mod->ins > 255 || mod->len > 256 || mod->pat > 255) { + return -1; + } + + MODULE_INFO(); + + /*D_(D_INFO "Creation date: %02d/%02d/%02d %02d:%02d:%02d", + day, month, year, hour, min, sec); + D_(D_INFO "Playing time: %02d:%02d:%02d", dhour, dmin, dsec);*/ + + return 0; +} + +static int get_cmnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + D_(D_INFO "Comment size: %d", size); + + return 0; +} + +static int get_ptdt(struct module_data *m, int size, HIO_HANDLE *f, void *parm) +{ + struct local_data *data = (struct local_data *)parm; + + /* Sanity check */ + if(data->has_ptdt) { + return -1; + } + data->has_ptdt = 1; + + ptdt_load(m, f, 0); + + return 0; +} + +static int pt3_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + iff_handle handle; + struct local_data data; + char buf[20]; + int ret; + + LOAD_INIT(); + + memset(&data, 0, sizeof(struct local_data)); + + hio_read32b(f); /* FORM */ + hio_read32b(f); /* size */ + hio_read32b(f); /* MODL */ + hio_read32b(f); /* VERS */ + hio_read32b(f); /* VERS size */ + + if (hio_read(buf, 1, 10, f) < 10) + return -1; + libxmp_set_type(m, "%-6.6s IFFMODL", buf + 4); + + handle = libxmp_iff_new(); + if (handle == NULL) + return -1; + + /* IFF chunk IDs */ + ret = libxmp_iff_register(handle, "INFO", get_info); + ret |= libxmp_iff_register(handle, "CMNT", get_cmnt); + ret |= libxmp_iff_register(handle, "PTDT", get_ptdt); + + if (ret != 0) + return -1; + + libxmp_iff_set_quirk(handle, IFF_FULL_CHUNK_SIZE); + + /* Load IFF chunks */ + if (libxmp_iff_load(handle, m, f, &data) < 0) { + libxmp_iff_release(handle); + return -1; + } + + libxmp_iff_release(handle); + + /* Sanity check */ + if (m->mod.smp <= 0) { + return -1; + } + + return 0; +} + +static int ptdt_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct mod_header mh; + uint8 mod_event[4]; + + hio_read(mh.name, 20, 1, f); + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + if (hio_read(mh.order, 128, 1, f) < 1) { + D_(D_CRIT "read error at order list"); + return -1; + } + hio_read(mh.magic, 4, 1, f); + + mod->ins = 31; + mod->smp = mod->ins; + mod->chn = 4; + mod->len = mh.len; + mod->rst = mh.restart; + memcpy(mod->xxo, mh.order, 128); + + for (i = 0; i < 128; i++) { + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + } + + mod->pat++; + mod->trk = mod->chn * mod->pat; + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = 2 * mh.ins[i].size; + mod->xxs[i].lps = 2 * mh.ins[i].loop_start; + mod->xxs[i].lpe = mod->xxs[i].lps + 2 * mh.ins[i].loop_size; + mod->xxs[i].flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + mod->xxi[i].sub[0].fin = (int8)(mh.ins[i].finetune << 4); + mod->xxi[i].sub[0].vol = mh.ins[i].volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].rls = 0xfff; + + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d", + i, mod->xxi[i].name, + mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mh.ins[i].loop_size > 1 ? 'L' : ' ', + mod->xxi[i].sub[0].vol, + mod->xxi[i].sub[0].fin >> 4); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, j % 4, j / 4); + if (hio_read(mod_event, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + libxmp_decode_protracker_event(event, mod_event); + } + } + + m->period_type = PERIOD_MODRNG; + + /* Load samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (!mod->xxs[i].len) + continue; + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/ptm_load.c b/thirdparty/libxmp/src/loaders/ptm_load.c new file mode 100644 index 000000000..16804aee3 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/ptm_load.c @@ -0,0 +1,372 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +#define PTM_CH_MASK 0x1f +#define PTM_NI_FOLLOW 0x20 +#define PTM_VOL_FOLLOWS 0x80 +#define PTM_FX_FOLLOWS 0x40 + +struct ptm_file_header { + uint8 name[28]; /* Song name */ + uint8 doseof; /* 0x1a */ + uint8 vermin; /* Minor version */ + uint8 vermaj; /* Major type */ + uint8 rsvd1; /* Reserved */ + uint16 ordnum; /* Number of orders (must be even) */ + uint16 insnum; /* Number of instruments */ + uint16 patnum; /* Number of patterns */ + uint16 chnnum; /* Number of channels */ + uint16 flags; /* Flags (set to 0) */ + uint16 rsvd2; /* Reserved */ + uint32 magic; /* 'PTMF' */ + uint8 rsvd3[16]; /* Reserved */ + uint8 chset[32]; /* Channel settings */ + uint8 order[256]; /* Orders */ + uint16 patseg[128]; +}; + +struct ptm_instrument_header { + uint8 type; /* Sample type */ + uint8 dosname[12]; /* DOS file name */ + uint8 vol; /* Volume */ + uint16 c4spd; /* C4 speed */ + uint16 smpseg; /* Sample segment (not used) */ + uint32 smpofs; /* Sample offset */ + uint32 length; /* Length */ + uint32 loopbeg; /* Loop begin */ + uint32 loopend; /* Loop end */ + uint32 gusbeg; /* GUS begin address */ + uint32 guslps; /* GUS loop start address */ + uint32 guslpe; /* GUS loop end address */ + uint8 gusflg; /* GUS loop flags */ + uint8 rsvd1; /* Reserved */ + uint8 name[28]; /* Instrument name */ + uint32 magic; /* 'PTMS' */ +}; + +#define MAGIC_PTMF MAGIC4('P','T','M','F') + +static int ptm_test(HIO_HANDLE *, char *, const int); +static int ptm_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_ptm = { + "Poly Tracker", + ptm_test, + ptm_load +}; + +static int ptm_test(HIO_HANDLE *f, char *t, const int start) +{ + hio_seek(f, start + 44, SEEK_SET); + if (hio_read32b(f) != MAGIC_PTMF) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 28); + + return 0; +} + +static const int ptm_vol[] = { + 0, 5, 8, 10, 12, 14, 15, 17, 18, 20, 21, 22, 23, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 38, 39, 40, + 41, 42, 42, 43, 44, 45, 46, 46, 47, 48, 49, 49, 50, 51, 51, + 52, 53, 54, 54, 55, 56, 56, 57, 58, 58, 59, 59, 60, 61, 61, + 62, 63, 63, 64, 64 +}; + +static int ptm_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, r, i, smp_ofs[256]; + struct xmp_event *event; + struct ptm_file_header pfh; + struct ptm_instrument_header pih; + uint8 n, b; + + LOAD_INIT(); + + /* Load and convert header */ + + hio_read(pfh.name, 28, 1, f); /* Song name */ + pfh.doseof = hio_read8(f); /* 0x1a */ + pfh.vermin = hio_read8(f); /* Minor version */ + pfh.vermaj = hio_read8(f); /* Major type */ + pfh.rsvd1 = hio_read8(f); /* Reserved */ + pfh.ordnum = hio_read16l(f); /* Number of orders (must be even) */ + pfh.insnum = hio_read16l(f); /* Number of instruments */ + pfh.patnum = hio_read16l(f); /* Number of patterns */ + pfh.chnnum = hio_read16l(f); /* Number of channels */ + pfh.flags = hio_read16l(f); /* Flags (set to 0) */ + pfh.rsvd2 = hio_read16l(f); /* Reserved */ + pfh.magic = hio_read32b(f); /* 'PTMF' */ + + if (pfh.magic != MAGIC_PTMF) + return -1; + + /* Sanity check */ + if (pfh.ordnum > 256 || pfh.insnum > 255 || pfh.patnum > 128 || + pfh.chnnum > 32) { + return -1; + } + + hio_read(pfh.rsvd3, 16, 1, f); /* Reserved */ + hio_read(pfh.chset, 32, 1, f); /* Channel settings */ + hio_read(pfh.order, 256, 1, f); /* Orders */ + for (i = 0; i < 128; i++) + pfh.patseg[i] = hio_read16l(f); + + if (hio_error(f)) + return -1; + + mod->len = pfh.ordnum; + mod->ins = pfh.insnum; + mod->pat = pfh.patnum; + mod->chn = pfh.chnnum; + mod->trk = mod->pat * mod->chn; + mod->smp = mod->ins; + mod->spd = 6; + mod->bpm = 125; + memcpy(mod->xxo, pfh.order, 256); + + m->c4rate = C4_NTSC_RATE; + + libxmp_copy_adjust(mod->name, pfh.name, 28); + libxmp_set_type(m, "Poly Tracker PTM %d.%02x", pfh.vermaj, pfh.vermin); + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) { + return -1; + } + + /* Read and convert instruments and samples */ + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + pih.type = hio_read8(f); /* Sample type */ + hio_read(pih.dosname, 12, 1, f); /* DOS file name */ + pih.vol = hio_read8(f); /* Volume */ + pih.c4spd = hio_read16l(f); /* C4 speed */ + pih.smpseg = hio_read16l(f); /* Sample segment (not used) */ + pih.smpofs = hio_read32l(f); /* Sample offset */ + pih.length = hio_read32l(f); /* Length */ + pih.loopbeg = hio_read32l(f); /* Loop begin */ + pih.loopend = hio_read32l(f); /* Loop end */ + pih.gusbeg = hio_read32l(f); /* GUS begin address */ + pih.guslps = hio_read32l(f); /* GUS loop start address */ + pih.guslpe = hio_read32l(f); /* GUS loop end address */ + pih.gusflg = hio_read8(f); /* GUS loop flags */ + pih.rsvd1 = hio_read8(f); /* Reserved */ + hio_read(pih.name, 28, 1, f); /* Instrument name */ + pih.magic = hio_read32b(f); /* 'PTMS' */ + + if (hio_error(f)) { + return -1; + } + + if ((pih.type & 3) != 1) + continue; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) { + return -1; + } + + sub = &xxi->sub[0]; + + smp_ofs[i] = pih.smpofs; + xxs->len = pih.length; + xxs->lps = pih.loopbeg; + xxs->lpe = pih.loopend; + + if (mod->xxs[i].len > 0) { + mod->xxi[i].nsm = 1; + } + + xxs->flg = 0; + if (pih.type & 0x04) { + xxs->flg |= XMP_SAMPLE_LOOP; + } + if (pih.type & 0x08) { + xxs->flg |= XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR; + } + if (pih.type & 0x10) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + sub->vol = pih.vol; + sub->pan = 0x80; + sub->sid = i; + pih.magic = 0; + + libxmp_instrument_name(mod, i, pih.name, 28); + + D_(D_INFO "[%2X] %-28.28s %05x%c%05x %05x %c V%02x %5d", + i, mod->xxi[i].name, mod->xxs[i].len, + pih.type & 0x10 ? '+' : ' ', + xxs->lps, xxs->lpe, xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, pih.c4spd); + + /* Convert C4SPD to relnote/finetune */ + libxmp_c2spd_to_note(pih.c4spd, &sub->xpo, &sub->fin); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + + /* channel control to prevent infinite loop in pattern reading */ + /* addresses fuzz bug reported by Lionel Debroux in 20161223 */ + char chn_ctrl[32]; + + if (!pfh.patseg[i]) + continue; + + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + hio_seek(f, start + 16L * pfh.patseg[i], SEEK_SET); + r = 0; + + memset(chn_ctrl, 0, sizeof(chn_ctrl)); + + while (r < 64) { + + b = hio_read8(f); + if (!b) { + r++; + memset(chn_ctrl, 0, sizeof(chn_ctrl)); + continue; + } + + c = b & PTM_CH_MASK; + if (chn_ctrl[c]) { + /* uh-oh, something wrong happened */ + return -1; + } + /* mark this channel as read */ + chn_ctrl[c] = 1; + + if (c >= mod->chn) { + continue; + } + + event = &EVENT(i, c, r); + if (b & PTM_NI_FOLLOW) { + n = hio_read8(f); + switch (n) { + case 255: + n = 0; + break; /* Empty note */ + case 254: + n = XMP_KEY_OFF; + break; /* Key off */ + default: + n += 12; + } + event->note = n; + event->ins = hio_read8(f); + } + if (b & PTM_FX_FOLLOWS) { + event->fxt = hio_read8(f); + event->fxp = hio_read8(f); + + if (event->fxt > 0x17) + event->fxt = event->fxp = 0; + + switch (event->fxt) { + case 0x0e: /* Extended effect */ + if (MSN(event->fxp) == 0x8) { /* Pan set */ + event->fxt = FX_SETPAN; + event->fxp = + LSN(event->fxp) << 4; + } + break; + case 0x10: /* Set global volume */ + event->fxt = FX_GLOBALVOL; + break; + case 0x11: /* Multi retrig */ + event->fxt = FX_MULTI_RETRIG; + break; + case 0x12: /* Fine vibrato */ + event->fxt = FX_FINE_VIBRATO; + break; + case 0x13: /* Note slide down */ + event->fxt = FX_NSLIDE_DN; + break; + case 0x14: /* Note slide up */ + event->fxt = FX_NSLIDE_UP; + break; + case 0x15: /* Note slide down + retrig */ + event->fxt = FX_NSLIDE_R_DN; + break; + case 0x16: /* Note slide up + retrig */ + event->fxt = FX_NSLIDE_R_UP; + break; + case 0x17: /* Reverse sample */ + event->fxt = event->fxp = 0; + break; + } + } + if (b & PTM_VOL_FOLLOWS) { + event->vol = hio_read8(f) + 1; + } + } + } + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->smp; i++) { + if (mod->xxi[i].nsm == 0) + continue; + + if (mod->xxs[i].len == 0) + continue; + + hio_seek(f, start + smp_ofs[i], SEEK_SET); + if (libxmp_load_sample(m, f, SAMPLE_FLAG_8BDIFF, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->vol_table = ptm_vol; + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = pfh.chset[i] << 4; + + m->quirk |= QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/pw_load.c b/thirdparty/libxmp/src/loaders/pw_load.c new file mode 100644 index 000000000..38600c943 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/pw_load.c @@ -0,0 +1,202 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "mod.h" +#include "../period.h" + +#ifndef LIBXMP_NO_PROWIZARD + +#include "prowizard/prowiz.h" +#include "../tempfile.h" + +extern struct list_head *checked_format; + +static int pw_test(HIO_HANDLE *, char *, const int); +static int pw_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_pw = { + "prowizard", + pw_test, + pw_load +}; + +int pw_test_format(HIO_HANDLE *f, char *t, const int start, + struct xmp_test_info *info) +{ + const struct pw_format *format; + + format = pw_check(f, info); + + return format ? 0 : -1; +} + +static int pw_test(HIO_HANDLE *f, char *t, const int start) +{ + return pw_test_format(f, t, start, NULL); +} + +static int pw_load(struct module_data *m, HIO_HANDLE *h, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + struct mod_header mh; + uint8 mod_event[4]; + HIO_HANDLE *f; + FILE *temp; + const char *name; + char *temp_name; + int i, j; + + /* Prowizard depacking */ + + if ((temp = make_temp_file(&temp_name)) == NULL) { + goto err; + } + + if (pw_wizardry(h, temp, &name) < 0) { + fclose(temp); + goto err2; + } + + /* Module loading */ + + D_(D_INFO "loading generated module"); + if ((f = hio_open_file2(temp)) == NULL) { + goto err2; + } + + if (hio_seek(f, 0, start) < 0) { + goto err3; + } + + hio_read(mh.name, 20, 1, f); + for (i = 0; i < 31; i++) { + hio_read(mh.ins[i].name, 22, 1, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 128, 1, f); + hio_read(mh.magic, 4, 1, f); + + if (memcmp(mh.magic, "M.K.", 4)) { + goto err3; + } + + mod->ins = 31; + mod->smp = mod->ins; + mod->chn = 4; + mod->len = mh.len; + mod->rst = mh.restart; + memcpy(mod->xxo, mh.order, 128); + + for (i = 0; i < 128; i++) { + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + } + + mod->pat++; + + mod->trk = mod->chn * mod->pat; + + snprintf(mod->name, XMP_NAME_SIZE, "%s", (char *)mh.name); + snprintf(mod->type, XMP_NAME_SIZE, "%s", name); + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) { + goto err3; + } + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + goto err3; + + mod->xxs[i].len = 2 * mh.ins[i].size; + mod->xxs[i].lps = 2 * mh.ins[i].loop_start; + mod->xxs[i].lpe = mod->xxs[i].lps + 2 * mh.ins[i].loop_size; + mod->xxs[i].flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].fin = (int8) (mh.ins[i].finetune << 4); + mod->xxi[i].sub[0].vol = mh.ins[i].volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].rls = 0xfff; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + libxmp_instrument_name(mod, i, mh.ins[i].name, 22); + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d", + i, mod->xxi[i].name, mod->xxs[i].len, + mod->xxs[i].lps, mod->xxs[i].lpe, + mh.ins[i].loop_size > 1 ? 'L' : ' ', + mod->xxi[i].sub[0].vol, + mod->xxi[i].sub[0].fin >> 4); + } + + if (libxmp_init_pattern(mod) < 0) { + goto err3; + } + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + goto err3; + + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, j % 4, j / 4); + if (hio_read(mod_event, 1, 4, f) < 4) + goto err3; + libxmp_decode_protracker_event(event, mod_event); + } + } + + m->period_type = PERIOD_MODRNG; + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + for (i = 0; i < mod->smp; i++) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + goto err3; + } + + hio_close(f); + unlink_temp_file(temp_name); + return 0; + + err3: + hio_close(f); + err2: + unlink_temp_file(temp_name); + err: + return -1; +} + +#endif /* LIBXMP_NO_PROWIZARD */ diff --git a/thirdparty/libxmp/src/loaders/rtm_load.c b/thirdparty/libxmp/src/loaders/rtm_load.c new file mode 100644 index 000000000..0a2e3d189 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/rtm_load.c @@ -0,0 +1,521 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +/* Data structures from the specification of the RTM format version 1.10 by + * Arnaud Hasenfratz + */ + +struct ObjectHeader { + char id[4]; /* "RTMM", "RTND", "RTIN" or "RTSM" */ + char rc; /* 0x20 */ + char name[32]; /* object name */ + char eof; /* "\x1A" */ + uint16 version; /* version of the format (actual : 0x110) */ + uint16 headerSize; /* object header size */ +}; + +struct RTMMHeader { /* Real Tracker Music Module */ + char software[20]; /* software used for saving the module */ + char composer[32]; + uint16 flags; /* song flags */ + /* bit 0 : linear table, + bit 1 : track names present */ + uint8 ntrack; /* number of tracks */ + uint8 ninstr; /* number of instruments */ + uint16 nposition; /* number of positions */ + uint16 npattern; /* number of patterns */ + uint8 speed; /* initial speed */ + uint8 tempo; /* initial tempo */ + char panning[32]; /* initial pannings (for S3M compatibility) */ + uint32 extraDataSize; /* length of data after the header */ + +/* version 1.12 */ + char originalName[32]; +}; + +struct RTNDHeader { /* Real Tracker Note Data */ + uint16 flags; /* Always 1 */ + uint8 ntrack; + uint16 nrows; + uint32 datasize; /* Size of packed data */ +}; + +struct EnvelopePoint { + long x; + long y; +}; + +struct Envelope { + uint8 npoint; + struct EnvelopePoint point[12]; + uint8 sustain; + uint8 loopstart; + uint8 loopend; + uint16 flags; /* bit 0 : enable envelope, + bit 1 : sustain, bit 2 : loop */ +}; + +struct RTINHeader { /* Real Tracker Instrument */ + uint8 nsample; + uint16 flags; /* bit 0 : default panning enabled + bit 1 : mute samples */ + uint8 table[120]; /* sample number for each note */ + struct Envelope volumeEnv; + struct Envelope panningEnv; + char vibflg; /* vibrato type */ + char vibsweep; /* vibrato sweep */ + char vibdepth; /* vibrato depth */ + char vibrate; /* vibrato rate */ + uint16 volfade; + +/* version 1.10 */ + uint8 midiPort; + uint8 midiChannel; + uint8 midiProgram; + uint8 midiEnable; + +/* version 1.12 */ + char midiTranspose; + uint8 midiBenderRange; + uint8 midiBaseVolume; + char midiUseVelocity; +}; + +struct RTSMHeader { /* Real Tracker Sample */ + uint16 flags; /* bit 1 : 16 bits, + bit 2 : delta encoded (always) */ + uint8 basevolume; + uint8 defaultvolume; + uint32 length; + uint8 loop; /* =0:no loop, =1:forward loop, + =2:bi-directional loop */ + uint8 reserved[3]; + uint32 loopbegin; + uint32 loopend; + uint32 basefreq; + uint8 basenote; + char panning; /* Panning from -64 to 64 */ +}; + + +static int rtm_test(HIO_HANDLE *, char *, const int); +static int rtm_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_rtm = { + "Real Tracker", + rtm_test, + rtm_load +}; + +static int rtm_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[4]; + + if (hio_read(buf, 1, 4, f) < 4) + return -1; + if (memcmp(buf, "RTMM", 4)) + return -1; + + if (hio_read8(f) != 0x20) + return -1; + + libxmp_read_title(f, t, 32); + + return 0; +} + + +#define MAX_SAMP 1024 + +static int read_object_header(HIO_HANDLE *f, struct ObjectHeader *h, const char *id) +{ + hio_read(h->id, 4, 1, f); + D_(D_WARN "object id: %02x %02x %02x %02x", h->id[0], + h->id[1], h->id[2], h->id[3]); + + if (memcmp(id, h->id, 4)) + return -1; + + h->rc = hio_read8(f); + if (h->rc != 0x20) + return -1; + if (hio_read(h->name, 1, 32, f) != 32) + return -1; + h->eof = hio_read8(f); + h->version = hio_read16l(f); + h->headerSize = hio_read16l(f); + D_(D_INFO "object %-4.4s (%d)", h->id, h->headerSize); + + return 0; +} + + +static int rtm_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, r; + struct xmp_event *event; + struct ObjectHeader oh; + struct RTMMHeader rh; + struct RTNDHeader rp; + struct RTINHeader ri; + struct RTSMHeader rs; + int offset, smpnum, version; + char tracker_name[21], composer[33]; + + LOAD_INIT(); + + if (read_object_header(f, &oh, "RTMM") < 0) + return -1; + + version = oh.version; + + hio_read(tracker_name, 1, 20, f); + tracker_name[20] = 0; + hio_read(composer, 1, 32, f); + composer[32] = 0; + rh.flags = hio_read16l(f); /* bit 0: linear table, bit 1: track names */ + rh.ntrack = hio_read8(f); + rh.ninstr = hio_read8(f); + rh.nposition = hio_read16l(f); + rh.npattern = hio_read16l(f); + rh.speed = hio_read8(f); + rh.tempo = hio_read8(f); + hio_read(rh.panning, 32, 1, f); + rh.extraDataSize = hio_read32l(f); + + /* Sanity check */ + if (hio_error(f) || rh.nposition > 255 || rh.ntrack > 32 || rh.npattern > 255) { + return -1; + } + + if (version >= 0x0112) + hio_seek(f, 32, SEEK_CUR); /* skip original name */ + + for (i = 0; i < rh.nposition; i++) { + mod->xxo[i] = hio_read16l(f); + if (mod->xxo[i] >= rh.npattern) { + return -1; + } + } + + strncpy(mod->name, oh.name, 32); + mod->name[32] = '\0'; + snprintf(mod->type, XMP_NAME_SIZE, "%s RTM %x.%02x", + tracker_name, version >> 8, version & 0xff); + /* strncpy(m->author, composer, XMP_NAME_SIZE); */ + + mod->len = rh.nposition; + mod->pat = rh.npattern; + mod->chn = rh.ntrack; + mod->trk = mod->chn * mod->pat; + mod->ins = rh.ninstr; + mod->spd = rh.speed; + mod->bpm = rh.tempo; + + m->c4rate = C4_NTSC_RATE; + m->period_type = rh.flags & 0x01 ? PERIOD_LINEAR : PERIOD_AMIGA; + + MODULE_INFO(); + + for (i = 0; i < mod->chn; i++) + mod->xxc[i].pan = rh.panning[i] & 0xff; + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + offset = 42 + oh.headerSize + rh.extraDataSize; + + for (i = 0; i < mod->pat; i++) { + uint8 c; + + hio_seek(f, start + offset, SEEK_SET); + + if (read_object_header(f, &oh, "RTND") < 0) { + D_(D_CRIT "Error reading pattern %d", i); + return -1; + } + + rp.flags = hio_read16l(f); + rp.ntrack = hio_read8(f); + rp.nrows = hio_read16l(f); + rp.datasize = hio_read32l(f); + + /* Sanity check */ + if (rp.ntrack > rh.ntrack || rp.nrows > 256) { + return -1; + } + + offset += 42 + oh.headerSize + rp.datasize; + + if (libxmp_alloc_pattern_tracks(mod, i, rp.nrows) < 0) + return -1; + + for (r = 0; r < rp.nrows; r++) { + for (j = 0; /*j < rp.ntrack */; j++) { + + c = hio_read8(f); + if (c == 0) /* next row */ + break; + + /* Sanity check */ + if (j >= rp.ntrack) { + return -1; + } + + event = &EVENT(i, j, r); + + if (c & 0x01) { /* set track */ + j = hio_read8(f); + + /* Sanity check */ + if (j >= rp.ntrack) { + return -1; + } + + event = &EVENT(i, j, r); + } + if (c & 0x02) { /* read note */ + event->note = hio_read8(f) + 1; + if (event->note == 0xff) { + event->note = XMP_KEY_OFF; + } else { + event->note += 12; + } + } + if (c & 0x04) /* read instrument */ + event->ins = hio_read8(f); + if (c & 0x08) /* read effect */ + event->fxt = hio_read8(f); + if (c & 0x10) /* read parameter */ + event->fxp = hio_read8(f); + if (c & 0x20) /* read effect 2 */ + event->f2t = hio_read8(f); + if (c & 0x40) /* read parameter 2 */ + event->f2p = hio_read8(f); + } + } + } + + /* + * load instruments + */ + + D_(D_INFO "Instruments: %d", mod->ins); + + hio_seek(f, start + offset, SEEK_SET); + + /* ESTIMATED value! We don't know the actual value at this point */ + mod->smp = MAX_SAMP; + + if (libxmp_init_instrument(m) < 0) + return -1; + + smpnum = 0; + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + + if (read_object_header(f, &oh, "RTIN") < 0) { + D_(D_CRIT "Error reading instrument %d", i); + return -1; + } + + libxmp_instrument_name(mod, i, (uint8 *)oh.name, 32); + + if (oh.headerSize == 0) { + D_(D_INFO "[%2X] %-26.26s %2d ", i, + xxi->name, xxi->nsm); + ri.nsample = 0; + continue; + } + + ri.nsample = hio_read8(f); + ri.flags = hio_read16l(f); /* bit 0 : default panning enabled */ + if (hio_read(ri.table, 1, 120, f) != 120) + return -1; + + ri.volumeEnv.npoint = hio_read8(f); + + /* Sanity check */ + if (ri.volumeEnv.npoint >= 12) + return -1; + + for (j = 0; j < 12; j++) { + ri.volumeEnv.point[j].x = hio_read32l(f); + ri.volumeEnv.point[j].y = hio_read32l(f); + } + ri.volumeEnv.sustain = hio_read8(f); + ri.volumeEnv.loopstart = hio_read8(f); + ri.volumeEnv.loopend = hio_read8(f); + ri.volumeEnv.flags = hio_read16l(f); /* bit 0:enable 1:sus 2:loop */ + + ri.panningEnv.npoint = hio_read8(f); + + /* Sanity check */ + if (ri.panningEnv.npoint >= 12) + return -1; + + for (j = 0; j < 12; j++) { + ri.panningEnv.point[j].x = hio_read32l(f); + ri.panningEnv.point[j].y = hio_read32l(f); + } + ri.panningEnv.sustain = hio_read8(f); + ri.panningEnv.loopstart = hio_read8(f); + ri.panningEnv.loopend = hio_read8(f); + ri.panningEnv.flags = hio_read16l(f); + + ri.vibflg = hio_read8(f); + ri.vibsweep = hio_read8(f); + ri.vibdepth = hio_read8(f); + ri.vibrate = hio_read8(f); + ri.volfade = hio_read16l(f); + + if (version >= 0x0110) { + ri.midiPort = hio_read8(f); + ri.midiChannel = hio_read8(f); + ri.midiProgram = hio_read8(f); + ri.midiEnable = hio_read8(f); + } + if (version >= 0x0112) { + ri.midiTranspose = hio_read8(f); + ri.midiBenderRange = hio_read8(f); + ri.midiBaseVolume = hio_read8(f); + ri.midiUseVelocity = hio_read8(f); + } + + xxi->nsm = ri.nsample; + + D_(D_INFO "[%2X] %-26.26s %2d", i, xxi->name, xxi->nsm); + + if (xxi->nsm > 16) + xxi->nsm = 16; + + if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0) + return -1; + + for (j = 0; j < 120; j++) + xxi->map[j].ins = ri.table[j]; + + /* Envelope */ + xxi->rls = ri.volfade; + xxi->aei.npt = ri.volumeEnv.npoint; + xxi->aei.sus = ri.volumeEnv.sustain; + xxi->aei.lps = ri.volumeEnv.loopstart; + xxi->aei.lpe = ri.volumeEnv.loopend; + xxi->aei.flg = ri.volumeEnv.flags; + xxi->pei.npt = ri.panningEnv.npoint; + xxi->pei.sus = ri.panningEnv.sustain; + xxi->pei.lps = ri.panningEnv.loopstart; + xxi->pei.lpe = ri.panningEnv.loopend; + xxi->pei.flg = ri.panningEnv.flags; + + for (j = 0; j < xxi->aei.npt; j++) { + xxi->aei.data[j * 2 + 0] = ri.volumeEnv.point[j].x; + xxi->aei.data[j * 2 + 1] = ri.volumeEnv.point[j].y / 2; + } + for (j = 0; j < xxi->pei.npt; j++) { + xxi->pei.data[j * 2 + 0] = ri.panningEnv.point[j].x; + xxi->pei.data[j * 2 + 1] = 32 + ri.panningEnv.point[j].y / 2; + } + + /* For each sample */ + for (j = 0; j < xxi->nsm; j++, smpnum++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs; + + if (read_object_header(f, &oh, "RTSM") < 0) { + D_(D_CRIT "Error reading sample %d", j); + return -1; + } + + rs.flags = hio_read16l(f); + rs.basevolume = hio_read8(f); + rs.defaultvolume = hio_read8(f); + rs.length = hio_read32l(f); + rs.loop = hio_read32l(f); + rs.loopbegin = hio_read32l(f); + rs.loopend = hio_read32l(f); + rs.basefreq = hio_read32l(f); + rs.basenote = hio_read8(f); + rs.panning = hio_read8(f); + + libxmp_c2spd_to_note(rs.basefreq, &sub->xpo, &sub->fin); + sub->xpo += 48 - rs.basenote; + sub->vol = rs.defaultvolume * rs.basevolume / 0x40; + sub->pan = 0x80 + rs.panning * 2; + sub->vwf = ri.vibflg; + sub->vde = ri.vibdepth << 2; + sub->vra = ri.vibrate; + sub->vsw = ri.vibsweep; + sub->sid = smpnum; + + if (smpnum >= mod->smp) { + if (libxmp_realloc_samples(m, mod->smp * 3 / 2) < 0) + return -1; + } + xxs = &mod->xxs[smpnum]; + + libxmp_copy_adjust(xxs->name, (uint8 *)oh.name, 31); + + xxs->len = rs.length; + xxs->lps = rs.loopbegin; + xxs->lpe = rs.loopend; + + xxs->flg = 0; + if (rs.flags & 0x02) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + xxs->flg |= rs.loop & 0x03 ? XMP_SAMPLE_LOOP : 0; + xxs->flg |= rs.loop == 2 ? XMP_SAMPLE_LOOP_BIDIR : 0; + + D_(D_INFO " [%1x] %05x%c%05x %05x %c " + "V%02x F%+04d P%02x R%+03d", + j, xxs->len, + xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', + xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, sub->fin, sub->pan, sub->xpo); + + if (libxmp_load_sample(m, f, SAMPLE_FLAG_DIFF, xxs, NULL) < 0) + return -1; + } + } + + /* Final sample number adjustment */ + if (libxmp_realloc_samples(m, smpnum) < 0) + return -1; + + m->quirk |= QUIRKS_FT2; + m->read_event_type = READ_EVENT_FT2; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/s3m.h b/thirdparty/libxmp/src/loaders/s3m.h new file mode 100644 index 000000000..9b88f1b92 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/s3m.h @@ -0,0 +1,126 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LIBXMP_LOADERS_S3M_H +#define LIBXMP_LOADERS_S3M_H + +/* S3M packed pattern macros */ +#define S3M_EOR 0 /* End of row */ +#define S3M_CH_MASK 0x1f /* Channel */ +#define S3M_NI_FOLLOW 0x20 /* Note and instrument follow */ +#define S3M_VOL_FOLLOWS 0x40 /* Volume follows */ +#define S3M_FX_FOLLOWS 0x80 /* Effect and parameter follow */ + +/* S3M mix volume macros */ +#define S3M_MV_VOLUME 0x7f /* Module mix volume, typically 16 to 127 */ +#define S3M_MV_STEREO 0x80 /* Module is stereo if set, otherwise mono */ + +/* S3M channel info macros */ +#define S3M_CH_ON 0x80 /* Psi says it's bit 8, I'll assume bit 7 */ +#define S3M_CH_OFF 0xff +#define S3M_CH_NUMBER 0x1f +#define S3M_CH_RIGHT 0x08 +#define S3M_CH_ADLIB 0x10 + +/* S3M channel pan macros */ +#define S3M_PAN_SET 0x20 +#define S3M_PAN_MASK 0x0f + +/* S3M flags */ +#define S3M_ST2_VIB 0x01 /* Not recognized */ +#define S3M_ST2_TEMPO 0x02 /* Not recognized */ +#define S3M_AMIGA_SLIDE 0x04 /* Not recognized */ +#define S3M_VOL_OPT 0x08 /* Not recognized */ +#define S3M_AMIGA_RANGE 0x10 +#define S3M_SB_FILTER 0x20 /* Not recognized */ +#define S3M_ST300_VOLS 0x40 +#define S3M_CUSTOM_DATA 0x80 /* Not recognized */ + +/* S3M Adlib instrument types */ +#define S3M_INST_SAMPLE 0x01 +#define S3M_INST_AMEL 0x02 +#define S3M_INST_ABD 0x03 +#define S3M_INST_ASNARE 0x04 +#define S3M_INST_ATOM 0x05 +#define S3M_INST_ACYM 0x06 +#define S3M_INST_AHIHAT 0x07 + +struct s3m_file_header { + uint8 name[28]; /* Song name */ + uint8 doseof; /* 0x1a */ + uint8 type; /* File type */ + uint8 rsvd1[2]; /* Reserved */ + uint16 ordnum; /* Number of orders (must be even) */ + uint16 insnum; /* Number of instruments */ + uint16 patnum; /* Number of patterns */ + uint16 flags; /* Flags */ + uint16 version; /* Tracker ID and version */ + uint16 ffi; /* File format information */ + uint32 magic; /* 'SCRM' */ + uint8 gv; /* Global volume */ + uint8 is; /* Initial speed */ + uint8 it; /* Initial tempo */ + uint8 mv; /* Master volume */ + uint8 uc; /* Ultra click removal */ + uint8 dp; /* Default pan positions if 0xfc */ + uint8 rsvd2[8]; /* Reserved */ + uint16 special; /* Ptr to special custom data */ + uint8 chset[32]; /* Channel settings */ +}; + +struct s3m_instrument_header { + uint8 dosname[12]; /* DOS file name */ + uint8 memseg_hi; /* High byte of sample pointer */ + uint16 memseg; /* Pointer to sample data */ + uint32 length; /* Length */ + uint32 loopbeg; /* Loop begin */ + uint32 loopend; /* Loop end */ + uint8 vol; /* Volume */ + uint8 rsvd1; /* Reserved */ + uint8 pack; /* Packing type (not used) */ + uint8 flags; /* Loop/stereo/16bit samples flags */ + uint16 c2spd; /* C 4 speed */ + uint16 rsvd2; /* Reserved */ + uint8 rsvd3[4]; /* Reserved */ + uint16 int_gp; /* Internal - GUS pointer */ + uint16 int_512; /* Internal - SB pointer */ + uint32 int_last; /* Internal - SB index */ + uint8 name[28]; /* Instrument name */ + uint32 magic; /* 'SCRS' */ +}; + +#ifndef LIBXMP_CORE_PLAYER +struct s3m_adlib_header { + uint8 dosname[12]; /* DOS file name */ + uint8 rsvd1[3]; /* 0x00 0x00 0x00 */ + uint8 reg[12]; /* Adlib registers */ + uint8 vol; + uint8 dsk; + uint8 rsvd2[2]; + uint16 c2spd; /* C 4 speed */ + uint16 rsvd3; /* Reserved */ + uint8 rsvd4[12]; /* Reserved */ + uint8 name[28]; /* Instrument name */ + uint32 magic; /* 'SCRI' */ +}; +#endif +#endif /* LIBXMP_LOADERS_S3M_H */ diff --git a/thirdparty/libxmp/src/loaders/s3m_load.c b/thirdparty/libxmp/src/loaders/s3m_load.c new file mode 100644 index 000000000..2cd7d5b5f --- /dev/null +++ b/thirdparty/libxmp/src/loaders/s3m_load.c @@ -0,0 +1,666 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Tue, 30 Jun 1998 20:23:11 +0200 + * Reported by John v/d Kamp : + * I have this song from Purple Motion called wcharts.s3m, the global + * volume was set to 0, creating a devide by 0 error in xmp. There should + * be an extra test if it's 0 or not. + * + * Claudio's fix: global volume ignored + */ + +/* + * Sat, 29 Aug 1998 18:50:43 -0500 (CDT) + * Reported by Joel Jordan : + * S3M files support tempos outside the ranges defined by xmp (that is, + * the MOD/XM tempo ranges). S3M's can have tempos from 0 to 255 and speeds + * from 0 to 255 as well, since these are handled with separate effects + * unlike the MOD format. This becomes an issue in such songs as Skaven's + * "Catch that Goblin", which uses speeds above 0x1f. + * + * Claudio's fix: FX_S3M_SPEED added. S3M supports speeds from 0 to 255 and + * tempos from 32 to 255 (S3M speed == xmp tempo, S3M tempo == xmp BPM). + */ + +/* Wed, 21 Oct 1998 15:03:44 -0500 Geoff Reedy + * It appears that xmp has issues loading/playing a specific instrument + * used in LUCCA.S3M. + * (Fixed by Hipolito in xmp-2.0.0dev34) + */ + +/* + * From http://code.pui.ch/2007/02/18/turn-demoscene-modules-into-mp3s/ + * The only flaw I noticed [in xmp] is a problem with portamento in Purple + * Motion's second reality soundtrack (1:06-1:17) + * + * Claudio's note: that's a dissonant beating between channels 6 and 7 + * starting at pos12, caused by pitchbending effect F25. + */ + +#include "loader.h" +#include "s3m.h" +#include "../period.h" + +#define MAGIC_SCRM MAGIC4('S','C','R','M') +#define MAGIC_SCRI MAGIC4('S','C','R','I') +#define MAGIC_SCRS MAGIC4('S','C','R','S') + +static int s3m_test(HIO_HANDLE *, char *, const int); +static int s3m_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_s3m = { + "Scream Tracker 3", + s3m_test, + s3m_load +}; + +static int s3m_test(HIO_HANDLE *f, char *t, const int start) +{ + hio_seek(f, start + 44, SEEK_SET); + if (hio_read32b(f) != MAGIC_SCRM) + return -1; + + hio_seek(f, start + 29, SEEK_SET); + if (hio_read8(f) != 0x10) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 28); + + return 0; +} + +#define NONE 0xff +#define FX_S3M_EXTENDED 0xfe + +/* Effect conversion table */ +static const uint8 fx[27] = { + NONE, + FX_S3M_SPEED, /* Axx Set speed to xx (the default is 06) */ + FX_JUMP, /* Bxx Jump to order xx (hexadecimal) */ + FX_BREAK, /* Cxx Break pattern to row xx (decimal) */ + FX_VOLSLIDE, /* Dxy Volume slide down by y/up by x */ + FX_PORTA_DN, /* Exx Slide down by xx */ + FX_PORTA_UP, /* Fxx Slide up by xx */ + FX_TONEPORTA, /* Gxx Tone portamento with speed xx */ + FX_VIBRATO, /* Hxy Vibrato with speed x and depth y */ + FX_TREMOR, /* Ixy Tremor with ontime x and offtime y */ + FX_S3M_ARPEGGIO, /* Jxy Arpeggio with halfnote additions */ + FX_VIBRA_VSLIDE, /* Kxy Dual command: H00 and Dxy */ + FX_TONE_VSLIDE, /* Lxy Dual command: G00 and Dxy */ + NONE, + NONE, + FX_OFFSET, /* Oxy Set sample offset */ + NONE, + FX_MULTI_RETRIG, /* Qxy Retrig (+volumeslide) note */ + FX_TREMOLO, /* Rxy Tremolo with speed x and depth y */ + FX_S3M_EXTENDED, /* Sxx (misc effects) */ + FX_S3M_BPM, /* Txx Tempo = xx (hex) */ + FX_FINE_VIBRATO, /* Uxx Fine vibrato */ + FX_GLOBALVOL, /* Vxx Set global volume */ + NONE, + FX_SETPAN, /* Xxx Set pan */ + NONE, + NONE +}; + +/* Effect translation */ +static void xlat_fx(int c, struct xmp_event *e) +{ + uint8 h = MSN(e->fxp), l = LSN(e->fxp); + + if (e->fxt >= ARRAY_SIZE(fx)) { + D_(D_WARN "invalid effect %02x", e->fxt); + e->fxt = e->fxp = 0; + return; + } + + switch (e->fxt = fx[e->fxt]) { + case FX_S3M_BPM: + if (e->fxp < 0x20) { + e->fxp = e->fxt = 0; + } + break; + case FX_S3M_EXTENDED: /* Extended effects */ + e->fxt = FX_EXTENDED; + switch (h) { + case 0x1: /* Glissando */ + e->fxp = LSN(e->fxp) | (EX_GLISS << 4); + break; + case 0x2: /* Finetune */ + e->fxp = + ((LSN(e->fxp) - 8) & 0x0f) | (EX_FINETUNE << 4); + break; + case 0x3: /* Vibrato wave */ + e->fxp = LSN(e->fxp) | (EX_VIBRATO_WF << 4); + break; + case 0x4: /* Tremolo wave */ + e->fxp = LSN(e->fxp) | (EX_TREMOLO_WF << 4); + break; + case 0x5: + case 0x6: + case 0x7: + case 0x9: + case 0xa: /* Ignore */ + e->fxt = e->fxp = 0; + break; + case 0x8: /* Set pan */ + e->fxt = FX_SETPAN; + e->fxp = l << 4; + break; + case 0xb: /* Pattern loop */ + e->fxp = LSN(e->fxp) | (EX_PATTERN_LOOP << 4); + break; + case 0xc: + if (!l) + e->fxt = e->fxp = 0; + } + break; + case FX_SETPAN: + /* Saga Musix says: "The X effect in S3M files is not + * exclusive to IT and clones. You will find tons of S3Ms made + * with ST3 itself using this effect (and relying on an + * external player being used). X in S3M also behaves + * differently than in IT, which your code does not seem to + * handle: X00 - X80 is left... right, XA4 is surround (like + * S91 in IT), other values are not supposed to do anything. + */ + if (e->fxp == 0xa4) { + // surround + e->fxt = FX_SURROUND; + e->fxp = 1; + } else { + int pan = ((int)e->fxp) << 1; + if (pan > 0xff) { + pan = 0xff; + } + e->fxp = pan; + } + break; + case NONE: /* No effect */ + e->fxt = e->fxp = 0; + break; + } +} + +static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, r, i; + struct xmp_event *event = 0, dummy; + struct s3m_file_header sfh; + struct s3m_instrument_header sih; +#ifndef LIBXMP_CORE_PLAYER + struct s3m_adlib_header sah; + char tracker_name[40]; +#endif + int pat_len; + uint8 n, b; + uint16 *pp_ins; /* Parapointers to instruments */ + uint16 *pp_pat; /* Parapointers to patterns */ + int stereo; + int ret; + uint8 buf[96] + + LOAD_INIT(); + + if (hio_read(buf, 1, 96, f) != 96) { + goto err; + } + + memcpy(sfh.name, buf, 28); /* Song name */ + sfh.type = buf[30]; /* File type */ + sfh.ordnum = readmem16l(buf + 32); /* Number of orders (must be even) */ + sfh.insnum = readmem16l(buf + 34); /* Number of instruments */ + sfh.patnum = readmem16l(buf + 36); /* Number of patterns */ + sfh.flags = readmem16l(buf + 38); /* Flags */ + sfh.version = readmem16l(buf + 40); /* Tracker ID and version */ + sfh.ffi = readmem16l(buf + 42); /* File format information */ + + /* Sanity check */ + if (sfh.ffi != 1 && sfh.ffi != 2) { + goto err; + } + if (sfh.ordnum > 255 || sfh.insnum > 255 || sfh.patnum > 255) { + goto err; + } + + sfh.magic = readmem32b(buf + 44); /* 'SCRM' */ + sfh.gv = buf[48]; /* Global volume */ + sfh.is = buf[49]; /* Initial speed */ + sfh.it = buf[50]; /* Initial tempo */ + sfh.mv = buf[51]; /* Master volume */ + sfh.uc = buf[52]; /* Ultra click removal */ + sfh.dp = buf[53]; /* Default pan positions if 0xfc */ + memcpy(sfh.rsvd2, buf + 54, 8); /* Reserved */ + sfh.special = readmem16l(buf + 62); /* Ptr to special custom data */ + memcpy(sfh.chset, buf + 64, 32); /* Channel settings */ + + if (sfh.magic != MAGIC_SCRM) { + goto err; + } + + libxmp_copy_adjust(mod->name, sfh.name, 28); + + pp_ins = (uint16 *) calloc(sfh.insnum, sizeof(uint16)); + if (pp_ins == NULL) { + goto err; + } + + pp_pat = (uint16 *) calloc(sfh.patnum, sizeof(uint16)); + if (pp_pat == NULL) { + goto err2; + } + + if (sfh.flags & S3M_AMIGA_RANGE) { + m->period_type = PERIOD_MODRNG; + } + if (sfh.flags & S3M_ST300_VOLS) { + m->quirk |= QUIRK_VSALL; + } + /* m->volbase = 4096 / sfh.gv; */ + mod->spd = sfh.is; + mod->bpm = sfh.it; + mod->chn = 0; + + /* Mix volume and stereo flag conversion (reported by Saga Musix). + * 1) Old format uses mix volume 0-7, and the stereo flag is 0x10. + * 2) Newer ST3s unconditionally convert MV 0x02 and 0x12 to 0x20. + */ + m->mvolbase = 48; + + if (sfh.ffi == 1) { + m->mvol = ((sfh.mv & 0xf) + 1) * 0x10; + stereo = sfh.mv & 0x10; + CLAMP(m->mvol, 0x10, 0x7f); + + } else if (sfh.mv == 0x02 || sfh.mv == 0x12) { + m->mvol = 0x20; + stereo = sfh.mv & 0x10; + + } else { + m->mvol = sfh.mv & S3M_MV_VOLUME; + stereo = sfh.mv & S3M_MV_STEREO; + + if (m->mvol == 0) { + m->mvol = 48; /* Default is 48 */ + } else if (m->mvol < 16) { + m->mvol = 16; /* Minimum is 16 */ + } + } + + /* "Note that in stereo, the mastermul is internally multiplied by + * 11/8 inside the player since there is generally more room in the + * output stream." Do the inverse to affect fewer modules. */ + if (!stereo) { + m->mvol = m->mvol * 8 / 11; + } + + for (i = 0; i < 32; i++) { + int x; + if (sfh.chset[i] == S3M_CH_OFF) + continue; + + mod->chn = i + 1; + + x = sfh.chset[i] & S3M_CH_NUMBER; + if (stereo && x < S3M_CH_ADLIB) { + mod->xxc[i].pan = x < S3M_CH_RIGHT ? 0x30 : 0xc0; + } else { + mod->xxc[i].pan = 0x80; + } + } + + if (sfh.ordnum <= XMP_MAX_MOD_LENGTH) { + mod->len = sfh.ordnum; + if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) { + goto err3; + } + } else { + mod->len = XMP_MAX_MOD_LENGTH; + if (hio_read(mod->xxo, 1, mod->len, f) != mod->len) { + goto err3; + } + if (hio_seek(f, sfh.ordnum - XMP_MAX_MOD_LENGTH, SEEK_CUR) < 0) { + goto err3; + } + } + + /* Don't trust sfh.patnum */ + mod->pat = -1; + for (i = 0; i < mod->len; ++i) { + if (mod->xxo[i] < 0xfe && mod->xxo[i] > mod->pat) { + mod->pat = mod->xxo[i]; + } + } + mod->pat++; + if (mod->pat > sfh.patnum) { + mod->pat = sfh.patnum; + } + if (mod->pat == 0) { + goto err3; + } + + mod->trk = mod->pat * mod->chn; + /* Load and convert header */ + mod->ins = sfh.insnum; + mod->smp = mod->ins; + + for (i = 0; i < sfh.insnum; i++) { + pp_ins[i] = hio_read16l(f); + } + + for (i = 0; i < sfh.patnum; i++) { + pp_pat[i] = hio_read16l(f); + } + + /* Default pan positions */ + + for (i = 0, sfh.dp -= 0xfc; !sfh.dp /* && n */ && (i < 32); i++) { + uint8 x = hio_read8(f); + if (x & S3M_PAN_SET) { + mod->xxc[i].pan = (x << 4) & 0xff; + } + } + + m->c4rate = C4_NTSC_RATE; + + if (sfh.version == 0x1300) { + m->quirk |= QUIRK_VSALL; + } + +#ifndef LIBXMP_CORE_PLAYER + switch (sfh.version >> 12) { + case 1: + snprintf(tracker_name, 40, "Scream Tracker %d.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff); + m->quirk |= QUIRK_ST3BUGS; + break; + case 2: + snprintf(tracker_name, 40, "Imago Orpheus %d.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff); + break; + case 3: + if (sfh.version == 0x3216) { + strcpy(tracker_name, "Impulse Tracker 2.14v3"); + } else if (sfh.version == 0x3217) { + strcpy(tracker_name, "Impulse Tracker 2.14v5"); + } else { + snprintf(tracker_name, 40, "Impulse Tracker %d.%02x", + (sfh.version & 0x0f00) >> 8, + sfh.version & 0xff); + } + break; + case 5: + if (sfh.version == 0x5447) { + strcpy(tracker_name, "Graoumf Tracker"); + } else if (sfh.rsvd2[0] || sfh.rsvd2[1]) { + snprintf(tracker_name, 40, "OpenMPT %d.%02x.%02x.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff, sfh.rsvd2[1], sfh.rsvd2[0]); + } else { + snprintf(tracker_name, 40, "OpenMPT %d.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff); + } + m->quirk |= QUIRK_ST3BUGS; + break; + case 4: + if (sfh.version != 0x4100) { + libxmp_schism_tracker_string(tracker_name, 40, + (sfh.version & 0x0fff), sfh.rsvd2[0] | (sfh.rsvd2[1] << 8)); + break; + } + /* fall through */ + case 6: + snprintf(tracker_name, 40, "BeRoTracker %d.%02x", + (sfh.version & 0x0f00) >> 8, sfh.version & 0xff); + break; + default: + snprintf(tracker_name, 40, "unknown (%04x)", sfh.version); + } + + libxmp_set_type(m, "%s S3M", tracker_name); +#else + libxmp_set_type(m, "Scream Tracker 3"); + m->quirk |= QUIRK_ST3BUGS; +#endif + + MODULE_INFO(); + + if (libxmp_init_pattern(mod) < 0) + goto err3; + + /* Read patterns */ + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + goto err3; + + if (pp_pat[i] == 0) + continue; + + hio_seek(f, start + pp_pat[i] * 16, SEEK_SET); + r = 0; + pat_len = hio_read16l(f) - 2; + + while (pat_len >= 0 && r < mod->xxp[i]->rows) { + b = hio_read8(f); + + if (hio_error(f)) { + goto err3; + } + + if (b == S3M_EOR) { + r++; + continue; + } + + c = b & S3M_CH_MASK; + event = c >= mod->chn ? &dummy : &EVENT(i, c, r); + + if (b & S3M_NI_FOLLOW) { + switch (n = hio_read8(f)) { + case 255: + n = 0; + break; /* Empty note */ + case 254: + n = XMP_KEY_OFF; + break; /* Key off */ + default: + n = 13 + 12 * MSN(n) + LSN(n); + } + event->note = n; + event->ins = hio_read8(f); + pat_len -= 2; + } + + if (b & S3M_VOL_FOLLOWS) { + event->vol = hio_read8(f) + 1; + pat_len--; + } + + if (b & S3M_FX_FOLLOWS) { + event->fxt = hio_read8(f); + event->fxp = hio_read8(f); + xlat_fx(c, event); + + pat_len -= 2; + } + } + } + + D_(D_INFO "Stereo enabled: %s", stereo ? "yes" : "no"); + D_(D_INFO "Pan settings: %s", sfh.dp ? "no" : "yes"); + + if (libxmp_init_instrument(m) < 0) + goto err3; + + /* Read and convert instruments and samples */ + + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + int load_sample_flags; + uint32 sample_segment; + + xxi->sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument)); + if (xxi->sub == NULL) { + goto err3; + } + + sub = &xxi->sub[0]; + + hio_seek(f, start + pp_ins[i] * 16, SEEK_SET); + sub->pan = 0x80; + sub->sid = i; + + if (hio_read(buf, 1, 80, f) != 80) { + goto err3; + } + + if (buf[0] >= 2) { +#ifndef LIBXMP_CORE_PLAYER + /* OPL2 FM instrument */ + + memcpy(sah.dosname, buf + 1, 12); /* DOS file name */ + memcpy(sah.reg, buf + 16, 12); /* Adlib registers */ + sah.vol = buf[28]; + sah.dsk = buf[29]; + sah.c2spd = readmem16l(buf + 32); /* C4 speed */ + memcpy(sah.name, buf + 48, 28); /* Instrument name */ + sah.magic = readmem32b(buf + 76); /* 'SCRI' */ + + if (sah.magic != MAGIC_SCRI) { + D_(D_CRIT "error: FM instrument magic"); + goto err3; + } + sah.magic = 0; + + libxmp_instrument_name(mod, i, sah.name, 28); + + xxi->nsm = 1; + sub->vol = sah.vol; + libxmp_c2spd_to_note(sah.c2spd, &sub->xpo, &sub->fin); + sub->xpo += 12; + ret = libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs, (char *)sah.reg); + if (ret < 0) + goto err3; + + D_(D_INFO "[%2X] %-28.28s", i, xxi->name); + + continue; +#else + goto err3; +#endif + } + + memcpy(sih.dosname, buf + 1, 12); /* DOS file name */ + sih.memseg_hi = buf[13]; /* High byte of sample pointer */ + sih.memseg = readmem16l(buf + 14); /* Pointer to sample data */ + sih.length = readmem32l(buf + 16); /* Length */ + +#if 0 + /* ST3 limit */ + if ((sfh.version >> 12) == 1 && sih.length > 64000) + sih.length = 64000; +#endif + + if (sih.length > MAX_SAMPLE_SIZE) { + goto err3; + } + + sih.loopbeg = readmem32l(buf + 20); /* Loop begin */ + sih.loopend = readmem32l(buf + 24); /* Loop end */ + sih.vol = buf[28]; /* Volume */ + sih.pack = buf[30]; /* Packing type */ + sih.flags = buf[31]; /* Loop/stereo/16bit flags */ + sih.c2spd = readmem16l(buf + 32); /* C4 speed */ + memcpy(sih.name, buf + 48, 28); /* Instrument name */ + sih.magic = readmem32b(buf + 76); /* 'SCRS' */ + + if (buf[0] == 1 && sih.magic != MAGIC_SCRS) { + D_(D_CRIT "error: instrument magic"); + goto err3; + } + + xxs->len = sih.length; + xxi->nsm = sih.length > 0 ? 1 : 0; + xxs->lps = sih.loopbeg; + xxs->lpe = sih.loopend; + + xxs->flg = sih.flags & 1 ? XMP_SAMPLE_LOOP : 0; + + if (sih.flags & 4) { + xxs->flg |= XMP_SAMPLE_16BIT; + } + + load_sample_flags = (sfh.ffi == 1) ? 0 : SAMPLE_FLAG_UNS; + if (sih.pack == 4) { + load_sample_flags = SAMPLE_FLAG_ADPCM; + } + + sub->vol = sih.vol; + sih.magic = 0; + + libxmp_instrument_name(mod, i, sih.name, 28); + + D_(D_INFO "[%2X] %-28.28s %04x%c%04x %04x %c V%02x %5d", + i, mod->xxi[i].name, mod->xxs[i].len, + xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ', + xxs->lps, mod->xxs[i].lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', sub->vol, sih.c2spd); + + libxmp_c2spd_to_note(sih.c2spd, &sub->xpo, &sub->fin); + + sample_segment = sih.memseg + ((uint32)sih.memseg_hi << 16); + + if (hio_seek(f, start + 16L * sample_segment, SEEK_SET) < 0) { + goto err3; + } + + ret = libxmp_load_sample(m, f, load_sample_flags, xxs, NULL); + if (ret < 0) { + goto err3; + } + } + + free(pp_pat); + free(pp_ins); + + m->quirk |= QUIRKS_ST3 | QUIRK_ARPMEM; + m->read_event_type = READ_EVENT_ST3; + + return 0; + +err3: + free(pp_pat); +err2: + free(pp_ins); +err: + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/sample.c b/thirdparty/libxmp/src/loaders/sample.c new file mode 100644 index 000000000..74e4e7515 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/sample.c @@ -0,0 +1,375 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "../common.h" +#include "loader.h" + +#ifndef LIBXMP_CORE_PLAYER +/* + * From the Audio File Formats (version 2.5) + * Submitted-by: Guido van Rossum + * Last-modified: 27-Aug-1992 + * + * The Acorn Archimedes uses a variation on U-LAW with the bit order + * reversed and the sign bit in bit 0. Being a 'minority' architecture, + * Arc owners are quite adept at converting sound/image formats from + * other machines, and it is unlikely that you'll ever encounter sound in + * one of the Arc's own formats (there are several). + */ +static const int8 vdic_table[128] = { + /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 24 */ 1, 1, 1, 1, 1, 1, 1, 1, + /* 32 */ 1, 1, 1, 1, 2, 2, 2, 2, + /* 40 */ 2, 2, 2, 2, 3, 3, 3, 3, + /* 48 */ 3, 3, 4, 4, 4, 4, 5, 5, + /* 56 */ 5, 5, 6, 6, 6, 6, 7, 7, + /* 64 */ 7, 8, 8, 9, 9, 10, 10, 11, + /* 72 */ 11, 12, 12, 13, 13, 14, 14, 15, + /* 80 */ 15, 16, 17, 18, 19, 20, 21, 22, + /* 88 */ 23, 24, 25, 26, 27, 28, 29, 30, + /* 96 */ 31, 33, 34, 36, 38, 40, 42, 44, + /* 104 */ 46, 48, 50, 52, 54, 56, 58, 60, + /* 112 */ 62, 65, 68, 72, 77, 80, 84, 91, + /* 120 */ 95, 98, 103, 109, 114, 120, 126, 127 +}; + +/* Convert 7 bit samples to 8 bit */ +static void convert_7bit_to_8bit(uint8 *p, int l) +{ + for (; l--; p++) { + *p <<= 1; + } +} + +/* Convert Archimedes VIDC samples to linear */ +static void convert_vidc_to_linear(uint8 *p, int l) +{ + int i; + uint8 x; + + for (i = 0; i < l; i++) { + x = p[i]; + p[i] = vdic_table[x >> 1]; + if (x & 0x01) + p[i] *= -1; + } +} + +static void adpcm4_decoder(uint8 *inp, uint8 *outp, char *tab, int len) +{ + char delta = 0; + uint8 b0, b1; + int i; + + len = (len + 1) / 2; + + for (i = 0; i < len; i++) { + b0 = *inp; + b1 = *inp++ >> 4; + delta += tab[b0 & 0x0f]; + *outp++ = delta; + delta += tab[b1 & 0x0f]; + *outp++ = delta; + } +} +#endif + +/* Convert differential to absolute sample data */ +static void convert_delta(uint8 *p, int l, int r) +{ + uint16 *w = (uint16 *)p; + uint16 absval = 0; + + if (r) { + for (; l--;) { + absval = *w + absval; + *w++ = absval; + } + } else { + for (; l--;) { + absval = *p + absval; + *p++ = (uint8) absval; + } + } +} + +/* Convert signed to unsigned sample data */ +static void convert_signal(uint8 *p, int l, int r) +{ + uint16 *w = (uint16 *)p; + + if (r) { + for (; l--; w++) + *w += 0x8000; + } else { + for (; l--; p++) + *p += (char)0x80; /* cast needed by MSVC++ */ + } +} + +/* Convert little-endian 16 bit samples to big-endian */ +static void convert_endian(uint8 *p, int l) +{ + uint8 b; + int i; + + for (i = 0; i < l; i++) { + b = p[0]; + p[0] = p[1]; + p[1] = b; + p += 2; + } +} + +#if 0 +/* Downmix stereo samples to mono */ +static void convert_stereo_to_mono(uint8 *p, int l, int r) +{ + int16 *b = (int16 *)p; + int i; + + if (r) { + l /= 2; + for (i = 0; i < l; i++) + b[i] = (b[i * 2] + b[i * 2 + 1]) / 2; + } else { + for (i = 0; i < l; i++) + p[i] = (p[i * 2] + p[i * 2 + 1]) / 2; + } +} +#endif + + +int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct xmp_sample *xxs, const void *buffer) +{ + int bytelen, extralen, i; + +#ifndef LIBXMP_CORE_PLAYER + /* Adlib FM patches */ + if (flags & SAMPLE_FLAG_ADLIB) { + return 0; + } +#endif + + /* Empty or invalid samples + */ + if (xxs->len <= 0) { + return 0; + } + + /* Skip sample loading + * FIXME: fails for ADPCM samples + * + * + Sanity check: skip huge samples (likely corrupt module) + */ + if (xxs->len > MAX_SAMPLE_SIZE || (m && m->smpctl & XMP_SMPCTL_SKIP)) { + if (~flags & SAMPLE_FLAG_NOLOAD) { + /* coverity[check_return] */ + hio_seek(f, xxs->len, SEEK_CUR); + } + return 0; + } + + /* If this sample starts at or after EOF, skip it entirely. + */ + if (~flags & SAMPLE_FLAG_NOLOAD) { + long file_pos, file_len; + if (!f) { + return 0; + } + file_pos = hio_tell(f); + file_len = hio_size(f); + if (file_pos >= file_len) { + D_(D_WARN "ignoring sample at EOF"); + return 0; + } + /* If this sample goes past EOF, truncate it. */ + if (file_pos + xxs->len > file_len && (~flags & SAMPLE_FLAG_ADPCM)) { + D_(D_WARN "sample would extend %ld bytes past EOF; truncating to %ld", + file_pos + xxs->len - file_len, file_len - file_pos); + xxs->len = file_len - file_pos; + } + } + + /* Loop parameters sanity check + */ + if (xxs->lps < 0) { + xxs->lps = 0; + } + if (xxs->lpe > xxs->len) { + xxs->lpe = xxs->len; + } + if (xxs->lps >= xxs->len || xxs->lps >= xxs->lpe) { + xxs->lps = xxs->lpe = 0; + xxs->flg &= ~(XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR); + } + + /* Patches with samples + * Allocate extra sample for interpolation. + */ + bytelen = xxs->len; + extralen = 4; + + /* Disable birectional loop flag if sample is not looped + */ + if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) { + if (~xxs->flg & XMP_SAMPLE_LOOP) + xxs->flg &= ~XMP_SAMPLE_LOOP_BIDIR; + } + if (xxs->flg & XMP_SAMPLE_SLOOP_BIDIR) { + if (~xxs->flg & XMP_SAMPLE_SLOOP) + xxs->flg &= ~XMP_SAMPLE_SLOOP_BIDIR; + } + + if (xxs->flg & XMP_SAMPLE_16BIT) { + bytelen *= 2; + extralen *= 2; + } + + /* add guard bytes before the buffer for higher order interpolation */ + xxs->data = (unsigned char *) malloc(bytelen + extralen + 4); + if (xxs->data == NULL) { + goto err; + } + + *(uint32 *)xxs->data = 0; + xxs->data += 4; + + if (flags & SAMPLE_FLAG_NOLOAD) { + memcpy(xxs->data, buffer, bytelen); + } else +#ifndef LIBXMP_CORE_PLAYER + if (flags & SAMPLE_FLAG_ADPCM) { + int x2 = (bytelen + 1) >> 1; + char table[16]; + + if (hio_read(table, 1, 16, f) != 16) { + goto err2; + } + if (hio_read(xxs->data + x2, 1, x2, f) != x2) { + goto err2; + } + adpcm4_decoder((uint8 *)xxs->data + x2, + (uint8 *)xxs->data, table, bytelen); + } else +#endif + { + int x = hio_read(xxs->data, 1, bytelen, f); + if (x != bytelen) { + D_(D_WARN "short read (%d) in sample load", x - bytelen); + memset(xxs->data + x, 0, bytelen - x); + } + } + +#ifndef LIBXMP_CORE_PLAYER + if (flags & SAMPLE_FLAG_7BIT) { + convert_7bit_to_8bit(xxs->data, xxs->len); + } +#endif + + /* Fix endianism if needed */ + if (xxs->flg & XMP_SAMPLE_16BIT) { +#ifdef WORDS_BIGENDIAN + if (~flags & SAMPLE_FLAG_BIGEND) + convert_endian(xxs->data, xxs->len); +#else + if (flags & SAMPLE_FLAG_BIGEND) + convert_endian(xxs->data, xxs->len); +#endif + } + + /* Convert delta samples */ + if (flags & SAMPLE_FLAG_DIFF) { + convert_delta(xxs->data, xxs->len, xxs->flg & XMP_SAMPLE_16BIT); + } else if (flags & SAMPLE_FLAG_8BDIFF) { + int len = xxs->len; + if (xxs->flg & XMP_SAMPLE_16BIT) { + len *= 2; + } + convert_delta(xxs->data, len, 0); + } + + /* Convert samples to signed */ + if (flags & SAMPLE_FLAG_UNS) { + convert_signal(xxs->data, xxs->len, + xxs->flg & XMP_SAMPLE_16BIT); + } + +#if 0 + /* Downmix stereo samples */ + if (flags & SAMPLE_FLAG_STEREO) { + convert_stereo_to_mono(xxs->data, xxs->len, + xxs->flg & XMP_SAMPLE_16BIT); + xxs->len /= 2; + } +#endif + +#ifndef LIBXMP_CORE_PLAYER + if (flags & SAMPLE_FLAG_VIDC) { + convert_vidc_to_linear(xxs->data, xxs->len); + } +#endif + + /* Check for full loop samples */ + if (flags & SAMPLE_FLAG_FULLREP) { + if (xxs->lps == 0 && xxs->len > xxs->lpe) + xxs->flg |= XMP_SAMPLE_LOOP_FULL; + } + + /* Add extra samples at end */ + if (xxs->flg & XMP_SAMPLE_16BIT) { + for (i = 0; i < 8; i++) { + xxs->data[bytelen + i] = xxs->data[bytelen - 2 + i]; + } + } else { + for (i = 0; i < 4; i++) { + xxs->data[bytelen + i] = xxs->data[bytelen - 1 + i]; + } + } + + /* Add extra samples at start */ + if (xxs->flg & XMP_SAMPLE_16BIT) { + xxs->data[-2] = xxs->data[0]; + xxs->data[-1] = xxs->data[1]; + } else { + xxs->data[-1] = xxs->data[0]; + } + + return 0; + +#ifndef LIBXMP_CORE_PLAYER + err2: + libxmp_free_sample(xxs); +#endif + err: + return -1; +} + +void libxmp_free_sample(struct xmp_sample *s) +{ + if (s->data) { + free(s->data - 4); + s->data = NULL; /* prevent double free in PCM load error */ + } +} diff --git a/thirdparty/libxmp/src/loaders/sfx_load.c b/thirdparty/libxmp/src/loaders/sfx_load.c new file mode 100644 index 000000000..0ccd9650e --- /dev/null +++ b/thirdparty/libxmp/src/loaders/sfx_load.c @@ -0,0 +1,261 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Reverse engineered from the two SFX files in the Delitracker mods disk + * and music from Future Wars, Twinworld and Operation Stealth. Effects + * must be verified/implemented. + */ + +/* From the ExoticRipper docs: + * [SoundFX 2.0 is] simply the same as SoundFX 1.3, except that it + * uses 31 samples [instead of 15]. + */ + +#include "loader.h" +#include "../period.h" + +#define MAGIC_SONG MAGIC4('S','O','N','G') + +static int sfx_test(HIO_HANDLE *, char *, const int); +static int sfx_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_sfx = { + "SoundFX v1.3/2.0", + sfx_test, + sfx_load +}; + +static int sfx_test(HIO_HANDLE * f, char *t, const int start) +{ + uint32 a, b; + + hio_seek(f, 4 * 15, SEEK_CUR); + a = hio_read32b(f); + hio_seek(f, 4 * 15, SEEK_CUR); + b = hio_read32b(f); + + if (a != MAGIC_SONG && b != MAGIC_SONG) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +struct sfx_ins { + uint8 name[22]; /* Instrument name */ + uint16 len; /* Sample length in words */ + uint8 finetune; /* Finetune */ + uint8 volume; /* Volume (0-63) */ + uint16 loop_start; /* Sample loop start in bytes */ + uint16 loop_length; /* Sample loop length in words */ +}; + +struct sfx_header { + uint32 magic; /* 'SONG' */ + uint16 delay; /* Delay value (tempo), default is 0x38e5 */ + uint16 unknown[7]; /* ? */ +}; + +struct sfx_header2 { + uint8 len; /* Song length */ + uint8 restart; /* Restart pos (?) */ + uint8 order[128]; /* Order list */ +}; + +static int sfx_13_20_load(struct module_data *m, HIO_HANDLE *f, const int nins, + const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct sfx_header sfx; + struct sfx_header2 sfx2; + uint8 ev[4]; + int ins_size[31]; + struct sfx_ins ins[31]; /* Instruments */ + + LOAD_INIT(); + + for (i = 0; i < nins; i++) + ins_size[i] = hio_read32b(f); + + sfx.magic = hio_read32b(f); + sfx.delay = hio_read16b(f); + if (sfx.delay < 178) /* min value for 10000bpm */ + return -1; + + hio_read(sfx.unknown, 14, 1, f); + + if (sfx.magic != MAGIC_SONG) + return -1; + + mod->chn = 4; + mod->ins = nins; + mod->smp = mod->ins; + mod->bpm = 14565 * 122 / sfx.delay; + + for (i = 0; i < mod->ins; i++) { + hio_read(ins[i].name, 22, 1, f); + ins[i].len = hio_read16b(f); + ins[i].finetune = hio_read8(f); + ins[i].volume = hio_read8(f); + ins[i].loop_start = hio_read16b(f); + ins[i].loop_length = hio_read16b(f); + } + + sfx2.len = hio_read8(f); + sfx2.restart = hio_read8(f); + if (hio_read(sfx2.order, 1, 128, f) != 128) + return -1; + + mod->len = sfx2.len; + if (mod->len > 0x7f) + return -1; + + memcpy(mod->xxo, sfx2.order, mod->len); + for (mod->pat = i = 0; i < mod->len; i++) + if (mod->xxo[i] > mod->pat) + mod->pat = mod->xxo[i]; + mod->pat++; + + mod->trk = mod->chn * mod->pat; + + if (mod->ins == 15) { + libxmp_set_type(m, "SoundFX 1.3"); + } else { + libxmp_set_type(m, "SoundFX 2.0"); + } + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi; + struct xmp_subinstrument *sub; + struct xmp_sample *xxs; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + xxi = &mod->xxi[i]; + xxs = &mod->xxs[i]; + sub = &xxi->sub[0]; + + xxs->len = ins_size[i]; + xxs->lps = ins[i].loop_start; + xxs->lpe = xxs->lps + 2 * ins[i].loop_length; + xxs->flg = ins[i].loop_length > 1 ? XMP_SAMPLE_LOOP : 0; + xxi->nsm = 1; + sub->vol = ins[i].volume; + sub->fin = (int8) (ins[i].finetune << 4); /* unsure */ + sub->pan = 0x80; + sub->sid = i; + + libxmp_instrument_name(mod, i, ins[i].name, 22); + + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c %02x %+d", + i, xxi->name, xxs->len, xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', sub->vol, + sub->fin >> 4); + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + for (j = 0; j < 64 * mod->chn; j++) { + event = &EVENT(i, j % mod->chn, j / mod->chn); + if (hio_read(ev, 1, 4, f) < 4) { + D_(D_CRIT "read error at pat %d", i); + return -1; + } + + event->note = libxmp_period_to_note((LSN(ev[0]) << 8) | ev[1]); + event->ins = (MSN(ev[0]) << 4) | MSN(ev[2]); + event->fxp = ev[3]; + + switch (LSN(ev[2])) { + case 0x01: /* Arpeggio */ + event->fxt = FX_ARPEGGIO; + break; + case 0x02: /* Pitch bend */ + if (event->fxp >> 4) { + event->fxt = FX_PORTA_DN; + event->fxp >>= 4; + } else if (event->fxp & 0x0f) { + event->fxt = FX_PORTA_UP; + event->fxp &= 0x0f; + } + break; + case 0x5: /* Add to volume */ + event->fxt = FX_VOL_ADD; + break; + case 0x6: /* Subtract from volume */ + event->fxt = FX_VOL_SUB; + break; + case 0x7: /* Add semitones to period */ + event->fxt = FX_PITCH_ADD; + break; + case 0x8: /* Subtract semitones from period */ + event->fxt = FX_PITCH_SUB; + break; + case 0x3: /* LED on */ + case 0x4: /* LED off */ + default: + event->fxt = event->fxp = 0; + break; + } + } + } + + m->quirk |= QUIRK_PBALL; + m->period_type = PERIOD_MODRNG; + + /* Read samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (mod->xxs[i].len <= 2) + continue; + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + return 0; +} + +static int sfx_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + if (sfx_13_20_load(m, f, 15, start) < 0) + return sfx_13_20_load(m, f, 31, start); + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/st_load.c b/thirdparty/libxmp/src/loaders/st_load.c new file mode 100644 index 000000000..97540bf62 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/st_load.c @@ -0,0 +1,517 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Ultimate Soundtracker support based on the module format description + * written by Michael Schwendt + */ + +#include "loader.h" +#include "mod.h" +#include "../period.h" + +static int st_test(HIO_HANDLE *, char *, const int); +static int st_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_st = { + "Soundtracker", + st_test, + st_load +}; + +/* musanx.mod contains 22 period and instrument errors */ +#define ST_MAX_PATTERN_ERRORS 22 +/* Allow some degree of sample truncation for ST modules. + * The worst known module currently is u2.mod with 7% truncation. */ +#define ST_TRUNCATION_LIMIT 93 + +static const int period[] = { + 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, + 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, + 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, + /* Off-by-one period values found in blueberry.mod, snd.mod, + * quite a lot.mod, sweet dreams.mod, and bar----fringdus.mod */ + 763, 679, 641, 571, 539, 509, 429, 340, 321, 300, 286, 270, + 227, 191, 162, + -1 +}; + +static int st_expected_size(int smp_size, int pat) +{ + return 600 + smp_size + 1024 * pat; +} + +static int st_test(HIO_HANDLE *f, char *t, const int start) +{ + int i, j, k; + int pat, pat_short, ins, smp_size; + struct st_header mh; + uint8 pat_buf[1024]; + uint8 *mod_event; + int pattern_errors; + int test_flags = TEST_NAME_IGNORE_AFTER_CR; + long size; + + size = hio_size(f); + + if (size < 600) { + return -1; + } + + smp_size = 0; + + hio_seek(f, start, SEEK_SET); + hio_read(mh.name, 1, 20, f); + /* The Super Ski 2 modules have unusual "SONG\x13\x88" names. */ + if (mh.name[5] == 0x88) { + mh.name[5] = 'X'; + if (mh.name[4] == 0x13) + mh.name[4] = 'X'; + } + if (libxmp_test_name(mh.name, 20, 0) < 0) { + D_(D_CRIT "bad module name; not ST"); + return -1; + } + + for (i = 0; i < 15; i++) { + hio_read(mh.ins[i].name, 1, 22, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 1, 128, f); + + for (pat = pat_short = i = 0; i < 128; i++) { + if (mh.order[i] > 0x7f) + return -1; + if (mh.order[i] > pat) { + pat = mh.order[i]; + if (i < mh.len) + pat_short = pat; + } + } + pat++; + pat_short++; + + if (pat > 0x7f || mh.len == 0 || mh.len > 0x80) + return -1; + + for (i = 0; i < 15; i++) { + smp_size += 2 * mh.ins[i].size; + + /* pennylane.mod and heymusic-sssexremix.mod have unusual + * values after the \0. */ + if (i == 0 && + (!memcmp(mh.ins[i].name, "funbass\0\r", 9) || + !memcmp(mh.ins[i].name, "st-69:baseline\0R\0\0\xA5", 17))) { + D_(D_INFO "ignoring junk name values after \\0"); + test_flags |= TEST_NAME_IGNORE_AFTER_0; + } + + /* Crepequs.mod has random values in first byte */ + mh.ins[i].name[0] = 'X'; + + if (libxmp_test_name(mh.ins[i].name, 22, test_flags) < 0) { + D_(D_CRIT "bad instrument name %d; not ST", i); + return -1; + } + + if (mh.ins[i].volume > 0x40) + return -1; + + if (mh.ins[i].finetune > 0x0f) + return -1; + + if (mh.ins[i].size > 0x8000) + return -1; + + /* This test is always false, disable it + * + * if ((mh.ins[i].loop_start >> 1) > 0x8000) + * return -1; + */ + + if (mh.ins[i].loop_size > 0x8000) + return -1; + + /* This test fails in atmosfer.mod, disable it + * + * if (mh.ins[i].loop_size > 1 && mh.ins[i].loop_size > mh.ins[i].size) + * return -1; + */ + + /* Bad rip of fin-nv1.mod has this unused instrument. */ + if (mh.ins[i].size == 0 && + mh.ins[i].loop_start == 4462 && + mh.ins[i].loop_size == 2078) { + D_(D_INFO "ignoring bad instrument for fin-nv1.mod"); + continue; + } + + if ((mh.ins[i].loop_start >> 1) > mh.ins[i].size) + return -1; + + if (mh.ins[i].size + && (mh.ins[i].loop_start >> 1) == mh.ins[i].size) + return -1; + + if (mh.ins[i].size == 0 && mh.ins[i].loop_start > 0) + return -1; + } + + if (smp_size < 8) { + return -1; + } + + /* If the file size is correct when counting only patterns prior to the + * module length, use the shorter count. This quirk is found in some + * ST modules, most of them authored by Jean Baudlot. See razor-1911.mod, + * the Operation Wolf soundtrack, or the Bad Dudes soundtrack. + */ + if (size < st_expected_size(smp_size, pat) && + size == st_expected_size(smp_size, pat_short)) { + D_(D_INFO "ST pattern list probably quirked, ignoring patterns past len"); + pat = pat_short; + } + + pattern_errors = 0; + for (ins = i = 0; i < pat; i++) { + if (hio_read(pat_buf, 1, 1024, f) < 1024) { + D_(D_CRIT "read error at pattern %d; not ST", i); + return -1; + } + mod_event = pat_buf; + for (j = 0; j < (64 * 4); j++, mod_event += 4) { + int p, s; + + s = (mod_event[0] & 0xf0) | MSN(mod_event[2]); + + if (s > 15) { /* sample number > 15 */ + D_(D_INFO "%d/%d/%d: invalid sample number: %d", i, j / 4, j % 4, s); + if ((++pattern_errors) > ST_MAX_PATTERN_ERRORS) + goto bad_pattern_data; + } + + if (s > ins) { /* find highest used sample */ + ins = s; + } + + p = 256 * LSN(mod_event[0]) + mod_event[1]; + + if (p == 0) { + continue; + } + + for (k = 0; period[k] >= 0; k++) { + if (p == period[k]) + break; + } + if (period[k] < 0) { + D_(D_INFO "%d/%d/%d: invalid period: %d", i, j / 4, j % 4, p); + if ((++pattern_errors) > ST_MAX_PATTERN_ERRORS) + goto bad_pattern_data; + } + } + } + + /* Check if file was cut before any unused samples */ + if (size < st_expected_size(smp_size, pat)) { + int ss, limit; + for (ss = i = 0; i < 15 && i < ins; i++) { + ss += 2 * mh.ins[i].size; + } + + limit = st_expected_size(ss, pat) * ST_TRUNCATION_LIMIT / 100; + if (size < limit) { + D_(D_CRIT "expected size %d, minimum allowed size %d, real size %ld, diff %ld", + st_expected_size(smp_size, pat), limit, size, size - limit); + return -1; + } + } + + hio_seek(f, start, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; + +bad_pattern_data: + D_(D_CRIT "too many pattern errors; not ST: %d", pattern_errors); + return -1; +} + +static int st_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xmp_event *event; + struct st_header mh; + uint8 pat_buf[1024]; + uint8 *mod_event; + int ust = 1; + /* int lps_mult = m->fetch & XMP_CTL_FIXLOOP ? 1 : 2; */ + const char *modtype; + int fxused; + int smp_size, pat_short; + long size; + + LOAD_INIT(); + + mod->chn = 4; + mod->ins = 15; + mod->smp = mod->ins; + + smp_size = 0; + + hio_read(mh.name, 1, 20, f); + for (i = 0; i < 15; i++) { + hio_read(mh.ins[i].name, 1, 22, f); + mh.ins[i].size = hio_read16b(f); + mh.ins[i].finetune = hio_read8(f); + mh.ins[i].volume = hio_read8(f); + mh.ins[i].loop_start = hio_read16b(f); + mh.ins[i].loop_size = hio_read16b(f); + smp_size += 2 * mh.ins[i].size; + } + mh.len = hio_read8(f); + mh.restart = hio_read8(f); + hio_read(mh.order, 1, 128, f); + + mod->len = mh.len; + mod->rst = mh.restart; + + /* UST: The byte at module offset 471 is BPM, not the song restart + * The default for UST modules is 0x78 = 120 BPM = 48 Hz. + */ + if (mod->rst < 0x40) /* should be 0x20 */ + ust = 0; + + memcpy(mod->xxo, mh.order, 128); + + for (pat_short = i = 0; i < 128; i++) { + if (mod->xxo[i] > mod->pat) { + mod->pat = mod->xxo[i]; + if (i < mh.len) + pat_short = mod->pat; + } + } + mod->pat++; + pat_short++; + + /* If the file size is correct when counting only patterns prior to the + * module length, use the shorter count. See test function for info. + */ + size = hio_size(f); + if (size < st_expected_size(smp_size, mod->pat) && + size == st_expected_size(smp_size, pat_short)) { + mod->pat = pat_short; + } + + for (i = 0; i < mod->ins; i++) { + /* UST: Volume word does not contain a "Finetuning" value in its + * high-byte. + */ + if (mh.ins[i].finetune) + ust = 0; + + /* if (mh.ins[i].size == 0 && mh.ins[i].loop_size == 1) + nt = 1; */ + + /* UST: Maximum sample length is 9999 bytes decimal, but 1387 + * words hexadecimal. Longest samples on original sample disk + * ST-01 were 9900 bytes. + */ + if (mh.ins[i].size > 0x1387 || mh.ins[i].loop_start > 9999 + || mh.ins[i].loop_size > 0x1387) { + ust = 0; + } + } + + if (libxmp_init_instrument(m) < 0) { + return -1; + } + + for (i = 0; i < mod->ins; i++) { + struct xmp_instrument *xxi = &mod->xxi[i]; + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sub = &xxi->sub[0]; + + xxs->len = 2 * mh.ins[i].size - mh.ins[i].loop_start; + xxs->lps = 0; + xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size; + xxs->flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + sub->fin = (int8) (mh.ins[i].finetune << 4); + sub->vol = mh.ins[i].volume; + sub->pan = 0x80; + sub->sid = i; + strncpy((char *)xxi->name, (char *)mh.ins[i].name, 22); + + if (xxs->len > 0) { + xxi->nsm = 1; + } + } + + mod->trk = mod->chn * mod->pat; + + strncpy(mod->name, (char *)mh.name, 20); + + if (libxmp_init_pattern(mod) < 0) { + return -1; + } + + /* Load and convert patterns */ + /* Also scan patterns for tracker detection */ + fxused = 0; + + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + if (hio_read(pat_buf, 1, 1024, f) < 1024) + return -1; + + mod_event = pat_buf; + for (j = 0; j < (64 * 4); j++, mod_event += 4) { + event = &EVENT(i, j % 4, j / 4); + libxmp_decode_protracker_event(event, mod_event); + + if (event->fxt) + fxused |= 1 << event->fxt; + else if (event->fxp) + fxused |= 1; + + /* UST: Only effects 1 (arpeggio) and 2 (pitchbend) are + * available. + */ + if (event->fxt && event->fxt != 1 && event->fxt != 2) + ust = 0; + + /* Karsten Obarski's sleepwalk uses arpeggio 30 and 40 */ + if (event->fxt == 1) { /* unlikely arpeggio */ + if (event->fxp == 0x00) + ust = 0; + /*if ((ev.fxp & 0x0f) == 0 || (ev.fxp & 0xf0) == 0) + ust = 0; */ + } + + if (event->fxt == 2) { /* bend up and down at same time? */ + if ((event->fxp & 0x0f) != 0 && (event->fxp & 0xf0) != 0) + ust = 0; + } + } + } + + if (fxused & ~0x0006) { + ust = 0; + } + + if (ust) { + modtype = "Ultimate Soundtracker"; + } else if ((fxused & ~0xd007) == 0) { + modtype = "Soundtracker IX"; /* or MasterSoundtracker? */ + } else if ((fxused & ~0xf807) == 0) { + modtype = "D.O.C Soundtracker 2.0"; + } else { + modtype = "unknown tracker 15 instrument"; + } + + snprintf(mod->type, XMP_NAME_SIZE, "%s", modtype); + + MODULE_INFO(); + + for (i = 0; i < mod->ins; i++) { + D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d", + i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, mh.ins[i].loop_size > 1 ? 'L' : ' ', + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].fin >> 4); + } + + m->quirk |= QUIRK_NOBPM; + m->period_type = PERIOD_MODRNG; + + /* Perform the necessary conversions for Ultimate Soundtracker */ + if (ust) { + /* Fix restart & bpm */ + mod->bpm = mod->rst; + mod->rst = 0; + + /* Fix sample loops */ + for (i = 0; i < mod->ins; i++) { + /* FIXME */ + } + + /* Fix effects (arpeggio and pitchbending) */ + for (i = 0; i < mod->pat; i++) { + for (j = 0; j < (64 * 4); j++) { + event = &EVENT(i, j % 4, j / 4); + if (event->fxt == 1) + event->fxt = 0; + else if (event->fxt == 2 + && (event->fxp & 0xf0) == 0) + event->fxt = 1; + else if (event->fxt == 2 + && (event->fxp & 0x0f) == 0) + event->fxp >>= 4; + } + } + } else { + if (mod->rst >= mod->len) + mod->rst = 0; + } + + /* Load samples */ + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (!mod->xxs[i].len) + continue; + + /* Skip transient part of sample. + * + * Dennis Lindroos reports: One main thing is + * sample-looping which on all trackers up to Noisetracker 1 (i + * think it was Mahoney who actually changed the loopstart to be + * in WORDS) never play looped samples from the beginning, i.e. + * only plays the looped part. This can be heard in old modules + * especially with "analogstring", "strings2" or "strings3" + * samples because these have "slow attack" that is not part of + * the loop and thus they sound "sharper".. + */ + hio_seek(f, mh.ins[i].loop_start, SEEK_CUR); + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) { + return -1; + } + } + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/stim_load.c b/thirdparty/libxmp/src/loaders/stim_load.c new file mode 100644 index 000000000..d1daa0ae5 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/stim_load.c @@ -0,0 +1,216 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Loader for Slamtilt modules based on the format description + * written by Sylvain Chipaux (Asle/ReDoX). Get the Slamtilt demo + * from game/demo in Aminet. + */ + +/* Tested with the Slamtilt modules sent by Sipos Attila */ + +#include "loader.h" + +#define MAGIC_STIM MAGIC4('S','T','I','M') + +static int stim_test(HIO_HANDLE *, char *, const int); +static int stim_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_stim = { + "Slamtilt", + stim_test, + stim_load +}; + +static int stim_test(HIO_HANDLE *f, char *t, const int start) +{ + if (hio_read32b(f) != MAGIC_STIM) + return -1; + + if (hio_read16b(f) > 16) + return -1; + + libxmp_read_title(f, t, 0); + + return 0; +} + +struct stim_instrument { + uint16 size; /* Lenght of the sample (/2) */ + uint8 finetune; /* Finetune (as ptk) */ + uint8 volume; /* Volume (as ptk) */ + uint16 loop_start; /* Loop start (/2) */ + uint16 loop_size; /* Loop lenght (/2) */ +}; + +struct stim_header { + uint32 id; /* "STIM" ID string */ + uint32 smpaddr; /* Address of the sample descriptions */ + uint32 unknown[2]; + uint16 nos; /* Number of samples (?) */ + uint16 len; /* Size of pattern list */ + uint16 pat; /* Number of patterns saved */ + uint8 order[128]; /* Pattern list */ + uint32 pataddr[64]; /* Pattern addresses (add 0xc) */ +}; + +static int stim_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k; + struct xmp_event *event; + struct stim_header sh; + struct stim_instrument si; + uint8 b1, b2, b3; + + LOAD_INIT(); + + sh.id = hio_read32b(f); + sh.smpaddr = hio_read32b(f); + hio_read32b(f); + hio_read32b(f); + sh.nos = hio_read16b(f); + sh.len = hio_read16b(f); + sh.pat = hio_read16b(f); + if (hio_read(sh.order, 128, 1, f) == 0) { + return -1; + } + + /* Sanity check */ + if (sh.nos > 31 || sh.len > 128 || sh.pat > 64) { + return -1; + } + + for (i = 0; i < 64; i++) { + sh.pataddr[i] = hio_read32b(f) + 0x0c; + if (sh.pataddr[i] > 0x00100000) + return -1; + } + + mod->chn = 4; + mod->len = sh.len; + mod->pat = sh.pat; + mod->ins = sh.nos; + mod->smp = mod->ins; + mod->trk = mod->pat * mod->chn; + + for (i = 0; i < mod->len; i++) + mod->xxo[i] = sh.order[i]; + + libxmp_set_type(m, "Slamtilt"); + + MODULE_INFO(); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Load and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + hio_seek(f, start + sh.pataddr[i] + 8, SEEK_SET); + + for (j = 0; j < 4; j++) { + for (k = 0; k < 64; k++) { + event = &EVENT(i, j, k); + b1 = hio_read8(f); + + if (b1 & 0x80) { + k += b1 & 0x7f; + continue; + } + + /* STIM event format: + * + * __ Fx __ + * / \ + * || || + * 0000 0000 0000 0000 0000 0000 + * | | | | | | | + * | \ / \ / \ / + * | smp note Fx Val + * | + * Description bit set to 0. + */ + + b2 = hio_read8(f); + b3 = hio_read8(f); + + if ((event->note = b2 & 0x3f) != 0) + event->note += 47; + event->ins = b1 & 0x1f; + event->fxt = ((b2 >> 4) & 0x0c) | (b1 >> 5); + event->fxp = b3; + + libxmp_disable_continue_fx(event); + } + } + } + + if (libxmp_init_instrument(m) < 0) + return -1; + + D_(D_INFO "Stored samples: %d", mod->smp); + + hio_seek(f, start + sh.smpaddr + mod->smp * 4, SEEK_SET); + + for (i = 0; i < mod->smp; i++) { + si.size = hio_read16b(f); + si.finetune = hio_read8(f); + si.volume = hio_read8(f); + si.loop_start = hio_read16b(f); + si.loop_size = hio_read16b(f); + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = 2 * si.size; + mod->xxs[i].lps = 2 * si.loop_start; + mod->xxs[i].lpe = mod->xxs[i].lps + 2 * si.loop_size; + mod->xxs[i].flg = si.loop_size > 1 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].fin = (int8) (si.finetune << 4); + mod->xxi[i].sub[0].vol = si.volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].rls = 0xfff; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + D_(D_INFO "[%2X] %04x %04x %04x %c V%02x %+d", + i, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, si.loop_size > 1 ? 'L' : ' ', + mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].fin >> 4); + + if (!mod->xxs[i].len) + continue; + + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->period_type = PERIOD_MODRNG; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/stm_load.c b/thirdparty/libxmp/src/loaders/stm_load.c new file mode 100644 index 000000000..d4b11b9e9 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/stm_load.c @@ -0,0 +1,416 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "../period.h" + +#define STM_TYPE_SONG 0x01 +#define STM_TYPE_MODULE 0x02 + +struct stm_instrument_header { + uint8 name[12]; /* ASCIIZ instrument name */ + uint8 id; /* Id=0 */ + uint8 idisk; /* Instrument disk */ + uint16 rsvd1; /* Reserved */ + uint16 length; /* Sample length */ + uint16 loopbeg; /* Loop begin */ + uint16 loopend; /* Loop end */ + uint8 volume; /* Playback volume */ + uint8 rsvd2; /* Reserved */ + uint16 c2spd; /* C4 speed */ + uint32 rsvd3; /* Reserved */ + uint16 paralen; /* Length in paragraphs */ +}; + +/* v1 format header based on disassembled ST2 */ +struct stm_file_subheader_v1 { + uint16 insnum; /* Number of instruments */ + uint16 ordnum; /* Number of orders */ + uint16 patnum; /* Number of patterns */ + uint16 srate; /* Sample rate? */ + uint8 tempo; /* Playback tempo */ + uint8 channels; /* Number of channels */ + uint16 psize; /* Pattern size */ + uint16 rsvd2; /* Reserved */ + uint16 skip; /* Bytes to skip */ +}; + +struct stm_file_subheader_v2 { + uint8 tempo; /* Playback tempo */ + uint8 patterns; /* Number of patterns */ + uint8 gvol; /* Global volume */ + uint8 rsvd2[13]; /* Reserved */ +}; + +struct stm_file_header { + uint8 name[20]; /* ASCIIZ song name */ + uint8 magic[8]; /* '!Scream!' */ + uint8 rsvd1; /* '\x1a' */ + uint8 type; /* 1=song, 2=module */ + uint8 vermaj; /* Major version number */ + uint8 vermin; /* Minor version number */ + union { + struct stm_file_subheader_v1 v1; + struct stm_file_subheader_v2 v2; + } sub; + struct stm_instrument_header ins[32]; +}; + +static int stm_test(HIO_HANDLE *, char *, const int); +static int stm_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_stm = { + "Scream Tracker 2", + stm_test, + stm_load +}; + +static int stm_test(HIO_HANDLE * f, char *t, const int start) +{ + uint8 buf[8]; + + hio_seek(f, start + 20, SEEK_SET); + if (hio_read(buf, 1, 8, f) < 8) + return -1; + + if (libxmp_test_name(buf, 8, 0)) /* Tracker name should be ASCII */ + return -1; + + if (hio_read8(f) != 0x1a) + return -1; + + if (hio_read8(f) > STM_TYPE_MODULE) + return -1; + + hio_seek(f, start + 60, SEEK_SET); + if (hio_read(buf, 1, 4, f) < 4) + return -1; + if (!memcmp(buf, "SCRM", 4)) /* We don't want STX files */ + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + +#define FX_NONE 0xff + +/* + * Skaven's note from http://www.futurecrew.com/skaven/oldies_music.html + * + * FYI for the tech-heads: In the old Scream Tracker 2 the Arpeggio command + * (Jxx), if used in a single row with a 0x value, caused the note to skip + * the specified amount of halftones upwards halfway through the row. I used + * this in some songs to give the lead some character. However, when played + * in ModPlug Tracker, this effect doesn't work the way it did back then. + */ +static const uint8 fx[16] = { + FX_NONE, + FX_SPEED, /* A - Set tempo to [INFO]. 60 normal. */ + FX_JUMP, /* B - Break pattern and jmp to order [INFO] */ + FX_BREAK, /* C - Break pattern */ + FX_VOLSLIDE, /* D - Slide volume; Hi-nibble=up, Lo-nibble=down */ + FX_PORTA_DN, /* E - Slide down at speed [INFO] */ + FX_PORTA_UP, /* F - Slide up at speed [INFO] */ + FX_TONEPORTA, /* G - Slide to the note specified at speed [INFO] */ + FX_VIBRATO, /* H - Vibrato; Hi-nibble, speed. Lo-nibble, size */ + FX_TREMOR, /* I - Tremor; Hi-nibble, ontime. Lo-nibble, offtime */ + FX_ARPEGGIO, /* J - Arpeggio */ + FX_NONE, + FX_NONE, + FX_NONE, + FX_NONE, + FX_NONE +}; + +static int stm_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + struct stm_file_header sfh; + uint8 b; + uint16 version; + int blank_pattern = 0; + int stored_patterns; + int i, j, k; + + LOAD_INIT(); + + hio_read(sfh.name, 20, 1, f); /* ASCIIZ song name */ + hio_read(sfh.magic, 8, 1, f); /* '!Scream!' */ + sfh.rsvd1 = hio_read8(f); /* '\x1a' */ + sfh.type = hio_read8(f); /* 1=song, 2=module */ + sfh.vermaj = hio_read8(f); /* Major version number */ + sfh.vermin = hio_read8(f); /* Minor version number */ + version = (100 * sfh.vermaj) + sfh.vermin; + + if (version != 110 && + version != 200 && version != 210 && + version != 220 && version != 221) { + D_(D_CRIT "Unknown version: %d", version); + return -1; + } + + // TODO: improve robustness of the loader against bad parameters + + if (version >= 200) { + sfh.sub.v2.tempo = hio_read8(f); /* Playback tempo */ + sfh.sub.v2.patterns = hio_read8(f); /* Number of patterns */ + sfh.sub.v2.gvol = hio_read8(f); /* Global volume */ + hio_read(sfh.sub.v2.rsvd2, 13, 1, f); /* Reserved */ + mod->chn = 4; + mod->pat = sfh.sub.v2.patterns; + mod->spd = (version < 221) ? LSN(sfh.sub.v2.tempo / 10) : MSN(sfh.sub.v2.tempo); + mod->ins = 31; + mod->len = (version == 200) ? 64 : 128; + } else { + if ((sfh.sub.v1.insnum = hio_read16l(f)) > 32) { /* Number of instruments */ + D_(D_CRIT "Wrong number of instruments: %d (max 32)", sfh.sub.v1.insnum); + return -1; + } + if ((sfh.sub.v1.ordnum = hio_read16l(f)) > XMP_MAX_MOD_LENGTH) { /* Number of orders */ + D_(D_CRIT "Wrong number of orders: %d (max %d)", sfh.sub.v1.ordnum, XMP_MAX_MOD_LENGTH); + return -1; + } + if ((sfh.sub.v1.patnum = hio_read16l(f)) > XMP_MAX_MOD_LENGTH) { /* Number of patterns */ + D_(D_CRIT "Wrong number of patterns: %d (max %d)", sfh.sub.v1.patnum, XMP_MAX_MOD_LENGTH); + return -1; + } + sfh.sub.v1.srate = hio_read16l(f); /* Sample rate? */ + sfh.sub.v1.tempo = hio_read8(f); /* Playback tempo */ + if ((sfh.sub.v1.channels = hio_read8(f)) != 4) { /* Number of channels */ + D_(D_CRIT "Wrong number of sound channels: %d", sfh.sub.v1.channels); + return -1; + } + if ((sfh.sub.v1.psize = hio_read16l(f)) != 64) { /* Pattern size */ + D_(D_CRIT "Wrong number of rows per pattern: %d", sfh.sub.v1.psize); + return -1; + } + sfh.sub.v1.rsvd2 = hio_read16l(f); /* Reserved */ + sfh.sub.v1.skip = hio_read16l(f); /* Bytes to skip */ + hio_seek(f, sfh.sub.v1.skip, SEEK_CUR); /* Skip bytes */ + mod->chn = sfh.sub.v1.channels; + mod->pat = sfh.sub.v1.patnum; + mod->spd = (version != 100) ? LSN(sfh.sub.v1.tempo / 10) : LSN(sfh.sub.v1.tempo); + mod->ins = sfh.sub.v1.insnum; + mod->len = sfh.sub.v1.ordnum; + } + + for (i = 0; i < mod->ins; i++) { + hio_read(sfh.ins[i].name, 12, 1, f); /* Instrument name */ + sfh.ins[i].id = hio_read8(f); /* Id=0 */ + sfh.ins[i].idisk = hio_read8(f); /* Instrument disk */ + sfh.ins[i].rsvd1 = hio_read16l(f); /* Reserved */ + sfh.ins[i].length = hio_read16l(f); /* Sample length */ + sfh.ins[i].loopbeg = hio_read16l(f); /* Loop begin */ + sfh.ins[i].loopend = hio_read16l(f); /* Loop end */ + sfh.ins[i].volume = hio_read8(f); /* Playback volume */ + sfh.ins[i].rsvd2 = hio_read8(f); /* Reserved */ + sfh.ins[i].c2spd = hio_read16l(f); /* C4 speed */ + sfh.ins[i].rsvd3 = hio_read32l(f); /* Reserved */ + sfh.ins[i].paralen = hio_read16l(f); /* Length in paragraphs */ + } + + if (hio_error(f)) { + return -1; + } + + mod->smp = mod->ins; + m->c4rate = C4_NTSC_RATE; + + libxmp_copy_adjust(mod->name, sfh.name, 20); + + if (!sfh.magic[0] || !strncmp((char *)sfh.magic, "PCSTV", 5) || !strncmp((char *)sfh.magic, "!Scream!", 8)) + libxmp_set_type(m, "Scream Tracker %d.%02d", sfh.vermaj, sfh.vermin); + else if (!strncmp((char *)sfh.magic, "SWavePro", 8)) + libxmp_set_type(m, "SoundWave Pro %d.%02d", sfh.vermaj, sfh.vermin); + else + libxmp_copy_adjust(mod->type, sfh.magic, 8); + + MODULE_INFO(); + + if (libxmp_init_instrument(m) < 0) + return -1; + + /* Read and convert instruments and samples */ + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + mod->xxs[i].len = sfh.ins[i].length; + mod->xxs[i].lps = sfh.ins[i].loopbeg; + mod->xxs[i].lpe = sfh.ins[i].loopend; + if (mod->xxs[i].lpe == 0xffff) + mod->xxs[i].lpe = 0; + mod->xxs[i].flg = mod->xxs[i].lpe > 0 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].vol = sfh.ins[i].volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + libxmp_instrument_name(mod, i, sfh.ins[i].name, 12); + + D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c V%02x %5d", i, + mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, sfh.ins[i].c2spd); + + libxmp_c2spd_to_note(sfh.ins[i].c2spd, &mod->xxi[i].sub[0].xpo, + &mod->xxi[i].sub[0].fin); + } + + if (hio_read(mod->xxo, 1, mod->len, f) < mod->len) + return -1; + + for (i = 0; i < mod->len; i++) { + if (mod->xxo[i] >= 99) { + break; + } + /* Patterns >= the pattern count are valid blank patterns. + * Examples: jimmy.stm, Rauno/dogs.stm, Skaven/hevijanis istu maas.stm. + * Patterns >= 64 have undefined behavior in Screamtracker 2. + */ + if (mod->xxo[i] >= mod->pat) { + mod->xxo[i] = mod->pat; + blank_pattern = 1; + } + } + stored_patterns = mod->pat; + if(blank_pattern) + mod->pat++; + + mod->trk = mod->pat * mod->chn; + mod->len = i; + + D_(D_INFO "Module length: %d", mod->len); + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", stored_patterns); + + if(blank_pattern) { + if (libxmp_alloc_pattern_tracks(mod, stored_patterns, 64) < 0) + return -1; + } + + for (i = 0; i < stored_patterns; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + + if (hio_error(f)) + return -1; + + for (j = 0; j < 64; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + b = hio_read8(f); + if (b == 251 || b == 252) + continue; /* Empty note */ + + if (b == 253) { + event->note = XMP_KEY_OFF; + continue; /* Key off */ + } + + if (b == 254) + event->note = XMP_KEY_OFF; + else if (b == 255) + event->note = 0; + else + event->note = 1 + LSN(b) + 12 * (3 + MSN(b)); + + b = hio_read8(f); + event->vol = b & 0x07; + event->ins = (b & 0xf8) >> 3; + + b = hio_read8(f); + event->vol += (b & 0xf0) >> 1; + if (version >= 200) { + event->vol = (event->vol > 0x40) ? 0 : event->vol + 1; + } else { + if (event->vol > 0) { + event->vol = (event->vol > 0x40) ? 1 : event->vol + 1; + } + } + + event->fxt = fx[LSN(b)]; + event->fxp = hio_read8(f); + switch (event->fxt) { + case FX_SPEED: + event->fxp = (version < 221) ? LSN(event->fxp / 10) : MSN(event->fxp); + break; + case FX_NONE: + event->fxp = event->fxt = 0; + break; + } + } + } + } + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (!sfh.ins[i].volume || !sfh.ins[i].length) { + mod->xxi[i].nsm = 0; + continue; + } + + if (sfh.type == STM_TYPE_SONG) { + HIO_HANDLE *s; + char sn[XMP_MAXPATH]; + char tmpname[32]; + const char *instname = mod->xxi[i].name; + + if (!instname[0] || !m->dirname) + continue; + + if (libxmp_copy_name_for_fopen(tmpname, instname, 32)) + continue; + + snprintf(sn, XMP_MAXPATH, "%s%s", m->dirname, tmpname); + s = hio_open(sn, "rb"); + if (s != NULL) { + if (libxmp_load_sample(m, s, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) { + hio_close(s); + return -1; + } + hio_close(s); + } + } else { + hio_seek(f, start + (sfh.ins[i].rsvd1 << 4), SEEK_SET); + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + } + + m->quirk |= QUIRK_VSALL | QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/stx_load.c b/thirdparty/libxmp/src/loaders/stx_load.c new file mode 100644 index 000000000..56a7a668a --- /dev/null +++ b/thirdparty/libxmp/src/loaders/stx_load.c @@ -0,0 +1,385 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From the STMIK 0.2 documentation: + * + * "The STMIK uses a special [Scream Tracker] beta-V3.0 module format. + * Due to the module formats beta nature, the current STMIK uses a .STX + * extension instead of the normal .STM. I'm not intending to do a + * STX->STM converter, so treat STX as the format to be used in finished + * programs, NOT as a format to be used in distributing modules. A program + * called STM2STX is included, and it'll convert STM modules to the STX + * format for usage in your own programs." + * + * Tested using "Future Brain" from Mental Surgery by Future Crew and + * STMs converted with STM2STX. + */ + +#include "loader.h" +#include "s3m.h" +#include "../period.h" + +struct stx_file_header { + uint8 name[20]; /* Song name */ + uint8 magic[8]; /* !Scream! */ + uint16 psize; /* Pattern 0 size? */ + uint16 unknown1; /* ?! */ + uint16 pp_pat; /* Pointer to pattern table */ + uint16 pp_ins; /* Pattern to instrument table */ + uint16 pp_chn; /* Pointer to channel table (?) */ + uint16 unknown2; + uint16 unknown3; + uint8 gvol; /* Global volume */ + uint8 tempo; /* Playback tempo */ + uint16 unknown4; + uint16 unknown5; + uint16 patnum; /* Number of patterns */ + uint16 insnum; /* Number of instruments */ + uint16 ordnum; /* Number of orders */ + uint16 unknown6; /* Flags? */ + uint16 unknown7; /* Version? */ + uint16 unknown8; /* Ffi? */ + uint8 magic2[4]; /* 'SCRM' */ +}; + +struct stx_instrument_header { + uint8 type; /* Instrument type */ + uint8 dosname[13]; /* DOS file name */ + uint16 memseg; /* Pointer to sample data */ + uint32 length; /* Length */ + uint32 loopbeg; /* Loop begin */ + uint32 loopend; /* Loop end */ + uint8 vol; /* Volume */ + uint8 rsvd1; /* Reserved */ + uint8 pack; /* Packing type (not used) */ + uint8 flags; /* Loop/stereo/16bit samples flags */ + uint16 c2spd; /* C 4 speed */ + uint16 rsvd2; /* Reserved */ + uint8 rsvd3[4]; /* Reserved */ + uint16 int_gp; /* Internal - GUS pointer */ + uint16 int_512; /* Internal - SB pointer */ + uint32 int_last; /* Internal - SB index */ + uint8 name[28]; /* Instrument name */ + uint8 magic[4]; /* Reserved (for 'SCRS') */ +}; + +static int stx_test(HIO_HANDLE *, char *, const int); +static int stx_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_stx = { + "STMIK 0.2", + stx_test, + stx_load +}; + +static int stx_test(HIO_HANDLE * f, char *t, const int start) +{ + char buf[8]; + + hio_seek(f, start + 20, SEEK_SET); + if (hio_read(buf, 1, 8, f) < 8) + return -1; + if (memcmp(buf, "!Scream!", 8) && memcmp(buf, "BMOD2STM", 8)) + return -1; + + hio_seek(f, start + 60, SEEK_SET); + if (hio_read(buf, 1, 4, f) < 4) + return -1; + if (memcmp(buf, "SCRM", 4)) + return -1; + + hio_seek(f, start + 0, SEEK_SET); + libxmp_read_title(f, t, 20); + + return 0; +} + +#define FX_NONE 0xff + +static const uint8 fx[11] = { + FX_NONE, + FX_SPEED, + FX_JUMP, + FX_BREAK, + FX_VOLSLIDE, + FX_PORTA_DN, + FX_PORTA_UP, + FX_TONEPORTA, + FX_VIBRATO, + FX_TREMOR, + FX_ARPEGGIO +}; + +static int stx_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int c, r, i, broken = 0; + struct xmp_event *event = 0, dummy; + struct stx_file_header sfh; + struct stx_instrument_header sih; + uint8 n, b; + uint16 x16; + int bmod2stm = 0; + uint16 *pp_ins; /* Parapointers to instruments */ + uint16 *pp_pat; /* Parapointers to patterns */ + + LOAD_INIT(); + + hio_read(sfh.name, 20, 1, f); + hio_read(sfh.magic, 8, 1, f); + sfh.psize = hio_read16l(f); + sfh.unknown1 = hio_read16l(f); + sfh.pp_pat = hio_read16l(f); + sfh.pp_ins = hio_read16l(f); + sfh.pp_chn = hio_read16l(f); + sfh.unknown2 = hio_read16l(f); + sfh.unknown3 = hio_read16l(f); + sfh.gvol = hio_read8(f); + sfh.tempo = hio_read8(f); + sfh.unknown4 = hio_read16l(f); + sfh.unknown5 = hio_read16l(f); + sfh.patnum = hio_read16l(f); + sfh.insnum = hio_read16l(f); + sfh.ordnum = hio_read16l(f); + sfh.unknown6 = hio_read16l(f); + sfh.unknown7 = hio_read16l(f); + sfh.unknown8 = hio_read16l(f); + hio_read(sfh.magic2, 4, 1, f); + + /* Sanity check */ + if (sfh.patnum > 254 || sfh.insnum > MAX_INSTRUMENTS || sfh.ordnum > 256) + return -1; + + /* BMOD2STM does not convert pitch */ + if (!strncmp((char *)sfh.magic, "BMOD2STM", 8)) + bmod2stm = 1; + +#if 0 + if ((strncmp((char *)sfh.magic, "!Scream!", 8) && + !bmod2stm) || strncmp((char *)sfh.magic2, "SCRM", 4)) + return -1; +#endif + + mod->ins = sfh.insnum; + mod->pat = sfh.patnum; + mod->trk = mod->pat * mod->chn; + mod->len = sfh.ordnum; + mod->spd = MSN(sfh.tempo); + mod->smp = mod->ins; + m->c4rate = C4_NTSC_RATE; + + /* STM2STX 1.0 released with STMIK 0.2 converts STMs with the pattern + * length encoded in the first two bytes of the pattern (like S3M). + */ + hio_seek(f, start + (sfh.pp_pat << 4), SEEK_SET); + x16 = hio_read16l(f); + hio_seek(f, start + (x16 << 4), SEEK_SET); + x16 = hio_read16l(f); + if (x16 == sfh.psize) + broken = 1; + + strncpy(mod->name, (char *)sfh.name, 20); + if (bmod2stm) + libxmp_set_type(m, "BMOD2STM STX"); + else + snprintf(mod->type, XMP_NAME_SIZE, "STM2STX 1.%d", broken ? 0 : 1); + + MODULE_INFO(); + + pp_pat = (uint16 *) calloc(mod->pat, sizeof(uint16)); + if (pp_pat == NULL) + goto err; + + pp_ins = (uint16 *) calloc(mod->ins, sizeof(uint16)); + if (pp_ins == NULL) + goto err2; + + /* Read pattern pointers */ + hio_seek(f, start + (sfh.pp_pat << 4), SEEK_SET); + for (i = 0; i < mod->pat; i++) + pp_pat[i] = hio_read16l(f); + + /* Read instrument pointers */ + hio_seek(f, start + (sfh.pp_ins << 4), SEEK_SET); + for (i = 0; i < mod->ins; i++) + pp_ins[i] = hio_read16l(f); + + /* Skip channel table (?) */ + hio_seek(f, start + (sfh.pp_chn << 4) + 32, SEEK_SET); + + /* Read orders */ + for (i = 0; i < mod->len; i++) { + mod->xxo[i] = hio_read8(f); + hio_seek(f, 4, SEEK_CUR); + } + + if (libxmp_init_instrument(m) < 0) + goto err3; + + /* Read and convert instruments and samples */ + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + goto err3; + + hio_seek(f, start + (pp_ins[i] << 4), SEEK_SET); + + sih.type = hio_read8(f); + hio_read(sih.dosname, 13, 1, f); + sih.memseg = hio_read16l(f); + sih.length = hio_read32l(f); + sih.loopbeg = hio_read32l(f); + sih.loopend = hio_read32l(f); + sih.vol = hio_read8(f); + sih.rsvd1 = hio_read8(f); + sih.pack = hio_read8(f); + sih.flags = hio_read8(f); + sih.c2spd = hio_read16l(f); + sih.rsvd2 = hio_read16l(f); + hio_read(sih.rsvd3, 4, 1, f); + sih.int_gp = hio_read16l(f); + sih.int_512 = hio_read16l(f); + sih.int_last = hio_read32l(f); + hio_read(sih.name, 28, 1, f); + hio_read(sih.magic, 4, 1, f); + if (hio_error(f)) { + D_(D_CRIT "read error at instrument %d", i); + goto err3; + } + + mod->xxs[i].len = sih.length; + mod->xxs[i].lps = sih.loopbeg; + mod->xxs[i].lpe = sih.loopend; + if (mod->xxs[i].lpe == 0xffff) + mod->xxs[i].lpe = 0; + mod->xxs[i].flg = mod->xxs[i].lpe > 0 ? XMP_SAMPLE_LOOP : 0; + mod->xxi[i].sub[0].vol = sih.vol; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + mod->xxi[i].nsm = 1; + + libxmp_instrument_name(mod, i, sih.name, 12); + + D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c V%02x %5d\n", i, + mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, + mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, sih.c2spd); + + libxmp_c2spd_to_note(sih.c2spd, &mod->xxi[i].sub[0].xpo, + &mod->xxi[i].sub[0].fin); + } + + if (libxmp_init_pattern(mod) < 0) + goto err3; + + /* Read and convert patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + goto err3; + + if (pp_pat[i] == 0) + continue; + + hio_seek(f, start + (pp_pat[i] << 4), SEEK_SET); + if (broken) + hio_seek(f, 2, SEEK_CUR); + + for (r = 0; r < 64;) { + b = hio_read8(f); + if (hio_error(f)) { + goto err3; + } + + if (b == S3M_EOR) { + r++; + continue; + } + + c = b & S3M_CH_MASK; + event = c >= mod->chn ? &dummy : &EVENT(i, c, r); + + if (b & S3M_NI_FOLLOW) { + n = hio_read8(f); + + switch (n) { + case 255: + n = 0; + break; /* Empty note */ + case 254: + n = XMP_KEY_OFF; + break; /* Key off */ + default: + n = 37 + 12 * MSN(n) + LSN(n); + } + + event->note = n; + event->ins = hio_read8(f);; + } + + if (b & S3M_VOL_FOLLOWS) { + event->vol = hio_read8(f) + 1; + } + + if (b & S3M_FX_FOLLOWS) { + int t = hio_read8(f); + int p = hio_read8(f); + if (t < ARRAY_SIZE(fx)) { + event->fxt = fx[t]; + event->fxp = p; + switch (event->fxt) { + case FX_SPEED: + event->fxp = MSN(event->fxp); + break; + case FX_NONE: + event->fxp = event->fxt = 0; + break; + } + } + } + } + } + + free(pp_ins); + free(pp_pat); + + /* Read samples */ + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + goto err; + } + + m->quirk |= QUIRK_VSALL | QUIRKS_ST3; + m->read_event_type = READ_EVENT_ST3; + + return 0; + +err3: + free(pp_ins); +err2: + free(pp_pat); +err: + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/sym_load.c b/thirdparty/libxmp/src/loaders/sym_load.c new file mode 100644 index 000000000..3cc062bea --- /dev/null +++ b/thirdparty/libxmp/src/loaders/sym_load.c @@ -0,0 +1,632 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" +#include "lzw.h" + + +static int sym_test(HIO_HANDLE *, char *, const int); +static int sym_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_sym = { + "Digital Symphony", + sym_test, + sym_load +}; + +static int sym_test(HIO_HANDLE *f, char *t, const int start) +{ + uint32 a, b; + int i, ver; + + a = hio_read32b(f); + b = hio_read32b(f); + + if (a != 0x02011313 || b != 0x1412010B) /* BASSTRAK */ + return -1; + + ver = hio_read8(f); + + /* v1 files are the same as v0 but may contain strange compression + * formats. Deal with that problem later if it arises. + */ + if (ver > 1) { + return -1; + } + + hio_read8(f); /* chn */ + hio_read16l(f); /* pat */ + hio_read16l(f); /* trk */ + hio_read24l(f); /* infolen */ + + for (i = 0; i < 63; i++) { + if (~hio_read8(f) & 0x80) + hio_read24l(f); + } + + libxmp_read_title(f, t, hio_read8(f)); + + return 0; +} + + +static void fix_effect(struct xmp_event *e, int parm) +{ + switch (e->fxt) { + case 0x00: /* 00 xyz Normal play or Arpeggio + Volume Slide Up */ + case 0x01: /* 01 xyy Slide Up + Volume Slide Up */ + case 0x02: /* 02 xyy Slide Down + Volume Slide Up */ + if (parm & 0xff) { + e->fxp = parm & 0xff; + } else { + e->fxt = 0; + } + if (parm >> 8) { + e->f2t = FX_VOLSLIDE_UP; + e->f2p = parm >> 8; + } + break; + case 0x03: /* 03 xyy Tone Portamento */ + case 0x04: /* 04 xyz Vibrato */ + case 0x07: /* 07 xyz Tremolo */ + e->fxp = parm; + break; + case 0x05: /* 05 xyz Tone Portamento + Volume Slide */ + case 0x06: /* 06 xyz Vibrato + Volume Slide */ + e->fxp = parm; + if (!parm) + e->fxt -= 2; + break; + case 0x09: /* 09 xxx Set Sample Offset */ + e->fxp = parm >> 1; + e->f2t = FX_HIOFFSET; + e->f2p = parm >> 9; + break; + case 0x0a: /* 0A xyz Volume Slide + Fine Slide Up */ + if (parm & 0xff) { + e->fxp = parm & 0xff; + } else { + e->fxt = 0; + } + e->f2t = FX_EXTENDED; + e->f2p = (EX_F_PORTA_UP << 4) | ((parm & 0xf00) >> 8); + break; + case 0x0b: /* 0B xxx Position Jump */ + case 0x0c: /* 0C xyy Set Volume */ + e->fxp = parm; + break; + case 0x0d: /* 0D xyy Pattern Break */ + e->fxt = FX_IT_BREAK; + e->fxp = (parm & 0xff) < 0x40 ? parm : 0; + break; + case 0x0f: /* 0F xxx Set Speed */ + if (parm) { + e->fxt = FX_S3M_SPEED; + e->fxp = MIN(parm, 255); + } else { + e->fxt = 0; + } + break; + case 0x13: /* 13 xxy Glissando Control */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_GLISS << 4) | (parm & 0x0f); + break; + case 0x14: /* 14 xxy Set Vibrato Waveform */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_VIBRATO_WF << 4) | (parm & 0x0f); + break; + case 0x15: /* 15 xxy Set Fine Tune */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_FINETUNE << 4) | (parm & 0x0f); + break; + case 0x16: /* 16 xxx Jump to Loop */ + /* TODO: 16, 19 should be able to support larger params. */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_PATTERN_LOOP << 4) | MIN(parm, 0x0f); + break; + case 0x17: /* 17 xxy Set Tremolo Waveform */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_TREMOLO_WF << 4) | (parm & 0x0f); + break; + case 0x19: /* 19 xxx Retrig Note */ + if (parm < 0x10) { + e->fxt = FX_EXTENDED; + e->fxp = (EX_RETRIG << 4) | (parm & 0x0f); + } else { + /* ignore */ + e->fxt = 0; + } + break; + case 0x11: /* 11 xyy Fine Slide Up + Fine Volume Slide Up */ + case 0x12: /* 12 xyy Fine Slide Down + Fine Volume Slide Up */ + case 0x1a: /* 1A xyy Fine Slide Up + Fine Volume Slide Down */ + case 0x1b: /* 1B xyy Fine Slide Down + Fine Volume Slide Down */ + { + uint8 pitch_effect = ((e->fxt == 0x11 || e->fxt == 0x1a) ? + FX_F_PORTA_UP : FX_F_PORTA_DN); + uint8 vol_effect = ((e->fxt == 0x11 || e->fxt == 0x12) ? + FX_F_VSLIDE_UP : FX_F_VSLIDE_DN); + + if (parm & 0xff) { + e->fxt = pitch_effect; + e->fxp = parm & 0xff; + } else + e->fxt = 0; + if (parm >> 8) { + e->f2t = vol_effect; + e->f2p = parm >> 8; + } + break; + } + case 0x1c: /* 1C xxx Note Cut */ + /* TODO: 1c, 1d, 1e should be able to support larger params. */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_CUT << 4) | MIN(parm, 0x0f); + break; + case 0x1d: /* 1D xxx Note Delay */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_DELAY << 4) | MIN(parm, 0x0f); + break; + case 0x1e: /* 1E xxx Pattern Delay */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_PATT_DELAY << 4) | MIN(parm, 0x0f); + break; + case 0x1f: /* 1F xxy Invert Loop */ + e->fxt = FX_EXTENDED; + e->fxp = (EX_INVLOOP << 4) | (parm & 0xf); + break; + case 0x20: /* 20 xyz Normal play or Arpeggio + Volume Slide Down */ + e->fxt = FX_ARPEGGIO; + e->fxp = parm & 0xff; + if (parm >> 8) { + e->f2t = FX_VOLSLIDE_DN; + e->f2p = parm >> 8; + } + break; + case 0x21: /* 21 xyy Slide Up + Volume Slide Down */ + if (parm & 0xff) { + e->fxt = FX_PORTA_UP; + e->fxp = parm & 0xff; + } else { + e->fxt = 0; + } + if (parm >> 8) { + e->f2t = FX_VOLSLIDE_DN; + e->f2p = parm >> 8; + } + break; + case 0x22: /* 22 xyy Slide Down + Volume Slide Down */ + if (parm & 0xff) { + e->fxt = FX_PORTA_DN; + e->fxp = parm & 0xff; + } else { + e->fxt = 0; + } + if (parm >> 8) { + e->f2t = FX_VOLSLIDE_DN; + e->f2p = parm >> 8; + } + break; + case 0x2a: /* 2A xyz Volume Slide + Fine Slide Down */ + if (parm & 0xff) { + e->fxt = FX_VOLSLIDE; + e->fxp = parm & 0xff; + } else { + e->fxt = 0; + } + e->f2t = FX_EXTENDED; + e->f2p = (EX_F_PORTA_DN << 4) | (parm >> 8); + break; + case 0x2b: /* 2B xyy Line Jump */ + e->fxt = FX_LINE_JUMP; + e->fxp = (parm < 0x40) ? parm : 0; + break; + case 0x2f: /* 2F xxx Set Tempo */ + if (parm) { + parm = (parm + 4) >> 3; /* round to nearest */ + CLAMP(parm, XMP_MIN_BPM, 255); + e->fxt = FX_S3M_BPM; + e->fxp = parm; + } else { + e->fxt = 0; + } + break; + case 0x30: /* 30 xxy Set Stereo */ + e->fxt = FX_SETPAN; + if (parm & 7) { + /* !Tracker-style panning: 1=left, 4=center, 7=right. */ + if (!(parm & 8)) { + e->fxp = 42 * ((parm & 7) - 1) + 2; + } else { + e->fxt = 0; + } + } else { + parm >>= 4; + if (parm < 128) { + e->fxp = parm + 128; + } else if (parm > 128) { + e->fxp = 255 - parm; + } else { + e->fxt = 0; + } + } + break; + case 0x31: /* 31 xxx Song Upcall */ + e->fxt = 0; + break; + case 0x32: /* 32 xxx Unset Sample Repeat */ + e->fxt = FX_KEYOFF; + e->fxp = 0; + break; + default: + e->fxt = 0; + } +} + +static uint32 readptr32l(uint8 *p) +{ + uint32 a, b, c, d; + + a = *p++; + b = *p++; + c = *p++; + d = *p++; + + return (d << 24) | (c << 16) | (b << 8) | a; +} + +static uint32 readptr16l(uint8 *p) +{ + uint32 a, b; + + a = *p++; + b = *p++; + + return (b << 8) | a; +} + +static int sym_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + int i, j; + int /*ver,*/ infolen, sn[64]; + uint32 a, b; + uint8 *buf; + int size, ret; + int tracks_size; + int sequence_size; + int max_sample_size = 1; + uint8 allowed_effects[8]; + + LOAD_INIT(); + + hio_seek(f, 8, SEEK_CUR); /* BASSTRAK */ + + /*ver =*/ hio_read8(f); + libxmp_set_type(m, "Digital Symphony"); + + mod->chn = hio_read8(f); + mod->len = mod->pat = hio_read16l(f); + + /* Sanity check */ + if (mod->chn < 1 || mod->chn > 8 || mod->pat > XMP_MAX_MOD_LENGTH) + return -1; + + mod->trk = hio_read16l(f); /* Symphony patterns are actually tracks */ + infolen = hio_read24l(f); + + /* Sanity check - track 0x1000 is used to indicate the empty track. */ + if (mod->trk > 0x1000) + return -1; + + mod->ins = mod->smp = 63; + + if (libxmp_init_instrument(m) < 0) + return -1; + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + sn[i] = hio_read8(f); /* sample name length */ + + if (~sn[i] & 0x80) { + mod->xxs[i].len = hio_read24l(f) << 1; + mod->xxi[i].nsm = 1; + + /* Sanity check */ + if (mod->xxs[i].len > 0x80000) + return -1; + + if (max_sample_size < mod->xxs[i].len) + max_sample_size = mod->xxs[i].len; + } + } + + a = hio_read8(f); /* track name length */ + if (a > 32) { + hio_read(mod->name, 1, 32, f); + hio_seek(f, a - 32, SEEK_SET); + } else { + hio_read(mod->name, 1, a, f); + } + + hio_read(allowed_effects, 1, 8, f); + + MODULE_INFO(); + + mod->trk++; /* alloc extra empty track */ + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Determine the required size of temporary buffer and allocate it now. */ + /* Uncompressed sequence size */ + sequence_size = mod->len * mod->chn * 2; + if (sequence_size > max_sample_size) + max_sample_size = sequence_size; + + tracks_size = 64 * (mod->trk - 1) * 4; /* Uncompressed tracks size */ + if (tracks_size > max_sample_size) + max_sample_size = tracks_size; + + if ((buf = (uint8 *)malloc(max_sample_size)) == NULL) + return -1; + + /* Sequence */ + a = hio_read8(f); /* packing */ + + if (a != 0 && a != 1) + goto err; + + D_(D_INFO "Packed sequence: %s", a ? "yes" : "no"); + + if (a) { + if (libxmp_read_lzw(buf, sequence_size, sequence_size, LZW_FLAGS_SYM, f) < 0) + goto err; + } else { + if (hio_read(buf, 1, sequence_size, f) != sequence_size) + goto err; + } + + for (i = 0; i < mod->len; i++) { /* len == pat */ + if (libxmp_alloc_pattern(mod, i) < 0) + goto err; + + mod->xxp[i]->rows = 64; + + for (j = 0; j < mod->chn; j++) { + int idx = 2 * (i * mod->chn + j); + int t = readptr16l(&buf[idx]); + + if (t == 0x1000) { + /* empty trk */ + t = mod->trk - 1; + } else if (t >= mod->trk - 1) { + /* Sanity check */ + goto err; + } + + mod->xxp[i]->index[j] = t; + } + mod->xxo[i] = i; + } + + /* Read and convert patterns */ + + D_(D_INFO "Stored tracks: %d", mod->trk - 1); + + /* Patterns are stored in blocks of up to 2000 patterns. If there are + * more than 2000 patterns, they need to be read in multiple passes. + * + * See 4096_patterns.dsym. + */ + for (i = 0; i < tracks_size; i += 4 * 64 * 2000) { + int blk_size = MIN(tracks_size - i, 4 * 64 * 2000); + + a = hio_read8(f); + + if (a != 0 && a != 1) + goto err; + + D_(D_INFO "Packed tracks: %s", a ? "yes" : "no"); + + if (a) { + if (libxmp_read_lzw(buf + i, blk_size, blk_size, LZW_FLAGS_SYM, f) < 0) + goto err; + } else { + if (hio_read(buf + i, 1, blk_size, f) != blk_size) + goto err; + } + } + + for (i = 0; i < mod->trk - 1; i++) { + if (libxmp_alloc_track(mod, i, 64) < 0) + goto err; + + for (j = 0; j < mod->xxt[i]->rows; j++) { + int parm; + + event = &mod->xxt[i]->event[j]; + + b = readptr32l(&buf[4 * (i * 64 + j)]); + event->note = b & 0x0000003f; + if (event->note) + event->note += 48; + event->ins = (b & 0x00001fc0) >> 6; + event->fxt = (b & 0x000fc000) >> 14; + parm = (b & 0xfff00000) >> 20; + + if (allowed_effects[event->fxt >> 3] & (1 << (event->fxt & 7))) { + fix_effect(event, parm); + } else { + event->fxt = 0; + } + } + } + + /* Extra track */ + if (libxmp_alloc_track(mod, i, 64) < 0) + goto err; + + /* Load and convert instruments */ + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + struct xmp_sample *xxs = &mod->xxs[i]; + struct xmp_instrument *xxi = &mod->xxi[i]; + struct extra_sample_data *xtra = &m->xtra[i]; + uint8 namebuf[128]; + + memset(namebuf, 0, sizeof(namebuf)); + hio_read(namebuf, 1, sn[i] & 0x7f, f); + libxmp_instrument_name(mod, i, namebuf, 32); + + if (~sn[i] & 0x80) { + int looplen; + + xtra->sus = hio_read24l(f) << 1; + looplen = hio_read24l(f) << 1; + xtra->sue = xtra->sus + looplen; + if (xtra->sus < xxs->len && xtra->sus < xtra->sue && + xtra->sue <= xxs->len && looplen > 2) + xxs->flg |= XMP_SAMPLE_SLOOP; + xxi->sub[0].vol = hio_read8(f); + xxi->sub[0].pan = 0x80; + /* finetune adjusted comparing DSym and S3M versions + * of "inside out" */ + xxi->sub[0].fin = (int8)(hio_read8(f) << 4); + xxi->sub[0].sid = i; + } + + D_(D_INFO "[%2X] %-22.22s %05x %05x %05x %c V%02x %+03d", + i, xxi->name, xxs->len, + xtra->sus, xtra->sue, + xxs->flg & XMP_SAMPLE_SLOOP ? 'L' : ' ', + xxi->sub[0].vol, xxi->sub[0].fin); + + if (sn[i] & 0x80 || xxs->len == 0) + continue; + + a = hio_read8(f); + + switch (a) { + case 0: /* Signed 8-bit, logarithmic. */ + D_(D_INFO "%27s VIDC", ""); + ret = libxmp_load_sample(m, f, SAMPLE_FLAG_VIDC, + xxs, NULL); + break; + + case 1: /* LZW compressed signed 8-bit delta, linear. */ + D_(D_INFO "%27s LZW", ""); + size = xxs->len; + + if (libxmp_read_lzw(buf, size, size, LZW_FLAGS_SYM, f) < 0) + goto err; + + ret = libxmp_load_sample(m, NULL, + SAMPLE_FLAG_NOLOAD | SAMPLE_FLAG_DIFF, + xxs, buf); + break; + + case 2: /* Signed 8-bit, linear. */ + D_(D_INFO "%27s 8-bit", ""); + ret = libxmp_load_sample(m, f, 0, xxs, NULL); + break; + + case 3: /* Signed 16-bit, linear. */ + D_(D_INFO "%27s 16-bit", ""); + xxs->flg |= XMP_SAMPLE_16BIT; + ret = libxmp_load_sample(m, f, 0, xxs, NULL); + break; + + case 4: /* Sigma-delta compressed unsigned 8-bit, linear. */ + D_(D_INFO "%27s Sigma-delta", ""); + size = xxs->len; + if (libxmp_read_sigma_delta(buf, size, size, f) < 0) + goto err; + + ret = libxmp_load_sample(m, NULL, + SAMPLE_FLAG_NOLOAD | SAMPLE_FLAG_UNS, + xxs, buf); + break; + + case 5: /* Sigma-delta compressed signed 8-bit, logarithmic. */ + D_(D_INFO "%27s Sigma-delta VIDC", ""); + size = xxs->len; + if (libxmp_read_sigma_delta(buf, size, size, f) < 0) + goto err; + + /* This uses a bit packing that isn't either mu-law or + * normal Archimedes VIDC. Convert to the latter... */ + for (j = 0; j < size; j++) { + uint8 t = (buf[j] < 128) ? ~buf[j] : buf[j]; + buf[j] = (buf[j] >> 7) | (t << 1); + } + + ret = libxmp_load_sample(m, NULL, + SAMPLE_FLAG_NOLOAD | SAMPLE_FLAG_VIDC, + xxs, buf); + break; + + default: + D_(D_CRIT "unknown sample type %d @ %ld\n", a, hio_tell(f)); + goto err; + } + + if (ret < 0) + goto err; + } + + /* Information text */ + if (infolen > 0) { + a = hio_read8(f); /* Packing */ + + m->comment = (char *)malloc(infolen + 1); + if (m->comment) { + m->comment[infolen] = '\0'; + if (a) { + ret = libxmp_read_lzw(m->comment, infolen, infolen, LZW_FLAGS_SYM, f); + } else { + size = hio_read(m->comment, 1, infolen, f); + ret = (size < infolen) ? -1 : 0; + } + if (ret < 0) { + free(m->comment); + m->comment = NULL; + } + } + } + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = DEFPAN((((i + 3) / 2) % 2) * 0xff); + } + + m->quirk = QUIRK_VIBALL | QUIRK_KEYOFF | QUIRK_INVLOOP; + + free(buf); + return 0; + +err: + free(buf); + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/ult_load.c b/thirdparty/libxmp/src/loaders/ult_load.c new file mode 100644 index 000000000..2ed88d748 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/ult_load.c @@ -0,0 +1,364 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Based on the format description by FreeJack of The Elven Nation + * + * The loader recognizes four subformats: + * - MAS_UTrack_V001: Ultra Tracker version < 1.4 + * - MAS_UTrack_V002: Ultra Tracker version 1.4 + * - MAS_UTrack_V003: Ultra Tracker version 1.5 + * - MAS_UTrack_V004: Ultra Tracker version 1.6 + */ + +#include "loader.h" +#include "../period.h" + + +static int ult_test (HIO_HANDLE *, char *, const int); +static int ult_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_ult = { + "Ultra Tracker", + ult_test, + ult_load +}; + +static int ult_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[15]; + + if (hio_read(buf, 1, 15, f) < 15) + return -1; + + if (memcmp(buf, "MAS_UTrack_V00", 14)) + return -1; + + if (buf[14] < '1' || buf[14] > '4') + return -1; + + libxmp_read_title(f, t, 32); + + return 0; +} + + +struct ult_header { + uint8 magic[15]; /* 'MAS_UTrack_V00x' */ + uint8 name[32]; /* Song name */ + uint8 msgsize; /* ver < 1.4: zero */ +}; + +struct ult_header2 { + uint8 order[256]; /* Orders */ + uint8 channels; /* Number of channels - 1 */ + uint8 patterns; /* Number of patterns - 1 */ +}; + +struct ult_instrument { + uint8 name[32]; /* Instrument name */ + uint8 dosname[12]; /* DOS file name */ + uint32 loop_start; /* Loop start */ + uint32 loopend; /* Loop end */ + uint32 sizestart; /* Sample size is sizeend - sizestart */ + uint32 sizeend; + uint8 volume; /* Volume (log; ver >= 1.4 linear) */ + uint8 bidiloop; /* Sample loop flags */ + int16 finetune; /* Finetune */ + uint16 c2spd; /* C2 frequency */ +}; + +struct ult_event { + /* uint8 note; */ + uint8 ins; + uint8 fxt; /* MSN = fxt, LSN = f2t */ + uint8 f2p; /* Secondary comes first -- little endian! */ + uint8 fxp; +}; + + +static int ult_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j, k, ver, cnt; + struct xmp_event *event; + struct ult_header ufh; + struct ult_header2 ufh2; + struct ult_instrument uih; + struct ult_event ue; + const char *verstr[4] = { "< 1.4", "1.4", "1.5", "1.6" }; + + uint8 x8; + + LOAD_INIT(); + + hio_read(ufh.magic, 15, 1, f); + hio_read(ufh.name, 32, 1, f); + ufh.msgsize = hio_read8(f); + + ver = ufh.magic[14] - '0'; + + strncpy(mod->name, (char *)ufh.name, 32); + mod->name[32] = '\0'; + libxmp_set_type(m, "Ultra Tracker %s ULT V%03d", verstr[ver - 1], ver); + + m->c4rate = C4_NTSC_RATE; + + MODULE_INFO(); + + hio_seek(f, ufh.msgsize * 32, SEEK_CUR); + + mod->ins = mod->smp = hio_read8(f); + /* mod->flg |= XXM_FLG_LINEAR; */ + + /* Read and convert instruments */ + + if (libxmp_init_instrument(m) < 0) + return -1; + + D_(D_INFO "Instruments: %d", mod->ins); + + for (i = 0; i < mod->ins; i++) { + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + return -1; + + hio_read(uih.name, 32, 1, f); + hio_read(uih.dosname, 12, 1, f); + uih.loop_start = hio_read32l(f); + uih.loopend = hio_read32l(f); + uih.sizestart = hio_read32l(f); + uih.sizeend = hio_read32l(f); + uih.volume = hio_read8(f); + uih.bidiloop = hio_read8(f); + uih.c2spd = (ver >= 4) ? hio_read16l(f) : 0; /* Incorrect in ult_form.txt */ + uih.finetune = hio_read16l(f); + if (hio_error(f)) { + D_(D_CRIT "read error at instrument %d", i); + return -1; + } + + /* Sanity check: + * "[SizeStart] seems to tell UT how to load the sample into the GUS's + * onboard memory." The maximum supported GUS RAM is 16 MB (PnP). + * Samples also can't cross 256k boundaries. In practice it seems like + * nothing ever goes over 1 MB, the maximum on most GUS cards. + */ + if (uih.sizestart > uih.sizeend || uih.sizeend > (16 << 20) || + uih.sizeend - uih.sizestart > (256 << 10)) { + D_(D_CRIT "invalid sample %d sizestart/sizeend", i); + return -1; + } + + mod->xxs[i].len = uih.sizeend - uih.sizestart; + mod->xxs[i].lps = uih.loop_start; + mod->xxs[i].lpe = uih.loopend; + + if (mod->xxs[i].len > 0) + mod->xxi[i].nsm = 1; + + /* BiDi Loop : (Bidirectional Loop) + * + * UT takes advantage of the Gus's ability to loop a sample in + * several different ways. By setting the Bidi Loop, the sample can + * be played forward or backwards, looped or not looped. The Bidi + * variable also tracks the sample resolution (8 or 16 bit). + * + * The following table shows the possible values of the Bidi Loop. + * Bidi = 0 : No looping, forward playback, 8bit sample + * Bidi = 4 : No Looping, forward playback, 16bit sample + * Bidi = 8 : Loop Sample, forward playback, 8bit sample + * Bidi = 12 : Loop Sample, forward playback, 16bit sample + * Bidi = 24 : Loop Sample, reverse playback 8bit sample + * Bidi = 28 : Loop Sample, reverse playback, 16bit sample + */ + + /* Claudio's note: I'm ignoring reverse playback for samples */ + + switch (uih.bidiloop) { + case 20: /* Type 20 is in seasons.ult */ + case 4: + mod->xxs[i].flg = XMP_SAMPLE_16BIT; + break; + case 8: + mod->xxs[i].flg = XMP_SAMPLE_LOOP; + break; + case 12: + mod->xxs[i].flg = XMP_SAMPLE_16BIT | XMP_SAMPLE_LOOP; + break; + case 24: + mod->xxs[i].flg = XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_REVERSE; + break; + case 28: + mod->xxs[i].flg = XMP_SAMPLE_16BIT | XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_REVERSE; + break; + } + +/* TODO: Add logarithmic volume support */ + mod->xxi[i].sub[0].vol = uih.volume; + mod->xxi[i].sub[0].pan = 0x80; + mod->xxi[i].sub[0].sid = i; + + libxmp_instrument_name(mod, i, uih.name, 24); + + D_(D_INFO "[%2X] %-32.32s %05x%c%05x %05x %c V%02x F%04x %5d", + i, mod->xxi[i].name, mod->xxs[i].len, + mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ', + mod->xxs[i].lps, mod->xxs[i].lpe, + mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + mod->xxi[i].sub[0].vol, uih.finetune, uih.c2spd); + + if (ver > 3) + libxmp_c2spd_to_note(uih.c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin); + } + + hio_read(ufh2.order, 256, 1, f); + ufh2.channels = hio_read8(f); + ufh2.patterns = hio_read8(f); + + if (hio_error(f)) { + return -1; + } + + for (i = 0; i < 256; i++) { + if (ufh2.order[i] == 0xff) + break; + mod->xxo[i] = ufh2.order[i]; + } + mod->len = i; + mod->chn = ufh2.channels + 1; + mod->pat = ufh2.patterns + 1; + mod->spd = 6; + mod->bpm = 125; + mod->trk = mod->chn * mod->pat; + + /* Sanity check */ + if (mod->chn > XMP_MAX_CHANNELS) { + return -1; + } + + for (i = 0; i < mod->chn; i++) { + if (ver >= 3) { + x8 = hio_read8(f); + mod->xxc[i].pan = 255 * x8 / 15; + } else { + mod->xxc[i].pan = DEFPAN((((i + 1) / 2) % 2) * 0xff); /* ??? */ + } + } + + if (libxmp_init_pattern(mod) < 0) + return -1; + + /* Read and convert patterns */ + + D_(D_INFO "Stored patterns: %d", mod->pat); + + /* Events are stored by channel */ + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + return -1; + } + + for (i = 0; i < mod->chn; i++) { + for (j = 0; j < 64 * mod->pat; ) { + cnt = 1; + x8 = hio_read8(f); /* Read note or repeat code (0xfc) */ + if (x8 == 0xfc) { + cnt = hio_read8(f); /* Read repeat count */ + x8 = hio_read8(f); /* Read note */ + } + if (hio_read(&ue, 1, 4, f) < 4) { /* Read rest of the event */ + D_(D_CRIT "read error at channel %d pos %d", i, j); + return -1; + } + + if (cnt == 0) + cnt++; + + if (j + cnt > 64 * mod->pat) { + D_(D_WARN "invalid track data packing"); + return -1; + } + + for (k = 0; k < cnt; k++, j++) { + event = &EVENT (j >> 6, i , j & 0x3f); + memset(event, 0, sizeof (struct xmp_event)); + if (x8) + event->note = x8 + 36; + event->ins = ue.ins; + event->fxt = MSN (ue.fxt); + event->f2t = LSN (ue.fxt); + event->fxp = ue.fxp; + event->f2p = ue.f2p; + + switch (event->fxt) { + case 0x03: /* Tone portamento */ + event->fxt = FX_ULT_TPORTA; + break; + case 0x05: /* 'Special' effect */ + case 0x06: /* Reserved */ + event->fxt = event->fxp = 0; + break; + case 0x0b: /* Pan */ + event->fxt = FX_SETPAN; + event->fxp <<= 4; + break; + case 0x09: /* Sample offset */ +/* TODO: fine sample offset */ + event->fxp <<= 2; + break; + } + + switch (event->f2t) { + case 0x03: /* Tone portamento */ + event->f2t = FX_ULT_TPORTA; + break; + case 0x05: /* 'Special' effect */ + case 0x06: /* Reserved */ + event->f2t = event->f2p = 0; + break; + case 0x0b: /* Pan */ + event->f2t = FX_SETPAN; + event->f2p <<= 4; + break; + case 0x09: /* Sample offset */ +/* TODO: fine sample offset */ + event->f2p <<= 2; + break; + } + + } + } + } + + D_(D_INFO "Stored samples: %d", mod->smp); + + for (i = 0; i < mod->ins; i++) { + if (!mod->xxs[i].len) + continue; + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0) + return -1; + } + + m->volbase = 0x100; + + return 0; +} + diff --git a/thirdparty/libxmp/src/loaders/umx_load.c b/thirdparty/libxmp/src/loaders/umx_load.c new file mode 100644 index 000000000..20e91aaa0 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/umx_load.c @@ -0,0 +1,422 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include /* offsetof() */ +#include "loader.h" + +static int umx_test (HIO_HANDLE *, char *, const int); +static int umx_load (struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_umx = { + "Epic Games UMX", + umx_test, + umx_load +}; + +/* UPKG parsing partially based on Unreal Media Ripper (UMR) v0.3 + * by Andy Ward , with additional fixes & updates + * by O. Sezer - see git repo at https://github.com/sezero/umr.git + */ +typedef int32 fci_t; /* FCompactIndex */ + +#define UPKG_HDR_TAG 0x9e2a83c1 + +struct _genhist { /* for upkg versions >= 68 */ + int32 export_count; + int32 name_count; +}; + +struct upkg_hdr { + uint32 tag; /* UPKG_HDR_TAG */ + int32 file_version; + uint32 pkg_flags; + int32 name_count; /* number of names in name table (>= 0) */ + int32 name_offset; /* offset to name table (>= 0) */ + int32 export_count; /* num. exports in export table (>= 0) */ + int32 export_offset; /* offset to export table (>= 0) */ + int32 import_count; /* num. imports in export table (>= 0) */ + int32 import_offset; /* offset to import table (>= 0) */ + + /* number of GUIDs in heritage table (>= 1) and table's offset: + * only with versions < 68. */ + int32 heritage_count; + int32 heritage_offset; + /* with versions >= 68: a GUID, a dword for generation count + * and export_count and name_count dwords for each generation: */ + uint32 guid[4]; + int32 generation_count; +#define UPKG_HDR_SIZE 64 /* 64 bytes up until here */ + struct _genhist *gen; +}; +/* compile time assert for upkg_hdr size */ +typedef int _check_hdrsize[2 * (offsetof(struct upkg_hdr, gen) == UPKG_HDR_SIZE) - 1]; + +#define UMUSIC_IT 0 +#define UMUSIC_S3M 1 +#define UMUSIC_XM 2 +#define UMUSIC_MOD 3 +#define UMUSIC_WAV 4 +#define UMUSIC_MP2 5 + +static const char *mustype[] = { + "IT", "S3M", "XM", "MOD", + "WAV", "MP2", NULL +}; + +/* decode an FCompactIndex. + * original documentation by Tim Sweeney was at + * http://unreal.epicgames.com/Packages.htm + * also see Unreal Wiki: + * http://wiki.beyondunreal.com/Legacy:Package_File_Format/Data_Details + */ +static fci_t get_fci (const char *in, int *pos) +{ + int32 a; + int size; + + size = 1; + a = in[0] & 0x3f; + + if (in[0] & 0x40) { + size++; + a |= (in[1] & 0x7f) << 6; + + if (in[1] & 0x80) { + size++; + a |= (in[2] & 0x7f) << 13; + + if (in[2] & 0x80) { + size++; + a |= (in[3] & 0x7f) << 20; + + if (in[3] & 0x80) { + size++; + a |= (in[4] & 0x3f) << 27; + } + } + } + } + + if (in[0] & 0x80) + a = -a; + + *pos += size; + + return a; +} + +static int get_objtype (HIO_HANDLE *f, int32 ofs, int type) +{ + char sig[16]; +_retry: + memset(sig, 0, sizeof(sig)); + hio_seek(f, ofs, SEEK_SET); + hio_read(sig, 16, 1, f); + if (type == UMUSIC_IT) { + if (memcmp(sig, "IMPM", 4) == 0) + return UMUSIC_IT; + return -1; + } + if (type == UMUSIC_XM) { + if (memcmp(sig, "Extended Module:", 16) != 0) + return -1; + hio_read(sig, 16, 1, f); + if (sig[0] != ' ') return -1; + hio_read(sig, 16, 1, f); + if (sig[5] != 0x1a) return -1; + return UMUSIC_XM; + } + if (type == UMUSIC_MP2) { + unsigned char *p = (unsigned char *)sig; + uint16 u = ((p[0] << 8) | p[1]) & 0xFFFE; + if (u == 0xFFFC || u == 0xFFF4) + return UMUSIC_MP2; + return -1; + } + if (type == UMUSIC_WAV) { + if (memcmp(sig, "RIFF", 4) == 0 && memcmp(&sig[8], "WAVE", 4) == 0) + return UMUSIC_WAV; + return -1; + } + + hio_seek(f, ofs + 44, SEEK_SET); + hio_read(sig, 4, 1, f); + if (type == UMUSIC_S3M) { + if (memcmp(sig, "SCRM", 4) == 0) + return UMUSIC_S3M; + /*return -1;*/ + /* SpaceMarines.umx and Starseek.umx from Return to NaPali + * report as "s3m" whereas the actual music format is "it" */ + type = UMUSIC_IT; + goto _retry; + } + + hio_seek(f, ofs + 1080, SEEK_SET); + hio_read(sig, 4, 1, f); + if (type == UMUSIC_MOD) { + if (memcmp(sig, "M.K.", 4) == 0 || memcmp(sig, "M!K!", 4) == 0) + return UMUSIC_MOD; + return -1; + } + + return -1; +} + +static int read_export (HIO_HANDLE *f, const struct upkg_hdr *hdr, + int32 *ofs, int32 *objsize) +{ + char buf[40]; + int idx = 0, t; + + hio_seek(f, *ofs, SEEK_SET); + if (hio_read(buf, 4, 10, f) < 10) + return -1; + + if (hdr->file_version < 40) idx += 8; /* 00 00 00 00 00 00 00 00 */ + if (hdr->file_version < 60) idx += 16; /* 81 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00 */ + get_fci(&buf[idx], &idx); /* skip junk */ + t = get_fci(&buf[idx], &idx); /* type_name */ + if (hdr->file_version > 61) idx += 4; /* skip export size */ + *objsize = get_fci(&buf[idx], &idx); + *ofs += idx; /* offset for real data */ + + return t; /* return type_name index */ +} + +static int read_typname(HIO_HANDLE *f, const struct upkg_hdr *hdr, + int idx, char *out) +{ + int i, s; + long l; + char buf[64]; + + if (idx >= hdr->name_count) return -1; + memset(buf, 0, 64); + for (i = 0, l = 0; i <= idx; i++) { + if (hio_seek(f, hdr->name_offset + l, SEEK_SET) < 0) return -1; + if (!hio_read(buf, 1, 63, f)) return -1; + if (hdr->file_version >= 64) { + s = *(signed char *)buf; /* numchars *including* terminator */ + if (s <= 0) return -1; + l += s + 5; /* 1 for buf[0], 4 for int32 name_flags */ + } else { + l += (long)strlen(buf); + l += 5; /* 1 for terminator, 4 for int32 name_flags */ + } + } + + strcpy(out, (hdr->file_version >= 64)? &buf[1] : buf); + return 0; +} + +static void umx_strupr(char *str) +{ + while (*str) { + if (*str >= 'a' && *str <= 'z') { + *str -= ('a' - 'A'); + } + str++; + } +} + +static int probe_umx (HIO_HANDLE *f, const struct upkg_hdr *hdr, + int32 *ofs, int32 *objsize) +{ + int i, idx, t; + int32 s, pos; + long fsiz; + char buf[64]; + + idx = 0; + fsiz = hio_size(f); + + if (hdr->name_offset >= fsiz || + hdr->export_offset >= fsiz || + hdr->import_offset >= fsiz) { + D_(D_INFO "UMX: Illegal values in header.\n"); + return -1; + } + + /* Find the offset and size of the first IT, S3M or XM + * by parsing the exports table. The umx files should + * have only one export. Kran32.umx from Unreal has two, + * but both pointing to the same music. */ + if (hdr->export_offset >= fsiz) return -1; + memset(buf, 0, 64); + hio_seek(f, hdr->export_offset, SEEK_SET); + hio_read(buf, 1, 64, f); + + get_fci(&buf[idx], &idx); /* skip class_index */ + get_fci(&buf[idx], &idx); /* skip super_index */ + if (hdr->file_version >= 60) idx += 4; /* skip int32 package_index */ + get_fci(&buf[idx], &idx); /* skip object_name */ + idx += 4; /* skip int32 object_flags */ + + s = get_fci(&buf[idx], &idx); /* get serial_size */ + if (s <= 0) return -1; + pos = get_fci(&buf[idx],&idx); /* get serial_offset */ + if (pos < 0 || pos > fsiz - 40) return -1; + + if ((t = read_export(f, hdr, &pos, &s)) < 0) return -1; + if (s <= 0 || s > fsiz - pos) return -1; + + if (read_typname(f, hdr, t, buf) < 0) return -1; + umx_strupr(buf); + for (i = 0; mustype[i] != NULL; i++) { + if (!strcmp(buf, mustype[i])) { + t = i; + break; + } + } + if (mustype[i] == NULL) return -1; + if ((t = get_objtype(f, pos, t)) < 0) return -1; + + *ofs = pos; + *objsize = s; + return t; +} + +static int32 probe_header (HIO_HANDLE *f, struct upkg_hdr *hdr) +{ + hdr->tag = hio_read32l(f); + hdr->file_version = (int32) hio_read32l(f); + hdr->pkg_flags = hio_read32l(f); + hdr->name_count = (int32) hio_read32l(f); + hdr->name_offset = (int32) hio_read32l(f); + hdr->export_count = (int32) hio_read32l(f); + hdr->export_offset = (int32) hio_read32l(f); + hdr->import_count = (int32) hio_read32l(f); + hdr->import_offset = (int32) hio_read32l(f); + + if (hdr->tag != UPKG_HDR_TAG) { + D_(D_INFO "UMX: Unknown header tag 0x%x\n", hdr->tag); + return -1; + } + if (hdr->name_count < 0 || + hdr->export_count < 0 || + hdr->import_count < 0 || + hdr->name_offset < 36 || + hdr->export_offset < 36 || + hdr->import_offset < 36) { + D_(D_INFO "UMX: Illegal values in header.\n"); + return -1; + } + +#if 1 /* no need being overzealous */ + return 0; +#else + switch (hdr->file_version) { + case 35: case 37: /* Unreal beta - */ + case 40: case 41: /* 1998 */ + case 61:/* Unreal */ + case 62:/* Unreal Tournament */ + case 63:/* Return to NaPali */ + case 64:/* Unreal Tournament */ + case 66:/* Unreal Tournament */ + case 68:/* Unreal Tournament */ + case 69:/* Tactical Ops */ + case 75:/* Harry Potter and the Philosopher's Stone */ + case 76: /* mpeg layer II data */ + case 83:/* Mobile Forces */ + return 0; + } + + D_(D_INFO "UMX: Unknown upkg version %d\n", hdr->file_version); + return -1; +#endif /* #if 0 */ +} + +static int process_upkg (HIO_HANDLE *f, int32 *ofs, int32 *objsize) +{ + struct upkg_hdr header; + + memset(&header, 0, sizeof(header)); + if (probe_header(f, &header) < 0) return -1; + return probe_umx(f, &header, ofs, objsize); +} + +static int umx_test(HIO_HANDLE *f, char *t, const int start) +{ + int32 ofs, size; + int type; + + type = process_upkg(f, &ofs, &size); + (void) hio_error(f); + if (type < 0) { + return -1; + } + + ofs += start; /** FIXME? **/ + switch (type) { + case UMUSIC_IT: + hio_seek(f, ofs + 4, SEEK_SET); + libxmp_read_title(f, t, 26); + return 0; + case UMUSIC_S3M: + hio_seek(f, ofs, SEEK_SET); + libxmp_read_title(f, t, 28); + return 0; + case UMUSIC_XM: + hio_seek(f, ofs + 17, SEEK_SET); + libxmp_read_title(f, t, 20); + return 0; + case UMUSIC_MOD: + hio_seek(f, ofs, SEEK_SET); + libxmp_read_title(f, t, 20); + return 0; + } + + return -1; +} + +static int umx_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + int32 ofs, size; + int type; + + LOAD_INIT(); + + D_(D_INFO "Container type : Epic Games UMX"); + + type = process_upkg(f, &ofs, &size); + (void) hio_error(f); + if (type < 0) { + return -1; + } + + D_(D_INFO "UMX: %s data @ 0x%x, %d bytes\n", mustype[type], ofs, size); + + ofs += start; /** FIXME? **/ + hio_seek(f, ofs, SEEK_SET); + switch (type) { + case UMUSIC_IT: + return libxmp_loader_it.loader(m, f, ofs); + case UMUSIC_S3M: + return libxmp_loader_s3m.loader(m, f, ofs); + case UMUSIC_XM: + return libxmp_loader_xm.loader(m, f, ofs); + case UMUSIC_MOD: + return libxmp_loader_mod.loader(m, f, ofs); + } + + return -1; +} diff --git a/thirdparty/libxmp/src/loaders/voltable.c b/thirdparty/libxmp/src/loaders/voltable.c new file mode 100644 index 000000000..d997a5d23 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/voltable.c @@ -0,0 +1,120 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "loader.h" + +/* +from Tom Hargreaves +date Sat, Jan 16, 2010 at 9:38 PM + +the volume table for volume commands and the VIDC lookup table for +sample conversion are (should be) one and the same. A full-precision +version of the table is as follows: + +0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 +48 52 56 60 64 68 72 76 80 84 88 92 96 100 104 108 +112 120 128 136 144 152 160 168 176 184 192 200 208 216 224 232 +240 256 272 288 304 320 336 352 368 384 400 416 432 448 464 480 +496 528 560 592 624 656 688 720 752 784 816 848 880 912 944 976 +1008 1072 1136 1200 1264 1328 1392 1456 1520 1584 1648 1712 1776 1840 1904 1968 +2032 2160 2288 2416 2544 2672 2800 2928 3056 3184 3312 3440 3568 3696 3824 3952 */ + + +/* Claudio's note: this is a curve approximation using linear segments, + * so I'll perform linear interpolation to have all 256 values + */ + +int const libxmp_arch_vol_table[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, + 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0f, 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, + 0x13, 0x13, 0x14, 0x14, 0x15, 0x15, 0x16, 0x16, + 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1a, 0x1a, + 0x1b, 0x1b, 0x1c, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, + 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, + 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x40, 0x41, 0x43, 0x45, 0x47, 0x49, 0x4b, 0x4d, + 0x4f, 0x51, 0x53, 0x55, 0x57, 0x59, 0x5b, 0x5d, + 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, + 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, + 0x81, 0x83, 0x87, 0x8b, 0x8f, 0x93, 0x97, 0x9b, + 0xa0, 0xa4, 0xa8, 0xac, 0xb0, 0xb4, 0xb8, 0xbc, + 0xc1, 0xc5, 0xc9, 0xcd, 0xd1, 0xd5, 0xd9, 0xdd, + 0xe2, 0xe6, 0xea, 0xee, 0xf2, 0xf6, 0xfa, 0xff, + 0xff +}; + +#if 0 +int arch_vol_table[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0f, 0x10, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, + 0x12, 0x13, 0x13, 0x14, 0x14, 0x15, 0x15, 0x16, + 0x16, 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1a, + 0x1a, 0x1b, 0x1c, 0x1c, 0x1d, 0x1e, 0x1e, 0x1f, + 0x20, 0x20, 0x21, 0x22, 0x23, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x3a, 0x3b, 0x3c, 0x3e, 0x3f, + 0x40, 0x42, 0x43, 0x45, 0x46, 0x48, 0x49, 0x4b, + 0x4d, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, + 0x5b, 0x5e, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6b, + 0x6d, 0x6f, 0x72, 0x74, 0x77, 0x7a, 0x7c, 0x7f, + 0x82, 0x85, 0x88, 0x8b, 0x8e, 0x91, 0x94, 0x97, + 0x9b, 0x9e, 0xa1, 0xa5, 0xa9, 0xac, 0xb0, 0xb4, + 0xb8, 0xbc, 0xc0, 0xc4, 0xc9, 0xcd, 0xd2, 0xd6, + 0xdb, 0xe0, 0xe5, 0xea, 0xef, 0xf4, 0xfa, 0xff, + 0xff +}; +#endif + diff --git a/thirdparty/libxmp/src/loaders/vorbis.c b/thirdparty/libxmp/src/loaders/vorbis.c new file mode 100644 index 000000000..2778207b4 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/vorbis.c @@ -0,0 +1,5676 @@ +// Ogg Vorbis audio decoder - v1.22 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking implementation +// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, +// Elias Software, Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// See end of file for license information. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster github:alxprd +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// github:manxorist Saga Musix github:infatum +// Timur Gagiev Maxwell Koo Peter Waller +// github:audinowho Dougall Johnson David Reid +// github:Clownacy Pedro J. Estebanez Remi Verschelde +// AnthoFoxo github:morlat Gabriel Ravier +// +// Partial history: +// 1.22 - 2021-07-11 - various small fixes +// 1.21 - 2021-07-02 - fix bug for files with no comments +// 1.20 - 2020-07-11 - several small fixes +// 1.19 - 2020-02-05 - warnings +// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. +// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) +// 1.16 - 2019-03-04 - fix warnings +// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found +// 1.14 - 2018-02-11 - delete bogus dealloca usage +// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) +// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files +// 1.11 - 2017-07-23 - fix MinGW compilation +// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory +// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant +// 1.04 - 2014-08-27 - fix missing const-correct case in API +// 1.03 - 2014-08-07 - warning fixes +// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + + +/* libxmp customizations: */ +#define STB_VORBIS_C +#include "vorbis.h" + +#ifdef _MSC_VER +#pragma warning(disable:4456) /* shadowing (hides previous local decl) */ +#pragma warning(disable:4457) /* shadowing (hides function parameter.) */ +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +typedef struct +{ + char *vendor; + + int comment_list_length; + char **comment_list; +} stb_vorbis_comment; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +#ifndef STB_VORBIS_NO_COMMENTS +// get ogg comments +extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); +#endif + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char * datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. +// +// *output points into stb_vorbis's internal output buffer storage; these +// buffers are owned by stb_vorbis and application code should not free +// them or modify their contents. They are transient and will be overwritten +// once you ask for more data to get decoded, so be sure to grab any data +// you need before then. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Moreover, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +#ifndef STB_VORBIS_NO_SEEK_API +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern int stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) +#endif + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +#ifndef STB_VORBIS_NO_FLOAT_CONVERSION +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, + VORBIS_ogg_skeleton_not_supported +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + +// STB_VORBIS_NO_COMMENTS +// disables reading and storing user comments +// #define STB_VORBIS_NO_COMMENTS + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT + #include + #include + #include + #include +#else // STB_VORBIS_NO_CRT + #define NULL 0 + #define malloc(s) 0 + #define free(p) ((void) 0) + #define realloc(p, s) 0 +#endif // STB_VORBIS_NO_CRT + +#include + +#ifndef STB_FORCEINLINE + #if defined(_MSC_VER) + #define STB_FORCEINLINE __forceinline + #elif (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2))) || defined(__clang__) + #define STB_FORCEINLINE static __inline __attribute__((always_inline)) + #else + #define STB_FORCEINLINE static inline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define CHECK(f) do {} while(0) +#endif + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +/* libxmp-specific change */ +#if 1 +#include "../common.h" +#else +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +#endif + +#ifdef __has_feature +#if __has_feature(undefined_behavior_sanitizer) +#define HAS_UBSAN +#endif +#endif +#ifdef HAS_UBSAN +#define STB_NO_SANITIZE(s) __attribute__((no_sanitize(s))) +#else +#define STB_NO_SANITIZE(s) +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float codetype; + +#ifdef _MSC_VER +#define STBV_NOTUSED(v) (void)(v) +#elif defined(__VBCC__) +#define STBV_NOTUSED(v) +#else +#define STBV_NOTUSED(v) (void)sizeof(v) +#endif + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + // libxmp hack: https://github.com/nothings/stb/pull/1312 + MappingChannel *chan; + uint16 coupling_steps; + uint8 submaps; + uint8 submap_floor[16]; // varies + uint8 submap_residue[16]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + +#ifndef STB_VORBIS_NO_COMMENTS + char *vendor; + int comment_list_length; + char **comment_list; +#endif + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + // the page to seek to when seeking to start, may be zero + uint32 first_audio_page_offset; + + // p_first is the page on which the first audio packet ends + // (but not necessarily the page on which it starts) + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; + + // libxmp hack: decode work buffer (used in inverse_mdct and decode_residues) + void *work_buffer; + + // temporary buffers + void *temp_lengths; + void *temp_codewords; + void *temp_values; + void *temp_mults; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : f->work_buffer) +#define temp_free(f,p) do {} while (0) +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + if (sz <= 0) return NULL; /* libxmp hack: https://github.com/nothings/stb/issues/1248 */ + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? calloc(sz, 1) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + if (sz <= 0) return NULL; /* libxmp hack: https://github.com/nothings/stb/issues/1248 */ + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return calloc(sz, 1); +} + +static void setup_temp_free(vorb *f, void **_p, int sz) +{ + void *p = *_p; + *_p = NULL; + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+7)&~7; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +STB_FORCEINLINE uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + if (n < 0) return 0; // signed n returns 0 + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, (int)exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + assert(len[k] < 32); // no error return required, code reading lens checks this + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + assert(z < 32); // no error return required, code reading lens checks this + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propagate availability up the tree + if (z != len[i]) { + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +// +// libxmp hack: suppress UBSan error caused by invalid input data. +// Reported upstream: https://github.com/nothings/stb/issues/1168. +STB_NO_SANITIZE("float-cast-overflow") +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + if (pow((float) r+1, dim) <= entries) + return -1; + if ((int) floor(pow((float) r, dim)) > entries) + return -1; + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,id; +} stbv__floor_ordering; + +static int STBV_CDECL point_compare(const void *p, const void *q) +{ + stbv__floor_ordering *a = (stbv__floor_ordering *) p; + stbv__floor_ordering *b = (stbv__floor_ordering *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif + return 0; /* silence warnings */ +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif + return 0; /* silence warnings */ +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif + return 0; /* silence warnings */ +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n; + if (f->first_decode && !IS_PUSH_MODE(f)) { + f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; + } + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + f->p_first.page_end = f->p_first.page_start + len; + f->p_first.last_decoded_sample = loc0; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +#ifndef STB_VORBIS_NO_COMMENTS +static int get32_packet(vorb *f) +{ + uint32 x; + x = get8_packet(f); + x += get8_packet(f) << 8; + x += get8_packet(f) << 16; + x += (uint32) get8_packet(f) << 24; + return x; +} +#endif + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + + assert(f->valid_bits >= n); + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +STB_FORCEINLINE void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + /* libxmp hack: unsigned left shift for 32-bit codewords. + * https://github.com/nothings/stb/issues/1168 */ + if (c->codewords[i] == (f->acc & ((1U << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_BASE(c) (0) + +static int codebook_decode_start(vorb *f, Codebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +STB_FORCEINLINE void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + LINE_OP(output[x], inverse_db_table[y&255]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y&255]); + } + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +// n is 1/2 of the blocksize -- +// specification: "Correct per-vector decode length is [n]/2" +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + unsigned int actual_size = rtype == 2 ? n*2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch > 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + temp_free(f,part_classdata); + #else + temp_free(f,classifications); + #endif + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +STB_FORCEINLINE void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + float l00,l11; + + k00 = z[-0] - z[ -8]; + k11 = z[-1] - z[ -9]; + l00 = z[-2] - z[-10]; + l11 = z[-3] - z[-11]; + z[ -0] = z[-0] + z[ -8]; + z[ -1] = z[-1] + z[ -9]; + z[ -2] = z[-2] + z[-10]; + z[ -3] = z[-3] + z[-11]; + z[ -8] = k00; + z[ -9] = k11; + z[-10] = (l00+l11) * A2; + z[-11] = (l11-l00) * A2; + + k00 = z[ -4] - z[-12]; + k11 = z[ -5] - z[-13]; + l00 = z[ -6] - z[-14]; + l11 = z[ -7] - z[-15]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-12] = k11; + z[-13] = -k00; + z[-14] = (l11-l00) * A2; + z[-15] = (l00+l11) * -A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propagates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_free(f,buf2); + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + STBV_NOTUSED(step2_flag); + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx,ly, hx,hy, n2); + CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + STBV_NOTUSED(left_end); + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + CHECK(f); +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + CHECK(f); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around) + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet; + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; // this doesn't seem right, but has no ill effect on my test files + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + CHECK(f); + + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + if (w == NULL) return 0; + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static int vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left, res; + res = vorbis_decode_packet(f, &len, &left, &right); + if (res) + vorbis_finish_frame(f, len, left, right); + return res; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + f->first_decode = TRUE; + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) { + // check for the Ogg skeleton fishead identifying header to refine our error + if (f->segments[0] == 64 && + getn(f, header, 6) && + header[0] == 'f' && + header[1] == 'i' && + header[2] == 's' && + header[3] == 'h' && + header[4] == 'e' && + header[5] == 'a' && + get8(f) == 'd' && + get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); + else + return error(f, VORBIS_invalid_first_page); + } + + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + +#ifndef STB_VORBIS_NO_COMMENTS + if (!next_segment(f)) return FALSE; + + if (get8_packet(f) != VORBIS_packet_comment) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + //file vendor + len = get32_packet(f); + f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->vendor == NULL) return error(f, VORBIS_outofmem); + for(i=0; i < len; ++i) { + f->vendor[i] = get8_packet(f); + } + f->vendor[len] = (char)'\0'; + //user comments + f->comment_list_length = get32_packet(f); + f->comment_list = NULL; + if (f->comment_list_length > 0) + { + f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + } + + for(i=0; i < f->comment_list_length; ++i) { + len = get32_packet(f); + f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); + + for(j=0; j < len; ++j) { + f->comment_list[i][j] = get8_packet(f); + } + f->comment_list[i][len] = (char)'\0'; + } + + // framing_flag + x = get8_packet(f); + if (!(x & 1)) return error(f, VORBIS_invalid_setup); + + + skip(f, f->bytes_in_seg); + f->bytes_in_seg = 0; +#endif // STB_VORBIS_NO_COMMENTS + + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + CHECK(f); + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + + if (c->sparse) { + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + f->temp_lengths = lengths; + } + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (current_length >= 32) return error(f, VORBIS_invalid_setup); + if (current_entry + n > (int) c->entries) return error(f, VORBIS_invalid_setup); + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) return error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, &f->temp_lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + CHECK(f); + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + f->temp_codewords = c->codewords; + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + f->temp_values = values; + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, &f->temp_values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, &f->temp_codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, &f->temp_lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + int values = lookup1_values(c->entries, c->dimensions); + if (values < 0) return error(f, VORBIS_invalid_setup); + c->lookup_values = (uint32) values; + } else { + /* libxmp hack: unsigned multiply to suppress (legitimate) warning. + * https://github.com/nothings/stb/issues/1168 */ + c->lookup_values = (unsigned)c->entries * (unsigned)c->dimensions; + } + if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + f->temp_mults = mults; + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (f->valid_bits < 0) return error(f, VORBIS_invalid_setup); + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) return error(f, VORBIS_outofmem); + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + return error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + CHECK(f); + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + setup_temp_free(f, &f->temp_mults, sizeof(mults[0])*c->lookup_values); + + CHECK(f); + } + CHECK(f); + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + stbv__floor_ordering p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = (int16)get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].id = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values-1; ++j) + if (p[j].x == p[j+1].x) + return error(f, VORBIS_invalid_setup); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].id; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low = 0,hi = 0; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup); + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + // maximum reasonable partition size is f->blocksize_1 + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } else { + f->work_buffer = setup_malloc(f, f->temp_memory_required); + if (f->work_buffer == NULL) return error(f, VORBIS_outofmem); + } + + // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page + // without PAGEFLAG_continued_packet, so this either points to the first page, or + // the page after the end of the headers. It might be cleaner to point to a page + // in the middle of the headers, when that's the page where the first audio packet + // starts, but we'd have to also correctly skip the end of any continued packet in + // stb_vorbis_seek_start. + if (f->next_seg == -1) { + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + } else { + f->first_audio_page_offset = 0; + } + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + +#ifndef STB_VORBIS_NO_COMMENTS + setup_free(p, p->vendor); + for (i=0; i < p->comment_list_length; ++i) { + setup_free(p, p->comment_list[i]); + } + setup_free(p, p->comment_list); +#endif + + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + if (c->codewords != p->temp_codewords) // Might be the temporary buffer-allocated array. + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } + if (!p->alloc.alloc_buffer) { + setup_free(p, p->work_buffer); + setup_temp_free(p, &p->temp_lengths, 0); + setup_temp_free(p, &p->temp_codewords, 0); + setup_temp_free(p, &p->temp_values, 0); + setup_temp_free(p, &p->temp_mults, 0); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes &= ~7; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +#ifndef STB_VORBIS_NO_COMMENTS +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) +{ + stb_vorbis_comment d; + d.vendor = f->vendor; + d.comment_list_length = f->comment_list_length; + d.comment_list = f->comment_list; + return d; +} +#endif + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); + } + + f->stream = (uint8 *) data; + f->stream_end = (uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + vorbis_deinit(&p); + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif + return 0; /* silence warnings */ +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + + +#define SAMPLE_unknown 0xffffffff + +#ifndef STB_VORBIS_NO_SEEK_API +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + if (!getn(f, header, 27)) + return 0; + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + if (!getn(f, lacing, header[26])) + return 0; + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceding page while finding the +// start of a packet +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe; + uint32 end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) +{ + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding, last_sample_limit; + double offset = 0.0, bytes_per_sample = 0.0; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + last_sample_limit = 0; + else + last_sample_limit = sample_number - padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (last_sample_limit <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) { + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + } + return 0; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough (if it wasn't an interpolation probe). + if (mid.page_start == right.page_start) { + if (probe >= 2 || delta <= 65536) + break; + } else { + if (last_sample_limit < mid.last_decoded_sample) + right = mid; + else + left = mid; + } + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + if (!vorbis_pump_first_frame(f)) + return 0; + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); +} + +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + uint32 max_frame_samples; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame should start with the sample + if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); + return 1; +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +int stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + return vorbis_pump_first_frame(f); +} +#endif /* STB_VORBIS_NO_SEEK_API */ + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + uint32 end,last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = (uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f; +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + if (0 != fopen_s(&f, filename, "rb")) + f = NULL; +#else + f = fopen(filename, "rb"); +#endif + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (!data) { + if (error) *error = VORBIS_unexpected_eof; + return NULL; + } + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + if (error) *error = VORBIS__no_error; + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + // libxmp hack: changed this to unsigned to suppress a UBSan error. + // Reported upstream: https://github.com/nothings/stb/issues/1168. + unsigned int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (int)(temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +// libxmp hack: replaced signed overflow clamps with unsigned overflow (UBSan). +// Reported upstream: https://github.com/nothings/stb/issues/1168. + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int)v + 32768 > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += STB_BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int)v + 32768 > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } + #undef STB_BUFFER_SIZE +} + +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int)v + 32768 > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } + #undef STB_BUFFER_SIZE +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output = NULL; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int)v + 32768 > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +#ifndef STB_VORBIS_NO_FLOAT_CONVERSION +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_FLOAT_CONVERSION +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 + found with Mayhem by ForAllSecure + 1.16 - 2019-03-04 - fix warnings + 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found + 1.14 - 2018-02-11 - delete bogus dealloca usage + 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) + 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files + 1.11 - 2017-07-23 - fix MinGW compilation + 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory + 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015-04-19 - don't define __forceinline if it's redundant + 1.04 - 2014-08-27 - fix missing const-correct case in API + 1.03 - 2014-08-07 - Warning fixes + 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float + 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_HEADER_ONLY + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/thirdparty/libxmp/src/loaders/vorbis.h b/thirdparty/libxmp/src/loaders/vorbis.h new file mode 100644 index 000000000..1ca02ec14 --- /dev/null +++ b/thirdparty/libxmp/src/loaders/vorbis.h @@ -0,0 +1,46 @@ +#ifndef DEBUG /* not a debug build */ +#ifndef NDEBUG +#define NDEBUG /* disable assert()s */ +#endif +#endif + +#define STB_VORBIS_NO_PUSHDATA_API +#define STB_VORBIS_NO_STDIO +#define STB_VORBIS_NO_COMMENTS +#define STB_VORBIS_NO_SEEK_API +#define STB_VORBIS_NO_FLOAT_CONVERSION + +/* change namespace from stb_ to libxmp_ for public functions: */ +#define stb_vorbis_get_info libxmp_vorbis_get_info +#define stb_vorbis_get_comment libxmp_vorbis_get_comment +#define stb_vorbis_get_error libxmp_vorbis_get_error +#define stb_vorbis_close libxmp_vorbis_close +#define stb_vorbis_get_sample_offset libxmp_vorbis_get_sample_offset +#define stb_vorbis_get_file_offset libxmp_vorbis_get_file_offset +#define stb_vorbis_open_pushdata libxmp_vorbis_open_pushdata +#define stb_vorbis_decode_frame_pushdata libxmp_vorbis_decode_frame_pushdata +#define stb_vorbis_flush_pushdata libxmp_vorbis_flush_pushdata +#define stb_vorbis_decode_filename libxmp_vorbis_decode_filename +#define stb_vorbis_decode_memory libxmp_vorbis_decode_memory +#define stb_vorbis_open_memory libxmp_vorbis_open_memory +#define stb_vorbis_open_filename libxmp_vorbis_open_filename +#define stb_vorbis_open_file libxmp_vorbis_open_file +#define stb_vorbis_open_file_section libxmp_vorbis_open_file_section +#define stb_vorbis_seek_frame libxmp_vorbis_seek_frame +#define stb_vorbis_seek libxmp_vorbis_seek +#define stb_vorbis_seek_start libxmp_vorbis_seek_start +#define stb_vorbis_stream_length_in_samples libxmp_vorbis_stream_length_in_samples +#define stb_vorbis_stream_length_in_seconds libxmp_vorbis_stream_length_in_seconds +#define stb_vorbis_get_frame_float libxmp_vorbis_get_frame_float +#define stb_vorbis_get_frame_short_interleaved libxmp_vorbis_get_frame_short_interleaved +#define stb_vorbis_get_frame_short libxmp_vorbis_get_frame_short +#define stb_vorbis_get_samples_float_interleaved libxmp_vorbis_get_samples_float_interleaved +#define stb_vorbis_get_samples_float libxmp_vorbis_get_samples_float +#define stb_vorbis_get_samples_short_interleaved libxmp_vorbis_get_samples_short_interleaved +#define stb_vorbis_get_samples_short libxmp_vorbis_get_samples_short + +#ifndef STB_VORBIS_C +/* client: */ +#define STB_VORBIS_HEADER_ONLY +#include "vorbis.c" +#endif diff --git a/thirdparty/libxmp/src/loaders/xm.h b/thirdparty/libxmp/src/loaders/xm.h new file mode 100644 index 000000000..4d41486cc --- /dev/null +++ b/thirdparty/libxmp/src/loaders/xm.h @@ -0,0 +1,101 @@ +#ifndef LIBXMP_LOADERS_XM_H +#define LIBXMP_LOADERS_XM_H + +#define XM_EVENT_PACKING 0x80 +#define XM_EVENT_PACK_MASK 0x7f +#define XM_EVENT_NOTE_FOLLOWS 0x01 +#define XM_EVENT_INSTRUMENT_FOLLOWS 0x02 +#define XM_EVENT_VOLUME_FOLLOWS 0x04 +#define XM_EVENT_FXTYPE_FOLLOWS 0x08 +#define XM_EVENT_FXPARM_FOLLOWS 0x10 +#define XM_LINEAR_FREQ 0x01 +#define XM_LOOP_MASK 0x03 +#define XM_LOOP_NONE 0 +#define XM_LOOP_FORWARD 1 +#define XM_LOOP_PINGPONG 2 +#define XM_SAMPLE_16BIT 0x10 +#define XM_SAMPLE_STEREO 0x20 +#define XM_ENVELOPE_ON 0x01 +#define XM_ENVELOPE_SUSTAIN 0x02 +#define XM_ENVELOPE_LOOP 0x04 +#define XM_LINEAR_PERIOD_MODE 0x01 + +struct xm_file_header { + uint8 id[17]; /* ID text: "Extended module: " */ + uint8 name[20]; /* Module name, padded with zeroes */ + uint8 doseof; /* 0x1a */ + uint8 tracker[20]; /* Tracker name */ + uint16 version; /* Version number, minor-major */ + uint32 headersz; /* Header size */ + uint16 songlen; /* Song length (in patten order table) */ + uint16 restart; /* Restart position */ + uint16 channels; /* Number of channels (2,4,6,8,10,...,32) */ + uint16 patterns; /* Number of patterns (max 256) */ + uint16 instruments; /* Number of instruments (max 128) */ + uint16 flags; /* bit 0: 0=Amiga freq table, 1=Linear */ + uint16 tempo; /* Default tempo */ + uint16 bpm; /* Default BPM */ + uint8 order[256]; /* Pattern order table */ +}; + +struct xm_pattern_header { + uint32 length; /* Pattern header length */ + uint8 packing; /* Packing type (always 0) */ + uint16 rows; /* Number of rows in pattern (1..256) */ + uint16 datasize; /* Packed patterndata size */ +}; + +struct xm_instrument_header { + uint32 size; /* Instrument size */ + uint8 name[22]; /* Instrument name */ + uint8 type; /* Instrument type (always 0) */ + uint16 samples; /* Number of samples in instrument */ + uint32 sh_size; /* Sample header size */ +}; + +struct xm_instrument { + uint8 sample[96]; /* Sample number for all notes */ + uint16 v_env[24]; /* Points for volume envelope */ + uint16 p_env[24]; /* Points for panning envelope */ + uint8 v_pts; /* Number of volume points */ + uint8 p_pts; /* Number of panning points */ + uint8 v_sus; /* Volume sustain point */ + uint8 v_start; /* Volume loop start point */ + uint8 v_end; /* Volume loop end point */ + uint8 p_sus; /* Panning sustain point */ + uint8 p_start; /* Panning loop start point */ + uint8 p_end; /* Panning loop end point */ + uint8 v_type; /* Bit 0: On; 1: Sustain; 2: Loop */ + uint8 p_type; /* Bit 0: On; 1: Sustain; 2: Loop */ + uint8 y_wave; /* Vibrato waveform */ + uint8 y_sweep; /* Vibrato sweep */ + uint8 y_depth; /* Vibrato depth */ + uint8 y_rate; /* Vibrato rate */ + uint16 v_fade; /* Volume fadeout */ +#if 0 + uint8 reserved[22]; /* Reserved; 2 bytes in specs, 22 in 1.04 */ +#endif +}; + +struct xm_sample_header { + uint32 length; /* Sample length */ + uint32 loop_start; /* Sample loop start */ + uint32 loop_length; /* Sample loop length */ + uint8 volume; /* Volume */ + int8 finetune; /* Finetune (signed byte -128..+127) */ + uint8 type; /* 0=No loop,1=Fwd loop,2=Ping-pong,16-bit */ + uint8 pan; /* Panning (0-255) */ + int8 relnote; /* Relative note number (signed byte) */ + uint8 reserved; /* Reserved */ + uint8 name[22]; /* Sample_name */ +}; + +struct xm_event { + uint8 note; /* Note (0-71, 0 = C-0) */ + uint8 instrument; /* Instrument (0-128) */ + uint8 volume; /* Volume column byte */ + uint8 fx_type; /* Effect type */ + uint8 fx_parm; /* Effect parameter */ +}; + +#endif /* LIBXMP_LOADERS_XM_H */ diff --git a/thirdparty/libxmp/src/loaders/xm_load.c b/thirdparty/libxmp/src/loaders/xm_load.c new file mode 100644 index 000000000..72189600b --- /dev/null +++ b/thirdparty/libxmp/src/loaders/xm_load.c @@ -0,0 +1,1023 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Fri, 26 Jun 1998 17:45:59 +1000 Andrew Leahy + * Finally got it working on the DEC Alpha running DEC UNIX! In the pattern + * reading loop I found I was getting "0" for (p-patbuf) and "0" for + * xph.datasize, the next if statement (where it tries to read the patbuf) + * would then cause a seg_fault. + * + * Sun Sep 27 12:07:12 EST 1998 Claudio Matsuoka + * Extended Module 1.02 stores data in a different order, we must handle + * this accordingly. MAX_SAMP used as a workaround to check the number of + * samples recognized by the player. + */ + +#include "loader.h" +#include "xm.h" +#ifndef LIBXMP_CORE_PLAYER +#include "vorbis.h" +#endif + +static int xm_test(HIO_HANDLE *, char *, const int); +static int xm_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_xm = { + "Fast Tracker II", + xm_test, + xm_load +}; + +static int xm_test(HIO_HANDLE *f, char *t, const int start) +{ + char buf[20]; + + if (hio_read(buf, 1, 17, f) < 17) /* ID text */ + return -1; + + if (memcmp(buf, "Extended Module: ", 17)) + return -1; + + libxmp_read_title(f, t, 20); + + return 0; +} + +static int load_xm_pattern(struct module_data *m, int num, int version, + uint8 *patbuf, HIO_HANDLE *f) +{ + const int headsize = version > 0x0102 ? 9 : 8; + struct xmp_module *mod = &m->mod; + struct xm_pattern_header xph; + struct xmp_event *event; + uint8 *pat, b; + int j, k, r; + int size, size_read; + + xph.length = hio_read32l(f); + xph.packing = hio_read8(f); + xph.rows = version > 0x0102 ? hio_read16l(f) : hio_read8(f) + 1; + + /* Sanity check */ + if (xph.rows > 256) { + goto err; + } + + xph.datasize = hio_read16l(f); + hio_seek(f, xph.length - headsize, SEEK_CUR); + if (hio_error(f)) { + goto err; + } + + r = xph.rows; + if (r == 0) { + r = 0x100; + } + + if (libxmp_alloc_pattern_tracks(mod, num, r) < 0) { + goto err; + } + + if (xph.datasize == 0) { + return 0; + } + + size = xph.datasize; + pat = patbuf; + + size_read = hio_read(patbuf, 1, size, f); + if (size_read < size) { + memset(patbuf + size_read, 0, size - size_read); + } + + for (j = 0; j < r; j++) { + for (k = 0; k < mod->chn; k++) { + /* + if ((pat - patbuf) >= xph.datasize) + break; + */ + + event = &EVENT(num, k, j); + + if (--size < 0) { + goto err; + } + + if ((b = *pat++) & XM_EVENT_PACKING) { + if (b & XM_EVENT_NOTE_FOLLOWS) { + if (--size < 0) + goto err; + event->note = *pat++; + } + if (b & XM_EVENT_INSTRUMENT_FOLLOWS) { + if (--size < 0) + goto err; + event->ins = *pat++; + } + if (b & XM_EVENT_VOLUME_FOLLOWS) { + if (--size < 0) + goto err; + event->vol = *pat++; + } + if (b & XM_EVENT_FXTYPE_FOLLOWS) { + if (--size < 0) + goto err; + event->fxt = *pat++; + } + if (b & XM_EVENT_FXPARM_FOLLOWS) { + if (--size < 0) + goto err; + event->fxp = *pat++; + } + } else { + size -= 4; + if (size < 0) + goto err; + event->note = b; + event->ins = *pat++; + event->vol = *pat++; + event->fxt = *pat++; + event->fxp = *pat++; + } + + /* Sanity check */ + switch (event->fxt) { + case 18: + case 19: + case 22: + case 23: + case 24: + case 26: + case 28: + case 30: + case 31: + case 32: + event->fxt = 0; + } + if (event->fxt > 34) { + event->fxt = 0; + } + + if (event->note == 0x61) { + /* See OpenMPT keyoff+instr.xm test case */ + if (event->fxt == 0x0e && MSN(event->fxp) == 0x0d) { + event->note = XMP_KEY_OFF; + } else { + event->note = + event->ins ? XMP_KEY_FADE : XMP_KEY_OFF; + } + } else if (event->note > 0) { + event->note += 12; + } + + if (event->fxt == 0x0e) { + if (MSN(event->fxp) == EX_FINETUNE) { + unsigned char val = (LSN(event->fxp) - 8) & 0xf; + event->fxp = (EX_FINETUNE << 4) | val; + } + switch (event->fxp) { + case 0x43: + case 0x73: + event->fxp--; + break; + } + } + if (event->fxt == FX_XF_PORTA && MSN(event->fxp) == 0x09) { + /* Translate MPT hacks */ + switch (LSN(event->fxp)) { + case 0x0: /* Surround off */ + case 0x1: /* Surround on */ + event->fxt = FX_SURROUND; + event->fxp = LSN(event->fxp); + break; + case 0xe: /* Play forward */ + case 0xf: /* Play reverse */ + event->fxt = FX_REVERSE; + event->fxp = LSN(event->fxp) - 0xe; + } + } + + if (!event->vol) { + continue; + } + + /* Volume set */ + if ((event->vol >= 0x10) && (event->vol <= 0x50)) { + event->vol -= 0x0f; + continue; + } + + /* Volume column effects */ + switch (event->vol >> 4) { + case 0x06: /* Volume slide down */ + event->f2t = FX_VOLSLIDE_2; + event->f2p = event->vol - 0x60; + break; + case 0x07: /* Volume slide up */ + event->f2t = FX_VOLSLIDE_2; + event->f2p = (event->vol - 0x70) << 4; + break; + case 0x08: /* Fine volume slide down */ + event->f2t = FX_EXTENDED; + event->f2p = + (EX_F_VSLIDE_DN << 4) | (event->vol - 0x80); + break; + case 0x09: /* Fine volume slide up */ + event->f2t = FX_EXTENDED; + event->f2p = + (EX_F_VSLIDE_UP << 4) | (event->vol - 0x90); + break; + case 0x0a: /* Set vibrato speed */ + event->f2t = FX_VIBRATO; + event->f2p = (event->vol - 0xa0) << 4; + break; + case 0x0b: /* Vibrato */ + event->f2t = FX_VIBRATO; + event->f2p = event->vol - 0xb0; + break; + case 0x0c: /* Set panning */ + event->f2t = FX_SETPAN; + event->f2p = (event->vol - 0xc0) << 4; + break; + case 0x0d: /* Pan slide left */ + event->f2t = FX_PANSL_NOMEM; + event->f2p = (event->vol - 0xd0) << 4; + break; + case 0x0e: /* Pan slide right */ + event->f2t = FX_PANSL_NOMEM; + event->f2p = event->vol - 0xe0; + break; + case 0x0f: /* Tone portamento */ + event->f2t = FX_TONEPORTA; + event->f2p = (event->vol - 0xf0) << 4; + + /* From OpenMPT TonePortamentoMemory.xm: + * "Another nice bug (...) is the combination of both + * portamento commands (Mx and 3xx) in the same cell: + * The 3xx parameter is ignored completely, and the Mx + * parameter is doubled. (M2 3FF is the same as M4 000) + */ + if (event->fxt == FX_TONEPORTA + || event->fxt == FX_TONE_VSLIDE) { + if (event->fxt == FX_TONEPORTA) { + event->fxt = 0; + } else { + event->fxt = FX_VOLSLIDE; + } + event->fxp = 0; + + if (event->f2p < 0x80) { + event->f2p <<= 1; + } else { + event->f2p = 0xff; + } + } + + /* From OpenMPT porta-offset.xm: + * "If there is a portamento command next to an offset + * command, the offset command is ignored completely. In + * particular, the offset parameter is not memorized." + */ + if (event->fxt == FX_OFFSET + && event->f2t == FX_TONEPORTA) { + event->fxt = event->fxp = 0; + } + break; + } + event->vol = 0; + } + } + + return 0; + +err: + return -1; +} + +static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f) +{ + struct xmp_module *mod = &m->mod; + uint8 *patbuf; + int i, j; + + mod->pat++; + if (libxmp_init_pattern(mod) < 0) { + return -1; + } + + D_(D_INFO "Stored patterns: %d", mod->pat - 1); + + if ((patbuf = (uint8 *) calloc(1, 65536)) == NULL) { + return -1; + } + + for (i = 0; i < mod->pat - 1; i++) { + if (load_xm_pattern(m, i, version, patbuf, f) < 0) { + goto err; + } + } + + /* Alloc one extra pattern */ + { + int t = i * mod->chn; + + if (libxmp_alloc_pattern(mod, i) < 0) { + goto err; + } + + mod->xxp[i]->rows = 64; + + if (libxmp_alloc_track(mod, t, 64) < 0) { + goto err; + } + + for (j = 0; j < mod->chn; j++) { + mod->xxp[i]->index[j] = t; + } + } + + free(patbuf); + return 0; + +err: + free(patbuf); + return -1; +} + +/* Packed structures size */ +#define XM_INST_HEADER_SIZE 29 +#define XM_INST_SIZE 212 + +/* grass.near.the.house.xm defines 23 samples in instrument 1. FT2 docs + * specify at most 16. See https://github.com/libxmp/libxmp/issues/168 + * for more details. */ +#define XM_MAX_SAMPLES_PER_INST 32 + +#ifndef LIBXMP_CORE_PLAYER +#define MAGIC_OGGS 0x4f676753 + +static int is_ogg_sample(HIO_HANDLE *f, struct xmp_sample *xxs) +{ + /* uint32 size; */ + uint32 id; + + /* Sample must be at least 4 bytes long to be an OGG sample. + * Bonnie's Bookstore music.oxm contains zero length samples + * followed immediately by OGG samples. */ + if (xxs->len < 4) + return 0; + + /* size = */ hio_read32l(f); + id = hio_read32b(f); + if (hio_error(f) != 0 || hio_seek(f, -8, SEEK_CUR) < 0) + return 0; + + if (id != MAGIC_OGGS) { /* copy input data if not Ogg file */ + return 0; + } + + return 1; +} + +static int oggdec(struct module_data *m, HIO_HANDLE *f, struct xmp_sample *xxs, int len) +{ + int i, n, ch, rate, ret, flags = 0; + uint8 *data; + int16 *pcm16 = NULL; + + if ((data = (uint8 *)calloc(1, len)) == NULL) + return -1; + + hio_read32b(f); + if (hio_error(f) != 0 || hio_read(data, 1, len - 4, f) != len - 4) { + free(data); + return -1; + } + + n = stb_vorbis_decode_memory(data, len, &ch, &rate, &pcm16); + free(data); + + if (n <= 0) { + free(pcm16); + return -1; + } + + xxs->len = n; + + if ((xxs->flg & XMP_SAMPLE_16BIT) == 0) { + uint8 *pcm = (uint8 *)pcm16; + + for (i = 0; i < n; i++) { + pcm[i] = pcm16[i] >> 8; + } + pcm = (uint8 *)realloc(pcm16, n); + if (pcm == NULL) { + free(pcm16); + return -1; + } + pcm16 = (int16 *)pcm; + } + + flags |= SAMPLE_FLAG_NOLOAD; +#ifdef WORDS_BIGENDIAN + flags |= SAMPLE_FLAG_BIGEND; +#endif + + ret = libxmp_load_sample(m, NULL, flags, xxs, pcm16); + free(pcm16); + + return ret; +} +#endif + +static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f) +{ + struct xmp_module *mod = &m->mod; + struct xm_instrument_header xih; + struct xm_instrument xi; + struct xm_sample_header xsh[XM_MAX_SAMPLES_PER_INST]; + int sample_num = 0; + long total_sample_size; + int i, j; + uint8 buf[208]; + + D_(D_INFO "Instruments: %d", mod->ins); + + /* ESTIMATED value! We don't know the actual value at this point */ + mod->smp = MAX_SAMPLES; + + if (libxmp_init_instrument(m) < 0) { + return -1; + } + + for (i = 0; i < mod->ins; i++) { + long instr_pos = hio_tell(f); + struct xmp_instrument *xxi = &mod->xxi[i]; + + /* Modules converted with MOD2XM 1.0 always say we have 31 + * instruments, but file may end abruptly before that. Also covers + * XMLiTE stripped modules and truncated files. This test will not + * work if file has trailing garbage. + * + * Note: loading 4 bytes past the instrument header to get the + * sample header size (if it exists). This is NOT considered to + * be part of the instrument header. + */ + if (hio_read(buf, XM_INST_HEADER_SIZE + 4, 1, f) != 1) { + D_(D_WARN "short read in instrument header data"); + break; + } + + xih.size = readmem32l(buf); /* Instrument size */ + memcpy(xih.name, buf + 4, 22); /* Instrument name */ + xih.type = buf[26]; /* Instrument type (always 0) */ + xih.samples = readmem16l(buf + 27); /* Number of samples */ + xih.sh_size = readmem32l(buf + 29); /* Sample header size */ + + /* Sanity check */ + if ((int)xih.size < XM_INST_HEADER_SIZE) { + D_(D_CRIT "instrument %d: instrument header size:%d", i + 1, xih.size); + return -1; + } + + if (xih.samples > XM_MAX_SAMPLES_PER_INST || (xih.samples > 0 && xih.sh_size > 0x100)) { + D_(D_CRIT "instrument %d: samples:%d sample header size:%d", i + 1, xih.samples, xih.sh_size); + return -1; + } + + libxmp_instrument_name(mod, i, xih.name, 22); + + xxi->nsm = xih.samples; + + D_(D_INFO "instrument:%2X (%s) samples:%2d", i, xxi->name, xxi->nsm); + + if (xxi->nsm == 0) { + /* Sample size should be in struct xm_instrument according to + * the official format description, but FT2 actually puts it in + * struct xm_instrument header. There's a tracker or converter + * that follow the specs, so we must handle both cases (see + * "Braintomb" by Jazztiz/ART). + */ + + /* Umm, Cyke O'Path sent me a couple of + * mods ("Breath of the Wind" and "Broken Dimension") that + * reserve the instrument data space after the instrument header + * even if the number of instruments is set to 0. In these modules + * the instrument header size is marked as 263. The following + * generalization should take care of both cases. + */ + + if (hio_seek(f, (int)xih.size - (XM_INST_HEADER_SIZE + 4), SEEK_CUR) < 0) { + return -1; + } + + continue; + } + + if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0) { + return -1; + } + + /* for BoobieSqueezer (see http://boobie.rotfl.at/) + * It works pretty much the same way as Impulse Tracker's sample + * only mode, where it will strip off the instrument data. + */ + if (xih.size < XM_INST_HEADER_SIZE + XM_INST_SIZE) { + memset(&xi, 0, sizeof(struct xm_instrument)); + hio_seek(f, xih.size - (XM_INST_HEADER_SIZE + 4), SEEK_CUR); + } else { + uint8 *b = buf; + + if (hio_read(buf, 208, 1, f) != 1) { + D_(D_CRIT "short read in instrument data"); + return -1; + } + + memcpy(xi.sample, b, 96); /* Sample map */ + b += 96; + + for (j = 0; j < 24; j++) { + xi.v_env[j] = readmem16l(b); /* Points for volume envelope */ + b += 2; + } + + for (j = 0; j < 24; j++) { + xi.p_env[j] = readmem16l(b); /* Points for pan envelope */ + b += 2; + } + + xi.v_pts = *b++; /* Number of volume points */ + xi.p_pts = *b++; /* Number of pan points */ + xi.v_sus = *b++; /* Volume sustain point */ + xi.v_start = *b++; /* Volume loop start point */ + xi.v_end = *b++; /* Volume loop end point */ + xi.p_sus = *b++; /* Pan sustain point */ + xi.p_start = *b++; /* Pan loop start point */ + xi.p_end = *b++; /* Pan loop end point */ + xi.v_type = *b++; /* Bit 0:On 1:Sustain 2:Loop */ + xi.p_type = *b++; /* Bit 0:On 1:Sustain 2:Loop */ + xi.y_wave = *b++; /* Vibrato waveform */ + xi.y_sweep = *b++; /* Vibrato sweep */ + xi.y_depth = *b++; /* Vibrato depth */ + xi.y_rate = *b++; /* Vibrato rate */ + xi.v_fade = readmem16l(b); /* Volume fadeout */ + + /* Skip reserved space */ + if (hio_seek(f, (int)xih.size - (XM_INST_HEADER_SIZE + XM_INST_SIZE), SEEK_CUR) < 0) { + return -1; + } + + /* Envelope */ + xxi->rls = xi.v_fade << 1; + xxi->aei.npt = xi.v_pts; + xxi->aei.sus = xi.v_sus; + xxi->aei.lps = xi.v_start; + xxi->aei.lpe = xi.v_end; + xxi->aei.flg = xi.v_type; + xxi->pei.npt = xi.p_pts; + xxi->pei.sus = xi.p_sus; + xxi->pei.lps = xi.p_start; + xxi->pei.lpe = xi.p_end; + xxi->pei.flg = xi.p_type; + + if (xxi->aei.npt <= 0 || xxi->aei.npt > 12 /*XMP_MAX_ENV_POINTS */ ) { + xxi->aei.flg &= ~XMP_ENVELOPE_ON; + } else { + memcpy(xxi->aei.data, xi.v_env, xxi->aei.npt * 4); + } + + if (xxi->pei.npt <= 0 || xxi->pei.npt > 12 /*XMP_MAX_ENV_POINTS */ ) { + xxi->pei.flg &= ~XMP_ENVELOPE_ON; + } else { + memcpy(xxi->pei.data, xi.p_env, xxi->pei.npt * 4); + } + + for (j = 12; j < 108; j++) { + xxi->map[j].ins = xi.sample[j - 12]; + if (xxi->map[j].ins >= xxi->nsm) + xxi->map[j].ins = 0xff; + } + } + + /* Read subinstrument and sample parameters */ + + for (j = 0; j < xxi->nsm; j++, sample_num++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs; + uint8 *b = buf; + + D_(D_INFO " sample index:%d sample id:%d", j, sample_num); + + if (sample_num >= mod->smp) { + if (libxmp_realloc_samples(m, mod->smp * 3 / 2) < 0) + return -1; + } + xxs = &mod->xxs[sample_num]; + + if (hio_read(buf, 40, 1, f) != 1) { + D_(D_CRIT "short read in sample data"); + return -1; + } + + xsh[j].length = readmem32l(b); /* Sample length */ + b += 4; + + /* Sanity check */ + if (xsh[j].length > MAX_SAMPLE_SIZE) { + D_(D_CRIT "sanity check: %d: bad sample size", j); + return -1; + } + + xsh[j].loop_start = readmem32l(b); /* Sample loop start */ + b += 4; + xsh[j].loop_length = readmem32l(b); /* Sample loop length */ + b += 4; + xsh[j].volume = *b++; /* Volume */ + xsh[j].finetune = *b++; /* Finetune (-128..+127) */ + xsh[j].type = *b++; /* Flags */ + xsh[j].pan = *b++; /* Panning (0-255) */ + xsh[j].relnote = *(int8 *) b++; /* Relative note number */ + xsh[j].reserved = *b++; + memcpy(xsh[j].name, b, 22); + + sub->vol = xsh[j].volume; + sub->pan = xsh[j].pan; + sub->xpo = xsh[j].relnote; + sub->fin = xsh[j].finetune; + sub->vwf = xi.y_wave; + sub->vde = xi.y_depth << 2; + sub->vra = xi.y_rate; + sub->vsw = xi.y_sweep; + sub->sid = sample_num; + + libxmp_copy_adjust(xxs->name, xsh[j].name, 22); + + xxs->len = xsh[j].length; + xxs->lps = xsh[j].loop_start; + xxs->lpe = xsh[j].loop_start + xsh[j].loop_length; + + xxs->flg = 0; + if (xsh[j].type & XM_SAMPLE_16BIT) { + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + if (xsh[j].type & XM_SAMPLE_STEREO) { + /* xxs->flg |= XMP_SAMPLE_STEREO; */ + xxs->len >>= 1; + xxs->lps >>= 1; + xxs->lpe >>= 1; + } + + xxs->flg |= xsh[j].type & XM_LOOP_FORWARD ? XMP_SAMPLE_LOOP : 0; + xxs->flg |= xsh[j].type & XM_LOOP_PINGPONG ? XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR : 0; + + D_(D_INFO " size:%06x loop start:%06x loop end:%06x %c V%02x F%+04d P%02x R%+03d %s", + mod->xxs[sub->sid].len, + mod->xxs[sub->sid].lps, + mod->xxs[sub->sid].lpe, + mod->xxs[sub->sid].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : + mod->xxs[sub->sid].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + sub->vol, sub->fin, sub->pan, sub->xpo, + mod->xxs[sub->sid].flg & XMP_SAMPLE_16BIT ? " (16 bit)" : ""); + } + + /* Read actual sample data */ + + total_sample_size = 0; + for (j = 0; j < xxi->nsm; j++) { + struct xmp_subinstrument *sub = &xxi->sub[j]; + struct xmp_sample *xxs = &mod->xxs[sub->sid]; + int flags; + + flags = SAMPLE_FLAG_DIFF; +#ifndef LIBXMP_CORE_PLAYER + if (xsh[j].reserved == 0xad) { + flags = SAMPLE_FLAG_ADPCM; + } +#endif + + if (version > 0x0103) { + D_(D_INFO " read sample: index:%d sample id:%d", j, sub->sid); + +#ifndef LIBXMP_CORE_PLAYER + if (is_ogg_sample(f, xxs)) { + if (oggdec(m, f, xxs, xsh[j].length) < 0) { + return -1; + } + + D_(D_INFO " sample is vorbis"); + total_sample_size += xsh[j].length; + continue; + } +#endif + + if (libxmp_load_sample(m, f, flags, xxs, NULL) < 0) { + return -1; + } + if (flags & SAMPLE_FLAG_ADPCM) { + D_(D_INFO " sample is adpcm"); + total_sample_size += 16 + ((xsh[j].length + 1) >> 1); + } else { + total_sample_size += xsh[j].length; + } + + /* TODO: implement stereo samples. + * For now, just skip the right channel. */ + if (xsh[j].type & XM_SAMPLE_STEREO) { + hio_seek(f, xsh[j].length >> 1, SEEK_CUR); + } + } + } + + /* Reposition correctly in case of 16-bit sample having odd in-file length. + * See "Lead Lined for '99", reported by Dennis Mulleneers. + */ + if (hio_seek(f, instr_pos + xih.size + 40 * xih.samples + total_sample_size, SEEK_SET) < 0) { + return -1; + } + } + + /* Final sample number adjustment */ + if (libxmp_realloc_samples(m, sample_num) < 0) { + return -1; + } + + return 0; +} + +static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start) +{ + struct xmp_module *mod = &m->mod; + int i, j; + struct xm_file_header xfh; + char tracker_name[21]; +#ifndef LIBXMP_CORE_PLAYER + int claims_ft2 = 0; + int is_mpt_116 = 0; +#endif + int len; + uint8 buf[80]; + + LOAD_INIT(); + + if (hio_read(buf, 80, 1, f) != 1) { + D_(D_CRIT "error reading header"); + return -1; + } + + memcpy(xfh.id, buf, 17); /* ID text */ + memcpy(xfh.name, buf + 17, 20); /* Module name */ + /* */ /* skip 0x1a */ + memcpy(xfh.tracker, buf + 38, 20); /* Tracker name */ + xfh.version = readmem16l(buf + 58); /* Version number, minor-major */ + xfh.headersz = readmem32l(buf + 60); /* Header size */ + xfh.songlen = readmem16l(buf + 64); /* Song length */ + xfh.restart = readmem16l(buf + 66); /* Restart position */ + xfh.channels = readmem16l(buf + 68); /* Number of channels */ + xfh.patterns = readmem16l(buf + 70); /* Number of patterns */ + xfh.instruments = readmem16l(buf + 72); /* Number of instruments */ + xfh.flags = readmem16l(buf + 74); /* 0=Amiga freq table, 1=Linear */ + xfh.tempo = readmem16l(buf + 76); /* Default tempo */ + xfh.bpm = readmem16l(buf + 78); /* Default BPM */ + + /* Sanity checks */ + if (xfh.songlen > 256) { + D_(D_CRIT "bad song length: %d", xfh.songlen); + return -1; + } + if (xfh.patterns > 256) { + D_(D_CRIT "bad pattern count: %d", xfh.patterns); + return -1; + } + if (xfh.instruments > 255) { + D_(D_CRIT "bad instrument count: %d", xfh.instruments); + return -1; + } + + if (xfh.restart > 255) { + D_(D_CRIT "bad restart position: %d", xfh.restart); + return -1; + } + if (xfh.channels > XMP_MAX_CHANNELS) { + D_(D_CRIT "bad channel count: %d", xfh.channels); + return -1; + } + + /* FT2 and MPT allow up to 255 BPM. OpenMPT allows up to 1000 BPM. */ + if (xfh.tempo >= 32 || xfh.bpm < 32 || xfh.bpm > 1000) { + if (memcmp("MED2XM", xfh.tracker, 6)) { + D_(D_CRIT "bad tempo or BPM: %d %d", xfh.tempo, xfh.bpm); + return -1; + } + } + + /* Honor header size -- needed by BoobieSqueezer XMs */ + len = xfh.headersz - 0x14; + if (len < 0 || len > 256) { + D_(D_CRIT "bad XM header length: %d", len); + return -1; + } + + memset(xfh.order, 0, sizeof(xfh.order)); + if (hio_read(xfh.order, len, 1, f) != 1) { /* Pattern order table */ + D_(D_CRIT "error reading orders"); + return -1; + } + + strncpy(mod->name, (char *)xfh.name, 20); + + mod->len = xfh.songlen; + mod->chn = xfh.channels; + mod->pat = xfh.patterns; + mod->ins = xfh.instruments; + mod->rst = xfh.restart; + mod->spd = xfh.tempo; + mod->bpm = xfh.bpm; + mod->trk = mod->chn * mod->pat + 1; + + m->c4rate = C4_NTSC_RATE; + m->period_type = xfh.flags & XM_LINEAR_PERIOD_MODE ? PERIOD_LINEAR : PERIOD_AMIGA; + + memcpy(mod->xxo, xfh.order, mod->len); + /*tracker_name[20] = 0;*/ + snprintf(tracker_name, 21, "%-20.20s", xfh.tracker); + for (i = 20; i >= 0; i--) { + if (tracker_name[i] == 0x20) + tracker_name[i] = 0; + if (tracker_name[i]) + break; + } + + /* OpenMPT accurately emulates weird FT2 bugs */ + if (!strncmp(tracker_name, "FastTracker v2.00", 17)) { + m->quirk |= QUIRK_FT2BUGS; +#ifndef LIBXMP_CORE_PLAYER + claims_ft2 = 1; +#endif + } else if (!strncmp(tracker_name, "OpenMPT ", 8)) { + m->quirk |= QUIRK_FT2BUGS; + } +#ifndef LIBXMP_CORE_PLAYER + if (xfh.headersz == 0x0113) { + strcpy(tracker_name, "unknown tracker"); + m->quirk &= ~QUIRK_FT2BUGS; + } else if (*tracker_name == 0) { + strcpy(tracker_name, "Digitrakker"); /* best guess */ + m->quirk &= ~QUIRK_FT2BUGS; + } + + /* See MMD1 loader for explanation */ + if (!strncmp(tracker_name, "MED2XM by J.Pynnone", 19)) { + if (mod->bpm <= 10) { + mod->bpm = 125 * (0x35 - mod->bpm * 2) / 33; + } + m->quirk &= ~QUIRK_FT2BUGS; + } + + if (!strncmp(tracker_name, "FastTracker v 2.00", 18)) { + strcpy(tracker_name, "old ModPlug Tracker"); + m->quirk &= ~QUIRK_FT2BUGS; + is_mpt_116 = 1; + } + + libxmp_set_type(m, "%s XM %d.%02d", tracker_name, xfh.version >> 8, xfh.version & 0xff); +#else + libxmp_set_type(m, tracker_name); +#endif + + MODULE_INFO(); + + /* Honor header size */ + if (hio_seek(f, start + xfh.headersz + 60, SEEK_SET) < 0) { + return -1; + } + + /* XM 1.02/1.03 has a different patterns and instruments order */ + if (xfh.version <= 0x0103) { + if (load_instruments(m, xfh.version, f) < 0) { + return -1; + } + if (load_patterns(m, xfh.version, f) < 0) { + return -1; + } + } else { + if (load_patterns(m, xfh.version, f) < 0) { + return -1; + } + if (load_instruments(m, xfh.version, f) < 0) { + return -1; + } + } + + D_(D_INFO "Stored samples: %d", mod->smp); + + /* XM 1.02 stores all samples after the patterns */ + if (xfh.version <= 0x0103) { + for (i = 0; i < mod->ins; i++) { + for (j = 0; j < mod->xxi[i].nsm; j++) { + int sid = mod->xxi[i].sub[j].sid; + if (libxmp_load_sample(m, f, SAMPLE_FLAG_DIFF, &mod->xxs[sid], NULL) < 0) { + return -1; + } + } + } + } + +#ifndef LIBXMP_CORE_PLAYER + /* Load MPT properties from the end of the file. */ + while (1) { + uint32 ext = hio_read32b(f); + uint32 sz = hio_read32l(f); + int known = 0; + + if (hio_error(f) || sz > 0x7fffffff /* INT32_MAX */) + break; + + switch (ext) { + case MAGIC4('t','e','x','t'): /* Song comment */ + known = 1; + if (m->comment != NULL) + break; + + if ((m->comment = (char *)malloc(sz + 1)) == NULL) + break; + + sz = hio_read(m->comment, 1, sz, f); + m->comment[sz] = '\0'; + + for (i = 0; i < (int)sz; i++) { + int b = m->comment[i]; + if (b == '\r') { + m->comment[i] = '\n'; + } else if ((b < 32 || b > 127) && b != '\n' + && b != '\t') { + m->comment[i] = '.'; + } + } + break; + + case MAGIC4('M','I','D','I'): /* MIDI config */ + case MAGIC4('P','N','A','M'): /* Pattern names */ + case MAGIC4('C','N','A','M'): /* Channel names */ + case MAGIC4('C','H','F','X'): /* Channel plugins */ + case MAGIC4('X','T','P','M'): /* Inst. extensions */ + known = 1; + /* fall-through */ + + default: + /* Plugin definition */ + if ((ext & MAGIC4('F','X', 0, 0)) == MAGIC4('F','X', 0, 0)) + known = 1; + + if (sz) hio_seek(f, sz, SEEK_CUR); + break; + } + + if(known && claims_ft2) + is_mpt_116 = 1; + + if (ext == MAGIC4('X','T','P','M')) + break; + } + + if (is_mpt_116) { + libxmp_set_type(m, "ModPlug Tracker 1.16 XM %d.%02d", + xfh.version >> 8, xfh.version & 0xff); + + m->mvolbase = 48; + m->mvol = 48; + libxmp_apply_mpt_preamp(m); + } +#endif + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = 0x80; + } + + m->quirk |= QUIRKS_FT2; + m->read_event_type = READ_EVENT_FT2; + + return 0; +} diff --git a/thirdparty/libxmp/src/loaders/xmf_load.c b/thirdparty/libxmp/src/loaders/xmf_load.c new file mode 100644 index 000000000..3b57a6c5d --- /dev/null +++ b/thirdparty/libxmp/src/loaders/xmf_load.c @@ -0,0 +1,433 @@ +/* Extended Module Player + * Copyright (C) 2023 Alice Rowan + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Loader for Astroidea XMF, used by Imperium Galactica and some other modules. + * This format is completely unrelated to the MIDI XMF format. + */ + +#include "loader.h" + +static int xmf_test(HIO_HANDLE *, char *, const int); +static int xmf_load(struct module_data *, HIO_HANDLE *, const int); + +const struct format_loader libxmp_loader_xmf = { + "Astroidea XMF", + xmf_test, + xmf_load +}; + +#define XMF_SAMPLE_ARRAY_SIZE (16 * 256) + +static int xmf_test(HIO_HANDLE *f, char *t, const int start) +{ + uint8 buf[XMF_SAMPLE_ARRAY_SIZE]; + uint8 *pos; + uint32 samples_length = 0; + long length; + int samples_start; + int num_patterns; + int num_channels; + int num_ins; + int xmf_type; + int i; + + /* This value is 0x03 for all Imperium Galactica modules. + * The demo "Prostate 666" and all other XMFs use 0x04 instead. */ + xmf_type = hio_read8(f); + if (xmf_type != 0x03 && xmf_type != 0x04) + return -1; + + if (hio_read(buf, 1, XMF_SAMPLE_ARRAY_SIZE, f) < XMF_SAMPLE_ARRAY_SIZE) + return -1; + + /* Test instruments */ + pos = buf; + num_ins = 0; + for (i = 0; i < 256; i++) { + uint32 loopstart = readmem24l(pos + 0); + uint32 loopend = readmem24l(pos + 3); + uint32 datastart = readmem24l(pos + 6); + uint32 dataend = readmem24l(pos + 9); + uint8 flags = pos[13]; + uint16 srate = readmem16l(pos + 14); + + uint32 len = dataend - datastart; + pos += 16; + + if (flags & ~(0x04 | 0x08 | 0x10)) { + D_(D_WARN "not XMF: smp %d: unknown flags", i); + return -1; + } + /* if ping-pong loop flag is enabled, normal loop flag should be enabled too */ + if ((flags & (0x08 | 0x10)) == 0x10) { + D_(D_WARN "not XMF: smp %d: inconsistent loop flags", i); + return -1; + } + /* if loop flag is enabled, the loop should have a valid end point */ + if ((flags & 0x08) && !loopend) { + D_(D_WARN "not XMF: smp %d: inconsistent loop data", i); + return -1; + } + /* a 16-bit sample should have an even number of bytes */ + if ((flags & 0x04) && (len & 1)) { + D_(D_WARN "not XMF: smp %d: inconsistent 16-bit sample length", i); + return -1; + } + /* if this slot contains a valid sample, it should have a somewhat realistic middle-c frequency */ + if (len && srate < 100) { + D_(D_WARN "not XMF: smp %d: low sample rate", i); + return -1; + } + + /* Despite the data start and end values, samples are stored + * sequentially after the pattern data. These fields are still + * required to calculate the sample length. */ + if (datastart > dataend) { + D_(D_WARN "not XMF: smp %d: data start %u > end %u", + i, (unsigned)datastart, (unsigned)dataend); + return -1; + } + + samples_length += len; + + /* All known XMFs have well-formed loops. */ + if (loopend != 0 && (loopstart >= len || loopend > len || loopstart > loopend)) { + D_(D_WARN "not XMF: smp %d: bad loop %u %u (len: %u)", + i, (unsigned)loopstart, (unsigned)loopend, (unsigned)len); + return -1; + } + + if (len > 0) + num_ins = i + 1; + } + if (num_ins > MAX_INSTRUMENTS) + return -1; + + /* Get pattern data size. */ + if (hio_read(buf, 1, 258, f) < 258) + return -1; + + num_channels = buf[256] + 1; + num_patterns = buf[257] + 1; + + if (num_channels > XMP_MAX_CHANNELS) + return -1; + + /* Test total module length */ + samples_start = 0x1103 + num_channels + num_patterns * num_channels * 64 * 6; + length = hio_size(f); + if (length < samples_start || (size_t)length - samples_start < samples_length) { + D_(D_WARN "not XMF: file length %ld is shorter than required %zu", + length, (size_t)samples_start + samples_length); + return -1; + } + + libxmp_read_title(f, t, 0); + + return 0; +} + + +/* TODO: command pages would be nice, but no official modules rely on 5xy/6xy. */ +static void xmf_insert_effect(struct xmp_event *event, uint8 fxt, uint8 fxp, int chn) +{ + if (chn == 0) { + event->fxt = fxt; + event->fxp = fxp; + } else { + event->f2t = fxt; + event->f2p = fxp; + } +} + +static void xmf_translate_effect(struct xmp_event *event, uint8 effect, uint8 param, int chn) +{ + /* Most effects are Protracker compatible. Only the effects actually + * implemented by Imperium Galactica are handled here. */ + + switch (effect) { + case 0x00: /* none/arpeggio */ + case 0x01: /* portamento up */ + case 0x02: /* portamento down */ + case 0x0f: /* set speed + set BPM */ + if (param) { + xmf_insert_effect(event, effect, param, chn); + } + break; + + case 0x03: /* tone portamento */ + case 0x04: /* vibrato */ + case 0x0c: /* set volume */ + case 0x0d: /* break */ + xmf_insert_effect(event, effect, param, chn); + break; + + case 0x05: /* volume slide + tone portamento */ + case 0x06: /* volume slide + vibrato */ + if (effect == 0x05) { + xmf_insert_effect(event, FX_TONEPORTA, 0, chn ^ 1); + } + if (effect == 0x06) { + xmf_insert_effect(event, FX_VIBRATO, 0, chn ^ 1); + } + + /* fall-through */ + + case 0x0a: /* volume slide */ + if (param & 0x0f) { + /* down takes precedence and uses the full param. */ + xmf_insert_effect(event, FX_VOLSLIDE_DN, param << 2, chn); + } else if (param & 0xf0) { + xmf_insert_effect(event, FX_VOLSLIDE_UP, param >> 2, chn); + } + break; + + case 0x0b: /* pattern jump (jumps to xx + 1) */ + if (param < 255) { + xmf_insert_effect(event, FX_JUMP, param + 1, chn); + } + break; + + case 0x0e: /* extended */ + switch (param >> 4) { + case 0x01: /* fine slide up */ + case 0x02: /* fine slide down */ + case 0x06: /* pattern loop (broken) */ + case 0x09: /* note retrigger (TODO: only once) */ + case 0x0c: /* note cut */ + case 0x0d: /* note delay */ + case 0x0e: /* pattern delay */ + if (param & 0x0f) { + xmf_insert_effect(event, effect, param, chn); + } + break; + + case 0x04: /* vibrato waveform */ + param &= 3; + param = param < 3 ? param : 2; + xmf_insert_effect(event, effect, param, chn); + break; + + case 0x0a: /* fine volume slide up */ + if (param & 0x0f) { + xmf_insert_effect(event, FX_F_VSLIDE_UP, (param & 0x0f) << 2, chn); + } + break; + + case 0x0b: /* fine volume slide down */ + if (param & 0x0f) { + xmf_insert_effect(event, FX_F_VSLIDE_DN, (param & 0x0f) << 2, chn); + } + break; + } + break; + + case 0x10: /* panning (4-bit, GUS driver only) */ + param &= 0x0f; + param |= (param << 4); + xmf_insert_effect(event, FX_SETPAN, param, chn); + break; + + case 0x11: /* Ultra Tracker retrigger */ + /* TODO: should support the full param range, needs testing. */ + xmf_insert_effect(event, FX_EXTENDED, (EX_RETRIG << 4) | (param & 0x0f), chn); + break; + } +} + +static int xmf_load(struct module_data *m, HIO_HANDLE *f, const int start) +{ + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + uint8 *buf, *pos; + size_t pat_sz; + int xmf_type; + int i, j, k; + + LOAD_INIT(); + + /* Imperium Galactica uses 0x03, other Astroidea tracks use 0x04 */ + xmf_type = hio_read8(f); + if(xmf_type == 0x03) + snprintf(mod->type, XMP_NAME_SIZE, "Imperium Galactica XMF"); + else + snprintf(mod->type, XMP_NAME_SIZE, "Astroidea XMF"); + + MODULE_INFO(); + + if ((buf = (uint8 *)malloc(XMF_SAMPLE_ARRAY_SIZE)) == NULL) + return -1; + + /* Count instruments */ + if (hio_read(buf, 1, XMF_SAMPLE_ARRAY_SIZE, f) < XMF_SAMPLE_ARRAY_SIZE) + goto err; + + mod->ins = 0; + pos = buf; + for (i = 0; i < 256; i++, pos += 16) { + if (readmem24l(pos + 9) > readmem24l(pos + 6)) + mod->ins = i; + } + mod->ins++; + mod->smp = mod->ins; + + if (libxmp_init_instrument(m) < 0) + goto err; + + /* Instruments */ + pos = buf; + for (i = 0; i < mod->ins; i++, pos += 16) { + struct extra_sample_data *xtra = &(m->xtra[i]); + struct xmp_instrument *xxi = &(mod->xxi[i]); + struct xmp_sample *xxs = &(mod->xxs[i]); + struct xmp_subinstrument *sub; + + if (libxmp_alloc_subinstrument(mod, i, 1) < 0) + goto err; + + sub = &(xxi->sub[0]); + + xxs->len = readmem24l(pos + 9) - readmem24l(pos + 6); + xxs->lps = readmem24l(pos + 0); + xxs->lpe = readmem24l(pos + 3); + xtra->c5spd = readmem16l(pos + 14); + sub->vol = pos[12]; + sub->sid = i; + + /* The Sound Blaster driver will only loop if both the + * loop start and loop end are non-zero. The Sound Blaster + * driver does not support 16-bit samples or bidirectional + * looping, and plays these as regular 8-bit looped samples. + * + * GUS: 16-bit samples are loaded as 8-bit but play as 16-bit. + * If the first sample is 16-bit it will partly work (due to + * having a GUS RAM address of 0?). Other 16-bit samples will + * read from silence, garbage, or other samples. + */ + if (pos[13] & 0x04) { /* GUS 16-bit flag */ + xxs->flg |= XMP_SAMPLE_16BIT; + xxs->len >>= 1; + } + if (pos[13] & 0x08) /* GUS loop enable */ + xxs->flg |= XMP_SAMPLE_LOOP; + if (pos[13] & 0x10) /* GUS reverse flag */ + xxs->flg |= XMP_SAMPLE_LOOP_BIDIR; + + if (xxs->len > 0) + xxi->nsm = 1; + + D_(D_INFO "[%2X] %06x %06x %06x %c%c V%02x %5d", i, + xxs->len, xxs->lps, xxs->lpe, + xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', + xxs->flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : ' ', + sub->vol, (int)xtra->c5spd); + } + + /* Sequence */ + if (hio_read(mod->xxo, 1, 256, f) < 256) + return -1; + + mod->chn = hio_read8(f) + 1; + mod->pat = hio_read8(f) + 1; + mod->trk = mod->chn * mod->pat; + + for (i = 0; i < 256; i++) { + if (mod->xxo[i] == 0xff) + break; + } + mod->len = i; + + /* Panning table (supported by the Gravis UltraSound driver only) */ + if (hio_read(buf, 1, mod->chn, f) < mod->chn) + goto err; + + for (i = 0; i < mod->chn; i++) { + mod->xxc[i].pan = 0x80 + (buf[i] - 7) * 16; + if (mod->xxc[i].pan > 255) + mod->xxc[i].pan = 255; + } + + D_(D_INFO "Module length: %d", mod->len); + + pat_sz = mod->chn * 6 * 64; + if (pat_sz > XMF_SAMPLE_ARRAY_SIZE) { + if ((pos = (uint8 *)realloc(buf, pat_sz)) == NULL) + goto err; + buf = pos; + } + + if (libxmp_init_pattern(mod) < 0) + goto err; + + /* Patterns */ + D_(D_INFO "Stored patterns: %d", mod->pat); + + for (i = 0; i < mod->pat; i++) { + if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) + goto err; + + if (hio_read(buf, 1, pat_sz, f) < pat_sz) + goto err; + + pos = buf; + for (j = 0; j < 64; j++) { + for (k = 0; k < mod->chn; k++) { + event = &EVENT(i, k, j); + + if (pos[0] > 0) + event->note = pos[0] + 36; + event->ins = pos[1]; + + xmf_translate_effect(event, pos[2], pos[5], 0); + xmf_translate_effect(event, pos[3], pos[4], 1); + pos += 6; + } + } + } + free(buf); + + /* Sample data */ + D_(D_INFO "Stored samples: %d", mod->smp); + + /* Despite the GUS sample start and end pointers saved in the file, + * these are actually just loaded sequentially. */ + for (i = 0; i < mod->ins; i++) { + if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL)) + return -1; + } + + /* With the Sound Blaster driver, full volume samples have a -0dB mix. + * Doing this in libxmp (x4 mvolbase) clips a little bit, so use a + * slightly lower level (x3 mvolbase, ~192 in IT terms). + * + * This only applies to the Imperium Galactica tracks; the tracks with + * 0x04 use the full GUS volume range. + */ + m->volbase = 0xff; + m->mvolbase = 48; + m->mvol = (xmf_type == 0x03) ? m->mvolbase * 3 : m->mvolbase; + return 0; + + err: + free(buf); + return -1; +} diff --git a/thirdparty/libxmp/src/md5.c b/thirdparty/libxmp/src/md5.c new file mode 100644 index 000000000..b9433a4d3 --- /dev/null +++ b/thirdparty/libxmp/src/md5.c @@ -0,0 +1,240 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include "common.h" +#include "md5.h" + +#define PUT_64BIT_LE(cp, value) do { \ + (cp)[7] = (value) >> 56; \ + (cp)[6] = (value) >> 48; \ + (cp)[5] = (value) >> 40; \ + (cp)[4] = (value) >> 32; \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +#define PUT_32BIT_LE(cp, value) do { \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +static uint8 PADDING[MD5_BLOCK_LENGTH] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(uint32 state[4], const uint8 block[MD5_BLOCK_LENGTH]) +{ + uint32 a, b, c, d, in[MD5_BLOCK_LENGTH / 4]; + +#ifndef WORDS_BIGENDIAN + memcpy(in, block, sizeof(in)); +#else + for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) { + in[a] = (uint32)( + (uint32)(block[a * 4 + 0]) | + (uint32)(block[a * 4 + 1]) << 8 | + (uint32)(block[a * 4 + 2]) << 16 | + (uint32)(block[a * 4 + 3]) << 24); + } +#endif + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(MD5_CTX *ctx) +{ + ctx->count = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len) +{ + size_t have, need; + + /* Check how many bytes we already have and how many more we need. */ + have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); + need = MD5_BLOCK_LENGTH - have; + + /* Update bitcount */ + ctx->count += (uint64)len << 3; + + if (len >= need) { + if (have != 0) { + memcpy(ctx->buffer + have, input, need); + MD5Transform(ctx->state, ctx->buffer); + input += need; + len -= need; + have = 0; + } + + /* Process data in MD5_BLOCK_LENGTH-byte chunks. */ + while (len >= MD5_BLOCK_LENGTH) { + MD5Transform(ctx->state, input); + input += MD5_BLOCK_LENGTH; + len -= MD5_BLOCK_LENGTH; + } + } + + /* Handle any remaining bytes of data. */ + if (len != 0) + memcpy(ctx->buffer + have, input, len); +} + +/* + * Pad pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void MD5Pad(MD5_CTX *ctx) +{ + uint8 count[8]; + size_t padlen; + + /* Convert count to 8 bytes in little endian order. */ + PUT_64BIT_LE(count, ctx->count); + + /* Pad out to 56 mod 64. */ + padlen = MD5_BLOCK_LENGTH - + ((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); + if (padlen < 1 + 8) + padlen += MD5_BLOCK_LENGTH; + MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ + MD5Update(ctx, count, 8); +} + +/* + * Final wrapup--call MD5Pad, fill in digest and zero out ctx. + */ +void MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) +{ + int i; + + MD5Pad(ctx); + if (digest != NULL) { + for (i = 0; i < 4; i++) + PUT_32BIT_LE(digest + i * 4, ctx->state[i]); + memset(ctx, 0, sizeof(*ctx)); + } +} + diff --git a/thirdparty/libxmp/src/md5.h b/thirdparty/libxmp/src/md5.h new file mode 100644 index 000000000..b5c44bac9 --- /dev/null +++ b/thirdparty/libxmp/src/md5.h @@ -0,0 +1,37 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + */ + +#ifndef LIBXMP_MD5_H +#define LIBXMP_MD5_H + +#include "common.h" + +#define MD5_BLOCK_LENGTH 64 +#define MD5_DIGEST_LENGTH 16 +#define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1) + +typedef struct MD5Context { + uint32 state[4]; /* state */ + uint64 count; /* number of bits, mod 2^64 */ + uint8 buffer[MD5_BLOCK_LENGTH]; /* input buffer */ +} MD5_CTX; + +LIBXMP_BEGIN_DECLS + +void MD5Init(MD5_CTX *); +void MD5Update(MD5_CTX *, const unsigned char *, size_t); +void MD5Final(uint8[MD5_DIGEST_LENGTH], MD5_CTX *); + +LIBXMP_END_DECLS + +#endif /* LIBXMP_MD5_H */ + diff --git a/thirdparty/libxmp/src/mdataio.h b/thirdparty/libxmp/src/mdataio.h new file mode 100644 index 000000000..c076c0894 --- /dev/null +++ b/thirdparty/libxmp/src/mdataio.h @@ -0,0 +1,124 @@ +#ifndef LIBXMP_MDATAIO_H +#define LIBXMP_MDATAIO_H + +#include +#include "common.h" + +static inline ptrdiff_t CAN_READ(MFILE *m) +{ + if (m->size >= 0) + return m->pos >= 0 ? m->size - m->pos : 0; + + return INT_MAX; +} + +static inline uint8 mread8(MFILE *m, int *err) +{ + uint8 x = 0xff; + size_t r = mread(&x, 1, 1, m); + if (err) { + *err = (r == 1) ? 0 : EOF; + } + return x; +} + +static inline int8 mread8s(MFILE *m, int *err) +{ + int r = mgetc(m); + if (err) { + *err = (r < 0) ? EOF : 0; + } + return (int8)r; +} + +static inline uint16 mread16l(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 2) { + uint16 n = readmem16l(m->start + m->pos); + m->pos += 2; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffff; + } +} + +static inline uint16 mread16b(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 2) { + uint16 n = readmem16b(m->start + m->pos); + m->pos += 2; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffff; + } +} + +static inline uint32 mread24l(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 3) { + uint32 n = readmem24l(m->start + m->pos); + m->pos += 3; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffffffff; + } +} + +static inline uint32 mread24b(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 3) { + uint32 n = readmem24b(m->start + m->pos); + m->pos += 3; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffffffff; + } +} + +static inline uint32 mread32l(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 4) { + uint32 n = readmem32l(m->start + m->pos); + m->pos += 4; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffffffff; + } +} + +static inline uint32 mread32b(MFILE *m, int *err) +{ + ptrdiff_t can_read = CAN_READ(m); + if (can_read >= 4) { + uint32 n = readmem32b(m->start + m->pos); + m->pos += 4; + if(err) *err = 0; + return n; + } else { + m->pos += can_read; + if(err) *err = EOF; + return 0xffffffff; + } +} + +#endif diff --git a/thirdparty/libxmp/src/med_extras.c b/thirdparty/libxmp/src/med_extras.c new file mode 100644 index 000000000..9f890b3b2 --- /dev/null +++ b/thirdparty/libxmp/src/med_extras.c @@ -0,0 +1,448 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "virtual.h" +#include "effects.h" +#include "med_extras.h" + +#ifdef __SUNPRO_C +#pragma error_messages (off,E_STATEMENT_NOT_REACHED) +#endif + +/* Commands in the volume and waveform sequence table: + * + * Cmd Vol Wave Action + * + * 0xff END End sequence + * 0xfe JMP Jump + * 0xfd - ARE End arpeggio definition + * 0xfc - ARP Begin arpeggio definition + * 0xfb HLT Halt + * 0xfa JWS JVS Jump waveform/volume sequence + * 0xf9 - - + * 0xf8 - - + * 0xf7 - VWF Set vibrato waveform + * 0xf6 EST RES ?/reset pitch + * 0xf5 EN2 VBS Looping envelope/set vibrato speed + * 0xf4 EN1 VBD One shot envelope/set vibrato depth + * 0xf3 CHU Change volume/pitch up speed + * 0xf2 CHD Change volume/pitch down speed + * 0xf1 WAI Wait + * 0xf0 SPD Set speed + */ + +#define VT ((ce->vp >= 0 && ce->vp < ie->vtlen) ? me->vol_table[xc->ins][ce->vp++] : 0xff) +#define WT ((ce->wp >= 0 && ce->wp < ie->wtlen) ? me->wav_table[xc->ins][ce->wp++] : 0xff) +#define VT_SKIP ce->vp++ +#define WT_SKIP ce->wp++ + +#define ARP(idx) ((idx) < ie->wtlen ? me->wav_table[xc->ins][(idx)] : 0xfd) + + +static const int sine[32] = { + 0, 49, 97, 141, 180, 212, 235, 250, + 255, 250, 235, 212, 180, 141, 97, 49, + 0, -49, -97,-141,-180,-212,-235,-250, + -255,-250,-235,-212,-180,-141, -97, -49 +}; + +int libxmp_med_change_period(struct context_data *ctx, struct channel_data *xc) +{ + struct med_channel_extras *ce = (struct med_channel_extras *)xc->extra; + int vib; + + /* Vibrato */ + +#if 0 + if (ce->vib_wf >= xxi[xc->ins].nsm) /* invalid waveform */ + return 0; + + if (xxs[xxi[xc->ins][ce->vib_wf].sid].len != 32) + return 0; +#endif + + /* FIXME: always using sine waveform */ + + vib = (sine[ce->vib_idx >> 5] * ce->vib_depth) >> 10; + ce->vib_idx += ce->vib_speed; + ce->vib_idx %= (32 << 5); + + return vib; +} + + +int libxmp_med_linear_bend(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + struct xmp_instrument *xxi = &m->mod.xxi[xc->ins]; + struct med_module_extras *me = (struct med_module_extras *)m->extra; + struct med_channel_extras *ce = (struct med_channel_extras *)xc->extra; + struct med_instrument_extras *ie = MED_INSTRUMENT_EXTRAS(*xxi); + int arp; + + /* Arpeggio */ + + if (ce->arp == 0) + return 0; + + if (ARP(ce->arp) == 0xfd) /* empty arpeggio */ + return 0; + + arp = ARP(ce->aidx); + if (arp == 0xfd) { + ce->aidx = ce->arp; + arp = ARP(ce->aidx); + } + ce->aidx++; + + return (100 << 7) * arp; +} + + +void libxmp_med_play_extras(struct context_data *ctx, struct channel_data *xc, int chn) +{ + struct module_data *m = &ctx->m; + struct player_data *p = &ctx->p; + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi = &m->mod.xxi[xc->ins]; + struct med_module_extras *me; + struct med_channel_extras *ce; + struct med_instrument_extras *ie; + int b, jws = 0, jvs = 0, loop; + int temp; + + if (!HAS_MED_MODULE_EXTRAS(*m)) + return; + + me = (struct med_module_extras *)m->extra; + ce = (struct med_channel_extras *)xc->extra; + ie = MED_INSTRUMENT_EXTRAS(*xxi); + + /* Handle hold/decay */ + + /* on the first row of a held note, continue note if ce->hold is 2 + * (this is set after pre-fetching the next row and see if we + * continue to hold. On remaining rows with hold on, we have the + * FX_MED_HOLD effect and ce->hold set to 1. On the last row, see + * if ce->hold_count is set (meaning that a note was held) and + * ce->hold is 0 (meaning that it's not held anymore). Then + * procceed with normal frame counting until decay. + */ + + if (ce->hold_count) { /* was held in the past */ + if (!ce->hold && p->frame >= ie->hold) { /* but not now */ + SET_NOTE(NOTE_FADEOUT); + ce->hold_count = 0; + } + } else if (ie->hold) { /* has instrument hold */ + if (p->frame >= ie->hold && ce->hold == 0) { + SET_NOTE(NOTE_FADEOUT); + } + } + + if (p->frame == (p->speed - 1) && ce->hold != 2) { + ce->hold = 0; + } + + /* Handle synth */ + + if (me->vol_table[xc->ins] == NULL || me->wav_table[xc->ins] == NULL) { + ce->volume = 64; /* we need this in extras_get_volume() */ + return; + } + + if (p->frame == 0 && TEST(NEW_NOTE)) { + ce->period = xc->period; + if (TEST(NEW_INS)) { + ce->arp = ce->aidx = 0; + ce->vp = ce->vc = ce->vw = 0; + ce->wp = ce->wc = ce->ww = 0; + ce->env_wav = -1; + ce->env_idx = 0; + ce->flags &= ~MED_SYNTH_ENV_LOOP; + ce->vv = 0; + ce->wv = 0; + ce->vs = ie->vts; + ce->ws = ie->wts; + } + } + + if (ce->vs > 0 && ce->vc-- == 0) { + ce->vc = ce->vs - 1; + + if (ce->vw > 0) { + ce->vw--; + goto skip_vol; + } + + loop = jws = 0; + + /* Volume commands */ + + next_vt: + switch (b = VT) { + case 0xff: /* END */ + case 0xfb: /* HLT */ + ce->vp--; + break; + case 0xfe: /* JMP */ + if (loop) /* avoid infinite loop */ + break; + temp = VT; + ce->vp = temp; + loop = 1; + goto next_vt; + case 0xfa: /* JWS */ + jws = VT; + break; + case 0xf5: /* EN2 */ + ce->env_wav = VT; + ce->flags |= MED_SYNTH_ENV_LOOP; + break; + case 0xf4: /* EN1 */ + ce->env_wav = VT; + break; + case 0xf3: /* CHU */ + ce->vv = VT; + break; + case 0xf2: /* CHD */ + ce->vv = -VT; + break; + case 0xf1: /* WAI */ + ce->vw = VT; + break; + case 0xf0: /* SPD */ + ce->vs = VT; + break; + default: + if (b >= 0x00 && b <= 0x40) + ce->volume = b; + } + + skip_vol: + /* volume envelope */ + if (ce->env_wav >= 0 && ce->env_wav < xxi->nsm) { + int sid = xxi->sub[ce->env_wav].sid; + struct xmp_sample *xxs = &mod->xxs[sid]; + if (xxs->len == 0x80) { /* sanity check */ + ce->volume = ((int8)xxs->data[ce->env_idx] + 0x80) >> 2; + ce->env_idx++; + + if (ce->env_idx >= 0x80) { + if (~ce->flags & MED_SYNTH_ENV_LOOP) { + ce->env_wav = -1; + } + ce->env_idx = 0; + } + } + } + + ce->volume += ce->vv; + CLAMP(ce->volume, 0, 64); + + if (ce->ww > 0) { + ce->ww--; + goto skip_wav; + } + + loop = jvs = 0; + + /* Waveform commands */ + + next_wt: + switch (b = WT) { + case 0xff: /* END */ + case 0xfb: /* HLT */ + ce->wp--; + break; + case 0xfe: /* JMP */ + if (loop) /* avoid infinite loop */ + break; + temp = WT; + if (temp == 0xff) { /* handle JMP END case */ + ce->wp--; /* see lepeltheme ins 0x02 */ + break; + } + ce->wp = temp; + loop = 1; + goto next_wt; + case 0xfd: /* ARE */ + break; + case 0xfc: /* ARP */ + ce->arp = ce->aidx = ce->wp++; + while (b != 0xfd && b != 0xff) b = WT; + break; + case 0xfa: /* JVS */ + jvs = WT; + break; + case 0xf7: /* VWF */ + ce->vwf = WT; + break; + case 0xf6: /* RES */ + xc->period = ce->period; + break; + case 0xf5: /* VBS */ + ce->vib_speed = WT; + break; + case 0xf4: /* VBD */ + ce->vib_depth = WT; + break; + case 0xf3: /* CHU */ + ce->wv = -WT; + break; + case 0xf2: /* CHD */ + ce->wv = WT; + break; + case 0xf1: /* WAI */ + ce->ww = WT; + break; + case 0xf0: /* SPD */ + ce->ws = WT; + break; + default: + if (b < xxi->nsm && xxi->sub[b].sid != xc->smp) { + xc->smp = xxi->sub[b].sid; + libxmp_virt_setsmp(ctx, chn, xc->smp); + } + } + + skip_wav: + xc->period += ce->wv; + } + + if (jws) { + ce->wp = jws; + /* jws = 0; */ + } + + if (jvs) { + ce->vp = jvs; + /* jvs = 0; */ + } +} + +int libxmp_med_new_instrument_extras(struct xmp_instrument *xxi) +{ + xxi->extra = calloc (1, sizeof(struct med_instrument_extras)); + if (xxi->extra == NULL) + return -1; + MED_INSTRUMENT_EXTRAS((*xxi))->magic = MED_EXTRAS_MAGIC; + + return 0; +} + +int libxmp_med_new_channel_extras(struct channel_data *xc) +{ + xc->extra = calloc(1, sizeof(struct med_channel_extras)); + if (xc->extra == NULL) + return -1; + MED_CHANNEL_EXTRAS((*xc))->magic = MED_EXTRAS_MAGIC; + + return 0; +} + +void libxmp_med_reset_channel_extras(struct channel_data *xc) +{ + memset((char *)xc->extra + 4, 0, sizeof(struct med_channel_extras) - 4); +} + +void libxmp_med_release_channel_extras(struct channel_data *xc) +{ + free(xc->extra); + xc->extra = NULL; +} + +int libxmp_med_new_module_extras(struct module_data *m) +{ + struct med_module_extras *me; + struct xmp_module *mod = &m->mod; + + m->extra = calloc(1, sizeof(struct med_module_extras)); + if (m->extra == NULL) + return -1; + MED_MODULE_EXTRAS((*m))->magic = MED_EXTRAS_MAGIC; + + me = (struct med_module_extras *)m->extra; + + me->vol_table = (uint8 **) calloc(sizeof(uint8 *), mod->ins); + if (me->vol_table == NULL) + return -1; + me->wav_table = (uint8 **) calloc(sizeof(uint8 *), mod->ins); + if (me->wav_table == NULL) + return -1; + + return 0; +} + +void libxmp_med_release_module_extras(struct module_data *m) +{ + struct med_module_extras *me; + struct xmp_module *mod = &m->mod; + int i; + + me = (struct med_module_extras *)m->extra; + + if (me->vol_table) { + for (i = 0; i < mod->ins; i++) + free(me->vol_table[i]); + free(me->vol_table); + } + + if (me->wav_table) { + for (i = 0; i < mod->ins; i++) + free(me->wav_table[i]); + free(me->wav_table); + } + + free(m->extra); + m->extra = NULL; +} + +void libxmp_med_extras_process_fx(struct context_data *ctx, struct channel_data *xc, + int chn, uint8 note, uint8 fxt, uint8 fxp, int fnum) +{ + switch (fxt) { + case FX_MED_HOLD: + MED_CHANNEL_EXTRAS((*xc))->hold_count++; + MED_CHANNEL_EXTRAS((*xc))->hold = 1; + break; + } +} + +void libxmp_med_hold_hack(struct context_data *ctx, int pat, int chn, int row) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + const int num_rows = mod->xxt[TRACK_NUM(pat, chn)]->rows; + + if (row + 1 < num_rows) { + struct player_data *p = &ctx->p; + struct xmp_event *event = &EVENT(pat, chn, row + 1); + struct channel_data *xc = &p->xc_data[chn]; + + if (event->f2t == FX_MED_HOLD) { + MED_CHANNEL_EXTRAS(*xc)->hold = 2; + } + } +} diff --git a/thirdparty/libxmp/src/med_extras.h b/thirdparty/libxmp/src/med_extras.h new file mode 100644 index 000000000..63087a154 --- /dev/null +++ b/thirdparty/libxmp/src/med_extras.h @@ -0,0 +1,77 @@ +#ifndef LIBXMP_MED_EXTRAS_H +#define LIBXMP_MED_EXTRAS_H + +#define MED_EXTRAS_MAGIC 0x7f20ca5 + +struct med_instrument_extras { + uint32 magic; + int vts; /* Volume table speed */ + int wts; /* Waveform table speed */ + int vtlen; /* Volume table length */ + int wtlen; /* Waveform table length */ + int hold; +}; + +struct med_channel_extras { + uint32 magic; + int vp; /* MED synth volume table pointer */ + int vv; /* MED synth volume slide value */ + int vs; /* MED synth volume speed */ + int vc; /* MED synth volume speed counter */ + int vw; /* MED synth volume wait counter */ + int wp; /* MED synth waveform table pointer */ + int wv; /* MED synth waveform slide value */ + int ws; /* MED synth waveform speed */ + int wc; /* MED synth waveform speed counter */ + int ww; /* MED synth waveform wait counter */ + int period; /* MED synth period for RES */ + int arp; /* MED synth arpeggio start */ + int aidx; /* MED synth arpeggio index */ + int vwf; /* MED synth vibrato waveform */ + int vib_depth; /* MED synth vibrato depth */ + int vib_speed; /* MED synth vibrato speed */ + int vib_idx; /* MED synth vibrato index */ + int vib_wf; /* MED synth vibrato waveform */ + int volume; /* MED synth note volume */ + int hold; /* MED note on hold flag */ + int hold_count; /* MED note on hold frame counter */ + int env_wav; /* MED synth volume envelope waveform */ + int env_idx; /* MED synth volume envelope index */ +#define MED_SYNTH_ENV_LOOP (1 << 0) + int flags; /* flags */ +}; + +struct med_module_extras { + uint32 magic; + uint8 **vol_table; /* MED volume sequence table */ + uint8 **wav_table; /* MED waveform sequence table */ +}; + +#define MED_INSTRUMENT_EXTRAS(x) ((struct med_instrument_extras *)(x).extra) +#define HAS_MED_INSTRUMENT_EXTRAS(x) \ + (MED_INSTRUMENT_EXTRAS(x) != NULL && \ + MED_INSTRUMENT_EXTRAS(x)->magic == MED_EXTRAS_MAGIC) + +#define MED_CHANNEL_EXTRAS(x) ((struct med_channel_extras *)(x).extra) +#define HAS_MED_CHANNEL_EXTRAS(x) \ + (MED_CHANNEL_EXTRAS(x) != NULL && \ + MED_CHANNEL_EXTRAS(x)->magic == MED_EXTRAS_MAGIC) + +#define MED_MODULE_EXTRAS(x) ((struct med_module_extras *)(x).extra) +#define HAS_MED_MODULE_EXTRAS(x) \ + (MED_MODULE_EXTRAS(x) != NULL && \ + MED_MODULE_EXTRAS(x)->magic == MED_EXTRAS_MAGIC) + +int libxmp_med_change_period(struct context_data *, struct channel_data *); +int libxmp_med_linear_bend(struct context_data *, struct channel_data *); +int libxmp_med_get_vibrato(struct channel_data *); +void libxmp_med_play_extras(struct context_data *, struct channel_data *, int); +int libxmp_med_new_instrument_extras(struct xmp_instrument *); +int libxmp_med_new_channel_extras(struct channel_data *); +void libxmp_med_reset_channel_extras(struct channel_data *); +void libxmp_med_release_channel_extras(struct channel_data *); +int libxmp_med_new_module_extras(struct module_data *); +void libxmp_med_release_module_extras(struct module_data *); +void libxmp_med_extras_process_fx(struct context_data *, struct channel_data *, int, uint8, uint8, uint8, int); + +#endif diff --git a/thirdparty/libxmp/src/memio.c b/thirdparty/libxmp/src/memio.c new file mode 100644 index 000000000..b3b61cd54 --- /dev/null +++ b/thirdparty/libxmp/src/memio.c @@ -0,0 +1,118 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "memio.h" + +static inline ptrdiff_t CAN_READ(MFILE *m) +{ + return m->pos >= 0 ? m->size - m->pos : 0; +} + + +int mgetc(MFILE *m) +{ + if (CAN_READ(m) >= 1) + return *(uint8 *)(m->start + m->pos++); + return EOF; +} + +size_t mread(void *buf, size_t size, size_t num, MFILE *m) +{ + size_t should_read = size * num; + ptrdiff_t can_read = CAN_READ(m); + + if (!size || !num || can_read <= 0) { + return 0; + } + + if (should_read > can_read) { + memcpy(buf, m->start + m->pos, can_read); + m->pos += can_read; + + return can_read / size; + } else { + memcpy(buf, m->start + m->pos, should_read); + m->pos += should_read; + + return num; + } +} + + +int mseek(MFILE *m, long offset, int whence) +{ + ptrdiff_t ofs = offset; + + switch (whence) { + case SEEK_SET: + break; + case SEEK_CUR: + ofs += m->pos; + break; + case SEEK_END: + ofs += m->size; + break; + default: + return -1; + } + if (ofs < 0) return -1; + if (ofs > m->size) + ofs = m->size; + m->pos = ofs; + return 0; +} + +long mtell(MFILE *m) +{ + return (long)m->pos; +} + +int meof(MFILE *m) +{ + return CAN_READ(m) <= 0; +} + +MFILE *mopen(const void *ptr, long size, int free_after_use) +{ + MFILE *m; + + m = (MFILE *) malloc(sizeof(MFILE)); + if (m == NULL) + return NULL; + + m->start = (const unsigned char *)ptr; + m->pos = 0; + m->size = size; + m->free_after_use = free_after_use; + + return m; +} + +int mclose(MFILE *m) +{ + if (m->free_after_use) + free((void *)m->start); + free(m); + return 0; +} + diff --git a/thirdparty/libxmp/src/memio.h b/thirdparty/libxmp/src/memio.h new file mode 100644 index 000000000..66bdc0921 --- /dev/null +++ b/thirdparty/libxmp/src/memio.h @@ -0,0 +1,26 @@ +#ifndef LIBXMP_MEMIO_H +#define LIBXMP_MEMIO_H + +#include +#include "common.h" + +typedef struct { + const unsigned char *start; + ptrdiff_t pos; + ptrdiff_t size; + int free_after_use; +} MFILE; + +LIBXMP_BEGIN_DECLS + +MFILE *mopen(const void *, long, int); +int mgetc(MFILE *stream); +size_t mread(void *, size_t, size_t, MFILE *); +int mseek(MFILE *, long, int); +long mtell(MFILE *); +int mclose(MFILE *); +int meof(MFILE *); + +LIBXMP_END_DECLS + +#endif diff --git a/thirdparty/libxmp/src/miniz.h b/thirdparty/libxmp/src/miniz.h new file mode 100644 index 000000000..9fcfffcc8 --- /dev/null +++ b/thirdparty/libxmp/src/miniz.h @@ -0,0 +1,1422 @@ +#ifndef MINIZ_EXPORT +#define MINIZ_EXPORT +#endif +/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateReset/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once + + + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ + +/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ +/*#define MINIZ_NO_DEFLATE_APIS */ + +/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ +/*#define MINIZ_NO_INFLATE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#ifdef MINIZ_NO_INFLATE_APIS +#define MINIZ_NO_ARCHIVE_APIS +#endif + +#ifdef MINIZ_NO_DEFLATE_APIS +#define MINIZ_NO_ARCHIVE_WRITING_APIS +#endif + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +/* Set MINIZ_LITTLE_ENDIAN only if not set */ +#if !defined(MINIZ_LITTLE_ENDIAN) +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#else + +#if MINIZ_X86_OR_X64_CPU +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#endif +#endif + +/* Using unaligned loads and stores causes errors when using UBSan */ +#if defined(__has_feature) +#if __has_feature(undefined_behavior_sanitizer) +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +MINIZ_EXPORT void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* Method */ +#define MZ_DEFLATED 8 + +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "11.0.2" +#define MZ_VERNUM 0xB002 +#define MZ_VER_MAJOR 11 +#define MZ_VER_MINOR 2 +#define MZ_VER_REVISION 0 +#define MZ_VER_SUBREVISION 0 + +#ifndef MINIZ_NO_ZLIB_APIS + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +MINIZ_EXPORT const char *mz_version(void); + +#ifndef MINIZ_NO_DEFLATE_APIS + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + +/* Initializes a decompressor. */ +MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ +MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +MINIZ_EXPORT const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream + +#ifndef MINIZ_NO_DEFLATE_APIS +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflateReset mz_inflateReset +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define uncompress2 mz_uncompress2 +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + + + + + +#pragma once +#include +#include +#include +#include + + + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag +{ + mz_uint32 m_dummy1; + mz_uint32 m_dummy2; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t +#endif + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) +#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) +#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); +extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif + #pragma once + + +#ifndef MINIZ_NO_DEFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); +MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + #pragma once + +/* ------------------- Low-level Decompression API Definitions */ + +#ifndef MINIZ_NO_INFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); +MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); +#endif + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; + mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; + mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; + mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; + mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; + mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#pragma once + + +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; + +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; + + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; + + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; + + /* File's compressed size. */ + mz_uint64 m_comp_size; + + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; + + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; + + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; + + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; + + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +#ifdef MINIZ_NO_TIME + MZ_TIME_T m_padding; +#else + MZ_TIME_T m_time; +#endif +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, + /*After adding a compressed file, seek back + to local file header and set the correct sizes*/ + MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct +{ + mz_zip_archive *pZip; + mz_uint flags; + + int status; + + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +#ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint padding; +#else + mz_uint file_crc32; +#endif + +} mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif + +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); +#endif + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +#endif + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ +/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ +MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a pointer to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +#ifndef MINIZ_NO_STDIO +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); +#endif + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINIZ_NO_ARCHIVE_APIS */ diff --git a/thirdparty/libxmp/src/misc.c b/thirdparty/libxmp/src/misc.c new file mode 100644 index 000000000..c775c04a0 --- /dev/null +++ b/thirdparty/libxmp/src/misc.c @@ -0,0 +1,30 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "xmp.h" + +int xmp_syserrno (void) +{ + return errno; +} + diff --git a/thirdparty/libxmp/src/mix_all.c b/thirdparty/libxmp/src/mix_all.c new file mode 100644 index 000000000..b39826c67 --- /dev/null +++ b/thirdparty/libxmp/src/mix_all.c @@ -0,0 +1,456 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "virtual.h" +#include "mixer.h" +#include "precomp_lut.h" + +/* Mixers + * + * To increase performance eight mixers are defined, one for each + * combination of the following parameters: interpolation, resolution + * and number of channels. + */ +#define NEAREST_NEIGHBOR() do { \ + smp_in = ((int16)sptr[pos] << 8); \ +} while (0) + +#define NEAREST_NEIGHBOR_16BIT() do { \ + smp_in = sptr[pos]; \ +} while (0) + +#define LINEAR_INTERP() do { \ + smp_l1 = ((int16)sptr[pos] << 8); \ + smp_dt = ((int16)sptr[pos + 1] << 8) - smp_l1; \ + smp_in = smp_l1 + (((frac >> 1) * smp_dt) >> (SMIX_SHIFT - 1)); \ +} while (0) + +#define LINEAR_INTERP_16BIT() do { \ + smp_l1 = sptr[pos]; \ + smp_dt = sptr[pos + 1] - smp_l1; \ + smp_in = smp_l1 + (((frac >> 1) * smp_dt) >> (SMIX_SHIFT - 1)); \ +} while (0) + +/* The following lut settings are PRECOMPUTED. If you plan on changing these + * settings, you MUST also regenerate the arrays. + */ +/* number of bits used to scale spline coefs */ +#define SPLINE_QUANTBITS 14 +#define SPLINE_SHIFT (SPLINE_QUANTBITS) + +/* log2(number) of precalculated splines (range is [4..14]) */ +#define SPLINE_FRACBITS 10 +#define SPLINE_LUTLEN (1L<> 6; \ + smp_in = (cubic_spline_lut0[f] * sptr[(int)pos - 1] + \ + cubic_spline_lut1[f] * sptr[pos ] + \ + cubic_spline_lut3[f] * sptr[pos + 2] + \ + cubic_spline_lut2[f] * sptr[pos + 1]) >> (SPLINE_SHIFT - 8); \ +} while (0) + +#define SPLINE_INTERP_16BIT() do { \ + int f = frac >> 6; \ + smp_in = (cubic_spline_lut0[f] * sptr[(int)pos - 1] + \ + cubic_spline_lut1[f] * sptr[pos ] + \ + cubic_spline_lut3[f] * sptr[pos + 2] + \ + cubic_spline_lut2[f] * sptr[pos + 1]) >> SPLINE_SHIFT; \ +} while (0) + +#define LOOP_AC for (; count > ramp; count--) + +#define LOOP for (; count; count--) + +#define UPDATE_POS() do { \ + frac += step; \ + pos += frac >> SMIX_SHIFT; \ + frac &= SMIX_MASK; \ +} while (0) + +#define MIX_MONO() do { \ + *(buffer++) += smp_in * vl; \ +} while (0) + +#define MIX_MONO_AC() do { \ + *(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \ +} while (0) + +/* IT's WAV output driver uses a clamp that seems to roughly match this: + * compare the WAV output of OpenMPT env-flt-max.it and filter-reset.it */ +#define MIX_FILTER_CLAMP(a) CLAMP((a), -65536, 65535) + +#define MIX_MONO_FILTER() do { \ + sl = (a0 * smp_in + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \ + MIX_FILTER_CLAMP(sl); \ + fl2 = fl1; fl1 = sl; \ + *(buffer++) += sl * vl; \ +} while (0) + +#define MIX_MONO_FILTER_AC() do { \ + int vl = old_vl >> 8; \ + MIX_MONO_FILTER(); \ + old_vl += delta_l; \ +} while (0) + +#define MIX_STEREO() do { \ + *(buffer++) += smp_in * vr; \ + *(buffer++) += smp_in * vl; \ +} while (0) + +#define MIX_STEREO_AC() do { \ + *(buffer++) += smp_in * (old_vr >> 8); old_vr += delta_r; \ + *(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \ +} while (0) + +#define MIX_STEREO_FILTER() do { \ + sr = (a0 * smp_in + b0 * fr1 + b1 * fr2) >> FILTER_SHIFT; \ + MIX_FILTER_CLAMP(sr); \ + fr2 = fr1; fr1 = sr; \ + sl = (a0 * smp_in + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \ + MIX_FILTER_CLAMP(sl); \ + fl2 = fl1; fl1 = sl; \ + *(buffer++) += sr * vr; \ + *(buffer++) += sl * vl; \ +} while (0) + +#define MIX_STEREO_FILTER_AC() do { \ + int vr = old_vr >> 8; \ + int vl = old_vl >> 8; \ + MIX_STEREO_FILTER(); \ + old_vr += delta_r; \ + old_vl += delta_l; \ +} while (0) + +/* For "nearest" to be nearest neighbor (instead of floor), the position needs + * to be rounded. This only needs to be done once at the start of mixing, and + * is required for reverse samples to round the same as forward samples. + */ +#define NEAREST_ROUND() do { \ + frac += (1 << (SMIX_SHIFT - 1)); \ + pos += frac >> SMIX_SHIFT; \ + frac &= SMIX_MASK; \ +} while (0) + +#define VAR_NORM(x) \ + register int smp_in; \ + x *sptr = (x *)vi->sptr; \ + unsigned int pos = vi->pos; \ + int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos) + +#define VAR_LINEAR_MONO(x) \ + VAR_NORM(x); \ + int old_vl = vi->old_vl; \ + int smp_l1, smp_dt + +#define VAR_LINEAR_STEREO(x) \ + VAR_LINEAR_MONO(x); \ + int old_vr = vi->old_vr + +#define VAR_SPLINE_MONO(x) \ + int old_vl = vi->old_vl; \ + VAR_NORM(x) + +#define VAR_SPLINE_STEREO(x) \ + VAR_SPLINE_MONO(x); \ + int old_vr = vi->old_vr + +#ifndef LIBXMP_CORE_DISABLE_IT + +#define VAR_FILTER_MONO \ + int fl1 = vi->filter.l1, fl2 = vi->filter.l2; \ + int64 a0 = vi->filter.a0, b0 = vi->filter.b0, b1 = vi->filter.b1; \ + int sl + +#define VAR_FILTER_STEREO \ + VAR_FILTER_MONO; \ + int fr1 = vi->filter.r1, fr2 = vi->filter.r2; \ + int sr + +#define SAVE_FILTER_MONO() do { \ + vi->filter.l1 = fl1; \ + vi->filter.l2 = fl2; \ +} while (0) + +#define SAVE_FILTER_STEREO() do { \ + SAVE_FILTER_MONO(); \ + vi->filter.r1 = fr1; \ + vi->filter.r2 = fr2; \ +} while (0) + +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4457) /* shadowing */ +#endif + + +/* + * Nearest neighbor mixers + */ + +/* Handler for 8 bit samples, nearest neighbor mono output + */ +MIXER(mono_8bit_nearest) +{ + VAR_NORM(int8); + NEAREST_ROUND(); + + LOOP { NEAREST_NEIGHBOR(); MIX_MONO(); UPDATE_POS(); } +} + + +/* Handler for 16 bit samples, nearest neighbor mono output + */ +MIXER(mono_16bit_nearest) +{ + VAR_NORM(int16); + NEAREST_ROUND(); + + LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_MONO(); UPDATE_POS(); } +} + +/* Handler for 8 bit samples, nearest neighbor stereo output + */ +MIXER(stereo_8bit_nearest) +{ + VAR_NORM(int8); + NEAREST_ROUND(); + + LOOP { NEAREST_NEIGHBOR(); MIX_STEREO(); UPDATE_POS(); } +} + +/* Handler for 16 bit samples, nearest neighbor stereo output + */ +MIXER(stereo_16bit_nearest) +{ + VAR_NORM(int16); + NEAREST_ROUND(); + + LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_STEREO(); UPDATE_POS(); } +} + + +/* + * Linear mixers + */ + +/* Handler for 8 bit samples, linear interpolated mono output + */ +MIXER(mono_8bit_linear) +{ + VAR_LINEAR_MONO(int8); + + LOOP_AC { LINEAR_INTERP(); MIX_MONO_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP(); MIX_MONO(); UPDATE_POS(); } +} + +/* Handler for 16 bit samples, linear interpolated mono output + */ +MIXER(mono_16bit_linear) +{ + VAR_LINEAR_MONO(int16); + + LOOP_AC { LINEAR_INTERP_16BIT(); MIX_MONO_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP_16BIT(); MIX_MONO(); UPDATE_POS(); } +} + +/* Handler for 8 bit samples, linear interpolated stereo output + */ +MIXER(stereo_8bit_linear) +{ + VAR_LINEAR_STEREO(int8); + + LOOP_AC { LINEAR_INTERP(); MIX_STEREO_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP(); MIX_STEREO(); UPDATE_POS(); } +} + +/* Handler for 16 bit samples, linear interpolated stereo output + */ +MIXER(stereo_16bit_linear) +{ + VAR_LINEAR_STEREO(int16); + + LOOP_AC { LINEAR_INTERP_16BIT(); MIX_STEREO_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP_16BIT(); MIX_STEREO(); UPDATE_POS(); } +} + + +#ifndef LIBXMP_CORE_DISABLE_IT + +/* Handler for 8 bit samples, filtered linear interpolated mono output + */ +MIXER(mono_8bit_linear_filter) +{ + VAR_LINEAR_MONO(int8); + VAR_FILTER_MONO; + + LOOP_AC { LINEAR_INTERP(); MIX_MONO_FILTER_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP(); MIX_MONO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_MONO(); +} + +/* Handler for 16 bit samples, filtered linear interpolated mono output + */ +MIXER(mono_16bit_linear_filter) +{ + VAR_LINEAR_MONO(int16); + VAR_FILTER_MONO; + + LOOP_AC { LINEAR_INTERP_16BIT(); MIX_MONO_FILTER_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP_16BIT(); MIX_MONO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_MONO(); +} + +/* Handler for 8 bit samples, filtered linear interpolated stereo output + */ +MIXER(stereo_8bit_linear_filter) +{ + VAR_LINEAR_STEREO(int8); + VAR_FILTER_STEREO; + + LOOP_AC { LINEAR_INTERP(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP(); MIX_STEREO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_STEREO(); +} + +/* Handler for 16 bit samples, filtered linear interpolated stereo output + */ +MIXER(stereo_16bit_linear_filter) +{ + VAR_LINEAR_STEREO(int16); + VAR_FILTER_STEREO; + + LOOP_AC { LINEAR_INTERP_16BIT(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); } + LOOP { LINEAR_INTERP_16BIT(); MIX_STEREO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_STEREO(); +} + +#endif + +/* + * Spline mixers + */ + +/* Handler for 8 bit samples, spline interpolated mono output + */ +MIXER(mono_8bit_spline) +{ + VAR_SPLINE_MONO(int8); + + LOOP_AC { SPLINE_INTERP(); MIX_MONO_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP(); MIX_MONO(); UPDATE_POS(); } +} + +/* Handler for 16 bit samples, spline interpolated mono output + */ +MIXER(mono_16bit_spline) +{ + VAR_SPLINE_MONO(int16); + + LOOP_AC { SPLINE_INTERP_16BIT(); MIX_MONO_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP_16BIT(); MIX_MONO(); UPDATE_POS(); } +} + +/* Handler for 8 bit samples, spline interpolated stereo output + */ +MIXER(stereo_8bit_spline) +{ + VAR_SPLINE_STEREO(int8); + + LOOP_AC { SPLINE_INTERP(); MIX_STEREO_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP(); MIX_STEREO(); UPDATE_POS(); } +} + +/* Handler for 16 bit samples, spline interpolated stereo output + */ +MIXER(stereo_16bit_spline) +{ + VAR_SPLINE_STEREO(int16); + + LOOP_AC { SPLINE_INTERP_16BIT(); MIX_STEREO_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP_16BIT(); MIX_STEREO(); UPDATE_POS(); } +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +/* Handler for 8 bit samples, filtered spline interpolated mono output + */ +MIXER(mono_8bit_spline_filter) +{ + VAR_SPLINE_MONO(int8); + VAR_FILTER_MONO; + + LOOP_AC { SPLINE_INTERP(); MIX_MONO_FILTER_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP(); MIX_MONO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_MONO(); +} + +/* Handler for 16 bit samples, filtered spline interpolated mono output + */ +MIXER(mono_16bit_spline_filter) +{ + VAR_SPLINE_MONO(int16); + VAR_FILTER_MONO; + + LOOP_AC { SPLINE_INTERP_16BIT(); MIX_MONO_FILTER_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP_16BIT(); MIX_MONO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_MONO(); +} + +/* Handler for 8 bit samples, filtered spline interpolated stereo output + */ +MIXER(stereo_8bit_spline_filter) +{ + VAR_SPLINE_STEREO(int8); + VAR_FILTER_STEREO; + + LOOP_AC { SPLINE_INTERP(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP(); MIX_STEREO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_STEREO(); +} + +/* Handler for 16 bit samples, filtered spline interpolated stereo output + */ +MIXER(stereo_16bit_spline_filter) +{ + VAR_SPLINE_STEREO(int16); + VAR_FILTER_STEREO; + + LOOP_AC { SPLINE_INTERP_16BIT(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); } + LOOP { SPLINE_INTERP_16BIT(); MIX_STEREO_FILTER(); UPDATE_POS(); } + + SAVE_FILTER_STEREO(); +} + +#endif diff --git a/thirdparty/libxmp/src/mix_paula.c b/thirdparty/libxmp/src/mix_paula.c new file mode 100644 index 000000000..92ed61d64 --- /dev/null +++ b/thirdparty/libxmp/src/mix_paula.c @@ -0,0 +1,165 @@ +#include "common.h" + +#ifdef LIBXMP_PAULA_SIMULATOR +/* + * Based on Antti S. Lankila's reference code, modified for libxmp + * by Claudio Matsuoka. + */ +#include "virtual.h" +#include "mixer.h" +#include "paula.h" +#include "precomp_blep.h" + +void libxmp_paula_init(struct context_data *ctx, struct paula_state *paula) +{ + struct mixer_data *s = &ctx->s; + + paula->global_output_level = 0; + paula->active_bleps = 0; + paula->fdiv = (double)PAULA_HZ / s->freq; + paula->remainder = paula->fdiv; +} + +/* return output simulated as series of bleps */ +static int16 output_sample(struct paula_state *paula, int tabnum) +{ + int i; + int32 output; + + output = paula->global_output_level << BLEP_SCALE; + for (i = 0; i < paula->active_bleps; i++) { + int age = paula->blepstate[i].age; + int level = paula->blepstate[i].level; + output -= winsinc_integral[tabnum][age] * level; + } + output >>= BLEP_SCALE; + + if (output < -32768) + output = -32768; + else if (output > 32767) + output = 32767; + + return output; +} + +static void input_sample(struct paula_state *paula, int16 sample) +{ + if (sample != paula->global_output_level) { + /* Start a new blep: level is the difference, age (or phase) is 0 clocks. */ + if (paula->active_bleps > MAX_BLEPS - 1) { + D_(D_WARN "active blep list truncated!"); + paula->active_bleps = MAX_BLEPS - 1; + } + + /* Make room for new blep */ + memmove(&paula->blepstate[1], &paula->blepstate[0], + sizeof(struct blep_state) * paula->active_bleps); + + /* Update state to account for the new blep */ + paula->active_bleps++; + paula->blepstate[0].age = 0; + paula->blepstate[0].level = sample - paula->global_output_level; + paula->global_output_level = sample; + } +} + +static void do_clock(struct paula_state *paula, int cycles) +{ + int i; + + if (cycles <= 0) { + return; + } + + for (i = 0; i < paula->active_bleps; i++) { + paula->blepstate[i].age += cycles; + if (paula->blepstate[i].age >= BLEP_SIZE) { + paula->active_bleps = i; + break; + } + } +} + +#define LOOP for (; count; count--) + +#define UPDATE_POS(x) do { \ + frac += (x); \ + pos += frac >> SMIX_SHIFT; \ + frac &= SMIX_MASK; \ +} while (0) + +#define PAULA_SIMULATION(x) do { \ + int num_in = vi->paula->remainder / MINIMUM_INTERVAL; \ + int ministep = step / num_in; \ + int i; \ + \ + /* input is always sampled at a higher rate than output */ \ + for (i = 0; i < num_in - 1; i++) { \ + input_sample(vi->paula, sptr[pos]); \ + do_clock(vi->paula, MINIMUM_INTERVAL); \ + UPDATE_POS(ministep); \ + } \ + input_sample(vi->paula, sptr[pos]); \ + vi->paula->remainder -= num_in * MINIMUM_INTERVAL; \ + \ + do_clock(vi->paula, (int)vi->paula->remainder); \ + smp_in = output_sample(vi->paula, (x)); \ + do_clock(vi->paula, MINIMUM_INTERVAL - (int)vi->paula->remainder); \ + UPDATE_POS(step - (num_in - 1) * ministep); \ + \ + vi->paula->remainder += vi->paula->fdiv; \ +} while (0) + +#define MIX_MONO() do { \ + *(buffer++) += smp_in * vl; \ +} while (0) + +#define MIX_STEREO() do { \ + *(buffer++) += smp_in * vr; \ + *(buffer++) += smp_in * vl; \ +} while (0) + +#define VAR_NORM(x) \ + int smp_in; \ + x *sptr = (x *)vi->sptr; \ + unsigned int pos = vi->pos; \ + int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos) + +#define VAR_PAULA_MONO(x) \ + VAR_NORM(x); \ + vl <<= 8 + +#define VAR_PAULA(x) \ + VAR_NORM(x); \ + vl <<= 8; \ + vr <<= 8 + +MIXER(mono_a500) +{ + VAR_PAULA_MONO(int8); + + LOOP { PAULA_SIMULATION(0); MIX_MONO(); } +} + +MIXER(mono_a500_filter) +{ + VAR_PAULA_MONO(int8); + + LOOP { PAULA_SIMULATION(1); MIX_MONO(); } +} + +MIXER(stereo_a500) +{ + VAR_PAULA(int8); + + LOOP { PAULA_SIMULATION(0); MIX_STEREO(); } +} + +MIXER(stereo_a500_filter) +{ + VAR_PAULA(int8); + + LOOP { PAULA_SIMULATION(1); MIX_STEREO(); } +} + +#endif /* LIBXMP_PAULA_SIMULATOR */ diff --git a/thirdparty/libxmp/src/mixer.c b/thirdparty/libxmp/src/mixer.c new file mode 100644 index 000000000..168f5d515 --- /dev/null +++ b/thirdparty/libxmp/src/mixer.c @@ -0,0 +1,1028 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "common.h" +#include "virtual.h" +#include "mixer.h" +#include "period.h" +#include "player.h" /* for set_sample_end() */ + +#ifdef LIBXMP_PAULA_SIMULATOR +#include "paula.h" +#endif + + +#define FLAG_16_BITS 0x01 +#define FLAG_STEREO 0x02 +#define FLAG_FILTER 0x04 +#define FLAG_ACTIVE 0x10 +/* #define FLAG_SYNTH 0x20 */ +#define FIDX_FLAGMASK (FLAG_16_BITS | FLAG_STEREO | FLAG_FILTER) + +#define DOWNMIX_SHIFT 12 +#define LIM8_HI 127 +#define LIM8_LO -128 +#define LIM16_HI 32767 +#define LIM16_LO -32768 + +struct loop_data +{ +#define LOOP_PROLOGUE 1 +#define LOOP_EPILOGUE 2 + void *sptr; + int start; + int end; + int first_loop; + int _16bit; + int active; + uint32 prologue[LOOP_PROLOGUE]; + uint32 epilogue[LOOP_EPILOGUE]; +}; + +#define MIX_FN(x) void libxmp_mix_##x(struct mixer_voice *, int32 *, int, int, int, int, int, int, int) + +#define ANTICLICK_FPSHIFT 24 + +MIX_FN(mono_8bit_nearest); +MIX_FN(mono_8bit_linear); +MIX_FN(mono_16bit_nearest); +MIX_FN(mono_16bit_linear); +MIX_FN(stereo_8bit_nearest); +MIX_FN(stereo_8bit_linear); +MIX_FN(stereo_16bit_nearest); +MIX_FN(stereo_16bit_linear); +MIX_FN(mono_8bit_spline); +MIX_FN(mono_16bit_spline); +MIX_FN(stereo_8bit_spline); +MIX_FN(stereo_16bit_spline); + +#ifndef LIBXMP_CORE_DISABLE_IT +MIX_FN(mono_8bit_linear_filter); +MIX_FN(mono_16bit_linear_filter); +MIX_FN(stereo_8bit_linear_filter); +MIX_FN(stereo_16bit_linear_filter); +MIX_FN(mono_8bit_spline_filter); +MIX_FN(mono_16bit_spline_filter); +MIX_FN(stereo_8bit_spline_filter); +MIX_FN(stereo_16bit_spline_filter); +#endif + +#ifdef LIBXMP_PAULA_SIMULATOR +MIX_FN(mono_a500); +MIX_FN(mono_a500_filter); +MIX_FN(stereo_a500); +MIX_FN(stereo_a500_filter); +#endif + +/* Mixers array index: + * + * bit 0: 0=8 bit sample, 1=16 bit sample + * bit 1: 0=mono output, 1=stereo output + * bit 2: 0=unfiltered, 1=filtered + */ + +typedef void (*MIX_FP) (struct mixer_voice *, int32 *, int, int, int, int, int, int, int); + +static MIX_FP nearest_mixers[] = { + libxmp_mix_mono_8bit_nearest, + libxmp_mix_mono_16bit_nearest, + libxmp_mix_stereo_8bit_nearest, + libxmp_mix_stereo_16bit_nearest, + +#ifndef LIBXMP_CORE_DISABLE_IT + libxmp_mix_mono_8bit_nearest, + libxmp_mix_mono_16bit_nearest, + libxmp_mix_stereo_8bit_nearest, + libxmp_mix_stereo_16bit_nearest, +#endif +}; + +static MIX_FP linear_mixers[] = { + libxmp_mix_mono_8bit_linear, + libxmp_mix_mono_16bit_linear, + libxmp_mix_stereo_8bit_linear, + libxmp_mix_stereo_16bit_linear, + +#ifndef LIBXMP_CORE_DISABLE_IT + libxmp_mix_mono_8bit_linear_filter, + libxmp_mix_mono_16bit_linear_filter, + libxmp_mix_stereo_8bit_linear_filter, + libxmp_mix_stereo_16bit_linear_filter +#endif +}; + +static MIX_FP spline_mixers[] = { + libxmp_mix_mono_8bit_spline, + libxmp_mix_mono_16bit_spline, + libxmp_mix_stereo_8bit_spline, + libxmp_mix_stereo_16bit_spline, + +#ifndef LIBXMP_CORE_DISABLE_IT + libxmp_mix_mono_8bit_spline_filter, + libxmp_mix_mono_16bit_spline_filter, + libxmp_mix_stereo_8bit_spline_filter, + libxmp_mix_stereo_16bit_spline_filter +#endif +}; + +#ifdef LIBXMP_PAULA_SIMULATOR +static MIX_FP a500_mixers[] = { + libxmp_mix_mono_a500, + NULL, + libxmp_mix_stereo_a500, + NULL, + NULL, + NULL, + NULL, + NULL +}; + + +static MIX_FP a500led_mixers[] = { + libxmp_mix_mono_a500_filter, + NULL, + libxmp_mix_stereo_a500_filter, + NULL, + NULL, + NULL, + NULL, + NULL +}; +#endif + + +/* Downmix 32bit samples to 8bit, signed or unsigned, mono or stereo output */ +static void downmix_int_8bit(char *dest, int32 *src, int num, int amp, int offs) +{ + int smp; + int shift = DOWNMIX_SHIFT + 8 - amp; + + for (; num--; src++, dest++) { + smp = *src >> shift; + if (smp > LIM8_HI) { + *dest = LIM8_HI; + } else if (smp < LIM8_LO) { + *dest = LIM8_LO; + } else { + *dest = smp; + } + + if (offs) *dest += offs; + } +} + + +/* Downmix 32bit samples to 16bit, signed or unsigned, mono or stereo output */ +static void downmix_int_16bit(int16 *dest, int32 *src, int num, int amp, int offs) +{ + int smp; + int shift = DOWNMIX_SHIFT - amp; + + for (; num--; src++, dest++) { + smp = *src >> shift; + if (smp > LIM16_HI) { + *dest = LIM16_HI; + } else if (smp < LIM16_LO) { + *dest = LIM16_LO; + } else { + *dest = smp; + } + + if (offs) *dest += offs; + } +} + +static void anticlick(struct mixer_voice *vi) +{ + vi->flags |= ANTICLICK; + vi->old_vl = 0; + vi->old_vr = 0; +} + +/* Ok, it's messy, but it works :-) Hipolito */ +static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int count) +{ + struct player_data *p = &ctx->p; + struct mixer_data *s = &ctx->s; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + int smp_l, smp_r; + int discharge = s->ticksize >> ANTICLICK_SHIFT; + int stepmul, stepval; + uint32 stepmul_sq; + + smp_r = vi->sright; + smp_l = vi->sleft; + vi->sright = vi->sleft = 0; + + if (smp_l == 0 && smp_r == 0) { + return; + } + + if (buf == NULL) { + buf = s->buf32; + count = discharge; + } else if (count > discharge) { + count = discharge; + } + + if (count <= 0) { + return; + } + + stepval = (1 << ANTICLICK_FPSHIFT) / count; + stepmul = stepval * count; + + if (~s->format & XMP_FORMAT_MONO) { + while ((stepmul -= stepval) > 0) { + /* Truncate to 16-bits of precision so the product is 32-bits. */ + stepmul_sq = stepmul >> (ANTICLICK_FPSHIFT - 16); + stepmul_sq *= stepmul_sq; + *buf++ += (stepmul_sq * (int64)smp_r) >> 32; + *buf++ += (stepmul_sq * (int64)smp_l) >> 32; + } + } else { + while ((stepmul -= stepval) > 0) { + stepmul_sq = stepmul >> (ANTICLICK_FPSHIFT - 16); + stepmul_sq *= stepmul_sq; + *buf++ += (stepmul_sq * (int64)smp_l) >> 32; + } + } +} + +static void set_sample_end(struct context_data *ctx, int voc, int end) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + struct channel_data *xc; + + if ((uint32)voc >= p->virt.maxvoc) + return; + + xc = &p->xc_data[vi->chn]; + + if (end) { + SET_NOTE(NOTE_SAMPLE_END); + vi->fidx &= ~FLAG_ACTIVE; + if (HAS_QUIRK(QUIRK_RSTCHN)) { + libxmp_virt_resetvoice(ctx, voc, 0); + } + } else { + RESET_NOTE(NOTE_SAMPLE_END); + } +} + +/* Back up sample data before and after loop and replace it for interpolation. + * TODO: use an overlap buffer like OpenMPT? This is easier, but a little dirty. */ +static void init_sample_wraparound(struct mixer_data *s, struct loop_data *ld, + struct mixer_voice *vi, struct xmp_sample *xxs) +{ + int bidir; + int i; + + if (!vi->sptr || s->interp == XMP_INTERP_NEAREST || (~xxs->flg & XMP_SAMPLE_LOOP)) { + ld->active = 0; + return; + } + + ld->sptr = vi->sptr; + ld->start = vi->start; + ld->end = vi->end; + ld->first_loop = !(vi->flags & SAMPLE_LOOP); + ld->_16bit = (xxs->flg & XMP_SAMPLE_16BIT); + ld->active = 1; + + bidir = vi->flags & VOICE_BIDIR; + + if (ld->_16bit) { + uint16 *start = (uint16 *)vi->sptr + vi->start; + uint16 *end = (uint16 *)vi->sptr + vi->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) { + int j = i - LOOP_PROLOGUE; + ld->prologue[i] = start[j]; + start[j] = bidir ? start[-1 - j] : end[j]; + } + } + for (i = 0; i < LOOP_EPILOGUE; i++) { + ld->epilogue[i] = end[i]; + end[i] = bidir ? end[-1 - i] : start[i]; + } + } else { + uint8 *start = (uint8 *)vi->sptr + vi->start; + uint8 *end = (uint8 *)vi->sptr + vi->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) { + int j = i - LOOP_PROLOGUE; + ld->prologue[i] = start[j]; + start[j] = bidir ? start[-1 - j] : end[j]; + } + } + for (i = 0; i < LOOP_EPILOGUE; i++) { + ld->epilogue[i] = end[i]; + end[i] = bidir ? end[-1 - i] : start[i]; + } + } +} + +/* Restore old sample data from before and after loop. */ +static void reset_sample_wraparound(struct loop_data *ld) +{ + int i; + + if (!ld->active) + return; + + if (ld->_16bit) { + uint16 *start = (uint16 *)ld->sptr + ld->start; + uint16 *end = (uint16 *)ld->sptr + ld->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) + start[i - LOOP_PROLOGUE] = ld->prologue[i]; + } + for (i = 0; i < LOOP_EPILOGUE; i++) + end[i] = ld->epilogue[i]; + } else { + uint8 *start = (uint8 *)ld->sptr + ld->start; + uint8 *end = (uint8 *)ld->sptr + ld->end; + + if (!ld->first_loop) { + for (i = 0; i < LOOP_PROLOGUE; i++) + start[i - LOOP_PROLOGUE] = ld->prologue[i]; + } + for (i = 0; i < LOOP_EPILOGUE; i++) + end[i] = ld->epilogue[i]; + } +} + +static int has_active_sustain_loop(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs) +{ +#ifndef LIBXMP_CORE_DISABLE_IT + struct module_data *m = &ctx->m; + return vi->smp < m->mod.smp && (xxs->flg & XMP_SAMPLE_SLOOP) && (~vi->flags & VOICE_RELEASE); +#else + return 0; +#endif +} + +static int has_active_loop(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs) +{ + return (xxs->flg & XMP_SAMPLE_LOOP) || has_active_sustain_loop(ctx, vi, xxs); +} + +/* Update the voice endpoints based on current sample loop state. */ +static void adjust_voice_end(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs, struct extra_sample_data *xtra) +{ + vi->flags &= ~VOICE_BIDIR; + + if (xtra && has_active_sustain_loop(ctx, vi, xxs)) { + vi->start = xtra->sus; + vi->end = xtra->sue; + if (xxs->flg & XMP_SAMPLE_SLOOP_BIDIR) vi->flags |= VOICE_BIDIR; + + } else if (xxs->flg & XMP_SAMPLE_LOOP) { + vi->start = xxs->lps; + if ((xxs->flg & XMP_SAMPLE_LOOP_FULL) && (~vi->flags & SAMPLE_LOOP)) { + vi->end = xxs->len; + } else { + vi->end = xxs->lpe; + if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) vi->flags |= VOICE_BIDIR; + } + } else { + vi->start = 0; + vi->end = xxs->len; + } +} + +static int loop_reposition(struct context_data *ctx, struct mixer_voice *vi, + struct xmp_sample *xxs, struct extra_sample_data *xtra) +{ + int loop_changed = !(vi->flags & SAMPLE_LOOP); + + vi->flags |= SAMPLE_LOOP; + + if(loop_changed) + adjust_voice_end(ctx, vi, xxs, xtra); + + if (~vi->flags & VOICE_BIDIR) { + /* Reposition for next loop */ + if (~vi->flags & VOICE_REVERSE) + vi->pos -= vi->end - vi->start; + else + vi->pos += vi->end - vi->start; + } else { + /* Bidirectional loop: switch directions */ + vi->flags ^= VOICE_REVERSE; + + /* Wrap voice position around endpoint */ + if (vi->flags & VOICE_REVERSE) { + /* OpenMPT Bidi-Loops.it: "In Impulse Tracker's software + * mixer, ping-pong loops are shortened by one sample." + */ + vi->pos = vi->end * 2 - ctx->s.bidir_adjust - vi->pos; + } else { + vi->pos = vi->start * 2 - vi->pos; + } + } + return loop_changed; +} + + +/* Prepare the mixer for the next tick */ +void libxmp_mixer_prepare(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_data *s = &ctx->s; + int bytelen; + + s->ticksize = s->freq * m->time_factor * m->rrate / p->bpm / 1000; + + if (s->ticksize < (1 << ANTICLICK_SHIFT)) + s->ticksize = 1 << ANTICLICK_SHIFT; + + bytelen = s->ticksize * sizeof(int32); + if (~s->format & XMP_FORMAT_MONO) { + bytelen *= 2; + } + memset(s->buf32, 0, bytelen); +} +/* Fill the output buffer calling one of the handlers. The buffer contains + * sound for one tick (a PAL frame or 1/50s for standard vblank-timed mods) + */ +void libxmp_mixer_softmixer(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct mixer_data *s = &ctx->s; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct extra_sample_data *xtra; + struct xmp_sample *xxs; + struct mixer_voice *vi; + struct loop_data loop_data; + double step, step_dir; + int samples, size; + int vol, vol_l, vol_r, voc, usmp; + int prev_l, prev_r = 0; + int32 *buf_pos; + MIX_FP mix_fn; + MIX_FP *mixerset; + + switch (s->interp) { + case XMP_INTERP_NEAREST: + mixerset = nearest_mixers; + break; + case XMP_INTERP_LINEAR: + mixerset = linear_mixers; + break; + case XMP_INTERP_SPLINE: + mixerset = spline_mixers; + break; + default: + mixerset = linear_mixers; + } + +#ifdef LIBXMP_PAULA_SIMULATOR + if (p->flags & XMP_FLAGS_A500) { + if (IS_AMIGA_MOD()) { + if (p->filter) { + mixerset = a500led_mixers; + } else { + mixerset = a500_mixers; + } + } + } +#endif + +#ifndef LIBXMP_CORE_DISABLE_IT + /* OpenMPT Bidi-Loops.it: "In Impulse Tracker's software + * mixer, ping-pong loops are shortened by one sample." + */ + s->bidir_adjust = IS_PLAYER_MODE_IT() ? 1 : 0; +#endif + + libxmp_mixer_prepare(ctx); + + for (voc = 0; voc < p->virt.maxvoc; voc++) { + int c5spd, rampsize, delta_l, delta_r; + + vi = &p->virt.voice_array[voc]; + + if (vi->flags & ANTICLICK) { + if (s->interp > XMP_INTERP_NEAREST) { + do_anticlick(ctx, voc, NULL, 0); + } + vi->flags &= ~ANTICLICK; + } + + if (vi->chn < 0) { + continue; + } + + if (vi->period < 1) { + libxmp_virt_resetvoice(ctx, voc, 1); + continue; + } + + /* Negative positions can be left over from some + * loop edge cases. These can be safely clamped. */ + if (vi->pos < 0.0) + vi->pos = 0.0; + + vi->pos0 = vi->pos; + + buf_pos = s->buf32; + vol = vi->vol; + + /* Mix volume (S3M and IT) */ + if (m->mvolbase > 0 && m->mvol != m->mvolbase) { + vol = vol * m->mvol / m->mvolbase; + } + + if (vi->pan == PAN_SURROUND) { + vol_r = vol * 0x80; + vol_l = -vol * 0x80; + } else { + vol_r = vol * (0x80 - vi->pan); + vol_l = vol * (0x80 + vi->pan); + } + + if (vi->smp < mod->smp) { + xxs = &mod->xxs[vi->smp]; + xtra = &m->xtra[vi->smp]; + c5spd = m->xtra[vi->smp].c5spd; + } else { + xxs = &ctx->smix.xxs[vi->smp - mod->smp]; + xtra = NULL; + c5spd = m->c4rate; + } + + step = C4_PERIOD * c5spd / s->freq / vi->period; + + /* Don't allow <=0, otherwise m5v-nwlf.it crashes + * Extremely high values that can cause undefined float/int + * conversion are also possible for c5spd modules. */ + if (step < 0.001 || step > (double)SHRT_MAX) { + continue; + } + + adjust_voice_end(ctx, vi, xxs, xtra); + init_sample_wraparound(s, &loop_data, vi, xxs); + + rampsize = s->ticksize >> ANTICLICK_SHIFT; + delta_l = (vol_l - vi->old_vl) / rampsize; + delta_r = (vol_r - vi->old_vr) / rampsize; + + for (size = usmp = s->ticksize; size > 0; ) { + int split_noloop = 0; + + if (p->xc_data[vi->chn].split) { + split_noloop = 1; + } + + /* How many samples we can write before the loop break + * or sample end... */ + if (~vi->flags & VOICE_REVERSE) { + if (vi->pos >= vi->end) { + samples = 0; + if (--usmp <= 0) + break; + } else { + double c = ceil(((double)vi->end - vi->pos) / step); + /* ...inside the tick boundaries */ + if (c > size) { + c = size; + } + samples = c; + } + step_dir = step; + } else { + /* Reverse */ + if (vi->pos <= vi->start) { + samples = 0; + if (--usmp <= 0) + break; + } else { + double c = ceil((vi->pos - (double)vi->start) / step); + if (c > size) { + c = size; + } + samples = c; + } + step_dir = -step; + } + + if (vi->vol) { + int mix_size = samples; + int mixer_id = vi->fidx & FIDX_FLAGMASK; + + if (~s->format & XMP_FORMAT_MONO) { + mix_size *= 2; + } + + /* For Hipolito's anticlick routine */ + if (samples > 0) { + if (~s->format & XMP_FORMAT_MONO) { + prev_r = buf_pos[mix_size - 2]; + } + prev_l = buf_pos[mix_size - 1]; + } else { + prev_r = prev_l = 0; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + /* See OpenMPT env-flt-max.it */ + if (vi->filter.cutoff >= 0xfe && + vi->filter.resonance == 0) { + mixer_id &= ~FLAG_FILTER; + } +#endif + + mix_fn = mixerset[mixer_id]; + + /* Call the output handler */ + if (samples > 0 && vi->sptr != NULL) { + int rsize = 0; + + if (rampsize > samples) { + rampsize -= samples; + } else { + rsize = samples - rampsize; + rampsize = 0; + } + + if (delta_l == 0 && delta_r == 0) { + /* no need to ramp */ + rsize = samples; + } + + if (mix_fn != NULL) { + mix_fn(vi, buf_pos, samples, + vol_l >> 8, vol_r >> 8, step_dir * (1 << SMIX_SHIFT), rsize, delta_l, delta_r); + } + + buf_pos += mix_size; + vi->old_vl += samples * delta_l; + vi->old_vr += samples * delta_r; + + + /* For Hipolito's anticlick routine */ + if (~s->format & XMP_FORMAT_MONO) { + vi->sright = buf_pos[-2] - prev_r; + } + vi->sleft = buf_pos[-1] - prev_l; + } + } + + vi->pos += step_dir * samples; + + /* No more samples in this tick */ + size -= samples; + if (size <= 0) { + if (has_active_loop(ctx, vi, xxs)) { + /* This isn't particularly important for + * forward loops, but reverse loops need + * to be corrected here to avoid their + * negative positions getting clamped + * in later ticks. */ + if (((~vi->flags & VOICE_REVERSE) && vi->pos >= vi->end) || + ((vi->flags & VOICE_REVERSE) && vi->pos <= vi->start)) { + if (loop_reposition(ctx, vi, xxs, xtra)) { + reset_sample_wraparound(&loop_data); + init_sample_wraparound(s, &loop_data, vi, xxs); + } + } + } + continue; + } + + /* First sample loop run */ + if (!has_active_loop(ctx, vi, xxs) || split_noloop) { + do_anticlick(ctx, voc, buf_pos, size); + set_sample_end(ctx, voc, 1); + size = 0; + continue; + } + + if (loop_reposition(ctx, vi, xxs, xtra)) { + reset_sample_wraparound(&loop_data); + init_sample_wraparound(s, &loop_data, vi, xxs); + } + } + + reset_sample_wraparound(&loop_data); + vi->old_vl = vol_l; + vi->old_vr = vol_r; + } + + /* Render final frame */ + + size = s->ticksize; + if (~s->format & XMP_FORMAT_MONO) { + size *= 2; + } + + if (size > XMP_MAX_FRAMESIZE) { + size = XMP_MAX_FRAMESIZE; + } + + if (s->format & XMP_FORMAT_8BIT) { + downmix_int_8bit(s->buffer, s->buf32, size, s->amplify, + s->format & XMP_FORMAT_UNSIGNED ? 0x80 : 0); + } else { + downmix_int_16bit((int16 *)s->buffer, s->buf32, size, s->amplify, + s->format & XMP_FORMAT_UNSIGNED ? 0x8000 : 0); + } + + s->dtright = s->dtleft = 0; +} + +void libxmp_mixer_voicepos(struct context_data *ctx, int voc, double pos, int ac) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + struct xmp_sample *xxs; + struct extra_sample_data *xtra; + + if (vi->smp < m->mod.smp) { + xxs = &m->mod.xxs[vi->smp]; + xtra = &m->xtra[vi->smp]; + } else { + xxs = &ctx->smix.xxs[vi->smp - m->mod.smp]; + xtra = NULL; + } + + if (xxs->flg & XMP_SAMPLE_SYNTH) { + return; + } + + vi->pos = pos; + + adjust_voice_end(ctx, vi, xxs, xtra); + + if (vi->pos >= vi->end) { + vi->pos = vi->end; + /* Restart forward sample loops. */ + if ((~vi->flags & VOICE_REVERSE) && has_active_loop(ctx, vi, xxs)) + loop_reposition(ctx, vi, xxs, xtra); + } else if ((vi->flags & VOICE_REVERSE) && vi->pos <= 0.1) { + /* Hack: 0 maps to the end for reversed samples. */ + vi->pos = vi->end; + } + + if (ac) { + anticlick(vi); + } +} + +double libxmp_mixer_getvoicepos(struct context_data *ctx, int voc) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + struct xmp_sample *xxs; + + xxs = libxmp_get_sample(ctx, vi->smp); + + if (xxs->flg & XMP_SAMPLE_SYNTH) { + return 0; + } + + return vi->pos; +} + +void libxmp_mixer_setpatch(struct context_data *ctx, int voc, int smp, int ac) +{ + struct player_data *p = &ctx->p; +#ifndef LIBXMP_CORE_DISABLE_IT + struct module_data *m = &ctx->m; +#endif + struct mixer_data *s = &ctx->s; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + struct xmp_sample *xxs; + + xxs = libxmp_get_sample(ctx, smp); + + vi->smp = smp; + vi->vol = 0; + vi->pan = 0; + vi->flags &= ~(SAMPLE_LOOP | VOICE_REVERSE | VOICE_BIDIR); + + vi->fidx = 0; + + if (~s->format & XMP_FORMAT_MONO) { + vi->fidx |= FLAG_STEREO; + } + + set_sample_end(ctx, voc, 0); + + /*mixer_setvol(ctx, voc, 0);*/ + + vi->sptr = xxs->data; + vi->fidx |= FLAG_ACTIVE; + +#ifndef LIBXMP_CORE_DISABLE_IT + if (HAS_QUIRK(QUIRK_FILTER) && s->dsp & XMP_DSP_LOWPASS) { + vi->fidx |= FLAG_FILTER; + } +#endif + + if (xxs->flg & XMP_SAMPLE_16BIT) { + vi->fidx |= FLAG_16_BITS; + } + + libxmp_mixer_voicepos(ctx, voc, 0, ac); +} + +void libxmp_mixer_setnote(struct context_data *ctx, int voc, int note) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + /* FIXME: Workaround for crash on notes that are too high + * see 6nations.it (+114 transposition on instrument 16) + */ + if (note > 149) { + note = 149; + } + + vi->note = note; + vi->period = libxmp_note_to_period_mix(note, 0); + + anticlick(vi); +} + +void libxmp_mixer_setperiod(struct context_data *ctx, int voc, double period) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + vi->period = period; +} + +void libxmp_mixer_setvol(struct context_data *ctx, int voc, int vol) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + if (vol == 0) { + anticlick(vi); + } + + vi->vol = vol; +} + +void libxmp_mixer_release(struct context_data *ctx, int voc, int rel) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + if (rel) { +#ifndef LIBXMP_CORE_DISABLE_IT + /* Cancel voice reverse when releasing an active sustain loop, + * unless the main loop is bidirectional. This is done both for + * bidirectional sustain loops and for forward sustain loops + * that have been reversed with MPT S9F Play Backward. */ + if (~vi->flags & VOICE_RELEASE) { + struct xmp_sample *xxs = libxmp_get_sample(ctx, vi->smp); + + if (has_active_sustain_loop(ctx, vi, xxs) && + (~xxs->flg & XMP_SAMPLE_LOOP_BIDIR)) + vi->flags &= ~VOICE_REVERSE; + } +#endif + vi->flags |= VOICE_RELEASE; + } else { + vi->flags &= ~VOICE_RELEASE; + } +} + +void libxmp_mixer_reverse(struct context_data *ctx, int voc, int rev) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + /* Don't reverse samples that have already ended */ + if (~vi->fidx & FLAG_ACTIVE) { + return; + } + + if (rev) { + vi->flags |= VOICE_REVERSE; + } else { + vi->flags &= ~VOICE_REVERSE; + } +} + +void libxmp_mixer_seteffect(struct context_data *ctx, int voc, int type, int val) +{ +#ifndef LIBXMP_CORE_DISABLE_IT + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + switch (type) { + case DSP_EFFECT_CUTOFF: + vi->filter.cutoff = val; + break; + case DSP_EFFECT_RESONANCE: + vi->filter.resonance = val; + break; + case DSP_EFFECT_FILTER_A0: + vi->filter.a0 = val; + break; + case DSP_EFFECT_FILTER_B0: + vi->filter.b0 = val; + break; + case DSP_EFFECT_FILTER_B1: + vi->filter.b1 = val; + break; + } +#endif +} + +void libxmp_mixer_setpan(struct context_data *ctx, int voc, int pan) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; + + vi->pan = pan; +} + +int libxmp_mixer_numvoices(struct context_data *ctx, int num) +{ + struct mixer_data *s = &ctx->s; + + if (num > s->numvoc || num < 0) { + return s->numvoc; + } else { + return num; + } +} + +int libxmp_mixer_on(struct context_data *ctx, int rate, int format, int c4rate) +{ + struct mixer_data *s = &ctx->s; + + s->buffer = (char *) calloc(2, XMP_MAX_FRAMESIZE); + if (s->buffer == NULL) + goto err; + + s->buf32 = (int32 *) calloc(sizeof(int32), XMP_MAX_FRAMESIZE); + if (s->buf32 == NULL) + goto err1; + + s->freq = rate; + s->format = format; + s->amplify = DEFAULT_AMPLIFY; + s->mix = DEFAULT_MIX; + /* s->pbase = C4_PERIOD * c4rate / s->freq; */ + s->interp = XMP_INTERP_LINEAR; /* default interpolation type */ + s->dsp = XMP_DSP_LOWPASS; /* enable filters by default */ + /* s->numvoc = SMIX_NUMVOC; */ + s->dtright = s->dtleft = 0; + s->bidir_adjust = 0; + + return 0; + + err1: + free(s->buffer); + s->buffer = NULL; + err: + return -1; +} + +void libxmp_mixer_off(struct context_data *ctx) +{ + struct mixer_data *s = &ctx->s; + + free(s->buffer); + free(s->buf32); + s->buf32 = NULL; + s->buffer = NULL; +} diff --git a/thirdparty/libxmp/src/mixer.h b/thirdparty/libxmp/src/mixer.h new file mode 100644 index 000000000..c80b1e897 --- /dev/null +++ b/thirdparty/libxmp/src/mixer.h @@ -0,0 +1,83 @@ +#ifndef LIBXMP_MIXER_H +#define LIBXMP_MIXER_H + +#define C4_PERIOD 428.0 + +#define SMIX_NUMVOC 128 /* default number of softmixer voices */ +#define SMIX_SHIFT 16 +#define SMIX_MASK 0xffff + +#define FILTER_SHIFT 16 +#define ANTICLICK_SHIFT 3 + +#ifdef LIBXMP_PAULA_SIMULATOR +#include "paula.h" +#endif + +#define MIXER(f) void libxmp_mix_##f(struct mixer_voice *vi, int *buffer, \ + int count, int vl, int vr, int step, int ramp, int delta_l, int delta_r) + +struct mixer_voice { + int chn; /* channel number */ + int root; /* */ + int note; /* */ +#define PAN_SURROUND 0x8000 + int pan; /* */ + int vol; /* */ + double period; /* current period */ + double pos; /* position in sample */ + int pos0; /* position in sample before mixing */ + int fidx; /* mixer function index */ + int ins; /* instrument number */ + int smp; /* sample number */ + int start; /* loop start */ + int end; /* loop end */ + int act; /* nna info & status of voice */ + int key; /* key for DCA note check */ + int old_vl; /* previous volume, left channel */ + int old_vr; /* previous volume, right channel */ + int sleft; /* last left sample output, in 32bit */ + int sright; /* last right sample output, in 32bit */ +#define VOICE_RELEASE (1 << 0) +#define ANTICLICK (1 << 1) +#define SAMPLE_LOOP (1 << 2) +#define VOICE_REVERSE (1 << 3) +#define VOICE_BIDIR (1 << 4) + int flags; /* flags */ + void *sptr; /* sample pointer */ +#ifdef LIBXMP_PAULA_SIMULATOR + struct paula_state *paula; /* paula simulation state */ +#endif + +#ifndef LIBXMP_CORE_DISABLE_IT + struct { + int r1; /* filter variables */ + int r2; + int l1; + int l2; + int a0; + int b0; + int b1; + int cutoff; + int resonance; + } filter; +#endif +}; + +int libxmp_mixer_on (struct context_data *, int, int, int); +void libxmp_mixer_off (struct context_data *); +void libxmp_mixer_setvol (struct context_data *, int, int); +void libxmp_mixer_seteffect (struct context_data *, int, int, int); +void libxmp_mixer_setpan (struct context_data *, int, int); +int libxmp_mixer_numvoices (struct context_data *, int); +void libxmp_mixer_softmixer (struct context_data *); +void libxmp_mixer_reset (struct context_data *); +void libxmp_mixer_setpatch (struct context_data *, int, int, int); +void libxmp_mixer_voicepos (struct context_data *, int, double, int); +double libxmp_mixer_getvoicepos(struct context_data *, int); +void libxmp_mixer_setnote (struct context_data *, int, int); +void libxmp_mixer_setperiod (struct context_data *, int, double); +void libxmp_mixer_release (struct context_data *, int, int); +void libxmp_mixer_reverse (struct context_data *, int, int); + +#endif /* LIBXMP_MIXER_H */ diff --git a/thirdparty/libxmp/src/mkstemp.c b/thirdparty/libxmp/src/mkstemp.c new file mode 100644 index 000000000..1ef25af99 --- /dev/null +++ b/thirdparty/libxmp/src/mkstemp.c @@ -0,0 +1,105 @@ +#ifdef __SUNPRO_C +#pragma error_messages (off,E_EMPTY_TRANSLATION_UNIT) +#endif + +#include "common.h" + +#if !(defined(LIBXMP_NO_PROWIZARD) && defined(LIBXMP_NO_DEPACKERS)) + +#ifndef HAVE_MKSTEMP + +/* + * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the Institute 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 THE INSTITUTE AND CONTRIBUTORS ``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 THE INSTITUTE OR CONTRIBUTORS 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. + */ + +#include +#include + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif +#if defined(_MSC_VER) || defined(__WATCOMC__) +#include +#else +#include +#endif +#ifdef _MSC_VER +#include +#define open _open +#endif +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +int mkstemp(char *pattern) +{ + int start, i; +#ifdef _WIN32 + int val = GetCurrentProcessId(); +#else + pid_t val = getpid(); +#endif + + start = strlen(pattern) - 1; + + while (pattern[start] == 'X') { + pattern[start] = '0' + val % 10; + val /= 10; + start--; + } + + do { + int fd; + fd = open(pattern, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600); + if (fd >= 0 || errno != EEXIST) + return fd; + i = start + 1; + do { + if (pattern[i] == 0) + return -1; + pattern[i]++; + if (pattern[i] == '9' + 1) + pattern[i] = 'a'; + if (pattern[i] <= 'z') + break; + pattern[i] = 'a'; + i++; + } while (1); + } while (1); +} + +#endif + +#endif diff --git a/thirdparty/libxmp/src/paula.h b/thirdparty/libxmp/src/paula.h new file mode 100644 index 000000000..8f3f381be --- /dev/null +++ b/thirdparty/libxmp/src/paula.h @@ -0,0 +1,37 @@ +#ifndef LIBXMP_PAULA_H +#define LIBXMP_PAULA_H + +/* 131072 to 0, 2048 entries */ +#define PAULA_HZ 3546895 +#define MINIMUM_INTERVAL 16 +#define BLEP_SCALE 17 +#define BLEP_SIZE 2048 +#define MAX_BLEPS (BLEP_SIZE / MINIMUM_INTERVAL) + +/* the structure that holds data of bleps */ +struct blep_state { + int16 level; + int16 age; +}; + +struct paula_state { + /* the instantenous value of Paula output */ + int16 global_output_level; + + /* count of simultaneous bleps to keep track of */ + unsigned int active_bleps; + + /* place to keep our bleps in. MAX_BLEPS should be + * defined as a BLEP_SIZE / MINIMUM_EVENT_INTERVAL. + * For Paula, minimum event interval could be even 1, but it makes + * sense to limit it to some higher value such as 16. */ + struct blep_state blepstate[MAX_BLEPS]; + + double remainder; + double fdiv; +}; + + +void libxmp_paula_init (struct context_data *, struct paula_state *); + +#endif /* !LIBXMP_PAULA_H */ diff --git a/thirdparty/libxmp/src/period.c b/thirdparty/libxmp/src/period.c new file mode 100644 index 000000000..2cbf95577 --- /dev/null +++ b/thirdparty/libxmp/src/period.c @@ -0,0 +1,285 @@ +/* Extended Module Player + * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "common.h" +#include "period.h" + +#include + +#ifdef LIBXMP_PAULA_SIMULATOR +/* + * Period table from the Protracker V2.1A play routine + */ +static uint16 pt_period_table[16][36] = { + /* Tuning 0, Normal */ + { + 856,808,762,720,678,640,604,570,538,508,480,453, + 428,404,381,360,339,320,302,285,269,254,240,226, + 214,202,190,180,170,160,151,143,135,127,120,113 + }, + /* Tuning 1 */ + { + 850,802,757,715,674,637,601,567,535,505,477,450, + 425,401,379,357,337,318,300,284,268,253,239,225, + 213,201,189,179,169,159,150,142,134,126,119,113 + }, + /* Tuning 2 */ + { + 844,796,752,709,670,632,597,563,532,502,474,447, + 422,398,376,355,335,316,298,282,266,251,237,224, + 211,199,188,177,167,158,149,141,133,125,118,112 + }, + /* Tuning 3 */ + { + 838,791,746,704,665,628,592,559,528,498,470,444, + 419,395,373,352,332,314,296,280,264,249,235,222, + 209,198,187,176,166,157,148,140,132,125,118,111 + }, + /* Tuning 4 */ + { + 832,785,741,699,660,623,588,555,524,495,467,441, + 416,392,370,350,330,312,294,278,262,247,233,220, + 208,196,185,175,165,156,147,139,131,124,117,110 + }, + /* Tuning 5 */ + { + 826,779,736,694,655,619,584,551,520,491,463,437, + 413,390,368,347,328,309,292,276,260,245,232,219, + 206,195,184,174,164,155,146,138,130,123,116,109 + }, + /* Tuning 6 */ + { + 820,774,730,689,651,614,580,547,516,487,460,434, + 410,387,365,345,325,307,290,274,258,244,230,217, + 205,193,183,172,163,154,145,137,129,122,115,109 + }, + /* Tuning 7 */ + { + 814,768,725,684,646,610,575,543,513,484,457,431, + 407,384,363,342,323,305,288,272,256,242,228,216, + 204,192,181,171,161,152,144,136,128,121,114,108 + }, + /* Tuning -8 */ + { + 907,856,808,762,720,678,640,604,570,538,508,480, + 453,428,404,381,360,339,320,302,285,269,254,240, + 226,214,202,190,180,170,160,151,143,135,127,120 + }, + /* Tuning -7 */ + { + 900,850,802,757,715,675,636,601,567,535,505,477, + 450,425,401,379,357,337,318,300,284,268,253,238, + 225,212,200,189,179,169,159,150,142,134,126,119 + }, + /* Tuning -6 */ + { + 894,844,796,752,709,670,632,597,563,532,502,474, + 447,422,398,376,355,335,316,298,282,266,251,237, + 223,211,199,188,177,167,158,149,141,133,125,118 + }, + /* Tuning -5 */ + { + 887,838,791,746,704,665,628,592,559,528,498,470, + 444,419,395,373,352,332,314,296,280,264,249,235, + 222,209,198,187,176,166,157,148,140,132,125,118 + }, + /* Tuning -4 */ + { + 881,832,785,741,699,660,623,588,555,524,494,467, + 441,416,392,370,350,330,312,294,278,262,247,233, + 220,208,196,185,175,165,156,147,139,131,123,117 + }, + /* Tuning -3 */ + { + 875,826,779,736,694,655,619,584,551,520,491,463, + 437,413,390,368,347,328,309,292,276,260,245,232, + 219,206,195,184,174,164,155,146,138,130,123,116 + }, + /* Tuning -2 */ + { + 868,820,774,730,689,651,614,580,547,516,487,460, + 434,410,387,365,345,325,307,290,274,258,244,230, + 217,205,193,183,172,163,154,145,137,129,122,115 + }, + /* Tuning -1 */ + { + 862,814,768,725,684,646,610,575,543,513,484,457, + 431,407,384,363,342,323,305,288,272,256,242,228, + 216,203,192,181,171,161,152,144,136,128,121,114 + } +}; +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + +static inline double libxmp_round(double val) +{ + return (val >= 0.0)? floor(val + 0.5) : ceil(val - 0.5); +} + +#ifdef LIBXMP_PAULA_SIMULATOR +/* Get period from note using Protracker tuning */ +static inline int libxmp_note_to_period_pt(int n, int f) +{ + if (n < MIN_NOTE_MOD || n > MAX_NOTE_MOD) { + return -1; + } + + n -= 48; + f >>= 4; + if (f < -8 || f > 7) { + return 0; + } + + if (f < 0) { + f += 16; + } + + return (int)pt_period_table[f][n]; +} +#endif + +/* Get period from note */ +double libxmp_note_to_period(struct context_data *ctx, int n, int f, double adj) +{ + double d, per; + struct module_data *m = &ctx->m; +#ifdef LIBXMP_PAULA_SIMULATOR + struct player_data *p = &ctx->p; + + /* If mod replayer, modrng and Amiga mixing are active */ + if (p->flags & XMP_FLAGS_A500) { + if (IS_AMIGA_MOD()) { + return libxmp_note_to_period_pt(n, f); + } + } +#endif + + d = (double)n + (double)f / 128; + + switch (m->period_type) { + case PERIOD_LINEAR: + per = (240.0 - d) * 16; /* Linear */ + break; + case PERIOD_CSPD: + per = 8363.0 * pow(2, n / 12.0) / 32 + f; /* Hz */ + break; + default: + per = PERIOD_BASE / pow(2, d / 12); /* Amiga */ + } + +#ifndef LIBXMP_CORE_PLAYER + if (adj > 0.1) { + per *= adj; + } +#endif + + return per; +} + +/* For the software mixer */ +double libxmp_note_to_period_mix(int n, int b) +{ + double d = (double)n + (double)b / 12800; + return PERIOD_BASE / pow(2, d / 12); +} + +/* Get note from period */ +/* This function is used only by the MOD loader */ +int libxmp_period_to_note(int p) +{ + if (p <= 0) { + return 0; + } + + return libxmp_round(12.0 * log(PERIOD_BASE / p) / M_LN2) + 1; +} + +/* Get pitchbend from base note and amiga period */ +int libxmp_period_to_bend(struct context_data *ctx, double p, int n, double adj) +{ + struct module_data *m = &ctx->m; + double d; + + if (n == 0 || p < 0.1) { + return 0; + } + + switch (m->period_type) { + case PERIOD_LINEAR: + return 100 * (8 * (((240 - n) << 4) - p)); + case PERIOD_CSPD: + d = libxmp_note_to_period(ctx, n, 0, adj); + return libxmp_round(100.0 * (1536.0 / M_LN2) * log(p / d)); + default: + /* Amiga */ + d = libxmp_note_to_period(ctx, n, 0, adj); + return libxmp_round(100.0 * (1536.0 / M_LN2) * log(d / p)); + } +} + +/* Convert finetune = 1200 * log2(C2SPD/8363)) + * + * c = (1200.0 * log(c2spd) - 1200.0 * log(c4_rate)) / M_LN2; + * xpo = c/100; + * fin = 128 * (c%100) / 100; + */ +void libxmp_c2spd_to_note(int c2spd, int *n, int *f) +{ + int c; + + if (c2spd <= 0) { + *n = *f = 0; + return; + } + + c = (int)(1536.0 * log((double)c2spd / 8363) / M_LN2); + *n = c / 128; + *f = c % 128; +} + +#ifndef LIBXMP_CORE_PLAYER +/* Gravis Ultrasound frequency increments in steps of Hz/1024, where Hz is the + * current rate of the card and is dependent on the active channel count. + * For <=14 channels, the rate is 44100. For 15 to 32 channels, the rate is + * round(14 * 44100 / active_channels). + */ +static const double GUS_rates[19] = { + /* <= 14 */ 44100.0, + /* 15-20 */ 41160.0, 38587.5, 36317.65, 34300.0, 32494.74, 30870.0, + /* 21-26 */ 29400.0, 28063.64, 26843.48, 25725.0, 24696.0, 23746.15, + /* 27-32 */ 22866.67, 22050.0, 21289.66, 20580.0, 19916.13, 19294.75 +}; + +/* Get a Gravis Ultrasound frequency offset in Hz for a given number of steps. + */ +double libxmp_gus_frequency_steps(int num_steps, int num_channels_active) +{ + CLAMP(num_channels_active, 14, 32); + return (num_steps * GUS_rates[num_channels_active - 14]) / 1024.0; +} +#endif diff --git a/thirdparty/libxmp/src/period.h b/thirdparty/libxmp/src/period.h new file mode 100644 index 000000000..bf4bd175a --- /dev/null +++ b/thirdparty/libxmp/src/period.h @@ -0,0 +1,27 @@ +#ifndef LIBXMP_PERIOD_H +#define LIBXMP_PERIOD_H + +#define PERIOD_BASE 13696.0 /* C0 period */ + +/* Macros for period conversion */ +#define NOTE_B0 11 +#define NOTE_Bb0 (NOTE_B0 + 1) +#define MAX_NOTE (NOTE_B0 * 8) +#define MAX_PERIOD 0x1c56 +#define MIN_PERIOD_A 0x0071 +#define MAX_PERIOD_A 0x0358 +#define MIN_PERIOD_L 0x0000 +#define MAX_PERIOD_L 0x1e00 +#define MIN_NOTE_MOD 48 +#define MAX_NOTE_MOD 83 + +double libxmp_note_to_period (struct context_data *, int, int, double); +double libxmp_note_to_period_mix (int, int); +int libxmp_period_to_note (int); +int libxmp_period_to_bend (struct context_data *, double, int, double); +void libxmp_c2spd_to_note (int, int *, int *); +#ifndef LIBXMP_CORE_PLAYER +double libxmp_gus_frequency_steps (int, int); +#endif + +#endif /* LIBXMP_PERIOD_H */ diff --git a/thirdparty/libxmp/src/player.c b/thirdparty/libxmp/src/player.c new file mode 100644 index 000000000..4eacab676 --- /dev/null +++ b/thirdparty/libxmp/src/player.c @@ -0,0 +1,2216 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Sat, 18 Apr 1998 20:23:07 +0200 Frederic Bujon + * Pan effect bug fixed: In Fastracker II the track panning effect erases + * the instrument panning effect, and the same should happen in xmp. + */ + +/* + * Fri, 26 Jun 1998 13:29:25 -0400 (EDT) + * Reported by Jared Spiegel + * when the volume envelope is not enabled (disabled) on a sample, and a + * notoff is delivered to ft2 (via either a noteoff in the note column or + * command Kxx [where xx is # of ticks into row to give a noteoff to the + * sample]), ft2 will set the volume of playback of the sample to 00h. + * + * Claudio's fix: implementing effect K + */ + +#include "virtual.h" +#include "period.h" +#include "effects.h" +#include "player.h" +#include "mixer.h" +#ifndef LIBXMP_CORE_PLAYER +#include "extras.h" +#endif + +/* Values for multi-retrig */ +static const struct retrig_control rval[] = { + { 0, 1, 1 }, { -1, 1, 1 }, { -2, 1, 1 }, { -4, 1, 1 }, + { -8, 1, 1 }, { -16, 1, 1 }, { 0, 2, 3 }, { 0, 1, 2 }, + { 0, 1, 1 }, { 1, 1, 1 }, { 2, 1, 1 }, { 4, 1, 1 }, + { 8, 1, 1 }, { 16, 1, 1 }, { 0, 3, 2 }, { 0, 2, 1 }, + { 0, 0, 1 } /* Note cut */ +}; + + +/* + * "Anyway I think this is the most brilliant piece of crap we + * have managed to put up!" + * -- Ice of FC about "Mental Surgery" + */ + + +/* Envelope */ + +static int check_envelope_end(struct xmp_envelope *env, int x) +{ + int16 *data = env->data; + int idx; + + if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) + return 0; + + idx = (env->npt - 1) * 2; + + /* last node */ + if (x >= data[idx] || idx == 0) { + if (~env->flg & XMP_ENVELOPE_LOOP) { + return 1; + } + } + + return 0; +} + +static int get_envelope(struct xmp_envelope *env, int x, int def) +{ + int x1, x2, y1, y2; + int16 *data = env->data; + int idx; + + if (x < 0 || ~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) + return def; + + idx = (env->npt - 1) * 2; + + x1 = data[idx]; /* last node */ + if (x >= x1 || idx == 0) { + return data[idx + 1]; + } + + do { + idx -= 2; + x1 = data[idx]; + } while (idx > 0 && x1 > x); + + /* interpolate */ + y1 = data[idx + 1]; + x2 = data[idx + 2]; + y2 = data[idx + 3]; + + /* Interpolation requires x1 <= x <= x2 */ + if (x < x1 || x2 < x1) return y1; + + return x2 == x1 ? y2 : ((y2 - y1) * (x - x1) / (x2 - x1)) + y1; +} + +static int update_envelope_xm(struct xmp_envelope *env, int x, int release) +{ + int16 *data = env->data; + int has_loop, has_sus; + int lpe, lps, sus; + + has_loop = env->flg & XMP_ENVELOPE_LOOP; + has_sus = env->flg & XMP_ENVELOPE_SUS; + + lps = env->lps << 1; + lpe = env->lpe << 1; + sus = env->sus << 1; + + /* FT2 and IT envelopes behave in a different way regarding loops, + * sustain and release. When the sustain point is at the end of the + * envelope loop end and the key is released, FT2 escapes the loop + * while IT runs another iteration. (See EnvLoops.xm in the OpenMPT + * test cases.) + */ + if (has_loop && has_sus && sus == lpe) { + if (!release) + has_sus = 0; + } + + /* If the envelope point is set to somewhere after the sustain point + * or sustain loop, enable release to prevent the envelope point to + * return to the sustain point or loop start. (See Filip Skutela's + * farewell_tear.xm.) + */ + if (has_loop && x > data[lpe] + 1) { + release = 1; + } else if (has_sus && x > data[sus] + 1) { + release = 1; + } + + /* If enabled, stay at the sustain point */ + if (has_sus && !release) { + if (x >= data[sus]) { + x = data[sus]; + } + } + + /* Envelope loops */ + if (has_loop && x >= data[lpe]) { + if (!(release && has_sus && sus == lpe)) + x = data[lps]; + } + + return x; +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +static int update_envelope_it(struct xmp_envelope *env, int x, int release, int key_off) +{ + int16 *data = env->data; + int has_loop, has_sus; + int lpe, lps, sus, sue; + + has_loop = env->flg & XMP_ENVELOPE_LOOP; + has_sus = env->flg & XMP_ENVELOPE_SUS; + + lps = env->lps << 1; + lpe = env->lpe << 1; + sus = env->sus << 1; + sue = env->sue << 1; + + /* Release at the end of a sustain loop, run another loop */ + if (has_sus && key_off && x == data[sue] + 1) { + x = data[sus]; + } else + /* If enabled, stay in the sustain loop */ + if (has_sus && !release) { + if (x == data[sue] + 1) { + x = data[sus]; + } + } else + /* Finally, execute the envelope loop */ + if (has_loop) { + if (x > data[lpe]) { + x = data[lps]; + } + } + + return x; +} + +#endif + +static int update_envelope(struct xmp_envelope *env, int x, int release, int key_off, int it_env) +{ + if (x < 0xffff) { /* increment tick */ + x++; + } + + if (x < 0) { + return -1; + } + + if (~env->flg & XMP_ENVELOPE_ON || env->npt <= 0) { + return x; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + return it_env ? + update_envelope_it(env, x, release, key_off) : + update_envelope_xm(env, x, release); +#else + return update_envelope_xm(env, x, release); +#endif +} + + +/* Returns: 0 if do nothing, <0 to reset channel, >0 if has fade */ +static int check_envelope_fade(struct xmp_envelope *env, int x) +{ + int16 *data = env->data; + int idx; + + if (~env->flg & XMP_ENVELOPE_ON) + return 0; + + idx = (env->npt - 1) * 2; /* last node */ + if (x > data[idx]) { + if (data[idx + 1] == 0) + return -1; + else + return 1; + } + + return 0; +} + + +#ifndef LIBXMP_CORE_DISABLE_IT + +/* Impulse Tracker's filter effects are implemented using its MIDI macros. + * Any module can customize these and they are parameterized using various + * player and mixer values, which requires parsing them here instead of in + * the loader. Since they're MIDI macros, they can contain actual MIDI junk + * that needs to be skipped, and one macro may have multiple IT commands. */ + +struct midi_stream +{ + const char *pos; + int buffer; + int param; +}; + +static int midi_nibble(struct context_data *ctx, struct channel_data *xc, + int chn, struct midi_stream *in) +{ + struct xmp_instrument *xxi; + struct mixer_voice *vi; + int voc, val, byte = -1; + if (in->buffer >= 0) { + val = in->buffer; + in->buffer = -1; + return val; + } + + while (*in->pos) { + val = *(in->pos)++; + if (val >= '0' && val <= '9') return val - '0'; + if (val >= 'A' && val <= 'F') return val - 'A' + 10; + switch (val) { + case 'z': /* Macro parameter */ + byte = in->param; + break; + case 'n': /* Host key */ + byte = xc->key & 0x7f; + break; + case 'h': /* Host channel */ + byte = chn; + break; + case 'o': /* Offset effect memory */ + /* Intentionally not clamped, see ZxxSecrets.it */ + byte = xc->offset.memory; + break; + case 'm': /* Voice reverse flag */ + voc = libxmp_virt_mapchannel(ctx, chn); + vi = (voc >= 0) ? &ctx->p.virt.voice_array[voc] : NULL; + byte = vi ? !!(vi->flags & VOICE_REVERSE) : 0; + break; + case 'v': /* Note velocity */ + xxi = libxmp_get_instrument(ctx, xc->ins); + byte = ((uint32)ctx->p.gvol * + (uint32)xc->volume * + (uint32)xc->mastervol * + (uint32)xc->gvl * + (uint32)(xxi ? xxi->vol : 0x40)) >> 24UL; + CLAMP(byte, 1, 127); + break; + case 'u': /* Computed velocity */ + byte = xc->macro.finalvol >> 3; + CLAMP(byte, 1, 127); + break; + case 'x': /* Note panning */ + byte = xc->macro.notepan >> 1; + CLAMP(byte, 0, 127); + break; + case 'y': /* Computed panning */ + byte = xc->info_finalpan >> 1; + CLAMP(byte, 0, 127); + break; + case 'a': /* Ins MIDI Bank hi */ + case 'b': /* Ins MIDI Bank lo */ + case 'p': /* Ins MIDI Program */ + case 's': /* MPT: SysEx checksum */ + byte = 0; + break; + case 'c': /* Ins MIDI Channel */ + return 0; + } + + /* Byte output */ + if (byte >= 0) { + in->buffer = byte & 0xf; + return (byte >> 4) & 0xf; + } + } + return -1; +} + +static int midi_byte(struct context_data *ctx, struct channel_data *xc, + int chn, struct midi_stream *in) +{ + int a = midi_nibble(ctx, xc, chn, in); + int b = midi_nibble(ctx, xc, chn, in); + return (a >= 0 && b >= 0) ? (a << 4) | b : -1; +} + +static void apply_midi_macro_effect(struct channel_data *xc, int type, int val) +{ + switch (type) { + case 0: /* Filter cutoff */ + xc->filter.cutoff = val << 1; + break; + case 1: /* Filter resonance */ + xc->filter.resonance = val << 1; + break; + } +} + +static void execute_midi_macro(struct context_data *ctx, struct channel_data *xc, + int chn, struct midi_macro *midi, int param) +{ + struct midi_stream in; + int byte, cmd, val; + + in.pos = midi->data; + in.buffer = -1; + in.param = param; + + while (*in.pos) { + /* Very simple MIDI 1.0 parser--most bytes can just be ignored + * (or passed through, if libxmp gets MIDI output). All bytes + * with bit 7 are statuses which interrupt unfinished messages + * ("Data Types: Status Bytes") or are real time messages. + * This holds even for SysEx messages, which end at ANY non- + * real time status ("System Common Messages: EOX"). + * + * IT intercepts internal "messages" that begin with F0 F0, + * which in MIDI is a useless zero-length SysEx followed by + * a second SysEx. They are four bytes long including F0 F0, + * and shouldn't be passed through. OpenMPT also uses F0 F1. + */ + cmd = -1; + byte = midi_byte(ctx, xc, chn, &in); + if (byte == 0xf0) { + byte = midi_byte(ctx, xc, chn, &in); + if (byte == 0xf0 || byte == 0xf1) + cmd = byte & 0xf; + } + if (cmd < 0) { + if (byte == 0xfa || byte == 0xfc || byte == 0xff) { + /* These real time statuses can appear anywhere + * (even in SysEx) and reset the channel filter + * params. See: OpenMPT ZxxSecrets.it */ + apply_midi_macro_effect(xc, 0, 127); + apply_midi_macro_effect(xc, 1, 0); + } + continue; + } + cmd = midi_byte(ctx, xc, chn, &in) | (cmd << 8); + val = midi_byte(ctx, xc, chn, &in); + if (cmd < 0 || cmd >= 0x80 || val < 0 || val >= 0x80) { + continue; + } + apply_midi_macro_effect(xc, cmd, val); + } +} + +/* This needs to occur before all process_* functions: + * - It modifies the filter parameters, used by process_frequency. + * - process_volume and process_pan apply slide effects, which the + * filter parameters expect to occur after macro effect parsing. */ +static void update_midi_macro(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + struct midi_macro_data *midicfg = m->midi; + struct midi_macro *macro; + int val; + + if (TEST(MIDI_MACRO) && HAS_QUIRK(QUIRK_FILTER)) { + if (xc->macro.slide > 0) { + xc->macro.val += xc->macro.slide; + if (xc->macro.val > xc->macro.target) { + xc->macro.val = xc->macro.target; + xc->macro.slide = 0; + } + } else if (xc->macro.slide < 0) { + xc->macro.val += xc->macro.slide; + if (xc->macro.val < xc->macro.target) { + xc->macro.val = xc->macro.target; + xc->macro.slide = 0; + } + } else if (p->frame) { + /* Execute non-smooth macros on frame 0 only */ + return; + } + + val = (int)xc->macro.val; + if (val >= 0x80) { + if (midicfg) { + macro = &midicfg->fixed[val - 0x80]; + execute_midi_macro(ctx, xc, chn, macro, val); + } else if (val < 0x90) { + /* Default fixed macro: set resonance */ + apply_midi_macro_effect(xc, 1, (val - 0x80) << 3); + } + } else if (midicfg) { + macro = &midicfg->param[xc->macro.active]; + execute_midi_macro(ctx, xc, chn, macro, val); + } else if (xc->macro.active == 0) { + /* Default parameterized macro 0: set filter cutoff */ + apply_midi_macro_effect(xc, 0, val); + } + } +} + +#endif /* LIBXMP_CORE_DISABLE_IT */ + + +#ifndef LIBXMP_CORE_PLAYER + +/* From http://www.un4seen.com/forum/?topic=7554.0 + * + * "Invert loop" effect replaces (!) sample data bytes within loop with their + * bitwise complement (NOT). The parameter sets speed of altering the samples. + * This effectively trashes the sample data. Because of that this effect was + * supposed to be removed in the very next ProTracker versions, but it was + * never removed. + * + * Prior to [Protracker 1.1A] this effect is called "Funk Repeat" and it moves + * loop of the instrument (just the loop information - sample data is not + * altered). The parameter is the speed of moving the loop. + */ + +static const int invloop_table[] = { + 0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128 +}; + +static void update_invloop(struct context_data *ctx, struct channel_data *xc) +{ + struct xmp_sample *xxs = libxmp_get_sample(ctx, xc->smp); + struct module_data *m = &ctx->m; + int lps, len = -1; + + xc->invloop.count += invloop_table[xc->invloop.speed]; + + if (xxs != NULL) { + if (xxs->flg & XMP_SAMPLE_LOOP) { + lps = xxs->lps; + len = xxs->lpe - lps; + } else if (xxs->flg & XMP_SAMPLE_SLOOP) { + /* Some formats that support invert loop use sustain + * loops instead (Digital Symphony). */ + lps = m->xtra[xc->smp].sus; + len = m->xtra[xc->smp].sue - lps; + } + } + + if (len >= 0 && xc->invloop.count >= 128) { + xc->invloop.count = 0; + + if (++xc->invloop.pos > len) { + xc->invloop.pos = 0; + } + + if (xxs->data == NULL) { + return; + } + + if (~xxs->flg & XMP_SAMPLE_16BIT) { + xxs->data[lps + xc->invloop.pos] ^= 0xff; + } + } +} + +#endif + +/* + * From OpenMPT Arpeggio.xm test: + * + * "[FT2] Arpeggio behavior is very weird with more than 16 ticks per row. This + * comes from the fact that Fasttracker 2 uses a LUT for computing the arpeggio + * note (instead of doing something like tick%3 or similar). The LUT only has + * 16 entries, so when there are more than 16 ticks, it reads beyond array + * boundaries. The vibrato table happens to be stored right after arpeggio + * table. The tables look like this in memory: + * + * ArpTab: 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0 + * VibTab: 0,24,49,74,97,120,141,161,180,197,... + * + * All values except for the first in the vibrato table are greater than 1, so + * they trigger the third arpeggio note. Keep in mind that Fasttracker 2 counts + * downwards, so the table has to be read from back to front, i.e. at 16 ticks + * per row, the 16th entry in the LUT is the first to be read. This is also the + * reason why Arpeggio is played 'backwards' in Fasttracker 2." + */ +static int ft2_arpeggio(struct context_data *ctx, struct channel_data *xc) +{ + struct player_data *p = &ctx->p; + int i; + + if (xc->arpeggio.val[1] == 0 && xc->arpeggio.val[2] == 0) { + return 0; + } + + if (p->frame == 0) { + return 0; + } + + i = p->speed - (p->frame % p->speed); + + if (i == 16) { + return 0; + } else if (i > 16) { + return xc->arpeggio.val[2]; + } + + return xc->arpeggio.val[i % 3]; +} + +static int arpeggio(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + int arp; + + if (HAS_QUIRK(QUIRK_FT2BUGS)) { + arp = ft2_arpeggio(ctx, xc); + } else { + arp = xc->arpeggio.val[xc->arpeggio.count]; + } + + xc->arpeggio.count++; + xc->arpeggio.count %= xc->arpeggio.size; + + return arp; +} + +static int is_first_frame(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + + switch (m->read_event_type) { +#ifndef LIBXMP_CORE_DISABLE_IT + case READ_EVENT_IT: + /* fall through */ +#endif + case READ_EVENT_ST3: + return p->frame % p->speed == 0; + default: + return p->frame == 0; + } +} + +static void reset_channels(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct smix_data *smix = &ctx->smix; + struct channel_data *xc; + int i; + +#ifndef LIBXMP_CORE_PLAYER + for (i = 0; i < p->virt.virt_channels; i++) { + void *extra; + + xc = &p->xc_data[i]; + extra = xc->extra; + memset(xc, 0, sizeof (struct channel_data)); + xc->extra = extra; + libxmp_reset_channel_extras(ctx, xc); + xc->ins = -1; + xc->old_ins = 1; /* raw value */ + xc->key = -1; + xc->volume = m->volbase; + } +#else + for (i = 0; i < p->virt.virt_channels; i++) { + xc = &p->xc_data[i]; + memset(xc, 0, sizeof (struct channel_data)); + xc->ins = -1; + xc->old_ins = 1; /* raw value */ + xc->key = -1; + xc->volume = m->volbase; + } +#endif + + for (i = 0; i < p->virt.num_tracks; i++) { + xc = &p->xc_data[i]; + + if (i >= mod->chn && i < mod->chn + smix->chn) { + xc->mastervol = 0x40; + xc->pan.val = 0x80; + } else { + xc->mastervol = mod->xxc[i].vol; + xc->pan.val = mod->xxc[i].pan; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + xc->filter.cutoff = 0xff; + + /* Amiga split channel */ + if (mod->xxc[i].flg & XMP_CHANNEL_SPLIT) { + int j; + + xc->split = ((mod->xxc[i].flg & 0x30) >> 4) + 1; + /* Connect split channel pairs */ + for (j = 0; j < i; j++) { + if (mod->xxc[j].flg & XMP_CHANNEL_SPLIT) { + if (p->xc_data[j].split == xc->split) { + p->xc_data[j].pair = i; + xc->pair = j; + } + } + } + } else { + xc->split = 0; + } +#endif + + /* Surround channel */ + if (mod->xxc[i].flg & XMP_CHANNEL_SURROUND) { + xc->pan.surround = 1; + } + } +} + +static int check_delay(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + struct module_data *m = &ctx->m; + + /* Tempo affects delay and must be computed first */ + if ((e->fxt == FX_SPEED && e->fxp < 0x20) || e->fxt == FX_S3M_SPEED) { + if (e->fxp) { + p->speed = e->fxp; + } + } + if ((e->f2t == FX_SPEED && e->f2p < 0x20) || e->f2t == FX_S3M_SPEED) { + if (e->f2p) { + p->speed = e->f2p; + } + } + + /* Delay event read */ + if (e->fxt == FX_EXTENDED && MSN(e->fxp) == EX_DELAY && LSN(e->fxp)) { + xc->delay = LSN(e->fxp) + 1; + goto do_delay; + } + if (e->f2t == FX_EXTENDED && MSN(e->f2p) == EX_DELAY && LSN(e->f2p)) { + xc->delay = LSN(e->f2p) + 1; + goto do_delay; + } + + return 0; + + do_delay: + memcpy(&xc->delayed_event, e, sizeof (struct xmp_event)); + + if (e->ins) { + xc->delayed_ins = e->ins; + } + + if (HAS_QUIRK(QUIRK_RTDELAY)) { + if (e->vol == 0 && e->f2t == 0 && e->ins == 0 && e->note != XMP_KEY_OFF) + xc->delayed_event.vol = xc->volume + 1; + if (e->note == 0) + xc->delayed_event.note = xc->key + 1; + if (e->ins == 0) + xc->delayed_event.ins = xc->old_ins; + } + + return 1; +} + +static inline void read_row(struct context_data *ctx, int pat, int row) +{ + int chn; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + struct xmp_event ev; + + for (chn = 0; chn < mod->chn; chn++) { + const int num_rows = mod->xxt[TRACK_NUM(pat, chn)]->rows; + if (row < num_rows) { + memcpy(&ev, &EVENT(pat, chn, row), sizeof(ev)); + } else { + memset(&ev, 0, sizeof(ev)); + } + + if (ev.note == XMP_KEY_OFF) { + int env_on = 0; + int ins = ev.ins - 1; + + if (IS_VALID_INSTRUMENT(ins) && + (mod->xxi[ins].aei.flg & XMP_ENVELOPE_ON)) { + env_on = 1; + } + + if (ev.fxt == FX_EXTENDED && MSN(ev.fxp) == EX_DELAY) { + if (ev.ins && (LSN(ev.fxp) || env_on)) { + if (LSN(ev.fxp)) { + ev.note = 0; + } + ev.fxp = ev.fxt = 0; + } + } + } + + if (check_delay(ctx, &ev, chn) == 0) { + /* rowdelay_set bit 1 is set only in the first tick of the row + * event if the delay causes the tick count resets to 0. We test + * it to read row events only in the start of the row. (see the + * OpenMPT test case FineVolColSlide.it) + */ + if (!f->rowdelay_set || ((f->rowdelay_set & ROWDELAY_FIRST_FRAME) && f->rowdelay > 0)) { + libxmp_read_event(ctx, &ev, chn); +#ifndef LIBXMP_CORE_PLAYER + libxmp_med_hold_hack(ctx, pat, chn, row); +#endif + } + } else { + if (IS_PLAYER_MODE_IT()) { + /* Reset flags. See SlideDelay.it */ + p->xc_data[chn].flags = 0; + } + } + } +} + +static inline int get_channel_vol(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + int root; + + /* channel is a root channel */ + if (chn < p->virt.num_tracks) + return p->channel_vol[chn]; + + /* channel is invalid */ + if (chn >= p->virt.virt_channels) + return 0; + + /* root is invalid */ + root = libxmp_virt_getroot(ctx, chn); + if (root < 0) + return 0; + + return p->channel_vol[root]; +} + +static int tremor_ft2(struct context_data *ctx, int chn, int finalvol) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + if (xc->tremor.count & 0x80) { + if (TEST(TREMOR) && p->frame != 0) { + xc->tremor.count &= ~0x20; + if (xc->tremor.count == 0x80) { + /* end of down cycle, set up counter for up */ + xc->tremor.count = xc->tremor.up | 0xc0; + } else if (xc->tremor.count == 0xc0) { + /* end of up cycle, set up counter for down */ + xc->tremor.count = xc->tremor.down | 0x80; + } else { + xc->tremor.count--; + } + } + + if ((xc->tremor.count & 0xe0) == 0x80) { + finalvol = 0; + } + } + + return finalvol; +} + +static int tremor_s3m(struct context_data *ctx, int chn, int finalvol) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + if (TEST(TREMOR)) { + if (xc->tremor.count == 0) { + /* end of down cycle, set up counter for up */ + xc->tremor.count = xc->tremor.up | 0x80; + } else if (xc->tremor.count == 0x80) { + /* end of up cycle, set up counter for down */ + xc->tremor.count = xc->tremor.down; + } + + xc->tremor.count--; + + if (~xc->tremor.count & 0x80) { + finalvol = 0; + } + } + + return finalvol; +} + +/* + * Update channel data + */ + +#define DOENV_RELEASE ((TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF)) + +static void process_volume(struct context_data *ctx, int chn, int act) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *instrument; + int finalvol; + uint16 vol_envelope; + int fade = 0; + + instrument = libxmp_get_instrument(ctx, xc->ins); + + /* Keyoff and fadeout */ + + /* Keyoff event in IT doesn't reset fadeout (see jeff93.it) + * In XM it depends on envelope (see graff-strange_land.xm vs + * Decibelter - Cosmic 'Wegian Mamas.xm) + */ + if (HAS_QUIRK(QUIRK_KEYOFF)) { + /* If IT, only apply fadeout on note release if we don't + * have envelope, or if we have envelope loop + */ + if (TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF) { + if ((~instrument->aei.flg & XMP_ENVELOPE_ON) || + (instrument->aei.flg & XMP_ENVELOPE_LOOP)) { + fade = 1; + } + } + } else { + if (~instrument->aei.flg & XMP_ENVELOPE_ON) { + if (TEST_NOTE(NOTE_ENV_RELEASE)) { + xc->fadeout = 0; + } + } + + if (TEST_NOTE(NOTE_ENV_RELEASE) || act == VIRT_ACTION_OFF) { + fade = 1; + } + } + + if (!TEST_PER(VENV_PAUSE)) { + xc->v_idx = update_envelope(&instrument->aei, xc->v_idx, + DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); + } + + vol_envelope = get_envelope(&instrument->aei, xc->v_idx, 64); + if (check_envelope_end(&instrument->aei, xc->v_idx)) { + if (vol_envelope == 0) { + SET_NOTE(NOTE_END); + } + SET_NOTE(NOTE_ENV_END); + } + + /* IT starts fadeout automatically at the end of the volume envelope. */ + switch (check_envelope_fade(&instrument->aei, xc->v_idx)) { + case -1: + SET_NOTE(NOTE_END); + /* Don't reset channel, we may have a tone portamento later + * virt_resetchannel(ctx, chn); + */ + break; + case 0: + break; + default: + if (HAS_QUIRK(QUIRK_ENVFADE)) { + SET_NOTE(NOTE_FADEOUT); + } + } + + /* IT envelope fadeout starts immediately after the envelope tick, + * so process fadeout after the volume envelope. */ + if (TEST_NOTE(NOTE_FADEOUT) || act == VIRT_ACTION_FADE) { + fade = 1; + } + + if (fade) { + if (xc->fadeout > xc->ins_fade) { + xc->fadeout -= xc->ins_fade; + } else { + xc->fadeout = 0; + SET_NOTE(NOTE_END); + } + } + + /* If note ended in background channel, we can safely reset it */ + if (TEST_NOTE(NOTE_END) && chn >= p->virt.num_tracks) { + libxmp_virt_resetchannel(ctx, chn); + return; + } + +#ifndef LIBXMP_CORE_PLAYER + finalvol = libxmp_extras_get_volume(ctx, xc); +#else + finalvol = xc->volume; +#endif + + if (IS_PLAYER_MODE_IT()) { + finalvol = xc->volume * (100 - xc->rvv) / 100; + } + + if (TEST(TREMOLO)) { + /* OpenMPT VibratoReset.mod */ + if (!is_first_frame(ctx) || !HAS_QUIRK(QUIRK_PROTRACK)) { + finalvol += libxmp_lfo_get(ctx, &xc->tremolo.lfo, 0) / (1 << 6); + } + + if (!is_first_frame(ctx) || HAS_QUIRK(QUIRK_VIBALL)) { + libxmp_lfo_update(&xc->tremolo.lfo); + } + } + + CLAMP(finalvol, 0, m->volbase); + + finalvol = (finalvol * xc->fadeout) >> 6; /* 16 bit output */ + + finalvol = (uint32)(vol_envelope * p->gvol * xc->mastervol / + m->gvolbase * ((int)finalvol * 0x40 / m->volbase)) >> 18; + + /* Apply channel volume */ + finalvol = finalvol * get_channel_vol(ctx, chn) / 100; + +#ifndef LIBXMP_CORE_PLAYER + /* Volume translation table (for PTM, ARCH, COCO) */ + if (m->vol_table) { + finalvol = m->volbase == 0xff ? + m->vol_table[finalvol >> 2] << 2 : + m->vol_table[finalvol >> 4] << 4; + } +#endif + + if (HAS_QUIRK(QUIRK_INSVOL)) { + finalvol = (finalvol * instrument->vol * xc->gvl) >> 12; + } + + if (IS_PLAYER_MODE_FT2()) { + finalvol = tremor_ft2(ctx, chn, finalvol); + } else { + finalvol = tremor_s3m(ctx, chn, finalvol); + } +#ifndef LIBXMP_CORE_DISABLE_IT + xc->macro.finalvol = finalvol; +#endif + + if (chn < m->mod.chn) { + finalvol = finalvol * p->master_vol / 100; + } else { + finalvol = finalvol * p->smix_vol / 100; + } + + xc->info_finalvol = TEST_NOTE(NOTE_SAMPLE_END) ? 0 : finalvol; + + libxmp_virt_setvol(ctx, chn, finalvol); + + /* Check Amiga split channel */ + if (xc->split) { + libxmp_virt_setvol(ctx, xc->pair, finalvol); + } +} + +static void process_frequency(struct context_data *ctx, int chn, int act) +{ +#ifndef LIBXMP_CORE_DISABLE_IT + struct mixer_data *s = &ctx->s; +#endif + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *instrument; + double period, vibrato; + double final_period; + int linear_bend; + int frq_envelope; + int arp; +#ifndef LIBXMP_CORE_DISABLE_IT + int cutoff, resonance; +#endif + + instrument = libxmp_get_instrument(ctx, xc->ins); + + if (!TEST_PER(FENV_PAUSE)) { + xc->f_idx = update_envelope(&instrument->fei, xc->f_idx, + DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); + } + frq_envelope = get_envelope(&instrument->fei, xc->f_idx, 0); + +#ifndef LIBXMP_CORE_PLAYER + /* Do note slide */ + + if (TEST(NOTE_SLIDE)) { + if (xc->noteslide.count == 0) { + xc->note += xc->noteslide.slide; + xc->period = libxmp_note_to_period(ctx, xc->note, + xc->finetune, xc->per_adj); + xc->noteslide.count = xc->noteslide.speed; + } + xc->noteslide.count--; + + libxmp_virt_setnote(ctx, chn, xc->note); + } +#endif + + /* Instrument vibrato */ + vibrato = 1.0 * libxmp_lfo_get(ctx, &xc->insvib.lfo, 1) / + (4096 * (1 + xc->insvib.sweep)); + libxmp_lfo_update(&xc->insvib.lfo); + if (xc->insvib.sweep > 1) { + xc->insvib.sweep -= 2; + } else { + xc->insvib.sweep = 0; + } + + /* Vibrato */ + if (TEST(VIBRATO) || TEST_PER(VIBRATO)) { + /* OpenMPT VibratoReset.mod */ + if (!is_first_frame(ctx) || !HAS_QUIRK(QUIRK_PROTRACK)) { + int shift = HAS_QUIRK(QUIRK_VIBHALF) ? 10 : 9; + int vib = libxmp_lfo_get(ctx, &xc->vibrato.lfo, 1) / (1 << shift); + + if (HAS_QUIRK(QUIRK_VIBINV)) { + vibrato -= vib; + } else { + vibrato += vib; + } + } + + if (!is_first_frame(ctx) || HAS_QUIRK(QUIRK_VIBALL)) { + libxmp_lfo_update(&xc->vibrato.lfo); + } + } + + period = xc->period; +#ifndef LIBXMP_CORE_PLAYER + period += libxmp_extras_get_period(ctx, xc); +#endif + + if (HAS_QUIRK(QUIRK_ST3BUGS)) { + if (period < 0.25) { + libxmp_virt_resetchannel(ctx, chn); + } + } + /* Sanity check */ + if (period < 0.1) { + period = 0.1; + } + + /* Arpeggio */ + arp = arpeggio(ctx, xc); + + /* Pitch bend */ + + linear_bend = libxmp_period_to_bend(ctx, period + vibrato, xc->note, xc->per_adj); + + if (TEST_NOTE(NOTE_GLISSANDO) && TEST(TONEPORTA)) { + if (linear_bend > 0) { + linear_bend = (linear_bend + 6400) / 12800 * 12800; + } else if (linear_bend < 0) { + linear_bend = (linear_bend - 6400) / 12800 * 12800; + } + } + + if (HAS_QUIRK(QUIRK_FT2BUGS)) { + if (arp) { + /* OpenMPT ArpSlide.xm */ + linear_bend = linear_bend / 12800 * 12800 + + xc->finetune * 100; + + /* OpenMPT ArpeggioClamp.xm */ + if (xc->note + arp > 107) { + if (p->speed - (p->frame % p->speed) > 0) { + arp = 108 - xc->note; + } + } + } + } + + /* Envelope */ + + if (xc->f_idx >= 0 && (~instrument->fei.flg & XMP_ENVELOPE_FLT)) { + /* IT pitch envelopes are always linear, even in Amiga period + * mode. Each unit in the envelope scale is 1/25 semitone. + */ + linear_bend += frq_envelope << 7; + } + + /* Arpeggio */ + + if (arp != 0) { + linear_bend += (100 << 7) * arp; + + /* OpenMPT ArpWrapAround.mod */ + if (HAS_QUIRK(QUIRK_PROTRACK)) { + if (xc->note + arp > MAX_NOTE_MOD + 1) { + linear_bend -= 12800 * (3 * 12); + } else if (xc->note + arp > MAX_NOTE_MOD) { + libxmp_virt_setvol(ctx, chn, 0); + } + } + } + + +#ifndef LIBXMP_CORE_PLAYER + linear_bend += libxmp_extras_get_linear_bend(ctx, xc); +#endif + + final_period = libxmp_note_to_period_mix(xc->note, linear_bend); + + /* From OpenMPT PeriodLimit.s3m: + * "ScreamTracker 3 limits the final output period to be at least 64, + * i.e. when playing a note that is too high or when sliding the + * period lower than 64, the output period will simply be clamped to + * 64. However, when reaching a period of 0 through slides, the + * output on the channel should be stopped." + */ + /* ST3 uses periods*4, so the limit is 16. Adjusted to the exact + * A6 value because we compute periods in floating point. + */ + if (HAS_QUIRK(QUIRK_ST3BUGS)) { + if (final_period < 16.239270) { /* A6 */ + final_period = 16.239270; + } + } + + libxmp_virt_setperiod(ctx, chn, final_period); + + /* For xmp_get_frame_info() */ + xc->info_pitchbend = linear_bend >> 7; + xc->info_period = MIN(final_period * 4096, INT_MAX); + + if (IS_PERIOD_MODRNG()) { + const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0) * 4096; + const double max_period = libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0) * 4096; + CLAMP(xc->info_period, min_period, max_period); + } else if (xc->info_period < (1 << 12)) { + xc->info_period = (1 << 12); + } + + +#ifndef LIBXMP_CORE_DISABLE_IT + + /* Process filter */ + + if (!HAS_QUIRK(QUIRK_FILTER)) { + return; + } + + if (xc->f_idx >= 0 && (instrument->fei.flg & XMP_ENVELOPE_FLT)) { + if (frq_envelope < 0xfe) { + xc->filter.envelope = frq_envelope; + } + cutoff = xc->filter.cutoff * xc->filter.envelope >> 8; + } else { + cutoff = xc->filter.cutoff; + } + resonance = xc->filter.resonance; + + if (cutoff > 0xff) { + cutoff = 0xff; + } + /* IT: cutoff 127 + resonance 0 turns off the filter, but this + * is only applied when playing a new note without toneporta. + * All other combinations take effect immediately. + * See OpenMPT filter-reset.it, filter-reset-carry.it */ + if (cutoff < 0xfe || resonance > 0 || xc->filter.can_disable) { + int a0, b0, b1; + libxmp_filter_setup(s->freq, cutoff, resonance, &a0, &b0, &b1); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_A0, a0); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B0, b0); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_FILTER_B1, b1); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_RESONANCE, resonance); + libxmp_virt_seteffect(ctx, chn, DSP_EFFECT_CUTOFF, cutoff); + xc->filter.can_disable = 0; + } + +#endif +} + +static void process_pan(struct context_data *ctx, int chn, int act) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct mixer_data *s = &ctx->s; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *instrument; + int finalpan, panbrello = 0; + int pan_envelope; + int channel_pan; + + instrument = libxmp_get_instrument(ctx, xc->ins); + + if (!TEST_PER(PENV_PAUSE)) { + xc->p_idx = update_envelope(&instrument->pei, xc->p_idx, + DOENV_RELEASE, TEST(KEY_OFF), IS_PLAYER_MODE_IT()); + } + pan_envelope = get_envelope(&instrument->pei, xc->p_idx, 32); + +#ifndef LIBXMP_CORE_DISABLE_IT + if (TEST(PANBRELLO)) { + panbrello = libxmp_lfo_get(ctx, &xc->panbrello.lfo, 0) / 512; + if (is_first_frame(ctx)) { + libxmp_lfo_update(&xc->panbrello.lfo); + } + } + xc->macro.notepan = xc->pan.val + panbrello + 0x80; +#endif + + channel_pan = xc->pan.val; + +#if 0 +#ifdef LIBXMP_PAULA_SIMULATOR + /* Always use 100% pan separation in Amiga mode */ + if (p->flags & XMP_FLAGS_A500) { + if (IS_AMIGA_MOD()) { + channel_pan = channel_pan < 0x80 ? 0 : 0xff; + } + } +#endif +#endif + + finalpan = channel_pan + panbrello + (pan_envelope - 32) * + (128 - abs(xc->pan.val - 128)) / 32; + + if (IS_PLAYER_MODE_IT()) { + finalpan = finalpan + xc->rpv * 4; + } + + CLAMP(finalpan, 0, 255); + + if (s->format & XMP_FORMAT_MONO || xc->pan.surround) { + finalpan = 0; + } else { + finalpan = (finalpan - 0x80) * s->mix / 100; + } + + xc->info_finalpan = finalpan + 0x80; + + if (xc->pan.surround) { + libxmp_virt_setpan(ctx, chn, PAN_SURROUND); + } else { + libxmp_virt_setpan(ctx, chn, finalpan); + } +} + +static void update_volume(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; +#ifndef LIBXMP_CORE_DISABLE_IT + struct flow_control *f = &p->flow; +#endif + struct channel_data *xc = &p->xc_data[chn]; + + /* Volume slides happen in all frames but the first, except when the + * "volume slide on all frames" flag is set. + */ + if (p->frame % p->speed != 0 || HAS_QUIRK(QUIRK_VSALL)) { + if (TEST(GVOL_SLIDE)) { + p->gvol += xc->gvol.slide; + } + + if (TEST(VOL_SLIDE) || TEST_PER(VOL_SLIDE)) { + xc->volume += xc->vol.slide; + } + +#ifndef LIBXMP_CORE_PLAYER + if (TEST_PER(VOL_SLIDE)) { + if (xc->vol.slide > 0) { + int target = MAX(xc->vol.target - 1, m->volbase); + if (xc->volume > target) { + xc->volume = target; + RESET_PER(VOL_SLIDE); + } + } + if (xc->vol.slide < 0) { + int target = xc->vol.target > 0 ? MIN(0, xc->vol.target - 1) : 0; + if (xc->volume < target) { + xc->volume = target; + RESET_PER(VOL_SLIDE); + } + } + } +#endif + + if (TEST(VOL_SLIDE_2)) { + xc->volume += xc->vol.slide2; + } + if (TEST(TRK_VSLIDE)) { + xc->mastervol += xc->trackvol.slide; + } + } + + if (p->frame % p->speed == 0) { + /* Process "fine" effects */ + if (TEST(FINE_VOLS)) { + xc->volume += xc->vol.fslide; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + if (TEST(FINE_VOLS_2)) { + /* OpenMPT FineVolColSlide.it: + * Unlike fine volume slides in the effect column, + * fine volume slides in the volume column are only + * ever executed on the first tick -- not on multiples + * of the first tick if there is a pattern delay. + */ + if (!f->rowdelay_set || f->rowdelay_set & ROWDELAY_FIRST_FRAME) { + xc->volume += xc->vol.fslide2; + } + } +#endif + + if (TEST(TRK_FVSLIDE)) { + xc->mastervol += xc->trackvol.fslide; + } + + if (TEST(GVOL_SLIDE)) { + p->gvol += xc->gvol.fslide; + } + } + + /* Clamp volumes */ + CLAMP(xc->volume, 0, m->volbase); + CLAMP(p->gvol, 0, m->gvolbase); + CLAMP(xc->mastervol, 0, m->volbase); + + if (xc->split) { + p->xc_data[xc->pair].volume = xc->volume; + } +} + +static void update_frequency(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + + if (!is_first_frame(ctx) || HAS_QUIRK(QUIRK_PBALL)) { + if (TEST(PITCHBEND) || TEST_PER(PITCHBEND)) { + xc->period += xc->freq.slide; + if (HAS_QUIRK(QUIRK_PROTRACK)) { + xc->porta.target = xc->period; + } + } + + /* Do tone portamento */ + if (TEST(TONEPORTA) || TEST_PER(TONEPORTA)) { + if (xc->porta.target > 0) { + int end = 0; + if (xc->porta.dir > 0) { + xc->period += xc->porta.slide; + if (xc->period >= xc->porta.target) + end = 1; + } else { + xc->period -= xc->porta.slide; + if (xc->period <= xc->porta.target) + end = 1; + } + + if (end) { + /* reached end */ + xc->period = xc->porta.target; + xc->porta.dir = 0; + RESET(TONEPORTA); + RESET_PER(TONEPORTA); + + if (HAS_QUIRK(QUIRK_PROTRACK)) { + xc->porta.target = -1; + } + } + } + } + } + + if (is_first_frame(ctx)) { + if (TEST(FINE_BEND)) { + xc->period += xc->freq.fslide; + } + +#ifndef LIBXMP_CORE_PLAYER + if (TEST(FINE_NSLIDE)) { + xc->note += xc->noteslide.fslide; + xc->period = libxmp_note_to_period(ctx, xc->note, + xc->finetune, xc->per_adj); + } +#endif + } + + switch (m->period_type) { + case PERIOD_LINEAR: + CLAMP(xc->period, MIN_PERIOD_L, MAX_PERIOD_L); + break; + case PERIOD_MODRNG: { + const double min_period = libxmp_note_to_period(ctx, MAX_NOTE_MOD, xc->finetune, 0); + const double max_period = libxmp_note_to_period(ctx, MIN_NOTE_MOD, xc->finetune, 0); + CLAMP(xc->period, min_period, max_period); + } + break; + } + + /* Check for invalid periods (from Toru Egashira's NSPmod) + * panic.s3m has negative periods + * ambio.it uses low (~8) period values + */ + if (xc->period < 0.25) { + libxmp_virt_setvol(ctx, chn, 0); + } +} + +static void update_pan(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + if (TEST(PAN_SLIDE)) { + if (is_first_frame(ctx)) { + xc->pan.val += xc->pan.fslide; + } else { + xc->pan.val += xc->pan.slide; + } + + if (xc->pan.val < 0) { + xc->pan.val = 0; + } else if (xc->pan.val > 0xff) { + xc->pan.val = 0xff; + } + } +} + +static void play_channel(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int act; + + xc->info_finalvol = 0; + +#ifndef LIBXMP_CORE_DISABLE_IT + /* IT tempo slide */ + if (!is_first_frame(ctx) && TEST(TEMPO_SLIDE)) { + p->bpm += xc->tempo.slide; + CLAMP(p->bpm, 0x20, 0xff); + } +#endif + + /* Do delay */ + if (xc->delay > 0) { + if (--xc->delay == 0) { + libxmp_read_event(ctx, &xc->delayed_event, chn); + } + } + +#ifndef LIBXMP_CORE_DISABLE_IT + /* IT MIDI macros need to update regardless of the current voice state. */ + update_midi_macro(ctx, chn); +#endif + + act = libxmp_virt_cstat(ctx, chn); + if (act == VIRT_INVALID) { + /* We need this to keep processing global volume slides */ + update_volume(ctx, chn); + return; + } + + if (p->frame == 0 && act != VIRT_ACTIVE) { + if (!IS_VALID_INSTRUMENT_OR_SFX(xc->ins) || act == VIRT_ACTION_CUT) { + libxmp_virt_resetchannel(ctx, chn); + return; + } + } + + if (!IS_VALID_INSTRUMENT_OR_SFX(xc->ins)) + return; + +#ifndef LIBXMP_CORE_PLAYER + libxmp_play_extras(ctx, xc, chn); +#endif + + /* Do cut/retrig */ + if (TEST(RETRIG)) { + int cond = HAS_QUIRK(QUIRK_S3MRTG) ? + --xc->retrig.count <= 0 : + --xc->retrig.count == 0; + + if (cond) { + if (xc->retrig.type < 0x10) { + /* don't retrig on cut */ + libxmp_virt_voicepos(ctx, chn, 0); + } else { + SET_NOTE(NOTE_END); + } + xc->volume += rval[xc->retrig.type].s; + xc->volume *= rval[xc->retrig.type].m; + xc->volume /= rval[xc->retrig.type].d; + xc->retrig.count = LSN(xc->retrig.val); + + if (xc->retrig.limit > 0) { + /* Limit the number of retriggers. */ + --xc->retrig.limit; + if (xc->retrig.limit == 0) + RESET(RETRIG); + } + } + } + + /* Do keyoff */ + if (xc->keyoff) { + if (--xc->keyoff == 0) + SET_NOTE(NOTE_RELEASE); + } + + libxmp_virt_release(ctx, chn, TEST_NOTE(NOTE_SAMPLE_RELEASE)); + + update_volume(ctx, chn); + update_frequency(ctx, chn); + update_pan(ctx, chn); + + process_volume(ctx, chn, act); + process_frequency(ctx, chn, act); + process_pan(ctx, chn, act); + +#ifndef LIBXMP_CORE_PLAYER + if (HAS_QUIRK(QUIRK_PROTRACK | QUIRK_INVLOOP) && xc->ins < mod->ins) { + update_invloop(ctx, xc); + } +#endif + + if (TEST_NOTE(NOTE_SUSEXIT)) { + SET_NOTE(NOTE_ENV_RELEASE); + } + + xc->info_position = libxmp_virt_getvoicepos(ctx, chn); +} + +/* + * Event injection + */ + +static void inject_event(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct smix_data *smix = &ctx->smix; + int chn; + + for (chn = 0; chn < mod->chn + smix->chn; chn++) { + struct xmp_event *e = &p->inject_event[chn]; + if (e->_flag > 0) { + libxmp_read_event(ctx, e, chn); + e->_flag = 0; + } + } +} + +/* + * Sequencing + */ + +static void next_order(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int reset_gvol = 0; + int mark; + + do { + p->ord++; + + /* Restart module */ + mark = HAS_QUIRK(QUIRK_MARKER) && p->ord < mod->len && mod->xxo[p->ord] == 0xff; + if (p->ord >= mod->len || mark) { + if (mod->rst > mod->len || + mod->xxo[mod->rst] >= mod->pat || + p->ord < m->seq_data[p->sequence].entry_point) { + p->ord = m->seq_data[p->sequence].entry_point; + } else { + if (libxmp_get_sequence(ctx, mod->rst) == p->sequence) { + p->ord = mod->rst; + } else { + p->ord = m->seq_data[p->sequence].entry_point; + } + } + /* This might be a marker, so delay updating global + * volume until an actual pattern is found */ + reset_gvol = 1; + } + } while (mod->xxo[p->ord] >= mod->pat); + + if (reset_gvol) + p->gvol = m->xxo_info[p->ord].gvl; + +#ifndef LIBXMP_CORE_PLAYER + /* Archimedes line jump -- don't reset time tracking. */ + if (f->jump_in_pat != p->ord) +#endif + p->current_time = m->xxo_info[p->ord].time; + + f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows; + if (f->jumpline >= f->num_rows) + f->jumpline = 0; + p->row = f->jumpline; + f->jumpline = 0; + + p->pos = p->ord; + p->frame = 0; + +#ifndef LIBXMP_CORE_PLAYER + f->jump_in_pat = -1; + + /* Reset persistent effects at new pattern */ + if (HAS_QUIRK(QUIRK_PERPAT)) { + int chn; + for (chn = 0; chn < mod->chn; chn++) { + p->xc_data[chn].per_flags = 0; + } + } +#endif +} + +static void next_row(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + + p->frame = 0; + f->delay = 0; + + if (f->pbreak) { + f->pbreak = 0; + + if (f->jump != -1) { + p->ord = f->jump - 1; + f->jump = -1; + } + + next_order(ctx); + } else { + if (f->rowdelay == 0) { + p->row++; + f->rowdelay_set = 0; + } else { + f->rowdelay--; + } + + if (f->loop_chn) { + p->row = f->loop[f->loop_chn - 1].start; + f->loop_chn = 0; + } + + /* check end of pattern */ + if (p->row >= f->num_rows) { + next_order(ctx); + } + } +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +/* + * Set note action for libxmp_virt_pastnote + */ +void libxmp_player_set_release(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + SET_NOTE(NOTE_RELEASE); +} + +void libxmp_player_set_fadeout(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + + SET_NOTE(NOTE_FADEOUT); +} + +#endif + +static void update_from_ord_info(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct ord_data *oinfo = &m->xxo_info[p->ord]; + + if (oinfo->speed) + p->speed = oinfo->speed; + p->bpm = oinfo->bpm; + p->gvol = oinfo->gvl; + p->current_time = oinfo->time; + p->frame_time = m->time_factor * m->rrate / p->bpm; + +#ifndef LIBXMP_CORE_PLAYER + p->st26_speed = oinfo->st26_speed; +#endif +} + +void libxmp_reset_flow(struct context_data *ctx) +{ + struct flow_control *f = &ctx->p.flow; + f->jumpline = 0; + f->jump = -1; + f->pbreak = 0; + f->loop_chn = 0; + f->delay = 0; + f->rowdelay = 0; + f->rowdelay_set = 0; +#ifndef LIBXMP_CORE_PLAYER + f->jump_in_pat = -1; +#endif +} + +int xmp_start_player(xmp_context opaque, int rate, int format) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct flow_control *f = &p->flow; + int i; + int ret = 0; + + if (rate < XMP_MIN_SRATE || rate > XMP_MAX_SRATE) + return -XMP_ERROR_INVALID; + + if (ctx->state < XMP_STATE_LOADED) + return -XMP_ERROR_STATE; + + if (ctx->state > XMP_STATE_LOADED) + xmp_end_player(opaque); + + if (libxmp_mixer_on(ctx, rate, format, m->c4rate) < 0) + return -XMP_ERROR_INTERNAL; + + p->master_vol = 100; + p->smix_vol = 100; + p->gvol = m->volbase; + p->pos = p->ord = 0; + p->frame = -1; + p->row = 0; + p->current_time = 0; + p->loop_count = 0; + p->sequence = 0; + + /* Set default volume and mute status */ + for (i = 0; i < mod->chn; i++) { + if (mod->xxc[i].flg & XMP_CHANNEL_MUTE) + p->channel_mute[i] = 1; + p->channel_vol[i] = 100; + } + for (i = mod->chn; i < XMP_MAX_CHANNELS; i++) { + p->channel_mute[i] = 0; + p->channel_vol[i] = 100; + } + + /* Skip invalid patterns at start (the seventh laboratory.it) */ + while (p->ord < mod->len && mod->xxo[p->ord] >= mod->pat) { + p->ord++; + } + /* Check if all positions skipped */ + if (p->ord >= mod->len) { + mod->len = 0; + } + + if (mod->len == 0) { + /* set variables to sane state */ + /* Note: previously did this for mod->chn == 0, which caused + * crashes on invalid order 0s. 0 channel modules are technically + * valid (if useless) so just let them play normally. */ + p->ord = p->scan[0].ord = 0; + p->row = p->scan[0].row = 0; + f->end_point = 0; + f->num_rows = 0; + } else { + f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows; + f->end_point = p->scan[0].num; + } + + update_from_ord_info(ctx); + + if (libxmp_virt_on(ctx, mod->chn + smix->chn) != 0) { + ret = -XMP_ERROR_INTERNAL; + goto err; + } + + libxmp_reset_flow(ctx); + + f->loop = (struct pattern_loop *) calloc(p->virt.virt_channels, sizeof(struct pattern_loop)); + if (f->loop == NULL) { + ret = -XMP_ERROR_SYSTEM; + goto err; + } + + p->xc_data = (struct channel_data *) calloc(p->virt.virt_channels, sizeof(struct channel_data)); + if (p->xc_data == NULL) { + ret = -XMP_ERROR_SYSTEM; + goto err1; + } + + /* Reset our buffer pointers */ + xmp_play_buffer(opaque, NULL, 0, 0); + +#ifndef LIBXMP_CORE_DISABLE_IT + for (i = 0; i < p->virt.virt_channels; i++) { + struct channel_data *xc = &p->xc_data[i]; + xc->filter.cutoff = 0xff; +#ifndef LIBXMP_CORE_PLAYER + if (libxmp_new_channel_extras(ctx, xc) < 0) + goto err2; +#endif + } +#endif + reset_channels(ctx); + + ctx->state = XMP_STATE_PLAYING; + + return 0; + +#ifndef LIBXMP_CORE_PLAYER + err2: + free(p->xc_data); + p->xc_data = NULL; +#endif + err1: + free(f->loop); + f->loop = NULL; + err: + return ret; +} + +static void check_end_of_module(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; + + /* check end of module */ + if (p->ord == p->scan[p->sequence].ord && + p->row == p->scan[p->sequence].row) { + if (f->end_point == 0) { + p->loop_count++; + f->end_point = p->scan[p->sequence].num; + /* return -1; */ + } + f->end_point--; + } +} + +int xmp_play_frame(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct flow_control *f = &p->flow; + int i; + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + if (mod->len <= 0) { + return -XMP_END; + } + + if (HAS_QUIRK(QUIRK_MARKER) && mod->xxo[p->ord] == 0xff) { + return -XMP_END; + } + + /* check reposition */ + if (p->ord != p->pos) { + int start = m->seq_data[p->sequence].entry_point; + + if (p->pos == -2) { /* set by xmp_module_stop */ + return -XMP_END; /* that's all folks */ + } + + if (p->pos == -1) { + /* restart sequence */ + p->pos = start; + } + + if (p->pos == start) { + f->end_point = p->scan[p->sequence].num; + } + + /* Check if lands after a loop point */ + if (p->pos > p->scan[p->sequence].ord) { + f->end_point = 0; + } + + f->jumpline = 0; + f->jump = -1; + + p->ord = p->pos - 1; + + /* Stay inside our subsong */ + if (p->ord < start) { + p->ord = start - 1; + } + + next_order(ctx); + + update_from_ord_info(ctx); + + libxmp_virt_reset(ctx); + reset_channels(ctx); + } else { + p->frame++; + if (p->frame >= (p->speed * (1 + f->delay))) { + /* If break during pattern delay, next row is skipped. + * See corruption.mod order 1D (pattern 0D) last line: + * EE2 + D31 ignores D00 in order 1C line 31. Reported + * by The Welder , Jan 14 2012 + */ + if (HAS_QUIRK(QUIRK_PROTRACK) && f->delay && f->pbreak) + { + next_row(ctx); + check_end_of_module(ctx); + } + next_row(ctx); + } + } + + for (i = 0; i < mod->chn; i++) { + struct channel_data *xc = &p->xc_data[i]; + RESET(KEY_OFF); + } + + /* check new row */ + + if (p->frame == 0) { /* first frame in row */ + check_end_of_module(ctx); + read_row(ctx, mod->xxo[p->ord], p->row); + +#ifndef LIBXMP_CORE_PLAYER + if (p->st26_speed) { + if (p->st26_speed & 0x10000) { + p->speed = (p->st26_speed & 0xff00) >> 8; + } else { + p->speed = p->st26_speed & 0xff; + } + p->st26_speed ^= 0x10000; + } +#endif + } + + inject_event(ctx); + + /* play_frame */ + for (i = 0; i < p->virt.virt_channels; i++) { + play_channel(ctx, i); + } + + f->rowdelay_set &= ~ROWDELAY_FIRST_FRAME; + + p->frame_time = m->time_factor * m->rrate / p->bpm; + p->current_time += p->frame_time; + + libxmp_mixer_softmixer(ctx); + + return 0; +} + +int xmp_play_buffer(xmp_context opaque, void *out_buffer, int size, int loop) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + int ret = 0, filled = 0, copy_size; + struct xmp_frame_info fi; + + /* Reset internal state + * Syncs buffer start with frame start */ + if (out_buffer == NULL) { + p->loop_count = 0; + p->buffer_data.consumed = 0; + p->buffer_data.in_size = 0; + return 0; + } + + if (ctx->state < XMP_STATE_PLAYING) + return -XMP_ERROR_STATE; + + /* Fill buffer */ + while (filled < size) { + /* Check if buffer full */ + if (p->buffer_data.consumed == p->buffer_data.in_size) { + ret = xmp_play_frame(opaque); + xmp_get_frame_info(opaque, &fi); + + /* Check end of module */ + if (ret < 0 || (loop > 0 && fi.loop_count >= loop)) { + /* Start of frame, return end of replay */ + if (filled == 0) { + p->buffer_data.consumed = 0; + p->buffer_data.in_size = 0; + return -1; + } + + /* Fill remaining of this buffer */ + memset((char *)out_buffer + filled, 0, size - filled); + return 0; + } + + p->buffer_data.consumed = 0; + p->buffer_data.in_buffer = (char *)fi.buffer; + p->buffer_data.in_size = fi.buffer_size; + } + + /* Copy frame data to user buffer */ + copy_size = MIN(size - filled, p->buffer_data.in_size - + p->buffer_data.consumed); + memcpy((char *)out_buffer + filled, p->buffer_data.in_buffer + + p->buffer_data.consumed, copy_size); + p->buffer_data.consumed += copy_size; + filled += copy_size; + } + + return ret; +} + +void xmp_end_player(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct flow_control *f = &p->flow; +#ifndef LIBXMP_CORE_PLAYER + struct channel_data *xc; + int i; +#endif + + if (ctx->state < XMP_STATE_PLAYING) + return; + + ctx->state = XMP_STATE_LOADED; + +#ifndef LIBXMP_CORE_PLAYER + /* Free channel extras */ + for (i = 0; i < p->virt.virt_channels; i++) { + xc = &p->xc_data[i]; + libxmp_release_channel_extras(ctx, xc); + } +#endif + + libxmp_virt_off(ctx); + + free(p->xc_data); + free(f->loop); + + p->xc_data = NULL; + f->loop = NULL; + + libxmp_mixer_off(ctx); +} + +void xmp_get_module_info(xmp_context opaque, struct xmp_module_info *info) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (ctx->state < XMP_STATE_LOADED) + return; + + memcpy(info->md5, m->md5, 16); + info->mod = mod; + info->comment = m->comment; + info->num_sequences = m->num_sequences; + info->seq_data = m->seq_data; + info->vol_base = m->volbase; +} + +void xmp_get_frame_info(xmp_context opaque, struct xmp_frame_info *info) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct mixer_data *s = &ctx->s; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + int chn, i; + + if (ctx->state < XMP_STATE_LOADED) + return; + + chn = mod->chn; + + if (p->pos >= 0 && p->pos < mod->len) { + info->pos = p->pos; + } else { + info->pos = 0; + } + + info->pattern = mod->xxo[info->pos]; + + if (info->pattern < mod->pat) { + info->num_rows = mod->xxp[info->pattern]->rows; + } else { + info->num_rows = 0; + } + + info->row = p->row; + info->frame = p->frame; + info->speed = p->speed; + info->bpm = p->bpm; + info->total_time = p->scan[p->sequence].time; + info->frame_time = p->frame_time * 1000; + info->time = p->current_time; + info->buffer = s->buffer; + + info->total_size = XMP_MAX_FRAMESIZE; + info->buffer_size = s->ticksize; + if (~s->format & XMP_FORMAT_MONO) { + info->buffer_size *= 2; + } + if (~s->format & XMP_FORMAT_8BIT) { + info->buffer_size *= 2; + } + + info->volume = p->gvol; + info->loop_count = p->loop_count; + info->virt_channels = p->virt.virt_channels; + info->virt_used = p->virt.virt_used; + + info->sequence = p->sequence; + + if (p->xc_data != NULL) { + for (i = 0; i < chn; i++) { + struct channel_data *c = &p->xc_data[i]; + struct xmp_channel_info *ci = &info->channel_info[i]; + struct xmp_track *track; + struct xmp_event *event; + int trk; + + ci->note = c->key; + ci->pitchbend = c->info_pitchbend; + ci->period = c->info_period; + ci->position = c->info_position; + ci->instrument = c->ins; + ci->sample = c->smp; + ci->volume = c->info_finalvol >> 4; + ci->pan = c->info_finalpan; + ci->reserved = 0; + memset(&ci->event, 0, sizeof(*event)); + + if (info->pattern < mod->pat && info->row < info->num_rows) { + trk = mod->xxp[info->pattern]->index[i]; + track = mod->xxt[trk]; + if (info->row < track->rows) { + event = &track->event[info->row]; + memcpy(&ci->event, event, sizeof(*event)); + } + } + } + } +} diff --git a/thirdparty/libxmp/src/player.h b/thirdparty/libxmp/src/player.h new file mode 100644 index 000000000..e1fc9ab79 --- /dev/null +++ b/thirdparty/libxmp/src/player.h @@ -0,0 +1,282 @@ +#ifndef LIBXMP_PLAYER_H +#define LIBXMP_PLAYER_H + +#include "lfo.h" + +/* Quirk control */ +#define HAS_QUIRK(x) (m->quirk & (x)) + +/* Channel flag control */ +#define SET(f) SET_FLAG(xc->flags,(f)) +#define RESET(f) RESET_FLAG(xc->flags,(f)) +#define TEST(f) TEST_FLAG(xc->flags,(f)) + +/* Persistent effect flag control */ +#define SET_PER(f) SET_FLAG(xc->per_flags,(f)) +#define RESET_PER(f) RESET_FLAG(xc->per_flags,(f)) +#define TEST_PER(f) TEST_FLAG(xc->per_flags,(f)) + +/* Note flag control */ +#define SET_NOTE(f) SET_FLAG(xc->note_flags,(f)) +#define RESET_NOTE(f) RESET_FLAG(xc->note_flags,(f)) +#define TEST_NOTE(f) TEST_FLAG(xc->note_flags,(f)) + +struct retrig_control { + int s; + int m; + int d; +}; + +/* The following macros are used to set the flags for each channel */ +#define VOL_SLIDE (1 << 0) +#define PAN_SLIDE (1 << 1) +#define TONEPORTA (1 << 2) +#define PITCHBEND (1 << 3) +#define VIBRATO (1 << 4) +#define TREMOLO (1 << 5) +#define FINE_VOLS (1 << 6) +#define FINE_BEND (1 << 7) +#define OFFSET (1 << 8) +#define TRK_VSLIDE (1 << 9) +#define TRK_FVSLIDE (1 << 10) +#define NEW_INS (1 << 11) +#define NEW_VOL (1 << 12) +#define VOL_SLIDE_2 (1 << 13) +#define NOTE_SLIDE (1 << 14) +#define FINE_NSLIDE (1 << 15) +#define NEW_NOTE (1 << 16) +#define FINE_TPORTA (1 << 17) +#define RETRIG (1 << 18) +#define PANBRELLO (1 << 19) +#define GVOL_SLIDE (1 << 20) +#define TEMPO_SLIDE (1 << 21) +#define VENV_PAUSE (1 << 22) +#define PENV_PAUSE (1 << 23) +#define FENV_PAUSE (1 << 24) +#define FINE_VOLS_2 (1 << 25) +#define KEY_OFF (1 << 26) /* for IT release on envloop end */ +#define TREMOR (1 << 27) /* for XM tremor */ +#define MIDI_MACRO (1 << 28) /* IT midi macro */ + +#define NOTE_FADEOUT (1 << 0) +#define NOTE_ENV_RELEASE (1 << 1) /* envelope sustain loop release */ +#define NOTE_END (1 << 2) +#define NOTE_CUT (1 << 3) +#define NOTE_ENV_END (1 << 4) +#define NOTE_SAMPLE_END (1 << 5) +#define NOTE_SET (1 << 6) /* for IT portamento after keyoff */ +#define NOTE_SUSEXIT (1 << 7) /* for delayed envelope release */ +#define NOTE_KEY_CUT (1 << 8) /* note cut with XMP_KEY_CUT event */ +#define NOTE_GLISSANDO (1 << 9) +#define NOTE_SAMPLE_RELEASE (1 << 10) /* sample sustain loop release */ + +/* Most of the time, these should be set/reset together. */ +#define NOTE_RELEASE (NOTE_ENV_RELEASE | NOTE_SAMPLE_RELEASE) + +/* Note: checking the data pointer for samples should be good enough to filter + * broken samples, since libxmp_load_sample will always allocate it for valid + * samples of >0 length and bound the loop values for these samples. */ +#define IS_VALID_INSTRUMENT(x) ((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0) +#define IS_VALID_INSTRUMENT_OR_SFX(x) (((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0) || (smix->ins > 0 && (uint32)(x) < mod->ins + smix->ins)) +#define IS_VALID_SAMPLE(x) ((uint32)(x) < mod->smp && mod->xxs[(x)].data != NULL) +#define IS_VALID_NOTE(x) ((uint32)(x) < XMP_MAX_KEYS) + +struct instrument_vibrato { + int phase; + int sweep; +}; + +struct channel_data { + int flags; /* Channel flags */ + int per_flags; /* Persistent effect channel flags */ + int note_flags; /* Note release, fadeout or end */ + int note; /* Note number */ + int key; /* Key number */ + double period; /* Amiga or linear period */ + double per_adj; /* MED period/pitch adjustment factor hack */ + int finetune; /* Guess what */ + int ins; /* Instrument number */ + int old_ins; /* Last instruemnt */ + int smp; /* Sample number */ + int mastervol; /* Master vol -- for IT track vol effect */ + int delay; /* Note delay in frames */ + int keyoff; /* Key off counter */ + int fadeout; /* Current fadeout (release) value */ + int ins_fade; /* Instrument fadeout value */ + int volume; /* Current volume */ + int gvl; /* Global volume for instrument for IT */ + + int rvv; /* Random volume variation */ + int rpv; /* Random pan variation */ + + uint8 split; /* Split channel */ + uint8 pair; /* Split channel pair */ + + int v_idx; /* Volume envelope index */ + int p_idx; /* Pan envelope index */ + int f_idx; /* Freq envelope index */ + + int key_porta; /* Key number for portamento target + * -- needed to handle IT portamento xpo */ + struct { + struct lfo lfo; + int memory; + } vibrato; + + struct { + struct lfo lfo; + int memory; + } tremolo; + +#ifndef LIBXMP_CORE_DISABLE_IT + struct { + struct lfo lfo; + int memory; + } panbrello; +#endif + + struct { + int8 val[16]; /* 16 for Smaksak MegaArps */ + int size; + int count; + int memory; + } arpeggio; + + struct { + struct lfo lfo; + int sweep; + } insvib; + + struct { + int val; + int val2; /* For fx9 bug emulation */ + int memory; + } offset; + + struct { + int val; /* Retrig value */ + int count; /* Retrig counter */ + int type; /* Retrig type */ + int limit; /* Number of retrigs */ + } retrig; + + struct { + uint8 up,down; /* Tremor value */ + uint8 count; /* Tremor counter */ + uint8 memory; /* Tremor memory */ + } tremor; + + struct { + int slide; /* Volume slide value */ + int fslide; /* Fine volume slide value */ + int slide2; /* Volume slide value */ + int memory; /* Volume slide effect memory */ +#ifndef LIBXMP_CORE_DISABLE_IT + int fslide2; + int memory2; /* Volume slide effect memory */ +#endif +#ifndef LIBXMP_CORE_PLAYER + int target; /* Target for persistent volslide */ +#endif + } vol; + + struct { + int up_memory; /* Fine volume slide up memory (XM) */ + int down_memory;/* Fine volume slide up memory (XM) */ + } fine_vol; + + struct { + int slide; /* Global volume slide value */ + int fslide; /* Fine global volume slide value */ + int memory; /* Global volume memory is saved per channel */ + } gvol; + + struct { + int slide; /* Track volume slide value */ + int fslide; /* Track fine volume slide value */ + int memory; /* Track volume slide effect memory */ + } trackvol; + + struct { + int slide; /* Frequency slide value */ + double fslide; /* Fine frequency slide value */ + int memory; /* Portamento effect memory */ + } freq; + + struct { + double target; /* Target period for tone portamento */ + int dir; /* Tone portamento up/down directionh */ + int slide; /* Delta for tone portamento */ + int memory; /* Tone portamento effect memory */ + int note_memory;/* Tone portamento note memory (ULT) */ + } porta; + + struct { + int up_memory; /* FT2 has separate memories for these */ + int down_memory;/* cases (see Porta-LinkMem.xm) */ + } fine_porta; + + struct { + int val; /* Current pan value */ + int slide; /* Pan slide value */ + int fslide; /* Pan fine slide value */ + int memory; /* Pan slide effect memory */ + int surround; /* Surround channel flag */ + } pan; + + struct { + int speed; + int count; + int pos; + } invloop; + +#ifndef LIBXMP_CORE_DISABLE_IT + struct { + int slide; /* IT tempo slide */ + } tempo; + + struct { + int cutoff; /* IT filter cutoff frequency */ + int resonance; /* IT filter resonance */ + int envelope; /* IT filter envelope */ + int can_disable;/* IT hack: allow disabling for cutoff 127 */ + } filter; + + struct { + float val; /* Current macro effect (use float for slides) */ + float target; /* Current macro target (smooth macro) */ + float slide; /* Current macro slide (smooth macro) */ + int active; /* Current active parameterized macro */ + int finalvol; /* Previous tick calculated volume (0-0x400) */ + int notepan; /* Previous tick note panning (0x80 center) */ + } macro; +#endif + +#ifndef LIBXMP_CORE_PLAYER + struct { + int slide; /* PTM note slide amount */ + int fslide; /* OKT fine note slide amount */ + int speed; /* PTM note slide speed */ + int count; /* PTM note slide counter */ + } noteslide; + + void *extra; +#endif + + struct xmp_event delayed_event; + int delayed_ins; /* IT save instrument emulation */ + + int info_period; /* Period */ + int info_pitchbend; /* Linear pitchbend */ + int info_position; /* Position before mixing */ + int info_finalvol; /* Final volume including envelopes */ + int info_finalpan; /* Final pan including envelopes */ +}; + + +void libxmp_process_fx (struct context_data *, struct channel_data *, + int, struct xmp_event *, int); +void libxmp_filter_setup (int, int, int, int*, int*, int *); +int libxmp_read_event (struct context_data *, struct xmp_event *, int); + +#endif /* LIBXMP_PLAYER_H */ diff --git a/thirdparty/libxmp/src/precomp_blep.h b/thirdparty/libxmp/src/precomp_blep.h new file mode 100644 index 000000000..ec38073da --- /dev/null +++ b/thirdparty/libxmp/src/precomp_blep.h @@ -0,0 +1,256 @@ + /* + * Table generated by compute-blep.py (a1200 and vanilla tables removed) + */ + +/* tables are: a500 off, a500 on */ +const int winsinc_integral[2][2048] = { + { +131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072, +131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131071,131071, +131071,131071,131071,131071,131071,131071,131071,131071,131071,131070,131070,131070, +131070,131070,131069,131069,131069,131068,131068,131068,131067,131067,131066,131066, +131065,131065,131064,131063,131063,131062,131061,131060,131059,131058,131056,131055, +131054,131052,131050,131049,131047,131045,131043,131040,131038,131035,131033,131030, +131026,131023,131020,131016,131012,131008,131003,130998,130993,130988,130982,130976, +130970,130963,130956,130949,130941,130932,130924,130914,130905,130895,130884,130872, +130861,130848,130835,130821,130807,130792,130776,130759,130742,130724,130705,130685, +130664,130642,130620,130596,130571,130545,130518,130490,130461,130430,130398,130365, +130331,130295,130257,130219,130178,130136,130093,130047,130000,129951,129901,129848, +129794,129737,129679,129618,129555,129490,129423,129353,129281,129207,129130,129050, +128968,128883,128795,128704,128611,128514,128415,128312,128206,128097,127985,127869, +127750,127627,127501,127371,127237,127100,126959,126813,126664,126510,126353,126191, +126025,125854,125679,125499,125315,125126,124933,124734,124531,124323,124110,123891, +123668,123439,123205,122965,122720,122470,122214,121952,121685,121412,121133,120849, +120558,120261,119959,119650,119335,119014,118687,118354,118014,117668,117315,116956, +116591,116219,115840,115455,115063,114665,114260,113849,113430,113005,112574,112135, +111690,111239,110780,110315,109843,109364,108879,108387,107888,107383,106871,106352, +105827,105295,104757,104212,103661,103104,102540,101970,101394,100812,100223,99629, +99028,98422,97810,97192,96568,95939,95305,94665,94020,93370,92714,92054,91389,90719, +90045,89366,88682,87995,87303,86607,85908,85205,84498,83788,83075,82358,81639,80916, +80191,79464,78734,78002,77268,76533,75795,75056,74316,73575,72833,72090,71346,70602, +69858,69114,68370,67626,66883,66140,65399,64658,63919,63181,62445,61711,60979,60249, +59521,58796,58074,57355,56639,55926,55217,54512,53810,53113,52419,51731,51046,50367, +49693,49023,48359,47701,47048,46400,45759,45124,44495,43872,43256,42646,42043,41447, +40858,40276,39702,39134,38575,38023,37478,36941,36413,35892,35379,34874,34378,33890, +33410,32938,32475,32020,31574,31137,30708,30288,29876,29473,29079,28693,28317,27948, +27589,27238,26896,26562,26238,25921,25613,25314,25023,24740,24466,24200,23942,23692, +23451,23217,22991,22773,22562,22359,22164,21975,21794,21621,21454,21294,21140,20994, +20853,20719,20592,20470,20354,20244,20139,20040,19946,19857,19774,19694,19620,19550, +19484,19422,19364,19310,19260,19213,19169,19128,19090,19054,19022,18991,18963,18936, +18912,18889,18867,18847,18828,18810,18792,18776,18759,18743,18727,18711,18695,18679, +18662,18644,18626,18607,18587,18565,18542,18518,18492,18465,18436,18404,18371,18336, +18298,18259,18216,18172,18124,18074,18022,17966,17908,17847,17783,17716,17646,17572, +17496,17416,17334,17248,17159,17066,16971,16872,16770,16664,16556,16444,16329,16211, +16090,15966,15839,15709,15576,15440,15301,15159,15015,14868,14718,14566,14412,14255, +14096,13935,13771,13606,13439,13270,13099,12927,12753,12578,12401,12224,12045,11866, +11685,11504,11322,11140,10958,10775,10592,10409,10226,10044,9862,9680,9499,9319,9139, +8961,8783,8607,8432,8258,8086,7915,7747,7580,7415,7252,7091,6932,6776,6622,6471, +6322,6176,6032,5892,5754,5619,5488,5359,5234,5111,4992,4877,4764,4655,4550,4448, +4349,4254,4163,4075,3990,3910,3832,3759,3689,3622,3560,3500,3445,3393,3344,3299, +3257,3219,3184,3153,3124,3099,3078,3059,3044,3031,3022,3015,3011,3010,3012,3016, +3023,3033,3044,3058,3075,3093,3113,3136,3160,3186,3213,3242,3273,3305,3338,3372, +3408,3444,3481,3520,3558,3597,3637,3677,3718,3758,3799,3839,3880,3920,3960,4000, +4039,4077,4115,4152,4188,4224,4258,4291,4323,4354,4384,4412,4439,4464,4488,4510, +4530,4549,4566,4581,4594,4606,4615,4623,4628,4631,4633,4632,4629,4624,4617,4608, +4597,4583,4568,4550,4530,4508,4484,4458,4429,4399,4366,4332,4296,4257,4217,4175, +4130,4085,4037,3988,3937,3884,3830,3774,3717,3658,3598,3537,3475,3411,3347,3281, +3215,3147,3079,3010,2940,2870,2799,2728,2657,2585,2513,2440,2368,2296,2224,2151, +2080,2008,1937,1866,1796,1726,1657,1589,1521,1454,1389,1324,1260,1197,1135,1075, +1016,958,901,846,792,740,689,640,592,546,502,459,419,379,342,307,273,241,211,183, +156,132,109,88,69,52,37,24,12,2,-5,-11,-16,-18,-19,-18,-16,-11,-6,2,11,21,33,47,61, +77,95,113,133,154,176,200,224,249,275,302,329,358,387,416,447,477,508,540,572,604, +636,669,702,734,767,800,832,864,896,928,960,991,1021,1051,1081,1110,1138,1166,1193, +1219,1245,1270,1293,1316,1338,1359,1379,1398,1416,1433,1448,1463,1476,1488,1499, +1509,1518,1525,1531,1536,1540,1542,1543,1543,1542,1539,1536,1530,1524,1517,1508, +1498,1487,1475,1462,1447,1432,1415,1397,1379,1359,1338,1317,1294,1271,1247,1222, +1196,1170,1143,1115,1086,1057,1028,998,967,936,905,874,842,809,777,744,712,679,646, +613,581,548,515,483,450,418,387,355,324,293,263,233,204,175,147,119,92,66,40,15, +-10,-33,-56,-78,-99,-120,-139,-158,-176,-193,-209,-224,-238,-252,-264,-275,-286, +-295,-304,-311,-318,-324,-329,-332,-335,-337,-338,-338,-338,-336,-333,-330,-326, +-321,-315,-308,-301,-293,-284,-274,-264,-253,-242,-229,-217,-203,-190,-175,-161, +-145,-130,-114,-98,-81,-64,-47,-29,-11,6,24,42,61,79,97,115,134,152,170,188,206,223, +241,258,275,291,308,324,340,355,370,384,399,412,425,438,450,462,473,484,494,503, +512,521,529,536,542,548,553,558,562,566,568,570,572,573,573,573,572,570,568,565, +562,558,553,548,543,537,530,523,515,507,498,489,479,469,459,448,437,426,414,402, +389,377,364,351,337,324,310,296,282,268,254,239,225,211,196,182,168,153,139,125, +111,97,83,70,56,43,30,17,5,-7,-19,-31,-42,-53,-64,-75,-85,-94,-104,-113,-121,-129, +-137,-144,-151,-158,-164,-170,-175,-180,-184,-188,-192,-195,-198,-200,-202,-203, +-204,-205,-205,-204,-204,-203,-201,-199,-197,-195,-192,-188,-185,-181,-176,-172, +-167,-162,-156,-151,-145,-139,-132,-126,-119,-112,-104,-97,-90,-82,-74,-66,-59,-51, +-42,-34,-26,-18,-10,-2,7,15,23,31,39,47,54,62,70,77,85,92,99,106,112,119,125,131, +137,143,148,154,159,163,168,172,176,180,183,187,190,192,195,197,199,201,202,203, +204,205,205,205,205,204,203,202,201,200,198,196,194,192,189,186,183,180,177,173, +169,165,161,157,153,148,143,139,134,129,124,119,113,108,103,97,92,86,81,75,70,64, +59,53,48,42,37,32,26,21,16,11,6,1,-4,-9,-13,-18,-22,-26,-30,-34,-38,-42,-46,-49, +-52,-55,-58,-61,-64,-66,-69,-71,-73,-74,-76,-78,-79,-80,-81,-82,-82,-83,-83,-83, +-83,-83,-83,-82,-82,-81,-80,-79,-78,-77,-75,-74,-72,-70,-68,-66,-64,-62,-60,-57, +-55,-52,-50,-47,-44,-42,-39,-36,-33,-30,-27,-24,-21,-18,-15,-12,-9,-6,-3,0,3,6,8, +11,14,17,20,22,25,27,30,32,35,37,39,41,43,45,47,49,50,52,54,55,56,58,59,60,61,61, +62,63,63,64,64,65,65,65,65,65,64,64,64,63,63,62,61,61,60,59,58,57,56,55,53,52,51, +49,48,46,45,43,41,40,38,36,35,33,31,29,28,26,24,22,20,19,17,15,13,11,10,8,6,5,3,1, +0,-2,-3,-5,-6,-8,-9,-10,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-23, +-24,-24,-25,-25,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-24, +-24,-23,-23,-22,-21,-21,-20,-19,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-10,-9, +-8,-7,-6,-5,-4,-3,-2,-1,0,1,1,2,3,4,5,6,6,7,8,9,9,10,11,11,12,12,13,14,14,15,15, +15,16,16,16,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,17,17,17,17,16,16, +16,16,15,15,14,14,14,13,13,12,12,11,11,11,10,10,9,9,8,8,7,7,6,6,5,5,4,4,3,3,2,2,1, +1,0,0,-1,-1,-1,-2,-2,-3,-3,-3,-4,-4,-4,-4,-5,-5,-5,-5,-6,-6,-6,-6,-6,-6,-6,-7,-7, +-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-6,-6,-6,-6,-6,-6,-6,-6,-5,-5,-5,-5,-5,-4,-4,-4, +-4,-4,-3,-3,-3,-3,-2,-2,-2,-2,-1,-1,-1,-1,-1,0,0,0,0,1,1,1,1,1,2,2,2,2,2,2,3,3,3, +3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3, +3,3,3,3,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + }, + { +131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072, +131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072, +131072,131072,131072,131072,131072,131072,131072,131072,131072,131071,131071,131071, +131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071, +131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071, +131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071, +131071,131071,131071,131071,131071,131071,131070,131070,131070,131070,131070,131070, +131070,131070,131070,131070,131070,131070,131070,131070,131070,131070,131069,131069, +131069,131069,131069,131069,131069,131069,131068,131068,131068,131068,131068,131068, +131067,131067,131067,131067,131066,131066,131066,131066,131065,131065,131064,131064, +131064,131063,131063,131062,131062,131061,131061,131060,131060,131059,131058,131058, +131057,131056,131055,131055,131054,131053,131052,131051,131050,131049,131047,131046, +131045,131044,131042,131041,131039,131038,131036,131034,131032,131031,131029,131027, +131024,131022,131020,131017,131015,131012,131009,131007,131004,131001,130997,130994, +130991,130987,130983,130979,130975,130971,130967,130962,130958,130953,130948,130942, +130937,130931,130926,130920,130913,130907,130900,130893,130886,130879,130871,130863, +130855,130847,130838,130829,130820,130810,130800,130790,130779,130768,130757,130745, +130733,130721,130708,130695,130682,130668,130654,130639,130624,130608,130592,130576, +130559,130541,130523,130505,130486,130466,130446,130426,130405,130383,130361,130338, +130314,130290,130265,130240,130214,130187,130160,130132,130103,130074,130044,130013, +129981,129949,129916,129882,129847,129812,129775,129738,129700,129661,129621,129581, +129539,129497,129453,129409,129364,129318,129270,129222,129173,129123,129071,129019, +128966,128911,128856,128799,128742,128683,128623,128562,128500,128436,128372,128306, +128239,128171,128101,128031,127959,127886,127811,127736,127659,127580,127501,127420, +127337,127254,127169,127082,126995,126906,126815,126723,126630,126535,126439,126341, +126242,126142,126040,125936,125831,125725,125617,125507,125396,125284,125170,125055, +124938,124819,124699,124577,124454,124329,124203,124075,123946,123815,123683,123549, +123413,123276,123137,122997,122855,122711,122566,122420,122272,122122,121971,121818, +121663,121507,121350,121191,121030,120868,120704,120539,120372,120204,120034,119863, +119690,119515,119339,119162,118983,118803,118621,118438,118253,118067,117879,117690, +117500,117308,117114,116920,116724,116526,116327,116127,115926,115723,115519,115313, +115106,114898,114689,114478,114266,114053,113839,113624,113407,113189,112970,112750, +112528,112306,112082,111858,111632,111405,111177,110948,110718,110487,110255,110022, +109788,109553,109317,109080,108842,108604,108364,108124,107883,107641,107398,107154, +106909,106664,106418,106171,105923,105675,105426,105176,104926,104675,104423,104171, +103918,103664,103410,103155,102899,102643,102387,102130,101872,101614,101356,101096, +100837,100577,100316,100055,99794,99532,99270,99007,98745,98481,98217,97953,97689, +97424,97159,96894,96628,96362,96096,95829,95563,95296,95028,94761,94493,94225,93957, +93688,93419,93151,92881,92612,92343,92073,91803,91534,91263,90993,90723,90452,90182, +89911,89640,89369,89098,88827,88556,88284,88013,87741,87470,87198,86926,86654,86382, +86110,85838,85566,85294,85022,84750,84477,84205,83933,83660,83388,83116,82843,82571, +82298,82026,81753,81481,81208,80936,80663,80391,80118,79846,79573,79301,79029,78756, +78484,78212,77939,77667,77395,77123,76851,76579,76307,76035,75763,75491,75220,74948, +74676,74405,74134,73862,73591,73320,73049,72778,72507,72237,71966,71696,71426,71156, +70886,70616,70346,70077,69807,69538,69269,69000,68732,68463,68195,67927,67659,67391, +67124,66857,66590,66323,66056,65790,65524,65258,64993,64728,64463,64198,63934,63670, +63406,63143,62880,62617,62354,62092,61830,61569,61308,61047,60787,60527,60267,60008, +59749,59491,59233,58975,58718,58461,58205,57949,57694,57439,57184,56930,56676,56423, +56171,55918,55667,55416,55165,54915,54665,54416,54167,53919,53671,53424,53178,52932, +52686,52442,52197,51954,51710,51468,51226,50984,50743,50503,50263,50024,49786,49548, +49310,49074,48838,48602,48367,48133,47899,47666,47434,47202,46971,46740,46510,46281, +46052,45824,45597,45370,45144,44918,44693,44469,44245,44022,43800,43578,43357,43137, +42917,42698,42479,42261,42044,41827,41612,41396,41181,40967,40754,40541,40329,40118, +39907,39696,39487,39278,39069,38862,38655,38448,38242,38037,37832,37628,37425,37222, +37020,36819,36618,36417,36218,36019,35820,35622,35425,35228,35032,34837,34642,34448, +34254,34061,33869,33677,33486,33295,33105,32916,32727,32539,32352,32164,31978,31792, +31607,31422,31238,31055,30872,30690,30508,30327,30147,29967,29787,29609,29431,29253, +29076,28900,28724,28549,28374,28200,28027,27854,27682,27510,27339,27169,26999,26830, +26661,26493,26326,26159,25993,25827,25662,25498,25334,25171,25008,24846,24685,24524, +24364,24204,24045,23887,23729,23572,23416,23260,23104,22950,22796,22642,22490,22337, +22186,22035,21884,21735,21586,21437,21289,21142,20996,20850,20704,20560,20416,20272, +20129,19987,19845,19705,19564,19425,19285,19147,19009,18872,18735,18599,18464,18329, +18195,18062,17929,17797,17665,17534,17404,17274,17145,17017,16889,16762,16635,16509, +16384,16259,16135,16011,15888,15766,15644,15523,15403,15283,15163,15044,14926,14809, +14692,14575,14460,14344,14230,14116,14002,13889,13777,13665,13554,13443,13333,13224, +13115,13007,12899,12792,12685,12579,12473,12368,12264,12160,12056,11953,11851,11749, +11648,11547,11447,11347,11248,11149,11051,10953,10856,10759,10663,10568,10472,10378, +10284,10190,10097,10004,9912,9820,9729,9638,9548,9458,9369,9280,9191,9104,9016,8929, +8843,8757,8671,8586,8501,8417,8333,8250,8167,8085,8003,7921,7840,7759,7679,7599, +7520,7441,7363,7285,7207,7130,7053,6977,6901,6826,6751,6676,6602,6528,6455,6382, +6310,6237,6166,6095,6024,5954,5884,5814,5745,5676,5608,5540,5473,5405,5339,5273, +5207,5141,5076,5012,4947,4884,4820,4757,4694,4632,4570,4509,4448,4387,4327,4267, +4208,4149,4090,4032,3974,3917,3859,3803,3746,3691,3635,3580,3525,3471,3417,3363, +3310,3257,3204,3152,3100,3049,2998,2947,2897,2847,2797,2748,2699,2651,2603,2555, +2507,2460,2414,2367,2321,2276,2230,2185,2141,2096,2052,2009,1966,1923,1880,1838, +1796,1754,1713,1672,1631,1591,1551,1511,1472,1432,1394,1355,1317,1279,1242,1204, +1167,1131,1094,1058,1023,987,952,917,882,848,814,780,747,713,680,648,615,583,551, +519,488,457,426,396,365,335,305,276,246,217,188,160,132,103,76,48,21,-7,-34,-60, +-87,-113,-139,-165,-190,-216,-241,-266,-290,-315,-339,-363,-387,-410,-434,-457, +-480,-502,-525,-547,-569,-591,-613,-634,-656,-677,-697,-718,-739,-759,-779,-799, +-818,-838,-857,-876,-895,-914,-932,-951,-969,-987,-1005,-1022,-1040,-1057,-1074, +-1091,-1107,-1124,-1140,-1156,-1172,-1188,-1203,-1219,-1234,-1249,-1264,-1279,-1293, +-1308,-1322,-1336,-1350,-1363,-1377,-1390,-1403,-1416,-1429,-1442,-1454,-1467,-1479, +-1491,-1503,-1514,-1526,-1537,-1548,-1559,-1570,-1581,-1592,-1602,-1613,-1623,-1633, +-1643,-1652,-1662,-1671,-1680,-1690,-1699,-1707,-1716,-1725,-1733,-1741,-1750,-1758, +-1765,-1773,-1781,-1788,-1796,-1803,-1810,-1817,-1824,-1830,-1837,-1843,-1850,-1856, +-1862,-1868,-1874,-1880,-1885,-1891,-1896,-1902,-1907,-1912,-1917,-1922,-1926,-1931, +-1935,-1940,-1944,-1948,-1952,-1956,-1960,-1964,-1968,-1971,-1975,-1978,-1982,-1985, +-1988,-1991,-1994,-1997,-2000,-2002,-2005,-2007,-2010,-2012,-2014,-2017,-2019,-2021, +-2022,-2024,-2026,-2028,-2029,-2031,-2032,-2034,-2035,-2036,-2037,-2038,-2039,-2040, +-2041,-2042,-2042,-2043,-2044,-2044,-2045,-2045,-2045,-2045,-2046,-2046,-2046,-2046, +-2046,-2045,-2045,-2045,-2044,-2044,-2044,-2043,-2042,-2042,-2041,-2040,-2039,-2039, +-2038,-2037,-2035,-2034,-2033,-2032,-2031,-2029,-2028,-2026,-2025,-2023,-2022,-2020, +-2018,-2017,-2015,-2013,-2011,-2009,-2007,-2005,-2003,-2001,-1999,-1996,-1994,-1992, +-1989,-1987,-1984,-1982,-1979,-1977,-1974,-1971,-1969,-1966,-1963,-1960,-1957,-1954, +-1951,-1948,-1945,-1942,-1939,-1936,-1933,-1929,-1926,-1923,-1919,-1916,-1913,-1909, +-1906,-1902,-1899,-1895,-1891,-1888,-1884,-1880,-1877,-1873,-1869,-1865,-1861,-1857, +-1853,-1849,-1845,-1841,-1837,-1833,-1829,-1825,-1821,-1817,-1813,-1809,-1804,-1800, +-1796,-1791,-1787,-1783,-1778,-1774,-1770,-1765,-1761,-1756,-1752,-1747,-1743,-1738, +-1734,-1729,-1725,-1720,-1716,-1711,-1706,-1702,-1697,-1692,-1688,-1683,-1678,-1673, +-1669,-1664,-1659,-1654,-1650,-1645,-1640,-1635,-1630,-1626,-1621,-1616,-1611,-1606, +-1601,-1596,-1591,-1586,-1582,-1577,-1572,-1567,-1562,-1557,-1552,-1547,-1542,-1537, +-1532,-1527,-1522,-1517,-1512,-1507,-1502,-1497,-1492,-1487,-1482,-1477,-1472,-1467, +-1462,-1457,-1452,-1447,-1442,-1437,-1432,-1427,-1422,-1417,-1412,-1407,-1402,-1397, +-1392,-1386,-1381,-1376,-1371,-1366,-1361,-1356,-1351,-1346,-1341,-1336,-1331,-1326, +-1321,-1316,-1311,-1306,-1301,-1296,-1291,-1286,-1281,-1276,-1271,-1266,-1261,-1256, +-1251,-1246,-1241,-1236,-1231,-1226,-1221,-1216,-1211,-1206,-1201,-1196,-1191,-1186, +-1181,-1176,-1171,-1166,-1161,-1156,-1152,-1147,-1142,-1137,-1132,-1127,-1122,-1117, +-1112,-1108,-1103,-1098,-1093,-1088,-1083,-1079,-1074,-1069,-1064,-1060,-1055,-1050, +-1045,-1040,-1036,-1031,-1026,-1022,-1017,-1012,-1008,-1003,-998,-994,-989,-984,-980, +-975,-970,-966,-961,-957,-952,-947,-943,-938,-934,-929,-925,-920,-916,-911,-907, +-902,-898,-894,-889,-885,-880,-876,-872,-867,-863,-859,-854,-850,-846,-841,-837, +-833,-828,-824,-820,-816,-812,-807,-803,-799,-795,-791,-787,-782,-778,-774,-770, +-766,-762,-758,-754,-750,-746,-742,-738,-734,-730,-726,-722,-718,-714,-710,-706, +-702,-699,-695,-691,-687,-683,-679,-676,-672,-668,-664,-661,-657,-653,-649,-646, +-642,-638,-635,-631,-627,-624,-620,-617,-613,-609,-606,-602,-599,-595,-592,-588, +-585,-581,-578,-574,-571,-568,-564,-561,-557,-554,-551,-547,-544,-541,-537,-534, +-531,-528,-524,-521,-518,-515,-511,-508,-505,-502,-499,-496,-492,-489,-486,-483, +-480,-477,-474,-471,-468,-465,-462,-459,-456,-453,-450,-447,-444,-441,-438,-435, +-433,-430,-427,-424,-421,-418,-416,-413,-410,-407,-405,-402,-399,-396,-394,-391, +-388,-386,-383,-380,-378,-375,-373,-370,-367,-365,-362,-360,-357,-355,-352,-350, +-347,-345,-342,-340,-337,-335,-333,-330,-328,-325,-323,-321,-318,-316,-314,-311, +-309,-307,-305,-302,-300,-298,-296,-293,-291,-289,-287,-285,-282,-280,-278,-276, +-274,-272,-270,-268,-266,-264,-261,-259,-257,-255,-253,-251,-249,-247,-246,-244, +-242,-240,-238,-236,-234,-232,-230,-228,-226,-225,-223,-221,-219,-217,-216,-214, +-212,-210,-209,-207,-205,-203,-202,-200,-198,-197,-195,-193,-192,-190,-188,-187, +-185,-183,-182,-180,-179,-177,-176,-174,-172,-171,-169,-168,-166,-165,-163,-162, +-161,-159,-158,-156,-155,-153,-152,-151,-149,-148,-146,-145,-144,-142,-141,-140, +-138,-137,-136,-134,-133,-132,-131,-129,-128,-127,-126,-124,-123,-122,-121,-120, +-118,-117,-116,-115,-114,-113,-111,-110,-109,-108,-107,-106,-105,-104,-103,-102, +-100,-99,-98,-97,-96,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-85,-84,-83,-82, +-81,-80,-79,-78,-77,-76,-75,-75,-74,-73,-72,-71,-70,-70,-69,-68,-67,-66,-66,-65, +-64,-63,-62,-62,-61,-60,-59,-59,-58,-57,-56,-56,-55,-54,-54,-53,-52,-52,-51,-50, +-50,-49,-48,-48,-47,-46,-46,-45,-45,-44,-43,-43,-42,-42,-41,-40,-40,-39,-39,-38, +-38,-37,-36,-36,-35,-35,-34,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-29,-29,-28, +-28,-27,-27,-26,-26,-25,-25,-25,-24,-24,-23,-23,-23,-22,-22,-21,-21,-21,-20,-20, +-20,-19,-19,-18,-18,-18,-17,-17,-17,-16,-16,-16,-15,-15,-15,-15,-14,-14,-14,-13, +-13,-13,-13,-12,-12,-12,-11,-11,-11,-11,-10,-10,-10,-10,-9,-9,-9,-9,-9,-8,-8,-8,-8, +-7,-7,-7,-7,-7,-6,-6,-6,-6,-6,-6,-5,-5,-5,-5,-5,-5,-4,-4,-4,-4,-4,-4,-4,-3,-3,-3, +-3,-3,-3,-3,-3,-2,-2,-2,-2,-2,-2,-2,-2,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +0,0,0,0,0,0,0,0,0, + } +}; diff --git a/thirdparty/libxmp/src/precomp_lut.h b/thirdparty/libxmp/src/precomp_lut.h new file mode 100644 index 000000000..67146725d --- /dev/null +++ b/thirdparty/libxmp/src/precomp_lut.h @@ -0,0 +1,524 @@ +static int16 cubic_spline_lut0[1024] = { + 0, -8, -16, -24, -32, -40, -47, -55, + -63, -71, -78, -86, -94, -101, -109, -117, + -124, -132, -139, -146, -154, -161, -169, -176, + -183, -190, -198, -205, -212, -219, -226, -233, + -240, -247, -254, -261, -268, -275, -282, -289, + -295, -302, -309, -316, -322, -329, -336, -342, + -349, -355, -362, -368, -375, -381, -388, -394, + -400, -407, -413, -419, -425, -432, -438, -444, + -450, -456, -462, -468, -474, -480, -486, -492, + -498, -504, -510, -515, -521, -527, -533, -538, + -544, -550, -555, -561, -566, -572, -577, -583, + -588, -594, -599, -604, -610, -615, -620, -626, + -631, -636, -641, -646, -651, -656, -662, -667, + -672, -677, -682, -686, -691, -696, -701, -706, + -711, -715, -720, -725, -730, -734, -739, -744, + -748, -753, -757, -762, -766, -771, -775, -780, + -784, -788, -793, -797, -801, -806, -810, -814, + -818, -822, -826, -831, -835, -839, -843, -847, + -851, -855, -859, -863, -866, -870, -874, -878, + -882, -886, -889, -893, -897, -900, -904, -908, + -911, -915, -918, -922, -925, -929, -932, -936, + -939, -943, -946, -949, -953, -956, -959, -962, + -966, -969, -972, -975, -978, -981, -984, -987, + -991, -994, -997, -999, -1002, -1005, -1008, -1011, + -1014, -1017, -1020, -1022, -1025, -1028, -1031, -1033, + -1036, -1039, -1041, -1044, -1047, -1049, -1052, -1054, + -1057, -1059, -1062, -1064, -1066, -1069, -1071, -1074, + -1076, -1078, -1080, -1083, -1085, -1087, -1089, -1092, + -1094, -1096, -1098, -1100, -1102, -1104, -1106, -1108, + -1110, -1112, -1114, -1116, -1118, -1120, -1122, -1124, + -1125, -1127, -1129, -1131, -1133, -1134, -1136, -1138, + -1139, -1141, -1143, -1144, -1146, -1147, -1149, -1150, + -1152, -1153, -1155, -1156, -1158, -1159, -1161, -1162, + -1163, -1165, -1166, -1167, -1169, -1170, -1171, -1172, + -1174, -1175, -1176, -1177, -1178, -1179, -1180, -1181, + -1182, -1184, -1185, -1186, -1187, -1187, -1188, -1189, + -1190, -1191, -1192, -1193, -1194, -1195, -1195, -1196, + -1197, -1198, -1198, -1199, -1200, -1200, -1201, -1202, + -1202, -1203, -1204, -1204, -1205, -1205, -1206, -1206, + -1207, -1207, -1208, -1208, -1208, -1209, -1209, -1210, + -1210, -1210, -1211, -1211, -1211, -1212, -1212, -1212, + -1212, -1212, -1213, -1213, -1213, -1213, -1213, -1213, + -1213, -1213, -1214, -1214, -1214, -1214, -1214, -1214, + -1214, -1214, -1213, -1213, -1213, -1213, -1213, -1213, + -1213, -1213, -1212, -1212, -1212, -1212, -1211, -1211, + -1211, -1211, -1210, -1210, -1210, -1209, -1209, -1209, + -1208, -1208, -1207, -1207, -1207, -1206, -1206, -1205, + -1205, -1204, -1204, -1203, -1202, -1202, -1201, -1201, + -1200, -1199, -1199, -1198, -1197, -1197, -1196, -1195, + -1195, -1194, -1193, -1192, -1192, -1191, -1190, -1189, + -1188, -1187, -1187, -1186, -1185, -1184, -1183, -1182, + -1181, -1180, -1179, -1178, -1177, -1176, -1175, -1174, + -1173, -1172, -1171, -1170, -1169, -1168, -1167, -1166, + -1165, -1163, -1162, -1161, -1160, -1159, -1158, -1156, + -1155, -1154, -1153, -1151, -1150, -1149, -1148, -1146, + -1145, -1144, -1142, -1141, -1140, -1138, -1137, -1135, + -1134, -1133, -1131, -1130, -1128, -1127, -1125, -1124, + -1122, -1121, -1119, -1118, -1116, -1115, -1113, -1112, + -1110, -1109, -1107, -1105, -1104, -1102, -1101, -1099, + -1097, -1096, -1094, -1092, -1091, -1089, -1087, -1085, + -1084, -1082, -1080, -1079, -1077, -1075, -1073, -1071, + -1070, -1068, -1066, -1064, -1062, -1061, -1059, -1057, + -1055, -1053, -1051, -1049, -1047, -1046, -1044, -1042, + -1040, -1038, -1036, -1034, -1032, -1030, -1028, -1026, + -1024, -1022, -1020, -1018, -1016, -1014, -1012, -1010, + -1008, -1006, -1004, -1002, -999, -997, -995, -993, + -991, -989, -987, -985, -982, -980, -978, -976, + -974, -972, -969, -967, -965, -963, -961, -958, + -956, -954, -952, -950, -947, -945, -943, -941, + -938, -936, -934, -931, -929, -927, -924, -922, + -920, -918, -915, -913, -911, -908, -906, -903, + -901, -899, -896, -894, -892, -889, -887, -884, + -882, -880, -877, -875, -872, -870, -867, -865, + -863, -860, -858, -855, -853, -850, -848, -845, + -843, -840, -838, -835, -833, -830, -828, -825, + -823, -820, -818, -815, -813, -810, -808, -805, + -803, -800, -798, -795, -793, -790, -787, -785, + -782, -780, -777, -775, -772, -769, -767, -764, + -762, -759, -757, -754, -751, -749, -746, -744, + -741, -738, -736, -733, -730, -728, -725, -723, + -720, -717, -715, -712, -709, -707, -704, -702, + -699, -696, -694, -691, -688, -686, -683, -680, + -678, -675, -672, -670, -667, -665, -662, -659, + -657, -654, -651, -649, -646, -643, -641, -638, + -635, -633, -630, -627, -625, -622, -619, -617, + -614, -611, -609, -606, -603, -601, -598, -595, + -593, -590, -587, -585, -582, -579, -577, -574, + -571, -569, -566, -563, -561, -558, -555, -553, + -550, -547, -545, -542, -539, -537, -534, -531, + -529, -526, -523, -521, -518, -516, -513, -510, + -508, -505, -502, -500, -497, -495, -492, -489, + -487, -484, -481, -479, -476, -474, -471, -468, + -466, -463, -461, -458, -455, -453, -450, -448, + -445, -442, -440, -437, -435, -432, -430, -427, + -424, -422, -419, -417, -414, -412, -409, -407, + -404, -402, -399, -397, -394, -392, -389, -387, + -384, -382, -379, -377, -374, -372, -369, -367, + -364, -362, -359, -357, -354, -352, -349, -347, + -345, -342, -340, -337, -335, -332, -330, -328, + -325, -323, -320, -318, -316, -313, -311, -309, + -306, -304, -302, -299, -297, -295, -292, -290, + -288, -285, -283, -281, -278, -276, -274, -272, + -269, -267, -265, -263, -260, -258, -256, -254, + -251, -249, -247, -245, -243, -240, -238, -236, + -234, -232, -230, -228, -225, -223, -221, -219, + -217, -215, -213, -211, -209, -207, -205, -202, + -200, -198, -196, -194, -192, -190, -188, -186, + -184, -182, -180, -178, -176, -175, -173, -171, + -169, -167, -165, -163, -161, -159, -157, -156, + -154, -152, -150, -148, -146, -145, -143, -141, + -139, -137, -136, -134, -132, -130, -129, -127, + -125, -124, -122, -120, -119, -117, -115, -114, + -112, -110, -109, -107, -106, -104, -102, -101, + -99, -98, -96, -95, -93, -92, -90, -89, + -87, -86, -84, -83, -82, -80, -79, -77, + -76, -75, -73, -72, -70, -69, -68, -67, + -65, -64, -63, -61, -60, -59, -58, -57, + -55, -54, -53, -52, -51, -49, -48, -47, + -46, -45, -44, -43, -42, -41, -40, -39, + -38, -37, -36, -35, -34, -33, -32, -31, + -30, -29, -28, -27, -26, -26, -25, -24, + -23, -22, -22, -21, -20, -19, -19, -18, + -17, -16, -16, -15, -14, -14, -13, -13, + -12, -11, -11, -10, -10, -9, -9, -8, + -8, -7, -7, -6, -6, -6, -5, -5, + -4, -4, -4, -3, -3, -3, -2, -2, + -2, -2, -2, -1, -1, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static int16 cubic_spline_lut1[1024] = { + 16384, 16384, 16384, 16384, 16384, 16383, 16382, 16381, + 16381, 16381, 16380, 16379, 16379, 16377, 16377, 16376, + 16374, 16373, 16371, 16370, 16369, 16366, 16366, 16364, + 16361, 16360, 16358, 16357, 16354, 16351, 16349, 16347, + 16345, 16342, 16340, 16337, 16335, 16331, 16329, 16326, + 16322, 16320, 16317, 16314, 16309, 16307, 16304, 16299, + 16297, 16293, 16290, 16285, 16282, 16278, 16274, 16269, + 16265, 16262, 16257, 16253, 16247, 16244, 16239, 16235, + 16230, 16225, 16220, 16216, 16211, 16206, 16201, 16196, + 16191, 16185, 16180, 16174, 16169, 16163, 16158, 16151, + 16146, 16140, 16133, 16128, 16122, 16116, 16109, 16104, + 16097, 16092, 16085, 16077, 16071, 16064, 16058, 16052, + 16044, 16038, 16030, 16023, 16015, 16009, 16002, 15995, + 15988, 15980, 15973, 15964, 15957, 15949, 15941, 15934, + 15926, 15918, 15910, 15903, 15894, 15886, 15877, 15870, + 15861, 15853, 15843, 15836, 15827, 15818, 15810, 15801, + 15792, 15783, 15774, 15765, 15756, 15747, 15738, 15729, + 15719, 15709, 15700, 15691, 15681, 15672, 15662, 15652, + 15642, 15633, 15623, 15613, 15602, 15592, 15582, 15572, + 15562, 15552, 15540, 15530, 15520, 15509, 15499, 15489, + 15478, 15467, 15456, 15446, 15433, 15423, 15412, 15401, + 15390, 15379, 15367, 15356, 15345, 15333, 15321, 15310, + 15299, 15287, 15276, 15264, 15252, 15240, 15228, 15216, + 15205, 15192, 15180, 15167, 15155, 15143, 15131, 15118, + 15106, 15094, 15081, 15067, 15056, 15043, 15031, 15017, + 15004, 14992, 14979, 14966, 14953, 14940, 14927, 14913, + 14900, 14887, 14874, 14860, 14846, 14833, 14819, 14806, + 14793, 14778, 14764, 14752, 14737, 14723, 14709, 14696, + 14681, 14668, 14653, 14638, 14625, 14610, 14595, 14582, + 14567, 14553, 14538, 14523, 14509, 14494, 14480, 14465, + 14450, 14435, 14420, 14406, 14391, 14376, 14361, 14346, + 14330, 14316, 14301, 14285, 14270, 14254, 14239, 14223, + 14208, 14192, 14177, 14161, 14146, 14130, 14115, 14099, + 14082, 14067, 14051, 14035, 14019, 14003, 13986, 13971, + 13955, 13939, 13923, 13906, 13890, 13873, 13857, 13840, + 13823, 13808, 13791, 13775, 13758, 13741, 13724, 13707, + 13691, 13673, 13657, 13641, 13623, 13607, 13589, 13572, + 13556, 13538, 13521, 13504, 13486, 13469, 13451, 13435, + 13417, 13399, 13383, 13365, 13347, 13330, 13312, 13294, + 13277, 13258, 13241, 13224, 13205, 13188, 13170, 13152, + 13134, 13116, 13098, 13080, 13062, 13044, 13026, 13008, + 12989, 12971, 12953, 12934, 12916, 12898, 12879, 12860, + 12842, 12823, 12806, 12787, 12768, 12750, 12731, 12712, + 12694, 12675, 12655, 12637, 12618, 12599, 12580, 12562, + 12542, 12524, 12504, 12485, 12466, 12448, 12427, 12408, + 12390, 12370, 12351, 12332, 12312, 12293, 12273, 12254, + 12235, 12215, 12195, 12176, 12157, 12137, 12118, 12097, + 12079, 12059, 12039, 12019, 11998, 11980, 11960, 11940, + 11920, 11900, 11880, 11860, 11839, 11821, 11801, 11780, + 11761, 11741, 11720, 11700, 11680, 11660, 11640, 11619, + 11599, 11578, 11559, 11538, 11518, 11498, 11477, 11457, + 11436, 11415, 11394, 11374, 11354, 11333, 11313, 11292, + 11272, 11251, 11231, 11209, 11189, 11168, 11148, 11127, + 11107, 11084, 11064, 11043, 11023, 11002, 10982, 10959, + 10939, 10918, 10898, 10876, 10856, 10834, 10814, 10792, + 10772, 10750, 10728, 10708, 10687, 10666, 10644, 10623, + 10602, 10581, 10560, 10538, 10517, 10496, 10474, 10453, + 10431, 10410, 10389, 10368, 10346, 10325, 10303, 10283, + 10260, 10239, 10217, 10196, 10175, 10152, 10132, 10110, + 10088, 10068, 10045, 10023, 10002, 9981, 9959, 9936, + 9915, 9893, 9872, 9851, 9829, 9806, 9784, 9763, + 9742, 9720, 9698, 9676, 9653, 9633, 9611, 9589, + 9567, 9545, 9523, 9501, 9479, 9458, 9436, 9414, + 9392, 9370, 9348, 9326, 9304, 9282, 9260, 9238, + 9216, 9194, 9172, 9150, 9128, 9106, 9084, 9062, + 9040, 9018, 8996, 8974, 8951, 8929, 8907, 8885, + 8863, 8841, 8819, 8797, 8775, 8752, 8730, 8708, + 8686, 8664, 8642, 8620, 8597, 8575, 8553, 8531, + 8509, 8487, 8464, 8442, 8420, 8398, 8376, 8353, + 8331, 8309, 8287, 8265, 8242, 8220, 8198, 8176, + 8154, 8131, 8109, 8087, 8065, 8042, 8020, 7998, + 7976, 7954, 7931, 7909, 7887, 7865, 7842, 7820, + 7798, 7776, 7754, 7731, 7709, 7687, 7665, 7643, + 7620, 7598, 7576, 7554, 7531, 7509, 7487, 7465, + 7443, 7421, 7398, 7376, 7354, 7332, 7310, 7288, + 7265, 7243, 7221, 7199, 7177, 7155, 7132, 7110, + 7088, 7066, 7044, 7022, 7000, 6978, 6956, 6934, + 6911, 6889, 6867, 6845, 6823, 6801, 6779, 6757, + 6735, 6713, 6691, 6669, 6647, 6625, 6603, 6581, + 6559, 6537, 6515, 6493, 6472, 6450, 6428, 6406, + 6384, 6362, 6340, 6318, 6297, 6275, 6253, 6231, + 6209, 6188, 6166, 6144, 6122, 6101, 6079, 6057, + 6035, 6014, 5992, 5970, 5949, 5927, 5905, 5884, + 5862, 5841, 5819, 5797, 5776, 5754, 5733, 5711, + 5690, 5668, 5647, 5625, 5604, 5582, 5561, 5540, + 5518, 5497, 5476, 5454, 5433, 5412, 5390, 5369, + 5348, 5327, 5305, 5284, 5263, 5242, 5221, 5199, + 5178, 5157, 5136, 5115, 5094, 5073, 5052, 5031, + 5010, 4989, 4968, 4947, 4926, 4905, 4885, 4864, + 4843, 4822, 4801, 4780, 4760, 4739, 4718, 4698, + 4677, 4656, 4636, 4615, 4595, 4574, 4553, 4533, + 4512, 4492, 4471, 4451, 4431, 4410, 4390, 4370, + 4349, 4329, 4309, 4288, 4268, 4248, 4228, 4208, + 4188, 4167, 4147, 4127, 4107, 4087, 4067, 4047, + 4027, 4007, 3988, 3968, 3948, 3928, 3908, 3889, + 3869, 3849, 3829, 3810, 3790, 3771, 3751, 3732, + 3712, 3693, 3673, 3654, 3634, 3615, 3595, 3576, + 3557, 3538, 3518, 3499, 3480, 3461, 3442, 3423, + 3404, 3385, 3366, 3347, 3328, 3309, 3290, 3271, + 3252, 3233, 3215, 3196, 3177, 3159, 3140, 3121, + 3103, 3084, 3066, 3047, 3029, 3010, 2992, 2974, + 2955, 2937, 2919, 2901, 2882, 2864, 2846, 2828, + 2810, 2792, 2774, 2756, 2738, 2720, 2702, 2685, + 2667, 2649, 2631, 2614, 2596, 2579, 2561, 2543, + 2526, 2509, 2491, 2474, 2456, 2439, 2422, 2405, + 2387, 2370, 2353, 2336, 2319, 2302, 2285, 2268, + 2251, 2234, 2218, 2201, 2184, 2167, 2151, 2134, + 2117, 2101, 2084, 2068, 2052, 2035, 2019, 2003, + 1986, 1970, 1954, 1938, 1922, 1906, 1890, 1874, + 1858, 1842, 1826, 1810, 1794, 1779, 1763, 1747, + 1732, 1716, 1701, 1685, 1670, 1654, 1639, 1624, + 1608, 1593, 1578, 1563, 1548, 1533, 1518, 1503, + 1488, 1473, 1458, 1444, 1429, 1414, 1400, 1385, + 1370, 1356, 1342, 1327, 1313, 1298, 1284, 1270, + 1256, 1242, 1228, 1214, 1200, 1186, 1172, 1158, + 1144, 1131, 1117, 1103, 1090, 1076, 1063, 1049, + 1036, 1022, 1009, 996, 983, 970, 956, 943, + 930, 917, 905, 892, 879, 866, 854, 841, + 828, 816, 803, 791, 778, 766, 754, 742, + 729, 717, 705, 693, 681, 669, 658, 646, + 634, 622, 611, 599, 588, 576, 565, 553, + 542, 531, 520, 508, 497, 486, 475, 464, + 453, 443, 432, 421, 411, 400, 389, 379, + 369, 358, 348, 338, 327, 317, 307, 297, + 287, 277, 268, 258, 248, 238, 229, 219, + 210, 200, 191, 182, 172, 163, 154, 145, + 136, 127, 118, 109, 100, 92, 83, 75, + 66, 58, 49, 41, 32, 24, 16, 8, +}; + +static int16 cubic_spline_lut2[1024] = { + 0, 8, 16, 24, 32, 41, 49, 58, + 66, 75, 83, 92, 100, 109, 118, 127, + 136, 145, 154, 163, 172, 182, 191, 200, + 210, 219, 229, 238, 248, 258, 268, 277, + 287, 297, 307, 317, 327, 338, 348, 358, + 369, 379, 389, 400, 411, 421, 432, 443, + 453, 464, 475, 486, 497, 508, 520, 531, + 542, 553, 565, 576, 588, 599, 611, 622, + 634, 646, 658, 669, 681, 693, 705, 717, + 729, 742, 754, 766, 778, 791, 803, 816, + 828, 841, 854, 866, 879, 892, 905, 917, + 930, 943, 956, 970, 983, 996, 1009, 1022, + 1036, 1049, 1063, 1076, 1090, 1103, 1117, 1131, + 1144, 1158, 1172, 1186, 1200, 1214, 1228, 1242, + 1256, 1270, 1284, 1298, 1313, 1327, 1342, 1356, + 1370, 1385, 1400, 1414, 1429, 1444, 1458, 1473, + 1488, 1503, 1518, 1533, 1548, 1563, 1578, 1593, + 1608, 1624, 1639, 1654, 1670, 1685, 1701, 1716, + 1732, 1747, 1763, 1779, 1794, 1810, 1826, 1842, + 1858, 1874, 1890, 1906, 1922, 1938, 1954, 1970, + 1986, 2003, 2019, 2035, 2052, 2068, 2084, 2101, + 2117, 2134, 2151, 2167, 2184, 2201, 2218, 2234, + 2251, 2268, 2285, 2302, 2319, 2336, 2353, 2370, + 2387, 2405, 2422, 2439, 2456, 2474, 2491, 2509, + 2526, 2543, 2561, 2579, 2596, 2614, 2631, 2649, + 2667, 2685, 2702, 2720, 2738, 2756, 2774, 2792, + 2810, 2828, 2846, 2864, 2882, 2901, 2919, 2937, + 2955, 2974, 2992, 3010, 3029, 3047, 3066, 3084, + 3103, 3121, 3140, 3159, 3177, 3196, 3215, 3233, + 3252, 3271, 3290, 3309, 3328, 3347, 3366, 3385, + 3404, 3423, 3442, 3461, 3480, 3499, 3518, 3538, + 3557, 3576, 3595, 3615, 3634, 3654, 3673, 3693, + 3712, 3732, 3751, 3771, 3790, 3810, 3829, 3849, + 3869, 3889, 3908, 3928, 3948, 3968, 3988, 4007, + 4027, 4047, 4067, 4087, 4107, 4127, 4147, 4167, + 4188, 4208, 4228, 4248, 4268, 4288, 4309, 4329, + 4349, 4370, 4390, 4410, 4431, 4451, 4471, 4492, + 4512, 4533, 4553, 4574, 4595, 4615, 4636, 4656, + 4677, 4698, 4718, 4739, 4760, 4780, 4801, 4822, + 4843, 4864, 4885, 4905, 4926, 4947, 4968, 4989, + 5010, 5031, 5052, 5073, 5094, 5115, 5136, 5157, + 5178, 5199, 5221, 5242, 5263, 5284, 5305, 5327, + 5348, 5369, 5390, 5412, 5433, 5454, 5476, 5497, + 5518, 5540, 5561, 5582, 5604, 5625, 5647, 5668, + 5690, 5711, 5733, 5754, 5776, 5797, 5819, 5841, + 5862, 5884, 5905, 5927, 5949, 5970, 5992, 6014, + 6035, 6057, 6079, 6101, 6122, 6144, 6166, 6188, + 6209, 6231, 6253, 6275, 6297, 6318, 6340, 6362, + 6384, 6406, 6428, 6450, 6472, 6493, 6515, 6537, + 6559, 6581, 6603, 6625, 6647, 6669, 6691, 6713, + 6735, 6757, 6779, 6801, 6823, 6845, 6867, 6889, + 6911, 6934, 6956, 6978, 7000, 7022, 7044, 7066, + 7088, 7110, 7132, 7155, 7177, 7199, 7221, 7243, + 7265, 7288, 7310, 7332, 7354, 7376, 7398, 7421, + 7443, 7465, 7487, 7509, 7531, 7554, 7576, 7598, + 7620, 7643, 7665, 7687, 7709, 7731, 7754, 7776, + 7798, 7820, 7842, 7865, 7887, 7909, 7931, 7954, + 7976, 7998, 8020, 8042, 8065, 8087, 8109, 8131, + 8154, 8176, 8198, 8220, 8242, 8265, 8287, 8309, + 8331, 8353, 8376, 8398, 8420, 8442, 8464, 8487, + 8509, 8531, 8553, 8575, 8597, 8620, 8642, 8664, + 8686, 8708, 8730, 8752, 8775, 8797, 8819, 8841, + 8863, 8885, 8907, 8929, 8951, 8974, 8996, 9018, + 9040, 9062, 9084, 9106, 9128, 9150, 9172, 9194, + 9216, 9238, 9260, 9282, 9304, 9326, 9348, 9370, + 9392, 9414, 9436, 9458, 9479, 9501, 9523, 9545, + 9567, 9589, 9611, 9633, 9653, 9676, 9698, 9720, + 9742, 9763, 9784, 9806, 9829, 9851, 9872, 9893, + 9915, 9936, 9959, 9981, 10002, 10023, 10045, 10068, + 10088, 10110, 10132, 10152, 10175, 10196, 10217, 10239, + 10260, 10283, 10303, 10325, 10346, 10368, 10389, 10410, + 10431, 10453, 10474, 10496, 10517, 10538, 10560, 10581, + 10602, 10623, 10644, 10666, 10687, 10708, 10728, 10750, + 10772, 10792, 10814, 10834, 10856, 10876, 10898, 10918, + 10939, 10959, 10982, 11002, 11023, 11043, 11064, 11084, + 11107, 11127, 11148, 11168, 11189, 11209, 11231, 11251, + 11272, 11292, 11313, 11333, 11354, 11374, 11394, 11415, + 11436, 11457, 11477, 11498, 11518, 11538, 11559, 11578, + 11599, 11619, 11640, 11660, 11680, 11700, 11720, 11741, + 11761, 11780, 11801, 11821, 11839, 11860, 11880, 11900, + 11920, 11940, 11960, 11980, 11998, 12019, 12039, 12059, + 12079, 12097, 12118, 12137, 12157, 12176, 12195, 12215, + 12235, 12254, 12273, 12293, 12312, 12332, 12351, 12370, + 12390, 12408, 12427, 12448, 12466, 12485, 12504, 12524, + 12542, 12562, 12580, 12599, 12618, 12637, 12655, 12675, + 12694, 12712, 12731, 12750, 12768, 12787, 12806, 12823, + 12842, 12860, 12879, 12898, 12916, 12934, 12953, 12971, + 12989, 13008, 13026, 13044, 13062, 13080, 13098, 13116, + 13134, 13152, 13170, 13188, 13205, 13224, 13241, 13258, + 13277, 13294, 13312, 13330, 13347, 13365, 13383, 13399, + 13417, 13435, 13451, 13469, 13486, 13504, 13521, 13538, + 13556, 13572, 13589, 13607, 13623, 13641, 13657, 13673, + 13691, 13707, 13724, 13741, 13758, 13775, 13791, 13808, + 13823, 13840, 13857, 13873, 13890, 13906, 13923, 13939, + 13955, 13971, 13986, 14003, 14019, 14035, 14051, 14067, + 14082, 14099, 14115, 14130, 14146, 14161, 14177, 14192, + 14208, 14223, 14239, 14254, 14270, 14285, 14301, 14316, + 14330, 14346, 14361, 14376, 14391, 14406, 14420, 14435, + 14450, 14465, 14480, 14494, 14509, 14523, 14538, 14553, + 14567, 14582, 14595, 14610, 14625, 14638, 14653, 14668, + 14681, 14696, 14709, 14723, 14737, 14752, 14764, 14778, + 14793, 14806, 14819, 14833, 14846, 14860, 14874, 14887, + 14900, 14913, 14927, 14940, 14953, 14966, 14979, 14992, + 15004, 15017, 15031, 15043, 15056, 15067, 15081, 15094, + 15106, 15118, 15131, 15143, 15155, 15167, 15180, 15192, + 15205, 15216, 15228, 15240, 15252, 15264, 15276, 15287, + 15299, 15310, 15321, 15333, 15345, 15356, 15367, 15379, + 15390, 15401, 15412, 15423, 15433, 15446, 15456, 15467, + 15478, 15489, 15499, 15509, 15520, 15530, 15540, 15552, + 15562, 15572, 15582, 15592, 15602, 15613, 15623, 15633, + 15642, 15652, 15662, 15672, 15681, 15691, 15700, 15709, + 15719, 15729, 15738, 15747, 15756, 15765, 15774, 15783, + 15792, 15801, 15810, 15818, 15827, 15836, 15843, 15853, + 15861, 15870, 15877, 15886, 15894, 15903, 15910, 15918, + 15926, 15934, 15941, 15949, 15957, 15964, 15973, 15980, + 15988, 15995, 16002, 16009, 16015, 16023, 16030, 16038, + 16044, 16052, 16058, 16064, 16071, 16077, 16085, 16092, + 16097, 16104, 16109, 16116, 16122, 16128, 16133, 16140, + 16146, 16151, 16158, 16163, 16169, 16174, 16180, 16185, + 16191, 16196, 16201, 16206, 16211, 16216, 16220, 16225, + 16230, 16235, 16239, 16244, 16247, 16253, 16257, 16262, + 16265, 16269, 16274, 16278, 16282, 16285, 16290, 16293, + 16297, 16299, 16304, 16307, 16309, 16314, 16317, 16320, + 16322, 16326, 16329, 16331, 16335, 16337, 16340, 16342, + 16345, 16347, 16349, 16351, 16354, 16357, 16358, 16360, + 16361, 16364, 16366, 16366, 16369, 16370, 16371, 16373, + 16374, 16376, 16377, 16377, 16379, 16379, 16380, 16381, + 16381, 16381, 16382, 16383, 16384, 16384, 16384, 16384, +}; + +static int16 cubic_spline_lut3[1024] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, -1, -1, -1, -1, -1, -2, -2, + -2, -2, -2, -3, -3, -3, -4, -4, + -4, -5, -5, -6, -6, -6, -7, -7, + -8, -8, -9, -9, -10, -10, -11, -11, + -12, -13, -13, -14, -14, -15, -16, -16, + -17, -18, -19, -19, -20, -21, -22, -22, + -23, -24, -25, -26, -26, -27, -28, -29, + -30, -31, -32, -33, -34, -35, -36, -37, + -38, -39, -40, -41, -42, -43, -44, -45, + -46, -47, -48, -49, -51, -52, -53, -54, + -55, -57, -58, -59, -60, -61, -63, -64, + -65, -67, -68, -69, -70, -72, -73, -75, + -76, -77, -79, -80, -82, -83, -84, -86, + -87, -89, -90, -92, -93, -95, -96, -98, + -99, -101, -102, -104, -106, -107, -109, -110, + -112, -114, -115, -117, -119, -120, -122, -124, + -125, -127, -129, -130, -132, -134, -136, -137, + -139, -141, -143, -145, -146, -148, -150, -152, + -154, -156, -157, -159, -161, -163, -165, -167, + -169, -171, -173, -175, -176, -178, -180, -182, + -184, -186, -188, -190, -192, -194, -196, -198, + -200, -202, -205, -207, -209, -211, -213, -215, + -217, -219, -221, -223, -225, -228, -230, -232, + -234, -236, -238, -240, -243, -245, -247, -249, + -251, -254, -256, -258, -260, -263, -265, -267, + -269, -272, -274, -276, -278, -281, -283, -285, + -288, -290, -292, -295, -297, -299, -302, -304, + -306, -309, -311, -313, -316, -318, -320, -323, + -325, -328, -330, -332, -335, -337, -340, -342, + -345, -347, -349, -352, -354, -357, -359, -362, + -364, -367, -369, -372, -374, -377, -379, -382, + -384, -387, -389, -392, -394, -397, -399, -402, + -404, -407, -409, -412, -414, -417, -419, -422, + -424, -427, -430, -432, -435, -437, -440, -442, + -445, -448, -450, -453, -455, -458, -461, -463, + -466, -468, -471, -474, -476, -479, -481, -484, + -487, -489, -492, -495, -497, -500, -502, -505, + -508, -510, -513, -516, -518, -521, -523, -526, + -529, -531, -534, -537, -539, -542, -545, -547, + -550, -553, -555, -558, -561, -563, -566, -569, + -571, -574, -577, -579, -582, -585, -587, -590, + -593, -595, -598, -601, -603, -606, -609, -611, + -614, -617, -619, -622, -625, -627, -630, -633, + -635, -638, -641, -643, -646, -649, -651, -654, + -657, -659, -662, -665, -667, -670, -672, -675, + -678, -680, -683, -686, -688, -691, -694, -696, + -699, -702, -704, -707, -709, -712, -715, -717, + -720, -723, -725, -728, -730, -733, -736, -738, + -741, -744, -746, -749, -751, -754, -757, -759, + -762, -764, -767, -769, -772, -775, -777, -780, + -782, -785, -787, -790, -793, -795, -798, -800, + -803, -805, -808, -810, -813, -815, -818, -820, + -823, -825, -828, -830, -833, -835, -838, -840, + -843, -845, -848, -850, -853, -855, -858, -860, + -863, -865, -867, -870, -872, -875, -877, -880, + -882, -884, -887, -889, -892, -894, -896, -899, + -901, -903, -906, -908, -911, -913, -915, -918, + -920, -922, -924, -927, -929, -931, -934, -936, + -938, -941, -943, -945, -947, -950, -952, -954, + -956, -958, -961, -963, -965, -967, -969, -972, + -974, -976, -978, -980, -982, -985, -987, -989, + -991, -993, -995, -997, -999, -1002, -1004, -1006, + -1008, -1010, -1012, -1014, -1016, -1018, -1020, -1022, + -1024, -1026, -1028, -1030, -1032, -1034, -1036, -1038, + -1040, -1042, -1044, -1046, -1047, -1049, -1051, -1053, + -1055, -1057, -1059, -1061, -1062, -1064, -1066, -1068, + -1070, -1071, -1073, -1075, -1077, -1079, -1080, -1082, + -1084, -1085, -1087, -1089, -1091, -1092, -1094, -1096, + -1097, -1099, -1101, -1102, -1104, -1105, -1107, -1109, + -1110, -1112, -1113, -1115, -1116, -1118, -1119, -1121, + -1122, -1124, -1125, -1127, -1128, -1130, -1131, -1133, + -1134, -1135, -1137, -1138, -1140, -1141, -1142, -1144, + -1145, -1146, -1148, -1149, -1150, -1151, -1153, -1154, + -1155, -1156, -1158, -1159, -1160, -1161, -1162, -1163, + -1165, -1166, -1167, -1168, -1169, -1170, -1171, -1172, + -1173, -1174, -1175, -1176, -1177, -1178, -1179, -1180, + -1181, -1182, -1183, -1184, -1185, -1186, -1187, -1187, + -1188, -1189, -1190, -1191, -1192, -1192, -1193, -1194, + -1195, -1195, -1196, -1197, -1197, -1198, -1199, -1199, + -1200, -1201, -1201, -1202, -1202, -1203, -1204, -1204, + -1205, -1205, -1206, -1206, -1207, -1207, -1207, -1208, + -1208, -1209, -1209, -1209, -1210, -1210, -1210, -1211, + -1211, -1211, -1211, -1212, -1212, -1212, -1212, -1213, + -1213, -1213, -1213, -1213, -1213, -1213, -1213, -1214, + -1214, -1214, -1214, -1214, -1214, -1214, -1214, -1213, + -1213, -1213, -1213, -1213, -1213, -1213, -1213, -1212, + -1212, -1212, -1212, -1212, -1211, -1211, -1211, -1210, + -1210, -1210, -1209, -1209, -1208, -1208, -1208, -1207, + -1207, -1206, -1206, -1205, -1205, -1204, -1204, -1203, + -1202, -1202, -1201, -1200, -1200, -1199, -1198, -1198, + -1197, -1196, -1195, -1195, -1194, -1193, -1192, -1191, + -1190, -1189, -1188, -1187, -1187, -1186, -1185, -1184, + -1182, -1181, -1180, -1179, -1178, -1177, -1176, -1175, + -1174, -1172, -1171, -1170, -1169, -1167, -1166, -1165, + -1163, -1162, -1161, -1159, -1158, -1156, -1155, -1153, + -1152, -1150, -1149, -1147, -1146, -1144, -1143, -1141, + -1139, -1138, -1136, -1134, -1133, -1131, -1129, -1127, + -1125, -1124, -1122, -1120, -1118, -1116, -1114, -1112, + -1110, -1108, -1106, -1104, -1102, -1100, -1098, -1096, + -1094, -1092, -1089, -1087, -1085, -1083, -1080, -1078, + -1076, -1074, -1071, -1069, -1066, -1064, -1062, -1059, + -1057, -1054, -1052, -1049, -1047, -1044, -1041, -1039, + -1036, -1033, -1031, -1028, -1025, -1022, -1020, -1017, + -1014, -1011, -1008, -1005, -1002, -999, -997, -994, + -991, -987, -984, -981, -978, -975, -972, -969, + -966, -962, -959, -956, -953, -949, -946, -943, + -939, -936, -932, -929, -925, -922, -918, -915, + -911, -908, -904, -900, -897, -893, -889, -886, + -882, -878, -874, -870, -866, -863, -859, -855, + -851, -847, -843, -839, -835, -831, -826, -822, + -818, -814, -810, -806, -801, -797, -793, -788, + -784, -780, -775, -771, -766, -762, -757, -753, + -748, -744, -739, -734, -730, -725, -720, -715, + -711, -706, -701, -696, -691, -686, -682, -677, + -672, -667, -662, -656, -651, -646, -641, -636, + -631, -626, -620, -615, -610, -604, -599, -594, + -588, -583, -577, -572, -566, -561, -555, -550, + -544, -538, -533, -527, -521, -515, -510, -504, + -498, -492, -486, -480, -474, -468, -462, -456, + -450, -444, -438, -432, -425, -419, -413, -407, + -400, -394, -388, -381, -375, -368, -362, -355, + -349, -342, -336, -329, -322, -316, -309, -302, + -295, -289, -282, -275, -268, -261, -254, -247, + -240, -233, -226, -219, -212, -205, -198, -190, + -183, -176, -169, -161, -154, -146, -139, -132, + -124, -117, -109, -101, -94, -86, -78, -71, + -63, -55, -47, -40, -32, -24, -16, -8, +}; + diff --git a/thirdparty/libxmp/src/read_event.c b/thirdparty/libxmp/src/read_event.c new file mode 100644 index 000000000..6d1e40a7b --- /dev/null +++ b/thirdparty/libxmp/src/read_event.c @@ -0,0 +1,1654 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "player.h" +#include "effects.h" +#include "virtual.h" +#include "period.h" + +#ifndef LIBXMP_CORE_PLAYER +#include "med_extras.h" +#endif + + +static struct xmp_subinstrument *get_subinstrument(struct context_data *ctx, + int ins, int key) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_instrument *instrument; + + if (IS_VALID_INSTRUMENT(ins)) { + instrument = &mod->xxi[ins]; + if (IS_VALID_NOTE(key)) { + int mapped = instrument->map[key].ins; + if (mapped != 0xff && mapped >= 0 && mapped < instrument->nsm) + return &instrument->sub[mapped]; + } else { + if (mod->xxi[ins].nsm > 0) { + return &instrument->sub[0]; + } + } + } + + return NULL; +} + +static void reset_envelopes(struct context_data *ctx, struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (!IS_VALID_INSTRUMENT(xc->ins)) + return; + + RESET_NOTE(NOTE_ENV_END); + + xc->v_idx = -1; + xc->p_idx = -1; + xc->f_idx = -1; +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +static void reset_envelope_volume(struct context_data *ctx, + struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (!IS_VALID_INSTRUMENT(xc->ins)) + return; + + RESET_NOTE(NOTE_ENV_END); + + xc->v_idx = -1; +} + +static void reset_envelopes_carry(struct context_data *ctx, + struct channel_data *xc) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi; + + if (!IS_VALID_INSTRUMENT(xc->ins)) + return; + + RESET_NOTE(NOTE_ENV_END); + + xxi = libxmp_get_instrument(ctx, xc->ins); + + /* Reset envelope positions */ + if (~xxi->aei.flg & XMP_ENVELOPE_CARRY) { + xc->v_idx = -1; + } + if (~xxi->pei.flg & XMP_ENVELOPE_CARRY) { + xc->p_idx = -1; + } + if (~xxi->fei.flg & XMP_ENVELOPE_CARRY) { + xc->f_idx = -1; + } +} + +#endif + +static void set_effect_defaults(struct context_data *ctx, int note, + struct xmp_subinstrument *sub, + struct channel_data *xc, int is_toneporta) +{ + struct module_data *m = &ctx->m; + + if (sub != NULL && note >= 0) { + if (!HAS_QUIRK(QUIRK_PROTRACK)) { + xc->finetune = sub->fin; + } + xc->gvl = sub->gvl; + +#ifndef LIBXMP_CORE_DISABLE_IT + if (sub->ifc & 0x80) { + xc->filter.cutoff = (sub->ifc - 0x80) * 2; + } + xc->filter.envelope = 0x100; + + if (sub->ifr & 0x80) { + xc->filter.resonance = (sub->ifr - 0x80) * 2; + } + + /* IT: on a new note without toneporta, allow a computed cutoff + * of 127 with resonance 0 to disable the filter. */ + xc->filter.can_disable = !is_toneporta; +#endif + + /* TODO: should probably expand the LFO period size instead + * of reducing the vibrato rate precision here. + */ + libxmp_lfo_set_depth(&xc->insvib.lfo, sub->vde); + libxmp_lfo_set_rate(&xc->insvib.lfo, (sub->vra + 2) >> 2); + libxmp_lfo_set_waveform(&xc->insvib.lfo, sub->vwf); + xc->insvib.sweep = sub->vsw; + + libxmp_lfo_set_phase(&xc->vibrato.lfo, 0); + libxmp_lfo_set_phase(&xc->tremolo.lfo, 0); + } + + xc->delay = 0; + xc->tremor.up = xc->tremor.down = 0; + + /* Reset arpeggio */ + xc->arpeggio.val[0] = 0; + xc->arpeggio.count = 0; + xc->arpeggio.size = 1; +} + +/* From OpenMPT PortaTarget.mod: + * "A new note (with no portamento command next to it) does not reset the + * portamento target. That is, if a previous portamento has not finished yet, + * calling 3xx or 5xx after the new note will slide it towards the old target. + * Once the portamento target period is reached, the target is reset. This + * means that if the period is modified by another slide (e.g. 1xx or 2xx), + * a following 3xx will not slide back to the original target." + */ +static void set_period(struct context_data *ctx, int note, + struct xmp_subinstrument *sub, + struct channel_data *xc, int is_toneporta) +{ + struct module_data *m = &ctx->m; + + if (sub != NULL && note >= 0) { + double per = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + + if (!HAS_QUIRK(QUIRK_PROTRACK) || (note > 0 && is_toneporta)) { + xc->porta.target = per; + } + + if (xc->period < 1 || !is_toneporta) { + xc->period = per; + } + } +} + +/* From OpenMPT Porta-Pickup.xm: + * "An instrument number should not reset the current portamento target. The + * portamento target is valid until a new target is specified by combining a + * note and a portamento effect." + */ +static void set_period_ft2(struct context_data *ctx, int note, + struct xmp_subinstrument *sub, + struct channel_data *xc, int is_toneporta) +{ + if (note > 0 && is_toneporta) { + xc->porta.target = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + } + if (sub != NULL && note >= 0) { + if (xc->period < 1 || !is_toneporta) { + xc->period = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + } + } +} + + +#ifndef LIBXMP_CORE_PLAYER +#define IS_SFX_PITCH(x) ((x) == FX_PITCH_ADD || (x) == FX_PITCH_SUB) +#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE \ + || (x) == FX_PER_TPORTA || (x) == FX_ULT_TPORTA \ + || (x) == FX_FAR_TPORTA) +#else +#define IS_TONEPORTA(x) ((x) == FX_TONEPORTA || (x) == FX_TONE_VSLIDE) +#endif + +#define set_patch(ctx,chn,ins,smp,note) \ + libxmp_virt_setpatch(ctx, chn, ins, smp, note, 0, 0, 0, 0) + +static int read_event_mod(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note; + struct xmp_subinstrument *sub; + int new_invalid_ins = 0; + int is_toneporta; + int use_ins_vol; + + xc->flags = 0; + note = -1; + is_toneporta = 0; + use_ins_vol = 0; + + if (IS_TONEPORTA(e->fxt) || IS_TONEPORTA(e->f2t)) { + is_toneporta = 1; + } + + /* Check instrument */ + + if (e->ins) { + int ins = e->ins - 1; + use_ins_vol = 1; + SET(NEW_INS); + xc->fadeout = 0x10000; /* for painlace.mod pat 0 ch 3 echo */ + xc->per_flags = 0; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + + if (IS_VALID_INSTRUMENT(ins)) { + sub = get_subinstrument(ctx, ins, e->note - 1); + + if (is_toneporta) { + /* Get new instrument volume */ + if (sub != NULL) { + /* Dennis Lindroos: instrument volume + * is not used on split channels + */ + if (!xc->split) { + xc->volume = sub->vol; + } + use_ins_vol = 0; + } + } else { + xc->ins = ins; + xc->ins_fade = mod->xxi[ins].rls; + + if (sub != NULL) { + if (HAS_QUIRK(QUIRK_PROTRACK)) { + xc->finetune = sub->fin; + } + } + } + } else { + new_invalid_ins = 1; + libxmp_virt_resetchannel(ctx, chn); + } + } + + /* Check note */ + + if (e->note) { + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + use_ins_vol = 0; + } else if (!is_toneporta && IS_VALID_NOTE(e->note - 1)) { + xc->key = e->note - 1; + RESET_NOTE(NOTE_END); + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + if (!new_invalid_ins && sub != NULL) { + int transp = mod->xxi[xc->ins].map[xc->key].xpo; + int smp; + + note = xc->key + sub->xpo + transp; + smp = sub->sid; + + if (!IS_VALID_SAMPLE(smp)) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (e->ins && sub != NULL) { + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (e->vol) { + xc->volume = e->vol - 1; + SET(NEW_VOL); + RESET_PER(VOL_SLIDE); /* FIXME: should this be for FAR only? */ + } + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, e, 1); + libxmp_process_fx(ctx, xc, chn, e, 0); + +#ifndef LIBXMP_CORE_PLAYER + if (IS_SFX_PITCH(e->fxt)) { + xc->period = libxmp_note_to_period(ctx, note, xc->finetune, + xc->per_adj); + } else +#endif + { + set_period(ctx, note, sub, xc, is_toneporta); + } + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (TEST(OFFSET)) { + if (HAS_QUIRK(QUIRK_PROTRACK) || p->flags & XMP_FLAGS_FX9BUG) { + xc->offset.val += xc->offset.val2; + } + RESET(OFFSET); + } + + if (use_ins_vol && !TEST(NEW_VOL) && !xc->split) { + xc->volume = sub->vol; + } + + return 0; +} + +static int sustain_check(struct xmp_envelope *env, int idx) +{ + return (env && + (env->flg & XMP_ENVELOPE_ON) && + (env->flg & XMP_ENVELOPE_SUS) && + (~env->flg & XMP_ENVELOPE_LOOP) && + idx == env->data[env->sus << 1]); +} + +static int read_event_ft2(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note, key, ins; + struct xmp_subinstrument *sub; + int new_invalid_ins; + int is_toneporta; + int use_ins_vol; + int k00 = 0; + struct xmp_event ev; + + /* From the OpenMPT DelayCombination.xm test case: + * "Naturally, Fasttracker 2 ignores notes next to an out-of-range + * note delay. However, to check whether the delay is out of range, + * it is simply compared against the current song speed, not taking + * any pattern delays into account." + */ + if (p->frame >= p->speed) { + return 0; + } + + memcpy(&ev, e, sizeof (struct xmp_event)); + + /* From OpenMPT TremorReset.xm test case: + * "Even if a tremor effect muted the sample on a previous row, volume + * commands should be able to override this effect." + */ + if (ev.vol) { + xc->tremor.count &= ~0x80; + } + + xc->flags = 0; + note = -1; + key = ev.note; + ins = ev.ins; + new_invalid_ins = 0; + is_toneporta = 0; + use_ins_vol = 0; + + /* From the OpenMPT key_off.xm test case: + * "Key off at tick 0 (K00) is very dodgy command. If there is a note + * next to it, the note is ignored. If there is a volume column + * command or instrument next to it and the current instrument has + * no volume envelope, the note is faded out instead of being cut." + */ + if (ev.fxt == FX_KEYOFF && ev.fxp == 0) { + k00 = 1; + key = 0; + + if (ins || ev.vol || ev.f2t) { + if (IS_VALID_INSTRUMENT(xc->ins) && + ~mod->xxi[xc->ins].aei.flg & XMP_ENVELOPE_ON) { + SET_NOTE(NOTE_FADEOUT); + ev.fxt = 0; + } + } + } + + if (IS_TONEPORTA(ev.fxt) || IS_TONEPORTA(ev.f2t)) { + is_toneporta = 1; + } + + /* Check instrument */ + + /* Ignore invalid instruments. The last instrument, invalid or + * not, is preserved in channel data (see read_event() below). + * Fixes stray delayed notes in forgotten_city.xm. + */ + if (ins > 0 && !IS_VALID_INSTRUMENT(ins - 1)) { + ins = 0; + } + + /* FT2: Retrieve old instrument volume */ + if (ins) { + if (key == 0 || key >= XMP_KEY_OFF) { + /* Previous instrument */ + sub = get_subinstrument(ctx, xc->ins, xc->key); + + /* No note */ + if (sub != NULL) { + int pan = mod->xxc[chn].pan - 128; + xc->volume = sub->vol; + + if (!HAS_QUIRK(QUIRK_FTMOD)) { + xc->pan.val = pan + ((sub->pan - 128) * + (128 - abs(pan))) / 128 + 128; + } + + xc->ins_fade = mod->xxi[xc->ins].rls; + SET(NEW_VOL); + } + } + } + + /* Do this regardless if the instrument is invalid or not -- unless + * XM keyoff is used. Fixes xyce-dans_la_rue.xm chn 0 patterns 0E/0F and + * chn 10 patterns 0D/0E, see https://github.com/libxmp/libxmp/issues/152 + * for details. + */ + if (ev.ins && key != XMP_KEY_FADE) { + SET(NEW_INS); + use_ins_vol = 1; + xc->per_flags = 0; + + RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT); + if (!k00) { + RESET_NOTE(NOTE_FADEOUT); + } + + xc->fadeout = 0x10000; + + if (IS_VALID_INSTRUMENT(ins - 1)) { + if (!is_toneporta) + xc->ins = ins - 1; + } else { + new_invalid_ins = 1; + + /* If no note is set FT2 doesn't cut on invalid + * instruments (it keeps playing the previous one). + * If a note is set it cuts the current sample. + */ + xc->flags = 0; + + if (is_toneporta) { + key = 0; + } + } + + xc->tremor.count = 0x20; + } + + /* Check note */ + if (ins) { + if (key > 0 && key < XMP_KEY_OFF) { + /* Retrieve volume when we have note */ + + /* and only if we have instrument, otherwise we're in + * case 1: new note and no instrument + */ + + /* Current instrument */ + sub = get_subinstrument(ctx, xc->ins, key - 1); + if (sub != NULL) { + int pan = mod->xxc[chn].pan - 128; + xc->volume = sub->vol; + + if (!HAS_QUIRK(QUIRK_FTMOD)) { + xc->pan.val = pan + ((sub->pan - 128) * + (128 - abs(pan))) / 128 + 128; + } + + xc->ins_fade = mod->xxi[xc->ins].rls; + } else { + xc->volume = 0; + } + SET(NEW_VOL); + } + } + + if (key) { + SET(NEW_NOTE); + + if (key == XMP_KEY_OFF) { + int env_on = 0; + int vol_set = ev.vol != 0 || ev.fxt == FX_VOLSET; + int delay_fx = ev.fxt == FX_EXTENDED && ev.fxp == 0xd0; + struct xmp_envelope *env = NULL; + + /* OpenMPT NoteOffVolume.xm: + * "If an instrument has no volume envelope, a note-off + * command should cut the sample completely - unless + * there is a volume command next it. This applies to + * both volume commands (volume and effect column)." + * + * ...and unless we have a keyoff+delay without setting + * an instrument. See OffDelay.xm. + */ + if (IS_VALID_INSTRUMENT(xc->ins)) { + env = &mod->xxi[xc->ins].aei; + if (env->flg & XMP_ENVELOPE_ON) { + env_on = 1; + } + } + + if (env_on || (!vol_set && (!ev.ins || !delay_fx))) { + if (sustain_check(env, xc->v_idx)) { + /* See OpenMPT EnvOff.xm. In certain + * cases a release event is effective + * only in the next frame + */ + SET_NOTE(NOTE_SUSEXIT); + } else { + SET_NOTE(NOTE_RELEASE); + } + use_ins_vol = 0; + } else { + SET_NOTE(NOTE_FADEOUT); + } + + /* See OpenMPT keyoff+instr.xm, pattern 2 row 0x40 */ + if (env_on && ev.fxt == FX_EXTENDED && + (ev.fxp >> 4) == EX_DELAY) { + /* See OpenMPT OffDelay.xm test case */ + if ((ev.fxp & 0xf) != 0) { + RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT); + } + } + } else if (key == XMP_KEY_FADE) { + /* Handle keyoff + instrument case (NoteOff2.xm) */ + SET_NOTE(NOTE_FADEOUT); + } else if (is_toneporta) { + /* set key to 0 so we can have the tone portamento from + * the original note (see funky_stars.xm pos 5 ch 9) + */ + key = 0; + + /* And do the same if there's no keyoff (see comic + * bakery remix.xm pos 1 ch 3) + */ + } + + if (ev.ins == 0 && !IS_VALID_INSTRUMENT(xc->old_ins - 1)) { + new_invalid_ins = 1; + } + + if (new_invalid_ins) { + libxmp_virt_resetchannel(ctx, chn); + } + } + + + /* Check note range -- from the OpenMPT test NoteLimit.xm: + * "I think one of the first things Fasttracker 2 does when parsing a + * pattern cell is calculating the “real†note (i.e. pattern note + + * sample transpose), and if this “real†note falls out of its note + * range, it is ignored completely (wiped from its internal channel + * memory). The instrument number next it, however, is not affected + * and remains in the memory." + */ + sub = NULL; + if (IS_VALID_NOTE(key - 1)) { + int k = key - 1; + sub = get_subinstrument(ctx, xc->ins, k); + if (!new_invalid_ins && sub != NULL) { + int transp = mod->xxi[xc->ins].map[k].xpo; + int k2 = k + sub->xpo + transp; + if (k2 < 12 || k2 > 130) { + key = 0; + RESET(NEW_NOTE); + } + } + } + + if (IS_VALID_NOTE(key - 1)) { + xc->key = --key; + xc->fadeout = 0x10000; + RESET_NOTE(NOTE_END); + + if (sub != NULL) { + if (~mod->xxi[xc->ins].aei.flg & XMP_ENVELOPE_ON) { + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + } + } + + if (!new_invalid_ins && sub != NULL) { + int transp = mod->xxi[xc->ins].map[key].xpo; + int smp; + + note = key + sub->xpo + transp; + smp = sub->sid; + + if (!IS_VALID_SAMPLE(smp)) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + + if (ins && sub != NULL && !k00) { + /* Reset envelopes on new instrument, see olympic.xm pos 10 + * But make sure we have an instrument set, see Letting go + * pos 4 chn 20 + */ + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (ev.vol) { + xc->volume = ev.vol - 1; + SET(NEW_VOL); + if (TEST_NOTE(NOTE_END)) { /* m5v-nine.xm */ + xc->fadeout = 0x10000; /* OpenMPT NoteOff.xm */ + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + } + } + + /* FT2: always reset sample offset */ + xc->offset.val = 0; + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, &ev, 1); + libxmp_process_fx(ctx, xc, chn, &ev, 0); + set_period_ft2(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + + /* From the OpenMPT test cases (3xx-no-old-samp.xm): + * "An offset effect that points beyond the sample end should + * stop playback on this channel." + * + * ... except in Skale Tracker (and possibly others), so make this a + * FastTracker2 quirk. See Armada Tanks game.it (actually an XM). + * Reported by Vladislav Suschikh. + */ + if (HAS_QUIRK(QUIRK_FT2BUGS) && xc->offset.val >= mod->xxs[sub->sid].len) { + libxmp_virt_resetchannel(ctx, chn); + } else { + + /* (From Decibelter - Cosmic 'Wegian Mamas.xm p04 ch7) + * We retrigger the sample only if we have a new note + * without tone portamento, otherwise we won't play + * sweeps and loops correctly. + */ + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + return 0; +} + +static int read_event_st3(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note; + struct xmp_subinstrument *sub; + int not_same_ins; + int is_toneporta; + int use_ins_vol; + + xc->flags = 0; + note = -1; + not_same_ins = 0; + is_toneporta = 0; + use_ins_vol = 0; + + if (IS_TONEPORTA(e->fxt) || IS_TONEPORTA(e->f2t)) { + is_toneporta = 1; + } + + if (libxmp_virt_mapchannel(ctx, chn) < 0 && xc->ins != e->ins - 1) { + is_toneporta = 0; + } + + /* Check instrument */ + + if (e->ins) { + int ins = e->ins - 1; + SET(NEW_INS); + use_ins_vol = 1; + xc->fadeout = 0x10000; + xc->per_flags = 0; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + + if (IS_VALID_INSTRUMENT(ins)) { + /* valid ins */ + if (xc->ins != ins) { + not_same_ins = 1; + if (!is_toneporta) { + xc->ins = ins; + xc->ins_fade = mod->xxi[ins].rls; + } else { + /* Get new instrument volume */ + sub = get_subinstrument(ctx, ins, e->note - 1); + if (sub != NULL) { + xc->volume = sub->vol; + use_ins_vol = 0; + } + } + } + } else { + /* invalid ins */ + + /* Ignore invalid instruments */ + xc->flags = 0; + use_ins_vol = 0; + } + } + + /* Check note */ + + if (e->note) { + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + use_ins_vol = 0; + } else if (is_toneporta) { + /* Always retrig in tone portamento: Fix portamento in + * 7spirits.s3m, mod.Biomechanoid + */ + if (not_same_ins) { + xc->offset.val = 0; + } + } else if (IS_VALID_NOTE(e->note - 1)) { + xc->key = e->note - 1; + RESET_NOTE(NOTE_END); + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + if (sub != NULL) { + int transp = mod->xxi[xc->ins].map[xc->key].xpo; + int smp; + + note = xc->key + sub->xpo + transp; + smp = sub->sid; + + if (!IS_VALID_SAMPLE(smp)) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (e->ins && sub != NULL) { + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (e->vol) { + xc->volume = e->vol - 1; + SET(NEW_VOL); + } + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, e, 1); + libxmp_process_fx(ctx, xc, chn, e, 0); + set_period(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + if (HAS_QUIRK(QUIRK_ST3BUGS) && TEST(NEW_VOL)) { + xc->volume = xc->volume * p->gvol / m->volbase; + } + + return 0; +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +static inline void copy_channel(struct player_data *p, int to, int from) +{ + if (to > 0 && to != from) { + memcpy(&p->xc_data[to], &p->xc_data[from], + sizeof (struct channel_data)); + } +} + +static inline int has_note_event(struct xmp_event *e) +{ + return (e->note && e->note <= XMP_MAX_KEYS); +} + +static int check_fadeout(struct context_data *ctx, struct channel_data *xc, int ins) +{ + struct xmp_instrument *xxi = libxmp_get_instrument(ctx, ins); + + if (xxi == NULL) { + return 1; + } + + return (~xxi->aei.flg & XMP_ENVELOPE_ON || + ~xxi->aei.flg & XMP_ENVELOPE_CARRY || + xc->ins_fade == 0 || + xc->fadeout <= xc->ins_fade); +} + +static int check_invalid_sample(struct context_data *ctx, int ins, int key) +{ + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + + if (ins < mod->ins) { + int smp = mod->xxi[ins].map[key].ins; + if (smp == 0xff || smp >= mod->smp) { + return 1; + }; + } + + return 0; +} + +static void fix_period(struct context_data *ctx, int chn, struct xmp_subinstrument *sub) +{ + if (sub->nna == XMP_INST_NNA_CONT) { + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_instrument *xxi = libxmp_get_instrument(ctx, xc->ins); + + xc->period = libxmp_note_to_period(ctx, xc->key + sub->xpo + + xxi->map[xc->key_porta].xpo, xc->finetune, xc->per_adj); + } +} + +static int is_same_sid(struct context_data *ctx, int chn, int ins, int key) +{ + struct player_data *p = &ctx->p; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_subinstrument *s1, *s2; + + s1 = get_subinstrument(ctx, ins, key); + s2 = get_subinstrument(ctx, xc->ins, xc->key); + + return (s1 && s2 && s1->sid == s2->sid); +} + +static int read_event_it(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note, key; + struct xmp_subinstrument *sub; + int not_same_ins, not_same_smp; + int new_invalid_ins; + int is_toneporta, is_release; + int candidate_ins; + int reset_env; + int reset_susloop; + int use_ins_vol; + int sample_mode; + int toneporta_offset; + int retrig_ins; + struct xmp_event ev; + + memcpy(&ev, e, sizeof (struct xmp_event)); + + /* Emulate Impulse Tracker "always read instrument" bug */ + if (ev.ins) { + xc->delayed_ins = 0; + } else if (ev.note && xc->delayed_ins) { + ev.ins = xc->delayed_ins; + xc->delayed_ins = 0; + } + + xc->flags = 0; + note = -1; + key = ev.note; + not_same_ins = 0; + not_same_smp = 0; + new_invalid_ins = 0; + is_toneporta = 0; + is_release = 0; + reset_env = 0; + reset_susloop = 0; + use_ins_vol = 0; + candidate_ins = xc->ins; + sample_mode = !HAS_QUIRK(QUIRK_VIRTUAL); + toneporta_offset = 0; + retrig_ins = 0; + + /* Keyoff + instrument retrigs current instrument in old fx mode */ + if (HAS_QUIRK(QUIRK_ITOLDFX)) { + if (ev.note == XMP_KEY_OFF && IS_VALID_INSTRUMENT(ev.ins -1)) { + retrig_ins = 1; + } + } + + /* Notes with unmapped instruments are ignored */ + if (ev.ins) { + if (ev.ins <= mod->ins && has_note_event(&ev)) { + int ins = ev.ins - 1; + if (check_invalid_sample(ctx, ins, ev.note - 1)) { + candidate_ins = ins; + memset(&ev, 0, sizeof (ev)); + } + } + } else { + if (has_note_event(&ev)) { + int ins = xc->old_ins - 1; + if (!IS_VALID_INSTRUMENT(ins)) { + new_invalid_ins = 1; + } else if (check_invalid_sample(ctx, ins, ev.note - 1)) { + memset(&ev, 0, sizeof (ev)); + } + } + } + + if (IS_TONEPORTA(ev.fxt) || IS_TONEPORTA(ev.f2t)) { + is_toneporta = 1; + } + + if (TEST_NOTE(NOTE_ENV_RELEASE | NOTE_FADEOUT)) { + is_release = 1; + } + + if (xc->period <= 0 || TEST_NOTE(NOTE_END)) { + is_toneporta = 0; + } + + /* Off-Porta.it */ + if (is_toneporta && ev.fxt == FX_OFFSET) { + toneporta_offset = 1; + if (!HAS_QUIRK(QUIRK_PRENV)) { + RESET_NOTE(NOTE_ENV_END); + } + } + + /* Check instrument */ + + if (ev.ins) { + int ins = ev.ins - 1; + int set_new_ins = 1; + + /* portamento_after_keyoff.it test case */ + if (is_release && !key) { + if (is_toneporta) { + if (HAS_QUIRK(QUIRK_PRENV) || TEST_NOTE(NOTE_SET)) { + is_toneporta = 0; + reset_envelopes_carry(ctx, xc); + } + } else { + /* fixes OpenMPT wnoteoff.it */ + reset_envelopes_carry(ctx, xc); + } + } + + if (is_toneporta && xc->ins == ins) { + if (!HAS_QUIRK(QUIRK_PRENV)) { + if (is_same_sid(ctx, chn, ins, key - 1)) { + /* same instrument and same sample */ + set_new_ins = !is_release; + } else { + /* same instrument, different sample */ + not_same_ins = 1; /* need this too */ + not_same_smp = 1; + } + } + } + + if (set_new_ins) { + SET(NEW_INS); + reset_env = 1; + } + /* Sample default volume is always enabled if a valid sample + * is provided (Atomic Playboy, default_volume.it). */ + use_ins_vol = 1; + xc->per_flags = 0; + + if (IS_VALID_INSTRUMENT(ins)) { + /* valid ins */ + + /* See OpenMPT StoppedInstrSwap.it for cut case */ + if (!key && !TEST_NOTE(NOTE_KEY_CUT)) { + /* Retrig in new ins in sample mode */ + if (sample_mode && TEST_NOTE(NOTE_END)) { + libxmp_virt_voicepos(ctx, chn, 0); + } + + /* IT: Reset note for every new != ins */ + if (xc->ins == ins) { + SET(NEW_INS); + use_ins_vol = 1; + } else { + key = xc->key + 1; + } + + RESET_NOTE(NOTE_SET); + } + + if (xc->ins != ins && (!is_toneporta || !HAS_QUIRK(QUIRK_PRENV))) { + candidate_ins = ins; + + if (!is_same_sid(ctx, chn, ins, key - 1)) { + not_same_ins = 1; + if (is_toneporta) { + /* Get new instrument volume */ + sub = get_subinstrument(ctx, ins, key); + if (sub != NULL) { + xc->volume = sub->vol; + use_ins_vol = 0; + } + } + } + } + } else { + /* In sample mode invalid instruments cut the current + * note (OpenMPT SampleNumberChange.it). + * TODO: portamento_sustain.it order 3 row 19: when + * sample release is set, this isn't always done? */ + if (sample_mode) { + xc->volume = 0; + } + + /* Ignore invalid instruments */ + new_invalid_ins = 1; + xc->flags = 0; + use_ins_vol = 0; + } + } + + /* Check note */ + + if (key) { + SET(NEW_NOTE); + SET_NOTE(NOTE_SET); + + if (key == XMP_KEY_FADE) { + SET_NOTE(NOTE_FADEOUT); + reset_env = 0; + reset_susloop = 0; + use_ins_vol = 0; + } else if (key == XMP_KEY_CUT) { + SET_NOTE(NOTE_END | NOTE_CUT | NOTE_KEY_CUT); + xc->period = 0; + libxmp_virt_resetchannel(ctx, chn); + } else if (key == XMP_KEY_OFF) { + struct xmp_envelope *env = NULL; + if (IS_VALID_INSTRUMENT(xc->ins)) { + env = &mod->xxi[xc->ins].aei; + } + if (sustain_check(env, xc->v_idx)) { + SET_NOTE(NOTE_SUSEXIT); + } else { + SET_NOTE(NOTE_RELEASE); + } + SET(KEY_OFF); + /* Use instrument volume if an instrument was explicitly + * provided on this row (see OpenMPT NoteOffInstr.it row 4). + * However, never reset the envelope (see OpenMPT wnoteoff.it). + */ + reset_env = 0; + reset_susloop = 0; + if (!ev.ins) { + use_ins_vol = 0; + } + } else if (!new_invalid_ins) { + /* Sample sustain release should always carry for tone + * portamento, and is not reset unless a note is + * present (Atomic Playboy, portamento_sustain.it). */ + /* portamento_after_keyoff.it test case */ + /* also see suburban_streets o13 c45 */ + if (!is_toneporta) { + reset_env = 1; + reset_susloop = 1; + } + + if (is_toneporta) { + if (not_same_ins || TEST_NOTE(NOTE_END)) { + SET(NEW_INS); + RESET_NOTE(NOTE_ENV_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); + } else { + if (IS_VALID_NOTE(key - 1)) { + xc->key_porta = key - 1; + } + key = 0; + } + } + } + } + + /* TODO: instrument change+porta(+release?) doesn't require a key. + * Order 3/row 11 of portamento_sustain.it should change the sample. */ + if (IS_VALID_NOTE(key - 1) && !new_invalid_ins) { + if (TEST_NOTE(NOTE_CUT)) { + use_ins_vol = 1; /* See OpenMPT NoteOffInstr.it */ + } + xc->key = --key; + RESET_NOTE(NOTE_END); + + sub = get_subinstrument(ctx, candidate_ins, key); + + if (sub != NULL) { + int transp = mod->xxi[candidate_ins].map[key].xpo; + int smp, to; + int dct; + int rvv; + + /* Clear note delay before duplicating channels: + * it_note_delay_nna.it */ + xc->delay = 0; + + note = key + sub->xpo + transp; + smp = sub->sid; + if (!IS_VALID_SAMPLE(smp)) { + smp = -1; + } + dct = sub->dct; + + if (not_same_smp) { + fix_period(ctx, chn, sub); + /* Toneporta, even when not executed, disables + * NNA and DCAs for the current note: + * portamento_nna_sample.it, gxsmp2.it */ + libxmp_virt_setnna(ctx, chn, XMP_INST_NNA_CUT); + dct = XMP_INST_DCT_OFF; + } + to = libxmp_virt_setpatch(ctx, chn, candidate_ins, smp, + note, key, sub->nna, dct, sub->dca); + + /* Random value for volume swing */ + rvv = sub->rvv & 0xff; + if (rvv) { + CLAMP(rvv, 0, 100); + xc->rvv = rand() % (rvv + 1); + } else { + xc->rvv = 0; + } + + /* Random value for pan swing */ + rvv = (sub->rvv & 0xff00) >> 8; + if (rvv) { + CLAMP(rvv, 0, 64); + xc->rpv = rand() % (rvv + 1) - (rvv / 2); + } else { + xc->rpv = 0; + } + + if (to < 0) + return -1; + if (to != chn) { + copy_channel(p, to, chn); + p->xc_data[to].flags = 0; + } + + if (smp >= 0) { /* Not sure if needed */ + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + + /* Do after virtual channel copy */ + if (is_toneporta || retrig_ins) { + if (HAS_QUIRK(QUIRK_PRENV) && ev.ins) { + reset_envelopes_carry(ctx, xc); + } + } + + if (IS_VALID_INSTRUMENT(candidate_ins)) { + if (xc->ins != candidate_ins) { + /* Reset envelopes if instrument changes */ + reset_envelopes(ctx, xc); + } + xc->ins = candidate_ins; + xc->ins_fade = mod->xxi[candidate_ins].rls; + } + + /* Reset in case of new instrument and the previous envelope has + * finished (OpenMPT test EnvReset.it). This must take place after + * channel copies in case of NNA (see test/test.it) + * Also if we have envelope in carry mode, check fadeout + * Also, only reset the volume envelope. (it_fade_env_reset_carry.it) + */ + if (ev.ins && TEST_NOTE(NOTE_ENV_END)) { + if (check_fadeout(ctx, xc, candidate_ins)) { + reset_envelope_volume(ctx, xc); + } else { + reset_env = 0; + } + } + + if (reset_env) { + if (ev.note) { + RESET_NOTE(NOTE_ENV_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); + } + /* Set after copying to new virtual channel (see ambio.it) */ + xc->fadeout = 0x10000; + } + if (reset_susloop && ev.note) { + RESET_NOTE(NOTE_SAMPLE_RELEASE); + } + + /* See OpenMPT wnoteoff.it vs noteoff3.it */ + if (retrig_ins && not_same_ins) { + SET(NEW_INS); + libxmp_virt_voicepos(ctx, chn, 0); + xc->fadeout = 0x10000; + RESET_NOTE(NOTE_RELEASE|NOTE_SUSEXIT|NOTE_FADEOUT); + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (sub != NULL) { + if (note >= 0) { + /* Reset pan, see OpenMPT PanReset.it */ + if (sub->pan >= 0) { + xc->pan.val = sub->pan; + xc->pan.surround = 0; + } + + if (TEST_NOTE(NOTE_CUT)) { + reset_envelopes(ctx, xc); + } else if (!toneporta_offset || HAS_QUIRK(QUIRK_PRENV)) { + reset_envelopes_carry(ctx, xc); + } + RESET_NOTE(NOTE_CUT); + } + } + + /* Process new volume */ + if (ev.vol && (!TEST_NOTE(NOTE_CUT) || ev.ins != 0)) { + /* Do this even for XMP_KEY_OFF (see OpenMPT NoteOffInstr.it row 4). */ + xc->volume = ev.vol - 1; + SET(NEW_VOL); + } + + /* IT: always reset sample offset */ + xc->offset.val &= ~0xffff; + + /* According to Storlek test 25, Impulse Tracker handles the volume + * column effects after the standard effects. + */ + libxmp_process_fx(ctx, xc, chn, &ev, 0); + libxmp_process_fx(ctx, xc, chn, &ev, 1); + set_period(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + } + if (note >= 0 || toneporta_offset) { + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + return 0; +} + +#endif + +#ifndef LIBXMP_CORE_PLAYER + +static int read_event_med(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + int note; + struct xmp_subinstrument *sub; + int new_invalid_ins = 0; + int is_toneporta; + int use_ins_vol; + int finetune; + + xc->flags = 0; + note = -1; + is_toneporta = 0; + use_ins_vol = 0; + + if (e->fxt == FX_TONEPORTA || e->fxt == FX_TONE_VSLIDE) { + is_toneporta = 1; + } + + /* Check instrument */ + + if (e->ins && e->note) { + int ins = e->ins - 1; + use_ins_vol = 1; + SET(NEW_INS); + xc->fadeout = 0x10000; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + + if (IS_VALID_INSTRUMENT(ins)) { + if (is_toneporta) { + /* Get new instrument volume */ + sub = get_subinstrument(ctx, ins, e->note - 1); + if (sub != NULL) { + xc->volume = sub->vol; + use_ins_vol = 0; + } + } else { + xc->ins = ins; + xc->ins_fade = mod->xxi[ins].rls; + } + } else { + new_invalid_ins = 1; + libxmp_virt_resetchannel(ctx, chn); + } + + MED_CHANNEL_EXTRAS(*xc)->arp = 0; + MED_CHANNEL_EXTRAS(*xc)->aidx = 0; + } else { + /* Hold */ + if (e->ins && !e->note) { + use_ins_vol = 1; + } + } + + /* Check note */ + + if (e->note) { + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + use_ins_vol = 0; + } else if (e->note == XMP_KEY_CUT) { + SET_NOTE(NOTE_END); + xc->period = 0; + libxmp_virt_resetchannel(ctx, chn); + } else if (!is_toneporta && IS_VALID_INSTRUMENT(xc->ins) && IS_VALID_NOTE(e->note - 1)) { + struct xmp_instrument *xxi = &mod->xxi[xc->ins]; + + xc->key = e->note - 1; + RESET_NOTE(NOTE_END); + + xc->per_adj = 0.0; + if (xxi->nsm > 1 && HAS_MED_INSTRUMENT_EXTRAS(*xxi)) { + /* synth or iffoct */ + if (MED_INSTRUMENT_EXTRAS(*xxi)->vts == 0 && + MED_INSTRUMENT_EXTRAS(*xxi)->wts == 0) { + /* iffoct */ + xc->per_adj = 2.0; + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + if (!new_invalid_ins && sub != NULL) { + int transp = xxi->map[xc->key].xpo; + int smp; + + note = xc->key + sub->xpo + transp; + smp = sub->sid; + + if (!IS_VALID_SAMPLE(smp)) { + smp = -1; + } + + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + xc->flags = 0; + use_ins_vol = 0; + } + } + } + + sub = get_subinstrument(ctx, xc->ins, xc->key); + + /* Keep effect-set finetune if no instrument set */ + finetune = xc->finetune; + set_effect_defaults(ctx, note, sub, xc, is_toneporta); + if (!e->ins) { + xc->finetune = finetune; + } + + if (e->ins && sub != NULL) { + reset_envelopes(ctx, xc); + } + + /* Process new volume */ + if (e->vol) { + xc->volume = e->vol - 1; + SET(NEW_VOL); + } + + /* Secondary effect handled first */ + libxmp_process_fx(ctx, xc, chn, e, 1); + libxmp_process_fx(ctx, xc, chn, e, 0); + set_period(ctx, note, sub, xc, is_toneporta); + + if (sub == NULL) { + return 0; + } + + if (note >= 0) { + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + } + + if (use_ins_vol && !TEST(NEW_VOL)) { + xc->volume = sub->vol; + } + + return 0; +} + +#endif + +static int read_event_smix(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct channel_data *xc = &p->xc_data[chn]; + struct xmp_subinstrument *sub; + struct xmp_instrument *xxi; + int ins, note, transp, smp; + + xc->flags = 0; + + if (!e->ins) + return 0; + + ins = e->ins - 1; + SET(NEW_INS); + xc->per_flags = 0; + xc->offset.val = 0; + RESET_NOTE(NOTE_RELEASE|NOTE_FADEOUT); + + xxi = libxmp_get_instrument(ctx, ins); + if (xxi != NULL) { + xc->ins_fade = xxi->rls; + } + xc->ins = ins; + + SET(NEW_NOTE); + + if (e->note == XMP_KEY_OFF) { + SET_NOTE(NOTE_RELEASE); + return 0; + } else if (e->note == XMP_KEY_FADE) { + SET_NOTE(NOTE_FADEOUT); + return 0; + } else if (e->note == XMP_KEY_CUT) { + SET_NOTE(NOTE_END); + xc->period = 0; + libxmp_virt_resetchannel(ctx, chn); + return 0; + } + + xc->key = e->note - 1; + xc->fadeout = 0x10000; + RESET_NOTE(NOTE_END); + + if (ins >= mod->ins && ins < mod->ins + smix->ins) { + sub = &xxi->sub[0]; + if (sub == NULL) { + return 0; + } + + note = xc->key + sub->xpo; + smp = sub->sid; + if (smix->xxs[smp].len == 0) + smp = -1; + if (smp >= 0 && smp < smix->smp) { + smp += mod->smp; + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } else { + sub = IS_VALID_NOTE(xc->key) ? + get_subinstrument(ctx, xc->ins, xc->key) : NULL; + if (sub == NULL) { + return 0; + } + transp = xxi->map[xc->key].xpo; + note = xc->key + sub->xpo + transp; + smp = sub->sid; + if (!IS_VALID_SAMPLE(smp)) + smp = -1; + if (smp >= 0 && smp < mod->smp) { + set_patch(ctx, chn, xc->ins, smp, note); + xc->smp = smp; + } + } + + set_effect_defaults(ctx, note, sub, xc, 0); + set_period(ctx, note, sub, xc, 0); + + if (e->ins) { + reset_envelopes(ctx, xc); + } + + xc->volume = e->vol - 1; + + xc->note = note; + libxmp_virt_voicepos(ctx, chn, xc->offset.val); + + return 0; +} + +int libxmp_read_event(struct context_data *ctx, struct xmp_event *e, int chn) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct channel_data *xc = &p->xc_data[chn]; + + if (e->ins != 0) + xc->old_ins = e->ins; + + if (TEST_NOTE(NOTE_SAMPLE_END)) { + SET_NOTE(NOTE_END); + } + + if (chn >= m->mod.chn) { + return read_event_smix(ctx, e, chn); + } else switch (m->read_event_type) { + case READ_EVENT_MOD: + return read_event_mod(ctx, e, chn); + case READ_EVENT_FT2: + return read_event_ft2(ctx, e, chn); + case READ_EVENT_ST3: + return read_event_st3(ctx, e, chn); +#ifndef LIBXMP_CORE_DISABLE_IT + case READ_EVENT_IT: + return read_event_it(ctx, e, chn); +#endif +#ifndef LIBXMP_CORE_PLAYER + case READ_EVENT_MED: + return read_event_med(ctx, e, chn); +#endif + default: + return read_event_mod(ctx, e, chn); + } +} diff --git a/thirdparty/libxmp/src/scan.c b/thirdparty/libxmp/src/scan.c new file mode 100644 index 000000000..a1a2daa28 --- /dev/null +++ b/thirdparty/libxmp/src/scan.c @@ -0,0 +1,721 @@ +/* Extended Module Player + * Copyright (C) 1996-2023 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Sun, 31 May 1998 17:50:02 -0600 + * Reported by ToyKeeper : + * For loop-prevention, I know a way to do it which lets most songs play + * fine once through even if they have backward-jumps. Just keep a small + * array (256 bytes, or even bits) of flags, each entry determining if a + * pattern in the song order has been played. If you get to an entry which + * is already non-zero, skip to the next song (assuming looping is off). + */ + +/* + * Tue, 6 Oct 1998 21:23:17 +0200 (CEST) + * Reported by John v/d Kamp : + * scan.c was hanging when it jumps to an invalid restart value. + * (Fixed by hipolito) + */ + + +#include "common.h" +#include "effects.h" +#include "mixer.h" + +#ifndef LIBXMP_CORE_PLAYER +#include "far_extras.h" +#endif + +#define VBLANK_TIME_THRESHOLD 480000 /* 8 minutes */ + +#define S3M_END 0xff +#define S3M_SKIP 0xfe + + +static int scan_module(struct context_data *ctx, int ep, int chain) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + const struct xmp_module *mod = &m->mod; + const struct xmp_track *tracks[XMP_MAX_CHANNELS]; + const struct xmp_event *event; + int parm, gvol_memory, f1, f2, p1, p2, ord, ord2; + int row, last_row, break_row, row_count, row_count_total; + int orders_since_last_valid, any_valid; + int gvl, bpm, speed, base_time, chn; + int frame_count; + double time, start_time; + int loop_chn, loop_num, inside_loop, line_jump; + int pdelay = 0; + int loop_count[XMP_MAX_CHANNELS]; + int loop_row[XMP_MAX_CHANNELS]; + int i, pat; + int has_marker; + struct ord_data *info; +#ifndef LIBXMP_CORE_PLAYER + int st26_speed; + int far_tempo_coarse, far_tempo_fine, far_tempo_mode; +#endif + /* was 255, but Global trash goes to 318. + * Higher limit for MEDs, defiance.crybaby.5 has blocks with 2048+ rows. */ + const int row_limit = IS_PLAYER_MODE_MED() ? 3200 : 512; + + if (mod->len == 0) + return 0; + + for (i = 0; i < mod->len; i++) { + pat = mod->xxo[i]; + memset(m->scan_cnt[i], 0, pat >= mod->pat ? 1 : + mod->xxp[pat]->rows ? mod->xxp[pat]->rows : 1); + } + + for (i = 0; i < mod->chn; i++) { + loop_count[i] = 0; + loop_row[i] = -1; + } + loop_num = 0; + loop_chn = -1; + line_jump = 0; + + gvl = mod->gvl; + bpm = mod->bpm; + + speed = mod->spd; + base_time = m->rrate; +#ifndef LIBXMP_CORE_PLAYER + st26_speed = 0; + far_tempo_coarse = 4; + far_tempo_fine = 0; + far_tempo_mode = 1; + + if (HAS_FAR_MODULE_EXTRAS(ctx->m)) { + far_tempo_coarse = FAR_MODULE_EXTRAS(ctx->m)->coarse_tempo; + libxmp_far_translate_tempo(far_tempo_mode, 0, far_tempo_coarse, + &far_tempo_fine, &speed, &bpm); + } +#endif + + has_marker = HAS_QUIRK(QUIRK_MARKER); + + /* By erlk ozlr + * + * xmp doesn't handle really properly the "start" option (-s for the + * command-line) for DeusEx's .umx files. These .umx files contain + * several loop "tracks" that never join together. That's how they put + * multiple musics on each level with a file per level. Each "track" + * starts at the same order in all files. The problem is that xmp starts + * decoding the module at order 0 and not at the order specified with + * the start option. If we have a module that does "0 -> 2 -> 0 -> ...", + * we cannot play order 1, even with the supposed right option. + * + * was: ord2 = ord = -1; + * + * CM: Fixed by using different "sequences" for each loop or subsong. + * Each sequence has its entry point. Sequences don't overlap. + */ + ord2 = -1; + ord = ep - 1; + + gvol_memory = break_row = row_count = row_count_total = frame_count = 0; + orders_since_last_valid = any_valid = 0; + start_time = time = 0.0; + inside_loop = 0; + + while (42) { + /* Sanity check to prevent getting stuck due to broken patterns. */ + if (orders_since_last_valid > 512) { + D_(D_CRIT "orders_since_last_valid = %d @ ord %d; ending scan", orders_since_last_valid, ord); + break; + } + orders_since_last_valid++; + + if ((uint32)++ord >= mod->len) { + if (mod->rst > mod->len || mod->xxo[mod->rst] >= mod->pat) { + ord = ep; + } else { + if (libxmp_get_sequence(ctx, mod->rst) == chain) { + ord = mod->rst; + } else { + ord = ep; + } + } + + pat = mod->xxo[ord]; + if (has_marker && pat == S3M_END) { + break; + } + } + + pat = mod->xxo[ord]; + info = &m->xxo_info[ord]; + + /* Allow more complex order reuse only in main sequence */ + if (ep != 0 && p->sequence_control[ord] != 0xff) { + break; + } + p->sequence_control[ord] = chain; + + /* All invalid patterns skipped, only S3M_END aborts replay */ + if (pat >= mod->pat) { + if (has_marker && pat == S3M_END) { + ord = mod->len; + continue; + } + continue; + } + + if (break_row >= mod->xxp[pat]->rows) { + break_row = 0; + } + + /* Loops can cross pattern boundaries, so check if we're not looping */ + if (m->scan_cnt[ord][break_row] && !inside_loop) { + break; + } + + /* Only update pattern information if we weren't here before. This also + * means that we don't update pattern information if we're inside a loop, + * otherwise a loop containing e.g. a global volume fade can make the + * pattern start with the wrong volume. (fixes xyce-dans_la_rue.xm replay, + * see https://github.com/libxmp/libxmp/issues/153 for more details). + */ + if (info->time < 0) { + info->gvl = gvl; + info->bpm = bpm; + info->speed = speed; + info->time = time + m->time_factor * frame_count * base_time / bpm; +#ifndef LIBXMP_CORE_PLAYER + info->st26_speed = st26_speed; +#endif + } + + if (info->start_row == 0 && ord != 0) { + if (ord == ep) { + start_time = time + m->time_factor * frame_count * base_time / bpm; + } + + info->start_row = break_row; + } + + /* Get tracks in advance to speed up the event parsing loop. */ + for (chn = 0; chn < mod->chn; chn++) { + tracks[chn] = mod->xxt[TRACK_NUM(pat, chn)]; + } + + last_row = mod->xxp[pat]->rows; + for (row = break_row, break_row = 0; row < last_row; row++, row_count++, row_count_total++) { + /* Prevent crashes caused by large softmixer frames */ + if (bpm < XMP_MIN_BPM) { + bpm = XMP_MIN_BPM; + } + + /* Date: Sat, 8 Sep 2007 04:01:06 +0200 + * Reported by Zbigniew Luszpinski + * The scan routine falls into infinite looping and doesn't let + * xmp play jos-dr4k.xm. + * Claudio's workaround: we'll break infinite loops here. + * + * Date: Oct 27, 2007 8:05 PM + * From: Adric Riedel + * Jesper Kyd: Global Trash 3.mod (the 'Hardwired' theme) only + * plays the first 4:41 of what should be a 10 minute piece. + * (...) it dies at the end of position 2F + */ + + if (row_count_total > row_limit) { + D_(D_CRIT "row_count_total = %d @ ord %d, pat %d, row %d; ending scan", row_count_total, ord, pat, row); + goto end_module; + } + + if (!loop_num && !line_jump && m->scan_cnt[ord][row]) { + row_count--; + goto end_module; + } + m->scan_cnt[ord][row]++; + orders_since_last_valid = 0; + any_valid = 1; + + /* If the scan count for this row overflows, break. + * A scan count of 0 will help break this loop in playback (storlek_11.it). + */ + if (!m->scan_cnt[ord][row]) { + goto end_module; + } + + pdelay = 0; + line_jump = 0; + + for (chn = 0; chn < mod->chn; chn++) { + if (row >= tracks[chn]->rows) + continue; + + //event = &EVENT(mod->xxo[ord], chn, row); + event = &tracks[chn]->event[row]; + + f1 = event->fxt; + p1 = event->fxp; + f2 = event->f2t; + p2 = event->f2p; + + if (f1 == FX_GLOBALVOL || f2 == FX_GLOBALVOL) { + gvl = (f1 == FX_GLOBALVOL) ? p1 : p2; + gvl = gvl > m->gvolbase ? m->gvolbase : gvl < 0 ? 0 : gvl; + } + + /* Process fine global volume slide */ + if (f1 == FX_GVOL_SLIDE || f2 == FX_GVOL_SLIDE) { + int h, l; + parm = (f1 == FX_GVOL_SLIDE) ? p1 : p2; + + process_gvol: + if (parm) { + gvol_memory = parm; + h = MSN(parm); + l = LSN(parm); + + if (HAS_QUIRK(QUIRK_FINEFX)) { + if (l == 0xf && h != 0) { + gvl += h; + } else if (h == 0xf && l != 0) { + gvl -= l; + } else { + if (m->quirk & QUIRK_VSALL) { + gvl += (h - l) * speed; + } else { + gvl += (h - l) * (speed - 1); + } + } + } else { + if (m->quirk & QUIRK_VSALL) { + gvl += (h - l) * speed; + } else { + gvl += (h - l) * (speed - 1); + } + } + } else { + if ((parm = gvol_memory) != 0) + goto process_gvol; + } + } + + /* Some formats can have two FX_SPEED effects, and both need + * to be checked. Slot 2 is currently handled first. */ + for (i = 0; i < 2; i++) { + parm = i ? p1 : p2; + if ((i ? f1 : f2) != FX_SPEED || parm == 0) + continue; + frame_count += row_count * speed; + row_count = 0; + if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) { + speed = parm; +#ifndef LIBXMP_CORE_PLAYER + st26_speed = 0; +#endif + } else { + time += m->time_factor * frame_count * base_time / bpm; + frame_count = 0; + bpm = parm; + } + } + +#ifndef LIBXMP_CORE_PLAYER + if (f1 == FX_SPEED_CP) { + f1 = FX_S3M_SPEED; + } + if (f2 == FX_SPEED_CP) { + f2 = FX_S3M_SPEED; + } + + /* ST2.6 speed processing */ + + if (f1 == FX_ICE_SPEED && p1) { + if (LSN(p1)) { + st26_speed = (MSN(p1) << 8) | LSN(p1); + } else { + st26_speed = MSN(p1); + } + } + + /* FAR tempo processing */ + + if (f1 == FX_FAR_TEMPO || f1 == FX_FAR_F_TEMPO) { + int far_speed, far_bpm, fine_change = 0; + if (f1 == FX_FAR_TEMPO) { + if (MSN(p1)) { + far_tempo_mode = MSN(p1) - 1; + } else { + far_tempo_coarse = LSN(p1); + } + } + if (f1 == FX_FAR_F_TEMPO) { + if (MSN(p1)) { + far_tempo_fine += MSN(p1); + fine_change = MSN(p1); + } else if (LSN(p1)) { + far_tempo_fine -= LSN(p1); + fine_change = -LSN(p1); + } else { + far_tempo_fine = 0; + } + } + if (libxmp_far_translate_tempo(far_tempo_mode, fine_change, + far_tempo_coarse, &far_tempo_fine, &far_speed, &far_bpm) == 0) { + frame_count += row_count * speed; + row_count = 0; + time += m->time_factor * frame_count * base_time / bpm; + frame_count = 0; + speed = far_speed; + bpm = far_bpm; + } + } +#endif + + if ((f1 == FX_S3M_SPEED && p1) || (f2 == FX_S3M_SPEED && p2)) { + parm = (f1 == FX_S3M_SPEED) ? p1 : p2; + if (parm > 0) { + frame_count += row_count * speed; + row_count = 0; + speed = parm; +#ifndef LIBXMP_CORE_PLAYER + st26_speed = 0; +#endif + } + } + + if ((f1 == FX_S3M_BPM && p1) || (f2 == FX_S3M_BPM && p2)) { + parm = (f1 == FX_S3M_BPM) ? p1 : p2; + if (parm >= XMP_MIN_BPM) { + frame_count += row_count * speed; + row_count = 0; + time += m->time_factor * frame_count * base_time / bpm; + frame_count = 0; + bpm = parm; + } + } + +#ifndef LIBXMP_CORE_DISABLE_IT + if ((f1 == FX_IT_BPM && p1) || (f2 == FX_IT_BPM && p2)) { + parm = (f1 == FX_IT_BPM) ? p1 : p2; + frame_count += row_count * speed; + row_count = 0; + time += m->time_factor * frame_count * base_time / bpm; + frame_count = 0; + + if (MSN(parm) == 0) { + time += m->time_factor * base_time / bpm; + for (i = 1; i < speed; i++) { + bpm -= LSN(parm); + if (bpm < 0x20) + bpm = 0x20; + time += m->time_factor * base_time / bpm; + } + + /* remove one row at final bpm */ + time -= m->time_factor * speed * base_time / bpm; + + } else if (MSN(parm) == 1) { + time += m->time_factor * base_time / bpm; + for (i = 1; i < speed; i++) { + bpm += LSN(parm); + if (bpm > 0xff) + bpm = 0xff; + time += m->time_factor * base_time / bpm; + } + + /* remove one row at final bpm */ + time -= m->time_factor * speed * base_time / bpm; + + } else { + bpm = parm; + } + } + + if (f1 == FX_IT_ROWDELAY) { + /* Don't allow the scan count for this row to overflow here. */ + int x = m->scan_cnt[ord][row] + (p1 & 0x0f); + m->scan_cnt[ord][row] = MIN(x, 255); + frame_count += (p1 & 0x0f) * speed; + } + + if (f1 == FX_IT_BREAK) { + break_row = p1; + last_row = 0; + } +#endif + + if (f1 == FX_JUMP || f2 == FX_JUMP) { + ord2 = (f1 == FX_JUMP) ? p1 : p2; + break_row = 0; + last_row = 0; + + /* prevent infinite loop, see OpenMPT PatLoop-Various.xm */ + inside_loop = 0; + } + + if (f1 == FX_BREAK || f2 == FX_BREAK) { + parm = (f1 == FX_BREAK) ? p1 : p2; + break_row = 10 * MSN(parm) + LSN(parm); + last_row = 0; + } + +#ifndef LIBXMP_CORE_PLAYER + /* Archimedes line jump */ + if (f1 == FX_LINE_JUMP || f2 == FX_LINE_JUMP) { + /* Don't set order if preceded by jump or break. */ + if (last_row > 0) + ord2 = ord; + parm = (f1 == FX_LINE_JUMP) ? p1 : p2; + break_row = parm; + last_row = 0; + line_jump = 1; + } +#endif + + if (f1 == FX_EXTENDED || f2 == FX_EXTENDED) { + parm = (f1 == FX_EXTENDED) ? p1 : p2; + + if ((parm >> 4) == EX_PATT_DELAY) { + if (m->read_event_type != READ_EVENT_ST3 || !pdelay) { + pdelay = parm & 0x0f; + frame_count += pdelay * speed; + } + } + + if ((parm >> 4) == EX_PATTERN_LOOP) { + if (parm &= 0x0f) { + /* Loop end */ + if (loop_count[chn]) { + if (--loop_count[chn]) { + /* next iteraction */ + loop_chn = chn; + } else { + /* finish looping */ + loop_num--; + inside_loop = 0; + if (m->quirk & QUIRK_S3MLOOP) + loop_row[chn] = row; + } + } else { + loop_count[chn] = parm; + loop_chn = chn; + loop_num++; + } + } else { + /* Loop start */ + loop_row[chn] = row - 1; + inside_loop = 1; + if (HAS_QUIRK(QUIRK_FT2BUGS)) + break_row = row; + } + } + } + } + + if (loop_chn >= 0) { + row = loop_row[loop_chn]; + loop_chn = -1; + } + +#ifndef LIBXMP_CORE_PLAYER + if (st26_speed) { + frame_count += row_count * speed; + row_count = 0; + if (st26_speed & 0x10000) { + speed = (st26_speed & 0xff00) >> 8; + } else { + speed = st26_speed & 0xff; + } + st26_speed ^= 0x10000; + } +#endif + } + + if (break_row && pdelay) { + break_row++; + } + + if (ord2 >= 0) { + ord = ord2 - 1; + ord2 = -1; + } + + frame_count += row_count * speed; + row_count_total = 0; + row_count = 0; + } + row = break_row; + +end_module: + + /* Sanity check */ + { + if (!any_valid) { + return -1; + } + pat = mod->xxo[ord]; + if (pat >= mod->pat || row >= mod->xxp[pat]->rows) { + row = 0; + } + } + + p->scan[chain].num = m->scan_cnt[ord][row]; + p->scan[chain].row = row; + p->scan[chain].ord = ord; + + time -= start_time; + frame_count += row_count * speed; + + return (time + m->time_factor * frame_count * base_time / bpm); +} + +static void reset_scan_data(struct context_data *ctx) +{ + int i; + for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) { + ctx->m.xxo_info[i].time = -1; + } + memset(ctx->p.sequence_control, 0xff, XMP_MAX_MOD_LENGTH); +} + +#ifndef LIBXMP_CORE_PLAYER +static void compare_vblank_scan(struct context_data *ctx) +{ + /* Calculate both CIA and VBlank time for certain long MODs + * and pick the more likely (i.e. shorter) one. The same logic + * works regardless of the initial mode selected--either way, + * the wrong timing mode usually makes modules MUCH longer. */ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct ord_data *info_backup; + struct scan_data scan_backup; + unsigned char ctrl_backup[256]; + + if ((info_backup = (struct ord_data *)malloc(sizeof(m->xxo_info))) != NULL) { + /* Back up the current info to avoid a third scan. */ + scan_backup = p->scan[0]; + memcpy(info_backup, m->xxo_info, sizeof(m->xxo_info)); + memcpy(ctrl_backup, p->sequence_control, + sizeof(p->sequence_control)); + + reset_scan_data(ctx); + + m->quirk ^= QUIRK_NOBPM; + p->scan[0].time = scan_module(ctx, 0, 0); + + D_(D_INFO "%-6s %dms", !HAS_QUIRK(QUIRK_NOBPM)?"VBlank":"CIA", scan_backup.time); + D_(D_INFO "%-6s %dms", HAS_QUIRK(QUIRK_NOBPM)?"VBlank":"CIA", p->scan[0].time); + + if (p->scan[0].time >= scan_backup.time) { + m->quirk ^= QUIRK_NOBPM; + p->scan[0] = scan_backup; + memcpy(m->xxo_info, info_backup, sizeof(m->xxo_info)); + memcpy(p->sequence_control, ctrl_backup, + sizeof(p->sequence_control)); + } + + free(info_backup); + } +} +#endif + +int libxmp_get_sequence(struct context_data *ctx, int ord) +{ + struct player_data *p = &ctx->p; + return p->sequence_control[ord]; +} + +int libxmp_scan_sequences(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct scan_data *s; + int i, ep; + int seq; + unsigned char temp_ep[XMP_MAX_MOD_LENGTH]; + + s = (struct scan_data *) realloc(p->scan, MAX(1, mod->len) * sizeof(struct scan_data)); + if (!s) { + D_(D_CRIT "failed to allocate scan data"); + return -1; + } + p->scan = s; + + /* Initialize order data to prevent overwrite when a position is used + * multiple times at different starting points (see janosik.xm). + */ + reset_scan_data(ctx); + + ep = 0; + temp_ep[0] = 0; + p->scan[0].time = scan_module(ctx, ep, 0); + seq = 1; + +#ifndef LIBXMP_CORE_PLAYER + if (m->compare_vblank && !(p->flags & XMP_FLAGS_VBLANK) && + p->scan[0].time >= VBLANK_TIME_THRESHOLD) { + compare_vblank_scan(ctx); + } +#endif + + if (p->scan[0].time < 0) { + D_(D_CRIT "scan was not able to find any valid orders"); + return -1; + } + + while (1) { + /* Scan song starting at given entry point */ + /* Check if any patterns left */ + for (i = 0; i < mod->len; i++) { + if (p->sequence_control[i] == 0xff) { + break; + } + } + if (i != mod->len && seq < MAX_SEQUENCES) { + /* New entry point */ + ep = i; + temp_ep[seq] = ep; + p->scan[seq].time = scan_module(ctx, ep, seq); + if (p->scan[seq].time > 0) + seq++; + } else { + break; + } + } + + if (seq < mod->len) { + s = (struct scan_data *) realloc(p->scan, seq * sizeof(struct scan_data)); + if (s != NULL) { + p->scan = s; + } + } + m->num_sequences = seq; + + /* Now place entry points in the public accessible array */ + for (i = 0; i < m->num_sequences; i++) { + m->seq_data[i].entry_point = temp_ep[i]; + m->seq_data[i].duration = p->scan[i].time; + } + + return 0; +} diff --git a/thirdparty/libxmp/src/smix.c b/thirdparty/libxmp/src/smix.c new file mode 100644 index 000000000..007734526 --- /dev/null +++ b/thirdparty/libxmp/src/smix.c @@ -0,0 +1,335 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "period.h" +#include "player.h" +#include "hio.h" +#include "loaders/loader.h" + + +struct xmp_instrument *libxmp_get_instrument(struct context_data *ctx, int ins) +{ + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_instrument *xxi; + + if (ins < 0) { + xxi = NULL; + } else if (ins < mod->ins) { + xxi = &mod->xxi[ins]; + } else if (ins < mod->ins + smix->ins) { + xxi = &smix->xxi[ins - mod->ins]; + } else { + xxi = NULL; + } + + return xxi; +} + +struct xmp_sample *libxmp_get_sample(struct context_data *ctx, int smp) +{ + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_sample *xxs; + + if (smp < 0) { + xxs = NULL; + } else if (smp < mod->smp) { + xxs = &mod->xxs[smp]; + } else if (smp < mod->smp + smix->smp) { + xxs = &smix->xxs[smp - mod->smp]; + } else { + xxs = NULL; + } + + return xxs; +} + +int xmp_start_smix(xmp_context opaque, int chn, int smp) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct smix_data *smix = &ctx->smix; + + if (ctx->state > XMP_STATE_LOADED) { + return -XMP_ERROR_STATE; + } + + smix->xxi = (struct xmp_instrument *) calloc(smp, sizeof(struct xmp_instrument)); + if (smix->xxi == NULL) { + goto err; + } + smix->xxs = (struct xmp_sample *) calloc(smp, sizeof(struct xmp_sample)); + if (smix->xxs == NULL) { + goto err1; + } + + smix->chn = chn; + smix->ins = smix->smp = smp; + + return 0; + + err1: + free(smix->xxi); + smix->xxi = NULL; + err: + return -XMP_ERROR_INTERNAL; +} + +int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int chn) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + + if (ctx->state < XMP_STATE_PLAYING) { + return -XMP_ERROR_STATE; + } + + if (chn >= smix->chn || chn < 0 || ins >= mod->ins || ins < 0) { + return -XMP_ERROR_INVALID; + } + + if (note == 0) { + note = 60; /* middle C note number */ + } + + event = &p->inject_event[mod->chn + chn]; + memset(event, 0, sizeof (struct xmp_event)); + event->note = (note < XMP_MAX_KEYS) ? note + 1 : note; + event->ins = ins + 1; + event->vol = vol + 1; + event->_flag = 1; + + return 0; +} + +int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_module *mod = &m->mod; + struct xmp_event *event; + + if (ctx->state < XMP_STATE_PLAYING) { + return -XMP_ERROR_STATE; + } + + if (chn >= smix->chn || chn < 0 || ins >= smix->ins || ins < 0) { + return -XMP_ERROR_INVALID; + } + + if (note == 0) { + note = 60; /* middle C note number */ + } + + event = &p->inject_event[mod->chn + chn]; + memset(event, 0, sizeof (struct xmp_event)); + event->note = (note < XMP_MAX_KEYS) ? note + 1 : note; + event->ins = mod->ins + ins + 1; + event->vol = vol + 1; + event->_flag = 1; + + return 0; +} + +int xmp_smix_channel_pan(xmp_context opaque, int chn, int pan) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct player_data *p = &ctx->p; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct channel_data *xc; + + if (chn >= smix->chn || pan < 0 || pan > 255) { + return -XMP_ERROR_INVALID; + } + + xc = &p->xc_data[m->mod.chn + chn]; + xc->pan.val = pan; + + return 0; +} + +int xmp_smix_load_sample(xmp_context opaque, int num, const char *path) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct smix_data *smix = &ctx->smix; + struct module_data *m = &ctx->m; + struct xmp_instrument *xxi; + struct xmp_sample *xxs; + HIO_HANDLE *h; + uint32 magic; + int chn, rate, bits, size; + int retval = -XMP_ERROR_INTERNAL; + + if (num >= smix->ins) { + retval = -XMP_ERROR_INVALID; + goto err; + } + + xxi = &smix->xxi[num]; + xxs = &smix->xxs[num]; + + h = hio_open(path, "rb"); + if (h == NULL) { + retval = -XMP_ERROR_SYSTEM; + goto err; + } + + /* Init instrument */ + + xxi->sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument)); + if (xxi->sub == NULL) { + retval = -XMP_ERROR_SYSTEM; + goto err1; + } + + xxi->vol = m->volbase; + xxi->nsm = 1; + xxi->sub[0].sid = num; + xxi->sub[0].vol = xxi->vol; + xxi->sub[0].pan = 0x80; + + /* Load sample */ + + magic = hio_read32b(h); + if (magic != 0x52494646) { /* RIFF */ + retval = -XMP_ERROR_FORMAT; + goto err2; + } + + if (hio_seek(h, 22, SEEK_SET) < 0) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + chn = hio_read16l(h); + if (chn != 1) { + retval = -XMP_ERROR_FORMAT; + goto err2; + } + + rate = hio_read32l(h); + if (rate == 0) { + retval = -XMP_ERROR_FORMAT; + goto err2; + } + + if (hio_seek(h, 34, SEEK_SET) < 0) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + bits = hio_read16l(h); + if (bits == 0) { + retval = -XMP_ERROR_FORMAT; + goto err2; + } + + if (hio_seek(h, 40, SEEK_SET) < 0) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + size = hio_read32l(h); + if (size == 0) { + retval = -XMP_ERROR_FORMAT; + goto err2; + } + + libxmp_c2spd_to_note(rate, &xxi->sub[0].xpo, &xxi->sub[0].fin); + + xxs->len = 8 * size / bits; + xxs->lps = 0; + xxs->lpe = 0; + xxs->flg = bits == 16 ? XMP_SAMPLE_16BIT : 0; + + xxs->data = (unsigned char *) malloc(size + 8); + if (xxs->data == NULL) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + + /* ugly hack to make the interpolator happy */ + memset(xxs->data, 0, 4); + memset(xxs->data + 4 + size, 0, 4); + xxs->data += 4; + + if (hio_seek(h, 44, SEEK_SET) < 0) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + if (hio_read(xxs->data, 1, size, h) != size) { + retval = -XMP_ERROR_SYSTEM; + goto err2; + } + hio_close(h); + + return 0; + + err2: + free(xxi->sub); + xxi->sub = NULL; + err1: + hio_close(h); + err: + return retval; +} + +int xmp_smix_release_sample(xmp_context opaque, int num) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct smix_data *smix = &ctx->smix; + + if (num >= smix->ins) { + return -XMP_ERROR_INVALID; + } + + libxmp_free_sample(&smix->xxs[num]); + free(smix->xxi[num].sub); + + smix->xxs[num].data = NULL; + smix->xxi[num].sub = NULL; + + return 0; +} + +void xmp_end_smix(xmp_context opaque) +{ + struct context_data *ctx = (struct context_data *)opaque; + struct smix_data *smix = &ctx->smix; + int i; + + for (i = 0; i < smix->smp; i++) { + xmp_smix_release_sample(opaque, i); + } + + free(smix->xxs); + free(smix->xxi); + smix->xxs = NULL; + smix->xxi = NULL; +} diff --git a/thirdparty/libxmp/src/tempfile.c b/thirdparty/libxmp/src/tempfile.c new file mode 100644 index 000000000..6b0582085 --- /dev/null +++ b/thirdparty/libxmp/src/tempfile.c @@ -0,0 +1,165 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef __SUNPRO_C +#pragma error_messages (off,E_EMPTY_TRANSLATION_UNIT) +#endif + +#include "common.h" + +#if !(defined(LIBXMP_NO_PROWIZARD) && defined(LIBXMP_NO_DEPACKERS)) + +#if defined(_MSC_VER) || defined(__WATCOMC__) +#include +#else +#include +#endif +#ifdef HAVE_UMASK +#include +#include +#endif + +#include "tempfile.h" + +#ifdef _WIN32 + +int mkstemp(char *); + +static int get_temp_dir(char *buf, size_t size) +{ + static const char def[] = "C:\\WINDOWS\\TEMP"; + const char *tmp = getenv("TEMP"); + snprintf(buf, size, "%s\\", (tmp != NULL)? tmp : def); + return 0; +} + +#elif defined(__OS2__) || defined(__EMX__) + +static int get_temp_dir(char *buf, size_t size) +{ + static const char def[] = "C:"; + const char *tmp = getenv("TMP"); + snprintf(buf, size, "%s\\", (tmp != NULL)? tmp : def); + return 0; +} + +#elif defined(__MSDOS__) || defined(_DOS) + +static int get_temp_dir(char *buf, size_t size) +{ + strcpy(buf, "C:\\"); /* size-safe against XMP_MAXPATH */ + return 0; +} + +#elif defined LIBXMP_AMIGA + +static int get_temp_dir(char *buf, size_t size) +{ + strcpy(buf, "T:"); /* size-safe against XMP_MAXPATH */ + return 0; +} + +#elif defined __ANDROID__ + +#include +#include + +static int get_temp_dir(char *buf, size_t size) +{ +#define APPDIR "/sdcard/Xmp for Android" + struct stat st; + if (stat(APPDIR, &st) < 0) { + if (mkdir(APPDIR, 0777) < 0) + return -1; + } + if (stat(APPDIR "/tmp", &st) < 0) { + if (mkdir(APPDIR "/tmp", 0777) < 0) + return -1; + } + strncpy(buf, APPDIR "/tmp/", size); + + return 0; +} + +#else /* unix */ + +static int get_temp_dir(char *buf, size_t size) +{ + const char *tmp = getenv("TMPDIR"); + + if (tmp) { + snprintf(buf, size, "%s/", tmp); + } else { + strncpy(buf, "/tmp/", size); + } + + return 0; +} + +#endif + + +FILE *make_temp_file(char **filename) { + char tmp[XMP_MAXPATH]; + FILE *temp; + int fd; + + if (get_temp_dir(tmp, XMP_MAXPATH) < 0) + return NULL; + + strncat(tmp, "xmp_XXXXXX", XMP_MAXPATH - 10); + + if ((*filename = libxmp_strdup(tmp)) == NULL) + goto err; + +#ifdef HAVE_UMASK + umask(0177); +#endif + if ((fd = mkstemp(*filename)) < 0) + goto err2; + + if ((temp = fdopen(fd, "w+b")) == NULL) + goto err3; + + return temp; + + err3: + close(fd); + err2: + free(*filename); + err: + return NULL; +} + +/* + * Windows doesn't allow you to unlink an open file, so we changed the + * temp file cleanup system to remove temporary files after we close it + */ +void unlink_temp_file(char *temp) +{ + if (temp) { + unlink(temp); + free(temp); + } +} + +#endif diff --git a/thirdparty/libxmp/src/tempfile.h b/thirdparty/libxmp/src/tempfile.h new file mode 100644 index 000000000..f3c8f77d9 --- /dev/null +++ b/thirdparty/libxmp/src/tempfile.h @@ -0,0 +1,7 @@ +#ifndef XMP_PLATFORM_H +#define XMP_PLATFORM_H + +FILE *make_temp_file(char **); +void unlink_temp_file(char *); + +#endif diff --git a/thirdparty/libxmp/src/virtual.c b/thirdparty/libxmp/src/virtual.c new file mode 100644 index 000000000..ed9a69f8e --- /dev/null +++ b/thirdparty/libxmp/src/virtual.c @@ -0,0 +1,626 @@ +/* Extended Module Player + * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common.h" +#include "virtual.h" +#include "mixer.h" + +#ifdef LIBXMP_PAULA_SIMULATOR +#include "paula.h" +#endif + +#define FREE -1 + +/* For virt_pastnote() */ +void libxmp_player_set_release(struct context_data *, int); +void libxmp_player_set_fadeout(struct context_data *, int); + + +/* Get parent channel */ +int libxmp_virt_getroot(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi; + int voc; + + voc = p->virt.virt_channel[chn].map; + if (voc < 0) { + return -1; + } + + vi = &p->virt.voice_array[voc]; + + return vi->root; +} + +void libxmp_virt_resetvoice(struct context_data *ctx, int voc, int mute) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[voc]; +#ifdef LIBXMP_PAULA_SIMULATOR + struct paula_state *paula; +#endif + + if ((uint32)voc >= p->virt.maxvoc) { + return; + } + + if (mute) { + libxmp_mixer_setvol(ctx, voc, 0); + } + + p->virt.virt_used--; + p->virt.virt_channel[vi->root].count--; + p->virt.virt_channel[vi->chn].map = FREE; +#ifdef LIBXMP_PAULA_SIMULATOR + paula = vi->paula; +#endif + memset(vi, 0, sizeof(struct mixer_voice)); +#ifdef LIBXMP_PAULA_SIMULATOR + vi->paula = paula; +#endif + vi->chn = vi->root = FREE; +} + +/* virt_on (number of tracks) */ +int libxmp_virt_on(struct context_data *ctx, int num) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + int i; + + p->virt.num_tracks = num; + num = libxmp_mixer_numvoices(ctx, -1); + + p->virt.virt_channels = p->virt.num_tracks; + + if (HAS_QUIRK(QUIRK_VIRTUAL)) { + p->virt.virt_channels += num; + } else if (num > p->virt.virt_channels) { + num = p->virt.virt_channels; + } + + p->virt.maxvoc = libxmp_mixer_numvoices(ctx, num); + + p->virt.voice_array = (struct mixer_voice *) calloc(p->virt.maxvoc, + sizeof(struct mixer_voice)); + if (p->virt.voice_array == NULL) + goto err; + + for (i = 0; i < p->virt.maxvoc; i++) { + p->virt.voice_array[i].chn = FREE; + p->virt.voice_array[i].root = FREE; + } + +#ifdef LIBXMP_PAULA_SIMULATOR + /* Initialize Paula simulator */ + if (IS_AMIGA_MOD()) { + for (i = 0; i < p->virt.maxvoc; i++) { + p->virt.voice_array[i].paula = (struct paula_state *) calloc(1, sizeof(struct paula_state)); + if (p->virt.voice_array[i].paula == NULL) { + goto err2; + } + libxmp_paula_init(ctx, p->virt.voice_array[i].paula); + } + } +#endif + + p->virt.virt_channel = (struct virt_channel *) malloc(p->virt.virt_channels * + sizeof(struct virt_channel)); + if (p->virt.virt_channel == NULL) + goto err2; + + for (i = 0; i < p->virt.virt_channels; i++) { + p->virt.virt_channel[i].map = FREE; + p->virt.virt_channel[i].count = 0; + } + + p->virt.virt_used = 0; + + return 0; + + err2: +#ifdef LIBXMP_PAULA_SIMULATOR + if (IS_AMIGA_MOD()) { + for (i = 0; i < p->virt.maxvoc; i++) { + free(p->virt.voice_array[i].paula); + } + } +#endif + free(p->virt.voice_array); + p->virt.voice_array = NULL; + err: + return -1; +} + +void libxmp_virt_off(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; +#ifdef LIBXMP_PAULA_SIMULATOR + struct module_data *m = &ctx->m; + int i; +#endif + +#ifdef LIBXMP_PAULA_SIMULATOR + /* Free Paula simulator state */ + if (IS_AMIGA_MOD()) { + for (i = 0; i < p->virt.maxvoc; i++) { + free(p->virt.voice_array[i].paula); + } + } +#endif + + p->virt.virt_used = p->virt.maxvoc = 0; + p->virt.virt_channels = 0; + p->virt.num_tracks = 0; + + free(p->virt.voice_array); + free(p->virt.virt_channel); + p->virt.voice_array = NULL; + p->virt.virt_channel = NULL; +} + +void libxmp_virt_reset(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + int i; + + if (p->virt.virt_channels < 1) { + return; + } + + /* CID 129203 (#1 of 1): Useless call (USELESS_CALL) + * Call is only useful for its return value, which is ignored. + * + * libxmp_mixer_numvoices(ctx, p->virt.maxvoc); + */ + + for (i = 0; i < p->virt.maxvoc; i++) { + struct mixer_voice *vi = &p->virt.voice_array[i]; +#ifdef LIBXMP_PAULA_SIMULATOR + struct paula_state *paula = vi->paula; +#endif + memset(vi, 0, sizeof(struct mixer_voice)); +#ifdef LIBXMP_PAULA_SIMULATOR + vi->paula = paula; +#endif + vi->chn = FREE; + vi->root = FREE; + } + + for (i = 0; i < p->virt.virt_channels; i++) { + p->virt.virt_channel[i].map = FREE; + p->virt.virt_channel[i].count = 0; + } + + p->virt.virt_used = 0; +} + +static int free_voice(struct context_data *ctx) +{ + struct player_data *p = &ctx->p; + int i, num, vol; + + /* Find background voice with lowest volume*/ + num = FREE; + vol = INT_MAX; + for (i = 0; i < p->virt.maxvoc; i++) { + struct mixer_voice *vi = &p->virt.voice_array[i]; + + if (vi->chn >= p->virt.num_tracks && vi->vol < vol) { + num = i; + vol = vi->vol; + } + } + + /* Free voice */ + if (num >= 0) { + p->virt.virt_channel[p->virt.voice_array[num].chn].map = FREE; + p->virt.virt_channel[p->virt.voice_array[num].root].count--; + p->virt.virt_used--; + } + + return num; +} + +static int alloc_voice(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + int i; + + /* Find free voice */ + for (i = 0; i < p->virt.maxvoc; i++) { + if (p->virt.voice_array[i].chn == FREE) + break; + } + + /* not found */ + if (i == p->virt.maxvoc) { + i = free_voice(ctx); + } + + if (i >= 0) { + p->virt.virt_channel[chn].count++; + p->virt.virt_used++; + + p->virt.voice_array[i].chn = chn; + p->virt.voice_array[i].root = chn; + p->virt.virt_channel[chn].map = i; + } + + return i; +} + +static int map_virt_channel(struct player_data *p, int chn) +{ + int voc; + + if ((uint32)chn >= p->virt.virt_channels) + return -1; + + voc = p->virt.virt_channel[chn].map; + + if ((uint32)voc >= p->virt.maxvoc) + return -1; + + return voc; +} + +int libxmp_virt_mapchannel(struct context_data *ctx, int chn) +{ + return map_virt_channel(&ctx->p, chn); +} + +void libxmp_virt_resetchannel(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi; +#ifdef LIBXMP_PAULA_SIMULATOR + struct paula_state *paula; +#endif + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) + return; + + libxmp_mixer_setvol(ctx, voc, 0); + + p->virt.virt_used--; + p->virt.virt_channel[p->virt.voice_array[voc].root].count--; + p->virt.virt_channel[chn].map = FREE; + + vi = &p->virt.voice_array[voc]; +#ifdef LIBXMP_PAULA_SIMULATOR + paula = vi->paula; +#endif + memset(vi, 0, sizeof(struct mixer_voice)); +#ifdef LIBXMP_PAULA_SIMULATOR + vi->paula = paula; +#endif + vi->chn = vi->root = FREE; +} + +void libxmp_virt_setvol(struct context_data *ctx, int chn, int vol) +{ + struct player_data *p = &ctx->p; + int voc, root; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + root = p->virt.voice_array[voc].root; + if (root < XMP_MAX_CHANNELS && p->channel_mute[root]) { + vol = 0; + } + + libxmp_mixer_setvol(ctx, voc, vol); + + if (vol == 0 && chn >= p->virt.num_tracks) { + libxmp_virt_resetvoice(ctx, voc, 1); + } +} + +void libxmp_virt_release(struct context_data *ctx, int chn, int rel) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_release(ctx, voc, rel); +} + +void libxmp_virt_reverse(struct context_data *ctx, int chn, int rev) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_reverse(ctx, voc, rev); +} + +void libxmp_virt_setpan(struct context_data *ctx, int chn, int pan) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_setpan(ctx, voc, pan); +} + +void libxmp_virt_seteffect(struct context_data *ctx, int chn, int type, int val) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_seteffect(ctx, voc, type, val); +} + +double libxmp_virt_getvoicepos(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return -1; + } + + return libxmp_mixer_getvoicepos(ctx, voc); +} + +#ifndef LIBXMP_CORE_PLAYER + +void libxmp_virt_setsmp(struct context_data *ctx, int chn, int smp) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi; + double pos; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + vi = &p->virt.voice_array[voc]; + if (vi->smp == smp) { + return; + } + + pos = libxmp_mixer_getvoicepos(ctx, voc); + libxmp_mixer_setpatch(ctx, voc, smp, 0); + libxmp_mixer_voicepos(ctx, voc, pos, 0); /* Restore old position */ +} + +#endif + +#ifndef LIBXMP_CORE_DISABLE_IT + +void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna) +{ + struct player_data *p = &ctx->p; + struct module_data *m = &ctx->m; + int voc; + + if (!HAS_QUIRK(QUIRK_VIRTUAL)) { + return; + } + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + p->virt.voice_array[voc].act = nna; +} + +static void check_dct(struct context_data *ctx, int i, int chn, int ins, + int smp, int key, int nna, int dct, int dca) +{ + struct player_data *p = &ctx->p; + struct mixer_voice *vi = &p->virt.voice_array[i]; + int voc; + + voc = p->virt.virt_channel[chn].map; + + if (vi->root == chn && vi->ins == ins) { + + if (nna == XMP_INST_NNA_CUT) { + libxmp_virt_resetvoice(ctx, i, 1); + return; + } + + vi->act = nna; + + if ((dct == XMP_INST_DCT_INST) || + (dct == XMP_INST_DCT_SMP && vi->smp == smp) || + (dct == XMP_INST_DCT_NOTE && vi->key == key)) { + + if (nna == XMP_INST_NNA_OFF && dca == XMP_INST_DCA_FADE) { + vi->act = VIRT_ACTION_OFF; + } else if (dca) { + if (i != voc || vi->act) { + vi->act = dca; + } + } else { + libxmp_virt_resetvoice(ctx, i, 1); + } + } + } +} + +#endif + +/* For note slides */ +void libxmp_virt_setnote(struct context_data *ctx, int chn, int note) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_setnote(ctx, voc, note); +} + +int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp, + int note, int key, int nna, int dct, int dca) +{ + struct player_data *p = &ctx->p; + int voc, vfree; + + if ((uint32)chn >= p->virt.virt_channels) { + return -1; + } + + if (ins < 0) { + smp = -1; + } + +#ifndef LIBXMP_CORE_DISABLE_IT + if (dct) { + int i; + + for (i = 0; i < p->virt.maxvoc; i++) { + check_dct(ctx, i, chn, ins, smp, key, nna, dct, dca); + } + } +#endif + + voc = p->virt.virt_channel[chn].map; + + if (voc > FREE) { + if (p->virt.voice_array[voc].act) { + vfree = alloc_voice(ctx, chn); + + if (vfree < 0) { + return -1; + } + + for (chn = p->virt.num_tracks; chn < p->virt.virt_channels && + p->virt.virt_channel[chn++].map > FREE;) ; + + p->virt.voice_array[voc].chn = --chn; + p->virt.virt_channel[chn].map = voc; + voc = vfree; + } + } else { + voc = alloc_voice(ctx, chn); + if (voc < 0) { + return -1; + } + } + + if (smp < 0) { + libxmp_virt_resetvoice(ctx, voc, 1); + return chn; /* was -1 */ + } + + libxmp_mixer_setpatch(ctx, voc, smp, 1); + libxmp_mixer_setnote(ctx, voc, note); + p->virt.voice_array[voc].ins = ins; + p->virt.voice_array[voc].act = nna; + p->virt.voice_array[voc].key = key; + + return chn; +} + +void libxmp_virt_setperiod(struct context_data *ctx, int chn, double period) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_setperiod(ctx, voc, period); +} + +void libxmp_virt_voicepos(struct context_data *ctx, int chn, double pos) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return; + } + + libxmp_mixer_voicepos(ctx, voc, pos, 1); +} + +#ifndef LIBXMP_CORE_DISABLE_IT + +void libxmp_virt_pastnote(struct context_data *ctx, int chn, int act) +{ + struct player_data *p = &ctx->p; + int c, voc; + + for (c = p->virt.num_tracks; c < p->virt.virt_channels; c++) { + if ((voc = map_virt_channel(p, c)) < 0) + continue; + + if (p->virt.voice_array[voc].root == chn) { + switch (act) { + case VIRT_ACTION_CUT: + libxmp_virt_resetvoice(ctx, voc, 1); + break; + case VIRT_ACTION_OFF: + libxmp_player_set_release(ctx, c); + break; + case VIRT_ACTION_FADE: + libxmp_player_set_fadeout(ctx, c); + break; + } + } + } +} + +#endif + +int libxmp_virt_cstat(struct context_data *ctx, int chn) +{ + struct player_data *p = &ctx->p; + int voc; + + if ((voc = map_virt_channel(p, chn)) < 0) { + return VIRT_INVALID; + } + + if (chn < p->virt.num_tracks) { + return VIRT_ACTIVE; + } + + return p->virt.voice_array[voc].act; +} diff --git a/thirdparty/libxmp/src/virtual.h b/thirdparty/libxmp/src/virtual.h new file mode 100644 index 000000000..88a8cdcfb --- /dev/null +++ b/thirdparty/libxmp/src/virtual.h @@ -0,0 +1,39 @@ +#ifndef LIBXMP_VIRTUAL_H +#define LIBXMP_VIRTUAL_H + +#include "common.h" + +#define VIRT_ACTION_CUT XMP_INST_NNA_CUT +#define VIRT_ACTION_CONT XMP_INST_NNA_CONT +#define VIRT_ACTION_OFF XMP_INST_NNA_OFF +#define VIRT_ACTION_FADE XMP_INST_NNA_FADE + +#define VIRT_ACTIVE 0x100 +#define VIRT_INVALID -1 + +int libxmp_virt_on (struct context_data *, int); +void libxmp_virt_off (struct context_data *); +int libxmp_virt_mute (struct context_data *, int, int); +int libxmp_virt_setpatch (struct context_data *, int, int, int, int, + int, int, int, int); +int libxmp_virt_cvt8bit (void); +void libxmp_virt_setnote (struct context_data *, int, int); +void libxmp_virt_setsmp (struct context_data *, int, int); +void libxmp_virt_setnna (struct context_data *, int, int); +void libxmp_virt_pastnote (struct context_data *, int, int); +void libxmp_virt_setvol (struct context_data *, int, int); +void libxmp_virt_voicepos (struct context_data *, int, double); +double libxmp_virt_getvoicepos (struct context_data *, int); +void libxmp_virt_setperiod (struct context_data *, int, double); +void libxmp_virt_setpan (struct context_data *, int, int); +void libxmp_virt_seteffect (struct context_data *, int, int, int); +int libxmp_virt_cstat (struct context_data *, int); +int libxmp_virt_mapchannel (struct context_data *, int); +void libxmp_virt_resetchannel(struct context_data *, int); +void libxmp_virt_resetvoice (struct context_data *, int, int); +void libxmp_virt_reset (struct context_data *); +void libxmp_virt_release (struct context_data *, int, int); +void libxmp_virt_reverse (struct context_data *, int, int); +int libxmp_virt_getroot (struct context_data *, int); + +#endif /* LIBXMP_VIRTUAL_H */ diff --git a/thirdparty/libxmp/src/win32.c b/thirdparty/libxmp/src/win32.c new file mode 100644 index 000000000..88ac97d19 --- /dev/null +++ b/thirdparty/libxmp/src/win32.c @@ -0,0 +1,47 @@ +/* _[v]snprintf() from msvcrt.dll might not nul terminate */ +/* OpenWatcom-provided versions seem to behave the same... */ + +#include "common.h" + +#if defined(USE_LIBXMP_SNPRINTF) + +#undef snprintf +#undef vsnprintf + +int libxmp_vsnprintf(char *str, size_t sz, const char *fmt, va_list ap) +{ + int rc = _vsnprintf(str, sz, fmt, ap); + if (sz != 0) { + if (rc < 0) rc = (int)sz; + if ((size_t)rc >= sz) str[sz - 1] = '\0'; + } + return rc; +} + +int libxmp_snprintf (char *str, size_t sz, const char *fmt, ...) +{ + va_list ap; + int rc; + + va_start (ap, fmt); + rc = _vsnprintf(str, sz, fmt, ap); + va_end (ap); + + return rc; +} + +#endif + +/* Win32 debug message helper by Mirko Buffoni */ +#if defined(_MSC_VER) && defined(DEBUG) +void libxmp_msvc_dbgprint(const char *format, ...) +{ + va_list argptr; + + /* do the output */ + va_start(argptr, format); + vprintf(format, argptr); + printf("\n"); + va_end(argptr); +} +#endif diff --git a/thirdparty/miniz/CMakeLists.txt b/thirdparty/miniz/CMakeLists.txt new file mode 100644 index 000000000..5d90673d3 --- /dev/null +++ b/thirdparty/miniz/CMakeLists.txt @@ -0,0 +1,9 @@ +make_release_only() + +if (MSVC) + set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4244" ) +endif() + +add_library( miniz OBJECT miniz.c ) +target_compile_definitions( miniz PRIVATE -DMINIZ_NO_STDIO ) +target_include_directories( miniz INTERFACE "$" ) diff --git a/thirdparty/miniz/miniz.c b/thirdparty/miniz/miniz.c new file mode 100644 index 000000000..8d0032f9e --- /dev/null +++ b/thirdparty/miniz/miniz.c @@ -0,0 +1,7833 @@ +#include "miniz.h" +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API's */ + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + return (s2 << 16) + s1; +} + +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +#if 0 + mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) + { + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) + { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; + } +#elif defined(USE_EXTERNAL_MZCRC) +/* If USE_EXTERNAL_CRC is defined, an external module will export the + * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. + * Depending on the impl, it may be necessary to ~ the input/output crc values. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); +#else +/* Faster, but larger CPU cache footprint. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; + const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; + + while (buf_len >= 4) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; + pByte_buf += 4; + buf_len -= 4; + } + + while (buf_len) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + ++pByte_buf; + --buf_len; + } + + return ~crc32; +} +#endif + +void mz_free(void *p) +{ + MZ_FREE(p); +} + +MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) +{ + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) +{ + (void)opaque, (void)address; + MZ_FREE(address); +} +MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) +{ + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); +} + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +#ifndef MINIZ_NO_ZLIB_APIS + +#ifndef MINIZ_NO_DEFLATE_APIS + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((mz_uint64)(source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflateReset(mz_streamp pStream) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + + pDecomp = (inflate_state *)pStream->state; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + /* pDecomp->m_window_bits = window_bits */; + + return MZ_OK; +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for (;;) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) + { + /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} +int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)*pSource_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + *pSource_len = *pSource_len - stream.avail_in; + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_uncompress2(pDest, pDest_len, pSource, &source_len); +} + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +const char *mz_error(int err) +{ + static struct + { + int m_err; + const char *m_pDesc; + } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif /*MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +#ifndef MINIZ_NO_DEFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Compression (independent from all decompression API's) */ + +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = + { + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 + }; + +static const mz_uint8 s_tdefl_len_extra[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 + }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = + { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct +{ + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_ARR(hist); + for (i = 0; i < num_syms; i++) + { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) + { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) + { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) + { + if (leaf >= n || A[root].m_key < A[leaf].m_key) + { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) + { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) + { + used++; + root--; + } + while (avbl > used) + { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +/* Limits canonical Huffman code table's max code size. */ +enum +{ + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 +}; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) + { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_ARR(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) + { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_ARR(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do \ + { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) \ + { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) \ + { \ + if (rle_repeat_count < 3) \ + { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } \ + else \ + { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) \ + { \ + if (rle_z_count < 3) \ + { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } \ + else if (rle_z_count <= 10) \ + { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } \ + else \ + { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static const mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0]; + mz_uint match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64)); + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static const mz_uint s_tdefl_num_probes[11]; + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + const mz_uint8 cmf = 0x78; + mz_uint8 flg, flevel = 3; + mz_uint header, i, mz_un = sizeof(s_tdefl_num_probes) / sizeof(mz_uint); + + /* Determine compression level by reversing the process in tdefl_create_comp_flags_from_zip_params() */ + for (i = 0; i < mz_un; i++) + if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break; + + if (i < 2) + flevel = 0; + else if (i < 6) + flevel = 1; + else if (i == 6) + flevel = 2; + + header = cmf << 8 | (flevel << 6); + header += 31 - (header % 31); + flg = header & 0xFF; + + TDEFL_PUT_BITS(cmf, 8); + TDEFL_PUT_BITS(flg, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) + { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) + { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) + { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } + else + { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) + { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) +#endif +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) + continue; + p = s; + probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + if (!probe_len) + { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); + break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p) +{ + mz_uint32 ret; + memcpy(&ret, p, sizeof(mz_uint32)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) +#endif +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); +#else + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; +#endif + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) + { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) + { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) + { + MZ_CLEAR_ARR(d->m_hash); + MZ_CLEAR_ARR(d->m_next); + d->m_dict_size = 0; + } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_ARR(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + *d->m_pLZ_flags = 0; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_ARR(d->m_dict); + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do + { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ +#endif + +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) + { + MZ_FREE(pComp); + return NULL; + } + /* write dummy header */ + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + /* compress image data */ + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) + { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) + { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + /* write real header */ + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; + mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, + 0x0a, 0x1a, 0x0a, 0x00, 0x00, + 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x44, 0x41, + 0x54 }; + pnghdr[18] = (mz_uint8)(w >> 8); + pnghdr[19] = (mz_uint8)w; + pnghdr[22] = (mz_uint8)(h >> 8); + pnghdr[23] = (mz_uint8)h; + pnghdr[25] = chans[num_chans]; + pnghdr[33] = (mz_uint8)(*pLen_out >> 24); + pnghdr[34] = (mz_uint8)(*pLen_out >> 16); + pnghdr[35] = (mz_uint8)(*pLen_out >> 8); + pnghdr[36] = (mz_uint8)*pLen_out; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + /* write footer (IDAT CRC-32, followed by IEND chunk) */ + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) + { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + /* compute final size of file, grab compressed data buffer and return */ + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor *tdefl_compressor_alloc(void) +{ + return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +} + +void tdefl_compressor_free(tdefl_compressor *pComp) +{ + MZ_FREE(pComp); +} +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +#ifndef MINIZ_NO_INFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Decompression (completely independent from all compression API's) */ + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) \ + { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do \ + { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do \ + { \ + for (;;) \ + { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +#define TINFL_GET_BYTE(state_index, c) \ + do \ + { \ + while (pIn_buf_cur >= pIn_buf_end) \ + { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do \ + { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \ + do \ + { \ + temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) \ + { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } \ + else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ +#define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \ + do \ + { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) \ + { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) \ + { \ + TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \ + } \ + else \ + { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +static void tinfl_clear_tree(tinfl_decompressor *r) +{ + if (r->m_type == 0) + MZ_CLEAR_ARR(r->m_tree_0); + else if (r->m_type == 1) + MZ_CLEAR_ARR(r->m_tree_1); + else + MZ_CLEAR_ARR(r->m_tree_2); +} + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const mz_uint8 s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 }; + + mz_int16 *pTrees[3]; + mz_uint8 *pCode_sizes[3]; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) + { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + pTrees[0] = r->m_tree_0; + pTrees[1] = r->m_tree_1; + pTrees[2] = r->m_tree_2; + pCode_sizes[0] = r->m_code_size_0; + pCode_sizes[1] = r->m_code_size_1; + pCode_sizes[2] = r->m_code_size_2; + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)((size_t)1 << (8U + (r->m_zhdr0 >> 4))))); + if (counter) + { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) + { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) + { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) + { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_code_size_0; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_code_size_1, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) + { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_ARR(r->m_code_size_2); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) + { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; + mz_int16 *pLookUp; + mz_int16 *pTree; + mz_uint8 *pCode_size; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; + pLookUp = r->m_look_up[r->m_type]; + pTree = pTrees[r->m_type]; + pCode_size = pCode_sizes[r->m_type]; + MZ_CLEAR_ARR(total_syms); + TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0])); + tinfl_clear_tree(r); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pCode_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) + { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) + { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) + { + pLookUp[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + { + pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTree[-tree_cur - 1]) + { + pTree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + else + tree_cur = pTree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); + pTree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) + { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2); + if (dist < 16) + { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for (;;) + { + mz_uint8 *pSrc; + for (;;) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } +#else + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2); +#else + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; +#endif + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + while(counter>2) + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + counter -= 3; + } + if (counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ + + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + for (counter = 0; counter < 4; ++counter) + { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + + TINFL_CR_FINISH + +common_exit: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) + { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits); + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +/* Higher level helper functions. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + memset(pDict,0,TINFL_LZ_DICT_SIZE); + tinfl_init(&decomp); + for (;;) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +#ifndef MINIZ_NO_MALLOC +tinfl_decompressor *tinfl_decompressor_alloc(void) +{ + tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); + if (pDecomp) + tinfl_init(pDecomp); + return pDecomp; +} + +void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +{ + MZ_FREE(pDecomp); +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- .ZIP archive reading */ + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include + +#if defined(_MSC_VER) || defined(__MINGW64__) + +#define WIN32_LEAN_AND_MEAN +#include + +static WCHAR* mz_utf8z_to_widechar(const char* str) +{ + int reqChars = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + WCHAR* wStr = (WCHAR*)malloc(reqChars * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, str, -1, wStr, reqChars); + return wStr; +} + +static FILE *mz_fopen(const char *pFilename, const char *pMode) +{ + WCHAR* wFilename = mz_utf8z_to_widechar(pFilename); + WCHAR* wMode = mz_utf8z_to_widechar(pMode); + FILE* pFile = NULL; + errno_t err = _wfopen_s(&pFile, wFilename, wMode); + free(wFilename); + free(wMode); + return err ? NULL : pFile; +} + +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) +{ + WCHAR* wPath = mz_utf8z_to_widechar(pPath); + WCHAR* wMode = mz_utf8z_to_widechar(pMode); + FILE* pFile = NULL; + errno_t err = _wfreopen_s(&pFile, wPath, wMode, pStream); + free(wPath); + free(wMode); + return err ? NULL : pFile; +} + +static int mz_stat64(const char *path, struct __stat64 *buffer) +{ + WCHAR* wPath = mz_utf8z_to_widechar(path); + int res = _wstat64(wPath, buffer); + free(wPath); + return res; +} + +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat64 +#define MZ_FILE_STAT mz_stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove + +#elif defined(__MINGW32__) || defined(__WATCOMC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__USE_LARGEFILE64) /* gcc, clang */ +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__APPLE__) || defined(__FreeBSD__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +#define MZ_DELETE_FILE remove + +#else +#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#ifdef __STRICT_ANSI__ +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#else +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif /* #ifdef _MSC_VER */ +#endif /* #ifdef MINIZ_NO_STDIO */ + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +enum +{ + /* ZIP archive identifiers and record sizes */ + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + /* Central directory header record offsets */ + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + + /* Local directory header offsets */ + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + + /* End of central directory offsets */ + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + mz_uint32 m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +#if defined(DEBUG) || defined(_DEBUG) +static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) +{ + MZ_ASSERT(index < pArray->m_size); + return index; +} +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +#else +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] +#endif + +static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +{ + memset(pArray, 0, sizeof(mz_zip_array)); + pArray->m_element_size = element_size; +} + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) + { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + if (n > 0) + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +{ + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) +{ + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + pZip->m_pState->m_init_flags = flags; + pZip->m_pState->m_zip64 = MZ_FALSE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do \ + { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices; + mz_uint32 start, end; + const mz_uint32 size = pZip->m_total_files; + + if (size <= 1U) + return; + + pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + + start = (size - 2U) >> 1U; + for (;;) + { + mz_uint64 child, root = start; + for (;;) + { + if ((child = (root << 1U) + 1U) >= size) + break; + child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + if (!start) + break; + start--; + } + + end = size - 1; + while (end > 0) + { + mz_uint64 child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) + { + if ((child = (root << 1U) + 1U) >= end) + break; + child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +{ + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) + { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) + { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + + if (i >= 0) + { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +{ + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) + { + zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) + { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) + { + mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < (mz_uint64)pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data; + void* buf = NULL; + + if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n) + { + buf = MZ_MALLOC(ext_data_size); + if(buf==NULL) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (mz_uint8*)buf; + } + else + { + pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + } + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + + MZ_FREE(buf); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) + { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) + { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +void mz_zip_zero_struct(mz_zip_archive *pZip) +{ + if (pZip) + MZ_CLEAR_PTR(pZip); +} + +static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_bool status = MZ_TRUE; + + if (!pZip) + return MZ_FALSE; + + if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + + return MZ_FALSE; + } + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; + status = MZ_FALSE; + } + } + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return status; +} + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + return mz_zip_reader_end_internal(pZip, MZ_TRUE); +} +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_archive_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +{ + if (!pMem) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pNeeds_keepalive = NULL; + +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + + pZip->m_pState->m_mem_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +} + +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) +{ + mz_uint64 file_size; + MZ_FILE *pFile; + + if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + file_size = archive_size; + if (!file_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + } + + file_size = MZ_FTELL64(pFile); + } + + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) +{ + mz_uint64 cur_file_ofs; + + if ((!pZip) || (!pFile)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + cur_file_ofs = MZ_FTELL64(pFile); + + if (!archive_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + + archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + + if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = archive_size; + pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +} + +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint bit_flag; + mz_uint method; + + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); + bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + + if ((method != 0) && (method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + return MZ_FALSE; + } + + if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + return MZ_FALSE; + } + + if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, attribute_mapping_id, external_attr; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ + attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; + (void)attribute_mapping_id; + + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) + { + return MZ_TRUE; + } + + return MZ_FALSE; +} + +static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +{ + mz_uint n; + const mz_uint8 *p = pCentral_dir_header; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_FALSE; + + if ((!p) || (!pStat)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Extract fields from the central directory record. */ + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + /* Copy as much of the filename and comment as possible. */ + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); + pStat->m_comment[n] = '\0'; + + /* Set some flags for convienance */ + pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); + pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); + pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + + /* See if we need to read any zip64 extended information fields. */ + /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ + if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; + mz_uint32 field_data_remaining = field_data_size; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_TRUE; + + if (pStat->m_uncomp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_uncomp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_comp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_comp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_local_header_ofs == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const mz_uint32 size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + + if (pIndex) + *pIndex = 0; + + if (size) + { + /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ + /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ + mz_int64 l = 0, h = (mz_int64)size - 1; + + while (l <= h) + { + mz_int64 m = l + ((h - l) >> 1); + mz_uint32 file_index = pIndices[(mz_uint32)m]; + + int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint32 index; + if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) + return -1; + else + return (int)index; +} + +mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +{ + mz_uint file_index; + size_t name_len, comment_len; + + if (pIndex) + *pIndex = 0; + + if ((!pZip) || (!pZip->m_pState) || (!pName)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* See if we can use a binary search */ + if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && + (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && + ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + { + return mz_zip_locate_file_binary_search(pZip, pName, pIndex); + } + + /* Locate the entry by scanning the entire central directory */ + name_len = strlen(pName); + if (name_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +static +mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size, const mz_zip_archive_file_stat *st) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (st) { + file_stat = *st; + } else + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Ensure supplied output buffer is large enough. */ + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + + /* Read and parse the local directory entry. */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) + { + if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + } +#endif + + return MZ_TRUE; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + /* Read directly from the archive in memory. */ + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + /* Use a user provided read buffer. */ + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + /* Temporarily allocate a read buffer. */ + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, NULL, 0, NULL); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_uint64 alloc_size; + void *pBuf; + + if (pSize) + *pSize = 0; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return NULL; + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + return NULL; + } + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + if (!mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, (size_t)alloc_size, flags, NULL, 0, &file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32 = MZ_CRC32_INIT; +#endif + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pState->m_pMem) + { + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + } + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +#endif + } + + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + } +#endif + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + status = TINFL_STATUS_FAILED; + } + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +#endif + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (file_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_reader_extract_iter_state *pState; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + /* Argument sanity check */ + if ((!pZip) || (!pZip->m_pState)) + return NULL; + + /* Allocate an iterator status structure */ + pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); + if (!pState) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + /* Fetch file details */ + if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Encryption and patch files are not supported. */ + if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Init state - save args */ + pState->pZip = pZip; + pState->flags = flags; + + /* Init state - reset variables to defaults */ + pState->status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + pState->file_crc32 = MZ_CRC32_INIT; +#endif + pState->read_buf_ofs = 0; + pState->out_buf_ofs = 0; + pState->pRead_buf = NULL; + pState->pWrite_buf = NULL; + pState->out_blk_remain = 0; + + /* Read and parse the local directory entry. */ + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; + pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + else + { + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, therefore intermediate read buffer required */ + pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + else + { + /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ + pState->read_buf_size = 0; + } + pState->read_buf_avail = 0; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, init decompressor */ + tinfl_init( &pState->inflator ); + + /* Allocate write buffer */ + if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + if (pState->pRead_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + + return pState; +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_uint32 file_index; + + /* Locate file index by name */ + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return NULL; + + /* Construct iterator */ + return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +} + +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +{ + size_t copied_to_caller = 0; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) + return 0; + + if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data, calc amount to return. */ + copied_to_caller = (size_t)MZ_MIN( buf_size, pState->comp_remaining ); + + /* Zip is in memory....or requires reading from a file? */ + if (pState->pZip->m_pState->m_pMem) + { + /* Copy data to caller's buffer */ + memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); + pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; + } + else + { + /* Read directly into caller's buffer */ + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) + { + /* Failed to read all that was asked for, flag failure and alert user */ + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + copied_to_caller = 0; + } + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Compute CRC if not returning compressed data only */ + if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +#endif + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += copied_to_caller; + pState->out_buf_ofs += copied_to_caller; + pState->comp_remaining -= copied_to_caller; + } + else + { + do + { + /* Calc ptr to write buffer - given current output pos and block size */ + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + /* Calc max output size - given current output pos and block size */ + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + if (!pState->out_blk_remain) + { + /* Read more data from file if none available (and reading from file) */ + if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) + { + /* Calc read size */ + pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += pState->read_buf_avail; + pState->comp_remaining -= pState->read_buf_avail; + pState->read_buf_ofs = 0; + } + + /* Perform decompression */ + in_buf_size = (size_t)pState->read_buf_avail; + pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + pState->read_buf_avail -= in_buf_size; + pState->read_buf_ofs += in_buf_size; + + /* Update current output block size remaining */ + pState->out_blk_remain = out_buf_size; + } + + if (pState->out_blk_remain) + { + /* Calc amount to return. */ + size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + + /* Copy data to caller's buffer */ + memcpy( (mz_uint8*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Perform CRC */ + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +#endif + + /* Decrement data consumed from block */ + pState->out_blk_remain -= to_copy; + + /* Inc output offset, while performing sanity check */ + if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Increment counter of data copied to caller */ + copied_to_caller += to_copy; + } + } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); + } + + /* Return how many bytes were copied into user buffer */ + return copied_to_caller; +} + +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +{ + int status; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) + return MZ_FALSE; + + /* Was decompression completed and requested? */ + if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + pState->status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (pState->file_crc32 != pState->file_stat.m_crc32) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + } +#endif + } + + /* Free buffers */ + if (!pState->pZip->m_pState->m_pMem) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); + if (pState->pWrite_buf) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + + /* Save status */ + status = pState->status; + + /* Free context */ + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + + return status == TINFL_STATUS_DONE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; + + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + + if (MZ_FCLOSE(pFile) == EOF) + { + if (status) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + + status = MZ_FALSE; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + + return status; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +} + +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_uint32 *p = (mz_uint32 *)pOpaque; + (void)file_ofs; + *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); + return n; +} + +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_zip_internal_state *pState; + const mz_uint8 *pCentral_dir_header; + mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint64 local_header_ofs = 0; + mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; + mz_bool has_data_descriptor; + mz_uint32 local_header_bit_flags; + + mz_zip_array file_data_array; + mz_zip_array_init(&file_data_array, 1); + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (file_index > pZip->m_total_files) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + + if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_is_encrypted) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports stored and deflate. */ + if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + if (!file_stat.m_is_supported) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + /* Read and parse the local directory entry. */ + local_header_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); + local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + has_data_descriptor = (local_header_bit_flags & 8) != 0; + + if (local_header_filename_len != strlen(file_stat.m_filename)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + goto handle_failure; + } + + if (local_header_filename_len) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ + if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_uint32 extra_size_remaining = local_header_extra_len; + const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ + /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ + if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) + { + mz_uint8 descriptor_buf[32]; + mz_bool has_id; + const mz_uint8 *pSrc; + mz_uint32 file_crc32; + mz_uint64 comp_size = 0, uncomp_size = 0; + + mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; + + file_crc32 = MZ_READ_LE32(pSrc); + + if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); + } + else + { + comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); + } + + if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + else + { + if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + mz_zip_array_clear(pZip, &file_data_array); + + if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) + { + if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) + return MZ_FALSE; + + /* 1 more check to be sure, although the extract checks too. */ + if (uncomp_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + return MZ_FALSE; + } + } + + return MZ_TRUE; + +handle_failure: + mz_zip_array_clear(pZip, &file_data_array); + return MZ_FALSE; +} + +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) +{ + mz_zip_internal_state *pState; + mz_uint32 i; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Basic sanity checks */ + if (!pState->m_zip64) + { + if (pZip->m_total_files > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pZip->m_archive_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + else + { + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + for (i = 0; i < pZip->m_total_files; i++) + { + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) + { + mz_uint32 found_index; + mz_zip_archive_file_stat stat; + + if (!mz_zip_reader_file_stat(pZip, i, &stat)) + return MZ_FALSE; + + if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ + if (found_index != i) + return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + } + + if (!mz_zip_validate_file(pZip, i, flags)) + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if ((!pMem) || (!size)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if (!pFilename) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +/* ------------------- .ZIP archive writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +{ + mz_write_le32(p, (mz_uint32)v); + mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +} + +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if (!n) + return 0; + + /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ + if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + return 0; + } + + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + + while (new_capacity < new_size) + new_capacity *= 2; + + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return 0; + } + + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + status = MZ_FALSE; + } + } + + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) +{ + mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + { + if (!pZip->m_pRead) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (pZip->m_file_offset_alignment) + { + /* Ensure user specified file offset alignment is a power of 2. */ + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + + pZip->m_pState->m_zip64 = zip64; + pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + return mz_zip_writer_init_v2(pZip, existing_size, 0); +} + +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_mem_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + return 0; + } + + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +} + +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) +{ + MZ_FILE *pFile; + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + + pZip->m_pState->m_pFile = pFile; + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; + char buf[4096]; + + MZ_CLEAR_ARR(buf); + + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, 0, flags)) + return MZ_FALSE; + + pZip->m_pState->m_pFile = pFile; + pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_zip_internal_state *pState; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) + { + /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ + if (!pZip->m_pState->m_zip64) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* No sense in trying to write to an archive that's already at the support max size */ + if (pZip->m_pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + (void)pFilename; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +#else + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (!pFilename) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + } + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; +#endif /* #ifdef MINIZ_NO_STDIO */ + } + else if (pState->m_pMem) + { + /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + } + /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ + else if (!pZip->m_pWrite) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Start writing new files at the archive's current central directory location. */ + /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_central_directory_file_ofs = 0; + + /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ + /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ + /* TODO: We could easily maintain the sorted central directory offsets. */ + mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +} + +/* TODO: pArchive_name is a terrible name here! */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) +{ + mz_uint8 *pDst = pBuf; + mz_uint32 field_size = 0; + + MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + MZ_WRITE_LE16(pDst + 2, 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + MZ_WRITE_LE64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pComp_size) + { + MZ_WRITE_LE64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + MZ_WRITE_LE64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + MZ_WRITE_LE16(pBuf + 2, field_size); + + return (mz_uint32)(pDst - pBuf); +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, + mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes, + const char *user_extra_data, mz_uint user_extra_data_len) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + if (!pZip->m_pState->m_zip64) + { + if (local_header_ofs > 0xFFFFFFFF) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + /* Try to resize the central directory array back into its original state. */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ + if (*pArchive_name == '/') + return MZ_FALSE; + + /* Making sure the name does not contain drive letters or DOS style backward slashes is the responsibility of the program using miniz*/ + + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +} + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_uint16 bit_flags = 0; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + if (((mz_uint64)buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +#ifndef MINIZ_NO_TIME + if (last_modified != NULL) + { + mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); + } + else + { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif /* #ifndef MINIZ_NO_TIME */ + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + /* Set DOS Subdirectory attribute bit. */ + ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + + /* Subdirectories cannot contain data. */ + if ((buf_size) || (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + cur_archive_file_ofs += num_alignment_padding_bytes; + + MZ_CLEAR_ARR(local_dir_header); + + if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + method = MZ_DEFLATED; + } + + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + + if (pExtra_data != NULL) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + if (uncomp_size) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 gen_flags; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_zip_internal_state *pState; + mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX)) + { + /* Source file is too large for non-zip64 */ + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + pState->m_zip64 = MZ_TRUE; + } + + /* We could support this, but why? */ + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + +#ifndef MINIZ_NO_TIME + if (pFile_time) + { + mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); + } +#endif + + if (max_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_archive_file_ofs; + + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + if (max_size && level) + { + method = MZ_DEFLATED; + } + + MZ_CLEAR_ARR(local_dir_header); + if (pState->m_zip64) + { + if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + else + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, NULL, + NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (max_size) + { + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!level) + { + while (1) + { + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if (n == 0) + break; + + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + cur_archive_file_ofs += n; + } + uncomp_size = file_ofs; + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + } + + for (;;) + { + tdefl_status status; + tdefl_flush flush = TDEFL_NO_FLUSH; + + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + break; + } + + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + + if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) + flush = TDEFL_FULL_FLUSH; + + if (n == 0) + flush = TDEFL_FINISH; + + status = tdefl_compress_buffer(pComp, pRead_buf, n, flush); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + break; + } + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return MZ_FALSE; + } + + uncomp_size = file_ofs; + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + { + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, + (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size, + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size, + uncomp_crc32, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + cur_archive_header_file_ofs = local_dir_header_ofs; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + if (pExtra_data != NULL) + { + cur_archive_header_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_header_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_header_file_ofs += extra_size; + } + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO + +static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pSrc_file); + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pSrc_file); +} + +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + return mz_zip_writer_add_read_buf_callback(pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags, + user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len); +} + +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + MZ_FILE *pSrc_file = NULL; + mz_uint64 uncomp_size = 0; + MZ_TIME_T file_modified_time; + MZ_TIME_T *pFile_time = NULL; + mz_bool status; + + memset(&file_modified_time, 0, sizeof(file_modified_time)); + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + pFile_time = &file_modified_time; + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +#endif + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); + + MZ_FCLOSE(pSrc_file); + + return status; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, mz_uint32 ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) +{ + /* + 64 should be enough for any new zip64 data */ + if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); + + if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) + { + mz_uint8 new_ext_block[64]; + mz_uint8 *pDst = new_ext_block; + mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + mz_write_le16(pDst + sizeof(mz_uint16), 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + mz_write_le64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + } + + if (pComp_size) + { + mz_write_le64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + mz_write_le64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + } + + if (pDisk_start) + { + mz_write_le32(pDst, *pDisk_start); + pDst += sizeof(mz_uint32); + } + + mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); + + if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if ((pExt) && (ext_len)) + { + mz_uint32 extra_size_remaining = ext_len; + const mz_uint8 *pExtra_data = pExt; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + return MZ_TRUE; +} + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; + mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + mz_zip_archive_file_stat src_file_stat; + mz_uint32 src_filename_len, src_comment_len, src_ext_len; + mz_uint32 local_header_filename_size, local_header_extra_len; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ + if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Get pointer to the source central dir header and crack it */ + if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); + src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); + src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + + /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ + if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + if (!pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) + return MZ_FALSE; + + cur_src_file_ofs = src_file_stat.m_local_header_ofs; + cur_dst_file_ofs = pZip->m_archive_size; + + /* Read the source archive's local dir header */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Compute the total size we need to copy (filename+extra data+compressed data) */ + local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; + + /* Try to find a zip64 extended information field */ + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_zip_array file_data_array; + const mz_uint8 *pExtra_data; + mz_uint32 extra_size_remaining = local_header_extra_len; + + mz_zip_array_init(&file_data_array, 1); + if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + + mz_zip_array_clear(pZip, &file_data_array); + } + + if (!pState->m_zip64) + { + /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ + /* We also check when the archive is finalized so this doesn't need to be perfect. */ + mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; + + if (approx_new_archive_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + /* Write dest archive padding */ + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + + cur_dst_file_ofs += num_alignment_padding_bytes; + + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + while (src_archive_bytes_remaining) + { + n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_dst_file_ofs += n; + + src_archive_bytes_remaining -= n; + } + + /* Now deal with the optional data descriptor */ + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + /* Copy data descriptor */ + if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + /* src is zip64, dest must be zip64 */ + + /* name uint32_t's */ + /* id 1 (optional in zip64?) */ + /* crc 1 */ + /* comp_size 2 */ + /* uncomp_size 2 */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); + } + else + { + /* src is NOT zip64 */ + mz_bool has_id; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + + if (pZip->m_pState->m_zip64) + { + /* dest is zip64, so upgrade the data descriptor */ + const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0); + const mz_uint32 src_crc32 = MZ_READ_LE32(pSrc_descriptor); + const mz_uint64 src_comp_size = MZ_READ_LE32(pSrc_descriptor + sizeof(mz_uint32)); + const mz_uint64 src_uncomp_size = MZ_READ_LE32(pSrc_descriptor + 2*sizeof(mz_uint32)); + + mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); + mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + + n = sizeof(mz_uint32) * 6; + } + else + { + /* dest is NOT zip64, just copy it as-is */ + n = sizeof(mz_uint32) * (has_id ? 4 : 3); + } + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + /* Finally, add the new central dir header */ + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + + if (pState->m_zip64) + { + /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ + const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; + mz_zip_array new_ext_block; + + mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); + + if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return MZ_FALSE; + } + + MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + mz_zip_array_clear(pZip, &new_ext_block); + } + else + { + /* sanity checks */ + if (cur_dst_file_ofs > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (local_dir_header_ofs >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + } + + /* This shouldn't trigger unless we screwed up during the initial sanity checks */ + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + { + /* TODO: Support central dirs >= 32-bits in size */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + } + + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[256]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if ((mz_uint64)pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + /* Write central directory */ + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += central_dir_size; + } + + if (pState->m_zip64) + { + /* Write zip64 end of central directory header */ + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; + + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + + /* Write zip64 end of central directory locator */ + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; + } + + /* Write end of central directory record */ + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) +{ + if ((!ppBuf) || (!pSize)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + *ppBuf = NULL; + *pSize = 0; + + if ((!pZip) || (!pZip->m_pState)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_pWrite != mz_zip_heap_write_func) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *ppBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + return mz_zip_writer_end_internal(pZip, MZ_TRUE); +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); +} + +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + mz_zip_zero_struct(&zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_FILENAME; + return MZ_FALSE; + } + + /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ + /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + /* Create a new archive. */ + if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + created_new_archive = MZ_TRUE; + } + else + { + /* Append to an existing archive. */ + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + + return MZ_FALSE; + } + } + + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + actual_err = zip_archive.m_last_error; + + /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ + if (!mz_zip_writer_finalize_archive(&zip_archive)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if (!mz_zip_writer_end_internal(&zip_archive, status)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if ((!status) && (created_new_archive)) + { + /* It's a new archive and something went wrong, so just delete it. */ + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + + if (pErr) + *pErr = actual_err; + + return status; +} + +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) +{ + mz_uint32 file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + + return NULL; + } + + mz_zip_zero_struct(&zip_archive); + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + return NULL; + } + + if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) + { + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + } + + mz_zip_reader_end_internal(&zip_archive, p != NULL); + + if (pErr) + *pErr = zip_archive.m_last_error; + + return p; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* ------------------- Misc utils */ + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +} + +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +} + +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = err_num; + return prev_err; +} + +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + return pZip->m_last_error; +} + +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) +{ + return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +} + +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = MZ_ZIP_NO_ERROR; + return prev_err; +} + +const char *mz_zip_get_error_string(mz_zip_error mz_err) +{ + switch (mz_err) + { + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return "undefined error"; + case MZ_ZIP_TOO_MANY_FILES: + return "too many files"; + case MZ_ZIP_FILE_TOO_LARGE: + return "file too large"; + case MZ_ZIP_UNSUPPORTED_METHOD: + return "unsupported method"; + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return "unsupported encryption"; + case MZ_ZIP_UNSUPPORTED_FEATURE: + return "unsupported feature"; + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return "failed finding central directory"; + case MZ_ZIP_NOT_AN_ARCHIVE: + return "not a ZIP archive"; + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return "invalid header or archive is corrupted"; + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return "unsupported multidisk archive"; + case MZ_ZIP_DECOMPRESSION_FAILED: + return "decompression failed or archive is corrupted"; + case MZ_ZIP_COMPRESSION_FAILED: + return "compression failed"; + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return "unexpected decompressed size"; + case MZ_ZIP_CRC_CHECK_FAILED: + return "CRC-32 check failed"; + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return "unsupported central directory size"; + case MZ_ZIP_ALLOC_FAILED: + return "allocation failed"; + case MZ_ZIP_FILE_OPEN_FAILED: + return "file open failed"; + case MZ_ZIP_FILE_CREATE_FAILED: + return "file create failed"; + case MZ_ZIP_FILE_WRITE_FAILED: + return "file write failed"; + case MZ_ZIP_FILE_READ_FAILED: + return "file read failed"; + case MZ_ZIP_FILE_CLOSE_FAILED: + return "file close failed"; + case MZ_ZIP_FILE_SEEK_FAILED: + return "file seek failed"; + case MZ_ZIP_FILE_STAT_FAILED: + return "file stat failed"; + case MZ_ZIP_INVALID_PARAMETER: + return "invalid parameter"; + case MZ_ZIP_INVALID_FILENAME: + return "invalid filename"; + case MZ_ZIP_BUF_TOO_SMALL: + return "buffer too small"; + case MZ_ZIP_INTERNAL_ERROR: + return "internal error"; + case MZ_ZIP_FILE_NOT_FOUND: + return "file not found"; + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return "archive is too large"; + case MZ_ZIP_VALIDATION_FAILED: + return "validation failed"; + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return "write callback failed"; + case MZ_ZIP_TOTAL_ERRORS: + return "total errors"; + default: + break; + } + + return "unknown error"; +} + +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return MZ_FALSE; + + return pZip->m_pState->m_zip64; +} + +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + + return pZip->m_pState->m_central_dir.m_size; +} + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) +{ + if (!pZip) + return 0; + return pZip->m_archive_size; +} + +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_file_archive_start_ofs; +} + +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_pFile; +} + +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + if (filename_buf_size) + pFilename[0] = '\0'; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +} + +mz_bool mz_zip_end(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_FALSE; + + if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) + return mz_zip_reader_end(pZip); +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) + return mz_zip_writer_end(pZip); +#endif + + return MZ_FALSE; +} + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ diff --git a/thirdparty/miniz/miniz.h b/thirdparty/miniz/miniz.h new file mode 100644 index 000000000..9fcfffcc8 --- /dev/null +++ b/thirdparty/miniz/miniz.h @@ -0,0 +1,1422 @@ +#ifndef MINIZ_EXPORT +#define MINIZ_EXPORT +#endif +/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateReset/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once + + + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ + +/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ +/*#define MINIZ_NO_DEFLATE_APIS */ + +/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ +/*#define MINIZ_NO_INFLATE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#ifdef MINIZ_NO_INFLATE_APIS +#define MINIZ_NO_ARCHIVE_APIS +#endif + +#ifdef MINIZ_NO_DEFLATE_APIS +#define MINIZ_NO_ARCHIVE_WRITING_APIS +#endif + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +/* Set MINIZ_LITTLE_ENDIAN only if not set */ +#if !defined(MINIZ_LITTLE_ENDIAN) +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#else + +#if MINIZ_X86_OR_X64_CPU +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#endif +#endif + +/* Using unaligned loads and stores causes errors when using UBSan */ +#if defined(__has_feature) +#if __has_feature(undefined_behavior_sanitizer) +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +MINIZ_EXPORT void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* Method */ +#define MZ_DEFLATED 8 + +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "11.0.2" +#define MZ_VERNUM 0xB002 +#define MZ_VER_MAJOR 11 +#define MZ_VER_MINOR 2 +#define MZ_VER_REVISION 0 +#define MZ_VER_SUBREVISION 0 + +#ifndef MINIZ_NO_ZLIB_APIS + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +MINIZ_EXPORT const char *mz_version(void); + +#ifndef MINIZ_NO_DEFLATE_APIS + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + +/* Initializes a decompressor. */ +MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ +MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +MINIZ_EXPORT const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream + +#ifndef MINIZ_NO_DEFLATE_APIS +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflateReset mz_inflateReset +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define uncompress2 mz_uncompress2 +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + + + + + +#pragma once +#include +#include +#include +#include + + + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag +{ + mz_uint32 m_dummy1; + mz_uint32 m_dummy2; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t +#endif + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) +#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) +#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); +extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif + #pragma once + + +#ifndef MINIZ_NO_DEFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); +MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + #pragma once + +/* ------------------- Low-level Decompression API Definitions */ + +#ifndef MINIZ_NO_INFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); +MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); +#endif + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; + mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; + mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; + mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; + mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; + mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#pragma once + + +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; + +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; + + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; + + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; + + /* File's compressed size. */ + mz_uint64 m_comp_size; + + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; + + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; + + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; + + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; + + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +#ifdef MINIZ_NO_TIME + MZ_TIME_T m_padding; +#else + MZ_TIME_T m_time; +#endif +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, + /*After adding a compressed file, seek back + to local file header and set the correct sizes*/ + MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct +{ + mz_zip_archive *pZip; + mz_uint flags; + + int status; + + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +#ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint padding; +#else + mz_uint file_crc32; +#endif + +} mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif + +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); +#endif + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +#endif + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ +/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ +MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a pointer to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +#ifndef MINIZ_NO_STDIO +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); +#endif + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINIZ_NO_ARCHIVE_APIS */ diff --git a/thirdparty/mpg123.h b/thirdparty/mpg123.h new file mode 100644 index 000000000..d7b7ef831 --- /dev/null +++ b/thirdparty/mpg123.h @@ -0,0 +1,1202 @@ +/* + libmpg123: MPEG Audio Decoder library (version 1.22.0) + + copyright 1995-2010 by the mpg123 project - free software under the terms of the LGPL 2.1 + see COPYING and AUTHORS files in distribution or http://mpg123.org +*/ + +#ifndef MPG123_LIB_H +#define MPG123_LIB_H + +/** \file mpg123.h The header file for the libmpg123 MPEG Audio decoder */ + +/* A macro to check at compile time which set of API functions to expect. + This should be incremented at least each time a new symbol is added to the header. */ +#define MPG123_API_VERSION 41 + +/* These aren't actually in use... seems to work without using libtool. */ +#ifdef BUILD_MPG123_DLL +/* The dll exports. */ +#define MPG123_EXPORT __declspec(dllexport) +#else +#ifdef LINK_MPG123_DLL +/* The exe imports. */ +#define MPG123_EXPORT __declspec(dllimport) +#else +/* Nothing on normal/UNIX builds */ +#define MPG123_EXPORT +#endif +#endif + +#ifndef MPG123_NO_CONFIGURE /* Enable use of this file without configure. */ +#include +#include + +/* Simplified large file handling. + I used to have a check here that prevents building for a library with conflicting large file setup + (application that uses 32 bit offsets with library that uses 64 bits). + While that was perfectly fine in an environment where there is one incarnation of the library, + it hurt GNU/Linux and Solaris systems with multilib where the distribution fails to provide the + correct header matching the 32 bit library (where large files need explicit support) or + the 64 bit library (where there is no distinction). + + New approach: When the app defines _FILE_OFFSET_BITS, it wants non-default large file support, + and thus functions with added suffix (mpg123_open_64). + Any mismatch will be caught at link time because of the _FILE_OFFSET_BITS setting used when + building libmpg123. Plus, there's dual mode large file support in mpg123 since 1.12 now. + Link failure is not the expected outcome of any half-sane usage anymore. + + More complication: What about client code defining _LARGEFILE64_SOURCE? It might want direct access to the _64 functions, along with the ones without suffix. Well, that's possible now via defining MPG123_NO_LARGENAME and MPG123_LARGESUFFIX, respectively, for disabling or enforcing the suffix names. +*/ + +/* + Now, the renaming of large file aware functions. + By default, it appends underscore _FILE_OFFSET_BITS (so, mpg123_seek_64 for mpg123_seek), if _FILE_OFFSET_BITS is defined. You can force a different suffix via MPG123_LARGESUFFIX (that must include the underscore), or you can just disable the whole mess by defining MPG123_NO_LARGENAME. +*/ +#if (!defined MPG123_NO_LARGENAME) && ((defined _FILE_OFFSET_BITS) || (defined MPG123_LARGESUFFIX)) + +/* Need some trickery to concatenate the value(s) of the given macro(s). */ +#define MPG123_MACROCAT_REALLY(a, b) a ## b +#define MPG123_MACROCAT(a, b) MPG123_MACROCAT_REALLY(a, b) +#ifndef MPG123_LARGESUFFIX +#define MPG123_LARGESUFFIX MPG123_MACROCAT(_, _FILE_OFFSET_BITS) +#endif +#define MPG123_LARGENAME(func) MPG123_MACROCAT(func, MPG123_LARGESUFFIX) + +#define mpg123_open MPG123_LARGENAME(mpg123_open) +#define mpg123_open_fd MPG123_LARGENAME(mpg123_open_fd) +#define mpg123_open_handle MPG123_LARGENAME(mpg123_open_handle) +#define mpg123_framebyframe_decode MPG123_LARGENAME(mpg123_framebyframe_decode) +#define mpg123_decode_frame MPG123_LARGENAME(mpg123_decode_frame) +#define mpg123_tell MPG123_LARGENAME(mpg123_tell) +#define mpg123_tellframe MPG123_LARGENAME(mpg123_tellframe) +#define mpg123_tell_stream MPG123_LARGENAME(mpg123_tell_stream) +#define mpg123_seek MPG123_LARGENAME(mpg123_seek) +#define mpg123_feedseek MPG123_LARGENAME(mpg123_feedseek) +#define mpg123_seek_frame MPG123_LARGENAME(mpg123_seek_frame) +#define mpg123_timeframe MPG123_LARGENAME(mpg123_timeframe) +#define mpg123_index MPG123_LARGENAME(mpg123_index) +#define mpg123_set_index MPG123_LARGENAME(mpg123_set_index) +#define mpg123_position MPG123_LARGENAME(mpg123_position) +#define mpg123_length MPG123_LARGENAME(mpg123_length) +#define mpg123_set_filesize MPG123_LARGENAME(mpg123_set_filesize) +#define mpg123_replace_reader MPG123_LARGENAME(mpg123_replace_reader) +#define mpg123_replace_reader_handle MPG123_LARGENAME(mpg123_replace_reader_handle) +#define mpg123_framepos MPG123_LARGENAME(mpg123_framepos) + +#endif /* largefile hackery */ + +#endif /* MPG123_NO_CONFIGURE */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup mpg123_init mpg123 library and handle setup + * + * Functions to initialise and shutdown the mpg123 library and handles. + * The parameters of handles have workable defaults, you only have to tune them when you want to tune something;-) + * Tip: Use a RVA setting... + * + * @{ + */ + +/** Opaque structure for the libmpg123 decoder handle. */ +struct mpg123_handle_struct; + +/** Opaque structure for the libmpg123 decoder handle. + * Most functions take a pointer to a mpg123_handle as first argument and operate on its data in an object-oriented manner. + */ +typedef struct mpg123_handle_struct mpg123_handle; + +/** Function to initialise the mpg123 library. + * This function is not thread-safe. Call it exactly once per process, before any other (possibly threaded) work with the library. + * + * \return MPG123_OK if successful, otherwise an error number. + */ +MPG123_EXPORT int mpg123_init(void); + +/** Function to close down the mpg123 library. + * This function is not thread-safe. Call it exactly once per process, before any other (possibly threaded) work with the library. */ +MPG123_EXPORT void mpg123_exit(void); + +/** Create a handle with optional choice of decoder (named by a string, see mpg123_decoders() or mpg123_supported_decoders()). + * and optional retrieval of an error code to feed to mpg123_plain_strerror(). + * Optional means: Any of or both the parameters may be NULL. + * + * \return Non-NULL pointer when successful. + */ +MPG123_EXPORT mpg123_handle *mpg123_new(const char* decoder, int *error); + +/** Delete handle, mh is either a valid mpg123 handle or NULL. */ +MPG123_EXPORT void mpg123_delete(mpg123_handle *mh); + +/** Enumeration of the parameters types that it is possible to set/get. */ +enum mpg123_parms +{ + MPG123_VERBOSE = 0, /**< set verbosity value for enabling messages to stderr, >= 0 makes sense (integer) */ + MPG123_FLAGS, /**< set all flags, p.ex val = MPG123_GAPLESS|MPG123_MONO_MIX (integer) */ + MPG123_ADD_FLAGS, /**< add some flags (integer) */ + MPG123_FORCE_RATE, /**< when value > 0, force output rate to that value (integer) */ + MPG123_DOWN_SAMPLE, /**< 0=native rate, 1=half rate, 2=quarter rate (integer) */ + MPG123_RVA, /**< one of the RVA choices above (integer) */ + MPG123_DOWNSPEED, /**< play a frame N times (integer) */ + MPG123_UPSPEED, /**< play every Nth frame (integer) */ + MPG123_START_FRAME, /**< start with this frame (skip frames before that, integer) */ + MPG123_DECODE_FRAMES, /**< decode only this number of frames (integer) */ + MPG123_ICY_INTERVAL, /**< stream contains ICY metadata with this interval (integer) */ + MPG123_OUTSCALE, /**< the scale for output samples (amplitude - integer or float according to mpg123 output format, normally integer) */ + MPG123_TIMEOUT, /**< timeout for reading from a stream (not supported on win32, integer) */ + MPG123_REMOVE_FLAGS, /**< remove some flags (inverse of MPG123_ADD_FLAGS, integer) */ + MPG123_RESYNC_LIMIT, /**< Try resync on frame parsing for that many bytes or until end of stream (<0 ... integer). This can enlarge the limit for skipping junk on beginning, too (but not reduce it). */ + MPG123_INDEX_SIZE /**< Set the frame index size (if supported). Values <0 mean that the index is allowed to grow dynamically in these steps (in positive direction, of course) -- Use this when you really want a full index with every individual frame. */ + ,MPG123_PREFRAMES /**< Decode/ignore that many frames in advance for layer 3. This is needed to fill bit reservoir after seeking, for example (but also at least one frame in advance is needed to have all "normal" data for layer 3). Give a positive integer value, please.*/ + ,MPG123_FEEDPOOL /**< For feeder mode, keep that many buffers in a pool to avoid frequent malloc/free. The pool is allocated on mpg123_open_feed(). If you change this parameter afterwards, you can trigger growth and shrinkage during decoding. The default value could change any time. If you care about this, then set it. (integer) */ + ,MPG123_FEEDBUFFER /**< Minimal size of one internal feeder buffer, again, the default value is subject to change. (integer) */ +}; + +/** Flag bits for MPG123_FLAGS, use the usual binary or to combine. */ +enum mpg123_param_flags +{ + MPG123_FORCE_MONO = 0x7 /**< 0111 Force some mono mode: This is a test bitmask for seeing if any mono forcing is active. */ + ,MPG123_MONO_LEFT = 0x1 /**< 0001 Force playback of left channel only. */ + ,MPG123_MONO_RIGHT = 0x2 /**< 0010 Force playback of right channel only. */ + ,MPG123_MONO_MIX = 0x4 /**< 0100 Force playback of mixed mono. */ + ,MPG123_FORCE_STEREO = 0x8 /**< 1000 Force stereo output. */ + ,MPG123_FORCE_8BIT = 0x10 /**< 00010000 Force 8bit formats. */ + ,MPG123_QUIET = 0x20 /**< 00100000 Suppress any printouts (overrules verbose). */ + ,MPG123_GAPLESS = 0x40 /**< 01000000 Enable gapless decoding (default on if libmpg123 has support). */ + ,MPG123_NO_RESYNC = 0x80 /**< 10000000 Disable resync stream after error. */ + ,MPG123_SEEKBUFFER = 0x100 /**< 000100000000 Enable small buffer on non-seekable streams to allow some peek-ahead (for better MPEG sync). */ + ,MPG123_FUZZY = 0x200 /**< 001000000000 Enable fuzzy seeks (guessing byte offsets or using approximate seek points from Xing TOC) */ + ,MPG123_FORCE_FLOAT = 0x400 /**< 010000000000 Force floating point output (32 or 64 bits depends on mpg123 internal precision). */ + ,MPG123_PLAIN_ID3TEXT = 0x800 /**< 100000000000 Do not translate ID3 text data to UTF-8. ID3 strings will contain the raw text data, with the first byte containing the ID3 encoding code. */ + ,MPG123_IGNORE_STREAMLENGTH = 0x1000 /**< 1000000000000 Ignore any stream length information contained in the stream, which can be contained in a 'TLEN' frame of an ID3v2 tag or a Xing tag */ + ,MPG123_SKIP_ID3V2 = 0x2000 /**< 10 0000 0000 0000 Do not parse ID3v2 tags, just skip them. */ + ,MPG123_IGNORE_INFOFRAME = 0x4000 /**< 100 0000 0000 0000 Do not parse the LAME/Xing info frame, treat it as normal MPEG data. */ + ,MPG123_AUTO_RESAMPLE = 0x8000 /**< 1000 0000 0000 0000 Allow automatic internal resampling of any kind (default on if supported). Especially when going lowlevel with replacing output buffer, you might want to unset this flag. Setting MPG123_DOWNSAMPLE or MPG123_FORCE_RATE will override this. */ + ,MPG123_PICTURE = 0x10000 /**< 17th bit: Enable storage of pictures from tags (ID3v2 APIC). */ +}; + +/** choices for MPG123_RVA */ +enum mpg123_param_rva +{ + MPG123_RVA_OFF = 0 /**< RVA disabled (default). */ + ,MPG123_RVA_MIX = 1 /**< Use mix/track/radio gain. */ + ,MPG123_RVA_ALBUM = 2 /**< Use album/audiophile gain */ + ,MPG123_RVA_MAX = MPG123_RVA_ALBUM /**< The maximum RVA code, may increase in future. */ +}; + +/* TODO: Assess the possibilities and troubles of changing parameters during playback. */ + +/** Set a specific parameter, for a specific mpg123_handle, using a parameter + * type key chosen from the mpg123_parms enumeration, to the specified value. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_param(mpg123_handle *mh, enum mpg123_parms type, long value, double fvalue); + +/** Get a specific parameter, for a specific mpg123_handle. + * See the mpg123_parms enumeration for a list of available parameters. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_getparam(mpg123_handle *mh, enum mpg123_parms type, long *val, double *fval); + +/** Feature set available for query with mpg123_feature. */ +enum mpg123_feature_set +{ + MPG123_FEATURE_ABI_UTF8OPEN = 0 /**< mpg123 expects path names to be given in UTF-8 encoding instead of plain native. */ + ,MPG123_FEATURE_OUTPUT_8BIT /**< 8bit output */ + ,MPG123_FEATURE_OUTPUT_16BIT /**< 16bit output */ + ,MPG123_FEATURE_OUTPUT_32BIT /**< 32bit output */ + ,MPG123_FEATURE_INDEX /**< support for building a frame index for accurate seeking */ + ,MPG123_FEATURE_PARSE_ID3V2 /**< id3v2 parsing */ + ,MPG123_FEATURE_DECODE_LAYER1 /**< mpeg layer-1 decoder enabled */ + ,MPG123_FEATURE_DECODE_LAYER2 /**< mpeg layer-2 decoder enabled */ + ,MPG123_FEATURE_DECODE_LAYER3 /**< mpeg layer-3 decoder enabled */ + ,MPG123_FEATURE_DECODE_ACCURATE /**< accurate decoder rounding */ + ,MPG123_FEATURE_DECODE_DOWNSAMPLE /**< downsample (sample omit) */ + ,MPG123_FEATURE_DECODE_NTOM /**< flexible rate decoding */ + ,MPG123_FEATURE_PARSE_ICY /**< ICY support */ + ,MPG123_FEATURE_TIMEOUT_READ /**< Reader with timeout (network). */ +}; + +/** Query libmpg123 feature, 1 for success, 0 for unimplemented functions. */ +MPG123_EXPORT int mpg123_feature(const enum mpg123_feature_set key); + +/* @} */ + + +/** \defgroup mpg123_error mpg123 error handling + * + * Functions to get text version of the error numbers and an enumeration + * of the error codes returned by libmpg123. + * + * Most functions operating on a mpg123_handle simply return MPG123_OK (0) + * on success and MPG123_ERR (-1) on failure, setting the internal error + * variable of the handle to the specific error code. If there was not a valid + * (non-NULL) handle provided to a function operating on one, MPG123_BAD_HANDLE + * may be returned if this can not be confused with a valid positive return + * value. + * Meaning: A function expected to return positive integers on success will + * always indicate error or a special condition by returning a negative one. + * + * Decoding/seek functions may also return message codes MPG123_DONE, + * MPG123_NEW_FORMAT and MPG123_NEED_MORE (all negative, see below on how to + * react). Note that calls to those can be nested, so generally watch out + * for these codes after initial handle setup. + * Especially any function that needs information about the current stream + * to work will try to at least parse the beginning if that did not happen + * yet. + * + * On a function that is supposed to return MPG123_OK on success and + * MPG123_ERR on failure, make sure you check for != MPG123_OK, not + * == MPG123_ERR, as the error code could get more specific in future, + * or there is just a special message from a decoding routine as indicated + * above. + * + * @{ + */ + +/** Enumeration of the message and error codes and returned by libmpg123 functions. */ +enum mpg123_errors +{ + MPG123_DONE=-12, /**< Message: Track ended. Stop decoding. */ + MPG123_NEW_FORMAT=-11, /**< Message: Output format will be different on next call. Note that some libmpg123 versions between 1.4.3 and 1.8.0 insist on you calling mpg123_getformat() after getting this message code. Newer verisons behave like advertised: You have the chance to call mpg123_getformat(), but you can also just continue decoding and get your data. */ + MPG123_NEED_MORE=-10, /**< Message: For feed reader: "Feed me more!" (call mpg123_feed() or mpg123_decode() with some new input data). */ + MPG123_ERR=-1, /**< Generic Error */ + MPG123_OK=0, /**< Success */ + MPG123_BAD_OUTFORMAT, /**< Unable to set up output format! */ + MPG123_BAD_CHANNEL, /**< Invalid channel number specified. */ + MPG123_BAD_RATE, /**< Invalid sample rate specified. */ + MPG123_ERR_16TO8TABLE, /**< Unable to allocate memory for 16 to 8 converter table! */ + MPG123_BAD_PARAM, /**< Bad parameter id! */ + MPG123_BAD_BUFFER, /**< Bad buffer given -- invalid pointer or too small size. */ + MPG123_OUT_OF_MEM, /**< Out of memory -- some malloc() failed. */ + MPG123_NOT_INITIALIZED, /**< You didn't initialize the library! */ + MPG123_BAD_DECODER, /**< Invalid decoder choice. */ + MPG123_BAD_HANDLE, /**< Invalid mpg123 handle. */ + MPG123_NO_BUFFERS, /**< Unable to initialize frame buffers (out of memory?). */ + MPG123_BAD_RVA, /**< Invalid RVA mode. */ + MPG123_NO_GAPLESS, /**< This build doesn't support gapless decoding. */ + MPG123_NO_SPACE, /**< Not enough buffer space. */ + MPG123_BAD_TYPES, /**< Incompatible numeric data types. */ + MPG123_BAD_BAND, /**< Bad equalizer band. */ + MPG123_ERR_NULL, /**< Null pointer given where valid storage address needed. */ + MPG123_ERR_READER, /**< Error reading the stream. */ + MPG123_NO_SEEK_FROM_END,/**< Cannot seek from end (end is not known). */ + MPG123_BAD_WHENCE, /**< Invalid 'whence' for seek function.*/ + MPG123_NO_TIMEOUT, /**< Build does not support stream timeouts. */ + MPG123_BAD_FILE, /**< File access error. */ + MPG123_NO_SEEK, /**< Seek not supported by stream. */ + MPG123_NO_READER, /**< No stream opened. */ + MPG123_BAD_PARS, /**< Bad parameter handle. */ + MPG123_BAD_INDEX_PAR, /**< Bad parameters to mpg123_index() and mpg123_set_index() */ + MPG123_OUT_OF_SYNC, /**< Lost track in bytestream and did not try to resync. */ + MPG123_RESYNC_FAIL, /**< Resync failed to find valid MPEG data. */ + MPG123_NO_8BIT, /**< No 8bit encoding possible. */ + MPG123_BAD_ALIGN, /**< Stack aligmnent error */ + MPG123_NULL_BUFFER, /**< NULL input buffer with non-zero size... */ + MPG123_NO_RELSEEK, /**< Relative seek not possible (screwed up file offset) */ + MPG123_NULL_POINTER, /**< You gave a null pointer somewhere where you shouldn't have. */ + MPG123_BAD_KEY, /**< Bad key value given. */ + MPG123_NO_INDEX, /**< No frame index in this build. */ + MPG123_INDEX_FAIL, /**< Something with frame index went wrong. */ + MPG123_BAD_DECODER_SETUP, /**< Something prevents a proper decoder setup */ + MPG123_MISSING_FEATURE /**< This feature has not been built into libmpg123. */ + ,MPG123_BAD_VALUE /**< A bad value has been given, somewhere. */ + ,MPG123_LSEEK_FAILED /**< Low-level seek failed. */ + ,MPG123_BAD_CUSTOM_IO /**< Custom I/O not prepared. */ + ,MPG123_LFS_OVERFLOW /**< Offset value overflow during translation of large file API calls -- your client program cannot handle that large file. */ + ,MPG123_INT_OVERFLOW /**< Some integer overflow. */ +}; + +/** Return a string describing that error errcode means. */ +MPG123_EXPORT const char* mpg123_plain_strerror(int errcode); + +/** Give string describing what error has occured in the context of handle mh. + * When a function operating on an mpg123 handle returns MPG123_ERR, you should check for the actual reason via + * char *errmsg = mpg123_strerror(mh) + * This function will catch mh == NULL and return the message for MPG123_BAD_HANDLE. */ +MPG123_EXPORT const char* mpg123_strerror(mpg123_handle *mh); + +/** Return the plain errcode intead of a string. + * \return error code recorded in handle or MPG123_BAD_HANDLE + */ +MPG123_EXPORT int mpg123_errcode(mpg123_handle *mh); + +/*@}*/ + + +/** \defgroup mpg123_decoder mpg123 decoder selection + * + * Functions to list and select the available decoders. + * Perhaps the most prominent feature of mpg123: You have several (optimized) decoders to choose from (on x86 and PPC (MacOS) systems, that is). + * + * @{ + */ + +/** Return a NULL-terminated array of generally available decoder names (plain 8bit ASCII). */ +MPG123_EXPORT const char **mpg123_decoders(void); + +/** Return a NULL-terminated array of the decoders supported by the CPU (plain 8bit ASCII). */ +MPG123_EXPORT const char **mpg123_supported_decoders(void); + +/** Set the chosen decoder to 'decoder_name' + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_decoder(mpg123_handle *mh, const char* decoder_name); + +/** Get the currently active decoder engine name. + The active decoder engine can vary depening on output constraints, + mostly non-resampling, integer output is accelerated via 3DNow & Co. but for other modes a fallback engine kicks in. + Note that this can return a decoder that is ony active in the hidden and not available as decoder choice from the outside. + \return The decoder name or NULL on error. */ +MPG123_EXPORT const char* mpg123_current_decoder(mpg123_handle *mh); + +/*@}*/ + + +/** \defgroup mpg123_output mpg123 output audio format + * + * Functions to get and select the format of the decoded audio. + * + * Before you dive in, please be warned that you might get confused by this. This seems to happen a lot, therefore I am trying to explain in advance. + * + * The mpg123 library decides what output format to use when encountering the first frame in a stream, or actually any frame that is still valid but differs from the frames before in the prompted output format. At such a deciding point, an internal table of allowed encodings, sampling rates and channel setups is consulted. According to this table, an output format is chosen and the decoding engine set up accordingly (including ptimized routines for different output formats). This might seem unusual but it just follows from the non-existence of "MPEG audio files" with defined overall properties. There are streams, streams are concatenations of (semi) independent frames. We store streams on disk and call them "MPEG audio files", but that does not change their nature as the decoder is concerned (the LAME/Xing header for gapless decoding makes things interesting again). + * + * To get to the point: What you do with mpg123_format() and friends is to fill the internal table of allowed formats before it is used. That includes removing support for some formats or adding your forced sample rate (see MPG123_FORCE_RATE) that will be used with the crude internal resampler. Also keep in mind that the sample encoding is just a question of choice -- the MPEG frames do only indicate their native sampling rate and channel count. If you want to decode to integer or float samples, 8 or 16 bit ... that is your decision. In a "clean" world, libmpg123 would always decode to 32 bit float and let you handle any sample conversion. But there are optimized routines that work faster by directly decoding to the desired encoding / accuracy. We prefer efficiency over conceptual tidyness. + * + * People often start out thinking that mpg123_format() should change the actual decoding format on the fly. That is wrong. It only has effect on the next natural change of output format, when libmpg123 will consult its format table again. To make life easier, you might want to call mpg123_format_none() before any thing else and then just allow one desired encoding and a limited set of sample rates / channel choices that you actually intend to deal with. You can force libmpg123 to decode everything to 44100 KHz, stereo, 16 bit integer ... it will duplicate mono channels and even do resampling if needed (unless that feature is disabled in the build, same with some encodings). But I have to stress that the resampling of libmpg123 is very crude and doesn't even contain any kind of "proper" interpolation. + * + * In any case, watch out for MPG123_NEW_FORMAT as return message from decoding routines and call mpg123_getformat() to get the currently active output format. + * + * @{ + */ + +/** An enum over all sample types possibly known to mpg123. + * The values are designed as bit flags to allow bitmasking for encoding families. + * + * Note that (your build of) libmpg123 does not necessarily support all these. + * Usually, you can expect the 8bit encodings and signed 16 bit. + * Also 32bit float will be usual beginning with mpg123-1.7.0 . + * What you should bear in mind is that (SSE, etc) optimized routines may be absent + * for some formats. We do have SSE for 16, 32 bit and float, though. + * 24 bit integer is done via postprocessing of 32 bit output -- just cutting + * the last byte, no rounding, even. If you want better, do it yourself. + * + * All formats are in native byte order. If you need different endinaness, you + * can simply postprocess the output buffers (libmpg123 wouldn't do anything else). + * mpg123_encsize() can be helpful there. + */ +enum mpg123_enc_enum +{ + MPG123_ENC_8 = 0x00f /**< 0000 0000 1111 Some 8 bit integer encoding. */ + ,MPG123_ENC_16 = 0x040 /**< 0000 0100 0000 Some 16 bit integer encoding. */ + ,MPG123_ENC_24 = 0x4000 /**< 0100 0000 0000 0000 Some 24 bit integer encoding. */ + ,MPG123_ENC_32 = 0x100 /**< 0001 0000 0000 Some 32 bit integer encoding. */ + ,MPG123_ENC_SIGNED = 0x080 /**< 0000 1000 0000 Some signed integer encoding. */ + ,MPG123_ENC_FLOAT = 0xe00 /**< 1110 0000 0000 Some float encoding. */ + ,MPG123_ENC_SIGNED_16 = (MPG123_ENC_16|MPG123_ENC_SIGNED|0x10) /**< 1101 0000 signed 16 bit */ + ,MPG123_ENC_UNSIGNED_16 = (MPG123_ENC_16|0x20) /**< 0110 0000 unsigned 16 bit */ + ,MPG123_ENC_UNSIGNED_8 = 0x01 /**< 0000 0001 unsigned 8 bit */ + ,MPG123_ENC_SIGNED_8 = (MPG123_ENC_SIGNED|0x02) /**< 1000 0010 signed 8 bit */ + ,MPG123_ENC_ULAW_8 = 0x04 /**< 0000 0100 ulaw 8 bit */ + ,MPG123_ENC_ALAW_8 = 0x08 /**< 0000 1000 alaw 8 bit */ + ,MPG123_ENC_SIGNED_32 = MPG123_ENC_32|MPG123_ENC_SIGNED|0x1000 /**< 0001 0001 1000 0000 signed 32 bit */ + ,MPG123_ENC_UNSIGNED_32 = MPG123_ENC_32|0x2000 /**< 0010 0001 0000 0000 unsigned 32 bit */ + ,MPG123_ENC_SIGNED_24 = MPG123_ENC_24|MPG123_ENC_SIGNED|0x1000 /**< 0101 0000 1000 0000 signed 24 bit */ + ,MPG123_ENC_UNSIGNED_24 = MPG123_ENC_24|0x2000 /**< 0110 0000 0000 0000 unsigned 24 bit */ + ,MPG123_ENC_FLOAT_32 = 0x200 /**< 0010 0000 0000 32bit float */ + ,MPG123_ENC_FLOAT_64 = 0x400 /**< 0100 0000 0000 64bit float */ + ,MPG123_ENC_ANY = ( MPG123_ENC_SIGNED_16 | MPG123_ENC_UNSIGNED_16 | MPG123_ENC_UNSIGNED_8 + | MPG123_ENC_SIGNED_8 | MPG123_ENC_ULAW_8 | MPG123_ENC_ALAW_8 + | MPG123_ENC_SIGNED_32 | MPG123_ENC_UNSIGNED_32 + | MPG123_ENC_SIGNED_24 | MPG123_ENC_UNSIGNED_24 + | MPG123_ENC_FLOAT_32 | MPG123_ENC_FLOAT_64 ) /**< Any encoding on the list. */ +}; + +/** They can be combined into one number (3) to indicate mono and stereo... */ +enum mpg123_channelcount +{ + MPG123_MONO = 1 + ,MPG123_STEREO = 2 +}; + +/** An array of supported standard sample rates + * These are possible native sample rates of MPEG audio files. + * You can still force mpg123 to resample to a different one, but by default you will only get audio in one of these samplings. + * \param list Store a pointer to the sample rates array there. + * \param number Store the number of sample rates there. */ +MPG123_EXPORT void mpg123_rates(const long **list, size_t *number); + +/** An array of supported audio encodings. + * An audio encoding is one of the fully qualified members of mpg123_enc_enum (MPG123_ENC_SIGNED_16, not MPG123_SIGNED). + * \param list Store a pointer to the encodings array there. + * \param number Store the number of encodings there. */ +MPG123_EXPORT void mpg123_encodings(const int **list, size_t *number); + +/** Return the size (in bytes) of one mono sample of the named encoding. + * \param encoding The encoding value to analyze. + * \return positive size of encoding in bytes, 0 on invalid encoding. */ +MPG123_EXPORT int mpg123_encsize(int encoding); + +/** Configure a mpg123 handle to accept no output format at all, + * use before specifying supported formats with mpg123_format + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_format_none(mpg123_handle *mh); + +/** Configure mpg123 handle to accept all formats + * (also any custom rate you may set) -- this is default. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_format_all(mpg123_handle *mh); + +/** Set the audio format support of a mpg123_handle in detail: + * \param mh audio decoder handle + * \param rate The sample rate value (in Hertz). + * \param channels A combination of MPG123_STEREO and MPG123_MONO. + * \param encodings A combination of accepted encodings for rate and channels, p.ex MPG123_ENC_SIGNED16 | MPG123_ENC_ULAW_8 (or 0 for no support). Please note that some encodings may not be supported in the library build and thus will be ignored here. + * \return MPG123_OK on success, MPG123_ERR if there was an error. */ +MPG123_EXPORT int mpg123_format(mpg123_handle *mh, long rate, int channels, int encodings); + +/** Check to see if a specific format at a specific rate is supported + * by mpg123_handle. + * \return 0 for no support (that includes invalid parameters), MPG123_STEREO, + * MPG123_MONO or MPG123_STEREO|MPG123_MONO. */ +MPG123_EXPORT int mpg123_format_support(mpg123_handle *mh, long rate, int encoding); + +/** Get the current output format written to the addresses given. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_getformat(mpg123_handle *mh, long *rate, int *channels, int *encoding); + +/*@}*/ + + +/** \defgroup mpg123_input mpg123 file input and decoding + * + * Functions for input bitstream and decoding operations. + * Decoding/seek functions may also return message codes MPG123_DONE, MPG123_NEW_FORMAT and MPG123_NEED_MORE (please read up on these on how to react!). + * @{ + */ + +/* reading samples / triggering decoding, possible return values: */ +/** Enumeration of the error codes returned by libmpg123 functions. */ + +/** Open and prepare to decode the specified file by filesystem path. + * This does not open HTTP urls; libmpg123 contains no networking code. + * If you want to decode internet streams, use mpg123_open_fd() or mpg123_open_feed(). + * \param path filesystem path + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_open(mpg123_handle *mh, const char *path); + +/** Use an already opened file descriptor as the bitstream input + * mpg123_close() will _not_ close the file descriptor. + */ +MPG123_EXPORT int mpg123_open_fd(mpg123_handle *mh, int fd); + +/** Use an opaque handle as bitstream input. This works only with the + * replaced I/O from mpg123_replace_reader_handle()! + * mpg123_close() will call the cleanup callback for your handle (if you gave one). + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_open_handle(mpg123_handle *mh, void *iohandle); + +/** Open a new bitstream and prepare for direct feeding + * This works together with mpg123_decode(); you are responsible for reading and feeding the input bitstream. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_open_feed(mpg123_handle *mh); + +/** Closes the source, if libmpg123 opened it. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_close(mpg123_handle *mh); + +/** Read from stream and decode up to outmemsize bytes. + * \param outmemory address of output buffer to write to + * \param outmemsize maximum number of bytes to write + * \param done address to store the number of actually decoded bytes to + * \return MPG123_OK or error/message code + */ +MPG123_EXPORT int mpg123_read(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done); + +/** Feed data for a stream that has been opened with mpg123_open_feed(). + * It's give and take: You provide the bytestream, mpg123 gives you the decoded samples. + * \param in input buffer + * \param size number of input bytes + * \return MPG123_OK or error/message code. + */ +MPG123_EXPORT int mpg123_feed(mpg123_handle *mh, const unsigned char *in, size_t size); + +/** Decode MPEG Audio from inmemory to outmemory. + * This is very close to a drop-in replacement for old mpglib. + * When you give zero-sized output buffer the input will be parsed until + * decoded data is available. This enables you to get MPG123_NEW_FORMAT (and query it) + * without taking decoded data. + * Think of this function being the union of mpg123_read() and mpg123_feed() (which it actually is, sort of;-). + * You can actually always decide if you want those specialized functions in separate steps or one call this one here. + * \param inmemory input buffer + * \param inmemsize number of input bytes + * \param outmemory output buffer + * \param outmemsize maximum number of output bytes + * \param done address to store the number of actually decoded bytes to + * \return error/message code (watch out especially for MPG123_NEED_MORE) + */ +MPG123_EXPORT int mpg123_decode(mpg123_handle *mh, const unsigned char *inmemory, size_t inmemsize, unsigned char *outmemory, size_t outmemsize, size_t *done); + +/** Decode next MPEG frame to internal buffer + * or read a frame and return after setting a new format. + * \param num current frame offset gets stored there + * \param audio This pointer is set to the internal buffer to read the decoded audio from. + * \param bytes number of output bytes ready in the buffer + * \return MPG123_OK or error/message code + */ +MPG123_EXPORT int mpg123_decode_frame(mpg123_handle *mh, off_t *num, unsigned char **audio, size_t *bytes); + +/** Decode current MPEG frame to internal buffer. + * Warning: This is experimental API that might change in future releases! + * Please watch mpg123 development closely when using it. + * \param num last frame offset gets stored there + * \param audio this pointer is set to the internal buffer to read the decoded audio from. + * \param bytes number of output bytes ready in the buffer + * \return MPG123_OK or error/message code + */ +MPG123_EXPORT int mpg123_framebyframe_decode(mpg123_handle *mh, off_t *num, unsigned char **audio, size_t *bytes); + +/** Find, read and parse the next mp3 frame + * Warning: This is experimental API that might change in future releases! + * Please watch mpg123 development closely when using it. + * \return MPG123_OK or error/message code + */ +MPG123_EXPORT int mpg123_framebyframe_next(mpg123_handle *mh); + +/** Get access to the raw input data for the last parsed frame. + * This gives you a direct look (and write access) to the frame body data. + * Together with the raw header, you can reconstruct the whole raw MPEG stream without junk and meta data, or play games by actually modifying the frame body data before decoding this frame (mpg123_framebyframe_decode()). + * A more sane use would be to use this for CRC checking (see mpg123_info() and MPG123_CRC), the first two bytes of the body make up the CRC16 checksum, if present. + * You can provide NULL for a parameter pointer when you are not interested in the value. + * + * \param header the 4-byte MPEG header + * \param bodydata pointer to the frame body stored in the handle (without the header) + * \param bodybytes size of frame body in bytes (without the header) + * \return MPG123_OK if there was a yet un-decoded frame to get the + * data from, MPG123_BAD_HANDLE or MPG123_ERR otherwise (without further + * explanation, the error state of the mpg123_handle is not modified by + * this function). + */ +MPG123_EXPORT int mpg123_framedata(mpg123_handle *mh, unsigned long *header, unsigned char **bodydata, size_t *bodybytes); + +/** Get the input position (byte offset in stream) of the last parsed frame. + * This can be used for external seek index building, for example. + * It just returns the internally stored offset, regardless of validity -- you ensure that a valid frame has been parsed before! */ +MPG123_EXPORT off_t mpg123_framepos(mpg123_handle *mh); + +/*@}*/ + + +/** \defgroup mpg123_seek mpg123 position and seeking + * + * Functions querying and manipulating position in the decoded audio bitstream. + * The position is measured in decoded audio samples, or MPEG frame offset for the specific functions. + * If gapless code is in effect, the positions are adjusted to compensate the skipped padding/delay - meaning, you should not care about that at all and just use the position defined for the samples you get out of the decoder;-) + * The general usage is modelled after stdlib's ftell() and fseek(). + * Especially, the whence parameter for the seek functions has the same meaning as the one for fseek() and needs the same constants from stdlib.h: + * - SEEK_SET: set position to (or near to) specified offset + * - SEEK_CUR: change position by offset from now + * - SEEK_END: set position to offset from end + * + * Note that sample-accurate seek only works when gapless support has been enabled at compile time; seek is frame-accurate otherwise. + * Also, really sample-accurate seeking (meaning that you get the identical sample value after seeking compared to plain decoding up to the position) is only guaranteed when you do not mess with the position code by using MPG123_UPSPEED, MPG123_DOWNSPEED or MPG123_START_FRAME. The first two mainly should cause trouble with NtoM resampling, but in any case with these options in effect, you have to keep in mind that the sample offset is not the same as counting the samples you get from decoding since mpg123 counts the skipped samples, too (or the samples played twice only once)! + * Short: When you care about the sample position, don't mess with those parameters;-) + * Also, seeking is not guaranteed to work for all streams (underlying stream may not support it). + * And yet another caveat: If the stream is concatenated out of differing pieces (Frankenstein stream), seeking may suffer, too. + * + * @{ + */ + +/** Returns the current position in samples. + * On the next successful read, you'd get that sample. + * \return sample offset or MPG123_ERR (null handle) + */ +MPG123_EXPORT off_t mpg123_tell(mpg123_handle *mh); + +/** Returns the frame number that the next read will give you data from. + * \return frame offset or MPG123_ERR (null handle) + */ +MPG123_EXPORT off_t mpg123_tellframe(mpg123_handle *mh); + +/** Returns the current byte offset in the input stream. + * \return byte offset or MPG123_ERR (null handle) + */ +MPG123_EXPORT off_t mpg123_tell_stream(mpg123_handle *mh); + +/** Seek to a desired sample offset. + * Set whence to SEEK_SET, SEEK_CUR or SEEK_END. + * \return The resulting offset >= 0 or error/message code */ +MPG123_EXPORT off_t mpg123_seek(mpg123_handle *mh, off_t sampleoff, int whence); + +/** Seek to a desired sample offset in data feeding mode. + * This just prepares things to be right only if you ensure that the next chunk of input data will be from input_offset byte position. + * \param input_offset The position it expects to be at the + * next time data is fed to mpg123_decode(). + * \return The resulting offset >= 0 or error/message code */ +MPG123_EXPORT off_t mpg123_feedseek(mpg123_handle *mh, off_t sampleoff, int whence, off_t *input_offset); + +/** Seek to a desired MPEG frame index. + * Set whence to SEEK_SET, SEEK_CUR or SEEK_END. + * \return The resulting offset >= 0 or error/message code */ +MPG123_EXPORT off_t mpg123_seek_frame(mpg123_handle *mh, off_t frameoff, int whence); + +/** Return a MPEG frame offset corresponding to an offset in seconds. + * This assumes that the samples per frame do not change in the file/stream, which is a good assumption for any sane file/stream only. + * \return frame offset >= 0 or error/message code */ +MPG123_EXPORT off_t mpg123_timeframe(mpg123_handle *mh, double sec); + +/** Give access to the frame index table that is managed for seeking. + * You are asked not to modify the values... Use mpg123_set_index to set the + * seek index + * \param offsets pointer to the index array + * \param step one index byte offset advances this many MPEG frames + * \param fill number of recorded index offsets; size of the array + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_index(mpg123_handle *mh, off_t **offsets, off_t *step, size_t *fill); + +/** Set the frame index table + * Setting offsets to NULL and fill > 0 will allocate fill entries. Setting offsets + * to NULL and fill to 0 will clear the index and free the allocated memory used by the index. + * \param offsets pointer to the index array + * \param step one index byte offset advances this many MPEG frames + * \param fill number of recorded index offsets; size of the array + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_set_index(mpg123_handle *mh, off_t *offsets, off_t step, size_t fill); + +/** Get information about current and remaining frames/seconds. + * WARNING: This function is there because of special usage by standalone mpg123 and may be removed in the final version of libmpg123! + * You provide an offset (in frames) from now and a number of output bytes + * served by libmpg123 but not yet played. You get the projected current frame + * and seconds, as well as the remaining frames/seconds. This does _not_ care + * about skipped samples due to gapless playback. */ +MPG123_EXPORT int mpg123_position( mpg123_handle *mh, off_t frame_offset, off_t buffered_bytes, off_t *current_frame, off_t *frames_left, double *current_seconds, double *seconds_left); + +/*@}*/ + + +/** \defgroup mpg123_voleq mpg123 volume and equalizer + * + * @{ + */ + +enum mpg123_channels +{ + MPG123_LEFT=0x1 /**< The Left Channel. */ + ,MPG123_RIGHT=0x2 /**< The Right Channel. */ + ,MPG123_LR=0x3 /**< Both left and right channel; same as MPG123_LEFT|MPG123_RIGHT */ +}; + +/** Set the 32 Band Audio Equalizer settings. + * \param channel Can be MPG123_LEFT, MPG123_RIGHT or MPG123_LEFT|MPG123_RIGHT for both. + * \param band The equaliser band to change (from 0 to 31) + * \param val The (linear) adjustment factor. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_eq(mpg123_handle *mh, enum mpg123_channels channel, int band, double val); + +/** Get the 32 Band Audio Equalizer settings. + * \param channel Can be MPG123_LEFT, MPG123_RIGHT or MPG123_LEFT|MPG123_RIGHT for (arithmetic mean of) both. + * \param band The equaliser band to change (from 0 to 31) + * \return The (linear) adjustment factor (zero for pad parameters) */ +MPG123_EXPORT double mpg123_geteq(mpg123_handle *mh, enum mpg123_channels channel, int band); + +/** Reset the 32 Band Audio Equalizer settings to flat + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_reset_eq(mpg123_handle *mh); + +/** Set the absolute output volume including the RVA setting, + * vol<0 just applies (a possibly changed) RVA setting. */ +MPG123_EXPORT int mpg123_volume(mpg123_handle *mh, double vol); + +/** Adjust output volume including the RVA setting by chosen amount */ +MPG123_EXPORT int mpg123_volume_change(mpg123_handle *mh, double change); + +/** Return current volume setting, the actual value due to RVA, and the RVA + * adjustment itself. It's all as double float value to abstract the sample + * format. The volume values are linear factors / amplitudes (not percent) + * and the RVA value is in decibels. */ +MPG123_EXPORT int mpg123_getvolume(mpg123_handle *mh, double *base, double *really, double *rva_db); + +/* TODO: Set some preamp in addition / to replace internal RVA handling? */ + +/*@}*/ + + +/** \defgroup mpg123_status mpg123 status and information + * + * @{ + */ + +/** Enumeration of the mode types of Variable Bitrate */ +enum mpg123_vbr { + MPG123_CBR=0, /**< Constant Bitrate Mode (default) */ + MPG123_VBR, /**< Variable Bitrate Mode */ + MPG123_ABR /**< Average Bitrate Mode */ +}; + +/** Enumeration of the MPEG Versions */ +enum mpg123_version { + MPG123_1_0=0, /**< MPEG Version 1.0 */ + MPG123_2_0, /**< MPEG Version 2.0 */ + MPG123_2_5 /**< MPEG Version 2.5 */ +}; + + +/** Enumeration of the MPEG Audio mode. + * Only the mono mode has 1 channel, the others have 2 channels. */ +enum mpg123_mode { + MPG123_M_STEREO=0, /**< Standard Stereo. */ + MPG123_M_JOINT, /**< Joint Stereo. */ + MPG123_M_DUAL, /**< Dual Channel. */ + MPG123_M_MONO /**< Single Channel. */ +}; + + +/** Enumeration of the MPEG Audio flag bits */ +enum mpg123_flags { + MPG123_CRC=0x1, /**< The bitstream is error protected using 16-bit CRC. */ + MPG123_COPYRIGHT=0x2, /**< The bitstream is copyrighted. */ + MPG123_PRIVATE=0x4, /**< The private bit has been set. */ + MPG123_ORIGINAL=0x8 /**< The bitstream is an original, not a copy. */ +}; + +/** Data structure for storing information about a frame of MPEG Audio */ +struct mpg123_frameinfo +{ + enum mpg123_version version; /**< The MPEG version (1.0/2.0/2.5). */ + int layer; /**< The MPEG Audio Layer (MP1/MP2/MP3). */ + long rate; /**< The sampling rate in Hz. */ + enum mpg123_mode mode; /**< The audio mode (Mono, Stereo, Joint-stero, Dual Channel). */ + int mode_ext; /**< The mode extension bit flag. */ + int framesize; /**< The size of the frame (in bytes, including header). */ + enum mpg123_flags flags; /**< MPEG Audio flag bits. Just now I realize that it should be declared as int, not enum. It's a bitwise combination of the enum values. */ + int emphasis; /**< The emphasis type. */ + int bitrate; /**< Bitrate of the frame (kbps). */ + int abr_rate; /**< The target average bitrate. */ + enum mpg123_vbr vbr; /**< The VBR mode. */ +}; + +/** Get frame information about the MPEG audio bitstream and store it in a mpg123_frameinfo structure. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_info(mpg123_handle *mh, struct mpg123_frameinfo *mi); + +/** Get the safe output buffer size for all cases (when you want to replace the internal buffer) */ +MPG123_EXPORT size_t mpg123_safe_buffer(void); + +/** Make a full parsing scan of each frame in the file. ID3 tags are found. An accurate length + * value is stored. Seek index will be filled. A seek back to current position + * is performed. At all, this function refuses work when stream is + * not seekable. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_scan(mpg123_handle *mh); + +/** Return, if possible, the full (expected) length of current track in samples. + * \return length >= 0 or MPG123_ERR if there is no length guess possible. */ +MPG123_EXPORT off_t mpg123_length(mpg123_handle *mh); + +/** Override the value for file size in bytes. + * Useful for getting sensible track length values in feed mode or for HTTP streams. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_set_filesize(mpg123_handle *mh, off_t size); + +/** Returns the time (seconds) per frame; <0 is error. */ +MPG123_EXPORT double mpg123_tpf(mpg123_handle *mh); + +/** Returns the samples per frame for the most recently parsed frame; <0 is error. */ +MPG123_EXPORT int mpg123_spf(mpg123_handle *mh); + +/** Get and reset the clip count. */ +MPG123_EXPORT long mpg123_clip(mpg123_handle *mh); + + +/** The key values for state information from mpg123_getstate(). */ +enum mpg123_state +{ + MPG123_ACCURATE = 1 /**< Query if positons are currently accurate (integer value, 0 if false, 1 if true). */ + ,MPG123_BUFFERFILL /**< Get fill of internal (feed) input buffer as integer byte count returned as long and as double. An error is returned on integer overflow while converting to (signed) long, but the returned floating point value shold still be fine. */ + ,MPG123_FRANKENSTEIN /**< Stream consists of carelessly stitched together files. Seeking may yield unexpected results (also with MPG123_ACCURATE, it may be confused). */ + ,MPG123_FRESH_DECODER /**< Decoder structure has been updated, possibly indicating changed stream (integer value, 0 if false, 1 if true). Flag is cleared after retrieval. */ +}; + +/** Get various current decoder/stream state information. + * \param key the key to identify the information to give. + * \param val the address to return (long) integer values to + * \param fval the address to return floating point values to + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_getstate(mpg123_handle *mh, enum mpg123_state key, long *val, double *fval); + +/*@}*/ + + +/** \defgroup mpg123_metadata mpg123 metadata handling + * + * Functions to retrieve the metadata from MPEG Audio files and streams. + * Also includes string handling functions. + * + * @{ + */ + +/** Data structure for storing strings in a safer way than a standard C-String. + * Can also hold a number of null-terminated strings. */ +typedef struct +{ + char* p; /**< pointer to the string data */ + size_t size; /**< raw number of bytes allocated */ + size_t fill; /**< number of used bytes (including closing zero byte) */ +} mpg123_string; + +/** Create and allocate memory for a new mpg123_string */ +MPG123_EXPORT void mpg123_init_string(mpg123_string* sb); + +/** Free-up mempory for an existing mpg123_string */ +MPG123_EXPORT void mpg123_free_string(mpg123_string* sb); + +/** Change the size of a mpg123_string + * \return 0 on error, 1 on success */ +MPG123_EXPORT int mpg123_resize_string(mpg123_string* sb, size_t news); + +/** Increase size of a mpg123_string if necessary (it may stay larger). + * Note that the functions for adding and setting in current libmpg123 use this instead of mpg123_resize_string(). + * That way, you can preallocate memory and safely work afterwards with pieces. + * \return 0 on error, 1 on success */ +MPG123_EXPORT int mpg123_grow_string(mpg123_string* sb, size_t news); + +/** Copy the contents of one mpg123_string string to another. + * \return 0 on error, 1 on success */ +MPG123_EXPORT int mpg123_copy_string(mpg123_string* from, mpg123_string* to); + +/** Append a C-String to an mpg123_string + * \return 0 on error, 1 on success */ +MPG123_EXPORT int mpg123_add_string(mpg123_string* sb, const char* stuff); + +/** Append a C-substring to an mpg123 string + * \return 0 on error, 1 on success + * \param from offset to copy from + * \param count number of characters to copy (a null-byte is always appended) */ +MPG123_EXPORT int mpg123_add_substring(mpg123_string *sb, const char *stuff, size_t from, size_t count); + +/** Set the conents of a mpg123_string to a C-string + * \return 0 on error, 1 on success */ +MPG123_EXPORT int mpg123_set_string(mpg123_string* sb, const char* stuff); + +/** Set the contents of a mpg123_string to a C-substring + * \return 0 on error, 1 on success + * \param from offset to copy from + * \param count number of characters to copy (a null-byte is always appended) */ +MPG123_EXPORT int mpg123_set_substring(mpg123_string *sb, const char *stuff, size_t from, size_t count); + +/** Count characters in a mpg123 string (non-null bytes or UTF-8 characters). + * \return character count + * \param sb the string + * \param utf8 a flag to tell if the string is in utf8 encoding + * Even with the fill property, the character count is not obvious as there could be multiple trailing null bytes. +*/ +MPG123_EXPORT size_t mpg123_strlen(mpg123_string *sb, int utf8); + +/** Remove trailing \r and \n, if present. + * \return 0 on error, 1 on success + * \param sb the string + */ +MPG123_EXPORT int mpg123_chomp_string(mpg123_string *sb); + +/** The mpg123 text encodings. This contains encodings we encounter in ID3 tags or ICY meta info. */ +enum mpg123_text_encoding +{ + mpg123_text_unknown = 0 /**< Unkown encoding... mpg123_id3_encoding can return that on invalid codes. */ + ,mpg123_text_utf8 = 1 /**< UTF-8 */ + ,mpg123_text_latin1 = 2 /**< ISO-8859-1. Note that sometimes latin1 in ID3 is abused for totally different encodings. */ + ,mpg123_text_icy = 3 /**< ICY metadata encoding, usually CP-1252 but we take it as UTF-8 if it qualifies as such. */ + ,mpg123_text_cp1252 = 4 /**< Really CP-1252 without any guessing. */ + ,mpg123_text_utf16 = 5 /**< Some UTF-16 encoding. The last of a set of leading BOMs (byte order mark) rules. + * When there is no BOM, big endian ordering is used. Note that UCS-2 qualifies as UTF-8 when + * you don't mess with the reserved code points. If you want to decode little endian data + * without BOM you need to prepend 0xff 0xfe yourself. */ + ,mpg123_text_utf16bom = 6 /**< Just an alias for UTF-16, ID3v2 has this as distinct code. */ + ,mpg123_text_utf16be = 7 /**< Another alias for UTF16 from ID3v2. Note, that, because of the mess that is reality, + * BOMs are used if encountered. There really is not much distinction between the UTF16 types for mpg123 + * One exception: Since this is seen in ID3v2 tags, leading null bytes are skipped for all other UTF16 + * types (we expect a BOM before real data there), not so for utf16be!*/ + ,mpg123_text_max = 7 /**< Placeholder for the maximum encoding value. */ +}; + +/** The encoding byte values from ID3v2. */ +enum mpg123_id3_enc +{ + mpg123_id3_latin1 = 0 /**< Note: This sometimes can mean anything in practice... */ + ,mpg123_id3_utf16bom = 1 /**< UTF16, UCS-2 ... it's all the same for practical purposes. */ + ,mpg123_id3_utf16be = 2 /**< Big-endian UTF-16, BOM see note for mpg123_text_utf16be. */ + ,mpg123_id3_utf8 = 3 /**< Our lovely overly ASCII-compatible 8 byte encoding for the world. */ + ,mpg123_id3_enc_max = 3 /**< Placeholder to check valid range of encoding byte. */ +}; + +/** Convert ID3 encoding byte to mpg123 encoding index. */ +MPG123_EXPORT enum mpg123_text_encoding mpg123_enc_from_id3(unsigned char id3_enc_byte); + +/** Store text data in string, after converting to UTF-8 from indicated encoding + * \return 0 on error, 1 on success (on error, mpg123_free_string is called on sb) + * \param sb target string + * \param enc mpg123 text encoding value + * \param source source buffer with plain unsigned bytes (you might need to cast from char *) + * \param source_size number of bytes in the source buffer + * + * A prominent error can be that you provided an unknown encoding value, or this build of libmpg123 lacks support for certain encodings (ID3 or ICY stuff missing). + * Also, you might want to take a bit of care with preparing the data; for example, strip leading zeroes (I have seen that). + */ +MPG123_EXPORT int mpg123_store_utf8(mpg123_string *sb, enum mpg123_text_encoding enc, const unsigned char *source, size_t source_size); + +/** Sub data structure for ID3v2, for storing various text fields (including comments). + * This is for ID3v2 COMM, TXXX and all the other text fields. + * Only COMM and TXXX have a description, only COMM and USLT have a language. + * You should consult the ID3v2 specification for the use of the various text fields ("frames" in ID3v2 documentation, I use "fields" here to separate from MPEG frames). */ +typedef struct +{ + char lang[3]; /**< Three-letter language code (not terminated). */ + char id[4]; /**< The ID3v2 text field id, like TALB, TPE2, ... (4 characters, no string termination). */ + mpg123_string description; /**< Empty for the generic comment... */ + mpg123_string text; /**< ... */ +} mpg123_text; + +/** The picture type values from ID3v2. */ +enum mpg123_id3_pic_type +{ + mpg123_id3_pic_other = 0 + ,mpg123_id3_pic_icon = 1 + ,mpg123_id3_pic_other_icon = 2 + ,mpg123_id3_pic_front_cover = 3 + ,mpg123_id3_pic_back_cover = 4 + ,mpg123_id3_pic_leaflet = 5 + ,mpg123_id3_pic_media = 6 + ,mpg123_id3_pic_lead = 7 + ,mpg123_id3_pic_artist = 8 + ,mpg123_id3_pic_conductor = 9 + ,mpg123_id3_pic_orchestra = 10 + ,mpg123_id3_pic_composer = 11 + ,mpg123_id3_pic_lyricist = 12 + ,mpg123_id3_pic_location = 13 + ,mpg123_id3_pic_recording = 14 + ,mpg123_id3_pic_performance = 15 + ,mpg123_id3_pic_video = 16 + ,mpg123_id3_pic_fish = 17 + ,mpg123_id3_pic_illustration = 18 + ,mpg123_id3_pic_artist_logo = 19 + ,mpg123_id3_pic_publisher_logo = 20 +}; + +/** Sub data structure for ID3v2, for storing picture data including comment. + * This is for the ID3v2 APIC field. You should consult the ID3v2 specification + * for the use of the APIC field ("frames" in ID3v2 documentation, I use "fields" + * here to separate from MPEG frames). */ +typedef struct +{ + char type; + mpg123_string description; + mpg123_string mime_type; + size_t size; + unsigned char* data; +} mpg123_picture; + +/** Data structure for storing IDV3v2 tags. + * This structure is not a direct binary mapping with the file contents. + * The ID3v2 text frames are allowed to contain multiple strings. + * So check for null bytes until you reach the mpg123_string fill. + * All text is encoded in UTF-8. */ +typedef struct +{ + unsigned char version; /**< 3 or 4 for ID3v2.3 or ID3v2.4. */ + mpg123_string *title; /**< Title string (pointer into text_list). */ + mpg123_string *artist; /**< Artist string (pointer into text_list). */ + mpg123_string *album; /**< Album string (pointer into text_list). */ + mpg123_string *year; /**< The year as a string (pointer into text_list). */ + mpg123_string *genre; /**< Genre String (pointer into text_list). The genre string(s) may very well need postprocessing, esp. for ID3v2.3. */ + mpg123_string *comment; /**< Pointer to last encountered comment text with empty description. */ + /* Encountered ID3v2 fields are appended to these lists. + There can be multiple occurences, the pointers above always point to the last encountered data. */ + mpg123_text *comment_list; /**< Array of comments. */ + size_t comments; /**< Number of comments. */ + mpg123_text *text; /**< Array of ID3v2 text fields (including USLT) */ + size_t texts; /**< Numer of text fields. */ + mpg123_text *extra; /**< The array of extra (TXXX) fields. */ + size_t extras; /**< Number of extra text (TXXX) fields. */ + mpg123_picture *picture; /**< Array of ID3v2 pictures fields (APIC). */ + size_t pictures; /**< Number of picture (APIC) fields. */ +} mpg123_id3v2; + +/** Data structure for ID3v1 tags (the last 128 bytes of a file). + * Don't take anything for granted (like string termination)! + * Also note the change ID3v1.1 did: comment[28] = 0; comment[29] = track_number + * It is your task to support ID3v1 only or ID3v1.1 ...*/ +typedef struct +{ + char tag[3]; /**< Always the string "TAG", the classic intro. */ + char title[30]; /**< Title string. */ + char artist[30]; /**< Artist string. */ + char album[30]; /**< Album string. */ + char year[4]; /**< Year string. */ + char comment[30]; /**< Comment string. */ + unsigned char genre; /**< Genre index. */ +} mpg123_id3v1; + +#define MPG123_ID3 0x3 /**< 0011 There is some ID3 info. Also matches 0010 or NEW_ID3. */ +#define MPG123_NEW_ID3 0x1 /**< 0001 There is ID3 info that changed since last call to mpg123_id3. */ +#define MPG123_ICY 0xc /**< 1100 There is some ICY info. Also matches 0100 or NEW_ICY.*/ +#define MPG123_NEW_ICY 0x4 /**< 0100 There is ICY info that changed since last call to mpg123_icy. */ + +/** Query if there is (new) meta info, be it ID3 or ICY (or something new in future). + The check function returns a combination of flags. */ +MPG123_EXPORT int mpg123_meta_check(mpg123_handle *mh); /* On error (no valid handle) just 0 is returned. */ + +/** Clean up meta data storage (ID3v2 and ICY), freeing memory. */ +MPG123_EXPORT void mpg123_meta_free(mpg123_handle *mh); + +/** Point v1 and v2 to existing data structures wich may change on any next read/decode function call. + * v1 and/or v2 can be set to NULL when there is no corresponding data. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_id3(mpg123_handle *mh, mpg123_id3v1 **v1, mpg123_id3v2 **v2); + +/** Point icy_meta to existing data structure wich may change on any next read/decode function call. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_icy(mpg123_handle *mh, char **icy_meta); /* same for ICY meta string */ + +/** Decode from windows-1252 (the encoding ICY metainfo used) to UTF-8. + * Note that this is very similar to mpg123_store_utf8(&sb, mpg123_text_icy, icy_text, strlen(icy_text+1)) . + * \param icy_text The input data in ICY encoding + * \return pointer to newly allocated buffer with UTF-8 data (You free() it!) */ +MPG123_EXPORT char* mpg123_icy2utf8(const char* icy_text); + + +/* @} */ + + +/** \defgroup mpg123_advpar mpg123 advanced parameter API + * + * Direct access to a parameter set without full handle around it. + * Possible uses: + * - Influence behaviour of library _during_ initialization of handle (MPG123_VERBOSE). + * - Use one set of parameters for multiple handles. + * + * The functions for handling mpg123_pars (mpg123_par() and mpg123_fmt() + * family) directly return a fully qualified mpg123 error code, the ones + * operating on full handles normally MPG123_OK or MPG123_ERR, storing the + * specific error code itseld inside the handle. + * + * @{ + */ + +/** Opaque structure for the libmpg123 decoder parameters. */ +struct mpg123_pars_struct; + +/** Opaque structure for the libmpg123 decoder parameters. */ +typedef struct mpg123_pars_struct mpg123_pars; + +/** Create a handle with preset parameters. */ +MPG123_EXPORT mpg123_handle *mpg123_parnew(mpg123_pars *mp, const char* decoder, int *error); + +/** Allocate memory for and return a pointer to a new mpg123_pars */ +MPG123_EXPORT mpg123_pars *mpg123_new_pars(int *error); + +/** Delete and free up memory used by a mpg123_pars data structure */ +MPG123_EXPORT void mpg123_delete_pars(mpg123_pars* mp); + +/** Configure mpg123 parameters to accept no output format at all, + * use before specifying supported formats with mpg123_format + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_fmt_none(mpg123_pars *mp); + +/** Configure mpg123 parameters to accept all formats + * (also any custom rate you may set) -- this is default. + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_fmt_all(mpg123_pars *mp); + +/** Set the audio format support of a mpg123_pars in detail: + \param rate The sample rate value (in Hertz). + \param channels A combination of MPG123_STEREO and MPG123_MONO. + \param encodings A combination of accepted encodings for rate and channels, p.ex MPG123_ENC_SIGNED16|MPG123_ENC_ULAW_8 (or 0 for no support). + \return MPG123_OK on success +*/ +MPG123_EXPORT int mpg123_fmt(mpg123_pars *mp, long rate, int channels, int encodings); /* 0 is good, -1 is error */ + +/** Check to see if a specific format at a specific rate is supported + * by mpg123_pars. + * \return 0 for no support (that includes invalid parameters), MPG123_STEREO, + * MPG123_MONO or MPG123_STEREO|MPG123_MONO. */ +MPG123_EXPORT int mpg123_fmt_support(mpg123_pars *mp, long rate, int encoding); + +/** Set a specific parameter, for a specific mpg123_pars, using a parameter + * type key chosen from the mpg123_parms enumeration, to the specified value. */ +MPG123_EXPORT int mpg123_par(mpg123_pars *mp, enum mpg123_parms type, long value, double fvalue); + +/** Get a specific parameter, for a specific mpg123_pars. + * See the mpg123_parms enumeration for a list of available parameters. */ +MPG123_EXPORT int mpg123_getpar(mpg123_pars *mp, enum mpg123_parms type, long *val, double *fval); + +/* @} */ + + +/** \defgroup mpg123_lowio mpg123 low level I/O + * You may want to do tricky stuff with I/O that does not work with mpg123's default file access or you want to make it decode into your own pocket... + * + * @{ */ + +/** Replace default internal buffer with user-supplied buffer. + * Instead of working on it's own private buffer, mpg123 will directly use the one you provide for storing decoded audio. + * Note that the required buffer size could be bigger than expected from output + * encoding if libmpg123 has to convert from primary decoder output (p.ex. 32 bit + * storage for 24 bit output. + * \param data pointer to user buffer + * \param size of buffer in bytes + * \return MPG123_OK on success + */ +MPG123_EXPORT int mpg123_replace_buffer(mpg123_handle *mh, unsigned char *data, size_t size); + +/** The max size of one frame's decoded output with current settings. + * Use that to determine an appropriate minimum buffer size for decoding one frame. */ +MPG123_EXPORT size_t mpg123_outblock(mpg123_handle *mh); + +/** Replace low-level stream access functions; read and lseek as known in POSIX. + * You can use this to make any fancy file opening/closing yourself, + * using mpg123_open_fd() to set the file descriptor for your read/lseek (doesn't need to be a "real" file descriptor...). + * Setting a function to NULL means that the default internal read is + * used (active from next mpg123_open call on). + * Note: As it would be troublesome to mess with this while having a file open, + * this implies mpg123_close(). */ +MPG123_EXPORT int mpg123_replace_reader(mpg123_handle *mh, ssize_t (*r_read) (int, void *, size_t), off_t (*r_lseek)(int, off_t, int)); + +/** Replace I/O functions with your own ones operating on some kind of handle instead of integer descriptors. + * The handle is a void pointer, so you can pass any data you want... + * mpg123_open_handle() is the call you make to use the I/O defined here. + * There is no fallback to internal read/seek here. + * Note: As it would be troublesome to mess with this while having a file open, + * this mpg123_close() is implied here. + * \param r_read The callback for reading (behaviour like posix read). + * \param r_lseek The callback for seeking (like posix lseek). + * \param cleanup A callback to clean up an I/O handle on mpg123_close, can be NULL for none (you take care of cleaning your handles). */ +MPG123_EXPORT int mpg123_replace_reader_handle(mpg123_handle *mh, ssize_t (*r_read) (void *, void *, size_t), off_t (*r_lseek)(void *, off_t, int), void (*cleanup)(void*)); + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/oplsynth/CMakeLists.txt b/thirdparty/oplsynth/CMakeLists.txt new file mode 100644 index 000000000..08e5096e5 --- /dev/null +++ b/thirdparty/oplsynth/CMakeLists.txt @@ -0,0 +1,31 @@ +add_library(oplsynth OBJECT + fmopl.cpp + musicblock.cpp + nukedopl3.cpp + opl_mus_player.cpp + OPL3.cpp + oplio.cpp + dosbox/opl.cpp +) + +file(GLOB HEADER_FILES oplsynth/*.h) +target_sources(oplsynth PRIVATE ${HEADER_FILES}) + +target_include_directories(oplsynth INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE oplsynth) + +set_target_properties(oplsynth +PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON +) + +use_fast_math(oplsynth) +require_stricmp(oplsynth PRIVATE) +require_strnicmp(oplsynth PRIVATE) + +if(COMPILER_IS_GNUC_COMPATIBLE) + target_compile_options(oplsynth + PRIVATE + "-Wall" "-Wextra" "-Wno-unused-parameter" "-fomit-frame-pointer" + ) +endif() diff --git a/thirdparty/oplsynth/OPL3.cpp b/thirdparty/oplsynth/OPL3.cpp new file mode 100644 index 000000000..9279d9a7d --- /dev/null +++ b/thirdparty/oplsynth/OPL3.cpp @@ -0,0 +1,1883 @@ +/* + * File: OPL3.java + * Software implementation of the Yamaha YMF262 sound generator. + * Copyright (C) 2008 Robson Cozendey + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * One of the objectives of this emulator is to stimulate further research in the + * OPL3 chip emulation. There was an explicit effort in making no optimizations, + * and making the code as legible as possible, so that a new programmer + * interested in modify and improve upon it could do so more easily. + * This emulator's main body of information was taken from reverse engineering of + * the OPL3 chip, from the YMF262 Datasheet and from the OPL3 section in the + * YMF278b Application's Manual, + * together with the vibrato table information, eighth waveform parameter + * information and feedback averaging information provided in MAME's YMF262 and + * YM3812 emulators, by Jarek Burczynski and Tatsuyuki Satoh. + * This emulator has a high degree of accuracy, and most of music files sound + * almost identical, exception made in some games which uses specific parts of + * the rhythm section. In this respect, some parts of the rhythm mode are still + * only an approximation of the real chip. + * The other thing to note is that this emulator was done through recordings of + * the SB16 DAC, so it has not bitwise precision. Additional equipment should be + * used to verify the samples directly from the chip, and allow this exact + * per-sample correspondence. As a good side-effect, since this emulator uses + * floating point and has a more fine-grained envelope generator, it can produce + * sometimes a crystal-clear, denser kind of OPL3 sound that, because of that, + * may be useful for creating new music. + * + * Version 1.0.6 + * + */ + +#include +#include +#include +#include + +#include "opl.h" +#include "opl3_Float.h" + +#define VOLUME_MUL 0.3333 + +static const double OPL_PI = 3.14159265358979323846; // matches value in gcc v2 math.h + +namespace JavaOPL3 +{ + +class Operator; + +static inline double StripIntPart(double num) +{ +#if 0 + double dontcare; + return modf(num, &dontcare); +#else + return num - xs_RoundToInt(num); +#endif +} + +// +// Channels +// + + +class Channel +{ +protected: + double feedback[2]; + + int fnuml, fnumh, kon, block, fb, cha, chb, cnt; + + // Factor to convert between normalized amplitude to normalized + // radians. The amplitude maximum is equivalent to 8*Pi radians. +#define toPhase (4.f) + +public: + int channelBaseAddress; + + double leftPan, rightPan; + + Channel (int baseAddress, double startvol); + virtual ~Channel() {} + void update_2_KON1_BLOCK3_FNUMH2(class OPL3 *OPL3); + void update_FNUML8(class OPL3 *OPL3); + void update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(class OPL3 *OPL3); + void updateChannel(class OPL3 *OPL3); + void updatePan(class OPL3 *OPL3); + virtual double getChannelOutput(class OPL3 *OPL3) = 0; + + virtual void keyOn() = 0; + virtual void keyOff() = 0; + virtual void updateOperators(class OPL3 *OPL3) = 0; +}; + + +class Channel2op : public Channel +{ +public: + Operator *op1, *op2; + + Channel2op (int baseAddress, double startvol, Operator *o1, Operator *o2); + double getChannelOutput(class OPL3 *OPL3); + + void keyOn(); + void keyOff(); + void updateOperators(class OPL3 *OPL3); +}; + + +class Channel4op : public Channel +{ +public: + Operator *op1, *op2, *op3, *op4; + + Channel4op (int baseAddress, double startvol, Operator *o1, Operator *o2, Operator *o3, Operator *o4); + double getChannelOutput(class OPL3 *OPL3); + + void keyOn(); + void keyOff(); + void updateOperators(class OPL3 *OPL3); +}; + +// There's just one instance of this class, that fills the eventual gaps in the Channel array; +class DisabledChannel : public Channel +{ +public: + DisabledChannel() : Channel(0, 0) { } + double getChannelOutput(class OPL3 *OPL3) { return 0; } + void keyOn() { } + void keyOff() { } + void updateOperators(class OPL3 *OPL3) { } +}; + + + +// +// Envelope Generator +// + + +class EnvelopeGenerator +{ +public: + enum Stage {ATTACK,DECAY,SUSTAIN,RELEASE,OFF}; + Stage stage; + int actualAttackRate, actualDecayRate, actualReleaseRate; + double xAttackIncrement, xMinimumInAttack; + double dBdecayIncrement; + double dBreleaseIncrement; + double attenuation, totalLevel, sustainLevel; + double x, envelope; + +public: + EnvelopeGenerator(); + void setActualSustainLevel(int sl); + void setTotalLevel(int tl); + void setAtennuation(int f_number, int block, int ksl); + void setActualAttackRate(int attackRate, int ksr, int keyScaleNumber); + void setActualDecayRate(int decayRate, int ksr, int keyScaleNumber); + void setActualReleaseRate(int releaseRate, int ksr, int keyScaleNumber); +private: + int calculateActualRate(int rate, int ksr, int keyScaleNumber); +public: + double getEnvelope(OPL3 *OPL3, int egt, int am); + void keyOn(); + void keyOff(); + +private: + static double dBtoX(double dB); + static double percentageToDB(double percentage); + static double percentageToX(double percentage); +}; + + +// +// Phase Generator +// + + +class PhaseGenerator { + double phase, phaseIncrement; + +public: + PhaseGenerator(); + void setFrequency(int f_number, int block, int mult); + double getPhase(class OPL3 *OPL3, int vib); + void keyOn(); +}; + + +// +// Operators +// + + +class Operator +{ +public: + PhaseGenerator phaseGenerator; + EnvelopeGenerator envelopeGenerator; + + double envelope, phase; + + int operatorBaseAddress; + int am, vib, ksr, egt, mult, ksl, tl, ar, dr, sl, rr, ws; + int keyScaleNumber, f_number, block; + + static const double noModulator; + +public: + Operator(int baseAddress); + void update_AM1_VIB1_EGT1_KSR1_MULT4(class OPL3 *OPL3); + void update_KSL2_TL6(class OPL3 *OPL3); + void update_AR4_DR4(class OPL3 *OPL3); + void update_SL4_RR4(class OPL3 *OPL3); + void update_5_WS3(class OPL3 *OPL3); + double getOperatorOutput(class OPL3 *OPL3, double modulator); + + void keyOn(); + void keyOff(); + void updateOperator(class OPL3 *OPL3, int ksn, int f_num, int blk); +protected: + double getOutput(double modulator, double outputPhase, double *waveform); +}; + + +// +// Rhythm +// + +// The getOperatorOutput() method in TopCymbalOperator, HighHatOperator and SnareDrumOperator +// were made through purely empyrical reverse engineering of the OPL3 output. + +class RhythmChannel : public Channel2op +{ +public: + RhythmChannel(int baseAddress, double startvol, Operator *o1, Operator *o2) + : Channel2op(baseAddress, startvol, o1, o2) + { } + double getChannelOutput(class OPL3 *OPL3); + + // Rhythm channels are always running, + // only the envelope is activated by the user. + void keyOn() { } + void keyOff() { } +}; + +class HighHatSnareDrumChannel : public RhythmChannel { + static const int highHatSnareDrumChannelBaseAddress = 7; +public: + HighHatSnareDrumChannel(double startvol, Operator *o1, Operator *o2) + : RhythmChannel(highHatSnareDrumChannelBaseAddress, startvol, o1, o2) + { } +}; + +class TomTomTopCymbalChannel : public RhythmChannel { + static const int tomTomTopCymbalChannelBaseAddress = 8; +public: + TomTomTopCymbalChannel(double startvol, Operator *o1, Operator *o2) + : RhythmChannel(tomTomTopCymbalChannelBaseAddress, startvol, o1, o2) + { } +}; + +class TopCymbalOperator : public Operator { + static const int topCymbalOperatorBaseAddress = 0x15; +public: + TopCymbalOperator(int baseAddress); + TopCymbalOperator(); + double getOperatorOutput(class OPL3 *OPL3, double modulator); + double getOperatorOutput(class OPL3 *OPL3, double modulator, double externalPhase); +}; + +class HighHatOperator : public TopCymbalOperator { + static const int highHatOperatorBaseAddress = 0x11; +public: + HighHatOperator(); + double getOperatorOutput(class OPL3 *OPL3, double modulator); +}; + +class SnareDrumOperator : public Operator { + static const int snareDrumOperatorBaseAddress = 0x14; +public: + SnareDrumOperator(); + double getOperatorOutput(class OPL3 *OPL3, double modulator); +}; + +class TomTomOperator : public Operator { + static const int tomTomOperatorBaseAddress = 0x12; +public: + TomTomOperator() : Operator(tomTomOperatorBaseAddress) { } +}; + +class BassDrumChannel : public Channel2op { + static const int bassDrumChannelBaseAddress = 6; + static const int op1BaseAddress = 0x10; + static const int op2BaseAddress = 0x13; + + Operator my_op1, my_op2; + +public: + BassDrumChannel(double startvol); + double getChannelOutput(class OPL3 *OPL3); + + // Key ON and OFF are unused in rhythm channels. + void keyOn() { } + void keyOff() { } +}; + + +// +// OPl3 Data +// + + +struct OPL3DataStruct +{ +public: + // OPL3-wide registers offsets: + static const int + _1_NTS1_6_Offset = 0x08, + DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1_Offset = 0xBD, + _7_NEW1_Offset = 0x105, + _2_CONNECTIONSEL6_Offset = 0x104; + + // The OPL3 tremolo repetition rate is 3.7 Hz. + #define tremoloFrequency (3.7) + + static const int tremoloTableLength = (int)(OPL_SAMPLE_RATE/tremoloFrequency); + static const int vibratoTableLength = 8192; + + OPL3DataStruct() + { + loadVibratoTable(); + loadTremoloTable(); + } + + // The first array is used when DVB=0 and the second array is used when DVB=1. + double vibratoTable[2][vibratoTableLength]; + + // First array used when AM = 0 and second array used when AM = 1. + double tremoloTable[2][tremoloTableLength]; + + static double calculateIncrement(double begin, double end, double period) { + return (end-begin)/OPL_SAMPLE_RATE * (1/period); + } + +private: + void loadVibratoTable(); + void loadTremoloTable(); +}; + + +// +// Channel Data +// + + +struct ChannelData +{ + static const int + _2_KON1_BLOCK3_FNUMH2_Offset = 0xB0, + FNUML8_Offset = 0xA0, + CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset = 0xC0; + + // Feedback rate in fractions of 2*Pi, normalized to (0,1): + // 0, Pi/16, Pi/8, Pi/4, Pi/2, Pi, 2*Pi, 4*Pi turns to be: + static const float feedback[8]; +}; +const float ChannelData::feedback[8] = {0,1/32.f,1/16.f,1/8.f,1/4.f,1/2.f,1,2}; + + +// +// Operator Data +// + + +struct OperatorDataStruct +{ + static const int + AM1_VIB1_EGT1_KSR1_MULT4_Offset = 0x20, + KSL2_TL6_Offset = 0x40, + AR4_DR4_Offset = 0x60, + SL4_RR4_Offset = 0x80, + _5_WS3_Offset = 0xE0; + + enum type {NO_MODULATION, CARRIER, FEEDBACK}; + + static const int waveLength = 1024; + + static const float multTable[16]; + static const float ksl3dBtable[16][8]; + + //OPL3 has eight waveforms: + double waveforms[8][waveLength]; + +#define MIN_DB (-120.0) +#define DB_TABLE_RES (4.0) +#define DB_TABLE_SIZE (int)(-MIN_DB * DB_TABLE_RES) + + double dbpow[DB_TABLE_SIZE]; + +#define ATTACK_MIN (-5.0) +#define ATTACK_MAX (8.0) +#define ATTACK_RES (0.03125) +#define ATTACK_TABLE_SIZE (int)((ATTACK_MAX - ATTACK_MIN) / ATTACK_RES) + + double attackTable[ATTACK_TABLE_SIZE]; + + OperatorDataStruct() + { + loadWaveforms(); + loaddBPowTable(); + loadAttackTable(); + } + + static double log2(double x) { + return log(x)/log(2.0); + } +private: + void loadWaveforms(); + void loaddBPowTable(); + void loadAttackTable(); +}; +const float OperatorDataStruct::multTable[16] = {0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15}; + +const float OperatorDataStruct::ksl3dBtable[16][8] = { + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,-3,-6,-9}, + {0,0,0,0,-3,-6,-9,-12}, + {0,0,0, -1.875, -4.875, -7.875, -10.875, -13.875}, + + {0,0,0,-3,-6,-9,-12,-15}, + {0,0, -1.125, -4.125, -7.125, -10.125, -13.125, -16.125}, + {0,0, -1.875, -4.875, -7.875, -10.875, -13.875, -16.875}, + {0,0, -2.625, -5.625, -8.625, -11.625, -14.625, -17.625}, + + {0,0,-3,-6,-9,-12,-15,-18}, + {0, -0.750, -3.750, -6.750, -9.750, -12.750, -15.750, -18.750}, + {0, -1.125, -4.125, -7.125, -10.125, -13.125, -16.125, -19.125}, + {0, -1.500, -4.500, -7.500, -10.500, -13.500, -16.500, -19.500}, + + {0, -1.875, -4.875, -7.875, -10.875, -13.875, -16.875, -19.875}, + {0, -2.250, -5.250, -8.250, -11.250, -14.250, -17.250, -20.250}, + {0, -2.625, -5.625, -8.625, -11.625, -14.625, -17.625, -20.625}, + {0,-3,-6,-9,-12,-15,-18,-21} +}; + +// +// Envelope Generator Data +// + + +namespace EnvelopeGeneratorData +{ + static const double MUGEN = std::numeric_limits::infinity(); + // This table is indexed by the value of Operator.ksr + // and the value of ChannelRegister.keyScaleNumber. + static const int rateOffset[2][16] = { + {0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3}, + {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} + }; + // These attack periods in miliseconds were taken from the YMF278B manual. + // The attack actual rates range from 0 to 63, with different data for + // 0%-100% and for 10%-90%: + static const double attackTimeValuesTable[64][2] = { + {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN}, + {2826.24,1482.75}, {2252.80,1155.07}, {1884.16,991.23}, {1597.44,868.35}, + {1413.12,741.38}, {1126.40,577.54}, {942.08,495.62}, {798.72,434.18}, + {706.56,370.69}, {563.20,288.77}, {471.04,247.81}, {399.36,217.09}, + + {353.28,185.34}, {281.60,144.38}, {235.52,123.90}, {199.68,108.54}, + {176.76,92.67}, {140.80,72.19}, {117.76,61.95}, {99.84,54.27}, + {88.32,46.34}, {70.40,36.10}, {58.88,30.98}, {49.92,27.14}, + {44.16,23.17}, {35.20,18.05}, {29.44,15.49}, {24.96,13.57}, + + {22.08,11.58}, {17.60,9.02}, {14.72,7.74}, {12.48,6.78}, + {11.04,5.79}, {8.80,4.51}, {7.36,3.87}, {6.24,3.39}, + {5.52,2.90}, {4.40,2.26}, {3.68,1.94}, {3.12,1.70}, + {2.76,1.45}, {2.20,1.13}, {1.84,0.97}, {1.56,0.85}, + + {1.40,0.73}, {1.12,0.61}, {0.92,0.49}, {0.80,0.43}, + {0.70,0.37}, {0.56,0.31}, {0.46,0.26}, {0.42,0.22}, + {0.38,0.19}, {0.30,0.14}, {0.24,0.11}, {0.20,0.11}, + {0.00,0.00}, {0.00,0.00}, {0.00,0.00}, {0.00,0.00} + }; + + // These decay and release periods in milliseconds were taken from the YMF278B manual. + // The rate index range from 0 to 63, with different data for + // 0%-100% and for 10%-90%: + static const double decayAndReleaseTimeValuesTable[64][2] = { + {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN}, {MUGEN,MUGEN}, + {39280.64,8212.48}, {31416.32,6574.08}, {26173.44,5509.12}, {22446.08,4730.88}, + {19640.32,4106.24}, {15708.16,3287.04}, {13086.72,2754.56}, {11223.04,2365.44}, + {9820.16,2053.12}, {7854.08,1643.52}, {6543.36,1377.28}, {5611.52,1182.72}, + + {4910.08,1026.56}, {3927.04,821.76}, {3271.68,688.64}, {2805.76,591.36}, + {2455.04,513.28}, {1936.52,410.88}, {1635.84,344.34}, {1402.88,295.68}, + {1227.52,256.64}, {981.76,205.44}, {817.92,172.16}, {701.44,147.84}, + {613.76,128.32}, {490.88,102.72}, {488.96,86.08}, {350.72,73.92}, + + {306.88,64.16}, {245.44,51.36}, {204.48,43.04}, {175.36,36.96}, + {153.44,32.08}, {122.72,25.68}, {102.24,21.52}, {87.68,18.48}, + {76.72,16.04}, {61.36,12.84}, {51.12,10.76}, {43.84,9.24}, + {38.36,8.02}, {30.68,6.42}, {25.56,5.38}, {21.92,4.62}, + + {19.20,4.02}, {15.36,3.22}, {12.80,2.68}, {10.96,2.32}, + {9.60,2.02}, {7.68,1.62}, {6.40,1.35}, {5.48,1.15}, + {4.80,1.01}, {3.84,0.81}, {3.20,0.69}, {2.74,0.58}, + {2.40,0.51}, {2.40,0.51}, {2.40,0.51}, {2.40,0.51} + }; +}; + +class OPL3 : public OPLEmul +{ +public: + uint8_t registers[0x200]; + + Operator *operators[2][0x20]; + Channel2op *channels2op[2][9]; + Channel4op *channels4op[2][3]; + Channel *channels[2][9]; + + // Unique instance to fill future gaps in the Channel array, + // when there will be switches between 2op and 4op mode. + DisabledChannel disabledChannel; + + // Specific operators to switch when in rhythm mode: + HighHatOperator highHatOperator; + SnareDrumOperator snareDrumOperator; + TomTomOperator tomTomOperator; + TomTomTopCymbalChannel tomTomTopCymbalChannel; + + // Rhythm channels + BassDrumChannel bassDrumChannel; + HighHatSnareDrumChannel highHatSnareDrumChannel; + TopCymbalOperator topCymbalOperator; + + Operator *highHatOperatorInNonRhythmMode; + Operator *snareDrumOperatorInNonRhythmMode; + Operator *tomTomOperatorInNonRhythmMode; + Operator *topCymbalOperatorInNonRhythmMode; + + int nts, dam, dvb, ryt, bd, sd, tom, tc, hh, _new, connectionsel; + int vibratoIndex, tremoloIndex; + + bool FullPan; + + static OperatorDataStruct *OperatorData; + static OPL3DataStruct *OPL3Data; + + // The methods read() and write() are the only + // ones needed by the user to interface with the emulator. + // read() returns one frame at a time, to be played at 49700 Hz, + // with each frame being four 16-bit samples, + // corresponding to the OPL3 four output channels CHA...CHD. +public: + //void read(float output[2]); + void write(int array, int address, int data); + + OPL3(bool fullpan); + ~OPL3(); + +private: + void initOperators(); + void initChannels2op(); + void initChannels4op(); + void initRhythmChannels(); + void initChannels(); + void update_1_NTS1_6(); + void update_DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1(); + void update_7_NEW1(); + void setEnabledChannels(); + void updateChannelPans(); + void update_2_CONNECTIONSEL6(); + void set4opConnections(); + void setRhythmMode(); + + static int InstanceCount; + + // OPLEmul interface +public: + void Reset(); + void WriteReg(int reg, int v); + void Update(float *buffer, int length); + void UpdateS(short *sndptr, int numsamples); + void SetPanning(int c, float left, float right); +}; + +OperatorDataStruct *OPL3::OperatorData; +OPL3DataStruct *OPL3::OPL3Data; +int OPL3::InstanceCount; + +void OPL3::Update(float *output, int numsamples) { + while (numsamples--) { + // If _new = 0, use OPL2 mode with 9 channels. If _new = 1, use OPL3 18 channels; + for(int array=0; array < (_new + 1); array++) + for(int channelNumber=0; channelNumber < 9; channelNumber++) { + // Reads output from each OPL3 channel, and accumulates it in the output buffer: + Channel *channel = channels[array][channelNumber]; + if (channel != &disabledChannel) + { + double channelOutput = channel->getChannelOutput(this); + output[0] += float(channelOutput * channel->leftPan); + output[1] += float(channelOutput * channel->rightPan); + } + } + + // Advances the OPL3-wide vibrato index, which is used by + // PhaseGenerator.getPhase() in each Operator. + vibratoIndex = (vibratoIndex + 1) & (OPL3DataStruct::vibratoTableLength - 1); + // Advances the OPL3-wide tremolo index, which is used by + // EnvelopeGenerator.getEnvelope() in each Operator. + tremoloIndex++; + if(tremoloIndex >= OPL3DataStruct::tremoloTableLength) tremoloIndex = 0; + output += 2; + } +} + +void OPL3::UpdateS(short *sndptr, int numsamples) +{ + // DUMMY + (void)sndptr; + (void)numsamples; +} + +void OPL3::write(int array, int address, int data) { + // The OPL3 has two registers arrays, each with adresses ranging + // from 0x00 to 0xF5. + // This emulator uses one array, with the two original register arrays + // starting at 0x00 and at 0x100. + int registerAddress = (array<<8) | address; + // If the address is out of the OPL3 memory map, returns. + if(registerAddress<0 || registerAddress>=0x200) return; + + registers[registerAddress] = data; + switch(address&0xE0) { + // The first 3 bits masking gives the type of the register by using its base address: + // 0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0 + // When it is needed, we further separate the register type inside each base address, + // which is the case of 0x00 and 0xA0. + + // Through out this emulator we will use the same name convention to + // reference a byte with several bit registers. + // The name of each bit register will be followed by the number of bits + // it occupies inside the byte. + // Numbers without accompanying names are unused bits. + case 0x00: + // Unique registers for the entire OPL3: + if(array==1) { + if(address==0x04) + update_2_CONNECTIONSEL6(); + else if(address==0x05) + update_7_NEW1(); + } + else if(address==0x08) update_1_NTS1_6(); + break; + + case 0xA0: + // 0xBD is a control register for the entire OPL3: + if(address==0xBD) { + if(array==0) + update_DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1(); + break; + } + // Registers for each channel are in A0-A8, B0-B8, C0-C8, in both register arrays. + // 0xB0...0xB8 keeps kon,block,fnum(h) for each channel. + if( (address&0xF0) == 0xB0 && address <= 0xB8) { + // If the address is in the second register array, adds 9 to the channel number. + // The channel number is given by the last four bits, like in A0,...,A8. + channels[array][address&0x0F]->update_2_KON1_BLOCK3_FNUMH2(this); + break; + } + // 0xA0...0xA8 keeps fnum(l) for each channel. + if( (address&0xF0) == 0xA0 && address <= 0xA8) + channels[array][address&0x0F]->update_FNUML8(this); + break; + // 0xC0...0xC8 keeps cha,chb,chc,chd,fb,cnt for each channel: + case 0xC0: + if(address <= 0xC8) + channels[array][address&0x0F]->update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(this); + break; + + // Registers for each of the 36 Operators: + default: + int operatorOffset = address&0x1F; + if(operators[array][operatorOffset] == NULL) break; + switch(address&0xE0) { + // 0x20...0x35 keeps am,vib,egt,ksr,mult for each operator: + case 0x20: + operators[array][operatorOffset]->update_AM1_VIB1_EGT1_KSR1_MULT4(this); + break; + // 0x40...0x55 keeps ksl,tl for each operator: + case 0x40: + operators[array][operatorOffset]->update_KSL2_TL6(this); + break; + // 0x60...0x75 keeps ar,dr for each operator: + case 0x60: + operators[array][operatorOffset]->update_AR4_DR4(this); + break; + // 0x80...0x95 keeps sl,rr for each operator: + case 0x80: + operators[array][operatorOffset]->update_SL4_RR4(this); + break; + // 0xE0...0xF5 keeps ws for each operator: + case 0xE0: + operators[array][operatorOffset]->update_5_WS3(this); + } + } +} + +OPL3::OPL3(bool fullpan) +: tomTomTopCymbalChannel(fullpan ? CENTER_PANNING_POWER : 1, &tomTomOperator, &topCymbalOperator), + bassDrumChannel(fullpan ? CENTER_PANNING_POWER : 1), + highHatSnareDrumChannel(fullpan ? CENTER_PANNING_POWER : 1, &highHatOperator, &snareDrumOperator) +{ + FullPan = fullpan; + nts = dam = dvb = ryt = bd = sd = tom = tc = hh = _new = connectionsel = 0; + vibratoIndex = tremoloIndex = 0; + + if (InstanceCount++ == 0) + { + OPL3Data = new struct OPL3DataStruct; + OperatorData = new struct OperatorDataStruct; + } + + initOperators(); + initChannels2op(); + initChannels4op(); + initRhythmChannels(); + initChannels(); +} + +OPL3::~OPL3() +{ + ryt = 0; + setRhythmMode(); // Make sure all operators point to the dynamically allocated ones. + for (int array = 0; array < 2; array++) + { + for (int operatorNumber = 0; operatorNumber < 0x20; operatorNumber++) + { + if (operators[array][operatorNumber] != NULL) + { + delete operators[array][operatorNumber]; + } + } + for (int channelNumber = 0; channelNumber < 9; channelNumber++) + { + delete channels2op[array][channelNumber]; + } + for (int channelNumber = 0; channelNumber < 3; channelNumber++) + { + delete channels4op[array][channelNumber]; + } + } + if (--InstanceCount == 0) + { + delete OPL3Data; + OPL3Data = NULL; + delete OperatorData; + OperatorData = NULL; + } +} + + +void OPL3::initOperators() { + int baseAddress; + // The YMF262 has 36 operators: + memset(operators, 0, sizeof(operators)); + for(int array=0; array<2; array++) + for(int group = 0; group<=0x10; group+=8) + for(int offset=0; offset<6; offset++) { + baseAddress = (array<<8) | (group+offset); + operators[array][group+offset] = new Operator(baseAddress); + } + + // Save operators when they are in non-rhythm mode: + // Channel 7: + highHatOperatorInNonRhythmMode = operators[0][0x11]; + snareDrumOperatorInNonRhythmMode = operators[0][0x14]; + // Channel 8: + tomTomOperatorInNonRhythmMode = operators[0][0x12]; + topCymbalOperatorInNonRhythmMode = operators[0][0x15]; + +} + +void OPL3::initChannels2op() { + // The YMF262 has 18 2-op channels. + // Each 2-op channel can be at a serial or parallel operator configuration: + memset(channels2op, 0, sizeof(channels2op)); + double startvol = FullPan ? CENTER_PANNING_POWER : 1; + for(int array=0; array<2; array++) + for(int channelNumber=0; channelNumber<3; channelNumber++) { + int baseAddress = (array<<8) | channelNumber; + // Channels 1, 2, 3 -> Operator offsets 0x0,0x3; 0x1,0x4; 0x2,0x5 + channels2op[array][channelNumber] = new Channel2op(baseAddress, startvol, operators[array][channelNumber], operators[array][channelNumber+0x3]); + // Channels 4, 5, 6 -> Operator offsets 0x8,0xB; 0x9,0xC; 0xA,0xD + channels2op[array][channelNumber+3] = new Channel2op(baseAddress+3, startvol, operators[array][channelNumber+0x8], operators[array][channelNumber+0xB]); + // Channels 7, 8, 9 -> Operators 0x10,0x13; 0x11,0x14; 0x12,0x15 + channels2op[array][channelNumber+6] = new Channel2op(baseAddress+6, startvol, operators[array][channelNumber+0x10], operators[array][channelNumber+0x13]); + } +} + +void OPL3::initChannels4op() { + // The YMF262 has 3 4-op channels in each array: + memset(channels4op, 0, sizeof(channels4op)); + double startvol = FullPan ? CENTER_PANNING_POWER : 1; + for(int array=0; array<2; array++) + for(int channelNumber=0; channelNumber<3; channelNumber++) { + int baseAddress = (array<<8) | channelNumber; + // Channels 1, 2, 3 -> Operators 0x0,0x3,0x8,0xB; 0x1,0x4,0x9,0xC; 0x2,0x5,0xA,0xD; + channels4op[array][channelNumber] = new Channel4op(baseAddress, startvol, operators[array][channelNumber], operators[array][channelNumber+0x3], operators[array][channelNumber+0x8], operators[array][channelNumber+0xB]); + } +} + +void OPL3::initRhythmChannels() { +} + +void OPL3::initChannels() { + // Channel is an abstract class that can be a 2-op, 4-op, rhythm or disabled channel, + // depending on the OPL3 configuration at the time. + // channels[] inits as a 2-op serial channel array: + for(int array=0; array<2; array++) + for(int i=0; i<9; i++) channels[array][i] = channels2op[array][i]; +} + +void OPL3::update_1_NTS1_6() { + int _1_nts1_6 = registers[OPL3DataStruct::_1_NTS1_6_Offset]; + // Note Selection. This register is used in Channel.updateOperators() implementations, + // to calculate the channel´s Key Scale Number. + // The value of the actual envelope rate follows the value of + // OPL3.nts,Operator.keyScaleNumber and Operator.ksr + nts = (_1_nts1_6 & 0x40) >> 6; +} + +void OPL3::update_DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1() { + int dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 = registers[OPL3DataStruct::DAM1_DVB1_RYT1_BD1_SD1_TOM1_TC1_HH1_Offset]; + // Depth of amplitude. This register is used in EnvelopeGenerator.getEnvelope(); + dam = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x80) >> 7; + + // Depth of vibrato. This register is used in PhaseGenerator.getPhase(); + dvb = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x40) >> 6; + + int new_ryt = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x20) >> 5; + if(new_ryt != ryt) { + ryt = new_ryt; + setRhythmMode(); + } + + int new_bd = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x10) >> 4; + if(new_bd != bd) { + bd = new_bd; + if(bd==1) { + bassDrumChannel.op1->keyOn(); + bassDrumChannel.op2->keyOn(); + } + } + + int new_sd = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x08) >> 3; + if(new_sd != sd) { + sd = new_sd; + if(sd==1) snareDrumOperator.keyOn(); + } + + int new_tom = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x04) >> 2; + if(new_tom != tom) { + tom = new_tom; + if(tom==1) tomTomOperator.keyOn(); + } + + int new_tc = (dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x02) >> 1; + if(new_tc != tc) { + tc = new_tc; + if(tc==1) topCymbalOperator.keyOn(); + } + + int new_hh = dam1_dvb1_ryt1_bd1_sd1_tom1_tc1_hh1 & 0x01; + if(new_hh != hh) { + hh = new_hh; + if(hh==1) highHatOperator.keyOn(); + } + +} + +void OPL3::update_7_NEW1() { + int _7_new1 = registers[OPL3DataStruct::_7_NEW1_Offset]; + // OPL2/OPL3 mode selection. This register is used in + // OPL3.read(), OPL3.write() and Operator.getOperatorOutput(); + _new = (_7_new1 & 0x01); + if(_new==1) setEnabledChannels(); + set4opConnections(); + updateChannelPans(); +} + +void OPL3::setEnabledChannels() { + for(int array=0; array<2; array++) + for(int i=0; i<9; i++) { + int baseAddress = channels[array][i]->channelBaseAddress; + registers[baseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset] |= 0xF0; + channels[array][i]->update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(this); + } +} + +void OPL3::updateChannelPans() { + for(int array=0; array<2; array++) + for(int i=0; i<9; i++) { + int baseAddress = channels[array][i]->channelBaseAddress; + registers[baseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset] |= 0xF0; + channels[array][i]->updatePan(this); + } + +} + +void OPL3::update_2_CONNECTIONSEL6() { + // This method is called only if _new is set. + int _2_connectionsel6 = registers[OPL3DataStruct::_2_CONNECTIONSEL6_Offset]; + // 2-op/4-op channel selection. This register is used here to configure the OPL3.channels[] array. + connectionsel = (_2_connectionsel6 & 0x3F); + set4opConnections(); +} + +void OPL3::set4opConnections() { + // bits 0, 1, 2 sets respectively 2-op channels (1,4), (2,5), (3,6) to 4-op operation. + // bits 3, 4, 5 sets respectively 2-op channels (10,13), (11,14), (12,15) to 4-op operation. + for(int array=0; array<2; array++) + for(int i=0; i<3; i++) { + if(_new == 1) { + int shift = array*3 + i; + int connectionBit = (connectionsel >> shift) & 0x01; + if(connectionBit == 1) { + channels[array][i] = channels4op[array][i]; + channels[array][i+3] = &disabledChannel; + channels[array][i]->updateChannel(this); + continue; + } + } + channels[array][i] = channels2op[array][i]; + channels[array][i+3] = channels2op[array][i+3]; + channels[array][i]->updateChannel(this); + channels[array][i+3]->updateChannel(this); + } +} + +void OPL3::setRhythmMode() { + if(ryt==1) { + channels[0][6] = &bassDrumChannel; + channels[0][7] = &highHatSnareDrumChannel; + channels[0][8] = &tomTomTopCymbalChannel; + operators[0][0x11] = &highHatOperator; + operators[0][0x14] = &snareDrumOperator; + operators[0][0x12] = &tomTomOperator; + operators[0][0x15] = &topCymbalOperator; + } + else { + for(int i=6; i<=8; i++) channels[0][i] = channels2op[0][i]; + operators[0][0x11] = highHatOperatorInNonRhythmMode; + operators[0][0x14] = snareDrumOperatorInNonRhythmMode; + operators[0][0x12] = tomTomOperatorInNonRhythmMode; + operators[0][0x15] = topCymbalOperatorInNonRhythmMode; + } + for(int i=6; i<=8; i++) channels[0][i]->updateChannel(this); +} + +static double EnvelopeFromDB(double db) +{ +#if 0 + return pow(10.0, db/10); +#else + if (db < MIN_DB) + return 0; + return OPL3::OperatorData->dbpow[xs_FloorToInt(-db * DB_TABLE_RES)]; +#endif +} + +Channel::Channel (int baseAddress, double startvol) { + channelBaseAddress = baseAddress; + fnuml = fnumh = kon = block = fb = cnt = 0; + feedback[0] = feedback[1] = 0; + leftPan = rightPan = startvol; +} + +void Channel::update_2_KON1_BLOCK3_FNUMH2(OPL3 *OPL3) { + + int _2_kon1_block3_fnumh2 = OPL3->registers[channelBaseAddress+ChannelData::_2_KON1_BLOCK3_FNUMH2_Offset]; + + // Frequency Number (hi-register) and Block. These two registers, together with fnuml, + // sets the Channel´s base frequency; + block = (_2_kon1_block3_fnumh2 & 0x1C) >> 2; + fnumh = _2_kon1_block3_fnumh2 & 0x03; + updateOperators(OPL3); + + // Key On. If changed, calls Channel.keyOn() / keyOff(). + int newKon = (_2_kon1_block3_fnumh2 & 0x20) >> 5; + if(newKon != kon) { + if(newKon == 1) keyOn(); + else keyOff(); + kon = newKon; + } +} + +void Channel::update_FNUML8(OPL3 *OPL3) { + int fnuml8 = OPL3->registers[channelBaseAddress+ChannelData::FNUML8_Offset]; + // Frequency Number, low register. + fnuml = fnuml8&0xFF; + updateOperators(OPL3); +} + +void Channel::update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(OPL3 *OPL3) { + int chd1_chc1_chb1_cha1_fb3_cnt1 = OPL3->registers[channelBaseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset]; +// chd = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x80) >> 7; +// chc = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x40) >> 6; + chb = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x20) >> 5; + cha = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x10) >> 4; + fb = (chd1_chc1_chb1_cha1_fb3_cnt1 & 0x0E) >> 1; + cnt = chd1_chc1_chb1_cha1_fb3_cnt1 & 0x01; + updatePan(OPL3); + updateOperators(OPL3); +} + +void Channel::updatePan(OPL3 *OPL3) { + if (!OPL3->FullPan) + { + if (OPL3->_new == 0) + { + leftPan = VOLUME_MUL; + rightPan = VOLUME_MUL; + } + else + { + leftPan = cha * VOLUME_MUL; + rightPan = chb * VOLUME_MUL; + } + } +} + +void Channel::updateChannel(OPL3 *OPL3) { + update_2_KON1_BLOCK3_FNUMH2(OPL3); + update_FNUML8(OPL3); + update_CHD1_CHC1_CHB1_CHA1_FB3_CNT1(OPL3); +} + +Channel2op::Channel2op (int baseAddress, double startvol, Operator *o1, Operator *o2) +: Channel(baseAddress, startvol) +{ + op1 = o1; + op2 = o2; +} + +double Channel2op::getChannelOutput(OPL3 *OPL3) { + double channelOutput = 0, op1Output = 0, op2Output = 0; + // The feedback uses the last two outputs from + // the first operator, instead of just the last one. + double feedbackOutput = (feedback[0] + feedback[1]) / 2; + + switch(cnt) { + // CNT = 0, the operators are in series, with the first in feedback. + case 0: + if(op2->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + channelOutput = op2->getOperatorOutput(OPL3, op1Output*toPhase); + break; + // CNT = 1, the operators are in parallel, with the first in feedback. + case 1: + if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF && + op2->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator); + channelOutput = (op1Output + op2Output) / 2; + } + + feedback[0] = feedback[1]; + feedback[1] = StripIntPart(op1Output * ChannelData::feedback[fb]); + return channelOutput; +} + +void Channel2op::keyOn() { + op1->keyOn(); + op2->keyOn(); + feedback[0] = feedback[1] = 0; +} + +void Channel2op::keyOff() { + op1->keyOff(); + op2->keyOff(); +} + +void Channel2op::updateOperators(OPL3 *OPL3) { + // Key Scale Number, used in EnvelopeGenerator.setActualRates(). + int keyScaleNumber = block*2 + ((fnumh>>OPL3->nts)&0x01); + int f_number = (fnumh<<8) | fnuml; + op1->updateOperator(OPL3, keyScaleNumber, f_number, block); + op2->updateOperator(OPL3, keyScaleNumber, f_number, block); +} + +Channel4op::Channel4op (int baseAddress, double startvol, Operator *o1, Operator *o2, Operator *o3, Operator *o4) +: Channel(baseAddress, startvol) +{ + op1 = o1; + op2 = o2; + op3 = o3; + op4 = o4; +} + +double Channel4op::getChannelOutput(OPL3 *OPL3) { + double channelOutput = 0, + op1Output = 0, op2Output = 0, op3Output = 0, op4Output = 0; + + int secondChannelBaseAddress = channelBaseAddress+3; + int secondCnt = OPL3->registers[secondChannelBaseAddress+ChannelData::CHD1_CHC1_CHB1_CHA1_FB3_CNT1_Offset] & 0x1; + int cnt4op = (cnt << 1) | secondCnt; + + double feedbackOutput = (feedback[0] + feedback[1]) / 2; + + switch(cnt4op) { + case 0: + if(op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + op2Output = op2->getOperatorOutput(OPL3, op1Output*toPhase); + op3Output = op3->getOperatorOutput(OPL3, op2Output*toPhase); + channelOutput = op4->getOperatorOutput(OPL3, op3Output*toPhase); + + break; + case 1: + if(op2->envelopeGenerator.stage==EnvelopeGenerator::OFF && + op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + op2Output = op2->getOperatorOutput(OPL3, op1Output*toPhase); + + op3Output = op3->getOperatorOutput(OPL3, Operator::noModulator); + op4Output = op4->getOperatorOutput(OPL3, op3Output*toPhase); + + channelOutput = (op2Output + op4Output) / 2; + break; + case 2: + if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF && + op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + + op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator); + op3Output = op3->getOperatorOutput(OPL3, op2Output*toPhase); + op4Output = op4->getOperatorOutput(OPL3, op3Output*toPhase); + + channelOutput = (op1Output + op4Output) / 2; + break; + case 3: + if(op1->envelopeGenerator.stage==EnvelopeGenerator::OFF && + op3->envelopeGenerator.stage==EnvelopeGenerator::OFF && + op4->envelopeGenerator.stage==EnvelopeGenerator::OFF) + return 0; + + op1Output = op1->getOperatorOutput(OPL3, feedbackOutput); + + op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator); + op3Output = op3->getOperatorOutput(OPL3, op2Output*toPhase); + + op4Output = op4->getOperatorOutput(OPL3, Operator::noModulator); + + channelOutput = (op1Output + op3Output + op4Output) / 3; + } + + feedback[0] = feedback[1]; + feedback[1] = StripIntPart(op1Output * ChannelData::feedback[fb]); + + return channelOutput; +} + +void Channel4op::keyOn() { + op1->keyOn(); + op2->keyOn(); + op3->keyOn(); + op4->keyOn(); + feedback[0] = feedback[1] = 0; +} + +void Channel4op::keyOff() { + op1->keyOff(); + op2->keyOff(); + op3->keyOff(); + op4->keyOff(); +} + +void Channel4op::updateOperators(OPL3 *OPL3) { + // Key Scale Number, used in EnvelopeGenerator.setActualRates(). + int keyScaleNumber = block*2 + ((fnumh>>OPL3->nts)&0x01); + int f_number = (fnumh<<8) | fnuml; + op1->updateOperator(OPL3, keyScaleNumber, f_number, block); + op2->updateOperator(OPL3, keyScaleNumber, f_number, block); + op3->updateOperator(OPL3, keyScaleNumber, f_number, block); + op4->updateOperator(OPL3, keyScaleNumber, f_number, block); +} + +const double Operator::noModulator = 0; + +Operator::Operator(int baseAddress) { + operatorBaseAddress = baseAddress; + + envelope = 0; + am = vib = ksr = egt = mult = ksl = tl = ar = dr = sl = rr = ws = 0; + keyScaleNumber = f_number = block = 0; +} + +void Operator::update_AM1_VIB1_EGT1_KSR1_MULT4(OPL3 *OPL3) { + + int am1_vib1_egt1_ksr1_mult4 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::AM1_VIB1_EGT1_KSR1_MULT4_Offset]; + + // Amplitude Modulation. This register is used int EnvelopeGenerator.getEnvelope(); + am = (am1_vib1_egt1_ksr1_mult4 & 0x80) >> 7; + // Vibrato. This register is used in PhaseGenerator.getPhase(); + vib = (am1_vib1_egt1_ksr1_mult4 & 0x40) >> 6; + // Envelope Generator Type. This register is used in EnvelopeGenerator.getEnvelope(); + egt = (am1_vib1_egt1_ksr1_mult4 & 0x20) >> 5; + // Key Scale Rate. Sets the actual envelope rate together with rate and keyScaleNumber. + // This register os used in EnvelopeGenerator.setActualAttackRate(). + ksr = (am1_vib1_egt1_ksr1_mult4 & 0x10) >> 4; + // Multiple. Multiplies the Channel.baseFrequency to get the Operator.operatorFrequency. + // This register is used in PhaseGenerator.setFrequency(). + mult = am1_vib1_egt1_ksr1_mult4 & 0x0F; + + phaseGenerator.setFrequency(f_number, block, mult); + envelopeGenerator.setActualAttackRate(ar, ksr, keyScaleNumber); + envelopeGenerator.setActualDecayRate(dr, ksr, keyScaleNumber); + envelopeGenerator.setActualReleaseRate(rr, ksr, keyScaleNumber); +} + +void Operator::update_KSL2_TL6(OPL3 *OPL3) { + + int ksl2_tl6 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::KSL2_TL6_Offset]; + + // Key Scale Level. Sets the attenuation in accordance with the octave. + ksl = (ksl2_tl6 & 0xC0) >> 6; + // Total Level. Sets the overall damping for the envelope. + tl = ksl2_tl6 & 0x3F; + + envelopeGenerator.setAtennuation(f_number, block, ksl); + envelopeGenerator.setTotalLevel(tl); +} + +void Operator::update_AR4_DR4(OPL3 *OPL3) { + + int ar4_dr4 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::AR4_DR4_Offset]; + + // Attack Rate. + ar = (ar4_dr4 & 0xF0) >> 4; + // Decay Rate. + dr = ar4_dr4 & 0x0F; + + envelopeGenerator.setActualAttackRate(ar, ksr, keyScaleNumber); + envelopeGenerator.setActualDecayRate(dr, ksr, keyScaleNumber); +} + +void Operator::update_SL4_RR4(OPL3 *OPL3) { + + int sl4_rr4 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::SL4_RR4_Offset]; + + // Sustain Level. + sl = (sl4_rr4 & 0xF0) >> 4; + // Release Rate. + rr = sl4_rr4 & 0x0F; + + envelopeGenerator.setActualSustainLevel(sl); + envelopeGenerator.setActualReleaseRate(rr, ksr, keyScaleNumber); +} + +void Operator::update_5_WS3(OPL3 *OPL3) { + int _5_ws3 = OPL3->registers[operatorBaseAddress+OperatorDataStruct::_5_WS3_Offset]; + ws = _5_ws3 & 0x07; +} + +double Operator::getOperatorOutput(OPL3 *OPL3, double modulator) { + if(envelopeGenerator.stage == EnvelopeGenerator::OFF) return 0; + + double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am); + envelope = EnvelopeFromDB(envelopeInDB); + + // If it is in OPL2 mode, use first four waveforms only: + ws &= ((OPL3->_new<<2) + 3); + double *waveform = OPL3::OperatorData->waveforms[ws]; + + phase = phaseGenerator.getPhase(OPL3, vib); + + double operatorOutput = getOutput(modulator, phase, waveform); + return operatorOutput; +} + +double Operator::getOutput(double modulator, double outputPhase, double *waveform) { + int sampleIndex = xs_FloorToInt((outputPhase + modulator) * OperatorDataStruct::waveLength) & (OperatorDataStruct::waveLength - 1); + return waveform[sampleIndex] * envelope; +} + +void Operator::keyOn() { + if(ar > 0) { + envelopeGenerator.keyOn(); + phaseGenerator.keyOn(); + } + else envelopeGenerator.stage = EnvelopeGenerator::OFF; +} + +void Operator::keyOff() { + envelopeGenerator.keyOff(); +} + +void Operator::updateOperator(OPL3 *OPL3, int ksn, int f_num, int blk) { + keyScaleNumber = ksn; + f_number = f_num; + block = blk; + update_AM1_VIB1_EGT1_KSR1_MULT4(OPL3); + update_KSL2_TL6(OPL3); + update_AR4_DR4(OPL3); + update_SL4_RR4(OPL3); + update_5_WS3(OPL3); +} + +EnvelopeGenerator::EnvelopeGenerator() { + stage = OFF; + actualAttackRate = actualDecayRate = actualReleaseRate = 0; + xAttackIncrement = xMinimumInAttack = 0; + dBdecayIncrement = 0; + dBreleaseIncrement = 0; + attenuation = totalLevel = sustainLevel = 0; + x = dBtoX(-96); + envelope = -96; +} + +void EnvelopeGenerator::setActualSustainLevel(int sl) { + // If all SL bits are 1, sustain level is set to -93 dB: + if(sl == 0x0F) { + sustainLevel = -93; + return; + } + // The datasheet states that the SL formula is + // sustainLevel = -24*d7 -12*d6 -6*d5 -3*d4, + // translated as: + sustainLevel = -3*sl; +} + +void EnvelopeGenerator::setTotalLevel(int tl) { + // The datasheet states that the TL formula is + // TL = -(24*d5 + 12*d4 + 6*d3 + 3*d2 + 1.5*d1 + 0.75*d0), + // translated as: + totalLevel = tl*-0.75; +} + +void EnvelopeGenerator::setAtennuation(int f_number, int block, int ksl) { + int hi4bits = (f_number>>6)&0x0F; + switch(ksl) { + case 0: + attenuation = 0; + break; + case 1: + // ~3 dB/Octave + attenuation = OperatorDataStruct::ksl3dBtable[hi4bits][block]; + break; + case 2: + // ~1.5 dB/Octave + attenuation = OperatorDataStruct::ksl3dBtable[hi4bits][block]/2; + break; + case 3: + // ~6 dB/Octave + attenuation = OperatorDataStruct::ksl3dBtable[hi4bits][block]*2; + } +} + +void EnvelopeGenerator::setActualAttackRate(int attackRate, int ksr, int keyScaleNumber) { + // According to the YMF278B manual's OPL3 section, the attack curve is exponential, + // with a dynamic range from -96 dB to 0 dB and a resolution of 0.1875 dB + // per level. + // + // This method sets an attack increment and attack minimum value + // that creates a exponential dB curve with 'period0to100' seconds in length + // and 'period10to90' seconds between 10% and 90% of the curve total level. + actualAttackRate = calculateActualRate(attackRate, ksr, keyScaleNumber); + double period0to100inSeconds = EnvelopeGeneratorData::attackTimeValuesTable[actualAttackRate][0]/1000.0; + int period0to100inSamples = (int)(period0to100inSeconds*OPL_SAMPLE_RATE); + double period10to90inSeconds = EnvelopeGeneratorData::attackTimeValuesTable[actualAttackRate][1]/1000.0; + int period10to90inSamples = (int)(period10to90inSeconds*OPL_SAMPLE_RATE); + // The x increment is dictated by the period between 10% and 90%: + xAttackIncrement = OPL3DataStruct::calculateIncrement(percentageToX(0.1), percentageToX(0.9), period10to90inSeconds); + // Discover how many samples are still from the top. + // It cannot reach 0 dB, since x is a logarithmic parameter and would be + // negative infinity. So we will use -0.1875 dB as the resolution + // maximum. + // + // percentageToX(0.9) + samplesToTheTop*xAttackIncrement = dBToX(-0.1875); -> + // samplesToTheTop = (dBtoX(-0.1875) - percentageToX(0.9)) / xAttackIncrement); -> + // period10to100InSamples = period10to90InSamples + samplesToTheTop; -> + int period10to100inSamples = (int) (period10to90inSamples + (dBtoX(-0.1875) - percentageToX(0.9)) / xAttackIncrement); + // Discover the minimum x that, through the attackIncrement value, keeps + // the 10%-90% period, and reaches 0 dB at the total period: + xMinimumInAttack = percentageToX(0.1) - (period0to100inSamples-period10to100inSamples)*xAttackIncrement; +} + + +void EnvelopeGenerator::setActualDecayRate(int decayRate, int ksr, int keyScaleNumber) { + actualDecayRate = calculateActualRate(decayRate, ksr, keyScaleNumber); + double period10to90inSeconds = EnvelopeGeneratorData::decayAndReleaseTimeValuesTable[actualDecayRate][1]/1000.0; + // Differently from the attack curve, the decay/release curve is linear. + // The dB increment is dictated by the period between 10% and 90%: + dBdecayIncrement = OPL3DataStruct::calculateIncrement(percentageToDB(0.1), percentageToDB(0.9), period10to90inSeconds); +} + +void EnvelopeGenerator::setActualReleaseRate(int releaseRate, int ksr, int keyScaleNumber) { + actualReleaseRate = calculateActualRate(releaseRate, ksr, keyScaleNumber); + double period10to90inSeconds = EnvelopeGeneratorData::decayAndReleaseTimeValuesTable[actualReleaseRate][1]/1000.0; + dBreleaseIncrement = OPL3DataStruct::calculateIncrement(percentageToDB(0.1), percentageToDB(0.9), period10to90inSeconds); +} + +int EnvelopeGenerator::calculateActualRate(int rate, int ksr, int keyScaleNumber) { + int rof = EnvelopeGeneratorData::rateOffset[ksr][keyScaleNumber]; + int actualRate = rate*4 + rof; + // If, as an example at the maximum, rate is 15 and the rate offset is 15, + // the value would + // be 75, but the maximum allowed is 63: + if(actualRate > 63) actualRate = 63; + return actualRate; +} + +double EnvelopeGenerator::getEnvelope(OPL3 *OPL3, int egt, int am) { + // The datasheets attenuation values + // must be halved to match the real OPL3 output. + double envelopeSustainLevel = sustainLevel / 2; + double envelopeTremolo = + OPL3::OPL3Data->tremoloTable[OPL3->dam][OPL3->tremoloIndex] / 2; + double envelopeAttenuation = attenuation / 2; + double envelopeTotalLevel = totalLevel / 2; + + double envelopeMinimum = -96; + double envelopeResolution = 0.1875; + + double outputEnvelope; + // + // Envelope Generation + // + switch(stage) { + case ATTACK: + // Since the attack is exponential, it will never reach 0 dB, so + // we´ll work with the next to maximum in the envelope resolution. + if(envelope<-envelopeResolution && xAttackIncrement != -EnvelopeGeneratorData::MUGEN) { + // The attack is exponential. +#if 0 + envelope = -pow(2.0,x); +#else + int index = xs_FloorToInt((x - ATTACK_MIN) / ATTACK_RES); + if (index < 0) + envelope = OPL3::OperatorData->attackTable[0]; + else if (index >= ATTACK_TABLE_SIZE) + envelope = OPL3::OperatorData->attackTable[ATTACK_TABLE_SIZE-1]; + else + envelope = OPL3::OperatorData->attackTable[index]; +#endif + x += xAttackIncrement; + break; + } + else { + // It is needed here to explicitly set envelope = 0, since + // only the attack can have a period of + // 0 seconds and produce an infinity envelope increment. + envelope = 0; + stage = DECAY; + } + case DECAY: + // The decay and release are linear. + if(envelope>envelopeSustainLevel) { + envelope -= dBdecayIncrement; + break; + } + else + stage = SUSTAIN; + case SUSTAIN: + // The Sustain stage is mantained all the time of the Key ON, + // even if we are in non-sustaining mode. + // This is necessary because, if the key is still pressed, we can + // change back and forth the state of EGT, and it will release and + // hold again accordingly. + if(egt==1) break; + else { + if(envelope > envelopeMinimum) + envelope -= dBreleaseIncrement; + else stage = OFF; + } + break; + case RELEASE: + // If we have Key OFF, only here we are in the Release stage. + // Now, we can turn EGT back and forth and it will have no effect,i.e., + // it will release inexorably to the Off stage. + if(envelope > envelopeMinimum) + envelope -= dBreleaseIncrement; + else stage = OFF; + case OFF: + break; + } + + // Ongoing original envelope + outputEnvelope = envelope; + + //Tremolo + if(am == 1) outputEnvelope += envelopeTremolo; + + //Attenuation + outputEnvelope += envelopeAttenuation; + + //Total Level + outputEnvelope += envelopeTotalLevel; + + return outputEnvelope; +} + +void EnvelopeGenerator::keyOn() { + // If we are taking it in the middle of a previous envelope, + // start to rise from the current level: + // envelope = - (2 ^ x); -> + // 2 ^ x = -envelope -> + // x = log2(-envelope); -> + double xCurrent = OperatorDataStruct::log2(-envelope); + x = xCurrent < xMinimumInAttack ? xCurrent : xMinimumInAttack; + stage = ATTACK; +} + +void EnvelopeGenerator::keyOff() { + if(stage != OFF) stage = RELEASE; +} + +double EnvelopeGenerator::dBtoX(double dB) { + return OperatorDataStruct::log2(-dB); +} + +double EnvelopeGenerator::percentageToDB(double percentage) { + return log10(percentage) * 10.0; +} + +double EnvelopeGenerator::percentageToX(double percentage) { + return dBtoX(percentageToDB(percentage)); +} + +PhaseGenerator::PhaseGenerator() { + phase = phaseIncrement = 0; +} + +void PhaseGenerator::setFrequency(int f_number, int block, int mult) { + // This frequency formula is derived from the following equation: + // f_number = baseFrequency * pow(2,19) / OPL_SAMPLE_RATE / pow(2,block-1); + double baseFrequency = + f_number * pow(2.0, block-1) * OPL_SAMPLE_RATE / pow(2.0,19); + double operatorFrequency = baseFrequency*OperatorDataStruct::multTable[mult]; + + // phase goes from 0 to 1 at + // period = (1/frequency) seconds -> + // Samples in each period is (1/frequency)*OPL_SAMPLE_RATE = + // = OPL_SAMPLE_RATE/frequency -> + // So the increment in each sample, to go from 0 to 1, is: + // increment = (1-0) / samples in the period -> + // increment = 1 / (OPL_SAMPLE_RATE/operatorFrequency) -> + phaseIncrement = operatorFrequency/OPL_SAMPLE_RATE; +} + +double PhaseGenerator::getPhase(OPL3 *OPL3, int vib) { + if(vib==1) + // phaseIncrement = (operatorFrequency * vibrato) / OPL_SAMPLE_RATE + phase += phaseIncrement*OPL3::OPL3Data->vibratoTable[OPL3->dvb][OPL3->vibratoIndex]; + else + // phaseIncrement = operatorFrequency / OPL_SAMPLE_RATE + phase += phaseIncrement; + // Originally clamped phase to [0,1), but that's not needed + return phase; +} + +void PhaseGenerator::keyOn() { + phase = 0; +} + +double RhythmChannel::getChannelOutput(OPL3 *OPL3) { + double channelOutput = 0, op1Output = 0, op2Output = 0; + + // Note that, different from the common channel, + // we do not check to see if the Operator's envelopes are Off. + // Instead, we always do the calculations, + // to update the publicly available phase. + op1Output = op1->getOperatorOutput(OPL3, Operator::noModulator); + op2Output = op2->getOperatorOutput(OPL3, Operator::noModulator); + channelOutput = (op1Output + op2Output) / 2; + + return channelOutput; +}; + +TopCymbalOperator::TopCymbalOperator(int baseAddress) +: Operator(baseAddress) +{ } + +TopCymbalOperator::TopCymbalOperator() +: Operator(topCymbalOperatorBaseAddress) +{ } + +double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator) { + double highHatOperatorPhase = + OPL3->highHatOperator.phase * OperatorDataStruct::multTable[OPL3->highHatOperator.mult]; + // The Top Cymbal operator uses its own phase together with the High Hat phase. + return getOperatorOutput(OPL3, modulator, highHatOperatorPhase); +} + +// This method is used here with the HighHatOperator phase +// as the externalPhase. +// Conversely, this method is also used through inheritance by the HighHatOperator, +// now with the TopCymbalOperator phase as the externalPhase. +double TopCymbalOperator::getOperatorOutput(OPL3 *OPL3, double modulator, double externalPhase) { + double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am); + envelope = EnvelopeFromDB(envelopeInDB); + + phase = phaseGenerator.getPhase(OPL3, vib); + + int waveIndex = ws & ((OPL3->_new<<2) + 3); + double *waveform = OPL3::OperatorData->waveforms[waveIndex]; + + // Empirically tested multiplied phase for the Top Cymbal: + double carrierPhase = 8 * phase; + double modulatorPhase = externalPhase; + double modulatorOutput = getOutput(Operator::noModulator, modulatorPhase, waveform); + double carrierOutput = getOutput(modulatorOutput, carrierPhase, waveform); + + int cycles = 4; + double chopped = (carrierPhase * cycles) /* %cycles */; + chopped = chopped - floor(chopped / cycles) * cycles; + if( chopped > 0.1) carrierOutput = 0; + + return carrierOutput*2; +} + +HighHatOperator::HighHatOperator() +: TopCymbalOperator(highHatOperatorBaseAddress) +{ } + +double HighHatOperator::getOperatorOutput(OPL3 *OPL3, double modulator) { + double topCymbalOperatorPhase = + OPL3->topCymbalOperator.phase * OperatorDataStruct::multTable[OPL3->topCymbalOperator.mult]; + // The sound output from the High Hat resembles the one from + // Top Cymbal, so we use the parent method and modify its output + // accordingly afterwards. + double operatorOutput = TopCymbalOperator::getOperatorOutput(OPL3, modulator, topCymbalOperatorPhase); + double randval = rand() / (double)RAND_MAX; + if(operatorOutput == 0) operatorOutput = randval*envelope; + return operatorOutput; +} + +SnareDrumOperator::SnareDrumOperator() +: Operator(snareDrumOperatorBaseAddress) +{ } + +double SnareDrumOperator::getOperatorOutput(OPL3 *OPL3, double modulator) { + if(envelopeGenerator.stage == EnvelopeGenerator::OFF) return 0; + + double envelopeInDB = envelopeGenerator.getEnvelope(OPL3, egt, am); + envelope = EnvelopeFromDB(envelopeInDB); + + // If it is in OPL2 mode, use first four waveforms only: + int waveIndex = ws & ((OPL3->_new<<2) + 3); + double *waveform = OPL3::OperatorData->waveforms[waveIndex]; + + phase = OPL3->highHatOperator.phase * 2; + + double operatorOutput = getOutput(modulator, phase, waveform); + + double randval = rand() / (double)RAND_MAX; + double noise = randval * envelope; + + if(operatorOutput/envelope != 1 && operatorOutput/envelope != -1) { + if(operatorOutput > 0) operatorOutput = noise; + else if(operatorOutput < 0) operatorOutput = -noise; + else operatorOutput = 0; + } + + return operatorOutput*2; +} + +BassDrumChannel::BassDrumChannel(double startvol) +: Channel2op(bassDrumChannelBaseAddress, startvol, &my_op1, &my_op2), + my_op1(op1BaseAddress), my_op2(op2BaseAddress) +{ } + +double BassDrumChannel::getChannelOutput(OPL3 *OPL3) { + // Bass Drum ignores first operator, when it is in series. + if(cnt == 1) op1->ar=0; + return Channel2op::getChannelOutput(OPL3); +} + +void OPL3DataStruct::loadVibratoTable() { + + // According to the YMF262 datasheet, the OPL3 vibrato repetition rate is 6.1 Hz. + // According to the YMF278B manual, it is 6.0 Hz. + // The information that the vibrato table has 8 levels standing 1024 samples each + // was taken from the emulator by Jarek Burczynski and Tatsuyuki Satoh, + // with a frequency of 6,06689453125 Hz, what makes sense with the difference + // in the information on the datasheets. + + const double semitone = pow(2.0,1/12.0); + // A cent is 1/100 of a semitone: + const double cent = pow(semitone, 1/100.0); + + // When dvb=0, the depth is 7 cents, when it is 1, the depth is 14 cents. + const double DVB0 = pow(cent,7.0); + const double DVB1 = pow(cent,14.0); + int i; + for(i = 0; i<1024; i++) + vibratoTable[0][i] = vibratoTable[1][i] = 1; + for(;i<2048; i++) { + vibratoTable[0][i] = sqrt(DVB0); + vibratoTable[1][i] = sqrt(DVB1); + } + for(;i<3072; i++) { + vibratoTable[0][i] = DVB0; + vibratoTable[1][i] = DVB1; + } + for(;i<4096; i++) { + vibratoTable[0][i] = sqrt(DVB0); + vibratoTable[1][i] = sqrt(DVB1); + } + for(; i<5120; i++) + vibratoTable[0][i] = vibratoTable[1][i] = 1; + for(;i<6144; i++) { + vibratoTable[0][i] = 1/sqrt(DVB0); + vibratoTable[1][i] = 1/sqrt(DVB1); + } + for(;i<7168; i++) { + vibratoTable[0][i] = 1/DVB0; + vibratoTable[1][i] = 1/DVB1; + } + for(;i<8192; i++) { + vibratoTable[0][i] = 1/sqrt(DVB0); + vibratoTable[1][i] = 1/sqrt(DVB1); + } + +} + +void OPL3DataStruct::loadTremoloTable() +{ + // The tremolo depth is -1 dB when DAM = 0, and -4.8 dB when DAM = 1. + static const double tremoloDepth[] = {-1, -4.8}; + + // According to the YMF278B manual's OPL3 section graph, + // the tremolo waveform is not + // \ / a sine wave, but a single triangle waveform. + // \ / Thus, the period to achieve the tremolo depth is T/2, and + // \ / the increment in each T/2 section uses a frequency of 2*f. + // \/ Tremolo varies from 0 dB to depth, to 0 dB again, at frequency*2: + const double tremoloIncrement[] = { + calculateIncrement(tremoloDepth[0],0,1/(2*tremoloFrequency)), + calculateIncrement(tremoloDepth[1],0,1/(2*tremoloFrequency)) + }; + + int tremoloTableLength = (int)(OPL_SAMPLE_RATE/tremoloFrequency); + + // This is undocumented. The tremolo starts at the maximum attenuation, + // instead of at 0 dB: + tremoloTable[0][0] = tremoloDepth[0]; + tremoloTable[1][0] = tremoloDepth[1]; + int counter = 0; + // The first half of the triangle waveform: + while(tremoloTable[0][counter]<0) { + counter++; + tremoloTable[0][counter] = tremoloTable[0][counter-1] + tremoloIncrement[0]; + tremoloTable[1][counter] = tremoloTable[1][counter-1] + tremoloIncrement[1]; + } + // The second half of the triangle waveform: + while(tremoloTable[0][counter]>tremoloDepth[0] && counter> 8, reg & 0xFF, v); +} + +void OPL3::SetPanning(int c, float left, float right) +{ + if (FullPan) + { + Channel *channel; + + if (c < 9) + { + channel = channels[0][c]; + } + else + { + channel = channels[1][c - 9]; + } + channel->leftPan = left; + channel->rightPan = right; + } +} + +} // JavaOPL3 + +OPLEmul *JavaOPLCreate(bool stereo) +{ + return new JavaOPL3::OPL3(stereo); +} diff --git a/thirdparty/oplsynth/dosbox/opl.cpp b/thirdparty/oplsynth/dosbox/opl.cpp new file mode 100644 index 000000000..71c0404e6 --- /dev/null +++ b/thirdparty/oplsynth/dosbox/opl.cpp @@ -0,0 +1,1452 @@ +/* + * Copyright (C) 2002-2011 The DOSBox Team + * OPL2/OPL3 emulation library + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +/* + * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman + * Copyright (C) 1998-2001 Ken Silverman + * Ken Silverman's official web site: "http://www.advsys.net/ken" + */ + +#include "../oplsynth/opl.h" +#include +#include +#include +#include + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +#define OPLTYPE_IS_OPL3 +#undef PI + +#include "opl.h" + +static Bit16s wavtable[WAVEPREC*3]; // wave form table + +// key scale levels +static Bit8u kslev[8][16]; + +// key scale level lookup table +static const fltype kslmul[4] = { + 0.0, 0.5, 0.25, 1.0 // -> 0, 3, 1.5, 6 dB/oct +}; + +// frequency multiplicator lookup table +static const fltype frqmul_tab[16] = { + 0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 +}; + +// map a channel number to the register offset of the modulator (=register base) +static const Bit8u modulatorbase[9] = { + 0,1,2, + 8,9,10, + 16,17,18 +}; + +// map a register base to a modulator operator number or operator number +#if defined(OPLTYPE_IS_OPL3) +static const Bit8u regbase2modop[44] = { + 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8, // first set + 18,19,20,18,19,20,0,0,21,22,23,21,22,23,0,0,24,25,26,24,25,26 // second set +}; +static const Bit8u regbase2op[44] = { + 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17, // first set + 18,19,20,27,28,29,0,0,21,22,23,30,31,32,0,0,24,25,26,33,34,35 // second set +}; +#else +static const Bit8u regbase2modop[22] = { + 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8 +}; +static const Bit8u regbase2op[22] = { + 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17 +}; +#endif + + +// start of the waveform +static const Bit32u waveform[8] = { + WAVEPREC, + WAVEPREC>>1, + WAVEPREC, + (WAVEPREC*3)>>2, + 0, + 0, + (WAVEPREC*5)>>2, + WAVEPREC<<1 +}; + +// length of the waveform as mask +static const Bit32u wavemask[8] = { + WAVEPREC-1, + WAVEPREC-1, + (WAVEPREC>>1)-1, + (WAVEPREC>>1)-1, + WAVEPREC-1, + ((WAVEPREC*3)>>2)-1, + WAVEPREC>>1, + WAVEPREC-1 +}; + +// where the first entry resides +static const Bit32u wavestart[8] = { + 0, + WAVEPREC>>1, + 0, + WAVEPREC>>2, + 0, + 0, + 0, + WAVEPREC>>3 +}; + +// envelope generator function constants +static const fltype attackconst[4] = { + (fltype)(1/2.82624), + (fltype)(1/2.25280), + (fltype)(1/1.88416), + (fltype)(1/1.59744) +}; +static const fltype decrelconst[4] = { + (fltype)(1/39.28064), + (fltype)(1/31.41608), + (fltype)(1/26.17344), + (fltype)(1/22.44608) +}; + + +void operator_advance(op_type* op_pt, Bit32s vib) { + op_pt->wfpos = op_pt->tcount; // waveform position + + // advance waveform time + op_pt->tcount += op_pt->tinc; + op_pt->tcount += (Bit32s)(op_pt->tinc)*vib/FIXEDPT; + + op_pt->generator_pos += generator_add; +} + +void operator_advance_drums(op_type* op_pt1, Bit32s vib1, op_type* op_pt2, Bit32s vib2, op_type* op_pt3, Bit32s vib3) { + Bit32u c1 = op_pt1->tcount/FIXEDPT; + Bit32u c3 = op_pt3->tcount/FIXEDPT; + Bit32u phasebit = (((c1 & 0x88) ^ ((c1<<5) & 0x80)) | ((c3 ^ (c3<<2)) & 0x20)) ? 0x02 : 0x00; + + Bit32u noisebit = rand() & 1; + + Bit32u snare_phase_bit = (Bit32u)(((Bitu)((op_pt1->tcount/FIXEDPT) / 0x100))&1); + + //Hihat + Bit32u inttm = (phasebit<<8) | (0x34<<(phasebit ^ (noisebit<<1))); + op_pt1->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt1->tcount += op_pt1->tinc; + op_pt1->tcount += (Bit32s)(op_pt1->tinc)*vib1/FIXEDPT; + op_pt1->generator_pos += generator_add; + + //Snare + inttm = ((1+snare_phase_bit) ^ noisebit)<<8; + op_pt2->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt2->tcount += op_pt2->tinc; + op_pt2->tcount += (Bit32s)(op_pt2->tinc)*vib2/FIXEDPT; + op_pt2->generator_pos += generator_add; + + //Cymbal + inttm = (1+phasebit)<<8; + op_pt3->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt3->tcount += op_pt3->tinc; + op_pt3->tcount += (Bit32s)(op_pt3->tinc)*vib3/FIXEDPT; + op_pt3->generator_pos += generator_add; +} + + +// output level is sustained, mode changes only when operator is turned off (->release) +// or when the keep-sustained bit is turned off (->sustain_nokeep) +void operator_output(op_type* op_pt, Bit32s modulator, Bit32s trem) { + if (op_pt->op_state != OF_TYPE_OFF) { + op_pt->lastcval = op_pt->cval; + Bit32u i = (Bit32u)((op_pt->wfpos+modulator)/FIXEDPT); + + // wform: -16384 to 16383 (0x4000) + // trem : 32768 to 65535 (0x10000) + // step_amp: 0.0 to 1.0 + // vol : 1/2^14 to 1/2^29 (/0x4000; /1../0x8000) + + op_pt->cval = (Bit32s)(op_pt->step_amp*op_pt->vol*op_pt->cur_wform[i&op_pt->cur_wmask]*trem/16.0); + } +} + + +// no action, operator is off +void operator_off(op_type* /*op_pt*/) { +} + +// output level is sustained, mode changes only when operator is turned off (->release) +// or when the keep-sustained bit is turned off (->sustain_nokeep) +void operator_sustain(op_type* op_pt) { + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + for (Bit32u ct=0; ctcur_env_step++; + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in release mode, if output level reaches zero the operator is turned off +void operator_release(op_type* op_pt) { + // ??? boundary? + if (op_pt->amp > 0.00000001) { + // release phase + op_pt->amp *= op_pt->releasemul; + } + + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + for (Bit32u ct=0; ctcur_env_step++; // sample counter + if ((op_pt->cur_env_step & op_pt->env_step_r)==0) { + if (op_pt->amp <= 0.00000001) { + // release phase finished, turn off this operator + op_pt->amp = 0.0; + if (op_pt->op_state == OF_TYPE_REL) { + op_pt->op_state = OF_TYPE_OFF; + } + } + op_pt->step_amp = op_pt->amp; + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in decay mode, if sustain level is reached the output level is either +// kept (sustain level keep enabled) or the operator is switched into release mode +void operator_decay(op_type* op_pt) { + if (op_pt->amp > op_pt->sustain_level) { + // decay phase + op_pt->amp *= op_pt->decaymul; + } + + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + for (Bit32u ct=0; ctcur_env_step++; + if ((op_pt->cur_env_step & op_pt->env_step_d)==0) { + if (op_pt->amp <= op_pt->sustain_level) { + // decay phase finished, sustain level reached + if (op_pt->sus_keep) { + // keep sustain level (until turned off) + op_pt->op_state = OF_TYPE_SUS; + op_pt->amp = op_pt->sustain_level; + } else { + // next: release phase + op_pt->op_state = OF_TYPE_SUS_NOKEEP; + } + } + op_pt->step_amp = op_pt->amp; + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in attack mode, if full output level is reached, +// the operator is switched into decay mode +void operator_attack(op_type* op_pt) { + op_pt->amp = ((op_pt->a3*op_pt->amp + op_pt->a2)*op_pt->amp + op_pt->a1)*op_pt->amp + op_pt->a0; + + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + for (Bit32u ct=0; ctcur_env_step++; // next sample + if ((op_pt->cur_env_step & op_pt->env_step_a)==0) { // check if next step already reached + if (op_pt->amp > 1.0) { + // attack phase finished, next: decay + op_pt->op_state = OF_TYPE_DEC; + op_pt->amp = 1.0; + op_pt->step_amp = 1.0; + } + op_pt->step_skip_pos_a <<= 1; + if (op_pt->step_skip_pos_a==0) op_pt->step_skip_pos_a = 1; + if (op_pt->step_skip_pos_a & op_pt->env_step_skip_a) { // check if required to skip next step + op_pt->step_amp = op_pt->amp; + } + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + + +typedef void (*optype_fptr)(op_type*); + +optype_fptr opfuncs[6] = { + operator_attack, + operator_decay, + operator_release, + operator_sustain, // sustain phase (keeping level) + operator_release, // sustain_nokeep phase (release-style) + operator_off +}; + +void DBOPL::change_attackrate(Bitu regbase, op_type* op_pt) { + Bits attackrate = adlibreg[ARC_ATTR_DECR+regbase]>>4; + if (attackrate) { + fltype f = (fltype)(pow(FL2,(fltype)attackrate+(op_pt->toff>>2)-1)*attackconst[op_pt->toff&3]*recipsamp); + // attack rate coefficients + op_pt->a0 = (fltype)(0.0377*f); + op_pt->a1 = (fltype)(10.73*f+1); + op_pt->a2 = (fltype)(-17.57*f); + op_pt->a3 = (fltype)(7.42*f); + + Bits step_skip = attackrate*4 + op_pt->toff; + Bits steps = step_skip >> 2; + op_pt->env_step_a = (1<<(steps<=12?12-steps:0))-1; + + Bits step_num = (step_skip<=48)?(4-(step_skip&3)):0; + static Bit8u step_skip_mask[5] = {0xff, 0xfe, 0xee, 0xba, 0xaa}; + op_pt->env_step_skip_a = step_skip_mask[step_num]; + +#if defined(OPLTYPE_IS_OPL3) + if (step_skip>=60) { +#else + if (step_skip>=62) { +#endif + op_pt->a0 = (fltype)(2.0); // something that triggers an immediate transition to amp:=1.0 + op_pt->a1 = (fltype)(0.0); + op_pt->a2 = (fltype)(0.0); + op_pt->a3 = (fltype)(0.0); + } + } else { + // attack disabled + op_pt->a0 = 0.0; + op_pt->a1 = 1.0; + op_pt->a2 = 0.0; + op_pt->a3 = 0.0; + op_pt->env_step_a = 0; + op_pt->env_step_skip_a = 0; + } +} + +void DBOPL::change_decayrate(Bitu regbase, op_type* op_pt) { + Bits decayrate = adlibreg[ARC_ATTR_DECR+regbase]&15; + // decaymul should be 1.0 when decayrate==0 + if (decayrate) { + fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp); + op_pt->decaymul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(decayrate+(op_pt->toff>>2))))); + Bits steps = (decayrate*4 + op_pt->toff) >> 2; + op_pt->env_step_d = (1<<(steps<=12?12-steps:0))-1; + } else { + op_pt->decaymul = 1.0; + op_pt->env_step_d = 0; + } +} + +void DBOPL::change_releaserate(Bitu regbase, op_type* op_pt) { + Bits releaserate = adlibreg[ARC_SUSL_RELR+regbase]&15; + // releasemul should be 1.0 when releaserate==0 + if (releaserate) { + fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp); + op_pt->releasemul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(releaserate+(op_pt->toff>>2))))); + Bits steps = (releaserate*4 + op_pt->toff) >> 2; + op_pt->env_step_r = (1<<(steps<=12?12-steps:0))-1; + } else { + op_pt->releasemul = 1.0; + op_pt->env_step_r = 0; + } +} + +void DBOPL::change_sustainlevel(Bitu regbase, op_type* op_pt) { + Bits sustainlevel = adlibreg[ARC_SUSL_RELR+regbase]>>4; + // sustainlevel should be 0.0 when sustainlevel==15 (max) + if (sustainlevel<15) { + op_pt->sustain_level = (fltype)(pow(FL2,(fltype)sustainlevel * (-FL05))); + } else { + op_pt->sustain_level = 0.0; + } +} + +void DBOPL::change_waveform(Bitu regbase, op_type* op_pt) { +#if defined(OPLTYPE_IS_OPL3) + if (regbase>=ARC_SECONDSET) regbase -= (ARC_SECONDSET-22); // second set starts at 22 +#endif + // waveform selection + op_pt->cur_wmask = wavemask[wave_sel[regbase]]; + op_pt->cur_wform = &wavtable[waveform[wave_sel[regbase]]]; + // (might need to be adapted to waveform type here...) +} + +void DBOPL::change_keepsustain(Bitu regbase, op_type* op_pt) { + op_pt->sus_keep = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x20)>0; + if (op_pt->op_state==OF_TYPE_SUS) { + if (!op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS_NOKEEP; + } else if (op_pt->op_state==OF_TYPE_SUS_NOKEEP) { + if (op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS; + } +} + +// enable/disable vibrato/tremolo LFO effects +void DBOPL::change_vibrato(Bitu regbase, op_type* op_pt) { + op_pt->vibrato = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x40)!=0; + op_pt->tremolo = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x80)!=0; +} + +// change amount of self-feedback +void DBOPL::change_feedback(Bitu chanbase, op_type* op_pt) { + Bits feedback = adlibreg[ARC_FEEDBACK+chanbase]&14; + if (feedback) op_pt->mfbi = (Bit32s)(pow(FL2,(fltype)((feedback>>1)+8))); + else op_pt->mfbi = 0; +} + +void DBOPL::change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt) { + // frequency + Bit32u frn = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])&3)<<8) + (Bit32u)adlibreg[ARC_FREQ_NUM+chanbase]; + // block number/octave + Bit32u oct = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])>>2)&7); + op_pt->freq_high = (Bit32s)((frn>>7)&7); + + // keysplit + Bit32u note_sel = (adlibreg[8]>>6)&1; + op_pt->toff = ((frn>>9)&(note_sel^1)) | ((frn>>8)¬e_sel); + op_pt->toff += (oct<<1); + + // envelope scaling (KSR) + if (!(adlibreg[ARC_TVS_KSR_MUL+regbase]&0x10)) op_pt->toff >>= 2; + + // 20+a0+b0: + op_pt->tinc = (Bit32u)((((fltype)(frn<>6]*kslev[oct][frn>>6]); + op_pt->vol = (fltype)(pow(FL2,(fltype)(vol_in * -0.125 - 14))); + + // operator frequency changed, care about features that depend on it + change_attackrate(regbase,op_pt); + change_decayrate(regbase,op_pt); + change_releaserate(regbase,op_pt); +} + +void DBOPL::enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type) { + // check if this is really an off-on transition + if (op_pt->act_state == OP_ACT_OFF) { + Bits wselbase = regbase; + if (wselbase>=ARC_SECONDSET) wselbase -= (ARC_SECONDSET-22); // second set starts at 22 + + op_pt->tcount = wavestart[wave_sel[wselbase]]*FIXEDPT; + + // start with attack mode + op_pt->op_state = OF_TYPE_ATT; + op_pt->act_state |= act_type; + } +} + +void DBOPL::disable_operator(op_type* op_pt, Bit32u act_type) { + // check if this is really an on-off transition + if (op_pt->act_state != OP_ACT_OFF) { + op_pt->act_state &= (~act_type); + if (op_pt->act_state == OP_ACT_OFF) { + if (op_pt->op_state != OF_TYPE_OFF) op_pt->op_state = OF_TYPE_REL; + } + } +} + +void DBOPL::Reset() { + Bit32u samplerate = (Bit32u)OPL_SAMPLE_RATE; + Bits i, j, oct; + + int_samplerate = samplerate; + + generator_add = (Bit32u)(INTFREQU*FIXEDPT/int_samplerate); + + + memset((void *)adlibreg,0,sizeof(adlibreg)); + memset((void *)op,0,sizeof(op_type)*MAXOPERATORS); + memset((void *)wave_sel,0,sizeof(wave_sel)); + + for (i=0;i=0;i--) { + frqmul[i] = (fltype)(frqmul_tab[i]*INTFREQU/(fltype)WAVEPREC*(fltype)FIXEDPT*recipsamp); + } + + status = 0; + opl_index = 0; + + + // create vibrato table + vib_table[0] = 8; + vib_table[1] = 4; + vib_table[2] = 0; + vib_table[3] = -4; + for (i=4; i(VIBTAB_SIZE*FIXEDPT_LFO/8192*INTFREQU/int_samplerate); + vibtab_pos = 0; + + for (i=0; i -0.5/6 to 0) + for (i=14; i<41; i++) trem_table_int[i] = Bit32s(-i+14); // downwards (26 to 0 -> 0 to -1/6) + for (i=41; i<53; i++) trem_table_int[i] = Bit32s(i-40-26); // upwards (1 to 12 -> -1/6 to -0.5/6) + + for (i=0; i>1);i++) { + wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1) )*PI*2/WAVEPREC)); + wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1)+1)*PI*2/WAVEPREC)); + wavtable[i] = wavtable[(i<<1) +WAVEPREC]; + // alternative: (zero-less) +/* wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+1)*PI/WAVEPREC)); + wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+3)*PI/WAVEPREC)); + wavtable[i] = wavtable[(i<<1)-1+WAVEPREC]; */ + } + for (i=0;i<(WAVEPREC>>3);i++) { + wavtable[i+(WAVEPREC<<1)] = wavtable[i+(WAVEPREC>>3)]-16384; + wavtable[i+((WAVEPREC*17)>>3)] = wavtable[i+(WAVEPREC>>2)]+16384; + } + + // key scale level table verified ([table in book]*8/3) + kslev[7][0] = 0; kslev[7][1] = 24; kslev[7][2] = 32; kslev[7][3] = 37; + kslev[7][4] = 40; kslev[7][5] = 43; kslev[7][6] = 45; kslev[7][7] = 47; + kslev[7][8] = 48; + for (i=9;i<16;i++) kslev[7][i] = (Bit8u)(i+41); + for (j=6;j>=0;j--) { + for (i=0;i<16;i++) { + oct = (Bits)kslev[j+1][i]-8; + if (oct < 0) oct = 0; + kslev[j][i] = (Bit8u)oct; + } + } + } + +} + + + +void DBOPL::WriteReg(int idx, int val) { + Bit32u second_set = (Bit32u)idx&0x100; + adlibreg[idx] = val; + + switch (idx&0xf0) { + case ARC_CONTROL: + // here we check for the second set registers, too: + switch (idx) { + case 0x02: // timer1 counter + case 0x03: // timer2 counter + break; + case 0x04: + // IRQ reset, timer mask/start + if (val&0x80) { + // clear IRQ bits in status register + status &= ~0x60; + } else { + status = 0; + } + break; +#if defined(OPLTYPE_IS_OPL3) + case 0x04|ARC_SECONDSET: + // 4op enable/disable switches for each possible channel + op[0].is_4op = (val&1)>0; + op[3].is_4op_attached = op[0].is_4op; + op[1].is_4op = (val&2)>0; + op[4].is_4op_attached = op[1].is_4op; + op[2].is_4op = (val&4)>0; + op[5].is_4op_attached = op[2].is_4op; + op[18].is_4op = (val&8)>0; + op[21].is_4op_attached = op[18].is_4op; + op[19].is_4op = (val&16)>0; + op[22].is_4op_attached = op[19].is_4op; + op[20].is_4op = (val&32)>0; + op[23].is_4op_attached = op[20].is_4op; + break; + case 0x05|ARC_SECONDSET: + break; +#endif + case 0x08: + // CSW, note select + break; + default: + break; + } + break; + case ARC_TVS_KSR_MUL: + case ARC_TVS_KSR_MUL+0x10: { + // tremolo/vibrato/sustain keeping enabled; key scale rate; frequency multiplication + int num = (int)idx&7; + Bitu base = (idx-ARC_TVS_KSR_MUL)&0xff; + if ((num<6) && (base<22)) { + Bitu modop = regbase2modop[second_set?(base+22):base]; + Bitu regbase = base+second_set; + Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; + + // change tremolo/vibrato and sustain keeping of this operator + op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)]; + change_keepsustain(regbase,op_ptr); + change_vibrato(regbase,op_ptr); + + // change frequency calculations of this operator as + // key scale rate and frequency multiplicator can be changed +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) { + // operator uses frequency of channel + change_frequency(chanbase-3,regbase,op_ptr); + } else { + change_frequency(chanbase,regbase,op_ptr); + } +#else + change_frequency(chanbase,base,op_ptr); +#endif + } + } + break; + case ARC_KSL_OUTLEV: + case ARC_KSL_OUTLEV+0x10: { + // key scale level; output rate + int num = (int)idx&7; + Bitu base = (idx-ARC_KSL_OUTLEV)&0xff; + if ((num<6) && (base<22)) { + Bitu modop = regbase2modop[second_set?(base+22):base]; + Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; + + // change frequency calculations of this operator as + // key scale level and output rate can be changed + op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)]; +#if defined(OPLTYPE_IS_OPL3) + Bitu regbase = base+second_set; + if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) { + // operator uses frequency of channel + change_frequency(chanbase-3,regbase,op_ptr); + } else { + change_frequency(chanbase,regbase,op_ptr); + } +#else + change_frequency(chanbase,base,op_ptr); +#endif + } + } + break; + case ARC_ATTR_DECR: + case ARC_ATTR_DECR+0x10: { + // attack/decay rates + int num = (int)idx&7; + Bitu base = (idx-ARC_ATTR_DECR)&0xff; + if ((num<6) && (base<22)) { + Bitu regbase = base+second_set; + + // change attack rate and decay rate of this operator + op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]]; + change_attackrate(regbase,op_ptr); + change_decayrate(regbase,op_ptr); + } + } + break; + case ARC_SUSL_RELR: + case ARC_SUSL_RELR+0x10: { + // sustain level; release rate + int num = (int)idx&7; + Bitu base = (idx-ARC_SUSL_RELR)&0xff; + if ((num<6) && (base<22)) { + Bitu regbase = base+second_set; + + // change sustain level and release rate of this operator + op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]]; + change_releaserate(regbase,op_ptr); + change_sustainlevel(regbase,op_ptr); + } + } + break; + case ARC_FREQ_NUM: { + // 0xa0-0xa8 low8 frequency + Bitu base = (idx-ARC_FREQ_NUM)&0xff; + if (base<9) { + Bits opbase = second_set?(base+18):base; +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break; +#endif + // regbase of modulator: + Bits modbase = modulatorbase[base]+second_set; + + Bitu chanbase = base+second_set; + + change_frequency(chanbase,modbase,&op[opbase]); + change_frequency(chanbase,modbase+3,&op[opbase+9]); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are modified to the frequency of the channel + if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) { + change_frequency(chanbase,modbase+8,&op[opbase+3]); + change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]); + } +#endif + } + } + break; + case ARC_KON_BNUM: { + if (idx == ARC_PERC_MODE) { +#if defined(OPLTYPE_IS_OPL3) + if (second_set) return; +#endif + + if ((val&0x30) == 0x30) { // BassDrum active + enable_operator(16,&op[6],OP_ACT_PERC); + change_frequency(6,16,&op[6]); + enable_operator(16+3,&op[6+9],OP_ACT_PERC); + change_frequency(6,16+3,&op[6+9]); + } else { + disable_operator(&op[6],OP_ACT_PERC); + disable_operator(&op[6+9],OP_ACT_PERC); + } + if ((val&0x28) == 0x28) { // Snare active + enable_operator(17+3,&op[16],OP_ACT_PERC); + change_frequency(7,17+3,&op[16]); + } else { + disable_operator(&op[16],OP_ACT_PERC); + } + if ((val&0x24) == 0x24) { // TomTom active + enable_operator(18,&op[8],OP_ACT_PERC); + change_frequency(8,18,&op[8]); + } else { + disable_operator(&op[8],OP_ACT_PERC); + } + if ((val&0x22) == 0x22) { // Cymbal active + enable_operator(18+3,&op[8+9],OP_ACT_PERC); + change_frequency(8,18+3,&op[8+9]); + } else { + disable_operator(&op[8+9],OP_ACT_PERC); + } + if ((val&0x21) == 0x21) { // Hihat active + enable_operator(17,&op[7],OP_ACT_PERC); + change_frequency(7,17,&op[7]); + } else { + disable_operator(&op[7],OP_ACT_PERC); + } + + break; + } + // regular 0xb0-0xb8 + Bitu base = (idx-ARC_KON_BNUM)&0xff; + if (base<9) { + Bits opbase = second_set?(base+18):base; +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break; +#endif + // regbase of modulator: + Bits modbase = modulatorbase[base]+second_set; + + if (val&32) { + // operator switched on + enable_operator(modbase,&op[opbase],OP_ACT_NORMAL); // modulator (if 2op) + enable_operator(modbase+3,&op[opbase+9],OP_ACT_NORMAL); // carrier (if 2op) +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are switched on + if ((adlibreg[0x105]&1) && op[opbase].is_4op) { + // turn on chan+3 operators as well + enable_operator(modbase+8,&op[opbase+3],OP_ACT_NORMAL); + enable_operator(modbase+3+8,&op[opbase+3+9],OP_ACT_NORMAL); + } +#endif + } else { + // operator switched off + disable_operator(&op[opbase],OP_ACT_NORMAL); + disable_operator(&op[opbase+9],OP_ACT_NORMAL); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are switched off + if ((adlibreg[0x105]&1) && op[opbase].is_4op) { + // turn off chan+3 operators as well + disable_operator(&op[opbase+3],OP_ACT_NORMAL); + disable_operator(&op[opbase+3+9],OP_ACT_NORMAL); + } +#endif + } + + Bitu chanbase = base+second_set; + + // change frequency calculations of modulator and carrier (2op) as + // the frequency of the channel has changed + change_frequency(chanbase,modbase,&op[opbase]); + change_frequency(chanbase,modbase+3,&op[opbase+9]); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are modified to the frequency of the channel + if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) { + // change frequency calculations of chan+3 operators as well + change_frequency(chanbase,modbase+8,&op[opbase+3]); + change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]); + } +#endif + } + } + break; + case ARC_FEEDBACK: { + // 0xc0-0xc8 feedback/modulation type (AM/FM) + Bitu base = (idx-ARC_FEEDBACK)&0xff; + if (base<9) { + Bits opbase = second_set?(base+18):base; + Bitu chanbase = base+second_set; + change_feedback(chanbase,&op[opbase]); +#if defined(OPLTYPE_IS_OPL3) + // OPL3 panning + if (!FullPan) + { + op[opbase].left_pan = (float)((val&0x10)>>4); + op[opbase].right_pan = (float)((val&0x20)>>5); + } +#endif + } + } + break; + case ARC_WAVE_SEL: + case ARC_WAVE_SEL+0x10: { + int num = (int)idx&7; + Bitu base = (idx-ARC_WAVE_SEL)&0xff; + if ((num<6) && (base<22)) { +#if defined(OPLTYPE_IS_OPL3) + Bits wselbase = second_set?(base+22):base; // for easier mapping onto wave_sel[] + // change waveform + if (adlibreg[0x105]&1) wave_sel[wselbase] = val&7; // opl3 mode enabled, all waveforms accessible + else wave_sel[wselbase] = val&3; + op_type* op_ptr = &op[regbase2modop[wselbase]+((num<3) ? 0 : 9)]; + change_waveform(wselbase,op_ptr); +#else + if (adlibreg[0x01]&0x20) { + // wave selection enabled, change waveform + wave_sel[base] = val&3; + op_type* op_ptr = &op[regbase2modop[base]+((num<3) ? 0 : 9)]; + change_waveform(base,op_ptr); + } +#endif + } + } + break; + default: + break; + } +} + +static void OPL_INLINE clipit16(float ival, float* outval) { + *outval += ival / 10240.f; +} + + + +// be careful with this +// uses cptr and chanval, outputs into outbufl(/outbufr) +// for opl3 check if opl3-mode is enabled (which uses stereo panning) +#undef CHANVAL_OUT +#if defined(OPLTYPE_IS_OPL3) +#define CHANVAL_OUT \ + if (adlibreg[0x105]&1) { \ + outbufl[i] += chanval*cptr[0].left_pan; \ + outbufr[i] += chanval*cptr[0].right_pan; \ + } else { \ + outbufl[i] += chanval; \ + } +#else +#define CHANVAL_OUT \ + outbufl[i] += chanval; +#endif + +void DBOPL::Update(float* sndptr, int numsamples) { + Bits i, endsamples; + op_type* cptr; + + float outbufl[BLOCKBUF_SIZE]; +#if defined(OPLTYPE_IS_OPL3) + // second output buffer (right channel for opl3 stereo) + float outbufr[BLOCKBUF_SIZE]; +#endif + + // vibrato/tremolo lookup tables (global, to possibly be used by all operators) + Bit32s vib_lut[BLOCKBUF_SIZE]; + Bit32s trem_lut[BLOCKBUF_SIZE]; + + Bits samples_to_process = numsamples; + + for (Bits cursmp=0; cursmpBLOCKBUF_SIZE) endsamples = BLOCKBUF_SIZE; + + memset((void*)&outbufl,0,endsamples*sizeof(Bit32s)); +#if defined(OPLTYPE_IS_OPL3) + // clear second output buffer (opl3 stereo) + if (adlibreg[0x105]&1) memset((void*)&outbufr,0,endsamples*sizeof(Bit32s)); +#endif + + // calculate vibrato/tremolo lookup tables + Bit32s vib_tshift = ((adlibreg[ARC_PERC_MODE]&0x40)==0) ? 1 : 0; // 14cents/7cents switching + for (i=0;i=VIBTAB_SIZE) vibtab_pos-=VIBTAB_SIZE*FIXEDPT_LFO; + vib_lut[i] = vib_table[vibtab_pos/FIXEDPT_LFO]>>vib_tshift; // 14cents (14/100 of a semitone) or 7cents + + // cycle through tremolo table + tremtab_pos += tremtab_add; + if (tremtab_pos/FIXEDPT_LFO>=TREMTAB_SIZE) tremtab_pos-=TREMTAB_SIZE*FIXEDPT_LFO; + if (adlibreg[ARC_PERC_MODE]&0x80) trem_lut[i] = trem_table[tremtab_pos/FIXEDPT_LFO]; + else trem_lut[i] = trem_table[TREMTAB_SIZE+tremtab_pos/FIXEDPT_LFO]; + } + + if (adlibreg[ARC_PERC_MODE]&0x20) { + //BassDrum + cptr = &op[6]; + if (adlibreg[ARC_FEEDBACK+6]&1) { + // additive synthesis + if (cptr[9].op_state != OF_TYPE_OFF) { + if (cptr[9].vibrato) { + vibval1 = vibval_var1; + for (i=0;i=0; cur_ch--) { + // skip drum/percussion operators + if ((adlibreg[ARC_PERC_MODE]&0x20) && (cur_ch >= 6) && (cur_ch < 9)) continue; + + Bitu k = cur_ch; +#if defined(OPLTYPE_IS_OPL3) + if (cur_ch < 9) { + cptr = &op[cur_ch]; + } else { + cptr = &op[cur_ch+9]; // second set is operator18-operator35 + k += (-9+256); // second set uses registers 0x100 onwards + } + // check if this operator is part of a 4-op + if ((adlibreg[0x105]&1) && cptr->is_4op_attached) continue; +#else + cptr = &op[cur_ch]; +#endif + + // check for FM/AM + if (adlibreg[ARC_FEEDBACK+k]&1) { +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && cptr->is_4op) { + if (adlibreg[ARC_FEEDBACK+k+3]&1) { + // AM-AM-style synthesis (op1[fb] + (op2 * op3) + op4) + if (cptr[0].op_state != OF_TYPE_OFF) { + if (cptr[0].vibrato) { + vibval1 = vibval_var1; + for (i=0;iis_4op) { + if (adlibreg[ARC_FEEDBACK+k+3]&1) { + // FM-AM-style synthesis ((op1[fb] * op2) + (op3 * op4)) + if ((cptr[0].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF)) { + if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { + vibval1 = vibval_var1; + for (i=0;istereo) + for (i=0;istereo) + for (i=0;i= 9) + { + c += 9; + } + op[c].left_pan = left; + op[c].right_pan = right; + } +} + +DBOPL::DBOPL(bool fullpan) +{ + FullPan = fullpan; + Reset(); +} + +OPLEmul *DBOPLCreate(bool fullpan) +{ + return new DBOPL(fullpan); +} diff --git a/thirdparty/oplsynth/dosbox/opl.h b/thirdparty/oplsynth/dosbox/opl.h new file mode 100644 index 000000000..ccdbc124a --- /dev/null +++ b/thirdparty/oplsynth/dosbox/opl.h @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2002-2011 The DOSBox Team + * OPL2/OPL3 emulation library + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +/* + * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman + * Copyright (C) 1998-2001 Ken Silverman + * Ken Silverman's official web site: "http://www.advsys.net/ken" + */ + + +#define fltype double + +/* + define Bits, Bitu, Bit32s, Bit32u, Bit16s, Bit16u, Bit8s, Bit8u here +*/ +/* +#include +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; +*/ + + +/* + define attribution that inlines/forces inlining of a function (optional) +*/ +#define OPL_INLINE inline + + +#undef NUM_CHANNELS +#if defined(OPLTYPE_IS_OPL3) +#define NUM_CHANNELS 18 +#else +#define NUM_CHANNELS 9 +#endif + +#define MAXOPERATORS (NUM_CHANNELS*2) + + +#define FL05 ((fltype)0.5) +#define FL2 ((fltype)2.0) +#define PI ((fltype)3.1415926535897932384626433832795) + + +#define FIXEDPT 0x10000 // fixed-point calculations using 16+16 +#define FIXEDPT_LFO 0x1000000 // fixed-point calculations using 8+24 + +#define WAVEPREC 1024 // waveform precision (10 bits) + +#define INTFREQU ((fltype)(14318180.0 / 288.0)) // clocking of the chip + + +#define OF_TYPE_ATT 0 +#define OF_TYPE_DEC 1 +#define OF_TYPE_REL 2 +#define OF_TYPE_SUS 3 +#define OF_TYPE_SUS_NOKEEP 4 +#define OF_TYPE_OFF 5 + +#define ARC_CONTROL 0x00 +#define ARC_TVS_KSR_MUL 0x20 +#define ARC_KSL_OUTLEV 0x40 +#define ARC_ATTR_DECR 0x60 +#define ARC_SUSL_RELR 0x80 +#define ARC_FREQ_NUM 0xa0 +#define ARC_KON_BNUM 0xb0 +#define ARC_PERC_MODE 0xbd +#define ARC_FEEDBACK 0xc0 +#define ARC_WAVE_SEL 0xe0 + +#define ARC_SECONDSET 0x100 // second operator set for OPL3 + + +#define OP_ACT_OFF 0x00 +#define OP_ACT_NORMAL 0x01 // regular channel activated (bitmasked) +#define OP_ACT_PERC 0x02 // percussion channel activated (bitmasked) + +#define BLOCKBUF_SIZE 512 + + +// vibrato constants +#define VIBTAB_SIZE 8 +#define VIBFAC 70/50000 // no braces, integer mul/div + +// tremolo constants and table +#define TREMTAB_SIZE 53 +#define TREM_FREQ ((fltype)(3.7)) // tremolo at 3.7hz + + +/* operator struct definition + For OPL2 all 9 channels consist of two operators each, carrier and modulator. + Channel x has operators x as modulator and operators (9+x) as carrier. + For OPL3 all 18 channels consist either of two operators (2op mode) or four + operators (4op mode) which is determined through register4 of the second + adlib register set. + Only the channels 0,1,2 (first set) and 9,10,11 (second set) can act as + 4op channels. The two additional operators for a channel y come from the + 2op channel y+3 so the operatorss y, (9+y), y+3, (9+y)+3 make up a 4op + channel. +*/ +typedef struct operator_struct { + Bit32s cval, lastcval; // current output/last output (used for feedback) + Bit32u tcount, wfpos, tinc; // time (position in waveform) and time increment + fltype amp, step_amp; // and amplification (envelope) + fltype vol; // volume + fltype sustain_level; // sustain level + Bit32s mfbi; // feedback amount + fltype a0, a1, a2, a3; // attack rate function coefficients + fltype decaymul, releasemul; // decay/release rate functions + Bit32u op_state; // current state of operator (attack/decay/sustain/release/off) + Bit32u toff; + Bit32s freq_high; // highest three bits of the frequency, used for vibrato calculations + Bit16s* cur_wform; // start of selected waveform + Bit32u cur_wmask; // mask for selected waveform + Bit32u act_state; // activity state (regular, percussion) + bool sus_keep; // keep sustain level when decay finished + bool vibrato,tremolo; // vibrato/tremolo enable bits + + // variables used to provide non-continuous envelopes + Bit32u generator_pos; // for non-standard sample rates we need to determine how many samples have passed + Bits cur_env_step; // current (standardized) sample position + Bits env_step_a,env_step_d,env_step_r; // number of std samples of one step (for attack/decay/release mode) + Bit8u step_skip_pos_a; // position of 8-cyclic step skipping (always 2^x to check against mask) + Bits env_step_skip_a; // bitmask that determines if a step is skipped (respective bit is zero then) + +#if defined(OPLTYPE_IS_OPL3) + bool is_4op,is_4op_attached; // base of a 4op channel/part of a 4op channel + float left_pan,right_pan; // opl3 stereo panning amount +#endif +} op_type; + +// per-chip variables +class DBOPL : public OPLEmul +{ +private: + op_type op[MAXOPERATORS]; + + Bits int_samplerate; + + Bit8u status; + Bit32u opl_index; +#if defined(OPLTYPE_IS_OPL3) + Bit8u adlibreg[512]; // adlib register set (including second set) + Bit8u wave_sel[44]; // waveform selection +#else + Bit8u adlibreg[256]; // adlib register set + Bit8u wave_sel[22]; // waveform selection +#endif + + fltype recipsamp; // inverse of sampling rate + + // vibrato/tremolo tables + Bit32s vib_table[VIBTAB_SIZE]; + Bit32s trem_table[TREMTAB_SIZE*2]; + + Bit32s vibval_const[BLOCKBUF_SIZE]; + Bit32s tremval_const[BLOCKBUF_SIZE]; + + // vibrato value tables (used per-operator) + Bit32s vibval_var1[BLOCKBUF_SIZE]; + Bit32s vibval_var2[BLOCKBUF_SIZE]; + + // vibrato/trmolo value table pointers + Bit32s *vibval1, *vibval2, *vibval3, *vibval4; + Bit32s *tremval1, *tremval2, *tremval3, *tremval4; + + // calculated frequency multiplication values (depend on sampling rate) + fltype frqmul[16]; + + + // vibrato/tremolo increment/counter + Bit32u vibtab_pos; + Bit32u vibtab_add; + Bit32u tremtab_pos; + Bit32u tremtab_add; + + // Enable full MIDI panning; disable OPL3 panning + bool FullPan; + + + // enable an operator + void enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type); + + void disable_operator(op_type* op_pt, Bit32u act_type); + + // functions to change parameters of an operator + void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt); + + void change_attackrate(Bitu regbase, op_type* op_pt); + void change_decayrate(Bitu regbase, op_type* op_pt); + void change_releaserate(Bitu regbase, op_type* op_pt); + void change_sustainlevel(Bitu regbase, op_type* op_pt); + void change_waveform(Bitu regbase, op_type* op_pt); + void change_keepsustain(Bitu regbase, op_type* op_pt); + void change_vibrato(Bitu regbase, op_type* op_pt); + void change_feedback(Bitu chanbase, op_type* op_pt); + + // general functions +public: + void Reset(); + void Update(float* sndptr, int numsamples); + void UpdateS(short* sndptr, int numsamples); + void WriteReg(int idx, int val); + void SetPanning(int c, float left, float right); + + DBOPL(bool stereo); +}; + +static Bit32u generator_add; // should be a chip parameter diff --git a/thirdparty/oplsynth/fmopl.cpp b/thirdparty/oplsynth/fmopl.cpp new file mode 100644 index 000000000..37312117d --- /dev/null +++ b/thirdparty/oplsynth/fmopl.cpp @@ -0,0 +1,1852 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh +/* + +This file is based on fmopl.c from MAME. The non-YM3816 parts have been +ripped out in the interest of making this simpler, since Doom music doesn't +need them. I also made it render the sound a voice at a time instead of a +sample at a time, so unused voices don't waste time being calculated. If all +voices are playing, it's not much difference, but it does offer a big +improvement when only a few voices are playing. + + + +** +** File: fmopl.c - software implementation of FM sound generator +** types OPL and OPL2 +** +** Copyright Jarek Burczynski (bujar at mame dot net) +** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 0.72 +** + +Revision History: + +04-08-2003 Jarek Burczynski: + - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip + handles memory read/write or during the adpcm synthesis when the chip + requests another byte of ADPCM data. + +24-07-2003 Jarek Burczynski: + - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after + some (unknown) delay). Right now it's always set. + +14-06-2003 Jarek Burczynski: + - implemented all of the status register flags in Y8950 emulation + - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since + they can be either RAM or ROM + +08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip) + - corrected ym3526_read() to always set bit 2 and bit 1 + to HIGH state - identical to ym3812_read (verified on real YM3526) + +04-28-2002 Jarek Burczynski: + - binary exact Envelope Generator (verified on real YM3812); + compared to YM2151: the EG clock is equal to internal_clock, + rates are 2 times slower and volume resolution is one bit less + - modified interface functions (they no longer return pointer - + that's internal to the emulator now): + - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init() + - corrected 'off by one' error in feedback calculations (when feedback is off) + - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22) + - speeded up noise generator calculations (Nicola Salmoria) + +03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip) + Complete rewrite (all verified on real YM3812): + - corrected sin_tab and tl_tab data + - corrected operator output calculations + - corrected waveform_select_enable register; + simply: ignore all writes to waveform_select register when + waveform_select_enable == 0 and do not change the waveform previously selected. + - corrected KSR handling + - corrected Envelope Generator: attack shape, Sustain mode and + Percussive/Non-percussive modes handling + - Envelope Generator rates are two times slower now + - LFO amplitude (tremolo) and phase modulation (vibrato) + - rhythm sounds phase generation + - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm) + - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM) + - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1) + +12-28-2001 Acho A. Tang + - reflected Delta-T EOS status on Y8950 status port. + - fixed subscription range of attack/decay tables + + + To do: + add delay before key off in CSM mode (see CSMKeyControll) + verify volume of the FM part on the Y8950 +*/ + +#include +#include +#include +#include +#include +#include +//#include "driver.h" /* use M.A.M.E. */ +#include "opl.h" + +/* compiler dependence */ +#ifndef OSD_CPU_H +#define OSD_CPU_H +#endif + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4244) +#endif + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (EG timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<>KSR */ + uint8_t mul; /* multiple: mul_tab[ML] */ + + /* Phase Generator */ + uint32_t Cnt; /* frequency counter */ + uint32_t Incr; /* frequency counter step */ + uint8_t FB; /* feedback shift value */ + int32_t *connect1; /* slot1 output pointer */ + int32_t op1_out[2]; /* slot1 output for feedback */ + uint8_t CON; /* connection (algorithm) type */ + + /* Envelope Generator */ + uint8_t eg_type; /* percussive/non-percussive mode */ + uint8_t state; /* phase type */ + uint32_t TL; /* total level: TL << 2 */ + int32_t TLL; /* adjusted now TL */ + int32_t volume; /* envelope counter */ + uint32_t sl; /* sustain level: sl_tab[SL] */ + uint8_t eg_sh_ar; /* (attack state) */ + uint8_t eg_sel_ar; /* (attack state) */ + uint8_t eg_sh_dr; /* (decay state) */ + uint8_t eg_sel_dr; /* (decay state) */ + uint8_t eg_sh_rr; /* (release state) */ + uint8_t eg_sel_rr; /* (release state) */ + uint32_t key; /* 0 = KEY OFF, >0 = KEY ON */ + + /* LFO */ + uint32_t AMmask; /* LFO Amplitude Modulation enable mask */ + uint8_t vib; /* LFO Phase Modulation enable flag (active high)*/ + + /* waveform select */ + uint16_t wavetable; +}; + +struct OPL_CH +{ + OPL_SLOT SLOT[2]; + /* phase generator state */ + uint32_t block_fnum; /* block+fnum */ + uint32_t fc; /* Freq. Increment base */ + uint32_t ksl_base; /* KeyScaleLevel Base step */ + uint8_t kcode; /* key code (for key scaling) */ + float LeftVol; /* volumes for stereo panning */ + float RightVol; +}; + +/* OPL state */ +struct FM_OPL +{ + /* FM channel slots */ + OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/ + + uint32_t eg_cnt; /* global envelope generator counter */ + uint32_t eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ + uint32_t eg_timer_add; /* step of eg_timer */ + uint32_t eg_timer_overflow; /* envelope generator timer overflows every 1 sample (on real chip) */ + + uint8_t rhythm; /* Rhythm mode */ + + uint32_t fn_tab[1024]; /* fnumber->increment counter */ + + /* LFO */ + + uint8_t lfo_am_depth; + uint8_t lfo_pm_depth_range; + uint32_t lfo_am_cnt; + uint32_t lfo_am_inc; + uint32_t lfo_pm_cnt; + uint32_t lfo_pm_inc; + + uint32_t noise_rng; /* 23 bit noise shift register */ + uint32_t noise_p; /* current noise 'phase' */ + uint32_t noise_f; /* current noise period */ + + uint8_t wavesel; /* waveform select enable flag */ + + int T[2]; /* timer counters */ + uint8_t st[2]; /* timer enable */ + + + uint8_t address; /* address register */ + uint8_t status; /* status flag */ + uint8_t statusmask; /* status mask */ + uint8_t mode; /* Reg.08 : CSM,notesel,etc. */ + + bool IsStereo; /* Write stereo output */ +}; + + + +/* mapping of register number (offset) to slot number used by the emulator */ +static const int slot_array[32]= +{ + 0, 2, 4, 1, 3, 5,-1,-1, + 6, 8,10, 7, 9,11,-1,-1, + 12,14,16,13,15,17,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +/* key scale level */ +/* table is 3dB/octave , DV converts this into 6dB/octave */ +/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ +#define DV (0.1875/2.0) +static const uint32_t ksl_tab[8*16]= +{ + /* OCT 0 */ + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + /* OCT 1 */ + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(0.750/DV), uint32_t(1.125/DV), uint32_t(1.500/DV), + uint32_t(1.875/DV), uint32_t(2.250/DV), uint32_t(2.625/DV), uint32_t(3.000/DV), + /* OCT 2 */ + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), + uint32_t(0.000/DV), uint32_t(1.125/DV), uint32_t(1.875/DV), uint32_t(2.625/DV), + uint32_t(3.000/DV), uint32_t(3.750/DV), uint32_t(4.125/DV), uint32_t(4.500/DV), + uint32_t(4.875/DV), uint32_t(5.250/DV), uint32_t(5.625/DV), uint32_t(6.000/DV), + /* OCT 3 */ + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(1.875/DV), + uint32_t(3.000/DV), uint32_t(4.125/DV), uint32_t(4.875/DV), uint32_t(5.625/DV), + uint32_t(6.000/DV), uint32_t(6.750/DV), uint32_t(7.125/DV), uint32_t(7.500/DV), + uint32_t(7.875/DV), uint32_t(8.250/DV), uint32_t(8.625/DV), uint32_t(9.000/DV), + /* OCT 4 */ + uint32_t(0.000/DV), uint32_t(0.000/DV), uint32_t(3.000/DV), uint32_t(4.875/DV), + uint32_t(6.000/DV), uint32_t(7.125/DV), uint32_t(7.875/DV), uint32_t(8.625/DV), + uint32_t(9.000/DV), uint32_t(9.750/DV),uint32_t(10.125/DV),uint32_t(10.500/DV), + uint32_t(10.875/DV),uint32_t(11.250/DV),uint32_t(11.625/DV),uint32_t(12.000/DV), + /* OCT 5 */ + uint32_t(0.000/DV), uint32_t(3.000/DV), uint32_t(6.000/DV), uint32_t(7.875/DV), + uint32_t(9.000/DV),uint32_t(10.125/DV),uint32_t(10.875/DV),uint32_t(11.625/DV), + uint32_t(12.000/DV),uint32_t(12.750/DV),uint32_t(13.125/DV),uint32_t(13.500/DV), + uint32_t(13.875/DV),uint32_t(14.250/DV),uint32_t(14.625/DV),uint32_t(15.000/DV), + /* OCT 6 */ + uint32_t(0.000/DV), uint32_t(6.000/DV), uint32_t(9.000/DV),uint32_t(10.875/DV), + uint32_t(12.000/DV),uint32_t(13.125/DV),uint32_t(13.875/DV),uint32_t(14.625/DV), + uint32_t(15.000/DV),uint32_t(15.750/DV),uint32_t(16.125/DV),uint32_t(16.500/DV), + uint32_t(16.875/DV),uint32_t(17.250/DV),uint32_t(17.625/DV),uint32_t(18.000/DV), + /* OCT 7 */ + uint32_t(0.000/DV), uint32_t(9.000/DV),uint32_t(12.000/DV),uint32_t(13.875/DV), + uint32_t(15.000/DV),uint32_t(16.125/DV),uint32_t(16.875/DV),uint32_t(17.625/DV), + uint32_t(18.000/DV),uint32_t(18.750/DV),uint32_t(19.125/DV),uint32_t(19.500/DV), + uint32_t(19.875/DV),uint32_t(20.250/DV),uint32_t(20.625/DV),uint32_t(21.000/DV) +}; +#undef DV + +/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ +static const uint32_t ksl_shift[4] = { 31, 1, 2, 0 }; + + +/* sustain level table (3dB per step) */ +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +#define SC(db) (uint32_t) ( db * (2.0/ENV_STEP) ) +static const uint32_t sl_tab[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const unsigned char eg_inc[15*RATE_STEPS]={ +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ +/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ +/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(13) in this table - it's directly in the code */ +static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), + +/* rates 00-12 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 13 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 14 */ +O( 8),O( 9),O(10),O(11), + +/* rate 15 */ +O(12),O(12),O(12),O(12), + +/* 16 dummy rates (same as 15 3) */ +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ +/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ +/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + +/* rates 00-12 */ +O(12),O(12),O(12),O(12), +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 16 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), + +}; +#undef O + + +/* multiple table */ +#define ML 2 +static const uint8_t mul_tab[16]= { +/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ + uint8_t(0.50*ML), uint8_t(1.00*ML), uint8_t(2.00*ML), uint8_t(3.00*ML), uint8_t(4.00*ML), uint8_t(5.00*ML), uint8_t(6.00*ML), uint8_t(7.00*ML), + uint8_t(8.00*ML), uint8_t(9.00*ML),uint8_t(10.00*ML),uint8_t(10.00*ML),uint8_t(12.00*ML),uint8_t(12.00*ML),uint8_t(15.00*ML),uint8_t(15.00*ML) +}; +#undef ML + +/* TL_TAB_LEN is calculated as: +* 12 - sinus amplitude bits (Y axis) +* 2 - sinus sign bit (Y axis) +* TL_RES_LEN - sinus resolution (X axis) +*/ +#define TL_TAB_LEN (12*2*TL_RES_LEN) +static signed int tl_tab[TL_TAB_LEN]; + +#define ENV_QUIET (TL_TAB_LEN>>4) + +/* sin waveform table in 'decibel' scale */ +/* four waveforms on OPL2 type chips */ +static unsigned int sin_tab[SIN_LEN * 4]; + + +/* LFO Amplitude Modulation table (verified on real YM3812) + 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples + + Length: 210 elements. + + Each of the elements has to be repeated + exactly 64 times (on 64 consecutive samples). + The whole table takes: 64 * 210 = 13440 samples. + + When AM = 1 data is used directly + When AM = 0 data is divided by 4 before being used (losing precision is important) +*/ + +#define LFO_AM_TAB_ELEMENTS 210 + +static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS] = { +0,0,0,0,0,0,0, +1,1,1,1, +2,2,2,2, +3,3,3,3, +4,4,4,4, +5,5,5,5, +6,6,6,6, +7,7,7,7, +8,8,8,8, +9,9,9,9, +10,10,10,10, +11,11,11,11, +12,12,12,12, +13,13,13,13, +14,14,14,14, +15,15,15,15, +16,16,16,16, +17,17,17,17, +18,18,18,18, +19,19,19,19, +20,20,20,20, +21,21,21,21, +22,22,22,22, +23,23,23,23, +24,24,24,24, +25,25,25,25, +26,26,26, +25,25,25,25, +24,24,24,24, +23,23,23,23, +22,22,22,22, +21,21,21,21, +20,20,20,20, +19,19,19,19, +18,18,18,18, +17,17,17,17, +16,16,16,16, +15,15,15,15, +14,14,14,14, +13,13,13,13, +12,12,12,12, +11,11,11,11, +10,10,10,10, +9,9,9,9, +8,8,8,8, +7,7,7,7, +6,6,6,6, +5,5,5,5, +4,4,4,4, +3,3,3,3, +2,2,2,2, +1,1,1,1 +}; + +/* LFO Phase Modulation table (verified on real YM3812) */ +static const int8_t lfo_pm_table[8*8*2] = { +/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ +}; + + + +/* work table */ +struct FM_WorkTable +{ + int32_t phase_modulation; /* phase modulation input (SLOT 2) */ + int32_t output; + + uint32_t LFO_AM; + int32_t LFO_PM; +}; + +static bool CalcVoice (FM_WorkTable *wt, FM_OPL *OPL, int voice, float *buffer, int length); +static bool CalcVoice (FM_WorkTable *wt, FM_OPL *OPL, int voice, short *buffer, int length); +static bool CalcRhythm (FM_WorkTable *wt, FM_OPL *OPL, float *buffer, int length); +static bool CalcRhythm (FM_WorkTable *wt, FM_OPL *OPL, short *buffer, int length); + + + +/* status set and IRQ handling */ +static inline void OPL_STATUS_SET(FM_OPL *OPL,int flag) +{ + /* set status flag */ + OPL->status |= flag; + if(!(OPL->status & 0x80)) + { + if(OPL->status & OPL->statusmask) + { /* IRQ on */ + OPL->status |= 0x80; + } + } +} + +/* status reset and IRQ handling */ +static inline void OPL_STATUS_RESET(FM_OPL *OPL,int flag) +{ + /* reset status flag */ + OPL->status &=~flag; + if((OPL->status & 0x80)) + { + if (!(OPL->status & OPL->statusmask) ) + { + OPL->status &= 0x7f; + } + } +} + +/* IRQ mask set */ +static inline void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) +{ + OPL->statusmask = flag; + /* IRQ handling check */ + OPL_STATUS_SET(OPL,0); + OPL_STATUS_RESET(OPL,0); +} + + +/* advance LFO to next sample */ +static inline void advance_lfo(FM_WorkTable *wt, FM_OPL *OPL) +{ + uint8_t tmp; + + /* LFO */ + OPL->lfo_am_cnt += OPL->lfo_am_inc; + if (OPL->lfo_am_cnt >= (uint32_t)(LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= (LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ]; + + if (OPL->lfo_am_depth) + wt->LFO_AM = tmp; + else + wt->LFO_AM = tmp>>2; + + OPL->lfo_pm_cnt += OPL->lfo_pm_inc; + wt->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range; +} + +/* advance to next sample */ +static inline void advance(FM_WorkTable *wt, FM_OPL *OPL, int loch, int hich) +{ + OPL_CH *CH; + OPL_SLOT *op; + int i; + + OPL->eg_timer += OPL->eg_timer_add; + loch *= 2; + hich *= 2; + + while (OPL->eg_timer >= OPL->eg_timer_overflow) + { + OPL->eg_timer -= OPL->eg_timer_overflow; + + OPL->eg_cnt++; + + for (i = loch; i <= hich + 1; i++) + { + CH = &OPL->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + /* Envelope Generator */ + switch(op->state) + { + case EG_ATT: /* attack phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_ar)-1) ) ) + { + op->volume += (~op->volume * + (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)]) + ) >>3; + + if (op->volume <= MIN_ATT_INDEX) + { + op->volume = MIN_ATT_INDEX; + op->state = EG_DEC; + } + + } + break; + + case EG_DEC: /* decay phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_dr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)]; + + if ( op->volume >= (int32_t)op->sl ) + op->state = EG_SUS; + + } + break; + + case EG_SUS: /* sustain phase */ + + /* this is important behaviour: + one can change percusive/non-percussive modes on the fly and + the chip will remain in sustain phase - verified on real YM3812 */ + + if(op->eg_type) /* non-percussive mode */ + { + /* do nothing */ + } + else /* percussive mode */ + { + /* during sustain phase chip adds Release Rate (in percussive mode) */ + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + op->volume = MAX_ATT_INDEX; + } + /* else do nothing in sustain phase */ + } + break; + + case EG_REL: /* release phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + + } + break; + + default: + break; + } + + /* Phase Generator */ + if(op->vib) + { + uint8_t block; + unsigned int block_fnum = CH->block_fnum; + + unsigned int fnum_lfo = (block_fnum&0x0380) >> 7; + + signed int lfo_fn_table_index_offset = lfo_pm_table[wt->LFO_PM + 16*fnum_lfo ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + block_fnum += lfo_fn_table_index_offset; + block = (block_fnum&0x1c00) >> 10; + op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; + } + else /* LFO phase modulation = zero */ + { + op->Cnt += op->Incr; + } + } + else /* LFO phase modulation disabled for this operator */ + { + op->Cnt += op->Incr; + } + } + } +} + +static inline void advance_noise(FM_OPL *OPL) +{ + int i; + + /* The Noise Generator of the YM3812 is 23-bit shift register. + * Period is equal to 2^23-2 samples. + * Register works at sampling frequency of the chip, so output + * can change on every sample. + * + * Output of the register and input to the bit 22 is: + * bit0 XOR bit14 XOR bit15 XOR bit22 + * + * Simply use bit 22 as the noise output. + */ + + OPL->noise_p += OPL->noise_f; + i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ + OPL->noise_p &= FREQ_MASK; + while (i) + { + /* + uint32_t j; + j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1; + OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1); + */ + + /* + Instead of doing all the logic operations above, we + use a trick here (and use bit 0 as the noise output). + The difference is only that the noise bit changes one + step ahead. This doesn't matter since we don't know + what is real state of the noise_rng after the reset. + */ + + if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302; + OPL->noise_rng >>= 1; + + i--; + } +} + + +static inline signed int op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + uint32_t p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +static inline signed int op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + uint32_t p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + + +#define volume_calc(OP) ((OP)->TLL + ((uint32_t)(OP)->volume) + (wt->LFO_AM & (OP)->AMmask)) + +/* calculate output */ +static inline float OPL_CALC_CH( FM_WorkTable *wt, OPL_CH *CH ) +{ + OPL_SLOT *SLOT; + unsigned int env; + signed int out; + + wt->phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + *SLOT->connect1 += SLOT->op1_out[0]; + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + { + wt->output += op_calc(SLOT->Cnt, env, wt->phase_modulation, SLOT->wavetable); + /* [RH] Convert to floating point. */ + return float(wt->output) / 10240; + } + return 0; +} + +static inline short OPL_CALC_CH_S( FM_WorkTable *wt, OPL_CH *CH ) +{ + OPL_SLOT *SLOT; + unsigned int env; + signed int out; + + wt->phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + *SLOT->connect1 += SLOT->op1_out[0]; + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + { + wt->output += op_calc(SLOT->Cnt, env, wt->phase_modulation, SLOT->wavetable); + return wt->output; + } + return 0; +} + +/* + operators used in the rhythm sounds generation process: + + Envelope Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal + 6 / 0 12 50 70 90 f0 + + 6 / 1 15 53 73 93 f3 + + 7 / 0 13 51 71 91 f1 + + 7 / 1 16 54 74 94 f4 + + 8 / 0 14 52 72 92 f2 + + 8 / 1 17 55 75 95 f5 + + + Phase Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number MULTIPLE Drum Hat Drum Tom Cymbal + 6 / 0 12 30 + + 6 / 1 15 33 + + 7 / 0 13 31 + + + + 7 / 1 16 34 ----- n o t u s e d ----- + 8 / 0 14 32 + + 8 / 1 17 35 + + + +channel operator register number Bass High Snare Tom Top +number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal + 6 12,15 B6 A6 + + + 7 13,16 B7 A7 + + + + + 8 14,17 B8 A8 + + + + +*/ + +/* calculate rhythm */ + +static inline void OPL_CALC_RH( FM_WorkTable *wt, OPL_CH *CH, unsigned int noise ) +{ + OPL_SLOT *SLOT; + signed int out; + unsigned int env; + + + /* Bass Drum (verified on real YM3812): + - depends on the channel 6 'connect' register: + when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) + when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored + - output sample always is multiplied by 2 + */ + + wt->phase_modulation = 0; + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env = volume_calc(SLOT); + + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + + if (!SLOT->CON) + wt->phase_modulation = SLOT->op1_out[0]; + /* else ignore output of operator 1 */ + + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + wt->output += op_calc(SLOT->Cnt, env, wt->phase_modulation, SLOT->wavetable) * 2; + + + /* Phase generation is based on: */ + /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */ + /* SD (16) channel 7->slot 1 */ + /* TOM (14) channel 8->slot 1 */ + /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */ + + /* Envelope generation based on: */ + /* HH channel 7->slot1 */ + /* SD channel 7->slot2 */ + /* TOM channel 8->slot1 */ + /* TOP channel 8->slot2 */ + + + /* The following formulas can be well optimized. + I leave them in direct form for now (in case I've missed something). + */ + + /* High Hat (verified on real YM3812) */ + env = volume_calc(&CH[7].SLOT[SLOT1]); + if( env < ENV_QUIET ) + { + + /* high hat phase generation: + phase = d0 or 234 (based on frequency only) + phase = 34 or 2d0 (based on noise) + */ + + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0xd0; */ + /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ + uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((CH[8].SLOT[SLOT2].Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((CH[8].SLOT[SLOT2].Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ + if (res2) + phase = (0x200|(0xd0>>2)); + + + /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ + /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ + if (phase&0x200) + { + if (noise) + phase = 0x200|0xd0; + } + else + /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ + /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ + { + if (noise) + phase = 0xd0>>2; + } + + wt->output += op_calc(phase<>FREQ_SH)>>8)&1; + + /* when bit8 = 0 phase = 0x100; */ + /* when bit8 = 1 phase = 0x200; */ + uint32_t phase = bit8 ? 0x200 : 0x100; + + /* Noise bit XOR'es phase by 0x100 */ + /* when noisebit = 0 pass the phase from calculation above */ + /* when noisebit = 1 phase ^= 0x100; */ + /* in other words: phase ^= (noisebit<<8); */ + if (noise) + phase ^= 0x100; + + wt->output += op_calc(phase<output += op_calc(CH[8].SLOT[SLOT1].Cnt, env, 0, CH[8].SLOT[SLOT2].wavetable) * 2; + + /* Top Cymbal (verified on real YM3812) */ + env = volume_calc(&CH[8].SLOT[SLOT2]); + if( env < ENV_QUIET ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((CH[7].SLOT[SLOT1].Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0x100; */ + /* when res1 = 1 phase = 0x200 | 0x100; */ + uint32_t phase = res1 ? 0x300 : 0x100; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((CH[8].SLOT[SLOT2].Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((CH[8].SLOT[SLOT2].Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | 0x100; */ + if (res2) + phase = 0x300; + + wt->output += op_calc(phase<>= 4; /* 12 bits here */ + n = (n+1)>>1; /* round to nearest */ + /* 11 bits here (rounded) */ + n <<= 1; /* 12 bits here (as in real chip) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + for (i=1; i<12; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 ]>>i; + } + } + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + } + + for (i=0; i>1) ]; + + /* waveform 3: _ _ _ _ */ + /* / |_/ |_/ |_/ |_*/ + /* abs(output only first quarter of the sinus waveform) */ + + if (i & (1<<(SIN_BITS-2)) ) + sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; + } + + did_init = true; +} + +static void OPL_initalize(FM_OPL *OPL) +{ + int i; + + /* make fnumber -> increment counter table */ + for( i=0 ; i < 1024 ; i++ ) + { + /* opn phase increment counter = 20bit */ + OPL->fn_tab[i] = (uint32_t)( (double)i * 64 * OPL_FREQBASE * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ + } + + /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ + /* One entry from LFO_AM_TABLE lasts for 64 samples */ + OPL->lfo_am_inc = uint32_t((1.0 / 64.0 ) * (1<lfo_pm_inc = uint32_t((1.0 / 1024.0) * (1<eg_timer_add = uint32_t((1<eg_timer_overflow = uint32_t(( 1 ) * (1<IsStereo = false; + for (int i = 0; i < 9; ++i) + { + OPL->P_CH[i].LeftVol = (float)CENTER_PANNING_POWER; + OPL->P_CH[i].RightVol = (float)CENTER_PANNING_POWER; + } +} + +static inline void FM_KEYON(OPL_SLOT *SLOT, uint32_t key_set) +{ + if( !SLOT->key ) + { + /* restart Phase Generator */ + SLOT->Cnt = 0; + /* phase -> Attack */ + SLOT->state = EG_ATT; + } + SLOT->key |= key_set; +} + +static inline void FM_KEYOFF(OPL_SLOT *SLOT, uint32_t key_clr) +{ + if( SLOT->key ) + { + SLOT->key &= key_clr; + + if( !SLOT->key ) + { + /* phase -> Release */ + if (SLOT->state>EG_REL) + SLOT->state = EG_REL; + } + } +} + +/* update phase increment counter of operator (also update the EG rates if necessary) */ +static inline void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) +{ + int ksr; + + /* (frequency) phase increment counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; + } +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +static inline void set_mul(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = mul_tab[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_type = (v&0x20); + SLOT->vib = (v&0x40); + SLOT->AMmask = (v&0x80) ? ~0 : 0; + CALC_FCSLOT(CH,SLOT); +} + +/* set ksl & tl */ +static inline void set_ksl_tl(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ksl = ksl_shift[v >> 6]; + SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ + + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); +} + +/* set attack rate & decay rate */ +static inline void set_ar_dr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; + + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + + SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; +} + +/* set sustain level & release rate */ +static inline void set_sl_rr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->sl = sl_tab[ v>>4 ]; + + SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; +} + + +/* write a value v to register r on OPL chip */ +static void WriteRegister(FM_WorkTable *wt, FM_OPL *OPL, int r, int v) +{ + OPL_CH *CH; + int slot; + int block_fnum; + + /* adjust bus to 8 bits */ + r &= 0xff; + v &= 0xff; + + switch(r&0xe0) + { + case 0x00: /* 00-1f:control */ + switch(r&0x1f) + { + case 0x01: /* waveform select enable */ + OPL->wavesel = v&0x20; + break; + case 0x02: /* Timer 1 */ + OPL->T[0] = (256-v)*4; + break; + case 0x03: /* Timer 2 */ + OPL->T[1] = (256-v)*16; + break; + case 0x04: /* IRQ clear / mask and Timer enable */ + if(v&0x80) + { /* IRQ flag clear */ + OPL_STATUS_RESET(OPL,0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */ + } + else + { /* set IRQ mask ,timer enable*/ + uint8_t st1 = v&1; + uint8_t st2 = (v>>1)&1; + + /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ + OPL_STATUS_RESET(OPL, v & (0x78-0x08) ); + OPL_STATUSMASK_SET(OPL, (~v) & 0x78 ); + + /* timer 2 */ + if(OPL->st[1] != st2) + { + OPL->st[1] = st2; + } + /* timer 1 */ + if(OPL->st[0] != st1) + { + OPL->st[0] = st1; + } + } + break; + case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ + OPL->mode = v; + break; + } + break; + case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_mul(OPL,slot,v); + break; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ksl_tl(OPL,slot,v); + break; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ar_dr(OPL,slot,v); + break; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_sl_rr(OPL,slot,v); + break; + case 0xa0: + if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ + { + OPL->lfo_am_depth = v & 0x80; + OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0; + + OPL->rhythm = v&0x3f; + + if(OPL->rhythm&0x20) + { + /* BD key on/off */ + if(v&0x10) + { + FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2); + FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2); + } + else + { + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); + } + /* HH key on/off */ + if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2); + else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); + /* SD key on/off */ + if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2); + else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); + /* TOM key on/off */ + if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2); + else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY key on/off */ + if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2); + else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); + } + else + { + /* BD key off */ + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); + /* HH key off */ + FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); + /* SD key off */ + FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); + /* TOM key off */ + FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY off */ + FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); + } + return; + } + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + /* update */ + if(CH->block_fnum != (uint32_t)block_fnum) + { + uint8_t block = block_fnum >> 10; + + CH->block_fnum = block_fnum; + + CH->ksl_base = ksl_tab[block_fnum>>6]; + CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block); + + /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ + CH->kcode = (CH->block_fnum&0x1c00)>>9; + + /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */ + /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ + /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ + if (OPL->mode&0x40) + CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ + else + CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ + + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + break; + case 0xc0: + /* FB,C */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; + CH->SLOT[SLOT1].CON = v&1; + CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &wt->output : &wt->phase_modulation; + break; + case 0xe0: /* waveform select */ + /* simply ignore write to the waveform select register if selecting not enabled in test register */ + if(OPL->wavesel) + { + slot = slot_array[r&0x1f]; + if(slot < 0) return; + CH = &OPL->P_CH[slot/2]; + + CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN; + } + break; + } +} + +static void OPLResetChip(FM_WorkTable *wt, FM_OPL *OPL) +{ + int c,s; + int i; + + OPL->eg_timer = 0; + OPL->eg_cnt = 0; + + OPL->noise_rng = 1; /* noise shift register */ + OPL->mode = 0; /* normal mode */ + OPL_STATUS_RESET(OPL,0x7f); + + /* reset with register write */ + WriteRegister(wt,OPL,0x01,0); /* wavesel disable */ + WriteRegister(wt,OPL,0x02,0); /* Timer1 */ + WriteRegister(wt,OPL,0x03,0); /* Timer2 */ + WriteRegister(wt,OPL,0x04,0); /* IRQ mask clear */ + for(i = 0xff ; i >= 0x20 ; i-- ) WriteRegister(wt,OPL,i,0); + + /* reset operator parameters */ + for( c = 0 ; c < 9 ; c++ ) + { + OPL_CH *CH = &OPL->P_CH[c]; + for(s = 0 ; s < 2 ; s++ ) + { + /* wave table */ + CH->SLOT[s].wavetable = 0; + CH->SLOT[s].state = EG_OFF; + CH->SLOT[s].volume = MAX_ATT_INDEX; + } + } +} + + +class YM3812 : public OPLEmul +{ +private: + FM_OPL Chip; + FM_WorkTable WorkTable; + +public: + /* Create one of virtual YM3812 */ + YM3812(bool stereo) + { + memset(&WorkTable, 0, sizeof(WorkTable)); + init_tables(); + + /* clear */ + memset(&Chip, 0, sizeof(Chip)); + + /* init global tables */ + OPL_initalize(&Chip); + + Chip.IsStereo = stereo; + + Reset(); + } + + /* YM3812 I/O interface */ + void WriteReg(int reg, int v) + { + WriteRegister(&WorkTable, &Chip, reg & 0xff, v); + } + + void Reset() + { + OPLResetChip(&WorkTable, &Chip); + } + + /* [RH] Full support for MIDI panning */ + void SetPanning(int c, float left, float right) + { + Chip.P_CH[c].LeftVol = left; + Chip.P_CH[c].RightVol = right; + } + + + /* + ** Generate samples for one of the YM3812's + ** + ** '*buffer' is the output buffer pointer + ** 'length' is the number of samples that should be generated + */ + void Update(float *buffer, int length) + { + int i; + + uint8_t rhythm = Chip.rhythm&0x20; + + uint32_t lfo_am_cnt_bak = Chip.lfo_am_cnt; + uint32_t eg_timer_bak = Chip.eg_timer; + uint32_t eg_cnt_bak = Chip.eg_cnt; + + uint32_t lfo_am_cnt_out = lfo_am_cnt_bak; + uint32_t eg_timer_out = eg_timer_bak; + uint32_t eg_cnt_out = eg_cnt_bak; + + for (i = 0; i <= (rhythm ? 5 : 8); ++i) + { + Chip.lfo_am_cnt = lfo_am_cnt_bak; + Chip.eg_timer = eg_timer_bak; + Chip.eg_cnt = eg_cnt_bak; + if (CalcVoice (&WorkTable, &Chip, i, buffer, length)) + { + lfo_am_cnt_out = Chip.lfo_am_cnt; + eg_timer_out = Chip.eg_timer; + eg_cnt_out = Chip.eg_cnt; + } + } + + Chip.lfo_am_cnt = lfo_am_cnt_out; + Chip.eg_timer = eg_timer_out; + Chip.eg_cnt = eg_cnt_out; + + if (rhythm) /* Rhythm part */ + { + Chip.lfo_am_cnt = lfo_am_cnt_bak; + Chip.eg_timer = eg_timer_bak; + Chip.eg_cnt = eg_cnt_bak; + CalcRhythm (&WorkTable, &Chip, buffer, length); + } + } + + void UpdateS(short *buffer, int length) + { + int i; + + uint8_t rhythm = Chip.rhythm&0x20; + + uint32_t lfo_am_cnt_bak = Chip.lfo_am_cnt; + uint32_t eg_timer_bak = Chip.eg_timer; + uint32_t eg_cnt_bak = Chip.eg_cnt; + + uint32_t lfo_am_cnt_out = lfo_am_cnt_bak; + uint32_t eg_timer_out = eg_timer_bak; + uint32_t eg_cnt_out = eg_cnt_bak; + + for (i = 0; i <= (rhythm ? 5 : 8); ++i) + { + Chip.lfo_am_cnt = lfo_am_cnt_bak; + Chip.eg_timer = eg_timer_bak; + Chip.eg_cnt = eg_cnt_bak; + if (CalcVoice (&WorkTable, &Chip, i, buffer, length)) + { + lfo_am_cnt_out = Chip.lfo_am_cnt; + eg_timer_out = Chip.eg_timer; + eg_cnt_out = Chip.eg_cnt; + } + } + + Chip.lfo_am_cnt = lfo_am_cnt_out; + Chip.eg_timer = eg_timer_out; + Chip.eg_cnt = eg_cnt_out; + + if (rhythm) /* Rhythm part */ + { + Chip.lfo_am_cnt = lfo_am_cnt_bak; + Chip.eg_timer = eg_timer_bak; + Chip.eg_cnt = eg_cnt_bak; + CalcRhythm (&WorkTable, &Chip, buffer, length); + } + } + + std::string GetVoiceString(void *chip) + { + FM_OPL *OPL = (FM_OPL *)chip; + char out[9*3]; + + for (int i = 0; i <= 8; ++i) + { + int color; + + if (OPL != NULL && (OPL->P_CH[i].SLOT[0].state != EG_OFF || OPL->P_CH[i].SLOT[1].state != EG_OFF)) + { + color = 'D'; // Green means in use + } + else + { + color = 'A'; // Brick means free + } + out[i*3+0] = '\x1c'; + out[i*3+1] = color; + out[i*3+2] = '*'; + } + return std::string (out, 9*3); + } +}; + +OPLEmul *YM3812Create(bool stereo) +{ + /* emulator create */ + return new YM3812(stereo); +} + +// [RH] Render a whole voice at once. If nothing else, it lets us avoid +// wasting a lot of time on voices that aren't playing anything. + +static bool CalcVoice (FM_WorkTable *wt, FM_OPL *OPL, int voice, float *buffer, int length) +{ + OPL_CH *const CH = &OPL->P_CH[voice]; + int i; + + if (CH->SLOT[0].state == EG_OFF && CH->SLOT[1].state == EG_OFF) + { // Voice is not playing, so don't do anything for it + return false; + } + + for (i = 0; i < length; ++i) + { + advance_lfo(wt, OPL); + + wt->output = 0; + float sample = OPL_CALC_CH(wt, CH); + if (!OPL->IsStereo) + { + buffer[i] += sample; + } + else + { + buffer[i*2] += sample * CH->LeftVol; + buffer[i*2+1] += sample * CH->RightVol; + } + + advance(wt, OPL, voice, voice); + } + return true; +} + +static bool CalcVoice (FM_WorkTable *wt, FM_OPL *OPL, int voice, short *buffer, int length) +{ + OPL_CH *const CH = &OPL->P_CH[voice]; + int i; + + if (CH->SLOT[0].state == EG_OFF && CH->SLOT[1].state == EG_OFF) + { // Voice is not playing, so don't do anything for it + return false; + } + + for (i = 0; i < length; ++i) + { + advance_lfo(wt, OPL); + + wt->output = 0; + short sample = OPL_CALC_CH_S(wt, CH); + + if (!OPL->IsStereo) + { + buffer[i] += sample; + } + else + { + buffer[i*2] += sample * CH->LeftVol; + buffer[i*2+1] += sample * CH->RightVol; + } + + advance(wt, OPL, voice, voice); + } + return true; +} + +static bool CalcRhythm (FM_WorkTable *wt, FM_OPL *OPL, float *buffer, int length) +{ + int i; + + for (i = 0; i < length; ++i) + { + advance_lfo(wt, OPL); + + wt->output = 0; + OPL_CALC_RH(wt, &OPL->P_CH[0], OPL->noise_rng & 1); + /* [RH] Convert to floating point. */ + float sample = float(wt->output) / 10240; + if (!OPL->IsStereo) + { + buffer[i] += sample; + } + else + { + // [RH] Always use center panning for rhythm. + // The MIDI player doesn't use the rhythm section anyway. + buffer[i*2] += sample * CENTER_PANNING_POWER; + buffer[i*2+1] += sample * CENTER_PANNING_POWER; + } + + advance(wt, OPL, 6, 8); + advance_noise(OPL); + } + return true; +} + +static bool CalcRhythm (FM_WorkTable *wt, FM_OPL *OPL, short *buffer, int length) +{ + int i; + + for (i = 0; i < length; ++i) + { + advance_lfo(wt, OPL); + + wt->output = 0; + OPL_CALC_RH(wt, &OPL->P_CH[0], OPL->noise_rng & 1); + + if (!OPL->IsStereo) + { + buffer[i] += wt->output; + } + else + { + // [RH] Always use center panning for rhythm. + // The MIDI player doesn't use the rhythm section anyway. + buffer[i*2] += wt->output * CENTER_PANNING_POWER; + buffer[i*2+1] += wt->output * CENTER_PANNING_POWER; + } + + advance(wt, OPL, 6, 8); + advance_noise(OPL); + } + return true; +} diff --git a/thirdparty/oplsynth/musicblock.cpp b/thirdparty/oplsynth/musicblock.cpp new file mode 100644 index 000000000..4d0aeb249 --- /dev/null +++ b/thirdparty/oplsynth/musicblock.cpp @@ -0,0 +1,498 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2002-2016 Randy Heit +// Copyright 2005-2014 Simon Howard +// Copyright 2017 Christoph Oelckers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// +// This is mostly a reimplementation of the interface provided by +// MusLib based on Chocolate-Doom's OPL player, although the +// interface has been cleaned up a bit to be more consistent and readable. +// +// + +#include +#include +#include "musicblock.h" + +musicBlock::musicBlock () +{ + memset (this, 0, sizeof(*this)); + for(auto &oplchannel : oplchannels) oplchannel.Panning = 64; // default to center panning. + for(auto &voice : voices) voice.index = ~0u; // mark all free. +} + +musicBlock::~musicBlock () +{ +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +int musicBlock::releaseVoice(uint32_t slot, uint32_t killed) +{ + struct OPLVoice *ch = &voices[slot]; + io->WriteFrequency(slot, ch->note, ch->pitch, 0); + ch->index = ~0u; + ch->sustained = false; + if (!killed) ch->timestamp = ++timeCounter; + if (killed) io->MuteChannel(slot); + return slot; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +int musicBlock::findFreeVoice() +{ + // We want to prefer the least recently freed voice, as more recently + // freed voices can still play a tone from their release state. + // Sustained voices are replaced when there are no free voices. + uint32_t min_value = ~0u; + int result = -1; + for (uint32_t i = 0; i < io->NumChannels; ++i) + { + uint32_t voice_value = voices[i].timestamp + (voices[i].sustained ? (1 << 31) : 0); + if ((voices[i].index == ~0u || voices[i].sustained) && (voice_value < min_value)) + { + min_value = voice_value; + result = i; + } + } + if (result >= 0) + { + releaseVoice(result, 1); + } + return result; +} + +//---------------------------------------------------------------------------- +// +// When all voices are in use, we must discard an existing voice to +// play a new note. Find and free an existing voice. The channel +// passed to the function is the channel for the new note to be +// played. +// +//---------------------------------------------------------------------------- + +int musicBlock::replaceExistingVoice() +{ + // Check the allocated voices, if we find an instrument that is + // of a lower priority to the new instrument, discard it. + // If a voice is being used to play the second voice of an instrument, + // use that, as second voices are non-essential. + // Lower numbered MIDI channels implicitly have a higher priority + // than higher-numbered channels, eg. MIDI channel 1 is never + // discarded for MIDI channel 2. + int result = 0; + + for (uint32_t i = 0; i < io->NumChannels; ++i) + { + if (voices[i].current_instr_voice == &voices[i].current_instr->voices[1] || + voices[i].index >= voices[result].index) + { + result = i; + } + } + + releaseVoice(result, 1); + return result; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::voiceKeyOn(uint32_t slot, uint32_t channo, GenMidiInstrument *instrument, uint32_t instrument_voice, uint32_t key, uint32_t volume) +{ + struct OPLVoice *voice = &voices[slot]; + auto &channel = oplchannels[channo]; + GenMidiVoice *gmvoice; + + voice->index = channo; + voice->key = key; + + // Program the voice with the instrument data: + voice->current_instr = instrument; + gmvoice = voice->current_instr_voice = &instrument->voices[instrument_voice]; + io->WriteInstrument(slot,gmvoice, channel.Vibrato); + io->WritePan(slot, gmvoice, channel.Panning); + + // Set the volume level. + voice->note_volume = volume; + io->WriteVolume(slot, gmvoice, channel.Volume, channel.Expression, volume); + + // Write the frequency value to turn the note on. + + // Work out the note to use. This is normally the same as + // the key, unless it is a fixed pitch instrument. + int note; + if (instrument->flags & GENMIDI_FLAG_FIXED) note = instrument->fixed_note; + else if (channo == CHAN_PERCUSSION) note = 60; + else note = key; + + // If this is the second voice of a double voice instrument, the + // frequency index can be adjusted by the fine tuning field. + voice->fine_tuning = (instrument_voice != 0) ? (voice->current_instr->fine_tuning / 2) - 64 : 0; + voice->pitch = voice->fine_tuning + channel.Pitch; + + if (!(instrument->flags & GENMIDI_FLAG_FIXED) && channo != CHAN_PERCUSSION) + { + note += gmvoice->base_note_offset; + } + + // Avoid possible overflow due to base note offset: + + while (note < 0) + { + note += 12; + } + + while (note > HIGHEST_NOTE) + { + note -= 12; + } + voice->note = note; + io->WriteFrequency(slot, note, voice->pitch, 1); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +bool opl_singlevoice; + +void musicBlock::noteOn(uint32_t channel, uint8_t key, int volume) +{ + if (volume <= 0) + { + noteOff(channel, key); + return; + } + GenMidiInstrument *instrument; + + // Percussion channel is treated differently. + if (channel == CHAN_PERCUSSION) + { + if (key < GENMIDI_FIST_PERCUSSION || key >= GENMIDI_FIST_PERCUSSION + GENMIDI_NUM_PERCUSSION) + { + return; + } + + instrument = &OPLinstruments[key + (GENMIDI_NUM_INSTRS - GENMIDI_FIST_PERCUSSION)]; + } + else + { + auto inst = oplchannels[channel].Instrument; + if (inst >= GENMIDI_NUM_TOTAL) return; // better safe than sorry. + instrument = &OPLinstruments[inst]; + } + + bool double_voice = ((instrument->flags) & GENMIDI_FLAG_2VOICE) && !opl_singlevoice; + + int i = findFreeVoice(); + if (i < 0) i = replaceExistingVoice(); + + if (i >= 0) + { + voiceKeyOn(i, channel, instrument, 0, key, volume); + if (double_voice) + { + i = findFreeVoice(); + if (i >= 0) + { + voiceKeyOn(i, channel, instrument, 1, key, volume); + } + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::noteOff(uint32_t id, uint8_t note) +{ + uint32_t sustain = oplchannels[id].Sustain; + + for(uint32_t i = 0; i < io->NumChannels; i++) + { + if (voices[i].index == id && voices[i].key == note) + { + if (sustain >= MIN_SUSTAIN) + { + voices[i].sustained = true; + voices[i].timestamp = ++timeCounter; + } + else releaseVoice(i, 0); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::changePitch(uint32_t id, int val1, int val2) +{ + // Convert pitch from 14-bit to 7-bit, then scale it, since the player + // code only understands sensitivities of 2 semitones. + int pitch = ((val1 | (val2 << 7)) - 8192) * oplchannels[id].PitchSensitivity / (200 * 128) + 64; + oplchannels[id].Pitch = pitch; + for(uint32_t i = 0; i < io->NumChannels; i++) + { + auto &ch = voices[i]; + if (ch.index == id) + { + ch.pitch = ch.fine_tuning + pitch; + io->WriteFrequency(i, ch.note, ch.pitch, 1); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::changeModulation(uint32_t id, int value) +{ + bool vibrato = (value >= VIBRATO_THRESHOLD); + oplchannels[id].Vibrato = vibrato; + for (uint32_t i = 0; i < io->NumChannels; i++) + { + auto &ch = voices[i]; + if (ch.index == id) + { + io->WriteTremolo(i, ch.current_instr_voice, vibrato); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::changeSustain(uint32_t id, int value) +{ + oplchannels[id].Sustain = value; + if (value < MIN_SUSTAIN) + { + for (uint32_t i = 0; i < io->NumChannels; i++) + { + if (voices[i].index == id && voices[i].sustained) + releaseVoice(i, 0); + } + } +} + +//---------------------------------------------------------------------------- +// +// Change volume or expression. +// Since both go to the same register, one function can handle both. +// +//---------------------------------------------------------------------------- + +void musicBlock::changeVolume(uint32_t id, int value, bool expression) +{ + auto &chan = oplchannels[id]; + if (!expression) chan.Volume = value; + else chan.Expression = value; + for (uint32_t i = 0; i < io->NumChannels; i++) + { + auto &ch = voices[i]; + if (ch.index == id) + { + io->WriteVolume(i, ch.current_instr_voice, chan.Volume, chan.Expression, ch.note_volume); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::changePanning(uint32_t id, int value) +{ + oplchannels[id].Panning = value; + for(uint32_t i = 0; i < io->NumChannels; i++) + { + auto &ch = voices[i]; + if (ch.index == id) + { + io->WritePan(i, ch.current_instr_voice, value); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::notesOff(uint32_t id, int value) +{ + for (uint32_t i = 0; i < io->NumChannels; ++i) + { + if (voices[i].index == id) + { + if (oplchannels[id].Sustain >= MIN_SUSTAIN) + { + voices[i].sustained = true; + voices[i].timestamp = ++timeCounter; + } + else releaseVoice(i, 0); + } + } +} + +//---------------------------------------------------------------------------- +// +// release all notes for this channel +// +//---------------------------------------------------------------------------- + +void musicBlock::allNotesOff(uint32_t id, int value) +{ + for (uint32_t i = 0; i < io->NumChannels; ++i) + { + if (voices[i].index == id) + { + releaseVoice(i, 0); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::changeExtended(uint32_t id, uint8_t controller, int value) +{ + switch (controller) + { + case ctrlRPNHi: + oplchannels[id].RPN = (oplchannels[id].RPN & 0x007F) | (value << 7); + break; + + case ctrlRPNLo: + oplchannels[id].RPN = (oplchannels[id].RPN & 0x3F80) | value; + break; + + case ctrlNRPNLo: + case ctrlNRPNHi: + oplchannels[id].RPN = 0x3FFF; + break; + + case ctrlDataEntryHi: + if (oplchannels[id].RPN == 0) + { + oplchannels[id].PitchSensitivity = value * 100 + (oplchannels[id].PitchSensitivity % 100); + } + break; + + case ctrlDataEntryLo: + if (oplchannels[id].RPN == 0) + { + oplchannels[id].PitchSensitivity = value + (oplchannels[id].PitchSensitivity / 100) * 100; + } + break; + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::resetControllers(uint32_t chan, int vol) +{ + auto &channel = oplchannels[chan]; + + channel.Volume = vol; + channel.Expression = 127; + channel.Sustain = 0; + channel.Pitch = 64; + channel.RPN = 0x3fff; + channel.PitchSensitivity = 200; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::programChange(uint32_t channel, int value) +{ + oplchannels[channel].Instrument = value; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::resetAllControllers(int vol) +{ + uint32_t i; + + for (i = 0; i < NUM_CHANNELS; i++) + { + resetControllers(i, vol); + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::stopAllVoices() +{ + for (uint32_t i = 0; i < io->NumChannels; i++) + { + if (voices[i].index != ~0u) releaseVoice(i, 1); + voices[i].timestamp = 0; + } + timeCounter = 0; +} diff --git a/thirdparty/oplsynth/nukedopl3.cpp b/thirdparty/oplsynth/nukedopl3.cpp new file mode 100644 index 000000000..2f564683d --- /dev/null +++ b/thirdparty/oplsynth/nukedopl3.cpp @@ -0,0 +1,1036 @@ +/* +* Copyright (C) 2013-2015 Nuke.YKT(Alexey Khokholov) +* +* This library 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 library 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 library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Nuked Yamaha YMF262(aka OPL3) emulator. + Thanks: + MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + Feedback and Rhythm part calculation information. + forums.submarine.org.uk(carbon14, opl3): + Tremolo and phase generator calculation information. + OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + OPL2 ROMs. +*/ + +//version 1.6 + +/* Changelog: + v1.1: + Vibrato's sign fix. + v1.2: + Operator key fix. + Corrected 4-operator mode. + Corrected rhythm mode. + Some small fixes. + v1.2.1: + Small envelope generator fix. + v1.3: + Complete rewrite. + v1.4: + New envelope and waveform generator. + Some small fixes. + v1.4.1: + Envelope generator rate calculation fix. + v1.4.2: + Version for ZDoom. + v1.5: + Optimizations. + v1.6: + Improved emulation output. +*/ + +#include +#include +#include "nukedopl3.h" + +// +// Envelope generator +// + +namespace NukedOPL3 +{ + +typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); +typedef void(*envelope_genfunc)(opl_slot *slott); + +Bit16s envelope_calcexp(Bit32u level) { + if (level > 0x1fff) { + level = 0x1fff; + } + return ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 1) >> (level >> 8); +} + +Bit16s envelope_calcsin0(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + Bit16u neg = 0; + if (phase & 0x200) { + neg = ~0; + } + if (phase & 0x100) { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)) ^ neg; +} + +Bit16s envelope_calcsin1(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x200) { + out = 0x1000; + } + else if (phase & 0x100) { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin2(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x100) { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin3(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x100) { + out = 0x1000; + } + else { + out = logsinrom[phase & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin4(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + Bit16u neg = 0; + if ((phase & 0x300) == 0x100) { + neg = ~0; + } + if (phase & 0x200) { + out = 0x1000; + } + else if (phase & 0x80) { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else { + out = logsinrom[(phase << 1) & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)) ^ neg; +} + +Bit16s envelope_calcsin5(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + if (phase & 0x200) { + out = 0x1000; + } + else if (phase & 0x80) { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else { + out = logsinrom[(phase << 1) & 0xff]; + } + return envelope_calcexp(out + (envelope << 3)); +} + +Bit16s envelope_calcsin6(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u neg = 0; + if (phase & 0x200) { + neg = ~0; + } + return envelope_calcexp(envelope << 3) ^ neg; +} + +Bit16s envelope_calcsin7(Bit16u phase, Bit16u envelope) { + phase &= 0x3ff; + Bit16u out = 0; + Bit16u neg = 0; + if (phase & 0x200) { + neg = ~0; + phase = (phase & 0x1ff) ^ 0x1ff; + } + out = phase << 3; + return envelope_calcexp(out + (envelope << 3)) ^ neg; +} + +envelope_sinfunc envelope_sin[8] = { + envelope_calcsin0, + envelope_calcsin1, + envelope_calcsin2, + envelope_calcsin3, + envelope_calcsin4, + envelope_calcsin5, + envelope_calcsin6, + envelope_calcsin7 +}; + +void envelope_gen_off(opl_slot *slott); +void envelope_gen_attack(opl_slot *slott); +void envelope_gen_decay(opl_slot *slott); +void envelope_gen_sustain(opl_slot *slott); +void envelope_gen_release(opl_slot *slott); + +envelope_genfunc envelope_gen[5] = { + envelope_gen_off, + envelope_gen_attack, + envelope_gen_decay, + envelope_gen_sustain, + envelope_gen_release +}; + +enum envelope_gen_num { + envelope_gen_num_off = 0, + envelope_gen_num_attack = 1, + envelope_gen_num_decay = 2, + envelope_gen_num_sustain = 3, + envelope_gen_num_release = 4, + envelope_gen_num_change = 5 +}; + +Bit8u envelope_calc_rate(opl_slot *slot, Bit8u reg_rate) { + if (reg_rate == 0x00) { + return 0x00; + } + Bit8u rate = (reg_rate << 2) + (slot->reg_ksr ? slot->channel->ksv : (slot->channel->ksv >> 2)); + if (rate > 0x3c) { + rate = 0x3c; + } + return rate; +} + +void envelope_update_ksl(opl_slot *slot) { + Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 2) - ((0x08 - slot->channel->block) << 5); + if (ksl < 0) { + ksl = 0; + } + slot->eg_ksl = (Bit8u)ksl; +} + +void envelope_update_rate(opl_slot *slot) { + switch (slot->eg_gen) { + case envelope_gen_num_off: + slot->eg_rate = 0; + break; + case envelope_gen_num_attack: + slot->eg_rate = envelope_calc_rate(slot, slot->reg_ar); + break; + case envelope_gen_num_decay: + slot->eg_rate = envelope_calc_rate(slot, slot->reg_dr); + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + slot->eg_rate = envelope_calc_rate(slot, slot->reg_rr); + break; + } +} + +void envelope_gen_off(opl_slot *slot) { + slot->eg_rout = 0x1ff; +} + +void envelope_gen_attack(opl_slot *slot) { + if (slot->eg_rout == 0x00) { + slot->eg_gen = envelope_gen_num_decay; + envelope_update_rate(slot); + return; + } + slot->eg_rout += ((~slot->eg_rout) *slot->eg_inc) >> 3; + if (slot->eg_rout < 0x00) { + slot->eg_rout = 0x00; + } +} + +void envelope_gen_decay(opl_slot *slot) { + if (slot->eg_rout >= slot->reg_sl << 4) { + slot->eg_gen = envelope_gen_num_sustain; + envelope_update_rate(slot); + return; + } + slot->eg_rout += slot->eg_inc; +} + +void envelope_gen_sustain(opl_slot *slot) { + if (!slot->reg_type) { + envelope_gen_release(slot); + } +} + +void envelope_gen_release(opl_slot *slot) { + if (slot->eg_rout >= 0x1ff) { + slot->eg_gen = envelope_gen_num_off; + slot->eg_rout = 0x1ff; + envelope_update_rate(slot); + return; + } + slot->eg_rout += slot->eg_inc; +} + +void envelope_calc(opl_slot *slot) { + Bit8u rate_h, rate_l; + rate_h = slot->eg_rate >> 2; + rate_l = slot->eg_rate & 3; + Bit8u inc = 0; + if (eg_incsh[rate_h] > 0) { + if ((slot->chip->timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l][((slot->chip->timer) >> eg_incsh[rate_h]) & 0x07]; + } + } + else { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l][slot->chip->timer & 0x07] << (-eg_incsh[rate_h]); + } + slot->eg_inc = inc; + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + envelope_gen[slot->eg_gen](slot); +} + +void eg_keyon(opl_slot *slot, Bit8u type) { + if (!slot->key) { + slot->eg_gen = envelope_gen_num_attack; + envelope_update_rate(slot); + if ((slot->eg_rate >> 2) == 0x0f) { + slot->eg_gen = envelope_gen_num_decay; + envelope_update_rate(slot); + slot->eg_rout = 0x00; + } + slot->pg_phase = 0x00; + } + slot->key |= type; +} + +void eg_keyoff(opl_slot *slot, Bit8u type) { + if (slot->key) { + slot->key &= (~type); + if (!slot->key) { + slot->eg_gen = envelope_gen_num_release; + envelope_update_rate(slot); + } + } +} + +// +// Phase Generator +// + +void pg_generate(opl_slot *slot) { + Bit16u f_num = slot->channel->f_num; + if (slot->reg_vib) { + Bit8u f_num_high = f_num >> (7 + vib_table[(slot->chip->timer >> 10) & 0x07] + (0x01 - slot->chip->dvb)); + f_num += f_num_high * vibsgn_table[(slot->chip->timer >> 10) & 0x07]; + } + slot->pg_phase += (((f_num << slot->channel->block) >> 1) * mt[slot->reg_mult]) >> 1; +} + +// +// Noise Generator +// + +void n_generate(opl_chip *chip) { + if (chip->noise & 0x01) { + chip->noise ^= 0x800302; + } + chip->noise >>= 1; +} + +// +// Slot +// + +void slot_write20(opl_slot *slot, Bit8u data) { + if ((data >> 7) & 0x01) { + slot->trem = &slot->chip->tremval; + } + else { + slot->trem = (Bit8u*)&slot->chip->zeromod; + } + slot->reg_vib = (data >> 6) & 0x01; + slot->reg_type = (data >> 5) & 0x01; + slot->reg_ksr = (data >> 4) & 0x01; + slot->reg_mult = data & 0x0f; + envelope_update_rate(slot); +} + +void slot_write40(opl_slot *slot, Bit8u data) { + slot->reg_ksl = (data >> 6) & 0x03; + slot->reg_tl = data & 0x3f; + envelope_update_ksl(slot); +} + +void slot_write60(opl_slot *slot, Bit8u data) { + slot->reg_ar = (data >> 4) & 0x0f; + slot->reg_dr = data & 0x0f; + envelope_update_rate(slot); +} + +void slot_write80(opl_slot *slot, Bit8u data) { + slot->reg_sl = (data >> 4) & 0x0f; + if (slot->reg_sl == 0x0f) { + slot->reg_sl = 0x1f; + } + slot->reg_rr = data & 0x0f; + envelope_update_rate(slot); +} + +void slot_writee0(opl_slot *slot, Bit8u data) { + slot->reg_wf = data & 0x07; + if (slot->chip->newm == 0x00) { + slot->reg_wf &= 0x03; + } +} + +void slot_generatephase(opl_slot *slot, Bit16u phase) { + slot->out = envelope_sin[slot->reg_wf](phase, slot->eg_out); +} + +void slot_generate(opl_slot *slot) { + slot->out = envelope_sin[slot->reg_wf]((Bit16u)(slot->pg_phase >> 9) + (*slot->mod), slot->eg_out); +} + +void slot_generatezm(opl_slot *slot) { + slot->out = envelope_sin[slot->reg_wf]((Bit16u)(slot->pg_phase >> 9), slot->eg_out); +} + +void slot_calcfb(opl_slot *slot) { + slot->prout[1] = slot->prout[0]; + slot->prout[0] = slot->out; + if (slot->channel->fb != 0x00) { + slot->fbmod = (slot->prout[0] + slot->prout[1]) >> (0x09 - slot->channel->fb); + } + else { + slot->fbmod = 0; + } +} + +// +// Channel +// + +void chan_setupalg(opl_channel *channel); + +void chan_updaterhythm(opl_chip *chip, Bit8u data) { + chip->rhy = data & 0x3f; + if (chip->rhy & 0x20) { + opl_channel *channel6 = &chip->channel[6]; + opl_channel *channel7 = &chip->channel[7]; + opl_channel *channel8 = &chip->channel[8]; + channel6->out[0] = &channel6->slots[1]->out; + channel6->out[1] = &channel6->slots[1]->out; + channel6->out[2] = &chip->zeromod; + channel6->out[3] = &chip->zeromod; + channel7->out[0] = &channel7->slots[0]->out; + channel7->out[1] = &channel7->slots[0]->out; + channel7->out[2] = &channel7->slots[1]->out; + channel7->out[3] = &channel7->slots[1]->out; + channel8->out[0] = &channel8->slots[0]->out; + channel8->out[1] = &channel8->slots[0]->out; + channel8->out[2] = &channel8->slots[1]->out; + channel8->out[3] = &channel8->slots[1]->out; + for (Bit8u chnum = 6; chnum < 9; chnum++) { + chip->channel[chnum].chtype = ch_drum; + } + chan_setupalg(channel6); + //hh + if (chip->rhy & 0x01) { + eg_keyon(channel7->slots[0], egk_drum); + } + else { + eg_keyoff(channel7->slots[0], egk_drum); + } + //tc + if (chip->rhy & 0x02) { + eg_keyon(channel8->slots[1], egk_drum); + } + else { + eg_keyoff(channel8->slots[1], egk_drum); + } + //tom + if (chip->rhy & 0x04) { + eg_keyon(channel8->slots[0], egk_drum); + } + else { + eg_keyoff(channel8->slots[0], egk_drum); + } + //sd + if (chip->rhy & 0x08) { + eg_keyon(channel7->slots[1], egk_drum); + } + else { + eg_keyoff(channel7->slots[1], egk_drum); + } + //bd + if (chip->rhy & 0x10) { + eg_keyon(channel6->slots[0], egk_drum); + eg_keyon(channel6->slots[1], egk_drum); + } + else { + eg_keyoff(channel6->slots[0], egk_drum); + eg_keyoff(channel6->slots[1], egk_drum); + } + } + else { + for (Bit8u chnum = 6; chnum < 9; chnum++) { + chip->channel[chnum].chtype = ch_2op; + chan_setupalg(&chip->channel[chnum]); + } + } +} + +void chan_writea0(opl_channel *channel, Bit8u data) { + if (channel->chip->newm && channel->chtype == ch_4op2) { + return; + } + channel->f_num = (channel->f_num & 0x300) | data; + channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + envelope_update_ksl(channel->slots[0]); + envelope_update_ksl(channel->slots[1]); + envelope_update_rate(channel->slots[0]); + envelope_update_rate(channel->slots[1]); + if (channel->chip->newm && channel->chtype == ch_4op) { + channel->pair->f_num = channel->f_num; + channel->pair->ksv = channel->ksv; + envelope_update_ksl(channel->pair->slots[0]); + envelope_update_ksl(channel->pair->slots[1]); + envelope_update_rate(channel->pair->slots[0]); + envelope_update_rate(channel->pair->slots[1]); + } +} + +void chan_writeb0(opl_channel *channel, Bit8u data) { + if (channel->chip->newm && channel->chtype == ch_4op2) { + return; + } + channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8); + channel->block = (data >> 2) & 0x07; + channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + envelope_update_ksl(channel->slots[0]); + envelope_update_ksl(channel->slots[1]); + envelope_update_rate(channel->slots[0]); + envelope_update_rate(channel->slots[1]); + if (channel->chip->newm && channel->chtype == ch_4op) { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + envelope_update_ksl(channel->pair->slots[0]); + envelope_update_ksl(channel->pair->slots[1]); + envelope_update_rate(channel->pair->slots[0]); + envelope_update_rate(channel->pair->slots[1]); + } +} + +void chan_setupalg(opl_channel *channel) { + if (channel->chtype == ch_drum) { + switch (channel->alg & 0x01) { + case 0x00: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->slots[0]->out; + break; + case 0x01: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->chip->zeromod; + break; + } + return; + } + if (channel->alg & 0x08) { + return; + } + if (channel->alg & 0x04) { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) { + case 0x00: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->chip->zeromod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[1]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x02: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x03: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[0]->out; + channel->out[2] = &channel->slots[1]->out; + channel->out[3] = &channel->chip->zeromod; + break; + } + } + else { + switch (channel->alg & 0x01) { + case 0x00: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + } + } +} + +void chan_writec0(opl_channel *channel, Bit8u data) { + channel->fb = (data & 0x0e) >> 1; + channel->con = data & 0x01; + channel->alg = channel->con; + if (channel->chip->newm) { + if (channel->chtype == ch_4op) { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + chan_setupalg(channel->pair); + } + else if (channel->chtype == ch_4op2) { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + chan_setupalg(channel); + } + else { + chan_setupalg(channel); + } + } + else { + chan_setupalg(channel); + } + if (channel->chip->newm) { + channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; + channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; + } + else { + channel->cha = channel->chb = ~0; + } +} + +void chan_generaterhythm1(opl_chip *chip) { + opl_channel *channel6 = &chip->channel[6]; + opl_channel *channel7 = &chip->channel[7]; + opl_channel *channel8 = &chip->channel[8]; + slot_generate(channel6->slots[0]); + Bit16u phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff; + Bit16u phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff; + Bit16u phase = 0x00; + //hh tc phase bit + Bit16u phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; + //hh + phase = (phasebit << 9) | (0x34 << ((phasebit ^ (chip->noise & 0x01) << 1))); + slot_generatephase(channel7->slots[0], phase); + //tt + slot_generatezm(channel8->slots[0]); +} + +void chan_generaterhythm2(opl_chip *chip) { + opl_channel *channel6 = &chip->channel[6]; + opl_channel *channel7 = &chip->channel[7]; + opl_channel *channel8 = &chip->channel[8]; + slot_generate(channel6->slots[1]); + Bit16u phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff; + Bit16u phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff; + Bit16u phase = 0x00; + //hh tc phase bit + Bit16u phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; + //sd + phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8); + slot_generatephase(channel7->slots[1], phase); + //tc + phase = 0x100 | (phasebit << 9); + slot_generatephase(channel8->slots[1], phase); +} + +void chan_enable(opl_channel *channel) { + if (channel->chip->newm) { + if (channel->chtype == ch_4op) { + eg_keyon(channel->slots[0], egk_norm); + eg_keyon(channel->slots[1], egk_norm); + eg_keyon(channel->pair->slots[0], egk_norm); + eg_keyon(channel->pair->slots[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { + eg_keyon(channel->slots[0], egk_norm); + eg_keyon(channel->slots[1], egk_norm); + } + } + else { + eg_keyon(channel->slots[0], egk_norm); + eg_keyon(channel->slots[1], egk_norm); + } +} + +void chan_disable(opl_channel *channel) { + if (channel->chip->newm) { + if (channel->chtype == ch_4op) { + eg_keyoff(channel->slots[0], egk_norm); + eg_keyoff(channel->slots[1], egk_norm); + eg_keyoff(channel->pair->slots[0], egk_norm); + eg_keyoff(channel->pair->slots[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { + eg_keyoff(channel->slots[0], egk_norm); + eg_keyoff(channel->slots[1], egk_norm); + } + } + else { + eg_keyoff(channel->slots[0], egk_norm); + eg_keyoff(channel->slots[1], egk_norm); + } +} + +void chan_set4op(opl_chip *chip, Bit8u data) { + for (Bit8u bit = 0; bit < 6; bit++) { + Bit8u chnum = bit; + if (bit >= 3) { + chnum += 9 - 3; + } + if ((data >> bit) & 0x01) { + chip->channel[chnum].chtype = ch_4op; + chip->channel[chnum + 3].chtype = ch_4op2; + } + else { + chip->channel[chnum].chtype = ch_2op; + chip->channel[chnum + 3].chtype = ch_2op; + } + } +} + +Bit16s limshort(Bit32s a) { + if (a > 32767) { + a = 32767; + } + else if (a < -32768) { + a = -32768; + } + return (Bit16s)a; +} + +void chip_generate(opl_chip *chip, Bit16s *buff) { + buff[1] = limshort(chip->mixbuff[1]); + + for (Bit8u ii = 0; ii < 12; ii++) { + slot_calcfb(&chip->slot[ii]); + pg_generate(&chip->slot[ii]); + envelope_calc(&chip->slot[ii]); + slot_generate(&chip->slot[ii]); + } + + for (Bit8u ii = 12; ii < 15; ii++) { + slot_calcfb(&chip->slot[ii]); + pg_generate(&chip->slot[ii]); + envelope_calc(&chip->slot[ii]); + } + + if (chip->rhy & 0x20) { + chan_generaterhythm1(chip); + } + else { + slot_generate(&chip->slot[12]); + slot_generate(&chip->slot[13]); + slot_generate(&chip->slot[14]); + } + + chip->mixbuff[0] = 0; + for (Bit8u ii = 0; ii < 18; ii++) { + Bit16s accm = 0; + for (Bit8u jj = 0; jj < 4; jj++) { + accm += *chip->channel[ii].out[jj]; + } + if (chip->FullPan) { + chip->mixbuff[0] += (Bit16s)(accm * chip->channel[ii].fcha); + } + else { + chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha); + } + } + + for (Bit8u ii = 15; ii < 18; ii++) { + slot_calcfb(&chip->slot[ii]); + pg_generate(&chip->slot[ii]); + envelope_calc(&chip->slot[ii]); + } + + if (chip->rhy & 0x20) { + chan_generaterhythm2(chip); + } + else { + slot_generate(&chip->slot[15]); + slot_generate(&chip->slot[16]); + slot_generate(&chip->slot[17]); + } + + buff[0] = limshort(chip->mixbuff[0]); + + for (Bit8u ii = 18; ii < 33; ii++) { + slot_calcfb(&chip->slot[ii]); + pg_generate(&chip->slot[ii]); + envelope_calc(&chip->slot[ii]); + slot_generate(&chip->slot[ii]); + } + + chip->mixbuff[1] = 0; + for (Bit8u ii = 0; ii < 18; ii++) { + Bit16s accm = 0; + for (Bit8u jj = 0; jj < 4; jj++) { + accm += *chip->channel[ii].out[jj]; + } + if (chip->FullPan) { + chip->mixbuff[1] += (Bit16s)(accm * chip->channel[ii].fchb); + } + else { + chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb); + } + } + + for (Bit8u ii = 33; ii < 36; ii++) { + slot_calcfb(&chip->slot[ii]); + pg_generate(&chip->slot[ii]); + envelope_calc(&chip->slot[ii]); + slot_generate(&chip->slot[ii]); + } + + n_generate(chip); + + if ((chip->timer & 0x3f) == 0x3f) { + if (!chip->tremdir) { + if (chip->tremtval == 105) { + chip->tremtval--; + chip->tremdir = 1; + } + else { + chip->tremtval++; + } + } + else { + if (chip->tremtval == 0) { + chip->tremtval++; + chip->tremdir = 0; + } + else { + chip->tremtval--; + } + } + chip->tremval = (chip->tremtval >> 2) >> ((1 - chip->dam) << 1); + } + + chip->timer++; +} + +void NukedOPL3::Reset() { + memset(&opl3, 0, sizeof(opl_chip)); + for (Bit8u slotnum = 0; slotnum < 36; slotnum++) { + opl3.slot[slotnum].chip = &opl3; + opl3.slot[slotnum].mod = &opl3.zeromod; + opl3.slot[slotnum].eg_rout = 0x1ff; + opl3.slot[slotnum].eg_out = 0x1ff; + opl3.slot[slotnum].eg_gen = envelope_gen_num_off; + opl3.slot[slotnum].trem = (Bit8u*)&opl3.zeromod; + } + for (Bit8u channum = 0; channum < 18; channum++) { + opl3.channel[channum].slots[0] = &opl3.slot[ch_slot[channum]]; + opl3.channel[channum].slots[1] = &opl3.slot[ch_slot[channum] + 3]; + opl3.slot[ch_slot[channum]].channel = &opl3.channel[channum]; + opl3.slot[ch_slot[channum] + 3].channel = &opl3.channel[channum]; + if ((channum % 9) < 3) { + opl3.channel[channum].pair = &opl3.channel[channum + 3]; + } + else if ((channum % 9) < 6) { + opl3.channel[channum].pair = &opl3.channel[channum - 3]; + } + opl3.channel[channum].chip = &opl3; + opl3.channel[channum].out[0] = &opl3.zeromod; + opl3.channel[channum].out[1] = &opl3.zeromod; + opl3.channel[channum].out[2] = &opl3.zeromod; + opl3.channel[channum].out[3] = &opl3.zeromod; + opl3.channel[channum].chtype = ch_2op; + opl3.channel[channum].cha = ~0; + opl3.channel[channum].chb = ~0; + opl3.channel[channum].fcha = 1.0; + opl3.channel[channum].fchb = 1.0; + chan_setupalg(&opl3.channel[channum]); + } + opl3.noise = 0x306600; + opl3.timer = 0; + opl3.FullPan = FullPan; +} + +void NukedOPL3::WriteReg(int reg, int v) { + v &= 0xff; + reg &= 0x1ff; + Bit8u high = (reg >> 8) & 0x01; + Bit8u regm = reg & 0xff; + switch (regm & 0xf0) { + case 0x00: + if (high) { + switch (regm & 0x0f) { + case 0x04: + chan_set4op(&opl3, v); + break; + case 0x05: + opl3.newm = v & 0x01; + break; + } + } + else { + switch (regm & 0x0f) { + case 0x08: + opl3.nts = (v >> 6) & 0x01; + break; + } + } + break; + case 0x20: + case 0x30: + if (ad_slot[regm & 0x1f] >= 0) { + slot_write20(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x40: + case 0x50: + if (ad_slot[regm & 0x1f] >= 0) { + slot_write40(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x60: + case 0x70: + if (ad_slot[regm & 0x1f] >= 0) { + slot_write60(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x80: + case 0x90: + if (ad_slot[regm & 0x1f] >= 0) { + slot_write80(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);; + } + break; + case 0xe0: + case 0xf0: + if (ad_slot[regm & 0x1f] >= 0) { + slot_writee0(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xa0: + if ((regm & 0x0f) < 9) { + chan_writea0(&opl3.channel[9 * high + (regm & 0x0f)], v); + } + break; + case 0xb0: + if (regm == 0xbd && !high) { + opl3.dam = v >> 7; + opl3.dvb = (v >> 6) & 0x01; + chan_updaterhythm(&opl3, v); + } + else if ((regm & 0x0f) < 9) { + chan_writeb0(&opl3.channel[9 * high + (regm & 0x0f)], v); + if (v & 0x20) { + chan_enable(&opl3.channel[9 * high + (regm & 0x0f)]); + } + else { + chan_disable(&opl3.channel[9 * high + (regm & 0x0f)]); + } + } + break; + case 0xc0: + if ((regm & 0x0f) < 9) { + chan_writec0(&opl3.channel[9 * high + (regm & 0x0f)], v); + } + break; + } +} + +void NukedOPL3::Update(float* sndptr, int numsamples) { + Bit16s buffer[2]; + for (Bit32u i = 0; i < (Bit32u)numsamples; i++) { + chip_generate(&opl3, buffer); + *sndptr++ += (float)(buffer[0] / 10240.0); + *sndptr++ += (float)(buffer[1] / 10240.0); + } +} + +void NukedOPL3::UpdateS(short *sndptr, int numsamples) { + Bit16s buffer[2]; + for (Bit32u i = 0; i < (Bit32u)numsamples; i++) { + chip_generate(&opl3, buffer); + *sndptr++ += buffer[0] * 2; + *sndptr++ += buffer[1] * 2; + } +} + +void NukedOPL3::SetPanning(int c, float left, float right) { + if (FullPan) { + opl3.channel[c].fcha = left; + opl3.channel[c].fchb = right; + } +} + +NukedOPL3::NukedOPL3(bool stereo) { + FullPan = stereo; + Reset(); +} + +} // namespace NukedOPL3 + +OPLEmul *NukedOPL3Create(bool stereo) { + return new NukedOPL3::NukedOPL3(stereo); +} diff --git a/thirdparty/oplsynth/opl_mus_player.cpp b/thirdparty/oplsynth/opl_mus_player.cpp new file mode 100644 index 000000000..9f88538af --- /dev/null +++ b/thirdparty/oplsynth/opl_mus_player.cpp @@ -0,0 +1,527 @@ +/* +** opl_mus_player.cpp +** +**--------------------------------------------------------------------------- +** Copyright 1999-2016 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifdef _WIN32 +#include +#endif +#include +#include +#include +#include + +#include "opl_mus_player.h" +#include "opl.h" +#include "o_swap.h" + + +#define IMF_RATE 700.0 + +OPLmusicBlock::OPLmusicBlock(int core, int numchips) +{ + currentCore = core; + scoredata = NULL; + NextTickIn = 0; + LastOffset = 0; + NumChips = std::min(numchips, 2); + Looping = false; + FullPan = false; + io = NULL; + io = new OPLio; +} + +OPLmusicBlock::~OPLmusicBlock() +{ + delete io; +} + +void OPLmusicBlock::ResetChips (int numchips) +{ + io->Reset (); + NumChips = io->Init(currentCore, std::min(numchips, 2), FullPan, false); +} + +void OPLmusicBlock::Restart() +{ + stopAllVoices (); + resetAllControllers (127); + playingcount = 0; + LastOffset = 0; +} + +OPLmusicFile::OPLmusicFile (const void *data, size_t length, int core, int numchips, const char *&errormessage) + : OPLmusicBlock(core, numchips), ScoreLen ((int)length) +{ + static char errorbuffer[80]; + errormessage = nullptr; + if (io == nullptr) + { + return; + } + + scoredata = new uint8_t[ScoreLen]; + memcpy(scoredata, data, length); + + if (0 == (NumChips = io->Init(core, NumChips, false, false))) + { + goto fail; + } + + // Check for RDosPlay raw OPL format + if (!memcmp(scoredata, "RAWADATA", 8)) + { + RawPlayer = RDosPlay; + if (*(uint16_t *)(scoredata + 8) == 0) + { // A clock speed of 0 is bad + *(uint16_t *)(scoredata + 8) = 0xFFFF; + } + SamplesPerTick = LittleShort(*(uint16_t *)(scoredata + 8)) / ADLIB_CLOCK_MUL; + } + // Check for DosBox OPL dump + else if (!memcmp(scoredata, "DBRAWOPL", 8)) + { + if (LittleShort(((uint16_t *)scoredata)[5]) == 1) + { + RawPlayer = DosBox1; + SamplesPerTick = OPL_SAMPLE_RATE / 1000; + ScoreLen = std::min(ScoreLen - 24, LittleLong(((uint32_t *)scoredata)[4])) + 24; + } + else if (LittleLong(((uint32_t *)scoredata)[2]) == 2) + { + bool okay = true; + if (scoredata[21] != 0) + { + snprintf(errorbuffer, 80, "Unsupported DOSBox Raw OPL format %d\n", scoredata[20]); + errormessage = errorbuffer; + okay = false; + } + if (scoredata[22] != 0) + { + snprintf(errorbuffer, 80, "Unsupported DOSBox Raw OPL compression %d\n", scoredata[21]); + errormessage = errorbuffer; + okay = false; + } + if (!okay) + goto fail; + RawPlayer = DosBox2; + SamplesPerTick = OPL_SAMPLE_RATE / 1000; + int headersize = 0x1A + scoredata[0x19]; + ScoreLen = std::min(ScoreLen - headersize, LittleLong(((uint32_t *)scoredata)[3]) * 2) + headersize; + } + else + { + snprintf(errorbuffer, 80, "Unsupported DOSBox Raw OPL version %d.%d\n", LittleShort(((uint16_t *)scoredata)[4]), LittleShort(((uint16_t *)scoredata)[5])); + errormessage = errorbuffer; + goto fail; + } + } + // Check for modified IMF format (includes a header) + else if (!memcmp(scoredata, "ADLIB\1", 6)) + { + int songlen; + uint8_t *max = scoredata + ScoreLen; + RawPlayer = IMF; + SamplesPerTick = OPL_SAMPLE_RATE / IMF_RATE; + + score = scoredata + 6; + // Skip track and game name + for (int i = 2; i != 0; --i) + { + while (score < max && *score++ != '\0') {} + } + if (score < max) score++; // Skip unknown byte + if (score + 8 > max) + { // Not enough room left for song data + goto fail; + } + songlen = LittleLong(*(uint32_t *)score); + if (songlen != 0 && (songlen +=4) < ScoreLen - (score - scoredata)) + { + ScoreLen = songlen + int(score - scoredata); + } + } + else + { + errormessage = "Unknown OPL format"; + goto fail; + } + + Restart (); + return; + +fail: + delete[] scoredata; + scoredata = nullptr; + return; + +} + +OPLmusicFile::~OPLmusicFile () +{ + if (scoredata != NULL) + { + io->Reset (); + delete[] scoredata; + scoredata = NULL; + } +} + +bool OPLmusicFile::IsValid () const +{ + return scoredata != NULL; +} + +void OPLmusicFile::SetLooping (bool loop) +{ + Looping = loop; +} + +void OPLmusicFile::Restart () +{ + OPLmusicBlock::Restart(); + WhichChip = 0; + switch (RawPlayer) + { + case RDosPlay: + score = scoredata + 10; + SamplesPerTick = LittleShort(*(uint16_t *)(scoredata + 8)) / ADLIB_CLOCK_MUL; + break; + + case DosBox1: + score = scoredata + 24; + SamplesPerTick = OPL_SAMPLE_RATE / 1000; + break; + + case DosBox2: + score = scoredata + 0x1A + scoredata[0x19]; + SamplesPerTick = OPL_SAMPLE_RATE / 1000; + break; + + case IMF: + score = scoredata + 6; + + // Skip track and game name + for (int i = 2; i != 0; --i) + { + while (*score++ != '\0') {} + } + score++; // Skip unknown byte + if (*(uint32_t *)score != 0) + { + score += 4; // Skip song length + } + break; + } + io->SetClockRate(SamplesPerTick); +} + +bool OPLmusicBlock::ServiceStream (void *buff, int numbytes) +{ + float *samples1 = (float *)buff; + int stereoshift = (int)(FullPan | io->IsOPL3); + int numsamples = numbytes / (sizeof(float) << stereoshift); + bool prevEnded = false; + bool res = true; + + memset(buff, 0, numbytes); + + while (numsamples > 0) + { + int tick_in = int(NextTickIn); + int samplesleft = std::min(numsamples, tick_in); + size_t i; + + if (samplesleft > 0) + { + for (i = 0; i < io->NumChips; ++i) + { + io->chips[i]->Update(samples1, samplesleft); + } + OffsetSamples(samples1, samplesleft << stereoshift); + NextTickIn -= samplesleft; + assert (NextTickIn >= 0); + numsamples -= samplesleft; + samples1 += samplesleft << stereoshift; + } + + if (NextTickIn < 1) + { + int next = PlayTick(); + assert(next >= 0); + if (next == 0) + { // end of song + if (!Looping || prevEnded) + { + if (numsamples > 0) + { + for (i = 0; i < io->NumChips; ++i) + { + io->chips[i]->Update(samples1, numsamples); + } + OffsetSamples(samples1, numsamples << stereoshift); + } + res = false; + break; + } + else + { + // Avoid infinite loops from songs that do nothing but end + prevEnded = true; + Restart (); + } + } + else + { + prevEnded = false; + io->WriteDelay(next); + NextTickIn += SamplesPerTick * next; + assert (NextTickIn >= 0); + } + } + } + return res; +} + +void OPLmusicBlock::OffsetSamples(float *buff, int count) +{ + // Three out of four of the OPL waveforms are non-negative. Depending on + // timbre selection, this can cause the output waveform to tend toward + // very large positive values. Heretic's music is particularly bad for + // this. This function attempts to compensate by offseting the sample + // data back to around the [-1.0, 1.0] range. + + double max = -1e10, min = 1e10, offset, step; + int i, ramp, largest_at = 0; + + // Find max and min values for this segment of the waveform. + for (i = 0; i < count; ++i) + { + if (buff[i] > max) + { + max = buff[i]; + largest_at = i; + } + if (buff[i] < min) + { + min = buff[i]; + largest_at = i; + } + } + // Prefer to keep the offset at 0, even if it means a little clipping. + if (LastOffset == 0 && min >= -1.1 && max <= 1.1) + { + offset = 0; + } + else + { + offset = (max + min) / 2; + // If the new offset is close to 0, make it 0 to avoid making another + // full loop through the sample data. + if (fabs(offset) < 1/256.0) + { + offset = 0; + } + } + // Ramp the offset change so there aren't any abrupt clicks in the output. + // If the ramp is too short, it can sound scratchy. cblood2.mid is + // particularly unforgiving of short ramps. + if (count >= 512) + { + ramp = 512; + step = (offset - LastOffset) / 512; + } + else + { + ramp = std::min(count, std::max(196, largest_at)); + step = (offset - LastOffset) / ramp; + } + offset = LastOffset; + i = 0; + if (step != 0) + { + for (; i < ramp; ++i) + { + buff[i] = float(buff[i] - offset); + offset += step; + } + } + if (offset != 0) + { + for (; i < count; ++i) + { + buff[i] = float(buff[i] - offset); + } + } + LastOffset = float(offset); +} + +int OPLmusicFile::PlayTick () +{ + uint8_t reg, data; + uint16_t delay; + + switch (RawPlayer) + { + case RDosPlay: + while (score < scoredata + ScoreLen) + { + data = *score++; + reg = *score++; + switch (reg) + { + case 0: // Delay + if (data != 0) + { + return data; + } + break; + + case 2: // Speed change or OPL3 switch + if (data == 0) + { + SamplesPerTick = LittleShort(*(uint16_t *)(score)) / ADLIB_CLOCK_MUL; + io->SetClockRate(SamplesPerTick); + score += 2; + } + else if (data == 1) + { + WhichChip = 0; + } + else if (data == 2) + { + WhichChip = 1; + } + break; + + case 0xFF: // End of song + if (data == 0xFF) + { + return 0; + } + break; + + default: // It's something to stuff into the OPL chip + io->WriteRegister(WhichChip, reg, data); + break; + } + } + break; + + case DosBox1: + while (score < scoredata + ScoreLen) + { + reg = *score++; + + if (reg == 4) + { + reg = *score++; + data = *score++; + } + else if (reg == 0) + { // One-byte delay + return *score++ + 1; + } + else if (reg == 1) + { // Two-byte delay + int delay = score[0] + (score[1] << 8) + 1; + score += 2; + return delay; + } + else if (reg == 2) + { // Select OPL chip 0 + WhichChip = 0; + continue; + } + else if (reg == 3) + { // Select OPL chip 1 + WhichChip = 1; + continue; + } + else + { + data = *score++; + } + io->WriteRegister(WhichChip, reg, data); + } + break; + + case DosBox2: + { + uint8_t *to_reg = scoredata + 0x1A; + uint8_t to_reg_size = scoredata[0x19]; + uint8_t short_delay_code = scoredata[0x17]; + uint8_t long_delay_code = scoredata[0x18]; + + while (score < scoredata + ScoreLen) + { + uint8_t code = *score++; + data = *score++; + + // Which OPL chip to write to is encoded in the high bit of the code value. + int which = !!(code & 0x80); + code &= 0x7F; + + if (code == short_delay_code) + { + return data + 1; + } + else if (code == long_delay_code) + { + return (data + 1) << 8; + } + else if (code < to_reg_size) + { + io->WriteRegister(which, to_reg[code], data); + } + } + } + break; + + case IMF: + delay = 0; + while (delay == 0 && score + 4 - scoredata <= ScoreLen) + { + if (*(uint32_t *)score == 0xFFFFFFFF) + { // This is a special value that means to end the song. + return 0; + } + reg = score[0]; + data = score[1]; + delay = LittleShort(((uint16_t *)score)[1]); + score += 4; + io->WriteRegister (0, reg, data); + } + return delay; + } + return 0; +} + diff --git a/thirdparty/oplsynth/oplio.cpp b/thirdparty/oplsynth/oplio.cpp new file mode 100644 index 000000000..e97d726f1 --- /dev/null +++ b/thirdparty/oplsynth/oplio.cpp @@ -0,0 +1,497 @@ +//----------------------------------------------------------------------------- +// +// Copyright 2002-2016 Randy Heit +// Copyright 2005-2014 Simon Howard +// Copyright 2017 Christoph Oelckers +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// +// OPL IO interface. Partly built from the non-MusLib code in the old version +// plus some additions from Chocolate Doom. +// + +#include +#include +#include +#include + +#include "genmidi.h" +#include "oplio.h" +#include "opl.h" + +const double HALF_PI = (3.14159265358979323846 * 0.5); + +OPLio::~OPLio() +{ +} + +void OPLio::SetClockRate(double samples_per_tick) +{ +} + +void OPLio::WriteDelay(int ticks) +{ +} + +//---------------------------------------------------------------------------- +// +// Initialize OPL emulator +// +//---------------------------------------------------------------------------- + +int OPLio::Init(int core, uint32_t numchips, bool stereo, bool initopl3) +{ + assert(numchips >= 1 && numchips <= OPL_NUM_VOICES); + uint32_t i; + IsOPL3 = (core == 1 || core == 2 || core == 3); + + using CoreInit = OPLEmul* (*)(bool); + static CoreInit inits[] = + { + YM3812Create, + DBOPLCreate, + JavaOPLCreate, + NukedOPL3Create, + }; + + if (core < 0) core = 0; + if (core > 3) core = 3; + + memset(chips, 0, sizeof(chips)); + if (IsOPL3) + { + numchips = (numchips + 1) >> 1; + } + for (i = 0; i < numchips; ++i) + { + OPLEmul* chip = inits[core](stereo); + if (chip == NULL) + { + break; + } + chips[i] = chip; + } + NumChips = i; + NumChannels = i * (IsOPL3 ? OPL3_NUM_VOICES : OPL_NUM_VOICES); + WriteInitState(initopl3); + return i; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WriteInitState(bool initopl3) +{ + for (uint32_t k = 0; k < NumChips; ++k) + { + int chip = k << (int)IsOPL3; + if (IsOPL3 && initopl3) + { + WriteRegister(chip, OPL_REG_OPL3_ENABLE, 1); + WriteRegister(chip, OPL_REG_4OPMODE_ENABLE, 0); + } + WriteRegister(chip, OPL_REG_WAVEFORM_ENABLE, WAVEFORM_ENABLED); + WriteRegister(chip, OPL_REG_PERCUSSION_MODE, 0); // should be the default, but cannot verify for some of the cores. + } + + // Reset all channels. + for (uint32_t k = 0; k < NumChannels; k++) + { + MuteChannel(k); + WriteValue(OPL_REGS_FREQ_2, k, 0); + } +} + + +//---------------------------------------------------------------------------- +// +// Deinitialize emulator before shutdown +// +//---------------------------------------------------------------------------- + +void OPLio::Reset(void) +{ + for (auto &c : chips) + { + if (c != nullptr) + { + delete c; + c = nullptr; + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WriteRegister(int chipnum, uint32_t reg, uint8_t data) +{ + if (IsOPL3) + { + reg |= (chipnum & 1) << 8; + chipnum >>= 1; + } + if (chips[chipnum] != nullptr) + { + chips[chipnum]->WriteReg(reg, data); + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WriteValue(uint32_t regbase, uint32_t channel, uint8_t value) +{ + WriteRegister (channel / OPL_NUM_VOICES, regbase + (channel % OPL_NUM_VOICES), value); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +static const int voice_operators[OPL_NUM_VOICES] = { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 }; + +void OPLio::WriteOperator(uint32_t regbase, uint32_t channel, int index, uint8_t data2) +{ + WriteRegister(channel / OPL_NUM_VOICES, regbase + voice_operators[channel % OPL_NUM_VOICES] + 3*index, data2); +} + +//---------------------------------------------------------------------------- +// +// Write frequency/octave/keyon data to a channel +// +// [RH] This is totally different from the original MUS library code +// but matches exactly what DMX does. I haven't a clue why there are 284 +// special bytes at the beginning of the table for the first few notes. +// That last byte in the table doesn't look right, either, but that's what +// it really is. +// +//---------------------------------------------------------------------------- + +static const uint16_t frequencies[] = { // (this is the table from Chocolate Doom, which contains the same values as ZDoom's original one but is better formatted. + + 0x133, 0x133, 0x134, 0x134, 0x135, 0x136, 0x136, 0x137, // -1 + 0x137, 0x138, 0x138, 0x139, 0x139, 0x13a, 0x13b, 0x13b, + 0x13c, 0x13c, 0x13d, 0x13d, 0x13e, 0x13f, 0x13f, 0x140, + 0x140, 0x141, 0x142, 0x142, 0x143, 0x143, 0x144, 0x144, + + 0x145, 0x146, 0x146, 0x147, 0x147, 0x148, 0x149, 0x149, // -2 + 0x14a, 0x14a, 0x14b, 0x14c, 0x14c, 0x14d, 0x14d, 0x14e, + 0x14f, 0x14f, 0x150, 0x150, 0x151, 0x152, 0x152, 0x153, + 0x153, 0x154, 0x155, 0x155, 0x156, 0x157, 0x157, 0x158, + + // These are used for the first seven MIDI note values: + + 0x158, 0x159, 0x15a, 0x15a, 0x15b, 0x15b, 0x15c, 0x15d, // 0 + 0x15d, 0x15e, 0x15f, 0x15f, 0x160, 0x161, 0x161, 0x162, + 0x162, 0x163, 0x164, 0x164, 0x165, 0x166, 0x166, 0x167, + 0x168, 0x168, 0x169, 0x16a, 0x16a, 0x16b, 0x16c, 0x16c, + + 0x16d, 0x16e, 0x16e, 0x16f, 0x170, 0x170, 0x171, 0x172, // 1 + 0x172, 0x173, 0x174, 0x174, 0x175, 0x176, 0x176, 0x177, + 0x178, 0x178, 0x179, 0x17a, 0x17a, 0x17b, 0x17c, 0x17c, + 0x17d, 0x17e, 0x17e, 0x17f, 0x180, 0x181, 0x181, 0x182, + + 0x183, 0x183, 0x184, 0x185, 0x185, 0x186, 0x187, 0x188, // 2 + 0x188, 0x189, 0x18a, 0x18a, 0x18b, 0x18c, 0x18d, 0x18d, + 0x18e, 0x18f, 0x18f, 0x190, 0x191, 0x192, 0x192, 0x193, + 0x194, 0x194, 0x195, 0x196, 0x197, 0x197, 0x198, 0x199, + + 0x19a, 0x19a, 0x19b, 0x19c, 0x19d, 0x19d, 0x19e, 0x19f, // 3 + 0x1a0, 0x1a0, 0x1a1, 0x1a2, 0x1a3, 0x1a3, 0x1a4, 0x1a5, + 0x1a6, 0x1a6, 0x1a7, 0x1a8, 0x1a9, 0x1a9, 0x1aa, 0x1ab, + 0x1ac, 0x1ad, 0x1ad, 0x1ae, 0x1af, 0x1b0, 0x1b0, 0x1b1, + + 0x1b2, 0x1b3, 0x1b4, 0x1b4, 0x1b5, 0x1b6, 0x1b7, 0x1b8, // 4 + 0x1b8, 0x1b9, 0x1ba, 0x1bb, 0x1bc, 0x1bc, 0x1bd, 0x1be, + 0x1bf, 0x1c0, 0x1c0, 0x1c1, 0x1c2, 0x1c3, 0x1c4, 0x1c4, + 0x1c5, 0x1c6, 0x1c7, 0x1c8, 0x1c9, 0x1c9, 0x1ca, 0x1cb, + + 0x1cc, 0x1cd, 0x1ce, 0x1ce, 0x1cf, 0x1d0, 0x1d1, 0x1d2, // 5 + 0x1d3, 0x1d3, 0x1d4, 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d8, + 0x1d9, 0x1da, 0x1db, 0x1dc, 0x1dd, 0x1de, 0x1de, 0x1df, + 0x1e0, 0x1e1, 0x1e2, 0x1e3, 0x1e4, 0x1e5, 0x1e5, 0x1e6, + + 0x1e7, 0x1e8, 0x1e9, 0x1ea, 0x1eb, 0x1ec, 0x1ed, 0x1ed, // 6 + 0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5, + 0x1f6, 0x1f6, 0x1f7, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc, + 0x1fd, 0x1fe, 0x1ff, 0x200, 0x201, 0x201, 0x202, 0x203, + + // First note of looped range used for all octaves: + + 0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b, // 7 + 0x20c, 0x20d, 0x20e, 0x20f, 0x210, 0x210, 0x211, 0x212, + 0x213, 0x214, 0x215, 0x216, 0x217, 0x218, 0x219, 0x21a, + 0x21b, 0x21c, 0x21d, 0x21e, 0x21f, 0x220, 0x221, 0x222, + + 0x223, 0x224, 0x225, 0x226, 0x227, 0x228, 0x229, 0x22a, // 8 + 0x22b, 0x22c, 0x22d, 0x22e, 0x22f, 0x230, 0x231, 0x232, + 0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x23a, + 0x23b, 0x23c, 0x23d, 0x23e, 0x23f, 0x240, 0x241, 0x242, + + 0x244, 0x245, 0x246, 0x247, 0x248, 0x249, 0x24a, 0x24b, // 9 + 0x24c, 0x24d, 0x24e, 0x24f, 0x250, 0x251, 0x252, 0x253, + 0x254, 0x256, 0x257, 0x258, 0x259, 0x25a, 0x25b, 0x25c, + 0x25d, 0x25e, 0x25f, 0x260, 0x262, 0x263, 0x264, 0x265, + + 0x266, 0x267, 0x268, 0x269, 0x26a, 0x26c, 0x26d, 0x26e, // 10 + 0x26f, 0x270, 0x271, 0x272, 0x273, 0x275, 0x276, 0x277, + 0x278, 0x279, 0x27a, 0x27b, 0x27d, 0x27e, 0x27f, 0x280, + 0x281, 0x282, 0x284, 0x285, 0x286, 0x287, 0x288, 0x289, + + 0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, 0x292, 0x293, // 11 + 0x294, 0x295, 0x296, 0x298, 0x299, 0x29a, 0x29b, 0x29c, + 0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a2, 0x2a4, 0x2a5, 0x2a6, + 0x2a7, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ae, 0x2af, 0x2b0, + + 0x2b1, 0x2b2, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b9, 0x2ba, // 12 + 0x2bb, 0x2bd, 0x2be, 0x2bf, 0x2c0, 0x2c2, 0x2c3, 0x2c4, + 0x2c5, 0x2c7, 0x2c8, 0x2c9, 0x2cb, 0x2cc, 0x2cd, 0x2ce, + 0x2d0, 0x2d1, 0x2d2, 0x2d4, 0x2d5, 0x2d6, 0x2d8, 0x2d9, + + 0x2da, 0x2dc, 0x2dd, 0x2de, 0x2e0, 0x2e1, 0x2e2, 0x2e4, // 13 + 0x2e5, 0x2e6, 0x2e8, 0x2e9, 0x2ea, 0x2ec, 0x2ed, 0x2ee, + 0x2f0, 0x2f1, 0x2f2, 0x2f4, 0x2f5, 0x2f6, 0x2f8, 0x2f9, + 0x2fb, 0x2fc, 0x2fd, 0x2ff, 0x300, 0x302, 0x303, 0x304, + + 0x306, 0x307, 0x309, 0x30a, 0x30b, 0x30d, 0x30e, 0x310, // 14 + 0x311, 0x312, 0x314, 0x315, 0x317, 0x318, 0x31a, 0x31b, + 0x31c, 0x31e, 0x31f, 0x321, 0x322, 0x324, 0x325, 0x327, + 0x328, 0x329, 0x32b, 0x32c, 0x32e, 0x32f, 0x331, 0x332, + + 0x334, 0x335, 0x337, 0x338, 0x33a, 0x33b, 0x33d, 0x33e, // 15 + 0x340, 0x341, 0x343, 0x344, 0x346, 0x347, 0x349, 0x34a, + 0x34c, 0x34d, 0x34f, 0x350, 0x352, 0x353, 0x355, 0x357, + 0x358, 0x35a, 0x35b, 0x35d, 0x35e, 0x360, 0x361, 0x363, + + 0x365, 0x366, 0x368, 0x369, 0x36b, 0x36c, 0x36e, 0x370, // 16 + 0x371, 0x373, 0x374, 0x376, 0x378, 0x379, 0x37b, 0x37c, + 0x37e, 0x380, 0x381, 0x383, 0x384, 0x386, 0x388, 0x389, + 0x38b, 0x38d, 0x38e, 0x390, 0x392, 0x393, 0x395, 0x397, + + 0x398, 0x39a, 0x39c, 0x39d, 0x39f, 0x3a1, 0x3a2, 0x3a4, // 17 + 0x3a6, 0x3a7, 0x3a9, 0x3ab, 0x3ac, 0x3ae, 0x3b0, 0x3b1, + 0x3b3, 0x3b5, 0x3b7, 0x3b8, 0x3ba, 0x3bc, 0x3bd, 0x3bf, + 0x3c1, 0x3c3, 0x3c4, 0x3c6, 0x3c8, 0x3ca, 0x3cb, 0x3cd, + + // The last note has an incomplete range, and loops round back to + // the start. Note that the last value is actually a buffer overrun + // and does not fit with the other values. + + 0x3cf, 0x3d1, 0x3d2, 0x3d4, 0x3d6, 0x3d8, 0x3da, 0x3db, // 18 + 0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e4, 0x3e6, 0x3e8, 0x3ea, + 0x3ec, 0x3ed, 0x3ef, 0x3f1, 0x3f3, 0x3f5, 0x3f6, 0x3f8, + 0x3fa, 0x3fc, 0x3fe, 0x36c, +}; + +void OPLio::WriteFrequency(uint32_t channel, uint32_t note, uint32_t pitch, uint32_t keyon) +{ + int octave = 0; + int j = (note << 5) + pitch; + + if (j < 0) + { + j = 0; + } + else if (j >= 284) + { + j -= 284; + octave = j / (32*12); + if (octave > 7) + { + octave = 7; + } + j = (j % (32*12)) + 284; + } + int i = frequencies[j] | (octave << 10); + + WriteValue (OPL_REGS_FREQ_1, channel, (uint8_t)i); + WriteValue (OPL_REGS_FREQ_2, channel, (uint8_t)(i>>8)|(keyon<<5)); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +static uint8_t volumetable[128] = { + 0, 1, 3, 5, 6, 8, 10, 11, + 13, 14, 16, 17, 19, 20, 22, 23, + 25, 26, 27, 29, 30, 32, 33, 34, + 36, 37, 39, 41, 43, 45, 47, 49, + 50, 52, 54, 55, 57, 59, 60, 61, + 63, 64, 66, 67, 68, 69, 71, 72, + 73, 74, 75, 76, 77, 79, 80, 81, + 82, 83, 84, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 92, 93, 94, 95, + 96, 96, 97, 98, 99, 99, 100, 101, + 101, 102, 103, 103, 104, 105, 105, 106, + 107, 107, 108, 109, 109, 110, 110, 111, + 112, 112, 113, 113, 114, 114, 115, 115, + 116, 117, 117, 118, 118, 119, 119, 120, + 120, 121, 121, 122, 122, 123, 123, 123, + 124, 124, 125, 125, 126, 126, 127, 127}; + +void OPLio::WriteVolume(uint32_t channel, struct GenMidiVoice *voice, uint32_t vol1, uint32_t vol2, uint32_t vol3) +{ + if (voice != nullptr) + { + uint32_t full_volume = volumetable[std::min(127, (uint32_t)((uint64_t)vol1*vol2*vol3) / (127 * 127))]; + int reg_volume2 = ((0x3f - voice->carrier.level) * full_volume) / 128; + reg_volume2 = (0x3f - reg_volume2) | voice->carrier.scale; + WriteOperator(OPL_REGS_LEVEL, channel, 1, reg_volume2); + + int reg_volume1; + if (voice->feedback & 0x01) + { + // Chocolate Doom says: + // If we are using non-modulated feedback mode, we must set the + // volume for both voices. + // Note that the same register volume value is written for + // both voices, always calculated from the carrier's level + // value. + + // But Muslib does it differently than the comment above states. Which one is correct? + + reg_volume1 = ((0x3f - voice->modulator.level) * full_volume) / 128; + reg_volume1 = (0x3f - reg_volume1) | voice->modulator.scale; + } + else + { + reg_volume1 = voice->modulator.level | voice->modulator.scale; + } + WriteOperator(OPL_REGS_LEVEL, channel, 0,reg_volume1); + } +} + + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WritePan(uint32_t channel, struct GenMidiVoice *voice, int pan) +{ + if (voice != 0) + { + WriteValue(OPL_REGS_FEEDBACK, channel, voice->feedback | (pan >= 28 ? 0x20 : 0) | (pan <= 100 ? 0x10 : 0)); + + // Set real panning if we're using emulated chips. + int chanper = IsOPL3 ? OPL3_NUM_VOICES : OPL_NUM_VOICES; + int which = channel / chanper; + if (chips[which] != NULL) + { + // This is the MIDI-recommended pan formula. 0 and 1 are + // both hard left so that 64 can be perfectly center. + double level = (pan <= 1) ? 0 : (pan - 1) / 126.0; + chips[which]->SetPanning(channel % chanper, + (float)cos(HALF_PI * level), (float)sin(HALF_PI * level)); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WriteTremolo(uint32_t channel, struct GenMidiVoice *voice, bool vibrato) +{ + int val1 = voice->modulator.tremolo, val2 = voice->carrier.tremolo; + if (vibrato) + { + if (voice->feedback & 1) val1 |= 0x40; + val2 |= 0x40; + } + WriteOperator(OPL_REGS_TREMOLO, channel, 1, val2); + WriteOperator(OPL_REGS_TREMOLO, channel, 0, val1); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::MuteChannel(uint32_t channel) +{ + WriteOperator(OPL_REGS_LEVEL, channel, 1, NO_VOLUME); + WriteOperator(OPL_REGS_ATTACK, channel, 1, MAX_ATTACK_DECAY); + WriteOperator(OPL_REGS_SUSTAIN, channel, 1, NO_SUSTAIN_MAX_RELEASE); + + WriteOperator(OPL_REGS_LEVEL, channel, 0, NO_VOLUME); + WriteOperator(OPL_REGS_ATTACK, channel, 0, MAX_ATTACK_DECAY); + WriteOperator(OPL_REGS_SUSTAIN, channel, 0, NO_SUSTAIN_MAX_RELEASE); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::LoadOperatorData(uint32_t channel, int op_index, genmidi_op_t *data, bool max_level, bool vibrato) +{ + // The scale and level fields must be combined for the level register. + // For the carrier wave we always set the maximum level. + + int level = data->scale; + if (max_level) level |= 0x3f; + else level |= data->level; + + int tremolo = data->tremolo; + if (vibrato) tremolo |= 0x40; + + WriteOperator(OPL_REGS_LEVEL, channel, op_index, level); + WriteOperator(OPL_REGS_TREMOLO, channel, op_index, tremolo); + WriteOperator(OPL_REGS_ATTACK, channel, op_index, data->attack); + WriteOperator(OPL_REGS_SUSTAIN, channel, op_index, data->sustain); + WriteOperator(OPL_REGS_WAVEFORM, channel, op_index, data->waveform); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WriteInstrument(uint32_t channel, struct GenMidiVoice *voice, bool vibrato) +{ + bool modulating = (voice->feedback & 0x01) == 0; + + // Doom loads the second operator first, then the first. + // The carrier is set to minimum volume until the voice volume + // is set later. If we are not using modulating mode, we must set both to minimum volume. + + LoadOperatorData(channel, 1, &voice->carrier, true, vibrato); + LoadOperatorData(channel, 0, &voice->modulator, !modulating, vibrato && modulating); + + // The feedback register is written by the calling code. +} diff --git a/thirdparty/oplsynth/oplsynth/genmidi.h b/thirdparty/oplsynth/oplsynth/genmidi.h new file mode 100644 index 000000000..6d8cd68a9 --- /dev/null +++ b/thirdparty/oplsynth/oplsynth/genmidi.h @@ -0,0 +1,60 @@ +#pragma once +#include + +#if defined(__GNUC__) +// With versions of GCC newer than 4.2, it appears it was determined that the +// cost of an unaligned pointer on PPC was high enough to add padding to the +// end of packed structs. For whatever reason __packed__ and pragma pack are +// handled differently in this regard. Note that this only needs to be applied +// to types which are used in arrays or sizeof is needed. This also prevents +// code from taking references to the struct members. +#define FORCE_PACKED __attribute__((__packed__)) +#else +#define FORCE_PACKED +#endif + +#pragma pack(push, 1) +struct genmidi_op_t +{ + uint8_t tremolo; + uint8_t attack; + uint8_t sustain; + uint8_t waveform; + uint8_t scale; + uint8_t level; +} FORCE_PACKED; + +struct GenMidiVoice +{ + genmidi_op_t modulator; + uint8_t feedback; + genmidi_op_t carrier; + uint8_t unused; + int16_t base_note_offset; +} FORCE_PACKED; + + +struct GenMidiInstrument +{ + uint16_t flags; + uint8_t fine_tuning; + uint8_t fixed_note; + GenMidiVoice voices[2]; +} FORCE_PACKED; + +#pragma pack(pop) + +enum +{ + GENMIDI_FLAG_FIXED = 0x0001, /* fixed pitch */ + GENMIDI_FLAG_2VOICE = 0x0004 /* double voice (OPL3) */ +}; + +enum +{ + GENMIDI_NUM_INSTRS = 128, + GENMIDI_FIST_PERCUSSION = 35, + GENMIDI_NUM_PERCUSSION = 47, + GENMIDI_NUM_TOTAL = GENMIDI_NUM_INSTRS + GENMIDI_NUM_PERCUSSION +}; + diff --git a/thirdparty/oplsynth/oplsynth/musicblock.h b/thirdparty/oplsynth/oplsynth/musicblock.h new file mode 100644 index 000000000..cc2612b8b --- /dev/null +++ b/thirdparty/oplsynth/oplsynth/musicblock.h @@ -0,0 +1,66 @@ +#pragma once +#include +#include "genmidi.h" +#include "oplio.h" + + +struct OPLVoice +{ + unsigned int index; // Index of this voice, or -1 if not in use. + unsigned int key; // The midi key that this voice is playing. + unsigned int note; // The note being played. This is normally the same as the key, but if the instrument is a fixed pitch instrument, it is different. + unsigned int note_volume; // The volume of the note being played on this channel. + GenMidiInstrument *current_instr; // Currently-loaded instrument data + GenMidiVoice *current_instr_voice;// The voice number in the instrument to use. This is normally set to the instrument's first voice; if this is a double voice instrument, it may be the second one + bool sustained; + int8_t fine_tuning; + int pitch; + uint32_t timestamp; +}; + +struct musicBlock { + musicBlock(); + ~musicBlock(); + + OPLChannel oplchannels[NUM_CHANNELS]; + OPLio *io; + uint32_t timeCounter; + + struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL]; + + void changeModulation(uint32_t id, int value); + void changeSustain(uint32_t id, int value); + void changeVolume(uint32_t id, int value, bool expression); + void changePanning(uint32_t id, int value); + void notesOff(uint32_t id, int value); + void allNotesOff(uint32_t id, int value); + void changeExtended(uint32_t channel, uint8_t controller, int value); + void resetControllers(uint32_t channel, int vol); + void programChange(uint32_t channel, int value); + void resetAllControllers(int vol); + void changePitch(uint32_t channel, int val1, int val2); + + void noteOn(uint32_t channel, uint8_t note, int volume); + void noteOff(uint32_t channel, uint8_t note); + void stopAllVoices(); + +protected: + OPLVoice voices[NUM_VOICES]; + + int findFreeVoice(); + int replaceExistingVoice(); + void voiceKeyOn(uint32_t slot, uint32_t channo, GenMidiInstrument *instrument, uint32_t instrument_voice, uint32_t, uint32_t volume); + int releaseVoice(uint32_t slot, uint32_t killed); + + friend class Stat_opl; + +}; + +enum ExtCtrl { + ctrlRPNHi, + ctrlRPNLo, + ctrlNRPNHi, + ctrlNRPNLo, + ctrlDataEntryHi, + ctrlDataEntryLo, +}; diff --git a/thirdparty/oplsynth/oplsynth/nukedopl3.h b/thirdparty/oplsynth/oplsynth/nukedopl3.h new file mode 100644 index 000000000..515f2dc08 --- /dev/null +++ b/thirdparty/oplsynth/oplsynth/nukedopl3.h @@ -0,0 +1,241 @@ +/* +* Copyright (C) 2013-2015 Nuke.YKT(Alexey Khokholov) +* +* This library 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 library 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 library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Nuked Yamaha YMF262(aka OPL3) emulator. + Thanks: + MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + Feedback and Rhythm part calculation information. + forums.submarine.org.uk(carbon14, opl3): + Tremolo and phase generator calculation information. + OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + OPL2 ROMs. +*/ + +//version 1.6 + +#include "opl.h" +#include "musicblock.h" + +namespace NukedOPL3 +{ + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +// Channel types + +enum { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2, + ch_drum = 3 +}; + +// Envelope key types + +enum { + egk_norm = 0x01, + egk_drum = 0x02 +}; + + +// +// logsin table +// + +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +// +// exp table +// + +static const Bit16u exprom[256] = { + 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, + 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, + 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, + 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, + 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, + 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, + 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, + 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, + 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, + 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, + 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, + 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, + 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, + 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa +}; + +// +// freq mult table multiplied by 2 +// +// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 +// + +static const Bit8u mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 }; + +// +// ksl table +// + +static const Bit8u kslrom[16] = { 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 }; + +static const Bit8u kslshift[4] = { 8, 1, 2, 0 }; + +// +// LFO vibrato +// + +static const Bit8u vib_table[8] = { 3, 1, 0, 1, 3, 1, 0, 1 }; +static const Bit8s vibsgn_table[8] = { 1, 1, 1, 1, -1, -1, -1, -1 }; + +// +// envelope generator constants +// + +static const Bit8u eg_incstep[3][4][8] = { + { { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } }, + { { 0, 1, 0, 1, 0, 1, 0, 1 }, { 0, 1, 0, 1, 1, 1, 0, 1 }, { 0, 1, 1, 1, 0, 1, 1, 1 }, { 0, 1, 1, 1, 1, 1, 1, 1 } }, + { { 1, 1, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 2, 2, 1, 1 }, { 2, 2, 2, 2, 2, 2, 1, 1 } } +}; + +static const Bit8u eg_incdesc[16] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 +}; + +static const Bit8s eg_incsh[16] = { + 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2 +}; + +// +// address decoding +// + +static const Bit8s ad_slot[0x20] = { 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +static const Bit8u ch_slot[18] = { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 }; + +struct opl_chip; +struct opl_slot; +struct opl_channel; + +struct opl_slot { + opl_channel *channel; + opl_chip *chip; + Bit16s out; + Bit16s fbmod; + Bit16s *mod; + Bit16s prout[2]; + Bit16s eg_rout; + Bit16s eg_out; + Bit8u eg_inc; + Bit8u eg_gen; + Bit8u eg_rate; + Bit8u eg_ksl; + Bit8u *trem; + Bit8u reg_vib; + Bit8u reg_type; + Bit8u reg_ksr; + Bit8u reg_mult; + Bit8u reg_ksl; + Bit8u reg_tl; + Bit8u reg_ar; + Bit8u reg_dr; + Bit8u reg_sl; + Bit8u reg_rr; + Bit8u reg_wf; + Bit8u key; + Bit32u pg_phase; +}; + +struct opl_channel { + opl_slot *slots[2]; + opl_channel *pair; + opl_chip *chip; + Bit16s *out[4]; + Bit8u chtype; + Bit16u f_num; + Bit8u block; + Bit8u fb; + Bit8u con; + Bit8u alg; + Bit8u ksv; + Bit16u cha, chb; + float fcha, fchb; +}; + +struct opl_chip { + opl_channel channel[18]; + opl_slot slot[36]; + Bit16u timer; + Bit8u newm; + Bit8u nts; + Bit8u dvb; + Bit8u dam; + Bit8u rhy; + Bit8u vibpos; + Bit8u tremval; + Bit8u tremtval; + Bit8u tremdir; + Bit32u noise; + Bit16s zeromod; + Bit32s mixbuff[2]; + Bit8u FullPan; +}; + + +class NukedOPL3 : public OPLEmul { +private: + opl_chip opl3; + bool FullPan; +public: + void Reset(); + void Update(float* sndptr, int numsamples); + void UpdateS(short *buffer, int length); + void WriteReg(int reg, int v); + void SetPanning(int c, float left, float right); + + NukedOPL3(bool stereo); +}; + +} // namespace NukedOPL3 diff --git a/thirdparty/oplsynth/oplsynth/o_swap.h b/thirdparty/oplsynth/oplsynth/o_swap.h new file mode 100644 index 000000000..de9b7780a --- /dev/null +++ b/thirdparty/oplsynth/oplsynth/o_swap.h @@ -0,0 +1,255 @@ +// +// DESCRIPTION: +// Endianess handling, swapping 16bit and 32bit. +// +//----------------------------------------------------------------------------- + + +#ifndef __M_SWAP_H__ +#define __M_SWAP_H__ + +#include + +// Endianess handling. +// WAD files are stored little endian. + +#ifdef __APPLE__ +#include + +inline short LittleShort(short x) +{ + return (short)OSSwapLittleToHostInt16((uint16_t)x); +} + +inline unsigned short LittleShort(unsigned short x) +{ + return OSSwapLittleToHostInt16(x); +} + +inline short LittleShort(int x) +{ + return OSSwapLittleToHostInt16((uint16_t)x); +} + +inline unsigned short LittleShort(unsigned int x) +{ + return OSSwapLittleToHostInt16((uint16_t)x); +} + +inline int LittleLong(int x) +{ + return OSSwapLittleToHostInt32((uint32_t)x); +} + +inline unsigned int LittleLong(unsigned int x) +{ + return OSSwapLittleToHostInt32(x); +} + +inline short BigShort(short x) +{ + return (short)OSSwapBigToHostInt16((uint16_t)x); +} + +inline unsigned short BigShort(unsigned short x) +{ + return OSSwapBigToHostInt16(x); +} + +inline int BigLong(int x) +{ + return OSSwapBigToHostInt32((uint32_t)x); +} + +inline unsigned int BigLong(unsigned int x) +{ + return OSSwapBigToHostInt32(x); +} + +#elif defined __BIG_ENDIAN__ + +// Swap 16bit, that is, MSB and LSB byte. +// No masking with 0xFF should be necessary. +inline short LittleShort (short x) +{ + return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8)); +} + +inline unsigned short LittleShort (unsigned short x) +{ + return (unsigned short)((x>>8) | (x<<8)); +} + +inline short LittleShort (int x) +{ + return LittleShort((short)x); +} + +inline unsigned short LittleShort (unsigned int x) +{ + return LittleShort((unsigned short)x); +} + +// Swapping 32bit. +inline unsigned int LittleLong (unsigned int x) +{ + return (unsigned int)( + (x>>24) + | ((x>>8) & 0xff00) + | ((x<<8) & 0xff0000) + | (x<<24)); +} + +inline int LittleLong (int x) +{ + return (int)( + (((unsigned int)x)>>24) + | ((((unsigned int)x)>>8) & 0xff00) + | ((((unsigned int)x)<<8) & 0xff0000) + | (((unsigned int)x)<<24)); +} + +inline short BigShort(short x) +{ + return x; +} + +inline unsigned short BigShort(unsigned short x) +{ + return x; +} + +inline unsigned int BigLong(unsigned int x) +{ + return x; +} + +inline int BigLong(int x) +{ + return x; +} + +#else + +inline short LittleShort(short x) +{ + return x; +} + +inline unsigned short LittleShort(unsigned short x) +{ + return x; +} + +inline unsigned int LittleLong(unsigned int x) +{ + return x; +} + +inline int LittleLong(int x) +{ + return x; +} + +#ifdef _MSC_VER + +inline short BigShort(short x) +{ + return (short)_byteswap_ushort((unsigned short)x); +} + +inline unsigned short BigShort(unsigned short x) +{ + return _byteswap_ushort(x); +} + +inline int BigLong(int x) +{ + return (int)_byteswap_ulong((unsigned long)x); +} + +inline unsigned int BigLong(unsigned int x) +{ + return (unsigned int)_byteswap_ulong((unsigned long)x); +} +#pragma warning (default: 4035) + +#else + +inline short BigShort (short x) +{ + return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8)); +} + +inline unsigned short BigShort (unsigned short x) +{ + return (unsigned short)((x>>8) | (x<<8)); +} + +inline unsigned int BigLong (unsigned int x) +{ + return (unsigned int)( + (x>>24) + | ((x>>8) & 0xff00) + | ((x<<8) & 0xff0000) + | (x<<24)); +} + +inline int BigLong (int x) +{ + return (int)( + (((unsigned int)x)>>24) + | ((((unsigned int)x)>>8) & 0xff00) + | ((((unsigned int)x)<<8) & 0xff0000) + | (((unsigned int)x)<<24)); +} + +#endif + +#endif // __BIG_ENDIAN__ + +// These may be destructive so they should create errors +unsigned long BigLong(unsigned long) = delete; +long BigLong(long) = delete; +unsigned long LittleLong(unsigned long) = delete; +long LittleLong(long) = delete; + + +// Data accessors, since some data is highly likely to be unaligned. +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) +inline int GetShort(const unsigned char *foo) +{ + return *(const short *)foo; +} +inline int GetInt(const unsigned char *foo) +{ + return *(const int *)foo; +} +#else +inline int GetShort(const unsigned char *foo) +{ + return short(foo[0] | (foo[1] << 8)); +} +inline int GetInt(const unsigned char *foo) +{ + return int(foo[0] | (foo[1] << 8) | (foo[2] << 16) | (foo[3] << 24)); +} +#endif +inline int GetBigInt(const unsigned char *foo) +{ + return int((foo[0] << 24) | (foo[1] << 16) | (foo[2] << 8) | foo[3]); +} + +#ifdef __BIG_ENDIAN__ +inline int GetNativeInt(const unsigned char *foo) +{ + return GetBigInt(foo); +} +#else +inline int GetNativeInt(const unsigned char *foo) +{ + return GetInt(foo); +} +#endif + +#endif // __M_SWAP_H__ diff --git a/thirdparty/oplsynth/oplsynth/opl.h b/thirdparty/oplsynth/oplsynth/opl.h new file mode 100644 index 000000000..7c89965ac --- /dev/null +++ b/thirdparty/oplsynth/oplsynth/opl.h @@ -0,0 +1,30 @@ +#ifndef OPL_H +#define OPL_H + +// Abstract base class for OPL emulators + +class OPLEmul +{ +public: + OPLEmul() {} + virtual ~OPLEmul() {} + + virtual void Reset() = 0; + virtual void WriteReg(int reg, int v) = 0; + virtual void Update(float *buffer, int length) = 0; + virtual void UpdateS(short *buffer, int length) = 0; + virtual void SetPanning(int c, float left, float right) = 0; +}; + +OPLEmul *YM3812Create(bool stereo); +OPLEmul *DBOPLCreate(bool stereo); +OPLEmul *JavaOPLCreate(bool stereo); +OPLEmul *NukedOPL3Create(bool stereo); + +#define OPL_SAMPLE_RATE 49716.0 +#define CENTER_PANNING_POWER 0.70710678118 /* [RH] volume at center for EQP */ +#define ADLIB_CLOCK_MUL 24.0 + + + +#endif diff --git a/thirdparty/oplsynth/oplsynth/opl3_Float.h b/thirdparty/oplsynth/oplsynth/opl3_Float.h new file mode 100644 index 000000000..6c676ee3a --- /dev/null +++ b/thirdparty/oplsynth/oplsynth/opl3_Float.h @@ -0,0 +1,238 @@ +// ==================================================================================================================== +// ==================================================================================================================== +// xs_Float.h +// +// Source: "Know Your FPU: Fixing Floating Fast" +// http://www.stereopsis.com/sree/fpu2006.html +// +// xs_CRoundToInt: Round toward nearest, but ties round toward even (just like FISTP) +// xs_ToInt: Round toward zero, just like the C (int) cast +// xs_FloorToInt: Round down +// xs_CeilToInt: Round up +// xs_RoundToInt: Round toward nearest, but ties round up +// ==================================================================================================================== +// ==================================================================================================================== +#ifndef _xs_FLOAT_H_ +#define _xs_FLOAT_H_ + +#include + +// ==================================================================================================================== +// Defines +// ==================================================================================================================== +#ifndef _xs_DEFAULT_CONVERSION +#define _xs_DEFAULT_CONVERSION 0 +#endif //_xs_DEFAULT_CONVERSION + + +#if __BIG_ENDIAN__ + #define _xs_iexp_ 0 + #define _xs_iman_ 1 +#else + #define _xs_iexp_ 1 //intel is little endian + #define _xs_iman_ 0 +#endif //BigEndian_ + +#ifdef __GNUC__ +#define finline inline +#else +#define finline __forceinline +#endif + +typedef double real64; + + +union _xs_doubleints +{ + real64 val; + uint32_t ival[2]; +}; + +#if 0 +#define _xs_doublecopysgn(a,b) ((int32_t*)&a)[_xs_iexp_]&=~(((int32_t*)&b)[_xs_iexp_]&0x80000000) +#define _xs_doubleisnegative(a) ((((int32_t*)&a)[_xs_iexp_])|0x80000000) +#endif + +// ==================================================================================================================== +// Constants +// ==================================================================================================================== +const real64 _xs_doublemagic = real64 (6755399441055744.0); //2^52 * 1.5, uses limited precisicion to floor +const real64 _xs_doublemagicdelta = (1.5e-8); //almost .5f = .5f + 1e^(number of exp bit) +const real64 _xs_doublemagicroundeps = (.5f-_xs_doublemagicdelta); //almost .5f = .5f - 1e^(number of exp bit) + + +// ==================================================================================================================== +// Prototypes +// ==================================================================================================================== +static int32_t xs_CRoundToInt (real64 val, real64 dmr = _xs_doublemagic); +static int32_t xs_ToInt (real64 val, real64 dme = -_xs_doublemagicroundeps); +static int32_t xs_FloorToInt (real64 val, real64 dme = _xs_doublemagicroundeps); +static int32_t xs_CeilToInt (real64 val, real64 dme = _xs_doublemagicroundeps); +static int32_t xs_RoundToInt (real64 val); + +//int32_t versions +finline static int32_t xs_CRoundToInt (int32_t val) {return val;} +finline static int32_t xs_ToInt (int32_t val) {return val;} + + + +// ==================================================================================================================== +// Fix Class +// ==================================================================================================================== +template class xs_Fix +{ +public: + typedef int32_t Fix; + + // ==================================================================================================================== + // Basic Conversion from Numbers + // ==================================================================================================================== + finline static Fix ToFix (int32_t val) {return val<>N;} + + + +protected: + // ==================================================================================================================== + // Helper function - mainly to preserve _xs_DEFAULT_CONVERSION + // ==================================================================================================================== + finline static int32_t xs_ConvertToFixed (real64 val) + { + #if _xs_DEFAULT_CONVERSION==0 + return xs_CRoundToInt(val, _xs_doublemagic/(1<= 1400 + // VC++ 2005's standard cast is a little bit faster than this + // magic number code. (Which is pretty amazing!) SSE has the + // fastest C-style float->int conversion, but unfortunately, + // checking for SSE support every time you need to do a + // conversion completely negates its performance advantage. + return int32_t(val); +#else +#if _xs_DEFAULT_CONVERSION==0 + return (val<0) ? xs_CRoundToInt(val-dme) : + xs_CRoundToInt(val+dme); +#else + return int32_t(val); +#endif +#endif +} + + +// ==================================================================================================================== +finline static int32_t xs_FloorToInt(real64 val, real64 dme) +{ +#if _xs_DEFAULT_CONVERSION==0 + return xs_CRoundToInt (val - dme); +#else + return floor(val); +#endif +} + + +// ==================================================================================================================== +finline static int32_t xs_CeilToInt(real64 val, real64 dme) +{ +#if _xs_DEFAULT_CONVERSION==0 + return xs_CRoundToInt (val + dme); +#else + return ceil(val); +#endif +} + + +// ==================================================================================================================== +finline static int32_t xs_RoundToInt(real64 val) +{ +#if _xs_DEFAULT_CONVERSION==0 + // Yes, it is important that two fadds be generated, so you cannot override the dmr + // passed to xs_CRoundToInt with _xs_doublemagic + _xs_doublemagicdelta. If you do, + // you'll end up with Banker's Rounding again. + return xs_CRoundToInt (val + _xs_doublemagicdelta); +#else + return floor(val+.5); +#endif +} + + + +// ==================================================================================================================== +// ==================================================================================================================== +// Unsigned variants +// ==================================================================================================================== +// ==================================================================================================================== +finline static uint32_t xs_CRoundToUInt(real64 val) +{ + return (uint32_t)xs_CRoundToInt(val); +} + +finline static uint32_t xs_FloorToUInt(real64 val) +{ + return (uint32_t)xs_FloorToInt(val); +} + +finline static uint32_t xs_CeilToUInt(real64 val) +{ + return (uint32_t)xs_CeilToInt(val); +} + +finline static uint32_t xs_RoundToUInt(real64 val) +{ + return (uint32_t)xs_RoundToInt(val); +} + + + +// ==================================================================================================================== +// ==================================================================================================================== +#endif // _xs_FLOAT_H_ \ No newline at end of file diff --git a/thirdparty/oplsynth/oplsynth/opl_mus_player.h b/thirdparty/oplsynth/oplsynth/opl_mus_player.h new file mode 100644 index 000000000..bc0620f88 --- /dev/null +++ b/thirdparty/oplsynth/oplsynth/opl_mus_player.h @@ -0,0 +1,51 @@ +#pragma once +#include +#include +#include +#include "musicblock.h" + +class OPLmusicBlock : public musicBlock +{ +public: + OPLmusicBlock(int core, int numchips); + virtual ~OPLmusicBlock(); + + bool ServiceStream(void *buff, int numbytes); + void ResetChips(int numchips); + + virtual void Restart(); + +protected: + virtual int PlayTick() = 0; + void OffsetSamples(float *buff, int count); + + uint8_t *score; + uint8_t *scoredata; + double NextTickIn; + double SamplesPerTick; + double LastOffset; + int playingcount; + int NumChips; + int currentCore; + bool Looping; + bool FullPan; +}; + +class OPLmusicFile : public OPLmusicBlock +{ +public: + OPLmusicFile(const void *data, size_t length, int core, int numchips, const char *&errormessage); + virtual ~OPLmusicFile(); + + bool IsValid() const; + void SetLooping(bool loop); + void Restart(); + +protected: + OPLmusicFile(int core, int numchips) : OPLmusicBlock(core, numchips) {} + int PlayTick(); + + enum { RDosPlay, IMF, DosBox1, DosBox2 } RawPlayer; + int ScoreLen; + int WhichChip; +}; diff --git a/thirdparty/oplsynth/oplsynth/oplio.h b/thirdparty/oplsynth/oplsynth/oplio.h new file mode 100644 index 000000000..92be980cb --- /dev/null +++ b/thirdparty/oplsynth/oplsynth/oplio.h @@ -0,0 +1,99 @@ +#pragma once + + +enum +{ + // Operator registers (21 of each): + OPL_REGS_TREMOLO = 0x20, + OPL_REGS_LEVEL = 0x40, + OPL_REGS_ATTACK = 0x60, + OPL_REGS_SUSTAIN = 0x80, + OPL_REGS_WAVEFORM = 0xE0, + + // Voice registers (9 of each): + OPL_REGS_FREQ_1 = 0xA0, + OPL_REGS_FREQ_2 = 0xB0, + OPL_REGS_FEEDBACK = 0xC0, +}; + +enum +{ + OPL_REG_WAVEFORM_ENABLE = 0x01, + OPL_REG_TIMER1 = 0x02, + OPL_REG_TIMER2 = 0x03, + OPL_REG_TIMER_CTRL = 0x04, + OPL_REG_FM_MODE = 0x08, + OPL_REG_PERCUSSION_MODE = 0xBD, + + OPL_REG_OPL3_ENABLE = 0x105, + OPL_REG_4OPMODE_ENABLE = 0x104, + +}; + +enum +{ + NO_VOLUME = 0x3f, + MAX_ATTACK_DECAY = 0xff, + NO_SUSTAIN_MAX_RELEASE = 0xf, + WAVEFORM_ENABLED = 0x20 +}; + +enum +{ + OPL_NUM_VOICES = 9, + OPL3_NUM_VOICES = 18, + MAXOPL2CHIPS = 8, + NUM_VOICES = (OPL_NUM_VOICES * MAXOPL2CHIPS), + + NUM_CHANNELS = 16, + CHAN_PERCUSSION = 15, + + VIBRATO_THRESHOLD = 40, + MIN_SUSTAIN = 0x40, + HIGHEST_NOTE = 127, + +}; + +struct GenMidiVoice; +struct genmidi_op_t; + +struct OPLio +{ + virtual ~OPLio(); + + void WriteOperator(uint32_t regbase, uint32_t channel, int index, uint8_t data2); + void LoadOperatorData(uint32_t channel, int op_index, genmidi_op_t *op_data, bool maxlevel, bool vibrato); + void WriteValue(uint32_t regbase, uint32_t channel, uint8_t value); + void WriteFrequency(uint32_t channel, uint32_t freq, uint32_t octave, uint32_t keyon); + void WriteVolume(uint32_t channel, GenMidiVoice *voice, uint32_t v1, uint32_t v2, uint32_t v3); + void WritePan(uint32_t channel, GenMidiVoice *voice, int pan); + void WriteTremolo(uint32_t channel, GenMidiVoice *voice, bool vibrato); + void WriteInstrument(uint32_t channel, GenMidiVoice *voice, bool vibrato); + void WriteInitState(bool opl3); + void MuteChannel(uint32_t chan); + void StopPlayback(); + + virtual int Init(int core, uint32_t numchips, bool stereo, bool initopl3); + virtual void Reset(); + virtual void WriteRegister(int which, uint32_t reg, uint8_t data); + virtual void SetClockRate(double samples_per_tick); + virtual void WriteDelay(int ticks); + + class OPLEmul *chips[OPL_NUM_VOICES]; + uint32_t NumChannels; + uint32_t NumChips; + bool IsOPL3; +}; + +struct OPLChannel +{ + uint32_t Instrument; + uint8_t Volume; + uint8_t Panning; + int8_t Pitch; + uint8_t Sustain; + bool Vibrato; + uint8_t Expression; + uint16_t PitchSensitivity; + uint16_t RPN; +}; diff --git a/thirdparty/opnmidi/CMakeLists.txt b/thirdparty/opnmidi/CMakeLists.txt new file mode 100644 index 000000000..81ffdc79e --- /dev/null +++ b/thirdparty/opnmidi/CMakeLists.txt @@ -0,0 +1,59 @@ +make_release_only() + +add_library(opn OBJECT) + +target_sources(opn +PRIVATE + opnmidi_load.cpp + opnmidi_private.cpp + opnmidi.cpp + opnmidi_midiplay.cpp + opnmidi_opn2.cpp + chips/np2/fmgen_fmgen.cpp + chips/np2/fmgen_opna.cpp + chips/np2/fmgen_fmtimer.cpp + chips/np2/fmgen_file.cpp + chips/np2/fmgen_psg.cpp + chips/mame_opn2.cpp + chips/gens_opn2.cpp + chips/mame_opna.cpp + chips/np2_opna.cpp + chips/mamefm/ymdeltat.cpp + chips/mamefm/resampler.cpp + chips/mamefm/fm.cpp + chips/nuked_opn2.cpp + chips/gens/Ym2612.cpp + chips/nuked/ym3438.c + chips/mamefm/emu2149.c + chips/mame/mame_ym2612fm.c + wopn/wopn_file.c +) + +if(COMPILER_SUPPORTS_CXX14) + set(YMFM_SOURCES + chips/ymfm_opn2.cpp + chips/ymfm_opna.cpp + chips/ymfm/ymfm_opn.cpp +# Duplicates at the libADLMIDI +# chips/ymfm/ymfm_misc.cpp +# chips/ymfm/ymfm_pcm.cpp +# chips/ymfm/ymfm_adpcm.cpp +# chips/ymfm/ymfm_ssg.cpp + ) + if(DEFINED FLAG_CPP14) + set_source_files_properties(${YMFM_SOURCES} COMPILE_FLAGS ${FLAG_CPP14}) + endif() + target_sources(opn PRIVATE ${YMFM_SOURCES}) +else() + target_compile_definitions(opn PUBLIC -DOPNMIDI_DISABLE_YMFM_EMULATOR) +endif() + +target_compile_definitions(opn +PRIVATE + # we play with out own sequencer + OPNMIDI_DISABLE_MIDI_SEQUENCER +) + +target_include_directories(opn PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") + +use_fast_math(opn) diff --git a/thirdparty/opnmidi/chips/chipset.cmake b/thirdparty/opnmidi/chips/chipset.cmake new file mode 100644 index 000000000..b124cec97 --- /dev/null +++ b/thirdparty/opnmidi/chips/chipset.cmake @@ -0,0 +1,40 @@ +set(CHIPS_SOURCES + "src/opl/chips/gens/Ym2612.cpp" + "src/opl/chips/mamefm/fm.cpp" + "src/opl/chips/mamefm/resampler.cpp" + "src/opl/chips/mamefm/ymdeltat.cpp" + "src/opl/chips/np2/fmgen_file.cpp" + "src/opl/chips/np2/fmgen_fmgen.cpp" + "src/opl/chips/np2/fmgen_fmtimer.cpp" + "src/opl/chips/np2/fmgen_opna.cpp" + "src/opl/chips/np2/fmgen_psg.cpp" + "src/opl/chips/gens_opn2.cpp" + "src/opl/chips/gx_opn2.cpp" + "src/opl/chips/mame_opn2.cpp" + "src/opl/chips/mame_opna.cpp" + "src/opl/chips/np2_opna.cpp" + "src/opl/chips/nuked_opn2.cpp" + "src/opl/chips/gx/gx_ym2612.c" + "src/opl/chips/mame/mame_ym2612fm.c" + "src/opl/chips/mamefm/emu2149.c" + "src/opl/chips/nuked/ym3438.c" + "src/opl/chips/pmdwin_opna.cpp" + "src/opl/chips/pmdwin/opna.c" + "src/opl/chips/pmdwin/psg.c" + "src/opl/chips/pmdwin/rhythmdata.c") + +if(COMPILER_SUPPORTS_CXX14) # YMFM can be built in only condition when C++14 and newer were available + set(YMFM_SOURCES + "src/opl/chips/ymfm_opn2.cpp" + "src/opl/chips/ymfm_opna.cpp" + "src/opl/chips/ymfm/ymfm_opn.cpp" + "src/opl/chips/ymfm/ymfm_misc.cpp" + "src/opl/chips/ymfm/ymfm_pcm.cpp" + "src/opl/chips/ymfm/ymfm_adpcm.cpp" + "src/opl/chips/ymfm/ymfm_ssg.cpp" + ) + if(DEFINED FLAG_CPP14) + set_source_files_properties(${YMFM_SOURCES} COMPILE_FLAGS ${FLAG_CPP14}) + endif() + list(APPEND CHIPS_SOURCES ${YMFM_SOURCES}) +endif() diff --git a/thirdparty/opnmidi/chips/chipset.pri b/thirdparty/opnmidi/chips/chipset.pri new file mode 100644 index 000000000..1d9ce1ae1 --- /dev/null +++ b/thirdparty/opnmidi/chips/chipset.pri @@ -0,0 +1,91 @@ +SOURCES+= \ + $$PWD/gens/Ym2612.cpp \ + $$PWD/mamefm/fm.cpp \ + $$PWD/mamefm/resampler.cpp \ + $$PWD/mamefm/ymdeltat.cpp \ + $$PWD/np2/fmgen_file.cpp \ + $$PWD/np2/fmgen_fmgen.cpp \ + $$PWD/np2/fmgen_fmtimer.cpp \ + $$PWD/np2/fmgen_opna.cpp \ + $$PWD/np2/fmgen_psg.cpp \ + $$PWD/gens_opn2.cpp \ + $$PWD/gx_opn2.cpp \ + $$PWD/mame_opn2.cpp \ + $$PWD/mame_opna.cpp \ + $$PWD/np2_opna.cpp \ + $$PWD/nuked_opn2.cpp \ + $$PWD/pmdwin_opna.cpp \ + $$PWD/gx/gx_ym2612.c \ + $$PWD/mame/mame_ym2612fm.c \ + $$PWD/mamefm/emu2149.c \ + $$PWD/nuked/ym3438.c \ + $$PWD/pmdwin/opna.c \ + $$PWD/pmdwin/psg.c \ + $$PWD/pmdwin/rhythmdata.c + +HEADERS+= \ + $$PWD/gens/Ym2612.hpp \ + $$PWD/gens/Ym2612_p.hpp \ + $$PWD/gx/gx_ym2612.h \ + $$PWD/mame/mame_ym2612fm.h \ + $$PWD/mame/mamedef.h \ + $$PWD/mamefm/2608intf.h \ + $$PWD/mamefm/emu.h \ + $$PWD/mamefm/emu2149.h \ + $$PWD/mamefm/emutypes.h \ + $$PWD/mamefm/fm.h \ + $$PWD/mamefm/fmopn_2608rom.h \ + $$PWD/mamefm/resampler.hpp \ + $$PWD/mamefm/ymdeltat.h \ + $$PWD/np2/compiler.h \ + $$PWD/np2/fmgen_diag.h \ + $$PWD/np2/fmgen_file.h \ + $$PWD/np2/fmgen_fmgen.h \ + $$PWD/np2/fmgen_fmgeninl.h \ + $$PWD/np2/fmgen_fmtimer.h \ + $$PWD/np2/fmgen_headers.h \ + $$PWD/np2/fmgen_misc.h \ + $$PWD/np2/fmgen_opna.h \ + $$PWD/np2/fmgen_psg.h \ + $$PWD/np2/fmgen_types.h \ + $$PWD/nuked/ym3438.h \ + $$PWD/pmdwin/op.h \ + $$PWD/pmdwin/opna.h \ + $$PWD/pmdwin/psg.h \ + $$PWD/pmdwin/rhythmdata.h \ + $$PWD/gens_opn2.h \ + $$PWD/gx_opn2.h \ + $$PWD/mame_opn2.h \ + $$PWD/mame_opna.h \ + $$PWD/np2_opna.h \ + $$PWD/nuked_opn2.h \ + $$PWD/pmdwin_opna.h \ + $$PWD/opn_chip_base.h \ + $$PWD/opn_chip_base.tcc \ + $$PWD/opn_chip_family.h + +# Available when C++14 is supported +enable_ymfm: { +DEFINES += ENABLE_YMFM_EMULATOR + +SOURCES+= \ + $$PWD/ymfm_opn2.cpp \ + $$PWD/ymfm_opna.cpp \ + $$PWD/ymfm/ymfm_adpcm.cpp \ + $$PWD/ymfm/ymfm_misc.cpp \ + $$PWD/ymfm/ymfm_opn.cpp \ + $$PWD/ymfm/ymfm_pcm.cpp \ + $$PWD/ymfm/ymfm_ssg.cpp + +HEADERS+= \ + $$PWD/ymfm_opn2.h \ + $$PWD/ymfm_opna.h \ + $$PWD/ymfm/ymfm.h \ + $$PWD/ymfm/ymfm_adpcm.h \ + $$PWD/ymfm/ymfm_fm.h \ + $$PWD/ymfm/ymfm_fm.ipp \ + $$PWD/ymfm/ymfm_misc.h \ + $$PWD/ymfm/ymfm_opn.h \ + $$PWD/ymfm/ymfm_pcm.h \ + $$PWD/ymfm/ymfm_ssg.h +} diff --git a/thirdparty/opnmidi/chips/gens/Ym2612.cpp b/thirdparty/opnmidi/chips/gens/Ym2612.cpp new file mode 100644 index 000000000..4535e0db8 --- /dev/null +++ b/thirdparty/opnmidi/chips/gens/Ym2612.cpp @@ -0,0 +1,2065 @@ +/*************************************************************************** + * libgens: Gens Emulation Library. * + * Ym2612.hpp: Yamaha YM2612 FM synthesis chip emulator. * + * * + * Copyright (c) 1999-2002 by Stéphane Dallongeville * + * Copyright (c) 2003-2004 by Stéphane Akhoun * + * Copyright (c) 2008-2010 by David Korth * + * * + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 2 of the License, or (at your * + * option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +/*********************************************************** + * * + * YM2612.C : YM2612 emulator * + * * + * Almost constantes are taken from the MAME core * + * * + * This source is a part of Gens project * + * Written by Stéphane Dallongeville (gens@consolemul.com) * + * Copyright (c) 2002 by Stéphane Dallongeville * + * * + ***********************************************************/ + +#include "Ym2612.hpp" +#include "Ym2612_p.hpp" + +// C includes. +#include + +// C includes. (C++ namespace) +#include +#include +#include +#include + +/* Message logging. */ +//#define LOG_MSG(...) +//#include "macros/log_msg.h" + +#if 0 +// Sound Manager. +#include "SoundMgr.hpp" +#include "Vdp/Vdp.hpp" + +// TODO: Get rid of EmuContext. +#include "EmuContext/EmuContext.hpp" +#endif + +namespace LibGens { + +/** Ym2612Private **/ + +// Static variables. +bool Ym2612Private::isInit = false; +int *Ym2612Private::SIN_TAB[SIN_LENGTH]; // SINUS TABLE (pointer on TL TABLE) +int Ym2612Private::TL_TAB[TL_LENGTH * 2]; // TOTAL LEVEL TABLE (plus and minus) +unsigned int Ym2612Private::ENV_TAB[2 * ENV_LENGTH * 8]; // ENV CURVE TABLE (attack & decay) +//unsigned int Ym2612Private::ATTACK_TO_DECAY[ENV_LENGTH]; // Conversion from attack to decay phase +unsigned int Ym2612Private::DECAY_TO_ATTACK[ENV_LENGTH]; // Conversion from decay to attack phase + +// libOPNMIDI: Fixed the compatibility with the error: "floating-point literal cannot appear in a constant-expression" +int Ym2612Private::LIMIT_CH_OUT = ((int) (((1 << OUT_BITS) * 1.5) - 1)); +int Ym2612Private::PG_CUT_OFF = ((int) (78.0 / ENV_STEP)); +int Ym2612Private::ENV_CUT_OFF = ((int) (68.0 / ENV_STEP)); +int Ym2612Private::LFO_FMS_BASE = ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS))); + +// Next Enveloppe phase functions pointer table +const Ym2612Private::Env_Event Ym2612Private::ENV_NEXT_EVENT[8] = { + Env_Attack_Next, + Env_Decay_Next, + Env_Substain_Next, + Env_Release_Next, + Env_NULL_Next, + Env_NULL_Next, + Env_NULL_Next, + Env_NULL_Next +}; + +// Default detune table. +// FD == F number +const uint8_t Ym2612Private::DT_DEF_TAB[4][32] = { + // FD = 0 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + // FD = 1 + {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8}, + + // FD = 2 + {1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16}, + + // FD = 3 + {2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22} +}; + +const uint8_t Ym2612Private::FKEY_TAB[16] = { + 0, 0, 0, 0, + 0, 0, 0, 1, + 2, 3, 3, 3, + 3, 3, 3, 3 +}; + +const uint8_t Ym2612Private::LFO_AMS_TAB[4] = { + 31, 4, 1, 0 +}; + +const uint8_t Ym2612Private::LFO_FMS_TAB[8] = { + static_cast(LFO_FMS_BASE * 0), static_cast(LFO_FMS_BASE * 1), + static_cast(LFO_FMS_BASE * 2), static_cast(LFO_FMS_BASE * 3), + static_cast(LFO_FMS_BASE * 4), static_cast(LFO_FMS_BASE * 6), + static_cast(LFO_FMS_BASE * 12), static_cast(LFO_FMS_BASE * 24) +}; + +/* + * Pan law table + */ +static const unsigned short panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + +unsigned int Ym2612Private::SL_TAB[16]; // Sustain level table. (STATIC) +unsigned int Ym2612Private::NULL_RATE[32]; // Table for NULL rate. (STATIC) + +// LFO tables. +int Ym2612Private::LFO_ENV_TAB[LFO_LENGTH]; // LFO AMS TABLE (adjusted for 11.8 dB) +int Ym2612Private::LFO_FREQ_TAB[LFO_LENGTH]; // LFO FMS TABLE + +// NOTE: INTER_TAB isn't used... +//int Ym2612::INTER_TAB[MAX_UPDATE_LENGTH]; // Interpolation table + +/** Gens-specific **/ + +// TODO for LibGens +#if 0 +#include "util/sound/gym.hpp" +#endif +/** end **/ + +Ym2612Private::Ym2612Private(Ym2612 *q) + : q(q) +{ + if (!isInit) { + // Initialize the static tables. + isInit = true; + doStaticInit(); + } +} + +void Ym2612Private::doStaticInit(void) +{ + // Sine table: + // SIN_TAB[x][y] = sin(x) * y; + // x = phase and y = volume + SIN_TAB[0] = SIN_TAB[SIN_LENGTH / 2] = &TL_TAB[(int)PG_CUT_OFF]; + + for (int i = 1; i <= SIN_LENGTH / 4; i++) { + double x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGTH)); // Sinus + x = 20 * log10(1 / x); // convert to dB + + int j = (int)(x / ENV_STEP); // Get TL range + if (j > PG_CUT_OFF) { + j = (int) PG_CUT_OFF; + } + + SIN_TAB[i] = SIN_TAB[(SIN_LENGTH / 2) - i] = &TL_TAB[j]; + SIN_TAB[(SIN_LENGTH / 2) + i] = SIN_TAB[SIN_LENGTH - i] = &TL_TAB[TL_LENGTH + j]; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG3, +// "SIN[%d][0] = %.8X SIN[%d][0] = %.8X SIN[%d][0] = %.8X SIN[%d][0] = %.8X", +// i, SIN_TAB[i][0], (SIN_LENGTH / 2) - i, +// SIN_TAB[(SIN_LENGTH / 2) - i][0], (SIN_LENGTH / 2) + i, +// SIN_TAB[(SIN_LENGTH / 2) + i][0], SIN_LENGTH - i, +// SIN_TAB[SIN_LENGTH - i][0]); + } + + // LFO table: + for (int i = 0; i < LFO_LENGTH; i++) { + double x = sin (2.0 * PI * (double) (i) / (double) (LFO_LENGTH)); // Sinus + x += 1.0; + x /= 2.0; // positive only + x *= 11.8 / ENV_STEP; // adjusted to MAX envelope modulation + + LFO_ENV_TAB[i] = (int) x; + + x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGTH)); // Sinus + x *= (double) ((1 << (LFO_HBITS - 1)) - 1); + + LFO_FREQ_TAB[i] = (int) x; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG3, +// "LFO[%d] = %.8X", i, LFO_ENV_TAB[i]); + } + + // Envelope table: + // ENV_TAB[0] -> ENV_TAB[ENV_LENGTH - 1] = attack curve + // ENV_TAB[ENV_LENGTH] -> ENV_TAB[2 * ENV_LENGTH - 1] = decay curve + for (int i = 0; i < ENV_LENGTH; i++) { + // Attack curve (x^8 - music level 2 Vectorman 2) + double x = pow(((double)((ENV_LENGTH - 1) - i) / (double)(ENV_LENGTH)), 8); + x *= ENV_LENGTH; + + ENV_TAB[i] = (int)x; + + // Decay curve (just linear) + x = pow(((double) (i) / (double) (ENV_LENGTH)), 1); + x *= ENV_LENGTH; + + ENV_TAB[ENV_LENGTH + i] = (int)x; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG3, +// "ATTACK[%d] = %d DECAY[%d] = %d", +// i, ENV_TAB[i], i, ENV_TAB[ENV_LENGTH + i]); + } + + ENV_TAB[ENV_END >> ENV_LBITS] = ENV_LENGTH - 1; // for the stopped state + + // Decay -> Attack table. + // NOTE: There used to be an Attack -> Decay table, + // but it wasn't implemented as of the original + // port of Gens for Linux... + for (int i = 0, j = ENV_LENGTH - 1; i < ENV_LENGTH; i++) { + while (j && (ENV_TAB[j] < (unsigned) i)) + j--; + + DECAY_TO_ATTACK[i] = j << ENV_LBITS; + } + + // Sustain Level table: + for (int i = 0; i < 15; i++) { + double x = i * 3; // 3 and not 6 (Mickey Mania first music for test) + x /= ENV_STEP; + + int j = (int)x; + j <<= ENV_LBITS; + + SL_TAB[i] = j + ENV_DECAY; + } + + int j = ENV_LENGTH - 1; // special case : volume off + j <<= ENV_LBITS; + SL_TAB[15] = j + ENV_DECAY; + + // TL table: + // [0 - 4095] = +output [4095 - ...] = +output overflow (fill with 0) + // [12288 - 16383] = -output [16384 - ...] = -output overflow (fill with 0) + for (int i = 0; i < TL_LENGTH; i++) { + if (i >= PG_CUT_OFF) { + // YM2612 cut off sound after 78 dB (14 bits output ?) + TL_TAB[TL_LENGTH + i] = TL_TAB[i] = 0; + } else { + double x = MAX_OUT; // Max output + x /= pow(10, (ENV_STEP * i) / 20); // Decibel -> Voltage + + TL_TAB[i] = (int) x; + TL_TAB[TL_LENGTH + i] = -TL_TAB[i]; + } + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG3, +// "TL_TAB[%d] = %.8X TL_TAB[%d] = %.8X", +// i, TL_TAB[i], TL_LENGTH + i, TL_TAB[TL_LENGTH + i]); + } + + // NULL frequency rate table. + // It's always 0. + memset(NULL_RATE, 0, sizeof(NULL_RATE)); +} + +/***************************************** + * Functions for calculating parameters. * + *****************************************/ + +inline void Ym2612Private::CALC_FINC_SL(slot_t *SL, int finc, int kc) +{ + int ksr; + + SL->Finc = (finc + SL->DT[kc]) * SL->MUL; + ksr = kc >> SL->KSR_S; // keycode atténuation + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "FINC = %d SL->Finc = %d", finc, SL->Finc); + + if (SL->KSR != ksr) { + // si le KSR a changé alors + // les différents taux pour l'enveloppe sont mis à jour + SL->KSR = ksr; + + SL->EincA = SL->AR[ksr]; + SL->EincD = SL->DR[ksr]; + SL->EincS = SL->SR[ksr]; + SL->EincR = SL->RR[ksr]; + + if (SL->Ecurp == ATTACK) { + SL->Einc = SL->EincA; + } else if (SL->Ecurp == DECAY) { + SL->Einc = SL->EincD; + } else if (SL->Ecnt < ENV_END) { + if (SL->Ecurp == SUSTAIN) + SL->Einc = SL->EincS; + else if (SL->Ecurp == RELEASE) + SL->Einc = SL->EincR; + } + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "KSR = %.4X EincA = %.8X EincD = %.8X EincS = %.8X EincR = %.8X", +// ksr, SL->EincA, SL->EincD, SL->EincS, SL->EincR); + } +} + +inline void Ym2612Private::CALC_FINC_CH(channel_t *CH) +{ + int finc, kc; + + finc = FINC_TAB[CH->FNUM[0]] >> (7 - CH->FOCT[0]); + kc = CH->KC[0]; + + CALC_FINC_SL(&CH->_SLOT[0], finc, kc); + CALC_FINC_SL(&CH->_SLOT[1], finc, kc); + CALC_FINC_SL(&CH->_SLOT[2], finc, kc); + CALC_FINC_SL(&CH->_SLOT[3], finc, kc); +} + +/********************************* + * Functions for setting values. * + *********************************/ + +/** + * KEY_ON event. + * @param ch Channel. + * @param nsl Slot number. + */ +void Ym2612Private::KEY_ON(channel_t *CH, int nsl) +{ + slot_t *SL = &(CH->_SLOT[nsl]); // on recupère le bon pointeur de slot + + // la touche est-elle relâchée ? + if (SL->Ecurp == RELEASE) { + SL->Fcnt = 0; + + // Fix Ecco 2 splash sound + SL->Ecnt = (DECAY_TO_ATTACK[ENV_TAB[SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM; + SL->ChgEnM = ~0; + + /* + SL->Ecnt = DECAY_TO_ATTACK[ENV_TAB[SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK; + SL->Ecnt = 0; + */ + + SL->Einc = SL->EincA; + SL->Ecmp = ENV_DECAY; + SL->Ecurp = ATTACK; + } +} + +/** + * KEY_OFF event. + * @param ch Channel. + * @param nsl Slot number. + */ +void Ym2612Private::KEY_OFF(channel_t *CH, int nsl) +{ + slot_t *SL = &(CH->_SLOT[nsl]); // on recupère le bon pointeur de slot + + // la touche est-elle appuyée ? + if (SL->Ecurp != RELEASE) { + // attack phase ? + if (SL->Ecnt < ENV_DECAY) { + SL->Ecnt = (ENV_TAB[SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY; + } + + SL->Einc = SL->EincR; + SL->Ecmp = ENV_END; + SL->Ecurp = RELEASE; + } +} + +inline void Ym2612Private::CSM_Key_Control(void) +{ + KEY_ON(&state.CHANNEL[2], 0); + KEY_ON(&state.CHANNEL[2], 1); + KEY_ON(&state.CHANNEL[2], 2); + KEY_ON(&state.CHANNEL[2], 3); +} + +/** + * Set a value for a slot. + * @param address Register address. + * @param data Data. + */ +int Ym2612Private::SLOT_SET(int address, uint8_t data) +{ + int nch = address & 3; + if (nch == 3) + return 1; + if (address & 0x100) + nch += 3; + channel_t *CH = &state.CHANNEL[nch]; + + const int nsl = (address >> 2) & 3; + slot_t *SL = &CH->_SLOT[nsl]; + + switch (address & 0xF0) { + case 0x30: + if ((SL->MUL = (data & 0x0F)) != 0) { + SL->MUL <<= 1; + } else { + SL->MUL = 1; + } + + SL->DT = DT_TAB[(data >> 4) & 7]; + CH->_SLOT[0].Finc = -1; +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[%d], SLOT[%d] DTMUL = %.2X", nch, nsl, data & 0x7F); + break; + + case 0x40: + SL->TL = data & 0x7F; + + // SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound... + q->specialUpdate(); + + #if ((ENV_HBITS - 7) < 0) + SL->TLL = SL->TL >> (7 - ENV_HBITS); + #else + SL->TLL = SL->TL << (ENV_HBITS - 7); + #endif + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[%d], SLOT[%d] TL = %.2X", nch, nsl, SL->TL); + break; + + case 0x50: + SL->KSR_S = 3 - (data >> 6); + CH->_SLOT[0].Finc = -1; + + if (data &= 0x1F) { + SL->AR = &AR_TAB[data << 1]; + } else { + SL->AR = &NULL_RATE[0]; + } + + SL->EincA = SL->AR[SL->KSR]; + if (SL->Ecurp == ATTACK) + SL->Einc = SL->EincA; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[%d], SLOT[%d] AR = %.2X EincA = %.6X", nch, nsl, data, SL->EincA); + break; + + case 0x60: + if ((SL->AMSon = (data & 0x80))) { + SL->AMS = CH->AMS; + } else { + SL->AMS = 31; + } + + if (data &= 0x1F) { + SL->DR = &DR_TAB[data << 1]; + } else { + SL->DR = &NULL_RATE[0]; + } + + SL->EincD = SL->DR[SL->KSR]; + if (SL->Ecurp == DECAY) + SL->Einc = SL->EincD; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[%d], SLOT[%d] AMS = %d DR = %.2X EincD = %.6X", +// nch, nsl, SL->AMSon, data, SL->EincD); + break; + + case 0x70: + if (data &= 0x1F) { + SL->SR = &DR_TAB[data << 1]; + } else { + SL->SR = &NULL_RATE[0]; + } + + SL->EincS = SL->SR[SL->KSR]; + if ((SL->Ecurp == SUSTAIN) && (SL->Ecnt < ENV_END)) + SL->Einc = SL->EincS; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[%d], SLOT[%d] SR = %.2X EincS = %.6X", +// nch, nsl, data, SL->EincS); + break; + + case 0x80: + SL->SLL = SL_TAB[data >> 4]; + SL->RR = &DR_TAB[((data & 0xF) << 2) + 2]; + SL->EincR = SL->RR[SL->KSR]; + if ((SL->Ecurp == RELEASE) && (SL->Ecnt < ENV_END)) + SL->Einc = SL->EincR; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[%d], SLOT[%d] SL = %.8X", nch, nsl, SL->SLL); +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[%d], SLOT[%d] RR = %.2X EincR = %.2X", +// nch, nsl, ((data & 0xF) << 1) | 2, SL->EincR); + break; + + case 0x90: + // SSG-EG envelope shapes: + /* + E At Al H + 1 0 0 0 \\\\ + 1 0 0 1 \___ + 1 0 1 0 \/\/ + 1 0 1 1 \ + 1 1 0 0 //// + 1 1 0 1 / + 1 1 1 0 /\/\ + 1 1 1 1 /___ + + E = SSG-EG enable + At = Start negate + Al = Altern + H = Hold + */ + + if (data & 0x08) { + SL->SEG = data & 0x0F; + } else { + SL->SEG = 0; + } + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[%d], SLOT[%d] SSG-EG = %.2X", nch, nsl, data); + break; + } + + return 0; +} + +/** + * Set a value for a channel. + * @param address Register address. + * @param data Data. + */ +int Ym2612Private::CHANNEL_SET(int address, uint8_t data) +{ + int nch = address & 3; + if (nch == 3) + return 1; + if (address & 0x100) + nch += 3; + + channel_t *CH = &state.CHANNEL[nch]; + + switch (address & 0xFC) { + case 0xA0: + q->specialUpdate(); + + CH->FNUM[0] = (CH->FNUM[0] & 0x700) + data; + CH->KC[0] = (CH->FOCT[0] << 2) | FKEY_TAB[CH->FNUM[0] >> 7]; + + CH->_SLOT[0].Finc = -1; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[%d] part1 FNUM = %d KC = %d", +// nch, CH->FNUM[0], CH->KC[0]); + break; + + case 0xA4: + q->specialUpdate(); + + CH->FNUM[0] = (CH->FNUM[0] & 0x0FF) + ((int) (data & 0x07) << 8); + CH->FOCT[0] = (data & 0x38) >> 3; + CH->KC[0] = (CH->FOCT[0] << 2) | FKEY_TAB[CH->FNUM[0] >> 7]; + + CH->_SLOT[0].Finc = -1; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[%d] part2 FNUM = %d FOCT = %d KC = %d", +// nch, CH->FNUM[0], CH->FOCT[0], CH->KC[0]); + break; + + case 0xA8: + if (address < 0x100) { + // Channel 3: Specific slot operator. + const int nsl = nch + 1; + + q->specialUpdate(); + + state.CHANNEL[2].FNUM[nsl] = (state.CHANNEL[2].FNUM[nsl] & 0x700) + data; + state.CHANNEL[2].KC[nsl] = (state.CHANNEL[2].FOCT[nsl] << 2) | + FKEY_TAB[state.CHANNEL[2].FNUM[nsl] >> 7]; + + state.CHANNEL[2]._SLOT[0].Finc = -1; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[2] part1 FNUM[%d] = %d KC[%d] = %d", +// nsl, state.CHANNEL[2].FNUM[nsl], +// nsl, state.CHANNEL[2].KC[nsl]); + } + break; + + case 0xAC: + if (address < 0x100) { + // Channel 3: Specific slot operator. + const int nsl = nch + 1; + + q->specialUpdate(); + + state.CHANNEL[2].FNUM[nsl] = (state.CHANNEL[2].FNUM[nsl] & 0x0FF) + + ((int) (data & 0x07) << 8); + state.CHANNEL[2].FOCT[nsl] = (data & 0x38) >> 3; + state.CHANNEL[2].KC[nsl] = (state.CHANNEL[2].FOCT[nsl] << 2) | + FKEY_TAB[state.CHANNEL[2].FNUM[nsl] >> 7]; + + state.CHANNEL[2]._SLOT[0].Finc = -1; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[2] part2 FNUM[%d] = %d FOCT[%d] = %d KC[%d] = %d", +// nsl, state.CHANNEL[2].FNUM[nsl], +// nsl, state.CHANNEL[2].FOCT[nsl], +// nsl, state.CHANNEL[2].KC[nsl]); + } + break; + + case 0xB0: + if (CH->ALGO != (data & 7)) { + // Fix VectorMan 2 heli sound (level 1) + q->specialUpdate(); + + CH->ALGO = data & 7; + + CH->_SLOT[0].ChgEnM = 0; + CH->_SLOT[1].ChgEnM = 0; + CH->_SLOT[2].ChgEnM = 0; + CH->_SLOT[3].ChgEnM = 0; + } + + CH->FB = 9 - ((data >> 3) & 7); // Real thing ? + + /*if (CH->FB = ((data >> 3) & 7)) { + // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound... + CH->FB = 9 - CH->FB; + } else { + CH->FB = 31; + }*/ + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "CHANNEL[%d] ALGO = %d FB = %d", nch, CH->ALGO, CH->FB); + break; + + case 0xB4: + q->specialUpdate(); + + // -1 = 0xFFFFFFFF. Useful trick from game-music-emu. + CH->LEFT = 0 - ((data >> 7) & 1); + CH->RIGHT = 0 - ((data >> 6) & 1); + + CH->AMS = LFO_AMS_TAB[(data >> 4) & 3]; + CH->FMS = LFO_FMS_TAB[data & 7]; + + for (int i = 0; i < 3; i++) { + slot_t *SL = &CH->_SLOT[i]; + SL->AMS = (SL->AMSon ? CH->AMS : 31); + } + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG1, +// "CHANNEL[%d] AMS = %d FMS = %d", nch, CH->AMS, CH->FMS); + break; + } + + return 0; +} + +/** + * Set a value for the entire device. + * @param address Register address. + * @param data Data. + */ +int Ym2612Private::YM_SET(int address, uint8_t data) +{ + switch (address) { + case 0x22: + // LFO enable + if (data & 8) { + // Cool Spot music 1, LFO modified severals time which + // distord the sound, have to check that on a real genesis... + state.LFOinc = LFO_INC_TAB[data & 7]; +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG1, +// "LFO Enable, LFOinc = %.8X %d", state.LFOinc, data & 7); + } else { + state.LFOinc = state.LFOcnt = 0; +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG1, +// "LFO Disable"); + } + break; + + case 0x24: + state.TimerA = (state.TimerA & 0x003) | (((int)data) << 2); + if (state.TimerAL != (1024 - state.TimerA) << 12) { + state.TimerAcnt = state.TimerAL = (1024 - state.TimerA) << 12; +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "Timer A Set = %.8X", state.TimerAcnt); + } + break; + + case 0x25: + state.TimerA = (state.TimerA & 0x3FC) | (data & 3); + if (state.TimerAL != (1024 - state.TimerA) << 12) { + state.TimerAcnt = state.TimerAL = (1024 - state.TimerA) << 12; +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "Timer A Set = %.8X", state.TimerAcnt); + } + break; + + case 0x26: + state.TimerB = data; + if (state.TimerBL != (256 - state.TimerB) << (4 + 12)) { + state.TimerBcnt = state.TimerBL = (256 - state.TimerB) << (4 + 12); +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "Timer B Set = %.8X", state.TimerBcnt); + } + break; + + case 0x27: + // Paramètre divers + // b7 = CSM MODE + // b6 = 3 slot mode + // b5 = reset b + // b4 = reset a + // b3 = timer enable b + // b2 = timer enable a + // b1 = load b + // b0 = load a + + if ((data ^ state.Mode) & 0x40) { + // We changed the channel 2 mode, so recalculate phase step + // This fix the punch sound in Street of Rage 2 + q->specialUpdate(); + state.CHANNEL[2]._SLOT[0].Finc = -1; // recalculate phase step + } + + /* + if ((data & 2) && (YM2612.Status & 2)) + YM2612.TimerBcnt = YM2612.TimerBL; + if ((data & 1) && (YM2612.Status & 1)) + YM2612.TimerAcnt = YM2612.TimerAL; + */ + + //YM2612.Status &= (~data >> 4); // Reset du Status au cas ou c'est demandé + state.status &= (~data >> 4) & (data >> 2); // Reset Status + + state.Mode = data; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG1, +// "Mode reg = %.2X", data); + break; + + case 0x28: { + int nch = data & 3; + if (nch == 3) + return 1; + + if (data & 4) + nch += 3; + channel_t *CH = &state.CHANNEL[nch]; + + q->specialUpdate(); + + // KEY_ON or KEY_OFF the selected operator(s). + if (data & 0x10) + KEY_ON(CH, S0); // On appuie sur la touche pour le slot 1 + else + KEY_OFF(CH, S0); // On relâche la touche pour le slot 1 + if (data & 0x20) + KEY_ON(CH, S1); // On appuie sur la touche pour le slot 3 + else + KEY_OFF(CH, S1); // On relâche la touche pour le slot 3 + if (data & 0x40) + KEY_ON(CH, S2); // On appuie sur la touche pour le slot 2 + else + KEY_OFF(CH, S2); // On relâche la touche pour le slot 2 + if (data & 0x80) + KEY_ON(CH, S3); // On appuie sur la touche pour le slot 4 + else + KEY_OFF(CH, S3); // On relâche la touche pour le slot 4 + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG1, +// "CHANNEL[%d] KEY %.1X", nch, ((data & 0xF0) >> 4)); + break; + } + + case 0x2A: + // Set the DAC value. + state.DACdata = ((int)data - 0x80) << 7; // donnée du DAC + break; + + case 0x2B: + if (state.DAC ^ (data & 0x80)) + q->specialUpdate(); + + // Enable/disable the DAC. + state.DAC = data & 0x80; // Activate / Deactivate the DAC. + break; + } + + return 0; +} + +/*********************************************** + * fonctions de génération * + ***********************************************/ + +void Ym2612Private::Env_NULL_Next(slot_t *SL) +{ + // Mark SL as unused. + // TODO: Write a macro to do this! + ((void)SL); +} + +void Ym2612Private::Env_Attack_Next(slot_t *SL) +{ + // Verified with Gynoug even in HQ (explode SFX) + SL->Ecnt = ENV_DECAY; + + SL->Einc = SL->EincD; + SL->Ecmp = SL->SLL; + SL->Ecurp = DECAY; +} + +void Ym2612Private::Env_Decay_Next(slot_t *SL) +{ + // Verified with Gynoug even in HQ (explode SFX) + SL->Ecnt = SL->SLL; + + SL->Einc = SL->EincS; + SL->Ecmp = ENV_END; + SL->Ecurp = SUSTAIN; +} + +void Ym2612Private::Env_Substain_Next(slot_t *SL) +{ + if (SL->SEG & 8) { + // SSG-EG is enabled. + if (SL->SEG & 1) { + SL->Ecnt = ENV_END; + SL->Einc = 0; + SL->Ecmp = ENV_END + 1; + } else { + // re KEY ON + + // SL->Fcnt = 0; + // SL->ChgEnM = 0xFFFFFFFF; + + SL->Ecnt = 0; + SL->Einc = SL->EincA; + SL->Ecmp = ENV_DECAY; + SL->Ecurp = ATTACK; + } + + SL->SEG ^= (SL->SEG & 2) << 1; + } else { + // SSG-EG is disabled. + SL->Ecnt = ENV_END; + SL->Einc = 0; + SL->Ecmp = ENV_END + 1; + } +} + +void Ym2612Private::Env_Release_Next(slot_t *SL) +{ + SL->Ecnt = ENV_END; + SL->Einc = 0; + SL->Ecmp = ENV_END + 1; +} + +#define GET_CURRENT_PHASE() do { \ + in0 = CH->_SLOT[S0].Fcnt; \ + in1 = CH->_SLOT[S1].Fcnt; \ + in2 = CH->_SLOT[S2].Fcnt; \ + in3 = CH->_SLOT[S3].Fcnt; \ +} while (0) + +#define UPDATE_PHASE() do { \ + CH->_SLOT[S0].Fcnt += CH->_SLOT[S0].Finc; \ + CH->_SLOT[S1].Fcnt += CH->_SLOT[S1].Finc; \ + CH->_SLOT[S2].Fcnt += CH->_SLOT[S2].Finc; \ + CH->_SLOT[S3].Fcnt += CH->_SLOT[S3].Finc; \ +} while (0) + +#define UPDATE_PHASE_LFO() do { \ +if ((freq_LFO = (CH->FMS * LFO_FREQ_UP[i]) >> (LFO_HBITS - 1))) \ +{ \ + CH->_SLOT[S0].Fcnt += CH->_SLOT[S0].Finc + ((CH->_SLOT[S0].Finc * freq_LFO) >> LFO_FMS_LBITS); \ + CH->_SLOT[S1].Fcnt += CH->_SLOT[S1].Finc + ((CH->_SLOT[S1].Finc * freq_LFO) >> LFO_FMS_LBITS); \ + CH->_SLOT[S2].Fcnt += CH->_SLOT[S2].Finc + ((CH->_SLOT[S2].Finc * freq_LFO) >> LFO_FMS_LBITS); \ + CH->_SLOT[S3].Fcnt += CH->_SLOT[S3].Finc + ((CH->_SLOT[S3].Finc * freq_LFO) >> LFO_FMS_LBITS); \ +} \ +else \ +{ \ + CH->_SLOT[S0].Fcnt += CH->_SLOT[S0].Finc; \ + CH->_SLOT[S1].Fcnt += CH->_SLOT[S1].Finc; \ + CH->_SLOT[S2].Fcnt += CH->_SLOT[S2].Finc; \ + CH->_SLOT[S3].Fcnt += CH->_SLOT[S3].Finc; \ +} \ +} while (0) + +// Commented out from Gens Rerecording +/* +#define GET_CURRENT_ENV \ +if (CH->_SLOT[S0].SEG & 4) \ +{ \ + if ((en0 = ENV_TAB[(CH->_SLOT[S0].Ecnt >> ENV_LBITS)] + CH->_SLOT[S0].TLL) > ENV_MASK) en0 = 0; \ + else en0 ^= ENV_MASK; \ +} \ +else en0 = ENV_TAB[(CH->_SLOT[S0].Ecnt >> ENV_LBITS)] + CH->_SLOT[S0].TLL; \ +if (CH->_SLOT[S1].SEG & 4) \ +{ \ + if ((en1 = ENV_TAB[(CH->_SLOT[S1].Ecnt >> ENV_LBITS)] + CH->_SLOT[S1].TLL) > ENV_MASK) en1 = 0; \ + else en1 ^= ENV_MASK; \ +} \ +else en1 = ENV_TAB[(CH->_SLOT[S1].Ecnt >> ENV_LBITS)] + CH->_SLOT[S1].TLL; \ +if (CH->_SLOT[S2].SEG & 4) \ +{ \ + if ((en2 = ENV_TAB[(CH->_SLOT[S2].Ecnt >> ENV_LBITS)] + CH->_SLOT[S2].TLL) > ENV_MASK) en2 = 0; \ + else en2 ^= ENV_MASK; \ +} \ +else en2 = ENV_TAB[(CH->_SLOT[S2].Ecnt >> ENV_LBITS)] + CH->_SLOT[S2].TLL; \ +if (CH->_SLOT[S3].SEG & 4) \ +{ \ + if ((en3 = ENV_TAB[(CH->_SLOT[S3].Ecnt >> ENV_LBITS)] + CH->_SLOT[S3].TLL) > ENV_MASK) en3 = 0; \ + else en3 ^= ENV_MASK; \ +} \ +else en3 = ENV_TAB[(CH->_SLOT[S3].Ecnt >> ENV_LBITS)] + CH->_SLOT[S3].TLL; +*/ + +// New version from Gens Rerecording +#define GET_CURRENT_ENV() do { \ + en0 = ENV_TAB[(CH->_SLOT[S0].Ecnt >> ENV_LBITS)] + CH->_SLOT[S0].TLL; \ + en1 = ENV_TAB[(CH->_SLOT[S1].Ecnt >> ENV_LBITS)] + CH->_SLOT[S1].TLL; \ + en2 = ENV_TAB[(CH->_SLOT[S2].Ecnt >> ENV_LBITS)] + CH->_SLOT[S2].TLL; \ + en3 = ENV_TAB[(CH->_SLOT[S3].Ecnt >> ENV_LBITS)] + CH->_SLOT[S3].TLL; \ +} while (0) + +// Commented out from Gens Rerecording +/* +#define GET_CURRENT_ENV_LFO \ +env_LFO = LFO_ENV_UP[i]; \ + \ +if (CH->_SLOT[S0].SEG & 4) \ +{ \ + if ((en0 = ENV_TAB[(CH->_SLOT[S0].Ecnt >> ENV_LBITS)] + CH->_SLOT[S0].TLL) > ENV_MASK) en0 = 0; \ + else en0 = (en0 ^ ENV_MASK) + (env_LFO >> CH->_SLOT[S0].AMS); \ +} \ +else en0 = ENV_TAB[(CH->_SLOT[S0].Ecnt >> ENV_LBITS)] + CH->_SLOT[S0].TLL + (env_LFO >> CH->_SLOT[S0].AMS); \ +if (CH->_SLOT[S1].SEG & 4) \ +{ \ + if ((en1 = ENV_TAB[(CH->_SLOT[S1].Ecnt >> ENV_LBITS)] + CH->_SLOT[S1].TLL) > ENV_MASK) en1 = 0; \ + else en1 = (en1 ^ ENV_MASK) + (env_LFO >> CH->_SLOT[S1].AMS); \ +} \ +else en1 = ENV_TAB[(CH->_SLOT[S1].Ecnt >> ENV_LBITS)] + CH->_SLOT[S1].TLL + (env_LFO >> CH->_SLOT[S1].AMS); \ +if (CH->_SLOT[S2].SEG & 4) \ +{ \ + if ((en2 = ENV_TAB[(CH->_SLOT[S2].Ecnt >> ENV_LBITS)] + CH->_SLOT[S2].TLL) > ENV_MASK) en2 = 0; \ + else en2 = (en2 ^ ENV_MASK) + (env_LFO >> CH->_SLOT[S2].AMS); \ +} \ +else en2 = ENV_TAB[(CH->_SLOT[S2].Ecnt >> ENV_LBITS)] + CH->_SLOT[S2].TLL + (env_LFO >> CH->_SLOT[S2].AMS); \ +if (CH->_SLOT[S3].SEG & 4) \ +{ \ + if ((en3 = ENV_TAB[(CH->_SLOT[S3].Ecnt >> ENV_LBITS)] + CH->_SLOT[S3].TLL) > ENV_MASK) en3 = 0; \ + else en3 = (en3 ^ ENV_MASK) + (env_LFO >> CH->_SLOT[S3].AMS); \ +} \ +else en3 = ENV_TAB[(CH->_SLOT[S3].Ecnt >> ENV_LBITS)] + CH->_SLOT[S3].TLL + (env_LFO >> CH->_SLOT[S3].AMS); +*/ + +// New version from Gens Rerecording +#define GET_CURRENT_ENV_LFO() do { \ +env_LFO = LFO_ENV_UP[i]; \ +en0 = ENV_TAB[(CH->_SLOT[S0].Ecnt >> ENV_LBITS)] + CH->_SLOT[S0].TLL + (env_LFO >> CH->_SLOT[S0].AMS); \ +en1 = ENV_TAB[(CH->_SLOT[S1].Ecnt >> ENV_LBITS)] + CH->_SLOT[S1].TLL + (env_LFO >> CH->_SLOT[S1].AMS); \ +en2 = ENV_TAB[(CH->_SLOT[S2].Ecnt >> ENV_LBITS)] + CH->_SLOT[S2].TLL + (env_LFO >> CH->_SLOT[S2].AMS); \ +en3 = ENV_TAB[(CH->_SLOT[S3].Ecnt >> ENV_LBITS)] + CH->_SLOT[S3].TLL + (env_LFO >> CH->_SLOT[S3].AMS); \ +} while (0) + +#define UPDATE_ENV() do { \ + if ((CH->_SLOT[S0].Ecnt += CH->_SLOT[S0].Einc) >= CH->_SLOT[S0].Ecmp) \ + ENV_NEXT_EVENT[CH->_SLOT[S0].Ecurp](&(CH->_SLOT[S0])); \ + if ((CH->_SLOT[S1].Ecnt += CH->_SLOT[S1].Einc) >= CH->_SLOT[S1].Ecmp) \ + ENV_NEXT_EVENT[CH->_SLOT[S1].Ecurp](&(CH->_SLOT[S1])); \ + if ((CH->_SLOT[S2].Ecnt += CH->_SLOT[S2].Einc) >= CH->_SLOT[S2].Ecmp) \ + ENV_NEXT_EVENT[CH->_SLOT[S2].Ecurp](&(CH->_SLOT[S2])); \ + if ((CH->_SLOT[S3].Ecnt += CH->_SLOT[S3].Einc) >= CH->_SLOT[S3].Ecmp) \ + ENV_NEXT_EVENT[CH->_SLOT[S3].Ecurp](&(CH->_SLOT[S3])); \ +} while (0) + +#define DO_LIMIT() do { \ + if (CH->OUTd > LIMIT_CH_OUT) \ + CH->OUTd = LIMIT_CH_OUT; \ + else if (CH->OUTd < -LIMIT_CH_OUT) \ + CH->OUTd = -LIMIT_CH_OUT; \ +} while (0) + +#define DO_FEEDBACK0() do { \ + in0 += CH->S0_OUT[0] >> CH->FB; \ + CH->S0_OUT[0] = SIN_TAB[(in0 >> SIN_LBITS) & SIN_MASK][en0]; \ +} while (0) + +#define DO_FEEDBACK() do { \ + in0 += (CH->S0_OUT[0] + CH->S0_OUT[1]) >> CH->FB; \ + CH->S0_OUT[1] = CH->S0_OUT[0]; \ + CH->S0_OUT[0] = SIN_TAB[(in0 >> SIN_LBITS) & SIN_MASK][en0]; \ +} while (0) + +#define DO_FEEDBACK2() do { \ + in0 += (CH->S0_OUT[0] + (CH->S0_OUT[0] >> 2) + CH->S0_OUT[1]) >> CH->FB; \ + CH->S0_OUT[1] = CH->S0_OUT[0] >> 2; \ + CH->S0_OUT[0] = SIN_TAB[(in0 >> SIN_LBITS) & SIN_MASK][en0]; \ +} while (0) + +#define DO_FEEDBACK3() do { \ + in0 += (CH->S0_OUT[0] + CH->S0_OUT[1] + CH->S0_OUT[2] + CH->S0_OUT[3]) >> CH->FB; \ + CH->S0_OUT[3] = CH->S0_OUT[2] >> 1; \ + CH->S0_OUT[2] = CH->S0_OUT[1] >> 1; \ + CH->S0_OUT[1] = CH->S0_OUT[0] >> 1; \ + CH->S0_OUT[0] = SIN_TAB[(in0 >> SIN_LBITS) & SIN_MASK][en0]; \ +} while (0) + +#define DO_ALGO_0() do { \ + DO_FEEDBACK(); \ + in1 += CH->S0_OUT[0]; \ + in2 += SIN_TAB[(in1 >> SIN_LBITS) & SIN_MASK][en1]; \ + in3 += SIN_TAB[(in2 >> SIN_LBITS) & SIN_MASK][en2]; \ + CH->OUTd = (SIN_TAB[(in3 >> SIN_LBITS) & SIN_MASK][en3]) >> OUT_SHIFT; \ +} while (0) + +#define DO_ALGO_1() do { \ + DO_FEEDBACK(); \ + in2 += CH->S0_OUT[0] + SIN_TAB[(in1 >> SIN_LBITS) & SIN_MASK][en1]; \ + in3 += SIN_TAB[(in2 >> SIN_LBITS) & SIN_MASK][en2]; \ + CH->OUTd = (SIN_TAB[(in3 >> SIN_LBITS) & SIN_MASK][en3]) >> OUT_SHIFT; \ +} while (0) + +#define DO_ALGO_2() do { \ + DO_FEEDBACK(); \ + in2 += SIN_TAB[(in1 >> SIN_LBITS) & SIN_MASK][en1]; \ + in3 += CH->S0_OUT[0] + SIN_TAB[(in2 >> SIN_LBITS) & SIN_MASK][en2]; \ + CH->OUTd = (SIN_TAB[(in3 >> SIN_LBITS) & SIN_MASK][en3]) >> OUT_SHIFT; \ +} while (0) + +#define DO_ALGO_3() do { \ + DO_FEEDBACK(); \ + in1 += CH->S0_OUT[0]; \ + in3 += SIN_TAB[(in1 >> SIN_LBITS) & SIN_MASK][en1] + \ + SIN_TAB[(in2 >> SIN_LBITS) & SIN_MASK][en2]; \ + CH->OUTd = (SIN_TAB[(in3 >> SIN_LBITS) & SIN_MASK][en3]) >> OUT_SHIFT; \ +} while (0) + +#define DO_ALGO_4() do { \ + DO_FEEDBACK(); \ + in1 += CH->S0_OUT[0]; \ + in3 += SIN_TAB[(in2 >> SIN_LBITS) & SIN_MASK][en2]; \ + CH->OUTd = ((int)SIN_TAB[(in3 >> SIN_LBITS) & SIN_MASK][en3] + \ + (int)SIN_TAB[(in1 >> SIN_LBITS) & SIN_MASK][en1]) >> OUT_SHIFT; \ + DO_LIMIT(); \ +} while (0) + +#define DO_ALGO_5() do { \ + DO_FEEDBACK(); \ + in1 += CH->S0_OUT[0]; \ + in2 += CH->S0_OUT[0]; \ + in3 += CH->S0_OUT[0]; \ + CH->OUTd = ((int)SIN_TAB[(in3 >> SIN_LBITS) & SIN_MASK][en3] + \ + (int)SIN_TAB[(in1 >> SIN_LBITS) & SIN_MASK][en1] + \ + (int)SIN_TAB[(in2 >> SIN_LBITS) & SIN_MASK][en2]) >> OUT_SHIFT; \ + DO_LIMIT(); \ +} while (0) + +#define DO_ALGO_6() do { \ + DO_FEEDBACK(); \ + in1 += CH->S0_OUT[0]; \ + CH->OUTd = ((int)SIN_TAB[(in3 >> SIN_LBITS) & SIN_MASK][en3] + \ + (int)SIN_TAB[(in1 >> SIN_LBITS) & SIN_MASK][en1] + \ + (int)SIN_TAB[(in2 >> SIN_LBITS) & SIN_MASK][en2]) >> OUT_SHIFT; \ + DO_LIMIT(); \ +} while (0) + +#define DO_ALGO_7() do { \ + DO_FEEDBACK(); \ + CH->OUTd = ((int)SIN_TAB[(in3 >> SIN_LBITS) & SIN_MASK][en3] + \ + (int)SIN_TAB[(in1 >> SIN_LBITS) & SIN_MASK][en1] + \ + (int)SIN_TAB[(in2 >> SIN_LBITS) & SIN_MASK][en2] + \ + CH->S0_OUT[0]) >> OUT_SHIFT; \ + DO_LIMIT(); \ +} while (0) + +#define DO_OUTPUT() do { \ + bufL[i] += (int)((CH->OUTd * CH->PANVolumeL / 65535) & CH->LEFT); \ + bufR[i] += (int)((CH->OUTd * CH->PANVolumeR / 65535) & CH->RIGHT); \ +} while (0) + +#define DO_OUTPUT_INT0() do { \ + if ((int_cnt += state.Inter_Step) & 0x04000) { \ + int_cnt &= 0x3FFF; \ + bufL[i] += (int)((CH->OUTd * CH->PANVolumeL / 65535) & CH->LEFT); \ + bufR[i] += (int)((CH->OUTd * CH->PANVolumeR / 65535) & CH->RIGHT); \ + } else { \ + i--; \ + } \ +} while (0) + +#define DO_OUTPUT_INT1() do { \ + CH->Old_OUTd = (CH->OUTd + CH->Old_OUTd) >> 1; \ + if ((int_cnt += state.Inter_Step) & 0x04000) { \ + int_cnt &= 0x3FFF; \ + bufL[i] += (int)((CH->Old_OUTd * CH->PANVolumeL / 65535) & CH->LEFT); \ + bufR[i] += (int)((CH->Old_OUTd * CH->PANVolumeR / 65535) & CH->RIGHT); \ + } else { \ + i--; \ + } \ +} while (0) + +#define DO_OUTPUT_INT2() do { \ + if ((int_cnt += state.Inter_Step) & 0x04000) { \ + int_cnt &= 0x3FFF; \ + CH->Old_OUTd = (CH->OUTd + CH->Old_OUTd) >> 1; \ + bufL[i] += (int)((CH->Old_OUTd * CH->PANVolumeL / 65535) & CH->LEFT); \ + bufR[i] += (int)((CH->Old_OUTd * CH->PANVolumeR / 65535) & CH->RIGHT); \ + } else { \ + i--; \ + } \ \ + CH->Old_OUTd = CH->OUTd; \ +} while (0) + +#define DO_OUTPUT_INT() do { \ + if ((int_cnt += state.Inter_Step) & 0x04000) { \ + int_cnt &= 0x3FFF; \ + CH->Old_OUTd = (((int_cnt ^ 0x3FFF) * CH->OUTd) + \ + (int_cnt * CH->Old_OUTd)) >> 14; \ + bufL[i] += (int)((CH->Old_OUTd * CH->PANVolumeL / 65535) & CH->LEFT); \ + bufR[i] += (int)((CH->Old_OUTd * CH->PANVolumeR / 65535) & CH->RIGHT); \ + } else { \ + i--; \ + } \ + CH->Old_OUTd = CH->OUTd; \ +} while (0) + +template +inline void Ym2612Private::T_Update_Chan(channel_t *CH, int32_t *bufL, int32_t *bufR, int length) +{ + // Check if the channel has reached the end of the update. + { + int not_end = (CH->_SLOT[S3].Ecnt - ENV_END); + + // Special cases. + // Copied from Game_Music_Emu v0.5.2. + if (algo == 7) + not_end |= (CH->_SLOT[S0].Ecnt - ENV_END); + if (algo >= 5) + not_end |= (CH->_SLOT[S2].Ecnt - ENV_END); + if (algo >= 4) + not_end |= (CH->_SLOT[S1].Ecnt - ENV_END); + + if (not_end == 0) + return; + } + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "Algo %d len = %d", algo, length); + + for (int i = 0; i < length; i++) { + int in0, in1, in2, in3; // current phase calculation + int en0, en1, en2, en3; // current envelope calculation + + GET_CURRENT_PHASE(); + UPDATE_PHASE(); + GET_CURRENT_ENV(); + UPDATE_ENV(); + + assert(algo >= 0 && algo <= 7); + switch (algo) { + case 0: + DO_ALGO_0(); + break; + case 1: + DO_ALGO_1(); + break; + case 2: + DO_ALGO_2(); + break; + case 3: + DO_ALGO_3(); + break; + case 4: + DO_ALGO_4(); + break; + case 5: + DO_ALGO_5(); + break; + case 6: + DO_ALGO_6(); + break; + case 7: + DO_ALGO_7(); + break; + default: + break; + } + + DO_OUTPUT(); + } +} + +template +inline void Ym2612Private::T_Update_Chan_LFO(channel_t *CH, int32_t *bufL, int32_t *bufR, int length) +{ + // Check if the channel has reached the end of the update. + { + int not_end = (CH->_SLOT[S3].Ecnt - ENV_END); + + // Special cases. + // Copied from Game_Music_Emu v0.5.2. + if (algo == 7) + not_end |= (CH->_SLOT[S0].Ecnt - ENV_END); + if (algo >= 5) + not_end |= (CH->_SLOT[S2].Ecnt - ENV_END); + if (algo >= 4) + not_end |= (CH->_SLOT[S1].Ecnt - ENV_END); + + if (not_end == 0) + return; + } + + int env_LFO, freq_LFO; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "Algo %d LFO len = %d", algo, length); + + for (int i = 0; i < length; i++) { + int in0, in1, in2, in3; // current phase calculation + int en0, en1, en2, en3; // current envelope calculation + + GET_CURRENT_PHASE(); + UPDATE_PHASE_LFO(); + GET_CURRENT_ENV_LFO(); + UPDATE_ENV(); + + assert(algo >= 0 && algo <= 7); + switch (algo) { + case 0: + DO_ALGO_0(); + break; + case 1: + DO_ALGO_1(); + break; + case 2: + DO_ALGO_2(); + break; + case 3: + DO_ALGO_3(); + break; + case 4: + DO_ALGO_4(); + break; + case 5: + DO_ALGO_5(); + break; + case 6: + DO_ALGO_6(); + break; + case 7: + DO_ALGO_7(); + break; + default: + break; + } + + DO_OUTPUT(); + } +} + +/****************************************************** + * Interpolated output * + *****************************************************/ + +template +inline void Ym2612Private::T_Update_Chan_Int(channel_t *CH, int32_t *bufL, int32_t *bufR, int length) +{ + // Check if the channel has reached the end of the update. + { + int not_end = (CH->_SLOT[S3].Ecnt - ENV_END); + + // Special cases. + // Copied from Game_Music_Emu v0.5.2. + if (algo == 7) + not_end |= (CH->_SLOT[S0].Ecnt - ENV_END); + if (algo >= 5) + not_end |= (CH->_SLOT[S2].Ecnt - ENV_END); + if (algo >= 4) + not_end |= (CH->_SLOT[S1].Ecnt - ENV_END); + + if (not_end == 0) + return; + } + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "Algo %d Int len = %d", algo, length); + + int_cnt = state.Inter_Cnt; + + for (int i = 0; i < length; i++) { + int in0, in1, in2, in3; // current phase calculation + int en0, en1, en2, en3; // current envelope calculation + + GET_CURRENT_PHASE(); + UPDATE_PHASE(); + GET_CURRENT_ENV(); + UPDATE_ENV(); + + assert(algo >= 0 && algo <= 7); + switch (algo) { + case 0: + DO_ALGO_0(); + break; + case 1: + DO_ALGO_1(); + break; + case 2: + DO_ALGO_2(); + break; + case 3: + DO_ALGO_3(); + break; + case 4: + DO_ALGO_4(); + break; + case 5: + DO_ALGO_5(); + break; + case 6: + DO_ALGO_6(); + break; + case 7: + DO_ALGO_7(); + break; + default: + break; + } + + DO_OUTPUT_INT(); + } +} + +template +inline void Ym2612Private::T_Update_Chan_LFO_Int(channel_t *CH, int32_t *bufL, int32_t *bufR, int length) +{ + // Check if the channel has reached the end of the update. + { + int not_end = (CH->_SLOT[S3].Ecnt - ENV_END); + + // Special cases. + // Copied from Game_Music_Emu v0.5.2. + if (algo == 7) + not_end |= (CH->_SLOT[S0].Ecnt - ENV_END); + if (algo >= 5) + not_end |= (CH->_SLOT[S2].Ecnt - ENV_END); + if (algo >= 4) + not_end |= (CH->_SLOT[S1].Ecnt - ENV_END); + + if (not_end == 0) + return; + } + + int_cnt = state.Inter_Cnt; + int env_LFO, freq_LFO; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "Algo %d LFO Int len = %d", algo, length); + + for (int i = 0; i < length; i++) { + int in0, in1, in2, in3; // current phase calculation + int en0, en1, en2, en3; // current envelope calculation + + GET_CURRENT_PHASE(); + UPDATE_PHASE_LFO(); + GET_CURRENT_ENV_LFO(); + UPDATE_ENV(); + + assert(algo >= 0 && algo <= 7); + switch (algo) { + case 0: + DO_ALGO_0(); + break; + case 1: + DO_ALGO_1(); + break; + case 2: + DO_ALGO_2(); + break; + case 3: + DO_ALGO_3(); + break; + case 4: + DO_ALGO_4(); + break; + case 5: + DO_ALGO_5(); + break; + case 6: + DO_ALGO_6(); + break; + case 7: + DO_ALGO_7(); + break; + default: + break; + } + + DO_OUTPUT_INT(); + } +} + +/** + * Update Channel function. + * Replaces the UPDATE_CHAN function pointer table. + * NOTE: This will probably be slower than the function pointer table. + * TODO: Figure out how to optimize it! + */ +void Ym2612Private::Update_Chan(int algo_type, channel_t *CH, int32_t *bufL, int32_t *bufR, int length) +{ + switch (algo_type & 0x1F) { + case 0x00: T_Update_Chan<0>(CH, bufL, bufR, length); break; + case 0x01: T_Update_Chan<1>(CH, bufL, bufR, length); break; + case 0x02: T_Update_Chan<2>(CH, bufL, bufR, length); break; + case 0x03: T_Update_Chan<3>(CH, bufL, bufR, length); break; + case 0x04: T_Update_Chan<4>(CH, bufL, bufR, length); break; + case 0x05: T_Update_Chan<5>(CH, bufL, bufR, length); break; + case 0x06: T_Update_Chan<6>(CH, bufL, bufR, length); break; + case 0x07: T_Update_Chan<7>(CH, bufL, bufR, length); break; + + case 0x08: T_Update_Chan_LFO<0>(CH, bufL, bufR, length); break; + case 0x09: T_Update_Chan_LFO<1>(CH, bufL, bufR, length); break; + case 0x0A: T_Update_Chan_LFO<2>(CH, bufL, bufR, length); break; + case 0x0B: T_Update_Chan_LFO<3>(CH, bufL, bufR, length); break; + case 0x0C: T_Update_Chan_LFO<4>(CH, bufL, bufR, length); break; + case 0x0D: T_Update_Chan_LFO<5>(CH, bufL, bufR, length); break; + case 0x0E: T_Update_Chan_LFO<6>(CH, bufL, bufR, length); break; + case 0x0F: T_Update_Chan_LFO<7>(CH, bufL, bufR, length); break; + + case 0x10: T_Update_Chan_Int<0>(CH, bufL, bufR, length); break; + case 0x11: T_Update_Chan_Int<1>(CH, bufL, bufR, length); break; + case 0x12: T_Update_Chan_Int<2>(CH, bufL, bufR, length); break; + case 0x13: T_Update_Chan_Int<3>(CH, bufL, bufR, length); break; + case 0x14: T_Update_Chan_Int<4>(CH, bufL, bufR, length); break; + case 0x15: T_Update_Chan_Int<5>(CH, bufL, bufR, length); break; + case 0x16: T_Update_Chan_Int<6>(CH, bufL, bufR, length); break; + case 0x17: T_Update_Chan_Int<7>(CH, bufL, bufR, length); break; + + case 0x18: T_Update_Chan_LFO_Int<0>(CH, bufL, bufR, length); break; + case 0x19: T_Update_Chan_LFO_Int<1>(CH, bufL, bufR, length); break; + case 0x1A: T_Update_Chan_LFO_Int<2>(CH, bufL, bufR, length); break; + case 0x1B: T_Update_Chan_LFO_Int<3>(CH, bufL, bufR, length); break; + case 0x1C: T_Update_Chan_LFO_Int<4>(CH, bufL, bufR, length); break; + case 0x1D: T_Update_Chan_LFO_Int<5>(CH, bufL, bufR, length); break; + case 0x1E: T_Update_Chan_LFO_Int<6>(CH, bufL, bufR, length); break; + case 0x1F: T_Update_Chan_LFO_Int<7>(CH, bufL, bufR, length); break; + + default: + break; + } +} + +/*********************************************** + * Public functions. * + ***********************************************/ + +/** Ym2612 **/ + +Ym2612::Ym2612() + : d(new Ym2612Private(this)) +{ + // TODO: Some initialization should go here! + m_writeLen = 0; + m_enabled = true; // TODO: Make this customizable. + m_dacEnabled = true; // TODO: Make this customizable. + m_improved = true; // TODO: Make this customizable. +} + +Ym2612::Ym2612(int clock, int rate) + : d(new Ym2612Private(this)) +{ + // TODO: Some initialization should go here! + m_writeLen = 0; + m_enabled = true; // TODO: Make this customizable. + m_dacEnabled = true; // TODO: Make this customizable. + m_improved = true; // TODO: Make this customizable. + + reInit(clock, rate); +} + +Ym2612::~Ym2612() +{ + delete d; +} + +/** + * (Re-)Initialize the YM2612. + * @param clock YM2612 clock frequency. + * @param rate Sound rate. + * @return 0 on success; non-zero on error. + */ +// Initialisation de l'émulateur YM2612 +int Ym2612::reInit(int clock, int rate) +{ + assert(rate > 0); + assert(clock > rate); + if (rate <= 0 || clock <= rate) + return -1; + + // Clear the state struct. + memset(&d->state, 0, sizeof(d->state)); + d->state.Clock = clock; + d->state.Rate = rate; + + // 144 = 12 * (prescale * 2) = 12 * 6 * 2 + // prescale set to 6 by default + + d->state.Frequence = ((double)(d->state.Clock) / (double)(d->state.Rate)) / 144.0; + d->state.TimerBase = (int)(d->state.Frequence * 4096.0); + + if (m_improved && (d->state.Frequence > 1.0)) { + d->state.Inter_Step = (unsigned int)((1.0 / d->state.Frequence) * (double)(0x4000)); + d->state.Inter_Cnt = 0; + + // We recalculate rate and frequence after interpolation + d->state.Rate = (int)(d->state.Clock / 144.0); + d->state.Frequence = 1.0; + } else { + // No interpolation. + d->state.Inter_Step = 0x4000; + d->state.Inter_Cnt = 0; + } + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG2, +// "YM2612 frequency = %g, rate = %d, interp step = %.8X", +// d->state.Frequence, d->state.Rate, d->state.Inter_Step); + + // Frequency Step table. + for (int i = 0; i < 2048; i++) { + double x = (double)(i) * d->state.Frequence; + #if ((SIN_LBITS + SIN_HBITS - (21 - 7)) < 0) + x /= (double) (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS)); + #else + x *= (double) (1 << (SIN_LBITS + SIN_HBITS - (21 - 7))); + #endif + x /= 2.0; // because MUL = value * 2 + d->FINC_TAB[i] = (unsigned int)x; + } + + // Attack and Decay Rate tables. + // Entries 0-3 are always 0. + for (int i = 0; i < 4; i++) { + d->AR_TAB[i] = 0; + d->DR_TAB[i] = 0; + } + + // Attack and Decay Rate tables. + // Calculate entries 32-63. + for (int i = 0; i < 60; i++) { + double x = d->state.Frequence; + + x *= 1.0 + ((i & 3) * 0.25); // bits 0-1 : x1.00, x1.25, x1.50, x1.75 + x *= (double) (1 << ((i >> 2))); // bits 2-5 : shift bits (x2^0 - x2^15) + x *= (double) (d->ENV_LENGTH << ENV_LBITS); // on ajuste pour le tableau ENV_TAB + + d->AR_TAB[i + 4] = (unsigned int) (x / d->AR_RATE); + d->DR_TAB[i + 4] = (unsigned int) (x / d->DR_RATE); + } + + // Attack and Decay Rate tables. + // Entries 64-95 are copies of entry 63. + for (int i = 64; i < 96; i++) { + d->AR_TAB[i] = d->AR_TAB[63]; + d->DR_TAB[i] = d->DR_TAB[63]; + } + + // Detune table. + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 32; j++) { + double x; + #if ((SIN_LBITS + SIN_HBITS - 21) < 0) + x = (double)d->DT_DEF_TAB[i][j] * d->state.Frequence / + (double)(1 << (21 - SIN_LBITS - SIN_HBITS)); + #else + x = (double)d->DT_DEF_TAB[i][j] * d->state.Frequence * + (double)(1 << (SIN_LBITS + SIN_HBITS - 21)); + #endif + + d->DT_TAB[i + 0][j] = (int) x; + d->DT_TAB[i + 4][j] = (int) -x; + } + } + + // LFO step table. + // TODO: Move to a static class variable? + static const double LFO_BITS[8] = { + 3.98, 5.56, 6.02, 6.37, 6.88, 9.63, 48.1, 72.2 + }; + + double j = (d->state.Rate * d->state.Inter_Step) / 0x4000; + for (int i = 0; i < 8; i++) { + d->LFO_INC_TAB[i] = (unsigned int) (LFO_BITS[i] * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); + } + + + // Reset the YM2612. + reset(); + return 0; +} + +void Ym2612::write_pan(int channel, int data) +{ + d->state.CHANNEL[channel].PANVolumeL = panlawtable[data & 0x7F]; + d->state.CHANNEL[channel].PANVolumeR = panlawtable[0x7F - (data & 0x7F)]; +} + +/** + * Reset the YM2612. + */ +void Ym2612::reset(void) +{ +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG1, +// "Starting reseting YM2612 ..."); + + d->state.LFOcnt = 0; + d->state.TimerA = 0; + d->state.TimerAL = 0; + d->state.TimerAcnt = 0; + d->state.TimerB = 0; + d->state.TimerBL = 0; + d->state.TimerBcnt = 0; + d->state.DAC = 0; + d->state.DACdata = 0; + + d->state.status = 0; + + d->state.OPNAadr = 0; + d->state.OPNBadr = 0; + d->state.Inter_Cnt = 0; + + for (int i = 0; i < 6; i++) { + d->state.CHANNEL[i].Old_OUTd = 0; + d->state.CHANNEL[i].OUTd = 0; + d->state.CHANNEL[i].LEFT = 0xFFFFFFFF; + d->state.CHANNEL[i].RIGHT = 0xFFFFFFFF; + d->state.CHANNEL[i].ALGO = 0;; + d->state.CHANNEL[i].FB = 31; + d->state.CHANNEL[i].FMS = 0; + d->state.CHANNEL[i].AMS = 0; + + + d->state.CHANNEL[i].PANVolumeL = 46340; + d->state.CHANNEL[i].PANVolumeR = 46340; + + for (int j = 0; j < 4; j++) { + d->state.CHANNEL[i].S0_OUT[j] = 0; + d->state.CHANNEL[i].FNUM[j] = 0; + d->state.CHANNEL[i].FOCT[j] = 0; + d->state.CHANNEL[i].KC[j] = 0; + + d->state.CHANNEL[i]._SLOT[j].Fcnt = 0; + d->state.CHANNEL[i]._SLOT[j].Finc = 0; + d->state.CHANNEL[i]._SLOT[j].Ecnt = d->ENV_END; // Put it at the end of Decay phase... + d->state.CHANNEL[i]._SLOT[j].Einc = 0; + d->state.CHANNEL[i]._SLOT[j].Ecmp = 0; + d->state.CHANNEL[i]._SLOT[j].Ecurp = d->RELEASE; + + d->state.CHANNEL[i]._SLOT[j].ChgEnM = 0; + } + } + + // Initialize registers to 0xFF. + memset(d->state.REG, 0xFF, sizeof(d->state.REG)); + + // Initialize other registers to 0xC0. + for (int i = 0xB6; i >= 0xB4; i--) { + this->write(0, (uint8_t)i); + this->write(2, (uint8_t)i); + this->write(1, 0xC0); + this->write(3, 0xC0); + } + + // Initialize more registers to 0x00. + for (int i = 0xB2; i >= 0x22; i--) { + this->write(0, (uint8_t)i); + this->write(2, (uint8_t)i); + this->write(1, 0x00); + this->write(3, 0x00); + } + + // Initialize DAC to 0x80. (silence) + this->write(0, 0x2A); + this->write(1, 0x80); + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG1, +// "Finishing reseting YM2612 ..."); +} + +/** + * Read the YM2612 status register. + * @return YM2612 status register. + */ +uint8_t Ym2612::read(void) const +{ +#if 0 + static int cnt = 0; + + if (cnt++ == 50) + { + cnt = 0; + return YM2612.Status; + } + else return YM2612.Status | 0x80; +#endif + + /** + * READ DATA is the same for all four addresses. + * Format: [BUSY X X X X X OVRA OVRB] + * BUSY: If 1, YM2612 is busy and cannot accept new data. + * OVRA: If 1, timer A has overflowed. + * OVRB: If 1, timer B has overflowed. + */ + return (uint8_t)d->state.status; +} + +/** + * Write to a YM2612 register. + * @param address Address. + * @param data Data. + * @return 0 on success; non-zero on error. (TODO: This isn't used by anything!) + */ +int Ym2612::write(unsigned int address, uint8_t data) +{ + /** + * Possible addresses: + * - 0: Bank 0 register number. + * - 1: Bank 0 data. + * - 2: Bank 1 register number. + * - 3: Bank 1 data. + */ + + int reg_num; + switch (address & 0x03) { + case 0: + d->state.OPNAadr = data; + break; + + case 1: + // Trivial optimization for DAC. + if (d->state.OPNAadr == 0x2A) { + d->state.DACdata = ((int)data - 0x80) << 7; + return 0; + } + + reg_num = d->state.OPNAadr & 0xF0; + if (reg_num >= 0x30) { + if (d->state.REG[0][d->state.OPNAadr] == data) { + // Don't bother doing anything if the + // register has the same value. + // TODO: Is this correct? + return 2; + } + d->state.REG[0][d->state.OPNAadr] = data; + + if (reg_num < 0xA0) { + d->SLOT_SET(d->state.OPNAadr, data); + } else { + d->CHANNEL_SET(d->state.OPNAadr, data); + } + } else { + // YM2612 control registers. + d->state.REG[0][d->state.OPNAadr] = data; + + /* TODO: Reimplement GYM dumping support in LibGens. + if ((GYM_Dumping) && + ((d->state.OPNAadr == 0x22) || + (d->state.OPNAadr == 0x27) || + (d->state.OPNAadr == 0x28))) + { + gym_dump_update(1, (uint8_t)d->state.OPNAadr, data); + } + */ + + d->YM_SET(d->state.OPNAadr, data); + } + break; + + case 2: + d->state.OPNBadr = data; + break; + + case 3: + reg_num = d->state.OPNBadr & 0xF0; + + if (reg_num >= 0x30) { + if (d->state.REG[1][d->state.OPNBadr] == data) { + // Don't bother doing anything if the + // register has the same value. + // TODO: Is this correct? + return 2; + } + d->state.REG[1][d->state.OPNBadr] = data; + + /* TODO: Reimplement GYM dumping support in LibGens. + if (GYM_Dumping) + gym_dump_update(2, (uint8_t)d->state.OPNBadr, data); + */ + + if (reg_num < 0xA0) { + d->SLOT_SET(d->state.OPNBadr + 0x100, data); + } else { + d->CHANNEL_SET(d->state.OPNBadr + 0x100, data); + } + } else { + // Invalid register. + // TODO: Save the value anyway? + return 1; + } + break; + } + + return 0; +} + +/** + * Update the YM2612 audio output. + * @param bufL Left audio buffer. (16-bit; int32_t is used for saturation.) + * @param bufR Right audio buffer. (16-bit; int32_t is used for saturation.) + * @param length Length to write. + */ +void Ym2612::update(int32_t *bufL, int32_t *bufR, int length) +{ +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG4, +// "Starting generating sound..."); + + // Mise à jour des pas des compteurs-fréquences s'ils ont été modifiés + if (d->state.CHANNEL[0]._SLOT[0].Finc == -1) { + d->CALC_FINC_CH(&d->state.CHANNEL[0]); + } + if (d->state.CHANNEL[1]._SLOT[0].Finc == -1) { + d->CALC_FINC_CH(&d->state.CHANNEL[1]); + } + if (d->state.CHANNEL[2]._SLOT[0].Finc == -1) { + if (d->state.Mode & 0x40) { + d->CALC_FINC_SL(&(d->state.CHANNEL[2]._SLOT[d->S0]), + d->FINC_TAB[d->state.CHANNEL[2].FNUM[2]] >> (7 - d->state.CHANNEL[2].FOCT[2]), + d->state.CHANNEL[2].KC[2]); + d->CALC_FINC_SL(&(d->state.CHANNEL[2]._SLOT[d->S1]), + d->FINC_TAB[d->state.CHANNEL[2].FNUM[3]] >> (7 - d->state.CHANNEL[2].FOCT[3]), + d->state.CHANNEL[2].KC[3]); + d->CALC_FINC_SL(&(d->state.CHANNEL[2]._SLOT[d->S2]), + d->FINC_TAB[d->state.CHANNEL[2].FNUM[1]] >> (7 - d->state.CHANNEL[2].FOCT[1]), + d->state.CHANNEL[2].KC[1]); + d->CALC_FINC_SL(&(d->state.CHANNEL[2]._SLOT[d->S3]), + d->FINC_TAB[d->state.CHANNEL[2].FNUM[0]] >> (7 - d->state.CHANNEL[2].FOCT[0]), + d->state.CHANNEL[2].KC[0]); + } else { + d->CALC_FINC_CH(&d->state.CHANNEL[2]); + } + } + if (d->state.CHANNEL[3]._SLOT[0].Finc == -1) { + d->CALC_FINC_CH(&d->state.CHANNEL[3]); + } + if (d->state.CHANNEL[4]._SLOT[0].Finc == -1) { + d->CALC_FINC_CH(&d->state.CHANNEL[4]); + } + if (d->state.CHANNEL[5]._SLOT[0].Finc == -1) { + d->CALC_FINC_CH(&d->state.CHANNEL[5]); + } + + /* + d->CALC_FINC_CH(&d->state.CHANNEL[0]); + d->CALC_FINC_CH(&d->state.CHANNEL[1]); + if (d->state.Mode & 0x40) { + d->CALC_FINC_SL(&(d->state.CHANNEL[2].SLOT[0]), d->FINC_TAB[d->state.CHANNEL[2].FNUM[2]] >> (7 - d->state.CHANNEL[2].FOCT[2]), YM2612.CHANNEL[2].KC[2]); + d->CALC_FINC_SL(&(d->state.CHANNEL[2].SLOT[1]), d->FINC_TAB[d->state.CHANNEL[2].FNUM[3]] >> (7 - d->state.CHANNEL[2].FOCT[3]), YM2612.CHANNEL[2].KC[3]); + d->CALC_FINC_SL(&(d->state.CHANNEL[2].SLOT[2]), d->FINC_TAB[d->state.CHANNEL[2].FNUM[1]] >> (7 - d->state.CHANNEL[2].FOCT[1]), YM2612.CHANNEL[2].KC[1]); + d->CALC_FINC_SL(&(d->state.CHANNEL[2].SLOT[3]), d->FINC_TAB[d->state.CHANNEL[2].FNUM[0]] >> (7 - d->state.CHANNEL[2].FOCT[0]), d->state.CHANNEL[2].KC[0]); + } else { + d->CALC_FINC_CH(&d->state.CHANNEL[2]); + } + d->CALC_FINC_CH(&d->state.CHANNEL[3]); + d->CALC_FINC_CH(&d->state.CHANNEL[4]); + d->CALC_FINC_CH(&d->state.CHANNEL[5]); + */ + + // Determine the algorithm type. + int algo_type; + if (d->state.Inter_Step & 0x04000) { + algo_type = 0; + } else { + algo_type = 16; + } + + if (d->state.LFOinc) { + // Precalculate LFO wav + + for (int i = 0; i < length; i++) { + int j = ((d->state.LFOcnt += d->state.LFOinc) >> LFO_LBITS) & d->LFO_MASK; + + d->LFO_ENV_UP[i] = d->LFO_ENV_TAB[j]; + d->LFO_FREQ_UP[i] = d->LFO_FREQ_TAB[j]; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG4, +// "LFO_ENV_UP[%d] = %d LFO_FREQ_UP[%d] = %d", +// i, d->LFO_ENV_UP[i], i, d->LFO_FREQ_UP[i]); + } + + algo_type |= 8; + } + + d->Update_Chan((d->state.CHANNEL[0].ALGO + algo_type), &(d->state.CHANNEL[0]), bufL, bufR, length); + d->Update_Chan((d->state.CHANNEL[1].ALGO + algo_type), &(d->state.CHANNEL[1]), bufL, bufR, length); + d->Update_Chan((d->state.CHANNEL[2].ALGO + algo_type), &(d->state.CHANNEL[2]), bufL, bufR, length); + d->Update_Chan((d->state.CHANNEL[3].ALGO + algo_type), &(d->state.CHANNEL[3]), bufL, bufR, length); + d->Update_Chan((d->state.CHANNEL[4].ALGO + algo_type), &(d->state.CHANNEL[4]), bufL, bufR, length); + if (!(d->state.DAC)) { + // Update channel 6 only if DAC is disabled. + d->Update_Chan((d->state.CHANNEL[5].ALGO + algo_type), &(d->state.CHANNEL[5]), bufL, bufR, length); + } + + d->state.Inter_Cnt = d->int_cnt; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG4, +// "Finishing generating sound..."); +} + +/** + * Update the YM2612 DAC output and timers. + * @param bufL Left audio buffer. (16-bit; int32_t is used for saturation.) + * @param bufR Right audio buffer. (16-bit; int32_t is used for saturation.) + * @param length Length of the output buffer. + */ +void Ym2612::updateDacAndTimers(int32_t *bufL, int32_t *bufR, int length) +{ + // Update DAC. + if (d->state.DAC && d->state.DACdata && m_dacEnabled) { + for (int i = 0; i < length; i++) { + bufL[i] += (d->state.DACdata & d->state.CHANNEL[5].LEFT); + bufR[i] += (d->state.DACdata & d->state.CHANNEL[5].RIGHT); + } + } + + // Update timers. + int i = d->state.TimerBase * length; + + if (d->state.Mode & 1) { + // Timer A is ON. + //if ((YM2612.TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) + if ((d->state.TimerAcnt -= i) <= 0) { + d->state.status |= (d->state.Mode & 0x04) >> 2; + d->state.TimerAcnt += d->state.TimerAL; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG1, +// "Counter A overflow"); + + if (d->state.Mode & 0x80) { + d->CSM_Key_Control(); + } + } + } + + if (d->state.Mode & 2) { + // Timer B is ON. + //if ((d->state.TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) + if ((d->state.TimerBcnt -= i) <= 0) { + d->state.status |= (d->state.Mode & 0x08) >> 2; + d->state.TimerBcnt += d->state.TimerBL; + +// LOG_MSG(ym2612, LOG_MSG_LEVEL_DEBUG1, +// "Counter B overflow"); + } + } +} + +/** + * Update the YM2612 buffer. + */ +void Ym2612::specialUpdate(void) +{ + if (!(m_writeLen > 0 && m_enabled)) + return; + + // Update the sound buffer. + update(m_bufPtrL, m_bufPtrR, m_writeLen); + m_writeLen = 0; + +#if 0 + // TODO: Don't use EmuContext here... + int line_num = 1; + EmuContext *context = EmuContext::Instance(); + if (context != nullptr) + line_num = (context->m_vdp->VDP_Lines.currentLine + 1); + + // Determine the new starting position. + int writePos = SoundMgr::GetWritePos(line_num); + + // Update the PSG buffer pointers. + m_bufPtrL = &SoundMgr::ms_SegBufL[writePos]; + m_bufPtrR = &SoundMgr::ms_SegBufR[writePos]; +#endif +} + +/** + * Get the value of a register. + * @param regID Register ID. + * @return Value of the register. + */ +int Ym2612::getReg(int regID) const +{ + if (regID < 0 || regID >= 0x200) + return -1; + + return d->state.REG[(regID >> 8) & 1][regID & 0xFF]; +} + +/** + * Reset the YM2612 buffer pointers. + */ +void Ym2612::resetBufferPtrs(int32_t *bufPtrL, int32_t *bufPtrR) +{ + m_bufPtrL = bufPtrL; + m_bufPtrR = bufPtrR; +} + +/* end */ + +} + diff --git a/thirdparty/opnmidi/chips/gens/Ym2612.hpp b/thirdparty/opnmidi/chips/gens/Ym2612.hpp new file mode 100644 index 000000000..76121c97c --- /dev/null +++ b/thirdparty/opnmidi/chips/gens/Ym2612.hpp @@ -0,0 +1,95 @@ +/*************************************************************************** + * libgens: Gens Emulation Library. * + * Ym2612.hpp: Yamaha YM2612 FM synthesis chip emulator. * + * * + * Copyright (c) 1999-2002 by Stéphane Dallongeville * + * Copyright (c) 2003-2004 by Stéphane Akhoun * + * Copyright (c) 2008-2015 by David Korth * + * * + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 2 of the License, or (at your * + * option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef __LIBGENS_SOUND_YM2612_HPP__ +#define __LIBGENS_SOUND_YM2612_HPP__ + +#include + +namespace LibGens { + +class Ym2612Private; +class Ym2612 +{ + public: + Ym2612(); + Ym2612(int clock, int rate); + ~Ym2612(); + + protected: + friend class Ym2612Private; + Ym2612Private *const d; + private: + // Q_DISABLE_COPY() equivalent. + // TODO: Add LibGens-specific version of Q_DISABLE_COPY(). + Ym2612(const Ym2612 &); + Ym2612 &operator=(const Ym2612 &); + + public: + int reInit(int clock, int rate); + void reset(void); + + uint8_t read(void) const; + int write(unsigned int address, uint8_t data); + void write_pan(int channel, int data); + void update(int32_t *bufL, int32_t *bufR, int length); + + // Properties. + // TODO: Read-only for now. + bool enabled(void) const { return m_enabled; } + bool dacEnabled(void) const { return m_dacEnabled; } + bool improved(void) const { return m_improved; } + + /** Gens-specific code. **/ + void updateDacAndTimers(int32_t *bufL, int32_t *bufR, int length); + void specialUpdate(void); + int getReg(int regID) const; + + // YM write length. + inline void addWriteLen(int len) + { m_writeLen += len; } + inline void clearWriteLen(void) + { m_writeLen = 0; } + + // Reset buffer pointers. + void resetBufferPtrs(int32_t *bufPtrL, int32_t *bufPtrR); + + protected: + // PSG write length. (for audio output) + int m_writeLen; + bool m_enabled; // YM2612 Enabled + bool m_dacEnabled; // DAC Enabled + bool m_improved; // YM2612 Improved + + // YM buffer pointers. + // TODO: Figure out how to get rid of these! + int32_t *m_bufPtrL; + int32_t *m_bufPtrR; +}; + +/* end */ + +} + +#endif /* __LIBGENS_SOUND_YM2612_HPP__ */ + diff --git a/thirdparty/opnmidi/chips/gens/Ym2612_p.hpp b/thirdparty/opnmidi/chips/gens/Ym2612_p.hpp new file mode 100644 index 000000000..2b5dd65fb --- /dev/null +++ b/thirdparty/opnmidi/chips/gens/Ym2612_p.hpp @@ -0,0 +1,328 @@ +/*************************************************************************** + * libgens: Gens Emulation Library. * + * Ym2612.hpp: Yamaha YM2612 FM synthesis chip emulator. (Private class) * + * * + * Copyright (c) 1999-2002 by Stéphane Dallongeville * + * Copyright (c) 2003-2004 by Stéphane Akhoun * + * Copyright (c) 2008-2015 by David Korth * + * * + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 2 of the License, or (at your * + * option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef __LIBGENS_SOUND_YM2612_P_HPP__ +#define __LIBGENS_SOUND_YM2612_P_HPP__ + +// C includes. +#include +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +namespace LibGens { + +class Ym2612; +class Ym2612Private +{ + public: + Ym2612Private(Ym2612 *q); + + protected: + friend class Ym2612; + Ym2612 *const q; + private: + // Q_DISABLE_COPY() equivalent. + // TODO: Add LibGens-specific version of Q_DISABLE_COPY(). + Ym2612Private(const Ym2612Private &); + Ym2612Private &operator=(const Ym2612Private &); + + public: + /** + * Initialize the static tables. + */ + static void doStaticInit(void); + + struct slot_t { + unsigned int *DT; // paramètre detune + int MUL; // paramètre "multiple de fréquence" + int TL; // Total Level = volume lorsque l'enveloppe est au plus haut + int TLL; // Total Level ajusted + int SLL; // Sustin Level (ajusted) = volume où l'enveloppe termine sa première phase de régression + int KSR_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe + int KSR; // Key Scale Rate = cette valeur est calculée par rapport à la fréquence actuelle, elle va influer + // sur les différents paramètres de l'enveloppe comme l'attaque, le decay ... comme dans la réalité ! + int SEG; // Type enveloppe SSG + unsigned int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR]) + unsigned int *DR; // Decay Rate (table pointeur) = Taux pour la régression (DR[KSR]) + unsigned int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR]) + unsigned int *RR; // Release Rate (table pointeur) = Taux pour le relâchement (RR[KSR]) + int Fcnt; // Frequency Count = compteur-fréquence pour déterminer l'amplitude actuelle (SIN[Finc >> 16]) + int Finc; // frequency step = pas d'incrémentation du compteur-fréquence + // plus le pas est grand, plus la fréquence est aïgu (ou haute) + int Ecurp; // Envelope current phase = cette variable permet de savoir dans quelle phase + // de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ... + // en fonction de la valeur de cette variable, on va appeler une fonction permettant + // de mettre à jour l'enveloppe courante. + int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe + int Einc; // Envelope step courant + int Ecmp; // Envelope counter limite pour la prochaine phase + int EincA; // Envelope step for Attack = pas d'incrémentation du compteur durant la phase d'attaque + // cette valeur est égal à AR[KSR] + int EincD; // Envelope step for Decay = pas d'incrémentation du compteur durant la phase de regression + // cette valeur est égal à DR[KSR] + int EincS; // Envelope step for Sustain = pas d'incrémentation du compteur durant la phase de maintenue + // cette valeur est égal à SR[KSR] + int EincR; // Envelope step for Release = pas d'incrémentation du compteur durant la phase de relâchement + // cette valeur est égal à RR[KSR] + int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entrée + // d'un autre ou carrement à la sortie de la voie + int INd; // input data of the slot = données en entrée du slot + int ChgEnM; // Change envelop mask. + int AMS; // AMS depth level of this SLOT = degré de modulation de l'amplitude par le LFO + int AMSon; // AMS enable flag = drapeau d'activation de l'AMS + }; + + struct channel_t { + int S0_OUT[4]; // anciennes sorties slot 0 (pour le feed back) + int Old_OUTd; // ancienne sortie de la voie (son brut) + int OUTd; // sortie de la voie (son brut) + int LEFT; // LEFT enable flag + int RIGHT; // RIGHT enable flag + int ALGO; // Algorythm = détermine les connections entre les opérateurs + int FB; // shift count of self feed back = degré de "Feed-Back" du SLOT 1 (il est son unique entrée) + int FMS; // Fréquency Modulation Sensitivity of channel = degré de modulation de la fréquence sur la voie par le LFO + int AMS; // Amplitude Modulation Sensitivity of channel = degré de modulation de l'amplitude sur la voie par le LFO + int FNUM[4]; // hauteur fréquence de la voie (+ 3 pour le mode spécial) + int FOCT[4]; // octave de la voie (+ 3 pour le mode spécial) + int KC[4]; // Key Code = valeur fonction de la fréquence (voir KSR pour les slots, KSR = KC >> KSR_S) + slot_t _SLOT[4]; // four slot.operators = les 4 slots de la voie + int FFlag; // Frequency step recalculation flag + int PANVolumeL; // Left PCM output channel volume + int PANVolumeR; // Right PCM output channel volume + }; + + struct state_t { + int Clock; // Horloge YM2612 + int Rate; // Sample Rate (11025/22050/44100) + int TimerBase; // TimerBase calculation + int status; // YM2612 Status (timer overflow) + int OPNAadr; // addresse pour l'écriture dans l'OPN A (propre à l'émulateur) + int OPNBadr; // addresse pour l'écriture dans l'OPN B (propre à l'émulateur) + int LFOcnt; // LFO counter = compteur-fréquence pour le LFO + int LFOinc; // LFO step counter = pas d'incrémentation du compteur-fréquence du LFO + // plus le pas est grand, plus la fréquence est grande + + int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter + int TimerAL; + int TimerAcnt; // timerA counter = valeur courante du Timer A + int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter + int TimerBL; + int TimerBcnt; // timerB counter = valeur courante du Timer B + int Mode; // Mode actuel des voie 3 et 6 (normal / spécial) + int DAC; // DAC enabled flag + int DACdata; // DAC data + + int dummy; // MSVC++ enforces 8-byte alignment on doubles. This forces said alignment on gcc. + double Frequence; // Fréquence de base, se calcul par rapport à l'horlage et au sample rate + + unsigned int Inter_Cnt; // Interpolation Counter + unsigned int Inter_Step; // Interpolation Step + channel_t CHANNEL[6]; // Les 6 voies du YM2612 + + uint8_t REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif + // cela nous rend le débuggage plus facile + }; + + // YM2612 state. + state_t state; + + // Change it if you need to do long update + static const int MAX_UPDATE_LENGTH = 2000; + + // Gens always uses 16 bits sound (in 32 bits buffer) and do the convertion later if needed. + static const int OUTPUT_BITS = 16; + + /** Various YM2612 definitions needed for table size. **/ + /** TODO: Rework them to be more C++-esque? **/ + // TODO: Signed or unsigned? + + // SIN_LBITS <= 16 + // LFO_HBITS <= 16 + // (SIN_LBITS + SIN_HBITS) <= 26 + // (ENV_LBITS + ENV_HBITS) <= 28 + // (LFO_LBITS + LFO_HBITS) <= 28 + + // NOTE: Needs to be a #define due to conditional use in shifting. + #define SIN_HBITS 12 // Sinus phase counter int part + #define SIN_LBITS (26 - SIN_HBITS) // Sinus phase counter float part (best setting) + #if (SIN_LBITS > 16) + #define SIN_LBITS 16 // Can't be greater than 16 bits + #endif + + // NOTE: Needs to be a #define due to conditional use in shifting. + #define ENV_HBITS 12 // Env phase counter int part + #define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting) + + // NOTE: Needs to be a #define due to conditional use in shifting. + #define LFO_HBITS 10 // LFO phase counter int part + #define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting) + + static const int SIN_LENGTH = (1 << SIN_HBITS); + static const int ENV_LENGTH = (1 << ENV_HBITS); + static const int LFO_LENGTH = (1 << LFO_HBITS); + + static const int TL_LENGTH = (ENV_LENGTH * 3); // Env + TL scaling + LFO + + static const int SIN_MASK = (SIN_LENGTH - 1); + static const int ENV_MASK = (ENV_LENGTH - 1); + static const int LFO_MASK = (LFO_LENGTH - 1); + + // TODO: Change to static const double. + #define ENV_STEP (96.0 / ENV_LENGTH) // ENV_MAX = 96 dB + + static const int ENV_ATTACK = ((ENV_LENGTH * 0) << ENV_LBITS); + static const int ENV_DECAY = ((ENV_LENGTH * 1) << ENV_LBITS); + static const int ENV_END = ((ENV_LENGTH * 2) << ENV_LBITS); + + static const int MAX_OUT_BITS = (SIN_HBITS + SIN_LBITS + 2); // Modulation = -4 <--> +4 + static const int MAX_OUT = ((1 << MAX_OUT_BITS) - 1); + + //Just for tests stuff... + // + //static const double COEF_MOD = 0.5; + //static const int MAX_OUT = ((int) (((1 << MAX_OUT_BITS) - 1) * COEF_MOD)); + + static const int OUT_BITS = (OUTPUT_BITS - 2); + static const int OUT_SHIFT = (MAX_OUT_BITS - OUT_BITS); + static /*const */int LIMIT_CH_OUT/* = ((int) (((1 << OUT_BITS) * 1.5) - 1))*/; + + static int PG_CUT_OFF/* = ((int) (78.0 / ENV_STEP))*/; + static int ENV_CUT_OFF/* = ((int) (68.0 / ENV_STEP))*/; + + static const int AR_RATE = 399128; + static const int DR_RATE = 5514396; + + //static const int AR_RATE = 426136; + //static const int DR_RATE = (AR_RATE * 12); + + static const int LFO_FMS_LBITS = 9; // FIXED (LFO_FMS_BASE gives somethink as 1) + static /*const*/ int LFO_FMS_BASE/* = ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS)))*/; + + enum ADSR { + ATTACK = 0, + DECAY = 1, + SUSTAIN = 2, + RELEASE = 3, + + ADSR_MAX + }; + + enum SlotID { + S0 = 0, + S1 = 2, // Stupid typo of the YM2612 + S2 = 1, + S3 = 3, + + SlotID_MAX + }; + + // Static tables. + static bool isInit; // True if the static tables have been initialized. + static int *SIN_TAB[SIN_LENGTH]; // SINUS TABLE (pointer on TL TABLE) + static int TL_TAB[TL_LENGTH * 2]; // TOTAL LEVEL TABLE (plus and minus) + static unsigned int ENV_TAB[2 * ENV_LENGTH * 8]; // ENV CURVE TABLE (attack & decay) + //static unsigned int ATTACK_TO_DECAY[ENV_LENGTH]; // Conversion from attack to decay phase + static unsigned int DECAY_TO_ATTACK[ENV_LENGTH]; // Conversion from decay to attack phase + + // Member tables. + unsigned int FINC_TAB[2048]; // Frequency step table + + // Rate tables. + // All of these are Member variables, except for NULL_RATE. + // (NULL_RATE consists of all zeroes.) + unsigned int AR_TAB[128]; // Attack rate table. + unsigned int DR_TAB[96]; // Decay rate table. + unsigned int DT_TAB[8][32]; // Detune table. + static unsigned int SL_TAB[16]; // Sustain level table. (STATIC) + static unsigned int NULL_RATE[32]; // Table for NULL rate. (STATIC) + + // LFO tables. (Static class variables) + static int LFO_ENV_TAB[LFO_LENGTH]; // LFO AMS TABLE (adjusted for 11.8 dB) + static int LFO_FREQ_TAB[LFO_LENGTH]; // LFO FMS TABLE + + // LFO temporary tables. (Member variables) + int LFO_ENV_UP[MAX_UPDATE_LENGTH]; // Temporary calculated LFO AMS (adjusted for 11.8 dB) + int LFO_FREQ_UP[MAX_UPDATE_LENGTH]; // Temporary calculated LFO FMS + + // NOTE: INTER_TAB isn't used... + //int INTER_TAB[MAX_UPDATE_LENGTH]; // Interpolation table + + // LFO step table. + unsigned int LFO_INC_TAB[8]; // LFO step table. + + // Envelope function declarations. + static void Env_Attack_Next(slot_t *SL); + static void Env_Decay_Next(slot_t *SL); + static void Env_Substain_Next(slot_t *SL); + static void Env_Release_Next(slot_t *SL); + static void Env_NULL_Next(slot_t *SL); + + typedef void (*Env_Event)(slot_t *SL); + static const Env_Event ENV_NEXT_EVENT[8]; + + // Default detune table. + // FD == F number + static const uint8_t DT_DEF_TAB[4][32]; + + static const uint8_t FKEY_TAB[16]; + static const uint8_t LFO_AMS_TAB[4]; + static const uint8_t LFO_FMS_TAB[8]; + + // Interpolation calculation. + int int_cnt; + + /** Functions for calculating parameters. **/ + static void CALC_FINC_SL(slot_t *SL, int finc, int kc); + void CALC_FINC_CH(channel_t *CH); + + /** Functions for setting values. **/ + static void KEY_ON(channel_t *CH, int nsl); + static void KEY_OFF(channel_t *CH, int nsl); + + void CSM_Key_Control(void); + + int SLOT_SET(int address, uint8_t data); + int CHANNEL_SET(int address, uint8_t data); + int YM_SET(int address, uint8_t data); + + /** Update Channel templates. **/ + template + inline void T_Update_Chan(channel_t *CH, int32_t *bufL, int32_t *bufR, int length); + + template + inline void T_Update_Chan_LFO(channel_t *CH, int32_t *bufL, int32_t *bufR, int length); + + template + inline void T_Update_Chan_Int(channel_t *CH, int32_t *bufL, int32_t *bufR, int length); + + template + inline void T_Update_Chan_LFO_Int(channel_t *CH, int32_t *bufL, int32_t *bufR, int length); + + void Update_Chan(int algo_type, channel_t *CH, int32_t *bufL, int32_t *bufR, int length); +}; + +} + +#endif /* __LIBGENS_SOUND_YM2612_P_HPP__ */ + diff --git a/thirdparty/opnmidi/chips/gens_opn2.cpp b/thirdparty/opnmidi/chips/gens_opn2.cpp new file mode 100644 index 000000000..cff83002b --- /dev/null +++ b/thirdparty/opnmidi/chips/gens_opn2.cpp @@ -0,0 +1,111 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "gens_opn2.h" +#include +#include +#include +#include "gens/Ym2612.hpp" + +#ifndef INT16_MIN +#define INT16_MIN (-0x7fff - 1) +#endif +#ifndef INT16_MAX +#define INT16_MAX 0x7fff +#endif + +GensOPN2::GensOPN2(OPNFamily f) + : OPNChipBaseBufferedT(f), + chip(new LibGens::Ym2612()) +{ + GensOPN2::setRate(m_rate, m_clock); +} + +GensOPN2::~GensOPN2() +{ + delete chip; +} + +void GensOPN2::setRate(uint32_t rate, uint32_t clock) +{ + OPNChipBaseBufferedT::setRate(rate, clock); + uint32_t chipRate = isRunningAtPcmRate() ? rate : nativeRate(); + chip->reInit(clock, chipRate); // implies reset() +} + +void GensOPN2::reset() +{ + OPNChipBaseBufferedT::reset(); + chip->reset(); +} + +void GensOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + LibGens::Ym2612 *chip = this->chip; + + switch (port) + { + case 0: + chip->write(0, addr); + chip->write(1, data); + break; + case 1: + chip->write(2, addr); + chip->write(3, data); + break; + } +} + +void GensOPN2::writePan(uint16_t chan, uint8_t data) +{ + chip->write_pan(static_cast(chan), static_cast(data)); +} + +void GensOPN2::nativeGenerateN(int16_t *output, size_t frames) +{ + enum { maxFrames = 256 }; + assert(frames <= maxFrames); + + LibGens::Ym2612 *chip = this->chip; + int32_t bufLR[2 * maxFrames] = {}; + int32_t *bufL = &bufLR[0]; + int32_t *bufR = &bufLR[maxFrames]; + + chip->resetBufferPtrs(bufL, bufR); + chip->addWriteLen((int)frames); + chip->specialUpdate(); + + //TODO + // chip->updateDacAndTimers(bufL, bufR, frames); + + for (size_t i = 0; i < 2 * frames; ++i) + { + int32_t sample = ((i & 1) ? bufR : bufL)[i / 2]; + sample /= 4; // has too high volume, attenuation needed + sample = (sample < INT16_MIN) ? INT16_MIN : sample; + sample = (sample > INT16_MAX) ? INT16_MAX : sample; + output[i] = (int16_t)sample; + } +} + +const char *GensOPN2::emulatorName() +{ + return "GENS/GS II OPN2"; +} diff --git a/thirdparty/opnmidi/chips/gens_opn2.h b/thirdparty/opnmidi/chips/gens_opn2.h new file mode 100644 index 000000000..9666ec5d1 --- /dev/null +++ b/thirdparty/opnmidi/chips/gens_opn2.h @@ -0,0 +1,45 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GENS_OPN2_H +#define GENS_OPN2_H + +#include "opn_chip_base.h" + +namespace LibGens { class Ym2612; } +class GensOPN2 final : public OPNChipBaseBufferedT +{ + LibGens::Ym2612 *chip; +public: + explicit GensOPN2(OPNFamily f); + ~GensOPN2() override; + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t chan, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerateN(int16_t *output, size_t frames) override; + const char *emulatorName() override; +}; + +#endif // GENS_OPN2_H diff --git a/thirdparty/opnmidi/chips/gx/gx_ym2612.c b/thirdparty/opnmidi/chips/gx/gx_ym2612.c new file mode 100644 index 000000000..e420a2703 --- /dev/null +++ b/thirdparty/opnmidi/chips/gx/gx_ym2612.c @@ -0,0 +1,2271 @@ +/* +** +** software implementation of Yamaha FM sound generator (YM2612/YM3438) +** +** Original code (MAME fm.c) +** +** Copyright (C) 2001, 2002, 2003 Jarek Burczynski (bujar at mame dot net) +** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 1.4 (final beta) +** +** Additional code & fixes by Eke-Eke for Genesis Plus GX +** +** Huge thanks to Nemesis, most of those fixes came from his tests on Sega Genesis hardware +** Additional info from YM2612 die shot analysis by Sauraen +** See http://gendev.spritesmind.net/forum/viewtopic.php?t=386 +** +*/ + +/* +** CHANGELOG: +** +** 08-06-2018 JPCima (OPNMIDI) +** - repaired the multichip support back +** +** 03-12-2017 Eke-Eke (Genesis Plus GX): +** - improved 9-bit DAC emulation accuracy +** - added discrete YM2612 DAC distortion emulation ("ladder effect") +** - replaced configurable DAC depth with configurable chip types (discrete, integrated or enhanced) +** +** 26-09-2017 Eke-Eke (Genesis Plus GX): +** - fixed EG counter loopback behavior (verified on YM3438 die) +** - reverted changes to EG rates 2-7 increment values +** +** 09-04-2017 Eke-Eke (Genesis Plus GX): +** - fixed LFO PM implementation: block & keyscale code should not be modified by LFO (verified on YM2612 die) +** - fixed Timer B overflow handling +** +** 12-03-2017 Eke-Eke (Genesis Plus GX): +** - fixed Op1 self-feedback regression introduced by previous modifications +** - removed one-sample extra delay on Op1 calculated output +** - refactored chan_calc() function +** +** 01-09-2012 Eke-Eke (Genesis Plus GX): +** - removed input clock / output samplerate frequency ratio, chip now always run at (original) internal sample frequency +** - removed now uneeded extra bits of precision +** +** 2006~2012 Eke-Eke (Genesis Plus GX): +** - removed unused multichip support +** - added YM2612 Context external access functions +** - fixed LFO implementation: +** .added support for CH3 special mode: fixes various sound effects (birds in Warlock, bug sound in Aladdin...) +** .inverted LFO AM waveform: fixes Spider-Man & Venom : Separation Anxiety (intro), California Games (surfing event) +** .improved LFO timing accuracy: now updated AFTER sample output, like EG/PG updates, and without any precision loss anymore. +** - improved internal timers emulation +** - adjusted lowest EG rates increment values +** - fixed Attack Rate not being updated in some specific cases (Batman & Robin intro) +** - fixed EG behavior when Attack Rate is maximal +** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation +** - implemented EG output immediate changes on register writes +** - fixed YM2612 initial values (after the reset): fixes missing intro in B.O.B +** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many other games using GEMS sound engine) +** - implemented accurate CSM mode emulation +** - implemented accurate SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Stix & many other games) +** - implemented accurate address/data ports behavior +** - added preliminar support for DAC precision +** +** 03-08-2003 Jarek Burczynski: +** - fixed YM2608 initial values (after the reset) +** - fixed flag and irqmask handling (YM2608) +** - fixed BUFRDY flag handling (YM2608) +** +** 14-06-2003 Jarek Burczynski: +** - implemented all of the YM2608 status register flags +** - implemented support for external memory read/write via YM2608 +** - implemented support for deltat memory limit register in YM2608 emulation +** +** 22-05-2003 Jarek Burczynski: +** - fixed LFO PM calculations (copy&paste bugfix) +** +** 08-05-2003 Jarek Burczynski: +** - fixed SSG support +** +** 22-04-2003 Jarek Burczynski: +** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608) +** +** 15-04-2003 Jarek Burczynski: +** - added support for YM2608's register 0x110 - status mask +** +** 01-12-2002 Jarek Burczynski: +** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608) +** The addressing patch used for early Neo-Geo games can be removed now. +** +** 26-11-2002 Jarek Burczynski, Nicola Salmoria: +** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to: +** - added emulation of YM2608 drums. +** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608) +** +** 16-08-2002 Jarek Burczynski: +** - binary exact Envelope Generator (verified on real YM2203); +** identical to YM2151 +** - corrected 'off by one' error in feedback calculations (when feedback is off) +** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610) +** +** 18-12-2001 Jarek Burczynski: +** - added SSG-EG support (verified on real YM2203) +** +** 12-08-2001 Jarek Burczynski: +** - corrected sin_tab and tl_tab data (verified on real chip) +** - corrected feedback calculations (verified on real chip) +** - corrected phase generator calculations (verified on real chip) +** - corrected envelope generator calculations (verified on real chip) +** - corrected FM volume level (YM2610 and YM2610B). +** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) : +** this was needed to calculate YM2610 FM channels output correctly. +** (Each FM channel is calculated as in other chips, but the output of the channel +** gets shifted right by one *before* sending to accumulator. That was impossible to do +** with previous implementation). +** +** 23-07-2001 Jarek Burczynski, Nicola Salmoria: +** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip) +** +** 11-06-2001 Jarek Burczynski: +** - corrected end of sample bug in ADPCMA_calc_cha(). +** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits). +** +** 08-12-98 hiro-shi: +** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA +** move ROM limit check.(CALC_CH? -> 2610Write1/2) +** test program (ADPCMB_TEST) +** move ADPCM A/B end check. +** ADPCMB repeat flag(no check) +** change ADPCM volume rate (8->16) (32->48). +** +** 09-12-98 hiro-shi: +** change ADPCM volume. (8->16, 48->64) +** replace ym2610 ch0/3 (YM-2610B) +** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff. +** add ADPCM_SHIFT_MASK +** change ADPCMA_DECODE_MIN/MAX. +*/ + +/************************************************************************/ +/* comment of hiro-shi(Hiromitsu Shioya) */ +/* YM2610(B) = OPN-B */ +/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/************************************************************************/ + +#include "gx_ym2612.h" +#include "../mame/mamedef.h" +#include +#include +#include + +/* envelope generator */ +#define ENV_BITS 10 +#define ENV_LEN (1<>3) + +/* sin waveform table in 'decibel' scale */ +static unsigned int sin_tab[SIN_LEN]; + +/* sustain level table (3dB per step) */ +/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ +/* 1, 2, 4, 8, 16, 32, 64 (value)*/ +/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ + +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +/* attenuation value (10 bits) = (SL << 2) << 3 */ +#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) ) +static const UINT32 sl_table[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const UINT8 eg_inc[19*RATE_STEPS]={ + +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ +/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ +/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ +/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ + +/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ +/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ +/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(17) in this table - it's directly in the code */ +static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates (same as Rate 0) */ +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), + +/* rates 00-11 */ +/* +O( 0),O( 1) +*/ +O(18),O(18), /* from Nemesis's tests on real YM2612 hardware */ + O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 12 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 13 */ +O( 8),O( 9),O(10),O(11), + +/* rate 14 */ +O(12),O(13),O(14),O(15), + +/* rate 15 */ +O(16),O(16),O(16),O(16), + +/* 32 dummy rates (same as 15 3) */ +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ +/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ +/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates */ +/* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */ + +/* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */ +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), + +/* rates 00-11 */ +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 12 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 32 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) + +}; +#undef O + +static const UINT8 dt_tab[4 * 32]={ +/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ +/* FD=0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* FD=1 */ + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, +/* FD=2 */ + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, +/* FD=3 */ + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 +}; + + +/* OPN key frequency number -> key code follow table */ +/* fnum higher 4bit -> keycode lower 2bit */ +static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; + + +/* 8 LFO speed parameters */ +/* each value represents number of samples that one LFO level will last for */ +static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; + + +/*There are 4 different LFO AM depths available, they are: + 0 dB, 1.4 dB, 5.9 dB, 11.8 dB + Here is how it is generated (in EG steps): + + 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 + 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 + 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 + + (1.4 dB is loosing precision as you can see) + + It's implemented as generator from 0..126 with step 2 then a shift + right N times, where N is: + 8 for 0 dB + 3 for 1.4 dB + 1 for 5.9 dB + 0 for 11.8 dB +*/ +static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0}; + + + +/*There are 8 different LFO PM depths available, they are: + 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) + + Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 + (bits 8,9,10 = FNUM MSB from OCT/FNUM register) + + Here we store only first quarter (positive one) of full waveform. + Full table (lfo_pm_table) containing all 128 waveforms is build + at run (init) time. + + One value in table below represents 4 (four) basic LFO steps + (1 PM step = 4 AM steps). + + For example: + at LFO SPEED=0 (which is 108 samples per basic LFO step) + one value from "lfo_pm_output" table lasts for 432 consecutive + samples (4*108=432) and one full LFO waveform cycle lasts for 13824 + samples (32*432=13824; 32 because we store only a quarter of whole + waveform in the table below) +*/ +static const UINT8 lfo_pm_output[7*8][8]={ +/* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ +/* FNUM BIT 4: 000 0001xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1}, + +/* FNUM BIT 5: 000 0010xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3}, + +/* FNUM BIT 6: 000 0100xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1}, +/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6}, + +/* FNUM BIT 7: 000 1000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1}, +/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2}, +/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, + +/* FNUM BIT 8: 001 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2}, +/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3}, +/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4}, +/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, + +/* FNUM BIT 9: 010 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2}, +/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4}, +/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6}, +/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8}, +/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, + +/* FNUM BIT10: 100 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4}, +/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8}, +/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc}, +/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10}, +/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, +/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}, + +}; + +/* all 128 LFO PM waveforms */ +static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ + +/* register number to channel number , slot offset */ +#define OPN_CHAN(N) (N&3) +#define OPN_SLOT(N) ((N>>2)&3) + +/* slot number */ +#define SLOT1 0 +#define SLOT2 2 +#define SLOT3 1 +#define SLOT4 3 + +/* + * Pan law table + */ + +static const UINT16 panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + +/* struct describing a single operator (SLOT) */ +typedef struct +{ + INT32 *DT; /* detune :dt_tab[DT] */ + UINT8 KSR; /* key scale rate :3-KSR */ + UINT32 ar; /* attack rate */ + UINT32 d1r; /* decay rate */ + UINT32 d2r; /* sustain rate */ + UINT32 rr; /* release rate */ + UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ + UINT32 mul; /* multiple :ML_TABLE[ML] */ + + /* Phase Generator */ + UINT32 phase; /* phase counter */ + INT32 Incr; /* phase step */ + + /* Envelope Generator */ + UINT8 state; /* phase type */ + UINT32 tl; /* total level: TL << 3 */ + INT32 volume; /* envelope counter */ + UINT32 sl; /* sustain level:sl_table[SL] */ + UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */ + + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT8 eg_sh_d1r; /* (decay state) */ + UINT8 eg_sel_d1r; /* (decay state) */ + UINT8 eg_sh_d2r; /* (sustain state) */ + UINT8 eg_sel_d2r; /* (sustain state) */ + UINT8 eg_sh_rr; /* (release state) */ + UINT8 eg_sel_rr; /* (release state) */ + + UINT8 ssg; /* SSG-EG waveform */ + UINT8 ssgn; /* SSG-EG negated output */ + + UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */ + + /* LFO */ + UINT32 AMmask; /* AM enable flag */ + +} FM_SLOT; + +typedef struct +{ + FM_SLOT SLOT[4]; /* four SLOTs (operators) */ + + UINT8 ALGO; /* algorithm */ + UINT8 FB; /* feedback shift */ + INT32 op1_out[2]; /* op1 output for feedback */ + + INT32 *connect1; /* SLOT1 output pointer */ + INT32 *connect3; /* SLOT3 output pointer */ + INT32 *connect2; /* SLOT2 output pointer */ + INT32 *connect4; /* SLOT4 output pointer */ + + INT32 *mem_connect; /* where to put the delayed sample (MEM) */ + INT32 mem_value; /* delayed sample (MEM) value */ + + INT32 pms; /* channel PMS */ + UINT8 ams; /* channel AMS */ + + UINT32 fc; /* fnum,blk */ + UINT8 kcode; /* key code */ + UINT32 block_fnum; /* blk/fnum value (for LFO PM calculations) */ + + INT32 pan_volume_l; + INT32 pan_volume_r; +} FM_CH; + + +typedef struct +{ + UINT16 address; /* address register */ + UINT8 status; /* status flag */ + UINT32 mode; /* mode CSM / 3SLOT */ + UINT8 fn_h; /* freq latch */ + INT32 TA; /* timer a value */ + INT32 TAL; /* timer a base */ + INT32 TAC; /* timer a counter */ + INT32 TB; /* timer b value */ + INT32 TBL; /* timer b base */ + INT32 TBC; /* timer b counter */ + INT32 dt_tab[8][32]; /* DeTune table */ + +} FM_ST; + + +/***********************************************************/ +/* OPN unit */ +/***********************************************************/ + +/* OPN 3slot struct */ +typedef struct +{ + UINT32 fc[3]; /* fnum3,blk3: calculated */ + UINT8 fn_h; /* freq3 latch */ + UINT8 kcode[3]; /* key code */ + UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + UINT8 key_csm; /* CSM mode Key-ON flag */ + +} FM_3SLOT; + +/* OPN/A/B common state */ +typedef struct +{ + FM_ST ST; /* general state */ + FM_3SLOT SL3; /* 3 slot mode state */ + unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */ + + /* EG */ + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */ + + /* LFO */ + UINT8 lfo_cnt; /* current LFO phase (out of 128) */ + UINT32 lfo_timer; /* current LFO phase runs at LFO frequency */ + UINT32 lfo_timer_overflow; /* LFO timer overflows every N samples (depends on LFO frequency) */ + UINT32 LFO_AM; /* current LFO AM step */ + UINT32 LFO_PM; /* current LFO PM step */ + +} FM_OPN; + +/***********************************************************/ +/* YM2612 chip */ +/***********************************************************/ +typedef struct YM2612GX +{ + FM_CH CH[6]; /* channel state */ + UINT8 dacen; /* DAC mode */ + INT32 dacout; /* DAC output */ + FM_OPN OPN; /* OPN state */ + + /* current chip state */ + INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ + INT32 mem; /* one sample delay memory */ + INT32 out_fm[6]; /* outputs of working channels */ + + /* chip type */ + UINT32 op_mask[8][4]; /* operator output bitmasking (DAC quantization) */ + int chip_type; +} YM2612; + + +INLINE void FM_KEYON(YM2612 *ym2612, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if (!SLOT->key && !ym2612->OPN.SL3.key_csm) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + SLOT->key = 1; +} + +INLINE void FM_KEYOFF(YM2612 *ym2612, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if (SLOT->key && !ym2612->OPN.SL3.key_csm) + { + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } + + SLOT->key = 0; +} + +INLINE void FM_KEYON_CSM(YM2612 *ym2612, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if (!SLOT->key && !ym2612->OPN.SL3.key_csm) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } +} + +INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + if (!SLOT->key) + { + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } +} + +/* CSM Key Controll */ +INLINE void CSMKeyControll(YM2612 *ym2612, FM_CH *CH) +{ + /* all key ON (verified by Nemesis on real hardware) */ + FM_KEYON_CSM(ym2612, CH,SLOT1); + FM_KEYON_CSM(ym2612, CH,SLOT2); + FM_KEYON_CSM(ym2612, CH,SLOT3); + FM_KEYON_CSM(ym2612, CH,SLOT4); + ym2612->OPN.SL3.key_csm = 1; +} + +INLINE void INTERNAL_TIMER_A(YM2612 *ym2612) +{ + if (ym2612->OPN.ST.mode & 0x01) + { + ym2612->OPN.ST.TAC--; + if (ym2612->OPN.ST.TAC <= 0) + { + /* set status (if enabled) */ + if (ym2612->OPN.ST.mode & 0x04) + ym2612->OPN.ST.status |= 0x01; + + /* reload the counter */ + ym2612->OPN.ST.TAC = ym2612->OPN.ST.TAL; + + /* CSM mode auto key on */ + if ((ym2612->OPN.ST.mode & 0xC0) == 0x80) + CSMKeyControll(ym2612, &ym2612->CH[2]); + } + } +} + +INLINE void INTERNAL_TIMER_B(YM2612 *ym2612, int step) +{ + if (ym2612->OPN.ST.mode & 0x02) + { + ym2612->OPN.ST.TBC-=step; + if (ym2612->OPN.ST.TBC <= 0) + { + /* set status (if enabled) */ + if (ym2612->OPN.ST.mode & 0x08) + ym2612->OPN.ST.status |= 0x02; + + /* reload the counter */ + do + { + ym2612->OPN.ST.TBC += ym2612->OPN.ST.TBL; + } + while (ym2612->OPN.ST.TBC <= 0); + } + } +} + +/* OPN Mode Register Write */ +INLINE void set_timers(YM2612 *ym2612, int v ) +{ + /* b7 = CSM MODE */ + /* b6 = 3 slot mode */ + /* b5 = reset b */ + /* b4 = reset a */ + /* b3 = timer enable b */ + /* b2 = timer enable a */ + /* b1 = load b */ + /* b0 = load a */ + + if ((ym2612->OPN.ST.mode ^ v) & 0xC0) + { + /* phase increment need to be recalculated */ + ym2612->CH[2].SLOT[SLOT1].Incr=-1; + + /* CSM mode disabled and CSM key ON active*/ + if (((v & 0xC0) != 0x80) && ym2612->OPN.SL3.key_csm) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT1); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT2); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT3); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT4); + ym2612->OPN.SL3.key_csm = 0; + } + } + + /* reload Timers */ + if ((v&1) && !(ym2612->OPN.ST.mode&1)) + ym2612->OPN.ST.TAC = ym2612->OPN.ST.TAL; + if ((v&2) && !(ym2612->OPN.ST.mode&2)) + ym2612->OPN.ST.TBC = ym2612->OPN.ST.TBL; + + /* reset Timers flags */ + ym2612->OPN.ST.status &= (~v >> 4); + + ym2612->OPN.ST.mode = v; +} + +/* set algorithm connection */ +INLINE void setup_connection(YM2612 *ym2612, FM_CH *CH, int ch ) +{ + INT32 *carrier = &ym2612->out_fm[ch]; + + INT32 **om1 = &CH->connect1; + INT32 **om2 = &CH->connect3; + INT32 **oc1 = &CH->connect2; + + INT32 **memc = &CH->mem_connect; + + switch( CH->ALGO ){ + case 0: + /* M1---C1---MEM---M2---C2---OUT */ + *om1 = &ym2612->c1; + *oc1 = &ym2612->mem; + *om2 = &ym2612->c2; + *memc= &ym2612->m2; + break; + case 1: + /* M1------+-MEM---M2---C2---OUT */ + /* C1-+ */ + *om1 = &ym2612->mem; + *oc1 = &ym2612->mem; + *om2 = &ym2612->c2; + *memc= &ym2612->m2; + break; + case 2: + /* M1-----------------+-C2---OUT */ + /* C1---MEM---M2-+ */ + *om1 = &ym2612->c2; + *oc1 = &ym2612->mem; + *om2 = &ym2612->c2; + *memc= &ym2612->m2; + break; + case 3: + /* M1---C1---MEM------+-C2---OUT */ + /* M2-+ */ + *om1 = &ym2612->c1; + *oc1 = &ym2612->mem; + *om2 = &ym2612->c2; + *memc= &ym2612->c2; + break; + case 4: + /* M1---C1-+-OUT */ + /* M2---C2-+ */ + /* MEM: not used */ + *om1 = &ym2612->c1; + *oc1 = carrier; + *om2 = &ym2612->c2; + *memc= &ym2612->mem; /* store it anywhere where it will not be used */ + break; + case 5: + /* +----C1----+ */ + /* M1-+-MEM---M2-+-OUT */ + /* +----C2----+ */ + *om1 = 0; /* special mark */ + *oc1 = carrier; + *om2 = carrier; + *memc= &ym2612->m2; + break; + case 6: + /* M1---C1-+ */ + /* M2-+-OUT */ + /* C2-+ */ + /* MEM: not used */ + *om1 = &ym2612->c1; + *oc1 = carrier; + *om2 = carrier; + *memc= &ym2612->mem; /* store it anywhere where it will not be used */ + break; + case 7: + /* M1-+ */ + /* C1-+-OUT */ + /* M2-+ */ + /* C2-+ */ + /* MEM: not used*/ + *om1 = carrier; + *oc1 = carrier; + *om2 = carrier; + *memc= &ym2612->mem; /* store it anywhere where it will not be used */ + break; + } + + CH->connect4 = carrier; +} + +/* set detune & multiple */ +INLINE void set_det_mul(YM2612 *ym2612, FM_CH *CH,FM_SLOT *SLOT,int v) +{ + SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1; + SLOT->DT = ym2612->OPN.ST.dt_tab[(v>>4)&7]; + CH->SLOT[SLOT1].Incr=-1; +} + +/* set total level */ +INLINE void set_tl(FM_SLOT *SLOT , int v) +{ + SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; +} + +/* set attack rate & key scale */ +INLINE void set_ar_ksr(FM_CH *CH,FM_SLOT *SLOT,int v) +{ + UINT8 old_KSR = SLOT->KSR; + + SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->KSR = 3-(v>>6); + if (SLOT->KSR != old_KSR) + { + CH->SLOT[SLOT1].Incr=-1; + } + + /* Even if it seems unnecessary to do it here, it could happen that KSR and KC */ + /* are modified but the resulted SLOT->ksr value (kc >> SLOT->KSR) remains unchanged. */ + /* In such case, Attack Rate would not be recalculated by "refresh_fc_eg_slot". */ + /* This actually fixes the intro of "The Adventures of Batman & Robin" (Eke-Eke) */ + if ((SLOT->ar + SLOT->ksr) < (32+62)) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + /* verified by Nemesis on real hardware (Attack phase is blocked) */ + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; + } + } + +/* set decay rate */ +INLINE void set_dr(FM_SLOT *SLOT,int v) +{ + SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr]; + +} + +/* set sustain rate */ +INLINE void set_sr(FM_SLOT *SLOT,int v) +{ + SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr]; +} + +/* set release rate */ +INLINE void set_sl_rr(FM_SLOT *SLOT,int v) +{ + SLOT->sl = sl_table[ v>>4 ]; + + /* check EG state changes */ + if ((SLOT->state == EG_DEC) && (SLOT->volume >= (INT32)(SLOT->sl))) + SLOT->state = EG_SUS; + + SLOT->rr = 34 + ((v&0x0f)<<2); + + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr]; +} + +/* advance LFO to next sample */ +INLINE void advance_lfo(YM2612 *ym2612) +{ + if (ym2612->OPN.lfo_timer_overflow) /* LFO enabled ? */ + { + /* increment LFO timer (every samples) */ + ym2612->OPN.lfo_timer ++; + + /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */ + if (ym2612->OPN.lfo_timer >= ym2612->OPN.lfo_timer_overflow) + { + ym2612->OPN.lfo_timer = 0; + + /* There are 128 LFO steps */ + ym2612->OPN.lfo_cnt = ( ym2612->OPN.lfo_cnt + 1 ) & 127; + + /* triangle (inverted) */ + /* AM: from 126 to 0 step -2, 0 to 126 step +2 */ + if (ym2612->OPN.lfo_cnt<64) + ym2612->OPN.LFO_AM = (ym2612->OPN.lfo_cnt ^ 63) << 1; + else + ym2612->OPN.LFO_AM = (ym2612->OPN.lfo_cnt & 63) << 1; + + /* PM works with 4 times slower clock */ + ym2612->OPN.LFO_PM = ym2612->OPN.lfo_cnt >> 2; + } + } +} + + +INLINE void advance_eg_channels(FM_CH *CH, unsigned int eg_cnt) +{ + unsigned int i = 6; /* six channels */ + unsigned int j; + FM_SLOT *SLOT; + + do + { + SLOT = &CH->SLOT[SLOT1]; + j = 4; /* four operators per channel */ + do + { + switch(SLOT->state) + { + case EG_ATT: /* attack phase */ + { + if (!(eg_cnt & ((1<eg_sh_ar)-1))) + { + /* update attenuation level */ + SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4; + + /* check phase transition*/ + if (SLOT->volume <= MIN_ATT_INDEX) + { + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */ + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + break; + } + + case EG_DEC: /* decay phase */ + { + if (!(eg_cnt & ((1<eg_sh_d1r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* check phase transition*/ + if (SLOT->volume >= (INT32)(SLOT->sl)) + SLOT->state = EG_SUS; + } + break; + } + + case EG_SUS: /* sustain phase */ + { + if (!(eg_cnt & ((1<eg_sh_d2r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* check phase transition*/ + if ( SLOT->volume >= MAX_ATT_INDEX ) + SLOT->volume = MAX_ATT_INDEX; + /* do not change SLOT->state (verified on real chip) */ + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + break; + } + + case EG_REL: /* release phase */ + { + if (!(eg_cnt & ((1<eg_sh_rr)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((eg_cnt>>SLOT->eg_sh_rr)&7)]; + + /* check phase transition */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((eg_cnt>>SLOT->eg_sh_rr)&7)]; + + /* check phase transition*/ + if (SLOT->volume >= MAX_ATT_INDEX) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + + } + break; + } + } + + /* next slot */ + SLOT++; + } while (--j); + + /* next channel */ + CH++; + } while (--i); +} + +/* SSG-EG update process */ +/* The behavior is based upon Nemesis tests on real hardware */ +/* This is actually executed before each samples */ +INLINE void update_ssg_eg_channels(FM_CH *CH) +{ + unsigned int i = 6; /* six channels */ + unsigned int j; + FM_SLOT *SLOT; + + do + { + j = 4; /* four operators per channel */ + SLOT = &CH->SLOT[SLOT1]; + + do + { + /* detect SSG-EG transition */ + /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */ + /* if an Attack Phase is programmed, inversion can occur on each sample */ + if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL)) + { + if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */ + { + /* set inversion flag */ + if (SLOT->ssg & 0x02) + SLOT->ssgn = 4; + + /* force attenuation level during decay phases */ + if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04))) + SLOT->volume = MAX_ATT_INDEX; + } + else /* loop SSG-EG */ + { + /* toggle output inversion flag or reset Phase Generator */ + if (SLOT->ssg & 0x02) + SLOT->ssgn ^= 4; + else + SLOT->phase = 0; + + /* same as Key ON */ + if (SLOT->state != EG_ATT) + { + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* Attack Rate is maximal: directly switch to Decay or Substain */ + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + } + } + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* next slot */ + SLOT++; + } while (--j); + + /* next channel */ + CH++; + } while (--i); +} + +INLINE void update_phase_lfo_slot(FM_SLOT *SLOT, UINT32 pm, UINT8 kc, UINT32 fc) +{ + INT32 lfo_fn_offset = lfo_pm_table[((fc & 0x7f0) << 4) + pm]; + + if (lfo_fn_offset) /* LFO phase modulation active */ + { + /* block is not modified by LFO PM */ + UINT8 blk = fc >> 11; + + /* LFO works with one more bit of a precision (12-bit) */ + fc = ((fc << 1) + lfo_fn_offset) & 0xfff; + + /* (frequency) phase increment counter (17-bit) */ + fc = (((fc << blk) >> 2) + SLOT->DT[kc]) & DT_MASK; + + /* update phase */ + SLOT->phase += ((fc * SLOT->mul) >> 1); + } + else /* LFO phase modulation = zero */ + { + SLOT->phase += SLOT->Incr; + } +} + +INLINE void update_phase_lfo_channel(YM2612 *ym2612, FM_CH *CH) +{ + UINT32 fc = CH->block_fnum; + + INT32 lfo_fn_offset = lfo_pm_table[((fc & 0x7f0) << 4) + CH->pms + ym2612->OPN.LFO_PM]; + + if (lfo_fn_offset) /* LFO phase modulation active */ + { + UINT32 finc; + + /* block & keyscale code are not modified by LFO PM */ + UINT8 blk = fc >> 11; + UINT8 kc = CH->kcode; + + /* LFO works with one more bit of a precision (12-bit) */ + fc = ((fc << 1) + lfo_fn_offset) & 0xfff; + + /* (frequency) phase increment counter (17-bit) */ + fc = (fc << blk) >> 2; + + /* apply DETUNE & MUL operator specific values */ + finc = (fc + CH->SLOT[SLOT1].DT[kc]) & DT_MASK; + CH->SLOT[SLOT1].phase += ((finc * CH->SLOT[SLOT1].mul) >> 1); + + finc = (fc + CH->SLOT[SLOT2].DT[kc]) & DT_MASK; + CH->SLOT[SLOT2].phase += ((finc * CH->SLOT[SLOT2].mul) >> 1); + + finc = (fc + CH->SLOT[SLOT3].DT[kc]) & DT_MASK; + CH->SLOT[SLOT3].phase += ((finc * CH->SLOT[SLOT3].mul) >> 1); + + finc = (fc + CH->SLOT[SLOT4].DT[kc]) & DT_MASK; + CH->SLOT[SLOT4].phase += ((finc * CH->SLOT[SLOT4].mul) >> 1); + } + else /* LFO phase modulation = zero */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +/* update phase increment and envelope generator */ +INLINE void refresh_fc_eg_slot(FM_SLOT *SLOT , unsigned int fc , unsigned int kc ) +{ + /* add detune value */ + fc += SLOT->DT[kc]; + + /* (frequency) phase overflow (credits to Nemesis) */ + fc &= DT_MASK; + + /* (frequency) phase increment counter */ + SLOT->Incr = (fc * SLOT->mul) >> 1; + + /* ksr */ + kc = kc >> SLOT->KSR; + + if( SLOT->ksr != kc ) + { + SLOT->ksr = kc; + + /* recalculate envelope generator rates */ + if ((SLOT->ar + kc) < (32+62)) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + kc ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + kc ]; + } + else + { + /* verified by Nemesis on real hardware (Attack phase is blocked) */ + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; + } + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + kc]; + SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + kc]; + + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + kc]; + SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + kc]; + + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + kc]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + kc]; + } +} + +/* update phase increment counters */ +INLINE void refresh_fc_eg_chan(FM_CH *CH ) +{ + if( CH->SLOT[SLOT1].Incr==-1) + { + int fc = CH->fc; + int kc = CH->kcode; + refresh_fc_eg_slot(&CH->SLOT[SLOT1] , fc , kc ); + refresh_fc_eg_slot(&CH->SLOT[SLOT2] , fc , kc ); + refresh_fc_eg_slot(&CH->SLOT[SLOT3] , fc , kc ); + refresh_fc_eg_slot(&CH->SLOT[SLOT4] , fc , kc ); + } +} + +#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) + +INLINE signed int op_calc(UINT32 phase, unsigned int env, unsigned int pm, unsigned int opmask) +{ + UINT32 p = (env<<3) + sin_tab[ ( (phase >> SIN_BITS) + (pm >> 1) ) & SIN_MASK ]; + + if (p >= TL_TAB_LEN) + return 0; + return (tl_tab[p] & opmask); +} + +INLINE signed int op_calc1(UINT32 phase, unsigned int env, unsigned int pm, unsigned int opmask) +{ + UINT32 p = (env<<3) + sin_tab[ ( ( phase >> SIN_BITS ) + pm ) & SIN_MASK ]; + + if (p >= TL_TAB_LEN) + return 0; + return (tl_tab[p] & opmask); +} + +INLINE void chan_calc(YM2612 *ym2612, FM_CH *CH, int num) +{ + do + { + INT32 out = 0; + UINT32 AM = ym2612->OPN.LFO_AM >> CH->ams; + unsigned int eg_out = volume_calc(&CH->SLOT[SLOT1]); + UINT32 *mask = ym2612->op_mask[CH->ALGO]; + + ym2612->m2 = ym2612->c1 = ym2612->c2 = ym2612->mem = 0; + + *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ + + if( eg_out < ENV_QUIET ) /* SLOT 1 */ + { + if (CH->FB < SIN_BITS) + out = (CH->op1_out[0] + CH->op1_out[1]) >> CH->FB; + + out = op_calc1(CH->SLOT[SLOT1].phase, eg_out, out, mask[0]); + } + + CH->op1_out[0] = CH->op1_out[1]; + CH->op1_out[1] = out; + + if( !CH->connect1 ){ + /* algorithm 5 */ + ym2612->mem = ym2612->c1 = ym2612->c2 = out; + }else{ + /* other algorithms */ + *CH->connect1 = out; + } + + eg_out = volume_calc(&CH->SLOT[SLOT3]); + if( eg_out < ENV_QUIET ) /* SLOT 3 */ + *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, ym2612->m2, mask[2]); + + eg_out = volume_calc(&CH->SLOT[SLOT2]); + if( eg_out < ENV_QUIET ) /* SLOT 2 */ + *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, ym2612->c1, mask[1]); + + eg_out = volume_calc(&CH->SLOT[SLOT4]); + if( eg_out < ENV_QUIET ) /* SLOT 4 */ + *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, ym2612->c2, mask[3]); + + /* store current MEM */ + CH->mem_value = ym2612->mem; + + /* update phase counters AFTER output calculations */ + if (CH->pms) + { + /* 3-slot mode */ + if ((ym2612->OPN.ST.mode & 0xC0) && (CH == &ym2612->CH[2])) + { + /* keyscale code is not modifiedby LFO */ + UINT8 kc = ym2612->CH[2].kcode; + UINT32 pm = ym2612->CH[2].pms + ym2612->OPN.LFO_PM; + update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT1], pm, kc, ym2612->OPN.SL3.block_fnum[1]); + update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT2], pm, kc, ym2612->OPN.SL3.block_fnum[2]); + update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT3], pm, kc, ym2612->OPN.SL3.block_fnum[0]); + update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT4], pm, kc, ym2612->CH[2].block_fnum); + } + else + { + update_phase_lfo_channel(ym2612, CH); + } + } + else /* no LFO phase modulation */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } + + /* next channel */ + CH++; + } while (--num); +} + +/* write a OPN mode register 0x20-0x2f */ +INLINE void OPNWriteMode(YM2612 *ym2612, int r, int v) +{ + UINT8 c; + FM_CH *CH; + + switch(r){ + case 0x21: /* Test */ + break; + + case 0x22: /* LFO FREQ */ + if (v&8) /* LFO enabled ? */ + { + ym2612->OPN.lfo_timer_overflow = lfo_samples_per_step[v&7]; + } + else + { + /* hold LFO waveform in reset state */ + ym2612->OPN.lfo_timer_overflow = 0; + ym2612->OPN.lfo_timer = 0; + ym2612->OPN.lfo_cnt = 0; + ym2612->OPN.LFO_PM = 0; + ym2612->OPN.LFO_AM = 126; + } + break; + case 0x24: /* timer A High */ + ym2612->OPN.ST.TA = (ym2612->OPN.ST.TA & 0x03)|(((int)v)<<2); + ym2612->OPN.ST.TAL = 1024 - ym2612->OPN.ST.TA; + break; + case 0x25: /* timer A Low */ + ym2612->OPN.ST.TA = (ym2612->OPN.ST.TA & 0x3fc)|(v&3); + ym2612->OPN.ST.TAL = 1024 - ym2612->OPN.ST.TA; + break; + case 0x26: /* timer B */ + ym2612->OPN.ST.TB = v; + ym2612->OPN.ST.TBL = (256 - v) << 4; + break; + case 0x27: /* mode, timer control */ + set_timers(ym2612, v); + break; + case 0x28: /* key on / off */ + c = v & 0x03; + if( c == 3 ) break; + if (v&0x04) c+=3; /* CH 4-6 */ + CH = &ym2612->CH[c]; + if (v&0x10) FM_KEYON(ym2612,CH,SLOT1); else FM_KEYOFF(ym2612,CH,SLOT1); + if (v&0x20) FM_KEYON(ym2612,CH,SLOT2); else FM_KEYOFF(ym2612,CH,SLOT2); + if (v&0x40) FM_KEYON(ym2612,CH,SLOT3); else FM_KEYOFF(ym2612,CH,SLOT3); + if (v&0x80) FM_KEYON(ym2612,CH,SLOT4); else FM_KEYOFF(ym2612,CH,SLOT4); + break; + } +} + +/* write a OPN register (0x30-0xff) */ +INLINE void OPNWriteReg(YM2612 *ym2612, int r, int v) +{ + FM_CH *CH; + FM_SLOT *SLOT; + + UINT8 c = OPN_CHAN(r); + + if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */ + + if (r >= 0x100) c+=3; + + CH = &ym2612->CH[c]; + + SLOT = &(CH->SLOT[OPN_SLOT(r)]); + + switch( r & 0xf0 ) { + case 0x30: /* DET , MUL */ + set_det_mul(ym2612,CH,SLOT,v); + break; + + case 0x40: /* TL */ + set_tl(SLOT,v); + break; + + case 0x50: /* KS, AR */ + set_ar_ksr(CH,SLOT,v); + break; + + case 0x60: /* bit7 = AM ENABLE, DR */ + set_dr(SLOT,v); + SLOT->AMmask = (v&0x80) ? ~0 : 0; + break; + + case 0x70: /* SR */ + set_sr(SLOT,v); + break; + + case 0x80: /* SL, RR */ + set_sl_rr(SLOT,v); + break; + + case 0x90: /* SSG-EG */ + SLOT->ssg = v&0x0f; + + /* recalculate EG output */ + if (SLOT->state > EG_REL) + { + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* SSG-EG envelope shapes : + + E AtAlH + 1 0 0 0 \\\\ + + 1 0 0 1 \___ + + 1 0 1 0 \/\/ + ___ + 1 0 1 1 \ + + 1 1 0 0 //// + ___ + 1 1 0 1 / + + 1 1 1 0 /\/\ + + 1 1 1 1 /___ + + + E = SSG-EG enable + + + The shapes are generated using Attack, Decay and Sustain phases. + + Each single character in the diagrams above represents this whole + sequence: + + - when KEY-ON = 1, normal Attack phase is generated (*without* any + difference when compared to normal mode), + + - later, when envelope level reaches minimum level (max volume), + the EG switches to Decay phase (which works with bigger steps + when compared to normal mode - see below), + + - later when envelope level passes the SL level, + the EG swithes to Sustain phase (which works with bigger steps + when compared to normal mode - see below), + + - finally when envelope level reaches maximum level (min volume), + the EG switches to Attack phase again (depends on actual waveform). + + Important is that when switch to Attack phase occurs, the phase counter + of that operator will be zeroed-out (as in normal KEY-ON) but not always. + (I havent found the rule for that - perhaps only when the output level is low) + + The difference (when compared to normal Envelope Generator mode) is + that the resolution in Decay and Sustain phases is 4 times lower; + this results in only 256 steps instead of normal 1024. + In other words: + when SSG-EG is disabled, the step inside of the EG is one, + when SSG-EG is enabled, the step is four (in Decay and Sustain phases). + + Times between the level changes are the same in both modes. + + + Important: + Decay 1 Level (so called SL) is compared to actual SSG-EG output, so + it is the same in both SSG and no-SSG modes, with this exception: + + when the SSG-EG is enabled and is generating raising levels + (when the EG output is inverted) the SL will be found at wrong level !!! + For example, when SL=02: + 0 -6 = -6dB in non-inverted EG output + 96-6 = -90dB in inverted EG output + Which means that EG compares its level to SL as usual, and that the + output is simply inverted afterall. + + + The Yamaha's manuals say that AR should be set to 0x1f (max speed). + That is not necessary, but then EG will be generating Attack phase. + + */ + break; + + case 0xa0: + switch( OPN_SLOT(r) ){ + case 0: /* 0xa0-0xa2 : FNUM1 */ + { + UINT32 fn = (((UINT32)((ym2612->OPN.ST.fn_h)&7))<<8) + v; + UINT8 blk = ym2612->OPN.ST.fn_h>>3; + /* keyscale code */ + CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + CH->fc = (fn<>1; + + /* store fnum in clear form for LFO PM calculations */ + CH->block_fnum = (blk<<11) | fn; + + CH->SLOT[SLOT1].Incr=-1; + break; + } + case 1: /* 0xa4-0xa6 : FNUM2,BLK */ + ym2612->OPN.ST.fn_h = v&0x3f; + break; + case 2: /* 0xa8-0xaa : 3CH FNUM1 */ + if(r < 0x100) + { + UINT32 fn = (((UINT32)(ym2612->OPN.SL3.fn_h&7))<<8) + v; + UINT8 blk = ym2612->OPN.SL3.fn_h>>3; + /* keyscale code */ + ym2612->OPN.SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + ym2612->OPN.SL3.fc[c] = (fn<>1; + ym2612->OPN.SL3.block_fnum[c] = (blk<<11) | fn; + ym2612->CH[2].SLOT[SLOT1].Incr=-1; + } + break; + case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ + if(r < 0x100) + ym2612->OPN.SL3.fn_h = v&0x3f; + break; + } + break; + + case 0xb0: + switch( OPN_SLOT(r) ){ + case 0: /* 0xb0-0xb2 : FB,ALGO */ + { + CH->ALGO = v&7; + CH->FB = SIN_BITS - ((v>>3)&7); + setup_connection(ym2612, CH, c ); + break; + } + case 1: /* 0xb4-0xb6 : L , R , AMS , PMS */ + /* b0-2 PMS */ + CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ + + /* b4-5 AMS */ + CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03]; + + /* PAN : b7 = L, b6 = R */ + ym2612->OPN.pan[ c*2 ] = (v & 0x80) ? 0xffffffff : 0; + ym2612->OPN.pan[ c*2+1 ] = (v & 0x40) ? 0xffffffff : 0; + break; + } + break; + } +} + +static void reset_channels(FM_CH *CH , int num ) +{ + int c,s; + + for( c = 0 ; c < num ; c++ ) + { + CH[c].mem_value = 0; + CH[c].op1_out[0] = 0; + CH[c].op1_out[1] = 0; + for(s = 0 ; s < 4 ; s++ ) + { + CH[c].SLOT[s].Incr = -1; + CH[c].SLOT[s].key = 0; + CH[c].SLOT[s].phase = 0; + CH[c].SLOT[s].ssgn = 0; + CH[c].SLOT[s].state = EG_OFF; + CH[c].SLOT[s].volume = MAX_ATT_INDEX; + CH[c].SLOT[s].vol_out = MAX_ATT_INDEX; + } + } +} + +/* initialize generic tables */ +static int tables_ready = 0; +static void init_tables() +{ + signed int i,x; + signed int n; + double o,m; + + if (tables_ready) + return; + + /* build Linear Power Table */ + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 2; /* 13 bits here (as in real chip) */ + + /* 14 bits (with sign bit) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + /* one entry in the 'Power' table use the following format, xxxxxyyyyyyyys with: */ + /* s = sign bit */ + /* yyyyyyyy = 8-bits decimal part (0-TL_RES_LEN) */ + /* xxxxx = 5-bits integer 'shift' value (0-31) but, since Power table output is 13 bits, */ + /* any value above 13 (included) would be discarded. */ + for (i=1; i<13; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + } + + /* build Logarithmic Sinus table */ + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + /* 13-bits (8.5) value is formatted for above 'Power' table */ + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + } + + /* build LFO PM modulation table */ + for(i = 0; i < 8; i++) /* 8 PM depths */ + { + UINT8 fnum; + for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */ + { + UINT8 value; + UINT8 step; + UINT32 offset_depth = i; + UINT32 offset_fnum_bit; + UINT32 bit_tmp; + + for (step=0; step<8; step++) + { + value = 0; + for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */ + { + if (fnum & (1<OPN.ST.dt_tab[d][i] = (INT32) dt_tab[d*32 + i]; + ym2612->OPN.ST.dt_tab[d+4][i] = -ym2612->OPN.ST.dt_tab[d][i]; + } + } + + /* build default OP mask table */ + for (i = 0;i < 8;i++) + { + for (d = 0;d < 4;d++) + { + ym2612->op_mask[i][d] = 0xffffffff; + } + } + + for (i = 0; i < 6; i++) + { + ym2612->CH[i].pan_volume_l = 46340; + ym2612->CH[i].pan_volume_r = 46340; + } + + init_tables(); +} + +/* reset OPN registers */ +void YM2612GXResetChip(YM2612 *ym2612) +{ + int i; + + ym2612->OPN.eg_timer = 0; + ym2612->OPN.eg_cnt = 0; + + ym2612->OPN.lfo_timer_overflow = 0; + ym2612->OPN.lfo_timer = 0; + ym2612->OPN.lfo_cnt = 0; + ym2612->OPN.LFO_AM = 126; + ym2612->OPN.LFO_PM = 0; + + ym2612->OPN.ST.TAC = 0; + ym2612->OPN.ST.TBC = 0; + + ym2612->OPN.SL3.key_csm = 0; + + ym2612->dacen = 0; + ym2612->dacout = 0; + + set_timers(ym2612, 0x30); + ym2612->OPN.ST.TB = 0; + ym2612->OPN.ST.TBL = 256 << 4; + ym2612->OPN.ST.TA = 0; + ym2612->OPN.ST.TAL = 1024; + + reset_channels(&ym2612->CH[0] , 6 ); + + for(i = 0xb6 ; i >= 0xb4 ; i-- ) + { + OPNWriteReg(ym2612, i ,0xc0); + OPNWriteReg(ym2612, i|0x100,0xc0); + } + for(i = 0xb2 ; i >= 0x30 ; i-- ) + { + OPNWriteReg(ym2612, i ,0); + OPNWriteReg(ym2612, i|0x100,0); + } +} + +/* ym2612 write */ +/* n = number */ +/* a = address */ +/* v = value */ +void YM2612GXWrite(YM2612 *ym2612, unsigned int a, unsigned int v) +{ + v &= 0xff; /* adjust to 8 bit bus */ + + switch( a ) + { + case 0: /* address port 0 */ + ym2612->OPN.ST.address = v; + break; + + case 2: /* address port 1 */ + ym2612->OPN.ST.address = v | 0x100; + break; + + default: /* data port */ + { + int addr = ym2612->OPN.ST.address; /* verified by Nemesis on real YM2612 */ + switch( addr & 0x1f0 ) + { + case 0x20: /* 0x20-0x2f Mode */ + switch( addr ) + { + case 0x2a: /* DAC data (ym2612) */ + ym2612->dacout = ((int)v - 0x80) << 6; /* convert to 14-bit output */ + break; + case 0x2b: /* DAC Sel (ym2612) */ + /* b7 = dac enable */ + ym2612->dacen = v & 0x80; + break; + default: /* OPN section */ + /* write register */ + OPNWriteMode(ym2612,addr,v); + } + break; + default: /* 0x30-0xff OPN section */ + /* write register */ + OPNWriteReg(ym2612,addr,v); + } + break; + } + } +} + +void YM2612GXWritePan(YM2612GX *chip, int c, unsigned char v) +{ + chip->CH[c].pan_volume_l = panlawtable[v & 0x7F]; + chip->CH[c].pan_volume_r = panlawtable[0x7F - (v & 0x7F)]; +} + +unsigned int YM2612GXRead(YM2612 *ym2612) +{ + return ym2612->OPN.ST.status; +} + +void YM2612GXPreGenerate(YM2612GX *ym2612) +{ + /* refresh PG increments and EG rates if required */ + refresh_fc_eg_chan(&ym2612->CH[0]); + refresh_fc_eg_chan(&ym2612->CH[1]); + + if (!(ym2612->OPN.ST.mode & 0xC0)) + { + refresh_fc_eg_chan(&ym2612->CH[2]); + } + else + { + /* 3SLOT MODE (operator order is 0,1,3,2) */ + if(ym2612->CH[2].SLOT[SLOT1].Incr==-1) + { + refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT1] , ym2612->OPN.SL3.fc[1] , ym2612->OPN.SL3.kcode[1] ); + refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT2] , ym2612->OPN.SL3.fc[2] , ym2612->OPN.SL3.kcode[2] ); + refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT3] , ym2612->OPN.SL3.fc[0] , ym2612->OPN.SL3.kcode[0] ); + refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT4] , ym2612->CH[2].fc , ym2612->CH[2].kcode ); + } + } + + refresh_fc_eg_chan(&ym2612->CH[3]); + refresh_fc_eg_chan(&ym2612->CH[4]); + refresh_fc_eg_chan(&ym2612->CH[5]); +} + +void YM2612GXPostGenerate(YM2612GX *ym2612, unsigned int count) +{ + /* timer B control */ + INTERNAL_TIMER_B(ym2612, count); +} + +void YM2612GXGenerateOneNative(YM2612GX *ym2612, FMSAMPLE *frame) +{ + int lt,rt; + INT32 *out_fm = ym2612->out_fm; + + /* clear outputs */ + out_fm[0] = 0; + out_fm[1] = 0; + out_fm[2] = 0; + out_fm[3] = 0; + out_fm[4] = 0; + out_fm[5] = 0; + + /* update SSG-EG output */ + update_ssg_eg_channels(&ym2612->CH[0]); + + /* calculate FM */ + if (!ym2612->dacen) + { + chan_calc(ym2612, &ym2612->CH[0],6); + } + else + { + /* DAC Mode */ + out_fm[5] = ym2612->dacout; + chan_calc(ym2612,&ym2612->CH[0],5); + } + + /* advance LFO */ + advance_lfo(ym2612); + + /* EG is updated every 3 samples */ + ym2612->OPN.eg_timer++; + if (ym2612->OPN.eg_timer >= 3) + { + /* reset EG timer */ + ym2612->OPN.eg_timer = 0; + + /* increment EG counter */ + ym2612->OPN.eg_cnt++; + + /* EG counter is 12-bit only and zero value is skipped (verified on real hardware) */ + if (ym2612->OPN.eg_cnt == 4096) + ym2612->OPN.eg_cnt = 1; + + /* advance envelope generator */ + advance_eg_channels(&ym2612->CH[0], ym2612->OPN.eg_cnt); + } + + /* channels accumulator output clipping (14-bit max) */ + if (out_fm[0] > 8191) out_fm[0] = 8191; + else if (out_fm[0] < -8192) out_fm[0] = -8192; + if (out_fm[1] > 8191) out_fm[1] = 8191; + else if (out_fm[1] < -8192) out_fm[1] = -8192; + if (out_fm[2] > 8191) out_fm[2] = 8191; + else if (out_fm[2] < -8192) out_fm[2] = -8192; + if (out_fm[3] > 8191) out_fm[3] = 8191; + else if (out_fm[3] < -8192) out_fm[3] = -8192; + if (out_fm[4] > 8191) out_fm[4] = 8191; + else if (out_fm[4] < -8192) out_fm[4] = -8192; + if (out_fm[5] > 8191) out_fm[5] = 8191; + else if (out_fm[5] < -8192) out_fm[5] = -8192; + +#define PANLAW_L(ch, chpan) ((out_fm[ch] * ym2612->CH[ch].pan_volume_l / 65535) & ym2612->OPN.pan[chpan]); +#define PANLAW_R(ch, chpan) ((out_fm[ch] * ym2612->CH[ch].pan_volume_r / 65535) & ym2612->OPN.pan[chpan]); + + /* stereo DAC output panning & mixing */ + lt = PANLAW_L(0, 0); + rt = PANLAW_R(0, 1); + lt += PANLAW_L(1, 2); + rt += PANLAW_R(1, 3); + lt += PANLAW_L(2, 4); + rt += PANLAW_R(2, 5); + lt += PANLAW_L(3, 6); + rt += PANLAW_R(3, 7); + lt += PANLAW_L(4, 8); + rt += PANLAW_R(4, 9); + lt += PANLAW_L(5, 10); + rt += PANLAW_R(5, 11); + +#undef PANLAW_L +#undef PANLAW_R + + /* discrete YM2612 DAC */ + if (ym2612->chip_type == YM2612_DISCRETE) + { + int i; + + /* DAC 'ladder effect' */ + for (i=0; i<6; i++) + { + if (out_fm[i] < 0) + { + /* -4 offset (-3 when not muted) on negative channel output (9-bit) */ + lt -= ((4 - (ym2612->OPN.pan[(2*i)+0] & 1)) << 5); + rt -= ((4 - (ym2612->OPN.pan[(2*i)+1] & 1)) << 5); + } + else + { + /* +4 offset (when muted or not) on positive channel output (9-bit) */ + lt += (4 << 5); + rt += (4 << 5); + } + } + } + + /* buffering */ + frame[0] = lt / 2; + frame[1] = rt / 2; + + /* CSM mode: if CSM Key ON has occurred, CSM Key OFF need to be sent */ + /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */ + ym2612->OPN.SL3.key_csm <<= 1; + + /* timer A control */ + INTERNAL_TIMER_A(ym2612); + + /* CSM Mode Key ON still disabled */ + if (ym2612->OPN.SL3.key_csm & 2) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT1); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT2); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT3); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT4); + ym2612->OPN.SL3.key_csm = 0; + } +} + +void YM2612GXConfig(YM2612 *ym2612, int type) +{ + /* YM2612 chip type */ + ym2612->chip_type = type; + + /* carrier operator outputs bitmask */ + if (ym2612->chip_type < YM2612_ENHANCED) + { + /* 9-bit DAC */ + ym2612->op_mask[0][3] = 0xffffffe0; + ym2612->op_mask[1][3] = 0xffffffe0; + ym2612->op_mask[2][3] = 0xffffffe0; + ym2612->op_mask[3][3] = 0xffffffe0; + ym2612->op_mask[4][1] = 0xffffffe0; + ym2612->op_mask[4][3] = 0xffffffe0; + ym2612->op_mask[5][1] = 0xffffffe0; + ym2612->op_mask[5][2] = 0xffffffe0; + ym2612->op_mask[5][3] = 0xffffffe0; + ym2612->op_mask[6][1] = 0xffffffe0; + ym2612->op_mask[6][2] = 0xffffffe0; + ym2612->op_mask[6][3] = 0xffffffe0; + ym2612->op_mask[7][0] = 0xffffffe0; + ym2612->op_mask[7][1] = 0xffffffe0; + ym2612->op_mask[7][2] = 0xffffffe0; + ym2612->op_mask[7][3] = 0xffffffe0; + } + else + { + /* 14-bit DAC */ + ym2612->op_mask[0][3] = 0xffffffff; + ym2612->op_mask[1][3] = 0xffffffff; + ym2612->op_mask[2][3] = 0xffffffff; + ym2612->op_mask[3][3] = 0xffffffff; + ym2612->op_mask[4][1] = 0xffffffff; + ym2612->op_mask[4][3] = 0xffffffff; + ym2612->op_mask[5][1] = 0xffffffff; + ym2612->op_mask[5][2] = 0xffffffff; + ym2612->op_mask[5][3] = 0xffffffff; + ym2612->op_mask[6][1] = 0xffffffff; + ym2612->op_mask[6][2] = 0xffffffff; + ym2612->op_mask[6][3] = 0xffffffff; + ym2612->op_mask[7][0] = 0xffffffff; + ym2612->op_mask[7][1] = 0xffffffff; + ym2612->op_mask[7][2] = 0xffffffff; + ym2612->op_mask[7][3] = 0xffffffff; + } +} diff --git a/thirdparty/opnmidi/chips/gx/gx_ym2612.h b/thirdparty/opnmidi/chips/gx/gx_ym2612.h new file mode 100644 index 000000000..77cbaa87b --- /dev/null +++ b/thirdparty/opnmidi/chips/gx/gx_ym2612.h @@ -0,0 +1,53 @@ +/* +** +** software implementation of Yamaha FM sound generator (YM2612/YM3438) +** +** Original code (MAME fm.c) +** +** Copyright (C) 2001, 2002, 2003 Jarek Burczynski (bujar at mame dot net) +** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 1.4 (final beta) +** +** Additional code & fixes by Eke-Eke for Genesis Plus GX +** Adaptations by Jean Pierre Cimalando for use in libOPNMIDI. +** (based on fee2bc8 dated Jan 7th, 2018) +** +*/ + +#ifndef _H_YM2612_ +#define _H_YM2612_ + +#if defined(__cplusplus) +extern "C" { +#endif + +enum { + YM2612_DISCRETE = 0, + YM2612_INTEGRATED, + YM2612_ENHANCED +}; + +struct YM2612GX; +typedef struct YM2612GX YM2612GX; + +/* typedef signed int FMSAMPLE; */ +typedef signed short FMSAMPLE; + +extern YM2612GX *YM2612GXAlloc(); +extern void YM2612GXFree(YM2612GX *ym2612); +extern void YM2612GXInit(YM2612GX *ym2612); +extern void YM2612GXConfig(YM2612GX *ym2612, int type); +extern void YM2612GXResetChip(YM2612GX *ym2612); +extern void YM2612GXPreGenerate(YM2612GX *ym2612); +extern void YM2612GXPostGenerate(YM2612GX *ym2612, unsigned int count); +extern void YM2612GXGenerateOneNative(YM2612GX *ym2612, FMSAMPLE *frame); +extern void YM2612GXWrite(YM2612GX *ym2612, unsigned int a, unsigned int v); +extern void YM2612GXWritePan(YM2612GX *chip, int c, unsigned char v); +extern unsigned int YM2612GXRead(YM2612GX *ym2612); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif /* _YM2612_ */ diff --git a/thirdparty/opnmidi/chips/gx_opn2.cpp b/thirdparty/opnmidi/chips/gx_opn2.cpp new file mode 100644 index 000000000..f0f26bba4 --- /dev/null +++ b/thirdparty/opnmidi/chips/gx_opn2.cpp @@ -0,0 +1,84 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "gx_opn2.h" +#include + +#include "gx/gx_ym2612.h" + +GXOPN2::GXOPN2(OPNFamily f) + : OPNChipBaseT(f), + m_chip(YM2612GXAlloc()), + m_framecount(0) +{ + YM2612GXInit(m_chip); + YM2612GXConfig(m_chip, YM2612_DISCRETE); + setRate(m_rate, m_clock); +} + +GXOPN2::~GXOPN2() +{ + YM2612GXFree(m_chip); +} + +void GXOPN2::setRate(uint32_t rate, uint32_t clock) +{ + OPNChipBaseT::setRate(rate, clock); + YM2612GXResetChip(m_chip); +} + +void GXOPN2::reset() +{ + OPNChipBaseT::reset(); + YM2612GXResetChip(m_chip); +} + +void GXOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + YM2612GXWrite(m_chip, 0 + port * 2, addr); + YM2612GXWrite(m_chip, 1 + port * 2, data); +} + +void GXOPN2::writePan(uint16_t chan, uint8_t data) +{ + YM2612GXWritePan(m_chip, chan, data); +} + +void GXOPN2::nativePreGenerate() +{ + YM2612GXPreGenerate(m_chip); + m_framecount = 0; +} + +void GXOPN2::nativePostGenerate() +{ + YM2612GXPostGenerate(m_chip, m_framecount); +} + +void GXOPN2::nativeGenerate(int16_t *frame) +{ + YM2612GXGenerateOneNative(m_chip, frame); + ++m_framecount; +} + +const char *GXOPN2::emulatorName() +{ + return "Genesis Plus GX"; +} diff --git a/thirdparty/opnmidi/chips/gx_opn2.h b/thirdparty/opnmidi/chips/gx_opn2.h new file mode 100644 index 000000000..06c6a2336 --- /dev/null +++ b/thirdparty/opnmidi/chips/gx_opn2.h @@ -0,0 +1,46 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GX_OPN2_H +#define GX_OPN2_H + +#include "opn_chip_base.h" + +struct YM2612GX; +class GXOPN2 final : public OPNChipBaseT +{ + YM2612GX *m_chip; + unsigned int m_framecount; +public: + explicit GXOPN2(OPNFamily f); + ~GXOPN2() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t chan, uint8_t data) override; + void nativePreGenerate() override; + void nativePostGenerate() override; + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; +}; + +#endif // GX_OPN2_H diff --git a/thirdparty/opnmidi/chips/mame/mame_ym2612fm.c b/thirdparty/opnmidi/chips/mame/mame_ym2612fm.c new file mode 100644 index 000000000..6af76a17c --- /dev/null +++ b/thirdparty/opnmidi/chips/mame/mame_ym2612fm.c @@ -0,0 +1,2900 @@ +/* +** +** File: fm2612.c -- software implementation of Yamaha YM2612 FM sound generator +** Split from fm.c to keep 2612 fixes from infecting other OPN chips +** +** Copyright Jarek Burczynski (bujar at mame dot net) +** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 1.5.1 (Genesis Plus GX ym2612.c rev. 368) +** +*/ + +/* +** History: +** +** 2006~2012 Eke-Eke (Genesis Plus GX): +** Huge thanks to Nemesis, lot of those fixes came from his tests on Sega Genesis hardware +** More informations at http://gendev.spritesmind.net/forum/viewtopic.php?t=386 +** +** TODO: +** +** - core documentation +** - BUSY flag support +** +** CHANGELOG: +** +** 26-09-2017 Eke-Eke (Genesis Plus GX): +** - fixed EG counter loopback behavior (verified on YM3438 die) +** - reverted changes to EG rates 2-7 increment values +** +** xx-xx-xxxx +** - fixed LFO implementation: +** .added support for CH3 special mode: fixes various sound effects (birds in Warlock, bug sound in Aladdin...) +** .inverted LFO AM waveform: fixes Spider-Man & Venom : Separation Anxiety (intro), California Games (surfing event) +** .improved LFO timing accuracy: now updated AFTER sample output, like EG/PG updates, and without any precision loss anymore. +** - improved internal timers emulation +** - adjusted lowest EG rates increment values +** - fixed Attack Rate not being updated in some specific cases (Batman & Robin intro) +** - fixed EG behavior when Attack Rate is maximal +** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation +** - implemented EG output immediate changes on register writes +** - fixed YM2612 initial values (after the reset): fixes missing intro in B.O.B +** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many other games using GEMS sound engine) +** - implemented accurate CSM mode emulation +** - implemented accurate SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Stix & many other games) +** - implemented accurate address/data ports behavior +** +** 06-23-2007 Zsolt Vasvari: +** - changed the timing not to require the use of floating point calculations +** +** 03-08-2003 Jarek Burczynski: +** - fixed YM2608 initial values (after the reset) +** - fixed flag and irqmask handling (YM2608) +** - fixed BUFRDY flag handling (YM2608) +** +** 14-06-2003 Jarek Burczynski: +** - implemented all of the YM2608 status register flags +** - implemented support for external memory read/write via YM2608 +** - implemented support for deltat memory limit register in YM2608 emulation +** +** 22-05-2003 Jarek Burczynski: +** - fixed LFO PM calculations (copy&paste bugfix) +** +** 08-05-2003 Jarek Burczynski: +** - fixed SSG support +** +** 22-04-2003 Jarek Burczynski: +** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608) +** +** 15-04-2003 Jarek Burczynski: +** - added support for YM2608's register 0x110 - status mask +** +** 01-12-2002 Jarek Burczynski: +** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608) +** The addressing patch used for early Neo-Geo games can be removed now. +** +** 26-11-2002 Jarek Burczynski, Nicola Salmoria: +** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to: +** - added emulation of YM2608 drums. +** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608) +** +** 16-08-2002 Jarek Burczynski: +** - binary exact Envelope Generator (verified on real YM2203); +** identical to YM2151 +** - corrected 'off by one' error in feedback calculations (when feedback is off) +** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610) +** +** 18-12-2001 Jarek Burczynski: +** - added SSG-EG support (verified on real YM2203) +** +** 12-08-2001 Jarek Burczynski: +** - corrected sin_tab and tl_tab data (verified on real chip) +** - corrected feedback calculations (verified on real chip) +** - corrected phase generator calculations (verified on real chip) +** - corrected envelope generator calculations (verified on real chip) +** - corrected FM volume level (YM2610 and YM2610B). +** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) : +** this was needed to calculate YM2610 FM channels output correctly. +** (Each FM channel is calculated as in other chips, but the output of the channel +** gets shifted right by one *before* sending to accumulator. That was impossible to do +** with previous implementation). +** +** 23-07-2001 Jarek Burczynski, Nicola Salmoria: +** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip) +** +** 11-06-2001 Jarek Burczynski: +** - corrected end of sample bug in ADPCMA_calc_cha(). +** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits). +** +** 08-12-98 hiro-shi: +** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA +** move ROM limit check.(CALC_CH? -> 2610Write1/2) +** test program (ADPCMB_TEST) +** move ADPCM A/B end check. +** ADPCMB repeat flag(no check) +** change ADPCM volume rate (8->16) (32->48). +** +** 09-12-98 hiro-shi: +** change ADPCM volume. (8->16, 48->64) +** replace ym2610 ch0/3 (YM-2610B) +** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff. +** add ADPCM_SHIFT_MASK +** change ADPCMA_DECODE_MIN/MAX. +*/ + + + + +/************************************************************************/ +/* comment of hiro-shi(Hiromitsu Shioya) */ +/* YM2610(B) = OPN-B */ +/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/************************************************************************/ + +#define RSM_ENABLE 0 +#define RSM_FRAC 10 + +#include +#include /* for memset */ +#include /* for NULL */ +#include +#include +#include "mamedef.h" +#include "mame_ym2612fm.h" + +static stream_sample_t *DUMMYBUF = NULL; + +/* shared function building option */ +#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B||BUILD_YM2612||BUILD_YM3438) +#define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608) + +/* globals */ +#define TYPE_SSG 0x01 /* SSG support */ +#define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */ +#define TYPE_6CH 0x04 /* FM 6CH / 3CH */ +#define TYPE_DAC 0x08 /* YM2612's DAC device */ +#define TYPE_ADPCM 0x10 /* two ADPCM units */ +#define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */ + + +#define TYPE_YM2203 (TYPE_SSG) +#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM) +#define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610) +#define TYPE_YM2612 (TYPE_DAC |TYPE_LFOPAN |TYPE_6CH) + + +/* globals */ +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<>3) + +/* sin waveform table in 'decibel' scale */ +static unsigned int sin_tab[SIN_LEN]; + +/* sustain level table (3dB per step) */ +/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ +/* 1, 2, 4, 8, 16, 32, 64 (value)*/ +/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ + +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +/* attenuation value (10 bits) = (SL << 2) << 3 */ +#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) ) +static const UINT32 sl_table[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const UINT8 eg_inc[19*RATE_STEPS]={ + +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ +/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ +/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ +/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ + +/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ +/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ +/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(17) in this table - it's directly in the code */ +static const UINT8 eg_rate_select2612[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates (same as Rate 0) */ +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), + +/* rates 00-11 */ +/* +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +*/ +O(18),O(18),O( 2),O( 3), /* from Nemesis's tests on real YM2612 hardware */ +O( 0),O( 1),O( 2),O( 2), /* Nemesis's tests */ + +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 12 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 13 */ +O( 8),O( 9),O(10),O(11), + +/* rate 14 */ +O(12),O(13),O(14),O(15), + +/* rate 15 */ +O(16),O(16),O(16),O(16), + +/* 32 dummy rates (same as 15 3) */ +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ +/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ +/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates */ +/* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */ + +/* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */ +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), + +/* rates 00-11 */ +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 12 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 32 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) + +}; +#undef O + +static const UINT8 dt_tab[4 * 32]={ +/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ +/* FD=0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* FD=1 */ + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, +/* FD=2 */ + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, +/* FD=3 */ + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 +}; + + +/* OPN key frequency number -> key code follow table */ +/* fnum higher 4bit -> keycode lower 2bit */ +static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; + + +/* 8 LFO speed parameters */ +/* each value represents number of samples that one LFO level will last for */ +static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; + + + +/*There are 4 different LFO AM depths available, they are: + 0 dB, 1.4 dB, 5.9 dB, 11.8 dB + Here is how it is generated (in EG steps): + + 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 + 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 + 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 + + (1.4 dB is losing precision as you can see) + + It's implemented as generator from 0..126 with step 2 then a shift + right N times, where N is: + 8 for 0 dB + 3 for 1.4 dB + 1 for 5.9 dB + 0 for 11.8 dB +*/ +static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0}; + + + +/*There are 8 different LFO PM depths available, they are: + 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) + + Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 + (bits 8,9,10 = FNUM MSB from OCT/FNUM register) + + Here we store only first quarter (positive one) of full waveform. + Full table (lfo_pm_table) containing all 128 waveforms is build + at run (init) time. + + One value in table below represents 4 (four) basic LFO steps + (1 PM step = 4 AM steps). + + For example: + at LFO SPEED=0 (which is 108 samples per basic LFO step) + one value from "lfo_pm_output" table lasts for 432 consecutive + samples (4*108=432) and one full LFO waveform cycle lasts for 13824 + samples (32*432=13824; 32 because we store only a quarter of whole + waveform in the table below) +*/ +static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ +/* FNUM BIT 4: 000 0001xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1}, + +/* FNUM BIT 5: 000 0010xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3}, + +/* FNUM BIT 6: 000 0100xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1}, +/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6}, + +/* FNUM BIT 7: 000 1000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1}, +/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2}, +/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, + +/* FNUM BIT 8: 001 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2}, +/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3}, +/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4}, +/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, + +/* FNUM BIT 9: 010 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2}, +/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4}, +/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6}, +/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8}, +/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, + +/* FNUM BIT10: 100 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4}, +/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8}, +/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc}, +/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10}, +/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, +/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}, + +}; + +/* all 128 LFO PM waveforms */ +static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ + +/* register number to channel number , slot offset */ +#define OPN_CHAN(N) (N&3) +#define OPN_SLOT(N) ((N>>2)&3) + +/* slot number */ +#define SLOT1 0 +#define SLOT2 2 +#define SLOT3 1 +#define SLOT4 3 + +/* bit0 = Right enable , bit1 = Left enable */ +#define OUTD_RIGHT 1 +#define OUTD_LEFT 2 +#define OUTD_CENTER 3 + + +/* save output as raw 16-bit sample */ +/* #define SAVE_SAMPLE */ + +#ifdef SAVE_SAMPLE +static FILE *sample[1]; + #if 1 /*save to MONO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = rt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + + +/* + * Pan law table + */ + +static const UINT16 panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + + +/* struct describing a single operator (SLOT) */ +typedef struct +{ + INT32 *DT; /* detune :dt_tab[DT] */ + UINT8 KSR; /* key scale rate :3-KSR */ + UINT32 ar; /* attack rate */ + UINT32 d1r; /* decay rate */ + UINT32 d2r; /* sustain rate */ + UINT32 rr; /* release rate */ + UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ + UINT32 mul; /* multiple :ML_TABLE[ML] */ + + /* Phase Generator */ + UINT32 phase; /* phase counter */ + INT32 Incr; /* phase step */ + + /* Envelope Generator */ + UINT8 state; /* phase type */ + UINT32 tl; /* total level: TL << 3 */ + INT32 volume; /* envelope counter */ + UINT32 sl; /* sustain level:sl_table[SL] */ + UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */ + + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT8 eg_sh_d1r; /* (decay state) */ + UINT8 eg_sel_d1r; /* (decay state) */ + UINT8 eg_sh_d2r; /* (sustain state) */ + UINT8 eg_sel_d2r; /* (sustain state) */ + UINT8 eg_sh_rr; /* (release state) */ + UINT8 eg_sel_rr; /* (release state) */ + + UINT8 ssg; /* SSG-EG waveform */ + UINT8 ssgn; /* SSG-EG negated output */ + + UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */ + + /* LFO */ + UINT32 AMmask; /* AM enable flag */ + +} FM_SLOT; + +typedef struct +{ + FM_SLOT SLOT[4]; /* four SLOTs (operators) */ + + UINT8 ALGO; /* algorithm */ + UINT8 FB; /* feedback shift */ + INT32 op1_out[2]; /* op1 output for feedback */ + + INT32 *connect1; /* SLOT1 output pointer */ + INT32 *connect3; /* SLOT3 output pointer */ + INT32 *connect2; /* SLOT2 output pointer */ + INT32 *connect4; /* SLOT4 output pointer */ + + INT32 *mem_connect;/* where to put the delayed sample (MEM) */ + INT32 mem_value; /* delayed sample (MEM) value */ + + INT32 pms; /* channel PMS */ + UINT8 ams; /* channel AMS */ + + UINT32 fc; /* fnum,blk:adjusted to sample rate */ + UINT8 kcode; /* key code: */ + UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + UINT8 Muted; + + INT32 pan_volume_l; + INT32 pan_volume_r; +} FM_CH; + + +typedef struct +{ + /* running_device *device; */ + void * param; /* this chip parameter */ + double freqbase; /* frequency base */ + int timer_prescaler; /* timer prescaler */ + UINT8 irq; /* interrupt level */ + UINT8 irqmask; /* irq mask */ +#if FM_BUSY_FLAG_SUPPORT + TIME_TYPE busy_expiry_time; /* expiry time of the busy status */ +#endif + UINT32 clock; /* master clock (Hz) */ + UINT32 rate; /* internal sampling rate (Hz) */ +#if RSM_ENABLE + INT32 rateratio; /* resampling ratio */ + INT32 framecnt; /* resampling frames count*/ + FMSAMPLE cur_sample[2]; /* previous sample */ + FMSAMPLE prev_sample[2]; /* previous sample */ +#endif + UINT8 address; /* address register */ + UINT8 status; /* status flag */ + UINT32 mode; /* mode CSM / 3SLOT */ + UINT8 fn_h; /* freq latch */ + UINT8 prescaler_sel; /* prescaler selector */ + INT32 TA; /* timer a */ + INT32 TAC; /* timer a counter */ + UINT8 TB; /* timer b */ + INT32 TBC; /* timer b counter */ + /* local time tables */ + INT32 dt_tab[8][32]; /* DeTune table */ + /* Extention Timer and IRQ handler */ + FM_TIMERHANDLER timer_handler; + FM_IRQHANDLER IRQ_Handler; + const ssg_callbacks *SSG; +} FM_ST; + + + +/***********************************************************/ +/* OPN unit */ +/***********************************************************/ + +/* OPN 3slot struct */ +typedef struct +{ + UINT32 fc[3]; /* fnum3,blk3: calculated */ + UINT8 fn_h; /* freq3 latch */ + UINT8 kcode[3]; /* key code */ + UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + UINT8 key_csm; /* CSM mode Key-ON flag */ +} FM_3SLOT; + +/* OPN/A/B common state */ +typedef struct +{ + UINT8 type; /* chip type */ + FM_ST ST; /* general state */ + FM_3SLOT SL3; /* 3 slot mode state */ + FM_CH *P_CH; /* pointer of CH */ + unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */ + + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */ + UINT32 eg_timer_add; /* step of eg_timer */ + UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */ + + + /* there are 2048 FNUMs that can be generated using FNUM/BLK registers + but LFO works with one more bit of a precision so we really need 4096 elements */ + UINT32 fn_table[4096]; /* fnumber->increment counter */ + UINT32 fn_max; /* maximal phase increment (used for phase overflow) */ + + /* LFO */ + UINT8 lfo_cnt; /* current LFO phase (out of 128) */ + UINT32 lfo_timer; /* current LFO phase runs at LFO frequency */ + UINT32 lfo_timer_add; /* step of lfo_timer */ + UINT32 lfo_timer_overflow; /* LFO timer overflows every N samples (depends on LFO frequency) */ + UINT32 LFO_AM; /* current LFO AM step */ + UINT32 LFO_PM; /* current LFO PM step */ + + INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ + INT32 mem; /* one sample delay memory */ + INT32 out_fm[6]; /* outputs of working channels */ + +} FM_OPN; + +/* here's the virtual YM2612 */ +typedef struct +{ + UINT8 REGS[512]; /* registers */ + FM_OPN OPN; /* OPN state */ + FM_CH CH[6]; /* channel state */ + UINT8 addr_A1; /* address line A1 */ + + /* dac output (YM2612) */ + /* int dacen; */ + UINT8 dacen; + UINT8 dac_test; + INT32 dacout; + UINT8 MuteDAC; + + UINT8 WaveOutMode; + INT32 WaveL; + INT32 WaveR; +} YM2612; + +/* log output level */ +#define LOG_ERR 3 /* ERROR */ +#define LOG_WAR 2 /* WARNING */ +#define LOG_INF 1 /* INFORMATION */ +#define LOG_LEVEL LOG_INF + +#ifndef __RAINE__ +#define LOG(n,x) do { if( (n)>=LOG_LEVEL ) logerror x; } while (0) +#endif + +/* limitter */ +#define Limit(val, max,min) { \ + if ( val > max ) val = max; \ + else if ( val < min ) val = min; \ +} + +#if 0 +#define USE_VGM_INIT_SWITCH +static UINT8 IsVGMInit = 0; +#endif +static UINT8 PseudoSt = 0x00; +/*#include +static FILE* hFile; +static UINT32 FileSample;*/ + +/* status set and IRQ handling */ +INLINE void FM_STATUS_SET(FM_ST *ST,int flag) +{ + /* set status flag */ + ST->status |= flag; + if ( !(ST->irq) && (ST->status & ST->irqmask) ) + { + ST->irq = 1; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,1); + } +} + +/* status reset and IRQ handling */ +INLINE void FM_STATUS_RESET(FM_ST *ST,int flag) +{ + /* reset status flag */ + ST->status &=~flag; + if ( (ST->irq) && !(ST->status & ST->irqmask) ) + { + ST->irq = 0; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,0); + } +} + +/* IRQ mask set */ +INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag) +{ + ST->irqmask = flag; + /* IRQ handling check */ + FM_STATUS_SET(ST,0); + FM_STATUS_RESET(ST,0); +} + +INLINE void FM_KEYON(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + /* Note by Valley Bell: + I assume that the CSM mode shouldn't affect channels + other than FM3, so I added a check for it here.*/ + if( !SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + SLOT->key = 1; +} + +INLINE void FM_KEYOFF(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if (SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) + { +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) /* workaround for VGMs trimmed with VGMTool */ + { + SLOT->state = EG_OFF; + SLOT->volume = MAX_ATT_INDEX; + SLOT->vol_out= MAX_ATT_INDEX; + } + else +#endif + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } + + SLOT->key = 0; +} + +INLINE void FM_KEYON_CSM(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if( !SLOT->key && !OPN->SL3.key_csm) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } +} + +INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + if (!SLOT->key) + { +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + { + SLOT->state = EG_OFF; + SLOT->volume = MAX_ATT_INDEX; + SLOT->vol_out= MAX_ATT_INDEX; + } + else +#endif + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } +} + +/* OPN Mode Register Write */ +INLINE void set_timers( FM_OPN *OPN, FM_ST *ST, void *n, int v ) +{ + /* b7 = CSM MODE */ + /* b6 = 3 slot mode */ + /* b5 = reset b */ + /* b4 = reset a */ + /* b3 = timer enable b */ + /* b2 = timer enable a */ + /* b1 = load b */ + /* b0 = load a */ + + if ((OPN->ST.mode ^ v) & 0xC0) + { + /* phase increment need to be recalculated */ + OPN->P_CH[2].SLOT[SLOT1].Incr=-1; + + /* CSM mode disabled and CSM key ON active*/ + if (((v & 0xC0) != 0x80) && OPN->SL3.key_csm) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT1); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT2); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT3); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT4); + OPN->SL3.key_csm = 0; + } + } + + /* reset Timer b flag */ + if( v & 0x20 ) + FM_STATUS_RESET(ST,0x02); + /* reset Timer a flag */ + if( v & 0x10 ) + FM_STATUS_RESET(ST,0x01); + /* load b */ + if ((v&2) && !(ST->mode&2)) + { + ST->TBC = ( 256-ST->TB)<<4; + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,(int)ST->clock); + } + /* load a */ + if ((v&1) && !(ST->mode&1)) + { + ST->TAC = (1024-ST->TA); + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,(int)ST->clock); + ST->TAC *= 4096; + } + + ST->mode = (UINT32)v; +} + + +/* Timer A Overflow */ +INLINE void TimerAOver(FM_ST *ST) +{ + /* set status (if enabled) */ + if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01); + /* clear or reload the counter */ + ST->TAC = (1024-ST->TA); + if (ST->timer_handler) (ST->timer_handler)(ST->param,0,ST->TAC * ST->timer_prescaler,(int)ST->clock); + ST->TAC *= 4096; +} +/* Timer B Overflow */ +INLINE void TimerBOver(FM_ST *ST) +{ + /* set status (if enabled) */ + if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02); + /* clear or reload the counter */ + ST->TBC = ( 256-ST->TB)<<4; + if (ST->timer_handler) (ST->timer_handler)(ST->param,1,ST->TBC * ST->timer_prescaler,(int)ST->clock); +} + + +#if FM_INTERNAL_TIMER +/* ----- internal timer mode , update timer */ +/* Valley Bell: defines fixed */ + +/* ---------- calculate timer A ---------- */ + #define INTERNAL_TIMER_A(ST,CSM_CH) \ + { \ + if( (ST)->TAC && ((ST)->timer_handler==0) ) \ + if( ((ST)->TAC -= (int)((ST)->freqbase*4096)) <= 0 ) \ + { \ + TimerAOver( ST ); \ + /* CSM mode total level latch and auto key on */ \ + if( (ST)->mode & 0x80 ) \ + CSMKeyControll( OPN, CSM_CH ); \ + } \ + } +/* ---------- calculate timer B ---------- */ + #define INTERNAL_TIMER_B(ST,step) \ + { \ + if( (ST)->TBC && ((ST)->timer_handler==0) ) \ + if( ((ST)->TBC -= (int)((ST)->freqbase*4096*step)) <= 0 ) \ + TimerBOver( ST ); \ + } +#else /* FM_INTERNAL_TIMER */ +/* external timer mode */ +#define INTERNAL_TIMER_A(ST,CSM_CH) +#define INTERNAL_TIMER_B(ST,step) +#endif /* FM_INTERNAL_TIMER */ + + + +#if FM_BUSY_FLAG_SUPPORT +#define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME) +INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST) +{ + if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 ) + { + if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(ST->device->machine)) > 0) + return ST->status | 0x80; /* with busy */ + /* expire */ + FM_BUSY_CLEAR(ST); + } + return ST->status; +} +INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock ) +{ + TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(ATTOTIME_IN_HZ(ST->clock), busyclock * ST->timer_prescaler); + ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(ST->device->machine), expiry_period); +} +#else +#define FM_STATUS_FLAG(ST) ((ST)->status) +#define FM_BUSY_SET(ST,bclock) {} +#define FM_BUSY_CLEAR(ST) {} +#endif + + +/* set algorithm connection */ +INLINE void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch ) +{ + INT32 *carrier = &OPN->out_fm[ch]; + + INT32 **om1 = &CH->connect1; + INT32 **om2 = &CH->connect3; + INT32 **oc1 = &CH->connect2; + + INT32 **memc = &CH->mem_connect; + + switch( CH->ALGO ) + { + case 0: + /* M1---C1---MEM---M2---C2---OUT */ + *om1 = &OPN->c1; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 1: + /* M1------+-MEM---M2---C2---OUT */ + /* C1-+ */ + *om1 = &OPN->mem; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 2: + /* M1-----------------+-C2---OUT */ + /* C1---MEM---M2-+ */ + *om1 = &OPN->c2; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 3: + /* M1---C1---MEM------+-C2---OUT */ + /* M2-+ */ + *om1 = &OPN->c1; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->c2; + break; + case 4: + /* M1---C1-+-OUT */ + /* M2---C2-+ */ + /* MEM: not used */ + *om1 = &OPN->c1; + *oc1 = carrier; + *om2 = &OPN->c2; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + case 5: + /* +----C1----+ */ + /* M1-+-MEM---M2-+-OUT */ + /* +----C2----+ */ + *om1 = 0; /* special mark */ + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->m2; + break; + case 6: + /* M1---C1-+ */ + /* M2-+-OUT */ + /* C2-+ */ + /* MEM: not used */ + *om1 = &OPN->c1; + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + case 7: + /* M1-+ */ + /* C1-+-OUT */ + /* M2-+ */ + /* C2-+ */ + /* MEM: not used*/ + *om1 = carrier; + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + } + + CH->connect4 = carrier; +} + +/* set detune & multiple */ +INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v) +{ + SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1; + SLOT->DT = ST->dt_tab[(v>>4)&7]; + CH->SLOT[SLOT1].Incr=-1; +} + +/* set total level */ +INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v) +{ + SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ + (void)CH; + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; +} + +/* set attack rate & key scale */ +INLINE void set_ar_ksr(UINT8 type, FM_CH *CH,FM_SLOT *SLOT,int v) +{ + UINT8 old_KSR = SLOT->KSR; + (void)type; + + SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->KSR = 3-(v>>6); + if (SLOT->KSR != old_KSR) + { + CH->SLOT[SLOT1].Incr=-1; + } + + /* Even if it seems unnecessary, in some odd case, KSR and KC are both modified */ + /* and could result in SLOT->kc remaining unchanged. */ + /* In such case, AR values would not be recalculated despite SLOT->ar has changed */ + /* This fixes the introduction music of Batman & Robin (Eke-Eke) */ + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware */ + } +} + +/* set decay rate */ +INLINE void set_dr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr]; +} + +/* set sustain rate */ +INLINE void set_sr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr]; +} + +/* set release rate */ +INLINE void set_sl_rr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->sl = sl_table[ v>>4 ]; + + /* check EG state changes */ + if ((SLOT->state == EG_DEC) && (SLOT->volume >= (INT32)(SLOT->sl))) + SLOT->state = EG_SUS; + + SLOT->rr = 34 + ((v&0x0f)<<2); + + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr]; +} + +/* advance LFO to next sample */ +INLINE void advance_lfo(FM_OPN *OPN) +{ + if (OPN->lfo_timer_overflow) /* LFO enabled ? */ + { + /* increment LFO timer */ + OPN->lfo_timer += OPN->lfo_timer_add; + + /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */ + while (OPN->lfo_timer >= OPN->lfo_timer_overflow) + { + OPN->lfo_timer -= OPN->lfo_timer_overflow; + + /* There are 128 LFO steps */ + OPN->lfo_cnt = ( OPN->lfo_cnt + 1 ) & 127; + + /* Valley Bell: Replaced old code (non-inverted triangle) with + the one from Genesis Plus GX 1.71. */ + /* triangle (inverted) */ + /* AM: from 126 to 0 step -2, 0 to 126 step +2 */ + if (OPN->lfo_cnt<64) + OPN->LFO_AM = (UINT32)(OPN->lfo_cnt ^ 63) << 1; + else + OPN->LFO_AM = (UINT32)(OPN->lfo_cnt & 63) << 1; + + /* PM works with 4 times slower clock */ + OPN->LFO_PM = OPN->lfo_cnt >> 2; + } + } +} + +INLINE void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT) +{ + /* unsigned int out; */ + unsigned int i = 4; /* four operators per channel */ + + do + { + switch(SLOT->state) + { + case EG_ATT: /* attack phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_ar)-1))) + { + /* update attenuation level */ + SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4; + + /* check phase transition*/ + if (SLOT->volume <= MIN_ATT_INDEX) + { + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */ + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + break; + + case EG_DEC: /* decay phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_d1r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* check phase transition*/ + if (SLOT->volume >= (INT32)(SLOT->sl)) + SLOT->state = EG_SUS; + } + break; + + case EG_SUS: /* sustain phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_d2r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* check phase transition*/ + if ( SLOT->volume >= MAX_ATT_INDEX ) + SLOT->volume = MAX_ATT_INDEX; + /* do not change SLOT->state (verified on real chip) */ + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + break; + + case EG_REL: /* release phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_rr)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; + /* check phase transition */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; + + /* check phase transition*/ + if (SLOT->volume >= MAX_ATT_INDEX) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + + } + break; + } + + /* Valley Bell: These few lines are missing in Genesis Plus GX' ym2612 core file. + Disabling them fixes the SSG-EG. + Additional Note: Asterix and the Great Rescue: Level 1 sounds "better" with these lines, + but less accurate. */ + #if 0 + out = ((UINT32)SLOT->volume); + + /* negate output (changes come from alternate bit, init comes from attack bit) */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL)) + out ^= MAX_ATT_INDEX; + + /* we need to store the result here because we are going to change ssgn + in next instruction */ + SLOT->vol_out = out + SLOT->tl; + #endif + + SLOT++; + i--; + } while (i); + +} + +/* SSG-EG update process */ +/* The behavior is based upon Nemesis tests on real hardware */ +/* This is actually executed before each samples */ +INLINE void update_ssg_eg_channel(FM_SLOT *SLOT) +{ + unsigned int i = 4; /* four operators per channel */ + + do + { + /* detect SSG-EG transition */ + /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */ + /* if an Attack Phase is programmed, inversion can occur on each sample */ + if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL)) + { + if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */ + { + /* set inversion flag */ + if (SLOT->ssg & 0x02) + SLOT->ssgn = 4; + + /* force attenuation level during decay phases */ + if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04))) + SLOT->volume = MAX_ATT_INDEX; + } + else /* loop SSG-EG */ + { + /* toggle output inversion flag or reset Phase Generator */ + if (SLOT->ssg & 0x02) + SLOT->ssgn ^= 4; + else + SLOT->phase = 0; + + /* same as Key ON */ + if (SLOT->state != EG_ATT) + { + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* Attack Rate is maximal: directly switch to Decay or Substain */ + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + } + } + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* next slot */ + SLOT++; + i--; + } while (i); +} + + +INLINE void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum) +{ + UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; + INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ]; + + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + UINT8 blk = (block_fnum&0x7000) >> 12; + UINT32 fn = block_fnum & 0xfff; + + /* recalculate keyscale code */ + /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/ + /* This really stupid bug caused a read outside of the + array [size 0x10] and returned invalid values. + This caused an annoying vibrato for some notes. + (Note: seems to be a copy-and-paste from OPNWriteReg -> case 0xA0) + Why are MAME cores always SOO buggy ?! */ + /* Oh, and before I forget: it's correct in fm.c */ + int kc = (blk<<2) | opn_fktable[fn >> 8]; + /* Thanks to Blargg - his patch that helped me to find this bug */ + + /* recalculate (frequency) phase increment counter */ + int fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc]; + + /* (frequency) phase overflow (credits to Nemesis) */ + if (fc < 0) fc += OPN->fn_max; + + /* update phase */ + SLOT->phase += (fc * SLOT->mul) >> 1; + } + else /* LFO phase modulation = zero */ + { + SLOT->phase += SLOT->Incr; + } +} + +INLINE void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH) +{ + UINT32 block_fnum = CH->block_fnum; + + UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; + INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ]; + + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + UINT8 blk = (block_fnum&0x7000) >> 12; + UINT32 fn = block_fnum & 0xfff; + + /* recalculate keyscale code */ + /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/ + /* the same stupid bug as above */ + int kc = (blk<<2) | opn_fktable[fn >> 8]; + + /* recalculate (frequency) phase increment counter */ + int fc = (OPN->fn_table[fn]>>(7-blk)); + + /* (frequency) phase overflow (credits to Nemesis) */ + int finc = fc + CH->SLOT[SLOT1].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1; + + finc = fc + CH->SLOT[SLOT2].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1; + + finc = fc + CH->SLOT[SLOT3].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1; + + finc = fc + CH->SLOT[SLOT4].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1; + } + else /* LFO phase modulation = zero */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +/* update phase increment and envelope generator */ +INLINE void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc ) +{ + int ksr = kc >> SLOT->KSR; + + fc += SLOT->DT[kc]; + + /* detects frequency overflow (credits to Nemesis) */ + if (fc < 0) fc += OPN->fn_max; + + /* (frequency) phase increment counter */ + SLOT->Incr = (fc * SLOT->mul) >> 1; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 32+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware (Attack phase is blocked) */ + } + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + + SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr]; + } +} + +/* update phase increment counters */ +INLINE void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH ) +{ + if( CH->SLOT[SLOT1].Incr==-1) + { + int fc = CH->fc; + int kc = CH->kcode; + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc ); + } +} + +#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) + +INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm) +{ + UINT32 p; + + p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm) +{ + UINT32 p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ]; + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE void chan_calc(YM2612 *F2612, FM_OPN *OPN, FM_CH *CH) +{ + UINT32 AM = OPN->LFO_AM >> CH->ams; + unsigned int eg_out; + + if (CH->Muted) + return; + + OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0; + + *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ + + eg_out = volume_calc(&CH->SLOT[SLOT1]); + { + INT32 out = CH->op1_out[0] + CH->op1_out[1]; + CH->op1_out[0] = CH->op1_out[1]; + + if( !CH->connect1 ) + { + /* algorithm 5 */ + OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0]; + } + else + { + /* other algorithms */ + *CH->connect1 += CH->op1_out[0]; + } + + + CH->op1_out[1] = 0; + if( eg_out < ENV_QUIET ) /* SLOT 1 */ + { + if (!CH->FB) + out=0; + + CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<FB) ); + } + } + + eg_out = volume_calc(&CH->SLOT[SLOT3]); + if( eg_out < ENV_QUIET ) /* SLOT 3 */ + *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2); + + eg_out = volume_calc(&CH->SLOT[SLOT2]); + if( eg_out < ENV_QUIET ) /* SLOT 2 */ + *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1); + + eg_out = volume_calc(&CH->SLOT[SLOT4]); + if( eg_out < ENV_QUIET ) /* SLOT 4 */ + *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2); + + + /* store current MEM */ + CH->mem_value = OPN->mem; + + /* update phase counters AFTER output calculations */ + if(CH->pms) + { + /* add support for 3 slot mode */ + if ((OPN->ST.mode & 0xC0) && (CH == &F2612->CH[2])) + { + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum); + } + else update_phase_lfo_channel(OPN, CH); + } + else /* no LFO phase modulation */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +static void FMCloseTable( void ) +{ +#ifdef SAVE_SAMPLE + fclose(sample[0]); +#endif + return; +} + + +/* CSM Key Controll */ +INLINE void CSMKeyControll(FM_OPN *OPN, FM_CH *CH) +{ + /* all key ON (verified by Nemesis on real hardware) */ + FM_KEYON_CSM(OPN,CH,SLOT1); + FM_KEYON_CSM(OPN,CH,SLOT2); + FM_KEYON_CSM(OPN,CH,SLOT3); + FM_KEYON_CSM(OPN,CH,SLOT4); + OPN->SL3.key_csm = 1; +} + +#ifdef __STATE_H__ +/* FM channel save , internal state only */ +static void FMsave_state_channel(running_device *device,FM_CH *CH,int num_ch) +{ + int slot , ch; + + for(ch=0;chop1_out); + state_save_register_device_item(device, ch, CH->fc); + /* slots */ + for(slot=0;slot<4;slot++) + { + FM_SLOT *SLOT = &CH->SLOT[slot]; + state_save_register_device_item(device, ch * 4 + slot, SLOT->phase); + state_save_register_device_item(device, ch * 4 + slot, SLOT->state); + state_save_register_device_item(device, ch * 4 + slot, SLOT->volume); + } + } +} + +static void FMsave_state_st(running_device *device,FM_ST *ST) +{ +#if FM_BUSY_FLAG_SUPPORT + state_save_register_device_item(device, 0, ST->busy_expiry_time.seconds ); + state_save_register_device_item(device, 0, ST->busy_expiry_time.attoseconds ); +#endif + state_save_register_device_item(device, 0, ST->address ); + state_save_register_device_item(device, 0, ST->irq ); + state_save_register_device_item(device, 0, ST->irqmask ); + state_save_register_device_item(device, 0, ST->status ); + state_save_register_device_item(device, 0, ST->mode ); + state_save_register_device_item(device, 0, ST->prescaler_sel ); + state_save_register_device_item(device, 0, ST->fn_h ); + state_save_register_device_item(device, 0, ST->TA ); + state_save_register_device_item(device, 0, ST->TAC ); + state_save_register_device_item(device, 0, ST->TB ); + state_save_register_device_item(device, 0, ST->TBC ); +} +#endif /* _STATE_H */ + +#if BUILD_OPN +/* write a OPN mode register 0x20-0x2f */ +static void OPNWriteMode(FM_OPN *OPN, int r, int v) +{ + UINT8 c; + FM_CH *CH; + + switch(r) + { + case 0x21: /* Test */ + break; + case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */ + if (v&8) /* LFO enabled ? */ + { + #if 0 + if (!OPN->lfo_timer_overflow) + { + /* restart LFO */ + OPN->lfo_cnt = 0; + OPN->lfo_timer = 0; + OPN->LFO_AM = 0; + OPN->LFO_PM = 0; + } + #endif + + OPN->lfo_timer_overflow = lfo_samples_per_step[v&7] << LFO_SH; + } + else + { + /* Valley Bell: Ported from Genesis Plus GX 1.71 + hold LFO waveform in reset state */ + OPN->lfo_timer_overflow = 0; + OPN->lfo_timer = 0; + OPN->lfo_cnt = 0; + + + OPN->LFO_PM = 0; + OPN->LFO_AM = 126; + /* OPN->lfo_timer_overflow = 0; */ + } + break; + case 0x24: /* timer A High 8*/ + OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2); + break; + case 0x25: /* timer A Low 2*/ + OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3); + break; + case 0x26: /* timer B */ + OPN->ST.TB = (UINT8)v; + break; + case 0x27: /* mode, timer control */ + set_timers( OPN, &(OPN->ST),OPN->ST.param,v ); + break; + case 0x28: /* key on / off */ + c = v & 0x03; + if( c == 3 ) break; + if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3; + CH = OPN->P_CH; + CH = &CH[c]; + if(v&0x10) FM_KEYON(OPN,CH,SLOT1); else FM_KEYOFF(OPN,CH,SLOT1); + if(v&0x20) FM_KEYON(OPN,CH,SLOT2); else FM_KEYOFF(OPN,CH,SLOT2); + if(v&0x40) FM_KEYON(OPN,CH,SLOT3); else FM_KEYOFF(OPN,CH,SLOT3); + if(v&0x80) FM_KEYON(OPN,CH,SLOT4); else FM_KEYOFF(OPN,CH,SLOT4); + break; + } +} + +/* write a OPN register (0x30-0xff) */ +static void OPNWriteReg(FM_OPN *OPN, int r, int v) +{ + FM_CH *CH; + FM_SLOT *SLOT; + + UINT8 c = OPN_CHAN(r); + + if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */ + + if (r >= 0x100) c+=3; + + CH = OPN->P_CH; + CH = &CH[c]; + + SLOT = &(CH->SLOT[OPN_SLOT(r)]); + + switch( r & 0xf0 ) { + case 0x30: /* DET , MUL */ + set_det_mul(&OPN->ST,CH,SLOT,v); + break; + + case 0x40: /* TL */ + set_tl(CH,SLOT,v); + break; + + case 0x50: /* KS, AR */ + set_ar_ksr(OPN->type,CH,SLOT,v); + break; + + case 0x60: /* bit7 = AM ENABLE, DR */ + set_dr(OPN->type, SLOT,v); + + if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */ + { + SLOT->AMmask = (v&0x80) ? ~0 : 0; + } + break; + + case 0x70: /* SR */ + set_sr(OPN->type,SLOT,v); + break; + + case 0x80: /* SL, RR */ + set_sl_rr(OPN->type,SLOT,v); + break; + + case 0x90: /* SSG-EG */ + SLOT->ssg = v&0x0f; + + /* recalculate EG output */ + if (SLOT->state > EG_REL) + { + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* SSG-EG envelope shapes : + + E AtAlH + 1 0 0 0 \\\\ + + 1 0 0 1 \___ + + 1 0 1 0 \/\/ + ___ + 1 0 1 1 \ + + 1 1 0 0 //// + ___ + 1 1 0 1 / + + 1 1 1 0 /\/\ + + 1 1 1 1 /___ + + + E = SSG-EG enable + + + The shapes are generated using Attack, Decay and Sustain phases. + + Each single character in the diagrams above represents this whole + sequence: + + - when KEY-ON = 1, normal Attack phase is generated (*without* any + difference when compared to normal mode), + + - later, when envelope level reaches minimum level (max volume), + the EG switches to Decay phase (which works with bigger steps + when compared to normal mode - see below), + + - later when envelope level passes the SL level, + the EG swithes to Sustain phase (which works with bigger steps + when compared to normal mode - see below), + + - finally when envelope level reaches maximum level (min volume), + the EG switches to Attack phase again (depends on actual waveform). + + Important is that when switch to Attack phase occurs, the phase counter + of that operator will be zeroed-out (as in normal KEY-ON) but not always. + (I havent found the rule for that - perhaps only when the output level is low) + + The difference (when compared to normal Envelope Generator mode) is + that the resolution in Decay and Sustain phases is 4 times lower; + this results in only 256 steps instead of normal 1024. + In other words: + when SSG-EG is disabled, the step inside of the EG is one, + when SSG-EG is enabled, the step is four (in Decay and Sustain phases). + + Times between the level changes are the same in both modes. + + + Important: + Decay 1 Level (so called SL) is compared to actual SSG-EG output, so + it is the same in both SSG and no-SSG modes, with this exception: + + when the SSG-EG is enabled and is generating raising levels + (when the EG output is inverted) the SL will be found at wrong level !!! + For example, when SL=02: + 0 -6 = -6dB in non-inverted EG output + 96-6 = -90dB in inverted EG output + Which means that EG compares its level to SL as usual, and that the + output is simply inverted afterall. + + + The Yamaha's manuals say that AR should be set to 0x1f (max speed). + That is not necessary, but then EG will be generating Attack phase. + + */ + + + break; + + case 0xa0: + switch( OPN_SLOT(r) ) + { + case 0: /* 0xa0-0xa2 : FNUM1 */ +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->ST.fn_h = CH->block_fnum >> 8; +#endif + { + UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v; + UINT8 blk = OPN->ST.fn_h>>3; + /* keyscale code */ + CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + CH->fc = OPN->fn_table[fn*2]>>(7-blk); + + /* store fnum in clear form for LFO PM calculations */ + CH->block_fnum = (blk<<11) | fn; + + CH->SLOT[SLOT1].Incr=-1; + } + break; + case 1: /* 0xa4-0xa6 : FNUM2,BLK */ + OPN->ST.fn_h = v&0x3f; +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) /* workaround for stupid Kega Fusion init block */ + CH->block_fnum = (OPN->ST.fn_h << 8) | (CH->block_fnum & 0xFF); +#endif + break; + case 2: /* 0xa8-0xaa : 3CH FNUM1 */ +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->SL3.fn_h = OPN->SL3.block_fnum[c] >> 8; +#endif + if(r < 0x100) + { + UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v; + UINT8 blk = OPN->SL3.fn_h>>3; + /* keyscale code */ + OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk); + OPN->SL3.block_fnum[c] = (blk<<11) | fn; + (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1; + } + break; + case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ + if(r < 0x100) + { + OPN->SL3.fn_h = v&0x3f; +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->SL3.block_fnum[c] = (OPN->SL3.fn_h << 8) | (OPN->SL3.block_fnum[c] & 0xFF); +#endif + } + break; + } + break; + + case 0xb0: + switch( OPN_SLOT(r) ) + { + case 0: /* 0xb0-0xb2 : FB,ALGO */ + { + unsigned char feedback = ((v>>3)&7); + CH->ALGO = v&7; + CH->FB = feedback ? feedback + 6 : 0; + setup_connection( OPN, CH, c ); + } + break; + case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */ + if( OPN->type & TYPE_LFOPAN) + { + /* b0-2 PMS */ + CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ + + /* b4-5 AMS */ + CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03]; + + /* PAN : b7 = L, b6 = R */ + OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0; + OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0; + + } + break; + } + break; + } +} + +/* initialize time tables */ +static void init_timetables(FM_OPN *OPN, double freqbase) +{ + int i,d; + double rate; + + /* DeTune table */ + for (d = 0;d <= 3;d++) + { + for (i = 0;i <= 31;i++) + { + rate = ((double)dt_tab[d*32 + i]) * freqbase * (1<<(FREQ_SH-10)); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ + OPN->ST.dt_tab[d][i] = (INT32) rate; + OPN->ST.dt_tab[d+4][i] = -OPN->ST.dt_tab[d][i]; + } + } + + /* there are 2048 FNUMs that can be generated using FNUM/BLK registers + but LFO works with one more bit of a precision so we really need 4096 elements */ + /* calculate fnumber -> increment counter table */ + for(i = 0; i < 4096; i++) + { + /* freq table for octave 7 */ + /* OPN phase increment counter = 20bit */ + /* the correct formula is : F-Number = (144 * fnote * 2^20 / M) / 2^(B-1) */ + /* where sample clock is M/144 */ + /* this means the increment value for one clock sample is FNUM * 2^(B-1) = FNUM * 64 for octave 7 */ + /* we also need to handle the ratio between the chip frequency and the emulated frequency (can be 1.0) */ + OPN->fn_table[i] = (UINT32)( (double)i * 32 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ + } + + /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */ + OPN->fn_max = (UINT32)( (double)0x20000 * freqbase * (1<<(FREQ_SH-10)) ); +} + +/* prescaler set (and make time tables) */ +static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres) +{ + /* frequency base */ + OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0; + + /* EG is updated every 3 samples */ + OPN->eg_timer_add = (UINT32)((1<ST.freqbase); + OPN->eg_timer_overflow = ( 3 ) * (1<lfo_timer_add = (UINT32)((1<ST.freqbase); + + /* Timer base time */ + OPN->ST.timer_prescaler = timer_prescaler; + + /* SSG part prescaler set */ + if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.param, OPN->ST.clock * 2 / SSGpres ); + + /* make time tables */ + init_timetables(OPN, OPN->ST.freqbase); +} + +static void reset_channels( FM_ST *ST , FM_CH *CH , int num ) +{ + int c,s; + (void)ST; + + for( c = 0 ; c < num ; c++ ) + { + /* memset(&CH[c], 0x00, sizeof(FM_CH)); */ + CH[c].mem_value = 0; + CH[c].op1_out[0] = 0; + CH[c].op1_out[1] = 0; + CH[c].fc = 0; + for(s = 0 ; s < 4 ; s++ ) + { + /* memset(&CH[c].SLOT[s], 0x00, sizeof(FM_SLOT)); */ + CH[c].SLOT[s].Incr = -1; + CH[c].SLOT[s].key = 0; + CH[c].SLOT[s].phase = 0; + CH[c].SLOT[s].ssg = 0; + CH[c].SLOT[s].ssgn = 0; + CH[c].SLOT[s].state= EG_OFF; + CH[c].SLOT[s].volume = MAX_ATT_INDEX; + CH[c].SLOT[s].vol_out= MAX_ATT_INDEX; + } + } +} + +/* initialize generic tables */ +static void init_tables(void) +{ + signed int i,x; + signed int n; + double o,m; + + /* build Linear Power Table */ + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 2; /* 13 bits here (as in real chip) */ + + + /* 14 bits (with sign bit) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + /* one entry in the 'Power' table use the following format, xxxxxyyyyyyyys with: */ + /* s = sign bit */ + /* yyyyyyyy = 8-bits decimal part (0-TL_RES_LEN) */ + /* xxxxx = 5-bits integer 'shift' value (0-31) but, since Power table output is 13 bits, */ + /* any value above 13 (included) would be discarded. */ + for (i=1; i<13; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + } + + /* build Logarithmic Sinus table */ + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + /* 13-bits (8.5) value is formatted for above 'Power' table */ + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + } + + /* build LFO PM modulation table */ + for(i = 0; i < 8; i++) /* 8 PM depths */ + { + UINT8 fnum; + for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */ + { + UINT8 value; + UINT8 step; + UINT32 offset_depth = i; + UINT32 offset_fnum_bit; + UINT32 bit_tmp; + + for (step=0; step<8; step++) + { + value = 0; + for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */ + { + if (fnum & (1<CH; + FMSAMPLE *bufOut = buffer; + int i; +#if !RSM_ENABLE + FMSAMPLE bufTmp[2]; +#endif + + ym2612_pre_generate(chip); + + if (!frames) + { + update_ssg_eg_channel(&cch[0].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[1].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[2].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[3].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[4].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[5].SLOT[SLOT1]); + } + + /* buffering */ + for(i=0 ; i < frames ; i++) + { +#if RSM_ENABLE + while(F2612->OPN.ST.framecnt >= F2612->OPN.ST.rateratio)/* Copy-Pasta from Nuked */ + { + /* Copy-Pasta from Nuked */ + F2612->OPN.ST.prev_sample[0] = F2612->OPN.ST.cur_sample[0]; + F2612->OPN.ST.prev_sample[1] = F2612->OPN.ST.cur_sample[1]; + ym2612_generate_one_native(chip, F2612->OPN.ST.cur_sample); + F2612->OPN.ST.framecnt -= F2612->OPN.ST.rateratio; + /* Copy-Pasta from Nuked */ + } + if (mix) + { + *bufOut++ += (FMSAMPLE)((F2612->OPN.ST.prev_sample[0] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[0] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + *bufOut++ += (FMSAMPLE)((F2612->OPN.ST.prev_sample[1] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[1] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + } else { + *bufOut++ = (FMSAMPLE)((F2612->OPN.ST.prev_sample[0] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[0] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + *bufOut++ = (FMSAMPLE)((F2612->OPN.ST.prev_sample[1] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[1] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + } + F2612->OPN.ST.framecnt += 1 << RSM_FRAC; +#else + if (mix) + { + ym2612_generate_one_native(chip, bufTmp); + bufOut[0] += bufTmp[0]; + bufOut[1] += bufTmp[1]; + } + else + { + ym2612_generate_one_native(chip, bufOut); + } + bufOut += 2; +#endif + } + /* ym2612_post_generate(chip, frames); */ +} + +void ym2612_pre_generate(void *chip) +{ + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + FM_CH *cch = F2612->CH; + + /* refresh PG and EG */ + refresh_fc_eg_chan( OPN, &cch[0] ); + refresh_fc_eg_chan( OPN, &cch[1] ); + if( (OPN->ST.mode & 0xc0) ) + { + /* 3SLOT MODE */ + if( cch[2].SLOT[SLOT1].Incr==-1) + { + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT4] , cch[2].fc , cch[2].kcode ); + } + } else + refresh_fc_eg_chan( OPN, &cch[2] ); + refresh_fc_eg_chan( OPN, &cch[3] ); + refresh_fc_eg_chan( OPN, &cch[4] ); + refresh_fc_eg_chan( OPN, &cch[5] ); +} + +void ym2612_generate_one_native(void *chip, FMSAMPLE *buffer) +{ + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + INT32 *out_fm = OPN->out_fm; + FM_CH *cch = F2612->CH; + INT32 dacout; + int lt,rt; + + if (! F2612->MuteDAC) + dacout = F2612->dacout; + else + dacout = 0; + + /* clear outputs */ + out_fm[0] = 0; + out_fm[1] = 0; + out_fm[2] = 0; + out_fm[3] = 0; + out_fm[4] = 0; + out_fm[5] = 0; + + /* update SSG-EG output */ + update_ssg_eg_channel(&cch[0].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[1].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[2].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[3].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[4].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[5].SLOT[SLOT1]); + + /* calculate FM */ + if (! F2612->dac_test) + { + chan_calc(F2612, OPN, &cch[0]); + chan_calc(F2612, OPN, &cch[1]); + chan_calc(F2612, OPN, &cch[2]); + chan_calc(F2612, OPN, &cch[3]); + chan_calc(F2612, OPN, &cch[4]); + if( F2612->dacen ) + cch[5].connect4 += dacout; + else + chan_calc(F2612, OPN, &cch[5]); + } + else + { + out_fm[0] = out_fm[1] = dacout; + out_fm[2] = out_fm[3] = dacout; + out_fm[5] = dacout; + } + + /* advance LFO */ + advance_lfo(OPN); + + /* advance envelope generator */ + OPN->eg_timer += OPN->eg_timer_add; + while (OPN->eg_timer >= OPN->eg_timer_overflow) + { + /* reset EG timer */ + OPN->eg_timer -= OPN->eg_timer_overflow; + /* increment EG counter */ + OPN->eg_cnt++; + /* EG counter is 12-bit only and zero value is skipped (verified on real hardware) */ + if (OPN->eg_cnt == 4096) + OPN->eg_cnt = 1; + + /* advance envelope generator */ + advance_eg_channel(OPN, &cch[0].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[1].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[2].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[3].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[4].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[5].SLOT[SLOT1]); + } + + /*fprintf(hFile, "%u", FileSample, out_fm[0]); + for (lt = 0; lt < 6; lt ++) + fprintf(hFile, "\t%d", out_fm[lt]); + fprintf(hFile, "\n"); + FileSample ++;*/ + + if (out_fm[0] > 8192) out_fm[0] = 8192; + else if (out_fm[0] < -8192) out_fm[0] = -8192; + if (out_fm[1] > 8192) out_fm[1] = 8192; + else if (out_fm[1] < -8192) out_fm[1] = -8192; + if (out_fm[2] > 8192) out_fm[2] = 8192; + else if (out_fm[2] < -8192) out_fm[2] = -8192; + if (out_fm[3] > 8192) out_fm[3] = 8192; + else if (out_fm[3] < -8192) out_fm[3] = -8192; + if (out_fm[4] > 8192) out_fm[4] = 8192; + else if (out_fm[4] < -8192) out_fm[4] = -8192; + if (out_fm[5] > 8192) out_fm[5] = 8192; + else if (out_fm[5] < -8192) out_fm[5] = -8192; + +#define PANLAW_L(ch, chpan) (((out_fm[ch]>>0) * cch[ch].pan_volume_l / 65535) & OPN->pan[chpan]); +#define PANLAW_R(ch, chpan) (((out_fm[ch]>>0) * cch[ch].pan_volume_r / 65535) & OPN->pan[chpan]); + + /* 6-channels mixing */ + lt = PANLAW_L(0, 0); + rt = PANLAW_R(0, 1); + lt += PANLAW_L(1, 2); + rt += PANLAW_R(1, 3); + lt += PANLAW_L(2, 4); + rt += PANLAW_R(2, 5); + lt += PANLAW_L(3, 6); + rt += PANLAW_R(3, 7); + if (! F2612->dac_test) + { + lt += PANLAW_L(4, 8); + rt += PANLAW_R(4, 9); + } + else + { + lt += dacout; + rt += dacout; + } + lt += PANLAW_L(5, 10); + rt += PANLAW_R(5, 11); + +#undef PANLAW_L +#undef PANLAW_R + + /* Limit( lt, MAXOUT, MINOUT ); */ + /* Limit( rt, MAXOUT, MINOUT ); */ + +#ifdef SAVE_SAMPLE + SAVE_ALL_CHANNELS +#endif + + /* buffering */ + if (F2612->WaveOutMode & 0x01) + F2612->WaveL = lt; + if (F2612->WaveOutMode & 0x02) + F2612->WaveR = rt; + if (F2612->WaveOutMode ^ 0x03) + F2612->WaveOutMode ^= 0x03; + + buffer[0] = (FMSAMPLE)(F2612->WaveL / 2); + buffer[1] = (FMSAMPLE)(F2612->WaveR / 2); + + /* CSM mode: if CSM Key ON has occured, CSM Key OFF need to be sent */ + /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */ + OPN->SL3.key_csm <<= 1; + + /* timer A control */ + /* INTERNAL_TIMER_A( &OPN->ST , cch[2] ) */ + { + if( OPN->ST.TAC && (OPN->ST.timer_handler==0) ) + if( (OPN->ST.TAC -= (int)(OPN->ST.freqbase*4096)) <= 0 ) + { + TimerAOver( &OPN->ST ); + /* CSM mode total level latch and auto key on */ + if( OPN->ST.mode & 0x80 ) + CSMKeyControll( OPN, &cch[2] ); + } + } + + /* CSM Mode Key ON still disabled */ + if (OPN->SL3.key_csm & 2) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&cch[2],SLOT1); + FM_KEYOFF_CSM(&cch[2],SLOT2); + FM_KEYOFF_CSM(&cch[2],SLOT3); + FM_KEYOFF_CSM(&cch[2],SLOT4); + OPN->SL3.key_csm = 0; + } +} + +#if 0 +void ym2612_post_generate(void *chip, int length) +{ + YM2612 *F2612 = (YM2612 *)chip; + /* timer B control */ + INTERNAL_TIMER_B(&F2612->OPN.ST, length); +} +#endif + +#ifdef __STATE_H__ +void ym2612_postload(void *chip) +{ + if (chip) + { + YM2612 *F2612 = (YM2612 *)chip; + int r; + + /* DAC data & port */ + F2612->dacout = ((int)F2612->REGS[0x2a] - 0x80) << 6; /* level unknown */ + F2612->dacen = F2612->REGS[0x2d] & 0x80; + /* OPN registers */ + /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ + for(r=0x30;r<0x9e;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); + OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); + } + /* FB / CONNECT , L / R / AMS / PMS */ + for(r=0xb0;r<0xb6;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); + OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); + } + /* channels */ + /*FM_channel_postload(F2612->CH,6);*/ + } +} + +static void YM2612_save_state(YM2612 *F2612, running_device *device) +{ + state_save_register_device_item_array(device, 0, F2612->REGS); + FMsave_state_st(device,&F2612->OPN.ST); + FMsave_state_channel(device,F2612->CH,6); + /* 3slots */ + state_save_register_device_item_array(device, 0, F2612->OPN.SL3.fc); + state_save_register_device_item(device, 0, F2612->OPN.SL3.fn_h); + state_save_register_device_item_array(device, 0, F2612->OPN.SL3.kcode); + /* address register1 */ + state_save_register_device_item(device, 0, F2612->addr_A1); +} +#endif /* _STATE_H */ + +/* initialize YM2612 emulator(s) */ +/* void * ym2612_init(void *param, running_device *device, int clock, int rate, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler) */ +void * ym2612_init(void *param, int clock, int rate, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler) +{ + YM2612 *F2612; + int i = 0; + + if (clock <= 0 || rate <= 0) + return NULL; /* Forbid zero clock and sample rate */ + + /* allocate extend state space */ + /* F2612 = auto_alloc_clear(device->machine, YM2612); */ + F2612 = (YM2612 *)malloc(sizeof(YM2612)); + if (F2612 == NULL) + return NULL; + memset(F2612, 0x00, sizeof(YM2612)); + /* allocate total level table (128kb space) */ + init_tables(); + + F2612->OPN.ST.param = param; + F2612->OPN.type = TYPE_YM2612; + F2612->OPN.P_CH = F2612->CH; + /* F2612->OPN.ST.device = device; */ + F2612->OPN.ST.clock = clock; +#if RSM_ENABLE + F2612->OPN.ST.rate = 53267; + F2612->OPN.ST.rateratio = (INT32)(UINT32)((((UINT64)144 * rate) << RSM_FRAC) / clock); + F2612->OPN.ST.framecnt = 1 << RSM_FRAC; + memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2); + memset(&(F2612->OPN.ST.prev_sample), 0x00, sizeof(FMSAMPLE) * 2); +#else + F2612->OPN.ST.rate = rate; +#endif + /* F2612->OPN.ST.irq = 0; */ + /* F2612->OPN.ST.status = 0; */ + /* Extend handler */ + F2612->OPN.ST.timer_handler = timer_handler; + F2612->OPN.ST.IRQ_Handler = IRQHandler; + + if (PseudoSt) + F2612->WaveOutMode = 0x01; + else + F2612->WaveOutMode = 0x03; + + for (i = 0; i < 6; i++) + { + F2612->CH[i].pan_volume_l = 46340; + F2612->CH[i].pan_volume_r = 46340; + } + + /*hFile = fopen("YM2612.log", "wt"); + fprintf(hFile, "Clock: %d, Sample Rate: %d\n", clock, rate); + fprintf(hFile, "Sample\tCh 0\tCh 1\tCh 2\tCh 3\tCh 4\tCh 5\n"); + FileSample = 0;*/ + +#ifdef __STATE_H__ + YM2612_save_state(F2612, device); +#endif + return F2612; +} + +/* shut down emulator */ +void ym2612_shutdown(void *chip) +{ + YM2612 *F2612 = (YM2612 *)chip; + /* fclose(hFile); */ + + FMCloseTable(); + /* auto_free(F2612->OPN.ST.device->machine, F2612); */ + free(F2612); +} + +/* reset one of chip */ +void ym2612_reset_chip(void *chip) +{ + int i; + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + + OPNSetPres( OPN, 6*24, 6*24, 0); + /* status clear */ + FM_IRQMASK_SET(&OPN->ST,0x03); + FM_BUSY_CLEAR(&OPN->ST); + /* OPNWriteMode(OPN,0x27,0x30); */ /* mode 0 , timer reset */ + +#if RSM_ENABLE + /* Resampler's state */ + F2612->OPN.ST.framecnt = 1 << RSM_FRAC; + memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2); + memset(&(F2612->OPN.ST.prev_sample), 0x00, sizeof(FMSAMPLE) * 2); +#endif + + OPN->eg_timer = 0; + OPN->eg_cnt = 0; + + OPN->lfo_timer = 0; + OPN->lfo_cnt = 0; + OPN->LFO_AM = 126; + OPN->LFO_PM = 0; + + OPN->ST.TAC = 0; + OPN->ST.TBC = 0; + + OPN->SL3.key_csm = 0; + + OPN->ST.status = 0; + OPN->ST.mode = 0; + + memset(F2612->REGS, 0x00, sizeof(UINT8) * 512); + + OPNWriteMode(OPN,0x22,0x00); + + OPNWriteMode(OPN,0x27,0x30); + OPNWriteMode(OPN,0x26,0x00); + OPNWriteMode(OPN,0x25,0x00); + OPNWriteMode(OPN,0x24,0x00); + + reset_channels( &OPN->ST , &F2612->CH[0] , 6 ); + + for(i = 0xb6 ; i >= 0xb4 ; i-- ) + { + OPNWriteReg(OPN,i ,0xc0); + OPNWriteReg(OPN,i|0x100,0xc0); + } + for(i = 0xb2 ; i >= 0x30 ; i-- ) + { + OPNWriteReg(OPN,i ,0); + OPNWriteReg(OPN,i|0x100,0); + } + + /* DAC mode clear */ + F2612->dacen = 0; + F2612->dac_test = 0; + F2612->dacout = 0; + + if (F2612->WaveOutMode == 0x02) + F2612->WaveOutMode >>= 1; +} + +/* YM2612 write */ +/* n = number */ +/* a = address */ +/* v = value */ +int ym2612_write(void *chip, int a, UINT8 v) +{ + YM2612 *F2612 = (YM2612 *)chip; + int addr; + + v &= 0xff; /* adjust to 8 bit bus */ + + switch( a&3) + { + case 0: /* address port 0 */ + F2612->OPN.ST.address = v; + F2612->addr_A1 = 0; + break; + + case 1: /* data port 0 */ + if (F2612->addr_A1 != 0) + break; /* verified on real YM2608 */ + + addr = F2612->OPN.ST.address; + F2612->REGS[addr] = v; + switch( addr & 0xf0 ) + { + case 0x20: /* 0x20-0x2f Mode */ + switch( addr ) + { + case 0x2a: /* DAC data (YM2612) */ + ym2612_update_one(chip, DUMMYBUF, 0); + F2612->dacout = ((int)v - 0x80) << 6; /* level unknown */ + break; + case 0x2b: /* DAC Sel (YM2612) */ + /* b7 = dac enable */ + F2612->dacen = v & 0x80; + break; + case 0x2C: /* undocumented: DAC Test Reg */ + /* b5 = volume enable */ + F2612->dac_test = v & 0x20; + break; + default: /* OPN section */ + /* ym2612_update_req(F2612->OPN.ST.param); */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* write register */ + OPNWriteMode(&(F2612->OPN),addr,v); + } + break; + default: /* 0x30-0xff OPN section */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* write register */ + OPNWriteReg(&(F2612->OPN),addr,v); + } + break; + + case 2: /* address port 1 */ + F2612->OPN.ST.address = v; + F2612->addr_A1 = 1; + break; + + case 3: /* data port 1 */ + if (F2612->addr_A1 != 1) + break; /* verified on real YM2608 */ + + addr = F2612->OPN.ST.address; + F2612->REGS[addr | 0x100] = v; + ym2612_update_one(chip, DUMMYBUF, 0); + OPNWriteReg(&(F2612->OPN),addr | 0x100,v); + break; + } + return F2612->OPN.ST.irq; +} + +void ym2612_write_pan(void *chip, int c, unsigned char v) +{ + YM2612 *F2612 = (YM2612 *)chip; + assert((c >= 0) && (c < 6)); + F2612->CH[c].pan_volume_l = panlawtable[v & 0x7F]; + F2612->CH[c].pan_volume_r = panlawtable[0x7F - (v & 0x7F)]; +} + +UINT8 ym2612_read(void *chip,int a) +{ + YM2612 *F2612 = (YM2612 *)chip; + + switch( a&3) + { + case 0: /* status 0 */ + return FM_STATUS_FLAG(&F2612->OPN.ST); + case 1: + case 2: + case 3: + /* LOG(LOG_WAR,("YM2612 #%p:A=%d read unmapped area\n",F2612->OPN.ST.param,a)); */ + return FM_STATUS_FLAG(&F2612->OPN.ST); + } + return 0; +} + +int ym2612_timer_over(void *chip,int c) +{ + YM2612 *F2612 = (YM2612 *)chip; + + if( c ) + { /* Timer B */ + TimerBOver( &(F2612->OPN.ST) ); + } + else + { /* Timer A */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* timer update */ + TimerAOver( &(F2612->OPN.ST) ); + /* CSM mode key,TL controll */ + if ((F2612->OPN.ST.mode & 0xc0) == 0x80) + { /* CSM mode total level latch and auto key on */ + CSMKeyControll( &F2612->OPN, &(F2612->CH[2]) ); + } + } + return F2612->OPN.ST.irq; +} + + +void ym2612_set_mutemask(void *chip, UINT32 MuteMask) +{ + YM2612 *F2612 = (YM2612 *)chip; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 6; CurChn ++) + F2612->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + F2612->MuteDAC = (MuteMask >> 6) & 0x01; + + return; +} + +void ym2612_setoptions(UINT8 Flags) +{ + PseudoSt = (Flags >> 2) & 0x01; + + return; +} +#endif /* (BUILD_YM2612||BUILD_YM3238) */ diff --git a/thirdparty/opnmidi/chips/mame/mame_ym2612fm.h b/thirdparty/opnmidi/chips/mame/mame_ym2612fm.h new file mode 100644 index 000000000..c549d40be --- /dev/null +++ b/thirdparty/opnmidi/chips/mame/mame_ym2612fm.h @@ -0,0 +1,165 @@ +/* + File: fm.h -- header file for software emulation for FM sound generator + +*/ + +#ifndef FM_HHHHH +#define FM_HHHHH + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mamedef.h" + +/* --- select emulation chips --- */ +/* +#define BUILD_YM2203 (HAS_YM2203) // build YM2203(OPN) emulator +#define BUILD_YM2608 (HAS_YM2608) // build YM2608(OPNA) emulator +#define BUILD_YM2610 (HAS_YM2610) // build YM2610(OPNB) emulator +#define BUILD_YM2610B (HAS_YM2610B) // build YM2610B(OPNB?)emulator +#define BUILD_YM2612 (HAS_YM2612) // build YM2612(OPN2) emulator +#define BUILD_YM3438 (HAS_YM3438) // build YM3438(OPN) emulator +*/ +#define BUILD_YM2203 0 +#define BUILD_YM2608 0 +#define BUILD_YM2610 0 +#define BUILD_YM2610B 0 +#define BUILD_YM2612 1 +#define BUILD_YM3438 0 + +#define FM_BUSY_FLAG_SUPPORT 0 + +/* select bit size of output : 8 or 16 */ +#define FM_SAMPLE_BITS 16 + +/* select timer system internal or external */ +#define FM_INTERNAL_TIMER 1 + +/* --- speedup optimize --- */ +/* busy flag enulation , The definition of FM_GET_TIME_NOW() is necessary. */ +/* #define FM_BUSY_FLAG_SUPPORT 1 */ + +/* --- external SSG(YM2149/AY-3-8910)emulator interface port */ +/* used by YM2203,YM2608,and YM2610 */ +typedef struct _ssg_callbacks ssg_callbacks; +struct _ssg_callbacks +{ + void (*set_clock)(void *param, int clock); + void (*write)(void *param, int address, int data); + int (*read)(void *param); + void (*reset)(void *param); +}; + +/* --- external callback funstions for realtime update --- */ + +#if FM_BUSY_FLAG_SUPPORT +#define TIME_TYPE attotime +#define UNDEFINED_TIME attotime_zero +#define FM_GET_TIME_NOW(machine) timer_get_time(machine) +#define ADD_TIMES(t1, t2) attotime_add((t1), (t2)) +#define COMPARE_TIMES(t1, t2) attotime_compare((t1), (t2)) +#define MULTIPLY_TIME_BY_INT(t,i) attotime_mul(t, i) +#endif + +/* compiler dependence */ +#if 0 +#ifndef OSD_CPU_H +#define OSD_CPU_H +typedef unsigned char UINT8; /* unsigned 8bit */ +typedef unsigned short UINT16; /* unsigned 16bit */ +typedef unsigned int UINT32; /* unsigned 32bit */ +typedef signed char INT8; /* signed 8bit */ +typedef signed short INT16; /* signed 16bit */ +typedef signed int INT32; /* signed 32bit */ +#endif /* OSD_CPU_H */ +#endif + + + +typedef stream_sample_t FMSAMPLE; +/* +#if (FM_SAMPLE_BITS==16) +typedef INT16 FMSAMPLE; +#endif +#if (FM_SAMPLE_BITS==8) +typedef unsigned char FMSAMPLE; +#endif +*/ + +typedef void (*FM_TIMERHANDLER)(void *param,int c,int cnt,int clock); +typedef void (*FM_IRQHANDLER)(void *param,int irq); +/* FM_TIMERHANDLER : Stop or Start timer */ +/* int n = chip number */ +/* int c = Channel 0=TimerA,1=TimerB */ +/* int count = timer count (0=stop) */ +/* doube stepTime = step time of one count (sec.)*/ + +/* FM_IRQHHANDLER : IRQ level changing sense */ +/* int n = chip number */ +/* int irq = IRQ level 0=OFF,1=ON */ + +#if (BUILD_YM2612||BUILD_YM3438) + +/** + * @brief Initialize chip and return the instance + * @param param Unused, keep NULL + * @param baseclock YM2612 clock + * @param rate Output sample rate + * @param TimerHandler Keep NULL + * @param IRQHandler Keep NULL + * @return Chip instance or NULL on any error + */ +void * ym2612_init(void *param, int baseclock, int rate, + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler); +/** + * @brief Free chip instance + * @param chip Chip instance + */ +void ym2612_shutdown(void *chip); +/** + * @brief Reset state of the chip + * @param chip Chip instance + */ +void ym2612_reset_chip(void *chip); +/** + * @brief Generate stereo output of specified length + * @param chip Chip instance + * @param buffer Output sound buffer + * @param frames Output buffer size in frames (one frame - two array entries of the buffer) + * @param mix 0 - override buffer data, 1 - mix output data with a content of the buffer + */ +void ym2612_generate(void *chip, FMSAMPLE *buffer, int frames, int mix); +#define ym2612_update_one(chip, buffer, length) ym2612_generate(chip, buffer, length, 0) + +/** + * @brief Single-Sample generation prepare + * @param chip Chip instance + */ +void ym2612_pre_generate(void *chip); +/** + * @brief Generate single stereo PCM frame. Will be used native sample rate of 53267 Hz + * @param chip Chip instance + * @param buffer One stereo PCM frame + */ +void ym2612_generate_one_native(void *chip, FMSAMPLE *buffer); + +/* void ym2612_post_generate(void *chip, int length); */ + +int ym2612_write(void *chip, int a, unsigned char v); +void ym2612_write_pan(void *chip, int c, unsigned char v); +unsigned char ym2612_read(void *chip, int a); +int ym2612_timer_over(void *chip, int c ); +void ym2612_postload(void *chip); + +void ym2612_set_mutemask(void *chip, UINT32 MuteMask); +void ym2612_setoptions(UINT8 Flags); +#endif /* (BUILD_YM2612||BUILD_YM3438) */ + +#ifdef __cplusplus +} +#endif + +#endif /* FM_HHHHH */ diff --git a/thirdparty/opnmidi/chips/mame/mamedef.h b/thirdparty/opnmidi/chips/mame/mamedef.h new file mode 100644 index 000000000..bbe0d36c2 --- /dev/null +++ b/thirdparty/opnmidi/chips/mame/mamedef.h @@ -0,0 +1,70 @@ +#ifndef MAMEDEF_H_ +#define MAMEDEF_H_ + +/* typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h) */ +/* 8-bit values */ +typedef unsigned char UINT8; +typedef signed char INT8; + +/* 16-bit values */ +typedef unsigned short UINT16; +typedef signed short INT16; + +/* 32-bit values */ +#ifndef _WINDOWS_H +typedef unsigned int UINT32; +typedef signed int INT32; +#endif + +/* 64-bit values */ +#if !defined(_WINDOWS_H) && defined(RSM_ENABLE) +# ifdef _MSC_VER +typedef signed __int64 INT64; +typedef unsigned __int64 UINT64; +# else +# if (__WORDSIZE == 64) || defined(_WIN64) +typedef signed long int INT64; +typedef unsigned long int UINT64; +# else +__extension__ typedef signed long long int INT64; +__extension__ typedef unsigned long long int UINT64; +# endif +# endif +#endif + +/* offsets and addresses are 32-bit (for now...) */ +typedef UINT32 offs_t; + +/* stream_sample_t is used to represent a single sample in a sound stream */ +typedef INT16 stream_sample_t; + +#if defined(VGM_BIG_ENDIAN) +#define BYTE_XOR_BE(x) (x) +#elif defined(VGM_LITTLE_ENDIAN) +#define BYTE_XOR_BE(x) ((x) ^ 0x01) +#else +/* don't define BYTE_XOR_BE so that it throws an error when compiling */ +#endif + +#if defined(_MSC_VER) +/* #define INLINE static __forceinline */ +#define INLINE static __inline +#elif defined(__GNUC__) +#define INLINE static __inline__ +#else +#define INLINE static inline +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifdef _DEBUG +#define logerror printf +#else +#define logerror +#endif + +typedef void (*SRATE_CALLBACK)(void*, UINT32); + +#endif /* __MAMEDEF_H__ */ diff --git a/thirdparty/opnmidi/chips/mame_opn2.cpp b/thirdparty/opnmidi/chips/mame_opn2.cpp new file mode 100644 index 000000000..981989ab3 --- /dev/null +++ b/thirdparty/opnmidi/chips/mame_opn2.cpp @@ -0,0 +1,80 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mame_opn2.h" +#include "mame/mame_ym2612fm.h" +#include +#include + +MameOPN2::MameOPN2(OPNFamily f) + : OPNChipBaseT(f) +{ + chip = NULL; + MameOPN2::setRate(m_rate, m_clock); +} + +MameOPN2::~MameOPN2() +{ + ym2612_shutdown(chip); +} + +void MameOPN2::setRate(uint32_t rate, uint32_t clock) +{ + OPNChipBaseT::setRate(rate, clock); + if(chip) + ym2612_shutdown(chip); + uint32_t chipRate = isRunningAtPcmRate() ? rate : nativeRate(); + chip = ym2612_init(NULL, (int)clock, (int)chipRate, NULL, NULL); + ym2612_reset_chip(chip); +} + +void MameOPN2::reset() +{ + OPNChipBaseT::reset(); + ym2612_reset_chip(chip); +} + +void MameOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + ym2612_write(chip, 0 + (int)(port) * 2, (uint8_t)addr); + ym2612_write(chip, 1 + (int)(port) * 2, data); +} + +void MameOPN2::writePan(uint16_t chan, uint8_t data) +{ + ym2612_write_pan(chip, (int)chan, data); +} + +void MameOPN2::nativePreGenerate() +{ + void *chip = this->chip; + ym2612_pre_generate(chip); +} + +void MameOPN2::nativeGenerate(int16_t *frame) +{ + void *chip = this->chip; + ym2612_generate_one_native(chip, frame); +} + +const char *MameOPN2::emulatorName() +{ + return "MAME YM2612"; +} diff --git a/thirdparty/opnmidi/chips/mame_opn2.h b/thirdparty/opnmidi/chips/mame_opn2.h new file mode 100644 index 000000000..e45a794e3 --- /dev/null +++ b/thirdparty/opnmidi/chips/mame_opn2.h @@ -0,0 +1,44 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAME_OPN2_H +#define MAME_OPN2_H + +#include "opn_chip_base.h" + +class MameOPN2 final : public OPNChipBaseT +{ + void *chip; +public: + explicit MameOPN2(OPNFamily f); + ~MameOPN2() override; + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t chan, uint8_t data) override; + void nativePreGenerate() override; + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; +}; + +#endif // MAME_OPN2_H diff --git a/thirdparty/opnmidi/chips/mame_opna.cpp b/thirdparty/opnmidi/chips/mame_opna.cpp new file mode 100644 index 000000000..4424691bc --- /dev/null +++ b/thirdparty/opnmidi/chips/mame_opna.cpp @@ -0,0 +1,202 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2018-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mame_opna.h" +#include "mamefm/fm.h" +#include "mamefm/fmopn_2608rom.h" +#include "mamefm/2608intf.h" +#include "mamefm/resampler.hpp" + +struct MameOPNA::Impl { + ym2608_device dev; + void *chip; + + // typedef chip::SincResampler Resampler; + typedef chip::LinearResampler Resampler; + + Resampler *psgrsm; + int32_t *psgbuffer; + + static const ssg_callbacks cbssg; + + // callbacks + static uint8_t cbInternalReadByte(device_t *, offs_t); + static uint8_t cbExternalReadByte(device_t *, offs_t) { return 0; } + static void cbExternalWriteByte(device_t *, offs_t, uint8_t) {} + static void cbHandleTimer(device_t *, int, int, int) {} + static void cbHandleIRQ(device_t *, int) {} + + static void cbSsgSetClock(device_t *dev, int clock); + static void cbSsgWrite(device_t *dev, int addr, int data); + static int cbSsgRead(device_t *dev); + static void cbSsgReset(device_t *dev); +}; + +const ssg_callbacks MameOPNA::Impl::cbssg = +{ + &cbSsgSetClock, + &cbSsgWrite, + &cbSsgRead, + &cbSsgReset, +}; + + +MameOPNA::MameOPNA(OPNFamily f) + : OPNChipBaseBufferedT(f), impl(new Impl) +{ + impl->chip = NULL; + impl->psgrsm = NULL; + impl->psgbuffer = NULL; + MameOPNA::setRate(m_rate, m_clock); +} + +MameOPNA::~MameOPNA() +{ + delete impl->psgrsm; + delete[] impl->psgbuffer; + ym2608_shutdown(impl->chip); + delete impl; +} + +void MameOPNA::setRate(uint32_t rate, uint32_t clock) +{ + OPNChipBaseBufferedT::setRate(rate, clock); + if(impl->chip) + ym2608_shutdown(impl->chip); + + uint32_t chipRate = isRunningAtPcmRate() ? rate : nativeRate(); + ym2608_device *device = &impl->dev; + void *chip = impl->chip = ym2608_init( + device, (int)clock, (int)chipRate, + &Impl::cbInternalReadByte, &Impl::cbExternalReadByte, + &Impl::cbExternalWriteByte, + &Impl::cbHandleTimer, &Impl::cbHandleIRQ, &Impl::cbssg); + + PSG *psg = &device->m_psg; + memset(psg, 0, sizeof(PSG)); + + uint32_t psgRate = clock / 32; + PSG_init(psg, clock / 4, psgRate); // TODO libOPNMIDI verify clocks + PSG_setVolumeMode(psg, 1); // YM2149 volume mode + + delete impl->psgrsm; + Impl::Resampler *psgrsm = impl->psgrsm = new Impl::Resampler; + psgrsm->init(psgRate, chipRate, 40); + + delete[] impl->psgbuffer; + impl->psgbuffer = new int32_t[2 * psgrsm->calculateInternalSampleSize(buffer_size)]; + + ym2608_reset_chip(chip); + ym2608_write(chip, 0, 0x29); + ym2608_write(chip, 1, 0x9f); +} + +void MameOPNA::reset() +{ + OPNChipBaseBufferedT::reset(); + void *chip = impl->chip; + ym2608_reset_chip(chip); + ym2608_write(chip, 0, 0x29); + ym2608_write(chip, 1, 0x9f); +} + +void MameOPNA::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + void *chip = impl->chip; + ym2608_write(chip, 0 + (int)(port) * 2, (uint8_t)addr); + ym2608_write(chip, 1 + (int)(port) * 2, data); +} + +void MameOPNA::writePan(uint16_t chan, uint8_t data) +{ + void *chip = impl->chip; + ym2608_write_pan(chip, (int)chan, data); +} + +void MameOPNA::nativeGenerateN(int16_t *output, size_t frames) +{ + void *chip = impl->chip; + + FMSAMPLE fmLR[2 * buffer_size]; + FMSAMPLE *fmR = fmLR + buffer_size; + FMSAMPLE *fmbufs[2] = { fmLR, fmR }; + + ym2608_update_one(chip, fmbufs, (int)frames); + + PSG *psg = &impl->dev.m_psg; + Impl::Resampler *psgrsm = impl->psgrsm; + size_t psgframes = psgrsm->calculateInternalSampleSize(frames); + + int32_t *rawpsgLR = impl->psgbuffer; + int32_t *rawpsgR = rawpsgLR + psgframes; + int32_t *rawpsgbufs[2] = { rawpsgLR, rawpsgR }; + PSG_calc_stereo(psg, rawpsgbufs, (int32_t)psgframes); + + int32_t **psgbufs = psgrsm->interpolate(rawpsgbufs, frames, psgframes); + int32_t *psgL = psgbufs[0]; + int32_t *psgR = psgbufs[1]; + + for(size_t i = 0; i < frames; ++i) + { + int32_t l = fmLR[i] + psgL[i]; + l = (l > -32768) ? l : -32768; + l = (l < 32767) ? l : 32767; + int32_t r = fmR[i] + psgR[i]; + r = (r > -32768) ? r : -32768; + r = (r < 32767) ? r : 32767; + output[2 * i] = l; + output[2 * i + 1] = r; + } +} + +const char *MameOPNA::emulatorName() +{ + return "MAME YM2608"; // git 2018-12-15 rev 8ab05c0 +} + +uint8_t MameOPNA::Impl::cbInternalReadByte(device_t *dev, offs_t off) +{ + (void)dev; + return YM2608_ADPCM_ROM[off & 0x1fff]; +} + +void MameOPNA::Impl::cbSsgSetClock(device_t *dev, int clock) +{ + ym2608_device *ym = static_cast(dev); + PSG_set_clock(&ym->m_psg, (uint32_t)clock); +} + +void MameOPNA::Impl::cbSsgWrite(device_t *dev, int addr, int data) +{ + ym2608_device *ym = static_cast(dev); + PSG_writeIO(&ym->m_psg, (uint32_t)addr, (uint32_t)data); +} + +int MameOPNA::Impl::cbSsgRead(device_t *dev) +{ + ym2608_device *ym = static_cast(dev); + return PSG_readIO(&ym->m_psg); +} + +void MameOPNA::Impl::cbSsgReset(device_t *dev) +{ + ym2608_device *ym = static_cast(dev); + return PSG_reset(&ym->m_psg); +} diff --git a/thirdparty/opnmidi/chips/mame_opna.h b/thirdparty/opnmidi/chips/mame_opna.h new file mode 100644 index 000000000..06bc19796 --- /dev/null +++ b/thirdparty/opnmidi/chips/mame_opna.h @@ -0,0 +1,45 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2018-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAME_OPNA_H +#define MAME_OPNA_H + +#include "opn_chip_base.h" + +class MameOPNA final : public OPNChipBaseBufferedT +{ + struct Impl; + Impl *impl; +public: + explicit MameOPNA(OPNFamily f); + ~MameOPNA() override; + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t chan, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerateN(int16_t *output, size_t frames) override; + const char *emulatorName() override; +}; + +#endif diff --git a/thirdparty/opnmidi/chips/mamefm/2608intf.h b/thirdparty/opnmidi/chips/mamefm/2608intf.h new file mode 100644 index 000000000..33518a741 --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/2608intf.h @@ -0,0 +1,48 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * OPNMIDI Library and YM2612 support: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OPNMIDI_MAMEFM_2608INTF_H +#define OPNMIDI_MAMEFM_2608INTF_H + +#include "emu.h" +#include "emu2149.h" + +struct ym2608_device : device_t +{ + void update_request(); + static void update_request(device_t *dev); + + PSG m_psg; +}; + +inline void ym2608_device::update_request() +{ + // libOPNMIDI: use this callback maybe +} + +inline void ym2608_device::update_request(device_t *dev) +{ + static_cast(dev)->update_request(); +} + +#endif diff --git a/thirdparty/opnmidi/chips/mamefm/emu.h b/thirdparty/opnmidi/chips/mamefm/emu.h new file mode 100644 index 000000000..dabe4e4eb --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/emu.h @@ -0,0 +1,104 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * OPNMIDI Library and YM2612 support: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OPNMIDI_MAMEFM_EMU_H +#define OPNMIDI_MAMEFM_EMU_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(M_PI) +# define M_PI 3.14159265358979323846 +#endif + +#if __cplusplus <= 199711L +#define CONSTEXPR +#else +#define CONSTEXPR constexpr +#endif + +#if __cplusplus <= 199711L +#define NULLPTR NULL +#else +#define NULLPTR nullptr +#endif + +typedef uint32_t offs_t; + +inline void vlogerror(const char *fmt, va_list ap) +{ +#if defined(OPNMIDI_MAMEFM_EMU_VERBOSE) + vfprintf(stderr, fmt, ap); +#else + (void)fmt; + (void)ap; +#endif +} + +inline void logerror(const char *fmt, ...) +{ +#if defined(OPNMIDI_MAMEFM_EMU_VERBOSE) + va_list ap; + va_start(ap, fmt); + vlogerror(fmt, ap); + va_end(ap); +#else + (void)fmt; +#endif +} + +struct device_t +{ + void logerror(const char *fmt, ...); + void vlogerror(const char *fmt, va_list ap); +}; + +inline void device_t::logerror(const char *fmt, ...) +{ +#if defined(OPNMIDI_MAMEFM_EMU_VERBOSE) + va_list ap; + va_start(ap, fmt); + vlogerror(fmt, ap); + va_end(ap); +#else + (void)fmt; +#endif +} + +inline void device_t::vlogerror(const char *fmt, va_list ap) +{ +#if defined(OPNMIDI_MAMEFM_EMU_VERBOSE) + ::vlogerror(fmt, ap); +#else + (void)fmt; + (void)ap; +#endif +} + +#endif diff --git a/thirdparty/opnmidi/chips/mamefm/emu2149.c b/thirdparty/opnmidi/chips/mamefm/emu2149.c new file mode 100644 index 000000000..d05f7c62a --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/emu2149.c @@ -0,0 +1,529 @@ +/* SPDX-License-Identifier: MIT */ +/**************************************************************************** + + emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001 + + 2001 04-28 : Version 1.00beta -- 1st Beta Release. + 2001 08-14 : Version 1.10 + 2001 10-03 : Version 1.11 -- Added PSG_set_quality(). + 2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close. + 2002 10-13 : Version 1.14 -- Fixed the envelope unit. + 2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask + 2004 01-11 : Version 1.16 -- Fixed an envelope problem where the envelope + frequency register is written before key-on. + + References: + psg.vhd -- 2000 written by Kazuhiro Tsujikawa. + s_fme7.c -- 1999,2000 written by Mamiya (NEZplug). + ay8910.c -- 1998-2001 Author unknown (MAME). + MSX-Datapack -- 1991 ASCII Corp. + AY-3-8910 data sheet + +*****************************************************************************/ +#include +#include +#include +#include "emu2149.h" + +static UINT32 voltbl[2][32] = { + {0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, + 0x0B, 0x0D, 0x0F, 0x12, + 0x16, 0x1A, 0x1F, 0x25, 0x2D, 0x35, 0x3F, 0x4C, 0x5A, 0x6A, 0x7F, 0x97, + 0xB4, 0xD6, 0xEB, 0xFF}, + {0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, + 0x0B, 0x0B, 0x0F, 0x0F, + 0x16, 0x16, 0x1F, 0x1F, 0x2D, 0x2D, 0x3F, 0x3F, 0x5A, 0x5A, 0x7F, 0x7F, + 0xB4, 0xB4, 0xFF, 0xFF} +}; + +#define GETA_BITS 24 + +static void +internal_refresh (PSG * psg) +{ + if (psg->quality) + { + psg->base_incr = 1 << GETA_BITS; + psg->realstep = (UINT32) ((1 << 31) / psg->rate); + psg->psgstep = (UINT32) ((1 << 31) / (psg->clk / 8)); + psg->psgtime = 0; + } + else + { + psg->base_incr = + (UINT32) ((double) psg->clk * (1 << GETA_BITS) / (8.0 * psg->rate)); + } +} + +EMU2149_API void +PSG_set_clock(PSG * psg, UINT32 c) +{ + psg->clk = c; + internal_refresh(psg); +} + +EMU2149_API void +PSG_set_rate (PSG * psg, UINT32 r) +{ + psg->rate = r ? r : 44100; + internal_refresh (psg); +} + +EMU2149_API void +PSG_set_quality (PSG * psg, UINT32 q) +{ + psg->quality = q; + internal_refresh (psg); +} + +EMU2149_API void +PSG_init (PSG * psg, UINT32 c, UINT32 r) +{ + memset(psg, 0x00, sizeof(PSG)); + + PSG_setVolumeMode (psg, EMU2149_VOL_DEFAULT); + psg->clk = c; + psg->rate = r ? r : 44100; + PSG_set_quality (psg, 0); + psg->stereo_mask[0] = 0x03; + psg->stereo_mask[1] = 0x03; + psg->stereo_mask[2] = 0x03; +} + +EMU2149_API void +PSG_setFlags (PSG * psg, UINT8 flags) +{ + if (flags & EMU2149_ZX_STEREO) + { + /* ABC Stereo */ + psg->stereo_mask[0] = 0x01; + psg->stereo_mask[1] = 0x03; + psg->stereo_mask[2] = 0x02; + } + else + { + psg->stereo_mask[0] = 0x03; + psg->stereo_mask[1] = 0x03; + psg->stereo_mask[2] = 0x03; + } + + return; +} + +EMU2149_API void +PSG_setVolumeMode (PSG * psg, int type) +{ + switch (type) + { + case 1: + psg->voltbl = voltbl[EMU2149_VOL_YM2149]; + break; + case 2: + psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910]; + break; + default: + psg->voltbl = voltbl[EMU2149_VOL_DEFAULT]; + break; + } +} + +EMU2149_API UINT32 +PSG_setMask (PSG *psg, UINT32 mask) +{ + UINT32 ret = 0; + if(psg) + { + ret = psg->mask; + psg->mask = mask; + } + return ret; +} + +EMU2149_API void +PSG_setStereoMask (PSG *psg, UINT32 mask) +{ + if(psg) + { + psg->stereo_mask[0] = (mask >>0) &3; + psg->stereo_mask[1] = (mask >>2) &3; + psg->stereo_mask[2] = (mask >>4) &3; + } +} + +EMU2149_API UINT32 +PSG_toggleMask (PSG *psg, UINT32 mask) +{ + UINT32 ret = 0; + if(psg) + { + ret = psg->mask; + psg->mask ^= mask; + } + return ret; +} + +EMU2149_API void +PSG_reset (PSG * psg) +{ + int i; + + psg->base_count = 0; + + for (i = 0; i < 3; i++) + { + psg->cout[i] = 0; + psg->count[i] = 0x1000; + psg->freq[i] = 0; + psg->edge[i] = 0; + psg->volume[i] = 0; + } + + psg->mask = 0; + + for (i = 0; i < 16; i++) + psg->reg[i] = 0; + psg->adr = 0; + + psg->noise_seed = 0xffff; + psg->noise_count = 0x40; + psg->noise_freq = 0; + + psg->env_volume = 0; + psg->env_ptr = 0; + psg->env_freq = 0; + psg->env_count = 0; + psg->env_pause = 1; + + psg->out = 0; +} + +EMU2149_API void +PSG_delete (PSG * psg) +{ + free (psg); +} + +EMU2149_API UINT8 +PSG_readIO (PSG * psg) +{ + return (UINT8) (psg->reg[psg->adr]); +} + +EMU2149_API UINT8 +PSG_readReg (PSG * psg, UINT32 reg) +{ + return (UINT8) (psg->reg[reg & 0x1f]); + +} + +EMU2149_API void +PSG_writeIO (PSG * psg, UINT32 adr, UINT32 val) +{ + if (adr & 1) + PSG_writeReg (psg, psg->adr, val); + else + psg->adr = val & 0x1f; +} + +/*INLINE*/ static INT16 +calc (PSG * psg) +{ + + int i, noise; + UINT32 incr; + INT32 mix = 0; + + psg->base_count += psg->base_incr; + incr = (psg->base_count >> GETA_BITS); + psg->base_count &= (1 << GETA_BITS) - 1; + + /* Envelope */ + psg->env_count += incr; + while (psg->env_count>=0x10000 && psg->env_freq!=0) + { + if (!psg->env_pause) + { + if(psg->env_face) + psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; + else + psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; + } + + if (psg->env_ptr & 0x20) /* if carry or borrow */ + { + if (psg->env_continue) + { + if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; + if (psg->env_hold) psg->env_pause = 1; + psg->env_ptr = psg->env_face?0:0x1f; + } + else + { + psg->env_pause = 1; + psg->env_ptr = 0; + } + } + + psg->env_count -= psg->env_freq; + } + + /* Noise */ + psg->noise_count += incr; + if (psg->noise_count & 0x40) + { + if (psg->noise_seed & 1) + psg->noise_seed ^= 0x24000; + psg->noise_seed >>= 1; + psg->noise_count -= psg->noise_freq; + } + noise = psg->noise_seed & 1; + + /* Tone */ + for (i = 0; i < 3; i++) + { + psg->count[i] += incr; + if (psg->count[i] & 0x1000) + { + if (psg->freq[i] > 1) + { + psg->edge[i] = !psg->edge[i]; + psg->count[i] -= psg->freq[i]; + } + else + { + psg->edge[i] = 1; + } + } + + psg->cout[i] = 0; /* BS maintaining cout for stereo mix */ + + if (psg->mask&PSG_MASK_CH(i)) + continue; + + if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) + { + if (!(psg->volume[i] & 32)) + psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; + else + psg->cout[i] = psg->voltbl[psg->env_ptr]; + + mix += psg->cout[i]; + } + } + + return (INT16) mix; +} + +EMU2149_API INT16 +PSG_calc (PSG * psg) +{ + if (!psg->quality) + return (INT16) (calc (psg) << 4); + + /* Simple rate converter */ + while (psg->realstep > psg->psgtime) + { + psg->psgtime += psg->psgstep; + psg->out += calc (psg); + psg->out >>= 1; + } + + psg->psgtime = psg->psgtime - psg->realstep; + + return (INT16) (psg->out << 4); +} + +/*INLINE*/ static void +calc_stereo (PSG * psg, INT32 out[2]) +{ + int i, noise; + UINT32 incr; + INT32 l = 0, r = 0; + + psg->base_count += psg->base_incr; + incr = (psg->base_count >> GETA_BITS); + psg->base_count &= (1 << GETA_BITS) - 1; + + /* Envelope */ + psg->env_count += incr; + while (psg->env_count>=0x10000 && psg->env_freq!=0) + { + if (!psg->env_pause) + { + if(psg->env_face) + psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; + else + psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; + } + + if (psg->env_ptr & 0x20) /* if carry or borrow */ + { + if (psg->env_continue) + { + if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; + if (psg->env_hold) psg->env_pause = 1; + psg->env_ptr = psg->env_face?0:0x1f; + } + else + { + psg->env_pause = 1; + psg->env_ptr = 0; + } + } + + psg->env_count -= psg->env_freq; + } + + /* Noise */ + psg->noise_count += incr; + if (psg->noise_count & 0x40) + { + if (psg->noise_seed & 1) + psg->noise_seed ^= 0x24000; + psg->noise_seed >>= 1; + psg->noise_count -= psg->noise_freq; + } + noise = psg->noise_seed & 1; + + /* Tone */ + for (i = 0; i < 3; i++) + { + psg->count[i] += incr; + if (psg->count[i] & 0x1000) + { + if (psg->freq[i] > 1) + { + psg->edge[i] = !psg->edge[i]; + psg->count[i] -= psg->freq[i]; + } + else + { + psg->edge[i] = 1; + } + } + + psg->cout[i] = 0; /* BS maintaining cout for stereo mix */ + + if (psg->mask&PSG_MASK_CH(i)) + continue; + + if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) + { + if (!(psg->volume[i] & 32)) + psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; + else + psg->cout[i] = psg->voltbl[psg->env_ptr]; + + if (psg->stereo_mask[i] & 0x01) + l += psg->cout[i]; + if (psg->stereo_mask[i] & 0x02) + r += psg->cout[i]; + } + } + + out[0] = l << 5; + out[1] = r << 5; + + return; +} + +EMU2149_API void +PSG_calc_stereo (PSG * psg, INT32 **out, INT32 samples) +{ + INT32 *bufMO = out[0]; + INT32 *bufRO = out[1]; + INT32 buffers[2]; + + int i; + + for (i = 0; i < samples; i ++) + { + if (!psg->quality) + { + calc_stereo (psg, buffers); + bufMO[i] = buffers[0]; + bufRO[i] = buffers[1]; + } + else + { + while (psg->realstep > psg->psgtime) + { + psg->psgtime += psg->psgstep; + psg->sprev[0] = psg->snext[0]; + psg->sprev[1] = psg->snext[1]; + calc_stereo (psg, psg->snext); + } + + psg->psgtime -= psg->realstep; + bufMO[i] = (INT32) (((double) psg->snext[0] * (psg->psgstep - psg->psgtime) + + (double) psg->sprev[0] * psg->psgtime) / psg->psgstep); + bufRO[i] = (INT32) (((double) psg->snext[1] * (psg->psgstep - psg->psgtime) + + (double) psg->sprev[1] * psg->psgtime) / psg->psgstep); + } + } +} + +EMU2149_API void +PSG_writeReg (PSG * psg, UINT32 reg, UINT32 val) +{ + int c; + + if (reg > 15) return; + + psg->reg[reg] = (UINT8) (val & 0xff); + switch (reg) + { + case 0: + case 2: + case 4: + case 1: + case 3: + case 5: + c = reg >> 1; + psg->freq[c] = ((psg->reg[c * 2 + 1] & 15) << 8) + psg->reg[c * 2]; + break; + + case 6: + psg->noise_freq = (val == 0) ? 1 : ((val & 31) << 1); + break; + + case 7: + psg->tmask[0] = (val & 1); + psg->tmask[1] = (val & 2); + psg->tmask[2] = (val & 4); + psg->nmask[0] = (val & 8); + psg->nmask[1] = (val & 16); + psg->nmask[2] = (val & 32); + break; + + case 8: + case 9: + case 10: + psg->volume[reg - 8] = val << 1; + + break; + + case 11: + case 12: + psg->env_freq = (psg->reg[12] << 8) + psg->reg[11]; + break; + + case 13: + psg->env_continue = (val >> 3) & 1; + psg->env_attack = (val >> 2) & 1; + psg->env_alternate = (val >> 1) & 1; + psg->env_hold = val & 1; + psg->env_face = psg->env_attack; + psg->env_pause = 0; + psg->env_count = 0x10000 - psg->env_freq; + psg->env_ptr = psg->env_face?0:0x1f; + break; + + case 14: + case 15: + default: + break; + } + + return; +} diff --git a/thirdparty/opnmidi/chips/mamefm/emu2149.h b/thirdparty/opnmidi/chips/mamefm/emu2149.h new file mode 100644 index 000000000..4e41df800 --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/emu2149.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: MIT */ +/* emu2149.h */ +#ifndef EMU2149_H_ +#define EMU2149_H_ +#include "emutypes.h" + +#define EMU2149_API + +#define EMU2149_VOL_DEFAULT 1 +#define EMU2149_VOL_YM2149 0 +#define EMU2149_VOL_AY_3_8910 1 + +#define EMU2149_ZX_STEREO 0x80 + +#define PSG_MASK_CH(x) (1<<(x)) + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct __PSG + { + + /* Volume Table */ + UINT32 *voltbl; + + UINT8 reg[0x20]; + INT32 out; + INT32 cout[3]; + + UINT32 clk, rate, base_incr, quality; + + UINT32 count[3]; + UINT32 volume[3]; + UINT32 freq[3]; + UINT32 edge[3]; + UINT32 tmask[3]; + UINT32 nmask[3]; + UINT32 mask; + UINT32 stereo_mask[3]; + + UINT32 base_count; + + UINT32 env_volume; + UINT32 env_ptr; + UINT32 env_face; + + UINT32 env_continue; + UINT32 env_attack; + UINT32 env_alternate; + UINT32 env_hold; + UINT32 env_pause; + UINT32 env_reset; + + UINT32 env_freq; + UINT32 env_count; + + UINT32 noise_seed; + UINT32 noise_count; + UINT32 noise_freq; + + /* rate converter */ + UINT32 realstep; + UINT32 psgtime; + UINT32 psgstep; + INT32 prev, next; + INT32 sprev[2], snext[2]; + + /* I/O Ctrl */ + UINT32 adr; + + } + PSG; + + EMU2149_API void PSG_set_quality (PSG * psg, UINT32 q); + EMU2149_API void PSG_set_clock(PSG * psg, UINT32 c); + EMU2149_API void PSG_set_rate (PSG * psg, UINT32 r); + EMU2149_API void PSG_init (PSG * psg, UINT32 clk, UINT32 rate); + EMU2149_API void PSG_reset (PSG *); + EMU2149_API void PSG_delete (PSG *); + EMU2149_API void PSG_writeReg (PSG *, UINT32 reg, UINT32 val); + EMU2149_API void PSG_writeIO (PSG * psg, UINT32 adr, UINT32 val); + EMU2149_API UINT8 PSG_readReg (PSG * psg, UINT32 reg); + EMU2149_API UINT8 PSG_readIO (PSG * psg); + EMU2149_API INT16 PSG_calc (PSG *); + EMU2149_API void PSG_calc_stereo (PSG * psg, INT32 **out, INT32 samples); + EMU2149_API void PSG_setFlags (PSG * psg, UINT8 flags); + EMU2149_API void PSG_setVolumeMode (PSG * psg, int type); + EMU2149_API UINT32 PSG_setMask (PSG *, UINT32 mask); + EMU2149_API UINT32 PSG_toggleMask (PSG *, UINT32 mask); + EMU2149_API void PSG_setStereoMask (PSG *psg, UINT32 mask); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/opnmidi/chips/mamefm/emutypes.h b/thirdparty/opnmidi/chips/mamefm/emutypes.h new file mode 100644 index 000000000..91ddd3c7f --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/emutypes.h @@ -0,0 +1,38 @@ +/* + * libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * OPNMIDI Library and YM2612 support: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBOPNMIDI_EMUTYPES_H +#define LIBOPNMIDI_EMUTYPES_H + +#include + +typedef uint8_t UINT8; +typedef uint16_t UINT16; +typedef uint32_t UINT32; +typedef uint64_t UINT64; +typedef int8_t INT8; +typedef int16_t INT16; +typedef int32_t INT32; +typedef int64_t INT64; + +#endif /* LIBOPNMIDI_EMUTYPES_H */ diff --git a/thirdparty/opnmidi/chips/mamefm/fm.cpp b/thirdparty/opnmidi/chips/mamefm/fm.cpp new file mode 100644 index 000000000..fa9b16e09 --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/fm.cpp @@ -0,0 +1,3987 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh +/* +** +** File: fm.c -- software implementation of Yamaha FM sound generator +** +** Copyright Jarek Burczynski (bujar at mame dot net) +** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 1.4.2 (final beta) +** +*/ + +/* +** History: +** +** 2018 Jean Pierre Cimalando (libOPNMIDI) +** - rewrote pooled memory allocations as ordinary new and delete +** +** 2006-2008 Eke-Eke (Genesis Plus GX), MAME backport by R. Belmont. +** - implemented PG overflow, aka "detune bug" (Ariel, Comix Zone, Shaq Fu, Spiderman,...), credits to Nemesis +** - fixed SSG-EG support, credits to Nemesis and additional fixes from Alone Coder +** - modified EG rates and frequency, tested by Nemesis on real hardware +** - implemented LFO phase update for CH3 special mode (Warlock birds, Alladin bug sound) +** - fixed Attack Rate update (Batman & Robin intro) +** - fixed attenuation level at the start of Substain (Gynoug explosions) +** - fixed EG decay->substain transition to handle special cases, like SL=0 and Decay rate is very slow (Mega Turrican tracks 03,09...) +** +** 06-23-2007 Zsolt Vasvari: +** - changed the timing not to require the use of floating point calculations +** +** 03-08-2003 Jarek Burczynski: +** - fixed YM2608 initial values (after the reset) +** - fixed flag and irqmask handling (YM2608) +** - fixed BUFRDY flag handling (YM2608) +** +** 14-06-2003 Jarek Burczynski: +** - implemented all of the YM2608 status register flags +** - implemented support for external memory read/write via YM2608 +** - implemented support for deltat memory limit register in YM2608 emulation +** +** 22-05-2003 Jarek Burczynski: +** - fixed LFO PM calculations (copy&paste bugfix) +** +** 08-05-2003 Jarek Burczynski: +** - fixed SSG support +** +** 22-04-2003 Jarek Burczynski: +** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608) +** +** 15-04-2003 Jarek Burczynski: +** - added support for YM2608's register 0x110 - status mask +** +** 01-12-2002 Jarek Burczynski: +** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608) +** The addressing patch used for early Neo-Geo games can be removed now. +** +** 26-11-2002 Jarek Burczynski, Nicola Salmoria: +** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to: +** - added emulation of YM2608 drums. +** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608) +** +** 16-08-2002 Jarek Burczynski: +** - binary exact Envelope Generator (verified on real YM2203); +** identical to YM2151 +** - corrected 'off by one' error in feedback calculations (when feedback is off) +** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610) +** +** 18-12-2001 Jarek Burczynski: +** - added SSG-EG support (verified on real YM2203) +** +** 12-08-2001 Jarek Burczynski: +** - corrected sin_tab and tl_tab data (verified on real chip) +** - corrected feedback calculations (verified on real chip) +** - corrected phase generator calculations (verified on real chip) +** - corrected envelope generator calculations (verified on real chip) +** - corrected FM volume level (YM2610 and YM2610B). +** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) : +** this was needed to calculate YM2610 FM channels output correctly. +** (Each FM channel is calculated as in other chips, but the output of the channel +** gets shifted right by one *before* sending to accumulator. That was impossible to do +** with previous implementation). +** +** 23-07-2001 Jarek Burczynski, Nicola Salmoria: +** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip) +** +** 11-06-2001 Jarek Burczynski: +** - corrected end of sample bug in ADPCMA_calc_cha(). +** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits). +** +** 08-12-98 hiro-shi: +** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA +** move ROM limit check.(CALC_CH? -> 2610Write1/2) +** test program (ADPCMB_TEST) +** move ADPCM A/B end check. +** ADPCMB repeat flag(no check) +** change ADPCM volume rate (8->16) (32->48). +** +** 09-12-98 hiro-shi: +** change ADPCM volume. (8->16, 48->64) +** replace ym2610 ch0/3 (YM-2610B) +** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff. +** add ADPCM_SHIFT_MASK +** change ADPCMA_DECODE_MIN/MAX. +*/ + + + + +/************************************************************************/ +/* comment of hiro-shi(Hiromitsu Shioya) */ +/* YM2610(B) = OPN-B */ +/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/************************************************************************/ + +#include "emu.h" + +#define YM2610B_WARNING +#include "fm.h" + + +/* include external DELTA-T unit (when needed) */ +#if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) + #include "ymdeltat.h" +#endif + + +#if BUILD_YM2203 +#include "2203intf.h" +#endif /* BUILD_YM2203 */ + +#if BUILD_YM2608 +#include "2608intf.h" +#endif /* BUILD_YM2608 */ + +#if (BUILD_YM2610||BUILD_YM2610B) +#include "2610intf.h" +#endif /* (BUILD_YM2610||BUILD_YM2610B) */ + + +/* shared function building option */ +#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) +#define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608) + + +/* globals */ +#define TYPE_SSG 0x01 /* SSG support */ +#define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */ +#define TYPE_6CH 0x04 /* FM 6CH / 3CH */ +#define TYPE_DAC 0x08 /* YM2612's DAC device */ +#define TYPE_ADPCM 0x10 /* two ADPCM units */ +#define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */ + + +#define TYPE_YM2203 (TYPE_SSG) +#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM) +#define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610) + + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<>3) + +/* sin waveform table in 'decibel' scale */ +static unsigned int sin_tab[SIN_LEN]; + +/* sustain level table (3dB per step) */ +/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ +/* 1, 2, 4, 8, 16, 32, 64 (value)*/ +/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ + +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +#define SC(db) (uint32_t) ( db * (4.0/ENV_STEP) ) +static const uint32_t sl_table[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const uint8_t eg_inc[19*RATE_STEPS]={ +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ +/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ +/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ +/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ + +/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ +/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ +/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(17) in this table - it's directly in the code */ +static const uint8_t eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates */ +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), + +/* rates 00-11 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 12 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 13 */ +O( 8),O( 9),O(10),O(11), + +/* rate 14 */ +O(12),O(13),O(14),O(15), + +/* rate 15 */ +O(16),O(16),O(16),O(16), + +/* 32 dummy rates (same as 15 3) */ +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) + +}; + +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ +/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ +/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const uint8_t eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + +/* rates 00-11 */ +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 12 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 32 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) + +}; +#undef O + +static const uint8_t dt_tab[4 * 32]={ +/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ +/* FD=0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* FD=1 */ + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, +/* FD=2 */ + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, +/* FD=3 */ + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 +}; + + +/* OPN key frequency number -> key code follow table */ +/* fnum higher 4bit -> keycode lower 2bit */ +static const uint8_t opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; + + +/* 8 LFO speed parameters */ +/* each value represents number of samples that one LFO level will last for */ +static const uint32_t lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; + + + +/*There are 4 different LFO AM depths available, they are: + 0 dB, 1.4 dB, 5.9 dB, 11.8 dB + Here is how it is generated (in EG steps): + + 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 + 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 + 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 + + (1.4 dB is losing precision as you can see) + + It's implemented as generator from 0..126 with step 2 then a shift + right N times, where N is: + 8 for 0 dB + 3 for 1.4 dB + 1 for 5.9 dB + 0 for 11.8 dB +*/ +static const uint8_t lfo_ams_depth_shift[4] = {8, 3, 1, 0}; + + + +/*There are 8 different LFO PM depths available, they are: + 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) + + Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 + (bits 8,9,10 = FNUM MSB from OCT/FNUM register) + + Here we store only first quarter (positive one) of full waveform. + Full table (lfo_pm_table) containing all 128 waveforms is build + at run (init) time. + + One value in table below represents 4 (four) basic LFO steps + (1 PM step = 4 AM steps). + + For example: + at LFO SPEED=0 (which is 108 samples per basic LFO step) + one value from "lfo_pm_output" table lasts for 432 consecutive + samples (4*108=432) and one full LFO waveform cycle lasts for 13824 + samples (32*432=13824; 32 because we store only a quarter of whole + waveform in the table below) +*/ +static const uint8_t lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ +/* FNUM BIT 4: 000 0001xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1}, + +/* FNUM BIT 5: 000 0010xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3}, + +/* FNUM BIT 6: 000 0100xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1}, +/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6}, + +/* FNUM BIT 7: 000 1000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1}, +/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2}, +/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, + +/* FNUM BIT 8: 001 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2}, +/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3}, +/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4}, +/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, + +/* FNUM BIT 9: 010 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2}, +/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4}, +/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6}, +/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8}, +/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, + +/* FNUM BIT10: 100 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4}, +/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8}, +/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc}, +/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10}, +/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, +/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}, + +}; + +/* all 128 LFO PM waveforms */ +static int32_t lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ + + +// libOPNMIDI: pan law table +static const uint16_t panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + + +/* register number to channel number , slot offset */ +#define OPN_CHAN(N) (N&3) +#define OPN_SLOT(N) ((N>>2)&3) + +/* slot number */ +#define SLOT1 0 +#define SLOT2 2 +#define SLOT3 1 +#define SLOT4 3 + +/* bit0 = Right enable , bit1 = Left enable */ +#define OUTD_RIGHT 1 +#define OUTD_LEFT 2 +#define OUTD_CENTER 3 + + +/* save output as raw 16-bit sample */ +/* #define SAVE_SAMPLE */ + +#ifdef SAVE_SAMPLE +static FILE *sample[1]; + #if 1 /*save to MONO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = rt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + + +/* struct describing a single operator (SLOT) */ +struct FM_SLOT +{ + int32_t *DT; /* detune :dt_tab[DT] */ + uint8_t KSR; /* key scale rate :3-KSR */ + uint32_t ar; /* attack rate */ + uint32_t d1r; /* decay rate */ + uint32_t d2r; /* sustain rate */ + uint32_t rr; /* release rate */ + uint8_t ksr; /* key scale rate :kcode>>(3-KSR) */ + uint32_t mul; /* multiple :ML_TABLE[ML] */ + + /* Phase Generator */ + uint32_t phase; /* phase counter */ + int32_t Incr; /* phase step */ + + /* Envelope Generator */ + uint8_t state; /* phase type */ + uint32_t tl; /* total level: TL << 3 */ + int32_t volume; /* envelope counter */ + uint32_t sl; /* sustain level:sl_table[SL] */ + uint32_t vol_out; /* current output from EG circuit (without AM from LFO) */ + + uint8_t eg_sh_ar; /* (attack state) */ + uint8_t eg_sel_ar; /* (attack state) */ + uint8_t eg_sh_d1r; /* (decay state) */ + uint8_t eg_sel_d1r; /* (decay state) */ + uint8_t eg_sh_d2r; /* (sustain state) */ + uint8_t eg_sel_d2r; /* (sustain state) */ + uint8_t eg_sh_rr; /* (release state) */ + uint8_t eg_sel_rr; /* (release state) */ + + uint8_t ssg; /* SSG-EG waveform */ + uint8_t ssgn; /* SSG-EG negated output */ + + uint32_t key; /* 0=last key was KEY OFF, 1=KEY ON */ + + /* LFO */ + uint32_t AMmask; /* AM enable flag */ + +}; + +struct FM_CH +{ + FM_SLOT SLOT[4]; /* four SLOTs (operators) */ + + uint8_t ALGO; /* algorithm */ + uint8_t FB; /* feedback shift */ + int32_t op1_out[2]; /* op1 output for feedback */ + + int32_t *connect1; /* SLOT1 output pointer */ + int32_t *connect3; /* SLOT3 output pointer */ + int32_t *connect2; /* SLOT2 output pointer */ + int32_t *connect4; /* SLOT4 output pointer */ + + int32_t *mem_connect;/* where to put the delayed sample (MEM) */ + int32_t mem_value; /* delayed sample (MEM) value */ + + int32_t pms; /* channel PMS */ + uint8_t ams; /* channel AMS */ + + uint32_t fc; /* fnum,blk:adjusted to sample rate */ + uint8_t kcode; /* key code: */ + uint32_t block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + + int32_t pan_volume_l; /* libOPNMIDI: left panning amount */ + int32_t pan_volume_r; /* libOPNMIDI: right panning amount */ +}; + + +struct FM_ST +{ + device_t *device; + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + int timer_prescaler; /* timer prescaler */ +#if FM_BUSY_FLAG_SUPPORT + TIME_TYPE busy_expiry_time; /* expiry time of the busy status */ +#endif + uint8_t address; /* address register */ + uint8_t irq; /* interrupt level */ + uint8_t irqmask; /* irq mask */ + uint8_t status; /* status flag */ + uint32_t mode; /* mode CSM / 3SLOT */ + uint8_t prescaler_sel; /* prescaler selector */ + uint8_t fn_h; /* freq latch */ + int32_t TA; /* timer a */ + int32_t TAC; /* timer a counter */ + uint8_t TB; /* timer b */ + int32_t TBC; /* timer b counter */ + /* local time tables */ + int32_t dt_tab[8][32]; /* DeTune table */ + /* Extention Timer and IRQ handler */ + FM_TIMERHANDLER timer_handler; + FM_IRQHANDLER IRQ_Handler; + const ssg_callbacks *SSG; +}; + + + +/***********************************************************/ +/* OPN unit */ +/***********************************************************/ + +/* OPN 3slot struct */ +struct FM_3SLOT +{ + uint32_t fc[3]; /* fnum3,blk3: calculated */ + uint8_t fn_h; /* freq3 latch */ + uint8_t kcode[3]; /* key code */ + uint32_t block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ +}; + +/* OPN/A/B common state */ +struct FM_OPN +{ + uint8_t type; /* chip type */ + FM_ST ST; /* general state */ + FM_3SLOT SL3; /* 3 slot mode state */ + FM_CH *P_CH; /* pointer of CH */ + unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */ + + uint32_t eg_cnt; /* global envelope generator counter */ + uint32_t eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 */ + uint32_t eg_timer_add; /* step of eg_timer */ + uint32_t eg_timer_overflow;/* envelope generator timer overflows every 3 samples (on real chip) */ + + + /* there are 2048 FNUMs that can be generated using FNUM/BLK registers + but LFO works with one more bit of a precision so we really need 4096 elements */ + + uint32_t fn_table[4096]; /* fnumber->increment counter */ + uint32_t fn_max; /* maximal phase increment (used for phase overflow) */ + + /* LFO */ + uint32_t LFO_AM; /* runtime LFO calculations helper */ + int32_t LFO_PM; /* runtime LFO calculations helper */ + + uint32_t lfo_cnt; + uint32_t lfo_inc; + + uint32_t lfo_freq[8]; /* LFO FREQ table */ + + int32_t m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ + int32_t mem; /* one sample delay memory */ + + int32_t out_fm[8]; /* outputs of working channels */ + +#if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) + int32_t out_adpcm[4]; /* channel output NONE,LEFT,RIGHT or CENTER for YM2608/YM2610 ADPCM */ + int32_t out_delta[4]; /* channel output NONE,LEFT,RIGHT or CENTER for YM2608/YM2610 DELTAT*/ +#endif +}; + + + +/* current chip state */ + +/* log output level */ +#define LOG_ERR 3 /* ERROR */ +#define LOG_WAR 2 /* WARNING */ +#define LOG_INF 1 /* INFORMATION */ +#define LOG_LEVEL LOG_INF + +#ifndef __RAINE__ +#define LOG(d,n,x) do { if( (n)>=LOG_LEVEL ) d->logerror x; } while (0) +#endif + +/* limitter */ +#define Limit(val, max,min) { \ + if ( val > max ) val = max; \ + else if ( val < min ) val = min; \ +} + + +/* status set and IRQ handling */ +static inline void FM_STATUS_SET(FM_ST *ST,int flag) +{ + /* set status flag */ + ST->status |= flag; + if ( !(ST->irq) && (ST->status & ST->irqmask) ) + { + ST->irq = 1; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->device,1); + } +} + +/* status reset and IRQ handling */ +static inline void FM_STATUS_RESET(FM_ST *ST,int flag) +{ + /* reset status flag */ + ST->status &=~flag; + if ( (ST->irq) && !(ST->status & ST->irqmask) ) + { + ST->irq = 0; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->device,0); + } +} + +/* IRQ mask set */ +static inline void FM_IRQMASK_SET(FM_ST *ST,int flag) +{ + ST->irqmask = flag; + /* IRQ handling check */ + FM_STATUS_SET(ST,0); + FM_STATUS_RESET(ST,0); +} + +/* OPN Mode Register Write */ +static inline void set_timers( FM_ST *ST, device_t *n, int v ) +{ + /* b7 = CSM MODE */ + /* b6 = 3 slot mode */ + /* b5 = reset b */ + /* b4 = reset a */ + /* b3 = timer enable b */ + /* b2 = timer enable a */ + /* b1 = load b */ + /* b0 = load a */ + ST->mode = v; + + /* reset Timer b flag */ + if( v & 0x20 ) + FM_STATUS_RESET(ST,0x02); + /* reset Timer a flag */ + if( v & 0x10 ) + FM_STATUS_RESET(ST,0x01); + /* load b */ + if( v & 0x02 ) + { + if( ST->TBC == 0 ) + { + ST->TBC = ( 256-ST->TB)<<4; + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,ST->clock); + } + } + else + { /* stop timer b */ + if( ST->TBC != 0 ) + { + ST->TBC = 0; + if (ST->timer_handler) (ST->timer_handler)(n,1,0,ST->clock); + } + } + /* load a */ + if( v & 0x01 ) + { + if( ST->TAC == 0 ) + { + ST->TAC = (1024-ST->TA); + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,ST->clock); + } + } + else + { /* stop timer a */ + if( ST->TAC != 0 ) + { + ST->TAC = 0; + if (ST->timer_handler) (ST->timer_handler)(n,0,0,ST->clock); + } + } +} + + +/* Timer A Overflow */ +static inline void TimerAOver(FM_ST *ST) +{ + /* set status (if enabled) */ + if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01); + /* clear or reload the counter */ + ST->TAC = (1024-ST->TA); + if (ST->timer_handler) (ST->timer_handler)(ST->device,0,ST->TAC * ST->timer_prescaler,ST->clock); +} +/* Timer B Overflow */ +static inline void TimerBOver(FM_ST *ST) +{ + /* set status (if enabled) */ + if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02); + /* clear or reload the counter */ + ST->TBC = ( 256-ST->TB)<<4; + if (ST->timer_handler) (ST->timer_handler)(ST->device,1,ST->TBC * ST->timer_prescaler,ST->clock); +} + + +#if FM_INTERNAL_TIMER +/* ----- internal timer mode , update timer */ + +/* ---------- calculate timer A ---------- */ + #define INTERNAL_TIMER_A(ST,CSM_CH) \ + { \ + if( ST->TAC && (ST->timer_handler==0) ) \ + if( (ST->TAC -= (int)(ST->freqbase*4096)) <= 0 ) \ + { \ + TimerAOver( ST ); \ + /* CSM mode total level latch and auto key on */ \ + if( ST->mode & 0x80 ) \ + CSMKeyControll( CSM_CH ); \ + } \ + } +/* ---------- calculate timer B ---------- */ + #define INTERNAL_TIMER_B(ST,step) \ + { \ + if( ST->TBC && (ST->timer_handler==0) ) \ + if( (ST->TBC -= (int)(ST->freqbase*4096*step)) <= 0 ) \ + TimerBOver( ST ); \ + } +#else /* FM_INTERNAL_TIMER */ +/* external timer mode */ +#define INTERNAL_TIMER_A(ST,CSM_CH) +#define INTERNAL_TIMER_B(ST,step) +#endif /* FM_INTERNAL_TIMER */ + + + +#if FM_BUSY_FLAG_SUPPORT +#define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME) +static inline uint8_t FM_STATUS_FLAG(FM_ST *ST) +{ + if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 ) + { + if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(&ST->device->machine())) > 0) + return ST->status | 0x80; /* with busy */ + /* expire */ + FM_BUSY_CLEAR(ST); + } + return ST->status; +} +static inline void FM_BUSY_SET(FM_ST *ST,int busyclock ) +{ + TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(attotime::from_hz(ST->clock), busyclock * ST->timer_prescaler); + ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(&ST->device->machine()), expiry_period); +} +#else +#define FM_STATUS_FLAG(ST) ((ST)->status) +#define FM_BUSY_SET(ST,bclock) {} +#define FM_BUSY_CLEAR(ST) {} +#endif + + + + +static inline void FM_KEYON(uint8_t type, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + (void)type; + if( !SLOT->key ) + { + SLOT->key = 1; + SLOT->phase = 0; /* restart Phase Generator */ + SLOT->ssgn = (SLOT->ssg & 0x04) >> 1; + SLOT->state = EG_ATT; + } +} + +static inline void FM_KEYOFF(FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + if( SLOT->key ) + { + SLOT->key = 0; + if (SLOT->state>EG_REL) + SLOT->state = EG_REL;/* phase -> Release */ + } +} + +/* set algorithm connection */ +static void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch ) +{ + int32_t *carrier = &OPN->out_fm[ch]; + + int32_t **om1 = &CH->connect1; + int32_t **om2 = &CH->connect3; + int32_t **oc1 = &CH->connect2; + + int32_t **memc = &CH->mem_connect; + + switch( CH->ALGO ) + { + case 0: + /* M1---C1---MEM---M2---C2---OUT */ + *om1 = &OPN->c1; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 1: + /* M1------+-MEM---M2---C2---OUT */ + /* C1-+ */ + *om1 = &OPN->mem; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 2: + /* M1-----------------+-C2---OUT */ + /* C1---MEM---M2-+ */ + *om1 = &OPN->c2; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 3: + /* M1---C1---MEM------+-C2---OUT */ + /* M2-+ */ + *om1 = &OPN->c1; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->c2; + break; + case 4: + /* M1---C1-+-OUT */ + /* M2---C2-+ */ + /* MEM: not used */ + *om1 = &OPN->c1; + *oc1 = carrier; + *om2 = &OPN->c2; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + case 5: + /* +----C1----+ */ + /* M1-+-MEM---M2-+-OUT */ + /* +----C2----+ */ + *om1 = NULLPTR; /* special mark */ + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->m2; + break; + case 6: + /* M1---C1-+ */ + /* M2-+-OUT */ + /* C2-+ */ + /* MEM: not used */ + *om1 = &OPN->c1; + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + case 7: + /* M1-+ */ + /* C1-+-OUT */ + /* M2-+ */ + /* C2-+ */ + /* MEM: not used*/ + *om1 = carrier; + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + } + + CH->connect4 = carrier; +} + +/* set detune & multiple */ +static inline void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v) +{ + SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1; + SLOT->DT = ST->dt_tab[(v>>4)&7]; + CH->SLOT[SLOT1].Incr=-1; +} + +/* set total level */ +static inline void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v) +{ + (void)CH; + SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ +} + +/* set attack rate & key scale */ +static inline void set_ar_ksr(uint8_t type, FM_CH *CH,FM_SLOT *SLOT,int v) +{ + uint8_t old_KSR = SLOT->KSR; + (void)type; + + SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->KSR = 3-(v>>6); + if (SLOT->KSR != old_KSR) + { + CH->SLOT[SLOT1].Incr=-1; + } + + /* refresh Attack rate */ + if ((SLOT->ar + SLOT->ksr) < 32+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 17*RATE_STEPS; + } +} + +/* set decay rate */ +static inline void set_dr(uint8_t type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr]; +} + +/* set sustain rate */ +static inline void set_sr(uint8_t type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr]; +} + +/* set release rate */ +static inline void set_sl_rr(uint8_t type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->sl = sl_table[ v>>4 ]; + + SLOT->rr = 34 + ((v&0x0f)<<2); + + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr]; +} + + + +static inline signed int op_calc(uint32_t phase, unsigned int env, signed int pm) +{ + uint32_t p; + + p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +static inline signed int op_calc1(uint32_t phase, unsigned int env, signed int pm) +{ + uint32_t p; + + p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +/* advance LFO to next sample */ +static inline void advance_lfo(FM_OPN *OPN) +{ + uint8_t pos; + + if (OPN->lfo_inc) /* LFO enabled ? */ + { + OPN->lfo_cnt += OPN->lfo_inc; + + pos = (OPN->lfo_cnt >> LFO_SH) & 127; + + + /* update AM when LFO output changes */ + + /* actually I can't optimize is this way without rewriting chan_calc() + to use chip->lfo_am instead of global lfo_am */ + { + /* triangle */ + /* AM: 0 to 126 step +2, 126 to 0 step -2 */ + if (pos<64) + OPN->LFO_AM = (pos&63) * 2; + else + OPN->LFO_AM = 126 - ((pos&63) * 2); + } + + /* PM works with 4 times slower clock */ + pos >>= 2; + /* update PM when LFO output changes */ + /*if (prev_pos != pos)*/ /* can't use global lfo_pm for this optimization, must be chip->lfo_pm instead*/ + { + OPN->LFO_PM = pos; + } + + } + else + { + OPN->LFO_AM = 0; + OPN->LFO_PM = 0; + } +} + +/* changed from static inline to static here to work around gcc 4.2.1 codegen bug */ +static void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT) +{ + unsigned int out; + unsigned int swap_flag; + unsigned int i; + + + i = 4; /* four operators per channel */ + do + { + /* reset SSG-EG swap flag */ + swap_flag = 0; + + switch(SLOT->state) + { + case EG_ATT: /* attack phase */ + if ( !(OPN->eg_cnt & ((1<eg_sh_ar)-1) ) ) + { + SLOT->volume += (~SLOT->volume * + (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)]) + ) >>4; + + if (SLOT->volume <= MIN_ATT_INDEX) + { + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = EG_DEC; + } + } + break; + + case EG_DEC: /* decay phase */ + { + if (SLOT->ssg&0x08) /* SSG EG type envelope selected */ + { + if ( !(OPN->eg_cnt & ((1<eg_sh_d1r)-1) ) ) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + if ( SLOT->volume >= (int32_t)(SLOT->sl) ) + SLOT->state = EG_SUS; + } + } + else + { + if ( !(OPN->eg_cnt & ((1<eg_sh_d1r)-1) ) ) + { + SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + if ( SLOT->volume >= (int32_t)(SLOT->sl) ) + SLOT->state = EG_SUS; + } + } + } + break; + + case EG_SUS: /* sustain phase */ + if (SLOT->ssg&0x08) /* SSG EG type envelope selected */ + { + if ( !(OPN->eg_cnt & ((1<eg_sh_d2r)-1) ) ) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + if ( SLOT->volume >= ENV_QUIET ) + { + SLOT->volume = MAX_ATT_INDEX; + + if (SLOT->ssg&0x01) /* bit 0 = hold */ + { + if (SLOT->ssgn&1) /* have we swapped once ??? */ + { + /* yes, so do nothing, just hold current level */ + } + else + swap_flag = (SLOT->ssg&0x02) | 1 ; /* bit 1 = alternate */ + + } + else + { + /* same as KEY-ON operation */ + + /* restart of the Phase Generator should be here */ + SLOT->phase = 0; + + { + /* phase -> Attack */ + SLOT->volume = 511; + SLOT->state = EG_ATT; + } + + swap_flag = (SLOT->ssg&0x02); /* bit 1 = alternate */ + } + } + } + } + else + { + if ( !(OPN->eg_cnt & ((1<eg_sh_d2r)-1) ) ) + { + SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + if ( SLOT->volume >= MAX_ATT_INDEX ) + { + SLOT->volume = MAX_ATT_INDEX; + /* do not change SLOT->state (verified on real chip) */ + } + } + + } + break; + + case EG_REL: /* release phase */ + if ( !(OPN->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + /* SSG-EG affects Release phase also (Nemesis) */ + SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; + + if ( SLOT->volume >= MAX_ATT_INDEX ) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + break; + + } + + + out = ((uint32_t)SLOT->volume); + + /* negate output (changes come from alternate bit, init comes from attack bit) */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL)) + out ^= MAX_ATT_INDEX; + + /* we need to store the result here because we are going to change ssgn + in next instruction */ + SLOT->vol_out = out + SLOT->tl; + + /* reverse SLOT inversion flag */ + SLOT->ssgn ^= swap_flag; + + SLOT++; + i--; + }while (i); + +} + + + +#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) + +static inline void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, int32_t pms, uint32_t block_fnum) +{ + uint32_t fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; + int32_t lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + uint8_t blk; + uint32_t fn; + int kc, fc; + + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; + + blk = (block_fnum&0x7000) >> 12; + fn = block_fnum & 0xfff; + + /* keyscale code */ + kc = (blk<<2) | opn_fktable[fn >> 8]; + + /* phase increment counter */ + fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc]; + + /* detects frequency overflow (credits to Nemesis) */ + if (fc < 0) fc += OPN->fn_max; + + /* update phase */ + SLOT->phase += (fc * SLOT->mul) >> 1; + } + else /* LFO phase modulation = zero */ + { + SLOT->phase += SLOT->Incr; + } +} + +static inline void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH) +{ + uint32_t block_fnum = CH->block_fnum; + + uint32_t fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; + int32_t lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + uint8_t blk; + uint32_t fn; + int kc, fc, finc; + + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; + + blk = (block_fnum&0x7000) >> 12; + fn = block_fnum & 0xfff; + + /* keyscale code */ + kc = (blk<<2) | opn_fktable[fn >> 8]; + + /* phase increment counter */ + fc = (OPN->fn_table[fn]>>(7-blk)); + + /* detects frequency overflow (credits to Nemesis) */ + finc = fc + CH->SLOT[SLOT1].DT[kc]; + + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1; + + finc = fc + CH->SLOT[SLOT2].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1; + + finc = fc + CH->SLOT[SLOT3].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1; + + finc = fc + CH->SLOT[SLOT4].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1; + } + else /* LFO phase modulation = zero */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +static inline void chan_calc(FM_OPN *OPN, FM_CH *CH, int chnum) +{ + unsigned int eg_out; + + uint32_t AM = OPN->LFO_AM >> CH->ams; + + + OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0; + + *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ + + eg_out = volume_calc(&CH->SLOT[SLOT1]); + { + int32_t out = CH->op1_out[0] + CH->op1_out[1]; + CH->op1_out[0] = CH->op1_out[1]; + + if( !CH->connect1 ) + { + /* algorithm 5 */ + OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0]; + } + else + { + /* other algorithms */ + *CH->connect1 += CH->op1_out[0]; + } + + CH->op1_out[1] = 0; + if( eg_out < ENV_QUIET ) /* SLOT 1 */ + { + if (!CH->FB) + out=0; + + CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<FB) ); + } + } + + eg_out = volume_calc(&CH->SLOT[SLOT3]); + if( eg_out < ENV_QUIET ) /* SLOT 3 */ + *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2); + + eg_out = volume_calc(&CH->SLOT[SLOT2]); + if( eg_out < ENV_QUIET ) /* SLOT 2 */ + *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1); + + eg_out = volume_calc(&CH->SLOT[SLOT4]); + if( eg_out < ENV_QUIET ) /* SLOT 4 */ + *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2); + + + /* store current MEM */ + CH->mem_value = OPN->mem; + + /* update phase counters AFTER output calculations */ + if(CH->pms) + { + /* add support for 3 slot mode */ + if ((OPN->ST.mode & 0xC0) && (chnum == 2)) + { + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum); + } + else update_phase_lfo_channel(OPN, CH); + } + else /* no LFO phase modulation */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +/* update phase increment and envelope generator */ +static inline void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc ) +{ + int ksr = kc >> SLOT->KSR; + + fc += SLOT->DT[kc]; + + /* detects frequency overflow (credits to Nemesis) */ + if (fc < 0) fc += OPN->fn_max; + + /* (frequency) phase increment counter */ + SLOT->Incr = (fc * SLOT->mul) >> 1; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 32+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 17*RATE_STEPS; + } + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + + SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr]; + } +} + +/* update phase increment counters */ +/* Changed from static inline to static to work around gcc 4.2.1 codegen bug */ +static void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH ) +{ + if( CH->SLOT[SLOT1].Incr==-1) + { + int fc = CH->fc; + int kc = CH->kcode; + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc ); + } +} + +/* initialize time tables */ +static void init_timetables( FM_ST *ST , const uint8_t *dttable ) +{ + int i,d; + double rate; + +#if 0 + logerror("FM.C: samplerate=%8i chip clock=%8i freqbase=%f \n", + ST->rate, ST->clock, ST->freqbase ); +#endif + + /* DeTune table */ + for (d = 0;d <= 3;d++) + { + for (i = 0;i <= 31;i++) + { + rate = ((double)dttable[d*32 + i]) * SIN_LEN * ST->freqbase * (1<dt_tab[d][i] = (int32_t) rate; + ST->dt_tab[d+4][i] = -ST->dt_tab[d][i]; +#if 0 + logerror("FM.C: DT [%2i %2i] = %8x \n", d, i, ST->dt_tab[d][i] ); +#endif + } + } + +} + + +static void reset_channels( FM_ST *ST , FM_CH *CH , int num ) +{ + int c,s; + + ST->mode = 0; /* normal mode */ + ST->TA = 0; + ST->TAC = 0; + ST->TB = 0; + ST->TBC = 0; + + for( c = 0 ; c < num ; c++ ) + { + CH[c].fc = 0; + for(s = 0 ; s < 4 ; s++ ) + { + CH[c].SLOT[s].ssg = 0; + CH[c].SLOT[s].ssgn = 0; + CH[c].SLOT[s].state= EG_OFF; + CH[c].SLOT[s].volume = MAX_ATT_INDEX; + CH[c].SLOT[s].vol_out= MAX_ATT_INDEX; + } + } +} + +/* initialize generic tables */ +static int init_tables(void) +{ + signed int i,x; + signed int n; + double o,m; + + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 2; /* 13 bits here (as in real chip) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + for (i=1; i<13; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + #if 0 + logerror("tl %04i", x); + for (i=0; i<13; i++) + logerror(", [%02i] %4x", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ]); + logerror("\n"); + #endif + } + /*logerror("FM.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ + + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + /*logerror("FM.C: sin [%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[i],tl_tab[sin_tab[i]]);*/ + } + + /*logerror("FM.C: ENV_QUIET= %08x\n",ENV_QUIET );*/ + + + /* build LFO PM modulation table */ + for(i = 0; i < 8; i++) /* 8 PM depths */ + { + uint8_t fnum; + for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */ + { + uint8_t value; + uint8_t step; + uint32_t offset_depth = i; + uint32_t offset_fnum_bit; + uint32_t bit_tmp; + + for (step=0; step<8; step++) + { + value = 0; + for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */ + { + if (fnum & (1<SLOT[SLOT1].key) + { + FM_KEYON(type, CH,SLOT1); + FM_KEYOFF(CH, SLOT1); + } + if (!CH->SLOT[SLOT2].key) + { + FM_KEYON(type, CH,SLOT2); + FM_KEYOFF(CH, SLOT2); + } + if (!CH->SLOT[SLOT3].key) + { + FM_KEYON(type, CH,SLOT3); + FM_KEYOFF(CH, SLOT3); + } + if (!CH->SLOT[SLOT4].key) + { + FM_KEYON(type, CH,SLOT4); + FM_KEYOFF(CH, SLOT4); + } +} + +#ifdef MAME_EMU_SAVE_H +/* FM channel save , internal state only */ +static void FMsave_state_channel(device_t *device,FM_CH *CH,int num_ch) +{ + int slot , ch; + + for(ch=0;chsave_item(NAME(CH->op1_out), ch); + device->save_item(NAME(CH->fc), ch); + /* slots */ + for(slot=0;slot<4;slot++) + { + FM_SLOT *SLOT = &CH->SLOT[slot]; + device->save_item(NAME(SLOT->phase), ch * 4 + slot); + device->save_item(NAME(SLOT->state), ch * 4 + slot); + device->save_item(NAME(SLOT->volume), ch * 4 + slot); + } + } +} + +static void FMsave_state_st(device_t *device,FM_ST *ST) +{ +#if FM_BUSY_FLAG_SUPPORT + device->save_item(NAME(ST->busy_expiry_time) ); +#endif + device->save_item(NAME(ST->address) ); + device->save_item(NAME(ST->irq) ); + device->save_item(NAME(ST->irqmask) ); + device->save_item(NAME(ST->status) ); + device->save_item(NAME(ST->mode) ); + device->save_item(NAME(ST->prescaler_sel) ); + device->save_item(NAME(ST->fn_h) ); + device->save_item(NAME(ST->TA) ); + device->save_item(NAME(ST->TAC) ); + device->save_item(NAME(ST->TB) ); + device->save_item(NAME(ST->TBC) ); +} +#endif /* MAME_EMU_SAVE_H */ + +#if BUILD_OPN + + + +/* prescaler set (and make time tables) */ +static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres) +{ + int i; + + /* frequency base */ + OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0; + +#if 0 + OPN->ST.rate = (double)OPN->ST.clock / pres; + OPN->ST.freqbase = 1.0; +#endif + + OPN->eg_timer_add = (uint32_t)((1<ST.freqbase); + OPN->eg_timer_overflow = ( 3 ) * (1<ST.timer_prescaler = timer_prescaler; + + /* SSG part prescaler set */ + if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.device, OPN->ST.clock * 2 / SSGpres ); + + /* make time tables */ + init_timetables( &OPN->ST, dt_tab ); + + /* there are 2048 FNUMs that can be generated using FNUM/BLK registers + but LFO works with one more bit of a precision so we really need 4096 elements */ + /* calculate fnumber -> increment counter table */ + for(i = 0; i < 4096; i++) + { + /* freq table for octave 7 */ + /* OPN phase increment counter = 20bit */ + OPN->fn_table[i] = (uint32_t)( (double)i * 32 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ +#if 0 + logerror("FM.C: fn_table[%4i] = %08x (dec=%8i)\n", + i, OPN->fn_table[i]>>6,OPN->fn_table[i]>>6 ); +#endif + } + + /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */ + OPN->fn_max = (uint32_t)( (double)0x20000 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) ); + + /* LFO freq. table */ + for(i = 0; i < 8; i++) + { + /* Amplitude modulation: 64 output levels (triangle waveform); 1 level lasts for one of "lfo_samples_per_step" samples */ + /* Phase modulation: one entry from lfo_pm_output lasts for one of 4 * "lfo_samples_per_step" samples */ + OPN->lfo_freq[i] = (uint32_t)((1.0 / lfo_samples_per_step[i]) * (1<ST.freqbase); +#if 0 + logerror("FM.C: lfo_freq[%i] = %08x (dec=%8i)\n", + i, OPN->lfo_freq[i],OPN->lfo_freq[i] ); +#endif + } +} + + + +/* write a OPN mode register 0x20-0x2f */ +static void OPNWriteMode(FM_OPN *OPN, int r, int v) +{ + uint8_t c; + FM_CH *CH; + + switch(r) + { + case 0x21: /* Test */ + break; + case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */ + if( OPN->type & TYPE_LFOPAN ) + { + if (v&0x08) /* LFO enabled ? */ + { + OPN->lfo_inc = OPN->lfo_freq[v&7]; + } + else + { + OPN->lfo_inc = 0; + } + } + break; + case 0x24: /* timer A High 8*/ + OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2); + break; + case 0x25: /* timer A Low 2*/ + OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3); + break; + case 0x26: /* timer B */ + OPN->ST.TB = v; + break; + case 0x27: /* mode, timer control */ + set_timers( &(OPN->ST),OPN->ST.device,v ); + break; + case 0x28: /* key on / off */ + c = v & 0x03; + if( c == 3 ) break; + if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3; + CH = OPN->P_CH; + CH = &CH[c]; + if(v&0x10) FM_KEYON(OPN->type,CH,SLOT1); else FM_KEYOFF(CH,SLOT1); + if(v&0x20) FM_KEYON(OPN->type,CH,SLOT2); else FM_KEYOFF(CH,SLOT2); + if(v&0x40) FM_KEYON(OPN->type,CH,SLOT3); else FM_KEYOFF(CH,SLOT3); + if(v&0x80) FM_KEYON(OPN->type,CH,SLOT4); else FM_KEYOFF(CH,SLOT4); + break; + } +} + +/* write a OPN register (0x30-0xff) */ +static void OPNWriteReg(FM_OPN *OPN, int r, int v) +{ + FM_CH *CH; + FM_SLOT *SLOT; + + uint8_t c = OPN_CHAN(r); + + if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */ + + if (r >= 0x100) c+=3; + + CH = OPN->P_CH; + CH = &CH[c]; + + SLOT = &(CH->SLOT[OPN_SLOT(r)]); + + switch( r & 0xf0 ) + { + case 0x30: /* DET , MUL */ + set_det_mul(&OPN->ST,CH,SLOT,v); + break; + + case 0x40: /* TL */ + set_tl(CH,SLOT,v); + break; + + case 0x50: /* KS, AR */ + set_ar_ksr(OPN->type,CH,SLOT,v); + break; + + case 0x60: /* bit7 = AM ENABLE, DR */ + set_dr(OPN->type, SLOT,v); + + if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */ + { + SLOT->AMmask = (v&0x80) ? ~0 : 0; + } + break; + + case 0x70: /* SR */ + set_sr(OPN->type,SLOT,v); + break; + + case 0x80: /* SL, RR */ + set_sl_rr(OPN->type,SLOT,v); + break; + + case 0x90: /* SSG-EG */ + SLOT->ssg = v&0x0f; + SLOT->ssgn = (v&0x04)>>1; /* bit 1 in ssgn = attack */ + + /* SSG-EG envelope shapes : + + E AtAlH + 1 0 0 0 \\\\ + + 1 0 0 1 \___ + + 1 0 1 0 \/\/ + ___ + 1 0 1 1 \ + + 1 1 0 0 //// + ___ + 1 1 0 1 / + + 1 1 1 0 /\/\ + + 1 1 1 1 /___ + + + E = SSG-EG enable + + + The shapes are generated using Attack, Decay and Sustain phases. + + Each single character in the diagrams above represents this whole + sequence: + + - when KEY-ON = 1, normal Attack phase is generated (*without* any + difference when compared to normal mode), + + - later, when envelope level reaches minimum level (max volume), + the EG switches to Decay phase (which works with bigger steps + when compared to normal mode - see below), + + - later when envelope level passes the SL level, + the EG swithes to Sustain phase (which works with bigger steps + when compared to normal mode - see below), + + - finally when envelope level reaches maximum level (min volume), + the EG switches to Attack phase again (depends on actual waveform). + + Important is that when switch to Attack phase occurs, the phase counter + of that operator will be zeroed-out (as in normal KEY-ON) but not always. + (I havent found the rule for that - perhaps only when the output level is low) + + The difference (when compared to normal Envelope Generator mode) is + that the resolution in Decay and Sustain phases is 4 times lower; + this results in only 256 steps instead of normal 1024. + In other words: + when SSG-EG is disabled, the step inside of the EG is one, + when SSG-EG is enabled, the step is four (in Decay and Sustain phases). + + Times between the level changes are the same in both modes. + + + Important: + Decay 1 Level (so called SL) is compared to actual SSG-EG output, so + it is the same in both SSG and no-SSG modes, with this exception: + + when the SSG-EG is enabled and is generating raising levels + (when the EG output is inverted) the SL will be found at wrong level !!! + For example, when SL=02: + 0 -6 = -6dB in non-inverted EG output + 96-6 = -90dB in inverted EG output + Which means that EG compares its level to SL as usual, and that the + output is simply inverted afterall. + + + The Yamaha's manuals say that AR should be set to 0x1f (max speed). + That is not necessary, but then EG will be generating Attack phase. + + */ + + + break; + + case 0xa0: + switch( OPN_SLOT(r) ) + { + case 0: /* 0xa0-0xa2 : FNUM1 */ + { + uint32_t fn = (((uint32_t)( (OPN->ST.fn_h)&7))<<8) + v; + uint8_t blk = OPN->ST.fn_h>>3; + /* keyscale code */ + CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + CH->fc = OPN->fn_table[fn*2]>>(7-blk); + + /* store fnum in clear form for LFO PM calculations */ + CH->block_fnum = (blk<<11) | fn; + + CH->SLOT[SLOT1].Incr=-1; + } + break; + case 1: /* 0xa4-0xa6 : FNUM2,BLK */ + OPN->ST.fn_h = v&0x3f; + break; + case 2: /* 0xa8-0xaa : 3CH FNUM1 */ + if(r < 0x100) + { + uint32_t fn = (((uint32_t)(OPN->SL3.fn_h&7))<<8) + v; + uint8_t blk = OPN->SL3.fn_h>>3; + /* keyscale code */ + OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk); + OPN->SL3.block_fnum[c] = (blk<<11) | fn; + (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1; + } + break; + case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ + if(r < 0x100) + OPN->SL3.fn_h = v&0x3f; + break; + } + break; + + case 0xb0: + switch( OPN_SLOT(r) ) + { + case 0: /* 0xb0-0xb2 : FB,ALGO */ + { + int feedback = (v>>3)&7; + CH->ALGO = v&7; + CH->FB = feedback ? feedback+6 : 0; + setup_connection( OPN, CH, c ); + } + break; + case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */ + if( OPN->type & TYPE_LFOPAN) + { + /* b0-2 PMS */ + CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ + + /* b4-5 AMS */ + CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03]; + + /* PAN : b7 = L, b6 = R */ + OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0; + OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0; + + } + break; + } + break; + } +} + +#endif /* BUILD_OPN */ + +#if BUILD_OPN_PRESCALER +/* + prescaler circuit (best guess to verified chip behaviour) + + +--------------+ +-sel2-+ + | +--|in20 | + +---+ | +-sel1-+ | | +M-CLK -+-|1/2|-+--|in10 | +---+ | out|--INT_CLOCK + | +---+ | out|-|1/3|-|in21 | + +----------|in11 | +---+ +------+ + +------+ + +reg.2d : sel2 = in21 (select sel2) +reg.2e : sel1 = in11 (select sel1) +reg.2f : sel1 = in10 , sel2 = in20 (clear selector) +reset : sel1 = in11 , sel2 = in21 (clear both) + +*/ +static void OPNPrescaler_w(FM_OPN *OPN , int addr, int pre_divider) +{ + static const int opn_pres[4] = { 2*12 , 2*12 , 6*12 , 3*12 }; + static const int ssg_pres[4] = { 1 , 1 , 4 , 2 }; + int sel; + + switch(addr) + { + case 0: /* when reset */ + OPN->ST.prescaler_sel = 2; + break; + case 1: /* when postload */ + break; + case 0x2d: /* divider sel : select 1/1 for 1/3line */ + OPN->ST.prescaler_sel |= 0x02; + break; + case 0x2e: /* divider sel , select 1/3line for output */ + OPN->ST.prescaler_sel |= 0x01; + break; + case 0x2f: /* divider sel , clear both selector to 1/2,1/2 */ + OPN->ST.prescaler_sel = 0; + break; + } + sel = OPN->ST.prescaler_sel & 3; + /* update prescaler */ + OPNSetPres( OPN, opn_pres[sel]*pre_divider, + opn_pres[sel]*pre_divider, + ssg_pres[sel]*pre_divider ); +} +#endif /* BUILD_OPN_PRESCALER */ + +#if BUILD_YM2203 +/*****************************************************************************/ +/* YM2203 local section */ +/*****************************************************************************/ + +/* here's the virtual YM2203(OPN) */ +namespace { +struct ym2203_state +{ + uint8_t REGS[256]; /* registers */ + FM_OPN OPN; /* OPN state */ + FM_CH CH[3]; /* channel state */ +}; +} // anonymous namespace + +/* Generate samples for one of the YM2203s */ +void ym2203_update_one(void *chip, FMSAMPLE *buffer, int length) +{ + ym2203_state *F2203 = (ym2203_state *)chip; + FM_OPN *OPN = &F2203->OPN; + int i; + FMSAMPLE *buf = buffer; + FM_CH *cch[3]; + + cch[0] = &F2203->CH[0]; + cch[1] = &F2203->CH[1]; + cch[2] = &F2203->CH[2]; + + + /* refresh PG and EG */ + refresh_fc_eg_chan( OPN, cch[0] ); + refresh_fc_eg_chan( OPN, cch[1] ); + if( (F2203->OPN.ST.mode & 0xc0) ) + { + /* 3SLOT MODE */ + if( cch[2]->SLOT[SLOT1].Incr==-1) + { + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode ); + } + } + else + refresh_fc_eg_chan( OPN, cch[2] ); + + + /* YM2203 doesn't have LFO so we must keep these globals at 0 level */ + OPN->LFO_AM = 0; + OPN->LFO_PM = 0; + + /* buffering */ + for (i=0; i < length ; i++) + { + /* clear outputs */ + OPN->out_fm[0] = 0; + OPN->out_fm[1] = 0; + OPN->out_fm[2] = 0; + + /* advance envelope generator */ + OPN->eg_timer += OPN->eg_timer_add; + while (OPN->eg_timer >= OPN->eg_timer_overflow) + { + OPN->eg_timer -= OPN->eg_timer_overflow; + OPN->eg_cnt++; + + advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); + } + + /* calculate FM */ + chan_calc(OPN, cch[0], 0 ); + chan_calc(OPN, cch[1], 1 ); + chan_calc(OPN, cch[2], 2 ); + + /* buffering */ + { + int lt; + + lt = OPN->out_fm[0] + OPN->out_fm[1] + OPN->out_fm[2]; + + lt >>= FINAL_SH; + + Limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + SAVE_ALL_CHANNELS + #endif + + /* buffering */ + buf[i] = lt; + } + + /* timer A control */ + INTERNAL_TIMER_A( &F2203->OPN.ST , cch[2] ) + } + INTERNAL_TIMER_B(&F2203->OPN.ST,length) +} + +/* ---------- reset one of chip ---------- */ +void ym2203_reset_chip(void *chip) +{ + int i; + ym2203_state *F2203 = (ym2203_state *)chip; + FM_OPN *OPN = &F2203->OPN; + + /* Reset Prescaler */ + OPNPrescaler_w(OPN, 0 , 1 ); + /* reset SSG section */ + (*OPN->ST.SSG->reset)(OPN->ST.device); + /* status clear */ + FM_IRQMASK_SET(&OPN->ST,0x03); + FM_BUSY_CLEAR(&OPN->ST); + OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */ + + OPN->eg_timer = 0; + OPN->eg_cnt = 0; + + FM_STATUS_RESET(&OPN->ST, 0xff); + + reset_channels( &OPN->ST , F2203->CH , 3 ); + /* reset OPerator paramater */ + for(i = 0xb2 ; i >= 0x30 ; i-- ) OPNWriteReg(OPN,i,0); + for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0); +} + +#ifdef MAME_EMU_SAVE_H +void ym2203_postload(void *chip) +{ + if (chip) + { + ym2203_state *F2203 = (ym2203_state *)chip; + int r; + + /* prescaler */ + OPNPrescaler_w(&F2203->OPN,1,1); + + /* SSG registers */ + for(r=0;r<16;r++) + { + (*F2203->OPN.ST.SSG->write)(F2203->OPN.ST.device,0,r); + (*F2203->OPN.ST.SSG->write)(F2203->OPN.ST.device,1,F2203->REGS[r]); + } + + /* OPN registers */ + /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ + for(r=0x30;r<0x9e;r++) + if((r&3) != 3) + OPNWriteReg(&F2203->OPN,r,F2203->REGS[r]); + /* FB / CONNECT , L / R / AMS / PMS */ + for(r=0xb0;r<0xb6;r++) + if((r&3) != 3) + OPNWriteReg(&F2203->OPN,r,F2203->REGS[r]); + + /* channels */ + /*FM_channel_postload(F2203->CH,3);*/ + } +} + +static void YM2203_save_state(ym2203_state *F2203, device_t *device) +{ + device->save_item(NAME(F2203->REGS)); + FMsave_state_st(device,&F2203->OPN.ST); + FMsave_state_channel(device,F2203->CH,3); + /* 3slots */ + device->save_item (NAME(F2203->OPN.SL3.fc)); + device->save_item (NAME(F2203->OPN.SL3.fn_h)); + device->save_item (NAME(F2203->OPN.SL3.kcode)); +} +#endif /* MAME_EMU_SAVE_H */ + +/* ---------- Initialize YM2203 emulator(s) ---------- + 'num' is the number of virtual YM2203s to allocate + 'clock' is the chip clock in Hz + 'rate' is sampling rate +*/ +void * ym2203_init(device_t *device, int clock, int rate, FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) +{ + ym2203_state *F2203; + + /* allocate ym2203 state space */ + F2203 = new ym2203_state; + memset(F2203, 0, sizeof(*F2203)); + + if( !init_tables() ) + { + delete F2203; + return nullptr; + } + + F2203->OPN.type = TYPE_YM2203; + F2203->OPN.P_CH = F2203->CH; + F2203->OPN.ST.device = device; + F2203->OPN.ST.clock = clock; + F2203->OPN.ST.rate = rate; + + F2203->OPN.ST.timer_handler = timer_handler; + F2203->OPN.ST.IRQ_Handler = IRQHandler; + F2203->OPN.ST.SSG = ssg; + + for (unsigned i = 0; i < 3; i++) + { + F2203->CH[i].pan_volume_l = 46340; + F2203->CH[i].pan_volume_r = 46340; + } + +#ifdef MAME_EMU_SAVE_H + YM2203_save_state(F2203, device); +#endif + return F2203; +} + +void ym2203_clock_changed(void *chip, int clock, int rate) +{ + ym2203_state *FM2203 = (ym2203_state *)chip; + + FM2203->OPN.ST.clock = clock; + FM2203->OPN.ST.rate = rate; +} + +/* shut down emulator */ +void ym2203_shutdown(void *chip) +{ + ym2203_state *FM2203 = (ym2203_state *)chip; + + FMCloseTable(); + delete FM2203; +} + +/* YM2203 I/O interface */ +int ym2203_write(void *chip,int a,uint8_t v) +{ + ym2203_state *F2203 = (ym2203_state *)chip; + FM_OPN *OPN = &F2203->OPN; + + if( !(a&1) ) + { /* address port */ + OPN->ST.address = (v &= 0xff); + + /* Write register to SSG emulator */ + if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.device,0,v); + + /* prescaler select : 2d,2e,2f */ + if( v >= 0x2d && v <= 0x2f ) + OPNPrescaler_w(OPN , v , 1); + } + else + { /* data port */ + int addr = OPN->ST.address; + F2203->REGS[addr] = v; + switch( addr & 0xf0 ) + { + case 0x00: /* 0x00-0x0f : SSG section */ + /* Write data to SSG emulator */ + (*OPN->ST.SSG->write)(OPN->ST.device,a,v); + break; + case 0x20: /* 0x20-0x2f : Mode section */ + ym2203_device::update_request(OPN->ST.device); + /* write register */ + OPNWriteMode(OPN,addr,v); + break; + default: /* 0x30-0xff : OPN section */ + ym2203_device::update_request(OPN->ST.device); + /* write register */ + OPNWriteReg(OPN,addr,v); + } + FM_BUSY_SET(&OPN->ST,1); + } + return OPN->ST.irq; +} + +uint8_t ym2203_read(void *chip,int a) +{ + ym2203_state *F2203 = (ym2203_state *)chip; + int addr = F2203->OPN.ST.address; + uint8_t ret = 0; + + if( !(a&1) ) + { /* status port */ + ret = FM_STATUS_FLAG(&F2203->OPN.ST); + } + else + { /* data port (only SSG) */ + if( addr < 16 ) ret = (*F2203->OPN.ST.SSG->read)(F2203->OPN.ST.device); + } + return ret; +} + +int ym2203_timer_over(void *chip,int c) +{ + ym2203_state *F2203 = (ym2203_state *)chip; + + if( c ) + { /* Timer B */ + TimerBOver( &(F2203->OPN.ST) ); + } + else + { /* Timer A */ + ym2203_device::update_request(F2203->OPN.ST.device); + /* timer update */ + TimerAOver( &(F2203->OPN.ST) ); + /* CSM mode key,TL control */ + if( F2203->OPN.ST.mode & 0x80 ) + { /* CSM mode auto key on */ + CSMKeyControll( F2203->OPN.type, &(F2203->CH[2]) ); + } + } + return F2203->OPN.ST.irq; +} +#endif /* BUILD_YM2203 */ + + + +#if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) + +namespace { +/**** YM2610 ADPCM defines ****/ +CONSTEXPR unsigned ADPCM_SHIFT = 16; /* frequency step rate */ +CONSTEXPR unsigned ADPCMA_ADDRESS_SHIFT = 8; /* adpcm A address shift */ + +/* speedup purposes only */ +static int jedi_table[ 49*16 ]; + +/* ADPCM type A channel struct */ +struct ADPCM_CH +{ + uint8_t flag; /* port state */ + uint8_t flagMask; /* arrived flag mask */ + uint8_t now_data; /* current ROM data */ + uint32_t now_addr; /* current ROM address */ + uint32_t now_step; + uint32_t step; + uint32_t start; /* sample data start address*/ + uint32_t end; /* sample data end address */ + uint8_t IL; /* Instrument Level */ + int32_t adpcm_acc; /* accumulator */ + int32_t adpcm_step; /* step */ + int32_t adpcm_out; /* (speedup) hiro-shi!! */ + int8_t vol_mul; /* volume in "0.75dB" steps */ + uint8_t vol_shift; /* volume in "-6dB" steps */ + int32_t *pan; /* &out_adpcm[OPN_xxxx] */ +}; + +/* here's the virtual YM2610 */ +struct ym2610_state +{ + uint8_t REGS[512]; /* registers */ + FM_OPN OPN; /* OPN state */ + FM_CH CH[6]; /* channel state */ + uint8_t addr_A1; /* address line A1 */ + + /* ADPCM-A unit */ + FM_READBYTE read_byte; + uint8_t adpcmTL; /* adpcmA total level */ + ADPCM_CH adpcm[6]; /* adpcm channels */ + uint32_t adpcmreg[0x30]; /* registers */ + uint8_t adpcm_arrivedEndAddress; + YM_DELTAT deltaT; /* Delta-T ADPCM unit */ + + uint8_t flagmask; /* YM2608 only */ + uint8_t irqmask; /* YM2608 only */ + + device_t *device; + + /* different from the usual ADPCM table */ + //static CONSTEXPR int step_inc[8] = { -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16 }; + static int step_inc[8]; + + /* ADPCM A (Non control type) : calculate one channel output */ + inline void ADPCMA_calc_chan( ADPCM_CH *ch ) + { + uint32_t step; + uint8_t data; + + + ch->now_step += ch->step; + if ( ch->now_step >= uint32_t(1<now_step >> ADPCM_SHIFT; + ch->now_step &= (1< instead of == */ + /* YM2610 checks lower 20 bits only, the 4 MSB bits are sample bank */ + /* Here we use 1<<21 to compensate for nibble calculations */ + + if ( (ch->now_addr & ((1<<21)-1)) == ((ch->end<<1) & ((1<<21)-1)) ) + { + ch->flag = 0; + adpcm_arrivedEndAddress |= ch->flagMask; + return; + } +#if 0 + if ( ch->now_addr > (pcmsizeA<<1) ) + { + LOG(LOG_WAR,("YM2610: Attempting to play past adpcm rom size!\n" )); + return; + } +#endif + if ( ch->now_addr&1 ) + data = ch->now_data & 0x0f; + else + { + ch->now_data = read_byte(device, ch->now_addr>>1); + data = (ch->now_data >> 4) & 0x0f; + } + + ch->now_addr++; + + ch->adpcm_acc += jedi_table[ch->adpcm_step + data]; + + /* extend 12-bit signed int */ + if (ch->adpcm_acc & ~0x7ff) + ch->adpcm_acc |= ~0xfff; + else + ch->adpcm_acc &= 0xfff; + + ch->adpcm_step += step_inc[data & 7]; + Limit( ch->adpcm_step, 48*16, 0*16 ); + + }while(--step); + + /* calc pcm * volume data */ + ch->adpcm_out = ((ch->adpcm_acc * ch->vol_mul) >> ch->vol_shift) & ~3; /* multiply, shift and mask out 2 LSB bits */ + } + + /* output for work of output channels (out_adpcm[OPNxxxx])*/ + *(ch->pan) += ch->adpcm_out; + } + + /* ADPCM type A Write */ + void FM_ADPCMAWrite(int r,int v) + { + uint8_t c = r&0x07; + + adpcmreg[r] = v&0xff; /* stock data */ + switch( r ) + { + case 0x00: /* DM,--,C5,C4,C3,C2,C1,C0 */ + if( !(v&0x80) ) + { + /* KEY ON */ + for( c = 0; c < 6; c++ ) + { + if( (v>>c)&1 ) + { + /**** start adpcm ****/ + adpcm[c].step = (uint32_t)((float)(1<>c)&1 ) + adpcm[c].flag = 0; + } + break; + case 0x01: /* B0-5 = TL */ + adpcmTL = (v & 0x3f) ^ 0x3f; + for( c = 0; c < 6; c++ ) + { + int volume = adpcmTL + adpcm[c].IL; + + if ( volume >= 63 ) /* This is correct, 63 = quiet */ + { + adpcm[c].vol_mul = 0; + adpcm[c].vol_shift = 0; + } + else + { + adpcm[c].vol_mul = 15 - (volume & 7); /* so called 0.75 dB */ + adpcm[c].vol_shift = 1 + (volume >> 3); /* Yamaha engineers used the approximation: each -6 dB is close to divide by two (shift right) */ + } + + /* calc pcm * volume data */ + adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc * adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask out low 2 bits */ + } + break; + default: + c = r&0x07; + if( c >= 0x06 ) return; + switch( r&0x38 ) + { + case 0x08: /* B7=L,B6=R, B4-0=IL */ + { + int volume; + + adpcm[c].IL = (v & 0x1f) ^ 0x1f; + + volume = adpcmTL + adpcm[c].IL; + + if ( volume >= 63 ) /* This is correct, 63 = quiet */ + { + adpcm[c].vol_mul = 0; + adpcm[c].vol_shift = 0; + } + else + { + adpcm[c].vol_mul = 15 - (volume & 7); /* so called 0.75 dB */ + adpcm[c].vol_shift = 1 + (volume >> 3); /* Yamaha engineers used the approximation: each -6 dB is close to divide by two (shift right) */ + } + + adpcm[c].pan = &OPN.out_adpcm[(v>>6)&0x03]; + + /* calc pcm * volume data */ + adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc * adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask out low 2 bits */ + } + break; + case 0x10: + case 0x18: + adpcm[c].start = ( (adpcmreg[0x18 + c]*0x0100 | adpcmreg[0x10 + c]) << ADPCMA_ADDRESS_SHIFT); + break; + case 0x20: + case 0x28: + adpcm[c].end = ( (adpcmreg[0x28 + c]*0x0100 | adpcmreg[0x20 + c]) << ADPCMA_ADDRESS_SHIFT); + adpcm[c].end += (1<save_item(NAME(adpcm->flag), ch); + device->save_item(NAME(adpcm->now_data), ch); + device->save_item(NAME(adpcm->now_addr), ch); + device->save_item(NAME(adpcm->now_step), ch); + device->save_item(NAME(adpcm->adpcm_acc), ch); + device->save_item(NAME(adpcm->adpcm_step), ch); + device->save_item(NAME(adpcm->adpcm_out), ch); + } +} +#endif /* MAME_EMU_SAVE_H */ +} // anonymous namespace + +#endif /* (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) */ + + +#if BUILD_YM2608 +/*****************************************************************************/ +/* YM2608 local section */ +/*****************************************************************************/ + + + +static const unsigned int YM2608_ADPCM_ROM_addr[2*6] = { +0x0000, 0x01bf, /* bass drum */ +0x01c0, 0x043f, /* snare drum */ +0x0440, 0x1b7f, /* top cymbal */ +0x1b80, 0x1cff, /* high hat */ +0x1d00, 0x1f7f, /* tom tom */ +0x1f80, 0x1fff /* rim shot */ +}; + + +/* flag enable control 0x110 */ +static inline void YM2608IRQFlagWrite(FM_OPN *OPN, ym2608_state *F2608, int v) +{ + if( v & 0x80 ) + { /* Reset IRQ flag */ + FM_STATUS_RESET(&OPN->ST, 0xf7); /* don't touch BUFRDY flag otherwise we'd have to call ymdeltat module to set the flag back */ + } + else + { /* Set status flag mask */ + F2608->flagmask = (~(v&0x1f)); + FM_IRQMASK_SET(&OPN->ST, (F2608->irqmask & F2608->flagmask) ); + } +} + +/* compatible mode & IRQ enable control 0x29 */ +static inline void YM2608IRQMaskWrite(FM_OPN *OPN, ym2608_state *F2608, int v) +{ + /* SCH,xx,xxx,EN_ZERO,EN_BRDY,EN_EOS,EN_TB,EN_TA */ + + /* extend 3ch. enable/disable */ + if(v&0x80) + OPN->type |= TYPE_6CH; /* OPNA mode - 6 FM channels */ + else + OPN->type &= ~TYPE_6CH; /* OPN mode - 3 FM channels */ + + /* IRQ MASK store and set */ + F2608->irqmask = v&0x1f; + FM_IRQMASK_SET(&OPN->ST, (F2608->irqmask & F2608->flagmask) ); +} + +/* Generate samples for one of the YM2608s */ +void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length) +{ + ym2608_state *F2608 = (ym2608_state *)chip; + FM_OPN *OPN = &F2608->OPN; + YM_DELTAT *DELTAT = &F2608->deltaT; + int i,j; + FMSAMPLE *bufL,*bufR; + FM_CH *cch[6]; + int32_t *out_fm = OPN->out_fm; + + /* set bufer */ + bufL = buffer[0]; + bufR = buffer[1]; + + cch[0] = &F2608->CH[0]; + cch[1] = &F2608->CH[1]; + cch[2] = &F2608->CH[2]; + cch[3] = &F2608->CH[3]; + cch[4] = &F2608->CH[4]; + cch[5] = &F2608->CH[5]; + + /* refresh PG and EG */ + refresh_fc_eg_chan( OPN, cch[0] ); + refresh_fc_eg_chan( OPN, cch[1] ); + if( (OPN->ST.mode & 0xc0) ) + { + /* 3SLOT MODE */ + if( cch[2]->SLOT[SLOT1].Incr==-1) + { + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode ); + } + } + else + refresh_fc_eg_chan( OPN, cch[2] ); + refresh_fc_eg_chan( OPN, cch[3] ); + refresh_fc_eg_chan( OPN, cch[4] ); + refresh_fc_eg_chan( OPN, cch[5] ); + + + /* buffering */ + for(i=0; i < length ; i++) + { + advance_lfo(OPN); + + /* clear output acc. */ + OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0; + OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0; + /* clear outputs */ + out_fm[0] = 0; + out_fm[1] = 0; + out_fm[2] = 0; + out_fm[3] = 0; + out_fm[4] = 0; + out_fm[5] = 0; + + /* calculate FM */ + chan_calc(OPN, cch[0], 0 ); + chan_calc(OPN, cch[1], 1 ); + chan_calc(OPN, cch[2], 2 ); + chan_calc(OPN, cch[3], 3 ); + chan_calc(OPN, cch[4], 4 ); + chan_calc(OPN, cch[5], 5 ); + + /* deltaT ADPCM */ + if( DELTAT->portstate&0x80 ) + DELTAT->ADPCM_CALC(); + + /* ADPCMA */ + for( j = 0; j < 6; j++ ) + { + if( F2608->adpcm[j].flag ) + F2608->ADPCMA_calc_chan( &F2608->adpcm[j]); + } + + /* advance envelope generator */ + OPN->eg_timer += OPN->eg_timer_add; + while (OPN->eg_timer >= OPN->eg_timer_overflow) + { + OPN->eg_timer -= OPN->eg_timer_overflow; + OPN->eg_cnt++; + + advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]); + } + + /* buffering */ + { + int lt,rt; + + lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]; + rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]; + lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9; + rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9; + + /* libOPNMIDI: 6-channels mixing */ +#define PANLAW_L(ch, chpan) (((out_fm[ch]>>1) * cch[ch]->pan_volume_l / 65535) & OPN->pan[chpan]); +#define PANLAW_R(ch, chpan) (((out_fm[ch]>>1) * cch[ch]->pan_volume_r / 65535) & OPN->pan[chpan]); + lt = PANLAW_L(0, 0); + rt = PANLAW_R(0, 1); + lt += PANLAW_L(1, 2); + rt += PANLAW_R(1, 3); + lt += PANLAW_L(2, 4); + rt += PANLAW_R(2, 5); + lt += PANLAW_L(3, 6); + rt += PANLAW_R(3, 7); + lt += PANLAW_L(4, 8); + rt += PANLAW_R(4, 9); + lt += PANLAW_L(5, 10); + rt += PANLAW_R(5, 11); +#undef PANLAW_L +#undef PANLAW_R + + lt >>= FINAL_SH; + rt >>= FINAL_SH; + + Limit( lt, MAXOUT, MINOUT ); + Limit( rt, MAXOUT, MINOUT ); + /* buffering */ + bufL[i] = lt; + bufR[i] = rt; + + #ifdef SAVE_SAMPLE + SAVE_ALL_CHANNELS + #endif + + } + + /* timer A control */ + INTERNAL_TIMER_A( &OPN->ST , cch[2] ) + } + INTERNAL_TIMER_B(&OPN->ST,length) + + + /* check IRQ for DELTA-T EOS */ + FM_STATUS_SET(&OPN->ST, 0); + +} +#ifdef MAME_EMU_SAVE_H +void ym2608_postload(void *chip) +{ + if (chip) + { + ym2608_state *F2608 = (ym2608_state *)chip; + int r; + + /* prescaler */ + OPNPrescaler_w(&F2608->OPN,1,2); + F2608->deltaT.freqbase = F2608->OPN.ST.freqbase; + /* IRQ mask / mode */ + YM2608IRQMaskWrite(&F2608->OPN, F2608, F2608->REGS[0x29]); + /* SSG registers */ + for(r=0;r<16;r++) + { + (*F2608->OPN.ST.SSG->write)(F2608->OPN.ST.device,0,r); + (*F2608->OPN.ST.SSG->write)(F2608->OPN.ST.device,1,F2608->REGS[r]); + } + + /* OPN registers */ + /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ + for(r=0x30;r<0x9e;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]); + OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]); + } + /* FB / CONNECT , L / R / AMS / PMS */ + for(r=0xb0;r<0xb6;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]); + OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]); + } + /* FM channels */ + /*FM_channel_postload(F2608->CH,6);*/ + /* rhythm(ADPCMA) */ + F2608->FM_ADPCMAWrite(1,F2608->REGS[0x111]); + for( r=0x08 ; r<0x0c ; r++) + F2608->FM_ADPCMAWrite(r,F2608->REGS[r+0x110]); + /* Delta-T ADPCM unit */ + F2608->deltaT.postload( &F2608->REGS[0x100] ); + } +} + +static void YM2608_save_state(ym2608_state *F2608, device_t *device) +{ + device->save_item(NAME(F2608->REGS)); + FMsave_state_st(device,&F2608->OPN.ST); + FMsave_state_channel(device,F2608->CH,6); + /* 3slots */ + device->save_item(NAME(F2608->OPN.SL3.fc)); + device->save_item(NAME(F2608->OPN.SL3.fn_h)); + device->save_item(NAME(F2608->OPN.SL3.kcode)); + /* address register1 */ + device->save_item(NAME(F2608->addr_A1)); + /* rhythm(ADPCMA) */ + FMsave_state_adpcma(device,F2608->adpcm); + /* Delta-T ADPCM unit */ + F2608->deltaT.savestate(device); +} +#endif /* MAME_EMU_SAVE_H */ + +static void YM2608_deltat_status_set(void *chip, uint8_t changebits) +{ + ym2608_state *F2608 = (ym2608_state *)chip; + FM_STATUS_SET(&(F2608->OPN.ST), changebits); +} +static void YM2608_deltat_status_reset(void *chip, uint8_t changebits) +{ + ym2608_state *F2608 = (ym2608_state *)chip; + FM_STATUS_RESET(&(F2608->OPN.ST), changebits); +} +/* YM2608(OPNA) */ +void * ym2608_init(device_t *device, int clock, int rate, + FM_READBYTE InternalReadByte, + FM_READBYTE ExternalReadByte, FM_WRITEBYTE ExternalWriteByte, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) +{ + ym2608_state *F2608; + + /* allocate extend state space */ + F2608 = new ym2608_state; + memset(F2608, 0, sizeof(*F2608)); + /* allocate total level table (128kb space) */ + if( !init_tables() ) + { + delete F2608; + return NULLPTR; + } + + F2608->device = device; + F2608->OPN.type = TYPE_YM2608; + F2608->OPN.P_CH = F2608->CH; + F2608->OPN.ST.device = device; + F2608->OPN.ST.clock = clock; + F2608->OPN.ST.rate = rate; + + /* External handlers */ + F2608->OPN.ST.timer_handler = timer_handler; + F2608->OPN.ST.IRQ_Handler = IRQHandler; + F2608->OPN.ST.SSG = ssg; + + /* DELTA-T */ + F2608->deltaT.read_byte = ExternalReadByte; + F2608->deltaT.write_byte = ExternalWriteByte; + + /*F2608->deltaT.write_time = 20.0 / clock;*/ /* a single byte write takes 20 cycles of main clock */ + /*F2608->deltaT.read_time = 18.0 / clock;*/ /* a single byte read takes 18 cycles of main clock */ + + F2608->deltaT.status_set_handler = YM2608_deltat_status_set; + F2608->deltaT.status_reset_handler = YM2608_deltat_status_reset; + F2608->deltaT.status_change_which_chip = F2608; + F2608->deltaT.status_change_EOS_bit = 0x04; /* status flag: set bit2 on End Of Sample */ + F2608->deltaT.status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY */ + F2608->deltaT.status_change_ZERO_bit = 0x10; /* status flag: set bit4 if silence continues for more than 290 milliseconds while recording the ADPCM */ + + /* ADPCM Rhythm */ + F2608->read_byte = InternalReadByte; + + Init_ADPCMATable(); + + for (unsigned i = 0; i < 6; i++) + { + F2608->CH[i].pan_volume_l = 46340; + F2608->CH[i].pan_volume_r = 46340; + } + +#ifdef MAME_EMU_SAVE_H + YM2608_save_state(F2608, device); +#endif + return F2608; +} + +void ym2608_clock_changed(void *chip, int clock, int rate) +{ + ym2608_state *F2608 = (ym2608_state *)chip; + + F2608->OPN.ST.clock = clock; + F2608->OPN.ST.rate = rate; +} + +/* shut down emulator */ +void ym2608_shutdown(void *chip) +{ + ym2608_state *F2608 = (ym2608_state *)chip; + + FMCloseTable(); + delete F2608; +} + +/* reset one of chips */ +void ym2608_reset_chip(void *chip) +{ + int i; + ym2608_state *F2608 = (ym2608_state *)chip; + FM_OPN *OPN = &F2608->OPN; + YM_DELTAT *DELTAT = &F2608->deltaT; + + /* Reset Prescaler */ + OPNPrescaler_w(OPN , 0 , 2); + F2608->deltaT.freqbase = OPN->ST.freqbase; + /* reset SSG section */ + (*OPN->ST.SSG->reset)(OPN->ST.device); + + /* status clear */ + FM_BUSY_CLEAR(&OPN->ST); + + /* register 0x29 - default value after reset is: + enable only 3 FM channels and enable all the status flags */ + YM2608IRQMaskWrite(OPN, F2608, 0x1f ); /* default value for D4-D0 is 1 */ + + /* register 0x10, A1=1 - default value is 1 for D4, D3, D2, 0 for the rest */ + YM2608IRQFlagWrite(OPN, F2608, 0x1c ); /* default: enable timer A and B, disable EOS, BRDY and ZERO */ + + OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */ + + OPN->eg_timer = 0; + OPN->eg_cnt = 0; + + FM_STATUS_RESET(&OPN->ST, 0xff); + + reset_channels( &OPN->ST , F2608->CH , 6 ); + /* reset OPerator paramater */ + for(i = 0xb6 ; i >= 0xb4 ; i-- ) + { + OPNWriteReg(OPN,i ,0xc0); + OPNWriteReg(OPN,i|0x100,0xc0); + } + for(i = 0xb2 ; i >= 0x30 ; i-- ) + { + OPNWriteReg(OPN,i ,0); + OPNWriteReg(OPN,i|0x100,0); + } + for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0); + + /* ADPCM - percussion sounds */ + for( i = 0; i < 6; i++ ) + { + if (i<=3) /* channels 0,1,2,3 */ + F2608->adpcm[i].step = (uint32_t)((float)(1<OPN.ST.freqbase)/3.0f); + else /* channels 4 and 5 work with slower clock */ + F2608->adpcm[i].step = (uint32_t)((float)(1<OPN.ST.freqbase)/6.0f); + + F2608->adpcm[i].start = YM2608_ADPCM_ROM_addr[i*2]; + F2608->adpcm[i].end = YM2608_ADPCM_ROM_addr[i*2+1]; + + F2608->adpcm[i].now_addr = 0; + F2608->adpcm[i].now_step = 0; + /* F2608->adpcm[i].delta = 21866; */ + F2608->adpcm[i].vol_mul = 0; + F2608->adpcm[i].pan = &OPN->out_adpcm[OUTD_CENTER]; /* default center */ + F2608->adpcm[i].flagMask = 0; + F2608->adpcm[i].flag = 0; + F2608->adpcm[i].adpcm_acc = 0; + F2608->adpcm[i].adpcm_step= 0; + F2608->adpcm[i].adpcm_out = 0; + } + F2608->adpcmTL = 0x3f; + + F2608->adpcm_arrivedEndAddress = 0; /* not used */ + + /* DELTA-T unit */ + DELTAT->freqbase = OPN->ST.freqbase; + DELTAT->output_pointer = OPN->out_delta; + DELTAT->portshift = 5; /* always 5bits shift */ /* ASG */ + DELTAT->output_range = 1<<23; + DELTAT->ADPCM_Reset(OUTD_CENTER,YM_DELTAT::EMULATION_MODE_NORMAL,F2608->device); +} + +/* YM2608 write */ +/* n = number */ +/* a = address */ +/* v = value */ +int ym2608_write(void *chip, int a,uint8_t v) +{ + ym2608_state *F2608 = (ym2608_state *)chip; + FM_OPN *OPN = &F2608->OPN; + int addr; + + v &= 0xff; /*adjust to 8 bit bus */ + + + switch(a&3) + { + case 0: /* address port 0 */ + OPN->ST.address = v; + F2608->addr_A1 = 0; + + /* Write register to SSG emulator */ + if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.device,0,v); + /* prescaler selecter : 2d,2e,2f */ + if( v >= 0x2d && v <= 0x2f ) + { + OPNPrescaler_w(OPN , v , 2); + F2608->deltaT.freqbase = OPN->ST.freqbase; + } + break; + + case 1: /* data port 0 */ + if (F2608->addr_A1 != 0) + break; /* verified on real YM2608 */ + + addr = OPN->ST.address; + F2608->REGS[addr] = v; + switch(addr & 0xf0) + { + case 0x00: /* SSG section */ + /* Write data to SSG emulator */ + (*OPN->ST.SSG->write)(OPN->ST.device,a,v); + break; + case 0x10: /* 0x10-0x1f : Rhythm section */ + ym2608_device::update_request(OPN->ST.device); + F2608->FM_ADPCMAWrite(addr-0x10,v); + break; + case 0x20: /* Mode Register */ + switch(addr) + { + case 0x29: /* SCH,xx,xxx,EN_ZERO,EN_BRDY,EN_EOS,EN_TB,EN_TA */ + YM2608IRQMaskWrite(OPN, F2608, v); + break; + default: + ym2608_device::update_request(OPN->ST.device); + OPNWriteMode(OPN,addr,v); + } + break; + default: /* OPN section */ + ym2608_device::update_request(OPN->ST.device); + OPNWriteReg(OPN,addr,v); + } + break; + + case 2: /* address port 1 */ + OPN->ST.address = v; + F2608->addr_A1 = 1; + break; + + case 3: /* data port 1 */ + if (F2608->addr_A1 != 1) + break; /* verified on real YM2608 */ + + addr = OPN->ST.address; + F2608->REGS[addr | 0x100] = v; + ym2608_device::update_request(OPN->ST.device); + switch( addr & 0xf0 ) + { + case 0x00: /* DELTAT PORT */ + switch( addr ) + { + case 0x0e: /* DAC data */ + F2608->device->logerror("YM2608: write to DAC data (unimplemented) value=%02x\n",v); + break; + default: + /* 0x00-0x0d */ + F2608->deltaT.ADPCM_Write(addr,v); + } + break; + case 0x10: /* IRQ Flag control */ + if( addr == 0x10 ) + { + YM2608IRQFlagWrite(OPN, F2608, v); + } + break; + default: + OPNWriteReg(OPN,addr | 0x100,v); + } + } + return OPN->ST.irq; +} + +// libOPNMIDI: soft panning +void ym2608_write_pan(void *chip, int c, unsigned char v) +{ + ym2608_state *F2608 = (ym2608_state *)chip; + assert((c >= 0) && (c < 6)); + F2608->CH[c].pan_volume_l = panlawtable[v & 0x7F]; + F2608->CH[c].pan_volume_r = panlawtable[0x7F - (v & 0x7F)]; +} + +uint8_t ym2608_read(void *chip,int a) +{ + ym2608_state *F2608 = (ym2608_state *)chip; + int addr = F2608->OPN.ST.address; + uint8_t ret = 0; + + switch( a&3 ) + { + case 0: /* status 0 : YM2203 compatible */ + /* BUSY:x:x:x:x:x:FLAGB:FLAGA */ + ret = FM_STATUS_FLAG(&F2608->OPN.ST) & 0x83; + break; + + case 1: /* status 0, ID */ + if( addr < 16 ) ret = (*F2608->OPN.ST.SSG->read)(F2608->OPN.ST.device); + else if(addr == 0xff) ret = 0x01; /* ID code */ + break; + + case 2: /* status 1 : status 0 + ADPCM status */ + /* BUSY : x : PCMBUSY : ZERO : BRDY : EOS : FLAGB : FLAGA */ + ret = (FM_STATUS_FLAG(&F2608->OPN.ST) & (F2608->flagmask|0x80)) | ((F2608->deltaT.PCM_BSY & 1)<<5) ; + break; + + case 3: + if(addr == 0x08) + { + ret = F2608->deltaT.ADPCM_Read(); + } + else + { + if(addr == 0x0f) + { + F2608->device->logerror("YM2608 A/D conversion is accessed but not implemented !\n"); + ret = 0x80; /* 2's complement PCM data - result from A/D conversion */ + } + } + break; + } + return ret; +} + +int ym2608_timer_over(void *chip,int c) +{ + ym2608_state *F2608 = (ym2608_state *)chip; + + switch(c) + { +#if 0 + case 2: + { /* BUFRDY flag */ + F2608->deltaT.BRDY_callback(); + } + break; +#endif + case 1: + { /* Timer B */ + TimerBOver( &(F2608->OPN.ST) ); + } + break; + case 0: + { /* Timer A */ + ym2608_device::update_request(F2608->OPN.ST.device); + /* timer update */ + TimerAOver( &(F2608->OPN.ST) ); + /* CSM mode key,TL controll */ + if( F2608->OPN.ST.mode & 0x80 ) + { /* CSM mode total level latch and auto key on */ + CSMKeyControll( F2608->OPN.type, &(F2608->CH[2]) ); + } + } + break; + default: + break; + } + + return F2608->OPN.ST.irq; +} + +#endif /* BUILD_YM2608 */ + + + +#if (BUILD_YM2610||BUILD_YM2610B) +/* YM2610(OPNB) */ + +/* Generate samples for one of the YM2610s */ +void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length) +{ + ym2610_state *F2610 = (ym2610_state *)chip; + FM_OPN *OPN = &F2610->OPN; + YM_DELTAT *DELTAT = &F2610->deltaT; + int i,j; + FMSAMPLE *bufL,*bufR; + FM_CH *cch[4]; + int32_t *out_fm = OPN->out_fm; + + /* buffer setup */ + bufL = buffer[0]; + bufR = buffer[1]; + + cch[0] = &F2610->CH[1]; + cch[1] = &F2610->CH[2]; + cch[2] = &F2610->CH[4]; + cch[3] = &F2610->CH[5]; + +#ifdef YM2610B_WARNING +#define FM_KEY_IS(SLOT) ((SLOT)->key) +#define FM_MSG_YM2610B "YM2610-%p.CH%d is playing,Check whether the type of the chip is YM2610B\n" + /* Check YM2610B warning message */ + if( FM_KEY_IS(&F2610->CH[0].SLOT[3]) ) + LOG(F2610->device,LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.device,0)); + if( FM_KEY_IS(&F2610->CH[3].SLOT[3]) ) + LOG(F2610->device,LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.device,3)); +#endif + + /* refresh PG and EG */ + refresh_fc_eg_chan( OPN, cch[0] ); + if( (OPN->ST.mode & 0xc0) ) + { + /* 3SLOT MODE */ + if( cch[1]->SLOT[SLOT1].Incr==-1) + { + refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); + refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); + refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); + refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT4] , cch[1]->fc , cch[1]->kcode ); + } + } + else + refresh_fc_eg_chan( OPN, cch[1] ); + refresh_fc_eg_chan( OPN, cch[2] ); + refresh_fc_eg_chan( OPN, cch[3] ); + + /* buffering */ + for(i=0; i < length ; i++) + { + advance_lfo(OPN); + + /* clear output acc. */ + OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0; + OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0; + /* clear outputs */ + out_fm[1] = 0; + out_fm[2] = 0; + out_fm[4] = 0; + out_fm[5] = 0; + + /* advance envelope generator */ + OPN->eg_timer += OPN->eg_timer_add; + while (OPN->eg_timer >= OPN->eg_timer_overflow) + { + OPN->eg_timer -= OPN->eg_timer_overflow; + OPN->eg_cnt++; + + advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]); + } + + /* calculate FM */ + chan_calc(OPN, cch[0], 1 ); /*remapped to 1*/ + chan_calc(OPN, cch[1], 2 ); /*remapped to 2*/ + chan_calc(OPN, cch[2], 4 ); /*remapped to 4*/ + chan_calc(OPN, cch[3], 5 ); /*remapped to 5*/ + + /* deltaT ADPCM */ + if( DELTAT->portstate&0x80 ) + DELTAT->ADPCM_CALC(); + + /* ADPCMA */ + for( j = 0; j < 6; j++ ) + { + if( F2610->adpcm[j].flag ) + F2610->ADPCMA_calc_chan(&F2610->adpcm[j]); + } + + /* buffering */ + { + int lt,rt; + + lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]; + rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]; + lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9; + rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9; + + + lt += ((out_fm[1]>>1) & OPN->pan[2]); /* the shift right was verified on real chip */ + rt += ((out_fm[1]>>1) & OPN->pan[3]); + lt += ((out_fm[2]>>1) & OPN->pan[4]); + rt += ((out_fm[2]>>1) & OPN->pan[5]); + + lt += ((out_fm[4]>>1) & OPN->pan[8]); + rt += ((out_fm[4]>>1) & OPN->pan[9]); + lt += ((out_fm[5]>>1) & OPN->pan[10]); + rt += ((out_fm[5]>>1) & OPN->pan[11]); + + + lt >>= FINAL_SH; + rt >>= FINAL_SH; + + Limit( lt, MAXOUT, MINOUT ); + Limit( rt, MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + SAVE_ALL_CHANNELS + #endif + + /* buffering */ + bufL[i] = lt; + bufR[i] = rt; + } + + /* timer A control */ + INTERNAL_TIMER_A( &OPN->ST , cch[1] ) + } + INTERNAL_TIMER_B(&OPN->ST,length) + +} + +#if BUILD_YM2610B +/* Generate samples for one of the YM2610Bs */ +void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length) +{ + ym2610_state *F2610 = (ym2610_state *)chip; + FM_OPN *OPN = &F2610->OPN; + YM_DELTAT *DELTAT = &F2610->deltaT; + int i,j; + FMSAMPLE *bufL,*bufR; + FM_CH *cch[6]; + int32_t *out_fm = OPN->out_fm; + + /* buffer setup */ + bufL = buffer[0]; + bufR = buffer[1]; + + cch[0] = &F2610->CH[0]; + cch[1] = &F2610->CH[1]; + cch[2] = &F2610->CH[2]; + cch[3] = &F2610->CH[3]; + cch[4] = &F2610->CH[4]; + cch[5] = &F2610->CH[5]; + + /* refresh PG and EG */ + refresh_fc_eg_chan( OPN, cch[0] ); + refresh_fc_eg_chan( OPN, cch[1] ); + if( (OPN->ST.mode & 0xc0) ) + { + /* 3SLOT MODE */ + if( cch[2]->SLOT[SLOT1].Incr==-1) + { + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); + refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode ); + } + } + else + refresh_fc_eg_chan( OPN, cch[2] ); + refresh_fc_eg_chan( OPN, cch[3] ); + refresh_fc_eg_chan( OPN, cch[4] ); + refresh_fc_eg_chan( OPN, cch[5] ); + + /* buffering */ + for(i=0; i < length ; i++) + { + advance_lfo(OPN); + + /* clear output acc. */ + OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0; + OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0; + /* clear outputs */ + out_fm[0] = 0; + out_fm[1] = 0; + out_fm[2] = 0; + out_fm[3] = 0; + out_fm[4] = 0; + out_fm[5] = 0; + + /* advance envelope generator */ + OPN->eg_timer += OPN->eg_timer_add; + while (OPN->eg_timer >= OPN->eg_timer_overflow) + { + OPN->eg_timer -= OPN->eg_timer_overflow; + OPN->eg_cnt++; + + advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]); + } + + /* calculate FM */ + chan_calc(OPN, cch[0], 0 ); + chan_calc(OPN, cch[1], 1 ); + chan_calc(OPN, cch[2], 2 ); + chan_calc(OPN, cch[3], 3 ); + chan_calc(OPN, cch[4], 4 ); + chan_calc(OPN, cch[5], 5 ); + + /* deltaT ADPCM */ + if( DELTAT->portstate&0x80 ) + DELTAT->ADPCM_CALC(); + + /* ADPCMA */ + for( j = 0; j < 6; j++ ) + { + if( F2610->adpcm[j].flag ) + F2610->ADPCMA_calc_chan(&F2610->adpcm[j]); + } + + /* buffering */ + { + int lt,rt; + + lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]; + rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]; + lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9; + rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9; + + lt += ((out_fm[0]>>1) & OPN->pan[0]); /* the shift right is verified on YM2610 */ + rt += ((out_fm[0]>>1) & OPN->pan[1]); + lt += ((out_fm[1]>>1) & OPN->pan[2]); + rt += ((out_fm[1]>>1) & OPN->pan[3]); + lt += ((out_fm[2]>>1) & OPN->pan[4]); + rt += ((out_fm[2]>>1) & OPN->pan[5]); + lt += ((out_fm[3]>>1) & OPN->pan[6]); + rt += ((out_fm[3]>>1) & OPN->pan[7]); + lt += ((out_fm[4]>>1) & OPN->pan[8]); + rt += ((out_fm[4]>>1) & OPN->pan[9]); + lt += ((out_fm[5]>>1) & OPN->pan[10]); + rt += ((out_fm[5]>>1) & OPN->pan[11]); + + + lt >>= FINAL_SH; + rt >>= FINAL_SH; + + Limit( lt, MAXOUT, MINOUT ); + Limit( rt, MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + SAVE_ALL_CHANNELS + #endif + + /* buffering */ + bufL[i] = lt; + bufR[i] = rt; + } + + /* timer A control */ + INTERNAL_TIMER_A( &OPN->ST , cch[2] ) + } + INTERNAL_TIMER_B(&OPN->ST,length) + +} +#endif /* BUILD_YM2610B */ + + +#ifdef MAME_EMU_SAVE_H +void ym2610_postload(void *chip) +{ + if (chip) + { + ym2610_state *F2610 = (ym2610_state *)chip; + int r; + + /* SSG registers */ + for(r=0;r<16;r++) + { + (*F2610->OPN.ST.SSG->write)(F2610->OPN.ST.device,0,r); + (*F2610->OPN.ST.SSG->write)(F2610->OPN.ST.device,1,F2610->REGS[r]); + } + + /* OPN registers */ + /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ + for(r=0x30;r<0x9e;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2610->OPN,r,F2610->REGS[r]); + OPNWriteReg(&F2610->OPN,r|0x100,F2610->REGS[r|0x100]); + } + /* FB / CONNECT , L / R / AMS / PMS */ + for(r=0xb0;r<0xb6;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2610->OPN,r,F2610->REGS[r]); + OPNWriteReg(&F2610->OPN,r|0x100,F2610->REGS[r|0x100]); + } + /* FM channels */ + /*FM_channel_postload(F2610->CH,6);*/ + + /* rhythm(ADPCMA) */ + F2610->FM_ADPCMAWrite(1,F2610->REGS[0x101]); + for( r=0 ; r<6 ; r++) + { + F2610->FM_ADPCMAWrite(r+0x08,F2610->REGS[r+0x108]); + F2610->FM_ADPCMAWrite(r+0x10,F2610->REGS[r+0x110]); + F2610->FM_ADPCMAWrite(r+0x18,F2610->REGS[r+0x118]); + F2610->FM_ADPCMAWrite(r+0x20,F2610->REGS[r+0x120]); + F2610->FM_ADPCMAWrite(r+0x28,F2610->REGS[r+0x128]); + } + /* Delta-T ADPCM unit */ + F2610->deltaT.postload( &F2610->REGS[0x010] ); + } +} + +static void YM2610_save_state(ym2610_state *F2610, device_t *device) +{ + device->save_item(NAME(F2610->REGS)); + FMsave_state_st(device,&F2610->OPN.ST); + FMsave_state_channel(device,F2610->CH,6); + /* 3slots */ + device->save_item(NAME(F2610->OPN.SL3.fc)); + device->save_item(NAME(F2610->OPN.SL3.fn_h)); + device->save_item(NAME(F2610->OPN.SL3.kcode)); + /* address register1 */ + device->save_item(NAME(F2610->addr_A1)); + + device->save_item(NAME(F2610->adpcm_arrivedEndAddress)); + /* rhythm(ADPCMA) */ + FMsave_state_adpcma(device,F2610->adpcm); + /* Delta-T ADPCM unit */ + F2610->deltaT.savestate(device); +} +#endif /* MAME_EMU_SAVE_H */ + +static void YM2610_deltat_status_set(void *chip, uint8_t changebits) +{ + ym2610_state *F2610 = (ym2610_state *)chip; + F2610->adpcm_arrivedEndAddress |= changebits; +} +static void YM2610_deltat_status_reset(void *chip, uint8_t changebits) +{ + ym2610_state *F2610 = (ym2610_state *)chip; + F2610->adpcm_arrivedEndAddress &= (~changebits); +} + +void *ym2610_init(device_t *device, int clock, int rate, + FM_READBYTE adpcm_a_read_byte, FM_READBYTE adpcm_b_read_byte, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) +{ + ym2610_state *F2610; + + /* allocate extend state space */ + F2610 = new ym2610_state; + memset(F2610, 0, sizeof(*F2610)); + /* allocate total level table (128kb space) */ + if( !init_tables() ) + { + delete F2610; + return nullptr; + } + + F2610->device = device; + /* FM */ + F2610->OPN.type = TYPE_YM2610; + F2610->OPN.P_CH = F2610->CH; + F2610->OPN.ST.device = device; + F2610->OPN.ST.clock = clock; + F2610->OPN.ST.rate = rate; + /* Extend handler */ + F2610->OPN.ST.timer_handler = timer_handler; + F2610->OPN.ST.IRQ_Handler = IRQHandler; + F2610->OPN.ST.SSG = ssg; + /* ADPCM */ + F2610->read_byte = adpcm_a_read_byte; + /* DELTA-T */ + F2610->deltaT.read_byte = adpcm_b_read_byte; + F2610->deltaT.write_byte = nullptr; + + F2610->deltaT.status_set_handler = YM2610_deltat_status_set; + F2610->deltaT.status_reset_handler = YM2610_deltat_status_reset; + F2610->deltaT.status_change_which_chip = F2610; + F2610->deltaT.status_change_EOS_bit = 0x80; /* status flag: set bit7 on End Of Sample */ + + Init_ADPCMATable(); + + for (unsigned i = 0; i < 6; i++) + { + F2610->CH[i].pan_volume_l = 46340; + F2610->CH[i].pan_volume_r = 46340; + } + +#ifdef MAME_EMU_SAVE_H + YM2610_save_state(F2610, device); +#endif + return F2610; +} + +void ym2610_clock_changed(void *chip, int clock, int rate) +{ + ym2610_state *F2610 = (ym2610_state *)chip; + + F2610->OPN.ST.clock = clock; + F2610->OPN.ST.rate = rate; +} + +/* shut down emulator */ +void ym2610_shutdown(void *chip) +{ + ym2610_state *F2610 = (ym2610_state *)chip; + + FMCloseTable(); + delete F2610; +} + +/* reset one of chip */ +void ym2610_reset_chip(void *chip) +{ + int i; + ym2610_state *F2610 = (ym2610_state *)chip; + FM_OPN *OPN = &F2610->OPN; + YM_DELTAT *DELTAT = &F2610->deltaT; + + device_t* dev = F2610->OPN.ST.device; + std::string name(dev->tag()); + + /* Reset Prescaler */ + OPNSetPres( OPN, 6*24, 6*24, 4*2); /* OPN 1/6 , SSG 1/4 */ + /* reset SSG section */ + (*OPN->ST.SSG->reset)(OPN->ST.device); + /* status clear */ + FM_IRQMASK_SET(&OPN->ST,0x03); + FM_BUSY_CLEAR(&OPN->ST); + OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */ + + OPN->eg_timer = 0; + OPN->eg_cnt = 0; + + FM_STATUS_RESET(&OPN->ST, 0xff); + + reset_channels( &OPN->ST , F2610->CH , 6 ); + /* reset OPerator paramater */ + for(i = 0xb6 ; i >= 0xb4 ; i-- ) + { + OPNWriteReg(OPN,i ,0xc0); + OPNWriteReg(OPN,i|0x100,0xc0); + } + for(i = 0xb2 ; i >= 0x30 ; i-- ) + { + OPNWriteReg(OPN,i ,0); + OPNWriteReg(OPN,i|0x100,0); + } + for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0); + /**** ADPCM work initial ****/ + for( i = 0; i < 6 ; i++ ) + { + F2610->adpcm[i].step = (uint32_t)((float)(1<OPN.ST.freqbase)/3.0f); + F2610->adpcm[i].now_addr = 0; + F2610->adpcm[i].now_step = 0; + F2610->adpcm[i].start = 0; + F2610->adpcm[i].end = 0; + /* F2610->adpcm[i].delta = 21866; */ + F2610->adpcm[i].vol_mul = 0; + F2610->adpcm[i].pan = &OPN->out_adpcm[OUTD_CENTER]; /* default center */ + F2610->adpcm[i].flagMask = 1<adpcm[i].flag = 0; + F2610->adpcm[i].adpcm_acc = 0; + F2610->adpcm[i].adpcm_step= 0; + F2610->adpcm[i].adpcm_out = 0; + } + F2610->adpcmTL = 0x3f; + + F2610->adpcm_arrivedEndAddress = 0; + + /* DELTA-T unit */ + DELTAT->freqbase = OPN->ST.freqbase; + DELTAT->output_pointer = OPN->out_delta; + DELTAT->portshift = 8; /* allways 8bits shift */ + DELTAT->output_range = 1<<23; + DELTAT->ADPCM_Reset(OUTD_CENTER,YM_DELTAT::EMULATION_MODE_YM2610,F2610->device); +} + +/* YM2610 write */ +/* n = number */ +/* a = address */ +/* v = value */ +int ym2610_write(void *chip, int a, uint8_t v) +{ + ym2610_state *F2610 = (ym2610_state *)chip; + FM_OPN *OPN = &F2610->OPN; + int addr; + int ch; + + v &= 0xff; /* adjust to 8 bit bus */ + + switch( a&3 ) + { + case 0: /* address port 0 */ + OPN->ST.address = v; + F2610->addr_A1 = 0; + + /* Write register to SSG emulator */ + if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.device,0,v); + break; + + case 1: /* data port 0 */ + if (F2610->addr_A1 != 0) + break; /* verified on real YM2608 */ + + addr = OPN->ST.address; + F2610->REGS[addr] = v; + switch(addr & 0xf0) + { + case 0x00: /* SSG section */ + /* Write data to SSG emulator */ + (*OPN->ST.SSG->write)(OPN->ST.device,a,v); + break; + case 0x10: /* DeltaT ADPCM */ + ym2610_device::update_request(OPN->ST.device); + + switch(addr) + { + case 0x10: /* control 1 */ + case 0x11: /* control 2 */ + case 0x12: /* start address L */ + case 0x13: /* start address H */ + case 0x14: /* stop address L */ + case 0x15: /* stop address H */ + + case 0x19: /* delta-n L */ + case 0x1a: /* delta-n H */ + case 0x1b: /* volume */ + { + F2610->deltaT.ADPCM_Write(addr-0x10,v); + } + break; + + case 0x1c: /* FLAG CONTROL : Extend Status Clear/Mask */ + { + uint8_t statusmask = ~v; + /* set arrived flag mask */ + for(ch=0;ch<6;ch++) + F2610->adpcm[ch].flagMask = statusmask&(1<deltaT.status_change_EOS_bit = statusmask & 0x80; /* status flag: set bit7 on End Of Sample */ + + /* clear arrived flag */ + F2610->adpcm_arrivedEndAddress &= statusmask; + } + break; + + default: + F2610->device->logerror("YM2610: write to unknown deltat register %02x val=%02x\n",addr,v); + break; + } + + break; + case 0x20: /* Mode Register */ + ym2610_device::update_request(OPN->ST.device); + OPNWriteMode(OPN,addr,v); + break; + default: /* OPN section */ + ym2610_device::update_request(OPN->ST.device); + /* write register */ + OPNWriteReg(OPN,addr,v); + } + break; + + case 2: /* address port 1 */ + OPN->ST.address = v; + F2610->addr_A1 = 1; + break; + + case 3: /* data port 1 */ + if (F2610->addr_A1 != 1) + break; /* verified on real YM2608 */ + + ym2610_device::update_request(OPN->ST.device); + addr = OPN->ST.address; + F2610->REGS[addr | 0x100] = v; + if( addr < 0x30 ) + /* 100-12f : ADPCM A section */ + F2610->FM_ADPCMAWrite(addr,v); + else + OPNWriteReg(OPN,addr | 0x100,v); + } + return OPN->ST.irq; +} + +uint8_t ym2610_read(void *chip,int a) +{ + ym2610_state *F2610 = (ym2610_state *)chip; + int addr = F2610->OPN.ST.address; + uint8_t ret = 0; + + switch( a&3) + { + case 0: /* status 0 : YM2203 compatible */ + ret = FM_STATUS_FLAG(&F2610->OPN.ST) & 0x83; + break; + case 1: /* data 0 */ + if( addr < 16 ) ret = (*F2610->OPN.ST.SSG->read)(F2610->OPN.ST.device); + if( addr == 0xff ) ret = 0x01; + break; + case 2: /* status 1 : ADPCM status */ + /* ADPCM STATUS (arrived End Address) */ + /* B,--,A5,A4,A3,A2,A1,A0 */ + /* B = ADPCM-B(DELTA-T) arrived end address */ + /* A0-A5 = ADPCM-A arrived end address */ + ret = F2610->adpcm_arrivedEndAddress; + break; + case 3: + ret = 0; + break; + } + return ret; +} + +int ym2610_timer_over(void *chip,int c) +{ + ym2610_state *F2610 = (ym2610_state *)chip; + + if( c ) + { /* Timer B */ + TimerBOver( &(F2610->OPN.ST) ); + } + else + { /* Timer A */ + ym2610_device::update_request(F2610->OPN.ST.device); + /* timer update */ + TimerAOver( &(F2610->OPN.ST) ); + /* CSM mode key,TL controll */ + if( F2610->OPN.ST.mode & 0x80 ) + { /* CSM mode total level latch and auto key on */ + CSMKeyControll( F2610->OPN.type, &(F2610->CH[2]) ); + } + } + return F2610->OPN.ST.irq; +} + +#endif /* (BUILD_YM2610||BUILD_YM2610B) */ diff --git a/thirdparty/opnmidi/chips/mamefm/fm.h b/thirdparty/opnmidi/chips/mamefm/fm.h new file mode 100644 index 000000000..b95c4ff8a --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/fm.h @@ -0,0 +1,193 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh +/* + File: fm.h -- header file for software emulation for FM sound generator + +*/ + +#ifndef MAME_SOUND_FM_H +#define MAME_SOUND_FM_H + +#pragma once +#include "emu.h" + +struct device_t; +typedef int32_t stream_sample_t; + +/* --- select emulation chips --- */ +#define BUILD_YM2203 (0) /* build YM2203(OPN) emulator */ +#define BUILD_YM2608 (1) /* build YM2608(OPNA) emulator */ +#define BUILD_YM2610 (0) /* build YM2610(OPNB) emulator */ +#define BUILD_YM2610B (0) /* build YM2610B(OPNB?)emulator */ +#define BUILD_YM2612 (0) /* build YM2612(OPN2) emulator */ +#define BUILD_YM3438 (0) /* build YM3438(OPN) emulator */ + +/* select bit size of output : 8 or 16 */ +#define FM_SAMPLE_BITS 16 + +/* select timer system internal or external */ +#define FM_INTERNAL_TIMER 0 + +/* --- speedup optimize --- */ +/* busy flag emulation , The definition of FM_GET_TIME_NOW() is necessary. */ +#define FM_BUSY_FLAG_SUPPORT 0 + +/* --- external SSG(YM2149/AY-3-8910)emulator interface port */ +/* used by YM2203,YM2608,and YM2610 */ +struct ssg_callbacks +{ + void (*set_clock)(device_t *device, int clock); + void (*write)(device_t *device, int address, int data); + int (*read)(device_t *device); + void (*reset)(device_t *device); +}; + +/* --- external callback functions for realtime update --- */ + +#if FM_BUSY_FLAG_SUPPORT +using TIME_TYPE = attotime; +#define UNDEFINED_TIME attotime::zero +#define FM_GET_TIME_NOW(machine) (machine)->time() +inline TIME_TYPE ADD_TIMES(TIME_TYPE const &t1, TIME_TYPE const &t2) { return t1 + t2; } +constexpr int COMPARE_TIMES(TIME_TYPE const &t1, TIME_TYPE const &t2) { return (t1 == t2) ? 0 : (t1 < t2) ? -1 : 1; } +template constexpr TIME_TYPE MULTIPLY_TIME_BY_INT(TIME_TYPE const &t, X &&i) { return t * i; } +#endif + + +typedef stream_sample_t FMSAMPLE; +/* +#if (FM_SAMPLE_BITS==16) +typedef int16_t FMSAMPLE; +#endif +#if (FM_SAMPLE_BITS==8) +typedef unsigned char FMSAMPLE; +#endif +*/ + +typedef uint8_t (*FM_READBYTE)(device_t *device, offs_t offset); +typedef void(*FM_WRITEBYTE)(device_t *device, offs_t offset, uint8_t data); +typedef void (*FM_TIMERHANDLER)(device_t *device,int c,int cnt,int clock); +typedef void (*FM_IRQHANDLER)(device_t *device,int irq); +/* FM_TIMERHANDLER : Stop or Start timer */ +/* int n = chip number */ +/* int c = Channel 0=TimerA,1=TimerB */ +/* int count = timer count (0=stop) */ +/* doube stepTime = step time of one count (sec.)*/ + +/* FM_IRQHHANDLER : IRQ level changing sense */ +/* int n = chip number */ +/* int irq = IRQ level 0=OFF,1=ON */ + +#if BUILD_YM2203 +/* -------------------- YM2203(OPN) Interface -------------------- */ + +/* +** Initialize YM2203 emulator(s). +** +** 'num' is the number of virtual YM2203's to allocate +** 'baseclock' +** 'rate' is sampling rate +** 'TimerHandler' timer callback handler when timer start and clear +** 'IRQHandler' IRQ callback handler when changed IRQ level +** return 0 = success +*/ +void * ym2203_init(device_t *device, int baseclock, int rate, + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); + +/* +** YM2203 clock changed notifier +*/ +void ym2203_clock_changed(void *chip, int clock, int rate); + +/* +** shutdown the YM2203 emulators +*/ +void ym2203_shutdown(void *chip); + +/* +** reset all chip registers for YM2203 number 'num' +*/ +void ym2203_reset_chip(void *chip); + +/* +** update one of chip +*/ +void ym2203_update_one(void *chip, FMSAMPLE *buffer, int length); + +/* +** Write +** return : InterruptLevel +*/ +int ym2203_write(void *chip,int a,unsigned char v); + +/* +** Read +** return : InterruptLevel +*/ +unsigned char ym2203_read(void *chip,int a); + +/* +** Timer OverFlow +*/ +int ym2203_timer_over(void *chip, int c); + +/* +** State Save +*/ +void ym2203_postload(void *chip); +#endif /* BUILD_YM2203 */ + +#if BUILD_YM2608 +/* -------------------- YM2608(OPNA) Interface -------------------- */ +void * ym2608_init(device_t *device, int baseclock, int rate, + FM_READBYTE InternalReadByte, + FM_READBYTE ExternalReadByte, FM_WRITEBYTE ExternalWriteByte, + FM_TIMERHANDLER TimerHandler, FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); +void ym2608_clock_changed(void *chip, int clock, int rate); +void ym2608_shutdown(void *chip); +void ym2608_reset_chip(void *chip); +void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length); + +int ym2608_write(void *chip, int a,unsigned char v); +void ym2608_write_pan(void *chip, int c,unsigned char v); // libOPNMIDI: soft panning +unsigned char ym2608_read(void *chip,int a); +int ym2608_timer_over(void *chip, int c ); +void ym2608_postload(void *chip); +#endif /* BUILD_YM2608 */ + +#if (BUILD_YM2610||BUILD_YM2610B) +/* -------------------- YM2610(OPNB) Interface -------------------- */ +void * ym2610_init(device_t *device, int baseclock, int rate, + FM_READBYTE adpcm_a_read_byte, FM_READBYTE adpcm_b_read_byte, + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); +void ym2610_clock_changed(void *chip, int clock, int rate); +void ym2610_shutdown(void *chip); +void ym2610_reset_chip(void *chip); +void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length); + +#if BUILD_YM2610B +void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length); +#endif /* BUILD_YM2610B */ + +int ym2610_write(void *chip, int a,unsigned char v); +unsigned char ym2610_read(void *chip,int a); +int ym2610_timer_over(void *chip, int c ); +void ym2610_postload(void *chip); +#endif /* (BUILD_YM2610||BUILD_YM2610B) */ + +#if (BUILD_YM2612||BUILD_YM3438) +void * ym2612_init(device_t *device, int baseclock, int rate, + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler); +void ym2612_clock_changed(void *chip, int clock, int rate); +void ym2612_shutdown(void *chip); +void ym2612_reset_chip(void *chip); +void ym2612_update_one(void *chip, FMSAMPLE **buffer, int length); + +int ym2612_write(void *chip, int a,unsigned char v); +unsigned char ym2612_read(void *chip,int a); +int ym2612_timer_over(void *chip, int c ); +void ym2612_postload(void *chip); +#endif /* (BUILD_YM2612||BUILD_YM3438) */ + + +#endif // MAME_SOUND_FM_H diff --git a/thirdparty/opnmidi/chips/mamefm/fmopn_2608rom.h b/thirdparty/opnmidi/chips/mamefm/fmopn_2608rom.h new file mode 100644 index 000000000..5d428ee5c --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/fmopn_2608rom.h @@ -0,0 +1,543 @@ +/* + This data is derived from the chip's output - internal ROM can't be read. + It was verified, using real YM2608, that this ADPCM stream produces 100% correct output signal. +*/ + +static const unsigned char YM2608_ADPCM_ROM[0x2000] = { + +/* Source: 01BD.ROM */ +/* Length: 448 / 0x000001C0 */ + +0x88,0x08,0x08,0x08,0x00,0x88,0x16,0x76,0x99,0xB8,0x22,0x3A,0x84,0x3C,0xB1,0x54, +0x10,0xA9,0x98,0x32,0x80,0x33,0x9A,0xA7,0x4A,0xB4,0x58,0xBC,0x15,0x29,0x8A,0x97, +0x9B,0x44,0xAC,0x80,0x12,0xDE,0x13,0x1B,0xC0,0x58,0xC8,0x11,0x0A,0xA2,0x1A,0xA0, +0x00,0x98,0x0B,0x93,0x9E,0x92,0x0A,0x88,0xBE,0x14,0x1B,0x98,0x08,0xA1,0x4A,0xC1, +0x30,0xD9,0x33,0x98,0x10,0x89,0x17,0x1A,0x82,0x29,0x37,0x0C,0x83,0x50,0x9A,0x24, +0x1A,0x83,0x10,0x23,0x19,0xB3,0x72,0x8A,0x16,0x10,0x0A,0x93,0x70,0x99,0x23,0x99, +0x02,0x20,0x91,0x18,0x02,0x41,0xAB,0x24,0x18,0x81,0x99,0x4A,0xE8,0x28,0x9A,0x99, +0xA1,0x2F,0xA8,0x9D,0x90,0x08,0xCC,0xA3,0x1D,0xCA,0x82,0x0B,0xD8,0x08,0xB9,0x09, +0xBC,0xB8,0x00,0xBE,0x90,0x1B,0xCA,0x00,0x9B,0x8A,0xA8,0x91,0x0F,0xB3,0x3D,0xB8, +0x31,0x0B,0xA5,0x0A,0x11,0xA1,0x48,0x92,0x10,0x50,0x91,0x30,0x23,0x09,0x37,0x39, +0xA2,0x72,0x89,0x92,0x30,0x83,0x1C,0x96,0x28,0xB9,0x24,0x8C,0xA1,0x31,0xAD,0xA9, +0x13,0x9C,0xBA,0xA8,0x0B,0xBF,0xB8,0x9B,0xCA,0x88,0xDB,0xB8,0x19,0xFC,0x92,0x0A, +0xBA,0x89,0xAB,0xB8,0xAB,0xD8,0x08,0xAD,0xBA,0x33,0x9D,0xAA,0x83,0x3A,0xC0,0x40, +0xB9,0x15,0x39,0xA2,0x52,0x89,0x02,0x63,0x88,0x13,0x23,0x03,0x52,0x02,0x54,0x00, +0x11,0x23,0x23,0x35,0x20,0x01,0x44,0x41,0x80,0x24,0x40,0xA9,0x45,0x19,0x81,0x12, +0x81,0x02,0x11,0x21,0x19,0x02,0x61,0x8A,0x13,0x3A,0x10,0x12,0x23,0x8B,0x37,0x18, +0x91,0x24,0x10,0x81,0x34,0x20,0x05,0x32,0x82,0x53,0x20,0x14,0x33,0x31,0x34,0x52, +0x00,0x43,0x32,0x13,0x52,0x22,0x13,0x52,0x11,0x43,0x11,0x32,0x32,0x32,0x22,0x02, +0x13,0x12,0x89,0x22,0x19,0x81,0x81,0x08,0xA8,0x08,0x8B,0x90,0x1B,0xBA,0x8A,0x9B, +0xB9,0x89,0xCA,0xB9,0xAB,0xCA,0x9B,0xCA,0xB9,0xAB,0xDA,0x99,0xAC,0xBB,0x9B,0xAC, +0xAA,0xBA,0xAC,0xAB,0x9A,0xAA,0xAA,0xBA,0xB8,0xA9,0xBA,0x99,0xA9,0x9A,0xA0,0x8A, +0xA9,0x08,0x8A,0xA9,0x00,0x99,0x89,0x88,0x98,0x08,0x99,0x00,0x89,0x80,0x08,0x98, +0x00,0x88,0x88,0x80,0x90,0x80,0x90,0x80,0x81,0x99,0x08,0x88,0x99,0x09,0x00,0x1A, +0xA8,0x10,0x9A,0x88,0x08,0x0A,0x8A,0x89,0x99,0xA8,0x98,0xA9,0x99,0x99,0xA9,0x99, +0xAA,0x8A,0xAA,0x9B,0x8A,0x9A,0xA9,0x9A,0xBA,0x99,0x9A,0xAA,0x99,0x89,0xA9,0x99, +0x98,0x9A,0x98,0x88,0x09,0x89,0x09,0x08,0x08,0x09,0x18,0x18,0x00,0x12,0x00,0x11, +0x11,0x11,0x12,0x12,0x21,0x21,0x22,0x22,0x22,0x22,0x22,0x22,0x32,0x31,0x32,0x31, +0x32,0x32,0x21,0x31,0x21,0x32,0x21,0x12,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + +/* Source: 02SD.ROM */ +/* Length: 640 / 0x00000280 */ + +0x0A,0xDC,0x14,0x0B,0xBA,0xBC,0x01,0x0F,0xF5,0x2F,0x87,0x19,0xC9,0x24,0x1B,0xA1, +0x31,0x99,0x90,0x32,0x32,0xFE,0x83,0x48,0xA8,0xA9,0x23,0x19,0xBC,0x91,0x02,0x41, +0xDE,0x81,0x28,0xA8,0x0A,0xB1,0x72,0xDA,0x23,0xBC,0x04,0x19,0xB8,0x21,0x8A,0x03, +0x29,0xBA,0x14,0x21,0x0B,0xC0,0x43,0x08,0x91,0x50,0x93,0x0F,0x86,0x1A,0x91,0x18, +0x21,0xCB,0x27,0x0A,0xA1,0x42,0x8C,0xA9,0x21,0x10,0x08,0xAB,0x94,0x2A,0xDA,0x02, +0x8B,0x91,0x09,0x98,0xAE,0x80,0xA9,0x02,0x0A,0xE9,0x21,0xBB,0x15,0x20,0xBE,0x92, +0x42,0x09,0xA9,0x11,0x34,0x08,0x12,0x0A,0x27,0x29,0xA1,0x52,0x12,0x8E,0x92,0x28, +0x92,0x2B,0xD1,0x23,0xBF,0x81,0x10,0x99,0xA8,0x0A,0xC4,0x3B,0xB9,0xB0,0x00,0x62, +0xCF,0x92,0x29,0x92,0x2B,0xB1,0x1C,0xB2,0x72,0xAA,0x88,0x11,0x18,0x80,0x13,0x9E, +0x03,0x18,0xB0,0x60,0xA1,0x28,0x88,0x08,0x04,0x10,0x8F,0x96,0x19,0x90,0x01,0x09, +0xC8,0x50,0x91,0x8A,0x01,0xAB,0x03,0x50,0xBA,0x9D,0x93,0x68,0xBA,0x80,0x22,0xCB, +0x41,0xBC,0x92,0x60,0xB9,0x1A,0x95,0x4A,0xC8,0x20,0x88,0x33,0xAC,0x92,0x38,0x83, +0x09,0x80,0x16,0x09,0x29,0xD0,0x54,0x8C,0xA2,0x28,0x91,0x89,0x93,0x60,0xCD,0x85, +0x1B,0xA1,0x49,0x90,0x8A,0x80,0x34,0x0C,0xC9,0x14,0x19,0x98,0xA0,0x40,0xA9,0x21, +0xD9,0x34,0x0A,0xA9,0x10,0x23,0xCB,0x25,0xAA,0x25,0x9B,0x13,0xCD,0x16,0x09,0xA0, +0x80,0x01,0x19,0x90,0x88,0x21,0xAC,0x33,0x8B,0xD8,0x27,0x3B,0xB8,0x81,0x31,0x80, +0xAF,0x97,0x0A,0x82,0x0A,0xA0,0x21,0x89,0x8A,0xA2,0x32,0x8D,0xBB,0x87,0x19,0x21, +0xC9,0xBC,0x45,0x09,0x90,0x09,0xA1,0x24,0x1A,0xD0,0x10,0x08,0x11,0xA9,0x21,0xE8, +0x60,0xA9,0x14,0x0C,0xD1,0x32,0xAB,0x04,0x0C,0x81,0x90,0x29,0x83,0x9B,0x01,0x8F, +0x97,0x0B,0x82,0x18,0x88,0xBA,0x06,0x39,0xC8,0x23,0xBC,0x04,0x09,0x92,0x08,0x1A, +0xBB,0x74,0x8C,0x81,0x18,0x81,0x9D,0x83,0x41,0xCD,0x81,0x40,0x9A,0x90,0x10,0x12, +0x9C,0xA1,0x68,0xD8,0x33,0x9C,0x91,0x01,0x12,0xBE,0x02,0x09,0x12,0x99,0x9A,0x36, +0x0A,0xB0,0x30,0x88,0xA3,0x2D,0x12,0xBC,0x03,0x3A,0x11,0xBD,0x08,0xC8,0x62,0x80, +0x8B,0xD8,0x23,0x38,0xF9,0x12,0x08,0x99,0x91,0x21,0x99,0x85,0x2F,0xB2,0x30,0x90, +0x88,0xD9,0x53,0xAC,0x82,0x19,0x91,0x20,0xCC,0x96,0x29,0xC9,0x24,0x89,0x80,0x99, +0x12,0x08,0x18,0x88,0x99,0x23,0xAB,0x73,0xCB,0x33,0x9F,0x04,0x2B,0xB1,0x08,0x03, +0x1B,0xC9,0x21,0x32,0xFA,0x33,0xDB,0x02,0x33,0xAE,0xB9,0x54,0x8B,0xA1,0x20,0x89, +0x90,0x11,0x88,0x09,0x98,0x23,0xBE,0x37,0x8D,0x81,0x20,0xAA,0x34,0xBB,0x13,0x18, +0xB9,0x40,0xB1,0x18,0x83,0x8E,0xB2,0x72,0xBC,0x82,0x30,0xA9,0x9A,0x24,0x8B,0x27, +0x0E,0x91,0x20,0x90,0x08,0xB0,0x32,0xB9,0x21,0xB0,0xAC,0x45,0x9A,0xA1,0x50,0xA9, +0x80,0x0A,0x26,0x9B,0x11,0xBB,0x23,0x71,0xCB,0x12,0x10,0xB8,0x40,0xA9,0xA5,0x39, +0xC0,0x30,0xB2,0x20,0xAA,0xBA,0x76,0x1C,0xC1,0x48,0x98,0x80,0x18,0x81,0xAA,0x23, +0x9C,0xA2,0x32,0xAC,0x9A,0x43,0x9C,0x12,0xAD,0x82,0x72,0xBC,0x00,0x82,0x39,0xD1, +0x3A,0xB8,0x35,0x9B,0x10,0x40,0xF9,0x22,0x0A,0xC0,0x51,0xB9,0x82,0x18,0x98,0xA3, +0x79,0xD0,0x20,0x88,0x09,0x01,0x99,0x82,0x11,0x38,0xFC,0x33,0x09,0xC8,0x40,0xA9, +0x11,0x29,0xAA,0x94,0x3A,0xC2,0x4A,0xC0,0x89,0x52,0xBC,0x11,0x08,0x09,0xB8,0x71, +0xA9,0x08,0xA8,0x62,0x8D,0x92,0x10,0x00,0x9E,0x94,0x38,0xBA,0x13,0x88,0x90,0x4A, +0xE2,0x30,0xBA,0x02,0x00,0x19,0xD9,0x62,0xBB,0x04,0x0B,0xA3,0x68,0xB9,0x21,0x88, +0x9D,0x04,0x10,0x8C,0xC8,0x62,0x99,0xAA,0x24,0x1A,0x80,0x9A,0x14,0x9B,0x26,0x8C, +0x92,0x30,0xB9,0x09,0xA3,0x71,0xBB,0x10,0x19,0x82,0x39,0xDB,0x02,0x44,0x9F,0x10, + +/* Source: 04TOP.ROM */ +/* Length: 5952 / 0x00001740 */ + +0x07,0xFF,0x7C,0x3C,0x31,0xC6,0xC4,0xBB,0x7F,0x7F,0x7B,0x82,0x8A,0x4D,0x5F,0x7C, +0x3E,0x44,0xD2,0xB3,0xA0,0x19,0x1B,0x6C,0x81,0x28,0xC4,0xA1,0x1C,0x4B,0x18,0x00, +0x2A,0xA2,0x0A,0x7C,0x2A,0x00,0x01,0x89,0x98,0x48,0x8A,0x3C,0x28,0x2A,0x5B,0x3E, +0x3A,0x1A,0x3B,0x3D,0x4B,0x3B,0x4A,0x08,0x2A,0x1A,0x2C,0x4A,0x3B,0x82,0x99,0x3C, +0x5D,0x29,0x2B,0x39,0x0B,0x23,0xAB,0x1A,0x4C,0x79,0xA3,0x01,0xC1,0x2A,0x0A,0x38, +0xA7,0xB9,0x12,0x1F,0x29,0x08,0x82,0xA1,0x08,0xA9,0x42,0xAA,0x95,0xB3,0x90,0x81, +0x09,0xD4,0x1A,0x80,0x1B,0x07,0xB8,0x12,0x8E,0x49,0x81,0x92,0xD3,0x90,0xA1,0x2A, +0x02,0xE1,0xA3,0x99,0x02,0xB3,0x94,0xB3,0xB0,0xF4,0x98,0x93,0x90,0x13,0xE1,0x81, +0x99,0x38,0x91,0xA6,0xD3,0x99,0x94,0xC1,0x83,0xB1,0x92,0x98,0x49,0xC4,0xB2,0xA4, +0xA3,0xD0,0x1A,0x30,0xBA,0x59,0x02,0xD4,0xA0,0xA4,0xA2,0x8A,0x01,0x00,0xB7,0xA8, +0x18,0x2A,0x2B,0x1E,0x23,0xC8,0x1A,0x00,0x39,0xA0,0x18,0x92,0x4F,0x2D,0x5A,0x10, +0x89,0x81,0x2A,0x8B,0x6A,0x02,0x09,0xB3,0x8D,0x48,0x1B,0x80,0x19,0x34,0xF8,0x29, +0x0A,0x7B,0x2A,0x28,0x81,0x0C,0x02,0x1E,0x29,0x09,0x12,0xC2,0x94,0xE1,0x18,0x98, +0x02,0xC4,0x89,0x91,0x1A,0x20,0xA9,0x02,0x1B,0x48,0x8E,0x20,0x88,0x2D,0x08,0x59, +0x1B,0x02,0xA3,0xB1,0x8A,0x1E,0x58,0x80,0xC2,0xB6,0x88,0x91,0x88,0x11,0xA1,0xA3, +0xE2,0x01,0xB0,0x19,0x11,0x09,0xF4,0x88,0x09,0x88,0x19,0x89,0x12,0xF1,0x2A,0x28, +0x8C,0x25,0x99,0xA4,0x98,0x39,0xA1,0x00,0xD0,0x58,0xAA,0x59,0x01,0x0C,0x00,0x2B, +0x00,0x08,0x89,0x6B,0x69,0x90,0x01,0x90,0x98,0x12,0xB3,0xF3,0xA0,0x89,0x02,0x3B, +0x0C,0x50,0xA9,0x4E,0x6B,0x19,0x28,0x09,0xA2,0x08,0x2F,0x20,0x88,0x92,0x8A,0x11, +0xC4,0x93,0xF1,0x18,0x88,0x11,0xF2,0x80,0x92,0xA8,0x02,0xA8,0xB7,0xB3,0xA3,0xA0, +0x88,0x1A,0x40,0xE2,0x91,0x19,0x88,0x18,0x91,0x83,0xC1,0xB5,0x92,0xA9,0xC6,0x90, +0x01,0xC2,0x81,0x98,0x03,0xF0,0x00,0x2C,0x2A,0x92,0x2C,0x83,0x1F,0x3A,0x29,0x00, +0xB8,0x70,0xAB,0x69,0x18,0x89,0x10,0x0D,0x12,0x0B,0x88,0x4A,0x3A,0x9B,0x70,0xA8, +0x28,0x2F,0x2A,0x3A,0x1B,0x85,0x88,0x8B,0x6A,0x29,0x00,0x91,0x91,0x1B,0x7C,0x29, +0x01,0x88,0x90,0x19,0x2B,0x2B,0x00,0x39,0xA8,0x5E,0x21,0x89,0x91,0x09,0x3A,0x6F, +0x2A,0x18,0x18,0x8B,0x50,0x89,0x2B,0x19,0x49,0x88,0x29,0xF5,0x89,0x08,0x09,0x12, +0xAA,0x15,0xB0,0x82,0xAC,0x38,0x00,0x3F,0x81,0x10,0xB0,0x49,0xA2,0x81,0x3A,0xC8, +0x87,0x90,0xC4,0xA3,0x99,0x19,0x83,0xE1,0x84,0xE2,0xA2,0x90,0x80,0x93,0xB5,0xC4, +0xB3,0xA1,0x0A,0x18,0x92,0xC4,0xA0,0x93,0x0C,0x3A,0x18,0x01,0x1E,0x20,0xB1,0x82, +0x8C,0x03,0xB5,0x2E,0x82,0x19,0xB2,0x1B,0x1B,0x6B,0x4C,0x19,0x12,0x8B,0x5A,0x11, +0x0C,0x3A,0x2C,0x18,0x3D,0x08,0x2A,0x5C,0x18,0x00,0x88,0x3D,0x29,0x80,0x2A,0x09, +0x00,0x7A,0x0A,0x10,0x0B,0x69,0x98,0x10,0x81,0x3F,0x00,0x18,0x19,0x91,0xB7,0x9A, +0x28,0x8A,0x48,0x92,0xF3,0xA2,0x88,0x98,0x87,0xA1,0x88,0x80,0x81,0x95,0xD1,0xA3, +0x1B,0x1C,0x39,0x10,0xA1,0x2A,0x0B,0x7A,0x4B,0x80,0x13,0xC1,0xD1,0x2B,0x2A,0x85, +0xB2,0xA2,0x93,0xB2,0xD3,0x80,0xD1,0x18,0x08,0x08,0xB7,0x98,0x81,0x3F,0x01,0x88, +0x01,0xE2,0x00,0x9A,0x59,0x08,0x10,0xC3,0x99,0x84,0xA9,0xA5,0x91,0x91,0x91,0x80, +0xB5,0x94,0xC0,0x01,0x98,0x09,0x84,0xB0,0x80,0x7A,0x08,0x18,0x90,0xA8,0x6A,0x1C, +0x39,0x2A,0xB7,0x98,0x19,0x10,0x2A,0xA1,0x10,0xBD,0x39,0x18,0x2D,0x39,0x3F,0x10, +0x3F,0x01,0x09,0x19,0x0A,0x38,0x8C,0x40,0xB3,0xB4,0x93,0xAD,0x20,0x2B,0xD4,0x81, +0xC3,0xB0,0x39,0xA0,0x23,0xD8,0x04,0xB1,0x9B,0xA7,0x1A,0x92,0x08,0xA5,0x88,0x81, +0xE2,0x01,0xB8,0x01,0x81,0xC1,0xC7,0x90,0x92,0x80,0xA1,0x97,0xA0,0xA2,0x82,0xB8, +0x18,0x00,0x9C,0x78,0x98,0x83,0x0B,0x0B,0x32,0x7D,0x19,0x10,0xA1,0x19,0x09,0x0A, +0x78,0xA8,0x10,0x1B,0x29,0x29,0x1A,0x14,0x2F,0x88,0x4A,0x1B,0x10,0x10,0xAB,0x79, +0x0D,0x49,0x18,0xA0,0x02,0x1F,0x19,0x3A,0x2B,0x11,0x8A,0x88,0x79,0x8A,0x20,0x49, +0x9B,0x58,0x0B,0x28,0x18,0xA9,0x3A,0x7D,0x00,0x29,0x88,0x82,0x3D,0x1A,0x38,0xBA, +0x15,0x09,0xAA,0x51,0x8B,0x83,0x3C,0x8A,0x58,0x1B,0xB5,0x01,0xBB,0x50,0x19,0x99, +0x24,0xCA,0x21,0x1B,0xA2,0x87,0xA8,0xB1,0x68,0xA1,0xA6,0xA2,0xA8,0x29,0x8B,0x24, +0xB4,0xE2,0x92,0x8A,0x00,0x19,0x93,0xB5,0xB4,0xB1,0x81,0xB1,0x03,0x9A,0x82,0xA7, +0x90,0xD6,0xA0,0x80,0x1B,0x29,0x01,0xA4,0xE1,0x18,0x0A,0x2A,0x29,0x92,0xC7,0xA8, +0x81,0x19,0x89,0x30,0x10,0xE0,0x30,0xB8,0x10,0x0C,0x1A,0x79,0x1B,0xA7,0x80,0xA0, +0x00,0x0B,0x28,0x18,0xB1,0x85,0x1E,0x00,0x20,0xA9,0x18,0x18,0x1C,0x13,0xBC,0x15, +0x99,0x2E,0x12,0x00,0xE1,0x00,0x0B,0x3B,0x21,0x90,0x06,0xC9,0x2A,0x49,0x0A,0x18, +0x20,0xD1,0x3C,0x08,0x00,0x83,0xC9,0x41,0x8E,0x18,0x08,0x02,0xA0,0x09,0xA4,0x7B, +0x90,0x19,0x2A,0x10,0x2A,0xA8,0x71,0xBA,0x10,0x4A,0x0E,0x22,0xB2,0xB2,0x1B,0x8C, +0x78,0x1A,0xB5,0x93,0xA9,0x1B,0x49,0x19,0x29,0xA3,0xC6,0x88,0xAA,0x32,0x0D,0x1B, +0x22,0x08,0xC2,0x18,0xB9,0x79,0x3F,0x01,0x10,0xA9,0x84,0x1C,0x09,0x21,0xB0,0xA7, +0x0A,0x99,0x50,0x0C,0x81,0x28,0x8B,0x48,0x2E,0x00,0x08,0x99,0x38,0x5B,0x88,0x14, +0xA9,0x08,0x11,0xAA,0x72,0xC1,0xB3,0x09,0x8A,0x05,0x91,0xF2,0x81,0xA1,0x09,0x02, +0xF2,0x92,0x99,0x1A,0x49,0x80,0xC5,0x90,0x90,0x18,0x09,0x12,0xA1,0xF2,0x81,0x98, +0xC6,0x91,0xA0,0x11,0xA0,0x94,0xB4,0xF2,0x81,0x8B,0x03,0x80,0xD2,0x93,0xA8,0x88, +0x69,0xA0,0x03,0xB8,0x88,0x32,0xBC,0x97,0x80,0xB1,0x3B,0x1A,0xA6,0x00,0xD1,0x01, +0x0B,0x3B,0x30,0x9B,0x31,0x3E,0x92,0x19,0x8A,0xD3,0x5C,0x1B,0x41,0xA0,0x93,0xA2, +0xAF,0x39,0x4C,0x01,0x92,0xA8,0x81,0x3C,0x0D,0x78,0x98,0x00,0x19,0x0A,0x20,0x2D, +0x29,0x3C,0x1B,0x48,0x88,0x99,0x7A,0x2D,0x29,0x2A,0x82,0x80,0xA8,0x49,0x3E,0x19, +0x11,0x98,0x82,0x9A,0x3B,0x28,0x2F,0x20,0x4C,0x90,0x29,0x19,0x9A,0x7A,0x29,0x28, +0x98,0x88,0x33,0xCD,0x11,0x3A,0xC1,0xA4,0xA0,0xC4,0x82,0xC8,0x50,0x98,0xB2,0x21, +0xC0,0xB6,0x98,0x82,0x80,0x9C,0x23,0x00,0xF8,0x30,0xA8,0x1A,0x68,0xA8,0x86,0x9A, +0x01,0x2A,0x0A,0x97,0x91,0xC1,0x18,0x89,0x02,0x83,0xE0,0x01,0x8B,0x29,0x30,0xE2, +0x91,0x0B,0x18,0x3B,0x1C,0x11,0x28,0xAC,0x78,0x80,0x93,0x91,0xA9,0x49,0x8B,0x87, +0x90,0x99,0x3D,0x5A,0x81,0x08,0xA1,0x11,0x2F,0x1A,0x21,0x9B,0x15,0xA2,0xB0,0x11, +0xC0,0x91,0x5B,0x98,0x24,0xA2,0xF2,0x92,0x8B,0x6A,0x18,0x81,0xB5,0xB1,0x88,0x4C, +0x00,0x00,0xA4,0xC1,0x2B,0x1A,0x59,0x0A,0x02,0x80,0x1E,0x02,0x08,0xB3,0x80,0x9A, +0x23,0xB8,0xF2,0x84,0xAB,0x01,0x48,0x90,0xA7,0x90,0x0A,0x29,0x09,0x95,0x99,0xA0, +0x59,0x2B,0x00,0x97,0xB0,0x29,0x89,0x2A,0x03,0xD0,0xB7,0x1B,0x81,0x00,0xA6,0xB1, +0x90,0x09,0x48,0xC0,0x11,0x00,0x8A,0x00,0x5B,0x83,0x9A,0x18,0x2F,0x3C,0x18,0x11, +0xA9,0x04,0x1A,0x4F,0x01,0x98,0x81,0x09,0x09,0x4A,0x18,0xB4,0xA2,0x0B,0x59,0x90, +0x3B,0x49,0xBC,0x40,0x6A,0x88,0x3A,0x08,0x3E,0x3A,0x80,0x93,0xB0,0xE1,0x5A,0x00, +0xA4,0xB3,0xE3,0x90,0x0D,0x38,0x09,0x82,0xC4,0xA1,0xB1,0x4C,0x18,0x10,0x91,0xB2, +0x13,0xEA,0x34,0x99,0x88,0xA6,0x89,0x92,0x91,0xC1,0x20,0xB2,0xC2,0x86,0xD2,0xB3, +0x80,0xB2,0x08,0x09,0x87,0x91,0xC0,0x11,0x89,0x90,0x28,0xB9,0x79,0x19,0xA4,0x82, +0xD0,0x03,0x0C,0xA3,0xA5,0xB2,0xB2,0x1B,0x29,0x13,0xF1,0xB4,0x81,0x9D,0x38,0x00, +0xC4,0xA1,0x89,0x59,0x1A,0x81,0xA4,0xA9,0x1C,0x6A,0x19,0x02,0xB1,0x1A,0x4A,0x0B, +0x78,0x89,0x81,0x1C,0x2A,0x29,0x4A,0xA3,0x3E,0x1C,0x49,0x1A,0x08,0x21,0xAE,0x28, +0x4B,0x19,0x20,0x8C,0x10,0x3A,0xAB,0x26,0x8B,0x18,0x59,0x99,0x13,0xA2,0xAB,0x79, +0x2F,0x18,0x10,0xB2,0x80,0x1B,0x4D,0x5A,0x80,0x82,0x98,0x81,0x80,0x09,0xA5,0x90, +0x91,0x03,0xC2,0xE2,0x81,0xA8,0x82,0x09,0xC6,0xA3,0xB1,0x08,0x5B,0x08,0x05,0xD1, +0xA2,0x89,0x2A,0x28,0x91,0xA6,0x88,0xB0,0x49,0x80,0x09,0x08,0x88,0x07,0xB8,0x05, +0x99,0x81,0x88,0x18,0xE2,0x00,0xC3,0x18,0x0D,0x10,0x30,0xD0,0x93,0x8A,0x09,0x10, +0x2F,0x11,0x90,0xA1,0x20,0x9B,0xB1,0x73,0xC8,0x94,0x98,0x3B,0x01,0x0C,0x30,0x19, +0xF8,0x12,0x90,0xBA,0x78,0x0A,0x11,0x98,0xA0,0x79,0x8A,0x30,0x2B,0xC2,0x11,0x0D, +0x09,0x7A,0x00,0x82,0xB9,0x01,0x7A,0x89,0x21,0x09,0xA1,0x0A,0x7C,0x10,0x88,0xB5, +0x88,0x0A,0x2B,0x69,0x1A,0x10,0xA0,0x5B,0x19,0x1A,0x10,0x19,0x1A,0x6C,0x20,0x90, +0xA5,0x98,0x1B,0x0A,0x69,0x82,0xD1,0x18,0x09,0x19,0x2A,0x93,0xD4,0x9A,0x01,0x49, +0xA2,0xA2,0x82,0xD8,0x22,0xAA,0x97,0xA9,0x2D,0x38,0x2A,0xB6,0x80,0x90,0x0A,0x3C, +0x82,0x94,0xB8,0x21,0x0E,0x2A,0x22,0xB8,0x00,0x4F,0x2B,0x3A,0x81,0xA1,0x29,0x2C, +0x6A,0x13,0xD1,0xA2,0x98,0x28,0x0C,0x01,0xD5,0x08,0xA9,0x31,0xB3,0xB0,0xA7,0xB0, +0x29,0x1B,0x87,0xA2,0xA1,0xB2,0x4A,0x89,0x11,0xC3,0xF3,0x98,0x08,0x03,0xA0,0xA3, +0xC5,0x90,0xB3,0xB5,0xB4,0xB8,0x02,0x91,0x91,0xD3,0xA4,0xC1,0x1B,0x82,0x28,0xA4, +0xD1,0x94,0x8A,0x28,0x08,0x03,0xE0,0x80,0xD4,0x90,0x91,0xA1,0x3B,0x3D,0x02,0xE4, +0xA1,0x92,0x89,0x1A,0x4B,0x95,0xB3,0x90,0x99,0x6A,0x0A,0x30,0xA1,0x93,0xA6,0xA9, +0x85,0x8B,0x82,0x10,0xB1,0xA3,0x94,0xF8,0x38,0x9A,0x30,0x1A,0x8B,0xA7,0x89,0x01, +0x5B,0x19,0x18,0x11,0xF0,0x18,0x1C,0x39,0x19,0x0C,0x12,0x1C,0x2A,0x7B,0x3A,0x88, +0x2B,0x18,0x2B,0x5C,0x20,0x92,0x8D,0x38,0x8A,0x3A,0x5B,0x2E,0x3A,0x2B,0x10,0x12, +0xBB,0x6A,0x4D,0x18,0x10,0xB1,0x81,0x2A,0x8B,0x79,0x80,0x01,0x0A,0x09,0x5B,0x2D, +0x84,0x8A,0x08,0x02,0xA2,0x91,0x82,0xE8,0x50,0x9B,0x85,0xA3,0xB0,0xA3,0x1B,0x02, +0x18,0xF3,0xA2,0x88,0xAB,0x53,0xD1,0xB4,0xA3,0x09,0x09,0x18,0xD4,0x08,0xB0,0x09, +0x58,0xD1,0x82,0x89,0x81,0x1A,0x18,0x05,0xB9,0xC3,0x30,0xC0,0x95,0x80,0xC3,0x89, +0x89,0x13,0x88,0xF2,0x93,0x0E,0x18,0x01,0x92,0xA5,0xB8,0x2A,0x39,0xAA,0x33,0x9A, +0xB1,0x11,0xF5,0xA1,0xA1,0x0A,0x50,0xB8,0x03,0xC4,0xA0,0x4E,0x29,0x10,0x88,0xC2, +0x1A,0x39,0x1D,0x28,0x98,0x94,0x0E,0x10,0x2A,0x3C,0x02,0x2D,0x1B,0x4B,0x3B,0x49, +0x19,0xA9,0x48,0x2F,0x29,0x10,0x89,0x02,0x0C,0x10,0x09,0xB9,0x70,0x1B,0x8A,0x50, +0xA8,0x2B,0x49,0x89,0x69,0x88,0x95,0x89,0x90,0x92,0x4C,0x19,0x82,0xC1,0x01,0x80, +0xA0,0x2B,0x7A,0x81,0x10,0xC2,0xB7,0x98,0x88,0x19,0x2C,0x03,0xB1,0xA4,0xA1,0x0C, +0x3B,0x78,0x88,0x85,0xB1,0xA0,0x1B,0x3A,0x4A,0x08,0x94,0x81,0xF1,0x80,0x00,0x0C, +0x59,0x09,0x18,0x90,0xA6,0x92,0x8C,0x1A,0x79,0x92,0xA8,0x00,0x81,0x2E,0x2A,0x13, +0xA2,0xB0,0xA5,0x88,0x88,0x89,0x11,0x19,0xA0,0xF3,0x82,0xB0,0x83,0x5F,0x2A,0x01, +0xA1,0x94,0xB0,0x09,0x78,0x98,0xA3,0xA6,0xA0,0x91,0x80,0x93,0x98,0xC1,0x12,0x18, +0xC9,0x17,0xA0,0xA0,0x1A,0x21,0x80,0x99,0xD4,0x30,0x9D,0x00,0x10,0x2F,0x08,0x1C, +0x21,0x08,0xB4,0xC3,0x2B,0xA9,0x52,0xD2,0xA3,0xD1,0x09,0x10,0x8B,0x24,0x92,0xD1, +0x80,0x19,0xA0,0x2C,0x12,0x49,0xAA,0xB6,0x95,0xB8,0x08,0x3A,0x2B,0x01,0xF3,0xB3, +0x0B,0x09,0x79,0x18,0xA2,0xA4,0xA0,0x18,0x0C,0x20,0x08,0xA9,0x16,0x0C,0x00,0x1B, +0x08,0x2B,0x7B,0x01,0x01,0xB9,0x59,0x19,0x8B,0x45,0xA8,0x80,0x0C,0x1A,0x41,0x1E, +0x00,0x28,0xA8,0x5A,0x00,0xC1,0x49,0x99,0x21,0x1D,0x08,0x85,0x99,0x95,0x89,0x90, +0x11,0x90,0xD1,0x28,0xB2,0xA7,0x99,0x81,0x02,0xAC,0x13,0x81,0xB2,0xA6,0xA9,0x28, +0x1C,0xB1,0x33,0xD1,0xC1,0x58,0xA8,0x14,0xB0,0xB7,0x91,0xA0,0x82,0x89,0xC2,0x28, +0xA1,0xB2,0x49,0xD2,0x94,0xC8,0x12,0x80,0x99,0x85,0x08,0xD3,0x09,0xA2,0xB3,0x1E, +0x08,0x21,0xB9,0x23,0xB4,0xAB,0x41,0xAC,0x87,0x09,0xA2,0xC5,0x0B,0x2A,0x5A,0x91, +0x20,0x9A,0x89,0x78,0x9B,0x31,0x89,0x80,0x29,0x0A,0xB7,0x3C,0x98,0x48,0x1D,0x00, +0x01,0xB0,0x20,0x2F,0x29,0x4A,0x89,0x94,0x1C,0x88,0x28,0x2B,0x10,0x88,0x9A,0x71, +0x9A,0x08,0x4A,0x2F,0x18,0x2B,0x18,0x02,0xA8,0x4B,0x7A,0x99,0x48,0x80,0xA8,0x20, +0x1D,0x40,0xA8,0x10,0x08,0xA8,0xC5,0x88,0xC2,0x18,0x88,0x2A,0x12,0xF3,0x82,0xD8, +0x20,0x0A,0x09,0xA6,0x98,0x04,0xB9,0x11,0x18,0xC3,0xE1,0x29,0xA1,0x11,0xC1,0x03, +0xE2,0x9A,0x33,0xA9,0xB5,0x98,0x92,0xA1,0x02,0xF8,0x21,0xA8,0x10,0x02,0xC1,0xB7, +0x1B,0x90,0x5B,0x3C,0x83,0x93,0xE0,0x19,0x1A,0x11,0x11,0xF1,0x92,0x89,0x19,0x2C, +0x2C,0x41,0x99,0x92,0x90,0x3F,0x18,0x4B,0x00,0x08,0xD2,0x01,0xB2,0xAA,0x78,0x09, +0x01,0x91,0xA2,0x98,0x2F,0x3A,0x2C,0x01,0x00,0x93,0xE0,0x28,0x2C,0x2B,0x01,0x12, +0xE1,0x80,0xB3,0x3D,0x3A,0x0A,0x50,0x98,0xC2,0xA0,0x11,0xAA,0x30,0x87,0x90,0xC2, +0x29,0x88,0x38,0xC8,0xB5,0x90,0xBA,0x70,0x1A,0x02,0x94,0xD0,0x80,0x1A,0x82,0xA6, +0xB0,0x91,0x18,0xB3,0x00,0x13,0xF1,0xA2,0xC1,0x82,0xB0,0x00,0x15,0x0B,0xD3,0x02, +0xA8,0x91,0x2B,0x1F,0x49,0x88,0xA6,0x80,0x88,0x08,0x1B,0xA5,0x80,0xB9,0x06,0x0B, +0x90,0x21,0x9D,0x48,0x18,0xA0,0x15,0xC9,0x82,0x2B,0x1A,0x42,0x9A,0xC4,0x39,0xBC, +0x69,0x00,0xA0,0x29,0x8C,0x39,0x59,0x08,0x09,0x49,0xA9,0x6B,0x81,0x00,0x98,0xB0, +0x68,0x3D,0x81,0x88,0x18,0x19,0x1D,0x12,0x80,0xB2,0x3A,0x3F,0x85,0x92,0xD0,0x00, +0x0A,0x19,0x12,0xF1,0x02,0x9B,0x19,0x40,0xB9,0x11,0x02,0xF2,0x1A,0x08,0x94,0x0A, +0xC2,0x83,0x0B,0xB4,0xA4,0xC0,0x32,0xD8,0x86,0x98,0x90,0x95,0x89,0xA3,0x83,0xC2, +0x92,0xE1,0x92,0x82,0xD9,0x03,0x08,0xA9,0x85,0x92,0xA2,0x80,0xE0,0x30,0x8B,0xB3, +0x87,0x89,0x90,0x83,0xA0,0x08,0x92,0x93,0x3E,0xAB,0x43,0x89,0xE3,0x80,0x83,0x2F, +0x00,0xA3,0x80,0xC9,0x22,0x3F,0x08,0x81,0x0B,0x33,0x9A,0xA3,0x7B,0x0C,0x29,0x4A, +0x1B,0x21,0xAA,0x70,0x1B,0x0D,0x48,0x1A,0x81,0x88,0xB1,0x39,0x3F,0x08,0x58,0xA0, +0x81,0x1A,0x1A,0x2B,0x6D,0x11,0x0A,0x91,0x01,0x1A,0x98,0x5A,0x0C,0x03,0xB1,0x84, +0xA3,0xAD,0x58,0x2A,0xA1,0x84,0xB1,0xA0,0x5C,0x2B,0x13,0xA8,0x95,0x83,0xE8,0x10, +0x81,0xB0,0x00,0xC2,0x96,0xA0,0x91,0x00,0x2C,0x90,0x30,0xF2,0x80,0xA8,0x39,0x21, +0xC1,0x03,0xAC,0x39,0x7C,0x29,0x91,0x1A,0x00,0x19,0x2C,0x3A,0x93,0xB0,0x29,0x8F, +0x28,0x02,0x93,0xF3,0xA9,0x01,0x03,0xE0,0x08,0x09,0x1D,0x58,0xA1,0x83,0xA9,0x6B, +0x2A,0x3C,0x21,0x89,0xC2,0x2C,0x4B,0x8A,0x50,0x81,0x98,0xA8,0x32,0x0C,0x8E,0x24, +0x0B,0x1A,0x81,0x92,0xA1,0x4F,0x18,0x3A,0x0A,0xB4,0x18,0x2E,0x39,0x82,0x19,0xD3, +0xD0,0x28,0x1B,0x11,0x98,0x07,0xAA,0x28,0x00,0x88,0xB4,0x89,0x1B,0x1F,0x22,0x00, +0xB3,0xC9,0x33,0xAB,0x2B,0xB5,0x48,0x98,0x98,0xA7,0x10,0xD2,0xC1,0x23,0xCA,0x93, +0xC6,0x80,0xA1,0x88,0x02,0x89,0xE2,0x09,0x38,0xBA,0x40,0x89,0x21,0xD8,0x49,0x10, +0x8D,0x02,0x90,0xC3,0x9A,0x24,0x89,0x08,0x84,0xA5,0x9C,0x10,0x11,0x9C,0x88,0x30, +0x3C,0xA1,0x94,0x58,0x8C,0x0B,0x69,0x29,0x9A,0x81,0x12,0x2B,0x8B,0x79,0x94,0xB0, +0xC1,0x84,0xC2,0x99,0x25,0x99,0x11,0xA2,0x93,0xE4,0x99,0x80,0x0A,0x00,0x10,0xB7, +0xB0,0x31,0xBA,0x3C,0x21,0xB3,0xF1,0x18,0xA0,0x2A,0x20,0xA3,0x06,0xE8,0x28,0xA1, +0xB4,0x08,0x0B,0x11,0x4B,0xB7,0x90,0xA5,0x98,0x3D,0x19,0x02,0xA1,0xC4,0xB2,0x19, +0x28,0xC0,0xA5,0x92,0xB1,0xA3,0x0A,0x0A,0x08,0x2B,0x70,0xC4,0xB3,0x00,0xBC,0x4B, +0x39,0x12,0xE3,0xA0,0x00,0x3F,0x18,0x29,0x94,0xD1,0x19,0x09,0x00,0xA1,0x83,0x99, +0x9B,0x35,0x80,0xC4,0xB1,0x6A,0x1A,0x1C,0x29,0x38,0x0E,0x19,0x5A,0x1A,0x82,0x8A, +0x59,0x2A,0x2E,0x20,0x88,0xA8,0x3A,0x38,0x3D,0x00,0xB3,0x29,0xAD,0x49,0x10,0x0C, +0x01,0x01,0xA3,0x8F,0x85,0x09,0x1B,0x88,0x10,0xA3,0xD2,0x90,0x3C,0x5C,0x39,0x03, +0xD1,0xA0,0x00,0x2A,0x0B,0x04,0xA7,0x90,0xA0,0x11,0x90,0x99,0x83,0xB4,0xB1,0xF1, +0x84,0x88,0x90,0x18,0x18,0xD3,0xD2,0xB3,0xA0,0x1A,0x21,0xA7,0xB2,0xB3,0x92,0x9A, +0x22,0xB9,0x28,0x38,0xBD,0x87,0x2A,0xB1,0x13,0x0D,0x0A,0x38,0xC9,0x24,0xC0,0x19, +0x23,0x0F,0x01,0x88,0xC0,0x2A,0x82,0x18,0x28,0xF0,0x18,0x2A,0x29,0x4B,0x35,0xB8, +0xA3,0x9D,0x18,0x1B,0x40,0x00,0x9A,0x5C,0x3A,0x09,0x2F,0x38,0x8A,0x3B,0x3B,0x11, +0x5C,0x19,0x2B,0x4A,0x08,0x0A,0x3D,0x20,0x4F,0x3A,0x19,0x2A,0x18,0x4D,0x1B,0x3A, +0x11,0x0D,0x3A,0x3C,0x4B,0x93,0x81,0xAA,0x6B,0x4A,0x18,0x00,0xC3,0xC3,0x9A,0x59, +0x2A,0x1B,0xA7,0xA1,0x81,0x88,0x88,0x58,0xB2,0xB1,0x2B,0x83,0xD4,0x81,0x08,0x0F, +0x00,0x20,0xC2,0xE2,0x80,0x08,0x1C,0x29,0x04,0xB1,0xA2,0x01,0x1C,0x91,0x00,0x0C, +0x49,0xB0,0x43,0xF2,0x99,0x39,0x3F,0x00,0x81,0x94,0xC1,0x09,0x1A,0x69,0x90,0x80, +0x94,0xAA,0x20,0x2A,0x91,0xB1,0x39,0x7A,0x38,0xD1,0x10,0x8A,0x8C,0x5A,0x01,0xB5, +0x98,0x80,0x2A,0x0B,0x32,0x92,0xF1,0x81,0x9A,0x23,0x8A,0xA3,0xB7,0x09,0x03,0x08, +0xD0,0x94,0x9A,0x09,0x01,0x93,0xB7,0xC2,0x8C,0x3A,0x83,0x99,0x05,0xA0,0x0B,0x29, +0x93,0xE5,0x80,0x89,0x38,0x90,0x8A,0xD7,0xA1,0x19,0x1B,0x48,0x98,0x92,0xC3,0xA1, +0x09,0x3F,0x02,0x0C,0x22,0xC3,0xB2,0xA1,0x01,0x9F,0x4A,0x01,0xA3,0xD3,0xB0,0x28, +0x3F,0x29,0x20,0xA2,0xC2,0xB1,0x08,0x5A,0x98,0x13,0xD2,0xC1,0x01,0xB2,0x80,0x3D, +0x03,0xC1,0x89,0x96,0x90,0x90,0x3A,0x1A,0x9A,0x32,0xB6,0xA2,0x8E,0x4A,0x28,0x8A, +0x84,0xA2,0x8A,0x2D,0x49,0x09,0x88,0x18,0x30,0x9D,0x2C,0x23,0xB1,0x0C,0x92,0x2D, +0x39,0x82,0xC4,0x2E,0x10,0x1A,0x10,0xB9,0x48,0x19,0x39,0xBA,0x34,0xDA,0x2D,0x48, +0x1A,0xA6,0x98,0x83,0x9A,0x1D,0x38,0x04,0xD0,0x18,0x90,0x2C,0x11,0x93,0xD3,0x9A, +0x11,0x08,0x82,0xF1,0x01,0xA0,0x2A,0x93,0xD3,0xB4,0xB8,0x82,0x2F,0x11,0xA3,0xB3, +0xA8,0x3B,0x09,0x23,0x96,0xC8,0x3B,0x3F,0x93,0x82,0xA1,0x90,0x3F,0x28,0x81,0xD1, +0x93,0x08,0x2D,0x18,0x91,0xB3,0xB5,0x98,0x2A,0x2B,0x84,0xB1,0x5B,0x8A,0x31,0x18, +0x80,0x8B,0x7E,0x39,0x2B,0x02,0xC1,0x8B,0x6C,0x49,0x09,0x10,0xA1,0x08,0x01,0x0C, +0x20,0xA1,0x09,0x4F,0x18,0x00,0x01,0xA0,0x5C,0x1B,0x5B,0x10,0x92,0x90,0x2B,0x5A, +0x3D,0x18,0x91,0x19,0x98,0x2D,0x39,0x89,0x2D,0x3A,0x48,0x2C,0x11,0xB5,0x9A,0x19, +0x5B,0x28,0x90,0x95,0x98,0x89,0x2B,0x40,0x08,0x90,0xF3,0x0A,0x08,0xA6,0x80,0x91, +0xB2,0xA0,0x02,0xF2,0xA1,0xB7,0x89,0x81,0x82,0x91,0xB1,0x21,0xAB,0x32,0xE9,0x04, +0xA2,0x8D,0x12,0x91,0xA3,0xA3,0xD2,0x8B,0x39,0xD1,0x84,0xE2,0x90,0x00,0x2B,0x29, +0xA3,0xD4,0xA1,0x91,0x1D,0x5A,0x08,0x19,0x11,0x99,0x08,0x18,0x49,0x0F,0x18,0x10, +0x82,0xF1,0x00,0x89,0x2F,0x3A,0x01,0xB3,0xC2,0x81,0x3F,0x29,0x08,0x10,0xA1,0xA1, +0x3B,0x5D,0x19,0x28,0x0B,0x38,0x82,0x91,0x19,0xBD,0x3B,0x7A,0x80,0x12,0xB3,0xE0, +0x0B,0x6A,0x01,0x88,0xA4,0x08,0x0B,0x08,0x59,0x80,0x80,0x1D,0x49,0x89,0x00,0x84, +0x99,0x1A,0x2B,0x32,0xE3,0xB4,0xA9,0x3A,0x99,0x31,0xE3,0xAA,0x58,0x3B,0x88,0x95, +0xC0,0x18,0x4A,0x09,0x30,0xF2,0xA3,0x1C,0x1B,0x49,0x00,0xD3,0xB2,0xA0,0x18,0x11, +0x92,0xD3,0xB2,0x91,0x80,0xE7,0xA1,0x91,0x98,0x19,0x22,0xC2,0xD2,0x18,0x8D,0x3B, +0x10,0xA5,0x91,0x98,0x02,0x3E,0x80,0x01,0x90,0xAA,0x13,0xF1,0x02,0xD1,0x08,0x19, +0x49,0xB4,0x91,0xB4,0x99,0x2A,0x0C,0x32,0xC0,0x05,0x88,0x0B,0x80,0x2C,0x81,0x10, +0x0B,0x51,0xA9,0x19,0x05,0xBF,0x28,0x20,0xE1,0x90,0x80,0x28,0x19,0x08,0x26,0xB1, +0xA1,0x18,0x88,0x2A,0xF0,0x12,0x8A,0xB3,0x14,0x1B,0xD4,0xD8,0x10,0x08,0x8A,0x17, +0xA0,0x98,0x2B,0x3A,0x29,0x48,0xA4,0x99,0x0E,0x4A,0x12,0x8B,0x31,0x8B,0x4E,0x1A, +0x11,0xB5,0x89,0x91,0x29,0x89,0xC2,0x97,0x90,0x0A,0x19,0x11,0x91,0xC1,0xD5,0x08, +0x89,0x20,0x91,0xB1,0x1A,0x2D,0x18,0x29,0xD2,0x3B,0x3E,0x3A,0x2A,0x90,0x82,0x1C, +0x49,0x3B,0x93,0xB6,0xC8,0x4C,0x02,0x91,0x93,0xF2,0x88,0x2D,0x28,0x81,0x82,0xC1, +0x89,0x2D,0x6B,0x19,0x82,0x80,0x18,0x8B,0x39,0x39,0xC8,0x3A,0x6A,0x0A,0x22,0xD2, +0x09,0x2C,0x1A,0x68,0x92,0xE2,0x89,0x2A,0x2A,0x30,0xC2,0xA3,0xB4,0x1D,0x2A,0x09, +0x93,0x18,0xF2,0x89,0x28,0xB3,0x01,0x8F,0x18,0x11,0xA1,0x93,0x90,0xD1,0x7A,0x20, +0xC3,0xA2,0xA8,0x88,0x1D,0x28,0xA5,0xA2,0xA2,0x0B,0x29,0x2B,0x87,0xC1,0x80,0x0A, +0x19,0x01,0x12,0xF1,0x10,0x80,0x0A,0x18,0x08,0x2F,0x4A,0x02,0x89,0x1B,0x29,0x5D, +0x4C,0x08,0x82,0xA1,0x0A,0x3A,0x4B,0x29,0xC6,0xC3,0x09,0x09,0x88,0x39,0x98,0x82, +0xA5,0x1A,0x30,0x11,0xBD,0x3F,0x12,0x8B,0x28,0xC3,0x88,0x3F,0x2B,0x3B,0x48,0xA1, +0x80,0x8A,0x4D,0x39,0x01,0x93,0xA2,0xF1,0x19,0x19,0x0A,0x02,0xB2,0x8B,0x24,0xD2, +0x4B,0x12,0xC8,0x2E,0x10,0xB5,0x89,0x01,0x09,0x1C,0x2A,0x03,0xD4,0x91,0x98,0x99, +0x11,0x2B,0xE4,0x00,0x00,0x01,0xE0,0xA5,0x89,0x99,0x31,0x18,0xD0,0xB7,0x98,0x18, +0x0A,0x10,0x94,0xC2,0x90,0x18,0x00,0x99,0x87,0xA0,0x90,0x2A,0x3C,0x02,0xB8,0xC1, +0x79,0x1A,0x20,0x08,0xA1,0xD2,0x1C,0x29,0x03,0xD1,0x29,0x99,0x2C,0x50,0xB3,0xD1, +0x08,0x09,0x3C,0x10,0x04,0xB2,0x0D,0x2B,0x59,0x80,0x90,0x01,0x0F,0x3A,0x18,0x01, +0xA2,0x9B,0x5B,0x3D,0x81,0x03,0xD2,0x98,0x59,0x90,0x81,0x92,0xB4,0x8B,0x1B,0x40, +0xB2,0xB5,0x08,0x4B,0x01,0x09,0xD1,0x91,0x8B,0x7A,0x10,0xB3,0xC3,0x99,0x49,0x1A, +0x29,0xB5,0xA2,0xAB,0x40,0x81,0x19,0xB7,0xB0,0x20,0x2B,0xD4,0x88,0xA1,0x91,0x3C, +0x82,0x37,0xD3,0xB1,0x8A,0x1B,0x30,0xB3,0xF4,0xA1,0x91,0x09,0x10,0x03,0xD0,0x83, +0xA9,0x8F,0x10,0x01,0x90,0x18,0x80,0x20,0x2B,0xF1,0x28,0x99,0x2A,0x41,0xF0,0x12, +0xAA,0x83,0x82,0xD1,0xC1,0x08,0x89,0x59,0x09,0x83,0x87,0xB0,0x2A,0x4D,0x18,0x09, +0x19,0xB3,0x4B,0x3F,0x39,0x19,0x09,0x01,0x89,0x03,0x1F,0x00,0x1A,0x0B,0x10,0x68, +0xA0,0x18,0x8C,0x6A,0x09,0x08,0x97,0xA1,0x81,0x1B,0x2B,0x4C,0x03,0xB4,0xA8,0x92, +0x4B,0x3C,0xA1,0x81,0x95,0xA8,0x81,0x12,0xBB,0x92,0x45,0xB9,0x93,0xF4,0x88,0x0A, +0x2D,0x28,0x00,0xA3,0xA3,0x8A,0x3F,0x48,0xB1,0x92,0xB4,0xA8,0x30,0x80,0xD3,0x80, +0xD1,0x19,0x3B,0xC4,0x81,0xC1,0x29,0x0D,0x20,0x13,0xC8,0xB4,0x4C,0x09,0x00,0x82, +0xC2,0x3B,0x0D,0x30,0x0B,0x12,0xF0,0x1B,0x20,0x0A,0xA6,0x80,0x0A,0x4A,0x4A,0x80, +0x94,0xB1,0x2E,0x3B,0x1A,0x10,0x93,0x10,0x4C,0x3D,0x08,0x82,0xC9,0x19,0x6A,0x2B, +0x38,0xD1,0x08,0x19,0x2A,0x5A,0x82,0xB1,0x8D,0x29,0x78,0x09,0x82,0x0A,0x2C,0x1B, +0x19,0x41,0xB8,0x8C,0x79,0x2B,0x11,0x88,0x82,0x91,0xDC,0x28,0x11,0xB0,0x11,0x18, +0xC9,0x62,0xA1,0x91,0x98,0x3B,0x3A,0xB0,0xF4,0x01,0xC0,0x29,0x39,0xF8,0x95,0x91, +0x88,0x88,0x91,0x03,0xA1,0xE2,0x18,0x82,0xD1,0xA2,0xD1,0x80,0x19,0x20,0x83,0xB1, +0xE3,0x80,0x91,0x4D,0x1A,0x03,0xB2,0x09,0x18,0xD1,0x19,0x09,0x92,0xA6,0xA0,0xB6, +0xB2,0x8B,0x38,0x10,0x42,0xD3,0xD0,0xA8,0x20,0x2C,0x10,0x01,0xB1,0xB4,0xAB,0x5B, +0x79,0x80,0x10,0x1A,0xA8,0x3D,0x18,0x20,0xB3,0x8F,0x18,0x01,0x00,0x09,0xF3,0x89, +0x69,0x88,0x81,0x91,0x08,0xE1,0x1A,0x08,0x11,0x81,0x1E,0x29,0xA0,0x01,0x00,0x90, +0x3E,0x7B,0x18,0x82,0xC3,0xA1,0x2A,0x2C,0x5B,0x81,0xA5,0x90,0x81,0x00,0x0B,0x1A, +0x1C,0x2C,0x32,0xC0,0xF3,0x80,0x2D,0x2A,0x10,0x02,0xE4,0xC1,0x89,0x4A,0x09,0x01, +0x03,0xD2,0x98,0x2A,0x39,0x8A,0x89,0x26,0xB1,0xB2,0x12,0xC0,0x0A,0x5A,0x18,0x98, +0xF3,0x92,0x99,0x99,0x79,0x01,0xB5,0xA1,0x80,0x80,0x90,0x83,0xA0,0xE2,0x81,0x29, +0x93,0x8A,0x0A,0x6A,0x1F,0x18,0x02,0xC8,0x01,0x19,0x3B,0x4A,0x98,0x17,0xA8,0x0D, +0x38,0xA1,0x91,0x10,0xA2,0x2B,0x4C,0xA6,0x81,0xBA,0x21,0x4C,0x80,0x21,0xD1,0x92, +0x2C,0x08,0x30,0x9F,0x93,0x2A,0x89,0x03,0x8B,0x87,0x0A,0x0D,0x12,0x98,0xA4,0x93, +0xBB,0x59,0x18,0xA1,0x32,0xE9,0x84,0x08,0x8A,0x02,0xA1,0x91,0x4B,0xB4,0x20,0x88, +0xF0,0x3A,0x1A,0x88,0x87,0xB1,0x92,0x0A,0x08,0x6B,0x83,0xC3,0x91,0xC0,0x2B,0x79, +0x08,0x8A,0x84,0xA0,0x89,0x40,0x1B,0xA1,0x39,0x98,0x17,0xC2,0xA2,0x12,0xCD,0x20, +0x89,0x92,0x25,0xB0,0x2D,0x3A,0x8B,0x58,0x2A,0xA0,0x4C,0x08,0x30,0xAE,0x82,0x59, +0x89,0x1A,0x10,0xC2,0x18,0x2C,0x40,0x1E,0x01,0xA3,0x8A,0x81,0x2C,0x29,0x29,0xA9, +0x13,0x51,0xAD,0x12,0x89,0x8F,0x18,0x2C,0x39,0x00,0xC1,0x10,0x3C,0x2A,0x41,0xC8, +0xA2,0x91,0x0A,0x6C,0x10,0x12,0x88,0xE8,0x30,0x91,0x81,0xD8,0x01,0x1B,0x0D,0x07, +0x00,0xA8,0x92,0x0A,0x28,0xD2,0xC3,0x02,0xAA,0x94,0x81,0xB4,0xB3,0x1A,0x0B,0x13, +0xF9,0x16,0xA1,0x8A,0x59,0x19,0x02,0xC1,0x91,0x8B,0x3D,0x18,0x3B,0xA4,0x94,0x80, +0x99,0x88,0x1C,0x79,0x0A,0x02,0x03,0xF8,0x90,0x39,0x5B,0x19,0x02,0xC3,0x90,0xBB, +0x58,0x6A,0x09,0x02,0x89,0x91,0x88,0x1A,0x69,0x8A,0x19,0x15,0xA0,0xA2,0x00,0x9A, +0x6B,0x49,0x88,0xA3,0x92,0xBB,0x6B,0x3D,0x38,0x01,0x98,0x91,0x3F,0x09,0x18,0x20, +0x90,0x80,0xAC,0x70,0x91,0x9B,0x51,0x09,0x88,0x99,0x14,0x8B,0x98,0x83,0x79,0xA0, +0x99,0x13,0x01,0x19,0xE0,0x83,0x0B,0xB0,0x0C,0x31,0x95,0xB5,0xC2,0x8A,0x39,0x20, +0x80,0x39,0xF3,0xB1,0x10,0x88,0x5E,0x18,0x94,0xA1,0x88,0xA1,0x98,0x15,0xAA,0x39, +0xD4,0x84,0xC0,0xA2,0xA2,0x0C,0x81,0x86,0xB5,0xA1,0xB1,0x14,0x1B,0xB1,0x02,0x92, +0xC3,0xE0,0x88,0x11,0xAA,0x69,0x18,0x81,0xA3,0xB0,0x01,0xBF,0x2A,0x31,0x93,0xF1, +0x00,0x89,0x18,0x19,0x11,0xD3,0xE0,0x10,0x18,0xB1,0x18,0x24,0x9A,0x2B,0xA4,0xC0, +0xB0,0x31,0x6C,0x19,0xB4,0x12,0xA8,0xEA,0x58,0x10,0x8B,0x93,0x82,0x88,0x9A,0x41, +0x10,0xC3,0xEA,0x41,0xA9,0x9C,0x34,0xA1,0x2A,0x79,0xA2,0x01,0xA8,0xB3,0x28,0xCC, +0x41,0x9A,0xB3,0x4B,0xB3,0x27,0x8B,0x83,0x2B,0x2F,0x08,0x28,0xB2,0x80,0x2C,0x30, +0x5E,0x09,0x12,0x9B,0x09,0x22,0x5B,0x19,0x8A,0x11,0x59,0x99,0xA4,0x32,0xCD,0x18, +0x08,0x10,0x85,0xB3,0xB4,0x1E,0x88,0x28,0x8A,0x11,0x09,0xC0,0x79,0x80,0x91,0x3B, +0x80,0x10,0x0F,0x01,0x80,0x91,0x19,0x3D,0x92,0x28,0xA8,0x37,0x9A,0x0A,0x3A,0x8A, +0x45,0xA9,0xA4,0x00,0xAA,0x09,0x3D,0x59,0x20,0xE1,0x08,0x98,0x90,0x59,0x10,0x09, +0xA3,0xC3,0x93,0x99,0x2B,0x69,0x11,0xD1,0xB1,0xA4,0x91,0x3C,0x89,0x83,0xF0,0x10, +0x91,0xA1,0x89,0x59,0x05,0x99,0x93,0x94,0xC8,0x08,0x0A,0x09,0x17,0xB1,0x83,0xC1, +0x91,0x40,0xA2,0xC2,0x98,0xC3,0xBA,0x28,0x23,0x0F,0x80,0x50,0xB8,0x19,0x10,0x96, +0x98,0x8C,0x05,0x98,0x19,0x29,0x2B,0x3B,0x0A,0xE2,0x01,0x0F,0x3C,0x38,0x08,0x09, +0x81,0x4A,0x6C,0x08,0x00,0x88,0x98,0x38,0x2C,0x5A,0x1B,0x20,0x1A,0x39,0xB0,0x09, +0xCB,0x5B,0x49,0x09,0x71,0x00,0xC1,0x0E,0x08,0x38,0x0C,0x02,0x10,0x0E,0x10,0x8A, +0x48,0x19,0x90,0x92,0x0D,0xA3,0x98,0x3B,0x79,0x19,0x01,0x10,0xE1,0x80,0x19,0x2B, +0x10,0xF2,0x02,0xAB,0x84,0x9A,0x29,0xB4,0x80,0x92,0x03,0x88,0x95,0xD0,0x03,0x90, +0xA0,0xC7,0xA1,0xB0,0xA2,0x02,0x18,0xB5,0xD4,0x01,0xC0,0x08,0xA2,0x93,0xA8,0xA0, +0xC3,0x20,0xF3,0x90,0x00,0xD5,0x08,0x89,0xA5,0x80,0xA0,0x81,0x82,0xC2,0x09,0xD1, +0x13,0xCB,0x03,0x84,0x91,0xE1,0x1B,0x12,0x08,0xAB,0x87,0x18,0xAB,0x58,0x89,0x28, +0x81,0xC9,0x33,0xA9,0x80,0x2E,0x20,0x83,0xB9,0x20,0x3B,0x9E,0x7A,0x08,0x81,0x18, +0x0B,0x88,0x79,0x80,0x8B,0x00,0x12,0x0E,0x89,0x51,0x1B,0x81,0xA0,0x3A,0x01,0xAF, +0x11,0x28,0xBA,0x35,0x98,0x88,0x52,0xC0,0x83,0x2F,0xA9,0x11,0x0A,0x19,0x25,0xD0, +0x30,0x9C,0x08,0x21,0x98,0x81,0x2A,0xF3,0x2A,0x80,0xB6,0x2B,0x08,0x93,0xE9,0x02, +0x81,0x8C,0x21,0x00,0xA6,0xA9,0x94,0x01,0x8F,0x80,0x94,0x98,0x93,0xB4,0x00,0x08, +0xC0,0x14,0x98,0xB3,0xB4,0xC1,0x09,0x18,0xA7,0x00,0xA3,0xC8,0x0A,0x3C,0x19,0x96, +0x83,0xC1,0x99,0x19,0x4A,0x85,0x80,0xC1,0x91,0x99,0x90,0x2A,0x17,0x95,0x99,0x88, +0x12,0xAE,0x39,0x08,0x92,0x84,0xB0,0xA8,0x79,0x09,0x19,0x01,0xB2,0xA3,0x8F,0x28, +0x2B,0xA2,0x40,0x82,0xA0,0x4C,0xA9,0x39,0x8D,0x81,0x70,0x88,0xA0,0x1A,0x49,0x2D, +0x1A,0x26,0xA8,0x98,0x08,0x29,0x0B,0x12,0x96,0xB1,0xB2,0x3A,0x13,0x9B,0x60,0xA0, +0x88,0xB2,0x34,0xEA,0x1A,0x2A,0x79,0x98,0x10,0x04,0x8C,0x1C,0x81,0x04,0x8C,0x83, +0x19,0x2F,0x81,0x93,0x98,0x10,0x08,0x30,0x2A,0xFA,0x05,0x08,0x2A,0x89,0x91,0xA3, +0xFA,0x11,0x11,0x00,0x8C,0x04,0x8A,0x2A,0xB5,0x10,0xA9,0xC2,0x3D,0x1B,0x32,0x04, +0x0A,0x1A,0x09,0x40,0x1F,0x92,0x1D,0x2A,0x91,0x10,0x30,0x2F,0x0B,0x68,0x99,0xA2, +0x92,0x88,0x78,0xA9,0x20,0x28,0xE2,0x92,0x1A,0x99,0x4B,0x19,0x22,0xA1,0xE2,0x21, +0x2F,0x98,0x29,0x18,0x91,0x08,0xB0,0x79,0x1A,0x82,0x3B,0xB1,0xA7,0x8A,0xB3,0x98, +0x5B,0x23,0xCA,0x42,0x83,0xF0,0x90,0x18,0x98,0x08,0xB4,0x20,0xA3,0xC0,0x43,0xD8, +0x80,0x81,0xA3,0x99,0xD9,0xA7,0x19,0x90,0x10,0x05,0xB1,0x8B,0x02,0xA4,0xBD,0x23, +0x93,0x8A,0x99,0x4B,0x03,0xC1,0xF8,0x38,0x09,0x2B,0x14,0xD0,0x03,0x8A,0x2A,0x39, +0xB9,0x97,0x90,0xAA,0x50,0x01,0x99,0x51,0xD1,0x09,0x1A,0xB5,0x00,0x8B,0x93,0x08, +0x98,0x11,0xF9,0x85,0x2B,0x08,0x96,0x89,0x90,0x2A,0x12,0x4A,0xD8,0x85,0x2B,0x0E, +0x10,0x00,0x01,0xB1,0x9B,0x69,0x1A,0x90,0x40,0xB8,0x01,0x08,0x0A,0x2C,0x09,0x14, +0x4B,0xE2,0x82,0x88,0xB1,0x78,0x0A,0x01,0xC2,0x93,0x19,0xCE,0x20,0x3C,0x82,0xB4, +0x1B,0x20,0x8C,0x3B,0x29,0xAB,0x86,0x23,0xD8,0x81,0x9A,0x5A,0x49,0xB0,0x16,0xA0, +0xB0,0x28,0x1B,0x13,0x93,0xE4,0xA2,0xA9,0x08,0x5A,0xB3,0x12,0xC1,0xE1,0x10,0x88, +0x01,0x0C,0x92,0x08,0x89,0xB7,0x88,0x81,0x10,0x9A,0x17,0xA0,0xB0,0x13,0x99,0xE0, +0x39,0x31,0xD2,0xB2,0x80,0x0B,0x2D,0x49,0x80,0x01,0xB0,0x06,0x09,0x0C,0x3A,0x69, +0xA0,0x08,0xB2,0xA1,0x69,0x2B,0x5A,0x81,0x92,0xBA,0x21,0xB1,0x7D,0x10,0x80,0x08, +0x88,0x82,0x32,0x0D,0xB0,0x1A,0x1C,0x21,0x94,0xA9,0x58,0xB9,0x5A,0x4A,0xA0,0x13, +0xA9,0x80,0x7C,0x00,0x20,0x8A,0x04,0x0C,0x00,0x82,0x2A,0xB2,0xAC,0x4B,0x69,0xA0, +0xA6,0x81,0x9B,0x19,0x38,0x8B,0x17,0xB2,0x81,0x2A,0xBB,0x94,0x29,0xA2,0x15,0xBA, +0x97,0xA3,0xB9,0x79,0x01,0xB2,0x02,0xF1,0x90,0x0A,0x29,0x11,0x88,0xE5,0xA0,0x81, +0x19,0x91,0x90,0x28,0xB3,0x14,0xD0,0xB5,0x91,0x9A,0x29,0x0B,0x07,0xA2,0xB3,0x01, +0x9D,0x28,0x41,0xD0,0x91,0x90,0x82,0x1A,0xA8,0x44,0x9A,0xA9,0x21,0xE3,0xA9,0x4B, +0x19,0x78,0x89,0x83,0xA3,0xB9,0x5A,0x3D,0x80,0x82,0xA2,0xA0,0x6C,0x10,0x20,0x8B, +0x93,0x8B,0x0E,0x33,0xA9,0xB1,0x68,0x8A,0x31,0xAC,0x94,0xB4,0x8B,0x32,0x0B,0xB4, +0x81,0x91,0x1D,0x33,0xD9,0x31,0xE1,0x8B,0x3B,0x30,0x12,0x49,0xD2,0x8E,0x29,0x18, +0x8A,0x92,0x02,0xAA,0x59,0x1C,0x32,0x88,0x01,0x23,0xFB,0x83,0x29,0xDA,0x59,0x01, +0x81,0x92,0xE1,0x18,0x8A,0x1D,0x30,0x93,0xF1,0x00,0x01,0x0B,0x39,0x92,0x89,0xA0, +0x11,0x5B,0xE0,0x82,0x09,0x13,0xAA,0xB4,0x16,0xD8,0x91,0x2A,0x29,0x84,0x1B,0xC5, +0x98,0x98,0x31,0x98,0x99,0x17,0xA9,0x20,0x92,0xC3,0x18,0x9D,0x20,0x3D,0x89,0x94, +0xA2,0x1C,0x5C,0x29,0x39,0xA0,0xB3,0x00,0x0C,0x4C,0x48,0x92,0x0A,0x91,0x85,0x9A, +0x01,0x82,0x1F,0x10,0x99,0x15,0xC1,0xA0,0x39,0x1A,0x1D,0x85,0xB4,0x90,0x1A,0x2A, +0x4B,0x01,0xB2,0x93,0xBE,0x12,0x83,0xC9,0x18,0x09,0x20,0x78,0xF1,0x08,0x19,0x88, +0x3A,0x83,0xB3,0xA9,0x93,0x7A,0x0A,0x96,0x98,0x00,0xA8,0x3A,0x30,0x92,0xF2,0x9B, +0x3D,0x38,0x92,0x92,0xC3,0xB8,0x6B,0x29,0x01,0x01,0xB2,0x2F,0x09,0x19,0x18,0x01, +0x3B,0x7B,0x10,0xA1,0x90,0x39,0x0F,0x38,0x0A,0xB5,0xA4,0x89,0x8B,0x6A,0x2B,0x12, +0xC8,0x90,0x40,0x2A,0x9E,0x22,0x88,0x18,0x09,0x3A,0xC3,0xE8,0x09,0x59,0x08,0x12, +0x94,0xD0,0x1A,0x2C,0x38,0x00,0xA1,0x83,0xE8,0x08,0x3A,0x08,0x10,0x9E,0x83,0x1D, +0x92,0x19,0x2C,0x39,0x3B,0x59,0x04,0xE1,0x80,0x08,0x8D,0x21,0x81,0xB2,0xB2,0x02, +0x99,0x91,0xA4,0xD6,0x98,0x99,0x03,0x80,0x98,0xA7,0x91,0x09,0xA1,0xB2,0xB3,0xE1, +0x12,0x92,0xB1,0x81,0x06,0x99,0x0A,0x23,0xC4,0xB1,0xF2,0x89,0x19,0x3A,0x94,0x82, +0xE0,0x89,0x38,0x0B,0xA4,0xA5,0x80,0x80,0x8C,0x34,0xB9,0xA9,0x23,0x13,0xB9,0xC1, +0xC7,0x1B,0x89,0x10,0x20,0x11,0xE3,0xA8,0x4B,0x0B,0x40,0x91,0x90,0x1B,0x5F,0x2A, +0x18,0x82,0x91,0x0B,0x4A,0x28,0xCA,0x40,0x80,0x5B,0x2C,0x13,0xB0,0x8A,0xA9,0x5A, +0x58,0x89,0x82,0x88,0x2E,0x3B,0x31,0xA1,0x9B,0x01,0x7A,0x2C,0x01,0x91,0x93,0x3F, +0x88,0x39,0x10,0xF1,0x91,0x8B,0x48,0x0A,0x12,0xE3,0xA8,0x18,0x28,0x92,0x97,0x98, +0x99,0x19,0xA1,0x11,0xB6,0x88,0x3B,0x10,0xD3,0xC3,0xA1,0x2A,0x8A,0x49,0x04,0xF1, +0x91,0x02,0x8A,0x89,0x04,0xF1,0x98,0x80,0x18,0x12,0xE3,0x81,0x98,0x80,0x01,0xB3, +0xF2,0x99,0x12,0x2A,0xB5,0xB3,0x92,0xAA,0x19,0x50,0xB2,0xC3,0x92,0xD0,0x2B,0x68, +0x93,0x99,0xC0,0x2C,0x3E,0x80,0x20,0x08,0x93,0x0D,0x2A,0x31,0x8D,0x02,0x2B,0x91, +0x08,0x0A,0x03,0x2C,0x3C,0x52,0xB9,0xA0,0x12,0xBF,0x3A,0x29,0x01,0x88,0xC0,0x6A, +0x3C,0x0A,0x49,0x18,0x0B,0x39,0x2B,0x69,0x0A,0x84,0x2A,0x2A,0x1C,0x2A,0xC3,0x8C, +0x19,0x50,0x09,0x91,0xA7,0x8D,0x18,0x1A,0x28,0x00,0xA0,0x94,0x10,0x1F,0x20,0x90, +0x8A,0x12,0xD0,0x1A,0x5A,0x81,0x04,0xBC,0x23,0x10,0xE0,0x90,0x90,0x18,0x1A,0xA6, +0x12,0xB1,0xD0,0x4A,0x08,0x82,0x92,0xB6,0x9A,0x0A,0x12,0x88,0xC3,0xC5,0x8A,0x89, +0x20,0xB5,0x93,0x0B,0x18,0x00,0x09,0xF2,0x88,0x2A,0x4A,0x08,0x05,0xB2,0xA9,0x3B, +0x5D,0x28,0xA4,0xB1,0x00,0x19,0x19,0x7A,0xA3,0xB3,0x0A,0x90,0xA1,0xC4,0x80,0xBA, +0x50,0x13,0xC1,0xC2,0x9A,0x2A,0x7B,0x28,0x84,0xC1,0x09,0x3B,0x4E,0x20,0x91,0xA1, +0x18,0xAB,0x79,0x10,0xB4,0x08,0x9A,0x11,0x2B,0xF0,0x93,0xAA,0x01,0x6A,0x01,0x93, +0x80,0xB8,0x2A,0x5B,0x10,0x80,0x89,0x4A,0x5B,0x92,0x15,0xB2,0xA0,0x2F,0x19,0x93, +0xB8,0x95,0x80,0x1C,0x21,0xA9,0x02,0x0B,0xA0,0x5A,0x18,0x98,0x39,0x1B,0x68,0x00, +0x91,0x91,0x9C,0x39,0x3E,0x18,0x84,0xB3,0x9B,0x7A,0x08,0x18,0x0A,0xB5,0x91,0x0B, +0x28,0x39,0x19,0x90,0x0A,0x50,0xAC,0x11,0x01,0xAB,0x88,0x52,0x1B,0x83,0xC4,0xA2, +0x9A,0xAB,0x03,0x90,0x19,0x93,0x81,0x08,0x92,0x9A,0x68,0x98,0x19,0x39,0xC1,0x92, +0x8A,0x38,0x4E,0x02,0xB1,0x90,0xC3,0x18,0x2B,0x04,0xC3,0xD2,0x91,0x90,0x81,0x89, +0x13,0xF1,0x88,0x93,0xA2,0x00,0x91,0xC0,0x5B,0x21,0x99,0x93,0x06,0x9A,0x1B,0x48, +0x99,0xB7,0x90,0x89,0x18,0x1B,0x11,0xA4,0xB2,0x81,0x9A,0x08,0x97,0x98,0x91,0x10, +0xB8,0x06,0xA2,0xA0,0x29,0x2B,0x21,0xC2,0xD1,0x10,0x1A,0x4A,0x29,0xF1,0x98,0x29, +0x1B,0x31,0x10,0xA0,0xA1,0x1D,0x5A,0x29,0xB2,0x82,0xA8,0x0F,0x28,0x21,0x09,0x91, +0x82,0x4D,0x10,0xA3,0xB0,0x89,0x4C,0x39,0xA0,0xA4,0xA1,0x89,0x1E,0x28,0x29,0xA3, +0xC3,0x2D,0x19,0x01,0x49,0x01,0x9B,0x0C,0x21,0xC2,0xA2,0x93,0x7C,0x2A,0x10,0x90, + +/* Source: 08HH.ROM */ +/* Length: 384 / 0x00000180 */ + +0x75,0xF2,0xAB,0x7D,0x7E,0x5C,0x3B,0x4B,0x3C,0x4D,0x4A,0x02,0xB3,0xC5,0xE7,0xE3, +0x92,0xB3,0xC4,0xB3,0xC3,0x8A,0x3B,0x5D,0x5C,0x3A,0x84,0xC2,0x91,0xA4,0xE7,0xF7, +0xF7,0xF4,0xA1,0x1B,0x49,0xA5,0xB1,0x1E,0x7F,0x5A,0x00,0x89,0x39,0xB7,0xA8,0x3D, +0x4A,0x84,0xE7,0xF7,0xE2,0x2D,0x4C,0x3A,0x4E,0x7D,0x04,0xB0,0x2D,0x4B,0x10,0x80, +0xA3,0x99,0x10,0x0E,0x59,0x93,0xC4,0xB1,0x81,0xC4,0xA2,0xB2,0x88,0x08,0x3F,0x3B, +0x28,0xA6,0xC3,0xA2,0xA2,0xC5,0xC1,0x3F,0x7E,0x39,0x81,0x93,0xC2,0xA3,0xE5,0xD2, +0x80,0x93,0xB8,0x6D,0x49,0x82,0xD4,0xA1,0x90,0x01,0xA0,0x09,0x04,0xE3,0xB2,0x91, +0xB7,0xB3,0xA8,0x2A,0x03,0xF3,0xA1,0x92,0xC5,0xC3,0xB2,0x0B,0x30,0xB3,0x8E,0x6D, +0x4A,0x01,0xB4,0xB4,0xC4,0xC3,0x99,0x3B,0x12,0xE3,0xA1,0x88,0x82,0xB4,0x9A,0x5C, +0x3A,0x18,0x93,0xC3,0xB3,0xB4,0xA8,0x19,0x04,0xF3,0xA8,0x3B,0x10,0xA2,0x88,0xA5, +0xB2,0x0B,0x6D,0x4B,0x10,0x91,0x89,0x3C,0x18,0x18,0xA6,0xC4,0xC3,0x98,0x19,0x2B, +0x20,0x91,0xA0,0x4E,0x28,0x93,0xB3,0xC2,0x92,0xA9,0x5A,0x96,0xC4,0xC2,0x09,0x01, +0xC4,0xA1,0x92,0xC4,0xA1,0x89,0x10,0xA3,0xA1,0x90,0x1C,0x5A,0x01,0xC5,0xA1,0x92, +0xD4,0xB3,0xC4,0xC4,0xC3,0xA1,0x88,0x1A,0x28,0x89,0x3C,0x3A,0x3D,0x29,0x00,0x93, +0xB0,0x3D,0x28,0x80,0x91,0x82,0xE3,0x99,0x2A,0x11,0xD6,0xC3,0x99,0x29,0x82,0xC4, +0xC3,0xA1,0x0A,0x3B,0x3D,0x3A,0x02,0xC3,0xA2,0x99,0x3B,0x2C,0x7C,0x28,0x81,0xA3, +0xB2,0xA3,0xB1,0x08,0x1A,0x3C,0x18,0x2E,0x4C,0x39,0xA5,0xB3,0xB4,0xC2,0x88,0x08, +0x19,0x0A,0x49,0xB7,0xB3,0xA2,0xA1,0x92,0xA1,0x93,0xB1,0x0C,0x7D,0x39,0x93,0xB3, +0xB1,0x1A,0x19,0x5D,0x28,0xA6,0xC4,0xB2,0x90,0x09,0x2A,0x18,0x1B,0x5B,0x28,0x88, +0x2C,0x29,0x82,0xA0,0x18,0x91,0x2D,0x29,0x2B,0x5C,0x4C,0x3B,0x4C,0x28,0x80,0x92, +0x90,0x09,0x2B,0x28,0x1D,0x6B,0x11,0xC5,0xB2,0x0B,0x39,0x09,0x4D,0x28,0x88,0x00, +0x1B,0x28,0x94,0xE3,0xA0,0x1A,0x28,0xB5,0xB4,0xB3,0xB2,0x93,0xE2,0x91,0x92,0xD4, +0xA0,0x1B,0x4A,0x01,0xA1,0x88,0x2D,0x5C,0x3B,0x28,0x08,0x93,0xD4,0xB2,0x91,0xB4, +0xA0,0x3E,0x3B,0x4B,0x3B,0x29,0x08,0x93,0x9B,0x7B,0x3A,0x19,0x00,0x80,0x80,0xA0, + +/* Source: 10TOM.ROM */ +/* Length: 640 / 0x00000280 */ + +0x77,0x27,0x87,0x01,0x2D,0x4F,0xC3,0xC1,0x92,0x91,0x89,0x59,0x83,0x1A,0x32,0xC2, +0x95,0xB1,0x81,0x88,0x81,0x4A,0x3D,0x11,0x9E,0x0B,0x88,0x0C,0x18,0x3B,0x11,0x11, +0x91,0x00,0xA0,0xE2,0x0A,0x48,0x13,0x24,0x81,0x48,0x1B,0x39,0x1C,0x83,0x84,0xA1, +0xD1,0x8E,0x8A,0x0B,0xC0,0x98,0x92,0xB8,0x39,0x90,0x10,0x92,0xF0,0xB5,0x88,0x32, +0x49,0x51,0x21,0x03,0x82,0x10,0x8A,0x7A,0x09,0x00,0xA2,0xCA,0x1B,0xCC,0x1C,0xB9, +0x8E,0x89,0x89,0xA1,0x89,0x92,0x29,0x11,0x60,0x40,0x14,0x22,0x32,0x78,0x40,0x01, +0x02,0x90,0x81,0xAB,0x0B,0x00,0xAF,0x99,0xCC,0xAB,0xDA,0xA9,0x99,0x1B,0x30,0x14, +0x92,0x22,0x19,0x68,0x32,0x14,0x26,0x13,0x23,0x23,0x20,0x12,0x9A,0xA8,0xB9,0xFA, +0xAA,0xCA,0xCC,0x0C,0xA8,0xAE,0x88,0xB9,0x88,0xA0,0x02,0x21,0x50,0x43,0x03,0x81, +0x2A,0x11,0x34,0x63,0x24,0x33,0x22,0x38,0x8B,0xEA,0xAE,0x99,0xA0,0x90,0x82,0x00, +0x89,0xBF,0x8A,0xE8,0xA9,0x90,0x01,0x12,0x13,0x12,0x08,0xA9,0xAA,0xC9,0x22,0x63, +0x63,0x12,0x44,0x00,0x10,0x88,0x9C,0x98,0xA1,0x85,0x03,0x32,0x36,0x80,0x89,0xDB, +0xDB,0xBB,0xB9,0xBA,0x01,0x81,0x28,0x19,0xCB,0xFA,0xBC,0x09,0x13,0x37,0x34,0x34, +0x23,0x31,0x20,0x10,0x00,0x00,0x28,0x38,0x10,0x88,0xEC,0x8D,0xCB,0xBC,0xCC,0xBB, +0xBB,0xC9,0x99,0x00,0x00,0x33,0x11,0x22,0x81,0x07,0x41,0x54,0x34,0x34,0x22,0x31, +0x00,0x88,0x9A,0x9B,0x98,0xAB,0x8E,0x9B,0xBD,0x9C,0xBC,0xBB,0xDA,0xAA,0xA9,0x99, +0x18,0x38,0x60,0x20,0x31,0x13,0x13,0x51,0x14,0x31,0x53,0x33,0x35,0x22,0x01,0x8A, +0x9C,0xA9,0xCA,0xC9,0xA8,0x00,0x10,0x81,0x9C,0x9E,0xAB,0xCC,0xAB,0xBA,0x98,0x30, +0x52,0x03,0x81,0x08,0x9C,0xAC,0xAC,0x18,0x11,0x03,0x51,0x61,0x41,0x31,0x31,0x02, +0x01,0x20,0x24,0x43,0x44,0x40,0x30,0x10,0xBC,0xBE,0xCB,0xDB,0xAB,0xBA,0x99,0x98, +0x99,0xAA,0xBD,0xAA,0xC8,0x90,0x11,0x53,0x37,0x23,0x43,0x34,0x33,0x33,0x33,0x11, +0x28,0x00,0x19,0xA9,0x9A,0xCB,0xCE,0xBB,0xEB,0xBC,0xBB,0xCA,0xBA,0xA8,0x88,0x11, +0x12,0x21,0x20,0x22,0x26,0x26,0x23,0x23,0x43,0x24,0x22,0x32,0x20,0x31,0x81,0x9A, +0xBC,0xBC,0xCB,0xBD,0x9A,0xA9,0x90,0x98,0xBA,0xCC,0xCB,0xBC,0x8B,0x88,0x22,0x35, +0x23,0x12,0x99,0x8B,0xAA,0xAA,0x89,0x82,0x93,0x31,0x42,0x23,0x23,0x21,0x32,0x11, +0x20,0x13,0x13,0x24,0x24,0x24,0x22,0x11,0x8A,0x9E,0xAC,0xAC,0xAA,0xBA,0xAA,0xAB, +0xBD,0xBC,0xCB,0xCB,0xA9,0xA8,0x91,0x12,0x44,0x43,0x44,0x34,0x34,0x42,0x33,0x42, +0x21,0x11,0x11,0x88,0x80,0xAA,0x0B,0xAC,0xCB,0xEC,0xAC,0xBA,0xCA,0xAB,0x9A,0x99, +0x80,0x91,0x09,0x08,0x10,0x22,0x44,0x43,0x44,0x33,0x43,0x22,0x13,0x21,0x22,0x20, +0x09,0x88,0xB9,0xC8,0xBB,0xAB,0xAB,0xA9,0xA9,0x9B,0x9B,0x99,0x90,0x90,0x00,0x81, +0x00,0x08,0x09,0x8A,0x9A,0xAA,0xA9,0xA9,0x99,0x90,0x80,0x01,0x80,0x00,0x09,0x31, +0x32,0x44,0x33,0x43,0x34,0x33,0x24,0x22,0x23,0x12,0x10,0x09,0x9B,0xAB,0xCA,0xCC, +0xBB,0xCB,0xDA,0xCA,0xAB,0xCA,0xAB,0xA9,0xA8,0x92,0x12,0x43,0x53,0x35,0x23,0x33, +0x43,0x43,0x52,0x22,0x22,0x21,0x01,0x09,0x89,0xA9,0xBB,0xBD,0xBC,0xCB,0xDA,0xAB, +0xAB,0xAB,0xAA,0xA9,0x99,0xA8,0x09,0x01,0x11,0x34,0x25,0x23,0x33,0x51,0x22,0x31, +0x12,0x20,0x21,0x12,0x10,0x80,0x99,0x9A,0x99,0x99,0x88,0x08,0x00,0x88,0xA9,0x99, +0x99,0x80,0x80,0x10,0x01,0x00,0x9A,0xAA,0xBB,0xBA,0xBA,0xA9,0x99,0x99,0x89,0x99, +0x99,0x00,0x01,0x33,0x35,0x24,0x23,0x34,0x23,0x33,0x34,0x33,0x43,0x32,0x21,0x88, +0xAB,0xBD,0xBB,0xDB,0xAB,0xBA,0xBB,0xDA,0xBB,0xCB,0xBB,0xBC,0xA8,0x90,0x01,0x12, +0x23,0x43,0x53,0x34,0x34,0x39,0x80,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00, + +/* Source: 20RIM.ROM */ +/* Length: 128 / 0x00000080 */ + +0x0F,0xFF,0x73,0x8E,0x71,0xCD,0x00,0x49,0x10,0x90,0x21,0x49,0xA0,0xDB,0x02,0x3A, +0xE3,0x0A,0x50,0x98,0xC0,0x59,0xA2,0x99,0x09,0x22,0xA2,0x80,0x10,0xA8,0x5B,0xD2, +0x88,0x21,0x09,0x96,0xA8,0x10,0x0A,0xE0,0x08,0x48,0x19,0xAB,0x52,0xA8,0x92,0x0C, +0x03,0x19,0xE2,0x0A,0x12,0xC2,0x81,0x1E,0x01,0xD0,0x48,0x88,0x98,0x01,0x49,0x91, +0xAA,0x2C,0x25,0x89,0x88,0xB5,0x81,0xA2,0x9A,0x12,0x9E,0x38,0x3B,0x81,0x9B,0x59, +0x01,0x93,0xCA,0x4A,0x21,0xA0,0x3D,0x0A,0x39,0x3D,0x12,0xA8,0x3F,0x18,0x01,0x92, +0x1C,0x00,0xB2,0x48,0xB9,0x94,0xA3,0x19,0x4F,0x19,0xB2,0x32,0x90,0xBA,0x01,0xE6, +0x91,0x80,0xC1,0xA4,0x2A,0x08,0xA1,0xB1,0x25,0xD2,0x88,0x99,0x21,0x80,0x88,0x80, +}; diff --git a/thirdparty/opnmidi/chips/mamefm/resampler.cpp b/thirdparty/opnmidi/chips/mamefm/resampler.cpp new file mode 100644 index 000000000..089c1b449 --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/resampler.cpp @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "resampler.hpp" + +enum Stereo +{ + LEFT = 0, + RIGHT = 1 +}; + +static const size_t SMPL_BUF_SIZE_ = 0x10000; + +namespace chip +{ + AbstractResampler::AbstractResampler() + { + for (int pan = LEFT; pan <= RIGHT; ++pan) { + destBuf_[pan] = new sample[SMPL_BUF_SIZE_](); + } + } + + AbstractResampler::~AbstractResampler() + { + for (int pan = LEFT; pan <= RIGHT; ++pan) { + delete[] destBuf_[pan]; + } + } + + void AbstractResampler::init(int srcRate, int destRate, size_t maxDuration) + { + srcRate_ = srcRate; + maxDuration_ = maxDuration; + destRate_ = destRate; + updateRateRatio(); + } + + void AbstractResampler::setDestributionRate(int destRate) + { + destRate_ = destRate; + updateRateRatio(); + } + + void AbstractResampler::setMaxDuration(size_t maxDuration) + { + maxDuration_ = maxDuration; + } + + /****************************************/ + sample** LinearResampler::interpolate(sample** src, size_t nSamples, size_t intrSize) + { + (void)intrSize; + // Linear interplation + for (int pan = LEFT; pan <= RIGHT; ++pan) { + for (size_t n = 0; n < nSamples; ++n) { + float curnf = n * rateRatio_; + int curni = static_cast(curnf); + float sub = curnf - curni; + if (sub) { + destBuf_[pan][n] = static_cast(src[pan][curni] + (src[pan][curni + 1] - src[pan][curni]) * sub); + } + else /* if (sub == 0) */ { + destBuf_[pan][n] = src[pan][curni]; + } + } + } + + return destBuf_; + } + + /****************************************/ + const float SincResampler::F_PI_ = 3.14159265f; + const int SincResampler::SINC_OFFSET_ = 16; + + void SincResampler::init(int srcRate, int destRate, size_t maxDuration) + { + AbstractResampler::init(srcRate, destRate, maxDuration); + initSincTables(); + } + + void SincResampler::setDestributionRate(int destRate) + { + AbstractResampler::setDestributionRate(destRate); + initSincTables(); + } + + void SincResampler::setMaxDuration(size_t maxDuration) + { + AbstractResampler::setMaxDuration(maxDuration); + initSincTables(); + } + + sample** SincResampler::interpolate(sample** src, size_t nSamples, size_t intrSize) + { + // Sinc interpolation + size_t offsetx2 = SINC_OFFSET_ << 1; + + for (int pan = LEFT; pan <= RIGHT; ++pan) { + for (size_t n = 0; n < nSamples; ++n) { + size_t seg = n * offsetx2; + int curn = static_cast(n * rateRatio_); + int k = curn - SINC_OFFSET_; + if (k < 0) k = 0; + int end = curn + SINC_OFFSET_; + if (static_cast(end) > intrSize) end = static_cast(intrSize); + sample samp = 0; + for (; k < end; ++k) { + samp += static_cast(src[pan][k] * sincTable_[seg + SINC_OFFSET_ + (k - curn)]); + } + destBuf_[pan][n] = samp; + } + } + + return destBuf_; + } + + void SincResampler::initSincTables() + { + size_t maxSamples = destRate_ * maxDuration_ / 1000; + + if (srcRate_ != destRate_) { + size_t intrSize = calculateInternalSampleSize(maxSamples); + size_t offsetx2 = SINC_OFFSET_ << 1; + sincTable_.resize(maxSamples * offsetx2); + for (size_t n = 0; n < maxSamples; ++n) { + size_t seg = n * offsetx2; + float rcurn = n * rateRatio_; + int curn = static_cast(rcurn); + int k = curn - SINC_OFFSET_; + if (k < 0) k = 0; + int end = curn + SINC_OFFSET_; + if (static_cast(end) > intrSize) end = static_cast(intrSize); + for (; k < end; ++k) { + sincTable_[seg + SINC_OFFSET_ + (k - curn)] = sinc(F_PI_ * (rcurn - k)); + } + } + } + } +} diff --git a/thirdparty/opnmidi/chips/mamefm/resampler.hpp b/thirdparty/opnmidi/chips/mamefm/resampler.hpp new file mode 100644 index 000000000..7aee2ae9f --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/resampler.hpp @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-only +#pragma once +#include +#include +#include +#include + +typedef int32_t sample; + +namespace chip +{ + class AbstractResampler + { + public: + AbstractResampler(); + virtual ~AbstractResampler(); + virtual void init(int srcRate, int destRate, size_t maxDuration); + virtual void setDestributionRate(int destRate); + virtual void setMaxDuration(size_t maxDuration); + virtual sample** interpolate(sample** src, size_t nSamples, size_t intrSize) = 0; + + inline size_t calculateInternalSampleSize(size_t nSamples) + { + float f = nSamples * rateRatio_; + size_t i = static_cast(f); + return ((f - i) ? (i + 1) : i); + } + + protected: + inline void updateRateRatio() { + rateRatio_ = static_cast(srcRate_) / destRate_; + } + + protected: + int srcRate_, destRate_; + size_t maxDuration_; + float rateRatio_; + sample* destBuf_[2]; + }; + + + class LinearResampler : public AbstractResampler + { + public: + sample** interpolate(sample** src, size_t nSamples, size_t intrSize); + }; + + + class SincResampler : public AbstractResampler + { + public: + void init(int srcRate, int destRate, size_t maxDuration); + void setDestributionRate(int destRate); + void setMaxDuration(size_t maxDuration); + sample** interpolate(sample** src, size_t nSamples, size_t intrSize); + + private: + std::vector sincTable_; + + static const float F_PI_; + static const int SINC_OFFSET_; + + void initSincTables(); + + static inline float sinc(float x) + { + return ((!x) ? 1.0f : (std::sin(x) / x)); + } + }; +} diff --git a/thirdparty/opnmidi/chips/mamefm/ymdeltat.cpp b/thirdparty/opnmidi/chips/mamefm/ymdeltat.cpp new file mode 100644 index 000000000..67e354dcf --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/ymdeltat.cpp @@ -0,0 +1,628 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski +/* +** +** File: ymdeltat.c +** +** YAMAHA DELTA-T adpcm sound emulation subroutine +** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B) +** +** Base program is YM2610 emulator by Hiromitsu Shioya. +** Written by Tatsuyuki Satoh +** Improvements by Jarek Burczynski (bujar at mame dot net) +** +** +** History: +** +** 03-08-2003 Jarek Burczynski: +** - fixed BRDY flag implementation. +** +** 24-07-2003 Jarek Burczynski, Frits Hilderink: +** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset +** +** 22-07-2003 Jarek Burczynski, Frits Hilderink: +** - fixed external memory support +** +** 15-06-2003 Jarek Burczynski: +** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08) +** - implemented support for the Limit address register +** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM +** - implemented external memory access (read/write) via the ADPCM data reg reads/writes +** Thanks go to Frits Hilderink for the example code. +** +** 14-06-2003 Jarek Burczynski: +** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO +** - modified EOS handling +** +** 05-04-2003 Jarek Burczynski: +** - implemented partial support for external/processor memory on sample replay +** +** 01-12-2002 Jarek Burczynski: +** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi +** - renamed/removed some YM_DELTAT struct fields +** +** 28-12-2001 Acho A. Tang +** - added EOS status report on ADPCM playback. +** +** 05-08-2001 Jarek Burczynski: +** - now_step is initialized with 0 at the start of play. +** +** 12-06-2001 Jarek Burczynski: +** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC. +** Checked on real YM2610 chip - address register is 24 bits wide. +** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem. +** +** TO DO: +** Check size of the address register on the other chips.... +** +** Version 0.72 +** +** sound chips that have this unit: +** YM2608 OPNA +** YM2610/B OPNB +** Y8950 MSX AUDIO +** +*/ + +#include "emu.h" +#include "ymdeltat.h" + +#define YM_DELTAT_SHIFT (16) + +#define YM_DELTAT_DELTA_MAX (24576) +#define YM_DELTAT_DELTA_MIN (127) +#define YM_DELTAT_DELTA_DEF (127) + +#define YM_DELTAT_DECODE_RANGE 32768 +#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE)) +#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1) + +/* Forecast to next Forecast (rate = *8) */ +/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */ +static CONSTEXPR int32_t ym_deltat_decode_tableB1[16] = { + 1, 3, 5, 7, 9, 11, 13, 15, + -1, -3, -5, -7, -9, -11, -13, -15, +}; +/* delta to next delta (rate= *64) */ +/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */ +static CONSTEXPR int32_t ym_deltat_decode_tableB2[16] = { + 57, 57, 57, 57, 77, 102, 128, 153, + 57, 57, 57, 57, 77, 102, 128, 153 +}; + +#if 0 +void YM_DELTAT::BRDY_callback() +{ + logerror("BRDY_callback reached (flag set) !\n"); + + /* set BRDY bit in status register */ + if(status_set_handler) + if(status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); +} +#endif + +uint8_t YM_DELTAT::ADPCM_Read() +{ + uint8_t v = 0; + + /* external memory read */ + if ((portstate & 0xe0) == 0x20) + { + /* two dummy reads */ + if (memread) + { + now_addr = start << 1; + memread--; + return 0; + } + + + if (now_addr != (end << 1)) + { + v = read_byte(device, now_addr>>1); + + /*logerror("YM Delta-T memory read $%08x, v=$%02x\n", now_addr >> 1, v);*/ + + now_addr += 2; /* two nibbles at a time */ + + /* reset BRDY bit in status register, which means we are reading the memory now */ + if (status_reset_handler && status_change_BRDY_bit) + (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); + + /* setup a timer that will callback us in 10 master clock cycles for Y8950 + * in the callback set the BRDY flag to 1 , which means we have another data ready. + * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. + */ + /* set BRDY bit in status register */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); + } + else + { + /* set EOS bit in status register */ + if (status_set_handler && status_change_EOS_bit) + (status_set_handler)(status_change_which_chip, status_change_EOS_bit); + } + } + + return v; +} + + +/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ +static CONSTEXPR uint8_t dram_rightshift[4]={3,0,0,0}; + +/* DELTA-T ADPCM write register */ +void YM_DELTAT::ADPCM_Write(int r, int v) +{ + if (r >= 0x10) return; + reg[r] = v; /* stock data */ + + switch (r) + { + case 0x00: +/* +START: + Accessing *external* memory is started when START bit (D7) is set to "1", so + you must set all conditions needed for recording/playback before starting. + If you access *CPU-managed* memory, recording/playback starts after + read/write of ADPCM data register $08. + +REC: + 0 = ADPCM synthesis (playback) + 1 = ADPCM analysis (record) + +MEMDATA: + 0 = processor (*CPU-managed*) memory (means: using register $08) + 1 = external memory (using start/end/limit registers to access memory: RAM or ROM) + + +SPOFF: + controls output pin that should disable the speaker while ADPCM analysis + +RESET and REPEAT only work with external memory. + + +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + /* handle emulation mode */ + if (emulation_mode == EMULATION_MODE_YM2610) + { + v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */ + v &= ~0x40; /* YM2610 has no rec bit */ + } + + portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */ + + if (portstate & 0x80)/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */ + { + /* set PCM BUSY bit */ + PCM_BSY = 1; + + /* start ADPCM */ + now_step = 0; + acc = 0; + prev_acc = 0; + adpcml = 0; + adpcmd = YM_DELTAT_DELTA_DEF; + now_data = 0; + + } + + if (portstate & 0x20) /* do we access external memory? */ + { + now_addr = start << 1; + memread = 2; /* two dummy reads needed before accesing external memory via register $08*/ + } + else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */ + { + now_addr = 0; + } + + if (portstate & 0x01) + { + portstate = 0x00; + + /* clear PCM BUSY bit (in status register) */ + PCM_BSY = 0; + + /* set BRDY flag */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); + } + break; + + case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */ + /* handle emulation mode */ + if (emulation_mode == EMULATION_MODE_YM2610) + { + v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't tave ROM/RAM memory flag bit. */ + } + + pan = &output_pointer[(v >> 6) & 0x03]; + if ((control2 & 3) != (v & 3)) + { + /*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ + if (DRAMportshift != dram_rightshift[v & 3]) + { + DRAMportshift = dram_rightshift[v & 3]; + + /* final shift value depends on chip type and memory type selected: + 8 for YM2610 (ROM only), + 5 for ROM for Y8950 and YM2608, + 5 for x8bit DRAMs for Y8950 and YM2608, + 2 for x1bit DRAMs for Y8950 and YM2608. + */ + + /* refresh addresses */ + start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift); + end = (reg[0x5] * 0x0100 | reg[0x4]) << (portshift - DRAMportshift); + end += (1 << (portshift - DRAMportshift)) - 1; + limit = (reg[0xd]*0x0100 | reg[0xc]) << (portshift - DRAMportshift); + } + } + control2 = v; + break; + + case 0x02: /* Start Address L */ + case 0x03: /* Start Address H */ + start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift); + /*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",reg[0x2], reg[0x3],start );*/ + break; + + case 0x04: /* Stop Address L */ + case 0x05: /* Stop Address H */ + end = (reg[0x5]*0x0100 | reg[0x4]) << (portshift - DRAMportshift); + end += (1 << (portshift - DRAMportshift)) - 1; + /*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",reg[0x4], reg[0x5],end );*/ + break; + + case 0x06: /* Prescale L (ADPCM and Record frq) */ + case 0x07: /* Prescale H */ + break; + + case 0x08: /* ADPCM data */ +/* +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + + /* external memory write */ + if ((portstate & 0xe0) == 0x60) + { + if (memread) + { + now_addr = start << 1; + memread = 0; + } + + /*logerror("YM Delta-T memory write $%08x, v=$%02x\n", now_addr >> 1, v);*/ + + if (now_addr != (end << 1)) + { + write_byte(device, now_addr >> 1, v); + now_addr += 2; /* two nybbles at a time */ + + /* reset BRDY bit in status register, which means we are processing the write */ + if (status_reset_handler && status_change_BRDY_bit) + (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); + + /* setup a timer that will callback us in 10 master clock cycles for Y8950 + * in the callback set the BRDY flag to 1 , which means we have written the data. + * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. + */ + /* set BRDY bit in status register */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); + + } + else + { + /* set EOS bit in status register */ + if (status_set_handler && status_change_EOS_bit) + (status_set_handler)(status_change_which_chip, status_change_EOS_bit); + } + + return; + } + + /* ADPCM synthesis from CPU */ + if ((portstate & 0xe0) == 0x80) + { + CPU_data = v; + + /* Reset BRDY bit in status register, which means we are full of data */ + if (status_reset_handler && status_change_BRDY_bit) + (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); + return; + } + + break; + + case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */ + case 0x0a: /* DELTA-N H */ + delta = (reg[0xa] * 0x0100 | reg[0x9]); + step = uint32_t(double(delta /* *(1<<(YM_DELTAT_SHIFT-16)) */) * freqbase); + /*logerror("DELTAT deltan:09=%2x 0a=%2x\n",reg[0x9], reg[0xa]);*/ + break; + + case 0x0b: /* Output level control (volume, linear) */ + { + const int32_t oldvol = volume; + volume = (v & 0xff) * (output_range / 256) / YM_DELTAT_DECODE_RANGE; +/* v * ((1<<16)>>8) >> 15; +* thus: v * (1<<8) >> 15; +* thus: output_range must be (1 << (15+8)) at least +* v * ((1<<23)>>8) >> 15; +* v * (1<<15) >> 15; +*/ + /*logerror("DELTAT vol = %2x\n",v&0xff);*/ + if (oldvol != 0) + { + adpcml = int(double(adpcml) / double(oldvol) * double(volume)); + } + } + break; + + case 0x0c: /* Limit Address L */ + case 0x0d: /* Limit Address H */ + limit = (reg[0xd] * 0x0100 | reg[0xc]) << (portshift - DRAMportshift); + /*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",reg[0xc], reg[0xd],limit );*/ + break; + } +} + +void YM_DELTAT::ADPCM_Reset(int panidx, int mode, device_t *dev) +{ + device = dev; + now_addr = 0; + now_step = 0; + step = 0; + start = 0; + end = 0; + limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */ + volume = 0; + pan = &output_pointer[panidx]; + acc = 0; + prev_acc = 0; + adpcmd = 127; + adpcml = 0; + emulation_mode = uint8_t(mode); + portstate = (emulation_mode == EMULATION_MODE_YM2610) ? 0x20 : 0; + control2 = (emulation_mode == EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */ + DRAMportshift = dram_rightshift[control2 & 3]; + + /* The flag mask register disables the BRDY after the reset, however + ** as soon as the mask is enabled the flag needs to be set. */ + + /* set BRDY bit in status register */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); +} + +void YM_DELTAT::postload(uint8_t *regs) +{ + /* to keep adpcml */ + volume = 0; + /* update */ + for (int r = 1; r < 16; r++) + ADPCM_Write(r, regs[r]); + reg[0] = regs[0]; + + /* current rom data */ + now_data = read_byte(device, now_addr >> 1); + +} +void YM_DELTAT::savestate(device_t *device) +{ + (void)device; +#ifdef MAME_EMU_SAVE_H + YM_DELTAT *const DELTAT = this; // makes the save name sensible + device->save_item(NAME(DELTAT->portstate)); + device->save_item(NAME(DELTAT->now_addr)); + device->save_item(NAME(DELTAT->now_step)); + device->save_item(NAME(DELTAT->acc)); + device->save_item(NAME(DELTAT->prev_acc)); + device->save_item(NAME(DELTAT->adpcmd)); + device->save_item(NAME(DELTAT->adpcml)); +#endif +} + + +#define YM_DELTAT_Limit(val,max,min) \ +{ \ + if ( val > max ) val = max; \ + else if ( val < min ) val = min; \ +} + +static inline void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT) +{ + uint32_t step; + int data; + + DELTAT->now_step += DELTAT->step; + if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; + DELTAT->now_step &= (1<now_addr == (DELTAT->limit<<1) ) + DELTAT->now_addr = 0; + + if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */ + if( DELTAT->portstate&0x10 ){ + /* repeat start */ + DELTAT->now_addr = DELTAT->start<<1; + DELTAT->acc = 0; + DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; + DELTAT->prev_acc = 0; + }else{ + /* set EOS bit in status register */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_EOS_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); + + /* clear PCM BUSY bit (reflected in status register) */ + DELTAT->PCM_BSY = 0; + + DELTAT->portstate = 0; + DELTAT->adpcml = 0; + DELTAT->prev_acc = 0; + return; + } + } + + if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f; + else + { + DELTAT->now_data = DELTAT->read_byte(DELTAT->device, DELTAT->now_addr>>1); + data = DELTAT->now_data >> 4; + } + + DELTAT->now_addr++; + /* 12-06-2001 JB: */ + /* YM2610 address register is 24 bits wide.*/ + /* The "+1" is there because we use 1 bit more for nibble calculations.*/ + /* WARNING: */ + /* Side effect: we should take the size of the mapped ROM into account */ + DELTAT->now_addr &= ( (1<<(24+1))-1); + + /* store accumulator value */ + DELTAT->prev_acc = DELTAT->acc; + + /* Forecast to next Forecast */ + DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); + YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); + + /* delta to next delta */ + DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; + YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); + + /* ElSemi: Fix interpolator. */ + /*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/ + + }while(--step); + + } + + /* ElSemi: Fix interpolator. */ + DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); + DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); + DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; + + /* output for work of output channels (outd[OPNxxxx])*/ + *(DELTAT->pan) += DELTAT->adpcml; +} + + + +static inline void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT) +{ + uint32_t step; + int data; + + DELTAT->now_step += DELTAT->step; + if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; + DELTAT->now_step &= (1<now_addr&1 ) + { + data = DELTAT->now_data & 0x0f; + + DELTAT->now_data = DELTAT->CPU_data; + + /* after we used CPU_data, we set BRDY bit in status register, + * which means we are ready to accept another byte of data */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); + } + else + { + data = DELTAT->now_data >> 4; + } + + DELTAT->now_addr++; + + /* store accumulator value */ + DELTAT->prev_acc = DELTAT->acc; + + /* Forecast to next Forecast */ + DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); + YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); + + /* delta to next delta */ + DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; + YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); + + + }while(--step); + + } + + /* ElSemi: Fix interpolator. */ + DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); + DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); + DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; + + /* output for work of output channels (outd[OPNxxxx])*/ + *(DELTAT->pan) += DELTAT->adpcml; +} + + + +/* ADPCM B (Delta-T control type) */ +void YM_DELTAT::ADPCM_CALC() +{ +/* +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + + if ( (portstate & 0xe0)==0xa0 ) + { + YM_DELTAT_synthesis_from_external_memory(this); + return; + } + + if ( (portstate & 0xe0)==0x80 ) + { + /* ADPCM synthesis from CPU-managed memory (from reg $08) */ + YM_DELTAT_synthesis_from_CPU_memory(this); /* change output based on data in ADPCM data reg ($08) */ + return; + } + +//todo: ADPCM analysis +// if ( (portstate & 0xe0)==0xc0 ) +// if ( (portstate & 0xe0)==0xe0 ) + + return; +} diff --git a/thirdparty/opnmidi/chips/mamefm/ymdeltat.h b/thirdparty/opnmidi/chips/mamefm/ymdeltat.h new file mode 100644 index 000000000..fefe02e8d --- /dev/null +++ b/thirdparty/opnmidi/chips/mamefm/ymdeltat.h @@ -0,0 +1,91 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski +#ifndef MAME_SOUND_YMDELTAT_H +#define MAME_SOUND_YMDELTAT_H + +#pragma once +#include "emu.h" + +typedef uint8_t (*FM_READBYTE)(device_t *device, offs_t offset); +typedef void (*FM_WRITEBYTE)(device_t *device, offs_t offset, uint8_t data); +typedef void (*STATUS_CHANGE_HANDLER)(void *chip, uint8_t status_bits); + + +/* DELTA-T (adpcm type B) struct */ +struct YM_DELTAT {/* AT: rearranged and tightened structure */ + enum + { + EMULATION_MODE_NORMAL = 0, + EMULATION_MODE_YM2610 = 1 + }; + + FM_READBYTE read_byte; + FM_WRITEBYTE write_byte; + int32_t *output_pointer;/* pointer of output pointers */ + int32_t *pan; /* pan : &output_pointer[pan] */ + double freqbase; +#if 0 + double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */ + double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */ +#endif + uint32_t memory_size; + int output_range; + uint32_t now_addr; /* current address */ + uint32_t now_step; /* correct step */ + uint32_t step; /* step */ + uint32_t start; /* start address */ + uint32_t limit; /* limit address */ + uint32_t end; /* end address */ + uint32_t delta; /* delta scale */ + int32_t volume; /* current volume */ + int32_t acc; /* shift Measurement value*/ + int32_t adpcmd; /* next Forecast */ + int32_t adpcml; /* current value */ + int32_t prev_acc; /* leveling value */ + uint8_t now_data; /* current rom data */ + uint8_t CPU_data; /* current data from reg 08 */ + uint8_t portstate; /* port status */ + uint8_t control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */ + uint8_t portshift; /* address bits shift-left: + ** 8 for YM2610, + ** 5 for Y8950 and YM2608 */ + + uint8_t DRAMportshift; /* address bits shift-right: + ** 0 for ROM and x8bit DRAMs, + ** 3 for x1 DRAMs */ + + uint8_t memread; /* needed for reading/writing external memory */ + + /* handlers and parameters for the status flags support */ + STATUS_CHANGE_HANDLER status_set_handler; + STATUS_CHANGE_HANDLER status_reset_handler; + + /* note that different chips have these flags on different + ** bits of the status register + */ + void * status_change_which_chip; /* this chip id */ + uint8_t status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/ + uint8_t status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */ + uint8_t status_change_ZERO_bit; /* 1 if silence lasts for more than 290 milliseconds on ADPCM recording */ + + /* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above, + ** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608) + */ + uint8_t PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */ + + uint8_t reg[16]; /* adpcm registers */ + uint8_t emulation_mode; /* which chip we're emulating */ + device_t *device; + + /*void BRDY_callback();*/ + + uint8_t ADPCM_Read(); + void ADPCM_Write(int r, int v); + void ADPCM_Reset(int panidx, int mode, device_t *dev); + void ADPCM_CALC(); + + void postload(uint8_t *regs); + void savestate(device_t *device); +}; + +#endif // MAME_SOUND_YMDELTAT_H diff --git a/thirdparty/opnmidi/chips/np2/LICENSE b/thirdparty/opnmidi/chips/np2/LICENSE new file mode 100644 index 000000000..9ce791db1 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/LICENSE @@ -0,0 +1,24 @@ +ã€è‘—作権ã€å…責è¦å®šã€‘ +・本ソースコードã¯ä½œè€…(cisc@retropc.net) ãŒè‘—作権を所有ã—ã¦ã„ã¾ã™ã€‚ + +・本ソースコードã¯ã‚ã‚‹ãŒã¾ã¾ã«æä¾›ã•れるもã®ã§ã‚り, + æš—é»™åŠã³æ˜Žç¤ºçš„ãªä¿è¨¼ã‚’一切å«ã¿ã¾ã›ã‚“. + +・本ソースコードを利用ã—ãŸã“ã¨ï¼Œåˆ©ç”¨ã—ãªã‹ã£ãŸã“ã¨ï¼Œ + 利用ã§ããªã‹ã£ãŸã“ã¨ã«é–¢ã—ã¦ç”Ÿã˜ãŸã‚ã‚‹ã„ã¯ç”Ÿã˜ã‚‹ã¨äºˆæ¸¬ã•れる + æå®³ã«ã¤ã„ã¦ï¼Œä½œè€…ã¯ä¸€åˆ‡è²¬ä»»ã‚’è² ã„ã¾ã›ã‚“. + +・本ソースコードã¯ï¼Œä»¥ä¸‹ã®åˆ¶é™ã‚’満ãŸã™é™ã‚Šè‡ªç”±ã«æ”¹å¤‰ãƒ»çµ„ã¿è¾¼ã¿ãƒ» + é…布・利用ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼Ž + + 1. 本ソフトã®ç”±æ¥(作者, 著作権)を明記ã™ã‚‹ã“ã¨. + 2. é…布ã™ã‚‹éš›ã«ã¯ãƒ•リーソフトã¨ã™ã‚‹ã“ã¨ï¼Ž + 3. 改変ã—ãŸã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’é…布ã™ã‚‹éš›ã¯æ”¹å¤‰å†…容を明示ã™ã‚‹ã“ã¨. + 4. ソースコードをé…布ã™ã‚‹éš›ã«ã¯ã“ã®ãƒ†ã‚­ã‚¹ãƒˆã‚’一切改変ã›ãšã« + ãã®ã¾ã¾æ·»ä»˜ã™ã‚‹ã“ã¨ï¼Ž + +・公開ã®éš›ã«ä½œè€…ã¸ã®é€£çµ¡ã‚’é ‚ã‘れã°å¹¸ã„ã§ã™ï¼Ž + +・商用ソフト(シェアウェアå«ã‚€) ã«æœ¬ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã®ä¸€éƒ¨ï¼Œã¾ãŸã¯ + 全部を組ã¿è¾¼ã‚€éš›ã«ã¯ï¼Œäº‹å‰ã«ä½œè€…ã®åˆæ„ã‚’å¾—ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼Ž + diff --git a/thirdparty/opnmidi/chips/np2/compiler.h b/thirdparty/opnmidi/chips/np2/compiler.h new file mode 100644 index 000000000..65b0d8d58 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/compiler.h @@ -0,0 +1,23 @@ +#ifndef OPNMIDI_FMGEN_COMPILER_H +#define OPNMIDI_FMGEN_COMPILER_H + +#if defined(_MSC_VER) && !defined(FASTCALL) +#define FASTCALL __fastcall +#elif defined(__GNUC__) && !defined(FASTCALL) +#define FASTCALL __attribute__((regparm(2))) +#endif + +#ifndef FASTCALL +#define FASTCALL +#endif +#define SOUNDCALL FASTCALL + +#ifndef MAX_PATH +# ifdef _WIN32 +# define MAX_PATH 256 +# else +# define MAX_PATH 2048 +# endif +#endif + +#endif diff --git a/thirdparty/opnmidi/chips/np2/fmgen_changelog_opnmidi.txt b/thirdparty/opnmidi/chips/np2/fmgen_changelog_opnmidi.txt new file mode 100644 index 000000000..604e0270e --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_changelog_opnmidi.txt @@ -0,0 +1,13 @@ +OPNA FMGen changelog since adding into libOPNMIDI + +- Shift-Jis to UTF8 +- compiler optimize flags +- Added soft-panning to simulate full-panning when chip itself can do 3-state panning only +- fixed warnings and Windows builds +- fixed some mistakes in the code +- fixed C++98 compatibility +- Attempted to fix the SSG-EG behavior + +The detailed changelog can be seen here: +https://github.com/Wohlstand/libOPNMIDI/commits/master/src/chips/np2 + diff --git a/thirdparty/opnmidi/chips/np2/fmgen_diag.h b/thirdparty/opnmidi/chips/np2/fmgen_diag.h new file mode 100644 index 000000000..e0ef26b39 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_diag.h @@ -0,0 +1,15 @@ +#ifndef incl_diag_h +#define incl_diag_h + +#define LOG0(m) void (0) +#define LOG1(m,a) void (0) +#define LOG2(m,a,b) void (0) +#define LOG3(m,a,b,c) void (0) +#define LOG4(m,a,b,c,d) void (0) +#define LOG5(m,a,b,c,d,e) void (0) +#define LOG6(m,a,b,c,d,e,f) void (0) +#define LOG7(m,a,b,c,d,e,f,g) void (0) +#define LOG8(m,a,b,c,d,e,f,g,h) void (0) +#define LOG9(m,a,b,c,d,e,f,g,h,i) void (0) + +#endif // incl_diag_h diff --git a/thirdparty/opnmidi/chips/np2/fmgen_file.cpp b/thirdparty/opnmidi/chips/np2/fmgen_file.cpp new file mode 100644 index 000000000..22b8f9b1c --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_file.cpp @@ -0,0 +1,177 @@ +// $Id: file.cpp,v 1.6 1999/12/28 11:14:05 cisc Exp $ + +#include +#include "fmgen_types.h" +#include "fmgen_headers.h" +#include "fmgen_file.h" + +// --------------------------------------------------------------------------- +// 構築/消滅 +// --------------------------------------------------------------------------- + +FileIO::FileIO() +{ + flags = 0; +} + +FileIO::FileIO(const char* filename, uint flg) +{ + flags = 0; + Open(filename, flg); +} + +FileIO::~FileIO() +{ + Close(); +} + +// --------------------------------------------------------------------------- +// ファイルを開ã +// --------------------------------------------------------------------------- + +bool FileIO::Open(const char* filename, uint flg) +{ + char mode[5] = "rwb"; + Close(); + + strncpy(path, filename, MAX_PATH - 1); + + if(flg & readonly) + strcpy(mode, "rb"); + else { + if(flg & create) + strcpy(mode, "rwb+"); + else + strcpy(mode, "rwb"); + } + + pfile = fopen(filename, mode); + + flags = (flg & readonly) | (pfile == NULL ? 0 : open); + + if (pfile == NULL) + error = file_not_found; + + SetLogicalOrigin(0); + + return !(pfile == NULL); +} + +// --------------------------------------------------------------------------- +// ファイルãŒãªã„å ´åˆã¯ä½œæˆ +// --------------------------------------------------------------------------- + +bool FileIO::CreateNew(const char* filename) +{ + uint flg = create; + + return Open(filename, flg); +} + +// --------------------------------------------------------------------------- +// ファイルを作り直㙠+// --------------------------------------------------------------------------- + +bool FileIO::Reopen(uint flg) +{ + if (!(flags & open)) return false; + if ((flags & readonly) && (flg & create)) return false; + + if (flags & readonly) flg |= readonly; + + return Open(path, flg); +} + +// --------------------------------------------------------------------------- +// ファイルを閉ã˜ã‚‹ +// --------------------------------------------------------------------------- + +void FileIO::Close() +{ + if (GetFlags() & open) + { + fclose(pfile); + flags = 0; + } +} + +// --------------------------------------------------------------------------- +// ファイル殻ã®èª­ã¿å‡ºã— +// --------------------------------------------------------------------------- + +int32 FileIO::Read(void* dest, int32 size) +{ + if (!(GetFlags() & open)) + return -1; + + size_t readsize; + if (!(readsize = fread(dest, 1, static_cast(size), pfile))) + return -1; + return size; +} + +// --------------------------------------------------------------------------- +// ファイルã¸ã®æ›¸ã出㗠+// --------------------------------------------------------------------------- + +int32 FileIO::Write(const void* dest, int32 size) +{ + if (!(GetFlags() & open) || (GetFlags() & readonly)) + return -1; + + size_t writtensize; + if (!(writtensize = fwrite(dest, 1, static_cast(size), pfile))) + return -1; + return static_cast(writtensize); +} + +// --------------------------------------------------------------------------- +// ファイルをシーク +// --------------------------------------------------------------------------- + +bool FileIO::Seek(int32 pos, SeekMethod method) +{ + if (!(GetFlags() & open)) + return false; + + int origin; + switch (method) + { + case begin: + origin = SEEK_SET; + break; + case current: + origin = SEEK_CUR; + break; + case end: + origin = SEEK_END; + break; + default: + return false; + } + + return (fseek(pfile, pos, origin) != 0); +} + +// --------------------------------------------------------------------------- +// ファイルã®ä½ç½®ã‚’å¾—ã‚‹ +// --------------------------------------------------------------------------- + +int32 FileIO::Tellp() +{ + if (!(GetFlags() & open)) + return 0; + + return static_cast(ftell(pfile)); +} + +// --------------------------------------------------------------------------- +// ç¾åœ¨ã®ä½ç½®ã‚’ファイルã®çµ‚端ã¨ã™ã‚‹ +// --------------------------------------------------------------------------- + +bool FileIO::SetEndOfFile() +{ + if (!(GetFlags() & open)) + return false; + return Seek(0, end); +} diff --git a/thirdparty/opnmidi/chips/np2/fmgen_file.h b/thirdparty/opnmidi/chips/np2/fmgen_file.h new file mode 100644 index 000000000..e54297fa3 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_file.h @@ -0,0 +1,64 @@ +// $Id: file.h,v 1.6 1999/11/26 10:14:09 cisc Exp $ + +#if !defined(win32_file_h) +#define win32_file_h + +#include "fmgen_types.h" + +// --------------------------------------------------------------------------- + +class FileIO +{ +public: + enum Flags + { + open = 0x000001, + readonly = 0x000002, + create = 0x000004 + }; + + enum SeekMethod + { + begin = 0, current = 1, end = 2 + }; + + enum Error + { + success = 0, + file_not_found, + sharing_violation, + unknown = -1 + }; + +public: + FileIO(); + FileIO(const char* filename, uint flg = 0); + virtual ~FileIO(); + + bool Open(const char* filename, uint flg = 0); + bool CreateNew(const char* filename); + bool Reopen(uint flg = 0); + void Close(); + Error GetError() { return error; } + + int32 Read(void* dest, int32 len); + int32 Write(const void* src, int32 len); + bool Seek(int32 fpos, SeekMethod method); + int32 Tellp(); + bool SetEndOfFile(); + + uint GetFlags() { return flags; } + void SetLogicalOrigin(int32 origin) { lorigin = origin; } + +private: + FILE* pfile; + uint flags; + uint32 lorigin; + Error error; + char path[MAX_PATH]; + + FileIO(const FileIO&); + const FileIO& operator=(const FileIO&); +}; + +#endif // diff --git a/thirdparty/opnmidi/chips/np2/fmgen_fmgen.cpp b/thirdparty/opnmidi/chips/np2/fmgen_fmgen.cpp new file mode 100644 index 000000000..d5b30d3be --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_fmgen.cpp @@ -0,0 +1,1156 @@ +// --------------------------------------------------------------------------- +// FM Sound Generator - Core Unit +// Copyright (C) cisc 1998, 2003. +// --------------------------------------------------------------------------- +// $Id: fmgen.cpp,v 1.49 2003/09/02 14:51:04 cisc Exp $ +// --------------------------------------------------------------------------- +// å‚考: +// FM sound generator for M.A.M.E., written by Tatsuyuki Satoh. +// +// 謎: +// OPNB ã® CSM モード(仕様ãŒã‚ˆãã‚ã‹ã‚‰ãªã„) +// +// 制é™: +// ・AR!=31 ã§ SSGEC を使ã†ã¨æ³¢å½¢ãŒå®Ÿéš›ã¨ç•°ãªã‚‹å¯èƒ½æ€§ã‚り +// +// è¬è¾ž: +// Tatsuyuki Satoh ã•ã‚“(fm.c) +// Hiromitsu Shioya ã•ã‚“(ADPCM-A) +// DMP-SOFT. ã•ã‚“(OPNB) +// KAJA ã•ã‚“(test program) +// ã»ã‹æŽ²ç¤ºæ¿ç­‰ã§æ§˜ã€…ãªã”åŠ©è¨€ï¼Œã”æ”¯æ´ã‚’ãŠå¯„ã›ã„ãŸã ã„ãŸçš†æ§˜ã« +// --------------------------------------------------------------------------- + +#include "fmgen_headers.h" +#include "fmgen_misc.h" +#include "fmgen_fmgen.h" +#include "fmgen_fmgeninl.h" + +#define LOGNAME "fmgen" + +// --------------------------------------------------------------------------- + +#define FM_EG_BOTTOM 955 + +// --------------------------------------------------------------------------- +// Table/etc +// +namespace FM +{ + const uint8 Operator::notetable[128] = + { + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 17, 18, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23, + 24, 24, 24, 24, 24, 24, 24, 25, 26, 27, 27, 27, 27, 27, 27, 27, + 28, 28, 28, 28, 28, 28, 28, 29, 30, 31, 31, 31, 31, 31, 31, 31, + }; + + const int8 Operator::dttable[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, + 4, 6, 6, 6, 8, 8, 8, 10, 10, 12, 12, 14, 16, 16, 16, 16, + 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, + 10, 12, 12, 14, 16, 16, 18, 20, 22, 24, 26, 28, 32, 32, 32, 32, + 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 12, 12, 14, + 16, 16, 18, 20, 22, 24, 26, 28, 32, 34, 38, 40, 44, 44, 44, 44, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2, -4, -4, -4, -4, + -4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14,-16,-16,-16,-16, + -2, -2, -2, -2, -4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10, + -10,-12,-12,-14,-16,-16,-18,-20,-22,-24,-26,-28,-32,-32,-32,-32, + -4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14, + -16,-16,-18,-20,-22,-24,-26,-28,-32,-34,-38,-40,-44,-44,-44,-44, + }; + + const int8 Operator::decaytable1[64][8] = + { + {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 0, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1}, {2, 1, 1, 1, 2, 1, 1, 1}, + {2, 1, 2, 1, 2, 1, 2, 1}, {2, 2, 2, 1, 2, 2, 2, 1}, + {2, 2, 2, 2, 2, 2, 2, 2}, {4, 2, 2, 2, 4, 2, 2, 2}, + {4, 2, 4, 2, 4, 2, 4, 2}, {4, 4, 4, 2, 4, 4, 4, 2}, + {4, 4, 4, 4, 4, 4, 4, 4}, {8, 4, 4, 4, 8, 4, 4, 4}, + {8, 4, 8, 4, 8, 4, 8, 4}, {8, 8, 8, 4, 8, 8, 8, 4}, + {16,16,16,16,16,16,16,16}, {16,16,16,16,16,16,16,16}, + {16,16,16,16,16,16,16,16}, {16,16,16,16,16,16,16,16}, + }; + + const int Operator::decaytable2[16] = + { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2047, 2047, 2047, 2047, 2047 + }; + + const int8 Operator::attacktable[64][8] = + { + {-1,-1,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,-1,-1}, + { 4, 4, 4, 4, 4, 4, 4, 4}, { 4, 4, 4, 4, 4, 4, 4, 4}, + { 4, 4, 4, 4, 4, 4, 4, 4}, { 4, 4, 4, 4, 4, 4, 4, 4}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4,-1, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4, 4, 4, 4, 4, 4, 4, 4}, { 3, 4, 4, 4, 3, 4, 4, 4}, + { 3, 4, 3, 4, 3, 4, 3, 4}, { 3, 3, 3, 4, 3, 3, 3, 4}, + { 3, 3, 3, 3, 3, 3, 3, 3}, { 2, 3, 3, 3, 2, 3, 3, 3}, + { 2, 3, 2, 3, 2, 3, 2, 3}, { 2, 2, 2, 3, 2, 2, 2, 3}, + { 2, 2, 2, 2, 2, 2, 2, 2}, { 1, 2, 2, 2, 1, 2, 2, 2}, + { 1, 2, 1, 2, 1, 2, 1, 2}, { 1, 1, 1, 2, 1, 1, 1, 2}, + { 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0 ,0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0 ,0, 0, 0, 0, 0, 0}, + }; + +#if 0 // libOPNMIDI: experimental SSG-EG + const int Operator::ssgenvtable[8][2][3][2] = + { + {{{1, 1}, {1, 1}, {1, 1}}, // 08 + {{0, 1}, {1, 1}, {1, 1}}}, // 08 56~ + {{{0, 1}, {2, 0}, {2, 0}}, // 09 + {{0, 1}, {2, 0}, {2, 0}}}, // 09 + {{{1,-1}, {0, 1}, {1,-1}}, // 10 + {{0, 1}, {1,-1}, {0, 1}}}, // 10 60~ + {{{1,-1}, {0, 0}, {0, 0}}, // 11 + {{0, 1}, {0, 0}, {0, 0}}}, // 11 60~ + {{{2,-1}, {2,-1}, {2,-1}}, // 12 + {{1,-1}, {2,-1}, {2,-1}}}, // 12 56~ + {{{1,-1}, {0, 0}, {0, 0}}, // 13 + {{1,-1}, {0, 0}, {0, 0}}}, // 13 + {{{0, 1}, {1,-1}, {0, 1}}, // 14 + {{1,-1}, {0, 1}, {1,-1}}}, // 14 60~ + {{{0, 1}, {2, 0}, {2, 0}}, // 15 + {{1,-1}, {2, 0}, {2, 0}}}, // 15 60~ + }; +#endif + + // fixed equasion-based tables + int pmtable[2][8][FM_LFOENTS]; + uint amtable[2][4][FM_LFOENTS]; + + static bool tablemade = false; +} + +namespace FM +{ + +// --------------------------------------------------------------------------- +// ãƒ†ãƒ¼ãƒ–ãƒ«ä½œæˆ +// +void MakeLFOTable() +{ + if (tablemade) + return; + + tablemade = true; + + int i; + + static const double pms[2][8] = + { + { 0, 1/360., 2/360., 3/360., 4/360., 6/360., 12/360., 24/360., }, // OPNA +// { 0, 1/240., 2/240., 4/240., 10/240., 20/240., 80/240., 140/240., }, // OPM + { 0, 1/480., 2/480., 4/480., 10/480., 20/480., 80/480., 140/480., }, // OPM +// { 0, 1/960., 2/960., 4/960., 10/960., 20/960., 80/960., 140/960., }, // OPM + }; + // 3 6, 12 30 60 240 420 / 720 + // 1.000963 + // lfofref[level * max * wave]; + // pre = lfofref[level][pms * wave >> 8]; + static const uint8 amt[2][4] = + { + { 31, 6, 4, 3 }, // OPNA + { 31, 2, 1, 0 }, // OPM + }; + + for (int type = 0; type < 2; type++) + { + for (i=0; i<8; i++) + { + double pmb = pms[type][i]; + for (int j=0; j> amt[type][i]) * 2) << 2; + } + } + } +} + + +// --------------------------------------------------------------------------- +// ãƒãƒƒãƒ—内ã§å…±é€šãªéƒ¨åˆ† +// +Chip::Chip() +: ratio_(0), aml_(0), pml_(0), pmv_(0) +//, optype_(typeN) +{ +} + +// クロック・サンプリングレート比ã«ä¾å­˜ã™ã‚‹ãƒ†ãƒ¼ãƒ–ãƒ«ã‚’ä½œæˆ +void Chip::SetRatio(uint ratio) +{ + if (ratio_ != ratio) + { + ratio_ = ratio; + MakeTable(); + } +} + +void Chip::MakeTable() +{ + int h, l; + + // PG Part + static const float dt2lv[4] = { 1.f, 1.414f, 1.581f, 1.732f }; + for (h=0; h<4; h++) + { + assert(2 + FM_RATIOBITS - FM_PGBITS >= 0); + double rr = dt2lv[h] * double(ratio_) / (1 << (2 + FM_RATIOBITS - FM_PGBITS)); + for (l=0; l<16; l++) + { + int mul = l ? l * 2 : 1; + multable_[h][l] = uint(mul * rr); + } + } +} + +void Chip::DataSave(struct ChipData* data) +{ + data->ratio_ = ratio_; + data->aml_ = aml_; + data->pml_ = pml_; + data->pmv_ = pmv_; + memcpy(data->multable_, multable_, sizeof(uint32) * 4 * 16); +} + +void Chip::DataLoad(struct ChipData* data) +{ + ratio_ = data->ratio_; + aml_ = data->aml_; + pml_ = data->pml_; + pmv_ = data->pmv_; + memcpy(multable_, data->multable_, sizeof(uint32) * 4 * 16); +} + +// --------------------------------------------------------------------------- +// Operator +// +bool FM::Operator::tablehasmade = false; +uint FM::Operator::sinetable[1024]; +int32 FM::Operator::cltable[FM_CLENTS]; + +// 構築 +FM::Operator::Operator() +: chip_(0) +{ + if (!tablehasmade) + MakeTable(); + + // EG Part + ar_ = dr_ = sr_ = rr_ = key_scale_rate_ = 0; + ams_ = amtable[0][0]; + mute_ = false; + keyon_ = false; + tl_out_ = false; + ssg_type_ = 0; +#if 1 // libOPNMIDI: experimental SSG-EG + inverted_ = false; + held_ = false; +#endif + + // PG Part + multiple_ = 0; + detune_ = 0; + detune2_ = 0; + + // LFO + ms_ = 0; + +// Reset(); +} + +// åˆæœŸåŒ– +void FM::Operator::Reset() +{ + // EG part + tl_ = tl_latch_ = 127; + ShiftPhase(off); + eg_count_ = 0; + eg_curve_count_ = 0; +#if 1 // libOPNMIDI: experimental SSG-EG + inverted_ = false; + held_ = false; +#else + ssg_phase_ = 0; +#endif + + // PG part + pg_count_ = 0; + + // OP part + out_ = out2_ = 0; + + param_changed_ = true; + PARAMCHANGE(0); +} + +void Operator::MakeTable() +{ + // 対数テーブルã®ä½œæˆ + assert(FM_CLENTS >= 256); + + int* p = cltable; + int i; + for (i=0; i<256; i++) + { + int v = int(floor(pow(2., 13. - i / 256.))); + v = (v + 2) & ~3; + *p++ = v; + *p++ = -v; + } + while (p < cltable + FM_CLENTS) + { + //*p++ = p[-512] / 2; + *p = p[-512] / 2; + p++; + } + +// for (i=0; i<13*256; i++) +// printf("%4d, %d, %d\n", i, cltable[i*2], cltable[i*2+1]); + + // サインテーブルã®ä½œæˆ + double log2 = log(2.); + for (i=0; iGetMulValue(detune2_, multiple_); + pg_diff_lfo_ = pg_diff_ >> 11; + + // EG Part + key_scale_rate_ = bn_ >> (3-ks_); + tl_out_ = mute_ ? 0x3ff : tl_ * 8; + + switch (eg_phase_) + { + case attack: + SetEGRate(static_cast(ar_ ? Min(63, ar_ + key_scale_rate_) : 0)); + break; + case decay: + SetEGRate(static_cast(dr_ ? Min(63, dr_ + key_scale_rate_) : 0)); + eg_level_on_next_phase_ = sl_ * 8; + break; + case sustain: + SetEGRate(static_cast(sr_ ? Min(63, sr_ + key_scale_rate_) : 0)); + break; + case release: + SetEGRate(static_cast(Min(63, rr_ + key_scale_rate_))); + break; + default: + break; + } + + // SSG-EG + inverted_ = false; + held_ = false; + if (ssg_type_ && (eg_phase_ != release)) + { +#if 1 // libOPNMIDI: experimental SSG-EG + inverted_ = (ssg_type_ & 4) != 0; + inverted_ ^= (ssg_type_ & 2) && ar_ != 62; // try to match polarity with nuked OPN +#else + int m = static_cast(ar_ >= ((ssg_type_ == 8 || ssg_type_ == 12) ? 56u : 60u)); + + assert(0 <= ssg_phase_ && ssg_phase_ <= 2); + const int* table = ssgenvtable[ssg_type_ & 7][m][ssg_phase_]; + + ssg_offset_ = table[0] * 0x200; + ssg_vector_ = table[1]; +#endif + } + // LFO + ams_ = amtable[type_][amon_ ? (ms_ >> 4) & 3 : 0]; + EGUpdate(); + + dbgopout_ = 0; + } +} + +void Operator::DataSave(struct OperatorData* data) +{ + data->out_ = out_; + data->out2_ = out2_; + data->in2_ = in2_; + data->dp_ = dp_; + data->detune_ = detune_; + data->detune2_ = detune2_; + data->multiple_ = multiple_; + data->pg_count_ = pg_count_; + data->pg_diff_ = pg_diff_; + data->pg_diff_lfo_ = pg_diff_lfo_; + data->type_ = type_; + data->bn_ = bn_; + data->eg_level_ = eg_level_; + data->eg_level_on_next_phase_ = eg_level_on_next_phase_; + data->eg_count_ = eg_count_; + data->eg_count_diff_ = eg_count_diff_; + data->eg_out_ = eg_out_; + data->tl_out_ = tl_out_; + data->eg_rate_ = eg_rate_; + data->eg_curve_count_ = eg_curve_count_; +#if 0 // libOPNMIDI: experimental SSG-EG + data->ssg_offset_ = ssg_offset_; + data->ssg_vector_ = ssg_vector_; + data->ssg_phase_ = ssg_phase_; +#endif + data->key_scale_rate_ = key_scale_rate_; + data->eg_phase_ = eg_phase_; + data->ms_ = ms_; + data->tl_ = tl_; + data->tl_latch_ = tl_latch_; + data->ar_ = ar_; + data->dr_ = dr_; + data->sr_ = sr_; + data->sl_ = sl_; + data->rr_ = rr_; + data->ks_ = ks_; + data->ssg_type_ = ssg_type_; + data->keyon_ = keyon_; + data->amon_ = amon_; + data->param_changed_ = param_changed_; + data->mute_ = mute_; + data->inverted_ = inverted_; + data->held_ = held_; +} + +void Operator::DataLoad(struct OperatorData* data) +{ + out_ = data->out_; + out2_ = data->out2_; + in2_ = data->in2_; + dp_ = data->dp_; + detune_ = data->detune_; + detune2_ = data->detune2_; + multiple_ = data->multiple_; + pg_count_ = data->pg_count_; + pg_diff_ = data->pg_diff_; + pg_diff_lfo_ = data->pg_diff_lfo_; + type_ = data->type_; + bn_ = data->bn_; + eg_level_ = data->eg_level_; + eg_level_on_next_phase_ = data->eg_level_on_next_phase_; + eg_count_ = data->eg_count_; + eg_count_diff_ = data->eg_count_diff_; + eg_out_ = data->eg_out_; + tl_out_ = data->tl_out_; + eg_rate_ = data->eg_rate_; + eg_curve_count_ = data->eg_curve_count_; +#if 0 // libOPNMIDI: experimental SSG-EG + ssg_offset_ = data->ssg_offset_; + ssg_vector_ = data->ssg_vector_; + ssg_phase_ = data->ssg_phase_; +#endif + key_scale_rate_ = data->key_scale_rate_; + eg_phase_ = data->eg_phase_; + ms_ = data->ms_; + tl_ = data->tl_; + tl_latch_ = data->tl_latch_; + ar_ = data->ar_; + dr_ = data->dr_; + sr_ = data->sr_; + sl_ = data->sl_; + rr_ = data->rr_; + ks_ = data->ks_; + ssg_type_ = data->ssg_type_; + keyon_ = data->keyon_; + amon_ = data->amon_; + param_changed_ = data->param_changed_; + mute_ = data->mute_; + inverted_ = data->inverted_; + held_ = data->held_; + ams_ = amtable[type_][amon_ ? (ms_ >> 4) & 3 : 0]; +} + + +// envelop ã® eg_phase_ 変更 +void Operator::ShiftPhase(EGPhase nextphase) +{ + switch (nextphase) + { + case attack: // Attack Phase + tl_ = tl_latch_; + if (ssg_type_) + { +#if 0 // libOPNMIDI: experimental SSG-EG + ssg_phase_ = ssg_phase_ + 1; + if (ssg_phase_ > 2) + ssg_phase_ = 1; + + int m = static_cast(ar_ >= ((ssg_type_ == 8 || ssg_type_ == 12) ? 56u : 60u)); + + assert(0 <= ssg_phase_ && ssg_phase_ <= 2); + const int* table = ssgenvtable[ssg_type_ & 7][m][ssg_phase_]; + + ssg_offset_ = table[0] * 0x200; + ssg_vector_ = table[1]; +#endif + } + if ((ar_ + key_scale_rate_) < 62) + { + SetEGRate(static_cast(ar_ ? Min(63, ar_ + key_scale_rate_) : 0)); + eg_phase_ = attack; + break; + } + // fall through + case decay: // Decay Phase + if (sl_) + { + eg_level_ = 0; + eg_level_on_next_phase_ = ssg_type_ ? Min(sl_ * 8, 0x200) : sl_ * 8; + + SetEGRate(static_cast(dr_ ? Min(63, dr_ + key_scale_rate_) : 0)); + eg_phase_ = decay; + break; + } + // fall through + case sustain: // Sustain Phase + eg_level_ = sl_ * 8; + eg_level_on_next_phase_ = ssg_type_ ? 0x200 : 0x400; + + SetEGRate(static_cast(sr_ ? Min(63, sr_ + key_scale_rate_) : 0)); + eg_phase_ = sustain; + break; + + case release: // Release Phase + inverted_ = false; + held_ = false; +#if 0 // libOPNMIDI: experimental SSG-EG + if (ssg_type_) + { + eg_level_ = eg_level_ * ssg_vector_ + ssg_offset_; + ssg_vector_ = 1; + ssg_offset_ = 0; + } +#endif + if (eg_phase_ == attack || (eg_level_ < FM_EG_BOTTOM)) //0x400/* && eg_phase_ != off*/)) + { + eg_level_on_next_phase_ = 0x400; + SetEGRate(static_cast(Min(63, rr_ + key_scale_rate_))); + eg_phase_ = release; + break; + } + // fall through + case off: // off + default: + eg_level_ = FM_EG_BOTTOM; + eg_level_on_next_phase_ = FM_EG_BOTTOM; + EGUpdate(); + SetEGRate(0); + eg_phase_ = off; + break; + } +} + +// Block/F-Num +void Operator::SetFNum(uint f) +{ + dp_ = (f & 2047) << ((f >> 11) & 7); + bn_ = notetable[(f >> 7) & 127]; + param_changed_ = true; + PARAMCHANGE(2); +} + +// ï¼‘ã‚µãƒ³ãƒ—ãƒ«åˆæˆ + +// ISample ã‚’ envelop count (2Ï€) ã«å¤‰æ›ã™ã‚‹ã‚·ãƒ•ãƒˆé‡ +#define IS2EC_SHIFT ((20 + FM_PGBITS) - 13) + + +// 入力: s = 20+FM_PGBITS = 29 +#define Sine(s) sinetable[((s) >> (20+FM_PGBITS-FM_OPSINBITS))&(FM_OPSINENTS-1)] +#define SINE(s) sinetable[(s) & (FM_OPSINENTS-1)] + +inline FM::ISample Operator::LogToLin(uint a) +{ +#if 1 // FM_CLENTS < 0xc00 // 400 for TL, 400 for ENV, 400 for LFO. + return (a < FM_CLENTS) ? cltable[a] : 0; +#else + return cltable[a]; +#endif +} + +inline void Operator::EGUpdate() +{ +#if 1 // libOPNMIDI: experimental SSG-EG + int level = eg_level_; + level = (!inverted_) ? level : (512 - level) & 0x3ff; + eg_out_ = Min(tl_out_ + level, 0x3ff) << (1 + 2); +#else + if (!ssg_type_) + { + eg_out_ = Min(tl_out_ + eg_level_, 0x3ff) << (1 + 2); + } + else + { + eg_out_ = Min(tl_out_ + eg_level_ * ssg_vector_ + ssg_offset_, 0x3ff) << (1 + 2); + } +#endif +} + +inline void Operator::SetEGRate(uint rate) +{ + eg_rate_ = rate; + eg_count_diff_ = decaytable2[rate / 4] * chip_->GetRatio(); +} + +// EG 計算 +void FM::Operator::EGCalc() +{ + eg_count_ = (2047 * 3) << FM_RATIOBITS; // ##ã“ã®æ‰‹æŠœãã¯å†ç¾æ€§ã‚’低下ã•ã›ã‚‹ + + if (eg_phase_ == attack) + { + int c = attacktable[eg_rate_][eg_curve_count_ & 7]; + if (c >= 0) + { + eg_level_ -= 1 + (eg_level_ >> c); + if (eg_level_ <= 0) + ShiftPhase(decay); + } + EGUpdate(); + } + else + { + if (!ssg_type_) + { + eg_level_ += decaytable1[eg_rate_][eg_curve_count_ & 7]; + if (eg_level_ >= eg_level_on_next_phase_) + ShiftPhase(EGPhase(eg_phase_+1)); + EGUpdate(); + } + else + { + if (!held_) + eg_level_ += 4 * decaytable1[eg_rate_][eg_curve_count_ & 7]; + else + eg_level_ = (((ssg_type_ & 4) != 0) ^ ((ssg_type_ & 2) != 0)) ? 0 : 1024;; + EGUpdate(); // libOPNMIDI: experimental SSG-EG + if (eg_level_ >= eg_level_on_next_phase_) + { + switch (eg_phase_) + { + case decay: + ShiftPhase(sustain); + break; + case sustain: +#if 1 // libOPNMIDI: experimental SSG-EG + if (ssg_type_ & 1) + { + inverted_ = false; + held_ = true; + } + if (!held_) + { + inverted_ ^= (ssg_type_ & 2) && (ar_ == 62); // try to match polarity with nuked OPN + ShiftPhase(attack); + } +#else + ShiftPhase(attack); +#endif + break; + case release: + ShiftPhase(off); + break; + default: + break; + } + } + } + } + eg_curve_count_++; +} + +inline void FM::Operator::EGStep() +{ + eg_count_ -= eg_count_diff_; + + // EG ã®å¤‰åŒ–ã¯å…¨ã‚¹ãƒ­ãƒƒãƒˆã§åŒæœŸã—ã¦ã„ã‚‹ã¨ã„ã†å™‚ã‚‚ã‚ã‚‹ + if (eg_count_ <= 0) + EGCalc(); +} + +// PG 計算 +// ret:2^(20+PGBITS) / cycle +inline uint32 FM::Operator::PGCalc() +{ + uint32 ret = pg_count_; + pg_count_ += pg_diff_; + dbgpgout_ = ret; + return ret; +} + +inline uint32 FM::Operator::PGCalcL() +{ + uint32 ret = pg_count_; + pg_count_ += pg_diff_ + ((pg_diff_lfo_ * chip_->GetPMV()) >> 5);// & -(1 << (2+IS2EC_SHIFT))); + dbgpgout_ = ret; + return ret /* + pmv * pg_diff_;*/; +} + +// OP 計算 +// in: ISample (最大 8Ï€) +inline FM::ISample FM::Operator::Calc(ISample in) +{ + EGStep(); + out2_ = out_; + + int pgin = PGCalc() >> (20+FM_PGBITS-FM_OPSINBITS); + pgin += in >> (20+FM_PGBITS-FM_OPSINBITS-(2+IS2EC_SHIFT)); + out_ = LogToLin(eg_out_ + SINE(pgin)); + + dbgopout_ = out_; + return out_; +} + +inline FM::ISample FM::Operator::CalcL(ISample in) +{ + EGStep(); + + int pgin = PGCalcL() >> (20+FM_PGBITS-FM_OPSINBITS); + pgin += in >> (20+FM_PGBITS-FM_OPSINBITS-(2+IS2EC_SHIFT)); + out_ = LogToLin(eg_out_ + SINE(pgin) + ams_[chip_->GetAML()]); + + dbgopout_ = out_; + return out_; +} + +inline FM::ISample FM::Operator::CalcN(uint noise) +{ + EGStep(); + + int lv = Max(0, 0x3ff - (tl_out_ + eg_level_)) << 1; + + // noise & 1 ? lv : -lv ã¨ç­‰ä¾¡ + noise = (noise & 1) - 1; + out_ = (lv + noise) ^ noise; + + dbgopout_ = out_; + return out_; +} + +// OP (FB) 計算 +// Self Feedback ã®å¤‰èª¿æœ€å¤§ = 4Ï€ +inline FM::ISample FM::Operator::CalcFB(uint fb) +{ + EGStep(); + + ISample in = out_ + out2_; + out2_ = out_; + + int pgin = PGCalc() >> (20+FM_PGBITS-FM_OPSINBITS); + if (fb < 31) + { + pgin += ((in << (1 + IS2EC_SHIFT)) >> fb) >> (20+FM_PGBITS-FM_OPSINBITS); + } + out_ = LogToLin(eg_out_ + SINE(pgin)); + dbgopout_ = out2_; + + return out2_; +} + +inline FM::ISample FM::Operator::CalcFBL(uint fb) +{ + EGStep(); + + ISample in = out_ + out2_; + out2_ = out_; + + int pgin = PGCalcL() >> (20+FM_PGBITS-FM_OPSINBITS); + if (fb < 31) + { + pgin += ((in << (1 + IS2EC_SHIFT)) >> fb) >> (20+FM_PGBITS-FM_OPSINBITS); + } + + out_ = LogToLin(eg_out_ + SINE(pgin) + ams_[chip_->GetAML()]); + dbgopout_ = out_; + + return out_; +} + +#undef Sine + +// --------------------------------------------------------------------------- +// 4-op Channel +// +const uint8 Channel4::fbtable[8] = { 31, 7, 6, 5, 4, 3, 2, 1 }; +int Channel4::kftable[64]; + +bool Channel4::tablehasmade = false; + + +Channel4::Channel4() +{ + if (!tablehasmade) + MakeTable(); + + SetAlgorithm(0); + pms = pmtable[0][0]; +} + +void Channel4::MakeTable() +{ + // 100/64 cent = 2^(i*100/64*1200) + for (int i=0; i<64; i++) + { + kftable[i] = int(0x10000 * pow(2., i / 768.) ); + } +} + +// リセット +void Channel4::Reset() +{ + op[0].Reset(); + op[1].Reset(); + op[2].Reset(); + op[3].Reset(); +} + +// Calc ã®ç”¨æ„ +int Channel4::Prepare() +{ + op[0].Prepare(); + op[1].Prepare(); + op[2].Prepare(); + op[3].Prepare(); + + pms = pmtable[op[0].type_][op[0].ms_ & 7]; + int key = (op[0].IsOn() | op[1].IsOn() | op[2].IsOn() | op[3].IsOn()) ? 1 : 0; + int lfo = op[0].ms_ & ((op[0].amon_ | op[1].amon_ | op[2].amon_ | op[3].amon_) ? 0x37 : 7) ? 2 : 0; + return key | lfo; +} + +// F-Number/BLOCK を設定 +void Channel4::SetFNum(uint f) +{ + for (int i=0; i<4; i++) + op[i].SetFNum(f); +} + +// KC/KF を設定 +void Channel4::SetKCKF(uint kc, uint kf) +{ + const static uint kctable[16] = + { + 5197, 5506, 5833, 6180, 6180, 6547, 6937, 7349, + 7349, 7786, 8249, 8740, 8740, 9259, 9810, 10394, + }; + + int oct = 19 - ((kc >> 4) & 7); + +//printf("%p", this); + uint kcv = kctable[kc & 0x0f]; + kcv = (kcv + 2) / 4 * 4; +//printf(" %.4x", kcv); + uint dp = kcv * kftable[kf & 0x3f]; +//printf(" %.4x %.4x %.8x", kcv, kftable[kf & 0x3f], dp >> oct); + dp >>= 16 + 3; + dp <<= 16 + 3; + dp >>= oct; + uint bn = (kc >> 2) & 31; + op[0].SetDPBN(dp, bn); + op[1].SetDPBN(dp, bn); + op[2].SetDPBN(dp, bn); + op[3].SetDPBN(dp, bn); +//printf(" %.8x\n", dp); +} + +// キー制御 +void Channel4::KeyControl(uint key) +{ + if (key & 0x1) op[0].KeyOn(); else op[0].KeyOff(); + if (key & 0x2) op[1].KeyOn(); else op[1].KeyOff(); + if (key & 0x4) op[2].KeyOn(); else op[2].KeyOff(); + if (key & 0x8) op[3].KeyOn(); else op[3].KeyOff(); +} + +// アルゴリズムを設定 +void Channel4::SetAlgorithm(uint algo) +{ + static const uint8 table1[8][6] = + { + { 0, 1, 1, 2, 2, 3 }, { 1, 0, 0, 1, 1, 2 }, + { 1, 1, 1, 0, 0, 2 }, { 0, 1, 2, 1, 1, 2 }, + { 0, 1, 2, 2, 2, 1 }, { 0, 1, 0, 1, 0, 1 }, + { 0, 1, 2, 1, 2, 1 }, { 1, 0, 1, 0, 1, 0 }, + }; + + in [0] = &buf[table1[algo][0]]; + out[0] = &buf[table1[algo][1]]; + in [1] = &buf[table1[algo][2]]; + out[1] = &buf[table1[algo][3]]; + in [2] = &buf[table1[algo][4]]; + out[2] = &buf[table1[algo][5]]; + + op[0].ResetFB(); + algo_ = algo; +} + +// åˆæˆ +ISample Channel4::Calc() +{ + int r; + switch (algo_) + { + case 0: + op[2].Calc(op[1].Out()); + op[1].Calc(op[0].Out()); + r = op[3].Calc(op[2].Out()); + op[0].CalcFB(fb); + break; + case 1: + op[2].Calc(op[0].Out() + op[1].Out()); + op[1].Calc(0); + r = op[3].Calc(op[2].Out()); + op[0].CalcFB(fb); + break; + case 2: + op[2].Calc(op[1].Out()); + op[1].Calc(0); + r = op[3].Calc(op[0].Out() + op[2].Out()); + op[0].CalcFB(fb); + break; + case 3: + op[2].Calc(0); + op[1].Calc(op[0].Out()); + r = op[3].Calc(op[1].Out() + op[2].Out()); + op[0].CalcFB(fb); + break; + case 4: + op[2].Calc(0); + r = op[1].Calc(op[0].Out()); + r += op[3].Calc(op[2].Out()); + op[0].CalcFB(fb); + break; + case 5: + r = op[2].Calc(op[0].Out()); + r += op[1].Calc(op[0].Out()); + r += op[3].Calc(op[0].Out()); + op[0].CalcFB(fb); + break; + case 6: + r = op[2].Calc(0); + r += op[1].Calc(op[0].Out()); + r += op[3].Calc(0); + op[0].CalcFB(fb); + break; + case 7: + r = op[2].Calc(0); + r += op[1].Calc(0); + r += op[3].Calc(0); + r += op[0].CalcFB(fb); + break; + default: + assert(false); + r = 0; + break; + } + return r; +} + +// åˆæˆ +ISample Channel4::CalcL() +{ + chip_->SetPMV(pms[chip_->GetPML()]); + + int r; + switch (algo_) + { + case 0: + op[2].CalcL(op[1].Out()); + op[1].CalcL(op[0].Out()); + r = op[3].CalcL(op[2].Out()); + op[0].CalcFBL(fb); + break; + case 1: + op[2].CalcL(op[0].Out() + op[1].Out()); + op[1].CalcL(0); + r = op[3].CalcL(op[2].Out()); + op[0].CalcFBL(fb); + break; + case 2: + op[2].CalcL(op[1].Out()); + op[1].CalcL(0); + r = op[3].CalcL(op[0].Out() + op[2].Out()); + op[0].CalcFBL(fb); + break; + case 3: + op[2].CalcL(0); + op[1].CalcL(op[0].Out()); + r = op[3].CalcL(op[1].Out() + op[2].Out()); + op[0].CalcFBL(fb); + break; + case 4: + op[2].CalcL(0); + r = op[1].CalcL(op[0].Out()); + r += op[3].CalcL(op[2].Out()); + op[0].CalcFBL(fb); + break; + case 5: + r = op[2].CalcL(op[0].Out()); + r += op[1].CalcL(op[0].Out()); + r += op[3].CalcL(op[0].Out()); + op[0].CalcFBL(fb); + break; + case 6: + r = op[2].CalcL(0); + r += op[1].CalcL(op[0].Out()); + r += op[3].CalcL(0); + op[0].CalcFBL(fb); + break; + case 7: + r = op[2].CalcL(0); + r += op[1].CalcL(0); + r += op[3].CalcL(0); + r += op[0].CalcFBL(fb); + break; + default: + assert(false); + r = 0; + break; + } + return r; +} + +// åˆæˆ +ISample Channel4::CalcN(uint noise) +{ + buf[1] = buf[2] = buf[3] = 0; + + buf[0] = op[0].out_; op[0].CalcFB(fb); + *out[0] += op[1].Calc(*in[0]); + *out[1] += op[2].Calc(*in[1]); + int o = op[3].out_; + op[3].CalcN(noise); + return *out[2] + o; +} + +// åˆæˆ +ISample Channel4::CalcLN(uint noise) +{ + chip_->SetPMV(pms[chip_->GetPML()]); + buf[1] = buf[2] = buf[3] = 0; + + buf[0] = op[0].out_; op[0].CalcFBL(fb); + *out[0] += op[1].CalcL(*in[0]); + *out[1] += op[2].CalcL(*in[1]); + int o = op[3].out_; + op[3].CalcN(noise); + return *out[2] + o; +} + +void Channel4::DataSave(struct Channel4Data* data) { + data->fb = fb; + memcpy(data->buf, buf, sizeof(int) * 4); + data->algo_ = algo_; + for(int i = 0; i < 4; i++) { + op[i].DataSave(&data->op[i]); + } +} + +void Channel4::DataLoad(struct Channel4Data* data) { + fb = data->fb; + memcpy(buf, data->buf, sizeof(int) * 4); + algo_ = data->algo_; + SetAlgorithm(algo_); + for(int i = 0; i < 4; i++) { + op[i].DataLoad(&data->op[i]); + } + pms = pmtable[op[0].type_][op[0].ms_ & 7]; +} +} // namespace FM diff --git a/thirdparty/opnmidi/chips/np2/fmgen_fmgen.h b/thirdparty/opnmidi/chips/np2/fmgen_fmgen.h new file mode 100644 index 000000000..398173cbb --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_fmgen.h @@ -0,0 +1,342 @@ +// --------------------------------------------------------------------------- +// FM Sound Generator +// Copyright (C) cisc 1998, 2001. +// --------------------------------------------------------------------------- +// $Id: fmgen.h,v 1.37 2003/08/25 13:33:11 cisc Exp $ + +#ifndef FM_GEN_H +#define FM_GEN_H + +#include "fmgen_types.h" + +// --------------------------------------------------------------------------- +// 出力サンプルã®åž‹ +// +// libOPNMIDI: change int32 to int16 +#define FM_SAMPLETYPE int16 // int16 or int32 + +// --------------------------------------------------------------------------- +// 定数ãã®ï¼‘ +// é™çš„テーブルã®ã‚µã‚¤ã‚º + +#define FM_LFOBITS 8 // 変更ä¸å¯ +#define FM_TLBITS 7 + +// --------------------------------------------------------------------------- + +#define FM_TLENTS (1 << FM_TLBITS) +#define FM_LFOENTS (1 << FM_LFOBITS) +#define FM_TLPOS (FM_TLENTS/4) + +// サイン波ã®ç²¾åº¦ã¯ 2^(1/256) +#define FM_CLENTS (0x1000 * 2) // sin + TL + LFO + +// --------------------------------------------------------------------------- + +namespace FM +{ + // Types ---------------------------------------------------------------- + typedef FM_SAMPLETYPE Sample; + typedef int32 ISample; + + enum OpType { typeN=0, typeM=1 }; + enum EGPhase { next, attack, decay, sustain, release, off }; + + void StoreSample(ISample& dest, int data); + + class Chip; + struct ChipData; + + // Operator ------------------------------------------------------------- + struct OperatorData { + ISample out_; + ISample out2_; + ISample in2_; + uint dp_; + uint detune_; + uint detune2_; + uint multiple_; + uint32 pg_count_; + uint32 pg_diff_; + int32 pg_diff_lfo_; + OpType type_; + uint bn_; + int eg_level_; + int eg_level_on_next_phase_; + int eg_count_; + int eg_count_diff_; + int eg_out_; + int tl_out_; + int eg_rate_; + int eg_curve_count_; +#if 0 // libOPNMIDI: experimental SSG-EG + int ssg_offset_; + int ssg_vector_; + int ssg_phase_; +#endif + uint key_scale_rate_; + EGPhase eg_phase_; + uint ms_; + + uint tl_; + uint tl_latch_; + uint ar_; + uint dr_; + uint sr_; + uint sl_; + uint rr_; + uint ks_; + uint ssg_type_; + + bool keyon_; + bool amon_; + bool param_changed_; + bool mute_; + bool inverted_; + bool held_; + }; + + class Operator + { + public: + Operator(); + void SetChip(Chip* chip) { chip_ = chip; } + + static void MakeTimeTable(uint ratio); + + ISample Calc(ISample in); + ISample CalcL(ISample in); + ISample CalcFB(uint fb); + ISample CalcFBL(uint fb); + ISample CalcN(uint noise); + void Prepare(); + void KeyOn(); + void KeyOff(); + void Reset(); + void ResetFB(); + int IsOn(); + + void SetDT(uint dt); + void SetDT2(uint dt2); + void SetMULTI(uint multi); + void SetTL(uint tl, bool csm); + void SetKS(uint ks); + void SetAR(uint ar); + void SetDR(uint dr); + void SetSR(uint sr); + void SetRR(uint rr); + void SetSL(uint sl); + void SetSSGEC(uint ssgec); + void SetFNum(uint fnum); + void SetDPBN(uint dp, uint bn); + void SetMode(bool modulator); + void SetAMON(bool on); + void SetMS(uint ms); + void Mute(bool); + +// static void SetAML(uint l); +// static void SetPML(uint l); + + int Out() { return out_; } + + int dbgGetIn2() { return in2_; } + void dbgStopPG() { pg_diff_ = 0; pg_diff_lfo_ = 0; } + + void DataSave(struct OperatorData* data); + void DataLoad(struct OperatorData* data); + + private: + typedef uint32 Counter; + + Chip* chip_; + ISample out_, out2_; + ISample in2_; + + // Phase Generator ------------------------------------------------------ + uint32 PGCalc(); + uint32 PGCalcL(); + + uint dp_; // ΔP + uint detune_; // Detune + uint detune2_; // DT2 + uint multiple_; // Multiple + uint32 pg_count_; // Phase ç¾åœ¨å€¤ + uint32 pg_diff_; // Phase 差分値 + int32 pg_diff_lfo_; // Phase 差分値 >> x + + // Envelop Generator --------------------------------------------------- + void EGCalc(); + void EGStep(); + void ShiftPhase(EGPhase nextphase); + void SSGShiftPhase(int mode); + void SetEGRate(uint); + void EGUpdate(); + int FBCalc(int fb); + ISample LogToLin(uint a); + + + OpType type_; // OP ã®ç¨®é¡ž (M, N...) + uint bn_; // Block/Note + int eg_level_; // EG ã®å‡ºåЛ値 + int eg_level_on_next_phase_; // 次㮠eg_phase_ ã«ç§»ã‚‹å€¤ + int eg_count_; // EG ã®æ¬¡ã®å¤‰ç§»ã¾ã§ã®æ™‚é–“ + int eg_count_diff_; // eg_count_ ã®å·®åˆ† + int eg_out_; // EG+TL ã‚’åˆã‚ã›ãŸå‡ºåЛ値 + int tl_out_; // TL 分ã®å‡ºåЛ値 +// int pm_depth_; // PM depth +// int am_depth_; // AM depth + int eg_rate_; + int eg_curve_count_; +#if 0 // libOPNMIDI: experimental SSG-EG + int ssg_offset_; + int ssg_vector_; + int ssg_phase_; +#endif + + uint key_scale_rate_; // key scale rate + EGPhase eg_phase_; + uint* ams_; + uint ms_; + + uint tl_; // Total Level (0-127) + uint tl_latch_; // Total Level Latch (for CSM mode) + uint ar_; // Attack Rate (0-63) + uint dr_; // Decay Rate (0-63) + uint sr_; // Sustain Rate (0-63) + uint sl_; // Sustain Level (0-127) + uint rr_; // Release Rate (0-63) + uint ks_; // Keyscale (0-3) + uint ssg_type_; // SSG-Type Envelop Control + + bool keyon_; + bool amon_; // enable Amplitude Modulation + bool param_changed_; // ãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãŒæ›´æ–°ã•れ㟠+ bool mute_; + bool inverted_; + bool held_; + + // Tables --------------------------------------------------------------- + static Counter rate_table[16]; + static uint32 multable[4][16]; + + static const uint8 notetable[128]; + static const int8 dttable[256]; + static const int8 decaytable1[64][8]; + static const int decaytable2[16]; + static const int8 attacktable[64][8]; + static const int ssgenvtable[8][2][3][2]; + + static uint sinetable[1024]; + static int32 cltable[FM_CLENTS]; + + static bool tablehasmade; + static void MakeTable(); + + + + // friends -------------------------------------------------------------- + friend class Channel4; + + public: + int dbgopout_; + int dbgpgout_; + static const int32* dbgGetClTable() { return cltable; } + static const uint* dbgGetSineTable() { return sinetable; } + }; + + // 4-op Channel --------------------------------------------------------- + struct Channel4Data { + uint fb; + int buf[4]; + int algo_; + struct OperatorData op[4]; + }; + + class Channel4 + { + public: + Channel4(); + void SetChip(Chip* chip); + void SetType(OpType type); + + ISample Calc(); + ISample CalcL(); + ISample CalcN(uint noise); + ISample CalcLN(uint noise); + void SetFNum(uint fnum); + void SetFB(uint fb); + void SetKCKF(uint kc, uint kf); + void SetAlgorithm(uint algo); + int Prepare(); + void KeyControl(uint key); + void Reset(); + void SetMS(uint ms); + void Mute(bool); + void Refresh(); + + void dbgStopPG() { for (int i=0; i<4; i++) op[i].dbgStopPG(); } + + void DataSave(struct Channel4Data* data); + void DataLoad(struct Channel4Data* data); + + private: + static const uint8 fbtable[8]; + uint fb; + int buf[4]; + int* in[3]; // å„ OP ã®å…¥åŠ›ãƒã‚¤ãƒ³ã‚¿ + int* out[3]; // å„ OP ã®å‡ºåŠ›ãƒã‚¤ãƒ³ã‚¿ + int* pms; + int algo_; + Chip* chip_; + + static void MakeTable(); + + static bool tablehasmade; + static int kftable[64]; + + + public: + Operator op[4]; + }; + + // Chip resource + struct ChipData { + uint ratio_; + uint aml_; + uint pml_; + int pmv_; + OpType optype_; + uint32 multable_[4][16]; + }; + + class Chip + { + public: + Chip(); + void SetRatio(uint ratio); + void SetAML(uint l); + void SetPML(uint l); + void SetPMV(int pmv) { pmv_ = pmv; } + + uint32 GetMulValue(uint dt2, uint mul) { return multable_[dt2][mul]; } + uint GetAML() { return aml_; } + uint GetPML() { return pml_; } + int GetPMV() { return pmv_; } + uint GetRatio() { return ratio_; } + + void DataSave(struct ChipData* data); + void DataLoad(struct ChipData* data); + + private: + void MakeTable(); + + uint ratio_; + uint aml_; + uint pml_; + int pmv_; +// OpType optype_; + uint32 multable_[4][16]; + }; +} + +#endif // FM_GEN_H diff --git a/thirdparty/opnmidi/chips/np2/fmgen_fmgeninl.h b/thirdparty/opnmidi/chips/np2/fmgen_fmgeninl.h new file mode 100644 index 000000000..35ca7b2f1 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_fmgeninl.h @@ -0,0 +1,274 @@ +// --------------------------------------------------------------------------- +// FM Sound Generator +// Copyright (C) cisc 1998, 2003. +// --------------------------------------------------------------------------- +// $Id: fmgeninl.h,v 1.26 2003/06/12 13:14:36 cisc Exp $ + +#ifndef FM_GEN_INL_H +#define FM_GEN_INL_H + +// --------------------------------------------------------------------------- +// 定数ãã®ï¼’ +// +#define FM_PI 3.14159265358979323846 + +#define FM_SINEPRESIS 2 // EGã¨ã‚µã‚¤ãƒ³æ³¢ã®ç²¾åº¦ã®å·® 0(低)-2(高) + + +#define FM_OPSINBITS 10 +#define FM_OPSINENTS (1 << FM_OPSINBITS) + +#define FM_EGCBITS 18 // eg ã® count ã®ã‚·ãƒ•ト値 +#define FM_LFOCBITS 14 + +#ifdef FM_TUNEBUILD + #define FM_PGBITS 2 + #define FM_RATIOBITS 0 +#else + #define FM_PGBITS 9 + #define FM_RATIOBITS 7 // 8-12 ãらã„ã¾ã§ï¼Ÿ +#endif + +#define FM_EGBITS 16 + +//extern int paramcount[]; +//#define PARAMCHANGE(i) paramcount[i]++; +#define PARAMCHANGE(i) + +namespace FM +{ + +// --------------------------------------------------------------------------- +// Operator +// +// フィードãƒãƒƒã‚¯ãƒãƒƒãƒ•ァをクリア +inline void Operator::ResetFB() +{ + out_ = out2_ = 0; +} + +// キーオン +inline void Operator::KeyOn() +{ + if (!keyon_) + { + keyon_ = true; + held_ = false; + if (eg_phase_ == off || eg_phase_ == release) + { +#if 1 // libOPNMIDI: experimental SSG-EG + inverted_ = (ssg_type_ & 4) != 0; + inverted_ ^= (ssg_type_ & 2) && ar_ != 62; // try to match polarity with nuked OPN +#else + ssg_phase_ = -1; +#endif + ShiftPhase(attack); + EGUpdate(); + in2_ = out_ = out2_ = 0; + pg_count_ = 0; + } + } +} + +// キーオフ +inline void Operator::KeyOff() +{ + if (keyon_) + { + keyon_ = false; + ShiftPhase(release); + } +} + +// オペレータã¯ç¨¼åƒä¸­ã‹ï¼Ÿ +inline int Operator::IsOn() +{ + return eg_phase_ - off; +} + +// Detune (0-7) +inline void Operator::SetDT(uint dt) +{ + detune_ = dt * 0x20, param_changed_ = true; + PARAMCHANGE(4); +} + +// DT2 (0-3) +inline void Operator::SetDT2(uint dt2) +{ + detune2_ = dt2 & 3, param_changed_ = true; + PARAMCHANGE(5); +} + +// Multiple (0-15) +inline void Operator::SetMULTI(uint mul) +{ + multiple_ = mul, param_changed_ = true; + PARAMCHANGE(6); +} + +// Total Level (0-127) (0.75dB step) +inline void Operator::SetTL(uint tl, bool csm) +{ + if (!csm) + { + tl_ = tl, param_changed_ = true; + PARAMCHANGE(7); + } + tl_latch_ = tl; +} + +// Attack Rate (0-63) +inline void Operator::SetAR(uint ar) +{ + ar_ = ar; + param_changed_ = true; + PARAMCHANGE(8); +} + +// Decay Rate (0-63) +inline void Operator::SetDR(uint dr) +{ + dr_ = dr; + param_changed_ = true; + PARAMCHANGE(9); +} + +// Sustain Rate (0-63) +inline void Operator::SetSR(uint sr) +{ + sr_ = sr; + param_changed_ = true; + PARAMCHANGE(10); +} + +// Sustain Level (0-127) +inline void Operator::SetSL(uint sl) +{ + sl_ = sl; + param_changed_ = true; + PARAMCHANGE(11); +} + +// Release Rate (0-63) +inline void Operator::SetRR(uint rr) +{ + rr_ = rr; + param_changed_ = true; + PARAMCHANGE(12); +} + +// Keyscale (0-3) +inline void Operator::SetKS(uint ks) +{ + ks_ = ks; + param_changed_ = true; + PARAMCHANGE(13); +} + +// SSG-type Envelop (0-15) +inline void Operator::SetSSGEC(uint ssgec) +{ + if (ssgec & 8) + ssg_type_ = ssgec; + else + ssg_type_ = 0; +} + +inline void Operator::SetAMON(bool amon) +{ + amon_ = amon; + param_changed_ = true; + PARAMCHANGE(14); +} + +inline void Operator::Mute(bool mute) +{ + mute_ = mute; + param_changed_ = true; + PARAMCHANGE(15); +} + +inline void Operator::SetMS(uint ms) +{ + ms_ = ms; + param_changed_ = true; + PARAMCHANGE(16); +} + +// --------------------------------------------------------------------------- +// 4-op Channel + +// オペレータã®ç¨®é¡ž (LFO) を設定 +inline void Channel4::SetType(OpType type) +{ + for (int i=0; i<4; i++) + op[i].type_ = type; +} + +// セルフ・フィードãƒãƒƒã‚¯ãƒ¬ãƒ¼ãƒˆã®è¨­å®š (0-7) +inline void Channel4::SetFB(uint feedback) +{ + fb = fbtable[feedback]; +} + +// OPNA ç³» LFO ã®è¨­å®š +inline void Channel4::SetMS(uint ms) +{ + op[0].SetMS(ms); + op[1].SetMS(ms); + op[2].SetMS(ms); + op[3].SetMS(ms); +} + +// ãƒãƒ£ãƒ³ãƒãƒ«ãƒ»ãƒžã‚¹ã‚¯ +inline void Channel4::Mute(bool m) +{ + for (int i=0; i<4; i++) + op[i].Mute(m); +} + +// 内部パラメータをå†è¨ˆç®— +inline void Channel4::Refresh() +{ + for (int i=0; i<4; i++) + op[i].param_changed_ = true; + PARAMCHANGE(3); +} + +inline void Channel4::SetChip(Chip* chip) +{ + chip_ = chip; + for (int i=0; i<4; i++) + op[i].SetChip(chip); +} + +// --------------------------------------------------------------------------- +// +// +inline void StoreSample(Sample& dest, ISample data) +{ + if (sizeof(Sample) == 2) + dest = (Sample) Limit(dest + data, 0x7fff, -0x8000); + else + dest += data; +} + + +// --------------------------------------------------------------------------- +// AM ã®ãƒ¬ãƒ™ãƒ«ã‚’設定 +inline void Chip::SetAML(uint l) +{ + aml_ = l & (FM_LFOENTS - 1); +} + +// PM ã®ãƒ¬ãƒ™ãƒ«ã‚’設定 +inline void Chip::SetPML(uint l) +{ + pml_ = l & (FM_LFOENTS - 1); +} + + +} + +#endif // FM_GEN_INL_H diff --git a/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.cpp b/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.cpp new file mode 100644 index 000000000..1ec2f4daf --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.cpp @@ -0,0 +1,217 @@ +// --------------------------------------------------------------------------- +// FM sound generator common timer module +// Copyright (C) cisc 1998, 2000. +// --------------------------------------------------------------------------- +// $Id: fmtimer.cpp,v 1.1 2000/09/08 13:45:56 cisc Exp $ + +#include "fmgen_headers.h" +#include "fmgen_fmtimer.h" + +using namespace FM; + +// --------------------------------------------------------------------------- +// タイマー制御 +// +void Timer::SetTimerControl(uint data) +{ + uint tmp = regtc ^ data; + regtc = uint8(data); + + if (data & 0x10) + ResetStatus(1); + if (data & 0x20) + ResetStatus(2); + + if (tmp & 0x01) + timera_count = (data & 1) ? timera : 0; + if (tmp & 0x02) + timerb_count = (data & 2) ? timerb : 0; +} + +#if 1 + +// --------------------------------------------------------------------------- +// タイマーA 周期設定 +// +void Timer::SetTimerA(uint addr, uint data) +{ + uint tmp; + regta[addr & 1] = uint8(data); + tmp = (regta[0] << 2) + (regta[1] & 3); + timera = (1024-tmp) * timer_step; +// LOG2("Timer A = %d %d us\n", tmp, timera >> 16); +} + +// --------------------------------------------------------------------------- +// タイマーB 周期設定 +// +void Timer::SetTimerB(uint data) +{ + timerb = (256-data) * timer_step; +// LOG2("Timer B = %d %d us\n", data, timerb >> 12); +} + +// --------------------------------------------------------------------------- +// ã‚¿ã‚¤ãƒžãƒ¼æ™‚é–“å‡¦ç† +// +bool Timer::Count(int32 us) +{ + bool event = false; + + if (timera_count) + { + timera_count -= us << 16; + if (timera_count <= 0) + { + event = true; + TimerA(); + + while (timera_count <= 0) + timera_count += timera; + + if (regtc & 4) + SetStatus(1); + } + } + if (timerb_count) + { + timerb_count -= us << 12; + if (timerb_count <= 0) + { + event = true; + while (timerb_count <= 0) + timerb_count += timerb; + + if (regtc & 8) + SetStatus(2); + } + } + return event; +} + +// --------------------------------------------------------------------------- +// 次ã«ã‚¿ã‚¤ãƒžãƒ¼ãŒç™ºç”Ÿã™ã‚‹ã¾ã§ã®æ™‚間を求ã‚ã‚‹ +// +int32 Timer::GetNextEvent() +{ + uint32 ta = ((timera_count + 0xffff) >> 16) - 1; + uint32 tb = ((timerb_count + 0xfff) >> 12) - 1; + return (ta < tb ? ta : tb) + 1; +} + +// --------------------------------------------------------------------------- +void Timer::DataSave(struct TimerData* data) +{ + data->status = status; + data->regtc = regtc; + data->regta[0] = regta[0]; + data->regta[1] = regta[1]; + data->timera = timera; + data->timera_count = timera_count; + data->timerb = timerb; + data->timerb_count = timerb_count; + data->timer_step = timer_step; +} + +// --------------------------------------------------------------------------- +void Timer::DataLoad(struct TimerData* data) +{ + status = data->status; + regtc = data->regtc; + regta[0] = data->regta[0]; + regta[1] = data->regta[1]; + timera = data->timera; + timera_count = data->timera_count; + timerb = data->timerb; + timerb_count = data->timerb_count; + timer_step = data->timer_step; +} + +// --------------------------------------------------------------------------- +// タイマー基準値設定 +// +void Timer::SetTimerBase(uint clock) +{ + timer_step = int32(1000000. * 65536 / clock); +} + +#else + +// --------------------------------------------------------------------------- +// タイマーA 周期設定 +// +void Timer::SetTimerA(uint addr, uint data) +{ + regta[addr & 1] = uint8(data); + timera = (1024 - ((regta[0] << 2) + (regta[1] & 3))) << 16; +} + +// --------------------------------------------------------------------------- +// タイマーB 周期設定 +// +void Timer::SetTimerB(uint data) +{ + timerb = (256-data) << (16 + 4); +} + +// --------------------------------------------------------------------------- +// ã‚¿ã‚¤ãƒžãƒ¼æ™‚é–“å‡¦ç† +// +bool Timer::Count(int32 us) +{ + bool event = false; + + int tick = us * timer_step; + + if (timera_count) + { + timera_count -= tick; + if (timera_count <= 0) + { + event = true; + TimerA(); + + while (timera_count <= 0) + timera_count += timera; + + if (regtc & 4) + SetStatus(1); + } + } + if (timerb_count) + { + timerb_count -= tick; + if (timerb_count <= 0) + { + event = true; + while (timerb_count <= 0) + timerb_count += timerb; + + if (regtc & 8) + SetStatus(2); + } + } + return event; +} + +// --------------------------------------------------------------------------- +// 次ã«ã‚¿ã‚¤ãƒžãƒ¼ãŒç™ºç”Ÿã™ã‚‹ã¾ã§ã®æ™‚間を求ã‚ã‚‹ +// +int32 Timer::GetNextEvent() +{ + uint32 ta = timera_count - 1; + uint32 tb = timerb_count - 1; + uint32 t = (ta < tb ? ta : tb) + 1; + + return (t+timer_step-1) / timer_step; +} + +// --------------------------------------------------------------------------- +// タイマー基準値設定 +// +void Timer::SetTimerBase(uint clock) +{ + timer_step = clock * 1024 / 15625; +} + +#endif diff --git a/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.h b/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.h new file mode 100644 index 000000000..880498801 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.h @@ -0,0 +1,67 @@ +// --------------------------------------------------------------------------- +// FM sound generator common timer module +// Copyright (C) cisc 1998, 2000. +// --------------------------------------------------------------------------- +// $Id: fmtimer.h,v 1.2 2003/04/22 13:12:53 cisc Exp $ + +#ifndef FM_TIMER_H +#define FM_TIMER_H + +#include "fmgen_types.h" + +// --------------------------------------------------------------------------- + +namespace FM +{ + struct TimerData { + uint8 status; + uint8 regtc; + uint8 regta[2]; + int32 timera, timera_count; + int32 timerb, timerb_count; + int32 timer_step; + }; + + class Timer + { + public: + void Reset(); + bool Count(int32 us); + int32 GetNextEvent(); + + void DataSave(struct TimerData* data); + void DataLoad(struct TimerData* data); + + protected: + virtual void SetStatus(uint bit) = 0; + virtual void ResetStatus(uint bit) = 0; + + void SetTimerBase(uint clock); + void SetTimerA(uint addr, uint data); + void SetTimerB(uint data); + void SetTimerControl(uint data); + + uint8 status; + uint8 regtc; + + private: + virtual void TimerA() {} + uint8 regta[2]; + + int32 timera, timera_count; + int32 timerb, timerb_count; + int32 timer_step; + }; + +// --------------------------------------------------------------------------- +// ‰Šú‰» +// +inline void Timer::Reset() +{ + timera_count = 0; + timerb_count = 0; +} + +} // namespace FM + +#endif // FM_TIMER_H diff --git a/thirdparty/opnmidi/chips/np2/fmgen_headers.h b/thirdparty/opnmidi/chips/np2/fmgen_headers.h new file mode 100644 index 000000000..f677a0930 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_headers.h @@ -0,0 +1,10 @@ +#ifndef WIN_HEADERS_H +#define WIN_HEADERS_H + +#include +#include +#include +#include +#include + +#endif // WIN_HEADERS_H diff --git a/thirdparty/opnmidi/chips/np2/fmgen_misc.h b/thirdparty/opnmidi/chips/np2/fmgen_misc.h new file mode 100644 index 000000000..e654fbd78 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_misc.h @@ -0,0 +1,69 @@ +// --------------------------------------------------------------------------- +// misc.h +// Copyright (C) cisc 1998, 1999. +// --------------------------------------------------------------------------- +// $Id: misc.h,v 1.5 2002/05/31 09:45:20 cisc Exp $ + +#ifndef MISC_H +#define MISC_H + +inline int Max(int x, int y) { return (x > y) ? x : y; } +inline int Min(int x, int y) { return (x < y) ? x : y; } +inline int Abs(int x) { return x >= 0 ? x : -x; } + +inline int Limit(int v, int max, int min) +{ + return v > max ? max : (v < min ? min : v); +} + +inline unsigned int BSwap(unsigned int a) +{ + return (a >> 24) | ((a >> 8) & 0xff00) | ((a << 8) & 0xff0000) | (a << 24); +} + +inline unsigned int NtoBCD(unsigned int a) +{ + return ((a / 10) << 4) + (a % 10); +} + +inline unsigned int BCDtoN(unsigned int v) +{ + return (v >> 4) * 10 + (v & 15); +} + + +template +inline T gcd(T x, T y) +{ + T t; + while (y) + { + t = x % y; + x = y; + y = t; + } + return x; +} + + +template +T bessel0(T x) +{ + T p, r, s; + + r = 1.0; + s = 1.0; + p = (x / 2.0) / s; + + while (p > 1.0E-10) + { + r += p * p; + s += 1.0; + p *= (x / 2.0) / s; + } + return r; +} + + +#endif // MISC_H + diff --git a/thirdparty/opnmidi/chips/np2/fmgen_opna.cpp b/thirdparty/opnmidi/chips/np2/fmgen_opna.cpp new file mode 100644 index 000000000..d6e3812f3 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_opna.cpp @@ -0,0 +1,2142 @@ +// --------------------------------------------------------------------------- +// OPN/A/B interface with ADPCM support +// Copyright (C) cisc 1998, 2001. +// --------------------------------------------------------------------------- +// $Id: opna.cpp,v 1.68 2003/06/12 14:03:44 cisc Exp $ + +#include "fmgen_types.h" +#include "fmgen_headers.h" +#include "fmgen_misc.h" +#include "fmgen_opna.h" +#include "fmgen_fmgeninl.h" + +#define BUILD_OPN +#define BUILD_OPNA +#define BUILD_OPNB + + +// TOFIX: +// OPN ch3 �����Prepare�̑ÎۂƂȂ��Ă��܂���Q + + +// --------------------------------------------------------------------------- +// OPNA: ADPCM �f�[�^�̊i�[�����̈Ⴂ (8bit/1bit) ���G�~�����[�g���Ȃ� +// ���̃I�v�V������L���ɂ���� ADPCM �������ւ̃A�N�Z�X(���� 8bit ���[�h)�� +// �����y���Ȃ邩�� +// +//#define NO_BITTYPE_EMULATION + +#ifdef BUILD_OPNA +#include "fmgen_file.h" +#endif + +namespace FM +{ + +// --------------------------------------------------------------------------- +// OPNBase + +#if defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB) + +uint32 OPNBase::lfotable[8]; // OPNA/B �p + +OPNBase::OPNBase() +{ + prescale = 0; +} + +// �p�����[�^�Z�b�g +void OPNBase::SetParameter(Channel4* ch, uint addr, uint data) +{ + const static uint slottable[4] = { 0, 2, 1, 3 }; + const static uint8 sltable[16] = + { + 0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 124, + }; + + if ((addr & 3) < 3) + { + uint slot = slottable[(addr >> 2) & 3]; + Operator* op = &ch->op[slot]; + + switch ((addr >> 4) & 15) + { + case 3: // 30-3E DT/MULTI + op->SetDT((data >> 4) & 0x07); + op->SetMULTI(data & 0x0f); + break; + + case 4: // 40-4E TL + op->SetTL(data & 0x7f, (regtc & 0x80) && (csmch == ch)); + break; + + case 5: // 50-5E KS/AR + op->SetKS((data >> 6) & 3); + op->SetAR((data & 0x1f) * 2); + break; + + case 6: // 60-6E DR/AMON + op->SetDR((data & 0x1f) * 2); + op->SetAMON((data & 0x80) != 0); + break; + + case 7: // 70-7E SR + op->SetSR((data & 0x1f) * 2); + break; + + case 8: // 80-8E SL/RR + op->SetSL(sltable[(data >> 4) & 15]); + op->SetRR((data & 0x0f) * 4 + 2); + break; + + case 9: // 90-9E SSG-EC + op->SetSSGEC(data & 0x0f); + break; + } + } +} + +// ���Z�b�g +void OPNBase::Reset() +{ + status = 0; + SetPrescaler(0); + Timer::Reset(); + psg.Reset(); +} + +// �v���X�P�[���ݒ� +void OPNBase::SetPrescaler(uint p) +{ + static const char table[3][2] = { { 6, 4 }, { 3, 2 }, { 2, 1 } }; + static const uint8 table2[8] = { 108, 77, 71, 67, 62, 44, 8, 5 }; + // 512 + if (prescale != p) + { + prescale = p; + /*warning: comparison is always true due to limited range of data type [-Wtype-limits]*/ + // assert(0 <= prescale && prescale < 3); + assert(prescale < 3); + + uint fmclock = clock / table[p][0] / 12; + + rate = psgrate; + + // �������g���Æo�͎��g���̔� + assert(fmclock < (0x80000000 >> FM_RATIOBITS)); + uint ratio = ((fmclock << FM_RATIOBITS) + rate/2) / rate; + + SetTimerBase(fmclock); +// MakeTimeTable(ratio); + chip.SetRatio(ratio); + psg.SetClock(clock / table[p][1], psgrate); + + for (int i=0; i<8; i++) + { + lfotable[i] = (ratio << (2+FM_LFOCBITS-FM_RATIOBITS)) / table2[i]; + } + } +} + +// ������ +bool OPNBase::Init(uint c, uint r) +{ + clock = c; + psgrate = r; + + return true; +} + +// ���Êݒ� +void OPNBase::SetVolumeFM(int db) +{ + db = Min(db, 20); + if (db > -192) + fmvolume = int(16384.0 * pow(10.0, db / 40.0)); + else + fmvolume = 0; +} + +// �^�C�}�[���Ô��� +void OPNBase::TimerA() +{ + if (regtc & 0x80) + { + csmch->KeyControl(0x00); + csmch->KeyControl(0x0f); + } +} + +void OPNBase::DataSave(struct OPNBaseData* data) { + Timer::DataSave(&data->timer); + data->fmvolume = fmvolume; + data->clock = clock; + data->rate = rate; + data->psgrate = psgrate; + data->status = status; + data->prescale = prescale; + chip.DataSave(&data->chip); + psg.DataSave(&data->psg); +} + +void OPNBase::DataLoad(struct OPNBaseData* data) { + Timer::DataLoad(&data->timer); + fmvolume = data->fmvolume; + clock = data->clock; + rate = data->rate; + psgrate = data->psgrate; + status = data->status; + prescale = data->prescale; + chip.DataLoad(&data->chip); + psg.DataLoad(&data->psg); +} + +#endif // defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB) + +// --------------------------------------------------------------------------- +// YM2203 +// +#ifdef BUILD_OPN + +OPN::OPN() +{ + SetVolumeFM(0); + SetVolumePSG(0); + + csmch = &ch[2]; + + for (int i=0; i<3; i++) + { + ch[i].SetChip(&chip); + ch[i].SetType(typeN); + } +} + +// ������ +bool OPN::Init(uint c, uint r, bool ip, const char*) +{ + if (!SetRate(c, r, ip)) + return false; + + Reset(); + + SetVolumeFM(0); + SetVolumePSG(0); + SetChannelMask(0); + return true; +} + +// �T���v�����O���[�g�ÏX +bool OPN::SetRate(uint c, uint r, bool) +{ + OPNBase::Init(c, r); + RebuildTimeTable(); + return true; +} + + +// ���Z�b�g +void OPN::Reset() +{ + int i; + for (i=0x20; i<0x28; i++) SetReg(i, 0); + for (i=0x30; i<0xc0; i++) SetReg(i, 0); + OPNBase::Reset(); + ch[0].Reset(); + ch[1].Reset(); + ch[2].Reset(); +} + + +// ���W�X�^�ǂÝ��� +uint OPN::GetReg(uint addr) +{ + if (addr < 0x10) + return psg.GetReg(addr); + else + return 0; +} + + +// ���W�X�^�A���C�Ƀf�[�^��ݒ� +void OPN::SetReg(uint addr, uint data) +{ +// LOG2("reg[%.2x] <- %.2x\n", addr, data); + if (addr >= 0x100) + return; + + int c = addr & 3; + switch (addr) + { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: + psg.SetReg(addr, data); + break; + + case 0x24: case 0x25: + SetTimerA(addr, data); + break; + + case 0x26: + SetTimerB(data); + break; + + case 0x27: + SetTimerControl(data); + break; + + case 0x28: // Key On/Off + if ((data & 3) < 3) + ch[data & 3].KeyControl(data >> 4); + break; + + case 0x2d: case 0x2e: case 0x2f: + SetPrescaler(addr-0x2d); + break; + + // F-Number + case 0xa0: case 0xa1: case 0xa2: + fnum[c] = data + fnum2[c] * 0x100; + break; + + case 0xa4: case 0xa5: case 0xa6: + fnum2[c] = uint8(data); + break; + + case 0xa8: case 0xa9: case 0xaa: + fnum3[c] = data + fnum2[c+3] * 0x100; + break; + + case 0xac: case 0xad: case 0xae: + fnum2[c+3] = uint8(data); + break; + + case 0xb0: case 0xb1: case 0xb2: + ch[c].SetFB((data >> 3) & 7); + ch[c].SetAlgorithm(data & 7); + break; + + default: + if (c < 3) + { + if ((addr & 0xf0) == 0x60) + data &= 0x1f; + OPNBase::SetParameter(&ch[c], addr, data); + } + break; + } +} + +// �X�e�[�^�X�t���O�ݒ� +void OPN::SetStatus(uint bits) +{ + if (!(status & bits)) + { + status |= bits; + Intr(true); + } +} + +void OPN::ResetStatus(uint bit) +{ + status &= ~bit; + if (!status) + Intr(false); +} + +// �}�X�N�ݒ� +void OPN::SetChannelMask(uint mask) +{ + for (int i=0; i<3; i++) + ch[i].Mute(!!(mask & (1 << i))); + psg.SetChannelMask(mask >> 6); +} + +void OPN::DataSave(struct OPNData* data) { + OPNBase::DataSave(&data->opnbase); + memcpy(data->fnum, fnum, sizeof(uint) * 3); + memcpy(data->fnum3, fnum3, sizeof(uint) * 3); + memcpy(data->fnum2, fnum2, 6); + for(int i = 0; i < 3; i++) { + ch[i].DataSave(&data->ch[i]); + } +} + +void OPN::DataLoad(struct OPNData* data) { + OPNBase::DataLoad(&data->opnbase); + memcpy(fnum, data->fnum, sizeof(uint) * 3); + memcpy(fnum3, data->fnum3, sizeof(uint) * 3); + memcpy(fnum2, data->fnum2, 6); + for(int i = 0; i < 3; i++) { + ch[i].DataLoad(&data->ch[i]); + } + csmch = &ch[2]; + for (int i=0; i<3; i++) + { + ch[i].SetChip(&chip); + ch[i].SetType(typeN); + } +} + +// ����(2ch) +void OPN::Mix(Sample* buffer, int nsamples) +{ +//printf("M:%d\n",nsamples); +#define IStoSample(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14) + + psg.Mix(buffer, nsamples); + + // Set F-Number + ch[0].SetFNum(fnum[0]); + ch[1].SetFNum(fnum[1]); + if (!(regtc & 0xc0)) + ch[2].SetFNum(fnum[2]); + else + { // ���ʉ� + ch[2].op[0].SetFNum(fnum3[1]); + ch[2].op[1].SetFNum(fnum3[2]); + ch[2].op[2].SetFNum(fnum3[0]); + ch[2].op[3].SetFNum(fnum[2]); + } + + int actch = (((ch[2].Prepare() << 2) | ch[1].Prepare()) << 2) | ch[0].Prepare(); +//printf("a %X\n",actch); + if (actch & 0x15) + { + Sample* limit = buffer + nsamples * 2; + for (Sample* dest = buffer; dest < limit; dest+=2) + { + ISample s = 0; + if (actch & 0x01) s = ch[0].Calc(); + if (actch & 0x04) s += ch[1].Calc(); + if (actch & 0x10) s += ch[2].Calc(); + s = IStoSample(s); + StoreSample(dest[0], s); + StoreSample(dest[1], s); +//printf("%08X,%08X\n",dest[0],dest[1]); + } + } +#undef IStoSample +} + +#endif // BUILD_OPN + +// --------------------------------------------------------------------------- +// YM2608/2610 common part +// --------------------------------------------------------------------------- + +#if defined(BUILD_OPNA) || defined(BUILD_OPNB) + +int OPNABase::amtable[FM_LFOENTS] = { -1, }; +int OPNABase::pmtable[FM_LFOENTS]; + +int32 OPNABase::tltable[FM_TLENTS+FM_TLPOS]; +bool OPNABase::tablehasmade = false; + +OPNABase::OPNABase() +{ + adpcmbuf = 0; + memaddr = 0; + startaddr = 0; + deltan = 256; + + adpcmvol = 0; + control2 = 0; + + MakeTable2(); + BuildLFOTable(); + for (int i=0; i<6; i++) + { + ch[i].SetChip(&chip); + ch[i].SetType(typeN); + } +} + +OPNABase::~OPNABase() +{ +} + +// --------------------------------------------------------------------------- +// ������ +// +bool OPNABase::Init(uint, uint, bool) +{ + RebuildTimeTable(); + + Reset(); + + SetVolumeFM(0); + SetVolumePSG(0); + SetChannelMask(0); + return true; +} + +// --------------------------------------------------------------------------- +// �e�[�uï¿½ï¿½ï¿½ì¬ +// +void OPNABase::MakeTable2() +{ + if (!tablehasmade) + { + for (int i=-FM_TLPOS; i> 16; + + RebuildTimeTable(); + + lfodcount = reg22 & 0x08 ? lfotable[reg22 & 7] : 0; + return true; +} + + +// --------------------------------------------------------------------------- +// �`�����l���}�X�N�Ìݒ� +// +void OPNABase::SetChannelMask(uint mask) +{ + for (int i=0; i<6; i++) + ch[i].Mute(!!(mask & (1 << i))); + psg.SetChannelMask(mask >> 6); + adpcmmask_ = (mask & (1 << 9)) != 0; + rhythmmask_ = (mask >> 10) & ((1 << 6) - 1); +} + +void OPNABase::SetPan(uint c, uint8 p) +{ + panvolume_l[c] = panlawtable[p & 0x7f]; + panvolume_r[c] = panlawtable[0x7f - (p & 0x7f)]; +} + +// --------------------------------------------------------------------------- +void OPNABase::DataSave(struct OPNABaseData* data) { + OPNBase::DataSave(&data->opnbase); + memcpy(data->pan, pan, 6); + memcpy(data->panvolume_l, panvolume_l, sizeof(uint16) * 6); + memcpy(data->panvolume_r, panvolume_r, sizeof(uint16) * 6); + memcpy(data->fnum2, fnum2, 9); + data->reg22 = reg22; + data->reg29 = reg29; + data->stmask = stmask; + data->statusnext = statusnext; + data->lfocount = lfocount; + data->lfodcount = lfodcount; + memcpy(data->fnum, fnum, sizeof(uint) * 6); + memcpy(data->fnum3, fnum3, sizeof(uint) * 3); + data->is_adpcmbuf = 0; + if(adpcmbuf) { + data->is_adpcmbuf = 1; + memcpy(data->adpcmbuf, adpcmbuf, 0x40000); + } + data->adpcmmask = adpcmmask; + data->adpcmnotice = adpcmnotice; + data->startaddr = startaddr; + data->stopaddr = stopaddr; + data->memaddr = memaddr; + data->limitaddr = limitaddr; + data->adpcmlevel = adpcmlevel; + data->adpcmvolume = adpcmvolume; + data->adpcmvol = adpcmvol; + data->deltan = deltan; + data->adplc = adplc; + data->adpld = adpld; + data->adplbase = adplbase; + data->adpcmx = adpcmx; + data->adpcmd = adpcmd; + data->adpcmout = adpcmout; + data->apout0 = apout0; + data->apout1 = apout1; + data->adpcmreadbuf = adpcmreadbuf; + data->adpcmplay = adpcmplay; + data->granuality = granuality; + data->control1 = control1; + data->control2 = control2; + memcpy(data->adpcmreg, adpcmreg, 8); + data->rhythmmask_ = rhythmmask_; + for(int i = 0; i < 6; i++) { + ch[i].DataSave(&data->ch[i]); + } +} + +// --------------------------------------------------------------------------- +void OPNABase::DataLoad(struct OPNABaseData* data) { + OPNBase::DataLoad(&data->opnbase); + memcpy(pan, data->pan, 6); + memcpy(panvolume_l, data->panvolume_l, sizeof(uint16) * 6); + memcpy(panvolume_r, data->panvolume_r, sizeof(uint16) * 6); + memcpy(fnum2, data->fnum2, 9); + reg22 = data->reg22; + reg29 = data->reg29; + stmask = data->stmask; + statusnext = data->statusnext; + lfocount = data->lfocount; + lfodcount = data->lfodcount; + memcpy(fnum, data->fnum, sizeof(uint) * 6); + memcpy(fnum3, data->fnum3, sizeof(uint) * 3); + if(data->is_adpcmbuf) { + if(!adpcmbuf) + adpcmbuf = new uint8[0x40000]; + memcpy(adpcmbuf, data->adpcmbuf, 0x40000); + } + adpcmmask = data->adpcmmask; + adpcmnotice = data->adpcmnotice; + startaddr = data->startaddr; + stopaddr = data->stopaddr; + memaddr = data->memaddr; + limitaddr = data->limitaddr; + adpcmlevel = data->adpcmlevel; + adpcmvolume = data->adpcmvolume; + adpcmvol = data->adpcmvol; + deltan = data->deltan; + adplc = data->adplc; + adpld = data->adpld; + adplbase = data->adplbase; + adpcmx = data->adpcmx; + adpcmd = data->adpcmd; + adpcmout = data->adpcmout; + apout0 = data->apout0; + apout1 = data->apout1; + adpcmreadbuf = data->adpcmreadbuf; + adpcmplay = data->adpcmplay; + granuality = data->granuality; + control1 = data->control1; + control2 = data->control2; + memcpy(adpcmreg, data->adpcmreg, 8); + rhythmmask_ = data->rhythmmask_; + for(int i = 0; i < 6; i++) { + ch[i].DataLoad(&data->ch[i]); + ch[i].SetChip(&chip); + } +} + +// --------------------------------------------------------------------------- +// ���W�X�^�A���C�Ƀf�[�^��ݒ� +// +void OPNABase::SetReg(uint addr, uint data) +{ + int c = addr & 3; + switch (addr) + { + uint modified; + + // Timer ----------------------------------------------------------------- + case 0x24: case 0x25: + SetTimerA(addr, data); + break; + + case 0x26: + SetTimerB(data); + break; + + case 0x27: + SetTimerControl(data); + break; + + // Misc ------------------------------------------------------------------ + case 0x28: // Key On/Off + if ((data & 3) < 3) + { + c = (data & 3) + (data & 4 ? 3 : 0); + ch[c].KeyControl(data >> 4); + } + break; + + // Status Mask ----------------------------------------------------------- + case 0x29: + reg29 = data; +// UpdateStatus(); //? + break; + + // Prescaler ------------------------------------------------------------- + case 0x2d: case 0x2e: case 0x2f: + SetPrescaler(addr-0x2d); + break; + + // F-Number -------------------------------------------------------------- + case 0x1a0: case 0x1a1: case 0x1a2: + c += 3; + // fall through + case 0xa0: case 0xa1: case 0xa2: + fnum[c] = data + fnum2[c] * 0x100; + ch[c].SetFNum(fnum[c]); + break; + + case 0x1a4: case 0x1a5: case 0x1a6: + c += 3; + // fall through + case 0xa4 : case 0xa5: case 0xa6: + fnum2[c] = uint8(data); + break; + + case 0xa8: case 0xa9: case 0xaa: + fnum3[c] = data + fnum2[c+6] * 0x100; + break; + + case 0xac : case 0xad: case 0xae: + fnum2[c+6] = uint8(data); + break; + + // Algorithm ------------------------------------------------------------- + + case 0x1b0: case 0x1b1: case 0x1b2: + c += 3; + // fall through + case 0xb0: case 0xb1: case 0xb2: + ch[c].SetFB((data >> 3) & 7); + ch[c].SetAlgorithm(data & 7); + break; + + case 0x1b4: case 0x1b5: case 0x1b6: + c += 3; + // fall through + case 0xb4: case 0xb5: case 0xb6: + pan[c] = (data >> 6) & 3; + ch[c].SetMS(data); + break; + + // LFO ------------------------------------------------------------------- + case 0x22: + modified = reg22 ^ data; + reg22 = data; + if (modified & 0x8) + lfocount = 0; + lfodcount = reg22 & 8 ? lfotable[reg22 & 7] : 0; + break; + + // PSG ------------------------------------------------------------------- + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: + psg.SetReg(addr, data); + break; + + // ���F ------------------------------------------------------------------ + default: + if (c < 3) + { + if (addr & 0x100) + c += 3; + OPNBase::SetParameter(&ch[c], addr, data); + } + break; + } +} + +// --------------------------------------------------------------------------- +// ADPCM B +// +void OPNABase::SetADPCMBReg(uint addr, uint data) +{ + switch (addr) + { + case 0x00: // Control Register 1 + if ((data & 0x80) && !adpcmplay) + { + adpcmplay = true; + memaddr = startaddr; + adpcmx = 0, adpcmd = 127; + adplc = 0; + } + if (data & 1) + { + adpcmplay = false; + } + control1 = data; + break; + + case 0x01: // Control Register 2 + control2 = data; + granuality = control2 & 2 ? 1 : 4; + break; + + case 0x02: // Start Address L + case 0x03: // Start Address H + adpcmreg[addr - 0x02 + 0] = data; + startaddr = (adpcmreg[1]*256+adpcmreg[0]) << 6; + memaddr = startaddr; +// LOG1(" startaddr %.6x", startaddr); + break; + + case 0x04: // Stop Address L + case 0x05: // Stop Address H + adpcmreg[addr - 0x04 + 2] = data; + stopaddr = (adpcmreg[3]*256+adpcmreg[2] + 1) << 6; +// LOG1(" stopaddr %.6x", stopaddr); + break; + + case 0x08: // ADPCM data + if ((control1 & 0x60) == 0x60) + { +// LOG2(" Wr [0x%.5x] = %.2x", memaddr, data); + WriteRAM(data); + } + break; + + case 0x09: // delta-N L + case 0x0a: // delta-N H + adpcmreg[addr - 0x09 + 4] = data; + deltan = adpcmreg[5]*256+adpcmreg[4]; + deltan = Max(256, deltan); + adpld = deltan * adplbase >> 16; + break; + + case 0x0b: // Level Control + adpcmlevel = data; + adpcmvolume = (adpcmvol * adpcmlevel) >> 12; + break; + + case 0x0c: // Limit Address L + case 0x0d: // Limit Address H + adpcmreg[addr - 0x0c + 6] = data; + limitaddr = (adpcmreg[7]*256+adpcmreg[6] + 1) << 6; +// LOG1(" limitaddr %.6x", limitaddr); + break; + + case 0x10: // Flag Control + if (data & 0x80) + { + status = 0; + UpdateStatus(); + } + else + { + stmask = ~(data & 0x1f); +// UpdateStatus(); //??? + } + break; + } +} + + +// --------------------------------------------------------------------------- +// ���W�X�^�擾 +// +uint OPNA::GetReg(uint addr) +{ + if (addr < 0x10) + return psg.GetReg(addr); + + if (addr == 0x108) + { +// LOG1("%d:reg[108] -> ", Diag::GetCPUTick()); + + uint data = adpcmreadbuf & 0xff; + adpcmreadbuf >>= 8; + if ((control1 & 0x60) == 0x20) + { + adpcmreadbuf |= ReadRAM() << 8; +// LOG2("Rd [0x%.6x:%.2x] ", memaddr, adpcmreadbuf >> 8); + } +// LOG0("%.2x\n"); + return data; + } + + if (addr == 0xff) + return 1; + + return 0; +} + + + + +// --------------------------------------------------------------------------- +// �X�e�[�^�X�t���O�ݒ� +// +void OPNABase::SetStatus(uint bits) +{ + if (!(status & bits)) + { +// LOG2("SetStatus(%.2x %.2x)\n", bits, stmask); + status |= bits & stmask; + UpdateStatus(); + } +// else +// LOG1("SetStatus(%.2x) - ignored\n", bits); +} + +void OPNABase::ResetStatus(uint bits) +{ + status &= ~bits; +// LOG1("ResetStatus(%.2x)\n", bits); + UpdateStatus(); +} + +inline void OPNABase::UpdateStatus() +{ +// LOG2("%d:INT = %d\n", Diag::GetCPUTick(), (status & stmask & reg29) != 0); + Intr((status & stmask & reg29) != 0); +} + +// --------------------------------------------------------------------------- +// ADPCM RAM �ւÌ����ݑ��� +// +void OPNABase::WriteRAM(uint data) +{ +#ifndef NO_BITTYPE_EMULATION + if (!(control2 & 2)) + { + // 1 bit mode + adpcmbuf[(memaddr >> 4) & 0x3ffff] = data; + memaddr += 16; + } + else + { + // 8 bit mode + uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff]; + uint bank = (memaddr >> 1) & 7; + uint8 mask = 1 << bank; + data <<= bank; + + p[0x00000] = (p[0x00000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x08000] = (p[0x08000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x10000] = (p[0x10000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x18000] = (p[0x18000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x20000] = (p[0x20000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x28000] = (p[0x28000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x30000] = (p[0x30000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x38000] = (p[0x38000] & ~mask) | (uint8(data) & mask); + memaddr += 2; + } +#else + adpcmbuf[(memaddr >> granuality) & 0x3ffff] = data; + memaddr += 1 << granuality; +#endif + + if (memaddr == stopaddr) + { + SetStatus(4); + statusnext = 0x04; // EOS + memaddr &= 0x3fffff; + } + if (memaddr == limitaddr) + { +// LOG1("Limit ! (%.8x)\n", limitaddr); + memaddr = 0; + } + SetStatus(8); +} + +// --------------------------------------------------------------------------- +// ADPCM RAM ����̓ǂÝ��ݑ��� +// +uint OPNABase::ReadRAM() +{ + uint data; +#ifndef NO_BITTYPE_EMULATION + if (!(control2 & 2)) + { + // 1 bit mode + data = adpcmbuf[(memaddr >> 4) & 0x3ffff]; + memaddr += 16; + } + else + { + // 8 bit mode + uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff]; + uint bank = (memaddr >> 1) & 7; + uint8 mask = 1 << bank; + + data = (p[0x38000] & mask); + data = data * 2 + (p[0x30000] & mask); + data = data * 2 + (p[0x28000] & mask); + data = data * 2 + (p[0x20000] & mask); + data = data * 2 + (p[0x18000] & mask); + data = data * 2 + (p[0x10000] & mask); + data = data * 2 + (p[0x08000] & mask); + data = data * 2 + (p[0x00000] & mask); + data >>= bank; + memaddr += 2; + } +#else + data = adpcmbuf[(memaddr >> granuality) & 0x3ffff]; + memaddr += 1 << granuality; +#endif + if (memaddr == stopaddr) + { + SetStatus(4); + statusnext = 0x04; // EOS + memaddr &= 0x3fffff; + } + if (memaddr == limitaddr) + { +// LOG1("Limit ! (%.8x)\n", limitaddr); + memaddr = 0; + } + if (memaddr < stopaddr) + SetStatus(8); + return data; +} + + +inline int OPNABase::DecodeADPCMBSample(uint data) +{ + static const int table1[16] = + { + 1, 3, 5, 7, 9, 11, 13, 15, + -1, -3, -5, -7, -9, -11, -13, -15, + }; + static const int table2[16] = + { + 57, 57, 57, 57, 77, 102, 128, 153, + 57, 57, 57, 57, 77, 102, 128, 153, + }; + adpcmx = Limit(adpcmx + table1[data] * adpcmd / 8, 32767, -32768); + adpcmd = Limit(adpcmd * table2[data] / 64, 24576, 127); + return adpcmx; +} + + +// --------------------------------------------------------------------------- +// ADPCM RAM ����� nibble �ǂÝ��݋y�� ADPCM �W�J +// +int OPNABase::ReadRAMN() +{ + uint data; + if (granuality > 0) + { +#ifndef NO_BITTYPE_EMULATION + if (!(control2 & 2)) + { + data = adpcmbuf[(memaddr >> 4) & 0x3ffff]; + memaddr += 8; + if (memaddr & 8) + return DecodeADPCMBSample(data >> 4); + data &= 0x0f; + } + else + { + uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff] + ((~memaddr & 1) << 17); + uint bank = (memaddr >> 1) & 7; + uint8 mask = 1 << bank; + + data = (p[0x18000] & mask); + data = data * 2 + (p[0x10000] & mask); + data = data * 2 + (p[0x08000] & mask); + data = data * 2 + (p[0x00000] & mask); + data >>= bank; + memaddr ++; + if (memaddr & 1) + return DecodeADPCMBSample(data); + } +#else + data = adpcmbuf[(memaddr >> granuality) & adpcmmask]; + memaddr += 1 << (granuality-1); + if (memaddr & (1 << (granuality-1))) + return DecodeADPCMBSample(data >> 4); + data &= 0x0f; +#endif + } + else + { + data = adpcmbuf[(memaddr >> 1) & adpcmmask]; + ++memaddr; + if (memaddr & 1) + return DecodeADPCMBSample(data >> 4); + data &= 0x0f; + } + + DecodeADPCMBSample(data); + + // check + if (memaddr == stopaddr) + { + if (control1 & 0x10) + { + memaddr = startaddr; + data = adpcmx; + adpcmx = 0, adpcmd = 127; + return data; + } + else + { + memaddr &= adpcmmask; //0x3fffff; + SetStatus(adpcmnotice); + adpcmplay = false; + } + } + + if (memaddr == limitaddr) + memaddr = 0; + + return adpcmx; +} + +// --------------------------------------------------------------------------- +// �g���X�e�[�^�X��ǂ݂��� +// +uint OPNABase::ReadStatusEx() +{ + uint r = ((status | 8) & stmask) | (adpcmplay ? 0x20 : 0); + status |= statusnext; + statusnext = 0; + return r; +} + +// --------------------------------------------------------------------------- +// ADPCM �W�J +// +inline void OPNABase::DecodeADPCMB() +{ + apout0 = apout1; + int n = (ReadRAMN() * adpcmvolume) >> 13; + apout1 = adpcmout + n; + adpcmout = n; +} + +// --------------------------------------------------------------------------- +// ADPCM ���� +// +void OPNABase::ADPCMBMix(Sample* dest, uint count) +{ + uint maskl = control2 & 0x80 ? -1 : 0; + uint maskr = control2 & 0x40 ? -1 : 0; + if (adpcmmask_) + { + maskl = maskr = 0; + } + + if (adpcmplay) + { +// LOG2("ADPCM Play: %d DeltaN: %d\n", adpld, deltan); + if (adpld <= 8192) // fplay < fsamp + { + for (; count>0; count--) + { + if (adplc < 0) + { + adplc += 8192; + DecodeADPCMB(); + if (!adpcmplay) + break; + } + int s = (adplc * apout0 + (8192-adplc) * apout1) >> 13; + StoreSample(dest[0], s & maskl); + StoreSample(dest[1], s & maskr); + dest += 2; + adplc -= adpld; + } + for (; count>0 && apout0; count--) + { + if (adplc < 0) + { + apout0 = apout1, apout1 = 0; + adplc += 8192; + } + int s = (adplc * apout1) >> 13; + StoreSample(dest[0], s & maskl); + StoreSample(dest[1], s & maskr); + dest += 2; + adplc -= adpld; + } + } + else // fplay > fsamp (adpld = fplay/famp*8192) + { + int t = (-8192*8192)/adpld; + for (; count>0; count--) + { + int s = apout0 * (8192+adplc); + while (adplc < 0) + { + DecodeADPCMB(); + if (!adpcmplay) + goto stop; + s -= apout0 * Max(adplc, t); + adplc -= t; + } + adplc -= 8192; + s >>= 13; + StoreSample(dest[0], s & maskl); + StoreSample(dest[1], s & maskr); + dest += 2; + } +stop: + ; + } + } + if (!adpcmplay) + { + apout0 = apout1 = adpcmout = 0; + adplc = 0; + } +} + +// --------------------------------------------------------------------------- +// ���� +// in: buffer ������ +// nsamples �����T���v���� +// +void OPNABase::FMMix(Sample* buffer, int nsamples) +{ + if (fmvolume > 0) + { + // ���� + // Set F-Number + if (!(regtc & 0xc0)) + csmch->SetFNum(fnum[csmch-ch]); + else + { + // ���ʉ����[�h + csmch->op[0].SetFNum(fnum3[1]); csmch->op[1].SetFNum(fnum3[2]); + csmch->op[2].SetFNum(fnum3[0]); csmch->op[3].SetFNum(fnum[2]); + } + + int act = (((ch[2].Prepare() << 2) | ch[1].Prepare()) << 2) | ch[0].Prepare(); + if (reg29 & 0x80) + act |= (ch[3].Prepare() | ((ch[4].Prepare() | (ch[5].Prepare() << 2)) << 2)) << 6; + if (!(reg22 & 0x08)) + act &= 0x555; + + if (act & 0x555) + { + Mix6(buffer, nsamples, act); + } + } +} + +// --------------------------------------------------------------------------- + +void OPNABase::MixSubSL(int activech, ISample** dest) +{ + if (activech & 0x001) (*dest[0] = ch[0].CalcL()); + if (activech & 0x004) (*dest[1] += ch[1].CalcL()); + if (activech & 0x010) (*dest[2] += ch[2].CalcL()); + if (activech & 0x040) (*dest[3] += ch[3].CalcL()); + if (activech & 0x100) (*dest[4] += ch[4].CalcL()); + if (activech & 0x400) (*dest[5] += ch[5].CalcL()); +} + +inline void OPNABase::MixSubS(int activech, ISample** dest) +{ + if (activech & 0x001) (*dest[0] = ch[0].Calc()); + if (activech & 0x004) (*dest[1] += ch[1].Calc()); + if (activech & 0x010) (*dest[2] += ch[2].Calc()); + if (activech & 0x040) (*dest[3] += ch[3].Calc()); + if (activech & 0x100) (*dest[4] += ch[4].Calc()); + if (activech & 0x400) (*dest[5] += ch[5].Calc()); +} + +// --------------------------------------------------------------------------- + +void OPNABase::BuildLFOTable() +{ + if (amtable[0] == -1) + { + for (int c=0; c<256; c++) + { + int v; + if (c < 0x40) v = c * 2 + 0x80; + else if (c < 0xc0) v = 0x7f - (c - 0x40) * 2 + 0x80; + else v = (c - 0xc0) * 2; + pmtable[c] = c; + + if (c < 0x80) v = 0xff - c * 2; + else v = (c - 0x80) * 2; + amtable[c] = v & ~3; + } + } +} + +// --------------------------------------------------------------------------- + +inline void OPNABase::LFO() +{ +// LOG3("%4d - %8d, %8d\n", c, lfocount, lfodcount); + +// Operator::SetPML(pmtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); +// Operator::SetAML(amtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); + chip.SetPML(pmtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); + chip.SetAML(amtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); + lfocount += lfodcount; +} + +// --------------------------------------------------------------------------- +// ���� +// +#define IStoSample(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14) + +void OPNABase::Mix6(Sample* buffer, int nsamples, int activech) +{ + // Mix + // libOPNMIDI: rewrite for panning support + + const uint activechmask[6] = {0x001, 0x004, 0x010, 0x040, 0x100, 0x400}; + + Sample* limit = buffer + nsamples * 2; + for (Sample* dest = buffer; dest < limit; dest+=2) + { + ISample out[6]; + if (activech & 0xaaa) + { + LFO(); + for (uint c = 0; c<6; ++c) + out[c] = (activechmask[c] & activech) ? ch[c].CalcL() : 0; + } + else + { + for (uint c = 0; c<6; ++c) + out[c] = (activechmask[c] & activech) ? ch[c].Calc() : 0; + } + + int lrouts[2] = {0, 0}; + for (uint c = 0; c<6; ++c) + { + int panl = panvolume_l[c]; + int panr = panvolume_r[c]; + panl = (pan[c] & 2) ? panl : 0; + panr = (pan[c] & 1) ? panr : 0; + lrouts[0] += out[c] * panl / 65535; + lrouts[1] += out[c] * panr / 65535; + } + + StoreSample(dest[0], lrouts[0]); + StoreSample(dest[1], lrouts[1]); + } +} + +#endif // defined(BUILD_OPNA) || defined(BUILD_OPNB) + +// --------------------------------------------------------------------------- +// YM2608(OPNA) +// --------------------------------------------------------------------------- + +#ifdef BUILD_OPNA + +// --------------------------------------------------------------------------- +// �\�z +// +OPNA::OPNA() +{ + for (int i=0; i<6; i++) + { + rhythm[i].sample = 0; + rhythm[i].pos = 0; + rhythm[i].size = 0; + rhythm[i].volume = 0; + } + rhythmtvol = 0; + adpcmmask = 0x3ffff; + adpcmnotice = 4; + csmch = &ch[2]; +} + +// --------------------------------------------------------------------------- + +OPNA::~OPNA() +{ + delete[] adpcmbuf; + for (int i=0; i<6; i++) + delete[] rhythm[i].sample; +} + + + +// --------------------------------------------------------------------------- +// ������ +// +bool OPNA::Init(uint c, uint r, bool ipflag, const char* path) +{ + rate = 8000; + LoadRhythmSample(path); + + if (!adpcmbuf) + adpcmbuf = new uint8[0x40000]; + if (!adpcmbuf) + return false; + + if (!SetRate(c, r, ipflag)) + return false; + if (!OPNABase::Init(c, r, ipflag)) + return false; + + Reset(); + + SetVolumeADPCM(0); + SetVolumeRhythmTotal(0); + for (int i=0; i<6; i++) + SetVolumeRhythm(0, 0); + return true; +} + +// --------------------------------------------------------------------------- +// ���Z�b�g +// +void OPNA::Reset() +{ + reg29 = 0x1f; + rhythmkey = 0; + limitaddr = 0x3ffff; + OPNABase::Reset(); +} + +// --------------------------------------------------------------------------- +// �T���v�����O���[�g�ÏX +// +bool OPNA::SetRate(uint c, uint r, bool ipflag) +{ + if (!OPNABase::SetRate(c, r, ipflag)) + return false; + + for (int i=0; i<6; i++) + { + rhythm[i].step = rhythm[i].rate * 1024 / r; + } + return true; +} + + +// --------------------------------------------------------------------------- +// ���Y������ǂ݂��� +// +bool OPNA::LoadRhythmSample(const char* path) +{ + static const char* rhythmname[6] = + { + "bd", "sd", "top", "hh", "tom", "rim", + }; + + int i; + for (i=0; i<6; i++) + rhythm[i].pos = ~0u; + + for (i=0; i<6; i++) + { + FileIO file; + uint32 fsize; + char buf[MAX_PATH + 1] = ""; + if (path) + strncpy(buf, path, MAX_PATH); + strncat(buf, "2608_", MAX_PATH); + strncat(buf, rhythmname[i], MAX_PATH); + strncat(buf, ".wav", MAX_PATH); + + if (!file.Open(buf, FileIO::readonly)) + { + if (i != 5) + break; + if (path) + strncpy(buf, path, MAX_PATH); + strncpy(buf, "2608_rym.wav", MAX_PATH); + if (!file.Open(buf, FileIO::readonly)) + break; + } + + struct + { + uint32 chunksize; + uint16 tag; + uint16 nch; + uint32 rate; + uint32 avgbytes; + uint16 align; + uint16 bps; + uint16 size; + } whdr; + + file.Seek(0x10, FileIO::begin); + file.Read(&whdr, sizeof(whdr)); + + uint8 subchunkname[4]; + fsize = 4 + whdr.chunksize - sizeof(whdr); + do + { + file.Seek(fsize, FileIO::current); + file.Read(&subchunkname, 4); + file.Read(&fsize, 4); + } while (memcmp("data", subchunkname, 4)); + + fsize /= 2; + if (fsize >= 0x100000 || whdr.tag != 1 || whdr.nch != 1) + break; + fsize = Max(fsize, (1<<31)/1024); + + if(!rhythm[i].sample) + delete rhythm[i].sample; + rhythm[i].sample = new int16[fsize]; + if (!rhythm[i].sample) + break; + + file.Read(rhythm[i].sample, fsize * 2); + + rhythm[i].rate = whdr.rate; + rhythm[i].step = rhythm[i].rate * 1024 / rate; + rhythm[i].pos = rhythm[i].size = fsize * 1024; + } + if (i != 6) + { + for (i=0; i<6; i++) + { + delete[] rhythm[i].sample; + rhythm[i].sample = 0; + } + return false; + } + return true; +} + + + +// --------------------------------------------------------------------------- +// ���W�X�^�A���C�Ƀf�[�^��ݒ� +// +void OPNA::SetReg(uint addr, uint data) +{ + addr &= 0x1ff; + + switch (addr) + { + case 0x29: + reg29 = data; +// UpdateStatus(); //? + break; + + // Rhythm ---------------------------------------------------------------- + case 0x10: // DM/KEYON + if (!(data & 0x80)) // KEY ON + { + rhythmkey |= data & 0x3f; + if (data & 0x01) rhythm[0].pos = 0; + if (data & 0x02) rhythm[1].pos = 0; + if (data & 0x04) rhythm[2].pos = 0; + if (data & 0x08) rhythm[3].pos = 0; + if (data & 0x10) rhythm[4].pos = 0; + if (data & 0x20) rhythm[5].pos = 0; + } + else + { // DUMP + rhythmkey &= ~data; + } + break; + + case 0x11: + rhythmtl = ~data & 63; + break; + + case 0x18: // Bass Drum + case 0x19: // Snare Drum + case 0x1a: // Top Cymbal + case 0x1b: // Hihat + case 0x1c: // Tom-tom + case 0x1d: // Rim shot + rhythm[addr & 7].pan = (data >> 6) & 3; + rhythm[addr & 7].level = ~data & 31; + break; + + case 0x100: case 0x101: + case 0x102: case 0x103: + case 0x104: case 0x105: + case 0x108: case 0x109: + case 0x10a: case 0x10b: + case 0x10c: case 0x10d: + case 0x110: + OPNABase::SetADPCMBReg(addr - 0x100, data); + break; + + default: + OPNABase::SetReg(addr, data); + break; + } +} + +// --------------------------------------------------------------------------- +void OPNA::DataSave(struct OPNAData* data) { + OPNABase::DataSave(&data->opnabase); + memcpy(data->rhythm, rhythm, sizeof(Rhythm) * 6); + data->rhythmtl = rhythmtl; + data->rhythmtvol = rhythmtvol; + data->rhythmkey = rhythmkey; +} + +// --------------------------------------------------------------------------- +void OPNA::DataLoad(struct OPNAData* data) { + OPNABase::DataLoad(&data->opnabase); + memcpy(rhythm, data->rhythm, sizeof(Rhythm) * 6); + rhythmtl = data->rhythmtl; + rhythmtvol = data->rhythmtvol; + rhythmkey = data->rhythmkey; + csmch = &ch[2]; +} + +// --------------------------------------------------------------------------- +// ���Y������ +// +void OPNA::RhythmMix(Sample* buffer, uint count) +{ + if (rhythmtvol < 128 && rhythm[0].sample && (rhythmkey & 0x3f)) + { + Sample* limit = buffer + count * 2; + for (int i=0; i<6; i++) + { + Rhythm& r = rhythm[i]; + if ((rhythmkey & (1 << i)) /*&& r.level < 128*/) // libOPNMIDI: Useless condition, signed int 8t has 127 max + { + int db = Limit(rhythmtl+rhythmtvol+r.level+r.volume, 127, -31); + int vol = tltable[FM_TLPOS+(db << (FM_TLBITS-7))] >> 4; + int maskl = -((r.pan >> 1) & 1); + int maskr = -(r.pan & 1); + + if (rhythmmask_ & (1 << i)) + { + maskl = maskr = 0; + } + + for (Sample* dest = buffer; dest> 12; + r.pos += r.step; + StoreSample(dest[0], sample & maskl); + StoreSample(dest[1], sample & maskr); + } + } + } + } +} + +// --------------------------------------------------------------------------- +// ���Êݒ� +// +void OPNA::SetVolumeRhythmTotal(int db) +{ + db = Min(db, 20); + rhythmtvol = -(db * 2 / 3); +} + +void OPNA::SetVolumeRhythm(int index, int db) +{ + db = Min(db, 20); + rhythm[index].volume = -(db * 2 / 3); +} + +void OPNA::SetVolumeADPCM(int db) +{ + db = Min(db, 20); + if (db > -192) + adpcmvol = int(65536.0 * pow(10.0, db / 40.0)); + else + adpcmvol = 0; + + adpcmvolume = (adpcmvol * adpcmlevel) >> 12; +} + +// --------------------------------------------------------------------------- +// ���� +// in: buffer ������ +// nsamples �����T���v���� +// +void OPNA::Mix(Sample* buffer, int nsamples) +{ + FMMix(buffer, nsamples); + psg.Mix(buffer, nsamples); + ADPCMBMix(buffer, nsamples); + RhythmMix(buffer, nsamples); +} + +#endif // BUILD_OPNA + +// --------------------------------------------------------------------------- +// YM2610(OPNB) +// --------------------------------------------------------------------------- + +#ifdef BUILD_OPNB + +// --------------------------------------------------------------------------- +// �\�z +// +OPNB::OPNB() +{ + adpcmabuf = 0; + adpcmasize = 0; + for (int i=0; i<6; i++) + { + adpcma[i].pan = 0; + adpcma[i].level = 0; + adpcma[i].volume = 0; + adpcma[i].pos = 0; + adpcma[i].step = 0; + adpcma[i].volume = 0; + adpcma[i].start = 0; + adpcma[i].stop = 0; + adpcma[i].adpcmx = 0; + adpcma[i].adpcmd = 0; + } + adpcmatl = 0; + adpcmakey = 0; + adpcmatvol = 0; + adpcmmask = 0; + adpcmnotice = 0x8000; + granuality = -1; + csmch = &ch[2]; + + InitADPCMATable(); +} + +OPNB::~OPNB() +{ +} + +// --------------------------------------------------------------------------- +// ������ +// +bool OPNB::Init(uint c, uint r, bool ipflag, + uint8 *_adpcma, int _adpcma_size, + uint8 *_adpcmb, int _adpcmb_size) +{ + int i; + if (!SetRate(c, r, ipflag)) + return false; + if (!OPNABase::Init(c, r, ipflag)) + return false; + + adpcmabuf = _adpcma; + adpcmasize = _adpcma_size; + adpcmbuf = _adpcmb; + + for (i=0; i<=24; i++) // max 16M bytes + { + if (_adpcmb_size <= (1 << i)) + { + adpcmmask = (1 << i) - 1; + break; + } + } + +// adpcmmask = _adpcmb_size - 1; + limitaddr = adpcmmask; + + Reset(); + + SetVolumeFM(0); + SetVolumePSG(0); + SetVolumeADPCMB(0); + SetVolumeADPCMATotal(0); + for (i=0; i<6; i++) + SetVolumeADPCMA(0, 0); + SetChannelMask(0); + return true; +} + +// --------------------------------------------------------------------------- +// ���Z�b�g +// +void OPNB::Reset() +{ + OPNABase::Reset(); + + stmask = ~0; + adpcmakey = 0; + reg29 = ~0; + + for (int i=0; i<6; i++) + { + adpcma[i].pan = 0; + adpcma[i].level = 0; + adpcma[i].volume = 0; + adpcma[i].pos = 0; + adpcma[i].step = 0; + adpcma[i].volume = 0; + adpcma[i].start = 0; + adpcma[i].stop = 0; + adpcma[i].adpcmx = 0; + adpcma[i].adpcmd = 0; + } +} + +// --------------------------------------------------------------------------- +// �T���v�����O���[�g�ÏX +// +bool OPNB::SetRate(uint c, uint r, bool ipflag) +{ + if (!OPNABase::SetRate(c, r, ipflag)) + return false; + + adpcmastep = int(double(c) / 54 * 8192 / r); + return true; +} + +// --------------------------------------------------------------------------- +// ���W�X�^�A���C�Ƀf�[�^��ݒ� +// +void OPNB::SetReg(uint addr, uint data) +{ + addr &= 0x1ff; + + switch (addr) + { + // omitted registers + case 0x29: + case 0x2d: case 0x2e: case 0x2f: + break; + + // ADPCM A --------------------------------------------------------------- + case 0x100: // DM/KEYON + if (!(data & 0x80)) // KEY ON + { + adpcmakey |= data & 0x3f; + for (int c=0; c<6; c++) + { + if (data & (1<> 6) & 3; + adpcma[addr & 7].level = ~data & 31; + break; + + case 0x110: case 0x111: case 0x112: // START ADDRESS (L) + case 0x113: case 0x114: case 0x115: + case 0x118: case 0x119: case 0x11a: // START ADDRESS (H) + case 0x11b: case 0x11c: case 0x11d: + adpcmareg[addr - 0x110] = data; + adpcma[addr & 7].pos = adpcma[addr & 7].start = + (adpcmareg[(addr&7)+8]*256+adpcmareg[addr&7]) << 9; + break; + + case 0x120: case 0x121: case 0x122: // END ADDRESS (L) + case 0x123: case 0x124: case 0x125: + case 0x128: case 0x129: case 0x12a: // END ADDRESS (H) + case 0x12b: case 0x12c: case 0x12d: + adpcmareg[addr - 0x110] = data; + adpcma[addr & 7].stop = + (adpcmareg[(addr&7)+24]*256+adpcmareg[(addr&7)+16] + 1) << 9; + break; + + // ADPCMB ----------------------------------------------------------------- + case 0x10: + if ((data & 0x80) && !adpcmplay) + { + adpcmplay = true; + memaddr = startaddr; + adpcmx = 0, adpcmd = 127; + adplc = 0; + } + if (data & 1) + adpcmplay = false; + control1 = data & 0x91; + break; + + + case 0x11: // Control Register 2 + control2 = data & 0xc0; + break; + + case 0x12: // Start Address L + case 0x13: // Start Address H + adpcmreg[addr - 0x12 + 0] = data; + startaddr = (adpcmreg[1]*256+adpcmreg[0]) << 9; + memaddr = startaddr; + break; + + case 0x14: // Stop Address L + case 0x15: // Stop Address H + adpcmreg[addr - 0x14 + 2] = data; + stopaddr = (adpcmreg[3]*256+adpcmreg[2] + 1) << 9; +// LOG1(" stopaddr %.6x", stopaddr); + break; + + case 0x19: // delta-N L + case 0x1a: // delta-N H + adpcmreg[addr - 0x19 + 4] = data; + deltan = adpcmreg[5]*256+adpcmreg[4]; + deltan = Max(256, deltan); + adpld = deltan * adplbase >> 16; + break; + + case 0x1b: // Level Control + adpcmlevel = data; + adpcmvolume = (adpcmvol * adpcmlevel) >> 12; + break; + + case 0x1c: // Flag Control + stmask = ~((data & 0xbf) << 8); + status &= stmask; + UpdateStatus(); + break; + + default: + OPNABase::SetReg(addr, data); + break; + } +// LOG0("\n"); +} + +// --------------------------------------------------------------------------- +// ���W�X�^�擾 +// +uint OPNB::GetReg(uint addr) +{ + if (addr < 0x10) + return psg.GetReg(addr); + + return 0; +} + +// --------------------------------------------------------------------------- +// �g���X�e�[�^�X��ǂ݂��� +// +uint OPNB::ReadStatusEx() +{ + return (status & stmask) >> 8; +} + +// --------------------------------------------------------------------------- +// YM2610 +// +int OPNB::jedi_table[(48+1)*16]; + +void OPNB::InitADPCMATable() +{ + const static int8 table2[] = + { + 1, 3, 5, 7, 9, 11, 13, 15, + -1, -3, -5, -7, -9,-11,-13,-15, + }; + + for (int i=0; i<=48; i++) + { + int s = int(16.0 * pow (1.1, i) * 3); + for (int j=0; j<16; j++) + { + jedi_table[i*16+j] = s * table2[j] / 8; + } + } +} + +// --------------------------------------------------------------------------- +// ADPCMA ���� +// +void OPNB::ADPCMAMix(Sample* buffer, uint count) +{ + const static int decode_tableA1[16] = + { + -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16, + -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16 + }; + + if (adpcmatvol < 128 && (adpcmakey & 0x3f)) + { + Sample* limit = buffer + count * 2; + for (int i=0; i<6; i++) + { + ADPCMA& r = adpcma[i]; + if ((adpcmakey & (1 << i))/* && r.level < 128*/) // libOPNMIDI: Useless condition, signed int 8t has 127 max + { + uint maskl = r.pan & 2 ? -1 : 0; + uint maskr = r.pan & 1 ? -1 : 0; + if (rhythmmask_ & (1 << i)) + { + maskl = maskr = 0; + } + + int db = Limit(adpcmatl+adpcmatvol+r.level+r.volume, 127, -31); + int vol = tltable[FM_TLPOS+(db << (FM_TLBITS-7))] >> 4; + + Sample* dest = buffer; + for ( ; dest= r.stop) + { + SetStatus(0x100 << i); + adpcmakey &= ~(1< 0x10000; r.step -= 0x10000) + { + int data; + if (!(r.pos & 1)) + { + r.nibble = adpcmabuf[r.pos>>1]; + data = r.nibble >> 4; + } + else + { + data = r.nibble & 0x0f; + } + r.pos++; + + r.adpcmx += jedi_table[r.adpcmd + data]; + r.adpcmx = Limit(r.adpcmx, 2048*3-1, -2048*3); + r.adpcmd += decode_tableA1[data]; + r.adpcmd = Limit(r.adpcmd, 48*16, 0); + } + int sample = (r.adpcmx * vol) >> 10; + StoreSample(dest[0], sample & maskl); + StoreSample(dest[1], sample & maskr); + } + } + } + } +} + +// --------------------------------------------------------------------------- +// ���Êݒ� +// +void OPNB::SetVolumeADPCMATotal(int db) +{ + db = Min(db, 20); + adpcmatvol = -(db * 2 / 3); +} + +void OPNB::SetVolumeADPCMA(int index, int db) +{ + db = Min(db, 20); + adpcma[index].volume = -(db * 2 / 3); +} + +void OPNB::SetVolumeADPCMB(int db) +{ + db = Min(db, 20); + if (db > -192) + adpcmvol = int(65536.0 * pow(10, db / 40.0)); + else + adpcmvol = 0; +} + +// --------------------------------------------------------------------------- +void OPNB::DataSave(struct OPNBData* data, void* adpcmadata) { + OPNABase::DataSave(&data->opnabase); + if(adpcmasize) { + adpcmadata = malloc(adpcmasize); + memcpy(adpcmadata, adpcmabuf, adpcmasize); + } + data->adpcmasize = adpcmasize; + memcpy(data->adpcma, adpcma, sizeof(ADPCMA) * 6); + data->adpcmatl = adpcmatl; + data->adpcmatvol = adpcmatvol; + data->adpcmakey = adpcmakey; + data->adpcmastep = adpcmastep; + memcpy(data->adpcmareg, adpcmareg, 32); + for(int i = 0; i < 6; i++) { + ch[i].DataSave(&data->ch[i]); + } +} + +// --------------------------------------------------------------------------- +void OPNB::DataLoad(struct OPNBData* data, void* adpcmadata) { + OPNABase::DataLoad(&data->opnabase); // libOPNMIDI: bugfix DataLoad (was DataSave here) + if(data->adpcmasize) { + adpcmabuf = (uint8*)malloc(data->adpcmasize); + memcpy(adpcmabuf, adpcmadata, data->adpcmasize); + } + adpcmasize = data->adpcmasize; + memcpy(adpcma, data->adpcma, sizeof(ADPCMA) * 6); + adpcmatl = data->adpcmatl; + adpcmatvol = data->adpcmatvol; + adpcmakey = data->adpcmakey; + adpcmastep = data->adpcmastep; + memcpy(adpcmareg, data->adpcmareg, 32); + for(int i = 0; i < 6; i++) { + ch[i].DataLoad(&data->ch[i]); + } + csmch = &ch[2]; +} + +// --------------------------------------------------------------------------- +// ���� +// in: buffer ������ +// nsamples �����T���v���� +// +void OPNB::Mix(Sample* buffer, int nsamples) +{ + FMMix(buffer, nsamples); + psg.Mix(buffer, nsamples); + ADPCMBMix(buffer, nsamples); + ADPCMAMix(buffer, nsamples); +} + +#endif // BUILD_OPNB + +} // namespace FM diff --git a/thirdparty/opnmidi/chips/np2/fmgen_opna.h b/thirdparty/opnmidi/chips/np2/fmgen_opna.h new file mode 100644 index 000000000..f072e3405 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_opna.h @@ -0,0 +1,523 @@ +// --------------------------------------------------------------------------- +// OPN/A/B interface with ADPCM support +// Copyright (C) cisc 1998, 2003. +// --------------------------------------------------------------------------- +// $Id: opna.h,v 1.33 2003/06/12 13:14:37 cisc Exp $ + +#ifndef FM_OPNA_H +#define FM_OPNA_H + +#include "fmgen_fmgen.h" +#include "fmgen_fmtimer.h" +#include "fmgen_psg.h" + +// --------------------------------------------------------------------------- +// class OPN/OPNA +// OPN/OPNA ã«è‰¯ãä¼¼ãŸéŸ³ã‚’生æˆã™ã‚‹éŸ³æºãƒ¦ãƒ‹ãƒƒãƒˆ +// +// interface: +// bool Init(uint clock, uint rate, bool, const char* path); +// åˆæœŸåŒ–.ã“ã®ã‚¯ãƒ©ã‚¹ã‚’使用ã™ã‚‹å‰ã«ã‹ãªã‚‰ãšå‘¼ã‚“ã§ãŠãã“ã¨ï¼Ž +// OPNA ã®å ´åˆã¯ã“ã®é–¢æ•°ã§ãƒªã‚ºãƒ ã‚µãƒ³ãƒ—ルを読ã¿è¾¼ã‚€ +// +// clock: OPN/OPNA/OPNB ã®ã‚¯ãƒ­ãƒƒã‚¯å‘¨æ³¢æ•°(Hz) +// +// rate: 生æˆã™ã‚‹ PCM ã®æ¨™æœ¬å‘¨æ³¢æ•°(Hz) +// +// path: リズムサンプルã®ãƒ‘ス(OPNA ã®ã¿æœ‰åй) +// çœç•¥æ™‚ã¯ã‚«ãƒ¬ãƒ³ãƒˆãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‹ã‚‰èª­ã¿è¾¼ã‚€ +// æ–‡å­—åˆ—ã®æœ«å°¾ã«ã¯ 'Â¥' ã‚„ '/' ãªã©ã‚’ã¤ã‘ã‚‹ã“㨠+// +// 返り値 åˆæœŸåŒ–ã«æˆåŠŸã™ã‚Œã° true +// +// bool LoadRhythmSample(const char* path) +// (OPNA ONLY) +// Rhythm サンプルを読ã¿ç›´ã™ï¼Ž +// path 㯠Init ã® path ã¨åŒã˜ï¼Ž +// +// bool SetRate(uint clock, uint rate, bool) +// クロックや PCM レートを変更ã™ã‚‹ +// 引数等㯠Init ã‚’å‚ç…§ã®ã“ã¨ï¼Ž +// +// void Mix(FM_SAMPLETYPE* dest, int nsamples) +// Stereo PCM データを nsamples åˆ†åˆæˆã—, dest ã§å§‹ã¾ã‚‹é…列㫠+// 加ãˆã‚‹(加算ã™ã‚‹) +// ・dest ã«ã¯ sample*2 個分ã®é ˜åŸŸãŒå¿…è¦ +// ・格ç´å½¢å¼ã¯ L, R, L, R... ã¨ãªã‚‹ï¼Ž +// ・ã‚ãã¾ã§åŠ ç®—ãªã®ã§ï¼Œã‚らã‹ã˜ã‚é…列をゼロクリアã™ã‚‹å¿…è¦ãŒã‚ã‚‹ +// ・FM_SAMPLETYPE ㌠short åž‹ã®å ´åˆã‚¯ãƒªãƒƒãƒ”ングãŒè¡Œã‚れる. +// ・ã“ã®é–¢æ•°ã¯éŸ³æºå†…部ã®ã‚¿ã‚¤ãƒžãƒ¼ã¨ã¯ç‹¬ç«‹ã—ã¦ã„る. +// Timer 㯠Count 㨠GetNextEvent ã§æ“作ã™ã‚‹å¿…è¦ãŒã‚る. +// +// void Reset() +// 音æºã‚’リセット(åˆæœŸåŒ–)ã™ã‚‹ +// +// void SetReg(uint reg, uint data) +// 音æºã®ãƒ¬ã‚¸ã‚¹ã‚¿ reg ã« data を書ã込む +// +// uint GetReg(uint reg) +// 音æºã®ãƒ¬ã‚¸ã‚¹ã‚¿ reg ã®å†…容を読ã¿å‡ºã™ +// 読ã¿è¾¼ã‚€ã“ã¨ãŒå‡ºæ¥ã‚‹ãƒ¬ã‚¸ã‚¹ã‚¿ã¯ PSG, ADPCM ã®ä¸€éƒ¨ï¼ŒID(0xff) ã¨ã‹ +// +// uint ReadStatus()/ReadStatusEx() +// 音æºã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒ¬ã‚¸ã‚¹ã‚¿ã‚’読ã¿å‡ºã™ +// ReadStatusEx ã¯æ‹¡å¼µã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒ¬ã‚¸ã‚¹ã‚¿ã®èª­ã¿å‡ºã—(OPNA) +// busy フラグã¯å¸¸ã« 0 +// +// bool Count(uint32 t) +// 音æºã®ã‚¿ã‚¤ãƒžãƒ¼ã‚’ t [μ秒] 進ã‚る. +// 音æºã®å†…部状態ã«å¤‰åŒ–ãŒã‚ã£ãŸæ™‚(timer オーãƒãƒ¼ãƒ•ロー) +// true を返㙠+// +// uint32 GetNextEvent() +// 音æºã®ã‚¿ã‚¤ãƒžãƒ¼ã®ã©ã¡ã‚‰ã‹ãŒã‚ªãƒ¼ãƒãƒ¼ãƒ•ローã™ã‚‹ã¾ã§ã«å¿…è¦ãª +// 時間[μ秒]を返㙠+// タイマーãŒåœæ­¢ã—ã¦ã„ã‚‹å ´åˆã¯ ULONG_MAX ã‚’è¿”ã™â€¦ ã¨æ€ã† +// +// void SetVolumeFM(int db)/SetVolumePSG(int db) ... +// å„音æºã®éŸ³é‡ã‚’+−方å‘ã«èª¿ç¯€ã™ã‚‹ï¼Žæ¨™æº–値㯠0. +// å˜ä½ã¯ç´„ 1/2 dB,有効範囲ã®ä¸Šé™ã¯ 20 (10dB) +// +namespace FM +{ + // OPN Base ------------------------------------------------------- + struct OPNBaseData { + struct TimerData timer; + int fmvolume; + uint clock; + uint rate; + uint psgrate; + uint status; + uint8 prescale; + struct ChipData chip; + struct PSGData psg; + }; + + class OPNBase : public Timer + { + public: + OPNBase(); + + bool Init(uint c, uint r); + virtual void Reset(); + + void SetVolumeFM(int db); + void SetVolumePSG(int db); + + void DataSave(struct OPNBaseData* data); + void DataLoad(struct OPNBaseData* data); + + protected: + void SetParameter(Channel4* ch, uint addr, uint data); + void SetPrescaler(uint p); + void RebuildTimeTable(); + + int fmvolume; + + uint clock; // OPN クロック + uint rate; // FM 音æºåˆæˆãƒ¬ãƒ¼ãƒˆ + uint psgrate; // FMGen 出力レート + uint status; + Channel4* csmch; + + + static uint32 lfotable[8]; + + private: + void TimerA(); + uint8 prescale; + + protected: + Chip chip; + PSG psg; + }; + + // OPN2 Base ------------------------------------------------------ + struct OPNABaseData { + struct OPNBaseData opnbase; + uint8 pan[6]; + uint16 panvolume_l[6]; // libOPNMIDI: soft panning + uint16 panvolume_r[6]; // libOPNMIDI: soft panning + uint8 fnum2[9]; + uint8 reg22; + uint reg29; + uint stmask; + uint statusnext; + uint32 lfocount; + uint32 lfodcount; + uint fnum[6]; + uint fnum3[3]; + bool is_adpcmbuf; + uint8 adpcmbuf[0x40000]; + uint adpcmmask; + uint adpcmnotice; + uint startaddr; + uint stopaddr; + uint memaddr; + uint limitaddr; + int adpcmlevel; + int adpcmvolume; + int adpcmvol; + uint deltan; + int adplc; + int adpld; + uint adplbase; + int adpcmx; + int adpcmd; + int adpcmout; + int apout0; + int apout1; + uint adpcmreadbuf; + bool adpcmplay; + int8 granuality; + bool adpcmmask_; + uint8 control1; + uint8 control2; + uint8 adpcmreg[8]; + int rhythmmask_; + struct Channel4Data ch[6]; + }; + + class OPNABase : public OPNBase + { + public: + OPNABase(); + ~OPNABase(); + + uint ReadStatus() { return status & 0x03; } + uint ReadStatusEx(); + void SetChannelMask(uint mask); + + // libOPNMIDI: soft panning + void SetPan(uint c, uint8 p); + + void DataSave(struct OPNABaseData* data); + void DataLoad(struct OPNABaseData* data); + + private: + virtual void Intr(bool) {} + + void MakeTable2(); + + protected: + bool Init(uint c, uint r, bool); + bool SetRate(uint c, uint r, bool); + + void Reset(); + void SetReg(uint addr, uint data); + void SetADPCMBReg(uint reg, uint data); + uint GetReg(uint addr); + + protected: + void FMMix(Sample* buffer, int nsamples); + void Mix6(Sample* buffer, int nsamples, int activech); + + void MixSubS(int activech, ISample**); + void MixSubSL(int activech, ISample**); + + void SetStatus(uint bit); + void ResetStatus(uint bit); + void UpdateStatus(); + void LFO(); + + void DecodeADPCMB(); + void ADPCMBMix(Sample* dest, uint count); + + void WriteRAM(uint data); + uint ReadRAM(); + int ReadRAMN(); + int DecodeADPCMBSample(uint); + + // FM 音æºé–¢ä¿‚ + uint8 pan[6]; + uint16 panvolume_l[6]; // libOPNMIDI: soft panning + uint16 panvolume_r[6]; // libOPNMIDI: soft panning + uint8 fnum2[9]; + + uint8 reg22; + uint reg29; // OPNA only? + + uint stmask; + uint statusnext; + + uint32 lfocount; + uint32 lfodcount; + + uint fnum[6]; + uint fnum3[3]; + + // ADPCM 関係 + uint8* adpcmbuf; // ADPCM RAM + uint adpcmmask; // メモリアドレスã«å¯¾ã™ã‚‹ãƒ“ットマスク + uint adpcmnotice; // ADPCM å†ç”Ÿçµ‚了時ã«ãŸã¤ãƒ“ット + uint startaddr; // Start address + uint stopaddr; // Stop address + uint memaddr; // å†ç”Ÿä¸­ã‚¢ãƒ‰ãƒ¬ã‚¹ + uint limitaddr; // Limit address/mask + int adpcmlevel; // ADPCM éŸ³é‡ + int adpcmvolume; + int adpcmvol; + uint deltan; // N + int adplc; // 周波数変æ›ç”¨å¤‰æ•° + int adpld; // 周波数変æ›ç”¨å¤‰æ•°å·®åˆ†å€¤ + uint adplbase; // adpld ã®å…ƒ + int adpcmx; // ADPCM åˆæˆç”¨ x + int adpcmd; // ADPCM åˆæˆç”¨ + int adpcmout; // ADPCM åˆæˆå¾Œã®å‡ºåŠ› + int apout0; // out(t-2)+out(t-1) + int apout1; // out(t-1)+out(t) + + uint adpcmreadbuf; // ADPCM リード用ãƒãƒƒãƒ•ã‚¡ + bool adpcmplay; // ADPCM å†ç”Ÿä¸­ + int8 granuality; + bool adpcmmask_; + + uint8 control1; // ADPCM コントロールレジスタ1 + uint8 control2; // ADPCM コントロールレジスタ2 + uint8 adpcmreg[8]; // ADPCM レジスタã®ä¸€éƒ¨åˆ† + + int rhythmmask_; + + Channel4 ch[6]; + + static void BuildLFOTable(); + static int amtable[FM_LFOENTS]; + static int pmtable[FM_LFOENTS]; + static int32 tltable[FM_TLENTS+FM_TLPOS]; + static bool tablehasmade; + }; + + // YM2203(OPN) ---------------------------------------------------- + struct OPNData { + struct OPNBaseData opnbase; + uint fnum[3]; + uint fnum3[3]; + uint8 fnum2[6]; + struct Channel4Data ch[3]; + }; + + class OPN : public OPNBase + { + public: + OPN(); + virtual ~OPN() {} + + bool Init(uint c, uint r, bool=false, const char* =0); + bool SetRate(uint c, uint r, bool=false); + + void Reset(); + void Mix(Sample* buffer, int nsamples); + void SetReg(uint addr, uint data); + uint GetReg(uint addr); + uint ReadStatus() { return status & 0x03; } + uint ReadStatusEx() { return 0xff; } + + void SetChannelMask(uint mask); + + int dbgGetOpOut(int c, int s) { return ch[c].op[s].dbgopout_; } + int dbgGetPGOut(int c, int s) { return ch[c].op[s].dbgpgout_; } + Channel4* dbgGetCh(int c) { return &ch[c]; } + + void DataSave(struct OPNData* data); + void DataLoad(struct OPNData* data); + + private: + virtual void Intr(bool) {} + + void SetStatus(uint bit); + void ResetStatus(uint bit); + + uint fnum[3]; + uint fnum3[3]; + uint8 fnum2[6]; + + Channel4 ch[3]; + }; + + // YM2608(OPNA) --------------------------------------------------- + struct Rhythm + { + uint8 pan; // ã±ã‚“ + int8 level; // ãŠã‚“りょㆠ+ int volume; // ãŠã‚“りょã†ã›ã£ã¦ã„ + int16* sample; // ã•ã‚“ã·ã‚‹ + uint size; // ã•ã„ãš + uint pos; // ã„ã¡ + uint step; // ã™ã¦ã£ã·ã¡ + uint rate; // ã•ã‚“ã·ã‚‹ã®ã‚Œãƒ¼ã¨ + }; + + struct OPNAData { + struct OPNABaseData opnabase; + Rhythm rhythm[6]; + int8 rhythmtl; + int rhythmtvol; + uint8 rhythmkey; + }; + + class OPNA : public OPNABase + { + public: + OPNA(); + virtual ~OPNA(); + + bool Init(uint c, uint r, bool = false, const char* rhythmpath=0); + bool LoadRhythmSample(const char*); + + bool SetRate(uint c, uint r, bool = false); + void Mix(Sample* buffer, int nsamples); + + void Reset(); + void SetReg(uint addr, uint data); + uint GetReg(uint addr); + + void SetVolumeADPCM(int db); + void SetVolumeRhythmTotal(int db); + void SetVolumeRhythm(int index, int db); + + uint8* GetADPCMBuffer() { return adpcmbuf; } + + int dbgGetOpOut(int c, int s) { return ch[c].op[s].dbgopout_; } + int dbgGetPGOut(int c, int s) { return ch[c].op[s].dbgpgout_; } + Channel4* dbgGetCh(int c) { return &ch[c]; } + + void DataSave(struct OPNAData* data); + void DataLoad(struct OPNAData* data); + + private: + void RhythmMix(Sample* buffer, uint count); + + // リズム音æºé–¢ä¿‚ + Rhythm rhythm[6]; + int8 rhythmtl; // リズム全体ã®éŸ³é‡ + int rhythmtvol; + uint8 rhythmkey; // リズムã®ã‚­ãƒ¼ + }; + + // YM2610/B(OPNB) --------------------------------------------------- + struct ADPCMA + { + uint8 pan; // ã±ã‚“ + int8 level; // ãŠã‚“りょㆠ+ int volume; // ãŠã‚“りょã†ã›ã£ã¦ã„ + uint pos; // ã„ã¡ + uint step; // ã™ã¦ã£ã·ã¡ + + uint start; // é–‹å§‹ + uint stop; // 終了 + uint nibble; // 次㮠4 bit + int adpcmx; // 変æ›ç”¨ + int adpcmd; // 変æ›ç”¨ + }; + + struct OPNBData { + struct OPNABaseData opnabase; +// uint8* adpcmabuf; + int adpcmasize; + ADPCMA adpcma[6]; + int8 adpcmatl; + int adpcmatvol; + uint8 adpcmakey; + int adpcmastep; + uint8 adpcmareg[32]; + struct Channel4Data ch[6]; + }; + + class OPNB : public OPNABase + { + public: + OPNB(); + virtual ~OPNB(); + + bool Init(uint c, uint r, bool = false, + uint8 *_adpcma = 0, int _adpcma_size = 0, + uint8 *_adpcmb = 0, int _adpcmb_size = 0); + + bool SetRate(uint c, uint r, bool = false); + void Mix(Sample* buffer, int nsamples); + + void Reset(); + void SetReg(uint addr, uint data); + uint GetReg(uint addr); + uint ReadStatusEx(); + + void SetVolumeADPCMATotal(int db); + void SetVolumeADPCMA(int index, int db); + void SetVolumeADPCMB(int db); + +// void SetChannelMask(uint mask); + + void DataSave(struct OPNBData* data, void* adpcmdata); + void DataLoad(struct OPNBData* data, void* adpcmdata); + + private: + int DecodeADPCMASample(uint); + void ADPCMAMix(Sample* buffer, uint count); + static void InitADPCMATable(); + + // ADPCMA 関係 + uint8* adpcmabuf; // ADPCMA ROM + int adpcmasize; + ADPCMA adpcma[6]; + int8 adpcmatl; // ADPCMA 全体ã®éŸ³é‡ + int adpcmatvol; + uint8 adpcmakey; // ADPCMA ã®ã‚­ãƒ¼ + int adpcmastep; + uint8 adpcmareg[32]; + + static int jedi_table[(48+1)*16]; + + Channel4 ch[6]; + }; + + // YM2612/3438(OPN2) ---------------------------------------------------- + class OPN2 : public OPNBase + { + public: + OPN2(); + virtual ~OPN2() {} + + bool Init(uint c, uint r, bool=false, const char* =0); + bool SetRate(uint c, uint r, bool); + + void Reset(); + void Mix(Sample* buffer, int nsamples); + void SetReg(uint addr, uint data); + uint GetReg(uint addr); + uint ReadStatus() { return status & 0x03; } + uint ReadStatusEx() { return 0xff; } + + void SetChannelMask(uint mask); + + private: + virtual void Intr(bool) {} + + void SetStatus(uint bit); + void ResetStatus(uint bit); + + uint fnum[3]; + uint fnum3[3]; + uint8 fnum2[6]; + + // 線形補間用ワーク + int32 mixc, mixc1; + + Channel4 ch[3]; + }; +} + +// --------------------------------------------------------------------------- + +inline void FM::OPNBase::RebuildTimeTable() +{ + int p = prescale; + prescale = -1; + SetPrescaler(p); +} + +inline void FM::OPNBase::SetVolumePSG(int db) +{ + psg.SetVolume(db); +} + +#endif // FM_OPNA_H diff --git a/thirdparty/opnmidi/chips/np2/fmgen_psg.cpp b/thirdparty/opnmidi/chips/np2/fmgen_psg.cpp new file mode 100644 index 000000000..aa1458821 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_psg.cpp @@ -0,0 +1,395 @@ +// --------------------------------------------------------------------------- +// PSG Sound Implementation +// Copyright (C) cisc 1997, 1999. +// --------------------------------------------------------------------------- +// $Id: psg.cpp,v 1.10 2002/05/15 21:38:01 cisc Exp $ + +#include "fmgen_headers.h" +#include "fmgen_misc.h" +#include "fmgen_psg.h" + +// --------------------------------------------------------------------------- +// コンストラクタ・デストラクタ +// +PSG::PSG() +{ + SetVolume(0); + MakeNoiseTable(); + Reset(); + mask = 0x3f; +} + +PSG::~PSG() +{ + +} + +// --------------------------------------------------------------------------- +// PSG ã‚’åˆæœŸåŒ–ã™ã‚‹(RESET) +// +void PSG::Reset() +{ + for (int i=0; i<14; i++) + SetReg(i, 0); + SetReg(7, 0xff); + SetReg(14, 0xff); + SetReg(15, 0xff); +} + +// --------------------------------------------------------------------------- +// クロック周波数ã®è¨­å®š +// +void PSG::SetClock(int clock, int rate) +{ + tperiodbase = int((1 << toneshift ) / 4.0 * clock / rate); + eperiodbase = int((1 << envshift ) / 4.0 * clock / rate); + nperiodbase = int((1 << noiseshift) / 4.0 * clock / rate); + + // å„ãƒ‡ãƒ¼ã‚¿ã®æ›´æ–° + int tmp; + tmp = ((reg[0] + reg[1] * 256) & 0xfff); + speriod[0] = tmp ? tperiodbase / tmp : tperiodbase; + tmp = ((reg[2] + reg[3] * 256) & 0xfff); + speriod[1] = tmp ? tperiodbase / tmp : tperiodbase; + tmp = ((reg[4] + reg[5] * 256) & 0xfff); + speriod[2] = tmp ? tperiodbase / tmp : tperiodbase; + tmp = reg[6] & 0x1f; + nperiod = tmp ? nperiodbase / tmp / 2 : nperiodbase / 2; + tmp = ((reg[11] + reg[12] * 256) & 0xffff); + eperiod = tmp ? eperiodbase / tmp : eperiodbase * 2; +} + +// --------------------------------------------------------------------------- +// ノイズテーブルを作æˆã™ã‚‹ +// +void PSG::MakeNoiseTable() +{ + if (!noisetable[0]) + { + int noise = 14321; + for (int i=0; i> 1) | (((noise << 14) ^ (noise << 16)) & 0x10000); + } + noisetable[i] = n; + } + } +} + +// --------------------------------------------------------------------------- +// å‡ºåŠ›ãƒ†ãƒ¼ãƒ–ãƒ«ã‚’ä½œæˆ +// ç´ ç›´ã«ãƒ†ãƒ¼ãƒ–ãƒ«ã§æŒã£ãŸã»ã†ãŒçœã‚¹ãƒšãƒ¼ã‚¹ã€‚ +// +void PSG::SetVolume(int volume) +{ + double base = 0x4000 / 3.0 * pow(10.0, volume / 40.0); + for (int i=31; i>=2; i--) + { + EmitTable[i] = int(base); + base /= 1.189207115; + } + EmitTable[1] = 0; + EmitTable[0] = 0; + MakeEnvelopTable(); + + SetChannelMask(~mask); +} + +void PSG::SetChannelMask(int c) +{ + mask = ~c; + for (int i=0; i<3; i++) + olevel[i] = mask & (1 << i) ? EmitTable[(reg[8+i] & 15) * 2 + 1] : 0; +} + +// --------------------------------------------------------------------------- +// エンベロープ波形テーブル +// +void PSG::MakeEnvelopTable() +{ + // 0 lo 1 up 2 down 3 hi + static uint8 table1[16*2] = + { + 2,0, 2,0, 2,0, 2,0, 1,0, 1,0, 1,0, 1,0, + 2,2, 2,0, 2,1, 2,3, 1,1, 1,3, 1,2, 1,0, + }; + static uint8 table2[4] = { 0, 0, 31, 31 }; + static int8 table3[4] = { 0, 1, -1, 0 }; + + uint* ptr = enveloptable[0]; + + for (int i=0; i<16*2; i++) + { + uint8 v = table2[table1[i]]; + + for (int j=0; j<32; j++) + { + *ptr++ = EmitTable[v]; + v += table3[table1[i]]; + } + } +} + +// --------------------------------------------------------------------------- +// PSG ã®ãƒ¬ã‚¸ã‚¹ã‚¿ã«å€¤ã‚’セットã™ã‚‹ +// regnum レジスタã®ç•ªå· (0 - 15) +// data セットã™ã‚‹å€¤ +// +void PSG::SetReg(uint regnum, uint8 data) +{ + if (regnum < 0x10) + { + reg[regnum] = data; + switch (regnum) + { + int tmp; + + case 0: // ChA Fine Tune + case 1: // ChA Coarse Tune + tmp = ((reg[0] + reg[1] * 256) & 0xfff); + speriod[0] = tmp ? tperiodbase / tmp : tperiodbase; + break; + + case 2: // ChB Fine Tune + case 3: // ChB Coarse Tune + tmp = ((reg[2] + reg[3] * 256) & 0xfff); + speriod[1] = tmp ? tperiodbase / tmp : tperiodbase; + break; + + case 4: // ChC Fine Tune + case 5: // ChC Coarse Tune + tmp = ((reg[4] + reg[5] * 256) & 0xfff); + speriod[2] = tmp ? tperiodbase / tmp : tperiodbase; + break; + + case 6: // Noise generator control + data &= 0x1f; + nperiod = data ? nperiodbase / data : nperiodbase; + break; + + case 8: + olevel[0] = mask & 1 ? EmitTable[(data & 15) * 2 + 1] : 0; + break; + + case 9: + olevel[1] = mask & 2 ? EmitTable[(data & 15) * 2 + 1] : 0; + break; + + case 10: + olevel[2] = mask & 4 ? EmitTable[(data & 15) * 2 + 1] : 0; + break; + + case 11: // Envelop period + case 12: + tmp = ((reg[11] + reg[12] * 256) & 0xffff); + eperiod = tmp ? eperiodbase / tmp : eperiodbase * 2; + break; + + case 13: // Envelop shape + ecount = 0; + envelop = enveloptable[data & 15]; + break; + } + } +} + +// --------------------------------------------------------------------------- +void PSG::DataSave(struct PSGData* data) { + memcpy(data->reg, reg, 16); + memcpy(data->olevel, olevel, sizeof(uint) * 6); + memcpy(data->scount, scount, sizeof(uint32) * 3); + memcpy(data->speriod, speriod, sizeof(uint32) * 3); + data->ecount = ecount; + data->eperiod = eperiod; + data->ncount = ncount; + data->nperiod = nperiod; + data->tperiodbase = tperiodbase; + data->eperiodbase = eperiodbase; + data->nperiodbase = nperiodbase; + data->volume = volume; + data->mask = mask; +} + +// --------------------------------------------------------------------------- +void PSG::DataLoad(struct PSGData* data) { + memcpy(reg, data->reg, 16); + memcpy(olevel, data->olevel, sizeof(uint) * 6); + memcpy(scount, data->scount, sizeof(uint32) * 3); + memcpy(speriod, data->speriod, sizeof(uint32) * 3); + ecount = data->ecount; + eperiod = data->eperiod; + ncount = data->ncount; + nperiod = data->nperiod; + tperiodbase = data->tperiodbase; + eperiodbase = data->eperiodbase; + nperiodbase = data->nperiodbase; + volume = data->volume; + mask = data->mask; +} + +// --------------------------------------------------------------------------- +// +// +inline void PSG::StoreSample(Sample& dest, int32 data) +{ + if (sizeof(Sample) == 2) + dest = (Sample) Limit(dest + data, 0x7fff, -0x8000); + else + dest += data; +} + +// --------------------------------------------------------------------------- +// PCM データをåã出ã™(2ch) +// dest PCM データを展開ã™ã‚‹ãƒã‚¤ãƒ³ã‚¿ +// nsamples 展開ã™ã‚‹ PCM ã®ã‚µãƒ³ãƒ—ル数 +// +void PSG::Mix(Sample* dest, int nsamples) +{ + uint8 chenable[3], nenable[3]; + uint8 r7 = ~reg[7]; + + if ((r7 & 0x3f) | ((reg[8] | reg[9] | reg[10]) & 0x1f)) + { + chenable[0] = (r7 & 0x01) && (speriod[0] <= (1 << toneshift)); + chenable[1] = (r7 & 0x02) && (speriod[1] <= (1 << toneshift)); + chenable[2] = (r7 & 0x04) && (speriod[2] <= (1 << toneshift)); + nenable[0] = (r7 >> 3) & 1; + nenable[1] = (r7 >> 4) & 1; + nenable[2] = (r7 >> 5) & 1; + + int noise, sample; + uint env; + uint* p1 = ((mask & 1) && (reg[ 8] & 0x10)) ? &env : &olevel[0]; + uint* p2 = ((mask & 2) && (reg[ 9] & 0x10)) ? &env : &olevel[1]; + uint* p3 = ((mask & 4) && (reg[10] & 0x10)) ? &env : &olevel[2]; + + #define SCOUNT(ch) (scount[ch] >> (toneshift+oversampling)) + + if (p1 != &env && p2 != &env && p3 != &env) + { + // エンベロープ無㗠+ if ((r7 & 0x38) == 0) + { + // ノイズ無㗠+ for (int i=0; i> (noiseshift+oversampling+6)) & (noisetablesize-1)] + >> (ncount >> (noiseshift+oversampling+1)); +#else + noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)] + >> (ncount >> (noiseshift+oversampling+1) & 31); +#endif + ncount += nperiod; + + int x, y, z; + x = ((SCOUNT(0) & chenable[0]) | (nenable[0] & noise)) - 1; // 0 or -1 + sample += (olevel[0] + x) ^ x; + scount[0] += speriod[0]; + y = ((SCOUNT(1) & chenable[1]) | (nenable[1] & noise)) - 1; + sample += (olevel[1] + y) ^ y; + scount[1] += speriod[1]; + z = ((SCOUNT(2) & chenable[2]) | (nenable[2] & noise)) - 1; + sample += (olevel[2] + z) ^ z; + scount[2] += speriod[2]; + } + sample /= (1 << oversampling); + StoreSample(dest[0], sample); + StoreSample(dest[1], sample); + dest += 2; + } + } + + // エンベロープã®è¨ˆç®—ã‚’ã•ã¼ã£ãŸå¸³å°»ã‚ã‚ã› + ecount = (ecount >> 8) + (eperiod >> (8-oversampling)) * nsamples; + if (ecount >= (1 << (envshift+6+oversampling-8))) + { + if ((reg[0x0d] & 0x0b) != 0x0a) + ecount |= (1 << (envshift+5+oversampling-8)); + ecount &= (1 << (envshift+6+oversampling-8)) - 1; + } + ecount <<= 8; + } + else + { + // エンベロープã‚り + for (int i=0; i> (envshift+oversampling)]; + ecount += eperiod; + if (ecount >= (1 << (envshift+6+oversampling))) + { + if ((reg[0x0d] & 0x0b) != 0x0a) + ecount |= (1 << (envshift+5+oversampling)); + ecount &= (1 << (envshift+6+oversampling)) - 1; + } +#ifdef _M_IX86 + noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)] + >> (ncount >> (noiseshift+oversampling+1)); +#else + noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)] + >> (ncount >> (noiseshift+oversampling+1) & 31); +#endif + ncount += nperiod; + + int x, y, z; + x = ((SCOUNT(0) & chenable[0]) | (nenable[0] & noise)) - 1; // 0 or -1 + sample += (*p1 + x) ^ x; + scount[0] += speriod[0]; + y = ((SCOUNT(1) & chenable[1]) | (nenable[1] & noise)) - 1; + sample += (*p2 + y) ^ y; + scount[1] += speriod[1]; + z = ((SCOUNT(2) & chenable[2]) | (nenable[2] & noise)) - 1; + sample += (*p3 + z) ^ z; + scount[2] += speriod[2]; + } + sample /= (1 << oversampling); + StoreSample(dest[0], sample); + StoreSample(dest[1], sample); + dest += 2; + } + } + } +} + +// --------------------------------------------------------------------------- +// テーブル +// +uint PSG::noisetable[noisetablesize] = { 0, }; +int PSG::EmitTable[0x20] = { -1, }; +uint PSG::enveloptable[16][64] = { {0, } }; diff --git a/thirdparty/opnmidi/chips/np2/fmgen_psg.h b/thirdparty/opnmidi/chips/np2/fmgen_psg.h new file mode 100644 index 000000000..fe085c585 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_psg.h @@ -0,0 +1,115 @@ +// --------------------------------------------------------------------------- +// PSG-like sound generator +// Copyright (C) cisc 1997, 1999. +// --------------------------------------------------------------------------- +// $Id: psg.h,v 1.8 2003/04/22 13:12:53 cisc Exp $ + +#ifndef PSG_H +#define PSG_H + +#include "fmgen_types.h" + +// libOPNMIDI: change int32 to int16 +#define PSG_SAMPLETYPE int16 // int32 or int16 + +// --------------------------------------------------------------------------- +// class PSG +// PSG ã«è‰¯ãä¼¼ãŸéŸ³ã‚’生æˆã™ã‚‹éŸ³æºãƒ¦ãƒ‹ãƒƒãƒˆ +// +// interface: +// bool SetClock(uint clock, uint rate) +// åˆæœŸåŒ–.ã“ã®ã‚¯ãƒ©ã‚¹ã‚’使用ã™ã‚‹å‰ã«ã‹ãªã‚‰ãšå‘¼ã‚“ã§ãŠãã“ã¨ï¼Ž +// PSG ã®ã‚¯ãƒ­ãƒƒã‚¯ã‚„ PCM レートを設定ã™ã‚‹ +// +// clock: PSG ã®å‹•作クロック +// rate: 生æˆã™ã‚‹ PCM ã®ãƒ¬ãƒ¼ãƒˆ +// retval åˆæœŸåŒ–ã«æˆåŠŸã™ã‚Œã° true +// +// void Mix(Sample* dest, int nsamples) +// PCM ã‚’ nsamples åˆ†åˆæˆã—, dest ã§å§‹ã¾ã‚‹é…列ã«åŠ ãˆã‚‹(加算ã™ã‚‹) +// ã‚ãã¾ã§åŠ ç®—ãªã®ã§ï¼Œæœ€åˆã«é…列をゼロクリアã™ã‚‹å¿…è¦ãŒã‚ã‚‹ +// +// void Reset() +// リセットã™ã‚‹ +// +// void SetReg(uint reg, uint8 data) +// レジスタ reg ã« data を書ã込む +// +// uint GetReg(uint reg) +// レジスタ reg ã®å†…容を読ã¿å‡ºã™ +// +// void SetVolume(int db) +// å„音æºã®éŸ³é‡ã‚’調節ã™ã‚‹ +// å˜ä½ã¯ç´„ 1/2 dB +// +struct PSGData { + uint8 reg[16]; + uint olevel[3]; + uint32 scount[3]; + uint32 speriod[3]; + uint32 ecount; + uint32 eperiod; + uint32 ncount; + uint32 nperiod; + uint32 tperiodbase; + uint32 eperiodbase; + uint32 nperiodbase; + int volume; + int mask; +}; + +class PSG +{ +public: + typedef PSG_SAMPLETYPE Sample; + + enum + { + noisetablesize = 1 << 11, // â†ãƒ¡ãƒ¢ãƒªä½¿ç”¨é‡ã‚’減らã—ãŸã„ãªã‚‰æ¸›ã‚‰ã—㦠+ toneshift = 24, + envshift = 22, + noiseshift = 14, + oversampling = 2 // ↠音質より速度ãŒå„ªå…ˆãªã‚‰æ¸›ã‚‰ã™ã¨ã„ã„ã‹ã‚‚ + }; + +public: + PSG(); + ~PSG(); + + void Mix(Sample* dest, int nsamples); + void SetClock(int clock, int rate); + + void SetVolume(int vol); + void SetChannelMask(int c); + + void Reset(); + void SetReg(uint regnum, uint8 data); + uint GetReg(uint regnum) { return reg[regnum & 0x0f]; } + + void DataSave(struct PSGData* data); + void DataLoad(struct PSGData* data); + +protected: + void MakeNoiseTable(); + void MakeEnvelopTable(); + static void StoreSample(Sample& dest, int32 data); + + uint8 reg[16]; + + const uint* envelop; + uint olevel[3]; + uint32 scount[3], speriod[3]; + uint32 ecount, eperiod; + uint32 ncount, nperiod; + uint32 tperiodbase; + uint32 eperiodbase; + uint32 nperiodbase; + int volume; + int mask; + + static uint enveloptable[16][64]; + static uint noisetable[noisetablesize]; + static int EmitTable[32]; +}; + +#endif // PSG_H diff --git a/thirdparty/opnmidi/chips/np2/fmgen_readme.txt b/thirdparty/opnmidi/chips/np2/fmgen_readme.txt new file mode 100644 index 000000000..05625be57 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_readme.txt @@ -0,0 +1,93 @@ +------------------------------------------------------------------------------ + FM Sound Generator with OPN/OPM interface + Copyright (C) by cisc 1998, 2003. +------------------------------------------------------------------------------ + +ã€æ¦‚è¦ã€‘ + C++ ã«ã‚ˆã‚‹ FM/PSG 音æºã®å®Ÿè£…ã§ã™ã€‚ + + AY8910, YM2203, YM2151, YM2608, YM2610 相当ã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェースも + 実装ã—ã¦ã‚りã¾ã™ã€‚ + + +ã€ä½¿ã„方】 + (TODO:中身を書ã) + +ã€æ³¨æ„】 + 以å‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‹ã‚‰å¹¾åˆ†æ‰‹ã‚’加ãˆã¾ã—ãŸã€‚インターフェース㯠+ ã„ã˜ã£ã¦ã„ãªã„ã¤ã‚‚りã§ã™ãŒã€ä½•ã‹ã—らã®ãƒã‚°ãŒéš ã‚Œã¦ã„ã‚‹ã‹ã‚‚ã—れã¾ã›ã‚“。 + + YM2610 ã¯å‹•作ãƒã‚§ãƒƒã‚¯ã™ã‚‰ã—ã¦ã„ã¾ã›ã‚“。 + + 線形補完モード(interpolation)ã¯å»ƒæ­¢ã•れã¾ã—ãŸã€‚ + Init() ã®å¼•数仕様ã¯å¤‰ã‚ã£ã¦ã„ã¾ã›ã‚“ãŒã€interpolation = true ã«ã—ã¦ã‚‚ + 動作ã¯å¤‰ã‚りã¾ã›ã‚“。 + + OPNA::Init/SetRate ã§ä¸Žãˆã‚‹ãƒãƒƒãƒ—クロックã®å€¤ã®ä»•様㌠+ 以å‰ã® M88 ã«æ­è¼‰ã•れã¦ã„ãŸãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¨ç•°ãªã£ã¦ã„ã¾ã™ï¼Ž + + +ã€è‘—作権ã€å…責è¦å®šã€‘ + + ・本ソースコードã¯ä½œè€…(cisc@retropc.net) ãŒè‘—作権を所有ã—ã¦ã„ã¾ã™ã€‚ + + ・本ソースコードã¯ã‚ã‚‹ãŒã¾ã¾ã«æä¾›ã•れるもã®ã§ã‚り, + æš—é»™åŠã³æ˜Žç¤ºçš„ãªä¿è¨¼ã‚’一切å«ã¿ã¾ã›ã‚“. + + ・本ソースコードを利用ã—ãŸã“ã¨ï¼Œåˆ©ç”¨ã—ãªã‹ã£ãŸã“ã¨ï¼Œ + 利用ã§ããªã‹ã£ãŸã“ã¨ã«é–¢ã—ã¦ç”Ÿã˜ãŸã‚ã‚‹ã„ã¯ç”Ÿã˜ã‚‹ã¨äºˆæ¸¬ã•れる + æå®³ã«ã¤ã„ã¦ï¼Œä½œè€…ã¯ä¸€åˆ‡è²¬ä»»ã‚’è² ã„ã¾ã›ã‚“. + + ・本ソースコードã¯ï¼Œä»¥ä¸‹ã®åˆ¶é™ã‚’満ãŸã™é™ã‚Šè‡ªç”±ã«æ”¹å¤‰ãƒ»çµ„ã¿è¾¼ã¿ãƒ» + é…布・利用ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼Ž + + 1. 本ソフトã®ç”±æ¥(作者, 著作権)を明記ã™ã‚‹ã“ã¨. + 2. é…布ã™ã‚‹éš›ã«ã¯ãƒ•リーソフトã¨ã™ã‚‹ã“ã¨ï¼Ž + 3. 改変ã—ãŸã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’é…布ã™ã‚‹éš›ã¯æ”¹å¤‰å†…容を明示ã™ã‚‹ã“ã¨. + 4. ソースコードをé…布ã™ã‚‹éš›ã«ã¯ã“ã®ãƒ†ã‚­ã‚¹ãƒˆã‚’一切改変ã›ãšã« + ãã®ã¾ã¾æ·»ä»˜ã™ã‚‹ã“ã¨ï¼Ž + + ・公開ã®éš›ã«ä½œè€…ã¸ã®é€£çµ¡ã‚’é ‚ã‘れã°å¹¸ã„ã§ã™ï¼Ž + + ・商用ソフト(シェアウェアå«ã‚€) ã«æœ¬ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã®ä¸€éƒ¨ï¼Œã¾ãŸã¯ + 全部を組ã¿è¾¼ã‚€éš›ã«ã¯ï¼Œäº‹å‰ã«ä½œè€…ã®åˆæ„ã‚’å¾—ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼Ž + + +ã€å¤‰æ›´ç‚¹ã€‘ +008 030902 ・出力サンプリングレートã®è¨­å®šã‚’ã€ãƒãƒƒãƒ—å…ƒæ¥ã®åˆæˆã‚µãƒ³ãƒ— + リングレートã¨ç•°ãªã‚‹è¨­å®šã«ã™ã‚‹ã¨ã‚¨ãƒ³ãƒ™ãƒ­ãƒ¼ãƒ—ãŒãŠã‹ã—ã + ãªã‚‹å•題を修正。 + +007a 030608 ・領域外アクセスãŒã‚ã£ãŸã®ã‚’修正 (Thanks to PI.様) + +007 030607 ・å†ç¾æ€§ã®å‘上 + ・OPN: SSG-EG ã®ã‚µãƒãƒ¼ãƒˆ + ・線形補完ã®å»ƒæ­¢ + ・asm 版ã®å»ƒæ­¢ + ・マルãƒã‚¹ãƒ¬ãƒƒãƒ‰ãƒ•リーã«ãªã£ãŸï¼Ÿ + +006 010330 ・å†ç¾æ€§ã«é–¢ã—ã¦ã„ãã‚‰ã‹æ‰‹ç›´ã—(正弦波,出力タイミング等) + +005 010317 ・OPN: FM 音æºã®åˆæˆå‘¨æ³¢æ•°ãŒå‡ºåŠ›å‘¨æ³¢æ•°ã‚ˆã‚Šã‚‚ä½Žã„ã¨ãã«ï¼Œ + 補完を使ã†ã¨éŸ³ç—´ã«ãªã‚‹/音ãŒã¾ã¨ã‚‚ã«å‡ºãªããªã‚‹å•題を修正. + ・FM: 補完を使ã‚ãªã„時ã®ç²¾åº¦ã‚’上ã’ã¦ã¿ã‚‹ï¼Ž + å•題ãŒèµ·ããŸã‚‰ fmgeninl.h ã® FM_RATIOBITS ã‚’ 8 ã«æˆ»ã™ã¨å‰ï¼Ž + +004a 010311 ・OPM: ノイズをãã“ãã“èžã‘るレベル(?)ã¾ã§ä¿®æ­£. + ・OPNA/OPM: FM_USE_CALC2 廃止. + ・デグレ修正, ãªã‚“ã®ãŸã‚ã« cvs ã§ç®¡ç†ã—ã¦ã„ã‚‹ã‚“ã ã‹â€¦(T-T + OPNB: ADPCMB ROM マスク作æˆãƒŸã‚¹. + OPNB: ADPCMA ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒ•ãƒ©ã‚°é–¢ä¿‚ã®æŒ™å‹•修復. + OPM: LFO パラメータã®åˆæœŸåŒ–を忘れã¦ã„ãŸã®ã‚’修正. + +003 010124 ・OPNA/OPM: 実際ã«ã¯è£œå®Œå‰ã®å€¤ã‚’出力ã—ã¦ã„ãŸãƒã‚°ã‚’修正. + +002 010123 ãƒ»åˆæˆå‘¨æ³¢æ•°ãŒå‡ºåŠ›å‘¨æ³¢æ•°ã‚ˆã‚Šä½Žã„ã¨ãã§ã‚‚補間ãŒåйãよã†ã«ã™ã‚‹. + ・OPN: 補間時ã«ãƒ—リスケーラã®è¨­å®šã‚’変更ã—ãŸã¨ãã«éŸ³åŒ–ã‘ã™ã‚‹ + ãƒã‚°ã‚’修正. + ・OPNA/B: LFO ãŒåйã‹ãªã„ãƒã‚°ã‚’修正. + +001 000928 ãƒ»åˆæœŸãƒãƒ¼ã‚¸ãƒ§ãƒ³ + + +$Id: readme.txt,v 1.1 2003/06/07 08:25:20 cisc Exp $ diff --git a/thirdparty/opnmidi/chips/np2/fmgen_readme_kai.txt b/thirdparty/opnmidi/chips/np2/fmgen_readme_kai.txt new file mode 100644 index 000000000..9481c9c37 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_readme_kai.txt @@ -0,0 +1,24 @@ +NP2kai ã«ãŠã„㦠fmgen 008 ã¸è¡Œã£ãŸæ”¹å¤‰ï¼Ž by AZO + +1.ファイルåã®å¤‰æ›´ï¼Ž (fmgen_*.*) + +2.fmgen.cpp Operator::MakeTable() ã®ã‚¤ãƒ³ã‚¯ãƒªãƒ¡ãƒ³ãƒˆä¿®æ­£ï¼Ž + +[修正å‰] +*p++ = p[-512] / 2; + +[修正後] +//*p++ = p[-512] / 2; +*p = p[-512] / 2; +p++; + +3.å„クラスã®ã‚¹ãƒ†ãƒ¼ãƒˆã‚»ãƒ¼ãƒ–用構造体ã¨ã€ã‚¹ãƒ†ãƒ¼ãƒˆã‚»ãƒ¼ãƒ–ï¼ãƒ­ãƒ¼ãƒ‰å®Ÿè£… + +struct xxxData : ステートセーブ用構造体 + +xxx_DataSave() : ステートセーブ +xxx_DataLoad() : ステートロード + +4.C言語用ラッパー fmgen_fmgwrap.cpp/.h + +以上. diff --git a/thirdparty/opnmidi/chips/np2/fmgen_types.h b/thirdparty/opnmidi/chips/np2/fmgen_types.h new file mode 100644 index 000000000..c06dd4bb5 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2/fmgen_types.h @@ -0,0 +1,23 @@ +#if !defined(win32_types_h) +#define win32_types_h + +#include "compiler.h" + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; + +typedef signed char sint8; +typedef signed short sint16; +typedef signed int sint32; + +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; + +#endif // win32_types_h diff --git a/thirdparty/opnmidi/chips/np2_opna.cpp b/thirdparty/opnmidi/chips/np2_opna.cpp new file mode 100644 index 000000000..c92ee38f8 --- /dev/null +++ b/thirdparty/opnmidi/chips/np2_opna.cpp @@ -0,0 +1,95 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "np2_opna.h" +#include "np2/fmgen_opna.h" +#include +#include +#include +#include + +template +NP2OPNA::NP2OPNA(OPNFamily f) + : ChipBase(f) +{ + ChipType *opn = (ChipType *)std::calloc(1, sizeof(ChipType)); + chip = new(opn) ChipType; + opn->Init(ChipBase::m_clock, ChipBase::m_rate); + opn->SetReg(0x29, 0x9f); // enable channels 4-6 +} + +template +NP2OPNA::~NP2OPNA() +{ + chip->~ChipType(); + std::free(chip); +} + +template +void NP2OPNA::setRate(uint32_t rate, uint32_t clock) +{ + ChipBase::setRate(rate, clock); + uint32_t chipRate = ChipBase::isRunningAtPcmRate() ? rate : ChipBase::nativeRate(); + chip->SetRate(clock, chipRate, false); // implies Reset() + chip->SetReg(0x29, 0x9f); // enable channels 4-6 +} + +template +void NP2OPNA::reset() +{ + ChipBase::reset(); + chip->Reset(); + chip->SetReg(0x29, 0x9f); // enable channels 4-6 +} + +template +void NP2OPNA::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + chip->SetReg((port << 8) | addr, data); +} + +template +void NP2OPNA::writePan(uint16_t chan, uint8_t data) +{ + chip->SetPan(chan, data); +} + +template +void NP2OPNA::nativeGenerateN(int16_t *output, size_t frames) +{ + std::memset(output, 0, 2 * frames * sizeof(output[0])); + chip->Mix(output, static_cast(frames)); +} + +template <> +const char *NP2OPNA::emulatorName() +{ + return "Neko Project II Kai OPNA"; // git 2018-10-28 rev e1c0609 +} + +template <> +const char *NP2OPNA::emulatorName() +{ + return "Neko Project II Kai OPNB"; // git 2018-10-28 rev e1c0609 +} + +// template class NP2OPNA; +template class NP2OPNA; +template class NP2OPNA; diff --git a/thirdparty/opnmidi/chips/np2_opna.h b/thirdparty/opnmidi/chips/np2_opna.h new file mode 100644 index 000000000..0da05f71d --- /dev/null +++ b/thirdparty/opnmidi/chips/np2_opna.h @@ -0,0 +1,48 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2018-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NP2_OPNA_H +#define NP2_OPNA_H + +#include "opn_chip_base.h" + +namespace FM { class OPN2; class OPNA; class OPNB; } +template +class NP2OPNA final : public OPNChipBaseBufferedT > +{ + typedef OPNChipBaseBufferedT > ChipBase; + ChipType *chip; +public: + explicit NP2OPNA(OPNFamily f); + ~NP2OPNA() override; + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t chan, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerateN(int16_t *output, size_t frames) override; + const char *emulatorName() override; + enum { resamplerPostAttenuate = 2 }; +}; + +#endif diff --git a/thirdparty/opnmidi/chips/nuked/ym3438.c b/thirdparty/opnmidi/chips/nuked/ym3438.c new file mode 100644 index 000000000..b2f75a50b --- /dev/null +++ b/thirdparty/opnmidi/chips/nuked/ym3438.c @@ -0,0 +1,1630 @@ +/* + * Copyright (C) 2017-2018 Alexey Khokholov (Nuke.YKT) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * + * Nuked OPN2(Yamaha YM3438) emulator. + * Thanks: + * Silicon Pr0n: + * Yamaha YM3438 decap and die shot(digshadow). + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.0.9 + */ + +#include +#include "ym3438.h" + +enum { + eg_num_attack = 0, + eg_num_decay = 1, + eg_num_sustain = 2, + eg_num_release = 3 +}; + +/* logsin table */ +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +/* exp table */ +static const Bit16u exprom[256] = { + 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, + 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, + 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, + 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, + 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, + 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, + 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, + 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, + 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, + 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, + 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, + 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, + 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, + 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, + 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, + 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, + 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, + 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, + 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, + 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, + 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, + 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, + 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, + 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, + 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, + 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, + 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, + 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, + 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, + 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa +}; + +/* Note table */ +static const Bit32u fn_note[16] = { + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3 +}; + +/* Envelope generator */ +static const Bit32u eg_stephi[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +static const Bit8u eg_am_shift[4] = { + 7, 3, 1, 0 +}; + +/* Phase generator */ +static const Bit32u pg_detune[8] = { 16, 17, 19, 20, 22, 24, 27, 29 }; + +static const Bit32u pg_lfo_sh1[8][8] = { + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 7, 7, 1, 1 }, + { 7, 7, 7, 7, 1, 1, 1, 1 }, + { 7, 7, 7, 1, 1, 1, 1, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 } +}; + +static const Bit32u pg_lfo_sh2[8][8] = { + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 2, 2, 2, 2 }, + { 7, 7, 7, 2, 2, 2, 7, 7 }, + { 7, 7, 2, 2, 7, 7, 2, 2 }, + { 7, 7, 2, 7, 7, 7, 2, 7 }, + { 7, 7, 7, 2, 7, 7, 2, 1 }, + { 7, 7, 7, 2, 7, 7, 2, 1 }, + { 7, 7, 7, 2, 7, 7, 2, 1 } +}; + +/* Address decoder */ +static const Bit32u op_offset[12] = { + 0x000, /* Ch1 OP1/OP2 */ + 0x001, /* Ch2 OP1/OP2 */ + 0x002, /* Ch3 OP1/OP2 */ + 0x100, /* Ch4 OP1/OP2 */ + 0x101, /* Ch5 OP1/OP2 */ + 0x102, /* Ch6 OP1/OP2 */ + 0x004, /* Ch1 OP3/OP4 */ + 0x005, /* Ch2 OP3/OP4 */ + 0x006, /* Ch3 OP3/OP4 */ + 0x104, /* Ch4 OP3/OP4 */ + 0x105, /* Ch5 OP3/OP4 */ + 0x106 /* Ch6 OP3/OP4 */ +}; + +static const Bit32u ch_offset[6] = { + 0x000, /* Ch1 */ + 0x001, /* Ch2 */ + 0x002, /* Ch3 */ + 0x100, /* Ch4 */ + 0x101, /* Ch5 */ + 0x102 /* Ch6 */ +}; + +/* LFO */ +static const Bit32u lfo_cycles[8] = { + 108, 77, 71, 67, 62, 44, 8, 5 +}; + +/* FM algorithm */ +static const Bit32u fm_algorithm[4][6][8] = { + { + { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_0 */ + { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_1 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 1 } /* Out */ + }, + { + { 0, 1, 0, 0, 0, 1, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 1, 1, 1, 0, 0, 0, 0, 0 }, /* OP2 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 1, 1, 1 } /* Out */ + }, + { + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ + { 1, 0, 0, 1, 1, 1, 1, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 1, 1, 1, 1 } /* Out */ + }, + { + { 0, 0, 1, 0, 0, 1, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 0, 0, 0, 1, 0, 0, 0, 0 }, /* OP2 */ + { 1, 1, 0, 1, 1, 0, 0, 0 }, /* Last operator */ + { 0, 0, 1, 0, 0, 0, 0, 0 }, /* Last operator */ + { 1, 1, 1, 1, 1, 1, 1, 1 } /* Out */ + } +}; + +/* + * Pan law table + */ + +static const Bit16u panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + +static Bit32u chip_type = ym3438_mode_readmode; + +void OPN2_DoIO(ym3438_t *chip) +{ + /* Write signal check */ + chip->write_a_en = (chip->write_a & 0x03) == 0x01; + chip->write_d_en = (chip->write_d & 0x03) == 0x01; + chip->write_a <<= 1; + chip->write_d <<= 1; + /* Busy counter */ + chip->busy = chip->write_busy; + chip->write_busy_cnt += chip->write_busy; + chip->write_busy = (chip->write_busy && !(chip->write_busy_cnt >> 5)) || chip->write_d_en; + chip->write_busy_cnt &= 0x1f; +} + +void OPN2_DoRegWrite(ym3438_t *chip) +{ + Bit32u i; + Bit32u slot = chip->cycles % 12; + Bit32u address; + Bit32u channel = chip->channel; + /* Update registers */ + if (chip->write_fm_data) + { + /* Slot */ + if (op_offset[slot] == (chip->address & 0x107)) + { + if (chip->address & 0x08) + { + /* OP2, OP4 */ + slot += 12; + } + address = chip->address & 0xf0; + switch (address) + { + case 0x30: /* DT, MULTI */ + chip->multi[slot] = chip->data & 0x0f; + if (!chip->multi[slot]) + { + chip->multi[slot] = 1; + } + else + { + chip->multi[slot] <<= 1; + } + chip->dt[slot] = (chip->data >> 4) & 0x07; + break; + case 0x40: /* TL */ + chip->tl[slot] = chip->data & 0x7f; + break; + case 0x50: /* KS, AR */ + chip->ar[slot] = chip->data & 0x1f; + chip->ks[slot] = (chip->data >> 6) & 0x03; + break; + case 0x60: /* AM, DR */ + chip->dr[slot] = chip->data & 0x1f; + chip->am[slot] = (chip->data >> 7) & 0x01; + break; + case 0x70: /* SR */ + chip->sr[slot] = chip->data & 0x1f; + break; + case 0x80: /* SL, RR */ + chip->rr[slot] = chip->data & 0x0f; + chip->sl[slot] = (chip->data >> 4) & 0x0f; + chip->sl[slot] |= (chip->sl[slot] + 1) & 0x10; + break; + case 0x90: /* SSG-EG */ + chip->ssg_eg[slot] = chip->data & 0x0f; + break; + default: + break; + } + } + + /* Channel */ + if (ch_offset[channel] == (chip->address & 0x103)) + { + address = chip->address & 0xfc; + switch (address) + { + case 0xa0: + chip->fnum[channel] = (chip->data & 0xff) | ((chip->reg_a4 & 0x07) << 8); + chip->block[channel] = (chip->reg_a4 >> 3) & 0x07; + chip->kcode[channel] = (chip->block[channel] << 2) | fn_note[chip->fnum[channel] >> 7]; + break; + case 0xa4: + chip->reg_a4 = chip->data & 0xff; + break; + case 0xa8: + chip->fnum_3ch[channel] = (chip->data & 0xff) | ((chip->reg_ac & 0x07) << 8); + chip->block_3ch[channel] = (chip->reg_ac >> 3) & 0x07; + chip->kcode_3ch[channel] = (chip->block_3ch[channel] << 2) | fn_note[chip->fnum_3ch[channel] >> 7]; + break; + case 0xac: + chip->reg_ac = chip->data & 0xff; + break; + case 0xb0: + chip->connect[channel] = chip->data & 0x07; + chip->fb[channel] = (chip->data >> 3) & 0x07; + break; + case 0xb4: + chip->pms[channel] = chip->data & 0x07; + chip->ams[channel] = (chip->data >> 4) & 0x03; + chip->pan_l[channel] = (chip->data >> 7) & 0x01; + chip->pan_r[channel] = (chip->data >> 6) & 0x01; + break; + default: + break; + } + } + } + + if (chip->write_a_en || chip->write_d_en) + { + /* Data */ + if (chip->write_a_en) + { + chip->write_fm_data = 0; + } + + if (chip->write_fm_address && chip->write_d_en) + { + chip->write_fm_data = 1; + } + + /* Address */ + if (chip->write_a_en) + { + if ((chip->write_data & 0xf0) != 0x00) + { + /* FM Write */ + chip->address = chip->write_data; + chip->write_fm_address = 1; + } + else + { + /* SSG write */ + chip->write_fm_address = 0; + } + } + + /* FM Mode */ + /* Data */ + if (chip->write_d_en && (chip->write_data & 0x100) == 0) + { + switch (chip->write_fm_mode_a) + { + case 0x21: /* LSI test 1 */ + for (i = 0; i < 8; i++) + { + chip->mode_test_21[i] = (chip->write_data >> i) & 0x01; + } + break; + case 0x22: /* LFO control */ + if ((chip->write_data >> 3) & 0x01) + { + chip->lfo_en = 0x7f; + } + else + { + chip->lfo_en = 0; + } + chip->lfo_freq = chip->write_data & 0x07; + break; + case 0x24: /* Timer A */ + chip->timer_a_reg &= 0x03; + chip->timer_a_reg |= (chip->write_data & 0xff) << 2; + break; + case 0x25: + chip->timer_a_reg &= 0x3fc; + chip->timer_a_reg |= chip->write_data & 0x03; + break; + case 0x26: /* Timer B */ + chip->timer_b_reg = chip->write_data & 0xff; + break; + case 0x27: /* CSM, Timer control */ + chip->mode_ch3 = (chip->write_data & 0xc0) >> 6; + chip->mode_csm = chip->mode_ch3 == 2; + chip->timer_a_load = chip->write_data & 0x01; + chip->timer_a_enable = (chip->write_data >> 2) & 0x01; + chip->timer_a_reset = (chip->write_data >> 4) & 0x01; + chip->timer_b_load = (chip->write_data >> 1) & 0x01; + chip->timer_b_enable = (chip->write_data >> 3) & 0x01; + chip->timer_b_reset = (chip->write_data >> 5) & 0x01; + break; + case 0x28: /* Key on/off */ + for (i = 0; i < 4; i++) + { + chip->mode_kon_operator[i] = (chip->write_data >> (4 + i)) & 0x01; + } + if ((chip->write_data & 0x03) == 0x03) + { + /* Invalid address */ + chip->mode_kon_channel = 0xff; + } + else + { + chip->mode_kon_channel = (chip->write_data & 0x03) + ((chip->write_data >> 2) & 1) * 3; + } + break; + case 0x2a: /* DAC data */ + chip->dacdata &= 0x01; + chip->dacdata |= (chip->write_data ^ 0x80) << 1; + break; + case 0x2b: /* DAC enable */ + chip->dacen = chip->write_data >> 7; + break; + case 0x2c: /* LSI test 2 */ + for (i = 0; i < 8; i++) + { + chip->mode_test_2c[i] = (chip->write_data >> i) & 0x01; + } + chip->dacdata &= 0x1fe; + chip->dacdata |= chip->mode_test_2c[3]; + chip->eg_custom_timer = !chip->mode_test_2c[7] && chip->mode_test_2c[6]; + break; + default: + break; + } + } + + /* Address */ + if (chip->write_a_en) + { + chip->write_fm_mode_a = chip->write_data & 0x1ff; + } + } + + if (chip->write_fm_data) + { + chip->data = chip->write_data & 0xff; + } +} + +void OPN2_PhaseCalcIncrement(ym3438_t *chip) +{ + Bit32u chan = chip->channel; + Bit32u slot = chip->cycles; + Bit32u fnum = chip->pg_fnum; + Bit32u fnum_h = fnum >> 4; + Bit32u fm; + Bit32u basefreq; + Bit8u lfo = chip->lfo_pm; + Bit8u lfo_l = lfo & 0x0f; + Bit8u pms = chip->pms[chan]; + Bit8u dt = chip->dt[slot]; + Bit8u dt_l = dt & 0x03; + Bit8u detune = 0; + Bit8u block, note; + Bit8u sum, sum_h, sum_l; + Bit8u kcode = chip->pg_kcode; + + fnum <<= 1; + /* Apply LFO */ + if (lfo_l & 0x08) + { + lfo_l ^= 0x0f; + } + fm = (fnum_h >> pg_lfo_sh1[pms][lfo_l]) + (fnum_h >> pg_lfo_sh2[pms][lfo_l]); + if (pms > 5) + { + fm <<= pms - 5; + } + fm >>= 2; + if (lfo & 0x10) + { + fnum -= fm; + } + else + { + fnum += fm; + } + fnum &= 0xfff; + + basefreq = (fnum << chip->pg_block) >> 2; + + /* Apply detune */ + if (dt_l) + { + if (kcode > 0x1c) + { + kcode = 0x1c; + } + block = kcode >> 2; + note = kcode & 0x03; + sum = block + 9 + ((dt_l == 3) | (dt_l & 0x02)); + sum_h = sum >> 1; + sum_l = sum & 0x01; + detune = pg_detune[(sum_l << 2) | note] >> (9 - sum_h); + } + if (dt & 0x04) + { + basefreq -= detune; + } + else + { + basefreq += detune; + } + basefreq &= 0x1ffff; + chip->pg_inc[slot] = (basefreq * chip->multi[slot]) >> 1; + chip->pg_inc[slot] &= 0xfffff; +} + +void OPN2_PhaseGenerate(ym3438_t *chip) +{ + Bit32u slot; + /* Mask increment */ + slot = (chip->cycles + 20) % 24; + if (chip->pg_reset[slot]) + { + chip->pg_inc[slot] = 0; + } + /* Phase step */ + slot = (chip->cycles + 19) % 24; + chip->pg_phase[slot] += chip->pg_inc[slot]; + chip->pg_phase[slot] &= 0xfffff; + if (chip->pg_reset[slot] || chip->mode_test_21[3]) + { + chip->pg_phase[slot] = 0; + } +} + +void OPN2_EnvelopeSSGEG(ym3438_t *chip) +{ + Bit32u slot = chip->cycles; + Bit8u direction = 0; + chip->eg_ssg_pgrst_latch[slot] = 0; + chip->eg_ssg_repeat_latch[slot] = 0; + chip->eg_ssg_hold_up_latch[slot] = 0; + chip->eg_ssg_inv[slot] = 0; + if (chip->ssg_eg[slot] & 0x08) + { + direction = chip->eg_ssg_dir[slot]; + if (chip->eg_level[slot] & 0x200) + { + /* Reset */ + if ((chip->ssg_eg[slot] & 0x03) == 0x00) + { + chip->eg_ssg_pgrst_latch[slot] = 1; + } + /* Repeat */ + if ((chip->ssg_eg[slot] & 0x01) == 0x00) + { + chip->eg_ssg_repeat_latch[slot] = 1; + } + /* Inverse */ + if ((chip->ssg_eg[slot] & 0x03) == 0x02) + { + direction ^= 1; + } + if ((chip->ssg_eg[slot] & 0x03) == 0x03) + { + direction = 1; + } + } + /* Hold up */ + if (chip->eg_kon_latch[slot] + && ((chip->ssg_eg[slot] & 0x07) == 0x05 || (chip->ssg_eg[slot] & 0x07) == 0x03)) + { + chip->eg_ssg_hold_up_latch[slot] = 1; + } + direction &= chip->eg_kon[slot]; + chip->eg_ssg_inv[slot] = (chip->eg_ssg_dir[slot] ^ ((chip->ssg_eg[slot] >> 2) & 0x01)) + & chip->eg_kon[slot]; + } + chip->eg_ssg_dir[slot] = direction; + chip->eg_ssg_enable[slot] = (chip->ssg_eg[slot] >> 3) & 0x01; +} + +void OPN2_EnvelopeADSR(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 22) % 24; + + Bit8u nkon = chip->eg_kon_latch[slot]; + Bit8u okon = chip->eg_kon[slot]; + Bit8u kon_event; + Bit8u koff_event; + Bit8u eg_off; + Bit16s level; + Bit16s nextlevel = 0; + Bit16s ssg_level; + Bit8u nextstate = chip->eg_state[slot]; + Bit16s inc = 0; + chip->eg_read[0] = chip->eg_read_inc; + chip->eg_read_inc = chip->eg_inc > 0; + + /* Reset phase generator */ + chip->pg_reset[slot] = (nkon && !okon) || chip->eg_ssg_pgrst_latch[slot]; + + /* KeyOn/Off */ + kon_event = (nkon && !okon) || (okon && chip->eg_ssg_repeat_latch[slot]); + koff_event = okon && !nkon; + + ssg_level = level = (Bit16s)chip->eg_level[slot]; + + if (chip->eg_ssg_inv[slot]) + { + /* Inverse */ + ssg_level = 512 - level; + ssg_level &= 0x3ff; + } + if (koff_event) + { + level = ssg_level; + } + if (chip->eg_ssg_enable[slot]) + { + eg_off = level >> 9; + } + else + { + eg_off = (level & 0x3f0) == 0x3f0; + } + nextlevel = level; + if (kon_event) + { + nextstate = eg_num_attack; + /* Instant attack */ + if (chip->eg_ratemax) + { + nextlevel = 0; + } + else if (chip->eg_state[slot] == eg_num_attack && level != 0 && chip->eg_inc && nkon) + { + inc = (~level << chip->eg_inc) >> 5; + } + } + else + { + switch (chip->eg_state[slot]) + { + case eg_num_attack: + if (level == 0) + { + nextstate = eg_num_decay; + } + else if(chip->eg_inc && !chip->eg_ratemax && nkon) + { + inc = (~level << chip->eg_inc) >> 5; + } + break; + case eg_num_decay: + if ((level >> 5) == chip->eg_sl[1]) + { + nextstate = eg_num_sustain; + } + else if (!eg_off && chip->eg_inc) + { + inc = 1 << (chip->eg_inc - 1); + if (chip->eg_ssg_enable[slot]) + { + inc <<= 2; + } + } + break; + case eg_num_sustain: + case eg_num_release: + if (!eg_off && chip->eg_inc) + { + inc = 1 << (chip->eg_inc - 1); + if (chip->eg_ssg_enable[slot]) + { + inc <<= 2; + } + } + break; + default: + break; + } + if (!nkon) + { + nextstate = eg_num_release; + } + } + if (chip->eg_kon_csm[slot]) + { + nextlevel |= chip->eg_tl[1] << 3; + } + + /* Envelope off */ + if (!kon_event && !chip->eg_ssg_hold_up_latch[slot] && chip->eg_state[slot] != eg_num_attack && eg_off) + { + nextstate = eg_num_release; + nextlevel = 0x3ff; + } + + nextlevel += inc; + + chip->eg_kon[slot] = chip->eg_kon_latch[slot]; + chip->eg_level[slot] = (Bit16u)nextlevel & 0x3ff; + chip->eg_state[slot] = nextstate; +} + +void OPN2_EnvelopePrepare(ym3438_t *chip) +{ + Bit8u rate; + Bit8u sum; + Bit8u inc = 0; + Bit32u slot = chip->cycles; + Bit8u rate_sel; + + /* Prepare increment */ + rate = (chip->eg_rate << 1) + chip->eg_ksv; + + if (rate > 0x3f) + { + rate = 0x3f; + } + + sum = ((rate >> 2) + chip->eg_shift_lock) & 0x0f; + if (chip->eg_rate != 0 && chip->eg_quotient == 2) + { + if (rate < 48) + { + switch (sum) + { + case 12: + inc = 1; + break; + case 13: + inc = (rate >> 1) & 0x01; + break; + case 14: + inc = rate & 0x01; + break; + default: + break; + } + } + else + { + inc = eg_stephi[rate & 0x03][chip->eg_timer_low_lock] + (rate >> 2) - 11; + if (inc > 4) + { + inc = 4; + } + } + } + chip->eg_inc = inc; + chip->eg_ratemax = (rate >> 1) == 0x1f; + + /* Prepare rate & ksv */ + rate_sel = chip->eg_state[slot]; + if ((chip->eg_kon[slot] && chip->eg_ssg_repeat_latch[slot]) + || (!chip->eg_kon[slot] && chip->eg_kon_latch[slot])) + { + rate_sel = eg_num_attack; + } + switch (rate_sel) + { + case eg_num_attack: + chip->eg_rate = chip->ar[slot]; + break; + case eg_num_decay: + chip->eg_rate = chip->dr[slot]; + break; + case eg_num_sustain: + chip->eg_rate = chip->sr[slot]; + break; + case eg_num_release: + chip->eg_rate = (chip->rr[slot] << 1) | 0x01; + break; + default: + break; + } + chip->eg_ksv = chip->pg_kcode >> (chip->ks[slot] ^ 0x03); + if (chip->am[slot]) + { + chip->eg_lfo_am = chip->lfo_am >> eg_am_shift[chip->ams[chip->channel]]; + } + else + { + chip->eg_lfo_am = 0; + } + /* Delay TL & SL value */ + chip->eg_tl[1] = chip->eg_tl[0]; + chip->eg_tl[0] = chip->tl[slot]; + chip->eg_sl[1] = chip->eg_sl[0]; + chip->eg_sl[0] = chip->sl[slot]; +} + +void OPN2_EnvelopeGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 23) % 24; + Bit16u level; + + level = chip->eg_level[slot]; + + if (chip->eg_ssg_inv[slot]) + { + /* Inverse */ + level = 512 - level; + } + if (chip->mode_test_21[5]) + { + level = 0; + } + level &= 0x3ff; + + /* Apply AM LFO */ + level += chip->eg_lfo_am; + + /* Apply TL */ + if (!(chip->mode_csm && chip->channel == 2 + 1)) + { + level += chip->eg_tl[0] << 3; + } + if (level > 0x3ff) + { + level = 0x3ff; + } + chip->eg_out[slot] = level; +} + +void OPN2_UpdateLFO(ym3438_t *chip) +{ + if ((chip->lfo_quotient & lfo_cycles[chip->lfo_freq]) == lfo_cycles[chip->lfo_freq]) + { + chip->lfo_quotient = 0; + chip->lfo_cnt++; + } + else + { + chip->lfo_quotient += chip->lfo_inc; + } + chip->lfo_cnt &= chip->lfo_en; +} + +void OPN2_FMPrepare(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 6) % 24; + Bit32u channel = chip->channel; + Bit16s mod, mod1, mod2; + Bit32u op = slot / 6; + Bit8u connect = chip->connect[channel]; + Bit32u prevslot = (chip->cycles + 18) % 24; + + /* Calculate modulation */ + mod1 = mod2 = 0; + + if (fm_algorithm[op][0][connect]) + { + mod2 |= chip->fm_op1[channel][0]; + } + if (fm_algorithm[op][1][connect]) + { + mod1 |= chip->fm_op1[channel][1]; + } + if (fm_algorithm[op][2][connect]) + { + mod1 |= chip->fm_op2[channel]; + } + if (fm_algorithm[op][3][connect]) + { + mod2 |= chip->fm_out[prevslot]; + } + if (fm_algorithm[op][4][connect]) + { + mod1 |= chip->fm_out[prevslot]; + } + mod = mod1 + mod2; + if (op == 0) + { + /* Feedback */ + mod = mod >> (10 - chip->fb[channel]); + if (!chip->fb[channel]) + { + mod = 0; + } + } + else + { + mod >>= 1; + } + chip->fm_mod[slot] = mod; + + slot = (chip->cycles + 18) % 24; + /* OP1 */ + if (slot / 6 == 0) + { + chip->fm_op1[channel][1] = chip->fm_op1[channel][0]; + chip->fm_op1[channel][0] = chip->fm_out[slot]; + } + /* OP2 */ + if (slot / 6 == 2) + { + chip->fm_op2[channel] = chip->fm_out[slot]; + } +} + +void OPN2_ChGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 18) % 24; + Bit32u channel = chip->channel; + Bit32u op = slot / 6; + Bit32u test_dac = chip->mode_test_2c[5]; + Bit16s acc = chip->ch_acc[channel]; + Bit16s add = test_dac; + Bit16s sum = 0; + if (op == 0 && !test_dac) + { + acc = 0; + } + if (fm_algorithm[op][5][chip->connect[channel]] && !test_dac) + { + add += chip->fm_out[slot] >> 5; + } + sum = acc + add; + /* Clamp */ + if (sum > 255) + { + sum = 255; + } + else if(sum < -256) + { + sum = -256; + } + + if (op == 0 || test_dac) + { + chip->ch_out[channel] = chip->ch_acc[channel]; + } + chip->ch_acc[channel] = sum; +} + +void OPN2_ChOutput(ym3438_t *chip) +{ + Bit32u cycles = chip->cycles; + Bit32u slot = chip->cycles; + Bit32u channel = chip->channel; + Bit32u test_dac = chip->mode_test_2c[5]; + Bit16s out; + Bit16s sign; + Bit32u out_en; + chip->ch_read = chip->ch_lock; + if (slot < 12) + { + /* Ch 4,5,6 */ + channel++; + } + if ((cycles & 3) == 0) + { + if (!test_dac) + { + /* Lock value */ + chip->ch_lock = chip->ch_out[channel]; + } + chip->ch_lock_l = chip->pan_l[channel]; + chip->ch_lock_r = chip->pan_r[channel]; + } + /* Ch 6 */ + if (((cycles >> 2) == 1 && chip->dacen) || test_dac) + { + out = (Bit16s)chip->dacdata; + out <<= 7; + out >>= 7; + } + else + { + out = chip->ch_lock; + } + chip->mol = 0; + chip->mor = 0; + + if (chip_type & ym3438_mode_ym2612) + { + out_en = ((cycles & 3) == 3) || test_dac; + /* YM2612 DAC emulation(not verified) */ + sign = out >> 8; + if (out >= 0) + { + out++; + sign++; + } + if (chip->ch_lock_l && out_en) + { + chip->mol = out; + } + else + { + chip->mol = sign; + } + if (chip->ch_lock_r && out_en) + { + chip->mor = out; + } + else + { + chip->mor = sign; + } + /* Amplify signal */ + chip->mol *= 3; + chip->mor *= 3; + } + else + { + out_en = ((cycles & 3) != 0) || test_dac; + if (chip->ch_lock_l && out_en) + { + chip->mol = out; + } + if (chip->ch_lock_r && out_en) + { + chip->mor = out; + } + } +} + +void OPN2_FMGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 19) % 24; + /* Calculate phase */ + Bit16u phase = (chip->fm_mod[slot] + (chip->pg_phase[slot] >> 10)) & 0x3ff; + Bit16u quarter; + Bit16u level; + Bit16s output; + if (phase & 0x100) + { + quarter = (phase ^ 0xff) & 0xff; + } + else + { + quarter = phase & 0xff; + } + level = logsinrom[quarter]; + /* Apply envelope */ + level += chip->eg_out[slot] << 2; + /* Transform */ + if (level > 0x1fff) + { + level = 0x1fff; + } + output = ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 2) >> (level >> 8); + if (phase & 0x200) + { + output = ((~output) ^ (chip->mode_test_21[4] << 13)) + 1; + } + else + { + output = output ^ (chip->mode_test_21[4] << 13); + } + output <<= 2; + output >>= 2; + chip->fm_out[slot] = output; +} + +void OPN2_DoTimerA(ym3438_t *chip) +{ + Bit16u time; + Bit8u load; + load = chip->timer_a_overflow; + if (chip->cycles == 2) + { + /* Lock load value */ + load |= (!chip->timer_a_load_lock && chip->timer_a_load); + chip->timer_a_load_lock = chip->timer_a_load; + if (chip->mode_csm) + { + /* CSM KeyOn */ + chip->mode_kon_csm = load; + } + else + { + chip->mode_kon_csm = 0; + } + } + /* Load counter */ + if (chip->timer_a_load_latch) + { + time = chip->timer_a_reg; + } + else + { + time = chip->timer_a_cnt; + } + chip->timer_a_load_latch = load; + /* Increase counter */ + if ((chip->cycles == 1 && chip->timer_a_load_lock) || chip->mode_test_21[2]) + { + time++; + } + /* Set overflow flag */ + if (chip->timer_a_reset) + { + chip->timer_a_reset = 0; + chip->timer_a_overflow_flag = 0; + } + else + { + chip->timer_a_overflow_flag |= chip->timer_a_overflow & chip->timer_a_enable; + } + chip->timer_a_overflow = (time >> 10); + chip->timer_a_cnt = time & 0x3ff; +} + +void OPN2_DoTimerB(ym3438_t *chip) +{ + Bit16u time; + Bit8u load; + load = chip->timer_b_overflow; + if (chip->cycles == 2) + { + /* Lock load value */ + load |= (!chip->timer_b_load_lock && chip->timer_b_load); + chip->timer_b_load_lock = chip->timer_b_load; + } + /* Load counter */ + if (chip->timer_b_load_latch) + { + time = chip->timer_b_reg; + } + else + { + time = chip->timer_b_cnt; + } + chip->timer_b_load_latch = load; + /* Increase counter */ + if (chip->cycles == 1) + { + chip->timer_b_subcnt++; + } + if ((chip->timer_b_subcnt == 0x10 && chip->timer_b_load_lock) || chip->mode_test_21[2]) + { + time++; + } + chip->timer_b_subcnt &= 0x0f; + /* Set overflow flag */ + if (chip->timer_b_reset) + { + chip->timer_b_reset = 0; + chip->timer_b_overflow_flag = 0; + } + else + { + chip->timer_b_overflow_flag |= chip->timer_b_overflow & chip->timer_b_enable; + } + chip->timer_b_overflow = (time >> 8); + chip->timer_b_cnt = time & 0xff; +} + +void OPN2_KeyOn(ym3438_t*chip) +{ + Bit32u slot = chip->cycles; + Bit32u chan = chip->channel; + /* Key On */ + chip->eg_kon_latch[slot] = chip->mode_kon[slot]; + chip->eg_kon_csm[slot] = 0; + if (chip->channel == 2 && chip->mode_kon_csm) + { + /* CSM Key On */ + chip->eg_kon_latch[slot] = 1; + chip->eg_kon_csm[slot] = 1; + } + if (chip->cycles == chip->mode_kon_channel) + { + /* OP1 */ + chip->mode_kon[chan] = chip->mode_kon_operator[0]; + /* OP2 */ + chip->mode_kon[chan + 12] = chip->mode_kon_operator[1]; + /* OP3 */ + chip->mode_kon[chan + 6] = chip->mode_kon_operator[2]; + /* OP4 */ + chip->mode_kon[chan + 18] = chip->mode_kon_operator[3]; + } +} + +void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock) +{ + Bit32u i, rateratio; + rateratio = (Bit32u)chip->rateratio; + memset(chip, 0, sizeof(ym3438_t)); + for (i = 0; i < 24; i++) + { + chip->eg_out[i] = 0x3ff; + chip->eg_level[i] = 0x3ff; + chip->eg_state[i] = eg_num_release; + chip->multi[i] = 1; + } + for (i = 0; i < 6; i++) + { + chip->pan_l[i] = 1; + chip->pan_r[i] = 1; + chip->pan_volume_l[i] = 46340; + chip->pan_volume_r[i] = 46340; + } + + if (rate != 0) + { + chip->rateratio = (Bit32s)(Bit32u)((((Bit64u)144 * rate) << RSM_FRAC) / clock); + } + else + { + chip->rateratio = (Bit32s)rateratio; + } +} + +void OPN2_SetChipType(Bit32u type) +{ + chip_type = type; +} + +void OPN2_Clock(ym3438_t *chip, Bit16s *buffer) +{ + Bit32u slot = chip->cycles; + chip->lfo_inc = chip->mode_test_21[1]; + chip->pg_read >>= 1; + chip->eg_read[1] >>= 1; + chip->eg_cycle++; + /* Lock envelope generator timer value */ + if (chip->cycles == 1 && chip->eg_quotient == 2) + { + if (chip->eg_cycle_stop) + { + chip->eg_shift_lock = 0; + } + else + { + chip->eg_shift_lock = chip->eg_shift + 1; + } + chip->eg_timer_low_lock = chip->eg_timer & 0x03; + } + /* Cycle specific functions */ + switch (chip->cycles) + { + case 0: + chip->lfo_pm = chip->lfo_cnt >> 2; + if (chip->lfo_cnt & 0x40) + { + chip->lfo_am = chip->lfo_cnt & 0x3f; + } + else + { + chip->lfo_am = chip->lfo_cnt ^ 0x3f; + } + chip->lfo_am <<= 1; + break; + case 1: + chip->eg_quotient++; + chip->eg_quotient %= 3; + chip->eg_cycle = 0; + chip->eg_cycle_stop = 1; + chip->eg_shift = 0; + chip->eg_timer_inc |= chip->eg_quotient >> 1; + chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; + chip->eg_timer_inc = chip->eg_timer >> 12; + chip->eg_timer &= 0xfff; + break; + case 2: + chip->pg_read = chip->pg_phase[21] & 0x3ff; + chip->eg_read[1] = chip->eg_out[0]; + break; + case 13: + chip->eg_cycle = 0; + chip->eg_cycle_stop = 1; + chip->eg_shift = 0; + chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; + chip->eg_timer_inc = chip->eg_timer >> 12; + chip->eg_timer &= 0xfff; + break; + case 23: + chip->lfo_inc |= 1; + break; + } + chip->eg_timer &= ~(chip->mode_test_21[5] << chip->eg_cycle); + if (((chip->eg_timer >> chip->eg_cycle) | (chip->pin_test_in & chip->eg_custom_timer)) & chip->eg_cycle_stop) + { + chip->eg_shift = chip->eg_cycle; + chip->eg_cycle_stop = 0; + } + + OPN2_DoIO(chip); + + OPN2_DoTimerA(chip); + OPN2_DoTimerB(chip); + OPN2_KeyOn(chip); + + OPN2_ChOutput(chip); + OPN2_ChGenerate(chip); + + OPN2_FMPrepare(chip); + OPN2_FMGenerate(chip); + + OPN2_PhaseGenerate(chip); + OPN2_PhaseCalcIncrement(chip); + + OPN2_EnvelopeADSR(chip); + OPN2_EnvelopeGenerate(chip); + OPN2_EnvelopeSSGEG(chip); + OPN2_EnvelopePrepare(chip); + + /* Prepare fnum & block */ + if (chip->mode_ch3) + { + /* Channel 3 special mode */ + switch (slot) + { + case 1: /* OP1 */ + chip->pg_fnum = chip->fnum_3ch[1]; + chip->pg_block = chip->block_3ch[1]; + chip->pg_kcode = chip->kcode_3ch[1]; + break; + case 7: /* OP3 */ + chip->pg_fnum = chip->fnum_3ch[0]; + chip->pg_block = chip->block_3ch[0]; + chip->pg_kcode = chip->kcode_3ch[0]; + break; + case 13: /* OP2 */ + chip->pg_fnum = chip->fnum_3ch[2]; + chip->pg_block = chip->block_3ch[2]; + chip->pg_kcode = chip->kcode_3ch[2]; + break; + case 19: /* OP4 */ + default: + chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; + chip->pg_block = chip->block[(chip->channel + 1) % 6]; + chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; + break; + } + } + else + { + chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; + chip->pg_block = chip->block[(chip->channel + 1) % 6]; + chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; + } + + OPN2_UpdateLFO(chip); + OPN2_DoRegWrite(chip); + chip->cycles = (chip->cycles + 1) % 24; + chip->channel = chip->cycles % 6; + + buffer[0] = chip->mol; + buffer[1] = chip->mor; + + if (chip->status_time) + chip->status_time--; +} + +void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data) +{ + port &= 3; + chip->write_data = ((port << 7) & 0x100) | data; + if (port & 1) + { + /* Data */ + chip->write_d |= 1; + } + else + { + /* Address */ + chip->write_a |= 1; + } +} + +void OPN2_SetTestPin(ym3438_t *chip, Bit32u value) +{ + chip->pin_test_in = value & 1; +} + +Bit32u OPN2_ReadTestPin(ym3438_t *chip) +{ + if (!chip->mode_test_2c[7]) + { + return 0; + } + return chip->cycles == 23; +} + +Bit32u OPN2_ReadIRQPin(ym3438_t *chip) +{ + return chip->timer_a_overflow_flag | chip->timer_b_overflow_flag; +} + +Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) +{ + if ((port & 3) == 0 || (chip_type & ym3438_mode_readmode)) + { + if (chip->mode_test_21[6]) + { + /* Read test data */ + Bit32u slot = (chip->cycles + 18) % 24; + Bit16u testdata = ((chip->pg_read & 0x01) << 15) + | ((chip->eg_read[chip->mode_test_21[0]] & 0x01) << 14); + if (chip->mode_test_2c[4]) + { + testdata |= chip->ch_read & 0x1ff; + } + else + { + testdata |= chip->fm_out[slot] & 0x3fff; + } + if (chip->mode_test_21[7]) + { + chip->status = testdata & 0xff; + } + else + { + chip->status = testdata >> 8; + } + } + else + { + chip->status = (chip->busy << 7) | (chip->timer_b_overflow_flag << 1) + | chip->timer_a_overflow_flag; + } + if (chip_type & ym3438_mode_ym2612) + { + chip->status_time = 300000; + } + else + { + chip->status_time = 40000000; + } + } + if (chip->status_time) + { + return chip->status; + } + return 0; +} + +void OPN2_WritePan(ym3438_t *chip, Bit32u channel, Bit8u data) +{ + chip->pan_volume_l[channel] = panlawtable[data & 0x7F]; + chip->pan_volume_r[channel] = panlawtable[0x7F - (data & 0x7F)]; +} + +void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data) +{ + Bit64u time1, time2; + Bit16s buffer[2]; + Bit64u skip; + + if (chip->writebuf[chip->writebuf_last].port & 0x04) + { + OPN2_Write(chip, chip->writebuf[chip->writebuf_last].port & 0X03, + chip->writebuf[chip->writebuf_last].data); + + chip->writebuf_cur = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; + skip = chip->writebuf[chip->writebuf_last].time - chip->writebuf_samplecnt; + chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + while (skip--) + { + OPN2_Clock(chip, buffer); + } + } + + chip->writebuf[chip->writebuf_last].port = (port & 0x03) | 0x04; + chip->writebuf[chip->writebuf_last].data = data; + time1 = chip->writebuf_lasttime + OPN_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + chip->writebuf[chip->writebuf_last].time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; +} + +void OPN2_Generate(ym3438_t *chip, Bit16s *buf) +{ + Bit32u i; + Bit16s buffer[2]; + Bit32u mute; + Bit32s channel = -1; + + buf[0] = 0; + buf[1] = 0; + + for (i = 0; i < 24; i++) + { + switch (chip->cycles >> 2) + { + case 0: /* Ch 2 */ + mute = chip->mute[1]; + channel = 1; + break; + case 1: /* Ch 6, DAC */ + mute = chip->mute[5 + chip->dacen]; + channel = 5; + break; + case 2: /* Ch 4 */ + mute = chip->mute[3]; + channel = 3; + break; + case 3: /* Ch 1 */ + mute = chip->mute[0]; + channel = 0; + break; + case 4: /* Ch 5 */ + mute = chip->mute[4]; + channel = 4; + break; + case 5: /* Ch 3 */ + mute = chip->mute[2]; + channel = 2; + break; + default: + mute = 0; + break; + } + OPN2_Clock(chip, buffer); + if (!mute) + { + if (channel >= 0) + { + buffer[0] = buffer[0] * chip->pan_volume_l[channel] / 65535; + buffer[1] = buffer[1] * chip->pan_volume_r[channel] / 65535; + } + buf[0] += buffer[0]; + buf[1] += buffer[1]; + } + + while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + { + if (!(chip->writebuf[chip->writebuf_cur].port & 0x04)) + { + break; + } + chip->writebuf[chip->writebuf_cur].port &= 0x03; + OPN2_Write(chip, chip->writebuf[chip->writebuf_cur].port, + chip->writebuf[chip->writebuf_cur].data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPN_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; + } +} + +void OPN2_GenerateResampled(ym3438_t *chip, Bit16s *buf) +{ + Bit16s buffer[2]; + + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPN2_Generate(chip, buffer); + chip->samples[0] = buffer[0] * 11; + chip->samples[1] = buffer[1] * 11; + chip->samplecnt -= chip->rateratio; + } + buf[0] = (Bit16s)(((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio)>>1); + buf[1] = (Bit16s)(((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio)>>1); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPN2_GenerateStream(ym3438_t *chip, Bit16s *output, Bit32u numsamples) +{ + Bit32u i; + Bit16s buffer[2]; + + for (i = 0; i < numsamples; i++) + { + OPN2_GenerateResampled(chip, buffer); + *output++ = buffer[0]; + *output++ = buffer[1]; + } +} + +void OPN2_GenerateStreamMix(ym3438_t *chip, Bit16s *output, Bit32u numsamples) +{ + Bit32u i; + Bit16s buffer[2]; + + for (i = 0; i < numsamples; i++) + { + OPN2_GenerateResampled(chip, buffer); + *output++ += buffer[0]; + *output++ += buffer[1]; + } +} + +void OPN2_SetMute(ym3438_t *chip, Bit32u mute) +{ + Bit32u i; + for (i = 0; i < 7; i++) + { + chip->mute[i] = (mute >> i) & 0x01; + } +} diff --git a/thirdparty/opnmidi/chips/nuked/ym3438.h b/thirdparty/opnmidi/chips/nuked/ym3438.h new file mode 100644 index 000000000..26abfdfec --- /dev/null +++ b/thirdparty/opnmidi/chips/nuked/ym3438.h @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2017-2018 Alexey Khokholov (Nuke.YKT) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * + * Nuked OPN2(Yamaha YM3438) emulator. + * Thanks: + * Silicon Pr0n: + * Yamaha YM3438 decap and die shot(digshadow). + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.0.9 + */ + +#ifndef YM3438_H +#define YM3438_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*EXTRA*/ +#define RSM_FRAC 10 +#define OPN_WRITEBUF_SIZE 2048 +#define OPN_WRITEBUF_DELAY 15 + +enum { + ym3438_mode_ym2612 = 0x01, /* Enables YM2612 emulation (MD1, MD2 VA2) */ + ym3438_mode_readmode = 0x02 /* Enables status read on any port (TeraDrive, MD1 VA7, MD2, etc) */ +}; + +#include + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +/*EXTRA*/ +typedef struct _opn2_writebuf { + Bit64u time; + Bit8u port; + Bit8u data; + Bit8u reserved[6]; +} opn2_writebuf; + +typedef struct +{ + Bit32u cycles; + Bit32u channel; + Bit16s mol, mor; + /* IO */ + Bit16u write_data; + Bit8u write_a; + Bit8u write_d; + Bit8u write_a_en; + Bit8u write_d_en; + Bit8u write_busy; + Bit8u write_busy_cnt; + Bit8u write_fm_address; + Bit8u write_fm_data; + Bit16u write_fm_mode_a; + Bit16u address; + Bit8u data; + Bit8u pin_test_in; + Bit8u pin_irq; + Bit8u busy; + /* LFO */ + Bit8u lfo_en; + Bit8u lfo_freq; + Bit8u lfo_pm; + Bit8u lfo_am; + Bit8u lfo_cnt; + Bit8u lfo_inc; + Bit8u lfo_quotient; + /* Phase generator */ + Bit16u pg_fnum; + Bit8u pg_block; + Bit8u pg_kcode; + Bit32u pg_inc[24]; + Bit32u pg_phase[24]; + Bit8u pg_reset[24]; + Bit32u pg_read; + /* Envelope generator */ + Bit8u eg_cycle; + Bit8u eg_cycle_stop; + Bit8u eg_shift; + Bit8u eg_shift_lock; + Bit8u eg_timer_low_lock; + Bit16u eg_timer; + Bit8u eg_timer_inc; + Bit16u eg_quotient; + Bit8u eg_custom_timer; + Bit8u eg_rate; + Bit8u eg_ksv; + Bit8u eg_inc; + Bit8u eg_ratemax; + Bit8u eg_sl[2]; + Bit8u eg_lfo_am; + Bit8u eg_tl[2]; + Bit8u eg_state[24]; + Bit16u eg_level[24]; + Bit16u eg_out[24]; + Bit8u eg_kon[24]; + Bit8u eg_kon_csm[24]; + Bit8u eg_kon_latch[24]; + Bit8u eg_csm_mode[24]; + Bit8u eg_ssg_enable[24]; + Bit8u eg_ssg_pgrst_latch[24]; + Bit8u eg_ssg_repeat_latch[24]; + Bit8u eg_ssg_hold_up_latch[24]; + Bit8u eg_ssg_dir[24]; + Bit8u eg_ssg_inv[24]; + Bit32u eg_read[2]; + Bit8u eg_read_inc; + /* FM */ + Bit16s fm_op1[6][2]; + Bit16s fm_op2[6]; + Bit16s fm_out[24]; + Bit16u fm_mod[24]; + /* Channel */ + Bit16s ch_acc[6]; + Bit16s ch_out[6]; + Bit16s ch_lock; + Bit8u ch_lock_l; + Bit8u ch_lock_r; + Bit16s ch_read; + /* Timer */ + Bit16u timer_a_cnt; + Bit16u timer_a_reg; + Bit8u timer_a_load_lock; + Bit8u timer_a_load; + Bit8u timer_a_enable; + Bit8u timer_a_reset; + Bit8u timer_a_load_latch; + Bit8u timer_a_overflow_flag; + Bit8u timer_a_overflow; + + Bit16u timer_b_cnt; + Bit8u timer_b_subcnt; + Bit16u timer_b_reg; + Bit8u timer_b_load_lock; + Bit8u timer_b_load; + Bit8u timer_b_enable; + Bit8u timer_b_reset; + Bit8u timer_b_load_latch; + Bit8u timer_b_overflow_flag; + Bit8u timer_b_overflow; + + /* Register set */ + Bit8u mode_test_21[8]; + Bit8u mode_test_2c[8]; + Bit8u mode_ch3; + Bit8u mode_kon_channel; + Bit8u mode_kon_operator[4]; + Bit8u mode_kon[24]; + Bit8u mode_csm; + Bit8u mode_kon_csm; + Bit8u dacen; + Bit16s dacdata; + + Bit8u ks[24]; + Bit8u ar[24]; + Bit8u sr[24]; + Bit8u dt[24]; + Bit8u multi[24]; + Bit8u sl[24]; + Bit8u rr[24]; + Bit8u dr[24]; + Bit8u am[24]; + Bit8u tl[24]; + Bit8u ssg_eg[24]; + + Bit16u fnum[6]; + Bit8u block[6]; + Bit8u kcode[6]; + Bit16u fnum_3ch[6]; + Bit8u block_3ch[6]; + Bit8u kcode_3ch[6]; + Bit8u reg_a4; + Bit8u reg_ac; + Bit8u connect[6]; + Bit8u fb[6]; + Bit8u pan_l[6], pan_r[6]; + Bit8u ams[6]; + Bit8u pms[6]; + Bit8u status; + Bit32u status_time; + + /*EXTRA*/ + Bit32u mute[7]; + Bit32s rateratio; + Bit32s samplecnt; + Bit32s oldsamples[2]; + Bit32s samples[2]; + + Bit32u pan_volume_l[6]; + Bit32u pan_volume_r[6]; + + Bit64u writebuf_samplecnt; + Bit32u writebuf_cur; + Bit32u writebuf_last; + Bit64u writebuf_lasttime; + opn2_writebuf writebuf[OPN_WRITEBUF_SIZE]; +} ym3438_t; + +/* EXTRA, original was "void OPN2_Reset(ym3438_t *chip)" */ +void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock); +void OPN2_SetChipType(Bit32u type); +void OPN2_Clock(ym3438_t *chip, Bit16s *buffer); +void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data); +void OPN2_SetTestPin(ym3438_t *chip, Bit32u value); +Bit32u OPN2_ReadTestPin(ym3438_t *chip); +Bit32u OPN2_ReadIRQPin(ym3438_t *chip); +Bit8u OPN2_Read(ym3438_t *chip, Bit32u port); + +/*EXTRA*/ +void OPN2_WritePan(ym3438_t *chip, Bit32u channel, Bit8u data); +void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data); +void OPN2_Generate(ym3438_t *chip, Bit16s *buf); +void OPN2_GenerateResampled(ym3438_t *chip, Bit16s *buf); +void OPN2_GenerateStream(ym3438_t *chip, Bit16s *output, Bit32u numsamples); +void OPN2_GenerateStreamMix(ym3438_t *chip, Bit16s *output, Bit32u numsamples); +void OPN2_SetMute(ym3438_t *chip, Bit32u mute); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/opnmidi/chips/nuked_opn2.cpp b/thirdparty/opnmidi/chips/nuked_opn2.cpp new file mode 100644 index 000000000..3dbbb0bb8 --- /dev/null +++ b/thirdparty/opnmidi/chips/nuked_opn2.cpp @@ -0,0 +1,76 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nuked_opn2.h" +#include "nuked/ym3438.h" +#include + +NukedOPN2::NukedOPN2(OPNFamily f, bool ym3438) + : OPNChipBaseT(f), m_isym3438(ym3438) +{ + OPN2_SetChipType(m_isym3438 ? ym3438_mode_readmode : ym3438_mode_ym2612); + chip = new ym3438_t; + NukedOPN2::setRate(m_rate, m_clock); +} + +NukedOPN2::~NukedOPN2() +{ + ym3438_t *chip_r = reinterpret_cast(chip); + delete chip_r; +} + +void NukedOPN2::setRate(uint32_t rate, uint32_t clock) +{ + OPNChipBaseT::setRate(rate, clock); + ym3438_t *chip_r = reinterpret_cast(chip); + OPN2_Reset(chip_r, rate, clock); +} + +void NukedOPN2::reset() +{ + OPNChipBaseT::reset(); + ym3438_t *chip_r = reinterpret_cast(chip); + OPN2_Reset(chip_r, m_rate, m_clock); +} + +void NukedOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + ym3438_t *chip_r = reinterpret_cast(chip); + OPN2_WriteBuffered(chip_r, 0 + (port) * 2, (uint8_t)addr); + OPN2_WriteBuffered(chip_r, 1 + (port) * 2, data); + //qDebug() << QString("%1: 0x%2 => 0x%3").arg(port).arg(addr, 2, 16, QChar('0')).arg(data, 2, 16, QChar('0')); +} + +void NukedOPN2::writePan(uint16_t chan, uint8_t data) +{ + ym3438_t *chip_r = reinterpret_cast(chip); + OPN2_WritePan(chip_r, (Bit32u)chan, data); +} + +void NukedOPN2::nativeGenerate(int16_t *frame) +{ + ym3438_t *chip_r = reinterpret_cast(chip); + OPN2_Generate(chip_r, frame); +} + +const char *NukedOPN2::emulatorName() +{ + return m_isym3438 ? "Nuked OPN2 (3438)" : "Nuked OPN2 (2612)"; +} diff --git a/thirdparty/opnmidi/chips/nuked_opn2.h b/thirdparty/opnmidi/chips/nuked_opn2.h new file mode 100644 index 000000000..08ca3e710 --- /dev/null +++ b/thirdparty/opnmidi/chips/nuked_opn2.h @@ -0,0 +1,47 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NUKED_OPN2_H +#define NUKED_OPN2_H + +#include "opn_chip_base.h" + +class NukedOPN2 final : public OPNChipBaseT +{ + void *chip; + bool m_isym3438; +public: + explicit NukedOPN2(OPNFamily f, bool ym3438); + ~NukedOPN2() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t chan, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; + // amplitude scale factors to use in resampling + enum { resamplerPreAmplify = 11, resamplerPostAttenuate = 2 }; +}; + +#endif // NUKED_OPN2_H diff --git a/thirdparty/opnmidi/chips/opn_chip_base.h b/thirdparty/opnmidi/chips/opn_chip_base.h new file mode 100644 index 000000000..fc3db2e08 --- /dev/null +++ b/thirdparty/opnmidi/chips/opn_chip_base.h @@ -0,0 +1,163 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ONP_CHIP_BASE_H +#define ONP_CHIP_BASE_H + +#include "opn_chip_family.h" +#include +#include + +#if !defined(_MSC_VER) && (__cplusplus <= 199711L) +#define final +#define override +#endif + +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) +class VResampler; +#endif + +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) +extern void opn2_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); +#endif + +class OPNChipBase +{ +protected: + uint32_t m_id; + uint32_t m_rate; + uint32_t m_clock; + OPNFamily m_family; +public: + explicit OPNChipBase(OPNFamily f); + virtual ~OPNChipBase(); + + virtual OPNFamily family() const = 0; + uint32_t clockRate() const; + virtual uint32_t nativeClockRate() const = 0; + + uint32_t chipId() const { return m_id; } + void setChipId(uint32_t id) { m_id = id; } + + virtual bool canRunAtPcmRate() const = 0; + virtual bool isRunningAtPcmRate() const = 0; + virtual bool setRunningAtPcmRate(bool r) = 0; +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) + virtual void setAudioTickHandlerInstance(void *instance) = 0; +#endif + + virtual void setRate(uint32_t rate, uint32_t clock) = 0; + virtual uint32_t effectiveRate() const = 0; + virtual uint32_t nativeRate() const = 0; + virtual void reset() = 0; + virtual void writeReg(uint32_t port, uint16_t addr, uint8_t data) = 0; + + // extended + virtual void writePan(uint16_t addr, uint8_t data) { (void)addr; (void)data; } + + virtual void nativePreGenerate() = 0; + virtual void nativePostGenerate() = 0; + virtual void nativeGenerate(int16_t *frame) = 0; + + virtual void generate(int16_t *output, size_t frames) = 0; + virtual void generateAndMix(int16_t *output, size_t frames) = 0; + virtual void generate32(int32_t *output, size_t frames) = 0; + virtual void generateAndMix32(int32_t *output, size_t frames) = 0; + + virtual const char* emulatorName() = 0; +private: + OPNChipBase(const OPNChipBase &c); + OPNChipBase &operator=(const OPNChipBase &c); +}; + +// A base class providing F-bounded generic and efficient implementations, +// supporting resampling of chip outputs +template +class OPNChipBaseT : public OPNChipBase +{ +public: + explicit OPNChipBaseT(OPNFamily f); + virtual ~OPNChipBaseT(); + + OPNFamily family() const override; + uint32_t nativeClockRate() const override; + + bool isRunningAtPcmRate() const override; + bool setRunningAtPcmRate(bool r) override; +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) + void setAudioTickHandlerInstance(void *instance); +#endif + + virtual void setRate(uint32_t rate, uint32_t clock) override; + uint32_t effectiveRate() const override; + uint32_t nativeRate() const override; + virtual void reset() override; + void generate(int16_t *output, size_t frames) override; + void generateAndMix(int16_t *output, size_t frames) override; + void generate32(int32_t *output, size_t frames) override; + void generateAndMix32(int32_t *output, size_t frames) override; +private: + bool m_runningAtPcmRate; +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) + void *m_audioTickHandlerInstance; +#endif + void nativeTick(int16_t *frame); + void setupResampler(uint32_t rate); + void resetResampler(); + void resampledGenerate(int32_t *output); +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) + VResampler *m_resampler; +#else + int32_t m_oldsamples[2]; + int32_t m_samples[2]; + int32_t m_samplecnt; + int32_t m_rateratio; + enum { rsm_frac = 10 }; +#endif + // amplitude scale factors in and out of resampler, varying for chips; + // values are OK to "redefine", the static polymorphism will accept it. + enum { resamplerPreAmplify = 1, resamplerPostAttenuate = 1 }; +}; + +// A base class which provides frame-by-frame interfaces on emulations which +// don't have a routine for it. It produces outputs in fixed size buffers. +// Fast register updates will suffer some latency because of buffering. +template +class OPNChipBaseBufferedT : public OPNChipBaseT +{ +public: + explicit OPNChipBaseBufferedT(OPNFamily f) + : OPNChipBaseT(f), m_bufferIndex(0) {} + virtual ~OPNChipBaseBufferedT() + {} + enum { buffer_size = Buffer }; +public: + void reset() override; + void nativeGenerate(int16_t *frame) override; +protected: + virtual void nativeGenerateN(int16_t *output, size_t frames) = 0; +private: + unsigned m_bufferIndex; + int16_t m_buffer[2 * Buffer]; +}; + +#include "opn_chip_base.tcc" + +#endif // ONP_CHIP_BASE_H diff --git a/thirdparty/opnmidi/chips/opn_chip_base.tcc b/thirdparty/opnmidi/chips/opn_chip_base.tcc new file mode 100644 index 000000000..27d720a10 --- /dev/null +++ b/thirdparty/opnmidi/chips/opn_chip_base.tcc @@ -0,0 +1,322 @@ +#include "opn_chip_base.h" +#include +#include + +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) +#include +#endif + +#if !defined(LIKELY) && defined(__GNUC__) +#define LIKELY(x) __builtin_expect((x), 1) +#elif !defined(LIKELY) +#define LIKELY(x) (x) +#endif + +#if !defined(UNLIKELY) && defined(__GNUC__) +#define UNLIKELY(x) __builtin_expect((x), 0) +#elif !defined(UNLIKELY) +#define UNLIKELY(x) (x) +#endif + +/* OPNChipBase */ +inline OPNChipBase::OPNChipBase(OPNFamily f) : + m_id(0), + m_rate(44100), + m_clock(7670454), + m_family(f) +{ +} + +inline OPNChipBase::~OPNChipBase() +{ +} + +inline uint32_t OPNChipBase::clockRate() const +{ + return m_clock; +} + +/* OPNChipBaseT */ + +template +OPNChipBaseT::OPNChipBaseT(OPNFamily f) + : OPNChipBase(f), + m_runningAtPcmRate(false) +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) + , + m_audioTickHandlerInstance(NULL) +#endif +{ +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) + m_resampler = new VResampler; +#endif + setupResampler(m_rate); +} + +template +OPNChipBaseT::~OPNChipBaseT() +{ +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) + delete m_resampler; +#endif +} + +template +OPNFamily OPNChipBaseT::family() const +{ + return m_family; +} + +template +uint32_t OPNChipBaseT::nativeClockRate() const +{ + return opn2_getNativeClockRate(m_family); +} + +template +bool OPNChipBaseT::isRunningAtPcmRate() const +{ + return m_runningAtPcmRate; +} + +template +bool OPNChipBaseT::setRunningAtPcmRate(bool r) +{ + if(r != m_runningAtPcmRate) + { + if(r && !static_cast(this)->canRunAtPcmRate()) + return false; + m_runningAtPcmRate = r; + static_cast(this)->setRate(m_rate, m_clock); + } + return true; +} + +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) +template +void OPNChipBaseT::setAudioTickHandlerInstance(void *instance) +{ + m_audioTickHandlerInstance = instance; +} +#endif + +template +void OPNChipBaseT::setRate(uint32_t rate, uint32_t clock) +{ + uint32_t oldRate = m_rate; + uint32_t oldClock = m_clock; + m_rate = rate; + m_clock = clock; + if(rate != oldRate || clock != oldClock) + setupResampler(rate); + else + resetResampler(); +} + +template +uint32_t OPNChipBaseT::effectiveRate() const +{ + return m_runningAtPcmRate ? m_rate : opn2_getNativeRate(m_family); +} + +template +uint32_t OPNChipBaseT::nativeRate() const +{ + return opn2_getNativeRate(m_family); +} + +template +void OPNChipBaseT::reset() +{ + resetResampler(); +} + +template +void OPNChipBaseT::generate(int16_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + for (unsigned c = 0; c < 2; ++c) { + int32_t temp = frame[c]; + temp = (temp > -32768) ? temp : -32768; + temp = (temp < 32767) ? temp : 32767; + output[c] = (int16_t)temp; + } + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPNChipBaseT::generateAndMix(int16_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + for (unsigned c = 0; c < 2; ++c) { + int32_t temp = (int32_t)output[c] + frame[c]; + temp = (temp > -32768) ? temp : -32768; + temp = (temp < 32767) ? temp : 32767; + output[c] = (int16_t)temp; + } + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPNChipBaseT::generate32(int32_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + static_cast(this)->resampledGenerate(output); + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPNChipBaseT::generateAndMix32(int32_t *output, size_t frames) +{ + static_cast(this)->nativePreGenerate(); + for(size_t i = 0; i < frames; ++i) + { + int32_t frame[2]; + static_cast(this)->resampledGenerate(frame); + output[0] += frame[0]; + output[1] += frame[1]; + output += 2; + } + static_cast(this)->nativePostGenerate(); +} + +template +void OPNChipBaseT::nativeTick(int16_t *frame) +{ +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) + opn2_audioTickHandler(m_audioTickHandlerInstance, m_id, effectiveRate()); +#endif + static_cast(this)->nativeGenerate(frame); +} + +template +void OPNChipBaseT::setupResampler(uint32_t rate) +{ +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) + double ratio = rate * (1.0 / opn2_getNativeRate(m_family)); + m_resampler->setup(ratio, 2, 48); +#else + m_oldsamples[0] = m_oldsamples[1] = 0; + m_samples[0] = m_samples[1] = 0; + m_samplecnt = 0; + m_rateratio = (int32_t)(uint32_t)((((uint64_t)144 * rate) << rsm_frac) / m_clock); +#endif +} + +template +void OPNChipBaseT::resetResampler() +{ +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) + m_resampler->reset(); +#else + m_oldsamples[0] = m_oldsamples[1] = 0; + m_samples[0] = m_samples[1] = 0; + m_samplecnt = 0; +#endif +} + +#if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) +template +void OPNChipBaseT::resampledGenerate(int32_t *output) +{ + if(UNLIKELY(m_runningAtPcmRate)) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + return; + } + + VResampler *rsm = m_resampler; + float scale = (float)T::resamplerPreAmplify / + (float)T::resamplerPostAttenuate; + float f_in[2]; + float f_out[2]; + rsm->inp_count = 0; + rsm->inp_data = f_in; + rsm->out_count = 1; + rsm->out_data = f_out; + while(rsm->process(), rsm->out_count != 0) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + f_in[0] = scale * (float)in[0]; + f_in[1] = scale * (float)in[1]; + rsm->inp_count = 1; + rsm->inp_data = f_in; + rsm->out_count = 1; + rsm->out_data = f_out; + } + output[0] = static_cast(lround(f_out[0])); + output[1] = static_cast(lround(f_out[1])); +} +#else +template +void OPNChipBaseT::resampledGenerate(int32_t *output) +{ + if(UNLIKELY(m_runningAtPcmRate)) + { + int16_t in[2]; + static_cast(this)->nativeTick(in); + output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; + return; + } + + int32_t samplecnt = m_samplecnt; + const int32_t rateratio = m_rateratio; + while(samplecnt >= rateratio) + { + m_oldsamples[0] = m_samples[0]; + m_oldsamples[1] = m_samples[1]; + int16_t buffer[2]; + static_cast(this)->nativeTick(buffer); + m_samples[0] = buffer[0] * T::resamplerPreAmplify; + m_samples[1] = buffer[1] * T::resamplerPreAmplify; + samplecnt -= rateratio; + } + output[0] = (int32_t)(((m_oldsamples[0] * (rateratio - samplecnt) + + m_samples[0] * samplecnt) / rateratio)/T::resamplerPostAttenuate); + output[1] = (int32_t)(((m_oldsamples[1] * (rateratio - samplecnt) + + m_samples[1] * samplecnt) / rateratio)/T::resamplerPostAttenuate); + m_samplecnt = samplecnt + (1 << rsm_frac); +} +#endif + +/* OPNChipBaseBufferedT */ + +template +void OPNChipBaseBufferedT::reset() +{ + OPNChipBaseT::reset(); + m_bufferIndex = 0; +} + +template +void OPNChipBaseBufferedT::nativeGenerate(int16_t *frame) +{ + unsigned bufferIndex = m_bufferIndex; + if(bufferIndex == 0) + static_cast(this)->nativeGenerateN(m_buffer, Buffer); + frame[0] = m_buffer[2 * bufferIndex]; + frame[1] = m_buffer[2 * bufferIndex + 1]; + bufferIndex = (bufferIndex + 1 < Buffer) ? (bufferIndex + 1) : 0; + m_bufferIndex = bufferIndex; +} diff --git a/thirdparty/opnmidi/chips/opn_chip_family.h b/thirdparty/opnmidi/chips/opn_chip_family.h new file mode 100644 index 000000000..43e26361e --- /dev/null +++ b/thirdparty/opnmidi/chips/opn_chip_family.h @@ -0,0 +1,84 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OPN_CHIP_FAMILY_H +#define OPN_CHIP_FAMILY_H + +#include + +#define OPN_FAMILY_EACH(F) \ + F(OPN2) F(OPNA) + +enum OPNFamily +{ + #define Each(x) OPNChip_##x, + OPN_FAMILY_EACH(Each) + #undef Each + OPNChip_Count +}; + +template +struct OPNFamilyTraits; + +template <> +struct OPNFamilyTraits +{ + enum + { + nativeRate = 53267, + nativeClockRate = 7670454 + }; +}; + +template <> +struct OPNFamilyTraits +{ + enum + { + nativeRate = 55466, + nativeClockRate = 7987200 + }; +}; + +inline uint32_t opn2_getNativeRate(OPNFamily f) +{ + switch(f) + { + default: + #define Each(x) case OPNChip_##x: \ + return OPNFamilyTraits::nativeRate; + OPN_FAMILY_EACH(Each) + #undef Each + } +} + +inline uint32_t opn2_getNativeClockRate(OPNFamily f) +{ + switch(f) + { + default: + #define Each(x) case OPNChip_##x: \ + return OPNFamilyTraits::nativeClockRate; + OPN_FAMILY_EACH(Each) + #undef Each + } +} + +#endif // OPN_CHIP_FAMILY_H diff --git a/thirdparty/opnmidi/chips/pmdwin/LICENSE b/thirdparty/opnmidi/chips/pmdwin/LICENSE new file mode 100644 index 000000000..a79c15b6a --- /dev/null +++ b/thirdparty/opnmidi/chips/pmdwin/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012/2013, Peter Barfuss +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. diff --git a/thirdparty/opnmidi/chips/pmdwin/op.h b/thirdparty/opnmidi/chips/pmdwin/op.h new file mode 100644 index 000000000..4f9111cf1 --- /dev/null +++ b/thirdparty/opnmidi/chips/pmdwin/op.h @@ -0,0 +1,144 @@ +#ifndef FM_OP_H +#define FM_OP_H + +#include +#define false 0 +#define true 1 +#define Max(a,b) ((a>b)?a:b) +#define Min(a,b) ((a max ? max : (v < min ? min : v); +} + +static inline int16_t Limit16(int a) +{ + if ((a+0x8000) & ~0xFFFF) return (a>>31) ^ 0x7FFF; + else return a; +} + +/* ------------------------------------------------------------------------- */ +struct _OPNA; +struct Channel4; + +/* Operator ---------------------------------------------------------------- */ +typedef struct _FMOperator +{ + struct Channel4 *master; + + int32_t out, out2; + + /* Phase Generator ----------------------------------------------------- */ + uint32_t dp; /* Octave (used to define note in conjunction with bn, below). */ + uint8_t detune; /* Detune */ + uint8_t multiple; /* Multiple */ + uint32_t pgcount; /* Phase generator sweep value. Only the top 9 bits are relevant/used. */ + uint32_t pgdcount; /* Phase generator increment-per-clock value. Hopefully self-explanatory. */ + uint32_t pgdcountl; /* Phase generator detune increment value. Used in the implementation of vibrato. */ + /* Equal to pgdcount >> 11. */ + + /* Envelope Generator -------------------------------------------------- */ + uint32_t bn; /* Block/Note */ + uint32_t egout; + int eglevel; /* EG ¤Î½ÃÃŽÃÃà */ + int eglvnext; /* ¼¡¤Î phase ¤Ë°Ü¤ëÃà */ + int32_t egstep; /* EG ¤Î¼¡¤ÎÊѰܤޤǤλþ´Ö */ + int32_t egstepd; /* egstep ¤Î»þ´Öº¹Ê¬ */ + uint8_t egtransa; /* EG ÊѲ½¤Î³ä¹ç (for attack) */ + uint8_t egtransd; /* EG ÊѲ½¤Î³ä¹ç (for decay) */ + + uint32_t ksr; /* key scale rate */ + EGPhase phase; + uint8_t ams; + uint8_t ms; + + uint8_t keyon; /* current key state */ + + uint8_t tl; /* Total Level (0-127) */ + uint8_t tll; /* Total Level Latch (for CSM mode) */ + uint8_t ar; /* Attack Rate (0-63) */ + uint8_t dr; /* Decay Rate (0-63) */ + uint8_t sr; /* Sustain Rate (0-63) */ + uint8_t sl; /* Sustain Level (0-127) */ + uint8_t rr; /* Release Rate (0-63) */ + uint8_t ks; /* Keyscale (0-3) */ + uint8_t ssgtype; /* SSG-Type Envelope Control */ + + uint8_t amon; /* enable Amplitude Modulation */ + uint8_t paramchanged; /* Set whenever f-number or any ADSR constants */ + /* are set in OPNASetReg(), as well as upon */ + /* chip reset and chip "DAC" samplerate change. */ + /* Causes the envelope generator to reset its */ + /* internal state, also sets correct increments */ + /* for the phase generator. */ + uint8_t mute; +} FMOperator; + +/* 4-op Channel ------------------------------------------------------------ */ +typedef struct Channel4 +{ + struct _OPNA *master; + uint32_t fb; + int buf[4]; + uint8_t idx[6]; + int *pms; + uint16_t panl; + uint16_t panr; + FMOperator op[4]; +} Channel4; + +/* OPNA Rhythm Generator --------------------------------------------------- */ +typedef struct Rhythm { + uint8_t pan; + int8_t level; + int8_t volume; + int8_t* sample; /* Rhythm sample data */ + uint32_t size; /* Rhythm sample data size */ + uint32_t pos; /* Current index into rhytm sample data array */ + uint32_t step; /* Amount to increment the above by every time */ + /* RhythmMix() gets called. */ + uint32_t rate; /* Samplerate of rhythm sample data */ + /* (44100Hz in this implementation). */ +} Rhythm; + +#ifdef __cplusplus +extern "C" { +#endif + +/* -------------------------------------------------------------------------- +// Miscellaneous and probably irrelevant function prototypes. */ +void OperatorInit(Channel4 *ch4, FMOperator *op); +void OperatorReset(FMOperator *op); +void OperatorPrepare(FMOperator *op); + +static inline uint32_t IsOn(FMOperator *op) { + return (op->phase - off); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/opnmidi/chips/pmdwin/opna.c b/thirdparty/opnmidi/chips/pmdwin/opna.c new file mode 100644 index 000000000..78b88a528 --- /dev/null +++ b/thirdparty/opnmidi/chips/pmdwin/opna.c @@ -0,0 +1,1369 @@ +/* FIXME: move ugly-ass legalese somewhere where it won't be seen +// by anyone other than lawyers. (/dev/null would be ideal but sadly +// we live in an imperfect world). */ +/* Copyright (c) 2012/2013, Peter Barfuss +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. */ + +#include +#include +#include +#include +#include "op.h" +#include "psg.h" +#include "opna.h" + +static const uint8_t notetab[128] = +{ + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 17, 18, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23, + 24, 24, 24, 24, 24, 24, 24, 25, 26, 27, 27, 27, 27, 27, 27, 27, + 28, 28, 28, 28, 28, 28, 28, 29, 30, 31, 31, 31, 31, 31, 31, 31, +}; + +static const int8_t dttab[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, + 4, 6, 6, 6, 8, 8, 8, 10, 10, 12, 12, 14, 16, 16, 16, 16, + 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, + 10, 12, 12, 14, 16, 16, 18, 20, 22, 24, 26, 28, 32, 32, 32, 32, + 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 12, 12, 14, + 16, 16, 18, 20, 22, 24, 26, 28, 32, 34, 38, 40, 44, 44, 44, 44, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2, -4, -4, -4, -4, + -4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14,-16,-16,-16,-16, + -2, -2, -2, -2, -4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10, + -10,-12,-12,-14,-16,-16,-18,-20,-22,-24,-26,-28,-32,-32,-32,-32, + -4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14, + -16,-16,-18,-20,-22,-24,-26,-28,-32,-34,-38,-40,-44,-44,-44,-44, +}; + +static const uint8_t gaintab[64] = { + 0xff, 0xea, 0xd7, 0xc5, 0xb5, 0xa6, 0x98, 0x8b, 0x80, 0x75, 0x6c, 0x63, 0x5a, 0x53, 0x4c, 0x46, + 0x40, 0x3b, 0x36, 0x31, 0x2d, 0x2a, 0x26, 0x23, 0x20, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x12, + 0x10, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x06, 0x06, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, +}; + +/* sinf(M_PI*(2*i+1)/1024.0f), i=0,...,511. +// Should make this twice as large (so a duplicate of the top 512, but with the other half of the +// interval [0,2*M_PI], therefore the negative of the first half), and then get rid of the +// silly hack in Sinetable(). However, I'm not actually sure which will use less gates on an FPGA, +// and there's really no speed difference on any machine newer than a 6502, probably. */ +static const int16_t sinetable[1024] = { + 1, 2, 4, 5, 7, 9, 10, 12, 13, 15, 16, 18, 20, 21, 23, 24, + 26, 27, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 45, 46, 48, 49, + 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 68, 69, 71, 72, 74, + 75, 77, 78, 80, 81, 83, 84, 86, 87, 88, 90, 91, 93, 94, 96, 97, + 99, 100, 102, 103, 104, 106, 107, 109, 110, 112, 113, 114, 116, 117, 119, 120, + 121, 123, 124, 125, 127, 128, 130, 131, 132, 134, 135, 136, 138, 139, 140, 142, + 143, 144, 145, 147, 148, 149, 151, 152, 153, 154, 156, 157, 158, 159, 161, 162, + 163, 164, 165, 167, 168, 169, 170, 171, 173, 174, 175, 176, 177, 178, 179, 180, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 212, + 213, 214, 215, 216, 217, 218, 218, 219, 220, 221, 222, 222, 223, 224, 225, 225, + 226, 227, 228, 228, 229, 230, 230, 231, 232, 232, 233, 234, 234, 235, 236, 236, + 237, 237, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, + 245, 246, 246, 247, 247, 247, 248, 248, 249, 249, 249, 250, 250, 250, 251, 251, + 251, 252, 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 254, 255, 255, + 255, 255, 255, 255, 255, 255, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 255, 255, 255, 255, 255, 255, + 255, 255, 254, 254, 254, 254, 254, 253, 253, 253, 253, 252, 252, 252, 252, 251, + 251, 251, 250, 250, 250, 249, 249, 249, 248, 248, 247, 247, 247, 246, 246, 245, + 245, 244, 244, 243, 243, 242, 242, 241, 241, 240, 240, 239, 239, 238, 237, 237, + 236, 236, 235, 234, 234, 233, 232, 232, 231, 230, 230, 229, 228, 228, 227, 226, + 225, 225, 224, 223, 222, 222, 221, 220, 219, 218, 218, 217, 216, 215, 214, 213, + 212, 212, 211, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, + 197, 196, 195, 194, 193, 192, 191, 190, 189, 188, 187, 186, 185, 184, 183, 182, + 180, 179, 178, 177, 176, 175, 174, 173, 171, 170, 169, 168, 167, 165, 164, 163, + 162, 161, 159, 158, 157, 156, 154, 153, 152, 151, 149, 148, 147, 145, 144, 143, + 142, 140, 139, 138, 136, 135, 134, 132, 131, 130, 128, 127, 125, 124, 123, 121, + 120, 119, 117, 116, 114, 113, 112, 110, 109, 107, 106, 104, 103, 102, 100, 99, + 97, 96, 94, 93, 91, 90, 88, 87, 86, 84, 83, 81, 80, 78, 77, 75, + 74, 72, 71, 69, 68, 66, 64, 63, 61, 60, 58, 57, 55, 54, 52, 51, + 49, 48, 46, 45, 43, 41, 40, 38, 37, 35, 34, 32, 31, 29, 27, 26, + 24, 23, 21, 20, 18, 16, 15, 13, 12, 10, 9, 7, 6, 4, 2, 1, + -1, -2, -4, -5, -7, -9, -10, -12, -13, -15, -16, -18, -20, -21, -23, -24, + -26, -27, -29, -31, -32, -34, -35, -37, -38, -40, -41, -43, -45, -46, -48, -49, + -51, -52, -54, -55, -57, -58, -60, -61, -63, -64, -66, -68, -69, -71, -72, -74, + -75, -77, -78, -80, -81, -83, -84, -86, -87, -88, -90, -91, -93, -94, -96, -97, + -99, -100, -102, -103, -104, -106, -107, -109, -110, -112, -113, -114, -116, -117, -119, -120, + -121, -123, -124, -125, -127, -128, -130, -131, -132, -134, -135, -136, -138, -139, -140, -142, + -143, -144, -145, -147, -148, -149, -151, -152, -153, -154, -156, -157, -158, -159, -161, -162, + -163, -164, -165, -167, -168, -169, -170, -171, -173, -174, -175, -176, -177, -178, -179, -180, + -182, -183, -184, -185, -186, -187, -188, -189, -190, -191, -192, -193, -194, -195, -196, -197, + -198, -199, -200, -201, -202, -203, -204, -205, -206, -207, -208, -209, -210, -211, -212, -212, + -213, -214, -215, -216, -217, -218, -218, -219, -220, -221, -222, -222, -223, -224, -225, -225, + -226, -227, -228, -228, -229, -230, -230, -231, -232, -232, -233, -234, -234, -235, -236, -236, + -237, -237, -238, -239, -239, -240, -240, -241, -241, -242, -242, -243, -243, -244, -244, -245, + -245, -246, -246, -247, -247, -247, -248, -248, -249, -249, -249, -250, -250, -250, -251, -251, + -251, -252, -252, -252, -252, -253, -253, -253, -253, -254, -254, -254, -254, -254, -255, -255, + -255, -255, -255, -255, -255, -255, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, + -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -255, -255, -255, -255, -255, -255, + -255, -255, -254, -254, -254, -254, -254, -253, -253, -253, -253, -252, -252, -252, -252, -251, + -251, -251, -250, -250, -250, -249, -249, -249, -248, -248, -247, -247, -247, -246, -246, -245, + -245, -244, -244, -243, -243, -242, -242, -241, -241, -240, -240, -239, -239, -238, -237, -237, + -236, -236, -235, -234, -234, -233, -232, -232, -231, -230, -230, -229, -228, -228, -227, -226, + -225, -225, -224, -223, -222, -222, -221, -220, -219, -218, -218, -217, -216, -215, -214, -213, + -212, -212, -211, -210, -209, -208, -207, -206, -205, -204, -203, -202, -201, -200, -199, -198, + -197, -196, -195, -194, -193, -192, -191, -190, -189, -188, -187, -186, -185, -184, -183, -182, + -180, -179, -178, -177, -176, -175, -174, -173, -171, -170, -169, -168, -167, -165, -164, -163, + -162, -161, -159, -158, -157, -156, -154, -153, -152, -151, -149, -148, -147, -145, -144, -143, + -142, -140, -139, -138, -136, -135, -134, -132, -131, -130, -128, -127, -125, -124, -123, -121, + -120, -119, -117, -116, -114, -113, -112, -110, -109, -107, -106, -104, -103, -102, -100, -99, + -97, -96, -94, -93, -91, -90, -88, -87, -86, -84, -83, -81, -80, -78, -77, -75, + -74, -72, -71, -69, -68, -66, -64, -63, -61, -60, -58, -57, -55, -54, -52, -51, + -49, -48, -46, -45, -43, -41, -40, -38, -37, -35, -34, -32, -31, -29, -27, -26, + -24, -23, -21, -20, -18, -16, -15, -13, -12, -10, -9, -7, -6, -4, -2, -1, +}; + +static const uint8_t cltab[512] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +}; + +static const uint8_t fbtab[8] = { 31, 7, 6, 5, 4, 3, 2, 1 }; + +/* Amplitude/Phase modulation tables. */ +static const uint8_t amt[4] = { 29, 4, 2, 1 }; /* OPNA */ + +/* libOPNMIDI: pan law table */ +static const uint16_t panlawtable[] = { + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + +/* --------------------------------------------------------------------------- */ +static inline void LFO(OPNA *opna) +{ + uint8_t c = (opna->lfocount >> FM_LFOCBITS) & 0xff; + opna->lfocount += opna->lfodcount; + if (c < 0x80) opna->aml = (c << 1); + else opna->aml = ~(c << 1); +} + +/* --------------------------------------------------------------------------- +// Magic. No, really. +// In reality this just initialises some tables used by everything else, +// that are dependent on both the chip clock and the "DAC" samplerate. +// The hilarious thing though is that this is really the only place where +// the chip clock value gets actually *used*, and even then it's indirectly +// via the ratio parameter. +*/ +static void MakeTimeTable(OPNA *opna, uint32_t ratio) +{ + int h, l; + uint32_t *ratetable = opna->ratetable; + + if (ratio != opna->currentratio) + { + opna->currentratio = ratio; + + /* EG */ + for (h=1; h<16; h++) + { + for (l=0; l<4; l++) + { + int m = h == 15 ? 8 : l+4; + ratetable[h*4+l] = + ((ratio << (FM_EGBITS - 3 - FM_RATIOBITS)) << Min(h, 11)) * m; + } + } + ratetable[0] = ratetable[1] = ratetable[2] = ratetable[3] = 0; + ratetable[5] = ratetable[4], ratetable[7] = ratetable[6]; + } +} + +static void SetEGRate(FMOperator *op, uint32_t r) +{ + Channel4 *ch = op->master; + OPNA *opna = ch->master; + op->egstepd = opna->ratetable[r]; + op->egtransa = Limit(15 - (r>>2), 4, 1); + op->egtransd = 16 >> op->egtransa; +} + +/* Standard operator init routine. Zeros out some more stuff +// than OperatorReset() does, then calls OperatorReset(). +*/ +void OperatorInit(Channel4 *ch4, FMOperator *op) +{ + op->master = ch4; + + /* EG Part */ + op->ar = op->dr = op->sr = op->rr = op->ksr = 0; + op->ams = 0; + op->mute = 0; + op->keyon = 0; + + /* PG Part */ + op->multiple = 0; + op->detune = 0; + + /* LFO */ + op->ms = 0; + + OperatorReset(op); +} + +/* Standard operator reset routine. Init EG/PG to defaults, +// clear any stored samples, then force a reinit of EG/PG +// in OperatorPrepare() below by setting paramchanged to 1. +*/ +void OperatorReset(FMOperator *op) +{ + /* EG part */ + op->tl = op->tll = 127; + op->eglevel = 0xff; + op->eglvnext = 0x100; + SetEGRate(op, 0); + op->phase = off; + op->egstep = 0; + + /* PG part */ + op->pgcount = 0; + + /* OP part */ + op->out = op->out2 = 0; + op->paramchanged = 1; +} + +/* Init EG, PG. +// PG init is trivial, simply set pgdcount (phase counter increment) +// based on multiple, detune and bn. +// See Pages 24-26 of the OPNA manual for details. +// EG init is your standard ADSR state machine. Should (hopefully!) +// be self-explanatory, especially if you've ever seen a software implementation +// of ADSR before (seriously, they're all the damn same). +*/ +void OperatorPrepare(FMOperator *op) +{ + Channel4 *ch = op->master; + OPNA *opna = ch->master; + + if (op->paramchanged) + { + uint8_t l = ((op->multiple) ? 2*op->multiple : 1); + op->paramchanged = 0; + /* PG Part */ + op->pgdcount = (op->dp + dttab[op->detune + op->bn]) * (uint32_t)(l * opna->rr); + op->pgdcountl = op->pgdcount >> 11; + + /* EG Part */ + op->ksr = op->bn >> (3-op->ks); + + switch (op->phase) + { + case attack: + SetEGRate(op, op->ar ? Min(63, op->ar+op->ksr) : 0); + break; + case decay: + SetEGRate(op, op->dr ? Min(63, op->dr+op->ksr) : 0); + op->eglvnext = op->sl * 8; + break; + case sustain: + SetEGRate(op, op->sr ? Min(63, op->sr+op->ksr) : 0); + break; + case release: + SetEGRate(op, Min(63, op->rr+op->ksr)); + break; + case next: /* temporal */ + break; + case off: /* temporal */ + break; + } + /* LFO */ + op->ams = (op->amon ? (op->ms >> 4) & 3 : 0); + } +} + +/* FIXME: Rename. "Phase" here refers to ADSR DFA state, +// not PG/sine table phase. Also, yeah, this does the +// ADSR DFA state transitions. +*/ +static void ShiftPhase(FMOperator *op, EGPhase nextphase) +{ + switch (nextphase) + { + case attack: /* Attack Phase */ + op->tl = op->tll; + if ((op->ar+op->ksr) < 62) { + SetEGRate(op, op->ar ? Min(63, op->ar+op->ksr) : 0); + op->phase = attack; + break; + } + /* fall through */ + case decay: /* Decay Phase */ + if (op->sl) { + op->eglevel = 0; + op->eglvnext = op->sl*8; + SetEGRate(op, op->dr ? Min(63, op->dr+op->ksr) : 0); + op->phase = decay; + break; + } + /* fall through */ + case sustain: /* Sustain Phase */ + op->eglevel = op->sl*8; + op->eglvnext = 0x100; + SetEGRate(op, op->sr ? Min(63, op->sr+op->ksr) : 0); + op->phase = sustain; + break; + + case release: /* Release Phase */ + if (op->phase == attack || (op->eglevel < 0x100/* && phase != off*/)) { + op->eglvnext = 0x100; + SetEGRate(op, Min(63, op->rr+op->ksr)); + op->phase = release; + break; + } + /* fall through */ + case off: /* off */ + default: + op->eglevel = 0xff; + op->eglvnext = 0x100; + SetEGRate(op, 0); + op->phase = off; + break; + } +} + +/* Block/F-Num */ +static inline void SetFNum(FMOperator *op, uint32_t f) +{ + op->dp = (f & 2047) << ((f >> 11) & 7); + op->bn = notetab[(f >> 7) & 127]; + op->paramchanged = 1; +} + +/* Clock the EG for one operator. +// Essentially just a call to ShiftPhase, +// but decrements the output EG level if starting +// from the attack phase, otherwise incrementing it. +// Should probably integrate the special case for attack +// from ShiftPhase() directly into here at some point. */ +void EGCalc(FMOperator *op) +{ + op->egstep += 3L << (11 + FM_EGBITS); + if (op->phase == attack) + { + op->eglevel -= 1 + (op->eglevel >> op->egtransa); + if (op->eglevel <= 0) + ShiftPhase(op, decay); + } + else + { + op->eglevel += op->egtransd; + if (op->eglevel >= op->eglvnext) + ShiftPhase(op, (EGPhase)(op->phase+1)); + } +} + +/* KeyOn, hopefully obvious. */ +static void KeyOn(FMOperator *op) +{ + /*if (!op->keyon && ((op->ar = 31) || (op->ar == 62))) {*/ + if (!op->keyon) { + op->keyon = 1; + if (!op->sl) { + ShiftPhase(op, sustain); + op->out = op->out2 = 0; + op->pgcount = 0; + } else { + if (op->phase == off || op->phase == release) { + ShiftPhase(op, attack); + op->out = op->out2 = 0; + op->pgcount = 0; + } + } + } +} + +/* KeyOff, hopefully obvious. */ +static void KeyOff(FMOperator *op) +{ + if (op->keyon) { + op->keyon = 0; + ShiftPhase(op, release); + } +} + +/* PG uses 9 bits, with the table itsself using another 10 bits. +// The top bits are the actually relevant ones, given that the PG increment will basically set +// the lowest few bits to nonsense. +// The hack there that checks for bit 10 in the right place and if yes, does some strange xor magic +// makes the value of Sine() negative if we're in the top half of the [0,2*M_PI] interval. +// It is, of course, one/two's complement specific, but I have yet to hear of an integer arithmetic implementation +// on any modern machine that isn't at least one of those two. (In fact, I think they're all two's complement, even). */ +/*#define Sine(s) sinetable[((s) >> (20+FM_PGBITS-FM_OPSINBITS))&(FM_OPSINENTS/2-1)]^(-(((s) & 0x10000000) >> 27))*/ +#define Sine(s) sinetable[((s) >> (20+FM_PGBITS-FM_OPSINBITS))&(FM_OPSINENTS-1)] + +static inline uint32_t LogToLin(uint32_t x) { + if(x >= 0xff) { + return 0; + } + return cltab[x]; +} + +/* PG clock routine. +// Does this really need to be in its own function anymore? +// It's literally just a trivial increment of a counter now, nothing more. +// Its output, btw, is 2^(20+PGBITS) / cycle, with PGBITS=9 in this implementation. */ +static inline uint32_t PGCalc(FMOperator *op) +{ + uint32_t ret = op->pgcount; + op->pgcount += op->pgdcount; + return ret; +} + +/* Clock one FM operator. Does a lookup in the sine table +// for the waveform to output, possibly frequency-modulating +// that with the contents of in, then clocks the Phase Generator +// for that operator, stores the output sample and returns. +// Should probably integrate PGCalc() into this function, +// at some point at least. */ +static inline int32_t Calc(FMOperator *op, int32_t in) +{ + int32_t tmp = Sine(op->pgcount + (in << 7)); + op->out = op->egout*tmp; + PGCalc(op); + return op->out; +} + +/* Clock operator 0. OP0 is special as it does not take an input from +// another operator, rather it can frequency-modulate itsself via the +// fb parameter (which specifies feedback amount). This is incredibly +// useful, and makes it possible to define a lot more instruments +// for the OPNA than you'd be able to otherwise. */ +#define FM_PRECISEFEEDBACK 1 +static inline void CalcFB(FMOperator *op, uint32_t fb) +{ + int32_t tmp; + int32_t in = op->out + op->out2; + op->out2 = op->out; + if (FM_PRECISEFEEDBACK && fb == 31) + tmp = Sine(op->pgcount); + else + tmp = Sine(op->pgcount + ((in << 6) >> fb)); + + op->out = op->egout*tmp; + PGCalc(op); +} + +/* --------------------------------------------------------------------------- +// 4-op Channel +// Sets the "algorithm", i.e. the connections between individual operators +// in a channel. See Page 22 of the manual for pretty drawings of all of the +// different algorithms supported by the OPNA. +*/ +static void SetAlgorithm(Channel4 *ch4, uint32_t algo) +{ + static const uint8_t table1[8][6] = + { + { 0, 1, 1, 2, 2, 3 }, { 1, 0, 0, 1, 1, 2 }, + { 1, 1, 1, 0, 0, 2 }, { 0, 1, 2, 1, 1, 2 }, + { 0, 1, 2, 2, 2, 1 }, { 0, 1, 0, 1, 0, 1 }, + { 0, 1, 2, 1, 2, 1 }, { 1, 0, 1, 0, 1, 0 }, + }; + + ch4->idx[0] = table1[algo][0]; /* in[0]; */ + ch4->idx[1] = table1[algo][2]; /* in[1]; */ + ch4->idx[2] = table1[algo][4]; /* in[2]; */ + ch4->idx[3] = table1[algo][1]; /* out[0]; */ + ch4->idx[4] = table1[algo][3]; /* out[1]; */ + ch4->idx[5] = table1[algo][5]; /* out[2]; */ + ch4->op[0].out2 = ch4->op[0].out = 0; +} + +static inline void Ch4Init(OPNA *opna, Channel4 *ch4) +{ + int i; + ch4->master = opna; + for(i=0; i<4; i++) { + OperatorInit(ch4, &ch4->op[i]); + } + SetAlgorithm(ch4, 0); +} + +/* Reinit all operators on a given channel if paramchanged=1 +// for that channel, set the PM table for that channel, then determine +// if there is any output from this channel, based on: +// - mute state of each operator +// - keyon state of each operator +// - AM (Tremolo) enable for each operator. +// Bit 0 of the return value is set if there is any output, +// Bit 1 is set if tremolo is enabled for any of the operators on this +// channel. */ +static inline int Ch4Prepare(Channel4 *ch4) +{ + OperatorPrepare(&ch4->op[0]); + OperatorPrepare(&ch4->op[1]); + OperatorPrepare(&ch4->op[2]); + OperatorPrepare(&ch4->op[3]); + + if(ch4->op[0].mute && ch4->op[1].mute && ch4->op[2].mute && ch4->op[3].mute) + return 0; + else { + int key = (IsOn(&ch4->op[0]) | IsOn(&ch4->op[1]) | IsOn(&ch4->op[2]) | IsOn(&ch4->op[3])) ? 1 : 0; + int lfo = ch4->op[0].ms & (ch4->op[0].amon | ch4->op[1].amon | ch4->op[2].amon | ch4->op[3].amon ? 0x37 : 7) ? 2 : 0; + return key | lfo; + } +} + +/* Clock one channel. Clocks all the Envelope Generators in parallel +// (well, okay, in sequence, but a hardware implementation *should* +// clock them in parallel as they are completely independent tasks, +// all that is important is that you don't execute Calc{L,FB,FBL} +// until all of the EGs are done clocking - but that should be, again, +// straightforward to implement in hardware). +*/ +int32_t Ch4Calc(Channel4 *ch4) +{ + int i, o; + OPNA *opna = ch4->master; + ch4->buf[1] = ch4->buf[2] = ch4->buf[3] = 0; + for(i=0; i<4; i++) { + if ((ch4->op[i].egstep -= ch4->op[i].egstepd) < 0) + EGCalc(&ch4->op[i]); + ch4->op[i].egout = (LogToLin(ch4->op[i].eglevel + (opna->aml >> amt[ch4->op[i].ams]))*gaintab[ch4->op[i].tl]); + } + + ch4->buf[0] = ch4->op[0].out; CalcFB(&ch4->op[0], ch4->fb); + if (!(ch4->idx[0] | ch4->idx[2] | ch4->idx[4])) { + o = Calc(&ch4->op[1], ch4->buf[0]); + o += Calc(&ch4->op[2], ch4->buf[0]); + o += Calc(&ch4->op[3], ch4->buf[0]); + return (o >> 8); + } else { + ch4->buf[ch4->idx[3]] += Calc(&ch4->op[1], ch4->buf[ch4->idx[0]]); + ch4->buf[ch4->idx[4]] += Calc(&ch4->op[2], ch4->buf[ch4->idx[1]]); + o = ch4->op[3].out; + Calc(&ch4->op[3], ch4->buf[ch4->idx[2]]); + return ((ch4->buf[ch4->idx[5]] + o) >> 8); + } +} + +/* This essentially initializes a couple constant tables +// and chip-specific parameters based on what the chip clock and "DAC" samplerate +// were set to in OPNAInit(). psgrate is always equal to the user-requested samplerate, +// whereas rate is only equal to that in the interpolation=0 case, otherwise +// it's set to whatever value is needed to downsample 55466Hz to the user-requested +// samplerate, which will (almost?) always be either 44100Hz or 48000Hz. +// TODO: better-quality resampling may be of use here, possibly. +*/ +static void SetPrescaler(OPNA *opna, uint32_t p) +{ + static const char table[3][2] = { { 6, 4 }, { 3, 2 }, { 2, 1 } }; + static const uint8_t table2[8] = { 109, 78, 72, 68, 63, 45, 9, 6 }; + /* 512 */ + if (opna->prescale != p) + { + uint32_t i, fmclock; + uint32_t ratio; + + opna->prescale = p; + fmclock = opna->clock / table[p][0] / 12; + + if (opna->interpolation) { + opna->rate = fmclock * 2; + do { + opna->rate >>= 1; + opna->mpratio = opna->psgrate * 16384 / opna->rate; + } while (opna->mpratio <= 8192); + } else { + opna->rate = opna->psgrate; + } + ratio = ((fmclock << FM_RATIOBITS) + opna->rate/2) / opna->rate; + opna->timer_step = (int32_t)(1000000.0f * 65536.0f/fmclock); + /* PG Part */ + opna->rr = (float)ratio / (1 << (2 + FM_RATIOBITS - FM_PGBITS)); + MakeTimeTable(opna, ratio); + PSGSetClock(&opna->psg, opna->clock / table[p][1], opna->psgrate); + + for (i=0; i<8; i++) { + opna->lfotab[i] = (ratio << (1+FM_LFOCBITS-FM_RATIOBITS)) / table2[i]; + } + } +} + +static inline void RebuildTimeTable(OPNA *opna) +{ + int p = opna->prescale; + opna->prescale = -1; + SetPrescaler(opna, p); +} + +/* Chip-internal TimerA() handler. All it does is implement CSM, i.e. +// channel 3 will get keyed on and off whenever the TimerA() interrupt fires. +// To the best of my knowledge, CSM was intended to be used to implement +// primitive formant synthesis (which Yamaha later repackaged in a much more +// elaborate and featured implementation in their FS1R), and used by +// approximately nobody. It's also been removed from the YMF288/OPN3. +*/ +static void TimerA(OPNA *opna) +{ + int i; + if (opna->regtc & 0x80) + { + for(i=0; i<4; i++) + KeyOn(&opna->csmch->op[i]); + for(i=0; i<4; i++) + KeyOff(&opna->csmch->op[i]); + } +} + +/* --------------------------------------------------------------------------- +// Clock timers. TimerA has a resolution of 9 microseconds (assuming standard +// chip clockspeed of 8MHz, which all of this code of course does), and +// on the Speak Board for the PC-9801 is used only for the purpose of sound effects. +// TimerB, on the other hand, has a resolution of 144 microseconds, and is basically +// used as the main chip clock. Also, binding "sound-effects" to TimerB (needed as +// ZUN uses the sound-effects feature to implement PSG percussion) results in tiny +// changes to the output file, precisely none of them audible, making TimerA +// all but useless in this case. Note that TimerA is also used internally in the chip +// to implement CSM-mode (see comment above). +*/ +uint8_t OPNATimerCount(OPNA *opna, int32_t us) +{ + uint8_t event = 0; + + if (opna->timera_count) { + opna->timera_count -= us << 16; + if (opna->timera_count <= 0) { + event = 1; + TimerA(opna); + + while (opna->timera_count <= 0) + opna->timera_count += opna->timera; + + if (opna->regtc & 4) { + if (!(opna->status & 1)) { + opna->status |= 1; + } + } + } + } + if (opna->timerb_count) { + opna->timerb_count -= us << 12; + if (opna->timerb_count <= 0) { + event = 1; + while (opna->timerb_count <= 0) + opna->timerb_count += opna->timerb; + + if (opna->regtc & 8) { + if (!(opna->status & 2)) { + opna->status |= 2; + } + } + } + } + return event; +} + +/* Rhythm source samples. pcm_s8 (*not* u8!), and found in rhythmdata.h, +// which is included in rhythmdata.c in order to keep the size of the +// object file that you get from compiling this file at a reasonable size, +// for debugging/testing/sanity purposes. +*/ +extern const unsigned char* rhythmdata[6]; +static const unsigned int rhythmdatalen[6] = { + 9013, 10674, 66610, 7259, 18562, 3042 +}; + +/* --------------------------------------------------------------------------- +// Main chip init routine. +// c is the chip clock, which should never be set to anything other than 8MHz. +// r is the chip samplerate, set to 44100 typically. +// ipflag - if 1, ignore the value of r, clock the "DAC" at the OPNA-internal +// samplerate of 55466Hz, then downsample to whatever the actual value of r is. +*/ +uint8_t OPNAInit(OPNA *opna, uint32_t c, uint32_t r, uint8_t ipflag) +{ + int i; + opna->devmask = 0x7; + opna->prescale = 0; + opna->rate = 44100; + opna->mixl = 0; + opna->mixr = 0; + opna->mixdelta = 16383; + opna->interpolation = 0; + + for (i=0; i<8; i++) + opna->lfotab[i] = 0; + + opna->aml = 0; + + opna->currentratio = ~0; + opna->rr = 0; + for (i=0; i<64; i++) + opna->ratetable[i] = 0; + + for (i=0; i<6; i++) { + Ch4Init(opna, &opna->ch[i]); + opna->rhythm[i].sample = 0; + opna->rhythm[i].pos = 0; + opna->rhythm[i].size = 0; + opna->rhythm[i].volume = 0; + } + opna->rhythmtvol = 0; + opna->csmch = &opna->ch[2]; + for (i=0; i<6; i++) + opna->rhythm[i].pos = ~0; + + for (i=0; i<6; i++) + { + uint8_t *file_buf = (uint8_t*)0; + uint32_t fsize; + file_buf = (uint8_t*)rhythmdata[i]; + fsize = rhythmdatalen[i]; + file_buf += 44; + fsize -= 44; + fsize /= 2; + opna->rhythm[i].sample = (int8_t*)file_buf; + opna->rhythm[i].rate = 44100; + opna->rhythm[i].step = opna->rhythm[i].rate * 1024 / opna->rate; + opna->rhythm[i].pos = opna->rhythm[i].size = fsize * 1024; + } + + c /= 2; + opna->clock = c; + if (!OPNASetRate(opna, r, ipflag)) + return 0; + RebuildTimeTable(opna); + OPNAReset(opna); + PSGInit(&opna->psg); + + OPNASetChannelMask(opna, ~0); + return 1; +} + +/* --------------------------------------------------------------------------- +// Reset chip. Your standard routine, basically zeros everything in sight. +*/ +void OPNAReset(OPNA *opna) +{ + int i, j; + + opna->status = 0; + SetPrescaler(opna, 0); + opna->timera_count = 0; + opna->timerb_count = 0; + PSGReset(&opna->psg); + opna->reg29 = 0x1f; + opna->rhythmkey = 0; + for (i=0x20; i<0x28; i++) OPNASetReg(opna, i, 0); + for (i=0x30; i<0xc0; i++) OPNASetReg(opna, i, 0); + for (i=0x130; i<0x1c0; i++) OPNASetReg(opna, i, 0); + for (i=0x100; i<0x110; i++) OPNASetReg(opna, i, 0); + for (i=0x10; i<0x20; i++) OPNASetReg(opna, i, 0); + for (i=0; i<6; i++) { + Channel4 *ch = &opna->ch[i]; + ch->panl = 46340; + ch->panr = 46340; + for(j=0; j<4; j++) { + FMOperator *op = &opna->ch[i].op[j]; + OperatorReset(op); + } + } + + opna->statusnext = 0; + opna->lfocount = 0; + opna->status = 0; +} + +/* --------------------------------------------------------------------------- +// Change OPNA "DAC" samplerate. +// r and ipflag are as in OPNAInit(), above. +*/ +uint8_t OPNASetRate(OPNA *opna, uint32_t r, uint8_t ipflag) +{ + int i, j; + opna->interpolation = ipflag; + opna->psgrate = r; + RebuildTimeTable(opna); + opna->lfodcount = opna->reg22 & 0x08 ? opna->lfotab[opna->reg22 & 7] : 0; + + for (i=0; i<6; i++) { + for (j=0; j<4; j++) + opna->ch[i].op[j].paramchanged = 1; + } + for (i=0; i<6; i++) { + opna->rhythm[i].step = opna->rhythm[i].rate * 1024 / r; + } + return 1; +} + +void SetVolumeRhythm(OPNA *opna, unsigned int index, int db) +{ + db = Min(db, 20); + opna->rhythm[index].volume = 16-(db * 2 / 3); +} + +/* --------------------------------------------------------------------------- +// Set OPNA channel mask. The 6 LSBs of mask are 0 to disable that FM channel, +// and 1 to enable it. The next 3 LSBs are passed to PSGSetChannelMask() to, +// well, set the PSG channel mask (which behaves the same way: 0 disables +// a given channel and 1 enables it). +*/ +void OPNASetChannelMask(OPNA *opna, uint32_t mask) +{ + int i, j; + for (i=0; i<6; i++) { + for (j=0; j<4; j++) { + opna->ch[i].op[j].mute = (!(mask & (1 << i))); + opna->ch[i].op[j].paramchanged = 1; + } + } + PSGSetChannelMask(&opna->psg, (mask >> 6)); + /*if (!(mask & 0x200)) opna->devmask = 3;*/ +} + +#include + +/* libOPNMIDI: allow to disable the console messages */ +#if defined(OPNA_VERBOSE) +static void message(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} +#else +static void message(const char *fmt, ...) +{ + (void)fmt; +} +#endif + +/* --------------------------------------------------------------------------- +// Main OPNA register-set routine. Really long and boring switch-case. +// Basically taken directly from the manual - the only parts of the spec +// that were even the least bit tricky to implement were the f-number tables, +// everything else is basically obvious. +*/ +void OPNASetReg(OPNA *opna, uint32_t addr, uint32_t data) +{ + uint32_t j, _dp = 0; + int c = addr & 3; + switch (addr) + { + uint32_t modified; + uint32_t tmp; + + /* Timer ----------------------------------------------------------------- */ + case 0x24: case 0x25: + opna->regta[addr & 1] = (uint8_t)data; + tmp = (opna->regta[0] << 2) + (opna->regta[1] & 3); + opna->timera = (1024-tmp) * opna->timer_step; + break; + + case 0x26: + opna->timerb = (256-data) * opna->timer_step; + break; + + case 0x27: + tmp = opna->regtc ^ data; + opna->regtc = (uint8_t)data; + if (data & 0x10) + opna->status &= ~1; + if (data & 0x20) + opna->status &= ~2; + if (tmp & 0x01) + opna->timera_count = (data & 1) ? opna->timera : 0; + if (tmp & 0x02) + opna->timerb_count = (data & 2) ? opna->timerb : 0; + break; + + /* Misc ------------------------------------------------------------------ */ + case 0x28: /* Key On/Off */ + if ((data & 3) < 3) + { + uint32_t key = (data >> 4); + c = (data & 3) + (data & 4 ? 3 : 0); + if (key & 0x1) KeyOn(&opna->ch[c].op[0]); else KeyOff(&opna->ch[c].op[0]); + if (key & 0x2) KeyOn(&opna->ch[c].op[1]); else KeyOff(&opna->ch[c].op[1]); + if (key & 0x4) KeyOn(&opna->ch[c].op[2]); else KeyOff(&opna->ch[c].op[2]); + if (key & 0x8) KeyOn(&opna->ch[c].op[3]); else KeyOff(&opna->ch[c].op[3]); + } + break; + + /* Status Mask ----------------------------------------------------------- */ + case 0x29: + opna->reg29 = data; + break; + + /* Prescaler ------------------------------------------------------------- */ + case 0x2d: case 0x2e: case 0x2f: + SetPrescaler(opna, (addr-0x2d)); + break; + + /* F-Number -------------------------------------------------------------- */ + case 0x1a0: case 0x1a1: case 0x1a2: + c += 3; + /* fall through */ + case 0xa0: case 0xa1: case 0xa2: + opna->fnum[c] = data + opna->fnum2[c] * 0x100; + _dp = (opna->fnum[c] & 2047) << ((opna->fnum[c] >> 11) & 7); + for(j=0; j<4; j++) { + opna->ch[c].op[j].dp = _dp; + opna->ch[c].op[j].bn = notetab[(opna->fnum[c] >> 7) & 127]; + opna->ch[c].op[j].paramchanged = 1; + } + break; + + case 0x1a4: case 0x1a5: case 0x1a6: + c += 3; + /* fall through */ + case 0xa4 : case 0xa5: case 0xa6: + opna->fnum2[c] = (uint8_t)data; + break; + + case 0xa8: case 0xa9: case 0xaa: + opna->fnum3[c] = data + opna->fnum2[c+6] * 0x100; + break; + + case 0xac : case 0xad: case 0xae: + opna->fnum2[c+6] = (uint8_t)data; + break; + + /* Algorithm ------------------------------------------------------------- */ + case 0x1b0: case 0x1b1: case 0x1b2: + c += 3; + /* fall through */ + case 0xb0: case 0xb1: case 0xb2: + opna->ch[c].fb = fbtab[((data >> 3) & 7)]; + SetAlgorithm(&opna->ch[c], data & 7); + message("OP%u: Algorithm: %u, FB: %u\n", c, data & 7, opna->ch[c].fb); + break; + + case 0x1b4: case 0x1b5: case 0x1b6: + c += 3; + /* fall through */ + case 0xb4: case 0xb5: case 0xb6: + /*opna->pan[c] = (data >> 6) & 3;*/ + for(j=0; j<4; j++) { + opna->ch[c].op[j].ms = data; + opna->ch[c].op[j].paramchanged = 1; + } + break; + + /* Rhythm ---------------------------------------------------------------- */ + case 0x10: /* DM/KEYON */ + if (!(data & 0x80)) /* KEY ON */ + { + opna->rhythmkey |= data & 0x3f; + if (data & 0x01) opna->rhythm[0].pos = 0; + if (data & 0x02) opna->rhythm[1].pos = 0; + if (data & 0x04) opna->rhythm[2].pos = 0; + if (data & 0x08) opna->rhythm[3].pos = 0; + if (data & 0x10) opna->rhythm[4].pos = 0; + if (data & 0x20) opna->rhythm[5].pos = 0; + } + else + { /* DUMP */ + opna->rhythmkey &= ~data; + } + break; + + case 0x11: + opna->rhythmtl = ~data & 63; + break; + + case 0x1a: /* Top Cymbal */ + break; + case 0x18: /* Bass Drum */ + case 0x19: /* Snare Drum */ + case 0x1b: /* Hihat */ + case 0x1c: /* Tom-tom */ + case 0x1d: /* Rim shot */ + opna->rhythm[addr & 7].pan = (data >> 6) & 3; + opna->rhythm[addr & 7].level = ~data & 31; + break; + + /* LFO ------------------------------------------------------------------- */ + case 0x22: + modified = opna->reg22 ^ data; + opna->reg22 = data; + if (modified & 0x8) + opna->lfocount = 0; + opna->lfodcount = opna->reg22 & 8 ? opna->lfotab[opna->reg22 & 7] : 0; + message("LFO: reg22: %u, lfodcount: %u\n", opna->reg22, opna->lfodcount); + break; + + /* PSG ------------------------------------------------------------------- */ + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: + PSGSetReg(&opna->psg, addr, data); + break; + + /* ADSR ------------------------------------------------------------------ */ + default: + if (c < 3) + { + if (addr & 0x100) + c += 3; + { + /*uint8_t slottable[4] = { 0, 2, 1, 3 };*/ + /*uint32_t slot = slottable[(addr >> 2) & 3];*/ + uint32_t slottable = 216; + uint32_t l, slot = ((slottable >> (((addr >> 2) & 3) << 1)) & 3); + FMOperator* op = &opna->ch[c].op[slot]; + + switch ((addr >> 4) & 15) + { + case 3: /* 30-3E DT/MULTI */ + op->detune = (((data >> 4) & 0x07) * 0x20); + op->multiple = (data & 0x0f); + l = ((op->multiple) ? 2*op->multiple : 1); + /* PG Part */ + op->pgdcount = (op->dp + dttab[op->detune + op->bn]) * (uint32_t)(l * opna->rr); + op->pgdcountl = op->pgdcount >> 11; + /*op->paramchanged = 1;*/ + if(!op->mute) + message("OP%u DT: %u, Mult: %u\n", c, op->detune, op->multiple); + break; + + case 4: /* 40-4E TL */ + if(!((opna->regtc & 0x80) && (opna->csmch == &opna->ch[c]))) { + op->tl = (data & 0x7f); + op->paramchanged = 1; + } + op->tll = (data & 0x7f); + break; + + case 5: /* 50-5E KS/AR */ + op->ks = ((data >> 6) & 3); + op->ar = ((data & 0x1f) * 2); + op->paramchanged = 1; + if(!op->mute) + message("OP%u KS: %u, AR: %u\n", c, op->ks, op->ar); + break; + + case 6: /* 60-6E DR/AMON */ + op->dr = ((data & 0x1f) * 2); + op->amon = ((data & 0x80) != 0); + op->paramchanged = 1; + if(!op->mute) + message("OP%u DR: %u, AM: %u\n", c, op->dr, op->amon); + break; + + case 7: /* 70-7E SR */ + op->sr = ((data & 0x1f) * 2); + op->paramchanged = 1; + if(!op->mute) + message("OP%u SR: %u\n", c, op->sr); + break; + + case 8: /* 80-8E SL/RR */ + op->sl = (((data >> 4) & 15) * 4); + op->rr = ((data & 0x0f) * 4 + 2); + op->paramchanged = 1; + if(!op->mute) + message("OP%u SL: %u, RR: %u\n", c, op->sl, op->rr); + break; + + case 9: /* 90-9E SSG-EC */ + op->ssgtype = (data & 0x0f); + message("OP%u SSG-EG: %u\n", c, op->ssgtype); + break; + } + } + } + break; + } +} + +/* libOPNMIDI: soft panning */ +void OPNASetPan(OPNA *opna, uint32_t chan, uint32_t data) +{ + assert(chan < 6); + assert(data < 128); + opna->ch[chan].panl = panlawtable[data & 0x7F]; + opna->ch[chan].panr = panlawtable[0x7F - (data & 0x7F)]; +} + +/* --------------------------------------------------------------------------- +// Read OPNA register. Pointless. Only SSG registers can be read, and of those +// the only one anyone seems to be interested in reading is register 7, +// which as I explain in detail in psg.c, is completely superfluous. +*/ +uint32_t OPNAGetReg(OPNA *opna, uint32_t addr) +{ + if (addr < 0x10) + return PSGGetReg(&opna->psg, addr); + if (addr == 0xff) + return 1; + return 0; +} + +/* --------------------------------------------------------------------------- */ + +static inline void MixSubS(Channel4 ch[6], int activech, int32_t *dest) +{ + unsigned int c; + int32_t l = 0; + int32_t r = 0; + + for (c = 0; c < 6; ++c) { + if (activech & (1 << (c << 1))) { + int32_t s = Ch4Calc(&ch[c]); + s >>= 2; /* libOPNMIDI: prevent FM channel clipping (TODO: also adjust PSG and rhythm) */ + l += s * ch[c].panl / 65536; + r += s * ch[c].panr / 65536; + } + } + + dest[0] = l; + dest[1] = r; +} + +/* --------------------------------------------------------------------------- +// Mix FM channels and output. Mix6 runs at user-specified samplerate, +// Mix6I runs at the chip samplerate of 55466Hz and then downsamples +// to the user-specified samplerate. It is an open problem as to determining +// if one of these sounds better than the other. +*/ +#define IStoSample(s) (Limit16((s))) + +static void Mix6(OPNA *opna, int32_t *buffer, uint32_t nsamples, int activech) +{ + /* Mix */ + int32_t ibuf[2]; + unsigned int i; + + for (i = 0; i < nsamples; i++) { + ibuf[0] = 0; + ibuf[1] = 0; + if (activech & 0xaaa) + LFO(opna), MixSubS(opna->ch, activech, ibuf); + else + MixSubS(opna->ch, activech, ibuf); + buffer[i * 2 + 0] += IStoSample(ibuf[0]); + buffer[i * 2 + 1] += IStoSample(ibuf[1]); + } +} + +/* --------------------------------------------------------------------------- +// See comment above Mix6(), above. +*/ +static void Mix6I(OPNA *opna, int32_t *buffer, uint32_t nsamples, int activech) +{ + /* Mix */ + int32_t ibuf[2], delta = opna->mixdelta; + unsigned int i; + + if (opna->mpratio < 16384) { + for (i = 0; i < nsamples; i++) { + int32_t l = 0, r = 0, d = 0; + while (delta > 0) { + ibuf[0] = 0; + ibuf[1] = 0; + if (activech & 0xaaa) + LFO(opna), MixSubS(opna->ch, activech, ibuf); + else + MixSubS(opna->ch, activech, ibuf); + + l = IStoSample(ibuf[0]); + r = IStoSample(ibuf[1]); + d = Min(opna->mpratio, delta); + opna->mixl += l * d; + opna->mixr += r * d; + delta -= opna->mpratio; + } + buffer[i * 2 + 0] += opna->mixl >> 14; + buffer[i * 2 + 1] += opna->mixr >> 14; + opna->mixl = l * (16384-d); + opna->mixr = r * (16384-d); + delta += 16384; + } + } else { + int impr = 16384 * 16384 / opna->mpratio; + for (i = 0; i < nsamples; i++) { + int32_t l, r; + if (delta < 0) { + delta += 16384; + opna->mixl = opna->mixl1; + opna->mixr = opna->mixr1; + + ibuf[0] = 0; + ibuf[1] = 0; + if (activech & 0xaaa) + LFO(opna), MixSubS(opna->ch, activech, ibuf); + else + MixSubS(opna->ch, activech, ibuf); + + opna->mixl1 = IStoSample(ibuf[0]); + opna->mixr1 = IStoSample(ibuf[1]); + } + l = (delta * opna->mixl + (16384 - delta) * opna->mixl1) / 16384; + r = (delta * opna->mixr + (16384 - delta) * opna->mixr1) / 16384; + buffer[i * 2 + 0] += l; + buffer[i * 2 + 1] += r; + delta -= impr; + } + } + opna->mixdelta = delta; +} + +/* --------------------------------------------------------------------------- +// Main FM output routine. Clocks all of the operators on the chip, then mixes +// together the output using one of Mix6() or Mix6I() above, and then outputs +// the result to OPNAMix, which is what the calling routine will actually use. +// buffer should be a pointer to a buffer of type Sample (int32_t in this +// implementation, though another used float and in principle int16_t *should* +// be sufficient), and be of size at least equal to nsamples. +*/ +static void FMMix(OPNA *opna, int32_t *buffer, uint32_t nsamples) +{ + uint32_t j; + { + /* Set F-Number */ + if (!(opna->regtc & 0xc0)) { + uint32_t _dp = (opna->fnum[opna->csmch-opna->ch] & 2047) << ((opna->fnum[opna->csmch-opna->ch] >> 11) & 7); + for(j=0; j<4; j++) { + opna->csmch->op[j].dp = _dp; + opna->csmch->op[j].bn = notetab[(opna->fnum[opna->csmch-opna->ch] >> 7) & 127]; + opna->csmch->op[j].paramchanged = 1; + } + } else { + SetFNum(&opna->csmch->op[0], opna->fnum3[1]); SetFNum(&opna->csmch->op[1], opna->fnum3[2]); + SetFNum(&opna->csmch->op[2], opna->fnum3[0]); SetFNum(&opna->csmch->op[3], opna->fnum[2]); + } + } + + { + int act = (((Ch4Prepare(&opna->ch[2]) << 2) | Ch4Prepare(&opna->ch[1])) << 2) | Ch4Prepare(&opna->ch[0]); + if (opna->reg29 & 0x80) + act |= (Ch4Prepare(&opna->ch[3]) | ((Ch4Prepare(&opna->ch[4]) | (Ch4Prepare(&opna->ch[5]) << 2)) << 2)) << 6; + if (!(opna->reg22 & 0x08)) + act &= 0x555; + + if (act & 0x555) { + if (opna->interpolation) + Mix6I(opna, buffer, nsamples, act); + else + Mix6(opna, buffer, nsamples, act); + } else { + opna->mixl = 0, opna->mixr = 0, opna->mixdelta = 16383; + } + } +} + +/* --------------------------------------------------------------------------- +// Mix Rhythm generator output. Boring, just takes the PCM samples, +// multiplies them by the volume set for that rhythm sample, and then outputs +// the appropriate length of sample for that given samplerate to buffer. +// The same restrictions on buffer as in FMMix() above apply. +*/ +static void RhythmMix(OPNA *opna, int32_t *buffer, uint32_t count) +{ + unsigned int i, j; + if (opna->rhythmtvol < 128 && opna->rhythm[0].sample && (opna->rhythmkey & 0x3f)) { + for (i=0; i<6; i++) { + Rhythm *r = &opna->rhythm[i]; + if ((opna->rhythmkey & (1 << i)) && r->level >= 0) { + int db = Limit(opna->rhythmtl+r->level+r->volume, 127, 0); + int vol = cltab[db]; + + for (j = 0; j < count && r->pos < r->size; j++) { + int sample = Limit16(((r->sample[r->pos >> 10] << 8) * vol) >> 10); + r->pos += r->step; + buffer[j * 2 + 0] += sample; + buffer[j * 2 + 1] += sample; + } + } + } + } +} + +/* --------------------------------------------------------------------------- +// Main OPNA output routine. See FMMix(), RhythmMix() above and PSGMix() +// in psg.c for details. +*/ +void OPNAMix(OPNA *opna, int16_t *buf, uint32_t nframes) +{ + int32_t buffer[16384]; + unsigned int i, clips = 0; + for (i = 0; i < 2 * nframes; i++) buffer[i] = 0; + if(opna->devmask & 1) FMMix(opna, buffer, nframes); + if(opna->devmask & 2) PSGMix(&opna->psg, buffer, nframes); + if(opna->devmask & 4) RhythmMix(opna, buffer, nframes); + for (i = 0; i < 2 * nframes; i++) { + int32_t k = (buffer[i] >> 2); + if (k > 32767 || k < -32767) clips++; + buf[i] = Limit16(k); + } + if (clips) message("clipped %u samples\n", clips); +} diff --git a/thirdparty/opnmidi/chips/pmdwin/opna.h b/thirdparty/opnmidi/chips/pmdwin/opna.h new file mode 100644 index 000000000..81dc51d75 --- /dev/null +++ b/thirdparty/opnmidi/chips/pmdwin/opna.h @@ -0,0 +1,91 @@ +#ifndef __OPNA_H__ +#define __OPNA_H__ + +#include +#include "op.h" +#include "psg.h" + +/* YM2608 (OPNA) ------------------------------------------------------ */ +typedef struct _OPNA +{ + int fmvolume; + uint32_t clock; + uint32_t rate; + uint32_t psgrate; + uint32_t status; + Channel4 ch[6]; + Channel4* csmch; + + int32_t mixdelta; + int mpratio; + uint8_t interpolation; + + uint8_t timer_status; + uint8_t regtc; + uint8_t regta[2]; + + int32_t timera, timera_count; + int32_t timerb, timerb_count; + int32_t timer_step; + uint8_t prescale; + uint8_t devmask; + + PSG psg; + + Rhythm rhythm[6]; + int8_t rhythmtl; + int rhythmtvol; + uint8_t rhythmkey; + + int32_t mixl, mixl1; + int32_t mixr, mixr1; + uint8_t fnum2[9]; + + uint8_t reg22; + uint32_t reg29; + uint32_t statusnext; + + uint32_t lfocount; + uint32_t lfodcount; + uint32_t lfotab[8]; + + uint32_t fnum[6]; + uint32_t fnum3[3]; + + uint8_t aml; + + uint32_t currentratio; + float rr; + uint32_t ratetable[64]; +} OPNA; + +#ifdef __cplusplus +extern "C" { +#endif + +/* --------------------------------------------------------------------------- */ +uint8_t OPNAInit(OPNA *opna, uint32_t c, uint32_t r, uint8_t ipflag); +void OPNAReset(OPNA *opna); +void OPNASetVolumeRhythm(OPNA *opna, int index, int db); +uint8_t OPNASetRate(OPNA *opna, uint32_t r, uint8_t ipflag); +void OPNASetChannelMask(OPNA *opna, uint32_t mask); +void OPNASetReg(OPNA *opna, uint32_t addr, uint32_t data); +void OPNASetPan(OPNA *opna, uint32_t chan, uint32_t data); +uint8_t OPNATimerCount(OPNA *opna, int32_t us); +void OPNAMix(OPNA *opna, int16_t *buffer, uint32_t nframes); + +/* --------------------------------------------------------------------------- */ +static inline uint32_t OPNAReadStatus(OPNA *opna) { return opna->status & 0x03; } + +static inline int32_t OPNAGetNextEvent(OPNA *opna) +{ + uint32_t ta = ((opna->timera_count + 0xffff) >> 16) - 1; + uint32_t tb = ((opna->timerb_count + 0xfff) >> 12) - 1; + return (ta < tb ? ta : tb) + 1; +} + +#ifdef __cplusplus +} +#endif + +#endif /* FM_OPNA_H */ diff --git a/thirdparty/opnmidi/chips/pmdwin/psg.c b/thirdparty/opnmidi/chips/pmdwin/psg.c new file mode 100644 index 000000000..25c55ae40 --- /dev/null +++ b/thirdparty/opnmidi/chips/pmdwin/psg.c @@ -0,0 +1,342 @@ +/* FIXME: move ugly-ass legalese somewhere where it won't be seen +// by anyone other than lawyers. (/dev/null would be ideal but sadly +// we live in an imperfect world). */ +/* Copyright (c) 2012/2013, Peter Barfuss +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. */ + +/* Quick, somewhat hacky PSG implementation. Seems to work fine in most cases. +// Known bugs: volume *may* still be off for a lot of samples. Importantly, +// waveform volume is too quiet but setting it at the correct volume makes +// the noise volume too loud and vice-versa. I *think* what I have currently +// is mostly correct (I'm basing this mostly on how good Strawberry Crisis +// sounds with the given settings), but it's possible that more fine-tuning +// is needed. Apart from that, this is probably the sketchiest part of all +// of my emulator code, but then again there's a bit-exact VHDL core of +// the YM2149F/AY-3-8910, so while I do want to make this as good +// as the code in opna.c, it's the lowest-priority of all of the code here. +// --bofh */ +#include +#include +#include +#include +#include "op.h" +#include "psg.h" + +/* --------------------------------------------------------------------------- +// ???? +*/ +int EmitTable[0x20] = { -1, }; +uint32_t enveloptable[16][64] = { { 0, }, }; + +/* --------------------------------------------------------------------------- +// PSG reset to power-on defaults +*/ +void PSGReset(PSG *psg) +{ + int i; + for (i=0; i<14; i++) + PSGSetReg(psg, i, 0); + PSGSetReg(psg, 7, 0xff); + PSGSetReg(psg, 14, 0xff); + PSGSetReg(psg, 15, 0xff); +} + +/* --------------------------------------------------------------------------- +// This code is strongly inspired by some random PSG emulator code I found, +// and is probably not the optimal way to define periods. It *is* at least +// among the fastest, given that it uses the hilarious hack of using the +// integer overflow on a 32-bit unsigned integer to compute ""moduli"". +*/ +void PSGSetClock(PSG *psg, uint32_t clock, uint32_t rate) +{ + int tmp; + psg->tperiodbase = (uint32_t)((1 << toneshift ) / 4.0f * clock / rate); + psg->eperiodbase = (uint32_t)((1 << envshift ) / 4.0f * clock / rate); + + tmp = ((psg->reg[0] + psg->reg[1] * 256) & 0xfff); + psg->speriod[0] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase; + tmp = ((psg->reg[2] + psg->reg[3] * 256) & 0xfff); + psg->speriod[1] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase; + tmp = ((psg->reg[4] + psg->reg[5] * 256) & 0xfff); + psg->speriod[2] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase; + tmp = psg->reg[6] & 0x1f; + psg->nperiod = tmp; + tmp = ((psg->reg[11] + psg->reg[12] * 256) & 0xffff); + psg->eperiod = tmp ? psg->eperiodbase / tmp : psg->eperiodbase * 2; +} + +/* --------------------------------------------------------------------------- +// ???????????? +*/ +static uint8_t table3[4] = { 0, 1, -1, 0 }; +void MakeEnvelopTable(void) +{ + /* 0 lo 1 up 2 down 3 hi */ + static uint8_t table1[16*2] = + { + 2,0, 2,0, 2,0, 2,0, 1,0, 1,0, 1,0, 1,0, + 2,2, 2,0, 2,1, 2,3, 1,1, 1,3, 1,2, 1,0, + }; + int i, j; + + if (!enveloptable[0][0]) { + uint32_t *ptr = enveloptable[0]; + for (i=0; i<16*2; i++) { + uint8_t v = ((table1[i] & 0x2) ? 31 : 0); + for (j=0; j<32; j++) { + *ptr++ = EmitTable[v]; + v += table3[table1[i]]; + } + } + } +} + +/* --------------------------------------------------------------------------- +// Sets the channel output mask for the PSG device. +// c is a bitvector where the 3 LSBs are set to 0 to disable a given +// PSG channel and 1 to enable it. +// TODO: Possibly allow enabling tone/noise output for each channel independently? +*/ +void PSGSetChannelMask(PSG *psg, int c) +{ + int i; + psg->mask = c; + for (i=0; i<3; i++) + psg->olevel[i] = psg->mask & (1 << i) ? EmitTable[(psg->reg[8+i] & 15) * 2 + 1] : 0; +} + +/* --------------------------------------------------------------------------- +// PSG register set routine. Mostly just what you'd expect from reading the manual. +// Fairly boring code overall. regnum can be 0 - 15, data can be 0x00 - 0xFF. +// (This should not be surprising - the YM2149F *did* use an 8-bit bus, after all). +// Interesting quirk: the task of register 7 (channel enable/disable) is basically +// entirely duplicated by other registers, to the point where you can basically +// just ignore any writes to register 7 entirely. I save it here in case some +// braindead routine wants to read its value and do something based on that +// (Another curiosity: register 7 on the PSG appears to be the only register +// between *both* the OPNA and the PSG which is actually *read from* by +// pmdwin.cpp and not just written to. Amusingly enough, the only reason +// that it is ever read is so that it can then OR something with what it just read +// and then write that back to register 7. Hilarity). +// HACK ALERT: The output levels for channels 0 and 1 are increased by a factor of 4 +// to make them match the actual chip in loudness, but without causing the noise channel +// to overtake everything in intensity. This is almost certainly wrong, and moreover +// it assumes that channel 2 will be playing back Speak Board effects which usually means +// drum kit only (for the most part, at least), and not being used as a separate tonal +// channel in its own right. To the best of my knowledge, this does hold for all of ZUN's +// songs, however, once you step outside that set of music, it's trivial to find +// all sorts of counterexamples to that assumption. Therefore, this should be fixed ASAP. +*/ +void PSGSetReg(PSG *psg, uint8_t regnum, uint8_t data) +{ + if (regnum < 0x10) + { + psg->reg[regnum] = data; + switch (regnum) + { + int tmp; + + case 0: /* ChA Fine Tune */ + case 1: /* ChA Coarse Tune */ + tmp = ((psg->reg[0] + psg->reg[1] * 256) & 0xfff); + psg->speriod[0] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase; + break; + + case 2: /* ChB Fine Tune */ + case 3: /* ChB Coarse Tune */ + tmp = ((psg->reg[2] + psg->reg[3] * 256) & 0xfff); + psg->speriod[1] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase; + break; + + case 4: /* ChC Fine Tune */ + case 5: /* ChC Coarse Tune */ + tmp = ((psg->reg[4] + psg->reg[5] * 256) & 0xfff); + psg->speriod[2] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase; + break; + + case 6: /* Noise generator control */ + data &= 0x1f; + psg->nperiod = data; + break; + + case 8: + psg->olevel[0] = psg->mask & 1 ? EmitTable[(data & 15) * 2 + 1] : 0; + break; + + case 9: + psg->olevel[1] = psg->mask & 2 ? EmitTable[(data & 15) * 2 + 1] : 0; + break; + + case 10: + psg->olevel[2] = psg->mask & 4 ? EmitTable[(data & 15) * 2 + 1] : 0; + break; + + case 11: /* Envelope period */ + case 12: + tmp = ((psg->reg[11] + psg->reg[12] * 256) & 0xffff); + psg->eperiod = tmp ? psg->eperiodbase / tmp : psg->eperiodbase * 2; + break; + + case 13: /* Envelope shape */ + psg->ecount = 0; + psg->envelop = enveloptable[data & 15]; + break; + } + } +} + +/* --------------------------------------------------------------------------- +// Init code. Set volume to 0, reset the chip, enable all channels, seed the RNG. +// RNG seed lifted from MAME's YM2149F emulation routine, appears to be correct. +*/ +void PSGInit(PSG *psg) +{ + int i; + float base = 0x4000 / 3.0f; + for (i=31; i>=2; i--) + { + EmitTable[i] = lrintf(base); + base *= 0.840896415f; /* 1.0f / 1.189207115f */ + } + EmitTable[1] = 0; + EmitTable[0] = 0; + MakeEnvelopTable(); + + PSGSetChannelMask(psg, psg->mask); + psg->rng = 14231; + psg->ncount = 0; + PSGReset(psg); + psg->mask = 0x3f; +} + +/* --------------------------------------------------------------------------- +// The main output routine for the PSG emulation. +// dest should be an array of size nsamples, and of type Sample +// (one of int16_t, int32_t or float - any will work here without causing +// clipping/precision problems). +// Everything is implemented using some form of fixed-point arithmetic +// that currently needs no more than 32-bits for its implementation, +// but I'm pretty certain that you can get by with much less than that +// and still have mostly correct-to-fully-correct emulation. +// +// TODO: In the future, test the veracity of the above statement. Moreover, +// if it turns out to be correct, rewrite this routine to not use more than +// the required precision. This is irrelevant for any PC newer than, well, +// a 386DX/68040, but important for efficient hardware implementation. +*/ +void PSGMix(PSG *psg, int32_t *dest, uint32_t nsamples) +{ + uint8_t chenable[3]; + uint8_t r7 = ~psg->reg[7]; + unsigned int i, k = 0; + int x, y, z; + + if ((r7 & 0x3f) | ((psg->reg[8] | psg->reg[9] | psg->reg[10]) & 0x1f)) { + int noise, sample; + uint32_t env; + uint32_t* p1; + uint32_t* p2; + uint32_t* p3; + + chenable[0] = (r7 & 0x01) && (psg->speriod[0] <= (1 << toneshift)); + chenable[1] = (r7 & 0x02) && (psg->speriod[1] <= (1 << toneshift)); + chenable[2] = (r7 & 0x04) && (psg->speriod[2] <= (1 << toneshift)); + + p1 = ((psg->mask & 1) && (psg->reg[ 8] & 0x10)) ? &env : &psg->olevel[0]; + p2 = ((psg->mask & 2) && (psg->reg[ 9] & 0x10)) ? &env : &psg->olevel[1]; + p3 = ((psg->mask & 4) && (psg->reg[10] & 0x10)) ? &env : &psg->olevel[2]; + #define SCOUNT(ch) (psg->scount[ch] >> toneshift) + + if (p1 != &env && p2 != &env && p3 != &env) { + for (i=0; incount++; + if(psg->ncount >= psg->nperiod) { + if(psg->rng & 1) + psg->rng ^= 0x24000; + psg->rng >>= 1; + psg->ncount = 0; + } + noise = (psg->rng & 1); + sample = 0; + { + x = ((SCOUNT(0) & chenable[0]) | ((r7 >> 3) & noise)) - 1; /* 0 or -1 */ + sample += (psg->olevel[0] + x) ^ x; + psg->scount[0] += psg->speriod[0]; + y = ((SCOUNT(1) & chenable[1]) | ((r7 >> 4) & noise)) - 1; + sample += (psg->olevel[1] + y) ^ y; + psg->scount[1] += psg->speriod[1]; + /*z = ((SCOUNT(2) & chenable[2]) | ((r7 >> 5) & noise)) - 1;*/ + z = ((r7 >> 5) & noise) - 1; + sample += (psg->olevel[2] + z) ^ z; + psg->scount[2] += psg->speriod[2]; + } + sample = Limit16(sample); + dest[k++] += sample; + dest[k++] += sample; + } + + psg->ecount = (psg->ecount >> 8) + (psg->eperiod >> 8) * nsamples; + if (psg->ecount >= (1 << (envshift+6-8))) { + if ((psg->reg[0x0d] & 0x0b) != 0x0a) + psg->ecount |= (1 << (envshift+5-8)); + psg->ecount &= (1 << (envshift+6-8)) - 1; + } + psg->ecount <<= 8; + } else { + for (i=0; incount++; + if(psg->ncount >= psg->nperiod) { + if(psg->rng & 1) + psg->rng ^= 0x24000; + psg->rng >>= 1; + psg->ncount = 0; + } + noise = (psg->rng & 1); + sample = 0; + { + env = psg->envelop[psg->ecount >> envshift]; + psg->ecount += psg->eperiod; + if (psg->ecount >= (1 << (envshift+6))) { + if ((psg->reg[0x0d] & 0x0b) != 0x0a) + psg->ecount |= (1 << (envshift+5)); + psg->ecount &= (1 << (envshift+6)) - 1; + } + x = ((SCOUNT(0) & chenable[0]) | ((r7 >> 3) & noise)) - 1; /* 0 or -1 */ + sample += (*p1 + x) ^ x; + psg->scount[0] += psg->speriod[0]; + y = ((SCOUNT(1) & chenable[1]) | ((r7 >> 4) & noise)) - 1; + sample += (*p2 + y) ^ y; + psg->scount[1] += psg->speriod[1]; + /*z = ((SCOUNT(2) & chenable[2]) | ((r7 >> 5) & noise)) - 1;*/ + z = ((r7 >> 5) & noise) - 1; + sample += (*p3 + z) ^ z; + psg->scount[2] += psg->speriod[2]; + } + sample = Limit16(sample); + dest[k++] += sample; + dest[k++] += sample; + } + } + } +} + diff --git a/thirdparty/opnmidi/chips/pmdwin/psg.h b/thirdparty/opnmidi/chips/pmdwin/psg.h new file mode 100644 index 000000000..9f6f1c9f9 --- /dev/null +++ b/thirdparty/opnmidi/chips/pmdwin/psg.h @@ -0,0 +1,52 @@ +#ifndef __PSG_H__ +#define __PSG_H__ + +#include +#define PSG_SAMPLETYPE int32_t /* any of int16_t, int32_t or float will work here. */ + +/* Constants for the shift amounts used in the counters. +*/ +enum { + toneshift = 24, + envshift = 22 +}; + +typedef struct _PSG { + uint8_t reg[16]; + + const uint32_t *envelop; + uint32_t rng; + uint32_t olevel[3]; + uint32_t scount[3], speriod[3]; + uint32_t ecount, eperiod; + uint32_t ncount, nperiod; + uint32_t tperiodbase; + uint32_t eperiodbase; + int volume; + int mask; +} PSG; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Mostly self-explanatory. +// Actual descriptions of each function can be found in psg.c +// Also, PSGGetReg() is basically useless. +// (More info on that can *also* be found in psg.c). */ +void PSGInit(PSG *psg); +void PSGReset(PSG *psg); +void PSGSetClock(PSG *psg, uint32_t clock, uint32_t rate); +void PSGSetChannelMask(PSG *psg, int c); +void PSGSetReg(PSG *psg, uint8_t regnum, uint8_t data); +void PSGMix(PSG *psg, int32_t *dest, uint32_t nsamples); + +static inline uint32_t PSGGetReg(PSG *psg, uint8_t regnum) { + return psg->reg[regnum & 0x0f]; +} + +#ifdef __cplusplus +} +#endif + +#endif /* PSG_H */ diff --git a/thirdparty/opnmidi/chips/pmdwin/rhythmdata.c b/thirdparty/opnmidi/chips/pmdwin/rhythmdata.c new file mode 100644 index 000000000..2d00327d7 --- /dev/null +++ b/thirdparty/opnmidi/chips/pmdwin/rhythmdata.c @@ -0,0 +1,5 @@ +#include "rhythmdata.h" + +const unsigned char* rhythmdata[6] = { + BD_2608, SD_2608, TOP_2608, HH_2608, TOM_2608, RIM_2608 +}; diff --git a/thirdparty/opnmidi/chips/pmdwin/rhythmdata.h b/thirdparty/opnmidi/chips/pmdwin/rhythmdata.h new file mode 100644 index 000000000..e3a7e365d --- /dev/null +++ b/thirdparty/opnmidi/chips/pmdwin/rhythmdata.h @@ -0,0 +1,5254 @@ +static const unsigned char BD_2608[9013] = { + 0x52, 0x49, 0x46, 0x46, 0x36, 0x46, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x44, 0xac, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x02, 0x00, 0x10, 0x00, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x61, 0x61, 0x46, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x01, 0xff, 0xff, 0xff, 0x02, 0x05, 0x05, 0x06, 0x09, 0x10, 0x19, 0x1c, 0x18, + 0x09, 0xef, 0xd2, 0xc5, 0xdd, 0xff, 0x1b, 0x27, 0x1e, 0x0d, 0x02, 0x00, 0xf1, 0xd3, 0xb9, 0xbe, 0xd8, 0x01, 0x2a, 0x3c, 0x39, 0x1b, + 0xe4, 0xbf, 0xb9, 0xcd, 0xed, 0x0f, 0x29, 0x32, 0x2c, 0x1a, 0x0c, 0x10, 0x15, 0x0e, 0xfa, 0xe2, 0xce, 0xc0, 0xb8, 0xb5, 0xbb, 0xcd, + 0xeb, 0x07, 0x14, 0x10, 0x00, 0xe9, 0xd7, 0xd1, 0xd3, 0xdb, 0xe2, 0xd9, 0xcc, 0xc3, 0xcc, 0xeb, 0x11, 0x12, 0xde, 0xb1, 0xa6, 0xb6, + 0xbd, 0xc9, 0xd6, 0xe0, 0xde, 0xd3, 0xbf, 0xaa, 0xa1, 0xbf, 0xf3, 0x0e, 0x08, 0xe8, 0xc4, 0xae, 0xab, 0xb2, 0xb6, 0xb9, 0xb3, 0xa9, + 0xa7, 0xa9, 0xab, 0xa9, 0xaa, 0xb4, 0xc2, 0xc3, 0xbd, 0xba, 0xc2, 0xc8, 0xcb, 0xc1, 0xac, 0x9d, 0x9e, 0xa9, 0xb2, 0xbe, 0xd0, 0xe4, + 0xf0, 0xf5, 0xef, 0xdf, 0xcb, 0xbb, 0xb3, 0xb2, 0xb5, 0xbf, 0xcd, 0xda, 0xd4, 0xc0, 0xb5, 0xae, 0xae, 0xb2, 0xb6, 0xbc, 0xc1, 0xc5, + 0xc8, 0xd0, 0xe2, 0xf6, 0x0a, 0x0c, 0xfc, 0xed, 0xdf, 0xd9, 0xda, 0xe1, 0xdf, 0xd3, 0xca, 0xc6, 0xd2, 0xee, 0x0a, 0x12, 0x0a, 0xfc, + 0xf3, 0xec, 0xe5, 0xe4, 0xf2, 0x04, 0x0a, 0x06, 0xfd, 0xfa, 0x02, 0x11, 0x1b, 0x1b, 0x15, 0x0c, 0x04, 0x00, 0x00, 0x0a, 0x1b, 0x27, + 0x2a, 0x28, 0x27, 0x2d, 0x34, 0x3a, 0x3e, 0x42, 0x3c, 0x2f, 0x26, 0x24, 0x29, 0x2f, 0x35, 0x38, 0x39, 0x38, 0x39, 0x3a, 0x3b, 0x3d, + 0x3e, 0x3e, 0x40, 0x43, 0x48, 0x4d, 0x50, 0x4e, 0x4e, 0x4f, 0x50, 0x4f, 0x50, 0x58, 0x64, 0x6b, 0x69, 0x64, 0x60, 0x5f, 0x61, 0x65, + 0x65, 0x63, 0x61, 0x5e, 0x61, 0x67, 0x6d, 0x6d, 0x69, 0x65, 0x65, 0x66, 0x65, 0x61, 0x5d, 0x5e, 0x63, 0x68, 0x68, 0x65, 0x61, 0x5f, + 0x60, 0x61, 0x62, 0x64, 0x65, 0x63, 0x5f, 0x5b, 0x5a, 0x5d, 0x62, 0x67, 0x6a, 0x6b, 0x6b, 0x6a, 0x64, 0x5d, 0x51, 0x47, 0x44, 0x48, + 0x53, 0x60, 0x6b, 0x6c, 0x66, 0x5a, 0x4a, 0x41, 0x40, 0x44, 0x49, 0x4e, 0x51, 0x53, 0x51, 0x52, 0x54, 0x51, 0x4b, 0x44, 0x40, 0x43, + 0x46, 0x46, 0x42, 0x3e, 0x3b, 0x39, 0x39, 0x37, 0x34, 0x32, 0x32, 0x34, 0x36, 0x37, 0x35, 0x30, 0x2b, 0x29, 0x24, 0x1f, 0x1a, 0x18, + 0x18, 0x1a, 0x1e, 0x22, 0x26, 0x28, 0x28, 0x25, 0x1f, 0x18, 0x0d, 0x03, 0xfa, 0xf3, 0xf2, 0xf8, 0x01, 0x0a, 0x0e, 0x0d, 0x08, 0x04, + 0xff, 0xf8, 0xf5, 0xf2, 0xf0, 0xee, 0xeb, 0xe8, 0xe5, 0xe4, 0xe4, 0xe6, 0xe7, 0xe4, 0xe1, 0xde, 0xdd, 0xdb, 0xd8, 0xd7, 0xd6, 0xd6, + 0xd4, 0xd1, 0xce, 0xcc, 0xc9, 0xc7, 0xc5, 0xc5, 0xc5, 0xc3, 0xc1, 0xc0, 0xbe, 0xba, 0xb7, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb3, 0xb4, + 0xb3, 0xb2, 0xb0, 0xac, 0xa9, 0xa6, 0xa4, 0xa0, 0x9e, 0x9f, 0xa2, 0xa5, 0xa6, 0xa4, 0xa0, 0x9c, 0x9b, 0x9b, 0x9c, 0x9a, 0x96, 0x92, + 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x91, 0x92, 0x91, 0x8f, 0x8d, 0x8c, 0x8b, 0x8a, 0x8b, 0x8c, 0x8e, 0x8d, 0x8b, 0x88, 0x85, 0x84, 0x84, + 0x83, 0x82, 0x83, 0x85, 0x87, 0x88, 0x88, 0x87, 0x84, 0x82, 0x81, 0x81, 0x80, 0x80, 0x80, 0x81, 0x81, 0x82, 0x82, 0x83, 0x84, 0x86, + 0x87, 0x86, 0x83, 0x82, 0x81, 0x80, 0x80, 0x80, 0x80, 0x82, 0x84, 0x85, 0x86, 0x86, 0x85, 0x86, 0x85, 0x84, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x84, 0x86, 0x88, 0x89, 0x89, 0x88, 0x86, 0x85, 0x85, 0x85, 0x86, 0x88, 0x89, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8b, + 0x8c, 0x8e, 0x8f, 0x8f, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, 0x90, 0x91, 0x92, 0x94, 0x96, 0x99, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0xa0, 0xa3, 0xa6, 0xa9, 0xac, 0xaf, 0xb2, 0xb3, 0xb2, 0xb0, 0xac, 0xa9, 0xa8, 0xa9, 0xac, 0xb1, + 0xb7, 0xbe, 0xc4, 0xc8, 0xc9, 0xc6, 0xc2, 0xbe, 0xbb, 0xbb, 0xbc, 0xbd, 0xbf, 0xc1, 0xc5, 0xc8, 0xcc, 0xd0, 0xd2, 0xd2, 0xd3, 0xd4, + 0xd6, 0xd8, 0xda, 0xdb, 0xdc, 0xdb, 0xdc, 0xdd, 0xde, 0xe1, 0xe4, 0xe7, 0xe9, 0xe9, 0xe9, 0xe8, 0xea, 0xed, 0xf1, 0xf3, 0xf4, 0xf3, + 0xf4, 0xf6, 0xf9, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x01, 0x03, 0x05, 0x07, 0x0a, 0x0b, 0x0b, 0x0b, 0x0c, 0x0d, 0x0f, 0x10, 0x11, 0x11, + 0x12, 0x13, 0x15, 0x17, 0x1a, 0x1c, 0x1e, 0x20, 0x23, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2b, 0x29, 0x28, 0x29, 0x2a, 0x2e, 0x31, + 0x34, 0x34, 0x35, 0x36, 0x37, 0x37, 0x38, 0x39, 0x3a, 0x3c, 0x3e, 0x41, 0x44, 0x47, 0x48, 0x47, 0x46, 0x45, 0x45, 0x45, 0x46, 0x48, + 0x4a, 0x4b, 0x4a, 0x49, 0x49, 0x4a, 0x4c, 0x4d, 0x50, 0x54, 0x58, 0x5a, 0x5a, 0x59, 0x57, 0x56, 0x56, 0x56, 0x56, 0x57, 0x58, 0x58, + 0x57, 0x56, 0x58, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x60, 0x61, 0x62, 0x62, 0x61, 0x5f, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5e, 0x5e, 0x5f, + 0x61, 0x63, 0x64, 0x65, 0x65, 0x65, 0x64, 0x64, 0x63, 0x61, 0x60, 0x5f, 0x60, 0x60, 0x60, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x66, 0x66, 0x66, 0x67, 0x66, 0x66, 0x65, 0x65, 0x65, 0x66, 0x66, 0x65, 0x63, 0x61, 0x61, 0x62, 0x63, 0x64, 0x63, 0x62, 0x62, 0x63, + 0x64, 0x64, 0x64, 0x63, 0x61, 0x60, 0x5f, 0x5e, 0x5e, 0x5e, 0x5f, 0x60, 0x62, 0x62, 0x62, 0x60, 0x5f, 0x5d, 0x5c, 0x5d, 0x5e, 0x5e, + 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x5f, 0x5d, 0x5b, 0x5a, 0x58, 0x57, 0x56, 0x56, 0x57, 0x58, 0x59, 0x5b, 0x5b, + 0x5b, 0x5b, 0x59, 0x58, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x59, 0x57, 0x55, 0x54, 0x53, 0x53, 0x54, 0x55, 0x56, 0x56, 0x57, 0x56, + 0x55, 0x54, 0x54, 0x54, 0x53, 0x53, 0x52, 0x51, 0x51, 0x51, 0x53, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x53, 0x51, 0x50, 0x4f, + 0x4d, 0x4d, 0x4e, 0x4e, 0x4f, 0x50, 0x50, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x51, 0x50, + 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x51, 0x52, 0x52, 0x53, 0x53, 0x51, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x52, 0x53, 0x53, + 0x53, 0x52, 0x51, 0x50, 0x4f, 0x4e, 0x4f, 0x50, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x52, 0x51, 0x51, 0x52, + 0x51, 0x51, 0x50, 0x4f, 0x4f, 0x4f, 0x50, 0x51, 0x51, 0x51, 0x51, 0x50, 0x4f, 0x4f, 0x4e, 0x4f, 0x4f, 0x4f, 0x4e, 0x4d, 0x4d, 0x4d, + 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4d, 0x4c, 0x4b, 0x49, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x48, 0x48, 0x48, 0x49, 0x49, + 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x43, 0x43, 0x44, 0x44, 0x43, 0x42, 0x41, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x40, 0x3f, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x37, 0x35, 0x34, + 0x34, 0x33, 0x34, 0x33, 0x33, 0x32, 0x30, 0x2f, 0x2e, 0x2e, 0x2d, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x29, 0x28, 0x27, + 0x26, 0x25, 0x24, 0x23, 0x23, 0x22, 0x22, 0x21, 0x21, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, + 0x16, 0x16, 0x16, 0x16, 0x14, 0x12, 0x10, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x0f, 0x0e, 0x0c, 0x0a, 0x09, 0x08, 0x07, 0x05, 0x04, 0x04, + 0x05, 0x05, 0x04, 0x02, 0x00, 0xfe, 0xfd, 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xf9, 0xf4, 0xf1, 0xef, 0xef, 0xee, 0xee, + 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xea, 0xe8, 0xe6, 0xe4, 0xe3, 0xe1, 0xdf, 0xde, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdb, 0xdb, + 0xda, 0xd9, 0xd7, 0xd4, 0xd1, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xcd, 0xcd, 0xcc, 0xcc, 0xca, 0xc8, 0xc5, 0xc2, 0xc0, 0xbf, + 0xbe, 0xbf, 0xc0, 0xbf, 0xbe, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xba, 0xb8, 0xb6, 0xb5, 0xb3, 0xb2, 0xb1, 0xb0, 0xaf, 0xae, 0xae, 0xae, + 0xaf, 0xae, 0xad, 0xac, 0xaa, 0xa9, 0xa7, 0xa6, 0xa5, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0, 0x9e, 0x9c, + 0x9b, 0x9b, 0x9b, 0x9c, 0x9e, 0x9f, 0x9f, 0x9e, 0x9d, 0x9b, 0x99, 0x97, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x94, 0x93, 0x92, 0x92, 0x92, 0x92, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8c, 0x8b, 0x8a, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8e, 0x8d, 0x8d, 0x8d, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x91, 0x92, 0x92, 0x92, 0x91, 0x91, 0x92, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9a, + 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9, + 0xaa, 0xab, 0xac, 0xad, 0xaf, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb9, 0xba, 0xbc, 0xbf, 0xc1, 0xc3, + 0xc4, 0xc5, 0xc5, 0xc6, 0xc7, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xce, 0xcf, 0xd0, 0xd1, 0xd3, 0xd4, 0xd6, 0xd8, 0xda, 0xdb, 0xdc, + 0xdd, 0xde, 0xe0, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe6, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf5, 0xf7, 0xf8, 0xf9, + 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xff, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, 0x09, 0x0b, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x12, 0x13, + 0x15, 0x16, 0x18, 0x1a, 0x1b, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28, 0x29, 0x29, 0x29, 0x2a, 0x2c, + 0x2d, 0x2f, 0x31, 0x33, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38, 0x3a, 0x3b, 0x3c, 0x3d, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x43, + 0x44, 0x45, 0x45, 0x46, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54, 0x54, + 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x5f, 0x5f, 0x5e, 0x5e, 0x5e, + 0x5f, 0x5f, 0x60, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x64, 0x64, 0x64, 0x64, 0x64, + 0x65, 0x65, 0x66, 0x67, 0x68, 0x68, 0x69, 0x68, 0x68, 0x68, 0x67, 0x66, 0x66, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, + 0x67, 0x67, 0x66, 0x66, 0x65, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x64, 0x64, 0x64, 0x63, 0x63, 0x63, 0x62, 0x61, 0x61, + 0x60, 0x60, 0x60, 0x61, 0x61, 0x60, 0x60, 0x5f, 0x5f, 0x5e, 0x5e, 0x5d, 0x5c, 0x5c, 0x5c, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5a, 0x59, + 0x59, 0x58, 0x57, 0x57, 0x56, 0x55, 0x54, 0x54, 0x53, 0x54, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x4f, 0x4f, 0x4f, 0x4e, 0x4e, 0x4d, + 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x47, 0x47, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x41, 0x41, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, + 0x3e, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x37, 0x37, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x31, 0x30, 0x2f, 0x2e, 0x2d, + 0x2d, 0x2c, 0x2a, 0x29, 0x29, 0x29, 0x29, 0x29, 0x28, 0x28, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x21, 0x22, 0x23, 0x23, 0x22, 0x21, + 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1a, 0x19, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16, 0x14, + 0x13, 0x12, 0x11, 0x10, 0x11, 0x11, 0x12, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, + 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x03, 0x04, + 0x05, 0x06, 0x06, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x08, 0x09, 0x08, 0x08, 0x07, 0x07, 0x08, 0x08, 0x09, 0x0a, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0d, 0x0f, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x10, 0x10, 0x11, + 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x17, 0x17, 0x16, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19, 0x19, 0x1b, 0x1b, 0x1c, 0x1c, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1c, 0x1e, 0x1e, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1d, 0x1e, 0x20, 0x22, 0x23, 0x23, 0x23, 0x23, 0x23, 0x22, 0x22, + 0x22, 0x22, 0x24, 0x24, 0x25, 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x28, 0x29, 0x2a, + 0x2b, 0x2b, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x29, 0x28, 0x28, 0x29, 0x2a, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2c, 0x2c, 0x2d, 0x2d, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2d, 0x2d, 0x2c, 0x2b, 0x2b, + 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2a, 0x2a, 0x29, 0x2a, 0x2a, 0x29, 0x29, 0x28, 0x28, 0x27, + 0x26, 0x26, 0x26, 0x26, 0x25, 0x25, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23, 0x23, 0x22, 0x21, 0x20, 0x20, 0x21, 0x20, 0x20, 0x1f, + 0x1f, 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1b, 0x1a, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16, 0x15, 0x13, 0x12, 0x11, 0x10, + 0x10, 0x10, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x06, 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x05, 0x05, 0x04, 0x03, + 0x02, 0x00, 0xff, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfa, 0xf8, 0xf7, 0xf5, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf1, 0xf1, 0xf0, 0xef, + 0xed, 0xec, 0xec, 0xeb, 0xea, 0xe9, 0xe9, 0xe8, 0xe8, 0xe6, 0xe4, 0xe2, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xdf, 0xde, 0xdd, 0xdb, 0xdb, + 0xda, 0xd9, 0xd8, 0xd7, 0xd7, 0xd6, 0xd5, 0xd3, 0xd1, 0xcf, 0xce, 0xcd, 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcb, 0xcb, 0xca, 0xc9, 0xc8, + 0xc7, 0xc6, 0xc5, 0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd, 0xbc, 0xba, 0xb9, 0xb8, 0xb8, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb1, 0xb1, 0xb0, 0xb0, 0xaf, 0xae, 0xad, 0xad, 0xac, 0xac, 0xac, + 0xac, 0xab, 0xab, 0xaa, 0xa9, 0xa9, 0xa8, 0xa7, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa6, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1, 0xa1, 0xa0, 0xa0, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa1, 0xa1, 0xa1, 0xa0, 0x9f, 0x9f, + 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, + 0xa1, 0xa1, 0xa2, 0xa1, 0xa1, 0xa0, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa6, + 0xa7, 0xa8, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xaa, 0xaa, 0xaa, 0xab, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf, 0xb0, + 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbb, + 0xbb, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc3, 0xc4, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xc9, 0xca, + 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xcf, 0xd0, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd4, 0xd6, 0xd7, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, + 0xda, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xdf, 0xe0, 0xe2, 0xe3, 0xe3, 0xe4, 0xe5, 0xe5, 0xe6, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, + 0xeb, 0xec, 0xec, 0xed, 0xee, 0xef, 0xef, 0xf0, 0xf1, 0xf2, 0xf2, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, 0xf6, 0xf7, 0xf7, 0xf8, 0xf9, + 0xfa, 0xfb, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x05, 0x06, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0b, 0x0b, 0x0b, 0x0c, 0x0d, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x10, 0x11, 0x10, 0x10, 0x11, 0x12, 0x13, + 0x13, 0x14, 0x15, 0x15, 0x16, 0x16, 0x17, 0x18, 0x18, 0x18, 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x23, + 0x23, 0x23, 0x23, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x26, 0x26, 0x27, 0x27, 0x27, + 0x27, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x27, 0x27, 0x26, 0x27, 0x27, 0x27, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x25, 0x24, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x23, 0x23, 0x22, + 0x22, 0x21, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1f, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1d, + 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x17, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x16, 0x15, + 0x14, 0x13, 0x12, 0x12, 0x12, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, + 0x0b, 0x0a, 0x09, 0x09, 0x08, 0x07, 0x06, 0x05, 0x05, 0x05, 0x05, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x01, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf5, 0xf5, 0xf4, 0xf3, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf1, 0xf0, 0xef, 0xee, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xec, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe8, 0xe7, 0xe6, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe4, 0xe4, 0xe4, 0xe3, 0xe2, 0xe0, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xde, 0xde, 0xdd, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdc, + 0xdc, 0xdb, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xd9, 0xd9, 0xd8, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4, 0xd3, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd3, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd2, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd1, + 0xd1, 0xd1, 0xd2, 0xd3, 0xd3, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd5, 0xd5, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdc, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdc, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xe0, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xec, 0xed, + 0xee, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf7, 0xf7, 0xf8, 0xf9, + 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0e, 0x0f, 0x10, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x13, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, + 0x1f, 0x20, 0x21, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x32, 0x32, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x32, + 0x32, 0x32, 0x33, 0x33, 0x34, 0x34, 0x34, 0x34, 0x33, 0x32, 0x32, 0x31, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33, 0x33, 0x32, 0x31, 0x31, + 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2d, 0x2d, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, 0x2a, 0x2a, 0x2a, 0x29, 0x29, 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x26, 0x26, 0x26, 0x26, 0x26, 0x25, 0x24, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x20, 0x1f, 0x1f, 0x1e, 0x1e, 0x1d, 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1c, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, + 0x18, 0x17, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, 0x15, 0x14, 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, + 0x0f, 0x0f, 0x0f, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x07, + 0x06, 0x06, 0x06, 0x05, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, + 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf7, 0xf6, 0xf6, 0xf5, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xef, 0xef, 0xee, 0xed, 0xed, 0xec, 0xec, 0xec, + 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xea, 0xea, 0xe9, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe2, 0xe1, 0xe1, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xdd, 0xdc, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, 0xd8, 0xd7, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, + 0xd2, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, + 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcc, 0xcc, 0xcb, 0xca, 0xca, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, + 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xca, 0xca, 0xca, 0xcb, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xcf, 0xcf, + 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd4, 0xd4, + 0xd4, 0xd3, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd6, 0xd6, 0xd7, 0xd7, 0xd8, 0xd8, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xda, 0xda, + 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe2, 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe7, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xea, 0xeb, + 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf7, 0xf8, 0xf8, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfd, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x14, 0x14, 0x14, 0x15, 0x15, 0x14, 0x14, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x1a, 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x13, 0x13, + 0x13, 0x12, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, + 0x0e, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf6, 0xf6, + 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf1, 0xf1, 0xf0, 0xf0, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xe9, 0xea, 0xea, + 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, + 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, + 0xdf, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdb, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdc, + 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd, + 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe0, 0xe0, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xea, 0xea, + 0xea, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xef, 0xef, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x10, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, + 0x17, 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2d, 0x2d, 0x2d, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2a, 0x2a, 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x24, 0x24, 0x23, 0x23, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x1f, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, 0x1b, 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x17, + 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x14, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, 0x06, 0x06, 0x06, + 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, + 0xfe, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf8, 0xf9, 0xf9, 0xf9, 0xf8, 0xf7, 0xf7, 0xf6, + 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xef, 0xef, + 0xef, 0xef, 0xee, 0xee, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xda, 0xda, + 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, + 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, + 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd2, + 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, + 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, + 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, + 0xe9, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xef, 0xef, 0xf0, 0xf0, + 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, + 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x10, + 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x13, 0x13, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x17, 0x17, + 0x17, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, + 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x26, 0x26, 0x26, 0x26, 0x25, 0x25, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x23, 0x23, 0x23, 0x23, 0x23, 0x22, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, 0x1b, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, + 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x06, 0x06, 0x06, 0x05, + 0x05, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, + 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, + 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xeb, + 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdd, 0xdc, 0xdc, 0xdc, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, + 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xdd, 0xdd, 0xdd, + 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xea, + 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf9, + 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, + 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, + 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x18, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xec, + 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfd, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const unsigned char HH_2608[7259] = { + 0x52, 0x49, 0x46, 0x46, 0x82, 0x38, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x44, 0xac, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x02, 0x00, 0x10, 0x00, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x61, 0x61, 0x38, 0x00, 0x00, 0x01, 0x01, 0x03, 0x02, 0x01, 0x02, 0xfc, 0xff, 0x01, 0xf7, 0x08, 0x01, 0xf1, 0x08, 0x05, + 0x00, 0x07, 0xfe, 0xf9, 0xfd, 0x02, 0xfe, 0xff, 0x03, 0xf1, 0x00, 0x04, 0xec, 0xfe, 0x01, 0xf9, 0x04, 0x02, 0x07, 0x0d, 0xfa, 0xf2, + 0xf0, 0xef, 0x03, 0x09, 0x01, 0xff, 0xf6, 0xf4, 0xfc, 0x06, 0x0a, 0x0b, 0x0c, 0x0c, 0x00, 0xfa, 0x11, 0x04, 0xf5, 0xf9, 0xf8, 0x00, + 0x05, 0xfb, 0x03, 0x00, 0xfb, 0x0e, 0x0b, 0x02, 0x01, 0x11, 0x01, 0xf5, 0x11, 0x11, 0x10, 0x11, 0x07, 0x09, 0x07, 0xfa, 0xff, 0x06, + 0x01, 0xef, 0xf4, 0xfa, 0xfc, 0x0e, 0xfd, 0xf6, 0x10, 0xf4, 0xf5, 0x07, 0xf9, 0xf9, 0xfa, 0xf5, 0xf0, 0xfc, 0x05, 0xfe, 0xf3, 0xf5, + 0xfc, 0xf2, 0xff, 0x06, 0xfa, 0xe8, 0xeb, 0xf4, 0xf2, 0xfe, 0x05, 0x04, 0x03, 0xfa, 0xf8, 0xfd, 0xf7, 0xf4, 0xf6, 0xfa, 0x01, 0x05, + 0x07, 0x10, 0x12, 0x0e, 0x04, 0xf9, 0xf8, 0xff, 0x02, 0x05, 0xfb, 0xed, 0xeb, 0x09, 0x15, 0xf9, 0x1e, 0x0d, 0xeb, 0x00, 0x0d, 0xff, + 0xef, 0x02, 0x0c, 0x06, 0xf9, 0xfb, 0x08, 0x10, 0x02, 0x18, 0x1f, 0x09, 0xf7, 0x09, 0x15, 0xfb, 0xf8, 0xf5, 0xf5, 0x04, 0xf8, 0xf4, + 0x04, 0x07, 0xfb, 0xf7, 0x0e, 0xeb, 0xec, 0x0d, 0x08, 0x01, 0x07, 0x14, 0x0b, 0xf8, 0xf2, 0x09, 0xe2, 0xf1, 0x20, 0xe4, 0xee, 0x0d, + 0x09, 0xde, 0xf9, 0x1e, 0xef, 0xf4, 0xf6, 0xec, 0xff, 0x09, 0xfc, 0xe9, 0xe2, 0xfa, 0x11, 0x04, 0x00, 0xf8, 0xed, 0x08, 0x00, 0xf8, + 0x11, 0x12, 0xfd, 0xef, 0x04, 0xf5, 0xe0, 0xdd, 0x04, 0xfa, 0xf6, 0x22, 0x14, 0xeb, 0xe0, 0x2d, 0x06, 0xe5, 0x07, 0x10, 0xf1, 0xe6, + 0x16, 0x18, 0x0b, 0x06, 0x12, 0x01, 0xf9, 0x08, 0x03, 0xfb, 0xfe, 0x0a, 0x11, 0x04, 0xf5, 0x0c, 0x05, 0xf5, 0xf9, 0xfc, 0x03, 0x0e, + 0x1e, 0x0a, 0x09, 0x18, 0xf5, 0xf2, 0x04, 0x09, 0x0c, 0xff, 0xee, 0xea, 0x00, 0x08, 0xfb, 0x1f, 0x07, 0xeb, 0x04, 0x0c, 0x01, 0xfa, + 0x1b, 0xfd, 0xed, 0x08, 0xf2, 0xfd, 0x15, 0x0f, 0xf5, 0xea, 0xf3, 0xed, 0x0f, 0x10, 0xe3, 0xff, 0x10, 0x07, 0xf6, 0x0e, 0x0a, 0xec, + 0xee, 0x06, 0x02, 0xe0, 0x29, 0x1c, 0xe5, 0x01, 0xef, 0xe4, 0xf6, 0x11, 0xf8, 0xf5, 0x2a, 0x00, 0xf6, 0x0d, 0x03, 0xfb, 0xff, 0x0a, + 0xf8, 0x13, 0x17, 0xd4, 0x0b, 0x01, 0xd9, 0x32, 0x02, 0xd4, 0xf8, 0xe7, 0x00, 0x15, 0xf3, 0xfc, 0x09, 0x09, 0x01, 0x0e, 0x03, 0xde, + 0x04, 0x07, 0xf6, 0x00, 0x14, 0x08, 0xef, 0x0c, 0x01, 0xf5, 0x01, 0x07, 0xed, 0xe1, 0x10, 0xfc, 0xf4, 0x0c, 0x06, 0x08, 0x08, 0xfd, + 0xf7, 0xff, 0x05, 0xef, 0x13, 0x20, 0x04, 0xfd, 0x07, 0x10, 0x0d, 0xeb, 0xf6, 0x16, 0xfb, 0x06, 0x0d, 0xfc, 0xe2, 0xef, 0x10, 0x21, + 0xf5, 0xec, 0xfe, 0xc5, 0xf9, 0x0f, 0xd8, 0x08, 0x13, 0x00, 0x04, 0x0f, 0x04, 0xf4, 0x0f, 0xfc, 0xf0, 0x09, 0x02, 0xf8, 0xf8, 0xfe, + 0x00, 0x03, 0x08, 0x08, 0x02, 0xf7, 0xea, 0xe5, 0xf5, 0x09, 0x0e, 0x18, 0x1e, 0x12, 0x00, 0xd8, 0xce, 0xf7, 0x09, 0x09, 0x08, 0x14, + 0x16, 0xf7, 0xd2, 0x0e, 0x0a, 0xf2, 0x0c, 0xf7, 0xef, 0xff, 0x0e, 0xf6, 0xe9, 0xfb, 0xf5, 0x0c, 0x0c, 0xc7, 0x0c, 0x05, 0xc6, 0x1e, + 0x28, 0x1a, 0x21, 0xed, 0xdc, 0xf0, 0xf4, 0x0b, 0x0b, 0xf9, 0xff, 0x14, 0x12, 0xf0, 0xfb, 0xed, 0xe4, 0x0e, 0x05, 0xf7, 0xfe, 0xeb, + 0xff, 0x12, 0x05, 0xe7, 0xfd, 0x1d, 0xf4, 0xfc, 0xfa, 0xe2, 0xf5, 0x0b, 0x0d, 0xfb, 0x0f, 0xfa, 0xdd, 0x10, 0x00, 0xf5, 0x12, 0x02, + 0xf9, 0x02, 0x18, 0xe2, 0xe6, 0x14, 0xf0, 0xf2, 0xfe, 0xfb, 0x24, 0x08, 0xef, 0x37, 0xf0, 0xe3, 0x27, 0x0c, 0x11, 0x09, 0xe2, 0xe3, + 0x0b, 0x1e, 0xf1, 0x02, 0x0e, 0xfb, 0x06, 0xf8, 0xf7, 0x16, 0x00, 0xe8, 0xef, 0x23, 0xea, 0xe1, 0x21, 0xfa, 0xfc, 0x09, 0xee, 0x04, + 0xee, 0xcf, 0x0e, 0x0e, 0x02, 0x09, 0xfd, 0xf7, 0xfe, 0x00, 0x06, 0xfd, 0xf0, 0xef, 0x14, 0x1c, 0xf7, 0xfc, 0xec, 0xe9, 0x1b, 0xfe, + 0x03, 0x21, 0xe0, 0xf0, 0x10, 0xff, 0x12, 0x02, 0xf3, 0x19, 0x1d, 0x03, 0xe8, 0xfe, 0xe4, 0xea, 0x31, 0x27, 0x13, 0x0c, 0x08, 0xf7, + 0xf2, 0xfd, 0x00, 0x0e, 0x17, 0x0a, 0x19, 0x04, 0xe7, 0x0a, 0x01, 0xf1, 0xfa, 0x2a, 0x0a, 0xed, 0x2a, 0xe4, 0xf2, 0x37, 0xf4, 0x02, + 0x1e, 0x0a, 0xec, 0xf4, 0x05, 0xf7, 0x05, 0x04, 0xf8, 0x06, 0x06, 0xf9, 0xee, 0xfc, 0xf6, 0xf1, 0x0d, 0x14, 0xfe, 0xe8, 0x15, 0xf9, + 0xda, 0xee, 0x26, 0x05, 0xd3, 0x0c, 0x05, 0xec, 0xe9, 0x0a, 0x02, 0xf1, 0xf5, 0x02, 0xee, 0xd9, 0xfb, 0x09, 0x06, 0xff, 0xf6, 0xfd, + 0x06, 0xfe, 0x11, 0x05, 0xe5, 0xe6, 0xf0, 0x02, 0x11, 0xff, 0x09, 0x0f, 0xe7, 0x17, 0x17, 0xe7, 0xf1, 0x0c, 0xff, 0xd2, 0x0f, 0x1e, + 0x08, 0x10, 0x0e, 0x08, 0xfe, 0xdf, 0xff, 0x12, 0xef, 0x1a, 0x1d, 0xfb, 0xee, 0x14, 0x19, 0xf6, 0x0c, 0x0b, 0xfc, 0xf4, 0x1c, 0x10, + 0xe8, 0xe6, 0x22, 0x2e, 0xf8, 0x03, 0xe8, 0xd1, 0xe9, 0x0f, 0x1f, 0x13, 0xf9, 0xf5, 0x07, 0x13, 0xe1, 0xed, 0x09, 0xec, 0xfe, 0x0d, + 0x01, 0xdf, 0xfa, 0xfe, 0xd3, 0x16, 0x10, 0xe9, 0x00, 0x1a, 0x14, 0xf9, 0xfd, 0xfd, 0xf9, 0xf5, 0x06, 0x00, 0xf4, 0xff, 0xf4, 0xe9, + 0xee, 0xf3, 0x02, 0x07, 0xf9, 0x01, 0x01, 0xf8, 0xf8, 0xf2, 0x02, 0x1b, 0xf1, 0x04, 0x15, 0xe6, 0x04, 0x0a, 0xf5, 0x00, 0x06, 0x08, + 0x07, 0xf4, 0xf4, 0x02, 0x0a, 0xe8, 0xfc, 0x25, 0xe8, 0x01, 0x17, 0xf6, 0xe3, 0xf7, 0x11, 0x0d, 0xff, 0x03, 0x10, 0x01, 0xf8, 0x01, + 0x12, 0xfc, 0xf2, 0xf6, 0xf7, 0xf8, 0x04, 0x12, 0x13, 0xf7, 0xeb, 0x04, 0xf8, 0xf8, 0x07, 0x13, 0xf7, 0xf5, 0x10, 0x07, 0xf4, 0xf2, + 0x08, 0x0a, 0x12, 0x0e, 0xe2, 0xf9, 0x0c, 0xf7, 0x11, 0xf7, 0xe7, 0x15, 0x02, 0xfa, 0x06, 0xee, 0x02, 0x0c, 0xfa, 0x15, 0x23, 0x02, + 0xc0, 0xf9, 0x15, 0xfa, 0x12, 0x06, 0xf4, 0xf5, 0x05, 0x02, 0xf7, 0xfe, 0xf1, 0x00, 0x1b, 0xf0, 0x08, 0x12, 0xdd, 0x04, 0xfb, 0xe0, + 0x0f, 0xfe, 0xf7, 0x0f, 0x06, 0xf2, 0xee, 0x03, 0xf3, 0x05, 0x18, 0xe4, 0xfc, 0x0a, 0xee, 0x05, 0x07, 0xf7, 0xeb, 0x22, 0x0e, 0xd8, + 0x12, 0x0d, 0xfa, 0x01, 0x16, 0xfc, 0xe8, 0x0b, 0x06, 0xfb, 0xfa, 0xf7, 0x07, 0x0d, 0xfc, 0x0d, 0x09, 0xfa, 0xfe, 0xfa, 0xf9, 0xfe, + 0xff, 0x00, 0x08, 0x14, 0xf1, 0x00, 0x19, 0xeb, 0xfc, 0x07, 0xf7, 0xfa, 0x0b, 0x19, 0x16, 0xf8, 0xf6, 0x00, 0xe1, 0xf5, 0x08, 0xfd, + 0xec, 0x00, 0x17, 0x0d, 0x1e, 0x0b, 0xea, 0xfb, 0xf5, 0xf7, 0x0b, 0x01, 0xfa, 0x06, 0x1c, 0xf8, 0xf0, 0x0a, 0x00, 0x0a, 0x04, 0xe8, + 0x02, 0x0c, 0x01, 0xfd, 0x03, 0xfd, 0xf1, 0x0b, 0xf4, 0xe9, 0x0a, 0x06, 0x02, 0x02, 0x00, 0xf8, 0x07, 0x14, 0xd7, 0xe4, 0x0b, 0x0c, + 0xf7, 0xfc, 0x0d, 0x00, 0xf9, 0x05, 0x17, 0xf3, 0x06, 0x18, 0xf6, 0xf8, 0x04, 0x03, 0xf6, 0xee, 0xfb, 0x11, 0x0d, 0xf4, 0xea, 0x0b, + 0xe3, 0xf0, 0x1b, 0xfb, 0xf5, 0x05, 0x11, 0xfc, 0xf9, 0x01, 0x03, 0x04, 0xff, 0xfb, 0x0c, 0xfb, 0xf4, 0x09, 0xf8, 0xeb, 0xf7, 0x11, + 0x12, 0x03, 0xf4, 0xec, 0xf7, 0x03, 0x03, 0xf0, 0xfa, 0x12, 0x0e, 0xf9, 0xee, 0xfb, 0x0a, 0x12, 0x07, 0xf3, 0xfe, 0x08, 0x06, 0x04, + 0xf8, 0xf0, 0xf7, 0x0e, 0x01, 0xf0, 0x03, 0xf4, 0xf2, 0x06, 0x1f, 0x08, 0xf8, 0x06, 0xfd, 0xf7, 0xfd, 0x10, 0x08, 0xfc, 0xfc, 0x15, + 0x00, 0xf3, 0x0a, 0x1e, 0x01, 0xe3, 0x03, 0xf4, 0xed, 0xfd, 0x0e, 0xfb, 0xf5, 0x13, 0xf3, 0xfa, 0x13, 0xfe, 0xfd, 0xfe, 0xfb, 0x13, + 0xfc, 0xec, 0x0c, 0x0b, 0x02, 0xfe, 0x04, 0xff, 0x01, 0x0a, 0x07, 0xf6, 0xf5, 0x10, 0x06, 0xfa, 0xf8, 0xf9, 0xfb, 0xf9, 0xf7, 0x02, + 0x09, 0x00, 0xf0, 0xf0, 0x04, 0x17, 0x0d, 0x0d, 0xff, 0xe3, 0x04, 0x00, 0xe8, 0xf2, 0x05, 0x0c, 0x04, 0xfa, 0x04, 0x0f, 0x0d, 0xe3, + 0xfb, 0x17, 0xd4, 0xed, 0x0c, 0x05, 0x08, 0x0a, 0x04, 0xf8, 0x04, 0xf8, 0xea, 0x0a, 0x00, 0xff, 0x16, 0xfe, 0x0c, 0x16, 0xf3, 0x07, + 0x08, 0xf6, 0xfb, 0x01, 0xfb, 0xf1, 0x0e, 0x02, 0xf5, 0x15, 0x08, 0xf8, 0xf6, 0xfc, 0xf1, 0xee, 0xfd, 0x13, 0x07, 0xf6, 0x07, 0xfd, + 0xf5, 0xf7, 0xfe, 0xee, 0xed, 0x07, 0x13, 0x05, 0xf8, 0x05, 0x00, 0xf8, 0xf7, 0xfd, 0x05, 0xff, 0xea, 0x08, 0x07, 0xf6, 0x06, 0x10, + 0x0b, 0xff, 0xfa, 0x05, 0x01, 0xe2, 0xeb, 0x04, 0x12, 0xfb, 0x18, 0x15, 0xe8, 0x18, 0x08, 0xea, 0x09, 0xff, 0xfe, 0x0a, 0xfd, 0x08, + 0x0a, 0xf8, 0x02, 0x06, 0xfc, 0xed, 0xf4, 0xff, 0x03, 0xfc, 0x03, 0x0b, 0x0a, 0x04, 0x01, 0xff, 0xfb, 0xeb, 0xf8, 0x13, 0xed, 0x01, + 0x10, 0xf0, 0x00, 0x19, 0x13, 0xde, 0xf6, 0x00, 0xe6, 0x19, 0xfe, 0xe0, 0x05, 0x0d, 0x07, 0xfc, 0xe8, 0xfe, 0x0d, 0x02, 0xfd, 0x08, + 0x0b, 0xfc, 0xff, 0x07, 0x07, 0xfd, 0xe9, 0xee, 0x0c, 0xfa, 0xfb, 0x01, 0xf2, 0xf0, 0x02, 0x0e, 0xeb, 0x00, 0x03, 0xd7, 0x05, 0x06, + 0xf1, 0x0f, 0xf7, 0xfa, 0x1a, 0xfc, 0xf6, 0xfd, 0xfd, 0xf1, 0x07, 0x1d, 0x04, 0xfe, 0xff, 0xfd, 0x0a, 0xfc, 0xf1, 0x05, 0x0c, 0x0c, + 0x06, 0xfb, 0xf9, 0xfd, 0x02, 0x05, 0x05, 0x02, 0xfc, 0xf7, 0xfa, 0x01, 0x07, 0x03, 0x01, 0x05, 0x0b, 0x00, 0xf4, 0xf8, 0x02, 0x06, + 0x02, 0xf8, 0xff, 0xf8, 0xe3, 0x0e, 0x0f, 0xfb, 0x0b, 0xfe, 0x01, 0x12, 0x05, 0xfe, 0xfe, 0xfe, 0xf8, 0x03, 0x0d, 0xf9, 0x0d, 0x09, + 0xee, 0x19, 0x05, 0xe8, 0xf9, 0x15, 0x0a, 0xf6, 0x12, 0x05, 0xf9, 0x02, 0x10, 0xfb, 0xf0, 0x11, 0xef, 0xfc, 0x1c, 0xe9, 0xfc, 0x0b, + 0xf2, 0x0d, 0x00, 0xee, 0x06, 0x07, 0x00, 0xfb, 0xf9, 0xff, 0xfe, 0xf5, 0x0b, 0x08, 0xfb, 0x04, 0xfc, 0xf9, 0xff, 0xfe, 0x00, 0xfe, + 0xf8, 0x0d, 0x02, 0xee, 0x03, 0xf4, 0xf3, 0x08, 0x05, 0xff, 0xfd, 0xfc, 0x06, 0xfa, 0xed, 0x04, 0x0c, 0x01, 0xf7, 0x20, 0x05, 0xe2, + 0xf9, 0xf7, 0xff, 0x0f, 0x0b, 0x0d, 0x01, 0xea, 0xef, 0x04, 0x0b, 0xf9, 0x04, 0x06, 0xfa, 0xee, 0xfd, 0x02, 0xf1, 0xf3, 0x02, 0x0b, + 0xfc, 0x10, 0x0b, 0xf0, 0xf3, 0x09, 0x0f, 0xfe, 0x0b, 0xff, 0xed, 0xf4, 0xfc, 0x00, 0x04, 0x0f, 0x0d, 0xfe, 0xeb, 0x03, 0x01, 0xf0, + 0xf6, 0x0b, 0x08, 0xf2, 0xfe, 0x03, 0xfe, 0xf6, 0x00, 0x06, 0x02, 0xfb, 0xfd, 0x0b, 0x16, 0xeb, 0x00, 0x19, 0xf0, 0xfe, 0x09, 0x01, + 0xf1, 0x03, 0x07, 0xf4, 0x0c, 0x14, 0x02, 0xeb, 0xf3, 0xfe, 0xff, 0x01, 0xfe, 0x02, 0x0f, 0xf3, 0xfb, 0x0d, 0xff, 0xf5, 0x06, 0x1a, + 0xfd, 0xf6, 0xfc, 0xfe, 0x02, 0x0c, 0x0f, 0xff, 0x03, 0x00, 0xf6, 0x00, 0x06, 0x01, 0xfa, 0x0a, 0x06, 0xfa, 0x12, 0xf5, 0xe8, 0xff, + 0xf4, 0xfc, 0x08, 0x05, 0xf7, 0xfd, 0x07, 0xf1, 0x07, 0x15, 0x04, 0x06, 0x08, 0xfd, 0xe5, 0xf7, 0x05, 0xff, 0xf5, 0xfc, 0x01, 0xf9, + 0xf4, 0xf6, 0xfe, 0x07, 0xfa, 0xf8, 0x06, 0x0b, 0xf4, 0xef, 0x0f, 0xed, 0xf2, 0x0f, 0x10, 0xe6, 0xef, 0x1b, 0xe7, 0xf5, 0x12, 0x05, + 0x01, 0x04, 0x01, 0xf4, 0xf2, 0xf8, 0xfe, 0xfe, 0x02, 0x04, 0xfe, 0xff, 0x01, 0x03, 0x06, 0x0f, 0x06, 0xef, 0x04, 0xf8, 0xed, 0x0c, + 0x06, 0xfc, 0xfc, 0xfd, 0xfe, 0xfd, 0xfb, 0xf5, 0x03, 0x10, 0x01, 0xff, 0xfc, 0xf9, 0x04, 0x04, 0xf9, 0xf4, 0xfe, 0x09, 0x0d, 0x09, + 0xf9, 0xf0, 0xfa, 0x1f, 0x0d, 0xf0, 0xf8, 0x08, 0x02, 0xf1, 0xf8, 0x01, 0x05, 0x01, 0x01, 0x03, 0x04, 0xff, 0x02, 0xf9, 0xf3, 0x06, + 0x07, 0xfd, 0xf7, 0x0b, 0xfc, 0xf1, 0x12, 0xf6, 0xf6, 0x0f, 0x00, 0xfb, 0xfb, 0xfb, 0x06, 0x00, 0xfa, 0x0c, 0x0a, 0xfd, 0xf7, 0x06, + 0xfc, 0xf9, 0x0a, 0xfa, 0x05, 0x13, 0xf3, 0x0d, 0x0d, 0xed, 0xfe, 0x05, 0x04, 0x04, 0x04, 0xfd, 0xf8, 0xff, 0x03, 0x01, 0xfe, 0x04, + 0xff, 0xfe, 0x0a, 0xfe, 0xfe, 0x07, 0x04, 0xfe, 0xfc, 0x00, 0x0c, 0xfb, 0xee, 0x02, 0xff, 0x02, 0x08, 0x03, 0xf3, 0xf0, 0xfd, 0x02, + 0x05, 0x02, 0xfa, 0xfd, 0x06, 0x09, 0x00, 0x00, 0x02, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0xfe, 0xfb, 0xf9, 0x00, 0x03, 0xfa, 0xfd, + 0xfd, 0xff, 0x08, 0x09, 0x04, 0x00, 0xfd, 0xfd, 0x02, 0x0b, 0xf7, 0xf3, 0xff, 0x08, 0xf7, 0xf5, 0x08, 0x0e, 0xfc, 0xf1, 0xff, 0x03, + 0x00, 0xfd, 0x06, 0x0b, 0x06, 0xf8, 0xfe, 0xf7, 0xf5, 0x0b, 0xfa, 0xfa, 0x06, 0xf6, 0xf2, 0xfc, 0x06, 0x0e, 0xf7, 0xeb, 0x12, 0xfc, + 0xf3, 0x07, 0x07, 0xff, 0xfb, 0xfc, 0xfd, 0xfd, 0xfc, 0xfc, 0x05, 0x0a, 0x01, 0xed, 0xf8, 0x05, 0xfe, 0x06, 0x0c, 0x05, 0xf2, 0xfd, + 0x04, 0xfb, 0x03, 0xfe, 0xfc, 0x09, 0xfb, 0xfa, 0x04, 0x02, 0xff, 0xfe, 0x00, 0x01, 0x06, 0x07, 0x03, 0xf6, 0xfc, 0x0c, 0x02, 0x08, + 0x0a, 0xfe, 0x00, 0xf7, 0xf4, 0x0b, 0x03, 0x04, 0x0c, 0xf3, 0xf9, 0x03, 0xf9, 0x01, 0x01, 0xfe, 0x07, 0x01, 0xfc, 0xff, 0x02, 0x08, + 0x02, 0xf1, 0xf9, 0x04, 0x03, 0xf2, 0x08, 0x0c, 0xf4, 0xfb, 0x05, 0x07, 0xfb, 0x04, 0xfb, 0xed, 0xfc, 0x01, 0x01, 0x01, 0xfe, 0xfd, + 0x01, 0x09, 0xf7, 0xfc, 0x0b, 0xf8, 0xef, 0xf9, 0x09, 0x01, 0x03, 0x0a, 0x05, 0xf8, 0xf8, 0x01, 0xf4, 0x05, 0x0b, 0xf3, 0x02, 0x06, + 0x01, 0x09, 0x00, 0xfb, 0x00, 0x01, 0xfb, 0xfa, 0x06, 0x02, 0x03, 0x02, 0xf7, 0xfa, 0x04, 0x08, 0x03, 0xfd, 0xfd, 0x01, 0xfd, 0xfd, + 0x01, 0xff, 0x0c, 0x0a, 0xf9, 0x01, 0xfc, 0xf8, 0x04, 0x02, 0xfe, 0x00, 0x07, 0x02, 0xfe, 0x01, 0xfd, 0xf6, 0xf9, 0x08, 0x0b, 0x06, + 0xff, 0xf7, 0x02, 0x08, 0xfd, 0x07, 0x06, 0xfc, 0xf6, 0xf8, 0x00, 0x07, 0x01, 0x04, 0x04, 0xf9, 0xfc, 0xfd, 0xfc, 0x03, 0xfc, 0xf6, + 0xfb, 0x0d, 0x02, 0xf8, 0x08, 0xf8, 0xfa, 0x07, 0xfd, 0xf6, 0xf9, 0x00, 0xfd, 0x06, 0x0c, 0x03, 0xf4, 0xfa, 0x05, 0xf6, 0xf9, 0xfe, + 0xfb, 0x05, 0xfd, 0xfa, 0x0f, 0xff, 0xf7, 0xff, 0x04, 0xfe, 0xf8, 0xf8, 0x04, 0x01, 0xf9, 0xfd, 0x07, 0x03, 0xf6, 0x02, 0xf7, 0xf0, + 0xfa, 0x04, 0xff, 0xf8, 0xf9, 0x09, 0x03, 0xef, 0x13, 0x05, 0xef, 0xfc, 0xf5, 0xff, 0x0f, 0x01, 0x06, 0x03, 0xf3, 0xf5, 0x02, 0x06, + 0xfb, 0xfb, 0xfe, 0xff, 0xfe, 0x00, 0x06, 0x0c, 0xf7, 0x03, 0x0f, 0xf4, 0x00, 0x03, 0xf7, 0xfe, 0x03, 0x00, 0xfa, 0x01, 0x07, 0x07, + 0x04, 0xf9, 0xf4, 0xf9, 0x0c, 0xfb, 0xfa, 0x19, 0x03, 0xf2, 0xf8, 0x0c, 0xfe, 0xf8, 0x02, 0x02, 0xf8, 0xfb, 0x0c, 0x08, 0xff, 0xfc, + 0x05, 0x01, 0xff, 0x04, 0x00, 0xfe, 0xfe, 0xfd, 0x06, 0x08, 0x04, 0xfd, 0x06, 0x05, 0xf9, 0xfd, 0x05, 0x04, 0xf9, 0x02, 0x06, 0x00, + 0x00, 0xfd, 0x00, 0x09, 0x04, 0x00, 0xfa, 0xef, 0xfe, 0x07, 0x01, 0x00, 0xfc, 0xfd, 0x03, 0x03, 0x05, 0x04, 0xf9, 0xfa, 0x01, 0x04, + 0xfa, 0xfa, 0x03, 0x0a, 0xfc, 0xfe, 0x05, 0xf7, 0xff, 0x06, 0x01, 0xf8, 0x02, 0x0a, 0x00, 0xfe, 0x04, 0x05, 0xf4, 0x01, 0x08, 0xfc, + 0x01, 0xff, 0xfd, 0x01, 0x08, 0x05, 0xfd, 0xfb, 0x00, 0xfd, 0xf3, 0x00, 0x05, 0x03, 0x03, 0xf9, 0xf7, 0xfd, 0xfc, 0x03, 0x08, 0x01, + 0xf3, 0xf2, 0xfd, 0x08, 0x07, 0x00, 0xfb, 0x01, 0xfb, 0xf9, 0x05, 0xf9, 0x02, 0x0c, 0xf7, 0xfe, 0x07, 0x02, 0xfe, 0xfe, 0x03, 0x0b, + 0xf7, 0xfd, 0x0b, 0xf8, 0xfa, 0x03, 0x03, 0xfd, 0xfc, 0x01, 0x06, 0x01, 0xfc, 0xfc, 0xfc, 0x07, 0x08, 0xfb, 0x00, 0xfe, 0xf9, 0xfb, + 0x07, 0x02, 0xf7, 0x06, 0x01, 0xfc, 0x05, 0xfb, 0x01, 0x0b, 0x02, 0xfa, 0xfe, 0x05, 0xfa, 0xfa, 0x02, 0x07, 0x00, 0xfc, 0xfd, 0xff, + 0x05, 0x03, 0xfd, 0x00, 0x01, 0xfd, 0xf6, 0x01, 0x03, 0xfd, 0xfb, 0x01, 0x00, 0xfb, 0xfc, 0x02, 0x03, 0xfb, 0xf8, 0xff, 0x05, 0xf9, + 0xfd, 0x03, 0x01, 0xfd, 0xfe, 0xff, 0xfb, 0xf9, 0xfd, 0x01, 0xfe, 0xfa, 0xfc, 0x03, 0x02, 0xfc, 0xfb, 0x07, 0xf7, 0xf7, 0x08, 0xfe, + 0x00, 0x02, 0xfa, 0xf9, 0x02, 0x06, 0xfb, 0xfc, 0x02, 0x05, 0x02, 0xff, 0x00, 0x06, 0xff, 0xfc, 0xfe, 0xfc, 0x03, 0x07, 0x02, 0x04, + 0xfc, 0xfa, 0x07, 0x01, 0xfb, 0xfc, 0x05, 0xfb, 0xf9, 0x03, 0x03, 0x01, 0xfe, 0xfd, 0xff, 0x03, 0x04, 0x03, 0xff, 0x00, 0x07, 0x01, + 0x03, 0x04, 0xfd, 0xfc, 0xff, 0xff, 0xfb, 0xfb, 0x03, 0x0c, 0x07, 0xff, 0xfa, 0xf9, 0x02, 0x05, 0xfe, 0x00, 0xfb, 0xfb, 0x03, 0x04, + 0x02, 0x00, 0xfd, 0xfc, 0xfd, 0xfc, 0xf7, 0x01, 0x08, 0x00, 0x02, 0x00, 0xfc, 0x01, 0xfc, 0xfa, 0xfe, 0x02, 0x00, 0xff, 0x06, 0x01, + 0xfa, 0xf7, 0xf9, 0x00, 0x03, 0xff, 0xfd, 0xf7, 0xf7, 0x03, 0x01, 0x01, 0x03, 0xfd, 0xf5, 0xf6, 0xff, 0xfc, 0x00, 0x05, 0x03, 0xfe, + 0xfd, 0xfe, 0x00, 0xfa, 0xfa, 0x03, 0x04, 0xfd, 0xf9, 0xff, 0xfe, 0xfd, 0xfe, 0x08, 0x00, 0xfb, 0x05, 0xfe, 0xfb, 0xfe, 0xff, 0x05, + 0x06, 0x01, 0x04, 0x02, 0xff, 0xfd, 0x04, 0x02, 0xfd, 0xfd, 0x01, 0x03, 0x01, 0xf9, 0xfe, 0x03, 0xf9, 0x01, 0x04, 0x00, 0x01, 0xff, + 0xff, 0x02, 0xfc, 0x01, 0x0a, 0x06, 0xfc, 0xfc, 0x02, 0xf8, 0x01, 0x05, 0xfa, 0x06, 0x0b, 0x04, 0xfd, 0x00, 0x05, 0x04, 0xfc, 0x00, + 0x03, 0xfa, 0xff, 0x04, 0x04, 0x04, 0x02, 0x02, 0x03, 0x00, 0xf4, 0xf7, 0x0d, 0xfb, 0xfb, 0x0a, 0xfd, 0x04, 0x06, 0xfa, 0xfe, 0x01, + 0xff, 0xfb, 0x00, 0x01, 0xfe, 0x00, 0x01, 0x00, 0xff, 0xfc, 0xff, 0x00, 0xf7, 0xf8, 0x01, 0x07, 0xff, 0xfe, 0x02, 0x03, 0xfc, 0xfd, + 0xfe, 0xf3, 0x01, 0x06, 0xfc, 0x04, 0xff, 0xff, 0x0a, 0xfa, 0xf5, 0xfc, 0xfa, 0x02, 0x06, 0x03, 0x08, 0xfe, 0xf7, 0x01, 0xfd, 0x01, + 0x06, 0xfc, 0xff, 0x03, 0xfe, 0x03, 0xff, 0xfb, 0x01, 0x05, 0x06, 0x03, 0x03, 0xf8, 0xf6, 0x00, 0x05, 0x01, 0xfd, 0x00, 0x03, 0xff, + 0xf9, 0x05, 0xff, 0xfb, 0x03, 0x05, 0x01, 0xfb, 0xf3, 0x07, 0x0c, 0xff, 0x05, 0x03, 0xfb, 0xf6, 0xfa, 0x03, 0x09, 0x05, 0x01, 0xfd, + 0xfc, 0x03, 0x00, 0xfe, 0x04, 0xfc, 0xfb, 0xff, 0xfc, 0xfe, 0xff, 0xfe, 0x02, 0x00, 0xfe, 0x03, 0xfb, 0xf9, 0xfe, 0xff, 0xfd, 0xfe, + 0x04, 0xfb, 0xfb, 0xfe, 0xfe, 0xfc, 0x00, 0x05, 0x01, 0xfc, 0xfb, 0xff, 0xff, 0x01, 0x01, 0xfe, 0xff, 0x01, 0x01, 0xfe, 0xfb, 0xfb, + 0x00, 0xff, 0x02, 0x03, 0xf8, 0x00, 0x05, 0xff, 0x03, 0xfd, 0xfa, 0x02, 0x03, 0x00, 0xfe, 0xfc, 0x01, 0x03, 0xfe, 0xfd, 0x01, 0x04, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x02, 0x03, 0x01, 0x04, 0x03, 0xf9, 0xfc, 0x00, 0x00, 0x00, 0x06, 0x03, 0xfa, 0x07, 0x04, 0xfb, + 0xfe, 0xff, 0x01, 0x03, 0x01, 0xff, 0xfd, 0xfb, 0x02, 0x09, 0x06, 0xf6, 0xff, 0x03, 0xfb, 0x04, 0x03, 0x02, 0x08, 0x00, 0xfc, 0xfe, + 0x00, 0xfd, 0xfd, 0x02, 0xfb, 0xfe, 0x00, 0xfb, 0x00, 0x02, 0xff, 0xfe, 0xfd, 0xfe, 0xff, 0xff, 0x01, 0x01, 0xfa, 0xff, 0x01, 0xfd, + 0xfa, 0xfd, 0xfd, 0xf9, 0x02, 0x01, 0xfd, 0x04, 0x01, 0xf6, 0xf2, 0x05, 0x02, 0xfb, 0x01, 0xf9, 0xfc, 0x01, 0xf9, 0xff, 0x00, 0xfa, + 0x02, 0x00, 0xfc, 0xfd, 0x02, 0x03, 0xfe, 0xfc, 0xfd, 0x00, 0x01, 0x02, 0xfd, 0xfb, 0x04, 0xfc, 0xfe, 0x06, 0x01, 0x01, 0x04, 0x03, + 0xf8, 0xfb, 0x05, 0x07, 0x02, 0xfe, 0xfe, 0xff, 0x01, 0x02, 0x00, 0xfe, 0xff, 0x03, 0x05, 0x02, 0xfd, 0xfd, 0x0b, 0x01, 0xf9, 0x03, + 0x06, 0x05, 0x02, 0x02, 0xfe, 0xff, 0x03, 0x02, 0x01, 0x01, 0x00, 0xfb, 0xfb, 0xff, 0x00, 0x04, 0x05, 0x03, 0x01, 0x00, 0xfd, 0xf8, + 0x01, 0x03, 0xfe, 0xfe, 0x02, 0x01, 0xfc, 0x01, 0x02, 0xfd, 0xf7, 0xfc, 0x00, 0x01, 0x03, 0x00, 0xfc, 0xfb, 0xf9, 0x00, 0x05, 0xfc, + 0xfd, 0xfc, 0xf9, 0xfe, 0xfd, 0xff, 0x05, 0xfe, 0xfc, 0xff, 0xff, 0xfd, 0x00, 0x03, 0xfa, 0xfe, 0x02, 0xfb, 0xff, 0x00, 0x00, 0x05, + 0x00, 0xfb, 0xfd, 0x01, 0x01, 0xff, 0xff, 0xfc, 0x00, 0x07, 0x05, 0xfd, 0xfc, 0x01, 0xfd, 0x00, 0x03, 0xff, 0x04, 0x01, 0xfa, 0xfe, + 0x00, 0x01, 0x04, 0x03, 0x02, 0x00, 0xf9, 0x01, 0x01, 0xfd, 0x07, 0x07, 0xff, 0xfa, 0x00, 0xfe, 0xfd, 0x07, 0x00, 0xfe, 0x04, 0x05, + 0xfe, 0xfd, 0x05, 0xff, 0xfe, 0x02, 0xff, 0xfe, 0x00, 0x04, 0x04, 0x04, 0x00, 0xfc, 0xff, 0x00, 0x00, 0xfe, 0x02, 0x00, 0xfc, 0x05, + 0x03, 0xfe, 0x00, 0x01, 0xfe, 0xfe, 0x03, 0xff, 0xfc, 0xff, 0xfd, 0x02, 0x04, 0xfb, 0xfc, 0xfe, 0xfd, 0xfe, 0x00, 0x00, 0xfd, 0xff, + 0x01, 0xff, 0xfb, 0xfe, 0x02, 0x02, 0xf9, 0xfb, 0xff, 0xfd, 0xf7, 0x02, 0x0c, 0xff, 0xfb, 0xff, 0x02, 0xfd, 0xfd, 0xff, 0x01, 0x02, + 0x02, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0x00, 0x02, 0x01, 0xfa, 0xfc, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x05, 0xfb, 0xfe, 0x03, 0xfd, 0xfd, + 0xfd, 0xfd, 0x07, 0x00, 0xfa, 0x00, 0x02, 0xff, 0xfc, 0xff, 0x00, 0xff, 0xff, 0xfd, 0x01, 0x03, 0xff, 0x02, 0x02, 0x01, 0xff, 0xfd, + 0xfe, 0x01, 0xfe, 0x02, 0x03, 0xfe, 0x01, 0x04, 0x01, 0xfa, 0xfe, 0x01, 0x00, 0x02, 0x04, 0x03, 0x01, 0xfe, 0x00, 0x02, 0xfb, 0xff, + 0x02, 0xfe, 0xfe, 0xff, 0x01, 0x00, 0x04, 0x01, 0xfc, 0xff, 0xff, 0xfd, 0xfd, 0x00, 0x00, 0xfe, 0xfd, 0x03, 0x01, 0xfd, 0x05, 0x00, + 0xfc, 0xff, 0xff, 0xfe, 0xfe, 0x03, 0x00, 0x01, 0x05, 0x02, 0x03, 0x00, 0xf9, 0xff, 0x01, 0xff, 0x02, 0x00, 0xfc, 0xfc, 0xff, 0xff, + 0x00, 0x04, 0xfc, 0xfe, 0x03, 0xfb, 0xfe, 0x02, 0x01, 0x04, 0x01, 0xfe, 0xfe, 0x02, 0x01, 0xfe, 0xfd, 0xff, 0xff, 0xfd, 0x00, 0x03, + 0x00, 0xf9, 0xfe, 0x02, 0x00, 0xfc, 0xfe, 0x00, 0xfe, 0xfe, 0xff, 0x01, 0x01, 0x02, 0xff, 0xfb, 0xff, 0x01, 0x01, 0xff, 0x01, 0x01, + 0x00, 0xff, 0x02, 0x01, 0xfd, 0x00, 0xfe, 0xfc, 0xfd, 0xfc, 0xfe, 0x00, 0x00, 0xfe, 0xff, 0x02, 0xfb, 0xfe, 0x02, 0xfd, 0xff, 0xfe, + 0xfc, 0x06, 0x02, 0xff, 0x03, 0xfe, 0xfa, 0xfc, 0x03, 0xfe, 0xff, 0x04, 0x01, 0xfb, 0xfe, 0x05, 0xfc, 0xfb, 0x00, 0x00, 0x01, 0x00, + 0xfe, 0x00, 0xff, 0xfe, 0xff, 0x02, 0x02, 0xff, 0xfc, 0x02, 0x01, 0xfa, 0x02, 0x02, 0x00, 0x02, 0x05, 0x00, 0xfa, 0x00, 0xff, 0xfe, + 0x02, 0xff, 0x03, 0x05, 0xff, 0xfb, 0xfe, 0x02, 0x05, 0x00, 0xfd, 0xfe, 0xfd, 0xff, 0x02, 0x04, 0x01, 0xff, 0xff, 0xff, 0xfe, 0xff, + 0x02, 0xff, 0xfe, 0xff, 0x01, 0x02, 0x01, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x04, 0x02, 0x00, 0x01, 0xfc, 0xfe, 0x04, 0xfe, 0x00, 0x01, + 0xfc, 0x00, 0xfe, 0xfc, 0x05, 0x00, 0xfe, 0x02, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0x02, 0x04, 0xfb, 0xff, 0x02, 0xfd, 0xfc, 0x00, 0x03, + 0xfe, 0x03, 0x02, 0xfd, 0xfe, 0xfe, 0x00, 0x04, 0x00, 0x01, 0x03, 0xfe, 0xfb, 0xfe, 0x01, 0xfd, 0x00, 0x03, 0x00, 0x03, 0x00, 0xfc, + 0x00, 0x01, 0x01, 0x00, 0xfc, 0x00, 0x03, 0xfe, 0x02, 0x01, 0xfe, 0x00, 0x02, 0x00, 0xff, 0x00, 0x01, 0xff, 0xfd, 0x02, 0x02, 0xff, + 0x00, 0xfd, 0xfe, 0x00, 0xfc, 0xff, 0x01, 0xfd, 0x03, 0x01, 0xfb, 0x02, 0x00, 0xff, 0x02, 0x00, 0xfd, 0xfc, 0x00, 0x00, 0x02, 0x03, + 0xfe, 0x00, 0x01, 0xfd, 0x01, 0xfe, 0xfb, 0xfe, 0x02, 0x01, 0xfe, 0x01, 0x02, 0x01, 0x00, 0xfe, 0xfe, 0x00, 0x01, 0x01, 0x01, 0x01, + 0xfe, 0xfe, 0x00, 0x01, 0x03, 0xfe, 0xfa, 0x00, 0xff, 0xfd, 0x00, 0x00, 0xfe, 0xff, 0x01, 0xff, 0xff, 0x00, 0xfa, 0x02, 0x03, 0xf9, + 0x01, 0x02, 0xff, 0x02, 0xfc, 0xfd, 0x03, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x02, 0x00, 0x00, 0x01, 0x00, 0xfe, 0x00, 0x03, + 0x03, 0xff, 0xfe, 0x04, 0xfe, 0xfd, 0x01, 0xff, 0xfe, 0x00, 0x01, 0xfe, 0xfe, 0x01, 0x03, 0xff, 0xfe, 0x00, 0xfc, 0xfe, 0x00, 0x00, + 0xfe, 0xfe, 0x00, 0xff, 0x02, 0x03, 0x00, 0xfc, 0xfd, 0x00, 0x01, 0x00, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x02, 0x02, 0x00, 0xfe, 0x00, 0x01, 0x00, 0x01, 0xfd, 0xfc, 0x02, 0x01, 0xff, 0xff, 0xfe, 0xfd, 0xff, 0x02, 0x00, 0xff, 0xff, 0xfd, + 0xff, 0x00, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0x03, 0x01, 0xfc, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xfe, 0x01, 0xff, 0x01, 0x02, 0xfe, 0x00, 0x01, 0x00, 0xff, 0x01, 0x02, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, + 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0xfe, 0x01, 0x04, 0xff, 0x01, 0x00, 0xfd, 0xfd, 0x00, 0x00, 0xff, 0x02, + 0x01, 0xff, 0xff, 0xfe, 0xff, 0x01, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0xfe, 0xfd, 0xff, 0x01, 0x02, 0x00, 0xfc, 0xfd, 0x00, 0x00, 0xfd, + 0xff, 0x02, 0xff, 0xfd, 0xff, 0x02, 0xff, 0xff, 0xff, 0xff, 0x02, 0xff, 0xfc, 0x00, 0xff, 0xff, 0x02, 0x02, 0x00, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x02, 0x02, 0xfe, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfd, 0xfd, 0x02, + 0x04, 0x01, 0xfb, 0xff, 0x03, 0x01, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x01, 0xfd, 0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0x01, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xfe, 0xfe, 0x00, 0xff, 0xfd, 0xfe, 0x01, 0x02, 0xfd, 0xfe, 0xff, 0xff, 0xfe, 0xff, + 0x01, 0x02, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0xfe, 0xfe, 0x01, 0x01, 0xff, 0xff, 0x01, 0x01, 0x01, + 0x00, 0xfe, 0x01, 0x01, 0x00, 0x01, 0xff, 0xff, 0x01, 0xfd, 0xff, 0x02, 0x02, 0x02, 0x01, 0x00, 0xfd, 0xff, 0x00, 0xfd, 0x01, 0x02, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xfe, 0x03, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xfe, 0xfe, 0x00, + 0x01, 0xfc, 0x01, 0x02, 0xff, 0xff, 0x00, 0xfe, 0xfd, 0xfe, 0x00, 0x00, 0x01, 0xfe, 0xfe, 0x01, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, + 0xfe, 0xff, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xff, 0x01, 0x00, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x01, 0x01, 0xff, 0xfd, 0xff, 0x00, 0x00, + 0xff, 0x01, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0x01, 0xfd, 0xff, 0x02, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0xfe, + 0xff, 0x00, 0x01, 0x02, 0x03, 0x00, 0xfe, 0xfe, 0xfd, 0x01, 0x03, 0x01, 0x00, 0x02, 0x01, 0xfd, 0xfd, 0x00, 0x03, 0x02, 0x00, 0xfe, + 0xff, 0xfe, 0xff, 0x00, 0xfe, 0x00, 0x02, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0xff, 0xfe, + 0xff, 0x00, 0x00, 0xff, 0xfc, 0xfe, 0xff, 0xfd, 0x02, 0x00, 0xfe, 0x00, 0xff, 0xff, 0x00, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01, + 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0xfd, 0xff, 0x00, 0xfc, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, + 0xff, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x00, + 0xfe, 0x00, 0x02, 0x02, 0x00, 0xfe, 0xfe, 0xff, 0x01, 0x03, 0x02, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0x00, 0x03, 0x02, 0xff, 0xff, + 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x02, 0xfe, 0x00, 0x02, 0x00, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x01, + 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x01, 0xff, 0x02, 0x01, 0xfd, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, 0xfd, 0xfe, 0x00, 0x00, + 0x00, 0xfe, 0xfe, 0x01, 0x00, 0xfd, 0xfe, 0x00, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, 0x01, 0x01, 0xff, 0x01, 0xfe, 0xfe, 0x01, 0x01, 0x00, 0xfe, 0xff, + 0x00, 0x01, 0x01, 0x02, 0xfe, 0xfe, 0x01, 0x00, 0xfe, 0xff, 0x01, 0x00, 0xff, 0xfe, 0x01, 0x00, 0x00, 0x01, 0xff, 0xfd, 0xfd, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0xfe, 0x00, 0xfe, + 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, + 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0xfe, 0x00, 0x01, 0xff, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xfe, 0x01, 0x00, 0xff, 0x01, 0x00, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xff, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0xfd, 0x01, 0x01, 0xff, 0x01, 0x00, 0xff, 0x00, 0x01, + 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x01, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x01, 0x01, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x01, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xfe, 0x01, 0x01, 0xff, 0xfe, 0x01, 0x01, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0xfd, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0xfe, 0xfd, 0xff, 0x01, 0xfe, 0xfe, + 0x00, 0x01, 0xff, 0x00, 0x01, 0xfe, 0xff, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, + 0xfe, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x01, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x01, 0xff, 0x00, 0x00, 0xfe, 0x00, 0x01, 0x01, 0x01, 0xff, 0xff, 0x00, 0xfe, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x01, 0x00, 0x00, 0x01, 0xff, 0xfe, 0xff, 0xff, 0x01, 0x00, 0xfe, 0x01, 0x00, 0xff, 0x01, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, + 0xff, 0xfd, 0x00, 0x02, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0xff, 0xfe, + 0xff, 0xff, 0xff, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xfe, 0xff, + 0x00, 0x01, 0x00, 0xff, 0x00, 0xfe, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0x01, 0x01, 0x00, + 0x00, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x02, 0x01, 0xff, 0xff, 0xfe, 0xfe, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0x00, + 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfd, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x01, 0x00, 0xfe, 0x00, 0x00, 0xff, 0x00, 0x00, 0x01, 0x01, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x01, + 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, +}; + +static const unsigned char RIM_2608[3042] = { + 0x52, 0x49, 0x46, 0x46, 0x90, 0x17, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x44, 0xac, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x02, 0x00, 0x10, 0x00, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x61, 0x61, 0x17, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x04, 0x01, 0xfb, 0xf5, 0xf3, 0xfe, 0x12, 0x1f, 0x0f, + 0xf0, 0xd9, 0xe1, 0xf6, 0x1c, 0x48, 0x63, 0x44, 0x08, 0xef, 0x0a, 0x3e, 0x5f, 0x5e, 0x4d, 0x40, 0x3f, 0x36, 0x2a, 0x10, 0xdc, 0xa0, + 0x80, 0x85, 0x94, 0xa4, 0xaf, 0xb6, 0xb7, 0xba, 0xc2, 0xd1, 0xf2, 0x19, 0x34, 0x2d, 0x1c, 0x09, 0xf8, 0xf5, 0x06, 0x23, 0x37, 0x48, + 0x49, 0x33, 0x0b, 0xed, 0xdb, 0xca, 0xad, 0x9e, 0xb1, 0xd5, 0x0a, 0x39, 0x4c, 0x3e, 0x2e, 0x34, 0x49, 0x56, 0x4f, 0x36, 0x1f, 0xfb, + 0xd3, 0xb6, 0x97, 0x8d, 0x9b, 0xb5, 0xd1, 0xe6, 0xe6, 0xc0, 0xb4, 0xd7, 0x0c, 0x30, 0x21, 0xf5, 0xdd, 0xe1, 0xe9, 0xe3, 0xdf, 0xea, + 0x02, 0x12, 0x03, 0xdf, 0xc6, 0xcc, 0xe6, 0xf1, 0xe8, 0xec, 0x01, 0x1a, 0x1e, 0x19, 0x0e, 0x0e, 0x25, 0x41, 0x4d, 0x41, 0x2f, 0x19, + 0x05, 0xf7, 0xe5, 0xda, 0xda, 0xe3, 0xe4, 0xee, 0x08, 0x21, 0x1a, 0xfe, 0xf1, 0x02, 0x1e, 0x2c, 0x28, 0x1d, 0x12, 0x0c, 0x17, 0x2f, + 0x3c, 0x27, 0x0d, 0x04, 0x0f, 0x15, 0x11, 0x00, 0xea, 0xdd, 0xdf, 0xeb, 0xfc, 0x13, 0x29, 0x38, 0x40, 0x41, 0x39, 0x2a, 0x1c, 0x16, + 0x17, 0x1b, 0x19, 0x11, 0x09, 0x06, 0x00, 0xf7, 0xf1, 0xf5, 0xfb, 0xfc, 0xf9, 0xf3, 0xe7, 0xd8, 0xd9, 0xe6, 0xfa, 0x0c, 0x1c, 0x1a, + 0x06, 0xf3, 0xf0, 0xfb, 0x08, 0x13, 0x17, 0x18, 0x16, 0x13, 0x08, 0xfa, 0xfa, 0x07, 0x15, 0x19, 0x14, 0x0f, 0x11, 0x18, 0x18, 0x14, + 0x16, 0x22, 0x2e, 0x31, 0x29, 0x24, 0x22, 0x1f, 0x17, 0x0b, 0xfc, 0xf0, 0xf0, 0xed, 0xe7, 0xe6, 0xf5, 0x02, 0xff, 0xf1, 0xe9, 0xf1, + 0x03, 0x16, 0x14, 0x02, 0xf1, 0xec, 0xe7, 0xdd, 0xd4, 0xd9, 0xee, 0x03, 0x08, 0xfd, 0xed, 0xe2, 0xe1, 0xe6, 0xea, 0xe7, 0xe5, 0xe5, + 0xe7, 0xec, 0xf6, 0x01, 0x04, 0x00, 0xfa, 0xf5, 0xf0, 0xea, 0xe1, 0xd7, 0xd5, 0xdd, 0xe6, 0xe9, 0xe8, 0xe7, 0xeb, 0xf1, 0xf9, 0x01, + 0x06, 0xfd, 0xeb, 0xdc, 0xd4, 0xd3, 0xda, 0xe4, 0xed, 0xf1, 0xf2, 0xf3, 0xf6, 0xef, 0xe2, 0xde, 0xe5, 0xf0, 0xf7, 0xfb, 0xfc, 0xf9, + 0xf5, 0xf9, 0x04, 0x0e, 0x0c, 0x04, 0xfa, 0xf3, 0xf2, 0xf7, 0xfd, 0x04, 0x04, 0xff, 0xf9, 0xf5, 0xf1, 0xec, 0xe5, 0xe6, 0xf0, 0x01, + 0x10, 0x15, 0x0e, 0x02, 0xfc, 0x00, 0x06, 0x04, 0xfd, 0xf5, 0xef, 0xf0, 0xf7, 0x04, 0x16, 0x21, 0x20, 0x16, 0x0a, 0x04, 0x01, 0xff, + 0xfc, 0xfb, 0xfe, 0x01, 0x08, 0x0c, 0x09, 0x01, 0xfa, 0xf7, 0xfb, 0x04, 0x0c, 0x0e, 0x0d, 0x0e, 0x12, 0x16, 0x13, 0x0a, 0xff, 0xf6, + 0xf5, 0xf8, 0xfd, 0x02, 0x03, 0x03, 0x04, 0x02, 0xfd, 0xff, 0x0a, 0x15, 0x1b, 0x1b, 0x1a, 0x16, 0x14, 0x14, 0x18, 0x18, 0x16, 0x12, + 0x0c, 0x04, 0xfb, 0xf7, 0xfa, 0x03, 0x0d, 0x17, 0x1b, 0x17, 0x0f, 0x06, 0x02, 0x05, 0x0d, 0x12, 0x10, 0x07, 0xfd, 0xf7, 0xf7, 0xfb, + 0x02, 0x0d, 0x18, 0x1e, 0x1e, 0x1c, 0x1b, 0x1c, 0x1b, 0x19, 0x15, 0x10, 0x08, 0x00, 0xfe, 0x01, 0x05, 0x07, 0x08, 0x08, 0x08, 0x05, + 0x05, 0x06, 0x07, 0x07, 0x05, 0x01, 0x00, 0x02, 0x03, 0x02, 0x00, 0xfe, 0xfc, 0xf9, 0xf3, 0xf0, 0xed, 0xec, 0xec, 0xef, 0xf1, 0xf0, + 0xef, 0xec, 0xe5, 0xe0, 0xe1, 0xe6, 0xec, 0xf0, 0xf2, 0xf2, 0xf2, 0xf1, 0xf0, 0xed, 0xef, 0xf5, 0xfd, 0x03, 0x08, 0x0b, 0x0b, 0x0a, + 0x0c, 0x10, 0x14, 0x12, 0x0d, 0x06, 0xfb, 0xef, 0xe6, 0xe1, 0xdd, 0xde, 0xde, 0xdb, 0xdb, 0xdc, 0xde, 0xe0, 0xe4, 0xe9, 0xef, 0xf7, + 0xfa, 0xfc, 0xff, 0x00, 0xfa, 0xf2, 0xef, 0xee, 0xef, 0xf1, 0xf4, 0xf8, 0xfc, 0xfe, 0xfb, 0xf6, 0xf3, 0xf4, 0xf9, 0x00, 0x06, 0x07, + 0x05, 0x04, 0x03, 0x03, 0x01, 0xfd, 0xf8, 0xf5, 0xf2, 0xef, 0xee, 0xf0, 0xf4, 0xf7, 0xfa, 0xfb, 0xf8, 0xf5, 0xf5, 0xf9, 0xfe, 0x00, + 0x01, 0x00, 0x00, 0xfe, 0xf9, 0xf6, 0xf9, 0x01, 0x06, 0x03, 0xfa, 0xf3, 0xf3, 0xf7, 0xf8, 0xf9, 0xfc, 0x00, 0x01, 0x02, 0x05, 0x0a, + 0x0d, 0x0e, 0x0d, 0x0a, 0x04, 0xfd, 0xf8, 0xf6, 0xf6, 0xf7, 0xfc, 0x02, 0x07, 0x07, 0x06, 0x04, 0x03, 0x05, 0x05, 0x03, 0x01, 0x01, + 0x01, 0x00, 0xfe, 0xfd, 0xfc, 0xfc, 0x01, 0x06, 0x0b, 0x0e, 0x12, 0x10, 0x09, 0x06, 0x0a, 0x13, 0x1c, 0x1c, 0x1b, 0x1d, 0x1e, 0x19, + 0x10, 0x0c, 0x0a, 0x0b, 0x09, 0x00, 0xfb, 0xf9, 0xf8, 0xf8, 0xfd, 0x02, 0x03, 0x04, 0x04, 0x04, 0x08, 0x0a, 0x0b, 0x0c, 0x0c, 0x0f, + 0x0f, 0x0a, 0x04, 0x05, 0x08, 0x05, 0x01, 0x00, 0x03, 0x06, 0x05, 0x02, 0x01, 0x02, 0x00, 0xfe, 0x02, 0x07, 0x0b, 0x0a, 0x0a, 0x05, + 0x03, 0x06, 0x07, 0x04, 0x01, 0x05, 0x04, 0x01, 0xfd, 0xfe, 0xfb, 0xf6, 0xf6, 0xfb, 0x02, 0x08, 0x09, 0x09, 0x08, 0x05, 0x02, 0x01, + 0x01, 0x01, 0x05, 0x07, 0x08, 0x0b, 0x0c, 0x0a, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0d, 0x0e, 0x0c, 0x06, 0x01, 0xfc, 0xfa, 0xf7, 0xf7, + 0xf8, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xfb, 0x00, 0x02, 0x02, 0x00, 0xfd, 0xfb, 0xfd, 0xfb, 0xf4, 0xf1, 0xf2, 0xf3, 0xef, 0xed, 0xef, + 0xf1, 0xf0, 0xf0, 0xef, 0xee, 0xed, 0xee, 0xee, 0xec, 0xee, 0xf0, 0xf1, 0xf4, 0xfa, 0xfe, 0x00, 0x01, 0x01, 0x02, 0x04, 0x08, 0x0c, + 0x0b, 0x03, 0xfc, 0xfb, 0x01, 0x06, 0x07, 0x06, 0x04, 0x00, 0xfa, 0xf5, 0xf3, 0xf3, 0xf6, 0xf7, 0xf6, 0xf1, 0xec, 0xee, 0xee, 0xed, + 0xec, 0xed, 0xee, 0xf1, 0xf7, 0xfb, 0xfa, 0xf6, 0xf3, 0xf2, 0xf3, 0xf7, 0xfe, 0xff, 0xff, 0x00, 0x04, 0x07, 0x06, 0x06, 0x08, 0x0b, + 0x0c, 0x0e, 0x0e, 0x0c, 0x08, 0x01, 0xfc, 0xfa, 0xfa, 0xf8, 0xf3, 0xf1, 0xf4, 0xfa, 0x00, 0x01, 0xfe, 0xfa, 0xf9, 0xf7, 0xf9, 0xfc, + 0xfc, 0x00, 0x03, 0x03, 0xff, 0x00, 0x04, 0x07, 0x08, 0x04, 0x02, 0x05, 0x0b, 0x0b, 0x0a, 0x0e, 0x10, 0x10, 0x0d, 0x09, 0x06, 0x07, + 0x0b, 0x0f, 0x11, 0x11, 0x0d, 0x0d, 0x0f, 0x0d, 0x0d, 0x0e, 0x0d, 0x0b, 0x07, 0x02, 0xff, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0xfe, + 0xfd, 0xfe, 0x03, 0x08, 0x09, 0x05, 0x02, 0x03, 0x06, 0x06, 0x04, 0x03, 0x01, 0x02, 0x03, 0x07, 0x07, 0x06, 0x08, 0x0c, 0x0e, 0x0b, + 0x08, 0x08, 0x0b, 0x0c, 0x0c, 0x0b, 0x09, 0x07, 0x07, 0x04, 0xff, 0xfd, 0xfb, 0xf8, 0xf5, 0xf6, 0xf9, 0xfb, 0xf9, 0xf7, 0xf5, 0xf4, + 0xf3, 0xf8, 0xfa, 0xf9, 0xf7, 0xf9, 0xff, 0x03, 0x02, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x02, 0x07, 0x0b, 0x0a, 0x07, 0x04, 0x01, 0xfe, + 0xfe, 0xfe, 0xfc, 0xfb, 0x00, 0x06, 0x08, 0x06, 0x04, 0x04, 0x03, 0xfe, 0xfa, 0xf8, 0xfa, 0xfa, 0xf8, 0xf5, 0xf6, 0xf5, 0xf5, 0xf5, + 0xf6, 0xf6, 0xf6, 0xf9, 0xfd, 0x02, 0x06, 0x06, 0x04, 0x02, 0x02, 0x05, 0x03, 0xff, 0xfe, 0xfd, 0xfd, 0xfc, 0xfb, 0xfa, 0xfd, 0x01, + 0x01, 0xff, 0xff, 0xfe, 0xfb, 0xf8, 0xf7, 0xf6, 0xf8, 0xfb, 0xfc, 0xfc, 0xfb, 0xfa, 0xf8, 0xf9, 0xfa, 0xf8, 0xf6, 0xf6, 0xf8, 0xf8, + 0xf8, 0xf9, 0xfb, 0xfc, 0xfb, 0xfb, 0xfc, 0xff, 0x00, 0x00, 0x01, 0xfd, 0xf7, 0xf4, 0xf3, 0xf3, 0xf3, 0xf5, 0xf5, 0xf4, 0xf3, 0xf7, + 0xf9, 0xfb, 0xfb, 0xf7, 0xf4, 0xf7, 0xfe, 0x01, 0x01, 0xff, 0x00, 0xfd, 0xfc, 0xfd, 0xfc, 0xf9, 0xf8, 0xfc, 0x00, 0x02, 0x02, 0x01, + 0x00, 0x01, 0x03, 0x05, 0x05, 0x03, 0x01, 0xfe, 0xfc, 0xfc, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x02, 0x07, 0x0b, 0x0c, 0x0c, 0x0e, + 0x0c, 0x07, 0x04, 0x03, 0x03, 0x03, 0x05, 0x05, 0x05, 0x05, 0x03, 0xfe, 0xfa, 0xf9, 0xfc, 0x00, 0x00, 0xfd, 0xfa, 0xfa, 0xfd, 0xfe, + 0xfb, 0xfa, 0xfb, 0xfb, 0xfb, 0xfd, 0x02, 0x06, 0x09, 0x09, 0x06, 0x03, 0x01, 0x02, 0x04, 0x07, 0x07, 0x04, 0xfe, 0xf9, 0xf7, 0xf7, + 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x02, 0x05, 0x06, 0x05, 0x05, 0x06, 0x06, 0x06, + 0x08, 0x0b, 0x0b, 0x07, 0x03, 0xff, 0xfc, 0xf9, 0xfa, 0xfd, 0x01, 0x04, 0x04, 0x04, 0x05, 0x07, 0x0a, 0x0a, 0x08, 0x07, 0x08, 0x0a, + 0x0a, 0x06, 0x04, 0x02, 0x04, 0x06, 0x04, 0x04, 0x07, 0x0a, 0x09, 0x08, 0x06, 0x05, 0x07, 0x08, 0x06, 0x02, 0x01, 0x02, 0x02, 0x05, + 0x07, 0x04, 0xfb, 0xf5, 0xf4, 0xf6, 0xf6, 0xf5, 0xf4, 0xf5, 0xf6, 0xf7, 0xf9, 0xfa, 0xf9, 0xf8, 0xf9, 0xfc, 0x00, 0x03, 0x05, 0x06, + 0x07, 0x08, 0x07, 0x02, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xfd, 0xfd, 0x01, 0x04, 0xff, 0xf8, 0xf7, 0xf9, 0xfc, 0xfe, 0xfe, 0xfb, + 0xfb, 0xff, 0x03, 0x01, 0xfd, 0xfc, 0xfd, 0xfd, 0xfd, 0xff, 0xfc, 0xfa, 0xf9, 0xfb, 0xfa, 0xf7, 0xf6, 0xf7, 0xf7, 0xf7, 0xf9, 0xfc, + 0xff, 0x02, 0x02, 0x00, 0xff, 0x00, 0x03, 0x06, 0x06, 0x05, 0x05, 0x06, 0x08, 0x0a, 0x08, 0x05, 0x04, 0x03, 0xfe, 0xf8, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xf9, 0xf7, 0xf8, 0xfa, 0xfc, 0xfc, 0xfc, 0xfa, 0xf8, 0xfa, 0xfe, 0x00, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0xfe, + 0xfd, 0xfc, 0xfa, 0xf8, 0xf8, 0xf5, 0xf4, 0xf5, 0xf8, 0xf5, 0xf5, 0xf9, 0xfd, 0xfc, 0xfb, 0xfa, 0xfa, 0xfa, 0xfb, 0xfd, 0xff, 0xff, + 0xfd, 0xfa, 0xfc, 0xfd, 0xfc, 0xfd, 0x00, 0x01, 0xfe, 0xff, 0x02, 0x03, 0x00, 0xfc, 0xfb, 0xff, 0x02, 0x01, 0xfe, 0xfe, 0x02, 0x06, + 0x08, 0x09, 0x07, 0x05, 0x06, 0x0a, 0x0d, 0x10, 0x11, 0x0e, 0x0b, 0x0a, 0x09, 0x06, 0x05, 0x04, 0x04, 0x03, 0x03, 0x05, 0x06, 0x04, + 0x02, 0xff, 0x01, 0x02, 0x00, 0xff, 0x03, 0x08, 0x0a, 0x0b, 0x09, 0x06, 0x04, 0x03, 0x02, 0x04, 0x09, 0x0b, 0x08, 0x03, 0x00, 0x00, + 0x02, 0x04, 0x06, 0x07, 0x07, 0x07, 0x06, 0x06, 0x05, 0x05, 0x03, 0x00, 0xfd, 0xfe, 0xfe, 0xfb, 0xf8, 0xf7, 0xf7, 0xf8, 0xf9, 0xf9, + 0xfa, 0xfd, 0xfd, 0xfa, 0xfa, 0xfc, 0xfd, 0xfd, 0xfe, 0xfc, 0xf7, 0xf5, 0xfa, 0xff, 0xff, 0xfd, 0xfa, 0xf9, 0xf9, 0xf8, 0xf9, 0xfa, + 0xfa, 0xf8, 0xf6, 0xf6, 0xf9, 0xfd, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x03, 0x06, 0x05, 0x00, 0xfe, 0x00, 0x01, 0xff, 0xfb, 0xf9, + 0xfa, 0xfb, 0xfe, 0x01, 0x03, 0x04, 0x05, 0x05, 0x06, 0x04, 0x00, 0xfa, 0xfa, 0xfb, 0xfe, 0x01, 0x02, 0x03, 0x02, 0xff, 0xfc, 0xfc, + 0xff, 0x03, 0x04, 0x01, 0xfe, 0xfb, 0xfb, 0xfa, 0xfa, 0xfb, 0xfc, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfb, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, + 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0xfa, 0xfb, 0xff, 0x05, 0x08, 0x05, 0xff, 0xfa, 0xf8, 0xfa, 0x00, 0x05, + 0x06, 0x03, 0x01, 0x01, 0x03, 0x06, 0x07, 0x06, 0x05, 0x06, 0x07, 0x08, 0x08, 0x09, 0x09, 0x08, 0x07, 0x06, 0x02, 0xff, 0xfe, 0xff, + 0x01, 0x02, 0x03, 0x03, 0x04, 0x03, 0x02, 0x02, 0x02, 0x03, 0x05, 0x06, 0x06, 0x04, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02, 0x00, 0xfe, + 0xfd, 0x00, 0x05, 0x0a, 0x0a, 0x07, 0x04, 0x04, 0x05, 0x06, 0x06, 0x03, 0x00, 0xff, 0x00, 0xff, 0x00, 0x02, 0x05, 0x03, 0xff, 0xfe, + 0xfe, 0xfd, 0xfc, 0xfb, 0xf9, 0xfa, 0xfa, 0xf7, 0xf5, 0xf6, 0xf7, 0xf8, 0xf8, 0xf9, 0xfc, 0xff, 0x00, 0x00, 0xff, 0xfd, 0xfa, 0xfa, + 0xf9, 0xfb, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0xff, 0xfc, 0xfb, 0xfa, 0xf9, 0xfa, 0xfd, 0x00, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0xff, 0xfd, 0xfb, 0xfb, 0xfa, 0xfa, 0xfb, 0xfc, 0xfb, 0xfa, 0xf9, 0xf9, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfb, 0xfb, 0xfc, 0xff, 0x01, 0x02, 0x02, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xfc, 0xfd, 0xff, 0xff, 0xfe, 0xff, 0x02, + 0x03, 0x04, 0x03, 0x00, 0xfe, 0xff, 0x02, 0x05, 0x05, 0x03, 0x01, 0x00, 0x01, 0x02, 0x03, 0x03, 0x02, 0x00, 0xfc, 0xfb, 0xfd, 0x00, + 0x02, 0x03, 0x02, 0x00, 0xfd, 0xfb, 0xfb, 0xfc, 0xfe, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x03, 0x03, 0xff, 0xfd, 0xfc, 0xfd, + 0xfc, 0xfb, 0xfd, 0x01, 0x03, 0x02, 0x03, 0x04, 0x06, 0x06, 0x03, 0xfe, 0xfb, 0xfd, 0x01, 0x03, 0x01, 0x01, 0x01, 0x02, 0x03, 0x04, + 0x03, 0x00, 0xfb, 0xfc, 0xff, 0x01, 0x00, 0xfe, 0xfd, 0xfe, 0x03, 0x05, 0x03, 0xff, 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x01, 0xfe, 0xfb, 0xfa, 0xfc, 0xff, 0x01, 0x01, 0x01, 0xfe, 0xfc, 0xfb, 0xfd, 0xff, 0xfd, 0xfc, 0xfe, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x03, 0x04, 0x03, 0x01, 0x00, 0x00, 0x01, 0x00, 0xfe, 0xfc, 0xfc, 0xfe, 0x00, 0x00, 0xff, 0xfe, 0xfd, + 0xfe, 0x00, 0x03, 0x02, 0xff, 0xfc, 0xfc, 0xfc, 0xfa, 0xfb, 0xfe, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x03, 0x03, 0x01, 0x01, + 0x02, 0x03, 0x02, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x03, 0x02, 0x00, 0xfe, 0xfe, 0xfd, 0xfc, 0xfb, 0xfd, 0x00, 0x03, 0x04, 0x04, + 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0xfe, 0xfb, 0xfa, 0xf9, 0xfa, 0xfb, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x00, + 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0x02, 0x04, 0x04, 0x02, 0x02, 0x03, 0x02, 0xff, 0xfc, 0xfb, 0xfd, 0x00, 0x02, 0x03, 0x03, 0x03, 0x03, + 0x02, 0xff, 0xff, 0x01, 0x02, 0x02, 0x03, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf9, 0xfb, 0xfe, 0x00, 0x01, 0xff, 0xfd, 0xfc, + 0xfb, 0xf8, 0xf8, 0xf9, 0xfb, 0xfc, 0xfd, 0xfe, 0x00, 0x02, 0x04, 0x05, 0x04, 0x01, 0xff, 0xff, 0xfe, 0x00, 0x03, 0x07, 0x07, 0x04, + 0xff, 0xfc, 0xfd, 0xff, 0x00, 0xfd, 0xf9, 0xf8, 0xfa, 0xfd, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfe, 0x01, 0x03, 0x01, 0xff, 0x00, + 0x02, 0x00, 0xfe, 0xfd, 0xfe, 0x01, 0x04, 0x03, 0x00, 0xfd, 0xfd, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x03, 0x03, 0x02, 0x01, 0x02, 0x01, 0xff, 0xfc, 0xfc, 0xff, 0xff, 0xfe, 0xff, 0x03, 0x02, 0x00, 0xfe, 0x00, 0x01, 0x01, + 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfb, 0xfc, 0xfd, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x01, 0x01, 0x02, 0x03, 0x02, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x03, 0x03, 0x02, 0x00, 0xff, 0xfe, 0x00, 0x02, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, + 0x00, 0xff, 0x00, 0x02, 0x02, 0x00, 0xfe, 0xfe, 0x00, 0x02, 0x02, 0x00, 0xff, 0x00, 0x00, 0xfd, 0xfb, 0xfa, 0xfb, 0xfe, 0x00, 0x01, + 0x01, 0x00, 0xfe, 0xfc, 0xfb, 0xfe, 0x00, 0xfe, 0xfd, 0xfe, 0x01, 0x02, 0x01, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x05, 0x06, 0x04, 0x01, 0xff, 0x00, 0x03, 0x05, 0x03, 0x00, 0xff, 0x00, 0xff, 0xfd, 0xfe, 0xff, 0xff, 0xff, + 0xfe, 0xfc, 0xfc, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0x01, 0x02, 0x03, 0x03, 0x01, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0x01, + 0x02, 0x02, 0x02, 0x03, 0x01, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x01, 0x02, 0x01, 0xfe, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xfe, 0xfe, + 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfd, 0xfb, 0xfb, 0xfc, 0xfe, 0xff, 0x02, 0x03, 0x01, 0xff, 0xff, 0xfe, 0xfc, 0xfa, 0xf9, 0xf9, 0xfc, + 0xfd, 0xff, 0xff, 0xfd, 0xfc, 0xfd, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x00, 0xfe, 0xfe, + 0xff, 0x01, 0x03, 0x03, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xfd, 0xfc, 0xfa, 0xfa, 0xfe, 0x00, 0x01, 0x01, 0x00, + 0xfe, 0xff, 0x02, 0x05, 0x05, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x03, 0x02, 0x00, 0xff, 0x00, 0x02, 0x03, 0x02, + 0x01, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0x02, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, + 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, + 0x00, 0x01, 0x02, 0x01, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfd, 0xfe, 0x00, 0x01, 0x00, 0xff, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0x01, 0x02, 0x01, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, + 0xff, 0xff, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x02, 0x03, 0x03, 0x01, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xfd, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x02, 0x02, 0x01, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0x02, + 0x02, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xfb, 0xfb, 0xfe, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x01, 0x02, 0x02, 0x00, 0xff, 0xfe, 0xfe, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfc, 0xfd, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xfe, 0x00, 0x01, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x01, 0x02, 0x03, 0x02, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x01, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, + 0xff, 0xff, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0xfd, 0xfc, 0xfb, 0xfc, 0xff, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xfd, 0xfe, 0xff, + 0x00, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00, + 0xff, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, + 0x02, 0x02, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0xff, 0xfd, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0xff, + 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, + 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0x01, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const unsigned char SD_2608[10674] = { + 0x52, 0x49, 0x46, 0x46, 0x30, 0x53, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x44, 0xac, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x02, 0x00, 0x10, 0x00, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x61, 0x61, 0x53, 0x00, 0xfe, 0xfe, 0xfd, 0x01, 0x0a, 0x14, 0x1f, 0x26, 0x26, 0x26, 0x24, 0x21, 0x1e, 0x1c, 0x1a, 0x19, + 0x18, 0x18, 0x18, 0x18, 0x1a, 0x1a, 0x19, 0x19, 0x17, 0x15, 0x14, 0x14, 0x12, 0x11, 0x12, 0x0c, 0x0b, 0x0b, 0x07, 0xfc, 0xf8, 0xfb, + 0xf9, 0xee, 0xed, 0xfe, 0x11, 0x0a, 0xf3, 0xe7, 0xff, 0x14, 0x0a, 0xfe, 0xf0, 0xee, 0xf6, 0xf7, 0xf1, 0xf3, 0x07, 0x0b, 0x05, 0xff, + 0xf3, 0xf1, 0xf5, 0xf9, 0xf8, 0xfd, 0x11, 0x2e, 0x4d, 0x64, 0x6b, 0x60, 0x3d, 0x0f, 0xe8, 0xbd, 0xa4, 0xaa, 0xce, 0xfb, 0x23, 0x42, + 0x5a, 0x6b, 0x6d, 0x5b, 0x3d, 0x1f, 0x07, 0xf0, 0xdd, 0xcc, 0xbc, 0xaf, 0xa6, 0x9f, 0x98, 0xa0, 0xaa, 0xad, 0xb4, 0xb7, 0xb9, 0xbb, + 0xbc, 0xb7, 0xaf, 0xad, 0xad, 0xb2, 0xb8, 0xb5, 0xb2, 0xb3, 0xbc, 0xcc, 0xd7, 0xd8, 0xd3, 0xcb, 0xc7, 0xca, 0xd2, 0xdb, 0xde, 0xdd, + 0xd9, 0xdd, 0xe8, 0xf0, 0xf6, 0xf7, 0xf4, 0xf1, 0xf1, 0xf3, 0xf5, 0xf6, 0xf5, 0xf1, 0xf4, 0xf5, 0xf1, 0xea, 0xe6, 0xdf, 0xdd, 0xe7, + 0xf3, 0xf7, 0xf4, 0xf4, 0xec, 0xe0, 0xd9, 0xd5, 0xd5, 0xdb, 0xe5, 0xed, 0xf4, 0xfc, 0x03, 0xfa, 0xe7, 0xd4, 0xc3, 0xb2, 0xa6, 0xa7, + 0xaf, 0xbc, 0xcd, 0xe3, 0xfb, 0x0e, 0x16, 0x13, 0x06, 0xf6, 0xe4, 0xd2, 0xc0, 0xb0, 0xa5, 0x9e, 0xa1, 0xb0, 0xca, 0xe2, 0xf4, 0x03, + 0x0c, 0x0e, 0x0b, 0x0e, 0x0e, 0x09, 0x07, 0xfd, 0xfa, 0xfc, 0xf6, 0xe6, 0xe2, 0xf4, 0xfb, 0x00, 0x0b, 0x19, 0x22, 0x23, 0x22, 0x2c, + 0x31, 0x31, 0x34, 0x37, 0x3d, 0x47, 0x52, 0x5a, 0x61, 0x6a, 0x6d, 0x70, 0x75, 0x7b, 0x7c, 0x7a, 0x77, 0x71, 0x6f, 0x70, 0x72, 0x77, + 0x7b, 0x7c, 0x7e, 0x7e, 0x7e, 0x7d, 0x7b, 0x77, 0x72, 0x6d, 0x67, 0x62, 0x60, 0x5e, 0x5e, 0x5d, 0x5a, 0x57, 0x53, 0x4f, 0x4b, 0x46, + 0x41, 0x3b, 0x36, 0x38, 0x38, 0x33, 0x30, 0x34, 0x3a, 0x3b, 0x3d, 0x3c, 0x39, 0x35, 0x30, 0x2c, 0x28, 0x22, 0x23, 0x27, 0x24, 0x27, + 0x2b, 0x2b, 0x25, 0x2b, 0x33, 0x32, 0x2e, 0x2b, 0x2a, 0x27, 0x26, 0x26, 0x24, 0x25, 0x24, 0x26, 0x2d, 0x2a, 0x25, 0x22, 0x1e, 0x1c, + 0x1e, 0x22, 0x23, 0x22, 0x1f, 0x1b, 0x16, 0x11, 0x0e, 0x0b, 0x08, 0x06, 0x04, 0x01, 0xfc, 0xf6, 0xf2, 0xee, 0xeb, 0xe7, 0xe3, 0xdd, + 0xd8, 0xd4, 0xd1, 0xcd, 0xc8, 0xc4, 0xbe, 0xbb, 0xba, 0xbb, 0xb9, 0xb9, 0xb9, 0xb4, 0xb2, 0xb2, 0xb2, 0xb4, 0xb5, 0xb4, 0xb2, 0xad, + 0xa7, 0xa1, 0x99, 0x95, 0x95, 0x98, 0x9c, 0x9f, 0xa2, 0xa4, 0xa3, 0xa1, 0x9d, 0x97, 0x91, 0x8a, 0x84, 0x83, 0x85, 0x87, 0x8b, 0x8f, + 0x94, 0x99, 0x9e, 0x9e, 0x99, 0x92, 0x8b, 0x85, 0x81, 0x80, 0x82, 0x87, 0x89, 0x8b, 0x8c, 0x8e, 0x92, 0x90, 0x8f, 0x92, 0x94, 0x93, + 0x93, 0x9a, 0x9b, 0x9b, 0x9c, 0x98, 0x9d, 0xa0, 0xa0, 0xa5, 0xb1, 0xbc, 0xc1, 0xc4, 0xca, 0xd3, 0xdd, 0xe2, 0xe1, 0xdf, 0xd9, 0xd5, + 0xd4, 0xd5, 0xd2, 0xd1, 0xd8, 0xe5, 0xf3, 0xfe, 0x05, 0x0b, 0x0e, 0x10, 0x13, 0x17, 0x1c, 0x21, 0x25, 0x2d, 0x36, 0x3e, 0x49, 0x4d, + 0x4a, 0x44, 0x46, 0x49, 0x47, 0x4c, 0x4f, 0x50, 0x53, 0x57, 0x5a, 0x5d, 0x61, 0x60, 0x59, 0x53, 0x53, 0x5a, 0x64, 0x68, 0x63, 0x59, + 0x52, 0x4e, 0x4e, 0x52, 0x55, 0x52, 0x4c, 0x4a, 0x4d, 0x51, 0x50, 0x4b, 0x44, 0x43, 0x45, 0x44, 0x3d, 0x37, 0x32, 0x2d, 0x2b, 0x31, + 0x3a, 0x3d, 0x42, 0x46, 0x46, 0x3e, 0x3c, 0x44, 0x4a, 0x48, 0x40, 0x38, 0x38, 0x40, 0x48, 0x48, 0x46, 0x46, 0x48, 0x4c, 0x4d, 0x51, + 0x5a, 0x60, 0x5e, 0x57, 0x54, 0x56, 0x5b, 0x5c, 0x51, 0x47, 0x41, 0x3f, 0x46, 0x4f, 0x53, 0x4d, 0x48, 0x45, 0x46, 0x47, 0x4d, 0x4e, + 0x47, 0x3f, 0x42, 0x49, 0x49, 0x3c, 0x28, 0x1e, 0x26, 0x2b, 0x25, 0x1c, 0x16, 0x13, 0x14, 0x15, 0x15, 0x16, 0x17, 0x0f, 0x09, 0x04, + 0xfd, 0xfa, 0xfb, 0xf9, 0xef, 0xe1, 0xd5, 0xd6, 0xde, 0xe5, 0xdf, 0xce, 0xb8, 0xae, 0xb4, 0xbf, 0xc3, 0xc5, 0xc8, 0xc1, 0xb8, 0xb2, + 0xb2, 0xb2, 0xb0, 0xac, 0xa8, 0xa7, 0xa8, 0xab, 0xb1, 0xc0, 0xca, 0xbd, 0xad, 0xa4, 0xa6, 0xb0, 0xbb, 0xbe, 0xb7, 0xb2, 0xb1, 0xb4, + 0xbb, 0xb8, 0xad, 0xa2, 0x9f, 0xa4, 0xb1, 0xbf, 0xbd, 0xb4, 0xad, 0xae, 0xb6, 0xc1, 0xc7, 0xc3, 0xb8, 0xae, 0xab, 0xb5, 0xc6, 0xd4, + 0xdf, 0xd7, 0xcb, 0xca, 0xd4, 0xd9, 0xd7, 0xd3, 0xc7, 0xbd, 0xbc, 0xc2, 0xc5, 0xc6, 0xc7, 0xc6, 0xc4, 0xc2, 0xc6, 0xc6, 0xcb, 0xd2, + 0xd1, 0xd2, 0xd5, 0xd8, 0xd0, 0xcc, 0xd1, 0xdb, 0xe2, 0xe7, 0xea, 0xdf, 0xd4, 0xce, 0xcb, 0xc7, 0xc8, 0xcf, 0xd9, 0xe8, 0xf3, 0xf6, + 0xf0, 0xef, 0xf1, 0xee, 0xeb, 0xeb, 0xef, 0xf1, 0xf0, 0xed, 0xea, 0xeb, 0xf4, 0xff, 0x05, 0x01, 0xff, 0xff, 0xf5, 0xfe, 0x13, 0x25, + 0x31, 0x31, 0x24, 0x13, 0x0f, 0x1a, 0x28, 0x2b, 0x22, 0x1b, 0x21, 0x2e, 0x37, 0x38, 0x34, 0x3c, 0x4a, 0x51, 0x4a, 0x38, 0x2e, 0x37, + 0x42, 0x4b, 0x55, 0x62, 0x59, 0x46, 0x39, 0x37, 0x3e, 0x48, 0x4e, 0x49, 0x3e, 0x33, 0x2a, 0x2d, 0x38, 0x44, 0x46, 0x4c, 0x53, 0x4f, + 0x46, 0x3b, 0x34, 0x33, 0x38, 0x3f, 0x45, 0x49, 0x45, 0x3a, 0x33, 0x35, 0x3e, 0x47, 0x49, 0x40, 0x36, 0x34, 0x33, 0x2f, 0x2c, 0x2e, + 0x36, 0x39, 0x33, 0x27, 0x24, 0x2c, 0x2f, 0x29, 0x1d, 0x15, 0x1c, 0x23, 0x22, 0x1c, 0x1c, 0x26, 0x31, 0x34, 0x22, 0x11, 0x0a, 0x03, + 0x00, 0x05, 0x0c, 0x0e, 0x07, 0x02, 0x08, 0x1a, 0x25, 0x23, 0x18, 0x0e, 0x0a, 0x08, 0x05, 0x06, 0x0b, 0x0c, 0x0a, 0x05, 0xff, 0xf7, + 0xf0, 0xec, 0xe9, 0xee, 0xf3, 0xf6, 0xfd, 0x02, 0x05, 0x08, 0x0b, 0x06, 0xfe, 0xfb, 0xf7, 0xf6, 0xf2, 0xed, 0xe7, 0xe8, 0xf0, 0xfa, + 0xfe, 0xfe, 0xff, 0xfe, 0xfb, 0xfa, 0x00, 0x03, 0xff, 0xf5, 0xe5, 0xe2, 0xe7, 0xe5, 0xe2, 0xe6, 0xef, 0xed, 0xeb, 0xea, 0xec, 0xec, + 0xf1, 0xf5, 0xf4, 0xef, 0xe5, 0xd7, 0xcc, 0xc4, 0xc3, 0xcb, 0xda, 0xd9, 0xcb, 0xc3, 0xbc, 0xb7, 0xb4, 0xb5, 0xb3, 0xb4, 0xbe, 0xb7, + 0xb7, 0xc4, 0xd2, 0xca, 0xc2, 0xc7, 0xbf, 0xb8, 0xb4, 0xb2, 0xaf, 0xb3, 0xb8, 0xb4, 0xb9, 0xc2, 0xc6, 0xc0, 0xbb, 0xbb, 0xba, 0xb8, + 0xb8, 0xb7, 0xaf, 0xac, 0xaf, 0xb3, 0xbb, 0xbc, 0xba, 0xbd, 0xca, 0xd1, 0xd2, 0xdb, 0xe5, 0xee, 0xf1, 0xee, 0xe5, 0xde, 0xe2, 0xe6, + 0xe5, 0xde, 0xd8, 0xdc, 0xe8, 0xf2, 0xee, 0xe3, 0xdb, 0xdf, 0xe6, 0xf8, 0x10, 0x1c, 0x18, 0x0a, 0x00, 0x06, 0x11, 0x16, 0x14, 0x16, + 0x1a, 0x1a, 0x14, 0x12, 0x10, 0x0a, 0x04, 0x04, 0x0c, 0x15, 0x1a, 0x1a, 0x19, 0x1d, 0x20, 0x22, 0x27, 0x2a, 0x24, 0x1b, 0x22, 0x2c, + 0x31, 0x30, 0x35, 0x34, 0x2c, 0x25, 0x16, 0x0c, 0x14, 0x30, 0x3a, 0x33, 0x2a, 0x25, 0x24, 0x26, 0x2a, 0x29, 0x2a, 0x2b, 0x25, 0x1c, + 0x1a, 0x26, 0x2e, 0x34, 0x37, 0x36, 0x3b, 0x44, 0x46, 0x3a, 0x2c, 0x22, 0x20, 0x23, 0x20, 0x18, 0x16, 0x19, 0x1e, 0x25, 0x2f, 0x3a, + 0x40, 0x3d, 0x3c, 0x42, 0x49, 0x46, 0x3f, 0x3a, 0x3b, 0x40, 0x3f, 0x32, 0x1f, 0x15, 0x11, 0x0e, 0x0d, 0x13, 0x1d, 0x28, 0x2d, 0x33, + 0x3c, 0x49, 0x46, 0x3c, 0x33, 0x2c, 0x25, 0x24, 0x29, 0x28, 0x1f, 0x12, 0x0c, 0x0f, 0x19, 0x22, 0x26, 0x22, 0x1e, 0x1d, 0x1b, 0x14, + 0x07, 0xfc, 0xfe, 0x08, 0x15, 0x20, 0x26, 0x21, 0x14, 0x0e, 0x10, 0x16, 0x1b, 0x14, 0x07, 0xf9, 0xe7, 0xe1, 0xe7, 0xf1, 0xf7, 0xfd, + 0x03, 0x08, 0x05, 0xfc, 0xf5, 0xf0, 0xf1, 0xf1, 0xe9, 0xdb, 0xd5, 0xd7, 0xcf, 0xd0, 0xd8, 0xe2, 0xf0, 0xfa, 0xf7, 0xe7, 0xe4, 0xe6, + 0xe5, 0xdf, 0xdb, 0xd9, 0xd6, 0xd2, 0xce, 0xcd, 0xcd, 0xd5, 0xdc, 0xdd, 0xd8, 0xdc, 0xe1, 0xd8, 0xd8, 0xd8, 0xd7, 0xdc, 0xdc, 0xd4, + 0xc7, 0xbb, 0xb4, 0xb5, 0xbe, 0xc4, 0xc3, 0xbd, 0xba, 0xc2, 0xd0, 0xd7, 0xd0, 0xc6, 0xbd, 0xb5, 0xb7, 0xc2, 0xce, 0xd3, 0xc5, 0xb2, + 0xaa, 0xb5, 0xc0, 0xbf, 0xb8, 0xb0, 0xab, 0xa7, 0xa5, 0xae, 0xbc, 0xc7, 0xcf, 0xd3, 0xd2, 0xcb, 0xc9, 0xcb, 0xd0, 0xd4, 0xd3, 0xcf, + 0xcd, 0xd3, 0xd6, 0xd1, 0xcf, 0xcd, 0xd1, 0xda, 0xe1, 0xe8, 0xef, 0xf5, 0xf7, 0xf8, 0xfb, 0xfc, 0x01, 0x01, 0xf8, 0xea, 0xec, 0xf9, + 0x04, 0x07, 0x01, 0xf8, 0xf6, 0xfb, 0x06, 0x10, 0x15, 0x10, 0x0b, 0x12, 0x15, 0x16, 0x14, 0x0f, 0x08, 0x0a, 0x17, 0x1b, 0x19, 0x1c, + 0x28, 0x34, 0x32, 0x23, 0x19, 0x1b, 0x22, 0x21, 0x27, 0x29, 0x29, 0x2c, 0x35, 0x36, 0x2e, 0x2b, 0x2c, 0x35, 0x41, 0x41, 0x35, 0x28, + 0x26, 0x28, 0x32, 0x41, 0x43, 0x48, 0x4f, 0x50, 0x46, 0x3a, 0x32, 0x2a, 0x25, 0x2c, 0x3d, 0x44, 0x44, 0x3d, 0x36, 0x36, 0x3e, 0x47, + 0x45, 0x43, 0x3c, 0x2d, 0x2d, 0x2a, 0x25, 0x29, 0x32, 0x38, 0x3a, 0x35, 0x35, 0x31, 0x1f, 0x0e, 0x08, 0x11, 0x21, 0x2a, 0x26, 0x1d, + 0x24, 0x2b, 0x2a, 0x21, 0x16, 0x11, 0x15, 0x1d, 0x1e, 0x17, 0x0f, 0x0a, 0x10, 0x1d, 0x29, 0x28, 0x22, 0x1d, 0x17, 0x17, 0x1d, 0x24, + 0x17, 0x0e, 0x0d, 0x0a, 0x0b, 0x14, 0x1d, 0x17, 0x0e, 0x07, 0x04, 0x15, 0x1d, 0x18, 0x13, 0x0b, 0xfe, 0xf1, 0xf0, 0xff, 0x0e, 0x0d, + 0x01, 0xfc, 0xfc, 0xf3, 0xea, 0xe8, 0xee, 0xf2, 0xf8, 0xfa, 0xf0, 0xdf, 0xd0, 0xcb, 0xd4, 0xe9, 0xf8, 0xf5, 0xe8, 0xde, 0xda, 0xdb, + 0xd2, 0xcd, 0xd1, 0xd1, 0xcd, 0xcc, 0xcf, 0xcb, 0xca, 0xcd, 0xcf, 0xd8, 0xde, 0xdb, 0xd4, 0xcb, 0xbd, 0xae, 0xb7, 0xc3, 0xca, 0xcd, + 0xd1, 0xd1, 0xcb, 0xc8, 0xca, 0xcd, 0xcd, 0xc9, 0xc7, 0xca, 0xca, 0xc8, 0xc6, 0xc8, 0xcd, 0xca, 0xc8, 0xd0, 0xd8, 0xdb, 0xd9, 0xd8, + 0xd3, 0xce, 0xcc, 0xcb, 0xcd, 0xd2, 0xd7, 0xd6, 0xca, 0xbe, 0xc8, 0xd8, 0xe0, 0xdb, 0xd0, 0xcc, 0xd1, 0xd6, 0xd8, 0xd2, 0xca, 0xcd, + 0xd5, 0xe1, 0xef, 0xf9, 0xf7, 0xee, 0xe9, 0xec, 0xf3, 0xf6, 0xf1, 0xf0, 0xf0, 0xec, 0xe5, 0xe4, 0xea, 0xf2, 0xfc, 0x02, 0x01, 0xfb, + 0xf9, 0xf9, 0xf8, 0xfa, 0x04, 0x0d, 0x08, 0x01, 0xff, 0x03, 0x0e, 0x18, 0x1a, 0x15, 0x12, 0x13, 0x15, 0x13, 0x08, 0x06, 0x14, 0x1e, + 0x1e, 0x19, 0x15, 0x1a, 0x1c, 0x1a, 0x1c, 0x2b, 0x39, 0x39, 0x38, 0x37, 0x32, 0x2a, 0x29, 0x32, 0x3f, 0x42, 0x38, 0x32, 0x3b, 0x43, + 0x40, 0x33, 0x26, 0x2b, 0x3c, 0x4c, 0x51, 0x4f, 0x4a, 0x45, 0x42, 0x40, 0x3c, 0x39, 0x2a, 0x25, 0x30, 0x37, 0x36, 0x33, 0x3a, 0x35, + 0x38, 0x41, 0x42, 0x38, 0x2b, 0x25, 0x24, 0x28, 0x2d, 0x30, 0x2d, 0x1f, 0x10, 0x15, 0x21, 0x2b, 0x2e, 0x27, 0x1e, 0x18, 0x13, 0x05, + 0x01, 0x0d, 0x18, 0x1e, 0x1e, 0x18, 0x09, 0x05, 0x0a, 0x0a, 0x08, 0x01, 0xfa, 0xf4, 0xfc, 0x09, 0x0c, 0x08, 0x04, 0x04, 0x02, 0x01, + 0xfe, 0xf9, 0xf5, 0xfb, 0x08, 0x13, 0x15, 0x06, 0xf0, 0xe2, 0xe6, 0xf1, 0xf8, 0xf5, 0xf1, 0xef, 0xec, 0xef, 0xf1, 0xf1, 0xf5, 0x03, + 0x0c, 0x03, 0xf1, 0xe5, 0xe7, 0xee, 0xf1, 0xeb, 0xe4, 0xe6, 0xf0, 0xf3, 0xeb, 0xe9, 0xf0, 0xf9, 0xfa, 0xfc, 0xfc, 0xf3, 0xde, 0xd3, + 0xd6, 0xdf, 0xe3, 0xe4, 0xe6, 0xe9, 0xf4, 0xfa, 0xf5, 0xee, 0xeb, 0xec, 0xef, 0xf4, 0xf8, 0xf4, 0xe6, 0xdc, 0xdc, 0xe3, 0xeb, 0xee, + 0xe7, 0xd8, 0xd3, 0xd4, 0xd5, 0xd7, 0xd4, 0xd1, 0xcf, 0xd1, 0xd0, 0xd5, 0xe6, 0xe9, 0xdb, 0xca, 0xc9, 0xd0, 0xdd, 0xe7, 0xde, 0xcd, + 0xc3, 0xc7, 0xd0, 0xd9, 0xdf, 0xe2, 0xe2, 0xe2, 0xdf, 0xd4, 0xcf, 0xd6, 0xe5, 0xe6, 0xdc, 0xd5, 0xd7, 0xdc, 0xdc, 0xd5, 0xd4, 0xd5, + 0xd6, 0xdb, 0xe5, 0xf3, 0xff, 0x02, 0xfc, 0xf4, 0xf0, 0xef, 0xe8, 0xdf, 0xe3, 0xe1, 0xe0, 0xe5, 0xf4, 0x00, 0x01, 0xfa, 0x00, 0x05, + 0x02, 0xfc, 0xf7, 0xf3, 0xf2, 0x05, 0x11, 0x0e, 0x09, 0xfb, 0xf9, 0x09, 0x14, 0x0f, 0x08, 0x0d, 0x0e, 0x13, 0x1a, 0x18, 0x1a, 0x1a, + 0x13, 0x10, 0x15, 0x19, 0x15, 0x0c, 0x07, 0x0c, 0x16, 0x26, 0x32, 0x32, 0x29, 0x1e, 0x15, 0x13, 0x16, 0x1d, 0x22, 0x23, 0x20, 0x1b, + 0x18, 0x1c, 0x25, 0x2c, 0x2a, 0x27, 0x1f, 0x17, 0x15, 0x1a, 0x1d, 0x1f, 0x26, 0x2b, 0x2a, 0x27, 0x1b, 0x17, 0x21, 0x28, 0x23, 0x1d, + 0x20, 0x1e, 0x1b, 0x15, 0x0d, 0x05, 0x05, 0x0c, 0x10, 0x15, 0x1c, 0x24, 0x23, 0x1d, 0x16, 0x0e, 0x0e, 0x13, 0x16, 0x0d, 0x0c, 0x11, + 0x17, 0x1d, 0x28, 0x2f, 0x29, 0x1d, 0x18, 0x18, 0x13, 0x0b, 0x08, 0x0e, 0x1c, 0x1d, 0x13, 0x16, 0x13, 0x10, 0x0d, 0x0d, 0x06, 0x02, + 0x09, 0x0c, 0x11, 0x16, 0x16, 0x1a, 0x1e, 0x1c, 0x16, 0x10, 0x0c, 0x06, 0x07, 0x05, 0xff, 0x02, 0x03, 0x07, 0x0f, 0x18, 0x19, 0x13, + 0x0b, 0x05, 0xff, 0xfc, 0xff, 0x06, 0x08, 0x00, 0xf9, 0xfc, 0x08, 0x14, 0x0e, 0x01, 0xfb, 0xf7, 0xf4, 0xf3, 0xf6, 0xfb, 0xff, 0xfd, + 0xf7, 0xfa, 0xff, 0xfe, 0x00, 0xff, 0xfa, 0xf4, 0xf1, 0xef, 0xee, 0xef, 0xed, 0xe5, 0xdb, 0xe4, 0xf5, 0xfd, 0xf3, 0xe9, 0xe5, 0xe4, + 0xe1, 0xe1, 0xe1, 0xe2, 0xe8, 0xeb, 0xe6, 0xdc, 0xd8, 0xd2, 0xc8, 0xc7, 0xcd, 0xd6, 0xdb, 0xe0, 0xdf, 0xd9, 0xda, 0xd5, 0xd1, 0xd2, + 0xd2, 0xcf, 0xd0, 0xdb, 0xe2, 0xe2, 0xdd, 0xd5, 0xd6, 0xdc, 0xdf, 0xe3, 0xe4, 0xe1, 0xdc, 0xd9, 0xd9, 0xdf, 0xe5, 0xe9, 0xe8, 0xe6, + 0xe3, 0xe4, 0xe4, 0xdd, 0xda, 0xe2, 0xf2, 0xfe, 0xff, 0xf2, 0xdd, 0xd3, 0xda, 0xe9, 0xf5, 0xf8, 0xfb, 0xfd, 0xf0, 0xe6, 0xe6, 0xef, + 0xf7, 0xfa, 0xf8, 0xf6, 0xf9, 0xf9, 0xf5, 0x03, 0x05, 0x00, 0x05, 0x01, 0xfe, 0xfa, 0xee, 0xe3, 0xe2, 0xeb, 0xef, 0xf2, 0xf5, 0xf5, + 0xfb, 0x07, 0x14, 0x14, 0x12, 0x0e, 0x09, 0x08, 0x08, 0x08, 0x05, 0x04, 0x03, 0x02, 0x05, 0x0a, 0x0d, 0x0c, 0x0b, 0x10, 0x16, 0x18, + 0x16, 0x11, 0x0c, 0x09, 0x0a, 0x13, 0x23, 0x27, 0x22, 0x1d, 0x1d, 0x1b, 0x1a, 0x1d, 0x1a, 0x1e, 0x21, 0x1b, 0x17, 0x1b, 0x20, 0x17, + 0x14, 0x16, 0x1c, 0x29, 0x29, 0x1b, 0x0e, 0x16, 0x21, 0x26, 0x2d, 0x2a, 0x24, 0x23, 0x27, 0x28, 0x25, 0x27, 0x23, 0x1d, 0x17, 0x14, + 0x13, 0x16, 0x1c, 0x27, 0x2c, 0x26, 0x1a, 0x1b, 0x1e, 0x1b, 0x1b, 0x1b, 0x20, 0x27, 0x1d, 0x10, 0x12, 0x2c, 0x2f, 0x24, 0x1d, 0x18, + 0x0d, 0x07, 0x0f, 0x17, 0x16, 0x0f, 0x0e, 0x11, 0x1b, 0x24, 0x1d, 0x13, 0x0f, 0x10, 0x13, 0x10, 0x06, 0xff, 0xff, 0x08, 0x11, 0x0e, + 0x06, 0x04, 0x0e, 0x0e, 0x08, 0x02, 0xff, 0x00, 0x03, 0x06, 0x0a, 0x06, 0xf9, 0xf0, 0xee, 0xf2, 0xf8, 0xf7, 0xf6, 0xf8, 0xfe, 0x03, + 0x04, 0x02, 0xfe, 0xf5, 0xef, 0xf1, 0xef, 0xf0, 0xf3, 0xf4, 0xe8, 0xe1, 0xe3, 0xe6, 0xea, 0xec, 0xea, 0xe1, 0xdf, 0xe2, 0xe2, 0xda, + 0xd9, 0xe1, 0xe7, 0xe5, 0xe4, 0xec, 0xf0, 0xef, 0xed, 0xef, 0xee, 0xea, 0xe9, 0xed, 0xe8, 0xde, 0xd6, 0xd6, 0xd2, 0xd0, 0xde, 0xe6, + 0xea, 0xeb, 0xeb, 0xe8, 0xe8, 0xeb, 0xe3, 0xde, 0xe0, 0xe4, 0xe6, 0xe3, 0xda, 0xd0, 0xcf, 0xd7, 0xe0, 0xe4, 0xe3, 0xe2, 0xe0, 0xe0, + 0xdb, 0xd6, 0xd7, 0xdf, 0xe7, 0xe8, 0xe8, 0xea, 0xee, 0xf0, 0xea, 0xe1, 0xdc, 0xd7, 0xdc, 0xe6, 0xec, 0xe9, 0xe3, 0xdf, 0xdc, 0xd7, + 0xd6, 0xde, 0xe9, 0xee, 0xed, 0xed, 0xf5, 0xf7, 0xed, 0xe3, 0xda, 0xdc, 0xe9, 0xf1, 0xf7, 0xfc, 0xfc, 0xfa, 0xfd, 0x03, 0x00, 0xfc, + 0xf9, 0xf9, 0xfa, 0xfd, 0x01, 0x03, 0x07, 0x08, 0x07, 0x06, 0x05, 0x06, 0x0a, 0x0e, 0x13, 0x16, 0x14, 0x0f, 0x0f, 0x17, 0x1c, 0x1f, + 0x1d, 0x10, 0x00, 0xf9, 0xfe, 0x0e, 0x1d, 0x25, 0x27, 0x27, 0x25, 0x23, 0x1f, 0x24, 0x28, 0x23, 0x19, 0x0d, 0x05, 0x08, 0x14, 0x27, + 0x36, 0x38, 0x33, 0x2c, 0x24, 0x1e, 0x1d, 0x1f, 0x1f, 0x21, 0x1f, 0x1d, 0x20, 0x2a, 0x30, 0x2b, 0x24, 0x1f, 0x1c, 0x18, 0x1d, 0x21, + 0x20, 0x1e, 0x1c, 0x1f, 0x27, 0x2e, 0x29, 0x1f, 0x1b, 0x1a, 0x18, 0x15, 0x12, 0x17, 0x1b, 0x18, 0x13, 0x19, 0x1e, 0x13, 0x16, 0x1d, + 0x1d, 0x16, 0x12, 0x11, 0x0e, 0x0c, 0x09, 0x08, 0x0b, 0x0f, 0x10, 0x0c, 0x04, 0x03, 0x0b, 0x15, 0x18, 0x10, 0x07, 0x05, 0x09, 0x0f, + 0x15, 0x19, 0x16, 0x0d, 0x05, 0xfd, 0xf8, 0xf9, 0xfe, 0xf8, 0xf4, 0xfa, 0x03, 0x08, 0x08, 0x07, 0xfe, 0xf8, 0xf6, 0xf2, 0xf4, 0xf9, + 0xfb, 0xed, 0xea, 0xf4, 0xfa, 0xfa, 0xf5, 0xf1, 0xf0, 0xed, 0xea, 0xe9, 0xf1, 0xf1, 0xeb, 0xe5, 0xe2, 0xe1, 0xe2, 0xe8, 0xea, 0xe6, + 0xdf, 0xd7, 0xda, 0xe3, 0xe9, 0xe7, 0xe4, 0xe5, 0xe6, 0xe3, 0xe2, 0xe7, 0xef, 0xed, 0xe4, 0xdf, 0xdc, 0xda, 0xdc, 0xe6, 0xeb, 0xe8, + 0xdf, 0xda, 0xdb, 0xe1, 0xea, 0xe8, 0xe1, 0xde, 0xe5, 0xe4, 0xdd, 0xdb, 0xe1, 0xe3, 0xdf, 0xe2, 0xe4, 0xe6, 0xe7, 0xe4, 0xe2, 0xe2, + 0xe5, 0xe6, 0xe8, 0xed, 0xed, 0xea, 0xe6, 0xe4, 0xe1, 0xe0, 0xe5, 0xf1, 0xf7, 0xf5, 0xf0, 0xeb, 0xe3, 0xe0, 0xe5, 0xf3, 0xf9, 0xf6, + 0xf3, 0xf3, 0xf3, 0xf2, 0xf1, 0xf0, 0xf1, 0xf9, 0xfd, 0xfc, 0xf9, 0xf4, 0xf5, 0xfa, 0xfd, 0xfd, 0xfd, 0x01, 0x0a, 0x17, 0x1c, 0x18, + 0x13, 0x0a, 0x02, 0xfe, 0xf9, 0xfc, 0x08, 0x14, 0x16, 0x14, 0x12, 0x11, 0x10, 0x10, 0x10, 0x0c, 0x08, 0x08, 0x0d, 0x16, 0x20, 0x22, + 0x13, 0x0b, 0x0c, 0x0f, 0x14, 0x19, 0x1a, 0x10, 0x03, 0xfc, 0x03, 0x1b, 0x26, 0x20, 0x19, 0x14, 0x13, 0x15, 0x18, 0x1f, 0x23, 0x21, + 0x18, 0x17, 0x1c, 0x1a, 0x1a, 0x1d, 0x1d, 0x15, 0x10, 0x11, 0x17, 0x21, 0x22, 0x1d, 0x1f, 0x28, 0x2b, 0x23, 0x17, 0x13, 0x16, 0x1a, + 0x1d, 0x1c, 0x17, 0x12, 0x0d, 0x08, 0x09, 0x15, 0x26, 0x2a, 0x1e, 0x11, 0x12, 0x1b, 0x1b, 0x1c, 0x1a, 0x15, 0x0e, 0x04, 0xf9, 0xf3, + 0xfd, 0x0d, 0x17, 0x17, 0x0e, 0x04, 0x04, 0x0d, 0x0e, 0x08, 0x05, 0x01, 0xfa, 0xf3, 0xf4, 0xf8, 0xff, 0x06, 0x05, 0xfb, 0xf1, 0xf1, + 0xf3, 0xf7, 0xfa, 0xf9, 0xf7, 0xf8, 0xfe, 0xfb, 0xf8, 0xfb, 0xff, 0xfc, 0xfa, 0xfc, 0xf7, 0xf1, 0xef, 0xf5, 0xfc, 0x03, 0x04, 0xfb, + 0xf6, 0xf4, 0xf0, 0xe6, 0xe1, 0xe6, 0xf1, 0xf7, 0xf6, 0xf4, 0xfa, 0x02, 0x07, 0x05, 0xfd, 0xf5, 0xf2, 0xf5, 0xf3, 0xee, 0xe8, 0xe2, + 0xe0, 0xe4, 0xea, 0xec, 0xf2, 0xf9, 0xf8, 0xf0, 0xe9, 0xea, 0xf1, 0xf7, 0xf6, 0xf0, 0xe5, 0xdf, 0xde, 0xde, 0xe0, 0xea, 0xf6, 0xf7, + 0xf0, 0xe4, 0xdc, 0xd8, 0xde, 0xe7, 0xec, 0xef, 0xf2, 0xf7, 0xfa, 0xfe, 0xff, 0xfc, 0xf4, 0xee, 0xed, 0xf2, 0xf5, 0xf8, 0xfa, 0xf9, + 0xfa, 0xfa, 0xfb, 0xf7, 0xfa, 0x00, 0xfe, 0xf6, 0xed, 0xe5, 0xe7, 0xe8, 0xec, 0xfa, 0x03, 0xfd, 0xf1, 0xf2, 0xf2, 0xf5, 0xfd, 0xfe, + 0xfb, 0xf5, 0xf0, 0xf0, 0xf9, 0x04, 0x09, 0xfe, 0xeb, 0xe2, 0xe8, 0xf4, 0xfd, 0x03, 0x01, 0xfd, 0xfb, 0x04, 0x06, 0x00, 0xfc, 0xfe, + 0x01, 0xff, 0xfd, 0xfa, 0xf8, 0xf6, 0xf8, 0xf9, 0xfc, 0x03, 0x02, 0xff, 0xff, 0x04, 0x07, 0x0f, 0x1a, 0x1b, 0x0f, 0x02, 0xfe, 0x07, + 0x0e, 0x0e, 0x0f, 0x10, 0x0f, 0x0b, 0x11, 0x17, 0x1a, 0x1b, 0x17, 0x10, 0x09, 0x0a, 0x10, 0x18, 0x1c, 0x16, 0x0e, 0x07, 0x04, 0xfe, + 0xf9, 0xfa, 0x02, 0x0c, 0x18, 0x23, 0x25, 0x1f, 0x13, 0x06, 0x05, 0x0c, 0x11, 0x0f, 0x0c, 0x06, 0xfe, 0xfb, 0x01, 0x0d, 0x18, 0x18, + 0x11, 0x0a, 0x00, 0x00, 0x05, 0x05, 0x03, 0x0b, 0x1a, 0x21, 0x23, 0x1b, 0x0a, 0xfe, 0xfc, 0xfe, 0xfe, 0x03, 0x08, 0x08, 0x01, 0xfe, + 0x02, 0x0c, 0x19, 0x1c, 0x14, 0x09, 0x01, 0xfe, 0xfd, 0x01, 0x05, 0x0b, 0x12, 0x15, 0x12, 0x0f, 0x0e, 0x0b, 0x09, 0x0b, 0x06, 0x03, + 0xff, 0xf9, 0xff, 0x05, 0x09, 0x0f, 0x0f, 0x0b, 0x02, 0xf5, 0xef, 0xf3, 0xfc, 0x01, 0x05, 0x08, 0x03, 0x04, 0x07, 0x07, 0x00, 0xfe, + 0xff, 0xfa, 0xf8, 0xfc, 0x04, 0x05, 0x03, 0x03, 0x04, 0xfe, 0xf5, 0xef, 0xf2, 0xf5, 0xf8, 0xfd, 0x01, 0xfc, 0xf4, 0xf2, 0xf5, 0xfa, + 0xfc, 0xfa, 0xf4, 0xf5, 0xfb, 0x00, 0xfd, 0xf7, 0xf4, 0xf2, 0xf0, 0xf0, 0xf3, 0xfb, 0x00, 0xfc, 0xfb, 0xf5, 0xf0, 0xf2, 0xf9, 0xfc, + 0xf8, 0xf7, 0xf2, 0xef, 0xf0, 0xf0, 0xee, 0xed, 0xeb, 0xe4, 0xe0, 0xe1, 0xe6, 0xf0, 0xf9, 0xfd, 0xfb, 0xf5, 0xf0, 0xf2, 0xf8, 0xfa, + 0xf7, 0xf8, 0xf5, 0xf4, 0xf6, 0xf3, 0xed, 0xeb, 0xf5, 0xf3, 0xef, 0xf1, 0xf8, 0xf9, 0xf8, 0xfa, 0xfa, 0xfb, 0xfc, 0xfb, 0x00, 0x04, + 0x03, 0xfb, 0xf8, 0xfb, 0xfd, 0xfc, 0xfb, 0xfd, 0xfd, 0xff, 0x02, 0x04, 0x05, 0x06, 0x04, 0xfd, 0xfb, 0xfb, 0xf9, 0xf5, 0xfa, 0x02, + 0x02, 0xfe, 0xf9, 0xf3, 0xf0, 0xf8, 0x06, 0x0f, 0x12, 0x0c, 0x04, 0x01, 0xfe, 0xf9, 0xf6, 0xf6, 0xf6, 0xf6, 0xf4, 0xef, 0xf3, 0xfa, + 0xfd, 0x03, 0x07, 0x09, 0x04, 0x03, 0x00, 0xfa, 0xf9, 0xfe, 0x04, 0x04, 0x01, 0xfb, 0xf6, 0xf1, 0xf1, 0xf8, 0x02, 0x09, 0x09, 0x05, + 0x03, 0x03, 0x01, 0xff, 0x07, 0x0f, 0x0f, 0x08, 0x03, 0x04, 0x09, 0x0d, 0x06, 0xff, 0xff, 0x03, 0x03, 0x01, 0x02, 0x00, 0x04, 0x0d, + 0x14, 0x15, 0x12, 0x0e, 0x0a, 0x07, 0x08, 0x0a, 0x0f, 0x12, 0x11, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, 0x0c, 0x0d, 0x11, + 0x15, 0x0f, 0x0b, 0x0f, 0x16, 0x14, 0x0b, 0x06, 0x09, 0x11, 0x16, 0x12, 0x0f, 0x0a, 0x02, 0x05, 0x0b, 0x11, 0x17, 0x1b, 0x17, 0x10, + 0x0f, 0x0b, 0x07, 0x05, 0x0c, 0x0d, 0x0a, 0x09, 0x07, 0x01, 0xfb, 0x00, 0x04, 0x09, 0x0e, 0x0a, 0x02, 0xfc, 0xfe, 0x04, 0x07, 0x00, + 0xf4, 0xf3, 0xf9, 0xfc, 0xfc, 0xfa, 0xf7, 0xf5, 0xf5, 0xf8, 0xfc, 0x01, 0xfe, 0xf9, 0xfb, 0xf5, 0xf3, 0xf6, 0xf8, 0xf3, 0xf1, 0xf5, + 0xf6, 0xf5, 0xf0, 0xe8, 0xeb, 0xf0, 0xf0, 0xed, 0xf0, 0xf9, 0x00, 0x03, 0xfe, 0xf7, 0xf7, 0xf5, 0xef, 0xeb, 0xf1, 0xf6, 0xf8, 0xfa, + 0xfd, 0xf9, 0xf2, 0xf1, 0xf2, 0xf6, 0xfb, 0xf8, 0xf4, 0xf5, 0xfb, 0x00, 0x06, 0x07, 0x00, 0xf8, 0xf4, 0xf6, 0x00, 0x03, 0xff, 0xf6, + 0xf2, 0xf3, 0xf8, 0x02, 0x04, 0x00, 0xfd, 0xfc, 0xf8, 0xf5, 0xf7, 0xfc, 0x00, 0xfd, 0xf7, 0xee, 0xed, 0xf4, 0xfa, 0xf8, 0xf4, 0xf7, + 0xf7, 0xf1, 0xeb, 0xf2, 0xfc, 0x02, 0xff, 0xf4, 0xef, 0xf1, 0xf5, 0xf9, 0xfd, 0x01, 0xff, 0xfb, 0xf6, 0xf0, 0xee, 0xee, 0xf3, 0xff, + 0x02, 0xff, 0xfa, 0xf2, 0xed, 0xef, 0xf7, 0xf9, 0xf9, 0xf8, 0xf5, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc, 0x00, 0x02, 0x08, 0x06, 0xfd, 0xf8, + 0xf4, 0xf8, 0x00, 0x05, 0x02, 0xfc, 0xf9, 0xf4, 0xf9, 0x05, 0x09, 0x07, 0x08, 0x0e, 0x0d, 0x08, 0x02, 0xfd, 0x02, 0x07, 0x06, 0x06, + 0x06, 0x08, 0x0a, 0x0b, 0x08, 0x06, 0x0b, 0x0d, 0x10, 0x15, 0x14, 0x0d, 0x06, 0x05, 0x04, 0x08, 0x0c, 0x0a, 0x08, 0x09, 0x0c, 0x0e, + 0x12, 0x15, 0x11, 0x0a, 0x07, 0x0c, 0x15, 0x1a, 0x17, 0x0f, 0x0d, 0x0c, 0x09, 0x06, 0x05, 0x06, 0x09, 0x0e, 0x10, 0x0e, 0x0d, 0x0f, + 0x15, 0x1a, 0x1c, 0x12, 0x09, 0x07, 0x03, 0x07, 0x0f, 0x12, 0x09, 0x01, 0xff, 0x00, 0x06, 0x0e, 0x11, 0x10, 0x0f, 0x0f, 0x0d, 0x08, + 0x01, 0xfb, 0xfc, 0x02, 0x09, 0x0e, 0x0c, 0x08, 0x05, 0x04, 0xff, 0xf9, 0xf5, 0xf7, 0xfa, 0xff, 0x05, 0x07, 0xff, 0xf5, 0xf7, 0xf9, + 0xfc, 0xfc, 0xf8, 0xf1, 0xf1, 0xfb, 0xfd, 0xfc, 0xfa, 0xf9, 0xf9, 0xfa, 0xfb, 0xf9, 0xfa, 0xfc, 0xfa, 0xfa, 0xf7, 0xf1, 0xec, 0xee, + 0xf2, 0xf4, 0xf3, 0xf5, 0xf6, 0xf5, 0xfa, 0xfd, 0xfb, 0xf7, 0xf7, 0xf9, 0xfb, 0xfd, 0xfa, 0xf3, 0xef, 0xf2, 0xf9, 0xfb, 0xf7, 0xf2, + 0xf2, 0xf8, 0xfa, 0xf7, 0xf5, 0xf8, 0xfc, 0xfe, 0xfb, 0xf6, 0xf5, 0xf7, 0xf8, 0xf8, 0xf5, 0xf0, 0xf0, 0xf0, 0xf3, 0xf8, 0xfd, 0xfc, + 0xf4, 0xec, 0xee, 0xf9, 0x02, 0x02, 0x03, 0x03, 0x02, 0x01, 0x00, 0xfe, 0xfa, 0xf8, 0xf8, 0xf7, 0xf3, 0xef, 0xf0, 0xf5, 0xfb, 0xfd, + 0xfc, 0xfd, 0x02, 0x03, 0xfd, 0xf8, 0xf7, 0xfa, 0xff, 0x08, 0x08, 0x02, 0xfd, 0xfc, 0xfd, 0xff, 0xff, 0x00, 0x00, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfe, 0xfd, 0xfc, 0xfd, 0xff, 0x03, 0x04, 0x02, 0x01, 0x01, 0xff, 0xf9, 0xf8, 0xfd, 0x04, 0x08, 0x07, 0x04, 0x02, 0x02, 0x03, + 0x02, 0xfd, 0xf8, 0xf6, 0xf8, 0xfc, 0x00, 0x02, 0x05, 0x0b, 0x0d, 0x08, 0x04, 0xfe, 0xfa, 0xfb, 0xfd, 0x01, 0x07, 0x0a, 0x08, 0x07, + 0x0a, 0x0c, 0x11, 0x12, 0x0a, 0x04, 0xff, 0xfd, 0xfe, 0x08, 0x0f, 0x0f, 0x0e, 0x0b, 0x0a, 0x0c, 0x0b, 0x07, 0x05, 0x06, 0x08, 0x0c, + 0x12, 0x10, 0x0f, 0x0c, 0x05, 0x01, 0x01, 0x04, 0x07, 0x07, 0x03, 0x01, 0x05, 0x0b, 0x0f, 0x0f, 0x0a, 0x08, 0x0a, 0x09, 0x07, 0x05, + 0x05, 0x03, 0x04, 0x04, 0x02, 0x05, 0x09, 0x08, 0x04, 0x04, 0x08, 0x0c, 0x0a, 0x07, 0x03, 0x00, 0xfd, 0xf9, 0xf7, 0xfb, 0xff, 0x00, + 0xff, 0xfd, 0xfb, 0xfb, 0x00, 0x04, 0x06, 0x07, 0x06, 0x04, 0x00, 0xfb, 0xf6, 0xf9, 0xfd, 0xfb, 0xf4, 0xed, 0xea, 0xed, 0xf4, 0xfc, + 0x04, 0x09, 0x04, 0xfc, 0xfa, 0xf6, 0xf3, 0xf4, 0xfc, 0xfd, 0xf8, 0xf4, 0xf3, 0xf7, 0xfd, 0xfe, 0xfc, 0xf8, 0xf6, 0xf8, 0xfd, 0x00, + 0xfe, 0xf9, 0xf5, 0xf2, 0xf3, 0xf3, 0xf5, 0xf9, 0xfb, 0xf9, 0xf8, 0xfc, 0xfb, 0xfd, 0x02, 0xff, 0xf6, 0xef, 0xf0, 0xf6, 0xfe, 0x04, + 0x05, 0x01, 0xfc, 0xfa, 0xfb, 0xf6, 0xf0, 0xf3, 0xfa, 0xff, 0x00, 0xfc, 0xfb, 0xfc, 0xfb, 0xfb, 0xfe, 0xff, 0xfc, 0xfa, 0xf8, 0xf7, + 0xfa, 0x00, 0x04, 0x02, 0xff, 0xfe, 0x00, 0x03, 0x06, 0x05, 0x02, 0xf9, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xfc, 0x03, 0x06, 0x05, 0x00, + 0xfc, 0xfc, 0xff, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x01, 0x05, 0x05, 0x01, 0xfe, 0xff, 0x03, 0x06, 0x05, 0xfe, + 0xf9, 0xf7, 0xfb, 0x02, 0x07, 0x09, 0x08, 0x07, 0x03, 0xff, 0xfc, 0xff, 0x04, 0x08, 0x0a, 0x09, 0x04, 0x00, 0x01, 0x04, 0x06, 0x06, + 0x07, 0x04, 0x00, 0x00, 0x02, 0x05, 0x06, 0x02, 0x03, 0x08, 0x0c, 0x0b, 0x0a, 0x07, 0x01, 0x00, 0x01, 0x04, 0x0c, 0x10, 0x0d, 0x07, + 0x00, 0xfe, 0x01, 0x07, 0x03, 0xff, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x09, 0x06, 0x06, 0x04, 0x01, 0xfe, 0x00, 0x02, 0x05, 0x07, + 0x05, 0x01, 0xfc, 0xf6, 0xf4, 0xf7, 0xfd, 0x03, 0x05, 0x02, 0xfe, 0xfe, 0x03, 0x07, 0x06, 0x00, 0xfa, 0xf9, 0xfa, 0xf9, 0xf8, 0xf9, + 0xfe, 0x04, 0x06, 0x06, 0x04, 0x00, 0xfa, 0xf9, 0xfe, 0x04, 0x06, 0x06, 0x02, 0xf9, 0xf5, 0xf5, 0xf7, 0xfa, 0x00, 0x01, 0xfe, 0xfc, + 0xfb, 0xfc, 0x00, 0x04, 0x04, 0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0xfc, 0xfb, 0xfe, 0xfd, 0xfd, 0xfe, 0xfd, 0xfe, 0xfc, 0xfa, 0xfb, + 0xfd, 0xff, 0xff, 0x01, 0x04, 0x04, 0xff, 0xfe, 0xfe, 0xfb, 0xf6, 0xf8, 0xfe, 0x02, 0x04, 0x04, 0x02, 0xfb, 0xf6, 0xf5, 0xf7, 0xfa, + 0xfd, 0x00, 0x04, 0x03, 0xfd, 0xf6, 0xf3, 0xf4, 0xf7, 0xfa, 0xfd, 0xfd, 0xfa, 0xf7, 0xf7, 0xfd, 0x04, 0x04, 0x01, 0xfb, 0xf7, 0xf6, + 0xfa, 0xff, 0xff, 0x04, 0x07, 0x06, 0x02, 0xfe, 0xfb, 0xf8, 0xf8, 0xfb, 0xfd, 0xfb, 0xf8, 0xfd, 0x07, 0x07, 0x03, 0x02, 0x04, 0x00, + 0xfc, 0xfb, 0xfd, 0xfc, 0xfa, 0xfa, 0xfc, 0x00, 0x06, 0x0a, 0x06, 0xff, 0xfb, 0xfe, 0x02, 0x06, 0x07, 0x06, 0x03, 0x01, 0x03, 0x04, + 0x03, 0x02, 0x00, 0x02, 0x02, 0xff, 0x01, 0x05, 0x06, 0x05, 0x02, 0x02, 0x04, 0x03, 0x00, 0xff, 0x00, 0x00, 0x03, 0x08, 0x0a, 0x0b, + 0x09, 0x05, 0x00, 0x03, 0x05, 0x03, 0x01, 0x00, 0xff, 0xfe, 0xfc, 0xfb, 0xfd, 0x01, 0x03, 0x02, 0x01, 0xfd, 0xfd, 0x01, 0x05, 0x05, + 0x04, 0x04, 0x02, 0xfc, 0xf8, 0xfb, 0x01, 0x05, 0x04, 0xff, 0x00, 0x03, 0x01, 0xfd, 0xf9, 0xfa, 0xfe, 0x01, 0xff, 0xfc, 0xfd, 0xff, + 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfc, 0xfe, 0xfe, 0xfe, 0xfc, 0xfa, 0xf7, 0xf6, 0xfa, 0xff, 0x04, 0x05, 0x02, 0x00, + 0x02, 0x04, 0x01, 0x00, 0x01, 0xff, 0xfb, 0xf9, 0xfb, 0xff, 0x04, 0x06, 0x03, 0xff, 0x00, 0x03, 0xfd, 0xf7, 0xf6, 0xfa, 0xff, 0x00, + 0xfe, 0xfe, 0x01, 0x04, 0x06, 0x05, 0x04, 0x01, 0xfd, 0xf7, 0xf8, 0xfe, 0x02, 0x02, 0x02, 0x03, 0x01, 0xfd, 0xfc, 0xfe, 0xfd, 0xfb, + 0xfb, 0x01, 0x05, 0x04, 0x01, 0x03, 0x02, 0x00, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0x00, 0x04, 0x09, 0x05, 0xfe, 0xf9, 0xf9, 0xfa, 0xff, + 0x03, 0x01, 0xfd, 0xf9, 0xf6, 0xf9, 0x02, 0x07, 0x05, 0xff, 0xfb, 0xfa, 0xfa, 0xfc, 0xfd, 0xfe, 0xfe, 0xfc, 0xfc, 0xff, 0x00, 0x02, + 0x02, 0x01, 0xfd, 0xfc, 0x01, 0x02, 0x01, 0x00, 0xfe, 0x00, 0x02, 0x01, 0xfb, 0xf9, 0xfb, 0x00, 0x05, 0x08, 0x06, 0x03, 0xff, 0x01, + 0x07, 0x0a, 0x0b, 0x0a, 0x06, 0xfd, 0xf8, 0xf9, 0xfd, 0x01, 0x03, 0x00, 0xfe, 0xff, 0x05, 0x0c, 0x0c, 0x08, 0x02, 0xff, 0xff, 0x00, + 0x01, 0x02, 0x01, 0x03, 0x08, 0x0d, 0x0c, 0x05, 0x00, 0x00, 0x02, 0x02, 0x00, 0xfe, 0xff, 0x00, 0x03, 0x05, 0x03, 0x01, 0x04, 0x07, + 0x04, 0x02, 0xfc, 0xf6, 0xf5, 0xf8, 0xfc, 0x00, 0x06, 0x03, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0xfc, 0xfd, 0xfa, 0xfa, 0xfc, + 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfb, 0xfa, 0xf9, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xf8, 0xf8, 0xfd, 0xfd, 0xfc, 0xfc, + 0xfd, 0xfd, 0xff, 0xff, 0xf9, 0xf7, 0xfb, 0x02, 0x00, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xfb, 0xfd, 0x00, 0x03, 0x03, 0x01, 0xfe, 0xfb, + 0xf9, 0xfb, 0xff, 0x03, 0x05, 0x03, 0x00, 0xfd, 0xfe, 0x01, 0x04, 0x05, 0x03, 0xff, 0xfc, 0xfb, 0xfd, 0x00, 0x02, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x01, 0x03, 0x04, 0x02, 0xfd, 0xfa, 0xfa, 0xfe, 0x00, + 0xfe, 0xfb, 0xfc, 0x00, 0x01, 0x02, 0x02, 0x01, 0xff, 0xfb, 0xf8, 0xf9, 0xfd, 0x01, 0x02, 0x02, 0x00, 0xfc, 0xfd, 0x00, 0x04, 0x04, + 0x00, 0xf9, 0xf5, 0xf9, 0xfd, 0xfe, 0xff, 0x04, 0x05, 0x03, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x04, + 0x01, 0x01, 0x00, 0xff, 0x02, 0x05, 0x06, 0x05, 0x04, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x04, 0x06, 0x07, 0x09, 0x08, 0x04, 0x00, + 0x01, 0x04, 0x03, 0xfd, 0xfc, 0xff, 0x03, 0x06, 0x09, 0x0a, 0x06, 0x00, 0xfb, 0xfd, 0x03, 0x09, 0x09, 0x05, 0x01, 0x00, 0xff, 0xfa, + 0xfd, 0x01, 0x03, 0x03, 0x02, 0xff, 0xfd, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00, 0xfe, 0xfe, 0x00, 0x01, 0x02, + 0xff, 0xfc, 0xfa, 0xfa, 0xfd, 0xff, 0x00, 0xfd, 0xfc, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x02, 0xfe, 0xf8, 0xf3, 0xf6, 0xfb, 0xfe, 0xff, + 0xfd, 0xfc, 0xfd, 0xfd, 0x00, 0x03, 0x00, 0xfa, 0xf9, 0xfd, 0xff, 0x00, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf8, 0xf8, 0xfa, 0xfe, 0x00, + 0xfe, 0xfc, 0xfc, 0xfc, 0xfc, 0xfe, 0x00, 0xff, 0xff, 0x01, 0x03, 0x05, 0x04, 0x01, 0xfd, 0xfa, 0xf7, 0xf9, 0xfd, 0x00, 0x00, 0xff, + 0xfd, 0xfb, 0xfc, 0xfe, 0x01, 0x03, 0x02, 0x02, 0x01, 0x00, 0xfd, 0xfb, 0xfc, 0xfd, 0xfe, 0xfd, 0xfb, 0xfc, 0x00, 0x02, 0x01, 0x00, + 0xfd, 0xfb, 0xf9, 0xfc, 0xfe, 0x00, 0x02, 0x01, 0xfc, 0xf9, 0xfa, 0xfe, 0x01, 0x02, 0x00, 0xff, 0x01, 0x02, 0x01, 0x00, 0x02, 0x05, + 0x03, 0xfe, 0xfb, 0xfc, 0xfe, 0x00, 0xff, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0xff, 0xfd, 0xfe, 0x01, 0x04, 0x05, 0x03, + 0x01, 0x00, 0x01, 0x04, 0x06, 0x05, 0x02, 0xff, 0xfe, 0x00, 0x03, 0x05, 0x04, 0x03, 0x03, 0x03, 0x04, 0x03, 0x04, 0x05, 0x05, 0x05, + 0x04, 0x01, 0xff, 0x00, 0x02, 0x03, 0x03, 0x01, 0x00, 0x03, 0x05, 0x06, 0x05, 0x05, 0x04, 0x03, 0x02, 0x00, 0xfe, 0xfe, 0xfe, 0x00, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x05, 0x04, 0x01, 0xfe, 0xfd, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0xfe, 0xfd, 0xfe, 0x00, 0x01, 0x00, + 0xfe, 0xff, 0x01, 0x01, 0xff, 0xfd, 0xfb, 0xfd, 0x00, 0x01, 0x00, 0xfe, 0xfc, 0xfd, 0xfe, 0xfe, 0xfd, 0xfe, 0x00, 0x02, 0x02, 0x00, + 0xfd, 0xfa, 0xf7, 0xf8, 0xfb, 0xff, 0x02, 0x01, 0xfe, 0xfa, 0xf8, 0xf9, 0xfb, 0xfe, 0x01, 0x01, 0x02, 0xff, 0xfa, 0xf8, 0xfb, 0xfd, + 0xfe, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x01, 0xfe, 0xfa, 0xfb, 0xfe, 0xfd, 0xfd, 0xfd, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xfd, 0xfb, 0xfb, 0xfc, 0xff, 0x01, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xfb, 0xfb, 0xfc, 0xff, 0x00, 0xff, 0xfe, 0xfd, 0xfe, + 0x00, 0x01, 0x02, 0x01, 0xfd, 0xfb, 0xfb, 0xfc, 0xfb, 0xfd, 0xff, 0x00, 0xfe, 0xfe, 0xfc, 0xfb, 0xfc, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x01, 0x01, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x01, 0x01, 0x04, 0x05, 0x05, 0x05, 0x04, 0x03, 0x03, 0x02, 0x00, 0x01, 0x04, 0x06, 0x07, + 0x06, 0x03, 0x02, 0x01, 0x00, 0x01, 0x04, 0x06, 0x05, 0x03, 0x03, 0x05, 0x08, 0x06, 0x01, 0xff, 0x00, 0x02, 0x03, 0x04, 0x07, 0x09, + 0x07, 0x01, 0xfc, 0xfb, 0xfb, 0xff, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x01, 0xfe, 0xfd, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xfd, 0xff, + 0xff, 0x00, 0x01, 0x02, 0x02, 0x01, 0xfd, 0xf8, 0xfa, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0x01, 0x02, 0x01, 0xfd, + 0xfc, 0xfa, 0xfb, 0xfd, 0x00, 0x01, 0xff, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x01, 0xff, 0xfd, + 0xfe, 0xfe, 0xfe, 0xff, 0x02, 0x02, 0x01, 0x00, 0xff, 0xfd, 0xfb, 0xf9, 0xfd, 0x01, 0x02, 0xff, 0xfe, 0xfe, 0xff, 0x01, 0x01, 0xff, + 0xfd, 0xfb, 0xfb, 0xfb, 0xfc, 0xff, 0x00, 0xfd, 0xfb, 0xfc, 0xfe, 0xfe, 0xff, 0xfe, 0xfc, 0xfc, 0xfe, 0xff, 0xfe, 0xfc, 0xfd, 0xfe, + 0x00, 0xff, 0xfd, 0xfc, 0xfd, 0xfd, 0xfc, 0xff, 0xfd, 0xfb, 0xfa, 0xfb, 0xfc, 0xfc, 0xfc, 0xfb, 0xfc, 0xfe, 0xff, 0xff, 0xfd, 0xfc, + 0xfc, 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x01, 0x01, 0x02, 0x03, + 0x02, 0x02, 0x01, 0x00, 0x02, 0x03, 0x01, 0x01, 0x02, 0x03, 0x04, 0x03, 0x03, 0x03, 0x04, 0x03, 0x02, 0x02, 0x02, 0x03, 0x04, 0x04, + 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x06, 0x06, 0x05, 0x03, 0x02, 0x02, 0x02, 0x03, 0x06, 0x07, 0x05, 0x02, 0xff, 0xff, 0x01, + 0x03, 0x04, 0x04, 0x02, 0x00, 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0x01, 0x03, 0x03, 0x04, 0x03, 0x02, 0x01, 0x01, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfe, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xfc, 0xfa, + 0xfb, 0xfe, 0x00, 0x01, 0x02, 0x03, 0x00, 0x00, 0xfe, 0xfb, 0xfa, 0xfa, 0xfa, 0xfb, 0xfd, 0xff, 0x00, 0xff, 0x00, 0x01, 0x01, 0xff, + 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x01, 0xff, 0xfd, 0xfc, 0xfc, 0xfe, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xfd, 0xfc, 0xfb, 0xfd, 0xff, 0x00, 0x00, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0xfe, 0xfd, 0xfb, + 0xfa, 0xfb, 0xfe, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfd, 0xfd, 0xfd, 0xfb, 0xfb, 0xfe, 0x00, 0x01, 0x01, 0xff, 0xfb, + 0xfa, 0xfc, 0xfe, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xfe, + 0xff, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x02, 0x04, 0x04, 0x03, 0x03, 0x02, 0x01, 0x00, 0x02, 0x03, 0x03, 0x02, + 0x02, 0x04, 0x04, 0x05, 0x06, 0x06, 0x04, 0x02, 0x00, 0xfe, 0xff, 0x02, 0x04, 0x05, 0x06, 0x05, 0x03, 0x02, 0x03, 0x04, 0x06, 0x04, + 0x01, 0x00, 0x00, 0x01, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x01, 0x03, 0x05, 0x04, 0x02, 0x00, 0xfe, 0xfe, 0x01, + 0x02, 0x03, 0x02, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xfc, 0xfc, 0xff, 0x02, 0x03, 0x02, 0x00, 0xfe, + 0xfd, 0xff, 0x00, 0x00, 0xff, 0xfd, 0xfc, 0xfd, 0xfe, 0xfd, 0xff, 0x01, 0x00, 0xff, 0xff, 0xfd, 0xfb, 0xfc, 0xfe, 0xfe, 0xfc, 0xfd, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xfe, 0xfd, 0xfd, 0xfd, 0xff, + 0x01, 0x03, 0x01, 0xff, 0xfe, 0xfc, 0xfc, 0xfd, 0xfc, 0xfc, 0xfd, 0xff, 0xfe, 0x00, 0x01, 0xff, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, + 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, 0xff, 0x00, + 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x00, 0x00, 0xff, 0xfd, 0xfd, 0xff, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x01, 0x03, 0x04, 0x03, 0x02, 0x02, 0x02, 0x03, 0x05, 0x05, + 0x02, 0x01, 0x00, 0x01, 0x03, 0x05, 0x05, 0x04, 0x04, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x05, 0x06, 0x06, 0x04, 0x03, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x02, 0x03, 0x04, 0x03, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0xfe, 0xfc, 0xfd, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0xfe, 0xfd, 0xfd, 0xfb, 0xfb, 0xfd, + 0xff, 0x00, 0x01, 0x00, 0x00, 0xff, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0x00, 0x02, 0x02, 0xff, 0xfd, 0xfd, 0xff, 0xff, 0xfd, 0xfc, + 0xfa, 0xfa, 0xfc, 0xfe, 0x00, 0x02, 0x02, 0xfd, 0xfa, 0xfa, 0xfc, 0xff, 0xff, 0xfd, 0xfb, 0xfb, 0xfc, 0xfd, 0xfe, 0x00, 0x01, 0x01, + 0x01, 0xff, 0xfd, 0xfa, 0xfa, 0xfb, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, 0xfd, 0xfc, 0xfc, 0xfe, 0x00, 0x00, + 0xfe, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfc, 0xfd, 0xfe, 0xfe, 0xfc, 0xfd, 0xfe, 0x00, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xfb, 0xfa, 0xfd, + 0xff, 0x00, 0x00, 0xff, 0xfc, 0xfd, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, + 0x00, 0x01, 0x03, 0x02, 0x00, 0xfe, 0xfe, 0xff, 0x02, 0x04, 0x03, 0x02, 0x00, 0x00, 0x01, 0x02, 0x03, 0x03, 0x02, 0x00, 0xff, 0x01, + 0x02, 0x03, 0x03, 0x04, 0x04, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x04, 0x05, 0x04, + 0x02, 0x00, 0x01, 0x02, 0x03, 0x04, 0x03, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x03, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x02, 0x03, 0x05, 0x04, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xfd, 0xfe, 0x00, 0x02, 0x02, 0x03, 0x02, 0x00, + 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0xfe, 0xfc, 0xfc, 0xfe, 0x00, 0x02, 0x01, 0xff, 0xfd, 0xfc, + 0xfd, 0xff, 0x00, 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xfd, 0xfc, 0xfc, 0xfd, + 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0xfd, 0xfc, 0xfb, 0xfb, 0xfd, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfa, + 0xf9, 0xfb, 0xfc, 0xfe, 0xfe, 0xfe, 0xfc, 0xfc, 0xfe, 0xff, 0xfd, 0xfd, 0xfd, 0xfe, 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0xfe, 0xfc, 0xfd, + 0xff, 0x01, 0xff, 0xfd, 0xfd, 0xff, 0x00, 0xff, 0xfd, 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, + 0x02, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x02, 0x01, 0x01, 0x01, 0x03, 0x02, 0x01, 0x01, 0x03, 0x04, + 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0xff, 0x01, 0x04, 0x06, 0x07, 0x06, 0x04, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, + 0x01, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x03, 0x02, 0x00, 0xff, 0xff, 0xff, 0x01, 0x03, 0x04, 0x05, 0x04, + 0x02, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x01, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x01, 0xff, 0xfd, 0xfd, 0xff, 0x00, 0xff, 0xfe, + 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xfd, 0xfe, + 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xfc, 0xfb, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfd, 0xfd, 0xfe, 0xff, + 0x00, 0x00, 0xff, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xfd, 0xfd, 0xfc, 0xfe, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0xff, 0x00, 0x03, 0x04, 0x03, 0x02, 0x00, 0x01, 0x02, + 0x04, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x05, 0x05, 0x03, 0x01, 0xff, 0xff, 0x00, 0x00, + 0x01, 0x02, 0x01, 0x00, 0x01, 0x02, 0x03, 0x03, 0x04, 0x03, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0xfe, + 0xff, 0xfe, 0xfd, 0xfc, 0xfd, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfd, + 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfe, 0xfe, 0xfd, 0xfd, 0xfe, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0xff, + 0x01, 0x01, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x02, 0x02, 0x02, 0x01, 0x00, 0xff, 0x01, 0x03, 0x03, 0x03, 0x02, 0x01, + 0x00, 0x01, 0x02, 0x02, 0x03, 0x04, 0x05, 0x04, 0x03, 0x01, 0x00, 0x00, 0x01, 0x03, 0x03, 0x04, 0x03, 0x02, 0x01, 0x01, 0x02, 0x03, + 0x02, 0x01, 0x02, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x01, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, + 0x00, 0x00, 0xfe, 0xfd, 0xfc, 0xfb, 0xfb, 0xfb, 0xfc, 0xfd, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, + 0xfb, 0xfb, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfd, 0xfc, 0xfb, 0xfb, 0xfd, 0xfd, 0xfd, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd, + 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xfe, 0xfd, 0xfe, 0x00, 0x01, 0x01, 0xff, 0xfe, + 0xff, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x01, 0x00, 0x00, 0x02, 0x04, 0x04, 0x04, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, + 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xfe, 0xfe, 0xff, 0x01, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, + 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xfd, + 0xfd, 0xfe, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfd, 0xfe, 0xfe, 0xfe, 0xfd, 0xfc, 0xfb, 0xfc, 0xfc, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, + 0xfe, 0xfe, 0xfd, 0xfc, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfc, 0xfd, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfe, + 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0x00, 0x01, 0x02, 0x02, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0x01, 0x03, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xfe, + 0xfc, 0xfb, 0xfb, 0xfc, 0xfd, 0xfd, 0xfd, 0xfc, 0xfb, 0xfc, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfe, + 0xfd, 0xfd, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, + 0x00, 0x01, 0x01, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0x01, 0x01, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0xff, 0x00, 0x01, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xff, + 0x00, 0xff, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfc, 0xfd, + 0xfd, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfd, + 0xfe, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, + 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfd, + 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfd, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, 0xfe, + 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char TOM_2608[18562] = { + 0x52, 0x49, 0x46, 0x46, 0xd0, 0x90, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x44, 0xac, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x02, 0x00, 0x10, 0x00, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x61, 0x61, 0x90, 0x00, 0xff, 0x02, 0x07, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0c, 0x0b, 0x0b, 0x0b, 0x02, 0x01, 0x09, 0x0d, + 0x05, 0xf9, 0xf4, 0xf0, 0xf0, 0xef, 0xed, 0xeb, 0xe9, 0xe7, 0xe8, 0xe8, 0xed, 0xf7, 0xf8, 0xe0, 0xc7, 0xd7, 0xd8, 0xe2, 0xf3, 0xe3, + 0xc9, 0xc5, 0xe0, 0xe9, 0xf4, 0xf2, 0xcd, 0xda, 0x00, 0x0f, 0xfe, 0xeb, 0xf0, 0x0b, 0xfc, 0xed, 0xea, 0xdf, 0xd9, 0xed, 0x0e, 0x03, + 0xe7, 0xc9, 0xb2, 0xb5, 0xbf, 0xbe, 0xb9, 0xd4, 0x03, 0x21, 0x1d, 0x08, 0x08, 0x2b, 0x4f, 0x59, 0x3d, 0xff, 0xdc, 0xdc, 0xf5, 0x1d, + 0x3f, 0x35, 0xf3, 0xdb, 0xf3, 0x1c, 0x39, 0x30, 0x15, 0x03, 0x0a, 0x10, 0x26, 0x5b, 0x7f, 0x63, 0x22, 0x23, 0x4a, 0x67, 0x52, 0x3c, + 0x1f, 0x13, 0x2d, 0x52, 0x60, 0x50, 0x34, 0x2f, 0x3c, 0x43, 0x2e, 0x21, 0x28, 0x29, 0x38, 0x47, 0x48, 0x3d, 0x39, 0x3e, 0x44, 0x40, + 0x31, 0x29, 0x39, 0x40, 0x39, 0x32, 0x33, 0x40, 0x50, 0x58, 0x55, 0x49, 0x35, 0x1c, 0x09, 0x0f, 0x26, 0x21, 0x09, 0xff, 0x1c, 0x2c, + 0x29, 0x26, 0x38, 0x4b, 0x4c, 0x36, 0x25, 0x1b, 0x0d, 0xed, 0xd9, 0xd5, 0xdf, 0xf3, 0xfe, 0xff, 0x00, 0xe8, 0xcf, 0xc5, 0xc7, 0xd5, + 0xe0, 0xd9, 0xb2, 0xa3, 0xb8, 0xd6, 0xd8, 0xd2, 0xc9, 0xa6, 0x9d, 0xa7, 0xae, 0xbb, 0xc5, 0xd1, 0xdc, 0xcf, 0xb5, 0xa5, 0xab, 0xbe, + 0xc8, 0xbc, 0x9b, 0x95, 0xac, 0xc0, 0xbf, 0xba, 0xb7, 0x9f, 0x97, 0xa6, 0xbd, 0xaa, 0x98, 0x94, 0x93, 0x89, 0x8e, 0xa1, 0xa9, 0x94, + 0x81, 0x90, 0xa0, 0xa6, 0xa4, 0xaa, 0xa9, 0xae, 0xb6, 0xb7, 0xa4, 0x9b, 0xb8, 0xc2, 0xc4, 0xce, 0xe3, 0xd9, 0xc0, 0xb9, 0xd4, 0xe6, + 0xe2, 0xdc, 0xe5, 0xf6, 0x02, 0x02, 0x02, 0x09, 0x11, 0xe9, 0xe3, 0xff, 0x04, 0xf4, 0xeb, 0xf8, 0x04, 0x0f, 0x0d, 0x01, 0xf7, 0xfb, + 0x04, 0x07, 0x05, 0x05, 0x00, 0xe1, 0xd7, 0xee, 0x11, 0x06, 0xf8, 0xff, 0x0e, 0x0f, 0x15, 0x25, 0x1b, 0x0c, 0x05, 0x05, 0x01, 0x03, + 0x0e, 0x15, 0x22, 0x24, 0x18, 0x0e, 0x16, 0x20, 0x1b, 0x17, 0x28, 0x45, 0x4e, 0x45, 0x39, 0x34, 0x2e, 0x2d, 0x35, 0x41, 0x4f, 0x51, + 0x49, 0x40, 0x43, 0x48, 0x45, 0x2a, 0x25, 0x39, 0x47, 0x4c, 0x4b, 0x4b, 0x54, 0x5d, 0x5d, 0x54, 0x4c, 0x49, 0x4c, 0x50, 0x5a, 0x5e, + 0x57, 0x5c, 0x5e, 0x64, 0x73, 0x79, 0x6d, 0x5b, 0x5a, 0x62, 0x69, 0x68, 0x64, 0x63, 0x63, 0x5e, 0x51, 0x48, 0x46, 0x43, 0x41, 0x45, + 0x4f, 0x57, 0x55, 0x4f, 0x4f, 0x42, 0x39, 0x3d, 0x44, 0x4b, 0x44, 0x31, 0x2b, 0x2f, 0x32, 0x2a, 0x24, 0x22, 0x24, 0x21, 0x19, 0x0d, + 0x03, 0x0b, 0x1a, 0x1c, 0x09, 0xf2, 0xe7, 0xed, 0xff, 0x04, 0xf9, 0xed, 0xe8, 0xed, 0xf4, 0xf0, 0xeb, 0xe4, 0xda, 0xd0, 0xce, 0xcf, + 0xcf, 0xd8, 0xea, 0xf7, 0xed, 0xe0, 0xda, 0xdc, 0xd6, 0xd2, 0xcf, 0xc9, 0xc8, 0xd2, 0xde, 0xdb, 0xd4, 0xd0, 0xd5, 0xe5, 0xe6, 0xd3, + 0xbd, 0xb0, 0xb1, 0xbd, 0xcd, 0xcf, 0xc3, 0xb7, 0xbe, 0xc8, 0xc9, 0xc0, 0xb7, 0xaf, 0xac, 0xb8, 0xc1, 0xbd, 0xb4, 0xb0, 0xb4, 0xb9, + 0xb6, 0xa7, 0x9a, 0x99, 0x9e, 0x9c, 0x97, 0x99, 0x98, 0xa3, 0xb5, 0xb7, 0xb4, 0xb2, 0xb5, 0xb6, 0xbd, 0xc6, 0xc2, 0xb3, 0xa0, 0x9a, + 0xb0, 0xcb, 0xd7, 0xcb, 0xb0, 0xa3, 0xaf, 0xc8, 0xdc, 0xdf, 0xd5, 0xcb, 0xc7, 0xc6, 0xc2, 0xc5, 0xc6, 0xc0, 0xb8, 0xb7, 0xc1, 0xcf, + 0xdb, 0xde, 0xe1, 0xe9, 0xed, 0xeb, 0xe5, 0xe6, 0xee, 0xf8, 0xfc, 0xf9, 0xf7, 0x00, 0x0e, 0x0d, 0xff, 0xf1, 0xf3, 0xfe, 0x07, 0x07, + 0x0c, 0x0f, 0x0e, 0x09, 0x03, 0xfe, 0xfd, 0x07, 0x11, 0x14, 0x10, 0x0c, 0x0b, 0x0e, 0x10, 0x11, 0x0f, 0x0d, 0x0e, 0x18, 0x23, 0x26, + 0x16, 0x19, 0x2d, 0x32, 0x35, 0x38, 0x35, 0x21, 0x1c, 0x23, 0x28, 0x31, 0x35, 0x33, 0x31, 0x2a, 0x26, 0x2b, 0x2f, 0x32, 0x35, 0x3b, + 0x39, 0x3d, 0x47, 0x4a, 0x48, 0x47, 0x49, 0x4e, 0x4d, 0x46, 0x43, 0x41, 0x43, 0x46, 0x44, 0x3c, 0x38, 0x3b, 0x36, 0x33, 0x34, 0x33, + 0x30, 0x2e, 0x30, 0x36, 0x3d, 0x3e, 0x35, 0x2c, 0x27, 0x27, 0x26, 0x23, 0x16, 0x09, 0x0a, 0x16, 0x1c, 0x13, 0x18, 0x24, 0x28, 0x19, + 0x17, 0x1d, 0x20, 0x24, 0x23, 0x22, 0x27, 0x25, 0x21, 0x1c, 0x16, 0x0e, 0x0d, 0x14, 0x25, 0x23, 0x19, 0x1a, 0x1b, 0x19, 0x15, 0x15, + 0x09, 0xf6, 0xe8, 0xef, 0xf3, 0xf4, 0xfc, 0x05, 0x01, 0xf6, 0xf6, 0xf4, 0xef, 0xe9, 0xea, 0xeb, 0xea, 0xe9, 0xe2, 0xdc, 0xda, 0xd9, + 0xd4, 0xcf, 0xd1, 0xd7, 0xdb, 0xd6, 0xc8, 0xc0, 0xc1, 0xcb, 0xd5, 0xd8, 0xd0, 0xc4, 0xbd, 0xbd, 0xbf, 0xbf, 0xba, 0xb9, 0xbc, 0xbc, + 0xb2, 0xa6, 0xa1, 0x9f, 0x9b, 0x96, 0x98, 0x9e, 0xa1, 0xa1, 0xa7, 0xa8, 0xa5, 0xa2, 0xa6, 0xaa, 0xaa, 0xaa, 0xa8, 0xa7, 0xaa, 0xb0, + 0xb2, 0xb0, 0xac, 0xac, 0xaf, 0xb2, 0xaf, 0xab, 0xa9, 0xac, 0xb2, 0xb6, 0xb6, 0xb2, 0xb2, 0xb8, 0xc0, 0xc5, 0xc8, 0xc7, 0xc2, 0xbc, + 0xba, 0xbd, 0xc5, 0xce, 0xd3, 0xd4, 0xd1, 0xd1, 0xd6, 0xe1, 0xe8, 0xe8, 0xe5, 0xe3, 0xe3, 0xe7, 0xed, 0xf3, 0xf9, 0xff, 0x05, 0x09, + 0x07, 0xff, 0xf9, 0xfe, 0x09, 0x10, 0x0b, 0x05, 0x06, 0x10, 0x16, 0x1a, 0x20, 0x1e, 0x1c, 0x1d, 0x21, 0x27, 0x2e, 0x33, 0x35, 0x33, + 0x31, 0x31, 0x31, 0x33, 0x36, 0x36, 0x38, 0x38, 0x36, 0x34, 0x33, 0x36, 0x3b, 0x3d, 0x39, 0x37, 0x39, 0x3b, 0x3c, 0x3e, 0x41, 0x41, + 0x40, 0x3e, 0x40, 0x43, 0x46, 0x46, 0x43, 0x3e, 0x3b, 0x3a, 0x3b, 0x3a, 0x36, 0x31, 0x2b, 0x27, 0x25, 0x28, 0x2b, 0x2d, 0x30, 0x30, + 0x30, 0x31, 0x32, 0x32, 0x35, 0x38, 0x3a, 0x37, 0x32, 0x31, 0x35, 0x3a, 0x3c, 0x3f, 0x3c, 0x35, 0x31, 0x33, 0x39, 0x3b, 0x3a, 0x37, + 0x35, 0x35, 0x33, 0x2f, 0x2b, 0x28, 0x29, 0x2c, 0x2d, 0x2b, 0x29, 0x29, 0x2a, 0x29, 0x27, 0x28, 0x2c, 0x32, 0x33, 0x30, 0x2c, 0x28, + 0x25, 0x22, 0x23, 0x24, 0x24, 0x20, 0x20, 0x21, 0x21, 0x1e, 0x17, 0x0f, 0x0c, 0x0d, 0x10, 0x12, 0x10, 0x0c, 0x08, 0x06, 0x06, 0x08, + 0x09, 0x07, 0x06, 0x02, 0xfb, 0xf5, 0xf8, 0x01, 0x05, 0x00, 0xf9, 0xf7, 0xf8, 0xf8, 0xf6, 0xf6, 0xf8, 0xf9, 0xf5, 0xee, 0xeb, 0xec, + 0xec, 0xed, 0xee, 0xf0, 0xf0, 0xee, 0xe9, 0xe4, 0xe0, 0xdf, 0xdf, 0xdd, 0xdb, 0xda, 0xdd, 0xe0, 0xe1, 0xdb, 0xd5, 0xd5, 0xdc, 0xdf, + 0xdc, 0xd9, 0xd7, 0xd5, 0xd5, 0xd2, 0xce, 0xca, 0xc8, 0xc7, 0xc9, 0xcc, 0xcd, 0xcb, 0xc9, 0xc8, 0xc5, 0xc2, 0xbe, 0xbc, 0xb8, 0xb5, + 0xb5, 0xb5, 0xb7, 0xba, 0xba, 0xb7, 0xb2, 0xb1, 0xb4, 0xb5, 0xb5, 0xb4, 0xb9, 0xba, 0xb9, 0xbb, 0xc0, 0xc1, 0xba, 0xb4, 0xae, 0xab, + 0xab, 0xae, 0xb2, 0xb6, 0xbb, 0xbe, 0xc0, 0xc2, 0xc5, 0xc9, 0xcb, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xce, 0xcf, 0xcf, 0xd2, 0xd6, 0xd8, + 0xd9, 0xd9, 0xd8, 0xd8, 0xdc, 0xde, 0xde, 0xdf, 0xe3, 0xe7, 0xe8, 0xe6, 0xe3, 0xe3, 0xe8, 0xec, 0xef, 0xf1, 0xf2, 0xf7, 0xfa, 0xfa, + 0xf9, 0xf7, 0xf4, 0xf3, 0xf9, 0x01, 0x0a, 0x11, 0x12, 0x10, 0x11, 0x15, 0x16, 0x16, 0x15, 0x16, 0x1a, 0x1e, 0x1e, 0x1a, 0x19, 0x1c, + 0x1f, 0x1f, 0x1a, 0x17, 0x18, 0x1b, 0x1c, 0x1a, 0x19, 0x19, 0x1a, 0x1e, 0x23, 0x28, 0x2a, 0x2e, 0x34, 0x3a, 0x3d, 0x39, 0x33, 0x2f, + 0x31, 0x35, 0x37, 0x37, 0x37, 0x39, 0x3b, 0x3e, 0x3f, 0x3e, 0x3e, 0x40, 0x42, 0x41, 0x3d, 0x39, 0x3b, 0x41, 0x45, 0x48, 0x49, 0x48, + 0x45, 0x43, 0x44, 0x47, 0x4a, 0x4b, 0x4a, 0x48, 0x47, 0x48, 0x4b, 0x4e, 0x4f, 0x4c, 0x48, 0x45, 0x44, 0x43, 0x43, 0x43, 0x43, 0x43, + 0x42, 0x40, 0x3f, 0x40, 0x41, 0x43, 0x42, 0x3e, 0x39, 0x35, 0x33, 0x32, 0x34, 0x36, 0x34, 0x2e, 0x27, 0x26, 0x29, 0x2e, 0x2f, 0x2c, + 0x25, 0x20, 0x21, 0x26, 0x27, 0x22, 0x1c, 0x1a, 0x19, 0x16, 0x11, 0x0e, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x03, 0x01, 0xfd, 0xfa, 0xf8, + 0xf5, 0xf4, 0xf7, 0xfc, 0xfd, 0xfc, 0xf9, 0xf3, 0xee, 0xea, 0xe8, 0xea, 0xed, 0xee, 0xec, 0xea, 0xe8, 0xe6, 0xe6, 0xe7, 0xe6, 0xe1, + 0xdc, 0xd9, 0xda, 0xdd, 0xdd, 0xd8, 0xd0, 0xcc, 0xce, 0xd3, 0xd4, 0xd0, 0xcc, 0xcd, 0xd1, 0xd6, 0xd7, 0xd6, 0xd5, 0xd8, 0xdd, 0xe0, + 0xdd, 0xd8, 0xd3, 0xd3, 0xd4, 0xd4, 0xd1, 0xcc, 0xc8, 0xc7, 0xc8, 0xc8, 0xc6, 0xc6, 0xc6, 0xc4, 0xc1, 0xc2, 0xc4, 0xc6, 0xc6, 0xc7, + 0xc7, 0xc9, 0xcb, 0xc9, 0xc7, 0xc5, 0xc6, 0xc8, 0xc9, 0xc7, 0xc7, 0xc4, 0xc3, 0xc4, 0xc6, 0xc6, 0xc4, 0xc3, 0xc2, 0xc2, 0xc2, 0xc4, + 0xc7, 0xca, 0xcc, 0xcc, 0xca, 0xc8, 0xc6, 0xc5, 0xc4, 0xc4, 0xc5, 0xc9, 0xcb, 0xcc, 0xcd, 0xce, 0xd0, 0xd2, 0xd5, 0xd8, 0xda, 0xd9, + 0xd8, 0xd7, 0xd8, 0xdc, 0xe2, 0xe5, 0xe4, 0xe2, 0xe5, 0xea, 0xee, 0xed, 0xea, 0xe7, 0xea, 0xf1, 0xf5, 0xf5, 0xf1, 0xf1, 0xf5, 0xf9, + 0xfd, 0xff, 0xff, 0xfd, 0xfb, 0xfb, 0x00, 0x05, 0x08, 0x08, 0x07, 0x09, 0x0c, 0x0e, 0x0f, 0x0f, 0x0f, 0x12, 0x15, 0x17, 0x17, 0x17, + 0x19, 0x1e, 0x22, 0x22, 0x21, 0x23, 0x25, 0x25, 0x25, 0x26, 0x2b, 0x2f, 0x31, 0x2f, 0x2c, 0x2c, 0x2f, 0x36, 0x3a, 0x3a, 0x39, 0x37, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x35, 0x35, 0x37, 0x37, 0x35, 0x33, 0x34, 0x39, 0x3e, 0x40, 0x3e, 0x3b, 0x3a, 0x39, 0x3a, + 0x3a, 0x3a, 0x3c, 0x3d, 0x3d, 0x3c, 0x3c, 0x3b, 0x39, 0x37, 0x35, 0x35, 0x36, 0x37, 0x36, 0x34, 0x35, 0x38, 0x38, 0x34, 0x30, 0x2b, + 0x29, 0x2b, 0x2c, 0x29, 0x26, 0x24, 0x26, 0x28, 0x29, 0x28, 0x25, 0x20, 0x1e, 0x1d, 0x1f, 0x21, 0x22, 0x22, 0x20, 0x20, 0x22, 0x23, + 0x23, 0x22, 0x24, 0x27, 0x26, 0x24, 0x20, 0x1d, 0x1c, 0x1d, 0x1b, 0x19, 0x18, 0x17, 0x17, 0x16, 0x15, 0x14, 0x14, 0x14, 0x10, 0x0c, + 0x0b, 0x0e, 0x0f, 0x0f, 0x0c, 0x0a, 0x0a, 0x0a, 0x0c, 0x0c, 0x0b, 0x08, 0x06, 0x04, 0x03, 0x03, 0x04, 0x03, 0x00, 0xfe, 0xff, 0xfd, + 0xfa, 0xf9, 0xfb, 0xfc, 0xfc, 0xfb, 0xf8, 0xf4, 0xf3, 0xf2, 0xf2, 0xf1, 0xee, 0xec, 0xec, 0xed, 0xec, 0xea, 0xe7, 0xe5, 0xe4, 0xe4, + 0xe3, 0xe2, 0xe2, 0xe3, 0xe4, 0xe5, 0xe4, 0xe3, 0xe3, 0xe1, 0xde, 0xdc, 0xdc, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xde, 0xda, 0xd6, + 0xd3, 0xd5, 0xd7, 0xd7, 0xd6, 0xd4, 0xd0, 0xcb, 0xc7, 0xc8, 0xca, 0xcb, 0xca, 0xcc, 0xcf, 0xcf, 0xcd, 0xcc, 0xcb, 0xca, 0xca, 0xc9, + 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7, 0xc3, 0xc0, 0xbe, 0xbe, 0xbe, 0xbd, 0xb9, 0xb9, 0xbc, 0xbf, 0xc0, 0xbf, 0xbf, 0xbf, 0xc0, 0xc2, + 0xc5, 0xc5, 0xc6, 0xc7, 0xc9, 0xcb, 0xcc, 0xcb, 0xc8, 0xca, 0xcd, 0xce, 0xce, 0xcd, 0xce, 0xd0, 0xd3, 0xd6, 0xd8, 0xd8, 0xd8, 0xd9, + 0xdc, 0xe1, 0xe4, 0xe3, 0xe3, 0xe4, 0xe8, 0xeb, 0xec, 0xeb, 0xec, 0xef, 0xf1, 0xf3, 0xf3, 0xf4, 0xf4, 0xf6, 0xfa, 0xfc, 0xfe, 0xff, + 0x00, 0x02, 0x03, 0x04, 0x05, 0x03, 0x03, 0x05, 0x06, 0x08, 0x0c, 0x10, 0x12, 0x11, 0x10, 0x12, 0x15, 0x17, 0x17, 0x19, 0x1b, 0x1e, + 0x21, 0x21, 0x20, 0x21, 0x24, 0x27, 0x29, 0x29, 0x28, 0x28, 0x29, 0x2b, 0x2d, 0x2e, 0x2e, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2d, 0x2d, 0x2e, 0x30, 0x31, 0x33, 0x34, 0x36, 0x38, 0x3a, 0x3b, 0x3b, 0x3c, 0x3f, 0x42, 0x43, 0x43, 0x40, 0x3e, 0x3f, 0x40, 0x41, + 0x41, 0x40, 0x40, 0x41, 0x42, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x46, 0x46, 0x43, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, + 0x3b, 0x3b, 0x3c, 0x3b, 0x39, 0x36, 0x34, 0x34, 0x35, 0x36, 0x35, 0x35, 0x34, 0x35, 0x35, 0x33, 0x30, 0x2d, 0x2b, 0x2b, 0x2b, 0x29, + 0x26, 0x23, 0x22, 0x23, 0x20, 0x1c, 0x19, 0x19, 0x1b, 0x1c, 0x1a, 0x17, 0x13, 0x10, 0x0d, 0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x06, 0x07, + 0x08, 0x05, 0xff, 0xfc, 0xfb, 0xfb, 0xfa, 0xf9, 0xf7, 0xf5, 0xf3, 0xf3, 0xf4, 0xf4, 0xf2, 0xf0, 0xef, 0xef, 0xed, 0xeb, 0xea, 0xe9, + 0xe8, 0xe6, 0xe4, 0xe5, 0xe6, 0xe7, 0xe6, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe4, 0xe1, 0xdf, 0xdd, 0xdb, 0xd9, 0xd7, 0xd6, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd5, 0xd4, 0xd4, 0xd4, 0xd5, 0xd7, 0xd8, 0xd9, 0xda, 0xda, 0xd9, 0xd8, 0xd8, 0xd7, 0xd7, 0xd8, 0xd9, + 0xd7, 0xd7, 0xd8, 0xd9, 0xda, 0xd8, 0xd5, 0xd4, 0xd2, 0xd2, 0xd3, 0xd3, 0xd2, 0xd1, 0xcf, 0xcf, 0xcf, 0xd0, 0xcf, 0xce, 0xce, 0xce, + 0xce, 0xcf, 0xce, 0xcd, 0xcd, 0xcd, 0xce, 0xd0, 0xd2, 0xd3, 0xd2, 0xd0, 0xcf, 0xce, 0xd0, 0xd2, 0xd2, 0xd1, 0xcf, 0xce, 0xcd, 0xce, + 0xcf, 0xd1, 0xd2, 0xd2, 0xd1, 0xd0, 0xcf, 0xcf, 0xd0, 0xd0, 0xd1, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd3, 0xd5, 0xd6, 0xd7, 0xd7, 0xd8, + 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdc, 0xdd, 0xdf, 0xdf, 0xdf, 0xe1, 0xe5, 0xe8, 0xe9, 0xe9, 0xeb, 0xee, 0xf0, 0xf2, 0xf5, 0xf8, + 0xfb, 0xfc, 0xfd, 0xfe, 0x01, 0x03, 0x04, 0x03, 0x04, 0x07, 0x0c, 0x0f, 0x10, 0x0f, 0x0f, 0x10, 0x12, 0x14, 0x15, 0x16, 0x18, 0x1c, + 0x20, 0x23, 0x23, 0x23, 0x24, 0x29, 0x2d, 0x30, 0x31, 0x30, 0x2f, 0x2f, 0x30, 0x33, 0x34, 0x34, 0x33, 0x32, 0x32, 0x34, 0x35, 0x37, + 0x36, 0x35, 0x35, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x33, 0x33, 0x34, 0x35, 0x35, 0x36, 0x37, 0x39, 0x38, 0x37, 0x36, 0x37, 0x37, + 0x37, 0x36, 0x36, 0x37, 0x37, 0x39, 0x3b, 0x3c, 0x3c, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x38, 0x35, 0x33, 0x33, 0x33, 0x32, 0x30, 0x2f, + 0x2f, 0x30, 0x31, 0x32, 0x32, 0x31, 0x2f, 0x2e, 0x2d, 0x2c, 0x2c, 0x2a, 0x28, 0x26, 0x25, 0x25, 0x26, 0x27, 0x25, 0x24, 0x25, 0x26, + 0x27, 0x27, 0x27, 0x26, 0x25, 0x24, 0x22, 0x20, 0x20, 0x21, 0x22, 0x21, 0x20, 0x1f, 0x1d, 0x1d, 0x1d, 0x1c, 0x1a, 0x17, 0x15, 0x13, + 0x13, 0x14, 0x14, 0x12, 0x0f, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x09, 0x08, 0x08, 0x07, 0x05, 0x02, 0x00, 0x00, 0x00, 0x01, 0xff, + 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf3, 0xf1, 0xef, 0xed, 0xec, 0xea, 0xe9, 0xe9, 0xe9, 0xe8, + 0xe7, 0xe6, 0xe4, 0xe2, 0xe0, 0xdf, 0xdd, 0xdb, 0xd8, 0xd8, 0xd8, 0xd9, 0xd8, 0xd6, 0xd5, 0xd3, 0xd3, 0xd3, 0xd4, 0xd3, 0xd2, 0xd3, + 0xd4, 0xd3, 0xd1, 0xcf, 0xce, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcc, 0xc9, 0xc6, 0xc4, 0xc2, 0xc1, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbd, 0xbd, 0xbe, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc3, 0xc4, 0xc5, 0xc4, 0xc2, 0xc0, 0xc0, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc5, + 0xc6, 0xc5, 0xc4, 0xc3, 0xc4, 0xc4, 0xc5, 0xc5, 0xc4, 0xc4, 0xc5, 0xc5, 0xc4, 0xc5, 0xc7, 0xca, 0xcd, 0xd0, 0xd1, 0xd2, 0xd2, 0xd2, + 0xd3, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd5, 0xd7, 0xd9, 0xda, 0xdc, 0xde, 0xe1, 0xe3, 0xe5, 0xe8, 0xea, 0xeb, 0xeb, 0xec, 0xee, 0xf0, + 0xf3, 0xf5, 0xf7, 0xf7, 0xf7, 0xf8, 0xf9, 0xfb, 0xfc, 0xff, 0x01, 0x03, 0x03, 0x05, 0x07, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x12, 0x14, + 0x16, 0x19, 0x1b, 0x1c, 0x1f, 0x20, 0x22, 0x23, 0x24, 0x25, 0x25, 0x26, 0x26, 0x26, 0x27, 0x28, 0x2a, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, + 0x30, 0x32, 0x33, 0x34, 0x35, 0x36, 0x36, 0x37, 0x39, 0x3b, 0x3c, 0x3d, 0x3d, 0x3d, 0x3e, 0x3f, 0x40, 0x40, 0x40, 0x41, 0x41, 0x42, + 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x40, 0x40, 0x41, 0x43, 0x42, 0x43, 0x43, 0x42, 0x40, 0x3f, 0x3f, + 0x41, 0x42, 0x42, 0x42, 0x40, 0x3e, 0x3e, 0x3f, 0x40, 0x42, 0x42, 0x42, 0x41, 0x40, 0x3f, 0x3d, 0x3c, 0x3a, 0x38, 0x36, 0x35, 0x34, + 0x34, 0x33, 0x31, 0x2f, 0x2e, 0x2e, 0x2c, 0x2b, 0x2b, 0x2a, 0x29, 0x28, 0x26, 0x25, 0x25, 0x25, 0x25, 0x23, 0x21, 0x1f, 0x1e, 0x1f, + 0x1e, 0x1c, 0x19, 0x16, 0x13, 0x11, 0x10, 0x0e, 0x0d, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x03, 0x01, 0x00, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, + 0xf9, 0xf7, 0xf6, 0xf5, 0xf5, 0xf6, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf1, 0xef, 0xed, 0xec, 0xe9, 0xe6, 0xe3, 0xe1, 0xe0, 0xde, 0xdd, + 0xdc, 0xdd, 0xde, 0xde, 0xdd, 0xdb, 0xd9, 0xd8, 0xd8, 0xd9, 0xd8, 0xd7, 0xd5, 0xd4, 0xd2, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, 0xd1, 0xd2, + 0xd3, 0xd2, 0xd1, 0xd0, 0xd0, 0xd0, 0xce, 0xce, 0xcd, 0xcd, 0xcc, 0xcb, 0xc9, 0xc7, 0xc7, 0xc7, 0xc7, 0xc6, 0xc5, 0xc4, 0xc3, 0xc2, + 0xc3, 0xc3, 0xc3, 0xc2, 0xc1, 0xc2, 0xc3, 0xc3, 0xc2, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, + 0xc1, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcd, 0xce, 0xce, 0xce, + 0xcf, 0xd1, 0xd2, 0xd4, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, 0xd7, 0xd8, 0xd9, 0xdb, 0xdc, 0xdd, 0xdf, 0xe1, 0xe3, 0xe5, 0xe6, 0xe8, 0xe8, + 0xe9, 0xea, 0xeb, 0xed, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf7, 0xf8, 0xfa, 0xfb, 0xfd, 0xff, 0x01, 0x03, 0x05, 0x06, 0x07, 0x08, 0x0a, + 0x0c, 0x0d, 0x0e, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1d, 0x20, 0x22, 0x24, 0x25, 0x25, 0x25, 0x26, + 0x27, 0x29, 0x2b, 0x2c, 0x2d, 0x2d, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x30, 0x31, 0x31, 0x31, 0x32, 0x34, 0x36, 0x38, 0x39, 0x39, + 0x39, 0x3a, 0x3b, 0x3d, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3c, 0x3b, 0x3a, 0x39, 0x37, 0x37, 0x37, + 0x37, 0x38, 0x39, 0x3a, 0x3a, 0x39, 0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3b, 0x3b, 0x3b, 0x3a, + 0x39, 0x38, 0x37, 0x38, 0x39, 0x3a, 0x3a, 0x39, 0x39, 0x38, 0x37, 0x36, 0x36, 0x35, 0x34, 0x32, 0x31, 0x2f, 0x2e, 0x2e, 0x2f, 0x2f, + 0x30, 0x2f, 0x2e, 0x2d, 0x2d, 0x2c, 0x2b, 0x29, 0x28, 0x26, 0x24, 0x22, 0x20, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x16, 0x14, + 0x13, 0x12, 0x10, 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xfe, 0xfc, 0xfa, 0xf7, 0xf5, 0xf4, 0xf2, 0xf1, + 0xef, 0xee, 0xec, 0xeb, 0xeb, 0xeb, 0xea, 0xe8, 0xe6, 0xe5, 0xe3, 0xe2, 0xe1, 0xde, 0xdc, 0xd9, 0xd7, 0xd4, 0xd2, 0xd0, 0xce, 0xcc, + 0xcb, 0xca, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7, 0xc6, 0xc6, 0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc1, 0xc0, 0xc1, 0xc1, 0xc0, 0xbf, + 0xbe, 0xbe, 0xbf, 0xc0, 0xbf, 0xbe, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xc0, 0xc1, 0xc0, 0xc0, 0xbf, 0xbe, 0xbc, 0xbb, 0xbb, 0xbb, 0xba, + 0xb9, 0xb8, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xba, 0xbb, 0xbc, 0xbc, 0xbd, 0xbe, 0xbe, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc2, 0xc3, 0xc4, + 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd2, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd9, 0xd9, 0xda, 0xdb, + 0xdd, 0xde, 0xe0, 0xe1, 0xe3, 0xe4, 0xe5, 0xe7, 0xe9, 0xeb, 0xed, 0xef, 0xf1, 0xf3, 0xf5, 0xf8, 0xfb, 0xfd, 0xff, 0x00, 0x02, 0x03, + 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x16, 0x16, 0x17, 0x17, 0x18, 0x19, 0x1b, 0x1d, 0x1f, 0x21, + 0x23, 0x25, 0x27, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2c, 0x2e, 0x2f, 0x2f, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x33, 0x34, + 0x36, 0x37, 0x37, 0x37, 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x40, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x40, 0x40, 0x3f, 0x40, 0x40, 0x3f, 0x3e, 0x3e, 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3d, 0x3c, 0x3c, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x37, 0x36, 0x35, 0x34, 0x34, 0x34, 0x33, 0x32, 0x31, 0x30, 0x30, 0x30, 0x30, + 0x2f, 0x2e, 0x2c, 0x2a, 0x29, 0x27, 0x26, 0x24, 0x22, 0x22, 0x21, 0x20, 0x1e, 0x1c, 0x1c, 0x1b, 0x1a, 0x18, 0x17, 0x15, 0x13, 0x12, + 0x10, 0x0e, 0x0c, 0x0a, 0x09, 0x07, 0x06, 0x05, 0x03, 0x02, 0x00, 0xfe, 0xfc, 0xfa, 0xf9, 0xf8, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3, 0xf3, + 0xf2, 0xf1, 0xef, 0xee, 0xec, 0xeb, 0xe9, 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe0, 0xdf, 0xdd, 0xdc, 0xdb, 0xda, 0xda, 0xd9, 0xd7, + 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xcf, 0xce, 0xcd, 0xcd, 0xcd, 0xcc, 0xca, + 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xcf, 0xcf, 0xd0, 0xd1, 0xd1, 0xd1, 0xd2, 0xd3, + 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd2, 0xd1, 0xd1, 0xd1, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd5, 0xd5, 0xd4, 0xd4, + 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xdf, 0xdf, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe4, 0xe4, 0xe6, 0xe7, 0xe8, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfb, 0xfd, 0xfe, 0x00, 0x01, 0x01, 0x02, 0x04, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x19, 0x1a, 0x1b, 0x1d, 0x1d, 0x1e, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, + 0x2b, 0x2c, 0x2b, 0x2b, 0x2b, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2d, 0x2d, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2b, 0x2b, 0x2a, 0x29, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x26, 0x25, 0x25, 0x24, 0x23, 0x21, 0x20, + 0x20, 0x20, 0x20, 0x1f, 0x1e, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x18, 0x18, 0x17, 0x17, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0e, 0x0b, + 0x09, 0x08, 0x07, 0x07, 0x06, 0x05, 0x04, 0x02, 0x01, 0x00, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7, 0xf7, + 0xf5, 0xf3, 0xf2, 0xf1, 0xf0, 0xee, 0xec, 0xeb, 0xe9, 0xe8, 0xe7, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xde, + 0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xda, 0xd9, 0xd8, 0xd8, 0xd8, 0xd7, 0xd6, 0xd6, 0xd4, 0xd4, 0xd4, 0xd3, 0xd2, 0xd1, 0xd1, 0xd0, 0xcf, + 0xcf, 0xce, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xcf, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd4, 0xd5, 0xd7, 0xd8, + 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe5, 0xe6, 0xe6, 0xe7, 0xe8, 0xe8, 0xe9, 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xee, 0xee, 0xef, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, + 0xfd, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, 0x1f, + 0x1f, 0x20, 0x20, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x26, 0x25, 0x26, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x26, 0x26, 0x26, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x27, 0x27, 0x26, 0x25, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x1f, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x15, 0x14, 0x13, 0x13, 0x13, 0x14, 0x14, 0x13, 0x13, 0x13, + 0x13, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11, 0x10, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x08, 0x07, 0x06, 0x05, + 0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfc, 0xfb, 0xfa, 0xf9, 0xf7, 0xf7, 0xf6, + 0xf6, 0xf5, 0xf4, 0xf3, 0xf1, 0xf1, 0xf0, 0xef, 0xed, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe8, + 0xe8, 0xe7, 0xe6, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe6, 0xe6, 0xe6, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xec, 0xed, 0xef, 0xf1, 0xf2, 0xf2, + 0xf3, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf5, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, + 0xfd, 0xff, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0a, 0x0a, 0x0b, 0x0c, 0x0d, 0x0d, 0x0e, 0x0f, 0x0f, + 0x10, 0x11, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, + 0x1e, 0x1f, 0x20, 0x21, 0x21, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24, 0x25, 0x25, 0x26, 0x27, 0x27, 0x28, 0x28, 0x28, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, 0x26, 0x25, 0x25, 0x26, 0x27, 0x27, 0x27, 0x26, + 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x27, 0x26, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x26, 0x25, 0x24, 0x24, 0x23, 0x23, + 0x22, 0x20, 0x1f, 0x1d, 0x1c, 0x1b, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x13, 0x12, + 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x01, 0x00, 0xff, 0xfd, 0xfc, 0xfa, 0xf9, + 0xf8, 0xf7, 0xf5, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xee, 0xed, 0xec, 0xec, 0xeb, 0xeb, 0xea, 0xe9, 0xe8, + 0xe7, 0xe6, 0xe6, 0xe5, 0xe5, 0xe4, 0xe4, 0xe3, 0xe2, 0xe1, 0xe1, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdb, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xda, 0xdb, + 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe2, 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe6, 0xe7, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xec, 0xed, 0xee, 0xef, 0xf0, + 0xf0, 0xf1, 0xf2, 0xf2, 0xf3, 0xf4, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xf9, 0xfa, 0xfa, 0xfb, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, + 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x11, 0x12, + 0x13, 0x14, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x22, 0x22, 0x22, 0x22, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1e, 0x1e, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1a, + 0x19, 0x19, 0x18, 0x17, 0x16, 0x15, 0x15, 0x14, 0x13, 0x12, 0x12, 0x11, 0x10, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0b, 0x0a, 0x09, + 0x08, 0x07, 0x07, 0x06, 0x06, 0x05, 0x04, 0x03, 0x03, 0x02, 0x01, 0x00, 0xff, 0xfe, 0xfc, 0xfb, 0xfa, 0xf8, 0xf6, 0xf5, 0xf3, 0xf2, + 0xf0, 0xef, 0xee, 0xed, 0xec, 0xeb, 0xeb, 0xea, 0xe9, 0xe8, 0xe8, 0xe7, 0xe6, 0xe5, 0xe4, 0xe4, 0xe3, 0xe3, 0xe2, 0xe1, 0xe1, 0xe0, + 0xdf, 0xde, 0xde, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdc, 0xdb, 0xdb, 0xda, 0xda, 0xd9, 0xd8, 0xd8, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdc, 0xdd, 0xdd, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xe0, 0xe1, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf0, 0xf1, 0xf2, 0xf4, 0xf5, + 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x08, 0x0a, 0x0c, 0x0d, 0x0f, 0x10, 0x12, + 0x13, 0x14, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1e, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2c, 0x2d, 0x2e, 0x2f, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x36, 0x36, 0x35, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, + 0x29, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, + 0x16, 0x15, 0x14, 0x12, 0x11, 0x10, 0x0e, 0x0d, 0x0b, 0x0a, 0x08, 0x07, 0x05, 0x04, 0x03, 0x02, 0x00, 0xff, 0xfe, 0xfd, 0xfc, 0xfc, + 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf5, 0xf4, 0xf2, 0xf1, 0xf0, 0xef, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, + 0xe1, 0xe0, 0xe0, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xdd, 0xdc, 0xdc, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xd9, 0xd8, 0xd8, 0xd8, 0xd7, 0xd7, + 0xd6, 0xd5, 0xd5, 0xd4, 0xd3, 0xd3, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd5, 0xd5, 0xd6, 0xd7, 0xd7, 0xd8, 0xd8, 0xd9, + 0xd9, 0xda, 0xda, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xde, 0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe5, + 0xe6, 0xe7, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xed, 0xee, 0xef, 0xf1, 0xf3, 0xf4, 0xf6, 0xf7, 0xf8, 0xfa, 0xfb, 0xfd, 0xfe, 0xff, 0x00, + 0x02, 0x03, 0x04, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0b, 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x13, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x1b, 0x1c, 0x1d, 0x1e, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2a, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, + 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2e, 0x2d, 0x2d, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2a, 0x2a, 0x2a, 0x29, 0x29, 0x28, 0x28, 0x27, 0x26, 0x26, 0x25, 0x24, 0x23, 0x23, 0x22, + 0x22, 0x22, 0x21, 0x21, 0x20, 0x20, 0x1f, 0x1f, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x15, + 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x05, 0x03, 0x02, 0x01, 0x00, + 0xff, 0xfe, 0xfd, 0xfc, 0xfc, 0xfb, 0xfa, 0xfa, 0xf9, 0xf8, 0xf7, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, + 0xe9, 0xe8, 0xe6, 0xe5, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, 0xe0, 0xdf, 0xde, 0xde, 0xdd, 0xdd, 0xdc, 0xdc, 0xdb, 0xda, 0xda, 0xd9, + 0xd8, 0xd8, 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd2, 0xd1, 0xd0, 0xd0, 0xcf, 0xce, 0xce, 0xce, 0xcd, 0xcd, 0xcd, 0xcc, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xd0, 0xd1, 0xd2, 0xd2, 0xd3, 0xd4, 0xd4, 0xd5, 0xd6, 0xd6, 0xd7, 0xd7, 0xd8, 0xd8, 0xd9, + 0xd9, 0xda, 0xdb, 0xdb, 0xdc, 0xdd, 0xdd, 0xde, 0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe3, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xeb, 0xec, 0xed, 0xee, 0xf0, 0xf1, 0xf2, 0xf3, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, 0x2a, 0x2b, + 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, + 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2e, 0x2d, 0x2d, + 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, 0x2a, 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x27, 0x27, 0x26, 0x26, 0x26, 0x25, 0x24, 0x24, 0x24, 0x23, + 0x22, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x12, 0x11, 0x10, 0x10, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xff, 0xfe, 0xfd, 0xfb, + 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0xef, 0xee, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, 0xe7, 0xe6, 0xe5, + 0xe4, 0xe3, 0xe3, 0xe2, 0xe1, 0xe0, 0xdf, 0xde, 0xde, 0xdd, 0xdc, 0xdb, 0xdb, 0xda, 0xda, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, + 0xdb, 0xdc, 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xde, 0xdf, 0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe6, 0xe6, + 0xe7, 0xe8, 0xe8, 0xe9, 0xea, 0xea, 0xeb, 0xec, 0xec, 0xed, 0xed, 0xee, 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf3, 0xf4, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfb, 0xfc, 0xfd, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x04, + 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0c, 0x0d, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x13, 0x14, + 0x15, 0x16, 0x16, 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1d, 0x1d, + 0x1e, 0x1e, 0x1f, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x12, 0x12, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x07, 0x07, + 0x06, 0x06, 0x05, 0x05, 0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, + 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf7, 0xf7, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1, 0xf1, + 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xee, 0xee, 0xed, 0xed, 0xec, 0xec, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xe9, 0xe8, 0xe8, 0xe7, 0xe7, 0xe6, + 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, + 0xe5, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xed, 0xed, 0xee, 0xef, 0xef, 0xf0, 0xf1, 0xf1, 0xf2, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, + 0xf4, 0xf5, 0xf5, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf9, 0xfa, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0x00, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, + 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x15, + 0x15, 0x15, 0x14, 0x14, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x12, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, + 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xfd, 0xfd, + 0xfc, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf7, 0xf7, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf1, 0xf1, + 0xf1, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xee, 0xee, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, + 0xeb, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xee, 0xee, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, + 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf6, 0xf6, 0xf7, 0xf8, 0xf8, 0xf9, 0xfa, 0xfb, 0xfb, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, + 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, + 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x11, + 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, + 0x16, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1c, + 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13, 0x12, 0x12, + 0x11, 0x10, 0x0f, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xff, 0xfe, 0xfe, + 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf4, 0xf3, 0xf2, 0xf1, 0xf1, 0xf0, 0xef, 0xef, 0xee, 0xed, 0xec, + 0xeb, 0xeb, 0xea, 0xe9, 0xe8, 0xe8, 0xe7, 0xe6, 0xe5, 0xe4, 0xe4, 0xe3, 0xe2, 0xe1, 0xe1, 0xe0, 0xdf, 0xdf, 0xde, 0xdd, 0xdd, 0xdc, + 0xdc, 0xdb, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, + 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xe0, 0xe0, 0xe1, 0xe1, 0xe2, + 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe6, 0xe6, 0xe7, 0xe8, 0xe8, 0xe9, 0xea, 0xea, 0xeb, 0xec, 0xec, 0xed, 0xee, 0xee, 0xef, 0xf0, 0xf0, + 0xf1, 0xf2, 0xf2, 0xf3, 0xf4, 0xf4, 0xf5, 0xf6, 0xf7, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0x00, 0x01, + 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x11, 0x12, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x21, + 0x21, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2b, 0x2b, 0x2b, 0x2a, 0x2a, 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x27, 0x27, 0x26, 0x26, 0x25, 0x25, 0x24, 0x24, 0x23, 0x23, + 0x22, 0x22, 0x21, 0x20, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, + 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x00, 0xff, 0xfe, 0xfd, + 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf2, 0xf1, 0xf0, 0xef, 0xef, 0xee, 0xee, 0xed, 0xec, 0xeb, 0xeb, + 0xea, 0xe9, 0xe8, 0xe8, 0xe7, 0xe6, 0xe6, 0xe5, 0xe4, 0xe4, 0xe3, 0xe2, 0xe2, 0xe1, 0xe1, 0xe0, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xdd, + 0xdd, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdc, + 0xdc, 0xdd, 0xdd, 0xde, 0xde, 0xdf, 0xe0, 0xe0, 0xe1, 0xe1, 0xe2, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe8, 0xe9, 0xea, + 0xeb, 0xec, 0xed, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, + 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x11, 0x12, + 0x13, 0x13, 0x14, 0x15, 0x16, 0x17, 0x17, 0x18, 0x19, 0x1a, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x21, + 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x27, 0x27, 0x26, 0x26, 0x25, 0x25, 0x25, 0x24, 0x24, 0x23, 0x23, 0x23, 0x22, 0x22, + 0x21, 0x21, 0x20, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1d, 0x1c, 0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x15, 0x15, 0x14, + 0x13, 0x13, 0x12, 0x12, 0x11, 0x10, 0x10, 0x0f, 0x0f, 0x0e, 0x0d, 0x0d, 0x0c, 0x0b, 0x0b, 0x0a, 0x09, 0x09, 0x08, 0x07, 0x06, 0x06, + 0x05, 0x04, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0xff, 0xfe, 0xfd, 0xfc, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf7, 0xf6, 0xf5, 0xf4, + 0xf3, 0xf2, 0xf1, 0xf0, 0xf0, 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe9, 0xe8, 0xe7, 0xe6, 0xe5, 0xe4, 0xe4, 0xe3, 0xe2, 0xe1, + 0xe1, 0xe0, 0xdf, 0xdf, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdb, 0xdb, 0xda, 0xda, 0xd9, 0xd9, 0xd8, 0xd8, 0xd7, 0xd7, 0xd7, + 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, + 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd6, 0xd6, 0xd7, 0xd7, 0xd8, 0xd9, 0xd9, + 0xda, 0xda, 0xdb, 0xdb, 0xdc, 0xdc, 0xdd, 0xdd, 0xde, 0xde, 0xdf, 0xe0, 0xe0, 0xe1, 0xe1, 0xe2, 0xe2, 0xe3, 0xe4, 0xe5, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xea, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x19, 0x19, 0x1a, 0x1a, 0x1b, 0x1c, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1f, + 0x1f, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23, 0x23, 0x22, 0x22, 0x21, 0x21, 0x21, + 0x20, 0x1f, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1b, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x17, 0x16, 0x15, 0x15, 0x14, 0x13, + 0x13, 0x12, 0x11, 0x11, 0x10, 0x0f, 0x0f, 0x0e, 0x0d, 0x0c, 0x0c, 0x0b, 0x0a, 0x09, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x04, 0x03, + 0x02, 0x01, 0x00, 0x00, 0xff, 0xfe, 0xfd, 0xfd, 0xfc, 0xfb, 0xfb, 0xfa, 0xf9, 0xf9, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6, 0xf5, 0xf4, 0xf4, + 0xf3, 0xf3, 0xf2, 0xf1, 0xf1, 0xf0, 0xf0, 0xef, 0xee, 0xee, 0xed, 0xed, 0xec, 0xec, 0xeb, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, + 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, + 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xea, 0xea, 0xeb, 0xeb, 0xeb, + 0xec, 0xec, 0xed, 0xed, 0xee, 0xee, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf6, 0xf6, 0xf7, 0xf7, 0xf8, + 0xf8, 0xf9, 0xf9, 0xfa, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, + 0x04, 0x05, 0x05, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, + 0x0e, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, + 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, 0x15, 0x14, 0x14, 0x14, 0x13, 0x13, + 0x13, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, + 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x07, 0x07, 0x06, 0x06, 0x05, 0x05, 0x04, 0x04, + 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfb, + 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xea, 0xea, + 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, + 0xed, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf8, 0xf8, 0xf9, 0xf9, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, + 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x08, + 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x11, 0x11, 0x11, 0x12, + 0x12, 0x12, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, 0x15, 0x15, 0x14, + 0x14, 0x14, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, + 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x07, 0x06, 0x06, 0x05, 0x05, + 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfc, + 0xfc, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf4, + 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, + 0xee, 0xee, 0xed, 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, + 0xeb, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf1, + 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, + 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, + 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, + 0x0f, 0x0f, 0x10, 0x10, 0x11, 0x11, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14, 0x14, 0x14, 0x15, 0x15, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, + 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, 0x15, 0x15, 0x14, 0x14, + 0x14, 0x14, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, + 0x0d, 0x0c, 0x0c, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x06, 0x05, 0x04, 0x04, 0x03, 0x02, 0x02, + 0x01, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfb, 0xfa, 0xfa, 0xf9, 0xf8, 0xf8, 0xf7, 0xf6, 0xf5, 0xf5, 0xf4, 0xf3, 0xf3, + 0xf2, 0xf2, 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xee, 0xee, 0xed, 0xed, 0xec, 0xec, 0xeb, 0xea, 0xea, 0xe9, 0xe9, 0xe8, 0xe8, 0xe7, 0xe7, + 0xe6, 0xe6, 0xe5, 0xe5, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe5, + 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe9, 0xe9, 0xea, 0xea, 0xeb, 0xeb, 0xec, 0xed, 0xed, 0xee, 0xee, 0xef, + 0xef, 0xf0, 0xf1, 0xf1, 0xf2, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf6, 0xf6, 0xf7, 0xf8, 0xf8, 0xf9, 0xf9, 0xfa, 0xfb, 0xfb, 0xfc, 0xfd, + 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, + 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14, 0x15, 0x15, 0x16, 0x16, 0x17, 0x18, 0x18, 0x19, 0x19, + 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x21, 0x21, + 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1f, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, + 0x1c, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x17, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, + 0x10, 0x10, 0x0f, 0x0e, 0x0e, 0x0d, 0x0c, 0x0c, 0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x03, 0x03, 0x02, + 0x01, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfb, 0xfa, 0xfa, 0xf9, 0xf8, 0xf8, 0xf7, 0xf6, 0xf6, 0xf5, 0xf4, 0xf4, + 0xf3, 0xf2, 0xf2, 0xf1, 0xf1, 0xf0, 0xef, 0xef, 0xee, 0xee, 0xed, 0xed, 0xed, 0xec, 0xec, 0xeb, 0xeb, 0xea, 0xea, 0xe9, 0xe9, 0xe8, + 0xe8, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe4, + 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xea, 0xea, 0xeb, 0xeb, 0xec, 0xec, 0xed, + 0xee, 0xee, 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf2, 0xf2, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, 0xf6, 0xf7, 0xf8, 0xf8, 0xf9, 0xfa, + 0xfa, 0xfb, 0xfb, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x06, 0x06, 0x07, + 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x12, 0x13, + 0x13, 0x14, 0x14, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1a, 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x14, + 0x13, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0a, + 0x0a, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x06, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfe, + 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7, 0xf7, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, + 0xf1, 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xee, 0xee, 0xed, 0xed, 0xec, 0xec, 0xeb, 0xeb, 0xea, 0xea, 0xe9, 0xe9, 0xe8, 0xe8, 0xe7, 0xe7, + 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, + 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, + 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xed, 0xed, 0xee, 0xee, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf3, + 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, 0xf6, 0xf7, 0xf8, 0xf8, 0xf9, 0xf9, 0xfa, 0xfa, 0xfb, 0xfb, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xff, + 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, + 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, + 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x16, 0x16, + 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, + 0x0d, 0x0d, 0x0c, 0x0c, 0x0b, 0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x07, 0x06, 0x06, 0x05, 0x05, 0x04, 0x04, 0x03, + 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, + 0xf8, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xef, + 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, + 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf9, + 0xf9, 0xf9, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, + 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf2, 0xf2, 0xf2, 0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, + 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, + 0x09, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, + 0x10, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, + 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, + 0xf2, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, + 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, + 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf9, + 0xf9, 0xf9, 0xfa, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0b, + 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x03, + 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, + 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1, 0xf1, + 0xf1, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, + 0xea, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, + 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xf0, + 0xf0, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf7, 0xf7, 0xf8, 0xf8, 0xf9, 0xf9, + 0xfa, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, + 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, + 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, 0x15, 0x14, 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, + 0x07, 0x07, 0x06, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfd, + 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, + 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, + 0xed, 0xec, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, + 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xee, + 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf5, 0xf5, 0xf6, + 0xf6, 0xf6, 0xf7, 0xf7, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x08, + 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, + 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, + 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf2, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, + 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, + 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, + 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, + 0x09, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, + 0x07, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, + 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, + 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, + 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfb, 0xfc, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x04, 0x04, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, +}; + +static const unsigned char TOP_2608[66610] = { + 0x52, 0x49, 0x46, 0x46, 0x30, 0x08, 0x02, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x44, 0xac, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x02, 0x00, 0x10, 0x00, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x61, 0x61, 0x08, 0x00, 0x00, 0xff, 0xff, 0x02, 0xff, 0xfe, 0x00, 0xfc, 0x00, 0x01, 0xfb, 0x01, 0x01, 0xfd, 0xfe, 0x04, + 0x03, 0xfe, 0x07, 0xf3, 0xed, 0x09, 0xfc, 0x01, 0x0b, 0xf8, 0xf6, 0xfd, 0x04, 0x08, 0x04, 0xfb, 0xf7, 0xf6, 0xfc, 0x04, 0x0c, 0x00, + 0xfa, 0x03, 0x05, 0x00, 0xfa, 0xf9, 0xef, 0xf3, 0x01, 0x0a, 0x09, 0x04, 0x02, 0x02, 0x01, 0xff, 0xfb, 0xf9, 0xfc, 0x02, 0x05, 0x02, + 0xfe, 0xfe, 0x00, 0x05, 0x06, 0x00, 0x00, 0xfc, 0xf9, 0x03, 0x05, 0x03, 0x04, 0x05, 0x06, 0x06, 0x06, 0xfd, 0xfb, 0x00, 0xf7, 0xff, + 0x06, 0xfd, 0x05, 0x00, 0xf6, 0xfc, 0x02, 0xfe, 0xf6, 0x09, 0x02, 0xf6, 0x00, 0xf9, 0xff, 0x08, 0xf5, 0xfb, 0xfe, 0xf6, 0xff, 0x00, + 0xfe, 0x02, 0xff, 0xfd, 0xfe, 0xfd, 0x00, 0x02, 0xff, 0x07, 0xfd, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x04, 0xfe, 0x0a, 0x04, 0xfa, + 0xf8, 0x06, 0xfb, 0xfd, 0x0c, 0xfb, 0xfc, 0x04, 0x02, 0x05, 0x05, 0x02, 0xfe, 0x04, 0x03, 0xfa, 0xfd, 0xfc, 0xfa, 0xfb, 0xf8, 0xfc, + 0x01, 0xfe, 0xf8, 0xf8, 0x00, 0x05, 0x08, 0x0b, 0x0d, 0xfd, 0xfa, 0x05, 0x02, 0x08, 0x07, 0xfc, 0xfc, 0x04, 0x04, 0xf5, 0xff, 0x05, + 0x00, 0xfe, 0x00, 0xfc, 0xf5, 0xf8, 0x03, 0x06, 0xf5, 0xf7, 0xfe, 0x00, 0xf9, 0xfb, 0xfd, 0xf8, 0xf3, 0x00, 0x0e, 0x06, 0xfd, 0xfb, + 0x00, 0x06, 0x03, 0xfa, 0xf6, 0xfe, 0x01, 0x03, 0x0f, 0x01, 0xfa, 0x04, 0xf6, 0x08, 0x14, 0xf8, 0x00, 0x07, 0x00, 0xf1, 0x00, 0x06, + 0xf7, 0x05, 0x07, 0xfc, 0xf6, 0xfe, 0xfd, 0xf7, 0x01, 0x00, 0xff, 0x07, 0xf5, 0x02, 0x10, 0xf5, 0xfb, 0x04, 0x00, 0xf9, 0xfc, 0xff, + 0xfe, 0xfe, 0x06, 0x08, 0xf2, 0x03, 0x0e, 0x03, 0xfd, 0x01, 0x04, 0xfd, 0x0d, 0x06, 0xf9, 0x0c, 0x02, 0xfc, 0x05, 0xfd, 0xfe, 0x07, + 0x0f, 0xf1, 0xf4, 0x0a, 0xf7, 0xfc, 0x03, 0xfc, 0xf8, 0xf1, 0xf3, 0x03, 0x04, 0x02, 0x04, 0x07, 0x00, 0xf7, 0xf7, 0xff, 0xfe, 0xfc, + 0x05, 0xf9, 0xfb, 0x07, 0xfa, 0xf9, 0x00, 0x04, 0xf7, 0xf8, 0x00, 0xf9, 0xfc, 0x00, 0xff, 0xf6, 0xff, 0x0b, 0x09, 0xfc, 0xff, 0x0a, + 0x00, 0x03, 0x02, 0x00, 0x13, 0x16, 0x07, 0xfa, 0xf0, 0xff, 0x0f, 0xfc, 0xfd, 0x05, 0x05, 0xf3, 0xf9, 0x02, 0xf6, 0xff, 0x01, 0xfe, + 0x0d, 0x00, 0xfa, 0x09, 0xfc, 0x02, 0x0a, 0xfb, 0x01, 0x08, 0x07, 0xff, 0x05, 0x00, 0xeb, 0x0e, 0x0c, 0xf9, 0x09, 0x05, 0xf9, 0xf5, + 0x00, 0xfb, 0x00, 0x16, 0xf4, 0xf0, 0x06, 0x08, 0xfb, 0xfe, 0x0f, 0xf7, 0xfb, 0x0a, 0x0b, 0xf1, 0xf2, 0x07, 0x06, 0xf8, 0xfb, 0x0b, + 0xe8, 0xf2, 0x07, 0xf8, 0xf3, 0xf6, 0xfa, 0xf5, 0x05, 0x05, 0xf2, 0xf7, 0x01, 0x03, 0xfd, 0xf8, 0x01, 0x09, 0xfc, 0xf4, 0x00, 0x14, + 0xff, 0xfa, 0x03, 0xff, 0x06, 0x04, 0xfc, 0x0c, 0xff, 0xf5, 0x04, 0x00, 0xff, 0x01, 0xf8, 0x02, 0x03, 0xf8, 0xfb, 0x05, 0x03, 0xf0, + 0x07, 0x07, 0xf8, 0x00, 0x05, 0x07, 0x03, 0xea, 0xfb, 0x11, 0xff, 0x0a, 0x05, 0xf5, 0xfb, 0x08, 0x09, 0xff, 0x0e, 0x05, 0xf6, 0xfc, + 0x01, 0x00, 0xfe, 0x06, 0x08, 0x03, 0xfc, 0x0b, 0x0a, 0x02, 0x05, 0x09, 0xff, 0xf1, 0xff, 0x0a, 0x0a, 0x00, 0x06, 0xf6, 0xed, 0x10, + 0xf9, 0xf2, 0x08, 0xf4, 0xf6, 0xfc, 0xf4, 0xf1, 0xf6, 0xfa, 0xf9, 0xf5, 0xfa, 0x02, 0xf7, 0xf5, 0xfc, 0x02, 0xf9, 0xf7, 0xff, 0x01, + 0x08, 0x02, 0xf3, 0xf7, 0xf7, 0xfd, 0x11, 0xfb, 0xf7, 0x06, 0xf1, 0x00, 0x11, 0x09, 0xf5, 0xf4, 0x01, 0x04, 0x0c, 0x00, 0xf0, 0x0d, + 0x07, 0xf8, 0xfd, 0x12, 0x03, 0xf3, 0x0e, 0x0b, 0x01, 0xfd, 0xf5, 0xfe, 0x09, 0x03, 0x08, 0x05, 0xfe, 0xfe, 0x05, 0x06, 0xfe, 0xfd, + 0x00, 0x05, 0x07, 0x00, 0xff, 0xfe, 0xe8, 0x08, 0x12, 0xf2, 0x06, 0x08, 0xfd, 0xf9, 0x0a, 0x04, 0xf5, 0x05, 0x10, 0x07, 0xf7, 0xf8, + 0x08, 0x0a, 0xe7, 0x00, 0x09, 0xf5, 0xf1, 0x03, 0x07, 0xf4, 0xfc, 0xfd, 0xf5, 0xf3, 0xf3, 0xfa, 0x00, 0xee, 0xf8, 0x07, 0x02, 0x05, + 0x01, 0xfd, 0x06, 0x09, 0xf7, 0xe3, 0x04, 0xfb, 0xf2, 0x0d, 0x00, 0xf4, 0xf7, 0xf6, 0x08, 0x08, 0xf4, 0xf9, 0x03, 0x00, 0xed, 0x04, + 0x13, 0x0b, 0xff, 0x03, 0x08, 0x02, 0xfe, 0x03, 0x0c, 0x12, 0x0b, 0x0b, 0x0e, 0xf9, 0x01, 0x08, 0xfa, 0x02, 0xff, 0xfa, 0x00, 0x0d, + 0x08, 0xfa, 0x02, 0x03, 0x07, 0x13, 0xf5, 0xfd, 0x0f, 0xe9, 0xfd, 0x0b, 0xfb, 0xf2, 0xfe, 0x07, 0xff, 0x03, 0x08, 0x05, 0xf3, 0x06, + 0x09, 0xf2, 0x08, 0x08, 0xfc, 0xfd, 0x02, 0xf9, 0xef, 0xf8, 0x01, 0xfe, 0xf3, 0xfc, 0xfd, 0xf9, 0xf9, 0x0f, 0x05, 0xeb, 0x03, 0xf4, + 0xec, 0x03, 0xff, 0xf8, 0xfb, 0x0a, 0x00, 0xf9, 0xfa, 0xfd, 0xf0, 0xf6, 0x14, 0x00, 0xf6, 0xfc, 0xfd, 0xf8, 0x04, 0x13, 0xf0, 0xf8, + 0x06, 0xf7, 0x05, 0x0d, 0x07, 0x08, 0x00, 0x03, 0x0d, 0x04, 0x01, 0x02, 0x00, 0x07, 0x0b, 0x09, 0x05, 0x05, 0x06, 0x05, 0xfb, 0xfd, + 0x06, 0x0c, 0x01, 0xf8, 0xf9, 0x05, 0x01, 0x07, 0x19, 0xff, 0xf7, 0x00, 0x01, 0xff, 0x01, 0x06, 0xfd, 0x04, 0x05, 0xf7, 0xff, 0x03, + 0xff, 0xfe, 0xed, 0xf7, 0x10, 0xf1, 0x01, 0x15, 0x02, 0xf3, 0xf7, 0x01, 0xf7, 0x03, 0x05, 0xf8, 0xfa, 0xff, 0xff, 0xf8, 0x00, 0xfd, + 0xf9, 0x0a, 0x07, 0xfa, 0xf5, 0x04, 0x06, 0xfe, 0xfb, 0x01, 0x01, 0xfd, 0x08, 0xf7, 0xf0, 0x02, 0x08, 0x07, 0x05, 0x07, 0x03, 0x00, + 0x00, 0xfd, 0xfc, 0x01, 0x08, 0xfe, 0xff, 0x03, 0xf8, 0xfb, 0x05, 0x07, 0xfc, 0xf8, 0xfc, 0x01, 0x0d, 0x03, 0xf3, 0xfc, 0xfa, 0xf8, + 0xfc, 0x08, 0xfb, 0xf4, 0x06, 0xfd, 0xfb, 0x02, 0x07, 0x05, 0x06, 0x09, 0xf2, 0xf4, 0x01, 0x01, 0x06, 0xff, 0xf4, 0x04, 0xee, 0xe9, + 0x07, 0x13, 0xfa, 0xeb, 0x10, 0xfd, 0xf8, 0x06, 0xfb, 0xee, 0xf1, 0x01, 0x0a, 0x00, 0xf7, 0x05, 0x07, 0x00, 0xfb, 0x05, 0x06, 0x01, + 0xfc, 0x04, 0xfb, 0xf4, 0x0c, 0x07, 0xff, 0x01, 0x03, 0xf9, 0xf3, 0xf8, 0x0e, 0x02, 0xf1, 0x13, 0x04, 0xfc, 0x0c, 0xfd, 0xfa, 0xff, + 0x00, 0xf4, 0xfb, 0x09, 0xfe, 0x04, 0x07, 0xfd, 0xfc, 0xf6, 0xf7, 0x0b, 0xf4, 0xf1, 0x07, 0x0b, 0xf7, 0xf4, 0x09, 0x00, 0xf5, 0xf3, + 0xfc, 0x02, 0xfe, 0xf9, 0x13, 0x00, 0xf1, 0x03, 0xf8, 0xf6, 0xfe, 0xff, 0x09, 0x03, 0xf1, 0x01, 0x02, 0xf8, 0xed, 0x0e, 0x01, 0xe6, + 0x0b, 0x0b, 0xf7, 0xed, 0xff, 0x04, 0x03, 0x09, 0x00, 0xfd, 0x01, 0xfe, 0xff, 0x01, 0x02, 0x05, 0x0e, 0x09, 0xed, 0x08, 0x0a, 0xf6, + 0x09, 0x0b, 0x04, 0x02, 0x05, 0x07, 0xfe, 0xe2, 0x1e, 0x1e, 0xe3, 0xfa, 0xf8, 0xf7, 0x08, 0x05, 0xff, 0x01, 0x04, 0xf9, 0xf8, 0x02, + 0x04, 0x06, 0x03, 0xfa, 0x0d, 0xff, 0xed, 0x10, 0x02, 0xf6, 0x00, 0x03, 0x00, 0xfc, 0xfa, 0xfd, 0xff, 0xfe, 0xf9, 0xfe, 0x01, 0xfd, + 0xfa, 0x04, 0x07, 0xf1, 0x08, 0x0c, 0xfb, 0xfb, 0x00, 0xfd, 0xf7, 0x0e, 0x10, 0xfd, 0xef, 0xff, 0x09, 0x00, 0xf8, 0xfc, 0xfb, 0xf2, + 0xfe, 0x07, 0x04, 0xf6, 0x00, 0x05, 0xff, 0xfe, 0x0b, 0x0b, 0xf7, 0x00, 0x03, 0xfe, 0xfc, 0x07, 0x0c, 0x04, 0xf8, 0xf8, 0x01, 0x0a, + 0xfd, 0x01, 0x0b, 0xfd, 0xf9, 0x03, 0x0d, 0xf9, 0xf3, 0xfc, 0x06, 0xfb, 0x04, 0x13, 0x00, 0x04, 0x0a, 0x03, 0xfc, 0xff, 0x07, 0x0e, + 0xfe, 0xff, 0x0a, 0xef, 0xf0, 0xfb, 0xff, 0xf3, 0x05, 0x14, 0xfc, 0xf6, 0x05, 0x11, 0xeb, 0xf1, 0x05, 0x06, 0xf5, 0xfe, 0x0b, 0xf6, + 0xfc, 0x02, 0xff, 0x04, 0xf8, 0xf5, 0x0c, 0xfa, 0xf9, 0x03, 0xf7, 0xfc, 0x0b, 0x0f, 0xf4, 0xf0, 0xff, 0x07, 0x07, 0x06, 0x03, 0xf8, + 0xfc, 0x01, 0x00, 0xff, 0x06, 0x08, 0xfa, 0xfd, 0xff, 0xff, 0x05, 0x08, 0x03, 0xfc, 0xfd, 0x04, 0x08, 0x08, 0xfd, 0x05, 0x15, 0xfc, + 0xfa, 0x02, 0x04, 0xfd, 0x05, 0x06, 0xe8, 0x03, 0x11, 0xfe, 0xfa, 0xf7, 0xfa, 0x02, 0xef, 0xff, 0x12, 0xe9, 0xfd, 0x0f, 0x01, 0x00, + 0xf8, 0xf9, 0x0b, 0xf3, 0xee, 0xff, 0x07, 0xfc, 0xfb, 0x08, 0x05, 0xf4, 0xf2, 0x0b, 0xfb, 0xea, 0xed, 0x00, 0x0a, 0x0a, 0x04, 0x0b, + 0xfe, 0xf4, 0x07, 0xf4, 0xf5, 0x0e, 0x07, 0x18, 0x13, 0xed, 0xf9, 0xfa, 0xf5, 0x09, 0x03, 0xfc, 0x00, 0x0f, 0x00, 0xf6, 0x09, 0x08, + 0xfd, 0xfa, 0x0d, 0xfa, 0xfa, 0x14, 0x10, 0x02, 0x00, 0x0d, 0xff, 0xfd, 0x04, 0xfc, 0xeb, 0xf1, 0x0a, 0x01, 0x00, 0x05, 0x03, 0xf6, + 0xed, 0xf0, 0x05, 0xfa, 0xf8, 0x0b, 0x00, 0xf7, 0xfd, 0x0e, 0x05, 0xfc, 0xfb, 0xeb, 0xf6, 0x05, 0x07, 0xf5, 0xf6, 0x02, 0x02, 0xfa, + 0x03, 0x10, 0xee, 0xf0, 0xfe, 0xfa, 0xf6, 0xf8, 0x01, 0x08, 0x00, 0xf9, 0xff, 0x0a, 0x09, 0x08, 0x15, 0x00, 0xf6, 0x00, 0x12, 0xf2, + 0xee, 0x16, 0x0f, 0x05, 0xfd, 0xfa, 0xee, 0x02, 0x19, 0x01, 0xfb, 0x00, 0x01, 0x00, 0xf5, 0xf6, 0x11, 0x06, 0xfc, 0x00, 0x02, 0xf8, + 0xf8, 0x05, 0x02, 0x01, 0x08, 0x13, 0x10, 0x02, 0xf5, 0xfe, 0xf8, 0xfa, 0x0b, 0xfb, 0xfd, 0x07, 0xf4, 0xfd, 0x07, 0x03, 0xeb, 0xfe, + 0x08, 0xe8, 0xf3, 0x03, 0x09, 0x0b, 0xfd, 0xf8, 0x03, 0x00, 0xfe, 0x00, 0x06, 0xee, 0xf6, 0x11, 0x05, 0x06, 0x04, 0xf7, 0xf4, 0xf1, + 0xf6, 0x07, 0xfe, 0xfa, 0xfe, 0xfb, 0xee, 0xf1, 0x04, 0xfc, 0xfc, 0x03, 0x06, 0xff, 0xfa, 0xfd, 0x08, 0x03, 0x01, 0x0c, 0xff, 0xfd, + 0x02, 0xfe, 0x02, 0x03, 0xff, 0xfd, 0xff, 0x02, 0x00, 0x02, 0x00, 0x01, 0x12, 0x07, 0xf9, 0xfa, 0x07, 0x01, 0xfc, 0x0a, 0xfd, 0xfe, + 0x0c, 0x07, 0x04, 0xff, 0xf9, 0xfb, 0x08, 0x0c, 0xfd, 0xf8, 0xff, 0x07, 0x06, 0xfc, 0xf8, 0xff, 0xf5, 0xf6, 0xfb, 0xfb, 0xf5, 0xf6, + 0xff, 0x15, 0xfe, 0xf2, 0x0b, 0x03, 0xed, 0xe9, 0x0b, 0xfd, 0xfc, 0x11, 0x12, 0x00, 0xfa, 0x09, 0xf9, 0xf3, 0xfb, 0x01, 0xfd, 0xfd, + 0x03, 0x06, 0xfd, 0xf8, 0xff, 0x04, 0xfe, 0xf9, 0x06, 0xf9, 0xf5, 0x04, 0x03, 0x0c, 0x0e, 0xf8, 0x07, 0x01, 0xee, 0x01, 0x0e, 0x0b, + 0x01, 0xfe, 0x02, 0x05, 0xfd, 0x00, 0xfe, 0xf9, 0xef, 0x07, 0x0c, 0xee, 0xf7, 0x09, 0x10, 0x03, 0xff, 0xfd, 0xfd, 0xfd, 0x03, 0x01, + 0xf5, 0xfd, 0x06, 0x06, 0x02, 0xfe, 0x03, 0x09, 0xf6, 0xf2, 0xfe, 0x09, 0xf6, 0xf5, 0x04, 0xfb, 0xfb, 0xf9, 0xf4, 0xe3, 0x06, 0x1b, + 0xf4, 0xfa, 0x0d, 0x0f, 0xf5, 0x05, 0x0e, 0xfc, 0x00, 0x05, 0x02, 0xf7, 0x08, 0x0f, 0x04, 0xef, 0x03, 0x0e, 0xfb, 0x02, 0x04, 0x00, + 0xfc, 0x03, 0x01, 0xf9, 0x00, 0xff, 0xf9, 0xf6, 0x15, 0x13, 0xf9, 0xee, 0x02, 0x0a, 0xfc, 0xf6, 0xff, 0x08, 0x03, 0xfd, 0xf9, 0xfc, + 0x01, 0xfc, 0xf5, 0xf6, 0xfa, 0x0a, 0x0e, 0x00, 0xfb, 0x03, 0x06, 0xf3, 0xf3, 0x00, 0x09, 0xf8, 0xf7, 0xfe, 0xfb, 0xff, 0x06, 0x08, + 0xf8, 0xfa, 0x00, 0xfe, 0xe7, 0xf5, 0x10, 0x0e, 0xf5, 0xf9, 0x13, 0x0a, 0x01, 0xfe, 0x03, 0xf6, 0x00, 0x0e, 0x0a, 0xfb, 0xfb, 0x06, + 0x02, 0xfa, 0xfd, 0x0b, 0xfb, 0xf2, 0xfd, 0x15, 0x09, 0xff, 0x08, 0x04, 0x0e, 0x0d, 0xf4, 0xf1, 0xfe, 0x07, 0xf9, 0x08, 0x13, 0x08, + 0xfb, 0xfd, 0x00, 0xf4, 0xfc, 0x01, 0xfe, 0xf6, 0x00, 0x07, 0x00, 0xff, 0xfd, 0xfd, 0x04, 0xfc, 0xfb, 0x02, 0x01, 0xfe, 0x01, 0x07, + 0xfd, 0xfc, 0xf9, 0xea, 0xef, 0xfc, 0x01, 0xfc, 0xf0, 0xf6, 0x0f, 0xfa, 0xf9, 0x04, 0xf8, 0xf6, 0xf9, 0xfc, 0x07, 0x01, 0xff, 0x0f, + 0x01, 0xfa, 0xfd, 0xfa, 0xf9, 0xf8, 0xf7, 0xff, 0x00, 0xfd, 0x00, 0xfd, 0x00, 0x02, 0xfc, 0xfc, 0xff, 0x02, 0x15, 0x08, 0xfa, 0x10, + 0x00, 0xf7, 0x01, 0x08, 0x03, 0x01, 0x0a, 0x0d, 0x0d, 0x05, 0xf9, 0xf2, 0xf8, 0x01, 0x01, 0xfd, 0xff, 0x07, 0xfc, 0xf9, 0x04, 0x12, + 0x02, 0xfd, 0x0a, 0x09, 0xf9, 0xf4, 0x06, 0x0d, 0x03, 0xf9, 0x04, 0xfe, 0xf6, 0xf8, 0xf9, 0xfc, 0x01, 0x01, 0xfb, 0xfd, 0xff, 0xee, + 0xf7, 0xff, 0xf8, 0x07, 0xfc, 0xf0, 0xfc, 0x14, 0x02, 0xe1, 0x04, 0x06, 0x01, 0x05, 0x04, 0xee, 0xeb, 0x13, 0x03, 0xf2, 0xfa, 0xf4, + 0xff, 0x03, 0xf6, 0xf6, 0x0a, 0x14, 0xfe, 0xfa, 0xff, 0x02, 0xfd, 0xfa, 0xfd, 0x07, 0x08, 0x09, 0x07, 0xfb, 0x0a, 0x0d, 0xfd, 0x05, + 0xfe, 0xfa, 0x08, 0x03, 0x0a, 0x11, 0xf6, 0xfb, 0x07, 0x06, 0xfd, 0xfd, 0x01, 0x00, 0x04, 0x05, 0x03, 0x0c, 0xfc, 0xfa, 0x0c, 0xf6, + 0xeb, 0xf7, 0x0d, 0xfe, 0xfc, 0x0c, 0x00, 0xf9, 0xfb, 0x01, 0xfd, 0x02, 0x04, 0xf4, 0xfb, 0x01, 0xfc, 0xf6, 0xfd, 0x04, 0x01, 0xf2, + 0x04, 0x13, 0xe1, 0x03, 0x12, 0xf1, 0xf7, 0x07, 0x09, 0xfc, 0xf7, 0xfe, 0x04, 0xf7, 0xf7, 0x05, 0x13, 0x02, 0xfd, 0xfe, 0xf5, 0x00, + 0x05, 0x00, 0x00, 0x06, 0x03, 0xf6, 0x06, 0x02, 0xf6, 0xf9, 0x08, 0x08, 0xfd, 0xfc, 0x0b, 0x0d, 0xf9, 0xf5, 0xfb, 0x04, 0x0a, 0x05, + 0xfc, 0xfb, 0x05, 0x08, 0x07, 0x08, 0xfe, 0xf7, 0xf6, 0xf4, 0xfe, 0xff, 0xfb, 0x10, 0x07, 0xf4, 0xfa, 0xf6, 0xfe, 0x06, 0xf7, 0xee, + 0xfe, 0x16, 0xfd, 0xfe, 0x07, 0xfd, 0xe7, 0xf3, 0x0c, 0x00, 0xf6, 0x00, 0x11, 0x00, 0xf1, 0xf3, 0x08, 0xf3, 0xef, 0xfe, 0xfc, 0xf1, + 0xff, 0x1f, 0x02, 0xfd, 0x0a, 0x02, 0x03, 0x02, 0xfc, 0x00, 0xfe, 0x05, 0x14, 0x0f, 0x07, 0x05, 0x04, 0x06, 0x01, 0xf8, 0xf5, 0x03, + 0x08, 0xf6, 0x03, 0x06, 0xff, 0x04, 0x00, 0x02, 0x07, 0xfa, 0xfa, 0x02, 0x02, 0x09, 0x08, 0x02, 0x01, 0x07, 0xfd, 0xea, 0x01, 0x09, + 0x02, 0xfd, 0x02, 0xfd, 0xf4, 0xfe, 0xfd, 0xfc, 0x01, 0xef, 0xf2, 0x02, 0x0b, 0xef, 0xf4, 0x11, 0xf8, 0xed, 0xf8, 0x0b, 0xfa, 0xfb, + 0x04, 0xf6, 0xfe, 0xff, 0xf6, 0x03, 0x07, 0x00, 0xfa, 0xf9, 0xfc, 0x01, 0x00, 0x09, 0x06, 0xfb, 0x05, 0x0b, 0x01, 0xf1, 0xf6, 0x07, + 0x11, 0x0c, 0xf7, 0xf4, 0x07, 0x06, 0x05, 0x01, 0xf9, 0xfe, 0x06, 0x0a, 0x05, 0x01, 0xfc, 0xf7, 0xff, 0x07, 0x09, 0x04, 0x03, 0xff, + 0xfc, 0x02, 0x0d, 0x0c, 0xfc, 0xf4, 0xfc, 0x05, 0xfe, 0x07, 0x04, 0xf7, 0x00, 0xff, 0xfc, 0xfe, 0xfc, 0xf9, 0xf9, 0xff, 0xfd, 0xfd, + 0x02, 0x09, 0x00, 0xf6, 0xfb, 0xf7, 0xf4, 0xfd, 0x1c, 0xfb, 0xea, 0x04, 0xfc, 0xf4, 0xfa, 0x08, 0xff, 0xf1, 0xef, 0xfe, 0x05, 0x04, + 0x02, 0xff, 0x00, 0x02, 0x03, 0xf9, 0xfb, 0x06, 0x07, 0xf3, 0xf6, 0x17, 0x06, 0xfd, 0xff, 0xf9, 0xf8, 0xfe, 0x04, 0x06, 0xfe, 0xf8, + 0xfe, 0x06, 0x04, 0x00, 0x0e, 0xfb, 0xf9, 0x0d, 0x04, 0x00, 0x01, 0xff, 0x07, 0x08, 0x01, 0x05, 0xff, 0xfe, 0x03, 0x06, 0xfe, 0xfb, + 0x0a, 0xfe, 0xf9, 0x02, 0x0f, 0xff, 0xf7, 0x06, 0xfb, 0xfd, 0x05, 0xff, 0xfc, 0xfc, 0xfe, 0x05, 0xfd, 0xf5, 0xf6, 0xfb, 0xf9, 0xf6, + 0xf9, 0xfc, 0xfb, 0xf8, 0xf6, 0x02, 0x05, 0xf6, 0xf9, 0xfa, 0xfa, 0x03, 0x01, 0xf8, 0xf4, 0x0a, 0x01, 0xfc, 0x16, 0x03, 0xf4, 0xf7, + 0x06, 0xf6, 0xed, 0xfb, 0x13, 0x02, 0xf0, 0x03, 0x08, 0xf9, 0xeb, 0x06, 0xf7, 0xf4, 0x0d, 0x07, 0x02, 0x04, 0x04, 0x04, 0x0f, 0x16, + 0xfe, 0x03, 0x07, 0xf9, 0xfc, 0x0b, 0x13, 0x03, 0x06, 0x01, 0xf8, 0x03, 0x07, 0xff, 0xf4, 0xfa, 0x01, 0x03, 0xfe, 0x15, 0x11, 0xf5, + 0x05, 0xf7, 0xec, 0x00, 0xfe, 0x01, 0x08, 0x03, 0xfc, 0xfa, 0xfe, 0xfb, 0xff, 0xff, 0xf6, 0x01, 0x00, 0xfb, 0x0e, 0x04, 0xfc, 0x03, + 0x06, 0xfe, 0xf7, 0xf9, 0xff, 0xfd, 0xf7, 0xfa, 0x02, 0x03, 0xfb, 0x07, 0x02, 0xf8, 0xfd, 0x0b, 0x0a, 0x01, 0x0f, 0xff, 0xf0, 0xf7, + 0x05, 0x0b, 0x08, 0x07, 0xfd, 0xf7, 0xfa, 0x01, 0x01, 0xfd, 0xfb, 0xfd, 0x00, 0x02, 0x02, 0xf8, 0xf5, 0xff, 0x05, 0x09, 0x04, 0xfa, + 0xff, 0x04, 0x04, 0x06, 0x06, 0x01, 0xfc, 0x07, 0x00, 0xf7, 0x03, 0xfa, 0x05, 0x18, 0x07, 0xf8, 0xf7, 0x03, 0xfe, 0x02, 0x03, 0xf7, + 0xfb, 0x01, 0x02, 0x05, 0xfa, 0xfa, 0x0a, 0x02, 0xfe, 0xfd, 0xf4, 0x06, 0x08, 0xf7, 0xf5, 0x06, 0x10, 0x05, 0xf0, 0xf2, 0x00, 0xfb, + 0xfb, 0x04, 0x0d, 0xfd, 0xfd, 0x01, 0xfb, 0xf3, 0xfd, 0x06, 0xf4, 0xf9, 0x03, 0x04, 0xff, 0x04, 0x08, 0x00, 0x03, 0xfe, 0xf7, 0xfc, + 0xfe, 0xfc, 0xfe, 0x00, 0x08, 0x09, 0xfd, 0x04, 0x03, 0xfa, 0x00, 0xf8, 0xfa, 0x0b, 0x02, 0x03, 0x06, 0xf8, 0xf5, 0xfa, 0xfe, 0xf5, + 0x01, 0x09, 0xfd, 0x01, 0x02, 0xff, 0x01, 0xf9, 0xf8, 0xfe, 0xfd, 0xf8, 0xff, 0x10, 0x01, 0xf8, 0xff, 0x08, 0x03, 0xff, 0x02, 0xfa, + 0xfd, 0x00, 0xfb, 0xfd, 0x03, 0x05, 0xfd, 0xff, 0xfe, 0xf8, 0x04, 0x02, 0xf9, 0xfd, 0xf5, 0xf6, 0xff, 0x04, 0xf8, 0xf8, 0x08, 0xfd, + 0xf6, 0xfb, 0x09, 0xfd, 0x02, 0x15, 0x00, 0x00, 0x04, 0xfd, 0xf9, 0x07, 0x11, 0xfd, 0xff, 0xff, 0xf7, 0x04, 0x00, 0xff, 0x0d, 0x0a, + 0x01, 0xfd, 0xfb, 0x02, 0xfd, 0xef, 0xfe, 0x00, 0xfc, 0x01, 0x07, 0x01, 0xf9, 0x00, 0xf9, 0xf7, 0x03, 0x00, 0x02, 0x04, 0xfd, 0xf6, + 0xf6, 0xfd, 0x0b, 0x01, 0xf5, 0xf8, 0xfb, 0xf4, 0xf3, 0x07, 0x05, 0x02, 0x03, 0xfd, 0xf3, 0xfa, 0x0f, 0xf2, 0xf6, 0x0c, 0x03, 0x05, + 0x0b, 0x0a, 0x05, 0x01, 0x02, 0x05, 0xff, 0xf3, 0xef, 0x01, 0xfe, 0xfe, 0x09, 0x03, 0x00, 0xfe, 0xfb, 0xfd, 0x01, 0x03, 0x07, 0xff, + 0xfe, 0x06, 0x05, 0xfe, 0x00, 0x11, 0x01, 0xf3, 0xf6, 0x02, 0x03, 0x06, 0x0e, 0x00, 0xff, 0x03, 0xf9, 0xfd, 0x01, 0xff, 0xfd, 0xfa, + 0xfb, 0x03, 0xfe, 0x0a, 0x13, 0xff, 0xfa, 0xf8, 0xf5, 0xf9, 0xff, 0x04, 0x07, 0xf8, 0xfa, 0x04, 0x00, 0xf9, 0xfc, 0x02, 0xee, 0xfb, + 0x07, 0xf5, 0xfa, 0xf8, 0xf4, 0xfd, 0x07, 0x06, 0xfd, 0xf9, 0xfd, 0x01, 0xfe, 0x00, 0x01, 0x04, 0x16, 0x06, 0xf9, 0x05, 0x0a, 0x07, + 0x02, 0x04, 0xf4, 0xf8, 0x07, 0xfe, 0xfe, 0x05, 0x06, 0x05, 0xf6, 0xef, 0x05, 0x00, 0x02, 0x08, 0xf5, 0xf9, 0x09, 0x0e, 0x0a, 0xf9, + 0xf3, 0x0c, 0x06, 0xf7, 0xf5, 0x05, 0x05, 0x01, 0x05, 0x0b, 0xfe, 0xef, 0xf5, 0x05, 0x03, 0xf2, 0x09, 0x08, 0xff, 0x03, 0x05, 0xfd, + 0xf8, 0x02, 0x04, 0xfc, 0xf4, 0xef, 0xfa, 0x07, 0x05, 0xfe, 0x02, 0x08, 0xf1, 0xfd, 0x03, 0xf1, 0x00, 0x02, 0xfb, 0xfb, 0x0f, 0x0e, + 0xfa, 0x00, 0xfc, 0xf2, 0xee, 0x09, 0x09, 0xfd, 0x0b, 0x09, 0xfd, 0xf7, 0x0b, 0x00, 0xf8, 0x0c, 0xfc, 0xfb, 0x09, 0x09, 0x04, 0xff, + 0xff, 0x08, 0x0a, 0x01, 0xf5, 0x06, 0x07, 0xfc, 0x02, 0x0a, 0x09, 0xfd, 0xfa, 0xf5, 0xfa, 0x09, 0x00, 0xf7, 0xfa, 0x04, 0xff, 0xfc, + 0x04, 0x04, 0x06, 0x06, 0xfa, 0xfd, 0xfa, 0xf5, 0x07, 0x0a, 0xfe, 0xf1, 0xfd, 0x02, 0xfb, 0xf5, 0x01, 0x02, 0xf6, 0x04, 0xf7, 0xef, + 0x01, 0xfc, 0xf9, 0xff, 0x06, 0x05, 0x04, 0x05, 0xfc, 0xfe, 0x06, 0x08, 0x03, 0x02, 0x02, 0xfd, 0xfd, 0xff, 0xfe, 0x01, 0xf9, 0xf2, + 0xfb, 0xff, 0x01, 0x00, 0xfc, 0x05, 0x06, 0xfb, 0xf8, 0x08, 0x10, 0xfc, 0x02, 0x01, 0xf8, 0xff, 0x09, 0x0d, 0x09, 0x05, 0x02, 0xfc, + 0xf1, 0xfb, 0x01, 0xff, 0x04, 0x06, 0x02, 0xfa, 0xfc, 0xff, 0xfc, 0xef, 0x08, 0x10, 0xff, 0x04, 0x09, 0x07, 0xfc, 0x08, 0x03, 0xf4, + 0xf9, 0xfd, 0xff, 0xff, 0xf8, 0xff, 0x07, 0x03, 0xf2, 0xee, 0xf6, 0xff, 0xf4, 0xfc, 0x14, 0xfd, 0xec, 0xf3, 0x0e, 0x07, 0x03, 0x08, + 0x01, 0xfd, 0xfe, 0x00, 0x00, 0x01, 0x02, 0x03, 0xfd, 0xf7, 0xf7, 0xff, 0x00, 0xfc, 0xfb, 0x01, 0x01, 0xfc, 0xfa, 0x00, 0x07, 0x07, + 0xff, 0xff, 0x02, 0x03, 0xfd, 0x03, 0x0a, 0xfb, 0x08, 0x0d, 0xff, 0xf3, 0x01, 0x0c, 0xfe, 0xf8, 0xfe, 0x05, 0xfa, 0x04, 0x08, 0xfd, + 0x03, 0x02, 0x00, 0x09, 0xf5, 0xf6, 0x0c, 0x13, 0x02, 0xfc, 0x09, 0xfd, 0xf7, 0xfe, 0x0d, 0xf9, 0xf7, 0x07, 0xfd, 0xf0, 0xf6, 0x0c, + 0x12, 0xfd, 0xe9, 0xf3, 0x06, 0x02, 0xed, 0xfd, 0xf6, 0xfd, 0x1d, 0x03, 0xed, 0xf3, 0x00, 0xfd, 0xf6, 0xf5, 0xf8, 0xfa, 0xfd, 0x05, + 0xea, 0xf9, 0x15, 0xf9, 0xe3, 0xf0, 0x0e, 0x02, 0xf4, 0xf8, 0x0c, 0x0c, 0x06, 0x02, 0x07, 0xf9, 0xfa, 0x0f, 0x02, 0xf6, 0xfb, 0x11, + 0xfe, 0x01, 0x14, 0xf5, 0xee, 0xfa, 0x06, 0xf8, 0xfd, 0x0a, 0x06, 0x02, 0x04, 0x09, 0x0a, 0x05, 0x02, 0x04, 0xf7, 0xfb, 0x0b, 0x0d, + 0x02, 0xf7, 0xf5, 0x0a, 0x02, 0xf8, 0x08, 0x04, 0xff, 0xfe, 0xf8, 0x04, 0x02, 0xee, 0xff, 0x06, 0x06, 0x05, 0x17, 0x0b, 0xf1, 0x01, + 0x02, 0xff, 0x02, 0x02, 0xfe, 0xfa, 0xfc, 0xf4, 0xfa, 0x04, 0xf6, 0xf3, 0xf8, 0xfb, 0x07, 0xff, 0xf4, 0x00, 0x0d, 0x07, 0xf9, 0x09, + 0xff, 0xf5, 0xfc, 0xfa, 0xfb, 0xfe, 0xfc, 0x02, 0x00, 0xf7, 0x00, 0xf5, 0xee, 0xfc, 0x06, 0xff, 0xf5, 0xfc, 0x04, 0x0b, 0x0c, 0xff, + 0xfd, 0x02, 0x00, 0x08, 0x05, 0x00, 0x0f, 0x04, 0xf6, 0xf9, 0x01, 0x05, 0x01, 0xfb, 0xf6, 0xf1, 0xf2, 0x01, 0x08, 0x07, 0x03, 0xfe, + 0x04, 0x08, 0xfc, 0x06, 0x0a, 0x02, 0xfa, 0xff, 0x0b, 0x14, 0x05, 0x01, 0x04, 0x00, 0xff, 0x08, 0x0c, 0xf4, 0xec, 0xfa, 0x0a, 0x0f, + 0x01, 0xf5, 0xfe, 0x07, 0x04, 0xfb, 0x0b, 0x03, 0xf6, 0xf9, 0x02, 0x01, 0xfa, 0xf9, 0x03, 0x04, 0xf8, 0xfd, 0xfb, 0xf8, 0xfd, 0x00, + 0xff, 0xfe, 0x01, 0xff, 0xff, 0x01, 0xf6, 0xf9, 0xff, 0xf3, 0x04, 0x0a, 0x00, 0x02, 0x00, 0xfe, 0xff, 0xff, 0x00, 0xfe, 0xf9, 0xfe, + 0x06, 0x07, 0xf8, 0xf9, 0x06, 0x0e, 0xf5, 0xfb, 0x0e, 0xf9, 0xf9, 0xff, 0x00, 0xfc, 0x02, 0x0a, 0x0e, 0xf4, 0xf5, 0x05, 0xf7, 0x00, + 0x03, 0xf9, 0x03, 0x01, 0xfb, 0x02, 0x01, 0x01, 0x04, 0x07, 0x01, 0xfc, 0xfc, 0x03, 0x0a, 0x07, 0xf5, 0x06, 0x13, 0x09, 0xf9, 0xfa, + 0xff, 0xff, 0xfd, 0x04, 0x05, 0xef, 0xff, 0x07, 0xfb, 0xf6, 0xfc, 0x00, 0xfc, 0xfc, 0x04, 0x09, 0xf9, 0x0b, 0x0e, 0xfa, 0xf9, 0xfe, + 0x02, 0x04, 0x03, 0x01, 0xfd, 0xfb, 0xf8, 0x05, 0x17, 0xfa, 0xf4, 0xf7, 0xe8, 0xfd, 0x02, 0xf8, 0xff, 0x06, 0x01, 0xf4, 0xfc, 0xfb, + 0xfb, 0x09, 0x08, 0x04, 0x00, 0xf5, 0xfd, 0x06, 0x04, 0x08, 0x05, 0xfa, 0xe9, 0xfb, 0x00, 0xf3, 0xf8, 0x00, 0x04, 0x00, 0x01, 0x03, + 0x01, 0xf7, 0x06, 0x0a, 0xfe, 0xff, 0xfa, 0xfa, 0x04, 0x02, 0x04, 0x08, 0x05, 0xf5, 0xf8, 0x0c, 0xfe, 0xf7, 0xff, 0x09, 0xfe, 0xef, + 0xee, 0x03, 0x06, 0x05, 0x0b, 0x02, 0x00, 0x01, 0xfe, 0xfe, 0x08, 0x11, 0x08, 0xfa, 0xf7, 0x01, 0x06, 0x07, 0x01, 0xfb, 0xf7, 0x02, + 0x0c, 0xf3, 0xfd, 0x0a, 0x00, 0xf2, 0xf9, 0x06, 0xff, 0xfc, 0xfb, 0xff, 0x0a, 0x01, 0xf6, 0xfb, 0x07, 0x05, 0xfb, 0x00, 0xfe, 0x03, + 0x0a, 0xf3, 0xf0, 0x00, 0x0f, 0x01, 0x00, 0x04, 0xea, 0xf1, 0xfe, 0xfd, 0x0b, 0x09, 0xff, 0xfd, 0xfd, 0xf9, 0xf8, 0x0a, 0xff, 0xf7, + 0x01, 0xff, 0xf7, 0xf6, 0x03, 0x06, 0x05, 0x04, 0x01, 0xff, 0xf6, 0xe8, 0x05, 0x06, 0xf7, 0x01, 0x05, 0x04, 0x03, 0x05, 0xfd, 0xf6, + 0xfb, 0xf7, 0xfd, 0x07, 0x0c, 0xfc, 0xfc, 0x0b, 0xfd, 0xf1, 0xf6, 0x09, 0xff, 0x04, 0x0e, 0xfa, 0xfc, 0x07, 0x08, 0xf4, 0xfc, 0x09, + 0xfc, 0x09, 0x08, 0xf8, 0xf9, 0x02, 0x05, 0x02, 0x13, 0x08, 0xf3, 0xfa, 0xf4, 0xfd, 0x0d, 0x04, 0xf9, 0xf5, 0xfb, 0x18, 0x0f, 0xf8, + 0x06, 0x03, 0xfd, 0xf8, 0xfb, 0xf3, 0xfc, 0x15, 0x0d, 0xfa, 0xf9, 0x16, 0xff, 0xee, 0xfc, 0xfd, 0x08, 0x02, 0xe6, 0xfe, 0x0f, 0x06, + 0xf5, 0x04, 0x09, 0xf7, 0xf0, 0xfc, 0x03, 0xf5, 0x06, 0x0b, 0x00, 0x00, 0xfa, 0xf4, 0xf3, 0xff, 0xf3, 0xe7, 0xfc, 0xf5, 0xfe, 0x12, + 0x10, 0xf5, 0xe6, 0xf5, 0xfc, 0x02, 0x03, 0xfb, 0x08, 0x03, 0xf4, 0x0f, 0x10, 0x02, 0xfa, 0x00, 0x02, 0x02, 0x05, 0x0b, 0x01, 0xf2, + 0x03, 0x04, 0x04, 0x11, 0x0b, 0x00, 0xf8, 0xf7, 0xfe, 0x07, 0x0c, 0x11, 0x02, 0xfb, 0x0d, 0xf7, 0xf5, 0x08, 0x08, 0x03, 0xfd, 0xfe, + 0x0d, 0x0c, 0x03, 0x05, 0xf1, 0xf4, 0x07, 0x03, 0xf8, 0xfe, 0x10, 0xf0, 0xf2, 0x09, 0x0b, 0xfa, 0x05, 0x19, 0xed, 0xf1, 0x01, 0xfc, + 0xff, 0x01, 0x03, 0x0b, 0xf1, 0xec, 0x05, 0x08, 0x01, 0xf2, 0xe4, 0xf6, 0x05, 0x00, 0xf1, 0xfc, 0xfb, 0xed, 0x05, 0x04, 0xf8, 0xf8, + 0xf8, 0xfe, 0x04, 0x00, 0x09, 0x06, 0xf6, 0x07, 0x0b, 0x00, 0xf6, 0xfe, 0x0c, 0x0c, 0xf5, 0xf7, 0x04, 0x0a, 0x09, 0x0d, 0x0a, 0xf7, + 0xf1, 0xf4, 0xfd, 0x04, 0x0e, 0x12, 0x0d, 0xf6, 0x02, 0x16, 0x02, 0xf7, 0xf6, 0xfc, 0x03, 0x0b, 0x08, 0xfe, 0x0a, 0x08, 0xfb, 0xfd, + 0xf5, 0xfc, 0x0e, 0x07, 0xfe, 0xf8, 0xf4, 0x02, 0x0b, 0x06, 0xfe, 0xfa, 0xfc, 0xfe, 0xf6, 0xff, 0x05, 0xf8, 0x06, 0x0b, 0x02, 0xfa, + 0xfc, 0xff, 0xfb, 0xf4, 0xf1, 0xfb, 0x10, 0xf7, 0xea, 0xf9, 0x02, 0x06, 0x00, 0xf9, 0xef, 0xfc, 0x08, 0xfa, 0xf9, 0xfa, 0xfb, 0x12, + 0xff, 0xee, 0xff, 0x04, 0x07, 0x04, 0xf2, 0x09, 0x0c, 0xf3, 0xfd, 0x04, 0x09, 0x12, 0xf1, 0xf9, 0x12, 0xed, 0xf4, 0x03, 0x06, 0x12, + 0x10, 0x00, 0xf4, 0xf0, 0xfa, 0x07, 0xfd, 0x13, 0x10, 0xee, 0xf8, 0x07, 0x0b, 0x05, 0x08, 0x04, 0xfc, 0xfd, 0xfa, 0xfe, 0x0a, 0xff, + 0xff, 0x01, 0xf6, 0x03, 0x0a, 0x00, 0xf2, 0xf7, 0x00, 0xfe, 0xf9, 0xfb, 0x05, 0x0d, 0x09, 0xfa, 0xf1, 0x05, 0x03, 0xfa, 0x01, 0x00, + 0xfe, 0x02, 0x11, 0xfe, 0xed, 0xf2, 0xf6, 0x01, 0x02, 0xf3, 0xf7, 0xf9, 0xf9, 0xff, 0x04, 0x03, 0x02, 0xf7, 0x05, 0x0d, 0xf7, 0xf6, + 0x05, 0x0f, 0x08, 0xfb, 0xfd, 0x0d, 0xfd, 0x01, 0x0c, 0x05, 0xf3, 0xf7, 0x08, 0xf3, 0x08, 0x19, 0x07, 0xef, 0xf7, 0x05, 0xf8, 0xf8, + 0x06, 0x13, 0x02, 0xff, 0x06, 0x0d, 0xeb, 0xf7, 0x13, 0xf9, 0xf4, 0xfd, 0x03, 0x00, 0xfd, 0x02, 0x10, 0xf6, 0xf2, 0x04, 0xfd, 0x04, + 0xfe, 0xec, 0xfd, 0x04, 0xfc, 0xf3, 0x01, 0x01, 0xf7, 0x03, 0x00, 0xf9, 0xf8, 0xfe, 0x00, 0xff, 0xfd, 0x05, 0x04, 0xf8, 0xf9, 0xf0, + 0xf9, 0x14, 0x08, 0xfa, 0xf8, 0xfa, 0x02, 0x03, 0xff, 0x03, 0x05, 0x05, 0x03, 0xfa, 0xf8, 0xfe, 0x01, 0x10, 0x11, 0x04, 0x00, 0x01, + 0x04, 0x05, 0xfb, 0xff, 0x08, 0xf9, 0x00, 0x07, 0x03, 0x0b, 0x0a, 0x02, 0xfc, 0xf7, 0xfc, 0x06, 0x0a, 0x00, 0x02, 0x0e, 0xfa, 0xe5, + 0xed, 0x13, 0x12, 0x04, 0xfe, 0x09, 0xfb, 0xf3, 0xff, 0x04, 0x04, 0x06, 0x11, 0xfa, 0xf5, 0x02, 0xf1, 0xee, 0xf6, 0xff, 0xff, 0x04, + 0x05, 0xf8, 0xf7, 0xfd, 0xfe, 0xe9, 0xf2, 0x04, 0x05, 0xf7, 0xf5, 0xff, 0x01, 0x07, 0x09, 0x04, 0xff, 0xf9, 0xf7, 0xf8, 0x00, 0xff, + 0xf9, 0xfc, 0xfd, 0x05, 0x0d, 0xfc, 0xf5, 0xf9, 0xfb, 0xfd, 0x01, 0x07, 0x0d, 0x13, 0x0e, 0x01, 0xee, 0xf4, 0x07, 0x0e, 0x02, 0xfd, + 0x03, 0xff, 0x02, 0x05, 0x01, 0x06, 0x03, 0xf8, 0xf0, 0xfa, 0x04, 0x05, 0xff, 0x05, 0x0d, 0x0b, 0xf5, 0xfe, 0x0f, 0xf4, 0x04, 0x12, + 0x07, 0xf8, 0xf9, 0x01, 0x01, 0x0b, 0x0c, 0x02, 0xf1, 0x01, 0x01, 0xe8, 0xf7, 0x06, 0x0b, 0x07, 0x01, 0xff, 0xfd, 0xf3, 0xf5, 0xfd, + 0x02, 0x15, 0x09, 0xf4, 0xfa, 0x00, 0x00, 0xfc, 0xf9, 0xfd, 0x02, 0x00, 0xf1, 0xf4, 0xfd, 0xf1, 0x07, 0x07, 0xf0, 0x08, 0x08, 0xf7, + 0xeb, 0xfe, 0xfd, 0xf3, 0xfe, 0x0e, 0x0b, 0xfa, 0x03, 0xfd, 0xf6, 0xfe, 0x05, 0x01, 0xfd, 0x0a, 0x00, 0xf8, 0x00, 0xf9, 0x00, 0x06, + 0xfc, 0xfc, 0x08, 0x0c, 0xef, 0xfe, 0x0b, 0xfe, 0x11, 0x08, 0xf5, 0xf9, 0x03, 0xfe, 0xf4, 0x04, 0x06, 0x01, 0x00, 0x0e, 0x05, 0xfa, + 0x0d, 0x04, 0xfe, 0x03, 0x01, 0xfc, 0x00, 0x08, 0x07, 0xfa, 0xf5, 0x09, 0x0c, 0xff, 0xf3, 0x01, 0x03, 0x00, 0x03, 0x0b, 0xfa, 0xe7, + 0xfc, 0x07, 0xfe, 0xef, 0x15, 0x04, 0xeb, 0x03, 0x04, 0xfd, 0xfd, 0x06, 0x03, 0xfb, 0xf6, 0xf9, 0xfb, 0xfd, 0x01, 0xfc, 0xfe, 0x04, + 0xfb, 0xf7, 0xfc, 0x06, 0xfd, 0xfe, 0xfe, 0xed, 0x05, 0x1a, 0x0e, 0xe3, 0xf1, 0x0e, 0x0d, 0xf8, 0xfc, 0x10, 0x11, 0xfe, 0xf7, 0x03, + 0xf7, 0xf6, 0x00, 0x0c, 0xf2, 0xfa, 0x0e, 0xf1, 0xf5, 0x05, 0x0a, 0xf5, 0xfd, 0x0b, 0x02, 0x00, 0x00, 0x01, 0xfe, 0x04, 0x07, 0x02, + 0xf7, 0xfd, 0x07, 0x02, 0xf7, 0xfe, 0x0c, 0x05, 0xf8, 0xf1, 0xf6, 0x03, 0x07, 0x03, 0x00, 0x02, 0x04, 0x04, 0xfa, 0x0e, 0x11, 0xf1, + 0xf4, 0x03, 0x0a, 0x02, 0x06, 0x09, 0x04, 0xf7, 0xfc, 0xff, 0xf3, 0x00, 0x08, 0x00, 0xef, 0xfb, 0x03, 0xfb, 0xf8, 0x01, 0x06, 0xfc, + 0xfa, 0x02, 0x07, 0xf0, 0x01, 0x0d, 0x00, 0xf1, 0x01, 0x11, 0x00, 0xf5, 0xfb, 0x08, 0xfd, 0xff, 0xff, 0xf8, 0xfe, 0x06, 0x02, 0xf5, + 0xf9, 0x03, 0x05, 0xf6, 0xfa, 0x05, 0x08, 0xf9, 0xf9, 0x02, 0x03, 0xfb, 0xfe, 0x08, 0x03, 0xf7, 0xfa, 0x0e, 0xfa, 0xfa, 0x07, 0xfc, + 0xfe, 0x05, 0x09, 0x00, 0x00, 0x04, 0x03, 0x00, 0x02, 0x02, 0xf9, 0xfc, 0x07, 0x0d, 0xfb, 0xf9, 0x01, 0x02, 0x02, 0xff, 0xff, 0x09, + 0x03, 0xfa, 0xfb, 0xf8, 0xfc, 0x03, 0x09, 0xfe, 0xfe, 0x05, 0xf6, 0xf8, 0x04, 0x0c, 0x02, 0x02, 0x00, 0xec, 0xf5, 0x03, 0x04, 0xf8, + 0xfa, 0x03, 0x05, 0xf8, 0xfc, 0x07, 0xfd, 0xfd, 0x01, 0x03, 0x03, 0x00, 0xfc, 0xfb, 0xfc, 0x02, 0x07, 0x04, 0xff, 0xfd, 0xff, 0x04, + 0xff, 0xfb, 0x01, 0x01, 0xff, 0xfe, 0xfd, 0xff, 0x00, 0x00, 0xfe, 0x02, 0x05, 0x01, 0x00, 0x04, 0x08, 0x03, 0x04, 0x05, 0x02, 0xf6, + 0xfd, 0x06, 0xfd, 0xfc, 0xfe, 0xff, 0xff, 0xf7, 0xfb, 0x0d, 0xf7, 0xf0, 0xfc, 0x01, 0x06, 0x02, 0xfa, 0xf7, 0x03, 0x09, 0xff, 0x03, + 0x0d, 0x0e, 0xf9, 0xfa, 0xfb, 0xf5, 0xfd, 0x05, 0x06, 0xff, 0x01, 0xfd, 0xf7, 0xf7, 0xff, 0x06, 0x07, 0xfd, 0x00, 0x01, 0xf6, 0xf7, + 0xff, 0x07, 0x0a, 0x05, 0x04, 0x09, 0xf5, 0xf8, 0x04, 0x00, 0x06, 0x0a, 0x05, 0xfa, 0xf5, 0xf5, 0xf7, 0x05, 0x05, 0xfb, 0xf8, 0x06, + 0x06, 0xf8, 0x09, 0x07, 0x00, 0x04, 0x03, 0x01, 0xfe, 0xf9, 0xff, 0x03, 0x01, 0x03, 0x05, 0x01, 0xf9, 0x03, 0x05, 0xfc, 0xf5, 0xfc, + 0xfb, 0xf1, 0x01, 0x02, 0xfb, 0xf9, 0x04, 0xfd, 0xf1, 0x00, 0x09, 0x07, 0x01, 0x01, 0xfd, 0xfb, 0x03, 0x00, 0xfd, 0xfd, 0xf1, 0xf7, + 0x00, 0x00, 0x01, 0x0c, 0x0e, 0xfb, 0xf6, 0xfe, 0x05, 0x01, 0xf9, 0xf9, 0x04, 0x0b, 0x08, 0xff, 0xfb, 0xfa, 0x01, 0x09, 0x05, 0xfc, + 0xfd, 0x09, 0xf7, 0xf5, 0x02, 0x00, 0x02, 0x04, 0x01, 0xf3, 0xf8, 0x05, 0x0b, 0x05, 0x03, 0x06, 0xff, 0xff, 0xfe, 0xfb, 0xfd, 0x02, + 0x01, 0xf6, 0xfe, 0x00, 0xfb, 0xff, 0x03, 0x06, 0x06, 0x02, 0xfa, 0xf8, 0x03, 0xfd, 0xf8, 0xfe, 0x0b, 0x01, 0xf8, 0x00, 0x07, 0x01, + 0xf7, 0xf6, 0xfd, 0xff, 0xfa, 0x05, 0xfd, 0xf6, 0xff, 0x01, 0xfa, 0xf5, 0xf9, 0x06, 0x09, 0x01, 0xfb, 0x01, 0x05, 0xfc, 0xfc, 0x05, + 0x08, 0xef, 0xff, 0x08, 0xf6, 0xf2, 0x04, 0x0e, 0xfd, 0x01, 0x03, 0xfb, 0xec, 0xf7, 0x05, 0x06, 0xf8, 0x02, 0x0d, 0xf9, 0xfc, 0x0c, + 0x16, 0x07, 0xfd, 0xfa, 0xfd, 0xfe, 0x08, 0x0d, 0x01, 0xfd, 0xfe, 0xfd, 0xf9, 0xf8, 0xfd, 0x07, 0xfc, 0xf8, 0x01, 0x09, 0xff, 0xfe, + 0x0b, 0x04, 0xfc, 0xfa, 0xfd, 0xff, 0x03, 0x08, 0x05, 0x0c, 0x0a, 0xfb, 0xfb, 0xfb, 0xf9, 0xf8, 0x02, 0x06, 0xff, 0xf6, 0xf3, 0xfa, + 0x05, 0x06, 0xff, 0xfa, 0xfc, 0xfd, 0xff, 0x03, 0xfd, 0x05, 0x07, 0xfa, 0xfd, 0x01, 0x01, 0xfc, 0xff, 0xfc, 0xf7, 0xff, 0x06, 0xfe, + 0xec, 0xfd, 0x03, 0xfc, 0xfe, 0xff, 0xfc, 0xf7, 0x01, 0xfe, 0xfd, 0x08, 0x0e, 0xfc, 0xed, 0x06, 0x0d, 0x02, 0xf7, 0x02, 0x06, 0x00, + 0xfa, 0x01, 0x05, 0x01, 0x06, 0xf7, 0xf4, 0x06, 0x0c, 0x03, 0xfb, 0x02, 0x04, 0x00, 0xfd, 0x02, 0x07, 0x0c, 0x0d, 0xf8, 0xf8, 0x04, + 0x04, 0xff, 0x04, 0x0e, 0x07, 0xfe, 0xfa, 0xfd, 0xfe, 0x00, 0x00, 0xff, 0x00, 0x03, 0x03, 0xf7, 0xf9, 0x04, 0x0b, 0x03, 0xfe, 0xfe, + 0xf7, 0xf4, 0xf7, 0xfe, 0xf8, 0x07, 0x0d, 0xf4, 0xf7, 0xfe, 0xfd, 0xee, 0xfb, 0x05, 0xff, 0xfd, 0xff, 0x01, 0x00, 0xfe, 0x01, 0x02, + 0xfc, 0xf8, 0xfb, 0x02, 0xfd, 0xfc, 0x00, 0x02, 0xf6, 0xf9, 0x09, 0x04, 0x00, 0xfa, 0xf4, 0xf9, 0x02, 0x09, 0x0c, 0x0a, 0x03, 0xfb, + 0xf9, 0xfe, 0x07, 0x0b, 0xff, 0xfc, 0x04, 0x08, 0x04, 0xff, 0xff, 0x02, 0x01, 0xff, 0xfe, 0xf7, 0xf8, 0x00, 0x02, 0x0a, 0x0c, 0x06, + 0x01, 0x03, 0x01, 0xf5, 0xf8, 0xff, 0x06, 0x0e, 0x0c, 0x01, 0xf8, 0x03, 0x09, 0x02, 0xf7, 0xfb, 0xff, 0xff, 0xfc, 0xff, 0x04, 0x04, + 0xfc, 0x01, 0x08, 0xfc, 0xf6, 0xfa, 0x03, 0x02, 0x06, 0x08, 0x02, 0x01, 0xfe, 0xf8, 0xf7, 0xf9, 0xfc, 0xfd, 0xf7, 0xfb, 0x02, 0x01, + 0x02, 0xfa, 0xf6, 0x08, 0x00, 0xf8, 0xfe, 0x02, 0xfd, 0xf9, 0x02, 0xfe, 0x01, 0x09, 0x09, 0xfc, 0xf3, 0xf9, 0xf8, 0xfb, 0x01, 0x05, + 0x04, 0xfa, 0xf4, 0x05, 0x00, 0xf6, 0xf8, 0x05, 0x05, 0xff, 0x05, 0x05, 0x03, 0x03, 0x0d, 0x05, 0xfd, 0x03, 0x08, 0x03, 0xfb, 0x00, + 0x01, 0x04, 0x07, 0x0a, 0x04, 0xfd, 0xfc, 0x00, 0xfb, 0xf5, 0xfd, 0x0a, 0x08, 0xf9, 0xfc, 0x00, 0x04, 0x08, 0x00, 0xfb, 0xff, 0x0a, + 0xfa, 0xf3, 0x04, 0xfc, 0xfb, 0x03, 0x08, 0xfa, 0xf7, 0xff, 0xfa, 0xf9, 0xfc, 0xfe, 0xf4, 0xfd, 0x0b, 0x03, 0xf8, 0xf5, 0xfb, 0xf5, + 0x01, 0x0a, 0xff, 0xf6, 0xfd, 0x07, 0xfc, 0x02, 0x02, 0xf7, 0xf5, 0xfd, 0x02, 0xff, 0xf4, 0xf6, 0x00, 0xfb, 0x02, 0x0a, 0x09, 0xfc, + 0xf9, 0x00, 0x09, 0x00, 0x01, 0x07, 0xf9, 0xfd, 0x05, 0x03, 0x05, 0xff, 0x00, 0x0e, 0xfe, 0xec, 0xf0, 0x05, 0x10, 0x08, 0xfc, 0xfa, + 0xff, 0x05, 0x04, 0xfa, 0xf7, 0xff, 0xfe, 0x08, 0x0d, 0x03, 0xff, 0x01, 0xff, 0xf6, 0x00, 0x05, 0xfd, 0xf9, 0xfa, 0xfe, 0x01, 0x0c, + 0x06, 0xfc, 0x0a, 0xfc, 0xf2, 0xfb, 0xfe, 0x04, 0x01, 0xf6, 0x00, 0x0d, 0x0c, 0xfe, 0xf5, 0xfd, 0x0d, 0xf9, 0xfa, 0x08, 0x09, 0x00, + 0xfc, 0xfe, 0xf3, 0x01, 0x09, 0xfc, 0xf1, 0xf9, 0x03, 0xfc, 0xfc, 0x02, 0x06, 0xfe, 0xfb, 0x01, 0x06, 0xfc, 0xf2, 0xf5, 0x07, 0x0b, + 0x05, 0x01, 0x08, 0xfd, 0xf3, 0xfb, 0xf9, 0x00, 0x08, 0xff, 0x04, 0x07, 0x02, 0xfd, 0x00, 0x04, 0x01, 0xfe, 0x00, 0x02, 0x01, 0x01, + 0x07, 0x0f, 0xf4, 0xf8, 0x05, 0xf8, 0xf7, 0xfd, 0x02, 0x06, 0xfd, 0xf6, 0xfa, 0xfb, 0xfe, 0x01, 0x05, 0xfe, 0xfd, 0x03, 0x02, 0xfa, + 0xfa, 0x05, 0xfc, 0xf9, 0xfe, 0x02, 0x00, 0x05, 0x0a, 0xf6, 0xf3, 0xfe, 0x08, 0x05, 0x00, 0xff, 0x08, 0xfc, 0xf8, 0x03, 0x0c, 0x00, + 0xfa, 0x0d, 0x0c, 0xfd, 0xf3, 0x0a, 0xfa, 0xf9, 0x11, 0xff, 0xf2, 0xfa, 0x0b, 0xfa, 0xf6, 0x03, 0x0a, 0x03, 0x00, 0x05, 0xfb, 0xf3, + 0xf7, 0x09, 0x0c, 0x08, 0x04, 0xfd, 0x07, 0x09, 0xfb, 0xff, 0x04, 0x05, 0x07, 0xf6, 0xfb, 0x0e, 0xf6, 0xf8, 0x01, 0xfd, 0x06, 0x06, + 0xfb, 0xee, 0xf8, 0x08, 0x0c, 0xf6, 0xfe, 0x08, 0xf5, 0xff, 0x02, 0xfc, 0xfe, 0x04, 0x00, 0xf6, 0x04, 0x00, 0xf5, 0xf7, 0xf5, 0xf9, + 0xff, 0xfe, 0xf9, 0xf9, 0xfd, 0x03, 0xff, 0xfe, 0x09, 0x03, 0xfd, 0xfe, 0xfa, 0x01, 0x08, 0x08, 0xfa, 0xfc, 0x06, 0x05, 0xfd, 0xfd, + 0x06, 0x04, 0x00, 0x02, 0x09, 0x04, 0x01, 0xff, 0xfe, 0xf6, 0xf8, 0x03, 0x08, 0x05, 0x02, 0x04, 0xfd, 0xf9, 0xfa, 0xfb, 0x06, 0x08, + 0xfd, 0x02, 0xfd, 0xfb, 0x06, 0x04, 0x00, 0xff, 0xfa, 0xfb, 0x01, 0x04, 0xfa, 0xfe, 0x08, 0x06, 0x04, 0xfe, 0xf9, 0xf7, 0xfe, 0x06, + 0x09, 0xfa, 0x04, 0x0d, 0xf3, 0xf8, 0x03, 0x02, 0xf3, 0xf9, 0x01, 0xfd, 0xfd, 0xfd, 0xfd, 0xfa, 0xfe, 0x03, 0x06, 0xfd, 0xfe, 0xfc, + 0xf2, 0xf7, 0x09, 0x11, 0xfb, 0xf6, 0xfa, 0xfe, 0x02, 0xfc, 0xf9, 0x04, 0xfb, 0xfc, 0x07, 0x06, 0xfb, 0xfc, 0x06, 0xf9, 0xf3, 0xff, + 0x14, 0x06, 0xfc, 0xfe, 0xfc, 0xf7, 0x01, 0x15, 0x09, 0xfe, 0xf9, 0xf4, 0xf9, 0x07, 0x0d, 0xf4, 0xf6, 0x05, 0x08, 0x0c, 0x01, 0xf7, + 0x01, 0xfe, 0xfb, 0xfe, 0x0a, 0x03, 0xf9, 0xfc, 0xfc, 0x00, 0x07, 0x0d, 0xfc, 0xf6, 0x00, 0xf3, 0xf5, 0x00, 0x05, 0x02, 0x01, 0x01, + 0xfc, 0x00, 0x00, 0xfa, 0xfb, 0xff, 0x04, 0x09, 0x05, 0x02, 0x03, 0x05, 0x00, 0xfa, 0xfb, 0xfb, 0xfe, 0xff, 0xff, 0x02, 0x0a, 0x09, + 0xef, 0xf4, 0x03, 0x04, 0x02, 0xfd, 0xfb, 0x00, 0xfb, 0xfa, 0x00, 0x07, 0x05, 0xfe, 0xfa, 0x04, 0xfd, 0xf4, 0x0a, 0x07, 0xfb, 0xf6, + 0xfd, 0x04, 0x04, 0xfb, 0x02, 0x01, 0xfa, 0xfc, 0xfe, 0x00, 0x02, 0x04, 0x02, 0x03, 0x09, 0xfe, 0xf6, 0xf7, 0xf5, 0x04, 0x0b, 0x01, + 0x05, 0x04, 0xfd, 0xf7, 0xff, 0x02, 0xfc, 0xfb, 0xfd, 0xfe, 0xfe, 0x03, 0x02, 0xff, 0x04, 0xff, 0xfc, 0xfe, 0x02, 0xf6, 0xf2, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x01, 0xfd, 0xfd, 0x08, 0xff, 0xf8, 0x05, 0xfa, 0x01, 0x10, 0x0b, 0xf9, 0xf5, 0x03, 0x00, 0xfc, 0x02, 0x12, + 0xfc, 0xf8, 0x04, 0xfc, 0x02, 0x09, 0x07, 0xff, 0xfd, 0xfc, 0xf7, 0xf7, 0xfb, 0x00, 0xfd, 0x07, 0x09, 0xfb, 0xfd, 0xfd, 0xf8, 0xf4, + 0xfa, 0x04, 0x0a, 0x0b, 0xfe, 0xf5, 0xf8, 0x04, 0x06, 0x02, 0x01, 0x05, 0xfe, 0xf4, 0x00, 0x07, 0x03, 0xfc, 0xf3, 0xf8, 0x04, 0x06, + 0xfe, 0xfd, 0x03, 0xf7, 0xf6, 0xff, 0x0b, 0x07, 0x09, 0x06, 0xef, 0xee, 0xfd, 0x09, 0xfe, 0xfc, 0x00, 0xfd, 0x03, 0x06, 0x03, 0xf8, + 0x07, 0x0d, 0xfc, 0xfd, 0xff, 0x01, 0x06, 0x00, 0xfd, 0x03, 0x08, 0x00, 0xfb, 0x03, 0xff, 0xfc, 0xfe, 0x05, 0xfa, 0xfd, 0x0a, 0xf9, + 0xfe, 0x07, 0x01, 0xfb, 0xf9, 0xfb, 0xfd, 0xfe, 0x02, 0x09, 0x11, 0x07, 0xf9, 0xf7, 0xf6, 0xf6, 0xf9, 0xfc, 0x01, 0x04, 0x03, 0x03, + 0x02, 0xff, 0xf8, 0xfb, 0x03, 0x06, 0xff, 0xfd, 0xfd, 0xfe, 0x07, 0x03, 0xfe, 0x09, 0xfd, 0xfa, 0x03, 0x05, 0xfc, 0xfb, 0x07, 0xfa, + 0xf9, 0x06, 0x0d, 0xfe, 0xf4, 0xf6, 0xf0, 0xfd, 0x05, 0xf9, 0x0e, 0x0e, 0xfb, 0xf1, 0x02, 0x04, 0xf6, 0x00, 0x08, 0x08, 0x02, 0xfa, + 0xf7, 0xfd, 0x06, 0x0a, 0x01, 0xf6, 0x03, 0x05, 0xfd, 0xfa, 0xfb, 0xfc, 0xff, 0x0e, 0x04, 0xfc, 0x04, 0x0a, 0x06, 0xfb, 0xf4, 0xfb, + 0x04, 0x08, 0x10, 0x03, 0xfa, 0x00, 0xfe, 0xf9, 0xfa, 0x03, 0xff, 0xfa, 0xfb, 0xfb, 0xff, 0x01, 0xff, 0x02, 0x00, 0xfb, 0xfb, 0xff, + 0x03, 0x02, 0xf2, 0xf4, 0x05, 0x11, 0x02, 0xf7, 0xfb, 0x02, 0xfe, 0xf3, 0xf1, 0x11, 0x0c, 0xf9, 0x03, 0xf9, 0x02, 0x11, 0x01, 0xf3, + 0xf5, 0x00, 0xfe, 0x03, 0x08, 0x02, 0xfd, 0xff, 0x02, 0x00, 0xfa, 0xf7, 0xfa, 0x05, 0x03, 0xfe, 0x04, 0x03, 0xfd, 0xf9, 0x03, 0x01, + 0xfd, 0x02, 0x09, 0x01, 0xf5, 0xfa, 0x01, 0x06, 0x06, 0x03, 0xfc, 0xfb, 0x00, 0xfb, 0xf6, 0xf9, 0x04, 0x08, 0x03, 0xfc, 0x01, 0xfa, + 0xf9, 0x0a, 0xfb, 0xfa, 0x07, 0xff, 0xfa, 0xfe, 0x03, 0xf1, 0xff, 0x0c, 0xf8, 0x02, 0x0b, 0x06, 0xf8, 0xf9, 0xf8, 0xf2, 0x02, 0x0d, + 0x09, 0xf9, 0x00, 0xff, 0xf5, 0xfc, 0x02, 0x03, 0x04, 0x05, 0x08, 0x04, 0xf3, 0xff, 0x03, 0xf9, 0x01, 0xff, 0xfb, 0xff, 0xff, 0x01, + 0x02, 0xfe, 0xfe, 0x03, 0x06, 0xff, 0xf3, 0xf5, 0x05, 0x05, 0xfd, 0xfc, 0x0b, 0x0a, 0x04, 0xff, 0xf8, 0xf1, 0xf9, 0x14, 0xf9, 0xf9, + 0x11, 0x07, 0xf9, 0xf9, 0x04, 0x00, 0xfa, 0xfb, 0x03, 0xfe, 0xfd, 0x03, 0x04, 0x06, 0x05, 0xff, 0xea, 0xf5, 0x06, 0xf8, 0xfd, 0xfd, + 0xf9, 0x08, 0x03, 0xfc, 0x05, 0x05, 0x06, 0x01, 0xf3, 0xf5, 0x01, 0x09, 0x0e, 0x04, 0xfb, 0xfd, 0x06, 0x01, 0xf8, 0xf7, 0x02, 0x03, + 0xfa, 0x06, 0x0b, 0x08, 0x03, 0xf5, 0xf9, 0x05, 0xf9, 0xfd, 0x01, 0xfd, 0xfd, 0x01, 0x02, 0xfa, 0x05, 0x02, 0xf4, 0xf8, 0xff, 0x01, + 0xff, 0xf1, 0x00, 0x11, 0xff, 0xf9, 0x03, 0x0c, 0xff, 0xf7, 0xf6, 0xf9, 0x06, 0x0a, 0x05, 0x04, 0x00, 0x00, 0x03, 0x04, 0xfd, 0xfb, + 0x02, 0xfc, 0xf9, 0xfd, 0x08, 0x0c, 0x07, 0xfc, 0x00, 0xf7, 0xf4, 0x04, 0xfe, 0xf9, 0xfd, 0x06, 0xfd, 0xfa, 0x02, 0xfc, 0xfb, 0xff, + 0x00, 0x00, 0x02, 0x01, 0xfb, 0x06, 0x0c, 0x04, 0x0d, 0x07, 0xf9, 0xf8, 0xf6, 0xf9, 0x01, 0x0a, 0x03, 0x00, 0x0b, 0x05, 0xfc, 0xfa, + 0xfc, 0x03, 0x05, 0x01, 0x01, 0xfd, 0xfd, 0x02, 0xf7, 0xfc, 0x08, 0xfd, 0xfa, 0xfb, 0xfb, 0xfa, 0xfd, 0x01, 0xff, 0x05, 0x01, 0xf7, + 0xf3, 0x01, 0x01, 0xf2, 0x03, 0x0a, 0x04, 0xfc, 0x06, 0x07, 0xfc, 0xf4, 0x03, 0x05, 0xf2, 0x15, 0x0c, 0xef, 0xef, 0x05, 0x0a, 0xfe, + 0x06, 0xff, 0xff, 0x0d, 0x00, 0xf7, 0xf9, 0xfc, 0xff, 0x04, 0x08, 0x04, 0xfe, 0xfa, 0xfc, 0xf7, 0xfe, 0x0b, 0x0f, 0x01, 0xfa, 0x01, + 0xfc, 0xfd, 0x04, 0x0c, 0xff, 0xf8, 0xfc, 0x01, 0x01, 0xfe, 0xfe, 0x05, 0x04, 0x03, 0x08, 0xff, 0xf5, 0xf5, 0x05, 0xfe, 0xf9, 0x05, + 0x04, 0xfd, 0xf9, 0x01, 0x03, 0x00, 0xfe, 0x09, 0xfe, 0xf5, 0xff, 0xf7, 0xfc, 0x05, 0x07, 0xf5, 0xf7, 0x09, 0x02, 0xfc, 0xfd, 0x00, + 0x03, 0xfe, 0xfc, 0x09, 0xff, 0xf9, 0xff, 0xfc, 0x01, 0x05, 0xff, 0x01, 0xfb, 0xf6, 0x02, 0x08, 0x03, 0xfb, 0x04, 0x04, 0xfd, 0xfa, + 0xfd, 0xff, 0x00, 0x02, 0x04, 0x07, 0x07, 0x01, 0xff, 0xfd, 0xf7, 0x06, 0x0d, 0x05, 0xfb, 0x04, 0x02, 0xef, 0xfe, 0x08, 0x08, 0x05, + 0xf9, 0xf7, 0xfd, 0xee, 0x04, 0x0e, 0xf4, 0x02, 0x0d, 0x04, 0xf0, 0xf8, 0xfb, 0xf5, 0x01, 0x06, 0x03, 0x03, 0xfe, 0xff, 0x02, 0xff, + 0xfd, 0xfc, 0xfa, 0x00, 0xff, 0xfc, 0x01, 0x0a, 0x05, 0xf9, 0x03, 0xf9, 0xfa, 0x0a, 0x08, 0xf8, 0xf4, 0x08, 0xfc, 0xf2, 0xfb, 0x0d, + 0x01, 0xf2, 0xf5, 0xfd, 0xff, 0xfb, 0xfb, 0x01, 0x07, 0x06, 0x01, 0xf9, 0xf9, 0x01, 0xfb, 0x00, 0x0a, 0x0a, 0xfd, 0xf9, 0x01, 0xfe, + 0x06, 0x0b, 0x03, 0xf7, 0xf8, 0x01, 0x0d, 0xfd, 0xfe, 0x11, 0xf9, 0x01, 0x10, 0x05, 0x04, 0x01, 0xfa, 0xf7, 0x02, 0x0a, 0x04, 0x04, + 0xf9, 0xf4, 0xfc, 0x0d, 0x07, 0xf5, 0xfe, 0xfb, 0xf6, 0xf8, 0xfc, 0x05, 0x08, 0xfe, 0xfc, 0x02, 0x08, 0x04, 0x01, 0x01, 0x00, 0xf7, + 0xf5, 0xfb, 0x04, 0x05, 0x00, 0xfb, 0xf4, 0xfa, 0xff, 0xf8, 0xf9, 0xfd, 0x01, 0x00, 0x07, 0x08, 0x02, 0xf8, 0xfd, 0x02, 0xfa, 0xf7, + 0xfc, 0x06, 0x07, 0x07, 0x01, 0xfb, 0x00, 0xfc, 0xf8, 0x00, 0xf5, 0xf5, 0x05, 0x1c, 0xf8, 0xea, 0x0c, 0xf8, 0xf5, 0x01, 0x08, 0xfe, + 0xfd, 0x04, 0x03, 0xff, 0x04, 0x0f, 0x0a, 0xfc, 0xf5, 0x00, 0xfe, 0xfc, 0x02, 0x0e, 0x0c, 0x01, 0xf9, 0x02, 0x01, 0xfa, 0x01, 0x00, + 0xfe, 0xff, 0xfd, 0x02, 0x06, 0x00, 0x03, 0x00, 0xfa, 0xfb, 0x02, 0x01, 0xf9, 0xff, 0x01, 0xfd, 0xf7, 0x01, 0x02, 0xfd, 0x07, 0x00, + 0xf3, 0xf0, 0xfb, 0x06, 0x07, 0xfd, 0x01, 0x05, 0x03, 0x00, 0xfd, 0xfe, 0x03, 0x05, 0x02, 0x02, 0x08, 0xfd, 0xf8, 0xff, 0xfd, 0x02, + 0x04, 0xfa, 0xfd, 0xfe, 0xfb, 0xf6, 0x0b, 0x0c, 0xf6, 0xfd, 0xff, 0xfd, 0x01, 0x04, 0x01, 0xfa, 0xf5, 0xfb, 0x00, 0xfd, 0xff, 0xfd, + 0xfe, 0x01, 0x03, 0xfb, 0xf2, 0xf9, 0x04, 0x0a, 0x09, 0x03, 0xfd, 0xfd, 0x02, 0x01, 0x04, 0x0d, 0x14, 0xfe, 0xef, 0xfe, 0xfd, 0x04, + 0x0d, 0x0f, 0xf9, 0xf0, 0xfa, 0x04, 0xf5, 0xf6, 0x13, 0x00, 0xf7, 0xfc, 0xfd, 0xf9, 0xfe, 0x04, 0x00, 0xee, 0xf0, 0x11, 0x09, 0x01, + 0x01, 0x02, 0xf8, 0xf8, 0x02, 0x07, 0xfd, 0xf3, 0xf6, 0x0b, 0x0d, 0xfe, 0xf9, 0x00, 0x01, 0xf9, 0x0c, 0x0f, 0x07, 0x01, 0xff, 0xfd, + 0xfb, 0xf4, 0x05, 0x0b, 0xfb, 0xf3, 0xfd, 0x08, 0x00, 0xfe, 0xfe, 0xff, 0xf8, 0xfe, 0x02, 0xfb, 0xf7, 0xfa, 0xfe, 0xfb, 0x01, 0x02, + 0xff, 0x00, 0x08, 0x07, 0xfb, 0x02, 0x04, 0xfc, 0xf5, 0xfa, 0x07, 0x0c, 0xf4, 0xee, 0xf7, 0xfd, 0x05, 0x0a, 0x09, 0xff, 0x04, 0x00, + 0xf1, 0xff, 0x03, 0x01, 0x05, 0x00, 0xfe, 0xfe, 0xfc, 0xf5, 0xfa, 0x0a, 0x04, 0xfd, 0xfd, 0x02, 0xfe, 0xff, 0x05, 0x01, 0x01, 0x02, + 0x00, 0xf3, 0xfe, 0x0e, 0x07, 0x00, 0x01, 0x06, 0xff, 0x02, 0x06, 0x04, 0xf8, 0xfc, 0x04, 0xfb, 0xfd, 0x04, 0x07, 0x07, 0xf8, 0xf2, + 0x03, 0xfe, 0x02, 0x09, 0xfd, 0xfd, 0x00, 0x00, 0x03, 0x03, 0xfd, 0xf2, 0x05, 0x08, 0xfc, 0x05, 0xf7, 0xf1, 0x00, 0x07, 0x01, 0xfb, + 0xfd, 0xfe, 0xf8, 0xf1, 0xff, 0x00, 0xfc, 0xfa, 0x05, 0xfe, 0xf6, 0x02, 0x09, 0x00, 0xf2, 0xff, 0xfd, 0xf9, 0xfe, 0x0c, 0x07, 0xf8, + 0xf8, 0xff, 0xff, 0xf7, 0x03, 0xfb, 0xf8, 0x0b, 0x00, 0x00, 0x08, 0x01, 0xf8, 0xfe, 0x0c, 0x07, 0x03, 0x01, 0x00, 0x07, 0x06, 0x00, + 0x05, 0x00, 0xfb, 0xfd, 0xfd, 0xfc, 0xfe, 0x08, 0xfb, 0xf9, 0x05, 0x04, 0x00, 0x00, 0x04, 0xfc, 0xfb, 0x00, 0x08, 0x00, 0xfe, 0x04, + 0x05, 0xfd, 0x00, 0x0f, 0x04, 0xf8, 0xf8, 0x03, 0x05, 0x06, 0x06, 0xfc, 0xfa, 0xfe, 0x00, 0x0d, 0x07, 0xf9, 0xfa, 0x03, 0xff, 0xf4, + 0x07, 0x04, 0xf8, 0xf5, 0x04, 0x00, 0xf2, 0xfa, 0xfd, 0xff, 0x03, 0x00, 0xfc, 0xfb, 0xfd, 0xfb, 0xf6, 0xf4, 0xfd, 0xf9, 0xfc, 0x09, + 0x02, 0xfc, 0xfc, 0xff, 0xfc, 0xff, 0x07, 0x07, 0xfb, 0xf8, 0x02, 0xfd, 0xfc, 0xff, 0xfe, 0x00, 0x05, 0x06, 0xf6, 0x00, 0x06, 0xf7, + 0xf9, 0x08, 0x0f, 0xfb, 0xfb, 0x01, 0x02, 0xff, 0x01, 0x04, 0x04, 0x06, 0x04, 0x00, 0xfe, 0x03, 0x04, 0x01, 0x05, 0x01, 0xfc, 0x00, + 0xf8, 0xf8, 0x05, 0x14, 0x08, 0xff, 0x06, 0xfa, 0xfd, 0x07, 0x08, 0x05, 0xfd, 0xf8, 0x07, 0x02, 0xfd, 0x04, 0x00, 0xf9, 0xf8, 0xfd, + 0x05, 0x02, 0xf9, 0x01, 0xfa, 0xf4, 0xf9, 0x06, 0x03, 0xfc, 0x06, 0x00, 0xf3, 0xf1, 0x0e, 0x0d, 0xfb, 0xf1, 0x06, 0x00, 0xef, 0xfe, + 0xfe, 0xf9, 0xf9, 0x06, 0x06, 0xfb, 0xf3, 0xfe, 0xff, 0xf8, 0x01, 0xfb, 0xfc, 0x07, 0x05, 0xf7, 0xf4, 0x09, 0x01, 0xff, 0x05, 0xfd, + 0x04, 0x09, 0x03, 0xfc, 0x03, 0x0b, 0x07, 0x01, 0xfe, 0xfe, 0x01, 0x00, 0xff, 0x04, 0x0c, 0x0b, 0x03, 0xfe, 0xfe, 0x00, 0xff, 0xfe, + 0xfe, 0x00, 0x01, 0x06, 0x00, 0xfb, 0x0d, 0xfc, 0xf1, 0xfe, 0x08, 0x04, 0xfd, 0xff, 0x00, 0xff, 0xff, 0x09, 0xfe, 0xfd, 0x0b, 0x06, + 0xfc, 0xfd, 0x0a, 0x05, 0x02, 0x04, 0xfb, 0xfa, 0xfd, 0xff, 0x01, 0x00, 0xfa, 0xf7, 0xf8, 0xfc, 0x00, 0xfe, 0xfd, 0xfe, 0x00, 0x02, + 0xff, 0xfb, 0xfc, 0xfb, 0xf7, 0xf5, 0x03, 0x07, 0x02, 0xfc, 0xfe, 0xfc, 0xfb, 0x05, 0x03, 0xfb, 0xf5, 0xfe, 0x03, 0x00, 0xfa, 0x01, + 0x01, 0xfd, 0x09, 0x05, 0x04, 0x07, 0xfa, 0xfc, 0x04, 0x02, 0x02, 0xff, 0xfc, 0xfc, 0xff, 0xfb, 0xf3, 0x01, 0x06, 0x01, 0xff, 0x08, + 0x05, 0xf8, 0xf4, 0xfb, 0xfe, 0xf9, 0x01, 0x06, 0x07, 0x04, 0x05, 0x04, 0x04, 0x07, 0x05, 0x03, 0x07, 0xf5, 0xfb, 0x09, 0xff, 0xf8, + 0xfb, 0x02, 0x04, 0x01, 0xfe, 0xfd, 0xf8, 0xf5, 0xfa, 0x07, 0x0b, 0x07, 0xff, 0xf2, 0xf9, 0xff, 0xf5, 0xfc, 0x05, 0x05, 0xfe, 0xfa, + 0xfa, 0xfd, 0x02, 0xfc, 0xf7, 0xff, 0xf8, 0xf8, 0xff, 0xf5, 0x03, 0x08, 0xf8, 0x02, 0x04, 0x00, 0x02, 0xfe, 0xfe, 0x02, 0x08, 0x02, + 0xff, 0x05, 0x01, 0xfe, 0xff, 0x03, 0x07, 0x05, 0xff, 0xfe, 0xf8, 0xfa, 0x07, 0x07, 0xff, 0xfd, 0x0a, 0x00, 0xf7, 0xfb, 0x02, 0xfd, + 0xfc, 0x0d, 0xff, 0xfb, 0x05, 0x09, 0xff, 0xfa, 0x00, 0x0b, 0xf5, 0xe8, 0x07, 0x03, 0xfa, 0xfb, 0x02, 0xfd, 0xfc, 0x04, 0xf3, 0xf5, + 0x04, 0x05, 0xfe, 0xfc, 0x01, 0x02, 0x02, 0xff, 0xfa, 0xfa, 0xff, 0x00, 0xfa, 0xfb, 0x06, 0x0c, 0xf5, 0xf9, 0x04, 0xfd, 0xf7, 0xfb, + 0x05, 0x0c, 0xfd, 0xf6, 0x01, 0xfc, 0x07, 0x0b, 0xfb, 0xff, 0x02, 0xfc, 0xf4, 0xf8, 0x03, 0x0b, 0x10, 0x04, 0xfb, 0x05, 0x07, 0xfd, + 0xf4, 0x06, 0xfd, 0xff, 0x15, 0x0b, 0xff, 0x01, 0x0c, 0x02, 0xfe, 0x03, 0xfc, 0x00, 0x06, 0x02, 0x04, 0xfd, 0xfa, 0x07, 0x08, 0xff, + 0xf9, 0x01, 0x00, 0xfb, 0xf9, 0x05, 0xfd, 0xf2, 0x05, 0x02, 0xf9, 0xf7, 0x01, 0xf1, 0xea, 0x02, 0x01, 0x01, 0x03, 0xf7, 0xfb, 0xfd, + 0xf7, 0xfb, 0xfb, 0xfa, 0xfe, 0xfe, 0xff, 0x01, 0x01, 0x04, 0x01, 0xfa, 0x09, 0x05, 0xf9, 0xfe, 0x03, 0x02, 0xfd, 0xff, 0x00, 0x01, + 0x01, 0x01, 0xff, 0xfe, 0xfe, 0x04, 0xfd, 0xf2, 0x04, 0x08, 0x05, 0x05, 0x00, 0xfc, 0xfb, 0xf7, 0x03, 0x0b, 0x09, 0x08, 0x05, 0x03, + 0x03, 0x07, 0x01, 0xfc, 0x0d, 0xfb, 0xf9, 0x0e, 0x00, 0x00, 0x07, 0x04, 0x07, 0x0a, 0x06, 0xf9, 0xfd, 0x03, 0x01, 0x00, 0xfc, 0xfb, + 0x03, 0xfb, 0xfb, 0x01, 0xfb, 0xfb, 0xfe, 0xfd, 0xf6, 0xf8, 0xfd, 0xfd, 0x05, 0x00, 0xf2, 0xf6, 0xf7, 0xf7, 0xfc, 0x06, 0x00, 0xf8, + 0x05, 0xff, 0xfc, 0x01, 0xf8, 0xfb, 0xfe, 0xf6, 0x07, 0x00, 0xf1, 0x00, 0xfb, 0xfa, 0x05, 0xff, 0x06, 0x0a, 0xfa, 0xf3, 0xfb, 0x04, + 0xf5, 0x02, 0x08, 0xfa, 0x03, 0x05, 0x00, 0xfd, 0x04, 0x06, 0x01, 0x02, 0x06, 0x0a, 0x0b, 0x08, 0x05, 0xff, 0xf8, 0xfe, 0x07, 0x09, + 0xfc, 0x02, 0x09, 0x02, 0x01, 0xfe, 0xfd, 0x02, 0x0c, 0x09, 0xfb, 0xf8, 0xf9, 0xfe, 0x04, 0x05, 0x0d, 0x0d, 0xf1, 0xfd, 0x05, 0xf8, + 0xfc, 0x00, 0xfe, 0xf7, 0xfc, 0x01, 0xff, 0xf2, 0xfe, 0x02, 0xf5, 0xf7, 0x04, 0x08, 0xf7, 0xfe, 0xff, 0xf9, 0xf7, 0x04, 0xff, 0xe6, + 0x03, 0x04, 0xf5, 0xfc, 0xfc, 0xff, 0x02, 0xfb, 0xfc, 0xfe, 0xfc, 0x00, 0xfa, 0xf5, 0x01, 0x07, 0x08, 0x06, 0xfd, 0xf6, 0x00, 0x13, + 0x00, 0xfa, 0x05, 0x00, 0x06, 0x02, 0xf9, 0x05, 0x0f, 0x0a, 0xfb, 0xfe, 0x04, 0x05, 0x05, 0xfa, 0xfd, 0x0e, 0x03, 0xfe, 0xff, 0xfe, + 0x00, 0x05, 0x08, 0x00, 0x01, 0xff, 0xf6, 0x05, 0x05, 0xff, 0x04, 0x03, 0x01, 0xfd, 0xf7, 0xf8, 0xfe, 0x04, 0x06, 0x03, 0x02, 0x05, + 0xff, 0xfb, 0xfd, 0xfe, 0xfd, 0xfb, 0xfc, 0xf9, 0xfd, 0x03, 0xff, 0xf9, 0xf6, 0xfa, 0x00, 0xfd, 0xf4, 0xed, 0x01, 0x02, 0xf5, 0xf9, + 0xfa, 0xff, 0x06, 0x00, 0xfa, 0xf9, 0xfb, 0xf8, 0xfe, 0x08, 0xff, 0x0a, 0x0a, 0xf8, 0x05, 0x00, 0xf5, 0x00, 0x03, 0x0b, 0x0f, 0xfc, + 0xf9, 0x02, 0x0a, 0xff, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x01, 0xfb, 0x00, 0x07, 0x09, 0x03, 0x04, 0x04, 0xf7, 0xf9, 0xff, 0x01, 0x0b, + 0x03, 0xf9, 0xfb, 0x07, 0x06, 0xfb, 0xf8, 0x03, 0x06, 0xfd, 0x06, 0x05, 0xfe, 0x00, 0xf7, 0x00, 0x0c, 0xfb, 0xfa, 0xfe, 0xfd, 0xfc, + 0xf9, 0xf7, 0xfc, 0xfe, 0x01, 0x03, 0xff, 0x01, 0x02, 0xff, 0xed, 0xf7, 0x07, 0xff, 0xfb, 0x00, 0x06, 0xfe, 0xff, 0xfe, 0xf7, 0x00, + 0x04, 0xff, 0xf9, 0xf6, 0xf9, 0xff, 0x03, 0x08, 0x06, 0xfe, 0xf2, 0xf5, 0x03, 0x0d, 0xfd, 0xf7, 0x04, 0x05, 0x02, 0x00, 0x04, 0xfe, + 0xfe, 0x02, 0x05, 0xfb, 0xf7, 0x00, 0x0b, 0x0c, 0x04, 0xfb, 0x04, 0x00, 0xf7, 0x07, 0x04, 0xfb, 0xfb, 0x05, 0x05, 0x05, 0x12, 0xfa, + 0xf3, 0x04, 0x02, 0xfe, 0xfc, 0xfe, 0x04, 0x06, 0x01, 0x00, 0xf5, 0xfa, 0x09, 0x01, 0xf7, 0xfa, 0x07, 0xf4, 0xf5, 0x07, 0x0d, 0x04, + 0xff, 0x03, 0xf9, 0xf5, 0xfb, 0x07, 0xf6, 0xf9, 0x0b, 0x0b, 0xfe, 0xfa, 0x01, 0xf5, 0xf4, 0xfd, 0x01, 0x08, 0x04, 0xfd, 0x02, 0x05, + 0xfd, 0xf1, 0x0a, 0x0b, 0xfc, 0xf9, 0xf8, 0xf6, 0xf8, 0x0a, 0x06, 0xfd, 0xff, 0x06, 0xff, 0xf5, 0xfb, 0xff, 0xfb, 0xf8, 0x0f, 0x0c, + 0x02, 0x02, 0x03, 0x00, 0xff, 0x04, 0x09, 0x06, 0x00, 0xfc, 0x05, 0x0a, 0x01, 0xf3, 0xf9, 0x09, 0x04, 0x01, 0xfe, 0xfb, 0xf2, 0x06, + 0x14, 0x00, 0x05, 0x03, 0xfa, 0xfb, 0xfd, 0xf8, 0xf2, 0x05, 0x07, 0xfe, 0x00, 0xf9, 0xf6, 0xfa, 0xfd, 0xfc, 0xfe, 0x04, 0xf9, 0xfa, + 0x00, 0xfd, 0x05, 0x04, 0xfa, 0xff, 0x04, 0xff, 0xf5, 0x00, 0x0a, 0x08, 0xfe, 0x02, 0xfd, 0xf1, 0xfb, 0x02, 0xfe, 0xf5, 0xfe, 0x01, + 0xff, 0x02, 0xff, 0xfd, 0x00, 0xf8, 0x00, 0x0a, 0x04, 0xf6, 0xf5, 0xff, 0xfc, 0x09, 0x0a, 0xfb, 0x0e, 0x06, 0xf9, 0x05, 0x03, 0xfb, + 0xf9, 0x06, 0x06, 0x05, 0x08, 0x02, 0xf3, 0xf3, 0x11, 0x04, 0xfa, 0x02, 0x05, 0x01, 0xfe, 0x00, 0xf4, 0xfd, 0x0b, 0x01, 0x05, 0xff, + 0xf0, 0xfd, 0xfd, 0xfa, 0xff, 0x13, 0x06, 0xef, 0xf6, 0x02, 0xfc, 0xee, 0xff, 0x06, 0x05, 0x05, 0x01, 0xf7, 0xf3, 0x00, 0x09, 0x03, + 0xf7, 0x04, 0x02, 0xf6, 0xf0, 0x00, 0x06, 0x01, 0x03, 0x06, 0x00, 0xf4, 0xfb, 0xfe, 0x02, 0x0c, 0x01, 0xf9, 0xfd, 0x07, 0x06, 0xff, + 0xfc, 0xee, 0xf9, 0x0b, 0x02, 0x06, 0x05, 0xfd, 0x00, 0xfc, 0xfc, 0x04, 0xf9, 0xfe, 0x08, 0xfc, 0x01, 0x08, 0x03, 0xfa, 0x02, 0x08, + 0xfe, 0x03, 0xff, 0xf8, 0x01, 0x02, 0xfe, 0xfc, 0xf5, 0xff, 0x0a, 0x07, 0xf9, 0xf8, 0x00, 0xfd, 0xfe, 0x01, 0x02, 0x07, 0xfd, 0xf6, + 0x04, 0x04, 0xff, 0xfe, 0x04, 0xfe, 0xfd, 0x07, 0x04, 0x00, 0x00, 0xff, 0xff, 0xfd, 0xfa, 0xf8, 0xfc, 0x01, 0x02, 0xf6, 0xfb, 0x05, + 0xfb, 0xfc, 0x00, 0x01, 0xfe, 0xfe, 0x01, 0x05, 0xff, 0xfd, 0x00, 0x03, 0x02, 0x04, 0x06, 0xfc, 0xf7, 0xfd, 0x06, 0x01, 0xfd, 0xff, + 0xfb, 0x01, 0x01, 0xf8, 0xf5, 0x01, 0x0a, 0x01, 0x01, 0x06, 0x07, 0x03, 0xfd, 0x00, 0x0b, 0x05, 0xfb, 0xfb, 0x09, 0xf9, 0xf7, 0x06, + 0xfd, 0xff, 0x04, 0x03, 0xfc, 0xfc, 0xfd, 0xf6, 0x02, 0x06, 0xfc, 0x06, 0xfe, 0xf8, 0x03, 0x07, 0x01, 0xfb, 0xfd, 0x04, 0x09, 0x07, + 0x05, 0xfd, 0xfa, 0x01, 0xff, 0xf9, 0xfb, 0x0f, 0x01, 0xf3, 0xf9, 0x03, 0xfe, 0xf7, 0xff, 0xfe, 0xff, 0x01, 0x05, 0xfd, 0xfb, 0x02, + 0xff, 0xf8, 0xfb, 0x0d, 0x00, 0xf9, 0x01, 0xfd, 0x00, 0x05, 0x06, 0xf9, 0xfa, 0x03, 0xff, 0x01, 0xff, 0xfb, 0x00, 0x01, 0x02, 0x05, + 0x05, 0xff, 0xfc, 0x06, 0xfe, 0xf8, 0xff, 0x0a, 0xff, 0xf7, 0x06, 0xfe, 0xfb, 0xff, 0x02, 0xf7, 0xf9, 0x07, 0x04, 0xfd, 0xfe, 0x04, + 0x05, 0x01, 0xfc, 0xf3, 0x07, 0x13, 0x05, 0xfd, 0xfb, 0xfe, 0x04, 0xfb, 0xfa, 0x03, 0x04, 0xfd, 0xf9, 0xfe, 0xf8, 0xff, 0x04, 0xf7, + 0x02, 0x0a, 0x00, 0xef, 0xf9, 0x07, 0x04, 0x03, 0x04, 0x03, 0xfd, 0xf6, 0xf7, 0x01, 0x03, 0xff, 0xfc, 0x01, 0xfd, 0x03, 0x06, 0xfa, + 0xf3, 0xfd, 0x0c, 0x03, 0xfa, 0xff, 0x0d, 0x04, 0xfb, 0xfa, 0xfb, 0x03, 0x07, 0x02, 0xfc, 0xfd, 0x01, 0x06, 0xfc, 0xfb, 0x05, 0x08, + 0xff, 0xf9, 0xfd, 0xfc, 0xff, 0x02, 0x04, 0x04, 0x01, 0xfc, 0xfa, 0xfe, 0x02, 0x03, 0x07, 0x02, 0xfd, 0xff, 0xfd, 0xfd, 0x00, 0xfe, + 0x00, 0x01, 0xfd, 0xf9, 0xfe, 0x03, 0xf8, 0xfd, 0x05, 0x05, 0xfc, 0xfb, 0xfc, 0xf8, 0x01, 0x07, 0x04, 0xfd, 0x00, 0x00, 0xf8, 0xfa, + 0x00, 0x05, 0x06, 0x01, 0xfb, 0xfa, 0xfe, 0x00, 0xff, 0xfd, 0x00, 0x02, 0x03, 0x01, 0x04, 0x01, 0xfa, 0xf9, 0x01, 0x05, 0xff, 0x02, + 0x04, 0x02, 0xfb, 0x01, 0x00, 0xf6, 0xfa, 0xfb, 0x01, 0x0c, 0xfd, 0xf1, 0xf9, 0x0a, 0x08, 0x01, 0x00, 0xff, 0xfd, 0xfc, 0xff, 0xfe, + 0x05, 0x0c, 0x09, 0xfb, 0xfa, 0x05, 0xfa, 0xf9, 0x03, 0x07, 0x08, 0xfd, 0xf4, 0xff, 0xff, 0xfe, 0x03, 0xfd, 0xfb, 0xfe, 0x04, 0xf9, + 0xf8, 0x03, 0x04, 0x03, 0x02, 0x02, 0xfe, 0xfb, 0xf9, 0xf6, 0x08, 0x0f, 0x04, 0xff, 0xf9, 0xf9, 0xff, 0xff, 0xfc, 0xfb, 0xff, 0xff, + 0xfb, 0xfa, 0x02, 0x05, 0x01, 0xfb, 0xf8, 0xfc, 0x04, 0x0b, 0x04, 0xfb, 0xfa, 0xf7, 0x03, 0x0b, 0xff, 0xfd, 0xfe, 0xfd, 0xf6, 0x02, + 0x05, 0xf7, 0xf4, 0xff, 0x0a, 0x03, 0x03, 0xff, 0xfa, 0xfe, 0x02, 0x02, 0x01, 0x00, 0x00, 0xff, 0xfc, 0x04, 0x09, 0x06, 0x01, 0x00, + 0xfe, 0xf8, 0xfe, 0x06, 0x09, 0x01, 0x03, 0x00, 0xf9, 0xfd, 0x04, 0x06, 0x01, 0xfc, 0xfb, 0xfc, 0xfe, 0xff, 0x03, 0x08, 0x02, 0xfc, + 0xf8, 0xf3, 0xff, 0x02, 0xfd, 0x06, 0x05, 0x03, 0x01, 0xfd, 0xfc, 0x01, 0x07, 0x07, 0xff, 0xf6, 0xf9, 0xfa, 0xfe, 0x07, 0x03, 0xff, + 0xff, 0x02, 0xec, 0xec, 0x04, 0x04, 0x01, 0xfd, 0xfb, 0xff, 0xfd, 0xf9, 0xfc, 0x01, 0x03, 0xfe, 0xf6, 0xf9, 0x05, 0x0d, 0x07, 0xfe, + 0xfe, 0x05, 0x01, 0xfb, 0xfb, 0xfc, 0xff, 0x04, 0x05, 0x07, 0x01, 0xfa, 0x04, 0x02, 0xfe, 0x00, 0x09, 0x04, 0xfd, 0xfe, 0x09, 0x06, + 0xfb, 0xfd, 0x06, 0x0b, 0x06, 0xff, 0xfb, 0xfe, 0x03, 0x02, 0xfc, 0xfb, 0x07, 0x07, 0x01, 0x00, 0xf8, 0xfb, 0x03, 0x04, 0xf9, 0xfd, + 0x0a, 0x07, 0xfc, 0xf7, 0xfe, 0x01, 0x01, 0x00, 0x03, 0xf9, 0xf8, 0x03, 0xfe, 0x00, 0x06, 0x06, 0xfb, 0xf5, 0xf7, 0xfd, 0xf4, 0xf8, + 0x08, 0xfb, 0xf9, 0x02, 0x09, 0xfb, 0xf6, 0xfd, 0xfe, 0xfd, 0xff, 0x03, 0xfe, 0x01, 0x05, 0x01, 0x02, 0x02, 0xfe, 0xff, 0xfa, 0xfc, + 0x07, 0xff, 0xfe, 0x04, 0x00, 0x05, 0x06, 0x01, 0x02, 0xfd, 0xfa, 0xff, 0xff, 0x01, 0x05, 0x07, 0x09, 0x05, 0xfe, 0x01, 0x00, 0xff, + 0x03, 0x02, 0xff, 0xfb, 0xf8, 0x01, 0x06, 0x04, 0x08, 0x08, 0xfe, 0xed, 0xfb, 0x07, 0x04, 0xfd, 0x01, 0x06, 0x03, 0x02, 0xfb, 0xf7, + 0xfb, 0x01, 0x01, 0xff, 0xfc, 0x00, 0xff, 0xf8, 0xff, 0x05, 0x02, 0xf9, 0xfc, 0xfc, 0xf5, 0xf9, 0xfe, 0x01, 0x03, 0x06, 0x03, 0xfb, + 0xf5, 0xfc, 0x03, 0x02, 0xfa, 0xfa, 0xff, 0xff, 0x03, 0x01, 0x00, 0x0c, 0xff, 0xf6, 0xfe, 0x00, 0xfe, 0xfd, 0x00, 0x07, 0x03, 0xfb, + 0x01, 0x03, 0x02, 0x01, 0x04, 0xfe, 0xf7, 0xfc, 0x07, 0x0a, 0x04, 0xff, 0x01, 0xfe, 0xf5, 0xfa, 0xfc, 0xfb, 0x03, 0x06, 0x09, 0x08, + 0xfd, 0xf5, 0xfd, 0x0e, 0xfa, 0xfb, 0x05, 0xf6, 0xfa, 0x01, 0x03, 0x0a, 0x04, 0xfb, 0xff, 0xfc, 0xfe, 0x01, 0xfc, 0xfc, 0x01, 0x04, + 0x03, 0xf8, 0xf9, 0x0d, 0x01, 0xf6, 0xf7, 0xfb, 0xf9, 0xfc, 0x06, 0x0a, 0x02, 0xfa, 0xfe, 0xf1, 0xf5, 0x04, 0xff, 0xf7, 0xfa, 0x04, + 0x05, 0xfd, 0xfa, 0x06, 0x08, 0x04, 0xfc, 0xee, 0xf7, 0x01, 0xfc, 0x05, 0x0a, 0x05, 0xf6, 0xfb, 0x02, 0x03, 0x04, 0x03, 0x00, 0x00, + 0xfd, 0x03, 0x09, 0x02, 0x00, 0x04, 0x07, 0xfc, 0xf9, 0xfe, 0x05, 0x01, 0x05, 0x0a, 0x01, 0xff, 0x00, 0x01, 0xfe, 0xfb, 0xfa, 0xfa, + 0xfe, 0x04, 0x05, 0x00, 0xfe, 0xfc, 0xf9, 0x00, 0xff, 0xfa, 0xfc, 0xf5, 0xf8, 0x06, 0x0b, 0x05, 0xfe, 0xfb, 0xfa, 0xff, 0x02, 0xfa, + 0x00, 0x01, 0xfc, 0x04, 0x05, 0x02, 0xff, 0xff, 0xff, 0xfc, 0xf5, 0xf6, 0xfb, 0x02, 0x11, 0x0c, 0xfc, 0xf1, 0xfe, 0x06, 0x01, 0xfb, + 0xfe, 0x03, 0x01, 0xfc, 0xf9, 0xfc, 0x01, 0x0a, 0x09, 0xff, 0xf5, 0xff, 0x05, 0xfc, 0xfa, 0x01, 0x07, 0x01, 0x09, 0x06, 0xfc, 0x01, + 0xfe, 0xfc, 0xff, 0x08, 0x03, 0xfc, 0x00, 0x04, 0x09, 0x07, 0xf8, 0xfb, 0x02, 0xfd, 0x01, 0xfb, 0xf6, 0x08, 0xfe, 0xfc, 0x0a, 0x05, + 0xfb, 0xf8, 0xfe, 0xf4, 0xfb, 0x07, 0xfa, 0xf9, 0x02, 0x0a, 0x01, 0xfd, 0xff, 0x03, 0xf1, 0xf9, 0x0d, 0x02, 0xf9, 0xf7, 0xfc, 0x07, + 0x05, 0xfc, 0xfd, 0xf9, 0xfe, 0x04, 0xfa, 0xfc, 0x04, 0x07, 0x04, 0x00, 0x01, 0x06, 0xfe, 0xf7, 0xf9, 0xff, 0x0a, 0x09, 0xfa, 0x00, + 0x04, 0x01, 0x00, 0x00, 0xfd, 0xfa, 0xfd, 0x01, 0x04, 0x05, 0xfe, 0x02, 0x08, 0x02, 0xfa, 0xfd, 0x07, 0xfc, 0x00, 0x06, 0x00, 0xff, + 0x05, 0x0a, 0x05, 0xfe, 0xf9, 0xf9, 0xfe, 0x08, 0x06, 0xf3, 0x00, 0x05, 0xfe, 0xfe, 0x07, 0x04, 0xf5, 0xfd, 0xfd, 0xf9, 0x02, 0xf6, + 0xf6, 0x02, 0xfb, 0x02, 0x06, 0xff, 0xfe, 0x01, 0x00, 0xf6, 0xf2, 0xfa, 0x05, 0xff, 0x04, 0x05, 0xf8, 0xfd, 0xfe, 0xfb, 0xff, 0x01, + 0xfb, 0xf6, 0xff, 0x0d, 0x0a, 0xf6, 0xf9, 0x02, 0x05, 0x01, 0xfb, 0xf6, 0xf8, 0x02, 0x0b, 0x0e, 0x0a, 0x02, 0xfd, 0xfb, 0xf9, 0xff, + 0x01, 0xfb, 0x0d, 0x07, 0xf9, 0x01, 0x04, 0x00, 0xff, 0x11, 0xfc, 0xf0, 0x05, 0x01, 0xfd, 0x00, 0x0a, 0xff, 0xfd, 0x05, 0xfb, 0xf7, + 0xf9, 0xfc, 0x07, 0x0a, 0x05, 0xfe, 0x02, 0x03, 0xfd, 0xfb, 0x02, 0x03, 0xf9, 0xf7, 0xfc, 0x05, 0x0c, 0x04, 0xfb, 0xfa, 0x04, 0xfe, + 0xf7, 0x00, 0xfe, 0xf8, 0xf9, 0x09, 0x01, 0xfb, 0x02, 0xf9, 0xfb, 0x03, 0x03, 0xf3, 0xf4, 0x03, 0x01, 0xff, 0xfe, 0xff, 0xf8, 0xfd, + 0x03, 0x00, 0xf2, 0xf7, 0x08, 0xfc, 0x05, 0x08, 0xf6, 0xfd, 0x03, 0x04, 0x06, 0x08, 0xfe, 0xf3, 0x01, 0x08, 0x06, 0x06, 0xfd, 0x05, + 0x0c, 0xf7, 0x01, 0x08, 0xfd, 0xf6, 0xfa, 0x02, 0x03, 0xfe, 0xfd, 0x00, 0x00, 0x02, 0x06, 0x08, 0xf8, 0xf9, 0x00, 0xf8, 0x01, 0x03, + 0xff, 0x08, 0x07, 0x00, 0xfe, 0xfb, 0xfc, 0xff, 0x01, 0xfa, 0xfb, 0x04, 0x04, 0xfe, 0xfe, 0x07, 0x04, 0x00, 0xfd, 0xf6, 0x01, 0x04, + 0xfc, 0xfc, 0x08, 0x0c, 0xfe, 0xfe, 0xf7, 0xf5, 0x09, 0x01, 0xf8, 0xfc, 0xfb, 0xfb, 0xfd, 0x00, 0x02, 0xf8, 0xf2, 0x02, 0x01, 0xf7, + 0xf7, 0x01, 0x11, 0x10, 0xf5, 0xf9, 0xff, 0xfc, 0xf6, 0x04, 0x0d, 0x03, 0xfc, 0x01, 0x05, 0xfb, 0x02, 0x03, 0xfd, 0x04, 0x02, 0xfb, + 0xf9, 0x03, 0x01, 0x01, 0x12, 0x0c, 0xf8, 0xec, 0xff, 0x01, 0xfc, 0x00, 0x09, 0x0b, 0x05, 0x03, 0xf6, 0xf6, 0x04, 0x01, 0x01, 0x01, + 0xf9, 0xf8, 0xfe, 0x03, 0xfb, 0x08, 0x05, 0xf2, 0x04, 0x08, 0xff, 0xfd, 0xf8, 0xfb, 0x02, 0x09, 0x01, 0x00, 0x08, 0x00, 0xf2, 0xf1, + 0x04, 0x07, 0x04, 0x01, 0x01, 0xf9, 0xf9, 0x03, 0xfc, 0xfd, 0x05, 0x03, 0x01, 0xfa, 0xf5, 0xfb, 0x01, 0x02, 0x01, 0x01, 0xfe, 0xfc, + 0x01, 0xff, 0xfb, 0xfa, 0xfe, 0x01, 0x05, 0x0a, 0xff, 0xf5, 0xf8, 0x03, 0x0b, 0x08, 0xfd, 0x03, 0xfe, 0xf9, 0xff, 0x05, 0x0a, 0x07, + 0xfa, 0xff, 0x02, 0xf8, 0xfd, 0x01, 0xff, 0xfb, 0x07, 0x05, 0xfa, 0x04, 0x00, 0xfb, 0x00, 0x02, 0x05, 0x04, 0xfa, 0xfe, 0x07, 0x0a, + 0xfb, 0xfa, 0x02, 0x06, 0xf4, 0xfb, 0x09, 0xf9, 0xfb, 0x06, 0x0a, 0xf2, 0xfe, 0x09, 0xf5, 0xf0, 0xf8, 0x02, 0x03, 0x03, 0x04, 0x06, + 0x08, 0xfe, 0xf6, 0xfd, 0x08, 0x05, 0xfb, 0xfe, 0x03, 0x01, 0xf8, 0xff, 0xfd, 0xfd, 0x09, 0x03, 0xf8, 0xf5, 0xfc, 0xfe, 0xfe, 0x01, + 0x06, 0x0c, 0x08, 0xf9, 0xfa, 0x01, 0x02, 0xf7, 0x03, 0x0b, 0xfd, 0xff, 0xff, 0xfe, 0xfe, 0x06, 0x00, 0xf3, 0xfc, 0xff, 0xfe, 0xff, + 0x00, 0x03, 0x05, 0x01, 0xfb, 0xfd, 0x06, 0x08, 0xfd, 0xf8, 0x06, 0xfe, 0xfe, 0x04, 0xff, 0x00, 0x05, 0x07, 0xf9, 0xfb, 0x01, 0xfb, + 0xfb, 0x00, 0x02, 0x00, 0xff, 0xfb, 0xf6, 0x04, 0xff, 0xf7, 0x04, 0x06, 0xfd, 0xf8, 0x0c, 0x07, 0xfd, 0xff, 0x05, 0x02, 0xfa, 0xf5, + 0xfd, 0x03, 0xff, 0x02, 0xfb, 0xf9, 0x01, 0x0a, 0x06, 0xfb, 0xf4, 0x02, 0x05, 0xf7, 0xff, 0xff, 0x02, 0x12, 0xff, 0xf9, 0x00, 0xfd, + 0xf9, 0x00, 0x0c, 0x03, 0xfc, 0xfe, 0x0a, 0x00, 0xfd, 0x02, 0x03, 0xf5, 0xf3, 0x02, 0x01, 0x02, 0x07, 0x0e, 0x00, 0xf8, 0xfb, 0xff, + 0xfa, 0xfa, 0x03, 0x00, 0xf9, 0xfa, 0x08, 0xfd, 0xfe, 0x0a, 0xf6, 0xf9, 0x04, 0x02, 0xf9, 0xf7, 0xff, 0x07, 0x07, 0x04, 0x04, 0x04, + 0xf9, 0xf2, 0xfb, 0xfc, 0x03, 0x0a, 0x04, 0xf7, 0xf6, 0x00, 0xfa, 0xfc, 0x01, 0x01, 0xfb, 0x01, 0x06, 0xf9, 0xf7, 0xfe, 0x04, 0x00, + 0xfd, 0x01, 0x06, 0x00, 0xfa, 0xfa, 0xfe, 0x05, 0x08, 0x04, 0x02, 0x04, 0x00, 0xf3, 0xfc, 0x05, 0x02, 0xf9, 0x01, 0x07, 0xfc, 0x03, + 0xfb, 0xf1, 0xfd, 0x0b, 0x07, 0xf9, 0x05, 0x01, 0xfd, 0x06, 0x01, 0x02, 0x09, 0x03, 0x06, 0x00, 0xf5, 0xfb, 0x07, 0x09, 0xfc, 0xfa, + 0xfe, 0x00, 0xf9, 0x02, 0x08, 0x02, 0xff, 0xfe, 0xfd, 0xfc, 0x02, 0x04, 0x00, 0xfe, 0x04, 0x04, 0xfb, 0xf4, 0xfd, 0x06, 0xff, 0xf7, + 0xfd, 0x08, 0x05, 0xf4, 0xf2, 0x09, 0x04, 0xff, 0xfd, 0xf9, 0xfd, 0x00, 0xff, 0xfb, 0x04, 0x06, 0xf6, 0xff, 0x03, 0xfd, 0xfc, 0xfb, + 0xfa, 0xfc, 0xfa, 0x02, 0x06, 0xfa, 0x06, 0x01, 0xf3, 0xfb, 0xfd, 0xfe, 0x04, 0x0f, 0x0a, 0x00, 0xff, 0xff, 0xfd, 0xfe, 0x06, 0x05, + 0x00, 0xfd, 0xfa, 0xfe, 0x04, 0x04, 0x00, 0xfe, 0xfe, 0x00, 0xfc, 0xf9, 0xfc, 0xfa, 0xfe, 0x04, 0x07, 0xff, 0xfc, 0x00, 0xfd, 0x04, + 0x0b, 0x0a, 0x03, 0xff, 0xff, 0x03, 0xfe, 0x00, 0x07, 0xfe, 0xfa, 0xfb, 0xfb, 0xfc, 0x00, 0x03, 0xfd, 0x02, 0x02, 0xfa, 0xfe, 0x01, + 0x03, 0x07, 0xfd, 0xfa, 0x00, 0x01, 0xfd, 0xff, 0x07, 0x00, 0x00, 0xff, 0xf5, 0xf9, 0xfd, 0xfd, 0xf9, 0x01, 0x04, 0xfc, 0xf7, 0xfc, + 0x02, 0xff, 0xfe, 0x02, 0x07, 0xfe, 0xff, 0x02, 0xfc, 0x00, 0x02, 0x00, 0xfe, 0x05, 0x07, 0x00, 0xf9, 0xf9, 0xff, 0x04, 0x04, 0xfd, + 0xf8, 0xfa, 0xfc, 0xff, 0x01, 0xf7, 0xfb, 0x06, 0x0b, 0xfa, 0xf9, 0x04, 0xfd, 0x04, 0x0b, 0x07, 0xfa, 0xff, 0x07, 0xff, 0x06, 0x06, + 0xff, 0xfa, 0x00, 0x05, 0x00, 0x01, 0x02, 0x00, 0xfc, 0xfb, 0x00, 0x06, 0xfe, 0xff, 0x04, 0x04, 0xfc, 0xfd, 0x03, 0x02, 0x02, 0xff, + 0xfb, 0xfa, 0x00, 0x02, 0xfa, 0x04, 0x02, 0xf9, 0x00, 0xfb, 0xf9, 0x00, 0xff, 0x01, 0x01, 0xfc, 0xf9, 0xfd, 0x07, 0x0b, 0x03, 0xf8, + 0xf6, 0xf9, 0xff, 0x03, 0x01, 0xfe, 0xff, 0x01, 0xfe, 0xff, 0x02, 0x01, 0xf8, 0xf8, 0x01, 0x08, 0x07, 0x02, 0x00, 0x03, 0x00, 0xff, + 0x05, 0x01, 0xfc, 0xfa, 0xfc, 0xfb, 0xfc, 0x00, 0x03, 0x01, 0xff, 0x00, 0xfc, 0xfd, 0xff, 0xfc, 0xfa, 0x04, 0x12, 0x01, 0xf9, 0x03, + 0x13, 0xfa, 0xf5, 0x04, 0xff, 0x04, 0x07, 0x02, 0x00, 0xfd, 0xf8, 0xf7, 0x03, 0x06, 0xfe, 0xf7, 0xfa, 0xff, 0x02, 0x01, 0x02, 0x02, + 0xfd, 0x00, 0x06, 0x06, 0xfb, 0xf9, 0xff, 0x07, 0x02, 0x00, 0x01, 0x00, 0xf6, 0xf8, 0x04, 0x00, 0x02, 0x01, 0xfb, 0x01, 0x05, 0x00, + 0xfd, 0xf9, 0xfe, 0x07, 0xfd, 0xfc, 0x02, 0x07, 0x05, 0x08, 0x0b, 0x03, 0xfb, 0xfd, 0x04, 0xf2, 0xfe, 0x0d, 0x01, 0xfe, 0xfe, 0xfe, + 0xf7, 0xfc, 0x03, 0x05, 0xff, 0xfb, 0xfc, 0xff, 0x04, 0x04, 0x00, 0x03, 0x01, 0xfd, 0xfd, 0xfc, 0x02, 0x06, 0xfc, 0xfd, 0x00, 0xff, + 0xf7, 0xfa, 0xfd, 0xfa, 0xfe, 0x02, 0x03, 0x00, 0xfc, 0xfc, 0xff, 0xfa, 0xf9, 0xfe, 0x03, 0x05, 0xff, 0xf8, 0xff, 0xfc, 0xfb, 0x00, + 0x08, 0x02, 0xfa, 0xfe, 0x02, 0x02, 0xfe, 0xfe, 0x02, 0xff, 0xf7, 0x04, 0x04, 0x01, 0x05, 0x01, 0xfa, 0xf8, 0x01, 0x02, 0x02, 0x06, + 0x04, 0xfc, 0xf8, 0xff, 0x00, 0x04, 0x07, 0x01, 0xfc, 0xfd, 0x02, 0x04, 0xff, 0xfc, 0x00, 0x05, 0x04, 0x01, 0x05, 0x06, 0x00, 0xf7, + 0xfa, 0x05, 0x09, 0xfc, 0x01, 0x08, 0x05, 0x00, 0xf6, 0xf5, 0xff, 0x0b, 0x03, 0xf4, 0xfa, 0x02, 0x04, 0x00, 0x07, 0xfc, 0xf1, 0xf9, + 0xfc, 0xfd, 0xff, 0x0a, 0x00, 0xf5, 0xf9, 0x04, 0x00, 0xf9, 0x03, 0xfb, 0xf6, 0xfb, 0x01, 0x01, 0x01, 0x03, 0xfe, 0xfd, 0xff, 0xfa, + 0xfb, 0xfd, 0xff, 0xfd, 0x05, 0x06, 0xf9, 0xf8, 0x02, 0x09, 0xfd, 0xfa, 0xfe, 0x04, 0x07, 0xff, 0xf9, 0x02, 0xfb, 0xfb, 0x04, 0x06, + 0x04, 0x02, 0x02, 0x05, 0xfe, 0xf8, 0xff, 0x01, 0x03, 0x05, 0x02, 0x00, 0x02, 0x05, 0xfc, 0xfe, 0x06, 0x05, 0x02, 0x01, 0x01, 0xf7, + 0xfb, 0x00, 0x00, 0x02, 0x06, 0x04, 0xf9, 0xfb, 0x05, 0x09, 0xf6, 0xff, 0x08, 0xfb, 0x03, 0x05, 0xfe, 0xf2, 0x01, 0x04, 0xf7, 0xff, + 0x05, 0x03, 0xfc, 0xfa, 0xfd, 0xfe, 0xf6, 0xf7, 0xfb, 0xfe, 0xfe, 0x02, 0x01, 0xf8, 0x03, 0x07, 0xfe, 0xf8, 0xfb, 0x02, 0x04, 0xfe, + 0xfd, 0xff, 0xfd, 0x06, 0x04, 0xf9, 0xf9, 0x05, 0x03, 0xf0, 0x01, 0x05, 0xff, 0x03, 0x05, 0x00, 0xfa, 0xfa, 0x00, 0x02, 0xfd, 0x08, + 0x07, 0x00, 0x06, 0x02, 0xfe, 0x00, 0x0a, 0x01, 0xfa, 0x04, 0xff, 0xf7, 0xf7, 0x05, 0x03, 0x01, 0x04, 0xfd, 0xfe, 0x02, 0x02, 0x01, + 0x07, 0x09, 0xfb, 0xf9, 0xff, 0x04, 0x00, 0x02, 0x06, 0x02, 0xf9, 0xfb, 0x04, 0x02, 0xff, 0xfb, 0xf8, 0xfd, 0x02, 0x02, 0xfd, 0xfb, + 0xfc, 0xfe, 0x03, 0x02, 0xfe, 0xfa, 0xfc, 0xfd, 0xfe, 0x01, 0x01, 0x04, 0x07, 0xfa, 0xf9, 0xfc, 0xfa, 0xf9, 0xfe, 0x05, 0x02, 0x01, + 0xfc, 0xf8, 0xff, 0x03, 0x02, 0x00, 0xf8, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x01, 0xfd, 0xfe, 0x03, 0x03, 0x01, 0xff, 0x00, 0x04, 0x08, + 0x04, 0xfc, 0xfc, 0x02, 0x03, 0xfb, 0xfa, 0xfe, 0x04, 0x06, 0x03, 0x02, 0x08, 0xfa, 0xfa, 0xff, 0xf6, 0xfa, 0x06, 0x0c, 0xfd, 0x01, + 0x08, 0x01, 0xf8, 0xfa, 0x00, 0x00, 0xfe, 0x01, 0x07, 0x02, 0xfd, 0xfd, 0x02, 0xfb, 0x02, 0x0a, 0xf9, 0xf9, 0xfd, 0xfc, 0x01, 0x04, + 0x01, 0xfd, 0xfc, 0x00, 0x01, 0xf7, 0xf9, 0x00, 0x03, 0x03, 0x00, 0xfe, 0x03, 0xfd, 0xfc, 0x01, 0x01, 0x02, 0x01, 0xfe, 0x02, 0x04, + 0x02, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0x01, 0x00, 0xfa, 0xfe, 0x02, 0x02, 0xfe, 0x01, 0x05, 0x06, 0xff, 0x02, 0x06, 0xfe, 0x04, 0x04, + 0x00, 0x01, 0x03, 0xfc, 0xf3, 0xfd, 0x07, 0x06, 0xff, 0xf9, 0xfb, 0x00, 0x00, 0xfe, 0xfb, 0xfb, 0x00, 0x00, 0xfe, 0x01, 0x05, 0x00, + 0xf8, 0x02, 0x04, 0xff, 0xfc, 0xff, 0x00, 0xff, 0x01, 0x04, 0xfd, 0xf3, 0x03, 0xfa, 0xf5, 0x07, 0x02, 0xfc, 0xfe, 0x06, 0x02, 0xfc, + 0xf9, 0x02, 0xff, 0xfa, 0xfe, 0x02, 0x04, 0x04, 0x04, 0xff, 0xfb, 0xfb, 0xfb, 0xfe, 0x03, 0x06, 0xfe, 0xfb, 0xfe, 0xff, 0xff, 0x04, + 0x09, 0xf8, 0xf4, 0xfc, 0x02, 0xff, 0xfe, 0x02, 0x06, 0x07, 0x06, 0x04, 0x01, 0xfd, 0xfc, 0x00, 0x08, 0x08, 0x05, 0x03, 0x05, 0xfe, + 0xf0, 0xfb, 0x05, 0x07, 0x04, 0x02, 0xfc, 0xf5, 0xfb, 0x01, 0x00, 0xfc, 0x05, 0x01, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, 0x05, 0x0b, 0x06, + 0xf6, 0xfe, 0x01, 0xfd, 0xfe, 0xfd, 0xfd, 0x00, 0xfc, 0xfb, 0xfd, 0xfc, 0x01, 0xfe, 0xfa, 0x04, 0x02, 0xfa, 0xfa, 0x01, 0x02, 0xff, + 0x00, 0xfc, 0x01, 0x0a, 0x02, 0xfc, 0xf9, 0xf8, 0x00, 0x01, 0x00, 0x06, 0x03, 0xff, 0xff, 0xfe, 0xfc, 0xfc, 0xfe, 0xfd, 0xfc, 0xfd, + 0x00, 0x06, 0x08, 0x07, 0x04, 0x03, 0x03, 0x02, 0xfd, 0xfd, 0x01, 0xfd, 0x02, 0x06, 0x04, 0xfc, 0xfe, 0x03, 0x02, 0x00, 0x01, 0x02, + 0xf9, 0x04, 0x09, 0xfe, 0xfd, 0xfd, 0xff, 0x03, 0x02, 0xfe, 0xfc, 0xfc, 0x00, 0x04, 0x06, 0x03, 0xfe, 0xfc, 0xfb, 0xff, 0xfc, 0xf4, + 0xf7, 0xf9, 0xfe, 0x06, 0x00, 0xff, 0x02, 0x00, 0xff, 0xfa, 0xf6, 0x0a, 0x05, 0xf7, 0xf7, 0xfd, 0xfe, 0xfb, 0xff, 0x01, 0x02, 0x02, + 0xfb, 0xfd, 0x00, 0xfd, 0x05, 0x04, 0xfe, 0x00, 0xfc, 0xf9, 0xfc, 0xff, 0xfb, 0xfc, 0x0b, 0x01, 0xff, 0x05, 0xfe, 0xf9, 0xfa, 0xfe, + 0x01, 0x01, 0xfe, 0xff, 0xfb, 0xfe, 0x04, 0x03, 0x03, 0x06, 0x08, 0x04, 0xfd, 0xfd, 0x0d, 0x06, 0xfc, 0xfd, 0x03, 0x06, 0x07, 0x09, + 0x01, 0xfc, 0xfc, 0xfc, 0xf8, 0xfa, 0x03, 0x02, 0x01, 0x01, 0x01, 0xf4, 0xf9, 0x08, 0x02, 0x02, 0x01, 0xfb, 0xf6, 0xfb, 0x04, 0x07, + 0x07, 0xff, 0xf8, 0xfd, 0xfe, 0xfc, 0xfc, 0xf8, 0xfa, 0xfe, 0xff, 0xf7, 0xfc, 0x08, 0xf8, 0xfa, 0x02, 0xff, 0x02, 0xff, 0xfd, 0x06, + 0x02, 0xfe, 0xff, 0xff, 0x03, 0x03, 0xfb, 0xfd, 0x02, 0x01, 0xf7, 0x00, 0x06, 0xff, 0x01, 0xfd, 0xfb, 0x01, 0x00, 0x03, 0x03, 0xf6, + 0xfd, 0x07, 0x05, 0x01, 0xfa, 0xf8, 0xfd, 0x0b, 0x09, 0xfc, 0x00, 0xfc, 0xfc, 0x05, 0x08, 0x01, 0xfd, 0x06, 0x00, 0xfa, 0xfa, 0x02, + 0xff, 0x01, 0x0b, 0x06, 0xfc, 0xf6, 0xf9, 0x01, 0x05, 0x05, 0x0a, 0x01, 0xfc, 0x02, 0xfb, 0xfe, 0x05, 0x04, 0x00, 0xff, 0xff, 0xfa, + 0xfb, 0x00, 0x03, 0x03, 0xfb, 0xf4, 0xfd, 0xff, 0xfe, 0xfe, 0xfe, 0x00, 0xff, 0xf9, 0x04, 0x07, 0xff, 0xf8, 0x01, 0x05, 0xfb, 0xff, + 0x00, 0xff, 0x02, 0xf9, 0xfa, 0x05, 0x02, 0xfe, 0xff, 0x04, 0x09, 0x02, 0xfa, 0xff, 0xff, 0xfa, 0xf7, 0xff, 0x04, 0x06, 0x04, 0x0b, + 0xff, 0xf2, 0xfe, 0x07, 0x03, 0xfa, 0xfb, 0x00, 0x00, 0xf8, 0xff, 0x05, 0x03, 0xfc, 0xfe, 0x01, 0xff, 0x02, 0xfe, 0xfd, 0x05, 0x01, + 0x01, 0x04, 0x00, 0xfc, 0xfd, 0x00, 0xfc, 0xff, 0x06, 0x0b, 0xfe, 0xfa, 0x01, 0x05, 0xfd, 0xfb, 0x04, 0xfa, 0xf8, 0x00, 0x08, 0x04, + 0xff, 0x00, 0x04, 0x00, 0xfc, 0x00, 0x01, 0xfa, 0xf6, 0x02, 0x06, 0x04, 0x00, 0xff, 0xf8, 0xf7, 0x00, 0x01, 0xff, 0xff, 0x05, 0x04, + 0x02, 0x00, 0x01, 0xfe, 0xfb, 0xfc, 0x02, 0x00, 0xfb, 0x02, 0x02, 0xff, 0x00, 0x07, 0x08, 0x01, 0xfb, 0xf4, 0xfb, 0x06, 0x02, 0xff, + 0x00, 0x03, 0x06, 0x01, 0xfa, 0xfb, 0xf8, 0xff, 0x0a, 0xfd, 0xfc, 0xfd, 0xfa, 0xfd, 0xff, 0xfd, 0xf9, 0xff, 0x03, 0x01, 0xfe, 0xfc, + 0xfa, 0xfa, 0x00, 0x02, 0x02, 0x05, 0x01, 0xfa, 0xf6, 0x04, 0xff, 0xf8, 0x03, 0x0a, 0x09, 0x02, 0x00, 0xff, 0xfc, 0xfa, 0xfc, 0x02, + 0x03, 0xfd, 0xfa, 0xfb, 0x00, 0x05, 0x06, 0x01, 0xfb, 0x03, 0x04, 0xfd, 0xfc, 0xfd, 0x01, 0x07, 0x0b, 0x06, 0x03, 0x05, 0xfd, 0xfa, + 0xff, 0x02, 0x06, 0x05, 0x01, 0x04, 0xfc, 0xf5, 0xf9, 0x05, 0x04, 0xfc, 0x01, 0xfd, 0xfb, 0x01, 0x0f, 0x0a, 0xff, 0xfc, 0x06, 0xfe, + 0xf1, 0xfc, 0xff, 0x01, 0x05, 0x02, 0x00, 0x00, 0xfc, 0xfb, 0xfd, 0xfc, 0xf2, 0xfa, 0x02, 0x01, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, + 0xfe, 0xfe, 0xfd, 0xff, 0x02, 0xff, 0xfd, 0xfc, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xff, 0xfe, 0x02, 0x06, 0xff, 0x00, 0xff, + 0xfc, 0xfe, 0xfd, 0x00, 0x09, 0x01, 0x04, 0x0a, 0xff, 0xfa, 0xfd, 0x04, 0x04, 0x06, 0x04, 0xfc, 0xf9, 0xfe, 0x03, 0x01, 0xfb, 0xfc, + 0x04, 0x03, 0x04, 0x07, 0x05, 0x00, 0xfd, 0xff, 0x01, 0x08, 0x04, 0xf9, 0xfe, 0x0b, 0x0c, 0xf7, 0xfc, 0xff, 0xfc, 0x03, 0x02, 0xfd, + 0xfd, 0xff, 0x01, 0x02, 0xff, 0xfb, 0xf9, 0xfb, 0x05, 0x03, 0xfd, 0xfe, 0x05, 0x07, 0x03, 0x01, 0xf9, 0xf8, 0xff, 0x00, 0xff, 0xfd, + 0xfd, 0xfa, 0xfc, 0x00, 0x05, 0xfc, 0xfa, 0x02, 0xf9, 0xf9, 0xfe, 0xfe, 0x02, 0x02, 0xfd, 0x01, 0xfd, 0xf7, 0xf7, 0x04, 0xff, 0xf6, + 0x00, 0x02, 0x04, 0x05, 0xfd, 0x02, 0x03, 0xf8, 0xfc, 0xfd, 0xfd, 0x03, 0x03, 0x00, 0x01, 0x06, 0xff, 0xfb, 0x03, 0x05, 0x04, 0xff, + 0xfa, 0xf8, 0xfd, 0x04, 0x06, 0x04, 0x00, 0xfb, 0x00, 0x01, 0xff, 0xff, 0x05, 0x03, 0xf9, 0x01, 0x03, 0xff, 0xff, 0xfa, 0xff, 0x07, + 0x00, 0xfd, 0xfd, 0xfd, 0x03, 0x01, 0xfe, 0x05, 0x04, 0x03, 0x03, 0x01, 0xf5, 0xf4, 0x03, 0x09, 0x03, 0xfe, 0x08, 0x02, 0xfa, 0xfb, + 0x07, 0x05, 0xfe, 0xfc, 0x00, 0xfe, 0xfa, 0xfc, 0x02, 0x00, 0xf9, 0x02, 0xfd, 0xf6, 0xfb, 0xf6, 0xfd, 0x05, 0xf9, 0xfc, 0x02, 0x03, + 0x0c, 0x04, 0xfa, 0xfd, 0xf7, 0xf9, 0x02, 0x07, 0x06, 0x05, 0x06, 0xfc, 0xfc, 0x00, 0xf9, 0x03, 0x01, 0xf6, 0x02, 0xf9, 0xf5, 0x05, + 0x05, 0x04, 0x02, 0xfd, 0xf6, 0xfa, 0x05, 0x00, 0x02, 0x06, 0x04, 0xfe, 0x00, 0x03, 0xfe, 0xfc, 0xff, 0x03, 0xfb, 0x00, 0x03, 0xfa, + 0x00, 0x08, 0x0a, 0x01, 0xfc, 0xf8, 0xf6, 0xfa, 0x00, 0x02, 0x01, 0x08, 0x07, 0x01, 0x04, 0x06, 0x06, 0x02, 0x02, 0xff, 0xfc, 0xfc, + 0x01, 0x03, 0x02, 0x00, 0x01, 0x00, 0xfd, 0xfa, 0xfd, 0xff, 0xfd, 0xfb, 0xff, 0x04, 0xfe, 0x01, 0xff, 0xf6, 0xff, 0x00, 0xfd, 0x03, + 0x05, 0x01, 0xfe, 0x06, 0x03, 0xfd, 0xfc, 0xf4, 0xfa, 0x05, 0x01, 0xfc, 0xfe, 0x04, 0xff, 0xfb, 0xfc, 0xff, 0x03, 0x03, 0xff, 0xfb, + 0xf8, 0xfb, 0x02, 0x00, 0x01, 0x04, 0x02, 0xfb, 0xf9, 0xfd, 0xff, 0xff, 0xfd, 0xfd, 0xfe, 0x04, 0x05, 0xfb, 0xfc, 0x05, 0x08, 0xfb, + 0xf7, 0xfc, 0x03, 0x05, 0x04, 0x02, 0xfe, 0x01, 0x00, 0xfd, 0x09, 0x05, 0xfd, 0xff, 0x02, 0xff, 0xfb, 0x02, 0x04, 0x09, 0x0f, 0x03, + 0xfc, 0xfe, 0xff, 0xfd, 0xfc, 0xfe, 0xff, 0x04, 0x02, 0xf9, 0x05, 0x04, 0xfd, 0xff, 0xfe, 0xfc, 0xfb, 0x00, 0x00, 0x00, 0x01, 0x06, + 0xfd, 0xf6, 0x02, 0x03, 0xfd, 0xfb, 0x05, 0x00, 0xfc, 0x00, 0x03, 0xff, 0xfb, 0xfb, 0xfd, 0xfa, 0xf6, 0xf8, 0xfe, 0x05, 0x07, 0x07, + 0x00, 0xf8, 0xf8, 0xfa, 0xfe, 0x02, 0x00, 0x01, 0x03, 0x01, 0xf5, 0xf8, 0x06, 0x09, 0x06, 0x00, 0xfb, 0xfe, 0xfe, 0xfc, 0xfc, 0x05, + 0x05, 0xfe, 0x00, 0xff, 0xfc, 0xfa, 0x03, 0x05, 0x03, 0x02, 0x04, 0x00, 0xfb, 0x08, 0x06, 0xfe, 0xfc, 0xfc, 0xfd, 0x00, 0x07, 0x01, + 0x02, 0x07, 0xfb, 0xfd, 0x00, 0xfa, 0xfd, 0x02, 0x03, 0xfa, 0x01, 0x02, 0xfa, 0xfe, 0x07, 0x0a, 0x05, 0xfc, 0xfc, 0x01, 0xfe, 0x02, + 0xff, 0xf7, 0xfd, 0x00, 0x01, 0x03, 0x04, 0x02, 0xfd, 0xfb, 0xfa, 0xfc, 0xff, 0xff, 0xff, 0x02, 0x07, 0x03, 0xfa, 0xf4, 0xfe, 0xff, + 0xff, 0x04, 0x06, 0x01, 0xfe, 0x05, 0x01, 0xff, 0x00, 0xff, 0xf6, 0xf5, 0x00, 0xff, 0x01, 0x05, 0xff, 0x01, 0xfe, 0xfa, 0x07, 0x06, + 0xfd, 0xf8, 0xf9, 0xff, 0x05, 0x02, 0x06, 0x05, 0xfc, 0xf9, 0xf9, 0xfa, 0xfc, 0x09, 0x07, 0xff, 0x0c, 0xfe, 0xf3, 0xfa, 0x01, 0x01, + 0x00, 0x04, 0xff, 0xfc, 0xff, 0x02, 0x03, 0x05, 0x06, 0xfe, 0xfb, 0xfe, 0x05, 0xfb, 0xf8, 0x00, 0x01, 0x05, 0x05, 0x00, 0xfa, 0xfb, + 0x01, 0x00, 0x03, 0x03, 0xfe, 0xf8, 0x00, 0x04, 0xf8, 0x03, 0x00, 0xf7, 0x00, 0x01, 0xfe, 0xfd, 0x05, 0x03, 0xff, 0x00, 0x01, 0x00, + 0xff, 0x04, 0x04, 0xff, 0xf7, 0xff, 0xff, 0x01, 0x0b, 0x08, 0xfc, 0xf5, 0x04, 0xfb, 0xf7, 0x02, 0xfb, 0xfc, 0x02, 0x01, 0x00, 0xfa, + 0xf8, 0x06, 0x09, 0x05, 0x02, 0xf8, 0xf5, 0xfc, 0x05, 0x06, 0x01, 0xff, 0xfd, 0x01, 0x00, 0xf6, 0xfe, 0x04, 0x03, 0xfd, 0xfe, 0xfa, + 0xf5, 0x02, 0xff, 0xfb, 0x05, 0xfe, 0xfc, 0x00, 0xfe, 0x01, 0x02, 0xfe, 0x02, 0x04, 0x03, 0x02, 0xf6, 0xf3, 0xfb, 0x02, 0x03, 0x03, + 0x04, 0xfb, 0xff, 0x02, 0xf8, 0xfe, 0x05, 0x03, 0xfa, 0x00, 0x08, 0x06, 0x00, 0xff, 0x01, 0x00, 0x03, 0x02, 0x00, 0xff, 0x04, 0x04, + 0xfd, 0xfb, 0x06, 0x0e, 0xff, 0x03, 0x02, 0xf6, 0xf9, 0xfe, 0x01, 0x02, 0x03, 0xff, 0xfb, 0xfe, 0xfb, 0xfc, 0x02, 0x02, 0x04, 0x02, + 0xfc, 0xf7, 0xf7, 0xfd, 0x06, 0x03, 0xff, 0x01, 0xf9, 0xfe, 0x04, 0xf9, 0xfe, 0x02, 0x01, 0xfe, 0x04, 0x01, 0xf4, 0xfa, 0x01, 0x01, + 0xf9, 0x01, 0x01, 0xf9, 0xfe, 0x02, 0x01, 0xfd, 0x06, 0xff, 0xf7, 0x03, 0x04, 0xfd, 0xf7, 0xfd, 0x00, 0x01, 0x02, 0x07, 0x02, 0xfc, + 0x04, 0xfd, 0xf8, 0xfd, 0x02, 0x00, 0x01, 0x06, 0x06, 0xfd, 0xf8, 0x04, 0x05, 0x02, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x07, 0x0a, + 0xfe, 0xfe, 0x04, 0x03, 0xfd, 0xfe, 0x00, 0xf7, 0x06, 0x08, 0xf9, 0xf9, 0x00, 0x06, 0x03, 0x08, 0x04, 0xfd, 0xfd, 0x04, 0xfe, 0xf4, + 0xfe, 0x04, 0x06, 0x04, 0xfa, 0xf9, 0xfe, 0xfd, 0xfc, 0xfe, 0x03, 0x02, 0xff, 0xfb, 0xfd, 0xf4, 0xf9, 0x04, 0xff, 0xf9, 0xfc, 0x04, + 0xf2, 0xf7, 0x08, 0x0c, 0xfd, 0xf8, 0x01, 0x02, 0xf8, 0xf8, 0x05, 0xfb, 0xfe, 0x04, 0xfd, 0x02, 0x03, 0xfe, 0x02, 0xfc, 0xf9, 0xfd, + 0x06, 0x04, 0xff, 0x02, 0x03, 0xfd, 0xf7, 0xff, 0x04, 0x03, 0x00, 0xfd, 0xfe, 0x01, 0x01, 0x03, 0x04, 0x03, 0x05, 0x04, 0xfc, 0xf3, + 0x00, 0x03, 0xfe, 0x05, 0x01, 0x02, 0x07, 0x03, 0x01, 0x00, 0xfa, 0xff, 0x03, 0x02, 0xfe, 0x02, 0x05, 0x02, 0xfb, 0xff, 0x06, 0x02, + 0xfa, 0xfb, 0x02, 0xfb, 0x00, 0x03, 0xfc, 0x02, 0x03, 0xfd, 0xf7, 0xfa, 0xf8, 0xf3, 0x03, 0xfd, 0xf9, 0x05, 0x04, 0x01, 0x00, 0x00, + 0xf9, 0xf9, 0x01, 0xfe, 0xfa, 0xfa, 0xfe, 0x03, 0x01, 0xfc, 0xfe, 0x01, 0x01, 0xfd, 0xfc, 0xfc, 0xfe, 0x00, 0x07, 0x07, 0x00, 0xfe, + 0xfb, 0xfe, 0x03, 0x00, 0xfb, 0xfa, 0x00, 0x06, 0x0b, 0x0a, 0x02, 0x02, 0xfe, 0xf5, 0x01, 0x04, 0xfb, 0xf6, 0x01, 0x09, 0x05, 0x03, + 0xfa, 0xf6, 0xfd, 0x03, 0xfd, 0xf6, 0xfc, 0x02, 0x02, 0xff, 0x04, 0x03, 0x01, 0x02, 0x03, 0x00, 0xfd, 0xfd, 0x03, 0x05, 0x00, 0x04, + 0x02, 0xfd, 0xfe, 0x04, 0x05, 0x01, 0xfe, 0xfa, 0xfd, 0x04, 0xf9, 0xfa, 0x05, 0x0a, 0x03, 0xfc, 0xfa, 0xfe, 0xfb, 0xfa, 0x01, 0x04, + 0x00, 0xfb, 0xfd, 0xfe, 0x00, 0x02, 0x04, 0xfe, 0xfc, 0x01, 0xfe, 0xfc, 0xfd, 0x01, 0x02, 0x03, 0x02, 0x00, 0xfb, 0xfa, 0xfe, 0xff, + 0x01, 0x04, 0x02, 0x03, 0x07, 0x08, 0xfa, 0xfa, 0xfd, 0xf7, 0xfe, 0x05, 0x04, 0xfd, 0xff, 0x05, 0x06, 0xf9, 0xf8, 0xfd, 0xfb, 0xfd, + 0x00, 0xff, 0xfa, 0xfe, 0x02, 0xff, 0x06, 0x03, 0xfc, 0xfb, 0x02, 0x03, 0xfd, 0xf9, 0x04, 0x0c, 0x05, 0x06, 0x01, 0xf9, 0xf9, 0xfe, + 0x03, 0x05, 0x01, 0x00, 0x02, 0x04, 0x02, 0xfe, 0xfc, 0xff, 0x04, 0x03, 0xfd, 0xfe, 0xff, 0x01, 0x03, 0x07, 0x02, 0xfc, 0xfb, 0x04, + 0x04, 0xfc, 0x04, 0x04, 0xfc, 0xf6, 0x03, 0x07, 0xfd, 0xfa, 0xf8, 0xfb, 0xfe, 0x02, 0xfc, 0xf8, 0x00, 0x03, 0x03, 0x04, 0x03, 0x08, + 0x06, 0xfd, 0x01, 0x00, 0xfc, 0xfb, 0xfb, 0xff, 0x05, 0x06, 0x01, 0xfb, 0xf9, 0xff, 0x01, 0xfe, 0xfb, 0xfc, 0xfb, 0xf8, 0xfc, 0xfe, + 0xff, 0x01, 0x01, 0x00, 0xff, 0xfe, 0xf8, 0xf9, 0x04, 0x05, 0x07, 0x05, 0xff, 0xfd, 0x01, 0x02, 0xf9, 0xf7, 0xfc, 0x03, 0x06, 0x07, + 0x04, 0xfc, 0x03, 0x03, 0xfb, 0xfa, 0xfc, 0x02, 0x07, 0xfa, 0xfc, 0x08, 0x08, 0x05, 0x01, 0xfe, 0xfd, 0xf9, 0xfb, 0x03, 0x02, 0x01, + 0x03, 0x03, 0xff, 0x00, 0x04, 0x05, 0xfb, 0xf6, 0xfc, 0x02, 0x04, 0x03, 0x03, 0x03, 0x01, 0xfd, 0xf5, 0xfc, 0x04, 0xff, 0xff, 0xff, + 0xfc, 0xfc, 0xfc, 0x01, 0x07, 0x06, 0xfd, 0xf8, 0xfe, 0xfe, 0xfb, 0xfc, 0x02, 0x05, 0x00, 0xf9, 0xfd, 0x03, 0x02, 0xf7, 0xf8, 0x00, + 0x04, 0xf9, 0xfc, 0x01, 0x00, 0x03, 0x03, 0xff, 0xfa, 0xf8, 0xfd, 0x03, 0xff, 0xff, 0x04, 0x08, 0xfc, 0xfd, 0x05, 0x00, 0xfa, 0xf9, + 0xfd, 0xf8, 0xfc, 0x01, 0x00, 0x04, 0x05, 0x05, 0x06, 0x02, 0xfc, 0xfa, 0x00, 0x05, 0x05, 0x01, 0x03, 0x02, 0x01, 0x0e, 0x05, 0xf8, + 0xf6, 0x04, 0x03, 0xfa, 0xfb, 0x03, 0x07, 0x04, 0x03, 0x01, 0xfe, 0xf7, 0xf7, 0xf9, 0xff, 0x07, 0x08, 0x00, 0xfc, 0x08, 0x04, 0xfc, + 0xfe, 0xfc, 0xff, 0x02, 0xfd, 0xfb, 0x00, 0x06, 0x06, 0x01, 0xfd, 0xfb, 0xfb, 0xf6, 0xf3, 0xfd, 0xff, 0xfe, 0xff, 0x04, 0x06, 0x03, + 0xfb, 0xfb, 0xf9, 0xf9, 0x08, 0xff, 0xf6, 0xff, 0x0a, 0x09, 0x01, 0xfb, 0xfd, 0xfe, 0xfa, 0xff, 0xfa, 0xfa, 0x05, 0xff, 0xfc, 0x01, + 0x09, 0xfc, 0xf7, 0xfe, 0xfe, 0xfb, 0xfe, 0x09, 0x06, 0x06, 0x08, 0x05, 0xff, 0xfc, 0xfc, 0xfe, 0x00, 0x01, 0xfe, 0x00, 0x05, 0x07, + 0x03, 0x05, 0x04, 0xfd, 0xfa, 0xfc, 0xfe, 0xfd, 0x05, 0x07, 0x03, 0x03, 0x03, 0x00, 0xfb, 0xfe, 0x00, 0xff, 0xfa, 0xff, 0xff, 0xfd, + 0x09, 0x03, 0xfb, 0xfb, 0xfe, 0xfc, 0xfa, 0xfe, 0xff, 0xfc, 0xfb, 0x08, 0x03, 0xfb, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfe, + 0x03, 0x05, 0x04, 0x02, 0xfb, 0xfb, 0xff, 0xff, 0xfc, 0xfd, 0x01, 0x01, 0x03, 0x01, 0xfa, 0xf9, 0xfe, 0x04, 0x06, 0x02, 0xfb, 0xf5, + 0xf7, 0xff, 0x04, 0x00, 0xfe, 0xfd, 0xff, 0x03, 0xff, 0xf9, 0xf9, 0xfd, 0x01, 0x03, 0x04, 0x08, 0x07, 0x03, 0x03, 0x00, 0xfc, 0xfa, + 0xfa, 0xfd, 0x02, 0x01, 0x06, 0x03, 0xfb, 0x01, 0x05, 0x01, 0xf9, 0xff, 0x01, 0xfe, 0xfd, 0x00, 0x04, 0x05, 0x07, 0x05, 0xff, 0xf9, + 0x00, 0xff, 0xfb, 0xff, 0x06, 0x07, 0x01, 0xfc, 0xfe, 0x01, 0xfd, 0xf7, 0xf8, 0xfe, 0xfe, 0x03, 0x07, 0x07, 0x00, 0x05, 0x08, 0xfd, + 0xf3, 0xf6, 0x00, 0x05, 0x01, 0xfe, 0x04, 0x04, 0x04, 0x01, 0xf9, 0xf8, 0xfb, 0xfd, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xfd, 0xfc, 0x01, + 0xfd, 0xf9, 0xfb, 0x01, 0x01, 0xff, 0x00, 0x07, 0x04, 0xfc, 0x03, 0xff, 0xfb, 0xfd, 0x01, 0x04, 0x02, 0xfd, 0x00, 0x01, 0xff, 0xfe, + 0x00, 0x01, 0x00, 0xfb, 0x05, 0x0a, 0xfd, 0xf8, 0xfe, 0x05, 0xfd, 0x00, 0x03, 0x03, 0x05, 0x04, 0x00, 0xfb, 0xf8, 0xfc, 0x02, 0x05, + 0xff, 0xfd, 0x03, 0xff, 0xfd, 0xfe, 0xfa, 0xff, 0x04, 0x02, 0xfe, 0xff, 0x02, 0x03, 0x01, 0x02, 0x02, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, + 0x05, 0x07, 0xff, 0x00, 0x03, 0x03, 0xff, 0xf9, 0xf9, 0xfe, 0xf9, 0xfa, 0x01, 0x06, 0x07, 0x02, 0xfd, 0xf9, 0x02, 0x03, 0xf9, 0xfe, + 0x04, 0x04, 0x00, 0x00, 0x02, 0x02, 0xf9, 0xfb, 0xff, 0xfd, 0xfc, 0xff, 0x02, 0x00, 0x02, 0x04, 0x02, 0xfb, 0xfc, 0xfe, 0xfc, 0xfe, + 0xfe, 0xfe, 0x05, 0x03, 0x00, 0x01, 0xff, 0xfe, 0xfc, 0xfa, 0x02, 0x04, 0xfe, 0xf9, 0x00, 0x04, 0xff, 0xfe, 0xfd, 0xfd, 0xfc, 0x02, + 0x05, 0x02, 0xfd, 0x02, 0x03, 0xfd, 0x01, 0x01, 0xff, 0x03, 0x02, 0x01, 0xff, 0xf8, 0xfc, 0x00, 0xfe, 0xff, 0x01, 0x02, 0x05, 0xff, + 0xfd, 0x00, 0x01, 0x02, 0x01, 0xfd, 0xfe, 0xfe, 0xfe, 0x01, 0x01, 0x00, 0xff, 0xfb, 0xfc, 0xff, 0x00, 0x02, 0x00, 0xfe, 0x05, 0x03, + 0xff, 0xfe, 0xfd, 0xff, 0x01, 0x01, 0xff, 0xfd, 0xfd, 0x07, 0x02, 0xfd, 0x01, 0xfc, 0xfa, 0xfb, 0xfd, 0x00, 0xff, 0xfe, 0x02, 0x05, + 0x04, 0x00, 0xfd, 0xfa, 0xfb, 0xff, 0x04, 0x06, 0x04, 0xfc, 0xfc, 0x02, 0x05, 0xff, 0xf9, 0xfa, 0x01, 0xfc, 0xfa, 0x00, 0x06, 0x00, + 0xfb, 0x00, 0xff, 0xff, 0xff, 0xf9, 0xfd, 0x03, 0x02, 0xfe, 0xfd, 0x00, 0x03, 0x06, 0x04, 0xfd, 0xfc, 0xfd, 0x03, 0x08, 0x08, 0xfb, + 0xf6, 0x07, 0x01, 0xf8, 0xf9, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0x04, 0xff, 0xfd, 0x03, 0x03, 0x01, 0xfe, 0xfc, 0x02, 0x03, + 0x01, 0x01, 0x04, 0x06, 0x04, 0xfb, 0xfc, 0x02, 0x03, 0xfb, 0xf9, 0xff, 0x01, 0x02, 0x04, 0x05, 0xf9, 0xfa, 0x02, 0x03, 0xff, 0xfe, + 0x02, 0x03, 0xff, 0xfe, 0x01, 0xff, 0xf9, 0xfa, 0x05, 0xfe, 0xfa, 0xff, 0x04, 0x04, 0x03, 0x02, 0xfd, 0xfe, 0x00, 0xff, 0xf9, 0xfd, + 0x04, 0xfe, 0xfe, 0x01, 0x01, 0x06, 0x02, 0xfc, 0xff, 0x00, 0x01, 0x01, 0xfb, 0xff, 0x05, 0x02, 0xfd, 0xfb, 0xfe, 0x03, 0x06, 0xff, + 0xf5, 0xf7, 0x02, 0x06, 0x00, 0x02, 0x02, 0xfe, 0xf9, 0x01, 0x05, 0x00, 0xfe, 0xfc, 0xfd, 0xfd, 0x01, 0xff, 0xfc, 0x05, 0x07, 0x02, + 0xfb, 0xfc, 0xff, 0x00, 0xfe, 0x02, 0x03, 0x00, 0x03, 0x00, 0xfd, 0x00, 0xfe, 0xfc, 0xfd, 0x03, 0x06, 0x00, 0xf9, 0xff, 0x02, 0x01, + 0xff, 0xff, 0x00, 0x01, 0x03, 0xf9, 0xf8, 0x02, 0x03, 0x03, 0x03, 0x02, 0x02, 0x00, 0xfb, 0xfb, 0xfc, 0x00, 0x03, 0x01, 0xfc, 0xfc, + 0x02, 0xfc, 0xf9, 0xfc, 0x03, 0xfe, 0xfc, 0x01, 0x06, 0x00, 0xfb, 0x04, 0xff, 0xff, 0x04, 0xfd, 0xfd, 0x00, 0x00, 0xfd, 0xfc, 0x02, + 0x09, 0x02, 0xf7, 0xf6, 0x00, 0x01, 0xff, 0x00, 0x00, 0x02, 0x02, 0x01, 0xfc, 0xfb, 0x01, 0x05, 0x03, 0x00, 0x00, 0x03, 0x01, 0xfd, + 0x02, 0x04, 0x03, 0x00, 0xfc, 0xfb, 0xfe, 0x03, 0x00, 0x00, 0x01, 0xfd, 0xfe, 0xfe, 0xfd, 0xfd, 0xff, 0x03, 0x05, 0x01, 0xfc, 0xfb, + 0x00, 0xfc, 0xfd, 0x07, 0x04, 0x01, 0x00, 0x03, 0xfd, 0xf9, 0xfb, 0x02, 0xfc, 0xf8, 0xfe, 0x03, 0x03, 0x00, 0xfe, 0x01, 0xff, 0xfa, + 0xff, 0x01, 0x00, 0xfe, 0x01, 0xff, 0xfc, 0xff, 0x01, 0xff, 0xfd, 0x02, 0xfe, 0xfa, 0xff, 0x05, 0x04, 0xff, 0xfc, 0xfd, 0x01, 0x00, + 0xff, 0xfd, 0xfd, 0x01, 0x08, 0x06, 0xfe, 0x00, 0x02, 0x02, 0xff, 0x01, 0x03, 0x02, 0x01, 0xff, 0xff, 0xff, 0xfc, 0xff, 0x02, 0x01, + 0xfc, 0xfe, 0x02, 0x00, 0xfd, 0xfd, 0xff, 0x00, 0xfe, 0xfc, 0xfa, 0x03, 0x04, 0xff, 0xfe, 0x02, 0x01, 0xfc, 0x00, 0x04, 0x03, 0xfc, + 0xfd, 0xff, 0x00, 0x00, 0x02, 0x00, 0xfc, 0xfa, 0xfd, 0xfd, 0xf8, 0x03, 0x06, 0x00, 0x00, 0x02, 0xfe, 0xf9, 0xfe, 0x01, 0x01, 0xff, + 0xfc, 0x00, 0x06, 0x01, 0xfd, 0xfb, 0xfa, 0x05, 0x02, 0xfa, 0xff, 0x01, 0x03, 0x03, 0xff, 0xfe, 0xfe, 0xfd, 0x00, 0xfd, 0xfc, 0x03, + 0x06, 0x04, 0x01, 0x09, 0x03, 0xfb, 0xfd, 0xfe, 0xff, 0x02, 0x06, 0x01, 0xff, 0x03, 0x00, 0xfe, 0xff, 0x03, 0x00, 0xfe, 0xff, 0xfa, + 0xfe, 0x02, 0xff, 0xfe, 0x01, 0x01, 0xfe, 0xfe, 0x03, 0x05, 0xf7, 0xfa, 0x02, 0x04, 0xfe, 0x00, 0x02, 0xf8, 0xf8, 0xfe, 0x03, 0xff, + 0x02, 0x03, 0xfc, 0xf7, 0xfa, 0xff, 0xfc, 0xff, 0x03, 0x03, 0x02, 0xf9, 0xf4, 0xfb, 0x01, 0x01, 0xff, 0x03, 0xfe, 0xfe, 0x05, 0x00, + 0xff, 0x00, 0xfd, 0x01, 0x02, 0xfe, 0xfd, 0xfb, 0xfe, 0x06, 0x08, 0x00, 0xf9, 0xfd, 0xfd, 0x00, 0x00, 0xfb, 0xfe, 0x07, 0x0c, 0x03, + 0xfd, 0xfd, 0xfc, 0xfc, 0xfe, 0x03, 0x06, 0x09, 0x09, 0x05, 0x05, 0x04, 0xff, 0xfc, 0xf9, 0xfb, 0x01, 0x01, 0xff, 0x00, 0x04, 0x01, + 0xfe, 0xfd, 0xff, 0xf8, 0xf7, 0x01, 0x04, 0x02, 0x00, 0x05, 0xff, 0xfc, 0xfe, 0xfb, 0xfa, 0xfc, 0x00, 0x06, 0x03, 0x00, 0x03, 0x03, + 0xfd, 0xfa, 0xff, 0xfc, 0xf8, 0xf9, 0xfc, 0xfe, 0x00, 0xff, 0x00, 0x00, 0xfe, 0x00, 0xff, 0xfa, 0xf7, 0x01, 0x01, 0xfd, 0x05, 0x05, + 0x04, 0x03, 0xfb, 0xfd, 0x01, 0xfd, 0x03, 0x03, 0xff, 0x02, 0xfe, 0xfe, 0x01, 0x00, 0xfd, 0xfd, 0x02, 0xff, 0x01, 0x03, 0xfb, 0xfc, + 0x03, 0x08, 0x03, 0x03, 0x02, 0xfb, 0x00, 0x03, 0x02, 0x01, 0x01, 0x02, 0x03, 0x03, 0xff, 0xfd, 0x03, 0x02, 0x00, 0x00, 0xfe, 0xfc, + 0xfc, 0x01, 0x03, 0x02, 0xff, 0x00, 0xfc, 0xfc, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0x00, 0x02, 0x02, 0x00, 0x02, 0x01, 0xfd, 0xf8, 0xf9, + 0xfd, 0xff, 0x01, 0x03, 0x02, 0xfd, 0x01, 0xff, 0xf7, 0xff, 0x03, 0x00, 0xfc, 0xfb, 0xff, 0x03, 0xfd, 0x00, 0x02, 0x00, 0x03, 0x00, + 0xfa, 0xfb, 0xfd, 0x02, 0x05, 0x01, 0xfe, 0xff, 0x02, 0x00, 0xfd, 0xfc, 0x01, 0x06, 0x03, 0xfd, 0x02, 0x01, 0xff, 0x01, 0x05, 0x02, + 0xfd, 0x00, 0xff, 0x00, 0x03, 0xf9, 0x00, 0x06, 0xff, 0x00, 0x01, 0xff, 0xfb, 0x02, 0x04, 0xff, 0xfd, 0x02, 0x05, 0xff, 0x01, 0x04, + 0x01, 0xf6, 0xfa, 0x00, 0xff, 0x00, 0x01, 0x03, 0x04, 0xfc, 0xfa, 0x02, 0x08, 0x04, 0xfe, 0xff, 0xf8, 0xf7, 0xfd, 0x05, 0xff, 0xfe, + 0x04, 0x01, 0xff, 0xfe, 0xfe, 0x01, 0x00, 0xff, 0x03, 0xfe, 0xfb, 0xfd, 0xfd, 0xfd, 0x00, 0x02, 0x01, 0xfc, 0xfa, 0x01, 0x04, 0x02, + 0xfe, 0x00, 0xfe, 0xfc, 0xfe, 0xfe, 0xfe, 0xff, 0xfb, 0xfd, 0x00, 0x02, 0x03, 0x03, 0x00, 0xfb, 0x01, 0x02, 0xfd, 0xfc, 0xfc, 0xfd, + 0xfe, 0x00, 0x01, 0x03, 0x05, 0x00, 0xfe, 0x02, 0xfc, 0xff, 0x02, 0xfe, 0xfa, 0x00, 0x07, 0x01, 0xfb, 0xfb, 0x00, 0x03, 0xfd, 0xfa, + 0x02, 0xfe, 0xfe, 0x03, 0x0a, 0x00, 0xf9, 0xfc, 0x09, 0x03, 0xfb, 0x01, 0x00, 0xfc, 0xfb, 0xff, 0x03, 0x05, 0x02, 0x00, 0xfd, 0xfb, + 0xfe, 0xfc, 0xff, 0x04, 0x00, 0x04, 0x02, 0xf8, 0xfa, 0x00, 0x03, 0x01, 0xfd, 0xfc, 0x00, 0x06, 0xff, 0xfd, 0x05, 0xfc, 0xfb, 0x01, + 0x02, 0xfb, 0xfb, 0x03, 0x02, 0xff, 0xff, 0x02, 0xfd, 0xff, 0x03, 0x00, 0xfb, 0xfc, 0x03, 0x06, 0x05, 0x00, 0xfd, 0xfc, 0xfd, 0xff, + 0x00, 0x00, 0x01, 0x03, 0xff, 0xfc, 0xfc, 0xfd, 0xfb, 0xfe, 0x03, 0x01, 0x00, 0xfd, 0xfa, 0x04, 0xff, 0xf8, 0xff, 0x06, 0x02, 0xfb, + 0x05, 0x03, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x00, 0x00, 0xfd, 0xff, 0x04, 0x05, 0x00, 0xfb, 0xf9, 0xfc, 0x00, 0x04, 0x03, 0x00, + 0x04, 0x05, 0x02, 0xfe, 0xfd, 0xfa, 0xf9, 0x01, 0x01, 0xfe, 0xfd, 0x05, 0x05, 0x00, 0xfc, 0xfe, 0xff, 0xfd, 0xfa, 0xff, 0x01, 0xff, + 0x06, 0x08, 0x03, 0xfe, 0x04, 0x00, 0xf9, 0x05, 0x02, 0xfa, 0xf9, 0x03, 0x09, 0x06, 0x00, 0xfc, 0xfb, 0xfc, 0xfc, 0xff, 0x03, 0x04, + 0x02, 0x05, 0x04, 0xf6, 0xf8, 0xfa, 0xf8, 0xfc, 0x03, 0x05, 0x01, 0xfe, 0x00, 0x04, 0x08, 0xfd, 0xf8, 0xfe, 0xfc, 0xfa, 0xfb, 0x02, + 0xfc, 0xff, 0x07, 0x05, 0xfc, 0xf7, 0xfa, 0xff, 0xff, 0xff, 0x04, 0x04, 0xfd, 0xf8, 0xff, 0x02, 0xff, 0xfb, 0x02, 0xfe, 0xf9, 0x00, + 0x00, 0x02, 0x04, 0x00, 0x00, 0x02, 0x01, 0xfd, 0xff, 0x02, 0xfc, 0x02, 0x06, 0x01, 0xfc, 0x02, 0x05, 0xfd, 0x02, 0x02, 0xfe, 0xfb, + 0x01, 0x02, 0xfd, 0xfc, 0xff, 0x01, 0x00, 0xfa, 0x03, 0x0d, 0xfb, 0xfb, 0x01, 0x02, 0x00, 0x02, 0x04, 0x02, 0x04, 0x02, 0xfd, 0xff, + 0xff, 0xfd, 0xfc, 0xfe, 0x00, 0x01, 0x00, 0xfe, 0x00, 0x02, 0xff, 0xfd, 0xfc, 0xfd, 0xfb, 0xfc, 0xff, 0xff, 0x02, 0x03, 0x01, 0x01, + 0x02, 0xff, 0xf8, 0xfe, 0xfd, 0xfb, 0x01, 0x06, 0x02, 0xf8, 0xfc, 0xfa, 0xf9, 0xfd, 0xfb, 0x02, 0x08, 0x00, 0xfb, 0xfe, 0x03, 0xfe, + 0xfb, 0xfd, 0x02, 0x02, 0xfe, 0xfb, 0x02, 0x03, 0x02, 0x03, 0x03, 0x00, 0xfe, 0xfc, 0x00, 0xfe, 0xfb, 0xff, 0x06, 0x06, 0xff, 0xfb, + 0x02, 0x09, 0x02, 0x03, 0x01, 0xfd, 0x02, 0x02, 0xff, 0x00, 0x03, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xf8, 0xfa, 0x06, 0x03, + 0x02, 0x04, 0x00, 0xff, 0xfe, 0xfc, 0xfc, 0xfd, 0x00, 0x03, 0xfe, 0xfd, 0x04, 0x08, 0x02, 0xfa, 0xfa, 0xfe, 0xfe, 0xfc, 0xfd, 0x01, + 0x02, 0xff, 0xfd, 0xfb, 0xfe, 0x05, 0xfa, 0xf6, 0xfc, 0x01, 0x01, 0x00, 0x01, 0x02, 0xfd, 0xfa, 0xfe, 0xff, 0xfc, 0xfb, 0xff, 0x00, + 0x02, 0x04, 0xf9, 0x00, 0x09, 0x04, 0x01, 0xfd, 0xfa, 0xfc, 0x01, 0x02, 0x00, 0x00, 0x05, 0x08, 0x03, 0xfb, 0xff, 0x06, 0xfd, 0xff, + 0x03, 0x02, 0x01, 0x00, 0xff, 0x02, 0x01, 0xfe, 0xfe, 0x00, 0xff, 0x01, 0x07, 0xfb, 0xfe, 0x07, 0x02, 0xfc, 0xfe, 0x02, 0xfd, 0xfd, + 0xff, 0xfe, 0x06, 0x01, 0xfa, 0x01, 0x00, 0xfd, 0xfc, 0xf9, 0xfc, 0xff, 0xfd, 0xfd, 0xff, 0x01, 0xfe, 0x03, 0x04, 0xff, 0xfe, 0x01, + 0x01, 0xfc, 0xfe, 0xfe, 0xfd, 0xfe, 0x03, 0x00, 0xf9, 0x01, 0x00, 0xfd, 0x00, 0xff, 0xfc, 0xfb, 0xff, 0x03, 0x04, 0x01, 0xfc, 0xff, + 0x01, 0xfc, 0x00, 0x03, 0x02, 0x00, 0x00, 0x04, 0x07, 0x00, 0xfd, 0xff, 0x00, 0x02, 0x01, 0xfe, 0xff, 0x01, 0x04, 0x05, 0x00, 0xfc, + 0xfc, 0xff, 0x01, 0x02, 0x01, 0xfb, 0xfd, 0x02, 0x04, 0x03, 0xfc, 0xf9, 0x02, 0x02, 0xfd, 0xfc, 0x00, 0xff, 0xfe, 0x03, 0x01, 0xfd, + 0xfb, 0xfd, 0xff, 0x00, 0x00, 0xfd, 0x00, 0x02, 0xfe, 0xfe, 0x00, 0x01, 0x02, 0xfc, 0xfe, 0x05, 0x02, 0xfa, 0xf9, 0x04, 0x00, 0xfa, + 0xfa, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfd, 0xfb, 0xff, 0x00, 0x03, 0x05, 0x03, 0x06, 0x02, 0xfc, 0xfd, 0xff, 0xff, + 0xfd, 0xff, 0xfe, 0xfd, 0x00, 0x05, 0x02, 0xfb, 0xfa, 0x01, 0x03, 0xfc, 0xfe, 0x02, 0x03, 0x03, 0x01, 0x02, 0x02, 0xfb, 0xfe, 0x04, + 0x04, 0xfa, 0xfd, 0x06, 0x06, 0x04, 0xff, 0xfb, 0xfa, 0xfd, 0xfe, 0xfc, 0xfd, 0x02, 0x05, 0xfc, 0xfc, 0xfe, 0xfe, 0x01, 0xff, 0xfc, + 0xfd, 0x00, 0x02, 0x03, 0x04, 0xff, 0xfd, 0xfd, 0xfd, 0xfd, 0xff, 0x01, 0x02, 0xfd, 0xfa, 0x00, 0x02, 0x00, 0xfb, 0xfc, 0xfe, 0xff, + 0xff, 0x01, 0x00, 0xfd, 0x02, 0x05, 0x03, 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0x02, 0x02, 0xfe, 0x00, 0x01, 0x00, 0xff, 0x01, 0x07, 0x06, + 0xf7, 0xfe, 0x07, 0x04, 0xf8, 0xfd, 0x03, 0xff, 0x00, 0x00, 0xff, 0x03, 0x00, 0xfe, 0xff, 0x01, 0xfd, 0xfc, 0x00, 0x05, 0x01, 0xfa, + 0xfe, 0x00, 0x00, 0x01, 0x02, 0xff, 0xfd, 0xff, 0x00, 0xff, 0xfe, 0x01, 0x01, 0xfe, 0xfb, 0xfe, 0xff, 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, + 0xfd, 0x00, 0x01, 0xfc, 0x01, 0xfe, 0xfa, 0xfe, 0x03, 0x02, 0xfd, 0x01, 0x00, 0xfd, 0xfc, 0xfe, 0xfe, 0xff, 0x02, 0x00, 0xfe, 0x01, + 0x00, 0x02, 0x02, 0xfe, 0xf6, 0xfc, 0x05, 0xfd, 0x00, 0x04, 0x02, 0xfc, 0xff, 0x04, 0x03, 0x00, 0x01, 0x05, 0x08, 0x05, 0x00, 0x00, + 0x02, 0x01, 0xfe, 0xfd, 0x01, 0x01, 0xfe, 0x05, 0x00, 0xfb, 0xfc, 0xff, 0x01, 0x01, 0xfb, 0x01, 0x04, 0xff, 0xfa, 0xfe, 0x04, 0x04, + 0xfe, 0xfd, 0xff, 0xf9, 0xfc, 0x02, 0x05, 0x04, 0x00, 0xfb, 0xf7, 0xfa, 0xfb, 0xfc, 0x02, 0x04, 0x02, 0xff, 0xfe, 0xff, 0xff, 0xfb, + 0x01, 0x05, 0x01, 0xfc, 0x00, 0x02, 0xff, 0xff, 0x00, 0xfe, 0xf9, 0xfd, 0xfc, 0xf9, 0xff, 0xfe, 0xfe, 0x03, 0x05, 0x03, 0xfd, 0xf8, + 0xfd, 0xff, 0xfc, 0xff, 0x02, 0x01, 0xfe, 0x06, 0x07, 0x02, 0xfd, 0xff, 0x02, 0x02, 0xfe, 0x01, 0x03, 0xff, 0xff, 0x01, 0x00, 0xfc, + 0xff, 0x02, 0x03, 0x00, 0x02, 0x04, 0x04, 0x04, 0x05, 0x03, 0xfa, 0xf7, 0xfb, 0x00, 0x04, 0x02, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, + 0xfc, 0xfb, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0xff, 0xfe, 0xfe, 0xff, 0x03, 0x03, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xfd, 0xfd, 0x00, 0x03, + 0xfe, 0xfa, 0xfc, 0x04, 0x01, 0xfa, 0xfe, 0x04, 0x00, 0xf8, 0x03, 0x04, 0xfe, 0xfa, 0xfe, 0x01, 0x00, 0xfb, 0xfa, 0xfd, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0xfd, 0xfe, 0x03, 0x04, 0xfd, 0xfc, 0x00, 0x02, 0x01, 0x00, 0x02, 0x06, 0x05, 0xff, 0xfc, 0xfe, + 0x02, 0x02, 0xfe, 0xff, 0x03, 0x03, 0x00, 0xfc, 0xfd, 0x03, 0x01, 0xfe, 0xfe, 0xfd, 0x03, 0x06, 0x03, 0xfe, 0xfc, 0xfc, 0xfc, 0xff, + 0x01, 0x01, 0x01, 0xff, 0xff, 0x03, 0x02, 0xff, 0xff, 0xff, 0x00, 0xfc, 0xf9, 0xff, 0xff, 0xfd, 0xfc, 0x01, 0x01, 0x00, 0xff, 0xff, + 0xff, 0xfe, 0xfd, 0x02, 0x04, 0x00, 0xff, 0x01, 0x00, 0xfa, 0xfa, 0xfe, 0x02, 0x03, 0x00, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x05, 0x00, + 0xfc, 0xfc, 0x00, 0xfc, 0xfb, 0x02, 0x07, 0x05, 0x00, 0x01, 0x01, 0xfe, 0xfa, 0xff, 0x01, 0x00, 0x01, 0xfd, 0xff, 0x04, 0xff, 0xfe, + 0xfc, 0xf8, 0x04, 0x03, 0xfb, 0xfe, 0x02, 0x05, 0x05, 0x02, 0x01, 0x02, 0x01, 0xfc, 0xfd, 0xff, 0xfe, 0x00, 0x02, 0x03, 0x05, 0x01, + 0xfb, 0xfa, 0xff, 0xff, 0xfd, 0x00, 0x00, 0xff, 0xfd, 0xfd, 0xfd, 0x00, 0x02, 0x05, 0x00, 0xfb, 0xfd, 0x01, 0x01, 0xff, 0xff, 0x01, + 0x02, 0x01, 0xfc, 0xfb, 0xfc, 0xfd, 0xfd, 0x00, 0x03, 0xff, 0xfe, 0x00, 0x01, 0x00, 0x00, 0x01, 0xfe, 0xfe, 0xff, 0x01, 0x01, 0xff, + 0x00, 0x03, 0x04, 0x02, 0x00, 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, 0xfc, 0xfe, 0x05, 0x03, 0xfe, 0xfc, 0xfe, 0xfd, 0xfe, 0x01, 0x05, 0x03, + 0x00, 0x01, 0xff, 0xff, 0x01, 0xfc, 0xfe, 0x01, 0xff, 0xff, 0x00, 0x02, 0x02, 0x02, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xfb, 0xfa, 0xfc, + 0x01, 0x04, 0x05, 0x02, 0xfc, 0xfc, 0xff, 0xff, 0xfc, 0x00, 0x00, 0xfc, 0xfa, 0x02, 0x06, 0xff, 0xfc, 0xfd, 0x00, 0xfe, 0xfe, 0xfc, + 0xfb, 0xfe, 0x04, 0x04, 0xfe, 0xfc, 0xff, 0x03, 0x02, 0x00, 0x00, 0x02, 0xfe, 0x01, 0x03, 0xfd, 0xff, 0x02, 0x02, 0xff, 0x01, 0x02, + 0x00, 0xfe, 0xff, 0x00, 0xfe, 0xff, 0xff, 0xfe, 0x01, 0x06, 0x06, 0x00, 0xfe, 0xfd, 0xfc, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xff, 0x01, + 0x00, 0xfd, 0xfc, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0x04, 0x05, 0x03, 0x00, 0x00, 0xfe, 0xfc, 0xfe, 0xff, 0x00, 0x01, 0x04, 0x05, 0x00, + 0xf9, 0xfc, 0xfd, 0xf9, 0xf9, 0xfd, 0x01, 0x02, 0x01, 0x02, 0x03, 0xff, 0x02, 0xfd, 0xf5, 0xf8, 0x00, 0x02, 0x00, 0x01, 0x04, 0x05, + 0xff, 0xfb, 0xfd, 0xff, 0xfe, 0xfc, 0xfb, 0xfc, 0x02, 0x01, 0x00, 0x06, 0x00, 0xfb, 0xfb, 0xfd, 0x01, 0x03, 0x05, 0x06, 0x06, 0x04, + 0xff, 0x00, 0xfe, 0xfa, 0xff, 0x01, 0xff, 0xfc, 0x00, 0x04, 0x05, 0x05, 0x01, 0xfc, 0xfa, 0xfb, 0xfe, 0x00, 0xff, 0x02, 0x02, 0x00, + 0x04, 0x03, 0x00, 0xfd, 0x01, 0x01, 0xff, 0xff, 0x05, 0x04, 0xff, 0xfe, 0xfb, 0xf9, 0xfc, 0xfd, 0xfe, 0x00, 0x04, 0x02, 0x01, 0x01, + 0xfe, 0xfd, 0xfd, 0xff, 0xfd, 0xff, 0x01, 0xfc, 0x01, 0x02, 0xff, 0xfd, 0xff, 0x02, 0x01, 0xfb, 0xfd, 0x02, 0xff, 0xfc, 0xfc, 0x00, + 0x00, 0xfd, 0xfb, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x03, 0x02, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x03, 0x01, 0xfd, 0x00, 0x03, 0x02, 0xfe, + 0xfe, 0x01, 0x03, 0x00, 0xfe, 0x03, 0x08, 0xfe, 0xfc, 0xff, 0xfe, 0x00, 0x02, 0x01, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x01, 0x01, 0x00, 0xfc, 0xfd, 0xfd, 0xfd, 0x02, 0x04, 0x01, 0xff, 0xfc, 0xfd, 0x01, 0x00, 0xff, 0xff, 0x01, 0x02, 0xff, 0xfd, 0xfd, + 0xfb, 0xfe, 0x02, 0x01, 0xfc, 0xfa, 0xff, 0x02, 0x03, 0x00, 0xfc, 0xfc, 0xfe, 0xff, 0x02, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, + 0x01, 0x00, 0xfe, 0xfe, 0xfd, 0xfe, 0x02, 0x03, 0xff, 0xfc, 0xff, 0xfe, 0xff, 0x01, 0x01, 0x00, 0xfe, 0xfd, 0xfc, 0xfe, 0x04, 0x08, + 0x03, 0xff, 0x01, 0xff, 0xfe, 0xfe, 0xfe, 0x00, 0x02, 0x01, 0x02, 0xfe, 0xfe, 0x01, 0xff, 0xfc, 0xfd, 0xfe, 0x03, 0x03, 0x00, 0x02, + 0xfe, 0xfc, 0x01, 0x05, 0x03, 0xfd, 0xfa, 0xfc, 0xff, 0x01, 0x04, 0x05, 0x02, 0xfe, 0xfd, 0xfc, 0xf9, 0xf6, 0xfc, 0x00, 0xff, 0x01, + 0xff, 0xfe, 0x00, 0xff, 0x01, 0x03, 0x01, 0xff, 0x00, 0x02, 0x00, 0xfb, 0xfa, 0x00, 0x04, 0x06, 0x04, 0xfe, 0xfc, 0xfb, 0xfa, 0xfe, + 0x02, 0x03, 0x03, 0x02, 0xff, 0xfc, 0xfa, 0xfe, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x01, 0xff, 0x02, 0xff, 0xfb, 0xf9, 0xfc, + 0x00, 0x01, 0xfd, 0xff, 0x04, 0x05, 0x02, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0x00, 0x04, 0x05, 0x01, 0xfc, 0xfd, 0x02, 0x03, 0xfd, 0x00, + 0x00, 0xfd, 0xfe, 0x00, 0xff, 0xfd, 0x00, 0x01, 0xff, 0xfa, 0xf9, 0xfb, 0xfc, 0xfc, 0x01, 0x03, 0x00, 0x01, 0x04, 0x05, 0x02, 0xfd, + 0xfc, 0xfe, 0x03, 0x01, 0xfe, 0xfe, 0x02, 0x00, 0xfc, 0xfd, 0x02, 0x03, 0xff, 0x01, 0x00, 0xfe, 0xfd, 0x04, 0x03, 0xfe, 0x00, 0x03, + 0x02, 0xff, 0x00, 0x01, 0x00, 0x00, 0x03, 0x06, 0x03, 0xfd, 0xfa, 0xfd, 0x01, 0xfe, 0xfe, 0xff, 0xfd, 0xfe, 0xfc, 0xfc, 0x01, 0x04, + 0x04, 0x03, 0x03, 0xfd, 0xf8, 0xfa, 0xfb, 0xff, 0x02, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0x04, + 0x04, 0xfd, 0xf9, 0xfe, 0x03, 0x00, 0xfe, 0xff, 0x02, 0xfd, 0xfc, 0x02, 0x08, 0x02, 0xfe, 0xfd, 0xf9, 0xf8, 0xfb, 0xfe, 0x03, 0x07, + 0x06, 0xff, 0x01, 0x00, 0xff, 0x01, 0x00, 0xfc, 0xfc, 0x00, 0x08, 0x09, 0x00, 0x00, 0x01, 0x00, 0xfb, 0x00, 0x02, 0xfc, 0xff, 0x01, + 0x03, 0x08, 0x08, 0x01, 0xfa, 0xfc, 0xfd, 0xfb, 0xf9, 0x01, 0x05, 0x04, 0x04, 0x03, 0x00, 0xfd, 0xfd, 0xfd, 0xfe, 0x00, 0xfd, 0xfd, + 0xff, 0xfd, 0xff, 0x00, 0xfd, 0xfa, 0xfd, 0x00, 0xfe, 0xff, 0xfe, 0xfe, 0x04, 0x04, 0x01, 0xfe, 0xfc, 0xfb, 0xfd, 0x01, 0xfe, 0x00, + 0x03, 0x00, 0xff, 0xfe, 0xfa, 0xfd, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xfe, 0x02, 0x01, 0x01, 0x03, 0x03, 0x02, 0x02, 0x00, + 0xfd, 0x00, 0x02, 0x02, 0x03, 0x02, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x06, 0x04, 0xfc, 0xf8, 0xff, 0x03, 0x02, 0x02, 0x04, 0x01, + 0xfc, 0xfe, 0x01, 0x03, 0x06, 0x00, 0x00, 0x05, 0x02, 0xff, 0xff, 0x01, 0xfe, 0xfb, 0xfb, 0x00, 0x01, 0xfe, 0xfc, 0x01, 0x01, 0xfc, + 0xfa, 0xfd, 0x00, 0x01, 0x01, 0xfe, 0xfc, 0xfc, 0x01, 0x02, 0x01, 0xff, 0xfd, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xf8, 0xfd, 0x04, + 0x00, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfe, 0x01, 0xfe, 0xff, 0x03, 0x03, 0x01, 0xff, 0x00, 0x03, 0x01, 0xfe, 0xfe, 0x00, 0x01, 0x01, + 0x01, 0xfe, 0xfe, 0x01, 0x04, 0x02, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x01, 0xff, 0x03, 0x08, 0xfe, 0xfe, 0x03, 0x04, 0xff, 0xfc, 0xfe, + 0x04, 0x02, 0xff, 0x01, 0x04, 0x00, 0xfd, 0xfe, 0xfe, 0xfe, 0xfd, 0xfc, 0xff, 0x02, 0x02, 0x04, 0x01, 0xfd, 0xfb, 0x00, 0xff, 0xfa, + 0xfe, 0x00, 0x01, 0x03, 0x00, 0x00, 0xfe, 0xf7, 0xfa, 0xfc, 0xfc, 0x00, 0x01, 0x00, 0x01, 0x03, 0xff, 0xfc, 0x02, 0x02, 0xfe, 0xfb, + 0xff, 0xff, 0x01, 0x04, 0xff, 0xfd, 0xff, 0xff, 0xfe, 0xfc, 0xfb, 0x02, 0x02, 0xff, 0xff, 0x00, 0x01, 0x01, 0x01, 0x01, 0xff, 0xfe, + 0x02, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0xfa, 0xf9, 0xf9, 0xfd, 0x00, 0x01, 0x04, 0x03, 0xff, 0xfd, 0x01, 0x01, 0xfd, + 0x02, 0x03, 0x02, 0x02, 0x01, 0xfe, 0xfe, 0x01, 0x02, 0x00, 0xfe, 0x03, 0x02, 0xff, 0x00, 0xfd, 0xfb, 0xfe, 0x00, 0xfd, 0xfb, 0xfd, + 0xfd, 0x00, 0x02, 0xfe, 0x01, 0x01, 0xfd, 0xff, 0xff, 0xfe, 0xfd, 0x03, 0x00, 0xfa, 0x00, 0x01, 0xfe, 0xfd, 0xff, 0xfd, 0xfc, 0xfe, + 0x00, 0x01, 0x01, 0xfe, 0xfc, 0xfd, 0x01, 0x02, 0x05, 0x04, 0x00, 0xfd, 0x00, 0x03, 0x02, 0x00, 0x01, 0x05, 0x04, 0xff, 0xfc, 0xfe, + 0xfc, 0xfe, 0x02, 0x02, 0xfe, 0xfc, 0xfd, 0xff, 0x00, 0x00, 0xff, 0x02, 0x02, 0x00, 0x02, 0x05, 0x04, 0xfe, 0xfd, 0xff, 0x00, 0x00, + 0xfe, 0xfe, 0x01, 0x06, 0x02, 0xfc, 0xfc, 0xff, 0xfd, 0xfb, 0x01, 0x01, 0x01, 0x04, 0xfc, 0xfe, 0x00, 0xf9, 0xfd, 0x01, 0xff, 0xfc, + 0x01, 0x03, 0xff, 0xfd, 0x01, 0x03, 0xfb, 0xfb, 0xfe, 0x00, 0xfc, 0xfd, 0x00, 0x01, 0x00, 0x01, 0x01, 0xf8, 0xfd, 0x03, 0x04, 0x01, + 0x00, 0xff, 0xfe, 0x02, 0x03, 0x01, 0xfd, 0x02, 0x02, 0xfc, 0x00, 0xff, 0xff, 0x03, 0x05, 0x00, 0xfc, 0xfe, 0x01, 0x01, 0xff, 0x02, + 0x00, 0xfe, 0x01, 0xff, 0x01, 0x05, 0x01, 0xfe, 0xfe, 0x00, 0xff, 0xfe, 0xfe, 0xfd, 0xfe, 0xff, 0x00, 0xff, 0xfd, 0xfd, 0x00, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x00, 0x02, 0x00, 0x00, 0xfe, 0xfb, 0xfc, 0x00, 0x02, 0xff, 0xfd, 0xff, 0x01, 0xfc, 0xfa, 0xfd, 0x03, 0x02, + 0x00, 0xff, 0x00, 0xfd, 0xfc, 0xfe, 0xfe, 0x02, 0x04, 0x02, 0x00, 0x02, 0x03, 0xfc, 0xfc, 0xff, 0x01, 0xfe, 0xfc, 0xfc, 0xfb, 0xff, + 0x02, 0x02, 0x00, 0x03, 0x02, 0xfc, 0xff, 0x01, 0x01, 0xfe, 0x02, 0x04, 0x03, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00, 0xff, 0x05, 0x05, + 0x02, 0xff, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xff, 0x01, 0x03, 0xfd, 0xfb, 0x01, 0x03, 0x04, 0x05, 0x04, 0xfe, 0xfc, 0xff, 0xfd, 0xfe, + 0x01, 0x01, 0xfc, 0xfc, 0xfe, 0xfb, 0xff, 0x00, 0xfd, 0xff, 0x01, 0x01, 0xff, 0xfd, 0xfd, 0x00, 0x04, 0x06, 0x02, 0xfd, 0xfd, 0xff, + 0x01, 0x02, 0xfe, 0x00, 0x03, 0xff, 0xfc, 0xfc, 0xfd, 0xfd, 0xfa, 0xf9, 0xfd, 0xfd, 0x01, 0x03, 0xfe, 0x01, 0x04, 0x02, 0x00, 0xff, + 0x00, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x03, 0x04, 0x02, 0x00, 0xff, 0xfd, 0xfa, 0xfd, 0x02, 0x04, 0xff, 0x02, 0x03, 0xfe, 0x00, 0xfb, + 0xf7, 0xfc, 0x03, 0x04, 0x01, 0x04, 0x04, 0x01, 0xfd, 0x01, 0x01, 0xff, 0xfe, 0xfc, 0xfa, 0xfb, 0x01, 0x03, 0x05, 0x07, 0x00, 0xfb, + 0xfb, 0xfb, 0xfd, 0xff, 0x03, 0x01, 0x06, 0x07, 0xfd, 0xf8, 0xfa, 0xfd, 0x00, 0x00, 0x00, 0x01, 0xfe, 0xfd, 0x00, 0x05, 0x01, 0xfc, + 0xfc, 0xfe, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x05, 0x02, 0xfe, 0xfd, 0xfd, 0xff, 0xfe, 0xfb, 0xff, 0x04, 0x06, 0x02, 0x02, 0xfd, + 0xf5, 0xf7, 0xff, 0x05, 0x06, 0x01, 0xfd, 0xfd, 0x01, 0xfd, 0xfd, 0x02, 0x00, 0xff, 0xff, 0xfd, 0xff, 0xfe, 0xfb, 0xff, 0x02, 0x04, + 0x03, 0xfa, 0xfa, 0xfe, 0x00, 0x00, 0x01, 0x03, 0x02, 0x02, 0xff, 0xfc, 0xfc, 0x01, 0x04, 0x02, 0x01, 0x00, 0x00, 0x01, 0xff, 0xfd, + 0xfe, 0xfd, 0x00, 0x03, 0x00, 0x01, 0xff, 0xfb, 0xfc, 0xff, 0x01, 0xff, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x02, 0x01, 0xfe, + 0xfd, 0xff, 0x02, 0x04, 0x03, 0x04, 0x03, 0xfe, 0xfb, 0xf9, 0xf9, 0xfc, 0x01, 0x03, 0x02, 0x03, 0x00, 0xfd, 0xfd, 0xfa, 0xfe, 0x03, + 0x04, 0x01, 0xff, 0xff, 0xfd, 0x00, 0x02, 0x01, 0x00, 0xfe, 0xfc, 0xfa, 0xfa, 0xfc, 0xfe, 0x02, 0x04, 0x02, 0xfc, 0xfd, 0xfa, 0xf8, + 0xfe, 0x03, 0x04, 0x03, 0x01, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0xfd, 0x03, 0x04, 0xfe, 0xfc, 0x01, 0x03, 0xff, 0x01, 0x00, 0xfd, + 0xfd, 0xfd, 0xfc, 0xfc, 0xfe, 0x03, 0x06, 0x05, 0x04, 0x00, 0xfc, 0xfa, 0xfe, 0x01, 0x00, 0x01, 0x02, 0x02, 0x03, 0xff, 0xfd, 0xff, + 0xfd, 0xfe, 0x03, 0x04, 0xfd, 0xfb, 0xff, 0xff, 0x03, 0x02, 0xfe, 0x01, 0xff, 0xfe, 0x01, 0x00, 0x02, 0x04, 0x00, 0xff, 0x00, 0x00, + 0xfe, 0xfd, 0xfd, 0xfe, 0x03, 0x01, 0xfb, 0xfb, 0xfb, 0xfe, 0x03, 0x00, 0x01, 0x02, 0xfe, 0xfa, 0xfc, 0x01, 0xfd, 0xfe, 0x01, 0x02, + 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xfd, 0xff, 0x02, 0x01, 0xff, 0x02, 0xfe, 0xff, 0x01, 0xff, 0xfb, 0xfd, 0x02, 0x02, 0xfe, 0xfd, 0x01, + 0x02, 0x01, 0x00, 0x02, 0x01, 0xff, 0xfd, 0xfc, 0x01, 0x03, 0x00, 0xfd, 0xfd, 0x00, 0x01, 0x00, 0xff, 0xff, 0x02, 0x00, 0xfd, 0xfe, + 0x01, 0x01, 0xff, 0x01, 0x00, 0xfe, 0xff, 0xfe, 0x00, 0x01, 0xff, 0x02, 0x03, 0x02, 0x01, 0xff, 0xff, 0x00, 0xfa, 0xfc, 0x02, 0x03, + 0x00, 0x02, 0x05, 0x00, 0xfc, 0xfe, 0x05, 0x03, 0x00, 0xfe, 0xfe, 0xfd, 0xfc, 0xfd, 0xff, 0xfd, 0xfb, 0xfe, 0x00, 0x01, 0x00, 0xfe, + 0xfd, 0xfe, 0x00, 0x00, 0xfe, 0xfd, 0xff, 0x01, 0x02, 0x01, 0xfe, 0xfd, 0xfe, 0xfe, 0xfd, 0xff, 0xff, 0xfc, 0x02, 0x03, 0xff, 0xfe, + 0xfd, 0xfc, 0xfd, 0x00, 0x01, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02, 0x01, 0xff, 0xfe, 0x03, 0x01, 0xfc, 0xfb, 0xfe, 0x01, 0x02, 0xff, + 0xfd, 0xfc, 0xfe, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x06, 0x00, 0xfa, 0x01, 0x02, 0x01, 0xff, 0x03, 0xff, 0xff, 0x04, 0x07, + 0x01, 0xfc, 0xfc, 0xff, 0xfe, 0xfa, 0xfb, 0xff, 0x03, 0x04, 0x01, 0xfe, 0xfe, 0xfe, 0xfb, 0xfb, 0xff, 0x02, 0x03, 0x03, 0x02, 0x01, + 0x00, 0xfe, 0xfa, 0xfc, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xfa, 0xfc, 0xfe, 0xfc, 0xfa, 0xfd, 0x02, 0x04, 0x05, + 0x01, 0xfd, 0xfe, 0x00, 0x00, 0xfc, 0x00, 0x01, 0x00, 0xfe, 0x00, 0x02, 0x01, 0xfd, 0xfc, 0xff, 0x02, 0x02, 0xff, 0xff, 0x03, 0x03, + 0x01, 0x00, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0x02, 0x03, 0x02, 0x00, 0xff, 0xfd, 0xfe, 0x03, 0x07, 0x05, 0xfd, 0xfc, + 0x01, 0x04, 0xfe, 0xfd, 0xff, 0x01, 0x00, 0x01, 0x02, 0xff, 0xfc, 0xfc, 0x00, 0x03, 0x04, 0x01, 0xfd, 0xfc, 0x00, 0x02, 0xf9, 0xfd, + 0x03, 0x02, 0xff, 0xfd, 0xfe, 0xff, 0x01, 0x00, 0xff, 0xfe, 0xfd, 0xfb, 0xfa, 0xfd, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xff, + 0xfc, 0xfb, 0xfd, 0x02, 0x04, 0xff, 0xff, 0x00, 0xff, 0xfc, 0xfe, 0x02, 0x02, 0x00, 0xfe, 0xfd, 0xff, 0x02, 0x02, 0x00, 0xff, 0xff, + 0xff, 0xfe, 0xfc, 0xfd, 0xff, 0x03, 0x04, 0x05, 0x06, 0x02, 0x00, 0xff, 0xfc, 0xff, 0x02, 0x01, 0x01, 0x00, 0x01, 0x01, 0x03, 0xfd, + 0xf9, 0xff, 0x03, 0x01, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0xff, 0xfc, 0xfe, 0x00, 0x02, 0x03, 0x03, 0x01, 0x00, + 0xff, 0xfe, 0xff, 0xff, 0xfd, 0xfb, 0xfd, 0x00, 0x00, 0xff, 0xfd, 0xff, 0x02, 0x01, 0xff, 0xfe, 0xfd, 0xfe, 0xfe, 0xfc, 0xfd, 0x02, + 0x07, 0x01, 0xfc, 0xfd, 0x01, 0x00, 0x00, 0x02, 0x04, 0x03, 0xff, 0xfb, 0xfd, 0xfe, 0xfe, 0xfc, 0xfe, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x01, 0x04, 0x05, 0x02, 0x01, 0xfd, 0xfb, 0xfd, 0x01, 0x03, 0x02, 0x04, 0x05, 0x02, 0xfd, 0xfb, 0xfe, 0x01, 0x01, 0xfe, 0xfd, + 0xfd, 0xfe, 0x01, 0x02, 0x00, 0xfd, 0xfe, 0x01, 0x00, 0xfe, 0xff, 0x01, 0xfe, 0xfc, 0xff, 0x05, 0xff, 0xfc, 0xfd, 0xfd, 0xfc, 0xfd, + 0x01, 0x00, 0x01, 0x03, 0x02, 0xfe, 0xfb, 0xfb, 0x01, 0x01, 0x01, 0x04, 0xfe, 0xfe, 0x00, 0x00, 0xfd, 0xfe, 0x01, 0x06, 0x02, 0xfe, + 0x03, 0x00, 0xfd, 0xfd, 0xff, 0x02, 0x03, 0x01, 0xfc, 0xfc, 0x00, 0x04, 0x00, 0xfd, 0xff, 0x03, 0x00, 0xfe, 0x01, 0xff, 0xff, 0x01, + 0x03, 0x00, 0xfe, 0x00, 0xfe, 0xff, 0x02, 0x03, 0xff, 0x00, 0x01, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xff, + 0xff, 0xfd, 0xff, 0x01, 0x01, 0xfe, 0xfa, 0xfb, 0xff, 0x01, 0x03, 0x04, 0x02, 0xfe, 0xfb, 0xfb, 0xfc, 0xfe, 0xfe, 0x01, 0x02, 0x01, + 0x02, 0xfd, 0xfc, 0xff, 0xfc, 0xfc, 0xff, 0x02, 0x01, 0x00, 0x01, 0x02, 0x00, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x01, 0xfe, 0xfe, 0x02, + 0x08, 0x04, 0xff, 0xff, 0xf9, 0xfa, 0xfe, 0x01, 0x01, 0x02, 0x03, 0x01, 0xfe, 0xff, 0x03, 0xfe, 0xfe, 0x00, 0xff, 0xff, 0x01, 0x02, + 0x00, 0xfe, 0x00, 0x02, 0x02, 0xfe, 0xfb, 0xff, 0x01, 0x00, 0xff, 0xfe, 0x02, 0x05, 0xff, 0xfc, 0xfc, 0xfd, 0xfd, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0xff, 0xfd, 0x01, 0x02, 0x00, 0xfd, 0x00, 0x00, 0xfd, 0xfb, + 0xfe, 0x01, 0x00, 0xfd, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x02, 0x00, 0x00, 0x02, 0x02, 0xfe, 0xfb, 0xff, 0x04, 0x02, 0xfe, 0xfe, + 0xfe, 0xff, 0x01, 0x00, 0xfe, 0xff, 0x01, 0x02, 0x02, 0xff, 0xfc, 0xfc, 0xff, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x03, 0x00, + 0x00, 0x02, 0x04, 0x00, 0xfd, 0xfe, 0x02, 0x03, 0x00, 0xfd, 0xfe, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0x00, 0x02, 0x01, + 0xfd, 0xfc, 0xff, 0x03, 0x02, 0xff, 0xfe, 0xff, 0x01, 0x02, 0xfd, 0xfe, 0x01, 0x01, 0xfe, 0xfe, 0xfe, 0xfb, 0xfe, 0xff, 0xfe, 0xff, + 0x00, 0x00, 0x01, 0x00, 0xff, 0xfe, 0xff, 0x02, 0x04, 0x02, 0xff, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x03, 0x00, 0x03, 0x02, 0xfe, 0xfc, + 0x01, 0x03, 0xfe, 0xfd, 0x00, 0x01, 0xfe, 0xfd, 0x01, 0x05, 0x03, 0xff, 0xfd, 0xff, 0x02, 0x04, 0x02, 0xfd, 0xfe, 0x01, 0x00, 0xfd, + 0xfe, 0x01, 0x02, 0x00, 0xfd, 0xfc, 0xfc, 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, 0x00, 0xff, 0x00, 0x02, 0x02, 0xfe, 0xfc, 0xfd, 0xff, 0xff, + 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0xff, 0xfd, 0xfd, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xfd, 0xff, 0x04, 0x02, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x01, 0x01, 0x01, 0xff, 0xfe, 0x00, 0x01, 0x00, 0xff, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0xfe, + 0xfd, 0xfd, 0xff, 0x00, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x03, 0x02, 0xfe, 0xfc, + 0xfd, 0x01, 0x03, 0x02, 0x01, 0x04, 0xff, 0xfb, 0xfb, 0xfb, 0xfd, 0xff, 0x00, 0x00, 0xff, 0x00, 0x02, 0xff, 0xfc, 0xfe, 0x00, 0x00, + 0xff, 0xff, 0xfd, 0xfe, 0x01, 0x01, 0x01, 0x02, 0x01, 0xfc, 0xfd, 0xff, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0x00, 0x01, 0xff, 0xfc, + 0xfd, 0xff, 0x04, 0x04, 0x01, 0x01, 0x02, 0x01, 0xfe, 0x00, 0x00, 0x03, 0x06, 0x02, 0xfe, 0xff, 0xff, 0x00, 0xfe, 0xfd, 0xfe, 0x00, + 0x00, 0xff, 0x00, 0x03, 0x04, 0x00, 0xfb, 0xfa, 0xfe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x03, 0x01, 0x00, 0xfe, 0xfb, 0xfb, 0xfd, 0x00, + 0x00, 0xfe, 0x01, 0x02, 0xfe, 0xfc, 0xfb, 0xfc, 0xfe, 0xfe, 0xfe, 0x00, 0x01, 0x03, 0x02, 0xff, 0xfa, 0xfa, 0xfe, 0xff, 0xfb, 0xfc, + 0x02, 0x04, 0xff, 0xfe, 0x02, 0xfd, 0xfd, 0xfe, 0xfd, 0xfb, 0xfc, 0xff, 0xff, 0x03, 0x03, 0xfe, 0xfb, 0xfd, 0x00, 0xff, 0xfe, 0x01, + 0x04, 0x05, 0x05, 0x04, 0x03, 0x01, 0xff, 0xff, 0x01, 0x01, 0x00, 0xff, 0xff, 0x00, 0x03, 0x04, 0x01, 0xfd, 0xfc, 0x01, 0xff, 0xfb, + 0xfd, 0x01, 0x04, 0x04, 0x02, 0x00, 0xfe, 0xfe, 0x01, 0x00, 0xfe, 0xfe, 0x03, 0x01, 0xff, 0x02, 0xff, 0xfd, 0xff, 0xff, 0xfe, 0xff, + 0xff, 0xfe, 0xfd, 0xfe, 0x02, 0x03, 0x00, 0xfd, 0x03, 0x03, 0xfe, 0xf9, 0xfc, 0xfe, 0xfe, 0xff, 0x01, 0x01, 0x00, 0x04, 0x00, 0xfc, + 0xfc, 0x03, 0x02, 0xfd, 0xfe, 0xfc, 0xfe, 0x02, 0xfd, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfd, 0xfc, 0xff, 0x01, 0x01, 0xfd, 0x00, 0x02, + 0xfb, 0xf9, 0xfc, 0x00, 0x00, 0x03, 0x03, 0x01, 0xfe, 0x01, 0x05, 0x02, 0x00, 0x00, 0x01, 0xff, 0xfc, 0xfd, 0x02, 0x01, 0x04, 0x07, + 0x04, 0xfb, 0xfa, 0xfe, 0xfd, 0xfe, 0x00, 0x03, 0x02, 0x04, 0x04, 0xff, 0xfd, 0xfa, 0xfa, 0x04, 0x06, 0x04, 0x05, 0x02, 0x00, 0x00, + 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xfc, 0xfb, 0xfe, 0xfe, 0x01, 0x04, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x04, 0x03, 0xff, + 0xff, 0xff, 0xfe, 0xfb, 0x00, 0x00, 0xff, 0xff, 0x01, 0xfe, 0xf9, 0xf9, 0x01, 0x04, 0xff, 0xfc, 0xff, 0x03, 0xfe, 0xfa, 0xfd, 0x04, + 0x02, 0x01, 0x01, 0x02, 0xfc, 0xfc, 0x00, 0xfe, 0xff, 0x01, 0x02, 0xfc, 0xfd, 0xff, 0xfb, 0x01, 0x01, 0xff, 0xff, 0x02, 0x03, 0xff, + 0xfb, 0xfe, 0x02, 0x01, 0x01, 0x02, 0x02, 0xff, 0x00, 0x02, 0x00, 0xfa, 0x00, 0x06, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x03, 0x03, 0xfd, + 0xfe, 0xff, 0xfe, 0xff, 0x00, 0x03, 0x06, 0x06, 0x05, 0x00, 0xfa, 0xff, 0x01, 0xff, 0xff, 0x06, 0x06, 0xff, 0xfd, 0xfc, 0xf9, 0xf8, + 0xfd, 0x01, 0x02, 0xff, 0x01, 0xff, 0xfc, 0xff, 0x03, 0x02, 0xfd, 0x01, 0x00, 0xfc, 0xfd, 0xfe, 0xff, 0x02, 0x02, 0x00, 0x00, 0x01, + 0xfd, 0xfc, 0xfe, 0xfb, 0xfc, 0xff, 0x02, 0xfc, 0xff, 0x01, 0xf9, 0xfe, 0x02, 0x02, 0x00, 0x02, 0x02, 0xfd, 0x00, 0xfe, 0xfd, 0x03, + 0x03, 0xfe, 0xfc, 0x00, 0xfe, 0xfe, 0x03, 0x01, 0x02, 0x05, 0x01, 0xfd, 0xfe, 0x01, 0xf8, 0xfd, 0x04, 0x00, 0x01, 0x03, 0x03, 0xfb, + 0xff, 0x02, 0x00, 0x09, 0x05, 0xfe, 0x02, 0xff, 0xfe, 0xff, 0xfe, 0xfd, 0x00, 0x03, 0xff, 0xfe, 0xff, 0x00, 0x02, 0x03, 0x00, 0xfd, + 0xff, 0xff, 0xfc, 0xfc, 0xff, 0x01, 0x00, 0xfe, 0xff, 0x02, 0x00, 0xfd, 0xfc, 0x00, 0x01, 0x03, 0x03, 0xfe, 0xfb, 0xff, 0x03, 0xff, + 0xfd, 0xff, 0x04, 0x01, 0xfe, 0xfe, 0xfc, 0xff, 0x01, 0xff, 0xff, 0xfb, 0xf9, 0xfc, 0xfe, 0xfd, 0xfd, 0x03, 0x05, 0x01, 0xfd, 0xff, + 0x02, 0x01, 0xfd, 0xfd, 0x02, 0x06, 0x04, 0xfe, 0xfd, 0x01, 0xfd, 0xfa, 0xfc, 0x00, 0x04, 0x02, 0xfe, 0x00, 0xff, 0xfd, 0xfc, 0x03, + 0x02, 0xfd, 0xfe, 0x03, 0x02, 0xfd, 0x01, 0x01, 0x01, 0x04, 0x01, 0xff, 0xff, 0xf9, 0xff, 0x02, 0xfe, 0x01, 0x03, 0xff, 0xf6, 0xfd, + 0xff, 0xfd, 0x03, 0x05, 0x02, 0xfc, 0x02, 0x00, 0xfd, 0x01, 0x03, 0x01, 0xff, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0x00, 0xfc, 0xff, + 0xfe, 0xfc, 0x00, 0xff, 0xff, 0x01, 0x02, 0xff, 0xfc, 0xfb, 0xfe, 0xff, 0xff, 0x00, 0xfe, 0xff, 0x04, 0x03, 0xfe, 0xfc, 0x03, 0x01, + 0x00, 0x02, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfe, 0x00, 0x00, 0x01, 0x00, 0xfe, 0xfc, 0x02, 0x01, 0xfd, 0x01, 0xff, + 0xfc, 0xfd, 0x01, 0x03, 0x03, 0x04, 0x01, 0xfe, 0xff, 0x02, 0x00, 0xfe, 0x00, 0xff, 0x00, 0xfe, 0xf9, 0xfb, 0xfe, 0xff, 0xff, 0x02, + 0x01, 0xfe, 0x01, 0x04, 0x02, 0xfd, 0xfc, 0xfe, 0x00, 0x00, 0xff, 0x01, 0x06, 0xff, 0x00, 0x06, 0x01, 0xfe, 0xfe, 0x00, 0x03, 0x01, + 0xfd, 0xfc, 0x00, 0x01, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x02, 0x04, 0x03, 0x01, 0xff, 0xff, 0xfd, 0xfc, 0xfd, 0x01, 0xfe, 0xfd, + 0xff, 0x01, 0x02, 0xff, 0xfc, 0x01, 0xfd, 0xfb, 0x00, 0x06, 0xff, 0xf8, 0xff, 0x02, 0xfe, 0xf9, 0xfd, 0xfe, 0xfe, 0x00, 0x00, 0x00, + 0x01, 0xfe, 0xfe, 0xfd, 0xfd, 0xff, 0x03, 0x03, 0xfb, 0xff, 0xfe, 0xfc, 0x00, 0x03, 0x03, 0x01, 0x00, 0xfe, 0xfe, 0x02, 0x01, 0xfe, + 0xfd, 0xfb, 0xff, 0x00, 0xff, 0x00, 0x05, 0x08, 0x02, 0x03, 0x01, 0xfc, 0xfc, 0x01, 0x01, 0xfd, 0x01, 0x04, 0x03, 0x02, 0xfe, 0xfd, + 0x00, 0x04, 0x01, 0xfd, 0xfe, 0xff, 0xfe, 0xff, 0x05, 0x00, 0xfe, 0x02, 0xfd, 0xfc, 0xfe, 0x00, 0x02, 0x03, 0x03, 0xfe, 0x01, 0x02, + 0xfd, 0xfa, 0xfc, 0xff, 0xfd, 0xfc, 0xfc, 0xfe, 0x02, 0x01, 0xfc, 0xfa, 0xfa, 0x00, 0x03, 0xff, 0xfe, 0xff, 0x00, 0xfe, 0x01, 0x04, + 0x02, 0xfd, 0xfd, 0x00, 0xfe, 0xfd, 0xff, 0x03, 0x00, 0x01, 0x01, 0xff, 0xf9, 0xfa, 0xff, 0x01, 0x00, 0x02, 0x06, 0x02, 0xff, 0xff, + 0x02, 0xff, 0xff, 0x02, 0x03, 0x02, 0x01, 0x00, 0xfc, 0xff, 0x02, 0x01, 0xfd, 0xfe, 0x01, 0xff, 0xfd, 0xfd, 0xfe, 0xfc, 0x01, 0x06, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0x01, 0x04, 0x02, 0x00, 0x00, 0xfc, 0xfd, 0xff, + 0xff, 0xff, 0x00, 0x01, 0xfe, 0xff, 0xff, 0xfc, 0x00, 0x01, 0x00, 0xff, 0x01, 0x00, 0xfb, 0xfd, 0x00, 0x00, 0xff, 0x01, 0xff, 0xfd, + 0xff, 0x00, 0xff, 0x00, 0xfe, 0xfe, 0x00, 0x01, 0xfc, 0xfd, 0x03, 0x01, 0xfd, 0xfe, 0x02, 0x01, 0x00, 0x00, 0x02, 0x03, 0x01, 0xff, + 0x01, 0x02, 0xff, 0xfc, 0xff, 0x01, 0x01, 0x04, 0x01, 0xff, 0x02, 0x02, 0x00, 0xfd, 0xfd, 0xfe, 0x00, 0x00, 0x01, 0xfe, 0xfd, 0xfe, + 0xfd, 0x00, 0x02, 0x00, 0xff, 0xfe, 0xfd, 0x00, 0x03, 0x02, 0xfe, 0x03, 0x02, 0xff, 0xff, 0xff, 0xfa, 0xf8, 0x02, 0x05, 0x03, 0x02, + 0xfe, 0xfd, 0xfd, 0xfc, 0xfa, 0xfc, 0x01, 0xff, 0x00, 0x02, 0x02, 0x00, 0xfe, 0xfe, 0xfe, 0x01, 0x01, 0xfe, 0xff, 0x00, 0x01, 0x03, + 0xff, 0xfd, 0x00, 0x00, 0xfd, 0xfa, 0xfc, 0xff, 0x02, 0x04, 0x03, 0x03, 0x01, 0xfe, 0xf8, 0xfe, 0x01, 0xff, 0x03, 0x03, 0x02, 0x03, + 0x04, 0x00, 0xfc, 0xff, 0x02, 0x02, 0x01, 0x01, 0x00, 0xfe, 0xfc, 0xfe, 0x01, 0x03, 0xff, 0xfe, 0xfe, 0xfc, 0xfb, 0xfc, 0x00, 0x02, + 0x01, 0x00, 0xff, 0xff, 0x01, 0x01, 0xfe, 0xfe, 0x01, 0x04, 0x03, 0xfe, 0xfc, 0xfe, 0x00, 0xfe, 0xfb, 0xfc, 0x04, 0x04, 0xfe, 0xfc, + 0x01, 0x03, 0xff, 0x01, 0xff, 0xfd, 0x00, 0xff, 0xfd, 0xfe, 0x01, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xfe, 0x01, 0x00, + 0xfc, 0xfc, 0x00, 0xfe, 0xff, 0x01, 0x00, 0x03, 0x02, 0xfc, 0xfe, 0xfd, 0xfb, 0xfd, 0x08, 0x05, 0xfc, 0x00, 0xfe, 0xfe, 0x00, 0x03, + 0x00, 0xfe, 0x02, 0x02, 0x03, 0x04, 0xfe, 0xfe, 0xff, 0xfe, 0xfd, 0xfd, 0xfe, 0x00, 0x00, 0xfd, 0xfe, 0x06, 0x04, 0x00, 0x02, 0xfd, + 0xfd, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0xfb, 0xfc, 0x06, 0x01, 0xff, 0x00, 0xfe, 0xff, 0x00, 0xfe, 0xfa, 0xfb, 0xfe, 0x00, 0x03, + 0xff, 0xfb, 0x01, 0x01, 0xfd, 0xfb, 0x03, 0x02, 0x01, 0x04, 0x03, 0xff, 0xfc, 0xfe, 0xfd, 0xff, 0x02, 0x03, 0xff, 0xfd, 0x00, 0x01, + 0xff, 0xfd, 0xfb, 0x01, 0x01, 0xfa, 0xfe, 0xfd, 0xfb, 0x00, 0x03, 0x03, 0x02, 0x04, 0xfc, 0xfa, 0x02, 0x04, 0x00, 0xfe, 0x05, 0xff, + 0xfe, 0x01, 0xfd, 0xfc, 0xfe, 0x00, 0xfe, 0x02, 0x07, 0x05, 0xfd, 0xfd, 0x04, 0x02, 0xff, 0xfe, 0xfe, 0xfd, 0xfe, 0x00, 0x06, 0x04, + 0x02, 0x02, 0xff, 0xfc, 0xfa, 0xfa, 0xff, 0x01, 0xff, 0xfb, 0x00, 0x01, 0xfc, 0xfe, 0xff, 0xfd, 0xfb, 0xfe, 0x00, 0x01, 0x00, 0xff, + 0xfd, 0xfb, 0xfe, 0xfd, 0xfd, 0x04, 0x02, 0x00, 0x01, 0x02, 0x01, 0x02, 0x04, 0xff, 0x00, 0x01, 0xfa, 0xfd, 0x01, 0x02, 0x00, 0x01, + 0x02, 0x02, 0x01, 0xfe, 0xfc, 0xfd, 0xfe, 0xfe, 0xfe, 0x03, 0x01, 0xfe, 0xff, 0x01, 0x02, 0x01, 0x00, 0xff, 0x01, 0x04, 0xff, 0x00, + 0x01, 0xfd, 0xff, 0x01, 0x00, 0xfe, 0x00, 0xff, 0xfd, 0x03, 0x02, 0x00, 0x02, 0xfe, 0xff, 0x03, 0x00, 0xfd, 0x00, 0x08, 0xfe, 0xfc, + 0x00, 0xfc, 0xff, 0x03, 0x04, 0x02, 0x01, 0x00, 0xfd, 0xf6, 0xf8, 0xff, 0x01, 0xfc, 0xfb, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0xfe, 0xfe, + 0xfd, 0xfd, 0xfd, 0xff, 0x02, 0x03, 0x00, 0xfe, 0xff, 0xfe, 0x00, 0x02, 0x00, 0xfe, 0xfe, 0x00, 0x02, 0x00, 0xfe, 0xfe, 0xff, 0x00, + 0x00, 0xfd, 0xff, 0x02, 0x01, 0xfe, 0x03, 0x07, 0xfe, 0xfd, 0xff, 0x01, 0x01, 0x02, 0x02, 0x01, 0xfd, 0xfd, 0xff, 0x02, 0xfe, 0xff, + 0x06, 0x00, 0xff, 0x00, 0x01, 0xfd, 0xfd, 0x00, 0x05, 0xfd, 0xfa, 0x01, 0x00, 0xff, 0x01, 0x06, 0x01, 0xff, 0x00, 0xfe, 0xfc, 0xfd, + 0x01, 0x01, 0xff, 0x01, 0x04, 0xfd, 0xf9, 0xfc, 0x02, 0xff, 0xfc, 0xfe, 0xfd, 0x00, 0x03, 0x00, 0xfc, 0xfb, 0xfd, 0xfc, 0xfe, 0x01, + 0x03, 0x05, 0x03, 0x00, 0xfe, 0xfe, 0xfc, 0xf9, 0xfc, 0x00, 0x01, 0x00, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfd, 0xfe, + 0x03, 0x01, 0x03, 0x06, 0x02, 0xff, 0xfd, 0xfd, 0x02, 0x01, 0x00, 0x03, 0x01, 0xff, 0x00, 0x03, 0x01, 0xfd, 0xfc, 0xff, 0x00, 0xff, + 0xfe, 0xff, 0xff, 0x00, 0x02, 0x00, 0xfe, 0xfe, 0xfd, 0x00, 0x02, 0x00, 0xfe, 0xfe, 0xff, 0x03, 0x02, 0xff, 0xff, 0xfe, 0xfd, 0xfd, + 0x02, 0x00, 0xfe, 0xfe, 0x00, 0x02, 0x00, 0xfc, 0xfd, 0x00, 0x00, 0xfe, 0x00, 0xff, 0xfb, 0xfe, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0xfe, 0xfe, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0x01, 0x03, 0x04, 0x04, 0x03, 0x02, 0xfe, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x01, 0xff, + 0xfd, 0xfe, 0xff, 0x00, 0xff, 0xfd, 0xfd, 0xfc, 0xfe, 0xff, 0xfd, 0x01, 0x02, 0x01, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x01, 0x05, + 0xff, 0xfc, 0xfe, 0xff, 0xfe, 0xfd, 0xfd, 0x01, 0x03, 0xff, 0xfb, 0xfd, 0xff, 0x01, 0x03, 0x04, 0x01, 0xfe, 0xfd, 0xfc, 0xfe, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0xfd, 0xfd, 0x01, 0x02, 0xfc, 0xfa, 0xfe, 0x03, 0xff, 0xfc, 0xfe, 0x03, 0x01, 0x01, 0x01, 0xff, + 0x00, 0x04, 0x05, 0x02, 0x01, 0x01, 0x01, 0xfb, 0xfc, 0x00, 0x00, 0xfd, 0xfd, 0x01, 0x01, 0x03, 0x01, 0xfc, 0xfa, 0xfa, 0xfc, 0xfd, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x01, 0x01, 0x01, 0xff, 0xfe, 0x03, 0x00, 0xfe, 0x00, 0x01, 0x00, 0xfd, 0xfc, 0xfc, 0xfa, 0xfa, 0x00, + 0x01, 0x01, 0x03, 0x01, 0x03, 0x05, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0x04, 0x04, 0x00, 0xfe, 0xff, 0x01, 0x02, 0x02, 0x03, 0x03, 0xfe, + 0xfb, 0xfd, 0x00, 0xff, 0x00, 0x01, 0xfe, 0x01, 0x02, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x03, 0x01, 0xfd, 0xfe, 0xfd, 0xfd, 0xff, 0x00, + 0x01, 0x01, 0x01, 0x00, 0xfd, 0xfa, 0xfb, 0xff, 0x02, 0x02, 0x03, 0x01, 0xff, 0x00, 0xfc, 0xfc, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, + 0xfd, 0xfb, 0xfe, 0x00, 0x00, 0x00, 0x01, 0xfc, 0xfb, 0xff, 0xff, 0x00, 0x01, 0x01, 0x01, 0x00, 0xfe, 0xfe, 0x00, 0x02, 0x00, 0x02, + 0x02, 0x00, 0x01, 0xff, 0xfe, 0xfe, 0xfb, 0xfe, 0x03, 0x03, 0x03, 0x00, 0xfd, 0x00, 0xff, 0xff, 0x01, 0x03, 0x01, 0x00, 0x03, 0x00, + 0x00, 0x02, 0xfc, 0xff, 0x02, 0xfe, 0xfb, 0xfc, 0x00, 0x02, 0xfe, 0xfd, 0x00, 0xff, 0x00, 0x00, 0xfe, 0x00, 0x02, 0x03, 0x02, 0xfe, + 0xfc, 0xfe, 0xfc, 0xff, 0x02, 0x04, 0xff, 0xfd, 0xfe, 0xfd, 0xfc, 0xfe, 0x01, 0x00, 0xfe, 0xfd, 0xfd, 0x01, 0x03, 0xff, 0xf8, 0xfb, + 0x01, 0x02, 0xfd, 0xff, 0x03, 0x01, 0x01, 0x01, 0x01, 0xfd, 0xff, 0x00, 0xfd, 0xfc, 0x00, 0x03, 0x02, 0x02, 0x02, 0x00, 0xfc, 0xfd, + 0xff, 0xfe, 0xff, 0x00, 0x03, 0x05, 0x07, 0x02, 0xfb, 0x00, 0x00, 0xff, 0x04, 0x03, 0x03, 0x04, 0x01, 0xfb, 0xfb, 0x01, 0x02, 0x00, + 0xfe, 0xfd, 0xfd, 0xff, 0x00, 0xff, 0x00, 0x01, 0x01, 0xfe, 0xfd, 0xfd, 0xfd, 0xff, 0x01, 0x02, 0x04, 0x02, 0xfd, 0xfc, 0xfd, 0xff, + 0xff, 0xfb, 0xfb, 0xff, 0x02, 0xfe, 0xfe, 0xfe, 0xfd, 0xff, 0xfe, 0xfc, 0xfd, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x01, 0xff, 0x00, + 0x03, 0x02, 0xfe, 0xfd, 0xff, 0xfd, 0xff, 0x01, 0xff, 0x02, 0x00, 0xfd, 0x03, 0xff, 0xfc, 0x00, 0x04, 0x01, 0xfd, 0xfb, 0x01, 0x04, + 0x01, 0x01, 0x03, 0x03, 0x00, 0x01, 0xfe, 0xff, 0x06, 0x02, 0x00, 0x01, 0x00, 0x00, 0xfe, 0xfb, 0xfd, 0xfe, 0xff, 0x01, 0x02, 0x02, + 0x01, 0x00, 0x03, 0x04, 0xfd, 0xfe, 0xfd, 0xfc, 0xfd, 0x01, 0x03, 0x01, 0x00, 0x01, 0xff, 0xfd, 0xfc, 0xfd, 0xfe, 0xfd, 0xfd, 0x00, + 0x03, 0x00, 0xfc, 0xfc, 0x01, 0xfd, 0xfc, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0x04, 0x03, 0xfd, 0xfb, 0xfa, 0xfd, 0x00, 0xfe, 0xff, 0x00, + 0x00, 0x02, 0x06, 0x06, 0x00, 0xff, 0xfe, 0xfd, 0xff, 0x00, 0x01, 0x01, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x01, 0x02, 0x00, 0x00, 0xfe, + 0xfb, 0xfb, 0x01, 0x04, 0xfd, 0x04, 0x04, 0xfb, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x02, 0x05, 0x01, 0x03, 0x02, 0xfe, 0x00, 0xfc, 0xf9, + 0xfd, 0xfe, 0xfd, 0xfe, 0x04, 0x06, 0x03, 0xff, 0xfe, 0x00, 0x02, 0x00, 0xfd, 0xfb, 0xfc, 0x05, 0x03, 0xff, 0xff, 0x03, 0x01, 0xfc, + 0xfb, 0xff, 0xfe, 0xfb, 0x00, 0xff, 0x00, 0x05, 0xfd, 0xfa, 0xfe, 0xfe, 0xfd, 0xfc, 0xfe, 0x07, 0x09, 0x04, 0x01, 0x00, 0x03, 0x03, + 0xfe, 0xf9, 0xfb, 0xff, 0xfe, 0x01, 0x02, 0xff, 0x02, 0x02, 0xff, 0xfc, 0xfb, 0xfd, 0xff, 0xfe, 0xfe, 0x01, 0x04, 0x02, 0x00, 0xff, + 0x00, 0xfe, 0xfe, 0x01, 0x02, 0x00, 0xff, 0x01, 0x03, 0x00, 0xf9, 0xfa, 0xfc, 0xfe, 0xff, 0xfd, 0xfe, 0x00, 0xfe, 0xff, 0xff, 0x00, + 0x02, 0x02, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xfe, 0x00, 0x03, 0x03, 0x03, 0x02, 0xff, 0x00, 0xfc, 0xf9, 0xfe, 0xfe, 0xfe, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0xfd, 0xfc, 0x00, 0xfe, 0xff, 0x04, + 0xff, 0xfe, 0x00, 0x01, 0x02, 0x02, 0x00, 0xfe, 0xff, 0xff, 0xfe, 0xfc, 0xfe, 0x02, 0x04, 0x02, 0xff, 0xfd, 0xfc, 0xfe, 0xfe, 0xfb, + 0xff, 0x02, 0x01, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0x01, 0x01, 0xff, 0x01, 0x00, 0xfd, 0xff, 0x03, 0x01, 0xfc, 0xfd, 0xfc, 0xfa, 0xfc, + 0x00, 0x01, 0xff, 0xfe, 0x02, 0x04, 0x02, 0x03, 0xfd, 0xf9, 0xfe, 0x02, 0x03, 0x00, 0x00, 0x00, 0x01, 0x03, 0x01, 0xfe, 0xff, 0x02, + 0x01, 0xfe, 0xfc, 0x01, 0x00, 0xfe, 0x01, 0x03, 0x03, 0x02, 0x02, 0xfe, 0xfb, 0xfa, 0xfb, 0x01, 0x06, 0x05, 0x06, 0x02, 0xfe, 0xfe, + 0xfd, 0xfe, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x01, 0xff, 0xfb, 0xfb, 0xff, 0x00, 0xff, 0x01, 0x02, 0x04, 0x04, 0x01, + 0xfb, 0xf9, 0xfd, 0xfe, 0xff, 0x00, 0xfe, 0x02, 0x03, 0x01, 0xff, 0xfc, 0xfb, 0xfd, 0xfb, 0xfc, 0x00, 0x07, 0x00, 0xfd, 0x01, 0x00, + 0xfe, 0xfe, 0x01, 0x00, 0xfe, 0xfd, 0x01, 0x02, 0x04, 0x05, 0xfe, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0x00, 0x03, 0x03, 0x01, 0x00, 0x01, + 0xfe, 0xfb, 0xfb, 0x01, 0x03, 0x01, 0x00, 0xff, 0x00, 0x02, 0x00, 0xff, 0x00, 0x03, 0x01, 0xfe, 0xfb, 0xfc, 0xff, 0x03, 0x05, 0x03, + 0xfe, 0xfb, 0xfd, 0xfb, 0xfe, 0x03, 0xff, 0xfd, 0xff, 0x00, 0xff, 0xff, 0x00, 0x04, 0x03, 0x02, 0x01, 0x01, 0xfd, 0xfb, 0x00, 0xff, + 0xff, 0xff, 0x02, 0xfe, 0xff, 0x04, 0xfe, 0xf9, 0xfa, 0xff, 0x01, 0x01, 0xff, 0xff, 0x00, 0x01, 0x01, 0x02, 0x00, 0xfe, 0x02, 0xff, + 0xfd, 0xfe, 0x00, 0x00, 0xfe, 0xfd, 0x03, 0x01, 0xfb, 0xfa, 0xfd, 0xff, 0xff, 0x02, 0x03, 0x03, 0x01, 0xfd, 0xfc, 0xfd, 0xfd, 0x01, + 0x03, 0x03, 0x03, 0x00, 0xfd, 0xff, 0x02, 0x00, 0xfd, 0x01, 0x02, 0x01, 0x00, 0xfd, 0xfc, 0xff, 0x02, 0x00, 0xfe, 0xff, 0x00, 0xfe, + 0xfd, 0x00, 0x03, 0x03, 0x00, 0x00, 0xfe, 0xfd, 0xfd, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0xfe, 0xfe, 0x02, 0x02, 0x00, 0x00, 0x01, + 0x01, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x02, 0xff, 0xfe, 0x00, 0xff, 0xfb, 0xfc, 0x01, 0x01, 0xfe, 0xfe, 0x02, 0x01, 0xfe, + 0xfc, 0xfd, 0x00, 0x01, 0xff, 0xfc, 0xfc, 0x00, 0x05, 0x04, 0x03, 0x01, 0xfb, 0xfa, 0xfb, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x02, 0xff, 0xfd, 0xff, 0x02, 0xff, 0xfe, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfc, 0xfc, 0xff, 0x02, 0x04, 0x03, + 0x00, 0xfd, 0xfd, 0xfb, 0xf9, 0xfc, 0x01, 0x03, 0x01, 0x04, 0x06, 0x03, 0xfd, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x05, 0x04, 0xfd, 0xfb, 0x00, 0xfd, 0xfe, 0x00, 0x02, 0xff, 0xff, 0x02, 0x01, 0x00, 0x00, 0xfe, 0xfd, 0xfc, 0xfd, 0x03, 0x04, 0x02, + 0xff, 0x00, 0xfd, 0xfb, 0xff, 0xfd, 0xff, 0x00, 0xfc, 0xf9, 0xfb, 0x00, 0xfe, 0xff, 0x01, 0xfe, 0xff, 0xff, 0xfe, 0x01, 0x00, 0xff, + 0x00, 0x00, 0x04, 0x04, 0xfe, 0xfe, 0xff, 0xff, 0x01, 0xff, 0xfe, 0xff, 0x01, 0xff, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, + 0xfe, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x02, 0xfe, 0xfd, 0x02, 0x02, 0xfe, 0xfd, 0x06, 0xff, 0xfc, 0x02, 0x01, 0xff, 0xff, + 0x00, 0xff, 0x00, 0x02, 0x03, 0xfe, 0xfd, 0x01, 0x03, 0xff, 0xfc, 0xff, 0x01, 0xff, 0xfe, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0xfe, + 0xfe, 0xfc, 0xfd, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xfc, 0xff, 0x02, 0xfe, 0xfc, 0xff, 0x02, 0xfe, 0xfc, 0xfe, 0x01, 0xfd, 0xff, 0x04, + 0x02, 0xfd, 0xfd, 0x01, 0x00, 0xfe, 0xfd, 0x00, 0x04, 0x04, 0x01, 0xfd, 0x02, 0x04, 0xfe, 0xff, 0xff, 0xfd, 0xfa, 0xfe, 0x04, 0x06, + 0x02, 0xff, 0xff, 0xff, 0xfd, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0x03, 0x03, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x02, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x01, 0xfe, 0xff, 0x02, 0x03, 0x00, 0xfe, 0xff, 0x01, 0x01, 0x01, 0x01, 0xfe, 0xfc, 0xfc, 0xfe, + 0x01, 0x01, 0xff, 0xfd, 0xfd, 0xff, 0x01, 0x01, 0xff, 0xfe, 0xff, 0xfc, 0xfc, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, + 0x00, 0x00, 0xff, 0xff, 0xfe, 0x00, 0x03, 0xfd, 0xfe, 0xff, 0xfd, 0xfe, 0x01, 0x01, 0xfb, 0x01, 0x03, 0xff, 0xfd, 0x01, 0x03, 0xfe, + 0xff, 0xff, 0xfe, 0x01, 0x00, 0x00, 0x03, 0x01, 0x03, 0x03, 0xff, 0xfd, 0xff, 0x00, 0xfc, 0xfc, 0x00, 0x03, 0x03, 0x00, 0xfe, 0xff, + 0xfc, 0xfb, 0xfe, 0x00, 0x02, 0x01, 0xff, 0x05, 0x02, 0xfb, 0xfe, 0x02, 0x01, 0xfc, 0xfe, 0x00, 0x02, 0x03, 0x02, 0x01, 0x01, 0x02, + 0xfb, 0xfa, 0xfe, 0x00, 0xfe, 0xfd, 0x00, 0x00, 0xfe, 0xfd, 0x02, 0xff, 0xfc, 0xff, 0x02, 0x01, 0xff, 0x00, 0xfd, 0xff, 0x01, 0xfe, + 0xfc, 0xfd, 0x02, 0x01, 0x01, 0x00, 0xff, 0xfd, 0xff, 0x02, 0xfe, 0xfe, 0xff, 0xff, 0xfc, 0x00, 0x05, 0x05, 0x00, 0x00, 0x03, 0x01, + 0x00, 0x01, 0x00, 0xfe, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0xfc, 0xfb, 0xfd, 0xff, 0x03, 0x02, 0xff, 0xfc, 0xff, 0xfe, 0xfb, 0x01, + 0x03, 0x00, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfd, 0x01, 0x03, 0xfe, 0x00, 0x00, 0xfe, 0x01, 0x00, 0xfe, 0xfd, 0x01, + 0xff, 0xfd, 0x04, 0x04, 0x02, 0x00, 0xfe, 0xfd, 0xfe, 0x00, 0x01, 0x00, 0xff, 0x01, 0x00, 0xff, 0xfe, 0xfd, 0xfe, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x00, 0xff, 0xfd, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x02, 0x00, 0x00, 0x01, 0xff, 0xfc, + 0xfd, 0x00, 0x00, 0x01, 0x00, 0xfd, 0xff, 0x00, 0x00, 0x01, 0x03, 0x03, 0xff, 0xfc, 0x00, 0x00, 0xfb, 0xfb, 0xff, 0x03, 0x04, 0xff, + 0xfd, 0x00, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xfd, 0x00, 0x04, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0x01, 0x02, + 0x01, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0xfc, 0xfd, 0x03, 0x06, 0x03, 0x00, 0xff, 0xfd, 0xfe, 0xff, 0x00, 0x02, 0x01, 0xff, 0x00, 0x00, + 0xff, 0xfd, 0xfd, 0xfe, 0x00, 0x01, 0xfc, 0xfd, 0x02, 0x00, 0xff, 0x02, 0x04, 0x00, 0xfc, 0xfc, 0xff, 0xfe, 0xff, 0x02, 0x03, 0x02, + 0x02, 0x02, 0xfe, 0xfe, 0x01, 0x02, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0xfe, 0xfe, 0x00, 0x02, 0x00, 0xfe, 0xff, 0xfd, 0x00, + 0x04, 0x04, 0xff, 0xff, 0x00, 0xfd, 0xfb, 0xfc, 0x00, 0x04, 0x01, 0xfd, 0xfd, 0xfe, 0xfc, 0xfa, 0xfe, 0x00, 0x01, 0x00, 0xfc, 0xfb, + 0xfe, 0x00, 0x01, 0x00, 0xff, 0x01, 0x04, 0x02, 0xfd, 0x00, 0x01, 0x00, 0xfd, 0xff, 0x01, 0x01, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x01, + 0x01, 0x06, 0x00, 0xfb, 0xfd, 0x01, 0x02, 0x00, 0xff, 0x00, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0x02, 0x01, 0xff, 0xff, 0x00, 0xfd, + 0xfa, 0x01, 0x02, 0x02, 0x02, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0x00, 0x04, 0x03, 0xfe, 0xfa, 0xff, 0x00, 0xfe, 0x00, 0x02, 0x01, + 0xfd, 0xfa, 0xfe, 0x01, 0xff, 0xfe, 0xff, 0x01, 0x00, 0xfe, 0xfc, 0xfc, 0xfe, 0x01, 0x02, 0x00, 0x00, 0xfe, 0xfd, 0xfc, 0x02, 0x03, + 0xfe, 0xfd, 0x01, 0x02, 0xfb, 0x00, 0x02, 0x00, 0xfe, 0x00, 0x02, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0x01, 0x02, 0x00, 0xff, 0x00, + 0x02, 0x00, 0xff, 0xff, 0x02, 0xff, 0xfe, 0x00, 0x02, 0x01, 0x01, 0x01, 0xfe, 0xfe, 0x01, 0x02, 0x02, 0x01, 0xff, 0xff, 0x01, 0x03, + 0x04, 0x01, 0xfe, 0xfc, 0xf8, 0xfc, 0x00, 0x01, 0x00, 0x04, 0x04, 0xfb, 0xfb, 0xff, 0x00, 0xfd, 0xff, 0xff, 0xfb, 0xfd, 0xff, 0x00, + 0x04, 0xfe, 0xfd, 0x02, 0x00, 0x02, 0xff, 0xf9, 0xfe, 0x01, 0x00, 0x01, 0xfd, 0xfd, 0x01, 0xfa, 0xfc, 0xff, 0xfd, 0xfc, 0x01, 0x04, + 0xfe, 0xfd, 0x01, 0x03, 0xff, 0xfe, 0x00, 0xff, 0xff, 0x00, 0x01, 0x02, 0x06, 0x04, 0xff, 0x00, 0x03, 0x02, 0xfc, 0xfb, 0xfd, 0xfe, + 0xfe, 0x01, 0x03, 0x02, 0x02, 0x01, 0xff, 0xfd, 0x00, 0x02, 0x03, 0x03, 0x04, 0x02, 0xff, 0x01, 0x01, 0xff, 0xfe, 0xfe, 0xff, 0xff, + 0xfd, 0xfd, 0xfe, 0x01, 0x01, 0x03, 0x01, 0xfb, 0xfc, 0xfe, 0xfe, 0xfd, 0xfe, 0x01, 0x03, 0x03, 0x00, 0xfc, 0xff, 0x01, 0xff, 0xfc, + 0xfe, 0xfc, 0xfc, 0x01, 0xfe, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xfd, 0x01, 0xff, 0xfe, 0x04, 0x03, 0x00, 0xff, 0xff, 0xfd, 0xfc, 0xfe, + 0x00, 0xff, 0xfe, 0xfe, 0x01, 0x03, 0x04, 0x01, 0xfe, 0xfd, 0xfe, 0xfb, 0xff, 0x05, 0x01, 0x01, 0x02, 0x00, 0xfe, 0x00, 0x04, 0x05, + 0x00, 0x00, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfc, 0xfb, 0xff, 0x03, 0x00, 0xfe, 0x00, 0x04, 0xfd, 0xfc, 0xff, 0xff, + 0x02, 0x04, 0x02, 0xfe, 0xfe, 0x01, 0x02, 0xfb, 0xf9, 0xfe, 0x05, 0x00, 0xfd, 0x01, 0xfe, 0xfe, 0x00, 0xff, 0xfd, 0xfb, 0xfd, 0x02, + 0xff, 0xfe, 0x01, 0x00, 0x00, 0xff, 0x00, 0xfd, 0xfc, 0xfe, 0xfe, 0x00, 0x00, 0xfd, 0x00, 0xfe, 0xfd, 0x02, 0x00, 0xfd, 0xfc, 0xfe, + 0x03, 0x06, 0x03, 0xfc, 0xfd, 0x02, 0x03, 0xff, 0xfc, 0xfc, 0x02, 0x03, 0x01, 0x00, 0x01, 0xfe, 0xfc, 0x02, 0x03, 0x03, 0x02, 0xfc, + 0xfd, 0x01, 0x02, 0x01, 0x02, 0x02, 0xff, 0x00, 0x02, 0x01, 0x00, 0x02, 0x04, 0x04, 0xfd, 0xfd, 0x01, 0xfb, 0xfc, 0xfd, 0xfc, 0x00, + 0x01, 0xff, 0xfd, 0xfc, 0xfd, 0xfe, 0xfa, 0x02, 0x06, 0x01, 0xfc, 0xfd, 0x00, 0xfe, 0xfe, 0x01, 0x04, 0xfd, 0x03, 0x05, 0xfd, 0x01, + 0xfd, 0xfb, 0x03, 0xfe, 0xfa, 0xfd, 0xfe, 0xfe, 0xff, 0x01, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x04, 0x01, 0x00, 0x05, 0x00, + 0xfc, 0xfe, 0x00, 0x01, 0xff, 0xfd, 0x01, 0x01, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0xff, 0xff, 0x01, 0x03, 0x03, + 0x00, 0xfe, 0xfe, 0x02, 0x04, 0x00, 0xfd, 0xfc, 0xfe, 0x03, 0x03, 0xfe, 0xfb, 0xfc, 0xfc, 0xff, 0x03, 0xfe, 0xfe, 0x02, 0xff, 0xfe, + 0xfd, 0xfd, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xfc, 0x01, 0x02, 0xfd, 0xfe, 0xfe, 0xff, 0xfe, 0x02, 0x00, 0xfd, 0xfb, 0x04, + 0x06, 0xfd, 0xfb, 0xfc, 0xff, 0x01, 0x04, 0x04, 0x01, 0xfd, 0x00, 0x01, 0xfc, 0xfd, 0x00, 0x03, 0x02, 0x04, 0x02, 0xfc, 0xfd, 0xfe, + 0x00, 0x00, 0xff, 0x02, 0x03, 0xff, 0xfd, 0x01, 0x04, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0x02, 0x01, 0xfc, 0xff, 0xff, + 0xfe, 0x00, 0xfc, 0xfd, 0x03, 0x02, 0x04, 0x01, 0xfb, 0xfe, 0x01, 0x00, 0xfd, 0xfc, 0xfe, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0xfe, + 0xfd, 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0xfe, 0xfd, 0x01, 0x03, 0x01, 0xff, 0xfd, 0xff, 0x00, 0xfc, 0x00, 0x04, 0x02, 0xff, 0xfd, + 0xfd, 0xfe, 0x04, 0x03, 0xfd, 0xff, 0x02, 0x01, 0xff, 0xfe, 0x00, 0x03, 0x00, 0xff, 0x00, 0x02, 0xfe, 0x02, 0x04, 0x01, 0x03, 0x03, + 0x01, 0x00, 0xff, 0x01, 0x02, 0xfe, 0xfc, 0xfd, 0xfe, 0x03, 0x03, 0xfe, 0xf9, 0xfd, 0x01, 0x01, 0x01, 0x00, 0xff, 0xfe, 0xff, 0x01, + 0x02, 0x02, 0xff, 0xfe, 0x01, 0xfe, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xfd, 0xfb, 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xfd, 0xff, 0x02, 0x05, 0x00, 0xff, 0x01, 0xfe, 0xfe, 0xff, 0x01, 0x01, 0x00, 0xff, 0xff, 0xfd, 0xff, 0x01, 0xff, 0xfc, 0xfe, + 0x01, 0x04, 0x03, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x04, 0x03, 0x00, 0x01, 0x01, 0x02, 0x02, 0xff, 0x01, 0x02, 0x01, 0x02, 0x00, 0xfd, + 0xfb, 0xfc, 0xfe, 0x00, 0xff, 0x04, 0x03, 0xfd, 0xfc, 0xff, 0x01, 0x00, 0x01, 0x03, 0x02, 0xff, 0xfd, 0xfd, 0xff, 0x00, 0x00, 0xff, + 0xfe, 0xff, 0xfe, 0xfc, 0xfd, 0xfe, 0x00, 0x02, 0xfe, 0xfd, 0xfd, 0xfc, 0xfe, 0x01, 0x03, 0x01, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xff, + 0x01, 0xfd, 0xfd, 0x01, 0x02, 0x00, 0xfd, 0xfe, 0xfe, 0xfd, 0xfd, 0x00, 0x02, 0x02, 0x01, 0x01, 0x00, 0xff, 0x00, 0x02, 0x03, 0x01, + 0xfe, 0xfd, 0xfe, 0xff, 0x01, 0xfd, 0xfc, 0x03, 0x05, 0x02, 0xff, 0x01, 0xfd, 0xfd, 0x03, 0x03, 0x00, 0xfd, 0xfd, 0x01, 0x03, 0x01, + 0x02, 0xff, 0xff, 0x01, 0x01, 0xff, 0xff, 0x01, 0xff, 0xfd, 0xff, 0x03, 0xfe, 0xf9, 0xfc, 0x03, 0x06, 0x02, 0xfc, 0xfd, 0x01, 0x01, + 0x00, 0xff, 0x00, 0x01, 0x01, 0xff, 0xfc, 0xfa, 0xff, 0x00, 0xfd, 0x03, 0x00, 0xfc, 0xfe, 0x00, 0xfd, 0xfa, 0xfd, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0xfc, 0xff, 0x01, 0xff, 0x01, 0x01, 0xff, 0xfe, 0x03, 0x02, 0xfe, 0xfd, 0xfb, 0xfd, 0x00, 0xfd, 0xfc, 0xff, 0x05, + 0x03, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xfd, 0xfe, 0x02, 0x04, 0x02, 0x01, 0x01, 0xfe, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x00, 0x02, + 0x02, 0x00, 0xff, 0xff, 0xff, 0x00, 0x02, 0x02, 0xfe, 0xfb, 0xfd, 0x02, 0x05, 0x04, 0x02, 0x00, 0xfe, 0xfc, 0xff, 0xfd, 0xfa, 0xfd, + 0x04, 0x03, 0xfc, 0xff, 0x00, 0xff, 0xfe, 0x00, 0xff, 0xfd, 0xfe, 0x02, 0x00, 0xfa, 0xfe, 0x00, 0xff, 0xff, 0x03, 0x04, 0x01, 0xff, + 0xfe, 0x00, 0x02, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfc, 0xfb, 0xff, 0xff, 0xfe, 0xff, 0x03, 0x01, 0xff, 0x01, 0xff, 0x01, 0x02, 0xfd, + 0xfd, 0xfe, 0xfe, 0xff, 0x04, 0x05, 0x00, 0xff, 0x00, 0x00, 0xff, 0xfd, 0xfc, 0xfc, 0xff, 0x01, 0x01, 0x00, 0x02, 0x01, 0xfe, 0x01, + 0xff, 0xfd, 0xfe, 0xfe, 0x00, 0x02, 0x02, 0x03, 0x03, 0x01, 0xfd, 0xfc, 0xfd, 0xff, 0x00, 0x02, 0x02, 0xff, 0xfe, 0xff, 0x01, 0x01, + 0xff, 0xfe, 0x00, 0xfc, 0xfb, 0xff, 0x05, 0x02, 0x00, 0x03, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0x00, 0x04, 0x02, 0xff, 0xfe, 0xfe, 0x00, + 0xff, 0xfd, 0xfd, 0x02, 0x00, 0xfa, 0xfd, 0x00, 0x01, 0x01, 0x01, 0xff, 0xfd, 0xfd, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xfe, + 0xfe, 0xff, 0x01, 0x03, 0x03, 0x02, 0x00, 0xff, 0xfe, 0xfd, 0xff, 0x01, 0x01, 0xff, 0xff, 0x00, 0x02, 0x00, 0xff, 0xff, 0xfe, 0xfe, + 0xff, 0x01, 0x00, 0xfe, 0xfe, 0x00, 0xfc, 0xff, 0x04, 0x04, 0x01, 0xfe, 0xfc, 0xfd, 0x01, 0x03, 0x03, 0x01, 0x01, 0x00, 0xfd, 0xfa, + 0xfc, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0xfb, 0xfb, 0xfe, 0x02, 0x02, 0x03, 0x01, 0xfd, 0xfe, 0xfe, 0xfe, 0x01, 0x03, + 0x01, 0xfd, 0xfc, 0xff, 0x03, 0x04, 0x01, 0xfe, 0xff, 0x00, 0xfd, 0xfb, 0xfc, 0xfe, 0x02, 0x05, 0x05, 0xff, 0xfb, 0xfe, 0x02, 0x00, + 0xfe, 0x01, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfd, + 0xfd, 0xff, 0x01, 0x02, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x01, 0x03, 0x02, 0xfe, 0xfd, 0xfb, 0xfc, 0x02, 0x00, 0xfe, 0x01, 0xff, 0xff, + 0x00, 0xfe, 0xfd, 0xff, 0xff, 0xfe, 0xfd, 0x01, 0x05, 0x03, 0xff, 0xff, 0x03, 0xfb, 0xfb, 0x03, 0x04, 0x01, 0xff, 0x00, 0xfc, 0xfe, + 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0x00, 0x03, 0x03, 0x01, 0x02, 0xfe, 0xfa, 0xfc, 0xff, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x04, 0xff, 0xfb, 0xfc, 0xff, 0xff, 0xff, 0x03, 0x02, 0xfe, 0xfd, 0x03, 0x03, 0xfe, 0xfa, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0x00, + 0x00, 0x01, 0x02, 0x00, 0xfd, 0xfe, 0xfd, 0xfc, 0xff, 0xfd, 0xff, 0x04, 0x04, 0x01, 0xfe, 0xfc, 0xfc, 0xfd, 0x00, 0x04, 0x01, 0xff, + 0xff, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x02, 0x03, 0x00, 0xff, 0x03, 0x04, 0x00, 0xfe, 0xfd, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x02, 0x03, 0x00, 0xfa, 0xfc, 0xff, 0xff, 0xff, 0x01, 0x03, 0x02, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0xfe, 0xfe, + 0x01, 0x02, 0x00, 0xfe, 0xff, 0x00, 0xfe, 0xfb, 0x00, 0x00, 0xfd, 0xfd, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xfd, 0xfd, 0x02, 0x02, 0xff, + 0x00, 0xfe, 0xfd, 0xfe, 0x01, 0xfe, 0xfd, 0x01, 0xff, 0xfd, 0xfc, 0xfe, 0x02, 0x03, 0x00, 0x01, 0x00, 0xfd, 0xfc, 0xfd, 0x00, 0x02, + 0x02, 0x00, 0xff, 0x00, 0x02, 0x00, 0xfe, 0x00, 0x02, 0x02, 0x03, 0x03, 0xfd, 0xfa, 0xff, 0x03, 0x03, 0x00, 0xff, 0xff, 0x02, 0x03, + 0xfe, 0xfd, 0xfe, 0xff, 0xff, 0x00, 0x01, 0xfe, 0xfe, 0xfd, 0xfe, 0x01, 0x03, 0x02, 0x00, 0xff, 0x00, 0xff, 0xfc, 0xff, 0x01, 0x01, + 0x00, 0x01, 0xff, 0xfc, 0xfd, 0x00, 0x00, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xfd, 0xfc, 0xfd, 0xfd, 0x01, 0x03, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x01, 0x01, 0xff, 0xff, 0x01, 0x00, 0xfd, 0xfe, 0x02, 0x04, 0xfd, 0xfd, 0x00, 0x00, 0xfe, 0xfb, 0xfb, 0xff, 0x00, 0x01, 0x03, + 0x04, 0x02, 0x00, 0xfe, 0x00, 0x02, 0x02, 0xfe, 0xff, 0x01, 0x00, 0xff, 0x00, 0x00, 0xfd, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, + 0x00, 0xfd, 0xfd, 0x01, 0x02, 0x01, 0x01, 0xfe, 0x00, 0x02, 0xff, 0xfe, 0x00, 0x04, 0x02, 0x01, 0xff, 0xfd, 0xfd, 0xfe, 0xfd, 0xfb, + 0xff, 0x00, 0xff, 0xfe, 0x01, 0x02, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x02, 0x01, 0xff, + 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0xff, 0xfc, 0xfe, 0xfd, 0xfc, 0xfe, 0x01, 0x02, 0x04, 0x05, 0x00, 0xfe, 0xff, 0xfd, + 0xfc, 0xfe, 0x01, 0xff, 0x01, 0x03, 0x01, 0xfd, 0xfd, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xfc, 0xfe, 0x05, 0x05, 0x00, 0xfe, 0x01, + 0xfe, 0xfc, 0xff, 0xff, 0x00, 0x02, 0x05, 0x02, 0x00, 0xff, 0xfe, 0xfb, 0xfb, 0xfd, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0xfe, 0x01, 0x02, 0xff, 0xfb, 0xfd, 0x02, 0x05, 0x01, 0xff, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, + 0xfe, 0xfe, 0xfd, 0xfd, 0xfe, 0x02, 0x05, 0x04, 0x01, 0xfe, 0xfb, 0xfc, 0xfe, 0xfe, 0xfe, 0xff, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x02, 0x01, 0xfe, 0xfd, 0xfe, 0xfe, 0xff, 0xfd, 0xfd, 0x00, 0x04, 0x00, 0xfa, 0xfd, 0x01, 0x00, 0xfb, 0xfc, + 0xff, 0x00, 0x02, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x01, 0x03, 0x03, 0x00, 0xfd, 0xfe, 0xff, 0x01, 0x02, 0xfe, 0xfd, 0xff, 0x00, 0xfd, + 0xfc, 0xff, 0x02, 0x02, 0x01, 0x01, 0x01, 0xff, 0x00, 0x04, 0x03, 0xff, 0xfe, 0x03, 0x04, 0x02, 0xff, 0x01, 0x00, 0xfe, 0xfe, 0xff, + 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0x00, 0xfd, 0xfb, 0xff, 0x02, 0x01, 0xfe, 0xfe, 0x00, 0x01, 0xff, 0x02, 0x00, 0xfc, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x01, 0xfd, 0xfb, 0xfc, 0x00, 0x01, 0xfd, 0xfd, 0xfe, 0x00, 0x01, 0x00, 0xfe, 0xfd, 0xff, 0x01, + 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0xfd, 0xfd, 0xff, 0xff, 0x00, 0x03, 0x04, 0x01, 0xfd, 0xfc, 0x00, 0x01, 0x00, 0xff, 0x00, 0xfe, + 0xfe, 0x01, 0xff, 0x01, 0x03, 0x01, 0x01, 0x00, 0xfd, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfe, 0xfc, 0xfb, 0x00, + 0x04, 0x02, 0x01, 0xfe, 0xfc, 0xff, 0x01, 0x00, 0xfe, 0x01, 0x03, 0x01, 0xfd, 0x00, 0x01, 0xff, 0xfd, 0x01, 0x02, 0xfe, 0xfd, 0xff, + 0x02, 0x01, 0xff, 0xfe, 0x00, 0x02, 0x03, 0x01, 0xfd, 0x00, 0xff, 0xfd, 0x00, 0x02, 0x01, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0x03, 0x04, + 0x00, 0xfa, 0x00, 0x02, 0xfd, 0xfe, 0x00, 0x01, 0x00, 0x02, 0x00, 0xfb, 0xff, 0x03, 0x03, 0xfe, 0xfc, 0xfe, 0x01, 0x00, 0x00, 0xfe, + 0xfc, 0x00, 0x00, 0xfc, 0xfa, 0xfd, 0xff, 0x00, 0x00, 0x03, 0x04, 0x01, 0xfd, 0xfb, 0xfd, 0x00, 0x03, 0x02, 0x01, 0x02, 0xfe, 0xfd, + 0x01, 0xfd, 0xff, 0x01, 0xfe, 0xfc, 0xfc, 0xfe, 0x01, 0xfe, 0xff, 0x04, 0x00, 0xfe, 0x00, 0x03, 0xfe, 0xff, 0x04, 0x03, 0x02, 0x01, + 0x02, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfd, 0x01, 0x02, 0xfe, 0xfd, 0x00, 0x03, 0x00, 0x00, 0x02, 0x03, 0x02, 0xff, 0xff, + 0x02, 0x00, 0x02, 0x05, 0x02, 0xff, 0xfd, 0xfd, 0xfd, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xfc, 0xfb, 0xfb, 0xfd, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x01, 0xfe, 0xfd, 0xff, 0xff, 0xfe, 0x02, 0xff, 0xfb, 0xfe, 0x01, 0xff, 0xfc, 0xfb, 0xfc, 0xfd, 0xfc, 0xfd, 0xfd, 0xfe, + 0x01, 0x00, 0xfe, 0xfe, 0x02, 0x03, 0x02, 0x01, 0xff, 0x01, 0x03, 0x00, 0xff, 0xff, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0xfd, 0x00, 0x02, + 0x00, 0xfd, 0x00, 0x04, 0x01, 0xfe, 0xff, 0x04, 0x04, 0x03, 0x03, 0x02, 0x00, 0x02, 0x05, 0x03, 0xff, 0xfd, 0x02, 0x01, 0x00, 0xff, + 0xff, 0xff, 0xfd, 0xfc, 0xff, 0x01, 0x01, 0xff, 0x00, 0xff, 0xfe, 0x04, 0xfe, 0xfa, 0x00, 0x00, 0xfe, 0xfd, 0xfc, 0x01, 0x02, 0xfd, + 0xfd, 0xff, 0xff, 0xfc, 0x02, 0x00, 0xf9, 0xf7, 0xfc, 0x00, 0x00, 0x00, 0x03, 0x01, 0xf8, 0xfe, 0x02, 0x01, 0x00, 0x00, 0x01, 0xff, + 0xfd, 0xfc, 0xfc, 0xfe, 0x02, 0x02, 0x00, 0x00, 0xfd, 0xfc, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x01, 0x00, 0x00, + 0xff, 0xfd, 0xff, 0x06, 0x01, 0xfd, 0x00, 0x05, 0xfe, 0xfa, 0xff, 0x00, 0x00, 0x00, 0x03, 0xfe, 0xfe, 0x03, 0x04, 0x01, 0x00, 0x03, + 0x02, 0x00, 0xff, 0xff, 0x00, 0x01, 0x02, 0xff, 0xfb, 0xf9, 0xf8, 0xfd, 0x00, 0x00, 0xfe, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0xfe, 0x01, 0x03, 0x03, 0x01, 0xfd, 0xfb, 0xfe, 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xfd, 0xfb, 0xff, 0xff, 0xfe, 0x00, + 0x02, 0x01, 0xff, 0x03, 0x00, 0xff, 0x03, 0x06, 0x02, 0xfb, 0xfd, 0xfe, 0xff, 0x01, 0x05, 0x00, 0xfc, 0x00, 0x00, 0xfe, 0xfd, 0xfe, + 0xff, 0xff, 0xfe, 0x01, 0x01, 0xfe, 0xff, 0x02, 0x03, 0x01, 0xff, 0xff, 0x01, 0x02, 0x03, 0x03, 0x02, 0x00, 0xfc, 0xfc, 0xff, 0x00, + 0xfe, 0xfe, 0x01, 0x00, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0x00, 0xfd, 0xff, 0x01, 0x00, 0x02, 0x01, 0xff, 0x00, 0xfe, 0xfd, 0xff, 0x00, + 0x00, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xfb, 0xfa, 0xff, 0x01, 0x00, 0xff, 0x00, 0x03, 0x05, 0x03, 0xfd, 0xfc, 0x00, 0x03, 0x00, + 0x01, 0x03, 0x01, 0x00, 0xfe, 0xfb, 0xfe, 0x01, 0x02, 0x01, 0x00, 0x01, 0x02, 0xfe, 0x00, 0x01, 0xfe, 0xfd, 0xff, 0x01, 0x01, 0xfe, + 0xfd, 0x00, 0x03, 0x02, 0x00, 0xff, 0x01, 0xfd, 0xfb, 0xfe, 0x05, 0x02, 0xfb, 0x00, 0x00, 0xfe, 0xfc, 0x02, 0x01, 0xfc, 0xfe, 0x02, + 0x02, 0xff, 0xfd, 0xff, 0x01, 0xfe, 0xfe, 0xfe, 0xff, 0x03, 0xff, 0xfe, 0x01, 0x00, 0xff, 0x00, 0x02, 0xff, 0xfd, 0xfd, 0xfe, 0x00, + 0x02, 0x03, 0xfd, 0xfe, 0x02, 0x02, 0xfe, 0xff, 0x02, 0x01, 0xfe, 0x00, 0x04, 0xff, 0xfe, 0xfe, 0xfc, 0xff, 0x01, 0x01, 0x01, 0xff, + 0xff, 0x03, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x03, 0x01, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x01, 0xfe, 0xfd, 0xff, 0x02, 0x00, + 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0xfe, 0xfa, 0xfc, 0x01, 0x02, 0x01, 0xff, 0xfe, 0xfc, 0xfd, + 0xfe, 0xfd, 0xfe, 0xfd, 0xfc, 0xff, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, 0xfd, 0xfe, 0x01, 0x04, 0x02, 0x00, 0x01, 0x04, 0x02, + 0xff, 0x00, 0xfc, 0xfd, 0xff, 0xfd, 0xfe, 0x02, 0x05, 0xfe, 0xfc, 0xff, 0x02, 0x00, 0xfe, 0x00, 0x04, 0x04, 0x02, 0x02, 0x01, 0x00, + 0x00, 0x01, 0xfd, 0xff, 0x03, 0x00, 0xfd, 0xff, 0x02, 0x00, 0xff, 0xff, 0xfe, 0xfc, 0xfd, 0xff, 0xfe, 0xff, 0x02, 0x03, 0x02, 0x02, + 0x03, 0x00, 0xff, 0xfe, 0xfd, 0xff, 0x00, 0xff, 0x00, 0xfe, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0x01, 0xff, 0xfc, 0xfc, 0xff, 0xff, + 0xff, 0x01, 0x01, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x02, 0x01, 0x01, 0x03, 0xff, 0xfb, 0xfb, 0x00, 0x01, 0x00, 0x00, 0x02, 0xff, 0xfc, + 0xfc, 0x03, 0x04, 0x01, 0xfe, 0x00, 0x01, 0x00, 0x02, 0x01, 0xff, 0xff, 0x00, 0xff, 0x00, 0x03, 0xfe, 0xfe, 0x02, 0x03, 0xff, 0xfe, + 0x01, 0xff, 0x00, 0x01, 0xfe, 0xfd, 0xfe, 0x00, 0x02, 0x02, 0x00, 0xff, 0xff, 0xfe, 0xfd, 0x00, 0x03, 0x03, 0xff, 0xfd, 0xfd, 0xfe, + 0xff, 0xfd, 0xfd, 0x00, 0x01, 0xff, 0xfd, 0xfe, 0xff, 0x04, 0x05, 0x00, 0xfe, 0xfd, 0xfd, 0xfc, 0xfe, 0x02, 0x04, 0xff, 0x00, 0x00, + 0xfc, 0xfc, 0xfd, 0x00, 0x02, 0x00, 0xff, 0x01, 0x01, 0xfe, 0xff, 0x03, 0x00, 0xff, 0x00, 0xff, 0x00, 0x01, 0x02, 0x01, 0xff, 0xfe, + 0xfd, 0xfc, 0xfc, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x01, 0x01, 0x03, 0xff, 0xfc, 0xfd, 0x03, 0x01, 0xfd, 0x00, 0x05, 0x03, 0xfe, + 0xff, 0xfd, 0xfd, 0x00, 0x01, 0x01, 0x00, 0xfd, 0xfa, 0xfd, 0x01, 0x01, 0x01, 0x00, 0xfe, 0xfd, 0xff, 0x00, 0xff, 0x03, 0x05, 0x03, + 0x02, 0x00, 0xfd, 0xfb, 0x00, 0x02, 0x00, 0xfe, 0xff, 0x00, 0xfe, 0x00, 0xff, 0xfe, 0x00, 0xfe, 0xff, 0x00, 0xfe, 0xfd, 0xff, 0x02, + 0x00, 0x01, 0x01, 0xff, 0x01, 0x03, 0x02, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x01, 0xfd, 0xfc, 0xfd, 0xfe, 0xfd, 0xfe, 0x01, + 0xfd, 0xff, 0x04, 0x04, 0xff, 0xff, 0x01, 0x01, 0xfd, 0xfc, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x00, 0xfa, 0xfb, 0xfe, 0xff, + 0x02, 0x00, 0xff, 0x03, 0x00, 0xfe, 0xff, 0xfe, 0xfc, 0xfd, 0x00, 0x01, 0x01, 0x02, 0x03, 0x01, 0xff, 0xff, 0x00, 0xfe, 0xfd, 0xfd, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x03, 0x01, 0xfc, 0xfa, 0xfd, 0x01, 0x00, 0x01, 0x03, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, + 0xff, 0x01, 0x06, 0x00, 0x00, 0x00, 0xfe, 0xfc, 0xff, 0x01, 0xfb, 0xfe, 0x02, 0x03, 0x02, 0x01, 0xfe, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, + 0xff, 0xfe, 0xff, 0x02, 0x02, 0xff, 0xfd, 0xfc, 0xfe, 0x01, 0xff, 0x00, 0x02, 0x01, 0xfe, 0xff, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xff, + 0x00, 0x00, 0xfe, 0x01, 0x02, 0x00, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0x01, 0x02, 0x02, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0x01, 0x03, 0x05, + 0x00, 0xfe, 0x03, 0x00, 0xfc, 0xfc, 0x02, 0x00, 0xfd, 0xfd, 0xfe, 0x01, 0x02, 0x01, 0x01, 0xff, 0xfd, 0xfe, 0x00, 0x01, 0x00, 0x02, + 0x01, 0x00, 0x02, 0x00, 0xfe, 0xff, 0x00, 0xff, 0xff, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0xfc, 0xfd, 0xff, 0x01, 0x01, + 0x00, 0xff, 0x00, 0xfd, 0xfe, 0xff, 0xfe, 0xff, 0x02, 0x03, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xfd, 0xff, 0x01, 0x00, 0xfe, 0x00, + 0x01, 0x01, 0xff, 0xfc, 0xfd, 0x01, 0x01, 0x00, 0x00, 0x03, 0x01, 0x01, 0x02, 0x01, 0xfe, 0xfd, 0x00, 0x02, 0xfe, 0xfc, 0x01, 0x01, + 0x00, 0xff, 0xfe, 0xff, 0xfe, 0xfd, 0xfc, 0x00, 0x03, 0x00, 0xff, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x03, 0x00, 0xff, + 0xff, 0xff, 0xfe, 0xff, 0x01, 0x01, 0xfe, 0xfd, 0xff, 0xff, 0x00, 0xff, 0xfd, 0xfd, 0xfe, 0x01, 0x02, 0x00, 0xff, 0xff, 0x01, 0x01, + 0xff, 0xff, 0x00, 0xfe, 0xff, 0x03, 0x03, 0x00, 0xfd, 0xfe, 0x01, 0x01, 0xfc, 0xfd, 0x00, 0x01, 0xff, 0x01, 0x01, 0xff, 0x00, 0x00, + 0xff, 0xfe, 0x01, 0x01, 0xff, 0xff, 0x01, 0x01, 0xff, 0xfe, 0x01, 0x02, 0xfd, 0xfe, 0xff, 0xff, 0xfc, 0x01, 0x03, 0xff, 0xfc, 0xfe, + 0x00, 0xfd, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xfe, 0xfc, 0xfe, 0x01, 0x01, 0x00, 0xff, 0xff, 0x00, 0x01, + 0x01, 0x00, 0xfe, 0xfd, 0xfe, 0xfe, 0xff, 0x01, 0x00, 0x00, 0xfe, 0xfd, 0xff, 0xfe, 0xfe, 0x00, 0x02, 0x03, 0x01, 0xfe, 0x01, 0x01, + 0x01, 0x03, 0x01, 0xff, 0xff, 0xff, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfe, 0xff, 0xfe, 0x00, 0x01, + 0x01, 0x01, 0x01, 0xff, 0xfc, 0xfe, 0x01, 0x02, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xfd, 0xfd, + 0xff, 0xff, 0x01, 0x01, 0xfc, 0xfe, 0xfe, 0xfd, 0xff, 0x05, 0x04, 0xfe, 0xfd, 0xfd, 0xff, 0x03, 0xff, 0xfd, 0xff, 0xff, 0xfe, 0xff, + 0x00, 0xfd, 0xff, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0x00, 0x01, 0x00, 0xff, 0x01, 0x02, 0x01, 0x02, 0x03, 0x02, 0xfe, 0x00, 0x02, + 0x01, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x02, 0xff, 0xfd, 0xff, 0xff, 0xfe, 0x00, 0x01, 0x02, 0x01, 0xff, 0xfd, 0xfd, 0xff, + 0x00, 0xff, 0xfe, 0x00, 0x03, 0xff, 0xfe, 0x01, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x01, 0x01, 0xfd, + 0xf9, 0xfd, 0x00, 0x00, 0x02, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x03, 0x02, 0xff, 0xff, 0xfe, 0xfd, + 0xfd, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0xff, + 0xfe, 0x01, 0x03, 0x01, 0x01, 0xfe, 0xfe, 0x00, 0xfe, 0xfd, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x02, 0x01, 0xff, 0xfe, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0xfd, 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfc, 0xfc, 0xff, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0xfe, 0xfd, 0xfe, 0xfe, 0x00, 0x01, 0x00, + 0xfe, 0xfd, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xfe, 0xfd, 0xff, 0x02, 0x02, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0x00, + 0x02, 0x01, 0xff, 0xfd, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x02, 0x02, 0x00, + 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xfd, 0xff, 0xfe, 0xff, 0x01, 0x02, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x03, + 0x01, 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, 0x01, 0x02, 0x02, 0x00, 0xff, 0xff, 0xff, 0xfd, 0x02, 0xff, 0xfd, 0x00, 0x02, 0x00, 0xfd, 0xfe, + 0x00, 0x00, 0xfe, 0x00, 0xff, 0xfe, 0xfe, 0x03, 0x01, 0xfc, 0xfe, 0x02, 0x03, 0x00, 0xfd, 0xfe, 0x02, 0x04, 0x03, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x01, 0xff, 0xfe, 0x00, 0xfe, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0xfd, 0x00, 0x03, 0x01, 0xff, + 0xfe, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xff, 0x03, 0x02, 0xfd, 0xff, 0xfe, 0xfd, 0xfe, 0xfe, 0x00, 0x02, 0x01, 0xff, 0xfe, 0xff, 0xff, + 0xff, 0xff, 0xfc, 0x00, 0x03, 0x03, 0xff, 0xfd, 0xff, 0x01, 0xfd, 0xfe, 0x00, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x01, 0xfe, + 0xfe, 0x00, 0xfe, 0xfd, 0x00, 0x03, 0x03, 0x02, 0x01, 0x00, 0x01, 0xff, 0xfd, 0x01, 0x04, 0x03, 0x00, 0x02, 0xfe, 0xfc, 0xff, 0xff, + 0xfd, 0xfc, 0xff, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0x03, 0x04, 0xff, 0x00, 0xfe, 0xfd, 0x02, 0x02, + 0x00, 0xfe, 0xfe, 0xff, 0x01, 0x02, 0x00, 0xfe, 0xff, 0x01, 0xfc, 0xfc, 0xff, 0xff, 0x00, 0x01, 0x02, 0xff, 0xff, 0xff, 0x00, 0xfe, + 0xfd, 0xfe, 0x01, 0x01, 0xff, 0xfe, 0x00, 0x01, 0xfe, 0xfb, 0xfd, 0xff, 0x00, 0xfe, 0xff, 0x01, 0x02, 0xff, 0xff, 0xff, 0xfb, 0xff, + 0x01, 0xff, 0x01, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0xfd, 0x01, 0x03, 0xff, 0x00, 0x02, 0x02, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x02, 0x00, 0xfe, 0xfe, 0x00, 0xfc, 0xfd, 0x03, 0x05, 0x01, 0xff, 0x00, 0xfd, 0xfd, + 0xff, 0x01, 0xfe, 0xff, 0x01, 0xff, 0xfd, 0xff, 0x02, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfb, 0xfa, 0x02, 0x02, 0x00, 0x01, 0xff, 0xff, + 0x00, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0x01, 0x01, 0xff, 0xfb, 0xfe, 0x01, 0x00, 0xfe, 0xff, 0x01, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x02, + 0x02, 0xfd, 0xfe, 0x00, 0x00, 0xff, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0xfe, 0xfe, 0x01, 0x03, 0xfd, 0xfe, 0x02, 0xff, 0xfe, 0xff, + 0x01, 0xfe, 0xff, 0x00, 0xff, 0x02, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x02, 0xff, 0xfe, 0xff, 0x00, 0xfd, 0xfd, + 0x00, 0x01, 0xff, 0xfc, 0xfa, 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0xfd, 0xfd, 0x02, 0x03, 0x02, 0x01, 0x03, 0x01, 0xfd, 0xfd, 0xff, 0x01, + 0x02, 0x01, 0x00, 0xff, 0xfd, 0xfe, 0xff, 0xff, 0x01, 0xfe, 0xfc, 0xfe, 0x00, 0xff, 0xfe, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x02, 0x00, 0xfe, 0xfd, 0x00, 0x02, + 0xff, 0xfe, 0x02, 0x03, 0xfc, 0xfd, 0x00, 0x02, 0x00, 0x00, 0x00, 0xfc, 0xfe, 0xff, 0xfd, 0xfd, 0x00, 0x01, 0xfd, 0xfc, 0xfd, 0xfe, + 0xfd, 0x01, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xfe, 0xfe, + 0x01, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x02, 0xfe, 0xfe, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x01, 0x01, + 0xff, 0xfe, 0xfe, 0x00, 0xff, 0x01, 0x02, 0xfd, 0x00, 0x04, 0x04, 0x00, 0xfe, 0xfd, 0xfe, 0xfc, 0xfd, 0x00, 0x01, 0xff, 0x01, 0x03, + 0xfc, 0xfe, 0x00, 0xfd, 0xff, 0xff, 0xfe, 0xfc, 0x01, 0x03, 0x00, 0x00, 0xff, 0xfd, 0xfc, 0xfc, 0xff, 0x02, 0xfc, 0xff, 0x03, 0x01, + 0xff, 0xfe, 0xfd, 0xfc, 0xfc, 0xfe, 0x00, 0xff, 0x01, 0x02, 0x02, 0x02, 0x01, 0xfe, 0xfc, 0xff, 0x00, 0x00, 0x01, 0x03, 0x03, 0x01, + 0xff, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x01, 0xff, 0xff, 0x01, 0x02, 0x02, 0x00, 0xfe, 0xfe, 0x00, 0x01, 0x00, 0x00, 0x01, 0x03, 0x04, + 0x01, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0xfd, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xfc, 0xfd, 0x02, 0x02, 0x00, 0x00, 0x01, + 0x00, 0xff, 0x00, 0xff, 0xff, 0x01, 0x02, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfb, 0xfc, 0x00, 0xfe, 0xfe, 0xff, 0x00, + 0xfe, 0xfd, 0xfe, 0x02, 0x01, 0xff, 0xff, 0x03, 0x01, 0xfc, 0xfe, 0x00, 0x01, 0x00, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x02, 0x01, 0xfe, 0xfe, 0x02, 0x03, 0x01, 0x01, 0x02, 0x03, 0x01, 0x00, 0x02, 0x02, 0xfc, 0x00, 0x01, 0xfe, 0xff, + 0xfe, 0xfe, 0x00, 0xfd, 0xfe, 0x01, 0x02, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0x00, 0x02, 0x02, 0xff, 0xfd, 0x00, 0x00, 0x01, 0x02, 0x00, + 0xfe, 0xfe, 0xff, 0xfd, 0xff, 0x01, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, + 0xfe, 0xff, 0xff, 0x01, 0x01, 0x00, 0xfe, 0xfd, 0xfe, 0x01, 0x03, 0x01, 0x00, 0x01, 0x01, 0xfe, 0xfd, 0xfe, 0x00, 0x01, 0xff, 0x01, + 0x00, 0xfe, 0xfe, 0x01, 0x03, 0x01, 0xfe, 0xff, 0x00, 0x00, 0xfc, 0xfe, 0x03, 0x04, 0x02, 0xff, 0xfe, 0x00, 0xff, 0xfe, 0x00, 0x01, + 0x00, 0xff, 0x01, 0xff, 0xfd, 0xfe, 0xfc, 0xff, 0x01, 0xfd, 0x00, 0x02, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x00, 0xfe, 0xfe, 0x01, 0xff, + 0xff, 0x00, 0xff, 0x01, 0x02, 0x02, 0x01, 0x00, 0xfe, 0xfd, 0xfc, 0xff, 0x02, 0xff, 0x00, 0x00, 0xfe, 0xfd, 0xff, 0x01, 0x01, 0xff, + 0xff, 0x00, 0x01, 0x01, 0x01, 0x02, 0x04, 0x00, 0xfd, 0xff, 0xfe, 0xfd, 0xff, 0xff, 0x02, 0x03, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xfe, + 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x02, 0x01, 0x02, 0x03, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0xff, 0xfd, 0xfd, + 0xfe, 0xff, 0xfd, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfd, 0xfd, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0xff, 0xfe, 0xfe, 0x01, + 0x01, 0xfe, 0xff, 0xfd, 0xfc, 0x00, 0x00, 0xff, 0x00, 0x01, 0xfe, 0xfc, 0xfe, 0x00, 0x01, 0x01, 0x04, 0x02, 0xff, 0xff, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x01, 0xfd, 0xfd, 0x01, 0x03, 0xfe, 0xfc, 0xfe, 0x00, 0x00, 0x01, 0x02, 0x02, 0xfe, 0xfe, + 0x02, 0x03, 0x01, 0xff, 0xff, 0xfd, 0xff, 0x01, 0xff, 0x00, 0x01, 0x00, 0xfd, 0xfd, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xff, 0x02, 0x01, 0xfe, 0xfd, 0x01, 0xff, 0xfc, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x00, 0xfe, 0xfb, 0xfe, 0x01, 0x01, 0xff, 0xfe, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x02, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x00, 0x02, 0x03, + 0x02, 0xff, 0xff, 0x01, 0x01, 0xfe, 0xff, 0x02, 0x02, 0xff, 0xff, 0x03, 0xff, 0xff, 0x00, 0xfe, 0xff, 0x00, 0xff, 0xfd, 0x00, 0x02, + 0xff, 0xff, 0x01, 0x02, 0xfd, 0xff, 0x00, 0xff, 0xfe, 0x01, 0x02, 0x00, 0xff, 0xfe, 0xfd, 0xfe, 0xff, 0xfe, 0xfc, 0xfe, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x01, 0x01, 0x00, 0xff, 0xfe, 0x01, 0x03, 0x02, 0xfe, 0xfd, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xfe, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x01, 0x02, 0x01, 0xff, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, + 0xff, 0xff, 0xfe, 0xfd, 0x00, 0xff, 0xfe, 0x01, 0x01, 0x00, 0xff, 0x00, 0xfe, 0xfd, 0xfe, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x00, 0xfe, + 0xff, 0x00, 0x00, 0x00, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x02, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x03, + 0x01, 0xff, 0xff, 0x00, 0x02, 0x01, 0xff, 0xff, 0xfd, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x01, 0x00, 0xfe, 0xfd, 0xfe, + 0x00, 0x02, 0x01, 0x00, 0x00, 0xff, 0xfc, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0xfd, 0x00, 0x02, 0xff, 0xfd, 0xfd, 0xff, 0xff, + 0xfe, 0xff, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0xff, 0xfd, 0xfd, 0x00, 0x03, 0x03, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xfd, + 0xfe, 0x00, 0x01, 0xff, 0xfe, 0xff, 0x02, 0xff, 0xfd, 0xff, 0x03, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x02, 0xff, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfd, 0xff, 0x01, 0x01, 0x00, 0xfd, + 0xfe, 0x01, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0xff, 0xfc, 0xfd, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xfc, 0xff, 0x01, 0x01, 0xfe, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x01, 0x00, + 0xfe, 0xfd, 0xfe, 0x01, 0x01, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x02, 0x02, 0x01, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00, + 0x00, 0x01, 0x02, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0x00, 0x01, 0x01, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0x00, 0x00, + 0x00, 0x01, 0xff, 0x00, 0x01, 0x00, 0x00, 0xff, 0x00, 0x01, 0x01, 0xff, 0xfd, 0xfe, 0x01, 0x02, 0x00, 0xff, 0xff, 0xfe, 0xfd, 0x00, + 0x02, 0xff, 0xff, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x00, 0x02, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0x00, 0x00, + 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x01, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x01, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0xff, 0xff, 0x00, 0x02, 0x01, 0x00, 0x00, 0xfe, 0xfd, 0x00, 0x01, 0xff, 0xfd, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xfe, 0xfd, 0xff, 0x01, 0xff, 0xfd, 0xfd, 0x00, 0x02, 0x00, 0x00, 0x02, 0xff, 0xfe, 0xfd, 0xfd, 0x00, 0x02, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xfd, 0xfd, 0xff, 0xfe, 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, + 0x00, 0xfe, 0x00, 0x02, 0x01, 0xfe, 0xff, 0x02, 0x02, 0x00, 0xff, 0x01, 0xff, 0xfe, 0xfe, 0x01, 0x00, 0x01, 0x02, 0x01, 0xff, 0xfe, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x02, 0x00, 0xfe, 0xfd, 0xfe, 0x00, 0xff, 0xfd, + 0xfd, 0xfd, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfd, + 0xfe, 0x00, 0xff, 0xfe, 0x00, 0x01, 0xff, 0xfe, 0x00, 0x00, 0xfd, 0xfd, 0xff, 0x00, 0x00, 0x03, 0x01, 0xff, 0x00, 0x02, 0x00, 0xfe, + 0x00, 0xff, 0xff, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xfe, 0xfd, 0xfd, 0x00, 0x01, 0xff, 0x01, 0x00, 0x00, 0x01, 0xff, 0x00, 0x01, + 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0x00, 0x02, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x02, 0x02, 0xfd, 0xfd, 0xff, 0x00, + 0xff, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xfd, 0xfd, 0xff, 0xff, 0xfe, + 0x00, 0x00, 0xff, 0xfe, 0x02, 0x02, 0x00, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x01, 0x00, 0xff, 0xff, 0xfd, 0xfd, + 0x00, 0x01, 0x00, 0xff, 0x01, 0x00, 0xfe, 0x00, 0xff, 0xff, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0xfe, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xfd, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0x00, 0xfe, 0xfe, 0x00, + 0x00, 0xfe, 0xfe, 0xff, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0x01, 0xff, 0x00, 0x01, 0xfe, + 0xfd, 0xff, 0x01, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, + 0xfe, 0xff, 0x00, 0xff, 0xfc, 0xfd, 0x01, 0x01, 0xff, 0xfe, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0x02, + 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xfd, 0xfb, 0xfc, 0x01, 0x00, + 0x01, 0x01, 0xfe, 0xff, 0x00, 0x00, 0xfc, 0xfe, 0x01, 0x01, 0xff, 0x01, 0x02, 0x00, 0xff, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xfe, + 0xfe, 0xff, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x01, 0x02, 0x01, 0xff, 0xff, 0x00, 0x01, 0xff, 0xfe, 0xfe, 0xff, 0x00, + 0x01, 0x00, 0xff, 0x00, 0xff, 0xfe, 0x01, 0xff, 0xfd, 0xff, 0x01, 0x00, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x00, 0x02, 0x01, 0xff, + 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x01, 0xff, 0xff, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xff, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xfd, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x01, 0xff, 0xfd, 0xff, 0x00, 0xff, 0xff, 0x01, 0x01, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0x01, 0xff, 0xfe, 0xff, 0xfe, 0xfc, + 0xfe, 0x03, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0xfe, 0xfd, 0xff, 0x02, 0x01, 0xff, 0xff, 0x00, 0xff, + 0xfe, 0xff, 0xfe, 0xfe, 0x00, 0x02, 0x02, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0x02, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0xfe, + 0xff, 0x01, 0xff, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x02, 0x02, 0xff, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xfc, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0xff, 0xfd, + 0xfe, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x02, 0x01, 0xff, 0x00, 0xfe, 0xfe, 0xfe, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, + 0xfd, 0xfe, 0x00, 0x01, 0xfe, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xfc, 0xff, 0x01, 0x00, 0xfd, 0x00, 0x02, 0x01, 0xff, 0xff, 0x00, + 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, + 0x00, 0xfd, 0xfe, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x01, 0x01, + 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, + 0xff, 0xfe, 0xfe, 0x00, 0x01, 0xff, 0xfd, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x01, 0x01, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0xff, 0x00, 0x02, 0x02, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x03, + 0x01, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x01, 0xfe, 0xfe, 0x01, 0x00, 0xfd, 0xfe, 0x01, + 0x00, 0xff, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0x00, 0xff, 0xfe, 0xfe, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x02, 0x01, 0xfe, 0xfe, 0x00, 0x01, 0xfd, + 0xff, 0x02, 0x02, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x01, 0xff, 0x01, 0x02, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, + 0x01, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x01, 0xff, 0xfd, 0xfe, 0x00, 0x00, 0xff, 0x01, + 0x01, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0x00, 0x01, 0xff, 0xfe, 0x00, 0x02, 0x00, 0xfd, 0xfc, 0xfd, 0xfe, 0x00, 0x00, 0x00, 0x02, + 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x02, 0x00, + 0xff, 0xfe, 0xfe, 0x00, 0xff, 0xfe, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xfd, 0xfe, 0xfe, 0xff, 0x03, 0x03, 0xff, 0xff, + 0x00, 0x00, 0xfd, 0xff, 0x01, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x01, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0xfe, 0xfd, 0xfd, 0xff, 0x01, 0x01, 0x00, 0x00, 0xff, 0x00, + 0xfe, 0xfd, 0x00, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff, 0x00, 0xff, 0xfe, 0x01, 0x01, 0x00, 0x00, 0x02, 0xff, + 0xfd, 0xfe, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xfe, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xfe, 0x01, + 0x01, 0xfd, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x00, 0xfd, 0xff, 0x00, 0xff, 0xfc, 0xff, 0x00, 0xff, 0x01, 0x02, 0x00, 0xfd, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, + 0xff, 0x02, 0xfe, 0xfe, 0x01, 0x01, 0xff, 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0x01, 0x03, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, + 0xff, 0x00, 0x02, 0x02, 0xff, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x00, + 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xff, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x02, 0x01, 0xfe, 0xfd, 0x00, 0x00, 0xff, + 0xff, 0xfd, 0xfd, 0xff, 0x01, 0xfe, 0xfe, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x01, + 0xff, 0xfe, 0xff, 0x01, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x02, 0x00, 0xfd, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, + 0x01, 0x02, 0x00, 0xfc, 0xfe, 0xff, 0xfe, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x01, 0xff, 0xff, 0x02, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0xfd, 0xfe, 0x01, 0x02, 0x01, 0x00, 0x01, + 0xff, 0x00, 0x01, 0x00, 0xfd, 0xfd, 0xfe, 0x00, 0xff, 0xfe, 0xff, 0x01, 0xff, 0xfd, 0x00, 0x00, 0x01, 0x01, 0xff, 0x00, 0x00, 0xfe, + 0x01, 0x02, 0x00, 0xfe, 0xff, 0xff, 0xfe, 0x00, 0xff, 0xfe, 0xfe, 0x01, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0xff, 0xfe, 0x00, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x02, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xff, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, + 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xfe, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xfd, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xfe, 0xfe, 0xfe, 0x00, + 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x02, 0x01, 0xfe, 0xfe, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x02, 0x02, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x02, 0xff, 0xfe, 0x00, 0xff, 0x00, 0xff, 0xfe, 0xfe, + 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xfd, 0xfe, 0xff, 0xfd, 0xfe, 0x00, 0xff, 0xfd, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x01, 0x01, 0x01, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0xff, + 0xff, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x01, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, + 0xff, 0xfe, 0x00, 0x01, 0x01, 0x01, 0xfe, 0xfc, 0xfd, 0xff, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xfe, 0x00, 0x01, 0xff, 0xfd, 0xfe, + 0xff, 0x01, 0x01, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0xff, 0xfe, 0x00, 0x01, 0x00, 0xfe, 0xff, 0x01, 0x02, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, + 0xff, 0x01, 0x00, 0xfe, 0xfe, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x00, 0xfd, 0xfd, 0x00, 0x02, 0x00, 0xff, 0xff, + 0x01, 0x01, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, 0xfe, 0xfd, 0xfe, 0xfe, 0xfe, 0x00, + 0x01, 0x02, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0xfe, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x02, 0x02, 0x00, 0x01, 0x00, 0xfe, 0xfd, 0xfe, 0xff, + 0x01, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x01, 0xff, 0xfe, 0xff, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xfe, + 0xfd, 0x00, 0x01, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x01, 0x01, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x01, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0x00, 0x02, 0x01, 0x00, 0xff, 0xff, + 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfd, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x01, 0x02, + 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xfd, 0xfd, 0xfe, 0x00, 0x02, 0x01, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0x00, 0xff, 0xff, 0x01, 0x00, 0xfe, + 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xfe, + 0xfd, 0xfe, 0x00, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xfc, 0xfe, 0x00, 0x01, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0xfe, + 0xff, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xfe, 0x01, 0x02, 0x01, 0xfe, + 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x02, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x01, 0x00, 0x01, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x02, + 0x01, 0xfe, 0xfe, 0x01, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, + 0x00, 0xff, 0x01, 0x00, 0xff, 0xfe, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xfe, + 0xff, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0xfd, 0xff, + 0x00, 0xff, 0xfe, 0x00, 0x01, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x01, 0x01, 0xff, 0x00, 0xff, + 0xfe, 0xff, 0x00, 0xff, 0x00, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, + 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0x01, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, + 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0x00, + 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x01, 0x01, 0x00, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x02, + 0x02, 0x00, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, + 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, + 0xff, 0xfe, 0xff, 0x00, 0xfd, 0xfe, 0x00, 0x01, 0xff, 0xfe, 0xfe, 0xff, 0xfd, 0xfe, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfd, 0xff, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, + 0xfe, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xfe, + 0x00, 0xfe, 0xfd, 0xff, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xfe, 0xfd, 0xff, 0x01, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x01, 0x02, 0x00, 0xfe, 0xff, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0xfe, 0xff, 0x00, 0x00, + 0xff, 0x01, 0x02, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x01, 0x01, 0xfe, 0xff, 0x00, 0xfe, 0xfd, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0x00, + 0x01, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0xff, 0x00, 0xfe, 0xfe, 0xff, + 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x01, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x01, 0x02, 0x01, 0x00, 0xff, 0xfe, 0x01, 0x00, 0xfe, 0xff, 0x01, 0x00, 0xfd, 0xfd, 0xff, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0xfd, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0xfe, 0xfe, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0x00, 0x03, 0x02, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xfe, 0x01, + 0x01, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x02, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfd, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xfe, 0x00, 0x01, 0xfe, 0xff, 0x01, 0x00, 0xfe, 0xfc, 0xfe, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, + 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x02, 0x01, 0xff, 0xfe, 0x01, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x02, 0x01, + 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x02, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfd, 0xfe, 0x01, 0x01, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, + 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xfe, 0xfd, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x01, 0xff, 0xfe, + 0xff, 0x00, 0xfe, 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x01, 0x02, 0xff, 0xfe, + 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x02, 0x01, 0x01, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0x01, 0x02, 0x01, 0xfe, 0xff, 0xff, + 0xfe, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff, 0xfd, 0xfd, 0xfe, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xfd, 0xfd, 0xff, 0xff, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x02, + 0x00, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, + 0x02, 0x00, 0xfd, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x01, 0xff, 0xfd, 0xfe, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x00, 0x02, 0x03, 0x00, + 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, + 0x01, 0x01, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xfd, + 0xfe, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x00, + 0xff, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xfc, 0xfe, 0x01, 0x01, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x01, + 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, + 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x01, 0xff, 0xfe, 0x00, 0x00, 0xff, 0x00, 0x02, 0x01, + 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x01, 0x01, 0xff, 0xfe, 0xff, + 0x01, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x02, 0x01, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x02, 0x01, 0xff, 0xff, 0xff, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, + 0x00, 0xff, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0xff, 0xff, 0x00, 0xfe, 0xfe, + 0xff, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0xfe, 0xfd, 0xfe, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0xfe, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x01, 0x01, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, + 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0xff, 0xfd, 0x01, 0x01, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, + 0x02, 0x01, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0xfe, + 0xfe, 0xff, 0x00, 0x01, 0xff, 0xfe, 0x01, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xfe, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x02, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x01, 0xff, 0xfe, + 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x01, 0x01, + 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x01, 0x00, 0xff, 0xfe, 0x01, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x01, 0x01, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0xfe, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0xfe, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfd, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, 0xfd, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0x01, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfd, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xfe, 0x00, 0x01, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0x00, 0xfe, 0xfe, + 0x01, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x01, 0x00, 0x00, 0xfe, 0xfd, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x02, 0x01, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x01, 0x01, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0x00, 0x00, 0xfe, 0xfd, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0x00, 0x01, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xfe, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, + 0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0xfe, 0xfd, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0xfe, + 0xfe, 0xff, 0x01, 0xff, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xfe, 0xfe, 0xff, 0xff, 0x01, + 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x02, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x01, 0x00, + 0xff, 0xfe, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, + 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x02, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0x01, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, + 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xff, + 0x00, 0x01, 0x01, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0xfe, 0xfe, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0x01, 0x00, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x01, 0xff, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, + 0xfe, 0x00, 0x01, 0x01, 0x01, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x01, 0x00, 0xff, 0x00, 0x01, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xfe, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, + 0x01, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfd, 0xfe, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0xff, + 0x00, 0x01, 0x00, 0xfe, 0xfd, 0xfe, 0xff, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0xff, 0xfe, + 0xff, 0x01, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x01, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x01, 0x01, 0x00, 0xff, 0x01, 0x00, 0xff, 0x00, 0x01, 0x00, + 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0x01, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0x01, 0x02, 0x01, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x01, 0x01, 0x00, 0x01, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xfe, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x02, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x00, 0xff, 0xff, + 0xfe, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xfe, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, + 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xfe, 0xfd, 0xff, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x01, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xfe, 0xff, 0x01, 0x01, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, + 0xfe, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x02, 0x01, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfe, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0xfe, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xff, 0x01, 0x01, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0xfe, 0x00, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xfd, 0xfe, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0xff, 0xfe, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0xff, 0xff, 0x00, + 0x01, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xfe, 0x00, 0xfe, + 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x01, 0x00, 0xff, 0xff, 0x00, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x01, 0x01, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0x01, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, + 0xfe, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x01, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xfe, 0xfe, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x01, 0x01, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xfe, 0xff, + 0xff, 0x00, 0x02, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xfe, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x01, 0xff, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x01, 0x01, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x01, 0x00, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x01, 0xff, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xfe, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x01, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xff, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, + 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xfe, 0xff, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xfe, 0xff, 0x00, 0xfe, 0xfe, 0x00, 0x01, 0x01, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xfe, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x01, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x01, 0x01, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x01, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x01, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x01, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x01, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x01, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xfe, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x01, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/thirdparty/opnmidi/chips/pmdwin_opna.cpp b/thirdparty/opnmidi/chips/pmdwin_opna.cpp new file mode 100644 index 000000000..62fca0b85 --- /dev/null +++ b/thirdparty/opnmidi/chips/pmdwin_opna.cpp @@ -0,0 +1,84 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (C) 2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "pmdwin_opna.h" +#include "pmdwin/opna.h" +#include +#include + +PMDWinOPNA::PMDWinOPNA(OPNFamily f) + : OPNChipBaseBufferedT(f) +{ + OPNA *opn = new OPNA; + chip = reinterpret_cast(opn); + setRate(m_rate, m_clock); +} + +PMDWinOPNA::~PMDWinOPNA() +{ + OPNA *opn = reinterpret_cast(chip); + delete opn; +} + +void PMDWinOPNA::setRate(uint32_t rate, uint32_t clock) +{ + OPNA *opn = reinterpret_cast(chip); + OPNChipBaseBufferedT::setRate(rate, clock); + + uint32_t chipRate = isRunningAtPcmRate() ? rate : nativeRate(); + std::memset(chip, 0, sizeof(*opn)); + OPNAInit(opn, m_clock, chipRate, 0); + OPNASetReg(opn, 0x29, 0x9f); +} + +void PMDWinOPNA::reset() +{ + OPNChipBaseBufferedT::reset(); + OPNA *opn = reinterpret_cast(chip); + OPNAReset(opn); + OPNASetReg(opn, 0x29, 0x9f); +} + +void PMDWinOPNA::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + OPNA *opn = reinterpret_cast(chip); + OPNASetReg(opn, (port << 8) | addr, data); +} + +void PMDWinOPNA::writePan(uint16_t chan, uint8_t data) +{ + OPNA *opn = reinterpret_cast(chip); + OPNASetPan(opn, chan, data); +} + +void PMDWinOPNA::nativeGenerateN(int16_t *output, size_t frames) +{ + // be cautious to avoid overflowing stack buffer on PMDWin side! + // (on OPNChipBaseBuffered it's fine) + assert(frames < 16384 / 2); + + OPNA *opn = reinterpret_cast(chip); + OPNAMix(opn, output, static_cast(frames)); +} + +const char *PMDWinOPNA::emulatorName() +{ + return "PMDWin OPNA"; // git 2018-05-11 rev 255ef52 +} diff --git a/thirdparty/opnmidi/chips/pmdwin_opna.h b/thirdparty/opnmidi/chips/pmdwin_opna.h new file mode 100644 index 000000000..a0147474a --- /dev/null +++ b/thirdparty/opnmidi/chips/pmdwin_opna.h @@ -0,0 +1,45 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (C) 2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PMDWIN_OPNA_H +#define PMDWIN_OPNA_H + +#include "opn_chip_base.h" + +class PMDWinOPNA final : public OPNChipBaseBufferedT +{ + struct ChipType; + ChipType *chip; +public: + explicit PMDWinOPNA(OPNFamily f); + ~PMDWinOPNA() override; + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t chan, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerateN(int16_t *output, size_t frames) override; + const char *emulatorName() override; +}; + +#endif diff --git a/thirdparty/opnmidi/chips/vgm_file_dumper.cpp b/thirdparty/opnmidi/chips/vgm_file_dumper.cpp new file mode 100644 index 000000000..e5e289bbf --- /dev/null +++ b/thirdparty/opnmidi/chips/vgm_file_dumper.cpp @@ -0,0 +1,302 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "vgm_file_dumper.h" +#include +#include + +#include + +//! FIXME: Replace this ugly crap with proper public call +static const char *g_vgm_path = "kek.vgm"; +extern "C" +{ + OPNMIDI_EXPORT void opn2_set_vgm_out_path(const char *path) + { + g_vgm_path = path; + } +} + + +#define VGM_LOOP_START_BASE 0x1C +#define VGM_SONG_DATA_START 0x38 + +static void g_write_le(FILE *f_out, uint32_t &field) +{ + uint8_t out[4]; + out[0] = (field) & 0xFF; + out[1] = (field >> 8) & 0xFF; + out[2] = (field >> 16) & 0xFF; + out[3] = (field >> 24) & 0xFF; + std::fwrite(&out, 1, 4, f_out); +} + +static void g_write_le(FILE *f_out, uint16_t &field) +{ + uint8_t out[4]; + out[0] = (field) & 0xFF; + out[1] = (field >> 8) & 0xFF; + std::fwrite(&out, 1, 2, f_out); +} + +void VGMFileDumper::writeHead() +{ + if(!m_output) + return; + + long offset = std::ftell(m_output); + + if(offset < 0) + return; // FATAL ERROR: + + std::fseek(m_output, 0x00, SEEK_SET); + std::fwrite(m_vgm_head.magic, 1, 4, m_output); + g_write_le(m_output, m_vgm_head.eof_offset); + g_write_le(m_output, m_vgm_head.version); + g_write_le(m_output, m_vgm_head.clock_sn76489); + g_write_le(m_output, m_vgm_head.clock_ym2413); + g_write_le(m_output, m_vgm_head.offset_gd3); + g_write_le(m_output, m_vgm_head.total_samples); + g_write_le(m_output, m_vgm_head.offset_loop); + g_write_le(m_output, m_vgm_head.loop_samples); + g_write_le(m_output, m_vgm_head.rate); + g_write_le(m_output, m_vgm_head.feedback_sn76489); + std::fwrite(&m_vgm_head.shift_register_width_sn76489, 1, 1, m_output); + std::fwrite(&m_vgm_head.flags_sn76489, 1, 1, m_output); + g_write_le(m_output, m_vgm_head.clock_ym2612); + g_write_le(m_output, m_vgm_head.clock_ym2151); + g_write_le(m_output, m_vgm_head.offset_data); + std::fseek(m_output, offset, SEEK_SET); +} + +void VGMFileDumper::writeWait(uint_fast16_t value) +{ + if(!m_output) + return; + uint8_t out[3]; + out[0] = 0x61; + if(value == 735) + { + out[0] = 0x62; + std::fwrite(&out, 1, 1, m_output); + m_bytes_written += 1; + } + else if(value == 882) + { + out[0] = 0x63; + std::fwrite(&out, 1, 1, m_output); + m_bytes_written += 1; + } + else + { + out[1] = value & 0xFF; + out[2] = (value >> 8) & 0xFF; + std::fwrite(&out, 1, 3, m_output); + m_bytes_written += 3; + } + m_samples_written += value; + m_samples_loop += value; +} + +void VGMFileDumper::flushWait() +{ + if(!m_output) + return; + + if(m_chip_index > 0) + return; + + while(m_delay > 0) + { + uint16_t to_copy; + if(m_delay > 65535) + { + to_copy = 65535; + m_delay -= 65535; + } + else + { + to_copy = static_cast(m_delay); + m_delay = 0; + } + writeWait(to_copy); + } +} + +void VGMFileDumper::writeCommand(uint_fast8_t cmd, uint_fast16_t key, uint_fast8_t value) +{ + if(!m_output) + return; + uint8_t out[3]; + out[0] = static_cast(cmd); + out[1] = static_cast(key); + out[2] = static_cast(value); + std::fwrite(&out, 1, 3, m_output); + m_bytes_written += 3; +} + +VGMFileDumper::VGMFileDumper(OPNFamily f, int index, void *first) + : OPNChipBaseBufferedT(f) +{ + m_output = NULL; + m_bytes_written = 0; + m_samples_written = 0; + m_samples_loop = 0; + m_actual_rate = 0; + m_delay = 0; + m_end_caught = false; + m_chip_index = index; + m_first = NULL; + + VGMFileDumper::setRate(m_rate, m_clock); + std::memset(&m_vgm_head, 0, sizeof(VgmHead)); + + if(m_chip_index == 0) + { + m_output = std::fopen(g_vgm_path, "wb"); + assert(m_output); + std::memcpy(m_vgm_head.magic, "Vgm ", 4); + m_vgm_head.version = 0x00000150; + m_vgm_head.offset_loop = VGM_LOOP_START_BASE; + std::fseek(m_output, VGM_SONG_DATA_START, SEEK_SET); + } + else + { + m_first = reinterpret_cast(first); + } +} + +VGMFileDumper::~VGMFileDumper() +{ + if(m_chip_index > 0) + return; + + uint8_t out[1]; + out[0] = 0x66;// end of sound data + std::fwrite(&out, 1, 1, m_output); + m_bytes_written += 1; + + m_vgm_head.total_samples = m_samples_written; + m_vgm_head.loop_samples = m_samples_loop; + m_vgm_head.eof_offset = (VGM_SONG_DATA_START + m_bytes_written - 4); + m_vgm_head.offset_data = 0x04; + + writeHead(); + + if(m_output) + std::fclose(m_output); +} + +void VGMFileDumper::setRate(uint32_t rate, uint32_t clock) +{ + OPNChipBaseBufferedT::setRate(rate, clock); + m_actual_rate = isRunningAtPcmRate() ? rate : nativeRate(); + m_vgm_head.clock_ym2612 = m_clock; +} + +void VGMFileDumper::reset() +{ + if(!m_output) + return; + OPNChipBaseBufferedT::reset(); + std::fseek(m_output, VGM_SONG_DATA_START, SEEK_SET); + m_samples_written = 0; + m_samples_loop = 0; + m_bytes_written = 0; + m_end_caught = false; + m_delay = 0; +} + +void VGMFileDumper::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + if(m_chip_index > 0) // When it's a second chip + { + if(m_first) + m_first->writeReg(port + 2, addr, data); + return; + } + + if(!m_output) + return; + + if(port >= 4u) + return; // VGM DOESN'T SUPPORTS MORE THAN 2 CHIPS + + if(port >= 2u && ((m_vgm_head.clock_ym2612 & 0x40000000) == 0)) + m_vgm_head.clock_ym2612 |= 0x40000000; + + flushWait(); + + uint_fast8_t ports[] = {0x52, 0x53, 0xA2, 0xA3}; + writeCommand(ports[port], addr, data); +} + +void VGMFileDumper::writePan(uint16_t /*chan*/, uint8_t /*data*/) +{} + +void VGMFileDumper::nativeGenerateN(int16_t *output, size_t frames) +{ + if(!m_output) + return; + if(m_chip_index > 0 || m_end_caught) // When it's a second chip + return; + std::memset(output, 0, frames * sizeof(int16_t) * 2); + m_delay += size_t(frames * (44100.0 / double(m_actual_rate))); +} + +const char *VGMFileDumper::emulatorName() +{ + return "VGM Writer"; +} + +void VGMFileDumper::writeLoopStart() +{ + if(m_chip_index > 0) + return; + flushWait(); + m_vgm_head.offset_loop = VGM_LOOP_START_BASE + m_bytes_written; + m_samples_loop = 0; + std::printf(" - MIDI2VGM: Loop start at 0x%04X\n", m_vgm_head.offset_loop); + std::fflush(stdout); +} + +void VGMFileDumper::writeLoopEnd() +{ + if(m_chip_index > 0 || m_end_caught) + return; + m_end_caught = true; + flushWait(); + std::printf(" - MIDI2VGM: Loop end with total wait in %u samples\n", m_samples_loop); + std::fflush(stdout); +} + +void VGMFileDumper::loopStartHook(void *self) +{ + VGMFileDumper *s = reinterpret_cast(self); + if(s) + s->writeLoopStart(); +} + +void VGMFileDumper::loopEndHook(void *self) +{ + VGMFileDumper *s = reinterpret_cast(self); + if(s) + s->writeLoopEnd(); +} diff --git a/thirdparty/opnmidi/chips/vgm_file_dumper.h b/thirdparty/opnmidi/chips/vgm_file_dumper.h new file mode 100644 index 000000000..d2728ec52 --- /dev/null +++ b/thirdparty/opnmidi/chips/vgm_file_dumper.h @@ -0,0 +1,91 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef VGM_FILE_DUMPER_H +#define VGM_FILE_DUMPER_H + +#include "opn_chip_base.h" + +class VGMFileDumper final : public OPNChipBaseBufferedT +{ + //! Output file instance + FILE *m_output; + //! Count of song bytes written into the file + uint32_t m_bytes_written; + //! Waiting delay of song in 1/44100'ths of second + uint32_t m_samples_written; + //! Waiting delay of loop part in 1/44100'ths of second + uint32_t m_samples_loop; + //! Requested sample rate + uint32_t m_actual_rate; + //! Cached delay value in 1/44100'ths of second + uint64_t m_delay; + //! Don't increase waiting delay after end of song caught + bool m_end_caught; + //! Index of chip (0'th is master, 1 is a helper) + int m_chip_index; + //! First chip + VGMFileDumper* m_first; + + struct VgmHead + { + char magic[4]; + uint32_t eof_offset; + uint32_t version; + uint32_t clock_sn76489; + uint32_t clock_ym2413; + uint32_t offset_gd3; + uint32_t total_samples; + uint32_t offset_loop; + uint32_t loop_samples; + uint32_t rate; + uint16_t feedback_sn76489; + uint8_t shift_register_width_sn76489; + uint8_t flags_sn76489; + uint32_t clock_ym2612; + uint32_t clock_ym2151; + uint32_t offset_data; + }; + VgmHead m_vgm_head; + + void writeHead(); + void writeCommand(uint_fast8_t cmd, uint_fast16_t key = 0, uint_fast8_t value = 0); + void writeWait(uint_fast16_t value); + void flushWait(); +public: + explicit VGMFileDumper(OPNFamily f, int index, void *first); + ~VGMFileDumper() override; + + bool canRunAtPcmRate() const override { return true; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t chan, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerateN(int16_t *output, size_t frames) override; + const char *emulatorName() override; + void writeLoopStart(); + void writeLoopEnd(); + static void loopStartHook(void *self); + static void loopEndHook(void *self); +}; + +#endif // VGM_FILE_DUMPER_H diff --git a/thirdparty/opnmidi/chips/ymfm/README.md b/thirdparty/opnmidi/chips/ymfm/README.md new file mode 100644 index 000000000..29f0d87dc --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/README.md @@ -0,0 +1,125 @@ +# ymfm + +
+ +
+ +[ymfm](https://github.com/aaronsgiles/ymfm) is a collection of BSD-licensed Yamaha FM sound cores (OPM, OPN, OPL, and others), written by [Aaron Giles](https://aarongiles.com) + +## Supported environments + +This code should compile cleanly in any environment that has C++14 support. +It has been tested on gcc, clang, and Microsoft Visual C++ 2019. + +## Supported chip families + +Currently, support is present for the following chips (organized by header file): + +* ymfm_misc.h: + * YM2149 (SSG) [1983: MSX; Atari ST] +* ymfm_opm.h: + * YM2151 (OPM) [1983: Sharp X1, X68000; MSX; synths: DX21, DX27, DX100] + * YM2164 (OPP) [1985: FB-01 MIDI Expander; IBM Music Feature Card; MSX; synths: Korg DS-8, 707] +* ymfm_opn.h: + * YM2203 (OPN) [1984: NEC PC-88, PC-98, NEC PC-6001mkII SR, PC-6601 SR] + * YM2608 (OPNA) [1985: NEC PC-88, PC-98] + * YM2610 (OPNB) [1987: Neo Geo] + * YM2610B (OPNB2) + * YM2612 (OPN2) [1988: Sega Mega Drive/Genesis; FM Towns] + * YM3438 (OPN2C) + * YMF276 (OPN2L) + * YMF288 (OPN3L) [1995: NEC PC-98] +* ymfm_opl.h: + * YM3526 (OPL) [1984: C64 SFX Sound Expander] + * Y8950 (MSX-Audio) [1984: MSX] + * YM3812 (OPL2) [1985: AdLib, Sound Blaster; synths: some Portasound keyboards] + * YMF262 (OPL3) [1988: Sound Blaster Pro 2.0, SB16] + * YMF289B (OPL3L) + * YMF278B (OPL4) [1993: MSX Moonsound cartridge] + * YM2413 (OPLL) [1986: Sega Master System, Mark III; MSX; synths: Portasound PSS-140, PSS-170, PSS-270] + * YM2423 (OPLL-X) + * YMF281 (OPLLP) + * DS1001 (Konami 053982/VRC7) [1991: Famicom cartridge Lagrange Point] +* ymfm_opq.h: + * YM3806 (OPQ) [synths: PSR-60/70] +* ymfm_opz.h: + * YM2414 (OPZ) [1987: synths: TX81Z, DX11, YS200; Korg Z3 guitar synth] + +There are some obviously-related chips that also are on my horizon but have no implementation as yet: + +* YMW-258-F 'GEW8' (aka Sega 315-5560 aka Sega Multi-PCM) +* YMF271 (OPX) +* YM21280 (OPS) / YM21290 (EGS) [synths: DX7, DX1, DX5, DX9, TX7, TX216, TX416, TX816] +* OPK? + +## History + +These cores were originally written during the summer and fall of 2020 as part of the [MAME](https://mamedev.org/) project. +As such, their design started off heavily based on how MAME works. + +The OPM/OPN cores first appeared in MAME 0.230. +The OPL cores were added in MAME 0.231. +A further rewrite to abstract MAME dependencies is planned for MAME 0.232. + +The goal was threefold: +1. provide BSD-licensed emulation cores that are more compatible with MAME's core licensing +1. modernize and unify the code around a common implementation of shared features +1. improve accuracy where possible based on discoveries made by others + +## Accuracy + +The goal of these cores is not 100% digital accuracy. +To achieve that would require full emulation of the pipelines, which would make the code extremely difficult to comprehend. +It would also make it much harder to share common implementations of features, or to add support for less well-known chip types. +If you want that level of accuracy, there are [several](https://github.com/nukeykt/Nuked-OPN2) [decap-based](https://github.com/nukeykt/Nuked-OPM) [emulation cores](https://github.com/nukeykt/Nuked-OPLL) out there. + +Instead, the main goals are: +1. Extremely high (audibly indistinguishable) accuracy +1. Reasonable performance +1. Clean design with readable code +1. Clear documentation of the various chips + +## General approach + +Check out the [examples directory](https://github.com/aaronsgiles/ymfm/tree/main/examples) for some example usage patterns. +I'm not a big fan of makefiles for simple things, so instructions on how to compile each example are provided at the top. + +# IMPORTANT + +As of May 2021, the interface to these is still a bit in flux. +Be prepared when syncing with upstream to make some adjustments. + +### Clocking + +The general philosophy of the emulators provided here is that they are clock-independent. +Much like the actual chips, you (the consumer) control the clock; the chips themselves have no idea what time it is. +They just tick forward each time you ask them to. + +The way you move things along is via the `generate()` function, which ticks the internal system forward one or more samples, and writes out an array out chip-specific `output_data`. +But what, exactly, is a "sample", and how long is it? + +This is where the external clock comes in. +Most of the Yamaha chips are externally clocked in the MHz range. +They then divide that clock by a factor (sometimes dynamically controllable), and then the internal operators are pipelined to further divide the clock. + +For example, the YM2151 internally divides the clock by 2, and has 32 operators to iterate through. +Thus, for a nominal input lock of 3.58MHz, you end up at around a 55.9kHz sample rate. +Fortunately, all the chip implementations can compute this for you; just pass the raw external clock value to the `sample_rate()` method and it will hand you back the output sample rate you want. + +Then call `generate()` that many times per second to output the results. + +But what if I want to output at a "normal" rate, like 44.1kHz? +Sorry, you'll have to rate convert as needed. + +### Reading and Writing + +To read or write to the chips, you can call the `read()` and `write()` methods. +The offset provided corresponds to the addressing input lines in a (hopefully) logical way. + +For reads, almost all chips have a status register, which you can read via `read_status()`. +Some chips have a data port that can be read via `read_data()`. +And chips with extended addressing may also have `read_status_hi()` and `read_data_hi()`. + +For writes, almost all chips have an address register and a data register, and so you can reliably count on there being a `write_address()` and `write_data()` method as well. +If the chip supports extended addressing, it may also have `write_address_hi()` and `write_data_hi()`. + diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm.h b/thirdparty/opnmidi/chips/ymfm/ymfm.h new file mode 100644 index 000000000..804a6c95e --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm.h @@ -0,0 +1,572 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_H +#define YMFM_H + +#pragma once + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) + #define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ymfm +{ + +//********************************************************* +// DEBUGGING +//********************************************************* + +class debug +{ +public: + // masks to help isolate specific channels + static constexpr uint32_t GLOBAL_FM_CHANNEL_MASK = 0xffffffff; + static constexpr uint32_t GLOBAL_ADPCM_A_CHANNEL_MASK = 0xffffffff; + static constexpr uint32_t GLOBAL_ADPCM_B_CHANNEL_MASK = 0xffffffff; + static constexpr uint32_t GLOBAL_PCM_CHANNEL_MASK = 0xffffffff; + + // types of logging + static constexpr bool LOG_FM_WRITES = false; + static constexpr bool LOG_KEYON_EVENTS = false; + static constexpr bool LOG_UNEXPECTED_READ_WRITES = false; + + // helpers to write based on the log type + template static void log_fm_write(Params &&... args) { if (LOG_FM_WRITES) log(args...); } + template static void log_keyon(Params &&... args) { if (LOG_KEYON_EVENTS) log(args...); } + template static void log_unexpected_read_write(Params &&... args) { if (LOG_UNEXPECTED_READ_WRITES) log(args...); } + + // downstream helper to output log data; defaults to printf + template static void log(Params &&... args) { printf(args...); } +}; + + + +//********************************************************* +// GLOBAL HELPERS +//********************************************************* + +//------------------------------------------------- +// bitfield - extract a bitfield from the given +// value, starting at bit 'start' for a length of +// 'length' bits +//------------------------------------------------- + +inline uint32_t bitfield(uint32_t value, int start, int length = 1) +{ + return (value >> start) & ((1 << length) - 1); +} + + +//------------------------------------------------- +// clamp - clamp between the minimum and maximum +// values provided +//------------------------------------------------- + +inline int32_t clamp(int32_t value, int32_t minval, int32_t maxval) +{ + if (value < minval) + return minval; + if (value > maxval) + return maxval; + return value; +} + + +//------------------------------------------------- +// array_size - return the size of an array +//------------------------------------------------- + +template +constexpr uint32_t array_size(ArrayType (&array)[ArraySize]) +{ + (void)array; + return ArraySize; +} + + +//------------------------------------------------- +// count_leading_zeros - return the number of +// leading zeros in a 32-bit value; CPU-optimized +// versions for various architectures are included +// below +//------------------------------------------------- + +#if defined(__GNUC__) + +inline uint8_t count_leading_zeros(uint32_t value) +{ + if (value == 0) + return 32; + return __builtin_clz(value); +} + +#elif defined(_MSC_VER) + +inline uint8_t count_leading_zeros(uint32_t value) +{ + unsigned long index; + return _BitScanReverse(&index, value) ? uint8_t(31U - index) : 32U; +} + +#else + +inline uint8_t count_leading_zeros(uint32_t value) +{ + if (value == 0) + return 32; + uint8_t count; + for (count = 0; int32_t(value) >= 0; count++) + value <<= 1; + return count; +} + +#endif + + +// Many of the Yamaha FM chips emit a floating-point value, which is sent to +// a DAC for processing. The exact format of this floating-point value is +// documented below. This description only makes sense if the "internal" +// format treats sign as 1=positive and 0=negative, so the helpers below +// presume that. +// +// Internal OPx data 16-bit signed data Exp Sign Mantissa +// ================= ================= === ==== ======== +// 1 1xxxxxxxx------ -> 0 1xxxxxxxx------ -> 111 1 1xxxxxxx +// 1 01xxxxxxxx----- -> 0 01xxxxxxxx----- -> 110 1 1xxxxxxx +// 1 001xxxxxxxx---- -> 0 001xxxxxxxx---- -> 101 1 1xxxxxxx +// 1 0001xxxxxxxx--- -> 0 0001xxxxxxxx--- -> 100 1 1xxxxxxx +// 1 00001xxxxxxxx-- -> 0 00001xxxxxxxx-- -> 011 1 1xxxxxxx +// 1 000001xxxxxxxx- -> 0 000001xxxxxxxx- -> 010 1 1xxxxxxx +// 1 000000xxxxxxxxx -> 0 000000xxxxxxxxx -> 001 1 xxxxxxxx +// 0 111111xxxxxxxxx -> 1 111111xxxxxxxxx -> 001 0 xxxxxxxx +// 0 111110xxxxxxxx- -> 1 111110xxxxxxxx- -> 010 0 0xxxxxxx +// 0 11110xxxxxxxx-- -> 1 11110xxxxxxxx-- -> 011 0 0xxxxxxx +// 0 1110xxxxxxxx--- -> 1 1110xxxxxxxx--- -> 100 0 0xxxxxxx +// 0 110xxxxxxxx---- -> 1 110xxxxxxxx---- -> 101 0 0xxxxxxx +// 0 10xxxxxxxx----- -> 1 10xxxxxxxx----- -> 110 0 0xxxxxxx +// 0 0xxxxxxxx------ -> 1 0xxxxxxxx------ -> 111 0 0xxxxxxx + +//------------------------------------------------- +// encode_fp - given a 32-bit signed input value +// convert it to a signed 3.10 floating-point +// value +//------------------------------------------------- + +inline int16_t encode_fp(int32_t value) +{ + // handle overflows first + if (value < -32768) + return (7 << 10) | 0x000; + if (value > 32767) + return (7 << 10) | 0x3ff; + + // we need to count the number of leading sign bits after the sign + // we can use count_leading_zeros if we invert negative values + int32_t scanvalue = value ^ (int32_t(value) >> 31); + + // exponent is related to the number of leading bits starting from bit 14 + int exponent = 7 - count_leading_zeros(scanvalue << 17); + + // smallest exponent value allowed is 1 + exponent = std::max(exponent, 1); + + // mantissa + int32_t mantissa = value >> (exponent - 1); + + // assemble into final form, inverting the sign + return ((exponent << 10) | (mantissa & 0x3ff)) ^ 0x200; +} + + +//------------------------------------------------- +// decode_fp - given a 3.10 floating-point value, +// convert it to a signed 16-bit value +//------------------------------------------------- + +inline int16_t decode_fp(int16_t value) +{ + // invert the sign and the exponent + value ^= 0x1e00; + + // shift mantissa up to 16 bits then apply inverted exponent + return int16_t(value << 6) >> bitfield(value, 10, 3); +} + + +//------------------------------------------------- +// roundtrip_fp - compute the result of a round +// trip through the encode/decode process above +//------------------------------------------------- + +inline int16_t roundtrip_fp(int32_t value) +{ + // handle overflows first + if (value < -32768) + return -32768; + if (value > 32767) + return 32767; + + // we need to count the number of leading sign bits after the sign + // we can use count_leading_zeros if we invert negative values + int32_t scanvalue = value ^ (int32_t(value) >> 31); + + // exponent is related to the number of leading bits starting from bit 14 + int exponent = 7 - count_leading_zeros(scanvalue << 17); + + // smallest exponent value allowed is 1 + exponent = std::max(exponent, 1); + + // apply the shift back and forth to zero out bits that are lost + exponent -= 1; + return (value >> exponent) << exponent; +} + + + +//********************************************************* +// HELPER CLASSES +//********************************************************* + +// various envelope states +enum envelope_state : uint32_t +{ + EG_DEPRESS = 0, // OPLL only; set EG_HAS_DEPRESS to enable + EG_ATTACK = 1, + EG_DECAY = 2, + EG_SUSTAIN = 3, + EG_RELEASE = 4, + EG_REVERB = 5, // OPQ/OPZ only; set EG_HAS_REVERB to enable + EG_STATES = 6 +}; + +// external I/O access classes +enum access_class : uint32_t +{ + ACCESS_IO = 0, + ACCESS_ADPCM_A, + ACCESS_ADPCM_B, + ACCESS_PCM, + ACCESS_CLASSES +}; + + + +//********************************************************* +// HELPER CLASSES +//********************************************************* + +// ======================> ymfm_output + +// struct containing an array of output values +template +struct ymfm_output +{ + // clear all outputs to 0 + ymfm_output &clear() + { + for (uint32_t index = 0; index < NumOutputs; index++) + data[index] = 0; + return *this; + } + + // clamp all outputs to a 16-bit signed value + ymfm_output &clamp16() + { + for (uint32_t index = 0; index < NumOutputs; index++) + data[index] = clamp(data[index], -32768, 32767); + return *this; + } + + // run each output value through the floating-point processor + ymfm_output &roundtrip_fp() + { + for (uint32_t index = 0; index < NumOutputs; index++) + data[index] = ymfm::roundtrip_fp(data[index]); + return *this; + } + + // internal state + int32_t data[NumOutputs]; +}; + + +// ======================> ymfm_wavfile + +// this class is a debugging helper that accumulates data and writes it to wav files +template +class ymfm_wavfile +{ +public: + // construction + ymfm_wavfile(uint32_t samplerate = 44100) : + m_samplerate(samplerate) + { + } + + // configuration + ymfm_wavfile &set_index(uint32_t index) { m_index = index; return *this; } + ymfm_wavfile &set_samplerate(uint32_t samplerate) { m_samplerate = samplerate; return *this; } + + // destruction + ~ymfm_wavfile() + { + if (!m_buffer.empty()) + { + // create file + char name[20]; + sprintf(name, "wavlog-%02d.wav", m_index); + FILE *out = fopen(name, "wb"); + + // make the wav file header + uint8_t header[44]; + memcpy(&header[0], "RIFF", 4); + *(uint32_t *)&header[4] = m_buffer.size() * 2 + 44 - 8; + memcpy(&header[8], "WAVE", 4); + memcpy(&header[12], "fmt ", 4); + *(uint32_t *)&header[16] = 16; + *(uint16_t *)&header[20] = 1; + *(uint16_t *)&header[22] = Channels; + *(uint32_t *)&header[24] = m_samplerate; + *(uint32_t *)&header[28] = m_samplerate * 2 * Channels; + *(uint16_t *)&header[32] = 2 * Channels; + *(uint16_t *)&header[34] = 16; + memcpy(&header[36], "data", 4); + *(uint32_t *)&header[40] = m_buffer.size() * 2 + 44 - 44; + + // write header then data + fwrite(&header[0], 1, sizeof(header), out); + fwrite(&m_buffer[0], 2, m_buffer.size(), out); + fclose(out); + } + } + + // add data to the file + template + void add(ymfm_output output) + { + int16_t sum[Channels] = { 0 }; + for (int index = 0; index < Outputs; index++) + sum[index % Channels] += output.data[index]; + for (int index = 0; index < Channels; index++) + m_buffer.push_back(sum[index]); + } + + // add data to the file, using a reference + template + void add(ymfm_output output, ymfm_output const &ref) + { + int16_t sum[Channels] = { 0 }; + for (int index = 0; index < Outputs; index++) + sum[index % Channels] += output.data[index] - ref.data[index]; + for (int index = 0; index < Channels; index++) + m_buffer.push_back(sum[index]); + } + +private: + // internal state + uint32_t m_index; + uint32_t m_samplerate; + std::vector m_buffer; +}; + + +// ======================> ymfm_saved_state + +// this class contains a managed vector of bytes that is used to save and +// restore state +class ymfm_saved_state +{ +public: + // construction + ymfm_saved_state(std::vector &buffer, bool saving) : + m_buffer(buffer), + m_offset(saving ? -1 : 0) + { + if (saving) + buffer.resize(0); + } + + // are we saving or restoring? + bool saving() const { return (m_offset < 0); } + + // generic save/restore + template + void save_restore(DataType &data) + { + if (saving()) + save(data); + else + restore(data); + } + +public: + // save data to the buffer + void save(bool &data) { write(data ? 1 : 0); } + void save(int8_t &data) { write(data); } + void save(uint8_t &data) { write(data); } + void save(int16_t &data) { write(uint8_t(data)).write(data >> 8); } + void save(uint16_t &data) { write(uint8_t(data)).write(data >> 8); } + void save(int32_t &data) { write(data).write(data >> 8).write(data >> 16).write(data >> 24); } + void save(uint32_t &data) { write(data).write(data >> 8).write(data >> 16).write(data >> 24); } + void save(envelope_state &data) { write(uint8_t(data)); } + template + void save(DataType (&data)[Count]) { for (uint32_t index = 0; index < Count; index++) save(data[index]); } + + // restore data from the buffer + void restore(bool &data) { data = read() ? true : false; } + void restore(int8_t &data) { data = read(); } + void restore(uint8_t &data) { data = read(); } + void restore(int16_t &data) { data = read(); data |= read() << 8; } + void restore(uint16_t &data) { data = read(); data |= read() << 8; } + void restore(int32_t &data) { data = read(); data |= read() << 8; data |= read() << 16; data |= read() << 24; } + void restore(uint32_t &data) { data = read(); data |= read() << 8; data |= read() << 16; data |= read() << 24; } + void restore(envelope_state &data) { data = envelope_state(read()); } + template + void restore(DataType (&data)[Count]) { for (uint32_t index = 0; index < Count; index++) restore(data[index]); } + + // internal helper + ymfm_saved_state &write(uint8_t data) { m_buffer.push_back(data); return *this; } + uint8_t read() { return (m_offset < int32_t(m_buffer.size())) ? m_buffer[m_offset++] : 0; } + + // internal state + std::vector &m_buffer; + int32_t m_offset; +}; + + + +//********************************************************* +// INTERFACE CLASSES +//********************************************************* + +// ======================> ymfm_engine_callbacks + +// this class represents functions in the engine that the ymfm_interface +// needs to be able to call; it is represented here as a separate interface +// that is independent of the actual engine implementation +class ymfm_engine_callbacks +{ +public: + // timer callback; called by the interface when a timer fires + virtual void engine_timer_expired(uint32_t tnum) = 0; + + // check interrupts; called by the interface after synchronization + virtual void engine_check_interrupts() = 0; + + // mode register write; called by the interface after synchronization + virtual void engine_mode_write(uint8_t data) = 0; +}; + + +// ======================> ymfm_interface + +// this class represents the interface between the fm_engine and the outside +// world; it provides hooks for timers, synchronization, and I/O +class ymfm_interface +{ + // the engine is our friend + template friend class fm_engine_base; + +public: + // the following functions must be implemented by any derived classes; the + // default implementations are sufficient for some minimal operation, but will + // likely need to be overridden to integrate with the outside world; they are + // all prefixed with ymfm_ to reduce the likelihood of namespace collisions + + // + // timing and synchronizaton + // + + // the chip implementation calls this when a write happens to the mode + // register, which could affect timers and interrupts; our responsibility + // is to ensure the system is up to date before calling the engine's + // engine_mode_write() method + void ymfm_sync_mode_write(uint8_t data) { m_engine->engine_mode_write(data); } + + // the chip implementation calls this when the chip's status has changed, + // which may affect the interrupt state; our responsibility is to ensure + // the system is up to date before calling the engine's + // engine_check_interrupts() method + void ymfm_sync_check_interrupts() { m_engine->engine_check_interrupts(); } + + // the chip implementation calls this when one of the two internal timers + // has changed state; our responsibility is to arrange to call the engine's + // engine_timer_expired() method after the provided number of clocks; if + // duration_in_clocks is negative, we should cancel any outstanding timers + void ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks) { (void)tnum; (void) duration_in_clocks; } + + // the chip implementation calls this to indicate that the chip should be + // considered in a busy state until the given number of clocks has passed; + // our responsibility is to compute and remember the ending time based on + // the chip's clock for later checking + void ymfm_set_busy_end(uint32_t clocks) { (void)clocks; } + + // the chip implementation calls this to see if the chip is still currently + // is a busy state, as specified by a previous call to ymfm_set_busy_end(); + // our responsibility is to compare the current time against the previously + // noted busy end time and return true if we haven't yet passed it + bool ymfm_is_busy() { return false; } + + // + // I/O functions + // + + // the chip implementation calls this when the state of the IRQ signal has + // changed due to a status change; our responsibility is to respond as + // needed to the change in IRQ state, signaling any consumers + void ymfm_update_irq(bool asserted) { (void)asserted; } + + // the chip implementation calls this whenever data is read from outside + // of the chip; our responsibility is to provide the data requested + uint8_t ymfm_external_read(access_class type, uint32_t address) { (void)type; (void)address; return 0; } + + // the chip implementation calls this whenever data is written outside + // of the chip; our responsibility is to pass the written data on to any consumers + void ymfm_external_write(access_class type, uint32_t address, uint8_t data) { (void)type; (void)address; (void)data; } + +protected: + // pointer to engine callbacks -- this is set directly by the engine at + // construction time + ymfm_engine_callbacks *m_engine; +}; + +} + +#endif // YMFM_H diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_adpcm.cpp b/thirdparty/opnmidi/chips/ymfm/ymfm_adpcm.cpp new file mode 100644 index 000000000..4bc22beb2 --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_adpcm.cpp @@ -0,0 +1,807 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +#include "ymfm_adpcm.h" + +namespace ymfm +{ + +//********************************************************* +// ADPCM "A" REGISTERS +//********************************************************* + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void adpcm_a_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); + + // initialize the pans to on by default, and max instrument volume; + // some neogeo homebrews (for example ffeast) rely on this + m_regdata[0x08] = m_regdata[0x09] = m_regdata[0x0a] = + m_regdata[0x0b] = m_regdata[0x0c] = m_regdata[0x0d] = 0xdf; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_a_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_regdata); +} + + +//********************************************************* +// ADPCM "A" CHANNEL +//********************************************************* + +//------------------------------------------------- +// adpcm_a_channel - constructor +//------------------------------------------------- + +adpcm_a_channel::adpcm_a_channel(adpcm_a_engine &owner, uint32_t choffs, uint32_t addrshift) : + m_choffs(choffs), + m_address_shift(addrshift), + m_playing(0), + m_curnibble(0), + m_curbyte(0), + m_curaddress(0), + m_accumulator(0), + m_step_index(0), + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +void adpcm_a_channel::reset() +{ + m_playing = 0; + m_curnibble = 0; + m_curbyte = 0; + m_curaddress = 0; + m_accumulator = 0; + m_step_index = 0; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_a_channel::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_playing); + state.save_restore(m_curnibble); + state.save_restore(m_curbyte); + state.save_restore(m_curaddress); + state.save_restore(m_accumulator); + state.save_restore(m_step_index); +} + + +//------------------------------------------------- +// keyonoff - signal key on/off +//------------------------------------------------- + +void adpcm_a_channel::keyonoff(bool on) +{ + // QUESTION: repeated key ons restart the sample? + m_playing = on; + if (m_playing) + { + m_curaddress = m_regs.ch_start(m_choffs) << m_address_shift; + m_curnibble = 0; + m_curbyte = 0; + m_accumulator = 0; + m_step_index = 0; + + // don't log masked channels + if (((debug::GLOBAL_ADPCM_A_CHANNEL_MASK >> m_choffs) & 1) != 0) + debug::log_keyon("KeyOn ADPCM-A%d: pan=%d%d start=%04X end=%04X level=%02X\n", + m_choffs, + m_regs.ch_pan_left(m_choffs), + m_regs.ch_pan_right(m_choffs), + m_regs.ch_start(m_choffs), + m_regs.ch_end(m_choffs), + m_regs.ch_instrument_level(m_choffs)); + } +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +bool adpcm_a_channel::clock() +{ + // if not playing, just output 0 + if (m_playing == 0) + { + m_accumulator = 0; + return false; + } + + // if we're about to read nibble 0, fetch the data + uint8_t data; + if (m_curnibble == 0) + { + // stop when we hit the end address; apparently only low 20 bits are used for + // comparison on the YM2610: this affects sample playback in some games, for + // example twinspri character select screen music will skip some samples if + // this is not correct + // + // note also: end address is inclusive, so wait until we are about to fetch + // the sample just after the end before stopping; this is needed for nitd's + // jump sound, for example + uint32_t end = (m_regs.ch_end(m_choffs) + 1) << m_address_shift; + if (((m_curaddress ^ end) & 0xfffff) == 0) + { + m_playing = m_accumulator = 0; + return true; + } + + m_curbyte = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_A, m_curaddress++); + data = m_curbyte >> 4; + m_curnibble = 1; + } + + // otherwise just extract from the previosuly-fetched byte + else + { + data = m_curbyte & 0xf; + m_curnibble = 0; + } + + // compute the ADPCM delta + static uint16_t const s_steps[49] = + { + 16, 17, 19, 21, 23, 25, 28, + 31, 34, 37, 41, 45, 50, 55, + 60, 66, 73, 80, 88, 97, 107, + 118, 130, 143, 157, 173, 190, 209, + 230, 253, 279, 307, 337, 371, 408, + 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552 + }; + int32_t delta = (2 * bitfield(data, 0, 3) + 1) * s_steps[m_step_index] / 8; + if (bitfield(data, 3)) + delta = -delta; + + // the 12-bit accumulator wraps on the ym2610 and ym2608 (like the msm5205) + m_accumulator = (m_accumulator + delta) & 0xfff; + + // adjust ADPCM step + static int8_t const s_step_inc[8] = { -1, -1, -1, -1, 2, 5, 7, 9 }; + m_step_index = clamp(m_step_index + s_step_inc[bitfield(data, 0, 3)], 0, 48); + + return false; +} + + +//------------------------------------------------- +// output - return the computed output value, with +// panning applied +//------------------------------------------------- + +template +void adpcm_a_channel::output(ymfm_output &output) const +{ + // volume combines instrument and total levels + int vol = (m_regs.ch_instrument_level(m_choffs) ^ 0x1f) + (m_regs.total_level() ^ 0x3f); + + // if combined is maximum, don't add to outputs + if (vol >= 63) + return; + + // convert into a shift and a multiplier + // QUESTION: verify this from other sources + int8_t mul = 15 - (vol & 7); + uint8_t shift = 4 + 1 + (vol >> 3); + + // m_accumulator is a 12-bit value; shift up to sign-extend; + // the downshift is incorporated into 'shift' + int16_t value = ((int16_t(m_accumulator << 4) * mul) >> shift) & ~3; + + // apply to left/right as appropriate + if (NumOutputs == 1 || m_regs.ch_pan_left(m_choffs)) + output.data[0] += value; + if (NumOutputs > 1 && m_regs.ch_pan_right(m_choffs)) + output.data[1] += value; +} + + + +//********************************************************* +// ADPCM "A" ENGINE +//********************************************************* + +//------------------------------------------------- +// adpcm_a_engine - constructor +//------------------------------------------------- + +adpcm_a_engine::adpcm_a_engine(ymfm_interface &intf, uint32_t addrshift) : + m_intf(intf) +{ + // create the channels + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum] = std::make_unique(*this, chnum, addrshift); +} + + +//------------------------------------------------- +// reset - reset the engine state +//------------------------------------------------- + +void adpcm_a_engine::reset() +{ + // reset register state + m_regs.reset(); + + // reset each channel + for (auto &chan : m_channel) + chan->reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_a_engine::save_restore(ymfm_saved_state &state) +{ + // save register state + m_regs.save_restore(state); + + // save channel state + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum]->save_restore(state); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +uint32_t adpcm_a_engine::clock(uint32_t chanmask) +{ + // clock each channel, setting a bit in result if it finished + uint32_t result = 0; + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + if (m_channel[chnum]->clock()) + result |= 1 << chnum; + + // return the bitmask of completed samples + return result; +} + + +//------------------------------------------------- +// update - master update function +//------------------------------------------------- + +template +void adpcm_a_engine::output(ymfm_output &output, uint32_t chanmask) +{ + // mask out some channels for debug purposes + chanmask &= debug::GLOBAL_ADPCM_A_CHANNEL_MASK; + + // compute the output of each channel + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->output(output); +} + +template void adpcm_a_engine::output<1>(ymfm_output<1> &output, uint32_t chanmask); +template void adpcm_a_engine::output<2>(ymfm_output<2> &output, uint32_t chanmask); + + +//------------------------------------------------- +// write - handle writes to the ADPCM-A registers +//------------------------------------------------- + +void adpcm_a_engine::write(uint32_t regnum, uint8_t data) +{ + // store the raw value to the register array; + // most writes are passive, consumed only when needed + m_regs.write(regnum, data); + + // actively handle writes to the control register + if (regnum == 0x00) + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(data, chnum)) + m_channel[chnum]->keyonoff(bitfield(~data, 7)); +} + + + +//********************************************************* +// ADPCM "B" REGISTERS +//********************************************************* + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void adpcm_b_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); + + // default limit to wide open + m_regdata[0x0c] = m_regdata[0x0d] = 0xff; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_b_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_regdata); +} + + + +//********************************************************* +// ADPCM "B" CHANNEL +//********************************************************* + +//------------------------------------------------- +// adpcm_b_channel - constructor +//------------------------------------------------- + +adpcm_b_channel::adpcm_b_channel(adpcm_b_engine &owner, uint32_t addrshift) : + m_address_shift(addrshift), + m_status(STATUS_BRDY), + m_curnibble(0), + m_curbyte(0), + m_dummy_read(0), + m_position(0), + m_curaddress(0), + m_accumulator(0), + m_prev_accum(0), + m_adpcm_step(STEP_MIN), + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +void adpcm_b_channel::reset() +{ + m_status = STATUS_BRDY; + m_curnibble = 0; + m_curbyte = 0; + m_dummy_read = 0; + m_position = 0; + m_curaddress = 0; + m_accumulator = 0; + m_prev_accum = 0; + m_adpcm_step = STEP_MIN; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_b_channel::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_status); + state.save_restore(m_curnibble); + state.save_restore(m_curbyte); + state.save_restore(m_dummy_read); + state.save_restore(m_position); + state.save_restore(m_curaddress); + state.save_restore(m_accumulator); + state.save_restore(m_prev_accum); + state.save_restore(m_adpcm_step); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void adpcm_b_channel::clock() +{ + // only process if active and not recording (which we don't support) + if (!m_regs.execute() || m_regs.record() || (m_status & STATUS_PLAYING) == 0) + { + m_status &= ~STATUS_PLAYING; + return; + } + + // otherwise, advance the step + uint32_t position = m_position + m_regs.delta_n(); + m_position = uint16_t(position); + if (position < 0x10000) + return; + + // if we're about to process nibble 0, fetch sample + if (m_curnibble == 0) + { + // playing from RAM/ROM + if (m_regs.external()) + m_curbyte = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress); + } + + // extract the nibble from our current byte + uint8_t data = uint8_t(m_curbyte << (4 * m_curnibble)) >> 4; + m_curnibble ^= 1; + + // we just processed the last nibble + if (m_curnibble == 0) + { + // if playing from RAM/ROM, check the end/limit address or advance + if (m_regs.external()) + { + // handle the sample end, either repeating or stopping + if (at_end()) + { + // if repeating, go back to the start + if (m_regs.repeat()) + load_start(); + + // otherwise, done; set the EOS bit + else + { + m_accumulator = 0; + m_prev_accum = 0; + m_status = (m_status & ~STATUS_PLAYING) | STATUS_EOS; + debug::log_keyon("%s\n", "ADPCM EOS"); + return; + } + } + + // wrap at the limit address + else if (at_limit()) + m_curaddress = 0; + + // otherwise, advance the current address + else + { + m_curaddress++; + m_curaddress &= 0xffffff; + } + } + + // if CPU-driven, copy the next byte and request more + else + { + m_curbyte = m_regs.cpudata(); + m_status |= STATUS_BRDY; + } + } + + // remember previous value for interpolation + m_prev_accum = m_accumulator; + + // forecast to next forecast: 1/8, 3/8, 5/8, 7/8, 9/8, 11/8, 13/8, 15/8 + int32_t delta = (2 * bitfield(data, 0, 3) + 1) * m_adpcm_step / 8; + if (bitfield(data, 3)) + delta = -delta; + + // add and clamp to 16 bits + m_accumulator = clamp(m_accumulator + delta, -32768, 32767); + + // scale the ADPCM step: 0.9, 0.9, 0.9, 0.9, 1.2, 1.6, 2.0, 2.4 + static uint8_t const s_step_scale[8] = { 57, 57, 57, 57, 77, 102, 128, 153 }; + m_adpcm_step = clamp((m_adpcm_step * s_step_scale[bitfield(data, 0, 3)]) / 64, STEP_MIN, STEP_MAX); +} + + +//------------------------------------------------- +// output - return the computed output value, with +// panning applied +//------------------------------------------------- + +template +void adpcm_b_channel::output(ymfm_output &output, uint32_t rshift) const +{ + // mask out some channels for debug purposes + if ((debug::GLOBAL_ADPCM_B_CHANNEL_MASK & 1) == 0) + return; + + // do a linear interpolation between samples + int32_t result = (m_prev_accum * int32_t((m_position ^ 0xffff) + 1) + m_accumulator * int32_t(m_position)) >> 16; + + // apply volume (level) in a linear fashion and reduce + result = (result * int32_t(m_regs.level())) >> (8 + rshift); + + // apply to left/right + if (NumOutputs == 1 || m_regs.pan_left()) + output.data[0] += result; + if (NumOutputs > 1 && m_regs.pan_right()) + output.data[1] += result; +} + + +//------------------------------------------------- +// read - handle special register reads +//------------------------------------------------- + +uint8_t adpcm_b_channel::read(uint32_t regnum) +{ + uint8_t result = 0; + + // register 8 reads over the bus under some conditions + if (regnum == 0x08 && !m_regs.execute() && !m_regs.record() && m_regs.external()) + { + // two dummy reads are consumed first + if (m_dummy_read != 0) + { + load_start(); + m_dummy_read--; + } + + // read the data + else + { + // read from outside of the chip + result = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress++); + + // did we hit the end? if so, signal EOS + if (at_end()) + { + m_status = STATUS_EOS | STATUS_BRDY; + debug::log_keyon("%s\n", "ADPCM EOS"); + } + else + { + // signal ready + m_status = STATUS_BRDY; + } + + // wrap at the limit address + if (at_limit()) + m_curaddress = 0; + } + } + return result; +} + + +//------------------------------------------------- +// write - handle special register writes +//------------------------------------------------- + +void adpcm_b_channel::write(uint32_t regnum, uint8_t value) +{ + // register 0 can do a reset; also use writes here to reset the + // dummy read counter + if (regnum == 0x00) + { + if (m_regs.execute()) + { + load_start(); + + // don't log masked channels + if ((debug::GLOBAL_ADPCM_B_CHANNEL_MASK & 1) != 0) + debug::log_keyon("KeyOn ADPCM-B: rep=%d spk=%d pan=%d%d dac=%d 8b=%d rom=%d ext=%d rec=%d start=%04X end=%04X pre=%04X dn=%04X lvl=%02X lim=%04X\n", + m_regs.repeat(), + m_regs.speaker(), + m_regs.pan_left(), + m_regs.pan_right(), + m_regs.dac_enable(), + m_regs.dram_8bit(), + m_regs.rom_ram(), + m_regs.external(), + m_regs.record(), + m_regs.start(), + m_regs.end(), + m_regs.prescale(), + m_regs.delta_n(), + m_regs.level(), + m_regs.limit()); + } + else + m_status &= ~STATUS_EOS; + if (m_regs.resetflag()) + reset(); + if (m_regs.external()) + m_dummy_read = 2; + } + + // register 8 writes over the bus under some conditions + else if (regnum == 0x08) + { + // if writing from the CPU during execute, clear the ready flag + if (m_regs.execute() && !m_regs.record() && !m_regs.external()) + m_status &= ~STATUS_BRDY; + + // if writing during "record", pass through as data + else if (!m_regs.execute() && m_regs.record() && m_regs.external()) + { + // clear out dummy reads and set start address + if (m_dummy_read != 0) + { + load_start(); + m_dummy_read = 0; + } + + // did we hit the end? if so, signal EOS + if (at_end()) + { + debug::log_keyon("%s\n", "ADPCM EOS"); + m_status = STATUS_EOS | STATUS_BRDY; + } + + // otherwise, write the data and signal ready + else + { + m_owner.intf().ymfm_external_write(ACCESS_ADPCM_B, m_curaddress++, value); + m_status = STATUS_BRDY; + } + } + } +} + + +//------------------------------------------------- +// address_shift - compute the current address +// shift amount based on register settings +//------------------------------------------------- + +uint32_t adpcm_b_channel::address_shift() const +{ + // if a constant address shift, just provide that + if (m_address_shift != 0) + return m_address_shift; + + // if ROM or 8-bit DRAM, shift is 5 bits + if (m_regs.rom_ram()) + return 5; + if (m_regs.dram_8bit()) + return 5; + + // otherwise, shift is 2 bits + return 2; +} + + +//------------------------------------------------- +// load_start - load the start address and +// initialize the state +//------------------------------------------------- + +void adpcm_b_channel::load_start() +{ + m_status = (m_status & ~STATUS_EOS) | STATUS_PLAYING; + m_curaddress = m_regs.external() ? (m_regs.start() << address_shift()) : 0; + m_curnibble = 0; + m_curbyte = 0; + m_position = 0; + m_accumulator = 0; + m_prev_accum = 0; + m_adpcm_step = STEP_MIN; +} + + + +//********************************************************* +// ADPCM "B" ENGINE +//********************************************************* + +//------------------------------------------------- +// adpcm_b_engine - constructor +//------------------------------------------------- + +adpcm_b_engine::adpcm_b_engine(ymfm_interface &intf, uint32_t addrshift) : + m_intf(intf) +{ + // create the channel (only one supported for now, but leaving possibilities open) + m_channel = std::make_unique(*this, addrshift); +} + + +//------------------------------------------------- +// reset - reset the engine state +//------------------------------------------------- + +void adpcm_b_engine::reset() +{ + // reset registers + m_regs.reset(); + + // reset each channel + m_channel->reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void adpcm_b_engine::save_restore(ymfm_saved_state &state) +{ + // save our state + m_regs.save_restore(state); + + // save channel state + m_channel->save_restore(state); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void adpcm_b_engine::clock() +{ + // clock each channel, setting a bit in result if it finished + m_channel->clock(); +} + + +//------------------------------------------------- +// output - master output function +//------------------------------------------------- + +template +void adpcm_b_engine::output(ymfm_output &output, uint32_t rshift) +{ + // compute the output of each channel + m_channel->output(output, rshift); +} + +template void adpcm_b_engine::output<1>(ymfm_output<1> &output, uint32_t rshift); +template void adpcm_b_engine::output<2>(ymfm_output<2> &output, uint32_t rshift); + + +//------------------------------------------------- +// write - handle writes to the ADPCM-B registers +//------------------------------------------------- + +void adpcm_b_engine::write(uint32_t regnum, uint8_t data) +{ + // store the raw value to the register array; + // most writes are passive, consumed only when needed + m_regs.write(regnum, data); + + // let the channel handle any special writes + m_channel->write(regnum, data); +} + +} diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_adpcm.h b/thirdparty/opnmidi/chips/ymfm/ymfm_adpcm.h new file mode 100644 index 000000000..700ed4f6c --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_adpcm.h @@ -0,0 +1,411 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_ADPCM_H +#define YMFM_ADPCM_H + +#pragma once + +#include "ymfm.h" + +namespace ymfm +{ + +//********************************************************* +// INTERFACE CLASSES +//********************************************************* + +// forward declarations +class adpcm_a_engine; +class adpcm_b_engine; + + +// ======================> adpcm_a_registers + +// +// ADPCM-A register map: +// +// System-wide registers: +// 00 x------- Dump (disable=1) or keyon (0) control +// --xxxxxx Mask of channels to dump or keyon +// 01 --xxxxxx Total level +// 02 xxxxxxxx Test register +// 08-0D x------- Pan left +// -x------ Pan right +// ---xxxxx Instrument level +// 10-15 xxxxxxxx Start address (low) +// 18-1D xxxxxxxx Start address (high) +// 20-25 xxxxxxxx End address (low) +// 28-2D xxxxxxxx End address (high) +// +class adpcm_a_registers +{ +public: + // constants + static constexpr uint32_t OUTPUTS = 2; + static constexpr uint32_t CHANNELS = 6; + static constexpr uint32_t REGISTERS = 0x30; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + + // constructor + adpcm_a_registers() { } + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // map channel number to register offset + static inline uint32_t channel_offset(uint32_t chnum) + { + assert(chnum < CHANNELS); + return chnum; + } + + // direct read/write access + void write(uint32_t index, uint8_t data) { m_regdata[index] = data; } + + // system-wide registers + uint32_t dump() const { return bitfield(m_regdata[0x00], 7); } + uint32_t dump_mask() const { return bitfield(m_regdata[0x00], 0, 6); } + uint32_t total_level() const { return bitfield(m_regdata[0x01], 0, 6); } + uint32_t test() const { return m_regdata[0x02]; } + + // per-channel registers + uint32_t ch_pan_left(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 7); } + uint32_t ch_pan_right(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 6); } + uint32_t ch_instrument_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x08], 0, 5); } + uint32_t ch_start(uint32_t choffs) const { return m_regdata[choffs + 0x10] | (m_regdata[choffs + 0x18] << 8); } + uint32_t ch_end(uint32_t choffs) const { return m_regdata[choffs + 0x20] | (m_regdata[choffs + 0x28] << 8); } + + // per-channel writes + void write_start(uint32_t choffs, uint32_t address) + { + write(choffs + 0x10, address); + write(choffs + 0x18, address >> 8); + } + void write_end(uint32_t choffs, uint32_t address) + { + write(choffs + 0x20, address); + write(choffs + 0x28, address >> 8); + } + +private: + // internal state + uint8_t m_regdata[REGISTERS]; // register data +}; + + +// ======================> adpcm_a_channel + +class adpcm_a_channel +{ +public: + // constructor + adpcm_a_channel(adpcm_a_engine &owner, uint32_t choffs, uint32_t addrshift); + + // reset the channel state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // signal key on/off + void keyonoff(bool on); + + // master clockingfunction + bool clock(); + + // return the computed output value, with panning applied + template + void output(ymfm_output &output) const; + +private: + // internal state + uint32_t const m_choffs; // channel offset + uint32_t const m_address_shift; // address bits shift-left + uint32_t m_playing; // currently playing? + uint32_t m_curnibble; // index of the current nibble + uint32_t m_curbyte; // current byte of data + uint32_t m_curaddress; // current address + int32_t m_accumulator; // accumulator + int32_t m_step_index; // index in the stepping table + adpcm_a_registers &m_regs; // reference to registers + adpcm_a_engine &m_owner; // reference to our owner +}; + + +// ======================> adpcm_a_engine + +class adpcm_a_engine +{ +public: + static constexpr int CHANNELS = adpcm_a_registers::CHANNELS; + + // constructor + adpcm_a_engine(ymfm_interface &intf, uint32_t addrshift); + + // reset our status + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // master clocking function + uint32_t clock(uint32_t chanmask); + + // compute sum of channel outputs + template + void output(ymfm_output &output, uint32_t chanmask); + + // write to the ADPCM-A registers + void write(uint32_t regnum, uint8_t data); + + // set the start/end address for a channel (for hardcoded YM2608 percussion) + void set_start_end(uint8_t chnum, uint16_t start, uint16_t end) + { + uint32_t choffs = adpcm_a_registers::channel_offset(chnum); + m_regs.write_start(choffs, start); + m_regs.write_end(choffs, end); + } + + // return a reference to our interface + ymfm_interface &intf() { return m_intf; } + + // return a reference to our registers + adpcm_a_registers ®s() { return m_regs; } + +private: + // internal state + ymfm_interface &m_intf; // reference to the interface + std::unique_ptr m_channel[CHANNELS]; // array of channels + adpcm_a_registers m_regs; // registers +}; + + +// ======================> adpcm_b_registers + +// +// ADPCM-B register map: +// +// System-wide registers: +// 00 x------- Start of synthesis/analysis +// -x------ Record +// --x----- External/manual driving +// ---x---- Repeat playback +// ----x--- Speaker off +// -------x Reset +// 01 x------- Pan left +// -x------ Pan right +// ----x--- Start conversion +// -----x-- DAC enable +// ------x- DRAM access (1=8-bit granularity; 0=1-bit) +// -------x RAM/ROM (1=ROM, 0=RAM) +// 02 xxxxxxxx Start address (low) +// 03 xxxxxxxx Start address (high) +// 04 xxxxxxxx End address (low) +// 05 xxxxxxxx End address (high) +// 06 xxxxxxxx Prescale value (low) +// 07 -----xxx Prescale value (high) +// 08 xxxxxxxx CPU data/buffer +// 09 xxxxxxxx Delta-N frequency scale (low) +// 0a xxxxxxxx Delta-N frequency scale (high) +// 0b xxxxxxxx Level control +// 0c xxxxxxxx Limit address (low) +// 0d xxxxxxxx Limit address (high) +// 0e xxxxxxxx DAC data [YM2608/10] +// 0f xxxxxxxx PCM data [YM2608/10] +// 0e xxxxxxxx DAC data high [Y8950] +// 0f xx------ DAC data low [Y8950] +// 10 -----xxx DAC data exponent [Y8950] +// +class adpcm_b_registers +{ +public: + // constants + static constexpr uint32_t REGISTERS = 0x11; + + // constructor + adpcm_b_registers() { } + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // direct read/write access + void write(uint32_t index, uint8_t data) { m_regdata[index] = data; } + + // system-wide registers + uint32_t execute() const { return bitfield(m_regdata[0x00], 7); } + uint32_t record() const { return bitfield(m_regdata[0x00], 6); } + uint32_t external() const { return bitfield(m_regdata[0x00], 5); } + uint32_t repeat() const { return bitfield(m_regdata[0x00], 4); } + uint32_t speaker() const { return bitfield(m_regdata[0x00], 3); } + uint32_t resetflag() const { return bitfield(m_regdata[0x00], 0); } + uint32_t pan_left() const { return bitfield(m_regdata[0x01], 7); } + uint32_t pan_right() const { return bitfield(m_regdata[0x01], 6); } + uint32_t start_conversion() const { return bitfield(m_regdata[0x01], 3); } + uint32_t dac_enable() const { return bitfield(m_regdata[0x01], 2); } + uint32_t dram_8bit() const { return bitfield(m_regdata[0x01], 1); } + uint32_t rom_ram() const { return bitfield(m_regdata[0x01], 0); } + uint32_t start() const { return m_regdata[0x02] | (m_regdata[0x03] << 8); } + uint32_t end() const { return m_regdata[0x04] | (m_regdata[0x05] << 8); } + uint32_t prescale() const { return m_regdata[0x06] | (bitfield(m_regdata[0x07], 0, 3) << 8); } + uint32_t cpudata() const { return m_regdata[0x08]; } + uint32_t delta_n() const { return m_regdata[0x09] | (m_regdata[0x0a] << 8); } + uint32_t level() const { return m_regdata[0x0b]; } + uint32_t limit() const { return m_regdata[0x0c] | (m_regdata[0x0d] << 8); } + uint32_t dac() const { return m_regdata[0x0e]; } + uint32_t pcm() const { return m_regdata[0x0f]; } + +private: + // internal state + uint8_t m_regdata[REGISTERS]; // register data +}; + + +// ======================> adpcm_b_channel + +class adpcm_b_channel +{ + static constexpr int32_t STEP_MIN = 127; + static constexpr int32_t STEP_MAX = 24576; + +public: + static constexpr uint8_t STATUS_EOS = 0x01; + static constexpr uint8_t STATUS_BRDY = 0x02; + static constexpr uint8_t STATUS_PLAYING = 0x04; + + // constructor + adpcm_b_channel(adpcm_b_engine &owner, uint32_t addrshift); + + // reset the channel state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // signal key on/off + void keyonoff(bool on); + + // master clocking function + void clock(); + + // return the computed output value, with panning applied + template + void output(ymfm_output &output, uint32_t rshift) const; + + // return the status register + uint8_t status() const { return m_status; } + + // handle special register reads + uint8_t read(uint32_t regnum); + + // handle special register writes + void write(uint32_t regnum, uint8_t value); + +private: + // helper - return the current address shift + uint32_t address_shift() const; + + // load the start address + void load_start(); + + // limit checker; stops at the last byte of the chunk described by address_shift() + bool at_limit() const { return (m_curaddress == (((m_regs.limit() + 1) << address_shift()) - 1)); } + + // end checker; stops at the last byte of the chunk described by address_shift() + bool at_end() const { return (m_curaddress == (((m_regs.end() + 1) << address_shift()) - 1)); } + + // internal state + uint32_t const m_address_shift; // address bits shift-left + uint32_t m_status; // currently playing? + uint32_t m_curnibble; // index of the current nibble + uint32_t m_curbyte; // current byte of data + uint32_t m_dummy_read; // dummy read tracker + uint32_t m_position; // current fractional position + uint32_t m_curaddress; // current address + int32_t m_accumulator; // accumulator + int32_t m_prev_accum; // previous accumulator (for linear interp) + int32_t m_adpcm_step; // next forecast + adpcm_b_registers &m_regs; // reference to registers + adpcm_b_engine &m_owner; // reference to our owner +}; + + +// ======================> adpcm_b_engine + +class adpcm_b_engine +{ +public: + // constructor + adpcm_b_engine(ymfm_interface &intf, uint32_t addrshift = 0); + + // reset our status + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // master clocking function + void clock(); + + // compute sum of channel outputs + template + void output(ymfm_output &output, uint32_t rshift); + + // read from the ADPCM-B registers + uint32_t read(uint32_t regnum) { return m_channel->read(regnum); } + + // write to the ADPCM-B registers + void write(uint32_t regnum, uint8_t data); + + // status + uint8_t status() const { return m_channel->status(); } + + // return a reference to our interface + ymfm_interface &intf() { return m_intf; } + + // return a reference to our registers + adpcm_b_registers ®s() { return m_regs; } + +private: + // internal state + ymfm_interface &m_intf; // reference to our interface + std::unique_ptr m_channel; // channel pointer + adpcm_b_registers m_regs; // registers +}; + +} + +#endif // YMFM_ADPCM_H diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_fm.h b/thirdparty/opnmidi/chips/ymfm/ymfm_fm.h new file mode 100644 index 000000000..f5ac4a3d9 --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_fm.h @@ -0,0 +1,477 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_FM_H +#define YMFM_FM_H + +#pragma once + +#define YMFM_DEBUG_LOG_WAVFILES (0) + +namespace ymfm +{ + +//********************************************************* +// GLOBAL ENUMERATORS +//********************************************************* + +// three different keyon sources; actual keyon is an OR over all of these +enum keyon_type : uint32_t +{ + KEYON_NORMAL = 0, + KEYON_RHYTHM = 1, + KEYON_CSM = 2 +}; + + + +//********************************************************* +// CORE IMPLEMENTATION +//********************************************************* + +// ======================> opdata_cache + +// this class holds data that is computed once at the start of clocking +// and remains static during subsequent sound generation +struct opdata_cache +{ + // set phase_step to this value to recalculate it each sample; needed + // in the case of PM LFO changes + static constexpr uint32_t PHASE_STEP_DYNAMIC = 1; + + uint16_t const *waveform; // base of sine table + uint32_t phase_step; // phase step, or PHASE_STEP_DYNAMIC if PM is active + uint32_t total_level; // total level * 8 + KSL + uint32_t block_freq; // raw block frequency value (used to compute phase_step) + int32_t detune; // detuning value (used to compute phase_step) + uint32_t multiple; // multiple value (x.1, used to compute phase_step) + uint32_t eg_sustain; // sustain level, shifted up to envelope values + uint8_t eg_rate[EG_STATES]; // envelope rate, including KSR + uint8_t eg_shift = 0; // envelope shift amount +}; + + +// ======================> fm_registers_base + +// base class for family-specific register classes; this provides a few +// constants, common defaults, and helpers, but mostly each derived class is +// responsible for defining all commonly-called methods +class fm_registers_base +{ +public: + // this value is returned from the write() function for rhythm channels + static constexpr uint32_t RHYTHM_CHANNEL = 0xff; + + // this is the size of a full sin waveform + static constexpr uint32_t WAVEFORM_LENGTH = 0x400; + + // + // the following constants need to be defined per family: + // uint32_t OUTPUTS: The number of outputs exposed (1-4) + // uint32_t CHANNELS: The number of channels on the chip + // uint32_t ALL_CHANNELS: A bitmask of all channels + // uint32_t OPERATORS: The number of operators on the chip + // uint32_t WAVEFORMS: The number of waveforms offered + // uint32_t REGISTERS: The number of 8-bit registers allocated + // uint32_t DEFAULT_PRESCALE: The starting clock prescale + // uint32_t EG_CLOCK_DIVIDER: The clock divider of the envelope generator + // uint32_t CSM_TRIGGER_MASK: Mask of channels to trigger in CSM mode + // uint32_t REG_MODE: The address of the "mode" register controlling timers + // uint8_t STATUS_TIMERA: Status bit to set when timer A fires + // uint8_t STATUS_TIMERB: Status bit to set when tiemr B fires + // uint8_t STATUS_BUSY: Status bit to set when the chip is busy + // uint8_t STATUS_IRQ: Status bit to set when an IRQ is signalled + // + // the following constants are uncommon: + // bool DYNAMIC_OPS: True if ops/channel can be changed at runtime (OPL3+) + // bool EG_HAS_DEPRESS: True if the chip has a DP ("depress"?) envelope stage (OPLL) + // bool EG_HAS_REVERB: True if the chip has a faux reverb envelope stage (OPQ/OPZ) + // bool EG_HAS_SSG: True if the chip has SSG envelope support (OPN) + // bool MODULATOR_DELAY: True if the modulator is delayed by 1 sample (OPL pre-OPL3) + // + static constexpr bool DYNAMIC_OPS = false; + static constexpr bool EG_HAS_DEPRESS = false; + static constexpr bool EG_HAS_REVERB = false; + static constexpr bool EG_HAS_SSG = false; + static constexpr bool MODULATOR_DELAY = false; + + // system-wide register defaults + uint32_t status_mask() const { return 0; } // OPL only + uint32_t irq_reset() const { return 0; } // OPL only + uint32_t noise_enable() const { return 0; } // OPM only + uint32_t rhythm_enable() const { return 0; } // OPL only + + // per-operator register defaults + uint32_t op_ssg_eg_enable(uint32_t opoffs) const { (void)opoffs; return 0; } // OPN(A) only + uint32_t op_ssg_eg_mode(uint32_t opoffs) const { (void)opoffs; return 0; } // OPN(A) only + +protected: + // helper to encode four operator numbers into a 32-bit value in the + // operator maps for each register class + static inline uint32_t operator_list(uint8_t o1 = 0xff, uint8_t o2 = 0xff, uint8_t o3 = 0xff, uint8_t o4 = 0xff) + { + return o1 | (o2 << 8) | (o3 << 16) | (o4 << 24); + } + + // helper to apply KSR to the raw ADSR rate, ignoring ksr if the + // raw value is 0, and clamping to 63 + static inline uint32_t effective_rate(uint32_t rawrate, uint32_t ksr) + { + return (rawrate == 0) ? 0 : std::min(rawrate + ksr, 63); + } +}; + + + +//********************************************************* +// CORE ENGINE CLASSES +//********************************************************* + +// forward declarations +template class fm_engine_base; + +// ======================> fm_operator + +// fm_operator represents an FM operator (or "slot" in FM parlance), which +// produces an output sine wave modulated by an envelope +template +class fm_operator +{ + // "quiet" value, used to optimize when we can skip doing work + static constexpr uint32_t EG_QUIET = 0x380; + +public: + // constructor + fm_operator(fm_engine_base &owner, uint32_t opoffs); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset the operator state + void reset(); + + // return the operator/channel offset + uint32_t opoffs() const { return m_opoffs; } + uint32_t choffs() const { return m_choffs; } + + // set the current channel + void set_choffs(uint32_t choffs) { m_choffs = choffs; } + + // prepare prior to clocking + bool prepare(); + + // master clocking function + void clock(uint32_t env_counter, int32_t lfo_raw_pm); + + // return the current phase value + uint32_t phase() const { return m_phase >> 10; } + + // compute operator volume + int32_t compute_volume(uint32_t phase, uint32_t am_offset) const; + + // compute volume for the OPM noise channel + int32_t compute_noise_volume(uint32_t am_offset) const; + + // key state control + void keyonoff(uint32_t on, keyon_type type); + + // return a reference to our registers + RegisterType ®s() const { return m_regs; } + + // simple getters for debugging + envelope_state debug_eg_state() const { return m_env_state; } + uint16_t debug_eg_attenuation() const { return m_env_attenuation; } + uint8_t debug_ssg_inverted() const { return m_ssg_inverted; } + opdata_cache &debug_cache() { return m_cache; } + +private: + // start the attack phase + void start_attack(bool is_restart = false); + + // start the release phase + void start_release(); + + // clock phases + void clock_keystate(uint32_t keystate); + void clock_ssg_eg_state(); + void clock_envelope(uint32_t env_counter); + void clock_phase(int32_t lfo_raw_pm); + + // return effective attenuation of the envelope + uint32_t envelope_attenuation(uint32_t am_offset) const; + + // internal state + uint32_t m_choffs; // channel offset in registers + uint32_t m_opoffs; // operator offset in registers + uint32_t m_phase; // current phase value (10.10 format) + uint16_t m_env_attenuation; // computed envelope attenuation (4.6 format) + envelope_state m_env_state; // current envelope state + uint8_t m_ssg_inverted; // non-zero if the output should be inverted (bit 0) + uint8_t m_key_state; // current key state: on or off (bit 0) + uint8_t m_keyon_live; // live key on state (bit 0 = direct, bit 1 = rhythm, bit 2 = CSM) + opdata_cache m_cache; // cached values for performance + RegisterType &m_regs; // direct reference to registers + fm_engine_base &m_owner; // reference to the owning engine +}; + + +// ======================> fm_channel + +// fm_channel represents an FM channel which combines the output of 2 or 4 +// operators into a final result +template +class fm_channel +{ + using output_data = ymfm_output; + +public: + // constructor + fm_channel(fm_engine_base &owner, uint32_t choffs); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset the channel state + void reset(); + + // return the channel offset + uint32_t choffs() const { return m_choffs; } + + // assign operators + void assign(uint32_t index, fm_operator *op) + { + assert(index < array_size(m_op)); + m_op[index] = op; + if (op != nullptr) + op->set_choffs(m_choffs); + } + + // signal key on/off to our operators + void keyonoff(uint32_t states, keyon_type type, uint32_t chnum); + + // prepare prior to clocking + bool prepare(); + + // master clocking function + void clock(uint32_t env_counter, int32_t lfo_raw_pm); + + // EXTRA: Write the panning value for a simulated full-panning + void write_pan(int32_t data); + int32_t panL() const { return m_panVolumeL; } + int32_t panR() const { return m_panVolumeR; } + + // specific 2-operator and 4-operator output handlers + void output_2op(output_data &output, uint32_t rshift, int32_t clipmax) const; + void output_4op(output_data &output, uint32_t rshift, int32_t clipmax) const; + + // compute the special OPL rhythm channel outputs + void output_rhythm_ch6(output_data &output, uint32_t rshift, int32_t clipmax) const; + void output_rhythm_ch7(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const; + void output_rhythm_ch8(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const; + + // are we a 4-operator channel or a 2-operator one? + bool is4op() const + { + if (RegisterType::DYNAMIC_OPS) + return (m_op[2] != nullptr); + return (RegisterType::OPERATORS / RegisterType::CHANNELS == 4); + } + + // return a reference to our registers + RegisterType ®s() const { return m_regs; } + + // simple getters for debugging + fm_operator *debug_operator(uint32_t index) const { return m_op[index]; } + +private: + // helper to add values to the outputs based on channel enables + void add_to_output(uint32_t choffs, output_data &output, int32_t value) const + { + // create these constants to appease overzealous compilers checking array + // bounds in unreachable code (looking at you, clang) + constexpr int out0_index = 0; + constexpr int out1_index = 1 % RegisterType::OUTPUTS; + constexpr int out2_index = 2 % RegisterType::OUTPUTS; + constexpr int out3_index = 3 % RegisterType::OUTPUTS; + + if (RegisterType::OUTPUTS == 1 || m_regs.ch_output_0(choffs)) + output.data[out0_index] += value; + if (RegisterType::OUTPUTS >= 2 && m_regs.ch_output_1(choffs)) + output.data[out1_index] += value; + if (RegisterType::OUTPUTS >= 3 && m_regs.ch_output_2(choffs)) + output.data[out2_index] += value; + if (RegisterType::OUTPUTS >= 4 && m_regs.ch_output_3(choffs)) + output.data[out3_index] += value; + } + + // internal state + uint32_t m_choffs; // channel offset in registers + int16_t m_feedback[2]; // feedback memory for operator 1 + mutable int16_t m_feedback_in; // next input value for op 1 feedback (set in output) + fm_operator *m_op[4]; // up to 4 operators + RegisterType &m_regs; // direct reference to registers + fm_engine_base &m_owner; // reference to the owning engine + // EXTRA: + int32_t m_panVolumeL; // Left PCM output channel volume + int32_t m_panVolumeR; // Right PCM output channel volume +}; + + +// ======================> fm_engine_base + +// fm_engine_base represents a set of operators and channels which together +// form a Yamaha FM core; chips that implement other engines (ADPCM, wavetable, +// etc) take this output and combine it with the others externally +template +class fm_engine_base : public ymfm_engine_callbacks +{ +public: + // expose some constants from the registers + static constexpr uint32_t OUTPUTS = RegisterType::OUTPUTS; + static constexpr uint32_t CHANNELS = RegisterType::CHANNELS; + static constexpr uint32_t ALL_CHANNELS = RegisterType::ALL_CHANNELS; + static constexpr uint32_t OPERATORS = RegisterType::OPERATORS; + + // also expose status flags for consumers that inject additional bits + static constexpr uint8_t STATUS_TIMERA = RegisterType::STATUS_TIMERA; + static constexpr uint8_t STATUS_TIMERB = RegisterType::STATUS_TIMERB; + static constexpr uint8_t STATUS_BUSY = RegisterType::STATUS_BUSY; + static constexpr uint8_t STATUS_IRQ = RegisterType::STATUS_IRQ; + + // expose the correct output class + using output_data = ymfm_output; + + // constructor + fm_engine_base(ymfm_interface &intf); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset the overall state + void reset(); + + // master clocking function + uint32_t clock(uint32_t chanmask); + + // compute sum of channel outputs + void output(output_data &output, uint32_t rshift, int32_t clipmax, uint32_t chanmask) const; + + // write to the OPN registers + void write(uint16_t regnum, uint8_t data); + + // EXTRA: Write panning value to the output channel + void write_pan(uint16_t chan, uint8_t data); + + int32_t get_pan_l(uint16_t chan); + int32_t get_pan_r(uint16_t chan); + + // return the current status + uint8_t status() const; + + // set/reset bits in the status register, updating the IRQ status + uint8_t set_reset_status(uint8_t set, uint8_t reset) + { + m_status = (m_status | set) & ~(reset | STATUS_BUSY); + m_intf.ymfm_sync_check_interrupts(); + return m_status & ~m_regs.status_mask(); + } + + // set the IRQ mask + void set_irq_mask(uint8_t mask) { m_irq_mask = mask; m_intf.ymfm_sync_check_interrupts(); } + + // return the current clock prescale + uint32_t clock_prescale() const { return m_clock_prescale; } + + // set prescale factor (2/3/6) + void set_clock_prescale(uint32_t prescale) { m_clock_prescale = prescale; } + + // compute sample rate + uint32_t sample_rate(uint32_t baseclock) const + { +#if (YMFM_DEBUG_LOG_WAVFILES) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + m_wavfile[chnum].set_samplerate(baseclock / (m_clock_prescale * OPERATORS)); +#endif + return baseclock / (m_clock_prescale * OPERATORS); + } + + // return the owning device + ymfm_interface &intf() const { return m_intf; } + + // return a reference to our registers + RegisterType ®s() { return m_regs; } + + // invalidate any caches + void invalidate_caches() { m_modified_channels = RegisterType::ALL_CHANNELS; } + + // simple getters for debugging + fm_channel *debug_channel(uint32_t index) const { return m_channel[index].get(); } + fm_operator *debug_operator(uint32_t index) const { return m_operator[index].get(); } + +public: + // timer callback; called by the interface when a timer fires + virtual void engine_timer_expired(uint32_t tnum) override; + + // check interrupts; called by the interface after synchronization + virtual void engine_check_interrupts() override; + + // mode register write; called by the interface after synchronization + virtual void engine_mode_write(uint8_t data) override; + +protected: + // assign the current set of operators to channels + void assign_operators(); + + // update the state of the given timer + void update_timer(uint32_t which, uint32_t enable, int32_t delta_clocks); + + // internal state + ymfm_interface &m_intf; // reference to the system interface + uint32_t m_env_counter; // envelope counter; low 2 bits are sub-counter + uint8_t m_status; // current status register + uint8_t m_clock_prescale; // prescale factor (2/3/6) + uint8_t m_irq_mask; // mask of which bits signal IRQs + uint8_t m_irq_state; // current IRQ state + uint8_t m_timer_running[2]; // current timer running state + uint8_t m_total_clocks; // low 8 bits of the total number of clocks processed + uint32_t m_active_channels; // mask of active channels (computed by prepare) + uint32_t m_modified_channels; // mask of channels that have been modified + uint32_t m_prepare_count; // counter to do periodic prepare sweeps + RegisterType m_regs; // register accessor + std::unique_ptr> m_channel[CHANNELS]; // channel pointers + std::unique_ptr> m_operator[OPERATORS]; // operator pointers +#if (YMFM_DEBUG_LOG_WAVFILES) + mutable ymfm_wavfile<1> m_wavfile[CHANNELS]; // for debugging +#endif +}; + +} + +#endif // YMFM_FM_H diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_fm.hpp b/thirdparty/opnmidi/chips/ymfm/ymfm_fm.hpp new file mode 100644 index 000000000..4fa6ee4ce --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_fm.hpp @@ -0,0 +1,1646 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +namespace ymfm +{ + +/* + * Pan law table + */ +static const unsigned short panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + + +//********************************************************* +// GLOBAL TABLE LOOKUPS +//********************************************************* + +//------------------------------------------------- +// abs_sin_attenuation - given a sin (phase) input +// where the range 0-2*PI is mapped onto 10 bits, +// return the absolute value of sin(input), +// logarithmically-adjusted and treated as an +// attenuation value, in 4.8 fixed point format +//------------------------------------------------- + +inline uint32_t abs_sin_attenuation(uint32_t input) +{ + // the values here are stored as 4.8 logarithmic values for 1/4 phase + // this matches the internal format of the OPN chip, extracted from the die + static uint16_t const s_sin_table[256] = + { + 0x859,0x6c3,0x607,0x58b,0x52e,0x4e4,0x4a6,0x471,0x443,0x41a,0x3f5,0x3d3,0x3b5,0x398,0x37e,0x365, + 0x34e,0x339,0x324,0x311,0x2ff,0x2ed,0x2dc,0x2cd,0x2bd,0x2af,0x2a0,0x293,0x286,0x279,0x26d,0x261, + 0x256,0x24b,0x240,0x236,0x22c,0x222,0x218,0x20f,0x206,0x1fd,0x1f5,0x1ec,0x1e4,0x1dc,0x1d4,0x1cd, + 0x1c5,0x1be,0x1b7,0x1b0,0x1a9,0x1a2,0x19b,0x195,0x18f,0x188,0x182,0x17c,0x177,0x171,0x16b,0x166, + 0x160,0x15b,0x155,0x150,0x14b,0x146,0x141,0x13c,0x137,0x133,0x12e,0x129,0x125,0x121,0x11c,0x118, + 0x114,0x10f,0x10b,0x107,0x103,0x0ff,0x0fb,0x0f8,0x0f4,0x0f0,0x0ec,0x0e9,0x0e5,0x0e2,0x0de,0x0db, + 0x0d7,0x0d4,0x0d1,0x0cd,0x0ca,0x0c7,0x0c4,0x0c1,0x0be,0x0bb,0x0b8,0x0b5,0x0b2,0x0af,0x0ac,0x0a9, + 0x0a7,0x0a4,0x0a1,0x09f,0x09c,0x099,0x097,0x094,0x092,0x08f,0x08d,0x08a,0x088,0x086,0x083,0x081, + 0x07f,0x07d,0x07a,0x078,0x076,0x074,0x072,0x070,0x06e,0x06c,0x06a,0x068,0x066,0x064,0x062,0x060, + 0x05e,0x05c,0x05b,0x059,0x057,0x055,0x053,0x052,0x050,0x04e,0x04d,0x04b,0x04a,0x048,0x046,0x045, + 0x043,0x042,0x040,0x03f,0x03e,0x03c,0x03b,0x039,0x038,0x037,0x035,0x034,0x033,0x031,0x030,0x02f, + 0x02e,0x02d,0x02b,0x02a,0x029,0x028,0x027,0x026,0x025,0x024,0x023,0x022,0x021,0x020,0x01f,0x01e, + 0x01d,0x01c,0x01b,0x01a,0x019,0x018,0x017,0x017,0x016,0x015,0x014,0x014,0x013,0x012,0x011,0x011, + 0x010,0x00f,0x00f,0x00e,0x00d,0x00d,0x00c,0x00c,0x00b,0x00a,0x00a,0x009,0x009,0x008,0x008,0x007, + 0x007,0x007,0x006,0x006,0x005,0x005,0x005,0x004,0x004,0x004,0x003,0x003,0x003,0x002,0x002,0x002, + 0x002,0x001,0x001,0x001,0x001,0x001,0x001,0x001,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000 + }; + + // if the top bit is set, we're in the second half of the curve + // which is a mirror image, so invert the index + if (bitfield(input, 8)) + input = ~input; + + // return the value from the table + return s_sin_table[input & 0xff]; +} + + +//------------------------------------------------- +// attenuation_to_volume - given a 5.8 fixed point +// logarithmic attenuation value, return a 13-bit +// linear volume +//------------------------------------------------- + +inline uint32_t attenuation_to_volume(uint32_t input) +{ + // the values here are 10-bit mantissas with an implied leading bit + // this matches the internal format of the OPN chip, extracted from the die + + // as a nod to performance, the implicit 0x400 bit is pre-incorporated, and + // the values are left-shifted by 2 so that a simple right shift is all that + // is needed; also the order is reversed to save a NOT on the input +#define X(a) (((a) | 0x400) << 2) + static uint16_t const s_power_table[256] = + { + X(0x3fa),X(0x3f5),X(0x3ef),X(0x3ea),X(0x3e4),X(0x3df),X(0x3da),X(0x3d4), + X(0x3cf),X(0x3c9),X(0x3c4),X(0x3bf),X(0x3b9),X(0x3b4),X(0x3ae),X(0x3a9), + X(0x3a4),X(0x39f),X(0x399),X(0x394),X(0x38f),X(0x38a),X(0x384),X(0x37f), + X(0x37a),X(0x375),X(0x370),X(0x36a),X(0x365),X(0x360),X(0x35b),X(0x356), + X(0x351),X(0x34c),X(0x347),X(0x342),X(0x33d),X(0x338),X(0x333),X(0x32e), + X(0x329),X(0x324),X(0x31f),X(0x31a),X(0x315),X(0x310),X(0x30b),X(0x306), + X(0x302),X(0x2fd),X(0x2f8),X(0x2f3),X(0x2ee),X(0x2e9),X(0x2e5),X(0x2e0), + X(0x2db),X(0x2d6),X(0x2d2),X(0x2cd),X(0x2c8),X(0x2c4),X(0x2bf),X(0x2ba), + X(0x2b5),X(0x2b1),X(0x2ac),X(0x2a8),X(0x2a3),X(0x29e),X(0x29a),X(0x295), + X(0x291),X(0x28c),X(0x288),X(0x283),X(0x27f),X(0x27a),X(0x276),X(0x271), + X(0x26d),X(0x268),X(0x264),X(0x25f),X(0x25b),X(0x257),X(0x252),X(0x24e), + X(0x249),X(0x245),X(0x241),X(0x23c),X(0x238),X(0x234),X(0x230),X(0x22b), + X(0x227),X(0x223),X(0x21e),X(0x21a),X(0x216),X(0x212),X(0x20e),X(0x209), + X(0x205),X(0x201),X(0x1fd),X(0x1f9),X(0x1f5),X(0x1f0),X(0x1ec),X(0x1e8), + X(0x1e4),X(0x1e0),X(0x1dc),X(0x1d8),X(0x1d4),X(0x1d0),X(0x1cc),X(0x1c8), + X(0x1c4),X(0x1c0),X(0x1bc),X(0x1b8),X(0x1b4),X(0x1b0),X(0x1ac),X(0x1a8), + X(0x1a4),X(0x1a0),X(0x19c),X(0x199),X(0x195),X(0x191),X(0x18d),X(0x189), + X(0x185),X(0x181),X(0x17e),X(0x17a),X(0x176),X(0x172),X(0x16f),X(0x16b), + X(0x167),X(0x163),X(0x160),X(0x15c),X(0x158),X(0x154),X(0x151),X(0x14d), + X(0x149),X(0x146),X(0x142),X(0x13e),X(0x13b),X(0x137),X(0x134),X(0x130), + X(0x12c),X(0x129),X(0x125),X(0x122),X(0x11e),X(0x11b),X(0x117),X(0x114), + X(0x110),X(0x10c),X(0x109),X(0x106),X(0x102),X(0x0ff),X(0x0fb),X(0x0f8), + X(0x0f4),X(0x0f1),X(0x0ed),X(0x0ea),X(0x0e7),X(0x0e3),X(0x0e0),X(0x0dc), + X(0x0d9),X(0x0d6),X(0x0d2),X(0x0cf),X(0x0cc),X(0x0c8),X(0x0c5),X(0x0c2), + X(0x0be),X(0x0bb),X(0x0b8),X(0x0b5),X(0x0b1),X(0x0ae),X(0x0ab),X(0x0a8), + X(0x0a4),X(0x0a1),X(0x09e),X(0x09b),X(0x098),X(0x094),X(0x091),X(0x08e), + X(0x08b),X(0x088),X(0x085),X(0x082),X(0x07e),X(0x07b),X(0x078),X(0x075), + X(0x072),X(0x06f),X(0x06c),X(0x069),X(0x066),X(0x063),X(0x060),X(0x05d), + X(0x05a),X(0x057),X(0x054),X(0x051),X(0x04e),X(0x04b),X(0x048),X(0x045), + X(0x042),X(0x03f),X(0x03c),X(0x039),X(0x036),X(0x033),X(0x030),X(0x02d), + X(0x02a),X(0x028),X(0x025),X(0x022),X(0x01f),X(0x01c),X(0x019),X(0x016), + X(0x014),X(0x011),X(0x00e),X(0x00b),X(0x008),X(0x006),X(0x003),X(0x000) + }; +#undef X + + // look up the fractional part, then shift by the whole + return s_power_table[input & 0xff] >> (input >> 8); +} + + +//------------------------------------------------- +// attenuation_increment - given a 6-bit ADSR +// rate value and a 3-bit stepping index, +// return a 4-bit increment to the attenutaion +// for this step (or for the attack case, the +// fractional scale factor to decrease by) +//------------------------------------------------- + +inline uint32_t attenuation_increment(uint32_t rate, uint32_t index) +{ + static uint32_t const s_increment_table[64] = + { + 0x00000000, 0x00000000, 0x10101010, 0x10101010, // 0-3 (0x00-0x03) + 0x10101010, 0x10101010, 0x11101110, 0x11101110, // 4-7 (0x04-0x07) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 8-11 (0x08-0x0B) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 12-15 (0x0C-0x0F) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 16-19 (0x10-0x13) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 20-23 (0x14-0x17) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 24-27 (0x18-0x1B) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 28-31 (0x1C-0x1F) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 32-35 (0x20-0x23) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 36-39 (0x24-0x27) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 40-43 (0x28-0x2B) + 0x10101010, 0x10111010, 0x11101110, 0x11111110, // 44-47 (0x2C-0x2F) + 0x11111111, 0x21112111, 0x21212121, 0x22212221, // 48-51 (0x30-0x33) + 0x22222222, 0x42224222, 0x42424242, 0x44424442, // 52-55 (0x34-0x37) + 0x44444444, 0x84448444, 0x84848484, 0x88848884, // 56-59 (0x38-0x3B) + 0x88888888, 0x88888888, 0x88888888, 0x88888888 // 60-63 (0x3C-0x3F) + }; + return bitfield(s_increment_table[rate], 4*index, 4); +} + + +//------------------------------------------------- +// detune_adjustment - given a 5-bit key code +// value and a 3-bit detune parameter, return a +// 6-bit signed phase displacement; this table +// has been verified against Nuked's equations, +// but the equations are rather complicated, so +// we'll keep the simplicity of the table +//------------------------------------------------- + +inline int32_t detune_adjustment(uint32_t detune, uint32_t keycode) +{ + static uint8_t const s_detune_adjustment[32][4] = + { + { 0, 0, 1, 2 }, { 0, 0, 1, 2 }, { 0, 0, 1, 2 }, { 0, 0, 1, 2 }, + { 0, 1, 2, 2 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, + { 0, 1, 2, 4 }, { 0, 1, 3, 4 }, { 0, 1, 3, 4 }, { 0, 1, 3, 5 }, + { 0, 2, 4, 5 }, { 0, 2, 4, 6 }, { 0, 2, 4, 6 }, { 0, 2, 5, 7 }, + { 0, 2, 5, 8 }, { 0, 3, 6, 8 }, { 0, 3, 6, 9 }, { 0, 3, 7, 10 }, + { 0, 4, 8, 11 }, { 0, 4, 8, 12 }, { 0, 4, 9, 13 }, { 0, 5, 10, 14 }, + { 0, 5, 11, 16 }, { 0, 6, 12, 17 }, { 0, 6, 13, 19 }, { 0, 7, 14, 20 }, + { 0, 8, 16, 22 }, { 0, 8, 16, 22 }, { 0, 8, 16, 22 }, { 0, 8, 16, 22 } + }; + int32_t result = s_detune_adjustment[keycode][detune & 3]; + return bitfield(detune, 2) ? -result : result; +} + + +//------------------------------------------------- +// opm_key_code_to_phase_step - converts an +// OPM concatenated block (3 bits), keycode +// (4 bits) and key fraction (6 bits) to a 0.10 +// phase step, after applying the given delta; +// this applies to OPM and OPZ, so it lives here +// in a central location +//------------------------------------------------- + +inline uint32_t opm_key_code_to_phase_step(uint32_t block_freq, int32_t delta) +{ + // The phase step is essentially the fnum in OPN-speak. To compute this table, + // we used the standard formula for computing the frequency of a note, and + // then converted that frequency to fnum using the formula documented in the + // YM2608 manual. + // + // However, the YM2608 manual describes everything in terms of a nominal 8MHz + // clock, which produces an FM clock of: + // + // 8000000 / 24(operators) / 6(prescale) = 55555Hz FM clock + // + // Whereas the descriptions for the YM2151 use a nominal 3.579545MHz clock: + // + // 3579545 / 32(operators) / 2(prescale) = 55930Hz FM clock + // + // To correct for this, the YM2608 formula was adjusted to use a clock of + // 8053920Hz, giving this equation for the fnum: + // + // fnum = (double(144) * freq * (1 << 20)) / double(8053920) / 4; + // + // Unfortunately, the computed table differs in a few spots from the data + // verified from an actual chip. The table below comes from David Viens' + // analysis, used with his permission. + static const uint32_t s_phase_step[12*64] = + { + 41568,41600,41632,41664,41696,41728,41760,41792,41856,41888,41920,41952,42016,42048,42080,42112, + 42176,42208,42240,42272,42304,42336,42368,42400,42464,42496,42528,42560,42624,42656,42688,42720, + 42784,42816,42848,42880,42912,42944,42976,43008,43072,43104,43136,43168,43232,43264,43296,43328, + 43392,43424,43456,43488,43552,43584,43616,43648,43712,43744,43776,43808,43872,43904,43936,43968, + 44032,44064,44096,44128,44192,44224,44256,44288,44352,44384,44416,44448,44512,44544,44576,44608, + 44672,44704,44736,44768,44832,44864,44896,44928,44992,45024,45056,45088,45152,45184,45216,45248, + 45312,45344,45376,45408,45472,45504,45536,45568,45632,45664,45728,45760,45792,45824,45888,45920, + 45984,46016,46048,46080,46144,46176,46208,46240,46304,46336,46368,46400,46464,46496,46528,46560, + 46656,46688,46720,46752,46816,46848,46880,46912,46976,47008,47072,47104,47136,47168,47232,47264, + 47328,47360,47392,47424,47488,47520,47552,47584,47648,47680,47744,47776,47808,47840,47904,47936, + 48032,48064,48096,48128,48192,48224,48288,48320,48384,48416,48448,48480,48544,48576,48640,48672, + 48736,48768,48800,48832,48896,48928,48992,49024,49088,49120,49152,49184,49248,49280,49344,49376, + 49440,49472,49504,49536,49600,49632,49696,49728,49792,49824,49856,49888,49952,49984,50048,50080, + 50144,50176,50208,50240,50304,50336,50400,50432,50496,50528,50560,50592,50656,50688,50752,50784, + 50880,50912,50944,50976,51040,51072,51136,51168,51232,51264,51328,51360,51424,51456,51488,51520, + 51616,51648,51680,51712,51776,51808,51872,51904,51968,52000,52064,52096,52160,52192,52224,52256, + 52384,52416,52448,52480,52544,52576,52640,52672,52736,52768,52832,52864,52928,52960,52992,53024, + 53120,53152,53216,53248,53312,53344,53408,53440,53504,53536,53600,53632,53696,53728,53792,53824, + 53920,53952,54016,54048,54112,54144,54208,54240,54304,54336,54400,54432,54496,54528,54592,54624, + 54688,54720,54784,54816,54880,54912,54976,55008,55072,55104,55168,55200,55264,55296,55360,55392, + 55488,55520,55584,55616,55680,55712,55776,55808,55872,55936,55968,56032,56064,56128,56160,56224, + 56288,56320,56384,56416,56480,56512,56576,56608,56672,56736,56768,56832,56864,56928,56960,57024, + 57120,57152,57216,57248,57312,57376,57408,57472,57536,57568,57632,57664,57728,57792,57824,57888, + 57952,57984,58048,58080,58144,58208,58240,58304,58368,58400,58464,58496,58560,58624,58656,58720, + 58784,58816,58880,58912,58976,59040,59072,59136,59200,59232,59296,59328,59392,59456,59488,59552, + 59648,59680,59744,59776,59840,59904,59936,60000,60064,60128,60160,60224,60288,60320,60384,60416, + 60512,60544,60608,60640,60704,60768,60800,60864,60928,60992,61024,61088,61152,61184,61248,61280, + 61376,61408,61472,61536,61600,61632,61696,61760,61824,61856,61920,61984,62048,62080,62144,62208, + 62272,62304,62368,62432,62496,62528,62592,62656,62720,62752,62816,62880,62944,62976,63040,63104, + 63200,63232,63296,63360,63424,63456,63520,63584,63648,63680,63744,63808,63872,63904,63968,64032, + 64096,64128,64192,64256,64320,64352,64416,64480,64544,64608,64672,64704,64768,64832,64896,64928, + 65024,65056,65120,65184,65248,65312,65376,65408,65504,65536,65600,65664,65728,65792,65856,65888, + 65984,66016,66080,66144,66208,66272,66336,66368,66464,66496,66560,66624,66688,66752,66816,66848, + 66944,66976,67040,67104,67168,67232,67296,67328,67424,67456,67520,67584,67648,67712,67776,67808, + 67904,67936,68000,68064,68128,68192,68256,68288,68384,68448,68512,68544,68640,68672,68736,68800, + 68896,68928,68992,69056,69120,69184,69248,69280,69376,69440,69504,69536,69632,69664,69728,69792, + 69920,69952,70016,70080,70144,70208,70272,70304,70400,70464,70528,70560,70656,70688,70752,70816, + 70912,70976,71040,71104,71136,71232,71264,71360,71424,71488,71552,71616,71648,71744,71776,71872, + 71968,72032,72096,72160,72192,72288,72320,72416,72480,72544,72608,72672,72704,72800,72832,72928, + 72992,73056,73120,73184,73216,73312,73344,73440,73504,73568,73632,73696,73728,73824,73856,73952, + 74080,74144,74208,74272,74304,74400,74432,74528,74592,74656,74720,74784,74816,74912,74944,75040, + 75136,75200,75264,75328,75360,75456,75488,75584,75648,75712,75776,75840,75872,75968,76000,76096, + 76224,76288,76352,76416,76448,76544,76576,76672,76736,76800,76864,76928,77024,77120,77152,77248, + 77344,77408,77472,77536,77568,77664,77696,77792,77856,77920,77984,78048,78144,78240,78272,78368, + 78464,78528,78592,78656,78688,78784,78816,78912,78976,79040,79104,79168,79264,79360,79392,79488, + 79616,79680,79744,79808,79840,79936,79968,80064,80128,80192,80256,80320,80416,80512,80544,80640, + 80768,80832,80896,80960,80992,81088,81120,81216,81280,81344,81408,81472,81568,81664,81696,81792, + 81952,82016,82080,82144,82176,82272,82304,82400,82464,82528,82592,82656,82752,82848,82880,82976 + }; + + // extract the block (octave) first + uint32_t block = bitfield(block_freq, 10, 3); + + // the keycode (bits 6-9) is "gappy", mapping 12 values over 16 in each + // octave; to correct for this, we multiply the 4-bit value by 3/4 (or + // rather subtract 1/4); note that a (invalid) value of 15 will bleed into + // the next octave -- this is confirmed + uint32_t adjusted_code = bitfield(block_freq, 6, 4) - bitfield(block_freq, 8, 2); + + // now re-insert the 6-bit fraction + int32_t eff_freq = (adjusted_code << 6) | bitfield(block_freq, 0, 6); + + // now that the gaps are removed, add the delta + eff_freq += delta; + + // handle over/underflow by adjusting the block: + if (uint32_t(eff_freq) >= 768) + { + // minimum delta is -512 (PM), so we can only underflow by 1 octave + if (eff_freq < 0) + { + eff_freq += 768; + if (block-- == 0) + return s_phase_step[0] >> 7; + } + + // maximum delta is +512+608 (PM+detune), so we can overflow by up to 2 octaves + else + { + eff_freq -= 768; + if (eff_freq >= 768) + block++, eff_freq -= 768; + if (block++ >= 7) + return s_phase_step[767]; + } + } + + // look up the phase shift for the key code, then shift by octave + return s_phase_step[eff_freq] >> (block ^ 7); +} + + +//------------------------------------------------- +// opn_lfo_pm_phase_adjustment - given the 7 most +// significant frequency number bits, plus a 3-bit +// PM depth value and a signed 5-bit raw PM value, +// return a signed PM adjustment to the frequency; +// algorithm written to match Nuked behavior +//------------------------------------------------- + +inline int32_t opn_lfo_pm_phase_adjustment(uint32_t fnum_bits, uint32_t pm_sensitivity, int32_t lfo_raw_pm) +{ + // this table encodes 2 shift values to apply to the top 7 bits + // of fnum; it is effectively a cheap multiply by a constant + // value containing 0-2 bits + static uint8_t const s_lfo_pm_shifts[8][8] = + { + { 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 }, + { 0x77, 0x77, 0x77, 0x77, 0x72, 0x72, 0x72, 0x72 }, + { 0x77, 0x77, 0x77, 0x72, 0x72, 0x72, 0x17, 0x17 }, + { 0x77, 0x77, 0x72, 0x72, 0x17, 0x17, 0x12, 0x12 }, + { 0x77, 0x77, 0x72, 0x17, 0x17, 0x17, 0x12, 0x07 }, + { 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 }, + { 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 }, + { 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 } + }; + + // look up the relevant shifts + int32_t abs_pm = (lfo_raw_pm < 0) ? -lfo_raw_pm : lfo_raw_pm; + uint32_t const shifts = s_lfo_pm_shifts[pm_sensitivity][bitfield(abs_pm, 0, 3)]; + + // compute the adjustment + int32_t adjust = (fnum_bits >> bitfield(shifts, 0, 4)) + (fnum_bits >> bitfield(shifts, 4, 4)); + if (pm_sensitivity > 5) + adjust <<= pm_sensitivity - 5; + adjust >>= 2; + + // every 16 cycles it inverts sign + return (lfo_raw_pm < 0) ? -adjust : adjust; +} + + + +//********************************************************* +// FM OPERATOR +//********************************************************* + +//------------------------------------------------- +// fm_operator - constructor +//------------------------------------------------- + +template +fm_operator::fm_operator(fm_engine_base &owner, uint32_t opoffs) : + m_choffs(0), + m_opoffs(opoffs), + m_phase(0), + m_env_attenuation(0x3ff), + m_env_state(EG_RELEASE), + m_ssg_inverted(false), + m_key_state(0), + m_keyon_live(0), + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +template +void fm_operator::reset() +{ + // reset our data + m_phase = 0; + m_env_attenuation = 0x3ff; + m_env_state = EG_RELEASE; + m_ssg_inverted = 0; + m_key_state = 0; + m_keyon_live = 0; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +template +void fm_operator::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_phase); + state.save_restore(m_env_attenuation); + state.save_restore(m_env_state); + state.save_restore(m_ssg_inverted); + state.save_restore(m_key_state); + state.save_restore(m_keyon_live); +} + + +//------------------------------------------------- +// prepare - prepare for clocking +//------------------------------------------------- + +template +bool fm_operator::prepare() +{ + // cache the data + m_regs.cache_operator_data(m_choffs, m_opoffs, m_cache); + + // clock the key state + clock_keystate(uint32_t(m_keyon_live != 0)); + m_keyon_live &= ~(1 << KEYON_CSM); + + // we're active until we're quiet after the release + return (m_env_state != (RegisterType::EG_HAS_REVERB ? EG_REVERB : EG_RELEASE) || m_env_attenuation < EG_QUIET); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +template +void fm_operator::clock(uint32_t env_counter, int32_t lfo_raw_pm) +{ + // clock the SSG-EG state (OPN/OPNA) + if (m_regs.op_ssg_eg_enable(m_opoffs)) + clock_ssg_eg_state(); + else + m_ssg_inverted = false; + + // clock the envelope if on an envelope cycle; env_counter is a x.2 value + if (bitfield(env_counter, 0, 2) == 0) + clock_envelope(env_counter >> 2); + + // clock the phase + clock_phase(lfo_raw_pm); +} + + +//------------------------------------------------- +// compute_volume - compute the 14-bit signed +// volume of this operator, given a phase +// modulation and an AM LFO offset +//------------------------------------------------- + +template +int32_t fm_operator::compute_volume(uint32_t phase, uint32_t am_offset) const +{ + // the low 10 bits of phase represents a full 2*PI period over + // the full sin wave + + // early out if the envelope is effectively off + if (m_env_attenuation > EG_QUIET) + return 0; + + // get the absolute value of the sin, as attenuation, as a 4.8 fixed point value + uint32_t sin_attenuation = m_cache.waveform[phase & (RegisterType::WAVEFORM_LENGTH - 1)]; + + // get the attenuation from the evelope generator as a 4.6 value, shifted up to 4.8 + uint32_t env_attenuation = envelope_attenuation(am_offset) << 2; + + // combine into a 5.8 value, then convert from attenuation to 13-bit linear volume + int32_t result = attenuation_to_volume((sin_attenuation & 0x7fff) + env_attenuation); + + // negate if in the negative part of the sin wave (sign bit gives 14 bits) + return bitfield(sin_attenuation, 15) ? -result : result; +} + + +//------------------------------------------------- +// compute_noise_volume - compute the 14-bit +// signed noise volume of this operator, given a +// noise input value and an AM offset +//------------------------------------------------- + +template +int32_t fm_operator::compute_noise_volume(uint32_t am_offset) const +{ + // application manual says the logarithmic transform is not applied here, so we + // just use the raw envelope attenuation, inverted (since 0 attenuation should be + // maximum), and shift it up from a 10-bit value to an 11-bit value + int32_t result = (envelope_attenuation(am_offset) ^ 0x3ff) << 1; + + // QUESTION: is AM applied still? + + // negate based on the noise state + return bitfield(m_regs.noise_state(), 0) ? -result : result; +} + + +//------------------------------------------------- +// keyonoff - signal a key on/off event +//------------------------------------------------- + +template +void fm_operator::keyonoff(uint32_t on, keyon_type type) +{ + m_keyon_live = (m_keyon_live & ~(1 << int(type))) | (bitfield(on, 0) << int(type)); +} + + +//------------------------------------------------- +// start_attack - start the attack phase; called +// when a keyon happens or when an SSG-EG cycle +// is complete and restarts +//------------------------------------------------- + +template +void fm_operator::start_attack(bool is_restart) +{ + // don't change anything if already in attack state + if (m_env_state == EG_ATTACK) + return; + m_env_state = EG_ATTACK; + + // generally not inverted at start, except if SSG-EG is enabled and + // one of the inverted modes is specified; leave this alone on a + // restart, as it is managed by the clock_ssg_eg_state() code + if (RegisterType::EG_HAS_SSG && !is_restart) + m_ssg_inverted = m_regs.op_ssg_eg_enable(m_opoffs) & bitfield(m_regs.op_ssg_eg_mode(m_opoffs), 2); + + // reset the phase when we start an attack due to a key on + // (but not when due to an SSG-EG restart except in certain cases + // managed directly by the SSG-EG code) + if (!is_restart) + m_phase = 0; + + // if the attack rate >= 62 then immediately go to max attenuation + if (m_cache.eg_rate[EG_ATTACK] >= 62) + m_env_attenuation = 0; +} + + +//------------------------------------------------- +// start_release - start the release phase; +// called when a keyoff happens +//------------------------------------------------- + +template +void fm_operator::start_release() +{ + // don't change anything if already in release state + if (m_env_state >= EG_RELEASE) + return; + m_env_state = EG_RELEASE; + + // if attenuation if inverted due to SSG-EG, snap the inverted attenuation + // as the starting point + if (RegisterType::EG_HAS_SSG && m_ssg_inverted) + { + m_env_attenuation = (0x200 - m_env_attenuation) & 0x3ff; + m_ssg_inverted = false; + } +} + + +//------------------------------------------------- +// clock_keystate - clock the keystate to match +// the incoming keystate +//------------------------------------------------- + +template +void fm_operator::clock_keystate(uint32_t keystate) +{ + assert(keystate == 0 || keystate == 1); + + // has the key changed? + if ((keystate ^ m_key_state) != 0) + { + m_key_state = keystate; + + // if the key has turned on, start the attack + if (keystate != 0) + { + // OPLL has a DP ("depress"?) state to bring the volume + // down before starting the attack + if (RegisterType::EG_HAS_DEPRESS && m_env_attenuation < 0x200) + m_env_state = EG_DEPRESS; + else + start_attack(); + } + + // otherwise, start the release + else + start_release(); + } +} + + +//------------------------------------------------- +// clock_ssg_eg_state - clock the SSG-EG state; +// should only be called if SSG-EG is enabled +//------------------------------------------------- + +template +void fm_operator::clock_ssg_eg_state() +{ + // work only happens once the attenuation crosses above 0x200 + if (!bitfield(m_env_attenuation, 9)) + return; + + // 8 SSG-EG modes: + // 000: repeat normally + // 001: run once, hold low + // 010: repeat, alternating between inverted/non-inverted + // 011: run once, hold high + // 100: inverted repeat normally + // 101: inverted run once, hold low + // 110: inverted repeat, alternating between inverted/non-inverted + // 111: inverted run once, hold high + uint32_t mode = m_regs.op_ssg_eg_mode(m_opoffs); + + // hold modes (1/3/5/7) + if (bitfield(mode, 0)) + { + // set the inverted flag to the end state (0 for modes 1/7, 1 for modes 3/5) + m_ssg_inverted = bitfield(mode, 2) ^ bitfield(mode, 1); + + // if holding, force the attenuation to the expected value once we're + // past the attack phase + if (m_env_state != EG_ATTACK) + m_env_attenuation = m_ssg_inverted ? 0x200 : 0x3ff; + } + + // continuous modes (0/2/4/6) + else + { + // toggle invert in alternating mode (even in attack state) + m_ssg_inverted ^= bitfield(mode, 1); + + // restart attack if in decay/sustain states + if (m_env_state == EG_DECAY || m_env_state == EG_SUSTAIN) + start_attack(true); + + // phase is reset to 0 in modes 0/4 + if (bitfield(mode, 1) == 0) + m_phase = 0; + } + + // in all modes, once we hit release state, attenuation is forced to maximum + if (m_env_state == EG_RELEASE) + m_env_attenuation = 0x3ff; +} + + +//------------------------------------------------- +// clock_envelope - clock the envelope state +// according to the given count +//------------------------------------------------- + +template +void fm_operator::clock_envelope(uint32_t env_counter) +{ + // handle attack->decay transitions + if (m_env_state == EG_ATTACK && m_env_attenuation == 0) + m_env_state = EG_DECAY; + + // handle decay->sustain transitions; it is important to do this immediately + // after the attack->decay transition above in the event that the sustain level + // is set to 0 (in which case we will skip right to sustain without doing any + // decay); as an example where this can be heard, check the cymbals sound + // in channel 0 of shinobi's test mode sound #5 + if (m_env_state == EG_DECAY && m_env_attenuation >= m_cache.eg_sustain) + m_env_state = EG_SUSTAIN; + + // fetch the appropriate 6-bit rate value from the cache + uint32_t rate = m_cache.eg_rate[m_env_state]; + + // compute the rate shift value; this is the shift needed to + // apply to the env_counter such that it becomes a 5.11 fixed + // point number + uint32_t rate_shift = rate >> 2; + env_counter <<= rate_shift; + + // see if the fractional part is 0; if not, it's not time to clock + if (bitfield(env_counter, 0, 11) != 0) + return; + + // determine the increment based on the non-fractional part of env_counter + uint32_t relevant_bits = bitfield(env_counter, (rate_shift <= 11) ? 11 : rate_shift, 3); + uint32_t increment = attenuation_increment(rate, relevant_bits); + + // attack is the only one that increases + if (m_env_state == EG_ATTACK) + { + // glitch means that attack rates of 62/63 don't increment if + // changed after the initial key on (where they are handled + // specially); nukeykt confirms this happens on OPM, OPN, OPL/OPLL + // at least so assuming it is true for everyone + if (rate < 62) + m_env_attenuation += (~m_env_attenuation * increment) >> 4; + } + + // all other cases are similar + else + { + // non-SSG-EG cases just apply the increment + if (!m_regs.op_ssg_eg_enable(m_opoffs)) + m_env_attenuation += increment; + + // SSG-EG only applies if less than mid-point, and then at 4x + else if (m_env_attenuation < 0x200) + m_env_attenuation += 4 * increment; + + // clamp the final attenuation + if (m_env_attenuation >= 0x400) + m_env_attenuation = 0x3ff; + + // transition from depress to attack + if (RegisterType::EG_HAS_DEPRESS && m_env_state == EG_DEPRESS && m_env_attenuation >= 0x200) + start_attack(); + + // transition from release to reverb, should switch at -18dB + if (RegisterType::EG_HAS_REVERB && m_env_state == EG_RELEASE && m_env_attenuation >= 0xc0) + m_env_state = EG_REVERB; + } +} + + +//------------------------------------------------- +// clock_phase - clock the 10.10 phase value; the +// OPN version of the logic has been verified +// against the Nuked phase generator +//------------------------------------------------- + +template +void fm_operator::clock_phase(int32_t lfo_raw_pm) +{ + // read from the cache, or recalculate if PM active + uint32_t phase_step = m_cache.phase_step; + if (phase_step == opdata_cache::PHASE_STEP_DYNAMIC) + phase_step = m_regs.compute_phase_step(m_choffs, m_opoffs, m_cache, lfo_raw_pm); + + // finally apply the step to the current phase value + m_phase += phase_step; +} + + +//------------------------------------------------- +// envelope_attenuation - return the effective +// attenuation of the envelope +//------------------------------------------------- + +template +uint32_t fm_operator::envelope_attenuation(uint32_t am_offset) const +{ + uint32_t result = m_env_attenuation >> m_cache.eg_shift; + + // invert if necessary due to SSG-EG + if (RegisterType::EG_HAS_SSG && m_ssg_inverted) + result = (0x200 - result) & 0x3ff; + + // add in LFO AM modulation + if (m_regs.op_lfo_am_enable(m_opoffs)) + result += am_offset; + + // add in total level and KSL from the cache + result += m_cache.total_level; + + // clamp to max, apply shift, and return + return std::min(result, 0x3ff); +} + + + +//********************************************************* +// FM CHANNEL +//********************************************************* + +//------------------------------------------------- +// fm_channel - constructor +//------------------------------------------------- + +template +fm_channel::fm_channel(fm_engine_base &owner, uint32_t choffs) : + m_choffs(choffs), + m_feedback{ 0, 0 }, + m_feedback_in(0), + m_op{ nullptr, nullptr, nullptr, nullptr }, + m_regs(owner.regs()), + m_owner(owner), + m_panVolumeL(46340), + m_panVolumeR(46340) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +template +void fm_channel::reset() +{ + // reset our data + m_feedback[0] = m_feedback[1] = 0; + m_feedback_in = 0; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +template +void fm_channel::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_feedback[0]); + state.save_restore(m_feedback[1]); + state.save_restore(m_feedback_in); +} + + +//------------------------------------------------- +// keyonoff - signal key on/off to our operators +//------------------------------------------------- + +template +void fm_channel::keyonoff(uint32_t states, keyon_type type, uint32_t chnum) +{ + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + if (m_op[opnum] != nullptr) + m_op[opnum]->keyonoff(bitfield(states, opnum), type); + + if (debug::LOG_KEYON_EVENTS && ((debug::GLOBAL_FM_CHANNEL_MASK >> chnum) & 1) != 0) + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + if (m_op[opnum] != nullptr) + debug::log_keyon("%c%s\n", bitfield(states, opnum) ? '+' : '-', m_regs.log_keyon(m_choffs, m_op[opnum]->opoffs()).c_str()); +} + + +//------------------------------------------------- +// prepare - prepare for clocking +//------------------------------------------------- + +template +bool fm_channel::prepare() +{ + uint32_t active_mask = 0; + + // prepare all operators and determine if they are active + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + if (m_op[opnum] != nullptr) + if (m_op[opnum]->prepare()) + active_mask |= 1 << opnum; + + return (active_mask != 0); +} + + +//------------------------------------------------- +// clock - master clock of all operators +//------------------------------------------------- + +template +void fm_channel::clock(uint32_t env_counter, int32_t lfo_raw_pm) +{ + // clock the feedback through + m_feedback[0] = m_feedback[1]; + m_feedback[1] = m_feedback_in; + + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + if (m_op[opnum] != nullptr) + m_op[opnum]->clock(env_counter, lfo_raw_pm); + +/* +useful temporary code for envelope debugging +if (m_choffs == 0x101) +{ + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + { + auto &op = *m_op[((opnum & 1) << 1) | ((opnum >> 1) & 1)]; + printf(" %c%03X%c%c ", + "PADSRV"[op.debug_eg_state()], + op.debug_eg_attenuation(), + op.debug_ssg_inverted() ? '-' : '+', + m_regs.op_ssg_eg_enable(op.opoffs()) ? '0' + m_regs.op_ssg_eg_mode(op.opoffs()) : ' '); + } +printf(" -- "); +} +*/ +} + + +template +void fm_channel::write_pan(int32_t data) +{ + m_panVolumeL = panlawtable[data & 0x7F]; + m_panVolumeR = panlawtable[0x7F - (data & 0x7F)]; +} + + +//------------------------------------------------- +// output_2op - combine 4 operators according to +// the specified algorithm, returning a sum +// according to the rshift and clipmax parameters, +// which vary between different implementations +//------------------------------------------------- + +template +void fm_channel::output_2op(output_data &output, uint32_t rshift, int32_t clipmax) const +{ + // The first 2 operators should be populated + assert(m_op[0] != nullptr); + assert(m_op[1] != nullptr); + + // AM amount is the same across all operators; compute it once + uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); + + // operator 1 has optional self-feedback + int32_t opmod = 0; + uint32_t feedback = m_regs.ch_feedback(m_choffs); + if (feedback != 0) + opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback); + + // compute the 14-bit volume/value of operator 1 and update the feedback + int32_t op1value = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset); + + // now that the feedback has been computed, skip the rest if all volumes + // are clear; no need to do all this work for nothing + if (m_regs.ch_output_any(m_choffs) == 0) + return; + + // Algorithms for two-operator case: + // 0: O1 -> O2 -> out + // 1: (O1 + O2) -> out + int32_t result; + if (bitfield(m_regs.ch_algorithm(m_choffs), 0) == 0) + { + // some OPL chips use the previous sample for modulation instead of + // the current sample + opmod = (RegisterType::MODULATOR_DELAY ? m_feedback[1] : op1value) >> 1; + result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift; + } + else + { + result = (RegisterType::MODULATOR_DELAY ? m_feedback[1] : op1value) >> rshift; + result += m_op[1]->compute_volume(m_op[1]->phase(), am_offset) >> rshift; + int32_t clipmin = -clipmax - 1; + result = clamp(result, clipmin, clipmax); + } + + // add to the output + add_to_output(m_choffs, output, result); +} + + +//------------------------------------------------- +// output_4op - combine 4 operators according to +// the specified algorithm, returning a sum +// according to the rshift and clipmax parameters, +// which vary between different implementations +//------------------------------------------------- + +template +void fm_channel::output_4op(output_data &output, uint32_t rshift, int32_t clipmax) const +{ + // all 4 operators should be populated + assert(m_op[0] != nullptr); + assert(m_op[1] != nullptr); + assert(m_op[2] != nullptr); + assert(m_op[3] != nullptr); + + // AM amount is the same across all operators; compute it once + uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); + + // operator 1 has optional self-feedback + int32_t opmod = 0; + uint32_t feedback = m_regs.ch_feedback(m_choffs); + if (feedback != 0) + opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback); + + // compute the 14-bit volume/value of operator 1 and update the feedback + int32_t op1value = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset); + + // now that the feedback has been computed, skip the rest if all volumes + // are clear; no need to do all this work for nothing + if (m_regs.ch_output_any(m_choffs) == 0) + return; + + // OPM/OPN offer 8 different connection algorithms for 4 operators, + // and OPL3 offers 4 more, which we designate here as 8-11. + // + // The operators are computed in order, with the inputs pulled from + // an array of values (opout) that is populated as we go: + // 0 = 0 + // 1 = O1 + // 2 = O2 + // 3 = O3 + // 4 = (O4) + // 5 = O1+O2 + // 6 = O1+O3 + // 7 = O2+O3 + // + // The s_algorithm_ops table describes the inputs and outputs of each + // algorithm as follows: + // + // ---------x use opout[x] as operator 2 input + // ------xxx- use opout[x] as operator 3 input + // ---xxx---- use opout[x] as operator 4 input + // --x------- include opout[1] in final sum + // -x-------- include opout[2] in final sum + // x--------- include opout[3] in final sum + #define ALGORITHM(op2in, op3in, op4in, op1out, op2out, op3out) \ + ((op2in) | ((op3in) << 1) | ((op4in) << 4) | ((op1out) << 7) | ((op2out) << 8) | ((op3out) << 9)) + static uint16_t const s_algorithm_ops[8+4] = + { + ALGORITHM(1,2,3, 0,0,0), // 0: O1 -> O2 -> O3 -> O4 -> out (O4) + ALGORITHM(0,5,3, 0,0,0), // 1: (O1 + O2) -> O3 -> O4 -> out (O4) + ALGORITHM(0,2,6, 0,0,0), // 2: (O1 + (O2 -> O3)) -> O4 -> out (O4) + ALGORITHM(1,0,7, 0,0,0), // 3: ((O1 -> O2) + O3) -> O4 -> out (O4) + ALGORITHM(1,0,3, 0,1,0), // 4: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4) + ALGORITHM(1,1,1, 0,1,1), // 5: ((O1 -> O2) + (O1 -> O3) + (O1 -> O4)) -> out (O2+O3+O4) + ALGORITHM(1,0,0, 0,1,1), // 6: ((O1 -> O2) + O3 + O4) -> out (O2+O3+O4) + ALGORITHM(0,0,0, 1,1,1), // 7: (O1 + O2 + O3 + O4) -> out (O1+O2+O3+O4) + ALGORITHM(1,2,3, 0,0,0), // 8: O1 -> O2 -> O3 -> O4 -> out (O4) [same as 0] + ALGORITHM(0,2,3, 1,0,0), // 9: (O1 + (O2 -> O3 -> O4)) -> out (O1+O4) [unique] + ALGORITHM(1,0,3, 0,1,0), // 10: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4) [same as 4] + ALGORITHM(0,2,0, 1,0,1) // 11: (O1 + (O2 -> O3) + O4) -> out (O1+O3+O4) [unique] + }; + uint32_t algorithm_ops = s_algorithm_ops[m_regs.ch_algorithm(m_choffs)]; + + // populate the opout table + int16_t opout[8]; + opout[0] = 0; + opout[1] = op1value; + + // compute the 14-bit volume/value of operator 2 + opmod = opout[bitfield(algorithm_ops, 0, 1)] >> 1; + opout[2] = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset); + opout[5] = opout[1] + opout[2]; + + // compute the 14-bit volume/value of operator 3 + opmod = opout[bitfield(algorithm_ops, 1, 3)] >> 1; + opout[3] = m_op[2]->compute_volume(m_op[2]->phase() + opmod, am_offset); + opout[6] = opout[1] + opout[3]; + opout[7] = opout[2] + opout[3]; + + // compute the 14-bit volume/value of operator 4; this could be a noise + // value on the OPM; all algorithms consume OP4 output at a minimum + int32_t result; + if (m_regs.noise_enable() && m_choffs == 7) + result = m_op[3]->compute_noise_volume(am_offset); + else + { + opmod = opout[bitfield(algorithm_ops, 4, 3)] >> 1; + result = m_op[3]->compute_volume(m_op[3]->phase() + opmod, am_offset); + } + result >>= rshift; + + // optionally add OP1, OP2, OP3 + int32_t clipmin = -clipmax - 1; + if (bitfield(algorithm_ops, 7) != 0) + result = clamp(result + (opout[1] >> rshift), clipmin, clipmax); + if (bitfield(algorithm_ops, 8) != 0) + result = clamp(result + (opout[2] >> rshift), clipmin, clipmax); + if (bitfield(algorithm_ops, 9) != 0) + result = clamp(result + (opout[3] >> rshift), clipmin, clipmax); + + // add to the output + add_to_output(m_choffs, output, result); +} + + +//------------------------------------------------- +// output_rhythm_ch6 - special case output +// computation for OPL channel 6 in rhythm mode, +// which outputs a Bass Drum instrument +//------------------------------------------------- + +template +void fm_channel::output_rhythm_ch6(output_data &output, uint32_t rshift, int32_t clipmax) const +{ + (void)clipmax; + // AM amount is the same across all operators; compute it once + uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); + + // Bass Drum: this uses operators 12 and 15 (i.e., channel 6) + // in an almost-normal way, except that if the algorithm is 1, + // the first operator is ignored instead of added in + + // operator 1 has optional self-feedback + int32_t opmod = 0; + uint32_t feedback = m_regs.ch_feedback(m_choffs); + if (feedback != 0) + opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback); + + // compute the 14-bit volume/value of operator 1 and update the feedback + int32_t opout1 = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset); + + // compute the 14-bit volume/value of operator 2, which is the result + opmod = bitfield(m_regs.ch_algorithm(m_choffs), 0) ? 0 : (opout1 >> 1); + int32_t result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift; + + // add to the output + add_to_output(m_choffs, output, result * 2); +} + + +//------------------------------------------------- +// output_rhythm_ch7 - special case output +// computation for OPL channel 7 in rhythm mode, +// which outputs High Hat and Snare Drum +// instruments +//------------------------------------------------- + +template +void fm_channel::output_rhythm_ch7(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const +{ + // AM amount is the same across all operators; compute it once + uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); + uint32_t noise_state = bitfield(m_regs.noise_state(), 0); + + // High Hat: this uses the envelope from operator 13 (channel 7), + // and a combination of noise and the operator 13/17 phase select + // to compute the phase + uint32_t phase = (phase_select << 9) | (0xd0 >> (2 * (noise_state ^ phase_select))); + int32_t result = m_op[0]->compute_volume(phase, am_offset) >> rshift; + + // Snare Drum: this uses the envelope from operator 16 (channel 7), + // and a combination of noise and operator 13 phase to pick a phase + uint32_t op13phase = m_op[0]->phase(); + phase = (0x100 << bitfield(op13phase, 8)) ^ (noise_state << 8); + result += m_op[1]->compute_volume(phase, am_offset) >> rshift; + result = clamp(result, -clipmax - 1, clipmax); + + // add to the output + add_to_output(m_choffs, output, result * 2); +} + + +//------------------------------------------------- +// output_rhythm_ch8 - special case output +// computation for OPL channel 8 in rhythm mode, +// which outputs Tom Tom and Top Cymbal instruments +//------------------------------------------------- + +template +void fm_channel::output_rhythm_ch8(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const +{ + // AM amount is the same across all operators; compute it once + uint32_t am_offset = m_regs.lfo_am_offset(m_choffs); + + // Tom Tom: this is just a single operator processed normally + int32_t result = m_op[0]->compute_volume(m_op[0]->phase(), am_offset) >> rshift; + + // Top Cymbal: this uses the envelope from operator 17 (channel 8), + // and the operator 13/17 phase select to compute the phase + uint32_t phase = 0x100 | (phase_select << 9); + result += m_op[1]->compute_volume(phase, am_offset) >> rshift; + result = clamp(result, -clipmax - 1, clipmax); + + // add to the output + add_to_output(m_choffs, output, result * 2); +} + + + +//********************************************************* +// FM ENGINE BASE +//********************************************************* + +//------------------------------------------------- +// fm_engine_base - constructor +//------------------------------------------------- + +template +fm_engine_base::fm_engine_base(ymfm_interface &intf) : + m_intf(intf), + m_env_counter(0), + m_status(0), + m_clock_prescale(RegisterType::DEFAULT_PRESCALE), + m_irq_mask(STATUS_TIMERA | STATUS_TIMERB), + m_irq_state(0), + m_timer_running{0,0}, + m_total_clocks(0), + m_active_channels(ALL_CHANNELS), + m_modified_channels(ALL_CHANNELS), + m_prepare_count(0) +{ + // inform the interface of their engine + m_intf.m_engine = this; + + // create the channels + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum] = std::make_unique>(*this, RegisterType::channel_offset(chnum)); + + // create the operators + for (uint32_t opnum = 0; opnum < OPERATORS; opnum++) + m_operator[opnum] = std::make_unique>(*this, RegisterType::operator_offset(opnum)); + +#if (YMFM_DEBUG_LOG_WAVFILES) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + m_wavfile[chnum].set_index(chnum); +#endif + + // do the initial operator assignment + assign_operators(); +} + + +//------------------------------------------------- +// reset - reset the overall state +//------------------------------------------------- + +template +void fm_engine_base::reset() +{ + // reset all status bits + set_reset_status(0, 0xff); + + // register type-specific initialization + m_regs.reset(); + + // explicitly write to the mode register since it has side-effects + // QUESTION: old cores initialize this to 0x30 -- who is right? + write(RegisterType::REG_MODE, 0); + + // reset the channels + for (auto &chan : m_channel) + chan->reset(); + + // reset the operators + for (auto &op : m_operator) + op->reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +template +void fm_engine_base::save_restore(ymfm_saved_state &state) +{ + // save our data + state.save_restore(m_env_counter); + state.save_restore(m_status); + state.save_restore(m_clock_prescale); + state.save_restore(m_irq_mask); + state.save_restore(m_irq_state); + state.save_restore(m_timer_running[0]); + state.save_restore(m_timer_running[1]); + state.save_restore(m_total_clocks); + + // save the register/family data + m_regs.save_restore(state); + + // save channel data + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum]->save_restore(state); + + // save operator data + for (uint32_t opnum = 0; opnum < OPERATORS; opnum++) + m_operator[opnum]->save_restore(state); + + // invalidate any caches + invalidate_caches(); +} + + +//------------------------------------------------- +// clock - iterate over all channels, clocking +// them forward one step +//------------------------------------------------- + +template +uint32_t fm_engine_base::clock(uint32_t chanmask) +{ + // update the clock counter + m_total_clocks++; + + // if something was modified, prepare + // also prepare every 4k samples to catch ending notes + if (m_modified_channels != 0 || m_prepare_count++ >= 4096) + { + // reassign operators to channels if dynamic + if (RegisterType::DYNAMIC_OPS) + assign_operators(); + + // call each channel to prepare + m_active_channels = 0; + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + if (m_channel[chnum]->prepare()) + m_active_channels |= 1 << chnum; + + // reset the modified channels and prepare count + m_modified_channels = m_prepare_count = 0; + } + + // if the envelope clock divider is 1, just increment by 4; + // otherwise, increment by 1 and manually wrap when we reach the divide count + if (RegisterType::EG_CLOCK_DIVIDER == 1) + m_env_counter += 4; + else if (bitfield(++m_env_counter, 0, 2) == RegisterType::EG_CLOCK_DIVIDER) + m_env_counter += 4 - RegisterType::EG_CLOCK_DIVIDER; + + // clock the noise generator + int32_t lfo_raw_pm = m_regs.clock_noise_and_lfo(); + + // now update the state of all the channels and operators + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->clock(m_env_counter, lfo_raw_pm); + + // return the envelope counter as it is used to clock ADPCM-A + return m_env_counter; +} + + +//------------------------------------------------- +// output - compute a sum over the relevant +// channels +//------------------------------------------------- + +template +void fm_engine_base::output(output_data &output, uint32_t rshift, int32_t clipmax, uint32_t chanmask) const +{ + // mask out some channels for debug purposes + chanmask &= debug::GLOBAL_FM_CHANNEL_MASK; + + // mask out inactive channels + if (!YMFM_DEBUG_LOG_WAVFILES) + chanmask &= m_active_channels; + + // handle the rhythm case, where some of the operators are dedicated + // to percussion (this is an OPL-specific feature) + if (m_regs.rhythm_enable()) + { + // we don't support the OPM noise channel here; ensure it is off + assert(m_regs.noise_enable() == 0); + + // precompute the operator 13+17 phase selection value + uint32_t op13phase = m_operator[13]->phase(); + uint32_t op17phase = m_operator[17]->phase(); + uint32_t phase_select = (bitfield(op13phase, 2) ^ bitfield(op13phase, 7)) | bitfield(op13phase, 3) | (bitfield(op17phase, 5) ^ bitfield(op17phase, 3)); + + // sum over all the desired channels + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + { +#if (YMFM_DEBUG_LOG_WAVFILES) + auto reference = output; +#endif + if (chnum == 6) + m_channel[chnum]->output_rhythm_ch6(output, rshift, clipmax); + else if (chnum == 7) + m_channel[chnum]->output_rhythm_ch7(phase_select, output, rshift, clipmax); + else if (chnum == 8) + m_channel[chnum]->output_rhythm_ch8(phase_select, output, rshift, clipmax); + else if (m_channel[chnum]->is4op()) + m_channel[chnum]->output_4op(output, rshift, clipmax); + else + m_channel[chnum]->output_2op(output, rshift, clipmax); +#if (YMFM_DEBUG_LOG_WAVFILES) + m_wavfile[chnum].add(output, reference); +#endif + } + } + else + { + // sum over all the desired channels + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + { +#if (YMFM_DEBUG_LOG_WAVFILES) + auto reference = output; +#endif + if (m_channel[chnum]->is4op()) + m_channel[chnum]->output_4op(output, rshift, clipmax); + else + m_channel[chnum]->output_2op(output, rshift, clipmax); +#if (YMFM_DEBUG_LOG_WAVFILES) + m_wavfile[chnum].add(output, reference); +#endif + } + } +} + + +//------------------------------------------------- +// write - handle writes to the OPN registers +//------------------------------------------------- + +template +void fm_engine_base::write(uint16_t regnum, uint8_t data) +{ + debug::log_fm_write("%03X = %02X\n", regnum, data); + + // special case: writes to the mode register can impact IRQs; + // schedule these writes to ensure ordering with timers + if (regnum == RegisterType::REG_MODE) + { + m_intf.ymfm_sync_mode_write(data); + return; + } + + // for now just mark all channels as modified + m_modified_channels = ALL_CHANNELS; + + // most writes are passive, consumed only when needed + uint32_t keyon_channel; + uint32_t keyon_opmask; + if (m_regs.write(regnum, data, keyon_channel, keyon_opmask)) + { + // handle writes to the keyon register(s) + if (keyon_channel < CHANNELS) + { + // normal channel on/off + m_channel[keyon_channel]->keyonoff(keyon_opmask, KEYON_NORMAL, keyon_channel); + } + else if (CHANNELS >= 9 && keyon_channel == RegisterType::RHYTHM_CHANNEL) + { + // special case for the OPL rhythm channels + m_channel[6]->keyonoff(bitfield(keyon_opmask, 4) ? 3 : 0, KEYON_RHYTHM, 6); + m_channel[7]->keyonoff(bitfield(keyon_opmask, 0) | (bitfield(keyon_opmask, 3) << 1), KEYON_RHYTHM, 7); + m_channel[8]->keyonoff(bitfield(keyon_opmask, 2) | (bitfield(keyon_opmask, 1) << 1), KEYON_RHYTHM, 8); + } + } +} + + +template +void fm_engine_base::write_pan(uint16_t chan, uint8_t data) +{ + m_channel[chan]->write_pan(data); +} + +template +int32_t fm_engine_base::get_pan_l(uint16_t chan) +{ + return m_channel[chan]->panL(); +} + +template +int32_t fm_engine_base::get_pan_r(uint16_t chan) +{ + return m_channel[chan]->panR(); +} + + +//------------------------------------------------- +// status - return the current state of the +// status flags +//------------------------------------------------- + +template +uint8_t fm_engine_base::status() const +{ + return m_status & ~STATUS_BUSY & ~m_regs.status_mask(); +} + + +//------------------------------------------------- +// assign_operators - get the current mapping of +// operators to channels and assign them all +//------------------------------------------------- + +template +void fm_engine_base::assign_operators() +{ + typename RegisterType::operator_mapping map; + m_regs.operator_map(map); + + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + for (uint32_t index = 0; index < 4; index++) + { + uint32_t opnum = bitfield(map.chan[chnum], 8 * index, 8); + m_channel[chnum]->assign(index, (opnum == 0xff) ? nullptr : m_operator[opnum].get()); + } +} + + +//------------------------------------------------- +// update_timer - update the state of the given +// timer +//------------------------------------------------- + +template +void fm_engine_base::update_timer(uint32_t tnum, uint32_t enable, int32_t delta_clocks) +{ + // if the timer is live, but not currently enabled, set the timer + if (enable && !m_timer_running[tnum]) + { + // period comes from the registers, and is different for each + uint32_t period = (tnum == 0) ? (1024 - m_regs.timer_a_value()) : 16 * (256 - m_regs.timer_b_value()); + + // caller can also specify a delta to account for other effects + period += delta_clocks; + + // reset it + m_intf.ymfm_set_timer(tnum, period * OPERATORS * m_clock_prescale); + m_timer_running[tnum] = 1; + } + + // if the timer is not live, ensure it is not enabled + else if (!enable) + { + m_intf.ymfm_set_timer(tnum, -1); + m_timer_running[tnum] = 0; + } +} + + +//------------------------------------------------- +// engine_timer_expired - timer has expired - signal +// status and possibly IRQs +//------------------------------------------------- + +template +void fm_engine_base::engine_timer_expired(uint32_t tnum) +{ + // update status + if (tnum == 0 && m_regs.enable_timer_a()) + set_reset_status(STATUS_TIMERA, 0); + else if (tnum == 1 && m_regs.enable_timer_b()) + set_reset_status(STATUS_TIMERB, 0); + + // if timer A fired in CSM mode, trigger CSM on all relevant channels + if (tnum == 0 && m_regs.csm()) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(RegisterType::CSM_TRIGGER_MASK, chnum)) + { + m_channel[chnum]->keyonoff(1, KEYON_CSM, chnum); + m_modified_channels |= 1 << chnum; + } + + // reset + m_timer_running[tnum] = false; + update_timer(tnum, 1, 0); +} + + +//------------------------------------------------- +// check_interrupts - check the interrupt sources +// for interrupts +//------------------------------------------------- + +template +void fm_engine_base::engine_check_interrupts() +{ + // update the state + uint8_t old_state = m_irq_state; + m_irq_state = ((m_status & m_irq_mask & ~m_regs.status_mask()) != 0); + + // set the IRQ status bit + if (m_irq_state) + m_status |= STATUS_IRQ; + else + m_status &= ~STATUS_IRQ; + + // if changed, signal the new state + if (old_state != m_irq_state) + m_intf.ymfm_update_irq(m_irq_state ? true : false); +} + + +//------------------------------------------------- +// engine_mode_write - handle a mode register write +// via timer callback +//------------------------------------------------- + +template +void fm_engine_base::engine_mode_write(uint8_t data) +{ + // mark all channels as modified + m_modified_channels = ALL_CHANNELS; + + // actually write the mode register now + uint32_t dummy1, dummy2; + m_regs.write(RegisterType::REG_MODE, data, dummy1, dummy2); + + // reset IRQ status -- when written, all other bits are ignored + // QUESTION: should this maybe just reset the IRQ bit and not all the bits? + // That is, check_interrupts would only set, this would only clear? + if (m_regs.irq_reset()) + set_reset_status(0, 0x78); + else + { + // reset timer status + uint8_t reset_mask = 0; + if (m_regs.reset_timer_b()) + reset_mask |= RegisterType::STATUS_TIMERB; + if (m_regs.reset_timer_a()) + reset_mask |= RegisterType::STATUS_TIMERA; + set_reset_status(0, reset_mask); + + // load timers; note that timer B gets a small negative adjustment because + // the *16 multiplier is free-running, so the first tick of the clock + // is a bit shorter + update_timer(1, m_regs.load_timer_b(), -(m_total_clocks & 15)); + update_timer(0, m_regs.load_timer_a(), 0); + } +} + +} diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_misc.cpp b/thirdparty/opnmidi/chips/ymfm/ymfm_misc.cpp new file mode 100644 index 000000000..fd0575f55 --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_misc.cpp @@ -0,0 +1,175 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +#include "ymfm_misc.h" + +namespace ymfm +{ + +//********************************************************* +// YM2149 +//********************************************************* + +//------------------------------------------------- +// ym2149 - constructor +//------------------------------------------------- + +ym2149::ym2149(ymfm_interface &intf) : + m_address(0), + m_ssg(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ym2149::reset() +{ + // reset the engines + m_ssg.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ym2149::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_ssg.save_restore(state); +} + + +//------------------------------------------------- +// read_data - read the data register +//------------------------------------------------- + +uint8_t ym2149::read_data() +{ + return m_ssg.read(m_address & 0x0f); +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ym2149::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 3) // BC2,BC1 + { + case 0: // inactive + break; + + case 1: // address + break; + + case 2: // inactive + break; + + case 3: // read + result = read_data(); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ym2149::write_address(uint8_t data) +{ + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym2149::write_data(uint8_t data) +{ + m_ssg.write(m_address & 0x0f, data); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym2149::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) // BC2,BC1 + { + case 0: // address + write_address(data); + break; + + case 1: // inactive + break; + + case 2: // write + write_data(data); + break; + + case 3: // address + write_address(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of SSG sound +//------------------------------------------------- + +void ym2149::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the SSG + m_ssg.clock(); + + // YM2149 keeps the three SSG outputs independent + m_ssg.output(*output); + } +} + +} diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_misc.h b/thirdparty/opnmidi/chips/ymfm/ymfm_misc.h new file mode 100644 index 000000000..628d128f6 --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_misc.h @@ -0,0 +1,93 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_MISC_H +#define YMFM_MISC_H + +#pragma once + +#include "ymfm.h" +#include "ymfm_adpcm.h" +#include "ymfm_ssg.h" + +namespace ymfm +{ + +//********************************************************* +// SSG IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ym2149 + +// ym2149 is just an SSG with no FM part, but we expose FM-like parts so that it +// integrates smoothly with everything else; they just don't do anything +class ym2149 +{ +public: + static constexpr uint32_t OUTPUTS = ssg_engine::OUTPUTS; + static constexpr uint32_t SSG_OUTPUTS = ssg_engine::OUTPUTS; + using output_data = ymfm_output; + + // constructor + ym2149(ymfm_interface &intf); + + // configuration + void ssg_override(ssg_override &intf) { m_ssg.override(intf); } + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return input_clock / ssg_engine::CLOCK_DIVIDER / 8; } + + // read access + uint8_t read_data(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate one sample of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint8_t m_address; // address register + ssg_engine m_ssg; // SSG engine +}; + +} + +#endif // YMFM_MISC_H diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_opn.cpp b/thirdparty/opnmidi/chips/ymfm/ymfm_opn.cpp new file mode 100644 index 000000000..2cc27efd4 --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_opn.cpp @@ -0,0 +1,2500 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +#include "ymfm_opn.h" +#include "ymfm_fm.hpp" + +namespace ymfm +{ + +//********************************************************* +// OPN/OPNA REGISTERS +//********************************************************* + +//------------------------------------------------- +// opn_registers_base - constructor +//------------------------------------------------- + +template +opn_registers_base::opn_registers_base() : + m_lfo_counter(0), + m_lfo_am(0) +{ + // create the waveforms + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) + m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15); +} + + +//------------------------------------------------- +// reset - reset to initial state +//------------------------------------------------- + +template +void opn_registers_base::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); + if (IsOpnA) + { + // enable output on both channels by default + m_regdata[0xb4] = m_regdata[0xb5] = m_regdata[0xb6] = 0xc0; + m_regdata[0x1b4] = m_regdata[0x1b5] = m_regdata[0x1b6] = 0xc0; + } +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +template +void opn_registers_base::save_restore(ymfm_saved_state &state) +{ + if (IsOpnA) + { + state.save_restore(m_lfo_counter); + state.save_restore(m_lfo_am); + } + state.save_restore(m_regdata); +} + + +//------------------------------------------------- +// operator_map - return an array of operator +// indices for each channel; for OPN this is fixed +//------------------------------------------------- + +template<> +void opn_registers_base::operator_map(operator_mapping &dest) const +{ + // Note that the channel index order is 0,2,1,3, so we bitswap the index. + // + // This is because the order in the map is: + // carrier 1, carrier 2, modulator 1, modulator 2 + // + // But when wiring up the connections, the more natural order is: + // carrier 1, modulator 1, carrier 2, modulator 2 + static const operator_mapping s_fixed_map = + { { + operator_list( 0, 6, 3, 9 ), // Channel 0 operators + operator_list( 1, 7, 4, 10 ), // Channel 1 operators + operator_list( 2, 8, 5, 11 ), // Channel 2 operators + } }; + dest = s_fixed_map; +} + +template<> +void opn_registers_base::operator_map(operator_mapping &dest) const +{ + // Note that the channel index order is 0,2,1,3, so we bitswap the index. + // + // This is because the order in the map is: + // carrier 1, carrier 2, modulator 1, modulator 2 + // + // But when wiring up the connections, the more natural order is: + // carrier 1, modulator 1, carrier 2, modulator 2 + static const operator_mapping s_fixed_map = + { { + operator_list( 0, 6, 3, 9 ), // Channel 0 operators + operator_list( 1, 7, 4, 10 ), // Channel 1 operators + operator_list( 2, 8, 5, 11 ), // Channel 2 operators + operator_list( 12, 18, 15, 21 ), // Channel 3 operators + operator_list( 13, 19, 16, 22 ), // Channel 4 operators + operator_list( 14, 20, 17, 23 ), // Channel 5 operators + } }; + dest = s_fixed_map; +} + + +//------------------------------------------------- +// write - handle writes to the register array +//------------------------------------------------- + +template +bool opn_registers_base::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask) +{ + assert(index < REGISTERS); + + // writes in the 0xa0-af/0x1a0-af region are handled as latched pairs + // borrow unused registers 0xb8-bf/0x1b8-bf as temporary holding locations + if ((index & 0xf0) == 0xa0) + { + if (bitfield(index, 0, 2) == 3) + return false; + + uint32_t latchindex = 0xb8 | bitfield(index, 3); + if (IsOpnA) + latchindex |= index & 0x100; + + // writes to the upper half just latch (only low 6 bits matter) + if (bitfield(index, 2)) + m_regdata[latchindex] = data | 0x80; + + // writes to the lower half only commit if the latch is there + else if (bitfield(m_regdata[latchindex], 7)) + { + m_regdata[index] = data; + m_regdata[index | 4] = m_regdata[latchindex] & 0x3f; + m_regdata[latchindex] = 0; + } + return false; + } + else if ((index & 0xf8) == 0xb8) + { + // registers 0xb8-0xbf are used internally + return false; + } + + // everything else is normal + m_regdata[index] = data; + + // handle writes to the key on index + if (index == 0x28) + { + channel = bitfield(data, 0, 2); + if (channel == 3) + return false; + if (IsOpnA) + channel += bitfield(data, 2, 1) * 3; + opmask = bitfield(data, 4, 4); + return true; + } + return false; +} + + +//------------------------------------------------- +// clock_noise_and_lfo - clock the noise and LFO, +// handling clock division, depth, and waveform +// computations +//------------------------------------------------- + +template +int32_t opn_registers_base::clock_noise_and_lfo() +{ + // OPN has no noise generation + + // if LFO not enabled (not present on OPN), quick exit with 0s + if (!IsOpnA || !lfo_enable()) + { + m_lfo_counter = 0; + + // special case: if LFO is disabled on OPNA, it basically just keeps the counter + // at 0; since position 0 gives an AM value of 0x3f, it is important to reflect + // that here; for example, MegaDrive Venom plays some notes with LFO globally + // disabled but enabling LFO on the operators, and it expects this added attenutation + m_lfo_am = IsOpnA ? 0x3f : 0x00; + return 0; + } + + // this table is based on converting the frequencies in the applications + // manual to clock dividers, based on the assumption of a 7-bit LFO value + static uint8_t const lfo_max_count[8] = { 109, 78, 72, 68, 63, 45, 9, 6 }; + uint32_t subcount = uint8_t(m_lfo_counter++); + + // when we cross the divider count, add enough to zero it and cause an + // increment at bit 8; the 7-bit value lives from bits 8-14 + if (subcount >= lfo_max_count[lfo_rate()]) + { + // note: to match the published values this should be 0x100 - subcount; + // however, tests on the hardware and nuked bear out an off-by-one + // error exists that causes the max LFO rate to be faster than published + m_lfo_counter += 0x101 - subcount; + } + + // AM value is 7 bits, staring at bit 8; grab the low 6 directly + m_lfo_am = bitfield(m_lfo_counter, 8, 6); + + // first half of the AM period (bit 6 == 0) is inverted + if (bitfield(m_lfo_counter, 8+6) == 0) + m_lfo_am ^= 0x3f; + + // PM value is 5 bits, starting at bit 10; grab the low 3 directly + int32_t pm = bitfield(m_lfo_counter, 10, 3); + + // PM is reflected based on bit 3 + if (bitfield(m_lfo_counter, 10+3)) + pm ^= 7; + + // PM is negated based on bit 4 + return bitfield(m_lfo_counter, 10+4) ? -pm : pm; +} + + +//------------------------------------------------- +// lfo_am_offset - return the AM offset from LFO +// for the given channel +//------------------------------------------------- + +template +uint32_t opn_registers_base::lfo_am_offset(uint32_t choffs) const +{ + // shift value for AM sensitivity is [7, 3, 1, 0], + // mapping to values of [0, 1.4, 5.9, and 11.8dB] + uint32_t am_shift = (1 << (ch_lfo_am_sens(choffs) ^ 3)) - 1; + + // QUESTION: max sensitivity should give 11.8dB range, but this value + // is directly added to an x.8 attenuation value, which will only give + // 126/256 or ~4.9dB range -- what am I missing? The calculation below + // matches several other emulators, including the Nuked implemenation. + + // raw LFO AM value on OPN is 0-3F, scale that up by a factor of 2 + // (giving 7 bits) before applying the final shift + return (m_lfo_am << 1) >> am_shift; +} + + +//------------------------------------------------- +// cache_operator_data - fill the operator cache +// with prefetched data +//------------------------------------------------- + +template +void opn_registers_base::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache) +{ + // set up the easy stuff + cache.waveform = &m_waveform[0][0]; + + // get frequency from the channel + uint32_t block_freq = cache.block_freq = ch_block_freq(choffs); + + // if multi-frequency mode is enabled and this is channel 2, + // fetch one of the special frequencies + if (multi_freq() && choffs == 2) + { + if (opoffs == 2) + block_freq = cache.block_freq = multi_block_freq(1); + else if (opoffs == 10) + block_freq = cache.block_freq = multi_block_freq(2); + else if (opoffs == 6) + block_freq = cache.block_freq = multi_block_freq(0); + } + + // compute the keycode: block_freq is: + // + // BBBFFFFFFFFFFF + // ^^^^??? + // + // the 5-bit keycode uses the top 4 bits plus a magic formula + // for the final bit + uint32_t keycode = bitfield(block_freq, 10, 4) << 1; + + // lowest bit is determined by a mix of next lower FNUM bits + // according to this equation from the YM2608 manual: + // + // (F11 & (F10 | F9 | F8)) | (!F11 & F10 & F9 & F8) + // + // for speed, we just look it up in a 16-bit constant + keycode |= bitfield(0xfe80, bitfield(block_freq, 7, 4)); + + // detune adjustment + cache.detune = detune_adjustment(op_detune(opoffs), keycode); + + // multiple value, as an x.1 value (0 means 0.5) + cache.multiple = op_multiple(opoffs) * 2; + if (cache.multiple == 0) + cache.multiple = 1; + + // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on + // block_freq, detune, and multiple, so compute it after we've done those + if (!IsOpnA || lfo_enable() == 0 || ch_lfo_pm_sens(choffs) == 0) + cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0); + else + cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC; + + // total level, scaled by 8 + cache.total_level = op_total_level(opoffs) << 3; + + // 4-bit sustain level, but 15 means 31 so effectively 5 bits + cache.eg_sustain = op_sustain_level(opoffs); + cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10; + cache.eg_sustain <<= 5; + + // determine KSR adjustment for enevlope rates + uint32_t ksrval = keycode >> (op_ksr(opoffs) ^ 3); + cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 2, ksrval); + cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 2, ksrval); + cache.eg_rate[EG_SUSTAIN] = effective_rate(op_sustain_rate(opoffs) * 2, ksrval); + cache.eg_rate[EG_RELEASE] = effective_rate(op_release_rate(opoffs) * 4 + 2, ksrval); +} + + +//------------------------------------------------- +// compute_phase_step - compute the phase step +//------------------------------------------------- + +template +uint32_t opn_registers_base::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm) +{ + (void)opoffs; + // OPN phase calculation has only a single detune parameter + // and uses FNUMs instead of keycodes + + // extract frequency number (low 11 bits of block_freq) + uint32_t fnum = bitfield(cache.block_freq, 0, 11) << 1; + + // if there's a non-zero PM sensitivity, compute the adjustment + uint32_t pm_sensitivity = ch_lfo_pm_sens(choffs); + if (pm_sensitivity != 0) + { + // apply the phase adjustment based on the upper 7 bits + // of FNUM and the PM depth parameters + fnum += opn_lfo_pm_phase_adjustment(bitfield(cache.block_freq, 4, 7), pm_sensitivity, lfo_raw_pm); + + // keep fnum to 12 bits + fnum &= 0xfff; + } + + // apply block shift to compute phase step + uint32_t block = bitfield(cache.block_freq, 11, 3); + uint32_t phase_step = (fnum << block) >> 2; + + // apply detune based on the keycode + phase_step += cache.detune; + + // clamp to 17 bits in case detune overflows + // QUESTION: is this specific to the YM2612/3438? + phase_step &= 0x1ffff; + + // apply frequency multiplier (which is cached as an x.1 value) + return (phase_step * cache.multiple) >> 1; +} + + +//------------------------------------------------- +// log_keyon - log a key-on event +//------------------------------------------------- + +template +std::string opn_registers_base::log_keyon(uint32_t choffs, uint32_t opoffs) +{ + uint32_t chnum = (choffs & 3) + 3 * bitfield(choffs, 8); + uint32_t opnum = (opoffs & 15) - ((opoffs & 15) / 4) + 12 * bitfield(opoffs, 8); + + uint32_t block_freq = ch_block_freq(choffs); + if (multi_freq() && choffs == 2) + { + if (opoffs == 2) + block_freq = multi_block_freq(1); + else if (opoffs == 10) + block_freq = multi_block_freq(2); + else if (opoffs == 6) + block_freq = multi_block_freq(0); + } + + char buffer[256]; + char *end = &buffer[0]; + + end += sprintf(end, "%u.%02u freq=%04X dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X", + chnum, opnum, + block_freq, + op_detune(opoffs), + ch_feedback(choffs), + ch_algorithm(choffs), + op_multiple(opoffs), + op_total_level(opoffs), + op_ksr(opoffs), + op_attack_rate(opoffs), + op_decay_rate(opoffs), + op_sustain_rate(opoffs), + op_release_rate(opoffs), + op_sustain_level(opoffs)); + + if (OUTPUTS > 1) + end += sprintf(end, " out=%c%c", + ch_output_0(choffs) ? 'L' : '-', + ch_output_1(choffs) ? 'R' : '-'); + if (op_ssg_eg_enable(opoffs)) + end += sprintf(end, " ssg=%X", op_ssg_eg_mode(opoffs)); + bool am = (op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0); + if (am) + end += sprintf(end, " am=%u", ch_lfo_am_sens(choffs)); + bool pm = (ch_lfo_pm_sens(choffs) != 0); + if (pm) + end += sprintf(end, " pm=%u", ch_lfo_pm_sens(choffs)); + if (am || pm) + end += sprintf(end, " lfo=%02X", lfo_rate()); + if (multi_freq() && choffs == 2) + end += sprintf(end, " multi=1"); + + return buffer; +} + + + +//********************************************************* +// SSG RESAMPLER +//********************************************************* + +//------------------------------------------------- +// add_last - helper to add the last computed +// value to the sums, applying the given scale +//------------------------------------------------- + +template +void ssg_resampler::add_last(int32_t &sum0, int32_t &sum1, int32_t &sum2, int32_t scale) +{ + sum0 += m_last.data[0] * scale; + sum1 += m_last.data[1] * scale; + sum2 += m_last.data[2] * scale; +} + + +//------------------------------------------------- +// clock_and_add - helper to clock a new value +// and then add it to the sums, applying the +// given scale +//------------------------------------------------- + +template +void ssg_resampler::clock_and_add(int32_t &sum0, int32_t &sum1, int32_t &sum2, int32_t scale) +{ + m_ssg.clock(); + m_ssg.output(m_last); + add_last(sum0, sum1, sum2, scale); +} + + +//------------------------------------------------- +// write_to_output - helper to write the sums to +// the appropriate outputs, applying the given +// divisor to the final result +//------------------------------------------------- + +template +void ssg_resampler::write_to_output(OutputType *output, int32_t sum0, int32_t sum1, int32_t sum2, int32_t divisor) +{ + if (MixTo1) + { + // mixing to one, apply a 2/3 factor to prevent overflow + output->data[FirstOutput] = (sum0 + sum1 + sum2) * 2 / (3 * divisor); + } + else + { + // write three outputs in a row + output->data[FirstOutput + 0] = sum0 / divisor; + output->data[FirstOutput + 1] = sum1 / divisor; + output->data[FirstOutput + 2] = sum2 / divisor; + } + + // track the sample index here + m_sampindex++; +} + + +//------------------------------------------------- +// ssg_resampler - constructor +//------------------------------------------------- + +template +ssg_resampler::ssg_resampler(ssg_engine &ssg) : + m_ssg(ssg), + m_sampindex(0), + m_resampler(&ssg_resampler::resample_nop) +{ + m_last.clear(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +template +void ssg_resampler::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_sampindex); + state.save_restore(m_last.data); +} + + +//------------------------------------------------- +// configure - configure a new ratio +//------------------------------------------------- + +template +void ssg_resampler::configure(uint8_t outsamples, uint8_t srcsamples) +{ + switch (outsamples * 10 + srcsamples) + { + case 4*10 + 1: /* 4:1 */ m_resampler = &ssg_resampler::resample_n_1<4>; break; + case 2*10 + 1: /* 2:1 */ m_resampler = &ssg_resampler::resample_n_1<2>; break; + case 4*10 + 3: /* 4:3 */ m_resampler = &ssg_resampler::resample_4_3; break; + case 1*10 + 1: /* 1:1 */ m_resampler = &ssg_resampler::resample_n_1<1>; break; + case 2*10 + 3: /* 2:3 */ m_resampler = &ssg_resampler::resample_2_3; break; + case 1*10 + 3: /* 1:3 */ m_resampler = &ssg_resampler::resample_1_n<3>; break; + case 2*10 + 9: /* 2:9 */ m_resampler = &ssg_resampler::resample_2_9; break; + case 1*10 + 6: /* 1:6 */ m_resampler = &ssg_resampler::resample_1_n<6>; break; + case 0*10 + 0: /* 0:0 */ m_resampler = &ssg_resampler::resample_nop; break; + default: assert(false); break; + } +} + + +//------------------------------------------------- +// resample_n_1 - resample SSG output to the +// target at a rate of 1 SSG sample to every +// n output sample +//------------------------------------------------- + +template +template +void ssg_resampler::resample_n_1(OutputType *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + if (m_sampindex % Multiplier == 0) + { + m_ssg.clock(); + m_ssg.output(m_last); + } + write_to_output(output, m_last.data[0], m_last.data[1], m_last.data[2]); + } +} + + +//------------------------------------------------- +// resample_1_n - resample SSG output to the +// target at a rate of n SSG samples to every +// 1 output sample +//------------------------------------------------- + +template +template +void ssg_resampler::resample_1_n(OutputType *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + int32_t sum0 = 0, sum1 = 0, sum2 = 0; + for (int rep = 0; rep < Divisor; rep++) + clock_and_add(sum0, sum1, sum2); + write_to_output(output, sum0, sum1, sum2, Divisor); + } +} + + +//------------------------------------------------- +// resample_2_9 - resample SSG output to the +// target at a rate of 9 SSG samples to every +// 2 output samples +//------------------------------------------------- + +template +void ssg_resampler::resample_2_9(OutputType *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + int32_t sum0 = 0, sum1 = 0, sum2 = 0; + if (bitfield(m_sampindex, 0) != 0) + add_last(sum0, sum1, sum2, 1); + clock_and_add(sum0, sum1, sum2, 2); + clock_and_add(sum0, sum1, sum2, 2); + clock_and_add(sum0, sum1, sum2, 2); + clock_and_add(sum0, sum1, sum2, 2); + if (bitfield(m_sampindex, 0) == 0) + clock_and_add(sum0, sum1, sum2, 1); + write_to_output(output, sum0, sum1, sum2, 9); + } +} + + +//------------------------------------------------- +// resample_2_3 - resample SSG output to the +// target at a rate of 3 SSG samples to every +// 2 output samples +//------------------------------------------------- + +template +void ssg_resampler::resample_2_3(OutputType *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + int32_t sum0 = 0, sum1 = 0, sum2 = 0; + if (bitfield(m_sampindex, 0) == 0) + { + clock_and_add(sum0, sum1, sum2, 2); + clock_and_add(sum0, sum1, sum2, 1); + } + else + { + add_last(sum0, sum1, sum2, 1); + clock_and_add(sum0, sum1, sum2, 2); + } + write_to_output(output, sum0, sum1, sum2, 3); + } +} + + +//------------------------------------------------- +// resample_4_3 - resample SSG output to the +// target at a rate of 3 SSG samples to every +// 4 output samples +//------------------------------------------------- + +template +void ssg_resampler::resample_4_3(OutputType *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + int32_t sum0 = 0, sum1 = 0, sum2 = 0; + int32_t step = bitfield(m_sampindex, 0, 2); + add_last(sum0, sum1, sum2, step); + if (step != 3) + clock_and_add(sum0, sum1, sum2, 3 - step); + write_to_output(output, sum0, sum1, sum2, 3); + } +} + + +//------------------------------------------------- +// resample_nop - no-op resampler +//------------------------------------------------- + +template +void ssg_resampler::resample_nop(OutputType *output, uint32_t numsamples) +{ + (void)output; + // nothing to do except increment the sample index + m_sampindex += numsamples; +} + + + +//********************************************************* +// YM2203 +//********************************************************* + +//------------------------------------------------- +// ym2203 - constructor +//------------------------------------------------- + +ym2203::ym2203(ymfm_interface &intf) : + m_fidelity(OPN_FIDELITY_MAX), + m_address(0), + m_fm(intf), + m_ssg(intf), + m_ssg_resampler(m_ssg) +{ + m_last_fm.clear(); + update_prescale(m_fm.clock_prescale()); +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ym2203::reset() +{ + // reset the engines + m_fm.reset(); + m_ssg.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ym2203::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + state.save_restore(m_last_fm.data); + + m_fm.save_restore(state); + m_ssg.save_restore(state); + m_ssg_resampler.save_restore(state); + + update_prescale(m_fm.clock_prescale()); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ym2203::read_status() +{ + uint8_t result = m_fm.status(); + if (m_fm.intf().ymfm_is_busy()) + result |= fm_engine::STATUS_BUSY; + return result; +} + + +//------------------------------------------------- +// read_data - read the data register +//------------------------------------------------- + +uint8_t ym2203::read_data() +{ + uint8_t result = 0; + if (m_address < 0x10) + { + // 00-0F: Read from SSG + result = m_ssg.read(m_address & 0x0f); + } + return result; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ym2203::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 1) + { + case 0: // status port + result = read_status(); + break; + + case 1: // data port (only SSG) + result = read_data(); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ym2203::write_address(uint8_t data) +{ + // just set the address + m_address = data; + + // special case: update the prescale + if (m_address >= 0x2d && m_address <= 0x2f) + { + // 2D-2F: prescaler select + if (m_address == 0x2d) + update_prescale(6); + else if (m_address == 0x2e && m_fm.clock_prescale() == 6) + update_prescale(3); + else if (m_address == 0x2f) + update_prescale(2); + } +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym2203::write_data(uint8_t data) +{ + if (m_address < 0x10) + { + // 00-0F: write to SSG + m_ssg.write(m_address & 0x0f, data); + } + else + { + // 10-FF: write to FM + m_fm.write(m_address, data); + } + + // mark busy for a bit + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym2203::write(uint32_t offset, uint8_t data) +{ + switch (offset & 1) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void ym2203::generate(output_data *output, uint32_t numsamples) +{ + // FM output is just repeated the prescale number of times; note that + // 0 is a special 1.5 case + if (m_fm_samples_per_output != 0) + { + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0) + clock_fm(); + output->data[0] = m_last_fm.data[0]; + } + } + else + { + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + uint32_t step = (m_ssg_resampler.sampindex() + samp) % 3; + if (step == 0) + clock_fm(); + output->data[0] = m_last_fm.data[0]; + if (step == 1) + { + clock_fm(); + output->data[0] = (output->data[0] + m_last_fm.data[0]) / 2; + } + } + } + + // resample the SSG as configured + m_ssg_resampler.resample(output - numsamples, numsamples); +} + + +//------------------------------------------------- +// update_prescale - update the prescale value, +// recomputing derived values +//------------------------------------------------- + +void ym2203::update_prescale(uint8_t prescale) +{ + // tell the FM engine + m_fm.set_clock_prescale(prescale); + m_ssg.prescale_changed(); + + // Fidelity: ---- minimum ---- ---- medium ----- ---- maximum----- + // rate = clock/24 rate = clock/12 rate = clock/4 + // Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate + // 6 3:1 2:3 6:1 4:3 18:1 4:1 + // 3 1.5:1 1:3 3:1 2:3 9:1 2:1 + // 2 1:1 1:6 2:1 1:3 6:1 1:1 + + // compute the number of FM samples per output sample, and select the + // resampler function + if (m_fidelity == OPN_FIDELITY_MIN) + { + switch (prescale) + { + default: + case 6: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break; + case 3: m_fm_samples_per_output = 0; m_ssg_resampler.configure(1, 3); break; + case 2: m_fm_samples_per_output = 1; m_ssg_resampler.configure(1, 6); break; + } + } + else if (m_fidelity == OPN_FIDELITY_MED) + { + switch (prescale) + { + default: + case 6: m_fm_samples_per_output = 6; m_ssg_resampler.configure(4, 3); break; + case 3: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break; + case 2: m_fm_samples_per_output = 2; m_ssg_resampler.configure(1, 3); break; + } + } + else + { + switch (prescale) + { + default: + case 6: m_fm_samples_per_output = 18; m_ssg_resampler.configure(4, 1); break; + case 3: m_fm_samples_per_output = 9; m_ssg_resampler.configure(2, 1); break; + case 2: m_fm_samples_per_output = 6; m_ssg_resampler.configure(1, 1); break; + } + } + + // if overriding the SSG, override the configuration with the nop + // resampler to at least keep the sample index moving forward + if (m_ssg.overridden()) + m_ssg_resampler.configure(0, 0); +} + + +//------------------------------------------------- +// clock_fm - clock FM state +//------------------------------------------------- + +void ym2203::clock_fm() +{ + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; OPN is full 14-bit with no intermediate clipping + m_fm.output(m_last_fm.clear(), 0, 32767, fm_engine::ALL_CHANNELS); + + // convert to 10.3 floating point value for the DAC and back + m_last_fm.roundtrip_fp(); +} + + + +//********************************************************* +// YM2608 +//********************************************************* + +//------------------------------------------------- +// ym2608 - constructor +//------------------------------------------------- + +ym2608::ym2608(ymfm_interface &intf) : + m_fidelity(OPN_FIDELITY_MAX), + m_address(0), + m_irq_enable(0x1f), + m_flag_control(0x1c), + m_fm(intf), + m_ssg(intf), + m_ssg_resampler(m_ssg), + m_adpcm_a(intf, 0), + m_adpcm_b(intf) +{ + m_last_fm.clear(); + update_prescale(m_fm.clock_prescale()); +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ym2608::reset() +{ + // reset the engines + m_fm.reset(); + m_ssg.reset(); + m_adpcm_a.reset(); + m_adpcm_b.reset(); + + // configure ADPCM percussion sounds; these are present in an embedded ROM + m_adpcm_a.set_start_end(0, 0x0000, 0x01bf); // bass drum + m_adpcm_a.set_start_end(1, 0x01c0, 0x043f); // snare drum + m_adpcm_a.set_start_end(2, 0x0440, 0x1b7f); // top cymbal + m_adpcm_a.set_start_end(3, 0x1b80, 0x1cff); // high hat + m_adpcm_a.set_start_end(4, 0x1d00, 0x1f7f); // tom tom + m_adpcm_a.set_start_end(5, 0x1f80, 0x1fff); // rim shot + + // initialize our special interrupt states, then read the upper status + // register, which updates the IRQs + m_irq_enable = 0x1f; + m_flag_control = 0x1c; + read_status_hi(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ym2608::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + state.save_restore(m_irq_enable); + state.save_restore(m_flag_control); + state.save_restore(m_last_fm.data); + + m_fm.save_restore(state); + m_ssg.save_restore(state); + m_ssg_resampler.save_restore(state); + m_adpcm_a.save_restore(state); + m_adpcm_b.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ym2608::read_status() +{ + uint8_t result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB); + if (m_fm.intf().ymfm_is_busy()) + result |= fm_engine::STATUS_BUSY; + return result; +} + + +//------------------------------------------------- +// read_data - read the data register +//------------------------------------------------- + +uint8_t ym2608::read_data() +{ + uint8_t result = 0; + if (m_address < 0x10) + { + // 00-0F: Read from SSG + result = m_ssg.read(m_address & 0x0f); + } + else if (m_address == 0xff) + { + // FF: ID code + result = 1; + } + return result; +} + + +//------------------------------------------------- +// read_status_hi - read the extended status +// register +//------------------------------------------------- + +uint8_t ym2608::read_status_hi() +{ + // fetch regular status + uint8_t status = m_fm.status() & ~(STATUS_ADPCM_B_EOS | STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_PLAYING); + + // fetch ADPCM-B status, and merge in the bits + uint8_t adpcm_status = m_adpcm_b.status(); + if ((adpcm_status & adpcm_b_channel::STATUS_EOS) != 0) + status |= STATUS_ADPCM_B_EOS; + if ((adpcm_status & adpcm_b_channel::STATUS_BRDY) != 0) + status |= STATUS_ADPCM_B_BRDY; + if ((adpcm_status & adpcm_b_channel::STATUS_PLAYING) != 0) + status |= STATUS_ADPCM_B_PLAYING; + + // turn off any bits that have been requested to be masked + status &= ~(m_flag_control & 0x1f); + + // update the status so that IRQs are propagated + m_fm.set_reset_status(status, ~status); + + // merge in the busy flag + if (m_fm.intf().ymfm_is_busy()) + status |= fm_engine::STATUS_BUSY; + return status; +} + + +//------------------------------------------------- +// read_data_hi - read the upper data register +//------------------------------------------------- + +uint8_t ym2608::read_data_hi() +{ + uint8_t result = 0; + if ((m_address & 0xff) < 0x10) + { + // 00-0F: Read from ADPCM-B + result = m_adpcm_b.read(m_address & 0x0f); + } + return result; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ym2608::read(uint32_t offset) +{ + uint8_t result = 0; + switch (offset & 3) + { + case 0: // status port, YM2203 compatible + result = read_status(); + break; + + case 1: // data port (only SSG) + result = read_data(); + break; + + case 2: // status port, extended + result = read_status_hi(); + break; + + case 3: // ADPCM-B data + result = read_data_hi(); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ym2608::write_address(uint8_t data) +{ + // just set the address + m_address = data; + + // special case: update the prescale + if (m_address >= 0x2d && m_address <= 0x2f) + { + // 2D-2F: prescaler select + if (m_address == 0x2d) + update_prescale(6); + else if (m_address == 0x2e && m_fm.clock_prescale() == 6) + update_prescale(3); + else if (m_address == 0x2f) + update_prescale(2); + } +} + + +//------------------------------------------------- +// write - handle a write to the data register +//------------------------------------------------- + +void ym2608::write_data(uint8_t data) +{ + // ignore if paired with upper address + if (bitfield(m_address, 8)) + return; + + if (m_address < 0x10) + { + // 00-0F: write to SSG + m_ssg.write(m_address & 0x0f, data); + } + else if (m_address < 0x20) + { + // 10-1F: write to ADPCM-A + m_adpcm_a.write(m_address & 0x0f, data); + } + else if (m_address == 0x29) + { + // 29: special IRQ mask register + m_irq_enable = data; + m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x1f); + } + else + { + // 20-28, 2A-FF: write to FM + m_fm.write(m_address, data); + } + + // mark busy for a bit + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ym2608::write_address_hi(uint8_t data) +{ + // just set the address + m_address = 0x100 | data; +} + + +//------------------------------------------------- +// write_data_hi - handle a write to the upper +// data register +//------------------------------------------------- + +void ym2608::write_data_hi(uint8_t data) +{ + // ignore if paired with upper address + if (!bitfield(m_address, 8)) + return; + + if (m_address < 0x110) + { + // 100-10F: write to ADPCM-B + m_adpcm_b.write(m_address & 0x0f, data); + } + else if (m_address == 0x110) + { + // 110: IRQ flag control + if (bitfield(data, 7)) + m_fm.set_reset_status(0, 0xff); + else + { + m_flag_control = data; + m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x1f); + } + } + else + { + // 111-1FF: write to FM + m_fm.write(m_address, data); + } + + // mark busy for a bit + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym2608::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // upper address port + write_address_hi(data); + break; + + case 3: // upper data port + write_data_hi(data); + break; + } +} + +void ym2608::write_pan(uint16_t chan, uint8_t data) +{ + m_fm.write_pan(chan, data); +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void ym2608::generate(output_data *output, uint32_t numsamples) +{ + // FM output is just repeated the prescale number of times; note that + // 0 is a special 1.5 case + if (m_fm_samples_per_output != 0) + { + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0) + clock_fm_and_adpcm(); + output->data[0] = m_last_fm.data[0]; + output->data[1] = m_last_fm.data[1]; + } + } + else + { + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + uint32_t step = (m_ssg_resampler.sampindex() + samp) % 3; + if (step == 0) + clock_fm_and_adpcm(); + output->data[0] = m_last_fm.data[0]; + output->data[1] = m_last_fm.data[1]; + if (step == 1) + { + clock_fm_and_adpcm(); + output->data[0] = (output->data[0] + m_last_fm.data[0]) / 2; + output->data[1] = (output->data[1] + m_last_fm.data[1]) / 2; + } + } + } + + // resample the SSG as configured + m_ssg_resampler.resample(output - numsamples, numsamples); +} + + +//------------------------------------------------- +// update_prescale - update the prescale value, +// recomputing derived values +//------------------------------------------------- + +void ym2608::update_prescale(uint8_t prescale) +{ + // tell the FM engine + m_fm.set_clock_prescale(prescale); + m_ssg.prescale_changed(); + + // Fidelity: ---- minimum ---- ---- medium ----- ---- maximum----- + // rate = clock/48 rate = clock/24 rate = clock/8 + // Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate + // 6 3:1 2:3 6:1 4:3 18:1 4:1 + // 3 1.5:1 1:3 3:1 2:3 9:1 2:1 + // 2 1:1 1:6 2:1 1:3 6:1 1:1 + + // compute the number of FM samples per output sample, and select the + // resampler function + if (m_fidelity == OPN_FIDELITY_MIN) + { + switch (prescale) + { + default: + case 6: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break; + case 3: m_fm_samples_per_output = 0; m_ssg_resampler.configure(1, 3); break; + case 2: m_fm_samples_per_output = 1; m_ssg_resampler.configure(1, 6); break; + } + } + else if (m_fidelity == OPN_FIDELITY_MED) + { + switch (prescale) + { + default: + case 6: m_fm_samples_per_output = 6; m_ssg_resampler.configure(4, 3); break; + case 3: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break; + case 2: m_fm_samples_per_output = 2; m_ssg_resampler.configure(1, 3); break; + } + } + else + { + switch (prescale) + { + default: + case 6: m_fm_samples_per_output = 18; m_ssg_resampler.configure(4, 1); break; + case 3: m_fm_samples_per_output = 9; m_ssg_resampler.configure(2, 1); break; + case 2: m_fm_samples_per_output = 6; m_ssg_resampler.configure(1, 1); break; + } + } + + // if overriding the SSG, override the configuration with the nop + // resampler to at least keep the sample index moving forward + if (m_ssg.overridden()) + m_ssg_resampler.configure(0, 0); +} + + +//------------------------------------------------- +// clock_fm_and_adpcm - clock FM and ADPCM state +//------------------------------------------------- + +void ym2608::clock_fm_and_adpcm() +{ + // top bit of the IRQ enable flags controls 3-channel vs 6-channel mode + uint32_t fmmask = bitfield(m_irq_enable, 7) ? 0x3f : 0x07; + + // clock the system + uint32_t env_counter = m_fm.clock(fm_engine::ALL_CHANNELS); + + // clock the ADPCM-A engine on every envelope cycle + // (channels 4 and 5 clock every 2 envelope clocks) + if (bitfield(env_counter, 0, 2) == 0) + m_adpcm_a.clock(bitfield(env_counter, 2) ? 0x0f : 0x3f); + + // clock the ADPCM-B engine every cycle + m_adpcm_b.clock(); + + // update the FM content; OPNA is 13-bit with no intermediate clipping + m_fm.output(m_last_fm.clear(), 1, 32767, fmmask); + + // mix in the ADPCM and clamp + m_adpcm_a.output(m_last_fm, 0x3f); + m_adpcm_b.output(m_last_fm, 1); + m_last_fm.clamp16(); +} + + +//********************************************************* +// YMF288 +//********************************************************* + +// YMF288 is a YM2608 with the following changes: +// * ADPCM-B part removed +// * prescaler removed (fixed at 6) +// * CSM removed +// * Low power mode added +// * SSG tone frequency is altered in some way? (explicitly DC for Tp 0-7, also double volume in some cases) +// * I/O ports removed +// * Shorter busy times +// * All registers can be read + +//------------------------------------------------- +// ymf288 - constructor +//------------------------------------------------- + +ymf288::ymf288(ymfm_interface &intf) : + m_fidelity(OPN_FIDELITY_MAX), + m_address(0), + m_irq_enable(0x03), + m_flag_control(0x03), + m_fm(intf), + m_ssg(intf), + m_ssg_resampler(m_ssg), + m_adpcm_a(intf, 0) +{ + m_last_fm.clear(); + update_prescale(); +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ymf288::reset() +{ + // reset the engines + m_fm.reset(); + m_ssg.reset(); + m_adpcm_a.reset(); + + // configure ADPCM percussion sounds; these are present in an embedded ROM + m_adpcm_a.set_start_end(0, 0x0000, 0x01bf); // bass drum + m_adpcm_a.set_start_end(1, 0x01c0, 0x043f); // snare drum + m_adpcm_a.set_start_end(2, 0x0440, 0x1b7f); // top cymbal + m_adpcm_a.set_start_end(3, 0x1b80, 0x1cff); // high hat + m_adpcm_a.set_start_end(4, 0x1d00, 0x1f7f); // tom tom + m_adpcm_a.set_start_end(5, 0x1f80, 0x1fff); // rim shot + + // initialize our special interrupt states, then read the upper status + // register, which updates the IRQs + m_irq_enable = 0x03; + m_flag_control = 0x00; + read_status_hi(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ymf288::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + state.save_restore(m_irq_enable); + state.save_restore(m_flag_control); + state.save_restore(m_last_fm.data); + + m_fm.save_restore(state); + m_ssg.save_restore(state); + m_ssg_resampler.save_restore(state); + m_adpcm_a.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ymf288::read_status() +{ + uint8_t result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB); + if (m_fm.intf().ymfm_is_busy()) + result |= fm_engine::STATUS_BUSY; + return result; +} + + +//------------------------------------------------- +// read_data - read the data register +//------------------------------------------------- + +uint8_t ymf288::read_data() +{ + uint8_t result = 0; + if (m_address < 0x0e) + { + // 00-0D: Read from SSG + result = m_ssg.read(m_address & 0x0f); + } + else if (m_address < 0x10) + { + // 0E-0F: I/O ports not supported + result = 0xff; + } + else if (m_address == 0xff) + { + // FF: ID code + result = 2; + } + else if (ymf288_mode()) + { + // registers are readable in YMF288 mode + result = m_fm.regs().read(m_address); + } + return result; +} + + +//------------------------------------------------- +// read_status_hi - read the extended status +// register +//------------------------------------------------- + +uint8_t ymf288::read_status_hi() +{ + // fetch regular status + uint8_t status = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB); + + // turn off any bits that have been requested to be masked + status &= ~(m_flag_control & 0x03); + + // update the status so that IRQs are propagated + m_fm.set_reset_status(status, ~status); + + // merge in the busy flag + if (m_fm.intf().ymfm_is_busy()) + status |= fm_engine::STATUS_BUSY; + return status; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ymf288::read(uint32_t offset) +{ + uint8_t result = 0; + switch (offset & 3) + { + case 0: // status port, YM2203 compatible + result = read_status(); + break; + + case 1: // data port + result = read_data(); + break; + + case 2: // status port, extended + result = read_status_hi(); + break; + + case 3: // unmapped + debug::log_unexpected_read_write("Unexpected read from YMF288 offset %d\n", offset & 3); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ymf288::write_address(uint8_t data) +{ + // just set the address + m_address = data; + + // in YMF288 mode, busy is signaled after address writes too + if (ymf288_mode()) + m_fm.intf().ymfm_set_busy_end(16); +} + + +//------------------------------------------------- +// write - handle a write to the data register +//------------------------------------------------- + +void ymf288::write_data(uint8_t data) +{ + // ignore if paired with upper address + if (bitfield(m_address, 8)) + return; + + // wait times are shorter in YMF288 mode + int busy_cycles = ymf288_mode() ? 16 : 32 * m_fm.clock_prescale(); + if (m_address < 0x0e) + { + // 00-0D: write to SSG + m_ssg.write(m_address & 0x0f, data); + } + else if (m_address < 0x10) + { + // 0E-0F: I/O ports not supported + } + else if (m_address < 0x20) + { + // 10-1F: write to ADPCM-A + m_adpcm_a.write(m_address & 0x0f, data); + busy_cycles = 32 * m_fm.clock_prescale(); + } + else if (m_address == 0x27) + { + // 27: mode register; CSM isn't supported so disable it + data &= 0x7f; + m_fm.write(m_address, data); + } + else if (m_address == 0x29) + { + // 29: special IRQ mask register + m_irq_enable = data; + m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x03); + } + else + { + // 20-27, 2A-FF: write to FM + m_fm.write(m_address, data); + } + + // mark busy for a bit + m_fm.intf().ymfm_set_busy_end(busy_cycles); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf288::write_address_hi(uint8_t data) +{ + // just set the address + m_address = 0x100 | data; + + // in YMF288 mode, busy is signaled after address writes too + if (ymf288_mode()) + m_fm.intf().ymfm_set_busy_end(16); +} + + +//------------------------------------------------- +// write_data_hi - handle a write to the upper +// data register +//------------------------------------------------- + +void ymf288::write_data_hi(uint8_t data) +{ + // ignore if paired with upper address + if (!bitfield(m_address, 8)) + return; + + // wait times are shorter in YMF288 mode + int busy_cycles = ymf288_mode() ? 16 : 32 * m_fm.clock_prescale(); + if (m_address == 0x110) + { + // 110: IRQ flag control + if (bitfield(data, 7)) + m_fm.set_reset_status(0, 0xff); + else + { + m_flag_control = data; + m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x03); + } + } + else + { + // 100-10F,111-1FF: write to FM + m_fm.write(m_address, data); + } + + // mark busy for a bit + m_fm.intf().ymfm_set_busy_end(busy_cycles); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ymf288::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // upper address port + write_address_hi(data); + break; + + case 3: // upper data port + write_data_hi(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void ymf288::generate(output_data *output, uint32_t numsamples) +{ + // FM output is just repeated the prescale number of times; note that + // 0 is a special 1.5 case + if (m_fm_samples_per_output != 0) + { + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0) + clock_fm_and_adpcm(); + output->data[0] = m_last_fm.data[0]; + output->data[1] = m_last_fm.data[1]; + } + } + else + { + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + uint32_t step = (m_ssg_resampler.sampindex() + samp) % 3; + if (step == 0) + clock_fm_and_adpcm(); + output->data[0] = m_last_fm.data[0]; + output->data[1] = m_last_fm.data[1]; + if (step == 1) + { + clock_fm_and_adpcm(); + output->data[0] = (output->data[0] + m_last_fm.data[0]) / 2; + output->data[1] = (output->data[1] + m_last_fm.data[1]) / 2; + } + } + } + + // resample the SSG as configured + m_ssg_resampler.resample(output - numsamples, numsamples); +} + + +//------------------------------------------------- +// update_prescale - update the prescale value, +// recomputing derived values +//------------------------------------------------- + +void ymf288::update_prescale() +{ + // Fidelity: ---- minimum ---- ---- medium ----- ---- maximum----- + // rate = clock/144 rate = clock/144 rate = clock/16 + // Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate + // 6 1:1 2:9 1:1 2:9 9:1 2:1 + + // compute the number of FM samples per output sample, and select the + // resampler function + if (m_fidelity == OPN_FIDELITY_MIN || m_fidelity == OPN_FIDELITY_MED) + { + m_fm_samples_per_output = 1; + m_ssg_resampler.configure(2, 9); + } + else + { + m_fm_samples_per_output = 9; + m_ssg_resampler.configure(2, 1); + } + + // if overriding the SSG, override the configuration with the nop + // resampler to at least keep the sample index moving forward + if (m_ssg.overridden()) + m_ssg_resampler.configure(0, 0); +} + + +//------------------------------------------------- +// clock_fm_and_adpcm - clock FM and ADPCM state +//------------------------------------------------- + +void ymf288::clock_fm_and_adpcm() +{ + // top bit of the IRQ enable flags controls 3-channel vs 6-channel mode + uint32_t fmmask = bitfield(m_irq_enable, 7) ? 0x3f : 0x07; + + // clock the system + uint32_t env_counter = m_fm.clock(fm_engine::ALL_CHANNELS); + + // clock the ADPCM-A engine on every envelope cycle + // (channels 4 and 5 clock every 2 envelope clocks) + if (bitfield(env_counter, 0, 2) == 0) + m_adpcm_a.clock(bitfield(env_counter, 2) ? 0x0f : 0x3f); + + // update the FM content; OPNA is 13-bit with no intermediate clipping + m_fm.output(m_last_fm.clear(), 1, 32767, fmmask); + + // mix in the ADPCM + m_adpcm_a.output(m_last_fm, 0x3f); +} + + + +//********************************************************* +// YM2610 +//********************************************************* + +//------------------------------------------------- +// ym2610 - constructor +//------------------------------------------------- + +ym2610::ym2610(ymfm_interface &intf, uint8_t channel_mask) : + m_fidelity(OPN_FIDELITY_MAX), + m_address(0), + m_fm_mask(channel_mask), + m_eos_status(0x00), + m_flag_mask(EOS_FLAGS_MASK), + m_fm(intf), + m_ssg(intf), + m_ssg_resampler(m_ssg), + m_adpcm_a(intf, 8), + m_adpcm_b(intf, 8) +{ + update_prescale(); +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ym2610::reset() +{ + // reset the engines + m_fm.reset(); + m_ssg.reset(); + m_adpcm_a.reset(); + m_adpcm_b.reset(); + + // initialize our special interrupt states + m_eos_status = 0x00; + m_flag_mask = EOS_FLAGS_MASK; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ym2610::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + state.save_restore(m_eos_status); + state.save_restore(m_flag_mask); + + m_fm.save_restore(state); + m_ssg.save_restore(state); + m_ssg_resampler.save_restore(state); + m_adpcm_a.save_restore(state); + m_adpcm_b.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ym2610::read_status() +{ + uint8_t result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB); + if (m_fm.intf().ymfm_is_busy()) + result |= fm_engine::STATUS_BUSY; + return result; +} + + +//------------------------------------------------- +// read_data - read the data register +//------------------------------------------------- + +uint8_t ym2610::read_data() +{ + uint8_t result = 0; + if (m_address < 0x0e) + { + // 00-0D: Read from SSG + result = m_ssg.read(m_address & 0x0f); + } + else if (m_address < 0x10) + { + // 0E-0F: I/O ports not supported + result = 0xff; + } + else if (m_address == 0xff) + { + // FF: ID code + result = 1; + } + return result; +} + + +//------------------------------------------------- +// read_status_hi - read the extended status +// register +//------------------------------------------------- + +uint8_t ym2610::read_status_hi() +{ + return m_eos_status & m_flag_mask; +} + + +//------------------------------------------------- +// read_data_hi - read the upper data register +//------------------------------------------------- + +uint8_t ym2610::read_data_hi() +{ + uint8_t result = 0; + return result; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ym2610::read(uint32_t offset) +{ + uint8_t result = 0; + switch (offset & 3) + { + case 0: // status port, YM2203 compatible + result = read_status(); + break; + + case 1: // data port (only SSG) + result = read_data(); + break; + + case 2: // status port, extended + result = read_status_hi(); + break; + + case 3: // ADPCM-B data + result = read_data_hi(); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ym2610::write_address(uint8_t data) +{ + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write - handle a write to the data register +//------------------------------------------------- + +void ym2610::write_data(uint8_t data) +{ + // ignore if paired with upper address + if (bitfield(m_address, 8)) + return; + + if (m_address < 0x0e) + { + // 00-0D: write to SSG + m_ssg.write(m_address & 0x0f, data); + } + else if (m_address < 0x10) + { + // 0E-0F: I/O ports not supported + } + else if (m_address < 0x1c) + { + // 10-1B: write to ADPCM-B + // YM2610 effectively forces external mode on, and disables recording + if (m_address == 0x10) + data = (data | 0x20) & ~0x40; + m_adpcm_b.write(m_address & 0x0f, data); + } + else if (m_address == 0x1c) + { + // 1C: EOS flag reset + m_flag_mask = ~data & EOS_FLAGS_MASK; + m_eos_status &= ~(data & EOS_FLAGS_MASK); + } + else + { + // 1D-FF: write to FM + m_fm.write(m_address, data); + } + + // mark busy for a bit + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ym2610::write_address_hi(uint8_t data) +{ + // just set the address + m_address = 0x100 | data; +} + + +//------------------------------------------------- +// write_data_hi - handle a write to the upper +// data register +//------------------------------------------------- + +void ym2610::write_data_hi(uint8_t data) +{ + // ignore if paired with upper address + if (!bitfield(m_address, 8)) + return; + + if (m_address < 0x130) + { + // 100-12F: write to ADPCM-A + m_adpcm_a.write(m_address & 0x3f, data); + } + else + { + // 130-1FF: write to FM + m_fm.write(m_address, data); + } + + // mark busy for a bit + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym2610::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // upper address port + write_address_hi(data); + break; + + case 3: // upper data port + write_data_hi(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void ym2610::generate(output_data *output, uint32_t numsamples) +{ + // FM output is just repeated the prescale number of times + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0) + clock_fm_and_adpcm(); + output->data[0] = m_last_fm.data[0]; + output->data[1] = m_last_fm.data[1]; + } + + // resample the SSG as configured + m_ssg_resampler.resample(output - numsamples, numsamples); +} + + +//------------------------------------------------- +// update_prescale - update the prescale value, +// recomputing derived values +//------------------------------------------------- + +void ym2610::update_prescale() +{ + // Fidelity: ---- minimum ---- ---- medium ----- ---- maximum----- + // rate = clock/144 rate = clock/144 rate = clock/16 + // Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate + // 6 1:1 2:9 1:1 2:9 9:1 2:1 + + // compute the number of FM samples per output sample, and select the + // resampler function + if (m_fidelity == OPN_FIDELITY_MIN || m_fidelity == OPN_FIDELITY_MED) + { + m_fm_samples_per_output = 1; + m_ssg_resampler.configure(2, 9); + } + else + { + m_fm_samples_per_output = 9; + m_ssg_resampler.configure(2, 1); + } + + // if overriding the SSG, override the configuration with the nop + // resampler to at least keep the sample index moving forward + if (m_ssg.overridden()) + m_ssg_resampler.configure(0, 0); +} + + +//------------------------------------------------- +// clock_fm_and_adpcm - clock FM and ADPCM state +//------------------------------------------------- + +void ym2610::clock_fm_and_adpcm() +{ + // clock the system + uint32_t env_counter = m_fm.clock(m_fm_mask); + + // clock the ADPCM-A engine on every envelope cycle + if (bitfield(env_counter, 0, 2) == 0) + m_eos_status |= m_adpcm_a.clock(0x3f); + + // clock the ADPCM-B engine every cycle + m_adpcm_b.clock(); + + // we track the last ADPCM-B EOS value in bit 6 (which is hidden from callers); + // if it changed since the last sample, update the visible EOS state in bit 7 + uint8_t live_eos = ((m_adpcm_b.status() & adpcm_b_channel::STATUS_EOS) != 0) ? 0x40 : 0x00; + if (((live_eos ^ m_eos_status) & 0x40) != 0) + m_eos_status = (m_eos_status & ~0xc0) | live_eos | (live_eos << 1); + + // update the FM content; OPNB is 13-bit with no intermediate clipping + m_fm.output(m_last_fm.clear(), 1, 32767, m_fm_mask); + + // mix in the ADPCM and clamp + m_adpcm_a.output(m_last_fm, 0x3f); + m_adpcm_b.output(m_last_fm, 1); + m_last_fm.clamp16(); +} + + + +//********************************************************* +// YM2612 +//********************************************************* + +//------------------------------------------------- +// ym2612 - constructor +//------------------------------------------------- + +ym2612::ym2612(ymfm_interface &intf) : + m_address(0), + m_dac_data(0), + m_dac_enable(0), + m_fm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ym2612::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ym2612::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + state.save_restore(m_dac_data); + state.save_restore(m_dac_enable); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ym2612::read_status() +{ + uint8_t result = m_fm.status(); + if (m_fm.intf().ymfm_is_busy()) + result |= fm_engine::STATUS_BUSY; + return result; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ym2612::read(uint32_t offset) +{ + uint8_t result = 0; + switch (offset & 3) + { + case 0: // status port, YM2203 compatible + result = read_status(); + break; + + case 1: // data port (unused) + case 2: // status port, extended + case 3: // data port (unused) + debug::log_unexpected_read_write("Unexpected read from YM2612 offset %d\n", offset & 3); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ym2612::write_address(uint8_t data) +{ + // just set the address + m_address = data; +} + + +//------------------------------------------------- +// write_data - handle a write to the data +// register +//------------------------------------------------- + +void ym2612::write_data(uint8_t data) +{ + // ignore if paired with upper address + if (bitfield(m_address, 8)) + return; + + if (m_address == 0x2a) + { + // 2A: DAC data (most significant 8 bits) + m_dac_data = (m_dac_data & ~0x1fe) | ((data ^ 0x80) << 1); + } + else if (m_address == 0x2b) + { + // 2B: DAC enable (bit 7) + m_dac_enable = bitfield(data, 7); + } + else if (m_address == 0x2c) + { + // 2C: test/low DAC bit + m_dac_data = (m_dac_data & ~1) | bitfield(data, 3); + } + else + { + // 00-29, 2D-FF: write to FM + m_fm.write(m_address, data); + } + + // mark busy for a bit + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ym2612::write_address_hi(uint8_t data) +{ + // just set the address + m_address = 0x100 | data; +} + + +//------------------------------------------------- +// write_data_hi - handle a write to the upper +// data register +//------------------------------------------------- + +void ym2612::write_data_hi(uint8_t data) +{ + // ignore if paired with upper address + if (!bitfield(m_address, 8)) + return; + + // 100-1FF: write to FM + m_fm.write(m_address, data); + + // mark busy for a bit + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ym2612::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // upper address port + write_address_hi(data); + break; + + case 3: // upper data port + write_data_hi(data); + break; + } +} + +void ym2612::write_pan(uint16_t chan, uint8_t data) +{ + m_fm.write_pan(chan, data); +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void ym2612::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // sum individual channels to apply DAC discontinuity on each + output->clear(); + output_data temp; + + // first do FM-only channels; OPN2 is 9-bit with intermediate clipping + int const last_fm_channel = m_dac_enable ? 5 : 6; + for (int chan = 0; chan < last_fm_channel; chan++) + { + m_fm.output(temp.clear(), 5, 256, 1 << chan); + output->data[0] += (dac_discontinuity(temp.data[0]) * m_fm.get_pan_l(chan) / 65535); + output->data[1] += (dac_discontinuity(temp.data[1]) * m_fm.get_pan_r(chan) / 65535); + } + + // add in DAC + if (m_dac_enable) + { + // DAC enabled: start with DAC value then add the first 5 channels only + int32_t dacval = dac_discontinuity(int16_t(m_dac_data << 7) >> 7); + output->data[0] += m_fm.regs().ch_output_0(0x102) ? dacval : dac_discontinuity(0); + output->data[1] += m_fm.regs().ch_output_1(0x102) ? dacval : dac_discontinuity(0); + } + + // output is technically multiplexed rather than mixed, but that requires + // a better sound mixer than we usually have, so just average over the six + // channels; also apply a 64/65 factor to account for the discontinuity + // adjustment above + output->data[0] = (output->data[0] * 128) * 64 / (6 * 65); + output->data[1] = (output->data[1] * 128) * 64 / (6 * 65); + } +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void ym3438::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // first do FM-only channels; OPN2C is 9-bit with intermediate clipping + if (!m_dac_enable) + { + // DAC disabled: all 6 channels sum together + m_fm.output(output->clear(), 5, 256, fm_engine::ALL_CHANNELS); + } + else + { + // DAC enabled: start with DAC value then add the first 5 channels only + int32_t dacval = int16_t(m_dac_data << 7) >> 7; + output->data[0] = m_fm.regs().ch_output_0(0x102) ? dacval : 0; + output->data[1] = m_fm.regs().ch_output_1(0x102) ? dacval : 0; + m_fm.output(*output, 5, 256, fm_engine::ALL_CHANNELS ^ (1 << 5)); + } + + // YM3438 doesn't have the same DAC discontinuity, though its output is + // multiplexed like the YM2612 + output->data[0] = (output->data[0] * 128) / 6; + output->data[1] = (output->data[1] * 128) / 6; + } +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void ymf276::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // first do FM-only channels; OPN2L is 14-bit with intermediate clipping + if (!m_dac_enable) + { + // DAC disabled: all 6 channels sum together + m_fm.output(output->clear(), 0, 8191, fm_engine::ALL_CHANNELS); + } + else + { + // DAC enabled: start with DAC value then add the first 5 channels only + int32_t dacval = int16_t(m_dac_data << 7) >> 7; + output->data[0] = m_fm.regs().ch_output_0(0x102) ? dacval : 0; + output->data[1] = m_fm.regs().ch_output_1(0x102) ? dacval : 0; + m_fm.output(*output, 0, 8191, fm_engine::ALL_CHANNELS ^ (1 << 5)); + } + + // YMF276 is properly mixed; it shifts down 1 bit before clamping + output->data[0] = clamp(output->data[0] >> 1, -32768, 32767); + output->data[1] = clamp(output->data[1] >> 1, -32768, 32767); + } +} + +} diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_opn.h b/thirdparty/opnmidi/chips/ymfm/ymfm_opn.h new file mode 100644 index 000000000..72ad31a00 --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_opn.h @@ -0,0 +1,807 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_OPN_H +#define YMFM_OPN_H + +#pragma once + +#include "ymfm.h" +#include "ymfm_adpcm.h" +#include "ymfm_fm.h" +#include "ymfm_ssg.h" + +namespace ymfm +{ + +//********************************************************* +// REGISTER CLASSES +//********************************************************* + +// ======================> opn_registers_base + +// +// OPN register map: +// +// System-wide registers: +// 21 xxxxxxxx Test register +// 22 ----x--- LFO enable [OPNA+ only] +// -----xxx LFO rate [OPNA+ only] +// 24 xxxxxxxx Timer A value (upper 8 bits) +// 25 ------xx Timer A value (lower 2 bits) +// 26 xxxxxxxx Timer B value +// 27 xx------ CSM/Multi-frequency mode for channel #2 +// --x----- Reset timer B +// ---x---- Reset timer A +// ----x--- Enable timer B +// -----x-- Enable timer A +// ------x- Load timer B +// -------x Load timer A +// 28 x------- Key on/off operator 4 +// -x------ Key on/off operator 3 +// --x----- Key on/off operator 2 +// ---x---- Key on/off operator 1 +// ------xx Channel select +// +// Per-channel registers (channel in address bits 0-1) +// Note that all these apply to address+100 as well on OPNA+ +// A0-A3 xxxxxxxx Frequency number lower 8 bits +// A4-A7 --xxx--- Block (0-7) +// -----xxx Frequency number upper 3 bits +// B0-B3 --xxx--- Feedback level for operator 1 (0-7) +// -----xxx Operator connection algorithm (0-7) +// B4-B7 x------- Pan left [OPNA] +// -x------ Pan right [OPNA] +// --xx---- LFO AM shift (0-3) [OPNA+ only] +// -----xxx LFO PM depth (0-7) [OPNA+ only] +// +// Per-operator registers (channel in address bits 0-1, operator in bits 2-3) +// Note that all these apply to address+100 as well on OPNA+ +// 30-3F -xxx---- Detune value (0-7) +// ----xxxx Multiple value (0-15) +// 40-4F -xxxxxxx Total level (0-127) +// 50-5F xx------ Key scale rate (0-3) +// ---xxxxx Attack rate (0-31) +// 60-6F x------- LFO AM enable [OPNA] +// ---xxxxx Decay rate (0-31) +// 70-7F ---xxxxx Sustain rate (0-31) +// 80-8F xxxx---- Sustain level (0-15) +// ----xxxx Release rate (0-15) +// 90-9F ----x--- SSG-EG enable +// -----xxx SSG-EG envelope (0-7) +// +// Special multi-frequency registers (channel implicitly #2; operator in address bits 0-1) +// A8-AB xxxxxxxx Frequency number lower 8 bits +// AC-AF --xxx--- Block (0-7) +// -----xxx Frequency number upper 3 bits +// +// Internal (fake) registers: +// B8-BB --xxxxxx Latched frequency number upper bits (from A4-A7) +// BC-BF --xxxxxx Latched frequency number upper bits (from AC-AF) +// + +template +class opn_registers_base : public fm_registers_base +{ +public: + // constants + static constexpr uint32_t OUTPUTS = IsOpnA ? 2 : 1; + static constexpr uint32_t CHANNELS = IsOpnA ? 6 : 3; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + static constexpr uint32_t OPERATORS = CHANNELS * 4; + static constexpr uint32_t WAVEFORMS = 1; + static constexpr uint32_t REGISTERS = IsOpnA ? 0x200 : 0x100; + static constexpr uint32_t REG_MODE = 0x27; + static constexpr uint32_t DEFAULT_PRESCALE = 6; + static constexpr uint32_t EG_CLOCK_DIVIDER = 3; + static constexpr bool EG_HAS_SSG = true; + static constexpr bool MODULATOR_DELAY = false; + static constexpr uint32_t CSM_TRIGGER_MASK = 1 << 2; + static constexpr uint8_t STATUS_TIMERA = 0x01; + static constexpr uint8_t STATUS_TIMERB = 0x02; + static constexpr uint8_t STATUS_BUSY = 0x80; + static constexpr uint8_t STATUS_IRQ = 0; + + // constructor + opn_registers_base(); + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // map channel number to register offset + static inline uint32_t channel_offset(uint32_t chnum) + { + assert(chnum < CHANNELS); + if (!IsOpnA) + return chnum; + else + return (chnum % 3) + 0x100 * (chnum / 3); + } + + // map operator number to register offset + static inline uint32_t operator_offset(uint32_t opnum) + { + assert(opnum < OPERATORS); + if (!IsOpnA) + return opnum + opnum / 3; + else + return (opnum % 12) + ((opnum % 12) / 3) + 0x100 * (opnum / 12); + } + + // return an array of operator indices for each channel + struct operator_mapping { uint32_t chan[CHANNELS]; }; + void operator_map(operator_mapping &dest) const; + + // read a register value + uint8_t read(uint16_t index) const { return m_regdata[index]; } + + // handle writes to the register array + bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask); + + // clock the noise and LFO, if present, returning LFO PM value + int32_t clock_noise_and_lfo(); + + // reset the LFO + void reset_lfo() { m_lfo_counter = 0; } + + // return the AM offset from LFO for the given channel + uint32_t lfo_am_offset(uint32_t choffs) const; + + // return LFO/noise states + uint32_t noise_state() const { return 0; } + + // caching helpers + void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache); + + // compute the phase step, given a PM value + uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm); + + // log a key-on event + std::string log_keyon(uint32_t choffs, uint32_t opoffs); + + // system-wide registers + uint32_t test() const { return byte(0x21, 0, 8); } + uint32_t lfo_enable() const { return IsOpnA ? byte(0x22, 3, 1) : 0; } + uint32_t lfo_rate() const { return IsOpnA ? byte(0x22, 0, 3) : 0; } + uint32_t timer_a_value() const { return word(0x24, 0, 8, 0x25, 0, 2); } + uint32_t timer_b_value() const { return byte(0x26, 0, 8); } + uint32_t csm() const { return (byte(0x27, 6, 2) == 2); } + uint32_t multi_freq() const { return (byte(0x27, 6, 2) != 0); } + uint32_t reset_timer_b() const { return byte(0x27, 5, 1); } + uint32_t reset_timer_a() const { return byte(0x27, 4, 1); } + uint32_t enable_timer_b() const { return byte(0x27, 3, 1); } + uint32_t enable_timer_a() const { return byte(0x27, 2, 1); } + uint32_t load_timer_b() const { return byte(0x27, 1, 1); } + uint32_t load_timer_a() const { return byte(0x27, 0, 1); } + uint32_t multi_block_freq(uint32_t num) const { return word(0xac, 0, 6, 0xa8, 0, 8, num); } + + // per-channel registers + uint32_t ch_block_freq(uint32_t choffs) const { return word(0xa4, 0, 6, 0xa0, 0, 8, choffs); } + uint32_t ch_feedback(uint32_t choffs) const { return byte(0xb0, 3, 3, choffs); } + uint32_t ch_algorithm(uint32_t choffs) const { return byte(0xb0, 0, 3, choffs); } + uint32_t ch_output_any(uint32_t choffs) const { return IsOpnA ? byte(0xb4, 6, 2, choffs) : 1; } + uint32_t ch_output_0(uint32_t choffs) const { return IsOpnA ? byte(0xb4, 7, 1, choffs) : 1; } + uint32_t ch_output_1(uint32_t choffs) const { return IsOpnA ? byte(0xb4, 6, 1, choffs) : 0; } + uint32_t ch_output_2(uint32_t choffs) const { (void)choffs; return 0; } + uint32_t ch_output_3(uint32_t choffs) const { (void)choffs; return 0; } + uint32_t ch_lfo_am_sens(uint32_t choffs) const { return IsOpnA ? byte(0xb4, 4, 2, choffs) : 0; } + uint32_t ch_lfo_pm_sens(uint32_t choffs) const { return IsOpnA ? byte(0xb4, 0, 3, choffs) : 0; } + + // per-operator registers + uint32_t op_detune(uint32_t opoffs) const { return byte(0x30, 4, 3, opoffs); } + uint32_t op_multiple(uint32_t opoffs) const { return byte(0x30, 0, 4, opoffs); } + uint32_t op_total_level(uint32_t opoffs) const { return byte(0x40, 0, 7, opoffs); } + uint32_t op_ksr(uint32_t opoffs) const { return byte(0x50, 6, 2, opoffs); } + uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x50, 0, 5, opoffs); } + uint32_t op_decay_rate(uint32_t opoffs) const { return byte(0x60, 0, 5, opoffs); } + uint32_t op_lfo_am_enable(uint32_t opoffs) const { return IsOpnA ? byte(0x60, 7, 1, opoffs) : 0; } + uint32_t op_sustain_rate(uint32_t opoffs) const { return byte(0x70, 0, 5, opoffs); } + uint32_t op_sustain_level(uint32_t opoffs) const { return byte(0x80, 4, 4, opoffs); } + uint32_t op_release_rate(uint32_t opoffs) const { return byte(0x80, 0, 4, opoffs); } + uint32_t op_ssg_eg_enable(uint32_t opoffs) const { return byte(0x90, 3, 1, opoffs); } + uint32_t op_ssg_eg_mode(uint32_t opoffs) const { return byte(0x90, 0, 3, opoffs); } + +protected: + // return a bitfield extracted from a byte + uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const + { + return bitfield(m_regdata[offset + extra_offset], start, count); + } + + // return a bitfield extracted from a pair of bytes, MSBs listed first + uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const + { + return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset); + } + + // internal state + uint32_t m_lfo_counter; // LFO counter + uint8_t m_lfo_am; // current LFO AM value + uint8_t m_regdata[REGISTERS]; // register data + uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms +}; + +using opn_registers = opn_registers_base; +using opna_registers = opn_registers_base; + + + +//********************************************************* +// OPN IMPLEMENTATION CLASSES +//********************************************************* + +// A note about prescaling and sample rates. +// +// YM2203, YM2608, and YM2610 contain an onboard SSG (basically, a YM2149). +// In order to properly generate sound at fully fidelity, the output sample +// rate of the YM2149 must be input_clock / 8. This is much higher than the +// FM needs, but in the interest of keeping things simple, the OPN generate +// functions will output at the higher rate and just replicate the last FM +// sample as many times as needed. +// +// To make things even more complicated, the YM2203 and YM2608 allow for +// software-controlled prescaling, which affects the FM and SSG clocks in +// different ways. There are three settings: divide by 6/4 (FM/SSG); divide +// by 3/2; and divide by 2/1. +// +// Thus, the minimum output sample rate needed by each part of the chip +// varies with the prescale as follows: +// +// ---- YM2203 ----- ---- YM2608 ----- ---- YM2610 ----- +// Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate +// 6 /72 /16 /144 /32 /144 /32 +// 3 /36 /8 /72 /16 +// 2 /24 /4 /48 /8 +// +// If we standardized on the fastest SSG rate, we'd end up with the following +// (ratios are output_samples:source_samples): +// +// ---- YM2203 ----- ---- YM2608 ----- ---- YM2610 ----- +// rate = clock/4 rate = clock/8 rate = clock/16 +// Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate +// 6 18:1 4:1 18:1 4:1 9:1 2:1 +// 3 9:1 2:1 9:1 2:1 +// 2 6:1 1:1 6:1 1:1 +// +// However, that's a pretty big performance hit for minimal gain. Going to +// the other extreme, we could standardize on the fastest FM rate, but then +// at least one prescale case (3) requires the FM to be smeared across two +// output samples: +// +// ---- YM2203 ----- ---- YM2608 ----- ---- YM2610 ----- +// rate = clock/24 rate = clock/48 rate = clock/144 +// Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate +// 6 3:1 2:3 3:1 2:3 1:1 2:9 +// 3 1.5:1 1:3 1.5:1 1:3 +// 2 1:1 1:6 1:1 1:6 +// +// Stepping back one factor of 2 addresses that issue: +// +// ---- YM2203 ----- ---- YM2608 ----- ---- YM2610 ----- +// rate = clock/12 rate = clock/24 rate = clock/144 +// Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate +// 6 6:1 4:3 6:1 4:3 1:1 2:9 +// 3 3:1 2:3 3:1 2:3 +// 2 2:1 1:3 2:1 1:3 +// +// This gives us three levels of output fidelity: +// OPN_FIDELITY_MAX -- highest sample rate, using fastest SSG rate +// OPN_FIDELITY_MIN -- lowest sample rate, using fastest FM rate +// OPN_FIDELITY_MED -- medium sample rate such that FM is never smeared +// +// At the maximum clocks for YM2203/YM2608 (4Mhz/8MHz), these rates will +// end up as: +// OPN_FIDELITY_MAX = 1000kHz +// OPN_FIDELITY_MIN = 166kHz +// OPN_FIEDLITY_MED = 333kHz + + +// ======================> opn_fidelity + +enum opn_fidelity : uint8_t +{ + OPN_FIDELITY_MAX, + OPN_FIDELITY_MIN, + OPN_FIDELITY_MED, + + OPN_FIDELITY_DEFAULT = OPN_FIDELITY_MAX +}; + + +// ======================> ssg_resampler + +template +class ssg_resampler +{ +private: + // helper to add the last computed value to the sums, applying the given scale + void add_last(int32_t &sum0, int32_t &sum1, int32_t &sum2, int32_t scale = 1); + + // helper to clock a new value and then add it to the sums, applying the given scale + void clock_and_add(int32_t &sum0, int32_t &sum1, int32_t &sum2, int32_t scale = 1); + + // helper to write the sums to the appropriate outputs, applying the given + // divisor to the final result + void write_to_output(OutputType *output, int32_t sum0, int32_t sum1, int32_t sum2, int32_t divisor = 1); + +public: + // constructor + ssg_resampler(ssg_engine &ssg); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // get the current sample index + uint32_t sampindex() const { return m_sampindex; } + + // configure the ratio + void configure(uint8_t outsamples, uint8_t srcsamples); + + // resample + void resample(OutputType *output, uint32_t numsamples) + { + (this->*m_resampler)(output, numsamples); + } + +private: + // resample SSG output to the target at a rate of 1 SSG sample + // to every n output samples + template + void resample_n_1(OutputType *output, uint32_t numsamples); + + // resample SSG output to the target at a rate of n SSG samples + // to every 1 output sample + template + void resample_1_n(OutputType *output, uint32_t numsamples); + + // resample SSG output to the target at a rate of 9 SSG samples + // to every 2 output samples + void resample_2_9(OutputType *output, uint32_t numsamples); + + // resample SSG output to the target at a rate of 3 SSG samples + // to every 1 output sample + void resample_1_3(OutputType *output, uint32_t numsamples); + + // resample SSG output to the target at a rate of 3 SSG samples + // to every 2 output samples + void resample_2_3(OutputType *output, uint32_t numsamples); + + // resample SSG output to the target at a rate of 3 SSG samples + // to every 4 output samples + void resample_4_3(OutputType *output, uint32_t numsamples); + + // no-op resampler + void resample_nop(OutputType *output, uint32_t numsamples); + + // define a pointer type + using resample_func = void (ssg_resampler::*)(OutputType *output, uint32_t numsamples); + + // internal state + ssg_engine &m_ssg; + uint32_t m_sampindex; + resample_func m_resampler; + ssg_engine::output_data m_last; +}; + + +// ======================> ym2203 + +class ym2203 +{ +public: + using fm_engine = fm_engine_base; + static constexpr uint32_t FM_OUTPUTS = fm_engine::OUTPUTS; + static constexpr uint32_t SSG_OUTPUTS = ssg_engine::OUTPUTS; + static constexpr uint32_t OUTPUTS = FM_OUTPUTS + SSG_OUTPUTS; + using output_data = ymfm_output; + + // constructor + ym2203(ymfm_interface &intf); + + // configuration + void ssg_override(ssg_override &intf) { m_ssg.override(intf); } + void set_fidelity(opn_fidelity fidelity) { m_fidelity = fidelity; update_prescale(m_fm.clock_prescale()); } + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const + { + switch (m_fidelity) + { + case OPN_FIDELITY_MIN: return input_clock / 24; + case OPN_FIDELITY_MED: return input_clock / 12; + default: + case OPN_FIDELITY_MAX: return input_clock / 4; + } + } + uint32_t ssg_effective_clock(uint32_t input_clock) const { uint32_t scale = m_fm.clock_prescale() * 2 / 3; return input_clock * 2 / scale; } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate one sample of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal helpers + void update_prescale(uint8_t prescale); + void clock_fm(); + + // internal state + opn_fidelity m_fidelity; // configured fidelity + uint8_t m_address; // address register + uint8_t m_fm_samples_per_output; // how many samples to repeat + fm_engine::output_data m_last_fm; // last FM output + fm_engine m_fm; // core FM engine + ssg_engine m_ssg; // SSG engine + ssg_resampler m_ssg_resampler; // SSG resampler helper +}; + + + +//********************************************************* +// OPNA IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ym2608 + +class ym2608 +{ + static constexpr uint8_t STATUS_ADPCM_B_EOS = 0x04; + static constexpr uint8_t STATUS_ADPCM_B_BRDY = 0x08; + static constexpr uint8_t STATUS_ADPCM_B_ZERO = 0x10; + static constexpr uint8_t STATUS_ADPCM_B_PLAYING = 0x20; + +public: + using fm_engine = fm_engine_base; + static constexpr uint32_t FM_OUTPUTS = fm_engine::OUTPUTS; + static constexpr uint32_t SSG_OUTPUTS = 1; + static constexpr uint32_t OUTPUTS = FM_OUTPUTS + SSG_OUTPUTS; + using output_data = ymfm_output; + + // constructor + ym2608(ymfm_interface &intf); + + // configuration + void ssg_override(ssg_override &intf) { m_ssg.override(intf); } + void set_fidelity(opn_fidelity fidelity) { m_fidelity = fidelity; update_prescale(m_fm.clock_prescale()); } + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const + { + switch (m_fidelity) + { + case OPN_FIDELITY_MIN: return input_clock / 48; + case OPN_FIDELITY_MED: return input_clock / 24; + default: + case OPN_FIDELITY_MAX: return input_clock / 8; + } + } + uint32_t ssg_effective_clock(uint32_t input_clock) const { uint32_t scale = m_fm.clock_prescale() * 2 / 3; return input_clock / scale; } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data(); + uint8_t read_status_hi(); + uint8_t read_data_hi(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write_data_hi(uint8_t data); + void write(uint32_t offset, uint8_t data); + + void write_pan(uint16_t chan, uint8_t data); + + // generate one sample of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal helpers + void update_prescale(uint8_t prescale); + void clock_fm_and_adpcm(); + + // internal state + opn_fidelity m_fidelity; // configured fidelity + uint16_t m_address; // address register + uint8_t m_fm_samples_per_output; // how many samples to repeat + uint8_t m_irq_enable; // IRQ enable register + uint8_t m_flag_control; // flag control register + fm_engine::output_data m_last_fm; // last FM output + fm_engine m_fm; // core FM engine + ssg_engine m_ssg; // SSG engine + ssg_resampler m_ssg_resampler; // SSG resampler helper + adpcm_a_engine m_adpcm_a; // ADPCM-A engine + adpcm_b_engine m_adpcm_b; // ADPCM-B engine +}; + + +// ======================> ymf288 + +class ymf288 +{ +public: + using fm_engine = fm_engine_base; + static constexpr uint32_t FM_OUTPUTS = fm_engine::OUTPUTS; + static constexpr uint32_t SSG_OUTPUTS = 1; + static constexpr uint32_t OUTPUTS = FM_OUTPUTS + SSG_OUTPUTS; + using output_data = ymfm_output; + + // constructor + ymf288(ymfm_interface &intf); + + // configuration + void ssg_override(ssg_override &intf) { m_ssg.override(intf); } + void set_fidelity(opn_fidelity fidelity) { m_fidelity = fidelity; update_prescale(); } + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const + { + switch (m_fidelity) + { + case OPN_FIDELITY_MIN: return input_clock / 144; + case OPN_FIDELITY_MED: return input_clock / 144; + default: + case OPN_FIDELITY_MAX: return input_clock / 16; + } + } + uint32_t ssg_effective_clock(uint32_t input_clock) const { return input_clock / 4; } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data(); + uint8_t read_status_hi(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write_data_hi(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate one sample of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal helpers + bool ymf288_mode() { return ((m_fm.regs().read(0x20) & 0x02) != 0); } + void update_prescale(); + void clock_fm_and_adpcm(); + + // internal state + opn_fidelity m_fidelity; // configured fidelity + uint16_t m_address; // address register + uint8_t m_fm_samples_per_output; // how many samples to repeat + uint8_t m_irq_enable; // IRQ enable register + uint8_t m_flag_control; // flag control register + fm_engine::output_data m_last_fm; // last FM output + fm_engine m_fm; // core FM engine + ssg_engine m_ssg; // SSG engine + ssg_resampler m_ssg_resampler; // SSG resampler helper + adpcm_a_engine m_adpcm_a; // ADPCM-A engine +}; + + +// ======================> ym2610/ym2610b + +class ym2610 +{ + static constexpr uint8_t EOS_FLAGS_MASK = 0xbf; + +public: + using fm_engine = fm_engine_base; + static constexpr uint32_t FM_OUTPUTS = fm_engine::OUTPUTS; + static constexpr uint32_t SSG_OUTPUTS = 1; + static constexpr uint32_t OUTPUTS = FM_OUTPUTS + SSG_OUTPUTS; + using output_data = ymfm_output; + + // constructor + ym2610(ymfm_interface &intf, uint8_t channel_mask = 0x36); + + // configuration + void ssg_override(ssg_override &intf) { m_ssg.override(intf); } + void set_fidelity(opn_fidelity fidelity) { m_fidelity = fidelity; update_prescale(); } + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const + { + switch (m_fidelity) + { + case OPN_FIDELITY_MIN: return input_clock / 144; + case OPN_FIDELITY_MED: return input_clock / 144; + default: + case OPN_FIDELITY_MAX: return input_clock / 16; + } + } + uint32_t ssg_effective_clock(uint32_t input_clock) const { return input_clock / 4; } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data(); + uint8_t read_status_hi(); + uint8_t read_data_hi(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write_data_hi(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate one sample of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal helpers + void update_prescale(); + void clock_fm_and_adpcm(); + + // internal state + opn_fidelity m_fidelity; // configured fidelity + uint16_t m_address; // address register + uint8_t const m_fm_mask; // FM channel mask + uint8_t m_fm_samples_per_output; // how many samples to repeat + uint8_t m_eos_status; // end-of-sample signals + uint8_t m_flag_mask; // flag mask control + fm_engine::output_data m_last_fm; // last FM output + fm_engine m_fm; // core FM engine + ssg_engine m_ssg; // core FM engine + ssg_resampler m_ssg_resampler; // SSG resampler helper + adpcm_a_engine m_adpcm_a; // ADPCM-A engine + adpcm_b_engine m_adpcm_b; // ADPCM-B engine +}; + +class ym2610b : public ym2610 +{ +public: + // constructor + ym2610b(ymfm_interface &intf) : ym2610(intf, 0x3f) { } +}; + + +// ======================> ym2612 + +class ym2612 +{ +public: + using fm_engine = fm_engine_base; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + using output_data = fm_engine::output_data; + + // constructor + ym2612(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write_data_hi(uint8_t data); + void write(uint32_t offset, uint8_t data); + + void write_pan(uint16_t chan, uint8_t data); + + // generate one sample of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // simulate the DAC discontinuity + static inline int32_t dac_discontinuity(int32_t value) { return (value < 0) ? (value - 3) : (value + 4); } + // constexpr int32_t dac_discontinuity(int32_t value) const { return (value < 0) ? (value - 3) : (value + 4); } + + // internal state + uint16_t m_address; // address register + uint16_t m_dac_data; // 9-bit DAC data + uint8_t m_dac_enable; // DAC enabled? + fm_engine m_fm; // core FM engine +}; + + +// ======================> ym3438 + +class ym3438 : public ym2612 +{ +public: + ym3438(ymfm_interface &intf) : ym2612(intf) { } + + // generate one sample of sound + void generate(output_data *output, uint32_t numsamples = 1); +}; + + +// ======================> ymf276 + +class ymf276 : public ym2612 +{ +public: + ymf276(ymfm_interface &intf) : ym2612(intf) { } + + // generate one sample of sound + void generate(output_data *output, uint32_t numsamples); +}; + +} + + +#endif // YMFM_OPN_H diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_pcm.cpp b/thirdparty/opnmidi/chips/ymfm/ymfm_pcm.cpp new file mode 100644 index 000000000..fcadf716f --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_pcm.cpp @@ -0,0 +1,714 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +#include "ymfm_pcm.h" +#include "ymfm_fm.h" +#include "ymfm_fm.hpp" + +namespace ymfm +{ + +//********************************************************* +// PCM REGISTERS +//********************************************************* + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void pcm_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); + m_regdata[0xf8] = 0x1b; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_regdata); +} + + +//------------------------------------------------- +// cache_channel_data - update the cache with +// data from the registers +//------------------------------------------------- + +void pcm_registers::cache_channel_data(uint32_t choffs, pcm_cache &cache) +{ + // compute step from octave and fnumber; the math here implies + // a .18 fraction but .16 should be perfectly fine + int32_t octave = int8_t(ch_octave(choffs) << 4) >> 4; + uint32_t fnum = ch_fnumber(choffs); + cache.step = ((0x400 | fnum) << (octave + 7)) >> 2; + + // total level is computed as a .10 value for interpolation + cache.total_level = ch_total_level(choffs) << 10; + + // compute panning values in terms of envelope attenuation + int32_t panpot = int8_t(ch_panpot(choffs) << 4) >> 4; + if (panpot >= 0) + { + cache.pan_left = (panpot == 7) ? 0x3ff : 0x20 * panpot; + cache.pan_right = 0; + } + else if (panpot >= -7) + { + cache.pan_left = 0; + cache.pan_right = (panpot == -7) ? 0x3ff : -0x20 * panpot; + } + else + cache.pan_left = cache.pan_right = 0x3ff; + + // determine the LFO stepping value; this how much to add to a running + // x.18 value for the LFO; steps were derived from frequencies in the + // manual and come out very close with these values + static const uint8_t s_lfo_steps[8] = { 1, 12, 19, 25, 31, 35, 37, 42 }; + cache.lfo_step = s_lfo_steps[ch_lfo_speed(choffs)]; + + // AM LFO depth values, derived from the manual; note each has at most + // 2 bits to make the "multiply" easy in hardware + static const uint8_t s_am_depth[8] = { 0, 0x14, 0x20, 0x28, 0x30, 0x40, 0x50, 0x80 }; + cache.am_depth = s_am_depth[ch_am_depth(choffs)]; + + // PM LFO depth values; these are converted from the manual's cents values + // into f-numbers; the computations come out quite cleanly so pretty sure + // these are correct + static const uint8_t s_pm_depth[8] = { 0, 2, 3, 4, 6, 12, 24, 48 }; + cache.pm_depth = s_pm_depth[ch_vibrato(choffs)]; + + // 4-bit sustain level, but 15 means 31 so effectively 5 bits + cache.eg_sustain = ch_sustain_level(choffs); + cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10; + cache.eg_sustain <<= 5; + + // compute the key scaling correction factor; 15 means don't do any correction + int32_t correction = ch_rate_correction(choffs); + if (correction == 15) + correction = 0; + else + correction = (octave + correction) * 2 + bitfield(fnum, 9); + + // compute the envelope generator rates + cache.eg_rate[EG_ATTACK] = effective_rate(ch_attack_rate(choffs), correction); + cache.eg_rate[EG_DECAY] = effective_rate(ch_decay_rate(choffs), correction); + cache.eg_rate[EG_SUSTAIN] = effective_rate(ch_sustain_rate(choffs), correction); + cache.eg_rate[EG_RELEASE] = effective_rate(ch_release_rate(choffs), correction); + cache.eg_rate[EG_REVERB] = 5; + + // if damping is on, override some things; essentially decay at a hardcoded + // rate of 48 until -12db (0x80), then at maximum rate for the rest + if (ch_damp(choffs) != 0) + { + cache.eg_rate[EG_DECAY] = 48; + cache.eg_rate[EG_SUSTAIN] = 63; + cache.eg_rate[EG_RELEASE] = 63; + cache.eg_sustain = 0x80; + } +} + + +//------------------------------------------------- +// effective_rate - return the effective rate, +// clamping and applying corrections as needed +//------------------------------------------------- + +uint32_t pcm_registers::effective_rate(uint32_t raw, uint32_t correction) +{ + // raw rates of 0 and 15 just pin to min/max + if (raw == 0) + return 0; + if (raw == 15) + return 63; + + // otherwise add the correction and clamp to range + return clamp(raw * 4 + correction, 0, 63); +} + + + +//********************************************************* +// PCM CHANNEL +//********************************************************* + +//------------------------------------------------- +// pcm_channel - constructor +//------------------------------------------------- + +pcm_channel::pcm_channel(pcm_engine &owner, uint32_t choffs) : + m_choffs(choffs), + m_baseaddr(0), + m_endpos(0), + m_looppos(0), + m_curpos(0), + m_nextpos(0), + m_lfo_counter(0), + m_eg_state(EG_RELEASE), + m_env_attenuation(0x3ff), + m_total_level(0x7f << 10), + m_format(0), + m_key_state(0), + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +void pcm_channel::reset() +{ + m_baseaddr = 0; + m_endpos = 0; + m_looppos = 0; + m_curpos = 0; + m_nextpos = 0; + m_lfo_counter = 0; + m_eg_state = EG_RELEASE; + m_env_attenuation = 0x3ff; + m_total_level = 0x7f << 10; + m_format = 0; + m_key_state = 0; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_channel::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_baseaddr); + state.save_restore(m_endpos); + state.save_restore(m_looppos); + state.save_restore(m_curpos); + state.save_restore(m_nextpos); + state.save_restore(m_lfo_counter); + state.save_restore(m_eg_state); + state.save_restore(m_env_attenuation); + state.save_restore(m_total_level); + state.save_restore(m_format); + state.save_restore(m_key_state); +} + + +//------------------------------------------------- +// prepare - prepare for clocking +//------------------------------------------------- + +bool pcm_channel::prepare() +{ + // cache the data + m_regs.cache_channel_data(m_choffs, m_cache); + + // clock the key state + if ((m_key_state & KEY_PENDING) != 0) + { + uint8_t oldstate = m_key_state; + m_key_state = (m_key_state >> 1) & KEY_ON; + if (((oldstate ^ m_key_state) & KEY_ON) != 0) + { + if ((m_key_state & KEY_ON) != 0) + start_attack(); + else + start_release(); + } + } + + // set the total level directly if not interpolating + if (m_regs.ch_level_direct(m_choffs)) + m_total_level = m_cache.total_level; + + // we're active until we're quiet after the release + return (m_eg_state < EG_RELEASE || m_env_attenuation < EG_QUIET); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void pcm_channel::clock(uint32_t env_counter) +{ + // clock the LFO, which is an x.18 value incremented based on the + // LFO speed value + m_lfo_counter += m_cache.lfo_step; + + // clock the envelope + clock_envelope(env_counter); + + // determine the step after applying vibrato + uint32_t step = m_cache.step; + if (m_cache.pm_depth != 0) + { + // shift the LFO by 1/4 cycle for PM so that it starts at 0 + uint32_t lfo_shifted = m_lfo_counter + (1 << 16); + int32_t lfo_value = bitfield(lfo_shifted, 10, 7); + if (bitfield(lfo_shifted, 17) != 0) + lfo_value ^= 0x7f; + lfo_value -= 0x40; + step += (lfo_value * int32_t(m_cache.pm_depth)) >> 7; + } + + // advance the sample step and loop as needed + m_curpos = m_nextpos; + m_nextpos = m_curpos + step; + if (m_nextpos >= m_endpos) + m_nextpos += m_looppos - m_endpos; + + // interpolate total level if needed + if (m_total_level != m_cache.total_level) + { + // max->min volume takes 156.4ms, or pretty close to 19/1024 per 44.1kHz sample + // min->max volume is half that, so advance by 38/1024 per sample + if (m_total_level < m_cache.total_level) + m_total_level = std::min(m_total_level + 19, m_cache.total_level); + else + m_total_level = std::max(m_total_level - 38, m_cache.total_level); + } +} + + +//------------------------------------------------- +// output - return the computed output value, with +// panning applied +//------------------------------------------------- + +void pcm_channel::output(output_data &output) const +{ + // early out if the envelope is effectively off + uint32_t envelope = m_env_attenuation; + if (envelope > EG_QUIET) + return; + + // add in LFO AM modulation + if (m_cache.am_depth != 0) + { + uint32_t lfo_value = bitfield(m_lfo_counter, 10, 7); + if (bitfield(m_lfo_counter, 17) != 0) + lfo_value ^= 0x7f; + envelope += (lfo_value * m_cache.am_depth) >> 7; + } + + // add in the current interpolated total level value, which is a .10 + // value shifted left by 2 + envelope += m_total_level >> 8; + + // add in panning effect and clamp + uint32_t lenv = std::min(envelope + m_cache.pan_left, 0x3ff); + uint32_t renv = std::min(envelope + m_cache.pan_right, 0x3ff); + + // convert to volume as a .11 fraction + int32_t lvol = attenuation_to_volume(lenv << 2); + int32_t rvol = attenuation_to_volume(renv << 2); + + // fetch current sample and add + int16_t sample = fetch_sample(); + uint32_t outnum = m_regs.ch_output_channel(m_choffs) * 2; + output.data[outnum + 0] += (lvol * sample) >> 15; + output.data[outnum + 1] += (rvol * sample) >> 15; +} + + +//------------------------------------------------- +// keyonoff - signal key on/off +//------------------------------------------------- + +void pcm_channel::keyonoff(bool on) +{ + // mark the key state as pending + m_key_state |= KEY_PENDING | (on ? KEY_PENDING_ON : 0); + + // don't log masked channels + if ((m_key_state & (KEY_PENDING_ON | KEY_ON)) == KEY_PENDING_ON && ((debug::GLOBAL_PCM_CHANNEL_MASK >> m_choffs) & 1) != 0) + { + debug::log_keyon("KeyOn PCM-%02d: num=%3d oct=%2d fnum=%03X level=%02X%c ADSR=%X/%X/%X/%X SL=%X", + m_choffs, + m_regs.ch_wave_table_num(m_choffs), + int8_t(m_regs.ch_octave(m_choffs) << 4) >> 4, + m_regs.ch_fnumber(m_choffs), + m_regs.ch_total_level(m_choffs), + m_regs.ch_level_direct(m_choffs) ? '!' : '/', + m_regs.ch_attack_rate(m_choffs), + m_regs.ch_decay_rate(m_choffs), + m_regs.ch_sustain_rate(m_choffs), + m_regs.ch_release_rate(m_choffs), + m_regs.ch_sustain_level(m_choffs)); + + if (m_regs.ch_rate_correction(m_choffs) != 15) + debug::log_keyon(" RC=%X", m_regs.ch_rate_correction(m_choffs)); + + if (m_regs.ch_pseudo_reverb(m_choffs) != 0) + debug::log_keyon(" %s", "REV"); + if (m_regs.ch_damp(m_choffs) != 0) + debug::log_keyon(" %s", "DAMP"); + + if (m_regs.ch_vibrato(m_choffs) != 0 || m_regs.ch_am_depth(m_choffs) != 0) + { + if (m_regs.ch_vibrato(m_choffs) != 0) + debug::log_keyon(" VIB=%d", m_regs.ch_vibrato(m_choffs)); + if (m_regs.ch_am_depth(m_choffs) != 0) + debug::log_keyon(" AM=%d", m_regs.ch_am_depth(m_choffs)); + debug::log_keyon(" LFO=%d", m_regs.ch_lfo_speed(m_choffs)); + } + debug::log_keyon("%s", "\n"); + } +} + + +//------------------------------------------------- +// load_wavetable - load a wavetable by fetching +// its data from external memory +//------------------------------------------------- + +void pcm_channel::load_wavetable() +{ + // determine the address of the wave table header + uint32_t wavnum = m_regs.ch_wave_table_num(m_choffs); + uint32_t wavheader = 12 * wavnum; + + // above 384 it may be in a different bank + if (wavnum >= 384) + { + uint32_t bank = m_regs.wave_table_header(); + if (bank != 0) + wavheader = 512*1024 * bank + (wavnum - 384) * 12; + } + + // fetch the 22-bit base address and 2-bit format + uint8_t byte = read_pcm(wavheader + 0); + m_format = bitfield(byte, 6, 2); + m_baseaddr = bitfield(byte, 0, 6) << 16; + m_baseaddr |= read_pcm(wavheader + 1) << 8; + m_baseaddr |= read_pcm(wavheader + 2) << 0; + + // fetch the 16-bit loop position + m_looppos = read_pcm(wavheader + 3) << 8; + m_looppos |= read_pcm(wavheader + 4); + m_looppos <<= 16; + + // fetch the 16-bit end position, which is stored as a negative value + // for some reason that is unclear + m_endpos = read_pcm(wavheader + 5) << 8; + m_endpos |= read_pcm(wavheader + 6); + m_endpos = -int32_t(m_endpos) << 16; + + // remaining data values set registers + m_owner.write(0x80 + m_choffs, read_pcm(wavheader + 7)); + m_owner.write(0x98 + m_choffs, read_pcm(wavheader + 8)); + m_owner.write(0xb0 + m_choffs, read_pcm(wavheader + 9)); + m_owner.write(0xc8 + m_choffs, read_pcm(wavheader + 10)); + m_owner.write(0xe0 + m_choffs, read_pcm(wavheader + 11)); + + // reset the envelope so we don't continue playing mid-sample from previous key ons + m_env_attenuation = 0x3ff; +} + + +//------------------------------------------------- +// read_pcm - read a byte from the external PCM +// memory interface +//------------------------------------------------- + +uint8_t pcm_channel::read_pcm(uint32_t address) const +{ + return m_owner.intf().ymfm_external_read(ACCESS_PCM, address); +} + + +//------------------------------------------------- +// start_attack - start the attack phase +//------------------------------------------------- + +void pcm_channel::start_attack() +{ + // don't change anything if already in attack state + if (m_eg_state == EG_ATTACK) + return; + m_eg_state = EG_ATTACK; + + // reset the LFO if requested + if (m_regs.ch_lfo_reset(m_choffs)) + m_lfo_counter = 0; + + // if the attack rate == 63 then immediately go to max attenuation + if (m_cache.eg_rate[EG_ATTACK] == 63) + m_env_attenuation = 0; + + // reset the positions + m_curpos = m_nextpos = 0; +} + + +//------------------------------------------------- +// start_release - start the release phase +//------------------------------------------------- + +void pcm_channel::start_release() +{ + // don't change anything if already in release or reverb state + if (m_eg_state >= EG_RELEASE) + return; + m_eg_state = EG_RELEASE; +} + + +//------------------------------------------------- +// clock_envelope - clock the envelope generator +//------------------------------------------------- + +void pcm_channel::clock_envelope(uint32_t env_counter) +{ + // handle attack->decay transitions + if (m_eg_state == EG_ATTACK && m_env_attenuation == 0) + m_eg_state = EG_DECAY; + + // handle decay->sustain transitions + if (m_eg_state == EG_DECAY && m_env_attenuation >= m_cache.eg_sustain) + m_eg_state = EG_SUSTAIN; + + // fetch the appropriate 6-bit rate value from the cache + uint32_t rate = m_cache.eg_rate[m_eg_state]; + + // compute the rate shift value; this is the shift needed to + // apply to the env_counter such that it becomes a 5.11 fixed + // point number + uint32_t rate_shift = rate >> 2; + env_counter <<= rate_shift; + + // see if the fractional part is 0; if not, it's not time to clock + if (bitfield(env_counter, 0, 11) != 0) + return; + + // determine the increment based on the non-fractional part of env_counter + uint32_t relevant_bits = bitfield(env_counter, (rate_shift <= 11) ? 11 : rate_shift, 3); + uint32_t increment = attenuation_increment(rate, relevant_bits); + + // attack is the only one that increases + if (m_eg_state == EG_ATTACK) + m_env_attenuation += (~m_env_attenuation * increment) >> 4; + + // all other cases are similar + else + { + // apply the increment + m_env_attenuation += increment; + + // clamp the final attenuation + if (m_env_attenuation >= 0x400) + m_env_attenuation = 0x3ff; + + // transition to reverb at -18dB if enabled + if (m_env_attenuation >= 0xc0 && m_eg_state < EG_REVERB && m_regs.ch_pseudo_reverb(m_choffs)) + m_eg_state = EG_REVERB; + } +} + + +//------------------------------------------------- +// fetch_sample - fetch a sample at the current +// position +//------------------------------------------------- + +int16_t pcm_channel::fetch_sample() const +{ + uint32_t addr = m_baseaddr; + uint32_t pos = m_curpos >> 16; + + // 8-bit PCM: shift up by 8 + if (m_format == 0) + return read_pcm(addr + pos) << 8; + + // 16-bit PCM: assemble from 2 halves + if (m_format == 2) + { + addr += pos * 2; + return (read_pcm(addr) << 8) | read_pcm(addr + 1); + } + + // 12-bit PCM: assemble out of half of 3 bytes + addr += (pos / 2) * 3; + if ((pos & 1) == 0) + return (read_pcm(addr + 0) << 8) | ((read_pcm(addr + 1) << 4) & 0xf0); + else + return (read_pcm(addr + 2) << 8) | ((read_pcm(addr + 1) << 0) & 0xf0); +} + + + +//********************************************************* +// PCM ENGINE +//********************************************************* + +//------------------------------------------------- +// pcm_engine - constructor +//------------------------------------------------- + +pcm_engine::pcm_engine(ymfm_interface &intf) : + m_intf(intf), + m_env_counter(0), + m_modified_channels(ALL_CHANNELS), + m_active_channels(ALL_CHANNELS) +{ + // create the channels + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum] = std::make_unique(*this, chnum); +} + + +//------------------------------------------------- +// reset - reset the engine state +//------------------------------------------------- + +void pcm_engine::reset() +{ + // reset register state + m_regs.reset(); + + // reset each channel + for (auto &chan : m_channel) + chan->reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_engine::save_restore(ymfm_saved_state &state) +{ + // save our data + state.save_restore(m_env_counter); + + // save channel state + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum]->save_restore(state); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void pcm_engine::clock(uint32_t chanmask) +{ + // if something was modified, prepare + // also prepare every 4k samples to catch ending notes + if (m_modified_channels != 0 || m_prepare_count++ >= 4096) + { + // call each channel to prepare + m_active_channels = 0; + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + if (m_channel[chnum]->prepare()) + m_active_channels |= 1 << chnum; + + // reset the modified channels and prepare count + m_modified_channels = m_prepare_count = 0; + } + + // increment the envelope counter; the envelope generator + // only clocks every other sample in order to make the PCM + // envelopes line up with the FM envelopes (after taking into + // account the different FM sampling rate) + m_env_counter++; + + // now update the state of all the channels and operators + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->clock(m_env_counter >> 1); +} + + +//------------------------------------------------- +// update - master update function +//------------------------------------------------- + +void pcm_engine::output(output_data &output, uint32_t chanmask) +{ + // mask out some channels for debug purposes + chanmask &= debug::GLOBAL_PCM_CHANNEL_MASK; + + // compute the output of each channel + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->output(output); +} + + +//------------------------------------------------- +// read - handle reads from the PCM registers +//------------------------------------------------- + +uint8_t pcm_engine::read(uint32_t regnum) +{ + // handle reads from the data register + if (regnum == 0x06 && m_regs.memory_access_mode() != 0) + return m_intf.ymfm_external_read(ACCESS_PCM, m_regs.memory_address_autoinc()); + + return m_regs.read(regnum); +} + + +//------------------------------------------------- +// write - handle writes to the PCM registers +//------------------------------------------------- + +void pcm_engine::write(uint32_t regnum, uint8_t data) +{ + // handle reads to the data register + if (regnum == 0x06 && m_regs.memory_access_mode() != 0) + { + m_intf.ymfm_external_write(ACCESS_PCM, m_regs.memory_address_autoinc(), data); + return; + } + + // for now just mark all channels as modified + m_modified_channels = ALL_CHANNELS; + + // most writes are passive, consumed only when needed + m_regs.write(regnum, data); + + // however, process keyons immediately + if (regnum >= 0x68 && regnum <= 0x7f) + m_channel[regnum - 0x68]->keyonoff(bitfield(data, 7)); + + // and also wavetable writes + else if (regnum >= 0x08 && regnum <= 0x1f) + m_channel[regnum - 0x08]->load_wavetable(); +} + +} diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_pcm.h b/thirdparty/opnmidi/chips/ymfm/ymfm_pcm.h new file mode 100644 index 000000000..b471fa611 --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_pcm.h @@ -0,0 +1,347 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_PCM_H +#define YMFM_PCM_H + +#pragma once + +#include "ymfm.h" + +namespace ymfm +{ + +/* +Note to self: Sega "Multi-PCM" is almost identical to this + +28 channels + +Writes: +00 = data reg, causes write +01 = target slot = data - (data / 8) +02 = address (clamped to 7) + +Slot data (registers with ADSR/KSR seem to be inaccessible): +0: xxxx---- panpot +1: xxxxxxxx wavetable low +2: xxxxxx-- pitch low + -------x wavetable high +3: xxxx---- octave + ----xxxx pitch hi +4: x------- key on +5: xxxxxxx- total level + -------x level direct (0=interpolate) +6: --xxx--- LFO frequency + -----xxx PM sensitivity +7: -----xxx AM sensitivity + +Sample data: ++00: start hi ++01: start mid ++02: start low ++03: loop hi ++04: loop low ++05: -end hi ++06: -end low ++07: vibrato (reg 6) ++08: attack/decay ++09: sustain level/rate ++0A: ksr/release ++0B: LFO amplitude (reg 7) + +*/ + +//********************************************************* +// INTERFACE CLASSES +//********************************************************* + +class pcm_engine; + + +// ======================> pcm_cache + +// this class holds data that is computed once at the start of clocking +// and remains static during subsequent sound generation +struct pcm_cache +{ + uint32_t step; // sample position step, as a .16 value + uint32_t total_level; // target total level, as a .10 value + uint32_t pan_left; // left panning attenuation + uint32_t pan_right; // right panning attenuation + uint32_t eg_sustain; // sustain level, shifted up to envelope values + uint8_t eg_rate[EG_STATES]; // envelope rate, including KSR + uint8_t lfo_step; // stepping value for LFO + uint8_t am_depth; // scale value for AM LFO + uint8_t pm_depth; // scale value for PM LFO +}; + + +// ======================> pcm_registers + +// +// PCM register map: +// +// System-wide registers: +// 00-01 xxxxxxxx LSI Test +// 02 -------x Memory access mode (0=sound gen, 1=read/write) +// ------x- Memory type (0=ROM, 1=ROM+SRAM) +// ---xxx-- Wave table header +// xxx----- Device ID (=1 for YMF278B) +// 03 --xxxxxx Memory address high +// 04 xxxxxxxx Memory address mid +// 05 xxxxxxxx Memory address low +// 06 xxxxxxxx Memory data +// F8 --xxx--- Mix control (FM_R) +// -----xxx Mix control (FM_L) +// F9 --xxx--- Mix control (PCM_R) +// -----xxx Mix control (PCM_L) +// +// Channel-specific registers: +// 08-1F xxxxxxxx Wave table number low +// 20-37 -------x Wave table number high +// xxxxxxx- F-number low +// 38-4F -----xxx F-number high +// ----x--- Pseudo-reverb +// xxxx---- Octave +// 50-67 xxxxxxx- Total level +// -------x Level direct +// 68-7F x------- Key on +// -x------ Damp +// --x----- LFO reset +// ---x---- Output channel +// ----xxxx Panpot +// 80-97 --xxx--- LFO speed +// -----xxx Vibrato +// 98-AF xxxx---- Attack rate +// ----xxxx Decay rate +// B0-C7 xxxx---- Sustain level +// ----xxxx Sustain rate +// C8-DF xxxx---- Rate correction +// ----xxxx Release rate +// E0-F7 -----xxx AM depth + +class pcm_registers +{ +public: + // constants + static constexpr uint32_t OUTPUTS = 4; + static constexpr uint32_t CHANNELS = 24; + static constexpr uint32_t REGISTERS = 0x100; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + + // constructor + pcm_registers() { } + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset to initial state + void reset(); + + // update cache information + void cache_channel_data(uint32_t choffs, pcm_cache &cache); + + // direct read/write access + uint8_t read(uint32_t index ) { return m_regdata[index]; } + void write(uint32_t index, uint8_t data) { m_regdata[index] = data; } + + // system-wide registers + uint32_t memory_access_mode() const { return bitfield(m_regdata[0x02], 0); } + uint32_t memory_type() const { return bitfield(m_regdata[0x02], 1); } + uint32_t wave_table_header() const { return bitfield(m_regdata[0x02], 2, 3); } + uint32_t device_id() const { return bitfield(m_regdata[0x02], 5, 3); } + uint32_t memory_address() const { return (bitfield(m_regdata[0x03], 0, 6) << 16) | (m_regdata[0x04] << 8) | m_regdata[0x05]; } + uint32_t memory_data() const { return m_regdata[0x06]; } + uint32_t mix_fm_r() const { return bitfield(m_regdata[0xf8], 3, 3); } + uint32_t mix_fm_l() const { return bitfield(m_regdata[0xf8], 0, 3); } + uint32_t mix_pcm_r() const { return bitfield(m_regdata[0xf9], 3, 3); } + uint32_t mix_pcm_l() const { return bitfield(m_regdata[0xf9], 0, 3); } + + // per-channel registers + uint32_t ch_wave_table_num(uint32_t choffs) const { return m_regdata[choffs + 0x08] | (bitfield(m_regdata[choffs + 0x20], 0) << 8); } + uint32_t ch_fnumber(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x20], 1, 7) | (bitfield(m_regdata[choffs + 0x38], 0, 3) << 7); } + uint32_t ch_pseudo_reverb(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 3); } + uint32_t ch_octave(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 4, 4); } + uint32_t ch_total_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 1, 7); } + uint32_t ch_level_direct(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 0); } + uint32_t ch_keyon(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 7); } + uint32_t ch_damp(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 6); } + uint32_t ch_lfo_reset(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 5); } + uint32_t ch_output_channel(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 4); } + uint32_t ch_panpot(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 0, 4); } + uint32_t ch_lfo_speed(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 3, 3); } + uint32_t ch_vibrato(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 0, 3); } + uint32_t ch_attack_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 4, 4); } + uint32_t ch_decay_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 0, 4); } + uint32_t ch_sustain_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 4, 4); } + uint32_t ch_sustain_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 0, 4); } + uint32_t ch_rate_correction(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 4, 4); } + uint32_t ch_release_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 0, 4); } + uint32_t ch_am_depth(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xe0], 0, 3); } + + // return the memory address and increment it + uint32_t memory_address_autoinc() + { + uint32_t result = memory_address(); + uint32_t newval = result + 1; + m_regdata[0x05] = newval >> 0; + m_regdata[0x04] = newval >> 8; + m_regdata[0x03] = (newval >> 16) & 0x3f; + return result; + } + +private: + // internal helpers + uint32_t effective_rate(uint32_t raw, uint32_t correction); + + // internal state + uint8_t m_regdata[REGISTERS]; // register data +}; + + +// ======================> pcm_channel + +class pcm_channel +{ + static constexpr uint8_t KEY_ON = 0x01; + static constexpr uint8_t KEY_PENDING_ON = 0x02; + static constexpr uint8_t KEY_PENDING = 0x04; + + // "quiet" value, used to optimize when we can skip doing working + static constexpr uint32_t EG_QUIET = 0x200; + +public: + using output_data = ymfm_output; + + // constructor + pcm_channel(pcm_engine &owner, uint32_t choffs); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset the channel state + void reset(); + + // return the channel offset + uint32_t choffs() const { return m_choffs; } + + // prepare prior to clocking + bool prepare(); + + // master clocking function + void clock(uint32_t env_counter); + + // return the computed output value, with panning applied + void output(output_data &output) const; + + // signal key on/off + void keyonoff(bool on); + + // load a new wavetable entry + void load_wavetable(); + +private: + // internal helpers + void start_attack(); + void start_release(); + void clock_envelope(uint32_t env_counter); + int16_t fetch_sample() const; + uint8_t read_pcm(uint32_t address) const; + + // internal state + uint32_t const m_choffs; // channel offset + uint32_t m_baseaddr; // base address + uint32_t m_endpos; // ending position + uint32_t m_looppos; // loop position + uint32_t m_curpos; // current position + uint32_t m_nextpos; // next position + uint32_t m_lfo_counter; // LFO counter + envelope_state m_eg_state; // envelope state + uint16_t m_env_attenuation; // computed envelope attenuation + uint32_t m_total_level; // total level with as 7.10 for interp + uint8_t m_format; // sample format + uint8_t m_key_state; // current key state + pcm_cache m_cache; // cached data + pcm_registers &m_regs; // reference to registers + pcm_engine &m_owner; // reference to our owner +}; + + +// ======================> pcm_engine + +class pcm_engine +{ +public: + static constexpr int OUTPUTS = pcm_registers::OUTPUTS; + static constexpr int CHANNELS = pcm_registers::CHANNELS; + static constexpr uint32_t ALL_CHANNELS = pcm_registers::ALL_CHANNELS; + using output_data = pcm_channel::output_data; + + // constructor + pcm_engine(ymfm_interface &intf); + + // reset our status + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // master clocking function + void clock(uint32_t chanmask); + + // compute sum of channel outputs + void output(output_data &output, uint32_t chanmask); + + // read from the PCM registers + uint8_t read(uint32_t regnum); + + // write to the PCM registers + void write(uint32_t regnum, uint8_t data); + + // return a reference to our interface + ymfm_interface &intf() { return m_intf; } + + // return a reference to our registers + pcm_registers ®s() { return m_regs; } + +private: + // internal state + ymfm_interface &m_intf; // reference to the interface + uint32_t m_env_counter; // envelope counter + uint32_t m_modified_channels; // bitmask of modified channels + uint32_t m_active_channels; // bitmask of active channels + uint32_t m_prepare_count; // counter to do periodic prepare sweeps + std::unique_ptr m_channel[CHANNELS]; // array of channels + pcm_registers m_regs; // registers +}; + +} + +#endif // YMFM_PCM_H diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_ssg.cpp b/thirdparty/opnmidi/chips/ymfm/ymfm_ssg.cpp new file mode 100644 index 000000000..1c477d0de --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_ssg.cpp @@ -0,0 +1,279 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +#include "ymfm_ssg.h" + +namespace ymfm +{ + +//********************************************************* +// SSG REGISTERS +//********************************************************* + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void ssg_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ssg_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_regdata); +} + + + +//********************************************************* +// SSG ENGINE +//********************************************************* + +//------------------------------------------------- +// ssg_engine - constructor +//------------------------------------------------- + +ssg_engine::ssg_engine(ymfm_interface &intf) : + m_intf(intf), + m_tone_count{ 0,0,0 }, + m_tone_state{ 0,0,0 }, + m_envelope_count(0), + m_envelope_state(0), + m_noise_count(0), + m_noise_state(1), + m_override(nullptr) +{ +} + + +//------------------------------------------------- +// reset - reset the engine state +//------------------------------------------------- + +void ssg_engine::reset() +{ + // defer to the override if present + if (m_override != nullptr) + return m_override->ssg_reset(); + + // reset register state + m_regs.reset(); + + // reset engine state + for (int chan = 0; chan < 3; chan++) + { + m_tone_count[chan] = 0; + m_tone_state[chan] = 0; + } + m_envelope_count = 0; + m_envelope_state = 0; + m_noise_count = 0; + m_noise_state = 1; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ssg_engine::save_restore(ymfm_saved_state &state) +{ + // save register state + m_regs.save_restore(state); + + // save engine state + state.save_restore(m_tone_count); + state.save_restore(m_tone_state); + state.save_restore(m_envelope_count); + state.save_restore(m_envelope_state); + state.save_restore(m_noise_count); + state.save_restore(m_noise_state); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void ssg_engine::clock() +{ + // clock tones; tone period units are clock/16 but since we run at clock/8 + // that works out for us to toggle the state (50% duty cycle) at twice the + // programmed period + for (int chan = 0; chan < 3; chan++) + { + m_tone_count[chan]++; + if (m_tone_count[chan] >= m_regs.ch_tone_period(chan)) + { + m_tone_state[chan] ^= 1; + m_tone_count[chan] = 0; + } + } + + // clock noise; noise period units are clock/16 but since we run at clock/8, + // our counter needs a right shift prior to compare; note that a period of 0 + // should produce an indentical result to a period of 1, so add a special + // check against that case + m_noise_count++; + if ((m_noise_count >> 1) >= m_regs.noise_period() && m_noise_count != 1) + { + m_noise_state ^= (bitfield(m_noise_state, 0) ^ bitfield(m_noise_state, 3)) << 17; + m_noise_state >>= 1; + m_noise_count = 0; + } + + // clock envelope; envelope period units are clock/8 (manual says clock/256 + // but that's for all 32 steps) + m_envelope_count++; + if (m_envelope_count >= m_regs.envelope_period()) + { + m_envelope_state++; + m_envelope_count = 0; + } +} + + +//------------------------------------------------- +// output - output the current state +//------------------------------------------------- + +void ssg_engine::output(output_data &output) +{ + // volume to amplitude table, taken from MAME's implementation but biased + // so that 0 == 0 + static int16_t const s_amplitudes[32] = + { + 0, 32, 78, 141, 178, 222, 262, 306, + 369, 441, 509, 585, 701, 836, 965, 1112, + 1334, 1595, 1853, 2146, 2576, 3081, 3576, 4135, + 5000, 6006, 7023, 8155, 9963,11976,14132,16382 + }; + + // compute the envelope volume + uint32_t envelope_volume; + if ((m_regs.envelope_hold() | (m_regs.envelope_continue() ^ 1)) && m_envelope_state >= 32) + { + m_envelope_state = 32; + envelope_volume = ((m_regs.envelope_attack() ^ m_regs.envelope_alternate()) & m_regs.envelope_continue()) ? 31 : 0; + } + else + { + uint32_t attack = m_regs.envelope_attack(); + if (m_regs.envelope_alternate()) + attack ^= bitfield(m_envelope_state, 5); + envelope_volume = (m_envelope_state & 31) ^ (attack ? 0 : 31); + } + + // iterate over channels + for (int chan = 0; chan < 3; chan++) + { + // noise depends on the noise state, which is the LSB of m_noise_state + uint32_t noise_on = m_regs.ch_noise_enable_n(chan) | m_noise_state; + + // tone depends on the current tone state + uint32_t tone_on = m_regs.ch_tone_enable_n(chan) | m_tone_state[chan]; + + // if neither tone nor noise enabled, return 0 + uint32_t volume; + if ((noise_on & tone_on) == 0) + volume = 0; + + // if the envelope is enabled, use its amplitude + else if (m_regs.ch_envelope_enable(chan)) + volume = envelope_volume; + + // otherwise, scale the tone amplitude up to match envelope values + // according to the datasheet, amplitude 15 maps to envelope 31 + else + { + volume = m_regs.ch_amplitude(chan) * 2; + if (volume != 0) + volume |= 1; + } + + // convert to amplitude + output.data[chan] = s_amplitudes[volume]; + } +} + + +//------------------------------------------------- +// read - handle reads from the SSG registers +//------------------------------------------------- + +uint8_t ssg_engine::read(uint32_t regnum) +{ + // defer to the override if present + if (m_override != nullptr) + return m_override->ssg_read(regnum); + + // read from the I/O ports call the handlers if they are configured for input + if (regnum == 0x0e && !m_regs.io_a_out()) + return m_intf.ymfm_external_read(ACCESS_IO, 0); + else if (regnum == 0x0f && !m_regs.io_b_out()) + return m_intf.ymfm_external_read(ACCESS_IO, 1); + + // otherwise just return the register value + return m_regs.read(regnum); +} + + +//------------------------------------------------- +// write - handle writes to the SSG registers +//------------------------------------------------- + +void ssg_engine::write(uint32_t regnum, uint8_t data) +{ + // defer to the override if present + if (m_override != nullptr) + return m_override->ssg_write(regnum, data); + + // store the raw value to the register array; + // most writes are passive, consumed only when needed + m_regs.write(regnum, data); + + // writes to the envelope shape register reset the state + if (regnum == 0x0d) + m_envelope_state = 0; + + // writes to the I/O ports call the handlers if they are configured for output + else if (regnum == 0x0e && m_regs.io_a_out()) + m_intf.ymfm_external_write(ACCESS_IO, 0, data); + else if (regnum == 0x0f && m_regs.io_b_out()) + m_intf.ymfm_external_write(ACCESS_IO, 1, data); +} + +} diff --git a/thirdparty/opnmidi/chips/ymfm/ymfm_ssg.h b/thirdparty/opnmidi/chips/ymfm/ymfm_ssg.h new file mode 100644 index 000000000..749ad146f --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm/ymfm_ssg.h @@ -0,0 +1,205 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 YMFM_SSG_H +#define YMFM_SSG_H + +#pragma once + +#include "ymfm.h" + +namespace ymfm +{ + +//********************************************************* +// OVERRIDE INTERFACE +//********************************************************* + +// ======================> ssg_override + +// this class describes a simple interface to allow the internal SSG to be +// overridden with another implementation +class ssg_override +{ +public: + // reset our status + virtual void ssg_reset() = 0; + + // read/write to the SSG registers + virtual uint8_t ssg_read(uint32_t regnum) = 0; + virtual void ssg_write(uint32_t regnum, uint8_t data) = 0; + + // notification when the prescale has changed + virtual void ssg_prescale_changed() = 0; +}; + + +//********************************************************* +// REGISTER CLASS +//********************************************************* + +// ======================> ssg_registers + +// +// SSG register map: +// +// System-wide registers: +// 06 ---xxxxx Noise period +// 07 x------- I/O B in(0) or out(1) +// -x------ I/O A in(0) or out(1) +// --x----- Noise enable(0) or disable(1) for channel C +// ---x---- Noise enable(0) or disable(1) for channel B +// ----x--- Noise enable(0) or disable(1) for channel A +// -----x-- Tone enable(0) or disable(1) for channel C +// ------x- Tone enable(0) or disable(1) for channel B +// -------x Tone enable(0) or disable(1) for channel A +// 0B xxxxxxxx Envelope period fine +// 0C xxxxxxxx Envelope period coarse +// 0D ----x--- Envelope shape: continue +// -----x-- Envelope shape: attack/decay +// ------x- Envelope shape: alternate +// -------x Envelope shape: hold +// 0E xxxxxxxx 8-bit parallel I/O port A +// 0F xxxxxxxx 8-bit parallel I/O port B +// +// Per-channel registers: +// 00,02,04 xxxxxxxx Tone period (fine) for channel A,B,C +// 01,03,05 ----xxxx Tone period (coarse) for channel A,B,C +// 08,09,0A ---x---- Mode: fixed(0) or variable(1) for channel A,B,C +// ----xxxx Amplitude for channel A,B,C +// +class ssg_registers +{ +public: + // constants + static constexpr uint32_t OUTPUTS = 3; + static constexpr uint32_t CHANNELS = 3; + static constexpr uint32_t REGISTERS = 0x10; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + + // constructor + ssg_registers() { } + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // direct read/write access + uint8_t read(uint32_t index) { return m_regdata[index]; } + void write(uint32_t index, uint8_t data) { m_regdata[index] = data; } + + // system-wide registers + uint32_t noise_period() const { return bitfield(m_regdata[0x06], 0, 5); } + uint32_t io_b_out() const { return bitfield(m_regdata[0x07], 7); } + uint32_t io_a_out() const { return bitfield(m_regdata[0x07], 6); } + uint32_t envelope_period() const { return m_regdata[0x0b] | (m_regdata[0x0c] << 8); } + uint32_t envelope_continue() const { return bitfield(m_regdata[0x0d], 3); } + uint32_t envelope_attack() const { return bitfield(m_regdata[0x0d], 2); } + uint32_t envelope_alternate() const { return bitfield(m_regdata[0x0d], 1); } + uint32_t envelope_hold() const { return bitfield(m_regdata[0x0d], 0); } + uint32_t io_a_data() const { return m_regdata[0x0e]; } + uint32_t io_b_data() const { return m_regdata[0x0f]; } + + // per-channel registers + uint32_t ch_noise_enable_n(uint32_t choffs) const { return bitfield(m_regdata[0x07], 3 + choffs); } + uint32_t ch_tone_enable_n(uint32_t choffs) const { return bitfield(m_regdata[0x07], 0 + choffs); } + uint32_t ch_tone_period(uint32_t choffs) const { return m_regdata[0x00 + 2 * choffs] | (bitfield(m_regdata[0x01 + 2 * choffs], 0, 4) << 8); } + uint32_t ch_envelope_enable(uint32_t choffs) const { return bitfield(m_regdata[0x08 + choffs], 4); } + uint32_t ch_amplitude(uint32_t choffs) const { return bitfield(m_regdata[0x08 + choffs], 0, 4); } + +private: + // internal state + uint8_t m_regdata[REGISTERS]; // register data +}; + + +// ======================> ssg_engine + +class ssg_engine +{ +public: + static constexpr int OUTPUTS = ssg_registers::OUTPUTS; + static constexpr int CHANNELS = ssg_registers::CHANNELS; + static constexpr int CLOCK_DIVIDER = 8; + + using output_data = ymfm_output; + + // constructor + ssg_engine(ymfm_interface &intf); + + // configure an override + void override(ssg_override &override) { m_override = &override; } + + // reset our status + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // master clocking function + void clock(); + + // compute sum of channel outputs + void output(output_data &output); + + // read/write to the SSG registers + uint8_t read(uint32_t regnum); + void write(uint32_t regnum, uint8_t data); + + // return a reference to our interface + ymfm_interface &intf() { return m_intf; } + + // return a reference to our registers + ssg_registers ®s() { return m_regs; } + + // true if we are overridden + bool overridden() const { return (m_override != nullptr); } + + // indicate the prescale has changed + void prescale_changed() { if (m_override != nullptr) m_override->ssg_prescale_changed(); } + +private: + // internal state + ymfm_interface &m_intf; // reference to the interface + uint32_t m_tone_count[3]; // current tone counter + uint32_t m_tone_state[3]; // current tone state + uint32_t m_envelope_count; // envelope counter + uint32_t m_envelope_state; // envelope state + uint32_t m_noise_count; // current noise counter + uint32_t m_noise_state; // current noise state + ssg_registers m_regs; // registers + ssg_override *m_override; // override interface +}; + +} + +#endif // YMFM_SSG_H diff --git a/thirdparty/opnmidi/chips/ymfm_opn2.cpp b/thirdparty/opnmidi/chips/ymfm_opn2.cpp new file mode 100644 index 000000000..59b66d289 --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm_opn2.cpp @@ -0,0 +1,115 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ymfm_opn2.h" +#include "ymfm/ymfm_opn.h" +#include + +YmFmOPN2::YmFmOPN2(OPNFamily f) : + OPNChipBaseT(f), + m_headPos(0), + m_tailPos(0), + m_queueCount(0) +{ + ymfm::ymfm_interface* intf = new ymfm::ymfm_interface; + m_intf = intf; + m_chip = new ymfm::ym2612(*intf); + YmFmOPN2::setRate(m_rate, m_clock); +} + +YmFmOPN2::~YmFmOPN2() +{ + ymfm::ym2612 *chip_r = reinterpret_cast(m_chip); + ymfm::ymfm_interface *intf_r = reinterpret_cast(m_intf); + delete chip_r; + delete intf_r; +} + +void YmFmOPN2::setRate(uint32_t rate, uint32_t clock) +{ + OPNChipBaseT::setRate(rate, clock); + ymfm::ym2612 *chip_r = reinterpret_cast(m_chip); + chip_r->reset(); +} + +void YmFmOPN2::reset() +{ + OPNChipBaseT::reset(); + ymfm::ym2612 *chip_r = reinterpret_cast(m_chip); + chip_r->reset(); +} + +void YmFmOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + Reg &back = m_queue[m_headPos++]; + back.addr = port > 0 ? addr | 0x100 : addr; + back.data = data; + + if(m_headPos >= c_queueSize) + m_headPos = 0; + + ++m_queueCount; +} + +void YmFmOPN2::writePan(uint16_t addr, uint8_t data) +{ + ymfm::ym2612 *chip_r = reinterpret_cast(m_chip); + chip_r->write_pan(addr, data); +} + +void YmFmOPN2::nativeGenerate(int16_t *frame) +{ + ymfm::ym2612 *chip_r = reinterpret_cast(m_chip); + ymfm::ym2612::output_data frames_i; + + uint32_t addr1 = 0xffff, addr2 = 0xffff; + uint8_t data1 = 0, data2 = 0; + + // see if there is data to be written; if so, extract it and dequeue + if(m_queueCount > 0) + { + const Reg &front = m_queue[m_tailPos++]; + + if(m_tailPos >= c_queueSize) + m_tailPos = 0; + --m_queueCount; + + addr1 = 0 + 2 * ((front.addr >> 8) & 3); + data1 = front.addr & 0xff; + addr2 = addr1 + 1; + data2 = front.data; + } + + // write to the chip + if(addr1 != 0xffff) + { + chip_r->write(addr1, data1); + chip_r->write(addr2, data2); + } + + chip_r->generate(&frames_i); + frame[0] = static_cast(ymfm::clamp(static_cast(frames_i.data[0] * 0.8f), -32768, 32767)); + frame[1] = static_cast(ymfm::clamp(static_cast(frames_i.data[1] * 0.8f), -32768, 32767)); +} + +const char *YmFmOPN2::emulatorName() +{ + return "YMFM OPN2"; +} diff --git a/thirdparty/opnmidi/chips/ymfm_opn2.h b/thirdparty/opnmidi/chips/ymfm_opn2.h new file mode 100644 index 000000000..45d67fa8e --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm_opn2.h @@ -0,0 +1,59 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef YMFM_OPN2_H +#define YMFM_OPN2_H + +#include "opn_chip_base.h" + +class YmFmOPN2 final : public OPNChipBaseT +{ + void *m_chip; + void *m_intf; + + static const size_t c_queueSize = 500; + + struct Reg + { + uint32_t addr; + uint8_t data; + }; + + Reg m_queue[c_queueSize]; + size_t m_headPos; + size_t m_tailPos; + long m_queueCount; + +public: + explicit YmFmOPN2(OPNFamily f); + ~YmFmOPN2() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; +}; + +#endif // YMFM_OPN2_H diff --git a/thirdparty/opnmidi/chips/ymfm_opna.cpp b/thirdparty/opnmidi/chips/ymfm_opna.cpp new file mode 100644 index 000000000..92f7a2c9b --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm_opna.cpp @@ -0,0 +1,162 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ymfm_opna.h" +#include "ymfm/ymfm_opn.h" +#include + +struct YmFmOPNA_Private +{ + YmFmOPNA::Reg m_queue[YmFmOPNA::c_queueSize]; + size_t m_headPos = 0; + size_t m_tailPos = 0; + long m_queueCount = 0; + unsigned long long m_step = 0; + unsigned long long m_pos = 0; + unsigned long long m_out_step = 0; + + void writeReg(uint32_t port, uint16_t addr, uint8_t data); + void nativeGenerate(int16_t *frame, void *m_chip, void *m_output); +}; + +YmFmOPNA::YmFmOPNA(OPNFamily f) : + OPNChipBaseT(f), + p(new YmFmOPNA_Private) +{ + ymfm::ymfm_interface* intf = new ymfm::ymfm_interface; + m_intf = intf; + m_chip = new ymfm::ym2608(*intf); + ymfm::ym2608::output_data* output = new ymfm::ym2608::output_data; + output->clear(); + m_output = output; + YmFmOPNA::setRate(m_rate, m_clock); + writeReg(0, 0x29, 0x9f); // enable channels 4-6 +} + +YmFmOPNA::~YmFmOPNA() +{ + delete p; + ymfm::ym2608 *chip_r = reinterpret_cast(m_chip); + ymfm::ymfm_interface *intf_r = reinterpret_cast(m_intf); + ymfm::ym2608::output_data *output_r = reinterpret_cast(m_output); + delete chip_r; + delete intf_r; + delete output_r; +} + +void YmFmOPNA::setRate(uint32_t rate, uint32_t clock) +{ + OPNChipBaseT::setRate(rate, clock); + ymfm::ym2608 *chip_r = reinterpret_cast(m_chip); + chip_r->reset(); + p->m_out_step = 0x100000000ull / nativeRate(); + p->m_step = 0x100000000ull / chip_r->sample_rate(m_clock); + p->m_pos = 0; + writeReg(0, 0x29, 0x9f); // enable channels 4-6 +} + +void YmFmOPNA::reset() +{ + OPNChipBaseT::reset(); + ymfm::ym2608 *chip_r = reinterpret_cast(m_chip); + chip_r->reset(); + p->m_out_step = 0x100000000ull / nativeRate(); + p->m_step = 0x100000000ull / chip_r->sample_rate(m_clock); + p->m_pos = 0; + writeReg(0, 0x29, 0x9f); // enable channels 4-6 +} + +void YmFmOPNA::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + p->writeReg(port, addr, data); +} + +void YmFmOPNA_Private::writeReg(uint32_t port, uint16_t addr, uint8_t data) +{ + YmFmOPNA::Reg &back = m_queue[m_headPos++]; + back.addr = port > 0 ? addr | 0x100 : addr; + back.data = data; + + if(m_headPos >= YmFmOPNA::c_queueSize) + m_headPos = 0; + + ++m_queueCount; +} + +void YmFmOPNA::writePan(uint16_t /*addr*/, uint8_t /*data*/) +{ + // ymfm::ym2608 *chip_r = reinterpret_cast(m_chip); + // OPL3_WritePan(chip_r, addr, data); +} + +void YmFmOPNA::nativeGenerate(int16_t *frame) +{ + p->nativeGenerate(frame, m_chip, m_output); +} + +void YmFmOPNA_Private::nativeGenerate(int16_t *frame, void *m_chip, void *m_output) +{ + ymfm::ym2608 *chip_r = reinterpret_cast(m_chip); + ymfm::ym2608::output_data *output_r = reinterpret_cast(m_output); + + uint32_t addr1 = 0xffff, addr2 = 0xffff; + uint8_t data1 = 0, data2 = 0; + + for ( ; m_pos <= m_out_step; m_pos += m_step) + { + // see if there is data to be written; if so, extract it and dequeue + if(m_queueCount > 0) + { + const YmFmOPNA::Reg &front = m_queue[m_tailPos++]; + + if(m_tailPos >= YmFmOPNA::c_queueSize) + m_tailPos = 0; + --m_queueCount; + + addr1 = 0 + 2 * ((front.addr >> 8) & 3); + data1 = front.addr & 0xff; + addr2 = addr1 + 1; + data2 = front.data; + } + + // write to the chip + if(addr1 != 0xffff) + { + chip_r->write(addr1, data1); + chip_r->write(addr2, data2); + } + + chip_r->generate(output_r); + } + + int32_t o0 = output_r->data[0]; + int32_t o1 = output_r->data[1 % ymfm::ym2608::OUTPUTS]; + int32_t o2 = output_r->data[2 % ymfm::ym2608::OUTPUTS]; + + frame[0] = static_cast(ymfm::clamp(static_cast((o0 + o2) * 0.8f), -32768, 32767)); + frame[1] = static_cast(ymfm::clamp(static_cast((o1 + o2) * 0.8f), -32768, 32767)); + + m_pos = (m_pos % m_out_step); +} + +const char *YmFmOPNA::emulatorName() +{ + return "YMFM OPNA"; +} diff --git a/thirdparty/opnmidi/chips/ymfm_opna.h b/thirdparty/opnmidi/chips/ymfm_opna.h new file mode 100644 index 000000000..8b54c22ed --- /dev/null +++ b/thirdparty/opnmidi/chips/ymfm_opna.h @@ -0,0 +1,60 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (c) 2017-2025 Vitaly Novichkov (Wohlstand) + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef YMFM_OPNA_H +#define YMFM_OPNA_H + +#include "opn_chip_base.h" + +struct YmFmOPNA_Private; + +class YmFmOPNA final : public OPNChipBaseT +{ + friend struct YmFmOPNA_Private; + void *m_chip; + void *m_intf; + void *m_output; + + static const size_t c_queueSize = 500; + + struct Reg + { + uint32_t addr; + uint8_t data; + }; + + YmFmOPNA_Private *p; + +public: + explicit YmFmOPNA(OPNFamily f); + ~YmFmOPNA() override; + + bool canRunAtPcmRate() const override { return false; } + void setRate(uint32_t rate, uint32_t clock) override; + void reset() override; + void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; + void nativePreGenerate() override {} + void nativePostGenerate() override {} + void nativeGenerate(int16_t *frame) override; + const char *emulatorName() override; +}; + +#endif // YMFM_OPNA_H diff --git a/thirdparty/opnmidi/file_reader.hpp b/thirdparty/opnmidi/file_reader.hpp new file mode 100644 index 000000000..8a3a8d43b --- /dev/null +++ b/thirdparty/opnmidi/file_reader.hpp @@ -0,0 +1,301 @@ +/* + * FileAndMemoryReader - a tiny helper to utify file reading from a disk and memory block + * + * Copyright (c) 2015-2025 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#ifndef FILE_AND_MEM_READER_HHHH +#define FILE_AND_MEM_READER_HHHH + +#include // std::string +#include // std::fopen, std::fread, std::fseek, std::ftell, std::fclose, std::feof +#include // uint*_t +#include // size_t and friends +#ifdef _WIN32 +#define NOMINMAX 1 +#include // std::strlen +#include // MultiByteToWideChar +#endif + +/** + * @brief A little class gives able to read filedata from disk and also from a memory segment + */ +class FileAndMemReader +{ + //! Currently loaded filename (empty for a memory blocks) + std::string m_file_name; + //! File reader descriptor + std::FILE *m_fp; + + //! Memory pointer descriptor + const void *m_mp; + //! Size of memory block + size_t m_mp_size; + //! Cursor position in the memory block + size_t m_mp_tell; + +public: + /** + * @brief Relation direction + */ + enum relTo + { + //! At begin position + SET = SEEK_SET, + //! At current position + CUR = SEEK_CUR, + //! At end position + END = SEEK_END + }; + + /** + * @brief C.O.: It's a constructor! + */ + FileAndMemReader() : + m_fp(NULL), + m_mp(NULL), + m_mp_size(0), + m_mp_tell(0) + {} + + /** + * @brief C.O.: It's a destructor! + */ + ~FileAndMemReader() + { + close(); + } + + /** + * @brief Open file from a disk + * @param path Path to the file in UTF-8 (even on Windows!) + */ + void openFile(const char *path) + { + if(m_fp) + this->close();//Close previously opened file first! +#if !defined(_WIN32) || defined(__WATCOMC__) + m_fp = std::fopen(path, "rb"); +#else + wchar_t widePath[MAX_PATH]; + int size = MultiByteToWideChar(CP_UTF8, 0, path, static_cast(std::strlen(path)), widePath, MAX_PATH); + widePath[size] = '\0'; + m_fp = _wfopen(widePath, L"rb"); +#endif + m_file_name = path; + m_mp = NULL; + m_mp_size = 0; + m_mp_tell = 0; + } + + /** + * @brief Open file from memory block + * @param mem Pointer to the memory block + * @param length Size of given block + */ + void openData(const void *mem, size_t length) + { + if(m_fp) + this->close();//Close previously opened file first! + m_fp = NULL; + m_mp = mem; + m_mp_size = length; + m_mp_tell = 0; + } + + /** + * @brief Seek to given position + * @param pos Offset or position + * @param rel_to Relation (at begin, at current, or at end) + */ + void seek(long pos, int rel_to) + { + if(!this->isValid()) + return; + + if(m_fp)//If a file + { + std::fseek(m_fp, pos, rel_to); + } + else//If a memory block + { + switch(rel_to) + { + default: + case SET: + m_mp_tell = static_cast(pos); + break; + + case END: + m_mp_tell = m_mp_size - static_cast(pos); + break; + + case CUR: + m_mp_tell = m_mp_tell + static_cast(pos); + break; + } + + if(m_mp_tell > m_mp_size) + m_mp_tell = m_mp_size; + } + } + + /** + * @brief Seek to given position (unsigned integer 64 as relation. Negative values not supported) + * @param pos Offset or position + * @param rel_to Relation (at begin, at current, or at end) + */ + inline void seeku(uint64_t pos, int rel_to) + { + this->seek(static_cast(pos), rel_to); + } + + /** + * @brief Read the buffer from a file + * @param buf Pointer to the destination memory block + * @param num Number of elements + * @param size Size of one element + * @return Size + */ + size_t read(void *buf, size_t num, size_t size) + { + if(!this->isValid()) + return 0; + if(m_fp) + return std::fread(buf, num, size, m_fp); + else + { + size_t pos = 0; + size_t maxSize = static_cast(size * num); + + while((pos < maxSize) && (m_mp_tell < m_mp_size)) + { + reinterpret_cast(buf)[pos] = reinterpret_cast(m_mp)[m_mp_tell]; + m_mp_tell++; + pos++; + } + + return pos / num; + } + } + + /** + * @brief Get one byte and seek forward + * @return Readed byte or EOF (a.k.a. -1) + */ + int getc() + { + if(!this->isValid()) + return -1; + if(m_fp)//If a file + { + return std::getc(m_fp); + } + else //If a memory block + { + if(m_mp_tell >= m_mp_size) + return -1; + int x = reinterpret_cast(m_mp)[m_mp_tell]; + m_mp_tell++; + return x; + } + } + + /** + * @brief Returns current offset of cursor in a file + * @return Offset position + */ + size_t tell() + { + if(!this->isValid()) + return 0; + if(m_fp)//If a file + return static_cast(std::ftell(m_fp)); + else//If a memory block + return m_mp_tell; + } + + /** + * @brief Close the file + */ + void close() + { + if(m_fp) + std::fclose(m_fp); + + m_fp = NULL; + m_mp = NULL; + m_mp_size = 0; + m_mp_tell = 0; + } + + /** + * @brief Is file instance valid + * @return true if vaild + */ + bool isValid() + { + return (m_fp) || (m_mp); + } + + /** + * @brief Is End Of File? + * @return true if end of file was reached + */ + bool eof() + { + if(!this->isValid()) + return true; + if(m_fp) + return (std::feof(m_fp) != 0); + else + return m_mp_tell >= m_mp_size; + } + + /** + * @brief Get a current file name + * @return File name of currently loaded file + */ + const std::string &fileName() + { + return m_file_name; + } + + /** + * @brief Retrieve file size + * @return Size of file in bytes + */ + size_t fileSize() + { + if(!this->isValid()) + return 0; + if(!m_fp) + return m_mp_size; //Size of memory block is well known + size_t old_pos = this->tell(); + seek(0l, FileAndMemReader::END); + size_t file_size = this->tell(); + seek(static_cast(old_pos), FileAndMemReader::SET); + return file_size; + } +}; + +#endif /* FILE_AND_MEM_READER_HHHH */ diff --git a/thirdparty/opnmidi/fraction.hpp b/thirdparty/opnmidi/fraction.hpp new file mode 100644 index 000000000..892ba25d4 --- /dev/null +++ b/thirdparty/opnmidi/fraction.hpp @@ -0,0 +1,220 @@ +/* + * Fraction number handling. + * Copyright (C) 1992,2001 Bisqwit (http://iki.fi/bisqwit/) + * + * The license of this file is in Public Domain: + * https://bisqwit.iki.fi/src/index.html + * + * "... and orphan source code files are copyrighted public domain." + */ + +#ifndef bqw_fraction_h +#define bqw_fraction_h + +#include +#include + +template +class fraction +{ + inttype num1, num2; + typedef fraction self; + void Optim(); + + #if 1 + inline void Debug(char, const self &) { } + #else + inline void Debug(char op, const self &b) + { + cerr << nom() << '/' << denom() << ' ' << op + << ' ' << b.nom() << '/' << denom() + << ":\n"; + } + #endif +public: + void set(inttype n, inttype d) { num1=n; num2=d; Optim(); } + + fraction() : num1(0), num2(1) { } + fraction(inttype value) : num1(value), num2(1) { } + fraction(inttype n, inttype d) : num1(n), num2(d) { } + fraction(int value) : num1(value), num2(1) { } + template + explicit fraction(const floattype value) { operator= (value); } + inline double value() const {return nom() / (double)denom(); } + inline long double valuel() const {return nom() / (long double)denom(); } + self &operator+= (const inttype &value) { num1+=value*denom(); Optim(); return *this; } + self &operator-= (const inttype &value) { num1-=value*denom(); Optim(); return *this; } + self &operator*= (const inttype &value) { num1*=value; Optim(); return *this; } + self &operator/= (const inttype &value) { num2*=value; Optim(); return *this; } + self &operator+= (const self &b); + self &operator-= (const self &b); + self &operator*= (const self &b) { Debug('*',b);num1*=b.nom(); num2*=b.denom(); Optim(); return *this; } + self &operator/= (const self &b) { Debug('/',b);num1*=b.denom(); num2*=b.nom(); Optim(); return *this; } + self operator- () const { return self(-num1, num2); } + +#define fraction_blah_func(op1, op2) \ + self operator op1 (const self &b) const { self tmp(*this); tmp op2 b; return tmp; } + + fraction_blah_func( +, += ) + fraction_blah_func( -, -= ) + fraction_blah_func( /, /= ) + fraction_blah_func( *, *= ) + +#undef fraction_blah_func +#define fraction_blah_func(op) \ + bool operator op(const self &b) const { return value() op b.value(); } \ + bool operator op(inttype b) const { return value() op b; } + + fraction_blah_func( < ) + fraction_blah_func( > ) + fraction_blah_func( <= ) + fraction_blah_func( >= ) + +#undef fraction_blah_func + + const inttype &nom() const { return num1; } + const inttype &denom() const { return num2; } + inline bool operator == (inttype b) const { return denom() == 1 && nom() == b; } + inline bool operator != (inttype b) const { return denom() != 1 || nom() != b; } + inline bool operator == (const self &b) const { return denom()==b.denom() && nom()==b.nom(); } + inline bool operator != (const self &b) const { return denom()!=b.denom() || nom()!=b.nom(); } + //operator bool () const { return nom() != 0; } + inline bool negative() const { return (nom() < 0) ^ (denom() < 0); } + + self &operator= (const inttype value) { num2=1; num1=value; return *this; } + //self &operator= (int value) { num2=1; num1=value; return *this; } + + self &operator= (double orig) { return *this = (long double)orig; } + self &operator= (long double orig); +}; + +#ifdef _MSC_VER +#pragma warning(disable:4146) +#endif + +template +void fraction::Optim() +{ + /* Euclidean algorithm */ + inttype n1, n2, nn1, nn2; + + nn1 = std::numeric_limits::is_signed ? (num1 >= 0 ? num1 : -num1) : num1; + nn2 = std::numeric_limits::is_signed ? (num2 >= 0 ? num2 : -num2) : num2; + + if(nn1 < nn2) + n1 = num1, n2 = num2; + else + n1 = num2, n2 = num1; + + if(!num1) { num2 = 1; return; } + for(;;) + { + //fprintf(stderr, "%d/%d: n1=%d,n2=%d\n", nom(),denom(),n1,n2); + inttype tmp = n2 % n1; + if(!tmp)break; + n2 = n1; + n1 = tmp; + } + num1 /= n1; + num2 /= n1; + //fprintf(stderr, "result: %d/%d\n\n", nom(), denom()); +} + +#ifdef _MSC_VER +#pragma warning(default:4146) +#endif + +template +inline const fraction abs(const fraction &f) +{ + return fraction(abs(f.nom()), abs(f.denom())); +} + +#define fraction_blah_func(op) \ + template \ + fraction operator op \ + (const inttype bla, const fraction &b) \ + { return fraction (bla) op b; } +fraction_blah_func( + ) +fraction_blah_func( - ) +fraction_blah_func( * ) +fraction_blah_func( / ) +#undef fraction_blah_func + +#define fraction_blah_func(op1, op2) \ + template \ + fraction &fraction::operator op2 (const fraction &b) \ + { \ + inttype newnom = nom()*b.denom() op1 denom()*b.nom(); \ + num2 *= b.denom(); \ + num1 = newnom; \ + Optim(); \ + return *this; \ + } +fraction_blah_func( +, += ) +fraction_blah_func( -, -= ) +#undef fraction_blah_func + +template +fraction &fraction::operator= (long double orig) +{ + if(orig == 0.0) + { + set(0, 0); + return *this; + } + + inttype cf[25]; + for(int maxdepth=1; maxdepth<25; ++maxdepth) + { + inttype u,v; + long double virhe, a=orig; + int i, viim; + + for(i = 0; i < maxdepth; ++i) + { + cf[i] = (inttype)a; + if(cf[i]-1 > cf[i])break; + a = 1.0 / (a - cf[i]); + } + + for(viim=i-1; i < maxdepth; ++i) + cf[i] = 0; + + u = cf[viim]; + v = 1; + for(i = viim-1; i >= 0; --i) + { + inttype w = cf[i] * u + v; + v = u; + u = w; + } + + virhe = (orig - (u / (long double)v)) / orig; + + set(u, v); + //if(verbose > 4) + // cerr << "Guess: " << *this << " - error = " << virhe*100 << "%\n"; + + if(virhe < 1e-8 && virhe > -1e-8)break; + } + + //if(verbose > 4) + //{ + // cerr << "Fraction=" << orig << ": " << *this << endl; + //} + + return *this; +} + + +/* +template +ostream &operator << (ostream &dest, const fraction &m) +{ + if(m.denom() == (inttype)1) return dest << m.nom(); + return dest << m.nom() << '/' << m.denom(); +} +*/ + +#endif diff --git a/thirdparty/opnmidi/opnbank.h b/thirdparty/opnmidi/opnbank.h new file mode 100644 index 000000000..68aa28fca --- /dev/null +++ b/thirdparty/opnmidi/opnbank.h @@ -0,0 +1,145 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2016-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OPNMIDI_OPNBANK_H +#define OPNMIDI_OPNBANK_H + +#include +#include + +#ifdef ADLMIDI_buildAsApp +#include +class MutexType +{ + SDL_mutex* mut; +public: + MutexType() : mut(SDL_CreateMutex()) { } + ~MutexType() { SDL_DestroyMutex(mut); } + void Lock() { SDL_mutexP(mut); } + void Unlock() { SDL_mutexV(mut); } +}; +#endif + +enum { opnNoteOnMaxTime = 40000 }; + +/* *********** FM Operator indexes *********** */ +enum +{ + OPERATOR1 = 0, + OPERATOR2 = 1, + OPERATOR3 = 2, + OPERATOR4 = 3 +}; +/* *********** FM Operator indexes *end******* */ + +#pragma pack(push, 1) +#define OPNDATA_BYTE_COMPARABLE(T) \ + inline bool operator==(const T &a, const T &b) \ + { return !memcmp(&a, &b, sizeof(T)); } \ + inline bool operator!=(const T &a, const T &b) \ + { return !operator==(a, b); } + +struct OPN_Operator +{ + //! Raw register data + uint8_t data[7]; + /* + Bytes: + 0 - Deture/Multiple + 1 - Total Level + 2 - Rate Scale / Attack + 3 - Amplitude modulation / Decay-1 + 4 - Decay-2 + 5 - Systain / Release + 6 - SSG-EG byte + */ +}; +OPNDATA_BYTE_COMPARABLE(struct OPN_Operator) + +struct OpnTimbre +{ + //! Operators prepared for sending to OPL chip emulator + OPN_Operator OPS[4]; + //! Feedback / algorithm + uint8_t fbalg; + //! LFO sensitivity + uint8_t lfosens; + //! Semi-tone offset + int16_t noteOffset; +}; +OPNDATA_BYTE_COMPARABLE(struct OpnTimbre) + +/** + * @brief Instrument data with operators included + */ +struct OpnInstMeta +{ + enum + { + Flag_Pseudo8op = 0x01, + Flag_NoSound = 0x02 + }; + + //! Operator data + OpnTimbre op[2]; + //! Fixed note for drum instruments + uint8_t drumTone; + //! Instrument flags + uint8_t flags; + //! Number of milliseconds it produces sound while key on + uint16_t soundKeyOnMs; + //! Number of milliseconds it produces sound while releasing after key off + uint16_t soundKeyOffMs; + //! MIDI velocity offset + int8_t midiVelocityOffset; + //! Second voice detune + double voice2_fine_tune; +}; +OPNDATA_BYTE_COMPARABLE(struct OpnInstMeta) + +#undef OPNDATA_BYTE_COMPARABLE +#pragma pack(pop) + +/** + * @brief Bank global setup + */ +struct OpnBankSetup +{ + int volumeModel; + int lfoEnable; + int lfoFrequency; + int chipType; + bool mt32defaults; +}; + +/** + * @brief Convert external instrument to internal instrument + */ +void cvt_OPNI_to_FMIns(OpnInstMeta &dst, const struct OPN2_Instrument &src); + +/** + * @brief Convert internal instrument to external instrument + */ +void cvt_FMIns_to_OPNI(struct OPN2_Instrument &dst, const OpnInstMeta &src); + +#endif // OPNMIDI_OPNBANK_H diff --git a/thirdparty/opnmidi/opnmidi.cpp b/thirdparty/opnmidi/opnmidi.cpp new file mode 100644 index 000000000..cbefab299 --- /dev/null +++ b/thirdparty/opnmidi/opnmidi.cpp @@ -0,0 +1,1539 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * OPNMIDI Library and YM2612 support: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "opnmidi_midiplay.hpp" +#include "opnmidi_opn2.hpp" +#include "opnmidi_private.hpp" +#include "chips/opn_chip_base.h" +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER +#include "midi_sequencer.hpp" +#endif + +/* Unify MIDI player casting and interface between ADLMIDI and OPNMIDI */ +#define GET_MIDI_PLAYER(device) reinterpret_cast((device)->opn2_midiPlayer) +typedef OPNMIDIplay MidiPlayer; + +static OPN2_Version opn2_version = { + OPNMIDI_VERSION_MAJOR, + OPNMIDI_VERSION_MINOR, + OPNMIDI_VERSION_PATCHLEVEL +}; + +static const OPNMIDI_AudioFormat opn2_DefaultAudioFormat = +{ + OPNMIDI_SampleType_S16, + sizeof(int16_t), + 2 * sizeof(int16_t), +}; + +/*---------------------------EXPORTS---------------------------*/ + +OPNMIDI_EXPORT struct OPN2_MIDIPlayer *opn2_init(long sample_rate) +{ + OPN2_MIDIPlayer *midi_device; + midi_device = (OPN2_MIDIPlayer *)malloc(sizeof(OPN2_MIDIPlayer)); + if(!midi_device) + { + OPN2MIDI_ErrorString = "Can't initialize OPNMIDI: out of memory!"; + return NULL; + } + + OPNMIDIplay *player = new(std::nothrow) OPNMIDIplay(static_cast(sample_rate)); + if(!player) + { + free(midi_device); + OPN2MIDI_ErrorString = "Can't initialize OPNMIDI: out of memory!"; + return NULL; + } + midi_device->opn2_midiPlayer = player; + return midi_device; +} + +OPNMIDI_EXPORT int opn2_setDeviceIdentifier(OPN2_MIDIPlayer *device, unsigned id) +{ + if(!device || id > 0x0f) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->setDeviceId(static_cast(id)); + return 0; +} + +OPNMIDI_EXPORT int opn2_setNumChips(OPN2_MIDIPlayer *device, int numChips) +{ + if(device == NULL) + return -2; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.numChips = static_cast(numChips); + if(play->m_setup.numChips < 1 || play->m_setup.numChips > OPN_MAX_CHIPS) + { + play->setErrorString("number of chips may only be 1.." OPN_MAX_CHIPS_STR ".\n"); + return -1; + } + + Synth &synth = *play->m_synth; + if(!synth.setupLocked()) + { + synth.m_numChips = play->m_setup.numChips; + play->partialReset(); + } + + return 0; +} + +OPNMIDI_EXPORT int opn2_getNumChips(struct OPN2_MIDIPlayer *device) +{ + if(device == NULL) + return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_setup.numChips; +} + +OPNMIDI_EXPORT int opn2_getNumChipsObtained(struct OPN2_MIDIPlayer *device) +{ + if(device == NULL) + return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_synth->m_numChips; +} + + +OPNMIDI_EXPORT int opn2_reserveBanks(OPN2_MIDIPlayer *device, unsigned banks) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth::BankMap &map = play->m_synth->m_insBanks; + map.reserve(banks); + return (int)map.capacity(); +} + +OPNMIDI_EXPORT int opn2_getBank(OPN2_MIDIPlayer *device, const OPN2_BankId *idp, int flags, OPN2_Bank *bank) +{ + if(!device || !idp || !bank) + return -1; + + OPN2_BankId id = *idp; + if(id.lsb > 127 || id.msb > 127 || id.percussive > 1) + return -1; + size_t idnumber = ((id.msb << 8) | id.lsb | (id.percussive ? size_t(Synth::PercussionTag) : 0)); + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth::BankMap &map = play->m_synth->m_insBanks; + + Synth::BankMap::iterator it; + if(!(flags & OPNMIDI_Bank_Create)) + { + it = map.find(idnumber); + if(it == map.end()) + return -1; + } + else + { + std::pair value; + value.first = idnumber; + memset(&value.second, 0, sizeof(value.second)); + for (unsigned i = 0; i < 128; ++i) + value.second.ins[i].flags = OpnInstMeta::Flag_NoSound; + + std::pair ir; + if((flags & OPNMIDI_Bank_CreateRt) == OPNMIDI_Bank_CreateRt) + { + ir = map.insert(value, Synth::BankMap::do_not_expand_t()); + if(ir.first == map.end()) + return -1; + } + else + ir = map.insert(value); + it = ir.first; + } + + it.to_ptrs(bank->pointer); + return 0; +} + +OPNMIDI_EXPORT int opn2_getBankId(OPN2_MIDIPlayer *device, const OPN2_Bank *bank, OPN2_BankId *id) +{ + if(!device || !bank) + return -1; + + Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer); + Synth::BankMap::key_type idnumber = it->first; + id->msb = (idnumber >> 8) & 127; + id->lsb = idnumber & 127; + id->percussive = (idnumber & Synth::PercussionTag) ? 1 : 0; + return 0; +} + +OPNMIDI_EXPORT int opn2_removeBank(OPN2_MIDIPlayer *device, OPN2_Bank *bank) +{ + if(!device || !bank) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth::BankMap &map = play->m_synth->m_insBanks; + Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer); + size_t size = map.size(); + map.erase(it); + return (map.size() != size) ? 0 : -1; +} + +OPNMIDI_EXPORT int opn2_getFirstBank(OPN2_MIDIPlayer *device, OPN2_Bank *bank) +{ + if(!device) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth::BankMap &map = play->m_synth->m_insBanks; + + Synth::BankMap::iterator it = map.begin(); + if(it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +OPNMIDI_EXPORT int opn2_getNextBank(OPN2_MIDIPlayer *device, OPN2_Bank *bank) +{ + if(!device) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth::BankMap &map = play->m_synth->m_insBanks; + + Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer); + if(++it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +OPNMIDI_EXPORT int opn2_getInstrument(OPN2_MIDIPlayer *device, const OPN2_Bank *bank, unsigned index, OPN2_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return -1; + + Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer); + cvt_FMIns_to_OPNI(*ins, it->second.ins[index]); + ins->version = 0; + return 0; +} + +OPNMIDI_EXPORT int opn2_setInstrument(OPN2_MIDIPlayer *device, OPN2_Bank *bank, unsigned index, const OPN2_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return -1; + + if(ins->version != 0) + return -1; + + Synth::BankMap::iterator it = Synth::BankMap::iterator::from_ptrs(bank->pointer); + cvt_OPNI_to_FMIns(it->second.ins[index], *ins); + return 0; +} + +OPNMIDI_EXPORT int opn2_openBankFile(OPN2_MIDIPlayer *device, const char *filePath) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadBank(filePath)) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("OPN2 MIDI: Can't load file"); + return -1; + } + else + return 0; + } + OPN2MIDI_ErrorString = "Can't load file: OPN2 MIDI is not initialized"; + return -1; +} + +OPNMIDI_EXPORT int opn2_openBankData(OPN2_MIDIPlayer *device, const void *mem, long size) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadBank(mem, static_cast(size))) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("OPN2 MIDI: Can't load data from memory"); + return -1; + } + else return 0; + } + + OPN2MIDI_ErrorString = "Can't load file: OPN2 MIDI is not initialized"; + return -1; +} + +OPNMIDI_EXPORT void opn2_setLfoEnabled(struct OPN2_MIDIPlayer *device, int lfoEnable) +{ + if(!device) return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + play->m_setup.lfoEnable = lfoEnable; + synth.m_lfoEnable = (lfoEnable < 0 ? + synth.m_insBankSetup.lfoEnable : + play->m_setup.lfoEnable) != 0; + synth.commitLFOSetup(); +} + +OPNMIDI_EXPORT int opn2_getLfoEnabled(struct OPN2_MIDIPlayer *device) +{ + if(!device) return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth->m_lfoEnable; +} + +OPNMIDI_EXPORT void opn2_setLfoFrequency(struct OPN2_MIDIPlayer *device, int lfoFrequency) +{ + if(!device) return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + play->m_setup.lfoFrequency = lfoFrequency; + synth.m_lfoFrequency = lfoFrequency < 0 ? + synth.m_insBankSetup.lfoFrequency : + (uint8_t)play->m_setup.lfoFrequency; + synth.commitLFOSetup(); +} + +OPNMIDI_EXPORT int opn2_getLfoFrequency(struct OPN2_MIDIPlayer *device) +{ + if(!device) return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth->m_lfoFrequency; +} + +/*Override chip type. -1 - use bank default state*/ +OPNMIDI_EXPORT void opn2_setChipType(struct OPN2_MIDIPlayer *device, int chipType) +{ + if(!device) return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.chipType = chipType; + play->applySetup(); +} + +/*Get the chip type*/ +OPNMIDI_EXPORT int opn2_getChipType(struct OPN2_MIDIPlayer *device) +{ + if(!device) return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth->chipFamily(); +} + +OPNMIDI_EXPORT void opn2_setScaleModulators(OPN2_MIDIPlayer *device, int smod) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.ScaleModulators = smod; + play->m_synth->m_scaleModulators = (play->m_setup.ScaleModulators != 0); +} + +OPNMIDI_EXPORT void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device, int fr_brightness) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.fullRangeBrightnessCC74 = (fr_brightness != 0); +} + +OPNMIDI_EXPORT void opn2_setAutoArpeggio(OPN2_MIDIPlayer *device, int aaEn) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.enableAutoArpeggio = (aaEn != 0); +} + +OPNMIDI_EXPORT int opn2_getAutoArpeggio(OPN2_MIDIPlayer *device) +{ + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_setup.enableAutoArpeggio ? 1 : 0; +} + +OPNMIDI_EXPORT void opn2_setLoopEnabled(OPN2_MIDIPlayer *device, int loopEn) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer->setLoopEnabled(loopEn != 0); +#else + ADL_UNUSED(device); + ADL_UNUSED(loopEn); +#endif +} + +OPNMIDI_EXPORT void opn2_setLoopCount(OPN2_MIDIPlayer *device, int loopCount) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer->setLoopsCount(loopCount); +#else + ADL_UNUSED(device); + ADL_UNUSED(loopCount); +#endif +} + +OPNMIDI_EXPORT void opn2_setLoopHooksOnly(OPN2_MIDIPlayer *device, int loopHooksOnly) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer->setLoopHooksOnly(loopHooksOnly); +#else + ADL_UNUSED(device); + ADL_UNUSED(loopHooksOnly); +#endif +} + +OPNMIDI_EXPORT void opn2_setSoftPanEnabled(OPN2_MIDIPlayer *device, int softPanEn) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_synth->m_softPanning = (softPanEn != 0); +} + +/* !!!DEPRECATED!!! */ +OPNMIDI_EXPORT void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, int logvol) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + play->m_setup.LogarithmicVolumes = static_cast(logvol); + if(!synth.setupLocked()) + { + if(play->m_setup.LogarithmicVolumes != 0) + synth.setVolumeScaleModel(OPNMIDI_VolumeModel_NativeOPN2); + else + synth.setVolumeScaleModel(static_cast(play->m_setup.VolumeModel)); + } +} + +OPNMIDI_EXPORT void opn2_setVolumeRangeModel(struct OPN2_MIDIPlayer *device, int volumeModel) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + play->m_setup.VolumeModel = volumeModel; + if(!synth.setupLocked()) + { + if(play->m_setup.VolumeModel == OPNMIDI_VolumeModel_AUTO)//Use bank default volume model + synth.m_volumeScale = (Synth::VolumesScale)synth.m_insBankSetup.volumeModel; + else + synth.setVolumeScaleModel(static_cast(volumeModel)); + } +} + +OPNMIDI_EXPORT void opn2_setChannelAllocMode(struct OPN2_MIDIPlayer *device, int chanalloc) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + if(chanalloc < -1 || chanalloc >= OPNMIDI_ChanAlloc_Count) + chanalloc = OPNMIDI_ChanAlloc_AUTO; + synth.m_channelAlloc = static_cast(chanalloc); +} + +OPNMIDI_EXPORT int opn2_getChannelAllocMode(struct OPN2_MIDIPlayer *device) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return static_cast(play->m_synth->m_channelAlloc); +} + +OPNMIDI_EXPORT int opn2_getVolumeRangeModel(struct OPN2_MIDIPlayer *device) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth->getVolumeScaleModel(); +} + +OPNMIDI_EXPORT int opn2_openFile(OPN2_MIDIPlayer *device, const char *filePath) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadMIDI(filePath)) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("OPN2 MIDI: Can't load file"); + return -1; + } + else return 0; +#else + ADL_UNUSED(filePath); + play->setErrorString("OPNMIDI: MIDI Sequencer is not supported in this build of library!"); + return -1; +#endif + } + + OPN2MIDI_ErrorString = "Can't load file: OPN2 MIDI is not initialized"; + return -1; +} + +OPNMIDI_EXPORT int opn2_openData(OPN2_MIDIPlayer *device, const void *mem, unsigned long size) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + play->m_setup.tick_skip_samples_delay = 0; + if(!play->LoadMIDI(mem, static_cast(size))) + { + std::string err = play->getErrorString(); + if(err.empty()) + play->setErrorString("OPN2 MIDI: Can't load data from memory"); + return -1; + } + else return 0; +#else + ADL_UNUSED(mem); + ADL_UNUSED(size); + play->setErrorString("OPNMIDI: MIDI Sequencer is not supported in this build of library!"); + return -1; +#endif + } + + OPN2MIDI_ErrorString = "Can't load file: OPN2 MIDI is not initialized"; + return -1; +} + +OPNMIDI_EXPORT void opn2_selectSongNum(struct OPN2_MIDIPlayer *device, int songNumber) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer->setSongNum(songNumber); +#else + ADL_UNUSED(device); + ADL_UNUSED(songNumber); +#endif +} + +OPNMIDI_EXPORT int opn2_getSongsCount(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getSongsCount(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +OPNMIDI_EXPORT const char *opn2_emulatorName() +{ + return ""; +} + +OPNMIDI_EXPORT const char *opn2_chipEmulatorName(struct OPN2_MIDIPlayer *device) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + if(!synth.m_chips.empty()) + return synth.m_chips[0]->emulatorName(); + } + return "Unknown"; +} + +OPNMIDI_EXPORT int opn2_switchEmulator(struct OPN2_MIDIPlayer *device, int emulator) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + if(opn2_isEmulatorAvailable(emulator)) + { + play->m_setup.emulator = emulator; + play->partialReset(); + return 0; + } + play->setErrorString("OPN2 MIDI: Unknown emulation core!"); + } + return -1; +} + + +OPNMIDI_EXPORT int opn2_setRunAtPcmRate(OPN2_MIDIPlayer *device, int enabled) +{ + if(device) + { + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + Synth &synth = *play->m_synth; + play->m_setup.runAtPcmRate = (enabled != 0); + if(!synth.setupLocked()) + play->partialReset(); + return 0; + } + return -1; +} + + +OPNMIDI_EXPORT const char *opn2_linkedLibraryVersion() +{ +#if !defined(OPNMIDI_ENABLE_HQ_RESAMPLER) + return OPNMIDI_VERSION; +#else + return OPNMIDI_VERSION " (HQ)"; +#endif +} + +OPNMIDI_EXPORT const OPN2_Version *opn2_linkedVersion() +{ + return &opn2_version; +} + +OPNMIDI_EXPORT const char *opn2_errorString() +{ + return OPN2MIDI_ErrorString.c_str(); +} + +OPNMIDI_EXPORT const char *opn2_errorInfo(struct OPN2_MIDIPlayer *device) +{ + if(!device) + return opn2_errorString(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + if(!play) + return opn2_errorString(); + return play->getErrorString().c_str(); +} + +OPNMIDI_EXPORT void opn2_close(OPN2_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + delete play; + device->opn2_midiPlayer = NULL; + free(device); + device = NULL; +} + +OPNMIDI_EXPORT void opn2_reset(OPN2_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->partialReset(); + play->resetMIDI(); +} + +OPNMIDI_EXPORT double opn2_totalTimeLength(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->timeLength(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +OPNMIDI_EXPORT double opn2_loopStartTime(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getLoopStart(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +OPNMIDI_EXPORT double opn2_loopEndTime(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getLoopEnd(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +OPNMIDI_EXPORT double opn2_positionTell(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->tell(); +#else + ADL_UNUSED(device); + return -1.0; +#endif +} + +OPNMIDI_EXPORT void opn2_positionSeek(struct OPN2_MIDIPlayer *device, double seconds) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(seconds < 0.0) + return;//Seeking negative position is forbidden! :-P + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); + play->m_setup.delay = play->m_sequencer->seek(seconds, play->m_setup.mindelay); + play->m_setup.carry = 0.0; +#else + ADL_UNUSED(device); + ADL_UNUSED(seconds); +#endif +} + +OPNMIDI_EXPORT void opn2_positionRewind(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); + play->m_sequencer->rewind(); +#else + ADL_UNUSED(device); +#endif +} + +OPNMIDI_EXPORT void opn2_setTempo(struct OPN2_MIDIPlayer *device, double tempo) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device || (tempo <= 0.0)) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer->setTempo(tempo); +#else + ADL_UNUSED(device); + ADL_UNUSED(tempo); +#endif +} + + +OPNMIDI_EXPORT int opn2_describeChannels(struct OPN2_MIDIPlayer *device, char *str, char *attr, size_t size) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->describeChannels(str, attr, size); + return 0; +} + + +OPNMIDI_EXPORT const char *opn2_metaMusicTitle(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return ""; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getMusicTitle().c_str(); +#else + ADL_UNUSED(device); + return ""; +#endif +} + + +OPNMIDI_EXPORT const char *opn2_metaMusicCopyright(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return ""; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getMusicCopyright().c_str(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +OPNMIDI_EXPORT size_t opn2_metaTrackTitleCount(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getTrackTitles().size(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +OPNMIDI_EXPORT const char *opn2_metaTrackTitle(struct OPN2_MIDIPlayer *device, size_t index) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return ""; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + const std::vector &titles = play->m_sequencer->getTrackTitles(); + if(index >= titles.size()) + return "INVALID"; + return titles[index].c_str(); +#else + ADL_UNUSED(device); + ADL_UNUSED(index); + return "NOT SUPPORTED"; +#endif +} + + +OPNMIDI_EXPORT size_t opn2_metaMarkerCount(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getMarkers().size(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +OPNMIDI_EXPORT Opn2_MarkerEntry opn2_metaMarker(struct OPN2_MIDIPlayer *device, size_t index) +{ + struct Opn2_MarkerEntry marker; + +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + { + marker.label = "INVALID"; + marker.pos_time = 0.0; + marker.pos_ticks = 0; + return marker; + } + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + + const std::vector &markers = play->m_sequencer->getMarkers(); + if(index >= markers.size()) + { + marker.label = "INVALID"; + marker.pos_time = 0.0; + marker.pos_ticks = 0; + return marker; + } + + const MidiSequencer::MIDI_MarkerEntry &mk = markers[index]; + marker.label = mk.label.c_str(); + marker.pos_time = mk.pos_time; + marker.pos_ticks = (unsigned long)mk.pos_ticks; +#else + (void)device; (void)index; + marker.label = "NOT SUPPORTED"; + marker.pos_time = 0.0; + marker.pos_ticks = 0; +#endif + return marker; +} + +OPNMIDI_EXPORT void opn2_setRawEventHook(struct OPN2_MIDIPlayer *device, OPN2_RawEventHook rawEventHook, void *userData) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencerInterface->onEvent = rawEventHook; + play->m_sequencerInterface->onEvent_userData = userData; +#else + ADL_UNUSED(device); + ADL_UNUSED(rawEventHook); + ADL_UNUSED(userData); +#endif +} + +/* Set note hook */ +OPNMIDI_EXPORT void opn2_setNoteHook(struct OPN2_MIDIPlayer *device, OPN2_NoteHook noteHook, void *userData) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->hooks.onNote = noteHook; + play->hooks.onNote_userData = userData; +} + +/* Set debug message hook */ +OPNMIDI_EXPORT void opn2_setDebugMessageHook(struct OPN2_MIDIPlayer *device, OPN2_DebugMessageHook debugMessageHook, void *userData) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->hooks.onDebugMessage = debugMessageHook; + play->hooks.onDebugMessage_userData = userData; +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencerInterface->onDebugMessage = debugMessageHook; + play->m_sequencerInterface->onDebugMessage_userData = userData; +#endif +} + +/* Set loop start hook */ +OPNMIDI_EXPORT void opn2_setLoopStartHook(struct OPN2_MIDIPlayer *device, OPN2_LoopPointHook loopStartHook, void *userData) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->hooks.onLoopStart = loopStartHook; + play->hooks.onLoopStart_userData = userData; +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencerInterface->onloopStart = loopStartHook; + play->m_sequencerInterface->onloopStart_userData = userData; +#endif +} + +/* Set loop end hook */ +OPNMIDI_EXPORT void opn2_setLoopEndHook(struct OPN2_MIDIPlayer *device, OPN2_LoopPointHook loopEndHook, void *userData) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->hooks.onLoopEnd = loopEndHook; + play->hooks.onLoopEnd_userData = userData; +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencerInterface->onloopEnd = loopEndHook; + play->m_sequencerInterface->onloopEnd_userData = userData; +#endif +} + + + +template +static void CopySamplesRaw(OPN2_UInt8 *dstLeft, OPN2_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + (i * sampleOffset)) = src[2 * i]; + *(Dst *)(dstRight + (i * sampleOffset)) = src[(2 * i) + 1]; + } +} + +template +static void CopySamplesTransformed(OPN2_UInt8 *dstLeft, OPN2_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset, + Ret(&transform)(int32_t)) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + (i * sampleOffset)) = static_cast(transform(src[2 * i])); + *(Dst *)(dstRight + (i * sampleOffset)) = static_cast(transform(src[(2 * i) + 1])); + } +} + +static int SendStereoAudio(int samples_requested, + ssize_t in_size, + int32_t *_in, + ssize_t out_pos, + OPN2_UInt8 *left, + OPN2_UInt8 *right, + const OPNMIDI_AudioFormat *format) +{ + if(!in_size) + return 0; + size_t outputOffset = static_cast(out_pos); + size_t inSamples = static_cast(in_size * 2); + size_t maxSamples = static_cast(samples_requested) - outputOffset; + size_t toCopy = std::min(maxSamples, inSamples); + + OPNMIDI_SampleType sampleType = format->type; + const unsigned containerSize = format->containerSize; + const unsigned sampleOffset = format->sampleOffset; + + left += (outputOffset / 2) * sampleOffset; + right += (outputOffset / 2) * sampleOffset; + + typedef int32_t(&pfnConvert)(int32_t); + + switch(sampleType) { + case OPNMIDI_SampleType_S8: + case OPNMIDI_SampleType_U8: + { + pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S8) ? opn2_cvtS8 : opn2_cvtU8; + switch(containerSize) { + case sizeof(int8_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case OPNMIDI_SampleType_S16: + case OPNMIDI_SampleType_U16: + { + pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S16) ? opn2_cvtS16 : opn2_cvtU16; + switch(containerSize) { + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + case sizeof(int32_t): + CopySamplesRaw(left, right, _in, toCopy / 2, sampleOffset); + break; + default: + return -1; + } + break; + } + case OPNMIDI_SampleType_S24: + case OPNMIDI_SampleType_U24: + { + pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S24) ? opn2_cvtS24 : opn2_cvtU24; + switch(containerSize) { + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case OPNMIDI_SampleType_S32: + case OPNMIDI_SampleType_U32: + { + pfnConvert cvt = (sampleType == OPNMIDI_SampleType_S32) ? opn2_cvtS32 : opn2_cvtU32; + switch(containerSize) { + case sizeof(int32_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, cvt); + break; + default: + return -1; + } + break; + } + case OPNMIDI_SampleType_F32: + if(containerSize != sizeof(float)) + return -1; + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, opn2_cvtReal); + break; + case OPNMIDI_SampleType_F64: + if(containerSize != sizeof(double)) + return -1; + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, opn2_cvtReal); + break; + default: + return -1; + } + + return 0; +} + + +OPNMIDI_EXPORT int opn2_play(struct OPN2_MIDIPlayer *device, int sampleCount, short *out) +{ + return opn2_playFormat(device, sampleCount, (OPN2_UInt8 *)out, (OPN2_UInt8 *)(out + 1), &opn2_DefaultAudioFormat); +} + +OPNMIDI_EXPORT int opn2_playFormat(OPN2_MIDIPlayer *device, int sampleCount, + OPN2_UInt8 *out_left, OPN2_UInt8 *out_right, + const OPNMIDI_AudioFormat *format) +{ +#if defined(OPNMIDI_DISABLE_MIDI_SEQUENCER) + ADL_UNUSED(device); + ADL_UNUSED(sampleCount); + ADL_UNUSED(out_left); + ADL_UNUSED(out_right); + ADL_UNUSED(format); + return 0; +#endif + +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + sampleCount -= sampleCount % 2; //Avoid even sample requests + if(sampleCount < 0) + return 0; + if(!device) + return 0; + + MidiPlayer *player = GET_MIDI_PLAYER(device); + assert(player); + MidiPlayer::Setup &setup = player->m_setup; + + ssize_t gotten_len = 0; + ssize_t n_periodCountStereo = 512; + //ssize_t n_periodCountPhys = n_periodCountStereo * 2; + int left = sampleCount; + bool hasSkipped = setup.tick_skip_samples_delay > 0; + + while(left > 0) + { + const double eat_delay = setup.delay < setup.maxdelay ? setup.delay : setup.maxdelay; + if(hasSkipped) + { + size_t samples = setup.tick_skip_samples_delay > sampleCount ? sampleCount : setup.tick_skip_samples_delay; + n_periodCountStereo = samples / 2; + } + else + { + setup.delay -= eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; + n_periodCountStereo = static_cast(setup.carry); + setup.carry -= double(n_periodCountStereo); + } + + //if(setup.SkipForward > 0) + // setup.SkipForward -= 1; + //else + { + if((player->m_sequencer->positionAtEnd()) && (setup.delay <= 0.0)) + break;//Stop to fetch samples at reaching the song end with disabled loop + + ssize_t leftSamples = left / 2; + if(n_periodCountStereo > leftSamples) + { + setup.tick_skip_samples_delay = (n_periodCountStereo - leftSamples) * 2; + n_periodCountStereo = leftSamples; + } + //! Count of stereo samples + ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo; + //! Total count of samples + ssize_t in_generatedPhys = in_generatedStereo * 2; + //! Unsigned total sample count + //fill buffer with zeros + int32_t *out_buf = player->m_outBuf; + std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); + Synth &synth = *player->m_synth; + unsigned int chips = synth.m_numChips; + if(chips == 1) + synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); + else/* if(n_periodCountStereo > 0)*/ + { + /* Generate data from every chip and mix result */ + for(size_t card = 0; card < chips; ++card) + synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + } + /* Process it */ + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; + + left -= (int)in_generatedPhys; + gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; + } + if(hasSkipped) + { + setup.tick_skip_samples_delay -= n_periodCountStereo * 2; + hasSkipped = setup.tick_skip_samples_delay > 0; + } + else + setup.delay = player->Tick(eat_delay, setup.mindelay); + } + + return static_cast(gotten_len); +#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER +} + + +OPNMIDI_EXPORT int opn2_generate(struct OPN2_MIDIPlayer *device, int sampleCount, short *out) +{ + return opn2_generateFormat(device, sampleCount, (OPN2_UInt8 *)out, (OPN2_UInt8 *)(out + 1), &opn2_DefaultAudioFormat); +} + +OPNMIDI_EXPORT int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampleCount, + OPN2_UInt8 *out_left, OPN2_UInt8 *out_right, + const OPNMIDI_AudioFormat *format) +{ + sampleCount -= sampleCount % 2; //Avoid even sample requests + if(sampleCount < 0) + return 0; + if(!device) + return 0; + + MidiPlayer *player = GET_MIDI_PLAYER(device); + assert(player); + MidiPlayer::Setup &setup = player->m_setup; + + ssize_t gotten_len = 0; + ssize_t n_periodCountStereo = 512; + + int left = sampleCount; + double delay = double(sampleCount / 2) / double(setup.PCM_RATE); + + while(left > 0) + { + if(delay <= 0.0) + delay = double(left / 2) / double(setup.PCM_RATE); + const double eat_delay = delay < setup.maxdelay ? delay : setup.maxdelay; + delay -= eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; + n_periodCountStereo = static_cast(setup.carry); + setup.carry -= double(n_periodCountStereo); + + { + ssize_t leftSamples = left / 2; + if(n_periodCountStereo > leftSamples) + n_periodCountStereo = leftSamples; + //! Count of stereo samples + ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo; + //! Total count of samples + ssize_t in_generatedPhys = in_generatedStereo * 2; + //! Unsigned total sample count + //fill buffer with zeros + int32_t *out_buf = player->m_outBuf; + std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); + Synth &synth = *player->m_synth; + unsigned int chips = synth.m_numChips; + if(chips == 1) + synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); + else/* if(n_periodCountStereo > 0)*/ + { + /* Generate data from every chip and mix result */ + for(size_t card = 0; card < chips; ++card) + synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + } + /* Process it */ + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; + + left -= (int)in_generatedPhys; + gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; + } + + player->TickIterators(eat_delay); + } + + return static_cast(gotten_len); +} + +OPNMIDI_EXPORT double opn2_tickEvents(struct OPN2_MIDIPlayer *device, double seconds, double granuality) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1.0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->Tick(seconds, granuality); +#else + ADL_UNUSED(device); + ADL_UNUSED(seconds); + ADL_UNUSED(granuality); + return -1.0; +#endif +} + +OPNMIDI_EXPORT int opn2_atEnd(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_sequencer->positionAtEnd(); +#else + ADL_UNUSED(device); + return 1; +#endif +} + +OPNMIDI_EXPORT size_t opn2_trackCount(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer->getTrackCount(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +OPNMIDI_EXPORT int opn2_setTrackOptions(struct OPN2_MIDIPlayer *device, size_t trackNumber, unsigned trackOptions) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + MidiSequencer &seq = *play->m_sequencer; + + unsigned enableFlag = trackOptions & 3; + trackOptions &= ~3u; + + // handle on/off/solo + switch(enableFlag) + { + default: + break; + case OPNMIDI_TrackOption_On: + case OPNMIDI_TrackOption_Off: + if(!seq.setTrackEnabled(trackNumber, enableFlag == OPNMIDI_TrackOption_On)) + return -1; + break; + case OPNMIDI_TrackOption_Solo: + seq.setSoloTrack(trackNumber); + break; + } + + // handle others... + if(trackOptions != 0) + return -1; + + return 0; + +#else + ADL_UNUSED(device); + ADL_UNUSED(trackNumber); + ADL_UNUSED(trackOptions); + return -1; +#endif +} + +OPNMIDI_EXPORT int opn2_setChannelEnabled(struct OPN2_MIDIPlayer *device, size_t channelNumber, int enabled) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + MidiSequencer &seq = *play->m_sequencer; + if(!seq.setChannelEnabled(channelNumber, (bool)enabled)) + return -1; + return 0; +#else + ADL_UNUSED(device); + ADL_UNUSED(channelNumber); + ADL_UNUSED(enabled); + return -1; +#endif +} + +OPNMIDI_EXPORT void opn2_panic(struct OPN2_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); +} + +OPNMIDI_EXPORT void opn2_rt_resetState(struct OPN2_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_ResetState(); +} + +OPNMIDI_EXPORT int opn2_rt_noteOn(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note, OPN2_UInt8 velocity) +{ + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->realTime_NoteOn(channel, note, velocity); +} + +OPNMIDI_EXPORT void opn2_rt_noteOff(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_NoteOff(channel, note); +} + +OPNMIDI_EXPORT void opn2_rt_noteAfterTouch(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note, OPN2_UInt8 atVal) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_NoteAfterTouch(channel, note, atVal); +} + +OPNMIDI_EXPORT void opn2_rt_channelAfterTouch(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 atVal) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_ChannelAfterTouch(channel, atVal); +} + +OPNMIDI_EXPORT void opn2_rt_controllerChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 type, OPN2_UInt8 value) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_Controller(channel, type, value); +} + +OPNMIDI_EXPORT void opn2_rt_patchChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 patch) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PatchChange(channel, patch); +} + +OPNMIDI_EXPORT void opn2_rt_pitchBend(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt16 pitch) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PitchBend(channel, pitch); +} + +OPNMIDI_EXPORT void opn2_rt_pitchBendML(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 msb, OPN2_UInt8 lsb) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PitchBend(channel, msb, lsb); +} + +OPNMIDI_EXPORT void opn2_rt_bankChangeLSB(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 lsb) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChangeLSB(channel, lsb); +} + +OPNMIDI_EXPORT void opn2_rt_bankChangeMSB(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 msb) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChangeMSB(channel, msb); +} + +OPNMIDI_EXPORT void opn2_rt_bankChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_SInt16 bank) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChange(channel, (uint16_t)bank); +} + +OPNMIDI_EXPORT int opn2_rt_systemExclusive(struct OPN2_MIDIPlayer *device, const OPN2_UInt8 *msg, size_t size) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->realTime_SysEx(msg, size); +} diff --git a/thirdparty/opnmidi/opnmidi.h b/thirdparty/opnmidi/opnmidi.h new file mode 100644 index 000000000..eaf1a16ce --- /dev/null +++ b/thirdparty/opnmidi/opnmidi.h @@ -0,0 +1,1259 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * OPNMIDI Library and YM2612 support: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OPNMIDI_H +#define OPNMIDI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define OPNMIDI_VERSION_MAJOR 1 +#define OPNMIDI_VERSION_MINOR 6 +#define OPNMIDI_VERSION_PATCHLEVEL 0 + +#define OPNMIDI_TOSTR_I(s) #s +#define OPNMIDI_TOSTR(s) OPNMIDI_TOSTR_I(s) +#define OPNMIDI_VERSION \ + OPNMIDI_TOSTR(OPNMIDI_VERSION_MAJOR) "." \ + OPNMIDI_TOSTR(OPNMIDI_VERSION_MINOR) "." \ + OPNMIDI_TOSTR(OPNMIDI_VERSION_PATCHLEVEL) + +#define OPN_OPN2_SAMPLE_RATE 53267 +#define OPN_OPNA_SAMPLE_RATE 55466 + +#include + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#include +typedef uint8_t OPN2_UInt8; +typedef uint16_t OPN2_UInt16; +typedef int8_t OPN2_SInt8; +typedef int16_t OPN2_SInt16; +#else +typedef unsigned char OPN2_UInt8; +typedef unsigned short OPN2_UInt16; +typedef char OPN2_SInt8; +typedef short OPN2_SInt16; +#endif + + +/* == Deprecated function markers == */ + +#if defined(_MSC_VER) /* MSVC */ +# if _MSC_VER >= 1500 /* MSVC 2008 */ + /*! Indicates that the following function is deprecated. */ +# define OPNMIDI_DEPRECATED(message) __declspec(deprecated(message)) +# endif +#endif /* defined(_MSC_VER) */ + +#ifdef __clang__ +# if __has_extension(attribute_deprecated_with_message) +# define OPNMIDI_DEPRECATED(message) __attribute__((deprecated(message))) +# endif +#elif defined __GNUC__ /* not clang (gcc comes later since clang emulates gcc) */ +# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +# define OPNMIDI_DEPRECATED(message) __attribute__((deprecated(message))) +# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +# define OPNMIDI_DEPRECATED(message) __attribute__((__deprecated__)) +# endif /* GNUC version */ +#endif /* __clang__ || __GNUC__ */ + +#if !defined(OPNMIDI_DEPRECATED) +# define OPNMIDI_DEPRECATED(message) +#endif /* if !defined(OPNMIDI_DEPRECATED) */ + + +#ifdef OPNMIDI_BUILD +# ifndef OPNMIDI_DECLSPEC +# if defined (_WIN32) && defined(OPNMIDI_BUILD_DLL) +# define OPNMIDI_DECLSPEC __declspec(dllexport) +# else +# define OPNMIDI_DECLSPEC +# endif +# endif +#else +# define OPNMIDI_DECLSPEC +#endif + +/** + * @brief Chip types + */ +enum OPNMIDI_ChipType +{ + /*! The Yamaha OPN2, alias YM2612 YM3438 */ + OPNMIDI_ChipType_OPN2 = 0, + /*! The Yamaha OPNA, alias YM2608 */ + OPNMIDI_ChipType_OPNA +}; + +/** + * @brief Volume scaling models + */ +enum OPNMIDI_VolumeModels +{ + /*! Automatical choice by the specific bank */ + OPNMIDI_VolumeModel_AUTO = 0, + /*! Linearized scaling model, most standard */ + OPNMIDI_VolumeModel_Generic, + /*! Native OPN2's logarithmic volume scale */ + OPNMIDI_VolumeModel_NativeOPN2, + /*! Logarithmic volume scale, using volume map table. Used in DMX. */ + OPNMIDI_VolumeModel_DMX, + /*! Logarithmic volume scale, used in Apogee Sound System. */ + OPNMIDI_VolumeModel_APOGEE, + /*! Aproximated and shorted volume map table. Similar to general, but has less granularity. */ + OPNMIDI_VolumeModel_9X, + /*! Count of available volume model modes */ + OPNMIDI_VolumeModel_Count +}; + +/*! + * \brief Algorithms of channel allocation for new notes + */ +enum OPNMIDI_ChannelAlloc +{ + /*! Automatical choise of the method according to the volume model and internal preferrences */ + OPNMIDI_ChanAlloc_AUTO = -1, + /*! Take only channels that has expired sounding delay */ + OPNMIDI_ChanAlloc_OffDelay, + /*! Take any first released channel with the same instrument */ + OPNMIDI_ChanAlloc_SameInst, + /*! Take any first released channel */ + OPNMIDI_ChanAlloc_AnyReleased, + /*! Count of available channel allocation modes */ + OPNMIDI_ChanAlloc_Count +}; + +/** + * @brief Sound output format + */ +enum OPNMIDI_SampleType +{ + /*! signed PCM 16-bit */ + OPNMIDI_SampleType_S16 = 0, + /*! signed PCM 8-bit */ + OPNMIDI_SampleType_S8, + /*! float 32-bit */ + OPNMIDI_SampleType_F32, + /*! float 64-bit */ + OPNMIDI_SampleType_F64, + /*! signed PCM 24-bit */ + OPNMIDI_SampleType_S24, + /*! signed PCM 32-bit */ + OPNMIDI_SampleType_S32, + /*! unsigned PCM 8-bit */ + OPNMIDI_SampleType_U8, + /*! unsigned PCM 16-bit */ + OPNMIDI_SampleType_U16, + /*! unsigned PCM 24-bit */ + OPNMIDI_SampleType_U24, + /*! unsigned PCM 32-bit */ + OPNMIDI_SampleType_U32, + /*! Count of available sample format types */ + OPNMIDI_SampleType_Count +}; + +/** + * @brief Sound output format context + */ +struct OPNMIDI_AudioFormat +{ + /*! type of sample */ + enum OPNMIDI_SampleType type; + /*! size in bytes of the storage type */ + unsigned containerSize; + /*! distance in bytes between consecutive samples */ + unsigned sampleOffset; +}; + +/** + * @brief Instance of the library + */ +struct OPN2_MIDIPlayer +{ + /*! Private context descriptor */ + void *opn2_midiPlayer; +}; + +/* DEPRECATED */ +#define opn2_setNumCards opn2_setNumChips + +/** + * @brief Sets number of emulated chips (from 1 to 100). Emulation of multiple chips extends polyphony limits + * @param device Instance of the library + * @param numChips Count of virtual chips to emulate + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_setNumChips(struct OPN2_MIDIPlayer *device, int numChips); + +/** + * @brief Get current number of emulated chips + * @param device Instance of the library + * @return Count of working chip emulators + */ +extern OPNMIDI_DECLSPEC int opn2_getNumChips(struct OPN2_MIDIPlayer *device); + +/** + * @brief Get obtained number of emulated chips + * @param device Instance of the library + * @return Count of working chip emulators + */ +extern OPNMIDI_DECLSPEC int opn2_getNumChipsObtained(struct OPN2_MIDIPlayer *device); + +/** + * @brief Reference to dynamic bank + */ +typedef struct OPN2_Bank +{ + void *pointer[3]; +} OPN2_Bank; + +/** + * @brief Identifier of dynamic bank + */ +typedef struct OPN2_BankId +{ + /*! 0 if bank is melodic set, or 1 if bank is a percussion set */ + OPN2_UInt8 percussive; + /*! Assign to MSB bank number */ + OPN2_UInt8 msb; + /*! Assign to LSB bank number */ + OPN2_UInt8 lsb; +} OPN2_BankId; + +/** + * @brief Flags for dynamic bank access + */ +enum OPN2_BankAccessFlags +{ + /*! create bank, allocating memory as needed */ + OPNMIDI_Bank_Create = 1, + /*! create bank, never allocating memory */ + OPNMIDI_Bank_CreateRt = 1|2 +}; + + + +/* ======== Instrument structures ======== */ + +/** + * @brief Version of the instrument data format + */ +enum +{ + OPNMIDI_InstrumentVersion = 0 +}; + +/** + * @brief Instrument flags + */ +typedef enum OPN2_InstrumentFlags +{ + OPNMIDI_Ins_Pseudo8op = 0x01, /*Reserved for future use, not implemented yet*/ + OPNMIDI_Ins_IsBlank = 0x02 +} OPN2_InstrumentFlags; + +/** + * @brief Operator structure, part of Instrument structure + */ +typedef struct OPN2_Operator +{ + /* Detune and frequency multiplication register data */ + OPN2_UInt8 dtfm_30; + /* Total level register data */ + OPN2_UInt8 level_40; + /* Rate scale and attack register data */ + OPN2_UInt8 rsatk_50; + /* Amplitude modulation enable and Decay-1 register data */ + OPN2_UInt8 amdecay1_60; + /* Decay-2 register data */ + OPN2_UInt8 decay2_70; + /* Sustain and Release register data */ + OPN2_UInt8 susrel_80; + /* SSG-EG register data */ + OPN2_UInt8 ssgeg_90; +} OPN2_Operator; + +/** + * @brief Instrument structure + */ +typedef struct OPN2_Instrument +{ + /*! Version of the instrument object */ + int version; + /* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + OPN2_SInt16 note_offset; + /* Reserved */ + OPN2_SInt8 midi_velocity_offset; + /* Percussion MIDI base tone number at which this drum will be played */ + OPN2_UInt8 percussion_key_number; + /* Instrument flags */ + OPN2_UInt8 inst_flags; + /* Feedback and Algorithm register data */ + OPN2_UInt8 fbalg; + /* LFO Sensitivity register data */ + OPN2_UInt8 lfosens; + /* Operators register data */ + OPN2_Operator operators[4]; + /* Millisecond delay of sounding while key is on */ + OPN2_UInt16 delay_on_ms; + /* Millisecond delay of sounding after key off */ + OPN2_UInt16 delay_off_ms; +} OPN2_Instrument; + + + + +/* ======== Setup ======== */ + +#ifdef OPNMIDI_UNSTABLE_API + +/** + * @brief Preallocates a minimum number of bank slots. Returns the actual capacity + * @param device Instance of the library + * @param banks Count of bank slots to pre-allocate. + * @return actual capacity of reserved bank slots. + */ +extern OPNMIDI_DECLSPEC int opn2_reserveBanks(struct OPN2_MIDIPlayer *device, unsigned banks); +/** + * @brief Gets the bank designated by the identifier, optionally creating if it does not exist + * @param device Instance of the library + * @param id Identifier of dynamic bank + * @param flags Flags for dynamic bank access (OPN2_BankAccessFlags) + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_getBank(struct OPN2_MIDIPlayer *device, const OPN2_BankId *id, int flags, OPN2_Bank *bank); +/** + * @brief Gets the identifier of a bank + * @param device Instance of the library + * @param bank Reference to dynamic bank. + * @param id Identifier of dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_getBankId(struct OPN2_MIDIPlayer *device, const OPN2_Bank *bank, OPN2_BankId *id); +/** + * @brief Removes a bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_removeBank(struct OPN2_MIDIPlayer *device, OPN2_Bank *bank); +/** + * @brief Gets the first bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_getFirstBank(struct OPN2_MIDIPlayer *device, OPN2_Bank *bank); +/** + * @brief Iterates to the next bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred or end has been reached. + */ +extern OPNMIDI_DECLSPEC int opn2_getNextBank(struct OPN2_MIDIPlayer *device, OPN2_Bank *bank); +/** + * @brief Gets the nth intrument in the bank [0..127] + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param index Index of the instrument + * @param ins Instrument entry + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_getInstrument(struct OPN2_MIDIPlayer *device, const OPN2_Bank *bank, unsigned index, OPN2_Instrument *ins); +/** + * @brief Sets the nth intrument in the bank [0..127] + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param index Index of the instrument + * @param ins Instrument structure pointer + * @return 0 on success, <0 when any error has occurred + * + * This function allows to override an instrument on the fly + */ +extern OPNMIDI_DECLSPEC int opn2_setInstrument(struct OPN2_MIDIPlayer *device, OPN2_Bank *bank, unsigned index, const OPN2_Instrument *ins); + +#endif /* OPNMIDI_UNSTABLE_API */ + + + +/*Override Enable(1) or Disable(0) LFO. -1 - use bank default state*/ +extern OPNMIDI_DECLSPEC void opn2_setLfoEnabled(struct OPN2_MIDIPlayer *device, int lfoEnable); + +/*Get the LFO state*/ +extern OPNMIDI_DECLSPEC int opn2_getLfoEnabled(struct OPN2_MIDIPlayer *device); + +/*Override LFO frequency. -1 - use bank default state*/ +extern OPNMIDI_DECLSPEC void opn2_setLfoFrequency(struct OPN2_MIDIPlayer *device, int lfoFrequency); + +/*Get the LFO frequency*/ +extern OPNMIDI_DECLSPEC int opn2_getLfoFrequency(struct OPN2_MIDIPlayer *device); + +/*Override chip type. -1 - use bank default state*/ +extern OPNMIDI_DECLSPEC void opn2_setChipType(struct OPN2_MIDIPlayer *device, int chipType); + +/*Get the chip type*/ +extern OPNMIDI_DECLSPEC int opn2_getChipType(struct OPN2_MIDIPlayer *device); + +/** + * @brief Override Enable(1) or Disable(0) scaling of modulator volumes. -1 - use bank default scaling of modulator volumes + * @param device Instance of the library + * @param smod 0 - disabled, 1 - enabled + */ +extern OPNMIDI_DECLSPEC void opn2_setScaleModulators(struct OPN2_MIDIPlayer *device, int smod); + +/** + * @brief Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling + * + * By default, brightness affects sound between 0 and 64. + * When this option is enabled, the brightness will use full range from 0 up to 127. + * + * @param device Instance of the library + * @param fr_brightness 0 - disabled, 1 - enabled + */ +extern OPNMIDI_DECLSPEC void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device, int fr_brightness); + +/** + * @brief Enable(1) or Disable(0) the automatical arpeggio system + * + * @param device Instance of the library + * @param aaEn 0 - disabled, 1 - enabled + */ +extern OPNMIDI_DECLSPEC void opn2_setAutoArpeggio(struct OPN2_MIDIPlayer *device, int aaEn); + +/** + * @brief Get the state of the automatical arpeggio system enable state + * @param device Instalce of the library + * @return 0 - disabled, 1 - enabled + */ +extern OPNMIDI_DECLSPEC int opn2_getAutoArpeggio(struct OPN2_MIDIPlayer *device); + +/** + * @brief Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part) + * @param device Instance of the library + * @param loopEn 0 - disabled, 1 - enabled + */ +extern OPNMIDI_DECLSPEC void opn2_setLoopEnabled(struct OPN2_MIDIPlayer *device, int loopEn); + +/** + * @brief Set how many times loop will be played + * + * Note: The song will be played once if loop has been disabled with no matter which value of loop count was set + * + * @param device Instance of the library + * @param loopCount Number of loops or -1 to loop infinitely + */ +extern OPNMIDI_DECLSPEC void opn2_setLoopCount(struct OPN2_MIDIPlayer *device, int loopCount); + +/** + * @brief Make song immediately stop on reaching a loop end point + * @param device Instance of the library + * @param loopHooksOnly 0 - disabled, 1 - enabled + */ +extern OPNMIDI_DECLSPEC void opn2_setLoopHooksOnly(struct OPN2_MIDIPlayer *device, int loopHooksOnly); + +/** + * @brief Enable or disable soft panning with chip emulators + * @param device Instance of the library + * @param softPanEn 0 - disabled, 1 - enabled + */ +extern OPNMIDI_DECLSPEC void opn2_setSoftPanEnabled(struct OPN2_MIDIPlayer *device, int softPanEn); + +/** + * @brief [DEPRECATED] Enable or disable Logarithmic volume changer + * + * This function is deprecated. Suggested replacement: `opn2_setVolumeRangeModel` with `OPNMIDI_VolumeModel_NativeOPN2` volume model value; + */ +OPNMIDI_DEPRECATED("Use `opn2_setVolumeRangeModel(device, OPNMIDI_VolumeModel_NativeOPN2)` instead") +extern OPNMIDI_DECLSPEC void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, int logvol); + +/** + * @brief Set different volume range model + * @param device Instance of the library + * @param volumeModel Volume model type (#OPNMIDI_VolumeModels) + */ +extern OPNMIDI_DECLSPEC void opn2_setVolumeRangeModel(struct OPN2_MIDIPlayer *device, int volumeModel); + +/** + * @brief Get the volume range model + * @param device Instance of the library + * @return volume model on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_getVolumeRangeModel(struct OPN2_MIDIPlayer *device); + +/** + * @brief Set the channel allocation mode + * @param device Instance of the library + * @param chanalloc Channel allocation mode (#OPNMIDI_ChannelAlloc) + */ +extern OPNMIDI_DECLSPEC void opn2_setChannelAllocMode(struct OPN2_MIDIPlayer *device, int chanalloc); + +/** + * @brief Get the current channel allocation mode + * @param device Instance of the library + * @return Channel allocation mode (#OPNMIDI_ChannelAlloc) + */ +extern OPNMIDI_DECLSPEC int opn2_getChannelAllocMode(struct OPN2_MIDIPlayer *device); + +/** + * @brief Load WOPN bank file from File System + * + * Is recommended to call opn2_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param filePath Absolute or relative path to the WOPL bank file. UTF8 encoding is required, even on Windows. + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_openBankFile(struct OPN2_MIDIPlayer *device, const char *filePath); + +/** + * @brief Load WOPN bank file from memory data + * + * Is recommended to call opn2_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param mem Pointer to memory block where is raw data of WOPL bank file is stored + * @param size Size of given memory block + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_openBankData(struct OPN2_MIDIPlayer *device, const void *mem, long size); + + +/** + * @brief [DEPRECATED] Dummy function + * + * This function is deprecated. Suggested replacement: `opn2_chipEmulatorName` + * + * @return A string that contains a notice to use `opn2_chipEmulatorName` instead of this function. + */ +OPNMIDI_DEPRECATED("Use `opn2_chipEmulatorName(device)` instead") +extern OPNMIDI_DECLSPEC const char *opn2_emulatorName(); + +/** + * @brief Returns chip emulator name string + * @param device Instance of the library + * @return Understandable name of current OPN2 emulator + */ +extern OPNMIDI_DECLSPEC const char *opn2_chipEmulatorName(struct OPN2_MIDIPlayer *device); + +/** + * @brief List of available OPN2 emulators + */ +enum Opn2_Emulator +{ + /*! Mame YM2612 */ + OPNMIDI_EMU_MAME = 0, + /*! Nuked OPN2 (YM3438 mode) */ + OPNMIDI_EMU_NUKED_YM3438, + OPNMIDI_EMU_NUKED = OPNMIDI_EMU_NUKED_YM3438, + /*! GENS */ + OPNMIDI_EMU_GENS, + /*! YMFM OPN2 */ + OPNMIDI_EMU_YMFM_OPN2, + /*! Neko Project II OPNA */ + OPNMIDI_EMU_NP2, + /*! Mame YM2608 */ + OPNMIDI_EMU_MAME_2608, + /*! YMFM OPNA */ + OPNMIDI_EMU_YMFM_OPNA, + /*! VGM file dumper (required for MIDI2VGM) */ + OPNMIDI_VGM_DUMPER, + /*! Nuked OPN2 (YM2612 mode) */ + OPNMIDI_EMU_NUKED_YM2612, + /*! Count instrument on the level */ + OPNMIDI_EMU_end, + + /* Backward compatibility */ + /*! Genesis Plus GX (a fork of Mame YM2612): Somewhere broken and unstable */ + OPNMIDI_EMU_GX = OPNMIDI_EMU_YMFM_OPN2, + /*! PMDWin OPNA: Very old and very glitchy */ + OPNMIDI_EMU_PMDWIN = OPNMIDI_EMU_YMFM_OPNA +}; + +/** + * @brief Switch the emulation core + * @param device Instance of the library + * @param emulator Type of emulator (#Opn2_Emulator) + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_switchEmulator(struct OPN2_MIDIPlayer *device, int emulator); + +/** + * @brief Library version context + */ +typedef struct { + OPN2_UInt16 major; + OPN2_UInt16 minor; + OPN2_UInt16 patch; +} OPN2_Version; + +/** + * @brief Run emulator with PCM rate to reduce CPU usage on slow devices. + * + * May decrease sounding accuracy on some chip emulators. + * + * @param device Instance of the library + * @param enabled 0 - disabled, 1 - enabled + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_setRunAtPcmRate(struct OPN2_MIDIPlayer *device, int enabled); + +/** + * @brief Set 4-bit device identifier. Used by the SysEx processor. + * @param device Instance of the library + * @param id 4-bit device identifier + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_setDeviceIdentifier(struct OPN2_MIDIPlayer *device, unsigned id); + + +/** + * @section Information + */ + +/** + * @brief Returns string which contains a version number + * @return String which contains a version of the library + */ +extern OPNMIDI_DECLSPEC const char *opn2_linkedLibraryVersion(); + +/** + * @brief Returns structure which contains a version number of library + * @return Library version context structure which contains version number of the library + */ +extern OPNMIDI_DECLSPEC const OPN2_Version *opn2_linkedVersion(); + +/** + * @brief Returns string which contains last error message of initialization + * + * Don't use this function to get info on any function except of `opn2_init`! + * Use `opn2_errorInfo()` to get error information while workflow + * + * @return String with error message related to library initialization + */ +extern OPNMIDI_DECLSPEC const char *opn2_errorString(); + +/** + * @brief Returns string which contains last error message on specific device + * @param device Instance of the library + * @return String with error message related to last function call returned non-zero value. + */ +extern OPNMIDI_DECLSPEC const char *opn2_errorInfo(struct OPN2_MIDIPlayer *device); + + +/* ======== Initialization ======== */ + +/** + * @brief Initialize OPNMIDI Player device + * + * Tip 1: You can initialize multiple instances and run them in parallel + * Tip 2: Library is NOT thread-safe, therefore don't use same instance in different threads or use mutexes + * Tip 3: Changing of sample rate on the fly is not supported. Re-create the instance again. + * Top 4: To generate output in OPN2 or OPNA chip native sample rate, please initialize it with sample rate + * value as `OPN_OPN2_SAMPLE_RATE` or `OPN_OPNA_SAMPLE_RATE` in dependence on the chip + * + * @param sample_rate Output sample rate + * @return Instance of the library. If NULL was returned, check the `opn2_errorString` message for more info. + */ +extern OPNMIDI_DECLSPEC struct OPN2_MIDIPlayer *opn2_init(long sample_rate); + +/** + * @brief Close and delete OPNMIDI device + * @param device Instance of the library + */ +extern OPNMIDI_DECLSPEC void opn2_close(struct OPN2_MIDIPlayer *device); + + +/* ======== MIDI Sequencer ======== */ + +/** + * @brief Load MIDI (or any other supported format) file from File System + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param filePath Absolute or relative path to the music file. UTF8 encoding is required, even on Windows. + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_openFile(struct OPN2_MIDIPlayer *device, const char *filePath); + +/** + * @brief Load MIDI (or any other supported format) file from memory data + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param mem Pointer to memory block where is raw data of music file is stored + * @param size Size of given memory block + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_openData(struct OPN2_MIDIPlayer *device, const void *mem, unsigned long size); + +/** + * @brief Switch another song if multi-song file is playing (for example, XMI) + * + * Note: to set the initial song to load, you should call this function + * BBEFORE calling `opn2_openFile` or `opn2_openData`. When loaded file has more than + * one built-in songs (Usually XMIformat), it will be started from the selected number. + * You may call this function to switch another song. + * + * @param device Instance of the library + * @param songNumber Identifier of the track to load (or -1 to mix all tracks as one song) + * @return + */ +extern OPNMIDI_DECLSPEC void opn2_selectSongNum(struct OPN2_MIDIPlayer *device, int songNumber); + +/** + * @brief Retrive the number of songs in a currently opened file + * @param device Instance of the library + * @return Number of songs in the file. If 1 or less, means, the file has only one song inside. + */ +extern OPNMIDI_DECLSPEC int opn2_getSongsCount(struct OPN2_MIDIPlayer *device); + +/** + * @brief Resets MIDI player (per-channel setup) into initial state + * @param device Instance of the library + */ +extern OPNMIDI_DECLSPEC void opn2_reset(struct OPN2_MIDIPlayer *device); + +/** + * @brief Get total time length of current song + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Total song length in seconds + */ +extern OPNMIDI_DECLSPEC double opn2_totalTimeLength(struct OPN2_MIDIPlayer *device); + +/** + * @brief Get loop start time if presented. + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Time position in seconds of loop start point, or -1 when file has no loop points + */ +extern OPNMIDI_DECLSPEC double opn2_loopStartTime(struct OPN2_MIDIPlayer *device); + +/** + * @brief Get loop endtime if presented. + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Time position in seconds of loop end point, or -1 when file has no loop points + */ +extern OPNMIDI_DECLSPEC double opn2_loopEndTime(struct OPN2_MIDIPlayer *device); + +/** + * @brief Get current time position in seconds + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Current time position in seconds + */ +extern OPNMIDI_DECLSPEC double opn2_positionTell(struct OPN2_MIDIPlayer *device); + +/** + * @brief Jump to absolute time position in seconds + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param seconds Destination time position in seconds to seek + */ +extern OPNMIDI_DECLSPEC void opn2_positionSeek(struct OPN2_MIDIPlayer *device, double seconds); + +/** + * @brief Reset MIDI track position to begin + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + */ +extern OPNMIDI_DECLSPEC void opn2_positionRewind(struct OPN2_MIDIPlayer *device); + +/** + * @brief Set tempo multiplier + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param tempo Tempo multiplier value: 1.0 - original tempo, >1 - play faster, <1 - play slower + */ +extern OPNMIDI_DECLSPEC void opn2_setTempo(struct OPN2_MIDIPlayer *device, double tempo); + +/** + * @brief Returns 1 if music position has reached end + * @param device Instance of the library + * @return 1 when end of sing has been reached, otherwise, 0 will be returned. <0 is returned on any error + */ +extern OPNMIDI_DECLSPEC int opn2_atEnd(struct OPN2_MIDIPlayer *device); + +/** + * @brief Returns the number of tracks of the current sequence + * @param device Instance of the library + * @return Count of tracks in the current sequence + */ +extern OPNMIDI_DECLSPEC size_t opn2_trackCount(struct OPN2_MIDIPlayer *device); + + + +/* ======== Meta-Tags ======== */ + +/** + * @brief Returns string which contains a music title + * @param device Instance of the library + * @return A string that contains music title + */ +extern OPNMIDI_DECLSPEC const char *opn2_metaMusicTitle(struct OPN2_MIDIPlayer *device); + +/** + * @brief Returns string which contains a copyright string* + * @param device Instance of the library + * @return A string that contains copyright notice, otherwise NULL + */ +extern OPNMIDI_DECLSPEC const char *opn2_metaMusicCopyright(struct OPN2_MIDIPlayer *device); + +/** + * @brief Returns count of available track titles + * + * NOTE: There are CAN'T be associated with channel in any of event or note hooks + * + * @param device Instance of the library + * @return Count of available MIDI tracks, otherwise NULL + */ +extern OPNMIDI_DECLSPEC size_t opn2_metaTrackTitleCount(struct OPN2_MIDIPlayer *device); + +/** + * @brief Get track title by index + * @param device Instance of the library + * @param index Index of the track to retreive the title + * @return A string that contains track title, otherwise NULL. + */ +extern OPNMIDI_DECLSPEC const char *opn2_metaTrackTitle(struct OPN2_MIDIPlayer *device, size_t index); + +/** + * @brief MIDI Marker structure + */ +struct Opn2_MarkerEntry +{ + /*! MIDI Marker title */ + const char *label; + /*! Absolute time position of the marker in seconds */ + double pos_time; + /*! Absolute time position of the marker in MIDI ticks */ + unsigned long pos_ticks; +}; + +/** + * @brief Returns count of available markers + * @param device Instance of the library + * @return Count of available MIDI markers + */ +extern OPNMIDI_DECLSPEC size_t opn2_metaMarkerCount(struct OPN2_MIDIPlayer *device); + +/** + * @brief Returns the marker entry + * @param device Instance of the library + * @param index Index of the marker to retreive it. + * @return MIDI Marker description structure. + */ +extern OPNMIDI_DECLSPEC struct Opn2_MarkerEntry opn2_metaMarker(struct OPN2_MIDIPlayer *device, size_t index); + + + + +/* ======== Audio output Generation ======== */ + +/** + * @brief Generate PCM signed 16-bit stereo audio output and iterate MIDI timers + * + * Use this function when you are playing MIDI file loaded by `opn2_openFile` or by `opn2_openData` + * with using of built-in MIDI sequencer. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param sampleCount Count of samples (not frames!) + * @param out Pointer to output with 16-bit stereo PCM output + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern OPNMIDI_DECLSPEC int opn2_play(struct OPN2_MIDIPlayer *device, int sampleCount, short *out); + +/** + * @brief Generate PCM stereo audio output in sample format declared by given context and iterate MIDI timers + * + * Use this function when you are playing MIDI file loaded by `opn2_openFile` or by `opn2_openData` + * with using of built-in MIDI sequencer. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param sampleCount Count of samples (not frames!) + * @param left Left channel buffer output (Must be casted into bytes array) + * @param right Right channel buffer output (Must be casted into bytes array) + * @param format Destination PCM format format context + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern OPNMIDI_DECLSPEC int opn2_playFormat(struct OPN2_MIDIPlayer *device, int sampleCount, OPN2_UInt8 *left, OPN2_UInt8 *right, const struct OPNMIDI_AudioFormat *format); + +/** + * @brief Generate PCM signed 16-bit stereo audio output without iteration of MIDI timers + * + * Use this function when you are using library as Real-Time MIDI synthesizer or with + * an external MIDI sequencer. You must to request the amount of samples which is equal + * to the delta between of MIDI event rows. One MIDI row is a group of MIDI events + * are having zero delta/delay between each other. When you are receiving events in + * real time, request the minimal possible delay value. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * @param device Instance of the library + * @param sampleCount + * @param out Pointer to output with 16-bit stereo PCM output + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern OPNMIDI_DECLSPEC int opn2_generate(struct OPN2_MIDIPlayer *device, int sampleCount, short *out); + +/** + * @brief Generate PCM stereo audio output in sample format declared by given context without iteration of MIDI timers + * + * Use this function when you are using library as Real-Time MIDI synthesizer or with + * an external MIDI sequencer. You must to request the amount of samples which is equal + * to the delta between of MIDI event rows. One MIDI row is a group of MIDI events + * are having zero delta/delay between each other. When you are receiving events in + * real time, request the minimal possible delay value. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * @param device Instance of the library + * @param sampleCount + * @param left Left channel buffer output (Must be casted into bytes array) + * @param right Right channel buffer output (Must be casted into bytes array) + * @param format Destination PCM format format context + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern OPNMIDI_DECLSPEC int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampleCount, OPN2_UInt8 *left, OPN2_UInt8 *right, const struct OPNMIDI_AudioFormat *format); + +/** + * @brief Periodic tick handler. + * @param device + * @param seconds seconds since last call + * @param granularity don't expect intervals smaller than this, in seconds + * @return desired number of seconds until next call + * + * Use it for Hardware OPL3 mode or when you want to process events differently from opn2_play() function. + * DON'T USE IT TOGETHER WITH opn2_play()!!! + */ +extern OPNMIDI_DECLSPEC double opn2_tickEvents(struct OPN2_MIDIPlayer *device, double seconds, double granuality); + +/** + * @brief Track options + */ +enum OPNMIDI_TrackOptions +{ + /*! Enabled track */ + OPNMIDI_TrackOption_On = 1, + /*! Disabled track */ + OPNMIDI_TrackOption_Off = 2, + /*! Solo track */ + OPNMIDI_TrackOption_Solo = 3 +}; + +/** + * @brief Sets options on a track of the current sequence + * @param device Instance of the library + * @param trackNumber Identifier of the designated track. + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_setTrackOptions(struct OPN2_MIDIPlayer *device, size_t trackNumber, unsigned trackOptions); + +/** + * @brief Sets the channel of the current sequence enable state + * @param device Instance of the library + * @param channelNumber Number of the channel (from 0 to 15) + * @param enabled 1 to enable and 0 to disable + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_setChannelEnabled(struct OPN2_MIDIPlayer *device, size_t channelNumber, int enabled); + + + + +/* ======== Real-Time MIDI ======== */ + +/** + * @brief Force Off all notes on all channels + * @param device Instance of the library + */ +extern OPNMIDI_DECLSPEC void opn2_panic(struct OPN2_MIDIPlayer *device); + +/** + * @brief Reset states of all controllers on all MIDI channels + * @param device Instance of the library + */ +extern OPNMIDI_DECLSPEC void opn2_rt_resetState(struct OPN2_MIDIPlayer *device); + +/** + * @brief Turn specific MIDI note ON + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to on [Between 0 and 127] + * @param velocity Velocity level [Between 0 and 127] + * @return 1 when note was successfully started, 0 when note was rejected by any reason. + */ +extern OPNMIDI_DECLSPEC int opn2_rt_noteOn(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note, OPN2_UInt8 velocity); + +/** + * @brief Turn specific MIDI note OFF + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to off [Between 0 and 127] + */ +extern OPNMIDI_DECLSPEC void opn2_rt_noteOff(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note); + +/** + * @brief Set note after-touch + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to affect by aftertouch event [Between 0 and 127] + * @param atVal After-Touch value [Between 0 and 127] + */ +extern OPNMIDI_DECLSPEC void opn2_rt_noteAfterTouch(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note, OPN2_UInt8 atVal); + +/** + * @brief Set channel after-touch + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param atVal After-Touch level [Between 0 and 127] + */ +extern OPNMIDI_DECLSPEC void opn2_rt_channelAfterTouch(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 atVal); + +/** + * @brief Apply controller change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param type Type of the controller [Between 0 and 255] + * @param value Value of the controller event [Between 0 and 127] + */ +extern OPNMIDI_DECLSPEC void opn2_rt_controllerChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 type, OPN2_UInt8 value); + +/** + * @brief Apply patch change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param patch Patch number [Between 0 and 127] + */ +extern OPNMIDI_DECLSPEC void opn2_rt_patchChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 patch); + +/** + * @brief Apply pitch bend change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param pitch 24-bit pitch bend value + */ +extern OPNMIDI_DECLSPEC void opn2_rt_pitchBend(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt16 pitch); + +/** + * @brief Apply pitch bend change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param msb MSB part of 24-bit pitch bend value + * @param lsb LSB part of 24-bit pitch bend value + */ +extern OPNMIDI_DECLSPEC void opn2_rt_pitchBendML(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 msb, OPN2_UInt8 lsb); + +/** + * @brief Change LSB of the bank number (Alias to CC-32 event) + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param lsb LSB value of the MIDI bank number + */ +extern OPNMIDI_DECLSPEC void opn2_rt_bankChangeLSB(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 lsb); + +/** + * @brief Change MSB of the bank (Alias to CC-0 event) + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param msb MSB value of the MIDI bank number + */ +extern OPNMIDI_DECLSPEC void opn2_rt_bankChangeMSB(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 msb); + +/** + * @brief Change bank by absolute signed value + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param bank Bank number as concoctated signed 16-bit value of MSB and LSB parts. + */ +extern OPNMIDI_DECLSPEC void opn2_rt_bankChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_SInt16 bank); + +/** + * @brief Perform a system exclusive message + * @param device Instance of the library + * @param msg Raw SysEx message buffer (must begin with 0xF0 and end with 0xF7) + * @param size Size of given SysEx message buffer + * @return 1 when SysEx message was successfully processed, 0 when SysEx message was rejected by any reason + */ +extern OPNMIDI_DECLSPEC int opn2_rt_systemExclusive(struct OPN2_MIDIPlayer *device, const OPN2_UInt8 *msg, size_t size); + +/* ======== Hooks and debugging ======== */ + +/** + * @brief Raw event callback + * @param userdata Pointer to user data (usually, context of someting) + * @param type MIDI event type + * @param subtype MIDI event sub-type (special events only) + * @param channel MIDI channel + * @param data Raw event data + * @param len Length of event data + */ +typedef void (*OPN2_RawEventHook)(void *userdata, OPN2_UInt8 type, OPN2_UInt8 subtype, OPN2_UInt8 channel, const OPN2_UInt8 *data, size_t len); +/** + * @brief Note on/off callback + * @param userdata Pointer to user data (usually, context of someting) + * @param opnchn Chip channel where note was played + * @param note Note number [between 0 and 127] + * @param pressure Velocity level, or -1 when it's note off event + * @param bend Pitch bend offset value + */ +typedef void (*OPN2_NoteHook)(void *userdata, int opnchn, int note, int ins, int pressure, double bend); + +/** + * @brief Debug messages callback + * @param userdata Pointer to user data (usually, context of someting) + * @param fmt Format strign output (in context of `printf()` standard function) + */ +typedef void (*OPN2_DebugMessageHook)(void *userdata, const char *fmt, ...); + +/** + * @brief Loop start/end point reach hook + * @param userdata Pointer to user data (usually, context of someting) + */ +typedef void (*OPN2_LoopPointHook)(void *userdata); + +/** + * @brief Set raw MIDI event hook + * + * CAUTION: Don't call any libOPNMIDI API functions from off this hook directly! + * Suggestion: Use boolean variables to mark the fact this hook got been called, and then, + * apply your action outside of this hook, for example, in the next after audio output call. + * + * @param device Instance of the library + * @param rawEventHook Pointer to the callback function which will be called on every MIDI event + * @param userData Pointer to user data which will be passed through the callback. + */ +extern OPNMIDI_DECLSPEC void opn2_setRawEventHook(struct OPN2_MIDIPlayer *device, OPN2_RawEventHook rawEventHook, void *userData); + +/** + * @brief Set note hook + * + * CAUTION: Don't call any libOPNMIDI API functions from off this hook directly! + * Suggestion: Use boolean variables to mark the fact this hook got been called, and then, + * apply your action outside of this hook, for example, in the next after audio output call. + * + * @param device Instance of the library + * @param noteHook Pointer to the callback function which will be called on every noteOn MIDI event + * @param userData Pointer to user data which will be passed through the callback. + */ +extern OPNMIDI_DECLSPEC void opn2_setNoteHook(struct OPN2_MIDIPlayer *device, OPN2_NoteHook noteHook, void *userData); + +/** + * @brief Set debug message hook + * + * CAUTION: Don't call any libOPNMIDI API functions from off this hook directly! + * Suggestion: Use boolean variables to mark the fact this hook got been called, and then, + * apply your action outside of this hook, for example, in the next after audio output call. + * + * @param device Instance of the library + * @param debugMessageHook Pointer to the callback function which will be called on every debug message + * @param userData Pointer to user data which will be passed through the callback. + */ +extern OPNMIDI_DECLSPEC void opn2_setDebugMessageHook(struct OPN2_MIDIPlayer *device, OPN2_DebugMessageHook debugMessageHook, void *userData); + +/** + * @brief Set the look start point hook + * + * CAUTION: Don't call any libOPNMIDI API functions from off this hook directly! + * Suggestion: Use boolean variables to mark the fact this hook got been called, and then, + * apply your action outside of this hook, for example, in the next after audio output call. + * + * @param device Instance of the library + * @param loopStartHook Pointer to the callback function which will be called on every loop start point passing + * @param userData Pointer to user data which will be passed through the callback. + */ +extern OPNMIDI_DECLSPEC void opn2_setLoopStartHook(struct OPN2_MIDIPlayer *device, OPN2_LoopPointHook loopStartHook, void *userData); + +/** + * @brief Set the look start point hook + * + * CAUTION: Don't call any libOPNMIDI API functions from off this hook directly! + * Suggestion: Use boolean variables to mark the fact this hook got been called, and then, + * apply your action outside of this hook, for example, in the next after audio output call. + * + * If you want to switch the song after calling this hook, suggested to call the function + * opn2_setLoopHooksOnly(device, 1) to immediately stop the song on reaching the loop point + * + * @param device Instance of the library + * @param loopStartHook Pointer to the callback function which will be called on every loop start point passing + * @param userData Pointer to user data which will be passed through the callback. + */ +extern OPNMIDI_DECLSPEC void opn2_setLoopEndHook(struct OPN2_MIDIPlayer *device, OPN2_LoopPointHook loopEndHook, void *userData); + +/** + * @brief Get a textual description of the channel state. For display only. + * @param device Instance of the library + * @param text Destination char buffer for channel usage state. Every entry is assigned to the chip channel. + * @param attr Destination char buffer for additional attributes like MIDI channel number that uses this chip channel. + * @param size Size of given buffers (both text and attr are must have same size!) + * @return 0 on success, <0 when any error has occurred + * + * Every character in the `text` buffer means the type of usage: + * ``` + * `-` - channel is unused (free) + * `+` - channel is used by regular voice + * `@` - channel is used to play automatic arpeggio on chip channels overflow + * ``` + * + * The `attr` field receives the MIDI channel from which the chip channel is used. + * To get the valid MIDI channel you will need to apply the & 0x0F mask to every value. + */ +extern OPNMIDI_DECLSPEC int opn2_describeChannels(struct OPN2_MIDIPlayer *device, char *text, char *attr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* OPNMIDI_H */ diff --git a/thirdparty/opnmidi/opnmidi_bankmap.h b/thirdparty/opnmidi/opnmidi_bankmap.h new file mode 100644 index 000000000..cd28da52d --- /dev/null +++ b/thirdparty/opnmidi/opnmidi_bankmap.h @@ -0,0 +1,127 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OPNMIDI_BANKMAP_H +#define OPNMIDI_BANKMAP_H + +#include +#include +#include +#include + +#include "opnmidi_ptr.hpp" + +/** + * A simple hash map which accepts bank numbers as keys, can be reserved to a + * fixed size, offers O(1) search and insertion, has a hash function to + * optimize for the worst case, and has some good cache locality properties. + */ +template +class BasicBankMap +{ +public: + typedef size_t key_type; /* the bank identifier */ + typedef T mapped_type; + typedef std::pair value_type; + + BasicBankMap(); + void reserve(size_t capacity); + + size_t size() const + { return m_size; } + size_t capacity() const + { return m_capacity; } + bool empty() const + { return m_size == 0; } + + class iterator; + iterator begin() const; + iterator end() const; + + struct do_not_expand_t {}; + + iterator find(key_type key); + void erase(iterator it); + std::pair insert(const value_type &value); + std::pair insert(const value_type &value, do_not_expand_t); + void clear(); + + T &operator[](key_type key); + +private: + struct Slot; + enum { minimum_allocation = 4 }; + enum + { + hash_bits = 8, /* worst case # of collisions: 128^2/2^hash_bits */ + hash_buckets = 1 << hash_bits + }; + +public: + class iterator + { + public: + iterator(); + value_type &operator*() const { return slot->value; } + value_type *operator->() const { return &slot->value; } + iterator &operator++(); + bool operator==(const iterator &o) const; + bool operator!=(const iterator &o) const; + void to_ptrs(void *ptrs[3]); + static iterator from_ptrs(void *const ptrs[3]); + private: + Slot **buckets; + Slot *slot; + size_t index; + iterator(Slot **buckets, Slot *slot, size_t index); +#ifdef _MSC_VER + template + friend class BasicBankMap; +#else + friend class BasicBankMap; +#endif + }; + +private: + struct Slot { + Slot *next, *prev; + value_type value; + Slot() : next(NULL), prev(NULL) {} + }; + AdlMIDI_SPtrArray m_buckets; + std::list< AdlMIDI_SPtrArray > m_allocations; + Slot *m_freeslots; + size_t m_size; + size_t m_capacity; + static size_t hash(key_type key); + Slot *allocate_slot(); + Slot *ensure_allocate_slot(); + void free_slot(Slot *slot); + Slot *bucket_find(size_t index, key_type key); + void bucket_add(size_t index, Slot *slot); + void bucket_remove(size_t index, Slot *slot); +}; + +#include "opnmidi_bankmap.tcc" + +#endif // OPNMIDI_BANKMAP_H diff --git a/thirdparty/opnmidi/opnmidi_bankmap.tcc b/thirdparty/opnmidi/opnmidi_bankmap.tcc new file mode 100644 index 000000000..3068d0ac7 --- /dev/null +++ b/thirdparty/opnmidi/opnmidi_bankmap.tcc @@ -0,0 +1,283 @@ +/* + * libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "opnmidi_bankmap.h" +#include + +template +inline BasicBankMap::BasicBankMap() + : m_freeslots(NULL), + m_size(0), + m_capacity(0) +{ + m_buckets.reset(new Slot *[hash_buckets]()); +} + +template +inline size_t BasicBankMap::hash(key_type key) +{ + // disregard the 0 high bit in LSB + key = (key & 127) | ((key >> 8) << 7); + // take low part as hash value + return key & (hash_buckets - 1); +} + +template +void BasicBankMap::reserve(size_t capacity) +{ + if(m_capacity >= capacity) + return; + + size_t need = capacity - m_capacity; + const size_t minalloc = static_cast(minimum_allocation); + need = (need < minalloc) ? minalloc : need; + + AdlMIDI_SPtrArray slotz; + slotz.reset(new Slot[need]); + m_allocations.push_back(slotz); + m_capacity += need; + + for(size_t i = need; i-- > 0;) + free_slot(&slotz[i]); +} + +template +typename BasicBankMap::iterator +BasicBankMap::begin() const +{ + iterator it(m_buckets.get(), NULL, 0); + while(it.index < hash_buckets && !(it.slot = m_buckets[it.index])) + ++it.index; + return it; +} + +template +typename BasicBankMap::iterator +BasicBankMap::end() const +{ + iterator it(m_buckets.get(), NULL, hash_buckets); + return it; +} + +template +typename BasicBankMap::iterator BasicBankMap::find(key_type key) +{ + size_t index = hash(key); + Slot *slot = bucket_find(index, key); + if(!slot) + return end(); + return iterator(m_buckets.get(), slot, index); +} + +template +void BasicBankMap::erase(iterator it) +{ + bucket_remove(it.index, it.slot); + free_slot(it.slot); + --m_size; +} + +template +inline BasicBankMap::iterator::iterator() + : buckets(NULL), slot(NULL), index(0) +{ +} + +template +inline BasicBankMap::iterator::iterator(Slot **buckets, Slot *slot, size_t index) + : buckets(buckets), slot(slot), index(index) +{ +} + +template +typename BasicBankMap::iterator & +BasicBankMap::iterator::operator++() +{ + if(slot->next) + slot = slot->next; + else { + Slot *slot = NULL; + ++index; + while(index < hash_buckets && !(slot = buckets[index])) + ++index; + this->slot = slot; + } + return *this; +} + +template +bool BasicBankMap::iterator::operator==(const iterator &o) const +{ + return buckets == o.buckets && slot == o.slot && index == o.index; +} + +template +inline bool BasicBankMap::iterator::operator!=(const iterator &o) const +{ + return !operator==(o); +} + +template +void BasicBankMap::iterator::to_ptrs(void *ptrs[3]) +{ + ptrs[0] = buckets; + ptrs[1] = slot; + ptrs[2] = (void *)index; +} + +template +typename BasicBankMap::iterator +BasicBankMap::iterator::from_ptrs(void *const ptrs[3]) +{ + iterator it; + it.buckets = (Slot **)ptrs[0]; + it.slot = (Slot *)ptrs[1]; + it.index = (size_t)ptrs[2]; + return it; +} + +template +std::pair::iterator, bool> +BasicBankMap::insert(const value_type &value) +{ + size_t index = hash(value.first); + Slot *slot = bucket_find(index, value.first); + if(slot) + return std::make_pair(iterator(m_buckets.get(), slot, index), false); + slot = allocate_slot(); + if(!slot) { + reserve(m_capacity + minimum_allocation); + slot = ensure_allocate_slot(); + } + slot->value = value; + bucket_add(index, slot); + ++m_size; + return std::make_pair(iterator(m_buckets.get(), slot, index), true); +} + +template +std::pair::iterator, bool> +BasicBankMap::insert(const value_type &value, do_not_expand_t) +{ + size_t index = hash(value.first); + Slot *slot = bucket_find(index, value.first); + if(slot) + return std::make_pair(iterator(m_buckets.get(), slot, index), false); + slot = allocate_slot(); + if(!slot) + return std::make_pair(end(), false); + slot->value = value; + bucket_add(index, slot); + ++m_size; + return std::make_pair(iterator(m_buckets.get(), slot, index), true); +} + +template +void BasicBankMap::clear() +{ + for(size_t i = 0; i < hash_buckets; ++i) { + Slot *slot = m_buckets[i]; + while (Slot *cur = slot) { + slot = slot->next; + free_slot(cur); + } + m_buckets[i] = NULL; + } + m_size = 0; +} + +template +inline T &BasicBankMap::operator[](key_type key) +{ + return insert(value_type(key, T())).first->second; +} + +template +typename BasicBankMap::Slot * +BasicBankMap::allocate_slot() +{ + Slot *slot = m_freeslots; + if(!slot) + return NULL; + Slot *next = slot->next; + if(next) + next->prev = NULL; + m_freeslots = next; + return slot; +} + +template +inline typename BasicBankMap::Slot * +BasicBankMap::ensure_allocate_slot() +{ + Slot *slot = allocate_slot(); + assert(slot); + return slot; +} + +template +void BasicBankMap::free_slot(Slot *slot) +{ + Slot *next = m_freeslots; + if(next) + next->prev = slot; + slot->prev = NULL; + slot->next = next; + m_freeslots = slot; + m_freeslots->value.second = T(); +} + +template +typename BasicBankMap::Slot * +BasicBankMap::bucket_find(size_t index, key_type key) +{ + Slot *slot = m_buckets[index]; + while(slot && slot->value.first != key) + slot = slot->next; + return slot; +} + +template +void BasicBankMap::bucket_add(size_t index, Slot *slot) +{ + assert(slot); + Slot *next = m_buckets[index]; + if(next) + next->prev = slot; + slot->next = next; + m_buckets[index] = slot; +} + +template +void BasicBankMap::bucket_remove(size_t index, Slot *slot) +{ + assert(slot); + Slot *prev = slot->prev; + Slot *next = slot->next; + if(!prev) + m_buckets[index] = next; + else + prev->next = next; + if(next) + next->prev = prev; +} diff --git a/thirdparty/opnmidi/opnmidi_cvt.hpp b/thirdparty/opnmidi/opnmidi_cvt.hpp new file mode 100644 index 000000000..da13c4ea1 --- /dev/null +++ b/thirdparty/opnmidi/opnmidi_cvt.hpp @@ -0,0 +1,82 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "opnbank.h" + +template +static void cvt_generic_to_FMIns(OpnInstMeta &ins, const WOPNI &in) +{ + ins.drumTone = in.percussion_key_number; + ins.flags = in.inst_flags; + /* Junk, delete later */ + ins.voice2_fine_tune = 0.0; + /* Junk, delete later */ + + ins.op[0].fbalg = in.fbalg; + ins.op[0].lfosens = in.lfosens; + ins.op[0].noteOffset = in.note_offset; + ins.midiVelocityOffset = in.midi_velocity_offset; + + for(size_t op = 0; op < 4; op++) + { + ins.op[0].OPS[op].data[0] = in.operators[op].dtfm_30; + ins.op[0].OPS[op].data[1] = in.operators[op].level_40; + ins.op[0].OPS[op].data[2] = in.operators[op].rsatk_50; + ins.op[0].OPS[op].data[3] = in.operators[op].amdecay1_60; + ins.op[0].OPS[op].data[4] = in.operators[op].decay2_70; + ins.op[0].OPS[op].data[5] = in.operators[op].susrel_80; + ins.op[0].OPS[op].data[6] = in.operators[op].ssgeg_90; + } + + ins.op[1] = ins.op[0]; + + ins.soundKeyOnMs = in.delay_on_ms; + ins.soundKeyOffMs = in.delay_off_ms; +} + +template +static void cvt_FMIns_to_generic(WOPNI &ins, const OpnInstMeta &in) +{ + ins.percussion_key_number = in.drumTone; + ins.inst_flags = in.flags; + + ins.fbalg = in.op[0].fbalg; + ins.lfosens = in.op[0].lfosens; + ins.note_offset = in.op[0].noteOffset; + + ins.midi_velocity_offset = in.midiVelocityOffset; + + for(size_t op = 0; op < 4; op++) + { + ins.operators[op].dtfm_30 = in.op[0].OPS[op].data[0]; + ins.operators[op].level_40 = in.op[0].OPS[op].data[1]; + ins.operators[op].rsatk_50 = in.op[0].OPS[op].data[2]; + ins.operators[op].amdecay1_60 = in.op[0].OPS[op].data[3]; + ins.operators[op].decay2_70 = in.op[0].OPS[op].data[4]; + ins.operators[op].susrel_80 = in.op[0].OPS[op].data[5]; + ins.operators[op].ssgeg_90 = in.op[0].OPS[op].data[6]; + } + + ins.delay_on_ms = in.soundKeyOnMs; + ins.delay_off_ms = in.soundKeyOffMs; +} diff --git a/thirdparty/opnmidi/opnmidi_load.cpp b/thirdparty/opnmidi/opnmidi_load.cpp new file mode 100644 index 000000000..9373fd8f4 --- /dev/null +++ b/thirdparty/opnmidi/opnmidi_load.cpp @@ -0,0 +1,250 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * OPNMIDI Library and YM2612 support: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "opnmidi_midiplay.hpp" +#include "opnmidi_opn2.hpp" +#include "opnmidi_private.hpp" +#include "opnmidi_cvt.hpp" +#include "file_reader.hpp" +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER +#include "midi_sequencer.hpp" +#endif +#include "wopn/wopn_file.h" + +bool OPNMIDIplay::LoadBank(const std::string &filename) +{ + FileAndMemReader file; + file.openFile(filename.c_str()); + return LoadBank(file); +} + +bool OPNMIDIplay::LoadBank(const void *data, size_t size) +{ + FileAndMemReader file; + file.openData(data, (size_t)size); + return LoadBank(file); +} + +void cvt_OPNI_to_FMIns(OpnInstMeta &ins, const OPN2_Instrument &in) +{ + return cvt_generic_to_FMIns(ins, in); +} + +void cvt_FMIns_to_OPNI(OPN2_Instrument &ins, const OpnInstMeta &in) +{ + cvt_FMIns_to_generic(ins, in); +} + +bool OPNMIDIplay::LoadBank(FileAndMemReader &fr) +{ + int err = 0; + WOPNFile *wopn = NULL; + char *raw_file_data = NULL; + size_t fsize; + if(!fr.isValid()) + { + errorStringOut = "Custom bank: Invalid data stream!"; + return false; + } + + // Read complete bank file into the memory + fsize = fr.fileSize(); + fr.seek(0, FileAndMemReader::SET); + // Allocate necessary memory block + raw_file_data = (char*)malloc(fsize); + if(!raw_file_data) + { + errorStringOut = "Custom bank: Out of memory before of read!"; + return false; + } + fr.read(raw_file_data, 1, fsize); + + // Parse bank file from the memory + wopn = WOPN_LoadBankFromMem((void*)raw_file_data, fsize, &err); + //Free the buffer no more needed + free(raw_file_data); + + // Check for any erros + if(!wopn) + { + switch(err) + { + case WOPN_ERR_BAD_MAGIC: + errorStringOut = "Custom bank: Invalid magic!"; + return false; + case WOPN_ERR_UNEXPECTED_ENDING: + errorStringOut = "Custom bank: Unexpected ending!"; + return false; + case WOPN_ERR_INVALID_BANKS_COUNT: + errorStringOut = "Custom bank: Invalid banks count!"; + return false; + case WOPN_ERR_NEWER_VERSION: + errorStringOut = "Custom bank: Version is newer than supported by this library!"; + return false; + case WOPN_ERR_OUT_OF_MEMORY: + errorStringOut = "Custom bank: Out of memory!"; + return false; + default: + errorStringOut = "Custom bank: Unknown error!"; + return false; + } + } + + Synth &synth = *m_synth; + synth.m_insBankSetup.volumeModel = wopn->volume_model; + synth.m_insBankSetup.lfoEnable = (wopn->lfo_freq & 8) != 0; + synth.m_insBankSetup.lfoFrequency = wopn->lfo_freq & 7; + synth.m_insBankSetup.chipType = wopn->chip_type; + // FIXME: Implement the bank-side flag to enable this + synth.m_insBankSetup.mt32defaults = false; + m_setup.VolumeModel = OPNMIDI_VolumeModel_AUTO; + m_setup.lfoEnable = -1; + m_setup.lfoFrequency = -1; + m_setup.chipType = -1; + + synth.m_insBanks.clear(); + + uint16_t slots_counts[2] = {wopn->banks_count_melodic, wopn->banks_count_percussion}; + WOPNBank *slots_src_ins[2] = { wopn->banks_melodic, wopn->banks_percussive }; + + for(size_t ss = 0; ss < 2; ss++) + { + for(size_t i = 0; i < slots_counts[ss]; i++) + { + size_t bankno = (slots_src_ins[ss][i].bank_midi_msb * 256) + + (slots_src_ins[ss][i].bank_midi_lsb) + + (ss ? size_t(Synth::PercussionTag) : 0); + Synth::Bank &bank = synth.m_insBanks[bankno]; + for(int j = 0; j < 128; j++) + { + OpnInstMeta &ins = bank.ins[j]; + std::memset(&ins, 0, sizeof(OpnInstMeta)); + WOPNInstrument &inIns = slots_src_ins[ss][i].ins[j]; + cvt_generic_to_FMIns(ins, inIns); + } + } + } + + applySetup(); + + WOPN_Free(wopn); + + return true; +} + +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + +bool OPNMIDIplay::LoadMIDI_pre() +{ + Synth &synth = *m_synth; + if(synth.m_insBanks.empty()) + { + errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!"; + return false; + } + + /**** Set all properties BEFORE starting of actial file reading! ****/ + resetMIDI(); + applySetup(); + + return true; +} + +bool OPNMIDIplay::LoadMIDI_post() +{ + Synth &synth = *m_synth; + MidiSequencer &seq = *m_sequencer; + MidiSequencer::FileFormat format = seq.getFormat(); + if(format == MidiSequencer::Format_CMF) + { + errorStringOut = "OPNMIDI doesn't supports CMF, use ADLMIDI to play this file!"; + /* As joke, why not to try implemented the converter of patches from OPL3 into OPN2? */ + return false; + } + else if(format == MidiSequencer::Format_RSXX) + { + synth.m_musicMode = Synth::MODE_RSXX; + synth.m_volumeScale = Synth::VOLUME_Generic; + synth.m_numChips = 2; + } + else if(format == MidiSequencer::Format_IMF) + { + errorStringOut = "OPNMIDI doesn't supports IMF, use ADLMIDI to play this file!"; + /* Same as for CMF */ + return false; + } + else if(format == MidiSequencer::Format_XMIDI) + synth.m_musicMode = Synth::MODE_XMIDI; + + m_setup.tick_skip_samples_delay = 0; + synth.reset(m_setup.emulator, m_setup.PCM_RATE, synth.chipFamily(), this); // Reset OPN2 chip + m_chipChannels.clear(); + m_chipChannels.resize(synth.m_numChannels); + resetMIDIDefaults(); +#ifdef OPNMIDI_MIDI2VGM + m_sequencerInterface->onloopStart = synth.m_loopStartHook; + m_sequencerInterface->onloopStart_userData = synth.m_loopStartHookData; + m_sequencerInterface->onloopEnd = synth.m_loopEndHook; + m_sequencerInterface->onloopEnd_userData = synth.m_loopEndHookData; + m_sequencer->setLoopHooksOnly(m_sequencerInterface->onloopStart != NULL); +#endif + + return true; +} + + +bool OPNMIDIplay::LoadMIDI(const std::string &filename) +{ + FileAndMemReader file; + file.openFile(filename.c_str()); + if(!LoadMIDI_pre()) + return false; + MidiSequencer &seq = *m_sequencer; + if(!seq.loadMIDI(file)) + { + errorStringOut = seq.getErrorString(); + return false; + } + if(!LoadMIDI_post()) + return false; + return true; +} + +bool OPNMIDIplay::LoadMIDI(const void *data, size_t size) +{ + FileAndMemReader file; + file.openData(data, size); + if(!LoadMIDI_pre()) + return false; + MidiSequencer &seq = *m_sequencer; + if(!seq.loadMIDI(file)) + { + errorStringOut = seq.getErrorString(); + return false; + } + if(!LoadMIDI_post()) + return false; + return true; +} + +#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER diff --git a/thirdparty/opnmidi/opnmidi_midiplay.cpp b/thirdparty/opnmidi/opnmidi_midiplay.cpp new file mode 100644 index 000000000..d53c2987f --- /dev/null +++ b/thirdparty/opnmidi/opnmidi_midiplay.cpp @@ -0,0 +1,2029 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * OPNMIDI Library and YM2612 support: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "opnmidi_midiplay.hpp" +#include "opnmidi_opn2.hpp" +#include "opnmidi_private.hpp" +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER +#include "midi_sequencer.hpp" +#endif +#include "chips/opn_chip_base.h" + +// Minimum life time of percussion notes +static const double drum_note_min_time = 0.03; + +enum { MasterVolumeDefault = 127 }; + +inline bool isXgPercChannel(uint8_t msb, uint8_t lsb) +{ + ADL_UNUSED(lsb); + return (msb == 0x7E || msb == 0x7F); +} + +void OPNMIDIplay::OpnChannel::addAge(int64_t us) +{ + const int64_t neg = 1000 * static_cast(-0x1FFFFFFFl); + if(users.empty()) + { + koff_time_until_neglible_us = std::max(koff_time_until_neglible_us - us, neg); + if(koff_time_until_neglible_us < 0) + koff_time_until_neglible_us = 0; + } + else + { + koff_time_until_neglible_us = 0; + for(users_iterator i = users.begin(); !i.is_end(); ++i) + { + LocationData &d = i->value; + if(!d.fixed_sustain) + d.kon_time_until_neglible_us = std::max(d.kon_time_until_neglible_us - us, neg); + d.vibdelay_us += us; + } + } +} + +OPNMIDIplay::OPNMIDIplay(unsigned long sampleRate) : + m_sysExDeviceId(0), + m_synthMode(Mode_XG), + m_arpeggioCounter(0) +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + , m_audioTickCounter(0) +#endif +{ + m_midiDevices.clear(); + + m_setup.emulator = opn2_getLowestEmulator(); + m_setup.runAtPcmRate = false; + + m_setup.PCM_RATE = sampleRate; + m_setup.mindelay = 1.0 / static_cast(m_setup.PCM_RATE); + m_setup.maxdelay = 512.0 / static_cast(m_setup.PCM_RATE); + + m_setup.OpnBank = 0; + m_setup.numChips = 2; + m_setup.LogarithmicVolumes = false; + m_setup.VolumeModel = OPNMIDI_VolumeModel_AUTO; + m_setup.lfoEnable = -1; + m_setup.lfoFrequency = -1; + m_setup.chipType = -1; + //m_setup.SkipForward = 0; + m_setup.ScaleModulators = 0; + m_setup.fullRangeBrightnessCC74 = false; + m_setup.enableAutoArpeggio = false; + m_setup.delay = 0.0; + m_setup.carry = 0.0; + m_setup.tick_skip_samples_delay = 0; + + m_synth.reset(new Synth); + +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + m_sequencer.reset(new MidiSequencer); + initSequencerInterface(); +#endif + resetMIDI(); + applySetup(); + realTime_ResetState(); +} + +OPNMIDIplay::~OPNMIDIplay() +{ +} + +void OPNMIDIplay::applySetup() +{ + Synth &synth = *m_synth; + + synth.m_musicMode = Synth::MODE_MIDI; + + m_setup.tick_skip_samples_delay = 0; + + synth.m_runAtPcmRate = m_setup.runAtPcmRate; + + synth.m_scaleModulators = (m_setup.ScaleModulators != 0); + + if(m_setup.LogarithmicVolumes != 0) + synth.setVolumeScaleModel(OPNMIDI_VolumeModel_NativeOPN2); + else + synth.setVolumeScaleModel(static_cast(m_setup.VolumeModel)); + + if(m_setup.VolumeModel == OPNMIDI_VolumeModel_AUTO) + synth.m_volumeScale = static_cast(synth.m_insBankSetup.volumeModel); + + synth.m_numChips = m_setup.numChips; + + if(m_setup.lfoEnable < 0) + synth.m_lfoEnable = (synth.m_insBankSetup.lfoEnable != 0); + else + synth.m_lfoEnable = (m_setup.lfoEnable != 0); + + if(m_setup.lfoFrequency < 0) + synth.m_lfoFrequency = static_cast(synth.m_insBankSetup.lfoFrequency); + else + synth.m_lfoFrequency = static_cast(m_setup.lfoFrequency); + + int chipType; + if(m_setup.chipType < 0) + chipType = synth.m_insBankSetup.chipType; + else + chipType = m_setup.chipType; + + synth.reset(m_setup.emulator, m_setup.PCM_RATE, static_cast(chipType), this); + m_chipChannels.clear(); + m_chipChannels.resize(synth.m_numChannels, OpnChannel()); + resetMIDIDefaults(); +#if defined(OPNMIDI_MIDI2VGM) && !defined(OPNMIDI_DISABLE_MIDI_SEQUENCER) + m_sequencerInterface->onloopStart = synth.m_loopStartHook; + m_sequencerInterface->onloopStart_userData = synth.m_loopStartHookData; + m_sequencerInterface->onloopEnd = synth.m_loopEndHook; + m_sequencerInterface->onloopEnd_userData = synth.m_loopEndHookData; + m_sequencer->setLoopHooksOnly(m_sequencerInterface->onloopStart != NULL); +#endif + // Reset the arpeggio counter + m_arpeggioCounter = 0; +} + +void OPNMIDIplay::partialReset() +{ + Synth &synth = *m_synth; + realTime_panic(); + m_setup.tick_skip_samples_delay = 0; + synth.m_runAtPcmRate = m_setup.runAtPcmRate; + synth.reset(m_setup.emulator, m_setup.PCM_RATE, synth.chipFamily(), this); + m_chipChannels.clear(); + m_chipChannels.resize(synth.m_numChannels); + resetMIDIDefaults(); +#if defined(OPNMIDI_MIDI2VGM) && !defined(OPNMIDI_DISABLE_MIDI_SEQUENCER) + m_sequencerInterface->onloopStart = synth.m_loopStartHook; + m_sequencerInterface->onloopStart_userData = synth.m_loopStartHookData; + m_sequencerInterface->onloopEnd = synth.m_loopEndHook; + m_sequencerInterface->onloopEnd_userData = synth.m_loopEndHookData; + m_sequencer->setLoopHooksOnly(m_sequencerInterface->onloopStart != NULL); +#endif +} + +void OPNMIDIplay::resetMIDI() +{ + Synth &synth = *m_synth; + synth.m_masterVolume = MasterVolumeDefault; + m_sysExDeviceId = 0; + m_synthMode = Mode_XG; + m_arpeggioCounter = 0; + + m_midiChannels.clear(); + m_midiChannels.resize(16, MIDIchannel()); + + resetMIDIDefaults(); + + caugh_missing_instruments.clear(); + caugh_missing_banks_melodic.clear(); + caugh_missing_banks_percussion.clear(); +} + +void OPNMIDIplay::resetMIDIDefaults(int offset) +{ + Synth &synth = *m_synth; + + for(size_t c = offset, n = m_midiChannels.size(); c < n; ++c) + { + MIDIchannel &ch = m_midiChannels[c]; + + if(synth.m_musicMode == Synth::MODE_RSXX) + ch.def_volume = 127; + else if(synth.m_insBankSetup.mt32defaults) + { + ch.def_volume = 127; + ch.def_bendsense_lsb = 0; + ch.def_bendsense_msb = 12; + } + } +} + +void OPNMIDIplay::TickIterators(double s) +{ + Synth &synth = *m_synth; + for(uint32_t c = 0, n = synth.m_numChannels; c < n; ++c) + { + OpnChannel &ch = m_chipChannels[c]; + ch.addAge(static_cast(s * 1e6)); + } + + // Resolve "hell of all times" of too short drum notes + for(size_t c = 0, n = m_midiChannels.size(); c < n; ++c) + { + MIDIchannel &ch = m_midiChannels[c]; + if(ch.extended_note_count == 0) + continue; + + for(MIDIchannel::notes_iterator inext = ch.activenotes.begin(); !inext.is_end();) + { + MIDIchannel::notes_iterator i(inext++); + MIDIchannel::NoteInfo &ni = i->value; + + double ttl = ni.ttl; + if(ttl <= 0) + continue; + + ni.ttl = ttl = ttl - s; + if(ttl <= 0) + { + --ch.extended_note_count; + if(ni.isOnExtendedLifeTime) + { + noteUpdate(c, i, Upd_Off); + ni.isOnExtendedLifeTime = false; + } + } + } + } + + updateVibrato(s); + updateArpeggio(s); +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + updateGlide(s); +#endif +} + +void OPNMIDIplay::realTime_ResetState() +{ + Synth &synth = *m_synth; + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) + { + MIDIchannel &chan = m_midiChannels[ch]; + chan.resetAllControllers(); + chan.vibpos = 0.0; + chan.lastlrpn = 0; + chan.lastmrpn = 0; + chan.nrpn = false; + if((m_synthMode & Mode_GS) != 0)// Reset custom drum channels on GS + chan.is_xg_percussion = false; + noteUpdateAll(uint16_t(ch), Upd_All); + noteUpdateAll(uint16_t(ch), Upd_Off); + } + synth.m_masterVolume = MasterVolumeDefault; +} + +bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) +{ + Synth &synth = *m_synth; + + if(note >= 127) + note = 127; + + if((synth.m_musicMode == Synth::MODE_RSXX) && (velocity != 0)) + { + // Check if this is just a note after-touch + MIDIchannel::notes_iterator i = m_midiChannels[channel].find_activenote(note); + if(!i.is_end()) + { + MIDIchannel::NoteInfo &ni = i->value; + const int veloffset = ni.ains ? ni.ains->midiVelocityOffset : 0; + velocity = static_cast(std::min(127, std::max(1, static_cast(velocity) + veloffset))); + ni.vol = velocity; + if(ni.ains) + noteUpdate(channel, i, Upd_Volume); + return false; + } + } + + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + + // noteOff(channel, note, velocity != 0); + // On Note on, Keyoff the note first, just in case keyoff + // was omitted; this fixes Dance of sugar-plum fairy + // by Microsoft. Now that we've done a Keyoff, + // check if we still need to do a Keyon. + // vol=0 and event 8x are both Keyoff-only. + if(velocity == 0) + { + noteOff(channel, note, false); + return false; + } + + MIDIchannel &midiChan = m_midiChannels[channel]; + + size_t midiins = midiChan.patch; + bool isPercussion = (channel % 16 == 9) || midiChan.is_xg_percussion; + + size_t bank = 0; + if(midiChan.bank_msb || midiChan.bank_lsb) + { + if((m_synthMode & Mode_GS) != 0) //in GS mode ignore LSB + bank = (midiChan.bank_msb * 256); + else + bank = (midiChan.bank_msb * 256) + midiChan.bank_lsb; + } + + if(isPercussion) + { + // == XG bank numbers == + // 0x7E00 - XG "SFX Kits" SFX1/SFX2 channel (16128 signed decimal) + // 0x7F00 - XG "Drum Kits" Percussion channel (16256 signed decimal) + + // MIDI instrument defines the patch: + if((m_synthMode & Mode_XG) != 0) + { + // Let XG SFX1/SFX2 bank will go in 128...255 range of LSB in WOPN file) + // Let XG Percussion bank will use (0...127 LSB range in WOPN file) + + // Choose: SFX or Drum Kits + bank = midiins + ((midiChan.bank_msb == 0x7E) ? 128 : 0); + } + else + { + bank = midiins; + } + midiins = note; // Percussion instrument + } + + if(isPercussion) + bank += Synth::PercussionTag; + + const OpnInstMeta *ains = &Synth::m_emptyInstrument; + + //Set bank bank + const Synth::Bank *bnk = NULL; + bool caughtMissingBank = false; + if((bank & ~static_cast(Synth::PercussionTag)) > 0) + { + Synth::BankMap::iterator b = synth.m_insBanks.find(bank); + if(b != synth.m_insBanks.end()) + bnk = &b->second; + if(bnk) + ains = &bnk->ins[midiins]; + else + caughtMissingBank = true; + } + + //Or fall back to bank ignoring LSB (GS/XG) + if(ains->flags & OpnInstMeta::Flag_NoSound) + { + size_t fallback = bank & ~(size_t)0x7F; + if(fallback != bank) + { + Synth::BankMap::iterator b = synth.m_insBanks.find(fallback); + caughtMissingBank = false; + if(b != synth.m_insBanks.end()) + bnk = &b->second; + if(bnk) + ains = &bnk->ins[midiins]; + else + caughtMissingBank = true; + } + } + + if(caughtMissingBank && hooks.onDebugMessage) + { + std::set &missing = (isPercussion) ? + caugh_missing_banks_percussion : caugh_missing_banks_melodic; + const char *text = (isPercussion) ? + "percussion" : "melodic"; + if(missing.insert(bank).second) + { + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "[%i] Playing missing %s MIDI bank %i (patch %i)", + channel, text, (bank & ~static_cast(Synth::PercussionTag)), midiins); + } + } + + //Or fall back to first bank + if((ains->flags & OpnInstMeta::Flag_NoSound) != 0) + { + Synth::BankMap::iterator b = synth.m_insBanks.find(bank & Synth::PercussionTag); + if(b != synth.m_insBanks.end()) + bnk = &b->second; + if(bnk) + ains = &bnk->ins[midiins]; + } + + const int veloffset = ains->midiVelocityOffset; + velocity = static_cast(std::min(127, std::max(1, static_cast(velocity) + veloffset))); + + int32_t tone = note; + if(!isPercussion && (bank > 0)) // For non-zero banks + { + if(ains->flags & OpnInstMeta::Flag_NoSound) + { + if(hooks.onDebugMessage) + { + if(!caugh_missing_instruments.count(static_cast(midiins))) + { + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caught a blank instrument %i (offset %i) in the MIDI bank %u", channel, midiChan.patch, midiins, bank); + caugh_missing_instruments.insert(static_cast(midiins)); + } + } + bank = 0; + midiins = midiChan.patch; + } + } + + if(ains->drumTone) + { + if(ains->drumTone >= 128) + tone = ains->drumTone - 128; + else + tone = ains->drumTone; + } + + MIDIchannel::NoteInfo::Phys voices[MIDIchannel::NoteInfo::MaxNumPhysChans] = { + {0, ains->op[0], /*false*/}, + {0, ains->op[1], /*pseudo_4op*/}, + }; + //bool pseudo_4op = ains.flags & opnInstMeta::Flag_Pseudo8op; + //if((opn.AdlPercussionMode == 1) && PercussionMap[midiins & 0xFF]) i[1] = i[0]; + + bool isBlankNote = (ains->flags & OpnInstMeta::Flag_NoSound) != 0; + + if(hooks.onDebugMessage) + { + if(!caugh_missing_instruments.count(static_cast(midiins)) && isBlankNote) + { + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing instrument %i", channel, midiins); + caugh_missing_instruments.insert(static_cast(midiins)); + } + } + + if(isBlankNote) + { + // Don't even try to play the blank instrument! But, insert the dummy note. + MIDIchannel::notes_iterator i = midiChan.ensure_create_activenote(note); + MIDIchannel::NoteInfo &dummy = i->value; + dummy.isBlank = true; + dummy.isOnExtendedLifeTime = false; + dummy.ttl = 0; + dummy.ains = NULL; + dummy.chip_channels_count = 0; + // Record the last note on MIDI channel as source of portamento + midiChan.portamentoSource = static_cast(note); + return false; + } + + // Allocate OPN2 channel (the physical sound channel for the note) + int32_t adlchannel[MIDIchannel::NoteInfo::MaxNumPhysChans] = { -1, -1 }; + + for(uint32_t ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) + { + if(ccount == 1) + { + if(voices[0] == voices[1]) + break; // No secondary channel + if(adlchannel[0] == -1) + break; // No secondary if primary failed + } + + int32_t c = -1; + int32_t bs = -0x7FFFFFFFl; + + for(size_t a = 0; a < static_cast(synth.m_numChannels); ++a) + { + if(ccount == 1 && static_cast(a) == adlchannel[0]) continue; + // ^ Don't use the same channel for primary&secondary + // ===== Kept for future pseudo-8-op mode + //if(voices[0] == voices[1] || pseudo_4op) + //{ + // // Only use regular channels + // uint8_t expected_mode = 0; + // if(opn.AdlPercussionMode == 1) + // { + // if(cmf_percussion_mode) + // expected_mode = MidCh < 11 ? 0 : (3 + MidCh - 11); // CMF + // else + // expected_mode = PercussionMap[midiins & 0xFF]; + // } + // if(opn.four_op_category[a] != expected_mode) + // continue; + //} + int64_t s = calculateChipChannelGoodness(a, voices[ccount]); + if(s > bs) + { + bs = static_cast(s); // Best candidate wins + c = static_cast(a); + } + } + + if(c < 0) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "ignored unplaceable note [bank %i, inst %i, note %i, MIDI channel %i]", + bank, midiChan.patch, note, channel); + continue; // Could not play this note. Ignore it. + } + + prepareChipChannelForNewNote(static_cast(c), voices[ccount]); + adlchannel[ccount] = c; + } + + if(adlchannel[0] < 0 && adlchannel[1] < 0) + { + // The note could not be played, at all. + return false; + } + + //if(hooks.onDebugMessage) + // hooks.onDebugMessage(hooks.onDebugMessage_userData, "i1=%d:%d, i2=%d:%d", i[0],adlchannel[0], i[1],adlchannel[1]); + + if(midiChan.softPedal) // Apply Soft Pedal level reducing + velocity = static_cast(std::floor(static_cast(velocity) * 0.8f)); + + // Allocate active note for MIDI channel + MIDIchannel::notes_iterator ir = midiChan.ensure_create_activenote(note); + MIDIchannel::NoteInfo &ni = ir->value; + ni.vol = velocity; + ni.vibrato = midiChan.noteAftertouch[note]; + ni.noteTone = static_cast(tone); + ni.currentTone = tone; + ni.glideRate = HUGE_VAL; + ni.midiins = midiins; + ni.isPercussion = isPercussion; + ni.isBlank = isBlankNote; + ni.isOnExtendedLifeTime = false; + ni.ttl = 0; + ni.ains = ains; + ni.chip_channels_count = 0; + + int8_t currentPortamentoSource = midiChan.portamentoSource; + double currentPortamentoRate = midiChan.portamentoRate; + bool portamentoEnable = + midiChan.portamentoEnable && currentPortamentoRate != HUGE_VAL && !isPercussion; + // Record the last note on MIDI channel as source of portamento + midiChan.portamentoSource = static_cast(note); + // midiChan.portamentoSource = portamentoEnable ? (int8_t)note : (int8_t)-1; + + // Enable gliding on portamento note + if (portamentoEnable && currentPortamentoSource >= 0) + { + ni.currentTone = currentPortamentoSource; + ni.glideRate = currentPortamentoRate; + ++midiChan.gliding_note_count; + } + + // Enable life time extension on percussion note + if (isPercussion) + { + ni.ttl = drum_note_min_time; + ++midiChan.extended_note_count; + } + + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) + { + int32_t c = adlchannel[ccount]; + if(c < 0) + continue; + uint16_t chipChan = static_cast(adlchannel[ccount]); + ni.phys_ensure_find_or_create(chipChan)->assign(voices[ccount]); + } + + noteUpdate(channel, ir, Upd_All | Upd_Patch); + + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) + { + int32_t c = adlchannel[ccount]; + if(c < 0) + continue; + m_chipChannels[c].recent_ins = voices[ccount]; + m_chipChannels[c].addAge(0); + } + + return true; +} + +void OPNMIDIplay::realTime_NoteOff(uint8_t channel, uint8_t note) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + noteOff(channel, note); +} + +void OPNMIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + MIDIchannel &chan = m_midiChannels[channel]; + MIDIchannel::notes_iterator i = m_midiChannels[channel].find_activenote(note); + if(!i.is_end()) + { + i->value.vibrato = atVal; + } + + uint8_t oldAtVal = chan.noteAftertouch[note % 128]; + if(atVal != oldAtVal) + { + chan.noteAftertouch[note % 128] = atVal; + bool inUse = atVal != 0; + for(unsigned n = 0; !inUse && n < 128; ++n) + inUse = chan.noteAftertouch[n] != 0; + chan.noteAfterTouchInUse = inUse; + } +} + +void OPNMIDIplay::realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].aftertouch = atVal; +} + +void OPNMIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + switch(type) + { + case 1: // Adjust vibrato + //UI.PrintLn("%u:vibrato %d", MidCh,value); + m_midiChannels[channel].vibrato = value; + break; + + case 0: // Set bank msb (GM bank) + m_midiChannels[channel].bank_msb = value; + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); + break; + + case 32: // Set bank lsb (XG bank) + m_midiChannels[channel].bank_lsb = value; + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); + break; + + case 5: // Set portamento msb + m_midiChannels[channel].portamento = static_cast((m_midiChannels[channel].portamento & 0x007F) | (value << 7)); + updatePortamento(channel); + break; + + case 37: // Set portamento lsb + m_midiChannels[channel].portamento = static_cast((m_midiChannels[channel].portamento & 0x3F80) | (value)); + updatePortamento(channel); + break; + + case 65: // Enable/disable portamento + m_midiChannels[channel].portamentoEnable = value >= 64; + updatePortamento(channel); + break; + + case 7: // Change volume + m_midiChannels[channel].volume = value; + noteUpdateAll(channel, Upd_Volume); + break; + + case 74: // Change brightness + m_midiChannels[channel].brightness = value; + noteUpdateAll(channel, Upd_Volume); + break; + + case 64: // Enable/disable sustain + m_midiChannels[channel].sustain = (value >= 64); + if(!m_midiChannels[channel].sustain) + killSustainingNotes(channel, -1, OpnChannel::LocationData::Sustain_Pedal); + break; + + case 66: // Enable/disable sostenuto + if(value >= 64) //Find notes and mark them as sostenutoed + markSostenutoNotes(channel); + else + killSustainingNotes(channel, -1, OpnChannel::LocationData::Sustain_Sostenuto); + break; + + case 67: // Enable/disable soft-pedal + m_midiChannels[channel].softPedal = (value >= 64); + break; + + case 11: // Change expression (another volume factor) + m_midiChannels[channel].expression = value; + noteUpdateAll(channel, Upd_Volume); + break; + + case 10: // Change panning + m_midiChannels[channel].panning = value; + noteUpdateAll(channel, Upd_Pan); + break; + + case 121: // Reset all controllers + m_midiChannels[channel].resetAllControllers121(); + noteUpdateAll(channel, Upd_Pan + Upd_Volume + Upd_Pitch); + // Kill all sustained notes + killSustainingNotes(channel, -1, OpnChannel::LocationData::Sustain_ANY); + break; + + case 120: // All sounds off + noteUpdateAll(channel, Upd_OffMute); + break; + + case 123: // All notes off + noteUpdateAll(channel, Upd_Off); + break; + + case 91: + break; // Reverb effect depth. We don't do per-channel reverb. + + case 92: + break; // Tremolo effect depth. We don't do... + + case 93: + break; // Chorus effect depth. We don't do. + + case 94: + break; // Celeste effect depth. We don't do. + + case 95: + break; // Phaser effect depth. We don't do. + + case 98: + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = true; + break; + + case 99: + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = true; + break; + + case 100: + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = false; + break; + + case 101: + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = false; + break; + + case 113: + break; // Related to pitch-bender, used by missimp.mid in Duke3D + + case 6: + setRPN(channel, value, true); + break; + + case 38: + setRPN(channel, value, false); + break; + + //case 103: + // cmf_percussion_mode = (value != 0); + // break; // CMF (ctrl 0x67) rhythm mode + + default: + break; + //UI.PrintLn("Ctrl %d <- %d (ch %u)", ctrlno, value, MidCh); + } +} + +void OPNMIDIplay::realTime_PatchChange(uint8_t channel, uint8_t patch) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].patch = patch; +} + +void OPNMIDIplay::realTime_PitchBend(uint8_t channel, uint16_t pitch) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bend = int(pitch) - 8192; + noteUpdateAll(channel, Upd_Pitch); +} + +void OPNMIDIplay::realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bend = int(lsb) + int(msb) * 128 - 8192; + noteUpdateAll(channel, Upd_Pitch); +} + +void OPNMIDIplay::realTime_BankChangeLSB(uint8_t channel, uint8_t lsb) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_lsb = lsb; +} + +void OPNMIDIplay::realTime_BankChangeMSB(uint8_t channel, uint8_t msb) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_msb = msb; +} + +void OPNMIDIplay::realTime_BankChange(uint8_t channel, uint16_t bank) +{ + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_lsb = uint8_t(bank & 0xFF); + m_midiChannels[channel].bank_msb = uint8_t((bank >> 8) & 0xFF); +} + +void OPNMIDIplay::setDeviceId(uint8_t id) +{ + m_sysExDeviceId = id; +} + +bool OPNMIDIplay::realTime_SysEx(const uint8_t *msg, size_t size) +{ + if(size < 4 || msg[0] != 0xF0 || msg[size - 1] != 0xF7) + return false; + + unsigned manufacturer = msg[1]; + unsigned dev = msg[2]; + msg += 3; + size -= 4; + + switch(manufacturer) + { + default: + break; + case Manufacturer_UniversalNonRealtime: + case Manufacturer_UniversalRealtime: + return doUniversalSysEx( + dev, manufacturer == Manufacturer_UniversalRealtime, msg, size); + case Manufacturer_Roland: + return doRolandSysEx(dev, msg, size); + case Manufacturer_Yamaha: + return doYamahaSysEx(dev, msg, size); + } + + return false; +} + +bool OPNMIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || dev == m_sysExDeviceId; + if(size < 2 || !devicematch) + return false; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 8) | + (((unsigned)data[1] & 0x7F)); + data += 2; + size -= 2; + + switch(((unsigned)realtime << 16) | address) + { + case (0 << 16) | 0x0901: // GM System On + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System On"); + m_synthMode = Mode_GM; + realTime_ResetState(); + return true; + case (0 << 16) | 0x0902: // GM System Off + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System Off"); + m_synthMode = Mode_XG;//TODO: TEMPORARY, make something RIGHT + realTime_ResetState(); + return true; + case (1 << 16) | 0x0401: // MIDI Master Volume + if(size != 2) + break; + unsigned volume = + (((unsigned)data[0] & 0x7F)) | + (((unsigned)data[1] & 0x7F) << 7); + if(m_synth.get()) + m_synth->m_masterVolume = static_cast(volume >> 7); + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) + noteUpdateAll(uint16_t(ch), Upd_Volume); + return true; + } + + return false; +} + +bool OPNMIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 6 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + unsigned mode = data[1] & 0x7F; + unsigned checksum = data[size - 1] & 0x7F; + data += 2; + size -= 3; + +#if !defined(OPNMIDI_SKIP_ROLAND_CHECKSUM) + { + unsigned checkvalue = 0; + for(size_t i = 0; i < size; ++i) + checkvalue += data[i] & 0x7F; + checkvalue = (128 - (checkvalue & 127)) & 127; + if(checkvalue != checksum) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught invalid roland SysEx message!"); + return false; + } + } +#endif + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + unsigned target_channel = 0; + + /* F0 41 10 42 12 40 00 7F 00 41 F7 */ + + if((address & 0xFFF0FF) == 0x401015) // Turn channel 1 into percussion + { + address = 0x401015; + target_channel = data[1] & 0x0F; + } + + data += 3; + size -= 3; + + if(mode != RolandMode_Send) // don't have MIDI-Out reply ability + return false; + + // Mode Set + // F0 {41 10 42 12} {40 00 7F} {00 41} F7 + + // Custom drum channels + // F0 {41 10 42 12} {40 1 15} { } F7 + + switch((model << 24) | address) + { + case (RolandModel_GS << 24) | 0x00007F: // System Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned gs_mode = data[0] & 0x7F; + ADL_UNUSED(gs_mode);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland System Mode Set: %02X", gs_mode); + m_synthMode = Mode_GS; + realTime_ResetState(); + return true; + } + case (RolandModel_GS << 24) | 0x40007F: // Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned value = data[0] & 0x7F; + ADL_UNUSED(value);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland Mode Set: %02X", value); + m_synthMode = Mode_GS; + realTime_ResetState(); + return true; + } + case (RolandModel_GS << 24) | 0x401015: // Percussion channel + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + if(m_midiChannels.size() < 16) + break; + unsigned value = data[0] & 0x7F; + const uint8_t channels_map[16] = + { + 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15 + }; + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "SysEx: Caught Roland Percussion set: %02X on channel %u (from %X)", + value, channels_map[target_channel], target_channel); + m_midiChannels[channels_map[target_channel]].is_xg_percussion = ((value == 0x01)) || ((value == 0x02)); + return true; + } + } + + return false; +} + +bool OPNMIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 1 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + ++data; + --size; + + switch((model << 8) | (dev & 0xF0)) + { + case (YamahaModel_XG << 8) | 0x10: // parameter change + { + if(size < 3) + break; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + data += 3; + size -= 3; + + switch(address) + { + case 0x00007E: // XG System On + if(size != 1) + break; + unsigned value = data[0] & 0x7F; + ADL_UNUSED(value);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Yamaha XG System On: %02X", value); + m_synthMode = Mode_XG; + realTime_ResetState(); + return true; + } + + break; + } + } + + return false; +} + +void OPNMIDIplay::realTime_panic() +{ + panic(); + killSustainingNotes(-1, -1, OpnChannel::LocationData::Sustain_ANY); +} + +void OPNMIDIplay::realTime_deviceSwitch(size_t track, const char *data, size_t length) +{ + const std::string indata(data, length); + m_currentMidiDevice[track] = chooseDevice(indata); +} + +size_t OPNMIDIplay::realTime_currentDevice(size_t track) +{ + if(m_currentMidiDevice.empty()) + return 0; + return m_currentMidiDevice[track]; +} + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +void OPNMIDIplay::AudioTick(uint32_t chipId, uint32_t rate) +{ + if(chipId != 0) // do first chip ticks only + return; + + uint32_t tickNumber = m_audioTickCounter++; + double timeDelta = 1.0 / rate; + + enum { portamentoInterval = 32 }; // for efficiency, set rate limit on pitch updates + + if(tickNumber % portamentoInterval == 0) + { + double portamentoDelta = timeDelta * portamentoInterval; + UpdateGlide(portamentoDelta); + } +} +#endif + +void OPNMIDIplay::noteUpdate(size_t midCh, + OPNMIDIplay::MIDIchannel::notes_iterator i, + unsigned props_mask, + int32_t select_adlchn) +{ + Synth &synth = *m_synth; + MIDIchannel::NoteInfo &info = i->value; + const int16_t noteTone = info.noteTone; + const double currentTone = info.currentTone; + const uint8_t vol = info.vol; + const size_t midiins = info.midiins; + const OpnInstMeta &ains = *info.ains; + OpnChannel::Location my_loc; + my_loc.MidCh = static_cast(midCh); + my_loc.note = info.note; + + if(info.isBlank) + { + if(props_mask & Upd_Off) + m_midiChannels[midCh].activenotes.erase(i); + return; + } + + for(unsigned ccount = 0, ctotal = info.chip_channels_count; ccount < ctotal; ccount++) + { + const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; + uint16_t c = ins.chip_chan; + + if(select_adlchn >= 0 && c != select_adlchn) continue; + + if(props_mask & Upd_Patch) + { + synth.setPatch(c, ins.ains); + OpnChannel::users_iterator ci = m_chipChannels[c].find_or_create_user(my_loc); + if(!ci.is_end()) // inserts if necessary + { + OpnChannel::LocationData &d = ci->value; + d.sustained = OpnChannel::LocationData::Sustain_None; + d.vibdelay_us = 0; + d.fixed_sustain = (ains.soundKeyOnMs == static_cast(opnNoteOnMaxTime)); + d.kon_time_until_neglible_us = 1000 * ains.soundKeyOnMs; + d.ins = ins; + } + } + } + + for(unsigned ccount = 0; ccount < info.chip_channels_count; ccount++) + { + const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; + uint16_t c = ins.chip_chan; + + if(select_adlchn >= 0 && c != select_adlchn) + continue; + + if(props_mask & Upd_Off) // note off + { + if(m_midiChannels[midCh].sustain == 0) + { + OpnChannel::users_iterator k = m_chipChannels[c].find_user(my_loc); + bool do_erase_user = (!k.is_end() && ((k->value.sustained & OpnChannel::LocationData::Sustain_Sostenuto) == 0)); + if(do_erase_user) + m_chipChannels[c].users.erase(k); + + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, c, noteTone, static_cast(midiins), 0, 0.0); + + if(do_erase_user && m_chipChannels[c].users.empty()) + { + synth.noteOff(c); + if(props_mask & Upd_Mute) // Mute the note + { + synth.touchNote(c, 0); + m_chipChannels[c].koff_time_until_neglible_us = 0; + } + else + { + m_chipChannels[c].koff_time_until_neglible_us = 1000 * int64_t(ains.soundKeyOffMs); + } + } + } + else + { + // Sustain: Forget about the note, but don't key it off. + // Also will avoid overwriting it very soon. + OpnChannel::users_iterator d = m_chipChannels[c].find_or_create_user(my_loc); + if(!d.is_end()) + d->value.sustained |= OpnChannel::LocationData::Sustain_Pedal; // note: not erased! + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, c, noteTone, static_cast(midiins), -1, 0.0); + } + + info.phys_erase_at(&ins); // decrements channel count + --ccount; // adjusts index accordingly + continue; + } + + if(props_mask & Upd_Pan) + synth.setPan(c, m_midiChannels[midCh].panning); + + if(props_mask & Upd_Volume) + { + const MIDIchannel &ch = m_midiChannels[midCh]; + bool is_percussion = (midCh == 9) || ch.is_xg_percussion; + uint_fast32_t brightness = is_percussion ? 127 : ch.brightness; + + if(!m_setup.fullRangeBrightnessCC74) + { + // Simulate post-High-Pass filter result which affects sounding by half level only + if(brightness >= 64) + brightness = 127; + else + brightness *= 2; + } + + synth.touchNote(c, + vol, + ch.volume, + ch.expression, + static_cast(brightness)); + } + + if(props_mask & Upd_Pitch) + { + OpnChannel::users_iterator d = m_chipChannels[c].find_user(my_loc); + + // Don't bend a sustained note + if(d.is_end() || (d->value.sustained == OpnChannel::LocationData::Sustain_None)) + { + MIDIchannel &chan = m_midiChannels[midCh]; + double midibend = chan.bend * chan.bendsense; + double bend = midibend + ins.ains.noteOffset; + double phase = 0.0; + uint8_t vibrato = std::max(chan.vibrato, chan.aftertouch); + + vibrato = std::max(vibrato, info.vibrato); + + if((ains.flags & OpnInstMeta::Flag_Pseudo8op) && ins.ains == ains.op[1]) + { + phase = ains.voice2_fine_tune; + } + + if(vibrato && (d.is_end() || d->value.vibdelay_us >= chan.vibdelay_us)) + bend += static_cast(vibrato) * chan.vibdepth * std::sin(chan.vibpos); + + synth.noteOn(c, currentTone + bend + phase); + + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, c, noteTone, static_cast(midiins), vol, midibend); + } + } + } + + if(info.chip_channels_count == 0) + { + m_midiChannels[midCh].cleanupNote(i); + m_midiChannels[midCh].activenotes.erase(i); + } +} + +void OPNMIDIplay::noteUpdateAll(size_t midCh, unsigned props_mask) +{ + for(MIDIchannel::notes_iterator + i = m_midiChannels[midCh].activenotes.begin(); !i.is_end();) + { + MIDIchannel::notes_iterator j(i++); + noteUpdate(midCh, j, props_mask); + } +} + +const std::string &OPNMIDIplay::getErrorString() +{ + return errorStringOut; +} + +void OPNMIDIplay::setErrorString(const std::string &err) +{ + errorStringOut = err; +} + +int64_t OPNMIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const +{ + Synth &synth = *m_synth; + const OpnChannel &chan = m_chipChannels[c]; + int64_t koff_ms = chan.koff_time_until_neglible_us / 1000; + int64_t s = -koff_ms; + OPNMIDI_ChannelAlloc allocType = synth.m_channelAlloc; + + if(allocType == OPNMIDI_ChanAlloc_AUTO) + { + if(synth.m_musicMode == Synth::MODE_CMF) + allocType = OPNMIDI_ChanAlloc_SameInst; + else + allocType = OPNMIDI_ChanAlloc_OffDelay; + } + + // Rate channel with a releasing note + if(s < 0 && chan.users.empty()) + { + bool isSame = (chan.recent_ins == ins); + s -= 40000; + + // If it's same instrument, better chance to get it when no free channels + switch(allocType) + { + case OPNMIDI_ChanAlloc_SameInst: + if(isSame) + s = 0; // Re-use releasing channel with the same instrument + break; + case OPNMIDI_ChanAlloc_AnyReleased: + s = 0; // Re-use any releasing channel + break; + default: + case OPNMIDI_ChanAlloc_OffDelay: + if(isSame) + s = -koff_ms; // Wait until releasing sound will complete + break; + } + + return s; + } + + // Same midi-instrument = some stability + for(OpnChannel::const_users_iterator j = chan.users.begin(); !j.is_end(); ++j) + { + const OpnChannel::LocationData &jd = j->value; + + int64_t kon_ms = jd.kon_time_until_neglible_us / 1000; + s -= (jd.sustained == OpnChannel::LocationData::Sustain_None) ? + (4000000 + kon_ms) : (500000 + (kon_ms / 2)); + + MIDIchannel::notes_iterator + k = const_cast(m_midiChannels[jd.loc.MidCh]).find_activenote(jd.loc.note); + + if(!k.is_end()) + { + const MIDIchannel::NoteInfo &info = k->value; + + // Same instrument = good + if(jd.ins == ins) + { + s += 300; + // Arpeggio candidate = even better + if(jd.vibdelay_us < 70000 + || jd.kon_time_until_neglible_us > 20000000) + s += 10; + } + + // Percussion is inferior to melody + s += info.isPercussion ? 50 : 0; + } + + // If there is another channel to which this note + // can be evacuated to in the case of congestion, + // increase the score slightly. +// unsigned n_evacuation_stations = 0; + +// for(unsigned c2 = 0; c2 < opn.NumChannels; ++c2) +// { +// if(c2 == c) continue; + +// if(opn.four_op_category[c2] +// != opn.four_op_category[c]) continue; + +// for(AdlChannel::const_users_iterator m = m_chipChannels[c2].users.begin(); !m.is_end(); ++m) +// { +// const AdlChannel::LocationData &md = m->value; +// if(md.sustained != OpnChannel::LocationData::Sustain_None) continue; +// if(md.vibdelay >= 200000) continue; +// if(md.ins != jd.ins) continue; +// n_evacuation_stations += 1; +// } +// } + +// s += n_evacuation_stations * 4; + } + + return s; +} + + +void OPNMIDIplay::prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins) +{ + if(m_chipChannels[c].users.empty()) return; // Nothing to do + + Synth &synth = *m_synth; + + if(!m_setup.enableAutoArpeggio) + { + // Kill all notes on this channel with no mercy + for(OpnChannel::users_iterator jnext = m_chipChannels[c].users.begin(); !jnext.is_end();) + { + OpnChannel::users_iterator j = jnext; + OpnChannel::LocationData &jd = j->value; + ++jnext; + + m_midiChannels[jd.loc.MidCh].clear_all_phys_users(c); + m_chipChannels[c].users.erase(j); + } + + synth.noteOff(c); + assert(m_chipChannels[c].users.empty()); // No users should remain! + + return; + } + + //bool doing_arpeggio = false; + for(OpnChannel::users_iterator jnext = m_chipChannels[c].users.begin(); !jnext.is_end();) + { + OpnChannel::users_iterator j = jnext; + OpnChannel::LocationData &jd = jnext->value; + ++jnext; + + if(jd.sustained == OpnChannel::LocationData::Sustain_None) + { + // Collision: Kill old note, + // UNLESS we're going to do arpeggio + MIDIchannel::notes_iterator i + (m_midiChannels[jd.loc.MidCh].ensure_find_activenote(jd.loc.note)); + + // Check if we can do arpeggio. + if((jd.vibdelay_us < 70000 || jd.kon_time_until_neglible_us > 20000000) && jd.ins == ins) + { + // Do arpeggio together with this note. + //doing_arpeggio = true; + continue; + } + + killOrEvacuate(c, j, i); + // ^ will also erase j from ch[c].users. + } + } + + // Kill all sustained notes on this channel + // Don't keep them for arpeggio, because arpeggio requires + // an intact "activenotes" record. This is a design flaw. + killSustainingNotes(-1, static_cast(c), OpnChannel::LocationData::Sustain_ANY); + + // Keyoff the channel so that it can be retriggered, + // unless the new note will be introduced as just an arpeggio. + if(m_chipChannels[c].users.empty()) + synth.noteOff(c); +} + +void OPNMIDIplay::killOrEvacuate(size_t from_channel, + OpnChannel::users_iterator j, + OPNMIDIplay::MIDIchannel::notes_iterator i) +{ + Synth &synth = *m_synth; + uint32_t maxChannels = OPN_MAX_CHIPS * 6; + OpnChannel::LocationData &jd = j->value; + MIDIchannel::NoteInfo &info = i->value; + + // Before killing the note, check if it can be + // evacuated to another channel as an arpeggio + // instrument. This helps if e.g. all channels + // are full of strings and we want to do percussion. + // FIXME: This does not care about four-op entanglements. + for(uint32_t c = 0; c < synth.m_numChannels; ++c) + { + uint16_t cs = static_cast(c); + + if(c >= maxChannels) + break; + if(c == from_channel) + continue; + //if(opn.four_op_category[c] != opn.four_op_category[from_channel]) + // continue; + + OpnChannel &adlch = m_chipChannels[c]; + if(adlch.users.size() == adlch.users.capacity()) + continue; // no room for more arpeggio on channel + + if(!m_chipChannels[cs].find_user(jd.loc).is_end()) + continue; // channel already has this note playing (sustained) + // avoid introducing a duplicate location. + + for(OpnChannel::users_iterator m = adlch.users.begin(); !m.is_end(); ++m) + { + OpnChannel::LocationData &mv = m->value; + + if(mv.vibdelay_us >= 200000 + && mv.kon_time_until_neglible_us < 10000000) continue; + if(mv.ins != jd.ins) + continue; + if(hooks.onNote) + { + hooks.onNote(hooks.onNote_userData, + static_cast(from_channel), + info.noteTone, + static_cast(info.midiins), 0, 0.0); + hooks.onNote(hooks.onNote_userData, + static_cast(c), + info.noteTone, + static_cast(info.midiins), + info.vol, 0.0); + } + + info.phys_erase(static_cast(from_channel)); + info.phys_ensure_find_or_create(cs)->assign(jd.ins); + m_chipChannels[cs].users.push_back(jd); + m_chipChannels[from_channel].users.erase(j); + return; + } + } + + /*UI.PrintLn( + "collision @%u: [%ld] <- ins[%3u]", + c, + //ch[c].midiins<128?'M':'P', ch[c].midiins&127, + ch[c].age, //adlins[ch[c].insmeta].ms_sound_kon, + ins + );*/ + // Kill it + noteUpdate(jd.loc.MidCh, + i, + Upd_Off, + static_cast(from_channel)); +} + +void OPNMIDIplay::panic() +{ + for(uint8_t chan = 0; chan < m_midiChannels.size(); chan++) + { + for(uint8_t note = 0; note < 128; note++) + realTime_NoteOff(chan, note); + } +} + +void OPNMIDIplay::killSustainingNotes(int32_t midCh, int32_t this_adlchn, uint32_t sustain_type) +{ + Synth &synth = *m_synth; + uint32_t first = 0, last = synth.m_numChannels; + + if(this_adlchn >= 0) + { + first = static_cast(this_adlchn); + last = first + 1; + } + + for(uint32_t c = first; c < last; ++c) + { + if(m_chipChannels[c].users.empty()) + continue; // Nothing to do + + for(OpnChannel::users_iterator jnext = m_chipChannels[c].users.begin(); !jnext.is_end();) + { + OpnChannel::users_iterator j = jnext; + OpnChannel::LocationData &jd = j->value; + ++jnext; + + if((midCh < 0 || jd.loc.MidCh == midCh) + && ((jd.sustained & sustain_type) != 0)) + { + int midiins = '?'; + if(hooks.onNote) + hooks.onNote(hooks.onNote_userData, static_cast(c), jd.loc.note, midiins, 0, 0.0); + jd.sustained &= ~sustain_type; + if(jd.sustained == OpnChannel::LocationData::Sustain_None) + m_chipChannels[c].users.erase(j);//Remove only when note is clean from any holders + } + } + + // Keyoff the channel, if there are no users left. + if(m_chipChannels[c].users.empty()) + synth.noteOff(c); + } +} + +void OPNMIDIplay::markSostenutoNotes(int32_t midCh) +{ + Synth &synth = *m_synth; + uint32_t first = 0, last = synth.m_numChannels; + for(uint32_t c = first; c < last; ++c) + { + if(m_chipChannels[c].users.empty()) + continue; // Nothing to do + + for(OpnChannel::users_iterator jnext = m_chipChannels[c].users.begin(); !jnext.is_end();) + { + OpnChannel::users_iterator j = jnext; + OpnChannel::LocationData &jd = j->value; + ++jnext; + if((jd.loc.MidCh == midCh) && (jd.sustained == OpnChannel::LocationData::Sustain_None)) + jd.sustained |= OpnChannel::LocationData::Sustain_Sostenuto; + } + } +} + +void OPNMIDIplay::setRPN(size_t midCh, unsigned value, bool MSB) +{ + bool nrpn = m_midiChannels[midCh].nrpn; + unsigned addr = m_midiChannels[midCh].lastmrpn * 0x100 + m_midiChannels[midCh].lastlrpn; + + switch(addr + nrpn * 0x10000 + MSB * 0x20000) + { + case 0x0000 + 0*0x10000 + 1*0x20000: // Pitch-bender sensitivity + m_midiChannels[midCh].bendsense_msb = static_cast(value); + m_midiChannels[midCh].updateBendSensitivity(); + break; + case 0x0000 + 0*0x10000 + 0*0x20000: // Pitch-bender sensitivity LSB + m_midiChannels[midCh].bendsense_lsb = static_cast(value); + m_midiChannels[midCh].updateBendSensitivity(); + break; + case 0x0108 + 1*0x10000 + 1*0x20000: // Vibrato speed + if((m_synthMode & Mode_XG) != 0) // Vibrato speed + { + if(value == 64) m_midiChannels[midCh].vibspeed = 1.0; + else if(value < 100) m_midiChannels[midCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1)); + else m_midiChannels[midCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385); + m_midiChannels[midCh].vibspeed *= 2 * 3.141592653 * 5.0; + } + break; + case 0x0109 + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato depth + { + m_midiChannels[midCh].vibdepth = ((static_cast(value) - 64) * 0.15) * 0.01; + } + break; + case 0x010A + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato delay in millisecons + { + m_midiChannels[midCh].vibdelay_us = value ? int64_t(209.2 * std::exp(0.0795 * static_cast(value))) : 0; + } + break; + default:/* UI.PrintLn("%s %04X <- %d (%cSB) (ch %u)", + "NRPN"+!nrpn, addr, value, "LM"[MSB], MidCh);*/ + break; + } +} + +void OPNMIDIplay::updatePortamento(size_t midCh) +{ + double rate = HUGE_VAL; + uint16_t midival = m_midiChannels[midCh].portamento; + if(m_midiChannels[midCh].portamentoEnable && midival > 0) + rate = 350.0 * std::pow(2.0, -0.062 * (1.0 / 128) * midival); + m_midiChannels[midCh].portamentoRate = rate; +} + +void OPNMIDIplay::noteOff(size_t midCh, uint8_t note, bool forceNow) +{ + MIDIchannel &ch = m_midiChannels[midCh]; + MIDIchannel::notes_iterator i = ch.find_activenote(note); + + if(!i.is_end()) + { + MIDIchannel::NoteInfo &ni = i->value; + if(forceNow || ni.ttl <= 0) + noteUpdate(midCh, i, Upd_Off); + else + ni.isOnExtendedLifeTime = true; + } +} + + +void OPNMIDIplay::updateVibrato(double amount) +{ + for(size_t a = 0, b = m_midiChannels.size(); a < b; ++a) + { + if(m_midiChannels[a].hasVibrato() && !m_midiChannels[a].activenotes.empty()) + { + noteUpdateAll(static_cast(a), Upd_Pitch); + m_midiChannels[a].vibpos += amount * m_midiChannels[a].vibspeed; + } + else + m_midiChannels[a].vibpos = 0.0; + } +} + + + + +size_t OPNMIDIplay::chooseDevice(const std::string &name) +{ + std::map::iterator i = m_midiDevices.find(name); + + if(i != m_midiDevices.end()) + return i->second; + + size_t n = m_midiDevices.size() * 16; + m_midiDevices.insert(std::make_pair(name, n)); + m_midiChannels.resize(n + 16); + resetMIDIDefaults(static_cast(n)); + return n; +} + +void OPNMIDIplay::updateArpeggio(double) // amount = amount of time passed +{ + // If there is an adlib channel that has multiple notes + // simulated on the same channel, arpeggio them. + + Synth &synth = *m_synth; + + if(!m_setup.enableAutoArpeggio) // Arpeggio was disabled + { + if(m_arpeggioCounter != 0) + m_arpeggioCounter = 0; + return; + } + +#if 0 + const unsigned desired_arpeggio_rate = 40; // Hz (upper limit) +# if 1 + static unsigned cache = 0; + amount = amount; // Ignore amount. Assume we get a constant rate. + cache += MaxSamplesAtTime * desired_arpeggio_rate; + + if(cache < PCM_RATE) return; + + cache %= PCM_RATE; +# else + static double arpeggio_cache = 0; + arpeggio_cache += amount * desired_arpeggio_rate; + + if(arpeggio_cache < 1.0) return; + + arpeggio_cache = 0.0; +# endif +#endif + + ++m_arpeggioCounter; + + for(uint32_t c = 0; c < synth.m_numChannels; ++c) + { +retry_arpeggio: + if(c > uint32_t(std::numeric_limits::max())) + break; + + size_t n_users = m_chipChannels[c].users.size(); + + if(n_users > 1) + { + OpnChannel::users_iterator i = m_chipChannels[c].users.begin(); + size_t rate_reduction = 3; + + if(n_users >= 3) + rate_reduction = 2; + + if(n_users >= 4) + rate_reduction = 1; + + for(size_t count = (m_arpeggioCounter / rate_reduction) % n_users, + n = 0; n < count; ++n) + ++i; + + OpnChannel::LocationData &d = i->value; + if(d.sustained == OpnChannel::LocationData::Sustain_None) + { + if(d.kon_time_until_neglible_us <= 0) + { + noteUpdate( + d.loc.MidCh, + m_midiChannels[ d.loc.MidCh ].ensure_find_activenote(d.loc.note), + Upd_Off, + static_cast(c)); + goto retry_arpeggio; + } + + noteUpdate( + d.loc.MidCh, + m_midiChannels[ d.loc.MidCh ].ensure_find_activenote(d.loc.note), + Upd_Pitch | Upd_Volume | Upd_Pan, + static_cast(c)); + } + } + } +} + +void OPNMIDIplay::updateGlide(double amount) +{ + size_t num_channels = m_midiChannels.size(); + + for(size_t channel = 0; channel < num_channels; ++channel) + { + MIDIchannel &midiChan = m_midiChannels[channel]; + if(midiChan.gliding_note_count == 0) + continue; + + for(MIDIchannel::notes_iterator it = midiChan.activenotes.begin(); + !it.is_end(); ++it) + { + MIDIchannel::NoteInfo &info = it->value; + double finalTone = info.noteTone; + double previousTone = info.currentTone; + + bool directionUp = previousTone < finalTone; + double toneIncr = amount * (directionUp ? +info.glideRate : -info.glideRate); + + double currentTone = previousTone + toneIncr; + bool glideFinished = !(directionUp ? (currentTone < finalTone) : (currentTone > finalTone)); + currentTone = glideFinished ? finalTone : currentTone; + + if(int64_t(currentTone * 1000000.0) != int64_t(previousTone * 1000000.0)) + { + info.currentTone = currentTone; + noteUpdate(static_cast(channel), it, Upd_Pitch); + } + } + } +} + +void OPNMIDIplay::describeChannels(char *str, char *attr, size_t size) +{ + if (!str || size <= 0) + return; + + Synth &synth = *m_synth; + uint32_t numChannels = synth.m_numChannels; + + uint32_t index = 0; + while(index < numChannels && index < size - 1) + { + const OpnChannel &adlChannel = m_chipChannels[index]; + + OpnChannel::const_users_iterator loc = adlChannel.users.begin(); + OpnChannel::const_users_iterator locnext(loc); + if(!loc.is_end()) ++locnext; + + if(loc.is_end()) // off + { + str[index] = '-'; + } + else if(!locnext.is_end()) // arpeggio + { + str[index] = '@'; + } + else // on + { + str[index] = '+'; + } + + uint8_t attribute = 0; + if (!loc.is_end()) // 4-bit color index of MIDI channel + attribute |= uint8_t(loc->value.loc.MidCh & 0xF); + + attr[index] = static_cast(attribute); + ++index; + } + + str[index] = 0; + attr[index] = 0; +} + +/* TODO */ + +//#ifndef ADLMIDI_DISABLE_CPP_EXTRAS + +//ADLMIDI_EXPORT AdlInstrumentTester::AdlInstrumentTester(ADL_MIDIPlayer *device) +//{ +// cur_gm = 0; +// ins_idx = 0; +// play = reinterpret_cast(device->adl_midiPlayer); +// if(!play) +// return; +// opl = &play->opl; +//} + +//ADLMIDI_EXPORT AdlInstrumentTester::~AdlInstrumentTester() +//{} + +//ADLMIDI_EXPORT void AdlInstrumentTester::FindAdlList() +//{ +// const unsigned NumBanks = (unsigned)adl_getBanksCount(); +// std::set adl_ins_set; +// for(unsigned bankno = 0; bankno < NumBanks; ++bankno) +// adl_ins_set.insert(banks[bankno][cur_gm]); +// adl_ins_list.assign(adl_ins_set.begin(), adl_ins_set.end()); +// ins_idx = 0; +// NextAdl(0); +// opl->Silence(); +//} + + + +//ADLMIDI_EXPORT void AdlInstrumentTester::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127 +//{ +// if(opl->LogarithmicVolumes) +// opl->Touch_Real(c, volume * 127 / (127 * 127 * 127) / 2); +// else +// { +// // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) +// opl->Touch_Real(c, volume > 8725 ? static_cast(std::log((double)volume) * 11.541561 + (0.5 - 104.22845)) : 0); +// // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) +// //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); +// } +//} + +//ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) +//{ +// if(adl_ins_list.empty()) FindAdlList(); +// const unsigned meta = adl_ins_list[ins_idx]; +// const adlinsdata &ains = opl->GetAdlMetaIns(meta); + +// int tone = (cur_gm & 128) ? (cur_gm & 127) : (note + 50); +// if(ains.tone) +// { +// /*if(ains.tone < 20) +// tone += ains.tone; +// else */ +// if(ains.tone < 128) +// tone = ains.tone; +// else +// tone -= ains.tone - 128; +// } +// double hertz = 172.00093 * std::exp(0.057762265 * (tone + 0.0)); +// int i[2] = { ains.adlno1, ains.adlno2 }; +// int32_t adlchannel[2] = { 0, 3 }; +// if(i[0] == i[1]) +// { +// adlchannel[1] = -1; +// adlchannel[0] = 6; // single-op +// if(play->hooks.onDebugMessage) +// { +// play->hooks.onDebugMessage(play->hooks.onDebugMessage_userData, +// "noteon at %d(%d) for %g Hz\n", adlchannel[0], i[0], hertz); +// } +// } +// else +// { +// if(play->hooks.onDebugMessage) +// { +// play->hooks.onDebugMessage(play->hooks.onDebugMessage_userData, +// "noteon at %d(%d) and %d(%d) for %g Hz\n", adlchannel[0], i[0], adlchannel[1], i[1], hertz); +// } +// } + +// opl->NoteOff(0); +// opl->NoteOff(3); +// opl->NoteOff(6); +// for(unsigned c = 0; c < 2; ++c) +// { +// if(adlchannel[c] < 0) continue; +// opl->Patch((uint16_t)adlchannel[c], (uint16_t)i[c]); +// opl->Touch_Real((uint16_t)adlchannel[c], 127 * 127 * 100); +// opl->Pan((uint16_t)adlchannel[c], 0x30); +// opl->NoteOn((uint16_t)adlchannel[c], hertz); +// } +//} + +//ADLMIDI_EXPORT void AdlInstrumentTester::NextGM(int offset) +//{ +// cur_gm = (cur_gm + 256 + (uint32_t)offset) & 0xFF; +// FindAdlList(); +//} + +//ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) +//{ +// if(adl_ins_list.empty()) FindAdlList(); +// const unsigned NumBanks = (unsigned)adl_getBanksCount(); +// ins_idx = (uint32_t)((int32_t)ins_idx + (int32_t)adl_ins_list.size() + offset) % adl_ins_list.size(); + +// #if 0 +// UI.Color(15); +// std::fflush(stderr); +// std::printf("SELECTED G%c%d\t%s\n", +// cur_gm < 128 ? 'M' : 'P', cur_gm < 128 ? cur_gm + 1 : cur_gm - 128, +// "<-> select GM, ^v select ins, qwe play note"); +// std::fflush(stdout); +// UI.Color(7); +// std::fflush(stderr); +// #endif + +// for(unsigned a = 0; a < adl_ins_list.size(); ++a) +// { +// const unsigned i = adl_ins_list[a]; +// const adlinsdata &ains = opl->GetAdlMetaIns(i); + +// char ToneIndication[8] = " "; +// if(ains.tone) +// { +// /*if(ains.tone < 20) +// snprintf(ToneIndication, 8, "+%-2d", ains.tone); +// else*/ +// if(ains.tone < 128) +// snprintf(ToneIndication, 8, "=%-2d", ains.tone); +// else +// snprintf(ToneIndication, 8, "-%-2d", ains.tone - 128); +// } +// std::printf("%s%s%s%u\t", +// ToneIndication, +// ains.adlno1 != ains.adlno2 ? "[2]" : " ", +// (ins_idx == a) ? "->" : "\t", +// i +// ); + +// for(unsigned bankno = 0; bankno < NumBanks; ++bankno) +// if(banks[bankno][cur_gm] == i) +// std::printf(" %u", bankno); + +// std::printf("\n"); +// } +//} + +//ADLMIDI_EXPORT bool AdlInstrumentTester::HandleInputChar(char ch) +//{ +// static const char notes[] = "zsxdcvgbhnjmq2w3er5t6y7ui9o0p"; +// // c'd'ef'g'a'bC'D'EF'G'A'Bc'd'e +// switch(ch) +// { +// case '/': +// case 'H': +// case 'A': +// NextAdl(-1); +// break; +// case '*': +// case 'P': +// case 'B': +// NextAdl(+1); +// break; +// case '-': +// case 'K': +// case 'D': +// NextGM(-1); +// break; +// case '+': +// case 'M': +// case 'C': +// NextGM(+1); +// break; +// case 3: +// #if !((!defined(__WIN32__) || defined(__CYGWIN__)) && !defined(__DJGPP__)) +// case 27: +// #endif +// return false; +// default: +// const char *p = std::strchr(notes, ch); +// if(p && *p) +// DoNote((int)(p - notes) - 12); +// } +// return true; +//} + +//#endif//ADLMIDI_DISABLE_CPP_EXTRAS diff --git a/thirdparty/opnmidi/opnmidi_midiplay.hpp b/thirdparty/opnmidi/opnmidi_midiplay.hpp new file mode 100644 index 000000000..51a10cb7b --- /dev/null +++ b/thirdparty/opnmidi/opnmidi_midiplay.hpp @@ -0,0 +1,1046 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * OPNMIDI Library and YM2612 support: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OPNMIDI_MIDIPLAY_HPP +#define OPNMIDI_MIDIPLAY_HPP + +#include "opnbank.h" +#include "opnmidi_private.hpp" +#include "opnmidi_ptr.hpp" +#include "structures/pl_list.hpp" + +/** + * @brief Hooks of the internal events + */ +struct MIDIEventHooks +{ + MIDIEventHooks() : + onNote(NULL), + onNote_userData(NULL), + onLoopStart(NULL), + onLoopStart_userData(NULL), + onLoopEnd(NULL), + onLoopEnd_userData(NULL), + onDebugMessage(NULL), + onDebugMessage_userData(NULL) + {} + + //! Note on/off hooks + typedef void (*NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); + NoteHook onNote; + void *onNote_userData; + + // Loop start/end hooks + OPN2_LoopPointHook onLoopStart; + void *onLoopStart_userData; + OPN2_LoopPointHook onLoopEnd; + void *onLoopEnd_userData; + + //! Library internal debug messages + typedef void (*DebugMessageHook)(void *userdata, const char *fmt, ...); + DebugMessageHook onDebugMessage; + void *onDebugMessage_userData; +}; + +class OPNMIDIplay +{ + friend void opn2_reset(struct OPN2_MIDIPlayer*); +public: + explicit OPNMIDIplay(unsigned long sampleRate = 22050); + ~OPNMIDIplay(); + + void applySetup(); + + void partialReset(); + void resetMIDI(); + +private: + void resetMIDIDefaults(int offset = 0); + +public: + /**********************Internal structures and classes**********************/ + + /** + * @brief Persistent settings for each MIDI channel + */ + struct MIDIchannel + { + //! Default MIDI volume + uint8_t def_volume; + //! Default LSB of a bend sensitivity + int def_bendsense_lsb; + //! Default MSB of a bend sensitivity + int def_bendsense_msb; + + //! LSB Bank number + uint8_t bank_lsb, + //! MSB Bank number + bank_msb; + //! Current patch number + uint8_t patch; + //! Volume level + uint8_t volume, + //! Expression level + expression; + //! Panning level + uint8_t panning, + //! Vibrato level + vibrato, + //! Channel aftertouch level + aftertouch; + //! Portamento time + uint16_t portamento; + //! Is Pedal sustain active + bool sustain; + //! Is Soft pedal active + bool softPedal; + //! Is portamento enabled + bool portamentoEnable; + //! Source note number used by portamento + int8_t portamentoSource; // note number or -1 + //! Portamento rate + double portamentoRate; + //! Per note Aftertouch values + uint8_t noteAftertouch[128]; + //! Is note aftertouch has any non-zero value + bool noteAfterTouchInUse; + //! Reserved + char _padding[6]; + //! Pitch bend value + int bend; + //! Pitch bend sensitivity + double bendsense; + //! Pitch bend sensitivity LSB value + int bendsense_lsb, + //! Pitch bend sensitivity MSB value + bendsense_msb; + //! Vibrato position value + double vibpos, + //! Vibrato speed value + vibspeed, + //! Vibrato depth value + vibdepth; + //! Vibrato delay time + int64_t vibdelay_us; + //! Last LSB part of RPN value received + uint8_t lastlrpn, + //! Last MSB poart of RPN value received + lastmrpn; + //! Interpret RPN value as NRPN + bool nrpn; + //! Brightness level + uint8_t brightness; + + //! Is melodic channel turned into percussion + bool is_xg_percussion; + + /** + * @brief Per-Note information + */ + struct NoteInfo + { + //! Note number + uint8_t note; + //! Current pressure + uint8_t vol; + //! Note vibrato (a part of Note Aftertouch feature) + uint8_t vibrato; + //! Tone selected on noteon: + int16_t noteTone; + //! Current tone (!= noteTone if gliding note) + double currentTone; + //! Gliding rate + double glideRate; + //! Patch selected on noteon; index to bank.ins[] + size_t midiins; + //! Is note the percussion instrument + bool isPercussion; + //! Note that plays missing instrument. Doesn't using any chip channels + bool isBlank; + //! Whether releasing and on extended life time defined by TTL + bool isOnExtendedLifeTime; + //! Time-to-live until release (short percussion note fix) + double ttl; + //! Patch selected + const OpnInstMeta *ains; + enum + { + MaxNumPhysChans = 2, + MaxNumPhysItemCount = MaxNumPhysChans + }; + + struct FindPredicate + { + explicit FindPredicate(unsigned note) + : note(note) {} + bool operator()(const NoteInfo &ni) const + { return ni.note == note; } + unsigned note; + }; + + /** + * @brief Reference to currently using chip channel + */ + struct Phys + { + //! Destination chip channel + uint16_t chip_chan; + //! ins, inde to adl[] + OpnTimbre ains; + + void assign(const Phys &oth) + { + ains = oth.ains; + } + bool operator==(const Phys &oth) const + { + return (ains == oth.ains); + } + bool operator!=(const Phys &oth) const + { + return !operator==(oth); + } + }; + + //! List of OPN2 channels it is currently occupying. + Phys chip_channels[MaxNumPhysItemCount]; + //! Count of used channels. + unsigned chip_channels_count; + + Phys *phys_find(unsigned chip_chan) + { + Phys *ph = NULL; + for(unsigned i = 0; i < chip_channels_count && !ph; ++i) + if(chip_channels[i].chip_chan == chip_chan) + ph = &chip_channels[i]; + return ph; + } + Phys *phys_find_or_create(uint16_t chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(!ph) { + if(chip_channels_count < MaxNumPhysItemCount) { + ph = &chip_channels[chip_channels_count++]; + ph->chip_chan = chip_chan; + } + } + return ph; + } + Phys *phys_ensure_find_or_create(uint16_t chip_chan) + { + Phys *ph = phys_find_or_create(chip_chan); + assert(ph); + return ph; + } + void phys_erase_at(const Phys *ph) + { + intptr_t pos = ph - chip_channels; + assert(pos < static_cast(chip_channels_count)); + for(intptr_t i = pos + 1; i < static_cast(chip_channels_count); ++i) + chip_channels[i - 1] = chip_channels[i]; + --chip_channels_count; + } + void phys_erase(unsigned chip_chan) + { + Phys *ph = phys_find(chip_chan); + if(ph) + phys_erase_at(ph); + } + }; + + //! Reserved + char _padding2[5]; + //! Count of gliding notes in this channel + unsigned gliding_note_count; + //! Count of notes having a TTL countdown in this channel + unsigned extended_note_count; + + //! Active notes in the channel + pl_list activenotes; + typedef pl_list::iterator notes_iterator; + typedef pl_list::const_iterator const_notes_iterator; + + void clear_all_phys_users(unsigned chip_chan) + { + for(pl_list::iterator it = activenotes.begin(); it != activenotes.end(); ) + { + NoteInfo::Phys *p = it->value.phys_find(chip_chan); + if(p) + { + it->value.phys_erase_at(p); + if(it->value.chip_channels_count == 0) + it = activenotes.erase(it); + else + ++it; + } + else + ++it; + } + } + + notes_iterator find_activenote(unsigned note) + { + return activenotes.find_if(NoteInfo::FindPredicate(note)); + } + + notes_iterator ensure_find_activenote(unsigned note) + { + notes_iterator it = find_activenote(note); + assert(!it.is_end()); + return it; + } + + notes_iterator find_or_create_activenote(unsigned note) + { + notes_iterator it = find_activenote(note); + if(!it.is_end()) + cleanupNote(it); + else + { + NoteInfo ni; + ni.note = note; + it = activenotes.insert(activenotes.end(), ni); + } + return it; + } + + notes_iterator create_activenote(unsigned note) + { + NoteInfo ni; + ni.note = note; + notes_iterator it = activenotes.insert(activenotes.end(), ni); + return it; + } + + notes_iterator ensure_find_or_create_activenote(unsigned note) + { + notes_iterator it = find_or_create_activenote(note); + assert(!it.is_end()); + return it; + } + + notes_iterator ensure_create_activenote(unsigned note) + { + notes_iterator it = create_activenote(note); + assert(!it.is_end()); + return it; + } + + /** + * @brief Reset channel into initial state + */ + void reset() + { + resetAllControllers(); + patch = 0; + vibpos = 0; + bank_lsb = 0; + bank_msb = 0; + lastlrpn = 0; + lastmrpn = 0; + nrpn = false; + is_xg_percussion = false; + } + + + void resetAllControllers() + { + volume = def_volume; + brightness = 127; + panning = 64; + + resetAllControllers121(); + } + + /** + * @brief Reset all MIDI controllers into initial state + */ + void resetAllControllers121() + { + bend = 0; + bendsense_msb = def_bendsense_msb; + bendsense_lsb = def_bendsense_lsb; + updateBendSensitivity(); + expression = 127; + sustain = false; + softPedal = false; + vibrato = 0; + aftertouch = 0; + std::memset(noteAftertouch, 0, 128); + noteAfterTouchInUse = false; + vibspeed = 2 * 3.141592653 * 5.0; + vibdepth = 0.5 / 127; + vibdelay_us = 0; + portamento = 0; + portamentoEnable = false; + portamentoSource = -1; + portamentoRate = HUGE_VAL; + } + + /** + * @brief Has channel vibrato to process + * @return + */ + bool hasVibrato() + { + return (vibrato > 0) || (aftertouch > 0) || noteAfterTouchInUse; + } + + /** + * @brief Commit pitch bend sensitivity value from MSB and LSB + */ + void updateBendSensitivity() + { + int cent = bendsense_msb * 128 + bendsense_lsb; + bendsense = cent * (1.0 / (128 * 8192)); + } + + /** + * @brief Clean up the state of the active note before removal + */ + void cleanupNote(notes_iterator i) + { + NoteInfo &info = i->value; + if(info.glideRate != HUGE_VAL) + --gliding_note_count; + if(info.ttl > 0) + --extended_note_count; + } + + MIDIchannel() : + def_volume(100), + def_bendsense_lsb(0), + def_bendsense_msb(2), + activenotes(128) + { + gliding_note_count = 0; + extended_note_count = 0; + reset(); + } + }; + + /** + * @brief Additional information about OPN2 channels + */ + struct OpnChannel + { + struct Location + { + uint16_t MidCh; + uint8_t note; + bool operator==(const Location &l) const + { return MidCh == l.MidCh && note == l.note; } + bool operator!=(const Location &l) const + { return !operator==(l); } + char _padding[1]; + }; + struct LocationData + { + Location loc; + enum { + Sustain_None = 0x00, + Sustain_Pedal = 0x01, + Sustain_Sostenuto = 0x02, + Sustain_ANY = Sustain_Pedal | Sustain_Sostenuto + }; + uint32_t sustained; + char _padding[3]; + MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[] + //! Has fixed sustain, don't iterate "on" timeout + bool fixed_sustain; + //! Timeout until note will be allowed to be killed by channel manager while it is on + int64_t kon_time_until_neglible_us; + int64_t vibdelay_us; + + struct FindPredicate + { + explicit FindPredicate(Location loc) + : loc(loc) {} + bool operator()(const LocationData &ld) const + { return ld.loc == loc; } + Location loc; + }; + }; + + //! Time left until sounding will be muted after key off + int64_t koff_time_until_neglible_us; + + //! Recently passed instrument, improves a goodness of released but busy channel when matching + MIDIchannel::NoteInfo::Phys recent_ins; + + pl_list users; + typedef pl_list::iterator users_iterator; + typedef pl_list::const_iterator const_users_iterator; + + users_iterator find_user(const Location &loc) + { + return users.find_if(LocationData::FindPredicate(loc)); + } + + users_iterator find_or_create_user(const Location &loc) + { + users_iterator it = find_user(loc); + if(it.is_end() && users.size() != users.capacity()) + { + LocationData ld; + ld.loc = loc; + it = users.insert(users.end(), ld); + } + return it; + } + + // For channel allocation: + OpnChannel(): koff_time_until_neglible_us(0), users(128) + { + std::memset(&recent_ins, 0, sizeof(MIDIchannel::NoteInfo::Phys)); + } + + OpnChannel(const OpnChannel &oth): koff_time_until_neglible_us(oth.koff_time_until_neglible_us), users(oth.users) + { + } + + OpnChannel &operator=(const OpnChannel &oth) + { + koff_time_until_neglible_us = oth.koff_time_until_neglible_us; + users = oth.users; + return *this; + } + + /** + * @brief Increases age of active note in microseconds time + * @param us Amount time in microseconds + */ + void addAge(int64_t us); + }; + +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + /** + * @brief MIDI files player sequencer + */ + AdlMIDI_UPtr m_sequencer; + + /** + * @brief Interface between MIDI sequencer and this library + */ + AdlMIDI_UPtr m_sequencerInterface; + + /** + * @brief Initialize MIDI sequencer interface + */ + void initSequencerInterface(); +#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER + + struct Setup + { + int emulator; + bool runAtPcmRate; + unsigned int OpnBank; + unsigned int numChips; + unsigned int LogarithmicVolumes; + int VolumeModel; + int lfoEnable; + int lfoFrequency; + int chipType; + //unsigned int SkipForward; + int ScaleModulators; + bool fullRangeBrightnessCC74; + bool enableAutoArpeggio; + + double delay; + double carry; + + /* The lag between visual content and audio content equals */ + /* the sum of these two buffers. */ + double mindelay; + double maxdelay; + + /* For internal usage */ + ssize_t tick_skip_samples_delay; /* Skip tick processing after samples count. */ + /* For internal usage */ + + unsigned long PCM_RATE; + }; + + /** + * @brief MIDI Marker entry + */ + struct MIDI_MarkerEntry + { + //! Label of marker + std::string label; + //! Absolute position in seconds + double pos_time; + //! Absolute position in ticks in the track + uint64_t pos_ticks; + }; + + //! Available MIDI Channels + std::vector m_midiChannels; + + //! SysEx device ID + uint8_t m_sysExDeviceId; + + /** + * @brief MIDI Synthesizer mode + */ + enum SynthMode + { + Mode_GM = 0x00, + Mode_GS = 0x01, + Mode_XG = 0x02, + Mode_GM2 = 0x04 + }; + //! MIDI Synthesizer mode + uint32_t m_synthMode; + + //! Installed function hooks + MIDIEventHooks hooks; + +private: + //! Per-track MIDI devices map + std::map m_midiDevices; + //! Current MIDI device per track + std::map m_currentMidiDevice; + + //! Chip channels map + std::vector m_chipChannels; + //! Counter of arpeggio processing + size_t m_arpeggioCounter; + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + //! Audio tick counter + uint32_t m_audioTickCounter; +#endif + + //! Local error string + std::string errorStringOut; + + //! Missing instruments catches + std::set caugh_missing_instruments; + //! Missing melodic banks catches + std::set caugh_missing_banks_melodic; + //! Missing percussion banks catches + std::set caugh_missing_banks_percussion; + +public: + + const std::string &getErrorString(); + void setErrorString(const std::string &err); + + //! OPN2 Chip manager + AdlMIDI_UPtr m_synth; + + //! Generator output buffer + int32_t m_outBuf[1024]; + + //! Synthesizer setup + Setup m_setup; + + /** + * @brief Load bank from file + * @param filename Path to bank file + * @return true on succes + */ + bool LoadBank(const std::string &filename); + + /** + * @brief Load bank from memory block + * @param data Pointer to memory block where raw bank file is stored + * @param size Size of given memory block + * @return true on succes + */ + bool LoadBank(const void *data, size_t size); + + /** + * @brief Load bank from opened FileAndMemReader class + * @param fr Instance with opened file + * @return true on succes + */ + bool LoadBank(FileAndMemReader &fr); + +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + /** + * @brief MIDI file loading pre-process + * @return true on success, false on failure + */ + bool LoadMIDI_pre(); + + /** + * @brief MIDI file loading post-process + * @return true on success, false on failure + */ + bool LoadMIDI_post(); + + /** + * @brief Load music file from a file + * @param filename Path to music file + * @return true on success, false on failure + */ + + bool LoadMIDI(const std::string &filename); + + /** + * @brief Load music file from the memory block + * @param data pointer to the memory block + * @param size size of memory block + * @return true on success, false on failure + */ + bool LoadMIDI(const void *data, size_t size); + + /** + * @brief Periodic tick handler. + * @param s seconds since last call + * @param granularity don't expect intervals smaller than this, in seconds + * @return desired number of seconds until next call + */ + double Tick(double s, double granularity); +#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER + + /** + * @brief Process extra iterators like vibrato or arpeggio + * @param s seconds since last call + */ + void TickIterators(double s); + + + /* RealTime event triggers */ + /** + * @brief Reset state of all channels + */ + void realTime_ResetState(); + + /** + * @brief Note On event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param velocity Velocity level (from 0 to 127) + * @return true if Note On event was accepted + */ + bool realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity); + + /** + * @brief Note Off event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + */ + void realTime_NoteOff(uint8_t channel, uint8_t note); + + /** + * @brief Note aftertouch event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param atVal After-Touch level (from 0 to 127) + */ + void realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal); + + /** + * @brief Channel aftertouch event + * @param channel MIDI channel + * @param atVal After-Touch level (from 0 to 127) + */ + void realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal); + + /** + * @brief Controller Change event + * @param channel MIDI channel + * @param type Type of controller + * @param value Value of the controller (from 0 to 127) + */ + void realTime_Controller(uint8_t channel, uint8_t type, uint8_t value); + + /** + * @brief Patch change + * @param channel MIDI channel + * @param patch Patch Number (from 0 to 127) + */ + void realTime_PatchChange(uint8_t channel, uint8_t patch); + + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param pitch Concoctated raw pitch value + */ + void realTime_PitchBend(uint8_t channel, uint16_t pitch); + + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param msb MSB of pitch value + * @param lsb LSB of pitch value + */ + void realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb); + + /** + * @brief LSB Bank Change CC + * @param channel MIDI channel + * @param lsb LSB value of bank number + */ + void realTime_BankChangeLSB(uint8_t channel, uint8_t lsb); + + /** + * @brief MSB Bank Change CC + * @param channel MIDI channel + * @param lsb MSB value of bank number + */ + void realTime_BankChangeMSB(uint8_t channel, uint8_t msb); + + /** + * @brief Bank Change (united value) + * @param channel MIDI channel + * @param bank Bank number value + */ + void realTime_BankChange(uint8_t channel, uint16_t bank); + + /** + * @brief Sets the Device identifier + * @param id 7-bit Device identifier + */ + void setDeviceId(uint8_t id); + + /** + * @brief System Exclusive message + * @param msg Raw SysEx Message + * @param size Length of SysEx message + * @return true if message was passed successfully. False on any errors + */ + bool realTime_SysEx(const uint8_t *msg, size_t size); + + /** + * @brief Turn off all notes and mute the sound of releasing notes + */ + void realTime_panic(); + + /** + * @brief Device switch (to extend 16-channels limit of MIDI standard) + * @param track MIDI track index + * @param data Device name + * @param length Length of device name string + */ + void realTime_deviceSwitch(size_t track, const char *data, size_t length); + + /** + * @brief Currently selected device index + * @param track MIDI track index + * @return Multiple 16 value + */ + size_t realTime_currentDevice(size_t track); + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + // Audio rate tick handler + void AudioTick(uint32_t chipId, uint32_t rate); +#endif + +private: + /** + * @brief Hardware manufacturer (Used for SysEx) + */ + enum + { + Manufacturer_Roland = 0x41, + Manufacturer_Yamaha = 0x43, + Manufacturer_UniversalNonRealtime = 0x7E, + Manufacturer_UniversalRealtime = 0x7F + }; + + /** + * @brief Roland Mode (Used for SysEx) + */ + enum + { + RolandMode_Request = 0x11, + RolandMode_Send = 0x12 + }; + + /** + * @brief Device model (Used for SysEx) + */ + enum + { + RolandModel_GS = 0x42, + RolandModel_SC55 = 0x45, + YamahaModel_XG = 0x4C + }; + + /** + * @brief Process generic SysEx events + * @param dev Device ID + * @param realtime Is real-time event + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Roland devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doRolandSysEx(unsigned dev, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Yamaha devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size); + +private: + /** + * @brief Note Update properties + */ + enum + { + Upd_Patch = 0x1, + Upd_Pan = 0x2, + Upd_Volume = 0x4, + Upd_Pitch = 0x8, + Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch, + Upd_Off = 0x20, + Upd_Mute = 0x40, + Upd_OffMute = Upd_Off + Upd_Mute + }; + + /** + * @brief Update active note + * @param MidCh MIDI Channel where note is processing + * @param i Iterator that points to active note in the MIDI channel + * @param props_mask Properties to update + * @param select_adlchn Specify chip channel, or -1 - all chip channels used by the note + */ + void noteUpdate(size_t midCh, + MIDIchannel::notes_iterator i, + unsigned props_mask, + int32_t select_adlchn = -1); + + void noteUpdateAll(size_t midCh, unsigned props_mask); + + /** + * @brief Determine how good a candidate this adlchannel would be for playing a note from this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * @return Calculated coodness points + */ + int64_t calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const; + + /** + * @brief A new note will be played on this channel using this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * Kill existing notes on this channel (or don't, if we do arpeggio) + */ + void prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); + + /** + * @brief Kills note that uses wanted channel. When arpeggio is possible, note is evaluating to another channel + * @param from_channel Wanted chip channel + * @param j Chip channel instance + * @param i MIDI Channel active note instance + */ + void killOrEvacuate( + size_t from_channel, + OpnChannel::users_iterator j, + MIDIchannel::notes_iterator i); + + /** + * @brief Off all notes and silence sound + */ + void panic(); + + /** + * @brief Kill note, sustaining by pedal or sostenuto + * @param MidCh MIDI channel, -1 - all MIDI channels + * @param this_adlchn Chip channel, -1 - all chip channels + * @param sustain_type Type of systain to process + */ + void killSustainingNotes(int32_t midCh = -1, + int32_t this_adlchn = -1, + uint32_t sustain_type = OpnChannel::LocationData::Sustain_ANY); + /** + * @brief Find active notes and mark them as sostenuto-sustained + * @param MidCh MIDI channel, -1 - all MIDI channels + */ + void markSostenutoNotes(int32_t midCh = -1); + + /** + * @brief Set RPN event value + * @param MidCh MIDI channel + * @param value 1 byte part of RPN value + * @param MSB is MSB or LSB part of value + */ + void setRPN(size_t midCh, unsigned value, bool MSB); + + /** + * @brief Update portamento setup in MIDI channel + * @param midCh MIDI channel where portamento needed to be updated + */ + void updatePortamento(size_t midCh); + + /** + * @brief Off the note + * @param midCh MIDI channel + * @param note Note to off + * @param forceNow Do not delay the key-off to a later time + */ + void noteOff(size_t midCh, uint8_t note, bool forceNow = false); + + /** + * @brief Update processing of vibrato to amount of seconds + * @param amount Amount value in seconds + */ + void updateVibrato(double amount); + + /** + * @brief Update auto-arpeggio + * @param amount Amount value in seconds [UNUSED] + */ + void updateArpeggio(double /*amount*/); + + /** + * @brief Update Portamento gliding to amount of seconds + * @param amount Amount value in seconds + */ + void updateGlide(double amount); + +public: + /** + * @brief Checks was device name used or not + * @param name Name of MIDI device + * @return Offset of the MIDI Channels, multiple to 16 + */ + size_t chooseDevice(const std::string &name); + + /** + * @brief Gets a textual description of the state of chip channels + * @param text character pointer for text + * @param attr character pointer for text attributes + * @param size number of characters available to write + */ + void describeChannels(char *text, char *attr, size_t size); +}; + +#endif // OPNMIDI_MIDIPLAY_HPP diff --git a/thirdparty/opnmidi/opnmidi_opn2.cpp b/thirdparty/opnmidi/opnmidi_opn2.cpp new file mode 100644 index 000000000..dfa334764 --- /dev/null +++ b/thirdparty/opnmidi/opnmidi_opn2.cpp @@ -0,0 +1,747 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * OPNMIDI Library and YM2612 support: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "opnmidi_opn2.hpp" +#include "opnmidi_private.hpp" + +#if defined(OPNMIDI_DISABLE_NUKED_EMULATOR) && defined(OPNMIDI_DISABLE_MAME_EMULATOR) && \ + defined(OPNMIDI_DISABLE_GENS_EMULATOR) && defined(OPNMIDI_DISABLE_GX_EMULATOR) && \ + defined(OPNMIDI_DISABLE_NP2_EMULATOR) && defined(OPNMIDI_DISABLE_MAME_2608_EMULATOR) && \ + defined(OPNMIDI_DISABLE_PMDWIN_EMULATOR) +#error "No emulators enabled. You must enable at least one emulator to use this library!" +#endif + +// Nuked OPN2 emulator, Most accurate, but requires the powerful CPU +#ifndef OPNMIDI_DISABLE_NUKED_EMULATOR +#include "chips/nuked_opn2.h" +#endif + +// MAME YM2612 emulator, Well-accurate and fast +#ifndef OPNMIDI_DISABLE_MAME_EMULATOR +#include "chips/mame_opn2.h" +#endif + +// GENS 2.10 emulator, very outdated and inaccurate, but gives the best performance +#ifndef OPNMIDI_DISABLE_GENS_EMULATOR +#include "chips/gens_opn2.h" +#endif + +//// Genesis Plus GX emulator, Variant of MAME with enhancements +//#ifndef OPNMIDI_DISABLE_GX_EMULATOR +//#include "chips/gx_opn2.h" +//#endif + +// Neko Project II OPNA emulator +#ifndef OPNMIDI_DISABLE_NP2_EMULATOR +#include "chips/np2_opna.h" +#endif + +// MAME YM2608 emulator +#ifndef OPNMIDI_DISABLE_MAME_2608_EMULATOR +#include "chips/mame_opna.h" +#endif + +//// PMDWin OPNA emulator +//#ifndef OPNMIDI_DISABLE_PMDWIN_EMULATOR +//#include "chips/pmdwin_opna.h" +//#endif + +// YMFM emulators +#ifndef OPNMIDI_DISABLE_YMFM_EMULATOR +#include "chips/ymfm_opn2.h" +#include "chips/ymfm_opna.h" +#endif + + +// VGM File dumper +#ifdef OPNMIDI_MIDI2VGM +#include "chips/vgm_file_dumper.h" +#endif + +static const unsigned opn2_emulatorSupport = 0 +#ifndef OPNMIDI_DISABLE_NUKED_EMULATOR + | (1u << OPNMIDI_EMU_NUKED_YM2612) + | (1u << OPNMIDI_EMU_NUKED_YM3438) +#endif +#ifndef OPNMIDI_DISABLE_MAME_EMULATOR + | (1u << OPNMIDI_EMU_MAME) +#endif +#ifndef OPNMIDI_DISABLE_GENS_EMULATOR + | (1u << OPNMIDI_EMU_GENS) +#endif +#ifndef OPNMIDI_DISABLE_YMFM_EMULATOR + | (1u << OPNMIDI_EMU_YMFM_OPN2) + | (1u << OPNMIDI_EMU_YMFM_OPNA) +#endif +//#ifndef OPNMIDI_DISABLE_GX_EMULATOR +// | (1u << OPNMIDI_EMU_GX) +//#endif +#ifndef OPNMIDI_DISABLE_NP2_EMULATOR + | (1u << OPNMIDI_EMU_NP2) +#endif +#ifndef OPNMIDI_DISABLE_MAME_2608_EMULATOR + | (1u << OPNMIDI_EMU_MAME_2608) +#endif +//#ifndef OPNMIDI_DISABLE_PMDWIN_EMULATOR +// | (1u << OPNMIDI_EMU_PMDWIN) +//#endif +#ifdef OPNMIDI_MIDI2VGM + | (1u << OPNMIDI_VGM_DUMPER) +#endif +; + +//! Check emulator availability +bool opn2_isEmulatorAvailable(int emulator) +{ + return (opn2_emulatorSupport & (1u << (unsigned)emulator)) != 0; +} + +//! Find highest emulator +int opn2_getHighestEmulator() +{ + int emu = -1; + for(unsigned m = opn2_emulatorSupport; m > 0; m >>= 1) + ++emu; + return emu; +} + +//! Find lowest emulator +int opn2_getLowestEmulator() +{ + int emu = -1; + unsigned m = opn2_emulatorSupport; + if(m > 0) + { + for(emu = 0; (m & 1) == 0; m >>= 1) + ++emu; + } + return emu; +} + + + +/*************************************************************** + * Volume model tables * + ***************************************************************/ + +// Mapping from MIDI volume level to OPL level value. + +static const uint_fast32_t s_dmx_volume_model[128] = +{ + 0, 1, 3, 5, 6, 8, 10, 11, + 13, 14, 16, 17, 19, 20, 22, 23, + 25, 26, 27, 29, 30, 32, 33, 34, + 36, 37, 39, 41, 43, 45, 47, 49, + 50, 52, 54, 55, 57, 59, 60, 61, + 63, 64, 66, 67, 68, 69, 71, 72, + 73, 74, 75, 76, 77, 79, 80, 81, + 82, 83, 84, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 92, 93, 94, 95, + 96, 96, 97, 98, 99, 99, 100, 101, + 101, 102, 103, 103, 104, 105, 105, 106, + 107, 107, 108, 109, 109, 110, 110, 111, + 112, 112, 113, 113, 114, 114, 115, 115, + 116, 117, 117, 118, 118, 119, 119, 120, + 120, 121, 121, 122, 122, 123, 123, 123, + 124, 124, 125, 125, 126, 126, 127, 127, +}; + +static const uint_fast32_t W9X_volume_mapping_table[32] = +{ + 63, 63, 40, 36, 32, 28, 23, 21, + 19, 17, 15, 14, 13, 12, 11, 10, + 9, 8, 7, 6, 5, 5, 4, 4, + 3, 3, 2, 2, 1, 1, 0, 0 +}; + +static const uint32_t g_noteChannelsMap[6] = { 0, 1, 2, 4, 5, 6 }; + +static inline void getOpnChannel(size_t in_channel, + size_t &out_chip, + uint8_t &out_port, + uint32_t &out_ch) +{ + out_chip = in_channel / 6; + size_t ch4 = in_channel % 6; + out_port = ((ch4 < 3) ? 0 : 1); + out_ch = static_cast(ch4 % 3); +} + + +/*************************************************************** + * Standard frequency formula * + * *************************************************************/ + +static inline double s_commonFreq(double tone) +{ + return std::exp(0.057762265 * tone); +} + + + +enum +{ + MasterVolumeDefault = 127 +}; + +enum +{ + OPN_PANNING_LEFT = 0x80, + OPN_PANNING_RIGHT = 0x40, + OPN_PANNING_BOTH = 0xC0 +}; + +static OpnInstMeta makeEmptyInstrument() +{ + OpnInstMeta ins; + memset(&ins, 0, sizeof(OpnInstMeta)); + ins.flags = OpnInstMeta::Flag_NoSound; + return ins; +} + +const OpnInstMeta OPN2::m_emptyInstrument = makeEmptyInstrument(); + +OPN2::OPN2() : + m_regLFOSetup(0), + m_numChips(1), + m_scaleModulators(false), + m_runAtPcmRate(false), + m_softPanning(false), + m_masterVolume(MasterVolumeDefault), + m_musicMode(MODE_MIDI), + m_volumeScale(VOLUME_Generic), + m_channelAlloc(OPNMIDI_ChanAlloc_AUTO), + m_lfoEnable(false), + m_lfoFrequency(0), + m_chipFamily(OPNChip_OPN2) +{ + m_insBankSetup.volumeModel = OPN2::VOLUME_Generic; + m_insBankSetup.lfoEnable = false; + m_insBankSetup.lfoFrequency = 0; + m_insBankSetup.chipType = OPNChip_OPN2; + m_insBankSetup.mt32defaults = false; + + // Initialize blank instruments banks + m_insBanks.clear(); +} + +OPN2::~OPN2() +{ + clearChips(); +} + +bool OPN2::setupLocked() +{ + return (m_musicMode == MODE_CMF || + m_musicMode == MODE_IMF || + m_musicMode == MODE_RSXX); +} + +void OPN2::writeReg(size_t chip, uint8_t port, uint8_t index, uint8_t value) +{ + m_chips[chip]->writeReg(port, index, value); +} + +void OPN2::writeRegI(size_t chip, uint8_t port, uint32_t index, uint32_t value) +{ + m_chips[chip]->writeReg(port, static_cast(index), static_cast(value)); +} + +void OPN2::writePan(size_t chip, uint32_t index, uint32_t value) +{ + m_chips[chip]->writePan(static_cast(index), static_cast(value)); +} + +void OPN2::noteOff(size_t c) +{ + size_t chip; + uint8_t port; + uint32_t cc; + size_t ch4 = c % 6; + getOpnChannel(c, chip, port, cc); + writeRegI(chip, 0, 0x28, g_noteChannelsMap[ch4]); +} + +void OPN2::noteOn(size_t c, double tone) +{ + // Hertz range: 0..131071 + double hertz = s_commonFreq(tone); + + if(hertz < 0) // Avoid infinite loop + return; + + double coef; + switch(m_chipFamily) + { + case OPNChip_OPN2: default: + coef = 321.88557; break; + case OPNChip_OPNA: + coef = 309.12412; break; + } + hertz *= coef; + + size_t chip; + uint8_t port; + uint32_t cc; + size_t ch4 = c % 6; + getOpnChannel(c, chip, port, cc); + + uint32_t octave = 0, ftone = 0, mul_offset = 0; + const OpnTimbre &adli = m_insCache[c]; + + //Basic range until max of octaves reaching + while((hertz >= 1023.75) && (octave < 0x3800)) + { + hertz /= 2.0; // Calculate octave + octave += 0x800; + } + //Extended range, rely on frequency multiplication increment + while(hertz >= 2036.75) + { + hertz /= 2.0; // Calculate octave + mul_offset++; + } + ftone = octave + static_cast(hertz + 0.5); + + for(size_t op = 0; op < 4; op++) + { + uint32_t reg = adli.OPS[op].data[0]; + uint16_t address = static_cast(0x30 + (op * 4) + cc); + if(mul_offset > 0) // Increase frequency multiplication value + { + uint32_t dt = reg & 0xF0; + uint32_t mul = reg & 0x0F; + if((mul + mul_offset) > 0x0F) + { + mul_offset = 0; + mul = 0x0F; + } + writeRegI(chip, port, address, uint8_t(dt | (mul + mul_offset))); + } + else + { + writeRegI(chip, port, address, uint8_t(reg)); + } + } + + writeRegI(chip, port, 0xA4 + cc, (ftone>>8) & 0xFF);//Set frequency and octave + writeRegI(chip, port, 0xA0 + cc, ftone & 0xFF); + writeRegI(chip, 0, 0x28, 0xF0 + g_noteChannelsMap[ch4]); +} + +void OPN2::touchNote(size_t c, + uint_fast32_t velocity, + uint_fast32_t channelVolume, + uint_fast32_t channelExpression, + uint8_t brightness) +{ + size_t chip; + uint8_t port; + uint32_t cc; + getOpnChannel(c, chip, port, cc); + + const OpnTimbre &adli = m_insCache[c]; + + uint_fast32_t volume = 0; + + uint8_t op_vol[4] = + { + adli.OPS[OPERATOR1].data[1], + adli.OPS[OPERATOR2].data[1], + adli.OPS[OPERATOR3].data[1], + adli.OPS[OPERATOR4].data[1], + }; + + bool alg_do[8][4] = + { + /* + * Yeah, Operator 2 and 3 are seems swapped + * which we can see in the algorithm 4 + */ + //OP1 OP3 OP2 OP4 + //30 34 38 3C + {false,false,false,true},//Algorithm #0: W = 1 * 2 * 3 * 4 + {false,false,false,true},//Algorithm #1: W = (1 + 2) * 3 * 4 + {false,false,false,true},//Algorithm #2: W = (1 + (2 * 3)) * 4 + {false,false,false,true},//Algorithm #3: W = ((1 * 2) + 3) * 4 + {false,false,true, true},//Algorithm #4: W = (1 * 2) + (3 * 4) + {false,true ,true ,true},//Algorithm #5: W = (1 * (2 + 3 + 4) + {false,true ,true ,true},//Algorithm #6: W = (1 * 2) + 3 + 4 + {true ,true ,true ,true},//Algorithm #7: W = 1 + 2 + 3 + 4 + }; + + switch(m_volumeScale) + { + default: + case Synth::VOLUME_Generic: + { + volume = velocity * m_masterVolume * + channelVolume * channelExpression; + + /* If the channel has arpeggio, the effective volume of + * *this* instrument is actually lower due to timesharing. + * To compensate, add extra volume that corresponds to the + * time this note is *not* heard. + * Empirical tests however show that a full equal-proportion + * increment sounds wrong. Therefore, using the square root. + */ + //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() )); + const double c1 = 11.541560327111707; + const double c2 = 1.601379199767093e+02; + const uint_fast32_t minVolume = 1108075; // 8725 * 127 + + // The formula below: SOLVE(V=127^4 * 2^( (A-63.49999) / 8), A) + if(volume > minVolume) + { + double lv = std::log(static_cast(volume)); + volume = static_cast(lv * c1 - c2) * 2; + } + else + volume = 0; + } + break; + + case Synth::VOLUME_NATIVE: + { + volume = velocity * channelVolume * channelExpression; + //volume = volume * m_masterVolume / (127 * 127 * 127) / 2; + volume = (volume * m_masterVolume) / 4096766; + + if(volume > 0) + volume += 64;//OPN has 0~127 range. As 0...63 is almost full silence, but at 64 to 127 is very closed to OPL3, just add 64. + } + break; + + case Synth::VOLUME_DMX: + { + volume = (channelVolume * channelExpression * m_masterVolume) / 16129; + volume = (s_dmx_volume_model[volume] + 1) << 1; + volume = (s_dmx_volume_model[(velocity < 128) ? velocity : 127] * volume) >> 9; + + if(volume > 0) + volume += 64;//OPN has 0~127 range. As 0...63 is almost full silence, but at 64 to 127 is very closed to OPL3, just add 64. + } + break; + + case Synth::VOLUME_APOGEE: + { + volume = (channelVolume * channelExpression * m_masterVolume / 16129); + volume = ((64 * (velocity + 0x80)) * volume) >> 15; + //volume = ((63 * (vol + 0x80)) * Ch[MidCh].volume) >> 15; + if(volume > 0) + volume += 64;//OPN has 0~127 range. As 0...63 is almost full silence, but at 64 to 127 is very closed to OPL3, just add 64. + } + break; + + case Synth::VOLUME_9X: + { + //volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * 127 / 16129 /*2048383*/) >> 2)]; + volume = 63 - W9X_volume_mapping_table[((velocity * channelVolume * channelExpression * m_masterVolume / 2048383) >> 2)]; + //volume = W9X_volume_mapping_table[vol >> 2] + volume; + if(volume > 0) + volume += 64;//OPN has 0~127 range. As 0...63 is almost full silence, but at 64 to 127 is very closed to OPL3, just add 64. + } + break; + } + + + if(volume > 127) + volume = 127; + + uint8_t alg = adli.fbalg & 0x07; + for(uint8_t op = 0; op < 4; op++) + { + bool do_op = alg_do[alg][op] || m_scaleModulators; + uint32_t x = op_vol[op]; + uint32_t vol_res = do_op ? (127 - (static_cast(volume) * (127 - (x & 127))) / 127) : x; + if(brightness != 127) + { + brightness = static_cast(::round(127.0 * ::sqrt((static_cast(brightness)) * (1.0 / 127.0)))); + if(!do_op) + vol_res = (127 - (brightness * (127 - (static_cast(vol_res) & 127))) / 127); + } + writeRegI(chip, port, 0x40 + cc + (4 * op), vol_res); + } + // Correct formula (ST3, AdPlug): + // 63-((63-(instrvol))/63)*chanvol + // Reduces to (tested identical): + // 63 - chanvol + chanvol*instrvol/63 + // Also (slower, floats): + // 63 + chanvol * (instrvol / 63.0 - 1) +} + +void OPN2::setPatch(size_t c, const OpnTimbre &instrument) +{ + size_t chip; + uint8_t port; + uint32_t cc; + getOpnChannel(c, chip, port, cc); + m_insCache[c] = instrument; + for(uint8_t d = 0; d < 7; d++) + { + for(uint8_t op = 0; op < 4; op++) + writeRegI(chip, port, 0x30 + (0x10 * d) + (op * 4) + cc, instrument.OPS[op].data[d]); + } + + writeRegI(chip, port, 0xB0 + cc, instrument.fbalg);//Feedback/Algorithm + m_regLFOSens[c] = (m_regLFOSens[c] & 0xC0) | (instrument.lfosens & 0x3F); + writeRegI(chip, port, 0xB4 + cc, m_regLFOSens[c]);//Panorame and LFO bits +} + +void OPN2::setPan(size_t c, uint8_t value) +{ + size_t chip; + uint8_t port; + uint32_t cc; + getOpnChannel(c, chip, port, cc); + const OpnTimbre &adli = m_insCache[c]; + uint8_t val = 0; + if(m_softPanning) + { + val = (OPN_PANNING_BOTH & 0xC0) | (adli.lfosens & 0x3F); + writePan(chip, c % 6, value); + writeRegI(chip, port, 0xB4 + cc, val); + } + else + { + int panning = 0; + if(value < 64 + 16) panning |= OPN_PANNING_LEFT; + if(value >= 64 - 16) panning |= OPN_PANNING_RIGHT; + val = (panning & 0xC0) | (adli.lfosens & 0x3F); + writePan(chip, c % 6, 64); + writeRegI(chip, port, 0xB4 + cc, val); + } + m_regLFOSens[c] = val; +} + +void OPN2::silenceAll() // Silence all OPL channels. +{ + for(size_t c = 0; c < m_numChannels; ++c) + { + noteOff(c); + touchNote(c, 0); + } +} + +void OPN2::commitLFOSetup() +{ + uint8_t regLFOSetup = (m_lfoEnable ? 8 : 0) | (m_lfoFrequency & 7); + m_regLFOSetup = regLFOSetup; + for(size_t chip = 0; chip < m_numChips; ++chip) + writeReg(chip, 0, 0x22, regLFOSetup); +} + +void OPN2::setVolumeScaleModel(OPNMIDI_VolumeModels volumeModel) +{ + switch(volumeModel) + { + default: + case OPNMIDI_VolumeModel_AUTO://Do nothing until restart playing + break; + + case OPNMIDI_VolumeModel_Generic: + m_volumeScale = OPN2::VOLUME_Generic; + break; + + case OPNMIDI_VolumeModel_NativeOPN2: + m_volumeScale = OPN2::VOLUME_NATIVE; + break; + + case OPNMIDI_VolumeModel_DMX: + m_volumeScale = OPN2::VOLUME_DMX; + break; + + case OPNMIDI_VolumeModel_APOGEE: + m_volumeScale = OPN2::VOLUME_APOGEE; + break; + + case OPNMIDI_VolumeModel_9X: + m_volumeScale = OPN2::VOLUME_9X; + break; + } +} + +OPNMIDI_VolumeModels OPN2::getVolumeScaleModel() +{ + switch(m_volumeScale) + { + default: + case OPN2::VOLUME_Generic: + return OPNMIDI_VolumeModel_Generic; + case OPN2::VOLUME_NATIVE: + return OPNMIDI_VolumeModel_NativeOPN2; + case OPN2::VOLUME_DMX: + return OPNMIDI_VolumeModel_DMX; + case OPN2::VOLUME_APOGEE: + return OPNMIDI_VolumeModel_APOGEE; + case OPN2::VOLUME_9X: + return OPNMIDI_VolumeModel_9X; + } +} + +void OPN2::clearChips() +{ + for(size_t i = 0; i < m_chips.size(); i++) + m_chips[i].reset(NULL); + m_chips.clear(); +} + +void OPN2::reset(int emulator, unsigned long PCM_RATE, OPNFamily family, void *audioTickHandler) +{ +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + ADL_UNUSED(audioTickHandler); +#endif + clearChips(); + m_insCache.clear(); + m_regLFOSens.clear(); +#ifdef OPNMIDI_MIDI2VGM + if(emulator == OPNMIDI_VGM_DUMPER && (m_numChips > 2)) + m_numChips = 2;// VGM Dumper can't work in multichip mode +#endif + m_chips.clear(); + m_chips.resize(m_numChips, AdlMIDI_SPtr()); + +#ifdef OPNMIDI_MIDI2VGM + m_loopStartHook = NULL; + m_loopStartHookData = NULL; + m_loopEndHook = NULL; + m_loopEndHookData = NULL; +#endif + + for(size_t i = 0; i < m_chips.size(); i++) + { + OPNChipBase *chip = NULL; + + switch(emulator) + { + default: + assert(false); + abort(); +#ifndef OPNMIDI_DISABLE_MAME_EMULATOR + case OPNMIDI_EMU_MAME: + chip = new MameOPN2(family); + break; +#endif +#ifndef OPNMIDI_DISABLE_NUKED_EMULATOR + case OPNMIDI_EMU_NUKED_YM3438: + chip = new NukedOPN2(family, true); + break; + case OPNMIDI_EMU_NUKED_YM2612: + chip = new NukedOPN2(family, false); + break; +#endif +#ifndef OPNMIDI_DISABLE_GENS_EMULATOR + case OPNMIDI_EMU_GENS: + chip = new GensOPN2(family); + break; +#endif +#ifndef OPNMIDI_DISABLE_YMFM_EMULATOR + case OPNMIDI_EMU_YMFM_OPN2: + chip = new YmFmOPN2(family); + break; +#endif +//#ifndef OPNMIDI_DISABLE_GX_EMULATOR +// case OPNMIDI_EMU_GX: +// chip = new GXOPN2(family); +// break; +//#endif +#ifndef OPNMIDI_DISABLE_NP2_EMULATOR + case OPNMIDI_EMU_NP2: + chip = new NP2OPNA<>(family); + break; +#endif +#ifndef OPNMIDI_DISABLE_MAME_2608_EMULATOR + case OPNMIDI_EMU_MAME_2608: + chip = new MameOPNA(family); + break; +#endif +#ifndef OPNMIDI_DISABLE_YMFM_EMULATOR + case OPNMIDI_EMU_YMFM_OPNA: + chip = new YmFmOPNA(family); + break; +#endif +//#ifndef OPNMIDI_DISABLE_PMDWIN_EMULATOR +// case OPNMIDI_EMU_PMDWIN: +// chip = new PMDWinOPNA(family); +// break; +//#endif +#ifdef OPNMIDI_MIDI2VGM + case OPNMIDI_VGM_DUMPER: + chip = new VGMFileDumper(family, i, (i == 0 ? NULL : m_chips[0].get())); + if(i == 0)//Set hooks for first chip only + { + m_loopStartHook = &VGMFileDumper::loopStartHook; + m_loopStartHookData = chip; + m_loopEndHook = &VGMFileDumper::loopEndHook; + m_loopEndHookData = chip; + } + break; +#endif + } + + m_chips[i].reset(chip); + chip->setChipId(static_cast(i)); + chip->setRate(static_cast(PCM_RATE), chip->nativeClockRate()); + + if(m_runAtPcmRate) + chip->setRunningAtPcmRate(true); + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + chip->setAudioTickHandlerInstance(audioTickHandler); +#endif + family = chip->family(); + } + + m_chipFamily = family; + m_numChannels = m_numChips * 6; + m_insCache.resize(m_numChannels, m_emptyInstrument.op[0]); + m_regLFOSens.resize(m_numChannels, 0); + + uint8_t regLFOSetup = (m_lfoEnable ? 8 : 0) | (m_lfoFrequency & 7); + m_regLFOSetup = regLFOSetup; + + for(size_t chip = 0; chip < m_numChips; ++chip) + { + writeReg(chip, 0, 0x22, regLFOSetup);//push current LFO state + writeReg(chip, 0, 0x27, 0x00); //set Channel 3 normal mode + writeReg(chip, 0, 0x2B, 0x00); //Disable DAC + //Shut up all channels + writeReg(chip, 0, 0x28, 0x00); //Note Off 0 channel + writeReg(chip, 0, 0x28, 0x01); //Note Off 1 channel + writeReg(chip, 0, 0x28, 0x02); //Note Off 2 channel + writeReg(chip, 0, 0x28, 0x04); //Note Off 3 channel + writeReg(chip, 0, 0x28, 0x05); //Note Off 4 channel + writeReg(chip, 0, 0x28, 0x06); //Note Off 5 channel + } + + silenceAll(); +#ifdef OPNMIDI_MIDI2VGM + if(m_loopStartHook) // Post-initialization Loop Start hook (fix for loop edge passing clicks) + m_loopStartHook(m_loopStartHookData); +#endif +} + +OPNFamily OPN2::chipFamily() const +{ + return m_chipFamily; +} diff --git a/thirdparty/opnmidi/opnmidi_opn2.hpp b/thirdparty/opnmidi/opnmidi_opn2.hpp new file mode 100644 index 000000000..e277981e9 --- /dev/null +++ b/thirdparty/opnmidi/opnmidi_opn2.hpp @@ -0,0 +1,289 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * OPNMIDI Library and YM2612 support: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OPNMIDI_OPN2_HPP +#define OPNMIDI_OPN2_HPP + +#include "opnbank.h" +#include "opnmidi_ptr.hpp" +#include "opnmidi_private.hpp" +#include "opnmidi_bankmap.h" +#include "chips/opn_chip_family.h" + +/** + * @brief OPN2 Chip management class + */ +class OPN2 +{ + friend class OPNMIDIplay; +public: + enum { PercussionTag = 1 << 15 }; + + //! Total number of chip channels between all running emulators + uint32_t m_numChannels; + //! Just a padding. Reserved. + char _padding[4]; + //! Running chip emulators + std::vector > m_chips; +#ifdef OPNMIDI_MIDI2VGM + //! Loop Start hook + void (*m_loopStartHook)(void*); + //! Loop Start hook data + void *m_loopStartHookData; + //! Loop End hook + void (*m_loopEndHook)(void*); + //! Loop End hook data + void *m_loopEndHookData; +#endif +private: + //! Cached patch data, needed by Touch() + std::vector m_insCache; + //! Cached per-channel LFO sensitivity flags + std::vector m_regLFOSens; + //! LFO setup registry cache + uint8_t m_regLFOSetup; + +public: + /** + * @brief MIDI bank entry + */ + struct Bank + { + //! MIDI Bank instruments + OpnInstMeta ins[128]; + }; + typedef BasicBankMap BankMap; + //! MIDI bank instruments data + BankMap m_insBanks; + //! MIDI bank-wide setup + OpnBankSetup m_insBankSetup; + +public: + //! Blank instrument template + static const OpnInstMeta m_emptyInstrument; + + //! Total number of running concurrent emulated chips + uint32_t m_numChips; + //! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too. + bool m_scaleModulators; + //! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates. + bool m_runAtPcmRate; + //! Enable soft panning + bool m_softPanning; + //! Master volume, controlled via SysEx (0...127) + uint8_t m_masterVolume; + + //! Just a padding. Reserved. + char _padding2[3]; + + /** + * @brief Music playing mode + */ + enum MusicMode + { + //! MIDI mode + MODE_MIDI, + //! MIDI mode + MODE_XMIDI, + //! Id-Software Music mode + MODE_IMF, + //! Creative Music Files mode + MODE_CMF, + //! EA-MUS (a.k.a. RSXX) mode + MODE_RSXX + } m_musicMode; + + /** + * @brief Volume models enum + */ + enum VolumesScale + { + //! Generic volume model (linearization of logarithmic scale) + VOLUME_Generic, + //! OPN2 native logarithmic scale + VOLUME_NATIVE, + //! DMX volume scale logarithmic table + VOLUME_DMX, + //! Apoge Sound System volume scaling model + VOLUME_APOGEE, + //! Windows 9x driver volume scale table + VOLUME_9X + } m_volumeScale; + + //! Channel allocation algorithm + OPNMIDI_ChannelAlloc m_channelAlloc; + + //! Reserved + bool m_lfoEnable; + uint8_t m_lfoFrequency; + + //! Category of the channel + /*! 1 = DAC, 0 = regular + */ + std::vector m_channelCategory; + + //! Chip family + OPNFamily m_chipFamily; + + /** + * @brief C.O. Constructor + */ + OPN2(); + + /** + * @brief C.O. Destructor + */ + ~OPN2(); + + /** + * @brief Checks are setup locked to be changed on the fly or not + * @return true when setup on the fly is locked + */ + bool setupLocked(); + + /** + * @brief Write data to OPN2 chip register + * @param chip Index of emulated chip. In hardware OPN2 builds, this parameter is ignored + * @param port Port of the chip to write + * @param index Register address to write + * @param value Value to write + */ + void writeReg(size_t chip, uint8_t port, uint8_t index, uint8_t value); + + /** + * @brief Write data to OPN2 chip register + * @param chip Index of emulated chip. In hardware OPN2 builds, this parameter is ignored + * @param port Port of the chip to write + * @param index Register address to write + * @param value Value to write + */ + void writeRegI(size_t chip, uint8_t port, uint32_t index, uint32_t value); + + /** + * @brief Write to soft panning control of OPN2 chip emulator + * @param chip Index of emulated chip. + * @param address Register of channel to write + * @param value Value to write + */ + void writePan(size_t chip, uint32_t index, uint32_t value); + + /** + * @brief Off the note in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + */ + void noteOff(size_t c); + + /** + * @brief On the note in specified chip channel with specified frequency of the tone + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param tone The tone to play (integer part - MIDI halftone, decimal part - relative bend offset) + */ + void noteOn(size_t c, double tone); + + /** + * @brief Change setup of instrument in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param volume Volume level (from 0 to 127) + * @param brightness CC74 Brightness level (from 0 to 127) + */ + void touchNote(size_t c, + uint_fast32_t velocity, + uint_fast32_t channelVolume = 127, + uint_fast32_t channelExpression = 127, + uint8_t brightness = 127); + + /** + * @brief Set the instrument into specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param instrument Instrument data to set into the chip channel + */ + void setPatch(size_t c, const OpnTimbre &instrument); + + /** + * @brief Set panpot position + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param value 3-bit panpot value + */ + void setPan(size_t c, uint8_t value); + + /** + * @brief Shut up all chip channels + */ + void silenceAll(); + + /** + * @brief commit LFO enable and frequency + */ + void commitLFOSetup(); + + /** + * @brief Set the volume scaling model + * @param volumeModel Type of volume scale model scale + */ + void setVolumeScaleModel(OPNMIDI_VolumeModels volumeModel); + + /** + * @brief Get the volume scaling model + */ + OPNMIDI_VolumeModels getVolumeScaleModel(); + + /** + * @brief Clean up all running emulated chip instances + */ + void clearChips(); + + /** + * @brief Reset chip properties and initialize them + * @param emulator Type of chip emulator + * @param PCM_RATE Output sample rate to generate on output + * @param audioTickHandler PCM-accurate clock hook + */ + void reset(int emulator, unsigned long PCM_RATE, OPNFamily family, void *audioTickHandler); + + /** + * @brief Gets the family of current chips + * @return the chip family + */ + OPNFamily chipFamily() const; +}; + +/** + * @brief Check emulator availability + * @param emulator Emulator ID (Opn2_Emulator) + * @return true when emulator is available + */ +extern bool opn2_isEmulatorAvailable(int emulator); + +/** + * @brief Find highest emulator + * @return The Opn2_Emulator enum value which contains ID of highest emulator + */ +extern int opn2_getHighestEmulator(); + +/** + * @brief Find lowest emulator + * @return The Opn2_Emulator enum value which contains ID of lowest emulator + */ +extern int opn2_getLowestEmulator(); + +#endif // OPNMIDI_OPN2_HPP diff --git a/thirdparty/opnmidi/opnmidi_private.cpp b/thirdparty/opnmidi/opnmidi_private.cpp new file mode 100644 index 000000000..969e6dc6a --- /dev/null +++ b/thirdparty/opnmidi/opnmidi_private.cpp @@ -0,0 +1,38 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * OPNMIDI Library and YM2612 support: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "opnmidi_midiplay.hpp" +#include "opnmidi_opn2.hpp" +#include "opnmidi_private.hpp" + +std::string OPN2MIDI_ErrorString; + +// Generator callback on audio rate ticks + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +void opn2_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate) +{ + reinterpret_cast(instance)->AudioTick(chipId, rate); +} +#endif + diff --git a/thirdparty/opnmidi/opnmidi_private.hpp b/thirdparty/opnmidi/opnmidi_private.hpp new file mode 100644 index 000000000..b582b5a6a --- /dev/null +++ b/thirdparty/opnmidi/opnmidi_private.hpp @@ -0,0 +1,192 @@ +/* + * libADLMIDI is a free Software MIDI synthesizer library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2017-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADLMIDI_PRIVATE_HPP +#define ADLMIDI_PRIVATE_HPP + +#define OPNMIDI_UNSTABLE_API + +// Setup compiler defines useful for exporting required public API symbols in gme.cpp +#ifndef OPNMIDI_EXPORT +# if defined (_WIN32) && defined(OPNMIDI_BUILD_DLL) +# define OPNMIDI_EXPORT __declspec(dllexport) +# elif defined (LIBOPNMIDI_VISIBILITY) && defined (__GNUC__) +# define OPNMIDI_EXPORT __attribute__((visibility ("default"))) +# else +# define OPNMIDI_EXPORT +# endif +#endif + +#ifdef _WIN32 +#define NOMINMAX 1 +#endif + +#ifdef _WIN32 +# undef NO_OLDNAMES +# include +# ifdef _MSC_VER +# ifdef _WIN64 +typedef __int64 ssize_t; +# else +typedef __int32 ssize_t; +# endif +# else +# ifdef _WIN64 +typedef int64_t ssize_t; +# else +typedef int32_t ssize_t; +# endif +# endif +# include +#endif + +#include +#include +#include +#include +#include +#include // nothrow +#include +#include +#include +#include +#include +#include +#include // vector +#include // deque +#include // exp, log, ceil +#include +#include +#include +#include // numeric_limit + +#ifndef _WIN32 +#include +#endif + +#include +#include + +/* + * Workaround for some compilers are has no those macros in their headers! + */ +#ifndef INT8_MIN +#define INT8_MIN (-0x7f - 1) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-0x7fff - 1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-0x7fffffff - 1) +#endif +#ifndef INT8_MAX +#define INT8_MAX 0x7f +#endif +#ifndef INT16_MAX +#define INT16_MAX 0x7fff +#endif +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff +#endif + +class FileAndMemReader; + +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER +// Rename class to avoid ABI collisions +#define BW_MidiSequencer OpnMidiSequencer +class BW_MidiSequencer; +typedef BW_MidiSequencer MidiSequencer; +typedef struct BW_MidiRtInterface BW_MidiRtInterface; +#endif//OPNMIDI_DISABLE_MIDI_SEQUENCER + +class OPN2; +class OPNChipBase; + +typedef class OPN2 Synth; + +#include "opnbank.h" + +#define OPNMIDI_BUILD +#include "opnmidi.h" //Main API + +#include "opnmidi_ptr.hpp" + +class MIDIplay; + +#define ADL_UNUSED(x) (void)x + +#define OPN_MAX_CHIPS 100 +#define OPN_MAX_CHIPS_STR "100" + +extern std::string OPN2MIDI_ErrorString; + +/* + Sample conversions to various formats +*/ +template +inline Real opn2_cvtReal(int32_t x) +{ + return static_cast(x) * (static_cast(1) / static_cast(INT16_MAX)); +} +inline int32_t opn2_cvtS16(int32_t x) +{ + x = (x < INT16_MIN) ? INT16_MIN : x; + x = (x > INT16_MAX) ? INT16_MAX : x; + return x; +} +inline int32_t opn2_cvtS8(int32_t x) +{ + return opn2_cvtS16(x) / 256; +} +inline int32_t opn2_cvtS24(int32_t x) +{ + return opn2_cvtS16(x) * 256; +} +inline int32_t opn2_cvtS32(int32_t x) +{ + return opn2_cvtS16(x) * 65536; +} +inline int32_t opn2_cvtU16(int32_t x) +{ + return opn2_cvtS16(x) - INT16_MIN; +} +inline int32_t opn2_cvtU8(int32_t x) +{ + return opn2_cvtS8(x) - INT8_MIN; +} +inline int32_t opn2_cvtU24(int32_t x) +{ + enum { int24_min = -(1 << 23) }; + return opn2_cvtS24(x) - int24_min; +} +inline int32_t opn2_cvtU32(int32_t x) +{ + // unsigned operation because overflow on signed integers is undefined + return (uint32_t)opn2_cvtS32(x) - (uint32_t)INT32_MIN; +} + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +extern void opn2_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); +#endif + +#endif // ADLMIDI_PRIVATE_HPP diff --git a/thirdparty/opnmidi/opnmidi_ptr.hpp b/thirdparty/opnmidi/opnmidi_ptr.hpp new file mode 100644 index 000000000..47efc2244 --- /dev/null +++ b/thirdparty/opnmidi/opnmidi_ptr.hpp @@ -0,0 +1,217 @@ +/* + * libOPNMIDI is a free Software MIDI synthesizer library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2025 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OPNMIDI_PTR_HPP_THING +#define OPNMIDI_PTR_HPP_THING + +#include // swap +#include +#include + +/* + Generic deleters for smart pointers + */ +template +struct ADLMIDI_DefaultDelete +{ + void operator()(T *x) { delete x; } +}; +template +struct ADLMIDI_DefaultArrayDelete +{ + void operator()(T *x) { delete[] x; } +}; +struct ADLMIDI_CDelete +{ + void operator()(void *x) { free(x); } +}; + +/* + Safe unique pointer for C++98, non-copyable but swappable. +*/ +template< class T, class Deleter = ADLMIDI_DefaultDelete > +class AdlMIDI_UPtr +{ + T *m_p; +public: + explicit AdlMIDI_UPtr(T *p = NULL) + : m_p(p) {} + ~AdlMIDI_UPtr() + { + reset(); + } + + void reset(T *p = NULL) + { + if(p != m_p) { + if(m_p) { + Deleter del; + del(m_p); + } + m_p = p; + } + } + + void swap(AdlMIDI_UPtr &other) + { + std::swap(m_p, other.m_p); + } + + T *get() const + { + return m_p; + } + T &operator*() const + { + return *m_p; + } + T *operator->() const + { + return m_p; + } + T &operator[](size_t index) const + { + return m_p[index]; + } +private: + AdlMIDI_UPtr(const AdlMIDI_UPtr &); + AdlMIDI_UPtr &operator=(const AdlMIDI_UPtr &); +}; + +template +void swap(AdlMIDI_UPtr &a, AdlMIDI_UPtr &b) +{ + a.swap(b); +} + +/** + Unique pointer for arrays. + */ +template +class AdlMIDI_UPtrArray : + public AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete > +{ +public: + explicit AdlMIDI_UPtrArray(T *p = NULL) + : AdlMIDI_UPtr< T, ADLMIDI_DefaultArrayDelete >(p) {} +}; + +/** + Unique pointer for C memory. + */ +template +class AdlMIDI_CPtr : + public AdlMIDI_UPtr< T, ADLMIDI_CDelete > +{ +public: + explicit AdlMIDI_CPtr(T *p = NULL) + : AdlMIDI_UPtr< T, ADLMIDI_CDelete >(p) {} +}; + +/* + Shared pointer with non-atomic counter + FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it +*/ +template< class T, class Deleter = ADLMIDI_DefaultDelete > +class AdlMIDI_SPtr +{ + T *m_p; + size_t *m_counter; +public: + explicit AdlMIDI_SPtr(T *p = NULL) + : m_p(p), m_counter(p ? new size_t(1) : NULL) {} + ~AdlMIDI_SPtr() + { + reset(NULL); + } + + AdlMIDI_SPtr(const AdlMIDI_SPtr &other) + : m_p(other.m_p), m_counter(other.m_counter) + { + if(m_counter) + ++*m_counter; + } + + AdlMIDI_SPtr &operator=(const AdlMIDI_SPtr &other) + { + if(this == &other) + return *this; + reset(); + m_p = other.m_p; + m_counter = other.m_counter; + if(m_counter) + ++*m_counter; + return *this; + } + + void reset(T *p = NULL) + { + if(p != m_p) { + if(m_p && --*m_counter == 0) { + Deleter del; + del(m_p); + if(!p) { + delete m_counter; + m_counter = NULL; + } + } + m_p = p; + if(p) { + if(!m_counter) + m_counter = new size_t; + *m_counter = 1; + } + } + } + + T *get() const + { + return m_p; + } + T &operator*() const + { + return *m_p; + } + T *operator->() const + { + return m_p; + } + T &operator[](size_t index) const + { + return m_p[index]; + } +}; + +/** + Shared pointer for arrays. + */ +template +class AdlMIDI_SPtrArray : + public AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete > +{ +public: + explicit AdlMIDI_SPtrArray(T *p = NULL) + : AdlMIDI_SPtr< T, ADLMIDI_DefaultArrayDelete >(p) {} +}; + +#endif //ADLMIDI_PTR_HPP_THING diff --git a/thirdparty/opnmidi/structures/pl_list.hpp b/thirdparty/opnmidi/structures/pl_list.hpp new file mode 100644 index 000000000..0cbd23384 --- /dev/null +++ b/thirdparty/opnmidi/structures/pl_list.hpp @@ -0,0 +1,133 @@ +// Copyright Jean Pierre Cimalando 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef PL_LIST_HPP +#define PL_LIST_HPP + +#include +#include + +/* + pl_cell: the linked list cell + */ +template +struct pl_cell; + +template +struct pl_basic_cell +{ + pl_cell *prev, *next; +}; + +template +struct pl_cell : pl_basic_cell +{ + T value; +}; + +/* + pl_iterator: the linked list iterator + */ +template +class pl_iterator +{ +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef Cell value_type; + typedef Cell &reference; + typedef Cell *pointer; + typedef std::ptrdiff_t difference_type; + + pl_iterator(Cell *cell = NULL); + bool is_end() const; + Cell &operator*() const; + Cell *operator->() const; + bool operator==(const pl_iterator &i) const; + bool operator!=(const pl_iterator &i) const; + pl_iterator &operator++(); + pl_iterator operator++(int); + pl_iterator &operator--(); + pl_iterator operator--(int); + +private: + Cell *cell_; +}; + +/* + pl_list: the preallocated linked list + */ +template +class pl_list +{ +public: + typedef pl_cell value_type; + typedef value_type *pointer; + typedef value_type &reference; + typedef const value_type *const_pointer; + typedef const value_type &const_reference; + typedef pl_iterator< pl_cell > iterator; + typedef pl_iterator< const pl_cell > const_iterator; + + pl_list(std::size_t capacity = 0); + ~pl_list(); + + struct external_storage_policy {}; + pl_list(pl_cell *cells, std::size_t ncells, external_storage_policy); + + pl_list(const pl_list &other); + pl_list &operator=(const pl_list &other); + + std::size_t size() const; + std::size_t capacity() const; + bool empty() const; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + void clear(); + + pl_cell &front(); + const pl_cell &front() const; + pl_cell &back(); + const pl_cell &back() const; + + iterator insert(iterator pos, const T &x); + iterator erase(iterator pos); + void push_front(const T &x); + void push_back(const T &x); + void pop_front(); + void pop_back(); + + iterator find(const T &x); + const_iterator find(const T &x) const; + template iterator find_if(const Pred &p); + template const_iterator find_if(const Pred &p) const; + +private: + // number of cells in the list + std::size_t size_; + // number of cells allocated + std::size_t capacity_; + // array of cells allocated + pl_cell *cells_; + // pointer to the head cell + pl_cell *first_; + // pointer to the next free cell + pl_cell *free_; + // value-less cell which terminates the linked list + pl_basic_cell endcell_; + // whether cell storage is allocated + bool cells_allocd_; + + void initialize(std::size_t capacity, pl_cell *extcells = NULL); + pl_cell *allocate(pl_cell *pos); + void deallocate(pl_cell *cell); +}; + +#include "pl_list.tcc" + +#endif // PL_LIST_HPP diff --git a/thirdparty/opnmidi/structures/pl_list.tcc b/thirdparty/opnmidi/structures/pl_list.tcc new file mode 100644 index 000000000..959b08526 --- /dev/null +++ b/thirdparty/opnmidi/structures/pl_list.tcc @@ -0,0 +1,338 @@ +// Copyright Jean Pierre Cimalando 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "pl_list.hpp" + +template +pl_iterator::pl_iterator(Cell *cell) + : cell_(cell) +{ +} + +template +bool pl_iterator::is_end() const +{ + return cell_->next == NULL; +} + +template +Cell &pl_iterator::operator*() const +{ + return *cell_; +} + +template +Cell *pl_iterator::operator->() const +{ + return cell_; +} + +template +bool pl_iterator::operator==(const pl_iterator &i) const +{ + return cell_ == i.cell_; +} + +template +bool pl_iterator::operator!=(const pl_iterator &i) const +{ + return cell_ != i.cell_; +} + +template +pl_iterator &pl_iterator::operator++() +{ + cell_ = cell_->next; + return *this; +} + +template +pl_iterator pl_iterator::operator++(int) +{ + pl_iterator i(cell_); + cell_ = cell_->next; + return i; +} + +template +pl_iterator &pl_iterator::operator--() +{ + cell_ = cell_->prev; + return *this; +} + +template +pl_iterator pl_iterator::operator--(int) +{ + pl_iterator i(cell_); + cell_ = cell_->prev; + return i; +} + +template +pl_list::pl_list(std::size_t capacity) +{ + initialize(capacity); +} + +template +pl_list::~pl_list() +{ + if (cells_allocd_) + delete[] cells_; +} + +template +pl_list::pl_list(pl_cell *cells, std::size_t ncells, external_storage_policy) +{ + initialize(ncells, cells); +} + +template +pl_list::pl_list(const pl_list &other) +{ + initialize(other.capacity()); + for(const_iterator i = other.end(), b = other.begin(); i-- != b;) + push_front(i->value); +} + +template +pl_list &pl_list::operator=(const pl_list &other) +{ + if(this != &other) + { + std::size_t size = other.size(); + if(size > capacity()) + { + pl_cell *oldcells = cells_; + bool allocd = cells_allocd_; + initialize(other.capacity()); + if (allocd) + delete[] oldcells; + } + clear(); + for(const_iterator i = other.end(), b = other.begin(); i-- != b;) + push_front(i->value); + } + return *this; +} + +template +std::size_t pl_list::size() const +{ + return size_; +} + +template +std::size_t pl_list::capacity() const +{ + return capacity_; +} + +template +bool pl_list::empty() const +{ + return size_ == 0; +} + +template +typename pl_list::iterator pl_list::begin() +{ + return iterator(first_); +} + +template +typename pl_list::iterator pl_list::end() +{ + return iterator(reinterpret_cast *>(&endcell_)); +} + +template +typename pl_list::const_iterator pl_list::begin() const +{ + return const_iterator(first_); +} + +template +typename pl_list::const_iterator pl_list::end() const +{ + return const_iterator(reinterpret_cast *>(&endcell_)); +} + +template +void pl_list::clear() +{ + std::size_t capacity = capacity_; + pl_cell *cells = cells_; + pl_cell *endcell = &*end(); + size_ = 0; + first_ = endcell; + free_ = cells; + endcell->prev = NULL; + for(std::size_t i = 0; i < capacity; ++i) + { + cells[i].prev = (i > 0) ? &cells[i - 1] : NULL; + cells[i].next = (i + 1 < capacity) ? &cells[i + 1] : NULL; + cells[i].value = T(); + } +} + +template +pl_cell &pl_list::front() +{ + return *first_; +} + +template +const pl_cell &pl_list::front() const +{ + return *first_; +} + +template +pl_cell &pl_list::back() +{ + iterator i = end(); + return *--i; +} + +template +const pl_cell &pl_list::back() const +{ + const_iterator i = end(); + return *--i; +} + +template +typename pl_list::iterator pl_list::insert(iterator pos, const T &x) +{ + pl_cell *cell = allocate(&*pos); + if (!cell) + throw std::bad_alloc(); + cell->value = x; + return iterator(cell); +} + +template +typename pl_list::iterator pl_list::erase(iterator pos) +{ + deallocate(&*(pos++)); + return pos; +} + +template +void pl_list::push_front(const T &x) +{ + insert(begin(), x); +} + +template +void pl_list::push_back(const T &x) +{ + insert(end(), x); +} + +template +void pl_list::pop_front() +{ + deallocate(first_); +} + +template +void pl_list::pop_back() +{ + iterator i(&*end()); + deallocate(&*--i); +} + +template +typename pl_list::iterator pl_list::find(const T &x) +{ + const_iterator i = const_cast *>(this)->find(x); + return iterator(&const_cast(*i)); +} + +template +typename pl_list::const_iterator pl_list::find(const T &x) const +{ + const_iterator e = end(); + for (const_iterator i = begin(); i != e; ++i) + { + if(i->value == x) + return i; + } + return e; +} + +template +template +typename pl_list::iterator pl_list::find_if(const Pred &p) +{ + const_iterator i = const_cast *>(this)->find_if(p); + return iterator(&const_cast(*i)); +} + +template +template +typename pl_list::const_iterator pl_list::find_if(const Pred &p) const +{ + const_iterator e = end(); + for (const_iterator i = begin(); i != e; ++i) + { + if(p(i->value)) + return i; + } + return e; +} + +template +void pl_list::initialize(std::size_t capacity, pl_cell *extcells) +{ + cells_ = extcells ? extcells : new pl_cell[capacity]; + cells_allocd_ = extcells ? false : true; + capacity_ = capacity; + endcell_.next = NULL; + clear(); +} + +template +pl_cell *pl_list::allocate(pl_cell *pos) +{ + // remove free cells front + pl_cell *cell = free_; + if(!cell) + return NULL; + free_ = cell->next; + if(free_) + free_->prev = NULL; + + // insert at position + if (pos == first_) + first_ = cell; + cell->prev = pos->prev; + if (cell->prev) + cell->prev->next = cell; + cell->next = pos; + pos->prev = cell; + + ++size_; + return cell; +} + +template +void pl_list::deallocate(pl_cell *cell) +{ + if(cell->prev) + cell->prev->next = cell->next; + if(cell->next) + cell->next->prev = cell->prev; + if(cell == first_) + first_ = cell->next; + cell->prev = NULL; + cell->next = free_; + cell->value = T(); + free_ = cell; + --size_; +} diff --git a/thirdparty/opnmidi/wopn/wopn_file.c b/thirdparty/opnmidi/wopn/wopn_file.c new file mode 100644 index 000000000..f8b7371eb --- /dev/null +++ b/thirdparty/opnmidi/wopn/wopn_file.c @@ -0,0 +1,639 @@ +/* + * Wohlstand's OPN2 Bank File - a bank format to store OPN2 timbre data and setup + * + * Copyright (c) 2018-2025 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "wopn_file.h" +#include +#include + +static const char *wopn2_magic1 = "WOPN2-BANK\0"; +static const char *wopn2_magic2 = "WOPN2-B2NK\0"; +static const char *opni_magic1 = "WOPN2-INST\0"; +static const char *opni_magic2 = "WOPN2-IN2T\0"; + +static const uint16_t wopn_latest_version = 2; + +enum +{ + WOPN_INST_SIZE_V1 = 65, + WOPN_INST_SIZE_V2 = 69 +}; + +static uint16_t toUint16LE(const uint8_t *arr) +{ + uint16_t num = arr[0]; + num |= ((arr[1] << 8) & 0xFF00); + return num; +} + +static uint16_t toUint16BE(const uint8_t *arr) +{ + uint16_t num = arr[1]; + num |= ((arr[0] << 8) & 0xFF00); + return num; +} + +static int16_t toSint16BE(const uint8_t *arr) +{ + int16_t num = *(const int8_t *)(&arr[0]); + num *= 1 << 8; + num |= arr[1]; + return num; +} + +static void fromUint16LE(uint16_t in, uint8_t *arr) +{ + arr[0] = in & 0x00FF; + arr[1] = (in >> 8) & 0x00FF; +} + +static void fromUint16BE(uint16_t in, uint8_t *arr) +{ + arr[1] = in & 0x00FF; + arr[0] = (in >> 8) & 0x00FF; +} + +static void fromSint16BE(int16_t in, uint8_t *arr) +{ + arr[1] = in & 0x00FF; + arr[0] = ((uint16_t)in >> 8) & 0x00FF; +} + + +WOPNFile *WOPN_Init(uint16_t melodic_banks, uint16_t percussive_banks) +{ + WOPNFile *file = (WOPNFile*)calloc(1, sizeof(WOPNFile)); + if(!file) + return NULL; + + file->banks_count_melodic = (melodic_banks != 0) ? melodic_banks : 1; + file->banks_melodic = (WOPNBank*)calloc(file->banks_count_melodic, sizeof(WOPNBank)); + + if(melodic_banks == 0) + { + unsigned i; + for(i = 0; i < 128; ++i) + file->banks_melodic[0].ins[i].inst_flags = WOPN_Ins_IsBlank; + } + + file->banks_count_percussion = (percussive_banks != 0) ? percussive_banks : 1; + file->banks_percussive = (WOPNBank*)calloc(file->banks_count_percussion, sizeof(WOPNBank)); + + if(percussive_banks == 0) + { + unsigned i; + for(i = 0; i < 128; ++i) + file->banks_percussive[0].ins[i].inst_flags = WOPN_Ins_IsBlank; + } + + return file; +} + +void WOPN_Free(WOPNFile *file) +{ + if(file) + { + if(file->banks_melodic) + free(file->banks_melodic); + if(file->banks_percussive) + free(file->banks_percussive); + free(file); + } +} + +int WOPN_BanksCmp(const WOPNFile *bank1, const WOPNFile *bank2) +{ + int res = 1; + + res &= (bank1->version == bank2->version); + res &= (bank1->lfo_freq == bank2->lfo_freq); + res &= (bank1->chip_type == bank2->chip_type); + res &= (bank1->volume_model == bank2->volume_model); + res &= (bank1->banks_count_melodic == bank2->banks_count_melodic); + res &= (bank1->banks_count_percussion == bank2->banks_count_percussion); + + if(res) + { + int i; + for(i = 0; i < bank1->banks_count_melodic; i++) + res &= (memcmp(&bank1->banks_melodic[i], &bank2->banks_melodic[i], sizeof(WOPNBank)) == 0); + if(res) + { + for(i = 0; i < bank1->banks_count_percussion; i++) + res &= (memcmp(&bank1->banks_percussive[i], &bank2->banks_percussive[i], sizeof(WOPNBank)) == 0); + } + } + + return res; +} + +static void WOPN_parseInstrument(WOPNInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) +{ + int l; + strncpy(ins->inst_name, (const char*)cursor, 32); + ins->inst_name[31] = '\0'; + ins->note_offset = toSint16BE(cursor + 32); + ins->midi_velocity_offset = 0; /* TODO: for future version > 2 */ + ins->percussion_key_number = cursor[34]; + ins->inst_flags = 0; /* TODO: for future version > 2 */ + ins->fbalg = cursor[35]; + ins->lfosens = cursor[36]; + for(l = 0; l < 4; l++) + { + size_t off = 37 + (size_t)(l) * 7; + ins->operators[l].dtfm_30 = cursor[off + 0]; + ins->operators[l].level_40 = cursor[off + 1]; + ins->operators[l].rsatk_50 = cursor[off + 2]; + ins->operators[l].amdecay1_60 = cursor[off + 3]; + ins->operators[l].decay2_70 = cursor[off + 4]; + ins->operators[l].susrel_80 = cursor[off + 5]; + ins->operators[l].ssgeg_90 = cursor[off + 6]; + } + if((version >= 2) && has_sounding_delays) + { + ins->delay_on_ms = toUint16BE(cursor + 65); + ins->delay_off_ms = toUint16BE(cursor + 67); + + /* Null delays indicate the blank instrument in version 2 */ + if((version < 3) && ins->delay_on_ms == 0 && ins->delay_off_ms == 0) + ins->inst_flags |= WOPN_Ins_IsBlank; + } +} + +static void WOPN_writeInstrument(WOPNInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) +{ + int l; + strncpy((char*)cursor, ins->inst_name, 32); + fromSint16BE(ins->note_offset, cursor + 32); + cursor[34] = ins->percussion_key_number; + cursor[35] = ins->fbalg; + cursor[36] = ins->lfosens; + for(l = 0; l < 4; l++) + { + size_t off = 37 + (size_t)(l) * 7; + cursor[off + 0] = ins->operators[l].dtfm_30; + cursor[off + 1] = ins->operators[l].level_40; + cursor[off + 2] = ins->operators[l].rsatk_50; + cursor[off + 3] = ins->operators[l].amdecay1_60; + cursor[off + 4] = ins->operators[l].decay2_70; + cursor[off + 5] = ins->operators[l].susrel_80; + cursor[off + 6] = ins->operators[l].ssgeg_90; + } + if((version >= 2) && has_sounding_delays) + { + if((version < 3) && (ins->inst_flags & WOPN_Ins_IsBlank) != 0) + { + /* Null delays indicate the blank instrument in version 2 */ + fromUint16BE(0, cursor + 65); + fromUint16BE(0, cursor + 67); + } + else + { + fromUint16BE(ins->delay_on_ms, cursor + 65); + fromUint16BE(ins->delay_off_ms, cursor + 67); + } + } +} + +WOPNFile *WOPN_LoadBankFromMem(void *mem, size_t length, int *error) +{ + WOPNFile *outFile = NULL; + uint16_t i = 0, j = 0, k = 0; + uint16_t version = 0; + uint16_t count_melodic_banks = 1; + uint16_t count_percussive_banks = 1; + uint8_t *cursor = (uint8_t *)mem; + + WOPNBank *bankslots[2]; + uint16_t bankslots_sizes[2]; + +#define SET_ERROR(err) \ +{\ + WOPN_Free(outFile);\ + if(error)\ + {\ + *error = err;\ + }\ +} + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + if(!cursor) + { + SET_ERROR(WOPN_ERR_NULL_POINTER); + return NULL; + } + + {/* Magic number */ + if(length < 11) + { + SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); + return NULL; + } + if(memcmp(cursor, wopn2_magic1, 11) == 0) + { + version = 1; + } + else if(memcmp(cursor, wopn2_magic2, 11) != 0) + { + SET_ERROR(WOPN_ERR_BAD_MAGIC); + return NULL; + } + GO_FORWARD(11); + } + + if (version == 0) + {/* Version code */ + if(length < 2) + { + SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); + return NULL; + } + version = toUint16LE(cursor); + if(version > wopn_latest_version) + { + SET_ERROR(WOPN_ERR_NEWER_VERSION); + return NULL; + } + GO_FORWARD(2); + } + + {/* Header of WOPN */ + uint8_t head[5]; + if(length < 5) + { + SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); + return NULL; + } + memcpy(head, cursor, 5); + count_melodic_banks = toUint16BE(head); + count_percussive_banks = toUint16BE(head + 2); + GO_FORWARD(5); + + outFile = WOPN_Init(count_melodic_banks, count_percussive_banks); + if(!outFile) + { + SET_ERROR(WOPN_ERR_OUT_OF_MEMORY); + return NULL; + } + + outFile->version = version; + outFile->lfo_freq = head[4] & 0xf; + if(version >= 2) + outFile->chip_type = (head[4] >> 4) & 1; + outFile->volume_model = 0; + } + + bankslots_sizes[0] = count_melodic_banks; + bankslots[0] = outFile->banks_melodic; + bankslots_sizes[1] = count_percussive_banks; + bankslots[1] = outFile->banks_percussive; + + if(version >= 2) /* Bank names and LSB/MSB titles */ + { + for(i = 0; i < 2; i++) + { + for(j = 0; j < bankslots_sizes[i]; j++) + { + if(length < 34) + { + SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); + return NULL; + } + strncpy(bankslots[i][j].bank_name, (const char*)cursor, 32); + bankslots[i][j].bank_name[32] = '\0'; + bankslots[i][j].bank_midi_lsb = cursor[32]; + bankslots[i][j].bank_midi_msb = cursor[33]; + GO_FORWARD(34); + } + } + } + + {/* Read instruments data */ + uint16_t insSize = 0; + if(version > 1) + insSize = WOPN_INST_SIZE_V2; + else + insSize = WOPN_INST_SIZE_V1; + for(i = 0; i < 2; i++) + { + if(length < (insSize * 128) * (size_t)bankslots_sizes[i]) + { + SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); + return NULL; + } + + for(j = 0; j < bankslots_sizes[i]; j++) + { + for(k = 0; k < 128; k++) + { + WOPNInstrument *ins = &bankslots[i][j].ins[k]; + WOPN_parseInstrument(ins, cursor, version, 1); + GO_FORWARD(insSize); + } + } + } + } + +#undef GO_FORWARD +#undef SET_ERROR + + return outFile; +} + +int WOPN_LoadInstFromMem(OPNIFile *file, void *mem, size_t length) +{ + uint16_t version = 0; + uint8_t *cursor = (uint8_t *)mem; + uint16_t ins_size; + + if(!cursor) + return WOPN_ERR_NULL_POINTER; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + {/* Magic number */ + if(length < 11) + return WOPN_ERR_UNEXPECTED_ENDING; + if(memcmp(cursor, opni_magic1, 11) == 0) + version = 1; + else if(memcmp(cursor, opni_magic2, 11) != 0) + return WOPN_ERR_BAD_MAGIC; + GO_FORWARD(11); + } + + if (version == 0) {/* Version code */ + if(length < 2) + return WOPN_ERR_UNEXPECTED_ENDING; + version = toUint16LE(cursor); + if(version > wopn_latest_version) + return WOPN_ERR_NEWER_VERSION; + GO_FORWARD(2); + } + + file->version = version; + + {/* is drum flag */ + if(length < 1) + return WOPN_ERR_UNEXPECTED_ENDING; + file->is_drum = *cursor; + GO_FORWARD(1); + } + + if(version > 1) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPN_INST_SIZE_V2 - (sizeof(uint16_t) * 2); + else + ins_size = WOPN_INST_SIZE_V1; + + if(length < ins_size) + return WOPN_ERR_UNEXPECTED_ENDING; + + WOPN_parseInstrument(&file->inst, cursor, version, 0); + GO_FORWARD(ins_size); + + return WOPN_ERR_OK; +#undef GO_FORWARD +} + +size_t WOPN_CalculateBankFileSize(WOPNFile *file, uint16_t version) +{ + size_t final_size = 0; + size_t ins_size = 0; + + if(version == 0) + version = wopn_latest_version; + + if(!file) + return 0; + final_size += 11 + 2 + 2 + 2 + 1; + /* + * Magic number, + * Version, + * Count of melodic banks, + * Count of percussive banks, + * Chip specific flags + */ + + if(version >= 2) + { + /* Melodic banks meta-data */ + final_size += (32 + 1 + 1) * file->banks_count_melodic; + /* Percussive banks meta-data */ + final_size += (32 + 1 + 1) * file->banks_count_percussion; + } + + if(version >= 2) + ins_size = WOPN_INST_SIZE_V2; + else + ins_size = WOPN_INST_SIZE_V1; + /* Melodic instruments */ + final_size += (ins_size * 128) * file->banks_count_melodic; + /* Percussive instruments */ + final_size += (ins_size * 128) * file->banks_count_percussion; + + return final_size; +} + +size_t WOPN_CalculateInstFileSize(OPNIFile *file, uint16_t version) +{ + size_t final_size = 0; + size_t ins_size = 0; + + if(version == 0) + version = wopn_latest_version; + + if(!file) + return 0; + final_size += 11 + 1; + /* + * Magic number, + * is percussive instrument + */ + + /* Version */ + if (version > 1) + final_size += 2; + + if(version > 1) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPN_INST_SIZE_V2 - (sizeof(uint16_t) * 2); + else + ins_size = WOPN_INST_SIZE_V1; + final_size += ins_size; + + return final_size; +} + +int WOPN_SaveBankToMem(WOPNFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm) +{ + uint8_t *cursor = (uint8_t *)dest_mem; + uint16_t ins_size = 0; + uint16_t i, j, k; + uint16_t banks_melodic = force_gm ? 1 : file->banks_count_melodic; + uint16_t banks_percussive = force_gm ? 1 : file->banks_count_percussion; + + WOPNBank *bankslots[2]; + uint16_t bankslots_sizes[2]; + + if(version == 0) + version = wopn_latest_version; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + if(length < 11) + return WOPN_ERR_UNEXPECTED_ENDING; + if(version > 1) + memcpy(cursor, wopn2_magic2, 11); + else + memcpy(cursor, wopn2_magic1, 11); + GO_FORWARD(11); + + if(version > 1) + { + if(length < 2) + return WOPN_ERR_UNEXPECTED_ENDING; + fromUint16LE(version, cursor); + GO_FORWARD(2); + } + + if(length < 2) + return WOPN_ERR_UNEXPECTED_ENDING; + fromUint16BE(banks_melodic, cursor); + GO_FORWARD(2); + + if(length < 2) + return WOPN_ERR_UNEXPECTED_ENDING; + fromUint16BE(banks_percussive, cursor); + GO_FORWARD(2); + + if(length < 1) + return WOPN_ERR_UNEXPECTED_ENDING; + cursor[0] = file->lfo_freq & 0xf; + if (version >= 2) + cursor[0] |= (file->chip_type & 1) << 4; + GO_FORWARD(1); + + bankslots[0] = file->banks_melodic; + bankslots_sizes[0] = banks_melodic; + bankslots[1] = file->banks_percussive; + bankslots_sizes[1] = banks_percussive; + + if(version >= 2) + { + for(i = 0; i < 2; i++) + { + for(j = 0; j < bankslots_sizes[i]; j++) + { + if(length < 34) + return WOPN_ERR_UNEXPECTED_ENDING; + memcpy(cursor, bankslots[i][j].bank_name, 32); + cursor[32] = bankslots[i][j].bank_midi_lsb; + cursor[33] = bankslots[i][j].bank_midi_msb; + GO_FORWARD(34); + } + } + } + + {/* Write instruments data */ + if(version >= 2) + ins_size = WOPN_INST_SIZE_V2; + else + ins_size = WOPN_INST_SIZE_V1; + for(i = 0; i < 2; i++) + { + if(length < (ins_size * 128) * (size_t)bankslots_sizes[i]) + return WOPN_ERR_UNEXPECTED_ENDING; + + for(j = 0; j < bankslots_sizes[i]; j++) + { + for(k = 0; k < 128; k++) + { + WOPNInstrument *ins = &bankslots[i][j].ins[k]; + WOPN_writeInstrument(ins, cursor, version, 1); + GO_FORWARD(ins_size); + } + } + } + } + + return WOPN_ERR_OK; +#undef GO_FORWARD +} + +int WOPN_SaveInstToMem(OPNIFile *file, void *dest_mem, size_t length, uint16_t version) +{ + uint8_t *cursor = (uint8_t *)dest_mem; + uint16_t ins_size; + + if(!cursor) + return WOPN_ERR_NULL_POINTER; + + if(version == 0) + version = wopn_latest_version; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + {/* Magic number */ + if(length < 11) + return WOPN_ERR_UNEXPECTED_ENDING; + if(version > 1) + memcpy(cursor, opni_magic2, 11); + else + memcpy(cursor, opni_magic1, 11); + GO_FORWARD(11); + } + + if (version > 1) + {/* Version code */ + if(length < 2) + return WOPN_ERR_UNEXPECTED_ENDING; + fromUint16LE(version, cursor); + GO_FORWARD(2); + } + + {/* is drum flag */ + if(length < 1) + return WOPN_ERR_UNEXPECTED_ENDING; + *cursor = file->is_drum; + GO_FORWARD(1); + } + + if(version > 1) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPN_INST_SIZE_V2 - (sizeof(uint16_t) * 2); + else + ins_size = WOPN_INST_SIZE_V1; + + if(length < ins_size) + return WOPN_ERR_UNEXPECTED_ENDING; + + WOPN_writeInstrument(&file->inst, cursor, version, 0); + GO_FORWARD(ins_size); + + return WOPN_ERR_OK; +#undef GO_FORWARD +} diff --git a/thirdparty/opnmidi/wopn/wopn_file.h b/thirdparty/opnmidi/wopn/wopn_file.h new file mode 100644 index 000000000..f3fb568b9 --- /dev/null +++ b/thirdparty/opnmidi/wopn/wopn_file.h @@ -0,0 +1,275 @@ +/* + * Wohlstand's OPN2 Bank File - a bank format to store OPN2 timbre data and setup + * + * Copyright (c) 2018-2025 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef WOPN_FILE_H +#define WOPN_FILE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Solaris defines the integer types regardless of what C/C++ standard is actually available, + * so avoid defining them at all by ourselves. */ +#if !defined(WOPN_STDINT_TYPEDEFS_NOT_NEEDED) && defined(__sun) +# define WOPN_STDINT_TYPEDEFS_NOT_NEEDED +#endif + +#if !defined(WOPN_STDINT_TYPEDEFS_NEEDED) && !defined(WOPN_STDINT_TYPEDEFS_NOT_NEEDED) +# if !defined(__STDC_VERSION__) || \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L)) || \ + defined(__STRICT_ANSI__) || \ + !defined(__cplusplus) +# define WOPN_STDINT_TYPEDEFS_NEEDED +# endif +#endif + +#ifdef WOPN_STDINT_TYPEDEFS_NEEDED +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int int16_t; +typedef unsigned short int uint16_t; +#endif + +/* Type of chip for which a bank has been designed */ +typedef enum WOPN_ChipType +{ + /* The Yamaha OPN2 chip, alias YM2612 YM3438 */ + WOPN_Chip_OPN2 = 0, + /* The Yamaha OPNA chip, alias YM2608 */ + WOPN_Chip_OPNA = 1 +} WOPN_ChipType; + +/* Volume scaling model implemented in the libOPNMIDI */ +typedef enum WOPN_VolumeModel +{ + WOPN_VM_Generic = 0 +} WOPN_VolumeModel; + +typedef enum WOPN_InstrumentFlags +{ + /* Is pseudo eight-operator (two 4-operator voices) instrument */ + WOPN_Ins_Pseudo8op = 0x01, + /* Is a blank instrument entry */ + WOPN_Ins_IsBlank = 0x02, + + /* Mask of the flags range */ + WOPN_Ins_ALL_MASK = 0x03 +} WOPN_InstrumentFlags; + +/* Error codes */ +typedef enum WOPN_ErrorCodes +{ + WOPN_ERR_OK = 0, + /* Magic number is not maching */ + WOPN_ERR_BAD_MAGIC, + /* Too short file */ + WOPN_ERR_UNEXPECTED_ENDING, + /* Zero banks count */ + WOPN_ERR_INVALID_BANKS_COUNT, + /* Version of file is newer than supported by current version of library */ + WOPN_ERR_NEWER_VERSION, + /* Out of memory */ + WOPN_ERR_OUT_OF_MEMORY, + /* Given null pointer memory data */ + WOPN_ERR_NULL_POINTER +} WOPN_ErrorCodes; + +/* OPN2 Oerators data */ +typedef struct WOPNOperator +{ + /* Detune and frequency multiplication register data */ + uint8_t dtfm_30; + /* Total level register data */ + uint8_t level_40; + /* Rate scale and attack register data */ + uint8_t rsatk_50; + /* Amplitude modulation enable and Decay-1 register data */ + uint8_t amdecay1_60; + /* Decay-2 register data */ + uint8_t decay2_70; + /* Sustain and Release register data */ + uint8_t susrel_80; + /* SSG-EG register data */ + uint8_t ssgeg_90; +} WOPNOperator; + +/* Instrument entry */ +typedef struct WOPNInstrument +{ + /* Title of the instrument */ + char inst_name[32]; + /* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + int16_t note_offset; + /* Reserved */ + int8_t midi_velocity_offset; + /* Percussion MIDI base tone number at which this drum will be played */ + uint8_t percussion_key_number; + /* Enum WOPN_InstrumentFlags */ + uint8_t inst_flags; + /* Feedback and Algorithm register data */ + uint8_t fbalg; + /* LFO Sensitivity register data */ + uint8_t lfosens; + /* Operators register data */ + WOPNOperator operators[4]; + /* Millisecond delay of sounding while key is on */ + uint16_t delay_on_ms; + /* Millisecond delay of sounding after key off */ + uint16_t delay_off_ms; +} WOPNInstrument; + +/* Bank entry */ +typedef struct WOPNBank +{ + /* Name of bank */ + char bank_name[33]; + /* MIDI Bank LSB code */ + uint8_t bank_midi_lsb; + /* MIDI Bank MSB code */ + uint8_t bank_midi_msb; + /* Instruments data of this bank */ + WOPNInstrument ins[128]; +} WOPNBank; + +/* Instrument data file */ +typedef struct OPNIFile +{ + /* Version of instrument file */ + uint16_t version; + /* Is this a percussion instrument */ + uint8_t is_drum; + /* Instrument data */ + WOPNInstrument inst; +} OPNIFile; + +/* Bank data file */ +typedef struct WOPNFile +{ + /* Version of bank file */ + uint16_t version; + /* Count of melodic banks in this file */ + uint16_t banks_count_melodic; + /* Count of percussion banks in this file */ + uint16_t banks_count_percussion; + /* Chip global LFO enable flag and frequency register data */ + uint8_t lfo_freq; + /* Chip type this bank is designed for */ + uint8_t chip_type; + /* Reserved (Enum WOPN_VolumeModel) */ + uint8_t volume_model; + /* dynamically allocated data Melodic banks array */ + WOPNBank *banks_melodic; + /* dynamically allocated data Percussive banks array */ + WOPNBank *banks_percussive; +} WOPNFile; + + +/** + * @brief Initialize blank WOPN data structure with allocated bank data + * @param melodic_banks Count of melodic banks + * @param percussive_banks Count of percussive banks + * @return pointer to heap-allocated WOPN data structure or NULL when out of memory or incorrectly given banks counts + */ +extern WOPNFile *WOPN_Init(uint16_t melodic_banks, uint16_t percussive_banks); + +/** + * @brief Clean up WOPN data file (all allocated bank arrays will be fried too) + * @param file pointer to heap-allocated WOPN data structure + */ +extern void WOPN_Free(WOPNFile *file); + +/** + * @brief Compare two bank entries + * @param bank1 First bank + * @param bank2 Second bank + * @return 1 if banks are equal or 0 if there are different + */ +extern int WOPN_BanksCmp(const WOPNFile *bank1, const WOPNFile *bank2); + + +/** + * @brief Load WOPN bank file from the memory. + * WOPN data structure will be allocated. (don't forget to clear it with WOPN_Free() after use!) + * @param mem Pointer to memory block contains raw WOPN bank file data + * @param length Length of given memory block + * @param error pointer to integer to return an error code. Pass NULL if you don't want to use error codes. + * @return Heap-allocated WOPN file data structure or NULL if any error has occouped + */ +extern WOPNFile *WOPN_LoadBankFromMem(void *mem, size_t length, int *error); + +/** + * @brief Load WOPI instrument file from the memory. + * You must allocate OPNIFile structure by yourself and give the pointer to it. + * @param file Pointer to destination OPNIFile structure to fill it with parsed data. + * @param mem Pointer to memory block contains raw WOPI instrument file data + * @param length Length of given memory block + * @return 0 if no errors occouped, or an error code of WOPN_ErrorCodes enumeration + */ +extern int WOPN_LoadInstFromMem(OPNIFile *file, void *mem, size_t length); + +/** + * @brief Calculate the size of the output memory block + * @param file Heap-allocated WOPN file data structure + * @param version Destination version of the file + * @return Size of the raw WOPN file data + */ +extern size_t WOPN_CalculateBankFileSize(WOPNFile *file, uint16_t version); + +/** + * @brief Calculate the size of the output memory block + * @param file Pointer to WOPI file data structure + * @param version Destination version of the file + * @return Size of the raw WOPI file data + */ +extern size_t WOPN_CalculateInstFileSize(OPNIFile *file, uint16_t version); + +/** + * @brief Write raw WOPN into given memory block + * @param file Heap-allocated WOPN file data structure + * @param dest_mem Destination memory block pointer + * @param length Length of destination memory block + * @param version Wanted WOPN version + * @param force_gm Force GM set in saved bank file + * @return Error code or 0 on success + */ +extern int WOPN_SaveBankToMem(WOPNFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm); + +/** + * @brief Write raw WOPI into given memory block + * @param file Pointer to WOPI file data structure + * @param dest_mem Destination memory block pointer + * @param length Length of destination memory block + * @param version Wanted WOPI version + * @return Error code or 0 on success + */ +extern int WOPN_SaveInstToMem(OPNIFile *file, void *dest_mem, size_t length, uint16_t version); + +#ifdef __cplusplus +} +#endif + +#endif /* WOPN_FILE_H */ diff --git a/thirdparty/sndfile.h b/thirdparty/sndfile.h new file mode 100644 index 000000000..b5aa57fa7 --- /dev/null +++ b/thirdparty/sndfile.h @@ -0,0 +1,667 @@ +/* +** Copyright (C) 1999-2011Erik de Castro Lopo +** +** 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, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** sndfile.h -- system-wide definitions +** +** API documentation is in the doc/ directory of the source code tarball +** and at http://www.mega-nerd.com/libsndfile/api.html. +*/ + +#ifndef SNDFILE_H +#define SNDFILE_H + +/* This is the version 1.0.X header file. */ +#define SNDFILE_1 + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* The following file types can be read and written. +** A file type would consist of a major type (ie SF_FORMAT_WAV) bitwise +** ORed with a minor type (ie SF_FORMAT_PCM). SF_FORMAT_TYPEMASK and +** SF_FORMAT_SUBMASK can be used to separate the major and minor file +** types. +*/ + +enum +{ /* Major formats. */ + SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian default). */ + SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */ + SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */ + SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */ + SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */ + SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */ + SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */ + SF_FORMAT_VOC = 0x080000, /* VOC files. */ + SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */ + SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */ + SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */ + SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */ + SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */ + SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */ + SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */ + SF_FORMAT_SDS = 0x110000, /* Midi Sample Dump Standard */ + SF_FORMAT_AVR = 0x120000, /* Audio Visual Research */ + SF_FORMAT_WAVEX = 0x130000, /* MS WAVE with WAVEFORMATEX */ + SF_FORMAT_SD2 = 0x160000, /* Sound Designer 2 */ + SF_FORMAT_FLAC = 0x170000, /* FLAC lossless file format */ + SF_FORMAT_CAF = 0x180000, /* Core Audio File format */ + SF_FORMAT_WVE = 0x190000, /* Psion WVE format */ + SF_FORMAT_OGG = 0x200000, /* Xiph OGG container */ + SF_FORMAT_MPC2K = 0x210000, /* Akai MPC 2000 sampler */ + SF_FORMAT_RF64 = 0x220000, /* RF64 WAV file */ + + /* Subtypes from here on. */ + + SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */ + SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */ + SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */ + SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */ + + SF_FORMAT_PCM_U8 = 0x0005, /* Unsigned 8 bit data (WAV and RAW only) */ + + SF_FORMAT_FLOAT = 0x0006, /* 32 bit float data */ + SF_FORMAT_DOUBLE = 0x0007, /* 64 bit float data */ + + SF_FORMAT_ULAW = 0x0010, /* U-Law encoded. */ + SF_FORMAT_ALAW = 0x0011, /* A-Law encoded. */ + SF_FORMAT_IMA_ADPCM = 0x0012, /* IMA ADPCM. */ + SF_FORMAT_MS_ADPCM = 0x0013, /* Microsoft ADPCM. */ + + SF_FORMAT_GSM610 = 0x0020, /* GSM 6.10 encoding. */ + SF_FORMAT_VOX_ADPCM = 0x0021, /* OKI / Dialogix ADPCM */ + + SF_FORMAT_G721_32 = 0x0030, /* 32kbs G721 ADPCM encoding. */ + SF_FORMAT_G723_24 = 0x0031, /* 24kbs G723 ADPCM encoding. */ + SF_FORMAT_G723_40 = 0x0032, /* 40kbs G723 ADPCM encoding. */ + + SF_FORMAT_DWVW_12 = 0x0040, /* 12 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_16 = 0x0041, /* 16 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_24 = 0x0042, /* 24 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_N = 0x0043, /* N bit Delta Width Variable Word encoding. */ + + SF_FORMAT_DPCM_8 = 0x0050, /* 8 bit differential PCM (XI only) */ + SF_FORMAT_DPCM_16 = 0x0051, /* 16 bit differential PCM (XI only) */ + + SF_FORMAT_VORBIS = 0x0060, /* Xiph Vorbis encoding. */ + + /* Endian-ness options. */ + + SF_ENDIAN_FILE = 0x00000000, /* Default file endian-ness. */ + SF_ENDIAN_LITTLE = 0x10000000, /* Force little endian-ness. */ + SF_ENDIAN_BIG = 0x20000000, /* Force big endian-ness. */ + SF_ENDIAN_CPU = 0x30000000, /* Force CPU endian-ness. */ + + SF_FORMAT_SUBMASK = 0x0000FFFF, + SF_FORMAT_TYPEMASK = 0x0FFF0000, + SF_FORMAT_ENDMASK = 0x30000000 +} ; + +/* +** The following are the valid command numbers for the sf_command() +** interface. The use of these commands is documented in the file +** command.html in the doc directory of the source code distribution. +*/ + +enum +{ SFC_GET_LIB_VERSION = 0x1000, + SFC_GET_LOG_INFO = 0x1001, + SFC_GET_CURRENT_SF_INFO = 0x1002, + + + SFC_GET_NORM_DOUBLE = 0x1010, + SFC_GET_NORM_FLOAT = 0x1011, + SFC_SET_NORM_DOUBLE = 0x1012, + SFC_SET_NORM_FLOAT = 0x1013, + SFC_SET_SCALE_FLOAT_INT_READ = 0x1014, + SFC_SET_SCALE_INT_FLOAT_WRITE = 0x1015, + + SFC_GET_SIMPLE_FORMAT_COUNT = 0x1020, + SFC_GET_SIMPLE_FORMAT = 0x1021, + + SFC_GET_FORMAT_INFO = 0x1028, + + SFC_GET_FORMAT_MAJOR_COUNT = 0x1030, + SFC_GET_FORMAT_MAJOR = 0x1031, + SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032, + SFC_GET_FORMAT_SUBTYPE = 0x1033, + + SFC_CALC_SIGNAL_MAX = 0x1040, + SFC_CALC_NORM_SIGNAL_MAX = 0x1041, + SFC_CALC_MAX_ALL_CHANNELS = 0x1042, + SFC_CALC_NORM_MAX_ALL_CHANNELS = 0x1043, + SFC_GET_SIGNAL_MAX = 0x1044, + SFC_GET_MAX_ALL_CHANNELS = 0x1045, + + SFC_SET_ADD_PEAK_CHUNK = 0x1050, + SFC_SET_ADD_HEADER_PAD_CHUNK = 0x1051, + + SFC_UPDATE_HEADER_NOW = 0x1060, + SFC_SET_UPDATE_HEADER_AUTO = 0x1061, + + SFC_FILE_TRUNCATE = 0x1080, + + SFC_SET_RAW_START_OFFSET = 0x1090, + + SFC_SET_DITHER_ON_WRITE = 0x10A0, + SFC_SET_DITHER_ON_READ = 0x10A1, + + SFC_GET_DITHER_INFO_COUNT = 0x10A2, + SFC_GET_DITHER_INFO = 0x10A3, + + SFC_GET_EMBED_FILE_INFO = 0x10B0, + + SFC_SET_CLIPPING = 0x10C0, + SFC_GET_CLIPPING = 0x10C1, + + SFC_GET_INSTRUMENT = 0x10D0, + SFC_SET_INSTRUMENT = 0x10D1, + + SFC_GET_LOOP_INFO = 0x10E0, + + SFC_GET_BROADCAST_INFO = 0x10F0, + SFC_SET_BROADCAST_INFO = 0x10F1, + + SFC_GET_CHANNEL_MAP_INFO = 0x1100, + SFC_SET_CHANNEL_MAP_INFO = 0x1101, + + SFC_RAW_DATA_NEEDS_ENDSWAP = 0x1110, + + /* Support for Wavex Ambisonics Format */ + SFC_WAVEX_SET_AMBISONIC = 0x1200, + SFC_WAVEX_GET_AMBISONIC = 0x1201, + + SFC_SET_VBR_ENCODING_QUALITY = 0x1300, + + /* Following commands for testing only. */ + SFC_TEST_IEEE_FLOAT_REPLACE = 0x6001, + + /* + ** SFC_SET_ADD_* values are deprecated and will disappear at some + ** time in the future. They are guaranteed to be here up to and + ** including version 1.0.8 to avoid breakage of existng software. + ** They currently do nothing and will continue to do nothing. + */ + SFC_SET_ADD_DITHER_ON_WRITE = 0x1070, + SFC_SET_ADD_DITHER_ON_READ = 0x1071 +} ; + + +/* +** String types that can be set and read from files. Not all file types +** support this and even the file types which support one, may not support +** all string types. +*/ + +enum +{ SF_STR_TITLE = 0x01, + SF_STR_COPYRIGHT = 0x02, + SF_STR_SOFTWARE = 0x03, + SF_STR_ARTIST = 0x04, + SF_STR_COMMENT = 0x05, + SF_STR_DATE = 0x06, + SF_STR_ALBUM = 0x07, + SF_STR_LICENSE = 0x08, + SF_STR_TRACKNUMBER = 0x09, + SF_STR_GENRE = 0x10 +} ; + +/* +** Use the following as the start and end index when doing metadata +** transcoding. +*/ + +#define SF_STR_FIRST SF_STR_TITLE +#define SF_STR_LAST SF_STR_GENRE + +enum +{ /* True and false */ + SF_FALSE = 0, + SF_TRUE = 1, + + /* Modes for opening files. */ + SFM_READ = 0x10, + SFM_WRITE = 0x20, + SFM_RDWR = 0x30, + + SF_AMBISONIC_NONE = 0x40, + SF_AMBISONIC_B_FORMAT = 0x41 +} ; + +/* Public error values. These are guaranteed to remain unchanged for the duration +** of the library major version number. +** There are also a large number of private error numbers which are internal to +** the library which can change at any time. +*/ + +enum +{ SF_ERR_NO_ERROR = 0, + SF_ERR_UNRECOGNISED_FORMAT = 1, + SF_ERR_SYSTEM = 2, + SF_ERR_MALFORMED_FILE = 3, + SF_ERR_UNSUPPORTED_ENCODING = 4 +} ; + + +/* Channel map values (used with SFC_SET/GET_CHANNEL_MAP). +*/ + +enum +{ SF_CHANNEL_MAP_INVALID = 0, + SF_CHANNEL_MAP_MONO = 1, + SF_CHANNEL_MAP_LEFT, /* Apple calls this 'Left' */ + SF_CHANNEL_MAP_RIGHT, /* Apple calls this 'Right' */ + SF_CHANNEL_MAP_CENTER, /* Apple calls this 'Center' */ + SF_CHANNEL_MAP_FRONT_LEFT, + SF_CHANNEL_MAP_FRONT_RIGHT, + SF_CHANNEL_MAP_FRONT_CENTER, + SF_CHANNEL_MAP_REAR_CENTER, /* Apple calls this 'Center Surround', Msft calls this 'Back Center' */ + SF_CHANNEL_MAP_REAR_LEFT, /* Apple calls this 'Left Surround', Msft calls this 'Back Left' */ + SF_CHANNEL_MAP_REAR_RIGHT, /* Apple calls this 'Right Surround', Msft calls this 'Back Right' */ + SF_CHANNEL_MAP_LFE, /* Apple calls this 'LFEScreen', Msft calls this 'Low Frequency' */ + SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER, /* Apple calls this 'Left Center' */ + SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER, /* Apple calls this 'Right Center */ + SF_CHANNEL_MAP_SIDE_LEFT, /* Apple calls this 'Left Surround Direct' */ + SF_CHANNEL_MAP_SIDE_RIGHT, /* Apple calls this 'Right Surround Direct' */ + SF_CHANNEL_MAP_TOP_CENTER, /* Apple calls this 'Top Center Surround' */ + SF_CHANNEL_MAP_TOP_FRONT_LEFT, /* Apple calls this 'Vertical Height Left' */ + SF_CHANNEL_MAP_TOP_FRONT_RIGHT, /* Apple calls this 'Vertical Height Right' */ + SF_CHANNEL_MAP_TOP_FRONT_CENTER, /* Apple calls this 'Vertical Height Center' */ + SF_CHANNEL_MAP_TOP_REAR_LEFT, /* Apple and MS call this 'Top Back Left' */ + SF_CHANNEL_MAP_TOP_REAR_RIGHT, /* Apple and MS call this 'Top Back Right' */ + SF_CHANNEL_MAP_TOP_REAR_CENTER, /* Apple and MS call this 'Top Back Center' */ + + SF_CHANNEL_MAP_AMBISONIC_B_W, + SF_CHANNEL_MAP_AMBISONIC_B_X, + SF_CHANNEL_MAP_AMBISONIC_B_Y, + SF_CHANNEL_MAP_AMBISONIC_B_Z, + + SF_CHANNEL_MAP_MAX +} ; + + +/* A SNDFILE* pointer can be passed around much like stdio.h's FILE* pointer. */ + +typedef struct SNDFILE_tag SNDFILE ; + +/* The following typedef is system specific and is defined when libsndfile is +** compiled. sf_count_t will be a 64 bit value when the underlying OS allows +** 64 bit file offsets. +** On windows, we need to allow the same header file to be compiler by both GCC +** and the Microsoft compiler. +*/ + +#if (defined (_MSCVER) || defined (_MSC_VER)) +typedef __int64 sf_count_t ; +#define SF_COUNT_MAX 0x7fffffffffffffffi64 +#else +typedef int64_t sf_count_t ; +#define SF_COUNT_MAX 0x7FFFFFFFFFFFFFFFLL +#endif + + +/* A pointer to a SF_INFO structure is passed to sf_open () and filled in. +** On write, the SF_INFO structure is filled in by the user and passed into +** sf_open (). +*/ + +struct SF_INFO +{ sf_count_t frames ; /* Used to be called samples. Changed to avoid confusion. */ + int samplerate ; + int channels ; + int format ; + int sections ; + int seekable ; +} ; + +typedef struct SF_INFO SF_INFO ; + +/* The SF_FORMAT_INFO struct is used to retrieve information about the sound +** file formats libsndfile supports using the sf_command () interface. +** +** Using this interface will allow applications to support new file formats +** and encoding types when libsndfile is upgraded, without requiring +** re-compilation of the application. +** +** Please consult the libsndfile documentation (particularly the information +** on the sf_command () interface) for examples of its use. +*/ + +typedef struct +{ int format ; + const char *name ; + const char *extension ; +} SF_FORMAT_INFO ; + +/* +** Enums and typedefs for adding dither on read and write. +** See the html documentation for sf_command(), SFC_SET_DITHER_ON_WRITE +** and SFC_SET_DITHER_ON_READ. +*/ + +enum +{ SFD_DEFAULT_LEVEL = 0, + SFD_CUSTOM_LEVEL = 0x40000000, + + SFD_NO_DITHER = 500, + SFD_WHITE = 501, + SFD_TRIANGULAR_PDF = 502 +} ; + +typedef struct +{ int type ; + double level ; + const char *name ; +} SF_DITHER_INFO ; + +/* Struct used to retrieve information about a file embedded within a +** larger file. See SFC_GET_EMBED_FILE_INFO. +*/ + +typedef struct +{ sf_count_t offset ; + sf_count_t length ; +} SF_EMBED_FILE_INFO ; + +/* +** Structs used to retrieve music sample information from a file. +*/ + +enum +{ /* + ** The loop mode field in SF_INSTRUMENT will be one of the following. + */ + SF_LOOP_NONE = 800, + SF_LOOP_FORWARD, + SF_LOOP_BACKWARD, + SF_LOOP_ALTERNATING +} ; + +typedef struct +{ int gain ; + char basenote, detune ; + char velocity_lo, velocity_hi ; + char key_lo, key_hi ; + int loop_count ; + + struct + { int mode ; + unsigned int start ; + unsigned int end ; + unsigned int count ; + } loops [16] ; /* make variable in a sensible way */ +} SF_INSTRUMENT ; + + + +/* Struct used to retrieve loop information from a file.*/ +typedef struct +{ + short time_sig_num ; /* any positive integer > 0 */ + short time_sig_den ; /* any positive power of 2 > 0 */ + int loop_mode ; /* see SF_LOOP enum */ + + int num_beats ; /* this is NOT the amount of quarter notes !!!*/ + /* a full bar of 4/4 is 4 beats */ + /* a full bar of 7/8 is 7 beats */ + + float bpm ; /* suggestion, as it can be calculated using other fields:*/ + /* file's lenght, file's sampleRate and our time_sig_den*/ + /* -> bpms are always the amount of _quarter notes_ per minute */ + + int root_key ; /* MIDI note, or -1 for None */ + int future [6] ; +} SF_LOOP_INFO ; + + +/* Struct used to retrieve broadcast (EBU) information from a file. +** Strongly (!) based on EBU "bext" chunk format used in Broadcast WAVE. +*/ +#define SF_BROADCAST_INFO_VAR(coding_hist_size) \ + struct \ + { char description [256] ; \ + char originator [32] ; \ + char originator_reference [32] ; \ + char origination_date [10] ; \ + char origination_time [8] ; \ + unsigned int time_reference_low ; \ + unsigned int time_reference_high ; \ + short version ; \ + char umid [64] ; \ + char reserved [190] ; \ + unsigned int coding_history_size ; \ + char coding_history [coding_hist_size] ; \ + } + +/* SF_BROADCAST_INFO is the above struct with coding_history field of 256 bytes. */ +typedef SF_BROADCAST_INFO_VAR (256) SF_BROADCAST_INFO ; + + +/* Virtual I/O functionality. */ + +typedef sf_count_t (*sf_vio_get_filelen) (void *user_data) ; +typedef sf_count_t (*sf_vio_seek) (sf_count_t offset, int whence, void *user_data) ; +typedef sf_count_t (*sf_vio_read) (void *ptr, sf_count_t count, void *user_data) ; +typedef sf_count_t (*sf_vio_write) (const void *ptr, sf_count_t count, void *user_data) ; +typedef sf_count_t (*sf_vio_tell) (void *user_data) ; + +struct SF_VIRTUAL_IO +{ sf_vio_get_filelen get_filelen ; + sf_vio_seek seek ; + sf_vio_read read ; + sf_vio_write write ; + sf_vio_tell tell ; +} ; + +typedef struct SF_VIRTUAL_IO SF_VIRTUAL_IO ; + + +/* Open the specified file for read, write or both. On error, this will +** return a NULL pointer. To find the error number, pass a NULL SNDFILE +** to sf_strerror (). +** All calls to sf_open() should be matched with a call to sf_close(). +*/ + +SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) ; + + +/* Use the existing file descriptor to create a SNDFILE object. If close_desc +** is TRUE, the file descriptor will be closed when sf_close() is called. If +** it is FALSE, the descritor will not be closed. +** When passed a descriptor like this, the library will assume that the start +** of file header is at the current file offset. This allows sound files within +** larger container files to be read and/or written. +** On error, this will return a NULL pointer. To find the error number, pass a +** NULL SNDFILE to sf_strerror (). +** All calls to sf_open_fd() should be matched with a call to sf_close(). + +*/ + +SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ; + +SNDFILE* sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ; + + +/* sf_error () returns a error number which can be translated to a text +** string using sf_error_number(). +*/ + +int sf_error (SNDFILE *sndfile) ; + + +/* sf_strerror () returns to the caller a pointer to the current error message for +** the given SNDFILE. +*/ + +const char* sf_strerror (SNDFILE *sndfile) ; + + +/* sf_error_number () allows the retrieval of the error string for each internal +** error number. +** +*/ + +const char* sf_error_number (int errnum) ; + + +/* The following two error functions are deprecated but they will remain in the +** library for the forseeable future. The function sf_strerror() should be used +** in their place. +*/ + +int sf_perror (SNDFILE *sndfile) ; +int sf_error_str (SNDFILE *sndfile, char* str, size_t len) ; + + +/* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ + +int sf_command (SNDFILE *sndfile, int command, void *data, int datasize) ; + + +/* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ + +int sf_format_check (const SF_INFO *info) ; + + +/* Seek within the waveform data chunk of the SNDFILE. sf_seek () uses +** the same values for whence (SEEK_SET, SEEK_CUR and SEEK_END) as +** stdio.h function fseek (). +** An offset of zero with whence set to SEEK_SET will position the +** read / write pointer to the first data sample. +** On success sf_seek returns the current position in (multi-channel) +** samples from the start of the file. +** Please see the libsndfile documentation for moving the read pointer +** separately from the write pointer on files open in mode SFM_RDWR. +** On error all of these functions return -1. +*/ + +sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence) ; + + +/* Functions for retrieving and setting string data within sound files. +** Not all file types support this features; AIFF and WAV do. For both +** functions, the str_type parameter must be one of the SF_STR_* values +** defined above. +** On error, sf_set_string() returns non-zero while sf_get_string() +** returns NULL. +*/ + +int sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ; + +const char* sf_get_string (SNDFILE *sndfile, int str_type) ; + + +/* Return the library version string. */ + +const char * sf_version_string (void) ; + + +/* Functions for reading/writing the waveform data of a sound file. +*/ + +sf_count_t sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ; +sf_count_t sf_write_raw (SNDFILE *sndfile, const void *ptr, sf_count_t bytes) ; + + +/* Functions for reading and writing the data chunk in terms of frames. +** The number of items actually read/written = frames * number of channels. +** sf_xxxx_raw read/writes the raw data bytes from/to the file +** sf_xxxx_short passes data in the native short format +** sf_xxxx_int passes data in the native int format +** sf_xxxx_float passes data in the native float format +** sf_xxxx_double passes data in the native double format +** All of these read/write function return number of frames read/written. +*/ + +sf_count_t sf_readf_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ; +sf_count_t sf_writef_short (SNDFILE *sndfile, const short *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ; +sf_count_t sf_writef_int (SNDFILE *sndfile, const int *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ; +sf_count_t sf_writef_float (SNDFILE *sndfile, const float *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ; +sf_count_t sf_writef_double (SNDFILE *sndfile, const double *ptr, sf_count_t frames) ; + + +/* Functions for reading and writing the data chunk in terms of items. +** Otherwise similar to above. +** All of these read/write function return number of items read/written. +*/ + +sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ; +sf_count_t sf_write_short (SNDFILE *sndfile, const short *ptr, sf_count_t items) ; + +sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ; +sf_count_t sf_write_int (SNDFILE *sndfile, const int *ptr, sf_count_t items) ; + +sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ; +sf_count_t sf_write_float (SNDFILE *sndfile, const float *ptr, sf_count_t items) ; + +sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ; +sf_count_t sf_write_double (SNDFILE *sndfile, const double *ptr, sf_count_t items) ; + + +/* Close the SNDFILE and clean up all memory allocations associated with this +** file. +** Returns 0 on success, or an error number. +*/ + +int sf_close (SNDFILE *sndfile) ; + + +/* If the file is opened SFM_WRITE or SFM_RDWR, call fsync() on the file +** to force the writing of data to disk. If the file is opened SFM_READ +** no action is taken. +*/ + +void sf_write_sync (SNDFILE *sndfile) ; + + + +/* The function sf_wchar_open() is Windows Only! +** Open a file passing in a Windows Unicode filename. Otherwise, this is +** the same as sf_open(). +** +** In order for this to work, you need to do the following: +** +** #include +** #define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +** #including +*/ + +#if (defined (ENABLE_SNDFILE_WINDOWS_PROTOTYPES) && ENABLE_SNDFILE_WINDOWS_PROTOTYPES) +SNDFILE* sf_wchar_open (LPCWSTR wpath, int mode, SF_INFO *sfinfo) ; +#endif + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* SNDFILE_H */ + diff --git a/thirdparty/timidity/CHANGES b/thirdparty/timidity/CHANGES new file mode 100644 index 000000000..045e53739 --- /dev/null +++ b/thirdparty/timidity/CHANGES @@ -0,0 +1,58 @@ +This version of TiMidity should contain all the fixes from the +September 25 2003 SDL_mixer CVS snapshot, plus extended GUS patch +support from later SDL_mixer. In addition, it contains these changes +from SDL_sound: + +* Removal of much unused or unnecessary code, such as + + + The "hooks" for putting a user interface onto TiMidity. + + The antialias filter. It wasn't active, and even at 4 kHz I + couldn't hear any difference when activating it. + + Removed all traces of LOOKUP_HACK and LOOKUP_INTERPOLATION. + According to the code comments they weren't very good anyway. + ("degrades sound quality noticeably"). I also removed the + disclaimer about the "8-bit uLaw to 16-bit PCM and the 13-bit-PCM + to 8-bit uLaw tables" disclaimer, since I believe those were the + tables I removed. + + Removed LOOKUP_SINE since it was already commented out. I think we + can count on our target audience having math co-processors + nowadays. + + Removed USE_LDEXP since it wasn't being used and "it doesn't make + much of a difference either way". + + Removed decompress hack from open_file() since it didn't look very + portable. + + Removed heaps of unnecessary constants. + + Removed unused functions. + + Assume that LINEAR_INTERPOLATION is always used, so remove all + code dealing with it not being so. It's not that I think the + difference in audio quality is that great, but since it wouldn't + compile without code changes I assume no one's used it for quite + some time... + + Assume PRECALC_LOOPS is always defined. Judging by the comments it + may not make much of a difference either way, so why maintain two + versions of the same code? + +* Made TiMidity look for its configuration file in both /etc and + /usr/local/lib/timidity. (Windows version remains unchanged.) + +* The following files have been removed: controls.c, controls.h, + filter.c, filter.h, sdl_a.c, sdl_c.c + +* Added support for loading DLS format instruments: + Timidity_LoadDLS(), Timidity_FreeDLS(), Timidity_LoadDLSSong() + +This version of TiMidity also contains my own changes for ZDoom: + +* Removed readmidi.c: TiMidity is now fed MIDI events directly to + produce output. The TimidityMIDIDevice class is responsible for + feeding TiMidity data and collecting output from it. Since + ZDoom's MIDI parser ignores SysEx messages, so does this TiMidity, + though this can be changed if necessary. + +* Removed all the precalculated math from tables.c in favor of using + real math functions. + +* All sample values are now floats, and only a stereo 32-bit float + output buffer is supported. + +* Moved everything into the Timidity namespace. diff --git a/thirdparty/timidity/CMakeLists.txt b/thirdparty/timidity/CMakeLists.txt new file mode 100644 index 000000000..be22ccdf7 --- /dev/null +++ b/thirdparty/timidity/CMakeLists.txt @@ -0,0 +1,32 @@ +add_library(timidity OBJECT + common.cpp + instrum.cpp + instrum_dls.cpp + instrum_font.cpp + instrum_sf2.cpp + mix.cpp + playmidi.cpp + resample.cpp + timidity.cpp +) + +file(GLOB HEADER_FILES timidity/*.h) +target_sources(timidity PRIVATE ${HEADER_FILES}) + +target_include_directories(timidity INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE timidity) + +set_target_properties(timidity +PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON +) + +use_fast_math(timidity) +require_stricmp(timidity PRIVATE) + +if(COMPILER_IS_GNUC_COMPATIBLE) + target_compile_options(timidity + PRIVATE + "-Wno-unused-function" "-Wno-unused-variable" + ) +endif() diff --git a/thirdparty/timidity/COPYING b/thirdparty/timidity/COPYING new file mode 100644 index 000000000..2fa5da3ac --- /dev/null +++ b/thirdparty/timidity/COPYING @@ -0,0 +1,513 @@ +Please note that the included source from Timidity, the MIDI decoder, is also + licensed under the following terms (GNU LGPL), but can also be used + separately under the GNU GPL, or the Perl Artistic License. Those licensing + terms are not reprinted here, but can be found on the web easily. + + +------------------- + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/thirdparty/timidity/FAQ b/thirdparty/timidity/FAQ new file mode 100644 index 000000000..1ee0b77bf --- /dev/null +++ b/thirdparty/timidity/FAQ @@ -0,0 +1,100 @@ +---------------------------*-indented-text-*------------------------------ + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + +-------------------------------------------------------------------------- + + Frequently Asked Questions with answers: + +-------------------------------------------------------------------------- +Q: What is it? + +A: Where? Well Chris, TiMidity is a software-only synthesizer, MIDI + renderer, MIDI to WAVE converter, realtime MIDI player for UNIX machines, + even (I've heard) a Netscape helper application. It takes a MIDI file + and writes a WAVE or raw PCM data or plays it on your digital audio + device. It sounds much more realistic than FM synthesis, but you need a + ~100Mhz processor to listen to 32kHz stereo music in the background while + you work. 11kHz mono can be played on a low-end 486, and, to some, it + still sounds better than FM. + +-------------------------------------------------------------------------- +Q: I don't have a GUS, can I use TiMidity? + +A: Yes. That's the point. You don't need a Gravis Ultrasound to use + TiMidity, you just need GUS-compatible patches, which are freely + available on the Internet. See below for pointers. + +-------------------------------------------------------------------------- +Q: I have a GUS, can I use TiMidity? + +A: The DOS port doesn't have GUS support, and TiMidity won't be taking + advantage of the board's internal synthesizer under other operating + systems either. So it kind of defeats the purpose. But you can use it. + +-------------------------------------------------------------------------- +Q: I tried playing a MIDI file I got off the Net but all I got was a + dozen warnings saying "No instrument mapped to tone bank 0, program + xx - this instrument will not be heard". What's wrong? + +A: The General MIDI standard specifies 128 melodic instruments and + some sixty percussion sounds. If you wish to play arbitrary General + MIDI files, you'll need to get more patch files. + + There's a program called Midia for SGI's, which also plays MIDI + files and has a lot more bells and whistles than TiMidity. It uses + GUS-compatible patches, too -- so you can get the 8 MB set at + ftp://archive.cs.umbc.edu/pub/midia for pretty good GM compatibility. + + There are also many excellent patches on the Ultrasound FTP sites. + I can recommend Dustin McCartney's collections gsdrum*.zip and + wow*.zip in the "[.../]sound/patches/files" directory. The huge + ProPats series (pp3-*.zip) contains good patches as well. General + MIDI files can also be found on these sites. + + This site list is from the GUS FAQ: + +> FTP Sites Archive Directories +> --------- ------------------- +> Main N.American Site: archive.orst.edu pub/packages/gravis +> wuarchive.wustl.edu systems/ibmpc/ultrasound +> Main Asian Site: nctuccca.edu.tw PC/ultrasound +> Main European Site: src.doc.ic.ac.uk packages/ultrasound +> Main Australian Site: ftp.mpx.com.au /ultrasound/general +> /ultrasound/submit +> South African Site: ftp.sun.ac.za /pub/packages/ultrasound +> Submissions: archive.epas.utoronto.ca pub/pc/ultrasound/submit +> Newly Validated Files: archive.epas.utoronto.ca pub/pc/ultrasound +> +> Mirrors: garbo.uwasa.fi mirror/ultrasound +> ftp.st.nepean.uws.edu.au pc/ultrasound +> ftp.luth.se pub/msdos/ultrasound + +-------------------------------------------------------------------------- +Q: Some files have awful clicks and pops. + +A: Find out which patch is responsible for the clicking (try "timidity + -P ". Add "strip=tail" in + the config file after its name. If this doesn't fix it, mail me the + patch. + +-------------------------------------------------------------------------- +Q: I'm playing Fantasie Impromptu in the background. When I run Netscape, + the sound gets choppy and it takes ten minutes to load. What can I do? + +A: Here are some things to try: + + - Use a lower sampling rate. + + - Use mono output. This can improve performance by 10-30%. + (Using 8-bit instead of 16-bit output makes no difference.) + + - Use a smaller number of simultaneous voices. + + - Make sure you compiled with FAST_DECAY enabled in options.h + + - Recompile with an Intel-optimized gcc for a 5-15% + performance increase. + +-------------------------------------------------------------------------- diff --git a/thirdparty/timidity/README b/thirdparty/timidity/README new file mode 100644 index 000000000..9c9c55aad --- /dev/null +++ b/thirdparty/timidity/README @@ -0,0 +1,61 @@ +[This version of timidity has been stripped for simplicity in porting to SDL, +and then even further for SDL_sound] +---------------------------------*-text-*--------------------------------- + + From http://www.cgs.fi/~tt/discontinued.html : + + If you'd like to continue hacking on TiMidity, feel free. I'm + hereby extending the TiMidity license agreement: you can now + select the most convenient license for your needs from (1) the + GNU GPL, (2) the GNU LGPL, or (3) the Perl Artistic License. + +-------------------------------------------------------------------------- + + This is the README file for TiMidity v0.2i + + TiMidity is a MIDI to WAVE converter that uses Gravis +Ultrasound(*)-compatible patch files to generate digital audio data +from General MIDI files. The audio data can be played through any +sound device or stored on disk. On a fast machine, music can be +played in real time. TiMidity runs under Linux, FreeBSD, HP-UX, SunOS, and +Win32, and porting to other systems with gcc should be easy. + + TiMidity Features: + + * 32 or more dynamically allocated fully independent voices + * Compatibility with GUS patch files + * Output to 16- or 8-bit PCM or uLaw audio device, file, or + stdout at any sampling rate + * Optional interactive mode with real-time status display + under ncurses and SLang terminal control libraries. Also + a user friendly motif interface since version 0.2h + * Support for transparent loading of compressed MIDI files and + patch files + + * Support for the following MIDI events: + - Program change + - Key pressure + - Channel main volume + - Tempo + - Panning + - Damper pedal (Sustain) + - Pitch wheel + - Pitch wheel sensitivity + - Change drum set + +* The GNU General Public License can, as always, be found in the file + "../COPYING". + +* TiMidity requires sampled instruments (patches) to play MIDI files. You + should get the file "timidity-lib-0.1.tar.gz" and unpack it in the same + directory where you unpacked the source code archive. You'll want more + patches later -- read the file "FAQ" for pointers. + +* Timidity is no longer supported, but can be found by searching the web. + + + Tuukka Toivonen + +[(*) Any Registered Trademarks used anywhere in the documentation or +source code for TiMidity are acknowledged as belonging to their +respective owners.] diff --git a/thirdparty/timidity/common.cpp b/thirdparty/timidity/common.cpp new file mode 100644 index 000000000..53f099f6b --- /dev/null +++ b/thirdparty/timidity/common.cpp @@ -0,0 +1,67 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + common.c + +*/ + +#include +#include +#include + + +namespace Timidity +{ + + + +/* This'll allocate memory or die. */ +void *safe_malloc(size_t count) +{ +#if 0 + char buffer[80]; + void *p; + if (count > (1 << 21)) + { + snprintf(buffer, 80, "Timidity: Tried allocating %zu bytes. This must be a bug.", count); + throw std::runtime_error(buffer); + } + else if ((p = malloc(count))) + { + return p; + } + else + { + snprintf(buffer, 80, "Timidity: Couldn't malloc %zu bytes.", count); + throw std::runtime_error(buffer); + } + return 0; // Unreachable. +#else + // No, we cannot throw exceptions here - this code can get called from real-time worker threads. + auto p = malloc(count); + if (!p) + { + abort(); // we must abort, though... + } + return p; +#endif +} + + +} diff --git a/thirdparty/timidity/instrum.cpp b/thirdparty/timidity/instrum.cpp new file mode 100644 index 000000000..9240de456 --- /dev/null +++ b/thirdparty/timidity/instrum.cpp @@ -0,0 +1,728 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + instrum.c + + Code to load and unload GUS-compatible instrument patches. + +*/ + +#include +#include +#include +#include +#include +#include + +#include "timidity.h" +#include "gf1patch.h" +#include "t_swap.h" + +#include "common.h" +#include "instrum.h" +#include "playmidi.h" + +namespace Timidity +{ + +Instrument *load_instrument_dls(Renderer *song, int drum, int bank, int instrument); + +Instrument::Instrument() +: samples(0), sample(NULL) +{ +} + +Instrument::~Instrument() +{ + Sample *sp; + int i; + + for (i = samples, sp = &(sample[0]); i != 0; i--, sp++) + { + if (sp->type == INST_GUS && sp->data != NULL) + { + free(sp->data); + } + } + free(sample); +} + +ToneBank::ToneBank() +{ + tone = new ToneBankElement[128];; + for (int i = 0; i < MAXPROG; ++i) + { + instrument[i] = 0; + } +} + +ToneBank::~ToneBank() +{ + delete[] tone; + for (int i = 0; i < MAXPROG; i++) + { + if (instrument[i] != NULL && instrument[i] != MAGIC_LOAD_INSTRUMENT) + { + delete instrument[i]; + instrument[i] = NULL; + } + } +} + +int Renderer::convert_tremolo_sweep(uint8_t sweep) +{ + if (sweep == 0) + return 0; + + return + int(((control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / (rate * sweep)); +} + +int Renderer::convert_vibrato_sweep(uint8_t sweep, int vib_control_ratio) +{ + if (sweep == 0) + return 0; + + return + (int)(FSCALE((double)(vib_control_ratio)* SWEEP_TUNING, SWEEP_SHIFT) / (rate * sweep)); + + /* this was overflowing with seashore.pat + + ((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / (song->rate * sweep); + */ +} + +int Renderer::convert_tremolo_rate(uint8_t rate) +{ + return + int(((control_ratio * rate) << RATE_SHIFT) / (TREMOLO_RATE_TUNING * rate)); +} + +int Renderer::convert_vibrato_rate(uint8_t rate) +{ + /* Return a suitable vibrato_control_ratio value */ + return + int((VIBRATO_RATE_TUNING * rate) / (rate * 2 * VIBRATO_SAMPLE_INCREMENTS)); +} + +static void reverse_data(sample_t *sp, int ls, int le) +{ + sample_t s, *ep = sp + le; + sp += ls; + le -= ls; + le /= 2; + while (le--) + { + s = *sp; + *sp++ = *ep; + *ep-- = s; + } +} + +/* + If panning or note_to_use != -1, it will be used for all samples, + instead of the sample-specific values in the instrument file. + + For note_to_use, any value <0 or >127 will be forced to 0. + + For other parameters, 1 means yes, 0 means no, other values are + undefined. + + TODO: do reverse loops right */ +Instrument *Renderer::load_instrument(const char *name, int percussion, + int panning, int note_to_use, + int strip_loop, int strip_envelope, + int strip_tail) +{ + Instrument *ip; + Sample *sp; + GF1PatchHeader header; + GF1InstrumentData idata; + GF1LayerData layer_data; + GF1PatchData patch_data; + int i, j; + bool noluck = false; + auto reader = instruments->sfreader; + + if (!name || reader == nullptr) return nullptr; + + /* Open patch file */ + auto fp = reader->open_file(name); + if (!fp) + { + /* Try with various extensions */ + std::string tmp = name; + tmp += ".pat"; + fp = reader->open_file(tmp.c_str()); + if (!fp) + { +#ifndef _WIN32 // Windows isn't case-sensitive. + std::transform(tmp.begin(), tmp.end(), tmp.begin(), [](unsigned char c){ return toupper(c); } ); + fp = reader->open_file(tmp.c_str()); + if (!fp) +#endif + { + noluck = true; + } + } + } + + if (noluck) + { + printMessage(CMSG_ERROR, VERB_DEBUG, "Instrument `%s' can't be found.\n", name); + return 0; + } + + printMessage(CMSG_INFO, VERB_NOISY, "Loading instrument %s\n", name); + + /* Read some headers and do cursory sanity checks. */ + + if (sizeof(header) != fp->read(&header, sizeof(header))) + { +failread: + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: Error reading instrument.\n", name); + fp->close(); + return 0; + } + if (strncmp(header.Header, GF1_HEADER_TEXT, HEADER_SIZE - 4) != 0) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: Not an instrument.\n", name); + fp->close(); + return 0; + } + if (strcmp(header.Header + 8, "110") < 0) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: Is an old and unsupported patch version.\n", name); + fp->close(); + return 0; + } + if (sizeof(idata) != fp->read(&idata, sizeof(idata))) + { + goto failread; + } + + header.WaveForms = LittleShort(header.WaveForms); + header.MasterVolume = LittleShort(header.MasterVolume); + header.DataSize = LittleLong(header.DataSize); + idata.Instrument = LittleShort(idata.Instrument); + + if (header.Instruments != 1 && header.Instruments != 0) /* instruments. To some patch makers, 0 means 1 */ + { + printMessage(CMSG_ERROR, VERB_NORMAL, "Can't handle patches with %d instruments.\n", header.Instruments); + fp->close(); + return 0; + } + + if (idata.Layers != 1 && idata.Layers != 0) /* layers. What's a layer? */ + { + printMessage(CMSG_ERROR, VERB_NORMAL, "Can't handle instruments with %d layers.\n", idata.Layers); + fp->close(); + return 0; + } + + if (sizeof(layer_data) != fp->read(&layer_data, sizeof(layer_data))) + { + goto failread; + } + + if (layer_data.Samples == 0) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "Instrument has 0 samples.\n"); + fp->close(); + return 0; + } + + ip = new Instrument; + ip->samples = layer_data.Samples; + ip->sample = (Sample *)safe_malloc(sizeof(Sample) * layer_data.Samples); + memset(ip->sample, 0, sizeof(Sample) * layer_data.Samples); + for (i = 0; i < layer_data.Samples; ++i) + { + if (sizeof(patch_data) != fp->read(&patch_data, sizeof(patch_data))) + { +fail: + printMessage(CMSG_ERROR, VERB_NORMAL, "Error reading sample %d.\n", i); + delete ip; + fp->close(); + return 0; + } + + sp = &(ip->sample[i]); + + sp->data_length = LittleLong(patch_data.WaveSize); + sp->loop_start = LittleLong(patch_data.StartLoop); + sp->loop_end = LittleLong(patch_data.EndLoop); + sp->sample_rate = LittleShort(patch_data.SampleRate); + sp->low_freq = float(LittleLong(patch_data.LowFrequency)); + sp->high_freq = float(LittleLong(patch_data.HighFrequency)) + 0.9999f; + sp->root_freq = float(LittleLong(patch_data.RootFrequency)); + sp->high_vel = 127; + sp->velocity = -1; + sp->type = INST_GUS; + + // Expand to SF2 range. + if (panning == -1) + { + sp->panning = (patch_data.Balance & 0x0F) * 1000 / 15 - 500; + } + else + { + sp->panning = (panning & 0x7f) * 1000 / 127 - 500; + } + compute_pan((sp->panning + 500) / 1000.0, INST_GUS, sp->left_offset, sp->right_offset); + + /* tremolo */ + if (patch_data.TremoloRate == 0 || patch_data.TremoloDepth == 0) + { + sp->tremolo_sweep_increment = 0; + sp->tremolo_phase_increment = 0; + sp->tremolo_depth = 0; + printMessage(CMSG_INFO, VERB_DEBUG, " * no tremolo\n"); + } + else + { + sp->tremolo_sweep_increment = convert_tremolo_sweep(patch_data.TremoloSweep); + sp->tremolo_phase_increment = convert_tremolo_rate(patch_data.TremoloRate); + sp->tremolo_depth = patch_data.TremoloDepth; + printMessage(CMSG_INFO, VERB_DEBUG, " * tremolo: sweep %d, phase %d, depth %d\n", + sp->tremolo_sweep_increment, sp->tremolo_phase_increment, sp->tremolo_depth); + } + + /* vibrato */ + if (patch_data.VibratoRate == 0 || patch_data.VibratoDepth == 0) + { + sp->vibrato_sweep_increment = 0; + sp->vibrato_control_ratio = 0; + sp->vibrato_depth = 0; + printMessage(CMSG_INFO, VERB_DEBUG, " * no vibrato\n"); + } + else + { + sp->vibrato_control_ratio = convert_vibrato_rate(patch_data.VibratoRate); + sp->vibrato_sweep_increment = convert_vibrato_sweep(patch_data.VibratoSweep, sp->vibrato_control_ratio); + sp->vibrato_depth = patch_data.VibratoDepth; + printMessage(CMSG_INFO, VERB_DEBUG, " * vibrato: sweep %d, ctl %d, depth %d\n", + sp->vibrato_sweep_increment, sp->vibrato_control_ratio, sp->vibrato_depth); + } + + sp->modes = patch_data.Modes; + + /* Mark this as a fixed-pitch instrument if such a deed is desired. */ + if (note_to_use != -1) + { + sp->scale_note = note_to_use; + sp->scale_factor = 0; + } + else + { + sp->scale_note = LittleShort(patch_data.ScaleFrequency); + sp->scale_factor = LittleShort(patch_data.ScaleFactor); + if (sp->scale_factor <= 2) + { + sp->scale_factor *= 1024; + } + else if (sp->scale_factor > 2048) + { + sp->scale_factor = 1024; + } + if (sp->scale_factor != 1024) + { + printMessage(CMSG_INFO, VERB_DEBUG, " * Scale: note %d, factor %d\n", + sp->scale_note, sp->scale_factor); + } + } + +#if 0 + /* seashore.pat in the Midia patch set has no Sustain. I don't + understand why, and fixing it by adding the Sustain flag to + all looped patches probably breaks something else. We do it + anyway. */ + + if (sp->modes & PATCH_LOOPEN) + { + sp->modes |= PATCH_SUSTAIN; + } +#endif + /* [RH] Alas, eawpats has percussion instruments with bad envelopes. :( + * (See cymchina.pat for one example of this sadness.) + * Do this logic for instruments without a description, only. Hopefully that + * catches all the patches that need it without including any extra. + */ + for (j = 0; j < DESC_SIZE; ++j) + { + if (header.Description[j] != 0) + break; + } + /* Strip any loops and envelopes we're permitted to */ + /* [RH] (But PATCH_BACKWARD isn't a loop flag at all!) */ + if ((strip_loop == 1) && + (sp->modes & (PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD))) + { + printMessage(CMSG_INFO, VERB_DEBUG, " - Removing loop and/or sustain\n"); + if (j == DESC_SIZE) + { + sp->modes &= ~(PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD); + } + } + + if (strip_envelope == 1) + { + printMessage(CMSG_INFO, VERB_DEBUG, " - Removing envelope\n"); + /* [RH] The envelope isn't really removed, but this is the way the standard + * Gravis patches get that effect: All rates at maximum, and all offsets at + * a constant level. + */ + if (j == DESC_SIZE) + { + int k; + for (k = 1; k < ENVELOPES; ++k) + { /* Find highest offset. */ + if (patch_data.EnvelopeOffset[k] > patch_data.EnvelopeOffset[0]) + { + patch_data.EnvelopeOffset[0] = patch_data.EnvelopeOffset[k]; + } + } + for (k = 0; k < ENVELOPES; ++k) + { + patch_data.EnvelopeRate[k] = 63; + patch_data.EnvelopeOffset[k] = patch_data.EnvelopeOffset[0]; + } + } + } + + for (j = 0; j < 6; j++) + { + sp->envelope.gf1.rate[j] = patch_data.EnvelopeRate[j]; + /* [RH] GF1NEW clamps the offsets to the range [5,251], so we do too. */ + sp->envelope.gf1.offset[j] = std::max(5, std::min(251, patch_data.EnvelopeOffset[j])); + } + + /* Then read the sample data */ + if (((sp->modes & PATCH_16) && sp->data_length/2 > MAX_SAMPLE_SIZE) || + (!(sp->modes & PATCH_16) && sp->data_length > MAX_SAMPLE_SIZE)) + { + goto fail; + } + sp->data = (sample_t *)safe_malloc(sp->data_length); + + if (sp->data_length != fp->read(sp->data, sp->data_length)) + goto fail; + + convert_sample_data(sp, sp->data); + + /* Reverse reverse loops and pass them off as normal loops */ + if (sp->modes & PATCH_BACKWARD) + { + int t; + /* The GUS apparently plays reverse loops by reversing the + whole sample. We do the same because the GUS does not SUCK. */ + + printMessage(CMSG_WARNING, VERB_NORMAL, "Reverse loop in %s\n", name); + reverse_data((sample_t *)sp->data, 0, sp->data_length); + sp->data[sp->data_length] = sp->data[sp->data_length - 1]; + + t = sp->loop_start; + sp->loop_start = sp->data_length - sp->loop_end; + sp->loop_end = sp->data_length - t; + + sp->modes &= ~PATCH_BACKWARD; + sp->modes |= PATCH_LOOPEN; /* just in case */ + } + + /* Then fractional samples */ + sp->data_length <<= FRACTION_BITS; + sp->loop_start <<= FRACTION_BITS; + sp->loop_end <<= FRACTION_BITS; + + /* Adjust for fractional loop points. */ + sp->loop_start |= (patch_data.Fractions & 0x0F) << (FRACTION_BITS-4); + sp->loop_end |= (patch_data.Fractions & 0xF0) << (FRACTION_BITS-4-4); + + /* If this instrument will always be played on the same note, + and it's not looped, we can resample it now. */ + if (sp->scale_factor == 0 && !(sp->modes & PATCH_LOOPEN)) + { + pre_resample(this, sp); + } + + if (strip_tail == 1) + { + /* Let's not really, just say we did. */ + printMessage(CMSG_INFO, VERB_DEBUG, " - Stripping tail\n"); + sp->data_length = sp->loop_end; + } + } + fp->close(); + return ip; +} + +void convert_sample_data(Sample *sp, const void *data) +{ + /* convert everything to 32-bit floating point data */ + sample_t *newdata = NULL; + + switch (sp->modes & (PATCH_16 | PATCH_UNSIGNED)) + { + case 0: + { /* 8-bit, signed */ + int8_t *cp = (int8_t *)data; + newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t)); + for (int i = 0; i < sp->data_length; ++i) + { + if (cp[i] < 0) + { + newdata[i] = float(cp[i]) / 128.f; + } + else + { + newdata[i] = float(cp[i]) / 127.f; + } + } + break; + } + + case PATCH_UNSIGNED: + { /* 8-bit, unsigned */ + uint8_t *cp = (uint8_t *)data; + newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t)); + for (int i = 0; i < sp->data_length; ++i) + { + int c = cp[i] - 128; + if (c < 0) + { + newdata[i] = float(c) / 128.f; + } + else + { + newdata[i] = float(c) / 127.f; + } + } + break; + } + + case PATCH_16: + { /* 16-bit, signed */ + int16_t *cp = (int16_t *)data; + /* Convert these to samples */ + sp->data_length >>= 1; + sp->loop_start >>= 1; + sp->loop_end >>= 1; + newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t)); + for (int i = 0; i < sp->data_length; ++i) + { + int c = LittleShort(cp[i]); + if (c < 0) + { + newdata[i] = float(c) / 32768.f; + } + else + { + newdata[i] = float(c) / 32767.f; + } + } + break; + } + + case PATCH_16 | PATCH_UNSIGNED: + { /* 16-bit, unsigned */ + auto *cp = (uint16_t *)data; + /* Convert these to samples */ + sp->data_length >>= 1; + sp->loop_start >>= 1; + sp->loop_end >>= 1; + newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t)); + for (int i = 0; i < sp->data_length; ++i) + { + int c = LittleShort(cp[i]) - 32768; + if (c < 0) + { + newdata[i] = float(c) / 32768.f; + } + else + { + newdata[i] = float(c) / 32767.f; + } + } + break; + } + } + /* Duplicate the final sample for linear interpolation. */ + newdata[sp->data_length] = newdata[sp->data_length - 1]; + if (sp->data != NULL) + { + free(sp->data); + } + sp->data = newdata; +} + +int Renderer::fill_bank(int dr, int b) +{ + int i, errors = 0; + ToneBank *bank = ((dr) ? instruments->drumset[b] : instruments->tonebank[b]); + if (bank == NULL) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "Tried to load instruments in non-existent %s %d\n", + (dr) ? "drumset" : "tone bank", b); + return 0; + } + for (i = 0; i < MAXPROG; i++) + { + if (bank->instrument[i] == MAGIC_LOAD_INSTRUMENT) + { + bank->instrument[i] = NULL; + bank->instrument[i] = load_instrument_dls(this, dr, b, i); + if (bank->instrument[i] != NULL) + { + continue; + } + Instrument *ip; + ip = load_instrument_font_order(0, dr, b, i); + if (ip == NULL) + { + if (bank->tone[i].fontbank >= 0) + { + ip = load_instrument_font(bank->tone[i].name.c_str(), dr, b, i); + } + else + { + ip = load_instrument(bank->tone[i].name.c_str(), + (dr) ? 1 : 0, + bank->tone[i].pan, + (bank->tone[i].note != -1) ? bank->tone[i].note : ((dr) ? i : -1), + (bank->tone[i].strip_loop != -1) ? bank->tone[i].strip_loop : ((dr) ? 1 : -1), + (bank->tone[i].strip_envelope != -1) ? bank->tone[i].strip_envelope : ((dr) ? 1 : -1), + bank->tone[i].strip_tail); + } + if (ip == NULL) + { + ip = load_instrument_font_order(1, dr, b, i); + } + } + bank->instrument[i] = ip; + if (ip == NULL) + { + if (bank->tone[i].name.length() == 0) + { + printMessage(CMSG_WARNING, (b != 0) ? VERB_VERBOSE : VERB_DEBUG, + "No instrument mapped to %s %d, program %d%s\n", + (dr) ? "drum set" : "tone bank", b, i, + (b != 0) ? "" : " - this instrument will not be heard"); + } + else + { + printMessage(CMSG_ERROR, VERB_DEBUG, + "Couldn't load instrument %s (%s %d, program %d)\n", + bank->tone[i].name.c_str(), + (dr) ? "drum set" : "tone bank", b, i); + } + if (b != 0) + { + /* Mark the corresponding instrument in the default + bank / drumset for loading (if it isn't already) */ + if (((dr) ? instruments->drumset[0] : instruments->tonebank[0])->instrument[i] != NULL) + { + ((dr) ? instruments->drumset[0] : instruments->tonebank[0])->instrument[i] = MAGIC_LOAD_INSTRUMENT; + } + } + errors++; + } + } + } + return errors; +} + +int Renderer::load_missing_instruments() +{ + int i = MAXBANK, errors = 0; + while (i--) + { + if (instruments->tonebank[i] != NULL) + errors += fill_bank(0, i); + if (instruments->drumset[i] != NULL) + errors += fill_bank(1, i); + } + return errors; +} + +void Instruments::free_instruments() +{ + int i = MAXBANK; + while (i--) + { + if (tonebank[i] != NULL) + { + delete tonebank[i]; + tonebank[i] = NULL; + } + if (drumset[i] != NULL) + { + delete drumset[i]; + drumset[i] = NULL; + } + } +} + +Instruments::Instruments(MusicIO::SoundFontReaderInterface *reader) +{ + sfreader = reader; + tonebank[0] = new ToneBank; + drumset[0] = new ToneBank; +} + +Instruments::~Instruments() +{ + free_instruments(); + font_freeall(); + for (int i = 0; i < MAXBANK; ++i) + { + if (tonebank[i] != NULL) + { + delete tonebank[i]; + tonebank[i] = NULL; + } + if (drumset[i] != NULL) + { + delete drumset[i]; + drumset[i] = NULL; + } + } + if (sfreader != nullptr) sfreader->close(); + sfreader = nullptr; +} + + +int Renderer::set_default_instrument(const char *name) +{ + Instrument *ip; + if ((ip = load_instrument(name, 0, -1, -1, 0, 0, 0)) == NULL) + { + return -1; + } + if (default_instrument != NULL) + { + delete default_instrument; + } + default_instrument = ip; + default_program = SPECIAL_PROGRAM; + return 0; +} + + +} diff --git a/thirdparty/timidity/instrum_dls.cpp b/thirdparty/timidity/instrum_dls.cpp new file mode 100644 index 000000000..24c4c2ebc --- /dev/null +++ b/thirdparty/timidity/instrum_dls.cpp @@ -0,0 +1,1243 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + instrum_dls.c + +*/ + +#include +#include +#include +#include + +#include "t_swap.h" +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" + + +#define __Sound_SetError(x) + +namespace Timidity +{ + +/*-------------------------------------------------------------------------*/ +/* * * * * * * * * * * * * * * * * load_riff.h * * * * * * * * * * * * * * */ +/*-------------------------------------------------------------------------*/ +struct RIFF_Chunk +{ + RIFF_Chunk() + { + memset(this, 0, sizeof(*this)); + } + ~RIFF_Chunk() + { + // data is not freed here because it may be owned by a parent chunk + if (child != NULL) + { + delete child; + } + if (next != NULL) + { + delete next; + } + } + + uint32_t magic; + uint32_t length; + uint32_t subtype; + uint8_t *data; + RIFF_Chunk *child; + RIFF_Chunk *next; +}; + +RIFF_Chunk *LoadRIFF(FILE *src); +void FreeRIFF(RIFF_Chunk *chunk); +void PrintRIFF(RIFF_Chunk *chunk, int level); +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*-------------------------------------------------------------------------*/ +/* * * * * * * * * * * * * * * * * load_riff.c * * * * * * * * * * * * * * */ +/*-------------------------------------------------------------------------*/ +#define RIFF MAKE_ID('R','I','F','F') +#define LIST MAKE_ID('L','I','S','T') + +static bool ChunkHasSubType(uint32_t magic) +{ + return (magic == RIFF || magic == LIST); +} + +static int ChunkHasSubChunks(uint32_t magic) +{ + return (magic == RIFF || magic == LIST); +} + +static void LoadSubChunks(RIFF_Chunk *chunk, uint8_t *data, uint32_t left) +{ + uint8_t *subchunkData; + uint32_t subchunkDataLen; + + while ( left > 8 ) { + RIFF_Chunk *child = new RIFF_Chunk; + RIFF_Chunk *next, *prev = NULL; + for ( next = chunk->child; next; next = next->next ) { + prev = next; + } + if ( prev ) { + prev->next = child; + } else { + chunk->child = child; + } + + child->magic = *(uint32_t *)data; + data += 4; + left -= 4; + child->length = LittleLong(*(uint32_t *)data); + data += 4; + left -= 4; + child->data = data; + + if ( child->length > left ) { + child->length = left; + } + + subchunkData = child->data; + subchunkDataLen = child->length; + if ( ChunkHasSubType(child->magic) && subchunkDataLen >= 4 ) { + child->subtype = *(uint32_t *)subchunkData; + subchunkData += 4; + subchunkDataLen -= 4; + } + if ( ChunkHasSubChunks(child->magic) ) { + LoadSubChunks(child, subchunkData, subchunkDataLen); + } + + data += child->length + (child->length & 1); + left -= child->length + (child->length & 1); + } +} + +RIFF_Chunk *LoadRIFF(FILE *src) +{ + RIFF_Chunk *chunk; + uint8_t *subchunkData; + uint32_t subchunkDataLen; + + /* Allocate the chunk structure */ + chunk = new RIFF_Chunk; + + /* Make sure the file is in RIFF format */ + fread(&chunk->magic, 4, 1, src); + fread(&chunk->length, 4, 1, src); + chunk->length = LittleLong(chunk->length); + if ( chunk->magic != RIFF ) { + __Sound_SetError("Not a RIFF file"); + delete chunk; + return NULL; + } + chunk->data = (uint8_t *)malloc(chunk->length); + if ( chunk->data == NULL ) { + __Sound_SetError(ERR_OUT_OF_MEMORY); + delete chunk; + return NULL; + } + if ( fread(chunk->data, chunk->length, 1, src) != 1 ) { + __Sound_SetError(ERR_IO_ERROR); + FreeRIFF(chunk); + return NULL; + } + subchunkData = chunk->data; + subchunkDataLen = chunk->length; + if ( ChunkHasSubType(chunk->magic) && subchunkDataLen >= 4 ) { + chunk->subtype = *(uint32_t *)subchunkData; + subchunkData += 4; + subchunkDataLen -= 4; + } + if ( ChunkHasSubChunks(chunk->magic) ) { + LoadSubChunks(chunk, subchunkData, subchunkDataLen); + } + return chunk; +} + +void FreeRIFF(RIFF_Chunk *chunk) +{ + free(chunk->data); + delete chunk; +} + +void PrintRIFF(RIFF_Chunk *chunk, int level) +{ + static char prefix[128]; + + if ( level == sizeof(prefix)-1 ) { + return; + } + if ( level > 0 ) { + prefix[(level-1)*2] = ' '; + prefix[(level-1)*2+1] = ' '; + } + prefix[level*2] = '\0'; + printf("%sChunk: %c%c%c%c (%d bytes)", prefix, + ((chunk->magic >> 0) & 0xFF), + ((chunk->magic >> 8) & 0xFF), + ((chunk->magic >> 16) & 0xFF), + ((chunk->magic >> 24) & 0xFF), chunk->length); + if ( chunk->subtype ) { + printf(" subtype: %c%c%c%c", + ((chunk->subtype >> 0) & 0xFF), + ((chunk->subtype >> 8) & 0xFF), + ((chunk->subtype >> 16) & 0xFF), + ((chunk->subtype >> 24) & 0xFF)); + } + printf("\n"); + if ( chunk->child ) { + printf("%s{\n", prefix); + PrintRIFF(chunk->child, level + 1); + printf("%s}\n", prefix); + } + if ( chunk->next ) { + PrintRIFF(chunk->next, level); + } + if ( level > 0 ) { + prefix[(level-1)*2] = '\0'; + } +} + +#ifdef TEST_MAIN_RIFF + +main(int argc, char *argv[]) +{ + int i; + for ( i = 1; i < argc; ++i ) { + RIFF_Chunk *chunk; + SDL_RWops *src = SDL_RWFromFile(argv[i], "rb"); + if ( !src ) { + fprintf(stderr, "Couldn't open %s: %s", argv[i], SDL_GetError()); + continue; + } + chunk = LoadRIFF(src); + if ( chunk ) { + PrintRIFF(chunk, 0); + FreeRIFF(chunk); + } else { + fprintf(stderr, "Couldn't load %s: %s\n", argv[i], SDL_GetError()); + } + SDL_RWclose(src); + } +} + +#endif // TEST_MAIN +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*-------------------------------------------------------------------------*/ +/* * * * * * * * * * * * * * * * * load_dls.h * * * * * * * * * * * * * * */ +/*-------------------------------------------------------------------------*/ +/* This code is based on the DLS spec version 1.1, available at: +http://www.midi.org/about-midi/dls/dlsspec.shtml +*/ + +/* Some typedefs so the public dls headers don't need to be modified */ +#define FAR +typedef int16_t SHORT; +typedef uint16_t USHORT; +typedef int32_t LONG; +typedef uint32_t ULONG; +typedef uint32_t DWORD; + +#define mmioFOURCC MAKE_ID +#define DEFINE_GUID(A, B, C, E, F, G, H, I, J, K, L, M) + +#include "dls1.h" +#include "dls2.h" + +struct WaveFMT +{ + uint16_t wFormatTag; + uint16_t wChannels; + uint32_t dwSamplesPerSec; + uint32_t dwAvgBytesPerSec; + uint16_t wBlockAlign; + uint16_t wBitsPerSample; +}; + +struct DLS_Wave +{ + WaveFMT *format; + uint8_t *data; + uint32_t length; + WSMPL *wsmp; + WLOOP *wsmp_loop; +}; + +struct DLS_Region +{ + RGNHEADER *header; + WAVELINK *wlnk; + WSMPL *wsmp; + WLOOP *wsmp_loop; + CONNECTIONLIST *art; + CONNECTION *artList; +}; + +struct DLS_Instrument +{ + const char *name; + INSTHEADER *header; + DLS_Region *regions; + CONNECTIONLIST *art; + CONNECTION *artList; +}; + +struct DLS_Data +{ + RIFF_Chunk *chunk; + + uint32_t cInstruments; + DLS_Instrument *instruments; + + POOLTABLE *ptbl; + POOLCUE *ptblList; + DLS_Wave *waveList; + + const char *name; + const char *artist; + const char *copyright; + const char *comments; +}; + +DLS_Data *LoadDLS(FILE *src); +void FreeDLS(DLS_Data *chunk); +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*-------------------------------------------------------------------------*/ +/* * * * * * * * * * * * * * * * * load_dls.c * * * * * * * * * * * * * * */ +/*-------------------------------------------------------------------------*/ + +#define FOURCC_LIST mmioFOURCC('L','I','S','T') +#define FOURCC_FMT mmioFOURCC('f','m','t',' ') +#define FOURCC_DATA mmioFOURCC('d','a','t','a') +#define FOURCC_INFO mmioFOURCC('I','N','F','O') +#define FOURCC_IARL mmioFOURCC('I','A','R','L') +#define FOURCC_IART mmioFOURCC('I','A','R','T') +#define FOURCC_ICMS mmioFOURCC('I','C','M','S') +#define FOURCC_ICMT mmioFOURCC('I','C','M','T') +#define FOURCC_ICOP mmioFOURCC('I','C','O','P') +#define FOURCC_ICRD mmioFOURCC('I','C','R','D') +#define FOURCC_IENG mmioFOURCC('I','E','N','G') +#define FOURCC_IGNR mmioFOURCC('I','G','N','R') +#define FOURCC_IKEY mmioFOURCC('I','K','E','Y') +#define FOURCC_IMED mmioFOURCC('I','M','E','D') +#define FOURCC_INAM mmioFOURCC('I','N','A','M') +#define FOURCC_IPRD mmioFOURCC('I','P','R','D') +#define FOURCC_ISBJ mmioFOURCC('I','S','B','J') +#define FOURCC_ISFT mmioFOURCC('I','S','F','T') +#define FOURCC_ISRC mmioFOURCC('I','S','R','C') +#define FOURCC_ISRF mmioFOURCC('I','S','R','F') +#define FOURCC_ITCH mmioFOURCC('I','T','C','H') + + +static void FreeRegions(DLS_Instrument *instrument) +{ + if ( instrument->regions ) { + free(instrument->regions); + } +} + +static void AllocRegions(DLS_Instrument *instrument) +{ + int datalen = (instrument->header->cRegions * sizeof(DLS_Region)); + FreeRegions(instrument); + instrument->regions = (DLS_Region *)malloc(datalen); + if ( instrument->regions ) { + memset(instrument->regions, 0, datalen); + } +} + +static void FreeInstruments(DLS_Data *data) +{ + if ( data->instruments ) { + uint32_t i; + for ( i = 0; i < data->cInstruments; ++i ) { + FreeRegions(&data->instruments[i]); + } + free(data->instruments); + } +} + +static void AllocInstruments(DLS_Data *data) +{ + int datalen = (data->cInstruments * sizeof(DLS_Instrument)); + FreeInstruments(data); + data->instruments = (DLS_Instrument *)malloc(datalen); + if ( data->instruments ) { + memset(data->instruments, 0, datalen); + } +} + +static void FreeWaveList(DLS_Data *data) +{ + if ( data->waveList ) { + free(data->waveList); + } +} + +static void AllocWaveList(DLS_Data *data) +{ + int datalen = (data->ptbl->cCues * sizeof(DLS_Wave)); + FreeWaveList(data); + data->waveList = (DLS_Wave *)malloc(datalen); + if ( data->waveList ) { + memset(data->waveList, 0, datalen); + } +} + +static void Parse_colh(DLS_Data *data, RIFF_Chunk *chunk) +{ + data->cInstruments = LittleLong(*(uint32_t *)chunk->data); + AllocInstruments(data); +} + +static void Parse_insh(DLS_Data *data, RIFF_Chunk *chunk, DLS_Instrument *instrument) +{ + INSTHEADER *header = (INSTHEADER *)chunk->data; + header->cRegions = LittleLong(header->cRegions); + header->Locale.ulBank = LittleLong(header->Locale.ulBank); + header->Locale.ulInstrument = LittleLong(header->Locale.ulInstrument); + instrument->header = header; + AllocRegions(instrument); +} + +static void Parse_rgnh(DLS_Data *data, RIFF_Chunk *chunk, DLS_Region *region) +{ + RGNHEADER *header = (RGNHEADER *)chunk->data; + header->RangeKey.usLow = LittleShort(header->RangeKey.usLow); + header->RangeKey.usHigh = LittleShort(header->RangeKey.usHigh); + header->RangeVelocity.usLow = LittleShort(header->RangeVelocity.usLow); + header->RangeVelocity.usHigh = LittleShort(header->RangeVelocity.usHigh); + header->fusOptions = LittleShort(header->fusOptions); + header->usKeyGroup = LittleShort(header->usKeyGroup); + region->header = header; +} + +static void Parse_wlnk(DLS_Data *data, RIFF_Chunk *chunk, DLS_Region *region) +{ + WAVELINK *wlnk = (WAVELINK *)chunk->data; + wlnk->fusOptions = LittleShort(wlnk->fusOptions); + wlnk->usPhaseGroup = LittleShort(wlnk->usPhaseGroup); + wlnk->ulChannel = LittleLong((unsigned int)wlnk->ulChannel); + wlnk->ulTableIndex = LittleLong((unsigned int)wlnk->ulTableIndex); + region->wlnk = wlnk; +} + +static void Parse_wsmp(DLS_Data *data, RIFF_Chunk *chunk, WSMPL **wsmp_ptr, WLOOP **wsmp_loop_ptr) +{ + uint32_t i; + WSMPL *wsmp = (WSMPL *)chunk->data; + WLOOP *loop; + wsmp->cbSize = LittleLong(wsmp->cbSize); + wsmp->usUnityNote = LittleShort(wsmp->usUnityNote); + wsmp->sFineTune = LittleShort(wsmp->sFineTune); + wsmp->lAttenuation = LittleLong(wsmp->lAttenuation); + wsmp->fulOptions = LittleLong(wsmp->fulOptions); + wsmp->cSampleLoops = LittleLong(wsmp->cSampleLoops); + loop = (WLOOP *)((uint8_t *)chunk->data + wsmp->cbSize); + *wsmp_ptr = wsmp; + *wsmp_loop_ptr = loop; + for ( i = 0; i < wsmp->cSampleLoops; ++i ) { + loop->cbSize = LittleLong(loop->cbSize); + loop->ulType = LittleLong(loop->ulType); + loop->ulStart = LittleLong(loop->ulStart); + loop->ulLength = LittleLong(loop->ulLength); + ++loop; + } +} + +static void Parse_art(DLS_Data *data, RIFF_Chunk *chunk, CONNECTIONLIST **art_ptr, CONNECTION **artList_ptr) +{ + uint32_t i; + CONNECTIONLIST *art = (CONNECTIONLIST *)chunk->data; + CONNECTION *artList; + art->cbSize = LittleLong(art->cbSize); + art->cConnections = LittleLong(art->cConnections); + artList = (CONNECTION *)((uint8_t *)chunk->data + art->cbSize); + *art_ptr = art; + *artList_ptr = artList; + for ( i = 0; i < art->cConnections; ++i ) { + artList->usSource = LittleShort(artList->usSource); + artList->usControl = LittleShort(artList->usControl); + artList->usDestination = LittleShort(artList->usDestination); + artList->usTransform = LittleShort(artList->usTransform); + artList->lScale = LittleLong(artList->lScale); + ++artList; + } +} + +static void Parse_lart(DLS_Data *data, RIFF_Chunk *chunk, CONNECTIONLIST **conn_ptr, CONNECTION **connList_ptr) +{ + /* FIXME: This only supports one set of connections */ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32_t magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_ART1: + case FOURCC_ART2: + Parse_art(data, chunk, conn_ptr, connList_ptr); + return; + } + } +} + +static void Parse_rgn(DLS_Data *data, RIFF_Chunk *chunk, DLS_Region *region) +{ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32_t magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_RGNH: + Parse_rgnh(data, chunk, region); + break; + case FOURCC_WLNK: + Parse_wlnk(data, chunk, region); + break; + case FOURCC_WSMP: + Parse_wsmp(data, chunk, ®ion->wsmp, ®ion->wsmp_loop); + break; + case FOURCC_LART: + case FOURCC_LAR2: + Parse_lart(data, chunk, ®ion->art, ®ion->artList); + break; + } + } +} + +static void Parse_lrgn(DLS_Data *data, RIFF_Chunk *chunk, DLS_Instrument *instrument) +{ + uint32_t region = 0; + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32_t magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_RGN: + case FOURCC_RGN2: + if ( region < instrument->header->cRegions ) { + Parse_rgn(data, chunk, &instrument->regions[region++]); + } + break; + } + } +} + +static void Parse_INFO_INS(DLS_Data *data, RIFF_Chunk *chunk, DLS_Instrument *instrument) +{ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32_t magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_INAM: /* Name */ + instrument->name = (const char *)chunk->data; + break; + } + } +} + +static void Parse_ins(DLS_Data *data, RIFF_Chunk *chunk, DLS_Instrument *instrument) +{ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32_t magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_INSH: + Parse_insh(data, chunk, instrument); + break; + case FOURCC_LRGN: + Parse_lrgn(data, chunk, instrument); + break; + case FOURCC_LART: + case FOURCC_LAR2: + Parse_lart(data, chunk, &instrument->art, &instrument->artList); + break; + case FOURCC_INFO: + Parse_INFO_INS(data, chunk, instrument); + break; + } + } +} + +static void Parse_lins(DLS_Data *data, RIFF_Chunk *chunk) +{ + uint32_t instrument = 0; + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32_t magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_INS: + if ( instrument < data->cInstruments ) { + Parse_ins(data, chunk, &data->instruments[instrument++]); + } + break; + } + } +} + +static void Parse_ptbl(DLS_Data *data, RIFF_Chunk *chunk) +{ + uint32_t i; + POOLTABLE *ptbl = (POOLTABLE *)chunk->data; + ptbl->cbSize = LittleLong(ptbl->cbSize); + ptbl->cCues = LittleLong(ptbl->cCues); + data->ptbl = ptbl; + data->ptblList = (POOLCUE *)((uint8_t *)chunk->data + ptbl->cbSize); + for ( i = 0; i < ptbl->cCues; ++i ) { + data->ptblList[i].ulOffset = LittleLong(data->ptblList[i].ulOffset); + } + AllocWaveList(data); +} + +static void Parse_fmt(DLS_Data *data, RIFF_Chunk *chunk, DLS_Wave *wave) +{ + WaveFMT *fmt = (WaveFMT *)chunk->data; + fmt->wFormatTag = LittleShort(fmt->wFormatTag); + fmt->wChannels = LittleShort(fmt->wChannels); + fmt->dwSamplesPerSec = LittleLong(fmt->dwSamplesPerSec); + fmt->dwAvgBytesPerSec = LittleLong(fmt->dwAvgBytesPerSec); + fmt->wBlockAlign = LittleShort(fmt->wBlockAlign); + fmt->wBitsPerSample = LittleShort(fmt->wBitsPerSample); + wave->format = fmt; +} + +static void Parse_data(DLS_Data *data, RIFF_Chunk *chunk, DLS_Wave *wave) +{ + wave->data = chunk->data; + wave->length = chunk->length; +} + +static void Parse_wave(DLS_Data *data, RIFF_Chunk *chunk, DLS_Wave *wave) +{ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32_t magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_FMT: + Parse_fmt(data, chunk, wave); + break; + case FOURCC_DATA: + Parse_data(data, chunk, wave); + break; + case FOURCC_WSMP: + Parse_wsmp(data, chunk, &wave->wsmp, &wave->wsmp_loop); + break; + } + } +} + +static void Parse_wvpl(DLS_Data *data, RIFF_Chunk *chunk) +{ + uint32_t wave = 0; + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32_t magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_wave: + if ( wave < data->ptbl->cCues ) { + Parse_wave(data, chunk, &data->waveList[wave++]); + } + break; + } + } +} + +static void Parse_INFO_DLS(DLS_Data *data, RIFF_Chunk *chunk) +{ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32_t magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_IARL: /* Archival Location */ + break; + case FOURCC_IART: /* Artist */ + data->artist = (const char *)chunk->data; + break; + case FOURCC_ICMS: /* Commisioned */ + break; + case FOURCC_ICMT: /* Comments */ + data->comments = (const char *)chunk->data; + break; + case FOURCC_ICOP: /* Copyright */ + data->copyright = (const char *)chunk->data; + break; + case FOURCC_ICRD: /* Creation Date */ + break; + case FOURCC_IENG: /* Engineer */ + break; + case FOURCC_IGNR: /* Genre */ + break; + case FOURCC_IKEY: /* Keywords */ + break; + case FOURCC_IMED: /* Medium */ + break; + case FOURCC_INAM: /* Name */ + data->name = (const char *)chunk->data; + break; + case FOURCC_IPRD: /* Product */ + break; + case FOURCC_ISBJ: /* Subject */ + break; + case FOURCC_ISFT: /* Software */ + break; + case FOURCC_ISRC: /* Source */ + break; + case FOURCC_ISRF: /* Source Form */ + break; + case FOURCC_ITCH: /* Technician */ + break; + } + } +} + +DLS_Data *LoadDLS(FILE *src) +{ + RIFF_Chunk *chunk; + DLS_Data *data = (DLS_Data *)malloc(sizeof(*data)); + if ( !data ) { + __Sound_SetError(ERR_OUT_OF_MEMORY); + return NULL; + } + memset(data, 0, sizeof(*data)); + + data->chunk = LoadRIFF(src); + if ( !data->chunk ) { + FreeDLS(data); + return NULL; + } + + for ( chunk = data->chunk->child; chunk; chunk = chunk->next ) { + uint32_t magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_COLH: + Parse_colh(data, chunk); + break; + case FOURCC_LINS: + Parse_lins(data, chunk); + break; + case FOURCC_PTBL: + Parse_ptbl(data, chunk); + break; + case FOURCC_WVPL: + Parse_wvpl(data, chunk); + break; + case FOURCC_INFO: + Parse_INFO_DLS(data, chunk); + break; + } + } + return data; +} + +void FreeDLS(DLS_Data *data) +{ + if ( data->chunk ) { + FreeRIFF(data->chunk); + } + FreeInstruments(data); + FreeWaveList(data); + free(data); +} + +static const char *SourceToString(USHORT usSource) +{ + static char unknown[32]; + switch(usSource) { + case CONN_SRC_NONE: + return "NONE"; + case CONN_SRC_LFO: + return "LFO"; + case CONN_SRC_KEYONVELOCITY: + return "KEYONVELOCITY"; + case CONN_SRC_KEYNUMBER: + return "KEYNUMBER"; + case CONN_SRC_EG1: + return "EG1"; + case CONN_SRC_EG2: + return "EG2"; + case CONN_SRC_PITCHWHEEL: + return "PITCHWHEEL"; + case CONN_SRC_CC1: + return "CC1"; + case CONN_SRC_CC7: + return "CC7"; + case CONN_SRC_CC10: + return "CC10"; + case CONN_SRC_CC11: + return "CC11"; + case CONN_SRC_POLYPRESSURE: + return "POLYPRESSURE"; + case CONN_SRC_CHANNELPRESSURE: + return "CHANNELPRESSURE"; + case CONN_SRC_VIBRATO: + return "VIBRATO"; + case CONN_SRC_MONOPRESSURE: + return "MONOPRESSURE"; + case CONN_SRC_CC91: + return "CC91"; + case CONN_SRC_CC93: + return "CC93"; + default: + snprintf(unknown, sizeof(unknown), "UNKNOWN (0x%04x)", usSource); + return unknown; + } +} + +static const char *TransformToString(USHORT usTransform) +{ + static char unknown[32]; + switch (usTransform) { + case CONN_TRN_NONE: + return "NONE"; + case CONN_TRN_CONCAVE: + return "CONCAVE"; + case CONN_TRN_CONVEX: + return "CONVEX"; + case CONN_TRN_SWITCH: + return "SWITCH"; + default: + snprintf(unknown, sizeof(unknown), "UNKNOWN (0x%04x)", usTransform); + return unknown; + } +} + +static const char *DestinationToString(USHORT usDestination) +{ + static char unknown[32]; + switch (usDestination) { + case CONN_DST_NONE: + return "NONE"; + case CONN_DST_ATTENUATION: + return "ATTENUATION"; + case CONN_DST_PITCH: + return "PITCH"; + case CONN_DST_PAN: + return "PAN"; + case CONN_DST_LFO_FREQUENCY: + return "LFO_FREQUENCY"; + case CONN_DST_LFO_STARTDELAY: + return "LFO_STARTDELAY"; + case CONN_DST_EG1_ATTACKTIME: + return "EG1_ATTACKTIME"; + case CONN_DST_EG1_DECAYTIME: + return "EG1_DECAYTIME"; + case CONN_DST_EG1_RELEASETIME: + return "EG1_RELEASETIME"; + case CONN_DST_EG1_SUSTAINLEVEL: + return "EG1_SUSTAINLEVEL"; + case CONN_DST_EG2_ATTACKTIME: + return "EG2_ATTACKTIME"; + case CONN_DST_EG2_DECAYTIME: + return "EG2_DECAYTIME"; + case CONN_DST_EG2_RELEASETIME: + return "EG2_RELEASETIME"; + case CONN_DST_EG2_SUSTAINLEVEL: + return "EG2_SUSTAINLEVEL"; + case CONN_DST_KEYNUMBER: + return "KEYNUMBER"; + case CONN_DST_LEFT: + return "LEFT"; + case CONN_DST_RIGHT: + return "RIGHT"; + case CONN_DST_CENTER: + return "CENTER"; + case CONN_DST_LEFTREAR: + return "LEFTREAR"; + case CONN_DST_RIGHTREAR: + return "RIGHTREAR"; + case CONN_DST_LFE_CHANNEL: + return "LFE_CHANNEL"; + case CONN_DST_CHORUS: + return "CHORUS"; + case CONN_DST_REVERB: + return "REVERB"; + case CONN_DST_VIB_FREQUENCY: + return "VIB_FREQUENCY"; + case CONN_DST_VIB_STARTDELAY: + return "VIB_STARTDELAY"; + case CONN_DST_EG1_DELAYTIME: + return "EG1_DELAYTIME"; + case CONN_DST_EG1_HOLDTIME: + return "EG1_HOLDTIME"; + case CONN_DST_EG1_SHUTDOWNTIME: + return "EG1_SHUTDOWNTIME"; + case CONN_DST_EG2_DELAYTIME: + return "EG2_DELAYTIME"; + case CONN_DST_EG2_HOLDTIME: + return "EG2_HOLDTIME"; + case CONN_DST_FILTER_CUTOFF: + return "FILTER_CUTOFF"; + case CONN_DST_FILTER_Q: + return "FILTER_Q"; + default: + snprintf(unknown, sizeof(unknown), "UNKNOWN (0x%04x)", usDestination); + return unknown; + } +} + +static void PrintArt(const char *type, CONNECTIONLIST *art, CONNECTION *artList) +{ + uint32_t i; + printf("%s Connections:\n", type); + for ( i = 0; i < art->cConnections; ++i ) { + printf(" Source: %s, Control: %s, Destination: %s, Transform: %s, Scale: %d\n", + SourceToString(artList[i].usSource), + SourceToString(artList[i].usControl), + DestinationToString(artList[i].usDestination), + TransformToString(artList[i].usTransform), + artList[i].lScale); + } +} + +static void PrintWave(DLS_Wave *wave, uint32_t index) +{ + WaveFMT *format = wave->format; + if ( format ) { + printf(" Wave %u: Format: %hu, %hu channels, %u Hz, %hu bits (length = %u)\n", index, format->wFormatTag, format->wChannels, format->dwSamplesPerSec, format->wBitsPerSample, wave->length); + } + if ( wave->wsmp ) { + uint32_t i; + printf(" wsmp->usUnityNote = %hu\n", wave->wsmp->usUnityNote); + printf(" wsmp->sFineTune = %hd\n", wave->wsmp->sFineTune); + printf(" wsmp->lAttenuation = %d\n", wave->wsmp->lAttenuation); + printf(" wsmp->fulOptions = 0x%8.8x\n", wave->wsmp->fulOptions); + printf(" wsmp->cSampleLoops = %u\n", wave->wsmp->cSampleLoops); + for ( i = 0; i < wave->wsmp->cSampleLoops; ++i ) { + WLOOP *loop = &wave->wsmp_loop[i]; + printf(" Loop %u:\n", i); + printf(" ulStart = %u\n", loop->ulStart); + printf(" ulLength = %u\n", loop->ulLength); + } + } +} + +static void PrintRegion(DLS_Region *region, uint32_t index) +{ + printf(" Region %u:\n", index); + if ( region->header ) { + printf(" RangeKey = { %hu - %hu }\n", region->header->RangeKey.usLow, region->header->RangeKey.usHigh); + printf(" RangeVelocity = { %hu - %hu }\n", region->header->RangeVelocity.usLow, region->header->RangeVelocity.usHigh); + printf(" fusOptions = 0x%4.4hx\n", region->header->fusOptions); + printf(" usKeyGroup = %hu\n", region->header->usKeyGroup); + } + if ( region->wlnk ) { + printf(" wlnk->fusOptions = 0x%4.4hx\n", region->wlnk->fusOptions); + printf(" wlnk->usPhaseGroup = %hu\n", region->wlnk->usPhaseGroup); + printf(" wlnk->ulChannel = %u\n", region->wlnk->ulChannel); + printf(" wlnk->ulTableIndex = %u\n", region->wlnk->ulTableIndex); + } + if ( region->wsmp ) { + uint32_t i; + printf(" wsmp->usUnityNote = %hu\n", region->wsmp->usUnityNote); + printf(" wsmp->sFineTune = %hd\n", region->wsmp->sFineTune); + printf(" wsmp->lAttenuation = %d\n", region->wsmp->lAttenuation); + printf(" wsmp->fulOptions = 0x%8.8x\n", region->wsmp->fulOptions); + printf(" wsmp->cSampleLoops = %u\n", region->wsmp->cSampleLoops); + for ( i = 0; i < region->wsmp->cSampleLoops; ++i ) { + WLOOP *loop = ®ion->wsmp_loop[i]; + printf(" Loop %u:\n", i); + printf(" ulStart = %u\n", loop->ulStart); + printf(" ulLength = %u\n", loop->ulLength); + } + } + if ( region->art && region->art->cConnections > 0 ) { + PrintArt("Region", region->art, region->artList); + } +} + +static void PrintInstrument(DLS_Instrument *instrument, uint32_t index) +{ + printf("Instrument %u:\n", index); + if ( instrument->name ) { + printf(" Name: %s\n", instrument->name); + } + if ( instrument->header ) { + uint32_t i; + printf(" ulBank = 0x%8.8x\n", instrument->header->Locale.ulBank); + printf(" ulInstrument = %u\n", instrument->header->Locale.ulInstrument); + printf(" Regions: %u\n", instrument->header->cRegions); + for ( i = 0; i < instrument->header->cRegions; ++i ) { + PrintRegion(&instrument->regions[i], i); + } + } + if ( instrument->art && instrument->art->cConnections > 0 ) { + PrintArt("Instrument", instrument->art, instrument->artList); + } +}; + +void PrintDLS(DLS_Data *data) +{ + printf("DLS Data:\n"); + printf("cInstruments = %u\n", data->cInstruments); + if ( data->instruments ) { + uint32_t i; + for ( i = 0; i < data->cInstruments; ++i ) { + PrintInstrument(&data->instruments[i], i); + } + } + if ( data->ptbl && data->ptbl->cCues > 0 ) { + uint32_t i; + printf("Cues: "); + for ( i = 0; i < data->ptbl->cCues; ++i ) { + if ( i > 0 ) { + printf(", "); + } + printf("%u", data->ptblList[i].ulOffset); + } + printf("\n"); + } + if ( data->waveList && data->ptbl ) { + uint32_t i; + printf("Waves:\n"); + for ( i = 0; i < data->ptbl->cCues; ++i ) { + PrintWave(&data->waveList[i], i); + } + } + if ( data->name ) { + printf("Name: %s\n", data->name); + } + if ( data->artist ) { + printf("Artist: %s\n", data->artist); + } + if ( data->copyright ) { + printf("Copyright: %s\n", data->copyright); + } + if ( data->comments ) { + printf("Comments: %s\n", data->comments); + } +} + +#ifdef TEST_MAIN_DLS +} + +int main(int argc, char *argv[]) +{ + int i; + for ( i = 1; i < argc; ++i ) { + Timidity::DLS_Data *data; + FILE *src = fopen(argv[i], "rb"); + if ( !src ) { + fprintf(stderr, "Couldn't open %s: %s", argv[i], strerror(errno)); + continue; + } + data = Timidity::LoadDLS(src); + if ( data ) { + Timidity::PrintRIFF(data->chunk, 0); + Timidity::PrintDLS(data); + Timidity::FreeDLS(data); + } else { + fprintf(stderr, "Couldn't load %s: %s\n", argv[i], strerror(errno)); + } + fclose(src); + } + return 0; +} + +namespace Timidity +{ +#endif // TEST_MAIN +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*-------------------------------------------------------------------------*/ +/* * * * * * * * * * * * * * * * * instrum_dls.c * * * * * * * * * * * * * */ +/*-------------------------------------------------------------------------*/ + +#ifndef TEST_MAIN_DLS + +DLS_Data *Timidity_LoadDLS(FILE *src) +{ + return LoadDLS(src); +} + +void Timidity_FreeDLS(DLS_Data *patches) +{ + FreeDLS(patches); +} + +/* convert timecents to sec */ +static double to_msec(int timecent) +{ + if (timecent == INT_MIN || timecent == 0) + return 0.0; + return 1000.0 * pow(2.0, (double)(timecent / 65536) / 1200.0); +} + +/* convert decipercent to {0..1} */ +static double to_normalized_percent(int decipercent) +{ + return ((double)(decipercent / 65536)) / 1000.0; +} + +/* convert from 8bit value to fractional offset (15.15) */ +static int32_t to_offset(int offset) +{ + return (int32_t)offset << (7+15); +} + +/* calculate ramp rate in fractional unit; +* diff = 8bit, time = msec +*/ +static int32_t calc_rate(Renderer *song, int diff, int sample_rate, double msec) +{ + double rate; + + if(msec < 6) + msec = 6; + if(diff == 0) + diff = 255; + diff <<= (7+15); + rate = ((double)diff / song->rate) * song->control_ratio * 1000.0 / msec; + return (int32_t)rate; +} + +static int load_connection(ULONG cConnections, CONNECTION *artList, USHORT destination) +{ + ULONG i; + int value = 0; + for (i = 0; i < cConnections; ++i) { + CONNECTION *conn = &artList[i]; + if(conn->usDestination == destination) { + // The formula for the destination is: + // usDestination = usDestination + usTransform(usSource * (usControl * lScale)) + // Since we are only handling source/control of NONE and identity + // transform, this simplifies to: usDestination = usDestination + lScale + if (conn->usSource == CONN_SRC_NONE && + conn->usControl == CONN_SRC_NONE && + conn->usTransform == CONN_TRN_NONE) + value += conn->lScale; + } + } + return value; +} + +static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, uint32_t index) +{ + DLS_Region *rgn = &ins->regions[index]; + DLS_Wave *wave = &song->patches->waveList[rgn->wlnk->ulTableIndex]; + + sample->type = INST_DLS; + sample->self_nonexclusive = !!(rgn->header->fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE); + sample->key_group = (int8_t)rgn->header->usKeyGroup; + sample->low_freq = note_to_freq(rgn->header->RangeKey.usLow); + sample->high_freq = note_to_freq(rgn->header->RangeKey.usHigh); + sample->root_freq = note_to_freq(rgn->wsmp->usUnityNote + rgn->wsmp->sFineTune * .01f); + sample->low_vel = (uint8_t)rgn->header->RangeVelocity.usLow; + sample->high_vel = (uint8_t)rgn->header->RangeVelocity.usHigh; + + sample->modes = wave->format->wBitsPerSample == 8 ? PATCH_UNSIGNED : PATCH_16; + sample->sample_rate = wave->format->dwSamplesPerSec; + sample->data = NULL; + sample->data_length = wave->length; + convert_sample_data(sample, wave->data); + if (rgn->wsmp->cSampleLoops) + { + sample->modes |= (PATCH_LOOPEN | PATCH_SUSTAIN/* | PATCH_NO_SRELEASE*/); + sample->loop_start = rgn->wsmp_loop->ulStart / 2; + sample->loop_end = sample->loop_start + (rgn->wsmp_loop->ulLength / 2); + } + sample->scale_factor = 1024; + sample->scale_note = rgn->wsmp->usUnityNote; + + if (sample->modes & PATCH_SUSTAIN) + { + int value; + int attack, hold, decay, release; int sustain; + CONNECTIONLIST *art = NULL; + CONNECTION *artList = NULL; + + if (ins->art && ins->art->cConnections > 0 && ins->artList) { + art = ins->art; + artList = ins->artList; + } else { + art = rgn->art; + artList = rgn->artList; + } + + attack = load_connection(art->cConnections, artList, CONN_DST_EG1_ATTACKTIME); + hold = load_connection(art->cConnections, artList, CONN_DST_EG1_HOLDTIME); + decay = load_connection(art->cConnections, artList, CONN_DST_EG1_DECAYTIME); + release = load_connection(art->cConnections, artList, CONN_DST_EG1_RELEASETIME); + sustain = load_connection(art->cConnections, artList, CONN_DST_EG1_SUSTAINLEVEL); + value = load_connection(art->cConnections, artList, CONN_DST_PAN); + sample->panning = (int)((0.5 + to_normalized_percent(value)) * 16383.f); + + /* + printf("%d, Rate=%d LV=%d HV=%d Low=%d Hi=%d Root=%d Pan=%d Attack=%f Hold=%f Sustain=%d Decay=%f Release=%f\n", index, sample->sample_rate, rgn->header->RangeVelocity.usLow, rgn->header->RangeVelocity.usHigh, sample->low_freq, sample->high_freq, sample->root_freq, sample->panning, attack, hold, sustain, decay, release); + */ + + sample->envelope.sf2.delay_vol = -32768; + sample->envelope.sf2.attack_vol = (short)(attack >> 16); + sample->envelope.sf2.hold_vol = (short)(hold >> 16); + sample->envelope.sf2.decay_vol = (short)(decay >> 16); + sample->envelope.sf2.release_vol = (short)(release >> 16); + sample->envelope.sf2.sustain_vol = (short)(sustain >> 16); + } + + sample->data_length <<= FRACTION_BITS; + sample->loop_start <<= FRACTION_BITS; + sample->loop_end <<= FRACTION_BITS; +} + +Instrument *load_instrument_dls(Renderer *song, int drum, int bank, int instrument) +{ + Instrument *inst; + uint32_t i; + DLS_Instrument *dls_ins = NULL; + + if (song->patches == NULL) + { + return NULL; + } + drum = drum ? 0x80000000 : 0; + for (i = 0; i < song->patches->cInstruments; ++i) + { + dls_ins = &song->patches->instruments[i]; + if ((dls_ins->header->Locale.ulBank & 0x80000000) == (ULONG)drum && + ((dls_ins->header->Locale.ulBank >> 8) & 0xFF) == (ULONG)bank && + dls_ins->header->Locale.ulInstrument == (ULONG)instrument) + break; + } + if (i == song->patches->cInstruments && bank == 0) + { + for (i = 0; i < song->patches->cInstruments; ++i) + { + dls_ins = &song->patches->instruments[i]; + if ((dls_ins->header->Locale.ulBank & 0x80000000) == (ULONG)drum && + dls_ins->header->Locale.ulInstrument == (ULONG)instrument) + break; + } + } + if (i == song->patches->cInstruments) + { +// SNDDBG(("Couldn't find %s instrument %d in bank %d\n", drum ? "drum" : "melodic", instrument, bank)); + return NULL; + } + + inst = (Instrument *)safe_malloc(sizeof(Instrument)); + inst->samples = dls_ins->header->cRegions; + inst->sample = (Sample *)safe_malloc(inst->samples * sizeof(Sample)); + memset(inst->sample, 0, inst->samples * sizeof(Sample)); + /* + printf("Found %s instrument %d in bank %d named %s with %d regions\n", drum ? "drum" : "melodic", instrument, bank, dls_ins->name, inst->samples); + */ + for (i = 0; i < dls_ins->header->cRegions; ++i) + { + load_region_dls(song, &inst->sample[i], dls_ins, i); + } + return inst; +} +#endif /* !TEST_MAIN_DLS */ + +} diff --git a/thirdparty/timidity/instrum_font.cpp b/thirdparty/timidity/instrum_font.cpp new file mode 100644 index 000000000..2bbe8d8b3 --- /dev/null +++ b/thirdparty/timidity/instrum_font.cpp @@ -0,0 +1,128 @@ +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "sf2.h" + +#ifndef _WIN32 +#include +#define stricmp strcasecmp +#endif + +namespace Timidity +{ + + +FontFile *Instruments::ReadDLS(const char *filename, timidity_file *f) +{ + return NULL; +} + +void Instruments::font_freeall() +{ + FontFile *font, *next; + + for (font = Fonts; font != NULL; font = next) + { + next = font->Next; + delete font; + } + Fonts = NULL; +} + +FontFile * Instruments::font_find(const char *filename) +{ + for (FontFile *font = Fonts; font != NULL; font = font->Next) + { + if (stricmp(filename, font->Filename.c_str()) == 0) + { + return font; + } + } + return NULL; +} + +void Instruments::font_add(const char *filename, int load_order) +{ + FontFile *font; + + font = font_find(filename); + if (font != NULL) + { + font->SetAllOrders(load_order); + } + else + { + auto fp = sfreader->open_file(filename); + + if (fp) + { + if ((font = ReadSF2(filename, fp)) || (font = ReadDLS(filename, fp))) + { + font->Next = Fonts; + Fonts = font; + + font->SetAllOrders(load_order); + } + fp->close(); + } + } +} + +void Instruments::font_remove(const char *filename) +{ + FontFile *font; + + font = font_find(filename); + if (font != NULL) + { + // Don't actually remove the font from the list, because instruments + // from it might be loaded using the %font extension. + font->SetAllOrders(255); + } +} + +void Instruments::font_order(int order, int bank, int preset, int keynote) +{ + for (FontFile *font = Fonts; font != NULL; font = font->Next) + { + font->SetOrder(order, bank, preset, keynote); + } +} + +Instrument *Renderer::load_instrument_font(const char *font, int drum, int bank, int instr) +{ + FontFile *fontfile = instruments->font_find(font); + if (fontfile != NULL) + { + return fontfile->LoadInstrument(this, drum, bank, instr); + } + return NULL; +} + +Instrument *Renderer::load_instrument_font_order(int order, int drum, int bank, int instr) +{ + for (FontFile *font = instruments->Fonts; font != NULL; font = font->Next) + { + Instrument *ip = font->LoadInstrument(this, drum, bank, instr); + if (ip != NULL) + { + return ip; + } + } + return NULL; +} + +FontFile::FontFile(const char *filename) +: Filename(filename) +{ +} + +FontFile::~FontFile() +{ +} + +} diff --git a/thirdparty/timidity/instrum_sf2.cpp b/thirdparty/timidity/instrum_sf2.cpp new file mode 100644 index 000000000..555106196 --- /dev/null +++ b/thirdparty/timidity/instrum_sf2.cpp @@ -0,0 +1,1539 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "t_swap.h" +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "sf2.h" + +namespace Timidity +{ + +#define cindex(identifier) (uint8_t)(((size_t)&((SFGenComposite *)1)->identifier - 1) / 2) + +class CIOErr {}; +class CBadForm {}; +class CBadVer {}; + +struct ListHandler +{ + uint32_t ID; + void (*Parser)(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen); +}; + +enum +{ + GENF_InstrOnly = 1, // Only valid at intstrument level + GENF_PresetOnly = 2, // Only valid at preset level + GENF_Range = 4, // Value is a range, + GENF_Index = 8, // Value is an index (aka unsigned) + GENF_32768_Ok = 16, // -32768 is a valid value +}; +struct GenDef +{ + short Min; + short Max; + uint8_t StructIndex; + uint8_t Flags; +}; + +static const GenDef GenDefs[] = +{ + /* 0 */ { 0, 32767, cindex(startAddrsOffset), GENF_InstrOnly }, + /* 1 */ { -32768, 0, cindex(endAddrsOffset), GENF_InstrOnly }, + /* 2 */ { -32768, 32767, cindex(startLoopAddrsOffset), GENF_InstrOnly }, + /* 3 */ { -32768, 32767, cindex(endLoopAddrsOffset), GENF_InstrOnly }, + /* 4 */ { 0, 32767, cindex(startAddrsCoarseOffset), GENF_InstrOnly }, + /* 5 */ { -12000, 12000, cindex(modLfoToPitch), 0 }, + /* 6 */ { -12000, 12000, cindex(vibLfoToPitch), 0 }, + /* 7 */ { -12000, 12000, cindex(modEnvToPitch), 0 }, + /* 8 */ { 1500, 13500, cindex(initialFilterFc), 0 }, + /* 9 */ { 0, 960, cindex(initialFilterQ), 0 }, + /* 10 */ { -12000, 12000, cindex(modLfoToFilterFc), 0 }, + /* 11 */ { -12000, 12000, cindex(modEnvToFilterFc), 0 }, + /* 12 */ { -32768, 0, cindex(endAddrsCoarseOffset), 0 }, + /* 13 */ { -960, 960, cindex(modLfoToVolume), 0 }, + /* 14 */ { 0, 0, 255 /* unused1 */, 0 }, + /* 15 */ { 0, 1000, cindex(chorusEffectsSend), 0 }, + /* 16 */ { 0, 1000, cindex(reverbEffectsSend), 0 }, + /* 17 */ { -500, 500, cindex(pan), 0 }, + /* 18 */ { 0, 0, 255 /* unused2 */, 0 }, + /* 19 */ { 0, 0, 255 /* unused3 */, 0 }, + /* 20 */ { 0, 0, 255 /* unused4 */, 0 }, + /* 21 */ { -12000, 5000, cindex(delayModLFO), GENF_32768_Ok }, + /* 22 */ { -16000, 4500, cindex(freqModLFO), 0 }, + /* 23 */ { -12000, 5000, cindex(delayVibLFO), GENF_32768_Ok }, + /* 24 */ { -16000, 4500, cindex(freqVibLFO), 0 }, + /* 25 */ { -12000, 5000, cindex(delayModEnv), GENF_32768_Ok }, + /* 26 */ { -12000, 8000, cindex(attackModEnv), GENF_32768_Ok }, + /* 27 */ { -12000, 5000, cindex(holdModEnv), GENF_32768_Ok }, + /* 28 */ { -12000, 8000, cindex(decayModEnv), 0 }, + /* 29 */ { 0, 1000, cindex(sustainModEnv), 0 }, + /* 30 */ { -12000, 8000, cindex(releaseModEnv), 0 }, + /* 31 */ { -1200, 1200, cindex(keynumToModEnvHold), 0 }, + /* 32 */ { -1200, 1200, cindex(keynumToModEnvDecay), 0 }, + /* 33 */ { -12000, 5000, cindex(delayVolEnv), GENF_32768_Ok }, + /* 34 */ { -12000, 8000, cindex(attackVolEnv), GENF_32768_Ok }, + /* 35 */ { -12000, 5000, cindex(holdVolEnv), GENF_32768_Ok }, + /* 36 */ { -12000, 5000, cindex(decayVolEnv), 0 }, + /* 37 */ { 0, 1440, cindex(sustainVolEnv), 0 }, + /* 38 */ { -12000, 8000, cindex(releaseVolEnv), 0 }, + /* 39 */ { -1200, 1200, cindex(keynumToVolEnvHold), 0 }, + /* 40 */ { -1200, 1200, cindex(keynumToVolEnvDecay), 0 }, + /* 41 */ { -32768, 32767, 255 /* instrument */, GENF_Index | GENF_PresetOnly }, + /* 42 */ { 0, 0, 255 /* reserved1 */, 0 }, + /* 43 */ { 0, 127, 255 /* keyRange */, GENF_Range }, + /* 44 */ { 0, 127, 255 /* velRange */, GENF_Range }, + /* 45 */ { -32768, 32767, cindex(startLoopAddrsCoarseOffset), GENF_InstrOnly }, + /* 46 */ { 0, 127, cindex(keynum), GENF_InstrOnly }, + /* 47 */ { 1, 127, cindex(velocity), GENF_InstrOnly }, + /* 48 */ { 0, 1440, cindex(initialAttenuation), 0 }, + /* 49 */ { 0, 0, 255 /* reserved2 */, 0 }, + /* 50 */ { -32768, 32767, cindex(endLoopAddrsCoarseOffset), GENF_InstrOnly }, + /* 51 */ { -120, 120, cindex(coarseTune), 0 }, + /* 52 */ { -99, 99, cindex(fineTune), 0 }, + /* 53 */ { -32768, 32767, 255 /* sampleID */, GENF_Index | GENF_InstrOnly }, + /* 54 */ { -32768, 32767, cindex(sampleModes), GENF_InstrOnly }, + /* 55 */ { 0, 0, 255 /* reserved3 */, 0 }, + /* 56 */ { 0, 1200, cindex(scaleTuning), 0 }, + /* 57 */ { 1, 127, cindex(exclusiveClass), GENF_InstrOnly }, + /* 58 */ { 0, 127, cindex(overridingRootKey), GENF_InstrOnly }, +}; + +static const SFGenComposite DefaultGenerators = +{ + { { 0, 127 } }, // keyRange + { 0, 127 }, // velRange + { 0 }, // instrument/sampleID + 0, // modLfoToPitch + 0, // vibLfoToPitch + 0, // modEnvToPitch + 13500, // initialFilterFc + 0, // initialFilterQ + 0, // modLfoToFilterFc + 0, // modEnvToFilterFc + 0, // modLfoToVolume + 0, // chorusEffectsSend + 0, // reverbEffectsSend + 0, // pan + -12000, // delayModLFO + 0, // freqModLFO + -12000, // delayVibLFO + 0, // freqVibLFO + -12000, // delayModEnv + -12000, // attackModEnv + -12000, // holdModEnv + -12000, // decayModEnv + 0, // sustainModEnv + -12000, // releaseModEnv + 0, // keynumToModEnvHold + 0, // keynumToModEnvDecay + -12000, // delayVolEnv + -12000, // attackVolEnv + -12000, // holdVolEnv + -12000, // decayVolEnv + 0, // sustainVolEnv + -12000, // releaseVolEnv + 0, // keynumToVolEnvHold + 0, // keynumToVolEnvDecay + 0, // initialAttenuation + 0, // coarseTune + 0, // fineTune + 100, // scaleTuning + + 0, 0, // startAddrs(Coarse)Offset + 0, 0, // endAddrs(Coarse)Offset + 0, 0, // startLoop(Coarse)Offset + 0, 0, // endLoop(Coarse)Offset + -1, // keynum + -1, // velocity + 0, // sampleModes + 0, // exclusiveClass + -1 // overridingRootKey +}; + +static void ParseIfil(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen); +static void ParseSmpl(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen); +static void ParseSm24(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen); +static void ParsePhdr(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen); +static void ParseBag(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen); +static void ParseMod(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen); +static void ParseGen(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen); +static void ParseInst(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen); +static void ParseShdr(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen); + +ListHandler INFOHandlers[] = +{ + { ID_ifil, ParseIfil }, + { 0, 0 } +}; + +ListHandler SdtaHandlers[] = +{ + { ID_smpl, ParseSmpl }, + { ID_sm24, ParseSm24 }, + { 0, 0 } +}; + +ListHandler PdtaHandlers[] = +{ + { ID_phdr, ParsePhdr }, + { ID_pbag, ParseBag }, + { ID_pmod, ParseMod }, + { ID_pgen, ParseGen }, + { ID_inst, ParseInst }, + { ID_ibag, ParseBag }, + { ID_imod, ParseMod }, + { ID_igen, ParseGen }, + { ID_shdr, ParseShdr }, + { 0, 0 } +}; + +static double timecent_to_sec(int16_t timecent) +{ + if (timecent == -32768) + return 0; + return pow(2.0, timecent / 1200.0); +} + +static int32_t to_offset(int offset) +{ + return (int32_t)offset << (7+15); +} + +static int32_t calc_rate(Renderer *song, int diff, double sec) +{ + double rate; + + if(sec < 0.006) + sec = 0.006; + if(diff == 0) + diff = 255; + diff <<= (7+15); + rate = ((double)diff / song->rate) * song->control_ratio / sec; + return (int32_t)rate; +} + + +static inline uint32_t read_id(timidity_file *f) +{ + uint32_t id; + if (f->read(&id, 4) != 4) + { + throw CIOErr(); + } + return id; +} + +static inline int read_byte(timidity_file *f) +{ + uint8_t x; + if (f->read(&x, 1) != 1) + { + throw CIOErr(); + } + return x; +} + +static inline int read_char(timidity_file *f) +{ + int8_t x; + if (f->read(&x, 1) != 1) + { + throw CIOErr(); + } + return x; +} + +static inline int read_uword(timidity_file *f) +{ + uint16_t x; + if (f->read(&x, 2) != 2) + { + throw CIOErr(); + } + return LittleShort(x); +} + +static inline int read_sword(timidity_file *f) +{ + int16_t x; + if (f->read(&x, 2) != 2) + { + throw CIOErr(); + } + return LittleShort(x); +} + +static inline uint32_t read_dword(timidity_file *f) +{ + uint32_t x; + if (f->read(&x, 4) != 4) + { + throw CIOErr(); + } + return LittleLong(x); +} + +static inline void read_name(timidity_file *f, char name[21]) +{ + if (f->read(name, 20) != 20) + { + throw CIOErr(); + } + name[20] = 0; +} + +static inline void skip_chunk(timidity_file *f, uint32_t len) +{ + // RIFF, like IFF, adds an extra pad byte to the end of + // odd-sized chunks so that new chunks are always on even + // byte boundaries. + if (f->seek(len + (len & 1), SEEK_CUR) != 0) + { + throw CIOErr(); + } +} + +static void check_list(timidity_file *f, uint32_t id, uint32_t filelen, uint32_t &chunklen) +{ + if (read_id(f) != ID_LIST) + { + throw CBadForm(); + } + chunklen = read_dword(f); + if (chunklen + 8 > filelen) + { + throw CBadForm(); + } + if (read_id(f) != id) + { + throw CBadForm(); + } +} + +static void ParseIfil(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen) +{ + uint16_t major, minor; + + if (chunklen != 4) + { + throw CBadForm(); + } + + major = read_uword(f); + minor = read_uword(f); + + if (major != 2) + { + throw CBadVer(); + } + sf2->MinorVersion = minor; +} + +static void ParseLIST(SFFile *sf2, timidity_file *f, uint32_t chunklen, ListHandler *handlers) +{ + ListHandler *handler; + uint32_t id; + uint32_t len; + + chunklen -= 4; + while (chunklen > 0) + { + id = read_id(f); + len = read_dword(f); + + if (len + 8 > chunklen) + { + throw CBadForm(); + } + chunklen -= len + (len & 1) + 8; + + for (handler = handlers; handler->ID != 0; ++handler) + { + if (handler->ID == id && handler->Parser != NULL) + { + handler->Parser(sf2, f, id, len); + break; + } + } + if (handler->ID == 0) + { + // Skip unknown chunks + skip_chunk(f, len); + } + } +} + +static void ParseINFO(SFFile *sf2, timidity_file *f, uint32_t chunklen) +{ + sf2->MinorVersion = -1; + + ParseLIST(sf2, f, chunklen, INFOHandlers); + + if (sf2->MinorVersion < 0) + { // The ifil chunk must be present. + throw CBadForm(); + } +} + +static void ParseSdta(SFFile *sf2, timidity_file *f, uint32_t chunklen) +{ + ParseLIST(sf2, f, chunklen, SdtaHandlers); + if (sf2->SampleDataOffset == 0) + { + throw CBadForm(); + } + // Section 6.2, page 20: It is not clear if the extra pad byte for an + // odd chunk is supposed to be included in the chunk length field. + if (sf2->SizeSampleDataLSB != sf2->SizeSampleData && + sf2->SizeSampleDataLSB != sf2->SizeSampleData + (sf2->SizeSampleData & 1)) + { + sf2->SampleDataLSBOffset = 0; + sf2->SizeSampleDataLSB = 0; + } +} + +static void ParseSmpl(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen) +{ + // Only use the first smpl chunk. (Or should we reject files with more than one?) + if (sf2->SampleDataOffset == 0) + { + if (chunklen & 1) + { // Chunk must be an even number of bytes. + throw CBadForm(); + } + sf2->SampleDataOffset = (uint32_t)f->tell(); + sf2->SizeSampleData = chunklen >> 1; + } + skip_chunk(f, chunklen); +} + +static void ParseSm24(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen) +{ + // The sm24 chunk is ignored if the file version is < 2.04 + if (sf2->MinorVersion >= 4) + { + // Only use the first sm24 chunk. (Or should we reject files with more than one?) + if (sf2->SampleDataLSBOffset == 0) + { + sf2->SampleDataLSBOffset = (uint32_t)f->tell(); + sf2->SizeSampleDataLSB = chunklen; + } + } + skip_chunk(f, chunklen); +} + +static void ParsePdta(SFFile *sf2, timidity_file *f, uint32_t chunklen) +{ + ParseLIST(sf2, f, chunklen, PdtaHandlers); +} + +static void ParsePhdr(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen) +{ + SFPreset *preset; + + // Section 7.2, page 22: + // If the PHDR sub-chunk is missing, or contains fewer than two records, + // or its size is not a multiple of 38 bytes, the file should be rejected + // as structurally unsound. + if (chunklen < 38*2 || chunklen % 38 != 0) + { + throw CBadForm(); + } + + sf2->NumPresets = chunklen / 38; + sf2->Presets = new SFPreset[sf2->NumPresets]; + preset = sf2->Presets; + + for (int i = sf2->NumPresets; i != 0; --i, ++preset) + { + read_name(f, preset->Name); + preset->Program = read_uword(f); + preset->Bank = read_uword(f); + preset->BagIndex = read_uword(f); + skip_chunk(f, 4*3); // Skip library, genre, and morphology + + // Section 7.2, page 22: + // The preset bag indices will be monotonically increasing with + // increasing preset headers. + if (preset != sf2->Presets) + { + if (preset->BagIndex < (preset - 1)->BagIndex) + { + throw CBadForm(); + } + } + } +} + +static void ParseBag(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen) +{ + SFBag *bags, *bag; + uint16_t prev_mod = 0; + int numbags; + int i; + + // Section 7.3, page 22: + // It is always a multiple of four bytes in length, and contains one + // record for each preset zone plus one record for a terminal zone. ... + // If the PBAG sub-chunk is missing, or its size is not a multiple of + // four bytes, the file should be rejected as structurally unsound. + // Section 7.7: IBAG is the same, but substitute "instrument" for "preset". + if (chunklen & 3) + { + throw CBadForm(); + } + + numbags = chunklen >> 2; + + if (chunkid == ID_pbag) + { + if (numbags != sf2->Presets[sf2->NumPresets - 1].BagIndex + 1) + { + throw CBadForm(); + } + sf2->PresetBags = bags = new SFBag[numbags]; + sf2->NumPresetBags = numbags; + } + else + { + assert(chunkid == ID_ibag); + if (numbags != sf2->Instruments[sf2->NumInstruments - 1].BagIndex + 1) + { + throw CBadForm(); + } + sf2->InstrBags = bags = new SFBag[numbags]; + sf2->NumInstrBags = numbags; + } + + for (bag = bags, i = numbags; i != 0; --i, ++bag) + { + bag->GenIndex = read_uword(f); + uint16_t mod = read_uword(f); + // Section 7.3, page 22: + // If the generator or modulator indices are non-monotonic or do not + // match the size of the respective PGEN or PMOD sub-chunks, the file + // is structurally defective and should be rejected at load time. + if (bag != bags) + { + if (bag->GenIndex < (bag - 1)->GenIndex || mod < prev_mod) + { + throw CBadForm(); + } + } + prev_mod = mod; + bag->KeyRange.Lo = bag->VelRange.Lo = 0; + bag->KeyRange.Hi = bag->VelRange.Hi = 127; + bag->Target = -1; + } +} + +static void ParseMod(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen) +{ + // Section 7.4, page 23: + // It [the PMOD sub-chunk] is always a multiple of ten bytes in length, + // and contains zero or more modulators plus a terminal record + if (chunklen % 10 != 0) + { + throw CBadForm(); + } + // We've checked the length, now ignore the chunk. + skip_chunk(f, chunklen); +} + +static void ParseGen(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen) +{ + SFGenList *gens, *gen; + int numgens; + int i; + + // Section 7.5, page 24: + // If the PGEN sub-chunk is missing, or its size is not a multiple of + // four bytes, the file should be rejected as structurally unsound. + if (chunklen & 3) + { + throw CBadForm(); + } + numgens = chunklen >> 2; + + if (chunkid == ID_pgen) + { + // Section 7.3, page 22: + // the size of the PGEN sub-chunk in bytes will be equal to four + // times the terminal preset’s wGenNdx plus four. + if (numgens != sf2->PresetBags[sf2->NumPresetBags - 1].GenIndex + 1) + { + throw CBadForm(); + } + sf2->PresetGenerators = gens = new SFGenList[numgens]; + sf2->NumPresetGenerators = numgens; + } + else + { + assert(chunkid == ID_igen); + if (numgens != sf2->InstrBags[sf2->NumInstrBags - 1].GenIndex + 1) + { + throw CBadForm(); + } + sf2->InstrGenerators = gens = new SFGenList[numgens]; + sf2->NumInstrGenerators = numgens; + } + + for (i = numgens, gen = gens; i != 0; --i, ++gen) + { + gen->Oper = read_uword(f); + gen->uAmount = read_uword(f); +#ifdef __BIG_ENDIAN__ + if (gen->Oper == GEN_keyRange || gen->Oper == GEN_velRange) + { + // Reswap range generators + gen->uAmount = LittleShort(gen->uAmount); + } +#endif + } +} + +static void ParseInst(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen) +{ + int i; + SFInst *inst; + + // Section 7.6, page 25: + // If the INST sub-chunk is missing, contains fewer than two records, or its + // size is not a multiple of 22 bytes, the file should be rejected as + // structurally unsound. + if (chunklen < 22*2 || chunklen % 22 != 0) + { + throw CBadForm(); + } + + sf2->NumInstruments = chunklen / 22; + sf2->Instruments = inst = new SFInst[sf2->NumInstruments]; + for (i = sf2->NumInstruments; i != 0; --i, ++inst) + { + read_name(f, inst->Name); + inst->BagIndex = read_uword(f); + + // Section 7.6, page 25: + // If the instrument bag indices are non-monotonic or if the terminal + // instrument’s wInstBagNdx does not match the IBAG sub-chunk size, the + // file is structurally defective and should be rejected at load time. + if (inst != sf2->Instruments) + { + if (inst->BagIndex < (inst - 1)->BagIndex) + { + throw CBadForm(); + } + } + } +} + +static void ParseShdr(SFFile *sf2, timidity_file *f, uint32_t chunkid, uint32_t chunklen) +{ + int i; + SFSample *sample; + + // Section 7.10, page 29: + // If the SHDR sub-chunk is missing, or its is size is not a multiple of 46 + // bytes the file should be rejected as structurally unsound. + if (chunklen % 46 != 0) + { + throw CBadForm(); + } + + sf2->NumSamples = chunklen / 46; + sf2->Samples = sample = new SFSample[sf2->NumSamples]; + for (i = sf2->NumSamples; i != 0; --i, ++sample) + { + sample->InMemoryData = NULL; + read_name(f, sample->Name); + sample->Start = read_dword(f); + sample->End = read_dword(f); + sample->StartLoop = read_dword(f); + sample->EndLoop = read_dword(f); + sample->SampleRate = read_dword(f); + sample->OriginalPitch = read_byte(f); + sample->PitchCorrection = read_char(f); + sample->SampleLink = read_uword(f); + sample->SampleType = read_uword(f); + + if (sample->SampleRate == 0) + { + // Section 7.10, page 29: + // A value of zero is illegal. If an illegal or impractical value is + // encountered, the nearest practical value should be used. + sample->SampleRate = 400; + } + if (sample->OriginalPitch > 127) + { + // Section 7.10, page 29: + // For unpitched sounds, a conventional value of 255 should be used + // Values between 128 and 254 are illegal. Whenever an illegal value + // or a value of 255 is encountered, the value 60 should be used. + sample->OriginalPitch = 60; + } + + // Clamp sample positions to the available sample data. + sample->Start = std::min(sample->Start, sf2->SizeSampleData - 1); + sample->End = std::min(sample->End, sf2->SizeSampleData - 1); + sample->StartLoop = std::min(sample->StartLoop, sf2->SizeSampleData - 1); + sample->EndLoop = std::min(sample->EndLoop, sf2->SizeSampleData - 1); + + if (sample->Start >= sample->End) + { + sample->SampleType |= SFST_Bad; + } + } +} + + +SFFile *ReadSF2(const char *filename, timidity_file *f) +{ + SFFile *sf2 = NULL; + uint32_t filelen; + uint32_t chunklen; + + try + { + // Read RIFF sfbk header + if (read_id(f) != ID_RIFF) + { + return NULL; + } + filelen = read_dword(f); + if (read_id(f) != ID_sfbk) + { + return NULL; + } + filelen -= 4; + + // First chunk must be an INFO LIST + check_list(f, ID_INFO, filelen, chunklen); + + sf2 = new SFFile(filename); + + ParseINFO(sf2, f, chunklen); + filelen -= chunklen + 8; + + // Second chunk must be a sdta LIST + check_list(f, ID_sdta, filelen, chunklen); + ParseSdta(sf2, f, chunklen); + + // Third chunk must be a pdta LIST + check_list(f, ID_pdta, filelen, chunklen); + ParsePdta(sf2, f, chunklen); + + // There should be no more chunks. If there are, we'll just ignore them rather than reject the file. + if (!sf2->FinalStructureTest()) + { + throw CBadForm(); + } + + sf2->CheckBags(); + sf2->TranslatePercussions(); + + return sf2; + } + catch (CIOErr) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "Error reading %s: %s\n", filename, strerror(errno)); + } + catch (CBadForm) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s is corrupted.\n", filename); + } + catch (CBadVer) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s is not a SoundFont version 2 file.\n", filename); + } + if (sf2 != NULL) + { + delete sf2; + } + return NULL; +} + +SFFile::SFFile(const char *filename) +: FontFile(filename) +{ + Presets = NULL; + PresetBags = NULL; + PresetGenerators = NULL; + Instruments = NULL; + InstrBags = NULL; + InstrGenerators = NULL; + Samples = NULL; + MinorVersion = 0; + SampleDataOffset = 0; + SampleDataLSBOffset = 0; + SizeSampleData = 0; + SizeSampleDataLSB = 0; + NumPresets = 0; + NumPresetBags = 0; + NumPresetGenerators = 0; + NumInstruments = 0; + NumInstrBags = 0; + NumInstrGenerators = 0; + NumSamples = 0; +} + +SFFile::~SFFile() +{ + if (Presets != NULL) + { + delete[] Presets; + } + if (PresetBags != NULL) + { + delete[] PresetBags; + } + if (PresetGenerators != NULL) + { + delete[] PresetGenerators; + } + if (Instruments != NULL) + { + delete[] Instruments; + } + if (InstrBags != NULL) + { + delete[] InstrBags; + } + if (InstrGenerators != NULL) + { + delete[] InstrGenerators; + } + if (Samples != NULL) + { + for (int i = 0; i < NumSamples; ++i) + { + if (Samples[i].InMemoryData != NULL) + { + delete[] Samples[i].InMemoryData; + } + } + delete[] Samples; + } +} + +bool SFFile::FinalStructureTest() +{ + // All required chunks must be present. + if (Presets == NULL || PresetBags == NULL || PresetGenerators == NULL || + Instruments == NULL || InstrBags == NULL || InstrGenerators == NULL || + Samples == NULL) + { + return false; + } + // What good is it if it has no sample data? + if (SizeSampleData == 0) + { + return false; + } + return true; +} + +void SFFile::SetOrder(int order, int drum, int bank, int program) +{ + if (drum) + { + for (int i = 0; i < NumPresets; ++i) + { + if (Percussion[i].Generators.drumset == bank && Percussion[i].Generators.key == program) + { + Percussion[i].LoadOrder = order; + } + } + } + else + { + for (int i = 0; i < NumPresets; ++i) + { + if (Presets[i].Program == program && Presets[i].Bank == bank) + { + Presets[i].LoadOrder = order; + } + } + } +} + +void SFFile::SetAllOrders(int order) +{ + for (int i = 0; i < NumPresets; ++i) + { + Presets[i].LoadOrder = order; + } + for (size_t i = 0; i < Percussion.size(); ++i) + { + Percussion[i].LoadOrder = order; + } +} + +Instrument *SFFile::LoadInstrument(Renderer *song, int drum, int bank, int program) +{ + return LoadInstrumentOrder(song, -1, drum, bank, program); +} + +Instrument *SFFile::LoadInstrumentOrder(Renderer *song, int order, int drum, int bank, int program) +{ + if (drum) + { + for (size_t i = 0; i < Percussion.size(); ++i) + { + if ((order < 0 || Percussion[i].LoadOrder == order) && + Percussion[i].Generators.drumset == bank && + Percussion[i].Generators.key == program) + { + return LoadPercussion(song, &Percussion[i]); + } + } + } + else + { + for (int i = 0; i < NumPresets - 1; ++i) + { + if ((order < 0 || Presets[i].LoadOrder == order) && + Presets[i].Bank == bank && + Presets[i].Program == program) + { + return LoadPreset(song, &Presets[i]); + } + } + } + return NULL; +} + +//=========================================================================== +// +// SFFile :: CheckBags +// +// For all preset and instrument zones, extract the velocity and key ranges +// and instrument and sample targets. +// +//=========================================================================== + +void SFFile::CheckBags() +{ + int i; + + for (i = 0; i < NumPresets - 1; ++i) + { + if (Presets[i].BagIndex >= Presets[i + 1].BagIndex) + { // Preset is empty. + Presets[i].Bank = ~0; + } + else + { + CheckZones(Presets[i].BagIndex, Presets[i + 1].BagIndex, 0); + Presets[i].bHasGlobalZone = PresetBags[Presets[i].BagIndex].Target < 0; + } + } + for (i = 0; i < NumInstruments - 1; ++i) + { + if (Instruments[i].BagIndex >= Instruments[i + 1].BagIndex) + { // Instrument is empty. + } + else + { + CheckZones(Instruments[i].BagIndex, Instruments[i + 1].BagIndex, 1); + Instruments[i].bHasGlobalZone = InstrBags[Instruments[i].BagIndex].Target < 0; + } + } +} + +//=========================================================================== +// +// SFFile :: CheckZones +// +// For every zone in the bag, extract the velocity and key ranges and +// instrument and sample targets. +// +//=========================================================================== + +void SFFile::CheckZones(int start, int stop, bool instr) +{ + SFBag *bag; + SFGenList *gens; + SFGenerator terminal_gen; + int zone_start, zone_stop; + int i, j; + + if (!instr) + { + bag = PresetBags; + gens = PresetGenerators; + terminal_gen = GEN_instrument; + } + else + { + bag = InstrBags; + gens = InstrGenerators; + terminal_gen = GEN_sampleID; + } + for (i = start; i < stop; ++i) + { + zone_start = bag[i].GenIndex; + zone_stop = bag[i + 1].GenIndex; + + if (zone_start > zone_stop) + { + // Skip empty zones, and mark them inaccessible. + bag[i].KeyRange.Lo = 255; + bag[i].KeyRange.Hi = 255; + bag[i].VelRange.Lo = 255; + bag[i].VelRange.Hi = 255; + continue; + } + + // According to the specs, if keyRange is present, it must be the first generator. + // If velRange is present, it may only be preceded by keyRange. In real life, there + // exist Soundfonts that violate this rule, so we need to scan every generator. + + // Preload ranges from the global zone. + if (i != start && bag[start].Target < 0) + { + bag[i].KeyRange = bag[start].KeyRange; + bag[i].VelRange = bag[start].VelRange; + } + for (j = zone_start; j < zone_stop; ++j) + { + if (gens[j].Oper == GEN_keyRange) + { + bag[i].KeyRange = gens[j].Range; + } + else if (gens[j].Oper == GEN_velRange) + { + bag[i].VelRange = gens[j].Range; + } + else if (gens[j].Oper == terminal_gen) + { + if (terminal_gen == GEN_instrument && gens[j].uAmount < NumInstruments - 1) + { + bag[i].Target = gens[j].uAmount; + } + else if (terminal_gen == GEN_sampleID && gens[j].uAmount < NumSamples - 1) + { + bag[i].Target = gens[j].uAmount; + } + break; + } + } + if (bag[i].Target < 0 && i != start) + { + // Only the first zone may be targetless. If any other zones are, + // make them inaccessible. + bag[i].KeyRange.Lo = 255; + bag[i].KeyRange.Hi = 255; + bag[i].VelRange.Lo = 255; + bag[i].VelRange.Hi = 255; + } + + // Check for swapped ranges. (Should we fix them or ignore them?) + if (bag[i].KeyRange.Lo > bag[i].KeyRange.Hi) + { + std::swap(bag[i].KeyRange.Lo, bag[i].KeyRange.Hi); + } + if (bag[i].VelRange.Lo > bag[i].VelRange.Hi) + { + std::swap(bag[i].VelRange.Lo, bag[i].VelRange.Hi); + } + } +} + +//=========================================================================== +// +// SFFile :: TranslatePercussions +// +// For every percussion instrument, compile a set of composite generators +// for each key, to make creating TiMidity instruments for individual +// percussion parts easier. +// +//=========================================================================== + +void SFFile::TranslatePercussions() +{ + for (int i = 0; i < NumPresets - 1; ++i) + { + if (Presets[i].Bank == 128 && Presets[i].Program < 128) + { + TranslatePercussionPreset(&Presets[i]); + } + } +} + +//=========================================================================== +// +// SFFile :: TranslatePercussionPreset +// +// Compile a set of composite generators for each key of this percussion +// instrument. Note that one instrument is actually an entire drumset. +// +//=========================================================================== + +void SFFile::TranslatePercussionPreset(SFPreset *preset) +{ + SFPerc perc; + int i; + bool has_global; + + perc.LoadOrder = preset->LoadOrder; + i = preset->BagIndex; + has_global = false; + + for (i = preset->BagIndex; i < (preset + 1)->BagIndex; ++i) + { + if (PresetBags[i].Target < 0) + { // This preset zone has no instrument. + continue; + } + if (PresetBags[i].KeyRange.Lo > 127 || PresetBags[i].VelRange.Lo > 127) + { // This preset zone is inaccesible. + continue; + } + TranslatePercussionPresetZone(preset, &PresetBags[i]); + } +} + +//=========================================================================== +// +// SFFile :: TranslatePercussionPresetZone +// +// Create a composite generator set for all keys and velocity ranges in this +// preset zone that intersect with this zone's instrument. +// +//=========================================================================== + +void SFFile::TranslatePercussionPresetZone(SFPreset *preset, SFBag *pzone) +{ + int key, i; + + for (key = pzone->KeyRange.Lo; key <= pzone->KeyRange.Hi; ++key) + { + SFInst *inst = &Instruments[pzone->Target]; + for (i = inst->BagIndex; i < (inst + 1)->BagIndex; ++i) + { + if (InstrBags[i].Target < 0) + { // This instrument zone has no sample. + continue; + } + if (InstrBags[i].KeyRange.Lo > key || InstrBags[i].KeyRange.Hi < key) + { // This instrument zone does not contain the key we want. + continue; + } + if (InstrBags[i].VelRange.Lo > pzone->VelRange.Hi || + InstrBags[i].VelRange.Hi < pzone->VelRange.Lo) + { // This instrument zone does not intersect the current velocity range. + continue; + } + // An intersection! Add the composite generator for this key and velocity range. + SFPerc perc; + perc.LoadOrder = preset->LoadOrder; + perc.Preset = preset; + perc.Generators = DefaultGenerators; + if (inst->bHasGlobalZone) + { + SetInstrumentGenerators(&perc.Generators, InstrBags[inst->BagIndex].GenIndex, InstrBags[inst->BagIndex + 1].GenIndex); + } + SetInstrumentGenerators(&perc.Generators, InstrBags[i].GenIndex, InstrBags[i + 1].GenIndex); + AddPresetGenerators(&perc.Generators, pzone->GenIndex, (pzone + 1)->GenIndex, preset); + perc.Generators.drumset = (uint8_t)preset->Program; + perc.Generators.key = key; + perc.Generators.velRange.Lo = std::max(pzone->VelRange.Lo, InstrBags[i].VelRange.Lo); + perc.Generators.velRange.Hi = std::min(pzone->VelRange.Hi, InstrBags[i].VelRange.Hi); + perc.Generators.sampleID = InstrBags[i].Target; + Percussion.push_back(perc); + } + } +} + +void SFFile::SetInstrumentGenerators(SFGenComposite *composite, int start, int stop) +{ + // Proceed from first to last; later generators override earlier ones. + SFGenList *gen = &InstrGenerators[start]; + for (int i = stop - start; i != 0; --i, ++gen) + { + if (gen->Oper >= GEN_NumGenerators) + { // Unknown generator. + continue; + } + if (GenDefs[gen->Oper].StructIndex >= sizeof(SFGenComposite)/2) + { // Generator is either unused or ignored. + continue; + } + // Set the generator + ((uint16_t *)composite)[GenDefs[gen->Oper].StructIndex] = gen->uAmount; + if (gen->Oper == GEN_sampleID) + { // Anything past sampleID is ignored. + break; + } + } +} + +void SFFile::AddPresetGenerators(SFGenComposite *composite, int start, int stop, SFPreset *preset) +{ + bool gen_set[GEN_NumGenerators] = { false, }; + AddPresetGenerators(composite, start, stop, gen_set); + if (preset->bHasGlobalZone) + { + AddPresetGenerators(composite, PresetBags[preset->BagIndex].GenIndex, PresetBags[preset->BagIndex + 1].GenIndex, gen_set); + } +} + +void SFFile::AddPresetGenerators(SFGenComposite *composite, int start, int stop, bool gen_set[GEN_NumGenerators]) +{ + // Proceed from last to first; later generators override earlier ones. + SFGenList *gen = &PresetGenerators[stop - 1]; + const GenDef *def; + + for (int i = stop - start; i != 0; --i, --gen) + { + if (gen->Oper >= GEN_NumGenerators) + { // Unknown generator. + continue; + } + if (gen_set[gen->Oper]) + { // Generator was already set. + continue; + } + def = &GenDefs[gen->Oper]; + if (def->StructIndex >= sizeof(SFGenComposite)/2) + { // Generator is either unused or ignored. + continue; + } + if (def->Flags & GENF_InstrOnly) + { // Generator is not valid at the preset level. + continue; + } + // Add to instrument/default generator. + int added = ((int16_t *)composite)[def->StructIndex] + gen->Amount; + // Clamp to proper range. + if (added <= -32768 && def->Flags & GENF_32768_Ok) + { + added = -32768; + } + else + { + added = std::max(def->Max, std::min(def->Min, added)); + } + ((int16_t *)composite)[def->StructIndex] = added; + gen_set[gen->Oper] = true; + if (gen->Oper == GEN_instrument) + { // Anything past the instrument generator is ignored. + break; + } + } +} + +Instrument *SFFile::LoadPercussion(Renderer *song, SFPerc *perc) +{ + size_t i; + int drumkey; + int drumset; + int j; + + Instrument *ip = new Instrument; + ip->samples = 0; + drumkey = perc->Generators.key; + drumset = perc->Generators.drumset; + + // Count all percussion composites that match this one's key and set. + for (i = 0; i < Percussion.size(); ++i) + { + if (Percussion[i].Generators.key == drumkey && + Percussion[i].Generators.drumset == drumset && + Percussion[i].Generators.sampleID < NumSamples) + { + SFSample *sfsamp = &Samples[Percussion[i].Generators.sampleID]; + if (sfsamp->InMemoryData == NULL) + { + LoadSample(song, sfsamp); + } + if (sfsamp->InMemoryData != NULL) + { + ip->samples++; + } + } + } + if (ip->samples == 0) + { // Nothing here to play. + delete ip; + return NULL; + } + ip->sample = (Sample *)safe_malloc(sizeof(Sample) * ip->samples); + memset(ip->sample, 0, sizeof(Sample) * ip->samples); + + // Fill in Sample structure for each composite. + for (j = 0, i = 0; i < Percussion.size(); ++i) + { + SFPerc *zone = &Percussion[i]; + SFGenComposite *gen = &zone->Generators; + if (gen->key != drumkey || + gen->drumset != drumset || + gen->sampleID >= NumSamples) + { + continue; + } + SFSample *sfsamp = &Samples[gen->sampleID]; + if (sfsamp->InMemoryData == NULL) + { + continue; + } + Sample *sp = ip->sample + j++; + + // Set velocity range + sp->low_vel = gen->velRange.Lo; + sp->high_vel = gen->velRange.Hi; + + // Set frequency range + sp->low_freq = note_to_freq(gen->key); + sp->high_freq = sp->low_freq; + + ApplyGeneratorsToRegion(gen, sfsamp, song, sp); + } + assert(j == ip->samples); + return ip; +} + +//=========================================================================== +// +// SFFile :: LoadPreset +// +//=========================================================================== + +Instrument *SFFile::LoadPreset(Renderer *song, SFPreset *preset) +{ + SFInst *inst; + SFSample *sfsamp; + SFGenComposite gen; + int i, j, k; + + Instrument *ip = new Instrument; + ip->samples = 0; + + // Count the number of regions we'll need. + for (i = preset->BagIndex; i < (preset + 1)->BagIndex; ++i) + { + if (PresetBags[i].Target < 0) + { // Preset zone has no instrument. + continue; + } + inst = &Instruments[PresetBags[i].Target]; + for (j = inst->BagIndex; j < (inst + 1)->BagIndex; ++j) + { + if (InstrBags[j].Target < 0) + { // Instrument zone has no sample. + continue; + } + if (InstrBags[j].KeyRange.Lo <= PresetBags[i].KeyRange.Hi && + InstrBags[j].KeyRange.Hi >= PresetBags[i].KeyRange.Lo && + InstrBags[j].VelRange.Lo <= PresetBags[i].VelRange.Hi && + InstrBags[j].VelRange.Hi >= PresetBags[i].VelRange.Lo) + { // The preset and instrument zones intersect! + sfsamp = &Samples[InstrBags[j].Target]; + if (sfsamp->InMemoryData == NULL) + { + LoadSample(song, sfsamp); + } + if (sfsamp->InMemoryData != NULL) + { + ip->samples++; + } + } + } + } + if (ip->samples == 0) + { // Nothing here to play. + delete ip; + return NULL; + } + // Allocate the regions and define them + ip->sample = (Sample *)safe_malloc(sizeof(Sample) * ip->samples); + memset(ip->sample, 0, sizeof(Sample) * ip->samples); + k = 0; + for (i = preset->BagIndex; i < (preset + 1)->BagIndex; ++i) + { + if (PresetBags[i].Target < 0) + { // Preset zone has no instrument. + continue; + } + inst = &Instruments[PresetBags[i].Target]; + for (j = inst->BagIndex; j < (inst + 1)->BagIndex; ++j) + { + if (InstrBags[j].Target < 0) + { // Instrument zone has no sample. + continue; + } + if (InstrBags[j].KeyRange.Lo <= PresetBags[i].KeyRange.Hi && + InstrBags[j].KeyRange.Hi >= PresetBags[i].KeyRange.Lo && + InstrBags[j].VelRange.Lo <= PresetBags[i].VelRange.Hi && + InstrBags[j].VelRange.Hi >= PresetBags[i].VelRange.Lo) + { // The preset and instrument zones intersect! + sfsamp = &Samples[InstrBags[j].Target]; + if (sfsamp->InMemoryData == NULL) + { + continue; + } + Sample *sp = ip->sample + k++; + + // Set velocity range + sp->low_vel = std::max(InstrBags[j].VelRange.Lo, PresetBags[i].VelRange.Lo); + sp->high_vel = std::min(InstrBags[j].VelRange.Hi, PresetBags[i].VelRange.Hi); + + // Set frequency range + sp->low_freq = note_to_freq(std::max(InstrBags[j].KeyRange.Lo, PresetBags[i].KeyRange.Lo)); + sp->high_freq = note_to_freq(std::min(InstrBags[j].KeyRange.Hi, PresetBags[i].KeyRange.Hi)); + + gen = DefaultGenerators; + if (inst->bHasGlobalZone) + { + SetInstrumentGenerators(&gen, InstrBags[inst->BagIndex].GenIndex, InstrBags[inst->BagIndex + 1].GenIndex); + } + SetInstrumentGenerators(&gen, InstrBags[j].GenIndex, InstrBags[j + 1].GenIndex); + AddPresetGenerators(&gen, PresetBags[i].GenIndex, PresetBags[i + 1].GenIndex, preset); + ApplyGeneratorsToRegion(&gen, sfsamp, song, sp); + } + } + } + assert(k == ip->samples); + return ip; +} + +//=========================================================================== +// +// SFFile :: ApplyGeneratorsToRegion +// +// The caller must set the key and velocity ranges. Other information for +// the TiMidity sample will be filled in using the generators given. +// +// FIXME: At least try to do something useful with every parameter. +// +//=========================================================================== + +void SFFile::ApplyGeneratorsToRegion(SFGenComposite *gen, SFSample *sfsamp, Renderer *song, Sample *sp) +{ + sp->type = INST_SF2; + + // Set loop and sample points + int start, end; + start = gen->startAddrsOffset + gen->startAddrsCoarseOffset * 32768; + end = gen->endAddrsOffset + gen->endAddrsCoarseOffset * 32768; + start = std::max(sfsamp->Start, sfsamp->Start + start); + end = std::min(sfsamp->End, sfsamp->End + end); + sp->loop_start = std::max(start, sfsamp->StartLoop + gen->startLoopAddrsOffset + gen->startLoopAddrsCoarseOffset * 32768); + sp->loop_end = std::min(end, sfsamp->EndLoop + gen->endLoopAddrsOffset + gen->endLoopAddrsCoarseOffset * 32768); + + sp->loop_start = (sp->loop_start - start) << FRACTION_BITS; + sp->loop_end = (sp->loop_end - start) << FRACTION_BITS; + sp->data_length = (end - start) << FRACTION_BITS; + sp->data = sfsamp->InMemoryData + start - sfsamp->Start; + if (gen->overridingRootKey >= 0 && gen->overridingRootKey <= 127) + { + sp->scale_note = gen->overridingRootKey; + } + else + { + sp->scale_note = sfsamp->OriginalPitch; + } + sp->root_freq = note_to_freq(sp->scale_note); + sp->sample_rate = sfsamp->SampleRate; + sp->key_group = gen->exclusiveClass; + + // Set key scaling + if (gen->keynum >= 0 && gen->keynum <= 127) + { + sp->scale_note = gen->keynum; + sp->scale_factor = 0; + } + else if (gen->scaleTuning >= 0) + { + sp->scale_factor = gen->scaleTuning * 1024 / 100; + // Does the root key also serve as the scale key? Assuming it does here. + } + else + { + sp->scale_factor = 1024; + sp->scale_note = 60; + } + + // Set panning + sp->panning = gen->pan; + + // Set volume envelope + sp->envelope.sf2.delay_vol = gen->delayVolEnv; + sp->envelope.sf2.attack_vol = gen->attackVolEnv; + sp->envelope.sf2.hold_vol = gen->holdVolEnv; + sp->envelope.sf2.decay_vol = gen->decayVolEnv; + sp->envelope.sf2.sustain_vol = gen->sustainVolEnv; + sp->envelope.sf2.release_vol = gen->releaseVolEnv; + + // Set sample modes + if (gen->sampleModes == 1) + { + sp->modes = PATCH_LOOPEN | PATCH_SUSTAIN | PATCH_NO_SRELEASE; + } + else if (gen->sampleModes == 3) + { + sp->modes = PATCH_LOOPEN | PATCH_SUSTAIN; + } + else + { + sp->modes = PATCH_SUSTAIN; + } + + // Set tuning (in cents) + sp->tune = gen->coarseTune * 100 + gen->fineTune; + + sp->velocity = (int8_t)gen->velocity; + sp->initial_attenuation = gen->initialAttenuation; +} + +//=========================================================================== +// +// SFFile :: LoadSample +// +// Loads a sample's data and converts it from 16/24-bit to floating point. +// +//=========================================================================== + +void SFFile::LoadSample(Renderer *song, SFSample *sample) +{ + auto fp = song->instruments->sfreader->open_file(Filename.c_str()); + uint32_t i; + + if (!fp) + { + return; + } + sample->InMemoryData = new float[sample->End - sample->Start + 1]; + fp->seek(SampleDataOffset + sample->Start * 2, SEEK_SET); + // Load 16-bit sample data. + for (i = 0; i < sample->End - sample->Start; ++i) + { + uint16_t samp; + fp->read(&samp, 2); + samp = LittleShort(samp); + sample->InMemoryData[i] = samp / 32768.f; + } + if (SampleDataLSBOffset != 0) + { // Load lower 8 bits of 24-bit sample data. + fp->seek(SampleDataLSBOffset + sample->Start, SEEK_SET); + for (i = 0; i < sample->End - sample->Start; ++i) + { + uint8_t samp; + fp->read(&samp, 1); + sample->InMemoryData[i] = ((((int32_t(sample->InMemoryData[i] * 32768) << 8) | samp) << 8) >> 8) / 8388608.f; + } + } + // Final 0 byte is for interpolation. + sample->InMemoryData[i] = 0; + fp->close(); +} +} \ No newline at end of file diff --git a/thirdparty/timidity/mix.cpp b/thirdparty/timidity/mix.cpp new file mode 100644 index 000000000..a2cc249c1 --- /dev/null +++ b/thirdparty/timidity/mix.cpp @@ -0,0 +1,759 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + mix.c + +*/ + +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" + + +namespace Timidity +{ + +static int convert_envelope_rate(Renderer *song, uint8_t rate) +{ + int r; + + r = 3 - ((rate>>6) & 0x3); + r *= 3; + r = (int)(rate & 0x3f) << r; /* 6.9 fixed point */ + + /* 15.15 fixed point. */ + return int(((r * 44100) / song->rate) * song->control_ratio) << 9; +} + +void Envelope::Init(Renderer *song, Voice *v) +{ + Type = v->sample->type; + env.bUpdating = true; + if (Type == INST_GUS) + { + gf1.Init(song, v); + gf1.ApplyToAmp(v); + } + else + { + sf2.Init(song, v); + sf2.ApplyToAmp(v); + } +} + +void GF1Envelope::Init(Renderer *song, Voice *v) +{ + /* Ramp up from 0 */ + stage = 0; + volume = 0; + + for (int i = 0; i < 6; ++i) + { + offset[i] = v->sample->envelope.gf1.offset[i] << (7 + 15); + rate[i] = convert_envelope_rate(song, v->sample->envelope.gf1.rate[i]); + } + Recompute(v); +} + +void GF1Envelope::Release(Voice *v) +{ + if (!(v->sample->modes & PATCH_NO_SRELEASE) || (v->sample->modes & PATCH_FAST_REL)) + { + /* ramp out to minimum volume with rate from final release stage */ + stage = GF1_RELEASEC+1; + target = 0; + increment = -rate[GF1_RELEASEC]; + } + else if (v->sample->modes & PATCH_SUSTAIN) + { + if (stage < GF1_RELEASE) + { + stage = GF1_RELEASE; + } + Recompute(v); + } + bUpdating = true; +} + +/* Returns 1 if envelope runs out */ +bool GF1Envelope::Recompute(Voice *v) +{ + int newstage; + + newstage = stage; + + if (newstage > GF1_RELEASEC) + { + /* Envelope ran out. */ + increment = 0; + bUpdating = false; + v->status &= ~(VOICE_SUSTAINING | VOICE_LPE); + v->status |= VOICE_RELEASING; + /* play sampled release */ + return 0; + } + + if (newstage == GF1_RELEASE && !(v->status & VOICE_RELEASING) && (v->sample->modes & PATCH_SUSTAIN)) + { + v->status |= VOICE_SUSTAINING; + /* Freeze envelope until note turns off. Trumpets want this. */ + increment = 0; + bUpdating = false; + } + else + { + stage = newstage + 1; + + if (volume == offset[newstage]) + { + return Recompute(v); + } + target = offset[newstage]; + increment = rate[newstage]; + if (target < volume) + increment = -increment; + } + + return 0; +} + +bool GF1Envelope::Update(Voice *v) +{ + volume += increment; + if (((increment < 0) && (volume <= target)) || ((increment > 0) && (volume >= target))) + { + volume = target; + if (Recompute(v)) + { + return 1; + } + } + return 0; +} + +void GF1Envelope::ApplyToAmp(Voice *v) +{ + double env_vol = v->attenuation; + double final_amp; + + final_amp = FINAL_MIX_SCALE; + if (v->tremolo_phase_increment != 0) + { // [RH] FIXME: This is wrong. Tremolo should offset the + // envelope volume, not scale it. + env_vol *= v->tremolo_volume; + } + env_vol *= volume / float(1 << 30); + env_vol = calc_gf1_amp(env_vol); + env_vol *= final_amp; + v->left_mix = float(env_vol * v->left_offset); + v->right_mix = float(env_vol * v->right_offset); +} + +void SF2Envelope::Init(Renderer *song, Voice *v) +{ + stage = 0; + volume = 0; + DelayTime = v->sample->envelope.sf2.delay_vol; + AttackTime = v->sample->envelope.sf2.attack_vol; + HoldTime = v->sample->envelope.sf2.hold_vol; + DecayTime = v->sample->envelope.sf2.decay_vol; + SustainLevel = v->sample->envelope.sf2.sustain_vol; + ReleaseTime = v->sample->envelope.sf2.release_vol; + SampleRate = song->rate; + HoldStart = 0; + RateMul = song->control_ratio / song->rate; + RateMul_cB = RateMul * 960; + bUpdating = true; +} + +void SF2Envelope::Release(Voice *v) +{ + if (stage == SF2_ATTACK) + { + // The attack stage does not use an attenuation in cB like all the rest. + volume = float(log10(volume) * -200); + } + stage = SF2_RELEASE; + bUpdating = true; +} + +static double timecent_to_sec(float timecent) +{ + if (timecent == -32768) + return 0; + return pow(2.0, timecent / 1200.0); +} + +static double calc_rate(double ratemul, double sec) +{ + if (sec < 0.006) + sec = 0.006; + return ratemul / sec; +} + +static void shutoff_voice(Voice *v) +{ + v->status &= ~(VOICE_SUSTAINING | VOICE_LPE); + v->status |= VOICE_RELEASING | VOICE_STOPPING; +} + +static bool check_release(double RateMul, double sec) +{ + double rate = calc_rate(960 * RateMul, sec); + + // Is release rate very fast? If so, don't do the release, but do + // the voice off ramp instead. + return (rate < 960/20); +} + +/* Returns 1 if envelope runs out */ +bool SF2Envelope::Update(Voice *v) +{ + double sec; + double newvolume = 0; + + // NOTE! The volume scale is different for different stages of the + // envelope generator: + // Attack stage goes from 0.0 -> 1.0, multiplied directly to the output. + // The following stages go from 0 -> -1000 cB (but recorded positively) + switch (stage) + { + case SF2_DELAY: + if (v->sample_count >= timecent_to_sec(DelayTime) * SampleRate) + { + stage = SF2_ATTACK; + return Update(v); + } + return 0; + + case SF2_ATTACK: + sec = timecent_to_sec(AttackTime); + if (sec <= 0) + { // instantaneous attack + newvolume = 1; + } + else + { + newvolume = volume + calc_rate(RateMul, sec); + } + if (newvolume >= 1) + { + volume = 0; + HoldStart = v->sample_count; + if (HoldTime <= -32768) + { // hold time is 0, so skip right to decay + stage = SF2_DECAY; + } + else + { + stage = SF2_HOLD; + } + return Update(v); + } + break; + + case SF2_HOLD: + if (v->sample_count - HoldStart >= timecent_to_sec(HoldTime) * SampleRate) + { + stage = SF2_DECAY; + return Update(v); + } + return 0; + + case SF2_DECAY: + sec = timecent_to_sec(DecayTime); + if (sec <= 0) + { // instantaneous decay + newvolume = SustainLevel; + } + else + { + newvolume = volume + calc_rate(RateMul_cB, sec); + } + if (newvolume >= SustainLevel) + { + newvolume = SustainLevel; + stage = SF2_SUSTAIN; + bUpdating = false; + if (!(v->status & VOICE_RELEASING)) + { + v->status |= VOICE_SUSTAINING; + } + } + break; + + case SF2_SUSTAIN: + // Stay here until released. + return 0; + + case SF2_RELEASE: + sec = timecent_to_sec(ReleaseTime); + if (sec <= 0) + { // instantaneous release + newvolume = 1000; + } + else + { + newvolume = volume + calc_rate(RateMul_cB, sec); + } + if (newvolume >= 960) + { + stage = SF2_FINISHED; + shutoff_voice(v); + bUpdating = false; + return 1; + } + break; + + case SF2_FINISHED: + return 1; + } + volume = (float)newvolume; + return 0; +} + +/* EMU 8k/10k don't follow spec in regards to volume attenuation. + * This factor is used in the equation pow (10.0, cb / FLUID_ATTEN_POWER_FACTOR). + * By the standard this should be -200.0. */ +#define FLUID_ATTEN_POWER_FACTOR (-531.509) +#define atten2amp(x) pow(10.0, (x) / FLUID_ATTEN_POWER_FACTOR) + +static double cb_to_amp(double x) // centibels to amp +{ + return pow(10, x / -200.f); +} + +void SF2Envelope::ApplyToAmp(Voice *v) +{ + double amp; + + if (stage == SF2_DELAY) + { + v->left_mix = 0; + v->right_mix = 0; + return; + } + + amp = v->sample->type == INST_SF2 ? atten2amp(v->attenuation) : cb_to_amp(v->attenuation); + + switch (stage) + { + case SF2_ATTACK: + amp *= volume; + break; + + case SF2_HOLD: + break; + + default: + amp *= cb_to_amp(volume); + break; + } + amp *= FINAL_MIX_SCALE * 0.5; + v->left_mix = float(amp * v->left_offset); + v->right_mix = float(amp * v->right_offset); +} + +void apply_envelope_to_amp(Voice *v) +{ + v->eg1.ApplyToAmp(v); +} + +static void update_tremolo(Voice *v) +{ + int depth = v->sample->tremolo_depth << 7; + + if (v->tremolo_sweep != 0) + { + /* Update sweep position */ + + v->tremolo_sweep_position += v->tremolo_sweep; + if (v->tremolo_sweep_position >= (1 << SWEEP_SHIFT)) + { + /* Swept to max amplitude */ + v->tremolo_sweep = 0; + } + else + { + /* Need to adjust depth */ + depth *= v->tremolo_sweep_position; + depth >>= SWEEP_SHIFT; + } + } + + v->tremolo_phase += v->tremolo_phase_increment; + + v->tremolo_volume = (float) + (1.0 - FSCALENEG((sine(v->tremolo_phase >> RATE_SHIFT) + 1.0) + * depth * TREMOLO_AMPLITUDE_TUNING, + 17)); + + /* I'm not sure about the +1.0 there -- it makes tremoloed voices' + volumes on average the lower the higher the tremolo amplitude. */ +} + +/* Returns 1 if the note died */ +static int update_signal(Voice *v) +{ + if (v->eg1.env.bUpdating && v->eg1.Update(v)) + { + return 1; + } + if (v->tremolo_phase_increment != 0) + { + update_tremolo(v); + } + apply_envelope_to_amp(v); + return 0; +} + +static void mix_mystery_signal(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, int count) +{ + final_volume_t + left = v->left_mix, + right = v->right_mix; + int cc; + sample_t s; + + if (!(cc = v->control_counter)) + { + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + + left = v->left_mix; + right = v->right_mix; + } + + while (count) + { + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + lp[0] += left * s; + lp[1] += right * s; + lp += 2; + } + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left = v->left_mix; + right = v->right_mix; + } + else + { + v->control_counter = cc - count; + while (count--) + { + s = *sp++; + lp[0] += left * s; + lp[1] += right * s; + lp += 2; + } + return; + } + } +} + +static void mix_single_signal(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, float *ampat, int count) +{ + final_volume_t amp; + int cc; + + if (0 == (cc = v->control_counter)) + { + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + } + amp = *ampat; + + while (count) + { + if (cc < count) + { + count -= cc; + while (cc--) + { + lp[0] += *sp++ * amp; + lp += 2; + } + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + amp = *ampat; + } + else + { + v->control_counter = cc - count; + while (count--) + { + lp[0] += *sp++ * amp; + lp += 2; + } + return; + } + } +} + +static void mix_single_left_signal(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, int count) +{ + mix_single_signal(control_ratio, sp, lp, v, &v->left_mix, count); +} + +static void mix_single_right_signal(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, int count) +{ + mix_single_signal(control_ratio, sp, lp + 1, v, &v->right_mix, count); +} + +static void mix_mono_signal(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, int count) +{ + final_volume_t + left = v->left_mix; + int cc; + + if (!(cc = v->control_counter)) + { + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left = v->left_mix; + } + + while (count) + { + if (cc < count) + { + count -= cc; + while (cc--) + { + *lp++ += *sp++ * left; + } + cc = control_ratio; + if (update_signal(v)) + return; /* Envelope ran out */ + left = v->left_mix; + } + else + { + v->control_counter = cc - count; + while (count--) + { + *lp++ += *sp++ * left; + } + return; + } + } +} + +static void mix_mystery(int32_t control_ratio, const sample_t *sp, float *lp, Voice *v, int count) +{ + final_volume_t + left = v->left_mix, + right = v->right_mix; + sample_t s; + + while (count--) + { + s = *sp++; + lp[0] += s * left; + lp[1] += s * right; + lp += 2; + } +} + +static void mix_single(const sample_t *sp, float *lp, final_volume_t amp, int count) +{ + while (count--) + { + lp[0] += *sp++ * amp; + lp += 2; + } +} + +static void mix_single_left(const sample_t *sp, float *lp, Voice *v, int count) +{ + mix_single(sp, lp, v->left_mix, count); +} +static void mix_single_right(const sample_t *sp, float *lp, Voice *v, int count) +{ + mix_single(sp, lp + 1, v->right_mix, count); +} + +static void mix_mono(const sample_t *sp, float *lp, Voice *v, int count) +{ + final_volume_t + left = v->left_mix; + + while (count--) + { + *lp++ += *sp++ * left; + } +} + +/* Ramp a note out in c samples */ +static void ramp_out(const sample_t *sp, float *lp, Voice *v, int c) +{ + final_volume_t left, right, li, ri; + + sample_t s = 0; /* silly warning about uninitialized s */ + + /* Fix by James Caldwell */ + if ( c == 0 ) c = 1; + + /* printf("Ramping out: left=%d, c=%d, li=%d\n", left, c, li); */ + + if (v->right_mix == 0) // All the way to the left + { + left = v->left_mix; + li = -(left/c); + if (li == 0) li = -1; + + while (c--) + { + left += li; + if (left < 0) + return; + lp[0] += *sp++ * left; + lp += 2; + } + } + else if (v->left_mix == 0) // All the way to the right + { + right = v->right_mix; + ri = -(right/c); + if (ri == 0) ri = -1; + + while (c--) + { + right += ri; + if (right < 0) + return; + s = *sp++; + lp[1] += *sp++ * right; + lp += 2; + } + } + else // Somewhere in the middle + { + left = v->left_mix; + li = -(left/c); + if (li == 0) li = -1; + right = v->right_mix; + ri = -(right/c); + if (ri == 0) ri = -1; + + right = v->right_mix; + ri = -(right/c); + while (c--) + { + left += li; + right += ri; + if (left < 0) + { + if (right < 0) + { + return; + } + left = 0; + } + else if (right < 0) + { + right = 0; + } + s = *sp++; + lp[0] += s * left; + lp[1] += s * right; + lp += 2; + } + } +} + + +/**************** interface function ******************/ + +void mix_voice(Renderer *song, float *buf, Voice *v, int c) +{ + int count = c; + sample_t *sp; + if (c < 0) + { + return; + } + if (v->status & VOICE_STOPPING) + { + if (count >= MAX_DIE_TIME) + count = MAX_DIE_TIME; + sp = resample_voice(song, v, &count); + ramp_out(sp, buf, v, count); + v->status = 0; + } + else + { + sp = resample_voice(song, v, &count); + if (count < 0) + { + return; + } + if (v->right_mix == 0) // All the way to the left + { + if (v->eg1.env.bUpdating || v->tremolo_phase_increment != 0) + { + mix_single_left_signal(song->control_ratio, sp, buf, v, count); + } + else + { + mix_single_left(sp, buf, v, count); + } + } + else if (v->left_mix == 0) // All the way to the right + { + if (v->eg1.env.bUpdating || v->tremolo_phase_increment != 0) + { + mix_single_right_signal(song->control_ratio, sp, buf, v, count); + } + else + { + mix_single_right(sp, buf, v, count); + } + } + else // Somewhere in the middle + { + if (v->eg1.env.bUpdating || v->tremolo_phase_increment) + { + mix_mystery_signal(song->control_ratio, sp, buf, v, count); + } + else + { + mix_mystery(song->control_ratio, sp, buf, v, count); + } + } + v->sample_count += count; + } +} + +} diff --git a/thirdparty/timidity/playmidi.cpp b/thirdparty/timidity/playmidi.cpp new file mode 100644 index 000000000..4a81bdf52 --- /dev/null +++ b/thirdparty/timidity/playmidi.cpp @@ -0,0 +1,909 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + playmidi.c -- random stuff in need of rearrangement + +*/ + +#include +#include +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" + + +namespace Timidity +{ + +bool Envelope::Update(struct Voice* v) +{ + if (Type == INST_GUS) + return gf1.Update(v); + return sf2.Update(v); +} +void Envelope::ApplyToAmp(struct Voice* v) +{ + if (Type == INST_GUS) + return gf1.ApplyToAmp(v); + return sf2.ApplyToAmp(v); +} +void Envelope::Release(struct Voice* v) +{ + if (Type == INST_GUS) + return gf1.Release(v); + return sf2.Release(v); +} + +void Renderer::reset_voices() +{ + memset(voice, 0, sizeof(voice[0]) * voices); +} + +/* Process the Reset All Controllers event */ +void Renderer::reset_controllers(int c) +{ + channel[c].volume = 100; + channel[c].expression = 127; + channel[c].sustain = 0; + channel[c].pitchbend = 0x2000; + channel[c].pitchfactor = 0; /* to be computed */ + channel[c].mono = 0; + channel[c].rpn = RPN_RESET; + channel[c].nrpn = RPN_RESET; +} + +void Renderer::reset_midi() +{ + for (int i = 0; i < MAXCHAN; i++) + { + reset_controllers(i); + /* The rest of these are unaffected by the Reset All Controllers event */ + channel[i].program = default_program; + channel[i].panning = NO_PANNING; + channel[i].pitchsens = 200; + channel[i].bank = 0; /* tone bank or drum set */ + } + reset_voices(); +} + +void Renderer::recompute_freq(int v) +{ + Channel *ch = &channel[voice[v].channel]; + int + sign = (voice[v].sample_increment < 0), /* for bidirectional loops */ + pb = ch->pitchbend; + double a; + + if (voice[v].sample->sample_rate == 0) + { + return; + } + + if (voice[v].vibrato_control_ratio != 0) + { + /* This instrument has vibrato. Invalidate any precomputed + sample_increments. */ + memset(voice[v].vibrato_sample_increment, 0, sizeof(voice[v].vibrato_sample_increment)); + } + + if (pb == 0x2000 || pb < 0 || pb > 0x3FFF) + { + voice[v].frequency = voice[v].orig_frequency; + } + else + { + pb -= 0x2000; + if (ch->pitchfactor == 0) + { + /* Damn. Somebody bent the pitch. */ + ch->pitchfactor = float(pow(2.f, ((abs(pb) * ch->pitchsens) / (8191.f * 1200.f)))); + } + if (pb < 0) + { + voice[v].frequency = voice[v].orig_frequency / ch->pitchfactor; + } + else + { + voice[v].frequency = voice[v].orig_frequency * ch->pitchfactor; + } + } + + a = FSCALE(((double)(voice[v].sample->sample_rate) * voice[v].frequency) / + ((double)(voice[v].sample->root_freq) * rate), + FRACTION_BITS); + + if (sign) + a = -a; /* need to preserve the loop direction */ + + voice[v].sample_increment = (int)(a); +} + +static const uint8_t vol_table[] = { +000 /* 000 */, 129 /* 001 */, 145 /* 002 */, 155 /* 003 */, +161 /* 004 */, 166 /* 005 */, 171 /* 006 */, 174 /* 007 */, +177 /* 008 */, 180 /* 009 */, 182 /* 010 */, 185 /* 011 */, +187 /* 012 */, 188 /* 013 */, 190 /* 014 */, 192 /* 015 */, +193 /* 016 */, 195 /* 017 */, 196 /* 018 */, 197 /* 019 */, +198 /* 020 */, 199 /* 021 */, 201 /* 022 */, 202 /* 023 */, +203 /* 024 */, 203 /* 025 */, 204 /* 026 */, 205 /* 027 */, +206 /* 028 */, 207 /* 029 */, 208 /* 030 */, 208 /* 031 */, +209 /* 032 */, 210 /* 033 */, 211 /* 034 */, 211 /* 035 */, +212 /* 036 */, 213 /* 037 */, 213 /* 038 */, 214 /* 039 */, +214 /* 040 */, 215 /* 041 */, 215 /* 042 */, 216 /* 043 */, +217 /* 044 */, 217 /* 045 */, 218 /* 046 */, 218 /* 047 */, +219 /* 048 */, 219 /* 049 */, 219 /* 050 */, 220 /* 051 */, +220 /* 052 */, 221 /* 053 */, 221 /* 054 */, 222 /* 055 */, +222 /* 056 */, 222 /* 057 */, 223 /* 058 */, 223 /* 059 */, +224 /* 060 */, 224 /* 061 */, 224 /* 062 */, 225 /* 063 */, +225 /* 064 */, 226 /* 065 */, 227 /* 066 */, 228 /* 067 */, +229 /* 068 */, 230 /* 069 */, 231 /* 070 */, 231 /* 071 */, +232 /* 072 */, 233 /* 073 */, 234 /* 074 */, 234 /* 075 */, +235 /* 076 */, 236 /* 077 */, 236 /* 078 */, 237 /* 079 */, +238 /* 080 */, 238 /* 081 */, 239 /* 082 */, 239 /* 083 */, +240 /* 084 */, 241 /* 085 */, 241 /* 086 */, 242 /* 087 */, +242 /* 088 */, 243 /* 089 */, 243 /* 090 */, 244 /* 091 */, +244 /* 092 */, 244 /* 093 */, 245 /* 094 */, 245 /* 095 */, +246 /* 096 */, 246 /* 097 */, 247 /* 098 */, 247 /* 099 */, +247 /* 100 */, 248 /* 101 */, 248 /* 102 */, 249 /* 103 */, +249 /* 104 */, 249 /* 105 */, 250 /* 106 */, 250 /* 107 */, +250 /* 108 */, 251 /* 109 */, 251 /* 110 */, 251 /* 111 */, +252 /* 112 */, 252 /* 113 */, 252 /* 114 */, 253 /* 115 */, +253 /* 116 */, 253 /* 117 */, 254 /* 118 */, 254 /* 119 */, +254 /* 120 */, 254 /* 121 */, 255 /* 122 */, 255 /* 123 */, +255 /* 124 */, 255 /* 125 */, 255 /* 126 */, 255 /* 127 */, +}; + +void Renderer::recompute_amp(Voice *v) +{ + Channel *chan = &channel[v->channel]; + int chanvol = chan->volume; + int chanexpr = chan->expression; + + if (v->sample->type == INST_GUS) + { + v->attenuation = (vol_table[(chanvol * chanexpr) / 127] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f); + } + else + { + // Implicit modulators from SF2 spec + double velatten, cc7atten, cc11atten; + + velatten = log10(127.0 / v->velocity); + cc7atten = log10(127.0 / chanvol); + cc11atten = log10(127.0 / chanexpr); + v->attenuation = float(400 * (velatten + cc7atten + cc11atten)) + v->sample->initial_attenuation; + } +} + +// Pan must be in the range [0,1] +void Renderer::compute_pan(double pan, int type, float &left_offset, float &right_offset) +{ + if (pan <= 0) + { + left_offset = 1; + right_offset = 0; + } + else if (pan >= 127/128.0) + { + left_offset = 0; + right_offset = 1; + } + else + { + if (type == INST_GUS) + { + /* Original amp equation looks like this: + * calc_gf1_amp(atten + offset) + * which expands to: + * 2^(16*(atten + offset) - 16) + * Keeping in mind that 2^(x + y) == 2^x * 2^y, we can + * rewrite this to avoid doing two pows in GF1Envelope::ApplyToAmp(): + * 2^(16*atten + 16*offset - 16) + * 2^(16*atten - 16 + 16 * offset + 16 - 16) + * 2^(16*atten - 16) * 2^(16*offset + 16 - 16) + * 2^(16*atten - 16) * 2^(16*(offset + 1) - 16) + * calc_gf1_amp(atten) * calc_gf1_amp(offset + 1) + */ + right_offset = (float)calc_gf1_amp((log(pan) * (1 / (log_of_2 * 32))) + 1); + left_offset = (float)calc_gf1_amp((log(1 - pan) * (1 / (log_of_2 * 32))) + 1); + } + else + { + /* Equal Power Panning for SF2/DLS. + */ + left_offset = (float)sqrt(1 - pan); + right_offset = (float)sqrt(pan); + } + } +} + +void Renderer::kill_key_group(int i) +{ + int j = voices; + + if (voice[i].sample->key_group == 0) + { + return; + } + while (j--) + { + if ((voice[j].status & VOICE_RUNNING) && !(voice[j].status & (VOICE_RELEASING | VOICE_STOPPING))) continue; + if (i == j) continue; + if (voice[i].channel != voice[j].channel) continue; + if (voice[j].sample->key_group != voice[i].sample->key_group) continue; + kill_note(j); + } +} + +float Renderer::calculate_scaled_frequency(Sample *sp, int note) +{ + double scalednote = (note - sp->scale_note) * sp->scale_factor / 1024.0 + sp->scale_note + sp->tune * 0.01; + return (float)note_to_freq(scalednote); +} + +bool Renderer::start_region(int chan, int note, int vel, Sample *sp, float f) +{ + int voicenum; + Voice *v; + + voicenum = allocate_voice(); + if (voicenum < 0) + { + return false; + } + v = &voice[voicenum]; + v->sample = sp; + + if (sp->type == INST_GUS) + { + v->orig_frequency = f; + } + else + { + if (sp->scale_factor != 1024) + { + v->orig_frequency = calculate_scaled_frequency(sp, note); + } + else if (sp->tune != 0) + { + v->orig_frequency = note_to_freq(note + sp->tune * 0.01); + } + else + { + v->orig_frequency = note_to_freq(note); + } + } + + v->status = VOICE_RUNNING; + v->channel = chan; + v->note = note; + v->velocity = vel; + v->sample_offset = 0; + v->sample_increment = 0; /* make sure it isn't negative */ + v->sample_count = 0; + + v->tremolo_phase = 0; + v->tremolo_phase_increment = v->sample->tremolo_phase_increment; + v->tremolo_sweep = v->sample->tremolo_sweep_increment; + v->tremolo_sweep_position = 0; + + v->vibrato_sweep = v->sample->vibrato_sweep_increment; + v->vibrato_sweep_position = 0; + v->vibrato_control_ratio = v->sample->vibrato_control_ratio; + v->vibrato_control_counter = v->vibrato_phase = 0; + + kill_key_group(voicenum); + + memset(v->vibrato_sample_increment, 0, sizeof(v->vibrato_sample_increment)); + + if (sp->type == INST_SF2) + { + // Channel pan is added to instrument pan. + double pan; + if (channel[chan].panning == NO_PANNING) + { + pan = (sp->panning + 500) / 1000.0; + } + else + { + pan = channel[chan].panning / 128.0 + sp->panning / 1000.0; + } + compute_pan(pan, sp->type, v->left_offset, v->right_offset); + } + else if (channel[chan].panning != NO_PANNING) + { + compute_pan(channel[chan].panning / 128.0, sp->type, v->left_offset, v->right_offset); + } + else + { + v->left_offset = v->sample->left_offset; + v->right_offset = v->sample->right_offset; + } + + recompute_freq(voicenum); + recompute_amp(v); + v->control_counter = 0; + + v->eg1.Init(this, v); + + if (v->sample->modes & PATCH_LOOPEN) + { + v->status |= VOICE_LPE; + } + return true; +} + +void Renderer::start_note(int chan, int note, int vel) +{ + Instrument *ip; + Sample *sp; + int bank = channel[chan].bank; + int prog = channel[chan].program; + int i; + float f; + + note &= 0x7f; + if (ISDRUMCHANNEL(chan)) + { + if (NULL == instruments->drumset[bank] || NULL == (ip = instruments->drumset[bank]->instrument[note])) + { + if (!(ip = instruments->drumset[0]->instrument[note])) + return; /* No instrument? Then we can't play. */ + } + assert(ip != MAGIC_LOAD_INSTRUMENT); + if (ip == MAGIC_LOAD_INSTRUMENT) + { + return; + } + if (ip->samples != 1 && ip->sample->type == INST_GUS) + { + printMessage(CMSG_WARNING, VERB_VERBOSE, + "Strange: percussion instrument with %d samples!", ip->samples); + } + } + else + { + if (channel[chan].program == SPECIAL_PROGRAM) + { + ip = default_instrument; + } + else if (NULL == instruments->tonebank[bank] || NULL == (ip = instruments->tonebank[bank]->instrument[prog])) + { + if (NULL == (ip = instruments->tonebank[0]->instrument[prog])) + return; /* No instrument? Then we can't play. */ + } + assert(ip != MAGIC_LOAD_INSTRUMENT); + if (ip == MAGIC_LOAD_INSTRUMENT) + { + return; + } + } + + if (NULL == ip->sample || ip->samples == 0) + return; /* No samples? Then nothing to play. */ + + // For GF1 patches, scaling is based solely on the first + // waveform in this layer. + if (ip->sample->type == INST_GUS && ip->sample->scale_factor != 1024) + { + f = calculate_scaled_frequency(ip->sample, note); + } + else + { + f = note_to_freq(note); + } + + if (ip->sample->type == INST_GUS) + { + /* We're more lenient with matching ranges for GUS patches, since the + * official Gravis ones don't cover the full range of possible + * frequencies for every instrument. + */ + if (ip->samples == 1) + { // If there's only one sample, definitely play it. + start_region(chan, note, vel, ip->sample, f); + } + for (i = ip->samples, sp = ip->sample; i != 0; --i, ++sp) + { + // GUS patches don't have velocity ranges, so no need to compare against them. + if (sp->low_freq <= f && sp->high_freq >= f) + { + if (i > 1 && (sp + 1)->low_freq <= f && (sp + 1)->high_freq >= f) + { /* If there is a range of contiguous regions that match our + * desired frequency, the last one in that block is used. + */ + continue; + } + start_region(chan, note, vel, sp, f); + break; + } + } + if (i == 0) + { /* Found nothing. Try again, but look for the one with the closest root frequency. + * As per the suggestion in the original TiMidity function, this search uses + * note values rather than raw frequencies. + */ + double cdiff = 1e10; + double want_note = freq_to_note(f); + Sample *closest = sp = ip->sample; + for (i = ip->samples; i != 0; --i, ++sp) + { + double diff = fabs(freq_to_note(sp->root_freq) - want_note); + if (diff < cdiff) + { + cdiff = diff; + closest = sp; + } + } + start_region(chan, note, vel, closest, f); + } + } + else + { + for (i = ip->samples, sp = ip->sample; i != 0; --i, ++sp) + { + if ((sp->low_vel <= vel && sp->high_vel >= vel && + sp->low_freq <= f && sp->high_freq >= f)) + { + if (!start_region(chan, note, vel, sp, f)) + { // Ran out of voices + break; + } + } + } + } +} + +void Renderer::kill_note(int i) +{ + Voice *v = &voice[i]; + + if (v->status & VOICE_RUNNING) + { + v->status &= ~VOICE_SUSTAINING; + v->status |= VOICE_RELEASING | VOICE_STOPPING; + } +} + +int Renderer::allocate_voice() +{ + int i, lowest; + float lv, v; + + for (i = 0; i < voices; ++i) + { + if (!(voice[i].status & VOICE_RUNNING)) + { + return i; /* Can't get a lower volume than silence */ + } + } + + /* Look for the decaying note with the lowest volume */ + lowest = -1; + lv = 1e10; + i = voices; + while (i--) + { + if ((voice[i].status & VOICE_RELEASING) && !(voice[i].status & VOICE_STOPPING)) + { + v = voice[i].attenuation; + if (v < lv) + { + lv = v; + lowest = i; + } + } + } + + if (lowest >= 0) + { + /* This can still cause a click, but if we had a free voice to + spare for ramping down this note, we wouldn't need to kill it + in the first place... Still, this needs to be fixed. Perhaps + we could use a reserve of voices to play dying notes only. */ + + cut_notes++; + voice[lowest].status = 0; + } + else + { + lost_notes++; + } + return lowest; +} + +void Renderer::note_on(int chan, int note, int vel) +{ + if (vel == 0) + { + note_off(chan, note, 0); + return; + } + + int i = voices; + + /* Only one instance of a note can be playing on a single channel. */ + while (i--) + { + if (voice[i].channel == chan && ((voice[i].note == note && !voice[i].sample->self_nonexclusive) || channel[chan].mono)) + { + if (channel[chan].mono) + { + kill_note(i); + } + else + { + finish_note(i); + } + } + } + + start_note(chan, note, vel); +} + +void Renderer::finish_note(int i) +{ + Voice *v = &voice[i]; + + if ((v->status & (VOICE_RUNNING | VOICE_RELEASING)) == VOICE_RUNNING) + { + v->status &= ~VOICE_SUSTAINING; + v->status |= VOICE_RELEASING; + + if (!(v->sample->modes & PATCH_NO_SRELEASE)) + { + v->status &= ~VOICE_LPE; /* sampled release */ + } + v->eg1.Release(v); + v->eg2.Release(v); + } +} + +void Renderer::note_off(int chan, int note, int vel) +{ + int i; + + for (i = voices; i-- > 0; ) + { + if ((voice[i].status & VOICE_RUNNING) && !(voice[i].status & (VOICE_RELEASING | VOICE_STOPPING)) + && voice[i].channel == chan && voice[i].note == note) + { + if (channel[chan].sustain) + { + voice[i].status |= NOTE_SUSTAIN; + } + else + { + finish_note(i); + } + } + } +} + +/* Process the All Notes Off event */ +void Renderer::all_notes_off(int chan) +{ + int i = voices; + while (i--) + { + if ((voice[i].status & VOICE_RUNNING) && voice[i].channel == chan) + { + if (channel[chan].sustain) + { + voice[i].status |= NOTE_SUSTAIN; + } + else + { + finish_note(i); + } + } + } +} + +/* Process the All Sounds Off event */ +void Renderer::all_sounds_off(int chan) +{ + int i = voices; + while (i--) + { + if (voice[i].channel == chan && + (voice[i].status & VOICE_RUNNING) && + !(voice[i].status & VOICE_STOPPING)) + { + kill_note(i); + } + } +} + +void Renderer::adjust_pressure(int chan, int note, int amount) +{ + int i = voices; + while (i--) + { + if ((voice[i].status & VOICE_RUNNING) && + voice[i].channel == chan && + voice[i].note == note) + { + voice[i].velocity = amount; + recompute_amp(&voice[i]); + apply_envelope_to_amp(&voice[i]); + if (!(voice[i].sample->self_nonexclusive)) + { + return; + } + } + } +} + +void Renderer::adjust_panning(int chan) +{ + Channel *chanp = &channel[chan]; + int i = voices; + while (i--) + { + Voice *v = &voice[i]; + if ((v->channel == chan) && (v->status & VOICE_RUNNING)) + { + double pan = chanp->panning / 128.0; + if (v->sample->type == INST_SF2) + { // Add instrument pan to channel pan. + pan += v->sample->panning / 500.0; + } + compute_pan(pan, v->sample->type, v->left_offset, v->right_offset); + apply_envelope_to_amp(v); + } + } +} + +void Renderer::drop_sustain(int chan) +{ + int i = voices; + while (i--) + { + if (voice[i].channel == chan && (voice[i].status & NOTE_SUSTAIN)) + { + finish_note(i); + } + } +} + +void Renderer::adjust_pitchbend(int chan) +{ + int i = voices; + while (i--) + { + if ((voice[i].status & VOICE_RUNNING) && voice[i].channel == chan) + { + recompute_freq(i); + } + } +} + +void Renderer::adjust_volume(int chan) +{ + int i = voices; + while (i--) + { + if (voice[i].channel == chan && (voice[i].status & VOICE_RUNNING)) + { + recompute_amp(&voice[i]); + apply_envelope_to_amp(&voice[i]); + } + } +} + +void Renderer::HandleEvent(int status, int parm1, int parm2) +{ + int command = status & 0xF0; + int chan = status & 0x0F; + + switch (command) + { + case ME_NOTEON: + note_on(chan, parm1, parm2); + break; + + case ME_NOTEOFF: + note_off(chan, parm1, parm2); + break; + + case ME_KEYPRESSURE: + adjust_pressure(chan, parm1, parm2); + break; + + case ME_CONTROLCHANGE: + HandleController(chan, parm1, parm2); + break; + + case ME_PROGRAM: + if (ISDRUMCHANNEL(chan)) + { + /* Change drum set */ + channel[chan].bank = parm1; + } + else + { + channel[chan].program = parm1; + } + break; + + case ME_CHANNELPRESSURE: + /* Unimplemented */ + break; + + case ME_PITCHWHEEL: + channel[chan].pitchbend = parm1 | (parm2 << 7); + channel[chan].pitchfactor = 0; + /* Adjust for notes already playing */ + adjust_pitchbend(chan); + break; + } +} + +void Renderer::HandleController(int chan, int ctrl, int val) +{ + switch (ctrl) + { + /* These should be the SCC-1 tone bank switch + commands. I don't know why there are two, or + why the latter only allows switching to bank 0. + Also, some MIDI files use 0 as some sort of + continuous controller. This will cause lots of + warnings about undefined tone banks. */ + case CTRL_BANK_SELECT: + channel[chan].bank = val; + break; + + case CTRL_BANK_SELECT+32: + if (val == 0) + { + channel[chan].bank = 0; + } + break; + + case CTRL_VOLUME: + channel[chan].volume = val; + adjust_volume(chan); + break; + + case CTRL_EXPRESSION: + channel[chan].expression = val; + adjust_volume(chan); + break; + + case CTRL_PAN: + channel[chan].panning = val; + adjust_panning(chan); + break; + + case CTRL_SUSTAIN: + channel[chan].sustain = val; + if (val == 0) + { + drop_sustain(chan); + } + break; + + case CTRL_NRPN_LSB: + channel[chan].nrpn = (channel[chan].nrpn & 0x3F80) | (val); + channel[chan].nrpn_mode = true; + break; + + case CTRL_NRPN_MSB: + channel[chan].nrpn = (channel[chan].nrpn & 0x007F) | (val << 7); + channel[chan].nrpn_mode = true; + break; + + case CTRL_RPN_LSB: + channel[chan].rpn = (channel[chan].rpn & 0x3F80) | (val); + channel[chan].nrpn_mode = false; + break; + + case CTRL_RPN_MSB: + channel[chan].rpn = (channel[chan].rpn & 0x007F) | (val << 7); + channel[chan].nrpn_mode = false; + break; + + case CTRL_DATA_ENTRY: + if (channel[chan].nrpn_mode) + { + DataEntryCoarseNRPN(chan, channel[chan].nrpn, val); + } + else + { + DataEntryCoarseRPN(chan, channel[chan].rpn, val); + } + break; + + case CTRL_DATA_ENTRY+32: + if (channel[chan].nrpn_mode) + { + DataEntryFineNRPN(chan, channel[chan].nrpn, val); + } + else + { + DataEntryFineRPN(chan, channel[chan].rpn, val); + } + break; + + case CTRL_ALL_SOUNDS_OFF: + all_sounds_off(chan); + break; + + case CTRL_RESET_CONTROLLERS: + reset_controllers(chan); + break; + + case CTRL_ALL_NOTES_OFF: + all_notes_off(chan); + break; + } +} + +void Renderer::DataEntryCoarseRPN(int chan, int rpn, int val) +{ + switch (rpn) + { + case RPN_PITCH_SENS: + channel[chan].pitchsens = (channel[chan].pitchsens % 100) + (val * 100); + channel[chan].pitchfactor = 0; + break; + + // TiMidity resets the pitch sensitivity when a song attempts to write to + // RPN_RESET. My docs tell me this is just a dummy value that is guaranteed + // to not cause future data entry to go anywhere until a new RPN is set. + } +} + +void Renderer::DataEntryFineRPN(int chan, int rpn, int val) +{ + switch (rpn) + { + case RPN_PITCH_SENS: + channel[chan].pitchsens = (channel[chan].pitchsens / 100) * 100 + val; + channel[chan].pitchfactor = 0; + break; + } +} + +void Renderer::DataEntryCoarseNRPN(int chan, int nrpn, int val) +{ +} + +void Renderer::DataEntryFineNRPN(int chan, int nrpn, int val) +{ +} + +void Renderer::HandleLongMessage(const uint8_t *data, int len) +{ + // SysEx handling goes here. +} + +void Renderer::Reset() +{ + lost_notes = cut_notes = 0; + reset_midi(); +} + +} diff --git a/thirdparty/timidity/resample.cpp b/thirdparty/timidity/resample.cpp new file mode 100644 index 000000000..85f342cf4 --- /dev/null +++ b/thirdparty/timidity/resample.cpp @@ -0,0 +1,615 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + resample.c + +*/ + +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" + + +namespace Timidity +{ + +#define RESAMPLATION {\ + int o = ofs >> FRACTION_BITS, m = ofs & FRACTION_MASK; \ + *dest++ = src[o] + (src[o + 1] - src[o]) * m / (1 << FRACTION_BITS);\ + } + +#define FINALINTERP if (ofs == le) *dest++ = src[ofs >> FRACTION_BITS]; +/* So it isn't interpolation. At least it's final. */ + +/*************** resampling with fixed increment *****************/ + +static sample_t *rs_plain(sample_t *resample_buffer, Voice *v, int *countptr) +{ + /* Play sample until end, then free the voice. */ + + const sample_t + *src = v->sample->data; + sample_t + *dest = resample_buffer; + int + ofs = v->sample_offset, + incr = v->sample_increment, + le = v->sample->data_length, + count = *countptr; + + int i; + + if (incr < 0) incr = -incr; /* In case we're coming out of a bidir loop */ + + /* Precalc how many times we should go through the loop. + NOTE: Assumes that incr > 0 and that ofs <= le */ + i = (le - ofs) / incr + 1; + + if (i > count) + { + i = count; + count = 0; + } + else + { + count -= i; + } + + while (i--) + { + RESAMPLATION; + ofs += incr; + } + + if (ofs >= le) + { + FINALINTERP; + v->status = 0; + *countptr -= count + 1; + } + + v->sample_offset = ofs; /* Update offset */ + return resample_buffer; +} + +static sample_t *rs_loop(sample_t *resample_buffer, Voice *vp, int count) +{ + /* Play sample until end-of-loop, skip back and continue. */ + + int + ofs = vp->sample_offset, + incr = vp->sample_increment, + le = vp->sample->loop_end, + ll = le - vp->sample->loop_start; + sample_t + *dest = resample_buffer; + const sample_t + *src = vp->sample->data; + + int i; + + while (count) + { + if (ofs >= le) + /* NOTE: Assumes that ll > incr and that incr > 0. */ + ofs -= ll; + /* Precalc how many times we should go through the loop */ + i = (le - ofs) / incr + 1; + if (i > count) + { + i = count; + count = 0; + } + else + { + count -= i; + } + while (i--) + { + RESAMPLATION; + ofs += incr; + } + } + + vp->sample_offset=ofs; /* Update offset */ + return resample_buffer; +} + +static sample_t *rs_bidir(sample_t *resample_buffer, Voice *vp, int count) +{ + int + ofs = vp->sample_offset, + incr = vp->sample_increment, + le = vp->sample->loop_end, + ls = vp->sample->loop_start; + sample_t + *dest = resample_buffer; + const sample_t + *src = vp->sample->data; + + int + le2 = le << 1, + ls2 = ls << 1, + i; + /* Play normally until inside the loop region */ + + if (ofs <= ls) + { + /* NOTE: Assumes that incr > 0, which is NOT always the case + when doing bidirectional looping. I have yet to see a case + where both ofs <= ls AND incr < 0, however. */ + i = (ls - ofs) / incr + 1; + if (i > count) + { + i = count; + count = 0; + } + else + { + count -= i; + } + while (i--) + { + RESAMPLATION; + ofs += incr; + } + } + + /* Then do the bidirectional looping */ + + while(count) + { + /* Precalc how many times we should go through the loop */ + i = ((incr > 0 ? le : ls) - ofs) / incr + 1; + if (i > count) + { + i = count; + count = 0; + } + else + { + count -= i; + } + while (i--) + { + RESAMPLATION; + ofs += incr; + } + if (ofs >= le) + { + /* fold the overshoot back in */ + ofs = le2 - ofs; + incr *= -1; + } + else if (ofs <= ls) + { + ofs = ls2 - ofs; + incr *= -1; + } + } + + vp->sample_increment = incr; + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer; +} + +/*********************** vibrato versions ***************************/ + +/* We only need to compute one half of the vibrato sine cycle */ +static int vib_phase_to_inc_ptr(int phase) +{ + if (phase < VIBRATO_SAMPLE_INCREMENTS / 2) + return VIBRATO_SAMPLE_INCREMENTS / 2 - 1 - phase; + else if (phase >= VIBRATO_SAMPLE_INCREMENTS * 3 / 2) + return VIBRATO_SAMPLE_INCREMENTS * 5 / 2 - 1 - phase; + else + return phase - VIBRATO_SAMPLE_INCREMENTS / 2; +} + +static int update_vibrato(float output_rate, Voice *vp, int sign) +{ + int depth; + int phase; + double a, pb; + + if (vp->vibrato_phase++ >= 2 * VIBRATO_SAMPLE_INCREMENTS - 1) + vp->vibrato_phase = 0; + phase = vib_phase_to_inc_ptr(vp->vibrato_phase); + + if (vp->vibrato_sample_increment[phase]) + { + if (sign) + return -vp->vibrato_sample_increment[phase]; + else + return vp->vibrato_sample_increment[phase]; + } + + /* Need to compute this sample increment. */ + depth = vp->sample->vibrato_depth << 7; + + if (vp->vibrato_sweep != 0) + { + /* Need to update sweep */ + vp->vibrato_sweep_position += vp->vibrato_sweep; + if (vp->vibrato_sweep_position >= (1<vibrato_sweep=0; + else + { + /* Adjust depth */ + depth *= vp->vibrato_sweep_position; + depth >>= SWEEP_SHIFT; + } + } + + a = FSCALE(((double)(vp->sample->sample_rate) * vp->frequency) / + ((double)(vp->sample->root_freq) * output_rate), + FRACTION_BITS); + + pb = (sine(vp->vibrato_phase * (1.0/(2*VIBRATO_SAMPLE_INCREMENTS))) + * (double)(depth) * VIBRATO_AMPLITUDE_TUNING); + + a *= pow(2.0, pb / (8192 * 12.f)); + + /* If the sweep's over, we can store the newly computed sample_increment */ + if (!vp->vibrato_sweep) + vp->vibrato_sample_increment[phase] = (int) a; + + if (sign) + a = -a; /* need to preserve the loop direction */ + + return (int) a; +} + +static sample_t *rs_vib_plain(sample_t *resample_buffer, float rate, Voice *vp, int *countptr) +{ + /* Play sample until end, then free the voice. */ + + sample_t + *dest = resample_buffer; + const sample_t + *src = vp->sample->data; + int + le = vp->sample->data_length, + ofs = vp->sample_offset, + incr = vp->sample_increment, + count = *countptr; + int + cc = vp->vibrato_control_counter; + + /* This has never been tested */ + + if (incr < 0) incr = -incr; /* In case we're coming out of a bidir loop */ + + while (count--) + { + if (!cc--) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(rate, vp, 0); + } + RESAMPLATION; + ofs += incr; + if (ofs >= le) + { + FINALINTERP; + vp->status = 0; + *countptr -= count+1; + break; + } + } + + vp->vibrato_control_counter = cc; + vp->sample_increment = incr; + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer; +} + +static sample_t *rs_vib_loop(sample_t *resample_buffer, float rate, Voice *vp, int count) +{ + /* Play sample until end-of-loop, skip back and continue. */ + + int + ofs = vp->sample_offset, + incr = vp->sample_increment, + le = vp->sample->loop_end, + ll = le - vp->sample->loop_start; + sample_t + *dest = resample_buffer; + const sample_t + *src = vp->sample->data; + int + cc = vp->vibrato_control_counter; + + int i; + int + vibflag=0; + + while (count) + { + /* Hopefully the loop is longer than an increment */ + if (ofs >= le) + ofs -= ll; + /* Precalc how many times to go through the loop, taking + the vibrato control ratio into account this time. */ + i = (le - ofs) / incr + 1; + if (i > count) i = count; + if (i > cc) + { + i = cc; + vibflag = 1; + } + else + { + cc -= i; + } + count -= i; + while (i--) + { + RESAMPLATION; + ofs += incr; + } + if (vibflag) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(rate, vp, 0); + vibflag = 0; + } + } + + vp->vibrato_control_counter = cc; + vp->sample_increment = incr; + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer; +} + +static sample_t *rs_vib_bidir(sample_t *resample_buffer, float rate, Voice *vp, int count) +{ + int + ofs = vp->sample_offset, + incr = vp->sample_increment, + le = vp->sample->loop_end, + ls = vp->sample->loop_start; + sample_t + *dest = resample_buffer; + const sample_t + *src = vp->sample->data; + int + cc = vp->vibrato_control_counter; + + int + le2 = le << 1, + ls2 = ls << 1, + i; + int + vibflag = 0; + + /* Play normally until inside the loop region */ + while (count && (ofs <= ls)) + { + i = (ls - ofs) / incr + 1; + if (i > count) + { + i = count; + } + if (i > cc) + { + i = cc; + vibflag = 1; + } + else + { + cc -= i; + } + count -= i; + while (i--) + { + RESAMPLATION; + ofs += incr; + } + if (vibflag) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(rate, vp, 0); + vibflag = 0; + } + } + + /* Then do the bidirectional looping */ + + while (count) + { + /* Precalc how many times we should go through the loop */ + i = ((incr > 0 ? le : ls) - ofs) / incr + 1; + if(i > count) + { + i = count; + } + if(i > cc) + { + i = cc; + vibflag = 1; + } + else + { + cc -= i; + } + count -= i; + while (i--) + { + RESAMPLATION; + ofs += incr; + } + if (vibflag) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(rate, vp, (incr < 0)); + vibflag = 0; + } + if (ofs >= le) + { + /* fold the overshoot back in */ + ofs = le2 - ofs; + incr *= -1; + } + else if (ofs <= ls) + { + ofs = ls2 - ofs; + incr *= -1; + } + } + + vp->vibrato_control_counter = cc; + vp->sample_increment = incr; + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer; +} + +sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr) +{ + int ofs; + uint16_t modes; + + if (vp->sample->sample_rate == 0) + { + /* Pre-resampled data -- just update the offset and check if + we're out of data. */ + ofs = vp->sample_offset >> FRACTION_BITS; /* Kind of silly to use FRACTION_BITS here... */ + if (*countptr >= (vp->sample->data_length >> FRACTION_BITS) - ofs) + { + /* Note finished. Free the voice. */ + vp->status = 0; + + /* Let the caller know how much data we had left */ + *countptr = (vp->sample->data_length >> FRACTION_BITS) - ofs; + } + else + { + vp->sample_offset += *countptr << FRACTION_BITS; + } + return vp->sample->data + ofs; + } + + /* Need to resample. Use the proper function. */ + modes = vp->sample->modes; + + if (vp->status & VOICE_LPE) + { + if (vp->sample->loop_end - vp->sample->loop_start < 2) + { // Loop is too short; turn it off. + vp->status &= ~VOICE_LPE; + } + } + + if (vp->vibrato_control_ratio) + { + if (vp->status & VOICE_LPE) + { + if (modes & PATCH_BIDIR) + return rs_vib_bidir(song->resample_buffer, song->rate, vp, *countptr); + else + return rs_vib_loop(song->resample_buffer, song->rate, vp, *countptr); + } + else + { + return rs_vib_plain(song->resample_buffer, song->rate, vp, countptr); + } + } + else + { + if (vp->status & VOICE_LPE) + { + if (modes & PATCH_BIDIR) + return rs_bidir(song->resample_buffer, vp, *countptr); + else + return rs_loop(song->resample_buffer, vp, *countptr); + } + else + { + return rs_plain(song->resample_buffer, vp, countptr); + } + } +} + +void pre_resample(Renderer *song, Sample *sp) +{ + double a, xdiff; + int incr, ofs, newlen, count; + sample_t *newdata, *dest, *src = sp->data; + sample_t v1, v2, v3, v4, *vptr; + static const char note_name[12][3] = + { + "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" + }; + + if (sp->scale_factor != 0) + return; + + a = (sp->sample_rate * note_to_freq(sp->scale_note)) / (sp->root_freq * song->rate); + if (a <= 0) + return; + newlen = (int)(sp->data_length / a); + if (newlen < 0 || (newlen >> FRACTION_BITS) > MAX_SAMPLE_SIZE) + return; + + count = newlen >> FRACTION_BITS; + dest = newdata = (sample_t *)safe_malloc(count * sizeof(float)); + + ofs = incr = (sp->data_length - (1 << FRACTION_BITS)) / count; + + if (--count) + *dest++ = src[0]; + + /* Since we're pre-processing and this doesn't have to be done in + real-time, we go ahead and do the full sliding cubic interpolation. */ + while (--count) + { + vptr = src + (ofs >> FRACTION_BITS); + v1 = (vptr == src) ? *vptr : *(vptr - 1); + v2 = *vptr; + v3 = *(vptr + 1); + v4 = *(vptr + 2); + xdiff = FSCALENEG(ofs & FRACTION_MASK, FRACTION_BITS); + *dest++ = sample_t(v2 + (xdiff / 6.0) * (-2 * v1 - 3 * v2 + 6 * v3 - v4 + + xdiff * (3 * (v1 - 2 * v2 + v3) + xdiff * (-v1 + 3 * (v2 - v3) + v4)))); + ofs += incr; + } + + if (ofs & FRACTION_MASK) + { + RESAMPLATION + } + else + { + *dest++ = src[ofs >> FRACTION_BITS]; + } + + sp->data_length = newlen; + sp->loop_start = int(sp->loop_start / a); + sp->loop_end = int(sp->loop_end / a); + free(sp->data); + sp->data = newdata; + sp->sample_rate = 0; +} + +} diff --git a/thirdparty/timidity/timidity.cpp b/thirdparty/timidity/timidity.cpp new file mode 100644 index 000000000..7453f8dc2 --- /dev/null +++ b/thirdparty/timidity/timidity.cpp @@ -0,0 +1,839 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + timidity.c +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" + + +void ZMusic_Print(int type, const char* msg, va_list args); + +namespace Timidity +{ + +static long ParseCommandLine(const char* args, int* argc, char** argv) +{ + int count; + char* buffplace; + + count = 0; + buffplace = NULL; + if (argv != NULL) + { + buffplace = argv[0]; + } + + for (;;) + { + while (*args <= ' ' && *args) + { // skip white space + args++; + } + if (*args == 0) + { + break; + } + else if (*args == '\"') + { // read quoted string + char stuff; + if (argv != NULL) + { + argv[count] = buffplace; + } + count++; + args++; + do + { + stuff = *args++; + if (stuff == '\"') + { + stuff = 0; + } + else if (stuff == 0) + { + args--; + } + if (argv != NULL) + { + *buffplace = stuff; + } + buffplace++; + } while (stuff); + } + else + { // read unquoted string + const char* start = args++, * end; + + while (*args && *args > ' ' && *args != '\"') + args++; + + end = args; + if (argv != NULL) + { + argv[count] = buffplace; + while (start < end) + * buffplace++ = *start++; + *buffplace++ = 0; + } + else + { + buffplace += end - start + 1; + } + count++; + } + } + if (argc != NULL) + { + *argc = count; + } + return (long)(buffplace - (char*)0); +} + +class FCommandLine +{ +public: + FCommandLine(const char* commandline) + { + cmd = commandline; + _argc = -1; + _argv = NULL; + argsize = 0; + } + + ~FCommandLine() + { + if (_argv != NULL) + { + delete[] _argv; + } + } + + int argc() + { + if (_argc == -1) + { + argsize = ParseCommandLine(cmd, &_argc, NULL); + } + return _argc; + } + + char* operator[] (int i) + { + if (_argv == NULL) + { + int count = argc(); + _argv = new char* [count + (argsize + sizeof(char*) - 1) / sizeof(char*)]; + _argv[0] = (char*)_argv + count * sizeof(char*); + ParseCommandLine(cmd, NULL, _argv); + } + return _argv[i]; + } + + + const char* args() { return cmd; } + + void Shift() + { + // Only valid after _argv has been filled. + for (int i = 1; i < _argc; ++i) + { + _argv[i - 1] = _argv[i]; + } + } + +private: + const char* cmd; + int _argc; + char** _argv; + long argsize; +}; + + +int Instruments::read_config_file(const char *name) +{ + char tmp[1024], *cp; + ToneBank *bank = NULL; + int i, j, k, line = 0, words; + static int rcf_count = 0; + + if (rcf_count > 50) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "Timidity: Probable source loop in configuration files\n"); + return (-1); + } + auto fp = sfreader->open_file(name); + if (!fp) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "Timidity: Unable to open config file\n"); + return -1; + } + + while (fp->gets(tmp, sizeof(tmp))) + { + line++; + FCommandLine w(tmp); + words = w.argc(); + if (words == 0) continue; + + /* Originally the TiMidity++ extensions were prefixed like this */ + if (strcmp(w[0], "#extension") == 0) + { + w.Shift(); + words--; + } + else if (*w[0] == '#') + { + continue; + } + + for (i = 0; i < words; ++i) + { + if (*w[i] == '#') + { + words = i; + break; + } + } + + /* + * TiMidity++ adds a number of extensions to the config file format. + * Many of them are completely irrelevant to SDL_sound, but at least + * we shouldn't choke on them. + * + * Unfortunately the documentation for these extensions is often quite + * vague, gramatically strange or completely absent. + */ + if ( + !strcmp(w[0], "comm") /* "comm" program second */ + || !strcmp(w[0], "HTTPproxy") /* "HTTPproxy" hostname:port */ + || !strcmp(w[0], "FTPproxy") /* "FTPproxy" hostname:port */ + || !strcmp(w[0], "mailaddr") /* "mailaddr" your-mail-address */ + || !strcmp(w[0], "opt") /* "opt" timidity-options */ + ) + { + /* + * + "comm" sets some kind of comment -- the documentation is too + * vague for me to understand at this time. + * + "HTTPproxy", "FTPproxy" and "mailaddr" are for reading data + * over a network, rather than from the file system. + * + "opt" specifies default options for TiMidity++. + * + * These are all quite useless for our version of TiMidity, so + * they can safely remain no-ops. + */ + } + else if (!strcmp(w[0], "timeout")) /* "timeout" program second */ + { + /* + * Specifies a timeout value of the program. A number of seconds + * before TiMidity kills the note. This may be useful to implement + * later, but I don't see any urgent need for it. + */ + //printMessage(CMSG_ERROR, VERB_NORMAL, "FIXME: Implement \"timeout\" in TiMidity config.\n"); + } + else if (!strcmp(w[0], "copydrumset") /* "copydrumset" drumset */ + || !strcmp(w[0], "copybank")) /* "copybank" bank */ + { + /* + * Copies all the settings of the specified drumset or bank to + * the current drumset or bank. May be useful later, but not a + * high priority. + */ + //printMessage(CMSG_ERROR, VERB_NORMAL, "FIXME: Implement \"%s\" in TiMidity config.\n", w[0]); + } + else if (!strcmp(w[0], "undef")) /* "undef" progno */ + { + /* + * Undefines the tone "progno" of the current tone bank (or + * drum set?). Not a high priority. + */ + //printMessage(CMSG_ERROR, VERB_NORMAL, "FIXME: Implement \"undef\" in TiMidity config.\n"); + } + else if (!strcmp(w[0], "altassign")) /* "altassign" prog1 prog2 ... */ + { + /* + * Sets the alternate assign for drum set. Whatever that's + * supposed to mean. + */ + //printMessage(CMSG_ERROR, VERB_NORMAL, "FIXME: Implement \"altassign\" in TiMidity config.\n"); + } + else if (!strcmp(w[0], "soundfont")) + { + /* + * "soundfont" sf_file "remove" + * "soundfont" sf_file ["order=" order] ["cutoff=" cutoff] + * ["reso=" reso] ["amp=" amp] + */ + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No soundfont given\n", name, line); + return -2; + } + if (words > 2 && !strcmp(w[2], "remove")) + { + font_remove(w[1]); + } + else + { + int order = 0; + + for (i = 2; i < words; ++i) + { + if (!(cp = strchr(w[i], '='))) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: bad soundfont option %s\n", name, line, w[i]); + return -2; + } + } + font_add(w[1], order); + } + } + else if (!strcmp(w[0], "font")) + { + /* + * "font" "exclude" bank preset keynote + * "font" "order" order bank preset keynote + */ + int order, drum = -1, bank = -1, instr = -1; + + if (words < 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error\n", name, line); + return -2; + } + + if (!strcmp(w[1], "exclude")) + { + order = 254; + i = 2; + } + else if (!strcmp(w[1], "order")) + { + order = atoi(w[2]); + i = 3; + } + else + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: font subcommand must be 'order' or 'exclude'\n", name, line); + return -2; + } + if (i < words) + { + drum = atoi(w[i++]); + } + if (i < words) + { + bank = atoi(w[i++]); + } + if (i < words) + { + instr = atoi(w[i++]); + } + if (drum != 128) + { + instr = bank; + bank = drum; + drum = 0; + } + else + { + drum = 1; + } + font_order(order, drum, bank, instr); + } + else if (!strcmp(w[0], "progbase")) + { + /* + * The documentation for this makes absolutely no sense to me, but + * apparently it sets some sort of base offset for tone numbers. + * Why anyone would want to do this is beyond me. + */ + //printMessage(CMSG_ERROR, VERB_NORMAL, "FIXME: Implement \"progbase\" in TiMidity config.\n"); + } + else if (!strcmp(w[0], "map")) /* "map" name set1 elem1 set2 elem2 */ + { + /* + * This extension is the one we will need to implement, as it is + * used by the "eawpats". Unfortunately I cannot find any + * documentation whatsoever for it, but it looks like it's used + * for remapping one instrument to another somehow. + */ + //printMessage(CMSG_ERROR, VERB_NORMAL, "FIXME: Implement \"map\" in TiMidity config.\n"); + } + + /* Standard TiMidity config */ + + else if (!strcmp(w[0], "dir")) + { + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No directory given\n", name, line); + return -2; + } + for (i = 1; i < words; i++) + { + // Q: How does this deal with relative paths? In this form it just does not work. + sfreader->add_search_path(w[i]); + } + } + else if (!strcmp(w[0], "source")) + { + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No file name given\n", name, line); + return -2; + } + for (i=1; i 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Drum set must be between 0 and 127\n", name, line); + return -2; + } + if (drumset[i] == NULL) + { + drumset[i] = new ToneBank; + } + bank = drumset[i]; + } + else if (!strcmp(w[0], "bank")) + { + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: No bank number given\n", name, line); + return -2; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Tone bank must be between 0 and 127\n", name, line); + return -2; + } + if (tonebank[i] == NULL) + { + tonebank[i] = new ToneBank; + } + bank = tonebank[i]; + } + else + { + if ((words < 2) || (*w[0] < '0' || *w[0] > '9')) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error\n", name, line); + return -2; + } + i = atoi(w[0]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Program must be between 0 and 127\n", name, line); + return -2; + } + if (bank == NULL) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: Must specify tone bank or drum set before assignment\n", name, line); + return -2; + } + bank->tone[i].note = bank->tone[i].pan = + bank->tone[i].fontbank = bank->tone[i].fontpreset = + bank->tone[i].fontnote = bank->tone[i].strip_loop = + bank->tone[i].strip_envelope = bank->tone[i].strip_tail = -1; + + if (!strcmp(w[1], "%font")) + { + bank->tone[i].name = w[2]; + bank->tone[i].fontbank = atoi(w[3]); + bank->tone[i].fontpreset = atoi(w[4]); + if (words > 5 && (bank->tone[i].fontbank == 128 || (w[5][0] >= '0' && w[5][0] <= '9'))) + { + bank->tone[i].fontnote = atoi(w[5]); + j = 6; + } + else + { + j = 5; + } + font_add(w[2], 254); + } + else + { + bank->tone[i].name = w[1]; + j = 2; + } + + for (; j '9')) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: note must be between 0 and 127\n", name, line); + return -2; + } + bank->tone[i].note = k; + } + else if (!strcmp(w[j], "pan")) + { + if (!strcmp(cp, "center")) + k = 64; + else if (!strcmp(cp, "left")) + k = 0; + else if (!strcmp(cp, "right")) + k = 127; + else + k = ((atoi(cp)+100) * 100) / 157; + if ((k < 0 || k > 127) || + (k == 0 && *cp != '-' && (*cp < '0' || *cp > '9'))) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: panning must be left, right, " + "center, or between -100 and 100\n", name, line); + return -2; + } + bank->tone[i].pan = k; + } + else if (!strcmp(w[j], "keep")) + { + if (!strcmp(cp, "env")) + bank->tone[i].strip_envelope = 0; + else if (!strcmp(cp, "loop")) + bank->tone[i].strip_loop = 0; + else + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: keep must be env or loop\n", name, line); + return -2; + } + } + else if (!strcmp(w[j], "strip")) + { + if (!strcmp(cp, "env")) + bank->tone[i].strip_envelope = 1; + else if (!strcmp(cp, "loop")) + bank->tone[i].strip_loop = 1; + else if (!strcmp(cp, "tail")) + bank->tone[i].strip_tail = 1; + else + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: strip must be env, loop, or tail\n", name, line); + return -2; + } + } + else + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: bad patch option %s\n", name, line, w[j]); + return -2; + } + } + } + } + return 0; +} + +static char* gets(const char *&input, const char *eof, char* strbuf, size_t len) +{ + if (ptrdiff_t(len) > eof - input) len = eof - input; + if (len <= 0) return nullptr; + + char* p = strbuf; + while (len > 1) + { + if (*input == 0) + { + input++; + break; + } + if (*input != '\r') + { + *p++ = *input; + len--; + if (*input == '\n') + { + input++; + break; + } + } + input++; + } + if (p == strbuf) return nullptr; + *p++ = 0; + return strbuf; +} + +int Instruments::LoadDMXGUS(int gus_memsize, const char* dmxgusdata, size_t dmxgussize) +{ + char readbuffer[1024]; + const char* eof = dmxgusdata + dmxgussize; + long read = 0; + uint8_t remap[256]; + + std::string patches[256]; + memset(remap, 255, sizeof(remap)); + char temp[16]; + int current = -1; + int status = -1; + int gusbank = (gus_memsize >= 1 && gus_memsize <= 4) ? gus_memsize : -1; + + while (gets(dmxgusdata, eof, readbuffer, 1024)) + { + int i = 0; + while (readbuffer[i] != 0 && i < 1024) + { + // Don't try to parse comments + if (readbuffer[i] == '#') break; + // Actively ignore spaces + else if (readbuffer[i] == ' ') {} + // Comma separate values + else if (status >= 0 && status <= 4 && readbuffer[i] == ',') + { + if (++status == gusbank) + { + remap[current] = 0; + } + } + // Status -1: outside of a line + // Status 0: reading patch value + else if (status == -1 && readbuffer[i] >= '0' && readbuffer[i] <= '9') + { + current = readbuffer[i] - '0'; + status = 0; + } + else if (status == 0 && readbuffer[i] >= '0' && readbuffer[i] <= '9') + { + current *= 10; + current += readbuffer[i] - '0'; + } + // Status 1 through 4: remaps (256K, 512K, 768K, and 1024K resp.) + else if (status == gusbank && readbuffer[i] >= '0' && readbuffer[i] <= '9') + { + remap[current] *= 10; + remap[current] += readbuffer[i] - '0'; + } + // Status 5: parsing patch name + else if (status == 5 && i < 1015) + { + memcpy(temp, readbuffer + i, 8); + for (int j = 0; j < 8; ++j) + { + if (temp[j] < 33) + { + temp[j] = 0; + break; + } + } + temp[8] = 0; + patches[current] = temp; + // Prepare to parse next line + status = -1; + break; + } + + ++i; + } + read += i; + if (i == 0) continue; + readbuffer[i-1] = 0; + } + + /* Some functions get aggravated if not even the standard banks are available. */ + if (tonebank[0] == NULL) + { + tonebank[0] = new ToneBank; + drumset[0] = new ToneBank; + } + + // From 0 to 127: tonebank[0]; from 128 to 255: drumset[0]. + ToneBank *bank = tonebank[0]; + for (int k = 0; k < 256; ++k) + { + int j = (gusbank > 0) ? remap[k] : k; + if (k == 128) bank = drumset[0]; + // No need to bother with things that don't exist + if (patches[j].length() == 0) + continue; + + int val = k % 128; + bank->tone[val].note = bank->tone[val].pan = + bank->tone[val].fontbank = bank->tone[val].fontpreset = + bank->tone[val].fontnote = bank->tone[val].strip_loop = + bank->tone[val].strip_envelope = bank->tone[val].strip_tail = -1; + bank->tone[val].name = patches[j]; + } + + return 0; +} + +void FreeDLS(DLS_Data *data); +Renderer::Renderer(float sample_rate, int voices_, Instruments *inst) +{ + int res = 0; + + instruments = inst; + + rate = sample_rate; + patches = NULL; + resample_buffer_size = 0; + resample_buffer = NULL; + voice = NULL; + adjust_panning_immediately = false; + + control_ratio = std::min(1, std::max(MAX_CONTROL_RATIO, int(rate / CONTROLS_PER_SECOND))); + + lost_notes = 0; + cut_notes = 0; + + default_instrument = NULL; + default_program = DEFAULT_PROGRAM; + if (inst->def_instr_name.length() > 0) + set_default_instrument(inst->def_instr_name.c_str()); + + voices = std::max(voices_, 16); + voice = new Voice[voices]; + drumchannels = DEFAULT_DRUMCHANNELS; +} + +Renderer::~Renderer() +{ + if (resample_buffer != NULL) + { + free(resample_buffer); + } + if (voice != NULL) + { + delete[] voice; + } + if (patches != NULL) + { + FreeDLS(patches); + } +} + +void Renderer::ComputeOutput(float *buffer, int count) +{ + // count is in samples, not bytes. + if (count <= 0) + { + return; + } + Voice *v = &voice[0]; + + memset(buffer, 0, sizeof(float)*count*2); // An integer 0 is also a float 0. + if (resample_buffer_size < count) + { + resample_buffer_size = count; + resample_buffer = (sample_t *)realloc(resample_buffer, count * sizeof(float) * 2); + } + for (int i = 0; i < voices; i++, v++) + { + if (v->status & VOICE_RUNNING) + { + mix_voice(this, buffer, v, count); + } + } +} + +void Renderer::MarkInstrument(int banknum, int percussion, int instr) +{ + ToneBank *bank; + + if (banknum >= MAXBANK) + { + return; + } + if (banknum != 0) + { + /* Mark the standard bank in case it's not defined by this one. */ + MarkInstrument(0, percussion, instr); + } + if (percussion) + { + bank = instruments->drumset[banknum]; + } + else + { + bank = instruments->tonebank[banknum]; + } + if (bank == NULL) + { + return; + } + if (bank->instrument[instr] == NULL) + { + bank->instrument[instr] = MAGIC_LOAD_INSTRUMENT; + } +} + + +static void default_cmsg(int type, int verbosity_level, const char* fmt, ...) +{ + if (verbosity_level >= VERB_NOISY) return; // Don't waste time on diagnostics. + + va_list args; + va_start(args, fmt); + ZMusic_Print(type, fmt, args); + va_end(args); +} + +// Allow hosting applications to capture the messages and deal with them themselves. +void (*printMessage)(int type, int verbosity_level, const char* fmt, ...) = default_cmsg; + + +} diff --git a/thirdparty/timidity/timidity/common.h b/thirdparty/timidity/timidity/common.h new file mode 100644 index 000000000..4f02f399d --- /dev/null +++ b/thirdparty/timidity/timidity/common.h @@ -0,0 +1,149 @@ +#pragma once + +namespace Timidity +{ +/* +config.h +*/ + +/* Acoustic Grand Piano seems to be the usual default instrument. */ +#define DEFAULT_PROGRAM 0 + +/* 9 here is MIDI channel 10, which is the standard percussion channel. + Some files (notably C:\WINDOWS\CANYON.MID) think that 16 is one too. + On the other hand, some files know that 16 is not a drum channel and + try to play music on it. This is now a runtime option, so this isn't + a critical choice anymore. */ +#define DEFAULT_DRUMCHANNELS (1<<9) +/*#define DEFAULT_DRUMCHANNELS ((1<<9) | (1<<15))*/ + +#define MAXCHAN 16 +#define MAXNOTE 128 + +/* 1000 here will give a control ratio of 22:1 with 22 kHz output. + Higher CONTROLS_PER_SECOND values allow more accurate rendering + of envelopes and tremolo. The cost is CPU time. */ +#define CONTROLS_PER_SECOND 1000 + +/* A scalar applied to the final mix to try and approximate the + volume level of FMOD's built-in MIDI player. */ +#define FINAL_MIX_SCALE 0.5 + +/* How many bits to use for the fractional part of sample positions. + This affects tonal accuracy. The entire position counter must fit + in 32 bits, so with FRACTION_BITS equal to 12, the maximum size of + a sample is 1048576 samples (2 megabytes in memory). The GUS gets + by with just 9 bits and a little help from its friends... + "The GUS does not SUCK!!!" -- a happy user :) */ +#define FRACTION_BITS 12 + +/* For some reason the sample volume is always set to maximum in all + patch files. Define this for a crude adjustment that may help + equalize instrument volumes. */ +//#define ADJUST_SAMPLE_VOLUMES + +/* The number of samples to use for ramping out a dying note. Affects + click removal. */ +#define MAX_DIE_TIME 20 + +/**************************************************************************/ +/* Anything below this shouldn't need to be changed unless you're porting + to a new machine with other than 32-bit, big-endian words. */ +/**************************************************************************/ + +/* change FRACTION_BITS above, not these */ +#define INTEGER_BITS (32 - FRACTION_BITS) +#define INTEGER_MASK (0xFFFFFFFF << FRACTION_BITS) +#define FRACTION_MASK (~ INTEGER_MASK) +#define MAX_SAMPLE_SIZE (1 << INTEGER_BITS) + +/* This is enforced by some computations that must fit in an int */ +#define MAX_CONTROL_RATIO 255 + +#define MAX_AMPLIFICATION 800 + +#define FINAL_VOLUME(v) (v) + +#define FSCALE(a,b) ((a) * (float)(1<<(b))) +#define FSCALENEG(a,b) ((a) * (1.0L / (float)(1<<(b)))) + +/* Vibrato and tremolo Choices of the Day */ +#define SWEEP_TUNING 38 +#define VIBRATO_AMPLITUDE_TUNING 1.0 +#define VIBRATO_RATE_TUNING 38 +#define TREMOLO_AMPLITUDE_TUNING 1.0 +#define TREMOLO_RATE_TUNING 38 + +#define SWEEP_SHIFT 16 +#define RATE_SHIFT 5 + +#ifndef PI + #define PI 3.14159265358979323846 +#endif + +#if defined(__GNUC__) && !defined(__clang__) && (defined(__i386__) || defined(__x86_64__)) +// [RH] MinGW's pow() function is terribly slow compared to VC8's +// (I suppose because it's using an old version from MSVCRT.DLL). +// On an Opteron running x86-64 Linux, this also ended up being about +// 100 cycles faster than libm's pow(), which is why I'm using this +// for GCC in general and not just for MinGW. +// [CE] Clang doesn't yet support some inline ASM operations so I disabled it for that instance + +extern __inline__ double pow_x87_inline(double x,double y) +{ + double result; + + if (y == 0) + { + return 1; + } + if (x == 0) + { + if (y > 0) + { + return 0; + } + else + { + union { double fp; long long ip; } infinity; + infinity.ip = 0x7FF0000000000000ll; + return infinity.fp; + } + } + __asm__ ( + "fyl2x\n\t" + "fld %%st(0)\n\t" + "frndint\n\t" + "fxch\n\t" + "fsub %%st(1),%%st(0)\n\t" + "f2xm1\n\t" + "fld1\n\t" + "faddp\n\t" + "fxch\n\t" + "fld1\n\t" + "fscale\n\t" + "fstp %%st(1)\n\t" + "fmulp\n\t" + : "=t" (result) + : "0" (x), "u" (y) + : "st(1)", "st(7)" ); + return result; +} +#define pow pow_x87_inline +#endif + +/* +common.h +*/ + +extern void *safe_malloc(size_t count); + +#ifndef MAKE_ID +#ifndef __BIG_ENDIAN__ +#define MAKE_ID(a,b,c,d) ((uint32_t)((a)|((b)<<8)|((c)<<16)|((d)<<24))) +#else +#define MAKE_ID(a,b,c,d) ((uint32_t)((d)|((c)<<8)|((b)<<16)|((a)<<24))) +#endif +#endif + +} diff --git a/thirdparty/timidity/timidity/dls1.h b/thirdparty/timidity/timidity/dls1.h new file mode 100644 index 000000000..86eccffc9 --- /dev/null +++ b/thirdparty/timidity/timidity/dls1.h @@ -0,0 +1,266 @@ +/*==========================================================================; +// +// dls1.h +// +// +// Description: +// +// Interface defines and structures for the Instrument Collection Form +// RIFF DLS. +// +// +// Written by Sonic Foundry 1996. Released for public use. +// +//=========================================================================*/ + +#ifndef _INC_DLS1 +#define _INC_DLS1 + +/*////////////////////////////////////////////////////////////////////////// +// +// +// Layout of an instrument collection: +// +// +// RIFF [] 'DLS ' [dlid,colh,INSTLIST,WAVEPOOL,INFOLIST] +// +// INSTLIST +// LIST [] 'lins' +// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST] +// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST] +// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST] +// +// RGNLIST +// LIST [] 'lrgn' +// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST] +// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST] +// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST] +// +// ARTLIST +// LIST [] 'lart' +// 'art1' level 1 Articulation connection graph +// 'art2' level 2 Articulation connection graph +// '3rd1' Possible 3rd party articulation structure 1 +// '3rd2' Possible 3rd party articulation structure 2 .... and so on +// +// WAVEPOOL +// ptbl [] [pool table] +// LIST [] 'wvpl' +// [path], +// [path], +// LIST [] 'wave' [dlid,RIFFWAVE] +// LIST [] 'wave' [dlid,RIFFWAVE] +// LIST [] 'wave' [dlid,RIFFWAVE] +// LIST [] 'wave' [dlid,RIFFWAVE] +// LIST [] 'wave' [dlid,RIFFWAVE] +// +// INFOLIST +// LIST [] 'INFO' +// 'icmt' 'One of those crazy comments.' +// 'icop' 'Copyright (C) 1996 Sonic Foundry' +// +/////////////////////////////////////////////////////////////////////////*/ + + +/*///////////////////////////////////////////////////////////////////////// +// FOURCC's used in the DLS file +/////////////////////////////////////////////////////////////////////////*/ + +#define FOURCC_DLS mmioFOURCC('D','L','S',' ') +#define FOURCC_DLID mmioFOURCC('d','l','i','d') +#define FOURCC_COLH mmioFOURCC('c','o','l','h') +#define FOURCC_WVPL mmioFOURCC('w','v','p','l') +#define FOURCC_PTBL mmioFOURCC('p','t','b','l') +#define FOURCC_PATH mmioFOURCC('p','a','t','h') +#define FOURCC_wave mmioFOURCC('w','a','v','e') +#define FOURCC_LINS mmioFOURCC('l','i','n','s') +#define FOURCC_INS mmioFOURCC('i','n','s',' ') +#define FOURCC_INSH mmioFOURCC('i','n','s','h') +#define FOURCC_LRGN mmioFOURCC('l','r','g','n') +#define FOURCC_RGN mmioFOURCC('r','g','n',' ') +#define FOURCC_RGNH mmioFOURCC('r','g','n','h') +#define FOURCC_LART mmioFOURCC('l','a','r','t') +#define FOURCC_ART1 mmioFOURCC('a','r','t','1') +#define FOURCC_WLNK mmioFOURCC('w','l','n','k') +#define FOURCC_WSMP mmioFOURCC('w','s','m','p') +#define FOURCC_VERS mmioFOURCC('v','e','r','s') + +/*///////////////////////////////////////////////////////////////////////// +// Articulation connection graph definitions +/////////////////////////////////////////////////////////////////////////*/ + +/* Generic Sources */ +#define CONN_SRC_NONE 0x0000 +#define CONN_SRC_LFO 0x0001 +#define CONN_SRC_KEYONVELOCITY 0x0002 +#define CONN_SRC_KEYNUMBER 0x0003 +#define CONN_SRC_EG1 0x0004 +#define CONN_SRC_EG2 0x0005 +#define CONN_SRC_PITCHWHEEL 0x0006 + +/* Midi Controllers 0-127 */ +#define CONN_SRC_CC1 0x0081 +#define CONN_SRC_CC7 0x0087 +#define CONN_SRC_CC10 0x008a +#define CONN_SRC_CC11 0x008b + +/* Generic Destinations */ +#define CONN_DST_NONE 0x0000 +#define CONN_DST_ATTENUATION 0x0001 +#define CONN_DST_PITCH 0x0003 +#define CONN_DST_PAN 0x0004 + +/* LFO Destinations */ +#define CONN_DST_LFO_FREQUENCY 0x0104 +#define CONN_DST_LFO_STARTDELAY 0x0105 + +/* EG1 Destinations */ +#define CONN_DST_EG1_ATTACKTIME 0x0206 +#define CONN_DST_EG1_DECAYTIME 0x0207 +#define CONN_DST_EG1_RELEASETIME 0x0209 +#define CONN_DST_EG1_SUSTAINLEVEL 0x020a + +/* EG2 Destinations */ +#define CONN_DST_EG2_ATTACKTIME 0x030a +#define CONN_DST_EG2_DECAYTIME 0x030b +#define CONN_DST_EG2_RELEASETIME 0x030d +#define CONN_DST_EG2_SUSTAINLEVEL 0x030e + +#define CONN_TRN_NONE 0x0000 +#define CONN_TRN_CONCAVE 0x0001 + +typedef struct _DLSID { + ULONG ulData1; + USHORT usData2; + USHORT usData3; + uint8_t abData4[8]; +} DLSID, FAR *LPDLSID; + +typedef struct _DLSVERSION { + DWORD dwVersionMS; + DWORD dwVersionLS; +} DLSVERSION, FAR *LPDLSVERSION; + + +typedef struct _CONNECTION { + USHORT usSource; + USHORT usControl; + USHORT usDestination; + USHORT usTransform; + LONG lScale; +} CONNECTION, FAR *LPCONNECTION; + + +/* Level 1 Articulation Data */ + +typedef struct _CONNECTIONLIST { + ULONG cbSize; /* size of the connection list structure */ + ULONG cConnections; /* count of connections in the list */ +} CONNECTIONLIST, FAR *LPCONNECTIONLIST; + + + +/*///////////////////////////////////////////////////////////////////////// +// Generic type defines for regions and instruments +/////////////////////////////////////////////////////////////////////////*/ + +typedef struct _RGNRANGE { + USHORT usLow; + USHORT usHigh; +} RGNRANGE, FAR * LPRGNRANGE; + +#define F_INSTRUMENT_DRUMS 0x80000000 + +typedef struct _MIDILOCALE { + ULONG ulBank; + ULONG ulInstrument; +} MIDILOCALE, FAR *LPMIDILOCALE; + +/*///////////////////////////////////////////////////////////////////////// +// Header structures found in an DLS file for collection, instruments, and +// regions. +/////////////////////////////////////////////////////////////////////////*/ + +#define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001 + +typedef struct _RGNHEADER { + RGNRANGE RangeKey; /* Key range */ + RGNRANGE RangeVelocity; /* Velocity Range */ + USHORT fusOptions; /* Synthesis options for this range */ + USHORT usKeyGroup; /* Key grouping for non simultaneous play */ + /* 0 = no group, 1 up is group */ + /* for Level 1 only groups 1-15 are allowed */ +} RGNHEADER, FAR *LPRGNHEADER; + +typedef struct _INSTHEADER { + ULONG cRegions; /* Count of regions in this instrument */ + MIDILOCALE Locale; /* Intended MIDI locale of this instrument */ +} INSTHEADER, FAR *LPINSTHEADER; + +typedef struct _DLSHEADER { + ULONG cInstruments; /* Count of instruments in the collection */ +} DLSHEADER, FAR *LPDLSHEADER; + +/*//////////////////////////////////////////////////////////////////////////// +// definitions for the Wave link structure +////////////////////////////////////////////////////////////////////////////*/ + +/* **** For level 1 only WAVELINK_CHANNEL_MONO is valid **** */ +/* ulChannel allows for up to 32 channels of audio with each bit position */ +/* specifiying a channel of playback */ + +#define WAVELINK_CHANNEL_LEFT 0x0001l +#define WAVELINK_CHANNEL_RIGHT 0x0002l + +#define F_WAVELINK_PHASE_MASTER 0x0001 + +typedef struct _WAVELINK { /* any paths or links are stored right after struct */ + USHORT fusOptions; /* options flags for this wave */ + USHORT usPhaseGroup; /* Phase grouping for locking channels */ + ULONG ulChannel; /* channel placement */ + ULONG ulTableIndex; /* index into the wave pool table, 0 based */ +} WAVELINK, FAR *LPWAVELINK; + +#define POOL_CUE_NULL 0xffffffffl + +typedef struct _POOLCUE { + ULONG ulOffset; /* Offset to the entry in the list */ +} POOLCUE, FAR *LPPOOLCUE; + +typedef struct _POOLTABLE { + ULONG cbSize; /* size of the pool table structure */ + ULONG cCues; /* count of cues in the list */ +} POOLTABLE, FAR *LPPOOLTABLE; + +/*//////////////////////////////////////////////////////////////////////////// +// Structures for the "wsmp" chunk +////////////////////////////////////////////////////////////////////////////*/ + +#define F_WSMP_NO_TRUNCATION 0x0001l +#define F_WSMP_NO_COMPRESSION 0x0002l + + +typedef struct _rwsmp { + ULONG cbSize; + USHORT usUnityNote; /* MIDI Unity Playback Note */ + SHORT sFineTune; /* Fine Tune in log tuning */ + LONG lAttenuation; /* Overall Attenuation to be applied to data */ + ULONG fulOptions; /* Flag options */ + ULONG cSampleLoops; /* Count of Sample loops, 0 loops is one shot */ +} WSMPL, FAR *LPWSMPL; + + +/* This loop type is a normal forward playing loop which is continually */ +/* played until the envelope reaches an off threshold in the release */ +/* portion of the volume envelope */ + +#define WLOOP_TYPE_FORWARD 0 + +typedef struct _rloop { + ULONG cbSize; + ULONG ulType; /* Loop Type */ + ULONG ulStart; /* Start of loop in samples */ + ULONG ulLength; /* Length of loop in samples */ +} WLOOP, FAR *LPWLOOP; + +#endif /*_INC_DLS1 */ diff --git a/thirdparty/timidity/timidity/dls2.h b/thirdparty/timidity/timidity/dls2.h new file mode 100644 index 000000000..30cec23a2 --- /dev/null +++ b/thirdparty/timidity/timidity/dls2.h @@ -0,0 +1,130 @@ +/* + + dls2.h + + Description: + + Interface defines and structures for the DLS2 extensions of DLS. + + + Written by Microsoft 1998. Released for public use. + +*/ + +#ifndef _INC_DLS2 +#define _INC_DLS2 + +/* + FOURCC's used in the DLS2 file, in addition to DLS1 chunks +*/ + +#define FOURCC_RGN2 mmioFOURCC('r','g','n','2') +#define FOURCC_LAR2 mmioFOURCC('l','a','r','2') +#define FOURCC_ART2 mmioFOURCC('a','r','t','2') +#define FOURCC_CDL mmioFOURCC('c','d','l',' ') +#define FOURCC_DLID mmioFOURCC('d','l','i','d') + +/* + Articulation connection graph definitions. These are in addition to + the definitions in the DLS1 header. +*/ + +/* Generic Sources (in addition to DLS1 sources. */ +#define CONN_SRC_POLYPRESSURE 0x0007 /* Polyphonic Pressure */ +#define CONN_SRC_CHANNELPRESSURE 0x0008 /* Channel Pressure */ +#define CONN_SRC_VIBRATO 0x0009 /* Vibrato LFO */ +#define CONN_SRC_MONOPRESSURE 0x000a /* MIDI Mono pressure */ + + +/* Midi Controllers */ +#define CONN_SRC_CC91 0x00db /* Reverb Send */ +#define CONN_SRC_CC93 0x00dd /* Chorus Send */ + + +/* Generic Destinations */ +#define CONN_DST_GAIN 0x0001 /* Same as CONN_DST_ ATTENUATION, but more appropriate terminology. */ +#define CONN_DST_KEYNUMBER 0x0005 /* Key Number Generator */ + +/* Audio Channel Output Destinations */ +#define CONN_DST_LEFT 0x0010 /* Left Channel Send */ +#define CONN_DST_RIGHT 0x0011 /* Right Channel Send */ +#define CONN_DST_CENTER 0x0012 /* Center Channel Send */ +#define CONN_DST_LEFTREAR 0x0013 /* Left Rear Channel Send */ +#define CONN_DST_RIGHTREAR 0x0014 /* Right Rear Channel Send */ +#define CONN_DST_LFE_CHANNEL 0x0015 /* LFE Channel Send */ +#define CONN_DST_CHORUS 0x0080 /* Chorus Send */ +#define CONN_DST_REVERB 0x0081 /* Reverb Send */ + +/* Vibrato LFO Destinations */ +#define CONN_DST_VIB_FREQUENCY 0x0114 /* Vibrato Frequency */ +#define CONN_DST_VIB_STARTDELAY 0x0115 /* Vibrato Start Delay */ + +/* EG1 Destinations */ +#define CONN_DST_EG1_DELAYTIME 0x020B /* EG1 Delay Time */ +#define CONN_DST_EG1_HOLDTIME 0x020C /* EG1 Hold Time */ +#define CONN_DST_EG1_SHUTDOWNTIME 0x020D /* EG1 Shutdown Time */ + + +/* EG2 Destinations */ +#define CONN_DST_EG2_DELAYTIME 0x030F /* EG2 Delay Time */ +#define CONN_DST_EG2_HOLDTIME 0x0310 /* EG2 Hold Time */ + + +/* Filter Destinations */ +#define CONN_DST_FILTER_CUTOFF 0x0500 /* Filter Cutoff Frequency */ +#define CONN_DST_FILTER_Q 0x0501 /* Filter Resonance */ + + +/* Transforms */ +#define CONN_TRN_CONVEX 0x0002 /* Convex Transform */ +#define CONN_TRN_SWITCH 0x0003 /* Switch Transform */ + + +/* Conditional chunk operators */ + #define DLS_CDL_AND 0x0001 /* X = X & Y */ + #define DLS_CDL_OR 0x0002 /* X = X | Y */ + #define DLS_CDL_XOR 0x0003 /* X = X ^ Y */ + #define DLS_CDL_ADD 0x0004 /* X = X + Y */ + #define DLS_CDL_SUBTRACT 0x0005 /* X = X - Y */ + #define DLS_CDL_MULTIPLY 0x0006 /* X = X * Y */ + #define DLS_CDL_DIVIDE 0x0007 /* X = X / Y */ + #define DLS_CDL_LOGICAL_AND 0x0008 /* X = X && Y */ + #define DLS_CDL_LOGICAL_OR 0x0009 /* X = X || Y */ + #define DLS_CDL_LT 0x000A /* X = (X < Y) */ + #define DLS_CDL_LE 0x000B /* X = (X <= Y) */ + #define DLS_CDL_GT 0x000C /* X = (X > Y) */ + #define DLS_CDL_GE 0x000D /* X = (X >= Y) */ + #define DLS_CDL_EQ 0x000E /* X = (X == Y) */ + #define DLS_CDL_NOT 0x000F /* X = !X */ + #define DLS_CDL_CONST 0x0010 /* 32-bit constant */ + #define DLS_CDL_QUERY 0x0011 /* 32-bit value returned from query */ + #define DLS_CDL_QUERYSUPPORTED 0x0012 /* Test to see if query is supported by synth */ + +/* + Loop and release +*/ + +#define WLOOP_TYPE_RELEASE 1 + +/* + WaveLink chunk +*/ + +#define F_WAVELINK_MULTICHANNEL 0x0002 + + +/* + DLSID queries for +*/ + +DEFINE_GUID(DLSID_GMInHardware, 0x178f2f24, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12); +DEFINE_GUID(DLSID_GSInHardware, 0x178f2f25, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12); +DEFINE_GUID(DLSID_XGInHardware, 0x178f2f26, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12); +DEFINE_GUID(DLSID_SupportsDLS1, 0x178f2f27, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12); +DEFINE_GUID(DLSID_SupportsDLS2, 0xf14599e5, 0x4689, 0x11d2, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6); +DEFINE_GUID(DLSID_SampleMemorySize, 0x178f2f28, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12); +DEFINE_GUID(DLSID_ManufacturersID, 0xb03e1181, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8); +DEFINE_GUID(DLSID_ProductID, 0xb03e1182, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8); +DEFINE_GUID(DLSID_SamplePlaybackRate, 0x2a91f713, 0xa4bf, 0x11d2, 0xbb, 0xdf, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8); + +#endif /* _INC_DLS2 */ diff --git a/thirdparty/timidity/timidity/gf1patch.h b/thirdparty/timidity/timidity/gf1patch.h new file mode 100644 index 000000000..9b1800799 --- /dev/null +++ b/thirdparty/timidity/timidity/gf1patch.h @@ -0,0 +1,85 @@ +/* GF1 Patch definition: */ +enum +{ + HEADER_SIZE = 12, + ID_SIZE = 10, + DESC_SIZE = 60, + RESERVED_SIZE = 40, + PATCH_HEADER_RESERVED_SIZE = 36, + LAYER_RESERVED_SIZE = 40, + PATCH_DATA_RESERVED_SIZE = 36, + INST_NAME_SIZE = 16, + ENVELOPES = 6, + MAX_LAYERS = 4 +}; +#define GF1_HEADER_TEXT "GF1PATCH110" + +#ifdef _MSC_VER +#pragma pack(push, 1) +#define GCC_PACKED +#else +#define GCC_PACKED __attribute__((__packed__)) +#endif + +struct GF1PatchHeader +{ + char Header[HEADER_SIZE]; + char GravisID[ID_SIZE]; /* Id = "ID#000002" */ + char Description[DESC_SIZE]; + uint8_t Instruments; + uint8_t Voices; + uint8_t Channels; + uint16_t WaveForms; + uint16_t MasterVolume; + uint32_t DataSize; + uint8_t Reserved[PATCH_HEADER_RESERVED_SIZE]; +} GCC_PACKED; + +struct GF1InstrumentData +{ + uint16_t Instrument; + char InstrumentName[INST_NAME_SIZE]; + int InstrumentSize; + uint8_t Layers; + uint8_t Reserved[RESERVED_SIZE]; +} GCC_PACKED; + +struct GF1LayerData +{ + uint8_t LayerDuplicate; + uint8_t Layer; + int LayerSize; + uint8_t Samples; + uint8_t Reserved[LAYER_RESERVED_SIZE]; +} GCC_PACKED; + +struct GF1PatchData +{ + char WaveName[7]; + uint8_t Fractions; + int WaveSize; + int StartLoop; + int EndLoop; + uint16_t SampleRate; + int LowFrequency; + int HighFrequency; + int RootFrequency; + int16_t Tune; + uint8_t Balance; + uint8_t EnvelopeRate[ENVELOPES]; + uint8_t EnvelopeOffset[ENVELOPES]; + uint8_t TremoloSweep; + uint8_t TremoloRate; + uint8_t TremoloDepth; + uint8_t VibratoSweep; + uint8_t VibratoRate; + uint8_t VibratoDepth; + uint8_t Modes; + int16_t ScaleFrequency; + uint16_t ScaleFactor; /* From 0 to 2048 or 0 to 2 */ + uint8_t Reserved[PATCH_DATA_RESERVED_SIZE]; +} GCC_PACKED; +#ifdef _MSC_VER +#pragma pack(pop) +#endif +#undef GCC_PACKED diff --git a/thirdparty/timidity/timidity/instrum.h b/thirdparty/timidity/timidity/instrum.h new file mode 100644 index 000000000..8677260a9 --- /dev/null +++ b/thirdparty/timidity/timidity/instrum.h @@ -0,0 +1,197 @@ +#pragma once + +#include "../../../source/zmusic/fileio.h" + +namespace Timidity +{ +/* +instrum.h +*/ + +enum +{ + PATCH_16 = (1<<0), + PATCH_UNSIGNED = (1<<1), + PATCH_LOOPEN = (1<<2), + PATCH_BIDIR = (1<<3), + PATCH_BACKWARD = (1<<4), + PATCH_SUSTAIN = (1<<5), + PATCH_NO_SRELEASE = (1<<6), + PATCH_FAST_REL = (1<<7), +}; + +struct Sample +{ + int32_t + loop_start, loop_end, data_length, + sample_rate; + float + low_freq, high_freq, root_freq; + union + { + struct + { + uint8_t rate[6], offset[6]; + } gf1; + struct + { + short delay_vol; + short attack_vol; + short hold_vol; + short decay_vol; + short sustain_vol; + short release_vol; + } sf2; + } envelope; + sample_t *data; + int32_t + tremolo_sweep_increment, tremolo_phase_increment, + vibrato_sweep_increment, vibrato_control_ratio; + uint8_t + tremolo_depth, vibrato_depth, + low_vel, high_vel, + type; + uint16_t + modes; + int16_t + panning; + uint16_t + scale_factor, key_group; + int16_t + scale_note; + bool + self_nonexclusive; + float + left_offset, right_offset; + + // SF2 stuff + int16_t tune; + int8_t velocity; + + float initial_attenuation; +}; + + +/* Magic file words */ + +#define ID_RIFF MAKE_ID('R','I','F','F') +#define ID_LIST MAKE_ID('L','I','S','T') +#define ID_INFO MAKE_ID('I','N','F','O') +#define ID_sfbk MAKE_ID('s','f','b','k') +#define ID_sdta MAKE_ID('s','d','t','a') +#define ID_pdta MAKE_ID('p','d','t','a') +#define ID_ifil MAKE_ID('i','f','i','l') +#define ID_iver MAKE_ID('i','v','e','r') +#define ID_irom MAKE_ID('i','r','o','m') +#define ID_smpl MAKE_ID('s','m','p','l') +#define ID_sm24 MAKE_ID('s','m','2','4') +#define ID_phdr MAKE_ID('p','h','d','r') +#define ID_pbag MAKE_ID('p','b','a','g') +#define ID_pmod MAKE_ID('p','m','o','d') +#define ID_pgen MAKE_ID('p','g','e','n') +#define ID_inst MAKE_ID('i','n','s','t') +#define ID_ibag MAKE_ID('i','b','a','g') +#define ID_imod MAKE_ID('i','m','o','d') +#define ID_igen MAKE_ID('i','g','e','n') +#define ID_shdr MAKE_ID('s','h','d','r') + +/* Instrument definitions */ + +enum +{ + INST_GUS, + INST_DLS, + INST_SF2 +}; + +struct Instrument +{ + Instrument(); + ~Instrument(); + + int samples; + Sample *sample; +}; + +struct ToneBankElement +{ + ToneBankElement() : + note(0), pan(0), strip_loop(0), strip_envelope(0), strip_tail(0) + {} + + std::string name; + int note, pan, fontbank, fontpreset, fontnote; + int8_t strip_loop, strip_envelope, strip_tail; +}; + +/* A hack to delay instrument loading until after reading the entire MIDI file. */ +#define MAGIC_LOAD_INSTRUMENT ((Instrument *)(-1)) + +enum +{ + MAXPROG = 128, + MAXBANK = 128, + SPECIAL_PROGRAM = -1 +}; + +struct ToneBank +{ + ToneBank(); + ~ToneBank(); + + ToneBankElement *tone; + Instrument *instrument[MAXPROG]; +}; + + + +/* +instrum_font.cpp +*/ + +class FontFile +{ +public: + FontFile(const char *filename); + virtual ~FontFile(); + + std::string Filename; + FontFile *Next; + + virtual Instrument *LoadInstrument(struct Renderer *song, int drum, int bank, int program) = 0; + virtual Instrument *LoadInstrumentOrder(struct Renderer *song, int order, int drum, int bank, int program) = 0; + virtual void SetOrder(int order, int drum, int bank, int program) = 0; + virtual void SetAllOrders(int order) = 0; +}; + +class Instruments +{ +public: + MusicIO::SoundFontReaderInterface *sfreader; + ToneBank* tonebank[MAXBANK] = {}; + ToneBank* drumset[MAXBANK] = {}; + FontFile* Fonts = nullptr; + std::string def_instr_name; + + Instruments(MusicIO::SoundFontReaderInterface* reader); + ~Instruments(); + + int LoadConfig() { return read_config_file(nullptr); } + int read_config_file(const char* name); + int LoadDMXGUS(int gus_memsize, const char *dmxgusdata, size_t dmxgussize); + + void font_freeall(); + FontFile* font_find(const char* filename); + void font_add(const char* filename, int load_order); + void font_remove(const char* filename); + void font_order(int order, int bank, int preset, int keynote); + void convert_sample_data(Sample* sample, const void* data); + void free_instruments(); + + FontFile* ReadDLS(const char* filename, timidity_file* f); + +}; + +void convert_sample_data(Sample* sp, const void* data); + +} diff --git a/thirdparty/timidity/timidity/playmidi.h b/thirdparty/timidity/timidity/playmidi.h new file mode 100644 index 000000000..3cb605e12 --- /dev/null +++ b/thirdparty/timidity/timidity/playmidi.h @@ -0,0 +1,122 @@ +#pragma once + +namespace Timidity +{ +/* +mix.h +*/ + +extern void mix_voice(struct Renderer *song, float *buf, struct Voice *v, int c); +extern int recompute_envelope(struct Voice *v); +extern void apply_envelope_to_amp(struct Voice *v); + +/* +playmidi.h +*/ + +/* Midi events */ +enum +{ + ME_NOTEOFF = 0x80, + ME_NOTEON = 0x90, + ME_KEYPRESSURE = 0xA0, + ME_CONTROLCHANGE = 0xB0, + ME_PROGRAM = 0xC0, + ME_CHANNELPRESSURE = 0xD0, + ME_PITCHWHEEL = 0xE0 +}; + +/* Controllers */ +enum +{ + CTRL_BANK_SELECT = 0, + CTRL_DATA_ENTRY = 6, + CTRL_VOLUME = 7, + CTRL_PAN = 10, + CTRL_EXPRESSION = 11, + CTRL_SUSTAIN = 64, + CTRL_HARMONICCONTENT = 71, + CTRL_RELEASETIME = 72, + CTRL_ATTACKTIME = 73, + CTRL_BRIGHTNESS = 74, + CTRL_REVERBERATION = 91, + CTRL_CHORUSDEPTH = 93, + CTRL_NRPN_LSB = 98, + CTRL_NRPN_MSB = 99, + CTRL_RPN_LSB = 100, + CTRL_RPN_MSB = 101, + CTRL_ALL_SOUNDS_OFF = 120, + CTRL_RESET_CONTROLLERS = 121, + CTRL_ALL_NOTES_OFF = 123 +}; + +/* RPNs */ +enum +{ + RPN_PITCH_SENS = 0x0000, + RPN_FINE_TUNING = 0x0001, + RPN_COARSE_TUNING = 0x0002, + RPN_RESET = 0x3fff +}; + + +/* Causes the instrument's default panning to be used. */ +#define NO_PANNING -1 + + +/* Voice status options: */ +enum +{ + VOICE_RUNNING = (1<<0), + VOICE_SUSTAINING = (1<<1), + VOICE_RELEASING = (1<<2), + VOICE_STOPPING = (1<<3), + + VOICE_LPE = (1<<4), + NOTE_SUSTAIN = (1<<5), +}; + +/* Envelope stages: */ +enum +{ + GF1_ATTACK, + GF1_HOLD, + GF1_DECAY, + GF1_RELEASE, + GF1_RELEASEB, + GF1_RELEASEC +}; + +enum +{ + SF2_DELAY, + SF2_ATTACK, + SF2_HOLD, + SF2_DECAY, + SF2_SUSTAIN, + SF2_RELEASE, + SF2_FINISHED +}; + +#define ISDRUMCHANNEL(c) ((drumchannels & (1<<(c)))) + +/* +resample.h +*/ + +extern sample_t *resample_voice(struct Renderer *song, Voice *v, int *countptr); +extern void pre_resample(struct Renderer *song, Sample *sp); + +/* +tables.h +*/ + +const double log_of_2 = 0.69314718055994529; + +#define sine(x) (sin((2*PI/1024.0) * (x))) + +#define note_to_freq(x) (float(8175.7989473096690661233836992789 * pow(2.0, (x) / 12.0))) +#define freq_to_note(x) (log((x) / 8175.7989473096690661233836992789) * (12.0 / log_of_2)) + +#define calc_gf1_amp(x) (pow(2.0,((x)*16.0 - 16.0))) // Actual GUS equation +} diff --git a/thirdparty/timidity/timidity/sf2.h b/thirdparty/timidity/timidity/sf2.h new file mode 100644 index 000000000..b410635a4 --- /dev/null +++ b/thirdparty/timidity/timidity/sf2.h @@ -0,0 +1,327 @@ +#pragma once +#include +#include "../../../source/zmusic/fileio.h" + +namespace Timidity +{ +typedef uint16_t SFGenerator; + +using timidity_file = MusicIO::FileInterface; + +struct SFRange +{ + uint8_t Lo; + uint8_t Hi; +}; + +struct SFPreset +{ + char Name[21]; + uint8_t LoadOrder:7; + uint8_t bHasGlobalZone:1; + uint16_t Program; + uint16_t Bank; + uint16_t BagIndex; + /* Don't care about library, genre, and morphology */ +}; + +struct SFBag +{ + uint16_t GenIndex; +// uint16_t ModIndex; // If I am feeling ambitious, I might add support for modulators some day. + SFRange KeyRange; + SFRange VelRange; + int Target; // Either an instrument or sample index +}; + +struct SFInst +{ + char Name[21]; + uint8_t Pad:7; + uint8_t bHasGlobalZone:1; + uint16_t BagIndex; +}; + +struct SFSample +{ + float *InMemoryData; + uint32_t Start; + uint32_t End; + uint32_t StartLoop; + uint32_t EndLoop; + uint32_t SampleRate; + uint8_t OriginalPitch; + int8_t PitchCorrection; + uint16_t SampleLink; + uint16_t SampleType; + char Name[21]; +}; + +// Sample type bit fields (all but ROM are mutually exclusive) +enum +{ + SFST_Mono = 1, + SFST_Right = 2, + SFST_Left = 4, + SFST_Linked = 8, /* SF2.04 defines this bit but not its function */ + SFST_Bad = 16384, /* Used internally */ + SFST_ROM = 32768 +}; + +// Generator definitions + +struct SFGenList +{ + SFGenerator Oper; + union + { + SFRange Range; + int16_t Amount; + uint16_t uAmount; + }; +}; + +enum +{ + GEN_startAddrsOffset, + GEN_endAddrsOffset, + GEN_startloopAddrsOffset, + GEN_endloopAddrsOffset, + GEN_startAddrsCoarseOffset, + GEN_modLfoToPitch, + GEN_vibLfoToPitch, + GEN_modEnvToPitch, + GEN_initialFilterFC, + GEN_initialFilterQ, + GEN_modLfoToFilterFc, + GEN_modEnvToFilterFc, + GEN_endAddrsCoarseOffset, + GEN_modLfoToVolume, + GEN_unused1, + GEN_chorusEffectsSend, + GEN_reverbEffectsSend, + GEN_pan, + GEN_unused2, + GEN_unused3, + GEN_unused4, + GEN_delayModLFO, + GEN_freqModLFO, + GEN_delayVibLFO, + GEN_freqVibLFO, + GEN_delayModEnv, + GEN_attackModEnv, + GEN_holdModEnv, + GEN_decayModEnv, + GEN_sustainModEnv, + GEN_releaseModEnv, + GEN_keynumToModEnvHold, + GEN_keynumToModEnvDecay, + GEN_delayVolEnv, + GEN_attackVolEnv, + GEN_holdVolEnv, + GEN_decayVolEnv, + GEN_sustainVolEnv, + GEN_releaseVolEnv, + GEN_keynumToVolEnvHold, + GEN_keynumToVolEnvDecay, + GEN_instrument, + GEN_reserved1, + GEN_keyRange, + GEN_velRange, + GEN_startloopAddrsCoarseOffset, + GEN_keynum, + GEN_velocity, + GEN_initialAttenuation, + GEN_reserved2, + GEN_endloopAddrsCoarseOffset, + GEN_coarseTune, + GEN_fineTune, + GEN_sampleID, + GEN_sampleModes, + GEN_reserved3, + GEN_scaleTuning, + GEN_exclusiveClass, + GEN_overridingRootKey, + + GEN_NumGenerators +}; + +// Modulator definitions + +struct SFModulator +{ + uint16_t Index:7; + uint16_t CC:1; + uint16_t Dir:1; /* 0 = min->max, 1 = max->min */ + uint16_t Polarity:1; /* 0 = unipolar, 1 = bipolar */ + uint16_t Type:6; +}; + +struct SFModList +{ + SFModulator SrcOper; + SFGenerator DestOper; + int16_t Amount; + SFModulator AmtSrcOper; + uint16_t Transform; +}; + +// Modulator sources when CC is 0 + +enum +{ + SFMod_One = 0, // Pseudo-controller that always has the value 1 + SFMod_NoteVelocity = 2, + SFMod_KeyNumber = 3, + SFMod_PolyPressure = 10, + SFMod_ChannelPressure = 13, + SFMod_PitchWheel = 14, + SFMod_PitchSens = 16, + SFMod_Link = 127 +}; + +// Modulator types + +enum +{ + SFModType_Linear, + SFModType_Concave, // log(fabs(value)/(max value)^2) + SFModType_Convex, + SFModType_Switch +}; + +// Modulator transforms + +enum +{ + SFModTrans_Linear = 0, + SFModTrans_Abs = 2 +}; + +// All possible generators in a single structure + +struct SFGenComposite +{ + union + { + SFRange keyRange; // For normal use + struct // For intermediate percussion use + { + uint8_t drumset; + uint8_t key; + }; + }; + SFRange velRange; + union + { + uint16_t instrument; // At preset level + uint16_t sampleID; // At instrument level + }; + int16_t modLfoToPitch; + int16_t vibLfoToPitch; + int16_t modEnvToPitch; + int16_t initialFilterFc; + int16_t initialFilterQ; + int16_t modLfoToFilterFc; + int16_t modEnvToFilterFc; + int16_t modLfoToVolume; + int16_t chorusEffectsSend; + int16_t reverbEffectsSend; + int16_t pan; + int16_t delayModLFO; + int16_t freqModLFO; + int16_t delayVibLFO; + int16_t freqVibLFO; + int16_t delayModEnv; + int16_t attackModEnv; + int16_t holdModEnv; + int16_t decayModEnv; + int16_t sustainModEnv; + int16_t releaseModEnv; + int16_t keynumToModEnvHold; + int16_t keynumToModEnvDecay; + int16_t delayVolEnv; + int16_t attackVolEnv; + int16_t holdVolEnv; + int16_t decayVolEnv; + int16_t sustainVolEnv; + int16_t releaseVolEnv; + int16_t keynumToVolEnvHold; + int16_t keynumToVolEnvDecay; + int16_t initialAttenuation; + int16_t coarseTune; + int16_t fineTune; + int16_t scaleTuning; + + // The following are only for instruments: + int16_t startAddrsOffset, startAddrsCoarseOffset; + int16_t endAddrsOffset, endAddrsCoarseOffset; + int16_t startLoopAddrsOffset, startLoopAddrsCoarseOffset; + int16_t endLoopAddrsOffset, endLoopAddrsCoarseOffset; + int16_t keynum; + int16_t velocity; + uint16_t sampleModes; + int16_t exclusiveClass; + int16_t overridingRootKey; +}; + +// Intermediate percussion representation + +struct SFPerc +{ + SFPreset *Preset; + SFGenComposite Generators; + uint8_t LoadOrder; +}; + +// Container for all parameters from a SoundFont file + +struct SFFile : public FontFile +{ + SFFile(const char * filename); + ~SFFile(); + Instrument *LoadInstrument(struct Renderer *song, int drum, int bank, int program); + Instrument *LoadInstrumentOrder(struct Renderer *song, int order, int drum, int bank, int program); + void SetOrder(int order, int drum, int bank, int program); + void SetAllOrders(int order); + + bool FinalStructureTest(); + void CheckBags(); + void CheckZones(int start, int stop, bool instr); + void TranslatePercussions(); + void TranslatePercussionPreset(SFPreset *preset); + void TranslatePercussionPresetZone(SFPreset *preset, SFBag *zone); + + void SetInstrumentGenerators(SFGenComposite *composite, int start, int stop); + void AddPresetGenerators(SFGenComposite *composite, int start, int stop, SFPreset *preset); + void AddPresetGenerators(SFGenComposite *composite, int start, int stop, bool gen_set[GEN_NumGenerators]); + + Instrument *LoadPercussion(Renderer *song, SFPerc *perc); + Instrument *LoadPreset(Renderer *song, SFPreset *preset); + void LoadSample(Renderer* song, SFSample *sample); + void ApplyGeneratorsToRegion(SFGenComposite *gen, SFSample *sfsamp, Renderer *song, Sample *sp); + + SFPreset *Presets; + SFBag *PresetBags; + SFGenList *PresetGenerators; + SFInst *Instruments; + SFBag *InstrBags; + SFGenList *InstrGenerators; + SFSample *Samples; + std::vector Percussion; + int MinorVersion; + uint32_t SampleDataOffset; + uint32_t SampleDataLSBOffset; + uint32_t SizeSampleData; + uint32_t SizeSampleDataLSB; + int NumPresets; + int NumPresetBags; + int NumPresetGenerators; + int NumInstruments; + int NumInstrBags; + int NumInstrGenerators; + int NumSamples; +}; + +SFFile *ReadSF2(const char *filename, timidity_file *f); +} \ No newline at end of file diff --git a/thirdparty/timidity/timidity/t_swap.h b/thirdparty/timidity/timidity/t_swap.h new file mode 100644 index 000000000..de9b7780a --- /dev/null +++ b/thirdparty/timidity/timidity/t_swap.h @@ -0,0 +1,255 @@ +// +// DESCRIPTION: +// Endianess handling, swapping 16bit and 32bit. +// +//----------------------------------------------------------------------------- + + +#ifndef __M_SWAP_H__ +#define __M_SWAP_H__ + +#include + +// Endianess handling. +// WAD files are stored little endian. + +#ifdef __APPLE__ +#include + +inline short LittleShort(short x) +{ + return (short)OSSwapLittleToHostInt16((uint16_t)x); +} + +inline unsigned short LittleShort(unsigned short x) +{ + return OSSwapLittleToHostInt16(x); +} + +inline short LittleShort(int x) +{ + return OSSwapLittleToHostInt16((uint16_t)x); +} + +inline unsigned short LittleShort(unsigned int x) +{ + return OSSwapLittleToHostInt16((uint16_t)x); +} + +inline int LittleLong(int x) +{ + return OSSwapLittleToHostInt32((uint32_t)x); +} + +inline unsigned int LittleLong(unsigned int x) +{ + return OSSwapLittleToHostInt32(x); +} + +inline short BigShort(short x) +{ + return (short)OSSwapBigToHostInt16((uint16_t)x); +} + +inline unsigned short BigShort(unsigned short x) +{ + return OSSwapBigToHostInt16(x); +} + +inline int BigLong(int x) +{ + return OSSwapBigToHostInt32((uint32_t)x); +} + +inline unsigned int BigLong(unsigned int x) +{ + return OSSwapBigToHostInt32(x); +} + +#elif defined __BIG_ENDIAN__ + +// Swap 16bit, that is, MSB and LSB byte. +// No masking with 0xFF should be necessary. +inline short LittleShort (short x) +{ + return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8)); +} + +inline unsigned short LittleShort (unsigned short x) +{ + return (unsigned short)((x>>8) | (x<<8)); +} + +inline short LittleShort (int x) +{ + return LittleShort((short)x); +} + +inline unsigned short LittleShort (unsigned int x) +{ + return LittleShort((unsigned short)x); +} + +// Swapping 32bit. +inline unsigned int LittleLong (unsigned int x) +{ + return (unsigned int)( + (x>>24) + | ((x>>8) & 0xff00) + | ((x<<8) & 0xff0000) + | (x<<24)); +} + +inline int LittleLong (int x) +{ + return (int)( + (((unsigned int)x)>>24) + | ((((unsigned int)x)>>8) & 0xff00) + | ((((unsigned int)x)<<8) & 0xff0000) + | (((unsigned int)x)<<24)); +} + +inline short BigShort(short x) +{ + return x; +} + +inline unsigned short BigShort(unsigned short x) +{ + return x; +} + +inline unsigned int BigLong(unsigned int x) +{ + return x; +} + +inline int BigLong(int x) +{ + return x; +} + +#else + +inline short LittleShort(short x) +{ + return x; +} + +inline unsigned short LittleShort(unsigned short x) +{ + return x; +} + +inline unsigned int LittleLong(unsigned int x) +{ + return x; +} + +inline int LittleLong(int x) +{ + return x; +} + +#ifdef _MSC_VER + +inline short BigShort(short x) +{ + return (short)_byteswap_ushort((unsigned short)x); +} + +inline unsigned short BigShort(unsigned short x) +{ + return _byteswap_ushort(x); +} + +inline int BigLong(int x) +{ + return (int)_byteswap_ulong((unsigned long)x); +} + +inline unsigned int BigLong(unsigned int x) +{ + return (unsigned int)_byteswap_ulong((unsigned long)x); +} +#pragma warning (default: 4035) + +#else + +inline short BigShort (short x) +{ + return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8)); +} + +inline unsigned short BigShort (unsigned short x) +{ + return (unsigned short)((x>>8) | (x<<8)); +} + +inline unsigned int BigLong (unsigned int x) +{ + return (unsigned int)( + (x>>24) + | ((x>>8) & 0xff00) + | ((x<<8) & 0xff0000) + | (x<<24)); +} + +inline int BigLong (int x) +{ + return (int)( + (((unsigned int)x)>>24) + | ((((unsigned int)x)>>8) & 0xff00) + | ((((unsigned int)x)<<8) & 0xff0000) + | (((unsigned int)x)<<24)); +} + +#endif + +#endif // __BIG_ENDIAN__ + +// These may be destructive so they should create errors +unsigned long BigLong(unsigned long) = delete; +long BigLong(long) = delete; +unsigned long LittleLong(unsigned long) = delete; +long LittleLong(long) = delete; + + +// Data accessors, since some data is highly likely to be unaligned. +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) +inline int GetShort(const unsigned char *foo) +{ + return *(const short *)foo; +} +inline int GetInt(const unsigned char *foo) +{ + return *(const int *)foo; +} +#else +inline int GetShort(const unsigned char *foo) +{ + return short(foo[0] | (foo[1] << 8)); +} +inline int GetInt(const unsigned char *foo) +{ + return int(foo[0] | (foo[1] << 8) | (foo[2] << 16) | (foo[3] << 24)); +} +#endif +inline int GetBigInt(const unsigned char *foo) +{ + return int((foo[0] << 24) | (foo[1] << 16) | (foo[2] << 8) | foo[3]); +} + +#ifdef __BIG_ENDIAN__ +inline int GetNativeInt(const unsigned char *foo) +{ + return GetBigInt(foo); +} +#else +inline int GetNativeInt(const unsigned char *foo) +{ + return GetInt(foo); +} +#endif + +#endif // __M_SWAP_H__ diff --git a/thirdparty/timidity/timidity/timidity.h b/thirdparty/timidity/timidity/timidity.h new file mode 100644 index 000000000..831bee594 --- /dev/null +++ b/thirdparty/timidity/timidity/timidity.h @@ -0,0 +1,255 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This library 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 library 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 library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef TIMIDITY_H +#define TIMIDITY_H + +#include +#include +#include "../../../source/zmusic/fileio.h" + +namespace Timidity +{ + using timidity_file = MusicIO::FileInterface; + + +/* +controls.h +*/ + +enum +{ + CMSG_INFO = 10, + CMSG_WARNING = 50, + CMSG_ERROR = 100 +}; + +enum +{ + VERB_NORMAL, + VERB_VERBOSE, + VERB_NOISY, + VERB_DEBUG +}; + +extern void (*printMessage)(int type, int verbosity_level, const char *fmt, ...); + + +/* +timidity.h +*/ +struct DLS_Data; +struct Instrument; +struct Sample; +typedef float sample_t; +typedef float final_volume_t; +class Instruments; + +enum +{ + VIBRATO_SAMPLE_INCREMENTS = 32 +}; + +struct MinEnvelope +{ + uint8_t stage; + uint8_t bUpdating; +}; + +struct GF1Envelope : public MinEnvelope +{ + int volume, target, increment; + int rate[6], offset[6]; + + void Init(struct Renderer* song, struct Voice* v); + bool Update(struct Voice* v); + bool Recompute(struct Voice* v); + void ApplyToAmp(struct Voice* v); + void Release(struct Voice* v); +}; + +struct SF2Envelope : public MinEnvelope +{ + float volume; + float DelayTime; // timecents + float AttackTime; // timecents + float HoldTime; // timecents + float DecayTime; // timecents + float SustainLevel; // -0.1% + float ReleaseTime; // timecents + float SampleRate; + int HoldStart; + float RateMul; + float RateMul_cB; + + void Init(struct Renderer* song, Voice* v); + bool Update(struct Voice* v); + void ApplyToAmp(struct Voice* v); + void Release(struct Voice* v); +}; + +struct Envelope +{ + union + { + MinEnvelope env; + GF1Envelope gf1; + SF2Envelope sf2; + }; + + uint8_t Type; + + void Init(struct Renderer* song, struct Voice* v); + bool Update(struct Voice* v); + void ApplyToAmp(struct Voice* v); + void Release(struct Voice* v); +}; + +struct Channel +{ + int + bank, program, sustain, pitchbend, + mono, /* one note only on this channel */ + pitchsens; + uint8_t + volume, expression; + int8_t + panning; + uint16_t + rpn, nrpn; + bool + nrpn_mode; + float + pitchfactor; /* precomputed pitch bend factor to save some fdiv's */ +}; + +struct Voice +{ + uint8_t + status, channel, note, velocity; + Sample* sample; + float + orig_frequency, frequency; + int + sample_offset, sample_increment, + tremolo_sweep, tremolo_sweep_position, + tremolo_phase, tremolo_phase_increment, + vibrato_sweep, vibrato_sweep_position; + + Envelope eg1, eg2; + + final_volume_t left_mix, right_mix; + + float + attenuation, left_offset, right_offset; + float + tremolo_volume; + int + vibrato_sample_increment[VIBRATO_SAMPLE_INCREMENTS]; + int + vibrato_phase, vibrato_control_ratio, vibrato_control_counter, + control_counter; + + int + sample_count; +}; + +struct Renderer +{ +//private: + float rate; + DLS_Data *patches; + Instruments* instruments; + Instrument *default_instrument; + int default_program; + int resample_buffer_size; + sample_t *resample_buffer; + Channel channel[16]; + Voice *voice; + int control_ratio, amp_with_poly; + int drumchannels; + int adjust_panning_immediately; + int voices; + int lost_notes, cut_notes; +public: + Renderer(float sample_rate, int voices, Instruments *instr); + ~Renderer(); + + void HandleEvent(int status, int parm1, int parm2); + void HandleLongMessage(const uint8_t *data, int len); + void HandleController(int chan, int ctrl, int val); + void ComputeOutput(float *buffer, int num_samples); + void MarkInstrument(int bank, int percussion, int instr); + void Reset(); + + int load_missing_instruments(); +//private: + int set_default_instrument(const char *name); + int convert_tremolo_sweep(uint8_t sweep); + int convert_vibrato_sweep(uint8_t sweep, int vib_control_ratio); + int convert_tremolo_rate(uint8_t rate); + int convert_vibrato_rate(uint8_t rate); + + void recompute_freq(int voice); + void recompute_amp(Voice *v); + void recompute_pan(Channel *chan); + + void kill_key_group(int voice); + float calculate_scaled_frequency(Sample *sample, int note); + void start_note(int chan, int note, int vel); + bool start_region(int chan, int note, int vel, Sample *sp, float freq); + + void note_on(int chan, int note, int vel); + void note_off(int chan, int note, int vel); + void all_notes_off(int chan); + void all_sounds_off(int chan); + void adjust_pressure(int chan, int note, int amount); + void adjust_panning(int chan); + void drop_sustain(int chan); + void adjust_pitchbend(int chan); + void adjust_volume(int chan); + + void reset_voices(); + void reset_controllers(int chan); + void reset_midi(); + + int allocate_voice(); + + void kill_note(int voice); + void finish_note(int voice); + + void DataEntryCoarseRPN(int chan, int rpn, int val); + void DataEntryFineRPN(int chan, int rpn, int val); + void DataEntryCoarseNRPN(int chan, int nrpn, int val); + void DataEntryFineNRPN(int chan, int nrpn, int val); + + int fill_bank(int dr, int b); + Instrument* load_instrument(const char* name, int percussion, + int panning, int note_to_use, + int strip_loop, int strip_envelope, + int strip_tail); + + Instrument* load_instrument_font(const char* font, int drum, int bank, int instrument); + Instrument* load_instrument_font_order(int order, int drum, int bank, int instrument); + + static void compute_pan(double panning, int type, float &left_offset, float &right_offset); +}; + +} +#endif diff --git a/thirdparty/timidityplus/CMakeLists.txt b/thirdparty/timidityplus/CMakeLists.txt new file mode 100644 index 000000000..8543b25d7 --- /dev/null +++ b/thirdparty/timidityplus/CMakeLists.txt @@ -0,0 +1,36 @@ +add_library(timidityplus OBJECT + fft4g.cpp + reverb.cpp + common.cpp + configfile.cpp + effect.cpp + filter.cpp + freq.cpp + instrum.cpp + mblock.cpp + mix.cpp + playmidi.cpp + quantity.cpp + readmidic.cpp + recache.cpp + resample.cpp + sbkconv.cpp + sffile.cpp + sfitem.cpp + smplfile.cpp + sndfont.cpp + tables.cpp +) + +target_include_directories(timidityplus INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE timiditypp) + +set_target_properties(timidityplus +PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON +) + +file(GLOB HEADER_FILES timiditypp/*.h) +target_sources(timidityplus PRIVATE ${HEADER_FILES}) + +use_fast_math(timidityplus) diff --git a/thirdparty/timidityplus/common.cpp b/thirdparty/timidityplus/common.cpp new file mode 100644 index 000000000..5abcb7e6f --- /dev/null +++ b/thirdparty/timidityplus/common.cpp @@ -0,0 +1,171 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + common.c + + */ + +#include +#include +#include + +#include +#include +#include "common.h" +#include "controls.h" + +void ZMusic_Print(int type, const char* msg, va_list args); + +namespace TimidityPlus +{ + +/* This'll allocate memory or die. */ +void *safe_malloc(size_t count) +{ + auto p = malloc(count); + if (p == nullptr) + { + abort(); + } + return p; +} + +void *safe_large_malloc(size_t count) +{ + return safe_malloc(count); +} + +void *safe_realloc(void *ptr, size_t count) +{ + auto p = realloc(ptr, count); + if (p == nullptr) + { + abort(); + } + return p; +} + +char *safe_strdup(const char *s) +{ + if (s == nullptr) s = ""; + auto p = strdup(s); + if (p == nullptr) + { + abort(); + } + return p; +} + +/* free ((void **)ptr_list)[0..count-1] and ptr_list itself */ +void free_ptr_list(void *ptr_list, int count) +{ + int i; + for (i = 0; i < count; i++) + free(((void **)ptr_list)[i]); + free(ptr_list); +} + +static int atoi_limited(const char *string, int v_min, int v_max) +{ + int value = atoi(string); + + if (value <= v_min) + value = v_min; + else if (value > v_max) + value = v_max; + return value; +} + +int string_to_7bit_range(const char *string_, int *start, int *end) +{ + const char *string = string_; + + if (isdigit(*string)) + { + *start = atoi_limited(string, 0, 127); + while (isdigit(*++string)); + } + else + *start = 0; + if (*string == '-') + { + string++; + *end = isdigit(*string) ? atoi_limited(string, 0, 127) : 127; + if (*start > *end) + *end = *start; + } + else + *end = *start; + return string != string_; +} + + +double GenRand_Real1() +{ + return rand() * (1. / RAND_MAX); +} + +int int_rand(int n) +{ + return (int)GenRand_Real1() * n; +} + +double flt_rand() +{ + return (int)GenRand_Real1(); +} + +timidity_file *open_file(const char *name, MusicIO::SoundFontReaderInterface *sfreader) +{ + return sfreader->open_file(name); +} + +/* This closes files opened with open_file */ +void tf_close(timidity_file *tf) +{ + if (tf) tf->close(); +} + +/* This is meant for skipping a few bytes. */ +void skip(timidity_file *tf, size_t len) +{ + tf_seek(tf, (long)len, SEEK_CUR); +} + +int tf_getc(timidity_file *tf) +{ + unsigned char c; + auto read = tf_read(&c, 1, tf); + return read == 0 ? EOF : c; +} + +void default_ctl_cmsg(int type, int verbosity_level, const char* fmt, ...) +{ + if (verbosity_level >= VERB_DEBUG) return; // Don't waste time on diagnostics. + + va_list args; + va_start(args, fmt); + ZMusic_Print(type, fmt, args); + va_end(args); +} + +// Allow hosting applications to capture the messages and deal with them themselves. +void (*printMessage)(int type, int verbosity_level, const char* fmt, ...) = default_ctl_cmsg; + +} diff --git a/thirdparty/timidityplus/configfile.cpp b/thirdparty/timidityplus/configfile.cpp new file mode 100644 index 000000000..1fc0cb941 --- /dev/null +++ b/thirdparty/timidityplus/configfile.cpp @@ -0,0 +1,1612 @@ +/* +TiMidity++ -- MIDI to WAVE converter and player +Copyright (C) 1999-2008 Masanao Izumo +Copyright (C) 1995 Tuukka Toivonen + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "quantity.h" + + +namespace TimidityPlus +{ + +#define MAXWORDS 130 +#define CHECKERRLIMIT \ + if(++errcnt >= 10) { \ + printMessage(CMSG_ERROR, VERB_NORMAL, \ + "Too many errors... Give up read %s", name); \ + reuse_mblock(&varbuf); \ + tf_close(tf); return 1; } + + +typedef struct { + const char *name; + int mapid, isdrum; +} MapNameEntry; + +static int mapnamecompare(const void *name, const void *entry) +{ + return strcmp((const char *)name, ((const MapNameEntry *)entry)->name); +} + +static int mapname2id(char *name, int *isdrum) +{ + static const MapNameEntry data[] = { + /* sorted in alphabetical order */ + { "gm2", GM2_TONE_MAP, 0 }, + { "gm2drum", GM2_DRUM_MAP, 1 }, + { "sc55", SC_55_TONE_MAP, 0 }, + { "sc55drum", SC_55_DRUM_MAP, 1 }, + { "sc88", SC_88_TONE_MAP, 0 }, + { "sc8850", SC_8850_TONE_MAP, 0 }, + { "sc8850drum", SC_8850_DRUM_MAP, 1 }, + { "sc88drum", SC_88_DRUM_MAP, 1 }, + { "sc88pro", SC_88PRO_TONE_MAP, 0 }, + { "sc88prodrum", SC_88PRO_DRUM_MAP, 1 }, + { "xg", XG_NORMAL_MAP, 0 }, + { "xgdrum", XG_DRUM_MAP, 1 }, + { "xgsfx126", XG_SFX126_MAP, 1 }, + { "xgsfx64", XG_SFX64_MAP, 0 } + }; + const MapNameEntry *found; + + found = (MapNameEntry *)bsearch(name, data, sizeof data / sizeof data[0], sizeof data[0], mapnamecompare); + if (found != NULL) + { + *isdrum = found->isdrum; + return found->mapid; + } + return -1; +} + +static float *config_parse_tune(const char *cp, int *num) +{ + const char *p; + float *tune_list; + int i; + + /* count num */ + *num = 1, p = cp; + while ((p = strchr(p, ',')) != NULL) + (*num)++, p++; + /* alloc */ + tune_list = (float *)safe_malloc((*num) * sizeof(float)); + /* regist */ + for (i = 0, p = cp; i < *num; i++, p++) { + tune_list[i] = atof(p); + if (!(p = strchr(p, ','))) + break; + } + return tune_list; +} + +static int16_t *config_parse_int16(const char *cp, int *num) +{ + const char *p; + int16_t *list; + int i; + + /* count num */ + *num = 1, p = cp; + while ((p = strchr(p, ',')) != NULL) + (*num)++, p++; + /* alloc */ + list = (int16_t *)safe_malloc((*num) * sizeof(int16_t)); + /* regist */ + for (i = 0, p = cp; i < *num; i++, p++) { + list[i] = atoi(p); + if (!(p = strchr(p, ','))) + break; + } + return list; +} + +static int **config_parse_envelope(const char *cp, int *num) +{ + const char *p, *px; + int **env_list; + int i, j; + + /* count num */ + *num = 1, p = cp; + while ((p = strchr(p, ',')) != NULL) + (*num)++, p++; + /* alloc */ + env_list = (int **)safe_malloc((*num) * sizeof(int *)); + for (i = 0; i < *num; i++) + env_list[i] = (int *)safe_malloc(6 * sizeof(int)); + /* init */ + for (i = 0; i < *num; i++) + for (j = 0; j < 6; j++) + env_list[i][j] = -1; + /* regist */ + for (i = 0, p = cp; i < *num; i++, p++) { + px = strchr(p, ','); + for (j = 0; j < 6; j++, p++) { + if (*p == ':') + continue; + env_list[i][j] = atoi(p); + if (!(p = strchr(p, ':'))) + break; + if (px && p > px) + break; + } + if (!(p = px)) + break; + } + return env_list; +} + +static Quantity **config_parse_modulation(const char *name, int line, const char *cp, int *num, int mod_type) +{ + const char *p, *px, *err; + char buf[128], *delim; + Quantity **mod_list; + int i, j; + static const char * qtypestr[] = { "tremolo", "vibrato" }; + static const uint16_t qtypes[] = { + QUANTITY_UNIT_TYPE(TREMOLO_SWEEP), QUANTITY_UNIT_TYPE(TREMOLO_RATE), QUANTITY_UNIT_TYPE(DIRECT_INT), + QUANTITY_UNIT_TYPE(VIBRATO_SWEEP), QUANTITY_UNIT_TYPE(VIBRATO_RATE), QUANTITY_UNIT_TYPE(DIRECT_INT) + }; + + /* count num */ + *num = 1, p = cp; + while ((p = strchr(p, ',')) != NULL) + (*num)++, p++; + /* alloc */ + mod_list = (Quantity **)safe_malloc((*num) * sizeof(Quantity *)); + for (i = 0; i < *num; i++) + mod_list[i] = (Quantity *)safe_malloc(3 * sizeof(Quantity)); + /* init */ + for (i = 0; i < *num; i++) + for (j = 0; j < 3; j++) + INIT_QUANTITY(mod_list[i][j]); + buf[sizeof buf - 1] = '\0'; + /* regist */ + for (i = 0, p = cp; i < *num; i++, p++) { + px = strchr(p, ','); + for (j = 0; j < 3; j++, p++) { + if (*p == ':') + continue; + if ((delim = strpbrk(strncpy(buf, p, sizeof buf - 1), ":,")) != NULL) + *delim = '\0'; + if (*buf != '\0' && (err = string_to_quantity(buf, &mod_list[i][j], qtypes[mod_type * 3 + j])) != NULL) { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: %s: parameter %d of item %d: %s (%s)", + name, line, qtypestr[mod_type], j + 1, i + 1, err, buf); + free_ptr_list(mod_list, *num); + mod_list = NULL; + *num = 0; + return NULL; + } + if (!(p = strchr(p, ':'))) + break; + if (px && p > px) + break; + } + if (!(p = px)) + break; + } + return mod_list; +} + + +/*! copy bank and, if necessary, map appropriately */ +void Instruments::copybank(ToneBank *to, ToneBank *from, int mapid, int bankmapfrom, int bankno) +{ + ToneBankElement *toelm, *fromelm; + int i; + + if (from == NULL) + return; + for (i = 0; i < 128; i++) + { + toelm = &to->tone[i]; + fromelm = &from->tone[i]; + if (fromelm->name == NULL) + continue; + copy_tone_bank_element(toelm, fromelm); + toelm->instrument = NULL; + if (mapid != INST_NO_MAP) + set_instrument_map(mapid, bankmapfrom, i, bankno, i); + } +} + +/*! copy the whole mapped bank. returns 0 if no error. */ +int Instruments::copymap(int mapto, int mapfrom, int isdrum) +{ + ToneBank **tb = isdrum ? drumset : tonebank; + int i, bankfrom, bankto; + + for (i = 0; i < 128; i++) + { + bankfrom = find_instrument_map_bank(isdrum, mapfrom, i); + if (bankfrom <= 0) /* not mapped */ + continue; + bankto = alloc_instrument_map_bank(isdrum, mapto, i); + if (bankto == -1) /* failed */ + return 1; + copybank(tb[bankto], tb[bankfrom], mapto, i, bankto); + } + return 0; +} + +int Instruments::set_gus_patchconf_opts(const char *name, int line, char *opts, ToneBankElement *tone) +{ + char *cp; + int k; + + if (!(cp = strchr(opts, '='))) { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: bad patch option %s", name, line, opts); + return 1; + } + *cp++ = 0; + if (!strcmp(opts, "amp")) { + k = atoi(cp); + if ((k < 0 || k > MAX_AMPLIFICATION) || (*cp < '0' || *cp > '9')) { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: amplification must be between 0 and %d", + name, line, MAX_AMPLIFICATION); + return 1; + } + tone->amp = k; + } + else if (!strcmp(opts, "note")) { + k = atoi(cp); + if ((k < 0 || k > 127) || (*cp < '0' || *cp > '9')) { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: note must be between 0 and 127", + name, line); + return 1; + } + tone->note = k; + tone->scltune = config_parse_int16("100", &tone->scltunenum); + } + else if (!strcmp(opts, "pan")) { + if (!strcmp(cp, "center")) + k = 64; + else if (!strcmp(cp, "left")) + k = 0; + else if (!strcmp(cp, "right")) + k = 127; + else { + k = ((atoi(cp) + 100) * 100) / 157; + if ((k < 0 || k > 127) + || (k == 0 && *cp != '-' && (*cp < '0' || *cp > '9'))) { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: panning must be left, right, " + "center, or between -100 and 100", + name, line); + return 1; + } + } + tone->pan = k; + } + else if (!strcmp(opts, "tune")) + tone->tune = config_parse_tune(cp, &tone->tunenum); + else if (!strcmp(opts, "rate")) + tone->envrate = config_parse_envelope(cp, &tone->envratenum); + else if (!strcmp(opts, "offset")) + tone->envofs = config_parse_envelope(cp, &tone->envofsnum); + else if (!strcmp(opts, "keep")) { + if (!strcmp(cp, "env")) + tone->strip_envelope = 0; + else if (!strcmp(cp, "loop")) + tone->strip_loop = 0; + else { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: keep must be env or loop", name, line); + return 1; + } + } + else if (!strcmp(opts, "strip")) { + if (!strcmp(cp, "env")) + tone->strip_envelope = 1; + else if (!strcmp(cp, "loop")) + tone->strip_loop = 1; + else if (!strcmp(cp, "tail")) + tone->strip_tail = 1; + else { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: strip must be env, loop, or tail", + name, line); + return 1; + } + } + else if (!strcmp(opts, "tremolo")) { + if ((tone->trem = config_parse_modulation(name, + line, cp, &tone->tremnum, 0)) == NULL) + return 1; + } + else if (!strcmp(opts, "vibrato")) { + if ((tone->vib = config_parse_modulation(name, + line, cp, &tone->vibnum, 1)) == NULL) + return 1; + } + else if (!strcmp(opts, "sclnote")) + tone->sclnote = config_parse_int16(cp, &tone->sclnotenum); + else if (!strcmp(opts, "scltune")) + tone->scltune = config_parse_int16(cp, &tone->scltunenum); + else if (!strcmp(opts, "comm")) { + char *p; + + if (tone->comment) + free(tone->comment); + p = tone->comment = safe_strdup(cp); + while (*p) { + if (*p == ',') + *p = ' '; + p++; + } + } + else if (!strcmp(opts, "modrate")) + tone->modenvrate = config_parse_envelope(cp, &tone->modenvratenum); + else if (!strcmp(opts, "modoffset")) + tone->modenvofs = config_parse_envelope(cp, &tone->modenvofsnum); + else if (!strcmp(opts, "envkeyf")) + tone->envkeyf = config_parse_envelope(cp, &tone->envkeyfnum); + else if (!strcmp(opts, "envvelf")) + tone->envvelf = config_parse_envelope(cp, &tone->envvelfnum); + else if (!strcmp(opts, "modkeyf")) + tone->modenvkeyf = config_parse_envelope(cp, &tone->modenvkeyfnum); + else if (!strcmp(opts, "modvelf")) + tone->modenvvelf = config_parse_envelope(cp, &tone->modenvvelfnum); + else if (!strcmp(opts, "trempitch")) + tone->trempitch = config_parse_int16(cp, &tone->trempitchnum); + else if (!strcmp(opts, "tremfc")) + tone->tremfc = config_parse_int16(cp, &tone->tremfcnum); + else if (!strcmp(opts, "modpitch")) + tone->modpitch = config_parse_int16(cp, &tone->modpitchnum); + else if (!strcmp(opts, "modfc")) + tone->modfc = config_parse_int16(cp, &tone->modfcnum); + else if (!strcmp(opts, "fc")) + tone->fc = config_parse_int16(cp, &tone->fcnum); + else if (!strcmp(opts, "q")) + tone->reso = config_parse_int16(cp, &tone->resonum); + else if (!strcmp(opts, "fckeyf")) /* filter key-follow */ + tone->key_to_fc = atoi(cp); + else if (!strcmp(opts, "fcvelf")) /* filter velocity-follow */ + tone->vel_to_fc = atoi(cp); + else if (!strcmp(opts, "qvelf")) /* resonance velocity-follow */ + tone->vel_to_resonance = atoi(cp); + else { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: bad patch option %s", + name, line, opts); + return 1; + } + return 0; +} + + +void Instruments::reinit_tone_bank_element(ToneBankElement *tone) +{ + free_tone_bank_element(tone); + tone->note = tone->pan = -1; + tone->strip_loop = tone->strip_envelope = tone->strip_tail = -1; + tone->amp = -1; + tone->rnddelay = 0; + tone->loop_timeout = 0; + tone->legato = tone->damper_mode = tone->key_to_fc = tone->vel_to_fc = 0; + tone->reverb_send = tone->chorus_send = tone->delay_send = -1; + tone->tva_level = -1; + tone->play_note = -1; +} + + +int Instruments::set_gus_patchconf(const char *name, int line, ToneBankElement *tone, char *pat, char **opts) +{ + int j; + reinit_tone_bank_element(tone); + + if (strcmp(pat, "%font") == 0) /* Font extention */ + { + /* %font filename bank prog [note-to-use] + * %font filename 128 bank key + */ + + if (opts[0] == NULL || opts[1] == NULL || opts[2] == NULL || + (atoi(opts[1]) == 128 && opts[3] == NULL)) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Syntax error", name, line); + return 1; + } + tone->name = safe_strdup(opts[0]); + tone->instype = 1; + if (atoi(opts[1]) == 128) /* drum */ + { + tone->font_bank = 128; + tone->font_preset = atoi(opts[2]); + tone->font_keynote = atoi(opts[3]); + opts += 4; + } + else + { + tone->font_bank = atoi(opts[1]); + tone->font_preset = atoi(opts[2]); + + if (opts[3] && isdigit(opts[3][0])) + { + tone->font_keynote = atoi(opts[3]); + opts += 4; + } + else + { + tone->font_keynote = -1; + opts += 3; + } + } + } + else if (strcmp(pat, "%sample") == 0) /* Sample extention */ + { + /* %sample filename */ + + if (opts[0] == NULL) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Syntax error", name, line); + return 1; + } + tone->name = safe_strdup(opts[0]); + tone->instype = 2; + opts++; + } + else + { + tone->instype = 0; + tone->name = safe_strdup(pat); + } + + for (j = 0; opts[j] != NULL; j++) + { + int err; + if ((err = set_gus_patchconf_opts(name, line, opts[j], tone)) != 0) + return err; + } + if (tone->comment == NULL) + tone->comment = safe_strdup(tone->name); + return 0; +} + + + +int Instruments::set_patchconf(const char *name, int line, ToneBank *bank, char *w[], int dr, int mapid, int bankmapfrom, int bankno) +{ + int i; + + i = atoi(w[0]); + if (!dr) + i -= progbase; + if (i < 0 || i > 127) + { + if (dr) + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Drum number must be between " + "0 and 127", + name, line); + else + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Program must be between " + "%d and %d", + name, line, progbase, 127 + progbase); + return 1; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + return 1; + } + + if (set_gus_patchconf(name, line, &bank->tone[i], w[1], w + 2)) + return 1; + if (mapid != INST_NO_MAP) + set_instrument_map(mapid, bankmapfrom, i, bankno, i); + return 0; +} + + + +/* string[0] should not be '#' */ +int Instruments::strip_trailing_comment(char *string, int next_token_index) +{ + if (string[next_token_index - 1] == '#' /* strip \1 in /^\S+(#*[ \t].*)/ */ + && (string[next_token_index] == ' ' || string[next_token_index] == '\t')) + { + string[next_token_index] = '\0'; /* new c-string terminator */ + while (string[--next_token_index - 1] == '#') + ; + } + return next_token_index; +} + +char *Instruments::expand_variables(char *string, MBlockList *varbuf, const char *basedir) +{ + char *p, *expstr; + const char *copystr; + int limlen, copylen, explen, varlen, braced; + + if ((p = strchr(string, '$')) == NULL) + return string; + varlen = (int)strlen(basedir); + explen = limlen = 0; + expstr = NULL; + copystr = string; + copylen = p - string; + string = p; + for (;;) + { + if (explen + copylen + 1 > limlen) + { + limlen += copylen + 128; + expstr = (char*)memcpy(new_segment(varbuf, limlen), expstr, explen); + } + memcpy(&expstr[explen], copystr, copylen); + explen += copylen; + if (*string == '\0') + break; + else if (*string == '$') + { + braced = *++string == '{'; + if (braced) + { + if ((p = strchr(string + 1, '}')) == NULL) + p = string; /* no closing brace */ + else + string++; + } + else + for (p = string; isalnum(*p) || *p == '_'; p++); + if (p == string) /* empty */ + { + copystr = "${"; + copylen = 1 + braced; + } + else + { + if (p - string == 7 && memcmp(string, "basedir", 7) == 0) + { + copystr = basedir; + copylen = varlen; + } + else /* undefined variable */ + copylen = 0; + string = p + braced; + } + } + else /* search next */ + { + p = strchr(string, '$'); + if (p == NULL) + copylen = (int)strlen(string); + else + copylen = int(p - string); + copystr = string; + string += copylen; + } + } + expstr[explen] = '\0'; + return expstr; +} + + +int Instruments::read_config_file(const char *name, int self, int allow_missing_file) +{ + timidity_file *tf; + char buf[1024], *tmp, *w[MAXWORDS + 1], *cp; + ToneBank *bank = NULL; + int i, j, k, line = 0, words, errcnt = 0; + static int rcf_count = 0; + int dr = 0, bankno = 0, mapid = INST_NO_MAP, origbankno = 0x7FFFFFFF; + int extension_flag, param_parse_err; + MBlockList varbuf; + const char *basedir; + char *sep; + + if (rcf_count > 50) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "Probable source loop in configuration files"); + return READ_CONFIG_RECURSION; + } + + tf = open_file(name, sfreader); + if (tf == NULL) + return allow_missing_file ? READ_CONFIG_FILE_NOT_FOUND : + READ_CONFIG_ERROR; + + init_mblock(&varbuf); + if (!self) + { + char *c = strdup_mblock(&varbuf, tf->filename.c_str()); + basedir = c; + for (; *c; c++) if (*c == '\\') *c = '/'; + sep = (char*)strrchr(basedir, '/'); + } + else + sep = NULL; + if (sep == NULL) + { + basedir = "."; + } + else + { + if ((cp = (char*)strchr(sep, '#')) != NULL) + sep = cp + 1; /* inclusive of '#' */ + *sep = '\0'; + } + + while (tf_gets(buf, sizeof(buf), tf)) + { + line++; + if (strncmp(buf, "#extension", 10) == 0) { + extension_flag = 1; + i = 10; + } + else + { + extension_flag = 0; + i = 0; + } + + while (isspace(buf[i])) /* skip /^\s*(?#)/ */ + i++; + if (buf[i] == '#' || buf[i] == '\0') /* /^#|^$/ */ + continue; + tmp = expand_variables(buf, &varbuf, basedir); + j = (int)strcspn(tmp + i, " \t\r\n\240"); + if (j == 0) + j = (int)strlen(tmp + i); + j = strip_trailing_comment(tmp + i, j); + tmp[i + j] = '\0'; /* terminate the first token */ + w[0] = tmp + i; + i += j + 1; + words = param_parse_err = 0; + while (words < MAXWORDS - 1) /* -1 : next arg */ + { + char *terminator; + + while (isspace(tmp[i])) /* skip /^\s*(?#)/ */ + i++; + if (tmp[i] == '\0' + || tmp[i] == '#') /* /\s#/ */ + break; + if ((tmp[i] == '"' || tmp[i] == '\'') + && (terminator = strchr(tmp + i + 1, tmp[i])) != NULL) + { + if (!isspace(terminator[1]) && terminator[1] != '\0') + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: there must be at least one whitespace between " + "string terminator (%c) and the next parameter", name, line, tmp[i]); + CHECKERRLIMIT; + param_parse_err = 1; + break; + } + w[++words] = tmp + i + 1; + i = terminator - tmp + 1; + *terminator = '\0'; + } + else /* not terminated */ + { + j = (int)strcspn(tmp + i, " \t\r\n\240"); + if (j > 0) + j = strip_trailing_comment(tmp + i, j); + w[++words] = tmp + i; + i += j; + if (tmp[i] != '\0') /* unless at the end-of-string (i.e. EOF) */ + tmp[i++] = '\0'; /* terminate the token */ + } + } + if (param_parse_err) + continue; + w[++words] = NULL; + + /* + * #extension [something...] + */ + + /* #extension timeout program sec */ + if (strcmp(w[0], "timeout") == 0) + { + if (words != 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension timeout " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + bank->tone[i].loop_timeout = atoi(w[2]); + } + /* #extension copydrumset drumset */ + else if (strcmp(w[0], "copydrumset") == 0) + { + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No copydrumset number given", + name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension copydrumset " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or " + "drum set before assignment", name, line); + CHECKERRLIMIT; + continue; + } + copybank(bank, drumset[i], mapid, origbankno, bankno); + } + /* #extension copybank bank */ + else if (strcmp(w[0], "copybank") == 0) + { + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No copybank number given", + name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension copybank " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or " + "drum set before assignment", name, line); + CHECKERRLIMIT; + continue; + } + copybank(bank, tonebank[i], mapid, origbankno, bankno); + } + /* #extension copymap tomapid frommapid */ + else if (strcmp(w[0], "copymap") == 0) + { + int mapto, mapfrom; + int toisdrum, fromisdrum; + + if (words != 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if ((mapto = mapname2id(w[1], &toisdrum)) == -1) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid map name: %s", name, line, w[1]); + CHECKERRLIMIT; + continue; + } + if ((mapfrom = mapname2id(w[2], &fromisdrum)) == -1) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid map name: %s", name, line, w[2]); + CHECKERRLIMIT; + continue; + } + if (toisdrum != fromisdrum) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Map type should be matched", name, line); + CHECKERRLIMIT; + continue; + } + if (copymap(mapto, mapfrom, toisdrum)) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No free %s available to map", + name, line, toisdrum ? "drum set" : "tone bank"); + CHECKERRLIMIT; + continue; + } + } + /* #extension undef program */ + else if (strcmp(w[0], "undef") == 0) + { + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No undef number given", + name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension undef " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or " + "drum set before assignment", name, line); + CHECKERRLIMIT; + continue; + } + free_tone_bank_element(&bank->tone[i]); + } + /* #extension altassign numbers... */ + else if (strcmp(w[0], "altassign") == 0) + { + ToneBank *bk; + + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before altassign", name, line); + CHECKERRLIMIT; + continue; + } + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No alternate assignment", name, line); + CHECKERRLIMIT; + continue; + } + + if (!dr) { + printMessage(CMSG_WARNING, VERB_NORMAL, + "%s: line %d: Warning: Not a drumset altassign" + " (ignored)", + name, line); + continue; + } + + bk = drumset[bankno]; + bk->alt = add_altassign_string(bk->alt, w + 1, words - 1); + } /* #extension legato [program] [0 or 1] */ + else if (strcmp(w[0], "legato") == 0) + { + if (words != 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension legato " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + bank->tone[i].legato = atoi(w[2]); + } /* #extension damper [program] [0 or 1] */ + else if (strcmp(w[0], "damper") == 0) + { + if (words != 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension damper " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + bank->tone[i].damper_mode = atoi(w[2]); + } /* #extension rnddelay [program] [0 or 1] */ + else if (strcmp(w[0], "rnddelay") == 0) + { + if (words != 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[1]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension rnddelay " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + bank->tone[i].rnddelay = atoi(w[2]); + } /* #extension level program tva_level */ + else if (strcmp(w[0], "level") == 0) + { + if (words != 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[2]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension level " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + cp = w[1]; + do { + if (string_to_7bit_range(cp, &j, &k)) + { + while (j <= k) + bank->tone[j++].tva_level = i; + } + cp = strchr(cp, ','); + } while (cp++ != NULL); + } /* #extension reverbsend */ + else if (strcmp(w[0], "reverbsend") == 0) + { + if (words != 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[2]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension reverbsend " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + cp = w[1]; + do { + if (string_to_7bit_range(cp, &j, &k)) + { + while (j <= k) + bank->tone[j++].reverb_send = i; + } + cp = strchr(cp, ','); + } while (cp++ != NULL); + } /* #extension chorussend */ + else if (strcmp(w[0], "chorussend") == 0) + { + if (words != 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[2]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension chorussend " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + cp = w[1]; + do { + if (string_to_7bit_range(cp, &j, &k)) + { + while (j <= k) + bank->tone[j++].chorus_send = i; + } + cp = strchr(cp, ','); + } while (cp++ != NULL); + } /* #extension delaysend */ + else if (strcmp(w[0], "delaysend") == 0) + { + if (words != 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[2]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension delaysend " + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + cp = w[1]; + do { + if (string_to_7bit_range(cp, &j, &k)) + { + while (j <= k) + bank->tone[j++].delay_send = i; + } + cp = strchr(cp, ','); + } while (cp++ != NULL); + } /* #extension playnote */ + else if (strcmp(w[0], "playnote") == 0) + { + if (words != 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (!bank) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify tone bank or drum set " + "before assignment", name, line); + CHECKERRLIMIT; + continue; + } + i = atoi(w[2]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: extension playnote" + "must be between 0 and 127", name, line); + CHECKERRLIMIT; + continue; + } + cp = w[1]; + do { + if (string_to_7bit_range(cp, &j, &k)) + { + while (j <= k) + bank->tone[j++].play_note = i; + } + cp = strchr(cp, ','); + } while (cp++ != NULL); + } + else if (!strcmp(w[0], "soundfont")) + { + int order, cutoff, isremove, reso, amp; + char *sf_file; + + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No soundfont file given", + name, line); + CHECKERRLIMIT; + continue; + } + + sf_file = w[1]; + order = cutoff = reso = amp = -1; + isremove = 0; + for (j = 2; j < words; j++) + { + if (strcmp(w[j], "remove") == 0) + { + isremove = 1; + break; + } + if (!(cp = strchr(w[j], '='))) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: bad patch option %s", + name, line, w[j]); + CHECKERRLIMIT; + break; + } + *cp++ = 0; + k = atoi(cp); + if (!strcmp(w[j], "order")) + { + if (k < 0 || (*cp < '0' || *cp > '9')) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: order must be a digit", + name, line); + CHECKERRLIMIT; + break; + } + order = k; + } + else if (!strcmp(w[j], "cutoff")) + { + if (k < 0 || (*cp < '0' || *cp > '9')) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: cutoff must be a digit", + name, line); + CHECKERRLIMIT; + break; + } + cutoff = k; + } + else if (!strcmp(w[j], "reso")) + { + if (k < 0 || (*cp < '0' || *cp > '9')) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: reso must be a digit", + name, line); + CHECKERRLIMIT; + break; + } + reso = k; + } + else if (!strcmp(w[j], "amp")) + { + amp = k; + } + } + if (isremove) + remove_soundfont(sf_file); + else + add_soundfont(sf_file, order, cutoff, reso, amp); + } + else if (!strcmp(w[0], "font")) + { + int bank, preset, keynote; + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: no font command", name, line); + CHECKERRLIMIT; + continue; + } + if (!strcmp(w[1], "exclude")) + { + if (words < 3) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No bank/preset/key is given", + name, line); + CHECKERRLIMIT; + continue; + } + bank = atoi(w[2]); + if (words >= 4) + preset = atoi(w[3]) - progbase; + else + preset = -1; + if (words >= 5) + keynote = atoi(w[4]); + else + keynote = -1; + if (exclude_soundfont(bank, preset, keynote)) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No soundfont is given", + name, line); + CHECKERRLIMIT; + } + } + else if (!strcmp(w[1], "order")) + { + int order; + if (words < 4) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No order/bank is given", + name, line); + CHECKERRLIMIT; + continue; + } + order = atoi(w[2]); + bank = atoi(w[3]); + if (words >= 5) + preset = atoi(w[4]) - progbase; + else + preset = -1; + if (words >= 6) + keynote = atoi(w[5]); + else + keynote = -1; + if (order_soundfont(bank, preset, keynote, order)) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No soundfont is given", + name, line); + CHECKERRLIMIT; + } + } + } + else if (!strcmp(w[0], "progbase")) + { + if (words < 2 || *w[1] < '0' || *w[1] > '9') + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + progbase = atoi(w[1]); + } + else if (!strcmp(w[0], "map")) /* map set1 elem1 set2 elem2 */ + { + int arg[5], isdrum; + + if (words != 6) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if ((arg[0] = mapname2id(w[1], &isdrum)) == -1) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid map name: %s", name, line, w[1]); + CHECKERRLIMIT; + continue; + } + for (i = 2; i < 6; i++) + arg[i - 1] = atoi(w[i]); + if (isdrum) + { + arg[1] -= progbase; + arg[3] -= progbase; + } + else + { + arg[2] -= progbase; + arg[4] -= progbase; + } + + for (i = 1; i < 5; i++) + if (arg[i] < 0 || arg[i] > 127) + break; + if (i != 5) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid parameter", name, line); + CHECKERRLIMIT; + continue; + } + set_instrument_map(arg[0], arg[1], arg[2], arg[3], arg[4]); + } + + /* + * Standard configurations + */ + else if (!strcmp(w[0], "dir")) + { + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No directory given", name, line); + CHECKERRLIMIT; + continue; + } + for (i = 1; i < words; i++) + sfreader->add_search_path(w[i]); + } + else if (!strcmp(w[0], "source") || !strcmp(w[0], "trysource")) + { + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No file name given", name, line); + CHECKERRLIMIT; + continue; + } + for (i = 1; i < words; i++) + { + int status; + rcf_count++; + status = read_config_file(w[i], 0, !strcmp(w[0], "trysource")); + rcf_count--; + switch (status) { + case READ_CONFIG_SUCCESS: + break; + case READ_CONFIG_ERROR: + CHECKERRLIMIT; + continue; + case READ_CONFIG_RECURSION: + reuse_mblock(&varbuf); + tf_close(tf); + return READ_CONFIG_RECURSION; + case READ_CONFIG_FILE_NOT_FOUND: + break; + } + } + } + else if (!strcmp(w[0], "default")) + { + if (words != 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Must specify exactly one patch name", + name, line); + CHECKERRLIMIT; + continue; + } + strncpy(def_instr_name, w[1], 255); + def_instr_name[255] = '\0'; + default_instrument_name = def_instr_name; + } + /* drumset [mapid] num */ + else if (!strcmp(w[0], "drumset")) + { + int newmapid, isdrum, newbankno; + + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No drum set number given", name, line); + CHECKERRLIMIT; + continue; + } + if (words != 2 && !isdigit(*w[1])) + { + if ((newmapid = mapname2id(w[1], &isdrum)) == -1 || !isdrum) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid drum set map name: %s", name, line, w[1]); + CHECKERRLIMIT; + continue; + } + words--; + memmove(&w[1], &w[2], sizeof w[0] * words); + } + else + newmapid = INST_NO_MAP; + i = atoi(w[1]) - progbase; + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Drum set must be between %d and %d", + name, line, + progbase, progbase + 127); + CHECKERRLIMIT; + continue; + } + + newbankno = i; + i = alloc_instrument_map_bank(1, newmapid, i); + if (i == -1) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No free drum set available to map", + name, line); + CHECKERRLIMIT; + continue; + } + + if (words == 2) + { + bank = drumset[i]; + bankno = i; + mapid = newmapid; + origbankno = newbankno; + dr = 1; + } + else + { + if (words < 4 || *w[2] < '0' || *w[2] > '9') + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (set_patchconf(name, line, drumset[i], &w[2], 1, newmapid, newbankno, i)) + { + CHECKERRLIMIT; + continue; + } + } + } + /* bank [mapid] num */ + else if (!strcmp(w[0], "bank")) + { + int newmapid, isdrum, newbankno; + + if (words < 2) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No bank number given", name, line); + CHECKERRLIMIT; + continue; + } + if (words != 2 && !isdigit(*w[1])) + { + if ((newmapid = mapname2id(w[1], &isdrum)) == -1 || isdrum) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Invalid bank map name: %s", name, line, w[1]); + CHECKERRLIMIT; + continue; + } + words--; + memmove(&w[1], &w[2], sizeof w[0] * words); + } + else + newmapid = INST_NO_MAP; + i = atoi(w[1]); + if (i < 0 || i > 127) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: Tone bank must be between 0 and 127", + name, line); + CHECKERRLIMIT; + continue; + } + + newbankno = i; + i = alloc_instrument_map_bank(0, newmapid, i); + if (i == -1) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: No free tone bank available to map", + name, line); + CHECKERRLIMIT; + continue; + } + + if (words == 2) + { + bank = tonebank[i]; + bankno = i; + mapid = newmapid; + origbankno = newbankno; + dr = 0; + } + else + { + if (words < 4 || *w[2] < '0' || *w[2] > '9') + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (set_patchconf(name, line, tonebank[i], &w[2], 0, newmapid, newbankno, i)) + { + CHECKERRLIMIT; + continue; + } + } + } + else + { + if (words < 2 || *w[0] < '0' || *w[0] > '9') + { + if (extension_flag) + continue; + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: line %d: syntax error", name, line); + CHECKERRLIMIT; + continue; + } + if (set_patchconf(name, line, bank, w, dr, mapid, origbankno, bankno)) + { + CHECKERRLIMIT; + continue; + } + } + } + reuse_mblock(&varbuf); + tf_close(tf); + return (errcnt == 0) ? READ_CONFIG_SUCCESS : READ_CONFIG_ERROR; +} + +} diff --git a/thirdparty/timidityplus/effect.cpp b/thirdparty/timidityplus/effect.cpp new file mode 100644 index 000000000..5f1084c96 --- /dev/null +++ b/thirdparty/timidityplus/effect.cpp @@ -0,0 +1,220 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + effect.c - To apply sound effects. + Mainly written by Masanao Izumo + + Interfaces: + void init_effect(void); + do_effect(int32_t* buf, int32_t count); +*/ + +#include +#include + +#include "effect.h" +#include "instrum.h" +#include "playmidi.h" +#include "reverb.h" + + +namespace TimidityPlus +{ + + + +#define SIDE_CONTI_SEC 10 +#define CHANGE_SEC 2.0 + +void Effect::init_effect() +{ + effect_left_right_delay(NULL, 0); + reverb->init_for_effect(); +} + +/* + * Left & Right Delay Effect + */ +void Effect::effect_left_right_delay(int32_t *buff, int32_t count) +{ + int32_t save[AUDIO_BUFFER_SIZE * 2]; + int32_t pi, i, j, k, v, backoff; + int b; + int32_t *p; + + if (buff == NULL) + { + memset(prev, 0, sizeof(prev)); + return; + } + if (effect_lr_mode == 0 || effect_lr_mode == 1 || effect_lr_mode == 2) + b = effect_lr_mode; + else + return; + count *= 2; + backoff = 2 * (int)(playback_rate * effect_lr_delay_msec / 1000.0); + if (backoff == 0) + return; + if (backoff > count) + backoff = count; + if (count < AUDIO_BUFFER_SIZE * 2) + { + memset(buff + count, 0, 4 * (AUDIO_BUFFER_SIZE * 2 - count)); + count = AUDIO_BUFFER_SIZE * 2; + } + memcpy(save, buff, 4 * count); + pi = count - backoff; + if (b == 2) + { + if (turn_counter == 0) + { + turn_counter = SIDE_CONTI_SEC * playback_rate; + /* status: 0 -> 2 -> 3 -> 1 -> 4 -> 5 -> 0 -> ... + * status left right + * 0 - + (right) + * 1 + - (left) + * 2 -> + + (right -> center) + * 3 + -> - (center -> left) + * 4 -> - - (left -> center) + * 5 - -> + (center -> right) + */ + status = 0; + tc = 0; + } + p = prev; + for (i = 0; i < count; i += 2, pi += 2) + { + if (i < backoff) + p = prev; + else if (p == prev) + { + pi = 0; + p = save; + } + if (status < 2) + buff[i + status] = p[pi + status]; + else if (status < 4) + { + j = (status & 1); + v = (int32_t)(rate0 * buff[i + j] + rate1 * p[pi + j]); + buff[i + j] = v; + rate0 += dr, rate1 -= dr; + } + else + { + j = (status & 1); + k = !j; + v = (int32_t)(rate0 * buff[i + j] + rate1 * p[pi + j]); + buff[i + j] = v; + buff[i + k] = p[pi + k]; + rate0 += dr, rate1 -= dr; + } + tc++; + if (tc == turn_counter) + { + tc = 0; + switch (status) + { + case 0: + status = 2; + turn_counter = (CHANGE_SEC / 2.0) * playback_rate; + rate0 = 0.0; + rate1 = 1.0; + dr = 1.0 / turn_counter; + break; + case 2: + status = 3; + rate0 = 1.0; + rate1 = 0.0; + dr = -1.0 / turn_counter; + break; + case 3: + status = 1; + turn_counter = SIDE_CONTI_SEC * playback_rate; + break; + case 1: + status = 4; + turn_counter = (CHANGE_SEC / 2.0) * playback_rate; + rate0 = 1.0; + rate1 = 0.0; + dr = -1.0 / turn_counter; + break; + case 4: + status = 5; + turn_counter = (CHANGE_SEC / 2.0) * playback_rate; + rate0 = 0.0; + rate1 = 1.0; + dr = 1.0 / turn_counter; + break; + case 5: + status = 0; + turn_counter = SIDE_CONTI_SEC * playback_rate; + break; + } + } + } + } + else + { + for (i = 0; i < backoff; i += 2, pi += 2) + buff[b + i] = prev[b + pi]; + for (pi = 0; i < count; i += 2, pi += 2) + buff[b + i] = save[b + pi]; + } + memcpy(prev + count - backoff, save + count - backoff, 4 * backoff); +} + +void Effect::do_effect(int32_t *buf, int32_t count) +{ + int32_t nsamples = count * 2; + int reverb_level = (timidity_reverb < 0) + ? -timidity_reverb & 0x7f : DEFAULT_REVERB_SEND_LEVEL; + + /* for static reverb / chorus level */ + if (timidity_reverb == 2 || timidity_reverb == 4 + || (timidity_reverb < 0 && !(timidity_reverb & 0x80)) + || timidity_chorus < 0) + { + reverb->set_dry_signal(buf, nsamples); + /* chorus sounds horrible + * if applied globally on top of channel chorus + */ + if (timidity_reverb == 2 || timidity_reverb == 4 + || (timidity_reverb < 0 && !(timidity_reverb & 0x80))) + reverb->set_ch_reverb(buf, nsamples, reverb_level); + reverb->mix_dry_signal(buf, nsamples); + /* chorus sounds horrible + * if applied globally on top of channel chorus + */ + if (timidity_reverb == 2 || timidity_reverb == 4 + || (timidity_reverb < 0 && !(timidity_reverb & 0x80))) + reverb->do_ch_reverb(buf, nsamples); + } + /* L/R Delay */ + effect_left_right_delay(buf, count); +} + +int32_t Effect::my_mod(int32_t x, int32_t n) +{ + if (x >= n) + x -= n; + return x; +} + +} \ No newline at end of file diff --git a/thirdparty/timidityplus/fft4g.cpp b/thirdparty/timidityplus/fft4g.cpp new file mode 100644 index 000000000..a9e6e9855 --- /dev/null +++ b/thirdparty/timidityplus/fft4g.cpp @@ -0,0 +1,1353 @@ +/* EAW - May 11th, 2001: Changed all doubles to floats */ + +/* +Copyright: + Copyright(C) 1996-1999 Takuya OOURA + email: ooura@mmm.t.u-tokyo.ac.jp + download: http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html + You may use, copy, modify this code for any purpose and + without fee. You may distribute this ORIGINAL package. +*/ + +/* +Fast Fourier/Cosine/Sine Transform + dimension :one + data length :power of 2 + decimation :frequency + radix :4, 2 + data :inplace + table :use +functions + cdft: Complex Discrete Fourier Transform + rdft: Real Discrete Fourier Transform + ddct: Discrete Cosine Transform + ddst: Discrete Sine Transform + dfct: Cosine Transform of RDFT (Real Symmetric DFT) + dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) +function prototypes + void cdft(int, int, float *, int *, float *); + void rdft(int, int, float *, int *, float *); + void ddct(int, int, float *, int *, float *); + void ddst(int, int, float *, int *, float *); + void dfct(int, float *, float *, int *, float *); + void dfst(int, float *, float *, int *, float *); + + +-------- Complex DFT (Discrete Fourier Transform) -------- + [definition] + + X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k + X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k + ip[0] = 0; // first time only + cdft(2*n, 1, a, ip, w); + + ip[0] = 0; // first time only + cdft(2*n, -1, a, ip, w); + [parameters] + 2*n :data length (int) + n >= 1, n = power of 2 + a[0...2*n-1] :input/output data (float *) + input data + a[2*j] = Re(x[j]), + a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) + strictly, + length of ip >= + 2+(1<<(int)(log(n+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + cdft(2*n, -1, a, ip, w); + is + cdft(2*n, 1, a, ip, w); + for (j = 0; j <= 2 * n - 1; j++) { + a[j] *= 1.0 / n; + } + . + + +-------- Real DFT / Inverse of Real DFT -------- + [definition] + RDFT + R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 + I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0 IRDFT (excluding scale) + a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k + ip[0] = 0; // first time only + rdft(n, 1, a, ip, w); + + ip[0] = 0; // first time only + rdft(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + + output data + a[2*k] = R[k], 0<=k + input data + a[2*j] = R[j], 0<=j= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + rdft(n, 1, a, ip, w); + is + rdft(n, -1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- + [definition] + IDCT (excluding scale) + C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k DCT + C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k + ip[0] = 0; // first time only + ddct(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddct(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + output data + a[k] = C[k], 0<=k= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddct(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddct(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DST (Discrete Sine Transform) / Inverse of DST -------- + [definition] + IDST (excluding scale) + S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k DST + S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0 + ip[0] = 0; // first time only + ddst(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddst(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + + input data + a[j] = A[j], 0 + output data + a[k] = S[k], 0= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddst(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddst(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Cosine Transform of RDFT (Real Symmetric DFT) -------- + [definition] + C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n + [usage] + ip[0] = 0; // first time only + dfct(n, a, t, ip, w); + [parameters] + n :data length - 1 (int) + n >= 2, n = power of 2 + a[0...n] :input/output data (float *) + output data + a[k] = C[k], 0<=k<=n + t[0...n/2] :work area (float *) + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + is + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + for (j = 0; j <= n; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- + [definition] + S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + output data + a[k] = S[k], 0= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + dfst(n, a, t, ip, w); + is + dfst(n, a, t, ip, w); + for (j = 1; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +Appendix : + The cos/sin table is recalculated when the larger table required. + w[] and ip[] are compatible with all routines. +*/ + +#include + + +namespace TimidityPlus +{ + void makewt(int nw, int *ip, float *w); + void bitrv2(int n, int *ip, float *a); + void bitrv2conj(int n, int *ip, float *a); + void cftfsub(int n, float *a, float *w); + void cftbsub(int n, float *a, float *w); + void makect(int nc, int *ip, float *c); + void rftfsub(int n, float *a, int nc, float *c); + void rftbsub(int n, float *a, int nc, float *c); + void dctsub(int n, float *a, int nc, float *c); + void dstsub(int n, float *a, int nc, float *c); + void cft1st(int n, float *a, float *w); + void cftmdl(int n, int l, float *a, float *w); + + + +#ifdef _MSC_VER +#pragma warning(disable:4244) // conversion from 'double' to 'float', possible loss of data +#endif + + +void cdft(int n, int isgn, float *a, int *ip, float *w) +{ + if (n > (ip[0] << 2)) { + makewt(n >> 2, ip, w); + } + if (n > 4) { + if (isgn >= 0) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + } + else { + bitrv2conj(n, ip + 2, a); + cftbsub(n, a, w); + } + } + else if (n == 4) { + cftfsub(n, a, w); + } +} + + +void rdft(int n, int isgn, float *a, int *ip, float *w) +{ + int nw, nc; + float xi; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 2)) { + nc = n >> 2; + makect(nc, ip, w + nw); + } + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } + else if (n == 4) { + cftfsub(n, a, w); + } + xi = a[0] - a[1]; + a[0] += a[1]; + a[1] = xi; + } + else { + a[1] = 0.5 * (a[0] - a[1]); + a[0] -= a[1]; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } + else if (n == 4) { + cftfsub(n, a, w); + } + } +} + + +void ddct(int n, int isgn, float *a, int *ip, float *w) +{ + int j, nw, nc; + float xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = a[j] - a[j - 1]; + a[j] += a[j - 1]; + } + a[1] = a[0] - xr; + a[0] += xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } + else if (n == 4) { + cftfsub(n, a, w); + } + } + dctsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } + else if (n == 4) { + cftfsub(n, a, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = a[j] - a[j + 1]; + a[j] += a[j + 1]; + } + a[n - 1] = xr; + } +} + + +void ddst(int n, int isgn, float *a, int *ip, float *w) +{ + int j, nw, nc; + float xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = -a[j] - a[j - 1]; + a[j] -= a[j - 1]; + } + a[1] = a[0] + xr; + a[0] -= xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } + else if (n == 4) { + cftfsub(n, a, w); + } + } + dstsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } + else if (n == 4) { + cftfsub(n, a, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = -a[j] - a[j + 1]; + a[j] -= a[j + 1]; + } + a[n - 1] = -xr; + } +} + + +void dfct(int n, float *a, float *t, int *ip, float *w) +{ + int j, k, l, m, mh, nw, nc; + float xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + m = n >> 1; + yi = a[m]; + xi = a[0] + a[n]; + a[0] -= a[n]; + t[0] = xi - yi; + t[m] = xi + yi; + if (n > 2) { + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] - a[n - j]; + xi = a[j] + a[n - j]; + yr = a[k] - a[n - k]; + yi = a[k] + a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi - yi; + t[k] = xi + yi; + } + t[mh] = a[mh] + a[n - mh]; + a[mh] -= a[n - mh]; + dctsub(m, a, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, a); + cftfsub(m, a, w); + rftfsub(m, a, nc, w + nw); + } + else if (m == 4) { + cftfsub(m, a, w); + } + a[n - 1] = a[0] - a[1]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] + a[j + 1]; + a[2 * j - 1] = a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dctsub(m, t, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, t); + cftfsub(m, t, w); + rftfsub(m, t, nc, w + nw); + } + else if (m == 4) { + cftfsub(m, t, w); + } + a[n - l] = t[0] - t[1]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = t[j] - t[j + 1]; + a[k + l] = t[j] + t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 0; j < mh; j++) { + k = m - j; + t[j] = t[m + k] - t[m + j]; + t[k] = t[m + k] + t[m + j]; + } + t[mh] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + a[n] = t[2] - t[1]; + a[0] = t[2] + t[1]; + } + else { + a[1] = a[0]; + a[2] = t[0]; + a[0] = t[1]; + } +} + + +void dfst(int n, float *a, float *t, int *ip, float *w) +{ + int j, k, l, m, mh, nw, nc; + float xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + if (n > 2) { + m = n >> 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] + a[n - j]; + xi = a[j] - a[n - j]; + yr = a[k] + a[n - k]; + yi = a[k] - a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi + yi; + t[k] = xi - yi; + } + t[0] = a[mh] - a[n - mh]; + a[mh] += a[n - mh]; + a[0] = a[m]; + dstsub(m, a, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, a); + cftfsub(m, a, w); + rftfsub(m, a, nc, w + nw); + } + else if (m == 4) { + cftfsub(m, a, w); + } + a[n - 1] = a[1] - a[0]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] - a[j + 1]; + a[2 * j - 1] = -a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dstsub(m, t, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, t); + cftfsub(m, t, w); + rftfsub(m, t, nc, w + nw); + } + else if (m == 4) { + cftfsub(m, t, w); + } + a[n - l] = t[1] - t[0]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = -t[j] - t[j + 1]; + a[k + l] = t[j] - t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + t[j] = t[m + k] + t[m + j]; + t[k] = t[m + k] - t[m + j]; + } + t[0] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + } + a[0] = 0; +} + + +/* -------- initializing routines -------- */ + + + +void makewt(int nw, int *ip, float *w) +{ + void bitrv2(int n, int *ip, float *a); + int j, nwh; + float delta, x, y; + + ip[0] = nw; + ip[1] = 1; + if (nw > 2) { + nwh = nw >> 1; + delta = atan(1.0) / nwh; + w[0] = 1; + w[1] = 0; + w[nwh] = cos(delta * nwh); + w[nwh + 1] = w[nwh]; + if (nwh > 2) { + for (j = 2; j < nwh; j += 2) { + x = cos(delta * j); + y = sin(delta * j); + w[j] = x; + w[j + 1] = y; + w[nw - j] = y; + w[nw - j + 1] = x; + } + bitrv2(nw, ip + 2, w); + } + } +} + + +void makect(int nc, int *ip, float *c) +{ + int j, nch; + float delta; + + ip[1] = nc; + if (nc > 1) { + nch = nc >> 1; + delta = atan(1.0) / nch; + c[0] = cos(delta * nch); + c[nch] = 0.5 * c[0]; + for (j = 1; j < nch; j++) { + c[j] = 0.5 * cos(delta * j); + c[nc - j] = 0.5 * sin(delta * j); + } + } +} + + +/* -------- child routines -------- */ + + +void bitrv2(int n, int *ip, float *a) +{ + int j, j1, k, k1, l, m, m2; + float xr, xi, yr, yi; + + ip[0] = 0; + l = n; + m = 1; + while ((m << 3) < l) { + l >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + l; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == l) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + j1 = 2 * k + m2 + ip[k]; + k1 = j1 + m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } + else { + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } + } +} + + +void bitrv2conj(int n, int *ip, float *a) +{ + int j, j1, k, k1, l, m, m2; + float xr, xi, yr, yi; + + ip[0] = 0; + l = n; + m = 1; + while ((m << 3) < l) { + l >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + l; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == l) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 2 * k + ip[k]; + a[k1 + 1] = -a[k1 + 1]; + j1 = k1 + m2; + k1 = j1 + m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + k1 += m2; + a[k1 + 1] = -a[k1 + 1]; + } + } + else { + a[1] = -a[1]; + a[m2 + 1] = -a[m2 + 1]; + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 2 * k + ip[k]; + a[k1 + 1] = -a[k1 + 1]; + a[k1 + m2 + 1] = -a[k1 + m2 + 1]; + } + } +} + + +void cftfsub(int n, float *a, float *w) +{ + int j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + } + else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = a[j + 1] - a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] += a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + + +void cftbsub(int n, float *a, float *w) +{ + int j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = -a[j + 1] - a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = -a[j + 1] + a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i + x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i - x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i + x3r; + } + } + else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = -a[j + 1] + a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] = -a[j + 1] - a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + + +void cft1st(int n, float *a, float *w) +{ + int j, k1, k2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[2]; + x0i = a[1] + a[3]; + x1r = a[0] - a[2]; + x1i = a[1] - a[3]; + x2r = a[4] + a[6]; + x2i = a[5] + a[7]; + x3r = a[4] - a[6]; + x3i = a[5] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[2] = x1r - x3i; + a[3] = x1i + x3r; + a[6] = x1r + x3i; + a[7] = x1i - x3r; + wk1r = w[2]; + x0r = a[8] + a[10]; + x0i = a[9] + a[11]; + x1r = a[8] - a[10]; + x1i = a[9] - a[11]; + x2r = a[12] + a[14]; + x2i = a[13] + a[15]; + x3r = a[12] - a[14]; + x3i = a[13] - a[15]; + a[8] = x0r + x2r; + a[9] = x0i + x2i; + a[12] = x2i - x0i; + a[13] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[10] = wk1r * (x0r - x0i); + a[11] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[14] = wk1r * (x0i - x0r); + a[15] = wk1r * (x0i + x0r); + k1 = 0; + for (j = 16; j < n; j += 16) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + x0r = a[j] + a[j + 2]; + x0i = a[j + 1] + a[j + 3]; + x1r = a[j] - a[j + 2]; + x1i = a[j + 1] - a[j + 3]; + x2r = a[j + 4] + a[j + 6]; + x2i = a[j + 5] + a[j + 7]; + x3r = a[j + 4] - a[j + 6]; + x3i = a[j + 5] - a[j + 7]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 4] = wk2r * x0r - wk2i * x0i; + a[j + 5] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 2] = wk1r * x0r - wk1i * x0i; + a[j + 3] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 6] = wk3r * x0r - wk3i * x0i; + a[j + 7] = wk3r * x0i + wk3i * x0r; + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + x0r = a[j + 8] + a[j + 10]; + x0i = a[j + 9] + a[j + 11]; + x1r = a[j + 8] - a[j + 10]; + x1i = a[j + 9] - a[j + 11]; + x2r = a[j + 12] + a[j + 14]; + x2i = a[j + 13] + a[j + 15]; + x3r = a[j + 12] - a[j + 14]; + x3i = a[j + 13] - a[j + 15]; + a[j + 8] = x0r + x2r; + a[j + 9] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 12] = -wk2i * x0r - wk2r * x0i; + a[j + 13] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 10] = wk1r * x0r - wk1i * x0i; + a[j + 11] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 14] = wk3r * x0r - wk3i * x0i; + a[j + 15] = wk3r * x0i + wk3i * x0r; + } +} + + +void cftmdl(int n, int l, float *a, float *w) +{ + int j, j1, j2, j3, k, k1, k2, m, m2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + m = l << 2; + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + wk1r = w[2]; + for (j = m; j < l + m; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x2i - x0i; + a[j2 + 1] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * (x0r - x0i); + a[j1 + 1] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[j3] = wk1r * (x0i - x0r); + a[j3 + 1] = wk1r * (x0i + x0r); + } + k1 = 0; + m2 = 2 * m; + for (k = m2; k < n; k += m2) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + for (j = k; j < l + k; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = wk2r * x0r - wk2i * x0i; + a[j2 + 1] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + for (j = k + m; j < l + (k + m); j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = -wk2i * x0r - wk2r * x0i; + a[j2 + 1] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + } +} + + +void rftfsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + + +void rftbsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + a[1] = -a[1]; + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j] -= yr; + a[j + 1] = yi - a[j + 1]; + a[k] += yr; + a[k + 1] = yi - a[k + 1]; + } + a[m + 1] = -a[m + 1]; +} + + +void dctsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[j] - wkr * a[k]; + a[j] = wkr * a[j] + wki * a[k]; + a[k] = xr; + } + a[m] *= c[0]; +} + + +void dstsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[k] - wkr * a[j]; + a[k] = wkr * a[k] + wki * a[j]; + a[j] = xr; + } + a[m] *= c[0]; +} + +} diff --git a/thirdparty/timidityplus/filter.cpp b/thirdparty/timidityplus/filter.cpp new file mode 100644 index 000000000..08dbc83cf --- /dev/null +++ b/thirdparty/timidityplus/filter.cpp @@ -0,0 +1,199 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + filter.c: written by Vincent Pagel ( pagel@loria.fr ) + + implements fir antialiasing filter : should help when setting sample + rates as low as 8Khz. + + April 95 + - first draft + + 22/5/95 + - modify "filter" so that it simulate leading and trailing 0 in the buffer + */ + +#include +#include +#include +#include + +#include "common.h" +#include "filter.h" +#include "sysdep.h" + +namespace TimidityPlus +{ + +/* bessel function */ +static double ino(double x) +{ + double y, de, e, sde; + int i; + + y = x / 2; + e = 1.0; + de = 1.0; + i = 1; + do { + de = de * y / (double)i; + sde = de * de; + e += sde; + } while (!((e * 1.0e-08 - sde > 0) || (i++ > 25))); + return(e); +} + +/* Kaiser Window (symetric) */ +static void kaiser(double *w, int n, double beta) +{ + double xind, xi; + int i; + + xind = (2 * n - 1) * (2 * n - 1); + for (i = 0; i < n; i++) + { + xi = i + 0.5; + w[i] = ino((double)(beta * sqrt((double)(1. - 4 * xi * xi / xind)))) + / ino((double)beta); + } +} + +/* + * fir coef in g, cuttoff frequency in fc + */ +static void designfir(double *g, double fc) +{ + int i; + double xi, omega, att, beta; + double w[ORDER2]; + + for (i = 0; i < ORDER2; i++) + { + xi = (double)i + 0.5; + omega = M_PI * xi; + g[i] = sin((double)omega * fc) / omega; + } + + att = 40.; /* attenuation in db */ + beta = (double)exp(log((double)0.58417 * (att - 20.96)) * 0.4) + 0.07886 + * (att - 20.96); + kaiser(w, ORDER2, beta); + + /* Matrix product */ + for (i = 0; i < ORDER2; i++) + g[i] = g[i] * w[i]; +} + +/* + * FIR filtering -> apply the filter given by coef[] to the data buffer + * Note that we simulate leading and trailing 0 at the border of the + * data buffer + */ +static void filter(int16_t *result, int16_t *data, int32_t length, double coef[]) +{ + int32_t sample, i, sample_window; + int16_t peak = 0; + double sum; + + /* Simulate leading 0 at the begining of the buffer */ + for (sample = 0; sample < ORDER2; sample++) + { + sum = 0.0; + sample_window = sample - ORDER2; + + for (i = 0; i < ORDER; i++) + sum += coef[i] * + ((sample_window < 0) ? 0.0 : data[sample_window++]); + + /* Saturation ??? */ + if (sum > 32767.) { sum = 32767.; peak++; } + if (sum < -32768.) { sum = -32768; peak++; } + result[sample] = (int16_t)sum; + } + + /* The core of the buffer */ + for (sample = ORDER2; sample < length - ORDER + ORDER2; sample++) + { + sum = 0.0; + sample_window = sample - ORDER2; + + for (i = 0; i < ORDER; i++) + sum += data[sample_window++] * coef[i]; + + /* Saturation ??? */ + if (sum > 32767.) { sum = 32767.; peak++; } + if (sum < -32768.) { sum = -32768; peak++; } + result[sample] = (int16_t)sum; + } + + /* Simulate 0 at the end of the buffer */ + for (sample = length - ORDER + ORDER2; sample < length; sample++) + { + sum = 0.0; + sample_window = sample - ORDER2; + + for (i = 0; i < ORDER; i++) + sum += coef[i] * + ((sample_window >= length) ? 0.0 : data[sample_window++]); + + /* Saturation ??? */ + if (sum > 32767.) { sum = 32767.; peak++; } + if (sum < -32768.) { sum = -32768; peak++; } + result[sample] = (int16_t)sum; + } +} + +/***********************************************************************/ +/* Prevent aliasing by filtering any freq above the output_rate */ +/* */ +/* I don't worry about looping point -> they will remain soft if they */ +/* were already */ +/***********************************************************************/ +void antialiasing(int16_t *data, int32_t data_length, + int32_t sample_rate, int32_t output_rate) +{ + int16_t *temp; + int i; + double fir_symetric[ORDER]; + double fir_coef[ORDER2]; + double freq_cut; /* cutoff frequency [0..1.0] FREQ_CUT/SAMP_FREQ*/ + + + /* No oversampling */ + if (output_rate >= sample_rate) + return; + + freq_cut = (double)output_rate / (double)sample_rate; + + designfir(fir_coef, freq_cut); + + /* Make the filter symetric */ + for (i = 0; i < ORDER2; i++) + fir_symetric[ORDER - 1 - i] = fir_symetric[i] = fir_coef[ORDER2 - 1 - i]; + + /* We apply the filter we have designed on a copy of the patch */ + temp = (int16_t *)safe_malloc(2 * data_length); + memcpy(temp, data, 2 * data_length); + + filter(data, temp, data_length, fir_symetric); + + free(temp); +} + +} \ No newline at end of file diff --git a/thirdparty/timidityplus/freq.cpp b/thirdparty/timidityplus/freq.cpp new file mode 100644 index 000000000..670b43ea2 --- /dev/null +++ b/thirdparty/timidityplus/freq.cpp @@ -0,0 +1,710 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "freq.h" +#include "fft4g.h" + + +namespace TimidityPlus +{ + + +/* middle C = pitch 60 = 261.6 Hz + freq = 13.75 * exp((pitch - 9) / 12 * log(2)) + pitch = 9 - log(13.75 / freq) * 12 / log(2) + = -36.37631656 + 17.31234049 * log(freq) +*/ +const float pitch_freq_table[129] = { + 8.17579892f, 8.66195722f, 9.17702400f, 9.72271824f, 10.3008612f, 10.9133822f, + 11.5623257f, 12.2498574f, 12.9782718f, 13.7500000f, 14.5676175f, 15.4338532f, + + 16.3515978f, 17.3239144f, 18.3540480f, 19.4454365f, 20.6017223f, 21.8267645f, + 23.1246514f, 24.4997147f, 25.9565436f, 27.5000000f, 29.1352351f, 30.8677063f, + + 32.7031957f, 34.6478289f, 36.7080960f, 38.8908730f, 41.2034446f, 43.6535289f, + 46.2493028f, 48.9994295f, 51.9130872f, 55.0000000f, 58.2704702f, 61.7354127f, + + 65.4063913f, 69.2956577f, 73.4161920f, 77.7817459f, 82.4068892f, 87.3070579f, + 92.4986057f, 97.9988590f, 103.826174f, 110.000000f, 116.540940f, 123.470825f, + + 130.812783f, 138.591315f, 146.832384f, 155.563492f, 164.813778f, 174.614116f, + 184.997211f, 195.997718f, 207.652349f, 220.000000f, 233.081881f, 246.941651f, + + 261.625565f, 277.182631f, 293.664768f, 311.126984f, 329.627557f, 349.228231f, + 369.994423f, 391.995436f, 415.304698f, 440.000000f, 466.163762f, 493.883301f, + + 523.251131f, 554.365262f, 587.329536f, 622.253967f, 659.255114f, 698.456463f, + 739.988845f, 783.990872f, 830.609395f, 880.000000f, 932.327523f, 987.766603f, + + 1046.50226f, 1108.73052f, 1174.65907f, 1244.50793f, 1318.51023f, 1396.91293f, + 1479.97769f, 1567.98174f, 1661.21879f, 1760.00000f, 1864.65505f, 1975.53321f, + + 2093.00452f, 2217.46105f, 2349.31814f, 2489.01587f, 2637.02046f, 2793.82585f, + 2959.95538f, 3135.96349f, 3322.43758f, 3520.00000f, 3729.31009f, 3951.06641f, + + 4186.00904f, 4434.92210f, 4698.63629f, 4978.03174f, 5274.04091f, 5587.65170f, + 5919.91076f, 6271.92698f, 6644.87516f, 7040.00000f, 7458.62018f, 7902.13282f, + + 8372.01809f, 8869.84419f, 9397.27257f, 9956.06348f, 10548.0818f, 11175.3034f, + 11839.8215f, 12543.8540f, 13289.7503f +}; + + + +/* center_pitch + 0.49999 */ +const float pitch_freq_ub_table[129] = { + 8.41536325f, 8.91576679f, 9.44592587f, 10.0076099f, 10.6026933f, 11.2331623f, + 11.9011208f, 12.6087983f, 13.3585565f, 14.1528976f, 14.9944727f, 15.8860904f, + 16.8307265f, 17.8315336f, 18.8918517f, 20.0152197f, 21.2053866f, 22.4663245f, + 23.8022417f, 25.2175966f, 26.7171129f, 28.3057952f, 29.9889453f, 31.7721808f, + 33.6614530f, 35.6630672f, 37.7837035f, 40.0304394f, 42.4107732f, 44.9326490f, + 47.6044834f, 50.4351932f, 53.4342259f, 56.6115903f, 59.9778907f, 63.5443616f, + 67.3229060f, 71.3261343f, 75.5674070f, 80.0608788f, 84.8215464f, 89.8652980f, + 95.2089667f, 100.870386f, 106.868452f, 113.223181f, 119.955781f, 127.088723f, + 134.645812f, 142.652269f, 151.134814f, 160.121758f, 169.643093f, 179.730596f, + 190.417933f, 201.740773f, 213.736904f, 226.446361f, 239.911563f, 254.177446f, + 269.291624f, 285.304537f, 302.269628f, 320.243515f, 339.286186f, 359.461192f, + 380.835867f, 403.481546f, 427.473807f, 452.892723f, 479.823125f, 508.354893f, + 538.583248f, 570.609074f, 604.539256f, 640.487030f, 678.572371f, 718.922384f, + 761.671734f, 806.963092f, 854.947614f, 905.785445f, 959.646250f, 1016.70979f, + 1077.16650f, 1141.21815f, 1209.07851f, 1280.97406f, 1357.14474f, 1437.84477f, + 1523.34347f, 1613.92618f, 1709.89523f, 1811.57089f, 1919.29250f, 2033.41957f, + 2154.33299f, 2282.43630f, 2418.15702f, 2561.94812f, 2714.28948f, 2875.68954f, + 3046.68693f, 3227.85237f, 3419.79046f, 3623.14178f, 3838.58500f, 4066.83914f, + 4308.66598f, 4564.87260f, 4836.31405f, 5123.89624f, 5428.57897f, 5751.37907f, + 6093.37387f, 6455.70474f, 6839.58092f, 7246.28356f, 7677.17000f, 8133.67829f, + 8617.33197f, 9129.74519f, 9672.62809f, 10247.7925f, 10857.1579f, 11502.7581f, + 12186.7477f, 12911.4095f, 13679.1618f +}; + + + +/* center_pitch - 0.49999 */ +const float pitch_freq_lb_table[129] = { + 7.94305438f, 8.41537297f, 8.91577709f, 9.44593678f, 10.0076214f, 10.6027055f, + 11.2331752f, 11.9011346f, 12.6088129f, 13.3585719f, 14.1529139f, 14.9944900f, + 15.8861088f, 16.8307459f, 17.8315542f, 18.8918736f, 20.0152428f, 21.2054111f, + 22.4663505f, 23.8022692f, 25.2176258f, 26.7171438f, 28.3058279f, 29.9889800f, + 31.7722175f, 33.6614919f, 35.6631084f, 37.7837471f, 40.0304857f, 42.4108222f, + 44.9327009f, 47.6045384f, 50.4352515f, 53.4342876f, 56.6116557f, 59.9779599f, + 63.5444350f, 67.3229838f, 71.3262167f, 75.5674943f, 80.0609713f, 84.8216444f, + 89.8654018f, 95.2090767f, 100.870503f, 106.868575f, 113.223311f, 119.955920f, + 127.088870f, 134.645968f, 142.652433f, 151.134989f, 160.121943f, 169.643289f, + 179.730804f, 190.418153f, 201.741006f, 213.737151f, 226.446623f, 239.911840f, + 254.177740f, 269.291935f, 285.304867f, 302.269977f, 320.243885f, 339.286578f, + 359.461607f, 380.836307f, 403.482012f, 427.474301f, 452.893246f, 479.823680f, + 508.355480f, 538.583870f, 570.609734f, 604.539954f, 640.487770f, 678.573155f, + 718.923215f, 761.672614f, 806.964024f, 854.948602f, 905.786491f, 959.647359f, + 1016.71096f, 1077.16774f, 1141.21947f, 1209.07991f, 1280.97554f, 1357.14631f, + 1437.84643f, 1523.34523f, 1613.92805f, 1709.89720f, 1811.57298f, 1919.29472f, + 2033.42192f, 2154.33548f, 2282.43893f, 2418.15982f, 2561.95108f, 2714.29262f, + 2875.69286f, 3046.69045f, 3227.85610f, 3419.79441f, 3623.14597f, 3838.58944f, + 4066.84384f, 4308.67096f, 4564.87787f, 4836.31963f, 5123.90216f, 5428.58524f, + 5751.38572f, 6093.38091f, 6455.71219f, 6839.58882f, 7246.29193f, 7677.17887f, + 8133.68768f, 8617.34192f, 9129.75574f, 9672.63927f, 10247.8043f, 10857.1705f, + 11502.7714f, 12186.7618f, 12911.4244f +}; + + + +/* (M)ajor, rotate back 1, rotate back 2 + (m)inor, rotate back 1, rotate back 2 + (d)iminished minor, rotate back 1, rotate back 2 + (f)ifth, rotate back 1, rotate back 2 +*/ +const int chord_table[4][3][3] = { + { { 0, 4, 7, }, { -5, 0, 4, }, { -8, -5, 0, }, }, + { { 0, 3, 7, }, { -5, 0, 3, }, { -9, -5, 0, }, }, + { { 0, 3, 6, }, { -6, 0, 3, }, { -9, -6, 0, }, }, + { { 0, 5, 7, }, { -5, 0, 5, }, { -7, -5, 0, }, }, +}; + +/* write the chord type to *chord, returns the root note of the chord */ +int Freq::assign_chord(double *pitchbins, int *chord, + int min_guesspitch, int max_guesspitch, int root_pitch) +{ + + int type, subtype; + int pitches[19] = { 0 }; + int prune_pitches[10] = { 0 }; + int i, j, k, n, n2; + double val, cutoff, max; + int root_flag; + + *chord = -1; + + if (root_pitch - 9 > min_guesspitch) + min_guesspitch = root_pitch - 9; + + if (min_guesspitch <= LOWEST_PITCH) + min_guesspitch = LOWEST_PITCH + 1; + + if (root_pitch + 9 < max_guesspitch) + max_guesspitch = root_pitch + 9; + + if (max_guesspitch >= HIGHEST_PITCH) + max_guesspitch = HIGHEST_PITCH - 1; + + /* keep only local maxima */ + for (i = min_guesspitch, n = 0; i <= max_guesspitch; i++) + { + val = pitchbins[i]; + if (val) + { + if (pitchbins[i - 1] < val && pitchbins[i + 1] < val) + pitches[n++] = i; + } + } + + if (n < 3) + return -1; + + /* find largest peak */ + max = -1; + for (i = 0; i < n; i++) + { + val = pitchbins[pitches[i]]; + if (val > max) + max = val; + } + + /* discard any peaks below cutoff */ + cutoff = 0.2 * max; + for (i = 0, n2 = 0, root_flag = 0; i < n; i++) + { + val = pitchbins[pitches[i]]; + if (val >= cutoff) + { + prune_pitches[n2++] = pitches[i]; + if (pitches[i] == root_pitch) + root_flag = 1; + } + } + + if (!root_flag || n2 < 3) + return -1; + + /* search for a chord, must contain root pitch */ + for (i = 0; i < n2; i++) + { + for (subtype = 0; subtype < 3; subtype++) + { + if (i + subtype >= n2) + continue; + + for (type = 0; type < 4; type++) + { + for (j = 0, n = 0, root_flag = 0; j < 3; j++) + { + k = i + j; + + if (k >= n2) + continue; + + if (prune_pitches[k] == root_pitch) + root_flag = 1; + + if (prune_pitches[k] - prune_pitches[i + subtype] == + chord_table[type][subtype][j]) + n++; + } + if (root_flag && n == 3) + { + *chord = 3 * type + subtype; + return prune_pitches[i + subtype]; + } + } + } + } + + return -1; +} + + + +/* initialize FFT arrays for the frequency analysis */ +int Freq::freq_initialize_fft_arrays(Sample *sp) +{ + + uint32_t i; + uint32_t length, newlength; + unsigned int rate; + sample_t *origdata; + + rate = sp->sample_rate; + length = sp->data_length >> FRACTION_BITS; + origdata = sp->data; + + /* copy the sample to a new float array */ + floatData.resize(length); + for (i = 0; i < length; i++) + floatData[i] = origdata[i]; + + /* length must be a power of 2 */ + /* set it to smallest power of 2 >= 1.4*rate */ + /* at least 1.4*rate is required for decent resolution of low notes */ + newlength = pow(2, ceil(log(1.4*rate) / log(2))); + if (length < newlength) + { + floatData.resize(newlength); + memset(&floatData[0] + length, 0, (newlength - length) * sizeof(float)); + } + length = newlength; + + /* allocate FFT arrays */ + /* calculate sin/cos and fft1_bin_to_pitch tables */ + if (length != oldfftsize) + { + float f0; + + magData.resize(length); + pruneMagData.resize(length); + ipa.resize(int(2 + sqrt(length)) * sizeof(int)); + ipa[0] = 0; + wa.resize(length >> 1); + fft1BinToPitch.resize(length >> 1); + + for (i = 1, f0 = (float)rate / length; i < (length >> 1); i++) { + fft1BinToPitch[i] = assign_pitch_to_freq(i * f0); + } + } + oldfftsize = length; + + /* zero out arrays that need it */ + memset(pitchmags, 0, 129 * sizeof(float)); + memset(pitchbins, 0, 129 * sizeof(double)); + memset(new_pitchbins, 0, 129 * sizeof(double)); + memset(&pruneMagData[0], 0, length * sizeof(float)); + + return(length); +} + + + +/* return the frequency of the sample */ +/* max of 1.4 - 2.0 seconds of audio is analyzed, depending on sample rate */ +/* samples < 1.4 seconds are padded to max length for higher fft accuracy */ +float Freq::freq_fourier(Sample *sp, int *chord) +{ + + int32_t length, length0; + int32_t maxoffset, minoffset, minoffset1, minoffset2; + int32_t minbin, maxbin; + int32_t bin; + int32_t i; + int32_t j, n, total; + unsigned int rate; + int pitch, bestpitch, minpitch, maxpitch, maxpitch2; + sample_t *origdata; + float f0, mag, maxmag; + int16_t amp, oldamp, maxamp; + int32_t maxpos = 0; + double sum, weightsum, maxsum; + double f0_inv; + float freq, newfreq, bestfreq, freq_inc; + float minfreq, maxfreq, minfreq2, maxfreq2; + float min_guessfreq, max_guessfreq; + + rate = sp->sample_rate; + length = length0 = sp->data_length >> FRACTION_BITS; + origdata = sp->data; + + length = freq_initialize_fft_arrays(sp); + + /* base frequency of the FFT */ + f0 = (float)rate / length; + f0_inv = 1.0 / f0; + + /* get maximum amplitude */ + maxamp = -1; + for (int32_t i = 0; i < length0; i++) + { + amp = abs(origdata[i]); + if (amp >= maxamp) + { + maxamp = amp; + maxpos = i; + } + } + + /* go out 2 zero crossings in both directions, starting at maxpos */ + /* find the peaks after the 2nd crossing */ + minoffset1 = 0; + for (n = 0, oldamp = origdata[maxpos], i = maxpos - 1; i >= 0 && n < 2; i--) + { + amp = origdata[i]; + if ((oldamp && amp == 0) || (oldamp > 0 && amp < 0) || + (oldamp < 0 && amp > 0)) + n++; + oldamp = amp; + } + minoffset1 = i; + maxamp = labs(origdata[i]); + while (i >= 0) + { + amp = origdata[i]; + if ((oldamp && amp == 0) || (oldamp > 0 && amp < 0) || + (oldamp < 0 && amp > 0)) + { + break; + } + oldamp = amp; + + amp = labs(amp); + if (amp > maxamp) + { + maxamp = amp; + minoffset1 = i; + } + i--; + } + + minoffset2 = 0; + for (n = 0, oldamp = origdata[maxpos], i = maxpos + 1; i < length0 && n < 2; i++) + { + amp = origdata[i]; + if ((oldamp && amp == 0) || (oldamp > 0 && amp < 0) || + (oldamp < 0 && amp > 0)) + n++; + oldamp = amp; + } + minoffset2 = i; + maxamp = labs(origdata[i]); + while (i < length0) + { + amp = origdata[i]; + if ((oldamp && amp == 0) || (oldamp > 0 && amp < 0) || + (oldamp < 0 && amp > 0)) + { + break; + } + oldamp = amp; + + amp = labs(amp); + if (amp > maxamp) + { + maxamp = amp; + minoffset2 = i; + } + i++; + } + + /* upper bound on the detected frequency */ + /* distance between the two peaks is at most 2 periods */ + minoffset = (minoffset2 - minoffset1); + if (minoffset < 4) + minoffset = 4; + max_guessfreq = (float)rate / (minoffset * 0.5); + if (max_guessfreq >= (rate >> 1)) max_guessfreq = (rate >> 1) - 1; + + /* lower bound on the detected frequency */ + maxoffset = rate / pitch_freq_lb_table[LOWEST_PITCH] + 0.5; + if (maxoffset > (length >> 1)) + maxoffset = (length >> 1); + min_guessfreq = (float)rate / maxoffset; + + /* perform the in place FFT */ + rdft(length, 1, &floatData[0], &ipa[0], &wa[0]); + + /* calc the magnitudes */ + for (i = 2; i < length; i++) + { + mag = floatData[i++]; + mag *= mag; + mag += floatData[i] * floatData[i]; + magData[i >> 1] = sqrt(mag); + } + + /* find max mag */ + maxmag = 0; + for (i = 1; i < (length >> 1); i++) + { + mag = magData[i]; + + pitch = fft1BinToPitch[i]; + if (pitch && mag > maxmag) + maxmag = mag; + } + + /* Apply non-linear scaling to the magnitudes + * I don't know why this improves the pitch detection, but it does + * The best choice of power seems to be between 1.64 - 1.68 + */ + for (i = 1; i < (length >> 1); i++) + magData[i] = maxmag * pow(magData[i] / maxmag, 1.66); + + /* bin the pitches */ + for (i = 1; i < (length >> 1); i++) + { + mag = magData[i]; + + pitch = fft1BinToPitch[i]; + pitchbins[pitch] += mag; + + if (mag > pitchmags[pitch]) + pitchmags[pitch] = mag; + } + + /* zero out lowest pitch, since it contains all lower frequencies too */ + pitchbins[LOWEST_PITCH] = 0; + + /* find the largest peak */ + for (i = LOWEST_PITCH + 1, maxsum = -42; i <= HIGHEST_PITCH; i++) + { + sum = pitchbins[i]; + if (sum > maxsum) + { + maxsum = sum; + } + } + + minpitch = assign_pitch_to_freq(min_guessfreq); + if (minpitch > HIGHEST_PITCH) minpitch = HIGHEST_PITCH; + + /* zero out any peak below minpitch */ + for (int i = LOWEST_PITCH + 1; i < minpitch; i++) + pitchbins[i] = 0; + + /* remove all pitches below threshold */ + for (int i = minpitch; i <= HIGHEST_PITCH; i++) + { + if (pitchbins[i] / maxsum < 0.01 && pitchmags[i] / maxmag < 0.01) + pitchbins[i] = 0; + } + + /* keep local maxima */ + for (int i = LOWEST_PITCH + 1; i < HIGHEST_PITCH; i++) + { + double temp; + + temp = pitchbins[i]; + + /* also keep significant bands to either side */ + if (temp && pitchbins[i - 1] < temp && pitchbins[i + 1] < temp) + { + new_pitchbins[i] = temp; + + temp *= 0.5; + if (pitchbins[i - 1] >= temp) + new_pitchbins[i - 1] = pitchbins[i - 1]; + if (pitchbins[i + 1] >= temp) + new_pitchbins[i + 1] = pitchbins[i - 1]; + } + } + memcpy(pitchbins, new_pitchbins, 129 * sizeof(double)); + + /* find lowest and highest pitches */ + minpitch = LOWEST_PITCH; + while (minpitch < HIGHEST_PITCH && !pitchbins[minpitch]) + minpitch++; + maxpitch = HIGHEST_PITCH; + while (maxpitch > LOWEST_PITCH && !pitchbins[maxpitch]) + maxpitch--; + + /* uh oh, no pitches left... + * best guess is middle C + * return 260 Hz, since exactly 260 Hz is never returned except on error + * this should only occur on blank/silent samples + */ + if (maxpitch < minpitch) + { + return 260.0; + } + + /* pitch assignment bounds based on zero crossings and pitches kept */ + if (pitch_freq_lb_table[minpitch] > min_guessfreq) + min_guessfreq = pitch_freq_lb_table[minpitch]; + if (pitch_freq_ub_table[maxpitch] < max_guessfreq) + max_guessfreq = pitch_freq_ub_table[maxpitch]; + + minfreq = pitch_freq_lb_table[minpitch]; + if (minfreq >= (rate >> 1)) minfreq = (rate >> 1) - 1; + + maxfreq = pitch_freq_ub_table[maxpitch]; + if (maxfreq >= (rate >> 1)) maxfreq = (rate >> 1) - 1; + + minbin = minfreq / f0; + if (!minbin) + minbin = 1; + maxbin = ceil(maxfreq / f0); + if (maxbin >= (length >> 1)) + maxbin = (length >> 1) - 1; + + /* filter out all "noise" from magnitude array */ + for (int i = minbin, n = 0; i <= maxbin; i++) + { + pitch = fft1BinToPitch[i]; + if (pitchbins[pitch]) + { + pruneMagData[i] = magData[i]; + n++; + } + } + + /* whoa!, there aren't any strong peaks at all !!! bomb early + * best guess is middle C + * return 260 Hz, since exactly 260 Hz is never returned except on error + * this should only occur on blank/silent samples + */ + if (!n) + { + return 260.0; + } + + memset(new_pitchbins, 0, 129 * sizeof(double)); + + maxsum = -1; + minpitch = assign_pitch_to_freq(min_guessfreq); + maxpitch = assign_pitch_to_freq(max_guessfreq); + maxpitch2 = assign_pitch_to_freq(max_guessfreq) + 9; + if (maxpitch2 > HIGHEST_PITCH) maxpitch2 = HIGHEST_PITCH; + + /* initial guess is first local maximum */ + bestfreq = pitch_freq_table[minpitch]; + if (minpitch < HIGHEST_PITCH && + pitchbins[minpitch + 1] > pitchbins[minpitch]) + bestfreq = pitch_freq_table[minpitch + 1]; + + /* find best fundamental */ + for (int i = minpitch; i <= maxpitch2; i++) + { + if (!pitchbins[i]) + continue; + + minfreq2 = pitch_freq_lb_table[i]; + maxfreq2 = pitch_freq_ub_table[i]; + freq_inc = (maxfreq2 - minfreq2) * 0.1; + if (minfreq2 >= (rate >> 1)) minfreq2 = (rate >> 1) - 1; + if (maxfreq2 >= (rate >> 1)) maxfreq2 = (rate >> 1) - 1; + + /* look for harmonics */ + for (freq = minfreq2; freq <= maxfreq2; freq += freq_inc) + { + n = total = 0; + sum = weightsum = 0; + + for (j = 1; j <= 32 && (newfreq = j * freq) <= maxfreq; j++) + { + pitch = assign_pitch_to_freq(newfreq); + + if (pitchbins[pitch]) + { + sum += pitchbins[pitch]; + n++; + total = j; + } + } + + /* only pitches with good harmonics are assignment candidates */ + if (n > 1) + { + double ratio; + + ratio = (double)n / total; + if (ratio >= 0.333333) + { + weightsum = ratio * sum; + pitch = assign_pitch_to_freq(freq); + + /* use only these pitches for chord detection */ + if (pitch <= HIGHEST_PITCH && pitchbins[pitch]) + new_pitchbins[pitch] = weightsum; + + if (pitch > maxpitch) + continue; + + if (n < 2 || weightsum > maxsum) + { + maxsum = weightsum; + bestfreq = freq; + } + } + } + } + } + + bestpitch = assign_pitch_to_freq(bestfreq); + + /* assign chords */ + if ((pitch = assign_chord(new_pitchbins, chord, + bestpitch - 9, maxpitch2, bestpitch)) >= 0) + bestpitch = pitch; + + bestfreq = pitch_freq_table[bestpitch]; + + /* tune based on the fundamental and harmonics up to +5 octaves */ + sum = weightsum = 0; + for (i = 1; i <= 32 && (freq = i * bestfreq) <= maxfreq; i++) + { + double tune; + + minfreq2 = pitch_freq_lb_table[bestpitch]; + maxfreq2 = pitch_freq_ub_table[bestpitch]; + + minbin = minfreq2 * f0_inv; + if (!minbin) minbin = 1; + maxbin = ceil(maxfreq2 * f0_inv); + if (maxbin >= (length >> 1)) + maxbin = (length >> 1) - 1; + + for (bin = minbin; bin <= maxbin; bin++) + { + tune = -36.37631656 + 17.31234049 * log(bin*f0) - bestpitch; + sum += magData[bin]; + weightsum += magData[bin] * tune; + } + } + + bestfreq = 13.75 * exp(((bestpitch + weightsum / sum) - 9) / + 12 * log(2)); + + /* Since we are using exactly 260 Hz as an error code, fudge the freq + * on the extremely unlikely chance that the detected pitch is exactly + * 260 Hz. + */ + if (bestfreq == 260.0f) + bestfreq += 1E-5f; + + return bestfreq; +} + + + +int assign_pitch_to_freq(float freq) +{ + /* round to nearest integer using: ceil(fraction - 0.5) */ + /* -0.5 is already added into the first constant below */ + int pitch = ceil(-36.87631656f + 17.31234049f * log(freq)); + + /* min and max pitches */ + if (pitch < LOWEST_PITCH) pitch = LOWEST_PITCH; + else if (pitch > HIGHEST_PITCH) pitch = HIGHEST_PITCH; + + return pitch; +} + +} \ No newline at end of file diff --git a/thirdparty/timidityplus/instrum.cpp b/thirdparty/timidityplus/instrum.cpp new file mode 100644 index 000000000..245bc5b89 --- /dev/null +++ b/thirdparty/timidityplus/instrum.cpp @@ -0,0 +1,2050 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + instrum.c + + Code to load and unload GUS-compatible instrument patches. +*/ + +#include +#include +#include +#include + +#include "../../../source/zmusic/fileio.h" +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "resample.h" +#include "tables.h" +#include "filter.h" +#include "quantity.h" +#include "freq.h" + +namespace TimidityPlus +{ + + +Instruments::Instruments() +{ + // init one-time global stuff - this should go to the device class once it exists. + initialize_resampler_coeffs(); + init_tables(); + + memset(&standard_tonebank, 0, sizeof(standard_tonebank)); + memset(&standard_drumset, 0, sizeof(standard_drumset)); + memcpy(layer_items, static_layer_items, sizeof(layer_items)); +} + +bool Instruments::load(MusicIO::SoundFontReaderInterface *sf) +{ + sfreader = sf; + if (read_config_file(nullptr, 0, 0) == RC_OK) + { + init_load_soundfont(); + set_default_instrument(); + return true; + } + return false; +} + +Instruments::~Instruments() +{ + free_instruments(0); + free_soundfonts(); + + free_tone_bank(); + free_instrument_map(); + + if (sfreader != nullptr) sfreader->close(); +} + +void Instruments::free_instrument(Instrument *ip) +{ + Sample *sp; + int i; + if (!ip) return; + + for (i = 0; isamples; i++) + { + sp = &(ip->sample[i]); + if (sp->data_alloced) + free(sp->data); + } + free(ip->sample); + free(ip); +} + + + +/* calculate ramp rate in fractional unit; + * diff = 8bit, time = msec + */ +int32_t Instruments::calc_rate_i(int diff, double msec) +{ + double rate; + + if(msec < 6) + msec = 6; + if(diff == 0) + diff = 255; + diff <<= (7+15); + rate = ((double)diff / playback_rate) * control_ratio * 1000.0 / msec; + if(fast_decay) + rate *= 2; + return (int32_t)rate; +} +/*End of Pseudo Reverb*/ + + +void Instruments::clear_magic_instruments(void) +{ + int i, j; + + for (j = 0; j < 128 + map_bank_counter; j++) + { + if (tonebank[j]) + { + ToneBank *bank = tonebank[j]; + for (i = 0; i < 128; i++) + if (IS_MAGIC_INSTRUMENT(bank->tone[i].instrument)) + bank->tone[i].instrument = NULL; + } + if (drumset[j]) + { + ToneBank *bank = drumset[j]; + for (i = 0; i < 128; i++) + if (IS_MAGIC_INSTRUMENT(bank->tone[i].instrument)) + bank->tone[i].instrument = NULL; + } + } +} + + +int32_t Instruments::convert_envelope_rate(uint8_t rate) +{ + const int32_t GUS_ENVRATE_MAX = (int32_t)(0x3FFFFFFF >> 9); + + int32_t r; + + r = 3 - ((rate >> 6) & 0x3); + r *= 3; + r = (int32_t)(rate & 0x3f) << r; /* 6.9 fixed point */ + + /* 15.15 fixed point. */ + r = r * 44100 / playback_rate * control_ratio * (1 << fast_decay); + if (r > GUS_ENVRATE_MAX) { r = GUS_ENVRATE_MAX; } + return (r << 9); +} + +int32_t Instruments::convert_envelope_offset(uint8_t offset) +{ + /* This is not too good... Can anyone tell me what these values mean? + Are they GUS-style "exponential" volumes? And what does that mean? */ + + /* 15.15 fixed point */ + return offset << (7 + 15); +} + +int32_t Instruments::convert_tremolo_sweep(uint8_t sweep) +{ + if (!sweep) + return 0; + + return + ((control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (playback_rate * sweep); +} + +int32_t Instruments::convert_vibrato_sweep(uint8_t sweep, int32_t vib_control_ratio) +{ + if (!sweep) + return 0; + + return (int32_t)(TIM_FSCALE((double) (vib_control_ratio) + * SWEEP_TUNING, SWEEP_SHIFT) + / (double)(playback_rate * sweep)); + + /* this was overflowing with seashore.pat + + ((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (playback_rate * sweep); */ +} + +int32_t Instruments::convert_tremolo_rate(uint8_t rate) +{ + return + ((SINE_CYCLE_LENGTH * control_ratio * rate) << RATE_SHIFT) / + (TREMOLO_RATE_TUNING * playback_rate); +} + +int32_t Instruments::convert_vibrato_rate(uint8_t rate) +{ + /* Return a suitable vibrato_control_ratio value */ + return + (VIBRATO_RATE_TUNING * playback_rate) / + (rate * 2 * VIBRATO_SAMPLE_INCREMENTS); +} + +void Instruments::reverse_data(int16_t *sp, int32_t ls, int32_t le) +{ + int16_t s, *ep = sp + le; + int32_t i; + sp += ls; + le -= ls; + le /= 2; + for (i = 0; i < le; i++) + { + s = *sp; + *sp++ = *ep; + *ep-- = s; + } +} + +int Instruments::name_hash(char *name) +{ + unsigned int addr = 0; + + while(*name) + addr += *name++; + return addr % INSTRUMENT_HASH_SIZE; +} + +Instrument *Instruments::search_instrument_cache(char *name, int panning, int amp, int note_to_use, int strip_loop, int strip_envelope, int strip_tail) +{ + struct InstrumentCache *p; + + for (p = instrument_cache[name_hash(name)]; p != NULL; p = p->next) + { + if (strcmp(p->name, name) != 0) + return NULL; + if (p->panning == panning && + p->amp == amp && + p->note_to_use == note_to_use && + p->strip_loop == strip_loop && + p->strip_envelope == strip_envelope && + p->strip_tail == strip_tail) + return p->ip; + } + return NULL; +} + +void Instruments::store_instrument_cache(Instrument *ip, char *name, int panning, int amp, int note_to_use, int strip_loop, int strip_envelope, int strip_tail) +{ + struct InstrumentCache *p; + int addr; + + addr = name_hash(name); + p = (struct InstrumentCache *)safe_malloc(sizeof(struct InstrumentCache)); + p->next = instrument_cache[addr]; + instrument_cache[addr] = p; + p->name = name; + p->panning = panning; + p->amp = amp; + p->note_to_use = note_to_use; + p->strip_loop = strip_loop; + p->strip_envelope = strip_envelope; + p->strip_tail = strip_tail; + p->ip = ip; +} + +static int32_t adjust_tune_freq(int32_t val, float tune) +{ + if (! tune) + return val; + return val / pow(2.0, tune / 12.0); +} + +static int16_t adjust_scale_tune(int16_t val) +{ + return 1024 * (double) val / 100 + 0.5; +} + +static int16_t adjust_fc(int16_t val) +{ + if (val < 0 || val > playback_rate / 2) { + return 0; + } else { + return val; + } +} + +static int16_t adjust_reso(int16_t val) +{ + if (val < 0 || val > 960) { + return 0; + } else { + return val; + } +} + +int32_t Instruments::to_rate(int rate) +{ + return (rate) ? (int32_t) (0x200 * pow(2.0, rate / 17.0) + * 44100 / playback_rate * control_ratio) << fast_decay : 0; +} + + +void Instruments::apply_bank_parameter(Instrument *ip, ToneBankElement *tone) +{ + int i, j; + Sample *sp; + + if (tone->tunenum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->tunenum == 1) { + sp->low_freq = adjust_tune_freq(sp->low_freq, tone->tune[0]); + sp->high_freq = adjust_tune_freq(sp->high_freq, tone->tune[0]); + sp->root_freq = adjust_tune_freq(sp->root_freq, tone->tune[0]); + } else if (i < tone->tunenum) { + sp->low_freq = adjust_tune_freq(sp->low_freq, tone->tune[i]); + sp->high_freq = adjust_tune_freq(sp->high_freq, tone->tune[i]); + sp->root_freq = adjust_tune_freq(sp->root_freq, tone->tune[i]); + } + } + if (tone->envratenum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->envratenum == 1) { + for (j = 0; j < 6; j++) + if (tone->envrate[0][j] >= 0) + sp->envelope_rate[j] = to_rate(tone->envrate[0][j]); + } else if (i < tone->envratenum) { + for (j = 0; j < 6; j++) + if (tone->envrate[i][j] >= 0) + sp->envelope_rate[j] = to_rate(tone->envrate[i][j]); + } + } + if (tone->envofsnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->envofsnum == 1) { + for (j = 0; j < 6; j++) + if (tone->envofs[0][j] >= 0) + sp->envelope_offset[j] = to_offset_22(tone->envofs[0][j]); + } else if (i < tone->envofsnum) { + for (j = 0; j < 6; j++) + if (tone->envofs[i][j] >= 0) + sp->envelope_offset[j] = to_offset_22(tone->envofs[i][j]); + } + } + if (tone->tremnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->tremnum == 1) { + if (IS_QUANTITY_DEFINED(tone->trem[0][0])) + sp->tremolo_sweep_increment = + quantity_to_int(&tone->trem[0][0], 0); + if (IS_QUANTITY_DEFINED(tone->trem[0][1])) + sp->tremolo_phase_increment = + quantity_to_int(&tone->trem[0][1], 0); + if (IS_QUANTITY_DEFINED(tone->trem[0][2])) + sp->tremolo_depth = + quantity_to_int(&tone->trem[0][2], 0) << 1; + } else if (i < tone->tremnum) { + if (IS_QUANTITY_DEFINED(tone->trem[i][0])) + sp->tremolo_sweep_increment = + quantity_to_int(&tone->trem[i][0], 0); + if (IS_QUANTITY_DEFINED(tone->trem[i][1])) + sp->tremolo_phase_increment = + quantity_to_int(&tone->trem[i][1], 0); + if (IS_QUANTITY_DEFINED(tone->trem[i][2])) + sp->tremolo_depth = + quantity_to_int(&tone->trem[i][2], 0) << 1; + } + } + if (tone->vibnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->vibnum == 1) { + if (IS_QUANTITY_DEFINED(tone->vib[0][1])) + sp->vibrato_control_ratio = + quantity_to_int(&tone->vib[0][1], 0); + if (IS_QUANTITY_DEFINED(tone->vib[0][0])) + sp->vibrato_sweep_increment = + quantity_to_int(&tone->vib[0][0], + sp->vibrato_control_ratio); + if (IS_QUANTITY_DEFINED(tone->vib[0][2])) + sp->vibrato_depth = quantity_to_int(&tone->vib[0][2], 0); + } else if (i < tone->vibnum) { + if (IS_QUANTITY_DEFINED(tone->vib[i][1])) + sp->vibrato_control_ratio = + quantity_to_int(&tone->vib[i][1], 0); + if (IS_QUANTITY_DEFINED(tone->vib[i][0])) + sp->vibrato_sweep_increment = + quantity_to_int(&tone->vib[i][0], + sp->vibrato_control_ratio); + if (IS_QUANTITY_DEFINED(tone->vib[i][2])) + sp->vibrato_depth = quantity_to_int(&tone->vib[i][2], 0); + } + } + if (tone->sclnotenum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->sclnotenum == 1) + sp->scale_freq = tone->sclnote[0]; + else if (i < tone->sclnotenum) + sp->scale_freq = tone->sclnote[i]; + } + if (tone->scltunenum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->scltunenum == 1) + sp->scale_factor = adjust_scale_tune(tone->scltune[0]); + else if (i < tone->scltunenum) + sp->scale_factor = adjust_scale_tune(tone->scltune[i]); + } + if (tone->modenvratenum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modenvratenum == 1) { + for (j = 0; j < 6; j++) + if (tone->modenvrate[0][j] >= 0) + sp->modenv_rate[j] = to_rate(tone->modenvrate[0][j]); + } else if (i < tone->modenvratenum) { + for (j = 0; j < 6; j++) + if (tone->modenvrate[i][j] >= 0) + sp->modenv_rate[j] = to_rate(tone->modenvrate[i][j]); + } + } + if (tone->modenvofsnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modenvofsnum == 1) { + for (j = 0; j < 6; j++) + if (tone->modenvofs[0][j] >= 0) + sp->modenv_offset[j] = + to_offset_22(tone->modenvofs[0][j]); + } else if (i < tone->modenvofsnum) { + for (j = 0; j < 6; j++) + if (tone->modenvofs[i][j] >= 0) + sp->modenv_offset[j] = + to_offset_22(tone->modenvofs[i][j]); + } + } + if (tone->envkeyfnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->envkeyfnum == 1) { + for (j = 0; j < 6; j++) + if (tone->envkeyf[0][j] != -1) + sp->envelope_keyf[j] = tone->envkeyf[0][j]; + } else if (i < tone->envkeyfnum) { + for (j = 0; j < 6; j++) + if (tone->envkeyf[i][j] != -1) + sp->envelope_keyf[j] = tone->envkeyf[i][j]; + } + } + if (tone->envvelfnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->envvelfnum == 1) { + for (j = 0; j < 6; j++) + if (tone->envvelf[0][j] != -1) + sp->envelope_velf[j] = tone->envvelf[0][j]; + } else if (i < tone->envvelfnum) { + for (j = 0; j < 6; j++) + if (tone->envvelf[i][j] != -1) + sp->envelope_velf[j] = tone->envvelf[i][j]; + } + } + if (tone->modenvkeyfnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modenvkeyfnum == 1) { + for (j = 0; j < 6; j++) + if (tone->modenvkeyf[0][j] != -1) + sp->modenv_keyf[j] = tone->modenvkeyf[0][j]; + } else if (i < tone->modenvkeyfnum) { + for (j = 0; j < 6; j++) + if (tone->modenvkeyf[i][j] != -1) + sp->modenv_keyf[j] = tone->modenvkeyf[i][j]; + } + } + if (tone->modenvvelfnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modenvvelfnum == 1) { + for (j = 0; j < 6; j++) + if (tone->modenvvelf[0][j] != -1) + sp->modenv_velf[j] = tone->modenvvelf[0][j]; + } else if (i < tone->modenvvelfnum) { + for (j = 0; j < 6; j++) + if (tone->modenvvelf[i][j] != -1) + sp->modenv_velf[j] = tone->modenvvelf[i][j]; + } + } + if (tone->trempitchnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->trempitchnum == 1) + sp->tremolo_to_pitch = tone->trempitch[0]; + else if (i < tone->trempitchnum) + sp->tremolo_to_pitch = tone->trempitch[i]; + } + if (tone->tremfcnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->tremfcnum == 1) + sp->tremolo_to_fc = tone->tremfc[0]; + else if (i < tone->tremfcnum) + sp->tremolo_to_fc = tone->tremfc[i]; + } + if (tone->modpitchnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modpitchnum == 1) + sp->modenv_to_pitch = tone->modpitch[0]; + else if (i < tone->modpitchnum) + sp->modenv_to_pitch = tone->modpitch[i]; + } + if (tone->modfcnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->modfcnum == 1) + sp->modenv_to_fc = tone->modfc[0]; + else if (i < tone->modfcnum) + sp->modenv_to_fc = tone->modfc[i]; + } + if (tone->fcnum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->fcnum == 1) + sp->cutoff_freq = adjust_fc(tone->fc[0]); + else if (i < tone->fcnum) + sp->cutoff_freq = adjust_fc(tone->fc[i]); + } + if (tone->resonum) + for (i = 0; i < ip->samples; i++) { + sp = &ip->sample[i]; + if (tone->resonum == 1) + sp->resonance = adjust_reso(tone->reso[0]); + else if (i < tone->resonum) + sp->resonance = adjust_reso(tone->reso[i]); + } +} + +#define READ_CHAR(thing) { \ + uint8_t tmpchar; \ + \ + if (tf_read(&tmpchar, 1, tf) != 1) \ + goto fail; \ + thing = tmpchar; \ +} +#define READ_SHORT(thing) { \ + uint16_t tmpshort; \ + \ + if (tf_read(&tmpshort, 2, tf) != 2) \ + goto fail; \ + thing = LE_SHORT(tmpshort); \ +} +#define READ_LONG(thing) { \ + int32_t tmplong; \ + \ + if (tf_read(&tmplong, 4, tf) != 4) \ + goto fail; \ + thing = LE_LONG(tmplong); \ +} + +/* If panning or note_to_use != -1, it will be used for all samples, + * instead of the sample-specific values in the instrument file. + * + * For note_to_use, any value < 0 or > 127 will be forced to 0. + * + * For other parameters, 1 means yes, 0 means no, other values are + * undefined. + * + * TODO: do reverse loops right + */ +Instrument *Instruments::load_gus_instrument(char *name, ToneBank *bank, int dr, int prog) +{ + ToneBankElement *tone; + int amp, note_to_use, panning, strip_envelope, strip_loop, strip_tail; + Instrument *ip; + timidity_file *tf; + uint8_t tmp[1024], fractions; + Sample *sp; + int i, j, noluck = 0; + + if (!name) + return 0; + + if (bank) { + tone = &bank->tone[prog]; + amp = tone->amp; + note_to_use = (tone->note != -1) ? tone->note : ((dr) ? prog : -1); + panning = tone->pan; + strip_envelope = (tone->strip_envelope != -1) + ? tone->strip_envelope : ((dr) ? 1 : -1); + strip_loop = (tone->strip_loop != -1) + ? tone->strip_loop : ((dr) ? 1 : -1); + strip_tail = tone->strip_tail; + } + else { + tone = NULL; + amp = note_to_use = panning = -1; + strip_envelope = strip_loop = strip_tail = 0; + } + if (tone && tone->tunenum == 0 + && tone->envratenum == 0 && tone->envofsnum == 0 + && tone->tremnum == 0 && tone->vibnum == 0 + && tone->sclnotenum == 0 && tone->scltunenum == 0 + && tone->modenvratenum == 0 && tone->modenvofsnum == 0 + && tone->envkeyfnum == 0 && tone->envvelfnum == 0 + && tone->modenvkeyfnum == 0 && tone->modenvvelfnum == 0 + && tone->trempitchnum == 0 && tone->tremfcnum == 0 + && tone->modpitchnum == 0 && tone->modfcnum == 0 + && tone->fcnum == 0 && tone->resonum == 0) + if ((ip = search_instrument_cache(name, panning, amp, note_to_use, + strip_loop, strip_envelope, strip_tail)) != NULL) { + printMessage(CMSG_INFO, VERB_DEBUG, " * Cached"); + return ip; + } + /* Open patch file */ + tf = open_file(name, sfreader); + if (!tf) + { + int name_len, ext_len; + static const char *patch_ext[] = { ".pat", 0 }; + + noluck = 1; + name_len = (int)strlen(name); + /* Try with various extensions */ + for (i = 0; patch_ext[i]; i++) + { + ext_len = (int)strlen(patch_ext[i]); + if (name_len + ext_len < 1024) + { + if (name_len >= ext_len && strcmp(name + name_len - ext_len, + patch_ext[i]) == 0) + continue; /* duplicated ext. */ + strcpy((char *)tmp, name); + strcat((char *)tmp, patch_ext[i]); + tf = open_file((char *)tmp, sfreader); + if (tf) + { + noluck = 0; + break; + } + } + } + } + if (noluck) + { + printMessage(CMSG_INFO, VERB_DEBUG, "Instrument `%s' can't be found.", name); + return 0; + } + /* Read some headers and do cursory sanity checks. There are loads + * of magic offsets. This could be rewritten... + */ + tmp[0] = tf_getc(tf); + if (tmp[0] == '\0') { + /* for Mac binary */ + skip(tf, 127); + tmp[0] = tf_getc(tf); + } + if ((tf_read(tmp + 1, 238, tf) != 238) + || (memcmp(tmp, "GF1PATCH110\0ID#000002", 22) + && memcmp(tmp, "GF1PATCH100\0ID#000002", 22))) { + /* don't know what the differences are */ + printMessage(CMSG_ERROR, VERB_NORMAL, "%s: not an instrument", name); + tf_close(tf); + return 0; + } + /* instruments. To some patch makers, 0 means 1 */ + if (tmp[82] != 1 && tmp[82] != 0) { + printMessage(CMSG_ERROR, VERB_NORMAL, + "Can't handle patches with %d instruments", tmp[82]); + tf_close(tf); + return 0; + } + if (tmp[151] != 1 && tmp[151] != 0) { /* layers. What's a layer? */ + printMessage(CMSG_ERROR, VERB_NORMAL, + "Can't handle instruments with %d layers", tmp[151]); + tf_close(tf); + return 0; + } + ip = (Instrument *)safe_malloc(sizeof(Instrument)); + ip->type = INST_GUS; + ip->samples = tmp[198]; + ip->sample = (Sample *)safe_malloc(sizeof(Sample) * ip->samples); + memset(ip->sample, 0, sizeof(Sample) * ip->samples); + for (i = 0; i < ip->samples; i++) { + skip(tf, 7); /* Skip the wave name */ + if (tf_read(&fractions, 1, tf) != 1) { + fail: + printMessage(CMSG_ERROR, VERB_NORMAL, "Error reading sample %d", i); + for (j = 0; j < i; j++) + free(ip->sample[j].data); + free(ip->sample); + free(ip); + tf_close(tf); + return 0; + } + sp = &(ip->sample[i]); + sp->low_vel = 0; + sp->high_vel = 127; + sp->cutoff_freq = sp->resonance = 0; + sp->tremolo_to_pitch = sp->tremolo_to_fc = 0; + sp->modenv_to_pitch = sp->modenv_to_fc = 0; + sp->vel_to_fc = sp->key_to_fc = sp->vel_to_resonance = 0; + sp->envelope_velf_bpo = sp->modenv_velf_bpo = 64; + sp->vel_to_fc_threshold = 64; + sp->key_to_fc_bpo = 60; + sp->envelope_delay = sp->modenv_delay = 0; + sp->tremolo_delay = sp->vibrato_delay = 0; + sp->inst_type = INST_GUS; + sp->sample_type = SF_SAMPLETYPE_MONO; + sp->sf_sample_link = -1; + sp->sf_sample_index = 0; + memset(sp->envelope_velf, 0, sizeof(sp->envelope_velf)); + memset(sp->envelope_keyf, 0, sizeof(sp->envelope_keyf)); + memset(sp->modenv_velf, 0, sizeof(sp->modenv_velf)); + memset(sp->modenv_keyf, 0, sizeof(sp->modenv_keyf)); + memset(sp->modenv_rate, 0, sizeof(sp->modenv_rate)); + memset(sp->modenv_offset, 0, sizeof(sp->modenv_offset)); + READ_LONG(sp->data_length); + READ_LONG(sp->loop_start); + READ_LONG(sp->loop_end); + READ_SHORT(sp->sample_rate); + READ_LONG(sp->low_freq); + READ_LONG(sp->high_freq); + READ_LONG(sp->root_freq); + skip(tf, 2); /* Why have a "root frequency" and then "tuning"?? */ + READ_CHAR(tmp[0]); + printMessage(CMSG_INFO, VERB_DEBUG, "Rate/Low/Hi/Root = %d/%d/%d/%d", + sp->sample_rate, sp->low_freq, sp->high_freq, sp->root_freq); + if (panning == -1) + /* 0x07 and 0x08 are both center panning */ + sp->panning = ((tmp[0] - ((tmp[0] < 8) ? 7 : 8)) * 63) / 7 + 64; + else + sp->panning = (uint8_t)(panning & 0x7f); + /* envelope, tremolo, and vibrato */ + if (tf_read(tmp, 18, tf) != 18) + goto fail; + if (!tmp[13] || !tmp[14]) { + sp->tremolo_sweep_increment = sp->tremolo_phase_increment = 0; + sp->tremolo_depth = 0; + printMessage(CMSG_INFO, VERB_DEBUG, " * no tremolo"); + } + else { + sp->tremolo_sweep_increment = convert_tremolo_sweep(tmp[12]); + sp->tremolo_phase_increment = convert_tremolo_rate(tmp[13]); + sp->tremolo_depth = tmp[14]; + printMessage(CMSG_INFO, VERB_DEBUG, + " * tremolo: sweep %d, phase %d, depth %d", + sp->tremolo_sweep_increment, sp->tremolo_phase_increment, + sp->tremolo_depth); + } + if (!tmp[16] || !tmp[17]) { + sp->vibrato_sweep_increment = sp->vibrato_control_ratio = 0; + sp->vibrato_depth = 0; + printMessage(CMSG_INFO, VERB_DEBUG, " * no vibrato"); + } + else { + sp->vibrato_control_ratio = convert_vibrato_rate(tmp[16]); + sp->vibrato_sweep_increment = convert_vibrato_sweep(tmp[15], + sp->vibrato_control_ratio); + sp->vibrato_depth = tmp[17]; + printMessage(CMSG_INFO, VERB_DEBUG, + " * vibrato: sweep %d, ctl %d, depth %d", + sp->vibrato_sweep_increment, sp->vibrato_control_ratio, + sp->vibrato_depth); + } + READ_CHAR(sp->modes); + printMessage(CMSG_INFO, VERB_DEBUG, " * mode: 0x%02x", sp->modes); + READ_SHORT(sp->scale_freq); + READ_SHORT(sp->scale_factor); + skip(tf, 36); /* skip reserved space */ + /* Mark this as a fixed-pitch instrument if such a deed is desired. */ + sp->note_to_use = (note_to_use != -1) ? (uint8_t)note_to_use : 0; + /* seashore.pat in the Midia patch set has no Sustain. I don't + * understand why, and fixing it by adding the Sustain flag to + * all looped patches probably breaks something else. We do it + * anyway. + */ + if (sp->modes & MODES_LOOPING) + sp->modes |= MODES_SUSTAIN; + /* Strip any loops and envelopes we're permitted to */ + if ((strip_loop == 1) && (sp->modes & (MODES_SUSTAIN | MODES_LOOPING + | MODES_PINGPONG | MODES_REVERSE))) { + sp->modes &= ~(MODES_SUSTAIN | MODES_LOOPING + | MODES_PINGPONG | MODES_REVERSE); + printMessage(CMSG_INFO, VERB_DEBUG, + " - Removing loop and/or sustain"); + } + if (strip_envelope == 1) { + if (sp->modes & MODES_ENVELOPE) + printMessage(CMSG_INFO, VERB_DEBUG, " - Removing envelope"); + sp->modes &= ~MODES_ENVELOPE; + } + else if (strip_envelope != 0) { + /* Have to make a guess. */ + if (!(sp->modes & (MODES_LOOPING + | MODES_PINGPONG | MODES_REVERSE))) { + /* No loop? Then what's there to sustain? + * No envelope needed either... + */ + sp->modes &= ~(MODES_SUSTAIN | MODES_ENVELOPE); + printMessage(CMSG_INFO, VERB_DEBUG, + " - No loop, removing sustain and envelope"); + } + else if (!memcmp(tmp, "??????", 6) || tmp[11] >= 100) { + /* Envelope rates all maxed out? + * Envelope end at a high "offset"? + * That's a weird envelope. Take it out. + */ + sp->modes &= ~MODES_ENVELOPE; + printMessage(CMSG_INFO, VERB_DEBUG, + " - Weirdness, removing envelope"); + } + else if (!(sp->modes & MODES_SUSTAIN)) { + /* No sustain? Then no envelope. I don't know if this is + * justified, but patches without sustain usually don't need + * the envelope either... at least the Gravis ones. They're + * mostly drums. I think. + */ + sp->modes &= ~MODES_ENVELOPE; + printMessage(CMSG_INFO, VERB_DEBUG, + " - No sustain, removing envelope"); + } + } + for (j = 0; j < 6; j++) { + sp->envelope_rate[j] = convert_envelope_rate(tmp[j]); + sp->envelope_offset[j] = convert_envelope_offset(tmp[j + 6]); + } + /* this envelope seems to give reverb like effects to most patches + * use the same method as soundfont + */ + if (modify_release) { + sp->envelope_offset[3] = to_offset_22(5); + sp->envelope_rate[3] = calc_rate_i(255, modify_release); + sp->envelope_offset[4] = to_offset_22(4); + sp->envelope_rate[4] = to_offset_22(200); + sp->envelope_offset[5] = to_offset_22(4); + sp->envelope_rate[5] = to_offset_22(200); + } + /* Then read the sample data */ + sp->data = (sample_t *)safe_malloc(sp->data_length + 4); + sp->data_alloced = 1; + if ((j = tf_read(sp->data, sp->data_length, tf)) != (int)sp->data_length) { + printMessage(CMSG_ERROR, VERB_NORMAL, "Too small this patch length: %d < %d", j, sp->data_length); + goto fail; + } + if (!(sp->modes & MODES_16BIT)) { /* convert to 16-bit data */ + uint16_t *tmp; + uint8_t *cp = (uint8_t *)sp->data; + + tmp = (uint16_t *)safe_malloc(sp->data_length * 2 + 4); + for (splen_t i = 0; i < sp->data_length; i++) + tmp[i] = (uint16_t)cp[i] << 8; + sp->data = (sample_t *)tmp; + free(cp); + sp->data_length *= 2; + sp->loop_start *= 2; + sp->loop_end *= 2; + } +#ifdef _BIG_ENDIAN_ + else { /* convert to machine byte order */ + int32_t i; + int16_t *tmp = (int16_t *)sp->data, s; + + for (i = 0; i < sp->data_length / 2; i++) + s = LE_SHORT(tmp[i]), tmp[i] = s; + } +#endif + if (sp->modes & MODES_UNSIGNED) { /* convert to signed data */ + int32_t i = sp->data_length / 2; + int16_t *tmp = (int16_t *)sp->data; + + while (i--) + *tmp++ ^= 0x8000; + } + /* Reverse loops and pass them off as normal loops */ + if (sp->modes & MODES_REVERSE) { + /* The GUS apparently plays reverse loops by reversing the + * whole sample. We do the same because the GUS does not SUCK. + */ + int32_t t; + + reverse_data((int16_t *)sp->data, 0, sp->data_length / 2); + t = sp->loop_start; + sp->loop_start = sp->data_length - sp->loop_end; + sp->loop_end = sp->data_length - t; + sp->modes &= ~MODES_REVERSE; + sp->modes |= MODES_LOOPING; /* just in case */ + printMessage(CMSG_WARNING, VERB_NORMAL, "Reverse loop in %s", name); + } + /* If necessary do some anti-aliasing filtering */ + if (antialiasing_allowed) + antialiasing((int16_t *)sp->data, sp->data_length / 2, + sp->sample_rate, playback_rate); + if (amp != -1) + sp->volume = (double) amp / 100; + else { + /* Try to determine a volume scaling factor for the sample. + * This is a very crude adjustment, but things sound more + * balanced with it. Still, this should be a runtime option. + */ + int32_t a, maxamp = 0; + int16_t *tmp = (int16_t *)sp->data; + + for (splen_t i = 0; i < sp->data_length / 2; i++) + if ((a = abs(tmp[i])) > maxamp) + maxamp = a; + sp->volume = 32768 / (double)maxamp; + printMessage(CMSG_INFO, VERB_DEBUG, + " * volume comp: %f", sp->volume); + } + /* These are in bytes. Convert into samples. */ + sp->data_length /= 2; + sp->loop_start /= 2; + sp->loop_end /= 2; + /* The sample must be padded out by 2 extra sample, so that + * round off errors in the offsets used in interpolation will not + * cause a "pop" by reading random data beyond data_length + */ + sp->data[sp->data_length] = sp->data[sp->data_length + 1] = 0; + /* Remove abnormal loops which cause pop noise + * in long sustain stage + */ + if (!(sp->modes & MODES_LOOPING)) { + sp->loop_start = sp->data_length - 1; + sp->loop_end = sp->data_length; + sp->data[sp->data_length - 1] = 0; + } + /* Then fractional samples */ + sp->data_length <<= FRACTION_BITS; + sp->loop_start <<= FRACTION_BITS; + sp->loop_end <<= FRACTION_BITS; + /* Adjust for fractional loop points. This is a guess. Does anyone + * know what "fractions" really stands for? + */ + sp->loop_start |= (fractions & 0x0f) << (FRACTION_BITS - 4); + sp->loop_end |= ((fractions >> 4) & 0x0f) << (FRACTION_BITS - 4); + /* If this instrument will always be played on the same note, + * and it's not looped, we can resample it now. + */ + if (sp->note_to_use && !(sp->modes & MODES_LOOPING)) + pre_resample(sp); + + /* do pitch detection on drums if surround chorus is used */ + if (dr && timidity_surround_chorus) + { + Freq freq; + sp->chord = -1; + sp->root_freq_detected = freq.freq_fourier(sp, &(sp->chord)); + sp->transpose_detected = + assign_pitch_to_freq(sp->root_freq_detected) - + assign_pitch_to_freq(sp->root_freq / 1024.0); + } + + if (strip_tail == 1) { + /* Let's not really, just say we did. */ + sp->data_length = sp->loop_end; + printMessage(CMSG_INFO, VERB_DEBUG, " - Stripping tail"); + } + } + tf_close(tf); + store_instrument_cache(ip, name, panning, amp, note_to_use, + strip_loop, strip_envelope, strip_tail); + return ip; +} + + +Instrument *Instruments::load_instrument(int dr, int b, int prog) +{ + ToneBank *bank = ((dr) ? drumset[b] : tonebank[b]); + Instrument *ip; + int i, font_bank, font_preset, font_keynote; + double volume_max; + int pan, panning; + +#if 0 + // This cannot possibly work as implemented. + if (play_system_mode == GS_SYSTEM_MODE && (b == 64 || b == 65)) { + if (!dr) /* User Instrument */ + recompute_userinst(b, prog); + else { /* User Drumset */ + ip = recompute_userdrum(b, prog); + if (ip != NULL) { + return ip; + } + } + } +#endif + if (bank->tone[prog].instype == 1 || bank->tone[prog].instype == 2) { + if (bank->tone[prog].instype == 1) { /* Font extention */ + font_bank = bank->tone[prog].font_bank; + font_preset = bank->tone[prog].font_preset; + font_keynote = bank->tone[prog].font_keynote; + ip = extract_soundfont(bank->tone[prog].name, + font_bank, font_preset, font_keynote); + } + else /* Sample extension */ + ip = extract_sample_file(bank->tone[prog].name); + /* amp tuning */ + if (ip != NULL && bank->tone[prog].amp != -1) { + for (i = 0, volume_max = 0; i < ip->samples; i++) + if (volume_max < ip->sample[i].volume) + volume_max = ip->sample[i].volume; + if (volume_max != 0) + for (i = 0; i < ip->samples; i++) + ip->sample[i].volume *= bank->tone[prog].amp + / 100.0 / volume_max; + } + /* panning */ + if (ip != NULL && bank->tone[prog].pan != -1) { + pan = ((int)bank->tone[prog].pan & 0x7f) - 64; + for (i = 0; i < ip->samples; i++) { + panning = (int)ip->sample[i].panning + pan; + panning = (panning < 0) ? 0 + : ((panning > 127) ? 127 : panning); + ip->sample[i].panning = panning; + } + } + /* note to use */ + if (ip != NULL && bank->tone[prog].note != -1) + for (i = 0; i < ip->samples; i++) + ip->sample[i].root_freq = + freq_table[bank->tone[prog].note & 0x7f]; + /* filter key-follow */ + if (ip != NULL && bank->tone[prog].key_to_fc != 0) + for (i = 0; i < ip->samples; i++) + ip->sample[i].key_to_fc = bank->tone[prog].key_to_fc; + /* filter velocity-follow */ + if (ip != NULL && bank->tone[prog].vel_to_fc != 0) + for (i = 0; i < ip->samples; i++) + ip->sample[i].key_to_fc = bank->tone[prog].vel_to_fc; + /* resonance velocity-follow */ + if (ip != NULL && bank->tone[prog].vel_to_resonance != 0) + for (i = 0; i < ip->samples; i++) + ip->sample[i].vel_to_resonance = + bank->tone[prog].vel_to_resonance; + /* strip tail */ + if (ip != NULL && bank->tone[prog].strip_tail == 1) + for (i = 0; i < ip->samples; i++) + ip->sample[i].data_length = ip->sample[i].loop_end; + if (ip != NULL) { + i = (dr) ? 0 : prog; + if (bank->tone[i].comment) + free(bank->tone[i].comment); + bank->tone[i].comment = safe_strdup(ip->instname); + apply_bank_parameter(ip, &bank->tone[prog]); + } + return ip; + } + if (!dr) { + font_bank = b; + font_preset = prog; + font_keynote = -1; + } + else { + font_bank = 128; + font_preset = b; + font_keynote = prog; + } + /* preload soundfont */ + ip = load_soundfont_inst(0, font_bank, font_preset, font_keynote); + if (ip != NULL) { + if (bank->tone[prog].name == NULL) /* this should not be NULL to play the instrument */ + bank->tone[prog].name = safe_strdup(DYNAMIC_INSTRUMENT_NAME); + if (bank->tone[prog].comment) + free(bank->tone[prog].comment); + bank->tone[prog].comment = safe_strdup(ip->instname); + } + if (ip == NULL) { /* load GUS/patch file */ + ip = load_gus_instrument(bank->tone[prog].name, bank, dr, prog); + if (ip == NULL) { /* no patch; search soundfont again */ + ip = load_soundfont_inst(1, font_bank, font_preset, font_keynote); + if (ip != NULL) { + if (bank->tone[0].comment) + free(bank->tone[0].comment); + bank->tone[0].comment = safe_strdup(ip->instname); + } + } + } + if (ip != NULL) + apply_bank_parameter(ip, &bank->tone[prog]); + return ip; +} + +int Instruments::fill_bank(int dr, int b, int *rc) +{ + int i, errors = 0; + ToneBank *bank = ((dr) ? drumset[b] : tonebank[b]); + + if (rc != NULL) + *rc = RC_OK; + + for (i = 0; i < 128; i++) + { + if (bank->tone[i].instrument == MAGIC_LOAD_INSTRUMENT) + { + if (!(bank->tone[i].name)) + { + bank->tone[i].instrument = load_instrument(dr, b, i); + if (bank->tone[i].instrument == NULL) + { + // This would be too annoying on 'warning' level. + printMessage(CMSG_WARNING, VERB_DEBUG, + "No instrument mapped to %s %d, program %d%s", + dr ? "drum set" : "tone bank", + dr ? b + progbase : b, + dr ? i : i + progbase, + (b != 0) ? "" : + " - this instrument will not be heard"); + if (b != 0) + { + /* Mark the corresponding instrument in the default + bank / drumset for loading (if it isn't already) */ + if (!dr) + { + if (!(standard_tonebank.tone[i].instrument)) + standard_tonebank.tone[i].instrument = + MAGIC_LOAD_INSTRUMENT; + } + else + { + if (!(standard_drumset.tone[i].instrument)) + standard_drumset.tone[i].instrument = + MAGIC_LOAD_INSTRUMENT; + } + bank->tone[i].instrument = 0; + } + else + bank->tone[i].instrument = MAGIC_ERROR_INSTRUMENT; + errors++; + } + } + else + { + if (rc != NULL) + { + *rc = RC_OK; + } + + bank->tone[i].instrument = load_instrument(dr, b, i); + if (!bank->tone[i].instrument) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "Couldn't load instrument %s " + "(%s %d, program %d)", bank->tone[i].name, + dr ? "drum set" : "tone bank", + dr ? b + progbase : b, + dr ? i : i + progbase); + errors++; + } + } + } + } + return errors; +} + +int Instruments::load_missing_instruments(int *rc) +{ + int i = 128 + map_bank_counter, errors = 0; + if (rc != NULL) + *rc = RC_OK; + while (i--) + { + if (tonebank[i]) + errors += fill_bank(0, i, rc); + if (rc != NULL && RC_IS_SKIP_FILE(*rc)) + return errors; + if (drumset[i]) + errors += fill_bank(1, i, rc); + if (rc != NULL && RC_IS_SKIP_FILE(*rc)) + return errors; + } + return errors; +} + +// The precaching code is from ZDoom's Timidity-based GUS emulation +void Instruments::MarkInstrument(int banknum, int percussion, int instr) +{ + ToneBank *bank; + + if (banknum >= 128) + { + return; + } + if (banknum != 0) + { + /* Mark the standard bank in case it's not defined by this one. */ + MarkInstrument(0, percussion, instr); + } + if (percussion) + { + bank = drumset[banknum]; + } + else + { + bank = tonebank[banknum]; + } + if (bank == NULL) + { + return; + } + if (bank->tone[instr].instrument == NULL) + { + bank->tone[instr].instrument = MAGIC_LOAD_INSTRUMENT; + } +} + +void Instruments::PrecacheInstruments(const uint16_t *instruments, int count) +{ + for (int i = 0; i < count; ++i) + { + MarkInstrument((instruments[i] >> 7) & 127, instruments[i] >> 14, instruments[i] & 127); + } + load_missing_instruments(nullptr); +} + + + +void *Instruments::safe_memdup(void *s, size_t size) +{ + return memcpy(safe_malloc(size), s, size); +} + +/*! Copy ToneBankElement src to elm. The original elm is released. */ +void Instruments::copy_tone_bank_element(ToneBankElement *elm, const ToneBankElement *src) +{ + int i; + + free_tone_bank_element(elm); + memcpy(elm, src, sizeof(ToneBankElement)); + if (elm->name) + elm->name = safe_strdup(elm->name); + if (elm->tunenum) + elm->tune = (float *)safe_memdup(elm->tune, + elm->tunenum * sizeof(float)); + if (elm->envratenum) { + elm->envrate = (int **)safe_memdup(elm->envrate, + elm->envratenum * sizeof(int *)); + for (i = 0; i < elm->envratenum; i++) + elm->envrate[i] = (int *)safe_memdup(elm->envrate[i], + 6 * sizeof(int)); + } + if (elm->envofsnum) { + elm->envofs = (int **)safe_memdup(elm->envofs, + elm->envofsnum * sizeof(int *)); + for (i = 0; i < elm->envofsnum; i++) + elm->envofs[i] = (int *)safe_memdup(elm->envofs[i], + 6 * sizeof(int)); + } + if (elm->tremnum) { + elm->trem = (Quantity **)safe_memdup(elm->trem, + elm->tremnum * sizeof(Quantity *)); + for (i = 0; i < elm->tremnum; i++) + elm->trem[i] = (Quantity *)safe_memdup(elm->trem[i], + 3 * sizeof(Quantity)); + } + if (elm->vibnum) { + elm->vib = (Quantity **)safe_memdup(elm->vib, + elm->vibnum * sizeof(Quantity *)); + for (i = 0; i < elm->vibnum; i++) + elm->vib[i] = (Quantity *)safe_memdup(elm->vib[i], + 3 * sizeof(Quantity)); + } + if (elm->sclnotenum) + elm->sclnote = (int16_t *)safe_memdup(elm->sclnote, + elm->sclnotenum * sizeof(int16_t)); + if (elm->scltunenum) + elm->scltune = (int16_t *)safe_memdup(elm->scltune, + elm->scltunenum * sizeof(int16_t)); + if (elm->comment) + elm->comment = safe_strdup(elm->comment); + if (elm->modenvratenum) { + elm->modenvrate = (int **)safe_memdup(elm->modenvrate, + elm->modenvratenum * sizeof(int *)); + for (i = 0; i < elm->modenvratenum; i++) + elm->modenvrate[i] = (int *)safe_memdup(elm->modenvrate[i], + 6 * sizeof(int)); + } + if (elm->modenvofsnum) { + elm->modenvofs = (int **)safe_memdup(elm->modenvofs, + elm->modenvofsnum * sizeof(int *)); + for (i = 0; i < elm->modenvofsnum; i++) + elm->modenvofs[i] = (int *)safe_memdup(elm->modenvofs[i], + 6 * sizeof(int)); + } + if (elm->envkeyfnum) { + elm->envkeyf = (int **)safe_memdup(elm->envkeyf, + elm->envkeyfnum * sizeof(int *)); + for (i = 0; i < elm->envkeyfnum; i++) + elm->envkeyf[i] = (int *)safe_memdup(elm->envkeyf[i], + 6 * sizeof(int)); + } + if (elm->envvelfnum) { + elm->envvelf = (int **)safe_memdup(elm->envvelf, + elm->envvelfnum * sizeof(int *)); + for (i = 0; i < elm->envvelfnum; i++) + elm->envvelf[i] = (int *)safe_memdup(elm->envvelf[i], + 6 * sizeof(int)); + } + if (elm->modenvkeyfnum) { + elm->modenvkeyf = (int **)safe_memdup(elm->modenvkeyf, + elm->modenvkeyfnum * sizeof(int *)); + for (i = 0; i < elm->modenvkeyfnum; i++) + elm->modenvkeyf[i] = (int *)safe_memdup(elm->modenvkeyf[i], + 6 * sizeof(int)); + } + if (elm->modenvvelfnum) { + elm->modenvvelf = (int **)safe_memdup(elm->modenvvelf, + elm->modenvvelfnum * sizeof(int *)); + for (i = 0; i < elm->modenvvelfnum; i++) + elm->modenvvelf[i] = (int *)safe_memdup(elm->modenvvelf[i], + 6 * sizeof(int)); + } + if (elm->trempitchnum) + elm->trempitch = (int16_t *)safe_memdup(elm->trempitch, + elm->trempitchnum * sizeof(int16_t)); + if (elm->tremfcnum) + elm->tremfc = (int16_t *)safe_memdup(elm->tremfc, + elm->tremfcnum * sizeof(int16_t)); + if (elm->modpitchnum) + elm->modpitch = (int16_t *)safe_memdup(elm->modpitch, + elm->modpitchnum * sizeof(int16_t)); + if (elm->modfcnum) + elm->modfc = (int16_t *)safe_memdup(elm->modfc, + elm->modfcnum * sizeof(int16_t)); + if (elm->fcnum) + elm->fc = (int16_t *)safe_memdup(elm->fc, + elm->fcnum * sizeof(int16_t)); + if (elm->resonum) + elm->reso = (int16_t *)safe_memdup(elm->reso, + elm->resonum * sizeof(int16_t)); + +} + +/*! Release ToneBank[128 + MAP_BANK_COUNT] */ +void Instruments::free_tone_bank_list(ToneBank *tb[]) +{ + int i, j; + ToneBank *bank; + + for (i = 0; i < 128 + map_bank_counter; i++) + { + bank = tb[i]; + if (!bank) + continue; + for (j = 0; j < 128; j++) + free_tone_bank_element(&bank->tone[j]); + if (i > 0) + { + free(bank); + tb[i] = NULL; + } + } +} + +/*! Release tonebank and drumset */ +void Instruments::free_tone_bank(void) +{ + free_tone_bank_list(tonebank); + free_tone_bank_list(drumset); +} + +/*! Release ToneBankElement. */ +void Instruments::free_tone_bank_element(ToneBankElement *elm) +{ + elm->instype = 0; + if (elm->name) + free(elm->name); + elm->name = NULL; + if (elm->tune) + free(elm->tune); + elm->tune = NULL, elm->tunenum = 0; + if (elm->envratenum) + free_ptr_list(elm->envrate, elm->envratenum); + elm->envrate = NULL, elm->envratenum = 0; + if (elm->envofsnum) + free_ptr_list(elm->envofs, elm->envofsnum); + elm->envofs = NULL, elm->envofsnum = 0; + if (elm->tremnum) + free_ptr_list(elm->trem, elm->tremnum); + elm->trem = NULL, elm->tremnum = 0; + if (elm->vibnum) + free_ptr_list(elm->vib, elm->vibnum); + elm->vib = NULL, elm->vibnum = 0; + if (elm->sclnote) + free(elm->sclnote); + elm->sclnote = NULL, elm->sclnotenum = 0; + if (elm->scltune) + free(elm->scltune); + elm->scltune = NULL, elm->scltunenum = 0; + if (elm->comment) + free(elm->comment); + elm->comment = NULL; + if (elm->modenvratenum) + free_ptr_list(elm->modenvrate, elm->modenvratenum); + elm->modenvrate = NULL, elm->modenvratenum = 0; + if (elm->modenvofsnum) + free_ptr_list(elm->modenvofs, elm->modenvofsnum); + elm->modenvofs = NULL, elm->modenvofsnum = 0; + if (elm->envkeyfnum) + free_ptr_list(elm->envkeyf, elm->envkeyfnum); + elm->envkeyf = NULL, elm->envkeyfnum = 0; + if (elm->envvelfnum) + free_ptr_list(elm->envvelf, elm->envvelfnum); + elm->envvelf = NULL, elm->envvelfnum = 0; + if (elm->modenvkeyfnum) + free_ptr_list(elm->modenvkeyf, elm->modenvkeyfnum); + elm->modenvkeyf = NULL, elm->modenvkeyfnum = 0; + if (elm->modenvvelfnum) + free_ptr_list(elm->modenvvelf, elm->modenvvelfnum); + elm->modenvvelf = NULL, elm->modenvvelfnum = 0; + if (elm->trempitch) + free(elm->trempitch); + elm->trempitch = NULL, elm->trempitchnum = 0; + if (elm->tremfc) + free(elm->tremfc); + elm->tremfc = NULL, elm->tremfcnum = 0; + if (elm->modpitch) + free(elm->modpitch); + elm->modpitch = NULL, elm->modpitchnum = 0; + if (elm->modfc) + free(elm->modfc); + elm->modfc = NULL, elm->modfcnum = 0; + if (elm->fc) + free(elm->fc); + elm->fc = NULL, elm->fcnum = 0; + if (elm->reso) + free(elm->reso); + elm->reso = NULL, elm->resonum = 0; +} + +void Instruments::free_instruments(int reload_default_inst) +{ + int i = 128 + map_bank_counter, j; + struct InstrumentCache *p; + ToneBank *bank; + Instrument *ip; + struct InstrumentCache *default_entry; + int default_entry_addr; + + clear_magic_instruments(); + + /* Free soundfont instruments */ + while (i--) + { + /* Note that bank[*]->tone[j].instrument may pointer to + bank[0]->tone[j].instrument. See play_midi_load_instrument() + at playmidi.c for the implementation */ + + if ((bank = tonebank[i]) != NULL) + for (j = 127; j >= 0; j--) + { + ip = bank->tone[j].instrument; + if (ip != NULL && ip->type == INST_SF2 && + (i == 0 || ip != tonebank[0]->tone[j].instrument)) + free_instrument(ip); + bank->tone[j].instrument = NULL; + if (bank->tone[j].name && !bank->tone[j].name[0]) /* DYNAMIC_INSTRUMENT_NAME */ + { + free(bank->tone[j].name); + bank->tone[j].name = NULL; + } + } + if ((bank = drumset[i]) != NULL) + for (j = 127; j >= 0; j--) + { + ip = bank->tone[j].instrument; + if (ip != NULL && ip->type == INST_SF2 && + (i == 0 || ip != drumset[0]->tone[j].instrument)) + free_instrument(ip); + bank->tone[j].instrument = NULL; + if (bank->tone[j].name && !bank->tone[j].name[0]) /* DYNAMIC_INSTRUMENT_NAME */ + { + free(bank->tone[j].name); + bank->tone[j].name = NULL; + } + } +#if 0 + if ((drumset[i] != NULL) && (drumset[i]->alt != NULL)) { + free(drumset[i]->alt); + drumset[i]->alt = NULL; + } +#endif + } + + /* Free GUS/patch instruments */ + default_entry = NULL; + default_entry_addr = 0; + for (i = 0; i < INSTRUMENT_HASH_SIZE; i++) + { + p = instrument_cache[i]; + while (p != NULL) + { + if (!reload_default_inst && p->ip == default_instrument) + { + default_entry = p; + default_entry_addr = i; + p = p->next; + } + else + { + struct InstrumentCache *tmp; + + tmp = p; + p = p->next; + free_instrument(tmp->ip); + free(tmp); + } + } + instrument_cache[i] = NULL; + } + + if (reload_default_inst) + set_default_instrument(NULL); + else if (default_entry) + { + default_entry->next = NULL; + instrument_cache[default_entry_addr] = default_entry; + } +} + +void Instruments::free_special_patch(int id) +{ + int i, j, start, end; + + if (id >= 0) + start = end = id; + else + { + start = 0; + end = NSPECIAL_PATCH - 1; + } + + for (i = start; i <= end; i++) + { + if (special_patch[i] != NULL) + { + Sample *sp; + int n; + + if (special_patch[i]->name != NULL) + free(special_patch[i]->name); + special_patch[i]->name = NULL; + n = special_patch[i]->samples; + sp = special_patch[i]->sample; + if (sp) + { + for (j = 0; j < n; j++) + if (sp[j].data_alloced && sp[j].data) + free(sp[j].data); + free(sp); + } + free(special_patch[i]); + special_patch[i] = NULL; + } + } +} + +int Instruments::set_default_instrument(char *name) +{ + Instrument *ip; + int i; + static char *last_name; + + if (name == NULL) + { + name = last_name; + if (name == NULL) + return 0; + } + + if (!(ip = load_gus_instrument(name, NULL, 0, 0))) + return -1; + if (default_instrument) + free_instrument(default_instrument); + default_instrument = ip; + for (i = 0; i < MAX_CHANNELS; i++) + default_program[i] = SPECIAL_PROGRAM; + last_name = name; + + return 0; +} + +/*! search mapped bank. + returns negative value indicating free bank if not found, + 0 if no free bank was available */ +int Instruments::find_instrument_map_bank(int dr, int map, int bk) +{ + struct bank_map_elem *bm; + int i; + + if (map == INST_NO_MAP) + return 0; + bm = dr ? map_drumset : map_bank; + for (i = 0; i < MAP_BANK_COUNT; i++) + { + if (!bm[i].used) + return -(128 + i); + else if (bm[i].mapid == map && bm[i].bankno == bk) + return 128 + i; + } + return 0; +} + +/*! allocate mapped bank if needed. returns -1 if allocation failed. */ +int Instruments::alloc_instrument_map_bank(int dr, int map, int bk) +{ + struct bank_map_elem *bm; + int i; + + if (map == INST_NO_MAP) + { + alloc_instrument_bank(dr, bk); + return bk; + } + i = find_instrument_map_bank(dr, map, bk); + if (i == 0) + return -1; + if (i < 0) + { + i = -i - 128; + bm = dr ? map_drumset : map_bank; + bm[i].used = 1; + bm[i].mapid = map; + bm[i].bankno = bk; + if (map_bank_counter < i + 1) + map_bank_counter = i + 1; + i += 128; + alloc_instrument_bank(dr, i); + } + return i; +} + +void Instruments::alloc_instrument_bank(int dr, int bk) +{ + ToneBank *b; + + if (dr) + { + if ((b = drumset[bk]) == NULL) + { + b = drumset[bk] = (ToneBank *)safe_malloc(sizeof(ToneBank)); + memset(b, 0, sizeof(ToneBank)); + } + } + else + { + if ((b = tonebank[bk]) == NULL) + { + b = tonebank[bk] = (ToneBank *)safe_malloc(sizeof(ToneBank)); + memset(b, 0, sizeof(ToneBank)); + } + } +} + + +/* Instrument alias map - Written by Masanao Izumo */ + +int Instruments::instrument_map(int mapID, int *set, int *elem) const +{ + int s, e; + struct inst_map_elem *p; + + if (mapID == INST_NO_MAP) + return 0; /* No map */ + + s = *set; + e = *elem; + p = inst_map_table[mapID][s]; + if (p != NULL && p[e].mapped) + { + *set = p[e].set; + *elem = p[e].elem; + return 1; + } + + if (s != 0) + { + p = inst_map_table[mapID][0]; + if (p != NULL && p[e].mapped) + { + *set = p[e].set; + *elem = p[e].elem; + } + return 2; + } + return 0; +} + +void Instruments::set_instrument_map(int mapID, + int set_from, int elem_from, + int set_to, int elem_to) +{ + struct inst_map_elem *p; + + p = inst_map_table[mapID][set_from]; + if (p == NULL) + { + p = (struct inst_map_elem *) + safe_malloc(128 * sizeof(struct inst_map_elem)); + memset(p, 0, 128 * sizeof(struct inst_map_elem)); + inst_map_table[mapID][set_from] = p; + } + p[elem_from].set = set_to; + p[elem_from].elem = elem_to; + p[elem_from].mapped = 1; +} + +void Instruments::free_instrument_map(void) +{ + int i, j; + + for (i = 0; i < map_bank_counter; i++) + map_bank[i].used = map_drumset[i].used = 0; + /* map_bank_counter = 0; never shrinks rather than assuming tonebank was already freed */ + for (i = 0; i < NUM_INST_MAP; i++) { + for (j = 0; j < 128; j++) { + struct inst_map_elem *map; + map = inst_map_table[i][j]; + if (map) { + free(map); + inst_map_table[i][j] = NULL; + } + } + } +} + +/* Alternate assign - Written by Masanao Izumo */ + +AlternateAssign *Instruments::add_altassign_string(AlternateAssign *old, char **params, int n) +{ + int i, j; + char *p; + int beg, end; + AlternateAssign *alt; + + if (n == 0) + return old; + if (!strcmp(*params, "clear")) { + while (old) { + AlternateAssign *next; + next = old->next; + free(old); + old = next; + } + params++; + n--; + if (n == 0) + return NULL; + } + + alt = (AlternateAssign *)safe_malloc(sizeof(AlternateAssign)); + memset(alt, 0, sizeof(AlternateAssign)); + for (i = 0; i < n; i++) { + p = params[i]; + if (*p == '-') { + beg = 0; + p++; + } + else + beg = atoi(p); + if ((p = strchr(p, '-')) != NULL) { + if (p[1] == '\0') + end = 127; + else + end = atoi(p + 1); + } + else + end = beg; + if (beg > end) { + int t; + t = beg; + beg = end; + end = t; + } + if (beg < 0) + beg = 0; + if (end > 127) + end = 127; + for (j = beg; j <= end; j++) + alt->bits[(j >> 5) & 0x3] |= 1 << (j & 0x1F); + } + alt->next = old; + return alt; +} + +AlternateAssign *Instruments::find_altassign(AlternateAssign *altassign, int note) +{ + AlternateAssign *p; + uint32_t mask; + int idx; + + mask = 1 << (note & 0x1F); + idx = (note >> 5) & 0x3; + for (p = altassign; p != NULL; p = p->next) + if (p->bits[idx] & mask) + return p; + return NULL; +} + +Instrument *Instruments::play_midi_load_instrument(int dr, int bk, int prog, bool *pLoad_success) +{ + ToneBank **bank = (dr) ? drumset : tonebank; + ToneBankElement *tone; + Instrument *ip; + bool load_success = false; + + if (bank[bk] == NULL) + alloc_instrument_bank(dr, bk); + + tone = &bank[bk]->tone[prog]; + /* tone->name is NULL if "soundfont" directive is used, and ip is NULL when not preloaded */ + /* dr: not sure but only drumsets are concerned at the moment */ + if (dr && !tone->name && ((ip = tone->instrument) == MAGIC_LOAD_INSTRUMENT || ip == NULL) + && (ip = load_instrument(dr, bk, prog)) != NULL) { + tone->instrument = ip; + tone->name = safe_strdup(DYNAMIC_INSTRUMENT_NAME); + load_success = 1; + } + else if (tone->name) { + /* Instrument is found. */ + if ((ip = tone->instrument) == MAGIC_LOAD_INSTRUMENT +#ifndef SUPPRESS_CHANNEL_LAYER + || ip == NULL /* see also readmidi.c: groom_list(). */ +#endif + ) { + ip = tone->instrument = load_instrument(dr, bk, prog); + } + if (ip == NULL || IS_MAGIC_INSTRUMENT(ip)) { + tone->instrument = MAGIC_ERROR_INSTRUMENT; + } + else { + load_success = true; + } + } + else { + /* Instrument is not found. + Try to load the instrument from bank 0 */ + ToneBankElement *tone0 = &bank[0]->tone[prog]; + if ((ip = tone0->instrument) == NULL + || ip == MAGIC_LOAD_INSTRUMENT) + ip = tone0->instrument = load_instrument(dr, 0, prog); + if (ip == NULL || IS_MAGIC_INSTRUMENT(ip)) { + tone0->instrument = MAGIC_ERROR_INSTRUMENT; + } + else { + copy_tone_bank_element(tone, tone0); + tone->instrument = ip; + load_success = 1; + } + } + + *pLoad_success = load_success; + + if (ip == MAGIC_ERROR_INSTRUMENT) + return NULL; + + return ip; +} + + + +//void recompute_userinst_altassign(int bank, int group); + + +/*! initialize GS user drumset. */ +void Instruments::init_userdrum() +{ + int i; + + free_userdrum(); + + for (i = 0; i<2; i++) { /* allocate alternative assign */ + memset(&alt[i], 0, sizeof(AlternateAssign)); + alloc_instrument_bank(1, 64 + i); + drumset[64 + i]->alt = &alt[i]; + } +} + + +/*! free GS user drumset. */ +void Instruments::free_userdrum() +{ + UserDrumset *p, *next; + + for (p = userdrum_first; p != NULL; p = next) { + next = p->next; + free(p); + } + userdrum_first = userdrum_last = NULL; +} + +/*! free GS user instrument. */ +void Instruments::free_userinst() +{ + UserInstrument *p, *next; + + for (p = userinst_first; p != NULL; p = next) { + next = p->next; + free(p); + } + userinst_first = userinst_last = NULL; +} + + + + +/*! recompute GS user instrument. */ +/*! get pointer to requested GS user instrument. +if it's not found, allocate a new item first. */ +Instruments::UserInstrument *Instruments::get_userinst(int bank, int prog) +{ + UserInstrument *p; + + for (p = userinst_first; p != NULL; p = p->next) { + if (p->bank == bank && p->prog == prog) { return p; } + } + + p = (UserInstrument *)safe_malloc(sizeof(UserInstrument)); + memset(p, 0, sizeof(UserInstrument)); + p->next = NULL; + if (userinst_first == NULL) { + userinst_first = p; + userinst_last = p; + } + else { + userinst_last->next = p; + userinst_last = p; + } + p->bank = bank; + p->prog = prog; + + return p; +} + + + +void Instruments::recompute_userinst(int bank, int prog) +{ + auto p = get_userinst(bank, prog); + auto source_bank = p->source_bank; + auto source_prog = p->source_prog; + free_tone_bank_element(&tonebank[bank]->tone[prog]); + if (tonebank[source_bank]) { + if (tonebank[source_bank]->tone[source_prog].name) + { + copy_tone_bank_element(&tonebank[bank]->tone[prog], &tonebank[source_bank]->tone[source_prog]); + } + else if (tonebank[0]->tone[source_prog].name) + { + copy_tone_bank_element(&tonebank[bank]->tone[prog], &tonebank[0]->tone[source_prog]); + } + } +} + +/*! get pointer to requested GS user drumset. +if it's not found, allocate a new item first. */ +Instruments::UserDrumset *Instruments::get_userdrum(int bank, int prog) +{ + UserDrumset *p; + + for (p = userdrum_first; p != NULL; p = p->next) { + if (p->bank == bank && p->prog == prog) { return p; } + } + + p = (UserDrumset *)safe_malloc(sizeof(UserDrumset)); + memset(p, 0, sizeof(UserDrumset)); + p->next = NULL; + if (userdrum_first == NULL) { + userdrum_first = p; + userdrum_last = p; + } + else { + userdrum_last->next = p; + userdrum_last = p; + } + p->bank = bank; + p->prog = prog; + + return p; +} + + +/*! recompute GS user drumset. */ +Instrument *Instruments::recompute_userdrum(int bank, int prog) +{ + Instrument *ip = NULL; + + auto p = get_userdrum(bank, prog); + auto source_note = p->source_note; + auto source_prog = p->source_prog; + + free_tone_bank_element(&drumset[bank]->tone[prog]); + if (drumset[source_prog]) { + ToneBankElement *source_tone = &drumset[source_prog]->tone[source_note]; + + if (source_tone->name == NULL /* NULL if "soundfont" directive is used */ + && source_tone->instrument == NULL) { + if ((ip = load_instrument(1, source_prog, source_note)) == NULL) { + ip = MAGIC_ERROR_INSTRUMENT; + } + source_tone->instrument = ip; + } + if (source_tone->name) + { + copy_tone_bank_element(&drumset[bank]->tone[prog], source_tone); + } + else if (drumset[0]->tone[source_note].name) + { + copy_tone_bank_element(&drumset[bank]->tone[prog], &drumset[0]->tone[source_note]); + } + else { + printMessage(CMSG_WARNING, VERB_NORMAL, "Referring user drum set %d, note %d not found - this instrument will not be heard as expected", bank, prog); + } + } + return ip; +} + +/*! convert GS user drumset assign groups to internal "alternate assign". */ +void Instruments::recompute_userdrum_altassign(int bank, int group) +{ + int number = 0, i; + char *params[131], param[10]; + ToneBank *bk; + UserDrumset *p; + + for (p = userdrum_first; p != NULL; p = p->next) { + if (p->assign_group == group) { + sprintf(param, "%d", p->prog); + params[number] = safe_strdup(param); + number++; + } + } + params[number] = NULL; + + alloc_instrument_bank(1, bank); + bk = drumset[bank]; + bk->alt = add_altassign_string(bk->alt, params, number); + for (i = number - 1; i >= 0; i--) + free(params[i]); +} + + + +} diff --git a/thirdparty/timidityplus/mblock.cpp b/thirdparty/timidityplus/mblock.cpp new file mode 100644 index 000000000..9116a3676 --- /dev/null +++ b/thirdparty/timidityplus/mblock.cpp @@ -0,0 +1,174 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include + +#include + +#include "timidity.h" +#include "common.h" + +namespace TimidityPlus +{ + +struct MBlock +{ + MBlockNode* free_mblock_list = NULL; + + ~MBlock() + { + int cnt; + + cnt = 0; + while (free_mblock_list) + { + MBlockNode* tmp; + + tmp = free_mblock_list; + free_mblock_list = free_mblock_list->next; + free(tmp); + cnt++; + } + } +}; + +static MBlock free_list; + +#define ADDRALIGN 8 +/* #define DEBUG */ + +void init_mblock(MBlockList *mblock) +{ + mblock->first = NULL; + mblock->allocated = 0; +} + +static MBlockNode *new_mblock_node(size_t n) +{ + MBlockNode *p; + + if (n > MIN_MBLOCK_SIZE) + { + if ((p = (MBlockNode *)safe_malloc(n + sizeof(MBlockNode))) == NULL) + return NULL; + p->block_size = n; + } + else if (free_list.free_mblock_list == NULL) + { + if ((p = (MBlockNode *)safe_malloc(sizeof(MBlockNode) + MIN_MBLOCK_SIZE)) == NULL) + return NULL; + p->block_size = MIN_MBLOCK_SIZE; + } + else + { + p = free_list.free_mblock_list; + free_list.free_mblock_list = free_list.free_mblock_list->next; + } + + p->offset = 0; + p->next = NULL; + + return p; +} + +static int enough_block_memory(MBlockList *mblock, size_t n) +{ + size_t newoffset; + + if(mblock->first == NULL) + return 0; + + newoffset = mblock->first->offset + n; + + if(newoffset < mblock->first->offset) /* exceed representable in size_t */ + return 0; + + if(newoffset > mblock->first->block_size) + return 0; + + return 1; +} + +void *new_segment(MBlockList *mblock, size_t nbytes) +{ + MBlockNode *p; + void *addr; + + /* round up to ADDRALIGN */ + nbytes = ((nbytes + ADDRALIGN - 1) & ~(ADDRALIGN - 1)); + if (!enough_block_memory(mblock, nbytes)) + { + p = new_mblock_node(nbytes); + p->next = mblock->first; + mblock->first = p; + mblock->allocated += p->block_size; + } + else + p = mblock->first; + + addr = (void *)(p->buffer + p->offset); + p->offset += nbytes; + + return addr; +} + +static void reuse_mblock1(MBlockNode *p) +{ + if (p->block_size > MIN_MBLOCK_SIZE) + free(p); + else /* p->block_size <= MIN_MBLOCK_SIZE */ + { + p->next = free_list.free_mblock_list; + free_list.free_mblock_list = p; + } +} + +void reuse_mblock(MBlockList *mblock) +{ + MBlockNode *p; + + if ((p = mblock->first) == NULL) + return; /* There is nothing to collect memory */ + + while (p) + { + MBlockNode *tmp; + + tmp = p; + p = p->next; + reuse_mblock1(tmp); + } + init_mblock(mblock); +} + +char *strdup_mblock(MBlockList *mblock, const char *str) +{ + int len; + char *p; + + len = (int)strlen(str); + p = (char *)new_segment(mblock, len + 1); /* for '\0' */ + memcpy(p, str, len + 1); + return p; +} + +} \ No newline at end of file diff --git a/thirdparty/timidityplus/mix.cpp b/thirdparty/timidityplus/mix.cpp new file mode 100644 index 000000000..7d7eff5d0 --- /dev/null +++ b/thirdparty/timidityplus/mix.cpp @@ -0,0 +1,1624 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + mix.c +*/ + +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "tables.h" +#include "resample.h" +#include "mix.h" +#include "optcode.h" + +namespace TimidityPlus +{ +extern float min_sustain_time; + + +#define FROM_FINAL_VOLUME(a) (a) + +#define OFFSET_MAX (0x3FFFFFFFL) + +typedef int32_t mix_t; + +#define MIXATION(a) *lp++ += (a) * s + +#define DELAYED_MIXATION(a) *lp++ += pan_delay_buf[pan_delay_spt]; \ + if (++pan_delay_spt == PAN_DELAY_BUF_MAX) {pan_delay_spt = 0;} \ + pan_delay_buf[pan_delay_wpt] = (a) * s; \ + if (++pan_delay_wpt == PAN_DELAY_BUF_MAX) {pan_delay_wpt = 0;} + + + + +/**************** interface function ****************/ +void Mixer::mix_voice(int32_t *buf, int v, int32_t c) +{ + Resampler re(player); + Voice *vp = player->voice + v; + resample_t *sp; + + if (vp->status == VOICE_DIE) + { + if (c >= MAX_DIE_TIME) + c = MAX_DIE_TIME; + sp = re.resample_voice(v, &c); + if (do_voice_filter(v, sp, filter_buffer, c)) { sp = filter_buffer; } + if (c > 0) + ramp_out(sp, buf, v, c); + player->free_voice(v); + } + else { + vp->delay_counter = c; + if (vp->delay) { + if (c < vp->delay) { + vp->delay -= c; + if (vp->tremolo_phase_increment) + update_tremolo(v); + if (timidity_modulation_envelope && vp->sample->modes & MODES_ENVELOPE) + update_modulation_envelope(v); + return; + } + buf += vp->delay * 2; + c -= vp->delay; + vp->delay = 0; + } + sp = re.resample_voice(v, &c); + if (do_voice_filter(v, sp, filter_buffer, c)) { sp = filter_buffer; } + + if (vp->panned == PANNED_MYSTERY) { + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_mystery_signal(sp, buf, v, c); + else + mix_mystery(sp, buf, v, c); + } + else if (vp->panned == PANNED_CENTER) { + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_center_signal(sp, buf, v, c); + else + mix_center(sp, buf, v, c); + } + else { + /* It's either full left or full right. In either case, + * every other sample is 0. Just get the offset right: + */ + if (vp->panned == PANNED_RIGHT) + buf++; + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_single_signal(sp, buf, v, c); + else + mix_single(sp, buf, v, c); + } + } +} + +/* return 1 if filter is enabled. */ +int Mixer::do_voice_filter(int v, resample_t *sp, mix_t *lp, int32_t count) +{ + FilterCoefficients *fc = &(player->voice[v].fc); + int32_t i, f, q, p, b0, b1, b2, b3, b4, t1, t2, x; + + if (fc->type == 1) { /* copy with applying Chamberlin's lowpass filter. */ + recalc_voice_resonance(v); + recalc_voice_fc(v); + f = fc->f, q = fc->q, b0 = fc->b0, b1 = fc->b1, b2 = fc->b2; + for(i = 0; i < count; i++) { + b0 = b0 + imuldiv24(b2, f); + b1 = sp[i] - b0 - imuldiv24(b2, q); + b2 = imuldiv24(b1, f) + b2; + lp[i] = b0; + } + fc->b0 = b0, fc->b1 = b1, fc->b2 = b2; + return 1; + } else if(fc->type == 2) { /* copy with applying Moog lowpass VCF. */ + recalc_voice_resonance(v); + recalc_voice_fc(v); + f = fc->f, q = fc->q, p = fc->p, b0 = fc->b0, b1 = fc->b1, + b2 = fc->b2, b3 = fc->b3, b4 = fc->b4; + for(i = 0; i < count; i++) { + x = sp[i] - imuldiv24(q, b4); /* feedback */ + t1 = b1; b1 = imuldiv24(x + b0, p) - imuldiv24(b1, f); + t2 = b2; b2 = imuldiv24(b1 + t1, p) - imuldiv24(b2, f); + t1 = b3; b3 = imuldiv24(b2 + t2, p) - imuldiv24(b3, f); + lp[i] = b4 = imuldiv24(b3 + t1, p) - imuldiv24(b4, f); + b0 = x; + } + fc->b0 = b0, fc->b1 = b1, fc->b2 = b2, fc->b3 = b3, fc->b4 = b4; + return 1; + } else { + return 0; + } +} + +//#define MOOG_RESONANCE_MAX 0.897638f +#define MOOG_RESONANCE_MAX 0.88f + +void Mixer::recalc_voice_resonance(int v) +{ + double q; + FilterCoefficients *fc = &(player->voice[v].fc); + + if (fc->reso_dB != fc->last_reso_dB || fc->q == 0) { + fc->last_reso_dB = fc->reso_dB; + if(fc->type == 1) { + q = 1.0 / chamberlin_filter_db_to_q_table[(int)(fc->reso_dB * 4)]; + fc->q = TIM_FSCALE(q, 24); + if(fc->q <= 0) {fc->q = 1;} /* must never be 0. */ + } else if(fc->type == 2) { + fc->reso_lin = fc->reso_dB * MOOG_RESONANCE_MAX / 20.0f; + if (fc->reso_lin > MOOG_RESONANCE_MAX) {fc->reso_lin = MOOG_RESONANCE_MAX;} + else if(fc->reso_lin < 0) {fc->reso_lin = 0;} + } + fc->last_freq = -1; + } +} + +void Mixer::recalc_voice_fc(int v) +{ + double f, p, q, fr; + FilterCoefficients *fc = &(player->voice[v].fc); + + if (fc->freq != fc->last_freq) { + if(fc->type == 1) { + f = 2.0 * sin(M_PI * (double)fc->freq / (double)playback_rate); + fc->f = TIM_FSCALE(f, 24); + } else if(fc->type == 2) { + fr = 2.0 * (double)fc->freq / (double)playback_rate; + q = 1.0 - fr; + p = fr + 0.8 * fr * q; + f = p + p - 1.0; + q = fc->reso_lin * (1.0 + 0.5 * q * (1.0 - q + 5.6 * q * q)); + fc->f = TIM_FSCALE(f, 24); + fc->p = TIM_FSCALE(p, 24); + fc->q = TIM_FSCALE(q, 24); + } + fc->last_freq = fc->freq; + } +} + +/* Ramp a note out in c samples */ +void Mixer::ramp_out(mix_t *sp, int32_t *lp, int v, int32_t c) +{ + /* should be final_volume_t, but uint8_t gives trouble. */ + int32_t left, right, li, ri, i; + /* silly warning about uninitialized s */ + mix_t s = 0; + Voice *vp = &player->voice[v]; + int32_t pan_delay_wpt = vp->pan_delay_wpt, *pan_delay_buf = vp->pan_delay_buf, + pan_delay_spt = vp->pan_delay_spt; + + left = player->voice[v].left_mix; + li = -(left / c); + if (! li) + li = -1; + if (true) { + if (player->voice[v].panned == PANNED_MYSTERY) { + right = player->voice[v].right_mix; + ri = -(right / c); + if(vp->pan_delay_rpt == 0) { + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + left = 0; + right += ri; + if (right < 0) + right = 0; + s = *sp++; + MIXATION(left); + MIXATION(right); + } + } else if(vp->panning < 64) { + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + left = 0; + right += ri; + if (right < 0) + right = 0; + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + } + } else { + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + left = 0; + right += ri; + if (right < 0) + right = 0; + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + } + } + vp->pan_delay_wpt = pan_delay_wpt; + vp->pan_delay_spt = pan_delay_spt; + } else if (player->voice[v].panned == PANNED_CENTER) + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + return; + s = *sp++; + MIXATION(left); + MIXATION(left); + } + else if (player->voice[v].panned == PANNED_LEFT) + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + return; + s = *sp++; + MIXATION(left); + lp++; + } + else if (player->voice[v].panned == PANNED_RIGHT) + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + return; + s = *sp++; + lp++; + MIXATION(left); + } + } else + /* Mono output. */ + for (i = 0; i < c; i++) { + left += li; + if (left < 0) + return; + s = *sp++; + MIXATION(left); + } +} + +void Mixer::mix_mono_signal( + mix_t *sp, int32_t *lp, int v, int count) +{ + Voice *vp = player->voice + v; + final_volume_t left = vp->left_mix; + int cc, i; + mix_t s; + int32_t linear_left; + + if (! (cc = vp->control_counter)) { + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + } + compute_mix_smoothing(vp); + while (count) + if (cc < count) { + count -= cc; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < cc; i++) { + s = *sp++; + MIXATION(left); + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = linear_left; + cc -= i; + for (i = 0; i < cc; i++) { + s = *sp++; + MIXATION(left); + } + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + compute_mix_smoothing(vp); + } else { + vp->control_counter = cc - count; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < count; i++) { + s = *sp++; + MIXATION(left); + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = linear_left; + count -= i; + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + } + return; + } +} + + +void Mixer::mix_mystery_signal( + mix_t *sp, int32_t *lp, int v, int count) +{ + Voice *vp = player->voice + v; + final_volume_t left = vp->left_mix, right = vp->right_mix; + int cc, i; + mix_t s; + int32_t linear_left, linear_right; + int32_t pan_delay_wpt = vp->pan_delay_wpt, *pan_delay_buf = vp->pan_delay_buf, + pan_delay_spt = vp->pan_delay_spt; + + if (! (cc = vp->control_counter)) { + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + right = vp->right_mix; + } + compute_mix_smoothing(vp); + while (count) + if (cc < count) { + count -= cc; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + linear_right = FROM_FINAL_VOLUME(right); + if (vp->right_mix_offset) { + linear_right += vp->right_mix_offset; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + if(vp->pan_delay_rpt == 0) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < cc; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else if(vp->panning < 64) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < cc; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < cc; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } + vp->old_left_mix = linear_left; + vp->old_right_mix = linear_right; + cc -= i; + if(vp->pan_delay_rpt == 0) { + for (i = 0; i < cc; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + } + } else if(vp->panning < 64) { + for (i = 0; i < cc; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + } + } else { + for (i = 0; i < cc; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + } + } + + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + right = vp->right_mix; + compute_mix_smoothing(vp); + } else { + vp->control_counter = cc - count; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + linear_right = FROM_FINAL_VOLUME(right); + if (vp->right_mix_offset) { + linear_right += vp->right_mix_offset; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + if(vp->pan_delay_rpt == 0) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else if(vp->panning < 64) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } + + vp->old_left_mix = linear_left; + vp->old_right_mix = linear_right; + count -= i; + if(vp->pan_delay_rpt == 0) { + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + } + } else if(vp->panning < 64) { + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + } + } else { + for (i = 0; i < count; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + } + } + vp->pan_delay_wpt = pan_delay_wpt; + vp->pan_delay_spt = pan_delay_spt; + return; + } +} + +void Mixer::mix_mystery(mix_t *sp, int32_t *lp, int v, int count) +{ + final_volume_t left = player->voice[v].left_mix, right = player->voice[v].right_mix; + mix_t s; + int i; + Voice *vp = player->voice + v; + int32_t linear_left, linear_right; + int32_t pan_delay_wpt = vp->pan_delay_wpt, *pan_delay_buf = vp->pan_delay_buf, + pan_delay_spt = vp->pan_delay_spt; + + compute_mix_smoothing(vp); + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + linear_right = FROM_FINAL_VOLUME(right); + if (vp->right_mix_offset) { + linear_right += vp->right_mix_offset; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + if(vp->pan_delay_rpt == 0) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else if(vp->panning < 64) { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } else { + for (i = 0; (vp->left_mix_offset | vp->right_mix_offset) + && i < count; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + if (vp->left_mix_offset) { + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + if (vp->right_mix_offset) { + vp->right_mix_offset += vp->right_mix_inc; + linear_right += vp->right_mix_inc; + if (linear_right > MAX_AMP_VALUE) { + linear_right = MAX_AMP_VALUE; + vp->right_mix_offset = 0; + } + right = FINAL_VOLUME(linear_right); + } + } + } + + vp->old_left_mix = linear_left; + vp->old_right_mix = linear_right; + count -= i; + if(vp->pan_delay_rpt == 0) { + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(right); + } + } else if(vp->panning < 64) { + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + DELAYED_MIXATION(right); + } + } else { + for (i = 0; i < count; i++) { + s = *sp++; + DELAYED_MIXATION(left); + MIXATION(right); + } + } + vp->pan_delay_wpt = pan_delay_wpt; + vp->pan_delay_spt = pan_delay_spt; +} + + +void Mixer::mix_center_signal( + mix_t *sp, int32_t *lp, int v, int count) +{ + Voice *vp = player->voice + v; + final_volume_t left=vp->left_mix; + int cc, i; + mix_t s; + int32_t linear_left; + + if (! (cc = vp->control_counter)) { + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + } + compute_mix_smoothing(vp); + while (count) + if (cc < count) { + count -= cc; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < cc; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = vp->old_right_mix = linear_left; + cc -= i; + for (i = 0; i < cc; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + } + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + compute_mix_smoothing(vp); + } else { + vp->control_counter = cc - count; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = vp->old_right_mix = linear_left; + count -= i; + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + } + return; + } +} + +void Mixer::mix_center(mix_t *sp, int32_t *lp, int v, int count) +{ + final_volume_t left = player->voice[v].left_mix; + mix_t s; + int i; + Voice *vp = player->voice + v; + int32_t linear_left; + + compute_mix_smoothing(vp); + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = vp->old_right_mix = linear_left; + count -= i; + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + MIXATION(left); + } +} + +void Mixer::mix_single_signal(mix_t *sp, int32_t *lp, int v, int count) +{ + Voice *vp = player->voice + v; + final_volume_t left = vp->left_mix; + int cc, i; + mix_t s; + int32_t linear_left; + + if (!(cc = vp->control_counter)) { + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + } + compute_mix_smoothing(vp); + while (count) + if (cc < count) { + count -= cc; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < cc; i++) { + s = *sp++; + MIXATION(left); + lp++; + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = linear_left; + cc -= i; + for (i = 0; i < cc; i++) { + s = *sp++; + MIXATION(left); + lp++; + } + cc = control_ratio; + if (update_signal(v)) + /* Envelope ran out */ + return; + left = vp->left_mix; + compute_mix_smoothing(vp); + } else { + vp->control_counter = cc - count; + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < count; i++) { + s = *sp++; + MIXATION(left); + lp++; + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = linear_left; + count -= i; + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + lp++; + } + return; + } +} + +void Mixer::mix_single(mix_t *sp, int32_t *lp, int v, int count) +{ + final_volume_t left = player->voice[v].left_mix; + mix_t s; + int i; + Voice *vp = player->voice + v; + int32_t linear_left; + + compute_mix_smoothing(vp); + linear_left = FROM_FINAL_VOLUME(left); + if (vp->left_mix_offset) { + linear_left += vp->left_mix_offset; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + for (i = 0; vp->left_mix_offset && i < count; i++) { + s = *sp++; + MIXATION(left); + lp++; + vp->left_mix_offset += vp->left_mix_inc; + linear_left += vp->left_mix_inc; + if (linear_left > MAX_AMP_VALUE) { + linear_left = MAX_AMP_VALUE; + vp->left_mix_offset = 0; + } + left = FINAL_VOLUME(linear_left); + } + vp->old_left_mix = linear_left; + count -= i; + for (i = 0; i < count; i++) { + s = *sp++; + MIXATION(left); + lp++; + } +} + +/* Returns 1 if the note died */ +int Mixer::update_signal(int v) +{ + Voice *vp = &player->voice[v]; + + if (vp->envelope_increment && update_envelope(v)) + return 1; + if (vp->tremolo_phase_increment) + update_tremolo(v); + if (timidity_modulation_envelope && vp->sample->modes & MODES_ENVELOPE) + update_modulation_envelope(v); + return apply_envelope_to_amp(v); +} + +int Mixer::update_envelope(int v) +{ + Voice *vp = &player->voice[v]; + + vp->envelope_volume += vp->envelope_increment; + if ((vp->envelope_increment < 0) + ^ (vp->envelope_volume > vp->envelope_target)) { + vp->envelope_volume = vp->envelope_target; + if (recompute_envelope(v)) + return 1; + } + return 0; +} + +int Mixer::get_eg_stage(int v, int stage) +{ + int eg_stage; + Voice *vp = &player->voice[v]; + + eg_stage = stage; + if (vp->sample->inst_type == INST_SF2) { + if (stage >= EG_SF_RELEASE) { + eg_stage = EG_RELEASE; + } + } else { + if (stage == EG_GUS_DECAY) { + eg_stage = EG_DECAY; + } else if (stage == EG_GUS_SUSTAIN) { + eg_stage = EG_NULL; + } else if (stage >= EG_GUS_RELEASE1) { + eg_stage = EG_RELEASE; + } + } + return eg_stage; +} + + +/* Returns 1 if envelope runs out */ +int Mixer::recompute_envelope(int v) +{ + int stage, ch; + double sustain_time; + int32_t envelope_width; + Voice *vp = &player->voice[v]; + + stage = vp->envelope_stage; + if (stage > EG_GUS_RELEASE3) { + voice_ran_out(v); + return 1; + } else if (stage > EG_GUS_SUSTAIN && vp->envelope_volume <= 0) { + /* Remove silent player->voice in the release stage */ + voice_ran_out(v); + return 1; + } + + /* Routine to decay the sustain envelope + * + * Disabled if !min_sustain_time. + * min_sustain_time is given in msec, and is the minimum + * time it will take to decay a note to zero. + * 2000-3000 msec seem to be decent values to use. + */ + if (stage == EG_GUS_RELEASE1 && vp->sample->modes & MODES_ENVELOPE + && vp->status & (VOICE_ON | VOICE_SUSTAINED)) { + + int32_t new_rate; + + ch = vp->channel; + + /* Don't adjust the current rate if VOICE_ON */ + if (vp->status & VOICE_ON) + return 0; + + if (min_sustain_time > 0 || player->channel[ch].loop_timeout > 0) { + if (min_sustain_time == 1) + /* The sustain stage is ignored. */ + return next_stage(v); + + if (player->channel[ch].loop_timeout > 0 && + player->channel[ch].loop_timeout * 1000 < min_sustain_time) { + /* timeout (See also "#extension timeout" line in *.cfg file */ + sustain_time = player->channel[ch].loop_timeout * 1000; + } + else { + sustain_time = min_sustain_time; + } + + /* Sustain must not be 0 or else lots of dead notes! */ + if (player->channel[ch].sostenuto == 0 && + player->channel[ch].sustain > 0) { + sustain_time *= (double)player->channel[ch].sustain / 127.0f; + } + + /* Calculate the width of the envelope */ + envelope_width = sustain_time * playback_rate + / (1000.0f * (double)control_ratio); + + if (vp->sample->inst_type == INST_SF2) { + /* If the instrument is SoundFont, it sustains at the sustain stage. */ + vp->envelope_increment = -1; + vp->envelope_target = vp->envelope_volume - envelope_width; + if (vp->envelope_target < 0) {vp->envelope_target = 0;} + } else { + /* Otherwise, it decays at the sustain stage. */ + vp->envelope_target = 0; + new_rate = vp->envelope_volume / envelope_width; + /* Use the Release1 rate if slower than new rate */ + if (vp->sample->envelope_rate[EG_GUS_RELEASE1] && + vp->sample->envelope_rate[EG_GUS_RELEASE1] < new_rate) + new_rate = vp->sample->envelope_rate[EG_GUS_RELEASE1]; + /* Use the Sustain rate if slower than new rate */ + /* (Sustain rate exists only in GUS patches) */ + if (vp->sample->inst_type == INST_GUS && + vp->sample->envelope_rate[EG_GUS_SUSTAIN] && + vp->sample->envelope_rate[EG_GUS_SUSTAIN] < new_rate) + new_rate = vp->sample->envelope_rate[EG_GUS_SUSTAIN]; + /* Avoid freezing */ + if (!new_rate) + new_rate = 1; + vp->envelope_increment = -new_rate; + } + } + return 0; + } + return next_stage(v); +} + +/* Envelope ran out. */ +void Mixer::voice_ran_out(int v) +{ + player->free_voice(v); +} + +int Mixer::next_stage(int v) +{ + int stage, ch, eg_stage; + int32_t offset, val; + double rate, temp_rate; + Voice *vp = &player->voice[v]; + + stage = vp->envelope_stage++; + offset = vp->sample->envelope_offset[stage]; + rate = vp->sample->envelope_rate[stage]; + if (vp->envelope_volume == offset + || (stage > EG_GUS_SUSTAIN && vp->envelope_volume < offset)) + return recompute_envelope(v); + ch = vp->channel; + /* there is some difference between GUS patch and Soundfont at envelope. */ + eg_stage = get_eg_stage(v, stage); + + /* HACK -- force ramps to occur over 20 msec windows to avoid pops */ + /* Do not apply to attack envelope */ + if (eg_stage > EG_ATTACK) + { + temp_rate = control_ratio * (labs(vp->envelope_volume - offset) / + (playback_rate * 0.02)); + if (temp_rate < 1) + temp_rate = 1; + if (rate < 0) + temp_rate = -temp_rate; + if (fabs(temp_rate) < fabs(rate)) + rate = temp_rate; + } + + /* envelope generator (see also playmidi.[ch]) */ + if (player->ISDRUMCHANNEL(ch)) + val = (player->channel[ch].drums[vp->note] != NULL) + ? player->channel[ch].drums[vp->note]->drum_envelope_rate[eg_stage] + : -1; + else { + if (vp->sample->envelope_keyf[stage]) /* envelope key-follow */ + rate *= pow(2.0, (double) (player->voice[v].note - 60) + * (double)vp->sample->envelope_keyf[stage] / 1200.0f); + val = player->channel[ch].envelope_rate[eg_stage]; + } + if (vp->sample->envelope_velf[stage]) /* envelope velocity-follow */ + rate *= pow(2.0, (double) (player->voice[v].velocity - vp->sample->envelope_velf_bpo) + * (double)vp->sample->envelope_velf[stage] / 1200.0f); + + /* just before release-phase, some modifications are necessary */ + if (stage > EG_GUS_SUSTAIN) { + /* adjusting release-rate for consistent release-time */ + rate *= (double) vp->envelope_volume + / vp->sample->envelope_offset[EG_GUS_ATTACK]; + /* calculating current envelope scale and a inverted value for optimization */ + vp->envelope_scale = vp->last_envelope_volume; + vp->inv_envelope_scale = TIM_FSCALE(OFFSET_MAX / (double)vp->envelope_volume, 16); + } + + /* regularizing envelope */ + if (offset < vp->envelope_volume) { /* decaying phase */ + if (val != -1) { + if(eg_stage > EG_DECAY) { + rate *= sc_eg_release_table[val & 0x7f]; + } else { + rate *= sc_eg_decay_table[val & 0x7f]; + } + + if (fabs(rate) > OFFSET_MAX) + rate = (rate > 0) ? OFFSET_MAX : -OFFSET_MAX; + else if (fabs(rate) < 1) + rate = (rate > 0) ? 1 : -1; + } + if(stage < EG_SF_DECAY && rate > OFFSET_MAX) { /* instantaneous decay */ + vp->envelope_volume = offset; + return recompute_envelope(v); + } else if(rate > vp->envelope_volume - offset) { /* fastest decay */ + rate = -vp->envelope_volume + offset - 1; + } else if (rate < 1) { /* slowest decay */ + rate = -1; + } + else { /* ordinary decay */ + rate = -rate; + } + } else { /* attacking phase */ + if (val != -1) { + rate *= sc_eg_attack_table[val & 0x7f]; + + if (fabs(rate) > OFFSET_MAX) + rate = (rate > 0) ? OFFSET_MAX : -OFFSET_MAX; + else if (fabs(rate) < 1) + rate = (rate > 0) ? 1 : -1; + } + if(stage < EG_SF_DECAY && rate > OFFSET_MAX) { /* instantaneous attack */ + vp->envelope_volume = offset; + return recompute_envelope(v); + } else if(rate > offset - vp->envelope_volume) { /* fastest attack */ + rate = offset - vp->envelope_volume + 1; + } else if (rate < 1) {rate = 1;} /* slowest attack */ + } + + /* HACK -- force ramps to occur over 20 msec windows to avoid pops */ + /* Do not apply to attack envelope */ + /* Must check again in case the above conditions shortened it */ + if (eg_stage > EG_ATTACK) + { + temp_rate = control_ratio * (labs(vp->envelope_volume - offset) / + (playback_rate * 0.02)); + if (temp_rate < 1) + temp_rate = 1; + if (rate < 0) + temp_rate = -temp_rate; + if (fabs(temp_rate) < fabs(rate)) + rate = temp_rate; + } + + vp->envelope_increment = (int32_t)rate; + vp->envelope_target = offset; + + return 0; +} + +void Mixer::update_tremolo(int v) +{ + Voice *vp = &player->voice[v]; + int32_t depth = vp->tremolo_depth << 7; + + if(vp->tremolo_delay > 0) + { + vp->tremolo_delay -= vp->delay_counter; + if(vp->tremolo_delay > 0) { + vp->tremolo_volume = 1.0; + return; + } + vp->tremolo_delay = 0; + } + if (vp->tremolo_sweep) { + /* Update sweep position */ + vp->tremolo_sweep_position += vp->tremolo_sweep; + if (vp->tremolo_sweep_position >= 1 << SWEEP_SHIFT) + /* Swept to max amplitude */ + vp->tremolo_sweep = 0; + else { + /* Need to adjust depth */ + depth *= vp->tremolo_sweep_position; + depth >>= SWEEP_SHIFT; + } + } + vp->tremolo_phase += vp->tremolo_phase_increment; + + if(vp->sample->inst_type == INST_SF2) { + vp->tremolo_volume = 1.0 + TIM_FSCALENEG( + lookup_sine(vp->tremolo_phase >> RATE_SHIFT) + * depth * TREMOLO_AMPLITUDE_TUNING, 17); + } else { + vp->tremolo_volume = 1.0 + TIM_FSCALENEG( + lookup_sine(vp->tremolo_phase >> RATE_SHIFT) + * depth * TREMOLO_AMPLITUDE_TUNING, 17); + } + /* I'm not sure about the +1.0 there -- it makes tremoloed voices' + * volumes on average the lower the higher the tremolo amplitude. + */ +} + +int Mixer::apply_envelope_to_amp(int v) +{ + Voice *vp = &player->voice[v]; + double lamp = vp->left_amp, ramp, + *v_table = vp->sample->inst_type == INST_SF2 ? sb_vol_table : player->vol_table; + int32_t la, ra; + + if (vp->panned == PANNED_MYSTERY) { + ramp = vp->right_amp; + if (vp->tremolo_phase_increment) { + lamp *= vp->tremolo_volume; + ramp *= vp->tremolo_volume; + } + if (vp->sample->modes & MODES_ENVELOPE) { + if (vp->envelope_stage > 3) + vp->last_envelope_volume = v_table[ + imuldiv16(vp->envelope_volume, + vp->inv_envelope_scale) >> 20] + * vp->envelope_scale; + else if (vp->envelope_stage > 1) + vp->last_envelope_volume = v_table[ + vp->envelope_volume >> 20]; + else + vp->last_envelope_volume = attack_vol_table[ + vp->envelope_volume >> 20]; + lamp *= vp->last_envelope_volume; + ramp *= vp->last_envelope_volume; + } + la = TIM_FSCALE(lamp, AMP_BITS); + if (la > MAX_AMP_VALUE) + la = MAX_AMP_VALUE; + ra = TIM_FSCALE(ramp, AMP_BITS); + if (ra > MAX_AMP_VALUE) + ra = MAX_AMP_VALUE; + if ((vp->status & (VOICE_OFF | VOICE_SUSTAINED)) + && (la | ra) <= 0) { + player->free_voice(v); + return 1; + } + vp->left_mix = FINAL_VOLUME(la); + vp->right_mix = FINAL_VOLUME(ra); + } else { + if (vp->tremolo_phase_increment) + lamp *= vp->tremolo_volume; + if (vp->sample->modes & MODES_ENVELOPE) { + if (vp->envelope_stage > 3) + vp->last_envelope_volume = v_table[ + imuldiv16(vp->envelope_volume, + vp->inv_envelope_scale) >> 20] + * vp->envelope_scale; + else if (vp->envelope_stage > 1) + vp->last_envelope_volume = v_table[ + vp->envelope_volume >> 20]; + else + vp->last_envelope_volume = attack_vol_table[ + vp->envelope_volume >> 20]; + lamp *= vp->last_envelope_volume; + } + la = TIM_FSCALE(lamp, AMP_BITS); + if (la > MAX_AMP_VALUE) + la = MAX_AMP_VALUE; + if ((vp->status & (VOICE_OFF | VOICE_SUSTAINED)) + && la <= 0) { + player->free_voice(v); + return 1; + } + vp->left_mix = FINAL_VOLUME(la); + } + return 0; +} + +void Mixer::compute_mix_smoothing(Voice *vp) +{ + int32_t max_win, delta; + + /* reduce popping -- ramp the amp over a 20 msec window */ + max_win = (playback_rate * 0.02) / control_ratio; + delta = FROM_FINAL_VOLUME(vp->left_mix) - vp->old_left_mix; + if (labs(delta) > max_win) { + vp->left_mix_inc = delta / max_win; + vp->left_mix_offset = vp->left_mix_inc * (1 - max_win); + } else if (delta) { + vp->left_mix_inc = -1; + if (delta > 0) + vp->left_mix_inc = 1; + vp->left_mix_offset = vp->left_mix_inc - delta; + } + delta = FROM_FINAL_VOLUME(vp->right_mix) - vp->old_right_mix; + if (labs(delta) > max_win) { + vp->right_mix_inc = delta / max_win; + vp->right_mix_offset = vp->right_mix_inc * (1 - max_win); + } else if (delta) { + vp->right_mix_inc = -1; + if (delta > 0) + vp->right_mix_inc = 1; + vp->right_mix_offset = vp->right_mix_inc - delta; + } +} + +int Mixer::update_modulation_envelope(int v) +{ + Voice *vp = &player->voice[v]; + + if(vp->modenv_delay > 0) { + vp->modenv_delay -= vp->delay_counter; + if(vp->modenv_delay > 0) {return 1;} + vp->modenv_delay = 0; + } + vp->modenv_volume += vp->modenv_increment; + if ((vp->modenv_increment < 0) + ^ (vp->modenv_volume > vp->modenv_target)) { + vp->modenv_volume = vp->modenv_target; + if (recompute_modulation_envelope(v)) { + apply_modulation_envelope(v); + return 1; + } + } + + apply_modulation_envelope(v); + + return 0; +} + +int Mixer::apply_modulation_envelope(int v) +{ + Voice *vp = &player->voice[v]; + + if(!timidity_modulation_envelope) {return 0;} + + if (vp->sample->modes & MODES_ENVELOPE) { + vp->last_modenv_volume = modenv_vol_table[vp->modenv_volume >> 20]; + } + + player->recompute_voice_filter(v); + if(!(vp->porta_control_ratio && vp->porta_control_counter == 0)) { + player->recompute_freq(v); + } + return 0; +} + +int Mixer::modenv_next_stage(int v) +{ + int stage, ch, eg_stage; + int32_t offset, val; + double rate; + Voice *vp = &player->voice[v]; + + stage = vp->modenv_stage++; + offset = vp->sample->modenv_offset[stage]; + rate = vp->sample->modenv_rate[stage]; + if (vp->modenv_volume == offset + || (stage > EG_GUS_SUSTAIN && vp->modenv_volume < offset)) + return recompute_modulation_envelope(v); + else if(stage < EG_SF_DECAY && rate > OFFSET_MAX) { /* instantaneous attack */ + vp->modenv_volume = offset; + return recompute_modulation_envelope(v); + } + ch = vp->channel; + /* there is some difference between GUS patch and Soundfont at envelope. */ + eg_stage = get_eg_stage(v, stage); + + /* envelope generator (see also playmidi.[ch]) */ + if (player->ISDRUMCHANNEL(ch)) + val = (player->channel[ch].drums[vp->note] != NULL) + ? player->channel[ch].drums[vp->note]->drum_envelope_rate[eg_stage] + : -1; + else { + if (vp->sample->modenv_keyf[stage]) /* envelope key-follow */ + rate *= pow(2.0, (double) (player->voice[v].note - 60) + * (double)vp->sample->modenv_keyf[stage] / 1200.0f); + val = player->channel[ch].envelope_rate[eg_stage]; + } + if (vp->sample->modenv_velf[stage]) + rate *= pow(2.0, (double) (player->voice[v].velocity - vp->sample->modenv_velf_bpo) + * (double)vp->sample->modenv_velf[stage] / 1200.0f); + + /* just before release-phase, some modifications are necessary */ + if (stage > EG_GUS_SUSTAIN) { + /* adjusting release-rate for consistent release-time */ + rate *= (double) vp->modenv_volume + / vp->sample->modenv_offset[EG_GUS_ATTACK]; + } + + /* regularizing envelope */ + if (offset < vp->modenv_volume) { /* decaying phase */ + if (val != -1) { + if(stage > EG_DECAY) { + rate *= sc_eg_release_table[val & 0x7f]; + } else { + rate *= sc_eg_decay_table[val & 0x7f]; + } + } + if(rate > vp->modenv_volume - offset) { /* fastest decay */ + rate = -vp->modenv_volume + offset - 1; + } else if (rate < 1) { /* slowest decay */ + rate = -1; + } else { /* ordinary decay */ + rate = -rate; + } + } else { /* attacking phase */ + if (val != -1) + rate *= sc_eg_attack_table[val & 0x7f]; + if(rate > offset - vp->modenv_volume) { /* fastest attack */ + rate = offset - vp->modenv_volume + 1; + } else if (rate < 1) {rate = 1;} /* slowest attack */ + } + + vp->modenv_increment = (int32_t)rate; + vp->modenv_target = offset; + + return 0; +} + +int Mixer::recompute_modulation_envelope(int v) +{ + int stage, ch; + double sustain_time; + int32_t modenv_width; + Voice *vp = &player->voice[v]; + + if(!timidity_modulation_envelope) {return 0;} + + stage = vp->modenv_stage; + if (stage > EG_GUS_RELEASE3) {return 1;} + else if (stage > EG_GUS_SUSTAIN && vp->modenv_volume <= 0) { + return 1; + } + + /* Routine to sustain modulation envelope + * + * Disabled if !min_sustain_time. + * min_sustain_time is given in msec, and is the minimum + * time it will take to sustain a note. + * 2000-3000 msec seem to be decent values to use. + */ + if (stage == EG_GUS_RELEASE1 && vp->sample->modes & MODES_ENVELOPE + && vp->status & (VOICE_ON | VOICE_SUSTAINED)) { + ch = vp->channel; + + /* Don't adjust the current rate if VOICE_ON */ + if (vp->status & VOICE_ON) + return 0; + + if (min_sustain_time > 0 || player->channel[ch].loop_timeout > 0) { + if (min_sustain_time == 1) + /* The sustain stage is ignored. */ + return modenv_next_stage(v); + + if (player->channel[ch].loop_timeout > 0 && + player->channel[ch].loop_timeout * 1000 < min_sustain_time) { + /* timeout (See also "#extension timeout" line in *.cfg file */ + sustain_time = player->channel[ch].loop_timeout * 1000; + } + else { + sustain_time = min_sustain_time; + } + + /* Sustain must not be 0 or else lots of dead notes! */ + if (player->channel[ch].sostenuto == 0 && + player->channel[ch].sustain > 0) { + sustain_time *= (double)player->channel[ch].sustain / 127.0f; + } + + /* Calculate the width of the envelope */ + modenv_width = sustain_time * playback_rate + / (1000.0f * (double)control_ratio); + vp->modenv_increment = -1; + vp->modenv_target = vp->modenv_volume - modenv_width; + if (vp->modenv_target < 0) {vp->modenv_target = 0;} + } + return 0; + } + return modenv_next_stage(v); +} + +} diff --git a/thirdparty/timidityplus/playmidi.cpp b/thirdparty/timidityplus/playmidi.cpp new file mode 100644 index 000000000..9b1c1b6bf --- /dev/null +++ b/thirdparty/timidityplus/playmidi.cpp @@ -0,0 +1,6149 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 2018 Christoph Oelckers + Copyright (C) 1999-2009 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + playmidi.c -- random stuff in need of rearrangement +*/ + +#include +#include +#include + +#include +#include +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "mix.h" +#include "recache.h" +#include "reverb.h" +#include "freq.h" +#include "quantity.h" +#include "tables.h" +#include "effect.h" + + +namespace TimidityPlus +{ + FCriticalSection ConfigMutex; + int timidity_modulation_wheel = true; + int timidity_portamento = false; + int timidity_reverb = 0; + int timidity_chorus = 0; + int timidity_surround_chorus = false; // requires restart! + int timidity_channel_pressure = false; + int timidity_lpf_def = true; + int timidity_temper_control = true; + int timidity_modulation_envelope = true; + int timidity_overlap_voice_allow = true; + int timidity_drum_effect = false; + int timidity_pan_delay = false; + float timidity_drum_power = 1.f; + int timidity_key_adjust = 0; + float timidity_tempo_adjust = 1.f; + float min_sustain_time = 5000; + + // The following options have no generic use and are only meaningful for some SYSEX events not normally found in common MIDIs. + // For now they are kept as unchanging global variables + static bool opt_eq_control = false; + static bool op_nrpn_vibrato = true; + static bool opt_tva_attack = false; + static bool opt_tva_decay = false; + static bool opt_tva_release = false; + static bool opt_insertion_effect = false; + static bool opt_delay_control = false; + + +// These two variables need to remain global or things will get messy because they get accessed from non-class code. +int32_t control_ratio = 22; +int32_t playback_rate = 22050; + +#define PLAY_INTERLEAVE_SEC 1.0 +#define PORTAMENTO_TIME_TUNING (1.0 / 5000.0) +#define PORTAMENTO_CONTROL_RATIO 256 /* controls per sec */ +#define DEFAULT_CHORUS_DELAY1 0.02 +#define DEFAULT_CHORUS_DELAY2 0.003 +#define CHORUS_OPPOSITE_THRESHOLD 32 +#define EOT_PRESEARCH_LEN 32 +#define SPEED_CHANGE_RATE 1.0594630943592953 /* 2^(1/12) */ +#define DEFAULT_AMPLIFICATION 70 +#define VIBRATO_DEPTH_MAX 384 /* 600 cent */ + +void set_playback_rate(int freq) +{ + const int CONTROLS_PER_SECOND = 1000; + const int MAX_CONTROL_RATIO = 255; + + playback_rate = freq; + control_ratio = playback_rate / CONTROLS_PER_SECOND; + if (control_ratio < 1) + control_ratio = 1; + else if (control_ratio > MAX_CONTROL_RATIO) + control_ratio = MAX_CONTROL_RATIO; +} + + +Player::Player(Instruments *instr) +{ + last_reverb_setting = timidity_reverb; + memset(this, 0, sizeof(*this)); + + // init one-time global stuff - this should go to the device class once it exists. + instruments = instr; + initialize_resampler_coeffs(); + init_tables(); + + new_midi_file_info(); + init_mblock(&playmidi_pool); + + reverb = new Reverb; + reverb->init_effect_status(play_system_mode); + effect = new Effect(reverb); + + + mixer = new Mixer(this); + recache = new Recache(this); + + for (int i = 0; i < MAX_CHANNELS; i++) + init_channel_layer(i); + + instruments->init_userdrum(); + instruments->init_userinst(); + + master_volume_ratio = 0xFFFF; + vol_table = def_vol_table; + + play_system_mode = DEFAULT_SYSTEM_MODE; + midi_streaming = 0; + stream_max_compute = 500; /* compute time limit (in msec) when streaming */ + current_keysig = 0; + current_temper_keysig = 0; + temper_adj = 0; + current_play_tempo = 500000; + opt_realtime_playing = 0; + check_eot_flag = 0; + playmidi_seek_flag = 0; + opt_pure_intonation = 0; + current_freq_table = 0; + current_temper_freq_table = 0; + master_tuning = 0; + + make_rvid_flag = 0; /* For reverb optimization */ + + voices = DEFAULT_VOICES; + amplification = DEFAULT_AMPLIFICATION; + + + static const int drums[] = { 10, -1 }; + + CLEAR_CHANNELMASK(default_drumchannels); + for (int i = 0; drums[i] > 0; i++) + { + SET_CHANNELMASK(default_drumchannels, drums[i] - 1); + } + for (int i = 16; i < MAX_CHANNELS; i++) + { + if (IS_SET_CHANNELMASK(default_drumchannels, i & 0xF)) + SET_CHANNELMASK(default_drumchannels, i); + } + COPY_CHANNELMASK(drumchannels, default_drumchannels); + COPY_CHANNELMASK(drumchannel_mask, default_drumchannel_mask); + +} + +Player::~Player() +{ + reuse_mblock(&playmidi_pool); + if (reverb_buffer != nullptr) free(reverb_buffer); + for (int i = 0; i < MAX_CHANNELS; i++) free_drum_effect(i); + delete mixer; + delete recache; + delete effect; + delete reverb; +} + + +bool Player::IS_SYSEX_EVENT_TYPE(MidiEvent *event) +{ + return ((event)->type == ME_NONE || (event)->type >= ME_RANDOM_PAN || (event)->b == SYSEX_TAG); +} + + +void Player::init_freq_table_user(void) +{ + int p, i, j, k, l; + double f; + + for (p = 0; p < 4; p++) + for (i = 0; i < 12; i++) + for (j = -1; j < 11; j++) { + f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5); + for (k = 0; k < 12; k++) { + l = i + j * 12 + k; + if (l < 0 || l >= 128) + continue; + freq_table_user[p][i][l] = f * 1000 + 0.5; + freq_table_user[p][i + 12][l] = f * 1000 + 0.5; + freq_table_user[p][i + 24][l] = f * 1000 + 0.5; + freq_table_user[p][i + 36][l] = f * 1000 + 0.5; + } + } +} + + +/*! convert Hz to internal vibrato control ratio. */ +double Player::cnv_Hz_to_vib_ratio(double freq) +{ + return ((double)(playback_rate) / (freq * 2.0f * VIBRATO_SAMPLE_INCREMENTS)); +} + +void Player::adjust_amplification(void) +{ + static const double compensation_ratio = 1.0; + /* compensate master volume */ + master_volume = (double)(amplification) / 100.0 * + ((double)master_volume_ratio * (compensation_ratio/0xFFFF)); +} + +int Player::new_vidq(int ch, int note) +{ + int i; + + if(timidity_overlap_voice_allow) + { + i = ch * 128 + note; + return vidq_head[i]++; + } + return 0; +} + +int Player::last_vidq(int ch, int note) +{ + int i; + + if(timidity_overlap_voice_allow) + { + i = ch * 128 + note; + if(vidq_head[i] == vidq_tail[i]) + { + return -1; + } + return vidq_tail[i]++; + } + return 0; +} + +void Player::reset_voices(void) +{ + int i; + for(i = 0; i < max_voices; i++) + { + voice[i].status = VOICE_FREE; + voice[i].temper_instant = 0; + voice[i].chorus_link = i; + } + upper_voices = 0; + memset(vidq_head, 0, sizeof(vidq_head)); + memset(vidq_tail, 0, sizeof(vidq_tail)); +} + +void Player::kill_note(int i) +{ + voice[i].status = VOICE_DIE; +} + +void Player::kill_all_voices(void) +{ + int i, uv = upper_voices; + + for(i = 0; i < uv; i++) + if(voice[i].status & ~(VOICE_FREE | VOICE_DIE)) + kill_note(i); + memset(vidq_head, 0, sizeof(vidq_head)); + memset(vidq_tail, 0, sizeof(vidq_tail)); +} + +void Player::reset_drum_controllers(struct DrumParts *d[], int note) +{ + int i, j; + + if (note == -1) + { + for (i = 0; i < 128; i++) + if (d[i] != NULL) + { + d[i]->drum_panning = NO_PANNING; + for (j = 0; j < 6; j++) { d[i]->drum_envelope_rate[j] = -1; } + d[i]->pan_random = 0; + d[i]->drum_level = 1.0f; + d[i]->coarse = 0; + d[i]->fine = 0; + d[i]->delay_level = -1; + d[i]->chorus_level = -1; + d[i]->reverb_level = -1; + d[i]->play_note = -1; + d[i]->drum_cutoff_freq = 0; + d[i]->drum_resonance = 0; + init_rx_drum(d[i]); + } + } + else + { + d[note]->drum_panning = NO_PANNING; + for (j = 0; j < 6; j++) { d[note]->drum_envelope_rate[j] = -1; } + d[note]->pan_random = 0; + d[note]->drum_level = 1.0f; + d[note]->coarse = 0; + d[note]->fine = 0; + d[note]->delay_level = -1; + d[note]->chorus_level = -1; + d[note]->reverb_level = -1; + d[note]->play_note = -1; + d[note]->drum_cutoff_freq = 0; + d[note]->drum_resonance = 0; + init_rx_drum(d[note]); + } +} + +void Player::reset_nrpn_controllers(int c) +{ + int i; + + /* NRPN */ + reset_drum_controllers(channel[c].drums, -1); + channel[c].vibrato_ratio = 1.0; + channel[c].vibrato_depth = 0; + channel[c].vibrato_delay = 0; + channel[c].param_cutoff_freq = 0; + channel[c].param_resonance = 0; + channel[c].cutoff_freq_coef = 1.0; + channel[c].resonance_dB = 0; + + /* System Exclusive */ + channel[c].dry_level = 127; + channel[c].eq_gs = 1; + channel[c].insertion_effect = 0; + channel[c].velocity_sense_depth = 0x40; + channel[c].velocity_sense_offset = 0x40; + channel[c].pitch_offset_fine = 0; + if (play_system_mode == GS_SYSTEM_MODE) { channel[c].assign_mode = 1; } + else { + if (ISDRUMCHANNEL(c)) { channel[c].assign_mode = 1; } + else { channel[c].assign_mode = 2; } + } + for (i = 0; i < 12; i++) + channel[c].scale_tuning[i] = 0; + channel[c].prev_scale_tuning = 0; + channel[c].temper_type = 0; + + init_channel_layer(c); + init_part_eq_xg(&(channel[c].eq_xg)); + + /* channel pressure & polyphonic key pressure control */ + init_midi_controller(&(channel[c].mod)); + init_midi_controller(&(channel[c].bend)); + init_midi_controller(&(channel[c].caf)); + init_midi_controller(&(channel[c].paf)); + init_midi_controller(&(channel[c].cc1)); + init_midi_controller(&(channel[c].cc2)); + channel[c].bend.pitch = 2; + + init_rx(c); + channel[c].note_limit_high = 127; + channel[c].note_limit_low = 0; + channel[c].vel_limit_high = 127; + channel[c].vel_limit_low = 0; + + free_drum_effect(c); + + channel[c].legato = 0; + channel[c].damper_mode = 0; + channel[c].loop_timeout = 0; + + channel[c].sysex_gs_msb_addr = channel[c].sysex_gs_msb_val = + channel[c].sysex_xg_msb_addr = channel[c].sysex_xg_msb_val = + channel[c].sysex_msb_addr = channel[c].sysex_msb_val = 0; +} + +/* Process the Reset All Controllers event */ +void Player::reset_controllers(int c) +{ + int j; + /* Some standard says, although the SCC docs say 0. */ + + if (play_system_mode == XG_SYSTEM_MODE) + channel[c].volume = 100; + else + channel[c].volume = 90; + + channel[c].expression = 127; /* SCC-1 does this. */ + channel[c].sustain = 0; + channel[c].sostenuto = 0; + channel[c].pitchbend = 0x2000; + channel[c].pitchfactor = 0; /* to be computed */ + channel[c].mod.val = 0; + channel[c].bend.val = 0; + channel[c].caf.val = 0; + channel[c].paf.val = 0; + channel[c].cc1.val = 0; + channel[c].cc2.val = 0; + channel[c].portamento_time_lsb = 0; + channel[c].portamento_time_msb = 0; + channel[c].porta_control_ratio = 0; + channel[c].portamento = 0; + channel[c].last_note_fine = -1; + for (j = 0; j < 6; j++) { channel[c].envelope_rate[j] = -1; } + update_portamento_controls(c); + set_reverb_level(c, -1); + if (timidity_chorus == 1) + channel[c].chorus_level = 0; + else + channel[c].chorus_level = -timidity_chorus; + channel[c].mono = 0; + channel[c].delay_level = 0; +} + +int Player::get_default_mapID(int ch) +{ + if (play_system_mode == XG_SYSTEM_MODE) + return ISDRUMCHANNEL(ch) ? XG_DRUM_MAP : XG_NORMAL_MAP; + return INST_NO_MAP; +} + +void Player::reset_midi(int playing) +{ + int i; + + for (i = 0; i < MAX_CHANNELS; i++) { + reset_controllers(i); + reset_nrpn_controllers(i); + channel[i].tone_map0_number = 0; + channel[i].mod.lfo1_pitch_depth = 50; + /* The rest of these are unaffected + * by the Reset All Controllers event + */ + channel[i].program = instruments->defaultProgram(i); + channel[i].panning = NO_PANNING; + channel[i].pan_random = 0; + /* tone bank or drum set */ + if (ISDRUMCHANNEL(i)) { + channel[i].bank = 0; + channel[i].altassign = instruments->drumSet(0)->alt; + } else { + if (special_tonebank >= 0) + channel[i].bank = special_tonebank; + else + channel[i].bank = default_tonebank; + } + channel[i].bank_lsb = channel[i].bank_msb = 0; + if (play_system_mode == XG_SYSTEM_MODE && i % 16 == 9) + channel[i].bank_msb = 127; /* Use MSB=127 for XG */ + update_rpn_map(i, RPN_ADDR_FFFF, 0); + channel[i].special_sample = 0; + channel[i].key_shift = 0; + channel[i].mapID = get_default_mapID(i); + channel[i].lasttime = 0; + } + if (playing) { + kill_all_voices(); + if (temper_type_mute) { + if (temper_type_mute & 1) + FILL_CHANNELMASK(channel_mute); + else + CLEAR_CHANNELMASK(channel_mute); + } + } else + reset_voices(); + master_volume_ratio = 0xffff; + adjust_amplification(); + master_tuning = 0; + if (current_file_info) { + COPY_CHANNELMASK(drumchannels, current_file_info->drumchannels); + COPY_CHANNELMASK(drumchannel_mask, + current_file_info->drumchannel_mask); + } else { + COPY_CHANNELMASK(drumchannels, default_drumchannels); + COPY_CHANNELMASK(drumchannel_mask, default_drumchannel_mask); + } +} + +void Player::recompute_freq(int v) +{ + int i; + int ch = voice[v].channel; + int note = voice[v].note; + int32_t tuning = 0; + int8_t st = channel[ch].scale_tuning[note % 12]; + int8_t tt = channel[ch].temper_type; + uint8_t tp = channel[ch].rpnmap[RPN_ADDR_0003]; + int32_t f; + int pb = channel[ch].pitchbend; + int32_t tmp; + double pf, root_freq; + int32_t a; + Voice *vp = &(voice[v]); + + if (! voice[v].sample->sample_rate) + return; + if (! timidity_modulation_wheel) + channel[ch].mod.val = 0; + if (! timidity_portamento) + voice[v].porta_control_ratio = 0; + voice[v].vibrato_control_ratio = voice[v].orig_vibrato_control_ratio; + if (voice[v].vibrato_control_ratio || channel[ch].mod.val > 0) { + /* This instrument has vibrato. Invalidate any precomputed + * sample_increments. + */ + + /* MIDI controllers LFO pitch depth */ + if (timidity_channel_pressure || timidity_modulation_wheel) { + vp->vibrato_depth = vp->sample->vibrato_depth + channel[ch].vibrato_depth; + vp->vibrato_depth += get_midi_controller_pitch_depth(&(channel[ch].mod)) + + get_midi_controller_pitch_depth(&(channel[ch].bend)) + + get_midi_controller_pitch_depth(&(channel[ch].caf)) + + get_midi_controller_pitch_depth(&(channel[ch].paf)) + + get_midi_controller_pitch_depth(&(channel[ch].cc1)) + + get_midi_controller_pitch_depth(&(channel[ch].cc2)); + if (vp->vibrato_depth > VIBRATO_DEPTH_MAX) {vp->vibrato_depth = VIBRATO_DEPTH_MAX;} + else if (vp->vibrato_depth < 1) {vp->vibrato_depth = 1;} + if (vp->sample->vibrato_depth < 0) { /* in opposite phase */ + vp->vibrato_depth = -vp->vibrato_depth; + } + } + + /* fill parameters for modulation wheel */ + if (channel[ch].mod.val > 0) { + if(vp->vibrato_control_ratio == 0) { + vp->vibrato_control_ratio = + vp->orig_vibrato_control_ratio = (int)(cnv_Hz_to_vib_ratio(5.0) * channel[ch].vibrato_ratio); + } + vp->vibrato_delay = 0; + } + + for (i = 0; i < VIBRATO_SAMPLE_INCREMENTS; i++) + vp->vibrato_sample_increment[i] = 0; + vp->cache = NULL; + } + /* At least for GM2, it's recommended not to apply master_tuning for drum channels */ + tuning = ISDRUMCHANNEL(ch) ? 0 : master_tuning; + /* fine: [0..128] => [-256..256] + * 1 coarse = 256 fine (= 1 note) + * 1 fine = 2^5 tuning + */ + tuning += (channel[ch].rpnmap[RPN_ADDR_0001] - 0x40 + + (channel[ch].rpnmap[RPN_ADDR_0002] - 0x40) * 64) << 7; + /* for NRPN Coarse Pitch of Drum (GS) & Fine Pitch of Drum (XG) */ + if (ISDRUMCHANNEL(ch) && channel[ch].drums[note] != NULL + && (channel[ch].drums[note]->fine + || channel[ch].drums[note]->coarse)) { + tuning += (channel[ch].drums[note]->fine + + channel[ch].drums[note]->coarse * 64) << 7; + } + /* MIDI controllers pitch control */ + if (timidity_channel_pressure) { + tuning += get_midi_controller_pitch(&(channel[ch].mod)) + + get_midi_controller_pitch(&(channel[ch].bend)) + + get_midi_controller_pitch(&(channel[ch].caf)) + + get_midi_controller_pitch(&(channel[ch].paf)) + + get_midi_controller_pitch(&(channel[ch].cc1)) + + get_midi_controller_pitch(&(channel[ch].cc2)); + } + if (timidity_modulation_envelope) { + if (voice[v].sample->tremolo_to_pitch) { + tuning += lookup_triangular(voice[v].tremolo_phase >> RATE_SHIFT) + * (voice[v].sample->tremolo_to_pitch << 13) / 100.0 + 0.5; + channel[ch].pitchfactor = 0; + } + if (voice[v].sample->modenv_to_pitch) { + tuning += voice[v].last_modenv_volume + * (voice[v].sample->modenv_to_pitch << 13) / 100.0 + 0.5; + channel[ch].pitchfactor = 0; + } + } + /* GS/XG - Scale Tuning */ + if (! ISDRUMCHANNEL(ch)) { + tuning += ((st << 13) + 50) / 100; + if (st != channel[ch].prev_scale_tuning) { + channel[ch].pitchfactor = 0; + channel[ch].prev_scale_tuning = st; + } + } + if (! opt_pure_intonation + && timidity_temper_control && voice[v].temper_instant) { + switch (tt) { + case 0: + f = freq_table_tuning[tp][note]; + break; + case 1: + if (current_temper_keysig < 8) + f = freq_table_pytha[current_temper_freq_table][note]; + else + f = freq_table_pytha[current_temper_freq_table + 12][note]; + break; + case 2: + if (current_temper_keysig < 8) + f = freq_table_meantone[current_temper_freq_table + + ((temper_adj) ? 36 : 0)][note]; + else + f = freq_table_meantone[current_temper_freq_table + + ((temper_adj) ? 24 : 12)][note]; + break; + case 3: + if (current_temper_keysig < 8) + f = freq_table_pureint[current_temper_freq_table + + ((temper_adj) ? 36 : 0)][note]; + else + f = freq_table_pureint[current_temper_freq_table + + ((temper_adj) ? 24 : 12)][note]; + break; + default: /* user-defined temperament */ + if ((tt -= 0x40) >= 0 && tt < 4) { + if (current_temper_keysig < 8) + f = freq_table_user[tt][current_temper_freq_table + + ((temper_adj) ? 36 : 0)][note]; + else + f = freq_table_user[tt][current_temper_freq_table + + ((temper_adj) ? 24 : 12)][note]; + } else + f = freq_table[note]; + break; + } + voice[v].orig_frequency = f; + } + if (! voice[v].porta_control_ratio) { + if (tuning == 0 && pb == 0x2000) + voice[v].frequency = voice[v].orig_frequency; + else { + pb -= 0x2000; + if (! channel[ch].pitchfactor) { + /* Damn. Somebody bent the pitch. */ + tmp = pb * channel[ch].rpnmap[RPN_ADDR_0000] + tuning; + if (tmp >= 0) + channel[ch].pitchfactor = bend_fine[tmp >> 5 & 0xff] + * bend_coarse[tmp >> 13 & 0x7f]; + else + channel[ch].pitchfactor = 1.0 / + (bend_fine[-tmp >> 5 & 0xff] + * bend_coarse[-tmp >> 13 & 0x7f]); + } + voice[v].frequency = + voice[v].orig_frequency * channel[ch].pitchfactor; + if (voice[v].frequency != voice[v].orig_frequency) + voice[v].cache = NULL; + } + } else { /* Portamento */ + pb -= 0x2000; + tmp = pb * channel[ch].rpnmap[RPN_ADDR_0000] + + (voice[v].porta_pb << 5) + tuning; + if (tmp >= 0) + pf = bend_fine[tmp >> 5 & 0xff] + * bend_coarse[tmp >> 13 & 0x7f]; + else + pf = 1.0 / (bend_fine[-tmp >> 5 & 0xff] + * bend_coarse[-tmp >> 13 & 0x7f]); + voice[v].frequency = voice[v].orig_frequency * pf; + voice[v].cache = NULL; + } + root_freq = voice[v].sample->root_freq; + a = TIM_FSCALE(((double) voice[v].sample->sample_rate + * ((double)voice[v].frequency + channel[ch].pitch_offset_fine)) + / (root_freq * playback_rate), FRACTION_BITS) + 0.5; + /* need to preserve the loop direction */ + voice[v].sample_increment = (voice[v].sample_increment >= 0) ? a : -a; +} + +int32_t Player::calc_velocity(int32_t ch,int32_t vel) +{ + int32_t velocity; + velocity = channel[ch].velocity_sense_depth * vel / 64 + (channel[ch].velocity_sense_offset - 64) * 2; + if(velocity > 127) {velocity = 127;} + return velocity; +} + +void Player::recompute_voice_tremolo(int v) +{ + Voice *vp = &(voice[v]); + int ch = vp->channel; + int32_t depth = vp->sample->tremolo_depth; + depth += get_midi_controller_amp_depth(&(channel[ch].mod)) + + get_midi_controller_amp_depth(&(channel[ch].bend)) + + get_midi_controller_amp_depth(&(channel[ch].caf)) + + get_midi_controller_amp_depth(&(channel[ch].paf)) + + get_midi_controller_amp_depth(&(channel[ch].cc1)) + + get_midi_controller_amp_depth(&(channel[ch].cc2)); + if(depth > 256) {depth = 256;} + vp->tremolo_depth = depth; +} + +void Player::recompute_amp(int v) +{ + double tempamp; + int ch = voice[v].channel; + + /* master_volume and sample->volume are percentages, used to scale + * amplitude directly, NOT perceived volume + * + * all other MIDI volumes are linear in perceived volume, 0-127 + * use a lookup table for the non-linear scalings + */ + if (play_system_mode == GM2_SYSTEM_MODE) { + tempamp = master_volume * + voice[v].sample->volume * + gm2_vol_table[calc_velocity(ch, voice[v].velocity)] * /* velocity: not in GM2 standard */ + gm2_vol_table[channel[ch].volume] * + gm2_vol_table[channel[ch].expression]; /* 21 bits */ + } + else if (play_system_mode == GS_SYSTEM_MODE) { /* use measured curve */ + tempamp = master_volume * + voice[v].sample->volume * + sc_vel_table[calc_velocity(ch, voice[v].velocity)] * + sc_vol_table[channel[ch].volume] * + sc_vol_table[channel[ch].expression]; /* 21 bits */ + } + else { /* use generic exponential curve */ + tempamp = master_volume * + voice[v].sample->volume * + perceived_vol_table[calc_velocity(ch, voice[v].velocity)] * + perceived_vol_table[channel[ch].volume] * + perceived_vol_table[channel[ch].expression]; /* 21 bits */ + } + + /* every digital effect increases amplitude, + * so that it must be reduced in advance. + */ + if ( + (timidity_reverb || timidity_chorus || opt_delay_control + || (opt_eq_control && (reverb->eq_status_gs.low_gain != 0x40 + || reverb->eq_status_gs.high_gain != 0x40)) + || opt_insertion_effect)) + tempamp *= 1.35f * 0.55f; + else + tempamp *= 1.35f; + + /* Reduce amplitude for chorus partners. + * 2x voices -> 2x power -> sqrt(2)x amplitude. + * 1 / sqrt(2) = ~0.7071, which is very close to the old + * CHORUS_VELOCITY_TUNING1 value of 0.7. + * + * The previous amp scaling for the various digital effects should + * really be redone to split them into separate scalings for each + * effect, rather than a single scaling if any one of them is used + * (which is NOT correct). As it is now, if partner chorus is the + * only effect in use, then it is reduced in volume twice and winds + * up too quiet. Compare the output of "-EFreverb=0 -EFchorus=0", + * "-EFreverb=0 -EFchorus=2", "-EFreverb=4 -EFchorus=2", and + * "-EFreverb=4 -EFchorus=0" to see how the digital effect volumes + * are not scaled properly. Idealy, all the resulting output should + * have the same volume, regardless of effects used. This will + * require empirically determining the amount to scale for each + * individual effect. + */ + if (voice[v].chorus_link != v) + tempamp *= 0.7071067811865f; + + /* NRPN - drum instrument tva level */ + if (ISDRUMCHANNEL(ch)) { + if (channel[ch].drums[voice[v].note] != NULL) { + tempamp *= channel[ch].drums[voice[v].note]->drum_level; + } + tempamp *= (double)timidity_drum_power; /* global drum power */ + } + + /* MIDI controllers amplitude control */ + if (timidity_channel_pressure) { + tempamp *= get_midi_controller_amp(&(channel[ch].mod)) + * get_midi_controller_amp(&(channel[ch].bend)) + * get_midi_controller_amp(&(channel[ch].caf)) + * get_midi_controller_amp(&(channel[ch].paf)) + * get_midi_controller_amp(&(channel[ch].cc1)) + * get_midi_controller_amp(&(channel[ch].cc2)); + recompute_voice_tremolo(v); + } + + if (voice[v].fc.type != 0) { + tempamp *= voice[v].fc.gain; /* filter gain */ + } + + /* applying panning to amplitude */ + if (true) + { + if (voice[v].panning == 64) + { + voice[v].panned = PANNED_CENTER; + voice[v].left_amp = voice[v].right_amp = TIM_FSCALENEG(tempamp * pan_table[64], 27); + } + else if (voice[v].panning < 2) + { + voice[v].panned = PANNED_LEFT; + voice[v].left_amp = TIM_FSCALENEG(tempamp, 20); + voice[v].right_amp = 0; + } + else if (voice[v].panning == 127) + { + if (voice[v].panned == PANNED_MYSTERY) { + voice[v].old_left_mix = voice[v].old_right_mix; + voice[v].old_right_mix = 0; + } + voice[v].panned = PANNED_RIGHT; + voice[v].left_amp = TIM_FSCALENEG(tempamp, 20); + voice[v].right_amp = 0; + } + else + { + if (voice[v].panned == PANNED_RIGHT) { + voice[v].old_right_mix = voice[v].old_left_mix; + voice[v].old_left_mix = 0; + } + voice[v].panned = PANNED_MYSTERY; + voice[v].left_amp = TIM_FSCALENEG(tempamp * pan_table[128 - voice[v].panning], 27); + voice[v].right_amp = TIM_FSCALENEG(tempamp * pan_table[voice[v].panning], 27); + } + } + else + { + voice[v].panned = PANNED_CENTER; + voice[v].left_amp = TIM_FSCALENEG(tempamp, 21); + } +} + +#define RESONANCE_COEFF 0.2393 + +void Player::recompute_channel_filter(int ch, int note) +{ + double coef = 1.0f, reso = 0; + + if(channel[ch].special_sample > 0) {return;} + + /* Soft Pedal */ + if(channel[ch].soft_pedal != 0) { + if(note > 49) { /* tre corde */ + coef *= 1.0 - 0.20 * ((double)channel[ch].soft_pedal) / 127.0f; + } else { /* una corda (due corde) */ + coef *= 1.0 - 0.25 * ((double)channel[ch].soft_pedal) / 127.0f; + } + } + + if(!ISDRUMCHANNEL(ch)) { + /* NRPN Filter Cutoff */ + coef *= pow(1.26, (double)(channel[ch].param_cutoff_freq) / 8.0f); + /* NRPN Resonance */ + reso = (double)channel[ch].param_resonance * RESONANCE_COEFF; + } + + channel[ch].cutoff_freq_coef = coef; + channel[ch].resonance_dB = reso; +} + +void Player::init_voice_filter(int i) +{ + memset(&(voice[i].fc), 0, sizeof(FilterCoefficients)); + if(timidity_lpf_def && voice[i].sample->cutoff_freq) { + voice[i].fc.orig_freq = voice[i].sample->cutoff_freq; + voice[i].fc.orig_reso_dB = (double)voice[i].sample->resonance / 10.0f - 3.01f; + if (voice[i].fc.orig_reso_dB < 0.0f) {voice[i].fc.orig_reso_dB = 0.0f;} + if (timidity_lpf_def == 2) { + voice[i].fc.gain = 1.0; + voice[i].fc.type = 2; + } else if(timidity_lpf_def == 1) { + voice[i].fc.gain = pow(10.0f, -voice[i].fc.orig_reso_dB / 2.0f / 20.0f); + voice[i].fc.type = 1; + } + voice[i].fc.start_flag = 0; + } else { + voice[i].fc.type = 0; + } +} + +#define CHAMBERLIN_RESONANCE_MAX 24.0 + +void Player::recompute_voice_filter(int v) +{ + int ch = voice[v].channel, note = voice[v].note; + double coef = 1.0, reso = 0, cent = 0, depth_cent = 0, freq; + FilterCoefficients *fc = &(voice[v].fc); + Sample *sp = (Sample *) &voice[v].sample; + + if(fc->type == 0) {return;} + coef = channel[ch].cutoff_freq_coef; + + if(ISDRUMCHANNEL(ch) && channel[ch].drums[note] != NULL) { + /* NRPN Drum Instrument Filter Cutoff */ + coef *= pow(1.26, (double)(channel[ch].drums[note]->drum_cutoff_freq) / 8.0f); + /* NRPN Drum Instrument Filter Resonance */ + reso += (double)channel[ch].drums[note]->drum_resonance * RESONANCE_COEFF; + } + + /* MIDI controllers filter cutoff control and LFO filter depth */ + if(timidity_channel_pressure) { + cent += get_midi_controller_filter_cutoff(&(channel[ch].mod)) + + get_midi_controller_filter_cutoff(&(channel[ch].bend)) + + get_midi_controller_filter_cutoff(&(channel[ch].caf)) + + get_midi_controller_filter_cutoff(&(channel[ch].paf)) + + get_midi_controller_filter_cutoff(&(channel[ch].cc1)) + + get_midi_controller_filter_cutoff(&(channel[ch].cc2)); + depth_cent += get_midi_controller_filter_depth(&(channel[ch].mod)) + + get_midi_controller_filter_depth(&(channel[ch].bend)) + + get_midi_controller_filter_depth(&(channel[ch].caf)) + + get_midi_controller_filter_depth(&(channel[ch].paf)) + + get_midi_controller_filter_depth(&(channel[ch].cc1)) + + get_midi_controller_filter_depth(&(channel[ch].cc2)); + } + + if(sp->vel_to_fc) { /* velocity to filter cutoff frequency */ + if(voice[v].velocity > sp->vel_to_fc_threshold) + cent += sp->vel_to_fc * (double)(127 - voice[v].velocity) / 127.0f; + else + coef += sp->vel_to_fc * (double)(127 - sp->vel_to_fc_threshold) / 127.0f; + } + if(sp->vel_to_resonance) { /* velocity to filter resonance */ + reso += (double)voice[v].velocity * sp->vel_to_resonance / 127.0f / 10.0f; + } + if(sp->key_to_fc) { /* filter cutoff key-follow */ + cent += sp->key_to_fc * (double)(voice[v].note - sp->key_to_fc_bpo); + } + + if(timidity_modulation_envelope) { + if(voice[v].sample->tremolo_to_fc + (int16_t)depth_cent) { + cent += ((double)voice[v].sample->tremolo_to_fc + depth_cent) * lookup_triangular(voice[v].tremolo_phase >> RATE_SHIFT); + } + if(voice[v].sample->modenv_to_fc) { + cent += (double)voice[v].sample->modenv_to_fc * voice[v].last_modenv_volume; + } + } + + if(cent != 0) {coef *= pow(2.0, cent / 1200.0f);} + + freq = (double)fc->orig_freq * coef; + + if (freq > playback_rate / 2) {freq = playback_rate / 2;} + else if(freq < 5) {freq = 5;} + fc->freq = (int32_t)freq; + + fc->reso_dB = fc->orig_reso_dB + channel[ch].resonance_dB + reso; + if(fc->reso_dB < 0.0f) {fc->reso_dB = 0.0f;} + else if(fc->reso_dB > 96.0f) {fc->reso_dB = 96.0f;} + + if(fc->type == 1) { /* Chamberlin filter */ + if(fc->freq > playback_rate / 6) { + if (fc->start_flag == 0) {fc->type = 0;} /* turn off. */ + else {fc->freq = playback_rate / 6;} + } + if(fc->reso_dB > CHAMBERLIN_RESONANCE_MAX) {fc->reso_dB = CHAMBERLIN_RESONANCE_MAX;} + } else if(fc->type == 2) { /* Moog VCF */ + if(fc->reso_dB > fc->orig_reso_dB / 2) { + fc->gain = pow(10.0f, (fc->reso_dB - fc->orig_reso_dB / 2) / 20.0f); + } + } + fc->start_flag = 1; /* filter is started. */ +} + +float Player::calc_drum_tva_level(int ch, int note, int level) +{ + int def_level, nbank, nprog; + const ToneBank *bank; + + if(channel[ch].special_sample > 0) {return 1.0;} + + nbank = channel[ch].bank; + nprog = note; + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + + if(ISDRUMCHANNEL(ch)) { + bank = instruments->drumSet(nbank); + if(bank == NULL) {bank = instruments->drumSet(0);} + } else { + return 1.0; + } + + def_level = bank->tone[nprog].tva_level; + + if(def_level == -1 || def_level == 0) {def_level = 127;} + else if(def_level > 127) {def_level = 127;} + + return (sc_drum_level_table[level] / sc_drum_level_table[def_level]); +} + +int32_t Player::calc_random_delay(int ch, int note) +{ + int nbank, nprog; + const ToneBank *bank; + + if(channel[ch].special_sample > 0) {return 0;} + + nbank = channel[ch].bank; + + if(ISDRUMCHANNEL(ch)) { + nprog = note; + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + bank = instruments->drumSet(nbank); + if (bank == NULL) {bank = instruments->drumSet(0);} + } else { + nprog = channel[ch].program; + if(nprog == SPECIAL_PROGRAM) {return 0;} + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + bank = instruments->toneBank(nbank); + if(bank == NULL) {bank = instruments->toneBank(0);} + } + + if (bank->tone[nprog].rnddelay == 0) {return 0;} + else {return (int32_t)((double)bank->tone[nprog].rnddelay * playback_rate / 1000.0 + * (reverb->get_pink_noise_light(&reverb->global_pink_noise_light) + 1.0f) * 0.5);} +} + +void Player::recompute_bank_parameter(int ch, int note) +{ + int nbank, nprog; + const ToneBank *bank; + struct DrumParts *drum; + + if(channel[ch].special_sample > 0) {return;} + + nbank = channel[ch].bank; + + if(ISDRUMCHANNEL(ch)) { + nprog = note; + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + bank = instruments->drumSet(nbank); + if (bank == NULL) {bank = instruments->drumSet(0);} + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + drum = channel[ch].drums[note]; + if (drum->reverb_level == -1 && bank->tone[nprog].reverb_send != -1) { + drum->reverb_level = bank->tone[nprog].reverb_send; + } + if (drum->chorus_level == -1 && bank->tone[nprog].chorus_send != -1) { + drum->chorus_level = bank->tone[nprog].chorus_send; + } + if (drum->delay_level == -1 && bank->tone[nprog].delay_send != -1) { + drum->delay_level = bank->tone[nprog].delay_send; + } + } else { + nprog = channel[ch].program; + if (nprog == SPECIAL_PROGRAM) {return;} + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + bank = instruments->toneBank(nbank); + if (bank == NULL) {bank = instruments->toneBank(0);} + channel[ch].legato = bank->tone[nprog].legato; + channel[ch].damper_mode = bank->tone[nprog].damper_mode; + channel[ch].loop_timeout = bank->tone[nprog].loop_timeout; + } +} + + +/* this reduces voices while maintaining sound quality */ +int Player::reduce_voice(void) +{ + int32_t lv, v; + int i, j, lowest=-0x7FFFFFFF; + + i = upper_voices; + lv = 0x7FFFFFFF; + + /* Look for the decaying note with the smallest volume */ + /* Protect drum decays. Truncating them early sounds bad, especially on + snares and cymbals */ + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE || + (voice[j].sample->note_to_use && ISDRUMCHANNEL(voice[j].channel))) + continue; + + if(voice[j].status & ~(VOICE_ON | VOICE_DIE | VOICE_SUSTAINED)) + { + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + if(lowest != -0x7FFFFFFF) + { + /* This can still cause a click, but if we had a free voice to + spare for ramping down this note, we wouldn't need to kill it + in the first place... Still, this needs to be fixed. Perhaps + we could use a reserve of voices to play dying notes only. */ + + cut_notes++; + free_voice(lowest); + return lowest; + } + + /* try to remove VOICE_DIE before VOICE_ON */ + lv = 0x7FFFFFFF; + lowest = -1; + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE) + continue; + if(voice[j].status & ~(VOICE_ON | VOICE_SUSTAINED)) + { + /* continue protecting drum decays */ + if (voice[j].status & ~(VOICE_DIE) && + (voice[j].sample->note_to_use && ISDRUMCHANNEL(voice[j].channel))) + continue; + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + if(lowest != -1) + { + cut_notes++; + free_voice(lowest); + return lowest; + } + + /* try to remove VOICE_SUSTAINED before VOICE_ON */ + lv = 0x7FFFFFFF; + lowest = -0x7FFFFFFF; + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE) + continue; + if(voice[j].status & VOICE_SUSTAINED) + { + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + if(lowest != -0x7FFFFFFF) + { + cut_notes++; + free_voice(lowest); + return lowest; + } + + /* try to remove chorus before VOICE_ON */ + lv = 0x7FFFFFFF; + lowest = -0x7FFFFFFF; + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE) + continue; + if(voice[j].chorus_link < j) + { + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + if(lowest != -0x7FFFFFFF) + { + cut_notes++; + + /* fix pan */ + j = voice[lowest].chorus_link; + voice[j].panning = channel[voice[lowest].channel].panning; + recompute_amp(j); + mixer->apply_envelope_to_amp(j); + + free_voice(lowest); + return lowest; + } + + lost_notes++; + + /* remove non-drum VOICE_ON */ + lv = 0x7FFFFFFF; + lowest = -0x7FFFFFFF; + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE || + (voice[j].sample->note_to_use && ISDRUMCHANNEL(voice[j].channel))) + continue; + + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + if(lowest != -0x7FFFFFFF) + { + free_voice(lowest); + return lowest; + } + + /* remove all other types of notes */ + lv = 0x7FFFFFFF; + lowest = 0; + for(j = 0; j < i; j++) + { + if(voice[j].status & VOICE_FREE) + continue; + /* find lowest volume */ + v = voice[j].left_mix; + if(voice[j].panned == PANNED_MYSTERY && voice[j].right_mix > v) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + + free_voice(lowest); + return lowest; +} + +void Player::free_voice(int v1) +{ + int v2; + +#ifdef ENABLE_PAN_DELAY + if (voice[v1].pan_delay_buf != NULL) { + free(voice[v1].pan_delay_buf); + voice[v1].pan_delay_buf = NULL; + } +#endif /* ENABLE_PAN_DELAY */ + + v2 = voice[v1].chorus_link; + if(v1 != v2) + { + /* Unlink chorus link */ + voice[v1].chorus_link = v1; + voice[v2].chorus_link = v2; + } + voice[v1].status = VOICE_FREE; + voice[v1].temper_instant = 0; +} + +int Player::find_free_voice(void) +{ + int i, nv = voices, lowest; + int32_t lv, v; + + for(i = 0; i < nv; i++) + if(voice[i].status == VOICE_FREE) + { + if(upper_voices <= i) + upper_voices = i + 1; + return i; + } + + upper_voices = voices; + + /* Look for the decaying note with the lowest volume */ + lv = 0x7FFFFFFF; + lowest = -1; + for(i = 0; i < nv; i++) + { + if(voice[i].status & ~(VOICE_ON | VOICE_DIE) && + !(voice[i].sample && voice[i].sample->note_to_use && ISDRUMCHANNEL(voice[i].channel))) + { + v = voice[i].left_mix; + if((voice[i].panned==PANNED_MYSTERY) && (voice[i].right_mix>v)) + v = voice[i].right_mix; + if(vchannel; + if (channel[ch].special_sample > 0) { + if ((s = instruments->specialPatch(channel[ch].special_sample)) == NULL) { + return 0; + } + note = e->a + channel[ch].key_shift + note_key_offset; + note = (note < 0) ? 0 : ((note > 127) ? 127 : note); + return select_play_sample(s->sample, s->samples, ¬e, vlist, e); + } + bank = channel[ch].bank; + if (ISDRUMCHANNEL(ch)) { + note = e->a & 0x7f; + instruments->instrument_map(channel[ch].mapID, &bank, ¬e); + if (! (ip = play_midi_load_instrument(1, bank, note))) + return 0; /* No instrument? Then we can't play. */ + + /* "keynum" of SF2, and patch option "note=" */ + if (ip->sample->note_to_use) + note = ip->sample->note_to_use; + } else { + if ((prog = channel[ch].program) == SPECIAL_PROGRAM) + ip = instruments->defaultInstrument(); + else { + instruments->instrument_map(channel[ch].mapID, &bank, &prog); + if (! (ip = play_midi_load_instrument(0, bank, prog))) + return 0; /* No instrument? Then we can't play. */ + } + note = ((ip->sample->note_to_use) ? ip->sample->note_to_use : e->a) + + channel[ch].key_shift + note_key_offset; + note = (note < 0) ? 0 : ((note > 127) ? 127 : note); + } + nv = select_play_sample(ip->sample, ip->samples, ¬e, vlist, e); + /* Replace the sample if the sample is cached. */ + if (ip->sample->note_to_use) + note = MIDI_EVENT_NOTE(e); + for (i = 0; i < nv; i++) { + j = vlist[i]; + if (! opt_realtime_playing && allocate_cache_size > 0 + && ! channel[ch].portamento) { + voice[j].cache = recache->resamp_cache_fetch(voice[j].sample, note); + if (voice[j].cache) /* cache hit */ + voice[j].sample = voice[j].cache->resampled; + } else + voice[j].cache = NULL; + } + return nv; +} + +int Player::select_play_sample(Sample *splist, int nsp, int *note, int *vlist, MidiEvent *e) +{ + int ch = e->channel, kn = e->a & 0x7f, vel = e->b; + int32_t f, fs, ft, fst, fc, fr, cdiff, diff, sample_link; + int8_t tt = channel[ch].temper_type; + uint8_t tp = channel[ch].rpnmap[RPN_ADDR_0003]; + Sample *sp, *spc, *spr; + int16_t sf, sn; + double ratio; + int i, j, k, nv, nvc; + + if (ISDRUMCHANNEL(ch)) + f = fs = freq_table[*note]; + else { + if (opt_pure_intonation) { + if (current_keysig < 8) + f = freq_table_pureint[current_freq_table][*note]; + else + f = freq_table_pureint[current_freq_table + 12][*note]; + } else if (timidity_temper_control) + switch (tt) { + case 0: + f = freq_table_tuning[tp][*note]; + break; + case 1: + if (current_temper_keysig < 8) + f = freq_table_pytha[ + current_temper_freq_table][*note]; + else + f = freq_table_pytha[ + current_temper_freq_table + 12][*note]; + break; + case 2: + if (current_temper_keysig < 8) + f = freq_table_meantone[current_temper_freq_table + + ((temper_adj) ? 36 : 0)][*note]; + else + f = freq_table_meantone[current_temper_freq_table + + ((temper_adj) ? 24 : 12)][*note]; + break; + case 3: + if (current_temper_keysig < 8) + f = freq_table_pureint[current_temper_freq_table + + ((temper_adj) ? 36 : 0)][*note]; + else + f = freq_table_pureint[current_temper_freq_table + + ((temper_adj) ? 24 : 12)][*note]; + break; + default: /* user-defined temperament */ + if ((tt -= 0x40) >= 0 && tt < 4) { + if (current_temper_keysig < 8) + f = freq_table_user[tt][current_temper_freq_table + + ((temper_adj) ? 36 : 0)][*note]; + else + f = freq_table_user[tt][current_temper_freq_table + + ((temper_adj) ? 24 : 12)][*note]; + } else + f = freq_table[*note]; + break; + } + else + f = freq_table[*note]; + if (! opt_pure_intonation && timidity_temper_control + && tt == 0 && f != freq_table[*note]) { + *note = log(f / 440000.0) / log(2) * 12 + 69.5; + *note = (*note < 0) ? 0 : ((*note > 127) ? 127 : *note); + fs = freq_table[*note]; + } else + fs = freq_table[*note]; + } + nv = 0; + for (i = 0, sp = splist; i < nsp; i++, sp++) { + /* GUS/SF2 - Scale Tuning */ + if ((sf = sp->scale_factor) != 1024) { + sn = sp->scale_freq; + ratio = pow(2.0, (*note - sn) * (sf - 1024) / 12288.0); + ft = f * ratio + 0.5, fst = fs * ratio + 0.5; + } else + ft = f, fst = fs; + if (ISDRUMCHANNEL(ch) && channel[ch].drums[kn] != NULL) + if ((ratio = get_play_note_ratio(ch, kn)) != 1.0) + ft = ft * ratio + 0.5, fst = fst * ratio + 0.5; + if (sp->low_freq <= fst && sp->high_freq >= fst + && sp->low_vel <= vel && sp->high_vel >= vel + && ! (sp->inst_type == INST_SF2 + && sp->sample_type == SF_SAMPLETYPE_RIGHT)) { + j = vlist[nv] = find_voice(e); + voice[j].orig_frequency = ft; + voice[j].sample = sp; + voice[j].status = VOICE_ON; + nv++; + } + } + if (nv == 0) { /* we must select at least one sample. */ + fr = fc = 0; + spc = spr = NULL; + cdiff = 0x7fffffff; + for (i = 0, sp = splist; i < nsp; i++, sp++) { + /* GUS/SF2 - Scale Tuning */ + if ((sf = sp->scale_factor) != 1024) { + sn = sp->scale_freq; + ratio = pow(2.0, (*note - sn) * (sf - 1024) / 12288.0); + ft = f * ratio + 0.5, fst = fs * ratio + 0.5; + } else + ft = f, fst = fs; + if (ISDRUMCHANNEL(ch) && channel[ch].drums[kn] != NULL) + if ((ratio = get_play_note_ratio(ch, kn)) != 1.0) + ft = ft * ratio + 0.5, fst = fst * ratio + 0.5; + diff = abs(sp->root_freq - fst); + if (diff < cdiff) { + if (sp->inst_type == INST_SF2 + && sp->sample_type == SF_SAMPLETYPE_RIGHT) { + fr = ft; /* reserve */ + spr = sp; /* reserve */ + } else { + fc = ft; + spc = sp; + cdiff = diff; + } + } + } + /* If spc is not NULL, a makeshift sample is found. */ + /* Otherwise, it's a lonely right sample, but better than nothing. */ + j = vlist[nv] = find_voice(e); + voice[j].orig_frequency = (spc) ? fc : fr; + voice[j].sample = (spc) ? spc : spr; + voice[j].status = VOICE_ON; + nv++; + } + nvc = nv; + for (i = 0; i < nvc; i++) { + spc = voice[vlist[i]].sample; + /* If it's left sample, there must be right sample. */ + if (spc->inst_type == INST_SF2 + && spc->sample_type == SF_SAMPLETYPE_LEFT) { + sample_link = spc->sf_sample_link; + for (j = 0, sp = splist; j < nsp; j++, sp++) + if (sp->inst_type == INST_SF2 + && sp->sample_type == SF_SAMPLETYPE_RIGHT + && sp->sf_sample_index == sample_link) { + /* right sample is found. */ + /* GUS/SF2 - Scale Tuning */ + if ((sf = sp->scale_factor) != 1024) { + sn = sp->scale_freq; + ratio = pow(2.0, (*note - sn) * (sf - 1024) / 12288.0); + ft = f * ratio + 0.5; + } else + ft = f; + if (ISDRUMCHANNEL(ch) && channel[ch].drums[kn] != NULL) + if ((ratio = get_play_note_ratio(ch, kn)) != 1.0) + ft = ft * ratio + 0.5; + k = vlist[nv] = find_voice(e); + voice[k].orig_frequency = ft; + voice[k].sample = sp; + voice[k].status = VOICE_ON; + nv++; + break; + } + } + } + return nv; +} + +double Player::get_play_note_ratio(int ch, int note) +{ + int play_note = channel[ch].drums[note]->play_note; + int bank = channel[ch].bank; + const ToneBank *dbank; + int def_play_note; + + if (play_note == -1) + return 1.0; + instruments->instrument_map(channel[ch].mapID, &bank, ¬e); + dbank = (instruments->drumSet(bank)) ? instruments->drumSet(bank) : instruments->drumSet(0); + if ((def_play_note = dbank->tone[note].play_note) == -1) + return 1.0; + if (play_note >= def_play_note) + return bend_coarse[(play_note - def_play_note) & 0x7f]; + else + return 1 / bend_coarse[(def_play_note - play_note) & 0x7f]; +} + +/* Only one instance of a note can be playing on a single channel. */ +int Player::find_voice(MidiEvent *e) +{ + int ch = e->channel; + int note = MIDI_EVENT_NOTE(e); + int status_check, mono_check; + AlternateAssign *altassign; + int i, lowest = -1; + + status_check = (timidity_overlap_voice_allow) + ? (VOICE_OFF | VOICE_SUSTAINED) : 0xff; + mono_check = channel[ch].mono; + altassign = instruments->find_altassign(channel[ch].altassign, note); + for (i = 0; i < upper_voices; i++) + if (voice[i].status == VOICE_FREE) { + lowest = i; /* lower volume */ + break; + } + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE && voice[i].channel == ch) { + if (voice[i].note == note && (voice[i].status & status_check)) + kill_note(i); + else if (mono_check) + kill_note(i); + else if (altassign && instruments->find_altassign(altassign, voice[i].note)) + kill_note(i); + else if (voice[i].note == note && (channel[ch].assign_mode == 0 + || (channel[ch].assign_mode == 1 && + voice[i].proximate_flag == 0))) + kill_note(i); + } + for (i = 0; i < upper_voices; i++) + if (voice[i].channel == ch && voice[i].note == note) + voice[i].proximate_flag = 0; + if (lowest != -1) /* Found a free voice. */ + return lowest; + if (upper_voices < voices) + return upper_voices++; + return reduce_voice(); +} + +int Player::get_panning(int ch, int note,int v) +{ + int pan; + + if(channel[ch].panning != NO_PANNING) {pan = (int)channel[ch].panning - 64;} + else {pan = 0;} + if(ISDRUMCHANNEL(ch) && + channel[ch].drums[note] != NULL && + channel[ch].drums[note]->drum_panning != NO_PANNING) { + pan += channel[ch].drums[note]->drum_panning; + } else { + pan += voice[v].sample->panning; + } + + if (pan > 127) pan = 127; + else if (pan < 0) pan = 0; + + return pan; +} + +/*! initialize vibrato parameters for a voice. */ +void Player::init_voice_vibrato(int v) +{ + Voice *vp = &(voice[v]); + int ch = vp->channel, j, nrpn_vib_flag; + double ratio; + + /* if NRPN vibrato is set, it's believed that there must be vibrato. */ + nrpn_vib_flag = op_nrpn_vibrato + && (channel[ch].vibrato_ratio != 1.0 || channel[ch].vibrato_depth != 0); + + /* vibrato sweep */ + vp->vibrato_sweep = vp->sample->vibrato_sweep_increment; + vp->vibrato_sweep_position = 0; + + /* vibrato rate */ + if (nrpn_vib_flag) { + if(vp->sample->vibrato_control_ratio == 0) { + ratio = cnv_Hz_to_vib_ratio(5.0) * channel[ch].vibrato_ratio; + } else { + ratio = (double)vp->sample->vibrato_control_ratio * channel[ch].vibrato_ratio; + } + if (ratio < 0) {ratio = 0;} + vp->vibrato_control_ratio = (int)ratio; + } else { + vp->vibrato_control_ratio = vp->sample->vibrato_control_ratio; + } + + /* vibrato depth */ + if (nrpn_vib_flag) { + vp->vibrato_depth = vp->sample->vibrato_depth + channel[ch].vibrato_depth; + if (vp->vibrato_depth > VIBRATO_DEPTH_MAX) {vp->vibrato_depth = VIBRATO_DEPTH_MAX;} + else if (vp->vibrato_depth < 1) {vp->vibrato_depth = 1;} + if (vp->sample->vibrato_depth < 0) { /* in opposite phase */ + vp->vibrato_depth = -vp->vibrato_depth; + } + } else { + vp->vibrato_depth = vp->sample->vibrato_depth; + } + + /* vibrato delay */ + vp->vibrato_delay = vp->sample->vibrato_delay + channel[ch].vibrato_delay; + + /* internal parameters */ + vp->orig_vibrato_control_ratio = vp->vibrato_control_ratio; + vp->vibrato_control_counter = vp->vibrato_phase = 0; + for (j = 0; j < VIBRATO_SAMPLE_INCREMENTS; j++) { + vp->vibrato_sample_increment[j] = 0; + } +} + +/*! initialize panning-delay for a voice. */ +void Player::init_voice_pan_delay(int v) +{ +#ifdef ENABLE_PAN_DELAY + Voice *vp = &(voice[v]); + int ch = vp->channel; + double pan_delay_diff; + + if (vp->pan_delay_buf != NULL) { + free(vp->pan_delay_buf); + vp->pan_delay_buf = NULL; + } + vp->pan_delay_rpt = 0; + if (timidity_pan_delay && channel[ch].insertion_effect == 0 && !timidity_surround_chorus) { + if (vp->panning == 64) {vp->delay += pan_delay_table[64] * playback_rate / 1000;} + else { + if(pan_delay_table[vp->panning] > pan_delay_table[127 - vp->panning]) { + pan_delay_diff = pan_delay_table[vp->panning] - pan_delay_table[127 - vp->panning]; + vp->delay += (pan_delay_table[vp->panning] - pan_delay_diff) * playback_rate / 1000; + } else { + pan_delay_diff = pan_delay_table[127 - vp->panning] - pan_delay_table[vp->panning]; + vp->delay += (pan_delay_table[127 - vp->panning] - pan_delay_diff) * playback_rate / 1000; + } + vp->pan_delay_rpt = pan_delay_diff * playback_rate / 1000; + } + if(vp->pan_delay_rpt < 1) {vp->pan_delay_rpt = 0;} + vp->pan_delay_wpt = 0; + vp->pan_delay_spt = vp->pan_delay_wpt - vp->pan_delay_rpt; + if (vp->pan_delay_spt < 0) {vp->pan_delay_spt += PAN_DELAY_BUF_MAX;} + vp->pan_delay_buf = (int32_t *)safe_malloc(sizeof(int32_t) * PAN_DELAY_BUF_MAX); + memset(vp->pan_delay_buf, 0, sizeof(int32_t) * PAN_DELAY_BUF_MAX); + } +#endif /* ENABLE_PAN_DELAY */ +} + +/*! initialize portamento or legato for a voice. */ +void Player::init_voice_portamento(int v) +{ + Voice *vp = &(voice[v]); + int ch = vp->channel; + + vp->porta_control_counter = 0; + if (channel[ch].legato && channel[ch].legato_flag) { + update_legato_controls(ch); + } + else if (channel[ch].portamento && !channel[ch].porta_control_ratio) { + update_portamento_controls(ch); + } + vp->porta_control_ratio = 0; + if (channel[ch].porta_control_ratio) + { + if (channel[ch].last_note_fine == -1) { + /* first on */ + channel[ch].last_note_fine = vp->note * 256; + channel[ch].porta_control_ratio = 0; + } + else { + vp->porta_control_ratio = channel[ch].porta_control_ratio; + vp->porta_dpb = channel[ch].porta_dpb; + vp->porta_pb = channel[ch].last_note_fine - + vp->note * 256; + if (vp->porta_pb == 0) { vp->porta_control_ratio = 0; } + } + } +} + +/*! initialize tremolo for a voice. */ +void Player::init_voice_tremolo(int v) +{ + Voice *vp = &(voice[v]); + + vp->tremolo_delay = vp->sample->tremolo_delay; + vp->tremolo_phase = 0; + vp->tremolo_phase_increment = vp->sample->tremolo_phase_increment; + vp->tremolo_sweep = vp->sample->tremolo_sweep_increment; + vp->tremolo_sweep_position = 0; + vp->tremolo_depth = vp->sample->tremolo_depth; +} + +void Player::start_note(MidiEvent *e, int i, int vid, int cnt) +{ + int j, ch, note; + + ch = e->channel; + + note = MIDI_EVENT_NOTE(e); + voice[i].status = VOICE_ON; + voice[i].channel = ch; + voice[i].note = note; + voice[i].velocity = e->b; + voice[i].chorus_link = i; /* No link */ + voice[i].proximate_flag = 1; + + j = channel[ch].special_sample; + if (j == 0 || instruments->specialPatch(j) == NULL) + voice[i].sample_offset = 0; + else + { + voice[i].sample_offset = instruments->specialPatch(j)->sample_offset << FRACTION_BITS; + if (voice[i].sample->modes & MODES_LOOPING) + { + if (voice[i].sample_offset > voice[i].sample->loop_end) + voice[i].sample_offset = voice[i].sample->loop_start; + } + else if (voice[i].sample_offset > voice[i].sample->data_length) + { + free_voice(i); + return; + } + } + voice[i].sample_increment = 0; /* make sure it isn't negative */ + voice[i].vid = vid; + voice[i].delay = voice[i].sample->envelope_delay; + voice[i].modenv_delay = voice[i].sample->modenv_delay; + voice[i].delay_counter = 0; + + init_voice_tremolo(i); /* tremolo */ + init_voice_filter(i); /* resonant lowpass filter */ + init_voice_vibrato(i); /* vibrato */ + voice[i].panning = get_panning(ch, note, i); /* pan */ + init_voice_pan_delay(i); /* panning-delay */ + init_voice_portamento(i); /* portamento or legato */ + + if (cnt == 0) + channel[ch].last_note_fine = voice[i].note * 256; + + /* initialize modulation envelope */ + if (voice[i].sample->modes & MODES_ENVELOPE) + { + voice[i].modenv_stage = EG_GUS_ATTACK; + voice[i].modenv_volume = 0; + mixer->recompute_modulation_envelope(i); + mixer->apply_modulation_envelope(i); + } + else + { + voice[i].modenv_increment = 0; + mixer->apply_modulation_envelope(i); + } + recompute_freq(i); + recompute_voice_filter(i); + + recompute_amp(i); + /* initialize volume envelope */ + if (voice[i].sample->modes & MODES_ENVELOPE) + { + /* Ramp up from 0 */ + voice[i].envelope_stage = EG_GUS_ATTACK; + voice[i].envelope_volume = 0; + voice[i].control_counter = 0; + mixer->recompute_envelope(i); + mixer->apply_envelope_to_amp(i); + } + else + { + voice[i].envelope_increment = 0; + mixer->apply_envelope_to_amp(i); + } + + voice[i].timeout = -1; +} + +void Player::finish_note(int i) +{ + if (voice[i].sample->modes & MODES_ENVELOPE) + { + /* We need to get the envelope out of Sustain stage. */ + /* Note that voice[i].envelope_stage < EG_GUS_RELEASE1 */ + voice[i].status = VOICE_OFF; + voice[i].envelope_stage = EG_GUS_RELEASE1; + mixer->recompute_envelope(i); + voice[i].modenv_stage = EG_GUS_RELEASE1; + mixer->recompute_modulation_envelope(i); + mixer->apply_modulation_envelope(i); + mixer->apply_envelope_to_amp(i); + } + else + { + /* Set status to OFF so resample_voice() will let this voice out + of its loop, if any. In any case, this voice dies when it + hits the end of its data (ofs>=data_length). */ + if(voice[i].status != VOICE_OFF) + { + voice[i].status = VOICE_OFF; + } + } +} + +void Player::set_envelope_time(int ch, int val, int stage) +{ + val = val & 0x7F; +#if 0 + switch(stage) { + case EG_ATTACK: /* Attack */ + //printMessage(CMSG_INFO,VERB_NOISY,"Attack Time (CH:%d VALUE:%d)", ch, val); + break; + case EG_DECAY: /* Decay */ + //printMessage(CMSG_INFO,VERB_NOISY,"Decay Time (CH:%d VALUE:%d)", ch, val); + break; + case EG_RELEASE: /* Release */ + //printMessage(CMSG_INFO,VERB_NOISY,"Release Time (CH:%d VALUE:%d)", ch, val); + break; + default: + //printMessage(CMSG_INFO,VERB_NOISY,"? Time (CH:%d VALUE:%d)", ch, val); + } +#endif + channel[ch].envelope_rate[stage] = val; +} + + + +/* Yet another chorus implementation + * by Eric A. Welsh . + */ +void Player::new_chorus_voice_alternate(int v1, int level) +{ + int v2, ch, panlevel; + uint8_t pan; + double delay; + double freq, frac; + int note_adjusted; + + if((v2 = find_free_voice()) == -1) + return; + ch = voice[v1].channel; + voice[v2] = voice[v1]; + + /* NRPN Chorus Send Level of Drum */ + if(ISDRUMCHANNEL(ch) && channel[ch].drums[voice[v1].note] != NULL) { + level *= (double)channel[ch].drums[voice[v1].note]->chorus_level / 127.0; + } + + /* for our purposes, hard left will be equal to 1 instead of 0 */ + pan = voice[v1].panning; + if (!pan) pan = 1; + + /* Choose lower voice index for base voice (v1) */ + if(v1 > v2) + { + v1 ^= v2; + v2 ^= v1; + v1 ^= v2; + } + + /* Make doubled link v1 and v2 */ + voice[v1].chorus_link = v2; + voice[v2].chorus_link = v1; + + /* detune notes for chorus effect */ + level >>= 2; /* scale to a "better" value */ + if (level) + { + if(channel[ch].pitchbend + level < 0x2000) + voice[v2].orig_frequency *= bend_fine[level]; + else + voice[v2].orig_frequency /= bend_fine[level]; + voice[v2].cache = NULL; + } + + delay = 0.003; + + /* Try to keep the delayed voice from cancelling out the other voice */ + /* Pitch detection is used to find the real pitches for drums and MODs */ + note_adjusted = voice[v1].note + voice[v1].sample->transpose_detected; + if (note_adjusted > 127) note_adjusted = 127; + else if (note_adjusted < 0) note_adjusted = 0; + freq = pitch_freq_table[note_adjusted]; + delay *= freq; + frac = delay - floor(delay); + + /* force the delay away from 0.5 period */ + if (frac < 0.5 && frac > 0.40) + { + delay = (floor(delay) + 0.40) / freq; + delay += (0.5 - frac) * (1.0 - labs(64 - pan) / 63.0) / freq; + } + else if (frac >= 0.5 && frac < 0.60) + { + delay = (floor(delay) + 0.60) / freq; + delay += (0.5 - frac) * (1.0 - labs(64 - pan) / 63.0) / freq; + } + else + delay = 0.003; + + /* set panning & delay for pseudo-surround effect */ + { + panlevel = 63; + if (pan - panlevel < 1) panlevel = pan - 1; + if (pan + panlevel > 127) panlevel = 127 - pan; + voice[v1].panning -= panlevel; + voice[v2].panning += panlevel; + + /* choose which voice is delayed based on panning */ + if (voice[v1].panned == PANNED_CENTER) { + /* randomly choose which voice is delayed */ + if (int_rand(2)) + voice[v1].delay += (int)(playback_rate * delay); + else + voice[v2].delay += (int)(playback_rate * delay); + } + else if (pan - 64 < 0) { + voice[v2].delay += (int)(playback_rate * delay); + } + else { + voice[v1].delay += (int)(playback_rate * delay); + } + } + + /* check for similar drums playing simultaneously with center pans */ + if (ISDRUMCHANNEL(ch) && voice[v1].panned == PANNED_CENTER) + { + int i, j; + + /* force Rimshot (37), Snare1 (38), Snare2 (40), and XG #34 to have + * the same delay, otherwise there will be bad voice cancellation. + */ + if (voice[v1].note == 37 || + voice[v1].note == 38 || + voice[v1].note == 40 || + (voice[v1].note == 34 && play_system_mode == XG_SYSTEM_MODE)) + { + for (i = 0; i < upper_voices; i++) + { + if (voice[i].status & (VOICE_DIE | VOICE_FREE)) + continue; + + if (!ISDRUMCHANNEL(voice[i].channel)) + continue; + + if (i == v1 || i == v2) + continue; + + if (voice[i].note == 37 || + voice[i].note == 38 || + voice[i].note == 40 || + (voice[i].note == 34 && + play_system_mode == XG_SYSTEM_MODE)) + { + j = voice[i].chorus_link; + + if (voice[i].panned == PANNED_LEFT && + voice[j].panned == PANNED_RIGHT) + { + voice[v1].delay = voice[i].delay; + voice[v2].delay = voice[j].delay; + + break; + } + } + } + } + + /* force Kick1 (35), Kick2 (36), and XG Kick #33 to have the same + * delay, otherwise there will be bad voice cancellation. + */ + if (voice[v1].note == 35 || + voice[v1].note == 36 || + (voice[v1].note == 33 && play_system_mode == XG_SYSTEM_MODE)) + { + for (i = 0; i < upper_voices; i++) + { + if (voice[i].status & (VOICE_DIE | VOICE_FREE)) + continue; + + if (!ISDRUMCHANNEL(voice[i].channel)) + continue; + + if (i == v1 || i == v2) + continue; + + if (voice[i].note == 35 || + voice[i].note == 36 || + (voice[i].note == 33 && + play_system_mode == XG_SYSTEM_MODE)) + { + j = voice[i].chorus_link; + + if (voice[i].panned == PANNED_LEFT && + voice[j].panned == PANNED_RIGHT) + { + voice[v1].delay = voice[i].delay; + voice[v2].delay = voice[j].delay; + + break; + } + } + } + } + } + + init_voice_pan_delay(v1); + init_voice_pan_delay(v2); + + recompute_amp(v1); + mixer->apply_envelope_to_amp(v1); + recompute_amp(v2); + mixer->apply_envelope_to_amp(v2); + if (level) recompute_freq(v2); +} + +void Player::note_on(MidiEvent *e) +{ + int i, nv, v, ch, note; + int vlist[32]; + int vid; + int32_t random_delay = 0; + + ch = e->channel; + note = MIDI_EVENT_NOTE(e); + + if(ISDRUMCHANNEL(ch) && + channel[ch].drums[note] != NULL && + !get_rx_drum(channel[ch].drums[note], RX_NOTE_ON)) { /* Rx. Note On */ + return; + } + if(channel[ch].note_limit_low > note || + channel[ch].note_limit_high < note || + channel[ch].vel_limit_low > e->b || + channel[ch].vel_limit_high < e->b) { + return; + } + if((nv = find_samples(e, vlist)) == 0) + return; + + vid = new_vidq(e->channel, note); + + recompute_bank_parameter(ch, note); + recompute_channel_filter(ch, note); + random_delay = calc_random_delay(ch, note); + + for(i = 0; i < nv; i++) + { + v = vlist[i]; + if(ISDRUMCHANNEL(ch) && + channel[ch].drums[note] != NULL && + channel[ch].drums[note]->pan_random) + channel[ch].drums[note]->drum_panning = int_rand(128); + else if(channel[ch].pan_random) + { + channel[ch].panning = int_rand(128); + } + start_note(e, v, vid, nv - i - 1); + voice[v].delay += random_delay; + voice[v].modenv_delay += random_delay; + voice[v].old_left_mix = voice[v].old_right_mix = + voice[v].left_mix_inc = voice[v].left_mix_offset = + voice[v].right_mix_inc = voice[v].right_mix_offset = 0; + if(timidity_surround_chorus) + new_chorus_voice_alternate(v, 0); + } + + channel[ch].legato_flag = 1; +} + +/*! sostenuto is now implemented as an instant sustain */ +void Player::update_sostenuto_controls(int ch) +{ + int uv = upper_voices, i; + + if(ISDRUMCHANNEL(ch) || channel[ch].sostenuto == 0) {return;} + + for(i = 0; i < uv; i++) + { + if ((voice[i].status & (VOICE_ON | VOICE_OFF)) + && voice[i].channel == ch) + { + voice[i].status = VOICE_SUSTAINED; + voice[i].envelope_stage = EG_GUS_RELEASE1; + mixer->recompute_envelope(i); + } + } +} + +/*! redamper effect for piano instruments */ +void Player::update_redamper_controls(int ch) +{ + int uv = upper_voices, i; + + if(ISDRUMCHANNEL(ch) || channel[ch].damper_mode == 0) {return;} + + for(i = 0; i < uv; i++) + { + if ((voice[i].status & (VOICE_ON | VOICE_OFF)) + && voice[i].channel == ch) + { + voice[i].status = VOICE_SUSTAINED; + voice[i].envelope_stage = EG_GUS_RELEASE1; + mixer->recompute_envelope(i); + } + } +} + +void Player::note_off(MidiEvent *e) +{ + int uv = upper_voices, i; + int ch, note, vid, sustain; + + ch = e->channel; + note = MIDI_EVENT_NOTE(e); + + if(ISDRUMCHANNEL(ch)) + { + int nbank, nprog; + + nbank = channel[ch].bank; + nprog = note; + instruments->instrument_map(channel[ch].mapID, &nbank, &nprog); + + if (channel[ch].drums[nprog] != NULL && + get_rx_drum(channel[ch].drums[nprog], RX_NOTE_OFF)) + { + auto bank = instruments->drumSet(nbank); + if(bank == NULL) bank = instruments->drumSet(0); + + /* uh oh, this drum doesn't have an instrument loaded yet */ + if (bank->tone[nprog].instrument == NULL) + return; + + /* this drum is not loaded for some reason (error occured?) */ + if (IS_MAGIC_INSTRUMENT(bank->tone[nprog].instrument)) + return; + + /* only disallow Note Off if the drum sample is not looped */ + if (!(bank->tone[nprog].instrument->sample->modes & MODES_LOOPING)) + return; /* Note Off is not allowed. */ + } + } + + if ((vid = last_vidq(ch, note)) == -1) + return; + sustain = channel[ch].sustain; + for (i = 0; i < uv; i++) + { + if(voice[i].status == VOICE_ON && + voice[i].channel == ch && + voice[i].note == note && + voice[i].vid == vid) + { + if(sustain) + { + voice[i].status = VOICE_SUSTAINED; + } + else + finish_note(i); + } + } + + channel[ch].legato_flag = 0; +} + +/* Process the All Notes Off event */ +void Player::all_notes_off(int c) +{ + int i, uv = upper_voices; + printMessage(CMSG_INFO, VERB_DEBUG, "All notes off on channel %d", c); + for(i = 0; i < uv; i++) + if (voice[i].status==VOICE_ON && + voice[i].channel==c) + { + if (channel[c].sustain) + { + voice[i].status=VOICE_SUSTAINED; + } + else + finish_note(i); + } + for(i = 0; i < 128; i++) + vidq_head[c * 128 + i] = vidq_tail[c * 128 + i] = 0; +} + +/* Process the All Sounds Off event */ +void Player::all_sounds_off(int c) +{ + int i, uv = upper_voices; + for(i = 0; i < uv; i++) + if (voice[i].channel==c && + (voice[i].status & ~(VOICE_FREE | VOICE_DIE))) + { + kill_note(i); + } + for(i = 0; i < 128; i++) + vidq_head[c * 128 + i] = vidq_tail[c * 128 + i] = 0; +} + +/*! adjust polyphonic key pressure (PAf, PAT) */ +void Player::adjust_pressure(MidiEvent *e) +{ + int i, uv = upper_voices; + int note, ch; + + if(timidity_channel_pressure) + { + ch = e->channel; + note = MIDI_EVENT_NOTE(e); + channel[ch].paf.val = e->b; + if(channel[ch].paf.pitch != 0) {channel[ch].pitchfactor = 0;} + + for(i = 0; i < uv; i++) + if(voice[i].status == VOICE_ON && + voice[i].channel == ch && + voice[i].note == note) + { + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + recompute_freq(i); + recompute_voice_filter(i); + } + } +} + +/*! adjust channel pressure (channel aftertouch, CAf, CAT) */ +void Player::adjust_channel_pressure(MidiEvent *e) +{ + if(timidity_channel_pressure) + { + int i, uv = upper_voices; + int ch; + + ch = e->channel; + channel[ch].caf.val = e->a; + if(channel[ch].caf.pitch != 0) {channel[ch].pitchfactor = 0;} + + for(i = 0; i < uv; i++) + { + if(voice[i].status == VOICE_ON && voice[i].channel == ch) + { + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + recompute_freq(i); + recompute_voice_filter(i); + } + } + } +} + +void Player::adjust_panning(int c) +{ + int i, uv = upper_voices, pan = channel[c].panning; + for(i = 0; i < uv; i++) + { + if ((voice[i].channel==c) && + (voice[i].status & (VOICE_ON | VOICE_SUSTAINED))) + { + /* adjust pan to include drum/sample pan offsets */ + pan = get_panning(c, voice[i].note, i); + + /* Hack to handle -EFchorus=2 in a "reasonable" way */ + if(timidity_surround_chorus && voice[i].chorus_link != i) + { + int v1, v2; + + if(i >= voice[i].chorus_link) + /* `i' is not base chorus voice. + * This sub voice is already updated. + */ + continue; + + v1 = i; /* base voice */ + v2 = voice[i].chorus_link; /* sub voice (detuned) */ + + if(timidity_surround_chorus) /* Surround chorus mode by Eric. */ + { + int panlevel; + + if (!pan) pan = 1; /* make hard left be 1 instead of 0 */ + panlevel = 63; + if (pan - panlevel < 1) panlevel = pan - 1; + if (pan + panlevel > 127) panlevel = 127 - pan; + voice[v1].panning = pan - panlevel; + voice[v2].panning = pan + panlevel; + } + else + { + voice[v1].panning = pan; + if(pan > 60 && pan < 68) /* PANNED_CENTER */ + voice[v2].panning = + 64 + int_rand(40) - 20; /* 64 +- rand(20) */ + else if(pan < CHORUS_OPPOSITE_THRESHOLD) + voice[v2].panning = 127; + else if(pan > 127 - CHORUS_OPPOSITE_THRESHOLD) + voice[v2].panning = 0; + else + voice[v2].panning = (pan < 64 ? 0 : 127); + } + recompute_amp(v2); + mixer->apply_envelope_to_amp(v2); + /* v1 == i, so v1 will be updated next */ + } + else + voice[i].panning = pan; + + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + } + } +} + +void Player::play_midi_setup_drums(int ch, int note) +{ + channel[ch].drums[note] = (struct DrumParts *) + new_segment(&playmidi_pool, sizeof(struct DrumParts)); + reset_drum_controllers(channel[ch].drums, note); +} + +void Player::adjust_drum_panning(int ch, int note) +{ + int i, uv = upper_voices; + + for(i = 0; i < uv; i++) { + if(voice[i].channel == ch && + voice[i].note == note && + (voice[i].status & (VOICE_ON | VOICE_SUSTAINED))) + { + voice[i].panning = get_panning(ch, note, i); + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + } + } +} + +void Player::drop_sustain(int c) +{ + int i, uv = upper_voices; + for(i = 0; i < uv; i++) + if (voice[i].status == VOICE_SUSTAINED && voice[i].channel == c) + finish_note(i); +} + +void Player::adjust_all_pitch(void) +{ + int ch, i, uv = upper_voices; + + for (ch = 0; ch < MAX_CHANNELS; ch++) + channel[ch].pitchfactor = 0; + for (i = 0; i < uv; i++) + if (voice[i].status != VOICE_FREE) + recompute_freq(i); +} + +void Player::adjust_pitch(int c) +{ + int i, uv = upper_voices; + for(i = 0; i < uv; i++) + if (voice[i].status != VOICE_FREE && voice[i].channel == c) + recompute_freq(i); +} + +void Player::adjust_volume(int c) +{ + int i, uv = upper_voices; + for(i = 0; i < uv; i++) + if (voice[i].channel == c && + (voice[i].status & (VOICE_ON | VOICE_SUSTAINED))) + { + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + } +} + +void Player::set_reverb_level(int ch, int level) +{ + if (level == -1) { + channel[ch].reverb_level = channel[ch].reverb_id = + (timidity_reverb < 0) + ? -timidity_reverb & 0x7f : DEFAULT_REVERB_SEND_LEVEL; + make_rvid_flag = 1; + return; + } + channel[ch].reverb_level = level; + make_rvid_flag = 0; /* to update reverb_id */ +} + +int Player::get_reverb_level(int ch) +{ + if (channel[ch].reverb_level == -1) + return (timidity_reverb < 0) + ? -timidity_reverb & 0x7f : DEFAULT_REVERB_SEND_LEVEL; + return channel[ch].reverb_level; +} + +int Player::get_chorus_level(int ch) +{ +#ifdef DISALLOW_DRUM_BENDS + if(ISDRUMCHANNEL(ch)) + return 0; /* Not supported drum channel chorus */ +#endif + if(timidity_chorus == 1) + return channel[ch].chorus_level; + return -timidity_chorus; +} + + +void Player::free_drum_effect(int ch) +{ + int i; + if (channel[ch].drum_effect != NULL) { + for (i = 0; i < channel[ch].drum_effect_num; i++) { + if (channel[ch].drum_effect[i].buf != NULL) { + free(channel[ch].drum_effect[i].buf); + channel[ch].drum_effect[i].buf = NULL; + } + } + free(channel[ch].drum_effect); + channel[ch].drum_effect = NULL; + } + channel[ch].drum_effect_num = 0; + channel[ch].drum_effect_flag = 0; +} + +void Player::make_drum_effect(int ch) +{ + int i, note, num = 0; + int8_t note_table[128]; + struct DrumParts *drum; + struct DrumPartEffect *de; + + if (channel[ch].drum_effect_flag == 0) { + free_drum_effect(ch); + memset(note_table, 0, sizeof(int8_t) * 128); + + for(i = 0; i < 128; i++) { + if ((drum = channel[ch].drums[i]) != NULL) + { + if (drum->reverb_level != -1 + || drum->chorus_level != -1 || drum->delay_level != -1) { + note_table[num++] = i; + } + } + } + + channel[ch].drum_effect = (struct DrumPartEffect *)safe_malloc(sizeof(struct DrumPartEffect) * num); + + for(i = 0; i < num; i++) { + de = &(channel[ch].drum_effect[i]); + de->note = note = note_table[i]; + drum = channel[ch].drums[note]; + de->reverb_send = (int32_t)drum->reverb_level * (int32_t)get_reverb_level(ch) / 127; + de->chorus_send = (int32_t)drum->chorus_level * (int32_t)channel[ch].chorus_level / 127; + de->delay_send = (int32_t)drum->delay_level * (int32_t)channel[ch].delay_level / 127; + de->buf = (int32_t *)safe_malloc(AUDIO_BUFFER_SIZE * 8); + memset(de->buf, 0, AUDIO_BUFFER_SIZE * 8); + } + + channel[ch].drum_effect_num = num; + channel[ch].drum_effect_flag = 1; + } +} + +void Player::adjust_master_volume(void) +{ + int i, uv = upper_voices; + adjust_amplification(); + for(i = 0; i < uv; i++) + if(voice[i].status & (VOICE_ON | VOICE_SUSTAINED)) + { + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + } +} + +int Player::midi_drumpart_change(int ch, int isdrum) +{ + if (IS_SET_CHANNELMASK(drumchannel_mask, ch)) + return 0; + if (isdrum) { + SET_CHANNELMASK(drumchannels, ch); + SET_CHANNELMASK(current_file_info->drumchannels, ch); + } else { + UNSET_CHANNELMASK(drumchannels, ch); + UNSET_CHANNELMASK(current_file_info->drumchannels, ch); + } + return 1; +} + +void Player::midi_program_change(int ch, int prog) +{ + int dr = ISDRUMCHANNEL(ch); + int newbank, b, p, map; + + switch (play_system_mode) { + case GS_SYSTEM_MODE: /* GS */ + if ((map = channel[ch].bank_lsb) == 0) { + map = channel[ch].tone_map0_number; + } + switch (map) { + case 0: /* No change */ + break; + case 1: + channel[ch].mapID = (dr) ? SC_55_DRUM_MAP : SC_55_TONE_MAP; + break; + case 2: + channel[ch].mapID = (dr) ? SC_88_DRUM_MAP : SC_88_TONE_MAP; + break; + case 3: + channel[ch].mapID = (dr) ? SC_88PRO_DRUM_MAP : SC_88PRO_TONE_MAP; + break; + case 4: + channel[ch].mapID = (dr) ? SC_8850_DRUM_MAP : SC_8850_TONE_MAP; + break; + default: + break; + } + newbank = channel[ch].bank_msb; + break; + case XG_SYSTEM_MODE: /* XG */ + switch (channel[ch].bank_msb) { + case 0: /* Normal */ +#if 0 + if (ch == 9 && channel[ch].bank_lsb == 127 + && channel[ch].mapID == XG_DRUM_MAP) + /* FIXME: Why this part is drum? Is this correct? */ + break; +#endif +/* Eric's explanation for the FIXME (March 2004): + * + * I don't have the original email from my archived inbox, but I found a + * reply I made in my archived sent-mail from 1999. A September 5th message + * to Masanao Izumo is discussing a problem with a "reapxg.mid", a file which + * I still have, and how it issues an MSB=0 with a program change on ch 9, + * thus turning it into a melodic channel. The strange thing is, this doesn't + * happen on XG hardware, nor on the XG softsynth. It continues to play as a + * normal drum. The author of the midi file obviously intended it to be + * drumset 16 too. The original fix was to detect LSB == -1, then break so + * as to not set it to a melodic channel. I'm guessing that this somehow got + * mutated into checking for 127 instead, and the current FIXME is related to + * the original hack from Sept 1999. The Sept 5th email discusses patches + * being applied to version 2.5.1 to get XG drums to work properly, and a + * Sept 7th email to someone else discusses the fixes being part of the + * latest 2.6.0-beta3. A September 23rd email to Masanao Izumo specifically + * mentions the LSB == -1 hack (and reapxg.mid not playing "correctly" + * anymore), as well as new changes in 2.6.0 that broke a lot of other XG + * files (XG drum support was extremely buggy in 1999 and we were still trying + * to figure out how to initialize things to reproduce hardware behavior). An + * October 5th email says that 2.5.1 was correct, 2.6.0 had very broken XG + * drum changes, and 2.6.1 still has problems. Further discussions ensued + * over what was "correct": to follow the XG spec, or to reproduce + * "features" / bugs in the hardware. I can't find the rest of the + * discussions, but I think it ended with us agreeing to just follow the spec + * and not try to reproduce the hardware strangeness. I don't know how the + * current FIXME wound up the way it is now. I'm still going to guess it is + * related to the old reapxg.mid hack. + * + * Now that reset_midi() initializes channel[ch].bank_lsb to 0 instead of -1, + * checking for LSB == -1 won't do anything anymore, so changing the above + * FIXME to the original == -1 won't do any good. It is best to just #if 0 + * it out and leave it here as a reminder that there is at least one XG + * hardware / softsynth "bug" that is not reproduced by timidity at the + * moment. + * + * If the current FIXME actually reproduces some other XG hadware bug that + * I don't know about, then it may have a valid purpose. I just don't know + * what that purpose is at the moment. Perhaps someone else does? I still + * have src going back to 2.10.4, and the FIXME comment was already there by + * then. I don't see any entries in the Changelog that could explain it + * either. If someone has src from 2.5.1 through 2.10.3 and wants to + * investigate this further, go for it :) + */ + midi_drumpart_change(ch, 0); + channel[ch].mapID = XG_NORMAL_MAP; + dr = ISDRUMCHANNEL(ch); + break; + case 64: /* SFX voice */ + midi_drumpart_change(ch, 0); + channel[ch].mapID = XG_SFX64_MAP; + dr = ISDRUMCHANNEL(ch); + break; + case 126: /* SFX kit */ + midi_drumpart_change(ch, 1); + channel[ch].mapID = XG_SFX126_MAP; + dr = ISDRUMCHANNEL(ch); + break; + case 127: /* Drum kit */ + midi_drumpart_change(ch, 1); + channel[ch].mapID = XG_DRUM_MAP; + dr = ISDRUMCHANNEL(ch); + break; + default: + break; + } + newbank = channel[ch].bank_lsb; + break; + case GM2_SYSTEM_MODE: /* GM2 */ + if ((channel[ch].bank_msb & 0xfe) == 0x78) { /* 0x78/0x79 */ + midi_drumpart_change(ch, channel[ch].bank_msb == 0x78); + dr = ISDRUMCHANNEL(ch); + } + channel[ch].mapID = (dr) ? GM2_DRUM_MAP : GM2_TONE_MAP; + newbank = channel[ch].bank_lsb; + break; + default: + newbank = channel[ch].bank_msb; + break; + } + if (dr) { + channel[ch].bank = prog; /* newbank is ignored */ + channel[ch].program = prog; + if (instruments->drumSet(prog) == NULL || instruments->drumSet(prog)->alt == NULL) + channel[ch].altassign = instruments->drumSet(0)->alt; + else + channel[ch].altassign = instruments->drumSet(prog)->alt; + } else { + channel[ch].bank = (special_tonebank >= 0) + ? special_tonebank : newbank; + channel[ch].program = (instruments->defaultProgram(ch) == SPECIAL_PROGRAM) + ? SPECIAL_PROGRAM : prog; + channel[ch].altassign = NULL; + if (opt_realtime_playing) + { + b = channel[ch].bank, p = prog; + instruments->instrument_map(channel[ch].mapID, &b, &p); + play_midi_load_instrument(0, b, p); + } + } +} + + +/*! add a new layer. */ +void Player::add_channel_layer(int to_ch, int from_ch) +{ + if (to_ch >= MAX_CHANNELS || from_ch >= MAX_CHANNELS) + return; + /* add a channel layer */ + UNSET_CHANNELMASK(channel[to_ch].channel_layer, to_ch); + SET_CHANNELMASK(channel[to_ch].channel_layer, from_ch); + //printMessage(CMSG_INFO,VERB_NOISY,"Channel Layer (CH:%d -> CH:%d)", from_ch, to_ch); +} + +/*! remove all layers for this channel. */ +void Player::remove_channel_layer(int ch) +{ + int i, offset; + + if (ch >= MAX_CHANNELS) + return; + /* remove channel layers */ + offset = ch & ~0xf; + for (i = offset; i < offset + REDUCE_CHANNELS; i++) + UNSET_CHANNELMASK(channel[i].channel_layer, ch); + SET_CHANNELMASK(channel[ch].channel_layer, ch); +} + +/*! process system exclusive sent from parse_sysex_event_multi(). */ +void Player::process_sysex_event(int ev, int ch, int val, int b) +{ + int temp, msb, note; + + if (ch >= MAX_CHANNELS) + return; + if (ev == ME_SYSEX_MSB) { + channel[ch].sysex_msb_addr = b; + channel[ch].sysex_msb_val = val; + } else if(ev == ME_SYSEX_GS_MSB) { + channel[ch].sysex_gs_msb_addr = b; + channel[ch].sysex_gs_msb_val = val; + } else if(ev == ME_SYSEX_XG_MSB) { + channel[ch].sysex_xg_msb_addr = b; + channel[ch].sysex_xg_msb_val = val; + } else if(ev == ME_SYSEX_LSB) { /* Universal system exclusive message */ + msb = channel[ch].sysex_msb_addr; + note = channel[ch].sysex_msb_val; + channel[ch].sysex_msb_addr = channel[ch].sysex_msb_val = 0; + switch(b) + { + case 0x00: /* CAf Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].caf.pitch = val - 64; + //printMessage(CMSG_INFO,VERB_NOISY,"CAf Pitch Control (CH:%d %d semitones)", ch, channel[ch].caf.pitch); + break; + case 0x01: /* CAf Filter Cutoff Control */ + channel[ch].caf.cutoff = (val - 64) * 150; + //printMessage(CMSG_INFO,VERB_NOISY,"CAf Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].caf.cutoff); + break; + case 0x02: /* CAf Amplitude Control */ + channel[ch].caf.amp = (float)val / 64.0f - 1.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"CAf Amplitude Control (CH:%d %.2f)", ch, channel[ch].caf.amp); + break; + case 0x03: /* CAf LFO1 Rate Control */ + channel[ch].caf.lfo1_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"CAf LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].caf.lfo1_rate); + break; + case 0x04: /* CAf LFO1 Pitch Depth */ + channel[ch].caf.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CAf LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].caf.lfo1_pitch_depth); + break; + case 0x05: /* CAf LFO1 Filter Depth */ + channel[ch].caf.lfo1_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CAf LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].caf.lfo1_tvf_depth); + break; + case 0x06: /* CAf LFO1 Amplitude Depth */ + channel[ch].caf.lfo1_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"CAf LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].caf.lfo1_tva_depth); + break; + case 0x07: /* CAf LFO2 Rate Control */ + channel[ch].caf.lfo2_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"CAf LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].caf.lfo2_rate); + break; + case 0x08: /* CAf LFO2 Pitch Depth */ + channel[ch].caf.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CAf LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].caf.lfo2_pitch_depth); + break; + case 0x09: /* CAf LFO2 Filter Depth */ + channel[ch].caf.lfo2_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CAf LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].caf.lfo2_tvf_depth); + break; + case 0x0A: /* CAf LFO2 Amplitude Depth */ + channel[ch].caf.lfo2_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"CAf LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].caf.lfo2_tva_depth); + break; + case 0x0B: /* PAf Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].paf.pitch = val - 64; + //printMessage(CMSG_INFO,VERB_NOISY,"PAf Pitch Control (CH:%d %d semitones)", ch, channel[ch].paf.pitch); + break; + case 0x0C: /* PAf Filter Cutoff Control */ + channel[ch].paf.cutoff = (val - 64) * 150; + //printMessage(CMSG_INFO,VERB_NOISY,"PAf Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].paf.cutoff); + break; + case 0x0D: /* PAf Amplitude Control */ + channel[ch].paf.amp = (float)val / 64.0f - 1.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"PAf Amplitude Control (CH:%d %.2f)", ch, channel[ch].paf.amp); + break; + case 0x0E: /* PAf LFO1 Rate Control */ + channel[ch].paf.lfo1_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"PAf LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].paf.lfo1_rate); + break; + case 0x0F: /* PAf LFO1 Pitch Depth */ + channel[ch].paf.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"PAf LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].paf.lfo1_pitch_depth); + break; + case 0x10: /* PAf LFO1 Filter Depth */ + channel[ch].paf.lfo1_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"PAf LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].paf.lfo1_tvf_depth); + break; + case 0x11: /* PAf LFO1 Amplitude Depth */ + channel[ch].paf.lfo1_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"PAf LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].paf.lfo1_tva_depth); + break; + case 0x12: /* PAf LFO2 Rate Control */ + channel[ch].paf.lfo2_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"PAf LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].paf.lfo2_rate); + break; + case 0x13: /* PAf LFO2 Pitch Depth */ + channel[ch].paf.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"PAf LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].paf.lfo2_pitch_depth); + break; + case 0x14: /* PAf LFO2 Filter Depth */ + channel[ch].paf.lfo2_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"PAf LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].paf.lfo2_tvf_depth); + break; + case 0x15: /* PAf LFO2 Amplitude Depth */ + channel[ch].paf.lfo2_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"PAf LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].paf.lfo2_tva_depth); + break; + case 0x16: /* MOD Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].mod.pitch = val - 64; + //printMessage(CMSG_INFO,VERB_NOISY,"MOD Pitch Control (CH:%d %d semitones)", ch, channel[ch].mod.pitch); + break; + case 0x17: /* MOD Filter Cutoff Control */ + channel[ch].mod.cutoff = (val - 64) * 150; + //printMessage(CMSG_INFO,VERB_NOISY,"MOD Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].mod.cutoff); + break; + case 0x18: /* MOD Amplitude Control */ + channel[ch].mod.amp = (float)val / 64.0f - 1.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"MOD Amplitude Control (CH:%d %.2f)", ch, channel[ch].mod.amp); + break; + case 0x19: /* MOD LFO1 Rate Control */ + channel[ch].mod.lfo1_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"MOD LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].mod.lfo1_rate); + break; + case 0x1A: /* MOD LFO1 Pitch Depth */ + channel[ch].mod.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"MOD LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].mod.lfo1_pitch_depth); + break; + case 0x1B: /* MOD LFO1 Filter Depth */ + channel[ch].mod.lfo1_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"MOD LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].mod.lfo1_tvf_depth); + break; + case 0x1C: /* MOD LFO1 Amplitude Depth */ + channel[ch].mod.lfo1_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"MOD LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].mod.lfo1_tva_depth); + break; + case 0x1D: /* MOD LFO2 Rate Control */ + channel[ch].mod.lfo2_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"MOD LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].mod.lfo2_rate); + break; + case 0x1E: /* MOD LFO2 Pitch Depth */ + channel[ch].mod.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"MOD LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].mod.lfo2_pitch_depth); + break; + case 0x1F: /* MOD LFO2 Filter Depth */ + channel[ch].mod.lfo2_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"MOD LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].mod.lfo2_tvf_depth); + break; + case 0x20: /* MOD LFO2 Amplitude Depth */ + channel[ch].mod.lfo2_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"MOD LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].mod.lfo2_tva_depth); + break; + case 0x21: /* BEND Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].bend.pitch = val - 64; + //printMessage(CMSG_INFO,VERB_NOISY,"BEND Pitch Control (CH:%d %d semitones)", ch, channel[ch].bend.pitch); + break; + case 0x22: /* BEND Filter Cutoff Control */ + channel[ch].bend.cutoff = (val - 64) * 150; + //printMessage(CMSG_INFO,VERB_NOISY,"BEND Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].bend.cutoff); + break; + case 0x23: /* BEND Amplitude Control */ + channel[ch].bend.amp = (float)val / 64.0f - 1.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"BEND Amplitude Control (CH:%d %.2f)", ch, channel[ch].bend.amp); + break; + case 0x24: /* BEND LFO1 Rate Control */ + channel[ch].bend.lfo1_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"BEND LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].bend.lfo1_rate); + break; + case 0x25: /* BEND LFO1 Pitch Depth */ + channel[ch].bend.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"BEND LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].bend.lfo1_pitch_depth); + break; + case 0x26: /* BEND LFO1 Filter Depth */ + channel[ch].bend.lfo1_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"BEND LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].bend.lfo1_tvf_depth); + break; + case 0x27: /* BEND LFO1 Amplitude Depth */ + channel[ch].bend.lfo1_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"BEND LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].bend.lfo1_tva_depth); + break; + case 0x28: /* BEND LFO2 Rate Control */ + channel[ch].bend.lfo2_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"BEND LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].bend.lfo2_rate); + break; + case 0x29: /* BEND LFO2 Pitch Depth */ + channel[ch].bend.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"BEND LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].bend.lfo2_pitch_depth); + break; + case 0x2A: /* BEND LFO2 Filter Depth */ + channel[ch].bend.lfo2_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"BEND LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].bend.lfo2_tvf_depth); + break; + case 0x2B: /* BEND LFO2 Amplitude Depth */ + channel[ch].bend.lfo2_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"BEND LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].bend.lfo2_tva_depth); + break; + case 0x2C: /* CC1 Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].cc1.pitch = val - 64; + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 Pitch Control (CH:%d %d semitones)", ch, channel[ch].cc1.pitch); + break; + case 0x2D: /* CC1 Filter Cutoff Control */ + channel[ch].cc1.cutoff = (val - 64) * 150; + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].cc1.cutoff); + break; + case 0x2E: /* CC1 Amplitude Control */ + channel[ch].cc1.amp = (float)val / 64.0f - 1.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 Amplitude Control (CH:%d %.2f)", ch, channel[ch].cc1.amp); + break; + case 0x2F: /* CC1 LFO1 Rate Control */ + channel[ch].cc1.lfo1_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].cc1.lfo1_rate); + break; + case 0x30: /* CC1 LFO1 Pitch Depth */ + channel[ch].cc1.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].cc1.lfo1_pitch_depth); + break; + case 0x31: /* CC1 LFO1 Filter Depth */ + channel[ch].cc1.lfo1_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].cc1.lfo1_tvf_depth); + break; + case 0x32: /* CC1 LFO1 Amplitude Depth */ + channel[ch].cc1.lfo1_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].cc1.lfo1_tva_depth); + break; + case 0x33: /* CC1 LFO2 Rate Control */ + channel[ch].cc1.lfo2_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].cc1.lfo2_rate); + break; + case 0x34: /* CC1 LFO2 Pitch Depth */ + channel[ch].cc1.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].cc1.lfo2_pitch_depth); + break; + case 0x35: /* CC1 LFO2 Filter Depth */ + channel[ch].cc1.lfo2_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].cc1.lfo2_tvf_depth); + break; + case 0x36: /* CC1 LFO2 Amplitude Depth */ + channel[ch].cc1.lfo2_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].cc1.lfo2_tva_depth); + break; + case 0x37: /* CC2 Pitch Control */ + if(val > 0x58) {val = 0x58;} + else if(val < 0x28) {val = 0x28;} + channel[ch].cc2.pitch = val - 64; + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 Pitch Control (CH:%d %d semitones)", ch, channel[ch].cc2.pitch); + break; + case 0x38: /* CC2 Filter Cutoff Control */ + channel[ch].cc2.cutoff = (val - 64) * 150; + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 Filter Cutoff Control (CH:%d %d cents)", ch, channel[ch].cc2.cutoff); + break; + case 0x39: /* CC2 Amplitude Control */ + channel[ch].cc2.amp = (float)val / 64.0f - 1.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 Amplitude Control (CH:%d %.2f)", ch, channel[ch].cc2.amp); + break; + case 0x3A: /* CC2 LFO1 Rate Control */ + channel[ch].cc2.lfo1_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 LFO1 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].cc2.lfo1_rate); + break; + case 0x3B: /* CC2 LFO1 Pitch Depth */ + channel[ch].cc2.lfo1_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 LFO1 Pitch Depth (CH:%d %d cents)", ch, channel[ch].cc2.lfo1_pitch_depth); + break; + case 0x3C: /* CC2 LFO1 Filter Depth */ + channel[ch].cc2.lfo1_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 LFO1 Filter Depth (CH:%d %d cents)", ch, channel[ch].cc2.lfo1_tvf_depth); + break; + case 0x3D: /* CC2 LFO1 Amplitude Depth */ + channel[ch].cc2.lfo1_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 LFO1 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].cc2.lfo1_tva_depth); + break; + case 0x3E: /* CC2 LFO2 Rate Control */ + channel[ch].cc2.lfo2_rate = (float)(val - 64) / 6.4f; + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 LFO2 Rate Control (CH:%d %.1f Hz)", ch, channel[ch].cc2.lfo2_rate); + break; + case 0x3F: /* CC2 LFO2 Pitch Depth */ + channel[ch].cc2.lfo2_pitch_depth = conv_lfo_pitch_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 LFO2 Pitch Depth (CH:%d %d cents)", ch, channel[ch].cc2.lfo2_pitch_depth); + break; + case 0x40: /* CC2 LFO2 Filter Depth */ + channel[ch].cc2.lfo2_tvf_depth = conv_lfo_filter_depth(val); + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 LFO2 Filter Depth (CH:%d %d cents)", ch, channel[ch].cc2.lfo2_tvf_depth); + break; + case 0x41: /* CC2 LFO2 Amplitude Depth */ + channel[ch].cc2.lfo2_tva_depth = (float)val / 127.0f; + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 LFO2 Amplitude Depth (CH:%d %.2f)", ch, channel[ch].cc2.lfo2_tva_depth); + break; + case 0x42: /* Note Limit Low */ + channel[ch].note_limit_low = val; + //printMessage(CMSG_INFO,VERB_NOISY,"Note Limit Low (CH:%d VAL:%d)", ch, val); + break; + case 0x43: /* Note Limit High */ + channel[ch].note_limit_high = val; + //printMessage(CMSG_INFO,VERB_NOISY,"Note Limit High (CH:%d VAL:%d)", ch, val); + break; + case 0x44: /* Velocity Limit Low */ + channel[ch].vel_limit_low = val; + //printMessage(CMSG_INFO,VERB_NOISY,"Velocity Limit Low (CH:%d VAL:%d)", ch, val); + break; + case 0x45: /* Velocity Limit High */ + channel[ch].vel_limit_high = val; + //printMessage(CMSG_INFO,VERB_NOISY,"Velocity Limit High (CH:%d VAL:%d)", ch, val); + break; + case 0x46: /* Rx. Note Off */ + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + set_rx_drum(channel[ch].drums[note], RX_NOTE_OFF, val); + printMessage(CMSG_INFO, VERB_NOISY, + "Drum Instrument Rx. Note Off (CH:%d NOTE:%d VAL:%d)", + ch, note, val); + break; + case 0x47: /* Rx. Note On */ + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + set_rx_drum(channel[ch].drums[note], RX_NOTE_ON, val); + printMessage(CMSG_INFO, VERB_NOISY, + "Drum Instrument Rx. Note On (CH:%d NOTE:%d VAL:%d)", + ch, note, val); + break; + case 0x48: /* Rx. Pitch Bend */ + set_rx(ch, RX_PITCH_BEND, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Pitch Bend (CH:%d VAL:%d)", ch, val); + break; + case 0x49: /* Rx. Channel Pressure */ + set_rx(ch, RX_CH_PRESSURE, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Channel Pressure (CH:%d VAL:%d)", ch, val); + break; + case 0x4A: /* Rx. Program Change */ + set_rx(ch, RX_PROGRAM_CHANGE, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Program Change (CH:%d VAL:%d)", ch, val); + break; + case 0x4B: /* Rx. Control Change */ + set_rx(ch, RX_CONTROL_CHANGE, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Control Change (CH:%d VAL:%d)", ch, val); + break; + case 0x4C: /* Rx. Poly Pressure */ + set_rx(ch, RX_POLY_PRESSURE, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Poly Pressure (CH:%d VAL:%d)", ch, val); + break; + case 0x4D: /* Rx. Note Message */ + set_rx(ch, RX_NOTE_MESSAGE, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Note Message (CH:%d VAL:%d)", ch, val); + break; + case 0x4E: /* Rx. RPN */ + set_rx(ch, RX_RPN, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. RPN (CH:%d VAL:%d)", ch, val); + break; + case 0x4F: /* Rx. NRPN */ + set_rx(ch, RX_NRPN, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. NRPN (CH:%d VAL:%d)", ch, val); + break; + case 0x50: /* Rx. Modulation */ + set_rx(ch, RX_MODULATION, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Modulation (CH:%d VAL:%d)", ch, val); + break; + case 0x51: /* Rx. Volume */ + set_rx(ch, RX_VOLUME, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Volume (CH:%d VAL:%d)", ch, val); + break; + case 0x52: /* Rx. Panpot */ + set_rx(ch, RX_PANPOT, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Panpot (CH:%d VAL:%d)", ch, val); + break; + case 0x53: /* Rx. Expression */ + set_rx(ch, RX_EXPRESSION, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Expression (CH:%d VAL:%d)", ch, val); + break; + case 0x54: /* Rx. Hold1 */ + set_rx(ch, RX_HOLD1, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Hold1 (CH:%d VAL:%d)", ch, val); + break; + case 0x55: /* Rx. Portamento */ + set_rx(ch, RX_PORTAMENTO, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Portamento (CH:%d VAL:%d)", ch, val); + break; + case 0x56: /* Rx. Sostenuto */ + set_rx(ch, RX_SOSTENUTO, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Sostenuto (CH:%d VAL:%d)", ch, val); + break; + case 0x57: /* Rx. Soft */ + set_rx(ch, RX_SOFT, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Soft (CH:%d VAL:%d)", ch, val); + break; + case 0x58: /* Rx. Bank Select */ + set_rx(ch, RX_BANK_SELECT, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Bank Select (CH:%d VAL:%d)", ch, val); + break; + case 0x59: /* Rx. Bank Select LSB */ + set_rx(ch, RX_BANK_SELECT_LSB, val); + //printMessage(CMSG_INFO,VERB_NOISY,"Rx. Bank Select LSB (CH:%d VAL:%d)", ch, val); + break; + case 0x60: /* Reverb Type (GM2) */ + if (val > 8) {val = 8;} + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Type (%d)", val); + reverb->set_reverb_macro_gm2(val); + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + break; + case 0x61: /* Chorus Type (GM2) */ + if (val > 5) {val = 5;} + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Type (%d)", val); + reverb->set_chorus_macro_gs(val); + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + break; + default: + break; + } + return; + } else if(ev == ME_SYSEX_GS_LSB) { /* GS system exclusive message */ + msb = channel[ch].sysex_gs_msb_addr; + note = channel[ch].sysex_gs_msb_val; + channel[ch].sysex_gs_msb_addr = channel[ch].sysex_gs_msb_val = 0; + switch(b) + { + case 0x00: /* EQ ON/OFF */ + if(!opt_eq_control) {break;} + channel[ch].eq_gs = val; + break; + case 0x01: /* EQ LOW FREQ */ + if(!opt_eq_control) {break;} + reverb->eq_status_gs.low_freq = val; + reverb->recompute_eq_status_gs(); + break; + case 0x02: /* EQ LOW GAIN */ + if(!opt_eq_control) {break;} + reverb->eq_status_gs.low_gain = val; + reverb->recompute_eq_status_gs(); + break; + case 0x03: /* EQ HIGH FREQ */ + if(!opt_eq_control) {break;} + reverb->eq_status_gs.high_freq = val; + reverb->recompute_eq_status_gs(); + break; + case 0x04: /* EQ HIGH GAIN */ + if(!opt_eq_control) {break;} + reverb->eq_status_gs.high_gain = val; + reverb->recompute_eq_status_gs(); + break; + case 0x05: /* Reverb Macro */ + if (val > 7) {val = 7;} + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Macro (%d)",val); + reverb->set_reverb_macro_gs(val); + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + break; + case 0x06: /* Reverb Character */ + if (val > 7) {val = 7;} + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Character (%d)",val); + if (reverb->reverb_status_gs.character != val) { + reverb->reverb_status_gs.character = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } + break; + case 0x07: /* Reverb Pre-LPF */ + if (val > 7) {val = 7;} + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Pre-LPF (%d)",val); + if(reverb->reverb_status_gs.pre_lpf != val) { + reverb->reverb_status_gs.pre_lpf = val; + reverb->recompute_reverb_status_gs(); + } + break; + case 0x08: /* Reverb Level */ + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Level (%d)",val); + if(reverb->reverb_status_gs.level != val) { + reverb->reverb_status_gs.level = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } + break; + case 0x09: /* Reverb Time */ + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Time (%d)",val); + if(reverb->reverb_status_gs.time != val) { + reverb->reverb_status_gs.time = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } + break; + case 0x0A: /* Reverb Delay Feedback */ + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Delay Feedback (%d)",val); + if(reverb->reverb_status_gs.delay_feedback != val) { + reverb->reverb_status_gs.delay_feedback = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } + break; + case 0x0C: /* Reverb Predelay Time */ + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Predelay Time (%d)",val); + if(reverb->reverb_status_gs.pre_delay_time != val) { + reverb->reverb_status_gs.pre_delay_time = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } + break; + case 0x0D: /* Chorus Macro */ + if (val > 7) {val = 7;} + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Macro (%d)",val); + reverb->set_chorus_macro_gs(val); + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + break; + case 0x0E: /* Chorus Pre-LPF */ + if (val > 7) {val = 7;} + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Pre-LPF (%d)",val); + if (reverb->chorus_status_gs.pre_lpf != val) { + reverb->chorus_status_gs.pre_lpf = val; + reverb->recompute_chorus_status_gs(); + } + break; + case 0x0F: /* Chorus Level */ + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Level (%d)",val); + if (reverb->chorus_status_gs.level != val) { + reverb->chorus_status_gs.level = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x10: /* Chorus Feedback */ + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Feedback (%d)",val); + if (reverb->chorus_status_gs.feedback != val) { + reverb->chorus_status_gs.feedback = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x11: /* Chorus Delay */ + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Delay (%d)",val); + if (reverb->chorus_status_gs.delay != val) { + reverb->chorus_status_gs.delay = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x12: /* Chorus Rate */ + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Rate (%d)",val); + if (reverb->chorus_status_gs.rate != val) { + reverb->chorus_status_gs.rate = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x13: /* Chorus Depth */ + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Depth (%d)",val); + if (reverb->chorus_status_gs.depth != val) { + reverb->chorus_status_gs.depth = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x14: /* Chorus Send Level to Reverb */ + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Send Level to Reverb (%d)",val); + if (reverb->chorus_status_gs.send_reverb != val) { + reverb->chorus_status_gs.send_reverb = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x15: /* Chorus Send Level to Delay */ + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Send Level to Delay (%d)",val); + if (reverb->chorus_status_gs.send_delay != val) { + reverb->chorus_status_gs.send_delay = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } + break; + case 0x16: /* Delay Macro */ + if (val > 7) {val = 7;} + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Macro (%d)",val); + reverb->set_delay_macro_gs(val); + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + break; + case 0x17: /* Delay Pre-LPF */ + if (val > 7) {val = 7;} + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Pre-LPF (%d)",val); + val &= 0x7; + if (reverb->delay_status_gs.pre_lpf != val) { + reverb->delay_status_gs.pre_lpf = val; + reverb->recompute_delay_status_gs(); + } + break; + case 0x18: /* Delay Time Center */ + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Time Center (%d)",val); + if (reverb->delay_status_gs.time_c != val) { + reverb->delay_status_gs.time_c = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x19: /* Delay Time Ratio Left */ + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Time Ratio Left (%d)",val); + if (val == 0) {val = 1;} + if (reverb->delay_status_gs.time_l != val) { + reverb->delay_status_gs.time_l = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1A: /* Delay Time Ratio Right */ + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Time Ratio Right (%d)",val); + if (val == 0) {val = 1;} + if (reverb->delay_status_gs.time_r != val) { + reverb->delay_status_gs.time_r = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1B: /* Delay Level Center */ + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Level Center (%d)",val); + if (reverb->delay_status_gs.level_center != val) { + reverb->delay_status_gs.level_center = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1C: /* Delay Level Left */ + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Level Left (%d)",val); + if (reverb->delay_status_gs.level_left != val) { + reverb->delay_status_gs.level_left = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1D: /* Delay Level Right */ + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Level Right (%d)",val); + if (reverb->delay_status_gs.level_right != val) { + reverb->delay_status_gs.level_right = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1E: /* Delay Level */ + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Level (%d)",val); + if (reverb->delay_status_gs.level != val) { + reverb->delay_status_gs.level = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x1F: /* Delay Feedback */ + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Feedback (%d)",val); + if (reverb->delay_status_gs.feedback != val) { + reverb->delay_status_gs.feedback = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x20: /* Delay Send Level to Reverb */ + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Send Level to Reverb (%d)",val); + if (reverb->delay_status_gs.send_reverb != val) { + reverb->delay_status_gs.send_reverb = val; + reverb->recompute_delay_status_gs(); + reverb->init_ch_delay(); + } + break; + case 0x21: /* Velocity Sense Depth */ + channel[ch].velocity_sense_depth = val; + //printMessage(CMSG_INFO,VERB_NOISY,"Velocity Sense Depth (CH:%d VAL:%d)",ch,val); + break; + case 0x22: /* Velocity Sense Offset */ + channel[ch].velocity_sense_offset = val; + //printMessage(CMSG_INFO,VERB_NOISY,"Velocity Sense Offset (CH:%d VAL:%d)",ch,val); + break; + case 0x23: /* Insertion Effect ON/OFF */ + if(!opt_insertion_effect) {break;} + if(channel[ch].insertion_effect != val) { + //if(val) {//printMessage(CMSG_INFO,VERB_NOISY,"EFX ON (CH:%d)",ch);} + //else {//printMessage(CMSG_INFO,VERB_NOISY,"EFX OFF (CH:%d)",ch);} + } + channel[ch].insertion_effect = val; + break; + case 0x24: /* Assign Mode */ + channel[ch].assign_mode = val; + if(val == 0) { + //printMessage(CMSG_INFO,VERB_NOISY,"Assign Mode: Single (CH:%d)",ch); + } else if(val == 1) { + //printMessage(CMSG_INFO,VERB_NOISY,"Assign Mode: Limited-Multi (CH:%d)",ch); + } else if(val == 2) { + //printMessage(CMSG_INFO,VERB_NOISY,"Assign Mode: Full-Multi (CH:%d)",ch); + } + break; + case 0x25: /* TONE MAP-0 NUMBER */ + channel[ch].tone_map0_number = val; + //printMessage(CMSG_INFO,VERB_NOISY,"Tone Map-0 Number (CH:%d VAL:%d)",ch,val); + break; + case 0x26: /* Pitch Offset Fine */ + channel[ch].pitch_offset_fine = (double)((((int32_t)val << 4) | (int32_t)val) - 0x80) / 10.0; + //printMessage(CMSG_INFO,VERB_NOISY,"Pitch Offset Fine (CH:%d %3fHz)",ch,channel[ch].pitch_offset_fine); + break; + case 0x27: /* Insertion Effect Parameter */ + if(!opt_insertion_effect) {break;} + temp = reverb->insertion_effect_gs.type; + reverb->insertion_effect_gs.type_msb = val; + reverb->insertion_effect_gs.type = ((int32_t)reverb->insertion_effect_gs.type_msb << 8) | (int32_t)reverb->insertion_effect_gs.type_lsb; + if(temp == reverb->insertion_effect_gs.type) { + reverb->recompute_insertion_effect_gs(); + } else { + reverb->realloc_insertion_effect_gs(); + } + break; + case 0x28: /* Insertion Effect Parameter */ + if(!opt_insertion_effect) {break;} + temp = reverb->insertion_effect_gs.type; + reverb->insertion_effect_gs.type_lsb = val; + reverb->insertion_effect_gs.type = ((int32_t)reverb->insertion_effect_gs.type_msb << 8) | (int32_t)reverb->insertion_effect_gs.type_lsb; + if(temp == reverb->insertion_effect_gs.type) { + reverb->recompute_insertion_effect_gs(); + } else { + reverb->realloc_insertion_effect_gs(); + } + break; + case 0x29: + reverb->insertion_effect_gs.parameter[0] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2A: + reverb->insertion_effect_gs.parameter[1] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2B: + reverb->insertion_effect_gs.parameter[2] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2C: + reverb->insertion_effect_gs.parameter[3] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2D: + reverb->insertion_effect_gs.parameter[4] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2E: + reverb->insertion_effect_gs.parameter[5] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x2F: + reverb->insertion_effect_gs.parameter[6] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x30: + reverb->insertion_effect_gs.parameter[7] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x31: + reverb->insertion_effect_gs.parameter[8] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x32: + reverb->insertion_effect_gs.parameter[9] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x33: + reverb->insertion_effect_gs.parameter[10] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x34: + reverb->insertion_effect_gs.parameter[11] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x35: + reverb->insertion_effect_gs.parameter[12] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x36: + reverb->insertion_effect_gs.parameter[13] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x37: + reverb->insertion_effect_gs.parameter[14] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x38: + reverb->insertion_effect_gs.parameter[15] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x39: + reverb->insertion_effect_gs.parameter[16] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3A: + reverb->insertion_effect_gs.parameter[17] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3B: + reverb->insertion_effect_gs.parameter[18] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3C: + reverb->insertion_effect_gs.parameter[19] = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3D: + reverb->insertion_effect_gs.send_reverb = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3E: + reverb->insertion_effect_gs.send_chorus = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x3F: + reverb->insertion_effect_gs.send_delay = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x40: + reverb->insertion_effect_gs.control_source1 = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x41: + reverb->insertion_effect_gs.control_depth1 = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x42: + reverb->insertion_effect_gs.control_source2 = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x43: + reverb->insertion_effect_gs.control_depth2 = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x44: + reverb->insertion_effect_gs.send_eq_switch = val; + reverb->recompute_insertion_effect_gs(); + break; + case 0x45: /* Rx. Channel */ + reset_controllers(ch); + all_notes_off(ch); + if (val == 0x80) + remove_channel_layer(ch); + else + add_channel_layer(ch, val); + break; + case 0x46: /* Channel Msg Rx Port */ + reset_controllers(ch); + all_notes_off(ch); + channel[ch].port_select = val; + break; + case 0x47: /* Play Note Number */ + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + channel[ch].drums[note]->play_note = val; + printMessage(CMSG_INFO, VERB_NOISY, + "Drum Instrument Play Note (CH:%d NOTE:%d VAL:%d)", + ch, note, channel[ch].drums[note]->play_note); + channel[ch].pitchfactor = 0; + break; + default: + break; + } + return; + } else if(ev == ME_SYSEX_XG_LSB) { /* XG system exclusive message */ + msb = channel[ch].sysex_xg_msb_addr; + note = channel[ch].sysex_xg_msb_val; + if (note == 3 && msb == 0) { /* Effect 2 */ + note = 0; /* force insertion effect num 0 ?? */ + if (note >= XG_INSERTION_EFFECT_NUM || note < 0) {return;} + switch(b) + { + case 0x00: /* Insertion Effect Type MSB */ + if (reverb->insertion_effect_xg[note].type_msb != val) { + //printMessage(CMSG_INFO,VERB_NOISY,"Insertion Effect Type MSB (%d %02X)", note, val); + reverb->insertion_effect_xg[note].type_msb = val; + reverb->realloc_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x01: /* Insertion Effect Type LSB */ + if (reverb->insertion_effect_xg[note].type_lsb != val) { + //printMessage(CMSG_INFO,VERB_NOISY,"Insertion Effect Type LSB (%d %02X)", note, val); + reverb->insertion_effect_xg[note].type_lsb = val; + reverb->realloc_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x02: /* Insertion Effect Parameter 1 - 10 */ + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + if (reverb->insertion_effect_xg[note].use_msb) {break;} + temp = b - 0x02; + //printMessage(CMSG_INFO,VERB_NOISY,"Insertion Effect Parameter %d (%d %d)", temp + 1, note, val); + if (reverb->insertion_effect_xg[note].param_lsb[temp] != val) { + reverb->insertion_effect_xg[note].param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x0C: /* Insertion Effect Part */ + //printMessage(CMSG_INFO,VERB_NOISY,"Insertion Effect Part (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].part != val) { + reverb->insertion_effect_xg[note].part = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x0D: /* MW Insertion Control Depth */ + //printMessage(CMSG_INFO,VERB_NOISY,"MW Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].mw_depth != val) { + reverb->insertion_effect_xg[note].mw_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x0E: /* BEND Insertion Control Depth */ + //printMessage(CMSG_INFO,VERB_NOISY,"BEND Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].bend_depth != val) { + reverb->insertion_effect_xg[note].bend_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x0F: /* CAT Insertion Control Depth */ + //printMessage(CMSG_INFO,VERB_NOISY,"CAT Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].cat_depth != val) { + reverb->insertion_effect_xg[note].cat_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x10: /* AC1 Insertion Control Depth */ + //printMessage(CMSG_INFO,VERB_NOISY,"AC1 Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].ac1_depth != val) { + reverb->insertion_effect_xg[note].ac1_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x11: /* AC2 Insertion Control Depth */ + //printMessage(CMSG_INFO,VERB_NOISY,"AC2 Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].ac2_depth != val) { + reverb->insertion_effect_xg[note].ac2_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x12: /* CBC1 Insertion Control Depth */ + //printMessage(CMSG_INFO,VERB_NOISY,"CBC1 Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].cbc1_depth != val) { + reverb->insertion_effect_xg[note].cbc1_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x13: /* CBC2 Insertion Control Depth */ + //printMessage(CMSG_INFO,VERB_NOISY,"CBC2 Insertion Control Depth (%d %d)", note, val); + if (reverb->insertion_effect_xg[note].cbc2_depth != val) { + reverb->insertion_effect_xg[note].cbc2_depth = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x20: /* Insertion Effect Parameter 11 - 16 */ + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + temp = b - 0x20 + 10; + //printMessage(CMSG_INFO,VERB_NOISY,"Insertion Effect Parameter %d (%d %d)", temp + 1, note, val); + if (reverb->insertion_effect_xg[note].param_lsb[temp] != val) { + reverb->insertion_effect_xg[note].param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x30: /* Insertion Effect Parameter 1 - 10 MSB */ + case 0x32: + case 0x34: + case 0x36: + case 0x38: + case 0x3A: + case 0x3C: + case 0x3E: + case 0x40: + case 0x42: + if (!reverb->insertion_effect_xg[note].use_msb) {break;} + temp = (b - 0x30) / 2; + //printMessage(CMSG_INFO,VERB_NOISY,"Insertion Effect Parameter %d MSB (%d %d)", temp + 1, note, val); + if (reverb->insertion_effect_xg[note].param_msb[temp] != val) { + reverb->insertion_effect_xg[note].param_msb[temp] = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + case 0x31: /* Insertion Effect Parameter 1 - 10 LSB */ + case 0x33: + case 0x35: + case 0x37: + case 0x39: + case 0x3B: + case 0x3D: + case 0x3F: + case 0x41: + case 0x43: + if (!reverb->insertion_effect_xg[note].use_msb) {break;} + temp = (b - 0x31) / 2; + //printMessage(CMSG_INFO,VERB_NOISY,"Insertion Effect Parameter %d LSB (%d %d)", temp + 1, note, val); + if (reverb->insertion_effect_xg[note].param_lsb[temp] != val) { + reverb->insertion_effect_xg[note].param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->insertion_effect_xg[note]); + } + break; + default: + break; + } + } else if (note == 2 && msb == 1) { /* Effect 1 */ + note = 0; /* force variation effect num 0 ?? */ + switch(b) + { + case 0x00: /* Reverb Type MSB */ + if (reverb->reverb_status_xg.type_msb != val) { + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Type MSB (%02X)", val); + reverb->reverb_status_xg.type_msb = val; + reverb->realloc_effect_xg(&reverb->reverb_status_xg); + } + break; + case 0x01: /* Reverb Type LSB */ + if (reverb->reverb_status_xg.type_lsb != val) { + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Type LSB (%02X)", val); + reverb->reverb_status_xg.type_lsb = val; + reverb->realloc_effect_xg(&reverb->reverb_status_xg); + } + break; + case 0x02: /* Reverb Parameter 1 - 10 */ + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Parameter %d (%d)", b - 0x02 + 1, val); + if (reverb->reverb_status_xg.param_lsb[b - 0x02] != val) { + reverb->reverb_status_xg.param_lsb[b - 0x02] = val; + reverb->recompute_effect_xg(&reverb->reverb_status_xg); + } + break; + case 0x0C: /* Reverb Return */ +#if 0 /* XG specific reverb is not currently implemented */ + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Return (%d)", val); + if (reverb->reverb_status_xg.ret != val) { + reverb->reverb_status_xg.ret = val; + reverb->recompute_effect_xg(&reverb->reverb_status_xg); + } +#else /* use GS reverb instead */ + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Return (%d)", val); + if (reverb->reverb_status_gs.level != val) { + reverb->reverb_status_gs.level = val; + reverb->recompute_reverb_status_gs(); + reverb->init_reverb(); + } +#endif + break; + case 0x0D: /* Reverb Pan */ + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Pan (%d)", val); + if (reverb->reverb_status_xg.pan != val) { + reverb->reverb_status_xg.pan = val; + reverb->recompute_effect_xg(&reverb->reverb_status_xg); + } + break; + case 0x10: /* Reverb Parameter 11 - 16 */ + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + temp = b - 0x10 + 10; + //printMessage(CMSG_INFO,VERB_NOISY,"Reverb Parameter %d (%d)", temp + 1, val); + if (reverb->reverb_status_xg.param_lsb[temp] != val) { + reverb->reverb_status_xg.param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->reverb_status_xg); + } + break; + case 0x20: /* Chorus Type MSB */ + if (reverb->chorus_status_xg.type_msb != val) { + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Type MSB (%02X)", val); + reverb->chorus_status_xg.type_msb = val; + reverb->realloc_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x21: /* Chorus Type LSB */ + if (reverb->chorus_status_xg.type_lsb != val) { + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Type LSB (%02X)", val); + reverb->chorus_status_xg.type_lsb = val; + reverb->realloc_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x22: /* Chorus Parameter 1 - 10 */ + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Parameter %d (%d)", b - 0x22 + 1, val); + if (reverb->chorus_status_xg.param_lsb[b - 0x22] != val) { + reverb->chorus_status_xg.param_lsb[b - 0x22] = val; + reverb->recompute_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x2C: /* Chorus Return */ +#if 0 /* XG specific chorus is not currently implemented */ + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Return (%d)", val); + if (reverb->chorus_status_xg.ret != val) { + reverb->chorus_status_xg.ret = val; + reverb->recompute_effect_xg(&reverb->chorus_status_xg); + } +#else /* use GS chorus instead */ + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Return (%d)", val); + if (reverb->chorus_status_gs.level != val) { + reverb->chorus_status_gs.level = val; + reverb->recompute_chorus_status_gs(); + reverb->init_ch_chorus(); + } +#endif + break; + case 0x2D: /* Chorus Pan */ + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Pan (%d)", val); + if (reverb->chorus_status_xg.pan != val) { + reverb->chorus_status_xg.pan = val; + reverb->recompute_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x2E: /* Send Chorus To Reverb */ + //printMessage(CMSG_INFO,VERB_NOISY,"Send Chorus To Reverb (%d)", val); + if (reverb->chorus_status_xg.send_reverb != val) { + reverb->chorus_status_xg.send_reverb = val; + reverb->recompute_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x30: /* Chorus Parameter 11 - 16 */ + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + temp = b - 0x30 + 10; + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Parameter %d (%d)", temp + 1, val); + if (reverb->chorus_status_xg.param_lsb[temp] != val) { + reverb->chorus_status_xg.param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->chorus_status_xg); + } + break; + case 0x40: /* Variation Type MSB */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + if (reverb->variation_effect_xg[note].type_msb != val) { + //printMessage(CMSG_INFO,VERB_NOISY,"Variation Type MSB (%02X)", val); + reverb->variation_effect_xg[note].type_msb = val; + reverb->realloc_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x41: /* Variation Type LSB */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + if (reverb->variation_effect_xg[note].type_lsb != val) { + //printMessage(CMSG_INFO,VERB_NOISY,"Variation Type LSB (%02X)", val); + reverb->variation_effect_xg[note].type_lsb = val; + reverb->realloc_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x42: /* Variation Parameter 1 - 10 MSB */ + case 0x44: + case 0x46: + case 0x48: + case 0x4A: + case 0x4C: + case 0x4E: + case 0x50: + case 0x52: + case 0x54: + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + temp = (b - 0x42) / 2; + //printMessage(CMSG_INFO,VERB_NOISY,"Variation Parameter %d MSB (%d)", temp, val); + if (reverb->variation_effect_xg[note].param_msb[temp] != val) { + reverb->variation_effect_xg[note].param_msb[temp] = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x43: /* Variation Parameter 1 - 10 LSB */ + case 0x45: + case 0x47: + case 0x49: + case 0x4B: + case 0x4D: + case 0x4F: + case 0x51: + case 0x53: + case 0x55: + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + temp = (b - 0x43) / 2; + //printMessage(CMSG_INFO,VERB_NOISY,"Variation Parameter %d LSB (%d)", temp, val); + if (reverb->variation_effect_xg[note].param_lsb[temp] != val) { + reverb->variation_effect_xg[note].param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x56: /* Variation Return */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"Variation Return (%d)", val); + if (reverb->variation_effect_xg[note].ret != val) { + reverb->variation_effect_xg[note].ret = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x57: /* Variation Pan */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"Variation Pan (%d)", val); + if (reverb->variation_effect_xg[note].pan != val) { + reverb->variation_effect_xg[note].pan = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x58: /* Send Variation To Reverb */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"Send Variation To Reverb (%d)", val); + if (reverb->variation_effect_xg[note].send_reverb != val) { + reverb->variation_effect_xg[note].send_reverb = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x59: /* Send Variation To Chorus */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"Send Variation To Chorus (%d)", val); + if (reverb->variation_effect_xg[note].send_chorus != val) { + reverb->variation_effect_xg[note].send_chorus = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5A: /* Variation Connection */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"Variation Connection (%d)", val); + if (reverb->variation_effect_xg[note].connection != val) { + reverb->variation_effect_xg[note].connection = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5B: /* Variation Part */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"Variation Part (%d)", val); + if (reverb->variation_effect_xg[note].part != val) { + reverb->variation_effect_xg[note].part = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5C: /* MW Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"MW Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].mw_depth != val) { + reverb->variation_effect_xg[note].mw_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5D: /* BEND Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"BEND Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].bend_depth != val) { + reverb->variation_effect_xg[note].bend_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5E: /* CAT Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"CAT Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].cat_depth != val) { + reverb->variation_effect_xg[note].cat_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x5F: /* AC1 Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"AC1 Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].ac1_depth != val) { + reverb->variation_effect_xg[note].ac1_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x60: /* AC2 Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"AC2 Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].ac2_depth != val) { + reverb->variation_effect_xg[note].ac2_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x61: /* CBC1 Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"CBC1 Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].cbc1_depth != val) { + reverb->variation_effect_xg[note].cbc1_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x62: /* CBC2 Variation Control Depth */ + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"CBC2 Variation Control Depth (%d)", val); + if (reverb->variation_effect_xg[note].cbc2_depth != val) { + reverb->variation_effect_xg[note].cbc2_depth = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + case 0x70: /* Variation Parameter 11 - 16 */ + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + temp = b - 0x70 + 10; + if (note >= XG_VARIATION_EFFECT_NUM || note < 0) {break;} + //printMessage(CMSG_INFO,VERB_NOISY,"Variation Parameter %d (%d)", temp + 1, val); + if (reverb->variation_effect_xg[note].param_lsb[temp] != val) { + reverb->variation_effect_xg[note].param_lsb[temp] = val; + reverb->recompute_effect_xg(&reverb->variation_effect_xg[note]); + } + break; + default: + break; + } + } else if (note == 2 && msb == 40) { /* Multi EQ */ + switch(b) + { + case 0x00: /* EQ type */ + if(opt_eq_control) { + //printMessage(CMSG_INFO,VERB_NOISY,"EQ type (%d)", val); + reverb->multi_eq_xg.type = val; + reverb->set_multi_eq_type_xg(val); + reverb->recompute_multi_eq_xg(); + } + break; + case 0x01: /* EQ gain1 */ + if(opt_eq_control) { + if(val > 0x4C) {val = 0x4C;} + else if(val < 0x34) {val = 0x34;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ gain1 (%d dB)", val - 0x40); + reverb->multi_eq_xg.gain1 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x02: /* EQ frequency1 */ + if(opt_eq_control) { + if(val > 60) {val = 60;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ frequency1 (%d Hz)", (int32_t)eq_freq_table_xg[val]); + reverb->multi_eq_xg.freq1 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x03: /* EQ Q1 */ + if(opt_eq_control) { + //printMessage(CMSG_INFO,VERB_NOISY,"EQ Q1 (%f)", (double)val / 10.0); + reverb->multi_eq_xg.q1 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x04: /* EQ shape1 */ + if(opt_eq_control) { + //printMessage(CMSG_INFO,VERB_NOISY,"EQ shape1 (%d)", val); + reverb->multi_eq_xg.shape1 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x05: /* EQ gain2 */ + if(opt_eq_control) { + if(val > 0x4C) {val = 0x4C;} + else if(val < 0x34) {val = 0x34;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ gain2 (%d dB)", val - 0x40); + reverb->multi_eq_xg.gain2 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x06: /* EQ frequency2 */ + if(opt_eq_control) { + if(val > 60) {val = 60;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ frequency2 (%d Hz)", (int32_t)eq_freq_table_xg[val]); + reverb->multi_eq_xg.freq2 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x07: /* EQ Q2 */ + if(opt_eq_control) { + //printMessage(CMSG_INFO,VERB_NOISY,"EQ Q2 (%f)", (double)val / 10.0); + reverb->multi_eq_xg.q2 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x09: /* EQ gain3 */ + if(opt_eq_control) { + if(val > 0x4C) {val = 0x4C;} + else if(val < 0x34) {val = 0x34;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ gain3 (%d dB)", val - 0x40); + reverb->multi_eq_xg.gain3 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x0A: /* EQ frequency3 */ + if(opt_eq_control) { + if(val > 60) {val = 60;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ frequency3 (%d Hz)", (int32_t)eq_freq_table_xg[val]); + reverb->multi_eq_xg.freq3 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x0B: /* EQ Q3 */ + if(opt_eq_control) { + //printMessage(CMSG_INFO,VERB_NOISY,"EQ Q3 (%f)", (double)val / 10.0); + reverb->multi_eq_xg.q3 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x0D: /* EQ gain4 */ + if(opt_eq_control) { + if(val > 0x4C) {val = 0x4C;} + else if(val < 0x34) {val = 0x34;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ gain4 (%d dB)", val - 0x40); + reverb->multi_eq_xg.gain4 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x0E: /* EQ frequency4 */ + if(opt_eq_control) { + if(val > 60) {val = 60;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ frequency4 (%d Hz)", (int32_t)eq_freq_table_xg[val]); + reverb->multi_eq_xg.freq4 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x0F: /* EQ Q4 */ + if(opt_eq_control) { + //printMessage(CMSG_INFO,VERB_NOISY,"EQ Q4 (%f)", (double)val / 10.0); + reverb->multi_eq_xg.q4 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x11: /* EQ gain5 */ + if(opt_eq_control) { + if(val > 0x4C) {val = 0x4C;} + else if(val < 0x34) {val = 0x34;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ gain5 (%d dB)", val - 0x40); + reverb->multi_eq_xg.gain5 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x12: /* EQ frequency5 */ + if(opt_eq_control) { + if(val > 60) {val = 60;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ frequency5 (%d Hz)", (int32_t)eq_freq_table_xg[val]); + reverb->multi_eq_xg.freq5 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x13: /* EQ Q5 */ + if(opt_eq_control) { + //printMessage(CMSG_INFO,VERB_NOISY,"EQ Q5 (%f)", (double)val / 10.0); + reverb->multi_eq_xg.q5 = val; + reverb->recompute_multi_eq_xg(); + } + break; + case 0x14: /* EQ shape5 */ + if(opt_eq_control) { + //printMessage(CMSG_INFO,VERB_NOISY,"EQ shape5 (%d)", val); + reverb->multi_eq_xg.shape5 = val; + reverb->recompute_multi_eq_xg(); + } + break; + } + } else if (note == 8 && msb == 0) { /* Multi Part */ + switch(b) + { + case 0x99: /* Rcv CHANNEL, remapped from 0x04 */ + reset_controllers(ch); + all_notes_off(ch); + if (val == 0x7f) + remove_channel_layer(ch); + else { + if((ch < REDUCE_CHANNELS) != (val < REDUCE_CHANNELS)) { + channel[ch].port_select = ch < REDUCE_CHANNELS ? 1 : 0; + } + if((ch % REDUCE_CHANNELS) != (val % REDUCE_CHANNELS)) { + add_channel_layer(ch, val); + } + } + break; + case 0x06: /* Same Note Number Key On Assign */ + if(val == 0) { + channel[ch].assign_mode = 0; + //printMessage(CMSG_INFO,VERB_NOISY,"Same Note Number Key On Assign: Single (CH:%d)",ch); + } else if(val == 1) { + channel[ch].assign_mode = 2; + //printMessage(CMSG_INFO,VERB_NOISY,"Same Note Number Key On Assign: Multi (CH:%d)",ch); + } else if(val == 2) { + //printMessage(CMSG_INFO,VERB_NOISY,"Same Note Number Key On Assign: Inst is not supported. (CH:%d)",ch); + } + break; + case 0x11: /* Dry Level */ + channel[ch].dry_level = val; + //printMessage(CMSG_INFO,VERB_NOISY,"Dry Level (CH:%d VAL:%d)", ch, val); + break; + } + } else if ((note & 0xF0) == 0x30) { /* Drum Setup */ + note = msb; + switch(b) + { + case 0x0E: /* EG Decay1 */ + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + //printMessage(CMSG_INFO,VERB_NOISY,"Drum Instrument EG Decay1 (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_envelope_rate[EG_DECAY1] = val; + break; + case 0x0F: /* EG Decay2 */ + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + //printMessage(CMSG_INFO,VERB_NOISY,"Drum Instrument EG Decay2 (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_envelope_rate[EG_DECAY2] = val; + break; + default: + break; + } + } + return; + } +} + +/*! convert GS NRPN to vibrato rate ratio. */ +/* from 0 to 3.0. */ +double Player::gs_cnv_vib_rate(int rate) +{ + double ratio; + + if(rate == 0) { + ratio = 1.6 / 100.0; + } else if(rate == 64) { + ratio = 1.0; + } else if(rate <= 100) { + ratio = (double)rate * 1.6 / 100.0; + } else { + ratio = (double)(rate - 101) * 1.33 / 26.0 + 1.67; + } + return (1.0 / ratio); +} + +/*! convert GS NRPN to vibrato depth. */ +/* from -9.6 cents to +9.45 cents. */ +int32_t Player::gs_cnv_vib_depth(int depth) +{ + double cent; + cent = (double)(depth - 64) * 0.15; + + return (int32_t)(cent * 256.0 / 400.0); +} + +/*! convert GS NRPN to vibrato delay. */ +/* from 0 ms to 5074 ms. */ +int32_t Player::gs_cnv_vib_delay(int delay) +{ + double ms; + ms = 0.2092 * exp(0.0795 * (double)delay); + if(delay == 0) {ms = 0;} + return (int32_t)((double)playback_rate * ms * 0.001); +} + +int Player::last_rpn_addr(int ch) +{ + int lsb, msb, addr, i; + const struct rpn_tag_map_t *addrmap; + struct rpn_tag_map_t { + int addr, mask, tag; + }; + static const struct rpn_tag_map_t nrpn_addr_map[] = { + {0x0108, 0xffff, NRPN_ADDR_0108}, + {0x0109, 0xffff, NRPN_ADDR_0109}, + {0x010a, 0xffff, NRPN_ADDR_010A}, + {0x0120, 0xffff, NRPN_ADDR_0120}, + {0x0121, 0xffff, NRPN_ADDR_0121}, + {0x0130, 0xffff, NRPN_ADDR_0130}, + {0x0131, 0xffff, NRPN_ADDR_0131}, + {0x0134, 0xffff, NRPN_ADDR_0134}, + {0x0135, 0xffff, NRPN_ADDR_0135}, + {0x0163, 0xffff, NRPN_ADDR_0163}, + {0x0164, 0xffff, NRPN_ADDR_0164}, + {0x0166, 0xffff, NRPN_ADDR_0166}, + {0x1400, 0xff00, NRPN_ADDR_1400}, + {0x1500, 0xff00, NRPN_ADDR_1500}, + {0x1600, 0xff00, NRPN_ADDR_1600}, + {0x1700, 0xff00, NRPN_ADDR_1700}, + {0x1800, 0xff00, NRPN_ADDR_1800}, + {0x1900, 0xff00, NRPN_ADDR_1900}, + {0x1a00, 0xff00, NRPN_ADDR_1A00}, + {0x1c00, 0xff00, NRPN_ADDR_1C00}, + {0x1d00, 0xff00, NRPN_ADDR_1D00}, + {0x1e00, 0xff00, NRPN_ADDR_1E00}, + {0x1f00, 0xff00, NRPN_ADDR_1F00}, + {0x3000, 0xff00, NRPN_ADDR_3000}, + {0x3100, 0xff00, NRPN_ADDR_3100}, + {0x3400, 0xff00, NRPN_ADDR_3400}, + {0x3500, 0xff00, NRPN_ADDR_3500}, + {-1, -1, 0} + }; + static const struct rpn_tag_map_t rpn_addr_map[] = { + {0x0000, 0xffff, RPN_ADDR_0000}, + {0x0001, 0xffff, RPN_ADDR_0001}, + {0x0002, 0xffff, RPN_ADDR_0002}, + {0x0003, 0xffff, RPN_ADDR_0003}, + {0x0004, 0xffff, RPN_ADDR_0004}, + {0x0005, 0xffff, RPN_ADDR_0005}, + {0x7f7f, 0xffff, RPN_ADDR_7F7F}, + {0xffff, 0xffff, RPN_ADDR_FFFF}, + {-1, -1} + }; + + if (channel[ch].nrpn == -1) + return -1; + lsb = channel[ch].lastlrpn; + msb = channel[ch].lastmrpn; + if (lsb == 0xff || msb == 0xff) + return -1; + addr = (msb << 8 | lsb); + if (channel[ch].nrpn) + addrmap = nrpn_addr_map; + else + addrmap = rpn_addr_map; + for (i = 0; addrmap[i].addr != -1; i++) + if (addrmap[i].addr == (addr & addrmap[i].mask)) + return addrmap[i].tag; + return -1; +} + +void Player::update_rpn_map(int ch, int addr, int update_now) +{ + int val, drumflag, i, note; + + val = channel[ch].rpnmap[addr]; + drumflag = 0; + switch (addr) { + case NRPN_ADDR_0108: /* Vibrato Rate */ + if (op_nrpn_vibrato) { + //printMessage(CMSG_INFO, VERB_NOISY, "Vibrato Rate (CH:%d VAL:%d)", ch, val - 64); + channel[ch].vibrato_ratio = gs_cnv_vib_rate(val); + } + if (update_now) + adjust_pitch(ch); + break; + case NRPN_ADDR_0109: /* Vibrato Depth */ + if (op_nrpn_vibrato) { + //printMessage(CMSG_INFO, VERB_NOISY, "Vibrato Depth (CH:%d VAL:%d)", ch, val - 64); + channel[ch].vibrato_depth = gs_cnv_vib_depth(val); + } + if (update_now) + adjust_pitch(ch); + break; + case NRPN_ADDR_010A: /* Vibrato Delay */ + if (op_nrpn_vibrato) { + //printMessage(CMSG_INFO,VERB_NOISY,"Vibrato Delay (CH:%d VAL:%d)", ch, val); + channel[ch].vibrato_delay = gs_cnv_vib_delay(val); + } + if (update_now) + adjust_pitch(ch); + break; + case NRPN_ADDR_0120: /* Filter Cutoff Frequency */ + if (timidity_lpf_def) { + //printMessage(CMSG_INFO, VERB_NOISY, "Filter Cutoff (CH:%d VAL:%d)", ch, val - 64); + channel[ch].param_cutoff_freq = val - 64; + } + break; + case NRPN_ADDR_0121: /* Filter Resonance */ + if (timidity_lpf_def) { + //printMessage(CMSG_INFO,VERB_NOISY,"Filter Resonance (CH:%d VAL:%d)", ch, val - 64); + channel[ch].param_resonance = val - 64; + } + break; + case NRPN_ADDR_0130: /* EQ BASS */ + if (opt_eq_control) { + //printMessage(CMSG_INFO,VERB_NOISY,"EQ BASS (CH:%d %.2f dB)", ch, 0.19 * (double)(val - 0x40)); + channel[ch].eq_xg.bass = val; + recompute_part_eq_xg(&(channel[ch].eq_xg)); + } + break; + case NRPN_ADDR_0131: /* EQ TREBLE */ + if (opt_eq_control) { + //printMessage(CMSG_INFO,VERB_NOISY,"EQ TREBLE (CH:%d %.2f dB)", ch, 0.19 * (double)(val - 0x40)); + channel[ch].eq_xg.treble = val; + recompute_part_eq_xg(&(channel[ch].eq_xg)); + } + break; + case NRPN_ADDR_0134: /* EQ BASS frequency */ + if (opt_eq_control) { + if(val < 4) {val = 4;} + else if(val > 40) {val = 40;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ BASS frequency (CH:%d %d Hz)", ch, (int32_t)eq_freq_table_xg[val]); + channel[ch].eq_xg.bass_freq = val; + recompute_part_eq_xg(&(channel[ch].eq_xg)); + } + break; + case NRPN_ADDR_0135: /* EQ TREBLE frequency */ + if (opt_eq_control) { + if(val < 28) {val = 28;} + else if(val > 58) {val = 58;} + //printMessage(CMSG_INFO,VERB_NOISY,"EQ TREBLE frequency (CH:%d %d Hz)", ch, (int32_t)eq_freq_table_xg[val]); + channel[ch].eq_xg.treble_freq = val; + recompute_part_eq_xg(&(channel[ch].eq_xg)); + } + break; + case NRPN_ADDR_0163: /* Attack Time */ + if (opt_tva_attack) {set_envelope_time(ch, val, EG_ATTACK);} + break; + case NRPN_ADDR_0164: /* EG Decay Time */ + if (opt_tva_decay) {set_envelope_time(ch, val, EG_DECAY);} + break; + case NRPN_ADDR_0166: /* EG Release Time */ + if (opt_tva_release) {set_envelope_time(ch, val, EG_RELEASE);} + break; + case NRPN_ADDR_1400: /* Drum Filter Cutoff (XG) */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + //printMessage(CMSG_INFO,VERB_NOISY,"Drum Instrument Filter Cutoff (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_cutoff_freq = val - 64; + break; + case NRPN_ADDR_1500: /* Drum Filter Resonance (XG) */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + //printMessage(CMSG_INFO,VERB_NOISY,"Drum Instrument Filter Resonance (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_resonance = val - 64; + break; + case NRPN_ADDR_1600: /* Drum EG Attack Time (XG) */ + drumflag = 1; + if (opt_tva_attack) { + val = val & 0x7f; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + val -= 64; + //printMessage(CMSG_INFO,VERB_NOISY,"Drum Instrument Attack Time (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_envelope_rate[EG_ATTACK] = val; + } + break; + case NRPN_ADDR_1700: /* Drum EG Decay Time (XG) */ + drumflag = 1; + if (opt_tva_decay) { + val = val & 0x7f; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + val -= 64; + //printMessage(CMSG_INFO,VERB_NOISY,"Drum Instrument Decay Time (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_envelope_rate[EG_DECAY1] = + channel[ch].drums[note]->drum_envelope_rate[EG_DECAY2] = val; + } + break; + case NRPN_ADDR_1800: /* Coarse Pitch of Drum (GS) */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + channel[ch].drums[note]->coarse = val - 64; + //printMessage(CMSG_INFO,VERB_NOISY,"Drum Instrument Pitch Coarse (CH:%d NOTE:%d VAL:%d)", ch, note, channel[ch].drums[note]->coarse); + channel[ch].pitchfactor = 0; + break; + case NRPN_ADDR_1900: /* Fine Pitch of Drum (XG) */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + channel[ch].drums[note]->fine = val - 64; + //printMessage(CMSG_INFO, VERB_NOISY, "Drum Instrument Pitch Fine (CH:%d NOTE:%d VAL:%d)", ch, note, channel[ch].drums[note]->fine); + channel[ch].pitchfactor = 0; + break; + case NRPN_ADDR_1A00: /* Level of Drum */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + //printMessage(CMSG_INFO,VERB_NOISY,"Drum Instrument TVA Level (CH:%d NOTE:%d VAL:%d)", ch, note, val); + channel[ch].drums[note]->drum_level = + calc_drum_tva_level(ch, note, val); + break; + case NRPN_ADDR_1C00: /* Panpot of Drum */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + if(val == 0) { + val = int_rand(128); + channel[ch].drums[note]->pan_random = 1; + } else + channel[ch].drums[note]->pan_random = 0; + channel[ch].drums[note]->drum_panning = val; + if (update_now && adjust_panning_immediately && ! channel[ch].pan_random) + adjust_drum_panning(ch, note); + break; + case NRPN_ADDR_1D00: /* Reverb Send Level of Drum */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + //printMessage(CMSG_INFO, VERB_NOISY, "Reverb Send Level of Drum (CH:%d NOTE:%d VALUE:%d)", ch, note, val); + if (channel[ch].drums[note]->reverb_level != val) { + channel[ch].drum_effect_flag = 0; + } + channel[ch].drums[note]->reverb_level = val; + break; + case NRPN_ADDR_1E00: /* Chorus Send Level of Drum */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + //printMessage(CMSG_INFO, VERB_NOISY, "Chorus Send Level of Drum (CH:%d NOTE:%d VALUE:%d)", ch, note, val); + if (channel[ch].drums[note]->chorus_level != val) { + channel[ch].drum_effect_flag = 0; + } + channel[ch].drums[note]->chorus_level = val; + + break; + case NRPN_ADDR_1F00: /* Variation Send Level of Drum */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Send Level of Drum (CH:%d NOTE:%d VALUE:%d)", ch, note, val); + if (channel[ch].drums[note]->delay_level != val) { + channel[ch].drum_effect_flag = 0; + } + channel[ch].drums[note]->delay_level = val; + break; + case NRPN_ADDR_3000: /* Drum EQ BASS */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + break; + case NRPN_ADDR_3100: /* Drum EQ TREBLE */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + break; + case NRPN_ADDR_3400: /* Drum EQ BASS frequency */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + break; + case NRPN_ADDR_3500: /* Drum EQ TREBLE frequency */ + drumflag = 1; + note = channel[ch].lastlrpn; + if (channel[ch].drums[note] == NULL) + play_midi_setup_drums(ch, note); + break; + case RPN_ADDR_0000: /* Pitch bend sensitivity */ + //printMessage(CMSG_INFO, VERB_DEBUG, "Pitch Bend Sensitivity (CH:%d VALUE:%d)", ch, val); + /* for mod2mid.c, arpeggio */ + if (channel[ch].rpnmap[RPN_ADDR_0000] > 24) + channel[ch].rpnmap[RPN_ADDR_0000] = 24; + channel[ch].pitchfactor = 0; + break; + case RPN_ADDR_0001: /* Master Fine Tuning */ + //printMessage(CMSG_INFO, VERB_DEBUG, "Master Fine Tuning (CH:%d VALUE:%d)", ch, val); + channel[ch].pitchfactor = 0; + break; + case RPN_ADDR_0002: /* Master Coarse Tuning */ + //printMessage(CMSG_INFO, VERB_DEBUG, "Master Coarse Tuning (CH:%d VALUE:%d)", ch, val); + channel[ch].pitchfactor = 0; + break; + case RPN_ADDR_0003: /* Tuning Program Select */ + //printMessage(CMSG_INFO, VERB_DEBUG, "Tuning Program Select (CH:%d VALUE:%d)", ch, val); + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + case RPN_ADDR_0004: /* Tuning Bank Select */ + //printMessage(CMSG_INFO, VERB_DEBUG, "Tuning Bank Select (CH:%d VALUE:%d)", ch, val); + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + case RPN_ADDR_0005: /* GM2: Modulation Depth Range */ + channel[ch].mod.lfo1_pitch_depth = (((int32_t)channel[ch].rpnmap[RPN_ADDR_0005] << 7) | channel[ch].rpnmap_lsb[RPN_ADDR_0005]) * 100 / 128; + //printMessage(CMSG_INFO,VERB_NOISY,"Modulation Depth Range (CH:%d VALUE:%d)", ch, channel[ch].rpnmap[RPN_ADDR_0005]); + break; + case RPN_ADDR_7F7F: /* RPN reset */ + channel[ch].rpn_7f7f_flag = 1; + break; + case RPN_ADDR_FFFF: /* RPN initialize */ + /* All reset to defaults */ + channel[ch].rpn_7f7f_flag = 0; + memset(channel[ch].rpnmap, 0, sizeof(channel[ch].rpnmap)); + channel[ch].lastlrpn = channel[ch].lastmrpn = 0; + channel[ch].nrpn = 0; + channel[ch].rpnmap[RPN_ADDR_0000] = 2; + channel[ch].rpnmap[RPN_ADDR_0001] = 0x40; + channel[ch].rpnmap[RPN_ADDR_0002] = 0x40; + channel[ch].rpnmap_lsb[RPN_ADDR_0005] = 0x40; + channel[ch].rpnmap[RPN_ADDR_0005] = 0; /* +- 50 cents */ + channel[ch].pitchfactor = 0; + break; + } + drumflag = 0; + if (drumflag && midi_drumpart_change(ch, 1)) { + midi_program_change(ch, channel[ch].program); + } +} + +void Player::voice_increment(int n) +{ + int i; + for(i = 0; i < n; i++) + { + if(voices == max_voices) + break; + voice[voices].status = VOICE_FREE; + voice[voices].temper_instant = 0; + voice[voices].chorus_link = voices; + voices++; + } +} + +void Player::voice_decrement(int n) +{ + int i, j, lowest; + int32_t lv, v; + + /* decrease voice */ + for(i = 0; i < n && voices > 0; i++) + { + voices--; + if(voice[voices].status == VOICE_FREE) + continue; /* found */ + + for(j = 0; j < voices; j++) + if(voice[j].status == VOICE_FREE) + break; + if(j != voices) + { + voice[j] = voice[voices]; + continue; /* found */ + } + + /* Look for the decaying note with the lowest volume */ + lv = 0x7FFFFFFF; + lowest = -1; + for(j = 0; j <= voices; j++) + { + if(voice[j].status & ~(VOICE_ON | VOICE_DIE)) + { + v = voice[j].left_mix; + if((voice[j].panned==PANNED_MYSTERY) && + (voice[j].right_mix > v)) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + + if(lowest != -1) + { + cut_notes++; + free_voice(lowest); + voice[lowest] = voice[voices]; + } + else + lost_notes++; + } + if(upper_voices > voices) + upper_voices = voices; +} + +/* EAW -- do not throw away good notes, stop decrementing */ +void Player::voice_decrement_conservative(int n) +{ + int i, j, lowest, finalnv; + int32_t lv, v; + + /* decrease voice */ + finalnv = voices - n; + for(i = 1; i <= n && voices > 0; i++) + { + if(voice[voices-1].status == VOICE_FREE) { + voices--; + continue; /* found */ + } + + for(j = 0; j < finalnv; j++) + if(voice[j].status == VOICE_FREE) + break; + if(j != finalnv) + { + voice[j] = voice[voices-1]; + voices--; + continue; /* found */ + } + + /* Look for the decaying note with the lowest volume */ + lv = 0x7FFFFFFF; + lowest = -1; + for(j = 0; j < voices; j++) + { + if(voice[j].status & ~(VOICE_ON | VOICE_DIE) && + !(voice[j].sample->note_to_use && + ISDRUMCHANNEL(voice[j].channel))) + { + v = voice[j].left_mix; + if((voice[j].panned==PANNED_MYSTERY) && + (voice[j].right_mix > v)) + v = voice[j].right_mix; + if(v < lv) + { + lv = v; + lowest = j; + } + } + } + + if(lowest != -1) + { + voices--; + cut_notes++; + free_voice(lowest); + voice[lowest] = voice[voices]; + } + else break; + } + if(upper_voices > voices) + upper_voices = voices; +} + +void Player::mix_signal(int32_t *dest, int32_t *src, int32_t count) +{ + int32_t i; + for (i = 0; i < count; i++) { + dest[i] += src[i]; + } +} + +int Player::is_insertion_effect_xg(int ch) +{ + int i; + for (i = 0; i < XG_INSERTION_EFFECT_NUM; i++) { + if (reverb->insertion_effect_xg[i].part == ch) { + return 1; + } + } + for (i = 0; i < XG_VARIATION_EFFECT_NUM; i++) { + if (reverb->variation_effect_xg[i].connection == XG_CONN_INSERTION + && reverb->variation_effect_xg[i].part == ch) { + return 1; + } + } + return 0; +} + +/* do_compute_data_midi() with DSP Effect */ +void Player::do_compute_data(int32_t count) +{ + int i, j, uv, stereo, n, ch, note; + int32_t *vpblist[MAX_CHANNELS]; + int channel_effect, channel_reverb, channel_chorus, channel_delay, channel_eq; + int32_t cnt = count * 2, rev_max_delay_out; + struct DrumPartEffect *de; + + stereo = true; + n = count * ((stereo) ? 8 : 4); /* in bytes */ + + memset(buffer_pointer, 0, n); + memset(insertion_effect_buffer, 0, n); + + if (timidity_reverb == 3) { + rev_max_delay_out = 0x7fffffff; /* disable */ + } else { + rev_max_delay_out = REVERB_MAX_DELAY_OUT; + } + + /* are effects valid? / don't supported in mono */ + channel_reverb = (stereo && (timidity_reverb == 1 + || timidity_reverb == 3 + || (timidity_reverb < 0 && timidity_reverb & 0x80))); + channel_chorus = (stereo && timidity_chorus && !timidity_surround_chorus); + channel_delay = 0; + + /* is EQ valid? */ + channel_eq = 0; + + channel_effect = (stereo && (channel_reverb || channel_chorus + || channel_delay || channel_eq || opt_insertion_effect)); + + uv = upper_voices; + for(i = 0; i < uv; i++) { + if(voice[i].status != VOICE_FREE) { + channel[voice[i].channel].lasttime = current_sample + count; + } + } + + /* appropriate buffers for channels */ + if(channel_effect) { + int buf_index = 0; + + if(reverb_buffer == NULL) { /* allocating buffer for channel effect */ + reverb_buffer = (char *)safe_malloc(MAX_CHANNELS * AUDIO_BUFFER_SIZE * 8); + } + + for(i = 0; i < MAX_CHANNELS; i++) { + if(opt_insertion_effect && channel[i].insertion_effect) { + vpblist[i] = insertion_effect_buffer; + } else if(channel[i].eq_gs || (get_reverb_level(i) != DEFAULT_REVERB_SEND_LEVEL + && current_sample - channel[i].lasttime < rev_max_delay_out) + || channel[i].chorus_level > 0 || channel[i].delay_level > 0 + || channel[i].eq_xg.valid + || channel[i].dry_level != 127 + || (timidity_drum_effect && ISDRUMCHANNEL(i)) + || is_insertion_effect_xg(i)) { + vpblist[i] = (int32_t*)(reverb_buffer + buf_index); + buf_index += n; + } else { + vpblist[i] = buffer_pointer; + } + /* clear buffers of drum-part effect */ + if (timidity_drum_effect && ISDRUMCHANNEL(i)) { + for (j = 0; j < channel[i].drum_effect_num; j++) { + if (channel[i].drum_effect[j].buf != NULL) { + memset(channel[i].drum_effect[j].buf, 0, n); + } + } + } + } + + if(buf_index) {memset(reverb_buffer, 0, buf_index);} + } + + for (i = 0; i < uv; i++) { + if (voice[i].status != VOICE_FREE) { + int32_t *vpb = NULL; + int8_t flag; + + if (channel_effect) { + flag = 0; + ch = voice[i].channel; + if (timidity_drum_effect && ISDRUMCHANNEL(ch)) { + make_drum_effect(ch); + note = voice[i].note; + for (j = 0; j < channel[ch].drum_effect_num; j++) { + if (channel[ch].drum_effect[j].note == note) { + vpb = channel[ch].drum_effect[j].buf; + flag = 1; + } + } + if (flag == 0) {vpb = vpblist[ch];} + } else { + vpb = vpblist[ch]; + } + } else { + vpb = buffer_pointer; + } + + if(!IS_SET_CHANNELMASK(channel_mute, voice[i].channel)) { + mixer->mix_voice(vpb, i, count); + } else { + free_voice(i); + } + + if(voice[i].timeout == 1 && voice[i].timeout < current_sample) { + free_voice(i); + } + } + } + + while(uv > 0 && voice[uv - 1].status == VOICE_FREE) {uv--;} + upper_voices = uv; + + if(play_system_mode == XG_SYSTEM_MODE && channel_effect) { /* XG */ + if (opt_insertion_effect) { /* insertion effect */ + for (i = 0; i < XG_INSERTION_EFFECT_NUM; i++) { + if (reverb->insertion_effect_xg[i].part <= MAX_CHANNELS) { + reverb->do_insertion_effect_xg(vpblist[reverb->insertion_effect_xg[i].part], cnt, &reverb->insertion_effect_xg[i]); + } + } + for (i = 0; i < XG_VARIATION_EFFECT_NUM; i++) { + if (reverb->variation_effect_xg[i].part <= MAX_CHANNELS) { + reverb->do_insertion_effect_xg(vpblist[reverb->variation_effect_xg[i].part], cnt, &reverb->variation_effect_xg[i]); + } + } + } + for(i = 0; i < MAX_CHANNELS; i++) { /* system effects */ + int32_t *p; + p = vpblist[i]; + if(p != buffer_pointer) { + if (timidity_drum_effect && ISDRUMCHANNEL(i)) { + for (j = 0; j < channel[i].drum_effect_num; j++) { + de = &(channel[i].drum_effect[j]); + if (de->reverb_send > 0) { + reverb->set_ch_reverb(de->buf, cnt, de->reverb_send); + } + if (de->chorus_send > 0) { + reverb->set_ch_chorus(de->buf, cnt, de->chorus_send); + } + if (de->delay_send > 0) { + reverb->set_ch_delay(de->buf, cnt, de->delay_send); + } + mix_signal(p, de->buf, cnt); + } + } else { + if(channel_eq && channel[i].eq_xg.valid) { + reverb->do_ch_eq_xg(p, cnt, &(channel[i].eq_xg)); + } + if(channel_chorus && channel[i].chorus_level > 0) { + reverb->set_ch_chorus(p, cnt, channel[i].chorus_level); + } + if(channel_delay && channel[i].delay_level > 0) { + reverb->set_ch_delay(p, cnt, channel[i].delay_level); + } + if(channel_reverb && channel[i].reverb_level > 0 + && current_sample - channel[i].lasttime < rev_max_delay_out) { + reverb->set_ch_reverb(p, cnt, channel[i].reverb_level); + } + } + if(channel[i].dry_level == 127) { + reverb->set_dry_signal(p, cnt); + } else { + reverb->set_dry_signal_xg(p, cnt, channel[i].dry_level); + } + } + } + + if(channel_reverb) { + reverb->set_ch_reverb(buffer_pointer, cnt, DEFAULT_REVERB_SEND_LEVEL); + } + reverb->set_dry_signal(buffer_pointer, cnt); + + /* mixing signal and applying system effects */ + reverb->mix_dry_signal(buffer_pointer, cnt); + if(channel_delay) { reverb->do_variation_effect1_xg(buffer_pointer, cnt);} + if(channel_chorus) { reverb->do_ch_chorus_xg(buffer_pointer, cnt);} + if(channel_reverb) { reverb->do_ch_reverb(buffer_pointer, cnt);} + if(reverb->multi_eq_xg.valid) { reverb->do_multi_eq_xg(buffer_pointer, cnt);} + } else if(channel_effect) { /* GM & GS */ + if(opt_insertion_effect) { /* insertion effect */ + /* applying insertion effect */ + reverb->do_insertion_effect_gs(insertion_effect_buffer, cnt); + /* sending insertion effect voice to channel effect */ + reverb->set_ch_chorus(insertion_effect_buffer, cnt, reverb->insertion_effect_gs.send_chorus); + reverb->set_ch_delay(insertion_effect_buffer, cnt, reverb->insertion_effect_gs.send_delay); + reverb->set_ch_reverb(insertion_effect_buffer, cnt, reverb->insertion_effect_gs.send_reverb); + if(reverb->insertion_effect_gs.send_eq_switch && channel_eq) { + reverb->set_ch_eq_gs(insertion_effect_buffer, cnt); + } else { + reverb->set_dry_signal(insertion_effect_buffer, cnt); + } + } + + for(i = 0; i < MAX_CHANNELS; i++) { /* system effects */ + int32_t *p; + p = vpblist[i]; + if(p != buffer_pointer && p != insertion_effect_buffer) { + if (timidity_drum_effect && ISDRUMCHANNEL(i)) { + for (j = 0; j < channel[i].drum_effect_num; j++) { + de = &(channel[i].drum_effect[j]); + if (de->reverb_send > 0) { + reverb->set_ch_reverb(de->buf, cnt, de->reverb_send); + } + if (de->chorus_send > 0) { + reverb->set_ch_chorus(de->buf, cnt, de->chorus_send); + } + if (de->delay_send > 0) { + reverb->set_ch_delay(de->buf, cnt, de->delay_send); + } + mix_signal(p, de->buf, cnt); + } + } else { + if(channel_chorus && channel[i].chorus_level > 0) { + reverb->set_ch_chorus(p, cnt, channel[i].chorus_level); + } + if(channel_delay && channel[i].delay_level > 0) { + reverb->set_ch_delay(p, cnt, channel[i].delay_level); + } + if(channel_reverb && channel[i].reverb_level > 0 + && current_sample - channel[i].lasttime < rev_max_delay_out) { + reverb->set_ch_reverb(p, cnt, channel[i].reverb_level); + } + } + if(channel_eq && channel[i].eq_gs) { + reverb->set_ch_eq_gs(p, cnt); + } else { + reverb->set_dry_signal(p, cnt); + } + } + } + + if(channel_reverb) { + reverb->set_ch_reverb(buffer_pointer, cnt, DEFAULT_REVERB_SEND_LEVEL); + } + reverb->set_dry_signal(buffer_pointer, cnt); + + /* mixing signal and applying system effects */ + reverb->mix_dry_signal(buffer_pointer, cnt); + if(channel_eq) { reverb->do_ch_eq_gs(buffer_pointer, cnt);} + if(channel_chorus) { reverb->do_ch_chorus(buffer_pointer, cnt);} + if(channel_delay) { reverb->do_ch_delay(buffer_pointer, cnt);} + if(channel_reverb) { reverb->do_ch_reverb(buffer_pointer, cnt);} + } + + current_sample += count; +} + +int Player::compute_data(float *buffer, int32_t count) +{ + if (count == 0) return RC_OK; + + std::lock_guard lock(ConfigMutex); + + if (last_reverb_setting != timidity_reverb) + { + // If the reverb mode has changed some buffers need to be reallocated before doing any sound generation. + reverb->free_effect_buffers(); + reverb->init_reverb(); + last_reverb_setting = timidity_reverb; + } + + buffer_pointer = common_buffer; + computed_samples += count; + + while (count > 0) + { + int process = std::min(count, AUDIO_BUFFER_SIZE); + do_compute_data(process); + count -= process; + + effect->do_effect(common_buffer, process); + // pass to caller + for (int i = 0; i < process*2; i++) + { + *buffer++ = (common_buffer[i])*(5.f / 0x80000000u); + } + } + return RC_OK; +} + +void Player::update_modulation_wheel(int ch) +{ + int i, uv = upper_voices; + channel[ch].pitchfactor = 0; + for(i = 0; i < uv; i++) + if(voice[i].status != VOICE_FREE && voice[i].channel == ch) + { + /* Set/Reset mod-wheel */ + voice[i].vibrato_control_counter = voice[i].vibrato_phase = 0; + recompute_amp(i); + mixer->apply_envelope_to_amp(i); + recompute_freq(i); + recompute_voice_filter(i); + } +} + +void Player::drop_portamento(int ch) +{ + int i, uv = upper_voices; + + channel[ch].porta_control_ratio = 0; + for(i = 0; i < uv; i++) + if(voice[i].status != VOICE_FREE && + voice[i].channel == ch && + voice[i].porta_control_ratio) + { + voice[i].porta_control_ratio = 0; + recompute_freq(i); + } + channel[ch].last_note_fine = -1; +} + +void Player::update_portamento_controls(int ch) +{ + if(!channel[ch].portamento || + (channel[ch].portamento_time_msb | channel[ch].portamento_time_lsb) + == 0) + drop_portamento(ch); + else + { + double mt, dc; + int d; + + mt = midi_time_table[channel[ch].portamento_time_msb & 0x7F] * + midi_time_table2[channel[ch].portamento_time_lsb & 0x7F] * + PORTAMENTO_TIME_TUNING; + dc = playback_rate * mt; + d = (int)(1.0 / (mt * PORTAMENTO_CONTROL_RATIO)); + d++; + channel[ch].porta_control_ratio = (int)(d * dc + 0.5); + channel[ch].porta_dpb = d; + } +} + +void Player::update_portamento_time(int ch) +{ + int i, uv = upper_voices; + int dpb; + int32_t ratio; + + update_portamento_controls(ch); + dpb = channel[ch].porta_dpb; + ratio = channel[ch].porta_control_ratio; + + for(i = 0; i < uv; i++) + { + if(voice[i].status != VOICE_FREE && + voice[i].channel == ch && + voice[i].porta_control_ratio) + { + voice[i].porta_control_ratio = ratio; + voice[i].porta_dpb = dpb; + recompute_freq(i); + } + } +} + +void Player::update_legato_controls(int ch) +{ + double mt, dc; + int d; + + mt = 0.06250 * PORTAMENTO_TIME_TUNING * 0.3; + dc = playback_rate * mt; + d = (int)(1.0 / (mt * PORTAMENTO_CONTROL_RATIO)); + d++; + channel[ch].porta_control_ratio = (int)(d * dc + 0.5); + channel[ch].porta_dpb = d; +} + +int Player::play_event(MidiEvent *ev) +{ + int32_t i, j; + int k, l, ch, orig_ch, port_ch, offset, layered; + + current_event = ev; + +#ifndef SUPPRESS_CHANNEL_LAYER + orig_ch = ev->channel; + layered = !IS_SYSEX_EVENT_TYPE(ev); + for (k = 0; k < MAX_CHANNELS; k += 16) { + port_ch = (orig_ch + k) % MAX_CHANNELS; + offset = port_ch & ~0xf; + for (l = offset; l < offset + 16; l++) { + if (!layered && (k || l != offset)) + continue; + if (layered) { + if (!IS_SET_CHANNELMASK(channel[l].channel_layer, port_ch) + || channel[l].port_select != (orig_ch >> 4)) + continue; + ev->channel = l; + } +#endif + ch = ev->channel; + + switch (ev->type) + { + /* MIDI Events */ + case ME_NOTEOFF: + note_off(ev); + break; + + case ME_NOTEON: + note_on(ev); + break; + + case ME_KEYPRESSURE: + adjust_pressure(ev); + break; + + case ME_PROGRAM: + midi_program_change(ch, ev->a); + break; + + case ME_CHANNEL_PRESSURE: + adjust_channel_pressure(ev); + break; + + case ME_PITCHWHEEL: + channel[ch].pitchbend = ev->a + ev->b * 128; + channel[ch].pitchfactor = 0; + /* Adjust pitch for notes already playing */ + adjust_pitch(ch); + break; + + /* Controls */ + case ME_TONE_BANK_MSB: + channel[ch].bank_msb = ev->a; + break; + + case ME_TONE_BANK_LSB: + channel[ch].bank_lsb = ev->a; + break; + + case ME_MODULATION_WHEEL: + channel[ch].mod.val = ev->a; + update_modulation_wheel(ch); + break; + + case ME_MAINVOLUME: + channel[ch].volume = ev->a; + adjust_volume(ch); + break; + + case ME_PAN: + channel[ch].panning = ev->a; + channel[ch].pan_random = 0; + if (adjust_panning_immediately && !channel[ch].pan_random) + adjust_panning(ch); + break; + + case ME_EXPRESSION: + channel[ch].expression = ev->a; + adjust_volume(ch); + break; + + case ME_SUSTAIN: + if (channel[ch].sustain == 0 && ev->a >= 64) { + update_redamper_controls(ch); + } + channel[ch].sustain = ev->a; + if (channel[ch].damper_mode == 0) { /* half-damper is not allowed. */ + if (channel[ch].sustain >= 64) { channel[ch].sustain = 127; } + else { channel[ch].sustain = 0; } + } + if (channel[ch].sustain == 0 && channel[ch].sostenuto == 0) + drop_sustain(ch); + break; + + case ME_SOSTENUTO: + channel[ch].sostenuto = (ev->a >= 64); + if (channel[ch].sustain == 0 && channel[ch].sostenuto == 0) + drop_sustain(ch); + else { update_sostenuto_controls(ch); } + break; + + case ME_LEGATO_FOOTSWITCH: + channel[ch].legato = (ev->a >= 64); + break; + + case ME_HOLD2: + break; + + case ME_BREATH: + break; + + case ME_FOOT: + break; + + case ME_BALANCE: + break; + + case ME_PORTAMENTO_TIME_MSB: + channel[ch].portamento_time_msb = ev->a; + update_portamento_time(ch); + break; + + case ME_PORTAMENTO_TIME_LSB: + channel[ch].portamento_time_lsb = ev->a; + update_portamento_time(ch); + break; + + case ME_PORTAMENTO: + channel[ch].portamento = (ev->a >= 64); + if (!channel[ch].portamento) + drop_portamento(ch); + break; + + case ME_SOFT_PEDAL: + if (timidity_lpf_def) { + channel[ch].soft_pedal = ev->a; + //printMessage(CMSG_INFO,VERB_NOISY,"Soft Pedal (CH:%d VAL:%d)", ch, channel[ch].soft_pedal); + } + break; + + case ME_HARMONIC_CONTENT: + if (timidity_lpf_def) { + channel[ch].param_resonance = ev->a - 64; + //printMessage(CMSG_INFO,VERB_NOISY,"Harmonic Content (CH:%d VAL:%d)", ch, channel[ch].param_resonance); + } + break; + + case ME_BRIGHTNESS: + if (timidity_lpf_def) { + channel[ch].param_cutoff_freq = ev->a - 64; + //printMessage(CMSG_INFO,VERB_NOISY,"Brightness (CH:%d VAL:%d)", ch, channel[ch].param_cutoff_freq); + } + break; + + case ME_DATA_ENTRY_MSB: + if (channel[ch].rpn_7f7f_flag) /* disable */ + break; + if ((i = last_rpn_addr(ch)) >= 0) + { + channel[ch].rpnmap[i] = ev->a; + update_rpn_map(ch, i, 1); + } + break; + + case ME_DATA_ENTRY_LSB: + if (channel[ch].rpn_7f7f_flag) /* disable */ + break; + if ((i = last_rpn_addr(ch)) >= 0) + { + channel[ch].rpnmap_lsb[i] = ev->a; + } + break; + + case ME_REVERB_EFFECT: + if (timidity_reverb) { + if (ISDRUMCHANNEL(ch) && get_reverb_level(ch) != ev->a) { channel[ch].drum_effect_flag = 0; } + set_reverb_level(ch, ev->a); + } + break; + + case ME_CHORUS_EFFECT: + if (timidity_chorus) + { + if (timidity_chorus == 1) { + if (ISDRUMCHANNEL(ch) && channel[ch].chorus_level != ev->a) { channel[ch].drum_effect_flag = 0; } + channel[ch].chorus_level = ev->a; + } + else { + channel[ch].chorus_level = -timidity_chorus; + } + if (ev->a) { + //printMessage(CMSG_INFO,VERB_NOISY,"Chorus Send (CH:%d LEVEL:%d)", ch, ev->a); + } + } + break; + + case ME_TREMOLO_EFFECT: + //printMessage(CMSG_INFO,VERB_NOISY,"Tremolo Send (CH:%d LEVEL:%d)", ch, ev->a); + break; + + case ME_CELESTE_EFFECT: + if (opt_delay_control) { + if (ISDRUMCHANNEL(ch) && channel[ch].delay_level != ev->a) { channel[ch].drum_effect_flag = 0; } + channel[ch].delay_level = ev->a; + if (play_system_mode == XG_SYSTEM_MODE) { + //printMessage(CMSG_INFO,VERB_NOISY,"Variation Send (CH:%d LEVEL:%d)", ch, ev->a); + } + else { + //printMessage(CMSG_INFO,VERB_NOISY,"Delay Send (CH:%d LEVEL:%d)", ch, ev->a); + } + } + break; + + case ME_ATTACK_TIME: + if (!opt_tva_attack) { break; } + set_envelope_time(ch, ev->a, EG_ATTACK); + break; + + case ME_RELEASE_TIME: + if (!opt_tva_release) { break; } + set_envelope_time(ch, ev->a, EG_RELEASE); + break; + + case ME_PHASER_EFFECT: + //printMessage(CMSG_INFO,VERB_NOISY,"Phaser Send (CH:%d LEVEL:%d)", ch, ev->a); + break; + + case ME_RPN_INC: + if (channel[ch].rpn_7f7f_flag) /* disable */ + break; + if ((i = last_rpn_addr(ch)) >= 0) + { + if (channel[ch].rpnmap[i] < 127) + channel[ch].rpnmap[i]++; + update_rpn_map(ch, i, 1); + } + break; + + case ME_RPN_DEC: + if (channel[ch].rpn_7f7f_flag) /* disable */ + break; + if ((i = last_rpn_addr(ch)) >= 0) + { + if (channel[ch].rpnmap[i] > 0) + channel[ch].rpnmap[i]--; + update_rpn_map(ch, i, 1); + } + break; + + case ME_NRPN_LSB: + channel[ch].lastlrpn = ev->a; + channel[ch].nrpn = 1; + break; + + case ME_NRPN_MSB: + channel[ch].lastmrpn = ev->a; + channel[ch].nrpn = 1; + break; + + case ME_RPN_LSB: + channel[ch].lastlrpn = ev->a; + channel[ch].nrpn = 0; + break; + + case ME_RPN_MSB: + channel[ch].lastmrpn = ev->a; + channel[ch].nrpn = 0; + break; + + case ME_ALL_SOUNDS_OFF: + all_sounds_off(ch); + break; + + case ME_RESET_CONTROLLERS: + reset_controllers(ch); + break; + + case ME_ALL_NOTES_OFF: + all_notes_off(ch); + break; + + case ME_MONO: + channel[ch].mono = 1; + all_notes_off(ch); + break; + + case ME_POLY: + channel[ch].mono = 0; + all_notes_off(ch); + break; + + /* TiMidity Extensionals */ + case ME_RANDOM_PAN: + channel[ch].panning = int_rand(128); + channel[ch].pan_random = 1; + if (adjust_panning_immediately && !channel[ch].pan_random) + adjust_panning(ch); + break; + + case ME_SET_PATCH: + i = channel[ch].special_sample = current_event->a; + instruments->setSpecialPatchOffset(i, 0); + break; + + case ME_TEMPO: + current_play_tempo = ch + ev->b * 256 + ev->a * 65536; + break; + + case ME_CHORUS_TEXT: + case ME_LYRIC: + case ME_MARKER: + case ME_INSERT_TEXT: + case ME_TEXT: + case ME_KARAOKE_LYRIC: + case ME_GSLCD: + break; + + case ME_MASTER_VOLUME: + master_volume_ratio = (int32_t)ev->a + 256 * (int32_t)ev->b; + adjust_master_volume(); + break; + + case ME_RESET: + change_system_mode(ev->a); + reset_midi(1); + break; + + case ME_PATCH_OFFS: + i = channel[ch].special_sample; + instruments->setSpecialPatchOffset(i, current_event->a | (256 * current_event->b)); + break; + + case ME_WRD: + break; + + case ME_SHERRY: + break; + + case ME_DRUMPART: + if (midi_drumpart_change(ch, current_event->a)) + { + /* Update bank information */ + midi_program_change(ch, channel[ch].program); + } + break; + + case ME_KEYSHIFT: + i = (int)current_event->a - 0x40; + if (i != channel[ch].key_shift) + { + all_sounds_off(ch); + channel[ch].key_shift = (int8_t)i; + } + break; + + case ME_KEYSIG: + if (opt_init_keysig != 8) + break; + current_keysig = current_event->a + current_event->b * 16; + if (opt_force_keysig != 8) { + i = current_keysig - ((current_keysig < 8) ? 0 : 16), j = 0; + while (i != opt_force_keysig && i != opt_force_keysig + 12) + i += (i > 0) ? -5 : 7, j++; + while (abs(j - note_key_offset) > 7) + j += (j > note_key_offset) ? -12 : 12; + if (abs(j - timidity_key_adjust) >= 12) + j += (j > timidity_key_adjust) ? -12 : 12; + note_key_offset = j; + kill_all_voices(); + } + i = current_keysig + ((current_keysig < 8) ? 7 : -9), j = 0; + while (i != 7) + i += (i < 7) ? 5 : -7, j++; + j += note_key_offset, j -= floor(j / 12.0) * 12; + current_freq_table = j; + break; + + case ME_MASTER_TUNING: + set_master_tuning((ev->b << 8) | ev->a); + adjust_all_pitch(); + break; + + case ME_SCALE_TUNING: + recache->resamp_cache_refer_alloff(ch, computed_samples); + channel[ch].scale_tuning[current_event->a] = current_event->b; + adjust_pitch(ch); + break; + + case ME_BULK_TUNING_DUMP: + set_single_note_tuning(ch, current_event->a, current_event->b, 0); + break; + + case ME_SINGLE_NOTE_TUNING: + set_single_note_tuning(ch, current_event->a, current_event->b, 1); + break; + + case ME_TEMPER_KEYSIG: + current_temper_keysig = (current_event->a + 8) % 32 - 8; + temper_adj = ((current_event->a + 8) & 0x20) ? 1 : 0; + i = current_temper_keysig + ((current_temper_keysig < 8) ? 7 : -9); + j = 0; + while (i != 7) + i += (i < 7) ? 5 : -7, j++; + j += note_key_offset, j -= floor(j / 12.0) * 12; + current_temper_freq_table = j; + if (current_event->b) + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + + case ME_TEMPER_TYPE: + channel[ch].temper_type = current_event->a; + if (temper_type_mute) { + if (temper_type_mute & (1 << (current_event->a + - ((current_event->a >= 0x40) ? 0x3c : 0)))) { + SET_CHANNELMASK(channel_mute, ch); + } + else { + UNSET_CHANNELMASK(channel_mute, ch); + } + } + if (current_event->b) + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + + case ME_MASTER_TEMPER_TYPE: + for (i = 0; i < MAX_CHANNELS; i++) { + channel[i].temper_type = current_event->a; + } + if (temper_type_mute) { + if (temper_type_mute & (1 << (current_event->a + - ((current_event->a >= 0x40) ? 0x3c : 0)))) { + FILL_CHANNELMASK(channel_mute); + } + else { + CLEAR_CHANNELMASK(channel_mute); + } + } + if (current_event->b) + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + + case ME_USER_TEMPER_ENTRY: + set_user_temper_entry(ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_LSB: + process_sysex_event(ME_SYSEX_LSB, ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_MSB: + process_sysex_event(ME_SYSEX_MSB, ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_GS_LSB: + process_sysex_event(ME_SYSEX_GS_LSB, ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_GS_MSB: + process_sysex_event(ME_SYSEX_GS_MSB, ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_XG_LSB: + process_sysex_event(ME_SYSEX_XG_LSB, ch, current_event->a, current_event->b); + break; + + case ME_SYSEX_XG_MSB: + process_sysex_event(ME_SYSEX_XG_MSB, ch, current_event->a, current_event->b); + break; + + case ME_NOTE_STEP: + break; + + case ME_EOT: + break; + } +#ifndef SUPPRESS_CHANNEL_LAYER + } + } + ev->channel = orig_ch; +#endif + + return RC_OK; +} + +void Player::set_master_tuning(int tune) +{ + if (tune & 0x4000) /* 1/8192 semitones + 0x2000 | 0x4000 */ + tune = (tune & 0x3FFF) - 0x2000; + else if (tune & 0x8000) /* 1 semitones | 0x8000 */ + tune = ((tune & 0x7F) - 0x40) << 13; + else /* millisemitones + 0x400 */ + tune = (((tune - 0x400) << 13) + 500) / 1000; + master_tuning = tune; +} + +void Player::set_single_note_tuning(int part, int a, int b, int rt) +{ + static int tp; /* tuning program number */ + static int kn; /* MIDI key number */ + static int st; /* the nearest equal-tempered semitone */ + double f, fst; /* fraction of semitone */ + int i; + + switch (part) { + case 0: + tp = a; + break; + case 1: + kn = a, st = b; + break; + case 2: + if (st == 0x7f && a == 0x7f && b == 0x7f) /* no change */ + break; + f = 440 * pow(2.0, (st - 69) / 12.0); + fst = pow(2.0, (a << 7 | b) / 196608.0); + freq_table_tuning[tp][kn] = f * fst * 1000 + 0.5; + if (rt) + for (i = 0; i < upper_voices; i++) + if (voice[i].status != VOICE_FREE) { + voice[i].temper_instant = 1; + recompute_freq(i); + } + break; + } +} + +void Player::set_user_temper_entry(int part, int a, int b) +{ + static int tp; /* temperament program number */ + static int ll; /* number of formula */ + static int fh, fl; /* applying pitch bit mask (forward) */ + static int bh, bl; /* applying pitch bit mask (backward) */ + static int aa, bb; /* fraction (aa/bb) */ + static int cc, dd; /* power (cc/dd)^(ee/ff) */ + static int ee, ff; + static int ifmax, ibmax, count; + static double rf[11], rb[11]; + int i, j, k, l, n, m; + double ratio[12], f, sc; + + switch (part) { + case 0: + for (i = 0; i < 11; i++) + rf[i] = rb[i] = 1; + ifmax = ibmax = 0; + count = 0; + tp = a, ll = b; + break; + case 1: + fh = a, fl = b; + break; + case 2: + bh = a, bl = b; + break; + case 3: + aa = a, bb = b; + break; + case 4: + cc = a, dd = b; + break; + case 5: + ee = a, ff = b; + for (i = 0; i < 11; i++) { + if (((fh & 0xf) << 7 | fl) & 1 << i) { + rf[i] *= (double) aa / bb + * pow((double) cc / dd, (double) ee / ff); + if (ifmax < i + 1) + ifmax = i + 1; + } + if (((bh & 0xf) << 7 | bl) & 1 << i) { + rb[i] *= (double) aa / bb + * pow((double) cc / dd, (double) ee / ff); + if (ibmax < i + 1) + ibmax = i + 1; + } + } + if (++count < ll) + break; + ratio[0] = 1; + for (i = n = m = 0; i < ifmax; i++, m = n) { + n += (n > 4) ? -5 : 7; + ratio[n] = ratio[m] * rf[i]; + if (ratio[n] > 2) + ratio[n] /= 2; + } + for (i = n = m = 0; i < ibmax; i++, m = n) { + n += (n > 6) ? -7 : 5; + ratio[n] = ratio[m] / rb[i]; + if (ratio[n] < 1) + ratio[n] *= 2; + } + sc = 27 / ratio[9] / 16; /* syntonic comma */ + for (i = 0; i < 12; i++) + for (j = -1; j < 11; j++) { + f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5); + for (k = 0; k < 12; k++) { + l = i + j * 12 + k; + if (l < 0 || l >= 128) + continue; + if (! (fh & 0x40)) { /* major */ + freq_table_user[tp][i][l] = + f * ratio[k] * 1000 + 0.5; + freq_table_user[tp][i + 36][l] = + f * ratio[k] * sc * 1000 + 0.5; + } + if (! (bh & 0x40)) { /* minor */ + freq_table_user[tp][i + 12][l] = + f * ratio[k] * sc * 1000 + 0.5; + freq_table_user[tp][i + 24][l] = + f * ratio[k] * 1000 + 0.5; + } + } + } + break; + } +} + + + + +struct midi_file_info *Player::new_midi_file_info() +{ + struct midi_file_info *p = &midifileinfo; + + /* Initialize default members */ + memset(p, 0, sizeof(struct midi_file_info)); + p->hdrsiz = -1; + p->format = -1; + p->tracks = -1; + p->divisions = -1; + p->time_sig_n = p->time_sig_d = -1; + p->samples = -1; + p->max_channel = -1; + COPY_CHANNELMASK(p->drumchannels, default_drumchannels); + COPY_CHANNELMASK(p->drumchannel_mask, default_drumchannel_mask); + return p; +} + + + +/* + * For MIDI stream player. + */ +void Player::playmidi_stream_init(void) +{ + int i; + static int first = 1; + + note_key_offset = timidity_key_adjust; + midi_time_ratio = timidity_tempo_adjust; + CLEAR_CHANNELMASK(channel_mute); + if (temper_type_mute & 1) + FILL_CHANNELMASK(channel_mute); + if (first) + { + first = 0; + init_mblock(&playmidi_pool); + midi_streaming = 1; + } + else + reuse_mblock(&playmidi_pool); + + /* Fill in current_file_info */ + current_file_info = &midifileinfo; + current_file_info->readflag = 1; + current_file_info->hdrsiz = 0; + current_file_info->format = 0; + current_file_info->tracks = 0; + current_file_info->divisions = 192; /* ?? */ + current_file_info->time_sig_n = 4; /* 4/ */ + current_file_info->time_sig_d = 4; /* /4 */ + current_file_info->time_sig_c = 24; /* clock */ + current_file_info->time_sig_b = 8; /* q.n. */ + current_file_info->samples = 0; + current_file_info->max_channel = MAX_CHANNELS; + current_file_info->compressed = 0; + + current_play_tempo = 500000; + check_eot_flag = 0; + + /* Setup default drums */ + COPY_CHANNELMASK(current_file_info->drumchannels, default_drumchannels); + COPY_CHANNELMASK(current_file_info->drumchannel_mask, default_drumchannel_mask); + for(i = 0; i < MAX_CHANNELS; i++) + memset(channel[i].drums, 0, sizeof(channel[i].drums)); + change_system_mode(DEFAULT_SYSTEM_MODE); + reset_midi(0); + + playmidi_tmr_reset(); +} + +void Player::playmidi_tmr_reset(void) +{ + int i; + + current_sample = 0; + buffer_pointer = common_buffer; + for(i = 0; i < MAX_CHANNELS; i++) + channel[i].lasttime = 0; +} + + +/*! initialize Part EQ (XG) */ +void Player::init_part_eq_xg(struct part_eq_xg *p) +{ + p->bass = 0x40; + p->treble = 0x40; + p->bass_freq = 0x0C; + p->treble_freq = 0x36; + p->valid = 0; +} + +/*! recompute Part EQ (XG) */ +void Player::recompute_part_eq_xg(struct part_eq_xg *p) +{ + int8_t vbass, vtreble; + + if(p->bass_freq >= 4 && p->bass_freq <= 40 && p->bass != 0x40) { + vbass = 1; + p->basss.q = 0.7; + p->basss.freq = eq_freq_table_xg[p->bass_freq]; + if(p->bass == 0) {p->basss.gain = -12.0;} + else {p->basss.gain = 0.19 * (double)(p->bass - 0x40);} + reverb->calc_filter_shelving_low(&(p->basss)); + } else {vbass = 0;} + if(p->treble_freq >= 28 && p->treble_freq <= 58 && p->treble != 0x40) { + vtreble = 1; + p->trebles.q = 0.7; + p->trebles.freq = eq_freq_table_xg[p->treble_freq]; + if(p->treble == 0) {p->trebles.gain = -12.0;} + else {p->trebles.gain = 0.19 * (double)(p->treble - 0x40);} + reverb->calc_filter_shelving_high(&(p->trebles)); + } else {vtreble = 0;} + p->valid = vbass || vtreble; +} + +void Player::init_midi_controller(midi_controller *p) +{ + p->val = 0; + p->pitch = 0; + p->cutoff = 0; + p->amp = 0.0; + p->lfo1_rate = p->lfo2_rate = p->lfo1_tva_depth = p->lfo2_tva_depth = 0; + p->lfo1_pitch_depth = p->lfo2_pitch_depth = p->lfo1_tvf_depth = p->lfo2_tvf_depth = 0; + p->variation_control_depth = p->insertion_control_depth = 0; +} + +float Player::get_midi_controller_amp(midi_controller *p) +{ + return (1.0 + (float)p->val * (1.0f / 127.0f) * p->amp); +} + +float Player::get_midi_controller_filter_cutoff(midi_controller *p) +{ + return ((float)p->val * (1.0f / 127.0f) * (float)p->cutoff); +} + +float Player::get_midi_controller_filter_depth(midi_controller *p) +{ + return ((float)p->val * (1.0f / 127.0f) * (float)p->lfo1_tvf_depth); +} + +int32_t Player::get_midi_controller_pitch(midi_controller *p) +{ + return ((int32_t)(p->val * p->pitch) << 6); +} + +int16_t Player::get_midi_controller_pitch_depth(midi_controller *p) +{ + return (int16_t)((float)p->val * (float)p->lfo1_pitch_depth * (1.0f / 127.0f * 256.0 / 400.0)); +} + +int16_t Player::get_midi_controller_amp_depth(midi_controller *p) +{ + return (int16_t)((float)p->val * (float)p->lfo1_tva_depth * (1.0f / 127.0f * 256.0)); +} + +void Player::init_rx(int ch) +{ + channel[ch].rx = 0xFFFFFFFF; /* all on */ +} + +void Player::set_rx(int ch, int32_t rx, int flag) +{ + if(ch > MAX_CHANNELS) {return;} + if(flag) {channel[ch].rx |= rx;} + else {channel[ch].rx &= ~rx;} +} + +void Player::init_rx_drum(struct DrumParts *p) +{ + p->rx = 0xFFFFFFFF; /* all on */ +} + +void Player::set_rx_drum(struct DrumParts *p, int32_t rx, int flag) +{ + if(flag) {p->rx |= rx;} + else {p->rx &= ~rx;} +} + +int32_t Player::get_rx_drum(struct DrumParts *p, int32_t rx) +{ + return (p->rx & rx); +} + +Instrument *Player::play_midi_load_instrument(int dr, int bk, int prog) +{ + bool load_success; + // The inner workings of this function which alters the instrument data has been put into the Instruments class. + auto instr = instruments->play_midi_load_instrument(dr, bk, prog, &load_success); + //if (load_success) send_output(NULL, 0); /* Update software buffer */ + return instr; +} + +void Player::change_system_mode(int mode) +{ + pan_table = sc_pan_table; + switch (mode) + { + case GM_SYSTEM_MODE: + if (play_system_mode == DEFAULT_SYSTEM_MODE) + { + play_system_mode = GM_SYSTEM_MODE; + vol_table = def_vol_table; + } + break; + case GM2_SYSTEM_MODE: + play_system_mode = GM2_SYSTEM_MODE; + vol_table = def_vol_table; + pan_table = gm2_pan_table; + break; + case GS_SYSTEM_MODE: + play_system_mode = GS_SYSTEM_MODE; + vol_table = gs_vol_table; + break; + case XG_SYSTEM_MODE: + if (play_system_mode != XG_SYSTEM_MODE) { reverb->init_all_effect_xg(); } + play_system_mode = XG_SYSTEM_MODE; + vol_table = xg_vol_table; + break; + default: + play_system_mode = DEFAULT_SYSTEM_MODE; + vol_table = def_vol_table; + break; + } +} + + +/*! initialize channel layers. */ +void Player::init_channel_layer(int ch) +{ + if (ch >= MAX_CHANNELS) + return; + CLEAR_CHANNELMASK(channel[ch].channel_layer); + SET_CHANNELMASK(channel[ch].channel_layer, ch); + channel[ch].port_select = ch >> 4; +} + + +static const struct ctl_chg_types { + unsigned char mtype; + int ttype; +} ctl_chg_list[] = { + { 0, ME_TONE_BANK_MSB }, +{ 1, ME_MODULATION_WHEEL }, +{ 2, ME_BREATH }, +{ 4, ME_FOOT }, +{ 5, ME_PORTAMENTO_TIME_MSB }, +{ 6, ME_DATA_ENTRY_MSB }, +{ 7, ME_MAINVOLUME }, +{ 8, ME_BALANCE }, +{ 10, ME_PAN }, +{ 11, ME_EXPRESSION }, +{ 32, ME_TONE_BANK_LSB }, +{ 37, ME_PORTAMENTO_TIME_LSB }, +{ 38, ME_DATA_ENTRY_LSB }, +{ 64, ME_SUSTAIN }, +{ 65, ME_PORTAMENTO }, +{ 66, ME_SOSTENUTO }, +{ 67, ME_SOFT_PEDAL }, +{ 68, ME_LEGATO_FOOTSWITCH }, +{ 69, ME_HOLD2 }, +{ 71, ME_HARMONIC_CONTENT }, +{ 72, ME_RELEASE_TIME }, +{ 73, ME_ATTACK_TIME }, +{ 74, ME_BRIGHTNESS }, +{ 84, ME_PORTAMENTO_CONTROL }, +{ 91, ME_REVERB_EFFECT }, +{ 92, ME_TREMOLO_EFFECT }, +{ 93, ME_CHORUS_EFFECT }, +{ 94, ME_CELESTE_EFFECT }, +{ 95, ME_PHASER_EFFECT }, +{ 96, ME_RPN_INC }, +{ 97, ME_RPN_DEC }, +{ 98, ME_NRPN_LSB }, +{ 99, ME_NRPN_MSB }, +{ 100, ME_RPN_LSB }, +{ 101, ME_RPN_MSB }, +{ 120, ME_ALL_SOUNDS_OFF }, +{ 121, ME_RESET_CONTROLLERS }, +{ 123, ME_ALL_NOTES_OFF }, +{ 126, ME_MONO }, +{ 127, ME_POLY }, +}; + +int Player::convert_midi_control_change(int chn, int type, int val, MidiEvent *ev_ret) +{ + for (auto &t : ctl_chg_list) + { + if (t.mtype == type) + { + if (val > 127) val = 127; + ev_ret->type = t.ttype; + ev_ret->channel = chn; + ev_ret->a = val; + ev_ret->b = 0; + return 1; + } + } + return 0; +} + + +int Player::send_event(int status, int parm1, int parm2) +{ + MidiEvent ev; + + ev.type = ME_NONE; + ev.channel = status & 0x0000000f; + //ev.channel = ev.channel + port * 16; + ev.a = (uint8_t)parm1; + ev.b = (uint8_t)parm2; + switch ((int)(status & 0x000000f0)) + { + case 0x80: + ev.type = ME_NOTEOFF; + break; + case 0x90: + ev.type = (ev.b) ? ME_NOTEON : ME_NOTEOFF; + break; + case 0xa0: + ev.type = ME_KEYPRESSURE; + break; + case 0xb0: + if (!convert_midi_control_change(ev.channel, ev.a, ev.b, &ev)) + ev.type = ME_NONE; + break; + case 0xc0: + ev.type = ME_PROGRAM; + break; + case 0xd0: + ev.type = ME_CHANNEL_PRESSURE; + break; + case 0xe0: + ev.type = ME_PITCHWHEEL; + break; + /* + case 0xf0: + if ((status & 0x000000ff) == 0xf2) + { + ev.type = ME_PROGRAM; + } + break; + */ + default: + break; + } + if (ev.type != ME_NONE) + { + play_event(&ev); + } + return 0; +} + +void Player::send_long_event(const uint8_t *sysexbuffer, int exlen) +{ + int i, ne; + MidiEvent ev; + MidiEvent evm[260]; + SysexConvert sc; + + if ((sysexbuffer[0] != 0xf0) && (sysexbuffer[0] != 0xf7)) return; + + if (sc.parse_sysex_event(sysexbuffer + 1, exlen - 1, &ev, instruments)) + { + if (ev.type == ME_RESET) + { + kill_all_voices(); + for (int i = 0; i < MAX_CHANNELS; i++) + init_channel_layer(i); + + /* initialize effect status */ + reverb->init_effect_status(play_system_mode); + effect->init_effect(); + instruments->init_userdrum(); + instruments->init_userinst(); + playmidi_stream_init(); + } + play_event(&ev); + return; + } + if ((ne = sc.parse_sysex_event_multi(sysexbuffer + 1, exlen - 1, evm, instruments))) + { + for (i = 0; i < ne; i++) + { + play_event(&evm[i]); + } + } +} + + + +} diff --git a/thirdparty/timidityplus/quantity.cpp b/thirdparty/timidityplus/quantity.cpp new file mode 100644 index 000000000..1060c73ff --- /dev/null +++ b/thirdparty/timidityplus/quantity.cpp @@ -0,0 +1,318 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + quantity.c + + string -> quantity -> native value convertion + by Kentaro Sato +*/ + +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "tables.h" + +#include "quantity.h" + + +namespace TimidityPlus +{ + typedef int32_t(*QuantityToIntProc)(int32_t value, int32_t param); + typedef double(*QuantityToFloatProc)(double value, int32_t param); + typedef union { + QuantityToIntProc i; + QuantityToFloatProc f; + } QuantityConvertProc; + + typedef struct { + const char *suffix; + uint16_t type, id; + int float_type; /* is floating-point type */ + QuantityConvertProc convert; + } QuantityHint; + + +/* + Guide To Add New Unit Types/Units + + append QUANTITY_UNIT_TYPE() + QUANTITY_UNIT_NAME() + ... to enum quantity_units (in quantity.h) + append QUANTITY_TYPE_INT/FLOAT() + REGISTER_TYPE_INT/FLOAT("", ); + ... + END_QUANTITY_TYPE; to GetQuantityHints() + write convert__NUM(int32_t/double value, int32_t param) + convert_(int32_t/double value, int32_t param) + ... functions. +*/ + +/*************** conversion functions ***************/ + +static int32_t convert_DIRECT_INT_NUM(int32_t value, int32_t param) +{ + return value; +} + +static double convert_DIRECT_FLOAT_NUM(double value, int32_t param) +{ + return value; +} + +/* from instrum.c, convert_tremolo_sweep() */ +static int32_t convert_TREMOLO_SWEEP_NUM(int32_t value, int32_t param) +{ + uint8_t sweep = value; + if (!sweep) + return 0; + + return + ((control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (playback_rate * sweep); +} + +static int32_t convert_TREMOLO_SWEEP_MS(int32_t value, int32_t param) +{ + if (value <= 0) + return 0; + #if SWEEP_SHIFT <= 16 + return ((uint32_t)(control_ratio * (1000 >> 2)) << SWEEP_SHIFT) / ((playback_rate * value) >> 2); + #else + #error "overflow" + #endif +} + +/* from instrum.c, convert_tremolo_rate() */ +static int32_t convert_TREMOLO_RATE_NUM(int32_t value, int32_t param) +{ + uint8_t rate = value; + return + ((SINE_CYCLE_LENGTH * control_ratio * rate) << RATE_SHIFT) / + (TREMOLO_RATE_TUNING * playback_rate); +} + +static int32_t convert_TREMOLO_RATE_MS(int32_t value, int32_t param) +{ + #if RATE_SHIFT <= 5 + return ((SINE_CYCLE_LENGTH * control_ratio * (1000 >> 1)) << RATE_SHIFT) / + ((playback_rate * (uint32_t)value) >> 1); + #else + #error "overflow" + #endif +} + +static double convert_TREMOLO_RATE_HZ(double value, int32_t param) +{ + if (value <= 0) + return 0; + return ((SINE_CYCLE_LENGTH * control_ratio) << RATE_SHIFT) * value / playback_rate; +} + +/* from instrum.c, convert_vibrato_sweep() */ +static int32_t convert_VIBRATO_SWEEP_NUM(int32_t value, int32_t vib_control_ratio) +{ + uint8_t sweep = value; + if (!sweep) + return 0; + + return (int32_t)(TIM_FSCALE((double) (vib_control_ratio) + * SWEEP_TUNING, SWEEP_SHIFT) + / (double)(playback_rate * sweep)); + + /* this was overflowing with seashore.pat + + ((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (playback_rate * sweep); */ +} + +static int32_t convert_VIBRATO_SWEEP_MS(int32_t value, int32_t vib_control_ratio) +{ + if (value <= 0) + return 0; + return (TIM_FSCALE((double)vib_control_ratio * 1000, SWEEP_SHIFT) + / (double)(playback_rate * value)); +} + +/* from instrum.c, to_control() */ +static int32_t convert_VIBRATO_RATE_NUM(int32_t control, int32_t param) +{ + return (int32_t) (0x2000 / pow(2.0, control / 31.0)); +} + +static int32_t convert_VIBRATO_RATE_MS(int32_t value, int32_t param) +{ + return 1000 * playback_rate / ((2 * VIBRATO_SAMPLE_INCREMENTS) * value); +} + +static double convert_VIBRATO_RATE_HZ(double value, int32_t param) +{ + return playback_rate / ((2 * VIBRATO_SAMPLE_INCREMENTS) * value); +} + +/*************** core functions ***************/ + +#define MAX_QUANTITY_UNITS_PER_UNIT_TYPES 8 + +static int GetQuantityHints(uint16_t type, QuantityHint *units) +{ + QuantityHint *unit; + + unit = units; + #define QUANTITY_TYPE_INT(type) \ + case QUANTITY_UNIT_TYPE(type): REGISTER_TYPE_INT("", type##_NUM) + #define QUANTITY_TYPE_FLOAT(type) \ + case QUANTITY_UNIT_TYPE(type): REGISTER_TYPE_FLOAT("", type##_NUM) + #define REGISTER_TYPE_INT(ustr, utype) REGISTER_TYPE_ENTITY_INT(ustr, utype, convert_##utype) + #define REGISTER_TYPE_FLOAT(ustr, utype) REGISTER_TYPE_ENTITY_FLOAT(ustr, utype, convert_##utype) + #define REGISTER_TYPE_ALIAS_INT(ustr, utype, atype) REGISTER_TYPE_ENTITY_INT(ustr, utype, convert_##atype) + #define REGISTER_TYPE_ALIAS_FLOAT(ustr, utype, atype) REGISTER_TYPE_ENTITY_FLOAT(ustr, utype, convert_##atype) + #define REGISTER_TYPE_ENTITY_INT(ustr, utype, ucvt) \ + unit->suffix = ustr, unit->type = type, unit->id = QUANTITY_UNIT_NAME(utype), unit->float_type = 0, unit->convert.i = ucvt, unit++ + #define REGISTER_TYPE_ENTITY_FLOAT(ustr, utype, ucvt) \ + unit->suffix = ustr, unit->type = type, unit->id = QUANTITY_UNIT_NAME(utype), unit->float_type = 1, unit->convert.f = ucvt, unit++ + #define END_QUANTITY_TYPE unit->suffix = NULL; break + switch (type) + { + QUANTITY_TYPE_INT(DIRECT_INT); + END_QUANTITY_TYPE; + QUANTITY_TYPE_FLOAT(DIRECT_FLOAT); + END_QUANTITY_TYPE; + QUANTITY_TYPE_INT(TREMOLO_SWEEP); + REGISTER_TYPE_INT("ms", TREMOLO_SWEEP_MS); + END_QUANTITY_TYPE; + QUANTITY_TYPE_INT(TREMOLO_RATE); + REGISTER_TYPE_INT("ms", TREMOLO_RATE_MS); + REGISTER_TYPE_FLOAT("Hz", TREMOLO_RATE_HZ); + END_QUANTITY_TYPE; + QUANTITY_TYPE_INT(VIBRATO_RATE); + REGISTER_TYPE_INT("ms", VIBRATO_RATE_MS); + REGISTER_TYPE_FLOAT("Hz", VIBRATO_RATE_HZ); + END_QUANTITY_TYPE; + QUANTITY_TYPE_INT(VIBRATO_SWEEP); + REGISTER_TYPE_INT("ms", VIBRATO_SWEEP_MS); + END_QUANTITY_TYPE; + default: + printMessage(CMSG_ERROR, VERB_NORMAL, "Internal parameter error (%d)", type); + return 0; + } + return 1; +} + +/* quantity is unchanged if an error occurred */ +static const char *number_to_quantity(int32_t number_i, const char *suffix_i, double number_f, const char *suffix_f, Quantity *quantity, uint16_t type) +{ + QuantityHint units[MAX_QUANTITY_UNITS_PER_UNIT_TYPES], *unit; + + if (!GetQuantityHints(type, units)) + return "Parameter error"; + unit = units; + while(unit->suffix != NULL) + { + if (suffix_i != NULL && strcmp(suffix_i, unit->suffix) == 0) /* number_i, suffix_i was valid */ + { + quantity->type = unit->type; + quantity->unit = unit->id; + if (unit->float_type) + quantity->value.f = number_i; + else + quantity->value.i = number_i; + return NULL; + } + else if (suffix_f != NULL && strcmp(suffix_f, unit->suffix) == 0) /* number_f, suffix_f was valid */ + { + if (unit->float_type) + { + quantity->type = unit->type; + quantity->unit = unit->id; + quantity->value.f = number_f; + return NULL; + } + else + return "integer expected"; + } + unit++; + } + return "invalid parameter"; +} + +const char *string_to_quantity(const char *string, Quantity *quantity, uint16_t type) +{ + int32_t number_i; + double number_f; + char *suffix_i, *suffix_f; + + number_i = strtol(string, &suffix_i, 10); /* base == 10 for compatibility with atoi() */ + if (string == suffix_i) /* doesn't start with valid character */ + return "Number expected"; + number_f = strtod(string, &suffix_f); + return number_to_quantity(number_i, suffix_i, number_f, suffix_f, quantity, type); +} + +static int GetQuantityConvertProc(const Quantity *quantity, QuantityConvertProc *proc) +{ + QuantityHint units[MAX_QUANTITY_UNITS_PER_UNIT_TYPES], *unit; + + if (!GetQuantityHints(quantity->type, units)) + return -1; /* already warned */ + unit = units; + while(unit->suffix != NULL) + { + if (quantity->unit == unit->id) + { + *proc = unit->convert; + return unit->float_type; + } + unit++; + } + printMessage(CMSG_ERROR, VERB_NORMAL, "Internal parameter error"); + return -1; +} + +int32_t quantity_to_int(const Quantity *quantity, int32_t param) +{ + QuantityConvertProc proc; + + switch (GetQuantityConvertProc(quantity, &proc)) + { + case 0: + return (*proc.i)(quantity->value.i, param); + case 1: + return (*proc.f)(quantity->value.f, param); + } + return 0; +} + +double quantity_to_float(const Quantity *quantity, int32_t param) +{ + QuantityConvertProc proc; + + switch (GetQuantityConvertProc(quantity, &proc)) + { + case 0: + return (*proc.i)(quantity->value.i, param); + case 1: + return (*proc.f)(quantity->value.f, param); + } + return 0; +} +} \ No newline at end of file diff --git a/thirdparty/timidityplus/readmidic.cpp b/thirdparty/timidityplus/readmidic.cpp new file mode 100644 index 000000000..8bd454322 --- /dev/null +++ b/thirdparty/timidityplus/readmidic.cpp @@ -0,0 +1,2664 @@ +/* +TiMidity++ -- MIDI to WAVE converter and player +Copyright (C) 1999-2009 Masanao Izumo +Copyright (C) 1995 Tuukka Toivonen + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include "timidity.h" +#include "instrum.h" +#include "playmidi.h" + +namespace TimidityPlus +{ + + +inline void SETMIDIEVENT(MidiEvent &e, int32_t /*time, not needed anymore*/, uint32_t t, uint32_t ch, uint32_t pa, uint32_t pb) +{ + (e).type = (t); + (e).channel = (uint8_t)(ch); + (e).a = (uint8_t)(pa); + (e).b = (uint8_t)(pb); +} + +#define MERGE_CHANNEL_PORT(ch) ((int)(ch) | (midi_port_number << 4)) +#define MERGE_CHANNEL_PORT2(ch, port) ((int)(ch) | ((int)port << 4)) + +/* Map XG types onto GS types. XG should eventually have its own tables */ +static int set_xg_reverb_type(int msb, int lsb) +{ + int type = 4; + + if ((msb == 0x00) || + (msb >= 0x05 && msb <= 0x0F) || + (msb >= 0x14)) /* NO EFFECT */ + { + //printMessage(CMSG_INFO,VERB_NOISY,"XG Set Reverb Type (NO EFFECT %d %d)", msb, lsb); + return -1; + } + + switch (msb) + { + case 0x01: + type = 3; /* Hall 1 */ + break; + case 0x02: + type = 0; /* Room 1 */ + break; + case 0x03: + type = 3; /* Stage 1 -> Hall 1 */ + break; + case 0x04: + type = 5; /* Plate */ + break; + default: + type = 4; /* unsupported -> Hall 2 */ + break; + } + if (lsb == 0x01) + { + switch (msb) + { + case 0x01: + type = 4; /* Hall 2 */ + break; + case 0x02: + type = 1; /* Room 2 */ + break; + case 0x03: + type = 4; /* Stage 2 -> Hall 2 */ + break; + default: + break; + } + } + if (lsb == 0x02 && msb == 0x02) + type = 2; /* Room 3 */ + + //printMessage(CMSG_INFO,VERB_NOISY,"XG Set Reverb Type (%d)", type); + return type; +} + + +/* Map XG types onto GS types. XG should eventually have its own tables */ +static int set_xg_chorus_type(int msb, int lsb) +{ + int type = 2; + + if ((msb >= 0x00 && msb <= 0x40) || + (msb >= 0x45 && msb <= 0x47) || + (msb >= 0x49)) /* NO EFFECT */ + { + //printMessage(CMSG_INFO,VERB_NOISY,"XG Set Chorus Type (NO EFFECT %d %d)", msb, lsb); + return -1; + } + + switch (msb) + { + case 0x41: + type = 0; /* Chorus 1 */ + break; + case 0x42: + type = 0; /* Celeste 1 -> Chorus 1 */ + break; + case 0x43: + type = 5; + break; + default: + type = 2; /* unsupported -> Chorus 3 */ + break; + } + if (lsb == 0x01) + { + switch (msb) + { + case 0x41: + type = 1; /* Chorus 2 */ + break; + case 0x42: + type = 1; /* Celeste 2 -> Chorus 2 */ + break; + default: + break; + } + } + else if (lsb == 0x02) + { + switch (msb) + { + case 0x41: + type = 2; /* Chorus 3 */ + break; + case 0x42: + type = 2; /* Celeste 3 -> Chorus 3 */ + break; + default: + break; + } + } + else if (lsb == 0x08) + { + switch (msb) + { + case 0x41: + type = 3; /* Chorus 4 */ + break; + case 0x42: + type = 3; /* Celeste 4 -> Chorus 4 */ + break; + default: + break; + } + } + + //printMessage(CMSG_INFO,VERB_NOISY,"XG Set Chorus Type (%d)", type); + return type; +} + +static int block_to_part(int block, int port) +{ + int p; + p = block & 0x0F; + if (p == 0) { p = 9; } + else if (p <= 9) { p--; } + return MERGE_CHANNEL_PORT2(p, port); +} + +static uint16_t gs_convert_master_vol(int vol) +{ + double v; + + if (vol >= 0x7f) + return 0xffff; + v = (double)vol * (0xffff / 127.0); + if (v >= 0xffff) + return 0xffff; + return (uint16_t)v; +} + +static uint16_t gm_convert_master_vol(uint16_t v1, uint16_t v2) +{ + return (((v1 & 0x7f) | ((v2 & 0x7f) << 7)) << 2) | 3; +} + + +/* XG SysEx parsing function by Eric A. Welsh +* Also handles GS patch+bank changes +* +* This function provides basic support for XG Bulk Dump and Parameter +* Change SysEx events +*/ +int SysexConvert::parse_sysex_event_multi(const uint8_t *val, int32_t len, MidiEvent *evm, Instruments *instruments) +{ + int num_events = 0; /* Number of events added */ + + uint32_t channel_tt; + int i, j; + static uint8_t xg_reverb_type_msb = 0x01, xg_reverb_type_lsb = 0x00; + static uint8_t xg_chorus_type_msb = 0x41, xg_chorus_type_lsb = 0x00; + + /* Effect 1 or Multi EQ */ + if (len >= 8 && + val[0] == 0x43 && /* Yamaha ID */ + val[2] == 0x4C && /* XG Model ID */ + ((val[1] < 0x10 && val[5] == 0x02) || /* Bulk Dump*/ + (val[1] >= 0x10 && val[3] == 0x02))) /* Parameter Change */ + { + uint8_t addhigh, addmid, addlow; /* Addresses */ + const uint8_t *body; /* SysEx body */ + int ent, v; /* Entry # of sub-event */ + const uint8_t *body_end; /* End of SysEx body */ + + if (val[1] < 0x10) /* Bulk Dump */ + { + addhigh = val[5]; + addmid = val[6]; + addlow = val[7]; + body = val + 8; + body_end = val + len - 3; + } + else /* Parameter Change */ + { + addhigh = val[3]; + addmid = val[4]; + addlow = val[5]; + body = val + 6; + body_end = val + len - 2; + } + + /* set the SYSEX_XG_MSB info */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, 0, addhigh, addmid); + num_events++; + + for (ent = addlow; body <= body_end; body++, ent++) { + if (addmid == 0x01) { /* Effect 1 */ + switch (ent) { + case 0x00: /* Reverb Type MSB */ + xg_reverb_type_msb = *body; +#if 0 /* XG specific reverb is not supported yet, use GS instead */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; +#endif + break; + + case 0x01: /* Reverb Type LSB */ + xg_reverb_type_lsb = *body; +#if 0 /* XG specific reverb is not supported yet, use GS instead */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; +#else + v = set_xg_reverb_type(xg_reverb_type_msb, xg_reverb_type_lsb); + if (v >= 0) { + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, v, 0x05); + num_events++; + } +#endif + break; + + case 0x0C: /* Reverb Return */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x20: /* Chorus Type MSB */ + xg_chorus_type_msb = *body; +#if 0 /* XG specific chorus is not supported yet, use GS instead */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; +#endif + break; + + case 0x21: /* Chorus Type LSB */ + xg_chorus_type_lsb = *body; +#if 0 /* XG specific chorus is not supported yet, use GS instead */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; +#else + v = set_xg_chorus_type(xg_chorus_type_msb, xg_chorus_type_lsb); + if (v >= 0) { + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, v, 0x0D); + num_events++; + } +#endif + break; + + case 0x2C: /* Chorus Return */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + default: + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + } + } + else if (addmid == 0x40) { /* Multi EQ */ + switch (ent) { + case 0x00: /* EQ type */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x01: /* EQ gain1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x02: /* EQ frequency1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x03: /* EQ Q1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x04: /* EQ shape1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x05: /* EQ gain2 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x06: /* EQ frequency2 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x07: /* EQ Q2 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x09: /* EQ gain3 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x0A: /* EQ frequency3 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x0B: /* EQ Q3 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x0D: /* EQ gain4 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x0E: /* EQ frequency4 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x0F: /* EQ Q4 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x11: /* EQ gain5 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x12: /* EQ frequency5 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x13: /* EQ Q5 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + case 0x14: /* EQ shape5 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + break; + + default: + break; + } + } + } + } + + /* Effect 2 (Insertion Effects) */ + else if (len >= 8 && + val[0] == 0x43 && /* Yamaha ID */ + val[2] == 0x4C && /* XG Model ID */ + ((val[1] < 0x10 && val[5] == 0x03) || /* Bulk Dump*/ + (val[1] >= 0x10 && val[3] == 0x03))) /* Parameter Change */ + { + uint8_t addhigh, addmid, addlow; /* Addresses */ + const uint8_t *body; /* SysEx body */ + int ent; /* Entry # of sub-event */ + const uint8_t *body_end; /* End of SysEx body */ + + if (val[1] < 0x10) /* Bulk Dump */ + { + addhigh = val[5]; + addmid = val[6]; + addlow = val[7]; + body = val + 8; + body_end = val + len - 3; + } + else /* Parameter Change */ + { + addhigh = val[3]; + addmid = val[4]; + addlow = val[5]; + body = val + 6; + body_end = val + len - 2; + } + + /* set the SYSEX_XG_MSB info */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, 0, addhigh, addmid); + num_events++; + + for (ent = addlow; body <= body_end; body++, ent++) { + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent); + num_events++; + } + } + + /* XG Multi Part Data parameter change */ + else if (len >= 10 && + val[0] == 0x43 && /* Yamaha ID */ + val[2] == 0x4C && /* XG Model ID */ + ((val[1] < 0x10 && val[5] == 0x08 && /* Bulk Dump */ + (val[4] == 0x29 || val[4] == 0x3F)) || /* Blocks 1 or 2 */ + (val[1] >= 0x10 && val[3] == 0x08))) /* Parameter Change */ + { + uint8_t addhigh, addmid, addlow; /* Addresses */ + const uint8_t *body; /* SysEx body */ + uint8_t p; /* Channel part number [0..15] */ + int ent; /* Entry # of sub-event */ + const uint8_t *body_end; /* End of SysEx body */ + + if (val[1] < 0x10) /* Bulk Dump */ + { + addhigh = val[5]; + addmid = val[6]; + addlow = val[7]; + body = val + 8; + p = addmid; + body_end = val + len - 3; + } + else /* Parameter Change */ + { + addhigh = val[3]; + addmid = val[4]; + addlow = val[5]; + body = val + 6; + p = addmid; + body_end = val + len - 2; + } + + /* set the SYSEX_XG_MSB info */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, p, addhigh, addmid); + num_events++; + + for (ent = addlow; body <= body_end; body++, ent++) { + switch (ent) { + case 0x00: /* Element Reserve */ + /* //printMessage(CMSG_INFO,VERB_NOISY,"Element Reserve is not supported. (CH:%d VAL:%d)", p, *body); */ + break; + + case 0x01: /* bank select MSB */ + SETMIDIEVENT(evm[num_events], 0, ME_TONE_BANK_MSB, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x02: /* bank select LSB */ + SETMIDIEVENT(evm[num_events], 0, ME_TONE_BANK_LSB, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x03: /* program number */ + SETMIDIEVENT(evm[num_events], 0, ME_PROGRAM, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x04: /* Rcv CHANNEL */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, p, *body, 0x99); + num_events++; + break; + + case 0x05: /* mono/poly mode */ + if (*body == 0) { SETMIDIEVENT(evm[num_events], 0, ME_MONO, p, 0, SYSEX_TAG); } + else { SETMIDIEVENT(evm[num_events], 0, ME_POLY, p, 0, SYSEX_TAG); } + num_events++; + break; + + case 0x06: /* Same Note Number Key On Assign */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, p, *body, ent); + num_events++; + break; + + case 0x07: /* Part Mode */ + drum_setup_xg[*body] = p; + SETMIDIEVENT(evm[num_events], 0, ME_DRUMPART, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x08: /* note shift */ + SETMIDIEVENT(evm[num_events], 0, ME_KEYSHIFT, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x09: /* Detune 1st bit */ + break; + + case 0x0A: /* Detune 2nd bit */ + break; + + case 0x0B: /* volume */ + SETMIDIEVENT(evm[num_events], 0, ME_MAINVOLUME, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x0C: /* Velocity Sense Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, p, *body, 0x21); + num_events++; + break; + + case 0x0D: /* Velocity Sense Offset */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, p, *body, 0x22); + num_events++; + break; + + case 0x0E: /* pan */ + if (*body == 0) { + SETMIDIEVENT(evm[num_events], 0, ME_RANDOM_PAN, p, 0, SYSEX_TAG); + } + else { + SETMIDIEVENT(evm[num_events], 0, ME_PAN, p, *body, SYSEX_TAG); + } + num_events++; + break; + + case 0x0F: /* Note Limit Low */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x42); + num_events++; + break; + + case 0x10: /* Note Limit High */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x43); + num_events++; + break; + + case 0x11: /* Dry Level */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, p, *body, ent); + num_events++; + break; + + case 0x12: /* chorus send */ + SETMIDIEVENT(evm[num_events], 0, ME_CHORUS_EFFECT, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x13: /* reverb send */ + SETMIDIEVENT(evm[num_events], 0, ME_REVERB_EFFECT, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x14: /* Variation Send */ + SETMIDIEVENT(evm[num_events], 0, ME_CELESTE_EFFECT, p, *body, SYSEX_TAG); + num_events++; + break; + + case 0x15: /* Vibrato Rate */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x08, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x16: /* Vibrato Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x09, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x17: /* Vibrato Delay */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x0A, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x18: /* Filter Cutoff Frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x20, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x19: /* Filter Resonance */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x21, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x1A: /* EG Attack Time */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x63, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x1B: /* EG Decay Time */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x64, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x1C: /* EG Release Time */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x66, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x1D: /* MW Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x16); + num_events++; + break; + + case 0x1E: /* MW Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x17); + num_events++; + break; + + case 0x1F: /* MW Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x18); + num_events++; + break; + + case 0x20: /* MW LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x1A); + num_events++; + break; + + case 0x21: /* MW LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x1B); + num_events++; + break; + + case 0x22: /* MW LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x1C); + num_events++; + break; + + case 0x23: /* bend pitch control */ + SETMIDIEVENT(evm[num_events], 0, ME_RPN_MSB, p, 0, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_RPN_LSB, p, 0, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, (*body - 0x40) & 0x7F, SYSEX_TAG); + num_events += 3; + break; + + case 0x24: /* Bend Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x22); + num_events++; + break; + + case 0x25: /* Bend Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x23); + num_events++; + break; + + case 0x26: /* Bend LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x25); + num_events++; + break; + + case 0x27: /* Bend LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x26); + num_events++; + break; + + case 0x28: /* Bend LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x27); + num_events++; + break; + + case 0x30: /* Rcv Pitch Bend */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x48); + num_events++; + break; + + case 0x31: /* Rcv Channel Pressure */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x49); + num_events++; + break; + + case 0x32: /* Rcv Program Change */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4A); + num_events++; + break; + + case 0x33: /* Rcv Control Change */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4B); + num_events++; + break; + + case 0x34: /* Rcv Poly Pressure */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4C); + num_events++; + break; + + case 0x35: /* Rcv Note Message */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4D); + num_events++; + break; + + case 0x36: /* Rcv RPN */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4E); + num_events++; + break; + + case 0x37: /* Rcv NRPN */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4F); + num_events++; + break; + + case 0x38: /* Rcv Modulation */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x50); + num_events++; + break; + + case 0x39: /* Rcv Volume */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x51); + num_events++; + break; + + case 0x3A: /* Rcv Pan */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x52); + num_events++; + break; + + case 0x3B: /* Rcv Expression */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x53); + num_events++; + break; + + case 0x3C: /* Rcv Hold1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x54); + num_events++; + break; + + case 0x3D: /* Rcv Portamento */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x55); + num_events++; + break; + + case 0x3E: /* Rcv Sostenuto */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x56); + num_events++; + break; + + case 0x3F: /* Rcv Soft */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x57); + num_events++; + break; + + case 0x40: /* Rcv Bank Select */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x58); + num_events++; + break; + + case 0x41: /* scale tuning */ + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + SETMIDIEVENT(evm[num_events], 0, ME_SCALE_TUNING, p, ent - 0x41, *body - 64); + num_events++; + break; + + case 0x4D: /* CAT Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x00); + num_events++; + break; + + case 0x4E: /* CAT Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x01); + num_events++; + break; + + case 0x4F: /* CAT Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x02); + num_events++; + break; + + case 0x50: /* CAT LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x04); + num_events++; + break; + + case 0x51: /* CAT LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x05); + num_events++; + break; + + case 0x52: /* CAT LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x06); + num_events++; + break; + + case 0x53: /* PAT Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0B); + num_events++; + break; + + case 0x54: /* PAT Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0C); + num_events++; + break; + + case 0x55: /* PAT Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0D); + num_events++; + break; + + case 0x56: /* PAT LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0F); + num_events++; + break; + + case 0x57: /* PAT LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x10); + num_events++; + break; + + case 0x58: /* PAT LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x11); + num_events++; + break; + + case 0x59: /* AC1 Controller Number */ + //printMessage(CMSG_INFO,VERB_NOISY,"AC1 Controller Number is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x5A: /* AC1 Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x2C); + num_events++; + break; + + case 0x5B: /* AC1 Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x2D); + num_events++; + break; + + case 0x5C: /* AC1 Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x2E); + num_events++; + break; + + case 0x5D: /* AC1 LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x30); + num_events++; + break; + + case 0x5E: /* AC1 LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x31); + num_events++; + break; + + case 0x5F: /* AC1 LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x32); + num_events++; + break; + + case 0x60: /* AC2 Controller Number */ + //printMessage(CMSG_INFO,VERB_NOISY,"AC2 Controller Number is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x61: /* AC2 Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x37); + num_events++; + break; + + case 0x62: /* AC2 Filter Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x38); + num_events++; + break; + + case 0x63: /* AC2 Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x39); + num_events++; + break; + + case 0x64: /* AC2 LFO PMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x3B); + num_events++; + break; + + case 0x65: /* AC2 LFO FMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x3C); + num_events++; + break; + + case 0x66: /* AC2 LFO AMod Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x3D); + num_events++; + break; + + case 0x67: /* Portamento Switch */ + SETMIDIEVENT(evm[num_events], 0, ME_PORTAMENTO, p, *body, SYSEX_TAG); + num_events++; + + case 0x68: /* Portamento Time */ + SETMIDIEVENT(evm[num_events], 0, ME_PORTAMENTO_TIME_MSB, p, *body, SYSEX_TAG); + num_events++; + + case 0x69: /* Pitch EG Initial Level */ + //printMessage(CMSG_INFO,VERB_NOISY,"Pitch EG Initial Level is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x6A: /* Pitch EG Attack Time */ + //printMessage(CMSG_INFO,VERB_NOISY,"Pitch EG Attack Time is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x6B: /* Pitch EG Release Level */ + //printMessage(CMSG_INFO,VERB_NOISY,"Pitch EG Release Level is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x6C: /* Pitch EG Release Time */ + //printMessage(CMSG_INFO,VERB_NOISY,"Pitch EG Release Time is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x6D: /* Velocity Limit Low */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x44); + num_events++; + break; + + case 0x6E: /* Velocity Limit High */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x45); + num_events++; + break; + + case 0x70: /* Bend Pitch Low Control */ + //printMessage(CMSG_INFO,VERB_NOISY,"Bend Pitch Low Control is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x71: /* Filter EG Depth */ + //printMessage(CMSG_INFO,VERB_NOISY,"Filter EG Depth is not supported. (CH:%d VAL:%d)", p, *body); + break; + + case 0x72: /* EQ BASS */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x30, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x73: /* EQ TREBLE */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x31, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x76: /* EQ BASS frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x34, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + case 0x77: /* EQ TREBLE frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x35, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG); + num_events += 3; + break; + + default: + //printMessage(CMSG_INFO,VERB_NOISY,"Unsupported XG Bulk Dump SysEx. (ADDR:%02X %02X %02X VAL:%02X)", addhigh, addlow, ent, *body); + continue; + break; + } + } + } + + /* XG Drum Setup */ + else if (len >= 10 && + val[0] == 0x43 && /* Yamaha ID */ + val[2] == 0x4C && /* XG Model ID */ + ((val[1] < 0x10 && (val[5] & 0xF0) == 0x30) || /* Bulk Dump*/ + (val[1] >= 0x10 && (val[3] & 0xF0) == 0x30))) /* Parameter Change */ + { + uint8_t addhigh, addmid, addlow; /* Addresses */ + const uint8_t *body; /* SysEx body */ + uint8_t dp, note; /* Channel part number [0..15] */ + int ent; /* Entry # of sub-event */ + const uint8_t *body_end; /* End of SysEx body */ + + if (val[1] < 0x10) /* Bulk Dump */ + { + addhigh = val[5]; + addmid = val[6]; + addlow = val[7]; + body = val + 8; + body_end = val + len - 3; + } + else /* Parameter Change */ + { + addhigh = val[3]; + addmid = val[4]; + addlow = val[5]; + body = val + 6; + body_end = val + len - 2; + } + + dp = drum_setup_xg[(addhigh & 0x0F) + 1]; + note = addmid; + + /* set the SYSEX_XG_MSB info */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, dp, addhigh, addmid); + num_events++; + + for (ent = addlow; body <= body_end; body++, ent++) { + switch (ent) { + case 0x00: /* Pitch Coarse */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x18, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x01: /* Pitch Fine */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x19, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x02: /* Level */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1A, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x03: /* Alternate Group */ + //printMessage(CMSG_INFO,VERB_NOISY,"Alternate Group is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body); + break; + case 0x04: /* Pan */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1C, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x05: /* Reverb Send */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1D, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x06: /* Chorus Send */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1E, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x07: /* Variation Send */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1F, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x08: /* Key Assign */ + //printMessage(CMSG_INFO,VERB_NOISY,"Key Assign is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body); + break; + case 0x09: /* Rcv Note Off */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_MSB, dp, note, 0); + SETMIDIEVENT(evm[num_events + 1], 0, ME_SYSEX_LSB, dp, *body, 0x46); + num_events += 2; + break; + case 0x0A: /* Rcv Note On */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_MSB, dp, note, 0); + SETMIDIEVENT(evm[num_events + 1], 0, ME_SYSEX_LSB, dp, *body, 0x47); + num_events += 2; + break; + case 0x0B: /* Filter Cutoff Frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x14, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x0C: /* Filter Resonance */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x15, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x0D: /* EG Attack */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x16, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x0E: /* EG Decay1 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, dp, *body, ent); + num_events++; + break; + case 0x0F: /* EG Decay2 */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, dp, *body, ent); + num_events++; + break; + case 0x20: /* EQ BASS */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x30, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x21: /* EQ TREBLE */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x31, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x24: /* EQ BASS frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x34, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x25: /* EQ TREBLE frequency */ + SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x35, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG); + SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG); + num_events += 3; + break; + case 0x50: /* High Pass Filter Cutoff Frequency */ + //printMessage(CMSG_INFO,VERB_NOISY,"High Pass Filter Cutoff Frequency is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body); + break; + case 0x60: /* Velocity Pitch Sense */ + //printMessage(CMSG_INFO,VERB_NOISY,"Velocity Pitch Sense is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body); + break; + case 0x61: /* Velocity LPF Cutoff Sense */ + //printMessage(CMSG_INFO,VERB_NOISY,"Velocity LPF Cutoff Sense is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body); + break; + default: + //printMessage(CMSG_INFO,VERB_NOISY,"Unsupported XG Bulk Dump SysEx. (ADDR:%02X %02X %02X VAL:%02X)", addhigh, addmid, ent, *body); + break; + } + } + } + + /* parsing GS System Exclusive Message... + * + * val[4] == Parameter Address(High) + * val[5] == Parameter Address(Middle) + * val[6] == Parameter Address(Low) + * val[7]... == Data... + * val[last] == Checksum(== 128 - (sum of addresses&data bytes % 128)) + */ + else if (len >= 9 && + val[0] == 0x41 && /* Roland ID */ + val[1] == 0x10 && /* Device ID */ + val[2] == 0x42 && /* GS Model ID */ + val[3] == 0x12) /* Data Set Command */ + { + uint8_t p, dp, udn, gslen, port = 0; + int i, addr, addr_h, addr_m, addr_l, checksum; + p = block_to_part(val[5], midi_port_number); + + /* calculate checksum */ + checksum = 0; + for (gslen = 9; gslen < len; gslen++) + if (val[gslen] == 0xF7) + break; + for (i = 4; i> 4]; + + /* calculate user drumset number */ + udn = (val[5] & 0xF0) >> 4; + + addr_h = val[4]; + addr_m = val[5]; + addr_l = val[6]; + if (addr_h == 0x50) { /* for double module mode */ + port = 1; + p = block_to_part(val[5], port); + addr_h = 0x40; + } + else if (addr_h == 0x51) { + port = 1; + p = block_to_part(val[5], port); + addr_h = 0x41; + } + addr = (((int32_t)addr_h) << 16 | ((int32_t)addr_m) << 8 | (int32_t)addr_l); + + switch (addr_h) { + case 0x40: + if ((addr & 0xFFF000) == 0x401000) { + switch (addr & 0xFF) { + case 0x00: /* Tone Number */ + SETMIDIEVENT(evm[0], 0, ME_TONE_BANK_MSB, p, val[7], SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_PROGRAM, p, val[8], SYSEX_TAG); + num_events += 2; + break; + case 0x02: /* Rx. Channel */ + if (val[7] == 0x10) { + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, + block_to_part(val[5], + midi_port_number ^ port), 0x80, 0x45); + } + else { + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, + block_to_part(val[5], + midi_port_number ^ port), + MERGE_CHANNEL_PORT2(val[7], + midi_port_number ^ port), 0x45); + } + num_events++; + break; + case 0x03: /* Rx. Pitch Bend */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x48); + num_events++; + break; + case 0x04: /* Rx. Channel Pressure */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x49); + num_events++; + break; + case 0x05: /* Rx. Program Change */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4A); + num_events++; + break; + case 0x06: /* Rx. Control Change */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4B); + num_events++; + break; + case 0x07: /* Rx. Poly Pressure */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4C); + num_events++; + break; + case 0x08: /* Rx. Note Message */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4D); + num_events++; + break; + case 0x09: /* Rx. RPN */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4E); + num_events++; + break; + case 0x0A: /* Rx. NRPN */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4F); + num_events++; + break; + case 0x0B: /* Rx. Modulation */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x50); + num_events++; + break; + case 0x0C: /* Rx. Volume */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x51); + num_events++; + break; + case 0x0D: /* Rx. Panpot */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x52); + num_events++; + break; + case 0x0E: /* Rx. Expression */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x53); + num_events++; + break; + case 0x0F: /* Rx. Hold1 */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x54); + num_events++; + break; + case 0x10: /* Rx. Portamento */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x55); + num_events++; + break; + case 0x11: /* Rx. Sostenuto */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x56); + num_events++; + break; + case 0x12: /* Rx. Soft */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x57); + num_events++; + break; + case 0x13: /* MONO/POLY Mode */ + if (val[7] == 0) { SETMIDIEVENT(evm[0], 0, ME_MONO, p, val[7], SYSEX_TAG); } + else { SETMIDIEVENT(evm[0], 0, ME_POLY, p, val[7], SYSEX_TAG); } + num_events++; + break; + case 0x14: /* Assign Mode */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x24); + num_events++; + break; + case 0x15: /* Use for Rhythm Part */ + if (val[7]) { + rhythm_part[val[7] - 1] = p; + } + break; + case 0x16: /* Pitch Key Shift (dummy. see parse_sysex_event()) */ + break; + case 0x17: /* Pitch Offset Fine */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x26); + num_events++; + break; + case 0x19: /* Part Level */ + SETMIDIEVENT(evm[0], 0, ME_MAINVOLUME, p, val[7], SYSEX_TAG); + num_events++; + break; + case 0x1A: /* Velocity Sense Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x21); + num_events++; + break; + case 0x1B: /* Velocity Sense Offset */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x22); + num_events++; + break; + case 0x1C: /* Part Panpot */ + if (val[7] == 0) { + SETMIDIEVENT(evm[0], 0, ME_RANDOM_PAN, p, 0, SYSEX_TAG); + } + else { + SETMIDIEVENT(evm[0], 0, ME_PAN, p, val[7], SYSEX_TAG); + } + num_events++; + break; + case 0x1D: /* Keyboard Range Low */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x42); + num_events++; + break; + case 0x1E: /* Keyboard Range High */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x43); + num_events++; + break; + case 0x1F: /* CC1 Controller Number */ + //printMessage(CMSG_INFO,VERB_NOISY,"CC1 Controller Number is not supported. (CH:%d VAL:%d)", p, val[7]); + break; + case 0x20: /* CC2 Controller Number */ + //printMessage(CMSG_INFO,VERB_NOISY,"CC2 Controller Number is not supported. (CH:%d VAL:%d)", p, val[7]); + break; + case 0x21: /* Chorus Send Level */ + SETMIDIEVENT(evm[0], 0, ME_CHORUS_EFFECT, p, val[7], SYSEX_TAG); + num_events++; + break; + case 0x22: /* Reverb Send Level */ + SETMIDIEVENT(evm[0], 0, ME_REVERB_EFFECT, p, val[7], SYSEX_TAG); + num_events++; + break; + case 0x23: /* Rx. Bank Select */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x58); + num_events++; + break; + case 0x24: /* Rx. Bank Select LSB */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x59); + num_events++; + break; + case 0x2C: /* Delay Send Level */ + SETMIDIEVENT(evm[0], 0, ME_CELESTE_EFFECT, p, val[7], SYSEX_TAG); + num_events++; + break; + case 0x2A: /* Pitch Fine Tune */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x00, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + SETMIDIEVENT(evm[3], 0, ME_DATA_ENTRY_LSB, p, val[8], SYSEX_TAG); + num_events += 4; + break; + case 0x30: /* TONE MODIFY1: Vibrato Rate */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x08, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x31: /* TONE MODIFY2: Vibrato Depth */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x09, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x32: /* TONE MODIFY3: TVF Cutoff Freq */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x20, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x33: /* TONE MODIFY4: TVF Resonance */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x21, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x34: /* TONE MODIFY5: TVF&TVA Env.attack */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x63, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x35: /* TONE MODIFY6: TVF&TVA Env.decay */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x64, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x36: /* TONE MODIFY7: TVF&TVA Env.release */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x66, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x37: /* TONE MODIFY8: Vibrato Delay */ + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x0A, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x40: /* Scale Tuning */ + for (i = 0; i < 12; i++) { + SETMIDIEVENT(evm[i], + 0, ME_SCALE_TUNING, p, i, val[i + 7] - 64); + } + num_events += 12; + break; + default: + //printMessage(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + else if ((addr & 0xFFF000) == 0x402000) { + switch (addr & 0xFF) { + case 0x00: /* MOD Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x16); + num_events++; + break; + case 0x01: /* MOD TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x17); + num_events++; + break; + case 0x02: /* MOD Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x18); + num_events++; + break; + case 0x03: /* MOD LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x19); + num_events++; + break; + case 0x04: /* MOD LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1A); + num_events++; + break; + case 0x05: /* MOD LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1B); + num_events++; + break; + case 0x06: /* MOD LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1C); + num_events++; + break; + case 0x07: /* MOD LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1D); + num_events++; + break; + case 0x08: /* MOD LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1E); + num_events++; + break; + case 0x09: /* MOD LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1F); + num_events++; + break; + case 0x0A: /* MOD LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x20); + num_events++; + break; + case 0x10: /* !!!FIXME!!! Bend Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_RPN_MSB, p, 0, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_RPN_LSB, p, 0, SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, (val[7] - 0x40) & 0x7F, SYSEX_TAG); + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x21); + num_events += 4; + break; + case 0x11: /* Bend TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x22); + num_events++; + break; + case 0x12: /* Bend Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x23); + num_events++; + break; + case 0x13: /* Bend LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x24); + num_events++; + break; + case 0x14: /* Bend LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x25); + num_events++; + break; + case 0x15: /* Bend LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x26); + num_events++; + break; + case 0x16: /* Bend LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x27); + num_events++; + break; + case 0x17: /* Bend LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x28); + num_events++; + break; + case 0x18: /* Bend LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x29); + num_events++; + break; + case 0x19: /* Bend LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2A); + num_events++; + break; + case 0x1A: /* Bend LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2B); + num_events++; + break; + case 0x20: /* CAf Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x00); + num_events++; + break; + case 0x21: /* CAf TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x01); + num_events++; + break; + case 0x22: /* CAf Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x02); + num_events++; + break; + case 0x23: /* CAf LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x03); + num_events++; + break; + case 0x24: /* CAf LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x04); + num_events++; + break; + case 0x25: /* CAf LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x05); + num_events++; + break; + case 0x26: /* CAf LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x06); + num_events++; + break; + case 0x27: /* CAf LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x07); + num_events++; + break; + case 0x28: /* CAf LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x08); + num_events++; + break; + case 0x29: /* CAf LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x09); + num_events++; + break; + case 0x2A: /* CAf LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0A); + num_events++; + break; + case 0x30: /* PAf Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0B); + num_events++; + break; + case 0x31: /* PAf TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0C); + num_events++; + break; + case 0x32: /* PAf Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0D); + num_events++; + break; + case 0x33: /* PAf LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0E); + num_events++; + break; + case 0x34: /* PAf LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0F); + num_events++; + break; + case 0x35: /* PAf LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x10); + num_events++; + break; + case 0x36: /* PAf LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x11); + num_events++; + break; + case 0x37: /* PAf LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x12); + num_events++; + break; + case 0x38: /* PAf LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x13); + num_events++; + break; + case 0x39: /* PAf LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x14); + num_events++; + break; + case 0x3A: /* PAf LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x15); + num_events++; + break; + case 0x40: /* CC1 Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2C); + num_events++; + break; + case 0x41: /* CC1 TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2D); + num_events++; + break; + case 0x42: /* CC1 Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2E); + num_events++; + break; + case 0x43: /* CC1 LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2F); + num_events++; + break; + case 0x44: /* CC1 LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x30); + num_events++; + break; + case 0x45: /* CC1 LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x31); + num_events++; + break; + case 0x46: /* CC1 LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x32); + num_events++; + break; + case 0x47: /* CC1 LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x33); + num_events++; + break; + case 0x48: /* CC1 LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x34); + num_events++; + break; + case 0x49: /* CC1 LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x35); + num_events++; + break; + case 0x4A: /* CC1 LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x36); + num_events++; + break; + case 0x50: /* CC2 Pitch Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x37); + num_events++; + break; + case 0x51: /* CC2 TVF Cutoff Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x38); + num_events++; + break; + case 0x52: /* CC2 Amplitude Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x39); + num_events++; + break; + case 0x53: /* CC2 LFO1 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3A); + num_events++; + break; + case 0x54: /* CC2 LFO1 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3B); + num_events++; + break; + case 0x55: /* CC2 LFO1 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3C); + num_events++; + break; + case 0x56: /* CC2 LFO1 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3D); + num_events++; + break; + case 0x57: /* CC2 LFO2 Rate Control */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3E); + num_events++; + break; + case 0x58: /* CC2 LFO2 Pitch Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3F); + num_events++; + break; + case 0x59: /* CC2 LFO2 TVF Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x40); + num_events++; + break; + case 0x5A: /* CC2 LFO2 TVA Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x41); + num_events++; + break; + default: + //printMessage(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + else if ((addr & 0xFFFF00) == 0x400100) { + switch (addr & 0xFF) { + case 0x30: /* Reverb Macro */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x05); + num_events++; + break; + case 0x31: /* Reverb Character */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x06); + num_events++; + break; + case 0x32: /* Reverb Pre-LPF */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x07); + num_events++; + break; + case 0x33: /* Reverb Level */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x08); + num_events++; + break; + case 0x34: /* Reverb Time */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x09); + num_events++; + break; + case 0x35: /* Reverb Delay Feedback */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0A); + num_events++; + break; + case 0x36: /* Unknown Reverb Parameter */ + break; + case 0x37: /* Reverb Predelay Time */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0C); + num_events++; + break; + case 0x38: /* Chorus Macro */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0D); + num_events++; + break; + case 0x39: /* Chorus Pre-LPF */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0E); + num_events++; + break; + case 0x3A: /* Chorus Level */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0F); + num_events++; + break; + case 0x3B: /* Chorus Feedback */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x10); + num_events++; + break; + case 0x3C: /* Chorus Delay */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x11); + num_events++; + break; + case 0x3D: /* Chorus Rate */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x12); + num_events++; + break; + case 0x3E: /* Chorus Depth */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x13); + num_events++; + break; + case 0x3F: /* Chorus Send Level to Reverb */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x14); + num_events++; + break; + case 0x40: /* Chorus Send Level to Delay */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x15); + num_events++; + break; + case 0x50: /* Delay Macro */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x16); + num_events++; + break; + case 0x51: /* Delay Pre-LPF */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x17); + num_events++; + break; + case 0x52: /* Delay Time Center */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x18); + num_events++; + break; + case 0x53: /* Delay Time Ratio Left */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x19); + num_events++; + break; + case 0x54: /* Delay Time Ratio Right */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1A); + num_events++; + break; + case 0x55: /* Delay Level Center */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1B); + num_events++; + break; + case 0x56: /* Delay Level Left */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1C); + num_events++; + break; + case 0x57: /* Delay Level Right */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1D); + num_events++; + break; + case 0x58: /* Delay Level */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1E); + num_events++; + break; + case 0x59: /* Delay Feedback */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1F); + num_events++; + break; + case 0x5A: /* Delay Send Level to Reverb */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x20); + num_events++; + break; + default: + //printMessage(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + else if ((addr & 0xFFFF00) == 0x400200) { + switch (addr & 0xFF) { /* EQ Parameter */ + case 0x00: /* EQ LOW FREQ */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x01); + num_events++; + break; + case 0x01: /* EQ LOW GAIN */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x02); + num_events++; + break; + case 0x02: /* EQ HIGH FREQ */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x03); + num_events++; + break; + case 0x03: /* EQ HIGH GAIN */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x04); + num_events++; + break; + default: + //printMessage(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + else if ((addr & 0xFFFF00) == 0x400300) { + switch (addr & 0xFF) { /* Insertion Effect Parameter */ + case 0x00: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x27); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_GS_LSB, p, val[8], 0x28); + num_events += 2; + break; + case 0x03: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x29); + num_events++; + break; + case 0x04: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2A); + num_events++; + break; + case 0x05: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2B); + num_events++; + break; + case 0x06: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2C); + num_events++; + break; + case 0x07: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2D); + num_events++; + break; + case 0x08: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2E); + num_events++; + break; + case 0x09: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2F); + num_events++; + break; + case 0x0A: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x30); + num_events++; + break; + case 0x0B: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x31); + num_events++; + break; + case 0x0C: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x32); + num_events++; + break; + case 0x0D: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x33); + num_events++; + break; + case 0x0E: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x34); + num_events++; + break; + case 0x0F: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x35); + num_events++; + break; + case 0x10: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x36); + num_events++; + break; + case 0x11: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x37); + num_events++; + break; + case 0x12: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x38); + num_events++; + break; + case 0x13: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x39); + num_events++; + break; + case 0x14: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3A); + num_events++; + break; + case 0x15: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3B); + num_events++; + break; + case 0x16: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3C); + num_events++; + break; + case 0x17: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3D); + num_events++; + break; + case 0x18: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3E); + num_events++; + break; + case 0x19: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3F); + num_events++; + break; + case 0x1B: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x40); + num_events++; + break; + case 0x1C: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x41); + num_events++; + break; + case 0x1D: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x42); + num_events++; + break; + case 0x1E: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x43); + num_events++; + break; + case 0x1F: + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x44); + num_events++; + break; + default: + //printMessage(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + else if ((addr & 0xFFF000) == 0x404000) { + switch (addr & 0xFF) { + case 0x00: /* TONE MAP NUMBER */ + SETMIDIEVENT(evm[0], 0, ME_TONE_BANK_LSB, p, val[7], SYSEX_TAG); + num_events++; + break; + case 0x01: /* TONE MAP-0 NUMBER */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x25); + num_events++; + break; + case 0x20: /* EQ ON/OFF */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x00); + num_events++; + break; + case 0x22: /* EFX ON/OFF */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x23); + num_events++; + break; + default: + //printMessage(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + } + break; + case 0x41: + switch (addr & 0xF00) { + case 0x100: /* Play Note Number */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_GS_LSB, dp, val[7], 0x47); + num_events += 2; + break; + case 0x200: + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1A, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x400: + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1C, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x500: + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1D, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x600: + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1E, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x700: /* Rx. Note Off */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x46); + num_events += 2; + break; + case 0x800: /* Rx. Note On */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x47); + num_events += 2; + break; + case 0x900: + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1F, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + default: + //printMessage(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + break; + case 0x21: /* User Drumset */ + switch (addr & 0xF00) { + case 0x100: /* Play Note */ + instruments->get_userdrum(64 + udn, val[6])->play_note = val[7]; + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_GS_LSB, dp, val[7], 0x47); + num_events += 2; + break; + case 0x200: /* Level */ + instruments->get_userdrum(64 + udn, val[6])->level = val[7]; + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1A, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x300: /* Assign Group */ + instruments->get_userdrum(64 + udn, val[6])->assign_group = val[7]; + if (val[7] != 0) { instruments->recompute_userdrum_altassign(udn + 64, val[7]); } + break; + case 0x400: /* Panpot */ + instruments->get_userdrum(64 + udn, val[6])->pan = val[7]; + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1C, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x500: /* Reverb Send Level */ + instruments->get_userdrum(64 + udn, val[6])->reverb_send_level = val[7]; + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1D, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x600: /* Chorus Send Level */ + instruments->get_userdrum(64 + udn, val[6])->chorus_send_level = val[7]; + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1E, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0x700: /* Rx. Note Off */ + instruments->get_userdrum(64 + udn, val[6])->rx_note_off = val[7]; + SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x46); + num_events += 2; + break; + case 0x800: /* Rx. Note On */ + instruments->get_userdrum(64 + udn, val[6])->rx_note_on = val[7]; + SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0); + SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x47); + num_events += 2; + break; + case 0x900: /* Delay Send Level */ + instruments->get_userdrum(64 + udn, val[6])->delay_send_level = val[7]; + SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1F, SYSEX_TAG); + SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG); + SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG); + num_events += 3; + break; + case 0xA00: /* Source Map */ + instruments->get_userdrum(64 + udn, val[6])->source_map = val[7]; + break; + case 0xB00: /* Source Prog */ + instruments->get_userdrum(64 + udn, val[6])->source_prog = val[7]; + break; +#if !defined(TIMIDITY_TOOLS) + case 0xC00: /* Source Note */ + instruments->get_userdrum(64 + udn, val[6])->source_note = val[7]; + break; +#endif + default: + //printMessage(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)", addr_h, addr_m, addr_l, val[7], val[8]); + break; + } + break; + case 0x00: /* System */ + switch (addr & 0xfff0) { + case 0x0100: /* Channel Msg Rx Port (A) */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, + block_to_part(addr & 0xf, 0), val[7], 0x46); + num_events++; + break; + case 0x0110: /* Channel Msg Rx Port (B) */ + SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, + block_to_part(addr & 0xf, 1), val[7], 0x46); + num_events++; + break; + default: + /* printMessage(CMSG_INFO,VERB_NOISY, "Unsupported GS SysEx. " + "(ADDR:%02X %02X %02X VAL:%02X %02X)", + addr_h, addr_m, addr_l, val[7], val[8]);*/ + break; + } + break; + } + } + + /* Non-RealTime / RealTime Universal SysEx messages + * 0 0x7e(Non-RealTime) / 0x7f(RealTime) + * 1 SysEx device ID. Could be from 0x00 to 0x7f. + * 0x7f means disregard device. + * 2 Sub ID + * ... + * E 0xf7 + */ + else if (len > 4 && val[0] >= 0x7e) + switch (val[2]) { + case 0x01: /* Sample Dump header */ + case 0x02: /* Sample Dump packet */ + case 0x03: /* Dump Request */ + case 0x04: /* Device Control */ + if (val[3] == 0x05) { /* Global Parameter Control */ + if (val[7] == 0x01 && val[8] == 0x01) { /* Reverb */ + for (i = 9; i < len && val[i] != 0xf7; i += 2) { + switch (val[i]) { + case 0x00: /* Reverb Type */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, 0, val[i + 1], 0x60); + num_events++; + break; + case 0x01: /* Reverb Time */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x09); + num_events++; + break; + } + } + } + else if (val[7] == 0x01 && val[8] == 0x02) { /* Chorus */ + for (i = 9; i < len && val[i] != 0xf7; i += 2) { + switch (val[i]) { + case 0x00: /* Chorus Type */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, 0, val[i + 1], 0x61); + num_events++; + break; + case 0x01: /* Modulation Rate */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x12); + num_events++; + break; + case 0x02: /* Modulation Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x13); + num_events++; + break; + case 0x03: /* Feedback */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x10); + num_events++; + break; + case 0x04: /* Send To Reverb */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x14); + num_events++; + break; + } + } + } + } + break; + case 0x05: /* Sample Dump extensions */ + case 0x06: /* Inquiry Message */ + case 0x07: /* File Dump */ + break; + case 0x08: /* MIDI Tuning Standard */ + switch (val[3]) { + case 0x01: + SETMIDIEVENT(evm[0], 0, ME_BULK_TUNING_DUMP, 0, val[4], 0); + for (i = 0; i < 128; i++) { + SETMIDIEVENT(evm[i * 2 + 1], 0, ME_BULK_TUNING_DUMP, + 1, i, val[i * 3 + 21]); + SETMIDIEVENT(evm[i * 2 + 2], 0, ME_BULK_TUNING_DUMP, + 2, val[i * 3 + 22], val[i * 3 + 23]); + } + num_events += 257; + break; + case 0x02: + SETMIDIEVENT(evm[0], 0, ME_SINGLE_NOTE_TUNING, + 0, val[4], 0); + for (i = 0; i < val[5]; i++) { + SETMIDIEVENT(evm[i * 2 + 1], 0, ME_SINGLE_NOTE_TUNING, + 1, val[i * 4 + 6], val[i * 4 + 7]); + SETMIDIEVENT(evm[i * 2 + 2], 0, ME_SINGLE_NOTE_TUNING, + 2, val[i * 4 + 8], val[i * 4 + 9]); + } + num_events += val[5] * 2 + 1; + break; + case 0x0b: + channel_tt = ((val[4] & 0x03) << 14 | val[5] << 7 | val[6]) + << ((val[4] >> 2) * 16); + if (val[1] == 0x7f) { + SETMIDIEVENT(evm[0], 0, ME_MASTER_TEMPER_TYPE, + 0, val[7], (val[0] == 0x7f)); + num_events++; + } + else { + for (i = j = 0; i < 32; i++) + if (channel_tt & 1 << i) { + SETMIDIEVENT(evm[j], 0, ME_TEMPER_TYPE, + MERGE_CHANNEL_PORT(i), + val[7], (val[0] == 0x7f)); + j++; + } + num_events += j; + } + break; + case 0x0c: + SETMIDIEVENT(evm[0], 0, ME_USER_TEMPER_ENTRY, + 0, val[4], val[21]); + for (i = 0; i < val[21]; i++) { + SETMIDIEVENT(evm[i * 5 + 1], 0, ME_USER_TEMPER_ENTRY, + 1, val[i * 10 + 22], val[i * 10 + 23]); + SETMIDIEVENT(evm[i * 5 + 2], 0, ME_USER_TEMPER_ENTRY, + 2, val[i * 10 + 24], val[i * 10 + 25]); + SETMIDIEVENT(evm[i * 5 + 3], 0, ME_USER_TEMPER_ENTRY, + 3, val[i * 10 + 26], val[i * 10 + 27]); + SETMIDIEVENT(evm[i * 5 + 4], 0, ME_USER_TEMPER_ENTRY, + 4, val[i * 10 + 28], val[i * 10 + 29]); + SETMIDIEVENT(evm[i * 5 + 5], 0, ME_USER_TEMPER_ENTRY, + 5, val[i * 10 + 30], val[i * 10 + 31]); + } + num_events += val[21] * 5 + 1; + break; + } + break; + case 0x09: /* General MIDI Message */ + switch (val[3]) { + case 0x01: /* Channel Pressure */ + for (i = 5; i < len && val[i] != 0xf7; i += 2) { + switch (val[i]) { + case 0x00: /* Pitch Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x00); + num_events++; + break; + case 0x01: /* Filter Cutoff Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x01); + num_events++; + break; + case 0x02: /* Amplitude Control */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x02); + num_events++; + break; + case 0x03: /* LFO Pitch Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x04); + num_events++; + break; + case 0x04: /* LFO Filter Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x05); + num_events++; + break; + case 0x05: /* LFO Amplitude Depth */ + SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x06); + num_events++; + break; + } + } + break; + } + break; + case 0x7b: /* End of File */ + case 0x7c: /* Handshaking Message: Wait */ + case 0x7d: /* Handshaking Message: Cancel */ + case 0x7e: /* Handshaking Message: NAK */ + case 0x7f: /* Handshaking Message: ACK */ + break; + } + + return(num_events); +} + + +int SysexConvert::parse_sysex_event(const uint8_t *val, int32_t len, MidiEvent *ev, Instruments *instruments) +{ + uint16_t vol; + + if (len >= 10 && + val[0] == 0x41 && /* Roland ID */ + val[1] == 0x10 && /* Device ID */ + val[2] == 0x42 && /* GS Model ID */ + val[3] == 0x12) /* Data Set Command */ + { + /* Roland GS-Based Synthesizers. + * val[4..6] is address, val[7..len-2] is body. + * + * GS Channel part number + * 0 10 + * 1-9 1-9 + * 10-15 11-16 + */ + + int32_t addr, checksum, i; /* SysEx address */ + const uint8_t *body; /* SysEx body */ + uint8_t p, gslen; /* Channel part number [0..15] */ + + /* check Checksum */ + checksum = 0; + for (gslen = 9; gslen < len; gslen++) + if (val[gslen] == 0xF7) + break; + for (i = 4; i < gslen - 1; i++) { + checksum += val[i]; + } + if (((128 - (checksum & 0x7F)) & 0x7F) != val[gslen - 1]) { + return 0; + } + + addr = (((int32_t)val[4]) << 16 | + ((int32_t)val[5]) << 8 | + (int32_t)val[6]); + body = val + 7; + p = (uint8_t)((addr >> 8) & 0xF); + if (p == 0) + p = 9; + else if (p <= 9) + p--; + p = MERGE_CHANNEL_PORT(p); + + if (val[4] == 0x50) { /* for double module mode */ + p += 16; + addr = (((int32_t)0x40) << 16 | + ((int32_t)val[5]) << 8 | + (int32_t)val[6]); + } + else { /* single module mode */ + addr = (((int32_t)val[4]) << 16 | + ((int32_t)val[5]) << 8 | + (int32_t)val[6]); + } + + if ((addr & 0xFFF0FF) == 0x401015) /* Rhythm Parts */ + { + /* GS drum part check from Masaaki Koyanagi's patch (GS_Drum_Part_Check()) */ + /* Modified by Masanao Izumo */ + SETMIDIEVENT(*ev, 0, ME_DRUMPART, p, *body, SYSEX_TAG); + return 1; + } + + if ((addr & 0xFFF0FF) == 0x401016) /* Key Shift */ + { + SETMIDIEVENT(*ev, 0, ME_KEYSHIFT, p, *body, SYSEX_TAG); + return 1; + } + + if (addr == 0x400000) /* Master Tune, not for SMF */ + { + uint16_t tune = ((body[1] & 0xF) << 8) | ((body[2] & 0xF) << 4) | (body[3] & 0xF); + + if (tune < 0x18) + tune = 0x18; + else if (tune > 0x7E8) + tune = 0x7E8; + SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune & 0xFF, (tune >> 8) & 0x7F); + return 1; + } + + if (addr == 0x400004) /* Master Volume */ + { + vol = gs_convert_master_vol(*body); + SETMIDIEVENT(*ev, 0, ME_MASTER_VOLUME, + 0, vol & 0xFF, (vol >> 8) & 0xFF); + return 1; + } + + if ((addr & 0xFFF0FF) == 0x401019) /* Volume on/off */ + { +#if 0 + SETMIDIEVENT(*ev, 0, ME_VOLUME_ONOFF, p, *body >= 64, SYSEX_TAG); +#endif + return 0; + } + + if ((addr & 0xFFF0FF) == 0x401002) /* Receive channel on/off */ + { +#if 0 + SETMIDIEVENT(*ev, 0, ME_RECEIVE_CHANNEL, (uint8_t)p, *body >= 64, SYSEX_TAG); +#endif + return 0; + } + + if (0x402000 <= addr && addr <= 0x402F5A) /* Controller Routing */ + return 0; + + if ((addr & 0xFFF0FF) == 0x401040) /* Alternate Scale Tunings */ + return 0; + + if ((addr & 0xFFFFF0) == 0x400130) /* Changing Effects */ + { +#if 0 + struct chorus_text_gs_t *chorus_text = &(reverb->chorus_status_gs.text); + switch (addr & 0xF) + { + case 0x8: /* macro */ + memcpy(chorus_text->macro, body, 3); + break; + case 0x9: /* PRE-LPF */ + memcpy(chorus_text->pre_lpf, body, 3); + break; + case 0xa: /* level */ + memcpy(chorus_text->level, body, 3); + break; + case 0xb: /* feed back */ + memcpy(chorus_text->feed_back, body, 3); + break; + case 0xc: /* delay */ + memcpy(chorus_text->delay, body, 3); + break; + case 0xd: /* rate */ + memcpy(chorus_text->rate, body, 3); + break; + case 0xe: /* depth */ + memcpy(chorus_text->depth, body, 3); + break; + case 0xf: /* send level */ + memcpy(chorus_text->send_level, body, 3); + break; + default: break; + } +#endif + return 0; + } + + if ((addr & 0xFFF0FF) == 0x401003) /* Rx Pitch-Bend */ + return 0; + + if (addr == 0x400110) /* Voice Reserve */ + { +#if 0 + if (len >= 25) + memcpy(reverb->chorus_status_gs.text.voice_reserve, body, 18); +#endif + return 0; + } + + if (addr == 0x40007F || /* GS Reset */ + addr == 0x00007F) /* SC-88 Single Module */ + { + SETMIDIEVENT(*ev, 0, ME_RESET, 0, GS_SYSTEM_MODE, SYSEX_TAG); + return 1; + } + return 0; + } + + if (len > 9 && + val[0] == 0x41 && /* Roland ID */ + val[1] == 0x10 && /* Device ID */ + val[2] == 0x45 && + val[3] == 0x12 && + val[4] == 0x10 && + val[5] == 0x00 && + val[6] == 0x00) + { + return 0; + } + + if (len > 9 && /* GS lcd event. by T.Nogami*/ + val[0] == 0x41 && /* Roland ID */ + val[1] == 0x10 && /* Device ID */ + val[2] == 0x45 && + val[3] == 0x12 && + val[4] == 0x10 && + val[5] == 0x01 && + val[6] == 0x00) + { + return 0; + } + + /* val[1] can have values other than 0x10 for the XG ON event, which + * work on real XG hardware. I have several midi that use 0x1f instead + * of 0x10. playmidi.h lists 0x10 - 0x13 as MU50/80/90/100. I don't + * know what real world Device Number 0x1f would correspond to, but the + * XG spec says the entire 0x1n range is valid, and 0x1f works on real + * hardware, so I have modified the check below to accept the entire + * 0x1n range. + * + * I think there are/were some hacks somewhere in playmidi.c (?) to work + * around non- 0x10 values, but this fixes the root of the problem, which + * allows the server mode to handle XG initialization properly as well. + */ + if (len >= 8 && + val[0] == 0x43 && + (val[1] >= 0x10 && val[1] <= 0x1f) && + val[2] == 0x4C) + { + int addr = (val[3] << 16) | (val[4] << 8) | val[5]; + + if (addr == 0x00007E) /* XG SYSTEM ON */ + { + SETMIDIEVENT(*ev, 0, ME_RESET, 0, XG_SYSTEM_MODE, SYSEX_TAG); + return 1; + } + else if (addr == 0x000000 && len >= 12) /* XG Master Tune */ + { + uint16_t tune = ((val[7] & 0xF) << 8) | ((val[8] & 0xF) << 4) | (val[9] & 0xF); + + if (tune > 0x7FF) + tune = 0x7FF; + SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune & 0xFF, (tune >> 8) & 0x7F); + return 1; + } + } + + if (len >= 7 && val[0] == 0x7F && val[1] == 0x7F) + { + if (val[2] == 0x04 && val[3] == 0x03) /* GM2 Master Fine Tune */ + { + uint16_t tune = (val[4] & 0x7F) | (val[5] << 7) | 0x4000; + + SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune & 0xFF, (tune >> 8) & 0x7F); + return 1; + } + if (val[2] == 0x04 && val[3] == 0x04) /* GM2 Master Coarse Tune */ + { + uint8_t tune = val[5]; + + if (tune < 0x28) + tune = 0x28; + else if (tune > 0x58) + tune = 0x58; + SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune, 0x80); + return 1; + } + } + + /* Non-RealTime / RealTime Universal SysEx messages + * 0 0x7e(Non-RealTime) / 0x7f(RealTime) + * 1 SysEx device ID. Could be from 0x00 to 0x7f. + * 0x7f means disregard device. + * 2 Sub ID + * ... + * E 0xf7 + */ + if (len > 4 && val[0] >= 0x7e) + switch (val[2]) { + case 0x01: /* Sample Dump header */ + case 0x02: /* Sample Dump packet */ + case 0x03: /* Dump Request */ + break; + case 0x04: /* MIDI Time Code Setup/Device Control */ + switch (val[3]) { + case 0x01: /* Master Volume */ + vol = gm_convert_master_vol(val[4], val[5]); + if (val[1] == 0x7f) { + SETMIDIEVENT(*ev, 0, ME_MASTER_VOLUME, 0, + vol & 0xff, vol >> 8 & 0xff); + } + else { + SETMIDIEVENT(*ev, 0, ME_MAINVOLUME, + MERGE_CHANNEL_PORT(val[1]), + vol >> 8 & 0xff, 0); + } + return 1; + } + break; + case 0x05: /* Sample Dump extensions */ + case 0x06: /* Inquiry Message */ + case 0x07: /* File Dump */ + break; + case 0x08: /* MIDI Tuning Standard */ + switch (val[3]) { + case 0x0a: + SETMIDIEVENT(*ev, 0, ME_TEMPER_KEYSIG, 0, + val[4] - 0x40 + val[5] * 16, (val[0] == 0x7f)); + return 1; + } + break; + case 0x09: /* General MIDI Message */ + /* GM System Enable/Disable */ + if (val[3] == 1) { + printMessage(CMSG_INFO, VERB_DEBUG, "SysEx: GM System On"); + SETMIDIEVENT(*ev, 0, ME_RESET, 0, GM_SYSTEM_MODE, 0); + } + else if (val[3] == 3) { + printMessage(CMSG_INFO, VERB_DEBUG, "SysEx: GM2 System On"); + SETMIDIEVENT(*ev, 0, ME_RESET, 0, GM2_SYSTEM_MODE, 0); + } + else { + printMessage(CMSG_INFO, VERB_DEBUG, "SysEx: GM System Off"); + SETMIDIEVENT(*ev, 0, ME_RESET, 0, DEFAULT_SYSTEM_MODE, 0); + } + return 1; + case 0x7b: /* End of File */ + case 0x7c: /* Handshaking Message: Wait */ + case 0x7d: /* Handshaking Message: Cancel */ + case 0x7e: /* Handshaking Message: NAK */ + case 0x7f: /* Handshaking Message: ACK */ + break; + } + + return 0; +} +} \ No newline at end of file diff --git a/thirdparty/timidityplus/recache.cpp b/thirdparty/timidityplus/recache.cpp new file mode 100644 index 000000000..28ac87676 --- /dev/null +++ b/thirdparty/timidityplus/recache.cpp @@ -0,0 +1,438 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + recache.c + + Code related to resample cache. +*/ + +#include +#include + +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "tables.h" +#include "recache.h" +#include "resample.h" + +namespace TimidityPlus +{ + +#define CACHE_DATA_LEN (allocate_cache_size / sizeof(sample_t)) + +inline uint32_t sp_hash(Sample *sp, int note) +{ + return ((uint32_t)(intptr_t)(sp)+(uint32_t)(note)); +} + + +#define RESAMPLATION_CACHE _x = do_resamplation(src, ofs, &resrc); \ + dest[i] = (int16_t) ((_x > 32767) ? 32767 \ + : ((_x < -32768) ? -32768 : _x)) + + + + +void Recache::free_cache_data(void) { + free(cache_data); + reuse_mblock(&hash_entry_pool); +} + +void Recache::resamp_cache_reset(void) +{ + if (cache_data == NULL) { + cache_data = (sample_t *) + safe_large_malloc((CACHE_DATA_LEN + 1) * sizeof(sample_t)); + memset(cache_data, 0, (CACHE_DATA_LEN + 1) * sizeof(sample_t)); + init_mblock(&hash_entry_pool); + } + cache_data_len = 0; + memset(cache_hash_table, 0, sizeof(cache_hash_table)); + memset(channel_note_table, 0, sizeof(channel_note_table)); + reuse_mblock(&hash_entry_pool); +} + +struct cache_hash *Recache::resamp_cache_fetch(Sample *sp, int note) +{ + unsigned int addr; + struct cache_hash *p; + + if (sp->vibrato_control_ratio || (sp->modes & MODES_PINGPONG) + || (sp->sample_rate == playback_rate + && sp->root_freq == get_note_freq(sp, sp->note_to_use))) + return NULL; + addr = sp_hash(sp, note) % HASH_TABLE_SIZE; + p = cache_hash_table[addr]; + while (p && (p->note != note || p->sp != sp)) + p = p->next; + if (p && p->resampled != NULL) + return p; + return NULL; +} + +void Recache::resamp_cache_refer_on(Voice *vp, int32_t sample_start) +{ + unsigned int addr; + struct cache_hash *p; + int note, ch; + + ch = vp->channel; + if (vp->vibrato_control_ratio || player->channel[ch].portamento + || (vp->sample->modes & MODES_PINGPONG) + || vp->orig_frequency != vp->frequency + || (vp->sample->sample_rate == playback_rate + && vp->sample->root_freq + == get_note_freq(vp->sample, vp->sample->note_to_use))) + return; + note = vp->note; + if (channel_note_table[ch].cache[note]) + resamp_cache_refer_off(ch, note, sample_start); + addr = sp_hash(vp->sample, note) % HASH_TABLE_SIZE; + p = cache_hash_table[addr]; + while (p && (p->note != note || p->sp != vp->sample)) + p = p->next; + if (! p) { + p = (struct cache_hash *) + new_segment(&hash_entry_pool, sizeof(struct cache_hash)); + p->cnt = 0; + p->note = vp->note; + p->sp = vp->sample; + p->resampled = NULL; + p->next = cache_hash_table[addr]; + cache_hash_table[addr] = p; + } + channel_note_table[ch].cache[note] = p; + channel_note_table[ch].on[note] = sample_start; +} + +void Recache::resamp_cache_refer_off(int ch, int note, int32_t sample_end) +{ + int32_t sample_start, len; + struct cache_hash *p; + Sample *sp; + + p = channel_note_table[ch].cache[note]; + if (p == NULL) + return; + sp = p->sp; + if (sp->sample_rate == playback_rate + && sp->root_freq == get_note_freq(sp, sp->note_to_use)) + return; + sample_start = channel_note_table[ch].on[note]; + len = sample_end - sample_start; + if (len < 0) { + channel_note_table[ch].cache[note] = NULL; + return; + } + if (! (sp->modes & MODES_LOOPING)) { + double a; + int32_t slen; + + a = ((double) sp->root_freq * playback_rate) + / ((double) sp->sample_rate * get_note_freq(sp, note)); + slen = (int32_t) ((sp->data_length >> FRACTION_BITS) * a); + if (len > slen) + len = slen; + } + p->cnt += len; + channel_note_table[ch].cache[note] = NULL; +} + +void Recache::resamp_cache_refer_alloff(int ch, int32_t sample_end) +{ + int i; + + for (i = 0; i < 128; i++) + resamp_cache_refer_off(ch, i, sample_end); +} + +void Recache::resamp_cache_create(void) +{ + int i, skip; + int32_t n, t1, t2, total; + struct cache_hash **array; + + /* It is NP completion that solve the best cache hit rate. + * So I thought better algorism O(n log n), but not a best solution. + * Follows implementation takes good hit rate, and it is fast. + */ + n = t1 = t2 = 0; + total = 0; + /* set size per count */ + for (i = 0; i < HASH_TABLE_SIZE; i++) { + struct cache_hash *p, *q; + + p = cache_hash_table[i], q = NULL; + while (p) { + struct cache_hash *tmp; + + t1 += p->cnt; + tmp = p, p = p->next; + if (tmp->cnt > 0) { + Sample *sp; + splen_t newlen; + + sp = tmp->sp; + sample_resamp_info(sp, tmp->note, NULL, NULL, &newlen); + if (newlen > 0) { + total += tmp->cnt; + tmp->r = (double) newlen / tmp->cnt; + tmp->next = q, q = tmp; + n++; + } + } + } + cache_hash_table[i] = q; + } + if (n == 0) { + return; + } + array = (struct cache_hash **) new_segment(&hash_entry_pool, + n * sizeof(struct cache_hash *)); + n = 0; + for (i = 0; i < HASH_TABLE_SIZE; i++) { + struct cache_hash *p; + + for (p = cache_hash_table[i]; p; p = p->next) + array[n++] = p; + } + if ((uint32_t)total > CACHE_DATA_LEN) + qsort_cache_array(array, 0, n - 1); + skip = 0; + for (i = 0; i < n; i++) { + if (array[i]->r != 0 + && cache_resampling(array[i]) == CACHE_RESAMPLING_OK) + t2 += array[i]->cnt; + else + skip++; + } + /* update cache_hash_table */ + if (skip) + for (i = 0; i < HASH_TABLE_SIZE; i++) { + struct cache_hash *p, *q; + + p = cache_hash_table[i], q = NULL; + while (p) { + struct cache_hash *tmp; + + tmp = p, p = p->next; + if (tmp->resampled) + tmp->next = q, q = tmp; + } + cache_hash_table[i] = q; + } +} + +double Recache::sample_resamp_info(Sample *sp, int note, + splen_t *loop_start, splen_t *loop_end, splen_t *data_length) +{ + splen_t xls, xle, ls, le, ll, newlen; + double a, xxls, xxle, xn; + + a = ((double) sp->sample_rate * get_note_freq(sp, note)) + / ((double) sp->root_freq * playback_rate); + a = TIM_FSCALENEG((double) (int32_t) TIM_FSCALE(a, FRACTION_BITS), + FRACTION_BITS); + xn = sp->data_length / a; + if (xn >= SPLEN_T_MAX) { + /* Ignore this sample */ + *data_length = 0; + return 0.0; + } + newlen = (splen_t) (TIM_FSCALENEG(xn, FRACTION_BITS) + 0.5); + ls = sp->loop_start; + le = sp->loop_end; + ll = le - ls; + xxls = ls / a + 0.5; + if (xxls >= SPLEN_T_MAX) { + /* Ignore this sample */ + *data_length = 0; + return 0.0; + } + xls = (splen_t) xxls; + xxle = le / a + 0.5; + if (xxle >= SPLEN_T_MAX) { + /* Ignore this sample */ + *data_length = 0; + return 0.0; + } + xle = (splen_t) xxle; + if ((sp->modes & MODES_LOOPING) + && ((xle - xls) >> FRACTION_BITS) < MIN_LOOPLEN) { + splen_t n; + splen_t newxle; + double xl; /* Resampled new loop length */ + double xnewxle; + + xl = ll / a; + if (xl >= SPLEN_T_MAX) { + /* Ignore this sample */ + *data_length = 0; + return 0.0; + } + n = (splen_t) (0.0001 + MIN_LOOPLEN + / TIM_FSCALENEG(xl, FRACTION_BITS)) + 1; + xnewxle = le / a + n * xl + 0.5; + if (xnewxle >= SPLEN_T_MAX) { + /* Ignore this sample */ + *data_length = 0; + return 0.0; + } + newxle = (splen_t) xnewxle; + newlen += (newxle - xle) >> FRACTION_BITS; + xle = newxle; + } + if (loop_start) + *loop_start = (splen_t) (xls & ~FRACTION_MASK); + if (loop_end) + *loop_end = (splen_t) (xle & ~FRACTION_MASK); + *data_length = newlen << FRACTION_BITS; + return a; +} + +void Recache::qsort_cache_array(struct cache_hash **a, int32_t first, int32_t last) +{ + int32_t i = first, j = last; + struct cache_hash *x, *t; + + if (j - i < SORT_THRESHOLD) { + insort_cache_array(a + i, j - i + 1); + return; + } + x = a[(first + last) / 2]; + for (;;) { + while (a[i]->r < x->r) + i++; + while (x->r < a[j]->r) + j--; + if (i >= j) + break; + t = a[i], a[i] = a[j], a[j] = t; + i++, j--; + } + if (first < i - 1) + qsort_cache_array(a, first, i - 1); + if (j + 1 < last) + qsort_cache_array(a, j + 1, last); +} + +void Recache::insort_cache_array(struct cache_hash **data, int32_t n) +{ + int32_t i, j; + struct cache_hash *x; + + for (i = 1; i < n; i++) { + x = data[i]; + for (j = i - 1; j >= 0 && x->r < data[j]->r; j--) + data[j + 1] = data[j]; + data[j + 1] = x; + } +} + +int Recache::cache_resampling(struct cache_hash *p) +{ + Sample *sp, *newsp; + sample_t *src, *dest; + splen_t newlen, ofs, le, ls, ll, xls, xle; + int32_t incr, _x; + resample_rec_t resrc; + double a; + int8_t note; + + sp = p->sp; + if (sp->note_to_use) + note = sp->note_to_use; + else + note = p->note; + a = sample_resamp_info(sp, note, &xls, &xle, &newlen); + if (newlen == 0) + return CACHE_RESAMPLING_NOTOK; + newlen >>= FRACTION_BITS; + if (cache_data_len + newlen + 1 > CACHE_DATA_LEN) + return CACHE_RESAMPLING_NOTOK; + resrc.loop_start = ls = sp->loop_start; + resrc.loop_end = le = sp->loop_end; + resrc.data_length = sp->data_length; + ll = sp->loop_end - sp->loop_start; + dest = cache_data + cache_data_len; + src = sp->data; + newsp = (Sample *) new_segment(&hash_entry_pool, sizeof(Sample)); + memcpy(newsp, sp, sizeof(Sample)); + newsp->data = dest; + ofs = 0; + incr = (splen_t) (TIM_FSCALE(a, FRACTION_BITS) + 0.5); + if (sp->modes & MODES_LOOPING) + for (splen_t i = 0; i < newlen; i++) { + if (ofs >= le) + ofs -= ll; + RESAMPLATION_CACHE; + ofs += incr; + } + else + for (splen_t i = 0; i < newlen; i++) { + RESAMPLATION_CACHE; + ofs += incr; + } + newsp->loop_start = xls; + newsp->loop_end = xle; + newsp->data_length = newlen << FRACTION_BITS; + if (sp->modes & MODES_LOOPING) + loop_connect(dest, (int32_t) (xls >> FRACTION_BITS), + (int32_t) (xle >> FRACTION_BITS)); + dest[xle >> FRACTION_BITS] = dest[xls >> FRACTION_BITS]; + newsp->root_freq = get_note_freq(newsp, note); + newsp->sample_rate = playback_rate; + p->resampled = newsp; + cache_data_len += newlen + 1; + return CACHE_RESAMPLING_OK; +} + +void Recache::loop_connect(sample_t *data, int32_t start, int32_t end) +{ + int i, mixlen; + int32_t t0, t1; + + mixlen = MIXLEN; + if (start < mixlen) + mixlen = start; + if (end - start < mixlen) + mixlen = end - start; + if (mixlen <= 0) + return; + t0 = start - mixlen; + t1 = end - mixlen; + for (i = 0; i < mixlen; i++) { + double x, b; + + b = i / (double) mixlen; /* 0 <= b < 1 */ + x = b * data[t0 + i] + (1.0 - b) * data[t1 + i]; + if (x < -32768) + data[t1 + i] = -32768; + else if (x > 32767) + data[t1 + i] = 32767; + else + data[t1 + i] = (sample_t) x; + } +} + +} \ No newline at end of file diff --git a/thirdparty/timidityplus/resample.cpp b/thirdparty/timidityplus/resample.cpp new file mode 100644 index 000000000..5cc1bfd1d --- /dev/null +++ b/thirdparty/timidityplus/resample.cpp @@ -0,0 +1,970 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + resample.c +*/ + +#include +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "tables.h" +#include "resample.h" +#include "recache.h" + +namespace TimidityPlus +{ + + +/* for start/end of samples */ +static float newt_coeffs[58][58]; +static int sample_bounds_min, sample_bounds_max; /* min/max bounds for sample data */ + +#define DEFAULT_GAUSS_ORDER 25 +std::vector gauss_table_data; +static float *gauss_table[(1 << FRACTION_BITS)] = { 0 }; /* don't need doubles */ +static int gauss_n = DEFAULT_GAUSS_ORDER; + + +static void initialize_newton_coeffs() +{ + int i, j, n = 57; + int sign; + + newt_coeffs[0][0] = 1; + for (i = 0; i <= n; i++) + { + newt_coeffs[i][0] = 1; + newt_coeffs[i][i] = 1; + + if (i > 1) + { + newt_coeffs[i][0] = newt_coeffs[i - 1][0] / i; + newt_coeffs[i][i] = newt_coeffs[i - 1][0] / i; + } + + for (j = 1; j < i; j++) + { + newt_coeffs[i][j] = newt_coeffs[i - 1][j - 1] + newt_coeffs[i - 1][j]; + + if (i > 1) + newt_coeffs[i][j] /= i; + } + } + for (i = 0; i <= n; i++) + for (j = 0, sign = pow(-1, i); j <= i; j++, sign *= -1) + newt_coeffs[i][j] *= sign; + +} + + + +/* Very fast and accurate table based interpolation. Better speed and higher + accuracy than Newton. This isn't *quite* true Gauss interpolation; it's + more a slightly modified Gauss interpolation that I accidently stumbled + upon. Rather than normalize all x values in the window to be in the range + [0 to 2*PI], it simply divides them all by 2*PI instead. I don't know why + this works, but it does. Gauss should only work on periodic data with the + window spanning exactly one period, so it is no surprise that regular Gauss + interpolation doesn't work too well on general audio data. But dividing + the x values by 2*PI magically does. Any other scaling produces degraded + results or total garbage. If anyone can work out the theory behind why + this works so well (at first glance, it shouldn't ??), please contact me + (Eric A. Welsh, ewelsh@ccb.wustl.edu), as I would really like to have some + mathematical justification for doing this. Despite the lack of any sound + theoretical basis, this method DOES result in highly accurate interpolation + (or possibly approximaton, not sure yet if it truly interpolates, but it + looks like it does). -N 34 is as high as it can go before errors start + appearing. But even at -N 34, it is more accurate than Newton at -N 57. + -N 34 has no problem running in realtime on my system, but -N 25 is the + default, since that is the optimal compromise between speed and accuracy. + I strongly recommend using Gauss interpolation. It is the highest + quality interpolation option available, and is much faster than using + Newton polynomials. */ + + +static resample_t resample_gauss(sample_t *src, splen_t ofs, resample_rec_t *rec) +{ + sample_t *sptr; + int32_t left, right, temp_n; + + left = (ofs >> FRACTION_BITS); + right = (rec->data_length >> FRACTION_BITS) - left - 1; + temp_n = (right << 1) - 1; + if (temp_n > (left << 1) + 1) + temp_n = (left << 1) + 1; + if (temp_n < gauss_n) { + int ii, jj; + float xd, y; + if (temp_n <= 0) + temp_n = 1; + xd = ofs & FRACTION_MASK; + xd /= (1L << FRACTION_BITS); + xd += temp_n >> 1; + y = 0; + sptr = src + (ofs >> FRACTION_BITS) - (temp_n >> 1); + for (ii = temp_n; ii;) { + for (jj = 0; jj <= ii; jj++) + y += sptr[jj] * newt_coeffs[ii][jj]; + y *= xd - --ii; + } + y += *sptr; + return ((y > sample_bounds_max) ? sample_bounds_max : + ((y < sample_bounds_min) ? sample_bounds_min : y)); + } + else { + float *gptr, *gend; + float y; + y = 0; + sptr = src + left - (gauss_n >> 1); + gptr = gauss_table[ofs&FRACTION_MASK]; + if (gauss_n == DEFAULT_GAUSS_ORDER) { + /* expanding the loop for the default case. + * this will allow intensive optimization when compiled + * with SSE2 capability. + */ +#define do_gauss y += *(sptr++) * *(gptr++); + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + do_gauss; + y += *sptr * *gptr; +#undef do_gauss + } + else { + gend = gptr + gauss_n; + do { + y += *(sptr++) * *(gptr++); + } while (gptr <= gend); + } + return ((y > sample_bounds_max) ? sample_bounds_max : + ((y < sample_bounds_min) ? sample_bounds_min : y)); + } +} + + +#define RESAMPLATION *dest++ = resample_gauss(src, ofs, &resrc); + +/* exported for recache.c */ +resample_t do_resamplation(sample_t *src, splen_t ofs, resample_rec_t *rec) +{ + return resample_gauss(src, ofs, rec); +} + +#define PRECALC_LOOP_COUNT(start, end, incr) (int32_t)(((int64_t)((end) - (start) + (incr) - 1)) / (incr)) + +void initialize_gauss_table(int n) +{ + int m, i, k, n_half = (n >> 1); + double ck; + double x, x_inc, xz; + double z[35], zsin_[34 + 35], *zsin, xzsin[35]; + float *gptr; + + for (i = 0; i <= n; i++) + z[i] = i / (4 * M_PI); + zsin = &zsin_[34]; + for (i = -n; i <= n; i++) + zsin[i] = sin(i / (4 * M_PI)); + + x_inc = 1.0 / (1 << FRACTION_BITS); + + gauss_table_data.resize((n + 1) * sizeof(float) * (1 << FRACTION_BITS)); + gptr = gauss_table_data.data(); + for (m = 0, x = 0.0; m < (1 << FRACTION_BITS); m++, x += x_inc) + { + xz = (x + n_half) / (4 * M_PI); + for (i = 0; i <= n; i++) + xzsin[i] = sin(xz - z[i]); + gauss_table[m] = gptr; + + for (k = 0; k <= n; k++) + { + ck = 1.0; + + for (i = 0; i <= n; i++) + { + if (i == k) + continue; + + ck *= xzsin[i] / zsin[k - i]; + } + + *gptr++ = ck; + } + } +} + +void free_gauss_table(void) +{ + if (gauss_table[0] != 0) + free(gauss_table[0]); + gauss_table[0] = NULL; +} + +/* initialize the coefficients of the current resampling algorithm */ +void initialize_resampler_coeffs(void) +{ + // Only needs to be done once. + static bool done = false; + if (done) return; + done = true; + + initialize_newton_coeffs(); + initialize_gauss_table(gauss_n); + + sample_bounds_min = -32768; + sample_bounds_max = 32767; +} + + +/*************** resampling with fixed increment *****************/ + +resample_t *Resampler::rs_plain_c(int v, int32_t *countptr) +{ + Voice *vp = &player->voice[v]; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int32_t ofs, count = *countptr, i, le; + + le = (int32_t)(vp->sample->loop_end >> FRACTION_BITS); + ofs = (int32_t)(vp->sample_offset >> FRACTION_BITS); + + i = ofs + count; + if (i > le) + i = le; + count = i - ofs; + + for (i = 0; i < count; i++) { + dest[i] = src[i + ofs]; + } + + ofs += count; + if (ofs == le) + { + vp->timeout = 1; + *countptr = count; + } + vp->sample_offset = ((splen_t)ofs << FRACTION_BITS); + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_plain(int v, int32_t *countptr) +{ + /* Play sample until end, then free the voice. */ + Voice *vp = &player->voice[v]; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + splen_t + ofs = vp->sample_offset, + ls = 0, + le = vp->sample->data_length; + resample_rec_t resrc; + int32_t count = *countptr, incr = vp->sample_increment; + int32_t i, j; + + if (vp->cache && incr == (1 << FRACTION_BITS)) + return rs_plain_c(v, countptr); + + resrc.loop_start = ls; + resrc.loop_end = le; + resrc.data_length = vp->sample->data_length; + if (incr < 0) incr = -incr; /* In case we're coming out of a bidir loop */ + + /* Precalc how many times we should go through the loop. + NOTE: Assumes that incr > 0 and that ofs <= le */ + i = PRECALC_LOOP_COUNT(ofs, le, incr); + + if (i > count) + { + i = count; + count = 0; + } + else count -= i; + + for (j = 0; j < i; j++) + { + RESAMPLATION; + ofs += incr; + } + + if (ofs >= le) + { + vp->timeout = 1; + *countptr -= count; + } + + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_loop_c(Voice *vp, int32_t count) +{ + int32_t + ofs = (int32_t)(vp->sample_offset >> FRACTION_BITS), + le = (int32_t)(vp->sample->loop_end >> FRACTION_BITS), + ll = le - (int32_t)(vp->sample->loop_start >> FRACTION_BITS); + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int32_t i, j; + + while (count) + { + while (ofs >= le) + ofs -= ll; + /* Precalc how many times we should go through the loop */ + i = le - ofs; + if (i > count) + i = count; + count -= i; + for (j = 0; j < i; j++) { + dest[j] = src[j + ofs]; + } + dest += i; + ofs += i; + } + vp->sample_offset = ((splen_t)ofs << FRACTION_BITS); + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_loop(Voice *vp, int32_t count) +{ + /* Play sample until end-of-loop, skip back and continue. */ + splen_t + ofs = vp->sample_offset, + ls, le, ll; + resample_rec_t resrc; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int32_t i, j; + int32_t incr = vp->sample_increment; + + if (vp->cache && incr == (1 << FRACTION_BITS)) + return rs_loop_c(vp, count); + + resrc.loop_start = ls = vp->sample->loop_start; + resrc.loop_end = le = vp->sample->loop_end; + ll = le - ls; + resrc.data_length = vp->sample->data_length; + + while (count) + { + while (ofs >= le) { ofs -= ll; } + /* Precalc how many times we should go through the loop */ + i = PRECALC_LOOP_COUNT(ofs, le, incr); + if (i > count) { + i = count; + count = 0; + } + else { count -= i; } + for (j = 0; j < i; j++) { + RESAMPLATION; + ofs += incr; + } + } + + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_bidir(Voice *vp, int32_t count) +{ + int32_t + ofs = vp->sample_offset, + le = vp->sample->loop_end, + ls = vp->sample->loop_start; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int32_t incr = vp->sample_increment; + resample_rec_t resrc; + + int32_t + le2 = le << 1, + ls2 = ls << 1; + int32_t i, j; + /* Play normally until inside the loop region */ + + resrc.loop_start = ls; + resrc.loop_end = le; + resrc.data_length = vp->sample->data_length; + + if (incr > 0 && ofs < ls) + { + /* NOTE: Assumes that incr > 0, which is NOT always the case + when doing bidirectional looping. I have yet to see a case + where both ofs <= ls AND incr < 0, however. */ + i = PRECALC_LOOP_COUNT(ofs, ls, incr); + if (i > count) + { + i = count; + count = 0; + } + else count -= i; + for (j = 0; j < i; j++) + { + RESAMPLATION; + ofs += incr; + } + } + + /* Then do the bidirectional looping */ + + while (count) + { + /* Precalc how many times we should go through the loop */ + i = PRECALC_LOOP_COUNT(ofs, incr > 0 ? le : ls, incr); + if (i > count) + { + i = count; + count = 0; + } + else count -= i; + for (j = 0; j < i; j++) + { + RESAMPLATION; + ofs += incr; + } + if (ofs >= 0 && ofs >= le) + { + /* fold the overshoot back in */ + ofs = le2 - ofs; + incr *= -1; + } + else if (ofs <= 0 || ofs <= ls) + { + ofs = ls2 - ofs; + incr *= -1; + } + } + vp->sample_increment = incr; + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer + resample_buffer_offset; +} + +/*********************** vibrato versions ***************************/ + +/* We only need to compute one half of the vibrato sine cycle */ +static int vib_phase_to_inc_ptr(int phase) +{ + if (phase < VIBRATO_SAMPLE_INCREMENTS / 2) + return VIBRATO_SAMPLE_INCREMENTS / 2 - 1 - phase; + else if (phase >= 3 * VIBRATO_SAMPLE_INCREMENTS / 2) + return 5 * VIBRATO_SAMPLE_INCREMENTS / 2 - 1 - phase; + else + return phase - VIBRATO_SAMPLE_INCREMENTS / 2; +} + +int32_t Resampler::update_vibrato(Voice *vp, int sign) +{ + int32_t depth; + int phase, pb; + double a; + int ch = vp->channel; + + if (vp->vibrato_delay > 0) + { + vp->vibrato_delay -= vp->vibrato_control_ratio; + if (vp->vibrato_delay > 0) + return vp->sample_increment; + } + + if (vp->vibrato_phase++ >= 2 * VIBRATO_SAMPLE_INCREMENTS - 1) + vp->vibrato_phase = 0; + phase = vib_phase_to_inc_ptr(vp->vibrato_phase); + + if (vp->vibrato_sample_increment[phase]) + { + if (sign) + return -vp->vibrato_sample_increment[phase]; + else + return vp->vibrato_sample_increment[phase]; + } + + /* Need to compute this sample increment. */ + + depth = vp->vibrato_depth; + depth <<= 7; + + if (vp->vibrato_sweep && !player->channel[ch].mod.val) + { + /* Need to update sweep */ + vp->vibrato_sweep_position += vp->vibrato_sweep; + if (vp->vibrato_sweep_position >= (1 << SWEEP_SHIFT)) + vp->vibrato_sweep = 0; + else + { + /* Adjust depth */ + depth *= vp->vibrato_sweep_position; + depth >>= SWEEP_SHIFT; + } + } + + if (vp->sample->inst_type == INST_SF2) { + pb = (int)((lookup_triangular(vp->vibrato_phase * + (SINE_CYCLE_LENGTH / (2 * VIBRATO_SAMPLE_INCREMENTS))) + * (double)(depth)* VIBRATO_AMPLITUDE_TUNING)); + } + else { + pb = (int)((lookup_sine(vp->vibrato_phase * + (SINE_CYCLE_LENGTH / (2 * VIBRATO_SAMPLE_INCREMENTS))) + * (double)(depth)* VIBRATO_AMPLITUDE_TUNING)); + } + + a = TIM_FSCALE(((double)(vp->sample->sample_rate) * + (double)(vp->frequency)) / + ((double)(vp->sample->root_freq) * + (double)(playback_rate)), + FRACTION_BITS); + + if (pb < 0) { + pb = -pb; + a /= bend_fine[(pb >> 5) & 0xFF] * bend_coarse[pb >> 13]; + pb = -pb; + } + else { + a *= bend_fine[(pb >> 5) & 0xFF] * bend_coarse[pb >> 13]; + } + a += 0.5; + + /* If the sweep's over, we can store the newly computed sample_increment */ + if (!vp->vibrato_sweep || player->channel[ch].mod.val) + vp->vibrato_sample_increment[phase] = (int32_t)a; + + if (sign) + a = -a; /* need to preserve the loop direction */ + + return (int32_t)a; +} + +resample_t *Resampler::rs_vib_plain(int v, int32_t *countptr) +{ + /* Play sample until end, then free the voice. */ + Voice *vp = &player->voice[v]; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + splen_t + ls = 0, + le = vp->sample->data_length, + ofs = vp->sample_offset; + resample_rec_t resrc; + + int32_t count = *countptr, incr = vp->sample_increment; + int cc = vp->vibrato_control_counter; + + resrc.loop_start = ls; + resrc.loop_end = le; + resrc.data_length = vp->sample->data_length; + /* This has never been tested */ + + if (incr < 0) incr = -incr; /* In case we're coming out of a bidir loop */ + + while (count--) + { + if (!cc--) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(vp, 0); + } + RESAMPLATION; + ofs += incr; + if (ofs >= le) + { + vp->timeout = 1; + *countptr -= count; + break; + } + } + + vp->vibrato_control_counter = cc; + vp->sample_increment = incr; + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_vib_loop(Voice *vp, int32_t count) +{ + /* Play sample until end-of-loop, skip back and continue. */ + splen_t + ofs = vp->sample_offset, + ls = vp->sample->loop_start, + le = vp->sample->loop_end, + ll = le - vp->sample->loop_start; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int cc = vp->vibrato_control_counter; + int32_t incr = vp->sample_increment; + resample_rec_t resrc; + int32_t i, j; + int vibflag = 0; + + resrc.loop_start = ls; + resrc.loop_end = le; + resrc.data_length = vp->sample->data_length; + + while (count) + { + /* Hopefully the loop is longer than an increment */ + while (ofs >= le) { ofs -= ll; } + /* Precalc how many times to go through the loop, taking + the vibrato control ratio into account this time. */ + i = PRECALC_LOOP_COUNT(ofs, le, incr); + if (i > count) { + i = count; + } + if (i > cc) { + i = cc; + vibflag = 1; + } + else { cc -= i; } + count -= i; + if (vibflag) { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(vp, 0); + vibflag = 0; + } + for (j = 0; j < i; j++) { + RESAMPLATION; + ofs += incr; + } + } + + vp->vibrato_control_counter = cc; + vp->sample_increment = incr; + vp->sample_offset = ofs; /* Update offset */ + return resample_buffer + resample_buffer_offset; +} + +resample_t *Resampler::rs_vib_bidir(Voice *vp, int32_t count) +{ + int32_t + ofs = vp->sample_offset, + le = vp->sample->loop_end, + ls = vp->sample->loop_start; + resample_t *dest = resample_buffer + resample_buffer_offset; + sample_t *src = vp->sample->data; + int cc = vp->vibrato_control_counter; + int32_t incr = vp->sample_increment; + resample_rec_t resrc; + + + resrc.loop_start = ls; + resrc.loop_end = le; + resrc.data_length = vp->sample->data_length; + /* Play normally until inside the loop region */ + + if (ofs < ls) + { + while (count--) + { + if (!cc--) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(vp, 0); + } + RESAMPLATION; + ofs += incr; + if (ofs >= ls) + break; + } + } + + /* Then do the bidirectional looping */ + + if (count > 0) + while (count--) + { + if (!cc--) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(vp, (incr < 0)); + } + RESAMPLATION; + ofs += incr; + if (ofs >= le) + { + /* fold the overshoot back in */ + ofs = le - (ofs - le); + incr = -incr; + } + else if (ofs <= ls) + { + ofs = ls + (ls - ofs); + incr = -incr; + } + } + + /* Update changed values */ + vp->vibrato_control_counter = cc; + vp->sample_increment = incr; + vp->sample_offset = ofs; + return resample_buffer + resample_buffer_offset; +} + +/*********************** portamento versions ***************************/ + +int Resampler::rs_update_porta(int v) +{ + Voice *vp = &player->voice[v]; + int32_t d; + + d = vp->porta_dpb; + if (vp->porta_pb < 0) + { + if (d > -vp->porta_pb) + d = -vp->porta_pb; + } + else + { + if (d > vp->porta_pb) + d = -vp->porta_pb; + else + d = -d; + } + + vp->porta_pb += d; + if (vp->porta_pb == 0) + { + vp->porta_control_ratio = 0; + vp->porta_pb = 0; + } + player->recompute_freq(v); + return vp->porta_control_ratio; +} + +resample_t *Resampler::porta_resample_voice(int v, int32_t *countptr, int mode) +{ + Voice *vp = &player->voice[v]; + int32_t n = *countptr, i; + resample_t *(Resampler::*resampler)(int, int32_t *, int); + int cc = vp->porta_control_counter; + int loop; + + if (vp->vibrato_control_ratio) + resampler = &Resampler::vib_resample_voice; + else + resampler = &Resampler::normal_resample_voice; + if (mode != 1) + loop = 1; + else + loop = 0; + + vp->cache = NULL; + resample_buffer_offset = 0; + while (resample_buffer_offset < n) + { + if (cc == 0) + { + if ((cc = rs_update_porta(v)) == 0) + { + i = n - resample_buffer_offset; + (this->*resampler)(v, &i, mode); + resample_buffer_offset += i; + break; + } + } + + i = n - resample_buffer_offset; + if (i > cc) + i = cc; + (this->*resampler)(v, &i, mode); + resample_buffer_offset += i; + + if (!loop && (i == 0 || vp->status == VOICE_FREE)) + break; + cc -= i; + } + *countptr = resample_buffer_offset; + resample_buffer_offset = 0; + vp->porta_control_counter = cc; + return resample_buffer; +} + +/* interface function */ +resample_t *Resampler::vib_resample_voice(int v, int32_t *countptr, int mode) +{ + Voice *vp = &player->voice[v]; + + vp->cache = NULL; + if (mode == 0) + return rs_vib_loop(vp, *countptr); + if (mode == 1) + return rs_vib_plain(v, countptr); + return rs_vib_bidir(vp, *countptr); +} + +/* interface function */ +resample_t *Resampler::normal_resample_voice(int v, int32_t *countptr, int mode) +{ + Voice *vp = &player->voice[v]; + if (mode == 0) + return rs_loop(vp, *countptr); + if (mode == 1) + return rs_plain(v, countptr); + return rs_bidir(vp, *countptr); +} + +/* interface function */ +resample_t *Resampler::resample_voice(int v, int32_t *countptr) +{ + Voice *vp = &player->voice[v]; + int mode; + resample_t *result; + int32_t i; + + if (vp->sample->sample_rate == playback_rate && + vp->sample->root_freq == get_note_freq(vp->sample, vp->sample->note_to_use) && + vp->frequency == vp->orig_frequency) + { + int32_t ofs; + + /* Pre-resampled data -- just update the offset and check if + we're out of data. */ + ofs = (int32_t)(vp->sample_offset >> FRACTION_BITS); /* Kind of silly to use + FRACTION_BITS here... */ + if (*countptr >= (int32_t)((vp->sample->data_length >> FRACTION_BITS) - ofs)) + { + /* Note finished. Free the voice. */ + vp->timeout = 1; + + /* Let the caller know how much data we had left */ + *countptr = (int32_t)(vp->sample->data_length >> FRACTION_BITS) - ofs; + } + else + vp->sample_offset += *countptr << FRACTION_BITS; + + for (i = 0; i < *countptr; i++) { + resample_buffer[i] = vp->sample->data[i + ofs]; + } + return resample_buffer; + } + + mode = vp->sample->modes; + if ((mode & MODES_LOOPING) && + ((mode & MODES_ENVELOPE) || + (vp->status & (VOICE_ON | VOICE_SUSTAINED)))) + { + if (mode & MODES_PINGPONG) + { + vp->cache = NULL; + mode = 2; /* Bidir loop */ + } + else + mode = 0; /* loop */ + } + else + mode = 1; /* no loop */ + + if (vp->porta_control_ratio) + result = porta_resample_voice(v, countptr, mode); + else if (vp->vibrato_control_ratio) + result = vib_resample_voice(v, countptr, mode); + else + result = normal_resample_voice(v, countptr, mode); + + return result; +} + + +void pre_resample(Sample * sp) +{ + double a, b; + splen_t ofs, newlen; + sample_t *newdata, *dest, *src = (sample_t *)sp->data; + int32_t i, count, incr, f, x; + resample_rec_t resrc; + + f = get_note_freq(sp, sp->note_to_use); + a = b = ((double)(sp->root_freq) * playback_rate) / + ((double)(sp->sample_rate) * f); + if ((int64_t)sp->data_length * a >= 0x7fffffffL) + { + /* Too large to compute */ + printMessage(CMSG_INFO, VERB_DEBUG, " *** Can't pre-resampling for note %d", + sp->note_to_use); + return; + } + newlen = (splen_t)(sp->data_length * a); + count = (newlen >> FRACTION_BITS); + ofs = incr = (sp->data_length - 1) / (count - 1); + + if ((double)newlen + incr >= 0x7fffffffL) + { + /* Too large to compute */ + printMessage(CMSG_INFO, VERB_DEBUG, " *** Can't pre-resampling for note %d", + sp->note_to_use); + return; + } + + // [EP] Fix the bad allocation count. + dest = newdata = (sample_t *)safe_malloc(((int32_t)(newlen >> (FRACTION_BITS - 1)) + 2)*sizeof(sample_t)); + dest[newlen >> FRACTION_BITS] = 0; + + *dest++ = src[0]; + + resrc.loop_start = 0; + resrc.loop_end = sp->data_length; + resrc.data_length = sp->data_length; + + /* Since we're pre-processing and this doesn't have to be done in + real-time, we go ahead and do the higher order interpolation. */ + for (i = 1; i < count; i++) + { + x = resample_gauss(src, ofs, &resrc); + *dest++ = (int16_t)((x > 32767) ? 32767 : ((x < -32768) ? -32768 : x)); + ofs += incr; + } + + sp->data_length = newlen; + sp->loop_start = (splen_t)(sp->loop_start * b); + sp->loop_end = (splen_t)(sp->loop_end * b); + free(sp->data); + sp->data = (sample_t *)newdata; + sp->root_freq = f; + sp->sample_rate = playback_rate; + sp->low_freq = freq_table[0]; + sp->high_freq = freq_table[127]; +} + +} \ No newline at end of file diff --git a/thirdparty/timidityplus/reverb.cpp b/thirdparty/timidityplus/reverb.cpp new file mode 100644 index 000000000..1426c95d7 --- /dev/null +++ b/thirdparty/timidityplus/reverb.cpp @@ -0,0 +1,4438 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * REVERB EFFECT FOR TIMIDITY++-1.X (Version 0.06e 1999/1/28) + * + * Copyright (C) 1997,1998,1999 Masaki Kiryu + * (http://w3mb.kcom.ne.jp/~mkiryu/) + * + * reverb.c -- main reverb engine. + * + */ + +#include +#include +#include "timidity.h" +#include "tables.h" +#include "common.h" +#include "reverb.h" +#include "optcode.h" + +namespace TimidityPlus +{ + +#define SYS_EFFECT_PRE_LPF + + +const double reverb_predelay_factor = 1.0; +const double freeverb_scaleroom = 0.28; +const double freeverb_offsetroom = 0.7; + +#define MASTER_CHORUS_LEVEL 1.7 +#define MASTER_DELAY_LEVEL 3.25 + +/* */ +/* Dry Signal */ +/* */ + + +void Reverb::set_dry_signal(int32_t *buf, int32_t n) +{ + int32_t i; + int32_t *dbuf = direct_buffer; + + for(i = n - 1; i >= 0; i--) + { + dbuf[i] += buf[i]; + } +} + +void Reverb::set_dry_signal_xg(int32_t *sbuffer, int32_t n, int32_t level) +{ + int32_t i; + int32_t count = n; + if(!level) {return;} + double send_level = (double)level / 127.0; + + for(i = 0; i < count; i++) + { + direct_buffer[i] += int32_t(sbuffer[i] * send_level); + } +} + +void Reverb::mix_dry_signal(int32_t *buf, int32_t n) +{ + memcpy(buf, direct_buffer, sizeof(int32_t) * n); + memset(direct_buffer, 0, sizeof(int32_t) * n); +} + +/* */ +/* Effect Utilities */ +/* */ +static int isprime(int val) +{ + int i; + if (val == 2) {return 1;} + if (val & 1) + { + for (i = 3; i < (int)sqrt((double)val) + 1; i += 2) { + if ((val % i) == 0) {return 0;} + } + return 1; /* prime */ + } + else + { + return 0; + } /* even */ +} + +/*! delay */ +void Reverb::free_delay(simple_delay *delay) +{ + if(delay->buf != NULL) + { + free(delay->buf); + delay->buf = NULL; + } +} + +void Reverb::set_delay(simple_delay *delay, int32_t size) +{ + if(size < 1) {size = 1;} + free_delay(delay); + delay->buf = (int32_t *)safe_malloc(sizeof(int32_t) * size); + if(delay->buf == NULL) {return;} + delay->index = 0; + delay->size = size; + memset(delay->buf, 0, sizeof(int32_t) * delay->size); +} + +void Reverb::do_delay(int32_t *stream, int32_t *buf, int32_t size, int32_t *index) +{ + int32_t output; + output = buf[*index]; + buf[*index] = *stream; + if (++*index >= size) {*index = 0;} + *stream = output; +} + +/*! LFO (low frequency oscillator) */ +void Reverb::init_lfo(lfo *lfo, double freq, int type, double phase) +{ + int32_t i, cycle, diff; + + lfo->count = 0; + lfo->freq = freq; + if (lfo->freq < 0.05) {lfo->freq = 0.05;} + cycle = (double)playback_rate / lfo->freq; + if (cycle < 1) {cycle = 1;} + lfo->cycle = cycle; + lfo->icycle = TIM_FSCALE((SINE_CYCLE_LENGTH - 1) / (double)cycle, 24) - 0.5; + diff = SINE_CYCLE_LENGTH * phase / 360.0; + + if(lfo->type != type) { /* generate LFO waveform */ + switch(type) { + case LFO_SINE: + for(i = 0; i < SINE_CYCLE_LENGTH; i++) + lfo->buf[i] = TIM_FSCALE((lookup_sine(i + diff) + 1.0) / 2.0, 16); + break; + case LFO_TRIANGULAR: + for(i = 0; i < SINE_CYCLE_LENGTH; i++) + lfo->buf[i] = TIM_FSCALE((lookup_triangular(i + diff) + 1.0) / 2.0, 16); + break; + default: + for(i = 0; i < SINE_CYCLE_LENGTH; i++) {lfo->buf[i] = TIM_FSCALE(0.5, 16);} + break; + } + } + lfo->type = type; +} + +/* returned value is between 0 and (1 << 16) */ +int32_t Reverb::do_lfo(lfo *lfo) +{ + int32_t val; + val = lfo->buf[imuldiv24(lfo->count, lfo->icycle)]; + if(++lfo->count == lfo->cycle) {lfo->count = 0;} + return val; +} + +void Reverb::do_mod_delay(int32_t *stream, int32_t *buf, int32_t size, int32_t *rindex, int32_t *windex, + int32_t ndelay, int32_t depth, int32_t lfoval, int32_t *hist) +{ + int32_t t1, t2; + if (++*windex == size) {*windex = 0;} + t1 = buf[*rindex]; + t2 = imuldiv24(lfoval, depth); + *rindex = *windex - ndelay - (t2 >> 8); + if (*rindex < 0) {*rindex += size;} + t2 = 0xFF - (t2 & 0xFF); + *hist = t1 + imuldiv8(buf[*rindex] - *hist, t2); + buf[*windex] = *stream; + *stream = *hist; +} + +/*! modulated allpass filter with allpass interpolation (for Plate Reverberator,...) */ +void Reverb::free_mod_allpass(mod_allpass *delay) +{ + if(delay->buf != NULL) { + free(delay->buf); + delay->buf = NULL; + } +} + +void Reverb::set_mod_allpass(mod_allpass *delay, int32_t ndelay, int32_t depth, double feedback) +{ + int32_t size = ndelay + depth + 1; + free_mod_allpass(delay); + delay->buf = (int32_t *)safe_malloc(sizeof(int32_t) * size); + if(delay->buf == NULL) {return;} + delay->rindex = 0; + delay->windex = 0; + delay->hist = 0; + delay->ndelay = ndelay; + delay->depth = depth; + delay->size = size; + delay->feedback = feedback; + delay->feedbacki = TIM_FSCALE(feedback, 24); + memset(delay->buf, 0, sizeof(int32_t) * delay->size); +} + +void Reverb::do_mod_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *rindex, int32_t *windex, + int32_t ndelay, int32_t depth, int32_t lfoval, int32_t *hist, int32_t feedback) +{ + int t1, t2, t3; + if (++*windex == size) {*windex = 0;} + t3 = *stream + imuldiv24(*hist, feedback); + t1 = buf[*rindex]; + t2 = imuldiv24(lfoval, depth); + *rindex = *windex - ndelay - (t2 >> 8); + if (*rindex < 0) {*rindex += size;} + t2 = 0xFF - (t2 & 0xFF); + *hist = t1 + imuldiv8(buf[*rindex] - *hist, t2); + buf[*windex] = t3; + *stream = *hist - imuldiv24(t3, feedback); +} + +/* allpass filter */ +void Reverb::free_allpass(allpass *allpass) +{ + if(allpass->buf != NULL) { + free(allpass->buf); + allpass->buf = NULL; + } +} + +void Reverb::set_allpass(allpass *allpass, int32_t size, double feedback) +{ + if(allpass->buf != NULL) { + free(allpass->buf); + allpass->buf = NULL; + } + allpass->buf = (int32_t *)safe_malloc(sizeof(int32_t) * size); + if(allpass->buf == NULL) {return;} + allpass->index = 0; + allpass->size = size; + allpass->feedback = feedback; + allpass->feedbacki = TIM_FSCALE(feedback, 24); + memset(allpass->buf, 0, sizeof(int32_t) * allpass->size); +} + +void Reverb::do_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *index, int32_t feedback) +{ + int32_t bufout, output; + bufout = buf[*index]; + output = *stream - imuldiv24(bufout, feedback); + buf[*index] = output; + if (++*index >= size) {*index = 0;} + *stream = bufout + imuldiv24(output, feedback); +} + +void Reverb::init_filter_moog(filter_moog *svf) +{ + svf->b0 = svf->b1 = svf->b2 = svf->b3 = svf->b4 = 0; +} + +/*! calculate Moog VCF coefficients */ +void Reverb::calc_filter_moog(filter_moog *svf) +{ + double res, fr, p, q, f; + + if (svf->freq > playback_rate / 2) {svf->freq = playback_rate / 2;} + else if(svf->freq < 20) {svf->freq = 20;} + + if(svf->freq != svf->last_freq || svf->res_dB != svf->last_res_dB) { + if(svf->last_freq == 0) {init_filter_moog(svf);} + svf->last_freq = svf->freq, svf->last_res_dB = svf->res_dB; + + res = pow(10, (svf->res_dB - 96) / 20); + fr = 2.0 * (double)svf->freq / (double)playback_rate; + q = 1.0 - fr; + p = fr + 0.8 * fr * q; + f = p + p - 1.0; + q = res * (1.0 + 0.5 * q * (1.0 - q + 5.6 * q * q)); + svf->f = TIM_FSCALE(f, 24); + svf->p = TIM_FSCALE(p, 24); + svf->q = TIM_FSCALE(q, 24); + } +} + +void Reverb::do_filter_moog(int32_t *stream, int32_t *high, int32_t f, int32_t p, int32_t q, + int32_t *b0, int32_t *b1, int32_t *b2, int32_t *b3, int32_t *b4) +{ + int32_t t1, t2, t3, tb0 = *b0, tb1 = *b1, tb2 = *b2, tb3 = *b3, tb4 = *b4; + t3 = *stream - imuldiv24(q, tb4); + t1 = tb1; tb1 = imuldiv24(t3 + tb0, p) - imuldiv24(tb1, f); + t2 = tb2; tb2 = imuldiv24(tb1 + t1, p) - imuldiv24(tb2, f); + t1 = tb3; tb3 = imuldiv24(tb2 + t2, p) - imuldiv24(tb3, f); + *stream = tb4 = imuldiv24(tb3 + t1, p) - imuldiv24(tb4, f); + tb0 = t3; + *stream = tb4; + *high = t3 - tb4; + *b0 = tb0, *b1 = tb1, *b2 = tb2, *b3 = tb3, *b4 = tb4; +} + +void Reverb::init_filter_moog_dist(filter_moog_dist *svf) +{ + svf->b0 = svf->b1 = svf->b2 = svf->b3 = svf->b4 = 0.0; +} + +/*! calculate Moog VCF coefficients */ +void Reverb::calc_filter_moog_dist(filter_moog_dist *svf) +{ + double res, fr, p, q, f; + + if (svf->freq > playback_rate / 2) {svf->freq = playback_rate / 2;} + else if (svf->freq < 20) {svf->freq = 20;} + + if (svf->freq != svf->last_freq || svf->res_dB != svf->last_res_dB + || svf->dist != svf->last_dist) { + if (svf->last_freq == 0) {init_filter_moog_dist(svf);} + svf->last_freq = svf->freq, svf->last_res_dB = svf->res_dB, + svf->last_dist = svf->dist; + + res = pow(10.0, (svf->res_dB - 96.0) / 20.0); + fr = 2.0 * (double)svf->freq / (double)playback_rate; + q = 1.0 - fr; + p = fr + 0.8 * fr * q; + f = p + p - 1.0; + q = res * (1.0 + 0.5 * q * (1.0 - q + 5.6 * q * q)); + svf->f = f; + svf->p = p; + svf->q = q; + svf->d = 1.0 + svf->dist; + } +} + +void Reverb::do_filter_moog_dist(double *stream, double *high, double *band, double f, double p, double q, double d, + double *b0, double *b1, double *b2, double *b3, double *b4) +{ + double t1, t2, in, tb0 = *b0, tb1 = *b1, tb2 = *b2, tb3 = *b3, tb4 = *b4; + in = *stream; + in -= q * tb4; + t1 = tb1; tb1 = (in + tb0) * p - tb1 * f; + t2 = tb2; tb2 = (tb1 + t1) * p - tb2 * f; + t1 = tb3; tb3 = (tb2 + t2) * p - tb3 * f; + tb4 = (tb3 + t1) * p - tb4 * f; + tb4 *= d; + tb4 = tb4 - tb4 * tb4 * tb4 * 0.166667; + tb0 = in; + *stream = tb4; + *high = in - tb4; + *band = 3.0 * (tb3 - tb4); + *b0 = tb0, *b1 = tb1, *b2 = tb2, *b3 = tb3, *b4 = tb4; +} + +void Reverb::do_filter_moog_dist_band(double *stream, double f, double p, double q, double d, + double *b0, double *b1, double *b2, double *b3, double *b4) +{ + double t1, t2, in, tb0 = *b0, tb1 = *b1, tb2 = *b2, tb3 = *b3, tb4 = *b4; + in = *stream; + in -= q * tb4; + t1 = tb1; tb1 = (in + tb0) * p - tb1 * f; + t2 = tb2; tb2 = (tb1 + t1) * p - tb2 * f; + t1 = tb3; tb3 = (tb2 + t2) * p - tb3 * f; + tb4 = (tb3 + t1) * p - tb4 * f; + tb4 *= d; + tb4 = tb4 - tb4 * tb4 * tb4 * 0.166667; + tb0 = in; + *stream = 3.0 * (tb3 - tb4); + *b0 = tb0, *b1 = tb1, *b2 = tb2, *b3 = tb3, *b4 = tb4; +} + +void Reverb::init_filter_lpf18(filter_lpf18 *p) +{ + p->ay1 = p->ay2 = p->aout = p->lastin = 0; +} + +/*! calculate LPF18 coefficients */ +void Reverb::calc_filter_lpf18(filter_lpf18 *p) +{ + double kfcn, kp, kp1, kp1h, kres, value; + + if(p->freq != p->last_freq || p->dist != p->last_dist + || p->res != p->last_res) { + if(p->last_freq == 0) {init_filter_lpf18(p);} + p->last_freq = p->freq, p->last_dist = p->dist, p->last_res = p->res; + + kfcn = 2.0 * (double)p->freq / (double)playback_rate; + kp = ((-2.7528 * kfcn + 3.0429) * kfcn + 1.718) * kfcn - 0.9984; + kp1 = kp + 1.0; + kp1h = 0.5 * kp1; + kres = p->res * (((-2.7079 * kp1 + 10.963) * kp1 - 14.934) * kp1 + 8.4974); + value = 1.0 + (p->dist * (1.5 + 2.0 * kres * (1.0 - kfcn))); + + p->kp = kp, p->kp1h = kp1h, p->kres = kres, p->value = value; + } +} + +void Reverb::do_filter_lpf18(double *stream, double *ay1, double *ay2, double *aout, double *lastin, + double kres, double value, double kp, double kp1h) +{ + double ax1, ay11, ay31; + ax1 = *lastin; + ay11 = *ay1; + ay31 = *ay2; + *lastin = *stream - tanh(kres * *aout); + *ay1 = kp1h * (*lastin + ax1) - kp * *ay1; + *ay2 = kp1h * (*ay1 + ay11) - kp * *ay2; + *aout = kp1h * (*ay2 + ay31) - kp * *aout; + *stream = tanh(*aout * value); +} + +#define WS_AMP_MAX ((int32_t) 0x0fffffff) +#define WS_AMP_MIN ((int32_t)-0x0fffffff) + +void Reverb::do_hard_clipping(int32_t *stream, int32_t d) +{ + int32_t x; + x = imuldiv24(*stream, d); + x = (x > WS_AMP_MAX) ? WS_AMP_MAX + : (x < WS_AMP_MIN) ? WS_AMP_MIN : x; + *stream = x; +} + +void Reverb::do_soft_clipping1(int32_t *stream, int32_t d) +{ + int32_t x, ai = TIM_FSCALE(1.5, 24), bi = TIM_FSCALE(0.5, 24); + x = imuldiv24(*stream, d); + x = (x > WS_AMP_MAX) ? WS_AMP_MAX + : (x < WS_AMP_MIN) ? WS_AMP_MIN : x; + x = imuldiv24(x, ai) - imuldiv24(imuldiv28(imuldiv28(x, x), x), bi); + *stream = x; +} + +void Reverb::do_soft_clipping2(int32_t *stream, int32_t d) +{ + int32_t x; + x = imuldiv24(*stream, d); + x = (x > WS_AMP_MAX) ? WS_AMP_MAX + : (x < WS_AMP_MIN) ? WS_AMP_MIN : x; + x = signlong(x) * ((abs(x) << 1) - imuldiv28(x, x)); + *stream = x; +} + +/*! 1st order lowpass filter */ +void Reverb::init_filter_lowpass1(filter_lowpass1 *p) +{ + if (p->a > 1.0) {p->a = 1.0;} + p->x1l = p->x1r = 0; + p->ai = TIM_FSCALE(p->a, 24); + p->iai = TIM_FSCALE(1.0 - p->a, 24); +} + +void Reverb::do_filter_lowpass1(int32_t *stream, int32_t *x1, int32_t a, int32_t ia) +{ + *stream = *x1 = imuldiv24(*x1, ia) + imuldiv24(*stream, a); +} + +void Reverb::do_filter_lowpass1_stereo(int32_t *buf, int32_t count, filter_lowpass1 *p) +{ + int32_t i, a = p->ai, ia = p->iai, x1l = p->x1l, x1r = p->x1r; + + for(i = 0; i < count; i++) { + do_filter_lowpass1(&buf[i], &x1l, a, ia); + ++i; + do_filter_lowpass1(&buf[i], &x1r, a, ia); + } + p->x1l = x1l, p->x1r = x1r; +} + +void Reverb::init_filter_biquad(filter_biquad *p) +{ + p->x1l = 0, p->x2l = 0, p->y1l = 0, p->y2l = 0, p->x1r = 0, + p->x2r = 0, p->y1r = 0, p->y2r = 0; +} + +/*! biquad lowpass filter */ +void Reverb::calc_filter_biquad_low(filter_biquad *p) +{ + double a0, a1, a2, b1, b02, omega, sn, cs, alpha; + + if(p->freq != p->last_freq || p->q != p->last_q) { + if (p->last_freq == 0) {init_filter_biquad(p);} + p->last_freq = p->freq, p->last_q = p->q; + omega = 2.0 * M_PI * (double)p->freq / (double)playback_rate; + sn = sin(omega); + cs = cos(omega); + if (p->q == 0 || p->freq < 0 || p->freq > playback_rate / 2) { + p->b02 = TIM_FSCALE(1.0, 24); + p->a1 = p->a2 = p->b1 = 0; + return; + } else {alpha = sn / (2.0 * p->q);} + + a0 = 1.0 / (1.0 + alpha); + b02 = ((1.0 - cs) / 2.0) * a0; + b1 = (1.0 - cs) * a0; + a1 = (-2.0 * cs) * a0; + a2 = (1.0 - alpha) * a0; + + p->b1 = TIM_FSCALE(b1, 24); + p->a2 = TIM_FSCALE(a2, 24); + p->a1 = TIM_FSCALE(a1, 24); + p->b02 = TIM_FSCALE(b02, 24); + } +} + +/*! biquad highpass filter */ +void Reverb::calc_filter_biquad_high(filter_biquad *p) +{ + double a0, a1, a2, b1, b02, omega, sn, cs, alpha; + + if(p->freq != p->last_freq || p->q != p->last_q) { + if (p->last_freq == 0) {init_filter_biquad(p);} + p->last_freq = p->freq, p->last_q = p->q; + omega = 2.0 * M_PI * (double)p->freq / (double)playback_rate; + sn = sin(omega); + cs = cos(omega); + if (p->q == 0 || p->freq < 0 || p->freq > playback_rate / 2) { + p->b02 = TIM_FSCALE(1.0, 24); + p->a1 = p->a2 = p->b1 = 0; + return; + } else {alpha = sn / (2.0 * p->q);} + + a0 = 1.0 / (1.0 + alpha); + b02 = ((1.0 + cs) / 2.0) * a0; + b1 = (-(1.0 + cs)) * a0; + a1 = (-2.0 * cs) * a0; + a2 = (1.0 - alpha) * a0; + + p->b1 = TIM_FSCALE(b1, 24); + p->a2 = TIM_FSCALE(a2, 24); + p->a1 = TIM_FSCALE(a1, 24); + p->b02 = TIM_FSCALE(b02, 24); + } +} + +void Reverb::do_filter_biquad(int32_t *stream, int32_t a1, int32_t a2, int32_t b1, + int32_t b02, int32_t *x1, int32_t *x2, int32_t *y1, int32_t *y2) +{ + int32_t t1; + t1 = imuldiv24(*stream + *x2, b02) + imuldiv24(*x1, b1) - imuldiv24(*y1, a1) - imuldiv24(*y2, a2); + *x2 = *x1; + *x1 = *stream; + *y2 = *y1; + *y1 = t1; + *stream = t1; +} + +void Reverb::init_filter_shelving(filter_shelving *p) +{ + p->x1l = 0, p->x2l = 0, p->y1l = 0, p->y2l = 0, p->x1r = 0, + p->x2r = 0, p->y1r = 0, p->y2r = 0; +} + +/*! shelving filter */ +void Reverb::calc_filter_shelving_low(filter_shelving *p) +{ + double a0, a1, a2, b0, b1, b2, omega, sn, cs, A, beta; + + init_filter_shelving(p); + + A = pow(10, p->gain / 40); + omega = 2.0 * M_PI * (double)p->freq / (double)playback_rate; + sn = sin(omega); + cs = cos(omega); + if (p->freq < 0 || p->freq > playback_rate / 2) { + p->b0 = TIM_FSCALE(1.0, 24); + p->a1 = p->b1 = p->a2 = p->b2 = 0; + return; + } + if (p->q == 0) {beta = sqrt(A + A);} + else {beta = sqrt(A) / p->q;} + + a0 = 1.0 / ((A + 1) + (A - 1) * cs + beta * sn); + a1 = 2.0 * ((A - 1) + (A + 1) * cs); + a2 = -((A + 1) + (A - 1) * cs - beta * sn); + b0 = A * ((A + 1) - (A - 1) * cs + beta * sn); + b1 = 2.0 * A * ((A - 1) - (A + 1) * cs); + b2 = A * ((A + 1) - (A - 1) * cs - beta * sn); + + a1 *= a0; + a2 *= a0; + b1 *= a0; + b2 *= a0; + b0 *= a0; + + p->a1 = TIM_FSCALE(a1, 24); + p->a2 = TIM_FSCALE(a2, 24); + p->b0 = TIM_FSCALE(b0, 24); + p->b1 = TIM_FSCALE(b1, 24); + p->b2 = TIM_FSCALE(b2, 24); +} + +void Reverb::calc_filter_shelving_high(filter_shelving *p) +{ + double a0, a1, a2, b0, b1, b2, omega, sn, cs, A, beta; + + init_filter_shelving(p); + + A = pow(10, p->gain / 40); + omega = 2.0 * M_PI * (double)p->freq / (double)playback_rate; + sn = sin(omega); + cs = cos(omega); + if (p->freq < 0 || p->freq > playback_rate / 2) { + p->b0 = TIM_FSCALE(1.0, 24); + p->a1 = p->b1 = p->a2 = p->b2 = 0; + return; + } + if (p->q == 0) {beta = sqrt(A + A);} + else {beta = sqrt(A) / p->q;} + + a0 = 1.0 / ((A + 1) - (A - 1) * cs + beta * sn); + a1 = (-2 * ((A - 1) - (A + 1) * cs)); + a2 = -((A + 1) - (A - 1) * cs - beta * sn); + b0 = A * ((A + 1) + (A - 1) * cs + beta * sn); + b1 = -2 * A * ((A - 1) + (A + 1) * cs); + b2 = A * ((A + 1) + (A - 1) * cs - beta * sn); + + a1 *= a0; + a2 *= a0; + b0 *= a0; + b1 *= a0; + b2 *= a0; + + p->a1 = TIM_FSCALE(a1, 24); + p->a2 = TIM_FSCALE(a2, 24); + p->b0 = TIM_FSCALE(b0, 24); + p->b1 = TIM_FSCALE(b1, 24); + p->b2 = TIM_FSCALE(b2, 24); +} + +void Reverb::do_shelving_filter_stereo(int32_t* buf, int32_t count, filter_shelving *p) +{ + int32_t i; + int32_t x1l = p->x1l, x2l = p->x2l, y1l = p->y1l, y2l = p->y2l, + x1r = p->x1r, x2r = p->x2r, y1r = p->y1r, y2r = p->y2r, yout; + int32_t a1 = p->a1, a2 = p->a2, b0 = p->b0, b1 = p->b1, b2 = p->b2; + + for(i = 0; i < count; i++) { + yout = imuldiv24(buf[i], b0) + imuldiv24(x1l, b1) + imuldiv24(x2l, b2) + imuldiv24(y1l, a1) + imuldiv24(y2l, a2); + x2l = x1l; + x1l = buf[i]; + y2l = y1l; + y1l = yout; + buf[i] = yout; + + yout = imuldiv24(buf[++i], b0) + imuldiv24(x1r, b1) + imuldiv24(x2r, b2) + imuldiv24(y1r, a1) + imuldiv24(y2r, a2); + x2r = x1r; + x1r = buf[i]; + y2r = y1r; + y1r = yout; + buf[i] = yout; + } + p->x1l = x1l, p->x2l = x2l, p->y1l = y1l, p->y2l = y2l, + p->x1r = x1r, p->x2r = x2r, p->y1r = y1r, p->y2r = y2r; +} + +void Reverb::init_filter_peaking(filter_peaking *p) +{ + p->x1l = 0, p->x2l = 0, p->y1l = 0, p->y2l = 0, p->x1r = 0, + p->x2r = 0, p->y1r = 0, p->y2r = 0; +} + +/*! peaking filter */ +void Reverb::calc_filter_peaking(filter_peaking *p) +{ + double a0, ba1, a2, b0, b2, omega, sn, cs, A, alpha; + + init_filter_peaking(p); + + A = pow(10, p->gain / 40); + omega = 2.0 * M_PI * (double)p->freq / (double)playback_rate; + sn = sin(omega); + cs = cos(omega); + if (p->q == 0 || p->freq < 0 || p->freq > playback_rate / 2) { + p->b0 = TIM_FSCALE(1.0, 24); + p->ba1 = p->a2 = p->b2 = 0; + return; + } else {alpha = sn / (2.0 * p->q);} + + a0 = 1.0 / (1.0 + alpha / A); + ba1 = -2.0 * cs; + a2 = 1.0 - alpha / A; + b0 = 1.0 + alpha * A; + b2 = 1.0 - alpha * A; + + ba1 *= a0; + a2 *= a0; + b0 *= a0; + b2 *= a0; + + p->ba1 = TIM_FSCALE(ba1, 24); + p->a2 = TIM_FSCALE(a2, 24); + p->b0 = TIM_FSCALE(b0, 24); + p->b2 = TIM_FSCALE(b2, 24); +} + +void Reverb::do_peaking_filter_stereo(int32_t* buf, int32_t count, filter_peaking *p) +{ + int32_t i; + int32_t x1l = p->x1l, x2l = p->x2l, y1l = p->y1l, y2l = p->y2l, + x1r = p->x1r, x2r = p->x2r, y1r = p->y1r, y2r = p->y2r, yout; + int32_t ba1 = p->ba1, a2 = p->a2, b0 = p->b0, b2 = p->b2; + + for(i = 0; i < count; i++) { + yout = imuldiv24(buf[i], b0) + imuldiv24(x1l - y1l, ba1) + imuldiv24(x2l, b2) - imuldiv24(y2l, a2); + x2l = x1l; + x1l = buf[i]; + y2l = y1l; + y1l = yout; + buf[i] = yout; + + yout = imuldiv24(buf[++i], b0) + imuldiv24(x1r - y1r, ba1) + imuldiv24(x2r, b2) - imuldiv24(y2r, a2); + x2r = x1r; + x1r = buf[i]; + y2r = y1r; + y1r = yout; + buf[i] = yout; + } + p->x1l = x1l, p->x2l = x2l, p->y1l = y1l, p->y2l = y2l, + p->x1r = x1r, p->x2r = x2r, p->y1r = y1r, p->y2r = y2r; +} + +void Reverb::init_pink_noise(pink_noise *p) +{ + p->b0 = p->b1 = p->b2 = p->b3 = p->b4 = p->b5 = p->b6 = 0; +} + +float Reverb::get_pink_noise(pink_noise *p) +{ + float b0 = p->b0, b1 = p->b1, b2 = p->b2, b3 = p->b3, + b4 = p->b4, b5 = p->b5, b6 = p->b6, pink, white; + + white = (float)flt_rand() * 2.0 - 1.0; + b0 = 0.99886 * b0 + white * 0.0555179; + b1 = 0.99332 * b1 + white * 0.0750759; + b2 = 0.96900 * b2 + white * 0.1538520; + b3 = 0.86650 * b3 + white * 0.3104856; + b4 = 0.55000 * b4 + white * 0.5329522; + b5 = -0.7616 * b5 - white * 0.0168980; + pink = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; + b6 = white * 0.115926; + pink *= 0.22f; + pink = (pink > 1.0) ? 1.0 : (pink < -1.0) ? -1.0 : pink; + + p->b0 = b0, p->b1 = b1, p->b2 = b2, p->b3 = b3, + p->b4 = b4, p->b5 = b5, p->b6 = b6; + + return pink; +} + +float Reverb::get_pink_noise_light(pink_noise *p) +{ + float b0 = p->b0, b1 = p->b1, b2 = p->b2, pink, white; + + white = (float)flt_rand() * 2.0 - 1.0; + b0 = 0.99765 * b0 + white * 0.0990460; + b1 = 0.96300 * b1 + white * 0.2965164; + b2 = 0.57000 * b2 + white * 1.0526913; + pink = b0 + b1 + b2 + white * 0.1848; + pink *= 0.22f; + pink = (pink > 1.0) ? 1.0 : (pink < -1.0) ? -1.0 : pink; + + p->b0 = b0, p->b1 = b1, p->b2 = b2; + + return pink; +} + +/* */ +/* Standard Reverb Effect */ +/* */ +#define REV_VAL0 5.3 +#define REV_VAL1 10.5 +#define REV_VAL2 44.12 +#define REV_VAL3 21.0 + + +void Reverb::set_ch_reverb(int32_t *sbuffer, int32_t n, int32_t level) +{ + int32_t i; + if(!level) {return;} + double send_level = (double)level / 127.0 * REV_INP_LEV; + + for(i = 0; i < n; i++) + { + reverb_effect_buffer[i] += int32_t(sbuffer[i] * send_level); + } +} + +double Reverb::gs_revchar_to_roomsize(int character) +{ + double rs; + switch(character) { + case 0: rs = 1.0; break; /* Room 1 */ + case 1: rs = 0.94; break; /* Room 2 */ + case 2: rs = 0.97; break; /* Room 3 */ + case 3: rs = 0.90; break; /* Hall 1 */ + case 4: rs = 0.85; break; /* Hall 2 */ + default: rs = 1.0; break; /* Plate, Delay, Panning Delay */ + } + return rs; +} + +double Reverb::gs_revchar_to_level(int character) +{ + double level; + switch(character) { + case 0: level = 0.744025605; break; /* Room 1 */ + case 1: level = 1.224309745; break; /* Room 2 */ + case 2: level = 0.858592403; break; /* Room 3 */ + case 3: level = 1.0471802; break; /* Hall 1 */ + case 4: level = 1.0; break; /* Hall 2 */ + case 5: level = 0.865335496; break; /* Plate */ + default: level = 1.0; break; /* Delay, Panning Delay */ + } + return level; +} + +double Reverb::gs_revchar_to_rt(int character) +{ + double rt; + switch(character) { + case 0: rt = 0.516850262; break; /* Room 1 */ + case 1: rt = 1.004226004; break; /* Room 2 */ + case 2: rt = 0.691046825; break; /* Room 3 */ + case 3: rt = 0.893006004; break; /* Hall 1 */ + case 4: rt = 1.0; break; /* Hall 2 */ + case 5: rt = 0.538476488; break; /* Plate */ + default: rt = 1.0; break; /* Delay, Panning Delay */ + } + return rt; +} + +void Reverb::init_standard_reverb(InfoStandardReverb *info) +{ + double time; + info->ta = info->tb = 0; + info->HPFL = info->HPFR = info->LPFL = info->LPFR = info->EPFL = info->EPFR = 0; + info->spt0 = info->spt1 = info->spt2 = info->spt3 = 0; + time = reverb_time_table[reverb_status_gs.time] * gs_revchar_to_rt(reverb_status_gs.character) + / reverb_time_table[64] * 0.8; + info->rpt0 = REV_VAL0 * playback_rate / 1000.0 * time; + info->rpt1 = REV_VAL1 * playback_rate / 1000.0 * time; + info->rpt2 = REV_VAL2 * playback_rate / 1000.0 * time; + info->rpt3 = REV_VAL3 * playback_rate / 1000.0 * time; + while (!isprime(info->rpt0)) {info->rpt0++;} + while (!isprime(info->rpt1)) {info->rpt1++;} + while (!isprime(info->rpt2)) {info->rpt2++;} + while (!isprime(info->rpt3)) {info->rpt3++;} + set_delay(&(info->buf0_L), info->rpt0 + 1); + set_delay(&(info->buf0_R), info->rpt0 + 1); + set_delay(&(info->buf1_L), info->rpt1 + 1); + set_delay(&(info->buf1_R), info->rpt1 + 1); + set_delay(&(info->buf2_L), info->rpt2 + 1); + set_delay(&(info->buf2_R), info->rpt2 + 1); + set_delay(&(info->buf3_L), info->rpt3 + 1); + set_delay(&(info->buf3_R), info->rpt3 + 1); + info->fbklev = 0.12; + info->nmixlev = 0.7; + info->cmixlev = 0.9; + info->monolev = 0.7; + info->hpflev = 0.5; + info->lpflev = 0.45; + info->lpfinp = 0.55; + info->epflev = 0.4; + info->epfinp = 0.48; + info->width = 0.125; + info->wet = 2.0 * (double)reverb_status_gs.level / 127.0 * gs_revchar_to_level(reverb_status_gs.character); + info->fbklevi = TIM_FSCALE(info->fbklev, 24); + info->nmixlevi = TIM_FSCALE(info->nmixlev, 24); + info->cmixlevi = TIM_FSCALE(info->cmixlev, 24); + info->monolevi = TIM_FSCALE(info->monolev, 24); + info->hpflevi = TIM_FSCALE(info->hpflev, 24); + info->lpflevi = TIM_FSCALE(info->lpflev, 24); + info->lpfinpi = TIM_FSCALE(info->lpfinp, 24); + info->epflevi = TIM_FSCALE(info->epflev, 24); + info->epfinpi = TIM_FSCALE(info->epfinp, 24); + info->widthi = TIM_FSCALE(info->width, 24); + info->weti = TIM_FSCALE(info->wet, 24); +} + +void Reverb::free_standard_reverb(InfoStandardReverb *info) +{ + free_delay(&(info->buf0_L)); + free_delay(&(info->buf0_R)); + free_delay(&(info->buf1_L)); + free_delay(&(info->buf1_R)); + free_delay(&(info->buf2_L)); + free_delay(&(info->buf2_R)); + free_delay(&(info->buf3_L)); + free_delay(&(info->buf3_R)); +} + +/*! Standard Reverberator; this implementation is specialized for system effect. */ +void Reverb::do_ch_standard_reverb(int32_t *buf, int32_t count, InfoStandardReverb *info) +{ + int32_t i, fixp, s, t; + int32_t spt0 = info->spt0, spt1 = info->spt1, spt2 = info->spt2, spt3 = info->spt3, + ta = info->ta, tb = info->tb, HPFL = info->HPFL, HPFR = info->HPFR, + LPFL = info->LPFL, LPFR = info->LPFR, EPFL = info->EPFL, EPFR = info->EPFR; + int32_t *buf0_L = info->buf0_L.buf, *buf0_R = info->buf0_R.buf, + *buf1_L = info->buf1_L.buf, *buf1_R = info->buf1_R.buf, + *buf2_L = info->buf2_L.buf, *buf2_R = info->buf2_R.buf, + *buf3_L = info->buf3_L.buf, *buf3_R = info->buf3_R.buf; + double fbklev = info->fbklev, cmixlev = info->cmixlev, + hpflev = info->hpflev, lpflev = info->lpflev, lpfinp = info->lpfinp, + epflev = info->epflev, epfinp = info->epfinp, width = info->width, + rpt0 = info->rpt0, rpt1 = info->rpt1, rpt2 = info->rpt2, rpt3 = info->rpt3, wet = info->wet; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_standard_reverb(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_standard_reverb(info); + return; + } + + for (i = 0; i < count; i++) + { + /* L */ + fixp = reverb_effect_buffer[i]; + + LPFL = LPFL * lpflev + (buf2_L[spt2] + tb) * lpfinp + ta * width; + ta = buf3_L[spt3]; + s = buf3_L[spt3] = buf0_L[spt0]; + buf0_L[spt0] = -LPFL; + + t = (HPFL + fixp) * hpflev; + HPFL = t - fixp; + + buf2_L[spt2] = (s - fixp * fbklev) * cmixlev; + tb = buf1_L[spt1]; + buf1_L[spt1] = t; + + EPFL = EPFL * epflev + ta * epfinp; + buf[i] += int32_t((ta + EPFL) * wet); + + /* R */ + fixp = reverb_effect_buffer[++i]; + + LPFR = LPFR * lpflev + (buf2_R[spt2] + tb) * lpfinp + ta * width; + ta = buf3_R[spt3]; + s = buf3_R[spt3] = buf0_R[spt0]; + buf0_R[spt0] = LPFR; + + t = (HPFR + fixp) * hpflev; + HPFR = t - fixp; + + buf2_R[spt2] = (s - fixp * fbklev) * cmixlev; + tb = buf1_R[spt1]; + buf1_R[spt1] = t; + + EPFR = EPFR * epflev + ta * epfinp; + buf[i] += int32_t((ta + EPFR) * wet); + + if (++spt0 == rpt0) {spt0 = 0;} + if (++spt1 == rpt1) {spt1 = 0;} + if (++spt2 == rpt2) {spt2 = 0;} + if (++spt3 == rpt3) {spt3 = 0;} + } + memset(reverb_effect_buffer, 0, sizeof(int32_t) * count); + info->spt0 = spt0, info->spt1 = spt1, info->spt2 = spt2, info->spt3 = spt3, + info->ta = ta, info->tb = tb, info->HPFL = HPFL, info->HPFR = HPFR, + info->LPFL = LPFL, info->LPFR = LPFR, info->EPFL = EPFL, info->EPFR = EPFR; +} + +/*! Standard Monoral Reverberator; this implementation is specialized for system effect. */ +void Reverb::do_ch_standard_reverb_mono(int32_t *buf, int32_t count, InfoStandardReverb *info) +{ + int32_t i, fixp, s, t; + int32_t spt0 = info->spt0, spt1 = info->spt1, spt2 = info->spt2, spt3 = info->spt3, + ta = info->ta, tb = info->tb, HPFL = info->HPFL, HPFR = info->HPFR, + LPFL = info->LPFL, LPFR = info->LPFR, EPFL = info->EPFL, EPFR = info->EPFR; + int32_t *buf0_L = info->buf0_L.buf, *buf0_R = info->buf0_R.buf, + *buf1_L = info->buf1_L.buf, *buf1_R = info->buf1_R.buf, + *buf2_L = info->buf2_L.buf, *buf2_R = info->buf2_R.buf, + *buf3_L = info->buf3_L.buf, *buf3_R = info->buf3_R.buf; + double fbklev = info->fbklev, nmixlev = info->nmixlev, monolev = info->monolev, + hpflev = info->hpflev, lpflev = info->lpflev, lpfinp = info->lpfinp, + epflev = info->epflev, epfinp = info->epfinp, width = info->width, + rpt0 = info->rpt0, rpt1 = info->rpt1, rpt2 = info->rpt2, rpt3 = info->rpt3, wet = info->wet; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_standard_reverb(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_standard_reverb(info); + return; + } + + for (i = 0; i < count; i++) + { + /* L */ + fixp = buf[i] * monolev; + + LPFL = LPFL * lpflev + (buf2_L[spt2] + tb) * lpfinp + ta * width; + ta = buf3_L[spt3]; + s = buf3_L[spt3] = buf0_L[spt0]; + buf0_L[spt0] = -LPFL; + + t = (HPFL + fixp) * hpflev; + HPFL = t - fixp; + + buf2_L[spt2] = (s - fixp * fbklev) * nmixlev; + tb = buf1_L[spt1]; + buf1_L[spt1] = t; + + /* R */ + LPFR = LPFR * lpflev + (buf2_R[spt2] + tb) * lpfinp + ta * width; + ta = buf3_R[spt3]; + s = buf3_R[spt3] = buf0_R[spt0]; + buf0_R[spt0] = LPFR; + + t = (HPFR + fixp) * hpflev; + HPFR = t - fixp; + + buf2_R[spt2] = (s - fixp * fbklev) * nmixlev; + tb = buf1_R[spt1]; + buf1_R[spt1] = t; + + EPFR = EPFR * epflev + ta * epfinp; + buf[i] = (ta + EPFR) * wet + fixp; + + if (++spt0 == rpt0) {spt0 = 0;} + if (++spt1 == rpt1) {spt1 = 0;} + if (++spt2 == rpt2) {spt2 = 0;} + if (++spt3 == rpt3) {spt3 = 0;} + } + memset(reverb_effect_buffer, 0, sizeof(int32_t) * count); + info->spt0 = spt0, info->spt1 = spt1, info->spt2 = spt2, info->spt3 = spt3, + info->ta = ta, info->tb = tb, info->HPFL = HPFL, info->HPFR = HPFR, + info->LPFL = LPFL, info->LPFR = LPFR, info->EPFL = EPFL, info->EPFR = EPFR; +} + +/* */ +/* Freeverb */ +/* */ +void Reverb::set_freeverb_allpass(allpass *allpass, int32_t size) +{ + if(allpass->buf != NULL) { + free(allpass->buf); + allpass->buf = NULL; + } + allpass->buf = (int32_t *)safe_malloc(sizeof(int32_t) * size); + if(allpass->buf == NULL) {return;} + allpass->index = 0; + allpass->size = size; +} + +void Reverb::init_freeverb_allpass(allpass *allpass) +{ + memset(allpass->buf, 0, sizeof(int32_t) * allpass->size); +} + +void Reverb::set_freeverb_comb(comb *comb, int32_t size) +{ + if(comb->buf != NULL) { + free(comb->buf); + comb->buf = NULL; + } + comb->buf = (int32_t *)safe_malloc(sizeof(int32_t) * size); + if(comb->buf == NULL) {return;} + comb->index = 0; + comb->size = size; + comb->filterstore = 0; +} + +void Reverb::init_freeverb_comb(comb *comb) +{ + memset(comb->buf, 0, sizeof(int32_t) * comb->size); +} + +#define scalewet 0.06 +#define scaledamp 0.4 +#define initialroom 0.5 +#define initialdamp 0.5 +#define initialwet 1 / scalewet +#define initialdry 0 +#define initialwidth 0.5 +#define initialallpassfbk 0.65 +#define stereospread 23 +static const int combtunings[numcombs] = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617}; +static const int allpasstunings[numallpasses] = {225, 341, 441, 556}; +#define fixedgain 0.025 +#define combfbk 3.0 + +void Reverb::realloc_freeverb_buf(InfoFreeverb *rev) +{ + int i; + int32_t tmpL, tmpR; + double time, samplerate = playback_rate; + + time = reverb_time_table[reverb_status_gs.time] * gs_revchar_to_rt(reverb_status_gs.character) * combfbk + / (60 * combtunings[numcombs - 1] / (-20 * log10(rev->roomsize1) * 44100.0)); + + for(i = 0; i < numcombs; i++) + { + tmpL = combtunings[i] * samplerate * time / 44100.0; + tmpR = (combtunings[i] + stereospread) * samplerate * time / 44100.0; + if(tmpL < 10) tmpL = 10; + if(tmpR < 10) tmpR = 10; + while(!isprime(tmpL)) tmpL++; + while(!isprime(tmpR)) tmpR++; + rev->combL[i].size = tmpL; + rev->combR[i].size = tmpR; + set_freeverb_comb(&rev->combL[i], rev->combL[i].size); + set_freeverb_comb(&rev->combR[i], rev->combR[i].size); + } + + for(i = 0; i < numallpasses; i++) + { + tmpL = allpasstunings[i] * samplerate * time / 44100.0; + tmpR = (allpasstunings[i] + stereospread) * samplerate * time / 44100.0; + if(tmpL < 10) tmpL = 10; + if(tmpR < 10) tmpR = 10; + while(!isprime(tmpL)) tmpL++; + while(!isprime(tmpR)) tmpR++; + rev->allpassL[i].size = tmpL; + rev->allpassR[i].size = tmpR; + set_freeverb_allpass(&rev->allpassL[i], rev->allpassL[i].size); + set_freeverb_allpass(&rev->allpassR[i], rev->allpassR[i].size); + } +} + +void Reverb::update_freeverb(InfoFreeverb *rev) +{ + int i; + double allpassfbk = 0.55, rtbase, rt; + + rev->wet = (double)reverb_status_gs.level / 127.0 * gs_revchar_to_level(reverb_status_gs.character) * fixedgain; + rev->roomsize = gs_revchar_to_roomsize(reverb_status_gs.character) * freeverb_scaleroom + freeverb_offsetroom; + rev->width = 0.5; + + rev->wet1 = rev->width / 2.0 + 0.5; + rev->wet2 = (1.0 - rev->width) / 2.0; + rev->roomsize1 = rev->roomsize; + rev->damp1 = rev->damp; + + realloc_freeverb_buf(rev); + + rtbase = 1.0 / (44100.0 * reverb_time_table[reverb_status_gs.time] * gs_revchar_to_rt(reverb_status_gs.character)); + + for(i = 0; i < numcombs; i++) + { + rt = pow(10.0, -combfbk * (double)combtunings[i] * rtbase); + rev->combL[i].feedback = rt; + rev->combR[i].feedback = rt; + rev->combL[i].damp1 = rev->damp1; + rev->combR[i].damp1 = rev->damp1; + rev->combL[i].damp2 = 1 - rev->damp1; + rev->combR[i].damp2 = 1 - rev->damp1; + rev->combL[i].damp1i = TIM_FSCALE(rev->combL[i].damp1, 24); + rev->combR[i].damp1i = TIM_FSCALE(rev->combR[i].damp1, 24); + rev->combL[i].damp2i = TIM_FSCALE(rev->combL[i].damp2, 24); + rev->combR[i].damp2i = TIM_FSCALE(rev->combR[i].damp2, 24); + rev->combL[i].feedbacki = TIM_FSCALE(rev->combL[i].feedback, 24); + rev->combR[i].feedbacki = TIM_FSCALE(rev->combR[i].feedback, 24); + } + + for(i = 0; i < numallpasses; i++) + { + rev->allpassL[i].feedback = allpassfbk; + rev->allpassR[i].feedback = allpassfbk; + rev->allpassL[i].feedbacki = TIM_FSCALE(rev->allpassL[i].feedback, 24); + rev->allpassR[i].feedbacki = TIM_FSCALE(rev->allpassR[i].feedback, 24); + } + + rev->wet1i = TIM_FSCALE(rev->wet1, 24); + rev->wet2i = TIM_FSCALE(rev->wet2, 24); + + set_delay(&(rev->pdelay), (int32_t)((double)reverb_status_gs.pre_delay_time * reverb_predelay_factor * playback_rate / 1000.0)); +} + +void Reverb::init_freeverb(InfoFreeverb *rev) +{ + int i; + for(i = 0; i < numcombs; i++) { + init_freeverb_comb(&rev->combL[i]); + init_freeverb_comb(&rev->combR[i]); + } + for(i = 0; i < numallpasses; i++) { + init_freeverb_allpass(&rev->allpassL[i]); + init_freeverb_allpass(&rev->allpassR[i]); + } +} + +void Reverb::alloc_freeverb_buf(InfoFreeverb *rev) +{ + int i; + if(rev->alloc_flag) {return;} + for (i = 0; i < numcombs; i++) { + set_freeverb_comb(&rev->combL[i], combtunings[i]); + set_freeverb_comb(&rev->combR[i], combtunings[i] + stereospread); + } + for (i = 0; i < numallpasses; i++) { + set_freeverb_allpass(&rev->allpassL[i], allpasstunings[i]); + set_freeverb_allpass(&rev->allpassR[i], allpasstunings[i] + stereospread); + rev->allpassL[i].feedback = initialallpassfbk; + rev->allpassR[i].feedback = initialallpassfbk; + } + + rev->wet = initialwet * scalewet; + rev->damp = initialdamp * scaledamp; + rev->width = initialwidth; + rev->roomsize = initialroom * freeverb_scaleroom + freeverb_offsetroom; + + rev->alloc_flag = 1; +} + +void Reverb::free_freeverb_buf(InfoFreeverb *rev) +{ + int i; + + for(i = 0; i < numcombs; i++) + { + if(rev->combL[i].buf != NULL) { + free(rev->combL[i].buf); + rev->combL[i].buf = NULL; + } + if(rev->combR[i].buf != NULL) { + free(rev->combR[i].buf); + rev->combR[i].buf = NULL; + } + } + for(i = 0; i < numallpasses; i++) + { + if(rev->allpassL[i].buf != NULL) { + free(rev->allpassL[i].buf); + rev->allpassL[i].buf = NULL; + } + if(rev->allpassR[i].buf != NULL) { + free(rev->allpassR[i].buf); + rev->allpassR[i].buf = NULL; + } + } + free_delay(&(rev->pdelay)); +} + +void Reverb::do_freeverb_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *index, int32_t feedback) +{ + int32_t bufout, output; + bufout = buf[*index]; + output = -*stream + bufout; + buf[*index] = *stream + imuldiv24(bufout, feedback); + if (++*index >= size) {*index = 0;} + *stream = output; +} + +void Reverb::do_freeverb_comb(int32_t input, int32_t *stream, int32_t *buf, int32_t size, int32_t *index, + int32_t damp1, int32_t damp2, int32_t *fs, int32_t feedback) +{ + int32_t output; + output = buf[*index]; + *fs = imuldiv24(output, damp2) + imuldiv24(*fs, damp1); + buf[*index] = input + imuldiv24(*fs, feedback); + if (++*index >= size) {*index = 0;} + *stream += output; +} + +void Reverb::do_ch_freeverb(int32_t *buf, int32_t count, InfoFreeverb *rev) +{ + int32_t i, k = 0; + int32_t outl, outr, input; + comb *combL = rev->combL, *combR = rev->combR; + allpass *allpassL = rev->allpassL, *allpassR = rev->allpassR; + simple_delay *pdelay = &(rev->pdelay); + + if(count == MAGIC_INIT_EFFECT_INFO) { + alloc_freeverb_buf(rev); + update_freeverb(rev); + init_freeverb(rev); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_freeverb_buf(rev); + return; + } + + for (k = 0; k < count; k+=2) + { + input = reverb_effect_buffer[k] + reverb_effect_buffer[k + 1]; + outl = outr = reverb_effect_buffer[k] = reverb_effect_buffer[k + 1] = 0; + + do_delay(&input, pdelay->buf, pdelay->size, &pdelay->index); + + for (i = 0; i < numcombs; i++) { + do_freeverb_comb(input, &outl, combL[i].buf, combL[i].size, &combL[i].index, + combL[i].damp1i, combL[i].damp2i, &combL[i].filterstore, combL[i].feedbacki); + do_freeverb_comb(input, &outr, combR[i].buf, combR[i].size, &combR[i].index, + combR[i].damp1i, combR[i].damp2i, &combR[i].filterstore, combR[i].feedbacki); + } + for (i = 0; i < numallpasses; i++) { + do_freeverb_allpass(&outl, allpassL[i].buf, allpassL[i].size, &allpassL[i].index, allpassL[i].feedbacki); + do_freeverb_allpass(&outr, allpassR[i].buf, allpassR[i].size, &allpassR[i].index, allpassR[i].feedbacki); + } + buf[k] += imuldiv24(outl, rev->wet1i) + imuldiv24(outr, rev->wet2i); + buf[k + 1] += imuldiv24(outr, rev->wet1i) + imuldiv24(outl, rev->wet2i); + } +} + +/* */ +/* Reverb: Delay & Panning Delay */ +/* */ +/*! initialize Reverb: Delay Effect; this implementation is specialized for system effect. */ +void Reverb::init_ch_reverb_delay(InfoDelay3 *info) +{ + int32_t x; + info->size[0] = (double)reverb_status_gs.time * 3.75 * playback_rate / 1000.0; + x = info->size[0] + 1; /* allowance */ + set_delay(&(info->delayL), x); + set_delay(&(info->delayR), x); + info->index[0] = x - info->size[0]; + info->level[0] = (double)reverb_status_gs.level * 1.82 / 127.0; + info->feedback = sqrt((double)reverb_status_gs.delay_feedback / 127.0) * 0.98; + info->leveli[0] = TIM_FSCALE(info->level[0], 24); + info->feedbacki = TIM_FSCALE(info->feedback, 24); +} + +void Reverb::free_ch_reverb_delay(InfoDelay3 *info) +{ + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); +} + +/*! Reverb: Panning Delay Effect; this implementation is specialized for system effect. */ +void Reverb::do_ch_reverb_panning_delay(int32_t *buf, int32_t count, InfoDelay3 *info) +{ + int32_t i, l, r; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], level0i = info->leveli[0], + feedbacki = info->feedbacki; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_ch_reverb_delay(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_ch_reverb_delay(info); + return; + } + + for (i = 0; i < count; i++) + { + bufL[buf_index] = reverb_effect_buffer[i] + imuldiv24(bufR[index0], feedbacki); + l = imuldiv24(bufL[index0], level0i); + bufR[buf_index] = reverb_effect_buffer[i + 1] + imuldiv24(bufL[index0], feedbacki); + r = imuldiv24(bufR[index0], level0i); + + buf[i] += r; + buf[++i] += l; + + if (++index0 == buf_size) {index0 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + memset(reverb_effect_buffer, 0, sizeof(int32_t) * count); + info->index[0] = index0; + delayL->index = delayR->index = buf_index; +} + +/*! Reverb: Normal Delay Effect; this implementation is specialized for system effect. */ +void Reverb::do_ch_reverb_normal_delay(int32_t *buf, int32_t count, InfoDelay3 *info) +{ + int32_t i; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], level0i = info->leveli[0], + feedbacki = info->feedbacki; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_ch_reverb_delay(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_ch_reverb_delay(info); + return; + } + + for (i = 0; i < count; i++) + { + bufL[buf_index] = reverb_effect_buffer[i] + imuldiv24(bufL[index0], feedbacki); + buf[i] += imuldiv24(bufL[index0], level0i); + + bufR[buf_index] = reverb_effect_buffer[++i] + imuldiv24(bufR[index0], feedbacki); + buf[i] += imuldiv24(bufR[index0], level0i); + + if (++index0 == buf_size) {index0 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + memset(reverb_effect_buffer, 0, sizeof(int32_t) * count); + info->index[0] = index0; + delayL->index = delayR->index = buf_index; +} + +/* */ +/* Plate Reverberator */ +/* */ +#define PLATE_SAMPLERATE 29761.0 +#define PLATE_DECAY 0.50 +#define PLATE_DECAY_DIFFUSION1 0.70 +#define PLATE_DECAY_DIFFUSION2 0.50 +#define PLATE_INPUT_DIFFUSION1 0.750 +#define PLATE_INPUT_DIFFUSION2 0.625 +#define PLATE_BANDWIDTH 0.9955 +#define PLATE_DAMPING 0.0005 +#define PLATE_WET 0.25 + +/*! calculate delay sample in current sample-rate */ +int32_t Reverb::get_plate_delay(double delay, double t) +{ + return (int32_t)(delay * playback_rate * t / PLATE_SAMPLERATE); +} + +/*! Plate Reverberator; this implementation is specialized for system effect. */ +void Reverb::do_ch_plate_reverb(int32_t *buf, int32_t count, InfoPlateReverb *info) +{ + int32_t i; + int32_t x, xd, val, outl, outr, temp1, temp2, temp3; + simple_delay *pd = &(info->pd), *od1l = &(info->od1l), *od2l = &(info->od2l), + *od3l = &(info->od3l), *od4l = &(info->od4l), *od5l = &(info->od5l), + *od6l = &(info->od6l), *od1r = &(info->od1r), *od2r = &(info->od2r), + *od3r = &(info->od3r), *od4r = &(info->od4r), *od5r = &(info->od5r), + *od7r = &(info->od7r), *od7l = &(info->od7l), *od6r = &(info->od6r), + *td1 = &(info->td1), *td2 = &(info->td2), *td1d = &(info->td1d), *td2d = &(info->td2d); + allpass *ap1 = &(info->ap1), *ap2 = &(info->ap2), *ap3 = &(info->ap3), + *ap4 = &(info->ap4), *ap6 = &(info->ap6), *ap6d = &(info->ap6d); + mod_allpass *ap5 = &(info->ap5), *ap5d = &(info->ap5d); + lfo *lfo1 = &(info->lfo1), *lfo1d = &(info->lfo1d); + filter_lowpass1 *lpf1 = &(info->lpf1), *lpf2 = &(info->lpf2); + int32_t t1 = info->t1, t1d = info->t1d; + int32_t decayi = info->decayi, ddif1i = info->ddif1i, ddif2i = info->ddif2i, + idif1i = info->idif1i, idif2i = info->idif2i; + double t; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_lfo(lfo1, 1.30, LFO_SINE, 0); + init_lfo(lfo1d, 1.30, LFO_SINE, 0); + t = reverb_time_table[reverb_status_gs.time] / reverb_time_table[64] - 1.0; + t = 1.0 + t / 2; + set_delay(pd, reverb_status_gs.pre_delay_time * playback_rate / 1000); + set_delay(td1, get_plate_delay(4453, t)), + set_delay(td1d, get_plate_delay(4217, t)); + set_delay(td2, get_plate_delay(3720, t)); + set_delay(td2d, get_plate_delay(3163, t)); + set_delay(od1l, get_plate_delay(266, t)); + set_delay(od2l, get_plate_delay(2974, t)); + set_delay(od3l, get_plate_delay(1913, t)); + set_delay(od4l, get_plate_delay(1996, t)); + set_delay(od5l, get_plate_delay(1990, t)); + set_delay(od6l, get_plate_delay(187, t)); + set_delay(od7l, get_plate_delay(1066, t)); + set_delay(od1r, get_plate_delay(353, t)); + set_delay(od2r, get_plate_delay(3627, t)); + set_delay(od3r, get_plate_delay(1228, t)); + set_delay(od4r, get_plate_delay(2673, t)); + set_delay(od5r, get_plate_delay(2111, t)); + set_delay(od6r, get_plate_delay(335, t)); + set_delay(od7r, get_plate_delay(121, t)); + set_allpass(ap1, get_plate_delay(142, t), PLATE_INPUT_DIFFUSION1); + set_allpass(ap2, get_plate_delay(107, t), PLATE_INPUT_DIFFUSION1); + set_allpass(ap3, get_plate_delay(379, t), PLATE_INPUT_DIFFUSION2); + set_allpass(ap4, get_plate_delay(277, t), PLATE_INPUT_DIFFUSION2); + set_allpass(ap6, get_plate_delay(1800, t), PLATE_DECAY_DIFFUSION2); + set_allpass(ap6d, get_plate_delay(2656, t), PLATE_DECAY_DIFFUSION2); + set_mod_allpass(ap5, get_plate_delay(672, t), get_plate_delay(16, t), PLATE_DECAY_DIFFUSION1); + set_mod_allpass(ap5d, get_plate_delay(908, t), get_plate_delay(16, t), PLATE_DECAY_DIFFUSION1); + lpf1->a = PLATE_BANDWIDTH, lpf2->a = 1.0 - PLATE_DAMPING; + init_filter_lowpass1(lpf1); + init_filter_lowpass1(lpf2); + info->t1 = info->t1d = 0; + info->decay = PLATE_DECAY; + info->decayi = TIM_FSCALE(info->decay, 24); + info->ddif1 = PLATE_DECAY_DIFFUSION1; + info->ddif1i = TIM_FSCALE(info->ddif1, 24); + info->ddif2 = PLATE_DECAY_DIFFUSION2; + info->ddif2i = TIM_FSCALE(info->ddif2, 24); + info->idif1 = PLATE_INPUT_DIFFUSION1; + info->idif1i = TIM_FSCALE(info->idif1, 24); + info->idif2 = PLATE_INPUT_DIFFUSION2; + info->idif2i = TIM_FSCALE(info->idif2, 24); + info->wet = PLATE_WET * (double)reverb_status_gs.level / 127.0; + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(pd); free_delay(td1); free_delay(td1d); free_delay(td2); + free_delay(td2d); free_delay(od1l); free_delay(od2l); free_delay(od3l); + free_delay(od4l); free_delay(od5l); free_delay(od6l); free_delay(od7l); + free_delay(od1r); free_delay(od2r); free_delay(od3r); free_delay(od4r); + free_delay(od5r); free_delay(od6r); free_delay(od7r); free_allpass(ap1); + free_allpass(ap2); free_allpass(ap3); free_allpass(ap4); free_allpass(ap6); + free_allpass(ap6d); free_mod_allpass(ap5); free_mod_allpass(ap5d); + return; + } + + for (i = 0; i < count; i+=2) + { + outr = outl = 0; + x = (reverb_effect_buffer[i] + reverb_effect_buffer[i + 1]) >> 1; + reverb_effect_buffer[i] = reverb_effect_buffer[i + 1] = 0; + + do_delay(&x, pd->buf, pd->size, &pd->index); + do_filter_lowpass1(&x, &lpf1->x1l, lpf1->ai, lpf1->iai); + do_allpass(&x, ap1->buf, ap1->size, &ap1->index, idif1i); + do_allpass(&x, ap2->buf, ap2->size, &ap2->index, idif1i); + do_allpass(&x, ap3->buf, ap3->size, &ap3->index, idif2i); + do_allpass(&x, ap4->buf, ap4->size, &ap4->index, idif2i); + + /* tank structure */ + xd = x; + x += imuldiv24(t1d, decayi); + val = do_lfo(lfo1); + do_mod_allpass(&x, ap5->buf, ap5->size, &ap5->rindex, &ap5->windex, + ap5->ndelay, ap5->depth, val, &ap5->hist, ddif1i); + temp1 = temp2 = temp3 = x; /* n_out_1 */ + do_delay(&temp1, od5l->buf, od5l->size, &od5l->index); + outl -= temp1; /* left output 5 */ + do_delay(&temp2, od1r->buf, od1r->size, &od1r->index); + outr += temp2; /* right output 1 */ + do_delay(&temp3, od2r->buf, od2r->size, &od2r->index); + outr += temp3; /* right output 2 */ + do_delay(&x, td1->buf, td1->size, &td1->index); + do_filter_lowpass1(&x, &lpf2->x1l, lpf2->ai, lpf2->iai); + temp1 = temp2 = x; /* n_out_2 */ + do_delay(&temp1, od6l->buf, od6l->size, &od6l->index); + outl -= temp1; /* left output 6 */ + do_delay(&temp2, od3r->buf, od3r->size, &od3r->index); + outr -= temp2; /* right output 3 */ + x = imuldiv24(x, decayi); + do_allpass(&x, ap6->buf, ap6->size, &ap6->index, ddif2i); + temp1 = temp2 = x; /* n_out_3 */ + do_delay(&temp1, od7l->buf, od7l->size, &od7l->index); + outl -= temp1; /* left output 7 */ + do_delay(&temp2, od4r->buf, od4r->size, &od4r->index); + outr += temp2; /* right output 4 */ + do_delay(&x, td2->buf, td2->size, &td2->index); + t1 = x; + + xd += imuldiv24(t1, decayi); + val = do_lfo(lfo1d); + do_mod_allpass(&x, ap5d->buf, ap5d->size, &ap5d->rindex, &ap5d->windex, + ap5d->ndelay, ap5d->depth, val, &ap5d->hist, ddif1i); + temp1 = temp2 = temp3 = xd; /* n_out_4 */ + do_delay(&temp1, od1l->buf, od1l->size, &od1l->index); + outl += temp1; /* left output 1 */ + do_delay(&temp2, od2l->buf, od2l->size, &od2l->index); + outl += temp2; /* left output 2 */ + do_delay(&temp3, od6r->buf, od6r->size, &od6r->index); + outr -= temp3; /* right output 6 */ + do_delay(&xd, td1d->buf, td1d->size, &td1d->index); + do_filter_lowpass1(&xd, &lpf2->x1r, lpf2->ai, lpf2->iai); + temp1 = temp2 = xd; /* n_out_5 */ + do_delay(&temp1, od3l->buf, od3l->size, &od3l->index); + outl -= temp1; /* left output 3 */ + do_delay(&temp2, od6r->buf, od6r->size, &od6r->index); + outr -= temp2; /* right output 6 */ + xd = imuldiv24(xd, decayi); + do_allpass(&xd, ap6d->buf, ap6d->size, &ap6d->index, ddif2i); + temp1 = temp2 = xd; /* n_out_6 */ + do_delay(&temp1, od4l->buf, od4l->size, &od4l->index); + outl += temp1; /* left output 4 */ + do_delay(&temp2, od7r->buf, od7r->size, &od7r->index); + outr -= temp2; /* right output 7 */ + do_delay(&xd, td2d->buf, td2d->size, &td2d->index); + t1d = xd; + + buf[i] += outl; + buf[i + 1] += outr; + } + info->t1 = t1, info->t1d = t1d; +} + +/*! initialize Reverb Effect */ +void Reverb::init_reverb(void) +{ + init_filter_lowpass1(&(reverb_status_gs.lpf)); + /* Only initialize freeverb if stereo output */ + /* Old non-freeverb must be initialized for mono reverb not to crash */ + if ( (timidity_reverb == 3 || timidity_reverb == 4 + || (timidity_reverb < 0 && ! (timidity_reverb & 0x100)))) { + switch(reverb_status_gs.character) { /* select reverb algorithm */ + case 5: /* Plate Reverb */ + do_ch_plate_reverb(NULL, MAGIC_INIT_EFFECT_INFO, &(reverb_status_gs.info_plate_reverb)); + REV_INP_LEV = reverb_status_gs.info_plate_reverb.wet; + break; + case 6: /* Delay */ + do_ch_reverb_normal_delay(NULL, MAGIC_INIT_EFFECT_INFO, &(reverb_status_gs.info_reverb_delay)); + REV_INP_LEV = 1.0; + break; + case 7: /* Panning Delay */ + do_ch_reverb_panning_delay(NULL, MAGIC_INIT_EFFECT_INFO, &(reverb_status_gs.info_reverb_delay)); + REV_INP_LEV = 1.0; + break; + default: /* Freeverb */ + do_ch_freeverb(NULL, MAGIC_INIT_EFFECT_INFO, &(reverb_status_gs.info_freeverb)); + REV_INP_LEV = reverb_status_gs.info_freeverb.wet; + break; + } + } else { /* Old Reverb */ + do_ch_standard_reverb(NULL, MAGIC_INIT_EFFECT_INFO, &(reverb_status_gs.info_standard_reverb)); + REV_INP_LEV = 1.0; + } + memset(reverb_effect_buffer, 0, reverb_effect_bufsize); + memset(direct_buffer, 0, direct_bufsize); +} + +void Reverb::do_ch_reverb(int32_t *buf, int32_t count) +{ +#ifdef SYS_EFFECT_PRE_LPF + if ((timidity_reverb == 3 || timidity_reverb == 4 + || (timidity_reverb < 0 && ! (timidity_reverb & 0x100))) && reverb_status_gs.pre_lpf) + do_filter_lowpass1_stereo(reverb_effect_buffer, count, &(reverb_status_gs.lpf)); +#endif /* SYS_EFFECT_PRE_LPF */ + if (timidity_reverb == 3 || timidity_reverb == 4 + || (timidity_reverb < 0 && ! (timidity_reverb & 0x100))) { + switch(reverb_status_gs.character) { /* select reverb algorithm */ + case 5: /* Plate Reverb */ + do_ch_plate_reverb(buf, count, &(reverb_status_gs.info_plate_reverb)); + REV_INP_LEV = reverb_status_gs.info_plate_reverb.wet; + break; + case 6: /* Delay */ + do_ch_reverb_normal_delay(buf, count, &(reverb_status_gs.info_reverb_delay)); + REV_INP_LEV = 1.0; + break; + case 7: /* Panning Delay */ + do_ch_reverb_panning_delay(buf, count, &(reverb_status_gs.info_reverb_delay)); + REV_INP_LEV = 1.0; + break; + default: /* Freeverb */ + do_ch_freeverb(buf, count, &(reverb_status_gs.info_freeverb)); + REV_INP_LEV = reverb_status_gs.info_freeverb.wet; + break; + } + } else { /* Old Reverb */ + do_ch_standard_reverb(buf, count, &(reverb_status_gs.info_standard_reverb)); + } +} + +/* */ +/* Delay Effect */ +/* */ + +void Reverb::init_ch_delay(void) +{ + memset(delay_effect_buffer, 0, sizeof(delay_effect_buffer)); + init_filter_lowpass1(&(delay_status_gs.lpf)); + do_ch_3tap_delay(NULL, MAGIC_INIT_EFFECT_INFO, &(delay_status_gs.info_delay)); +} + +void Reverb::do_ch_delay(int32_t *buf, int32_t count) +{ +#ifdef SYS_EFFECT_PRE_LPF + if ((timidity_reverb == 3 || timidity_reverb == 4 + || (timidity_reverb < 0 && ! (timidity_reverb & 0x100))) && delay_status_gs.pre_lpf) + do_filter_lowpass1_stereo(delay_effect_buffer, count, &(delay_status_gs.lpf)); +#endif /* SYS_EFFECT_PRE_LPF */ + switch (delay_status_gs.type) { + case 1: + do_ch_3tap_delay(buf, count, &(delay_status_gs.info_delay)); + break; + case 2: + do_ch_cross_delay(buf, count, &(delay_status_gs.info_delay)); + break; + default: + do_ch_normal_delay(buf, count, &(delay_status_gs.info_delay)); + break; + } +} + +void Reverb::set_ch_delay(int32_t *sbuffer, int32_t n, int32_t level) +{ + int32_t i; + if(!level) {return;} + double send_level = (double)level / 127.0; + + for(i = 0; i < n; i++) + { + delay_effect_buffer[i] += int32_t(sbuffer[i] * send_level); + } +} + +/*! initialize Delay Effect; this implementation is specialized for system effect. */ +void Reverb::init_ch_3tap_delay(InfoDelay3 *info) +{ + int32_t i, x; + + for (i = 0; i < 3; i++) { + info->size[i] = delay_status_gs.sample[i]; + } + x = info->size[0]; /* find maximum value */ + for (i = 1; i < 3; i++) { + if (info->size[i] > x) {x = info->size[i];} + } + x += 1; /* allowance */ + set_delay(&(info->delayL), x); + set_delay(&(info->delayR), x); + for (i = 0; i < 3; i++) { + info->index[i] = (x - info->size[i]) % x; /* set start-point */ + info->level[i] = delay_status_gs.level_ratio[i] * MASTER_DELAY_LEVEL; + info->leveli[i] = TIM_FSCALE(info->level[i], 24); + } + info->feedback = delay_status_gs.feedback_ratio; + info->send_reverb = delay_status_gs.send_reverb_ratio * REV_INP_LEV; + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->send_reverbi = TIM_FSCALE(info->send_reverb, 24); +} + +void Reverb::free_ch_3tap_delay(InfoDelay3 *info) +{ + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); +} + +/*! 3-Tap Stereo Delay Effect; this implementation is specialized for system effect. */ +void Reverb::do_ch_3tap_delay(int32_t *buf, int32_t count, InfoDelay3 *info) +{ + int32_t i, x; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], index1 = info->index[1], index2 = info->index[2]; + int32_t level0i = info->leveli[0], level1i = info->leveli[1], level2i = info->leveli[2], + feedbacki = info->feedbacki, send_reverbi = info->send_reverbi; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_ch_3tap_delay(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_ch_3tap_delay(info); + return; + } + + for (i = 0; i < count; i++) + { + bufL[buf_index] = delay_effect_buffer[i] + imuldiv24(bufL[index0], feedbacki); + x = imuldiv24(bufL[index0], level0i) + imuldiv24(bufL[index1] + bufR[index1], level1i); + buf[i] += x; + reverb_effect_buffer[i] += imuldiv24(x, send_reverbi); + + bufR[buf_index] = delay_effect_buffer[++i] + imuldiv24(bufR[index0], feedbacki); + x = imuldiv24(bufR[index0], level0i) + imuldiv24(bufL[index2] + bufR[index2], level2i); + buf[i] += x; + reverb_effect_buffer[i] += imuldiv24(x, send_reverbi); + + if (++index0 == buf_size) {index0 = 0;} + if (++index1 == buf_size) {index1 = 0;} + if (++index2 == buf_size) {index2 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + memset(delay_effect_buffer, 0, sizeof(int32_t) * count); + info->index[0] = index0, info->index[1] = index1, info->index[2] = index2; + delayL->index = delayR->index = buf_index; +} + +/*! Cross Delay Effect; this implementation is specialized for system effect. */ +void Reverb::do_ch_cross_delay(int32_t *buf, int32_t count, InfoDelay3 *info) +{ + int32_t i, l, r; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], level0i = info->leveli[0], + feedbacki = info->feedbacki, send_reverbi = info->send_reverbi; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_ch_3tap_delay(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_ch_3tap_delay(info); + return; + } + + for (i = 0; i < count; i++) + { + bufL[buf_index] = delay_effect_buffer[i] + imuldiv24(bufR[index0], feedbacki); + l = imuldiv24(bufL[index0], level0i); + bufR[buf_index] = delay_effect_buffer[i + 1] + imuldiv24(bufL[index0], feedbacki); + r = imuldiv24(bufR[index0], level0i); + + buf[i] += r; + reverb_effect_buffer[i] += imuldiv24(r, send_reverbi); + buf[++i] += l; + reverb_effect_buffer[i] += imuldiv24(l, send_reverbi); + + if (++index0 == buf_size) {index0 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + memset(delay_effect_buffer, 0, sizeof(int32_t) * count); + info->index[0] = index0; + delayL->index = delayR->index = buf_index; +} + +/*! Normal Delay Effect; this implementation is specialized for system effect. */ +void Reverb::do_ch_normal_delay(int32_t *buf, int32_t count, InfoDelay3 *info) +{ + int32_t i, x; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], level0i = info->leveli[0], + feedbacki = info->feedbacki, send_reverbi = info->send_reverbi; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_ch_3tap_delay(info); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_ch_3tap_delay(info); + return; + } + + for (i = 0; i < count; i++) + { + bufL[buf_index] = delay_effect_buffer[i] + imuldiv24(bufL[index0], feedbacki); + x = imuldiv24(bufL[index0], level0i); + buf[i] += x; + reverb_effect_buffer[i] += imuldiv24(x, send_reverbi); + + bufR[buf_index] = delay_effect_buffer[++i] + imuldiv24(bufR[index0], feedbacki); + x = imuldiv24(bufR[index0], level0i); + buf[i] += x; + reverb_effect_buffer[i] += imuldiv24(x, send_reverbi); + + if (++index0 == buf_size) {index0 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + memset(delay_effect_buffer, 0, sizeof(int32_t) * count); + info->index[0] = index0; + delayL->index = delayR->index = buf_index; +} + +/* */ +/* Chorus Effect */ +/* */ + +/*! Stereo Chorus; this implementation is specialized for system effect. */ +void Reverb::do_ch_stereo_chorus(int32_t *buf, int32_t count, InfoStereoChorus *info) +{ + int32_t i, output, f0, f1, v0, v1; + int32_t *bufL = info->delayL.buf, *bufR = info->delayR.buf, + *lfobufL = info->lfoL.buf, *lfobufR = info->lfoR.buf, + icycle = info->lfoL.icycle, cycle = info->lfoL.cycle, + leveli = info->leveli, feedbacki = info->feedbacki, + send_reverbi = info->send_reverbi, send_delayi = info->send_delayi, + depth = info->depth, pdelay = info->pdelay, rpt0 = info->rpt0; + int32_t wpt0 = info->wpt0, spt0 = info->spt0, spt1 = info->spt1, + hist0 = info->hist0, hist1 = info->hist1, lfocnt = info->lfoL.count; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_lfo(&(info->lfoL), (double)chorus_status_gs.rate * 0.122, LFO_TRIANGULAR, 0); + init_lfo(&(info->lfoR), (double)chorus_status_gs.rate * 0.122, LFO_TRIANGULAR, 90); + info->pdelay = chorus_delay_time_table[chorus_status_gs.delay] * (double)playback_rate / 1000.0; + info->depth = (double)(chorus_status_gs.depth + 1) / 3.2 * (double)playback_rate / 1000.0; + info->pdelay -= info->depth / 2; /* NOMINAL_DELAY to delay */ + if (info->pdelay < 1) {info->pdelay = 1;} + info->rpt0 = info->pdelay + info->depth + 2; /* allowance */ + set_delay(&(info->delayL), info->rpt0); + set_delay(&(info->delayR), info->rpt0); + info->feedback = (double)chorus_status_gs.feedback * 0.763 / 100.0; + info->level = (double)chorus_status_gs.level / 127.0 * MASTER_CHORUS_LEVEL; + info->send_reverb = (double)chorus_status_gs.send_reverb * 0.787 / 100.0 * REV_INP_LEV; + info->send_delay = (double)chorus_status_gs.send_delay * 0.787 / 100.0; + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->leveli = TIM_FSCALE(info->level, 24); + info->send_reverbi = TIM_FSCALE(info->send_reverb, 24); + info->send_delayi = TIM_FSCALE(info->send_delay, 24); + info->wpt0 = info->spt0 = info->spt1 = info->hist0 = info->hist1 = 0; + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + if (bufL == nullptr) + { + set_delay(&(info->delayL), info->rpt0); + set_delay(&(info->delayR), info->rpt0); + bufL = info->delayL.buf; + bufR = info->delayR.buf; + } + + /* LFO */ + f0 = imuldiv24(lfobufL[imuldiv24(lfocnt, icycle)], depth); + spt0 = wpt0 - pdelay - (f0 >> 8); /* integral part of delay */ + f0 = 0xFF - (f0 & 0xFF); /* (1 - frac) * 256 */ + if(spt0 < 0) {spt0 += rpt0;} + f1 = imuldiv24(lfobufR[imuldiv24(lfocnt, icycle)], depth); + spt1 = wpt0 - pdelay - (f1 >> 8); /* integral part of delay */ + f1 = 0xFF - (f1 & 0xFF); /* (1 - frac) * 256 */ + if(spt1 < 0) {spt1 += rpt0;} + + for(i = 0; i < count; i++) { + v0 = bufL[spt0]; + v1 = bufR[spt1]; + + /* LFO */ + if(++wpt0 == rpt0) {wpt0 = 0;} + f0 = imuldiv24(lfobufL[imuldiv24(lfocnt, icycle)], depth); + spt0 = wpt0 - pdelay - (f0 >> 8); /* integral part of delay */ + f0 = 0xFF - (f0 & 0xFF); /* (1 - frac) * 256 */ + if(spt0 < 0) {spt0 += rpt0;} + f1 = imuldiv24(lfobufR[imuldiv24(lfocnt, icycle)], depth); + spt1 = wpt0 - pdelay - (f1 >> 8); /* integral part of delay */ + f1 = 0xFF - (f1 & 0xFF); /* (1 - frac) * 256 */ + if(spt1 < 0) {spt1 += rpt0;} + if(++lfocnt == cycle) {lfocnt = 0;} + + /* left */ + /* delay with all-pass interpolation */ + output = hist0 = v0 + imuldiv8(bufL[spt0] - hist0, f0); + bufL[wpt0] = chorus_effect_buffer[i] + imuldiv24(output, feedbacki); + output = imuldiv24(output, leveli); + buf[i] += output; + /* send to other system effects (it's peculiar to GS) */ + reverb_effect_buffer[i] += imuldiv24(output, send_reverbi); + delay_effect_buffer[i] += imuldiv24(output, send_delayi); + + /* right */ + /* delay with all-pass interpolation */ + output = hist1 = v1 + imuldiv8(bufR[spt1] - hist1, f1); + bufR[wpt0] = chorus_effect_buffer[++i] + imuldiv24(output, feedbacki); + output = imuldiv24(output, leveli); + buf[i] += output; + /* send to other system effects (it's peculiar to GS) */ + reverb_effect_buffer[i] += imuldiv24(output, send_reverbi); + delay_effect_buffer[i] += imuldiv24(output, send_delayi); + } + memset(chorus_effect_buffer, 0, sizeof(int32_t) * count); + info->wpt0 = wpt0, info->spt0 = spt0, info->spt1 = spt1, + info->hist0 = hist0, info->hist1 = hist1; + info->lfoL.count = info->lfoR.count = lfocnt; +} + +void Reverb::init_ch_chorus(void) +{ + /* clear delay-line of LPF */ + init_filter_lowpass1(&(chorus_status_gs.lpf)); + do_ch_stereo_chorus(NULL, MAGIC_INIT_EFFECT_INFO, &(chorus_status_gs.info_stereo_chorus)); + memset(chorus_effect_buffer, 0, sizeof(chorus_effect_buffer)); +} + +void Reverb::set_ch_chorus(int32_t *sbuffer,int32_t n, int32_t level) +{ + int32_t i; + int32_t count = n; + if(!level) {return;} + double send_level = (double)level / 127.0; + + for(i = 0; i < count; i++) + { + chorus_effect_buffer[i] += int32_t(sbuffer[i] * send_level); + } +} + +void Reverb::do_ch_chorus(int32_t *buf, int32_t count) +{ +#ifdef SYS_EFFECT_PRE_LPF + if ((timidity_reverb == 3 || timidity_reverb == 4 + || (timidity_reverb < 0 && ! (timidity_reverb & 0x100))) && chorus_status_gs.pre_lpf) + do_filter_lowpass1_stereo(chorus_effect_buffer, count, &(chorus_status_gs.lpf)); +#endif /* SYS_EFFECT_PRE_LPF */ + + do_ch_stereo_chorus(buf, count, &(chorus_status_gs.info_stereo_chorus)); +} + +/* */ +/* EQ (Equalizer) */ +/* */ + +void Reverb::init_eq_gs() +{ + memset(eq_buffer, 0, sizeof(eq_buffer)); + calc_filter_shelving_low(&(eq_status_gs.lsf)); + calc_filter_shelving_high(&(eq_status_gs.hsf)); +} + +void Reverb::do_ch_eq_gs(int32_t* buf, int32_t count) +{ + int32_t i; + + do_shelving_filter_stereo(eq_buffer, count, &(eq_status_gs.lsf)); + do_shelving_filter_stereo(eq_buffer, count, &(eq_status_gs.hsf)); + + for(i = 0; i < count; i++) { + buf[i] += eq_buffer[i]; + eq_buffer[i] = 0; + } +} + +void Reverb::do_ch_eq_xg(int32_t* buf, int32_t count, struct part_eq_xg *p) +{ + if(p->bass - 0x40 != 0) { + do_shelving_filter_stereo(buf, count, &(p->basss)); + } + if(p->treble - 0x40 != 0) { + do_shelving_filter_stereo(buf, count, &(p->trebles)); + } +} + +void Reverb::do_multi_eq_xg(int32_t* buf, int32_t count) +{ + if(multi_eq_xg.valid1) { + if(multi_eq_xg.shape1) { /* peaking */ + do_peaking_filter_stereo(buf, count, &(multi_eq_xg.eq1p)); + } else { /* shelving */ + do_shelving_filter_stereo(buf, count, &(multi_eq_xg.eq1s)); + } + } + if(multi_eq_xg.valid2) { + do_peaking_filter_stereo(buf, count, &(multi_eq_xg.eq2p)); + } + if(multi_eq_xg.valid3) { + do_peaking_filter_stereo(buf, count, &(multi_eq_xg.eq3p)); + } + if(multi_eq_xg.valid4) { + do_peaking_filter_stereo(buf, count, &(multi_eq_xg.eq4p)); + } + if(multi_eq_xg.valid5) { + if(multi_eq_xg.shape5) { /* peaking */ + do_peaking_filter_stereo(buf, count, &(multi_eq_xg.eq5p)); + } else { /* shelving */ + do_shelving_filter_stereo(buf, count, &(multi_eq_xg.eq5s)); + } + } +} + +void Reverb::set_ch_eq_gs(int32_t *sbuffer, int32_t n) +{ + int32_t i; + + for(i = 0; i < n; i++) + { + eq_buffer[i] += sbuffer[i]; + } +} + + +/* */ +/* Insertion and Variation Effect */ +/* */ +void Reverb::do_insertion_effect_gs(int32_t *buf, int32_t count) +{ + do_effect_list(buf, count, insertion_effect_gs.ef); +} + +void Reverb::do_insertion_effect_xg(int32_t *buf, int32_t count, struct effect_xg_t *st) +{ + do_effect_list(buf, count, st->ef); +} + +void Reverb::do_variation_effect1_xg(int32_t *buf, int32_t count) +{ + int32_t i, x; + int32_t send_reverbi = TIM_FSCALE((double)variation_effect_xg[0].send_reverb * (0.787 / 100.0 * REV_INP_LEV), 24), + send_chorusi = TIM_FSCALE((double)variation_effect_xg[0].send_chorus * (0.787 / 100.0), 24); + if (variation_effect_xg[0].connection == XG_CONN_SYSTEM) { + do_effect_list(delay_effect_buffer, count, variation_effect_xg[0].ef); + for (i = 0; i < count; i++) { + x = delay_effect_buffer[i]; + buf[i] += x; + reverb_effect_buffer[i] += imuldiv24(x, send_reverbi); + chorus_effect_buffer[i] += imuldiv24(x, send_chorusi); + } + } + memset(delay_effect_buffer, 0, sizeof(int32_t) * count); +} + +void Reverb::do_ch_chorus_xg(int32_t *buf, int32_t count) +{ + int32_t i; + int32_t send_reverbi = TIM_FSCALE((double)chorus_status_xg.send_reverb * (0.787 / 100.0 * REV_INP_LEV), 24); + + do_effect_list(chorus_effect_buffer, count, chorus_status_xg.ef); + for (i = 0; i < count; i++) { + buf[i] += chorus_effect_buffer[i]; + reverb_effect_buffer[i] += imuldiv24(chorus_effect_buffer[i], send_reverbi); + } + memset(chorus_effect_buffer, 0, sizeof(int32_t) * count); +} + +void Reverb::do_ch_reverb_xg(int32_t *buf, int32_t count) +{ + int32_t i; + + do_effect_list(reverb_effect_buffer, count, reverb_status_xg.ef); + for (i = 0; i < count; i++) { + buf[i] += reverb_effect_buffer[i]; + } + memset(reverb_effect_buffer, 0, sizeof(int32_t) * count); +} + +void Reverb::init_ch_effect_xg(void) +{ + memset(reverb_effect_buffer, 0, sizeof(reverb_effect_buffer)); + memset(chorus_effect_buffer, 0, sizeof(chorus_effect_buffer)); + memset(delay_effect_buffer, 0, sizeof(delay_effect_buffer)); +} + +void Reverb::alloc_effect(EffectList *ef) +{ + int i; + + ef->engine = NULL; + for(i = 0; effect_engine[i].type != -1; i++) { + if (effect_engine[i].type == ef->type) { + ef->engine = &(effect_engine[i]); + break; + } + } + if (ef->engine == NULL) {return;} + + if (ef->info != NULL) { + free(ef->info); + ef->info = NULL; + } + ef->info = safe_malloc(ef->engine->info_size); + memset(ef->info, 0, ef->engine->info_size); + +/* //printMessage(CMSG_INFO,VERB_NOISY,"Effect Engine: %s", ef->engine->name); */ +} + +/*! allocate new effect item and add it into the tail of effect list. + EffectList *efc: pointer to the top of effect list. + int8_t type: type of new effect item. + void *info: pointer to infomation of new effect item. */ +EffectList *Reverb::push_effect(EffectList *efc, int type) +{ + EffectList *eft, *efn; + if (type == EFFECT_NONE) {return NULL;} + efn = (EffectList *)safe_malloc(sizeof(EffectList)); + memset(efn, 0, sizeof(EffectList)); + efn->type = type; + efn->next_ef = NULL; + efn->info = NULL; + alloc_effect(efn); + + if(efc == NULL) { + efc = efn; + } else { + eft = efc; + while(eft->next_ef != NULL) { + eft = eft->next_ef; + } + eft->next_ef = efn; + } + return efc; +} + +/*! process all items of effect list. */ +void Reverb::do_effect_list(int32_t *buf, int32_t count, EffectList *ef) +{ + EffectList *efc = ef; + if(ef == NULL) {return;} + while(efc != NULL && efc->engine->do_effect != NULL) + { + (this->*(efc->engine->do_effect))(buf, count, efc); + efc = efc->next_ef; + } +} + +/*! free all items of effect list. */ +void Reverb::free_effect_list(EffectList *ef) +{ + EffectList *efc, *efn; + efc = ef; + if (efc == NULL) {return;} + do { + efn = efc->next_ef; + if(efc->info != NULL) { + (this->*(efc->engine->do_effect))(NULL, MAGIC_FREE_EFFECT_INFO, efc); + free(efc->info); + efc->info = NULL; + } + efc->engine = NULL; + free(efc); + efc = NULL; + } while ((efc = efn) != NULL); +} + +/*! 2-Band EQ */ +void Reverb::do_eq2(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoEQ2 *eq = (InfoEQ2 *)ef->info; + if(count == MAGIC_INIT_EFFECT_INFO) { + eq->lsf.q = 0; + eq->lsf.freq = eq->low_freq; + eq->lsf.gain = eq->low_gain; + calc_filter_shelving_low(&(eq->lsf)); + eq->hsf.q = 0; + eq->hsf.freq = eq->high_freq; + eq->hsf.gain = eq->high_gain; + calc_filter_shelving_high(&(eq->hsf)); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + if(eq->low_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->lsf)); + } + if(eq->high_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->hsf)); + } +} + +/*! panning (pan = [0, 127]) */ +int32_t Reverb::do_left_panning(int32_t sample, int32_t pan) +{ + return imuldiv8(sample, 256 - pan - pan); +} + +int32_t Reverb::do_right_panning(int32_t sample, int32_t pan) +{ + return imuldiv8(sample, pan + pan); +} + +#define OD_BITS 28 +#define OD_MAX_NEG (1.0 / (double)(1L << OD_BITS)) +#define OD_DRIVE_GS 4.0 +#define OD_LEVEL_GS 0.5 +#define STEREO_OD_BITS 27 +#define STEREO_OD_MAX_NEG (1.0 / (double)(1L << STEREO_OD_BITS)) +#define OVERDRIVE_DIST 4.0 +#define OVERDRIVE_RES 0.1 +#define OVERDRIVE_LEVEL 1.0 +#define OVERDRIVE_OFFSET 0 +#define DISTORTION_DIST 40.0 +#define DISTORTION_RES 0.2 +#define DISTORTION_LEVEL 0.2 +#define DISTORTION_OFFSET 0 + +double Reverb::calc_gs_drive(int val) +{ + return (OD_DRIVE_GS * (double)val / 127.0 + 1.0); +} + +/*! GS 0x0110: Overdrive 1 */ +void Reverb::do_overdrive1(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoOverdrive1 *info = (InfoOverdrive1 *)ef->info; + filter_moog *svf = &(info->svf); + filter_biquad *lpf1 = &(info->lpf1); + void (Reverb::*do_amp_sim)(int32_t *, int32_t) = info->amp_sim; + int32_t i, input, high, leveli = info->leveli, di = info->di, + pan = info->pan, asdi = TIM_FSCALE(1.0, 24); + + if(count == MAGIC_INIT_EFFECT_INFO) { + /* decompositor */ + svf->freq = 500; + svf->res_dB = 0; + calc_filter_moog(svf); + init_filter_moog(svf); + /* amp simulator */ + info->amp_sim = &Reverb::do_dummy_clipping; + if (info->amp_sw == 1) { + if (info->amp_type <= 3) {info->amp_sim = &Reverb::do_soft_clipping2;} + } + /* waveshaper */ + info->di = TIM_FSCALE(calc_gs_drive(info->drive), 24); + info->leveli = TIM_FSCALE(info->level * OD_LEVEL_GS, 24); + /* anti-aliasing */ + lpf1->freq = 8000.0; + lpf1->q = 1.0; + calc_filter_biquad_low(lpf1); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + for(i = 0; i < count; i+=2) { + input = (buf[i] + buf[i + 1]) >> 1; + /* amp simulation */ + (this->*do_amp_sim)(&input, asdi); + /* decomposition */ + do_filter_moog(&input, &high, svf->f, svf->p, svf->q, + &svf->b0, &svf->b1, &svf->b2, &svf->b3, &svf->b4); + /* waveshaping */ + do_soft_clipping1(&high, di); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1l, &lpf1->x2l, &lpf1->y1l, &lpf1->y2l); + /* mixing */ + input = imuldiv24(high + input, leveli); + buf[i] = do_left_panning(input, pan); + buf[i + 1] = do_right_panning(input, pan); + } +} + +/*! GS 0x0111: Distortion 1 */ +void Reverb::do_distortion1(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoOverdrive1 *info = (InfoOverdrive1 *)ef->info; + filter_moog *svf = &(info->svf); + filter_biquad *lpf1 = &(info->lpf1); + void (Reverb::*do_amp_sim)(int32_t *, int32_t) = info->amp_sim; + int32_t i, input, high, leveli = info->leveli, di = info->di, + pan = info->pan, asdi = TIM_FSCALE(1.0, 24); + + if(count == MAGIC_INIT_EFFECT_INFO) { + /* decompositor */ + svf->freq = 500; + svf->res_dB = 0; + calc_filter_moog(svf); + init_filter_moog(svf); + /* amp simulator */ + info->amp_sim = &Reverb::do_dummy_clipping; + if (info->amp_sw == 1) { + if (info->amp_type <= 3) {info->amp_sim = &Reverb::do_soft_clipping2;} + } + /* waveshaper */ + info->di = TIM_FSCALE(calc_gs_drive(info->drive), 24); + info->leveli = TIM_FSCALE(info->level * OD_LEVEL_GS, 24); + /* anti-aliasing */ + lpf1->freq = 8000.0; + lpf1->q = 1.0; + calc_filter_biquad_low(lpf1); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + for(i = 0; i < count; i+=2) { + input = (buf[i] + buf[i + 1]) >> 1; + /* amp simulation */ + (this->*do_amp_sim)(&input, asdi); + /* decomposition */ + do_filter_moog(&input, &high, svf->f, svf->p, svf->q, + &svf->b0, &svf->b1, &svf->b2, &svf->b3, &svf->b4); + /* waveshaping */ + do_hard_clipping(&high, di); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1l, &lpf1->x2l, &lpf1->y1l, &lpf1->y2l); + /* mixing */ + input = imuldiv24(high + input, leveli); + buf[i] = do_left_panning(input, pan); + buf[i + 1] = do_right_panning(input, pan); + } +} + +/*! GS 0x1103: OD1 / OD2 */ +void Reverb::do_dual_od(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoOD1OD2 *info = (InfoOD1OD2 *)ef->info; + filter_moog *svfl = &(info->svfl), *svfr = &(info->svfr); + filter_biquad *lpf1 = &(info->lpf1); + void (Reverb::*do_amp_siml)(int32_t *, int32_t) = info->amp_siml, + (Reverb::*do_odl)(int32_t *, int32_t) = info->odl, + (Reverb::*do_odr)(int32_t *, int32_t) = info->odr; + int32_t i, inputl, inputr, high, levelli = info->levelli, levelri = info->levelri, + dli = info->dli, dri = info->dri, panl = info->panl, panr = info->panr, asdi = TIM_FSCALE(1.0, 24); + + if(count == MAGIC_INIT_EFFECT_INFO) { + /* left */ + /* decompositor */ + svfl->freq = 500; + svfl->res_dB = 0; + calc_filter_moog(svfl); + init_filter_moog(svfl); + /* amp simulator */ + info->amp_siml = &Reverb::do_dummy_clipping; + if (info->amp_swl == 1) { + if (info->amp_typel <= 3) {info->amp_siml = &Reverb::do_soft_clipping2;} + } + /* waveshaper */ + if(info->typel == 0) {info->odl = &Reverb::do_soft_clipping1;} + else {info->odl = &Reverb::do_hard_clipping;} + info->dli = TIM_FSCALE(calc_gs_drive(info->drivel), 24); + info->levelli = TIM_FSCALE(info->levell * OD_LEVEL_GS, 24); + /* right */ + /* decompositor */ + svfr->freq = 500; + svfr->res_dB = 0; + calc_filter_moog(svfr); + init_filter_moog(svfr); + /* amp simulator */ + info->amp_simr = &Reverb::do_dummy_clipping; + if (info->amp_swr == 1) { + if (info->amp_typer <= 3) {info->amp_simr = &Reverb::do_soft_clipping2;} + } + /* waveshaper */ + if(info->typer == 0) {info->odr = &Reverb::do_soft_clipping1;} + else {info->odr = &Reverb::do_hard_clipping;} + info->dri = TIM_FSCALE(calc_gs_drive(info->driver), 24); + info->levelri = TIM_FSCALE(info->levelr * OD_LEVEL_GS, 24); + /* anti-aliasing */ + lpf1->freq = 8000.0; + lpf1->q = 1.0; + calc_filter_biquad_low(lpf1); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + for(i = 0; i < count; i++) { + /* left */ + inputl = buf[i]; + /* amp simulation */ + (this->*do_amp_siml)(&inputl, asdi); + /* decomposition */ + do_filter_moog(&inputl, &high, svfl->f, svfl->p, svfl->q, + &svfl->b0, &svfl->b1, &svfl->b2, &svfl->b3, &svfl->b4); + /* waveshaping */ + (this->*do_odl)(&high, dli); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1l, &lpf1->x2l, &lpf1->y1l, &lpf1->y2l); + inputl = imuldiv24(high + inputl, levelli); + + /* right */ + inputr = buf[++i]; + /* amp simulation */ + (this->*do_amp_siml)(&inputr, asdi); + /* decomposition */ + do_filter_moog(&inputr, &high, svfr->f, svfr->p, svfr->q, + &svfr->b0, &svfr->b1, &svfr->b2, &svfr->b3, &svfr->b4); + /* waveshaping */ + (this->*do_odr)(&high, dri); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1r, &lpf1->x2r, &lpf1->y1r, &lpf1->y2r); + inputr = imuldiv24(high + inputr, levelri); + + /* panning */ + buf[i - 1] = do_left_panning(inputl, panl) + do_left_panning(inputr, panr); + buf[i] = do_right_panning(inputl, panl) + do_right_panning(inputr, panr); + } +} + +#define HEXA_CHORUS_WET_LEVEL 0.2 +#define HEXA_CHORUS_DEPTH_DEV (1.0 / (20.0 + 1.0)) +#define HEXA_CHORUS_DELAY_DEV (1.0 / (20.0 * 3.0)) + +/*! GS 0x0140: HEXA-CHORUS */ +void Reverb::do_hexa_chorus(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoHexaChorus *info = (InfoHexaChorus *)ef->info; + lfo *lfo = &(info->lfo0); + simple_delay *buf0 = &(info->buf0); + int32_t *ebuf = buf0->buf, size = buf0->size, index = buf0->index; + int32_t spt0 = info->spt0, spt1 = info->spt1, spt2 = info->spt2, + spt3 = info->spt3, spt4 = info->spt4, spt5 = info->spt5, + hist0 = info->hist0, hist1 = info->hist1, hist2 = info->hist2, + hist3 = info->hist3, hist4 = info->hist4, hist5 = info->hist5; + int32_t dryi = info->dryi, weti = info->weti; + int32_t pan0 = info->pan0, pan1 = info->pan1, pan2 = info->pan2, + pan3 = info->pan3, pan4 = info->pan4, pan5 = info->pan5; + int32_t depth0 = info->depth0, depth1 = info->depth1, depth2 = info->depth2, + depth3 = info->depth3, depth4 = info->depth4, depth5 = info->depth5, + pdelay0 = info->pdelay0, pdelay1 = info->pdelay1, pdelay2 = info->pdelay2, + pdelay3 = info->pdelay3, pdelay4 = info->pdelay4, pdelay5 = info->pdelay5; + int32_t i, lfo_val, + v0, v1, v2, v3, v4, v5, f0, f1, f2, f3, f4, f5; + + if(count == MAGIC_INIT_EFFECT_INFO) { + set_delay(buf0, (int32_t)(9600.0 * playback_rate / 44100.0)); + init_lfo(lfo, lfo->freq, LFO_TRIANGULAR, 0); + info->dryi = TIM_FSCALE(info->level * info->dry, 24); + info->weti = TIM_FSCALE(info->level * info->wet * HEXA_CHORUS_WET_LEVEL, 24); + v0 = info->depth * ((double)info->depth_dev * HEXA_CHORUS_DEPTH_DEV); + info->depth0 = info->depth - v0; + info->depth1 = info->depth; + info->depth2 = info->depth + v0; + info->depth3 = info->depth + v0; + info->depth4 = info->depth; + info->depth5 = info->depth - v0; + v0 = info->pdelay * ((double)info->pdelay_dev * HEXA_CHORUS_DELAY_DEV); + info->pdelay0 = info->pdelay + v0; + info->pdelay1 = info->pdelay + v0 * 2; + info->pdelay2 = info->pdelay + v0 * 3; + info->pdelay3 = info->pdelay + v0 * 3; + info->pdelay4 = info->pdelay + v0 * 2; + info->pdelay5 = info->pdelay + v0; + /* in this part, validation check may be necessary. */ + info->pan0 = 64 - info->pan_dev * 3; + info->pan1 = 64 - info->pan_dev * 2; + info->pan2 = 64 - info->pan_dev; + info->pan3 = 64 + info->pan_dev; + info->pan4 = 64 + info->pan_dev * 2; + info->pan5 = 64 + info->pan_dev * 3; + info->hist0 = info->hist1 = info->hist2 + = info->hist3 = info->hist4 = info->hist5 = 0; + info->spt0 = info->spt1 = info->spt2 + = info->spt3 = info->spt4 = info->spt5 = 0; + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(buf0); + return; + } + + /* LFO */ + lfo_val = lfo->buf[imuldiv24(lfo->count, lfo->icycle)]; + f0 = imuldiv24(lfo_val, depth0); + spt0 = index - pdelay0 - (f0 >> 8); /* integral part of delay */ + if(spt0 < 0) {spt0 += size;} + f1 = imuldiv24(lfo_val, depth1); + spt1 = index - pdelay1 - (f1 >> 8); /* integral part of delay */ + if(spt1 < 0) {spt1 += size;} + f2 = imuldiv24(lfo_val, depth2); + spt2 = index - pdelay2 - (f2 >> 8); /* integral part of delay */ + if(spt2 < 0) {spt2 += size;} + f3 = imuldiv24(lfo_val, depth3); + spt3 = index - pdelay3 - (f3 >> 8); /* integral part of delay */ + if(spt3 < 0) {spt3 += size;} + f4 = imuldiv24(lfo_val, depth4); + spt4 = index - pdelay4 - (f4 >> 8); /* integral part of delay */ + if(spt4 < 0) {spt4 += size;} + f5 = imuldiv24(lfo_val, depth5); + spt5 = index - pdelay5 - (f5 >> 8); /* integral part of delay */ + if(spt5 < 0) {spt5 += size;} + + for(i = 0; i < count; i+=2) { + v0 = ebuf[spt0], v1 = ebuf[spt1], v2 = ebuf[spt2], + v3 = ebuf[spt3], v4 = ebuf[spt4], v5 = ebuf[spt5]; + + /* LFO */ + if(++index == size) {index = 0;} + lfo_val = do_lfo(lfo); + f0 = imuldiv24(lfo_val, depth0); + spt0 = index - pdelay0 - (f0 >> 8); /* integral part of delay */ + f0 = 0xFF - (f0 & 0xFF); /* (1 - frac) * 256 */ + if(spt0 < 0) {spt0 += size;} + f1 = imuldiv24(lfo_val, depth1); + spt1 = index - pdelay1 - (f1 >> 8); /* integral part of delay */ + f1 = 0xFF - (f1 & 0xFF); /* (1 - frac) * 256 */ + if(spt1 < 0) {spt1 += size;} + f2 = imuldiv24(lfo_val, depth2); + spt2 = index - pdelay2 - (f2 >> 8); /* integral part of delay */ + f2 = 0xFF - (f2 & 0xFF); /* (1 - frac) * 256 */ + if(spt2 < 0) {spt2 += size;} + f3 = imuldiv24(lfo_val, depth3); + spt3 = index - pdelay3 - (f3 >> 8); /* integral part of delay */ + f3 = 0xFF - (f3 & 0xFF); /* (1 - frac) * 256 */ + if(spt3 < 0) {spt3 += size;} + f4 = imuldiv24(lfo_val, depth4); + spt4 = index - pdelay4 - (f4 >> 8); /* integral part of delay */ + f4 = 0xFF - (f4 & 0xFF); /* (1 - frac) * 256 */ + if(spt4 < 0) {spt4 += size;} + f5 = imuldiv24(lfo_val, depth5); + spt5 = index - pdelay5 - (f5 >> 8); /* integral part of delay */ + f5 = 0xFF - (f5 & 0xFF); /* (1 - frac) * 256 */ + if(spt5 < 0) {spt5 += size;} + + /* chorus effect */ + /* all-pass interpolation */ + hist0 = v0 + imuldiv8(ebuf[spt0] - hist0, f0); + hist1 = v1 + imuldiv8(ebuf[spt1] - hist1, f1); + hist2 = v2 + imuldiv8(ebuf[spt2] - hist2, f2); + hist3 = v3 + imuldiv8(ebuf[spt3] - hist3, f3); + hist4 = v4 + imuldiv8(ebuf[spt4] - hist4, f4); + hist5 = v5 + imuldiv8(ebuf[spt5] - hist5, f5); + ebuf[index] = imuldiv24(buf[i] + buf[i + 1], weti); + + /* mixing */ + buf[i] = do_left_panning(hist0, pan0) + do_left_panning(hist1, pan1) + + do_left_panning(hist2, pan2) + do_left_panning(hist3, pan3) + + do_left_panning(hist4, pan4) + do_left_panning(hist5, pan5) + + imuldiv24(buf[i], dryi); + buf[i + 1] = do_right_panning(hist0, pan0) + do_right_panning(hist1, pan1) + + do_right_panning(hist2, pan2) + do_right_panning(hist3, pan3) + + do_right_panning(hist4, pan4) + do_right_panning(hist5, pan5) + + imuldiv24(buf[i + 1], dryi); + + } + buf0->size = size, buf0->index = index; + info->spt0 = spt0, info->spt1 = spt1, info->spt2 = spt2, + info->spt3 = spt3, info->spt4 = spt4, info->spt5 = spt5, + info->hist0 = hist0, info->hist1 = hist1, info->hist2 = hist2, + info->hist3 = hist3, info->hist4 = hist4, info->hist5 = hist5; +} + +void Reverb::free_effect_xg(struct effect_xg_t *st) +{ + free_effect_list(st->ef); + st->ef = NULL; +} + +void Reverb::free_effect_buffers(void) +{ + int i; + /* free GM/GS/GM2 effects */ + do_ch_standard_reverb(NULL, MAGIC_FREE_EFFECT_INFO, &(reverb_status_gs.info_standard_reverb)); + do_ch_freeverb(NULL, MAGIC_FREE_EFFECT_INFO, &(reverb_status_gs.info_freeverb)); + do_ch_plate_reverb(NULL, MAGIC_FREE_EFFECT_INFO, &(reverb_status_gs.info_plate_reverb)); + do_ch_reverb_normal_delay(NULL, MAGIC_FREE_EFFECT_INFO, &(reverb_status_gs.info_reverb_delay)); + do_ch_stereo_chorus(NULL, MAGIC_FREE_EFFECT_INFO, &(chorus_status_gs.info_stereo_chorus)); + do_ch_3tap_delay(NULL, MAGIC_FREE_EFFECT_INFO, &(delay_status_gs.info_delay)); + free_effect_list(insertion_effect_gs.ef); + insertion_effect_gs.ef = NULL; + /* free XG effects */ + free_effect_xg(&reverb_status_xg); + free_effect_xg(&chorus_status_xg); + for (i = 0; i < XG_VARIATION_EFFECT_NUM; i++) { + free_effect_xg(&variation_effect_xg[i]); + } + for (i = 0; i < XG_INSERTION_EFFECT_NUM; i++) { + free_effect_xg(&insertion_effect_xg[i]); + } +} + +int Reverb::clip_int(int val, int min, int max) +{ + return ((val > max) ? max : (val < min) ? min : val); +} + +void Reverb::conv_gs_eq2(struct insertion_effect_gs_t *ieffect, EffectList *ef) +{ + InfoEQ2 *eq = (InfoEQ2 *)ef->info; + + eq->high_freq = 4000; + eq->high_gain = clip_int(ieffect->parameter[16] - 0x40, -12, 12); + eq->low_freq = 400; + eq->low_gain = clip_int(ieffect->parameter[17] - 0x40, -12, 12); +} + +void Reverb::conv_gs_overdrive1(struct insertion_effect_gs_t *ieffect, EffectList *ef) +{ + InfoOverdrive1 *od = (InfoOverdrive1 *)ef->info; + + od->drive = ieffect->parameter[0]; + od->amp_type = ieffect->parameter[1]; + od->amp_sw = ieffect->parameter[2]; + od->pan = ieffect->parameter[18]; + od->level = (double)ieffect->parameter[19] / 127.0; +} + +void Reverb::conv_gs_dual_od(struct insertion_effect_gs_t *ieffect, EffectList *ef) +{ + InfoOD1OD2 *od = (InfoOD1OD2 *)ef->info; + + od->typel = ieffect->parameter[0]; + od->drivel = ieffect->parameter[1]; + od->amp_typel = ieffect->parameter[2]; + od->amp_swl = ieffect->parameter[3]; + od->typer = ieffect->parameter[5]; + od->driver = ieffect->parameter[6]; + od->amp_typer = ieffect->parameter[7]; + od->amp_swr = ieffect->parameter[8]; + od->panl = ieffect->parameter[15]; + od->levell = (double)ieffect->parameter[16] / 127.0; + od->panr = ieffect->parameter[17]; + od->levelr = (double)ieffect->parameter[18] / 127.0; + od->level = (double)ieffect->parameter[19] / 127.0; +} + +double Reverb::calc_dry_gs(int val) +{ + return ((double)(127 - val) / 127.0); +} + +double Reverb::calc_wet_gs(int val) +{ + return ((double)val / 127.0); +} + +void Reverb::conv_gs_hexa_chorus(struct insertion_effect_gs_t *ieffect, EffectList *ef) +{ + InfoHexaChorus *info = (InfoHexaChorus *)ef->info; + + info->level = (double)ieffect->parameter[19] / 127.0; + info->pdelay = pre_delay_time_table[ieffect->parameter[0]] * (double)playback_rate / 1000.0; + info->depth = (double)(ieffect->parameter[2] + 1) / 3.2 * (double)playback_rate / 1000.0; + info->pdelay -= info->depth / 2; + if(info->pdelay <= 1) {info->pdelay = 1;} + info->lfo0.freq = rate1_table[ieffect->parameter[1]]; + info->pdelay_dev = ieffect->parameter[3]; + info->depth_dev = ieffect->parameter[4] - 64; + info->pan_dev = ieffect->parameter[5]; + info->dry = calc_dry_gs(ieffect->parameter[15]); + info->wet = calc_wet_gs(ieffect->parameter[15]); +} + +double Reverb::calc_dry_xg(int val, struct effect_xg_t *st) +{ + if (st->connection) {return 0.0;} + else {return ((double)(127 - val) / 127.0);} +} + +double Reverb::calc_wet_xg(int val, struct effect_xg_t *st) +{ + switch(st->connection) { + case XG_CONN_SYSTEM: + return ((double)st->ret / 127.0); + case XG_CONN_SYSTEM_CHORUS: + return ((double)st->ret / 127.0); + case XG_CONN_SYSTEM_REVERB: + return ((double)st->ret / 127.0); + default: + return ((double)val / 127.0); + } +} + +/*! 3-Band EQ */ +void Reverb::do_eq3(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoEQ3 *eq = (InfoEQ3 *)ef->info; + if (count == MAGIC_INIT_EFFECT_INFO) { + eq->lsf.q = 0; + eq->lsf.freq = eq->low_freq; + eq->lsf.gain = eq->low_gain; + calc_filter_shelving_low(&(eq->lsf)); + eq->hsf.q = 0; + eq->hsf.freq = eq->high_freq; + eq->hsf.gain = eq->high_gain; + calc_filter_shelving_high(&(eq->hsf)); + eq->peak.q = 1.0 / eq->mid_width; + eq->peak.freq = eq->mid_freq; + eq->peak.gain = eq->mid_gain; + calc_filter_peaking(&(eq->peak)); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + if (eq->low_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->lsf)); + } + if (eq->high_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->hsf)); + } + if (eq->mid_gain != 0) { + do_peaking_filter_stereo(buf, count, &(eq->peak)); + } +} + +/*! Stereo EQ */ +void Reverb::do_stereo_eq(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoStereoEQ *eq = (InfoStereoEQ *)ef->info; + int32_t i, leveli = eq->leveli; + if (count == MAGIC_INIT_EFFECT_INFO) { + eq->lsf.q = 0; + eq->lsf.freq = eq->low_freq; + eq->lsf.gain = eq->low_gain; + calc_filter_shelving_low(&(eq->lsf)); + eq->hsf.q = 0; + eq->hsf.freq = eq->high_freq; + eq->hsf.gain = eq->high_gain; + calc_filter_shelving_high(&(eq->hsf)); + eq->m1.q = eq->m1_q; + eq->m1.freq = eq->m1_freq; + eq->m1.gain = eq->m1_gain; + calc_filter_peaking(&(eq->m1)); + eq->m2.q = eq->m2_q; + eq->m2.freq = eq->m2_freq; + eq->m2.gain = eq->m2_gain; + calc_filter_peaking(&(eq->m2)); + eq->leveli = TIM_FSCALE(eq->level, 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + if (eq->level != 1.0) { + for (i = 0; i < count; i++) { + buf[i] = imuldiv24(buf[i], leveli); + } + } + if (eq->low_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->lsf)); + } + if (eq->high_gain != 0) { + do_shelving_filter_stereo(buf, count, &(eq->hsf)); + } + if (eq->m1_gain != 0) { + do_peaking_filter_stereo(buf, count, &(eq->m1)); + } + if (eq->m2_gain != 0) { + do_peaking_filter_stereo(buf, count, &(eq->m2)); + } +} + +void Reverb::conv_xg_eq2(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ2 *info = (InfoEQ2 *)ef->info; + + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[0], 4, 40)]; + info->low_gain = clip_int(st->param_lsb[1] - 64, -12, 12); + info->high_freq = eq_freq_table_xg[clip_int(st->param_lsb[2], 28, 58)]; + info->high_gain = clip_int(st->param_lsb[3] - 64, -12, 12); +} + +void Reverb::conv_xg_eq3(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ3 *info = (InfoEQ3 *)ef->info; + + info->low_gain = clip_int(st->param_lsb[0] - 64, -12, 12); + info->mid_freq = eq_freq_table_xg[clip_int(st->param_lsb[1], 14, 54)]; + info->mid_gain = clip_int(st->param_lsb[2] - 64, -12, 12); + info->mid_width = (double)clip_int(st->param_lsb[3], 10, 120) / 10.0; + info->high_gain = clip_int(st->param_lsb[4] - 64, -12, 12); + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[5], 4, 40)]; + info->high_freq = eq_freq_table_xg[clip_int(st->param_lsb[6], 28, 58)]; +} + +static const float eq_q_table_gs[] = +{ + 0.5, 1.0, 2.0, 4.0, 9.0, +}; + +void Reverb::conv_gs_stereo_eq(struct insertion_effect_gs_t *st, EffectList *ef) +{ + InfoStereoEQ *info = (InfoStereoEQ *)ef->info; + + info->low_freq = (st->parameter[0] == 0) ? 200 : 400; + info->low_gain = clip_int(st->parameter[1] - 64, -12, 12); + info->high_freq = (st->parameter[2] == 0) ? 4000 : 8000; + info->high_gain = clip_int(st->parameter[3] - 64, -12, 12); + info->m1_freq = eq_freq_table_gs[st->parameter[4]]; + info->m1_q = eq_q_table_gs[clip_int(st->parameter[5], 0, 4)]; + info->m1_gain = clip_int(st->parameter[6] - 64, -12, 12); + info->m2_freq = eq_freq_table_gs[st->parameter[7]]; + info->m2_q = eq_q_table_gs[clip_int(st->parameter[8], 0, 4)]; + info->m2_gain = clip_int(st->parameter[9] - 64, -12, 12); + info->level = (double)st->parameter[19] / 127.0; +} + +void Reverb::conv_xg_chorus_eq3(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ3 *info = (InfoEQ3 *)ef->info; + + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[5], 4, 40)]; + info->low_gain = clip_int(st->param_lsb[6] - 64, -12, 12); + info->high_freq = eq_freq_table_xg[clip_int(st->param_lsb[7], 28, 58)]; + info->high_gain = clip_int(st->param_lsb[8] - 64, -12, 12); + info->mid_freq = eq_freq_table_xg[clip_int(st->param_lsb[10], 14, 54)]; + info->mid_gain = clip_int(st->param_lsb[11] - 64, -12, 12); + info->mid_width = (double)clip_int(st->param_lsb[12], 10, 120) / 10.0; +} + +void Reverb::conv_xg_chorus(struct effect_xg_t *st, EffectList *ef) +{ + InfoChorus *info = (InfoChorus *)ef->info; + + info->rate = lfo_freq_table_xg[st->param_lsb[0]]; + info->depth_ms = (double)(st->param_lsb[1] + 1) / 3.2 / 2.0; + info->feedback = (double)(st->param_lsb[2] - 64) * (0.763 * 2.0 / 100.0); + info->pdelay_ms = mod_delay_offset_table_xg[st->param_lsb[3]]; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); + info->phase_diff = 90.0; +} + +void Reverb::conv_xg_flanger(struct effect_xg_t *st, EffectList *ef) +{ + InfoChorus *info = (InfoChorus *)ef->info; + + info->rate = lfo_freq_table_xg[st->param_lsb[0]]; + info->depth_ms = (double)(st->param_lsb[1] + 1) / 3.2 / 2.0; + info->feedback = (double)(st->param_lsb[2] - 64) * (0.763 * 2.0 / 100.0); + info->pdelay_ms = mod_delay_offset_table_xg[st->param_lsb[2]]; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); + info->phase_diff = (double)(clip_int(st->param_lsb[13], 4, 124) - 64) * 3.0; +} + +void Reverb::conv_xg_symphonic(struct effect_xg_t *st, EffectList *ef) +{ + InfoChorus *info = (InfoChorus *)ef->info; + + info->rate = lfo_freq_table_xg[st->param_lsb[0]]; + info->depth_ms = (double)(st->param_lsb[1] + 1) / 3.2 / 2.0; + info->feedback = 0.0; + info->pdelay_ms = mod_delay_offset_table_xg[st->param_lsb[3]]; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); + info->phase_diff = 90.0; +} + +void Reverb::do_chorus(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoChorus *info = (InfoChorus *)ef->info; + int32_t i, output, f0, f1, v0, v1; + int32_t *bufL = info->delayL.buf, *bufR = info->delayR.buf, + *lfobufL = info->lfoL.buf, *lfobufR = info->lfoR.buf, + icycle = info->lfoL.icycle, cycle = info->lfoL.cycle, + dryi = info->dryi, weti = info->weti, feedbacki = info->feedbacki, + depth = info->depth, pdelay = info->pdelay, rpt0 = info->rpt0; + int32_t wpt0 = info->wpt0, spt0 = info->spt0, spt1 = info->spt1, + hist0 = info->hist0, hist1 = info->hist1, lfocnt = info->lfoL.count; + + if (count == MAGIC_INIT_EFFECT_INFO) { + init_lfo(&(info->lfoL), info->rate, LFO_TRIANGULAR, 0); + init_lfo(&(info->lfoR), info->rate, LFO_TRIANGULAR, info->phase_diff); + info->pdelay = info->pdelay_ms * (double)playback_rate / 1000.0; + info->depth = info->depth_ms * (double)playback_rate / 1000.0; + info->pdelay -= info->depth / 2; /* NOMINAL_DELAY to delay */ + if (info->pdelay < 1) {info->pdelay = 1;} + info->rpt0 = info->pdelay + info->depth + 2; /* allowance */ + set_delay(&(info->delayL), info->rpt0); + set_delay(&(info->delayR), info->rpt0); + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + info->wpt0 = info->spt0 = info->spt1 = info->hist0 = info->hist1 = 0; + return; + } else if (count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + /* LFO */ + f0 = imuldiv24(lfobufL[imuldiv24(lfocnt, icycle)], depth); + spt0 = wpt0 - pdelay - (f0 >> 8); /* integral part of delay */ + f0 = 0xFF - (f0 & 0xFF); /* (1 - frac) * 256 */ + if (spt0 < 0) {spt0 += rpt0;} + f1 = imuldiv24(lfobufR[imuldiv24(lfocnt, icycle)], depth); + spt1 = wpt0 - pdelay - (f1 >> 8); /* integral part of delay */ + f1 = 0xFF - (f1 & 0xFF); /* (1 - frac) * 256 */ + if (spt1 < 0) {spt1 += rpt0;} + + for (i = 0; i < count; i++) { + v0 = bufL[spt0]; + v1 = bufR[spt1]; + + /* LFO */ + if(++wpt0 == rpt0) {wpt0 = 0;} + f0 = imuldiv24(lfobufL[imuldiv24(lfocnt, icycle)], depth); + spt0 = wpt0 - pdelay - (f0 >> 8); /* integral part of delay */ + f0 = 0xFF - (f0 & 0xFF); /* (1 - frac) * 256 */ + if(spt0 < 0) {spt0 += rpt0;} + f1 = imuldiv24(lfobufR[imuldiv24(lfocnt, icycle)], depth); + spt1 = wpt0 - pdelay - (f1 >> 8); /* integral part of delay */ + f1 = 0xFF - (f1 & 0xFF); /* (1 - frac) * 256 */ + if(spt1 < 0) {spt1 += rpt0;} + if(++lfocnt == cycle) {lfocnt = 0;} + + /* left */ + /* delay with all-pass interpolation */ + output = hist0 = v0 + imuldiv8(bufL[spt0] - hist0, f0); + bufL[wpt0] = buf[i] + imuldiv24(output, feedbacki); + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(output, weti); + + /* right */ + /* delay with all-pass interpolation */ + output = hist1 = v1 + imuldiv8(bufR[spt1] - hist1, f1); + bufR[wpt0] = buf[++i] + imuldiv24(output, feedbacki); + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(output, weti); + } + info->wpt0 = wpt0, info->spt0 = spt0, info->spt1 = spt1, + info->hist0 = hist0, info->hist1 = hist1; + info->lfoL.count = info->lfoR.count = lfocnt; +} + +void Reverb::conv_xg_od_eq3(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ3 *info = (InfoEQ3 *)ef->info; + + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[1], 4, 40)]; + info->low_gain = clip_int(st->param_lsb[2] - 64, -12, 12); + info->mid_freq = eq_freq_table_xg[clip_int(st->param_lsb[6], 14, 54)]; + info->mid_gain = clip_int(st->param_lsb[7] - 64, -12, 12); + info->mid_width = (double)clip_int(st->param_lsb[8], 10, 120) / 10.0; + info->high_freq = 0; + info->high_gain = 0; +} + +void Reverb::conv_xg_overdrive(struct effect_xg_t *st, EffectList *ef) +{ + InfoStereoOD *info = (InfoStereoOD *)ef->info; + + info->od = &Reverb::do_soft_clipping1; + info->drive = (double)st->param_lsb[0] / 127.0; + info->cutoff = eq_freq_table_xg[clip_int(st->param_lsb[3], 34, 60)]; + info->level = (double)st->param_lsb[4] / 127.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::conv_xg_distortion(struct effect_xg_t *st, EffectList *ef) +{ + InfoStereoOD *info = (InfoStereoOD *)ef->info; + + info->od = &Reverb::do_hard_clipping; + info->drive = (double)st->param_lsb[0] / 127.0; + info->cutoff = eq_freq_table_xg[clip_int(st->param_lsb[3], 34, 60)]; + info->level = (double)st->param_lsb[4] / 127.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::conv_xg_amp_simulator(struct effect_xg_t *st, EffectList *ef) +{ + InfoStereoOD *info = (InfoStereoOD *)ef->info; + + info->od = &Reverb::do_soft_clipping2; + info->drive = (double)st->param_lsb[0] / 127.0; + info->cutoff = eq_freq_table_xg[clip_int(st->param_lsb[2], 34, 60)]; + info->level = (double)st->param_lsb[3] / 127.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::do_stereo_od(int32_t *buf, int32_t count, EffectList *ef) +{ + InfoStereoOD *info = (InfoStereoOD *)ef->info; + filter_moog *svfl = &(info->svfl), *svfr = &(info->svfr); + filter_biquad *lpf1 = &(info->lpf1); + void (Reverb::*do_od)(int32_t *, int32_t) = info->od; + int32_t i, inputl, inputr, high, weti = info->weti, dryi = info->dryi, di = info->di; + + if(count == MAGIC_INIT_EFFECT_INFO) { + /* decompositor */ + svfl->freq = 500; + svfl->res_dB = 0; + calc_filter_moog(svfl); + init_filter_moog(svfl); + svfr->freq = 500; + svfr->res_dB = 0; + calc_filter_moog(svfr); + init_filter_moog(svfr); + /* anti-aliasing */ + lpf1->freq = info->cutoff; + lpf1->q = 1.0; + calc_filter_biquad_low(lpf1); + info->weti = TIM_FSCALE(info->wet * info->level, 24); + info->dryi = TIM_FSCALE(info->dry * info->level, 24); + info->di = TIM_FSCALE(calc_gs_drive(info->drive), 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + for(i = 0; i < count; i++) { + /* left */ + inputl = buf[i]; + /* decomposition */ + do_filter_moog(&inputl, &high, svfl->f, svfl->p, svfl->q, + &svfl->b0, &svfl->b1, &svfl->b2, &svfl->b3, &svfl->b4); + /* waveshaping */ + (this->*do_od)(&high, di); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1l, &lpf1->x2l, &lpf1->y1l, &lpf1->y2l); + buf[i] = imuldiv24(high + inputl, weti) + imuldiv24(buf[i], dryi); + + /* right */ + inputr = buf[++i]; + /* decomposition */ + do_filter_moog(&inputr, &high, svfr->f, svfr->p, svfr->q, + &svfr->b0, &svfr->b1, &svfr->b2, &svfr->b3, &svfr->b4); + /* waveshaping */ + (this->*do_od)(&high, di); + /* anti-aliasing */ + do_filter_biquad(&high, lpf1->a1, lpf1->a2, lpf1->b1, lpf1->b02, &lpf1->x1r, &lpf1->x2r, &lpf1->y1r, &lpf1->y2r); + buf[i] = imuldiv24(high + inputr, weti) + imuldiv24(buf[i], dryi); + } +} + +void Reverb::do_delay_lcr(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x; + InfoDelayLCR *info = (InfoDelayLCR *)ef->info; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + filter_lowpass1 *lpf = &(info->lpf); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t buf_index = delayL->index, buf_size = delayL->size; + int32_t index0 = info->index[0], index1 = info->index[1], index2 = info->index[2], + x1l = lpf->x1l, x1r = lpf->x1r; + int32_t cleveli = info->cleveli, feedbacki = info->feedbacki, + dryi = info->dryi, weti = info->weti, ai = lpf->ai, iai = lpf->iai; + + if(count == MAGIC_INIT_EFFECT_INFO) { + info->size[0] = info->ldelay * playback_rate / 1000.0; + info->size[1] = info->cdelay * playback_rate / 1000.0; + info->size[2] = info->rdelay * playback_rate / 1000.0; + x = info->fdelay * playback_rate / 1000.0; + for (i = 0; i < 3; i++) { + if (info->size[i] > x) {info->size[i] = x;} + } + x += 1; /* allowance */ + set_delay(&(info->delayL), x); + set_delay(&(info->delayR), x); + for (i = 0; i < 3; i++) { /* set start-point */ + info->index[i] = x - info->size[i]; + } + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->cleveli = TIM_FSCALE(info->clevel, 24); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + lpf->a = (1.0 - info->high_damp) * 44100.0 / playback_rate; + init_filter_lowpass1(lpf); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + for (i = 0; i < count; i++) + { + x = imuldiv24(bufL[buf_index], feedbacki); + do_filter_lowpass1(&x, &x1l, ai, iai); + bufL[buf_index] = buf[i] + x; + x = bufL[index0] + imuldiv24(bufL[index1], cleveli); + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(x, weti); + + x = imuldiv24(bufR[buf_index], feedbacki); + do_filter_lowpass1(&x, &x1r, ai, iai); + bufR[buf_index] = buf[++i] + x; + x = bufR[index2] + imuldiv24(bufR[index1], cleveli); + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(x, weti); + + if (++index0 == buf_size) {index0 = 0;} + if (++index1 == buf_size) {index1 = 0;} + if (++index2 == buf_size) {index2 = 0;} + if (++buf_index == buf_size) {buf_index = 0;} + } + info->index[0] = index0, info->index[1] = index1, info->index[2] = index2; + lpf->x1l = x1l, lpf->x1r = x1r; + delayL->index = delayR->index = buf_index; +} + +void Reverb::conv_xg_delay_eq2(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ2 *info = (InfoEQ2 *)ef->info; + + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[12], 4, 40)]; + info->low_gain = clip_int(st->param_lsb[13] - 64, -12, 12); + info->high_freq = eq_freq_table_xg[clip_int(st->param_lsb[14], 28, 58)]; + info->high_gain = clip_int(st->param_lsb[15] - 64, -12, 12); +} + +void Reverb::conv_xg_delay_lcr(struct effect_xg_t *st, EffectList *ef) +{ + InfoDelayLCR *info = (InfoDelayLCR *)ef->info; + + info->ldelay = (double)clip_int(st->param_msb[0] * 128 + st->param_lsb[0], 1, 14860) / 10.0; + info->rdelay = (double)clip_int(st->param_msb[1] * 128 + st->param_lsb[1], 1, 14860) / 10.0; + info->cdelay = (double)clip_int(st->param_msb[2] * 128 + st->param_lsb[2], 1, 14860) / 10.0; + info->fdelay = (double)clip_int(st->param_msb[3] * 128 + st->param_lsb[3], 1, 14860) / 10.0; + info->feedback = (double)(st->param_lsb[4] - 64) * (0.763 * 2.0 / 100.0); + info->clevel = (double)st->param_lsb[5] / 127.0; + info->high_damp = (double)clip_int(st->param_lsb[6], 1, 10) / 10.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::conv_xg_delay_lr(struct effect_xg_t *st, EffectList *ef) +{ + InfoDelayLR *info = (InfoDelayLR *)ef->info; + + info->ldelay = (double)clip_int(st->param_msb[0] * 128 + st->param_lsb[0], 1, 14860) / 10.0; + info->rdelay = (double)clip_int(st->param_msb[1] * 128 + st->param_lsb[1], 1, 14860) / 10.0; + info->fdelay1 = (double)clip_int(st->param_msb[2] * 128 + st->param_lsb[2], 1, 14860) / 10.0; + info->fdelay2 = (double)clip_int(st->param_msb[3] * 128 + st->param_lsb[3], 1, 14860) / 10.0; + info->feedback = (double)(st->param_lsb[4] - 64) * (0.763 * 2.0 / 100.0); + info->high_damp = (double)clip_int(st->param_lsb[5], 1, 10) / 10.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::do_delay_lr(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x; + InfoDelayLR *info = (InfoDelayLR *)ef->info; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + filter_lowpass1 *lpf = &(info->lpf); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t indexl = delayL->index, sizel = delayL->size, + indexr = delayR->index, sizer = delayR->size; + int32_t index0 = info->index[0], index1 = info->index[1], + x1l = lpf->x1l, x1r = lpf->x1r; + int32_t feedbacki = info->feedbacki, + dryi = info->dryi, weti = info->weti, ai = lpf->ai, iai = lpf->iai; + + if(count == MAGIC_INIT_EFFECT_INFO) { + info->size[0] = info->ldelay * playback_rate / 1000.0; + x = info->fdelay1 * playback_rate / 1000.0; + if (info->size[0] > x) {info->size[0] = x;} + x++; + set_delay(&(info->delayL), x); + info->index[0] = x - info->size[0]; + info->size[1] = info->rdelay * playback_rate / 1000.0; + x = info->fdelay2 * playback_rate / 1000.0; + if (info->size[1] > x) {info->size[1] = x;} + x++; + set_delay(&(info->delayR), x); + info->index[1] = x - info->size[1]; + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + lpf->a = (1.0 - info->high_damp) * 44100.0 / playback_rate; + init_filter_lowpass1(lpf); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + for (i = 0; i < count; i++) + { + x = imuldiv24(bufL[indexl], feedbacki); + do_filter_lowpass1(&x, &x1l, ai, iai); + bufL[indexl] = buf[i] + x; + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(bufL[index0], weti); + + x = imuldiv24(bufR[indexr], feedbacki); + do_filter_lowpass1(&x, &x1r, ai, iai); + bufR[indexr] = buf[++i] + x; + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(bufR[index1], weti); + + if (++index0 == sizel) {index0 = 0;} + if (++index1 == sizer) {index1 = 0;} + if (++indexl == sizel) {indexl = 0;} + if (++indexr == sizer) {indexr = 0;} + } + info->index[0] = index0, info->index[1] = index1; + lpf->x1l = x1l, lpf->x1r = x1r; + delayL->index = indexl, delayR->index = indexr; +} + +void Reverb::conv_xg_echo(struct effect_xg_t *st, EffectList *ef) +{ + InfoEcho *info = (InfoEcho *)ef->info; + + info->ldelay1 = (double)clip_int(st->param_msb[0] * 128 + st->param_lsb[0], 1, 7430) / 10.0; + info->lfeedback = (double)(st->param_lsb[1] - 64) * (0.763 * 2.0 / 100.0); + info->rdelay1 = (double)clip_int(st->param_msb[2] * 128 + st->param_lsb[2], 1, 7430) / 10.0; + info->rfeedback = (double)(st->param_lsb[3] - 64) * (0.763 * 2.0 / 100.0); + info->high_damp = (double)clip_int(st->param_lsb[4], 1, 10) / 10.0; + info->ldelay2 = (double)clip_int(st->param_msb[5] * 128 + st->param_lsb[5], 1, 7430) / 10.0; + info->rdelay2 = (double)clip_int(st->param_msb[6] * 128 + st->param_lsb[6], 1, 7430) / 10.0; + info->level = (double)st->param_lsb[7] / 127.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::do_echo(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x, y; + InfoEcho *info = (InfoEcho *)ef->info; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + filter_lowpass1 *lpf = &(info->lpf); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t indexl = delayL->index, sizel = delayL->size, + indexr = delayR->index, sizer = delayR->size; + int32_t index0 = info->index[0], index1 = info->index[1], + x1l = lpf->x1l, x1r = lpf->x1r; + int32_t lfeedbacki = info->lfeedbacki, rfeedbacki = info->rfeedbacki, leveli = info->leveli, + dryi = info->dryi, weti = info->weti, ai = lpf->ai, iai = lpf->iai; + + if(count == MAGIC_INIT_EFFECT_INFO) { + info->size[0] = info->ldelay2 * playback_rate / 1000.0; + x = info->ldelay1 * playback_rate / 1000.0; + if (info->size[0] > x) {info->size[0] = x;} + x++; + set_delay(&(info->delayL), x); + info->index[0] = x - info->size[0]; + info->size[1] = info->rdelay2 * playback_rate / 1000.0; + x = info->rdelay1 * playback_rate / 1000.0; + if (info->size[1] > x) {info->size[1] = x;} + x++; + set_delay(&(info->delayR), x); + info->index[1] = x - info->size[1]; + info->lfeedbacki = TIM_FSCALE(info->lfeedback, 24); + info->rfeedbacki = TIM_FSCALE(info->rfeedback, 24); + info->leveli = TIM_FSCALE(info->level, 24); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + lpf->a = (1.0 - info->high_damp) * 44100.0 / playback_rate; + init_filter_lowpass1(lpf); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + for (i = 0; i < count; i++) + { + y = bufL[indexl] + imuldiv24(bufL[index0], leveli); + x = imuldiv24(bufL[indexl], lfeedbacki); + do_filter_lowpass1(&x, &x1l, ai, iai); + bufL[indexl] = buf[i] + x; + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(y, weti); + + y = bufR[indexr] + imuldiv24(bufR[index1], leveli); + x = imuldiv24(bufR[indexr], rfeedbacki); + do_filter_lowpass1(&x, &x1r, ai, iai); + bufR[indexr] = buf[++i] + x; + buf[i] = imuldiv24(buf[i], dryi) + imuldiv24(y, weti); + + if (++index0 == sizel) {index0 = 0;} + if (++index1 == sizer) {index1 = 0;} + if (++indexl == sizel) {indexl = 0;} + if (++indexr == sizer) {indexr = 0;} + } + info->index[0] = index0, info->index[1] = index1; + lpf->x1l = x1l, lpf->x1r = x1r; + delayL->index = indexl, delayR->index = indexr; +} + +void Reverb::conv_xg_cross_delay(struct effect_xg_t *st, EffectList *ef) +{ + InfoCrossDelay *info = (InfoCrossDelay *)ef->info; + + info->lrdelay = (double)clip_int(st->param_msb[0] * 128 + st->param_lsb[0], 1, 7430) / 10.0; + info->rldelay = (double)clip_int(st->param_msb[1] * 128 + st->param_lsb[1], 1, 7430) / 10.0; + info->feedback = (double)(st->param_lsb[2] - 64) * (0.763 * 2.0 / 100.0); + info->input_select = st->param_lsb[3]; + info->high_damp = (double)clip_int(st->param_lsb[4], 1, 10) / 10.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::do_cross_delay(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, lfb, rfb, lout, rout; + InfoCrossDelay *info = (InfoCrossDelay *)ef->info; + simple_delay *delayL = &(info->delayL), *delayR = &(info->delayR); + filter_lowpass1 *lpf = &(info->lpf); + int32_t *bufL = delayL->buf, *bufR = delayR->buf; + int32_t indexl = delayL->index, sizel = delayL->size, + indexr = delayR->index, sizer = delayR->size, + x1l = lpf->x1l, x1r = lpf->x1r; + int32_t feedbacki = info->feedbacki, + dryi = info->dryi, weti = info->weti, ai = lpf->ai, iai = lpf->iai; + + if(count == MAGIC_INIT_EFFECT_INFO) { + set_delay(&(info->delayL), (int32_t)(info->lrdelay * playback_rate / 1000.0)); + set_delay(&(info->delayR), (int32_t)(info->rldelay * playback_rate / 1000.0)); + info->feedbacki = TIM_FSCALE(info->feedback, 24); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + lpf->a = (1.0 - info->high_damp) * 44100.0 / playback_rate; + init_filter_lowpass1(lpf); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + free_delay(&(info->delayL)); + free_delay(&(info->delayR)); + return; + } + + for (i = 0; i < count; i++) + { + lfb = imuldiv24(bufL[indexl], feedbacki); + do_filter_lowpass1(&lfb, &x1l, ai, iai); + lout = imuldiv24(buf[i], dryi) + imuldiv24(bufL[indexl], weti); + rfb = imuldiv24(bufR[indexr], feedbacki); + do_filter_lowpass1(&rfb, &x1r, ai, iai); + rout = imuldiv24(buf[i + 1], dryi) + imuldiv24(bufR[indexr], weti); + bufL[indexl] = buf[i] + rfb; + buf[i] = lout; + bufR[indexr] = buf[++i] + lfb; + buf[i] = rout; + + if (++indexl == sizel) {indexl = 0;} + if (++indexr == sizer) {indexr = 0;} + } + lpf->x1l = x1l, lpf->x1r = x1r; + delayL->index = indexl, delayR->index = indexr; +} + +void Reverb::conv_gs_lofi1(struct insertion_effect_gs_t *st, EffectList *ef) +{ + InfoLoFi1 *info = (InfoLoFi1 *)ef->info; + + info->pre_filter = st->parameter[0]; + info->lofi_type = 1 + clip_int(st->parameter[1], 0, 8); + info->post_filter = st->parameter[2]; + info->dry = calc_dry_gs(st->parameter[15] & 0x7F); + info->wet = calc_wet_gs(st->parameter[15] & 0x7F); + info->pan = st->parameter[18]; + info->level = (st->parameter[19] & 0x7F) / 127.0; +} + +int32_t Reverb::apply_lofi(int32_t input, int32_t bit_mask, int32_t level_shift) +{ + return (input + level_shift) & bit_mask; +} + +void Reverb::do_lofi1(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x, y; + InfoLoFi1 *info = (InfoLoFi1 *)ef->info; + int32_t bit_mask = info->bit_mask, dryi = info->dryi, weti = info->weti; + const int32_t level_shift = info->level_shift; + + if(count == MAGIC_INIT_EFFECT_INFO) { + info->bit_mask = ~0L << (info->lofi_type * 2); + info->level_shift = ~info->bit_mask >> 1; + info->dryi = TIM_FSCALE(info->dry * info->level, 24); + info->weti = TIM_FSCALE(info->wet * info->level, 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + + for (i = 0; i < count; i++) + { + x = buf[i]; + y = apply_lofi(x, bit_mask, level_shift); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + + x = buf[++i]; + y = apply_lofi(x, bit_mask, level_shift); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + } +} + +void Reverb::conv_gs_lofi2(struct insertion_effect_gs_t *st, EffectList *ef) +{ + InfoLoFi2 *info = (InfoLoFi2 *)ef->info; + + info->lofi_type = 1 + clip_int(st->parameter[0], 0, 5); + info->fil_type = clip_int(st->parameter[1], 0, 2); + info->fil.freq = cutoff_freq_table_gs[st->parameter[2]]; + info->rdetune = st->parameter[3]; + info->rnz_lev = (double)st->parameter[4] / 127.0; + info->wp_sel = clip_int(st->parameter[5], 0, 1); + info->wp_lpf.freq = lpf_table_gs[st->parameter[6]]; + info->wp_level = (double)st->parameter[7] / 127.0; + info->disc_type = clip_int(st->parameter[8], 0, 3); + info->disc_lpf.freq = lpf_table_gs[st->parameter[9]]; + info->discnz_lev = (double)st->parameter[10] / 127.0; + info->hum_type = clip_int(st->parameter[11], 0, 1); + info->hum_lpf.freq = lpf_table_gs[st->parameter[12]]; + info->hum_level = (double)st->parameter[13] / 127.0; + info->ms = clip_int(st->parameter[14], 0, 1); + info->dry = calc_dry_gs(st->parameter[15] & 0x7F); + info->wet = calc_wet_gs(st->parameter[15] & 0x7F); + info->pan = st->parameter[18]; + info->level = (st->parameter[19] & 0x7F) / 127.0; +} + +void Reverb::do_lofi2(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x, y; + InfoLoFi2 *info = (InfoLoFi2 *)ef->info; + filter_biquad *fil = &(info->fil); + int32_t bit_mask = info->bit_mask, dryi = info->dryi, weti = info->weti; + const int32_t level_shift = info->level_shift; + + if(count == MAGIC_INIT_EFFECT_INFO) { + fil->q = 1.0; + if (info->fil_type == 1) {calc_filter_biquad_low(fil);} + else if (info->fil_type == 2) {calc_filter_biquad_high(fil);} + else { + fil->freq = -1; /* bypass */ + calc_filter_biquad_low(fil); + } + info->bit_mask = ~0L << (info->lofi_type * 2); + info->level_shift = ~info->bit_mask >> 1; + info->dryi = TIM_FSCALE(info->dry * info->level, 24); + info->weti = TIM_FSCALE(info->wet * info->level, 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + for (i = 0; i < count; i++) + { + x = buf[i]; + y = apply_lofi(x, bit_mask, level_shift); + do_filter_biquad(&y, fil->a1, fil->a2, fil->b1, fil->b02, &fil->x1l, &fil->x2l, &fil->y1l, &fil->y2l); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + + x = buf[++i]; + y = apply_lofi(x, bit_mask, level_shift); + do_filter_biquad(&y, fil->a1, fil->a2, fil->b1, fil->b02, &fil->x1r, &fil->x2r, &fil->y1r, &fil->y2r); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + } +} + +void Reverb::conv_xg_lofi(struct effect_xg_t *st, EffectList *ef) +{ + InfoLoFi *info = (InfoLoFi *)ef->info; + + info->srf.freq = lofi_sampling_freq_table_xg[st->param_lsb[0]] / 2.0; + info->word_length = st->param_lsb[1]; + info->output_gain = clip_int(st->param_lsb[2], 0, 18); + info->lpf.freq = eq_freq_table_xg[clip_int(st->param_lsb[3], 10, 80)]; + info->filter_type = st->param_lsb[4]; + info->lpf.q = (double)clip_int(st->param_lsb[5], 10, 120) / 10.0; + info->bit_assign = clip_int(st->param_lsb[6], 0, 6); + info->emphasis = st->param_lsb[7]; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); +} + +void Reverb::do_lofi(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x, y; + InfoLoFi *info = (InfoLoFi *)ef->info; + filter_biquad *lpf = &(info->lpf), *srf = &(info->srf); + int32_t bit_mask = info->bit_mask, dryi = info->dryi, weti = info->weti; + const int32_t level_shift = info->level_shift; + + if(count == MAGIC_INIT_EFFECT_INFO) { + srf->q = 1.0; + calc_filter_biquad_low(srf); + calc_filter_biquad_low(lpf); + info->bit_mask = ~((1L << (info->bit_assign + 22 - GUARD_BITS)) - 1L); + info->level_shift = ~info->bit_mask >> 1; + info->dryi = TIM_FSCALE(info->dry * pow(10.0, (double)info->output_gain / 20.0), 24); + info->weti = TIM_FSCALE(info->wet * pow(10.0, (double)info->output_gain / 20.0), 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + + for (i = 0; i < count; i++) + { + x = buf[i]; + y = apply_lofi(x, bit_mask, level_shift); + do_filter_biquad(&y, srf->a1, srf->a2, srf->b1, srf->b02, &srf->x1l, &srf->x2l, &srf->y1l, &srf->y2l); + do_filter_biquad(&y, lpf->a1, lpf->a2, lpf->b1, lpf->b02, &lpf->x1l, &lpf->x2l, &lpf->y1l, &lpf->y2l); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + + x = buf[++i]; + y = apply_lofi(x, bit_mask, level_shift); + do_filter_biquad(&y, srf->a1, srf->a2, srf->b1, srf->b02, &srf->x1r, &srf->x2r, &srf->y1r, &srf->y2r); + do_filter_biquad(&y, lpf->a1, lpf->a2, lpf->b1, lpf->b02, &lpf->x1r, &lpf->x2r, &lpf->y1r, &lpf->y2r); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + } +} + +void Reverb::conv_xg_auto_wah_od(struct effect_xg_t *st, EffectList *ef) +{ + InfoXGAutoWahOD *info = (InfoXGAutoWahOD *)ef->info; + + info->lpf.freq = eq_freq_table_xg[clip_int(st->param_lsb[13], 34, 80)]; + info->level = (double)st->param_lsb[14] / 127.0; +} + +void Reverb::conv_xg_auto_wah_od_eq3(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ3 *info = (InfoEQ3 *)ef->info; + + info->low_freq = eq_freq_table_xg[24]; + info->low_gain = clip_int(st->param_lsb[11] - 64, -12, 12); + info->mid_freq = eq_freq_table_xg[41]; + info->mid_gain = clip_int(st->param_lsb[12] - 64, -12, 12); + info->mid_width = 1.0; + info->high_freq = 0; + info->high_gain = 0; +} + +void Reverb::conv_xg_auto_wah_eq2(struct effect_xg_t *st, EffectList *ef) +{ + InfoEQ2 *info = (InfoEQ2 *)ef->info; + + info->low_freq = eq_freq_table_xg[clip_int(st->param_lsb[5], 4, 40)]; + info->low_gain = clip_int(st->param_lsb[6] - 64, -12, 12); + info->high_freq = eq_freq_table_xg[clip_int(st->param_lsb[7], 28, 58)]; + info->high_gain = clip_int(st->param_lsb[8] - 64, -12, 12); +} + +void Reverb::conv_xg_auto_wah(struct effect_xg_t *st, EffectList *ef) +{ + InfoXGAutoWah *info = (InfoXGAutoWah *)ef->info; + + info->lfo_freq = lfo_freq_table_xg[st->param_lsb[0]]; + info->lfo_depth = st->param_lsb[1]; + info->offset_freq = (double)(st->param_lsb[2]) * 3900.0 / 127.0 + 100.0; + info->resonance = (double)clip_int(st->param_lsb[3], 10, 120) / 10.0; + info->dry = calc_dry_xg(st->param_lsb[9], st); + info->wet = calc_wet_xg(st->param_lsb[9], st); + info->drive = st->param_lsb[10]; +} + +double Reverb::calc_xg_auto_wah_freq(int32_t lfo_val, double offset_freq, int8_t depth) +{ + double freq; + int32_t fine; + fine = ((lfo_val - (1L << 15)) * depth) >> 7; /* max: +-2^8 fine */ + if (fine >= 0) { + freq = offset_freq * bend_fine[fine & 0xff] + * bend_coarse[fine >> 8 & 0x7f]; + } else { + freq = offset_freq / (bend_fine[(-fine) & 0xff] + * bend_coarse[(-fine) >> 8 & 0x7f]); + } + return freq; +} + +#define XG_AUTO_WAH_BITS (32 - GUARD_BITS) +#define XG_AUTO_WAH_MAX_NEG (1.0 / (double)(1L << XG_AUTO_WAH_BITS)) + +void Reverb::do_xg_auto_wah(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x, y, val; + InfoXGAutoWah *info = (InfoXGAutoWah *)ef->info; + filter_moog_dist *fil0 = &(info->fil0), *fil1 = &(info->fil1); + lfo *lfo = &(info->lfo); + int32_t dryi = info->dryi, weti = info->weti, fil_cycle = info->fil_cycle; + int8_t lfo_depth = info->lfo_depth; + double yf, offset_freq = info->offset_freq; + int32_t fil_count = info->fil_count; + + if(count == MAGIC_INIT_EFFECT_INFO) { + init_lfo(lfo, info->lfo_freq, LFO_TRIANGULAR, 0); + fil0->res_dB = fil1->res_dB = (info->resonance - 1.0) * 12.0 / 11.0; + fil0->dist = fil1->dist = 4.0 * sqrt((double)info->drive / 127.0); + val = do_lfo(lfo); + fil0->freq = fil1->freq = calc_xg_auto_wah_freq(val, info->offset_freq, info->lfo_depth); + calc_filter_moog_dist(fil0); + init_filter_moog_dist(fil0); + calc_filter_moog_dist(fil1); + init_filter_moog_dist(fil1); + info->fil_count = 0; + info->fil_cycle = (int32_t)(44.0 * playback_rate / 44100.0); + info->dryi = TIM_FSCALE(info->dry, 24); + info->weti = TIM_FSCALE(info->wet, 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + + for (i = 0; i < count; i++) + { + x = y = buf[i]; + yf = (double)y * XG_AUTO_WAH_MAX_NEG; + do_filter_moog_dist_band(&yf, fil0->f, fil0->p, fil0->q, fil0->d, + &fil0->b0, &fil0->b1, &fil0->b2, &fil0->b3, &fil0->b4); + y = TIM_FSCALE(yf, XG_AUTO_WAH_BITS); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + + x = y = buf[++i]; + yf = (double)y * XG_AUTO_WAH_MAX_NEG; + do_filter_moog_dist_band(&yf, fil0->f, fil0->p, fil0->q, fil0->d, + &fil1->b0, &fil1->b1, &fil1->b2, &fil1->b3, &fil1->b4); + y = TIM_FSCALE(yf, XG_AUTO_WAH_BITS); + buf[i] = imuldiv24(x, dryi) + imuldiv24(y, weti); + + val = do_lfo(lfo); + + if (++fil_count == fil_cycle) { + fil_count = 0; + fil0->freq = calc_xg_auto_wah_freq(val, offset_freq, lfo_depth); + calc_filter_moog_dist(fil0); + } + } + info->fil_count = fil_count; +} + +void Reverb::do_xg_auto_wah_od(int32_t *buf, int32_t count, EffectList *ef) +{ + int32_t i, x; + InfoXGAutoWahOD *info = (InfoXGAutoWahOD *)ef->info; + filter_biquad *lpf = &(info->lpf); + int32_t leveli = info->leveli; + + if(count == MAGIC_INIT_EFFECT_INFO) { + lpf->q = 1.0; + calc_filter_biquad_low(lpf); + info->leveli = TIM_FSCALE(info->level, 24); + return; + } else if(count == MAGIC_FREE_EFFECT_INFO) { + return; + } + + for (i = 0; i < count; i++) + { + x = buf[i]; + do_filter_biquad(&x, lpf->a1, lpf->a2, lpf->b1, lpf->b02, &lpf->x1l, &lpf->x2l, &lpf->y1l, &lpf->y2l); + buf[i] = imuldiv24(x, leveli); + + x = buf[++i]; + do_filter_biquad(&x, lpf->a1, lpf->a2, lpf->b1, lpf->b02, &lpf->x1r, &lpf->x2r, &lpf->y1r, &lpf->y2r); + buf[i] = imuldiv24(x, leveli); + } +} + +enum { /* width * length * height */ + ER_SMALL_ROOM, /* 5m * 5m * 3m */ + ER_MEDIUM_ROOM, /* 10m * 10m * 5m */ + ER_LARGE_ROOM, /* 12m * 12m * 8m */ + ER_MEDIUM_HALL, /* 15m * 20m * 10m */ + ER_LARGE_HALL, /* 20m * 40m * 18m */ + ER_EOF, +}; + + +const struct _EffectEngine Reverb::effect_engine[] = { +{ EFFECT_NONE, "None", NULL, NULL, NULL, 0,}, +{ EFFECT_STEREO_EQ, "Stereo-EQ", &Reverb::do_stereo_eq, &Reverb::conv_gs_stereo_eq, NULL, sizeof(InfoStereoEQ),}, +{ EFFECT_EQ2, "2-Band EQ", &Reverb::do_eq2, &Reverb::conv_gs_eq2, &Reverb::conv_xg_eq2, sizeof(InfoEQ2),}, +{ EFFECT_EQ3, "3-Band EQ", &Reverb::do_eq3, NULL, &Reverb::conv_xg_eq3, sizeof(InfoEQ3),}, +{ EFFECT_OVERDRIVE1, "Overdrive", &Reverb::do_overdrive1, &Reverb::conv_gs_overdrive1, NULL, sizeof(InfoOverdrive1),}, +{ EFFECT_DISTORTION1, "Distortion", &Reverb::do_distortion1, &Reverb::conv_gs_overdrive1, NULL, sizeof(InfoOverdrive1),}, +{ EFFECT_OD1OD2, "OD1/OD2", &Reverb::do_dual_od, &Reverb::conv_gs_dual_od, NULL, sizeof(InfoOD1OD2),}, +{ EFFECT_HEXA_CHORUS, "Hexa-Chorus", &Reverb::do_hexa_chorus, &Reverb::conv_gs_hexa_chorus, NULL, sizeof(InfoHexaChorus),}, +{ EFFECT_CHORUS, "Chorus", &Reverb::do_chorus, NULL, &Reverb::conv_xg_chorus, sizeof(InfoChorus),}, +{ EFFECT_FLANGER, "Flanger", &Reverb::do_chorus, NULL, &Reverb::conv_xg_flanger, sizeof(InfoChorus),}, +{ EFFECT_SYMPHONIC, "Symphonic", &Reverb::do_chorus, NULL, &Reverb::conv_xg_symphonic, sizeof(InfoChorus),}, +{ EFFECT_CHORUS_EQ3, "3-Band EQ (XG Chorus built-in)", &Reverb::do_eq3, NULL, &Reverb::conv_xg_chorus_eq3, sizeof(InfoEQ3),}, +{ EFFECT_STEREO_OVERDRIVE, "Stereo Overdrive", &Reverb::do_stereo_od, NULL, &Reverb::conv_xg_overdrive, sizeof(InfoStereoOD),}, +{ EFFECT_STEREO_DISTORTION, "Stereo Distortion", &Reverb::do_stereo_od, NULL, &Reverb::conv_xg_distortion, sizeof(InfoStereoOD),}, +{ EFFECT_STEREO_AMP_SIMULATOR, "Amp Simulator", &Reverb::do_stereo_od, NULL, &Reverb::conv_xg_amp_simulator, sizeof(InfoStereoOD),}, +{ EFFECT_OD_EQ3, "2-Band EQ (XG OD built-in)", &Reverb::do_eq3, NULL, &Reverb::conv_xg_od_eq3, sizeof(InfoEQ3),}, +{ EFFECT_DELAY_LCR, "Delay L,C,R", &Reverb::do_delay_lcr, NULL, &Reverb::conv_xg_delay_lcr, sizeof(InfoDelayLCR),}, +{ EFFECT_DELAY_LR, "Delay L,R", &Reverb::do_delay_lr, NULL, &Reverb::conv_xg_delay_lr, sizeof(InfoDelayLR),}, +{ EFFECT_ECHO, "Echo", &Reverb::do_echo, NULL, &Reverb::conv_xg_echo, sizeof(InfoEcho),}, +{ EFFECT_CROSS_DELAY, "Cross Delay", &Reverb::do_cross_delay, NULL, &Reverb::conv_xg_cross_delay, sizeof(InfoCrossDelay),}, +{ EFFECT_DELAY_EQ2, "2-Band EQ (XG Delay built-in)", &Reverb::do_eq2, NULL, &Reverb::conv_xg_delay_eq2, sizeof(InfoEQ2),}, +{ EFFECT_LOFI, "Lo-Fi", &Reverb::do_lofi, NULL, &Reverb::conv_xg_lofi, sizeof(InfoLoFi),}, +{ EFFECT_LOFI1, "Lo-Fi 1", &Reverb::do_lofi1, &Reverb::conv_gs_lofi1, NULL, sizeof(InfoLoFi1),}, +{ EFFECT_LOFI2, "Lo-Fi 2", &Reverb::do_lofi2, &Reverb::conv_gs_lofi2, NULL, sizeof(InfoLoFi2),}, +{ EFFECT_XG_AUTO_WAH, "Auto Wah", &Reverb::do_xg_auto_wah, NULL, &Reverb::conv_xg_auto_wah, sizeof(InfoXGAutoWah),}, +{ EFFECT_XG_AUTO_WAH_EQ2, "2-Band EQ (Auto Wah built-in)", &Reverb::do_eq2, NULL, &Reverb::conv_xg_auto_wah_eq2, sizeof(InfoEQ2),}, +{ EFFECT_XG_AUTO_WAH_OD, "OD (Auto Wah built-in)", &Reverb::do_xg_auto_wah_od, NULL, &Reverb::conv_xg_auto_wah_od, sizeof(InfoXGAutoWahOD),}, +{ EFFECT_XG_AUTO_WAH_OD_EQ3, "2-Band EQ (Auto Wah OD built-in)", &Reverb::do_eq3, NULL, &Reverb::conv_xg_auto_wah_od_eq3, sizeof(InfoEQ3),}, +{ -1, "EOF", NULL, NULL, NULL, 0, }, +}; + +const struct effect_parameter_xg_t Reverb::effect_parameter_xg[] = { +{ 0, 0, "NO EFFECT", + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, 0, }, + +{ 0x05, 0, "DELAY L,C,R", { 0x1A, 0x0D, 0x27, 0x27, 0, 0, 0, 0, 0, 0,},{ + 0x05, 0x03, 0x08, 0x08, 74, 100, 10, 0, 0, 32, 0, 0, 28, 64, 46, 64,}, 9,}, + +{ 0x06, 0, "DELAY L,R", { 0x13, 0x1D, 0x1D, 0x1D, 0, 0, 0, 0, 0, 0,},{ + 0x44, 0x26, 0x28, 0x26, 87, 10, 0, 0, 0, 32, 0, 0, 28, 64, 46, 64, },9,}, + +{ 0x07, 0, "ECHO", { 0x0D, 0, 0x0D, 0, 0, 0x0D, 0x0D, 0, 0, 0,},{ + 0x24, 80, 0x74, 80, 10, 0x24, 0x74, 0, 0, 40, 0, 0, 28, 64, 46, 64,}, 9,}, + +{ 0x08, 0, "CROSS DELAY", { 0x0D, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 0x24, 0x56, 111, 1, 10, 0, 0, 0, 0, 32, 0, 0, 28, 64, 46, 64,}, 9,}, + +{ 0x41, 0, "CHORUS 1", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 6, 54, 77, 106, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x01, "CHORUS 2",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 8, 63, 64, 30, 0, 28, 62, 42, 58, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x02, "CHORUS 3",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 4, 44, 64, 110, 0, 28, 64, 46, 66, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x03, "GM CHORUS 1",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 9, 10, 64, 109, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x04, "GM CHORUS 2", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 28, 34, 67, 105, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x05, "GM CHORUS 3", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 9, 34, 69, 105, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x06, "GM CHORUS 4", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 26, 29, 75, 102, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x07, "FB CHORUS", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 6, 43, 107, 111, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x41, 0x08, "CHORUS 4", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 9, 32, 69, 104, 0, 28, 64, 46, 64, 64, 46, 64, 10, 0, 1, 0,}, 9,}, + +{ 0x42, 0, "CELESTE 1", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 12, 32, 64, 0, 0, 28, 64, 46, 64, 127, 40, 68, 10, 0, 0, 0,}, 9,}, + +{ 0x42, 0x01, "CELESTE 2", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 28, 18, 90, 2, 0, 28, 62, 42, 60, 84, 40, 68, 10, 0, 0, 0,}, 9,}, + +{ 0x42, 0x02, "CELESTE 3", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 4, 63, 44, 2, 0, 28, 64, 46, 68, 127, 40, 68, 10, 0, 0,0,}, 9,}, + +{ 0x42, 0x08, "CELESTE 4", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 8, 29, 64, 0, 0, 28, 64, 51, 66, 127, 40, 68, 10, 0, 1, 0,}, 9,}, + +{ 0x43, 0, "FLANGER 1", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 14, 14, 104, 2, 0, 28, 64, 46, 64, 96, 40, 64, 10, 4, 0, 0,}, 9, }, + +{ 0x43, 0x01, "FLANGER 2", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 32, 17, 26, 2, 0, 28, 64, 46, 60, 96, 40, 64, 10, 4, 0, 0,}, 9, }, + +{ 0x43, 0x07, "GM FLANGER", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 3, 21, 120, 1, 0, 28, 64, 46, 64, 96, 40, 64, 10, 4, 0, 0,}, 9, }, + +{ 0x43, 0x08, "FLANGER 3", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 4, 109, 109, 2, 0, 28, 64, 46, 64, 127, 40, 64, 10, 4, 0, 0,}, 9, }, + +{ 0x44, 0, "SYMPHONIC", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 12, 25, 16, 0, 0, 28, 64, 46, 64, 127, 46, 64, 10, 0, 0, 0,}, 9,}, + +{ 0x49, 0, "DISTORTION", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 40, 20, 72, 53, 48, 0, 43, 74, 10, 127, 120, 0, 0, 0, 0,0,}, 0,}, + +{ 0x49, 0x08, "STEREO DISTORTION", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 18, 27, 71, 48, 84, 0, 32, 66, 10, 127, 105, 0, 0, 0, 0, 0,}, 0,}, + +{ 0x4A, 0, "OVERDRIVE", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 29, 24, 68, 45, 55, 0, 41, 72, 10, 127, 104, 0, 0, 0, 0, 0,}, 0,}, + +{ 0x4A, 0x08, "STEREO OVERDRIVE", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 10, 24, 69, 46, 105, 0, 41, 66, 10, 127, 104, 0, 0, 0, 0, 0,}, 0,}, + +{ 0x4B, 0, "AMP SIMULATOR", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 39, 1, 48, 55, 0, 0, 0, 0, 0, 127, 112, 0, 0, 0, 0, 0,}, 0,}, + +{ 0x4B, 0x08, "STEREO AMP SIMULATOR", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 16, 2, 46, 119, 0, 0, 0, 0, 0, 127, 106, 0, 0, 0, 0, 0,}, 0,}, + +{ 0x4C, 0, "3-BAND EQ", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 70, 34, 60, 10, 70, 28, 46, 0, 0, 127, 0, 0, 0, 0, 0, 0,}, -1,}, + +{ 0x4D, 0, "2-BAND EQ", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 28, 70, 46, 70, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0,}, -1,}, + +{ 0x4E, 0, "AUTO WAH", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 70, 56, 39, 25, 0, 28, 66, 46, 64, 127, 0, 0, 0, 0, 0, 0,}, 2, }, + +{ 0x4E, 0x01, "AUTO WAH+DISTORTION", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 40, 73, 26, 29, 0, 28, 66, 46, 64, 127, 30, 72, 74, 53, 48, 0,}, 2, }, + +{ 0x4E, 0x02, "AUTO WAH+OVERDRIVE", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 48, 64, 32, 23, 0, 28, 66, 46, 64, 127, 29, 68, 72, 45, 55, 0,}, 2, }, + +{ 0x5E, 0, "LO-FI",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 2, 60, 6, 54, 5, 10, 1, 1, 0, 127, 0, 0, 0, 0, 1, 0,}, 9, }, + +{ -1, -1, "EOF",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, 0,}, +}; + +const struct effect_parameter_gs_t Reverb::effect_parameter_gs[] = { +{ 0, 0, "None", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, 0, 0,}, +{ 0x01, 0x00, "Stereo-EQ", { 1, 0x45, 1, 0x34, 0x48, 0, 0x48, 0x38, 0, 0x48, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 127,}, 19, -1,}, +{ 0x01, 0x10, "Overdrive",{ 48, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0x40, 0x40, 0x40, 96,}, 0, 18,}, +{ 0x01, 0x11, "Distrotion",{ 76, 3, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0x40, 0x38, 0x40, 84,}, 0, 18, }, +{ 0x11, 0x03, "OD1/OD2",{ 0, 48, 1, 1, 0, 1, 76, 3, 1, 0, + 0, 0, 0, 0, 0, 0x40, 96, 0x40, 84, 127,}, 1, 6, }, +{ 0x01, 0x40, "Hexa Chorus",{ 0x18, 0x08, 127, 5, 66, 16, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0x40, 0x40, 64, 112,}, 1, 15, }, +{ 0x01, 0x72, "Lo-Fi 1",{ 2, 6, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 127, 0x40, 0x40, 64, 127,}, 15, 18, }, +{ 0x01, 0x73, "Lo-Fi 2",{ 2, 1, 0x20, 0, 64, 1, 127, 0, 0, 127, + 0, 0, 127, 0, 1, 127, 0x40, 0x40, 64, 127,}, 3, 15, }, +{ -1, -1, "EOF",{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, 0, 0,}, +}; + + +////////////////////////////////// from readmidi + +/*! initialize Delay Effect (GS) */ +void Reverb::init_delay_status_gs(void) +{ + struct delay_status_gs_t *p = &delay_status_gs; + p->type = 0; + p->level = 0x40; + p->level_center = 0x7F; + p->level_left = 0; + p->level_right = 0; + p->time_c = 0x61; + p->time_l = 0x01; + p->time_r = 0x01; + p->feedback = 0x50; + p->pre_lpf = 0; + recompute_delay_status_gs(); +} + +/*! recompute Delay Effect (GS) */ +void Reverb::recompute_delay_status_gs(void) +{ + struct delay_status_gs_t *p = &delay_status_gs; + p->time_center = delay_time_center_table[p->time_c > 0x73 ? 0x73 : p->time_c]; + p->time_ratio_left = (double)p->time_l / 24; + p->time_ratio_right = (double)p->time_r / 24; + p->sample[0] = p->time_center * playback_rate / 1000.0f; + p->sample[1] = p->sample[0] * p->time_ratio_left; + p->sample[2] = p->sample[0] * p->time_ratio_right; + p->level_ratio[0] = p->level * p->level_center / (127.0f * 127.0f); + p->level_ratio[1] = p->level * p->level_left / (127.0f * 127.0f); + p->level_ratio[2] = p->level * p->level_right / (127.0f * 127.0f); + p->feedback_ratio = (double)(p->feedback - 64) * (0.763f * 2.0f / 100.0f); + p->send_reverb_ratio = (double)p->send_reverb * (0.787f / 100.0f); + + if (p->level_left != 0 || (p->level_right != 0 && p->type == 0)) { + p->type = 1; /* it needs 3-tap delay effect. */ + } + + if (p->pre_lpf) { + p->lpf.a = 2.0 * ((double)(7 - p->pre_lpf) / 7.0f * 16000.0f + 200.0f) / playback_rate; + init_filter_lowpass1(&(p->lpf)); + } +} + +/*! Delay Macro (GS) */ +void Reverb::set_delay_macro_gs(int macro) +{ + struct delay_status_gs_t *p = &delay_status_gs; + if (macro >= 4) { p->type = 2; } /* cross delay */ + macro *= 10; + p->time_center = delay_time_center_table[delay_macro_presets[macro + 1]]; + p->time_ratio_left = (double)delay_macro_presets[macro + 2] / 24; + p->time_ratio_right = (double)delay_macro_presets[macro + 3] / 24; + p->level_center = delay_macro_presets[macro + 4]; + p->level_left = delay_macro_presets[macro + 5]; + p->level_right = delay_macro_presets[macro + 6]; + p->level = delay_macro_presets[macro + 7]; + p->feedback = delay_macro_presets[macro + 8]; +} + +/*! initialize Reverb Effect (GS) */ +void Reverb::init_reverb_status_gs(void) +{ + struct reverb_status_gs_t *p = &reverb_status_gs; + p->character = 0x04; + p->pre_lpf = 0; + p->level = 0x40; + p->time = 0x40; + p->delay_feedback = 0; + p->pre_delay_time = 0; + recompute_reverb_status_gs(); + init_reverb(); +} + +/*! recompute Reverb Effect (GS) */ +void Reverb::recompute_reverb_status_gs(void) +{ + struct reverb_status_gs_t *p = &reverb_status_gs; + + if (p->pre_lpf) { + p->lpf.a = 2.0 * ((double)(7 - p->pre_lpf) / 7.0f * 16000.0f + 200.0f) / playback_rate; + init_filter_lowpass1(&(p->lpf)); + } +} + +/*! Reverb Type (GM2) */ +void Reverb::set_reverb_macro_gm2(int macro) +{ + struct reverb_status_gs_t *p = &reverb_status_gs; + int type = macro; + if (macro == 8) { macro = 5; } + macro *= 6; + p->character = reverb_macro_presets[macro]; + p->pre_lpf = reverb_macro_presets[macro + 1]; + p->level = reverb_macro_presets[macro + 2]; + p->time = reverb_macro_presets[macro + 3]; + p->delay_feedback = reverb_macro_presets[macro + 4]; + p->pre_delay_time = reverb_macro_presets[macro + 5]; + + switch (type) { /* override GS macro's parameter */ + case 0: /* Small Room */ + p->time = 44; + break; + case 1: /* Medium Room */ + case 8: /* Plate */ + p->time = 50; + break; + case 2: /* Large Room */ + p->time = 56; + break; + case 3: /* Medium Hall */ + case 4: /* Large Hall */ + p->time = 64; + break; + } +} + +/*! Reverb Macro (GS) */ +void Reverb::set_reverb_macro_gs(int macro) +{ + struct reverb_status_gs_t *p = &reverb_status_gs; + macro *= 6; + p->character = reverb_macro_presets[macro]; + p->pre_lpf = reverb_macro_presets[macro + 1]; + p->level = reverb_macro_presets[macro + 2]; + p->time = reverb_macro_presets[macro + 3]; + p->delay_feedback = reverb_macro_presets[macro + 4]; + p->pre_delay_time = reverb_macro_presets[macro + 5]; +} + +/*! initialize Chorus Effect (GS) */ +void Reverb::init_chorus_status_gs(void) +{ + struct chorus_status_gs_t *p = &chorus_status_gs; + p->macro = 0; + p->pre_lpf = 0; + p->level = 0x40; + p->feedback = 0x08; + p->delay = 0x50; + p->rate = 0x03; + p->depth = 0x13; + p->send_reverb = 0; + p->send_delay = 0; + recompute_chorus_status_gs(); +} + +/*! recompute Chorus Effect (GS) */ +void Reverb::recompute_chorus_status_gs() +{ + struct chorus_status_gs_t *p = &chorus_status_gs; + + if (p->pre_lpf) { + p->lpf.a = 2.0 * ((double)(7 - p->pre_lpf) / 7.0f * 16000.0f + 200.0f) / playback_rate; + init_filter_lowpass1(&(p->lpf)); + } +} + +/*! Chorus Macro (GS), Chorus Type (GM2) */ +void Reverb::set_chorus_macro_gs(int macro) +{ + struct chorus_status_gs_t *p = &chorus_status_gs; + macro *= 8; + p->pre_lpf = chorus_macro_presets[macro]; + p->level = chorus_macro_presets[macro + 1]; + p->feedback = chorus_macro_presets[macro + 2]; + p->delay = chorus_macro_presets[macro + 3]; + p->rate = chorus_macro_presets[macro + 4]; + p->depth = chorus_macro_presets[macro + 5]; + p->send_reverb = chorus_macro_presets[macro + 6]; + p->send_delay = chorus_macro_presets[macro + 7]; +} + +/*! initialize EQ (GS) */ +void Reverb::init_eq_status_gs(void) +{ + struct eq_status_gs_t *p = &eq_status_gs; + p->low_freq = 0; + p->low_gain = 0x40; + p->high_freq = 0; + p->high_gain = 0x40; + recompute_eq_status_gs(); +} + +/*! recompute EQ (GS) */ +void Reverb::recompute_eq_status_gs(void) +{ + double freq, dbGain; + struct eq_status_gs_t *p = &eq_status_gs; + + /* Lowpass Shelving Filter */ + if (p->low_freq == 0) { freq = 200; } + else { freq = 400; } + dbGain = p->low_gain - 0x40; + if (freq < playback_rate / 2) { + p->lsf.q = 0; + p->lsf.freq = freq; + p->lsf.gain = dbGain; + calc_filter_shelving_low(&(p->lsf)); + } + + /* Highpass Shelving Filter */ + if (p->high_freq == 0) { freq = 3000; } + else { freq = 6000; } + dbGain = p->high_gain - 0x40; + if (freq < playback_rate / 2) { + p->hsf.q = 0; + p->hsf.freq = freq; + p->hsf.gain = dbGain; + calc_filter_shelving_high(&(p->hsf)); + } +} + +/*! initialize Multi EQ (XG) */ +void Reverb::init_multi_eq_xg(void) +{ + multi_eq_xg.valid = 0; + set_multi_eq_type_xg(0); + recompute_multi_eq_xg(); +} + +/*! set Multi EQ type (XG) */ +void Reverb::set_multi_eq_type_xg(int type) +{ + struct multi_eq_xg_t *p = &multi_eq_xg; + type *= 20; + p->gain1 = multi_eq_block_table_xg[type]; + p->freq1 = multi_eq_block_table_xg[type + 1]; + p->q1 = multi_eq_block_table_xg[type + 2]; + p->shape1 = multi_eq_block_table_xg[type + 3]; + p->gain2 = multi_eq_block_table_xg[type + 4]; + p->freq2 = multi_eq_block_table_xg[type + 5]; + p->q2 = multi_eq_block_table_xg[type + 6]; + p->gain3 = multi_eq_block_table_xg[type + 8]; + p->freq3 = multi_eq_block_table_xg[type + 9]; + p->q3 = multi_eq_block_table_xg[type + 10]; + p->gain4 = multi_eq_block_table_xg[type + 12]; + p->freq4 = multi_eq_block_table_xg[type + 13]; + p->q4 = multi_eq_block_table_xg[type + 14]; + p->gain5 = multi_eq_block_table_xg[type + 16]; + p->freq5 = multi_eq_block_table_xg[type + 17]; + p->q5 = multi_eq_block_table_xg[type + 18]; + p->shape5 = multi_eq_block_table_xg[type + 19]; +} + +/*! recompute Multi EQ (XG) */ +void Reverb::recompute_multi_eq_xg(void) +{ + struct multi_eq_xg_t *p = &multi_eq_xg; + + if (p->freq1 != 0 && p->freq1 < 60 && p->gain1 != 0x40) { + p->valid1 = 1; + if (p->shape1) { /* peaking */ + p->eq1p.q = (double)p->q1 / 10.0; + p->eq1p.freq = eq_freq_table_xg[p->freq1]; + p->eq1p.gain = p->gain1 - 0x40; + calc_filter_peaking(&(p->eq1p)); + } + else { /* shelving */ + p->eq1s.q = (double)p->q1 / 10.0; + p->eq1s.freq = eq_freq_table_xg[p->freq1]; + p->eq1s.gain = p->gain1 - 0x40; + calc_filter_shelving_low(&(p->eq1s)); + } + } + else { p->valid1 = 0; } + if (p->freq2 != 0 && p->freq2 < 60 && p->gain2 != 0x40) { + p->valid2 = 1; + p->eq2p.q = (double)p->q2 / 10.0; + p->eq2p.freq = eq_freq_table_xg[p->freq2]; + p->eq2p.gain = p->gain2 - 0x40; + calc_filter_peaking(&(p->eq2p)); + } + else { p->valid2 = 0; } + if (p->freq3 != 0 && p->freq3 < 60 && p->gain3 != 0x40) { + p->valid3 = 1; + p->eq3p.q = (double)p->q3 / 10.0; + p->eq4p.freq = eq_freq_table_xg[p->freq3]; + p->eq4p.gain = p->gain3 - 0x40; + calc_filter_peaking(&(p->eq3p)); + } + else { p->valid3 = 0; } + if (p->freq4 != 0 && p->freq4 < 60 && p->gain4 != 0x40) { + p->valid4 = 1; + p->eq4p.q = (double)p->q4 / 10.0; + p->eq4p.freq = eq_freq_table_xg[p->freq4]; + p->eq4p.gain = p->gain4 - 0x40; + calc_filter_peaking(&(p->eq4p)); + } + else { p->valid4 = 0; } + if (p->freq5 != 0 && p->freq5 < 60 && p->gain5 != 0x40) { + p->valid5 = 1; + if (p->shape5) { /* peaking */ + p->eq5p.q = (double)p->q5 / 10.0; + p->eq5p.freq = eq_freq_table_xg[p->freq5]; + p->eq5p.gain = p->gain5 - 0x40; + calc_filter_peaking(&(p->eq5p)); + } + else { /* shelving */ + p->eq5s.q = (double)p->q5 / 10.0; + p->eq5s.freq = eq_freq_table_xg[p->freq5]; + p->eq5s.gain = p->gain5 - 0x40; + calc_filter_shelving_high(&(p->eq5s)); + } + } + else { p->valid5 = 0; } + p->valid = p->valid1 || p->valid2 || p->valid3 || p->valid4 || p->valid5; +} + +void Reverb::set_effect_param_xg(struct effect_xg_t *st, int type_msb, int type_lsb) +{ + int i, j; + for (i = 0; effect_parameter_xg[i].type_msb != -1 + && effect_parameter_xg[i].type_lsb != -1; i++) { + if (type_msb == effect_parameter_xg[i].type_msb + && type_lsb == effect_parameter_xg[i].type_lsb) { + for (j = 0; j < 16; j++) { + st->param_lsb[j] = effect_parameter_xg[i].param_lsb[j]; + } + for (j = 0; j < 10; j++) { + st->param_msb[j] = effect_parameter_xg[i].param_msb[j]; + } + //printMessage(CMSG_INFO,VERB_NOISY,"XG EFX: %s", effect_parameter_xg[i].name); + return; + } + } + if (type_msb != 0) { + for (i = 0; effect_parameter_xg[i].type_msb != -1 + && effect_parameter_xg[i].type_lsb != -1; i++) { + if (type_lsb == effect_parameter_xg[i].type_lsb) { + for (j = 0; j < 16; j++) { + st->param_lsb[j] = effect_parameter_xg[i].param_lsb[j]; + } + for (j = 0; j < 10; j++) { + st->param_msb[j] = effect_parameter_xg[i].param_msb[j]; + } + //printMessage(CMSG_INFO,VERB_NOISY,"XG EFX: %s", effect_parameter_xg[i].name); + return; + } + } + } +} + +/*! recompute XG effect parameters. */ +void Reverb::recompute_effect_xg(struct effect_xg_t *st) +{ + EffectList *efc = st->ef; + + if (efc == NULL) { return; } + while (efc != NULL && efc->info != NULL) + { + (this->*(efc->engine->conv_xg))(st, efc); + (this->*(efc->engine->do_effect))(NULL, MAGIC_INIT_EFFECT_INFO, efc); + efc = efc->next_ef; + } +} + +void Reverb::realloc_effect_xg(struct effect_xg_t *st) +{ + int type_msb = st->type_msb, type_lsb = st->type_lsb; + + free_effect_list(st->ef); + st->ef = NULL; + st->use_msb = 0; + + switch (type_msb) { + case 0x05: + st->use_msb = 1; + st->ef = push_effect(st->ef, EFFECT_DELAY_LCR); + st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2); + break; + case 0x06: + st->use_msb = 1; + st->ef = push_effect(st->ef, EFFECT_DELAY_LR); + st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2); + break; + case 0x07: + st->use_msb = 1; + st->ef = push_effect(st->ef, EFFECT_ECHO); + st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2); + break; + case 0x08: + st->use_msb = 1; + st->ef = push_effect(st->ef, EFFECT_CROSS_DELAY); + st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2); + break; + case 0x41: + case 0x42: + st->ef = push_effect(st->ef, EFFECT_CHORUS); + st->ef = push_effect(st->ef, EFFECT_CHORUS_EQ3); + break; + case 0x43: + st->ef = push_effect(st->ef, EFFECT_FLANGER); + st->ef = push_effect(st->ef, EFFECT_CHORUS_EQ3); + break; + case 0x44: + st->ef = push_effect(st->ef, EFFECT_SYMPHONIC); + st->ef = push_effect(st->ef, EFFECT_CHORUS_EQ3); + break; + case 0x49: + st->ef = push_effect(st->ef, EFFECT_STEREO_DISTORTION); + st->ef = push_effect(st->ef, EFFECT_OD_EQ3); + break; + case 0x4A: + st->ef = push_effect(st->ef, EFFECT_STEREO_OVERDRIVE); + st->ef = push_effect(st->ef, EFFECT_OD_EQ3); + break; + case 0x4B: + st->ef = push_effect(st->ef, EFFECT_STEREO_AMP_SIMULATOR); + break; + case 0x4C: + st->ef = push_effect(st->ef, EFFECT_EQ3); + break; + case 0x4D: + st->ef = push_effect(st->ef, EFFECT_EQ2); + break; + case 0x4E: + if (type_lsb == 0x01 || type_lsb == 0x02) { + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH); + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_EQ2); + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_OD); + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_OD_EQ3); + } + else { + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH); + st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_EQ2); + } + break; + case 0x5E: + st->ef = push_effect(st->ef, EFFECT_LOFI); + break; + default: /* Not Supported */ + type_msb = type_lsb = 0; + break; + } + set_effect_param_xg(st, type_msb, type_lsb); + recompute_effect_xg(st); +} + +void Reverb::init_effect_xg(struct effect_xg_t *st) +{ + int i; + + free_effect_list(st->ef); + st->ef = NULL; + + st->use_msb = 0; + st->type_msb = st->type_lsb = st->connection = + st->send_reverb = st->send_chorus = 0; + st->part = 0x7f; + st->ret = st->pan = st->mw_depth = st->bend_depth = st->cat_depth = + st->ac1_depth = st->ac2_depth = st->cbc1_depth = st->cbc2_depth = 0x40; + for (i = 0; i < 16; i++) { st->param_lsb[i] = 0; } + for (i = 0; i < 10; i++) { st->param_msb[i] = 0; } +} + +/*! initialize XG effect parameters */ +void Reverb::init_all_effect_xg(void) +{ + int i; + init_effect_xg(&reverb_status_xg); + reverb_status_xg.type_msb = 0x01; + reverb_status_xg.connection = XG_CONN_SYSTEM_REVERB; + realloc_effect_xg(&reverb_status_xg); + init_effect_xg(&chorus_status_xg); + chorus_status_xg.type_msb = 0x41; + chorus_status_xg.connection = XG_CONN_SYSTEM_CHORUS; + realloc_effect_xg(&chorus_status_xg); + for (i = 0; i < XG_VARIATION_EFFECT_NUM; i++) { + init_effect_xg(&variation_effect_xg[i]); + variation_effect_xg[i].type_msb = 0x05; + realloc_effect_xg(&variation_effect_xg[i]); + } + for (i = 0; i < XG_INSERTION_EFFECT_NUM; i++) { + init_effect_xg(&insertion_effect_xg[i]); + insertion_effect_xg[i].type_msb = 0x49; + realloc_effect_xg(&insertion_effect_xg[i]); + } + init_ch_effect_xg(); +} + +/*! initialize GS insertion effect parameters */ +void Reverb::init_insertion_effect_gs(void) +{ + int i; + struct insertion_effect_gs_t *st = &insertion_effect_gs; + + free_effect_list(st->ef); + st->ef = NULL; + + for (i = 0; i < 20; i++) { st->parameter[i] = 0; } + + st->type = 0; + st->type_lsb = 0; + st->type_msb = 0; + st->send_reverb = 0x28; + st->send_chorus = 0; + st->send_delay = 0; + st->control_source1 = 0; + st->control_depth1 = 0x40; + st->control_source2 = 0; + st->control_depth2 = 0x40; + st->send_eq_switch = 0x01; +} + +void Reverb::set_effect_param_gs(struct insertion_effect_gs_t *st, int msb, int lsb) +{ + int i, j; + for (i = 0; effect_parameter_gs[i].type_msb != -1 + && effect_parameter_gs[i].type_lsb != -1; i++) { + if (msb == effect_parameter_gs[i].type_msb + && lsb == effect_parameter_gs[i].type_lsb) { + for (j = 0; j < 20; j++) { + st->parameter[j] = effect_parameter_gs[i].param[j]; + } + //printMessage(CMSG_INFO,VERB_NOISY,"GS EFX: %s", effect_parameter_gs[i].name); + break; + } + } +} + +/*! recompute GS insertion effect parameters. */ +void Reverb::recompute_insertion_effect_gs(void) +{ + struct insertion_effect_gs_t *st = &insertion_effect_gs; + EffectList *efc = st->ef; + + if (st->ef == NULL) { return; } + while (efc != NULL && efc->info != NULL) + { + (this->*(efc->engine->conv_gs))(st, efc); + (this->*(efc->engine->do_effect))(NULL, MAGIC_INIT_EFFECT_INFO, efc); + efc = efc->next_ef; + } +} + +/*! re-allocate GS insertion effect parameters. */ +void Reverb::realloc_insertion_effect_gs(void) +{ + struct insertion_effect_gs_t *st = &insertion_effect_gs; + int type_msb = st->type_msb, type_lsb = st->type_lsb; + + free_effect_list(st->ef); + st->ef = NULL; + + switch (type_msb) { + case 0x01: + switch (type_lsb) { + case 0x00: /* Stereo-EQ */ + st->ef = push_effect(st->ef, EFFECT_STEREO_EQ); + break; + case 0x10: /* Overdrive */ + st->ef = push_effect(st->ef, EFFECT_EQ2); + st->ef = push_effect(st->ef, EFFECT_OVERDRIVE1); + break; + case 0x11: /* Distortion */ + st->ef = push_effect(st->ef, EFFECT_EQ2); + st->ef = push_effect(st->ef, EFFECT_DISTORTION1); + break; + case 0x40: /* Hexa Chorus */ + st->ef = push_effect(st->ef, EFFECT_EQ2); + st->ef = push_effect(st->ef, EFFECT_HEXA_CHORUS); + break; + case 0x72: /* Lo-Fi 1 */ + st->ef = push_effect(st->ef, EFFECT_EQ2); + st->ef = push_effect(st->ef, EFFECT_LOFI1); + break; + case 0x73: /* Lo-Fi 2 */ + st->ef = push_effect(st->ef, EFFECT_EQ2); + st->ef = push_effect(st->ef, EFFECT_LOFI2); + break; + default: break; + } + break; + case 0x11: + switch (type_lsb) { + case 0x03: /* OD1 / OD2 */ + st->ef = push_effect(st->ef, EFFECT_OD1OD2); + break; + default: break; + } + break; + default: break; + } + + set_effect_param_gs(st, type_msb, type_lsb); + + recompute_insertion_effect_gs(); +} + +void Reverb::init_effect_status(int play_system_mode) +{ + free_effect_buffers(); + init_reverb_status_gs(); + init_delay_status_gs(); + init_chorus_status_gs(); + init_eq_status_gs(); + init_insertion_effect_gs(); + init_multi_eq_xg(); + if (play_system_mode == XG_SYSTEM_MODE) { init_all_effect_xg(); } +} + +///////////////////////////////////////////////////////////////////// +} diff --git a/thirdparty/timidityplus/sbkconv.cpp b/thirdparty/timidityplus/sbkconv.cpp new file mode 100644 index 000000000..1b93a431e --- /dev/null +++ b/thirdparty/timidityplus/sbkconv.cpp @@ -0,0 +1,208 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*================================================================ + * SBK --> SF2 Conversion + * + * Copyright (C) 1996,1997 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *================================================================*/ + +#include +#include +#include "common.h" +#include "sfitem.h" + + +namespace TimidityPlus +{ + +/*---------------------------------------------------------------- + * prototypes + *----------------------------------------------------------------*/ + +static int sbk_cutoff(int gen, int val); +static int sbk_filterQ(int gen, int val); +static int sbk_tenpct(int gen, int val); +static int sbk_panpos(int gen, int val); +static int sbk_atten(int gen, int val); +static int sbk_scale(int gen, int val); +static int sbk_time(int gen, int val); +static int sbk_tm_key(int gen, int val); +static int sbk_freq(int gen, int val); +static int sbk_pshift(int gen, int val); +static int sbk_cshift(int gen, int val); +static int sbk_tremolo(int gen, int val); +static int sbk_volsust(int gen, int val); +static int sbk_modsust(int gen, int val); + +/*---------------------------------------------------------------- + * convertor function table + *----------------------------------------------------------------*/ + +static SBKConv sbk_convertors[T_EOT] = { + NULL, NULL, NULL, NULL, NULL, + + sbk_cutoff, sbk_filterQ, sbk_tenpct, sbk_panpos, sbk_atten, sbk_scale, + + sbk_time, sbk_tm_key, sbk_freq, sbk_pshift, sbk_cshift, + sbk_tremolo, sbk_modsust, sbk_volsust, +}; + + +/*---------------------------------------------------------------- + * sbk --> sf2 conversion + *----------------------------------------------------------------*/ + +int sbk_to_sf2(int oper, int amount, const LayerItem *layer_items) +{ + const LayerItem *item = &layer_items[oper]; + if (item->type < 0 || item->type >= T_EOT) { + fprintf(stderr, "illegal gen item type %d\n", item->type); + return amount; + } + if (sbk_convertors[item->type]) + return sbk_convertors[item->type](oper, amount); + return amount; +} + +/*---------------------------------------------------------------- + * conversion rules for each type + *----------------------------------------------------------------*/ + +/* initial cutoff */ +static int sbk_cutoff(int gen, int val) +{ + if (val == 127) + return 14400; + else + return 59 * val + 4366; + /*return 50 * val + 4721;*/ +} + +/* initial resonance */ +static int sbk_filterQ(int gen, int val) +{ + return val * 3 / 2; +} + +/* chorus/reverb */ +static int sbk_tenpct(int gen, int val) +{ + return val * 1000 / 256; +} + +/* pan position */ +static int sbk_panpos(int gen, int val) +{ + return val * 1000 / 127 - 500; +} + +/* initial attenuation */ +static int sbk_atten(int gen, int val) +{ + if (val == 0) + return 1000; + return (int)(-200.0 * log10((double)val / 127.0) * 10); +} + +/* scale tuning */ +static int sbk_scale(int gen, int val) +{ + return (val ? 50 : 100); +} + +/* env/lfo time parameter */ +static int sbk_time(int gen, int val) +{ + if (val <= 0) val = 1; + return (int)(log((double)val / 1000.0) / log(2.0) * 1200.0); +} + +/* time change per key */ +static int sbk_tm_key(int gen, int val) +{ + return (int)(val * 5.55); +} + +/* lfo frequency */ +static int sbk_freq(int gen, int val) +{ + if (val == 0) { + if (gen == SF_freqLfo1) return -725; + else /* SF_freqLfo2*/ return -15600; + } + /*return (int)(3986.0 * log10((double)val) - 7925.0);*/ + return (int)(1200 * log10((double)val) / log10(2.0) - 7925.0); + +} + +/* lfo/env pitch shift */ +static int sbk_pshift(int gen, int val) +{ + return (1200 * val / 64 + 1) / 2; +} + +/* lfo/env cutoff freq shift */ +static int sbk_cshift(int gen, int val) +{ + if (gen == SF_lfo1ToFilterFc) + return (1200 * 3 * val) / 64; + else + return (1200 * 6 * val) / 64; +} + +/* lfo volume shift */ +static int sbk_tremolo(int gen, int val) +{ + return (120 * val) / 64; +} + +/* mod env sustain */ +static int sbk_modsust(int gen, int val) +{ + if (val < 96) + return 1000 * (96 - val) / 96; + else + return 0; +} + +/* vol env sustain */ +static int sbk_volsust(int gen, int val) +{ + if (val < 96) + return (2000 - 21 * val) / 2; + else + return 0; +} +} \ No newline at end of file diff --git a/thirdparty/timidityplus/sffile.cpp b/thirdparty/timidityplus/sffile.cpp new file mode 100644 index 000000000..ecc32c53a --- /dev/null +++ b/thirdparty/timidityplus/sffile.cpp @@ -0,0 +1,724 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*================================================================ + * sffile.c + * read SoundFont file (SBK/SF2) and store the layer lists + * + * Copyright (C) 1996,1997 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *================================================================*/ + +/* + * Modified by Masanao Izumo + */ + +#include +#include +#include +#include "timidity.h" +#include "common.h" +#include "instrum.h" + +namespace TimidityPlus +{ + +/*================================================================ + * preset / instrument bag record + *================================================================*/ + + + /*---------------------------------------------------------------- + * function prototypes + *----------------------------------------------------------------*/ + +#define NEW(type,nums) (type*)safe_malloc(sizeof(type) * (nums)) + +static int READCHUNK(SFChunk *vp, timidity_file *tf) +{ + if (tf_read(vp, 8, tf) != 8) + return -1; + vp->size = LE_LONG(vp->size); + return 1; +} + +static int READDW(uint32_t *vp, timidity_file *tf) +{ + if (tf_read(vp, 4, tf) != 4) + return -1; + *vp = LE_LONG(*vp); + return 1; +} + +static int READW(uint16_t *vp, timidity_file *tf) +{ + if (tf_read(vp, 2, tf) != 2) + return -1; + *vp = LE_SHORT(*vp); + return 1; +} + +static int READSTR(char *str, timidity_file *tf) +{ + int n; + + if (tf_read(str, 20, tf) != 20) + return -1; + str[19] = '\0'; + n = (int)strlen(str); + while (n > 0 && str[n - 1] == ' ') + n--; + str[n] = '\0'; + return n; +} + +#define READID(var,tf) tf_read(var, 4, tf) +#define READB(var,tf) tf_read(&var, 1, tf) +#define SKIPB(tf) skip(tf, 1) +#define SKIPW(tf) skip(tf, 2) +#define SKIPDW(tf) skip(tf, 4) + +#define FSKIP(size,tf) skip(tf, size) + + +/*----------------------------------------------------------------*/ + +/*---------------------------------------------------------------- + * id numbers + *----------------------------------------------------------------*/ + +enum { + /* level 0; chunk */ + UNKN_ID, RIFF_ID, LIST_ID, SFBK_ID, + /* level 1; id only */ + INFO_ID, SDTA_ID, PDTA_ID, + /* info stuff; chunk */ + IFIL_ID, ISNG_ID, IROM_ID, INAM_ID, IVER_ID, IPRD_ID, ICOP_ID, + ICRD_ID, IENG_ID, ISFT_ID, ICMT_ID, + /* sample data stuff; chunk */ + SNAM_ID, SMPL_ID, + /* preset stuff; chunk */ + PHDR_ID, PBAG_ID, PMOD_ID, PGEN_ID, + /* inst stuff; chunk */ + INST_ID, IBAG_ID, IMOD_ID, IGEN_ID, + /* sample header; chunk */ + SHDR_ID +}; + + +/*================================================================ + * load a soundfont file + *================================================================*/ + +int Instruments::load_soundfont(SFInfo *sf, timidity_file *fd) +{ + SFChunk chunk; + + sf->preset = NULL; + sf->sample = NULL; + sf->inst = NULL; + sf->sf_name = NULL; + + prbags.bag = inbags.bag = NULL; + prbags.gen = inbags.gen = NULL; + prbags.nbags = inbags.nbags = + prbags.ngens = inbags.ngens = 0; + + /* check RIFF file header */ + READCHUNK(&chunk, fd); + if (chunkid(chunk.id) != RIFF_ID) { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: *** not a RIFF file", fd->filename.c_str()); + return -1; + } + /* check file id */ + READID(chunk.id, fd); + if (chunkid(chunk.id) != SFBK_ID) { + printMessage(CMSG_ERROR, VERB_NORMAL, + "%s: *** not a SoundFont file", fd->filename.c_str()); + return -1; + } + + for (;;) { + if (READCHUNK(&chunk, fd) <= 0) + break; + else if (chunkid(chunk.id) == LIST_ID) { + if (process_list(chunk.size, sf, fd)) + break; + } + else { + printMessage(CMSG_WARNING, VERB_NORMAL, "%s: *** illegal id in level 0: %4.4s %4d", fd->filename.c_str(), chunk.id, chunk.size); + FSKIP(chunk.size, fd); + // Not aborting here will inevitably crash. + return -1; + } + } + + /* parse layer structure */ + convert_layers(sf); + + /* free private tables */ + if (prbags.bag) free(prbags.bag); + if (prbags.gen) free(prbags.gen); + if (inbags.bag) free(inbags.bag); + if (inbags.gen) free(inbags.gen); + + return 0; +} + + +/*================================================================ + * free buffer + *================================================================*/ + +void Instruments::free_soundfont(SFInfo *sf) +{ + int i; + if (sf->preset) { + for (i = 0; i < sf->npresets; i++) + free_layer(&sf->preset[i].hdr); + free(sf->preset); + } + if (sf->inst) { + for (i = 0; i < sf->ninsts; i++) + free_layer(&sf->inst[i].hdr); + free(sf->inst); + } + if (sf->sample) free(sf->sample); + if (sf->sf_name) free(sf->sf_name); +} + + +/*---------------------------------------------------------------- + * get id value from 4bytes ID string + *----------------------------------------------------------------*/ + +int Instruments::chunkid(char *id) +{ + static struct idstring { + const char *str; + int id; + } idlist[] = { + {"RIFF", RIFF_ID}, + {"LIST", LIST_ID}, + {"sfbk", SFBK_ID}, + {"INFO", INFO_ID}, + {"sdta", SDTA_ID}, + {"snam", SNAM_ID}, + {"smpl", SMPL_ID}, + {"pdta", PDTA_ID}, + {"phdr", PHDR_ID}, + {"pbag", PBAG_ID}, + {"pmod", PMOD_ID}, + {"pgen", PGEN_ID}, + {"inst", INST_ID}, + {"ibag", IBAG_ID}, + {"imod", IMOD_ID}, + {"igen", IGEN_ID}, + {"shdr", SHDR_ID}, + {"ifil", IFIL_ID}, + {"isng", ISNG_ID}, + {"irom", IROM_ID}, + {"iver", IVER_ID}, + {"INAM", INAM_ID}, + {"IPRD", IPRD_ID}, + {"ICOP", ICOP_ID}, + {"ICRD", ICRD_ID}, + {"IENG", IENG_ID}, + {"ISFT", ISFT_ID}, + {"ICMT", ICMT_ID}, + }; + + for (unsigned i = 0; i < sizeof(idlist) / sizeof(idlist[0]); i++) { + if (strncmp(id, idlist[i].str, 4) == 0) + return idlist[i].id; + } + + return UNKN_ID; +} + + +/*================================================================ + * process a list chunk + *================================================================*/ + +int Instruments::process_list(int size, SFInfo *sf, timidity_file *fd) +{ + SFChunk chunk; + + /* read the following id string */ + READID(chunk.id, fd); size -= 4; + printMessage(CMSG_INFO, VERB_DEBUG, "%c%c%c%c:", + chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]); + switch (chunkid(chunk.id)) { + case INFO_ID: + return process_info(size, sf, fd); + case SDTA_ID: + return process_sdta(size, sf, fd); + case PDTA_ID: + return process_pdta(size, sf, fd); + default: + printMessage(CMSG_WARNING, VERB_NORMAL, "%s: *** illegal id in level 1: %4.4s", fd->filename.c_str(), chunk.id); + FSKIP(size, fd); /* skip it */ + return 0; + } +} + + +/*================================================================ + * process info list + *================================================================*/ + +int Instruments::process_info(int size, SFInfo *sf, timidity_file *fd) +{ + sf->infopos = tf_tell(fd); + sf->infosize = size; + + /* parse the buffer */ + while (size > 0) { + SFChunk chunk; + + /* read a sub chunk */ + if (READCHUNK(&chunk, fd) <= 0) + return -1; + size -= 8; + + printMessage(CMSG_INFO, VERB_DEBUG, " %c%c%c%c:", + chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]); + switch (chunkid(chunk.id)) { + case IFIL_ID: + /* soundfont file version */ + READW(&sf->version, fd); + READW(&sf->minorversion, fd); + printMessage(CMSG_INFO, VERB_DEBUG, + " version %d, minor %d", + sf->version, sf->minorversion); + break; + case INAM_ID: + /* name of the font */ + sf->sf_name = (char*)safe_malloc(chunk.size + 1); + tf_read(sf->sf_name, chunk.size, fd); + sf->sf_name[chunk.size] = 0; + printMessage(CMSG_INFO, VERB_DEBUG, + " name %s", sf->sf_name); + break; + + default: + FSKIP(chunk.size, fd); + break; + } + size -= chunk.size; + } + return 0; +} + + +/*================================================================ + * process sample data list + *================================================================*/ + +int Instruments::process_sdta(int size, SFInfo *sf, timidity_file *fd) +{ + while (size > 0) { + SFChunk chunk; + + /* read a sub chunk */ + if (READCHUNK(&chunk, fd) <= 0) + return -1; + size -= 8; + + printMessage(CMSG_INFO, VERB_DEBUG, " %c%c%c%c:", + chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]); + switch (chunkid(chunk.id)) { + case SNAM_ID: + /* sample name list */ + load_sample_names(chunk.size, sf, fd); + break; + case SMPL_ID: + /* sample data starts from here */ + sf->samplepos = tf_tell(fd); + sf->samplesize = chunk.size; + FSKIP(chunk.size, fd); + break; + default: + FSKIP(chunk.size, fd); + break; + } + size -= chunk.size; + } + return 0; +} + + +/*================================================================ + * process preset data list + *================================================================*/ + +int Instruments::process_pdta(int size, SFInfo *sf, timidity_file *fd) +{ + while (size > 0) { + SFChunk chunk; + + /* read a subchunk */ + if (READCHUNK(&chunk, fd) <= 0) + return -1; + size -= 8; + + printMessage(CMSG_INFO, VERB_DEBUG, " %c%c%c%c:", + chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]); + switch (chunkid(chunk.id)) { + case PHDR_ID: + load_preset_header(chunk.size, sf, fd); + break; + case PBAG_ID: + load_bag(chunk.size, &prbags, fd); + break; + case PGEN_ID: + load_gen(chunk.size, &prbags, fd); + break; + case INST_ID: + load_inst_header(chunk.size, sf, fd); + break; + case IBAG_ID: + load_bag(chunk.size, &inbags, fd); + break; + case IGEN_ID: + load_gen(chunk.size, &inbags, fd); + break; + case SHDR_ID: + load_sample_info(chunk.size, sf, fd); + break; + case PMOD_ID: /* ignored */ + case IMOD_ID: /* ingored */ + default: + FSKIP(chunk.size, fd); + break; + } + size -= chunk.size; + } + return 0; +} + + +/*---------------------------------------------------------------- + * store sample name list; sf1 only + *----------------------------------------------------------------*/ + +void Instruments::load_sample_names(int size, SFInfo *sf, timidity_file *fd) +{ + int i, nsamples; + if (sf->version > 1) { + printMessage(CMSG_WARNING, VERB_NORMAL, "%s: *** version 2 has obsolete format??",fd->filename.c_str()); + FSKIP(size, fd); + return; + } + + /* each sample name has a fixed lentgh (20 bytes) */ + nsamples = size / 20; + if (sf->sample == NULL) { + sf->nsamples = nsamples; + sf->sample = NEW(SFSampleInfo, sf->nsamples); + } + else if (sf->nsamples != nsamples) { + printMessage(CMSG_WARNING, VERB_NORMAL, "%s: *** different # of samples ?? (%d : %d)\n",fd->filename.c_str(), sf->nsamples, nsamples); + FSKIP(size, fd); + return; + } + + /* read each name from file */ + for (i = 0; i < sf->nsamples; i++) { + READSTR(sf->sample[i].name, fd); + } +} + + +/*---------------------------------------------------------------- + * preset header list + *----------------------------------------------------------------*/ + +void Instruments::load_preset_header(int size, SFInfo *sf, timidity_file *fd) +{ + int i; + + sf->npresets = size / 38; + sf->preset = NEW(SFPresetHdr, sf->npresets); + for (i = 0; i < sf->npresets; i++) { + READSTR(sf->preset[i].hdr.name, fd); + READW(&sf->preset[i].preset, fd); + READW(&sf->preset[i].bank, fd); + READW(&sf->preset[i].hdr.bagNdx, fd); + SKIPDW(fd); /* lib; ignored*/ + SKIPDW(fd); /* genre; ignored */ + SKIPDW(fd); /* morph; ignored */ + /* initialize layer table; it'll be parsed later */ + sf->preset[i].hdr.nlayers = 0; + sf->preset[i].hdr.layer = NULL; + } +} + + +/*---------------------------------------------------------------- + * instrument header list + *----------------------------------------------------------------*/ + +void Instruments::load_inst_header(int size, SFInfo *sf, timidity_file *fd) +{ + int i; + + sf->ninsts = size / 22; + sf->inst = NEW(SFInstHdr, sf->ninsts); + for (i = 0; i < sf->ninsts; i++) { + READSTR(sf->inst[i].hdr.name, fd); + READW(&sf->inst[i].hdr.bagNdx, fd); + /* iniitialize layer table; it'll be parsed later */ + sf->inst[i].hdr.nlayers = 0; + sf->inst[i].hdr.layer = NULL; + + printMessage(CMSG_INFO, VERB_DEBUG, + " InstHdr %d (%s) bagNdx=%d", + i, sf->inst[i].hdr.name, sf->inst[i].hdr.bagNdx); + } +} + + +/*---------------------------------------------------------------- + * load preset/instrument bag list on the private table + *----------------------------------------------------------------*/ + +void Instruments::load_bag(int size, SFBags *bagp, timidity_file *fd) +{ + int i; + + size /= 4; + bagp->bag = NEW(uint16_t, size); + for (i = 0; i < size; i++) { + READW(&bagp->bag[i], fd); + SKIPW(fd); /* mod; ignored */ + } + bagp->nbags = size; +} + + +/*---------------------------------------------------------------- + * load preset/instrument generator list on the private table + *----------------------------------------------------------------*/ + +void Instruments::load_gen(int size, SFBags *bagp, timidity_file *fd) +{ + int i; + + size /= 4; + bagp->gen = NEW(SFGenRec, size); + for (i = 0; i < size; i++) { + READW((uint16_t *)&bagp->gen[i].oper, fd); + READW((uint16_t *)&bagp->gen[i].amount, fd); + } + bagp->ngens = size; +} + + +/*---------------------------------------------------------------- + * load sample info list + *----------------------------------------------------------------*/ + +void Instruments::load_sample_info(int size, SFInfo *sf, timidity_file *fd) +{ + int i; + int in_rom; + + /* the record size depends on the soundfont version */ + if (sf->version > 1) { + /* SF2 includes sample name and other infos */ + sf->nsamples = size / 46; + sf->sample = NEW(SFSampleInfo, sf->nsamples); + } + else { + /* SBK; sample name may be read already */ + int nsamples = size / 16; + if (sf->sample == NULL) { + sf->nsamples = nsamples; + sf->sample = NEW(SFSampleInfo, sf->nsamples); + } + else if (sf->nsamples != nsamples) { + /* overwrite it */ + sf->nsamples = nsamples; + } + } + + in_rom = 1; /* data may start from ROM samples */ + for (i = 0; i < sf->nsamples; i++) { + if (sf->version > 1) /* SF2 only */ + READSTR(sf->sample[i].name, fd); + READDW((uint32_t *)&sf->sample[i].startsample, fd); + READDW((uint32_t *)&sf->sample[i].endsample, fd); + READDW((uint32_t *)&sf->sample[i].startloop, fd); + READDW((uint32_t *)&sf->sample[i].endloop, fd); + if (sf->version > 1) { /* SF2 only */ + READDW((uint32_t *)&sf->sample[i].samplerate, fd); + READB(sf->sample[i].originalPitch, fd); + READB(sf->sample[i].pitchCorrection, fd); + READW(&sf->sample[i].samplelink, fd); + READW(&sf->sample[i].sampletype, fd); + } + else { /* for SBK; set missing infos */ + sf->sample[i].samplerate = 44100; + sf->sample[i].originalPitch = 60; + sf->sample[i].pitchCorrection = 0; + sf->sample[i].samplelink = 0; + /* the first RAM data starts from address 0 */ + if (sf->sample[i].startsample == 0) + in_rom = 0; + if (in_rom) + sf->sample[i].sampletype = 0x8001; + else + sf->sample[i].sampletype = 1; + } + } +} + + +/*================================================================ + * convert from bags to layers + *================================================================*/ + +void Instruments::convert_layers(SFInfo *sf) +{ + int i; + + if (prbags.bag == NULL || prbags.gen == NULL || + inbags.bag == NULL || inbags.gen == NULL) { + printMessage(CMSG_WARNING, VERB_NORMAL, "%s: *** illegal bags / gens", sf->sf_name); + return; + } + + for (i = 0; i < sf->npresets - 1; i++) { + generate_layers(&sf->preset[i].hdr, + &sf->preset[i + 1].hdr, + &prbags); + } + for (i = 0; i < sf->ninsts - 1; i++) { + generate_layers(&sf->inst[i].hdr, + &sf->inst[i + 1].hdr, + &inbags); + } +} + + +/*---------------------------------------------------------------- + * generate layer lists from stored bags + *----------------------------------------------------------------*/ + +void Instruments::generate_layers(SFHeader *hdr, SFHeader *next, SFBags *bags) +{ + int i; + SFGenLayer *layp; + + hdr->nlayers = next->bagNdx - hdr->bagNdx; + if (hdr->nlayers < 0) { + printMessage(CMSG_WARNING, VERB_NORMAL, "%s: illegal layer numbers %d", "", hdr->nlayers); + return; + } + if (hdr->nlayers == 0) + return; + hdr->layer = (SFGenLayer*)safe_malloc(sizeof(SFGenLayer) * hdr->nlayers); + layp = hdr->layer; + for (layp = hdr->layer, i = hdr->bagNdx; i < next->bagNdx; layp++, i++) { + int genNdx = bags->bag[i]; + layp->nlists = bags->bag[i + 1] - genNdx; + if (layp->nlists < 0) { + printMessage(CMSG_WARNING, VERB_NORMAL, "%s: illegal list numbers %d", "", layp->nlists); + return; + } + layp->list = (SFGenRec*)safe_malloc(sizeof(SFGenRec) * layp->nlists); + memcpy(layp->list, &bags->gen[genNdx], + sizeof(SFGenRec) * layp->nlists); + } +} + +/*---------------------------------------------------------------- + * free a layer + *----------------------------------------------------------------*/ + +void Instruments::free_layer(SFHeader *hdr) +{ + int i; + for (i = 0; i < hdr->nlayers; i++) { + SFGenLayer *layp = &hdr->layer[i]; + if (layp->nlists >= 0) + free(layp->list); + } + if (hdr->nlayers > 0) + free(hdr->layer); +} + +/* add blank loop for each data */ +static const int auto_add_blank = 0; +void Instruments::correct_samples(SFInfo *sf) +{ + int i; + SFSampleInfo *sp; + int prev_end; + + prev_end = 0; + for (sp = sf->sample, i = 0; i < sf->nsamples; i++, sp++) { + /* correct sample positions for SBK file */ + if (sf->version == 1) { + sp->startloop++; + sp->endloop += 2; + } + + /* calculate sample data size */ + if (sp->sampletype & 0x8000) + sp->size = 0; + else if (sp->startsample < prev_end && sp->startsample != 0) + sp->size = 0; + else { + sp->size = -1; + if (!auto_add_blank && i != sf->nsamples - 1) + sp->size = sp[1].startsample - sp->startsample; + if (sp->size < 0) + sp->size = sp->endsample - sp->startsample + 48; + } + prev_end = sp->endsample; + + /* calculate short-shot loop size */ + if (auto_add_blank || i == sf->nsamples - 1) + sp->loopshot = 48; + else { + sp->loopshot = sp[1].startsample - sp->endsample; + if (sp->loopshot < 0 || sp->loopshot > 48) + sp->loopshot = 48; + } + } +} +} diff --git a/thirdparty/timidityplus/sfitem.cpp b/thirdparty/timidityplus/sfitem.cpp new file mode 100644 index 000000000..1e2e5cbfd --- /dev/null +++ b/thirdparty/timidityplus/sfitem.cpp @@ -0,0 +1,96 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*================================================================ + * sfitem.c + * soundfont generator table definition + *================================================================*/ + +#include +#include "sflayer.h" +#include "sfitem.h" + +namespace TimidityPlus +{ + +/* layer type definitions */ +const LayerItem static_layer_items[SF_EOF] = { + {L_INHRT, T_OFFSET, 0, 0, 0}, /* startAddrs */ + {L_INHRT, T_OFFSET, 0, 0, 0}, /* endAddrs */ + {L_INHRT, T_OFFSET, 0, 0, 0}, /* startloopAddrs */ + {L_INHRT, T_OFFSET, 0, 0, 0}, /* endloopAddrs */ + {L_INHRT, T_HI_OFF, 0, 0, 0}, /* startAddrsHi */ + {L_INHRT, T_PSHIFT, -12000, 12000, 0}, /* lfo1ToPitch */ + {L_INHRT, T_PSHIFT, -12000, 12000, 0}, /* lfo2ToPitch */ + {L_INHRT, T_PSHIFT, -12000, 12000, 0}, /* env1ToPitch */ + {L_INHRT, T_CUTOFF, 1500, 13500, 13500}, /* initialFilterFc */ + {L_INHRT, T_FILTERQ, 0, 960, 0}, /* initialFilterQ */ + {L_INHRT, T_CSHIFT, -12000, 12000, 0}, /* lfo1ToFilterFc */ + {L_INHRT, T_CSHIFT, -12000, 12000, 0}, /* env1ToFilterFc */ + {L_INHRT, T_HI_OFF, 0, 0, 0}, /* endAddrsHi */ + {L_INHRT, T_TREMOLO, -960, 960, 0}, /* lfo1ToVolume */ + {L_INHRT, T_NOP, 0, 0, 0}, /* env2ToVolume / unused1 */ + {L_INHRT, T_TENPCT, 0, 1000, 0}, /* chorusEffectsSend */ + {L_INHRT, T_TENPCT, 0, 1000, 0}, /* reverbEffectsSend */ + {L_INHRT, T_PANPOS, 0, 1000, 0}, /* panEffectsSend */ + {L_INHRT, T_NOP, 0, 0, 0}, /* unused */ + {L_INHRT, T_NOP, 0, 0, 0}, /* sampleVolume / unused */ + {L_INHRT, T_NOP, 0, 0, 0}, /* unused3 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* delayLfo1 */ + {L_INHRT, T_FREQ, -16000, 4500, 0}, /* freqLfo1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* delayLfo2 */ + {L_INHRT, T_FREQ, -16000, 4500, 0}, /* freqLfo2 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* delayEnv1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* attackEnv1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* holdEnv1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* decayEnv1 */ + {L_INHRT, T_MODSUST, 0, 1000, 0}, /* sustainEnv1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* releaseEnv1 */ + {L_INHRT, T_TM_KEY, -1200, 1200, 0}, /* autoHoldEnv1 */ + {L_INHRT, T_TM_KEY, -1200, 1200, 0}, /* autoDecayEnv1 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* delayEnv2 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* attackEnv2 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* holdEnv2 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* decayEnv2 */ + {L_INHRT, T_VOLSUST, 0, 1440, 0}, /* sustainEnv2 */ + {L_INHRT, T_TIME, -12000, 5000, -12000}, /* releaseEnv2 */ + {L_INHRT, T_TM_KEY, -1200, 1200, 0}, /* autoHoldEnv2 */ + {L_INHRT, T_TM_KEY, -1200, 1200, 0}, /* autoDecayEnv2 */ + {L_PRSET, T_NOCONV, 0, 0, 0}, /* instrument */ + {L_INHRT, T_NOP, 0, 0, 0}, /* nop */ + {L_RANGE, T_RANGE, 0, 0, RANGE(0,127)}, /* keyRange */ + {L_RANGE, T_RANGE, 0, 0, RANGE(0,127)}, /* velRange */ + {L_INHRT, T_HI_OFF, 0, 0, 0}, /* startloopAddrsHi */ + {L_OVWRT, T_NOCONV, 0, 127, -1}, /* keynum */ + {L_OVWRT, T_NOCONV, 0, 127, -1}, /* velocity */ + {L_INHRT, T_ATTEN, 0, 1440, 0}, /* initialAttenuation */ + {L_INHRT, T_NOP, 0, 0, 0}, /* keyTuning */ + {L_INHRT, T_HI_OFF, 0, 0, 0}, /* endloopAddrsHi */ + {L_INHRT, T_NOCONV, -120, 120, 0}, /* coarseTune */ + {L_INHRT, T_NOCONV, -99, 99, 0}, /* fineTune */ + {L_INSTR, T_NOCONV, 0, 0, 0}, /* sampleId */ + {L_OVWRT, T_NOCONV, 0, 3, 0}, /* sampleFlags */ + {L_OVWRT, T_NOCONV, 0, 0, 0}, /* samplePitch (only in SBK) */ + {L_INHRT, T_SCALE, 0, 1200, 100}, /* scaleTuning */ + {L_OVWRT, T_NOCONV, 0, 127, 0}, /* keyExclusiveClass */ + {L_OVWRT, T_NOCONV, 0, 127, -1}, /* rootKey */ +}; + +} diff --git a/thirdparty/timidityplus/smplfile.cpp b/thirdparty/timidityplus/smplfile.cpp new file mode 100644 index 000000000..fd7189189 --- /dev/null +++ b/thirdparty/timidityplus/smplfile.cpp @@ -0,0 +1,1226 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + smplfile.c + + core and WAVE,AIFF/AIFF-C importer by Kentaro Sato +*/ + +#include +#include + +#include + +#include "timidity.h" +#include "common.h" +#include "filter.h" +#include "instrum.h" +#include "playmidi.h" +#include "resample.h" +#include "tables.h" + +namespace TimidityPlus +{ + +typedef int (Instruments::*SampleImporterDiscriminateProc)(char *sample_file); + /* returns 0 if file may be loadable */ +typedef int (Instruments::*SampleImporterSampleLoaderProc)(char *sample_file, Instrument *inst); + /* sets inst->samples, inst->sample and returns 0 if loaded */ + /* inst is pre-allocated, and is freed by caller if loading failed */ + /* -1 to let caller give up testing other importers */ + +struct SampleImporter { + const char *extension; /* file extension excluding '.' */ + SampleImporterDiscriminateProc discriminant; + SampleImporterSampleLoaderProc load; + /* either extension or discriminant may be NULL */ + int added; /* for get_importers()'s internal use */ +}; + +static double ConvertFromIeeeExtended(const char *); + +Instrument *Instruments::extract_sample_file(char *sample_file) +{ + Instrument *inst; + SampleImporter *importers[10], *importer; + int i, j, count, result; + Sample *sample; + + if ((count = get_importers(sample_file, sizeof importers / sizeof importers[0], importers)) == 0) + return NULL; + inst = (Instrument *)safe_malloc(sizeof(Instrument)); + inst->type = INST_PCM; + inst->instname = NULL; + inst->samples = 0; + inst->sample = NULL; + i = 0; + importer = NULL; + while ((i = get_next_importer(sample_file, i, count, importers)) < count) + { + if ((result = (this->*(importers[i]->load))(sample_file, inst)) == 0) + { + importer = importers[i]; + break; + } + if (result == -1) /* importer told to give up test */ + break; + j = inst->samples; + while(j > 0) + { + if (inst->sample[--j].data_alloced) + free(inst->sample[j].data); + } + inst->samples = 0; + free(inst->sample); + inst->sample = NULL; + i++; /* try next */ + } + if (importer == NULL) + { + free_instrument(inst); + return NULL; + } + /* post-process */ + if (inst->instname == NULL) + { + const char *name; + + name = strrchr(sample_file, '/'); + if (name == NULL) + name = sample_file - 1; + inst->instname = strdup(name + 1); + } + for(i = 0; i < inst->samples; i++) + { + sample = &inst->sample[i]; + /* If necessary do some anti-aliasing filtering */ + if (antialiasing_allowed) + antialiasing((int16_t *)sample->data, + sample->data_length >> FRACTION_BITS, + sample->sample_rate, playback_rate); + /* resample it if possible */ + if (sample->note_to_use && !(sample->modes & MODES_LOOPING)) + pre_resample(sample); + } + return inst; +} + +#define ADD_IMPORTER importer->added = 1; \ + importers[count++] = importer; + +/* returns number of importers which may be suitable for the file */ +int Instruments::get_importers(const char *sample_file, int limit, SampleImporter **importers) +{ + static SampleImporter sample_importers[] = { + {"wav", &Instruments::import_wave_discriminant, &Instruments::import_wave_load}, + {"aiff", &Instruments::import_aiff_discriminant, &Instruments::import_aiff_load}, + {NULL, NULL, NULL}, + }; + + SampleImporter *importer; + int count; + const char *extension; + + count = 0; + importer = sample_importers; + while(importer->load != NULL && count < limit) + { + importer->added = 0; + importer++; + } + /* first, extension matched importers */ + extension = strrchr(sample_file, '/'); + if (extension != NULL && (extension = strrchr(extension, '.')) != NULL) + { + extension++; + /* ones which have discriminant first */ + importer = sample_importers; + while(importer->load != NULL && count < limit) + { + if (!importer->added && importer->extension != NULL && importer->discriminant != NULL + && strcasecmp(extension, importer->extension) == 0) + {ADD_IMPORTER} + importer++; + } + /* then ones which don't have discriminant */ + importer = sample_importers; + while(importer->load != NULL && count < limit) + { + if (!importer->added && importer->extension != NULL + && importer->discriminant == NULL + && strcasecmp(extension, importer->extension) == 0) + {ADD_IMPORTER} + importer++; + } + } + /* lastly, ones which has discriminant */ + importer = sample_importers; + while(importer->load != NULL && count < limit) + { + if (!importer->added && importer->discriminant != NULL) + {ADD_IMPORTER} + importer++; + } + return count; +} + +/* returns importer index for the file */ +/* returns count if no importer available */ +int Instruments::get_next_importer(char *sample_file, int start, int count, SampleImporter **importers) +{ + int i; + + for(i = start; i < count; i++) + { + if (importers[i]->discriminant != NULL) + { + if ((this->*(importers[i]->discriminant))(sample_file) != 0) + continue; + } + return i; + } + return i; +} + +/*************** Sample Importers ***************/ + +#define MAX_SAMPLE_CHANNELS 16 + +/* from instrum.c */ +#define READ_CHAR(thing) \ + if (1 != tf_read(&tmpchar, 1, tf)) goto fail; \ + thing = tmpchar; + +#define READ_SHORT_LE(thing) \ + if (2 != tf_read(&tmpshort, 2, tf)) goto fail; \ + thing = LE_SHORT(tmpshort); +#define READ_LONG_LE(thing) \ + if (4 != tf_read(&tmplong, 4, tf)) goto fail; \ + thing = LE_LONG(tmplong); +#define READ_SHORT_BE(thing) \ + if (2 != tf_read(&tmpshort, 2, tf)) goto fail; \ + thing = BE_SHORT(tmpshort); +#define READ_LONG_BE(thing) \ + if (4 != tf_read(&tmplong, 4, tf)) goto fail; \ + thing = BE_LONG(tmplong); + +const uint8_t pan_mono[] = {64}; /* center */ +const uint8_t pan_stereo[] = {1,127}; /* left,right */ +const uint8_t pan_3ch[] = {1,127,64}; /* left,right,center*/ +/* pannings below are set by guess */ +/*const uint8_t pan_quad[] = {1,127,16,112};*/ /* front-left?,front-right?,rear-left?,rear-right? */ +const uint8_t pan_4ch[] = {1,64,127,64}; /* left,center,right,surround?*/ +const uint8_t pan_6ch[] = {1,32,64,127,95,64}; /* left,left-center?,center,right,right-center?,surround? */ +const uint8_t *const gen_pan_list[6] = { + pan_mono, pan_stereo, pan_3ch, + pan_4ch, NULL, pan_6ch, +}; + +typedef struct { + uint8_t baseNote; + int8_t detune; + uint8_t lowNote; + uint8_t highNote; + uint8_t lowVelocity; + uint8_t highVelocity; + int16_t gain; +} GeneralInstrumentInfo; + +static void apply_GeneralInstrumentInfo(int samples, Sample *sample, const GeneralInstrumentInfo *info); + +/* read_sample_data() flags */ +#define SAMPLE_BIG_ENDIAN (1 << 0) +#define SAMPLE_8BIT_UNSIGNED (1 << 1) + +static int read_sample_data(int32_t flags, timidity_file *tf, int bits, int samples, int frames, sample_t **sdata); + +/*************** WAV Importer ***************/ + +typedef struct { + int16_t wFormatTag; + uint16_t wChannels; + uint32_t dwSamplesPerSec; + uint32_t dwAvgBytesPerSec; + uint16_t wBlockAlign; + uint16_t wBitsPerSample; +} WAVFormatChunk; + +typedef struct { + int32_t dwSamplePeriod; + int32_t dwMIDIUnityNote; + uint32_t dwMIDIPitchFraction; + int hasLoop, loopType; + int32_t loop_dwStart, loop_dwEnd, loop_dwFraction; +} WAVSamplerChunk; + +static int read_WAVFormatChunk(timidity_file *tf, WAVFormatChunk *fmt, int psize); +static int read_WAVSamplerChunk(timidity_file *tf, WAVSamplerChunk *smpl, int psize); +static int read_WAVInstrumentChunk(timidity_file *tf, GeneralInstrumentInfo *inst, int psize); + +int Instruments::import_wave_discriminant(char *sample_file) +{ + timidity_file *tf; + char buf[12]; + + if ((tf = open_file(sample_file, sfreader)) == NULL) + return 1; + if (tf_read(buf, 12, tf) != 12 + || memcmp(&buf[0], "RIFF", 4) != 0 || memcmp(&buf[8], "WAVE", 4) != 0) + { + tf_close(tf); + return 1; + } + tf_close(tf); + return 0; +} + +#define WAVE_CHUNKFLAG_SAMPLER (1 << 0) +#define WAVE_CHUNKFLAG_INSTRUMENT (1 << 1) + +int Instruments::import_wave_load(char *sample_file, Instrument *inst) +{ + timidity_file *tf; + union { + int32_t i[3]; + char c[12]; + } xbuf; + char *buf = xbuf.c; + int state; /* initial > fmt_read > data_read */ + int i, chunk_size, type_index, type_size, samples = 0; + int32_t chunk_flags; + Sample *sample; + WAVFormatChunk format = {0,}; + WAVSamplerChunk samplerc = {0,}; + GeneralInstrumentInfo instc; + + if ((tf = open_file(sample_file, sfreader)) == NULL) + return 1; + if (tf_read(buf, 12, tf) != 12 + || memcmp(&buf[0], "RIFF", 4) != 0 || memcmp(&buf[8], "WAVE", 4) != 0) + { + tf_close(tf); + return 1; + } + //printMessage(CMSG_INFO,VERB_NOISY,"Loading WAV: %s", sample_file); + state = chunk_flags = 0; + type_index = 4, type_size = 8; + for(;;) { + if (tf_read(&buf[type_index], type_size, tf) != type_size) + break; + chunk_size = LE_LONG(xbuf.i[2]); + if (memcmp(&buf[4 + 0], "fmt ", 4) == 0) + { + if (state != 0 /* only one format chunk is required */ + || chunk_size < 0x10) /* too small */ + break; + if (!read_WAVFormatChunk(tf, &format, chunk_size)) + break; + if (format.wChannels < 1 /* invalid range */ + || format.wChannels > MAX_SAMPLE_CHANNELS + || format.wFormatTag != 1 /* compressed */ + || format.wBitsPerSample & 0x7 /* padding not supported */ + || format.wBitsPerSample > 16) /* more than 16-bit is not supported */ + break; + state++; + } + else if (memcmp(&buf[4 + 0], "data", 4) == 0) + { + int frames; + sample_t *sdata[MAX_SAMPLE_CHANNELS]; + + if (state != 1) + break; + frames = chunk_size / format.wBlockAlign; + inst->samples = samples = format.wChannels; + inst->sample = (Sample *)safe_malloc(sizeof(Sample) * samples); + //printMessage(CMSG_INFO,VERB_NOISY,"Format: %d-bits %dHz %dch, %d frames", format.wBitsPerSample, format.dwSamplesPerSec, samples, frames); + initialize_sample(inst, frames, format.wBitsPerSample, format.dwSamplesPerSec); + /* load waveform data */ + for(i = 0; i < samples; i++) + { + inst->sample[i].data = sdata[i] = (sample_t *)safe_malloc(sizeof(sample_t) * frames); + inst->sample[i].data_alloced = 1; + } + if (!read_sample_data(SAMPLE_8BIT_UNSIGNED, tf, format.wBitsPerSample, samples, frames, sdata)) + break; + state++; + } + else if (!(chunk_flags & WAVE_CHUNKFLAG_SAMPLER) && memcmp(&buf[4 + 0], "smpl", 4) == 0) + { + if (!read_WAVSamplerChunk(tf, &samplerc, chunk_size)) + break; + chunk_flags |= WAVE_CHUNKFLAG_SAMPLER; + } + else if (!(chunk_flags & WAVE_CHUNKFLAG_INSTRUMENT) && memcmp(&buf[4 + 0], "inst", 4) == 0) + { + if (!read_WAVInstrumentChunk(tf, &instc, chunk_size)) + break; + chunk_flags |= WAVE_CHUNKFLAG_INSTRUMENT; + } + else if (tf_seek(tf, chunk_size, SEEK_CUR) == -1) + break; + type_index = 4 - (chunk_size & 1); + type_size = 8 + (chunk_size & 1); + } + tf_close(tf); + if (chunk_flags & WAVE_CHUNKFLAG_SAMPLER) + { + uint8_t modes; + int32_t sample_rate, root_freq; + uint32_t loopStart = 0, loopEnd = 0; + + sample_rate = samplerc.dwSamplePeriod == 0 ? 0 : 1000000000 / samplerc.dwSamplePeriod; + root_freq = freq_table[samplerc.dwMIDIUnityNote]; + if (samplerc.dwMIDIPitchFraction != 0 + && samplerc.dwMIDIUnityNote != 127) /* no table data */ + { + int32_t diff; + + diff = freq_table[samplerc.dwMIDIUnityNote + 1] - root_freq; + root_freq += int32_t((double)samplerc.dwMIDIPitchFraction * diff / 0xFFFFFFFF); + } + if (samplerc.hasLoop) + { + const uint8_t loopModes[] = {MODES_LOOPING, MODES_LOOPING | MODES_PINGPONG, MODES_LOOPING | MODES_REVERSE}; + + modes = loopModes[samplerc.loopType]; + loopStart = samplerc.loop_dwStart << FRACTION_BITS; + loopEnd = samplerc.loop_dwEnd << FRACTION_BITS; + } + else + modes = 0; + for(i = 0; i < samples; i++) + { + sample = &inst->sample[i]; + if (sample_rate != 0) + sample->sample_rate = sample_rate; + sample->root_freq = root_freq; + if (modes != 0) + { + sample->loop_start = loopStart; + sample->loop_end = loopEnd; + } + sample->modes |= modes; + } + } + if (chunk_flags & WAVE_CHUNKFLAG_INSTRUMENT) + apply_GeneralInstrumentInfo(samples, inst->sample, &instc); + return (state != 2); +} + +static int read_WAVFormatChunk(timidity_file *tf, WAVFormatChunk *fmt, int csize) +{ + int32_t tmplong; + int16_t tmpshort; + + READ_SHORT_LE(fmt->wFormatTag); + READ_SHORT_LE(fmt->wChannels); + READ_LONG_LE(fmt->dwSamplesPerSec); + READ_LONG_LE(fmt->dwAvgBytesPerSec); + READ_SHORT_LE(fmt->wBlockAlign); + READ_SHORT_LE(fmt->wBitsPerSample); + if (tf_seek(tf, csize - 0x10, SEEK_CUR) == -1) + goto fail; + return 1; + fail: + printMessage(CMSG_WARNING, VERB_VERBOSE, "Unable to read format chunk"); + return 0; +} + +static int read_WAVSamplerChunk(timidity_file *tf, WAVSamplerChunk *smpl, int psize) +{ + int32_t tmplong; + int i, loopCount, cbSamplerData, dwPlayCount; + unsigned int loopType; + + smpl->hasLoop = 0; + /* skip dwManufacturer, dwProduct */ + if (tf_seek(tf, 4 + 4, SEEK_CUR) == -1) + goto fail; + READ_LONG_LE(smpl->dwSamplePeriod); + READ_LONG_LE(smpl->dwMIDIUnityNote); + READ_LONG_LE(smpl->dwMIDIPitchFraction); + /* skip dwSMPTEFormat, dwSMPTEOffset */ + if (tf_seek(tf, 4 + 4, SEEK_CUR) == -1) + goto fail; + READ_LONG_LE(loopCount); + READ_LONG_LE(cbSamplerData); + psize -= 4 * 9 + loopCount * 4 * 6; + for(i = 0; i < loopCount; i++) + { + /* skip dwIdentifier */ + if (tf_seek(tf, 4, SEEK_CUR) == -1) + goto fail; + READ_LONG_LE(loopType); /* dwType */ + if (!smpl->hasLoop && loopType <= 2) + { + smpl->loopType = loopType; + READ_LONG_LE(smpl->loop_dwStart); + READ_LONG_LE(smpl->loop_dwEnd); + READ_LONG_LE(smpl->loop_dwFraction); + READ_LONG_LE(dwPlayCount); + if (dwPlayCount == 0) /* infinite loop */ + smpl->hasLoop = 1; + } + else + { + if (tf_seek(tf, 4 * 4, SEEK_CUR) == -1) + goto fail; + } + } + if (psize != cbSamplerData) + printMessage(CMSG_WARNING, VERB_NOISY, "Bad sampler chunk length"); + if (tf_seek(tf, psize, SEEK_CUR) == -1) + goto fail; + //printMessage(CMSG_INFO,VERB_NOISY,"Sampler: %dns/frame, note=%d, loops=%d", smpl->dwSamplePeriod, smpl->dwMIDIUnityNote, loopCount); + return 1; + fail: + printMessage(CMSG_WARNING, VERB_VERBOSE, "Unable to read sampler chunk"); + return 0; +} + +static int read_WAVInstrumentChunk(timidity_file *tf, GeneralInstrumentInfo *inst, int psize) +{ + int8_t tmpchar; + + if (psize != 7) + goto fail; + READ_CHAR(inst->baseNote); + READ_CHAR(inst->detune); + READ_CHAR(inst->gain); + READ_CHAR(inst->lowNote); + READ_CHAR(inst->highNote); + READ_CHAR(inst->lowVelocity); + READ_CHAR(inst->highVelocity); + printMessage(CMSG_INFO, VERB_VERBOSE, "Instrument: note=%d (%d-%d), gain=%ddb, velocity=%d-%d", + inst->baseNote, inst->lowNote, inst->highNote, inst->gain, + inst->lowVelocity, inst->highVelocity); + return 1; + fail: + printMessage(CMSG_WARNING, VERB_VERBOSE, "Unable to read instrument chunk"); + return 0; +} + +/*************** AIFF importer ***************/ + +struct AIFFCommonChunk { + uint16_t numChannels; + uint32_t numSampleFrames; + uint16_t sampleSize; + double sampleRate; +}; + +struct AIFFSoundDataChunk { + uint32_t position; + Instrument *inst; + AIFFCommonChunk *common; +}; + +typedef struct { + uint16_t mode; + int16_t beginID, endID; +} AIFFLoopInfo; + +typedef struct { + int16_t id; + uint32_t position; +} AIFFMarkerData; + +static int read_AIFFInstumentChunk(timidity_file *tf, GeneralInstrumentInfo *inst, AIFFLoopInfo *loop, int csize); +static int read_AIFFMarkerChunk(timidity_file *tf, AIFFMarkerData **markers, int csize); +static int AIFFGetMarkerPosition(int16_t id, const AIFFMarkerData *markers, uint32_t *position); + +int Instruments::import_aiff_discriminant(char *sample_file) +{ + timidity_file *tf; + char buf[12]; + + if ((tf = open_file(sample_file, sfreader)) == NULL) + return 1; + if (tf_read(buf, 12, tf) != 12 + || memcmp(&buf[0], "FORM", 4) != 0 || memcmp(&buf[8], "AIF", 3) != 0 + || (buf[8 + 3] != 'F' && buf[8 + 3] != 'C')) + { + tf_close(tf); + return 1; + } + tf_close(tf); + return 0; +} + +#define AIFF_CHUNKFLAG_COMMON (1 << 0) +#define AIFF_CHUNKFLAG_SOUND (1 << 1) +#define AIFF_CHUNKFLAG_INSTRUMENT (1 << 2) +#define AIFF_CHUNKFLAG_MARKER (1 << 3) +#define AIFF_CHUNKFLAG_SOUNDREAD (1 << 29) +#define AIFF_CHUNKFLAG_READERR (1 << 30) +#define AIFF_CHUNKFLAG_DUPCHUNK (1 << 31) +#define AIFF_CHUNKFLAG_REQUIRED (AIFF_CHUNKFLAG_COMMON | AIFF_CHUNKFLAG_SOUND) +#define AIFF_CHUNKFLAG_FAILED (AIFF_CHUNKFLAG_READERR | AIFF_CHUNKFLAG_DUPCHUNK) + +int Instruments::import_aiff_load(char *sample_file, Instrument *inst) +{ + timidity_file *tf; + union { + int32_t i[3]; + char c[12]; + } xbuf; + char *buf = xbuf.c; + int chunk_size, type_index, type_size; + int compressed; + int32_t chunk_flags; + AIFFCommonChunk common; + AIFFSoundDataChunk sound; + GeneralInstrumentInfo inst_info; + AIFFLoopInfo loop_info = {0,0,0}; + AIFFMarkerData *marker_data; + + if ((tf = open_file(sample_file, sfreader)) == NULL) + return 1; + if (tf_read(buf, 12, tf) != 12 + || memcmp(&buf[0], "FORM", 4) != 0 || memcmp(&buf[8], "AIF", 3) != 0 + || (buf[8 + 3] != 'F' && buf[8 + 3] != 'C')) + { + tf_close(tf); + return 1; + } + compressed = buf[8 + 3] == 'C'; + //printMessage(CMSG_INFO,VERB_NOISY,"Loading AIFF: %s", sample_file); + type_index = 4, type_size = 8; + chunk_flags = 0; + sound.inst = inst; + sound.common = &common; + marker_data = NULL; + for(;;) { + if (tf_read(&buf[type_index], type_size, tf) != type_size) + break; + chunk_size = BE_LONG(xbuf.i[2]); + if (memcmp(&buf[4 + 0], "COMM", 4) == 0) + { + if (chunk_flags & AIFF_CHUNKFLAG_COMMON) + { + chunk_flags |= AIFF_CHUNKFLAG_DUPCHUNK; + break; + } + if (chunk_size < 18) /* too small */ + break; + if (!read_AIFFCommonChunk(tf, &common, chunk_size, compressed)) + break; + chunk_flags |= AIFF_CHUNKFLAG_COMMON; + } + else if (memcmp(&buf[4 + 0], "SSND", 4) == 0) + { + if (chunk_flags & AIFF_CHUNKFLAG_SOUND) + { + chunk_flags |= AIFF_CHUNKFLAG_DUPCHUNK; + break; + } + if (chunk_flags & AIFF_CHUNKFLAG_COMMON) + { + if (!read_AIFFSoundDataChunk(tf, &sound, chunk_size, 0)) + break; + chunk_flags |= AIFF_CHUNKFLAG_SOUNDREAD; + } + else if (!read_AIFFSoundDataChunk(tf, &sound, chunk_size, 1)) + break; + chunk_flags |= AIFF_CHUNKFLAG_SOUND; + } + else if (memcmp(&buf[4 + 0], "INST", 4) == 0) + { + if (chunk_flags & AIFF_CHUNKFLAG_INSTRUMENT) + { + chunk_flags |= AIFF_CHUNKFLAG_DUPCHUNK; + break; + } + else if (!read_AIFFInstumentChunk(tf, &inst_info, &loop_info, chunk_size)) + break; + chunk_flags |= AIFF_CHUNKFLAG_INSTRUMENT; + } + else if (memcmp(&buf[4 + 0], "MARK", 4) == 0) + { + if (chunk_flags & AIFF_CHUNKFLAG_MARKER) + { + chunk_flags |= AIFF_CHUNKFLAG_DUPCHUNK; + break; + } + else if (chunk_size < 2 || !read_AIFFMarkerChunk(tf, &marker_data, chunk_size)) + break; + chunk_flags |= AIFF_CHUNKFLAG_MARKER; + } + else if (inst->instname == NULL && memcmp(&buf[4 + 0], "NAME", 4) == 0) + { + inst->instname = (char*)malloc(chunk_size + 1); + if (tf_read(inst->instname, chunk_size, tf) != chunk_size) + { + chunk_flags |= AIFF_CHUNKFLAG_READERR; + break; + } + inst->instname[chunk_size] = '\0'; + } + else if (tf_seek(tf, chunk_size, SEEK_CUR) == -1) + break; + /* no need to check format version chunk */ + type_index = 4 - (chunk_size & 1); + type_size = 8 + (chunk_size & 1); + } + if (chunk_flags & AIFF_CHUNKFLAG_FAILED + || (chunk_flags & AIFF_CHUNKFLAG_REQUIRED) != AIFF_CHUNKFLAG_REQUIRED) + { + if (marker_data != NULL) + free(marker_data); + tf_close(tf); + return -1; + } + if (!(chunk_flags & AIFF_CHUNKFLAG_SOUNDREAD)) + { + if (!read_AIFFSoundDataChunk(tf, &sound, 0, 2)) + { + if (marker_data != NULL) + free(marker_data); + tf_close(tf); + return 1; + } + } + if (chunk_flags & AIFF_CHUNKFLAG_INSTRUMENT) + { + apply_GeneralInstrumentInfo(inst->samples, inst->sample, &inst_info); + if ((loop_info.mode == 1 || loop_info.mode == 2) + && chunk_flags & AIFF_CHUNKFLAG_MARKER && marker_data != NULL) + { + Sample *sample; + int i; + uint32_t loopStart, loopEnd; + uint8_t loopMode; + + if (AIFFGetMarkerPosition(loop_info.beginID, marker_data, &loopStart) + && AIFFGetMarkerPosition(loop_info.endID, marker_data, &loopEnd)) + { + loopMode = (loop_info.mode == 1) ? MODES_LOOPING : (MODES_LOOPING | MODES_PINGPONG); + loopStart <<= FRACTION_BITS; + loopEnd <<= FRACTION_BITS; + if (loopStart <= loopEnd) + { + for(i = 0; i < inst->samples; i++) + { + sample = &inst->sample[i]; + sample->loop_start = loopStart; + sample->loop_end = loopEnd; + sample->modes |= loopMode; + } + } + } + } + } + if (marker_data != NULL) + free(marker_data); + tf_close(tf); + return 0; +} + + int Instruments::read_AIFFCommonChunk(timidity_file *tf, AIFFCommonChunk *comm, int csize, int compressed) +{ + int32_t tmplong; + int16_t tmpshort; + int8_t tmpchar; + char sampleRate[10]; + uint32_t compressionType; + + READ_SHORT_BE(comm->numChannels); + READ_LONG_BE(comm->numSampleFrames); + READ_SHORT_BE(comm->sampleSize); + if (tf_read(sampleRate, 10, tf) != 10) + goto fail; + comm->sampleRate = ConvertFromIeeeExtended(sampleRate); + csize -= 8 + 10; + //printMessage(CMSG_INFO,VERB_NOISY,"Format: %d-bits %dHz %dch, %d frames", comm->sampleSize, (int)comm->sampleRate, comm->numChannels, comm->numSampleFrames); + if (compressed) + { + READ_LONG_BE(compressionType); + if (compressionType != (uint32_t)BE_LONG(0x4E4F4E45) /* NONE */) + { + char compressionName[256]; + uint8_t compressionNameLength; + + READ_CHAR(compressionNameLength); + if (tf_read(compressionName, compressionNameLength, tf) != compressionNameLength) + goto fail; + compressionName[compressionNameLength] = '\0'; + printMessage(CMSG_WARNING, VERB_VERBOSE, "AIFF-C unknown compression type: %s", compressionName); + goto fail; + } + csize -= 4; + /* ignore compressionName and its padding */ + } + if (tf_seek(tf, csize, SEEK_CUR) == -1) + goto fail; + return 1; + fail: + printMessage(CMSG_WARNING, VERB_VERBOSE, "Unable to read common chunk"); + return 0; +} + +int Instruments::read_AIFFSoundDataChunk(timidity_file *tf, AIFFSoundDataChunk *sound, int csize, int mode) +{ + int32_t tmplong; + uint32_t offset, blockSize; + + if (mode == 0 || mode == 1) + { + READ_LONG_BE(offset); + READ_LONG_BE(blockSize); + if (blockSize != 0) /* not implemented */ + goto fail; + if (mode == 0) /* read both information and data */ + return read_AIFFSoundData(tf, sound->inst, sound->common); + /* read information only */ + auto pos = tf_tell(tf); + if (pos == -1) + goto fail; + sound->position = pos + offset; + csize -= 8; + if (tf_seek(tf, csize, SEEK_CUR) == -1) + goto fail; + return 1; + } + else if (mode == 2) /* read data using information previously read */ + { + if (tf_seek(tf, sound->position, SEEK_SET) == -1) + goto fail; + return read_AIFFSoundData(tf, sound->inst, sound->common); + } + fail: + printMessage(CMSG_WARNING, VERB_VERBOSE, "Unable to read sound data chunk"); + return 0; +} + +int Instruments::read_AIFFSoundData(timidity_file *tf, Instrument *inst, AIFFCommonChunk *common) +{ + int i, samples; + Sample *sample; + sample_t *sdata[MAX_SAMPLE_CHANNELS]; + + if ((samples = common->numChannels) > MAX_SAMPLE_CHANNELS) + goto fail; + inst->samples = samples; + inst->sample = sample = (Sample *)safe_malloc(sizeof(Sample) * samples); + initialize_sample(inst, common->numSampleFrames, common->sampleSize, (int)common->sampleRate); + /* load samples */ + for(i = 0; i < samples; i++) + { + sample[i].data = sdata[i] = (sample_t *)safe_malloc(sizeof(sample_t) * common->numSampleFrames); + sample[i].data_alloced = 1; + } + if (!read_sample_data(SAMPLE_BIG_ENDIAN, tf, common->sampleSize, samples, common->numSampleFrames, sdata)) + goto fail; + return 1; + fail: + printMessage(CMSG_WARNING, VERB_VERBOSE, "Unable to read sound data"); + return 0; +} + +static int read_AIFFInstumentChunk(timidity_file *tf, GeneralInstrumentInfo *inst, AIFFLoopInfo *loop, int csize) +{ + int8_t tmpchar; + int16_t tmpshort; + + if (csize != 20) + { + printMessage(CMSG_WARNING, VERB_VERBOSE, "Bad instrument chunk length"); + if (tf_seek(tf, csize, SEEK_CUR) == -1) + goto fail; + return 1; + } + READ_CHAR(inst->baseNote); + READ_CHAR(inst->detune); + READ_CHAR(inst->lowNote); + READ_CHAR(inst->highNote); + READ_CHAR(inst->lowVelocity); + READ_CHAR(inst->highVelocity); + READ_SHORT_BE(inst->gain); + READ_SHORT_BE(loop->mode); /* sustain loop */ + READ_SHORT_BE(loop->beginID); + READ_SHORT_BE(loop->endID); + if (tf_seek(tf, 2 + 2 + 2, SEEK_CUR) == -1) /* release loop */ + goto fail; + printMessage(CMSG_INFO, VERB_VERBOSE, "Instrument: note=%d (%d-%d), gain=%ddb, velocity=%d-%d", + inst->baseNote, inst->lowNote, inst->highNote, inst->gain, + inst->lowVelocity, inst->highVelocity); + return 1; + fail: + printMessage(CMSG_WARNING, VERB_VERBOSE, "Unable to read instrument chunk"); + return 0; +} + +static int read_AIFFMarkerChunk(timidity_file *tf, AIFFMarkerData **markers, int csize) +{ + int32_t tmplong; + int16_t tmpshort; + int16_t markerCount; + int i, dest; + AIFFMarkerData *m; + + m = NULL; + READ_SHORT_BE(markerCount) + if (csize != 2 + markerCount * (2 + 4)) + { + printMessage(CMSG_WARNING, VERB_VERBOSE, "Bad marker chunk length"); + if (tf_seek(tf, csize, SEEK_CUR) == -1) + goto fail; + return 1; + } + if ((m = (AIFFMarkerData*)malloc(sizeof(AIFFMarkerData) * (markerCount + 1))) == NULL) + goto fail; + for(i = dest = 0; i < markerCount; i++) + { + READ_SHORT_BE(m[dest].id); + READ_LONG_BE(m[dest].position); + if (m[dest].id > 0) + dest++; + } + m[dest].id = 0; + *markers = m; + return 1; + fail: + if (m != NULL) + free(m); + printMessage(CMSG_WARNING, VERB_VERBOSE, "Unable to read marker chunk"); + return 0; +} + +static int AIFFGetMarkerPosition(int16_t id, const AIFFMarkerData *markers, uint32_t *position) +{ + const AIFFMarkerData *marker; + + marker = markers; + while(marker->id != 0) + { + if (marker->id == id) + { + *position = marker->position; + return 1; + } + marker++; + } + return 0; +} + +/******************************/ + +#define WAVE_BUF_SIZE (1 << 11) /* should be power of 2 */ +#define READ_WAVE_SAMPLE(dest, b, s) \ + if (tf_read(dest, (b) * (s), tf) != (b) * (s)) \ + goto fail +#define READ_WAVE_FRAME(dest, b, f) \ + READ_WAVE_SAMPLE(dest, b, (f) * channels) +#define BITS_S8_TO_16(n) ((uint16_t)((n) << 8) | ((n) ^ 0x80)) +#define BITS_U8_TO_16(n) ((uint16_t)(((n) ^ 0x80) << 8) | (n)) + +#define BLOCK_READ_BEGIN(stype, sbyte, fch) { /* sbyte may be sizeof(stype) */ \ + stype data[WAVE_BUF_SIZE / sizeof(stype)]; \ + int j; \ + for(block_frame_count = (sizeof data / sbyte / fch); block_frame_count != 0; block_frame_count >>= 1) { \ + while(i <= frames - block_frame_count) { \ + READ_WAVE_FRAME(data, sbyte, block_frame_count); \ + for(j = 0; j < (block_frame_count * (fch)); i++) +#define BLOCK_READ_END } } } + +static int read_sample_data(int32_t flags, timidity_file *tf, int bits, int channels, int frames, sample_t **sdata) +{ + int i, block_frame_count; + + i = 0; + if (bits == 16) + { + if (channels == 1) + { + READ_WAVE_SAMPLE(sdata[0], 2, frames); + if (flags & SAMPLE_BIG_ENDIAN) { + #ifndef _BIG_ENDIAN_ + for(i = 0; i < frames; i++) + sdata[0][i] = BE_SHORT(sdata[0][i]); + #endif + } else { + #ifdef _BIG_ENDIAN_ + for(i = 0; i < frames; i++) + sdata[0][i] = LE_SHORT(sdata[0][i]); + #endif + } + } else { + if (flags & SAMPLE_BIG_ENDIAN) { + BLOCK_READ_BEGIN(uint16_t, 2, channels) + { + int c; + for(c = 0; c < channels; c++, j++) + sdata[c][i] = BE_SHORT(data[j]); + } + BLOCK_READ_END + } else { + BLOCK_READ_BEGIN(uint16_t, 2, channels) + { + int c; + for(c = 0; c < channels; c++, j++) + sdata[c][i] = LE_SHORT(data[j]); + } + BLOCK_READ_END + } + } + } + else + { + if (channels == 1) + { + if (flags & SAMPLE_8BIT_UNSIGNED) { + BLOCK_READ_BEGIN(uint8_t, 1, 1) + { + sdata[0][i] = BITS_U8_TO_16(data[j]); j++; + } + BLOCK_READ_END + } else { + BLOCK_READ_BEGIN(uint8_t, 1, 1) + { + sdata[0][i] = BITS_S8_TO_16(data[j]); j++; + } + BLOCK_READ_END + } + } else { + if (flags & SAMPLE_8BIT_UNSIGNED) { + BLOCK_READ_BEGIN(uint8_t, 1, channels) + { + int c; + for(c = 0; c < channels; c++, j++) + sdata[c][i] = BITS_U8_TO_16(data[j]); + } + BLOCK_READ_END + } else { + BLOCK_READ_BEGIN(uint8_t, 1, channels) + { + int c; + for(c = 0; c < channels; c++, j++) + sdata[c][i] = BITS_S8_TO_16(data[j]); + } + BLOCK_READ_END + } + } + } + return 1; + fail: + printMessage(CMSG_WARNING, VERB_VERBOSE, "Unable to read sample data"); + return 0; +} + +/* from instrum.c */ +int32_t Instruments::convert_envelope_rate_s(uint8_t rate) +{ + int32_t r; + + r = 3 - ((rate >> 6) & 0x3); + r *= 3; + r = (int32_t)(rate & 0x3f) << r; /* 6.9 fixed point */ + + /* 15.15 fixed point. */ + return (((r * 44100) / playback_rate) * control_ratio) + << ((fast_decay) ? 10 : 9); +} + + +void Instruments::initialize_sample(Instrument *inst, int frames, int sample_bits, int sample_rate) +{ + int i, j, samples; + Sample *sample; + const uint8_t *panning; + + samples = inst->samples; + for(i = 0; i < samples; i++) + { + sample = &inst->sample[i]; + sample->data_alloced = 0; + sample->loop_start = 0; + sample->loop_end = sample->data_length = frames << FRACTION_BITS; + sample->sample_rate = sample_rate; + sample->low_freq = freq_table[0]; + sample->high_freq = freq_table[127]; + sample->root_freq = freq_table[60]; + sample->panning = 64; + sample->note_to_use = 0; + sample->volume = 1.0; + sample->modes = MODES_16BIT; + sample->low_vel = 0; + sample->high_vel = 127; + sample->tremolo_sweep_increment = + sample->tremolo_phase_increment = sample->tremolo_depth = + sample->vibrato_sweep_increment = sample->vibrato_control_ratio = sample->vibrato_depth = 0; + sample->cutoff_freq = sample->resonance = sample->tremolo_to_pitch = + sample->tremolo_to_fc = sample->modenv_to_pitch = sample->modenv_to_fc = + sample->vel_to_fc = sample->key_to_fc = sample->vel_to_resonance = 0; + sample->envelope_velf_bpo = sample->modenv_velf_bpo = + sample->vel_to_fc_threshold = 64; + sample->key_to_fc_bpo = 60; + sample->scale_freq = 60; + sample->scale_factor = 1024; + memset(sample->envelope_velf, 0, sizeof(sample->envelope_velf)); + memset(sample->envelope_keyf, 0, sizeof(sample->envelope_keyf)); + memset(sample->modenv_velf, 0, sizeof(sample->modenv_velf)); + memset(sample->modenv_keyf, 0, sizeof(sample->modenv_keyf)); + memset(sample->modenv_rate, 0, sizeof(sample->modenv_rate)); + memset(sample->modenv_offset, 0, sizeof(sample->modenv_offset)); + sample->envelope_delay = sample->modenv_delay = + sample->tremolo_delay = sample->vibrato_delay = 0; + sample->inst_type = INST_PCM; + sample->sample_type = SF_SAMPLETYPE_MONO; + sample->sf_sample_link = -1; + sample->sf_sample_index = 0; + } + if (samples <= 6 && (panning = gen_pan_list[samples - 1]) != NULL) + { + for(i = 0; i < samples; i++) + inst->sample[i].panning = panning[i]; + } + for(i = 0; i < 6; i++) + { + int32_t envelope_rate, envelope_offset; + + envelope_rate = convert_envelope_rate_s(63); /* wav2pat.c */ + envelope_offset = convert_envelope_offset(240); /* wav2pat.c */ + for(j = 0; j < samples; j++) + { + sample = &inst->sample[j]; + sample->envelope_rate[i] = envelope_rate; + sample->envelope_offset[i] = envelope_offset; + } + } +} + +static void apply_GeneralInstrumentInfo(int samples, Sample *sample, const GeneralInstrumentInfo *info) +{ + int32_t root_freq; + double gain; + int i; + + root_freq = freq_table[info->baseNote]; + if (info->detune < 0) + { + if (info->baseNote != 0) /* no table data */ + root_freq += (root_freq - freq_table[info->baseNote - 1]) * 50 / info->detune; + } + else if (info->detune > 0) + { + if (info->baseNote != 127) /* no table data */ + root_freq += (freq_table[info->baseNote + 1] - root_freq) * 50 / info->detune; + } + gain = pow(10, info->gain / 20.0); + for(i = 0; i < samples; i++) + { + sample[i].low_freq = freq_table[info->lowNote]; + sample[i].high_freq = freq_table[info->highNote]; + sample[i].root_freq = root_freq; + sample[i].volume *= gain; + sample[i].low_vel = info->lowVelocity; + sample[i].high_vel = info->highVelocity; + } +} + + +/* Copyright (C) 1989-1991 Ken Turkowski. + * + * All rights reserved. + * + * Warranty Information + * Even though I have reviewed this software, I make no warranty + * or representation, either express or implied, with respect to this + * software, its quality, accuracy, merchantability, or fitness for a + * particular purpose. As a result, this software is provided "as is," + * and you, its user, are assuming the entire risk as to its quality + * and accuracy. + * + * This code may be used and freely distributed as long as it includes + * this copyright notice and the above warranty information. + * + * Machine-independent I/O routines for IEEE floating-point numbers. + * + * NaN's and infinities are converted to HUGE_VAL or HUGE, which + * happens to be infinity on IEEE machines. Unfortunately, it is + * impossible to preserve NaN's in a machine-independent way. + * Infinities are, however, preserved on IEEE machines. + * + * These routines have been tested on the following machines: + * Apple Macintosh, MPW 3.1 C compiler + * Apple Macintosh, THINK C compiler + * Silicon Graphics IRIS, MIPS compiler + * Cray X/MP and Y/MP + * Digital Equipment VAX + * Sequent Balance (Multiprocesor 386) + * NeXT + * + * + * Implemented by Malcolm Slaney and Ken Turkowski. + * + * Malcolm Slaney contributions during 1988-1990 include big- and little- + * endian file I/O, conversion to and from Motorola's extended 80-bit + * floating-point format, and conversions to and from IEEE single- + * precision floating-point format. + * + * In 1991, Ken Turkowski implemented the conversions to and from + * IEEE double-precision format, added more precision to the extended + * conversions, and accommodated conversions involving +/- infinity, + * NaN's, and denormalized numbers. + */ + +/**************************************************************** + * Extended precision IEEE floating-point conversion routines. + * Extended is an 80-bit number as defined by Motorola, + * with a sign bit, 15 bits of exponent (offset 16383?), + * and a 64-bit mantissa, with no hidden bit. + ****************************************************************/ + +static double ConvertFromIeeeExtended(const char *bytes) +{ + double f; + int32_t expon; + uint32_t hiMant, loMant; + + expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); + hiMant = ((uint32_t)(bytes[2] & 0xFF) << 24) + | ((uint32_t)(bytes[3] & 0xFF) << 16) + | ((uint32_t)(bytes[4] & 0xFF) << 8) + | ((uint32_t)(bytes[5] & 0xFF)); + loMant = ((uint32_t)(bytes[6] & 0xFF) << 24) + | ((uint32_t)(bytes[7] & 0xFF) << 16) + | ((uint32_t)(bytes[8] & 0xFF) << 8) + | ((uint32_t)(bytes[9] & 0xFF)); + + if (expon == 0 && hiMant == 0 && loMant == 0) { + f = 0; + } + else { + if (expon == 0x7FFF) { /* Infinity or NaN */ + f = HUGE_VAL; + } + else { + expon -= 16383; + f = ldexp(hiMant, expon-=31); + f += ldexp(loMant, expon-=32); + } + } + + if (bytes[0] & 0x80) + return -f; + else + return f; +} +} diff --git a/thirdparty/timidityplus/sndfont.cpp b/thirdparty/timidityplus/sndfont.cpp new file mode 100644 index 000000000..2337f1718 --- /dev/null +++ b/thirdparty/timidityplus/sndfont.cpp @@ -0,0 +1,1511 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2005 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + This code from awesfx + Modified by Masanao Izumo + + ================================================================ + parsesf.c + parse SoundFont layers and convert it to AWE driver patch + + Copyright (C) 1996,1997 Takashi Iwai + ================================================================ +*/ + +#include +#include +#include +#include + +#include "timidity.h" +#include "common.h" +#include "tables.h" +#include "instrum.h" +#include "playmidi.h" +#include "filter.h" +#include "freq.h" +#include "resample.h" + +namespace TimidityPlus +{ + +#define SFMalloc(rec, count) new_segment(&(rec)->pool, count) +#define SFStrdup(rec, s) strdup_mblock(&(rec)->pool, s) + +/*---------------------------------------------------------------- + * compile flags + *----------------------------------------------------------------*/ + + +/* return value */ +#define AWE_RET_OK 0 /* successfully loaded */ +#define AWE_RET_ERR 1 /* some fatal error occurs */ +#define AWE_RET_SKIP 2 /* some fonts are skipped */ +#define AWE_RET_NOMEM 3 /* out or memory; not all fonts loaded */ +#define AWE_RET_NOT_FOUND 4 /* the file is not found */ + +/*---------------------------------------------------------------- + * local parameters + *----------------------------------------------------------------*/ + +struct SFPatchRec +{ + int preset, bank, keynote; /* -1 = matches all */ +}; + +struct SampleList +{ + Sample v; + SampleList *next; + int32_t start; + int32_t len; + int32_t cutoff_freq; + int16_t resonance; + int16_t root, tune; + char low, high; /* key note range */ + int8_t reverb_send, chorus_send; + + /* Depend on playback_rate */ + int32_t vibrato_freq; + int32_t attack; + int32_t hold; + int32_t sustain; + int32_t decay; + int32_t release; + + int32_t modattack; + int32_t modhold; + int32_t modsustain; + int32_t moddecay; + int32_t modrelease; + + int bank, keynote; /* for drum instruments */ +}; + +struct InstList { + SFPatchRec pat; + int pr_idx; + int samples; + int order; + SampleList *slist; + InstList *next; +}; + +struct SFExclude { + SFPatchRec pat; + SFExclude *next; +}; + +struct SFOrder { + SFPatchRec pat; + int order; + SFOrder *next; +}; + +#define INSTHASHSIZE 127 +#define INSTHASH(bank, preset, keynote) \ + ((int)(((unsigned)bank ^ (unsigned)preset ^ (unsigned)keynote) % INSTHASHSIZE)) + +struct SFInsts { + timidity_file *tf; + char *fname; + int8_t def_order, def_cutoff_allowed, def_resonance_allowed; + uint16_t version, minorversion; + int32_t samplepos, samplesize; + InstList *instlist[INSTHASHSIZE]; + char **inst_namebuf; + SFExclude *sfexclude; + SFOrder *sforder; + SFInsts *next; + double amptune; + MBlockList pool; +}; + +/*----------------------------------------------------------------*/ + +/* prototypes */ + +#define P_GLOBAL 1 +#define P_LAYER 2 +#define def_drum_inst 0 + + +/*----------------------------------------------------------------*/ + + +SFInsts *Instruments::find_soundfont(char *sf_file) +{ + SFInsts *sf; + + for(sf = sfrecs; sf != NULL; sf = sf->next) + if(sf->fname != NULL && strcmp(sf->fname, sf_file) == 0) + return sf; + return NULL; +} + +SFInsts *Instruments::new_soundfont(char *sf_file) +{ + SFInsts *sf, *prev; + + for(sf = sfrecs, prev = NULL; sf != NULL; prev = sf, sf = sf->next) + { + if(sf->fname == NULL) + { + /* remove the record from the chain to reuse */ + if (prev != NULL) + prev->next = sf->next; + else if (sfrecs == sf) + sfrecs = sf->next; + break; + } + } + if(sf == NULL) + sf = (SFInsts *)safe_malloc(sizeof(SFInsts)); + memset(sf, 0, sizeof(SFInsts)); + init_mblock(&sf->pool); + sf->fname = SFStrdup(sf, sf_file); + sf->def_order = DEFAULT_SOUNDFONT_ORDER; + sf->amptune = 1.0; + return sf; +} + +void Instruments::add_soundfont(char *sf_file, int sf_order, int sf_cutoff, int sf_resonance, int amp) +{ + SFInsts *sf; + + if((sf = find_soundfont(sf_file)) == NULL) + { + sf = new_soundfont(sf_file); + sf->next = sfrecs; + sfrecs = sf; + } + + if(sf_order >= 0) + sf->def_order = sf_order; + if(sf_cutoff >= 0) + sf->def_cutoff_allowed = sf_cutoff; + if(sf_resonance >= 0) + sf->def_resonance_allowed = sf_resonance; + if(amp >= 0) + sf->amptune = (double)amp * 0.01; + current_sfrec = sf; +} + +void Instruments::remove_soundfont(char *sf_file) +{ + SFInsts *sf; + + if((sf = find_soundfont(sf_file)) != NULL) + end_soundfont(sf); +} + +void Instruments::free_soundfonts() +{ + SFInsts *sf, *next; + + for (sf = sfrecs; sf != NULL; sf = next) { + if (sf->tf != nullptr) tf_close(sf->tf); + sf->tf = nullptr; + reuse_mblock(&sf->pool); + next = sf->next; + free(sf); + } +} + +char *Instruments::soundfont_preset_name(int bank, int preset, int keynote, + char **sndfile) +{ + SFInsts *rec; + if(sndfile != NULL) + *sndfile = NULL; + for(rec = sfrecs; rec != NULL; rec = rec->next) + if(rec->fname != NULL) + { + int addr; + InstList *ip; + + addr = INSTHASH(bank, preset, keynote); + for(ip = rec->instlist[addr]; ip; ip = ip->next) + if(ip->pat.bank == bank && ip->pat.preset == preset && + (keynote < 0 || keynote == ip->pat.keynote)) + break; + if(ip != NULL) + { + if(sndfile != NULL) + *sndfile = rec->fname; + return rec->inst_namebuf[ip->pr_idx]; + } + } + return NULL; +} + +void Instruments::init_sf(SFInsts *rec) +{ + SFInfo sfinfo; + int i; + + if ((rec->tf = open_file(rec->fname, sfreader)) == NULL) { + printMessage(CMSG_ERROR, VERB_NORMAL, + "Can't open soundfont file %s", rec->fname); + end_soundfont(rec); + return; + } + + // SoundFont spec, 7.2: ... contains a minimum of two records, one record for each preset and one for a terminal record + if(load_soundfont(&sfinfo, rec->tf) || sfinfo.npresets < 2) + { + end_soundfont(rec); + return; + } + + correct_samples(&sfinfo); + current_sfrec = rec; + for (i = 0; i < sfinfo.npresets - 1; i++) { + int bank = sfinfo.preset[i].bank; + int preset = sfinfo.preset[i].preset; + + if (bank == 128) + /* FIXME: why not allow exclusion of drumsets? */ + alloc_instrument_bank(1, preset); + else { + if (is_excluded(rec, bank, preset, -1)) + continue; + alloc_instrument_bank(0, bank); + } + load_font(&sfinfo, i); + } + + /* copy header info */ + rec->version = sfinfo.version; + rec->minorversion = sfinfo.minorversion; + rec->samplepos = sfinfo.samplepos; + rec->samplesize = sfinfo.samplesize; + rec->inst_namebuf = + (char **)SFMalloc(rec, sfinfo.npresets * sizeof(char *)); + for(i = 0; i < sfinfo.npresets; i++) + rec->inst_namebuf[i] = + (char *)SFStrdup(rec, sfinfo.preset[i].hdr.name); + free_soundfont(&sfinfo); + + if (opt_sf_close_each_file) { + tf_close(rec->tf); + rec->tf = NULL; + } +} + +void Instruments::init_load_soundfont(void) +{ + SFInsts *rec; + for(rec = sfrecs; rec != NULL; rec = rec->next) + if(rec->fname != NULL) + init_sf(rec); +} + +void Instruments::end_soundfont(SFInsts *rec) +{ + if (rec->tf) { + tf_close(rec->tf); + rec->tf = NULL; + } + + rec->fname = NULL; + rec->inst_namebuf = NULL; + rec->sfexclude = NULL; + rec->sforder = NULL; + reuse_mblock(&rec->pool); +} + +Instrument *Instruments::extract_soundfont(char *sf_file, int bank, int preset,int keynote) +{ + SFInsts *sf; + + if((sf = find_soundfont(sf_file)) != NULL) + return try_load_soundfont(sf, -1, bank, preset, keynote); + sf = new_soundfont(sf_file); + sf->next = sfrecs; + sf->def_order = 2; + sfrecs = sf; + init_sf(sf); + return try_load_soundfont(sf, -1, bank, preset, keynote); +} + +/*---------------------------------------------------------------- + * get converted instrument info and load the wave data from file + *----------------------------------------------------------------*/ + +Instrument *Instruments::try_load_soundfont(SFInsts *rec, int order, int bank,int preset, int keynote) +{ + InstList *ip; + Instrument *inst = NULL; + int addr; + + if (rec->tf == NULL) { + if (rec->fname == NULL) + return NULL; + if ((rec->tf = open_file(rec->fname, sfreader)) == NULL) + { + printMessage(CMSG_ERROR, VERB_NORMAL, + "Can't open soundfont file %s", rec->fname); + end_soundfont(rec); + return NULL; + } + } + + addr = INSTHASH(bank, preset, keynote); + for (ip = rec->instlist[addr]; ip; ip = ip->next) { + if (ip->pat.bank == bank && ip->pat.preset == preset && + (keynote < 0 || ip->pat.keynote == keynote) && + (order < 0 || ip->order == order)) + break; + } + + if (ip && ip->samples) + inst = load_from_file(rec, ip); + + if (opt_sf_close_each_file) { + tf_close(rec->tf); + rec->tf = NULL; + } + + return inst; +} + +Instrument *Instruments::load_soundfont_inst(int order, int bank, int preset, int keynote) +{ + SFInsts *rec; + Instrument *ip; + /* + * Search through all ordered soundfonts + */ + int o = order; + + for(rec = sfrecs; rec != NULL; rec = rec->next) + { + if(rec->fname != NULL) + { + ip = try_load_soundfont(rec, o, bank, preset, keynote); + if(ip != NULL) + return ip; + if (o > 0) o++; + } + } + return NULL; +} + +/*----------------------------------------------------------------*/ +#define TO_MHZ(abscents) (int32_t)(8176.0 * pow(2.0,(double)(abscents)/1200.0)) +#define TO_VOLUME(level) (uint8_t)(255.0 - (level) * (255.0/1000.0)) + +double Instruments::calc_volume(LayerTable *tbl) +{ + int v; + + if(!tbl->set[SF_initAtten] || (int)tbl->val[SF_initAtten] == 0) + return (double)1.0; + + v = (int)tbl->val[SF_initAtten]; + if(v < 0) {v = 0;} + else if(v > 960) {v = 960;} + return cb_to_amp_table[v]; +} + +/* convert from 16bit value to fractional offset (15.15) */ +int32_t Instruments::to_offset(int32_t offset) +{ + return offset << 14; +} + +#define SF_ENVRATE_MAX (0x3FFFFFFFL) +#define SF_ENVRATE_MIN (1L) + +/* calculate ramp rate in fractional unit; + * diff = 16bit, time = msec + */ +int32_t Instruments::calc_rate(int32_t diff, double msec) +{ + double rate; + + if(msec == 0) {return (int32_t)SF_ENVRATE_MAX + 1;} + if(diff <= 0) {diff = 1;} + diff <<= 14; + rate = ((double)diff / playback_rate) * control_ratio * 1000.0 / msec; + if(fast_decay) {rate *= 2;} + if(rate > SF_ENVRATE_MAX) {rate = SF_ENVRATE_MAX;} + else if(rate < SF_ENVRATE_MIN) {rate = SF_ENVRATE_MIN;} + return (int32_t)rate; +} + +/* calculate ramp rate in fractional unit; + * diff = 16bit, timecent + */ +int32_t Instruments::to_rate(int32_t diff, int timecent) +{ + double rate; + + if(timecent == -12000) /* instantaneous attack */ + {return (int32_t)SF_ENVRATE_MAX + 1;} + if(diff <= 0) {diff = 1;} + diff <<= 14; + rate = (double)diff * control_ratio / playback_rate / pow(2.0, (double)timecent / 1200.0); + if(fast_decay) {rate *= 2;} + if(rate > SF_ENVRATE_MAX) {rate = SF_ENVRATE_MAX;} + else if(rate < SF_ENVRATE_MIN) {rate = SF_ENVRATE_MIN;} + return (int32_t)rate; +} + +/* + * convert timecents to sec + */ +double Instruments::to_msec(int timecent) +{ + return timecent == -12000 ? 0 : 1000.0 * pow(2.0, (double)timecent / 1200.0); +} + +/* + * Sustain level + * sf: centibels + * parm: 0x7f - sustain_level(dB) * 0.75 + */ +int32_t Instruments::calc_sustain(int sust_cB) +{ + if(sust_cB <= 0) {return 65533;} + else if(sust_cB >= 1000) {return 0;} + else {return (1000 - sust_cB) * 65533 / 1000;} +} + +Instrument *Instruments::load_from_file(SFInsts *rec, InstList *ip) +{ + SampleList *sp; + Instrument *inst; + int i; + int32_t len; + + inst = (Instrument *)safe_malloc(sizeof(Instrument)); + inst->instname = rec->inst_namebuf[ip->pr_idx]; + inst->type = INST_SF2; + inst->samples = ip->samples; + inst->sample = (Sample *)safe_malloc(sizeof(Sample) * ip->samples); + memset(inst->sample, 0, sizeof(Sample) * ip->samples); + for (i = 0, sp = ip->slist; i < ip->samples && sp; i++, sp = sp->next) { + Sample *sample = inst->sample + i; + int32_t j; +#ifdef _BIG_ENDIAN_ + int32_t k; + int16_t *tmp, s; +#endif + memcpy(sample, &sp->v, sizeof(Sample)); + sample->data = NULL; + sample->data_alloced = 0; + + if(i > 0 && (!sample->note_to_use || + (sample->modes & MODES_LOOPING))) + { + SampleList *sps; + Sample *found, *s; + + found = NULL; + for(j = 0, sps = ip->slist, s = inst->sample; j < i && sps; + j++, sps = sps->next, s++) + { + if(s->data == NULL) + break; + if(sp->start == sps->start) + { + if(antialiasing_allowed) + { + if(sample->data_length != s->data_length || + sample->sample_rate != s->sample_rate) + continue; + } + if(s->note_to_use && !(s->modes & MODES_LOOPING)) + continue; + found = s; + break; + } + } + if(found) + { + sample->data = found->data; + sample->data_alloced = 0; + continue; + } + } + + sample->data = (sample_t *)safe_large_malloc(sp->len + 2 * 3); + sample->data_alloced = 1; + + tf_seek(rec->tf, sp->start, SEEK_SET); + tf_read(sample->data, sp->len, rec->tf); + +#ifdef _BIG_ENDIAN_ + tmp = (int16_t*)sample->data; + k = sp->len / 2; + for (j = 0; j < k; j++) { + s = LE_SHORT(*tmp); + *tmp++ = s; + } +#endif + /* set a small blank loop at the tail for avoiding abnormal loop. */ + len = sp->len / 2; + sample->data[len] = sample->data[len + 1] = sample->data[len + 2] = 0; + + if (antialiasing_allowed) + antialiasing((int16_t *)sample->data, + sample->data_length >> FRACTION_BITS, + sample->sample_rate, + playback_rate); + + /* resample it if possible */ + if (sample->note_to_use && !(sample->modes & MODES_LOOPING)) + pre_resample(sample); + + /* do pitch detection on drums if surround chorus is used */ + if (ip->pat.bank == 128 && timidity_surround_chorus) + { + Freq freq; + sample->chord = -1; + sample->root_freq_detected = + freq.freq_fourier(sample, &(sample->chord)); + sample->transpose_detected = + assign_pitch_to_freq(sample->root_freq_detected) - + assign_pitch_to_freq(sample->root_freq / 1024.0); + } + } + + return inst; +} + + +/*---------------------------------------------------------------- + * excluded samples + *----------------------------------------------------------------*/ + +int Instruments::exclude_soundfont(int bank, int preset, int keynote) +{ + SFExclude *exc; + if(current_sfrec == NULL) + return 1; + exc = (SFExclude*)SFMalloc(current_sfrec , sizeof(SFExclude)); + exc->pat.bank = bank; + exc->pat.preset = preset; + exc->pat.keynote = keynote; + exc->next = current_sfrec->sfexclude; + current_sfrec->sfexclude = exc; + return 0; +} + +/* check the instrument is specified to be excluded */ +int Instruments::is_excluded(SFInsts *rec, int bank, int preset, int keynote) +{ + SFExclude *p; + for (p = rec->sfexclude; p; p = p->next) { + if (p->pat.bank == bank && + (p->pat.preset < 0 || p->pat.preset == preset) && + (p->pat.keynote < 0 || p->pat.keynote == keynote)) + return 1; + } + return 0; +} + + +/*---------------------------------------------------------------- + * ordered samples + *----------------------------------------------------------------*/ + +int Instruments::order_soundfont(int bank, int preset, int keynote, int order) +{ + SFOrder *p; + if(current_sfrec == NULL) + return 1; + p = (SFOrder*)SFMalloc(current_sfrec, sizeof(SFOrder)); + p->pat.bank = bank; + p->pat.preset = preset; + p->pat.keynote = keynote; + p->order = order; + p->next = current_sfrec->sforder; + current_sfrec->sforder = p; + return 0; +} + +/* check the instrument is specified to be ordered */ +int Instruments::is_ordered(SFInsts *rec, int bank, int preset, int keynote) +{ + SFOrder *p; + for (p = rec->sforder; p; p = p->next) { + if (p->pat.bank == bank && + (p->pat.preset < 0 || p->pat.preset == preset) && + (p->pat.keynote < 0 || p->pat.keynote == keynote)) + return p->order; + } + return -1; +} + + +/*----------------------------------------------------------------*/ + +int Instruments::load_font(SFInfo *sf, int pridx) +{ + SFPresetHdr *preset = &sf->preset[pridx]; + int rc, j, nlayers; + SFGenLayer *layp, *globalp; + + /* if layer is empty, skip it */ + if ((nlayers = preset->hdr.nlayers) <= 0 || + (layp = preset->hdr.layer) == NULL) + return AWE_RET_SKIP; + /* check global layer */ + globalp = NULL; + if (is_global(layp)) { + globalp = layp; + layp++; + nlayers--; + } + /* parse for each preset layer */ + for (j = 0; j < nlayers; j++, layp++) { + LayerTable tbl; + + /* set up table */ + clear_table(&tbl); + if (globalp) + set_to_table(sf, &tbl, globalp, P_GLOBAL); + set_to_table(sf, &tbl, layp, P_LAYER); + + /* parse the instrument */ + rc = parse_layer(sf, pridx, &tbl, 0); + if(rc == AWE_RET_ERR || rc == AWE_RET_NOMEM) + return rc; + } + + return AWE_RET_OK; +} + + +/*----------------------------------------------------------------*/ + +/* parse a preset layer and convert it to the patch structure */ +int Instruments::parse_layer(SFInfo *sf, int pridx, LayerTable *tbl, int level) +{ + SFInstHdr *inst; + int rc, i, nlayers; + SFGenLayer *lay, *globalp; + + if (level >= 2) { + fprintf(stderr, "parse_layer: too deep instrument level\n"); + return AWE_RET_ERR; + } + + /* instrument must be defined */ + if (!tbl->set[SF_instrument]) + return AWE_RET_SKIP; + + inst = &sf->inst[tbl->val[SF_instrument]]; + + /* if layer is empty, skip it */ + if ((nlayers = inst->hdr.nlayers) <= 0 || + (lay = inst->hdr.layer) == NULL) + return AWE_RET_SKIP; + + reset_last_sample_info(); + + /* check global layer */ + globalp = NULL; + if (is_global(lay)) { + globalp = lay; + lay++; + nlayers--; + } + + /* parse for each layer */ + for (i = 0; i < nlayers; i++, lay++) { + LayerTable ctbl; + clear_table(&ctbl); + if (globalp) + set_to_table(sf, &ctbl, globalp, P_GLOBAL); + set_to_table(sf, &ctbl, lay, P_LAYER); + + if (!ctbl.set[SF_sampleId]) { + /* recursive loading */ + merge_table(sf, &ctbl, tbl); + if (! sanity_range(&ctbl)) + continue; + rc = parse_layer(sf, pridx, &ctbl, level+1); + if (rc != AWE_RET_OK && rc != AWE_RET_SKIP) + return rc; + + reset_last_sample_info(); + } else { + init_and_merge_table(sf, &ctbl, tbl); + if (! sanity_range(&ctbl)) + continue; + + /* load the info data */ + if ((rc = make_patch(sf, pridx, &ctbl)) == AWE_RET_ERR) + return rc; + } + } + return AWE_RET_OK; +} + + +int Instruments::is_global(SFGenLayer *layer) +{ + int i; + for (i = 0; i < layer->nlists; i++) { + if (layer->list[i].oper == SF_instrument || + layer->list[i].oper == SF_sampleId) + return 0; + } + return 1; +} + + +/*---------------------------------------------------------------- + * layer table handlers + *----------------------------------------------------------------*/ + +/* initialize layer table */ +void Instruments::clear_table(LayerTable *tbl) +{ + memset(tbl->val, 0, sizeof(tbl->val)); + memset(tbl->set, 0, sizeof(tbl->set)); +} + +/* set items in a layer to the table */ +void Instruments::set_to_table(SFInfo *sf, LayerTable *tbl, SFGenLayer *lay, int level) +{ + int i; + for (i = 0; i < lay->nlists; i++) { + SFGenRec *gen = &lay->list[i]; + /* copy the value regardless of its copy policy */ + tbl->val[gen->oper] = gen->amount; + tbl->set[gen->oper] = level; + } +} + +/* add an item to the table */ +void Instruments::add_item_to_table(LayerTable *tbl, int oper, int amount, int level) +{ + LayerItem *item = &layer_items[oper]; + int o_lo, o_hi, lo, hi; + + switch (item->copy) { + case L_INHRT: + tbl->val[oper] += amount; + break; + case L_OVWRT: + tbl->val[oper] = amount; + break; + case L_PRSET: + case L_INSTR: + /* do not overwrite */ + if (!tbl->set[oper]) + tbl->val[oper] = amount; + break; + case L_RANGE: + if (!tbl->set[oper]) { + tbl->val[oper] = amount; + } else { + o_lo = LOWNUM(tbl->val[oper]); + o_hi = HIGHNUM(tbl->val[oper]); + lo = LOWNUM(amount); + hi = HIGHNUM(amount); + if (lo < o_lo) lo = o_lo; + if (hi > o_hi) hi = o_hi; + tbl->val[oper] = RANGE(lo, hi); + } + break; + } +} + +/* merge two tables */ +void Instruments::merge_table(SFInfo *sf, LayerTable *dst, LayerTable *src) +{ + int i; + for (i = 0; i < SF_EOF; i++) { + if (src->set[i]) { + if (sf->version == 1) { + if (!dst->set[i] || + i == SF_keyRange || i == SF_velRange) + /* just copy it */ + dst->val[i] = src->val[i]; + } + else + add_item_to_table(dst, i, src->val[i], P_GLOBAL); + dst->set[i] = P_GLOBAL; + } + } +} + +/* merge and set default values */ +void Instruments::init_and_merge_table(SFInfo *sf, LayerTable *dst, LayerTable *src) +{ + int i; + + /* default value is not zero */ + if (sf->version == 1) { + layer_items[SF_sustainEnv1].defv = 1000; + layer_items[SF_sustainEnv2].defv = 1000; + layer_items[SF_freqLfo1].defv = -725; + layer_items[SF_freqLfo2].defv = -15600; + } else { + layer_items[SF_sustainEnv1].defv = 0; + layer_items[SF_sustainEnv2].defv = 0; + layer_items[SF_freqLfo1].defv = 0; + layer_items[SF_freqLfo2].defv = 0; + } + + /* set default */ + for (i = 0; i < SF_EOF; i++) { + if (!dst->set[i]) + dst->val[i] = layer_items[i].defv; + } + merge_table(sf, dst, src); + /* convert from SBK to SF2 */ + if (sf->version == 1) { + for (i = 0; i < SF_EOF; i++) { + if (dst->set[i]) + dst->val[i] = sbk_to_sf2(i, dst->val[i], layer_items); + } + } +} + + +/*---------------------------------------------------------------- + * check key and velocity range + *----------------------------------------------------------------*/ + +int Instruments::sanity_range(LayerTable *tbl) +{ + int lo, hi; + + lo = LOWNUM(tbl->val[SF_keyRange]); + hi = HIGHNUM(tbl->val[SF_keyRange]); + if (lo < 0 || lo > 127 || hi < 0 || hi > 127 || hi < lo) + return 0; + + lo = LOWNUM(tbl->val[SF_velRange]); + hi = HIGHNUM(tbl->val[SF_velRange]); + if (lo < 0 || lo > 127 || hi < 0 || hi > 127 || hi < lo) + return 0; + + return 1; +} + + +/*---------------------------------------------------------------- + * create patch record from the stored data table + *----------------------------------------------------------------*/ + +int Instruments::make_patch(SFInfo *sf, int pridx, LayerTable *tbl) +{ + int bank, preset, keynote; + int keynote_from, keynote_to, done; + int addr, order; + InstList *ip; + SFSampleInfo *sample; + SampleList *sp; + + sample = &sf->sample[tbl->val[SF_sampleId]]; + + if(sample->sampletype & SF_SAMPLETYPE_ROM) /* is ROM sample? */ + { + printMessage(CMSG_INFO, VERB_DEBUG, "preset %d is ROM sample: 0x%x", + pridx, sample->sampletype); + return AWE_RET_SKIP; + } + + bank = sf->preset[pridx].bank; + preset = sf->preset[pridx].preset; + if(bank == 128){ + keynote_from = LOWNUM(tbl->val[SF_keyRange]); + keynote_to = HIGHNUM(tbl->val[SF_keyRange]); + } else + keynote_from = keynote_to = -1; + + done = 0; + for(keynote=keynote_from;keynote<=keynote_to;keynote++){ + + if(is_excluded(current_sfrec, bank, preset, keynote)) + { + continue; + } else + done++; + + order = is_ordered(current_sfrec, bank, preset, keynote); + if(order < 0) + order = current_sfrec->def_order; + + addr = INSTHASH(bank, preset, keynote); + + for(ip = current_sfrec->instlist[addr]; ip; ip = ip->next) + { + if(ip->pat.bank == bank && ip->pat.preset == preset && + (keynote < 0 || keynote == ip->pat.keynote)) + break; + } + + if(ip == NULL) + { + ip = (InstList*)SFMalloc(current_sfrec, sizeof(InstList)); + memset(ip, 0, sizeof(InstList)); + ip->pr_idx = pridx; + ip->pat.bank = bank; + ip->pat.preset = preset; + ip->pat.keynote = keynote; + ip->order = order; + ip->samples = 0; + ip->slist = NULL; + ip->next = current_sfrec->instlist[addr]; + current_sfrec->instlist[addr] = ip; + } + + /* new sample */ + sp = (SampleList *)SFMalloc(current_sfrec, sizeof(SampleList)); + memset(sp, 0, sizeof(SampleList)); + + sp->bank = bank; + sp->keynote = keynote; + + if(tbl->set[SF_keynum]) { + sp->v.note_to_use = (int)tbl->val[SF_keynum]; + } else if(bank == 128) { + sp->v.note_to_use = keynote; + } + make_info(sf, sp, tbl); + + /* add a sample */ + if(ip->slist == NULL) + ip->slist = sp; + else + { + SampleList *cur, *prev; + int32_t start; + + /* Insert sample */ + start = sp->start; + cur = ip->slist; + prev = NULL; + while(cur && cur->start <= start) + { + prev = cur; + cur = cur->next; + } + if(prev == NULL) + { + sp->next = ip->slist; + ip->slist = sp; + } + else + { + prev->next = sp; + sp->next = cur; + } + } + ip->samples++; + + } /* for(;;) */ + + + if(done==0) + return AWE_RET_SKIP; + else + return AWE_RET_OK; +} + +/*---------------------------------------------------------------- + * + * Modified for TiMidity + */ + +/* conver to Sample parameter */ +void Instruments::make_info(SFInfo *sf, SampleList *vp, LayerTable *tbl) +{ + set_sample_info(sf, vp, tbl); + set_init_info(sf, vp, tbl); + set_rootkey(sf, vp, tbl); + set_rootfreq(vp); + + /* tremolo & vibrato */ + convert_tremolo(vp, tbl); + convert_vibrato(vp, tbl); +} + +void Instruments::set_envelope_parameters(SampleList *vp) +{ + /* convert envelope parameters */ + vp->v.envelope_offset[0] = to_offset(65535); + vp->v.envelope_rate[0] = vp->attack; + + vp->v.envelope_offset[1] = to_offset(65534); + vp->v.envelope_rate[1] = vp->hold; + + vp->v.envelope_offset[2] = to_offset(vp->sustain); + vp->v.envelope_rate[2] = vp->decay; + + vp->v.envelope_offset[3] = 0; + vp->v.envelope_rate[3] = vp->release; + + vp->v.envelope_offset[4] = 0; + vp->v.envelope_rate[4] = vp->release; + + vp->v.envelope_offset[5] = 0; + vp->v.envelope_rate[5] = vp->release; + + /* convert modulation envelope parameters */ + vp->v.modenv_offset[0] = to_offset(65535); + vp->v.modenv_rate[0] = vp->modattack; + + vp->v.modenv_offset[1] = to_offset(65534); + vp->v.modenv_rate[1] = vp->modhold; + + vp->v.modenv_offset[2] = to_offset(vp->modsustain); + vp->v.modenv_rate[2] = vp->moddecay; + + vp->v.modenv_offset[3] = 0; + vp->v.modenv_rate[3] = vp->modrelease; + + vp->v.modenv_offset[4] = 0; + vp->v.modenv_rate[4] = vp->modrelease; + + vp->v.modenv_offset[5] = 0; + vp->v.modenv_rate[5] = vp->modrelease; +} + +/* set sample address */ +void Instruments::set_sample_info(SFInfo *sf, SampleList *vp, LayerTable *tbl) +{ + SFSampleInfo *sp = &sf->sample[tbl->val[SF_sampleId]]; + + /* set sample position */ + vp->start = (tbl->val[SF_startAddrsHi] << 15) + + tbl->val[SF_startAddrs] + + sp->startsample; + vp->len = (tbl->val[SF_endAddrsHi] << 15) + + tbl->val[SF_endAddrs] + + sp->endsample - vp->start; + + vp->start = abs(vp->start); + vp->len = abs(vp->len); + + /* set loop position */ + vp->v.loop_start = (tbl->val[SF_startloopAddrsHi] << 15) + + tbl->val[SF_startloopAddrs] + + sp->startloop - vp->start; + vp->v.loop_end = (tbl->val[SF_endloopAddrsHi] << 15) + + tbl->val[SF_endloopAddrs] + + sp->endloop - vp->start; + + /* set data length */ + vp->v.data_length = vp->len + 1; + + /* fix loop position */ + if (vp->v.loop_end > (splen_t)vp->len + 1) + vp->v.loop_end = vp->len + 1; + if (vp->v.loop_start > (splen_t)vp->len) + vp->v.loop_start = vp->len; + if (vp->v.loop_start >= vp->v.loop_end) + { + vp->v.loop_start = vp->len; + vp->v.loop_end = vp->len + 1; + } + + /* Sample rate */ + if(sp->samplerate > 50000) {sp->samplerate = 50000;} + else if(sp->samplerate < 400) {sp->samplerate = 400;} + vp->v.sample_rate = sp->samplerate; + + /* sample mode */ + vp->v.modes = MODES_16BIT; + + /* volume envelope & total volume */ + vp->v.volume = calc_volume(tbl) * current_sfrec->amptune; + + convert_volume_envelope(vp, tbl); + set_envelope_parameters(vp); + + if(tbl->val[SF_sampleFlags] == 1 || tbl->val[SF_sampleFlags] == 3) + { + /* looping */ + vp->v.modes |= MODES_LOOPING | MODES_SUSTAIN; + if(tbl->val[SF_sampleFlags] == 3) + vp->v.data_length = vp->v.loop_end; /* strip the tail */ + } + else + { + /* set a small blank loop at the tail for avoiding abnormal loop. */ + vp->v.loop_start = vp->len; + vp->v.loop_end = vp->len + 1; + } + + /* convert to fractional samples */ + vp->v.data_length <<= FRACTION_BITS; + vp->v.loop_start <<= FRACTION_BITS; + vp->v.loop_end <<= FRACTION_BITS; + + /* point to the file position */ + vp->start = vp->start * 2 + sf->samplepos; + vp->len *= 2; + + vp->v.vel_to_fc = -2400; /* SF2 default value */ + vp->v.key_to_fc = vp->v.vel_to_resonance = 0; + vp->v.envelope_velf_bpo = vp->v.modenv_velf_bpo = + vp->v.vel_to_fc_threshold = 64; + vp->v.key_to_fc_bpo = 60; + memset(vp->v.envelope_velf, 0, sizeof(vp->v.envelope_velf)); + memset(vp->v.modenv_velf, 0, sizeof(vp->v.modenv_velf)); + + vp->v.inst_type = INST_SF2; +} + +/*----------------------------------------------------------------*/ + +/* set global information */ + +void Instruments::set_init_info(SFInfo *sf, SampleList *vp, LayerTable *tbl) +{ + int val; + SFSampleInfo *sample; + sample = &sf->sample[tbl->val[SF_sampleId]]; + + /* key range */ + if(tbl->set[SF_keyRange]) + { + vp->low = LOWNUM(tbl->val[SF_keyRange]); + vp->high = HIGHNUM(tbl->val[SF_keyRange]); + } + else + { + vp->low = 0; + vp->high = 127; + } + vp->v.low_freq = freq_table[(int)vp->low]; + vp->v.high_freq = freq_table[(int)vp->high]; + + /* velocity range */ + if(tbl->set[SF_velRange]) { + vp->v.low_vel = LOWNUM(tbl->val[SF_velRange]); + vp->v.high_vel = HIGHNUM(tbl->val[SF_velRange]); + } else { + vp->v.low_vel = 0; + vp->v.high_vel = 127; + } + + /* fixed key & velocity */ + if(tbl->set[SF_keynum]) + vp->v.note_to_use = (int)tbl->val[SF_keynum]; + if(tbl->set[SF_velocity] && (int)tbl->val[SF_velocity] != 0) { + printMessage(CMSG_INFO,VERB_DEBUG,"error: fixed-velocity is not supported."); + } + + vp->v.sample_type = sample->sampletype; + vp->v.sf_sample_index = tbl->val[SF_sampleId]; + vp->v.sf_sample_link = sample->samplelink; + + /* Some sf2 files don't contain valid sample links, so see if the + previous sample was a matching Left / Right sample with the + link missing and add it */ + switch (sample->sampletype) { + case SF_SAMPLETYPE_LEFT: + if (vp->v.sf_sample_link == 0 && + last_sample_type == SF_SAMPLETYPE_RIGHT && + last_sample_instrument == tbl->val[SF_instrument] && + last_sample_keyrange == tbl->val[SF_keyRange]) { + /* The previous sample was a matching right sample + set the link */ + vp->v.sf_sample_link = last_sample_list->v.sf_sample_index; + } + break; + case SF_SAMPLETYPE_RIGHT: + if (last_sample_list && + last_sample_list->v.sf_sample_link == 0 && + last_sample_type == SF_SAMPLETYPE_LEFT && + last_sample_instrument == tbl->val[SF_instrument] && + last_sample_keyrange == tbl->val[SF_keyRange]) { + /* The previous sample was a matching left sample + set the link on the previous sample*/ + last_sample_list->v.sf_sample_link = tbl->val[SF_sampleId]; + } + break; + } + + /* Remember this sample in case the next one is a match */ + last_sample_type = sample->sampletype;; + last_sample_instrument = tbl->val[SF_instrument]; + last_sample_keyrange = tbl->val[SF_keyRange]; + last_sample_list = vp; + + /* panning position: 0 to 127 */ + val = (int)tbl->val[SF_panEffectsSend]; + if(sample->sampletype == SF_SAMPLETYPE_MONO || val != 0) { /* monoSample = 1 */ + if(val < -500) + vp->v.panning = 0; + else if(val > 500) + vp->v.panning = 127; + else + vp->v.panning = (int8_t)((val + 500) * 127 / 1000); + } else if(sample->sampletype == SF_SAMPLETYPE_RIGHT) { /* rightSample = 2 */ + vp->v.panning = 127; + } else if(sample->sampletype == SF_SAMPLETYPE_LEFT) { /* leftSample = 4 */ + vp->v.panning = 0; + } else if(sample->sampletype == SF_SAMPLETYPE_LINKED) { /* linkedSample = 8 */ + printMessage(CMSG_ERROR,VERB_NOISY,"error: linkedSample is not supported."); + } + + memset(vp->v.envelope_keyf, 0, sizeof(vp->v.envelope_keyf)); + memset(vp->v.modenv_keyf, 0, sizeof(vp->v.modenv_keyf)); + if(tbl->set[SF_autoHoldEnv2]) { + vp->v.envelope_keyf[1] = (int16_t)tbl->val[SF_autoHoldEnv2]; + } + if(tbl->set[SF_autoDecayEnv2]) { + vp->v.envelope_keyf[2] = (int16_t)tbl->val[SF_autoDecayEnv2]; + } + if(tbl->set[SF_autoHoldEnv1]) { + vp->v.modenv_keyf[1] = (int16_t)tbl->val[SF_autoHoldEnv1]; + } + if(tbl->set[SF_autoDecayEnv1]) { + vp->v.modenv_keyf[2] = (int16_t)tbl->val[SF_autoDecayEnv1]; + } + + current_sfrec->def_cutoff_allowed = 1; + current_sfrec->def_resonance_allowed = 1; + + /* initial cutoff & resonance */ + vp->cutoff_freq = 0; + if((int)tbl->val[SF_initialFilterFc] < 0) + tbl->set[SF_initialFilterFc] = tbl->val[SF_initialFilterFc] = 0; + if(current_sfrec->def_cutoff_allowed && tbl->set[SF_initialFilterFc] + && (int)tbl->val[SF_initialFilterFc] >= 1500 && (int)tbl->val[SF_initialFilterFc] <= 13500) + { + val = (int)tbl->val[SF_initialFilterFc]; + val = abscent_to_Hz(val); + + if(!timidity_modulation_envelope) { + if(tbl->set[SF_env1ToFilterFc] && (int)tbl->val[SF_env1ToFilterFc] > 0) + { + val = int( val * pow(2.0,(double)tbl->val[SF_env1ToFilterFc] / 1200.0f)); + if(val > 20000) {val = 20000;} + } + } + + vp->cutoff_freq = val; + } + vp->v.cutoff_freq = vp->cutoff_freq; + + vp->resonance = 0; + if(current_sfrec->def_resonance_allowed && tbl->set[SF_initialFilterQ]) + { + val = (int)tbl->val[SF_initialFilterQ]; + vp->resonance = val; + } + vp->v.resonance = vp->resonance; +} + +void Instruments::reset_last_sample_info(void) +{ + last_sample_list = NULL; + last_sample_type = 0; + /* Set last instrument and keyrange to a value which cannot be represented + by LayerTable.val (which is a short) */ + last_sample_instrument = 0x80000000; + last_sample_keyrange = 0x80000000; +} + +int Instruments::abscent_to_Hz(int abscents) +{ + return (int)(8.176 * pow(2.0, (double)abscents / 1200.0)); +} + +/*----------------------------------------------------------------*/ + +#define SF_MODENV_CENT_MAX 1200 /* Live! allows only +-1200cents. */ + +/* calculate root key & fine tune */ +void Instruments::set_rootkey(SFInfo *sf, SampleList *vp, LayerTable *tbl) +{ + SFSampleInfo *sp = &sf->sample[tbl->val[SF_sampleId]]; + int temp; + + /* scale factor */ + vp->v.scale_factor = 1024 * (double) tbl->val[SF_scaleTuning] / 100 + 0.5; + /* set initial root key & fine tune */ + if (sf->version == 1 && tbl->set[SF_samplePitch]) { + /* set from sample pitch */ + vp->root = tbl->val[SF_samplePitch] / 100; + vp->tune = -tbl->val[SF_samplePitch] % 100; + if (vp->tune <= -50) + vp->root++, vp->tune += 100; + } else { + /* from sample info */ + vp->root = sp->originalPitch; + vp->tune = (int8_t) sp->pitchCorrection; + } + /* orverride root key */ + if (tbl->set[SF_rootKey]) + vp->root = tbl->val[SF_rootKey]; + else if (vp->bank == 128 && vp->v.scale_factor != 0) + vp->tune += int16_t((vp->keynote - sp->originalPitch) * 100 * (double) vp->v.scale_factor / 1024); + vp->tune += tbl->val[SF_coarseTune] * 100 + tbl->val[SF_fineTune]; + /* correct too high pitch */ + if (vp->root >= vp->high + 60) + vp->root -= 60; + vp->v.tremolo_to_pitch = + (tbl->set[SF_lfo1ToPitch]) ? tbl->val[SF_lfo1ToPitch] : 0; + vp->v.tremolo_to_fc = + (tbl->set[SF_lfo1ToFilterFc]) ? tbl->val[SF_lfo1ToFilterFc] : 0; + vp->v.modenv_to_pitch = + (tbl->set[SF_env1ToPitch]) ? tbl->val[SF_env1ToPitch] : 0; + /* correct tune with the sustain level of modulation envelope */ + temp = vp->v.modenv_to_pitch + * (double) (1000 - tbl->val[SF_sustainEnv1]) / 1000 + 0.5; + vp->tune += temp, vp->v.modenv_to_pitch -= temp; + vp->v.modenv_to_fc = + (tbl->set[SF_env1ToFilterFc]) ? tbl->val[SF_env1ToFilterFc] : 0; +} + +void Instruments::set_rootfreq(SampleList *vp) +{ + int root = vp->root; + int tune = 0.5 - 256 * (double) vp->tune / 100; + + /* 0 <= tune < 255 */ + while (tune < 0) + root--, tune += 256; + while (tune > 255) + root++, tune -= 256; + if (root < 0) { + vp->v.root_freq = freq_table[0] * (double) bend_fine[tune] + / bend_coarse[-root] + 0.5; + vp->v.scale_freq = 0; /* scale freq */ + } else if (root > 127) { + vp->v.root_freq = freq_table[127] * (double) bend_fine[tune] + * bend_coarse[root - 127] + 0.5; + vp->v.scale_freq = 127; /* scale freq */ + } else { + vp->v.root_freq = freq_table[root] * (double) bend_fine[tune] + 0.5; + vp->v.scale_freq = root; /* scale freq */ + } +} + +/*----------------------------------------------------------------*/ + + +/*Pseudo Reverb*/ +extern int32_t modify_release; + + +/* volume envelope parameters */ +void Instruments::convert_volume_envelope(SampleList *vp, LayerTable *tbl) +{ + vp->attack = to_rate(65535, tbl->val[SF_attackEnv2]); + vp->hold = to_rate(1, tbl->val[SF_holdEnv2]); + vp->sustain = calc_sustain(tbl->val[SF_sustainEnv2]); + vp->decay = to_rate(65533 - vp->sustain, tbl->val[SF_decayEnv2]); + if(modify_release) /* Pseudo Reverb */ + vp->release = calc_rate(65535, modify_release); + else + vp->release = to_rate(65535, tbl->val[SF_releaseEnv2]); + vp->v.envelope_delay = playback_rate * + to_msec(tbl->val[SF_delayEnv2]) * 0.001; + + /* convert modulation envelope */ + vp->modattack = to_rate(65535, tbl->val[SF_attackEnv1]); + vp->modhold = to_rate(1, tbl->val[SF_holdEnv1]); + vp->modsustain = calc_sustain(tbl->val[SF_sustainEnv1]); + vp->moddecay = to_rate(65533 - vp->modsustain, tbl->val[SF_decayEnv1]); + if(modify_release) /* Pseudo Reverb */ + vp->modrelease = calc_rate(65535, modify_release); + else + vp->modrelease = to_rate(65535, tbl->val[SF_releaseEnv1]); + vp->v.modenv_delay = playback_rate * + to_msec(tbl->val[SF_delayEnv1]) * 0.001; + + vp->v.modes |= MODES_ENVELOPE; +} + + +/*---------------------------------------------------------------- + * tremolo (LFO1) conversion + *----------------------------------------------------------------*/ + +void Instruments::convert_tremolo(SampleList *vp, LayerTable *tbl) +{ + int32_t freq; + double level; + + if (!tbl->set[SF_lfo1ToVolume]) + return; + + level = pow(10.0, (double)abs(tbl->val[SF_lfo1ToVolume]) / -200.0); + vp->v.tremolo_depth = 256 * (1.0 - level); + if ((int)tbl->val[SF_lfo1ToVolume] < 0) { vp->v.tremolo_depth = -vp->v.tremolo_depth; } + + /* frequency in mHz */ + if (!tbl->set[SF_freqLfo1]) + freq = 0; + else + { + freq = (int)tbl->val[SF_freqLfo1]; + freq = TO_MHZ(freq); + } + + /* convert mHz to sine table increment; 1024<v.tremolo_phase_increment = ((playback_rate / 1000 * freq) >> RATE_SHIFT) / control_ratio; + vp->v.tremolo_delay = playback_rate * + to_msec(tbl->val[SF_delayLfo1]) * 0.001; +} + +/*---------------------------------------------------------------- + * vibrato (LFO2) conversion + *----------------------------------------------------------------*/ + +void Instruments::convert_vibrato(SampleList *vp, LayerTable *tbl) +{ + int32_t shift, freq; + + if (!tbl->set[SF_lfo2ToPitch]) { + vp->v.vibrato_control_ratio = 0; + return; + } + + shift = (int)tbl->val[SF_lfo2ToPitch]; + + /* cents to linear; 400cents = 256 */ + shift = shift * 256 / 400; + if (shift > 255) { shift = 255; } + else if (shift < -255) { shift = -255; } + vp->v.vibrato_depth = (int16_t)shift; + + /* frequency in mHz */ + if (!tbl->set[SF_freqLfo2]) + freq = 0; + else + { + freq = (int)tbl->val[SF_freqLfo2]; + freq = TO_MHZ(freq); + if (freq == 0) { freq = 1; } + /* convert mHz to control ratio */ + vp->v.vibrato_control_ratio = (1000 * playback_rate) / + (freq * 2 * VIBRATO_SAMPLE_INCREMENTS); + } + + vp->v.vibrato_delay = playback_rate * + to_msec(tbl->val[SF_delayLfo2]) * 0.001; +} + +} diff --git a/thirdparty/timidityplus/tables.cpp b/thirdparty/timidityplus/tables.cpp new file mode 100644 index 000000000..504579df9 --- /dev/null +++ b/thirdparty/timidityplus/tables.cpp @@ -0,0 +1,1011 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + The 8-bit uLaw to 16-bit PCM and the 13-bit-PCM to 8-bit uLaw + tables were lifted from the rsynth-2.0 sources. The README says: + + This is a text to speech system produced by integrating various pieces + of code and tables of data, which are all (I believe) in the public domain. + + The bulk of the intergration was done by myself, that is Nick Ing-Simmons. + I can be reached via my employer at nik@tiuk.ti.com. +*/ + +#include +#include +#include "timidity.h" +#include "tables.h" +#include "instrum.h" + + +namespace TimidityPlus +{ + +int32_t freq_table[128]; +int32_t freq_table_tuning[128][128]; +int32_t freq_table_pytha[24][128]; +int32_t freq_table_meantone[48][128]; +int32_t freq_table_pureint[48][128]; + + +static void init_freq_table(void) +{ + int i; + + for (i = 0; i < 128; i++) { + freq_table[i] = 440 * pow(2.0, (i - 69) / 12.0) * 1000 + 0.5; + } +} + +static void init_freq_table_tuning(void) +{ + int p, i; + double f; + + for (i = 0; i < 128; i++) + freq_table_tuning[0][i] = freq_table[i]; + for (i = 0; i < 128; i++) { + f = 440 * pow(2.0, (i - 69) / 12.0); + for (p = 1; p < 128; p++) + freq_table_tuning[p][i] = f * 1000 + 0.5; + } +} + +static void init_freq_table_pytha(void) +{ + int i, j, k, l; + double f; + static const double major_ratio[] = { + 1.0 / 1, 256.0 / 243, 9.0 / 8, 32.0 / 27, + 81.0 / 64, 4.0 / 3, 729.0 / 512, 3.0 / 2, + 128.0 / 81, 27.0 / 16, 16.0 / 9, 243.0 / 128 + }; + static const double minor_ratio[] = { + 1.0 / 1, 2187.0 / 2048, 9.0 / 8, 19683.0 / 16384, + 81.0 / 64, 4.0 / 3, 729.0 / 512, 3.0 / 2, + 6561.0 / 4096, 27.0 / 16, 16.0 / 9, 243.0 / 128 + }; + + for (i = 0; i < 12; i++) + for (j = -1; j < 11; j++) { + f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5); + for (k = 0; k < 12; k++) { + l = i + j * 12 + k; + if (l < 0 || l >= 128) + continue; + freq_table_pytha[i][l] = f * major_ratio[k] * 1000 + 0.5; + freq_table_pytha[i + 12][l] = f * minor_ratio[k] * 1000 + 0.5; + } + } +} + +static void init_freq_table_meantone(void) +{ + int i, j, k, l; + double f; + static double major_ratio[12], minor_ratio[12]; + static const double sc = 81.0 / 80; + + major_ratio[0] = 1; + major_ratio[1] = 8 / pow(5.0, 5.0 / 4); + major_ratio[2] = pow(5.0, 1.0 / 2) / 2; + major_ratio[3] = 4 / pow(5.0, 3.0 / 4); + major_ratio[4] = 5.0 / 4; + major_ratio[5] = 2 / pow(5.0, 1.0 / 4); + major_ratio[6] = pow(5.0, 3.0 / 2) / 8; + major_ratio[7] = pow(5.0, 1.0 / 4); + major_ratio[8] = 8.0 / 5; + major_ratio[9] = pow(5.0, 3.0 / 4) / 2; + major_ratio[10] = 4 / pow(5.0, 1.0 / 2); + major_ratio[11] = pow(5.0, 5.0 / 4) / 4; + minor_ratio[0] = 1; + minor_ratio[1] = pow(10.0 / 3, 7.0 / 3) / 16; + minor_ratio[2] = pow(10.0 / 3, 2.0 / 3) / 2; + minor_ratio[3] = 125.0 / 108; + minor_ratio[4] = pow(10.0 / 3, 4.0 / 3) / 4; + minor_ratio[5] = 2 / pow(10.0 / 3, 1.0 / 3); + minor_ratio[6] = 25.0 / 18; + minor_ratio[7] = pow(10.0 / 3, 1.0 / 3); + minor_ratio[8] = pow(10.0 / 3, 8.0 / 3) / 16; + minor_ratio[9] = 5.0 / 3; + minor_ratio[10] = 4 / pow(10.0 / 3, 2.0 / 3); + minor_ratio[11] = pow(10.0 / 3, 5.0 / 3) / 4; + for (i = 0; i < 12; i++) + for (j = -1; j < 11; j++) { + f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5); + for (k = 0; k < 12; k++) { + l = i + j * 12 + k; + if (l < 0 || l >= 128) + continue; + freq_table_meantone[i][l] = + f * major_ratio[k] * 1000 + 0.5; + freq_table_meantone[i + 12][l] = + f * minor_ratio[k] * sc * 1000 + 0.5; + freq_table_meantone[i + 24][l] = + f * minor_ratio[k] * 1000 + 0.5; + freq_table_meantone[i + 36][l] = + f * major_ratio[k] * sc * 1000 + 0.5; + } + } +} + +static void init_freq_table_pureint(void) +{ + int i, j, k, l; + double f; + static const double major_ratio[] = { + 1.0 / 1, 16.0 / 15, 9.0 / 8, 6.0 / 5, 5.0 / 4, 4.0 / 3, + 45.0 / 32, 3.0 / 2, 8.0 / 5, 5.0 / 3, 9.0 / 5, 15.0 / 8 + }; + static const double minor_ratio[] = { + 1.0 / 1, 25.0 / 24, 10.0 / 9, 75.0 / 64, 5.0 / 4, 4.0 / 3, + 25.0 / 18, 3.0 / 2, 25.0 / 16, 5.0 / 3, 16.0 / 9, 15.0 / 8 + }; + static const double sc = 81.0 / 80; + + for (i = 0; i < 12; i++) + for (j = -1; j < 11; j++) { + f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5); + for (k = 0; k < 12; k++) { + l = i + j * 12 + k; + if (l < 0 || l >= 128) + continue; + freq_table_pureint[i][l] = + f * major_ratio[k] * 1000 + 0.5; + freq_table_pureint[i + 12][l] = + f * minor_ratio[k] * sc * 1000 + 0.5; + freq_table_pureint[i + 24][l] = + f * minor_ratio[k] * 1000 + 0.5; + freq_table_pureint[i + 36][l] = + f * major_ratio[k] * sc * 1000 + 0.5; + } + } +} + + +/* v=2.^((x/127-1) * 6) */ +double def_vol_table[1024]; + +static void init_def_vol_table(void) +{ + int i; + + for (i = 0; i < 1024; i++) + def_vol_table[i] = pow(2.0f,((double)i / 1023.0 - 1) * 6); +} + +/* v=2.^((x/127-1) * 8) */ +double gs_vol_table[1024]; + +static void init_gs_vol_table(void) +{ + int i; + + for (i = 0; i < 1024; i++) + gs_vol_table[i] = pow(2.0f,((double)i / 1023.0 - 1) * 8); +} + +double *xg_vol_table = gs_vol_table; + +double bend_fine[256]; +double bend_coarse[128]; + +static void init_bend_fine(void) +{ + int i; + + for (i = 0; i < 256; i++) + bend_fine[i] = pow(2.0, i / 12.0 / 256); +} + +static void init_bend_coarse(void) +{ + int i; + + for (i = 0; i < 128; i++) + bend_coarse[i] = pow(2.0, i / 12.0); +} + +/* + * midi_time_table(x + 16y) = midi_time_table(x) * (2^y) + * midi_time_table(64) = 1 + * then, + * midi_time_table(x) := (2^(x/16))/16 + */ +const double midi_time_table[128] = +{ + 0.06250, 0.06527, 0.06816, 0.07117, 0.07433, 0.07762, 0.08105, 0.08464, + 0.08839, 0.09230, 0.09639, 0.10066, 0.10511, 0.10977, 0.11463, 0.11970, + 0.12500, 0.13053, 0.13631, 0.14235, 0.14865, 0.15523, 0.16210, 0.16928, + 0.17678, 0.18460, 0.19278, 0.20131, 0.21022, 0.21953, 0.22925, 0.23940, + 0.25000, 0.26107, 0.27263, 0.28470, 0.29730, 0.31046, 0.32421, 0.33856, + 0.35355, 0.36921, 0.38555, 0.40262, 0.42045, 0.43906, 0.45850, 0.47880, + 0.50000, 0.52214, 0.54525, 0.56939, 0.59460, 0.62093, 0.64842, 0.67713, + 0.70711, 0.73841, 0.77111, 0.80525, 0.84090, 0.87813, 0.91700, 0.95760, + 1.00000, 1.04427, 1.09051, 1.13879, 1.18921, 1.24186, 1.29684, 1.35426, + 1.41421, 1.47683, 1.54221, 1.61049, 1.68179, 1.75625, 1.83401, 1.91521, + 2.00000, 2.08855, 2.18102, 2.27758, 2.37841, 2.48372, 2.59368, 2.70851, + 2.82843, 2.95365, 3.08442, 3.22098, 3.36359, 3.51250, 3.66802, 3.83041, + 4.00000, 4.17710, 4.36203, 4.55515, 4.75683, 4.96743, 5.18736, 5.41702, + 5.65685, 5.90730, 6.16884, 6.44196, 6.72717, 7.02501, 7.33603, 7.66083, + 8.00000, 8.35419, 8.72406, 9.11031, 9.51366, 9.93486,10.37472,10.83404, + 11.31371,11.81461,12.33769,12.88392,13.45434,14.05002,14.67206,15.32165 +}; +/* + * midi_time_table2(x) := 2^(x/16/128) (for lsb tunning) + */ +const double midi_time_table2[128] = +{ + 1.00000, 1.00034, 1.00068, 1.00102, 1.00135, 1.00169, 1.00203, 1.00237, + 1.00271, 1.00305, 1.00339, 1.00373, 1.00407, 1.00441, 1.00475, 1.00509, + 1.00543, 1.00577, 1.00611, 1.00645, 1.00679, 1.00713, 1.00747, 1.00781, + 1.00816, 1.00850, 1.00884, 1.00918, 1.00952, 1.00986, 1.01021, 1.01055, + 1.01089, 1.01123, 1.01157, 1.01192, 1.01226, 1.01260, 1.01294, 1.01329, + 1.01363, 1.01397, 1.01432, 1.01466, 1.01500, 1.01535, 1.01569, 1.01603, + 1.01638, 1.01672, 1.01707, 1.01741, 1.01776, 1.01810, 1.01844, 1.01879, + 1.01913, 1.01948, 1.01982, 1.02017, 1.02051, 1.02086, 1.02121, 1.02155, + 1.02190, 1.02224, 1.02259, 1.02294, 1.02328, 1.02363, 1.02397, 1.02432, + 1.02467, 1.02501, 1.02536, 1.02571, 1.02606, 1.02640, 1.02675, 1.02710, + 1.02745, 1.02779, 1.02814, 1.02849, 1.02884, 1.02919, 1.02953, 1.02988, + 1.03023, 1.03058, 1.03093, 1.03128, 1.03163, 1.03198, 1.03233, 1.03268, + 1.03302, 1.03337, 1.03372, 1.03407, 1.03442, 1.03477, 1.03512, 1.03548, + 1.03583, 1.03618, 1.03653, 1.03688, 1.03723, 1.03758, 1.03793, 1.03828, + 1.03863, 1.03899, 1.03934, 1.03969, 1.04004, 1.04039, 1.04075, 1.04110, + 1.04145, 1.04180, 1.04216, 1.04251, 1.04286, 1.04321, 1.04357, 1.04392 +}; + + +static double triangular_table[257]; + +void init_triangular_table(void) +{ + int i; + for (i = 0; i < 257; i++) { + triangular_table[i] = (double)(i/* - (genrand_int32() % 1)*/) / 256.0; + if(triangular_table[i] < 0) {triangular_table[i] = 0;} + else if(triangular_table[i] > 1.0) {triangular_table[i] = 1.0;} + } + triangular_table[0] = 0.0; + triangular_table[256] = 1.0; +} + +double lookup_triangular(int x) +{ + int xx = x & 0xFF; + switch ((x>>8) & 0x03) + { + default: + case 0: + return triangular_table[xx]; + case 1: + return triangular_table[0x100 - xx]; + case 2: + return -triangular_table[xx]; + case 3: + return -triangular_table[0x100 - xx]; + } +} + + +const uint8_t reverb_macro_presets[] = +{ /* CHARACTER,PRE-LPF,LEVEL,TIME,DELAY FEEDBACK,PREDELAY TIME */ + 0,3,64,80,0,0, /* 00: Room1 */ + 1,4,64,56,0,0, /* 01: Room2 */ + 2,0,64,64,0,0, /* 02: Room3 */ + 3,4,64,72,0,0, /* 03: Hall1 */ + 4,0,64,64,0,0, /* 04: Hall2 */ + 5,0,64,88,0,0, /* 05: Plate */ + 6,0,64,32,40,0, /* 06: Delay */ + 7,0,64,64,32,0, /* 07: Panning Delay */ +}; + +const uint8_t chorus_macro_presets[] = +{ /* PRE-LPF,LEVEL,FEEDBACK,DELAY,RATE,DEPTH,SEND TO REVERB,SEND TO DELAY */ + 0,64,0,112,3,5,0,0, /* 00: Chorus1 */ + 0,64,5,80,9,19,0,0, /* 01: Chorus2 */ + 0,64,8,80,3,19,0,0, /* 02: Chorus3 */ + 0,64,16,64,9,16,0,0, /* 03: Chorus4 */ + 0,64,64,127,2,24,0,0, /* 04: Feedback Chorus */ + 0,64,112,127,1,5,0,0, /* 05: Flanger */ + 0,64,0,127,0,127,0,0, /* 06: Short Delay */ + 0,64,80,127,0,127,0,0, /* 07: Short Delay(Feedback) */ +}; + +const uint8_t delay_macro_presets[] = +{ /* PRE-LPF,TIME(C),RATIO(L),RATIO(R),LEVEL(C),LEVEL(L),LEVEL(R),LEVEL,FEEDBACK,LEVEL TO REVERB */ + 0,97,1,1,127,0,0,64,79,0, /* 00: Delay1 */ + 0,106,1,1,127,0,0,64,79,0, /* 01: Delay2 */ + 0,115,1,1,127,0,0,64,63,0, /* 02: Delay3 */ + 0,83,1,1,127,0,0,64,71,0, /* 03: Delay4 */ + 0,90,12,24,0,125,60,64,73,0, /* 04: Pan Delay1 */ + 0,109,12,24,0,125,60,64,70,0, /* 05: Pan Delay2 */ + 0,115,12,24,0,120,64,64,72,0, /* 06: Pan Delay3 */ + 0,93,12,24,0,120,64,64,63,0, /* 07: Pan Delay4 */ + 0,109,12,24,0,114,60,64,60,36, /* 08: Delay to Reverb */ + 0,110,21,31,97,127,67,64,39,0, /* 09: Pan Repeat */ +}; + +const float delay_time_center_table[] = +{ /* 0x00~0x73, 0.1ms~1000ms */ + 0.1f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, + 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.2f, 4.4f, 4.6f, 4.8f, + 5.0f, 5.5f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.5f, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, + 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, + 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, + 200, 220, 240, 260, 280, 300, 320, 340, 360, 380, 400, 420, 440, 460, 480, + 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, +}; + +const float pre_delay_time_table[] = +{ + 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, + 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 2.5f, 2.6f, 2.7f, 2.8f, 2.9f, 3.0f, 3.1f, 3.2f, 3.3f, 3.4f, 3.5f, 3.6f, 3.7f, 3.8f, 3.9f, + 4.0f, 4.1f, 4.2f, 4.3f, 4.4f, 4.5f, 4.6f, 4.7f, 4.8f, 4.9f, + 5.0f, 5.5f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.5f, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, + 90, 92, 94, 96, 98, 100, 100, 100, +}; + +const float chorus_delay_time_table[] = +{ + 0.000000f, 0.078740f, 0.157480f, 0.236220f, 0.314961f, 0.393701f, 0.472441f, 0.551181f, + 0.629921f, 0.708661f, 0.787402f, 0.866142f, 0.944882f, 1.023622f, 1.102362f, 1.181102f, + 1.259843f, 1.338583f, 1.417323f, 1.496063f, 1.574803f, 1.653543f, 1.732283f, 1.811024f, + 1.889764f, 1.968504f, 2.047244f, 2.125984f, 2.204724f, 2.283465f, 2.362205f, 2.440945f, + 2.519685f, 2.598425f, 2.677165f, 2.755906f, 2.834646f, 2.913386f, 2.992126f, 3.070866f, + 3.149606f, 3.228346f, 3.307087f, 3.385827f, 3.464567f, 3.543307f, 3.622047f, 3.700787f, + 3.779528f, 3.858268f, 3.937008f, 4.015748f, 4.094488f, 4.173228f, 4.251969f, 4.330709f, + 4.409449f, 4.488189f, 4.566929f, 4.645669f, 4.724409f, 4.803150f, 4.881890f, 4.960630f, + 5.039370f, 5.118110f, 5.196850f, 5.275591f, 5.354331f, 5.433071f, 5.511811f, 5.590551f, + 5.669291f, 5.748031f, 5.826772f, 5.905512f, 5.984252f, 6.062992f, 6.141732f, 6.220472f, + 6.299213f, 6.377953f, 6.456693f, 6.535433f, 6.614173f, 6.692913f, 6.771654f, 6.850394f, + 6.929134f, 7.007874f, 7.086614f, 7.165354f, 7.244094f, 7.322835f, 7.401575f, 7.480315f, + 7.559055f, 7.637795f, 7.716535f, 7.795276f, 10.000000f, 10.555556f, 11.111111f, 11.666667f, + 12.222222f, 12.777778f, 13.333333f, 13.888889f, 14.444444f, 15.000000f, 15.555556f, 16.111111f, + 16.666667f, 17.222222f, 17.777778f, 18.333333f, 18.888889f, 19.444444f, 20.000000f, 20.555556f, + 21.111111f, 21.666667f, 22.222222f, 22.777778f, 23.333333f, 23.888889f, 24.444444f, 25.000000f, +}; + +const float rate1_table[] = +{ + 0.05f, 0.10f, 0.15f, 0.20f, 0.25f, 0.30f, 0.35f, 0.40f, 0.45f, 0.50f, 0.55f, 0.60f, 0.65f, 0.70f, 0.75f, 0.80f, + 0.85f, 0.90f, 0.95f, 1.00f, 1.05f, 1.10f, 1.15f, 1.20f, 1.25f, 1.30f, 1.35f, 1.40f, 1.45f, 1.50f, 1.55f, 1.60f, + 1.65f, 1.70f, 1.75f, 1.80f, 1.85f, 1.90f, 1.95f, 2.00f, 2.05f, 2.10f, 2.15f, 2.20f, 2.25f, 2.30f, 2.35f, 2.40f, + 2.45f, 2.50f, 2.55f, 2.60f, 2.65f, 2.70f, 2.75f, 2.80f, 2.85f, 2.90f, 2.95f, 3.00f, 3.05f, 3.10f, 3.15f, 3.20f, + 3.25f, 3.30f, 3.35f, 3.40f, 3.45f, 3.50f, 3.55f, 3.60f, 3.65f, 3.70f, 3.75f, 3.80f, 3.85f, 3.90f, 3.95f, 4.00f, + 4.05f, 4.10f, 4.15f, 4.20f, 4.25f, 4.30f, 4.35f, 4.40f, 4.45f, 4.50f, 4.55f, 4.60f, 4.65f, 4.70f, 4.75f, 4.80f, + 4.85f, 4.90f, 4.95f, 5.00f, 5.10f, 5.20f, 5.30f, 5.40f, 5.50f, 5.60f, 5.70f, 5.80f, 5.90f, 6.00f, 6.10f, 6.20f, + 6.30f, 6.40f, 6.50f, 6.60f, 6.70f, 6.80f, 6.90f, 7.00f, 7.50f, 8.00f, 8.50f, 9.00f, 9.50f, 10.00f, 10.00f, 10.00f, +}; + +/* Derivation of Perceived Volume Curve Equation: + * + * Given: delta dB = 20 * log10(amplitude_new / amplitude_old) + * delta dB of 10 == perceived volume change of 2x + * + * 10 = 20 * log10(?) + * 0.5 = log10(?) + * 10^0.5 = ? + * + * therefore: 2x perceived volume == ~3.16x amplitude + * 4x perceived volume == 10x amplitude + * + * Volume Amplitude + * ------------ --------------- + * 1 1 + * 0.25 0.1 + * 0.0625 0.01 + * 0.015625 0.001 + * 0.00390625 0.0001 + * 0.0009765625 0.00001 + * 0 0 + * + * Fit curve to table: + * + * amplification = pow(volume, 1.66096404744) + */ +double perceived_vol_table[128]; + +void init_perceived_vol_table(void) +{ + int i; + + for (i = 0; i < 128; i++) + perceived_vol_table[i] = + 127.0 * pow((double)i / 127.0, 1.66096404744); +} + +double gm2_vol_table[128]; + +void init_gm2_vol_table(void) +{ + int i; + + for(i = 0; i < 128; i++) + gm2_vol_table[i] = (i * i) / 127.0; +} + +/* measured value from SC-88STPro. + approximate expression: y = (-0.3768x6 + 0.9528x5 - 0.8253x4 + 0.2665x3 + 0.9892x2 - 0.0059x + 0.001) * 127 */ +double sc_vol_table[128] = +{ + 0.000000, 0.128905, 0.146482, 0.179815, 0.228982, 0.294049, 0.375078, 0.472120, + 0.585221, 0.714419, 0.859746, 1.021229, 1.198887, 1.392736, 1.602785, 1.829039, + 2.071501, 2.330166, 2.605028, 2.896078, 3.203301, 3.526682, 3.866202, 4.221841, + 4.593575, 4.981382, 5.385233, 5.805103, 6.240963, 6.692783, 7.160536, 7.644189, + 8.143714, 8.659080, 9.190256, 9.737215, 10.299925, 10.878360, 11.472491, 12.082292, + 12.707738, 13.348803, 14.005465, 14.677704, 15.365497, 16.068829, 16.787681, 17.522039, + 18.271890, 19.037223, 19.818029, 20.614301, 21.426034, 22.253225, 23.095873, 23.953980, + 24.827548, 25.716584, 26.621094, 27.541088, 28.476578, 29.427576, 30.394100, 31.376165, + 32.373791, 33.386998, 34.415810, 35.460249, 36.520342, 37.596115, 38.687597, 39.794815, + 40.917801, 42.056586, 43.211200, 44.381677, 45.568048, 46.770346, 47.988605, 49.222856, + 50.473131, 51.739464, 53.021883, 54.320420, 55.635102, 56.965957, 58.313010, 59.676284, + 61.055802, 62.451580, 63.863636, 65.291982, 66.736627, 68.197578, 69.674835, 71.168397, + 72.678255, 74.204399, 75.746811, 77.305466, 78.880337, 80.471388, 82.078576, 83.701851, + 85.341158, 86.996429, 88.667594, 90.354568, 92.057260, 93.775571, 95.509387, 97.258589, + 99.023042, 100.802603, 102.597116, 104.406412, 106.230309, 108.068613, 109.921115, 111.787592, + 113.667805, 115.561500, 117.468408, 119.388243, 121.320699, 123.265458, 125.222177, 127.000000, +}; + +/* measured value from SC-88STPro. + approximate expression: y = (-1.5374x6 + 4.4002x5 - 4.8309x4 + 2.572x3 + 0.1487x2 + 0.2412x + 0.0044) * 127 */ +double sc_vel_table[128] = +{ + 0.000000, 0.801328, 1.047122, 1.297056, 1.551953, 1.812583, 2.079668, 2.353885, + 2.635863, 2.926190, 3.225412, 3.534034, 3.852525, 4.181317, 4.520806, 4.871357, + 5.233303, 5.606946, 5.992560, 6.390392, 6.800666, 7.223577, 7.659301, 8.107993, + 8.569785, 9.044792, 9.533111, 10.034824, 10.549997, 11.078680, 11.620912, 12.176722, + 12.746124, 13.329126, 13.925725, 14.535911, 15.159666, 15.796968, 16.447787, 17.112090, + 17.789842, 18.481003, 19.185529, 19.903380, 20.634509, 21.378873, 22.136426, 22.907127, + 23.690932, 24.487801, 25.297696, 26.120582, 26.956425, 27.805197, 28.666872, 29.541428, + 30.428848, 31.329119, 32.242232, 33.168184, 34.106974, 35.058609, 36.023099, 37.000459, + 37.990710, 38.993877, 40.009989, 41.039080, 42.081190, 43.136361, 44.204639, 45.286075, + 46.380723, 47.488640, 48.609885, 49.744520, 50.892609, 52.054217, 53.229410, 54.418254, + 55.620816, 56.837160, 58.067351, 59.311450, 60.569517, 61.841607, 63.127770, 64.428053, + 65.742497, 67.071133, 68.413988, 69.771079, 71.142412, 72.527985, 73.927782, 75.341776, + 76.769925, 78.212172, 79.668444, 81.138652, 82.622687, 84.120420, 85.631702, 87.156361, + 88.694200, 90.244998, 91.808507, 93.384452, 94.972526, 96.572391, 98.183679, 99.805985, + 101.438869, 103.081852, 104.734417, 106.396007, 108.066018, 109.743805, 111.428675, 113.119886, + 114.816648, 116.518116, 118.223392, 119.931522, 121.641492, 123.352230, 125.062599, 127.000000, +}; + +double sc_pan_table[129] = +{ + 0.000000, 0.000000, 0.999479, 2.011744, 3.036530, 4.073569, 5.122593, 6.183332, + 7.255517, 8.338874, 9.433131, 10.538014, 11.653247, 12.778552, 13.913653, 15.058271, + 16.212123, 17.374930, 18.546409, 19.726275, 20.914243, 22.110027, 23.313339, 24.523890, + 25.741391, 26.965550, 28.196074, 29.432671, 30.675045, 31.922900, 33.175939, 34.433863, + 35.696373, 36.963168, 38.233946, 39.508403, 40.786235, 42.067137, 43.350800, 44.636918, + 45.925181, 47.215278, 48.506897, 49.799726, 51.093451, 52.387755, 53.682323, 54.976837, + 56.270977, 57.564424, 58.856855, 60.147950, 61.437382, 62.724829, 64.009963, 65.292456, + 66.571981, 67.848208, 69.120804, 70.389439, 71.653778, 72.913487, 74.168230, 75.417670, + 76.661468, 77.899286, 79.130781, 80.355614, 81.573439, 82.783913, 83.986691, 85.181425, + 86.367767, 87.545369, 88.713880, 89.872949, 91.022222, 92.161346, 93.289965, 94.407723, + 95.514263, 96.609225, 97.692249, 98.762975, 99.821039, 100.866079, 101.897729, 102.915623, + 103.919394, 104.908673, 105.883091, 106.842276, 107.785858, 108.713461, 109.624713, 110.519236, + 111.396655, 112.256590, 113.098663, 113.922493, 114.727699, 115.513896, 116.280702, 117.027730, + 117.754595, 118.460908, 119.146280, 119.810321, 120.452639, 121.072843, 121.670538, 122.245328, + 122.796819, 123.324612, 123.828308, 124.307509, 124.761812, 125.190815, 125.594115, 125.971307, + 126.321986, 126.645744, 126.942172, 127.210862, 127.451402, 127.663381, 127.846385, 128.000000, + 128.000000, +}; + +double gm2_pan_table[129]; +double *pan_table = sc_pan_table; + +void init_gm2_pan_table(void) +{ + int i; + + gm2_pan_table[0] = 0; + for(i = 0; i < 127; i++) + gm2_pan_table[i + 1] = sin(M_PI / 2 * i / 126) * 128; + /* lookup_sine(i * SINE_CYCLE_LENGTH / 4 / 126) */ + gm2_pan_table[128] = 128.0; +} + +double sc_drum_level_table[128] = +{ + 0.007874, 0.007874, 0.031496, 0.070866, 0.125984, 0.196850, 0.283465, 0.385827, + 0.503937, 0.637795, 0.787402, 0.952756, 1.133858, 1.330709, 1.543307, 1.771654, + 2.015748, 2.275591, 2.551181, 2.842520, 3.149606, 3.472441, 3.811024, 4.165354, + 4.535433, 4.921260, 5.322835, 5.740157, 6.173228, 6.622047, 7.086614, 7.566929, + 8.062992, 8.574803, 9.102362, 9.645669, 10.204724, 10.779528, 11.370079, 11.976378, + 12.598425, 13.236220, 13.889764, 14.559055, 15.244094, 15.944882, 16.661417, 17.393701, + 18.141732, 18.905512, 19.685039, 20.480315, 21.291339, 22.118110, 22.960630, 23.818898, + 24.692913, 25.582677, 26.488189, 27.409449, 28.346457, 29.299213, 30.267717, 31.251969, + 32.251969, 33.267717, 34.299213, 35.346457, 36.409449, 37.488189, 38.582677, 39.692913, + 40.818898, 41.960630, 43.118110, 44.291339, 45.480315, 46.685039, 47.905512, 49.141732, + 50.393701, 51.661417, 52.944882, 54.244094, 55.559055, 56.889764, 58.236220, 59.598425, + 60.976378, 62.370079, 63.779528, 65.204724, 66.645669, 68.102362, 69.574803, 71.062992, + 72.566929, 74.086614, 75.622047, 77.173228, 78.740157, 80.322835, 81.921260, 83.535433, + 85.165354, 86.811024, 88.472441, 90.149606, 91.842520, 93.551181, 95.275591, 97.015748, + 98.771654, 100.543307, 102.330709, 104.133858, 105.952756, 107.787402, 109.637795, 111.503937, + 113.385827, 115.283465, 117.196850, 119.125984, 121.070866, 123.031496, 125.007874, 127.000000, +}; + +double attack_vol_table[1024]; + +void init_attack_vol_table(void) +{ + int i; + + for (i = 0; i < 1024; i++) + attack_vol_table[i] = i / 1023.0; +} + +float sc_eg_decay_table[128] = +{ + 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, + 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, + 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 81.841218f, 74.928977f, 68.580327f, + 62.750584f, 57.398521f, 52.486110f, 47.978275f, 43.842671f, 40.049475f, 36.571192f, 33.382477f, + 30.459975f, 27.782161f, 25.329207f, 23.082845f, 21.026252f, 19.143935f, 17.421629f, 15.846202f, + 14.405568f, 13.088604f, 11.885077f, 10.785570f, 9.781425f, 8.864676f, 8.028000f, 7.264663f, + 6.568475f, 5.933745f, 5.355241f, 4.828153f, 4.348058f, 3.910885f, 3.512890f, 3.150618f, + 2.820877f, 2.520709f, 2.247348f, 1.998183f, 1.770681f, 1.562261f, 1.369978f, 1.189386f, + 1.000000f, 0.838459f, 0.726301f, 0.635581f, 0.559656f, 0.494986f, 0.439286f, 0.390934f, + 0.348712f, 0.311669f, 0.279045f, 0.250221f, 0.224684f, 0.202006f, 0.181825f, 0.163831f, + 0.147761f, 0.133387f, 0.120513f, 0.108967f, 0.098600f, 0.089282f, 0.080897f, 0.073346f, + 0.066540f, 0.060399f, 0.054854f, 0.049845f, 0.045315f, 0.041217f, 0.037506f, 0.034144f, + 0.031097f, 0.028333f, 0.025826f, 0.023549f, 0.021480f, 0.019601f, 0.017892f, 0.016337f, + 0.014923f, 0.013635f, 0.012462f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, + 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, + 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, 0.011394f, +}; + +float sc_eg_release_table[128] = +{ + 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, + 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, + 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 27.322002f, 25.299110f, 23.425992f, + 21.691556f, 20.085537f, 18.598425f, 17.221418f, 15.946363f, 14.765711f, 13.672474f, 12.660179f, + 11.722833f, 10.854887f, 10.051203f, 9.307023f, 8.617941f, 7.979878f, 7.389056f, 6.841978f, + 6.335406f, 5.866339f, 5.432002f, 5.029822f, 4.657419f, 4.312589f, 3.993290f, 3.697631f, + 3.423862f, 3.170363f, 2.935633f, 2.718282f, 2.517023f, 2.330665f, 2.158106f, 1.998322f, + 1.850368f, 1.713369f, 1.586513f, 1.469049f, 1.360282f, 1.259569f, 1.166311f, 1.079959f, + 1.000000f, 0.925961f, 0.857404f, 0.793923f, 0.735141f, 0.680712f, 0.630313f, 0.583645f, + 0.540433f, 0.500420f, 0.463369f, 0.429062f, 0.397295f, 0.367879f, 0.340642f, 0.315421f, + 0.292068f, 0.270443f, 0.250420f, 0.231879f, 0.214711f, 0.198814f, 0.184094f, 0.170464f, + 0.157843f, 0.146157f, 0.135335f, 0.125315f, 0.116037f, 0.107446f, 0.099491f, 0.092124f, + 0.085304f, 0.078988f, 0.073140f, 0.067724f, 0.062710f, 0.058067f, 0.053768f, 0.049787f, + 0.046101f, 0.042688f, 0.039527f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, + 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, + 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, 0.036601f, +}; + +float sc_eg_attack_table[128] = +{ + 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, + 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, + 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 82.756924f, 75.473398f, 68.815182f, + 62.729632f, 57.168464f, 52.087395f, 47.445819f, 43.206507f, 39.335325f, 35.800987f, 32.574817f, + 29.630534f, 26.944060f, 24.493331f, 22.258137f, 20.219967f, 18.361866f, 16.668311f, 15.125088f, + 13.719184f, 12.438688f, 11.272700f, 10.211246f, 9.245197f, 8.366205f, 7.566631f, 6.839489f, + 6.178391f, 5.577493f, 5.031451f, 4.535378f, 4.084805f, 3.675641f, 3.304143f, 2.966879f, + 2.660703f, 2.382715f, 2.130237f, 1.900768f, 1.691929f, 1.501374f, 1.326560f, 1.163993f, + 1.000000f, 0.859112f, 0.753830f, 0.666057f, 0.591041f, 0.526103f, 0.469431f, 0.419689f, + 0.375841f, 0.337054f, 0.302650f, 0.272061f, 0.244810f, 0.220489f, 0.198750f, 0.179292f, + 0.161854f, 0.146210f, 0.132159f, 0.119529f, 0.108164f, 0.097931f, 0.088710f, 0.080394f, + 0.072891f, 0.066115f, 0.059994f, 0.054461f, 0.049456f, 0.044927f, 0.040827f, 0.037114f, + 0.033749f, 0.030699f, 0.027932f, 0.025422f, 0.023145f, 0.021077f, 0.019199f, 0.017492f, + 0.015941f, 0.014532f, 0.013250f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, + 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, + 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, 0.012084f, +}; + +double sb_vol_table[1024]; + +void init_sb_vol_table(void) +{ + int i; + + for (i = 0; i < 1024; i++) + sb_vol_table[i] = pow(10.0, (double)(1023 - i) * 960.0 / (1023.0 * -200.0)); +} + +double modenv_vol_table[1024]; + +void init_modenv_vol_table(void) +{ + int i; + double x; + + modenv_vol_table[0] = (float)0; + for (i = 1; i < 1023; i++) { + x = (1.0 - (-20.0 / 96.0 * log(((double)i * (double)i) / (1023.0 * 1023.0)) / log(10.0))); + if (x < 0) {x = 0;} + modenv_vol_table[i] = log(x + 1) / log(2); + } + modenv_vol_table[1023] = (float)1.0; +} + +const float cb_to_amp_table[961] = +{ + 1.000000f, 0.995677f, 0.991373f, 0.987088f, 0.982821f, 0.978572f, 0.974342f, 0.970130f, + 0.965936f, 0.961761f, 0.957603f, 0.953464f, 0.949342f, 0.945238f, 0.941152f, 0.937084f, + 0.933033f, 0.929000f, 0.924984f, 0.920985f, 0.917004f, 0.913040f, 0.909093f, 0.905163f, + 0.901250f, 0.897355f, 0.893475f, 0.889613f, 0.885768f, 0.881939f, 0.878126f, 0.874330f, + 0.870551f, 0.866787f, 0.863040f, 0.859310f, 0.855595f, 0.851896f, 0.848214f, 0.844547f, + 0.840896f, 0.837261f, 0.833642f, 0.830038f, 0.826450f, 0.822878f, 0.819321f, 0.815779f, + 0.812252f, 0.808741f, 0.805245f, 0.801764f, 0.798298f, 0.794848f, 0.791412f, 0.787990f, + 0.784584f, 0.781192f, 0.777816f, 0.774453f, 0.771105f, 0.767772f, 0.764453f, 0.761149f, + 0.757858f, 0.754582f, 0.751320f, 0.748072f, 0.744839f, 0.741619f, 0.738413f, 0.735221f, + 0.732043f, 0.728878f, 0.725728f, 0.722590f, 0.719467f, 0.716357f, 0.713260f, 0.710177f, + 0.707107f, 0.704050f, 0.701007f, 0.697976f, 0.694959f, 0.691955f, 0.688964f, 0.685986f, + 0.683020f, 0.680068f, 0.677128f, 0.674201f, 0.671286f, 0.668384f, 0.665495f, 0.662618f, + 0.659754f, 0.656902f, 0.654062f, 0.651235f, 0.648420f, 0.645617f, 0.642826f, 0.640047f, + 0.637280f, 0.634525f, 0.631783f, 0.629051f, 0.626332f, 0.623625f, 0.620929f, 0.618245f, + 0.615572f, 0.612911f, 0.610262f, 0.607624f, 0.604997f, 0.602382f, 0.599778f, 0.597185f, + 0.594604f, 0.592033f, 0.589474f, 0.586926f, 0.584389f, 0.581862f, 0.579347f, 0.576843f, + 0.574349f, 0.571866f, 0.569394f, 0.566933f, 0.564482f, 0.562042f, 0.559612f, 0.557193f, + 0.554785f, 0.552387f, 0.549999f, 0.547621f, 0.545254f, 0.542897f, 0.540550f, 0.538213f, + 0.535887f, 0.533570f, 0.531264f, 0.528967f, 0.526681f, 0.524404f, 0.522137f, 0.519880f, + 0.517632f, 0.515395f, 0.513167f, 0.510949f, 0.508740f, 0.506541f, 0.504351f, 0.502171f, + 0.500000f, 0.497839f, 0.495687f, 0.493544f, 0.491410f, 0.489286f, 0.487171f, 0.485065f, + 0.482968f, 0.480880f, 0.478802f, 0.476732f, 0.474671f, 0.472619f, 0.470576f, 0.468542f, + 0.466516f, 0.464500f, 0.462492f, 0.460493f, 0.458502f, 0.456520f, 0.454547f, 0.452582f, + 0.450625f, 0.448677f, 0.446738f, 0.444807f, 0.442884f, 0.440969f, 0.439063f, 0.437165f, + 0.435275f, 0.433394f, 0.431520f, 0.429655f, 0.427798f, 0.425948f, 0.424107f, 0.422274f, + 0.420448f, 0.418631f, 0.416821f, 0.415019f, 0.413225f, 0.411439f, 0.409660f, 0.407889f, + 0.406126f, 0.404371f, 0.402623f, 0.400882f, 0.399149f, 0.397424f, 0.395706f, 0.393995f, + 0.392292f, 0.390596f, 0.388908f, 0.387227f, 0.385553f, 0.383886f, 0.382227f, 0.380574f, + 0.378929f, 0.377291f, 0.375660f, 0.374036f, 0.372419f, 0.370809f, 0.369207f, 0.367611f, + 0.366021f, 0.364439f, 0.362864f, 0.361295f, 0.359733f, 0.358178f, 0.356630f, 0.355088f, + 0.353553f, 0.352025f, 0.350503f, 0.348988f, 0.347480f, 0.345977f, 0.344482f, 0.342993f, + 0.341510f, 0.340034f, 0.338564f, 0.337100f, 0.335643f, 0.334192f, 0.332748f, 0.331309f, + 0.329877f, 0.328451f, 0.327031f, 0.325617f, 0.324210f, 0.322808f, 0.321413f, 0.320024f, + 0.318640f, 0.317263f, 0.315891f, 0.314526f, 0.313166f, 0.311812f, 0.310464f, 0.309122f, + 0.307786f, 0.306456f, 0.305131f, 0.303812f, 0.302499f, 0.301191f, 0.299889f, 0.298593f, + 0.297302f, 0.296017f, 0.294737f, 0.293463f, 0.292194f, 0.290931f, 0.289674f, 0.288421f, + 0.287175f, 0.285933f, 0.284697f, 0.283466f, 0.282241f, 0.281021f, 0.279806f, 0.278597f, + 0.277392f, 0.276193f, 0.274999f, 0.273811f, 0.272627f, 0.271448f, 0.270275f, 0.269107f, + 0.267943f, 0.266785f, 0.265632f, 0.264484f, 0.263340f, 0.262202f, 0.261068f, 0.259940f, + 0.258816f, 0.257697f, 0.256583f, 0.255474f, 0.254370f, 0.253270f, 0.252175f, 0.251085f, + 0.250000f, 0.248919f, 0.247843f, 0.246772f, 0.245705f, 0.244643f, 0.243585f, 0.242533f, + 0.241484f, 0.240440f, 0.239401f, 0.238366f, 0.237336f, 0.236310f, 0.235288f, 0.234271f, + 0.233258f, 0.232250f, 0.231246f, 0.230246f, 0.229251f, 0.228260f, 0.227273f, 0.226291f, + 0.225313f, 0.224339f, 0.223369f, 0.222403f, 0.221442f, 0.220485f, 0.219532f, 0.218583f, + 0.217638f, 0.216697f, 0.215760f, 0.214827f, 0.213899f, 0.212974f, 0.212053f, 0.211137f, + 0.210224f, 0.209315f, 0.208411f, 0.207510f, 0.206613f, 0.205719f, 0.204830f, 0.203945f, + 0.203063f, 0.202185f, 0.201311f, 0.200441f, 0.199575f, 0.198712f, 0.197853f, 0.196998f, + 0.196146f, 0.195298f, 0.194454f, 0.193613f, 0.192776f, 0.191943f, 0.191113f, 0.190287f, + 0.189465f, 0.188646f, 0.187830f, 0.187018f, 0.186210f, 0.185405f, 0.184603f, 0.183805f, + 0.183011f, 0.182220f, 0.181432f, 0.180648f, 0.179867f, 0.179089f, 0.178315f, 0.177544f, + 0.176777f, 0.176013f, 0.175252f, 0.174494f, 0.173740f, 0.172989f, 0.172241f, 0.171496f, + 0.170755f, 0.170017f, 0.169282f, 0.168550f, 0.167822f, 0.167096f, 0.166374f, 0.165655f, + 0.164938f, 0.164225f, 0.163516f, 0.162809f, 0.162105f, 0.161404f, 0.160706f, 0.160012f, + 0.159320f, 0.158631f, 0.157946f, 0.157263f, 0.156583f, 0.155906f, 0.155232f, 0.154561f, + 0.153893f, 0.153228f, 0.152565f, 0.151906f, 0.151249f, 0.150595f, 0.149944f, 0.149296f, + 0.148651f, 0.148008f, 0.147368f, 0.146731f, 0.146097f, 0.145466f, 0.144837f, 0.144211f, + 0.143587f, 0.142967f, 0.142349f, 0.141733f, 0.141121f, 0.140511f, 0.139903f, 0.139298f, + 0.138696f, 0.138097f, 0.137500f, 0.136905f, 0.136313f, 0.135724f, 0.135138f, 0.134553f, + 0.133972f, 0.133393f, 0.132816f, 0.132242f, 0.131670f, 0.131101f, 0.130534f, 0.129970f, + 0.129408f, 0.128849f, 0.128292f, 0.127737f, 0.127185f, 0.126635f, 0.126088f, 0.125543f, + 0.125000f, 0.124460f, 0.123922f, 0.123386f, 0.122853f, 0.122322f, 0.121793f, 0.121266f, + 0.120742f, 0.120220f, 0.119700f, 0.119183f, 0.118668f, 0.118155f, 0.117644f, 0.117135f, + 0.116629f, 0.116125f, 0.115623f, 0.115123f, 0.114626f, 0.114130f, 0.113637f, 0.113145f, + 0.112656f, 0.112169f, 0.111684f, 0.111202f, 0.110721f, 0.110242f, 0.109766f, 0.109291f, + 0.108819f, 0.108348f, 0.107880f, 0.107414f, 0.106949f, 0.106487f, 0.106027f, 0.105568f, + 0.105112f, 0.104658f, 0.104205f, 0.103755f, 0.103306f, 0.102860f, 0.102415f, 0.101972f, + 0.101532f, 0.101093f, 0.100656f, 0.100221f, 0.099787f, 0.099356f, 0.098926f, 0.098499f, + 0.098073f, 0.097649f, 0.097227f, 0.096807f, 0.096388f, 0.095972f, 0.095557f, 0.095144f, + 0.094732f, 0.094323f, 0.093915f, 0.093509f, 0.093105f, 0.092702f, 0.092302f, 0.091903f, + 0.091505f, 0.091110f, 0.090716f, 0.090324f, 0.089933f, 0.089545f, 0.089158f, 0.088772f, + 0.088388f, 0.088006f, 0.087626f, 0.087247f, 0.086870f, 0.086494f, 0.086120f, 0.085748f, + 0.085378f, 0.085008f, 0.084641f, 0.084275f, 0.083911f, 0.083548f, 0.083187f, 0.082827f, + 0.082469f, 0.082113f, 0.081758f, 0.081404f, 0.081052f, 0.080702f, 0.080353f, 0.080006f, + 0.079660f, 0.079316f, 0.078973f, 0.078631f, 0.078292f, 0.077953f, 0.077616f, 0.077281f, + 0.076947f, 0.076614f, 0.076283f, 0.075953f, 0.075625f, 0.075298f, 0.074972f, 0.074648f, + 0.074325f, 0.074004f, 0.073684f, 0.073366f, 0.073049f, 0.072733f, 0.072418f, 0.072105f, + 0.071794f, 0.071483f, 0.071174f, 0.070867f, 0.070560f, 0.070255f, 0.069952f, 0.069649f, + 0.069348f, 0.069048f, 0.068750f, 0.068453f, 0.068157f, 0.067862f, 0.067569f, 0.067277f, + 0.066986f, 0.066696f, 0.066408f, 0.066121f, 0.065835f, 0.065550f, 0.065267f, 0.064985f, + 0.064704f, 0.064424f, 0.064146f, 0.063869f, 0.063592f, 0.063318f, 0.063044f, 0.062771f, + 0.062500f, 0.062230f, 0.061961f, 0.061693f, 0.061426f, 0.061161f, 0.060896f, 0.060633f, + 0.060371f, 0.060110f, 0.059850f, 0.059591f, 0.059334f, 0.059077f, 0.058822f, 0.058568f, + 0.058315f, 0.058062f, 0.057811f, 0.057562f, 0.057313f, 0.057065f, 0.056818f, 0.056573f, + 0.056328f, 0.056085f, 0.055842f, 0.055601f, 0.055360f, 0.055121f, 0.054883f, 0.054646f, + 0.054409f, 0.054174f, 0.053940f, 0.053707f, 0.053475f, 0.053244f, 0.053013f, 0.052784f, + 0.052556f, 0.052329f, 0.052103f, 0.051877f, 0.051653f, 0.051430f, 0.051208f, 0.050986f, + 0.050766f, 0.050546f, 0.050328f, 0.050110f, 0.049894f, 0.049678f, 0.049463f, 0.049249f, + 0.049037f, 0.048825f, 0.048613f, 0.048403f, 0.048194f, 0.047986f, 0.047778f, 0.047572f, + 0.047366f, 0.047161f, 0.046958f, 0.046755f, 0.046552f, 0.046351f, 0.046151f, 0.045951f, + 0.045753f, 0.045555f, 0.045358f, 0.045162f, 0.044967f, 0.044772f, 0.044579f, 0.044386f, + 0.044194f, 0.044003f, 0.043813f, 0.043624f, 0.043435f, 0.043247f, 0.043060f, 0.042874f, + 0.042689f, 0.042504f, 0.042320f, 0.042138f, 0.041955f, 0.041774f, 0.041593f, 0.041414f, + 0.041235f, 0.041056f, 0.040879f, 0.040702f, 0.040526f, 0.040351f, 0.040177f, 0.040003f, + 0.039830f, 0.039658f, 0.039486f, 0.039316f, 0.039146f, 0.038977f, 0.038808f, 0.038640f, + 0.038473f, 0.038307f, 0.038141f, 0.037976f, 0.037812f, 0.037649f, 0.037486f, 0.037324f, + 0.037163f, 0.037002f, 0.036842f, 0.036683f, 0.036524f, 0.036366f, 0.036209f, 0.036053f, + 0.035897f, 0.035742f, 0.035587f, 0.035433f, 0.035280f, 0.035128f, 0.034976f, 0.034825f, + 0.034674f, 0.034524f, 0.034375f, 0.034226f, 0.034078f, 0.033931f, 0.033784f, 0.033638f, + 0.033493f, 0.033348f, 0.033204f, 0.033060f, 0.032918f, 0.032775f, 0.032634f, 0.032492f, + 0.032352f, 0.032212f, 0.032073f, 0.031934f, 0.031796f, 0.031659f, 0.031522f, 0.031386f, + 0.031250f, 0.031115f, 0.030980f, 0.030846f, 0.030713f, 0.030580f, 0.030448f, 0.030317f, + 0.030186f, 0.030055f, 0.029925f, 0.029796f, 0.029667f, 0.029539f, 0.029411f, 0.029284f, + 0.029157f, 0.029031f, 0.028906f, 0.028781f, 0.028656f, 0.028533f, 0.028409f, 0.028286f, + 0.028164f, 0.028042f, 0.027921f, 0.027800f, 0.027680f, 0.027561f, 0.027441f, 0.027323f, + 0.027205f, 0.027087f, 0.026970f, 0.026853f, 0.026737f, 0.026622f, 0.026507f, 0.026392f, + 0.026278f, 0.026164f, 0.026051f, 0.025939f, 0.025827f, 0.025715f, 0.025604f, 0.025493f, + 0.025383f, 0.025273f, 0.025164f, 0.025055f, 0.024947f, 0.024839f, 0.024732f, 0.024625f, + 0.024518f, 0.024412f, 0.024307f, 0.024202f, 0.024097f, 0.023993f, 0.023889f, 0.023786f, + 0.023683f, 0.023581f, 0.023479f, 0.023377f, 0.023276f, 0.023176f, 0.023075f, 0.022976f, + 0.022876f, 0.022777f, 0.022679f, 0.022581f, 0.022483f, 0.022386f, 0.022289f, 0.022193f, + 0.022097f, 0.022002f, 0.021906f, 0.021812f, 0.021717f, 0.021624f, 0.021530f, 0.021437f, + 0.021344f, 0.021252f, 0.021160f, 0.021069f, 0.020978f, 0.020887f, 0.020797f, 0.020707f, + 0.020617f, 0.020528f, 0.020439f, 0.020351f, 0.020263f, 0.020176f, 0.020088f, 0.020001f, + 0.019915f, 0.019829f, 0.019743f, 0.019658f, 0.019573f, 0.019488f, 0.019404f, 0.019320f, + 0.019237f, 0.019153f, 0.019071f, 0.018988f, 0.018906f, 0.018824f, 0.018743f, 0.018662f, + 0.018581f, 0.018501f, 0.018421f, 0.018341f, 0.018262f, 0.018183f, 0.018105f, 0.018026f, + 0.017948f, 0.017871f, 0.017794f, 0.017717f, 0.017640f, 0.017564f, 0.017488f, 0.017412f, + 0.017337f, 0.017262f, 0.017187f, 0.017113f, 0.017039f, 0.016966f, 0.016892f, 0.016819f, + 0.016746f, 0.016674f, 0.016602f, 0.016530f, 0.016459f, 0.016388f, 0.016317f, 0.016246f, + 0.016176f, 0.016106f, 0.016036f, 0.015967f, 0.015898f, 0.015829f, 0.015761f, 0.015693f, + 0.015625f, +}; + +/* Reverb Time in sec */ +const float reverb_time_table[128] = +{ + 0.410349f, 0.440872f, 0.468882f, 0.494640f, 0.518394f, 0.540373f, 0.560793f, 0.579854f, + 0.597743f, 0.614635f, 0.630688f, 0.646053f, 0.660866f, 0.675251f, 0.689325f, 0.703192f, + 0.716947f, 0.730676f, 0.744456f, 0.758358f, 0.772441f, 0.786761f, 0.801365f, 0.816293f, + 0.831583f, 0.847262f, 0.863356f, 0.879886f, 0.896866f, 0.914308f, 0.932223f, 0.950614f, + 0.969484f, 0.988835f, 1.008663f, 1.028967f, 1.049741f, 1.070980f, 1.092677f, 1.114826f, + 1.137419f, 1.160450f, 1.183914f, 1.207803f, 1.232115f, 1.256845f, 1.281992f, 1.307556f, + 1.333540f, 1.359947f, 1.386784f, 1.414061f, 1.441788f, 1.469982f, 1.498661f, 1.527845f, + 1.557561f, 1.587836f, 1.618703f, 1.650199f, 1.682363f, 1.715240f, 1.748879f, 1.783333f, + 1.818659f, 1.854921f, 1.892183f, 1.930517f, 1.970001f, 2.010713f, 2.052741f, 2.096173f, + 2.141107f, 2.187641f, 2.235880f, 2.285935f, 2.337920f, 2.391955f, 2.448163f, 2.506674f, + 2.567622f, 2.631144f, 2.697384f, 2.766490f, 2.838612f, 2.913907f, 2.992536f, 3.074662f, + 3.160454f, 3.250085f, 3.343730f, 3.441570f, 3.543786f, 3.650566f, 3.762098f, 3.878575f, + 4.000192f, 4.127146f, 4.259638f, 4.397868f, 4.542042f, 4.692364f, 4.849041f, 5.012281f, + 5.182294f, 5.359289f, 5.543476f, 5.735064f, 5.934264f, 6.141286f, 6.356336f, 6.356336f, + 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, + 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, 6.356336f, +}; + +/* phase lag between left and right ear. (in ms) */ +const float pan_delay_table[128] = +{ + 0.000000f, 0.006136f, 0.012271f, 0.018404f, 0.024534f, 0.030660f, 0.036782f, 0.042899f, + 0.049009f, 0.055111f, 0.061205f, 0.067290f, 0.073365f, 0.079429f, 0.085481f, 0.091520f, + 0.097545f, 0.103556f, 0.109551f, 0.115529f, 0.121490f, 0.127433f, 0.133356f, 0.139260f, + 0.145142f, 0.151003f, 0.156841f, 0.162655f, 0.168445f, 0.174209f, 0.179948f, 0.185659f, + 0.191342f, 0.196996f, 0.202621f, 0.208215f, 0.213778f, 0.219308f, 0.224806f, 0.230269f, + 0.235698f, 0.241092f, 0.246449f, 0.251769f, 0.257051f, 0.262295f, 0.267499f, 0.272662f, + 0.277785f, 0.282866f, 0.287904f, 0.292899f, 0.297850f, 0.302756f, 0.307616f, 0.312430f, + 0.317197f, 0.321916f, 0.326586f, 0.331208f, 0.335779f, 0.340300f, 0.344770f, 0.349188f, + 0.353553f, 0.357865f, 0.362124f, 0.366327f, 0.370476f, 0.374568f, 0.378604f, 0.382584f, + 0.386505f, 0.390369f, 0.394173f, 0.397918f, 0.401604f, 0.405229f, 0.408792f, 0.412295f, + 0.415735f, 0.419112f, 0.422427f, 0.425678f, 0.428864f, 0.431986f, 0.435043f, 0.438035f, + 0.440961f, 0.443820f, 0.446612f, 0.449337f, 0.451995f, 0.454584f, 0.457105f, 0.459557f, + 0.461940f, 0.464253f, 0.466496f, 0.468670f, 0.470772f, 0.472804f, 0.474764f, 0.476653f, + 0.478470f, 0.480215f, 0.481888f, 0.483488f, 0.485016f, 0.486470f, 0.487851f, 0.489159f, + 0.490393f, 0.491553f, 0.492639f, 0.493651f, 0.494588f, 0.495451f, 0.496240f, 0.496953f, + 0.497592f, 0.498156f, 0.498645f, 0.499059f, 0.499398f, 0.499661f, 0.499849f, 0.500000f, +}; + +/* for 0dBf, 0.25dBf, 0.5dBf,...f, 24dB. */ +const float chamberlin_filter_db_to_q_table[97] = +{ + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.029207f, 1.113701f, 1.205132f, + 1.304068f, 1.411127f, 1.526975f, 1.652334f, 1.787984f, 1.934771f, 2.093608f, 2.265485f, + 2.451472f, 2.652729f, 2.870507f, 3.106165f, 3.361169f, 3.637108f, 3.935700f, 4.258806f, + 4.608437f, 4.986772f, 5.396167f, 5.839171f, 6.318544f, 6.837272f, 7.398585f, 8.005980f, + 8.663240f, 9.374459f, 10.144065f, 10.976853f, 11.878010f, 12.853149f, 13.908342f, 15.050163f, + 16.285723f, 17.622717f, 19.069474f, 20.635003f, 22.329057f, 24.162185f, 26.145807f, 28.292276f, + 30.614961f, 33.128330f, 35.848037f, 38.791022f, 41.975614f, 45.421648f, 49.150589f, 53.185661f, + 57.551996f, 62.276791f, 67.389473f, 72.921887f, 78.908490f, 85.386569f, 92.396474f, 99.981865f, + 108.189987f, 117.071964f, 126.683116f, 137.083307f, 148.337313f, 160.515229f, 173.692904f, 187.952416f, + 203.382577f, 220.079495f, 238.147165f, 257.698120f, 278.854132f, 301.746971f, 326.519223f, 353.325180f, + 382.331802f, +}; + +const uint8_t multi_eq_block_table_xg[] = +{ /* Gain1, Freq1, Q1, Shape1, Gain2, Freq2, Q2, Not Used, Gain3, Freq3, Q3, Not Used, + Gain4, Freq4, Q4, Not Used, Gain5, Freq5, Shape5 */ + 64, 12, 7, 0, 64, 28, 7, 0, 64, 34, 7, 0, 64, 46, 7, 0, 64, 52, 7, 0, /* Flat */ + 58, 8, 7, 0, 66, 16, 3, 0, 68, 33, 3, 0, 60, 44, 5, 0, 58, 50, 7, 0, /* Jazz */ + 68, 16, 7, 0, 60, 24, 20, 0, 67, 34, 7, 0, 60, 40, 20, 0, 70, 48, 7, 0, /* Pops */ + 71, 16, 7, 0, 68, 20, 7, 0, 60, 36, 5, 0, 68, 41, 10, 0, 66, 50, 7, 0, /* Rock */ + 67, 12, 7, 0, 68, 24, 7, 0, 64, 34, 5, 0, 66, 50, 7, 0, 61, 52, 7, 0, /* Concert */ +}; + +const float eq_freq_table_xg[] = +{ + 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, 63, 70, 80, 90, 100, 110, + 125, 140, 160, 180, 200, 225, 250, 280, 315, 355, 400, 450, 500, 560, 630, + 700, 800, 900, 1000, 1100, 1200, 1400, 1600, 1800, 2000, 2200, 2500, 2800, 3200, 3600, + 4000, 4500, 5000, 5600, 6300, 7000, 8000, 9000, 10000, 11000, 12000, 14000, 16000, 18000, 20000, +}; + +const float lfo_freq_table_xg[] = +{ + 0.00f, 0.04f, 0.08f, 0.13f, 0.17f, 0.21f, 0.25f, 0.29f, 0.34f, 0.38f, 0.42f, 0.46f, 0.51f, 0.55f, 0.59f, 0.63f, + 0.67f, 0.72f, 0.76f, 0.80f, 0.84f, 0.88f, 0.93f, 0.97f, 1.01f, 1.05f, 1.09f, 1.14f, 1.18f, 1.22f, 1.26f, 1.30f, + 1.35f, 1.39f, 1.43f, 1.47f, 1.51f, 1.56f, 1.60f, 1.64f, 1.68f, 1.72f, 1.77f, 1.81f, 1.85f, 1.89f, 1.94f, 1.98f, + 2.02f, 2.06f, 2.10f, 2.15f, 2.19f, 2.23f, 2.27f, 2.31f, 2.36f, 2.40f, 2.44f, 2.48f, 2.52f, 2.57f, 2.61f, 2.65f, + 2.69f, 2.78f, 2.86f, 2.94f, 3.03f, 3.11f, 3.20f, 2.28f, 3.37f, 3.45f, 3.53f, 3.62f, 3.70f, 3.87f, 4.04f, 4.21f, + 4.37f, 4.54f, 4.71f, 4.88f, 5.05f, 5.22f, 5.38f, 5.55f, 5.72f, 6.06f, 6.39f, 6.73f, 7.07f, 7.40f, 7.74f, 8.08f, + 8.41f, 8.75f, 9.08f, 9.42f, 9.76f, 10.1f, 10.8f, 11.4f, 12.1f, 12.8f, 13.5f, 14.1f, 14.8f, 15.5f, 16.2f, 16.8f, + 17.5f, 18.2f, 19.5f, 20.9f, 22.2f, 23.6f, 24.9f, 26.2f, 27.6f, 28.9f, 30.3f, 31.6f, 33.0f, 34.3f, 37.0f, 39.7f, +}; + +const float mod_delay_offset_table_xg[] = +{ + 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, + 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 2.5f, 2.6f, 2.7f, 2.8f, 2.9f, 3.0f, 3.1f, + 3.2f, 3.3f, 3.4f, 3.5f, 3.6f, 3.7f, 3.8f, 3.9f, 4.0f, 4.1f, 4.2f, 4.3f, 4.4f, 4.5f, 4.6f, 4.7f, + 4.8f, 4.9f, 5.0f, 5.1f, 5.2f, 5.3f, 5.4f, 5.5f, 5.6f, 5.7f, 5.8f, 5.9f, 6.0f, 6.1f, 6.2f, 6.3f, + 6.4f, 6.5f, 6.6f, 6.7f, 6.8f, 6.9f, 7.0f, 7.1f, 7.2f, 7.3f, 7.4f, 7.5f, 7.6f, 7.7f, 7.8f, 7.9f, + 8.0f, 8.1f, 8.2f, 8.3f, 8.4f, 8.5f, 8.6f, 8.7f, 8.8f, 8.9f, 9.0f, 9.1f, 9.2f, 9.3f, 9.4f, 9.5f, + 9.6f, 9.7f, 9.8f, 9.9f, 10.0f, 11.1f, 12.2f, 13.3f, 14.4f, 15.5f, 17.1f, 18.6f, 20.2f, 21.8f, 23.3f, 24.9f, + 26.5f, 28.0f, 29.6f, 31.2f, 32.8f, 34.3f, 35.9f, 37.5f, 39.0f, 40.6f, 42.2f, 43.7f, 45.3f, 46.9f, 48.4f, 50.0f, +}; + +const float reverb_time_table_xg[] = +{ + 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, + 1.9f, 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 2.5f, 2.6f, 2.7f, 2.8f, 2.9f, 3.0f, 3.1f, 3.2f, 3.3f, 3.4f, + 3.5f, 3.6f, 3.7f, 3.8f, 3.9f, 4.0f, 4.1f, 4.2f, 4.3f, 4.4f, 4.5f, 4.6f, 4.7f, 4.8f, 4.9f, 5.0f, + 5.5f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.5f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, + 17.0f, 18.0f, 19.0f, 20.0f, 25.0f, 30.0f, +}; + +const float delay_time_table_xg[] = +{ + 0.1f, 1.7f, 3.2f, 4.8f, 6.4f, 8.0f, 9.5f, 11.1f, 12.7f, 14.3f, 15.8f, 17.4f, 19.0f, 20.6f, 22.1f, 23.7f, + 25.3f, 26.9f, 28.4f, 30.0f, 31.6f, 33.2f, 34.7f, 36.3f, 37.9f, 39.5f, 41.0f, 42.6f, 44.2f, 45.7f, 47.3f, 48.9f, + 50.5f, 52.0f, 53.6f, 55.2f, 56.8f, 58.3f, 59.9f, 61.5f, 63.1f, 64.6f, 66.2f, 67.8f, 69.4f, 70.9f, 72.5f, 74.1f, + 75.7f, 77.2f, 78.8f, 80.4f, 81.9f, 83.5f, 85.1f, 86.7f, 88.2f, 89.8f, 91.4f, 93.0f, 94.5f, 96.1f, 97.7f, 99.3f, + 100.8f, 102.4f, 104.0f, 105.6f, 107.1f, 108.7f, 110.3f, 111.9f, 113.4f, 115.0f, 116.6f, 118.2f, 119.7f, 121.3f, 122.9f, 124.4f, + 126.0f, 127.6f, 129.2f, 130.7f, 132.3f, 133.9f, 135.5f, 137.0f, 138.6f, 140.2f, 141.8f, 143.3f, 144.9f, 146.5f, 148.1f, 149.6f, + 151.2f, 152.8f, 154.4f, 155.9f, 157.5f, 159.1f, 160.6f, 162.2f, 163.8f, 165.4f, 166.9f, 168.5f, 170.1f, 171.7f, 173.2f, 174.8f, + 176.4f, 178.0f, 179.5f, 181.1f, 182.7f, 184.3f, 185.8f, 187.4f, 189.0f, 190.6f, 192.1f, 193.7f, 195.3f, 196.9f, 198.4f, 200.0f, +}; + +const int16_t cutoff_freq_table_gs[] = +{ + 250, 250, 250, 250, 250, 250, 250, 250, + 315, 315, 315, 315, 315, 315, 315, 315, + 400, 400, 400, 400, 400, 400, 400, 400, + 500, 500, 500, 500, 500, 500, 500, 500, + 630, 630, 630, 630, 630, 630, 630, 630, + 800, 800, 800, 800, 800, 800, 800, 800, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250, + 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, + 3150, 3150, 3150, 3150, 3150, 3150, 3150, 3150, + 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, + 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, +}; + +const int16_t lpf_table_gs[] = +{ + 250, 250, 250, 250, 250, 250, 250, 250, + 315, 315, 315, 315, 315, 315, 315, 315, + 400, 400, 400, 400, 400, 400, 400, 400, + 500, 500, 500, 500, 500, 500, 500, 500, + 630, 630, 630, 630, 630, 630, 630, 630, + 800, 800, 800, 800, 800, 800, 800, 800, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250, + 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, + 3150, 3150, 3150, 3150, 3150, 3150, 3150, 3150, + 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, + 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +const int16_t eq_freq_table_gs[] = +{ + 200, 200, 200, 200, 200, 200, 200, 200, + 250, 250, 250, 250, 250, 250, 250, 250, + 315, 315, 315, 315, 315, 315, 315, 315, + 400, 400, 400, 400, 400, 400, 400, 400, + 500, 500, 500, 500, 500, 500, 500, 500, + 630, 630, 630, 630, 630, 630, 630, 630, + 800, 800, 800, 800, 800, 800, 800, 800, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250, + 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, + 3150, 3150, 3150, 3150, 3150, 3150, 3150, 3150, + 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, + 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, +}; + +const float lofi_sampling_freq_table_xg[] = +{ + 44100.0, 22100.0, 14700.0, 11000.0, 8800.0, 7400.0, 6300.0, 5500.0, + 4900.0, 4400.0, 4000.0, 3700.0, 3400.0, 3200.0, 2900.0, 2800.0, + 2600.0, 2500.0, 2300.0, 2200.0, 2100.0, 2000.0, 1920.0, 1840.0, + 1760.0, 1700.0, 1630.0, 1580.0, 1520.0, 1470.0, 1420.0, 1380.0, + 1340.0, 1300.0, 1260.0, 1230.0, 1190.0, 1160.0, 1130.0, 1110.0, + 1080.0, 1050.0, 1030.0, 1000.0, 980.0, 959.0, 938.0, 919.0, + 900.0, 882.0, 865.0, 848.0, 832.0, 817.0, 802.0, 788.0, + 774.0, 760.0, 747.0, 735.0, 723.0, 711.0, 700.0, 689.0, + 678.0, 668.0, 658.0, 649.0, 639.0, 630.0, 621.0, 613.0, + 604.0, 596.0, 588.0, 580.0, 573.0, 565.0, 558.0, 551.0, + 544.0, 538.0, 531.0, 525.0, 519.0, 513.0, 507.0, 501.0, + 496.0, 490.0, 485.0, 479.0, 474.0, 469.0, 464.0, 459.0, + 455.0, 450.0, 445.0, 441.0, 437.0, 432.0, 428.0, 424.0, + 420.0, 416.0, 412.0, 408.0, 405.0, 401.0, 397.0, 394.0, + 390.0, 387.0, 383.0, 380.0, 377.0, 374.0, 371.0, 368.0, + 364.0, 361.0, 359.0, 356.0, 353.0, 350.0, 347.0, 345.0, +}; + +void init_tables(void) +{ + // Only needs to be done once. + static bool done = false; + if (done) return; + done = true; + + init_freq_table(); + init_freq_table_tuning(); + init_freq_table_pytha(); + init_freq_table_meantone(); + init_freq_table_pureint(); + init_bend_fine(); + init_bend_coarse(); + init_triangular_table(); + init_gm2_pan_table(); + init_attack_vol_table(); + init_sb_vol_table(); + init_modenv_vol_table(); + init_def_vol_table(); + init_gs_vol_table(); + init_perceived_vol_table(); + init_gm2_vol_table(); +} + +int32_t get_note_freq(Sample *sp, int note) +{ + int32_t f; + int16_t sf, sn; + double ratio; + + f = freq_table[note]; + /* GUS/SF2 - Scale Tuning */ + if ((sf = sp->scale_factor) != 1024) { + sn = sp->scale_freq; + ratio = pow(2.0, (note - sn) * (sf - 1024) / 12288.0); + f = f * ratio + 0.5; + } + return f; +} +} \ No newline at end of file diff --git a/thirdparty/timidityplus/timiditypp/common.h b/thirdparty/timidityplus/timiditypp/common.h new file mode 100644 index 000000000..fbbd84f87 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/common.h @@ -0,0 +1,73 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + common.h +*/ + +#ifndef ___COMMON_H_ +#define ___COMMON_H_ + +#include +#include +#include +#include "../../../source/zmusic/fileio.h" + + +namespace TimidityPlus +{ +using timidity_file = MusicIO::FileInterface; + +inline char* tf_gets(char* buff, int n, timidity_file* tf) +{ + return tf->gets(buff, n); +} + +inline long tf_read(void* buff, int32_t size, timidity_file* tf) +{ + return (long)tf->read(buff, size); +} + +inline long tf_seek(timidity_file* tf, long offset, int whence) +{ + return (long)tf->seek(offset, whence); +} + +inline long tf_tell(timidity_file* tf) +{ + return (long)tf->tell(); +} + +extern timidity_file *open_file(const char *name, MusicIO::SoundFontReaderInterface *); +extern void tf_close(timidity_file *tf); +extern void skip(timidity_file *tf, size_t len); +int tf_getc(timidity_file *tf); +extern int int_rand(int n); /* random [0..n-1] */ +double flt_rand(); + +extern void *safe_malloc(size_t count); +extern void *safe_realloc(void *old_ptr, size_t new_size); +extern void *safe_large_malloc(size_t count); +extern char *safe_strdup(const char *s); +extern void free_ptr_list(void *ptr_list, int count); +extern int string_to_7bit_range(const char *s, int *start, int *end); +extern int load_table(char *file); + +} +#endif /* ___COMMON_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/controls.h b/thirdparty/timidityplus/timiditypp/controls.h new file mode 100644 index 000000000..6973ec088 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/controls.h @@ -0,0 +1,60 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + controls.h +*/ + +#ifndef ___CONTROLS_H_ +#define ___CONTROLS_H_ + + +namespace TimidityPlus +{ + +enum +{ + /* Return values for ControlMode.read */ + RC_ERROR = -1, + RC_OK = 0, + RC_QUIT = 1, + RC_TUNE_END = 3, + RC_STOP = 4, /* Stop to play */ + + CMSG_INFO = 10, + CMSG_WARNING = 50, + CMSG_ERROR = 100, + + VERB_NORMAL = 0, + VERB_VERBOSE = 1, + VERB_NOISY = 2, + VERB_DEBUG = 3, +}; + +inline bool RC_IS_SKIP_FILE(int rc) +{ + return ((rc) == RC_QUIT || (rc) == RC_ERROR || (rc) == RC_STOP || (rc) == RC_TUNE_END); +} + + +extern void (*printMessage)(int type, int verbosity_level, const char* fmt, ...); + + +} + +#endif /* ___CONTROLS_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/effect.h b/thirdparty/timidityplus/timiditypp/effect.h new file mode 100644 index 000000000..6d65d8e20 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/effect.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "timidity.h" + +namespace TimidityPlus +{ + +class Reverb; + +class Effect +{ + void effect_left_right_delay(int32_t *, int32_t); + void init_mtrand(void); + int32_t my_mod(int32_t, int32_t); + + int turn_counter = 0, tc = 0; + int status = 0; + double rate0 = 0, rate1 = 0, dr = 0; + int32_t prev[AUDIO_BUFFER_SIZE * 2] = { 0 }; + + Reverb *reverb; + +public: + Effect(Reverb *_reverb) + { + reverb = _reverb; + init_effect(); + } + + void init_effect(); + void do_effect(int32_t *buf, int32_t count); + +}; + +} \ No newline at end of file diff --git a/thirdparty/timidityplus/timiditypp/fft4g.h b/thirdparty/timidityplus/timiditypp/fft4g.h new file mode 100644 index 000000000..2465feae3 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/fft4g.h @@ -0,0 +1,16 @@ +/* + Copyright(C) 1996-1999 Takuya OOURA + email: ooura@mmm.t.u-tokyo.ac.jp + download: http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html + You may use, copy, modify this code for any purpose and + without fee. You may distribute this ORIGINAL package. +*/ +namespace TimidityPlus +{ + extern void cdft(int, int, float *, int *, float *); + extern void rdft(int, int, float *, int *, float *); + extern void ddct(int, int, float *, int *, float *); + extern void ddst(int, int, float *, int *, float *); + extern void dfct(int, float *, float *, int *, float *); + extern void dfst(int, float *, float *, int *, float *); +} \ No newline at end of file diff --git a/thirdparty/timidityplus/timiditypp/filter.h b/thirdparty/timidityplus/timiditypp/filter.h new file mode 100644 index 000000000..fd868a6cb --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/filter.h @@ -0,0 +1,46 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + filter.h : written by Vincent Pagel ( pagel@loria.fr ) + + implements fir antialiasing filter : should help when setting sample + rates as low as 8Khz. + + */ + +#ifndef ___FILTER_H_ +#define ___FILTER_H_ + +#include + + +namespace TimidityPlus +{ + +/* Order of the FIR filter = 20 should be enough ! */ +enum +{ + ORDER = 20, + ORDER2 = ORDER / 2 +}; + +void antialiasing(int16_t *data, int32_t data_length, int32_t sample_rate, int32_t output_rate); + +} +#endif /* ___FILTER_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/freq.h b/thirdparty/timidityplus/timiditypp/freq.h new file mode 100644 index 000000000..9f3bef4f6 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/freq.h @@ -0,0 +1,67 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include + +namespace TimidityPlus +{ + +extern const float pitch_freq_table[129]; +extern const float pitch_freq_ub_table[129]; +extern const float pitch_freq_lb_table[129]; +extern const int chord_table[4][3][3]; + +extern int assign_pitch_to_freq(float freq); + +enum +{ + CHORD_MAJOR = 0, + CHORD_MINOR = 3, + CHORD_DIM = 6, + CHORD_FIFTH = 9, + LOWEST_PITCH = 0, + HIGHEST_PITCH = 127 +}; + +struct Sample; + +class Freq +{ + std::vector floatData; + std::vector magData; + std::vector pruneMagData; + std::vector ipa; + std::vector wa; + std::vector fft1BinToPitch; + uint32_t oldfftsize = 0; + float pitchmags[129] = { 0 }; + double pitchbins[129] = { 0 }; + double new_pitchbins[129] = { 0 }; + + int assign_chord(double *pitchbins, int *chord, int min_guesspitch, int max_guesspitch, int root_pitch); + int freq_initialize_fft_arrays(Sample *sp); + +public: + + float freq_fourier(Sample *sp, int *chord); + +}; + +} \ No newline at end of file diff --git a/thirdparty/timidityplus/timiditypp/instrum.h b/thirdparty/timidityplus/timiditypp/instrum.h new file mode 100644 index 000000000..2497a63f0 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/instrum.h @@ -0,0 +1,564 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + instrum.h + + */ + +#ifndef ___INSTRUM_H_ +#define ___INSTRUM_H_ + +#include +#include "common.h" +#include "sysdep.h" +#include "sffile.h" +#include "sflayer.h" +#include "sfitem.h" +#include "../../../source/zmusic/fileio.h" + + +namespace TimidityPlus +{ + using timidity_file = MusicIO::FileInterface; + +enum +{ + READ_CONFIG_SUCCESS = 0, + READ_CONFIG_ERROR = 1, + READ_CONFIG_RECURSION = 2, /* Too much recursion */ + READ_CONFIG_FILE_NOT_FOUND = 3, /* Returned only w. allow_missing_file */ +}; + + +struct Sample +{ + splen_t + loop_start, loop_end, data_length; + int32_t + sample_rate, low_freq, high_freq, root_freq; + int8_t panning, note_to_use; + int32_t + envelope_rate[6], envelope_offset[6], + modenv_rate[6], modenv_offset[6]; + double + volume; + sample_t + *data; + int32_t + tremolo_sweep_increment, tremolo_phase_increment, + vibrato_sweep_increment, vibrato_control_ratio; + int16_t + tremolo_depth; + int16_t vibrato_depth; + uint8_t + modes, data_alloced, + low_vel, high_vel; + int32_t cutoff_freq; /* in Hz, [1, 20000] */ + int16_t resonance; /* in centibels, [0, 960] */ + /* in cents, [-12000, 12000] */ + int16_t tremolo_to_pitch, tremolo_to_fc, modenv_to_pitch, modenv_to_fc, + envelope_keyf[6], envelope_velf[6], modenv_keyf[6], modenv_velf[6], + vel_to_fc, key_to_fc; + int16_t vel_to_resonance; /* in centibels, [-960, 960] */ + int8_t envelope_velf_bpo, modenv_velf_bpo, + key_to_fc_bpo, vel_to_fc_threshold; /* in notes */ + int32_t vibrato_delay, tremolo_delay, envelope_delay, modenv_delay; /* in samples */ + int16_t scale_freq; /* in notes */ + int16_t scale_factor; /* in 1024divs/key */ + int8_t inst_type; + int32_t sf_sample_index, sf_sample_link; /* for stereo SoundFont */ + uint16_t sample_type; /* 1 = Mono, 2 = Right, 4 = Left, 8 = Linked, $8000 = ROM */ + double root_freq_detected; /* root freq from pitch detection */ + int transpose_detected; /* note offset from detected root */ + int chord; /* type of chord for detected pitch */ +}; + +/* Bits in modes: */ +enum +{ + MODES_16BIT = (1 << 0), + MODES_UNSIGNED = (1 << 1), + MODES_LOOPING = (1 << 2), + MODES_PINGPONG = (1 << 3), + MODES_REVERSE = (1 << 4), + MODES_SUSTAIN = (1 << 5), + MODES_ENVELOPE = (1 << 6), + MODES_CLAMPED = (1 << 7), /* ?? (for last envelope??) */ + + INST_GUS = 0, + INST_SF2 = 1, + INST_MOD = 2, + INST_PCM = 3, /* %sample */ + + /* sfSampleType */ + SF_SAMPLETYPE_MONO = 1, + SF_SAMPLETYPE_RIGHT = 2, + SF_SAMPLETYPE_LEFT = 4, + SF_SAMPLETYPE_LINKED = 8, + SF_SAMPLETYPE_ROM = 0x8000, +}; + +struct Instrument +{ + int type; + int samples; + Sample *sample; + char *instname; +}; + +struct ToneBankElement +{ + char *name; + char *comment; + Instrument *instrument; + int8_t note, pan, strip_loop, strip_envelope, strip_tail, loop_timeout, + font_preset, font_keynote, legato, tva_level, play_note, damper_mode; + uint8_t font_bank; + uint8_t instype; /* 0: Normal + 1: %font + 2: %sample + 3-255: reserved + */ + int16_t amp; + int16_t rnddelay; + int tunenum; + float *tune; + int sclnotenum; + int16_t *sclnote; + int scltunenum; + int16_t *scltune; + int fcnum; + int16_t *fc; + int resonum; + int16_t *reso; + int trempitchnum, tremfcnum, modpitchnum, modfcnum; + int16_t *trempitch, *tremfc, *modpitch, *modfc; + int envratenum, envofsnum; + int **envrate, **envofs; + int modenvratenum, modenvofsnum; + int **modenvrate, **modenvofs; + int envvelfnum, envkeyfnum; + int **envvelf, **envkeyf; + int modenvvelfnum, modenvkeyfnum; + int **modenvvelf, **modenvkeyf; + int tremnum, vibnum; + struct Quantity_ **trem, **vib; + int16_t vel_to_fc, key_to_fc, vel_to_resonance; + int8_t reverb_send, chorus_send, delay_send; +}; + +/* A hack to delay instrument loading until after reading the + entire MIDI file. */ +#define MAGIC_LOAD_INSTRUMENT ((Instrument *)(-1)) +#define MAGIC_ERROR_INSTRUMENT ((Instrument *)(-2)) +#define IS_MAGIC_INSTRUMENT(ip) ((ip) == MAGIC_LOAD_INSTRUMENT || (ip) == MAGIC_ERROR_INSTRUMENT) + +#define DYNAMIC_INSTRUMENT_NAME "" + +struct AlternateAssign +{ + /* 128 bit vector: + * bits[(note >> 5) & 0x3] & (1 << (note & 0x1F)) + */ + uint32_t bits[4]; + AlternateAssign* next; +}; + +struct ToneBank +{ + ToneBankElement tone[128]; + AlternateAssign *alt; +}; + +struct SpecialPatch /* To be used MIDI Module play mode */ +{ + int type; + int samples; + Sample *sample; + char *name; + int32_t sample_offset; +}; + +enum instrument_mapID +{ + INST_NO_MAP = 0, + SC_55_TONE_MAP, + SC_55_DRUM_MAP, + SC_88_TONE_MAP, + SC_88_DRUM_MAP, + SC_88PRO_TONE_MAP, + SC_88PRO_DRUM_MAP, + SC_8850_TONE_MAP, + SC_8850_DRUM_MAP, + XG_NORMAL_MAP, + XG_SFX64_MAP, + XG_SFX126_MAP, + XG_DRUM_MAP, + GM2_TONE_MAP, + GM2_DRUM_MAP, + NUM_INST_MAP +}; + +enum +{ + MAP_BANK_COUNT = 256, + NSPECIAL_PATCH = 256, + SPECIAL_PROGRAM = -1, + MAX_MREL = 5000, + DEFAULT_MREL = 800, +}; + +struct SFInsts; +struct InstList; +struct SampleList; +struct AIFFCommonChunk; +struct AIFFSoundDataChunk; +struct SampleImporter; + +class Instruments +{ + std::string configFileName; + MusicIO::SoundFontReaderInterface *sfreader; + + ToneBank standard_tonebank, standard_drumset; + + enum + { + INSTRUMENT_HASH_SIZE = 128, + }; + + struct InstrumentCache + { + char *name; + int panning, amp, note_to_use, strip_loop, strip_envelope, strip_tail; + Instrument *ip; + InstrumentCache *next; + }; + + InstrumentCache *instrument_cache[INSTRUMENT_HASH_SIZE] = { nullptr }; + + /* bank mapping (mapped bank) */ + struct bank_map_elem + { + int16_t used = 0, mapid = 0; + int bankno = 0; + }; + bank_map_elem map_bank[MAP_BANK_COUNT], map_drumset[MAP_BANK_COUNT]; + int map_bank_counter = 0; + + struct inst_map_elem + { + int set, elem, mapped; + }; + + inst_map_elem *inst_map_table[NUM_INST_MAP][128] = { { nullptr} }; + + struct UserInstrument + { + int8_t bank; + int8_t prog; + int8_t source_map; + int8_t source_bank; + int8_t source_prog; + int8_t vibrato_rate; + int8_t vibrato_depth; + int8_t cutoff_freq; + int8_t resonance; + int8_t env_attack; + int8_t env_decay; + int8_t env_release; + int8_t vibrato_delay; + UserInstrument *next; + }; + + UserInstrument *userinst_first = (UserInstrument *)NULL; + UserInstrument *userinst_last = (UserInstrument *)NULL; + + struct UserDrumset { + int8_t bank; + int8_t prog; + int8_t play_note; + int8_t level; + int8_t assign_group; + int8_t pan; + int8_t reverb_send_level; + int8_t chorus_send_level; + int8_t rx_note_off; + int8_t rx_note_on; + int8_t delay_send_level; + int8_t source_map; + int8_t source_prog; + int8_t source_note; + UserDrumset *next; + }; + + struct SFBags + { + int nbags; + uint16_t *bag; + int ngens; + SFGenRec *gen; + }; + + SFBags prbags, inbags; + + UserDrumset *userdrum_first = (UserDrumset *)NULL; + UserDrumset *userdrum_last = (UserDrumset *)NULL; + + AlternateAssign alt[2]; + + /* Some functions get aggravated if not even the standard banks are available. */ + ToneBank + *tonebank[128 + MAP_BANK_COUNT] = { &standard_tonebank }, + *drumset[128 + MAP_BANK_COUNT] = { &standard_drumset }; + + Instrument *default_instrument = 0; + SpecialPatch *special_patch[NSPECIAL_PATCH] = { nullptr }; + int default_program[MAX_CHANNELS] = { 0 }; /* This is only used for tracks that don't specify a program */ + + char *default_instrument_name = nullptr; + int progbase = 0; + int32_t modify_release = 0; + bool opt_sf_close_each_file = true; + char def_instr_name[256] = { '\0' }; + SFInsts *sfrecs = nullptr; + SFInsts *current_sfrec = nullptr; + + int last_sample_type = 0; + int last_sample_instrument = 0; + int last_sample_keyrange = 0; + SampleList *last_sample_list = nullptr; + + LayerItem layer_items[SF_EOF]; + + /* convert from 8bit value to fractional offset (15.15) */ + int32_t to_offset_22(int offset) + { + return (int32_t)offset << (7 + 15); + } + + int32_t calc_rate_i(int diff, double msec); + int32_t convert_envelope_rate(uint8_t rate); + int32_t convert_envelope_offset(uint8_t offset); + int32_t convert_tremolo_sweep(uint8_t sweep); + int32_t convert_vibrato_sweep(uint8_t sweep, int32_t vib_control_ratio); + int32_t convert_tremolo_rate(uint8_t rate); + int32_t convert_vibrato_rate(uint8_t rate); + void reverse_data(int16_t *sp, int32_t ls, int32_t le); + int name_hash(char *name); + Instrument *search_instrument_cache(char *name, int panning, int amp, int note_to_use, int strip_loop, int strip_envelope, int strip_tail); + void store_instrument_cache(Instrument *ip, char *name, int panning, int amp, int note_to_use, int strip_loop, int strip_envelope, int strip_tail); + int32_t to_rate(int rate); + void apply_bank_parameter(Instrument *ip, ToneBankElement *tone); + Instrument *load_gus_instrument(char *name, ToneBank *bank, int dr, int prog); + int fill_bank(int dr, int b, int *rc); + void free_tone_bank_list(ToneBank *tb[]); + void free_tone_bank(void); + void free_instrument_map(void); + int set_default_instrument(char *name); + void *safe_memdup(void *s, size_t size); + void MarkInstrument(int banknum, int percussion, int instr); + + //smplfile.c + Instrument *extract_sample_file(char *); + int32_t convert_envelope_rate_s(uint8_t rate); + void initialize_sample(Instrument *inst, int frames, int sample_bits, int sample_rate); + int get_importers(const char *sample_file, int limit, SampleImporter **importers); + int get_next_importer(char *sample_file, int start, int count, SampleImporter **importers); + + int import_wave_discriminant(char *sample_file); + int import_wave_load(char *sample_file, Instrument *inst); + int import_aiff_discriminant(char *sample_file); + int import_aiff_load(char *sample_file, Instrument *inst); + int read_AIFFCommonChunk(timidity_file *tf, AIFFCommonChunk *comm, int csize, int compressed); + int read_AIFFSoundData(timidity_file *tf, Instrument *inst, AIFFCommonChunk *common); + int read_AIFFSoundDataChunk(timidity_file *tf, AIFFSoundDataChunk *sound, int csize, int mode); + + // sndfont.cpp + + SFInsts *find_soundfont(char *sf_file); + SFInsts *new_soundfont(char *sf_file); + void init_sf(SFInsts *rec); + void end_soundfont(SFInsts *rec); + Instrument *try_load_soundfont(SFInsts *rec, int order, int bank, int preset, int keynote); + Instrument *load_from_file(SFInsts *rec, InstList *ip); + int is_excluded(SFInsts *rec, int bank, int preset, int keynote); + int is_ordered(SFInsts *rec, int bank, int preset, int keynote); + int load_font(SFInfo *sf, int pridx); + int parse_layer(SFInfo *sf, int pridx, LayerTable *tbl, int level); + int is_global(SFGenLayer *layer); + void clear_table(LayerTable *tbl); + void set_to_table(SFInfo *sf, LayerTable *tbl, SFGenLayer *lay, int level); + void add_item_to_table(LayerTable *tbl, int oper, int amount, int level); + void merge_table(SFInfo *sf, LayerTable *dst, LayerTable *src); + void init_and_merge_table(SFInfo *sf, LayerTable *dst, LayerTable *src); + int sanity_range(LayerTable *tbl); + int make_patch(SFInfo *sf, int pridx, LayerTable *tbl); + void make_info(SFInfo *sf, SampleList *vp, LayerTable *tbl); + double calc_volume(LayerTable *tbl); + void set_sample_info(SFInfo *sf, SampleList *vp, LayerTable *tbl); + void set_init_info(SFInfo *sf, SampleList *vp, LayerTable *tbl); + void reset_last_sample_info(void); + int abscent_to_Hz(int abscents); + void set_rootkey(SFInfo *sf, SampleList *vp, LayerTable *tbl); + void set_rootfreq(SampleList *vp); + int32_t to_offset(int32_t offset); + int32_t to_rate(int32_t diff, int timecent); + int32_t calc_rate(int32_t diff, double msec); + double to_msec(int timecent); + int32_t calc_sustain(int sust_cB); + void convert_volume_envelope(SampleList *vp, LayerTable *tbl); + void convert_tremolo(SampleList *vp, LayerTable *tbl); + void convert_vibrato(SampleList *vp, LayerTable *tbl); + void set_envelope_parameters(SampleList *vp); + + // configfile + + int set_patchconf(const char *name, int line, ToneBank *bank, char *w[], int dr, int mapid, int bankmapfrom, int bankno); + int strip_trailing_comment(char *string, int next_token_index); + char *expand_variables(char *string, MBlockList *varbuf, const char *basedir); + int set_gus_patchconf(const char *name, int line, ToneBankElement *tone, char *pat, char **opts); + void reinit_tone_bank_element(ToneBankElement *tone); + int set_gus_patchconf_opts(const char *name, int line, char *opts, ToneBankElement *tone); + int copymap(int mapto, int mapfrom, int isdrum); + void copybank(ToneBank *to, ToneBank *from, int mapid, int bankmapfrom, int bankno); + + // sffile.cpp + + int chunkid(char *id); + int process_list(int size, SFInfo *sf, timidity_file *fd); + int process_info(int size, SFInfo *sf, timidity_file *fd); + int process_sdta(int size, SFInfo *sf, timidity_file *fd); + int process_pdta(int size, SFInfo *sf, timidity_file *fd); + void load_sample_names(int size, SFInfo *sf, timidity_file *fd); + void load_preset_header(int size, SFInfo *sf, timidity_file *fd); + void load_inst_header(int size, SFInfo *sf, timidity_file *fd); + void load_bag(int size, SFBags *bagp, timidity_file *fd); + void load_gen(int size, SFBags *bagp, timidity_file *fd); + void load_sample_info(int size, SFInfo *sf, timidity_file *fd); + void convert_layers(SFInfo *sf); + void generate_layers(SFHeader *hdr, SFHeader *next, SFBags *bags); + void free_layer(SFHeader *hdr); + int load_soundfont(SFInfo *sf, timidity_file *fd); + void free_soundfont(SFInfo *sf); + void correct_samples(SFInfo *sf); + + +public: + + Instruments(); + bool load(MusicIO::SoundFontReaderInterface *); + ~Instruments(); + + const ToneBank *toneBank(int i) const + { + return tonebank[i]; + } + + int defaultProgram(int i) const + { + return default_program[i]; + } + + const ToneBank *drumSet(int i) const + { + return drumset[i]; + } + + const SpecialPatch *specialPatch(int i) const + { + return special_patch[i]; + } + + void setSpecialPatchOffset(int i, int32_t ofs) + { + special_patch[i]->sample_offset = ofs; + } + Instrument *defaultInstrument() const + { + return default_instrument; + } + + /* instrum.c */ + int load_missing_instruments(int *rc); + void free_instruments(int reload_default_inst); + void free_special_patch(int id); + void clear_magic_instruments(void); + Instrument *load_instrument(int dr, int b, int prog); + int find_instrument_map_bank(int dr, int map, int bk); + int alloc_instrument_map_bank(int dr, int map, int bk); + void alloc_instrument_bank(int dr, int bankset); + int instrument_map(int mapID, int *set_in_out, int *elem_in_out) const; + void set_instrument_map(int mapID, int set_from, int elem_from, int set_to, int elem_to); + AlternateAssign *add_altassign_string(AlternateAssign *old, char **params, int n); + AlternateAssign *find_altassign(AlternateAssign *altassign, int note); + void copy_tone_bank_element(ToneBankElement *elm, const ToneBankElement *src); + void free_tone_bank_element(ToneBankElement *elm); + void free_instrument(Instrument *ip); + void squash_sample_16to8(Sample *sp); + Instrument *play_midi_load_instrument(int dr, int bk, int prog, bool *pLoad_success); + void recompute_userinst(int bank, int prog); + Instrument *recompute_userdrum(int bank, int prog); + UserInstrument *get_userinst(int bank, int prog); + UserDrumset *get_userdrum(int bank, int prog); + void recompute_userdrum_altassign(int bank, int group); + /*! initialize GS user drumset. */ + void init_userdrum(); + void free_userdrum(); + void init_userinst() { free_userinst(); } + void free_userinst(); + + void mark_instrument(int newbank, int newprog) + { + if (!(tonebank[newbank]->tone[newprog].instrument)) + tonebank[newbank]->tone[newprog].instrument = + MAGIC_LOAD_INSTRUMENT; + } + + void mark_drumset(int newbank, int newprog) + { + if (!(drumset[newbank]->tone[newprog].instrument)) + drumset[newbank]->tone[newprog].instrument = + MAGIC_LOAD_INSTRUMENT; + } + + /* sndfont.c */ + void add_soundfont(char *sf_file, int sf_order, int cutoff_allowed, int resonance_allowed, int amp); + void remove_soundfont(char *sf_file); + void init_load_soundfont(void); + Instrument *load_soundfont_inst(int order, int bank, int preset, int keynote); + Instrument *extract_soundfont(char *sf_file, int bank, int preset, int keynote); + int exclude_soundfont(int bank, int preset, int keynote); + int order_soundfont(int bank, int preset, int keynote, int order); + char *soundfont_preset_name(int bank, int preset, int keynote, char **sndfile); + void free_soundfonts(void); + void PrecacheInstruments(const uint16_t *instruments, int count); + + + int read_config_file(const char *name, int self, int allow_missing_file); + + void set_default_instrument() + { + set_default_instrument(def_instr_name); + } +}; + + +} +#endif /* ___INSTRUM_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/mblock.h b/thirdparty/timidityplus/timiditypp/mblock.h new file mode 100644 index 000000000..379926666 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/mblock.h @@ -0,0 +1,78 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef ___MBLOCK_H_ +#define ___MBLOCK_H_ + +namespace TimidityPlus +{ + struct MBlockNode; + +/* Memory block for decreasing malloc + * + * +------+ +------+ +-------+ + * |BLOCK1|--->|BLOCK2|---> ... --->|BLOCK N|---> NULL + * +------+ +------+ +-------+ + * + * + * BLOCK: + * +-----------------------+ + * | memory 1 | + * | | + * +-----------------------+ + * | memory 2 | + * +-----------------------+ + * | memory 3 | + * | | + * | | + * +-----------------------+ + * | unused ... | + * +-----------------------+ + */ + + +#define MIN_MBLOCK_SIZE 8192 + +struct MBlockNode +{ + size_t block_size; + size_t offset; + MBlockNode *next; +#ifndef MBLOCK_NOPAD + void *pad; +#endif /* MBLOCK_NOPAD */ + char buffer[1]; +}; + +struct MBlockList +{ + MBlockNode *first; + size_t allocated; +}; + +extern void init_mblock(MBlockList *mblock); +extern void *new_segment(MBlockList *mblock, size_t nbytes); +extern void reuse_mblock(MBlockList *mblock); +extern char *strdup_mblock(MBlockList *mblock, const char *str); +extern int free_global_mblock(void); + +} +#endif /* ___MBLOCK_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/mix.h b/thirdparty/timidityplus/timiditypp/mix.h new file mode 100644 index 000000000..15c7cc775 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/mix.h @@ -0,0 +1,76 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + In case you haven't heard, this program is free software; + you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + mix.h + +*/ + +#ifndef ___MIX_H_ +#define ___MIX_H_ + +#include "resample.h" + +namespace TimidityPlus +{ + + +typedef int32_t mix_t; +class Player; + +class Mixer +{ + Player *player; + int32_t filter_buffer[AUDIO_BUFFER_SIZE]; + + int do_voice_filter(int, resample_t*, mix_t*, int32_t); + void recalc_voice_resonance(int); + void recalc_voice_fc(int); + void ramp_out(mix_t *, int32_t *, int, int32_t); + void mix_mono_signal(mix_t *, int32_t *, int, int); + void mix_mystery_signal(mix_t *, int32_t *, int, int); + void mix_mystery(mix_t *, int32_t *, int, int); + void mix_center_signal(mix_t *, int32_t *, int, int); + void mix_center(mix_t *, int32_t *, int, int); + void mix_single_signal(mix_t *, int32_t *, int, int); + void mix_single(mix_t *, int32_t *, int, int); + int update_signal(int); + int update_envelope(int); + int update_modulation_envelope(int); + void voice_ran_out(int); + int next_stage(int); + int modenv_next_stage(int); + void update_tremolo(int); + void compute_mix_smoothing(Voice *); + int get_eg_stage(int v, int stage); + +public: + Mixer(Player *p) + { + player = p; + } + void mix_voice(int32_t *, int, int32_t); + int recompute_envelope(int); + int apply_envelope_to_amp(int); + int recompute_modulation_envelope(int); + int apply_modulation_envelope(int); +}; + +} +#endif /* ___MIX_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/optcode.h b/thirdparty/timidityplus/timiditypp/optcode.h new file mode 100644 index 000000000..228021255 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/optcode.h @@ -0,0 +1,65 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef OPTCODE_H_INCLUDED +#define OPTCODE_H_INCLUDED 1 + +#include + +namespace TimidityPlus +{ + +/*****************************************************************************/ + + +/*****************************************************************************/ + +/* Generic version of imuldiv. */ +inline int32_t imuldiv8(int32_t a, int32_t b) +{ + return (int32_t)(((int64_t)(a) * (int64_t)(b)) >> 8); +} + +inline int32_t imuldiv16(int32_t a, int32_t b) +{ + return (int32_t)(((int64_t)(a) * (int64_t)(b)) >> 16); +} + +inline int32_t imuldiv24(int32_t a, int32_t b) +{ + return (int32_t)(((int64_t)(a) * (int64_t)(b)) >> 24); +} + +inline int32_t imuldiv28(int32_t a, int32_t b) +{ + return (int32_t)(((int64_t)(a) * (int64_t)(b)) >> 28); +} + + +static inline int32_t signlong(int32_t a) +{ + return ((a | 0x7fffffff) >> 30); +} + + +} +/*****************************************************************************/ + +#endif /* OPTCODE_H_INCLUDED */ diff --git a/thirdparty/timidityplus/timiditypp/playmidi.h b/thirdparty/timidityplus/timiditypp/playmidi.h new file mode 100644 index 000000000..797284bf6 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/playmidi.h @@ -0,0 +1,747 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + playmidi.h +*/ + +#ifndef ___PLAYMIDI_H_ +#define ___PLAYMIDI_H_ +#include + +namespace TimidityPlus +{ + +struct AlternateAssign; + +struct MidiEvent +{ + uint8_t type, channel, a, b; +}; + +#define REDUCE_CHANNELS 16 +#define REVERB_MAX_DELAY_OUT (4 * playback_rate) + +#define SYSEX_TAG 0xFF + +/* Midi events */ +enum midi_event_t +{ + ME_NONE, + + /* MIDI events */ + ME_NOTEOFF, + ME_NOTEON, + ME_KEYPRESSURE, + ME_PROGRAM, + ME_CHANNEL_PRESSURE, + ME_PITCHWHEEL, + + /* Controls */ + ME_TONE_BANK_MSB, + ME_TONE_BANK_LSB, + ME_MODULATION_WHEEL, + ME_BREATH, + ME_FOOT, + ME_MAINVOLUME, + ME_BALANCE, + ME_PAN, + ME_EXPRESSION, + ME_SUSTAIN, + ME_PORTAMENTO_TIME_MSB, + ME_PORTAMENTO_TIME_LSB, + ME_PORTAMENTO, + ME_PORTAMENTO_CONTROL, + ME_DATA_ENTRY_MSB, + ME_DATA_ENTRY_LSB, + ME_SOSTENUTO, + ME_SOFT_PEDAL, + ME_LEGATO_FOOTSWITCH, + ME_HOLD2, + ME_HARMONIC_CONTENT, + ME_RELEASE_TIME, + ME_ATTACK_TIME, + ME_BRIGHTNESS, + ME_REVERB_EFFECT, + ME_TREMOLO_EFFECT, + ME_CHORUS_EFFECT, + ME_CELESTE_EFFECT, + ME_PHASER_EFFECT, + ME_RPN_INC, + ME_RPN_DEC, + ME_NRPN_LSB, + ME_NRPN_MSB, + ME_RPN_LSB, + ME_RPN_MSB, + ME_ALL_SOUNDS_OFF, + ME_RESET_CONTROLLERS, + ME_ALL_NOTES_OFF, + ME_MONO, + ME_POLY, + + /* TiMidity Extensionals */ + ME_MASTER_TUNING, /* Master tuning */ + ME_SCALE_TUNING, /* Scale tuning */ + ME_BULK_TUNING_DUMP, /* Bulk tuning dump */ + ME_SINGLE_NOTE_TUNING, /* Single-note tuning */ + ME_RANDOM_PAN, + ME_SET_PATCH, /* Install special instrument */ + ME_DRUMPART, + ME_KEYSHIFT, + ME_PATCH_OFFS, /* Change special instrument sample position + * Channel, LSB, MSB + */ + + /* Global channel events */ + ME_TEMPO, + ME_CHORUS_TEXT, + ME_LYRIC, + ME_GSLCD, /* GS L.C.D. Exclusive message event */ + ME_MARKER, + ME_INSERT_TEXT, /* for SC */ + ME_TEXT, + ME_KARAOKE_LYRIC, /* for KAR format */ + ME_MASTER_VOLUME, + ME_RESET, /* Reset and change system mode */ + ME_NOTE_STEP, + + ME_TIMESIG, /* Time signature */ + ME_KEYSIG, /* Key signature */ + ME_TEMPER_KEYSIG, /* Temperament key signature */ + ME_TEMPER_TYPE, /* Temperament type */ + ME_MASTER_TEMPER_TYPE, /* Master temperament type */ + ME_USER_TEMPER_ENTRY, /* User-defined temperament entry */ + + ME_SYSEX_LSB, /* Universal system exclusive message (LSB) */ + ME_SYSEX_MSB, /* Universal system exclusive message (MSB) */ + ME_SYSEX_GS_LSB, /* GS system exclusive message (LSB) */ + ME_SYSEX_GS_MSB, /* GS system exclusive message (MSB) */ + ME_SYSEX_XG_LSB, /* XG system exclusive message (LSB) */ + ME_SYSEX_XG_MSB, /* XG system exclusive message (MSB) */ + + ME_WRD, /* for MIMPI WRD tracer */ + ME_SHERRY, /* for Sherry WRD tracer */ + ME_BARMARKER, + ME_STEP, /* for Metronome */ + + ME_LAST = 254, /* Last sequence of MIDI list. + * This event is reserved for realtime player. + */ + ME_EOT = 255 /* End of MIDI. Finish to play */ +}; + +#define GLOBAL_CHANNEL_EVENT_TYPE(type) \ + ((type) == ME_NONE || (type) >= ME_TEMPO) + +enum rpn_data_address_t /* NRPN/RPN */ +{ + NRPN_ADDR_0108, + NRPN_ADDR_0109, + NRPN_ADDR_010A, + NRPN_ADDR_0120, + NRPN_ADDR_0121, + NRPN_ADDR_0130, + NRPN_ADDR_0131, + NRPN_ADDR_0134, + NRPN_ADDR_0135, + NRPN_ADDR_0163, + NRPN_ADDR_0164, + NRPN_ADDR_0166, + NRPN_ADDR_1400, + NRPN_ADDR_1500, + NRPN_ADDR_1600, + NRPN_ADDR_1700, + NRPN_ADDR_1800, + NRPN_ADDR_1900, + NRPN_ADDR_1A00, + NRPN_ADDR_1C00, + NRPN_ADDR_1D00, + NRPN_ADDR_1E00, + NRPN_ADDR_1F00, + NRPN_ADDR_3000, + NRPN_ADDR_3100, + NRPN_ADDR_3400, + NRPN_ADDR_3500, + RPN_ADDR_0000, + RPN_ADDR_0001, + RPN_ADDR_0002, + RPN_ADDR_0003, + RPN_ADDR_0004, + RPN_ADDR_0005, + RPN_ADDR_7F7F, + RPN_ADDR_FFFF, + RPN_MAX_DATA_ADDR +}; + +#define RX_PITCH_BEND (1<<0) +#define RX_CH_PRESSURE (1<<1) +#define RX_PROGRAM_CHANGE (1<<2) +#define RX_CONTROL_CHANGE (1<<3) +#define RX_POLY_PRESSURE (1<<4) +#define RX_NOTE_MESSAGE (1<<5) +#define RX_RPN (1<<6) +#define RX_NRPN (1<<7) +#define RX_MODULATION (1<<8) +#define RX_VOLUME (1<<9) +#define RX_PANPOT (1<<10) +#define RX_EXPRESSION (1<<11) +#define RX_HOLD1 (1<<12) +#define RX_PORTAMENTO (1<<13) +#define RX_SOSTENUTO (1<<14) +#define RX_SOFT (1<<15) +#define RX_NOTE_ON (1<<16) +#define RX_NOTE_OFF (1<<17) +#define RX_BANK_SELECT (1<<18) +#define RX_BANK_SELECT_LSB (1<<19) + +enum { + EG_ATTACK = 0, + EG_DECAY = 2, + EG_DECAY1 = 1, + EG_DECAY2 = 2, + EG_RELEASE = 3, + EG_NULL = 5, + EG_GUS_ATTACK = 0, + EG_GUS_DECAY = 1, + EG_GUS_SUSTAIN = 2, + EG_GUS_RELEASE1 = 3, + EG_GUS_RELEASE2 = 4, + EG_GUS_RELEASE3 = 5, + EG_SF_ATTACK = 0, + EG_SF_HOLD = 1, + EG_SF_DECAY = 2, + EG_SF_RELEASE = 3, +}; + +#ifndef PART_EQ_XG +#define PART_EQ_XG +/*! shelving filter */ +struct filter_shelving +{ + double freq, gain, q; + int32_t x1l, x2l, y1l, y2l, x1r, x2r, y1r, y2r; + int32_t a1, a2, b0, b1, b2; +}; + +/*! Part EQ (XG) */ +struct part_eq_xg { + int8_t bass, treble, bass_freq, treble_freq; + filter_shelving basss, trebles; + int8_t valid; +}; +#endif /* PART_EQ_XG */ + +struct midi_controller +{ + int16_t val; + int8_t pitch; /* in +-semitones [-24, 24] */ + int16_t cutoff; /* in +-cents [-9600, 9600] */ + float amp; /* [-1.0, 1.0] */ + /* in GS, LFO1 means LFO for voice 1, LFO2 means LFO for voice2. + LFO2 is not supported. */ + float lfo1_rate, lfo2_rate; /* in +-Hz [-10.0, 10.0] */ + int16_t lfo1_pitch_depth, lfo2_pitch_depth; /* in cents [0, 600] */ + int16_t lfo1_tvf_depth, lfo2_tvf_depth; /* in cents [0, 2400] */ + float lfo1_tva_depth, lfo2_tva_depth; /* [0, 1.0] */ + int8_t variation_control_depth, insertion_control_depth; +}; + +struct DrumPartEffect +{ + int32_t *buf; + int8_t note, reverb_send, chorus_send, delay_send; +}; + +struct DrumParts +{ + int8_t drum_panning; + int32_t drum_envelope_rate[6]; /* drum instrument envelope */ + int8_t pan_random; /* flag for drum random pan */ + float drum_level; + + int8_t chorus_level, reverb_level, delay_level, coarse, fine, + play_note, drum_cutoff_freq, drum_resonance; + int32_t rx; +}; + +struct Channel +{ + int8_t bank_msb, bank_lsb, bank, program, volume, + expression, sustain, panning, mono, portamento, + key_shift, loop_timeout; + + /* chorus, reverb... Coming soon to a 300-MHz, eight-way superscalar + processor near you */ + int8_t chorus_level, /* Chorus level */ + reverb_level; /* Reverb level. */ + int reverb_id; /* Reverb ID used for reverb optimize implementation + >=0 reverb_level + -1: DEFAULT_REVERB_SEND_LEVEL + */ + int8_t delay_level; /* Delay Send Level */ + int8_t eq_gs; /* EQ ON/OFF (GS) */ + int8_t insertion_effect; + + /* Special sample ID. (0 means Normal sample) */ + uint8_t special_sample; + + int pitchbend; + + double + pitchfactor; /* precomputed pitch bend factor to save some fdiv's */ + + /* For portamento */ + uint8_t portamento_time_msb, portamento_time_lsb; + int porta_control_ratio, porta_dpb; + int32_t last_note_fine; + + /* For Drum part */ + struct DrumParts *drums[128]; + + /* For NRPN Vibrato */ + int32_t vibrato_depth, vibrato_delay; + float vibrato_ratio; + + /* For RPN */ + uint8_t rpnmap[RPN_MAX_DATA_ADDR]; /* pseudo RPN address map */ + uint8_t rpnmap_lsb[RPN_MAX_DATA_ADDR]; + uint8_t lastlrpn, lastmrpn; + int8_t nrpn; /* 0:RPN, 1:NRPN, -1:Undefined */ + int rpn_7f7f_flag; /* Boolean flag used for RPN 7F/7F */ + + /* For channel envelope */ + int32_t envelope_rate[6]; /* for Envelope Generator in mix.c + * 0: value for attack rate + * 2: value for decay rate + * 3: value for release rate + */ + + int mapID; /* Program map ID */ + AlternateAssign *altassign; /* Alternate assign patch table */ + int32_t lasttime; /* Last sample time of computed voice on this channel */ + + /* flag for random pan */ + int pan_random; + + /* for Voice LPF / Resonance */ + int8_t param_resonance, param_cutoff_freq; /* -64 ~ 63 */ + float cutoff_freq_coef, resonance_dB; + + int8_t velocity_sense_depth, velocity_sense_offset; + + int8_t scale_tuning[12], prev_scale_tuning; + int8_t temper_type; + + int8_t soft_pedal; + int8_t sostenuto; + int8_t damper_mode; + + int8_t tone_map0_number; + double pitch_offset_fine; /* in Hz */ + int8_t assign_mode; + + int8_t legato; /* legato footswitch */ + int8_t legato_flag; /* note-on flag for legato */ + + midi_controller mod, bend, caf, paf, cc1, cc2; + + ChannelBitMask channel_layer; + int port_select; + + struct part_eq_xg eq_xg; + + int8_t dry_level; + int8_t note_limit_high, note_limit_low; /* Note Limit (Keyboard Range) */ + int8_t vel_limit_high, vel_limit_low; /* Velocity Limit */ + int32_t rx; /* Rx. ~ (Rcv ~) */ + + int drum_effect_num; + int8_t drum_effect_flag; + struct DrumPartEffect *drum_effect; + + int8_t sysex_gs_msb_addr, sysex_gs_msb_val, + sysex_xg_msb_addr, sysex_xg_msb_val, sysex_msb_addr, sysex_msb_val; +}; + +/* Causes the instrument's default panning to be used. */ +#define NO_PANNING -1 + +typedef struct { + int16_t freq, last_freq, orig_freq; + double reso_dB, last_reso_dB, orig_reso_dB, reso_lin; + int8_t type; /* filter type. 0: Off, 1: 12dB/oct, 2: 24dB/oct */ + int32_t f, q, p; /* coefficients in fixed-point */ + int32_t b0, b1, b2, b3, b4; + float gain; + int8_t start_flag; +} FilterCoefficients; + +#define ENABLE_PAN_DELAY +#define PAN_DELAY_BUF_MAX 48 /* 0.5ms in 96kHz */ + +typedef struct { + uint8_t + status, channel, note, velocity; + int vid, temper_instant; + Sample *sample; + int64_t sample_offset; /* sample_offset must be signed */ + int32_t + orig_frequency, frequency, sample_increment, + envelope_volume, envelope_target, envelope_increment, + tremolo_sweep, tremolo_sweep_position, + tremolo_phase, tremolo_phase_increment, + vibrato_sweep, vibrato_sweep_position; + + final_volume_t left_mix, right_mix; + int32_t old_left_mix, old_right_mix, + left_mix_offset, right_mix_offset, + left_mix_inc, right_mix_inc; + + double + left_amp, right_amp, tremolo_volume; + int32_t + vibrato_sample_increment[VIBRATO_SAMPLE_INCREMENTS], vibrato_delay; + int + vibrato_phase, orig_vibrato_control_ratio, vibrato_control_ratio, + vibrato_depth, vibrato_control_counter, + envelope_stage, control_counter, panning, panned; + int16_t tremolo_depth; + + /* for portamento */ + int porta_control_ratio, porta_control_counter, porta_dpb; + int32_t porta_pb; + + int delay; /* Note ON delay samples */ + int32_t timeout; + struct cache_hash *cache; + + uint8_t chorus_link; /* Chorus link */ + int8_t proximate_flag; + + FilterCoefficients fc; + + double envelope_scale, last_envelope_volume; + int32_t inv_envelope_scale; + + int modenv_stage; + int32_t + modenv_volume, modenv_target, modenv_increment; + double last_modenv_volume; + int32_t tremolo_delay, modenv_delay; + + int32_t delay_counter; + +#ifdef ENABLE_PAN_DELAY + int32_t *pan_delay_buf, pan_delay_rpt, pan_delay_wpt, pan_delay_spt; +#endif /* ENABLE_PAN_DELAY */ +} Voice; + +/* Voice status options: */ +#define VOICE_FREE (1<<0) +#define VOICE_ON (1<<1) +#define VOICE_SUSTAINED (1<<2) +#define VOICE_OFF (1<<3) +#define VOICE_DIE (1<<4) + +/* Voice panned options: */ +#define PANNED_MYSTERY 0 +#define PANNED_LEFT 1 +#define PANNED_RIGHT 2 +#define PANNED_CENTER 3 +/* Anything but PANNED_MYSTERY only uses the left volume */ + +enum { + MODULE_TIMIDITY_DEFAULT = 0x0, + /* GS modules */ + MODULE_SC55 = 0x1, + MODULE_SC88 = 0x2, + MODULE_SC88PRO = 0x3, + MODULE_SC8850 = 0x4, + /* XG modules */ + MODULE_MU50 = 0x10, + MODULE_MU80 = 0x11, + MODULE_MU90 = 0x12, + MODULE_MU100 = 0x13, + /* GM modules */ + MODULE_SBLIVE = 0x20, + MODULE_SBAUDIGY = 0x21, + /* Special modules */ + MODULE_TIMIDITY_SPECIAL1 = 0x70, + MODULE_TIMIDITY_DEBUG = 0x7f, +}; + + + +struct midi_file_info +{ + int readflag; + int16_t hdrsiz; + int16_t format; + int16_t tracks; + int32_t divisions; + int time_sig_n, time_sig_d, time_sig_c, time_sig_b; /* Time signature */ + int drumchannels_isset; + ChannelBitMask drumchannels; + ChannelBitMask drumchannel_mask; + int32_t samples; + int max_channel; + int compressed; /* True if midi_data is compressed */ +}; + + +class Recache; +class Mixer; +class Reverb; +class Effect; + +class Player +{ +public: + Channel channel[MAX_CHANNELS]; + Voice voice[max_voices]; + ChannelBitMask default_drumchannel_mask; + ChannelBitMask default_drumchannels; + ChannelBitMask drumchannel_mask; + ChannelBitMask drumchannels; + double *vol_table; + + // make this private later + Instruments *instruments; +private: + int last_reverb_setting; + Recache *recache; + Mixer *mixer; + Reverb *reverb; + Effect *effect; + + + MidiEvent *current_event; + int32_t sample_count; /* Length of event_list */ + int32_t current_sample; /* Number of calclated samples */ + double midi_time_ratio; /* For speed up/down */ + int computed_samples; + + int note_key_offset = 0; /* For key up/down */ + ChannelBitMask channel_mute; /* For channel mute */ + double master_volume; + int32_t master_volume_ratio; + + int play_system_mode; + int midi_streaming; + int volatile stream_max_compute; /* compute time limit (in msec) when streaming */ + int8_t current_keysig; + int8_t current_temper_keysig; + int temper_adj; + int32_t current_play_tempo; + int opt_realtime_playing; + int check_eot_flag; + int playmidi_seek_flag; + int opt_pure_intonation; + int current_freq_table; + int current_temper_freq_table; + int master_tuning; + + int make_rvid_flag; /* For reverb optimization */ + + int32_t amplification; + int voices, upper_voices; + + struct midi_file_info midifileinfo, *current_file_info; + MBlockList playmidi_pool; + int32_t freq_table_user[4][48][128]; + char *reverb_buffer; /* MAX_CHANNELS*AUDIO_BUFFER_SIZE*8 */ + + int32_t lost_notes, cut_notes; + int32_t common_buffer[AUDIO_BUFFER_SIZE * 2], *buffer_pointer; /* stereo samples */ + int16_t wav_buffer[AUDIO_BUFFER_SIZE * 2]; + + int32_t insertion_effect_buffer[AUDIO_BUFFER_SIZE * 2]; + + + /* Ring voice id for each notes. This ID enables duplicated note. */ + uint8_t vidq_head[128 * MAX_CHANNELS], vidq_tail[128 * MAX_CHANNELS]; + + int MIDI_EVENT_NOTE(MidiEvent *ep) + { + return (ISDRUMCHANNEL((ep)->channel) ? (ep)->a : (((int)(ep)->a + note_key_offset + channel[ep->channel].key_shift) & 0x7f)); + } + + int16_t conv_lfo_pitch_depth(float val) + { + return (int16_t)(0.0318f * val * val + 0.6858f * val + 0.5f); + } + + int16_t conv_lfo_filter_depth(float val) + { + return (int16_t)((0.0318f * val * val + 0.6858f * val) * 4.0f + 0.5f); + } + + bool IS_SYSEX_EVENT_TYPE(MidiEvent *event); + double cnv_Hz_to_vib_ratio(double freq); + int new_vidq(int ch, int note); + int last_vidq(int ch, int note); + void reset_voices(void); + void kill_note(int i); + void kill_all_voices(void); + void reset_drum_controllers(struct DrumParts *d[], int note); + void reset_nrpn_controllers(int c); + void reset_controllers(int c); + int32_t calc_velocity(int32_t ch, int32_t vel); + void recompute_voice_tremolo(int v); + void recompute_amp(int v); + void reset_midi(int playing); + void recompute_channel_filter(int ch, int note); + void init_voice_filter(int i); + int reduce_voice(void); + int find_free_voice(void); + int get_panning(int ch, int note, int v); + void init_voice_vibrato(int v); + void init_voice_pan_delay(int v); + void init_voice_portamento(int v); + void init_voice_tremolo(int v); + void start_note(MidiEvent *e, int i, int vid, int cnt); + void set_envelope_time(int ch, int val, int stage); + void new_chorus_voice_alternate(int v1, int level); + void note_on(MidiEvent *e); + void update_sostenuto_controls(int ch); + void update_redamper_controls(int ch); + void note_off(MidiEvent *e); + void all_notes_off(int c); + void all_sounds_off(int c); + void adjust_pressure(MidiEvent *e); + void adjust_channel_pressure(MidiEvent *e); + void adjust_panning(int c); + void adjust_drum_panning(int ch, int note); + void drop_sustain(int c); + void adjust_all_pitch(void); + void adjust_pitch(int c); + void adjust_volume(int c); + void set_reverb_level(int ch, int level); + void make_drum_effect(int ch); + void adjust_master_volume(void); + void add_channel_layer(int to_ch, int from_ch); + void remove_channel_layer(int ch); + void process_sysex_event(int ev, int ch, int val, int b); + double gs_cnv_vib_rate(int rate); + int32_t gs_cnv_vib_depth(int depth); + int32_t gs_cnv_vib_delay(int delay); + int last_rpn_addr(int ch); + void voice_increment(int n); + void voice_decrement(int n); + void voice_decrement_conservative(int n); + void mix_signal(int32_t *dest, int32_t *src, int32_t count); + int is_insertion_effect_xg(int ch); + void do_compute_data(int32_t count); + int check_midi_play_end(MidiEvent *e, int len); + int midi_play_end(void); + void update_modulation_wheel(int ch); + void drop_portamento(int ch); + void update_portamento_time(int ch); + void update_legato_controls(int ch); + void set_master_tuning(int tune); + struct midi_file_info *new_midi_file_info(); + + void adjust_amplification(void); + void init_freq_table_user(void); + int find_samples(MidiEvent *, int *); + int select_play_sample(Sample *, int, int *, int *, MidiEvent *); + double get_play_note_ratio(int, int); + int find_voice(MidiEvent *); + void finish_note(int i); + void update_portamento_controls(int ch); + void update_rpn_map(int ch, int addr, int update_now); + void set_single_note_tuning(int, int, int, int); + void set_user_temper_entry(int, int, int); + void recompute_bank_parameter(int, int); + float calc_drum_tva_level(int ch, int note, int level); + int32_t calc_random_delay(int ch, int note); + + + + /* XG Part EQ */ + void init_part_eq_xg(struct part_eq_xg *); + void recompute_part_eq_xg(struct part_eq_xg *); + /* MIDI controllers (MW, Bend, CAf, PAf,...) */ + void init_midi_controller(midi_controller *); + float get_midi_controller_amp(midi_controller *); + float get_midi_controller_filter_cutoff(midi_controller *); + float get_midi_controller_filter_depth(midi_controller *); + int32_t get_midi_controller_pitch(midi_controller *); + int16_t get_midi_controller_pitch_depth(midi_controller *); + int16_t get_midi_controller_amp_depth(midi_controller *); + /* Rx. ~ (Rcv ~) */ + void init_rx(int); + void set_rx(int, int32_t, int); + void init_rx_drum(struct DrumParts *); + void set_rx_drum(struct DrumParts *, int32_t, int); + int32_t get_rx_drum(struct DrumParts *, int32_t); + int convert_midi_control_change(int chn, int type, int val, MidiEvent *ev_ret); + + +public: + Player(Instruments *); + ~Player(); + + bool ISDRUMCHANNEL(int c) + { + return !!IS_SET_CHANNELMASK(drumchannels, c); + } + + int midi_drumpart_change(int ch, int isdrum); + int get_reverb_level(int ch); + int get_chorus_level(int ch); + Instrument *play_midi_load_instrument(int dr, int bk, int prog); + void midi_program_change(int ch, int prog); + void free_voice(int v); + void play_midi_setup_drums(int ch, int note); + + /* For stream player */ + void playmidi_stream_init(void); + void playmidi_tmr_reset(void); + int play_event(MidiEvent *ev); + + void recompute_voice_filter(int); + + void free_drum_effect(int); + void change_system_mode(int mode); + void recompute_freq(int v); + int get_default_mapID(int ch); + void init_channel_layer(int ch); + int compute_data(float *buffer, int32_t count); + int send_event(int status, int parm1, int parm2); + void send_long_event(const uint8_t *sysexbuffer, int exlen); +}; + +class SysexConvert +{ + const int midi_port_number = 0; + uint8_t rhythm_part[2] = { 0,0 }; /* for GS */ + uint8_t drum_setup_xg[16] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; /* for XG */ + +public: + int parse_sysex_event_multi(const uint8_t *val, int32_t len, MidiEvent *evm, Instruments *instruments); + int parse_sysex_event(const uint8_t *val, int32_t len, MidiEvent *ev, Instruments *instruments); +}; + +void free_gauss_table(void); +void set_playback_rate(int freq); + + +} + +#endif /* ___PLAYMIDI_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/quantity.h b/thirdparty/timidityplus/timiditypp/quantity.h new file mode 100644 index 000000000..2f8865c1b --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/quantity.h @@ -0,0 +1,70 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + quantity.h + + by Kentaro Sato +*/ + +#ifndef ___QUANTITY_H_ +#define ___QUANTITY_H_ + +#include + +namespace TimidityPlus +{ + + +#define QUANTITY_UNIT_TYPE(u) QUANTITY_OF_##u, QUANTITY_UNIT_NAME(u##_NUM) +#define QUANTITY_UNIT_NAME(name) QUANTITY_UNIT_##name +enum quantity_units { + QUANTITY_UNIT_TYPE(UNDEFINED), /* type only */ + QUANTITY_UNIT_TYPE(DIRECT_INT), /* internal use */ + QUANTITY_UNIT_TYPE(DIRECT_FLOAT), /* internal use */ + QUANTITY_UNIT_TYPE(TREMOLO_SWEEP), /* int */ + QUANTITY_UNIT_NAME(TREMOLO_SWEEP_MS), /* int */ + QUANTITY_UNIT_TYPE(TREMOLO_RATE), /* int */ + QUANTITY_UNIT_NAME(TREMOLO_RATE_MS), /* int */ + QUANTITY_UNIT_NAME(TREMOLO_RATE_HZ), /* float */ + QUANTITY_UNIT_TYPE(VIBRATO_SWEEP), /* int */ + QUANTITY_UNIT_NAME(VIBRATO_SWEEP_MS), /* int */ + QUANTITY_UNIT_TYPE(VIBRATO_RATE), /* int */ + QUANTITY_UNIT_NAME(VIBRATO_RATE_MS), /* int */ + QUANTITY_UNIT_NAME(VIBRATO_RATE_HZ), /* float */ +}; +#undef QUANTITY_UNIT_TYPE +#define QUANTITY_UNIT_TYPE(u) QUANTITY_OF_##u + +#define INIT_QUANTITY(q) (q).type = QUANTITY_UNIT_TYPE(UNDEFINED) +#define IS_QUANTITY_DEFINED(q) ((q).type != QUANTITY_UNIT_TYPE(UNDEFINED)) + +typedef struct Quantity_ { + uint16_t type, unit; + union { + int32_t i; + double f; + } value; +} Quantity; + +extern const char *string_to_quantity(const char *string, Quantity *quantity, uint16_t type); +extern int32_t quantity_to_int(const Quantity *quantity, int32_t param); +extern double quantity_to_float(const Quantity *quantity, int32_t param); + +} +#endif /* ___QUANTITY_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/recache.h b/thirdparty/timidityplus/timiditypp/recache.h new file mode 100644 index 000000000..79f01d611 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/recache.h @@ -0,0 +1,107 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef ___RECACHE_H_ +#define ___RECACHE_H_ + +#include + +namespace TimidityPlus +{ + + +struct cache_hash +{ + /* cache key */ + int note; + Sample *sp; + + int32_t cnt; /* counter */ + double r; /* size/refcnt */ + Sample *resampled; + struct cache_hash *next; +}; + +class Player; + +class Recache +{ + Player *player; + enum + { + HASH_TABLE_SIZE = 251, + MIXLEN = 256, + + MIN_LOOPSTART = MIXLEN, + MIN_LOOPLEN = 1024, + MAX_EXPANDLEN = (1024 * 32), + + CACHE_RESAMPLING_OK = 0, + CACHE_RESAMPLING_NOTOK = 1, + SORT_THRESHOLD = 20 + }; + + struct CNote + { + int32_t on[128]; + struct cache_hash *cache[128]; + }; + + CNote channel_note_table[MAX_CHANNELS]; + sample_t *cache_data; + splen_t cache_data_len; + struct cache_hash *cache_hash_table[HASH_TABLE_SIZE]; + MBlockList hash_entry_pool; + + + void free_cache_data(void); + double sample_resamp_info(Sample *, int, splen_t *, splen_t *, splen_t *); + void qsort_cache_array(struct cache_hash **, int32_t, int32_t); + void insort_cache_array(struct cache_hash **, int32_t); + int cache_resampling(struct cache_hash *); + void loop_connect(sample_t *, int32_t, int32_t); + +public: + + Recache(Player *p) + { + memset(this, 0, sizeof(*this)); + player = p; + resamp_cache_reset(); + } + + ~Recache() + { + free_cache_data(); + } + + void resamp_cache_reset(void); + void resamp_cache_refer_on(Voice *vp, int32_t sample_start); + void resamp_cache_refer_off(int ch, int note, int32_t sample_end); + void resamp_cache_refer_alloff(int ch, int32_t sample_end); + void resamp_cache_create(void); + struct cache_hash *resamp_cache_fetch(Sample *sp, int note); + +}; + +const int32_t allocate_cache_size = DEFAULT_CACHE_DATA_SIZE; + +} +#endif /* ___RECACHE_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/resample.h b/thirdparty/timidityplus/timiditypp/resample.h new file mode 100644 index 000000000..8523cdc31 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/resample.h @@ -0,0 +1,88 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + resample.h +*/ + +#ifndef ___RESAMPLE_H_ +#define ___RESAMPLE_H_ + +#include +#include "sysdep.h" + +namespace TimidityPlus +{ + + +typedef int32_t resample_t; + +enum { + RESAMPLE_CSPLINE, + RESAMPLE_LAGRANGE, + RESAMPLE_GAUSS, + RESAMPLE_NEWTON, + RESAMPLE_LINEAR, + RESAMPLE_NONE +}; + +extern void initialize_resampler_coeffs(void); +extern void free_gauss_table(void); + +typedef struct resample_rec { + splen_t loop_start; + splen_t loop_end; + splen_t data_length; +} resample_rec_t; + +extern resample_t do_resamplation(sample_t *src, splen_t ofs, resample_rec_t *rec); + +extern void pre_resample(Sample *sp); +class Player; + +class Resampler // This is only here to put the buffer on the stack without changing all the code. +{ + Player *player; + resample_t resample_buffer[AUDIO_BUFFER_SIZE] = { 0 }; + int resample_buffer_offset = 0; + + resample_t *rs_plain_c(int v, int32_t *countptr); + resample_t *rs_plain(int v, int32_t *countptr); + resample_t *rs_loop_c(Voice *vp, int32_t count); + resample_t *rs_loop(Voice *vp, int32_t count); + resample_t *rs_bidir(Voice *vp, int32_t count); + resample_t *rs_vib_plain(int v, int32_t *countptr); + resample_t *rs_vib_loop(Voice *vp, int32_t count); + resample_t *rs_vib_bidir(Voice *vp, int32_t count); + resample_t *porta_resample_voice(int v, int32_t *countptr, int mode); + resample_t *normal_resample_voice(int v, int32_t *countptr, int mode); + resample_t *vib_resample_voice(int v, int32_t *countptr, int mode); + int rs_update_porta(int v); + int32_t update_vibrato(Voice *vp, int sign); + +public: + Resampler(Player *p) + { + player = p; + } + resample_t * resample_voice(int v, int32_t *countptr); +}; + +} + +#endif /* ___RESAMPLE_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/reverb.h b/thirdparty/timidityplus/timiditypp/reverb.h new file mode 100644 index 000000000..1a5ab5737 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/reverb.h @@ -0,0 +1,814 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * REVERB EFFECT FOR TIMIDITY++-1.X (Version 0.06e 1999/1/28) + * + * Copyright (C) 1997,1998,1999 Masaki Kiryu + * (http://w3mb.kcom.ne.jp/~mkiryu/) + * + * reverb.h + * + */ +#ifndef ___REVERB_H_ +#define ___REVERB_H_ + +#include + +namespace TimidityPlus +{ + + +#define DEFAULT_REVERB_SEND_LEVEL 40 + + +/* */ +/* Effect Utitities */ +/* */ +/*! simple delay */ +typedef struct { + int32_t *buf, size, index; +} simple_delay; + +/*! Pink Noise Generator */ +typedef struct { + float b0, b1, b2, b3, b4, b5, b6; +} pink_noise; + + +#ifndef SINE_CYCLE_LENGTH +#define SINE_CYCLE_LENGTH 1024 +#endif + +/*! LFO */ +struct lfo { + int32_t buf[SINE_CYCLE_LENGTH]; + int32_t count, cycle; /* in samples */ + int32_t icycle; /* proportional to (SINE_CYCLE_LENGTH / cycle) */ + int type; /* current content of its buffer */ + double freq; /* in Hz */ +}; + +enum { + LFO_NONE = 0, + LFO_SINE, + LFO_TRIANGULAR, +}; + +/*! modulated delay with allpass interpolation */ +typedef struct { + int32_t *buf, size, rindex, windex, hist; + int32_t ndelay, depth; /* in samples */ +} mod_delay; + +/*! modulated allpass filter with allpass interpolation */ +typedef struct { + int32_t *buf, size, rindex, windex, hist; + int32_t ndelay, depth; /* in samples */ + double feedback; + int32_t feedbacki; +} mod_allpass; + +/*! Moog VCF (resonant IIR state variable filter) */ +typedef struct { + int16_t freq, last_freq; /* in Hz */ + double res_dB, last_res_dB; /* in dB */ + int32_t f, q, p; /* coefficients in fixed-point */ + int32_t b0, b1, b2, b3, b4; +} filter_moog; + +/*! Moog VCF (resonant IIR state variable filter with distortion) */ +typedef struct { + int16_t freq, last_freq; /* in Hz */ + double res_dB, last_res_dB; /* in dB */ + double dist, last_dist, f, q, p, d, b0, b1, b2, b3, b4; +} filter_moog_dist; + +/*! LPF18 (resonant IIR lowpass filter with waveshaping) */ +typedef struct { + int16_t freq, last_freq; /* in Hz */ + double dist, res, last_dist, last_res; /* in linear */ + double ay1, ay2, aout, lastin, kres, value, kp, kp1h; +} filter_lpf18; + +/*! 1st order lowpass filter */ +typedef struct { + double a; + int32_t ai, iai; /* coefficients in fixed-point */ + int32_t x1l, x1r; +} filter_lowpass1; + +/*! lowpass / highpass filter */ +typedef struct { + double freq, q, last_freq, last_q; + int32_t x1l, x2l, y1l, y2l, x1r, x2r, y1r, y2r; + int32_t a1, a2, b1, b02; +} filter_biquad; + +#ifndef PART_EQ_XG +#define PART_EQ_XG +/*! shelving filter */ +typedef struct { + double freq, gain, q; + int32_t x1l, x2l, y1l, y2l, x1r, x2r, y1r, y2r; + int32_t a1, a2, b0, b1, b2; +} filter_shelving; + +struct part_eq_xg { + int8_t bass, treble, bass_freq, treble_freq; + filter_shelving basss, trebles; + int8_t valid; +}; +#endif /* PART_EQ_XG */ + + +/*! peaking filter */ +typedef struct { + double freq, gain, q; + int32_t x1l, x2l, y1l, y2l, x1r, x2r, y1r, y2r; + int32_t ba1, a2, b0, b2; +} filter_peaking; + + +/*! allpass filter */ +typedef struct _allpass { + int32_t *buf, size, index; + double feedback; + int32_t feedbacki; +} allpass; + +/*! comb filter */ +typedef struct _comb { + int32_t *buf, filterstore, size, index; + double feedback, damp1, damp2; + int32_t feedbacki, damp1i, damp2i; +} comb; + +/* */ +/* Insertion and Variation Effect */ +/* */ +struct effect_xg_t { + int8_t use_msb, type_msb, type_lsb, param_lsb[16], param_msb[10], + ret, pan, send_reverb, send_chorus, connection, part, + mw_depth, bend_depth, cat_depth, ac1_depth, ac2_depth, cbc1_depth, + cbc2_depth; + struct _EffectList *ef; +}; + + +enum { + EFFECT_NONE, + EFFECT_EQ2, + EFFECT_EQ3, + EFFECT_STEREO_EQ, + EFFECT_OVERDRIVE1, + EFFECT_DISTORTION1, + EFFECT_OD1OD2, + EFFECT_CHORUS, + EFFECT_FLANGER, + EFFECT_SYMPHONIC, + EFFECT_CHORUS_EQ3, + EFFECT_STEREO_OVERDRIVE, + EFFECT_STEREO_DISTORTION, + EFFECT_STEREO_AMP_SIMULATOR, + EFFECT_OD_EQ3, + EFFECT_HEXA_CHORUS, + EFFECT_DELAY_LCR, + EFFECT_DELAY_LR, + EFFECT_ECHO, + EFFECT_CROSS_DELAY, + EFFECT_DELAY_EQ2, + EFFECT_LOFI, + EFFECT_LOFI1, + EFFECT_LOFI2, + EFFECT_XG_AUTO_WAH, + EFFECT_XG_AUTO_WAH_EQ2, + EFFECT_XG_AUTO_WAH_OD, + EFFECT_XG_AUTO_WAH_OD_EQ3, +}; + +#define MAGIC_INIT_EFFECT_INFO -1 +#define MAGIC_FREE_EFFECT_INFO -2 + +class Reverb; + +struct insertion_effect_gs_t { + int32_t type; + int8_t type_lsb, type_msb, parameter[20], send_reverb, + send_chorus, send_delay, control_source1, control_depth1, + control_source2, control_depth2, send_eq_switch; + struct _EffectList *ef; +}; + +enum { + XG_CONN_INSERTION = 0, + XG_CONN_SYSTEM = 1, + XG_CONN_SYSTEM_CHORUS, + XG_CONN_SYSTEM_REVERB, +}; + +#define XG_INSERTION_EFFECT_NUM 2 +#define XG_VARIATION_EFFECT_NUM 1 + +typedef struct _EffectList { + int type; + void *info; + const struct _EffectEngine *engine; + struct _EffectList *next_ef; +} EffectList; + +struct _EffectEngine { + int type; + const char *name; + void (Reverb::*do_effect)(int32_t *, int32_t, struct _EffectList *); + void (Reverb::*conv_gs)(struct insertion_effect_gs_t *, struct _EffectList *); + void (Reverb::*conv_xg)(struct effect_xg_t *, struct _EffectList *); + int info_size; +}; + + +struct effect_parameter_gs_t { + int8_t type_msb, type_lsb; + const char *name; + int8_t param[20]; + int8_t control1, control2; +}; + + +struct effect_parameter_xg_t { + int8_t type_msb, type_lsb; + const char *name; + int8_t param_msb[10], param_lsb[16]; + int8_t control; +}; + + +/*! 2-Band EQ */ +typedef struct { + int16_t low_freq, high_freq; /* in Hz */ + int16_t low_gain, high_gain; /* in dB */ + filter_shelving hsf, lsf; +} InfoEQ2; + +/*! 3-Band EQ */ +typedef struct { + int16_t low_freq, high_freq, mid_freq; /* in Hz */ + int16_t low_gain, high_gain, mid_gain; /* in dB */ + double mid_width; + filter_shelving hsf, lsf; + filter_peaking peak; +} InfoEQ3; + +/*! Stereo EQ */ +typedef struct { + int16_t low_freq, high_freq, m1_freq, m2_freq; /* in Hz */ + int16_t low_gain, high_gain, m1_gain, m2_gain; /* in dB */ + double m1_q, m2_q, level; + int32_t leveli; + filter_shelving hsf, lsf; + filter_peaking m1, m2; +} InfoStereoEQ; + +/*! Overdrive 1 / Distortion 1 */ +typedef struct { + double level; + int32_t leveli, di; /* in fixed-point */ + int8_t drive, pan, amp_sw, amp_type; + filter_moog svf; + filter_biquad lpf1; + void (Reverb::*amp_sim)(int32_t *, int32_t); +} InfoOverdrive1; + +/*! OD1 / OD2 */ +typedef struct { + double level, levell, levelr; + int32_t levelli, levelri, dli, dri; /* in fixed-point */ + int8_t drivel, driver, panl, panr, typel, typer, amp_swl, amp_swr, amp_typel, amp_typer; + filter_moog svfl, svfr; + filter_biquad lpf1; + void (Reverb::*amp_siml)(int32_t *, int32_t), (Reverb::*amp_simr)(int32_t *, int32_t); + void (Reverb::*odl)(int32_t *, int32_t), (Reverb::*odr)(int32_t *, int32_t); +} InfoOD1OD2; + +/*! HEXA-CHORUS */ +typedef struct { + simple_delay buf0; + lfo lfo0; + double dry, wet, level; + int32_t pdelay, depth; /* in samples */ + int8_t pdelay_dev, depth_dev, pan_dev; + int32_t dryi, weti; /* in fixed-point */ + int32_t pan0, pan1, pan2, pan3, pan4, pan5; + int32_t depth0, depth1, depth2, depth3, depth4, depth5, + pdelay0, pdelay1, pdelay2, pdelay3, pdelay4, pdelay5; + int32_t spt0, spt1, spt2, spt3, spt4, spt5, + hist0, hist1, hist2, hist3, hist4, hist5; +} InfoHexaChorus; + +/*! Plate Reverb */ +typedef struct { + simple_delay pd, od1l, od2l, od3l, od4l, od5l, od6l, od7l, + od1r, od2r, od3r, od4r, od5r, od6r, od7r, + td1, td2, td1d, td2d; + lfo lfo1, lfo1d; + allpass ap1, ap2, ap3, ap4, ap6, ap6d; + mod_allpass ap5, ap5d; + filter_lowpass1 lpf1, lpf2; + int32_t t1, t1d; + double decay, ddif1, ddif2, idif1, idif2, dry, wet; + int32_t decayi, ddif1i, ddif2i, idif1i, idif2i, dryi, weti; +} InfoPlateReverb; + +/*! Standard Reverb */ +typedef struct { + int32_t spt0, spt1, spt2, spt3, rpt0, rpt1, rpt2, rpt3; + int32_t ta, tb, HPFL, HPFR, LPFL, LPFR, EPFL, EPFR; + simple_delay buf0_L, buf0_R, buf1_L, buf1_R, buf2_L, buf2_R, buf3_L, buf3_R; + double fbklev, nmixlev, cmixlev, monolev, hpflev, lpflev, lpfinp, epflev, epfinp, width, wet; + int32_t fbklevi, nmixlevi, cmixlevi, monolevi, hpflevi, lpflevi, lpfinpi, epflevi, epfinpi, widthi, weti; +} InfoStandardReverb; + +/*! Freeverb */ +#define numcombs 8 +#define numallpasses 4 + +typedef struct { + simple_delay pdelay; + double roomsize, roomsize1, damp, damp1, wet, wet1, wet2, width; + comb combL[numcombs], combR[numcombs]; + allpass allpassL[numallpasses], allpassR[numallpasses]; + int32_t wet1i, wet2i; + int8_t alloc_flag; +} InfoFreeverb; + +/*! 3-Tap Stereo Delay Effect */ +typedef struct { + simple_delay delayL, delayR; + int32_t size[3], index[3]; + double level[3], feedback, send_reverb; + int32_t leveli[3], feedbacki, send_reverbi; +} InfoDelay3; + +/*! Stereo Chorus Effect */ +typedef struct { + simple_delay delayL, delayR; + lfo lfoL, lfoR; + int32_t wpt0, spt0, spt1, hist0, hist1; + int32_t rpt0, depth, pdelay; + double level, feedback, send_reverb, send_delay; + int32_t leveli, feedbacki, send_reverbi, send_delayi; +} InfoStereoChorus; + +/*! Chorus */ +typedef struct { + simple_delay delayL, delayR; + lfo lfoL, lfoR; + int32_t wpt0, spt0, spt1, hist0, hist1; + int32_t rpt0, depth, pdelay; + double dry, wet, feedback, pdelay_ms, depth_ms, rate, phase_diff; + int32_t dryi, weti, feedbacki; +} InfoChorus; + + +/*! Stereo Overdrive / Distortion */ +typedef struct { + double level, dry, wet, drive, cutoff; + int32_t dryi, weti, di; + filter_moog svfl, svfr; + filter_biquad lpf1; + void (Reverb::*od)(int32_t *, int32_t); +} InfoStereoOD; + +/*! Delay L,C,R */ +typedef struct { + simple_delay delayL, delayR; + int32_t index[3], size[3]; /* L,C,R */ + double rdelay, ldelay, cdelay, fdelay; /* in ms */ + double dry, wet, feedback, clevel, high_damp; + int32_t dryi, weti, feedbacki, cleveli; + filter_lowpass1 lpf; +} InfoDelayLCR; + +/*! Delay L,R */ +typedef struct { + simple_delay delayL, delayR; + int32_t index[2], size[2]; /* L,R */ + double rdelay, ldelay, fdelay1, fdelay2; /* in ms */ + double dry, wet, feedback, high_damp; + int32_t dryi, weti, feedbacki; + filter_lowpass1 lpf; +} InfoDelayLR; + +/*! Echo */ +typedef struct { + simple_delay delayL, delayR; + int32_t index[2], size[2]; /* L1,R1 */ + double rdelay1, ldelay1, rdelay2, ldelay2; /* in ms */ + double dry, wet, lfeedback, rfeedback, high_damp, level; + int32_t dryi, weti, lfeedbacki, rfeedbacki, leveli; + filter_lowpass1 lpf; +} InfoEcho; + +/*! Cross Delay */ +typedef struct { + simple_delay delayL, delayR; + double lrdelay, rldelay; /* in ms */ + double dry, wet, feedback, high_damp; + int32_t dryi, weti, feedbacki, input_select; + filter_lowpass1 lpf; +} InfoCrossDelay; + +/*! Lo-Fi 1 */ +typedef struct { + int8_t lofi_type, pan, pre_filter, post_filter; + double level, dry, wet; + int32_t bit_mask, level_shift, dryi, weti; + filter_biquad pre_fil, post_fil; +} InfoLoFi1; + +/*! Lo-Fi 2 */ +typedef struct { + int8_t wp_sel, disc_type, hum_type, ms, pan, rdetune, lofi_type, fil_type; + double wp_level, rnz_lev, discnz_lev, hum_level, dry, wet, level; + int32_t bit_mask, level_shift, wp_leveli, rnz_levi, discnz_levi, hum_keveki, dryi, weti; + filter_biquad fil, wp_lpf, hum_lpf, disc_lpf; +} InfoLoFi2; + +/*! LO-FI */ +typedef struct { + int8_t output_gain, word_length, filter_type, bit_assign, emphasis; + double dry, wet; + int32_t bit_mask, level_shift, dryi, weti; + filter_biquad lpf, srf; +} InfoLoFi; + +/*! XG: Auto Wah */ +typedef struct { + int8_t lfo_depth, drive; + double resonance, lfo_freq, offset_freq, dry, wet; + int32_t dryi, weti, fil_count, fil_cycle; + struct lfo lfo; + filter_moog_dist fil0, fil1; +} InfoXGAutoWah; + +typedef struct { + double level; + int32_t leveli; + filter_biquad lpf; +} InfoXGAutoWahOD; + + +/* GS parameters of reverb effect */ +struct reverb_status_gs_t +{ + /* GS parameters */ + int8_t character, pre_lpf, level, time, delay_feedback, pre_delay_time; + + InfoStandardReverb info_standard_reverb; + InfoPlateReverb info_plate_reverb; + InfoFreeverb info_freeverb; + InfoDelay3 info_reverb_delay; + filter_lowpass1 lpf; +}; + +struct chorus_text_gs_t +{ + int status; + uint8_t voice_reserve[18], macro[3], pre_lpf[3], level[3], feed_back[3], + delay[3], rate[3], depth[3], send_level[3]; +}; + +/* GS parameters of chorus effect */ +struct chorus_status_gs_t +{ + /* GS parameters */ + int8_t macro, pre_lpf, level, feedback, delay, rate, depth, send_reverb, send_delay; + + //struct chorus_text_gs_t text; + + InfoStereoChorus info_stereo_chorus; + filter_lowpass1 lpf; +}; + +/* GS parameters of delay effect */ +struct delay_status_gs_t +{ + /* GS parameters */ + int8_t type, level, level_center, level_left, level_right, + feedback, pre_lpf, send_reverb, time_c, time_l, time_r; + double time_center; /* in ms */ + double time_ratio_left, time_ratio_right; /* in pct */ + + /* for pre-calculation */ + int32_t sample[3]; /* center, left, right */ + double level_ratio[3]; /* center, left, right */ + double feedback_ratio, send_reverb_ratio; + + filter_lowpass1 lpf; + InfoDelay3 info_delay; +}; + +/* GS parameters of channel EQ */ +struct eq_status_gs_t +{ + /* GS parameters */ + int8_t low_freq, high_freq, low_gain, high_gain; + + filter_shelving hsf, lsf; +}; + +/* XG parameters of Multi EQ */ +struct multi_eq_xg_t +{ + /* XG parameters */ + int8_t type, gain1, gain2, gain3, gain4, gain5, + freq1, freq2, freq3, freq4, freq5, + q1, q2, q3, q4, q5, shape1, shape5; + + int8_t valid, valid1, valid2, valid3, valid4, valid5; + filter_shelving eq1s, eq5s; + filter_peaking eq1p, eq2p, eq3p, eq4p, eq5p; +}; + + + +class Reverb +{ + double REV_INP_LEV; + + int32_t direct_buffer[AUDIO_BUFFER_SIZE * 2]; + int32_t direct_bufsize; + + int32_t reverb_effect_buffer[AUDIO_BUFFER_SIZE * 2]; + int32_t reverb_effect_bufsize; + + int32_t delay_effect_buffer[AUDIO_BUFFER_SIZE * 2]; + int32_t chorus_effect_buffer[AUDIO_BUFFER_SIZE * 2]; + int32_t eq_buffer[AUDIO_BUFFER_SIZE * 2]; + + + static const struct _EffectEngine effect_engine[]; + + void free_delay(simple_delay *delay); + void set_delay(simple_delay *delay, int32_t size); + void do_delay(int32_t *stream, int32_t *buf, int32_t size, int32_t *index); + void init_lfo(lfo *lfo, double freq, int type, double phase); + int32_t do_lfo(lfo *lfo); + void do_mod_delay(int32_t *stream, int32_t *buf, int32_t size, int32_t *rindex, int32_t *windex, int32_t ndelay, int32_t depth, int32_t lfoval, int32_t *hist); + void free_mod_allpass(mod_allpass *delay); + void set_mod_allpass(mod_allpass *delay, int32_t ndelay, int32_t depth, double feedback); + void do_mod_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *rindex, int32_t *windex, int32_t ndelay, int32_t depth, int32_t lfoval, int32_t *hist, int32_t feedback); + void free_allpass(allpass *allpass); + void set_allpass(allpass *allpass, int32_t size, double feedback); + void do_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *index, int32_t feedback); + void init_filter_moog(filter_moog *svf); + void calc_filter_moog(filter_moog *svf); + void do_filter_moog(int32_t *stream, int32_t *high, int32_t f, int32_t p, int32_t q, int32_t *b0, int32_t *b1, int32_t *b2, int32_t *b3, int32_t *b4); + void init_filter_moog_dist(filter_moog_dist *svf); + void calc_filter_moog_dist(filter_moog_dist *svf); + void do_filter_moog_dist(double *stream, double *high, double *band, double f, double p, double q, double d, double *b0, double *b1, double *b2, double *b3, double *b4); + void do_filter_moog_dist_band(double *stream, double f, double p, double q, double d, double *b0, double *b1, double *b2, double *b3, double *b4); + void init_filter_lpf18(filter_lpf18 *p); + void calc_filter_lpf18(filter_lpf18 *p); + void do_filter_lpf18(double *stream, double *ay1, double *ay2, double *aout, double *lastin, double kres, double value, double kp, double kp1h); + void do_dummy_clipping(int32_t *stream, int32_t d) {} + void do_hard_clipping(int32_t *stream, int32_t d); + void do_soft_clipping1(int32_t *stream, int32_t d); + void do_soft_clipping2(int32_t *stream, int32_t d); + void do_filter_lowpass1(int32_t *stream, int32_t *x1, int32_t a, int32_t ia); + void do_filter_lowpass1_stereo(int32_t *buf, int32_t count, filter_lowpass1 *p); + void init_filter_biquad(filter_biquad *p); + void calc_filter_biquad_low(filter_biquad *p); + void calc_filter_biquad_high(filter_biquad *p); + void do_filter_biquad(int32_t *stream, int32_t a1, int32_t a2, int32_t b1, int32_t b02, int32_t *x1, int32_t *x2, int32_t *y1, int32_t *y2); + void init_filter_shelving(filter_shelving *p); + void do_shelving_filter_stereo(int32_t* buf, int32_t count, filter_shelving *p); + void do_peaking_filter_stereo(int32_t* buf, int32_t count, filter_peaking *p); + double gs_revchar_to_roomsize(int character); + double gs_revchar_to_level(int character); + double gs_revchar_to_rt(int character); + void init_filter_peaking(filter_peaking *p); + void init_standard_reverb(InfoStandardReverb *info); + void free_standard_reverb(InfoStandardReverb *info); + void do_ch_standard_reverb(int32_t *buf, int32_t count, InfoStandardReverb *info); + void do_ch_standard_reverb_mono(int32_t *buf, int32_t count, InfoStandardReverb *info); + void set_freeverb_allpass(allpass *allpass, int32_t size); + void init_freeverb_allpass(allpass *allpass); + void set_freeverb_comb(comb *comb, int32_t size); + void init_freeverb_comb(comb *comb); + void realloc_freeverb_buf(InfoFreeverb *rev); + void update_freeverb(InfoFreeverb *rev); + void init_freeverb(InfoFreeverb *rev); + void alloc_freeverb_buf(InfoFreeverb *rev); + void free_freeverb_buf(InfoFreeverb *rev); + void do_freeverb_allpass(int32_t *stream, int32_t *buf, int32_t size, int32_t *index, int32_t feedback); + void do_freeverb_comb(int32_t input, int32_t *stream, int32_t *buf, int32_t size, int32_t *index, int32_t damp1, int32_t damp2, int32_t *fs, int32_t feedback); + void do_ch_freeverb(int32_t *buf, int32_t count, InfoFreeverb *rev); + void init_ch_reverb_delay(InfoDelay3 *info); + void free_ch_reverb_delay(InfoDelay3 *info); + void do_ch_reverb_panning_delay(int32_t *buf, int32_t count, InfoDelay3 *info); + void do_ch_reverb_normal_delay(int32_t *buf, int32_t count, InfoDelay3 *info); + int32_t get_plate_delay(double delay, double t); + void do_ch_plate_reverb(int32_t *buf, int32_t count, InfoPlateReverb *info); + void init_ch_3tap_delay(InfoDelay3 *info); + void free_ch_3tap_delay(InfoDelay3 *info); + void do_ch_3tap_delay(int32_t *buf, int32_t count, InfoDelay3 *info); + void do_ch_cross_delay(int32_t *buf, int32_t count, InfoDelay3 *info); + void do_ch_normal_delay(int32_t *buf, int32_t count, InfoDelay3 *info); + void do_ch_stereo_chorus(int32_t *buf, int32_t count, InfoStereoChorus *info); + void alloc_effect(EffectList *ef); + void do_eq2(int32_t *buf, int32_t count, EffectList *ef); + int32_t do_left_panning(int32_t sample, int32_t pan); + int32_t do_right_panning(int32_t sample, int32_t pan); + double calc_gs_drive(int val); + void do_overdrive1(int32_t *buf, int32_t count, EffectList *ef); + void do_distortion1(int32_t *buf, int32_t count, EffectList *ef); + void do_dual_od(int32_t *buf, int32_t count, EffectList *ef); + void do_hexa_chorus(int32_t *buf, int32_t count, EffectList *ef); + void free_effect_xg(struct effect_xg_t *st); + int clip_int(int val, int min, int max); + void conv_gs_eq2(struct insertion_effect_gs_t *ieffect, EffectList *ef); + void conv_gs_overdrive1(struct insertion_effect_gs_t *ieffect, EffectList *ef); + void conv_gs_dual_od(struct insertion_effect_gs_t *ieffect, EffectList *ef); + double calc_dry_gs(int val); + double calc_wet_gs(int val); + void conv_gs_hexa_chorus(struct insertion_effect_gs_t *ieffect, EffectList *ef); + double calc_dry_xg(int val, struct effect_xg_t *st); + double calc_wet_xg(int val, struct effect_xg_t *st); + void do_eq3(int32_t *buf, int32_t count, EffectList *ef); + void do_stereo_eq(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_eq2(struct effect_xg_t *st, EffectList *ef); + void conv_xg_eq3(struct effect_xg_t *st, EffectList *ef); + void conv_gs_stereo_eq(struct insertion_effect_gs_t *st, EffectList *ef); + void conv_xg_chorus_eq3(struct effect_xg_t *st, EffectList *ef); + void conv_xg_chorus(struct effect_xg_t *st, EffectList *ef); + void conv_xg_flanger(struct effect_xg_t *st, EffectList *ef); + void conv_xg_symphonic(struct effect_xg_t *st, EffectList *ef); + void do_chorus(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_od_eq3(struct effect_xg_t *st, EffectList *ef); + void conv_xg_overdrive(struct effect_xg_t *st, EffectList *ef); + void conv_xg_distortion(struct effect_xg_t *st, EffectList *ef); + void conv_xg_amp_simulator(struct effect_xg_t *st, EffectList *ef); + void do_stereo_od(int32_t *buf, int32_t count, EffectList *ef); + void do_delay_lcr(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_delay_eq2(struct effect_xg_t *st, EffectList *ef); + void conv_xg_delay_lcr(struct effect_xg_t *st, EffectList *ef); + void conv_xg_delay_lr(struct effect_xg_t *st, EffectList *ef); + void do_delay_lr(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_echo(struct effect_xg_t *st, EffectList *ef); + void do_echo(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_cross_delay(struct effect_xg_t *st, EffectList *ef); + void do_cross_delay(int32_t *buf, int32_t count, EffectList *ef); + void conv_gs_lofi1(struct insertion_effect_gs_t *st, EffectList *ef); + inline int32_t apply_lofi(int32_t input, int32_t bit_mask, int32_t level_shift); + void do_lofi1(int32_t *buf, int32_t count, EffectList *ef); + void conv_gs_lofi2(struct insertion_effect_gs_t *st, EffectList *ef); + void do_lofi2(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_lofi(struct effect_xg_t *st, EffectList *ef); + void do_lofi(int32_t *buf, int32_t count, EffectList *ef); + void conv_xg_auto_wah_od(struct effect_xg_t *st, EffectList *ef); + void conv_xg_auto_wah_od_eq3(struct effect_xg_t *st, EffectList *ef); + void conv_xg_auto_wah_eq2(struct effect_xg_t *st, EffectList *ef); + void conv_xg_auto_wah(struct effect_xg_t *st, EffectList *ef); + double calc_xg_auto_wah_freq(int32_t lfo_val, double offset_freq, int8_t depth); + void do_xg_auto_wah(int32_t *buf, int32_t count, EffectList *ef); + void do_xg_auto_wah_od(int32_t *buf, int32_t count, EffectList *ef); + +public: + Reverb() + { + // Make sure that this starts out with all zeros. + memset(this, 0, sizeof(*this)); + REV_INP_LEV = 1.0; + direct_bufsize = sizeof(direct_buffer); + reverb_effect_bufsize = sizeof(reverb_effect_buffer); + + } + + ~Reverb() + { + free_effect_buffers(); + } + + void set_dry_signal(int32_t *, int32_t); + void set_dry_signal_xg(int32_t *, int32_t, int32_t); + void mix_dry_signal(int32_t *, int32_t); + void free_effect_buffers(void); + void init_pink_noise(pink_noise *); + float get_pink_noise(pink_noise *); + float get_pink_noise_light(pink_noise *); + void calc_filter_shelving_high(filter_shelving *); + void calc_filter_shelving_low(filter_shelving *); + void calc_filter_peaking(filter_peaking *); + void do_insertion_effect_gs(int32_t*, int32_t); + void do_insertion_effect_xg(int32_t*, int32_t, struct effect_xg_t *); + void do_variation_effect1_xg(int32_t*, int32_t); + void init_ch_effect_xg(void); + EffectList *push_effect(EffectList *, int); + void do_effect_list(int32_t *, int32_t, EffectList *); + void free_effect_list(EffectList *); + void init_filter_lowpass1(filter_lowpass1 *p); + + /* */ + /* System Effect */ + /* */ + /* Reverb Effect */ + void do_ch_reverb(int32_t *, int32_t); + void set_ch_reverb(int32_t *, int32_t, int32_t); + void init_reverb(void); + void do_ch_reverb_xg(int32_t *, int32_t); + + /* Chorus Effect */ + void do_ch_chorus(int32_t *, int32_t); + void set_ch_chorus(int32_t *, int32_t, int32_t); + void init_ch_chorus(void); + void do_ch_chorus_xg(int32_t *, int32_t); + + /* Delay (Celeste) Effect */ + void do_ch_delay(int32_t *, int32_t); + void set_ch_delay(int32_t *, int32_t, int32_t); + void init_ch_delay(void); + + /* EQ */ + void init_eq_gs(void); + void set_ch_eq_gs(int32_t *, int32_t); + void do_ch_eq_gs(int32_t *, int32_t); + void do_ch_eq_xg(int32_t *, int32_t, struct part_eq_xg *); + void do_multi_eq_xg(int32_t *, int32_t); + + // These get accessed directly by the player. + struct multi_eq_xg_t multi_eq_xg; + pink_noise global_pink_noise_light; + struct insertion_effect_gs_t insertion_effect_gs; + struct effect_xg_t insertion_effect_xg[XG_INSERTION_EFFECT_NUM], + variation_effect_xg[XG_VARIATION_EFFECT_NUM], reverb_status_xg, chorus_status_xg; + + static const struct effect_parameter_gs_t effect_parameter_gs[]; + static const struct effect_parameter_xg_t effect_parameter_xg[]; + + void init_for_effect() + { + init_pink_noise(&global_pink_noise_light); + init_reverb(); + init_ch_delay(); + init_ch_chorus(); + init_eq_gs(); + } + + + struct reverb_status_gs_t reverb_status_gs; + //struct chorus_text_gs_t chorus_text_gs; + struct chorus_status_gs_t chorus_status_gs; + struct delay_status_gs_t delay_status_gs; + struct eq_status_gs_t eq_status_gs; + + + ////////////////////////////////// from readmidi + + void init_delay_status_gs(void); + void recompute_delay_status_gs(void); + void set_delay_macro_gs(int macro); + void init_reverb_status_gs(void); + void recompute_reverb_status_gs(void); + void set_reverb_macro_gm2(int macro); + void set_reverb_macro_gs(int macro); + void init_chorus_status_gs(void); + void recompute_chorus_status_gs(); + void set_chorus_macro_gs(int macro); + void init_eq_status_gs(void); + void recompute_eq_status_gs(void); + void init_multi_eq_xg(void); + void set_multi_eq_type_xg(int type); + void recompute_multi_eq_xg(void); + void set_effect_param_xg(struct effect_xg_t *st, int type_msb, int type_lsb); + void recompute_effect_xg(struct effect_xg_t *st); + void realloc_effect_xg(struct effect_xg_t *st); + void init_effect_xg(struct effect_xg_t *st); + void init_all_effect_xg(void); + void init_insertion_effect_gs(void); + void set_effect_param_gs(struct insertion_effect_gs_t *st, int msb, int lsb); + void recompute_insertion_effect_gs(void); + void realloc_insertion_effect_gs(void); + void init_effect_status(int play_system_mode); + +}; + +} +#endif /* ___REVERB_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/sffile.h b/thirdparty/timidityplus/timiditypp/sffile.h new file mode 100644 index 000000000..ceec1b4d4 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/sffile.h @@ -0,0 +1,144 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*================================================================ + * sffile.h + * SoundFont file (SBK/SF2) format defintions + * + * Copyright (C) 1996,1997 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *================================================================*/ + +/* + * Modified by Masanao Izumo + */ + +#ifndef SFFILE_H_DEF +#define SFFILE_H_DEF + +namespace TimidityPlus +{ + +/* chunk record header */ +typedef struct _SFChunk { + char id[4]; + int32_t size; +} SFChunk; + +/* generator record */ +typedef struct _SFGenRec { + int16_t oper; + int16_t amount; +} SFGenRec; + +/* layered generators record */ +typedef struct _SFGenLayer { + int nlists; + SFGenRec *list; +} SFGenLayer; + +/* header record */ +typedef struct _SFHeader { + char name[20]; + uint16_t bagNdx; + /* layered stuff */ + int nlayers; + SFGenLayer *layer; +} SFHeader; + +/* preset header record */ +typedef struct _SFPresetHdr { + SFHeader hdr; + uint16_t preset, bank; + /*int32_t lib, genre, morphology;*/ /* not used */ +} SFPresetHdr; + +/* instrument header record */ +typedef struct _SFInstHdr { + SFHeader hdr; +} SFInstHdr; + +/* sample info record */ +typedef struct _SFSampleInfo { + char name[20]; + int32_t startsample, endsample; + int32_t startloop, endloop; + /* ver.2 additional info */ + int32_t samplerate; + uint8_t originalPitch; + int8_t pitchCorrection; + uint16_t samplelink; + uint16_t sampletype; /*1=mono, 2=right, 4=left, 8=linked, $8000=ROM*/ + /* optional info */ + int32_t size; /* sample size */ + int32_t loopshot; /* short-shot loop size */ +} SFSampleInfo; + + +/*---------------------------------------------------------------- + * soundfont file info record + *----------------------------------------------------------------*/ + +typedef struct _SFInfo { + /* file name */ + char *sf_name; + + /* version of this file */ + uint16_t version, minorversion; + /* sample position (from origin) & total size (in bytes) */ + int32_t samplepos; + int32_t samplesize; + + /* raw INFO chunk list */ + int32_t infopos, infosize; + + /* preset headers */ + int npresets; + SFPresetHdr *preset; + + /* sample infos */ + int nsamples; + SFSampleInfo *sample; + + /* instrument headers */ + int ninsts; + SFInstHdr *inst; + +} SFInfo; + + +/*---------------------------------------------------------------- + * functions + *----------------------------------------------------------------*/ +} +#endif diff --git a/thirdparty/timidityplus/timiditypp/sfitem.h b/thirdparty/timidityplus/timiditypp/sfitem.h new file mode 100644 index 000000000..ef6b4144d --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/sfitem.h @@ -0,0 +1,94 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/*================================================================ + * sfitem.h + * soundfont generator conversion table + *================================================================*/ + +#ifndef SFITEM_H_DEF +#define SFITEM_H_DEF + +#include "sflayer.h" +#include "sffile.h" + +namespace TimidityPlus +{ + + +typedef struct _LayerItem { + int copy; /* copy policy */ + int type; /* conversion type */ + int minv; /* minimum value */ + int maxv; /* maximum value */ + int defv; /* default value */ +} LayerItem; + +/* copy policy */ +enum { + L_INHRT, /* add to global */ + L_OVWRT, /* overwrite on global */ + L_RANGE, /* range */ + L_PRSET, /* preset only */ + L_INSTR /* instrument only */ +}; + +/* data type */ +enum { + T_NOP, /* nothing */ + T_NOCONV, /* no conversion */ + T_OFFSET, /* address offset */ + T_HI_OFF, /* address coarse offset (32k) */ + T_RANGE, /* range; composite values (0-127/0-127) */ + + T_CUTOFF, /* initial cutoff */ + T_FILTERQ, /* initial resonance */ + T_TENPCT, /* effects send */ + T_PANPOS, /* panning position */ + T_ATTEN, /* initial attenuation */ + T_SCALE, /* scale tuning */ + + T_TIME, /* envelope/LFO time */ + T_TM_KEY, /* time change per key */ + T_FREQ, /* LFO frequency */ + T_PSHIFT, /* env/LFO pitch shift */ + T_CSHIFT, /* env/LFO cutoff shift */ + T_TREMOLO, /* LFO tremolo shift */ + T_MODSUST, /* modulation env sustain level */ + T_VOLSUST, /* volume env sustain level */ + + T_EOT /* end of type */ +}; + +/* sbk->sf2 convertor function */ +typedef int (*SBKConv)(int gen, int amount); + +/* macros for range operation */ +#define RANGE(lo,hi) (((hi)&0xff) << 8 | ((lo)&0xff)) +#define LOWNUM(val) ((val) & 0xff) +#define HIGHNUM(val) (((val) >> 8) & 0xff) + +/* layer type definitions */ +extern const LayerItem static_layer_items[SF_EOF]; + +extern int sbk_to_sf2(int oper, int amount, const LayerItem *); + +} +#endif diff --git a/thirdparty/timidityplus/timiditypp/sflayer.h b/thirdparty/timidityplus/timiditypp/sflayer.h new file mode 100644 index 000000000..4119cf480 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/sflayer.h @@ -0,0 +1,107 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef ___SFLAYER_H_ +#define ___SFLAYER_H_ + +namespace TimidityPlus +{ + +/*================================================================ + * sflayer.h + * SoundFont layer structure + *================================================================*/ + +enum { + SF_startAddrs, /* 0 sample start address -4 (0to*0xffffff)*/ + SF_endAddrs, /* 1 */ + SF_startloopAddrs, /* 2 loop start address -4 (0 to * 0xffffff) */ + SF_endloopAddrs, /* 3 loop end address -3 (0 to * 0xffffff) */ + SF_startAddrsHi, /* 4 high word of startAddrs */ + SF_lfo1ToPitch, /* 5 main fm: lfo1-> pitch */ + SF_lfo2ToPitch, /* 6 aux fm: lfo2-> pitch */ + SF_env1ToPitch, /* 7 pitch env: env1(aux)-> pitch */ + SF_initialFilterFc, /* 8 initial filter cutoff */ + SF_initialFilterQ, /* 9 filter Q */ + SF_lfo1ToFilterFc, /* 10 filter modulation: lfo1->filter*cutoff */ + SF_env1ToFilterFc, /* 11 filter env: env1(aux)->filter * cutoff */ + SF_endAddrsHi, /* 12 high word of endAddrs */ + SF_lfo1ToVolume, /* 13 tremolo: lfo1-> volume */ + SF_env2ToVolume, /* 14 Env2Depth: env2-> volume */ + SF_chorusEffectsSend, /* 15 chorus */ + SF_reverbEffectsSend, /* 16 reverb */ + SF_panEffectsSend, /* 17 pan */ + SF_auxEffectsSend, /* 18 pan auxdata (internal) */ + SF_sampleVolume, /* 19 used internally */ + SF_unused3, /* 20 */ + SF_delayLfo1, /* 21 delay 0x8000-n*(725us) */ + SF_freqLfo1, /* 22 frequency */ + SF_delayLfo2, /* 23 delay 0x8000-n*(725us) */ + SF_freqLfo2, /* 24 frequency */ + SF_delayEnv1, /* 25 delay 0x8000 - n(725us) */ + SF_attackEnv1, /* 26 attack */ + SF_holdEnv1, /* 27 hold */ + SF_decayEnv1, /* 28 decay */ + SF_sustainEnv1, /* 29 sustain */ + SF_releaseEnv1, /* 30 release */ + SF_autoHoldEnv1, /* 31 */ + SF_autoDecayEnv1, /* 32 */ + SF_delayEnv2, /* 33 delay 0x8000 - n(725us) */ + SF_attackEnv2, /* 34 attack */ + SF_holdEnv2, /* 35 hold */ + SF_decayEnv2, /* 36 decay */ + SF_sustainEnv2, /* 37 sustain */ + SF_releaseEnv2, /* 38 release */ + SF_autoHoldEnv2, /* 39 */ + SF_autoDecayEnv2, /* 40 */ + SF_instrument, /* 41 */ + SF_nop, /* 42 */ + SF_keyRange, /* 43 */ + SF_velRange, /* 44 */ + SF_startloopAddrsHi, /* 45 high word of startloopAddrs */ + SF_keynum, /* 46 */ + SF_velocity, /* 47 */ + SF_initAtten, /* 48 */ + SF_keyTuning, /* 49 */ + SF_endloopAddrsHi, /* 50 high word of endloopAddrs */ + SF_coarseTune, /* 51 */ + SF_fineTune, /* 52 */ + SF_sampleId, /* 53 */ + SF_sampleFlags, /* 54 */ + SF_samplePitch, /* 55 SF1 only */ + SF_scaleTuning, /* 56 */ + SF_keyExclusiveClass, /* 57 */ + SF_rootKey, /* 58 */ + SF_EOF /* 59 */ +}; + + +/*---------------------------------------------------------------- + * layer value table + *----------------------------------------------------------------*/ + +typedef struct _LayerTable { + short val[SF_EOF]; + char set[SF_EOF]; +} LayerTable; + + +} +#endif /* ___SFLAYER_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/sysdep.h b/thirdparty/timidityplus/timiditypp/sysdep.h new file mode 100644 index 000000000..58aa0a843 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/sysdep.h @@ -0,0 +1,87 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SYSDEP_H_INCLUDED +#define SYSDEP_H_INCLUDED 1 + +#include + +#define DEFAULT_AUDIO_BUFFER_BITS 12 +#define SAMPLE_LENGTH_BITS 32 + +#include // int types are defined here + +#include "t_swap.h" + +namespace TimidityPlus +{ + + + /* Instrument files are little-endian, MIDI files big-endian, so we + need to do some conversions. */ +#define XCHG_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) + +#define LE_SHORT(x) LittleShort(x) +#define LE_LONG(x) LittleLong(x) +#define BE_SHORT(x) BigShort(x) +#define BE_LONG(x) BigLong(x) + + /* max_channels is defined in "timidity.h" */ +typedef struct _ChannelBitMask +{ + uint32_t b; /* 32-bit bitvector */ +} ChannelBitMask; +#define CLEAR_CHANNELMASK(bits) ((bits).b = 0) +#define FILL_CHANNELMASK(bits) ((bits).b = ~0) +#define IS_SET_CHANNELMASK(bits, c) ((bits).b & (1u << (c))) +#define SET_CHANNELMASK(bits, c) ((bits).b |= (1u << (c))) +#define UNSET_CHANNELMASK(bits, c) ((bits).b &= ~(1u << (c))) +#define TOGGLE_CHANNELMASK(bits, c) ((bits).b ^= (1u << (c))) +#define COPY_CHANNELMASK(dest, src) ((dest).b = (src).b) +#define REVERSE_CHANNELMASK(bits) ((bits).b = ~(bits).b) +#define COMPARE_CHANNELMASK(bitsA, bitsB) ((bitsA).b == (bitsB).b) + + typedef int16_t sample_t; + typedef int32_t final_volume_t; +# define FINAL_VOLUME(v) (v) +# define MAX_AMP_VALUE ((1<<(AMP_BITS+1))-1) +#define MIN_AMP_VALUE (MAX_AMP_VALUE >> 9) + + typedef uint32_t splen_t; +#define SPLEN_T_MAX (splen_t)((uint32_t)0xFFFFFFFF) + +# define TIM_FSCALE(a,b) ((a) * (double)(1<<(b))) +# define TIM_FSCALENEG(a,b) ((a) * (1.0 / (double)(1<<(b)))) + + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif /* M_PI */ + + +#if defined(_MSC_VER) +#define strncasecmp(a,b,c) _strnicmp((a),(b),(c)) +#define strcasecmp(a,b) _stricmp((a),(b)) +#endif /* _MSC_VER */ + +#define SAFE_CONVERT_LENGTH(len) (6 * (len) + 1) + +} +#endif /* SYSDEP_H_INCUDED */ diff --git a/thirdparty/timidityplus/timiditypp/t_swap.h b/thirdparty/timidityplus/timiditypp/t_swap.h new file mode 100644 index 000000000..de9b7780a --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/t_swap.h @@ -0,0 +1,255 @@ +// +// DESCRIPTION: +// Endianess handling, swapping 16bit and 32bit. +// +//----------------------------------------------------------------------------- + + +#ifndef __M_SWAP_H__ +#define __M_SWAP_H__ + +#include + +// Endianess handling. +// WAD files are stored little endian. + +#ifdef __APPLE__ +#include + +inline short LittleShort(short x) +{ + return (short)OSSwapLittleToHostInt16((uint16_t)x); +} + +inline unsigned short LittleShort(unsigned short x) +{ + return OSSwapLittleToHostInt16(x); +} + +inline short LittleShort(int x) +{ + return OSSwapLittleToHostInt16((uint16_t)x); +} + +inline unsigned short LittleShort(unsigned int x) +{ + return OSSwapLittleToHostInt16((uint16_t)x); +} + +inline int LittleLong(int x) +{ + return OSSwapLittleToHostInt32((uint32_t)x); +} + +inline unsigned int LittleLong(unsigned int x) +{ + return OSSwapLittleToHostInt32(x); +} + +inline short BigShort(short x) +{ + return (short)OSSwapBigToHostInt16((uint16_t)x); +} + +inline unsigned short BigShort(unsigned short x) +{ + return OSSwapBigToHostInt16(x); +} + +inline int BigLong(int x) +{ + return OSSwapBigToHostInt32((uint32_t)x); +} + +inline unsigned int BigLong(unsigned int x) +{ + return OSSwapBigToHostInt32(x); +} + +#elif defined __BIG_ENDIAN__ + +// Swap 16bit, that is, MSB and LSB byte. +// No masking with 0xFF should be necessary. +inline short LittleShort (short x) +{ + return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8)); +} + +inline unsigned short LittleShort (unsigned short x) +{ + return (unsigned short)((x>>8) | (x<<8)); +} + +inline short LittleShort (int x) +{ + return LittleShort((short)x); +} + +inline unsigned short LittleShort (unsigned int x) +{ + return LittleShort((unsigned short)x); +} + +// Swapping 32bit. +inline unsigned int LittleLong (unsigned int x) +{ + return (unsigned int)( + (x>>24) + | ((x>>8) & 0xff00) + | ((x<<8) & 0xff0000) + | (x<<24)); +} + +inline int LittleLong (int x) +{ + return (int)( + (((unsigned int)x)>>24) + | ((((unsigned int)x)>>8) & 0xff00) + | ((((unsigned int)x)<<8) & 0xff0000) + | (((unsigned int)x)<<24)); +} + +inline short BigShort(short x) +{ + return x; +} + +inline unsigned short BigShort(unsigned short x) +{ + return x; +} + +inline unsigned int BigLong(unsigned int x) +{ + return x; +} + +inline int BigLong(int x) +{ + return x; +} + +#else + +inline short LittleShort(short x) +{ + return x; +} + +inline unsigned short LittleShort(unsigned short x) +{ + return x; +} + +inline unsigned int LittleLong(unsigned int x) +{ + return x; +} + +inline int LittleLong(int x) +{ + return x; +} + +#ifdef _MSC_VER + +inline short BigShort(short x) +{ + return (short)_byteswap_ushort((unsigned short)x); +} + +inline unsigned short BigShort(unsigned short x) +{ + return _byteswap_ushort(x); +} + +inline int BigLong(int x) +{ + return (int)_byteswap_ulong((unsigned long)x); +} + +inline unsigned int BigLong(unsigned int x) +{ + return (unsigned int)_byteswap_ulong((unsigned long)x); +} +#pragma warning (default: 4035) + +#else + +inline short BigShort (short x) +{ + return (short)((((unsigned short)x)>>8) | (((unsigned short)x)<<8)); +} + +inline unsigned short BigShort (unsigned short x) +{ + return (unsigned short)((x>>8) | (x<<8)); +} + +inline unsigned int BigLong (unsigned int x) +{ + return (unsigned int)( + (x>>24) + | ((x>>8) & 0xff00) + | ((x<<8) & 0xff0000) + | (x<<24)); +} + +inline int BigLong (int x) +{ + return (int)( + (((unsigned int)x)>>24) + | ((((unsigned int)x)>>8) & 0xff00) + | ((((unsigned int)x)<<8) & 0xff0000) + | (((unsigned int)x)<<24)); +} + +#endif + +#endif // __BIG_ENDIAN__ + +// These may be destructive so they should create errors +unsigned long BigLong(unsigned long) = delete; +long BigLong(long) = delete; +unsigned long LittleLong(unsigned long) = delete; +long LittleLong(long) = delete; + + +// Data accessors, since some data is highly likely to be unaligned. +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) +inline int GetShort(const unsigned char *foo) +{ + return *(const short *)foo; +} +inline int GetInt(const unsigned char *foo) +{ + return *(const int *)foo; +} +#else +inline int GetShort(const unsigned char *foo) +{ + return short(foo[0] | (foo[1] << 8)); +} +inline int GetInt(const unsigned char *foo) +{ + return int(foo[0] | (foo[1] << 8) | (foo[2] << 16) | (foo[3] << 24)); +} +#endif +inline int GetBigInt(const unsigned char *foo) +{ + return int((foo[0] << 24) | (foo[1] << 16) | (foo[2] << 8) | foo[3]); +} + +#ifdef __BIG_ENDIAN__ +inline int GetNativeInt(const unsigned char *foo) +{ + return GetBigInt(foo); +} +#else +inline int GetNativeInt(const unsigned char *foo) +{ + return GetInt(foo); +} +#endif + +#endif // __M_SWAP_H__ diff --git a/thirdparty/timidityplus/timiditypp/tables.h b/thirdparty/timidityplus/timiditypp/tables.h new file mode 100644 index 000000000..9fbc5704e --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/tables.h @@ -0,0 +1,96 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2004 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + tables.h +*/ + +#ifndef ___TABLES_H_ +#define ___TABLES_H_ + +#include +#include "sysdep.h" + +namespace TimidityPlus +{ + + inline double lookup_sine(double x) + { + return (sin((2 * M_PI / 1024.0) * (x))); + } + + extern double lookup_triangular(int x); + extern double lookup_log(int x); + +#define SINE_CYCLE_LENGTH 1024 + extern int32_t freq_table[]; + extern int32_t freq_table_zapped[]; + extern int32_t freq_table_tuning[][128]; + extern int32_t freq_table_pytha[][128]; + extern int32_t freq_table_meantone[][128]; + extern int32_t freq_table_pureint[][128]; + extern double *vol_table; + extern double def_vol_table[]; + extern double gs_vol_table[]; + extern double *xg_vol_table; /* == gs_vol_table */ + extern double *pan_table; + extern double bend_fine[]; + extern double bend_coarse[]; + extern const double midi_time_table[], midi_time_table2[]; + extern const uint8_t reverb_macro_presets[]; + extern const uint8_t chorus_macro_presets[]; + extern const uint8_t delay_macro_presets[]; + extern const float delay_time_center_table[]; + extern const float pre_delay_time_table[]; + extern const float chorus_delay_time_table[]; + extern const float rate1_table[]; + extern double attack_vol_table[]; + extern double perceived_vol_table[]; + extern double gm2_vol_table[]; + extern float sc_eg_attack_table[]; + extern float sc_eg_decay_table[]; + extern float sc_eg_release_table[]; + extern double sc_vel_table[]; + extern double sc_vol_table[]; + extern double sc_pan_table[], gm2_pan_table[]; + extern double sc_drum_level_table[]; + extern double sb_vol_table[]; + extern double modenv_vol_table[]; + extern const float cb_to_amp_table[]; + extern const float reverb_time_table[]; + extern const float pan_delay_table[]; + extern const float chamberlin_filter_db_to_q_table[]; + extern const uint8_t multi_eq_block_table_xg[]; + extern const float eq_freq_table_xg[]; + extern const float lfo_freq_table_xg[]; + extern const float mod_delay_offset_table_xg[]; + extern const float reverb_time_table_xg[]; + extern const float delay_time_table_xg[]; + extern const int16_t cutoff_freq_table_gs[]; + extern const int16_t lpf_table_gs[]; + extern const int16_t eq_freq_table_gs[]; + extern const float lofi_sampling_freq_table_xg[]; + + extern void init_tables(void); + + struct Sample; + int32_t get_note_freq(Sample *sp, int note); + +} + +#endif /* ___TABLES_H_ */ diff --git a/thirdparty/timidityplus/timiditypp/timidity.h b/thirdparty/timidityplus/timiditypp/timidity.h new file mode 100644 index 000000000..34d5a5477 --- /dev/null +++ b/thirdparty/timidityplus/timiditypp/timidity.h @@ -0,0 +1,180 @@ +/* + TiMidity++ -- MIDI to WAVE converter and player + Copyright (C) 1999-2002 Masanao Izumo + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Historical issues: This file once was a huge header file, but now is + * devided into some smaller ones. Please do not add things to this + * header, but consider put them on other files. + */ + + +#ifndef TIMIDITY_H_INCLUDED +#define TIMIDITY_H_INCLUDED 1 + +#include "controls.h" +#include "mblock.h" +#include "../../../source/zmusic/critsec.h" +#include +#include + +#ifdef _MSC_VER +#pragma warning(disable:4244) // double->float truncation occurs so often in here that it's pointless to fix it all. +#endif + + + +/* + Table of contents: + (1) Flags and definitions to customize timidity + (3) inportant definitions not to customize + (2) #includes -- include other headers + */ + +/*****************************************************************************\ + section 1: some customize issues +\*****************************************************************************/ + + +/* How many bits to use for the fractional part of sample positions. + This affects tonal accuracy. The entire position counter must fit + in 32 bits, so with FRACTION_BITS equal to 12, the maximum size of + a sample is 1048576 samples (2 megabytes in memory). The GUS gets + by with just 9 bits and a little help from its friends... + "The GUS does not SUCK!!!" -- a happy user :) */ +#define FRACTION_BITS 12 + /* change FRACTION_BITS above, not this */ +#define FRACTION_MASK (~(0xFFFFFFFF << FRACTION_BITS)) + + +/* The number of samples to use for ramping out a dying note. Affects + click removal. */ +#define MAX_DIE_TIME 20 + + +/* Define the pre-resampling cache size. + * This value is default. You can change the cache saze with + * command line option. + */ +#define DEFAULT_CACHE_DATA_SIZE (2*1024*1024) + + +/*****************************************************************************\ + section 2: some important definitions +\*****************************************************************************/ +/* + Anything below this shouldn't need to be changed unless you're porting + to a new machine with other than 32-bit, big-endian words. + */ + + +/* Audio buffer size has to be a power of two to allow DMA buffer + fragments under the VoxWare (Linux & FreeBSD) audio driver */ +#define AUDIO_BUFFER_SIZE (1<<12) + +/* These affect general volume */ +#define GUARD_BITS 3 +#define AMP_BITS (15-GUARD_BITS) + +#define MAX_AMPLIFICATION 800 +#define MAX_CHANNELS 32 + +/* Vibrato and tremolo Choices of the Day */ +#define SWEEP_TUNING 38 +#define VIBRATO_AMPLITUDE_TUNING 1.0L +#define VIBRATO_RATE_TUNING 38 +#define TREMOLO_AMPLITUDE_TUNING 1.0L +#define TREMOLO_RATE_TUNING 38 + +#define SWEEP_SHIFT 16 +#define RATE_SHIFT 5 + +#define VIBRATO_SAMPLE_INCREMENTS 32 + +#define MODULATION_WHEEL_RATE (1.0/6.0) +/* #define MODULATION_WHEEL_RATE (midi_time_ratio/8.0) */ +/* #define MODULATION_WHEEL_RATE (current_play_tempo/500000.0/32.0) */ + +#define VIBRATO_DEPTH_TUNING (1.0/4.0) + +/* malloc's limit */ +#define MAX_SAFE_MALLOC_SIZE (1<<23) /* 8M */ + +#define DEFAULT_SOUNDFONT_ORDER 0 + + +/*****************************************************************************\ + section 3: include other headers +\*****************************************************************************/ + +namespace TimidityPlus +{ + +extern FCriticalSection ConfigMutex; +extern int timidity_modulation_wheel; +extern int timidity_portamento; +extern int timidity_reverb; +extern int timidity_chorus; +extern int timidity_surround_chorus; +extern int timidity_channel_pressure; +extern int timidity_lpf_de; +extern int timidity_temper_control; +extern int timidity_modulation_envelope; +extern int timidity_overlap_voice_allow; +extern int timidity_drum_effect; +extern int timidity_pan_delay; +extern float timidity_drum_power; +extern int timidity_key_adjust; +extern float timidity_tempo_adjust; +extern float min_sustain_time; +extern int timidity_lpf_def; + +extern int32_t playback_rate; +extern int32_t control_ratio; // derived from playback_rate + +enum play_system_modes +{ + DEFAULT_SYSTEM_MODE, + GM_SYSTEM_MODE, + GM2_SYSTEM_MODE, + GS_SYSTEM_MODE, + XG_SYSTEM_MODE +}; + + +const int DEFAULT_VOICES = 256; + +// These were configurable in Timidity++ but it doesn't look like this is really needed. +// In case it becomes necessary, they can be turned into CVARs. +const int default_tonebank = 0; +const int special_tonebank = -1; +const int effect_lr_mode = -1; +const int effect_lr_delay_msec = 25; +const int adjust_panning_immediately = 1; +const int antialiasing_allowed = 0; +const int fast_decay = 0; +const int cutoff_allowed = 0; +const int opt_force_keysig = 8; +const int max_voices = DEFAULT_VOICES; +const int temper_type_mute = 0; +const int opt_preserve_silence = 0; +const int opt_init_keysig = 8; + +} +#endif /* TIMIDITY_H_INCLUDED */ diff --git a/thirdparty/wildmidi/CMakeLists.txt b/thirdparty/wildmidi/CMakeLists.txt new file mode 100644 index 000000000..03fd6afbe --- /dev/null +++ b/thirdparty/wildmidi/CMakeLists.txt @@ -0,0 +1,29 @@ +add_library(wildmidi OBJECT + file_io.cpp + gus_pat.cpp + reverb.cpp + wildmidi_lib.cpp + wm_error.cpp +) + +file(GLOB HEADER_FILES wildmidi/*.h) +target_sources(wildmidi PRIVATE ${HEADER_FILES}) + +set_target_properties(wildmidi +PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON +) + +target_include_directories(wildmidi INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE wildmidi) + +use_fast_math(wildmidi) +require_stricmp(wildmidi PRIVATE) +require_strnicmp(wildmidi PRIVATE) + +if(COMPILER_IS_GNUC_COMPATIBLE) + target_compile_options(wildmidi + PRIVATE + "-Wall" "-Wextra" "-Wno-unused-parameter" "-fomit-frame-pointer" + ) +endif() diff --git a/thirdparty/wildmidi/file_io.cpp b/thirdparty/wildmidi/file_io.cpp new file mode 100644 index 000000000..b0c820b20 --- /dev/null +++ b/thirdparty/wildmidi/file_io.cpp @@ -0,0 +1,84 @@ +/* +** file_io.cpp +** ZDoom compatible file IO interface for WildMIDI +** (This file was completely redone to remove the low level IO code references) +** +**--------------------------------------------------------------------------- +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include +#include + +#include "wm_error.h" +#include "file_io.h" + + +namespace WildMidi +{ + +static const int WM_MAXFILESIZE = 0x1fffffff; + +unsigned char *_WM_BufferFile(MusicIO::SoundFontReaderInterface *reader, const char *filename, unsigned long int *size, std::string *fullname) +{ + auto fp = reader->open_file(filename); + + if (!fp) + { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); + return NULL; + } + + auto fsize = fp->filelength(); + + if (fsize > WM_MAXFILESIZE) + { + /* don't bother loading suspiciously long files */ + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LONGFIL, filename, 0); + return NULL; + } + + unsigned char *data = (unsigned char*)malloc(fsize+1); + if (data == NULL) + { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); + return NULL; + } + + fp->seek(0, SEEK_SET); + fp->read(data, fsize); + if (fullname)* fullname = fp->filename; + fp->close(); + data[fsize] = 0; + *size = (long)fsize; + return data; +} +} diff --git a/thirdparty/wildmidi/gus_pat.cpp b/thirdparty/wildmidi/gus_pat.cpp new file mode 100644 index 000000000..fbfc57971 --- /dev/null +++ b/thirdparty/wildmidi/gus_pat.cpp @@ -0,0 +1,1033 @@ +/* + * gus_pat.c -- Midi Wavetable Processing library + * + * Copyright (C) WildMIDI Developers 2001-2015 + * + * This file is part of WildMIDI. + * + * WildMIDI is free software: you can redistribute and/or modify the player + * under the terms of the GNU General Public License and you can redistribute + * and/or modify the library under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of + * the licenses, or(at your option) any later version. + * + * WildMIDI is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the + * GNU Lesser General Public License along with WildMIDI. If not, see + * . + */ + +#include +#include +#include +#include + +#include "gus_pat.h" +#include "common.h" +#include "wm_error.h" +#include "file_io.h" +#include "wildmidi_lib.h" + +namespace WildMidi +{ + + /* Guspat Envelope Rate Timings */ + + float env_time_table[] = { + /* Row 1 = (4095.0 / (x * ( 1.0 / (1.6 * 14.0) ))) / 1000000.0 */ + 0.0f, 0.091728000f, 0.045864000f, 0.030576000f, 0.022932000f, 0.018345600f, 0.015288000f, 0.013104000f, + 0.011466000f, 0.010192000f, 0.009172800f, 0.008338909f, 0.007644000f, 0.007056000f, 0.006552000f, 0.006115200f, + 0.005733000f, 0.005395765f, 0.005096000f, 0.004827789f, 0.004586400f, 0.004368000f, 0.004169455f, 0.003988174f, + 0.003822000f, 0.003669120f, 0.003528000f, 0.003397333f, 0.003276000f, 0.003163034f, 0.003057600f, 0.002958968f, + 0.002866500f, 0.002779636f, 0.002697882f, 0.002620800f, 0.002548000f, 0.002479135f, 0.002413895f, 0.002352000f, + 0.002293200f, 0.002237268f, 0.002184000f, 0.002133209f, 0.002084727f, 0.002038400f, 0.001994087f, 0.001951660f, + 0.001911000f, 0.001872000f, 0.001834560f, 0.001798588f, 0.001764000f, 0.001730717f, 0.001698667f, 0.001667782f, + 0.001638000f, 0.001609263f, 0.001581517f, 0.001554712f, 0.001528800f, 0.001503738f, 0.001479484f, 0.001456000f, + + /* Row 2 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 8.0 ))) / 1000000.0 */ + 0.0f, 0.733824000f, 0.366912000f, 0.244608000f, 0.183456000f, 0.146764800f, 0.122304000f, 0.104832000f, + 0.091728000f, 0.081536000f, 0.073382400f, 0.066711273f, 0.061152000f, 0.056448000f, 0.052416000f, 0.048921600f, + 0.045864000f, 0.043166118f, 0.040768000f, 0.038622316f, 0.036691200f, 0.034944000f, 0.033355636f, 0.031905391f, + 0.030576000f, 0.029352960f, 0.028224000f, 0.027178667f, 0.026208000f, 0.025304276f, 0.024460800f, 0.023671742f, + 0.022932000f, 0.022237091f, 0.021583059f, 0.020966400f, 0.020384000f, 0.019833081f, 0.019311158f, 0.018816000f, + 0.018345600f, 0.017898146f, 0.017472000f, 0.017065674f, 0.016677818f, 0.016307200f, 0.015952696f, 0.015613277f, + 0.015288000f, 0.014976000f, 0.014676480f, 0.014388706f, 0.014112000f, 0.013845736f, 0.013589333f, 0.013342255f, + 0.013104000f, 0.012874105f, 0.012652138f, 0.012437695f, 0.012230400f, 0.012029902f, 0.011835871f, 0.011648000f, + + /* Row 3 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 64.0 ))) / 1000000.0 */ + 0.0f, 5.870592000f, 2.935296000f, 1.956864000f, 1.467648000f, 1.174118400f, 0.978432000f, 0.838656000f, + 0.733824000f, 0.652288000f, 0.587059200f, 0.533690182f, 0.489216000f, 0.451584000f, 0.419328000f, 0.391372800f, + 0.366912000f, 0.345328941f, 0.326144000f, 0.308978526f, 0.293529600f, 0.279552000f, 0.266845091f, 0.255243130f, + 0.244608000f, 0.234823680f, 0.225792000f, 0.217429333f, 0.209664000f, 0.202434207f, 0.195686400f, 0.189373935f, + 0.183456000f, 0.177896727f, 0.172664471f, 0.167731200f, 0.163072000f, 0.158664649f, 0.154489263f, 0.150528000f, + 0.146764800f, 0.143185171f, 0.139776000f, 0.136525395f, 0.133422545f, 0.130457600f, 0.127621565f, 0.124906213f, + 0.122304000f, 0.119808000f, 0.117411840f, 0.115109647f, 0.112896000f, 0.110765887f, 0.108714667f, 0.106738036f, + 0.104832000f, 0.102992842f, 0.101217103f, 0.099501559f, 0.097843200f, 0.096239213f, 0.094686968f, 0.093184000f, + + /* Row 4 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 512.0))) / 1000000.0 */ + 0.0f, 46.964736000f,23.482368000f,15.654912000f,11.741184000f, 9.392947200f, 7.827456000f, 6.709248000f, + 5.870592000f, 5.218304000f, 4.696473600f, 4.269521455f, 3.913728000f, 3.612672000f, 3.354624000f, 3.130982400f, + 2.935296000f, 2.762631529f, 2.609152000f, 2.471828211f, 2.348236800f, 2.236416000f, 2.134760727f, 2.041945043f, + 1.956864000f, 1.878589440f, 1.806336000f, 1.739434667f, 1.677312000f, 1.619473655f, 1.565491200f, 1.514991484f, + 1.467648000f, 1.423173818f, 1.381315765f, 1.341849600f, 1.304576000f, 1.269317189f, 1.235914105f, 1.204224000f, + 1.174118400f, 1.145481366f, 1.118208000f, 1.092203163f, 1.067380364f, 1.043660800f, 1.020972522f, 0.999249702f, + 0.978432000f, 0.958464000f, 0.939294720f, 0.920877176f, 0.903168000f, 0.886127094f, 0.869717333f, 0.853904291f, + 0.838656000f, 0.823942737f, 0.809736828f, 0.796012475f, 0.782745600f, 0.769913705f, 0.757495742f, 0.745472000f + }; +#ifdef DEBUG_GUSPAT +#define GUSPAT_FILENAME_DEBUG(dx) fprintf(stderr,"\r%s\n",dx) + +#define GUSPAT_INT_DEBUG(dx,dy) fprintf(stderr,"\r%s: %i\n",dx,dy) +#define GUSPAT_FLOAT_DEBUG(dx,dy) fprintf(stderr,"\r%s: %f\n",dx,dy) +#define GUSPAT_START_DEBUG() fprintf(stderr,"\r") +#define GUSPAT_MODE_DEBUG(dx,dy,dz) if (dx & dy) fprintf(stderr,"%s",dz) +#define GUSPAT_END_DEBUG() fprintf(stderr,"\n") +#else +#define GUSPAT_FILENAME_DEBUG(dx) +#define GUSPAT_INT_DEBUG(dx,dy) +#define GUSPAT_FLOAT_DEBUG(dx,dy) +#define GUSPAT_START_DEBUG() +#define GUSPAT_MODE_DEBUG(dx,dy,dz) +#define GUSPAT_END_DEBUG() +#endif + +#ifdef DEBUG_SAMPLES +#define SAMPLE_CONVERT_DEBUG(dx) printf("\r%s\n",dx) +#else +#define SAMPLE_CONVERT_DEBUG(dx) +#endif +/* + * sample data conversion functions + * convert data to signed shorts + */ + +/* 8bit signed */ +static int convert_8s(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = (*read_data++) << 8; + } while (read_data != read_end); + return 0; + } + + _WM_ERROR_NEW("Calloc failed (%s)\n", strerror(errno)); + return -1; +} + +/* 8bit signed ping pong */ +static int convert_8sp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = (*read_data++) << 8; + } while (read_data != read_end); + + *write_data = (*read_data++ << 8); + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = (*read_data++ << 8); + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b++ = (*read_data++) << 8; + } while (read_data != read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit signed reverse */ +static int convert_8sr(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + gus_sample->data_length - 1; + do { + *write_data-- = (*read_data++) << 8; + } while (read_data != read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->modes ^= SAMPLE_REVERSE; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit signed reverse ping pong */ +static int convert_8srp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = (*read_data--) << 8; + } while (read_data != read_end); + + *write_data = (*read_data-- << 8); + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_start; + do { + *write_data = (*read_data--) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = (*read_data-- << 8); + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b++ = (*read_data--) << 8; + write_data_b++; + } while (read_data != read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned */ +static int convert_8u(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + gus_sample->modes ^= SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned ping pong */ +static int convert_8up(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + + *write_data = ((*read_data++) ^ 0x80) << 8; + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_end; + do { + *write_data = ((*read_data++) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = ((*read_data++) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b++ = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_UNSIGNED; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned reverse */ +static int convert_8ur(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + gus_sample->data_length - 1; + do { + *write_data-- = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned reverse ping pong */ +static int convert_8urp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = ((*read_data--) ^ 0x80) << 8; + } while (read_data != read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_start; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b++ = ((*read_data--) ^ 0x80) << 8; + } while (read_data != read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed */ +static int convert_16s(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = *read_data++; + *write_data++ |= (*read_data++) << 8; + } while (read_data < read_end); + + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed ping pong */ +static int convert_16sp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = (*read_data++); + *write_data++ |= (*read_data++) << 8; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= (*read_data++) << 8; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++); + *write_data |= (*read_data++) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = *(read_data++); + *write_data |= (*read_data++) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b = *(read_data++); + *write_data_b++ |= (*read_data++) << 8; + } while (read_data < read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed reverse */ +static int convert_16sr(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1; + do { + *write_data = *read_data++; + *write_data-- |= (*read_data++) << 8; + } while (read_data < read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_REVERSE; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed reverse ping pong */ +static int convert_16srp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = (*read_data--) << 8; + *write_data++ |= *read_data--; + } while (read_data < read_end); + + *write_data = (*read_data-- << 8); + *write_data |= *read_data--; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_start; + do { + *write_data = (*read_data--) << 8; + *write_data |= *read_data--; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = ((*read_data--) << 8); + *write_data |= *read_data--; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b = (*read_data--) << 8; + *write_data_b++ |= *read_data--; + } while (read_data < read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned */ +static int convert_16u(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = *read_data++; + *write_data++ |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned ping pong */ +static int convert_16up(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = (*read_data++); + *write_data++ |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b = (*read_data++); + *write_data_b++ |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned reverse */ +static int convert_16ur(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1; + do { + *write_data = *read_data++; + *write_data-- |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned reverse ping pong */ +static int convert_16urp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data++ |= *read_data--; + } while (read_data < read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_start; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b = ((*read_data--) ^ 0x80) << 8; + *write_data_b++ |= *read_data--; + } while (read_data < read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* sample loading */ + +struct _sample * Instruments::load_gus_pat(const char *filename) +{ + unsigned char *gus_patch; + unsigned long int gus_size; + unsigned long int gus_ptr; + unsigned char no_of_samples; + struct _sample *gus_sample = NULL; + struct _sample *first_gus_sample = NULL; + unsigned long int i = 0; + + int (*do_convert[])(unsigned char *data, struct _sample *gus_sample) = { + convert_8s, + convert_16s, + convert_8u, + convert_16u, + convert_8sp, + convert_16sp, + convert_8up, + convert_16up, + convert_8sr, + convert_16sr, + convert_8ur, + convert_16ur, + convert_8srp, + convert_16srp, + convert_8urp, + convert_16urp + }; + unsigned long int tmp_loop; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); SAMPLE_CONVERT_DEBUG(filename); + + if ((gus_patch = _WM_BufferFile(sfreader, filename, &gus_size)) == NULL) { + return NULL; + } + if (gus_size < 239) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + if (memcmp(gus_patch, "GF1PATCH110\0ID#000002", 22) + && memcmp(gus_patch, "GF1PATCH100\0ID#000002", 22)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(unsupported format)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + if (gus_patch[82] > 1) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(unsupported format)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + if (gus_patch[151] > 1) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(unsupported format)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + + GUSPAT_FILENAME_DEBUG(filename); GUSPAT_INT_DEBUG("voices",gus_patch[83]); + + no_of_samples = gus_patch[198]; + gus_ptr = 239; + while (no_of_samples) { + unsigned long int tmp_cnt; + if (first_gus_sample == NULL) { + first_gus_sample = (struct _sample*)malloc(sizeof(struct _sample)); + gus_sample = first_gus_sample; + } else { + gus_sample->next = (struct _sample*)malloc(sizeof(struct _sample)); + gus_sample = gus_sample->next; + } + if (gus_sample == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + + gus_sample->next = NULL; + gus_sample->loop_fraction = gus_patch[gus_ptr + 7]; + gus_sample->data_length = (gus_patch[gus_ptr + 11] << 24) + | (gus_patch[gus_ptr + 10] << 16) + | (gus_patch[gus_ptr + 9] << 8) | gus_patch[gus_ptr + 8]; + gus_sample->loop_start = (gus_patch[gus_ptr + 15] << 24) + | (gus_patch[gus_ptr + 14] << 16) + | (gus_patch[gus_ptr + 13] << 8) | gus_patch[gus_ptr + 12]; + gus_sample->loop_end = (gus_patch[gus_ptr + 19] << 24) + | (gus_patch[gus_ptr + 18] << 16) + | (gus_patch[gus_ptr + 17] << 8) | gus_patch[gus_ptr + 16]; + gus_sample->rate = (gus_patch[gus_ptr + 21] << 8) + | gus_patch[gus_ptr + 20]; + gus_sample->freq_low = ((gus_patch[gus_ptr + 25] << 24) + | (gus_patch[gus_ptr + 24] << 16) + | (gus_patch[gus_ptr + 23] << 8) | gus_patch[gus_ptr + 22]); + gus_sample->freq_high = ((gus_patch[gus_ptr + 29] << 24) + | (gus_patch[gus_ptr + 28] << 16) + | (gus_patch[gus_ptr + 27] << 8) | gus_patch[gus_ptr + 26]); + gus_sample->freq_root = ((gus_patch[gus_ptr + 33] << 24) + | (gus_patch[gus_ptr + 32] << 16) + | (gus_patch[gus_ptr + 31] << 8) | gus_patch[gus_ptr + 30]); + + /* This is done this way instead of ((freq * 1024) / rate) to avoid 32bit overflow. */ + /* Result is 0.001% inacurate */ + gus_sample->inc_div = ((gus_sample->freq_root * 512) / gus_sample->rate) * 2; + +#if 0 + /* We dont use this info at this time, kept in here for info */ + printf("\rTremolo Sweep: %i, Rate: %i, Depth %i\n", + gus_patch[gus_ptr+49], gus_patch[gus_ptr+50], gus_patch[gus_ptr+51]); + printf("\rVibrato Sweep: %i, Rate: %i, Depth %i\n", + gus_patch[gus_ptr+52], gus_patch[gus_ptr+53], gus_patch[gus_ptr+54]); +#endif + gus_sample->modes = gus_patch[gus_ptr + 55]; + GUSPAT_START_DEBUG(); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_16BIT, "16bit "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_UNSIGNED, "Unsigned "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_LOOP, "Loop "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_PINGPONG, "PingPong "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_REVERSE, "Reverse "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_SUSTAIN, "Sustain "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_ENVELOPE, "Envelope "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_CLAMPED, "Clamped "); GUSPAT_END_DEBUG(); + + if (gus_sample->loop_start > gus_sample->loop_end) { + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->loop_start; + gus_sample->loop_start = tmp_loop; + gus_sample->loop_fraction = + ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + } + + /* + FIXME: Experimental Hacky Fix + + This looks for "dodgy" release envelope settings that faulty editors + may have set and attempts to corrects it. + if (fix_release) + Lets make this automatic ... + */ + { + /* + After studying faulty gus_pats this way may work better + Testing to determine if any further adjustments are required + */ + if (env_time_table[gus_patch[gus_ptr + 40]] < env_time_table[gus_patch[gus_ptr + 41]]) { + unsigned char tmp_hack_rate = 0; + + if (env_time_table[gus_patch[gus_ptr + 41]] < env_time_table[gus_patch[gus_ptr + 42]]) { + // 1 2 3 + tmp_hack_rate = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 42] = tmp_hack_rate; + } else if (env_time_table[gus_patch[gus_ptr + 41]] == env_time_table[gus_patch[gus_ptr + 42]]) { + // 1 2 2 + tmp_hack_rate = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 41] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 42] = tmp_hack_rate; + + } else { + if (env_time_table[gus_patch[gus_ptr + 40]] < env_time_table[gus_patch[gus_ptr + 42]]) { + // 1 3 2 + tmp_hack_rate = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = gus_patch[gus_ptr + 41]; + gus_patch[gus_ptr + 41] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 42] = tmp_hack_rate; + } else { + // 2 3 1 or 1 2 1 + tmp_hack_rate = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = gus_patch[gus_ptr + 41]; + gus_patch[gus_ptr + 41] = tmp_hack_rate; + } + } + } else if (env_time_table[gus_patch[gus_ptr + 41]] < env_time_table[gus_patch[gus_ptr + 42]]) { + unsigned char tmp_hack_rate = 0; + + if (env_time_table[gus_patch[gus_ptr + 40]] < env_time_table[gus_patch[gus_ptr + 42]]) { + // 2 1 3 + tmp_hack_rate = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 42] = gus_patch[gus_ptr + 41]; + gus_patch[gus_ptr + 41] = tmp_hack_rate; + } else { + // 3 1 2 + tmp_hack_rate = gus_patch[gus_ptr + 41]; + gus_patch[gus_ptr + 41] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 42] = tmp_hack_rate; + } + } + +#if 0 + if ((env_time_table[gus_patch[gus_ptr + 40]] < env_time_table[gus_patch[gus_ptr + 41]]) && (env_time_table[gus_patch[gus_ptr + 41]] == env_time_table[gus_patch[gus_ptr + 42]])) { + uint8_t tmp_hack_rate = 0; + tmp_hack_rate = gus_patch[gus_ptr + 41]; + gus_patch[gus_ptr + 41] = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 42] = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = tmp_hack_rate; + tmp_hack_rate = gus_patch[gus_ptr + 47]; + gus_patch[gus_ptr + 47] = gus_patch[gus_ptr + 46]; + gus_patch[gus_ptr + 48] = gus_patch[gus_ptr + 46]; + gus_patch[gus_ptr + 46] = tmp_hack_rate; + } +#endif + } + + for (i = 0; i < 6; i++) { + GUSPAT_INT_DEBUG("Envelope #",i); + if (gus_sample->modes & SAMPLE_ENVELOPE) { + unsigned char env_rate = gus_patch[gus_ptr + 37 + i]; + gus_sample->env_target[i] = 16448 * gus_patch[gus_ptr + 43 + i]; + GUSPAT_INT_DEBUG("Envelope Level",gus_patch[gus_ptr+43+i]); GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[env_rate]); + gus_sample->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[env_rate])); + GUSPAT_INT_DEBUG("Envelope Rate",gus_sample->env_rate[i]); GUSPAT_INT_DEBUG("GUSPAT Rate",env_rate); + if (gus_sample->env_rate[i] == 0) { + _WM_ERROR_NEW("Warning: found invalid envelope(%lu) rate setting in %s. Using %f instead.\n", + i, filename, env_time_table[63]); + gus_sample->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[63]); + } + } else { + gus_sample->env_target[i] = 4194303; + gus_sample->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[63]); + } + } + + gus_sample->env_target[6] = 0; + gus_sample->env_rate[6] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + + gus_ptr += 96; + tmp_cnt = gus_sample->data_length; + + if (do_convert[(((gus_sample->modes & 0x18) >> 1) + | (gus_sample->modes & 0x03))](&gus_patch[gus_ptr], gus_sample) + == -1) { + free(gus_patch); + return NULL; + } + + /* + Test and set decay expected decay time after a note off + NOTE: This sets samples for full range decay + */ + if (gus_sample->modes & SAMPLE_ENVELOPE) { + double samples_f = 0.0; + + if (gus_sample->modes & SAMPLE_CLAMPED) { + samples_f = (4194301.0 - (float)gus_sample->env_target[5]) / gus_sample->env_rate[5]; + } else { + if (gus_sample->modes & SAMPLE_SUSTAIN) { + samples_f = (4194301.0 - (float)gus_sample->env_target[3]) / gus_sample->env_rate[3]; + samples_f += (float)(gus_sample->env_target[3] - gus_sample->env_target[4]) / gus_sample->env_rate[4]; + } else { + samples_f = (4194301.0 - (float)gus_sample->env_target[4]) / gus_sample->env_rate[4]; + } + samples_f += (float)(gus_sample->env_target[4] - gus_sample->env_target[5]) / gus_sample->env_rate[5]; + } + samples_f += (float)gus_sample->env_target[5] / gus_sample->env_rate[6]; + } + + gus_ptr += tmp_cnt; + gus_sample->loop_start = (gus_sample->loop_start << 10) + | (((gus_sample->loop_fraction & 0x0f) << 10) / 16); + gus_sample->loop_end = (gus_sample->loop_end << 10) + | (((gus_sample->loop_fraction & 0xf0) << 6) / 16); + gus_sample->loop_size = gus_sample->loop_end - gus_sample->loop_start; + gus_sample->data_length = gus_sample->data_length << 10; + no_of_samples--; + } + free(gus_patch); + return first_gus_sample; +} + +} diff --git a/thirdparty/wildmidi/reverb.cpp b/thirdparty/wildmidi/reverb.cpp new file mode 100644 index 000000000..2f95e28bd --- /dev/null +++ b/thirdparty/wildmidi/reverb.cpp @@ -0,0 +1,401 @@ +/* + reverb.c + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +//#include "config.h" + +#include +#include + +#include "common.h" +#include "reverb.h" + +namespace WildMidi +{ +/* + reverb function + */ +void _WM_reset_reverb(struct _rvb *rvb) { + int i, j, k; + for (i = 0; i < rvb->l_buf_size; i++) { + rvb->l_buf[i] = 0; + } + for (i = 0; i < rvb->r_buf_size; i++) { + rvb->r_buf[i] = 0; + } + for (k = 0; k < 8; k++) { + for (i = 0; i < 6; i++) { + for (j = 0; j < 2; j++) { + rvb->l_buf_flt_in[k][i][j] = 0; + rvb->l_buf_flt_out[k][i][j] = 0; + rvb->r_buf_flt_in[k][i][j] = 0; + rvb->r_buf_flt_out[k][i][j] = 0; + } + } + } +} + +/* + _WM_init_reverb + + ========================= + Engine Description + + 8 reflective points around the room + 2 speaker positions + 1 listener position + + Sounds come from the speakers to all points and to the listener. + Sound comes from the reflective points to the listener. + These sounds are combined, put through a filter that mimics surface absorbtion. + The combined sounds are also sent to the reflective points on the opposite side. + + */ +struct _rvb * +_WM_init_reverb(int rate, float room_x, float room_y, float listen_x, + float listen_y) { + + /* filters set at 125Hz, 250Hz, 500Hz, 1000Hz, 2000Hz, 4000Hz */ + double Freq[] = {125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0}; + + /* numbers calculated from + * 101.325 kPa, 20 deg C, 50% relative humidity */ + double dbAirAbs[] = {-0.00044, -0.00131, -0.002728, -0.004665, -0.009887, -0.029665}; + + /* modify these to adjust the absorption qualities of the surface. + * Remember that lower frequencies are less effected by surfaces + * Note: I am currently playing with the values and finding the ideal surfaces + * for nice default reverb. + */ + double dbAttn[8][6] = { + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942}, + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942} + }; + /* + double dbAttn[6] = { + // concrete covered in carpet + // -0.175, -0.537, -1.412, -4.437, -7.959, -7.959 + // pleated drapes + -0.630, -3.223, -5.849, -12.041, -10.458, -7.959 + }; + */ + + /* distance */ + double SPL_DST[8] = {0.0}; + double SPR_DST[8] = {0.0}; + double RFN_DST[8] = {0.0}; + + double MAXL_DST = 0.0; + double MAXR_DST = 0.0; + + double SPL_LSN_XOFS = 0.0; + double SPL_LSN_YOFS = 0.0; + double SPL_LSN_DST = 0.0; + + double SPR_LSN_XOFS = 0.0; + double SPR_LSN_YOFS = 0.0; + double SPR_LSN_DST = 0.0; + + + struct _rvb *rtn_rvb = (struct _rvb*)malloc(sizeof(struct _rvb)); + int j = 0; + int i = 0; + + struct _coord { + double x; + double y; + }; + +#if 0 + struct _coord SPL = {2.5, 5.0}; /* Left Speaker Position */ + struct _coord SPR = {7.5, 5.0}; /* Right Speaker Position */ + /* position of the reflective points */ + struct _coord RFN[] = { + { 5.0, 0.0}, + { 0.0, 6.66666}, + { 0.0, 13.3333}, + { 5.0, 20.0}, + { 10.0, 20.0}, + { 15.0, 13.3333}, + { 15.0, 6.66666}, + { 10.0, 0.0} + }; +#else + struct _coord SPL; /* Left Speaker Position */ + struct _coord SPR; /* Right Speaker Position */ + /* position of the reflective points */ + struct _coord RFN[8]; + + SPL.x = room_x / 4.0; + SPR.x = room_x / 4.0 * 3.0; + SPL.y = room_y / 10.0; + SPR.y = room_y / 10.0; + + RFN[0].x = room_x / 3.0; + RFN[0].y = 0.0; + RFN[1].x = 0.0; + RFN[1].y = room_y / 3.0; + RFN[2].x = 0.0; + RFN[2].y = room_y / 3.0 * 2.0; + RFN[3].x = room_x / 3.0; + RFN[3].y = room_y; + RFN[4].x = room_x / 3.0 * 2.0; + RFN[4].y = room_y; + RFN[5].x = room_x; + RFN[5].y = room_y / 3.0 * 2.0; + RFN[6].x = room_x; + RFN[6].y = room_y / 3.0; + RFN[7].x = room_x / 3.0 * 2.0; + RFN[7].y = 0.0; +#endif + + SPL_LSN_XOFS = SPL.x - listen_x; + SPL_LSN_YOFS = SPL.y - listen_y; + SPL_LSN_DST = sqrt((SPL_LSN_XOFS * SPL_LSN_XOFS) + (SPL_LSN_YOFS * SPL_LSN_YOFS)); + + if (SPL_LSN_DST > MAXL_DST) + MAXL_DST = SPL_LSN_DST; + + SPR_LSN_XOFS = SPR.x - listen_x; + SPR_LSN_YOFS = SPR.y - listen_y; + SPR_LSN_DST = sqrt((SPR_LSN_XOFS * SPR_LSN_XOFS) + (SPR_LSN_YOFS * SPR_LSN_YOFS)); + + if (SPR_LSN_DST > MAXR_DST) + MAXR_DST = SPR_LSN_DST; + + if (rtn_rvb == NULL) { + return NULL; + } + + for (j = 0; j < 8; j++) { + double SPL_RFL_XOFS = 0; + double SPL_RFL_YOFS = 0; + double SPR_RFL_XOFS = 0; + double SPR_RFL_YOFS = 0; + double RFN_XOFS = listen_x - RFN[j].x; + double RFN_YOFS = listen_y - RFN[j].y; + RFN_DST[j] = sqrt((RFN_XOFS * RFN_XOFS) + (RFN_YOFS * RFN_YOFS)); + + SPL_RFL_XOFS = SPL.x - RFN[i].x; + SPL_RFL_YOFS = SPL.y - RFN[i].y; + SPR_RFL_XOFS = SPR.x - RFN[i].x; + SPR_RFL_YOFS = SPR.y - RFN[i].y; + SPL_DST[i] = sqrt( + (SPL_RFL_XOFS * SPL_RFL_XOFS) + (SPL_RFL_YOFS * SPL_RFL_YOFS)); + SPR_DST[i] = sqrt( + (SPR_RFL_XOFS * SPR_RFL_XOFS) + (SPR_RFL_YOFS * SPR_RFL_YOFS)); + /* + add the 2 distances together and remove the speaker to listener distance + so we dont have to delay the initial output + */ + SPL_DST[i] += RFN_DST[i]; + + /* so i dont have to delay speaker output */ + SPL_DST[i] -= SPL_LSN_DST; + + if (i < 4) { + if (SPL_DST[i] > MAXL_DST) + MAXL_DST = SPL_DST[i]; + } else { + if (SPL_DST[i] > MAXR_DST) + MAXR_DST = SPL_DST[i]; + } + + SPR_DST[i] += RFN_DST[i]; + + /* so i dont have to delay speaker output */ + SPR_DST[i] -= SPR_LSN_DST; + + if (i < 4) { + if (SPR_DST[i] > MAXL_DST) + MAXL_DST = SPR_DST[i]; + } else { + if (SPR_DST[i] > MAXR_DST) + MAXR_DST = SPR_DST[i]; + } + + RFN_DST[j] *= 2.0; + + if (j < 4) { + if (RFN_DST[j] > MAXL_DST) + MAXL_DST = RFN_DST[j]; + } else { + if (RFN_DST[j] > MAXR_DST) + MAXR_DST = RFN_DST[j]; + } + + for (i = 0; i < 6; i++) { + double srate = (double) rate; + double bandwidth = 2.0; + double omega = 2.0 * M_PI * Freq[i] / srate; + double sn = sin(omega); + double cs = cos(omega); + double alpha = sn * sinh(M_LN2 / 2 * bandwidth * omega / sn); + double A = pow(10.0, ((/*dbAttn[i]*/dbAttn[j][i] + + (dbAirAbs[i] * RFN_DST[j])) / 40.0) ); + /* + Peaking band EQ filter + */ + double b0 = 1 + (alpha * A); + double b1 = -2 * cs; + double b2 = 1 - (alpha * A); + double a0 = 1 + (alpha / A); + double a1 = -2 * cs; + double a2 = 1 - (alpha / A); + + rtn_rvb->coeff[j][i][0] = (signed int) ((b0 / a0) * 1024.0); + rtn_rvb->coeff[j][i][1] = (signed int) ((b1 / a0) * 1024.0); + rtn_rvb->coeff[j][i][2] = (signed int) ((b2 / a0) * 1024.0); + rtn_rvb->coeff[j][i][3] = (signed int) ((a1 / a0) * 1024.0); + rtn_rvb->coeff[j][i][4] = (signed int) ((a2 / a0) * 1024.0); + } + } + + /* init the reverb buffers */ + rtn_rvb->l_buf_size = (int) ((float) rate * (MAXL_DST / 340.29)); + rtn_rvb->l_buf = (int*)malloc( + sizeof(signed int) * (rtn_rvb->l_buf_size + 1)); + rtn_rvb->l_out = 0; + + rtn_rvb->r_buf_size = (int) ((float) rate * (MAXR_DST / 340.29)); + rtn_rvb->r_buf = (int*)malloc( + sizeof(signed int) * (rtn_rvb->r_buf_size + 1)); + rtn_rvb->r_out = 0; + + for (i = 0; i < 4; i++) { + rtn_rvb->l_sp_in[i] = (int) ((float) rate * (SPL_DST[i] / 340.29)); + rtn_rvb->l_sp_in[i + 4] = (int) ((float) rate + * (SPL_DST[i + 4] / 340.29)); + rtn_rvb->r_sp_in[i] = (int) ((float) rate * (SPR_DST[i] / 340.29)); + rtn_rvb->r_sp_in[i + 4] = (int) ((float) rate + * (SPR_DST[i + 4] / 340.29)); + rtn_rvb->l_in[i] = (int) ((float) rate * (RFN_DST[i] / 340.29)); + rtn_rvb->r_in[i] = (int) ((float) rate * (RFN_DST[i + 4] / 340.29)); + } + + rtn_rvb->gain = 4; + + _WM_reset_reverb(rtn_rvb); + return rtn_rvb; +} + +/* _WM_free_reverb - free up memory used for reverb */ +void _WM_free_reverb(struct _rvb *rvb) { + if (!rvb) return; + free(rvb->l_buf); + free(rvb->r_buf); + free(rvb); +} + +void _WM_do_reverb(struct _rvb *rvb, signed int *buffer, int size) { + int i, j, k; + signed int l_buf_flt = 0; + signed int r_buf_flt = 0; + signed int l_rfl = 0; + signed int r_rfl = 0; + int vol_div = 64; + + for (i = 0; i < size; i += 2) { + signed int tmp_l_val = 0; + signed int tmp_r_val = 0; + /* + add the initial reflections + from each speaker, 4 to go the left, 4 go to the right buffers + */ + tmp_l_val = buffer[i] / vol_div; + tmp_r_val = buffer[i + 1] / vol_div; + for (j = 0; j < 4; j++) { + rvb->l_buf[rvb->l_sp_in[j]] += tmp_l_val; + rvb->l_sp_in[j] = (rvb->l_sp_in[j] + 1) % rvb->l_buf_size; + rvb->l_buf[rvb->r_sp_in[j]] += tmp_r_val; + rvb->r_sp_in[j] = (rvb->r_sp_in[j] + 1) % rvb->l_buf_size; + + rvb->r_buf[rvb->l_sp_in[j + 4]] += tmp_l_val; + rvb->l_sp_in[j + 4] = (rvb->l_sp_in[j + 4] + 1) % rvb->r_buf_size; + rvb->r_buf[rvb->r_sp_in[j + 4]] += tmp_r_val; + rvb->r_sp_in[j + 4] = (rvb->r_sp_in[j + 4] + 1) % rvb->r_buf_size; + } + + /* + filter the reverb output and add to buffer + */ + l_rfl = rvb->l_buf[rvb->l_out]; + rvb->l_buf[rvb->l_out] = 0; + rvb->l_out = (rvb->l_out + 1) % rvb->l_buf_size; + + r_rfl = rvb->r_buf[rvb->r_out]; + rvb->r_buf[rvb->r_out] = 0; + rvb->r_out = (rvb->r_out + 1) % rvb->r_buf_size; + + for (k = 0; k < 8; k++) { + for (j = 0; j < 6; j++) { + l_buf_flt = ((l_rfl * rvb->coeff[k][j][0]) + + (rvb->l_buf_flt_in[k][j][0] * rvb->coeff[k][j][1]) + + (rvb->l_buf_flt_in[k][j][1] * rvb->coeff[k][j][2]) + - (rvb->l_buf_flt_out[k][j][0] * rvb->coeff[k][j][3]) + - (rvb->l_buf_flt_out[k][j][1] * rvb->coeff[k][j][4])) + / 1024; + rvb->l_buf_flt_in[k][j][1] = rvb->l_buf_flt_in[k][j][0]; + rvb->l_buf_flt_in[k][j][0] = l_rfl; + rvb->l_buf_flt_out[k][j][1] = rvb->l_buf_flt_out[k][j][0]; + rvb->l_buf_flt_out[k][j][0] = l_buf_flt; + buffer[i] += l_buf_flt / 8; + + r_buf_flt = ((r_rfl * rvb->coeff[k][j][0]) + + (rvb->r_buf_flt_in[k][j][0] * rvb->coeff[k][j][1]) + + (rvb->r_buf_flt_in[k][j][1] * rvb->coeff[k][j][2]) + - (rvb->r_buf_flt_out[k][j][0] * rvb->coeff[k][j][3]) + - (rvb->r_buf_flt_out[k][j][1] * rvb->coeff[k][j][4])) + / 1024; + rvb->r_buf_flt_in[k][j][1] = rvb->r_buf_flt_in[k][j][0]; + rvb->r_buf_flt_in[k][j][0] = r_rfl; + rvb->r_buf_flt_out[k][j][1] = rvb->r_buf_flt_out[k][j][0]; + rvb->r_buf_flt_out[k][j][0] = r_buf_flt; + buffer[i + 1] += r_buf_flt / 8; + } + } + + /* + add filtered result back into the buffers but on the opposite side + */ + tmp_l_val = buffer[i + 1] / vol_div; + tmp_r_val = buffer[i] / vol_div; + for (j = 0; j < 4; j++) { + rvb->l_buf[rvb->l_in[j]] += tmp_l_val; + rvb->l_in[j] = (rvb->l_in[j] + 1) % rvb->l_buf_size; + + rvb->r_buf[rvb->r_in[j]] += tmp_r_val; + rvb->r_in[j] = (rvb->r_in[j] + 1) % rvb->r_buf_size; + } + } +} + +} diff --git a/thirdparty/wildmidi/wildmidi/common.h b/thirdparty/wildmidi/wildmidi/common.h new file mode 100644 index 000000000..e80a9a7d8 --- /dev/null +++ b/thirdparty/wildmidi/wildmidi/common.h @@ -0,0 +1,95 @@ +/* + common.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __COMMON_H +#define __COMMON_H + +namespace WildMidi +{ + +enum +{ + SAMPLE_16BIT = 0x01, + SAMPLE_UNSIGNED = 0x02, + SAMPLE_LOOP = 0x04, + SAMPLE_PINGPONG = 0x08, + SAMPLE_REVERSE = 0x10, + SAMPLE_SUSTAIN = 0x20, + SAMPLE_ENVELOPE = 0x40, + SAMPLE_CLAMPED = 0x80, +}; + + +struct _sample { + unsigned int data_length; + unsigned int loop_start; + unsigned int loop_end; + unsigned int loop_size; + unsigned char loop_fraction; + unsigned short rate; + unsigned int freq_low; + unsigned int freq_high; + unsigned int freq_root; + unsigned char modes; + signed int env_rate[7]; + signed int env_target[7]; + unsigned int inc_div; + signed short *data; + struct _sample *next; +}; + +struct _env { + float time; + float level; + unsigned char set; +}; + +struct _patch { + unsigned short patchid; + unsigned char loaded; + char *filename; + signed short int amp; + unsigned char keep; + unsigned char remove; + struct _env env[6]; + unsigned char note; + unsigned long int inuse_count; + struct _sample *first_sample; + struct _patch *next; +}; + +/* Set our global defines here */ +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + +} + +#endif /* __COMMON_H */ diff --git a/thirdparty/wildmidi/wildmidi/file_io.h b/thirdparty/wildmidi/wildmidi/file_io.h new file mode 100644 index 000000000..6a3aff6d2 --- /dev/null +++ b/thirdparty/wildmidi/wildmidi/file_io.h @@ -0,0 +1,37 @@ +/* + file_io.c + + file handling + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __FILE_IO_H +#define __FILE_IO_H + +#include "../../../source/zmusic/fileio.h" + +namespace WildMidi +{ + unsigned char *_WM_BufferFile(MusicIO::SoundFontReaderInterface *reader, const char *filename, unsigned long int *size, std::string *fullname = nullptr); +} + +#endif /* __FILE_IO_H */ diff --git a/thirdparty/wildmidi/wildmidi/gus_pat.h b/thirdparty/wildmidi/wildmidi/gus_pat.h new file mode 100644 index 000000000..31e8c5de6 --- /dev/null +++ b/thirdparty/wildmidi/wildmidi/gus_pat.h @@ -0,0 +1,40 @@ +/* + gus_pat.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __GUS_PAT_H +#define __GUS_PAT_H + +namespace WildMidi +{ + +class SoundFontReaderInterface; +/* Guspat Envelope Rate Timings */ +extern float env_time_table[]; + +} + +#endif /* __GUS_PAT_H */ + diff --git a/thirdparty/wildmidi/wildmidi/reverb.h b/thirdparty/wildmidi/wildmidi/reverb.h new file mode 100644 index 000000000..5362792a5 --- /dev/null +++ b/thirdparty/wildmidi/wildmidi/reverb.h @@ -0,0 +1,62 @@ +/* + reverb.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __REVERB_H +#define __REVERB_H + +namespace WildMidi +{ + +struct _rvb { + /* filter data */ + signed int l_buf_flt_in[8][6][2]; + signed int l_buf_flt_out[8][6][2]; + signed int r_buf_flt_in[8][6][2]; + signed int r_buf_flt_out[8][6][2]; + signed int coeff[8][6][5]; + /* buffer data */ + signed int *l_buf; + signed int *r_buf; + int l_buf_size; + int r_buf_size; + int l_out; + int r_out; + int l_sp_in[8]; + int r_sp_in[8]; + int l_in[4]; + int r_in[4]; + int gain; + unsigned long int max_reverb_time; +}; + + extern void _WM_reset_reverb (struct _rvb *rvb); + extern struct _rvb *_WM_init_reverb(int rate, float room_x, float room_y, float listen_x, float listen_y); + extern void _WM_free_reverb (struct _rvb *rvb); + extern void _WM_do_reverb (struct _rvb *rvb, signed int *buffer, int size); + +} + +#endif /* __REVERB_H */ diff --git a/thirdparty/wildmidi/wildmidi/wildmidi_lib.h b/thirdparty/wildmidi/wildmidi/wildmidi_lib.h new file mode 100644 index 000000000..09fbe7125 --- /dev/null +++ b/thirdparty/wildmidi/wildmidi/wildmidi_lib.h @@ -0,0 +1,146 @@ +/* + wildmidi_lib.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef WILDMIDI_LIB_H +#define WILDMIDI_LIB_H + +#include "../../../source/zmusic/fileio.h" +#include + +namespace WildMidi +{ +enum EMixerOptions +{ + WM_MO_LOG_VOLUME = 0x0001, + WM_MO_ENHANCED_RESAMPLING = 0x0002, + WM_MO_REVERB = 0x0004, + WM_MO_WHOLETEMPO = 0x8000, + WM_MO_ROUNDTEMPO = 0x2000, + + WM_GS_VERSION = 0x0001, +}; + + +class SoundFontReaderInterface; + +struct _WM_Info { + char *copyright; + unsigned long int current_sample; + unsigned long int approx_total_samples; + unsigned short int mixer_options; + unsigned long int total_midi_time; +}; + +typedef void midi; + +struct Instruments +{ + MusicIO::SoundFontReaderInterface *sfreader; + + struct _patch *patch[128] = {}; + float reverb_room_width = 16.875f; + float reverb_room_length = 22.5f; + + float reverb_listen_posx = 8.4375f; + float reverb_listen_posy = 16.875f; + + int fix_release = 0; + int auto_amp = 0; + int auto_amp_with_amp = 0; + + unsigned short int _WM_SampleRate; // WildMidi makes the sample rate a property of the patches, not the renderer. Meaning that the instruments need to be reloaded when it changes... :? + + Instruments(MusicIO::SoundFontReaderInterface *reader, int samplerate) + { + sfreader = reader; + _WM_SampleRate = samplerate; + } + ~Instruments(); + + int LoadConfig(const char *config_file); + int load_sample(struct _patch *sample_patch); + struct _patch *get_patch_data(unsigned short patchid); + void load_patch(struct _mdi *mdi, unsigned short patchid); + int GetSampleRate() { return _WM_SampleRate; } + struct _sample * load_gus_pat(const char *filename); + +private: + void FreePatches(void); +}; + +const char * WildMidi_GetString (unsigned short int info); + + + +class Renderer +{ + Instruments *instruments; + + signed int WM_MasterVolume = 948; + unsigned int WM_MixerOptions = 0; + +public: + Renderer(Instruments *instr, unsigned mixOpt = 0); + ~Renderer(); + + void ShortEvent(int status, int parm1, int parm2); + void LongEvent(const unsigned char *data, int len); + void ComputeOutput(float *buffer, int len); + void LoadInstrument(int bank, int percussion, int instr); + int GetVoiceCount(); + int SetOption(int opt, int set); + + void SetMasterVolume(unsigned char master_volume); + midi * NewMidi(); + +private: + void *handle; + + void AdjustNoteVolumes(struct _mdi *mdi, unsigned char ch, struct _note *nte); + void AdjustChannelVolumes(struct _mdi *mdi, unsigned char ch); + void do_note_on(struct _mdi *mdi, struct _event_data *data); + void do_aftertouch(struct _mdi *mdi, struct _event_data *data); + void do_control_channel_volume(struct _mdi *mdi, struct _event_data *data); + void do_control_channel_balance(struct _mdi *mdi, struct _event_data *data); + void do_control_channel_pan(struct _mdi *mdi, struct _event_data *data); + void do_control_channel_expression(struct _mdi *mdi, struct _event_data *data); + void do_control_channel_controllers_off(struct _mdi *mdi, struct _event_data *data); + void do_pitch(struct _mdi *mdi, struct _event_data *data); + void do_patch(struct _mdi *mdi, struct _event_data *data); + void do_channel_pressure(struct _mdi *mdi, struct _event_data *data); + void do_sysex_roland_drum_track(struct _mdi *mdi, struct _event_data *data); + void do_sysex_gm_reset(struct _mdi *mdi, struct _event_data *data); + void do_sysex_roland_reset(struct _mdi *mdi, struct _event_data *data); + void do_sysex_yamaha_reset(struct _mdi *mdi, struct _event_data *data); + struct _mdi *Init_MDI(); + unsigned long int get_inc(struct _mdi *mdi, struct _note *nte); +}; + +extern void (*wm_error_func)(const char *wmfmt, va_list args); +} + +#endif /* WILDMIDI_LIB_H */ + diff --git a/thirdparty/wildmidi/wildmidi/wm_error.h b/thirdparty/wildmidi/wildmidi/wm_error.h new file mode 100644 index 000000000..5fa6f4334 --- /dev/null +++ b/thirdparty/wildmidi/wildmidi/wm_error.h @@ -0,0 +1,60 @@ +/* + wm_error.h + + error reporting + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +#ifndef __WM_ERROR_H +#define __WM_ERROR_H + +namespace WildMidi +{ + +enum { + WM_ERR_NONE = 0, + WM_ERR_MEM, + WM_ERR_STAT, + WM_ERR_LOAD, + WM_ERR_OPEN, + WM_ERR_READ, + WM_ERR_INVALID, + WM_ERR_CORUPT, + WM_ERR_NOT_INIT, + WM_ERR_INVALID_ARG, + WM_ERR_ALR_INIT, + WM_ERR_NOT_MIDI, + WM_ERR_LONGFIL, + + WM_ERR_MAX +}; + + extern void _WM_ERROR_NEW(const char * wmfmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 1, 2))) +#endif + ; + extern void _WM_ERROR(const char * func, unsigned int lne, int wmerno, + const char * wmfor, int error); + +} +#endif /* __WM_ERROR_H */ diff --git a/thirdparty/wildmidi/wildmidi_lib.cpp b/thirdparty/wildmidi/wildmidi_lib.cpp new file mode 100644 index 000000000..9286b9825 --- /dev/null +++ b/thirdparty/wildmidi/wildmidi_lib.cpp @@ -0,0 +1,2690 @@ +/* + wildmidi_lib.c + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2014 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +//#include "config.h" + +#define UNUSED(x) (void)(x) + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include + +#include "common.h" +#include "wm_error.h" +#include "file_io.h" +#include "reverb.h" +#include "gus_pat.h" +#include "wildmidi_lib.h" + +namespace WildMidi +{ + +#define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\') +#ifdef _WIN32 +#define HAS_DRIVE_SPEC(f) ((f)[0] && ((f)[1] == ':')) +#else +#define HAS_DRIVE_SPEC(f) (0) +#endif +#define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC((f))) + + +/* + * ========================= + * Global Data and Data Structs + * ========================= + */ + +#define MEM_CHUNK 8192 + + +static const char WM_Version[] = "WildMidi Processing Library"; + +struct _channel { + unsigned char bank; + struct _patch *patch; + unsigned char hold; + unsigned char volume; + unsigned char pressure; + unsigned char expression; + signed char balance; + signed char pan; + signed short int left_adjust; + signed short int right_adjust; + signed short int pitch; + signed short int pitch_range; + signed long int pitch_adjust; + unsigned short reg_data; + unsigned char reg_non; + unsigned char isdrum; +}; + +#define HOLD_OFF 0x02 + +struct _note { + unsigned short noteid; + unsigned char velocity; + struct _patch *patch; + struct _sample *sample; + unsigned int sample_pos; + unsigned int sample_inc; + signed int env_inc; + unsigned char env; + signed int env_level; + unsigned char modes; + unsigned char hold; + unsigned char active; + struct _note *replay; + struct _note *next; + unsigned int left_mix_volume; + unsigned int right_mix_volume; + unsigned char is_off; +}; + +struct _event_data { + unsigned char channel; + unsigned int data; +}; + +struct _mdi { + _mdi() + { + samples_to_mix = 0; + midi_master_vol = 0; + memset(&info, 0, sizeof(info)); + tmp_info = NULL; + memset(&channel, 0, sizeof(channel)); + note = NULL; + memset(note_table, 0, sizeof(note_table)); + patches = NULL; + patch_count = 0; + amp = 0; + mix_buffer = NULL; + mix_buffer_size = 0; + reverb = NULL; + } + + unsigned long int samples_to_mix; + + unsigned short midi_master_vol; + struct _WM_Info info; + struct _WM_Info *tmp_info; + struct _channel channel[16]; + struct _note *note; + struct _note note_table[2][16][128]; + + struct _patch **patches; + unsigned long int patch_count; + signed short int amp; + + signed int *mix_buffer; + unsigned long int mix_buffer_size; + + struct _rvb *reverb; +}; + +#define FPBITS 10 +#define FPMASK ((1L< gauss_table; /* *gauss_table[1<> 1); + int j; + int sign; + double ck; + double x, x_inc, xz; + double z[35]; + double *gptr, *t; + + if (gauss_table.size()) { + return; + } + + newt_coeffs[0][0] = 1; + for (i = 0; i <= n; i++) { + newt_coeffs[i][0] = 1; + newt_coeffs[i][i] = 1; + + if (i > 1) { + newt_coeffs[i][0] = newt_coeffs[i - 1][0] / i; + newt_coeffs[i][i] = newt_coeffs[i - 1][0] / i; + } + + for (j = 1; j < i; j++) { + newt_coeffs[i][j] = newt_coeffs[i - 1][j - 1] + + newt_coeffs[i - 1][j]; + if (i > 1) + newt_coeffs[i][j] /= i; + } + z[i] = i / (4 * M_PI); + } + + for (i = 0; i <= n; i++) + for (j = 0, sign = (int)pow(-1., i); j <= i; j++, sign *= -1) + newt_coeffs[i][j] *= sign; + + gauss_table.resize((1<first_sample) { + tmp_sample = patch[i]->first_sample->next; + free(patch[i]->first_sample->data); + free(patch[i]->first_sample); + patch[i]->first_sample = tmp_sample; + } + free(patch[i]->filename); + tmp_patch = patch[i]->next; + free(patch[i]); + patch[i] = tmp_patch; + } + } +} + +/* wm_strdup -- adds extra space for appending up to 4 chars */ +static char *wm_strdup (const char *str) { + size_t l = strlen(str) + 5; + char *d = (char *) malloc(l * sizeof(char)); + if (d) { + strcpy(d, str); + return d; + } + return NULL; +} + +static inline int wm_isdigit(int c) { + return (c >= '0' && c <= '9'); +} + +#define TOKEN_CNT_INC 8 +static char** WM_LC_Tokenize_Line(char *line_data) +{ + int line_length = (int)strlen(line_data); + int token_data_length = 0; + int line_ofs = 0; + int token_start = 0; + char **token_data = NULL; + int token_count = 0; + bool in_quotes = false; + + if (line_length == 0) + return NULL; + + do { + /* ignore everything after # */ + if (line_data[line_ofs] == '#') { + break; + } + if (line_data[line_ofs] == '"') + { + in_quotes = !in_quotes; + } + else if (!in_quotes && ((line_data[line_ofs] == ' ') || (line_data[line_ofs] == '\t'))) { + /* whitespace means we aren't in a token */ + if (token_start) { + token_start = 0; + line_data[line_ofs] = '\0'; + } + } else { + if (!token_start) { + /* the start of a token in the line */ + token_start = 1; + if (token_count >= token_data_length) { + token_data_length += TOKEN_CNT_INC; + token_data = (char**)realloc(token_data, token_data_length * sizeof(char *)); + if (token_data == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM,"to parse config", errno); + return NULL; + } + } + + token_data[token_count] = &line_data[line_ofs]; + token_count++; + } + } + line_ofs++; + } while (line_ofs != line_length); + + /* if we have found some tokens then add a null token to the end */ + if (token_count) { + if (token_count >= token_data_length) { + token_data = (char**)realloc(token_data, + ((token_count + 1) * sizeof(char *))); + } + token_data[token_count] = NULL; + } + + return token_data; +} + +int Instruments::LoadConfig(const char *config_parm) +{ + unsigned long int config_size = 0; + char *config_buffer = NULL; + const char *dir_end = NULL; + char *config_dir = NULL; + unsigned long int config_ptr = 0; + unsigned long int line_start_ptr = 0; + unsigned short int patchid = 0; + struct _patch * tmp_patch; + char **line_tokens = NULL; + int token_count = 0; + std::string config_file_s; + + config_buffer = (char *)_WM_BufferFile(sfreader, config_parm, &config_size, &config_file_s); + if (!config_buffer) { + FreePatches(); + return -1; + } + + auto config_file = config_file_s.c_str(); + + // This part was rewritten because the original depended on a header that was GPL'd. + dir_end = strrchr(config_file, '/'); +#ifdef _WIN32 + const char *dir_end2 = strrchr(config_file, '\\'); + if (dir_end2 > dir_end) dir_end = dir_end2; +#endif + + if (dir_end) { + config_dir = (char*)malloc((dir_end - config_file + 2)); + if (config_dir == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", + errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + FreePatches(); + free(config_buffer); + return -1; + } + strncpy(config_dir, config_file, (dir_end - config_file + 1)); + config_dir[dir_end - config_file + 1] = '\0'; + } + + config_ptr = 0; + line_start_ptr = 0; + + /* handle files without a newline at the end: this relies on + * _WM_BufferFile() allocating the buffer with one extra byte */ + config_buffer[config_size] = '\n'; + + while (config_ptr <= config_size) { + if (config_buffer[config_ptr] == '\r' || + config_buffer[config_ptr] == '\n') + { + config_buffer[config_ptr] = '\0'; + + if (config_ptr != line_start_ptr) { + line_tokens = WM_LC_Tokenize_Line(&config_buffer[line_start_ptr]); + if (line_tokens) { + if (stricmp(line_tokens[0], "dir") == 0) { + free(config_dir); + if (!line_tokens[1]) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in dir line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } else if (!(config_dir = wm_strdup(line_tokens[1]))) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + "to parse config", errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } + if (!IS_DIR_SEPARATOR(config_dir[strlen(config_dir) - 1])) { + config_dir[strlen(config_dir) + 1] = '\0'; + config_dir[strlen(config_dir)] = '/'; + } + } else if (stricmp(line_tokens[0], "source") == 0) { + char *new_config = NULL; + if (!line_tokens[1]) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in source line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) { + new_config = (char*)malloc( + strlen(config_dir) + strlen(line_tokens[1]) + + 1); + if (new_config == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + "to parse config", errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + strcpy(new_config, config_dir); + strcpy(&new_config[strlen(config_dir)], line_tokens[1]); + } else { + if (!(new_config = wm_strdup(line_tokens[1]))) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + "to parse config", errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } + } + if (LoadConfig(new_config) == -1) { + free(new_config); + free(line_tokens); + free(config_buffer); + free(config_dir); + return -1; + } + free(new_config); + } else if (stricmp(line_tokens[0], "bank") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in bank line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + patchid = (atoi(line_tokens[1]) & 0xFF) << 8; + } else if (stricmp(line_tokens[0], "drumset") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in drumset line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + patchid = ((atoi(line_tokens[1]) & 0xFF) << 8) | 0x80; + } else if (stricmp(line_tokens[0], "reverb_room_width") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_room_width line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_room_width = (float) atof(line_tokens[1]); + if (reverb_room_width < 1.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_width < 1 meter, setting to minimum of 1 meter)", + 0); + reverb_room_width = 1.0f; + } else if (reverb_room_width > 100.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_width > 100 meters, setting to maximum of 100 meters)", + 0); + reverb_room_width = 100.0f; + } + } else if (stricmp(line_tokens[0], "reverb_room_length") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_room_length line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_room_length = (float) atof(line_tokens[1]); + if (reverb_room_length < 1.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_length < 1 meter, setting to minimum of 1 meter)", + 0); + reverb_room_length = 1.0f; + } else if (reverb_room_length > 100.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_length > 100 meters, setting to maximum of 100 meters)", + 0); + reverb_room_length = 100.0f; + } + } else if (stricmp(line_tokens[0], "reverb_listener_posx") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_listen_posx line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_listen_posx = (float) atof(line_tokens[1]); + if ((reverb_listen_posx > reverb_room_width) + || (reverb_listen_posx < 0.0f)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_listen_posx set outside of room)", + 0); + reverb_listen_posx = reverb_room_width / 2.0f; + } + } else if (stricmp(line_tokens[0], + "reverb_listener_posy") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_listen_posy line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_listen_posy = (float) atof(line_tokens[1]); + if ((reverb_listen_posy > reverb_room_width) + || (reverb_listen_posy < 0.0f)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_listen_posy set outside of room)", + 0); + reverb_listen_posy = reverb_room_length * 0.75f; + } + } else if (stricmp(line_tokens[0], + "guspat_editor_author_cant_read_so_fix_release_time_for_me") + == 0) { + fix_release = 1; + } else if (stricmp(line_tokens[0], "auto_amp") == 0) { + auto_amp = 1; + } else if (stricmp(line_tokens[0], "auto_amp_with_amp") + == 0) { + auto_amp = 1; + auto_amp_with_amp = 1; + } else if (wm_isdigit(line_tokens[0][0])) { + patchid = (patchid & 0xFF80) + | (atoi(line_tokens[0]) & 0x7F); + if (patch[(patchid & 0x7F)] == NULL) { + patch[(patchid & 0x7F)] = (struct _patch*)malloc( + sizeof(struct _patch)); + if (patch[(patchid & 0x7F)] == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + NULL, errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + tmp_patch = patch[(patchid & 0x7F)]; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } else { + tmp_patch = patch[(patchid & 0x7F)]; + if (tmp_patch->patchid == patchid) { + free(tmp_patch->filename); + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + } else { + if (tmp_patch->next) { + while (tmp_patch->next) { + if (tmp_patch->next->patchid == patchid) + break; + tmp_patch = tmp_patch->next; + } + if (tmp_patch->next == NULL) { + if ((tmp_patch->next = (struct _patch*)malloc( + sizeof(struct _patch))) + == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_MEM, NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_LOAD, config_file, + 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + tmp_patch = tmp_patch->next; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } else { + tmp_patch = tmp_patch->next; + free(tmp_patch->filename); + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + } + } else { + tmp_patch->next = (struct _patch*)malloc( + sizeof(struct _patch)); + if (tmp_patch->next == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_MEM, NULL, errno); + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_LOAD, config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + tmp_patch = tmp_patch->next; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } + } + } + if (!line_tokens[1]) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in patch line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) { + tmp_patch->filename = (char*)malloc( + strlen(config_dir) + strlen(line_tokens[1]) + + 5); + if (tmp_patch->filename == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + strcpy(tmp_patch->filename, config_dir); + strcat(tmp_patch->filename, line_tokens[1]); + } else { + if (!(tmp_patch->filename = wm_strdup(line_tokens[1]))) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + } + if (strnicmp( + &tmp_patch->filename[strlen(tmp_patch->filename) + - 4], ".pat", 4) != 0) { + strcat(tmp_patch->filename, ".pat"); + } + tmp_patch->env[0].set = 0x00; + tmp_patch->env[1].set = 0x00; + tmp_patch->env[2].set = 0x00; + tmp_patch->env[3].set = 0x00; + tmp_patch->env[4].set = 0x00; + tmp_patch->env[5].set = 0x00; + tmp_patch->keep = 0; + tmp_patch->remove = 0; + + token_count = 0; + while (line_tokens[token_count]) { + if (strnicmp(line_tokens[token_count], "amp=", 4) + == 0) { + if (!wm_isdigit(line_tokens[token_count][4])) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + tmp_patch->amp = (atoi( + &line_tokens[token_count][4]) << 10) + / 100; + } + } else if (strnicmp(line_tokens[token_count], + "note=", 5) == 0) { + if (!wm_isdigit(line_tokens[token_count][5])) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + tmp_patch->note = atoi( + &line_tokens[token_count][5]); + } + } else if (strnicmp(line_tokens[token_count], + "env_time", 8) == 0) { + if ((!wm_isdigit(line_tokens[token_count][8])) + || (!wm_isdigit( + line_tokens[token_count][10])) + || (line_tokens[token_count][9] != '=')) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + unsigned int env_no = atoi( + &line_tokens[token_count][8]); + if (env_no > 5) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", + 0); + } else { + tmp_patch->env[env_no].time = + (float) atof( + &line_tokens[token_count][10]); + if ((tmp_patch->env[env_no].time + > 45000.0f) + || (tmp_patch->env[env_no].time + < 1.47f)) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(range error in patch line)", + 0); + tmp_patch->env[env_no].set &= 0xFE; + } else { + tmp_patch->env[env_no].set |= 0x01; + } + } + } + } else if (strnicmp(line_tokens[token_count], + "env_level", 9) == 0) { + if ((!wm_isdigit(line_tokens[token_count][9])) + || (!wm_isdigit( + line_tokens[token_count][11])) + || (line_tokens[token_count][10] != '=')) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + unsigned int env_no = atoi( + &line_tokens[token_count][9]); + if (env_no > 5) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", + 0); + } else { + tmp_patch->env[env_no].level = + (float) atof( + &line_tokens[token_count][11]); + if ((tmp_patch->env[env_no].level > 1.0f) + || (tmp_patch->env[env_no].level + < 0.0f)) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(range error in patch line)", + 0); + tmp_patch->env[env_no].set &= 0xFD; + } else { + tmp_patch->env[env_no].set |= 0x02; + } + } + } + } else if (stricmp(line_tokens[token_count], + "keep=loop") == 0) { + tmp_patch->keep |= SAMPLE_LOOP; + } else if (stricmp(line_tokens[token_count], + "keep=env") == 0) { + tmp_patch->keep |= SAMPLE_ENVELOPE; + } else if (stricmp(line_tokens[token_count], + "remove=sustain") == 0) { + tmp_patch->remove |= SAMPLE_SUSTAIN; + } else if (stricmp(line_tokens[token_count], + "remove=clamped") == 0) { + tmp_patch->remove |= SAMPLE_CLAMPED; + } + token_count++; + } + } + } + /* free up tokens */ + free(line_tokens); + } + line_start_ptr = config_ptr + 1; + } + config_ptr++; + } + + free(config_buffer); + free(config_dir); + + return 0; +} + +/* sample loading */ + +int Instruments::load_sample(struct _patch *sample_patch) + { + struct _sample *guspat = NULL; + struct _sample *tmp_sample = NULL; + unsigned int i = 0; + + /* we only want to try loading the guspat once. */ + sample_patch->loaded = 1; + + if ((guspat = load_gus_pat(sample_patch->filename)) == NULL) { + return -1; + } + + if (auto_amp) { + signed short int tmp_max = 0; + signed short int tmp_min = 0; + signed short samp_max = 0; + signed short samp_min = 0; + tmp_sample = guspat; + do { + samp_max = 0; + samp_min = 0; + for (i = 0; i < (tmp_sample->data_length >> 10); i++) { + if (tmp_sample->data[i] > samp_max) + samp_max = tmp_sample->data[i]; + if (tmp_sample->data[i] < samp_min) + samp_min = tmp_sample->data[i]; + } + if (samp_max > tmp_max) + tmp_max = samp_max; + if (samp_min < tmp_min) + tmp_min = samp_min; + tmp_sample = tmp_sample->next; + } while (tmp_sample); + if (auto_amp_with_amp) { + if (tmp_max >= -tmp_min) { + sample_patch->amp = (sample_patch->amp + * ((32767 << 10) / tmp_max)) >> 10; + } else { + sample_patch->amp = (sample_patch->amp + * ((32768 << 10) / -tmp_min)) >> 10; + } + } else { + if (tmp_max >= -tmp_min) { + sample_patch->amp = (32767 << 10) / tmp_max; + } else { + sample_patch->amp = (32768 << 10) / -tmp_min; + } + } + } + + sample_patch->first_sample = guspat; + + if (sample_patch->patchid & 0x0080) { + if (!(sample_patch->keep & SAMPLE_LOOP)) { + do { + guspat->modes &= 0xFB; + guspat = guspat->next; + } while (guspat); + } + guspat = sample_patch->first_sample; + if (!(sample_patch->keep & SAMPLE_ENVELOPE)) { + do { + guspat->modes &= 0xBF; + guspat = guspat->next; + } while (guspat); + } + guspat = sample_patch->first_sample; + } + + if (sample_patch->patchid == 47) { + do { + if (!(guspat->modes & SAMPLE_LOOP)) { + for (i = 3; i < 6; i++) { + guspat->env_target[i] = guspat->env_target[2]; + guspat->env_rate[i] = guspat->env_rate[2]; + } + } + guspat = guspat->next; + } while (guspat); + guspat = sample_patch->first_sample; + } + + do { + if ((sample_patch->remove & SAMPLE_SUSTAIN) + && (guspat->modes & SAMPLE_SUSTAIN)) { + guspat->modes ^= SAMPLE_SUSTAIN; + } + if ((sample_patch->remove & SAMPLE_CLAMPED) + && (guspat->modes & SAMPLE_CLAMPED)) { + guspat->modes ^= SAMPLE_CLAMPED; + } + if (sample_patch->keep & SAMPLE_ENVELOPE) { + guspat->modes |= SAMPLE_ENVELOPE; + } + + for (i = 0; i < 6; i++) { + if (guspat->modes & SAMPLE_ENVELOPE) { + if (sample_patch->env[i].set & 0x02) { + guspat->env_target[i] = 16448 + * (signed long int) (255.0 + * sample_patch->env[i].level); + } + + if (sample_patch->env[i].set & 0x01) { + guspat->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate + * (sample_patch->env[i].time / 1000.0))); + } + } else { + guspat->env_target[i] = 4194303; + guspat->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + } + } + + guspat = guspat->next; + } while (guspat); + return 0; +} + +struct _patch *Instruments::get_patch_data(unsigned short patchid) +{ + struct _patch *search_patch; + + search_patch = patch[patchid & 0x007F]; + + if (search_patch == NULL) { + return NULL; + } + + while (search_patch) { + if (search_patch->patchid == patchid) { + return search_patch; + } + search_patch = search_patch->next; + } + if ((patchid >> 8) != 0) { + return (get_patch_data(patchid & 0x00FF)); + } + return NULL; +} + +void Instruments::load_patch(struct _mdi *mdi, unsigned short patchid) +{ + unsigned int i; + struct _patch *tmp_patch = NULL; + + for (i = 0; i < mdi->patch_count; i++) { + if (mdi->patches[i]->patchid == patchid) { + return; + } + } + + tmp_patch = get_patch_data(patchid); + if (tmp_patch == NULL) { + return; + } + + if (!tmp_patch->loaded) { + if (load_sample(tmp_patch) == -1) { + return; + } + } + + if (tmp_patch->first_sample == NULL) { + return; + } + + mdi->patch_count++; + mdi->patches = (struct _patch**)realloc(mdi->patches, + (sizeof(struct _patch*) * mdi->patch_count)); + mdi->patches[mdi->patch_count - 1] = tmp_patch; + tmp_patch->inuse_count++; +} + +Instruments::~Instruments() +{ + FreePatches(); + sfreader->close(); +} + + +static struct _sample *get_sample_data(struct _patch *sample_patch, unsigned long int freq) + { + struct _sample *last_sample = NULL; + struct _sample *return_sample = NULL; + + if (sample_patch == NULL) { + return NULL; + } + if (sample_patch->first_sample == NULL) { + return NULL; + } + if (freq == 0) { + return sample_patch->first_sample; + } + + return_sample = sample_patch->first_sample; + last_sample = sample_patch->first_sample; + while (last_sample) { + if (freq > last_sample->freq_low) { + if (freq < last_sample->freq_high) { + return last_sample; + } else { + return_sample = last_sample; + } + } + last_sample = last_sample->next; + } + return return_sample; +} + +/* Should be called in any function that effects note volumes */ +void Renderer::AdjustNoteVolumes(struct _mdi *mdi, unsigned char ch, struct _note *nte) + { + double premix_dBm; + double premix_lin; + int pan_ofs; + double premix_dBm_left; + double premix_dBm_right; + double premix_left; + double premix_right; + double volume_adj; + unsigned vol_ofs; + + /* + Pointless CPU heating checks to shoosh up a compiler + */ + if (ch > 0x0f) ch = 0x0f; + + pan_ofs = mdi->channel[ch].balance + mdi->channel[ch].pan - 64; + + vol_ofs = (nte->velocity * ((mdi->channel[ch].expression * mdi->channel[ch].volume) / 127)) / 127; + + /* + This value is to reduce the chance of clipping. + Higher value means lower overall volume, + Lower value means higher overall volume. + NOTE: The lower the value the higher the chance of clipping. + FIXME: Still needs tuning. Clipping heard at a value of 3.75 + */ +#define VOL_DIVISOR 4.0 + volume_adj = ((float)WM_MasterVolume / 1024.0) / VOL_DIVISOR; + + // Pan 0 and 1 are both hard left so 64 can be centered + if (pan_ofs > 127) pan_ofs = 127; + if (--pan_ofs < 0) pan_ofs = 0; + premix_dBm_left = dBm_pan_volume[126-pan_ofs]; + premix_dBm_right = dBm_pan_volume[pan_ofs]; + + if (mdi->info.mixer_options & WM_MO_LOG_VOLUME) { + premix_dBm = dBm_volume[vol_ofs]; + + premix_dBm_left += premix_dBm; + premix_dBm_right += premix_dBm; + + premix_left = (pow(10.0,(premix_dBm_left / 20.0))) * volume_adj; + premix_right = (pow(10.0,(premix_dBm_right / 20.0))) * volume_adj; + } else { + premix_lin = (float)(lin_volume[vol_ofs]) / 1024.0; + + premix_left = premix_lin * pow(10.0, (premix_dBm_left / 20)) * volume_adj; + premix_right = premix_lin * pow(10.0, (premix_dBm_right / 20)) * volume_adj; + } + nte->left_mix_volume = (int)(premix_left * 1024.0); + nte->right_mix_volume = (int)(premix_right * 1024.0); +} + +/* Should be called in any function that effects channel volumes */ +/* Calling this function with a value > 15 will make it adjust notes on all channels */ +void Renderer::AdjustChannelVolumes(struct _mdi *mdi, unsigned char ch) +{ + struct _note *nte = mdi->note; + if (nte != NULL) { + do { + if (ch <= 15) { + if ((nte->noteid >> 8) == ch) { + goto _DO_ADJUST; + } + } else { + _DO_ADJUST: + AdjustNoteVolumes(mdi, ch, nte); + if (nte->replay) AdjustNoteVolumes(mdi, ch, nte->replay); + } + nte = nte->next; + } while (nte != NULL); + } +} + +static void do_note_off_extra(struct _note *nte) { + + nte->is_off = 0; + + + if (!(nte->modes & SAMPLE_ENVELOPE)) { + if (nte->modes & SAMPLE_LOOP) { + nte->modes ^= SAMPLE_LOOP; + } + nte->env_inc = 0; + + } else if (nte->hold) { + nte->hold |= HOLD_OFF; + + } else if (nte->modes & SAMPLE_SUSTAIN) { + if (nte->env < 3) { + nte->env = 3; + if (nte->env_level > nte->sample->env_target[3]) { + nte->env_inc = -nte->sample->env_rate[3]; + } else { + nte->env_inc = nte->sample->env_rate[3]; + } + } + + } else if (nte->modes & SAMPLE_CLAMPED) { + if (nte->env < 5) { + nte->env = 5; + if (nte->env_level > nte->sample->env_target[5]) { + nte->env_inc = -nte->sample->env_rate[5]; + } else { + nte->env_inc = nte->sample->env_rate[5]; + } + } + } else if (nte->env < 4) { + nte->env = 4; + if (nte->env_level > nte->sample->env_target[4]) { + nte->env_inc = -nte->sample->env_rate[4]; + } else { + nte->env_inc = nte->sample->env_rate[4]; + } + } +} + +static void do_note_off(struct _mdi *mdi, struct _event_data *data) { + struct _note *nte; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + nte = &mdi->note_table[0][ch][(data->data >> 8)]; + if (!nte->active) + nte = &mdi->note_table[1][ch][(data->data >> 8)]; + if (!nte->active) { + return; + } + + if ((mdi->channel[ch].isdrum) && (!(nte->modes & SAMPLE_LOOP))) { + return; + } + + if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env == 0)) { + // This is a fix for notes that end before the + // initial step of the envelope has completed + // making it impossible to hear them at times. + nte->is_off = 1; + } else { + do_note_off_extra(nte); + } +} + +unsigned long int Renderer::get_inc(struct _mdi *mdi, struct _note *nte) +{ + int ch = nte->noteid >> 8; + signed long int note_f; + unsigned long int freq; + + if (nte->patch->note != 0) { + note_f = nte->patch->note * 100; + } else { + note_f = (nte->noteid & 0x7f) * 100; + } + note_f += mdi->channel[ch].pitch_adjust; + if (note_f < 0) { + note_f = 0; + } else if (note_f > 12700) { + note_f = 12700; + } + freq = freq_table[(note_f % 1200)] >> (10 - (note_f / 1200)); + return (((freq / ((instruments->GetSampleRate() * 100) / 1024)) * 1024 + / nte->sample->inc_div)); +} + +void Renderer::do_note_on(struct _mdi *mdi, struct _event_data *data) +{ + struct _note *nte; + struct _note *prev_nte; + struct _note *nte_array; + unsigned long int freq = 0; + struct _patch *patch; + struct _sample *sample; + unsigned char ch = data->channel; + unsigned char note = (unsigned char)(data->data >> 8); + unsigned char velocity = (unsigned char)(data->data & 0xFF); + + if (velocity == 0x00) { + do_note_off(mdi, data); + return; + } + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + if (!mdi->channel[ch].isdrum) { + patch = mdi->channel[ch].patch; + if (patch == NULL) { + return; + } + freq = freq_table[(note % 12) * 100] >> (10 - (note / 12)); + } else { + patch = instruments->get_patch_data(((mdi->channel[ch].bank << 8) | note | 0x80)); + if (patch == NULL) { + return; + } + if (patch->note) { + freq = freq_table[(patch->note % 12) * 100] + >> (10 - (patch->note / 12)); + } else { + freq = freq_table[(note % 12) * 100] >> (10 - (note / 12)); + } + } + + sample = get_sample_data(patch, (freq / 100)); + if (sample == NULL) { + return; + } + + nte = &mdi->note_table[0][ch][note]; + + if (nte->active) { + if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3) + && (!(nte->hold & HOLD_OFF))) + return; + nte->replay = &mdi->note_table[1][ch][note]; + nte->env = 6; + nte->env_inc = -nte->sample->env_rate[6]; + nte = nte->replay; + } else { + if (mdi->note_table[1][ch][note].active) { + if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3) + && (!(nte->hold & HOLD_OFF))) + return; + mdi->note_table[1][ch][note].replay = nte; + mdi->note_table[1][ch][note].env = 6; + mdi->note_table[1][ch][note].env_inc = + -mdi->note_table[1][ch][note].sample->env_rate[6]; + } else { + nte_array = mdi->note; + if (nte_array == NULL) { + mdi->note = nte; + } else { + do { + prev_nte = nte_array; + nte_array = nte_array->next; + } while (nte_array); + prev_nte->next = nte; + } + nte->active = 1; + nte->next = NULL; + } + } + nte->noteid = (ch << 8) | note; + nte->patch = patch; + nte->sample = sample; + nte->sample_pos = 0; + nte->sample_inc = get_inc(mdi, nte); + nte->velocity = velocity; + nte->env = 0; + nte->env_inc = nte->sample->env_rate[0]; + nte->env_level = 0; + nte->modes = sample->modes; + nte->hold = mdi->channel[ch].hold; + nte->replay = NULL; + nte->is_off = 0; + AdjustNoteVolumes(mdi, ch, nte); +} + +void Renderer::do_aftertouch(struct _mdi *mdi, struct _event_data *data) +{ + struct _note *nte; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + nte = &mdi->note_table[0][ch][(data->data >> 8)]; + if (!nte->active) { + nte = &mdi->note_table[1][ch][(data->data >> 8)]; + if (!nte->active) { + return; + } + } + + nte->velocity = (unsigned char)data->data; + AdjustNoteVolumes(mdi, ch, nte); + if (nte->replay) { + nte->replay->velocity = (unsigned char)data->data; + AdjustNoteVolumes(mdi, ch, nte->replay); + } +} + +static void do_control_bank_select(struct _mdi *mdi, struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].bank = (unsigned char)data->data; +} + +static void do_control_data_entry_course(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + int data_tmp; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + data_tmp = mdi->channel[ch].pitch_range % 100; + mdi->channel[ch].pitch_range = short(data->data * 100 + data_tmp); + /* printf("Data Entry Course: pitch_range: %i\n\r",mdi->channel[ch].pitch_range);*/ + /* printf("Data Entry Course: data %li\n\r",data->data);*/ + } +} + +void Renderer::do_control_channel_volume(struct _mdi *mdi, struct _event_data *data) +{ + unsigned char ch = data->channel; + + mdi->channel[ch].volume = (unsigned char)data->data; + AdjustChannelVolumes(mdi, ch); +} + +void Renderer::do_control_channel_balance(struct _mdi *mdi, struct _event_data *data) +{ + unsigned char ch = data->channel; + + mdi->channel[ch].balance = (signed char)(data->data); + AdjustChannelVolumes(mdi, ch); +} + +void Renderer::do_control_channel_pan(struct _mdi *mdi, struct _event_data *data) + { + unsigned char ch = data->channel; + + mdi->channel[ch].pan = (signed char)(data->data); + AdjustChannelVolumes(mdi, ch); +} + +void Renderer::do_control_channel_expression(struct _mdi *mdi, struct _event_data *data) +{ + unsigned char ch = data->channel; + + mdi->channel[ch].expression = (unsigned char)data->data; + AdjustChannelVolumes(mdi, ch); +} + +static void do_control_data_entry_fine(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + int data_tmp; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + data_tmp = mdi->channel[ch].pitch_range / 100; + mdi->channel[ch].pitch_range = short((data_tmp * 100) + data->data); + /* printf("Data Entry Fine: pitch_range: %i\n\r",mdi->channel[ch].pitch_range);*/ + /* printf("Data Entry Fine: data: %li\n\r", data->data);*/ + } + +} + +static void do_control_channel_hold(struct _mdi *mdi, struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + if (data->data > 63) { + mdi->channel[ch].hold = 1; + } else { + mdi->channel[ch].hold = 0; + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + if (note_data->hold & HOLD_OFF) { + if (note_data->modes & SAMPLE_ENVELOPE) { + if (note_data->modes & SAMPLE_CLAMPED) { + if (note_data->env < 5) { + note_data->env = 5; + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + } + } else if (note_data->env < 4) { + note_data->env = 4; + if (note_data->env_level + > note_data->sample->env_target[4]) { + note_data->env_inc = + -note_data->sample->env_rate[4]; + } else { + note_data->env_inc = + note_data->sample->env_rate[4]; + } + } + } else { + if (note_data->modes & SAMPLE_LOOP) { + note_data->modes ^= SAMPLE_LOOP; + } + note_data->env_inc = 0; + } + } + note_data->hold = 0x00; + } + note_data = note_data->next; + } while (note_data); + } + } +} + +static void do_control_data_increment(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + if (mdi->channel[ch].pitch_range < 0x3FFF) + mdi->channel[ch].pitch_range++; + } +} + +static void do_control_data_decrement(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + if (mdi->channel[ch].pitch_range > 0) + mdi->channel[ch].pitch_range--; + } +} +static void do_control_non_registered_param_fine(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].reg_data = (mdi->channel[ch].reg_data & 0x3F80) + | data->data; + mdi->channel[ch].reg_non = 1; +} + +static void do_control_non_registered_param_course(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].reg_data = (mdi->channel[ch].reg_data & 0x7F) + | (data->data << 7); + mdi->channel[ch].reg_non = 1; +} + +static void do_control_registered_param_fine(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].reg_data = (unsigned short) ((mdi->channel[ch].reg_data & 0x3F80) + | data->data); + mdi->channel[ch].reg_non = 0; +} + +static void do_control_registered_param_course(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].reg_data = (unsigned short) ((mdi->channel[ch].reg_data & 0x7F) + | (data->data << 7)); + mdi->channel[ch].reg_non = 0; +} + +static void do_control_channel_sound_off(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + note_data->active = 0; + if (note_data->replay) { + note_data->replay = NULL; + } + } + note_data = note_data->next; + } while (note_data); + } +} + +void Renderer::do_control_channel_controllers_off(struct _mdi *mdi, struct _event_data *data) +{ + unsigned char ch = data->channel; + + mdi->channel[ch].expression = 127; + mdi->channel[ch].pressure = 127; + mdi->channel[ch].reg_data = 0xffff; + mdi->channel[ch].pitch_range = 200; + mdi->channel[ch].pitch = 0; + mdi->channel[ch].pitch_adjust = 0; + mdi->channel[ch].hold = 0; + + AdjustChannelVolumes(mdi, ch); +} + +static void do_control_channel_notes_off(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + if (mdi->channel[ch].isdrum) + return; + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + if (!note_data->hold) { + if (note_data->modes & SAMPLE_ENVELOPE) { + if (note_data->env < 5) { + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + note_data->env = 5; + } + } + } else { + note_data->hold |= HOLD_OFF; + } + } + note_data = note_data->next; + } while (note_data); + } +} + +void Renderer::do_patch(struct _mdi *mdi, struct _event_data *data) +{ + unsigned char ch = data->channel; + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + if (!mdi->channel[ch].isdrum) { + mdi->channel[ch].patch = instruments->get_patch_data((unsigned short)(((mdi->channel[ch].bank << 8) | data->data))); + } else { + mdi->channel[ch].bank = (unsigned char)data->data; + } +} + +void Renderer::do_channel_pressure(struct _mdi *mdi, struct _event_data *data) +{ + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + while (note_data) { + if ((note_data->noteid >> 8) == ch) { + note_data->velocity = (unsigned char)data->data; + AdjustNoteVolumes(mdi, ch, note_data); + if (note_data->replay) { + note_data->replay->velocity = (unsigned char)data->data; + AdjustNoteVolumes(mdi, ch, note_data->replay); + } + } + note_data = note_data->next; + } +} + + void Renderer::do_pitch(struct _mdi *mdi, struct _event_data *data) +{ + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + mdi->channel[ch].pitch = short(data->data - 0x2000); + + if (mdi->channel[ch].pitch < 0) { + mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range + * mdi->channel[ch].pitch / 8192; + } else { + mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range + * mdi->channel[ch].pitch / 8191; + } + + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + note_data->sample_inc = get_inc(mdi, note_data); + } + note_data = note_data->next; + } while (note_data); + } +} + +void Renderer::do_sysex_roland_drum_track(struct _mdi *mdi, struct _event_data *data) +{ + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + if (data->data > 0) { + mdi->channel[ch].isdrum = 1; + mdi->channel[ch].patch = NULL; + } else { + mdi->channel[ch].isdrum = 0; + mdi->channel[ch].patch = instruments->get_patch_data(0); + } +} + +void Renderer::do_sysex_gm_reset(struct _mdi *mdi, struct _event_data *data) +{ + int i; + for (i = 0; i < 16; i++) { + mdi->channel[i].bank = 0; + if (i != 9) { + mdi->channel[i].patch = instruments->get_patch_data(0); + } else { + mdi->channel[i].patch = NULL; + } + mdi->channel[i].hold = 0; + mdi->channel[i].volume = 100; + mdi->channel[i].pressure = 127; + mdi->channel[i].expression = 127; + mdi->channel[i].balance = 64; + mdi->channel[i].pan = 64; + mdi->channel[i].pitch = 0; + mdi->channel[i].pitch_range = 200; + mdi->channel[i].reg_data = 0xFFFF; + mdi->channel[i].isdrum = 0; + } + /* I would not expect notes to be active when this event + triggers but we'll adjust active notes as well just in case */ + AdjustChannelVolumes(mdi,16); // A setting > 15 adjusts all channels + + mdi->channel[9].isdrum = 1; + UNUSED(data); /* NOOP, to please the compiler gods */ +} + +void Renderer::do_sysex_roland_reset(struct _mdi *mdi, struct _event_data *data) +{ + do_sysex_gm_reset(mdi, data); +} + +void Renderer::do_sysex_yamaha_reset(struct _mdi *mdi, struct _event_data *data) +{ + do_sysex_gm_reset(mdi, data); +} + +struct _mdi *Renderer::Init_MDI() +{ + struct _mdi *mdi; + + mdi = new _mdi; + + mdi->info.copyright = NULL; + mdi->info.mixer_options = WM_MixerOptions; + + instruments->load_patch(mdi, 0x0000); + + mdi->samples_to_mix = 0; + mdi->info.current_sample = 0; + mdi->info.total_midi_time = 0; + mdi->info.approx_total_samples = 0; + + do_sysex_roland_reset(mdi, NULL); + + return mdi; +} + +static void freeMDI(struct _mdi *mdi) +{ + struct _sample *tmp_sample; + unsigned long int i; + + if (mdi->patch_count != 0) { + for (i = 0; i < mdi->patch_count; i++) { + mdi->patches[i]->inuse_count--; + if (mdi->patches[i]->inuse_count == 0) { + /* free samples here */ + while (mdi->patches[i]->first_sample) { + tmp_sample = mdi->patches[i]->first_sample->next; + free(mdi->patches[i]->first_sample->data); + free(mdi->patches[i]->first_sample); + mdi->patches[i]->first_sample = tmp_sample; + } + mdi->patches[i]->loaded = 0; + } + } + free(mdi->patches); + } + + free(mdi->tmp_info); + _WM_free_reverb(mdi->reverb); + if (mdi->mix_buffer) free(mdi->mix_buffer); + delete mdi; +} + +static int *WM_Mix_Linear(midi * handle, int * buffer, unsigned long int count) +{ + struct _mdi *mdi = (struct _mdi *)handle; + unsigned long int data_pos; + signed int premix, left_mix, right_mix; + struct _note *note_data = NULL; + + do { + note_data = mdi->note; + left_mix = right_mix = 0; + if (note_data != NULL) { + while (note_data) { + /* + * =================== + * resample the sample + * =================== + */ + data_pos = note_data->sample_pos >> FPBITS; + premix = ((note_data->sample->data[data_pos] + (((note_data->sample->data[data_pos + 1] - note_data->sample->data[data_pos]) * (int)(note_data->sample_pos & FPMASK)) / 1024)) * (note_data->env_level >> 12)) / 1024; + + + left_mix += (premix * (int)note_data->left_mix_volume) / 1024; + right_mix += (premix * (int)note_data->right_mix_volume) / 1024; + + /* + * ======================== + * sample position checking + * ======================== + */ + note_data->sample_pos += note_data->sample_inc; + if (note_data->modes & SAMPLE_LOOP) { + if (note_data->sample_pos > note_data->sample->loop_end) { + note_data->sample_pos = + note_data->sample->loop_start + + ((note_data->sample_pos + - note_data->sample->loop_start) + % note_data->sample->loop_size); + } + } else if (note_data->sample_pos >= note_data->sample->data_length) { + goto END_THIS_NOTE; + } + + if (note_data->env_inc == 0) { + note_data = note_data->next; + continue; + } + + note_data->env_level += note_data->env_inc; + if (note_data->env_inc < 0) { + if (note_data->env_level > note_data->sample->env_target[note_data->env]) { + note_data = note_data->next; + continue; + } + } else if (note_data->env_inc > 0) { + if (note_data->env_level < note_data->sample->env_target[note_data->env]) { + note_data = note_data->next; + continue; + } + } + + // Yes could have a condition here but + // it would create another bottleneck + note_data->env_level = + note_data->sample->env_target[note_data->env]; + switch (note_data->env) { + case 0: + if (!(note_data->modes & SAMPLE_ENVELOPE)) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } + break; + case 2: + if (note_data->modes & SAMPLE_SUSTAIN /*|| note_data->hold*/) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } else if (note_data->modes & SAMPLE_CLAMPED) { + note_data->env = 5; + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + continue; + } + break; + case 5: + if (note_data->env_level == 0) { + goto END_THIS_NOTE; + } + /* sample release */ + if (note_data->modes & SAMPLE_LOOP) + note_data->modes ^= SAMPLE_LOOP; + note_data->env_inc = 0; + note_data = note_data->next; + continue; + case 6: + END_THIS_NOTE: + if (note_data->replay != NULL) { + note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while (nte_array != note_data); + } + if (prev_note) { + prev_note->next = note_data->replay; + } else { + mdi->note = note_data->replay; + } + note_data->replay->next = note_data->next; + note_data = note_data->replay; + note_data->active = 1; + } + } else { + note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while ((nte_array != note_data) + && (nte_array)); + } + if (prev_note) { + prev_note->next = note_data->next; + } else { + mdi->note = note_data->next; + } + note_data = note_data->next; + } + } + continue; + } + note_data->env++; + + if (note_data->is_off == 1) { + do_note_off_extra(note_data); + } + + if (note_data->env_level + > note_data->sample->env_target[note_data->env]) { + note_data->env_inc = + -note_data->sample->env_rate[note_data->env]; + } else { + note_data->env_inc = + note_data->sample->env_rate[note_data->env]; + } + note_data = note_data->next; + continue; + } + + /* + * ========================= + * mix the channels together + * ========================= + */ + } + *buffer++ = left_mix; + *buffer++ = right_mix; + } while (--count); + return buffer; +} + +static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count) +{ + if (!gauss_table.size()) init_gauss(); + + struct _mdi *mdi = (struct _mdi *)handle; + unsigned long int data_pos; + signed int premix, left_mix, right_mix; + struct _note *note_data = NULL; + signed short int *sptr; + double y, xd; + double *gptr, *gend; + int left, right, temp_n; + int ii, jj; + + do { + note_data = mdi->note; + left_mix = right_mix = 0; + if (note_data != NULL) { + while (note_data) { + /* + * =================== + * resample the sample + * =================== + */ + data_pos = note_data->sample_pos >> FPBITS; + + /* check to see if we're near one of the ends */ + left = data_pos; + right = (note_data->sample->data_length >> FPBITS) - left + - 1; + temp_n = (right << 1) - 1; + if (temp_n <= 0) + temp_n = 1; + if (temp_n > (left << 1) + 1) + temp_n = (left << 1) + 1; + + /* use Newton if we can't fill the window */ + if (temp_n < gauss_n) { + xd = note_data->sample_pos & FPMASK; + xd /= (1L << FPBITS); + xd += temp_n >> 1; + y = 0; + sptr = note_data->sample->data + + (note_data->sample_pos >> FPBITS) + - (temp_n >> 1); + for (ii = temp_n; ii;) { + for (jj = 0; jj <= ii; jj++) + y += sptr[jj] * newt_coeffs[ii][jj]; + y *= xd - --ii; + } + y += *sptr; + } else { /* otherwise, use Gauss as usual */ + y = 0; + gptr = &gauss_table[(note_data->sample_pos & FPMASK) * + (gauss_n + 1)]; + gend = gptr + gauss_n; + sptr = note_data->sample->data + + (note_data->sample_pos >> FPBITS) + - (gauss_n >> 1); + do { + y += *(sptr++) * *(gptr++); + } while (gptr <= gend); + } + + premix = (int)((y * (note_data->env_level >> 12)) / 1024); + + left_mix += (premix * (int)note_data->left_mix_volume) / 1024; + right_mix += (premix * (int)note_data->right_mix_volume) / 1024; + + /* + * ======================== + * sample position checking + * ======================== + */ + note_data->sample_pos += note_data->sample_inc; + if (note_data->sample_pos > note_data->sample->loop_end) + { + if (note_data->modes & SAMPLE_LOOP) { + note_data->sample_pos = + note_data->sample->loop_start + + ((note_data->sample_pos + - note_data->sample->loop_start) + % note_data->sample->loop_size); + } else if (note_data->sample_pos >= note_data->sample->data_length) { + goto END_THIS_NOTE; + } + } + + if (note_data->env_inc == 0) { + note_data = note_data->next; + continue; + } + + note_data->env_level += note_data->env_inc; + if (note_data->env_inc < 0) { + if (note_data->env_level + > note_data->sample->env_target[note_data->env]) { + note_data = note_data->next; + continue; + } + } else if (note_data->env_inc > 0) { + if (note_data->env_level + < note_data->sample->env_target[note_data->env]) { + note_data = note_data->next; + continue; + } + } + + // Yes could have a condition here but + // it would create another bottleneck + + note_data->env_level = + note_data->sample->env_target[note_data->env]; + switch (note_data->env) { + case 0: + if (!(note_data->modes & SAMPLE_ENVELOPE)) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } + break; + case 2: + if (note_data->modes & SAMPLE_SUSTAIN) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } else if (note_data->modes & SAMPLE_CLAMPED) { + note_data->env = 5; + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + continue; + } + break; + case 5: + if (note_data->env_level == 0) { + goto END_THIS_NOTE; + } + /* sample release */ + if (note_data->modes & SAMPLE_LOOP) + note_data->modes ^= SAMPLE_LOOP; + note_data->env_inc = 0; + note_data = note_data->next; + continue; + case 6: + END_THIS_NOTE: + if (note_data->replay != NULL) { + note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while (nte_array != note_data); + } + if (prev_note) { + prev_note->next = note_data->replay; + } else { + mdi->note = note_data->replay; + } + note_data->replay->next = note_data->next; + note_data = note_data->replay; + note_data->active = 1; + } + } else { + note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while ((nte_array != note_data) + && (nte_array)); + } + if (prev_note) { + prev_note->next = note_data->next; + } else { + mdi->note = note_data->next; + } + note_data = note_data->next; + } + } + continue; + } + note_data->env++; + + if (note_data->is_off == 1) { + do_note_off_extra(note_data); + } + + if (note_data->env_level + > note_data->sample->env_target[note_data->env]) { + note_data->env_inc = + -note_data->sample->env_rate[note_data->env]; + } else { + note_data->env_inc = + note_data->sample->env_rate[note_data->env]; + } + note_data = note_data->next; + continue; + } + + /* + * ========================= + * mix the channels together + * ========================= + */ + } + *buffer++ = left_mix; + *buffer++ = right_mix; + } while (--count); + return buffer; +} + +int *WM_Mix(midi *handle, int *buffer, unsigned long count) +{ + if (((struct _mdi *)handle)->info.mixer_options & WM_MO_ENHANCED_RESAMPLING) + { + return WM_Mix_Gauss(handle, buffer, count); + } + else + { + return WM_Mix_Linear(handle, buffer, count); + } +} + +/* + * ========================= + * External Functions + * ========================= + */ + +const char *WildMidi_GetString(unsigned short int info) +{ + switch (info) + { + case WM_GS_VERSION: + return WM_Version; + } + return NULL; +} + + +midi * Renderer::NewMidi() +{ + midi * ret = NULL; + + ret = Init_MDI(); + + ((_mdi*)ret)->reverb = _WM_init_reverb(instruments->GetSampleRate(), instruments->reverb_room_width, + instruments->reverb_room_length, instruments->reverb_listen_posx, instruments->reverb_listen_posy); + return ret; +} + +int Renderer::SetOption(int options, int setting) +{ + struct _mdi *mdi; + + if (handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", + 0); + return -1; + } + + mdi = (struct _mdi *) handle; + if ((!(options & 0x0007)) || (options & 0xFFF8)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", + 0); + return -1; + } + if (setting & 0xFFF8) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(invalid setting)", 0); + return -1; + } + + mdi->info.mixer_options = ((mdi->info.mixer_options & (0x00FF ^ options)) + | (options & setting)); + + if (options & WM_MO_LOG_VOLUME) { + AdjustChannelVolumes(mdi, 16); // Settings greater than 15 + // adjust all channels + } else if (options & WM_MO_REVERB) { + _WM_reset_reverb(mdi->reverb); + } + + return 0; +} + + +Renderer::Renderer(Instruments *instr, unsigned mixOpt) +{ + init_gauss(); + instruments = instr; + WM_MixerOptions = mixOpt; + handle = NewMidi(); +} + +Renderer::~Renderer() +{ + freeMDI((_mdi *)handle); +} + +void Renderer::ShortEvent(int status, int parm1, int parm2) +{ + _mdi *mdi = (_mdi *)handle; + _event_data ev; + + ev.channel = status & 0x0F; + switch ((status & 0xF0) >> 4) // command + { + case 0x8: + ev.data = (parm1 << 8) | parm2; + do_note_off(mdi, &ev); + break; + + case 0x9: + ev.data = (parm1 << 8) | parm2; + do_note_on(mdi, &ev); + break; + + case 0xA: + ev.data = (parm1 << 8) | parm2; + do_aftertouch(mdi, &ev); + break; + + case 0xC: + ev.data = parm1; + do_patch(mdi, &ev); + break; + + case 0xD: + ev.data = parm1; + do_channel_pressure(mdi, &ev); + break; + + case 0xE: + ev.data = parm1 | (parm2 << 7); + do_pitch(mdi, &ev); + break; + + case 0xB: // Controllers + ev.data = parm2; + switch (parm1) + { + case 0: do_control_bank_select(mdi, &ev); break; + case 6: do_control_data_entry_course(mdi, &ev); break; // [sic] + case 7: do_control_channel_volume(mdi, &ev); break; + case 8: do_control_channel_balance(mdi, &ev); break; + case 10: do_control_channel_pan(mdi, &ev); break; + case 11: do_control_channel_expression(mdi, &ev); break; + case 38: do_control_data_entry_fine(mdi, &ev); break; + case 64: do_control_channel_hold(mdi, &ev); break; + case 96: do_control_data_increment(mdi, &ev); break; + case 97: do_control_data_decrement(mdi, &ev); break; + case 98: do_control_non_registered_param_fine(mdi, &ev); break; + case 99: do_control_non_registered_param_course(mdi, &ev); break; // [sic] + case 100: do_control_registered_param_fine(mdi, &ev); break; + case 101: do_control_registered_param_course(mdi, &ev); break; // [sic] + case 120: do_control_channel_sound_off(mdi, &ev); break; + case 121: do_control_channel_controllers_off(mdi, &ev); break; + case 123: do_control_channel_notes_off(mdi, &ev); break; + } + } +} + +void Renderer::LongEvent(const unsigned char *data, int len) +{ + // Check for Roland SysEx + if (len >= 11 && // Must be at least 11 bytes + data[len-1] == 0xF7 && // SysEx end + data[0] == 0xF0 && // SysEx + data[1] == 0x41 && // Roland + data[2] == 0x10 && // Device ID, defaults to 0x10 + data[3] == 0x42 && // Model ID, 0x42 indicates a GS synth + data[4] == 0x12 && // The other end is sending data to us + data[5] == 0x40) // We only care about addresses with this first byte + { + // Calculate checksum + int cksum = 0; + for (int i = 5; i < len - 2; ++i) + { + cksum += data[i]; + } + cksum = 128 - (cksum & 0x7F); + if (data[len-2] == cksum) + { // Check destination address + if (((data[6] & 0xF0) == 0x10) && data[7] == 0x15) + { // Roland drum track setting + unsigned char sysex_ch = data[6] & 0x0F; + if (sysex_ch == 0) + { + sysex_ch = 9; + } + else if (sysex_ch <= 9) + { + sysex_ch -= 1; + } + _event_data ev = { sysex_ch, data[8] }; + do_sysex_roland_drum_track((_mdi *)handle, &ev); + } + else if (data[6] == 0x00 && data[7] == 0x7F && data[8] == 0x00) + { // Roland GS reset + do_sysex_roland_reset((_mdi *)handle, NULL); + } + } + } + // For non-Roland Sysex messages */ + else + { + const unsigned char gm_reset[] = { 0xf0, 0x7e, 0x7f, 0x09, 0x01, 0xf7 }; + const unsigned char yamaha_reset[] = { 0xf0, 0x43, 0x10, 0x4c, 0x00, 0x00, 0x7e, 0x00, 0xf7}; + if (len == 6 && memcmp(gm_reset, data, 6) == 0) + { + do_sysex_gm_reset((_mdi *)handle, NULL); + } + else if (len == 9 && memcmp(yamaha_reset, data, 9) == 0) + { + do_sysex_yamaha_reset((_mdi *)handle, NULL); + } + } +} + +void Renderer::ComputeOutput(float *fbuffer, int len) +{ + _mdi *mdi = (_mdi *)handle; + int *buffer = (int *)fbuffer; + int *newbuf = WM_Mix(handle, buffer, len); +// assert(newbuf - buffer == len); + if (mdi->info.mixer_options & WM_MO_REVERB) { + _WM_do_reverb(mdi->reverb, buffer, len * 2); + } + for (; buffer < newbuf; ++buffer) + { + *(float *)buffer = (float)*buffer * (1.3f / 32768.f); // boost the volume because Wildmidi is far more quiet than the other synths and therefore hard to balance. + } +} + +void Renderer::LoadInstrument(int bank, int percussion, int instr) +{ + instruments->load_patch((_mdi *)handle, (bank << 8) | instr | (percussion ? 0x80 : 0)); +} + +int Renderer::GetVoiceCount() +{ + int count = 0; + for (_note *note_data = ((_mdi *)handle)->note; note_data != NULL; note_data = note_data->next) + { + count++; + } + return count; +} + + +void Renderer::SetMasterVolume(unsigned char master_volume) +{ + WM_MasterVolume = lin_volume[std::min(127, master_volume)]; +} + +} diff --git a/thirdparty/wildmidi/wm_error.cpp b/thirdparty/wildmidi/wm_error.cpp new file mode 100644 index 000000000..385faa0c9 --- /dev/null +++ b/thirdparty/wildmidi/wm_error.cpp @@ -0,0 +1,87 @@ +/* + wm_error.c + error reporting + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +//#include "config.h" + +#include +#include +#include +#include "wm_error.h" + +void ZMusic_Print(int type, const char* msg, va_list args); + +namespace WildMidi +{ + +void _WM_ERROR_NEW(const char * wmfmt, ...) { + va_list args; + va_start(args, wmfmt); + ZMusic_Print(100, wmfmt, args); +} + +void _WM_ERROR(const char * func, unsigned int lne, int wmerno, + const char * wmfor, int error) { + + static const char *const errors[WM_ERR_MAX+1] = { + "No error", + "Unable to obtain memory", + "Unable to stat", + "Unable to load", + "Unable to open", + "Unable to read", + "Invalid or Unsuported file format", + "File corrupt", + "Library not Initialized", + "Invalid argument", + "Library Already Initialized", + "Not a midi file", + "Refusing to load unusually long file", + + "Invalid error code" + }; + + if (wmerno < 0 || wmerno > WM_ERR_MAX) + wmerno = WM_ERR_MAX; + + if (wmfor != NULL) { + if (error != 0) { + _WM_ERROR_NEW("libWildMidi(%s:%u): ERROR %s %s (%s)\n", func, lne, + errors[wmerno], wmfor, strerror(error)); + } else { + _WM_ERROR_NEW("libWildMidi(%s:%u): ERROR %s %s\n", func, lne, + errors[wmerno], wmfor); + } + } else { + if (error != 0) { + _WM_ERROR_NEW("libWildMidi(%s:%u): ERROR %s (%s)\n", func, lne, + errors[wmerno], strerror(error)); + } else { + _WM_ERROR_NEW("libWildMidi(%s:%u): ERROR %s\n", func, lne, + errors[wmerno]); + } + } +} + +} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 000000000..ca5b89d48 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", + "builtin-baseline": "65c013b9667ff1f58fb8724209227e5a2e761043", + "name": "zmusic", + "description": "GZDoom's music system as a standalone library", + "features": + { + "vcpkg-libsndfile": + { + "description": "Use libsndfile provided by vcpkg.", + "dependencies": + [ + { + "name": "libsndfile", + "default-features": false, + "features": [ "mpeg", "external-libs" ] + } + ] + } + }, + "dependencies": [ + { + "name": "alsa", + "platform": "linux" + }, + { + "name": "zlib" + } + ] +}